pax_global_header00006660000000000000000000000064132536665150014526gustar00rootroot0000000000000052 comment=6f28abfa588d36076ad24f39a17d6752fd070ab6 .qt-license-check.exclude000066400000000000000000000000211325366651500156330ustar00rootroot00000000000000^src/libs/7zip/ .qt-license-check.optional000066400000000000000000000000241325366651500160320ustar00rootroot00000000000000^src/libs/kdtools/ .tag000066400000000000000000000000521325366651500116430ustar00rootroot000000000000006f28abfa588d36076ad24f39a17d6752fd070ab6 3RDPARTY000066400000000000000000000014021325366651500122220ustar00rootroot00000000000000The Qt Installer Framework sources include third party code: ============================================================ LZMA SDK Parts of the sources from the LZMA SDK , version 9.20 are under src/libs/7zip/unix and src/libs/7z/win. The LZMA SDK is written and placed in the public domain by Igor Pavlov. Some code in LZMA SDK is based on public domain code from another developers: 1) PPMd var.H (2001): Dmitry Shkarin 2) SHA-256: Wei Dai (Crypto++ library) ============================================================ KD Tools Sources for the KD Tools library are under src/libs/kdtools. The library is released under the same licenses as the rest of the installer framework. The copyright is owned by Klaralvdalens Datakonsult AB. Changelog000066400000000000000000000450671325366651500127200ustar00rootroot000000000000003.0.4 - Fix infinite wait if downloadable package is missing (QTIFW-1064) - Fix the maintenancetool that failing to elevate to admin (QTIFW-1010) - Fix devtool's segmentation fault when using operation - Update Japanese translation - Remove unnecessary warning (QTIFW-1022) - Fix Mkdir operation in uninstall (QTIFW-1099) - Enable building IFW with Squish support 3.0.3 - Set correct proxy type (QTBUG-65143) - Add no-proxy option (QTIFW-1085) - Fix maintenancetool icon visibility in Mac (QTIFW-1074) - Fix EnvironmentVariable operation in Windows (QTIFW-794) - Fix crash when downloadable package is missing (QTIFW-1064) - Documentation fixes 3.0.2 - Add possibility to reopen admin query (QTIFW-988) - Implement package download resume (QTIFW-5) - Use QDir::rmdir and not rmpath as that will remove more than it should - Enable HighDPI support only in Windows - Do not reset core when pressing 'Restart' (QTIFW-1017) - Update italian translation - Update russian translation - Fixed building with dynamically linked Qt (QTIFW-993) 3.0.1 - Fix install type if --online-only passed to binarycreator - Fix install fail if there are missing repositories - Fix Component Name visibility in maintenancetool - Adminauthorization freeze fixed under unix (QTIFW-934) - Enable high-DPI scaling (QTBUG-61122) - Fix maintenance tool update with silentUpdate (QTIFW-976) 3.0.0 - Change required Qt version, minimum version is now 5.6.2. - Clarify the add/remove components string to make it clearer - Fix crash at the very end of install if admin rights needed in Windows (QTIFW-943) - Make installer to check the dependency version (QTIFW-914) - Fix uninstallation on Windows when target path contains non-ascii characters - Fix installer crash when it contains replaced and replacement component (QTIFW-915) - Avoid warning messages when passing '--platform minimal' - New --silentUpdate command line option (QTIFW-906) - Allow installing compressed packages. (QTIFW-886) - Make support for modifying installations configurable. Introduces new setting 'SupportsModify' in the config.xml. - Allow the use of relative URLs to update repositories in Updates.xml. - Fix cancel button functionality in Settings->Repositories->Test. (QTIFW-832) - Introduced gui.setTextItems() method - Store lock files in temporary directory - Vertical layout for LicenseAgreement page. (QTIFW-815) - add NOMINMAX to fix compile with Qt5.7 in Windows (QTIFW-854) - Make usage of authorization fallback optional - Fix building with ICC on Windows. (QTIFW-851) - Add ApplicationsDirX86 and ApplicationsDirX64 predefined Variables. - Fixed (and greatly simplified) creating .dmg files - Add a logging category and debug print for http download - Make communication via installer.execute() Unicode safe - added two new optional arguments to installer.execute() to define the used codec. - Allow specifying the installer size in 'em' or 'ex' units - Fixed deleting files when uninstalling on OS X - Fix arguments in RegisterFileType - Add new '--sign' switch to binarycreator for signing OS X app bundles - Fix setValue saving in restart. (QTIFW-504) - Allow calling installer.setValue() with an empty string as the value. - Fixed writing log on Windows if target dir requires admin rights. - Fixed final 'Finish' message on OS X. - Resize banner image to fit default installer width. - Allow defining non-checkable items - introduces new element 'Checkable' for package. (QTIFW-773) - Added support for setting descriptions of Windows shortcuts. - Let mkdmg.sh script on OS X actually create random temporary file names. (QTIFW-780) - Fix timeout errors while building app bundles files on OS X. - Fix timezone issue in archive, simply keep the UTC time. - Optimized checking validity of target directory value on Windows. (QTIFW-673) - Remove implicit expanding vertical spacer from dynamic pages. (QTIFW-779) - Fixed compiling lib7z_facade.cpp with Visual Studio 2013. - Fixed several sudo problems eg. (QTIFW-771) - Fixed running binarycreator if the temporary directory name contains spaces. (QTIFW-787) - Fix violated assertion in error handling of binarycreator. - Automatically choose to perform uninstall if appropriate. - Implemented installer.readFile(). - Implemented support for creating URL shortcuts on Windows. - Fixed occasional crash on Windows when terminating installer. - Fix broken daylight saving time check. - Fix crash when updating admin installation with user/temp repository. (QTIFW-740) - Read UrlQueryString from settings. (QTIFW-744) - Allow to use the stylesheet to customize UI. - Add Castilian Spanish translation. - Add possibility to run silently without a gui. (QTIFW-166) - Removed {kd} and {kdupdater} prefix. - Make the installation relocatable (with some limitations). (QTIFW-653) - Add factory auto test. - Fix canceling the download done by an external call. - Print Qt version in verbose output. - Fix missing filename conversion on UNIX systems. (QTIFW-643) - Remove superfluous clone() method from operations. - Implement generic factory using c++11 variadic template feature. - Follow description on MSDN to implement time utils. (QTIFW-445) - Fix compile for gcc 4.7.3. - Convert to Qt 5 connect syntax. - Remove signal finished() overload. - Use qmake .depends instead of CONFIG += ordered. - Update archivegen. * Stop on file errors. * Better verbose/ help output. * Add support for compression level. * Do not hide symbols in statically build lib7z to use e.g. CPercentPrinter symbol in dynamic IFW builds. - Update source tree with version 9.38.beta of LZMA SDK. - Store AutoDependOn inside components.xml. - Implemented xml:lang attribute support for DisplayName tag. - Added AllUsers constant. - Enable feature live preview of dependencies. Introduces new InstallActionColumnVisible property to config.xml. - Remove scRemoteVersion, it's the same as scVersion. - Introduce a new struct PackageInfo which replaces UpdateSourceInfo. - Use positional arguments instead of options in devtool. - Add a warning if component with data contains children. - Fix reading and checking arguments of CreateShortcutOperation - Introduc new isMaintainer() method 2.0.5 - Fix hang in Windows when admin rights needed. (QTIFW-902) - Use deterministic sorting order for components. (QTIFW-833) - Resolve relative URLs from Updates.xml. - Do not fail to install or update if repository is missing 2.0.4 - Set also display name for Repository action "remove" - Adjusted wrong repogen parameter name - Fix settings test - Make Execute operation work without arguments in Windows - Add make install functionality with INSTALL_ROOT - Do not force doc build on Linux when calling "make install" - Documentation updates 2.0.3 - Update Japanese translation. - Make IFW compile with FreeBSD. (QTIFW-841) - Fix MSVC2015 build. - Revert "Use QQmlV4Function to correctly get empty parameters from script." - Fix crash when updating admin installation with user/temp repository (QTIFW-740) - Compile with Qt 5.5, 5.6. Minimum version is now 5.5. - Fix compile with namespace'ed Qt. 2.0.2 - Doc: Remove dubious sentence from installer.calculateComponentsToUninstall(). - Update documentation for --runoperation. - Update the git-archive export options. - Add alternative option where to read the installer-framework sha1. - Add sync.profile. - Add .qt-license-check.exclude and .qt-license-check.optional rules. - Update license headers. - Fix target directory check for reserved words on Windows. - Use QQmlV4Function to correctly get empty parameters from script. (QTIFW-724) - Fix errors when running updates in a directory requiring elevation. (QTIFW-746) - Doc: Fix the function name in Controller Scripting page. - Fix usage of system proxy with credentials. (QTBUG-46547) - Unify handling of translations. QTIFW-390 - Init all components with proper install action. (QTIFW-727) - Fix .dat file that gets deleted after multiple component changes on Windows. - Fix maintenance tool upgrade on OS X. - Fix handling of system proxy with credentials. (QTBUG-46547) - Unify selection of language for translations. (QTIFW-390) - Fix return value of component.installAction() when updating. (QTIFW-727) - Fix errors when updating an installation requiring elevation on Windows. (QTIFW-746) - Documentation updates. 2.0.1 - Do not throw exception on empty translation files. - Fix --checkupdates mode. - Prevent disabled component to be selected using the Select All button. (QTIFW-635) - Windows: Fix crashes in elevated installation. (QTIFW-6656, QTIFW-659) - OS X: Fix problems with writing settings in elevated installation (QTIFW-709) - Fix crash on exit for Windows XP, Vista. (QTIFW-652) - Re-add handling of zero compression level files supported by 7z. - Improve Proxy Credentials dialog. - Make component checker warning optional (set QT_LOGGING_RULES=ifw.componentChecker=true to enable). - Make code ready to be compiled with Qt 5.5. - Documentation updates. 2.0.0 - Require Qt 5.4 as a minimal version, Qt 4 code removed. - Only support Qt 5 on documentation side as well. - Source code is now available also under LGPLv3. - Changed the default value of AllowSpaceInPath in config.xml to true. - Made cycles in component dependencies fatal. - Respect AllowSpaceInPath everywhere. - Fixed crash while canceling the meta data unzip task. - Fixed replacing of the maintenance tool binary. - Introduced new classes for client-server communication. - Replaced qscript with js engine. - Added an example how to use dynamic pages. - Added an example how to ask for a license agreement. - Provided an API to get all wizard pages from java script. (QTIFW-477) - Made the installer binary paths available for scripts. (QTIFW-424) - Fixed possible case of uninstalling all of Windows. (QTIFW-511) - Stopped trying to get admin rights on Windows if impossible. - Introduced developer tool. - Improved checking of Installationpath and better reporting to user. (QTIFW-468, QTIFW-512) - Fixed detailwindow not following content. (QTIFW-353) - Log messages are now prefixed with a timestamp. (QTIFW-496) - Renamed settings inside config.xml: - UninstallerName renamed to MaintenanceToolName (default value changed to "maintenancetool") - UninstallerIniFile renamed to MaintenanceToolIniFile - Windows: Re-use parent console for verbose output if possible - Fixed disk space checking for updates. (QTIFW-434) - List disk space requirements prior to installation. (QTIFW-16) - Toggle details with verbose mode. (QTIFW-140) - Implemented progress indicator on taskbar on Windows. (QTIFW-15) - Added --framework-version argument. - Fixed the uninstaller calculator. - Fixed broken command line parsing. - Fixed binary data extraction. (QTIFW-574) - Fixed uninstall after installing into a target with elevated permissions. (QTIFW-447, QTIFW-479) - Allow spaces in RunProgramArguments. (QTIFW-227) - Removed some deprecated things. - Removed SetQtCreatorArrayValue, AddQtCreatorArrayValue operations. - Removed ApplyProductKey, ReplaceInstallNames, QtPatch operations. - Added systemInfo to the scripting API. (QTIFW-592) - Changed default of 'AllowSpaceInPath' from false to true. - Added possibility to specify a control script on installer creation. (QTIFW-166, QTIFW-495) - Introduced a setting for Wizard default height and width. (QTIFW-47) - Added CreateLocalRepository configuration option. - Simplified registration of objects into the script engine. - Made sure widgets from .ui files are properly registered to the engine. - No longer use Version as the repository format version. - Introduced InstallAction property for components. - Marked "os" variable as deprecated. - Show "Forced" component as enabled, but without checkbox. (QTIFW-491) - Fixed running out of sockets in server. - No longer show checkbox for autodependent components. - No longer defer deletion of sockets in RemoteObject. - Fixed loading of translations. - Set objectName for QThread. - Use local sockets for client-server communication. (QTIFW-228) - Fixed handling of incomplete messages in client-server communication. - Improved the examples and their documentation. - Added and improved documentation. (QTIFW-526) - Added translations: Polish, Italian - Updated and improved translations. - Fixed various bugs. (QTIFW-397, QTIFW-469, QTIFW-481, QTIFW-524, QTIFW-538, QTIFW-541, QTIFW-542, QTIFW-562, QTIFW-564, QTIFW-568, QTIFW-569, QTIFW-583, QTIFW-589, QTIFW-593, QTIFW-600, QTIFW-602, QTIFW-605, QTIFW-612, QTIFW-615, QTIFW-616, QTIFW-618, QTIFW-620, QTIFW-621, QTIFW-622, QTIFW-625, QTBUG-633) Thanks go to Christoph VogtlÃĪnder, Sze Howe Koh, Ray Donnelly, Tasuku Suzuki, Takayuki Orito, Sascha Cunz, Zhang Xingtao, Sergey Belyashov and Cuoghi Massimiliano for contributions. 1.6.0 - No longer requires Xcode command line tools on Mac. (QTBUG-38015) - Use local encoding to parse executable output in ConsumeOutput. - Avoid 'Too many open files' on Mac. - Fixed HTTP proxy settings not having any effect. (QTIFW-498) - Made it possible to calculate dependencies from script. (QTIFW-503) - Bugfixes (QTBUG-38343, QTIFW-488) - Code cleanup 1.5.1 - The checkupdates option handles no-network situations better. (QTIFW-431) - Fixed random crash while accessing arguments. - Fixed I18N on Windows. - Qt5 compatibility fixes. - Fixed for path handling on Windows. - Fixed preselection of components from another component. - Added operationExists method on script side. - Unified access to the supported schemes. - Implemented missing setNativeArguments wrapper. (QTIFW-310) - Fixed banner image not working in dynamic pages. (QTIFW-471) - Fixed hang when entering wrong sudo password. (QTIFW-409 and QTIFW-451) - Documentation improvements. - Updated translations. Thanks go to Takumi Asaki and Sergey Belyashov for contributions. 1.5.0 - Fixed generation of random temp directory name. (QTIFW-427) - Now reuses http proxy settings for https. - Allow a page to force showing the settings button. - Read qmake output even if it crashed. - Implemented factory to be able to insert wizard pages dynamically. - Open a console window (Windows) to show the verbose output. (QTIFW-403) - Added new settingsoperation. - Offline installer do not require any temporary space. - Now .dmg files are named after the application bundle on Mac OS X. - Introduced ApplicationsDir variable. (QTIFW-258) - Now never delete the install directory if it's not empty. - Environment variables changes are now propagated to the system. (QTIFW-391) - Build installers with accessibility plugin. (QTBUG-34296) - Improved documentation. - Minor bugfixes. 1.4.1 - Added support to pass a query string when downloading archives. (QTIFW-329) - Fixed progress display for redirected HTTP Downloads. (QTIFW-267) - Add support to repogen to update only newer components. (QTIFW-234) - Don't show RunProgram after uninstall. (QTIFW-366) - Fix broken random name generation for temporary directories. (QTIFW-354) - Removed unnecessary WindowModal in the gui. (QTIFW-364) - Made some previously missed elements scriptable. (QTIFW-372) - Fixed searching for magic marker. - Now create the temp remoterepo directory later to avoid conflicts. - Disable close button during installer run to avoid crashes. - Child repositories added by setTempRepository are no longer default. (QTIFW-373) - Ignore filtered repositories as early as possible to avoid hang. - Connect extract operation to progress calculation. (QTIFW-11, QTIFW-141) - Fixed target dir for root installations and empty AdminTargetDir. - Fixed broken dependency resolver. - Implemented a way to replace the default resource. - Renamed forceRestart to needsHardRestart. - Made installer apps retina-ready. - Restart on the wizard now cleans up component leftovers. - Read file content in case mmap fails (fallback). (QTIFW-400) - Added more autotests. - Improved documentation. - Minor bugfixes. 1.4 - Force updating of Essential components. (QTIFW-38, QTIFW-155) - Display release date in Updater and Packagemanager. (QTIFW-25) - Fixed a crash in the package manager. (QTIFW-313) - Fixed component selection showing wrong package sizes. (QTIFW-302) - Better handling of dependencies while updating. (QTIFW-318) - Now allows to ignore SSL errors. - Implemented dedicated translation settings support. - Added exceptionhandler code for connected signals/JS methods. - Now properly calculates the file size for symlinks. (QTIFW-137) - Fixed downloading when the server redirects. - Changed two fatals to warnings. - Added errorString to execute operation error output. - Implemented configuration interface. (QTIFW-196) - Introduced no_app_bundle.pri for Mac. - Added a change installer ui example. - Added entered/left signals to pages. - Forwarded packagemanager core to ProductKeyCheck class. - Added installerscriptengine. - Replaced the external date and time implementation. - Now only uses the basic LZMA SDK (instead of all of 7zip). - Forwarded make "check" target to autotests. - Added documentation for JS API. - Fixed messageboxhandler. - Added INSTALL file, cleaned up README. - Now sets the subTitle to " " if empty on Linux as well. - Now checks for os-release instead of lsb-release on Linux. - Added getrepositorycontent tool. - Now provides documentation for Vendorprefix parameter. - Fixed output of line number in log. - Fixed broken update behavior (in the size and description label). - Binarycreator now assumes offline installer if there are no repositories. (QTIFW-224) - Rewrote copy configuration function. (QTIFW-221) - Added banner pixmap to the wizard. - Removed previously deprecated Pages config.xml element. - Deprecated Icon, introduce replacements for config.xml. - Added component model behavior auto test and fix broken model. (QTIFW-84, QTIFW-213) - Made CreateShortcut operation a NOOP on non-Windows systems. - Added RunProgramArguments to config.xml. - Qt5 SDK specific fixes. - Minor documentation fixes and additions. - Added more autotests. - Bugfixes - Added Japanese translation. - Updated translations - Cleaned up the Code. 1.3.1 - Fixed missing magic cookie in installer binary on Mac. (QTIFW-322) - Fixed UNDO of MkDirOperation and CopyOperation. - Enabled Qt4 patch syntax in QtPatchOperation. - Added the OS attribute to be compatible with old sdks. - Show the error string not the empty errorString when parsing Settings. - Now continues on unknown operations. - Fixed error handling in 7z lib. - Added a DisplayName setting for repositories. (QTIFW-244) - Chinese Translation added - Documentation fixes and additions. - Bugfixes (QTIFW-271 et al) INSTALL000066400000000000000000000045251325366651500121310ustar00rootroot00000000000000How to build ===================== The instructions should help you build the Installer Framework from scratch. Get the sources --------------------- Use Git to check out the Qt Installer Framework sources that are hosted at: http://code.qt.io/cgit/installer-framework/installer-framework.git/ Build a static Qt --------------------- Building the Qt Installer Framework from sources requires Qt (version 5.5 or newer). Supported compilers are MSVC 2013 or newer, GCC 4.7 or newer, and Clang 3.1 or newer. If you want to ship your installer as a single file you have to build Qt and the Qt Installer Framework statically. See the Qt documentation for the prerequisites and steps to build Qt from sources. ### Windows Recommended configuration options for Microsoft Windows: configure -prefix %CD%\qtbase -release -static -static-runtime -target xp -accessibility -no-opengl -no-icu -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -skip qtactiveqt -skip qtenginio -skip qtlocation -skip qtmultimedia -skip qtserialport -skip qtquick1 -skip qtquickcontrols -skip qtscript -skip qtsensors -skip qtwebkit -skip qtwebsockets -skip qtxmlpatterns -skip qt3d ### Linux Recommended configuration options for Linux: configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-xcb -qt-pcre -qt-freetype -no-glib -no-cups -no-sql-sqlite -no-qml-debug -no-opengl -no-egl -no-xinput -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -skip qtactiveqt -skip qtenginio -skip qtlocation -skip qtmultimedia -skip qtserialport -skip qtquick1 -skip qtquickcontrols -skip qtscript -skip qtsensors -skip qtwebkit -skip qtwebsockets -skip qtxmlpatterns -skip qt3d ### OS X Recommended configuration options for OS X: configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -no-cups -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -skip qtactiveqt -skip qtenginio -skip qtlocation -skip qtmultimedia -skip qtserialport -skip qtquick1 -skip qtquickcontrols -skip qtscript -skip qtsensors -skip qtwebkit -skip qtwebsockets -skip qtxmlpatterns -skip qt3d Build the Framework --------------------- Run 'qmake && make' (or 'mingw32-make', 'nmake' ...) to build the Qt Installer Framework. The documentation can be generated by 'make docs'. LICENSE.FDL000066400000000000000000000555631325366651500125210ustar00rootroot00000000000000 GNU Free Documentation License Version 1.3, 3 November 2008 Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 0. PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. 1. APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque". Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only. The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. The "publisher" means any person or entity that distributes copies of the Document to the public. A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. 2. VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. 3. COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. 4. MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. C. State on the Title page the name of the publisher of the Modified Version, as the publisher. D. Preserve all the copyright notices of the Document. E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License. I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version. N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section. O. Preserve any Warranty Disclaimers. If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. 5. COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements". 6. COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. 7. AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. 8. TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. 9. TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License. However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it. 10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document. 11. RELICENSING "Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site. "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization. "Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document. An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008. The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing. ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this: with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. LICENSE.GPL3-EXCEPT000066400000000000000000001103131325366651500135700ustar00rootroot00000000000000This is the GNU General Public License version 3, annotated with The Qt Company GPL Exception 1.0: ------------------------------------------------------------------------- The Qt Company GPL Exception 1.0 Exception 1: As a special exception you may create a larger work which contains the output of this application and distribute that work under terms of your choice, so long as the work is not otherwise derived from or based on this application and so long as the work does not in itself generate output that contains the output from this application in its original or modified form. Exception 2: As a special exception, you have permission to combine this application with Plugins licensed under the terms of your choice, to produce an executable, and to copy and distribute the resulting executable under the terms of your choice. However, the executable must be accompanied by a prominent notice offering all users of the executable the entire source code to this application, excluding the source code of the independent modules, but including any changes you have made to this application, under the terms of this license. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . README000066400000000000000000000015031325366651500117510ustar00rootroot00000000000000The Qt Installer Framework provides a set of tools and utilities to create installers for the supported desktop Qt platforms: Linux, Microsoft Windows, and OS X. Documentation -------------------------- The binary packages for the Qt Installer Framework include documentation in the doc directory. The documentation is also available online at https://doc.qt.io/qtinstallerframework/index.html Notes -------------------------- To build an installer, it is advised to use a statically linked Qt (5.4 or newer). See the documentation at https://doc.qt.io/qtinstallerframework/ifw-getting-started.html Getting Help -------------------------- If you think you found a bug, please report it to https://bugreports.qt.io/browse/QTIFW General questions are best asked on interest@qt-project.org. dist/000077500000000000000000000000001325366651500120355ustar00rootroot00000000000000dist/README000066400000000000000000000001151325366651500127120ustar00rootroot00000000000000Contains the configuration for the installer of the Qt Installer Framework. dist/config/000077500000000000000000000000001325366651500133025ustar00rootroot00000000000000dist/config/config.xml000066400000000000000000000007341325366651500152750ustar00rootroot00000000000000 Qt Installer Framework Qt Installer Framework 3.0.4 3.0.4 Qt Project http://qt-project.org watermark.png Uninstaller @HomeDir@/Qt/QtIFW-3.0.4 dist/config/watermark.png000066400000000000000000000065461325366651500160200ustar00rootroot00000000000000‰PNG  IHDRĪ:Ŋp ôļPLTEĸĸĸŠŠŠŅŅŅkkk&&&éééôôôBBBÝÝÝČå­ļļļXXX}}}œœœÅÅÅĀáĄÐéđØėÄĸĸþÃCïũį„ÅIþĸþÚíĮļݕ˜Ïf…ÆJøûôÂâĪŨėÄĻÖ}îũæ’Ë]‚ÄFØíÅŪŲ‡õúðëöá‡ĮMíöäŊ؈šÏiÔëŋõúïĮäŦ­Ø…þþýÅãĻßðÏÍįīƒÅGÎčķųüöÃã͜ÐlüýúåóØøüõŠČRóųíýþûæóڞŅoËZîũåôúï‚ÄE‹ČRŪŲ†ÜîĘûýøˆĮOŲíÆĶÕzĻÖ~žß›ûýųĘY˜ÎfŨėßŌpēی†ÆLŸŌqŽĘWĪÔx§Õ|ýþüšß™ąÚ‹ŋᠰډéõÞųüõ–ÎcÄã§ũûóüþûŅéŧĘæ°™ÏhÂâĨĢÔw‡ÆLũûōÕëÁŠŨ€âņԋČSÄDÏčļŲíĮ—ÎeĮåŽĨÔyŌęžãōՌÉU•Ía™ÏgīÛÉæŊĢÓv•Íbúüũ”Ė`ÝïĖđޖĨÕzðøéŠČQÚîČÛîÉÓęūģÛŽŽØ„ĩܑިƒäōŨŅéš―āčôÝŠŨúýø°ÚŠėöâÁâĢĶÕ{ÉU—ÎdëõáŋៈĮN„ÅHōųėĀáĒ›Ðk†ÆKÜïËŧߙāðÐôúîíũäčôÜÏč·ŧߚÞïÍĐÖ”Í`“Ė^áņŌÉVÁâĒīܐÉåŪņøęáņÓūāžĄÓsĄÓtķܒōųëÆäŠéôÞŦŨ‚âņӝŅmÖëÁžŅnÃãĨēۍĐŨ€Ðč―Čâ·Íįĩ”ËcÞï΅ÅIįóÛęõßķݓĘXūāŸ–ÍcËæąÛîĘÂā­Óę―äō֑Ë\ ŌrÆäŦóųėļޖÅäĐ  Ī·Ý”‘Ë[ŒÉTĖįģ―āœāðŅöûōæóŲ ŌqÖėÂĒÓt€ÃBƒÄF€ÃCFĢd iIDATx^ėÎG€@°õïpŊQ‹ <ð:f‰üČ˜žä’’’’’’’’’’’’’’’’’’’’’’ÛØÛsÝõ,}YįL/;ũÕÔÆ`xïu… Ēc$#Ā–€"ÔP{/ĶÅôÞL‰{{Ü{ï-.é―ũÞ{ģÁƒ39Ōß įûÖŌdqÎɌūÛũۙg–ŅîÚQ9ģ―zęæb!S{ˆĢ ŋpäO_o1o­& Ģũ ŠŦįŒüj;ųï1ŪøvŨõR^Čū 2ëŅýrüÆpR3{ä"2įĐŲp'öŲq†ČUÄÕÉĐÞĢúüäqōpžpä7_oíž"S‡ˆ;FïŸ|ī~žąÄcün aŨÁD·#ũ·ÎPwCïFd ™ŊĐéų#ÖMČ%d>ĮMȇ2xĪéAzöŒU~%/ė~õ9Q‘šÚýY3‹m…_#Ōū7SķŦÝđA0ä‰Gë7OŠ„ž`qžßîg'ō1ĪÎō@þšėôģMN‰ãMyđŒ‘Ï*Rh”QŽN[d@˜â1cX’"qAŪ—”į)$)æ‚ôũÁĶäÂNģ7Dnʀ’H.Č…ØØ}q"Ø·nĘ<(IâôĮÆÆéŌ”SČÅhȕȸýLļ›LÃ;ōÄŲ›{āZķ€Ð59ÏđõHPâ,4Xg 1<yĻŊ…Đ•†øļé`Ėį€<›ģÉ[(['Âō—5VĩÄųĘÁĻéúhųt"h oßΎåÖ%0@vĢ܀Ģ/-™‰G͙‡ōŽA ­æˆžŠōZĢ@:Äyį\@zžēÕDYMÃ؈ę6YÍ-‚ō<ŠiōH[ŧ –WvP@ęuoK>äÓĻÆČsˆĢ“ÁKÂsû™‚ųGŽ ,ÏI .ĶHÆwœeĻŠåųˆEܐaĘH ˆđܐT­ōÜâëܐĻŠäY â+ĸĪ7ĪŠ‘JWĻVnH\―äY…NģéúŸûE„ĸƒĢŒô|šÏĄjUB6ˆz[ėŅrßėïÝKđ!ŸDuDž}A|›r?ŠË”žä*đ![Q “įS n憞ƒj–<§‚Î đÕhyNĪ­pCnTüį€Ī4~H›Ę'q-ÉĖIŪĄ\‡c:Hũ8"Í(?Žc(HE‘[”n9Ý rDū‹r“ÓŊĄ"Gdv2ĖÚpģhąōD’`ԋņõ Œãö=ŽĶJ@2D†'ÂßK‹.“†ũĶ(ājöÔV $éwvŠĄvšQ*=Ņ'óöcLGČņœ™Sü ™CŽ–ÛM“ ’$!å%ÛT°ípôî@ĀcĀ؃W~ș e1ĀbĢōœdVnZYrzą"ę(æ…ĖhV>č'°Ŧû™’„*ŊõÂ]ƒ WÓ FHōĶ‚ŅdĀŧ·Ō–lūe$ސš§Æß[äËŲ·†O™[î›/ŋO… ÄéĘV&Ōû“‡ÚK‰HH’ē^~ĄnîŽïÞœËũĩĀŋDзĢáŲÜ,(’Ž‚ODĒ"?Č0 ČJļýá܌6VČĄ,€LühNČŧސD…öC>þäĮÏf‹,b†ž-ÉgÁl‘AːDã22rM―‹HƒÄINŧˆŒd‰ÔUļ„Œg‰$ķXWÃ$œÚ+sFÖiY ņŠ:+bŪhÆîíNøb6Ɩ@‰Ōõ)·HÂ#ũ–G4~)Iâ"㞈 ŽÐb$æES€PHĖĐũÏÅ<þHĖ }taˆ…Ä<ᐘ'óÄCtžxȀŋĄŋ #ó„CbžHHÄû·{Ëu܆Á,’Ēnd?š…öĐ{éæŧ›šTÔQėÎL3ƒ q0ąėüâwd;—y8#ũßCþņžý=CԐ†4Ī! iHCԐ†4Ī! iHCԐ†4Ī! iHCԐ†4Ī!Έ{ø Ęlôadņ0Š‚2h1Æ"+@BĒ}KøĶ·ŨÆÂŲZür˜ļw'MþĨ12äĮVƒþ1dšÆ·+ĀkdŋxéCČ éič‹Ã4ØZ1Ę  ĄôÆė―—Ĩ3pK~…Ą.Ĩß\õ)Į:“Œf‡{d^HōÐ|ÖÓÞäĮ5‘|ؘoYeo.nAZWŽĪ X&buUÐQrK‡[dzF6;6ðŅđРęH–Ŧ•ø`Ø5’–øz%ųpˆPá:råĐÃR'Z‘tŽ—ĩé@ 2~ÎCýßd“đTĨ'Ã/™S‡[Ī;­äĐCXŽŪH†øČä/ņizšŦ Đ9ÛĩÃ-’ž‘8=ĩ’ÂâŽŲûô„@­ŽãÅ;+ Ī[:švļAĶHģ†ŽŨ;ŊH‚Y_õtM†’€epép{woĸˆĖ°ŅĢĒŦĀUå+2ÓĢƌAރ2ęčÚáYãđjnwgĪķŋT‚š(ēŧžn]:Ü ĮûØY ]/Üô„ėô”OSQÂЙĨÃûïÝ9^‘ÎO|‘mĨį-óĩcūŒ‡ļūėī0P,Ą3rdâЃkžß| ⎈žiE†ī!vöú”Œ[âŽH— !Vį6€Žļ5ĄkÍ·Ý ›ŧ"]–ĐÐÃĐô—HådĐTVĪ‹yė5ĘV+48%=ÞFöĪÁÜRĩ"ŋ"jĶ:DžûúPˆČ]kđņˆVI™7t *—X<Ívm/Bą/bĸš iHCԐ†Īð1@ĪøÍȀRåEðĩč+ôÍH‚Œ˜Ų—ŊCBōGÕ…ÔlĖ)Þ#ŊúŠt‘…CŲįpĀk86hZjũđļˆAu_‘ĻaÂ%Ņãã >ÍU"ØŅ{…Íw’#ŪúmFß@š-9·3rp˜Z%–e°ŧīé/SWdö2ðyF8(ĀŪ3#Ä#G.ÂN*ƒĀUÞå ĘŒūƒDpŽqtŸÔR Čá$?ÎU1ðxT Π%*úž%ߝ`[–_„$mŽ|ïcBؘŅ7‘QÓ –‰Đ6…DÞĨýzã—PįĻđ:ŋÁÅmþ҈dŌąÎaė™Ņ·ėFÍNĪ*’Ĩ‘†ËéV—@OQąËšc“xCųYĢh"G―ƒl]TZŊǘVĪĻ „STtšæÂÁÃĄŽHZ[ÏčČ äė„ĘjĐz:vŲÕ°á éúķu§ŅtÍYĶ €i%ĨmAÎč=ēdĻŌ°‰'Œĸŋ ŒÃ’ŽAl}|Ą™éĻcTSzŽ@ni<49K)ŊHdņE.ŅāũŨČĢRÖ§ĮÎÞë―ē%Ï=diĮĀ‹!rŸ1Ōåd^ĢIcĐŦ> ė RÆÞsäÕČy­@õœ…õĢE™ÞïĒģ Dũš"QļF)ū‡\/ŽkUvïVîîÛëۑ‘*Ũũ„ËũFVïŪUüraßå{ąOæß­ iHCԐ†4Ī! iHCԐ†4Ī! iHCԐ†4Ī! iHCԐ†4Ī! iHCōPéIõ(’ RIENDŪB`‚dist/packages/000077500000000000000000000000001325366651500136135ustar00rootroot00000000000000dist/packages/org.qtproject.ifw.binaries/000077500000000000000000000000001325366651500207735ustar00rootroot00000000000000dist/packages/org.qtproject.ifw.binaries/meta/000077500000000000000000000000001325366651500217215ustar00rootroot00000000000000dist/packages/org.qtproject.ifw.binaries/meta/package.xml000066400000000000000000000004441325366651500240400ustar00rootroot00000000000000 Qt Installer Framework Binaries Installs the binaries, examples and help files. 3.0.4 2018-03-20 True dist/packages/org.qtproject.ifw/000077500000000000000000000000001325366651500172005ustar00rootroot00000000000000dist/packages/org.qtproject.ifw/meta/000077500000000000000000000000001325366651500201265ustar00rootroot00000000000000dist/packages/org.qtproject.ifw/meta/3RDPARTY000066400000000000000000000014021325366651500212560ustar00rootroot00000000000000The Qt Installer Framework sources include third party code: ============================================================ LZMA SDK Parts of the sources from the LZMA SDK , version 9.20 are under src/libs/7zip/unix and src/libs/7z/win. The LZMA SDK is written and placed in the public domain by Igor Pavlov. Some code in LZMA SDK is based on public domain code from another developers: 1) PPMd var.H (2001): Dmitry Shkarin 2) SHA-256: Wei Dai (Crypto++ library) ============================================================ KD Tools Sources for the KD Tools library are under src/libs/kdtools. The library is released under the same licenses as the rest of the installer framework. The copyright is owned by Klaralvdalens Datakonsult AB. dist/packages/org.qtproject.ifw/meta/LICENSE.GPL3-EXCEPT000066400000000000000000001103131325366651500226240ustar00rootroot00000000000000This is the GNU General Public License version 3, annotated with The Qt Company GPL Exception 1.0: ------------------------------------------------------------------------- The Qt Company GPL Exception 1.0 Exception 1: As a special exception you may create a larger work which contains the output of this application and distribute that work under terms of your choice, so long as the work is not otherwise derived from or based on this application and so long as the work does not in itself generate output that contains the output from this application in its original or modified form. Exception 2: As a special exception, you have permission to combine this application with Plugins licensed under the terms of your choice, to produce an executable, and to copy and distribute the resulting executable under the terms of your choice. However, the executable must be accompanied by a prominent notice offering all users of the executable the entire source code to this application, excluding the source code of the independent modules, but including any changes you have made to this application, under the terms of this license. ------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . dist/packages/org.qtproject.ifw/meta/installscript.qs000066400000000000000000000035441325366651500233740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { // Install to @RootDir@ instead of @HomeDir@ on Windows if (installer.value("os") === "win") { var homeDir = installer.value("HomeDir"); var targetDir = installer.value("TargetDir").replace(homeDir, "@RootDir@"); installer.setValue("TargetDir", targetDir); } // do not show component selection page installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); // no startmenu entry so no need to ask where to create it installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false); } dist/packages/org.qtproject.ifw/meta/package.xml000066400000000000000000000007311325366651500222440ustar00rootroot00000000000000 Qt Installer Framework Installs the Qt Installer Framework. 3.0.2 2017-11-21 doc/000077500000000000000000000000001325366651500116375ustar00rootroot00000000000000doc/config/000077500000000000000000000000001325366651500131045ustar00rootroot00000000000000doc/config/ifw.qdocconf000066400000000000000000000144331325366651500154140ustar00rootroot00000000000000include($QT_INSTALL_DOCS/global/qt-cpp-defines.qdocconf) include($QT_INSTALL_DOCS/global/compat.qdocconf) include($QT_INSTALL_DOCS/global/fileextensions.qdocconf) language = Cpp project = "Qt Installer Framework" description = "Qt Installer Framework Manual" url = http://qt-project.org/doc/qtinstallerframework/ sourcedirs += ../../src/libs/installer ../../src/libs/kdtools ../includes headerdirs += ../../src/libs/installer ../../src/libs/kdtools imagedirs = $SRCDIR/images $SRCDIR/templates/images outputdir = $OUTDIR exampledirs = $SRCDIR ../../examples headers.fileextensions = "*.h" sources.fileextensions = "*.qdoc *.qdocinc *.cpp" examples.fileextensions = "*.js *.qs *.txt *.xml *.ui *.ts" examples.imageextensions = "*.png" indexes += $QT_INSTALL_DOCS/qtcore/qtcore.index \ $QT_INSTALL_DOCS/qtwidgets/qtwidgets.index \ $QT_INSTALL_DOCS/qtqml/qtqml.index \ $QT_INSTALL_DOCS/qtxml/qtxml.index \ $QT_INSTALL_DOCS/qtconcurrent/qtconcurrent.index \ $QT_INSTALL_DOCS/qtnetwork/qtnetwork.index \ $QT_INSTALL_DOCS/qtdoc/qtdoc.index qhp.projects = InstallerFramework qhp.InstallerFramework.file = ifw.qhp qhp.InstallerFramework.namespace = org.qt-project.ifw.$IFW_VERSION_TAG qhp.InstallerFramework.virtualFolder = doc qhp.InstallerFramework.indexTitle = Qt Installer Framework Manual qhp.InstallerFramework.filterAttributes = ifw qhp.InstallerFramework.customFilters.InstallerFramework.name = Installer Framework qhp.InstallerFramework.customFilters.InstallerFramework.filterAttributes = ifw qhp.InstallerFramework.indexRoot = qhp.InstallerFramework.subprojects = manual qhp.InstallerFramework.subprojects.manual.title = Qt Installer Framework Manual qhp.InstallerFramework.subprojects.manual.indexTitle = Qt Installer Framework Manual qhp.InstallerFramework.subprojects.manual.type = manual macro.ifwversion = $IFW_VERSION # commands borrowed from qtbase/doc/global/macros.qdocconf macro.gui = "\\b" macro.key = "\\b" # Doxygen compatibility commands macro.see = "\\sa" macro.function = "\\fn" # We 'misuse' QML doc commands to generate JS documentation # (only works with qdoc from Qt 5) outputprefixes = QML outputprefixes.QML = navigation.homepage = "Qt Installer Framework Manual" defines += ABSTRACTTASK_H \ ADMINAUTHORIZATION_H \ COMPONENT_P_H \ COMPONENTCHECKER_H \ CONSTANTS_H \ CONSUMEOUTPUTOPERATION_H \ COPYDIRECTORYOPERATION_H \ COPYFILETASK_H \ CREATEDESKTOPENTRYOPERATION_H \ CREATELINKOPERATION_H \ CREATELOCALREPOSITORYOPERATION_H \ CREATESHORTCUTOPERATION_H \ DOWNLOADARCHIVESJOB_H \ DOWNLOADFILETASK_H \ DOWNLOADFILETASK_P_H \ ELEVATEDEXECUTEOPERATION_H \ ENVIRONMENTVARIABLESOPERATION_H \ ERRORS_H \ EXTRACTARCHIVEOPERATION_H \ EXTRACTARCHIVEOPERATION_P_H \ FAKESTOPPROCESSFORUPDATEOPERATION_H \ FILEIO_H \ FILEUTILS_H \ GLOBALS_H \ GLOBALSETTINGSOPERATION_H \ GRAPH_H \ INSTALLERCALCULATOR_H \ INSTALLICONSOPERATION_H \ KD_UPDATER_H \ KD_UPDATER_UPDATE_INFO_DATA_H \ KD_UPDATER_UPDATE_INFO_H \ KD_UPDATER_UPDATE_OPERATIONS_H \ KDSYSINFO_H \ KEEPALIVEOBJECT_H \ LIBINSTALLER_ENVIRONMENT_H \ LICENSEOPERATION_H \ LINEREPLACEOPERATION_H \ METADATAJOB_H \ METADATAJOB_P_H \ MINIMUMPROGRESSOPERATION_H \ OBSERVER_H \ PACKAGEMANAGERCORE_P_H \ PACKAGEMANAGERCOREDATA_H \ PACKAGEMANAGERPAGEFACTORY_H \ PACKAGEMANAGERPROXYFACTORY_H \ PERMISSIONSETTINGS_H \ PROGRESSCOORDINATOR_H \ PROTOCOL_H \ PROXYCREDENTIALSDIALOG_H \ Q_OS_.* \ QINSTALLER_FILEUTILS_H \ QINSTALLER_GLOBAL_H \ QINSTALLER_INIT_H \ QINSTALLER_UTILS_H \ QPROCESSWRAPPER_H \ QSETTINGSWRAPPER_H \ QT_VERSION \ REGISTERFILETYPEOPERATION_H \ REMOTECLIENT_H \ REMOTECLIENT_P_H \ REMOTEFILEENGINE_H \ REMOTEOBJECT_H \ REMOTESERVER_H \ REMOTESERVER_P_H \ REMOTESERVERCONNECTION_H \ REPLACEOPERATION_H \ REPOSITORY_H \ SCRIPTENGINE_P_H \ SELFRESTARTOPERATION_H \ SERVERAUTHENTICATIONDIALOG_H \ SETTINGS_H \ SETTINGSOPERATION_H \ SIMPLEMOVEFILEOPERATION_H \ TESTREPOSITORY_H \ UI_PROXYCREDENTIALSDIALOG_H \ UI_SERVERAUTHENTICATIONDIALOG_H \ UNINSTALLERCALCULATOR_H \ UNZIPTASK_H doc/config/style/000077500000000000000000000000001325366651500142445ustar00rootroot00000000000000doc/config/style/qt5-sidebar.html000066400000000000000000000015741325366651500172610ustar00rootroot00000000000000

Qt Installer Framework Manual

doc/doc.pri000066400000000000000000000035621325366651500131260ustar00rootroot00000000000000# Adapted from doc/doc.pri in Qt Creator. QDOC_BIN = $$[QT_INSTALL_BINS]/qdoc win32:QDOC_BIN = $$replace(QDOC_BIN, "/", "\\") IFW_VERSION_TAG = $$replace(IFW_VERSION_STR, "[-.]", ) defineReplace(cmdEnv) { !equals(QMAKE_DIR_SEP, /): 1 ~= s,^(.*)$,(set \\1) &&,g return("$$1") } QDOC = $$cmdEnv(SRCDIR=$$PWD OUTDIR=$$OUT_PWD/doc/html IFW_VERSION=$$IFW_VERSION_STR IFW_VERSION_TAG=$$IFW_VERSION_TAG QT_INSTALL_DOCS=$$[QT_INSTALL_DOCS]) $$QDOC_BIN unix { HELPGENERATOR = $$[QT_INSTALL_BINS]/qhelpgenerator } else { # Always run qhelpgenerator inside its own cmd; this is a workaround for # an unusual bug which causes qhelpgenerator.exe to do nothing HELPGENERATOR = cmd /C $$replace($$list($$[QT_INSTALL_BINS]/qhelpgenerator.exe), "/", "\\") } HELPGENERATOR = $$HELPGENERATOR -platform minimal QHP_FILE = $$OUT_PWD/doc/html/ifw.qhp QCH_FILE = $$OUT_PWD/doc/ifw.qch html_docs.commands = $$QDOC $$PWD/installerfw.qdocconf html_docs.files = $$QHP_FILE html_docs_online.commands = $$QDOC $$PWD/installerfw-online.qdocconf html_docs_online.files = $$QHP_FILE qch_docs.commands = $$HELPGENERATOR -o $$QCH_FILE $$QHP_FILE qch_docs.depends += html_docs qch_docs.files = $$QCH_FILE docs_online.depends = html_docs_online QMAKE_EXTRA_TARGETS += html_docs_online docs_online macx { DOC_DIR = "$${OUT_PWD}/bin/Ifw.app/Contents/Resources/doc" cp_docs.commands = mkdir -p \"$${DOC_DIR}\" ; $${QMAKE_COPY} \"$${QCH_FILE}\" \"$${DOC_DIR}\" cp_docs.depends += qch_docs docs.depends = cp_docs QMAKE_EXTRA_TARGETS += html_docs qch_docs cp_docs docs ht } !macx { docs.depends = qch_docs QMAKE_EXTRA_TARGETS += html_docs qch_docs docs } OTHER_FILES = $$HELP_DEP_FILES fixnavi.commands = \ cd $$replace(PWD, "/", $$QMAKE_DIR_SEP) && \ perl fixnavi.pl installerfw.qdoc . QMAKE_EXTRA_TARGETS += fixnavi doc/examples/000077500000000000000000000000001325366651500134555ustar00rootroot00000000000000doc/examples/config.xml000066400000000000000000000024371325366651500154520ustar00rootroot00000000000000 Some Application 1.0.0 Some Application Setup Your Company http://www.your-fantastic-company.com installericon installericon logo.png watermark.png @TargetDir@/YourAppToRun Argument 1 Argument 2 My nice application Some Application Entry Dir SDKMaintenanceTool true background.png @HomeDir@/testinstall @RootDir@/testinstall http://www.your-repo-location/packages/ doc/examples/package.xml000066400000000000000000000023551325366651500155770ustar00rootroot00000000000000 QtGui Qt gui libraries Qt GUI Bibliotheken 1.2.3 2009-04-23 com.vendor.root.component2 com.vendor.root.component1 false specialpage.ui errorpage.ui sv_se.qm de_de.qm component2.7z, component2a.7z com.vendor.root.component3 123 This changed compared to the last release false false false com.vendor.root.component2old doc/fixnavi.pl000066400000000000000000000173121325366651500136440ustar00rootroot00000000000000#!/usr/bin/env perl ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# use warnings; use strict; sub visitDir($) { my ($dir) = @_; my @ret = (); my @subret = (); opendir DIR, $dir or die "$dir: $!\n"; my @ents = readdir DIR; closedir DIR; for my $ent (grep !/^\./, @ents) { my $ret = $dir."/".$ent; if (-d $ret) { push @subret, &visitDir($ret); } elsif ($ret =~ /\.qdoc$/) { push @ret, $ret; } } return @ret, @subret; } my @files = (); my %defines = (); for (@ARGV) { if (/^-D(.*)$/) { $defines{$1} = 1; } elsif (/^-/) { printf STDERR "Unknown option '".$_."'\n"; exit 1; } else { if (-d $_) { push @files, visitDir($_); } else { push @files, $_; } } } int(@files) or die "usage: $0 [-D]... ...\n"; my @toc = (); my %title2page = (); my $doctitle = ""; my %prev_skips = (); my %next_skips = (); my %prev_define_skips = (); my %next_define_skips = (); my %prev_polarity_skips = (); my %next_polarity_skips = (); for my $file (@files) { my ($curpage, $inhdr, $intoc, $inif) = ("", 0, 0, 0); my ($define_skip, $polarity_skip, $skipping) = ("", 0, 0); my ($prev_define_skip, $prev_polarity_skip, $prev_skip, $next_define_skip, $next_polarity_skip, $next_skip) = ("", 0, "", "", 0, ""); open FILE, $file or die "File $file cannot be opened.\n"; while () { if (/^\h*\\if\h+defined\h*\(\h*(\H+)\h*\)/) { die "Nested \\if at $file:$.\n" if ($inif); $inif = 1; $skipping = !defined($defines{$1}); if ($inhdr) { $define_skip = $1; $polarity_skip = $skipping; } } elsif (/^\h*\\else/) { die "Unmatched \\else in $file:$.\n" if (!$inif); $skipping = 1 - $skipping; } elsif (/^\h*\\endif/) { die "Unmatched \\endif in $file:$.\n" if (!$inif); $inif = 0; $skipping = 0; $define_skip = ""; } elsif (keys(%title2page) == 1 && /^\h*\\list/) { $intoc++; } elsif ($intoc) { if (/^\h*\\endlist/) { $intoc--; } elsif (!$skipping && /^\h*\\o\h+\\l\h*{(.*)}$/) { push @toc, $1; } } elsif ($inhdr) { if (/^\h*\\previouspage\h+(\H+)/) { $prev_skip = $1 if ($skipping); ($prev_define_skip, $prev_polarity_skip) = ($define_skip, $polarity_skip); } elsif (/^\h*\\nextpage\h+(\H+)/) { $next_skip = $1 if ($skipping); ($next_define_skip, $next_polarity_skip) = ($define_skip, $polarity_skip); } elsif (/^\h*\\page\h+(\H+)/) { $curpage = $1; } elsif (/^\h*\\title\h+(.+)$/) { if ($curpage eq "") { die "Title '$1' appears in no \\page.\n"; } if (length($prev_define_skip)) { ($prev_define_skips{$1}, $prev_polarity_skips{$1}, $prev_skips{$1}) = ($prev_define_skip, $prev_polarity_skip, $prev_skip); $prev_define_skip = $prev_skip = ""; } if (length($next_define_skip)) { ($next_define_skips{$1}, $next_polarity_skips{$1}, $next_skips{$1}) = ($next_define_skip, $next_polarity_skip, $next_skip); $next_define_skip = $next_skip = ""; } $title2page{$1} = $curpage; $doctitle = $1 if (!$doctitle); $curpage = ""; $inhdr = 0; } } else { if (/^\h*\\contentspage\b/) { $inhdr = 1; } } } die "Missing \\title in $file\n" if ($inhdr); die "Unclosed TOC in $file\n" if ($intoc); close FILE; } my %prev = (); my %next = (); my $last = $doctitle; for my $title (@toc) { $next{$last} = $title2page{$title}; $prev{$title} = $title2page{$last}; $last = $title; } for my $file (@files) { open IN, $file or die "File $file cannot be opened a second time?!\n"; open OUT, '>'.$file.".out" or die "File $file.out cannot be created.\n"; my $cutting = 0; while () { if (!$cutting) { if (/^\h*\\contentspage/) { $cutting = 1; } } else { if (/^\h*\\title\h+(.+)$/) { if (defined($prev_define_skips{$1})) { print OUT " \\if defined(".$prev_define_skips{$1}.")\n"; if ($prev_polarity_skips{$1}) { print OUT " \\previouspage ".$prev_skips{$1} if ($prev_skips{$1}); print OUT " \\else\n"; } } print OUT " \\previouspage ".$prev{$1} if ($prev{$1}); if (defined($prev_define_skips{$1})) { if (!$prev_polarity_skips{$1}) { print OUT " \\else\n"; print OUT " \\previouspage ".$prev_skips{$1} if ($prev_skips{$1}); } print OUT " \\endif\n"; } print OUT " \\page ".$title2page{$1}; if (defined($next_define_skips{$1})) { print OUT " \\if defined(".$next_define_skips{$1}.")\n"; if ($next_polarity_skips{$1}) { print OUT " \\nextpage ".$next_skips{$1} if ($next_skips{$1}); print OUT " \\else\n"; } } print OUT " \\nextpage ".$next{$1} if ($next{$1}); if (defined($next_define_skips{$1})) { if (!$next_polarity_skips{$1}) { print OUT " \\else\n"; print OUT " \\nextpage ".$next_skips{$1} if ($next_skips{$1}); } print OUT " \\endif\n"; } print OUT "\n"; $cutting = 0; } else { next; } } print OUT $_; } close OUT; close IN; rename($file.".out", $file) or die "Cannot replace $file with new version.\n"; } doc/images/000077500000000000000000000000001325366651500131045ustar00rootroot00000000000000doc/images/arrow.png000066400000000000000000000020571325366651500147500ustar00rootroot00000000000000‰PNG  IHDR,oštEXtSoftwareAdobe ImageReadyqÉe<$iTXtXML:com.adobe.xmp ÂüH–ĄIDATxÚbüĸĸ?ÃPL C Œ:xÔÁĢĶ3`(‹ÛÛÛU€ÔtņĘĘĘAÂ@Ž1”’„?Ïb­Ąâāũ@l ÄˁØb(8˜JëņJ ŽbđĄRJ€ÚÄ>ƒķ”äVŨ) .âģƒŲÁlPz#ņ―Áž†9€x'ëØvp;gņ[’ręh~ÔÁĢuððr0@€oó!nŠIENDŪB`‚doc/images/ifw-add-components-introduction.png000066400000000000000000002416041325366651500220360ustar00rootroot00000000000000‰PNG  IHDRÎbðZŊrCKIDATxėVŲoTį·*å5Ę_žķQĢ<”Ļ…mœBX毯ÆûŠmlĖRlķīÆ6c°ÂĶ/ÄöxžÎxÖ;w™{įÎÝlÏӇŠJ’JmĨž™/>šđŸ|;Ŋ RŸÏ=įw–ûŧįHrŧ݋‹‹ĸ>ūüōKÚc:‘~D&Ŋūú C)øxĶÎīÕūÂSŌ‡fþ_Ÿ'Ý=öĘۜô&ŊRZŦ•šŌüN{<-}Ð~fõŸVøøÉóÏ?ĸâ‹/>ũÜs_/ãņãĮä$@ŋ%ôÍ7ߐ= €ĖÅ‚ö ,Õčâ8 ÂLĶņčŅ#ëĻ”aķąøSŅĢ?„>€Ģ>–ē6–ūŦŨ‡vŌē Į^tÚčƒßAŋ‚™ĸúØG‚}MýøÃéxÆõŸÖ—^zé…^HÓuýÁƒðÓŧīī'ãņøĀRð—xH O3‰ --z 6ÅtK5ÂÁfáiSG5―ˆ ŽúXĒ6úÐőđRČ2Ī―>ēe Bx–õĸjôąŸ*u}Ė)–aÉJqZÚXýþ žLaõú ‘Š&ÏŽ>ˆUęCšŪ§-,,ĖÍÍÍąxÜÐu‘ãŊ‡g5ÅbĀ„œx‰Į0 Q:§Ķi$jvtå6ĀøBĢĻ ŒDĒp"Ísk⁎čĮŲ,|K s_‚e$Ļ­æĒLzH$`ôYX\zļô`‘Īš_|ÏA}–ëĖĀĸ?ęƒYŒĸW}Åþ%‡KņØ­VKþ+ŧHN b,›š>ôš!bÐ!ģ0qðca"ļ–&ĸÏ1!BH+|qČ2,Ú˃v|īŒĨš?õA'ųĐëƒÎÔõA'ÔZZô™{}Rŋ_pŪF8Ÿ@āÐú`q‹>ˆ4= Č$ų„ōxÚŽĸõÆÃŊþüčëŋj*+$4Įõôôīķķ655ĩīī|ôŅGn·›”œÃëŸ=Ņ|8kĸ›oWŋ–Q·ņāĐJĮÄp"27Ŋ/ņ>" =MÓčt>Ēa)Ž XēEÂ2˜}ČBĀĘėHÖÝÜŨNŸYq9î;§<Š>—tœ{rØ1-ƒeŌ i?2îŠĻ:=6DaIh}Ā/ąÞI—_KVĢ5_ޜ…˜.ßëíėčžzŦŸ•Ø|‚ãÁ_dU‰ð#ƒƒũî}60<&EÁoýâP ēĖú`S‹>–ą“}õāŒÓ ÃâÓûƒ|, †"ņΑĄþþ{}ƒ žš HxL―}íōĩÁÍ vƒZTrU“·@ũOŽŧ|žNū#ÅīßúF€ũ™Đčƒ2æÚčCŊÚī>Đß/œ“æÐúāi!✨•ô#E}0—š<>iŠĒD“PUUI"ār}Õõ›uRÓЇÓĢZgÛÝ­›Žo|}ĒŋēdY&|Ļg0lkkëîîęrwûöíæææééi(Ej-a+ŠË3ģŧ>§Ēië` “yĮøÞWē2ũū9ėŌT 'šĀiöĻËĀGtœipĒaq"°Úh ŸŽD`ŅĮ\Į\ŲRßœvú(\ÃïŨmČŠv̰ j`QÆo3ęf•yCSðÉĄŒŠÏ‡'>ÉzũČ#ÁŨ„âЀ„āāŸđũųĪŽ€W?€47b ã]M9u­ŒĪāQPÍÐDÏņ}Eéé;ęÏ4îJOÏ*?80™ÓdGßļOœ3429ŒtÜ)ßē!·ŽzOUYõŅSnV1t ûEáíĒōīcðþļG7ôeņß߇$ÆFĢIýuc^mŊÎŦþðþ)Ū'A’NŠéŧÃ5 ŽÜĖܔžWūgOeiÝévŸ úō‘á ! aëņ˜ÜPđskŊŽ@ ã‘Iŋ+˜x!Ęų&û‡'Dň/Čgũ–üáōPŦÂĄ$°žKÉïĩŌþŊOŊIïTĀÕĩŋ_–{Ar +Óũ wa™ū2XŸū_ĨîŨFðĪE"I’ä$ĀŨ''.žüģŋŠýwĮ…^―üŊ΋_ŸŽŋĩæÕķŌRį#I!nÝšÕÞÞÎ0 þę‹ĒĸÉíęęË&[DÏ]nÜ^ŋæŪôũE_ðoýė}# m…-Ÿ­>ē yčA?9ĄZd`;bc LE$Āēæⷈ€-ú` Ö5†” @“Íú`/l‡ŽcĢ,2§wŪųéËéíwĶī……ĢgóÚWÞÜvĀ#ëaÖïtŒŽ:Æýސxĩ073!ƒgƒ!6äŸsNøBžfĻCįOo);106Åpb˜ Œ9 +Ā °.áPĀ Ir4Ė…–ųfɂd<V7”Ą?ŸYĸË-7&…ÅÅE-pŋ`íúŠú>äŠÛ‘YÓþYŌ‰ÚŠę­Ė/ ęą9CՐ'āóē;3éœtyÑĻĖ{ÎÖūSXsÜéaā{H|Ā9:<>=+@ŦˆÄ2– ÍLONûB\0ÝÎąąÉNn„ øƒÁ k|ŌāyŋĮĮ†Z‰œkÂ9::ęe€%Ŧ҈gj|Ä1Î,Edy0ĸ@wîî#Ìū0gĀ]füž'‚ÏĮžŽ †|nÐŌ9åæEy~>rzoq}Û YUü^? ŪhTųƒ!>ô:ԉ ÖTąįŌ‰‚ƒãþ°a<ģA–‹DYf&ĮGF>F„ _ Äóė„sl ū‘ąßú:9Đïú ŸŽšÛ°Þ/KYz‡ŅCß/:7…ûøŅč‡Ó`DøCÎ$Đ#ë]ŨkŊ<ÚđõÛĘĒėŦüķšäqnFtÃkíëŨú].Q’ŸĪtvvöõõy<†a8Ž …B^ŊwddäŌĨK~ŋŸ”!‘ũ~_ÓŧåJhŊÔp[9{CnėdßŦÚYÔļÅëó+C NEyžGNK31d~„7Į1č"Â2cÎ2§˜›N…ß+‚DéjæÁ°,9iÃFõ)|kíš Å ’&_oy[úÛģũÍĘŅŧ JƊr2ŠëšƒĒý$ŊüØ4q^ŋPZ\\ĩŧ8cûæâÚæ€ß}nÎ/Öm-Ŧ:ðГék? Y…‰ŽsLDqt7—;†ģ§ĩīĪ„d՞óó •ĪˆŽIÞæc9'ŊɊ EQŊޚ_{ģŧuÓúoïČmlŋŇ"Āú{ŠōŠn8ƒĄ@€ ņë>[™YX―·0?sË͌ ―SÂLïķŨŨl|csU}ŧÏ3ÕrĪ&ŋĻ`WÎŪó=C҈ÐqŠŠ xw~aé™OGŪÔïݕS‘“óÖ[;öļ&éáķ#ĩđY™9;ĘšnŽ}ü~å{W†YļvîøöĖÂōĒü–ÞAXýÏŪ5æäįV>ï‰IDĒQÏ―îÜēC7'‚L H|ĮŅüšÆŦQUáž#5ųŨ‡Į>Ž?PZZôÎķĖc—û5]:YWräâ %â=TđįâõŅĻĄÝýSmų‰ËĒînn(-,ĘÎĖØÝÐåõOĘÞžnÓķ‚ŠFW˜k9XvĒýN8Ė}üĮšßeï*ĖÍ+ĐûÜũAP!3|Ī”c–+-ĮrtMĶeÆÃ^‚hĪ„”2kLÉdzøšŌSŅÄAáäáĢÆĶ0'ČghĘëÞ{ÞgÏŲĩŨYw•Ë5ÉŋþŧũũýûÛĸųũ>W\Þđ>ŽŌg’$)ÄTÕävEY-Y6rĮÎŽˆáŋ~ē  UUĨ†aõz―îgƒ8 ž,Ë?§ôā‘^0ĻŋބtF™Õ+ðGgBAĄF #J‚(I@‘ú ­*Œ1x2 ^E’–ˆ!ÁƒĪÖh:E)ÔĻT‚§íf‰Qm0EņÔ(…HĨ‰fí.|ęĢÍ@ÚČp‡úĀ6‡Ÿ6ÅõÏ3ím_ŸĸîĘģgZ°,ĘũŒ›š_°2#éąÎ/ëūïäÄ)―–0Žy―Cũl֚ėũ· WԀļccĮN^”•>j 1âÁ)/­Z™‘8ąó|ýå.AeR‘ŽĘĀĸ\öj`]lžÔåQA hS°ŠŒŋ Ņó}:Ņcpū—3G?46:b|Ō’yOÞŊĘ"ŅĖč LWûgŧ‹‹Þý úĖh›ŪÎ.Éõęå­|RÚŌiŠ;Yq[dŨMė?dQãđÎ6bÁĘÂ5iÛ}sÝ-ÉūxÝŌņq=].NfXÆõEÓW†!–åŊšú@Œpû_ĮŽœŒyü™ÜôxwkmKëc L”TN―zîāŪâĒũķŸi퉛8Ūí|m{·rõËŠ!fhtøÃOÏ}iuÁs#ú_øķ—†eYhNXRvc$Š*V8†AÄÃ'LÏ+(ȜýÄ­ķooãþMŸ|ĸ˜I‹—Í 3â^Ų[Č­§6õ&.Zū~ÃKCPہēÓĀ—~üÎþāšõkÆ ā— ’ĒŌþ€ķߝû‡xmįÓķ!þϗĪ1š<ÓütiÚĨڏÚâsjĻNXQ  i‰§īĘi@$Ņ…þãúøTöÝŨ`w_V "Ã͈!  „b† aÃŅ―ą(:þ‹·xŸ€ę$åĢtŸúøÆš^$xES­<ˆIrŸ~ĒëŌ'ęŦPÓĘŋ^I– ;24.ŌÖ]VęčÅĄũFõc ų$Ūõ\UŅĶ·ö|~Zô302@Uļ€ yäŅ·Áyöģčž—IoÂ"d–ļ įNn~Ó˒ŒFTŒ Œũ(óĀzÄ˒“E§Š<Ī"Žtvó*hô Æ=7@~&ŌÆ2įæÉFDAxN24Ņž“—›5iĖ OŊ[g ņČCþXbÍ―N™ŒÕ€°ÂyÜYōp%{·Ÿ<Ý9˜Ux‰ĩčb‘].ÎĢ0‘ąąþHíŠnt{xĖDnadž0bAĒ›·Îœ73ärUᆭ'Î}ßÝu‹sđūq–nß[К#šŸ:! #”Ļ=ŨÔčĒ$›ÏųúoęĢ]‹Ūĸy}čĩĀŌwY ~,q\ čĮ…FCcҰ‘pÕʑ1—,6ÃĻ8―ÉDhô]_iĄcL&“EcžN§ĢŊ’9*8VüQõ“ý‚ýú››Bތ™ï"lũęIH•@@đ$ žč8}óøh#\ŠĐ 6'ÅÃ,ņ49Ņ@ëCŸ“‹V–|Ô*§–åÓú MNdÐ$„rįúíôP„­Đņž_ä˜`―ˆYî―\Ví|äų”īg&ę‘Ę0ŠĒē:Ø*ƒtŠāöxDIF0`Tîæ-·„…ÎöãŽúGÓԟāaĀŌŦBË%ˆ$€đ>mЄޑQũ]h9õÕĨ[HĮš:ÚęÏGņŨ)ĒĀõÜļ-bLęCķˆXVÆÞŦŪ]QVtˆņjáŊ,†"•nARü –P[D|ڋ…ŊĶÆĸ^/q Eðš“dÚėĮkŨ{8Ïå {Ýýî ĩ@Ėŧ8^„ÂxEC]Ä^5hØōõk'…KGĘ[ –␰QÍ\ąjõŦŊdŒŽēI"ųž%z…éYI‘DĖĢbÃØ #KöžåÂ~7ā›úēšģ|rÎ OŒŒNe°ŠĶ õë―~ĢģûZ{˅Ŧ{Ū}}žžuÂŽŽ”§G"'1*ÉÅyß2PxĖē˜AFK`0ūÞqŧKōÜ:ĸÝ5[LĪË Uįvqœ ˆa(ļĶĀß}ĸP˜–rįóEķ=ÅÓóEĖ'mZ*•hð9ÚŊ‡t]J!ĶĨøl‚ĐQ…wŪųĩúĀėĸŧ>0‡ĢĶĶĶķÏęęęĀ;kjJ7o:đ2ĸâŪ7ý~ßĮÎWÖ^·ĶķĒĒÖé<Ā(ÅétÂĸ9ŦŽŽ„ëččhkkƒYøí-Œ"!ĻvT}ðéĶĒýđG›6ŸšļũøŲmÅ%Ëķ|ļšēŠōü„ĐŪvôeŅ$4 St‚‡€îBģĢŧ2Č@=°ÄÐEÉļÏÚ=ú(ņIîĢßgGd-R[­-ا>^ ÚúT•f.(üøÄGÉû;wv4Wþ­895ŋēĄú•%s“æÍ]’žôĖė܂·+Žî°/|ųXuCɟ7ĶæĸĐĒŪŲqdĮ„…Ÿ”ÕÔߗüü,{îĘýeĮŨ/™;gþžÜœEI3ŸÏ[ŧußŧoĪl:álvðƒä„„”īt{röûûŨô=OŊ°ŧėģžJ秃°äī5ûVՕ—ĪÎx|EņþS§škJÞOIˆŸoÏĘÎLž˜üúG‡7.ÏZũöæoäÏ~nVjf֒ é…ï9ŦJó-ˆOīgf,ž•ŸĩqOŲĄísf§æü―ŪvC^æšwö467ė~3gނųéöyiKW–:ę+>ÉHYąûÓ--uë_Č(Üöhú79įōGÕDqĸ§Qذe‡ØðŠ"$1ŠōOfē!'ķ{ú1ýîžĀ†ü=üÆG]]uœŽ ĪTFåęzœŠ{šnēð(P;―?6ž?ņæLïzb”6]ß/HTâĻ"4pō0‘SUÓũkšĸ?čkøÁs4ēÝnÉÆH’„‰ÚŠ*‹ÍŸĸþ҇ëOïf?ýØå9ÕĪĨięUĩmËãfģáŋSXŊŨ t]:™ˆJl4mýëģ_,ï~ýĸū[|ē:ýĄĻģrW 3*‘íĶĄ=BG%3Œ åtŽðI@Œ€íĶ@~öØh‡<ķ`ĢSčF§‹øœZyÔËĩ?Ö?Å0tM]deó|?ķUžîšŪívyŅu-°eۏm›§ėfŸ&ÛēŽûĄ‚Õë‡=ĨŧšH•g[ö}č;6Ļî†ūĐóŽĻËj:•U=Ž]–’SõÃXïróÊāĄóæŲúgþŋuëö;·ï?nũϛ*+rĶ?$–}ßÕuU_I‘ï†ąošŠ(3õuMnؐQnh+‡ýXtI›Šņ9uUÂO]eĸ{ïÝũŋúūāUÝ8ķôĻęnlË4%=kÚķiĘ4+ÛšÉĀŠÚąŊéCW_^^ŧŠĖýę3;ŠŠ*Ŧę0fö}M #1S–ãŲ—|W5M74gę:†-pðŧáqhr8ôßôûķ.a§jÚū­!ŠČrĖĄÝ ]%įĘōŠĘœfîŠmšŨuŨ7…^đ.2đ?š\ûƒíl‹JœFŽïūōĢĒūa4jAŽ†•dō~)™7"§ŧLÞŊpΙü*âG2ŸųoĀÏgŧļļõÁH’Ūïųîß:Cû=؊žŸŸcĪLŲėŋe;|[…äq$™ĶÄPšŊÚĪÛtč%ŋøëÅ0 â!ŠV‰ īðuwČķs4XØÎsF…Œ!;Ų:,‘gRŧܞI~·=†Ŧ` ÂăžäG-B~ˆåčc#ļ4ô[æv†<2Õš-į—Ž õT ]ė õS}…gč’įE?þņįPx(!„‡4 •8ŠG8rŌė““•laÂFĻE3€JŒŽB (ôĻ|„ĶÓTžÁ™ čDī@ŦPåLō#@ •Düšïģ_ϏĀņ›Ïc~š†ô$?úč sø!ÁüPõ:üČðĢ1~ČĮCž?ž2°~0øq2N `p:äG~ŲâG :ļÆfÄä+ųŅãäþ(íÆ{??Øņŧ ö™ä‡„ y<Ēõū? ]ϏC~tØŨåG†.Â|~0ÂKt3~pšäeü ūýGî_ ÆņņąŒP"Ïdč›+‚-ŋĢŌ“Q~äläÔĻs͚LļwïžĢ3äÍóƒį­â'‚ē?âĮž7ȏėÐyðß·s.?ØsøąÓPo!?BļžŸŋŲŧï ĻŪþāŸsÎ―ŧw ÕöÔhÔ$ͨҘK4–'ŅhĒĢÆĮωXÁBĒbėØˆBĀˆDJbÁFS:ėē°l―å· Ļ!óĖ3ŋäųæIž™ûšqĮŲ;įœÏįÜđïŲ=üąÖIþĒýąúïî4‰D"‘čOu2‘H$ýįQ+‰D"1jE"‘HŒZ‘H$ĢV$‰DbԊD"‘ĩ"‘H$ ú›C[ „þ†ĨáÖŌþŨvBð_W6””a$4ųÏũŸō?ąý" Ēŋ/DśŒ5U5*‰ãĸÍC…Zü·ēŠPņl}uUeEec“SÖčjSĘŋ)ō9ø7þΆƒN]øða…ŠIôG'ų­ßsSimiö‘ÐoĢ. ‚ŅZaÄŨU•?zTĪnbĸø>‰ÄĻÄ$˜ņ֋CFŽ9|HŊaï&Þo”Jü g6éu:/ŸQ„ŧ{9ráÔąƒ^ýĘ+cž8råæÓÅ ,EPs)Ž^Ŋ7q|ÛA˜ÓÞ[:Âë…~}šwíÚĨŦÕĖ-įĖ…ā_c- ĸį Q4yœ7aäČwCâꠧ ËBĖÆO―úũïÝĢ{—æNúŽōI-6Čhōĸ5žÐ†ŠĖāí›öĮ? ôï,@āÍ―Î`äÉdÂđM+GŽđ)ąˆĄü"1jEˆŦē—Î_{ĩFžóÁžŲþÞÝ;3,g„Ĩ2…R!' ˆ’ Įũn^ĩ.ð‘Ņ–02™”F`%R†aĪ`ÂČЄĶ%Lëp„āw"ę^äþ)SžË(ė;gÉây―ė ‡ķ-ö[―§Ė„Ĩ)HÞŋlؚôĒzFÎĀŊņ抒’Š&xaøĻŅĢ^ķdÄĀž-‘ÉŠĶđ))M@BļÄ―ŦWSq2ƚG-]Ëm,2 ̧ïČĪŠĒ%6JĨ„`DhcJ*·Ne‰@īDj]‚ĒåJ !özøO™:~ A‚ĩ#J"W4Oސ=û”IhĐŌÆŠ‘PÐB QՔ–•1ýĮŒ5ŌÂ{°Ģ-CKĨ­ûl…$RkmČR$#k™ĘzGZŊJ8Nā˜Ē-ĩŅķŠ)F&o9XĀÄú›æû$Ĩ›kĀ”ą&wmĀēïâRyFÉóĪŋũËS§øs·ãxūĨ…ēe‡ĪļeĨæÕ--B7_’[·N$F­č„°^õ$Į`nßkhČÆĀ­ÁĒÂw ínà ˜kŠ=đå#__ßũ}ž_Ū—I„é ;‚BNFœ Í(Ž5kŦcnÛq⒖ÃÄ\q8tïþð2áŠóŋÝšûÔŲ I‘ûüýüæ/[ũPc"ĸŪ/ŋĻáŅĶßTpķo|üÁĀ5ĸü:6bûŌģ{ŋ ÍQ] übKddØÎ][ΜÏÚ>Ų4ĮŲũąũDtlLtôđĻĩ ŲIgwl Íš“·oÃ'“'ûŸ|3tޅ3k·;yôó›žõ@ē^ ԔīíĀŽ”•óý\ČĄėœû{ âီņÄÝψ XėoŲÏEKÖįU뭉MpAōÉïXޛō]L‹éß”! Ā?`OLĖŅįΝ ę!ÜûfįŪ}߅W0EˆĨܘĢÁGâ~ŅŠŸÚŧ}úī)ū“ßþúø…zßrÚðô€<ÉNØīmwbN f$uEYßnZuíĶqI~jHāZK~sđp“ÃÅVG =t:ōôĐĢûöG”ë8Ûn=‡ũęá åŒ ÓX4gšeȇ‡ËŒīD"hŦ‡FĶæ%8cę”éëēŸhÅī}NŒZ‘ Œ“›Ŧ‹ž4ýüīKĒRnꁹQ2lÝí+f­Ü}.Ŋš*1ęĻĸĖ5Tęėœ,#Ģþräé‡e\ciôîŊ6†^Ö°˜æ5‡í ÜģģD‹Ø†G[‚ŋZō9‹ūØõó•Ī“ĄA—í/kjsŒ0ĨP(EAüëĻm,{PP_kįÞcŅžĐīąIĢŅ*ûūōŲÔū‚đþQÁÍĘē۷딟’›VŋyŠtę‚Ü_ēģoæßĻ5ðRÂ^ŋĩqÓŠÉ|‚ÃĒããZ―:ārą^•ŸĪ7>Iˆ ŧō@,oæ[̊îT\?ąuáÂđĩeŠų~ûöeLõ›ûųĩËË ęĒËÛ6ޚ4iJ`D|ōÅøŊVÎúōD2Æ|Lôą _­š:ų―Sq??Q7Ô=Ėܰ:(6IŗūY0wþÁĻ\ Ļ2SĢ j 4ë'7úžïqÞíŒþcÁwq9˜‘"ø-UŅík'đĨĩ2Š>―gÍšM đ• %SSxcÛÚĀGõú'ũ2v‡†Đ ĶŠėÔāsŨŸšÂ!‚=Ûíęŧ ›ŋ žrĢ”RHUnmÞþ]Ęmš.Fœ>|:šįØüŸNþáŒc7*)íãØÔ\ĻČˈ:“` ųýĄ€åQyĩ ?Ÿøú­·? O(ÐTÞ>ŧkÍŦsūH{ŌD›ëÎßĩjņ”7ü>ŽKžw.čŸkk‰C ‘ĩ"e\Üąj`gŧžØïĶŧlkX™–{˜õÓūČtÏÉŦģÏĮm^ęW›v.ŦqÆėéŽhß%4þŌĖWzœ‰PÏķÜ]ÄņĮ›k"3k”Ųŧđ/æzė‘qŪēŠØýEjŒÐģ 5Ö…8vøĐ#GÂï?iDOӖĻŦ*kR öĢûšILf€7›Č€!/#€šútįŸŽ_8 ÝšČSë&"'Ās„‘Šo_öúð!^CĮMûĪĪQ ðÛũÕwĒãįūčQWUyŋīaüʝóžÁŪßkQIi§õɉˆMŧãöR@â―Äuþžrn]Ë}LK()Į%öo.؜|%ōí Fš‘›ũĖ)7"ö8PÚã‡+šXx^0|}úŊ~<~áÍHZ@„B•ũn—”éÚŧŽ ÚsöjZš_o'Ą>?hũÉŌ!ä@lVônđŪä|ōĨĘF„qÛ?NœX;{—ũ0ŊĄ!ņīó +ĶbĩEЗŊЍ\ÆÏ?ޗŋ6Þ{@ũ!ã’Ŋ ;šĸK7sSáĩÐæøY@H ‡@ŽŊ4ÏŒh꧛._ŧ~üô‘ģĮŌŠō'å*ė2lĮÆEðڜ€„ä#=•ˆâ8ÄóRDáúümÁGÕžKPTėÕīŽ ĮÔ\9z2!ƒEk°ņ›ŋ'åĮo<Đ’ å EˆÄĻĩāü’ĸŠsŅQ[VĖĶđÆï·/_øbĨZe(M9:Ųgō氋(ŧHE(Ö$0†ÖĩÞ[Œ1ÁČšąFčæí=ņ ŊžCFžėëBĩFŊGðÂŽęþÁŊ·ïīÚĩ+xý͐Ôû՘hÆó`cg+“ƒÞ˜Ŋj‚[áúÚ2K:H°€ @„9 -Îdēé:bßÉļŸÎE†ŊqUû4w>øėËá=:šH%4B žGĻų âyŒ Š­ ŽāÐīąūI)=ŊŠÖ sfÖyÄÔUógôïæéâ X–eœįĖ{l_ïaĢû:{ï~­žC`Aæ­Úāݧ›“Že{Ž‘íõúÛc<ß9ãĸæøiģūČ-֚īŠj]ÅĶOßóŋĄānyCÖ„jóĨĀįÓ―įããbŋŸ3ĘÝ`ā―}– Ąā§’ēnæ'ė‰?Û`mQƆÅģ&ūõ֜åAåü;€’ŠFk™OœĩaƒŸ‡kg à „ˆþˆþķxžãqįÁĢú ęâ&ŸūâƒÂĢ€C|6,Ÿ&GŽ%šlÚw3ą%œ ÂRÐüÓˈbä2UqEĢF‡ą4šį%€ÁÐPU @Ņ@=[‘•ŧŽLJä jÉ#–™Rə˭Ã9ÞÆÅÃQÉ<*ŧŸœëýŽŃ + ?sCIGŨáv Įņ,€@Ie #Ņ™ĖB›Oę<ãÜþõIzXËLz=Ąg0ģR^ZŠīB`ЙĶÏ#`\ûø|ýõ†ã‰DÚūĢ›QO@A!ÄZ<ÂÖĄ €°fC“ @Ņ^)E­‹› ,Ŧ Ãóvö}&ž:čƒK1A;·'Åh4ŧĮėũâĖ–9Ï]8ÆS& ,Q8vēXN€ķúŒýÖļūÖÚÍFNoÓiāôY/­<―mCŅ mŧ-oŒnĮ–ïÛ<óX|§UÁ[Įy–,žû™Ó…á9Ä#“ĩ6ÄKAĻ-+ŅP”DSx%ðŦ)Ŋ†Å†rɁ+ö]ähš`$ðœu ĶMÐóÜ'„âx0ĖM,Ќ„5jôÔz)d6ĩü€6ī!ĢV„ˆĄ:įxtŽg·îIö­RP*eîÝx:Â―ŽŽĖ;ƒ†ũîPrįÍ:té-sD”V]wîØI—YÛKī9Đ'öïƌ?~ŦTãÜ­34ĢhüäÁøóĘ’ïŨ‰;;ķųF‹%ķNmÎ%„įŲ$u{qŅdßôo#lXėÆlëÛ_Þ‘ņÐÅÛÆÂ—‰ÁČÐpåB”‡ïþžáéôp„čŦ O í Xž§œúMÝšqžÐ’ėÏjá)ÐÜ=y&’™íÓgØ{ Šš–ykðˆžNE9LŽ}ŧΈĀZÝģú°eęKG īp+âdn-ôž7ļ“BŌ|™įŸÂ[Kī"4Ur=>ģHßsțøĶæÞþąšē‰8tÖŊã݄âkŨŌûš Gõ•%\ÛŽŒĩŪ#€u]€œÄðƒõM&°bðĻWuwðöņiw*-%1ÃsĀÛãđu%ęJ(lÛؚÓS~)o„ú›)+&ĩíó€ĀÛĩũ0&_ æŪŸŲē§ €B`Ö5 @uRRæŠĪäbMŸŸ–\;ŪŧLfc ›z%6ú…ņã^ä[įā$–šû8D\đÎŋėt,"ÎNût&ŽXîŊÏXQčoIĨnxœyŒ€…ĖÉQ€zM\p%įAcƒ*5fϘŽJPØY_ėKŌé4BVŧÓþą+RÓXūyķõHĮÎúĻgWgÏÞ Ę2Ų*)›Nō Y§~ŧ~ĘRŨŨĸÎŌÔęšâƒÛWpĩĐž\ˆ ŠNoÐĻëÕęüäãĢûķ€ÞKïŠīõŠ–~,ƒŪOķcāŨ/-­,\î ā–VĪo*ĸr\O‰SŨîĩuįŋYÐC €č~66ÖFÝ:ž·ØØ*ƒÏä4TĨO Ā}ŌŌĖ"UC―J­ižvøS‚={ôswwĀ–é_ŸwõvĐYS2ËĮ  ÃĐô"ZĨŅjrØŨ`ÄōPĩΔžg‰=@;''ZŌeÐøƒgsՍÚōŧ—ĶŋF™Œðœļ$§ī^ĢníDgTœ;Iŋæļ>üĢŲP_’î3Ô€,ؒĪmŌhT51ŧ:P”ė…q3õå–&…ļ9+―–„5ęīU…ïé V/Ė_øn{Ŋ~~\Uųpõ;ØšųÎ~ŋ““;ũŠĘŠ0Ômžþ* äÂo~<hYXšNŦ-ÍûņĢ CKmÚɰÜéģŅĨŠ&õĢī·ØÓĢW—Ļĩę{ Ý:*]_™]þĸØ;Ÿ§(Ž{<"ĒâIðæ!^AžŦ'=ėāAÁĸÁ‹„u؃zRžøƒm:īÍögŌĪMĨM›4ÓĖ›™fYe―8­T&ë^DÅK>§žé{™ïũ <æÔė/V^2ēĸ@Ȑ@L‚fĩŽí~ßäÔôrJįL>ę˜:Bš†+æ$ˆ(0 3§VFŧCŸqÏüJQŨ‹õ)™ũší–íD°ðJ/ä}õęõÛŚƒPĄŌt™āŋŊŒņ}Aûvãģü–öûŨ›wnœ?{úܙS.]ūĸd'Īāđ. jĢKĻōö8ī,SÁęŽb‡=Ól$ þ mŲŽ?›3Ïėš4ėxĄŒÁ†nKGZ>ŦĶDsˆIß2íÞ0Š—Û,GíģÍ'/>~‰\ŦаÞÁĢó~Ŋ#·IžROįáÄ1͎7ĄQčÛÍJ^ÓōļØéû|!d  GĩēŪå6ŠÝĄÔ Š} z­”g`–{YūīģLJ‚ú^  / Ŧ^@ļ="ōtlŧÕņĶ a,īu”Ŋ4ú„„Ët―‰\ ýnYĮša†„4Œ".ÕÆ3Ę›CĢ„ ؐ'ûCÆpÁJó|ŌßÓą–Ëï™åi=’‡ou―xeÛą[vÛ]u:#ĩęL“ðō@ýaÍz`‘pļčÏå=éĘĩ[=þ-Lō‡ÚøŠ„Ó&zûčÁ͍ŧo>đ\pXK;Ώ üTКšv”ŽÕWŦéLėįžÞ“ũ‡;åÃÃTĨRĒl“jÔ/)nąŠZt4ŌĄâBĩĮE˜šĻŠSCՇZĒ:9Ō- ý‡ddĢ6X4nmom―ûļú7á"9øō5I.øóĖ3s[ÛŊ į;;į·rۖ]õÞZïcŪ―ÏŽ'PæFr/ä ÔQ|2ßÂ?ˆŨJ]$†€•ˆ‚æBŦRNrū5Gĸãš―ģŦēŦ@*ĐSÔÆ=ëfŽÁX}Î5?hīŊ>Öĸļ ÝÍÍo‰[joąýŅëŨ þō›Ÿþðĸį7Rĸ·ûigß~ûW?ųîuöææ–Ú››››[jonnnnL>Kn>įnó››ßÁ†ŠzũUŋ@JĮ/JdĶ óvŸ.Á þúļ ―xŠþōž Sđ~6š·3œų_šâTčŌÕc3čøä†>þtÁ§ßeøå'0•?đú'•ûMO~§íþ|{CWŽã0Ó[oon>7q―ũw ĩïßŋĸĸüŸýĮ?ú#3ĩudÉWĩŊ’Šš ^RŠ2ŨZ‘Fą}+ĩDTđũ6ĩĐSĐšŧOŲįyĻžß{îĖĖ\."æÐÉŦęŒg p][Ŧ2܃äTž đ* HŠTfTŌÝEÄĖxDe`_1+—­đndŪeá?WeÕĘ4ģlĻŽŠÖRĻ2#G{[øûÕęĘ%UŠúÍ_~óþá?úĮĸäŸū――ýLg_[Lú§ĸYUåæææs""þāþþŨ_ĸ―ýŨ•ZUý“?þãóŊþå‡ßų@jdfÄĨ2Š‘˜ADšé>ϒz<ÞÍ9ëĖ8އTe%@īNe•^Ð="ÜĖH=ŸoĪÚZ"—VÕ\EŠZ•îÁņxļ{†SŠū7 Š­ŒūIQū*H>ŪKËö]YĮãðíŒWËåžũ­Tö-‰ÔZGķ–ˆ’Ō˜Ufö|>ąÖÜh-^Įrw)ąĩd„G /3PVĶŧŦÚ|ЌTS€{šJÍ|ïo~úÓï}ï{]üMņ?þ―ßûŧŋĸû/"åæææóĀLĸėÏþۏ~ôãßýÝŊmWÛØąÞõâÃÞ[Uųx@–"u-|ŠV"éŪ_}Ĩ„Šđ;ģĩ}K‚›TĨ‚âTSūŊž^Į{[6âēÖĒŌ·ŦZÛU=Ïįēõ’Kôs­U™ŧuó8wŨ.-`DĻ™Đķ 5}ÝgDīĩ”ĘŒūPÆõz…$•Ųbš^„{ęļUÝ{ģoĒŠ"xž{žËLx<ޑpßĶŌÔ"Į1Îú<Ïã8Ļ ‘pï`ÍģZķōRíŊ>|0yŽ%ŋóÕûŋÎøðáŦŊŋþ:"äæææóĀĖ>|ø™ßŲÏÍĻŠ™ffŧ9Ë,―tøøĸļxž'(ŦD„ī`x8Š2K—fĢÔŽÜo›äqŊ Ģ,/­ŽKÍMAėgë Ļ­Yŧęū+'7ļžaH*˜%RYRf+#Ĩ „ķÜgĪŽø†—ČZK:­Œc™ĩ3ĪÆGfĢÔmmŦ€ë˂ļT;32Æŧo ‡dd\óB[6&ýX+Ŧ2ģŊxDÄ,îGũ$‘UFÎžŲŠoßķŸ€üõđđđų<Ļæŧü―ÚĘriąĻ_šĻ<ũŪJŠĒu–ŠfŦJFžl­Ép+ë8ŽĖ+JexHĨ*ĢÃ\Į:Z§ē‹˜ŧ—TIĐ,‘+…ęž4^“#Ģz™ĮĄĘŅĩ1žĀ&ĐR>A„Uæ5 [:w'ŠV­Ķ_ ^•ū·R_xÄT3쌰öØó@*kbV\ðš%ßᚠz.GV–ï]uEŪÕd;ӗÎÖ8hģvýP* œ°âæææ üipģi”TĮĐRbķ #CänũȐ^py^Ā{ķēī(ó|žUĄĶ^“T3”ÔDęîĨ/ČŨŸŒéUÕ)ęš]2Ōz3j‹Ô52kaĖļ·gU —ŲļK ĻúÚwŠJöW‹H)Qrä~-ŧŠĨ/3ˆL…lcÛ2ę1ӌ (ŧ—UĄWw')ĀÓwuĶÓEí]ÝOĶKJ:ĀÕFdž- rssóeþ^­-›Lķ#PmąÐVNnũJ7;ˆÓjwÏįSDŽõroŊ,š°Ýņ‚Úæ7@Že=ÎŲ{žgeūĪgvųčtUüÜ%ĩ&“Ĩ–€sŸ$…ˆïMē3+Ûaw'CģfĢ?ÂÖå|>I*™U‚ž1ę,Ũ•ˆ@ÂígĢ·ķĻ*Ív'°: !%Ķ+še2ķ›^dÄÞįlše&yM āįĄÔðë)·Ŧ―đų]m‰ĻjxˆĖîýl—/tÛĀģwš,3ÂÝ^N$1ÝŊ%jVRiÔĨË}ûÞ`owß)"™/Sœë8JdZSuH >vžfDÔˆí."c$'Ē]f§Â:íÍ,%JŌ{1ˆ@ @į/ÂTŧxWæXŨ–l]‡ĄJ TŽ3•J%â€(•@ļC@jßp*ųčķ‡óų,5æĮc@ŧí6™)įđ+RA‘ŠLĨĘÍÍ͘ÕJJķFFUœæÐŅßđvĻŠF’ŠMĒNŦlKæīūzdAîð>\wÍ&mm=ė‰Ĩî]ĩŒÏ <"TōgGøŌ#b­‘YĐĶĶÚ·”f Õ.ĮzÜđŠŲŽĻŠæÝ%E•Ŋݓé>šGÂFW(ȋso_Į*Đpũt"ß―{ŋŊž-âŧ+SÕÔ<ÜLŨą<âēÛĘ%777_`VKąuėíT,3ÝũėhUx•ĖŋÛ!RŌFēûü1vïMČ%ū’~†RŨZŧ+€XĮÚÛŦę8ĘÕÉ`kQ5|GÆŌö°ģ_Dvō{*ijRåįî>H–ŠVũųâ‚<\ ĶK•î^5EĶ@iÓ ‘RËĖ[ĩDéįÎUJUëöÄđ’Ķ:š?Vúg›fÝ˕Aå;[ûŠP$Ė֜ģ DŸ”S(·Ÿ™Õ;pëž\|Îņ†‡ÜÜÜ|yYí1G§2(xŅZ&Į1›æU™ĪķįÜ ;ðĘ"A• ™s:–j-OR@ÉŧĮû–Ņ4Séļm˜kīŌÖOßŲą@ú5OՂxzužP“ĻB”ˆˆĖ w—Đ LÖŽäGGÞ~vŸã‚>°›‘ĮC qîaßũNW^9—ōz}tũxÍą‘Ĩ"2ÛŊuLK†éõ’9XŽ„T™šû|ĩé þ2]íÍ͝՚ïéI*‘įyf…ŠÕ(#DÍ’%kč#R"°efŦõĻT)–'ŅĨ;ŪjUĄfŨČCĮ]–ĖųÎ―cNô 2ó°ek=û°Ã(uEp\ŪjlGĖČčŸĀÝIīÕŒHË}ͅ&&ŽéóULŸÖt)ž=Ÿ‘AĨˆ„ŧŅ@î;Z{įä.ŽØžįÆôŸ‰dĩŒŠŠēŽķįSG'ž1‡wĐîþ<ß@Įvũ˜ŽyMģpVAnnnūžŽ6*ŨązÓĸ âXGëlPĄŠ1~ķUÎÏģ l•9ũéáģMÝïĩĖÂC•yŲ@‘˰e%•™"XĮ#ģÎýTāxžóÓ3$”ÞÝWTmQ˘ļģõËL§Ē2§ßŦÝąĻšˆx·-ŋRÕ ÔŠō…Šâ;^"Dloûi2‘t•ęļspTž€fÚAsŲ:Ī*ÂĨ3ïoú‚WŠō6Mi%2ÅY%9Ķ:hEpēÚĘRr}™?âussgĩJJÉ4!Đjī9Ž*UËĘ Ž…8ģĖÁŲíąķ>ķũŋ;Þŧo’EYķÞÎ'„jڕ]JŠ’ĪęRÛ~ŽÎÚēð(‘ŽT脾ĮûޚcÁ\v>ÏČ0U’Ģ€FføÆDÆ)Ûtš‚'~GÅÛ_― ÄÔ^D†(_ˆHe‚Ô9L\Bœ6^ĻDv8pŦĪïs‹īh“-ßĻ,]Ü{ĢĪ―33"˜Ģ=Xed2Į!777_œŦUNį“_Pč<XĮƌĘļÆë҆ŅlĐE:Āu"ˆ v@)RRÖY§Ššą;ɞí æčA7ááĐfs0ŨĮš’{Ï!ŨGļû>ÛĨęÞÎ ]vL/Ä4WļŌ”ĶŌ••č2K$"@t{Žîs·)þŋķįmÓ  2süĐLũ[ÕÐõ"sĐuŋDūuĸĻėģûWUþ<įįĮššdæHĸäË(Ќ_ŦŲŦŋø_Bšý"`O6PĸF·Uĩ þßDu å7H@~u>―/ü*õ ČߐŋՇĪ5$ä;ā iøóÛBÁ_ïJĪøĸAjũ ߓ―zg$Š―œŠ››ý$ŸÏ'ÁŅ‘pō„*tN—U)ĩJ&ũDÕ8e͜šYÎîĸZĪV–õ֞KUĩĩ:ÉLĸėÄĐz―ö>œ?fJQĄjހ0ӈĻH3ĨęđÏČ$•`ÉEį;ģø“ÏŠ―P;ý„ óĮ5Ÿ{gFAH>ۖęG]Î `bâšß‘˜V)ĮólįXģŠäo āÏįþũSČó›ýø'ßÖ5۟üā‡ožŸŽ•ø/ßĸwĸþĸ§šþ†ĪĮTy5SŠHųÛøŨĸöĸü[Ó_Aw Įþßĸëũŋĸý?ųOĸKhý)˜Ų'Ōäĸåî;ĢŠšūíOŧũ"H“"D]ąÆŠ…hŒŅØboĻØ@ĀJQ„ĻØcbI4Å*lX°‹(6Ĩ ^:ÜvúŸ―— ïũ2^ĮûðōχŒqĘ>ûœ;ũ\s­57Ŧ++Sæį”Tˆ2·Ÿžõųū#šŠŠZ:ÓīŸÕÖĻ‹a\đï+4ĩŠČ/Ū]š™]AĸÉÁvyðŠŦËĘuœôžĪ^SųčÁýŸ—Vþlð0ÝĸĖ?›ÕVUVÖBađĶĒŽFĮâiŠË+ëĢŽēŠåÏûÎhIsóĘy5čŌĸúšZˆ”Aā”%Ĩ )Š<ÏAé(H“`ā4Âøô1q#îąESŲÐu-ĸ=hĘQë”T8˜ÃrPÚmĩ K˜(Ö_gÛpM”ļ!Ü/ÐüJãû86—(‚„ÛÕ[2r K-ÏąĘéĄDâööư $°xøIÁ9jpRŒ›p)°DĢ/=lýŦĀÝ"ĻÕ(ã‡ó`0‡ĻŪaŋ1ž&ÓĮėü3+hšĐxýlíʀg•ēJÁ&G†,ۓĀ*%Ÿšzį3ĩþS@ĄH>õÄÖݑŲāóðŋūĸ§Ŧ)VWHō_V[QĒŪÐĒĨ™­ŠÝžþØŦ†ųkîIÉڔˆĀĐS'ûŽ ˜ųíīŋ&—ō$%óÅ%ï5ž,|ĢŠyųĮĘÅßųl Þļ1dož­†ĸąâpč,@ÉϖÞdÝĐÝ!ŅóU*æoŊR8vãXÁbp2ā$úAC/ī bdT^GīN: [% G(Ņ]02‚Ë-īÔá‰Ï‘”įøzor x"Ō0\Ībû\â WÂR€?Xð*ōœÓ`ÔݐCÛ04f—BÝ`0ó•qēČāē?dÁÄ ZéÐģÖېĢ9„ÄčQÍî6Æ€Ņc@Čþđ–nÖ6” ~ųð‰ZŌUÜđvæũwŠDĒėŲĩK―ĩĐ EļZßT&3+…Bqw Ē„ĸǰŧz­ôG ‚8VÄ6@2Ę$‚] Sp‹‡„Ūų‰ÁqŅų vŸ­âŅ\âd&šū$Ģ‹\ôå >‰Ïi4Ũ„ĐĢĘZĄ”8N |1lĐ.þįÅIZ™síā‚ ƒƒž".ĨĪß7ųDȌ}į3H}~°·WüÝ·ÄG … %/Ÿę'.\îā;ßÄFiJÜų"ČX„6;h™WÂý·2EÁŽ'@=ļH‚z<”‘ˆXëh˜ô%ĘAã§ ïb[w*NÕō8t#ęį‡D4įŸ<Ĩ+Ęäė{/_åĀmÖŧÄbŠŠg―ÂUĖ4Ņ'7ŽØPuÄŠT* О"ļ,„‡ĒČSx˜uJÖ KÅØ Y,0‘A`Į 0 î_ļš‚Š/ &°`‚”Įžß.˜|pŒĪ(ĻFĀ'JPˆ†P‚‚0,ėJ0]NýŸKŊ$I2næØĨ‹ÅóWęâ7đYïÉ‚ĖžęÚ'iŊûvëÚŌTšztÓ(‘c\L/†"_'5sŊÆÎž0ÆmäˆïüRĢ 3SŸû͏CįúmËЕTJmĪïęĀ Ŋo‡t›ėú\īũ,wŨÎÓO>)S*ČôËQ3‡>úë=I™2EéjߝK‰đ|hũÔyĐoõėëī53&ŧ{Œ3wUjf E“@9ĩų$ĸŋqÅRï ũÆDÖŋūrØÓsØā!3’2ЌŒˆ—‰?Ϙ4lØ  G3åčF)ýÅČļã7ĖrStgũųŦÜ:EŋréĖogâŨ/œūáāuZ‰ïĀŽētttrqqqnےĐÎ Ųēeûķ >Åëaayú­ ŋEgm:rKIÖ$â>rÂ<ß;y5F*âüķŋåKfö›r*1=zýę ŦWN<ĻËĻ…ÉéJ%qõŊoČúIã'œŋ—‘‘šŪŪbHöĖŽĩ#Ý=<ƌ―žŦUŅŌ݋‡Ķuė9ýÐÕŨä§ī‹ĪM­Z9·kįâŌąĩđf·ĸž”ĖšG6*}uŲ7twvAöÖĨģ=Fë7Í'-_O#x!DJAÔ<]ģdĮë*QĄþØ―xßĩ<•Ē&&Ėß}äˆ>ĢgDÝ-–Ŧóđ”py_ø’YËēĘ*Ó/žyĨf”x/ņČôqƒGš}érāTï,óÞ0ýŦŪ|ï―34Ä •…(ð…EjŧķƒwwV0töĢ K==Fļylüí&Ë._9ž|~ü°ĪÆå‹nų7"}ÂnDžõÝxgpx%aLcVĘW<ôšžšę<5îĖé° ąFĨ鋖G8L8w6ĶQnüōÍ ôįÁÅį―7Ÿčĩĸ;ŧ܈ļ|Փe~?šo‹>8öPČķ—å ĒSÛþÝ\ûįÖĮ^·iöĒÕļSįNÏvx·tv`>K1u‡‰|#ŧv_uv5ßĮoýRëF„ķ27õڛ%A;ÝmģļŠ)äíýcŧÅQ‘ácŽŊZÍ((XÕĩyBĮ~ŪĶ"'ˆžDõîÓÚT_ęāÜĨgËģ}×Oė%ņ Ō… gļ―wkЊåÁgŌ HZΞp4:þ•ĸÁŸÛTÞ8•ō ýŪ=ZķŸģæ‡U“ú>ˆ _ó뛟NņäžmÜĢ'銜ôĪëO'î öÞäÅõKÔŌúčĢ m ÖyoŨ’ŠēĖÔĻĪįãýÃÝ:Û>ŧ™ŸÏJ5/vDœ™ô{t—æš·,]ũë„cšÝrwāö܁úĻ!0“OúŪÛ~°Hkb"WĮžŧÂI╄ļÂÚ&M­mFÎ JNŒ[–së5p:ôo}Ų―Œt­„eqö‹įeZR"ÚđÍIšœļĘū<îĖ%ŅžđG'—/ĮO  tīïß―[ÎRėÛÄųŦũYqčlėáš{‘ū;Ŋ’*2ûvŌÅ{ĩ+wödŊEÝzÁŧČ ­ûÏíÆ{ïđ&ô·œRAaxŧÆgs“iã~ōNÚŧíRV­kφ \đwß“û”Ôh)’Ž.Č~\PJĪĶøitBēđGĀĄf\ßŧ2öŅ{ƒIŲđ9Ų>zþâޓüÞį p(ÉĖx|ĩÖķ{Įīð>ãöÓō2ū™ëĸmZč™õð†ÁØĖHA4íäq(.qŸĸļóŋFģ%ÔĪ]ž@™ļíČŨé'ũíJ2Ų~üôšy=đŠr@Ŋ;ԒDhxŌ@KyþƒV€ji11„Ī“ˆuŠĪ%žÚĒïŧÅņ<ė_ˆÁZ"i Ð M€N\ Â‚Ό€Æ‚Iė†_TðÂT‘Äžģr‚ČUË Đõ8Ž€ÃpŒ”,€eT=&Jš"­C–āŌÐz@“hEÁ‹Hš ÉŊ24tý„`á‘%ĐîëNĮ> "Ëq˜ÎĒĸ†š9Ļ7ø Œ4Œ‹3xüå|vv†—ûö*ŧsĸė•ŧfCW-ėkyėÄáUĨ=šķŊ~“Uýv‡ŨĖ]ąŨJÁĢ G [Wō*§ãĒÕóímlÚ99”>ūŪąpįŲÏŠĐÓŠĐn%Ũn•4kd}”öäqÚÅWSĶL°Ķ5O’ķO^šébĐÖTÁ|\J11fPú–VŅ4Î>“ÕŊN~?gÁá§ų*#%E*›ķmlmßĖĐU %’Dý4å,éā6ĮĢ›ĩcŊ•ú―đtKG0Ē…õŠā°A]ŧ:·kŽĻĸ␊u‡ýŋĝ8šëŲŊ›ĶM ÏĖĘ(}“{ųpčlŸC%o­V0oaS7g­[·27S‘” ÄĶĻ•?oûMņ ÐßõŦÉÝ­›>Î)§0ŊdÕĀ1ŪŊâ/<ÉëčūxjWæĮ#ûy‡Öœš2Øó“&–v.ķúuĄûjĮîØï3Ņ˜âō^mš;mįé4‰dxQ%ëŪŪ ĖtnfünB—nœ…ÍRÉ3ÔĘ8Pū0BQ•> ú(üJ2īŸ"ÉUel‡Ha_DÜօÄAÔO…ĸ€2)Ļ0A+ĀY`(œÂĐ!’Õ3ŒˆÎBĨØUŨüK4i5ÔKáJX€Ktk,SˆčrjĻėW Ų<%f‚*• n)6ė!‚El*:„d'†jŅŅÁiJDÃáŅxžåú)Ć-sĀ—<%q0.ųŠŋÍ b XÆ>SC$+įVúŧągŊũčŌq@ŸÛŅą9jëîmLHNīģvö ?—{d“…Žð\)MÍéēŠ—ŊKŠdŅĪĐ=Ą..ĐB•ųo933#BFš&o ž“d/ĄÅÆĩþYZ· MÚĖ]ŧõŨcG>ÜÝ­ĐJŽĮ8QæÔ%ZlŋĶ4ÖÕEŽ•$EV—žä ÆÆJ\›A™eŲRĩž šĄ$hc­ØINASŒŌjōŠQ1g’tk"ó"ÉIËÁÃmnGËĻQÔĒË?x>ŦI‡.æJŠŠ ’žü/_ēD1xŒ›4ytWG Yâ ĘTnÏéq@Šģ…wugifĐlæÜ;8|üû7"W˜ē€ˆF’ãž§,•Ũh ‚(ÍÎeŒ:Ų4ǐ„JʄŊÁŠÆÓ6ļpŲ’āy―N„]Ûô:0c }ČZ9}9ÎģidØÎc1ŅöĮi!@F‹ēĩĢSa|LčŽ#y”ãŌ1CÛšfóķ°UÁąÃܗtķĨhâČĪe§+?lÞ*I/3žA‹ļ*AœĻÓqVÎCwsˆÝs<ōØŅ\ Ua"Oļ/ÚÚÎ,sÞ7ówîÞ―|Þžôũm"VN7ĩmŅŲQ™pdgdĖŠĄë‰ QUÅÕPŋUkýü6üüN'ģz‘Ãw zãUfug1ņGvDĮey,õękú(lŨãGï:y™Ûô"/) šĀĩ>aaÁÉîÓgŲЂAŊÓģ0k'9Q–4Y{|6Ä&ÝŠĒ,ÛY›ØđNínvpsÄoą‘?ˆ+á$ęcîHČžéëŋÎÏÏgÓŪ šÆŽ3·šsíÚ đ3,eĐąĨ}ųÛ{QQąÉ…ėéÓŋgW $Ŋ7paŲķƒ-đ#ÐowÐÝŌB”MĖíĨšÜøČcņ9šKI ņŊ*švÎ:~8ė§Ø Ž$ :žãėûLÔ­tÕ ĸ°ĀÛSô_-ĸÚHdōOžAŨPČ(•ÏũŊ^đ2|ûö ―IÎŪÛ9õøfjŨ‹ũFF;°72ģ°ÚĒu[íýÔ°[ģ5Ķ=ÛÛýū›ßōUž―$ LéQƒÖmõņZTęÔRGNáËTØ}1žąPķicË4oŨ―‘dŅūÝ€ÆØōOoišŋy|ŨŪ ·Û7VÞēq}øïeŽą…1wõ\ĖŲ?ŌrËģ.ß|ÂĪĪ7`fb4āËQO/ð^―fđÏūôœjĨ‘qÕģ„oįøežgúĸ:ĩĨýüüþûĸ­””ääėŽŽFF$I* öŽEe(ļ‘LJ„tPEãF7‰áĒ+ĻÃu`˜áŨÞG'Cžm8 ŕû—žģ5E9?ðĨDicüJģ^dļ‚ Ę~ŌŲÜ‘“e2ŲßupĶĻĐf˜$F^,5Ø ëDlßļsëŠŦWYķāšū§Kyä%]ĩŲÁ?::Ę,Ëü/Ečðððßģ€OōÍŨ_y#Ūę]á•QîđdVÖ.VŠ_ēũm!]W~ëēũ>ĸOŅ݊eYēĪØ–åȎ= Ė L2aúÔæĐ8N\ętō0yfJ ̓q›6i  y+} -y()īSZōŌH‡aâŌÆ9qmëjŲ’üųûŸ―ũZŦGkO(i…íLԖ~į‡ŸĪĸõØ,ÖųíßÅëÁÅTĢûķ·Î]&v]—ō\ŽØ[΃n·ødÔ^[ bðLÛė·aI`&ë]8{ö•ļIŒ=ž;k1Į'Fð°D4ž€ž}ã4jĘŲšvíĖ ÍšˆëžŅ4ķNeóc.h―…B ‰ÁtūÁ‘Ã.Ðī Ķ93Ũ‡9F5™? ›(Z0ŋ­Ú,sC55ŨrÄšîtŊ'ßßĮqýäSOýƒŊãÝw]ÖîäÆ+Ŋžšąą:yōÄ,'įžĘ,ãšŌ0mĩ‰š_ —Ė0Át+įÂÐĮĶHДēœq]ŌP’ĸIÆęŨdXYŧÚôJ­]9úš —’ýđVĮQ f ­†fĩŽ T†Bqß<ÖxL‰‡’MĨķžōāĮRdŽý–u\ßúDJ)'Ō\_ĸé_üā{ßûϟø[öųß9é”OGđõõÁ៰åÂ―uņwAŨaųU!Ó6VĄ”KlÄ&ëąÍāܟ<õ•?øGĸęĐĮîšų-RY‘Öę+aĘĩ5ÁaUð–oúKŨg=*ĖðoF0CÆu‹ËÞÆQĖâcûL6BhĩAXjŋōŒB cS†ˆQŨĨÕĶčW^ĮzãókE8•œbŧŽ­`Y RGŋ+ōWūõƒÅ1roĩ‹ÞüÏgÍxãÛļ6*%{ÐĻš§ïĸÝ/kßįþãģO1€ļō—ķŦ„}=v@^ đųĪĄpŦ9įD0Ckm”J"4CéÍû\Ú8ÃLãFY‡Âþ?áĸđrō—^zŲĖŽ?Vkû ŒÚŊ}õïý‡ĸïvíÚEī―ÏbxUQĪį”˜S“hā÷‹_”Ht-úĖuۀųĄYrÔVM âFޘݛ0ÏYŊ€ÜĶ_c‘äÄLcģf"P51ÍNƒÎų‡sģ‘ũƒÅ #†HĘEĪÏ ­œû8šĨ ä™_RJņÜGAĩĀŽvā(Ę5圃`$æˆëU€9‰FÅRÔAÆ`Æ8ŧà FH*ę•đķdt)ųÚĩëO~á‹_ĸÆ?ų ĢvH†ūUÍîÔ{ĀúĩW^ßsė䁛Á‚ÛnyŊŸ―Ž{8vw@EÞmGíŊÏÕÆä›-ŠÁO’ģāáYðãė8VŠų•Ā°yžwœ-­ëÕjŌURžzmŌ%nØã8ˆ]1^Įķ+sր’s˜rfJæ>Wŋ7§Z[ë=ÆšĐŧȘz„3$V“6{+ØTīwéÂ9Iïā ĒÐĻŧšËB#1GôŠĄģķ"R{c§{gĄ"؜{Ę\ĸ–sÐL$Ā<ęĖÅÂā~°˜bļ'/†0&NsãŊ|8;øģÐoĻŲ|X<}úā2gßOŲ}ðū>ņŅ_sÎ.É^Ý·)ŨÚÁyØVĨÝ*ĩÞsÎ%—ææŨjáûrM0į)i‚—‰Đï°ä n ꭋ—ĮTÍÐn€ú֖Ļ0ĩs’î [`‰Ó8ŽÆ>é"ū ä\ýį”2ĻuiMZ@­·ķÐißRJũ“Ŧ”ýnī !pæ.b&n=ā CRdãΒŽė*ąęƒ˜ 0âi(ūķoĘn$ëÁ\ŧFĒ™ösŧ^rŨŸĐ‡ēØĖß%%ƒĸŨ°ĀĪwx?,ÐĨ…ôŊ2jĀåŪ Č ó*jŌÉÛ―]&øYŨļw;ĄF-(30öŠóHŸĨ”ÆqM@yĩqøž{ĒN&|jJˆķ]§æux—Ąaũ耠|NáĖÏ$iS/žÓŲ,ā[@Xo­Ŋ\ūĻ]lN:WëœÐ0EØĖßÉ'{o­Ĩ\ÐDæ-ū+ø;&ώ1p5XĪ:ČlK€ÕjĨĶ@âŧ2ų†Ý8ÂâA#Ð,X°óFmōY֛æĖ‘/Ô`ĮА(Œ )ģžëĘNYV?(ós0éq\1MÂÐ{ ·ęÞ―{?ĸ…§ü·&RXUnŨūƒ·ûĢÃüïðæŋÞæa)Ĩ—_~ųŸĸģzîÍŨSɑoāT@t…5@ĖÃā\­"bá')ĨøÜūŅgÄíÄTVE];ƒäÕd­É°*`6nEáBŨ1hō~8=‹ķąā=Čml°`Á‚8j=į;ēŊD fĘUmB”Î﯀L™Z­4·|ũØ4ÅŒ‘JĖ<FųŲ―vˆ3xøãgĶ#ļŸüäų'žxĒÖz§ųûÉĻþÝýi―õÎDŲ°æxYfW˜Kŋ8ZŅ‚Eat-WW•œ˜R’Y}œS™Ý˜ģu–S4?zŒoĘ­7bU į1EĸFA"Á‚ vÞĻ5Š*°yNųДēÃT{•(Štō{fŊĸōnõī*öZÝZGõH-ö‘+Ė ÝÝûąûxėąĮž}öŲcĮŽ:uŠũ~§§í§?óû?ûïĸíG?ü!ÝúК†zĄķ™č!V ϛ:+C<ļ ЈK™ŋ`bN<ŪŨqjčɐ ˆ’įx…,ļļĀÖOÔ4ŧhaŪöݎ:K‰a‡ž),X°)oā‹ÉÍĶ!―ōÄBäDę0ÜSŦÎð&ŽžAįJ•ý|ÉïĮįôÂ}ûũđũȃ§Nåœŋýíoó›ß7âXo^4™―†Ý #[õfI&$ęâe9‘“%ŧ'ļV@`JD(ŌÁ v ,X„îūU@P5uæ e@ĘĒÄ)4øčZڐĘOHĩ60ˆā‹(–œž”F€˜ĘÅ @A:D~ˆ9ŅąSŌöÏ~ú‚KēĀ} @H‰ļËo”ˆžģ8Úr2ž-⠀0Š}"Ë&ú(Õ`cĩÕÞ%įũj‘f jmމ؏ãa*ĖĩU@@óWë "ŅÆ`įaÁ‚eÔÆŒËļÐg°é`âÛ"`ü’V€)ų]s'oˆS7xŽ8ĮĄĸž}{ïđįHÎyĮįŸþÕ˜U°7ŌHÚme‡iÏÄķ™Ōķël|Xųï·›ž­5p$æqkKzÏđˆ4Ð9ĐŦõy1]üsv #+# ™ŋCJ‘°cņ†ĖmŽäÁ\âĄ2ž2c.›ŽöIpÛóӍæ–ōę:MÏHÄ>ēQŪv‡bÁĒ@čĩ—œ9'l­brŊÝ@=ú:ĻĒQ§,é<Ð8G™ ĶčļŨ9'BûčÝũ<öøã­ĩ_üâ“…!žŋāðđøâÆ;WVWÞæõGN—SŪø‹·[ó{ÔŪģn_'ūĘxéoģūMÖķ_!€„ĢŽ7+|Ū\đœ8wdĒ:V"Î%‚jĮū­“;ÞxHuÍüpÏÂúeDAGkōė.ó/‹ėĩėUâKoŽāø%ė{Ŧ:rNˆÞ.áņŧ)gÝÔŠũZ°`ĩĒūÁqƒÞ[LQïĪąœį&0ŒŲ:wƒš†KDjm)1Ĩė$F‡ã݇ßĸýQ{öėŲ›ė9"āÅÝ~ūûœĐ~xŨßŲãíĩ kßaŲė`‡C &hčuãÖ!ŋéŽð Ũa' 6õ&wŋëŌđ7―0"$= Č8—ā2…õK'€& ýo 8֚yvŒŠúęšr˜2\,Ė"qhĄ3·xhW0_”ÞZWöQíųāRÂĶÜ 3,X°`ŽÚč1īPÕútˆâXfoĮĸ[ö,ŨØ {ëÉĨÕÓ9ēī- Äšyä‘HEļxņĒŦR Y3ŅģyóÕ]’Aޛïûäž Ļ‡! €Ė{7ØÜ}ũXOV#H#ðĩDï&ļŠé’ô·qu=IÞZŨMŽ€Œ Öĩ§ÄĨ$DčÞ8‰8ũƒ3Ũąr„ę uïþbbđPY€ŠJÄt%ö Ë%Įũé%Bę& ˜ˆ{o"šsÎŌšĒ1“Ýä|}‰6X°`ÁNĩY‚‘ãyƒyŪ°e6_`ãHÍZoėč―‚Đe0ˆŽ[4ģš^O&ąG}lZiŨëõ4jCņų,ĄZ}uũ›Ėb öÞļ{/Ŋ; žĖĢDšM}ėÛßáļŧÁHęÅ+güöFŋ:ĀÛ0žŊðV–+ØÞ’ū)`@@ÞÖ<Rëā`"°œsīãø°gߎÃbKL>x…|~y0P°(é‰å™\<‹z#5ÜÅÔ,X°#ŧÅĖĖnÆYų:ë›]ëĐ$1SÍ%ĩÖTÍŦõ9z "ôkŽãÜ"―2°ĸĀÁ#GŽ˜Ų4g/]šeD6‡·p Ӎ õčÉU@øĩā“ÁĄķßĘÁėnېÝ@ÐÄúŧŠ/Ę{/ŒõG p]Ũ`ý`žœ HĐ`Éĩ\ҌGyĩVrF„‚û?1ķ:ûÓZuš)–âč_ĩHcÆ)(,X°`'‹‰„ŠylÄ!ŽëŠ„˜‰{„(æ4[x ]s*Æ)ΑŽÉÊóžeX:õÐÆŪ Uō777WŦ•™ÍЃockQ~ži`4ƒß ÐÐÔ @ƒ}æ= ũ1âÍ?oĀØks$ĄŸēwŲ:æąčFSCg!ˆéfĪotũĒŊįž–ˆ­E{ÅCň&Įœr.æÃ&ĀĸŋX°`ÁmĀDCDÅũÖĖLâĪĐOˆâXdrqBb›ÞŅtg✊—Ŧ‰&Nđä‡Ïœ™Ž“ð`’”R˜™1pŧ€UŽð>š~ô_sã‡…ÍO Ý.VpQp‹‘jjĩÖ(`ž™ æˆ*"”’Eü@Ėŧxk­Ķ6 +"ōïŠÔUB_<Į0šýbþžŽĄ3̐5,X°`ŽZJIAĩ‹ŧQ9ôģeČóœcNĶÚjó?SņŊ,ÝV‚mÍŋÅwm|䡇N‹ÜðãNBJ á˜0û ũŅŪ~Å5ī·ÆwŨ2šIÕqS6›uøð@S­ĩŠ˜Ûq)ŽéĘj„ WsÎŅ”kë=”Â@ĄūNŽ^™ĀŦ‚M”™[mL‰™‘ Ö9čËL<ęW‡ÕĘĀ]u ,؁ĢÖw.ÍĨpJĩ5uÅĸžÖA—îŌ}0D›œzŽãļ]ÁM*Š‘ŸíÖĐĢĮŽÝ{ï―o°^Ŋ§Å6ÜbԚãƒÄķĒņ7Î^đüúžOîúÜwĸ/ýōïýŌ_;ôŨĶý[šūý’{[æŨ|ýd&ōä‡8ë3OŌI\ĮŅO‘|ķÆgÂ^+x0€EÚ3+ĻhO9Sâ֜dH‰Âë<1øi[JÜZSQ „ ėĀd/_c˜};“”“7ļtSá4€‚Ļ!q vq*b†î°2‘đL,&5á'{|ęõšrĶn›Þ{ÎųíÁ@b8waýóŋũ{ýígũéO?ŽĀkï―ōƒ·ūĸÜÅ{Ą^ʘ!`&€M43T“‚ū^þ æĻ/Ššæ4˰ :ÁČW]ÂÚ*##‚·=ŽHĖH!MvōAš0gk]Ð\„ Ņ>#äŨC|’zÕ# ÐÎ{-X°lĩ•]·ķ4åėŊMÕJY™AëÜŸ j dbĖÔZģ()ąiWSböZQÓ>|OœĸžôŌKĄ=øešŌÞįģ2^:?þåšúĩÏþÃï>ųLĖŲ[q|ŨOßũĮĸÔ?>2ÜÓŽÅ+€?þŊ—ĮÎü‹ĮĸÍŋ|ü;gv?Zĩþ mKÄ1gU |Iwq›03Ļ3ŧa·’_3ë­Ą!ŧËÖf­‰nWēúƒýŠFē-…ýŨ,žÎĶ&)ebęĩ",X°`į…(†Ý+…"01!Ζ$šŨ:_ːC―”rv TcŊƒõ°pãÄ.ó:ôÐéӓCl―^OebĖ|kn7āû\Ų8ÚóĸåâßøÔ}åÓOۘ`W·Ök­‡?ēėýԟ~ėŦßzéŲ-w˜qģ+/―óôßüėņ]ĮāŪrHM…UP0é=•Č|™si‡UĐãˆD‰ ĖšŦ_gįą*ä’[UÉ)aũjŠc‹XÛä1ˆƒ!Ķä,„ø6―Ón1"_æÃΧæaíĸŨą`ÁēՊ4#DSčmö†™ZũÓöXNC>ED­·Þ{J‰LĀ(ZMÍāðáÃ>ø ˆD-nWcŦ ýOöÞ<Šjkß{Ÿ2“―„^BïMPD zEÅWQTė Ä DAŠ( Ļ6ĪØąÐ{ï- !^&sÎŲåŋŨÞaū\‚åûŪųžßĸ7ë‰yvvÎ9œŸžģÎŧÞõ.-+ļ8u@ŽlÏ­ÁęL―îÁ(à íï/ÜqßÎ^õîõO.ž ŪÞėPĐgĸĪ jq^ëĒŨk~WŪ āg•Æ€RVę ÓŌ<°Ý0?`Ú~ŸįQílŦ’w„ÕŅ͚đ ŧ! ÏLwÕqŒxin ģŨĀ­‘€âKŧŲVÜl1ŽšüÎYķüčī™Ûߙūýó%‡ũíË\”“=@ۃ`üg.m„)æ+†ZlûmBôļĶEYXUx ÚŪĖĻ”mĖڒĄk\cz ī C)s…āÍ[ī0 CNo”­b ”:č|`Ĩäîđ;y8Øđ]Ÿš•ЇöóžœéĐ3ųãŽI+'ÏZó)ĪĢ*úWXß$Ûwô\ĩJÕŠWŪĐũÁ6VÐ #€Qå[K•ˆÂVÙBOĖ9sÕ§ā]Ýđ+R3րyП4HuÄĐ2ÓMJĶ›gÚF—ZØ(7ĶeÜDÅ$ģEEîŠoŪ\—.âĒ:]Ņīkĸf‘ÕâݒątőėŽä𥷀9Ų§Ž?~*ý\#Čå; ÓČ=žįŦU{Ýp›ÛŋáC­ēdœ2 m––0=ĶP)öĐPo ’mŲšý_ËĪ ĄĖóÎëO‰ã*ŧĪī”/­ôŌýļĪL`äâ~ˆr;ïŽk‘ĻÆ Û•Ýß]°u[Áö(#*:1šjJÂó+?Č(ÎC*ŠúŠu4û=TrE•ūĸėþz1•õ~œgFƒF@°ĸĘg•0MEZʐLƒ ’•8:ŸÕ#Ų‰’+…ęI NęÂđÎgĮ.e”jAģ―ķmÂĖ`üšjd$3XƒƒxäģžĮV­I;ëâÎWĪ4nU'"6Æ]ŊiÍŪWīĖ'öŋĪƒ”ý›ôÝŊ>~Ũ/Ž{čî›~ų―ãÔ$ø·ĄÖ:·nŲ+[ĖÙíĸ8†Z(Ýhï™Å(ó\WąĕĄf ZĶÅáĶqR#HœR˜%$”@åG…ÜŊ’”$•^’Ĩ•Ãqsrräï.ĀYý÷‹ÄÜ3N #ęķÆËîŊËZiDrЙá„DĘ9•™—zšÔ?ļ‘ž}õãþņD”iëý;jŧåœ7Rf5ˆnēXĒôéÞķĩŨÓ= Z'Ŧ;,”Ŧ™NŪčWízŪ{Ã0ÖVg€˜85ŊA5,pϰ ŪëaÔĢ€öôRJ2lpŪšu+ víĘ8”hŌą17ŽÜbVäAž`."Û7ĖaÖķí§…(}ŋxŅđėï–ž{úþ>ŧæ=ŋtÝ NpPšU8L{ķÁ‚ĀČBrã 3I4$XČ. L*hãäņŽK•…°AĖåŽŦäqšk‚ÉĮĢ ČÅĀ Ũq<† “xŪãzðÆĒÐeÏ߆€C‰`^Ðq*ÝōļŦóĪ0Ô ]OĸoÕ;^PîĻ2CéE° ŪG G8þ6ĻÕ-LeĻ·ĀVŅŊj ÆŊ”ęF2íÄ*Tâ&/īzTWáĨWlrrđÞļqãæÍ›5"ŦÐk[ˆĸ/nČād{1vtƒ2ė@"ÃI7ÔK°Š Ü+Î*Î _ÕðEE*@ E‚T?Ši‹ØķņV‚ŪaíöĀđ6ęfL*j˕Š$ĸÔ1ÁšÝ@A$0'šeY{~sÕ­`™–žÚ ŲmĶBĻþfFÕZëš 'Ķ6dxԓ?V„_-gbρĖļzÕýčĶWtïŲ}ņę4ÛĒ '>­nãúw—ī}ūÃ+ß―ĖÄQÇöėÖîá)Ÿš–?sįWũ đēߞ·Ž|ųt‰0 ]ũÕĖ]zšĸþۆÜþŌ‚>Ú°üÝ;úöësíāĐßí5}þýË&>ųėÃîūþÅŦÏNĖĸķ@DjĸVŠ-YmM) !ÔÐF/ÔPë([SĐšī˜ ’PÏĨŠĨJgLIUŦmÝšUĘi%ėĘAđR]+rßoŦ° @&V>­%É\,Ŋ˜ĩ›—ÝwxÐÐÃĶs"â'ØB‡ģÓC4HLjP;þýßŽ;s4īy6“^˜U ũ"þ,æSÆĩļ†HCĨŲĘD‘€Ĩ·Ā ƒ ÚÅÜĀHË3īĖ€˜Dp­É6žõOíÝĨ. dŪ€ęcZķÅŋ:(ã§ÏæGVN,vXĀŸšxōļĶ7sÚÔ"‡\nFĮœÉ*ÔT b[îŲSË>š9î‘1ŌvjSŊM,ûqåØÁEL\RhĒŊ_j⩜—?XüųÂųkF)įâāó^˜0õÄí7_cĒF=oĸô›oŸ―%åŦųģ úთgîððåŌzWßļzuznŅũó^™ŧ7ęģeŸv-úaܔo°mãŌĸÅlßĶe 7æ>óÖīîþÕO>ĩô‘Ķ éņážbīï3xŲ?>70wÎÄĨyӜÔÏfΊÜgčŽ7ÜþÉäã<2ĄÞÓūZąðÍž óŨ+Ē'ŋųÜ—MxoþØûŅ™mßoÍpŌūxĖÜĶ~ōî} g›t,ĀĖCŸ~õCÜ5Ģïļšģð<ôũE8ÂsMÕú|ĨÎڄŌí–åđT.9WS[Ā Zþ…ŌKĐDØP0$ũԘĮ•P’y63/?_› „zÃ4Ĩë8ŽöTËĩŠxŋl1AÜáÜa­k5þW(q<~Þc‘#bŠę`Nfč€f I­;Æ<þáÃwôŲmā(―đāÔĖõ9kcíøŲ=ŽAAė8Ԁ,·Ô #ād™ ʈnYæZW‹Lpĸru]ąĻ(Qu`$ō‹g\Ë.D4XrøðĪþožL;q$ģØŽŠĢĖĖ-.Ū]Á$ŊĪðĀņã-šũ›þĀU]DŧpÍÓ/OƧÉvDÉڟīšaōm’=J-ÓĖŽņX<åū ~ču—ĨÄđ%đđÛV―ë™{}.JŊ^čbĪm>8éö>&u”Ÿr8Âņw™(ŠÂ‘@ČR#xĐŨŒÉ•:É4īÞ@52Ļ’‘* a pyĐ Ā `°M0d‚ųđy•*W†Įp!E- ĨĨøš{~Ą<c#·ŒŦZvŸ O•ķt˜f Ó‚Ð~#Ōoų‘`e€ŽHäœĨ'ōø [ЏP&īÖĢqĪôNjrš€ i ЗãŌä—Ĩ"Uė8Âįũ‹‚Cī æ1čôUéVӂáļ<Á1Á e.–[T„Ė+>Ö,Ę/ˆŦRÉÓãō+ÓîŒũėÓ/*ūR‰'ĒøhE<Ôuk5šcÄØ^ĩ-ðÉ ï>õÖĒ7?úþäÓË-G$Uņwh[íƒY‰‹iĀkÚį†ëĖeSGüĢįgþí‹sÅļ/?"k§Nĸ<Ã_ŦNĩŸ·l<“ÓlÏ7?ïMóōŋ^<ÏHđé†+cžúŅgžštH4n^―f›Ūŧ:qæ ãß{oÆ[“>Nw‘ð‚ ‹ÂŽŋÝAĻīH{t1&lÛt]‡ĻB†&m åDËu€@ r­ŠíūŸe؊äjn#‘ä@Qa‘įyšÛ―@H Bā Īūī*@Ļïj8CQ–ŋėČqÍĸKú‹(uĸ•į5Пą°Qƒ|ŨŅō}Ęš.âÂgÛ rŠŲ H0Ū&;ÏĢð˜Š+æ\óHÝ/§]%}Ó#Ë,j(ët“C eC}0Ĩž\˜0i˜Ąŋ8ÍŧīopbÅVVPŧj­ŠĄ˜† äw‡ĒDgÉĒiûšõinųLÎāƒ3:đËã/ÔŽž€< –―ÄfŨ͚ŧãh~ģ!ƒHEąĻfį!ó?hēnë^—DķIŽŦŅā֗šã˜øäĮÞYÞiõúäö—Ly/iëþŒæí‡ ÎØO#qý67|4'åČÉ<’―åíw~ä†Ņļ뭟}Ðėŧõ;%>6hVpĶAû‡ū0ÃĻïRsĀ=c{™5\—u0Ž^o_RƒĪY3ßݙZÐ|Čāëũ3ĒEƒ”GÞnîgŪįŦÔâÅioÕōGßųę;5~ŲčŊÞä†7<™Į#k>?aō;ÛUĢ–<~Ūi― ß]ýýĘcđÁ†Æ0·ÅÍÏ―äÆû0Co„#ĖÕáx^õeš`ĻGýˆíbL ųās­ÔĨĶÔ!)ŒāđÎŌ~W€&fQQaIIĀķ}J|Ą1·lz+ÖŋP_3ä—vƒï(ā:!œÕI.vßčO„–…‚—NHÃB]Ýu]ÍPŦŽ (s1İŪb‹2OOoœƒ”ĩ,SûÏĘĩšÍΉzkŧq%c\w:(é.….4RJ ģ  !ęÖ­ÞŧC―_6­ēžVĩԉ‰ûŅîéĢi§ķîėØ ĄAÃZ‚—lÅ$ĩlWyŪn]ãČŨĒëu­š#dĪīâĖó‚NÆ5ëõOÏa(ķ}u–į•û^óÏuIĮËZt&`3ÔĒ9!lïęÅ/Žûļy6ŋ.úūņÕ7­bļŽWĐaÛ!MÚÁ;É<ŨcHņ7iŨ]P—rQ·QëdŦŠuÕÛI–Ō}@ëžę6ZīäÔcœt­*_Ĩ–ÝšRĮÁInū­‰ō”@M[LOŽņXAR3ïȖí§[LîՄzũUę}ãāÞTmāpŸÜķ2æŪGQ8ÂņwB-ÆÚ#Fõė :•2Ÿ ādQƒÐ[Ĩī•k8Ũ˜ĐĶ| |ū]Ę0`.·ãåÚïAm™dB&#V(ÞÁs„a(ïFŠü-ÉýFüb5ŋŠČ…Z&XYčŅ€.€+(TwĒōV)܌ˆņ+›FĀũĀâęÍZ*HÐõŧĄSƒĮó…`•§i§ēŌ—Ę€FĀY@mŧĘņV(TYZé(ÕĢ!Š€ZŒIŧvÍƛvmË=r ".š2 ŠYQAŊVõÛīnŠk˜ĨĮøąēðÜ §—ŌA=—ę5…ÃՉÁ ޘëĒŌ€IņõZ]þėóÕROåô™{ŧV lęÂ[čđĖŧČÍzŪĢWĄį`\ÔĒÜmp‡—Þą§;AcŦ…Ā˜$ÞĸėsŧũózõÝĩcõh“q.rĘ$Āþ2áøO˜˜ŦŌRK™Õ"Åc*ÂØQ@3eĻ  ÓŠRŨuáĒŠ’VģVmƒ˜ųųđRó.=jEEąąqeĩ\š{UëRŒ™kåë9†â\_|4ë ˜ūČ u36BĐ)ֆ \$Zĸ…Š‚zĖC‚hLe Šy*S†/uũĨ6įō3 y˖És0UĶs=W.°ÂeUûBz8nĐwŒĒV=ĮE0ÅĮ§Û đ2@n–äŠjž9֙ē"sAwŽ<Ïl 8„)ãZTĄ1:wiÕļq3gēēģs‘@qukÔHJޜ gL Š ø4ōĮĩîÚŦ­Ģ qCĸ;ïnÕú-j5nĨ ØgÃŽĸä‰đJ3ë(°°@ËĨō\ÅfŠ:y`_p$+Û@Ī‚Óö; žéæėėlĐ.:uę”Üt=O:‚sD…ĩ‹KK1†äUQģˆĄ`€1đ§Ü/Ą€;Î΁&LPČ$–…MTšÖjÂՌŒŅwŽ.r Kœ€\ÝËQ ˆ1c\OxŪ†€mÅï2*’b+]3h ã–xōŋ€üP( Č*M0({ˆsēģ ä͚æšŽĢĸ5 H ę‰[ii-HÄ87Tp-Zp·Ô̰e)WāĢå­ÃĮ’jP.m—Ļ8ČcLHË*I•1&ŠtŊâA{ž‹þŅ*gÔa…#ĸéPŦúĀ6ÅïCĻ4AS"Ãs)ô°úl· ĪųŽĢ€ƒhûØÜŧgw§Î]š6mš’’"ϒóq333eW.ĨTb™\ș7đyđL€ F„ŦŒˆ€dķĻ„™ƒŪüĶÆ˜e§’ðĮM%Ģē°ý&6ËĻāBubuË)Í*:()Drƒn°8 ųa’’Ŋ$ȅÁ•9”E˜âė<ÎüQ‘qqqpybP•ˆAߗ 5L&æųy‡Úģk‡Ŧō\ S ãÁKBF~^`uęîŦüJ,SņČVģĻ”õđ›Ē ƒ (áÄô'DÄJĪ+,.*)ĶfĨäĢ -,,ČČ9—įŘîđĒÂėĒŋ!ρAšÅ5L “ðaųKęĩë”0äŌ ŨUŦ&]yUŋ H`UȄqúÔĐškÁq˜Óč3ä°ī\>[‘đcd‚ Ū{úĀāZ fĀ· b$ÎJ§}ûöUŠT!„čMđģGE›6mäĶŪōŲųýīųkï=ĶQ‚|iėŌž’äįâǐdYNÚã úýe=jĮģēā3ŽiŲĸÝlAÞkķi†Âņŋ•ÕDŨp ũQ ŽĘgKį*Ïl0Ĩ•?*;WƒŠq°XđR‡rA9’%ŸUË iĩ>Áóž(Č+$FĮܑŌįąÞ·œČ;óÕū/ķdîXwbcV @Ø8‚äyÕĮööŠÝ‚˜†„~ŋåoߟz!ė2‘[ÄxDBlDlq S—W0ëh^Qą›Tģr y‡ŅFdĪåŋĪF·:‰Õüf„Ø–ÄklØÄŠDâ›ó–2áÔö9Ę G đątíÎ+”RW…ė'֝c7Ž?|pŋÏgŦ~hŨÕęÃ$úÅĐöĒRtïÞ]ĸ/íWŪ\đS§NëŨŊOOOŊUŦ–ÚD…gRWþļåš§ôŸāŊkŨũ~71bāI+žu<%Vģ}ÐbåwžšHÃýRarZāôâũfÕčũÐå)Õ%·n˜ķĨÞŨ rņ/ũgÛ>ÆždûüÚö…ƒ‰Hm"eSwē[þˆ8h ˜üVjõ+‡r Āý>ŧTŸāļBŽÕ8ÎÐ1Ƃا>m˜ãxeäܔÞAnņâ7&Ũ>šomÛqĻO_Ãƒ G8*†ŦUÂ|x†88^ŧD/įŠÁ‰ PĶLž äRÛPvĐÝe†?+ëÜîÝŧëÔЧó)\&ôėH”” iÐsjWŊq_ŧh 5ïė{ūøxÏŌ?ã4˜vlwf›ūü %.úF6í^ĐĮšü5ĻÄ8ģ7ûŸ]%EÄKp?›—}:7}UÆN͙ũLčß ‹ôģÁqĸßu‹ŠŦKÍþrDPÏėqGfßÚÖK/kÝPFÞ tYOfˆŧví °ˆÛķÉÕtUï2UjË1}ØĸrF đ5(øRŌR.Čã*ĀŲ‹óÔÔTIHœ-_v‹ŒŒ”C1Ž=*YY éøŊDTî™>Ÿ/sĮũKvd ģ[–ü”ÚũާÜËOó–LđoúŠŽ”ÆÍóy”Í OMsôŠ-Y uZū4þÅāÎŊžyņĩˆ~ōð„1CŧíúzÎģ“Į%·ya똯•|Z€ ÓËOŸ7õ™/Öe‰Ę-Į―úbŧøœ·&LüvýÁĘ5Ŋ{î•Û›&ŋ,ýč€SuįwóOG|öņÔYãŋ8^rý#Ïßy“UKįĶģØU˧f‘Į_™Ø·e-'/õ™'Æn;˜^Ĩր ïŠÉúęĢsfÕýK>ܔŸ?ąwģj<˜ûÁč'n9XŦϰ‰ôį|oæŨQÕģū\ē%ŠĮð·ŧņÜúOÆž1­æOÛŽ|âķÞUž{qüŪÔė&7ŒžtoWæVԀ p„đZÐuŲ–ÏĨž`ÔT“ŧđPOÁ–!à šÄĀzö3°,KĄ7T§iÐuäGĘÍ͑OŊĄ4–_„~$ˆH7čŨĄ”ՏI~ēûcōzŨ͊­áßtxĮšãûúúښ–éũĮ™ņƒkÞ} ðÄš­Į/Ŋ{Åc—Ý,.>—Ÿ—_°1cãþœc=jvØŽgaNaūÃj2D ī™Ž†NH…^‡ðF§ÛeŅ6īÐe%9-íĀÞ=ÔĨ„@–„1Rū‚Ø$$H›\É3īY-ãLŋĨōK‰mĄ™Ą" VŌēē&éĐz]ųßĘâØŽ;ôĖtą ĶUœđûõqïīšoė·ÕũԐ)klzïÅųÁqo.úéĢώœ3ɒ@­îCÞĸîØ‘sVŽ~īcÖ‰•ë\wũõ=S27}1üųĨOOŸŧxŌč įž:4qȂEÎô‰Ožĩ!zʘ1ŽSåNōÎÏēū6~ėSžųÐîEM;ąvŅčŲÏūôDĩ]ÜvuŋûŸuyėŠgLđåŌŲĐk―ð…ïņ§ïnxčģûGOø|ęóŦĮßĩĮėõüøáßū=æéWJĶ―ōðņ5þ,ĸágę°ãƒw?XÔéĩGW}ðō‡'"ĮŋöÜĖŅOÎŽŨôÉūÎįŊŋ$n1ôķïŋ6qåuWôŦÛĪKrĩwÞsY§?|ōâæ‚JŊū1 3›V ˆ#Ü-F0R]üœq-ŠmđęI-uQý+(`ÕTGýëyb>ÓįyĘ{Å0OĶĨÉÔIBíEöĒy.Ĩ4 šjRNĀ DڝkvˆsE<"éË6ŽhUĨ1ŒØčÛįkÛn\ƒĐ[Ē_UŊ5 x'sNäžĖMû9s ņð°öC‡Cē{ Ą ĸjœĒ$ÔqÚʝÐBï—Û –Ÿ ›7Ž/ČÏĮŅÝeʓÛ~K!ąÐBđÔ0ÕÔĶ-”zŒ"ÐķĄ *jåÛ(*åw!DyzAĩ–8 …K_Aļ æÃ/XЭvÍðyÞčÏ[·āËÕkwI=ÐûŸCŊūĪ‹Ų.öý^pƒNd:5ŠķþšrW!ó‹‚`Bí–-Z·ŽŅŪkÛÆ‰ßŋąî`NÞĶ•_įî9ķ&/ąĀĄQ~Hi‹NîÝō펧_^1 O#J™{vÃŌïrŧNÓģCÝ#îÞíW 6ĮB€KD>Ë@FvÖđ}ŧw·nÝZ–鑊ō[–^‰“䃭ķsuĻÛĨv§I-VĨmŪŧųāĶIŋTuųmqŅ1>Û'ņĒibĶ•ëäįįŸÉ=—“›wō܉ÅĐŸĶæėY·cïäÞÅųĨĩļē@Đwthâ8„ĪúB;:ôZ“eųāÁƒ‡Tî’D`Īoˆ 5™QŲĨ[&Wo1‰úðk īŠŦÅĘĄ&ČĄ―õŊV!h0•o‹Žā•'o Ý­_Y\båˆøÂœ|psN ãã"lŽP­ŸÐ V”GÉ% ęÉNaŪtüQxãĒ)Ė?ôÔĢ7Ö=ļ>€Áã8čeđ"ĻïĄJbĩ”&-ãÛĩŋģzÃĘķúčAąq‰‡-ŋÖ!Q€Ré\{|͔ĩ“c|1}]æ66Q|ƒˆuŋ{þ›Ų{O•>·žë]G2žųE…gr27œZ?ïč‚=đG}Ėš,đŧ_ø=ę^åå1Ÿ‹î”Ý,ûi!•ķ[7oĘÍÍÆ‘_ŠMH―-œĐrĒĘkՌŽĖjWΰû,ËÔ}n&ŽÄ_.§•.ėŌŽŒr!QXŌ R"zCbrŦNÕNž=~ė7?þ0}ō·CJ­8ӊīv}øáėŲ‹?™ųęšâ˜\ŅĨ]ģ͛=uÎ{<õÚĶÝ9Ķiór2ƒœ:E§3NïØđ!‹FÕ­ģæŧOݘŅöęk›úũl;žë3ØĄÔ MxrÆĒЧ\:ðē—Į?7}ÁÂyó?Mũꚥʊ·Ÿ_ōõ—§―g·îÚšFt°Ø xðūyŽl6š›6P"°Ïį–/zåãŊ–ŋþÎûfģķ];uŋąÜ“_ýęë/ޘþ~JÏn-át7ðTƒ/°QÄ}ÉÍ·ĪŲ™^ˆ“~6'‡#áŪ î”Ã―ųĒü^ŅgïĖŲ›~nĸ–6Ÿ(Ū]?Ų –NÂÃ*ÃQAPŦû­Ęgu“˜Đ; ė8Is †ÂNh:Ž6C@ TPĶ-V ĻøĀþýŪŦč|üá://OB•ߎ8W|.Ŋ8ŊgÃŅv,u™a‘Š #æ­zāó1Óū›{:û u]YOË/,ܙūcÖĄ9‡ōO`ŠęÆTŧĐõ ‚aåÅsę?ü~Ņ Ã8tčÐū―ŧõ sÏ#PcS–l•š%0ŠdčʒQb€Ž^Ä) JIE˜(ƚ˜TԂ @Ąm™_Á=ČáļÕŦWWãāLNhüðØųMÄą™ïLÝhøÎčŧ+›ˆQžØŪ։ ËŋøåäČŨ^ëTŧJũA#ŸžĒöšoÖ$·ōŌļaąØlw݃ZãeßlždðÃ-}ûōŽ›ï|ļzaÚēeûc›^:yℾÝßŋ?ïãUĮ2™~ówEômũyÝ_–~ņã–C#öÎįgđ$öĢđ;ĩzū5~LžÉSz žēsUÆxÃ6=ŊïŨ‘1–”ÜöšëŊô‹æKĻW%n㊏;u§ŒQ·RôįÞíVģāÃđŸÐ:—ūōčqoÕãĶŽMãåYUë·ŧōēN&c=Ūx öŦ—|°`Ҋ“gƒþؚŨXŲĮđÓ­ĸ-Lf$ÜôċqΆ­N3æŽürÁŠÍđĢ_ŸC―0][AÖÕę2—GAȞ 5ĻQ „­ë9)ĶĐō!Ž„Ū qF9R™ŒósbŠ‹‹ķoÝrIŊ^IIIœóēDÁÅÓ[ĩ€q‘žÓ-đ[›ę­)ĢMŠ6nžÐ`MÞ&lZ%”'VŽĘ-Ė|mÉ;fbõGš]›]›‘u†ĄH'ēČÉ”ÝÝyHøzé™éåŲ€ â‚zPQ>Õ‹K+SÂÍ7æåäÂ( Jđ0ïRåØņx”Jˆdąî @Âuo &2čN3m‰þËEĩõęՓŸXÛ·ooÞžđÔ!„h™ïßŋ_þØ Aý’ĩ?lBÃޓį\Š_>§.—›ŒŨíûúŒGbU:t]‘4æÕiH…ÖiņøæŊM›TÜ1čvę:"yāė…åŲÁ [·ãuó> ïHĶ—į―q)ōU1aʈRߚ ĩ‡zgx鏎ËÄĨ7ÞÕGxA‡ĩđt@‡>B^ŠF›+ïé€YÉđ@ņŲķ―_5Ž$­žŪFQÉĢ^š%—[—ÝtfŪãyĩÛ_u{'$ĸiYýzîę‡ô―€ƒÅ°Ņπ %ÞüĀu,oÓë–/ÄĐĮqۅ—ÜĒ( t)úũ"áĀRŦT~W>W>ņØ#Kŋú2ÂaЉBĨđÚN›ÉPSĩ!…ƒmÝŋ @dJLm†€Kg@îwïˆ:wîBŪ?"m!dw“„Ī d Ļ'Ʉ_üBë vô額D6løpޜB)m L 7\ Î<’c}Žõ˜ ‚‰’g0āūąĄ'âčįŲ|ó Á^›(­&‘ ŸÏwôhŠ―VŊ^]yĘŋCŨJņŊlU[ĐĨÕÝbŌ†BķDKųA‹-$þ^X1 áąúDÉ9~pĮYŅ­mc›TD •"jĸÛ§°ã{·Ģ›4oČÂónĸw#ĶiĘŲQBˆ:ujđŪũïfĩډJëÖ]ü  7AīäЁÏððŦ›wõŒn!”ó·Ąq6ƜQD ËķĨŊŨŽíÛÛĩkŊ›\ËeĩĻƒēĪĢĐ ĮsÚ&·oQ­ÅÞĖÃ1J(‰#Øö‰―ŧķlnąŨ(ý\fn~~œHhfÅķiŨ8ҟPR\JŲô"týÐfųôķėúĒßĩð`ÛķÍgu7‚Äl-Ã4lxŧ4—bšF)Ί—Ę<Ąt &"—ŽÍ―PmŠ"šÅäØŪ];ŲČ ér)‘›p[ķlYĢF -e+Nh‹1_§ņeuQÅMßĸ“SŒz-ŧaÁĸÏãl8‚ʭþþąįo}5H)Fķ‚ý<˜ӛœ™ÔÁīp“˜PFõLYΠëá襃ōO]þ‘3ÆtöZ>Ÿ―`ßqĶ&Ôęæ‚_ÔU­ûn]ēŨðEp,ŠKHŠ8y2uüēũoJéíƒŌŽËõ<9ũÚŪŨ Ž.Ā͋.ĘŊõĒŽŌ žö@zėß―Įu]Ó}†nų ā•+lU2â9؞™—W&ëZÏk˜Ķ2ƒWĶsÛ ĩ›Đ]ŧv:uBŊ=Ƒð?ĨT7õ?Â.‹)(3ˆĐp–—NÁRŪTķe UD†.~=―QgspÖc0ÉčK•đRÚā€ĘČ8}ðāJĄ8Vž7­/ؗh*éČč\ŋ} ņ1F8šAAƒ"ūJDjÆöëo8ą=-ïÔЂÓ5j&%ÄÆéŽüĒúĘåuå+ĸ+ybQqņúuksēģ l‚–Kđ"ō8Á‘§”Bkfaޚ­ĖÅÁ Âēīfƒ"E8čÎõŽ€+ÔāDÁĪQƒīē”U2Iđ.Ļ2þ“=_īfƒĻîĸßF8ÂP+ c%ZŠĨ™`·6úŌ |ƒŌe€­†Ūb1ʔÚIUؑ0L5ą\pH ÕDœ]Û·Ëj’ΗĸĪAB­$.ĩŊãđíë·mYŦ™t唂ųŋįq_%;hfn)ØôCæOóÖ$ŨŊÎŅåņīžĖāÏ+!GÚŋg·šČ 4[ęƒCË2Us…AGĨ<'aa˜ĘļVĨŪĘhœËPšcí‰ôĒ"5ōŌūrÛķmŧví:ĪBķJË%{[îó•XķO…mcŒ\éŅð§Eų.ãčĸŨŽðĀpHą ‹”ŧŪc@†‘ē’‘K[=Ôk7Eđy^RŠý˜Ö;ĐRĖ8‡lNu:9rčŌ߄RúgÚÆ47* ;PÜWßŋ]ŋm‡ũ0W€ā ­B˜ÐÖbœũIé= óYv//žÕqqJĄ\\@)„îJēīÖŊ?{6†‰1Ī €ÂīmxEp$öûmŨó L„+ĐTÐ$ĶfB€PÁą-į.ÖVM,‡ž_[Ž32•t‡”"HAV;5.ÓÛ―{ũJĨW“&MJ_ !%ŲĮ—/^tīˆXÄŨþōk/iSŸ°ĸýįtlÛü›9“wû{>~måiĢ'ĩŋãõ~]=Ąĸāø7ˆöp„‡“—Š dd UM ”*LŒS4ÎBgÐjRŦH5IYĶܓûQÔöm›eĸŌEEõŋ•ęę§]―. ũk{Y\d<ä­T А^0Á)·°1âšû\Į―^Ë?,—―þŸidĨīR#ĩkĮvýāQW9Bø˜G•…98iA&2ŦøÃ"\›‚Ŧ2Ģb™úģĻ'§Tsæ_þŨŠĩÉgÛķm+ ÚgUČB™4šéÐĄƒŽ•eee•r)ÄðÎ^ōųGé.Þŋxæa#I-ē-BLŸ_…NsM™öÚ–Þą â;ŋŌýþóŠ@Ӗŋ@*dēėũÉõųl•‰ē'Júæ;Ÿ“‡…—žŸ[f=e5üĨa+ŠÁôëðYę(lųüķíÓ{Ä0Ïß<kÃŊBwxÁ?íS;pŒeų.~Œz™ðšô; w0ÞŲOæ-Č·āŽ°Qú+ŸIÐG8ÂP+iČPŠTÈæŌ ”Ę}9,TÛ1A ΉđÎ0ƒ—ėŲð랝ŦįLŸ4kþ·…”@y9Ë??ņ‰s?Þč" ģĀž ŦŽÞ>oæ”ŲĘcĻĒyŅW ߛ8qԚ#YĶed§îX·ũÄÚå‹'―õöšƒY–ámZ2{ėkŊN|{Ųþīb'ïØ‡ï―ýÆÄ·ŨÉųSv―áw‹aŒ•ĘHF&ļ6T5ÕÃŊžBˆÕ€A„ 0ŊZRð U3΄Jš° „50Î:{NŠūļšlL/HrÉųÐkmf(Ŋ\ú[lŒžv+*AÓ*ðEeGÆdGGgGEœņžjD|tœPégųžõ· üĒtíEmĐ Øĩs8˜zTŋXĮq„zą‚=n.ģ|ŋ2āęAßnLÝō݌Ŧúö™ýãÆŦ>úŌëÜ\1ųG&L'Ņþ•sGóZÎđ―O>þøŽsØĒŲÓF=úáÁŽeO―ôX”uîém>ÐJ +˜}bۑŒĻHüå˧ßĸnBÖoÔĘLQpâgOXžJ’_T,JßïօßĨGÚŲï―û―Ï™ūāâ17\óčĪmi'<;°ï­ßydÉÔgū YÎĒįoðĖ;;2ē—ž9|Üŧ‹D–žyßø+Ė(ë›YOŽ}wQPļ‹^ļáša/oJ=öŅļ‡,=fYlņO―ĸía›eŒā‘Y䈏\ÝʧkŽîXōðOdļ8_l2^œŽšįæŋpÛgŋÄÅH9 ôþT„íf„ 5K—qáœĘ]„QŪē3"8ĒÔ3ˆĄa‹!́ÉlĻÉ ŒauĘøöí[/ïÛW*ęËōĪå[ĘĨ44ōĀõœŪÍŧuŽŌý‘̐n iK\§kŧŽ―:õÐ―XŋåČUÞP&īŊŨåŲ[ĩbiŨʔVÛõĘsķônĄ ĀW'ëšßAÞ")ŽŅ2mzärĘ8\YŅ@cˆmÉ ŪEŲ&›.ęėĨ%džj‘0ü~vbĮSƒ/OKËŋcęįZÆþ8įëïŌ‹_Ū^ãl’đdíÏŲî?ÝÖīËÝÓßūoõÜ'v}”ņĘÔwēš1ö­5ų9ûßwKį—=vKĘŅ&ū{ŸX˜}ÏėëĢ&ÏXēĢiũ]?žĸļ_ŌÃSŪ~Åã5ŠEŽÎۖšÓđfMô”UĢéu—ļ…YGjŨ8uōĖoKĮŅŅMßÍý"ïĩ•‹Ī$ļÏØ6ĸ“cņ”ą­ŦÐ$gøĖ_ūžņŠaųV•[xá…þĩßÉÝ·ŋÚ}ïŒéģämïŧSi”ŨËóU6ō•gŊhī#ÅϜM{Ŋh4wîŪ[į~ý`ÚW%“ŧ&ýīũĘNEūěî}~ė gäíÝ{ėpQ.}ę·U†ŠU5xüįų?í8u V­{éŦ“ߎ”ûË]7ŋ˜Œč1`@ŧw lhˈĖï‹ģ Š j§ÜÐĨY•° íOE˜Ŧ-mpRaBu=ɝÉ5Wã°[oõÄĘ˜ŅŪZĪ€„Rærā~\NOÝđsgˆ?Åŋú·e]ĩ4EFDöëŅĮß/7‡á]Ý;u‘ķŠ”Ņ‹Šŧ~kQū&VöŽ2ÉŊ!}iũėÜé:.VÝÆÔÓs‰z7Ô―L”ŨPsÝãŠÅFÚ VϐlĄfŪM€đĘ?QK˜ĸęĀ*ĪÍđsąÐS$Ęūī$hŨmĸæœĩ­ķeÓæ€‡äïŋþ°ėXqĨ›‡ ŽEAiVKiaŠ/šV­.Õ|&ĨðqŠđāvD|B(.ĄŠßv Dåk^vzɌũf―ŨúЇZÅ{ÅeÚðåŨkÚ]7īS―JŒk%Į7~ýÄ#ĢįūüDnąÏē~ŧ<(<'("+%VŠF ʰ‚šv|ÍÄXxlŠŪ\™ ‡2ßžvMƒ3ïŦÕĪX!ҁĘC\Tl“ZU „ü‘q–š)>t$īu$Vu―✂`lL•ÖM’M… “ØBPÓuÏî^·ėŨ]]G ëQ/&(œĪ֗&Į›LŋvęaÔEÜ@Y•‡ûļ{ÍSĢïžþåß2ðúĢG8ŦÕÏõPSę%•”š ĐÔUĄĒ c˜CŌ=yĒíģõģ?úČÔÜ-yM9ŒfũŪ]]ŧvÕķŠå’Ų‹§·šCßÛvðû"”U.Œ%U­:ôæÛ$‚”kŧx”OĻŋmL&†ŦWĸ*'NĘĩķāžCg‡įđ\aŦiŨĨúæåV”­ęŋ€O­7ð ‡erĨßāū-CîĻÏ0ü—w.ȔV2ãRōŨ/'ķÕÎ^rtø?•”ōÚä1ƒnūelózwÕiÝ’Ūšâ–goč—•[(e.ÓbÎÏ-ŠÆFn1õ{ī§[>û8­íāï/>•œR7>ąïõĩÞï7zvũ•Gš"ŋÓ7ķĘv_'Þæ œq"‰~T2 šï—ïTï9yÔmģŸv"3’|U]U ÆBŠcÎI͖;Ԟ1ã―U†tËę0§Āƒ›"āRmϘŸ›ŋũðņúMĢö,>nzģ˜k^ĸ|ݘĮD"8āũ"aEƀ‘Ć)ą@p=˜X>ĨTË Čųn.đ;Ðˀü>?†Ð%~ÍBpbčĮmH“,dü[7oûÏˆŽ‘ÜĐQhÎEbõ†M&sÎĢãŦĨ4mhųcysF ūjČMC>ßëūúÖëĩ,UŧåĨ·ÜsÝ3wķNŠpDÜģ fwˆúäö\wëЇö ՋėqŦÓõƒ›ýüÏŸlŲ·hûĒŽŦ^ŧ~Ģ‰ØŒŊßĪ}ĨčŌîaF―čäÎŊL™TļnÁ=C‡Ï^ąšÔé5ýÝg·M9čöGN7|pԈ~åĩ·LŒ$œÕę·ĐYÓÏđHĻ^ŋ^ÍŠ‚q;ÞÜņãža· û9ŦōĻĮoŦðčĪétũ‚[oēâ˜ųús#jE š [&F`ŽHÍFíëŨL fĨG&Ό>öÍÍŪūíūQŦŌō“jwHiPœˆí˜Ö­ZD"2)ǻ-'?öÄÏ{Oíûiü­nxw―ô„ĄQāŨóްÝLôĢ?°bŲ稘XĘOĄ}žļ’‰bbč2gÔē|\î"Ž87 “—*D…\S—Ę]•Ū’€‹‚kXÖ°ŋmȍĄŋo† M\hząR|ĨÉsfūõÞ Îh|\ÜŨó?ŦZđŠÜŨ,oĢŨč,SÖ\FïhĮ–ysį,]ō…mTûöķB6Šgáhį„QМVq/ĶmzÁ ŠČKu|ŒĐ}%Ãóyý[n~}üËS&ŋýË/ŋœÉĖĪ”Ų Y'ļ\”-vYĶ•Ė}kÍĪü’žþ―úöîÔ­a―úg/*‰-ŋų;æ2:.8&U vï–Ïøj*#Ā%ej*°Å@ŠoķekĖ‚ûlŸēÝĄjTž”YĶ-ŨžjįŒËđ"„ŽÁ+ČGąqãÆ=zôļVEŊ^―š5k&‡“ĢĸŨBüįĩφ#Ž@! ûs€˜„ĩę‘ÅÄķ`Ļ< ģīݐĮpW Ō‘ī.pXW~]`*ąTĄÓXJĐ$ ĪŽĪ5ĨK‹Ž&­]―:3#úû1†ķ.ĄŌRŽpé™ÁõŦ;”;Ę&ÜS/JýDÏdS/1ÄķëÎ],€ÚÃ`8ÂņĸdVŦōY.TÓ+FšqĀ ĶÜiFĶayŒjVĄÔ/QõāžÏžVčĖä1Ķ\‚&FĘÔø.áaD=ā$™Xę·€ĩēģΝÍĖÜĩcÛį‹ĩ•T;IoŠúõëkīUŠ.8ÅqYÞ)?ĒŸōԃÐH1)Ŧ’HŦBŪ]Ũ-Čϓ;YįÎÉŊÂâBęļŁ⒒SČÎø5æH“ŅĶeëOŽ €óÔ 0  ŽÃČ/5HpŸÏ6Ö6ôƒP8P€šŦYf‚ÂŽpü?čW[:4GÏÓZ.đ#Cą~ķ_€beJāéÄČ\Ï!€ –vö }4—Š•UĻÓ,eÝÄ<Ę„ô|Cy”å3ĩma›ÔĢé§Ï,Oĸõ§•›5ëØąģ$ęÖ­+UdžaHŌ ZĩjpC: ĨÂu]đ–p\$ãĒ"™ŦfggeËĸÎÁũ@qq°$ ~U››ã91ČĸĮÞīFÕAŠš{ÞįÍŋ đˆ^ýðŪþj‘õ B@Ԁ7‰„Đėn’7=oœžŲčČŦe—ŲÝ7ŨĒĶŧĶš=ė^zĢoŠ ƒņėĒ„Ü+ÐiJîK.yHc/ĸ:ĢV`)EĪ—GûK”ÈQĮLķ’ĒĖģĮ‚‚ûÉĩ+VŽĩÚq1Ïq7ĸÕTkcÎB,z–B ÝÝt"Ņ~AïYšúÔë™äpû{ņĄ„ŧέ]‚sÛ ýX*šĩNo>~xĸåččðÍëĮOžūxųŠBv,Kœåíķƒĸ6°đļļü}~~rōģ…Z5•Úøt·ŧÞnۚzmŸ9gUL rē>sÔ1ūŽ›Õ$%Å>}o\CŽ‚KĢeúw8.IKņļ#"\ž0"M…€Ïdˆw"{‘lŠý&ƒŠPdsr**šnŊũjˆ“ēšAî@īGēVGÛ)ĄmÁ* ō~wD‰Ï9gMĸå{UÅ((ÞcVD„RēÂnļĮē‹ Ĩ)įÛRК™ČþōŦYû―GWŠĸöõĮņņŧ·‡žýĸžÎÎÛëėôôW#ÚëÝÍf{Õhw” <Ïmôŋ—’’đŧPTīĄŅåR–izÔėیü­xūļŠŒ‹@ĢļŌž`{`é–` ä’qgfÎģ́ņöž›NUBrÎá(ŋD…šôØČ ˜ęœCņš1†9zØ6îVŽXCKØðÉŅŦĒÚiqt},ųRj/īï·}ð™Ý]?eCŽÜU DÏl-”PŽ>"4šj^ēXāČRdßGJfŅFŠčvŽŠDEÝl/>}îĢĒD„˜Øûû$Į‘ŸgHĶÜïýïšŋ’’$v'éŠgïPBÏD8*T–šĸ€i&>l­+sCšUFwĮZësUmw7s öãfæ_ë@_–ÕÝåNoUŦ |CæCfQˆðėĶ(õZëޏ‰4L'Ģ$Θý@‹p·ŊPC-ąÝ―æÝÜ)ėÜĖ‹/þ\ˆbžVíŅx ļ<íýú|ŪÜERÍĘ6žšF6ëՕUáËČ<œĻŒ.õ7YÜŽ2ņýZm•ĐÃtœ­jsĢ!|­ë2(HáÖ] >Ÿĸ>Ÿîðųo{ëÐHŽŦIį!Ũó 4 ĢU%Īk]{įÄrĸzt†ŪėšĻ]&ČŠÎĒ4i <<§·ĶÕ7Ýyļ:œ@ExDW—úô šj&hōD uŸŨēžęŋWėá­ųĘk­ė+&æcĪqü Õíöx§€Ė­Ïj%đY·EĐöôū˜ŦUjšüėœėWAũýCp]‘đwîs7Ŋ˜ŽôëÓÝĪkīûyZĸý0wJjéļģŠę<žYkþĻ:Ÿį!lŌs@ŠÏš8ŦzŦ[†ĖÝj#!ā+č6kk†ũđũ!nöpÅEēšwÞ]Ŧ†p]ëð>ÜÍmýüü {4pžvĸeĄeVWÄ_Ëoc.Zꖚî;s^ŪÕîAÚýÜFZ8ĪîæŲBîį˜ËŨ~ķ…ļ­ęāFLN˜›…Îó6zļßû†ägām}+sJ-Á',ŧ…ˆ0·ýÜĮ°xFTĐĮ·Ö]ZøóÜ ‡6)ČĖōŲ8ŊŅŠCë~ß'(ĮœnûyævËŊ[ˆ# ĶŅüōg'@3NsÔaŅ4F\*Uo@˃WƒâwŽķ1ÖU–ūb†ņwQûGņâ­q,ÉĖ>ŸOUĐÛ8u5îžĩ!™[Đš:ˇg%˜ņ\ŧ ÍaFJČo8–ï#)5ũĩūzĐët—e>ęŠōIĐiÖĨRŅ€óÅü·ĘJIŨúļđÔnŨĨßF3›/ėÝSŲ[0ÆZ’fjfũiŽ0böá—ŧ“SØÓ§+ 3Ã°ë?DøyŒ;3Í\DVrsH­6ũXŨģŸėąŠkœfnþä b2‡ÄņâŋŋFĩ‡\ĘhGFJ7X'‹ÖýˌâųŊÎj5Ýæ‚Ģ4:9+Ÿës­ošļ9% k›Û4ârôޚðó‘6đb;7iĢE­Ü$ÃĢŧ!ŽuņlŦÛÜZ•gôö`ÕVwxýųęNÜ*cąUU‚^ÃļfæąöÞcÄøVēowÆRɌhUŽÜu(~ų’īũãæ#7ĩƒÛŊzŒ~| ?c–“ÆJg–gâ]q qŋŧÚ?‡ïAgï)á;ցÕ ĖŋĩÛīŦŧ 6Ü:"ûÕų_ëĒņÞ7EŦ*AÓĩ•ŧ$ØojøoÛãŨ°ŧÖĘo·Û7LĀbšŧŒv.æól3‹uC”*Ū•U]įņÜŋi/îNŧóÁXvJáî1Ėn>Y\ޘū•rũĄxs_ŨUy~ŅÝ<ŽÞŨõ!ЧÂW„OŒäaví|úX–ÍØÕna ÃÔ1æ+Ū‘gāŋģðFUŧ6ĀųFÉČn=ũĶäæÏ~ö~͇dõŪJā›ē8Đg?él k2ėļģŦËíÜYéf‚ōôn™YuuËþŋq34s›AōðĩM=ļÏĐZ7 +.ĩ†.cuÅÕRÖķÓ―ØYRÏŌķ@š§,éîĨÞύÃŋĪŽæ‘GËU- Ûû4.ļį~ĖÝÝ&ņÜe=ĮũņûArþ âKКJJ€šįÏî‹/þā!ŧ\Ũ•­h& ûW6lÅõ9ߚũsŧG\ŦՓ7 a? áðžĐKj;ÔS9~VënĢP:GX§Ėl6ŠÕĒûŠxžŧOˆĘ4ÚaC휜Ý1TmôVKÂW^V0_îęžÁs­Ŧēšå$Gĩ‚fŠž”o›™EÄUY Iû|Ūýėášŧ?ŊšŧfJ]Qߍ„ŧŲtúš; Ę&ĖÎ;ïįi lėsûž‡áŋowĩŽë‰mTYáAŸ 4Æ;é[úĪjŠĻŧ€Žĩ”―ŧŒ\ŨĘÎŪŅHą-ī|Ápz#:<ū…'ėðĩŸ9ŧÆž Ly;Ë<Ėėž[é°y· ĀqŒ ŋÁcG#aąBÂpčšČ w>{ŒÅ4ÚģÏz-Đŧ$ĀÃÆ›0'oũޙu­Kā7Ō@P ĀX3üÂÝįņ1˜đšŦōúũA#ŧœæî{gu­čë(Ë Ŋ…Ąŧ1ãĒŅg‹Úmîj}ãU=ģcxHj4)ŪVr‚Ĩ&đ>WW*åī8›ĘĐïNueĐËÜiÖU ŪuåwŌ4’]Ųjũåv ŊînČL•Œþđ>Cd0ŌīŸ;#Į™†ĮB3ģܰÂw&a$x*lŧåánQU„ųĩ&°ĶšŽ.Ē$X0bí*#ÖZt[h†“Ė[]ņÕüÎĢFfĩú—þėjđ/ŲÕ]ëZ™UgeLģî& ^žxņũZ˜ÝĸÜP―+í°ZWÍ(zJŸŠZëŠĐā–ÂCÝŠvsęnÐWð„%NIŸđ?{ūþŧÐÃāk-=ÝbĀää͖U7ÅŧĮä ";1Œūd|jÏ;ļņŲ§é`]Ē*Ĩ2_û™Áļw‚ÖÝëŠ'ģŦĶþā~~[W@˜[„Gå$ĖÍÎÜ―Ū ęÉÄą°Ų>SZWėc‹Xßō…Ö”ãBpįī6ÜÏÆ1;T·ŧgąP4võ;ÕūxņŧÅÉihĐŋģa·t~á8tŦŋ§ížũ“]īßĻ@ŒFÁ˜ĩƒÞ;IšyŽkį6Āà æ“sŽðŦru—°ŧĖĖÝ:kęžOý@r·ŠĒ@Ō—w—JëúČ*ũõđþ™Yž=ãsI €ïcÅ}?]u,^–Uį1֑jíß0‡Ŋ•–nÕ2;jXeu)VĪՉE'aákÂ'!yļĖę+ŪXY™•ĀéÐîūžšY ŒîánŠ}ņâjyþ?™HÖĄ ąúVž˜GÄ3EáRŸd[-ŧ&•æģŪęœĻ- ß;Éφ`\ą€COø6û›Ųv6i ŒžŸ›œ˜ËcŌ]v,Õf€Z]āhāÉlđƒĨ-ï–ŠÎüßqœLđÄZ!ĩ™ŅÄ~ánäģӀˆïq–ŅéyYvFÄÄøyļŽ4c­]$Ht%iR}Ö§šįîœfŋ­ ÝoŠ}ņâjö<;+.uÏŅV8Aýōl•4 žL[kA™4ŦÛ=üxÚö\œ•] œ›7$Ĩ–z˜ŽĄVIÎ7}îý˜9ĪėDį9Ĩ&lz'L "ėý /?Ï ĻÜ|ĖctÏ7_FŌo‰š{TÎFĒiVĸaęŲMã€@Ŋļ$ä™Ų[õė[h5<žŦČÉüEuuŨ$v)Ią|ĘilrĩÕ}ņ§ģ <<ï–a“:˜U6ŧũ―ww9ÝČŠ4c?ÛPÞuÍ#ĩĸŋUíá<ŊÏÜmõ›Ũuöæaįð•Ó[ãūŦZ˜fģ!MA4>9 5č[t0õ9þ<;'Ũ―šūŋHŦƒéŧü“ŧÚ/Þrō0†;Ÿ/Ø7Sf… ķï'ĖiV_[ę|=føhŒfnSŽ{&åĶ;öß+Ï+;…7ũÏiÆß[Ÿ*Ĩ6·å! 8ũ0ž„­6,Đöģ°õĘÎnyŽq—eĨûrûöõš‡ŅįÞ+âtßn_ÆoŽ9Ý*KsŽãnÚWĐÞiqß7A7úXllП4#ģģģčŒXUÝ舸zØJŨuĩzwZĸSgū- /^üŅļ™āæŨgÕw_ŠpïJģ03šw0v7j]+šŌ38s"$*ÓÝü;üŠÄōĻ]Õ% âĘ'Ýüš.:[-čŽ/tDŽ=ô”UnFgU/6î]'ĮÚ;IđQÝģĩ3]twũ}?đ'ũÉ4ōŠĩwĒ!Į[AÁĖ%䓄œ”ŠēÃ"|ĩþÎå{ß "Ėv> AŠ/_ë,ũũΙ•”ĶĒ‚ĒŠ VReĸÕõÁ‹ïąāī3ÏæaIŸŨÃĄQUĢ―ÏÎ<ÉÜq­c ĀYkö›?Ïcfž{WĨ( ĪúŒŪþ-Ũ"[ÕÕŠ óŪúy~Ė-<š:ŦŒôˆÎę.’3ë:áåNÛŲ‰ƒÉ"æĩ71f2ėŪ)KŨ!V=âūfômS]°ótDAq]ĪĩP]æVݓ*é'_Ķ&û U™į1ÃHM(Ēiï{šŌ§… "éæžqÛIZkEž…7/^üÁĐvjąhY;3#"ܟ{ķ"Ķ·ŦÍÞ%[j&4‹û9Õ tˆ™eæËĀIŦîîZ'‘ ũÎÓŧEÄuMŠö'>æþœ&ņė§ščŅŌól’mPũqÐŌÜK=1Œî~ĸüĀܟĢÞí4ZŽĩŸí Ŋå˧{m­Õ]•ifá^ŧšEĀÜrïe~đÆķĨÏįrģ†ŒF㟠Q|ņâÍ@ø ĸĢĸiŋÍÝĖ[Š,Iįė+ 8Ö*>2įWxā6ï$‰ÂúÄŪDŌøĐHs7‚YmĪ_ŦŧēzBuI·|vnÔWXŨBiwŌmĘÆEJžŪĸķ―ôši7ÏŊ•ŦđĄuïž"üšNqÏĶ9ČûþmE8mWŌ@xwkfÔ)Œtģ–,Üh uĶâAė’TgāÍ >â2ĒÐų<æ9i4sģŽŠþ“;„/^‚ę:åļŸĐrqwÏý˜q] hĖĀl NZKė7ōŠuúJāásZÅÃÃÕ•IûĪ+š;aßVp*3Í€›I°ÃM”ō<ÛLÁ= ŧŨ:i/m2;vƒŽ}.·Ü‡Äĩ+Í įóėR…GŲE#@Ügýęfv˜ķIdåœ\-wĩ* ßOA?ųÓ­$˜YVC\ß›6bœlðpxVÍzãŋŅ-ķëpŲɘôU‚uøÔ<Īî ?d'ô đwxŒ‰áyž+üT@þvŒK0\îþÜÏ4{ĪßÝG†AĻŠN€Ë΂ŲđuÂ8e6=•3–™SHģĢ +ũÐ1&c;wîÚĪ…Į!čé)°îæw ISý0õ9ŋ­ qNæüōHLĄŊŧ ŽV·‡đ{>·š;Īã XšG鞀]grũITũ]īkUWg“ßÁį‚ģr͜Gz·ĖxöģšĘ5SŦj>™XU,BíŠŌuJÝ+ŠčjÃ#ë{―xņk5:ĢyxļwõaÆčî1ïŸÔðΝ>=ŒYĒNé!ũÞæ~]—9!‘8 FŸĩ@ŦŨšbŪm4wŸž‚áŊ:ŽF0üü"Īŧu5XAģÜ ÁhÏÏ­CÖï*ýJĩúĢęޕ­Z+vn5Ķq}Īŧą|8ÍcÝMPĻ1ķˆAâyžžvô~n‚Ó·6’?’ŒĄïÓiöH‰Y. Ũi)ŸȚÆ:œ{˜}ÂÔ_žxņĮäņ°ŠFīv%ÝÃl^€îņ ÐøüÜ0ÉTe‘tš~UcËŨ!jH·ã”ÅlNģ:ÜaP7hj™™€ŠžũŲ9AāŠÎÞ-Ūp ”ėÛ} šÅëė‘m]ŨÐëų<øLkÃ2'lJSč[SSōwIÂęn\ŨGnđđŒųėEÞÏmnq­}? NcTĶTá—Ï~ƅnY Č܁ßŧ„w)O*‚9ŸCĘŠņâŋŋEĩƒY9ķ Á‰a u_ŸÏaF ECUĨ:<ŒxžÍÆõïĘ*4xūČ?ũ@[ĄÖ,s[åUÉóæŠ–:ÜÂWž*3_ÃģZ)[šNÆ%ĖBâÐýxĀĖÍėđošá Ŧ Ö-šÆ:1SįÞiÆËýįþ™PZAi§˜·v ĀĐųĐŽŲHČ―i[•ÕPLŊåY _Ũ5õšę‚(Ũt\Š<~Ülčs}W#°íîęsGgýöxÄkĖ}ņâ/Š―ŪC{Ąk]ꞾŊuˆĢŧÍ=xU5Áë0ã7ûũ$Mhxîŧ%w7 ĨC1ŪŌ}ߜВV݀Á,kķ{?hARSasčOÁÃwoĨ| oðKÄWŒŧ ÜpNÛ&.Öhãžh…Ėt;qã žŪØũ†ŅÝaŌŪŲ“Ž.ËŪûđš‘FĐŧĘˉ'ŧ§ģ@€Č*I“i_žxójIšqŽcTÃŊÃY%730ïĖęåîf]ßĀ„ĖzžM#gą+]ë2āĻāĪRvũpkĩĶuÖŋŒtŪXÝínæÞ­―7ÍNhKuŦē-Ē$Ģ…y˜ĩäĮÍ&p|ßëęŪŪ&ļŪuîAöߕ'ņh>”™uiül4dnĀ,lÅĘn`ūÚĢ[4LBWŧq}VWöāė ~ĪŽ°ˆĻLmķ•vöË­:ęæÖhsÆuÍļMČý5æūxņ'uĩc*U{›Ųu]đ‹Ī]fĀŪĶņŠÐ‰šž­œbšYMøũr@ÏÞ ŊuAĘŽ5ä\žÂČŽ­n3“°ũvģŊ؀˜ÄØųæÞÕĸþ]3䖍z’+Ėu7F<ËIëúæNgî2āú\G˜Pî~}ĒŦŠŨ2ZW åGBpšŲŨ„Ė~ŨŽRUqöï OŦnc]Œ?ũc4~?'ĢĮŠC óŸį5ŪZUeanþ*―þ$^žS­pŠŌšÕģĒ%ŸŊĀÖõĄČĩ.óoTvŽÕ0―ÞÕ"VwũLÁÔēąĨnđ…YĻ;Ü v+G—j^YûI ÃYęŌŽdčn§MNnŽÉgHIįų1š`wîg0Ü=MĀV™] ‰ēįįķVcïvģuÞM­.GęįyâdØĒu§ð́ŅBĻvR§šŽjü k}E`ę^áũ؜ŽuáLúCâ%u7^žxņkŧ§6\FŦB›s­ļ‚áÖ­į~*ÓhÝ5þ+žÔðįūsˆ#Våu]ērŽbÕu֝Œ˜œėŽ+<Ž:ŧÐä'Œyl؜8gu—‡­Ë+ ŧGø}?4šSÕŌwML#šiæY1Õ|ö°vî ņls3ú„+šÎžiž”ē―“"Ā―ģZN^ŨŪ,w'íóđ&Ąf” YŲ]ĀåėŲJCŠ*33ÚáØWTûâşmaø›øŨyæÜ‚ XãS˜8+㕚›ĘŠŨįRi˜‘DfuM#™ü–īFwÎčjGHænfûyšÓŒ&~áúŽĐ ‡æâ"9ƇÜiáîĄ5ķīÕÝ;S‡ĩŧkķ þwß0ŪÏE€ˆËWv“ï…qÁŲŦ†™ˆp[+žįYUvÖŽ-]Ÿ%a‚~gU]Ũá]%ČĖĮ8ĮóĀęóg‚kS‹Câŋ‘jÉs€^ŧÔá1õ-ŧw0c[ŠĘC=ÞÕhŊņ°N$öäȘÉÝF;.I-āëŽ5PĨĐóX ëŨ0–5+‹enõ› â~žŪrzîœīšˆï˜ŌïúĒ% •đ››Ŧ{HąŪ&ĖÍ8tĸü|évfUđđ/ĸöĒà }ϟÕ=ņ1ĮyœÃŲ–}þģëÖy㉗LJŨõųßÏOf™ŧęŊjw7ZfYŨlgif.•úu‹―xņ'uĩęĘ,UAösßđE·Yŋ ídVNX@Äęt._īx~6É8aݚfōęúmüæsFQ Ü;óŲÃĄ““―"4‰­õų ĨÆĩœ~v˜=@;yäOnņÖ~ĪžÞ‡ŧēŨZFë]ü†“YfŅÆƒ›’„1Ÿšļ@ŲEŅ’8éųu[ļW5wûږÜ}ĘĖN9cøųqļšé4·!b3‚čjĻVĩ[ŊŽöŋ?žŦ]s°h]ŅYuZÜHĄ…$:)#­ŦĖÍhJRËAW5ĨpŦ.ĩ͉ķ* ]ŸKBNļyu ēßâņĘvs7’“übnīĸ •™CĸÁ–›ûÉ“‘"šmácfÛÏcüúŲro ÏĪđá$íSMgöŪ $ŧĮëkUšŨĩĶëFjskMzLÅr'ŧ+čU9―„UĨ~ŠÚ5ĸJá§QĒĒĮ!ë/^ü―†0ĸßu}eįLsB֖™­ļNWXMö}ß> ÜÔū·Î‘ŅÞ% -zVÃāæĪÝ?ŽÃ@֞ÅođŽûdfKMgxÜÏCr…ëˆU‰aØo0âôƒđÓ"l$―Ð'ĀŪĶ(SíÝÂoœŅÅÉūyÜâyŒiužę–ŧûqMYúðæéŧ] &Ŋk>i* :ë‹{ƒŒkĐ{š{§L§ĸÜY$EØ™_̈ÛiŸ$^žxņÅÎFĩŲ õū·ÛŲÉķū-īŽ„ÛoŽA™ÃÃG K깖úw5Ä}rqXēŧh‡Á…ŸŸĮáW\$aZÓö˜ÉƒģpļiXSĩ+MĒę<0F2@F‹8<›―"TęjĻ'|D“PĢnĖí7qŅhzî‡ éĐ<Ÿ‚tÄđ#ÁÃÏŽ]4~âÂaßŅՎēF’úU ƒœë?ŸŦ57T„įÎęķ8 •…/^ü―]mÍ:ÕhîYIŌZ~v9ņTÏ}WwÄYŋîr#ðˆkå‘ô‡{˜ßũ}X2~‹aŧģz˜ÝũoŋÉį~ĶĶSŲéËAĢŧĨžQšŋŪõü<“(6/ãbĻŠį~ŠŌ͟―ģrqÖŽČÝ]ßHÃ.@v&eI]šÃ―Ė’äæÓž;1į]§ŊW s— ƒĻŪIÕÉ„Ïą·DbÄ 80Žk_žxój7ïîŠėVĖm?ÛhŨŋkĘlĖ`WŅčæFÛõeÉ(Đē=Ü=Î`NwĮwRĶ1ZzÅ7†Qlþ‡xžM0"ÜĶĩĄG›ÏCģ‰7“4-Ïóāܚā™m Ą?YxóâÅŧ@ *Ŧŧ'LäaMũâHBW,ð›6_Øgķ­ž]ægi :}ET–ûó „V›Ųĩ.ĐösŊŨš2SøžõŸƒĻģm8!„kR@Ė9’ŠÏĖļš{ė[€ōŲÂÉrDÏNŲÝïĢXXąĖžNëi*K]S –Įäö-=û8ãjZÐaq_^]YI˜ĸî”ö[xã=•šScžU0˜s†Ųãŧ aþzR"…/^üÉcąFU—Į"9.Sâ=͉Į˜ûðĀhðĸŪÏЍ™ŸĘœîágę4šŅ~3b$œ„-›Ús€‡Íãyî NĪyf›YļWæģ7 ÉHĩ>Ũó˜™ĮøėGßjÅΔGŽ3ûŒĻPUé0ųĄO~[؍rl6ÎîffÕåĪÝÏOũ4<ķ$ŋåf§6íšVĢ'rl–*SrSU§üNŊÔĶ™ųÏýT.ŽČgwŊþāŋŋYx#ýĘķv ũŠāæną+1Ü@î*ssęž~þß'Ÿ§fԉÁŽðe^įÐLÓ]X™įĩ™ŨÎÃA4[óC7ŠĩŸĮœëút dx„yeJ 8i°œ °kđšT2Ão‰ŊÜb"ÉÕr 7/Uw"Îę6ûžVAēð‰V ™Ŧûy6eĸ>Ũۜd2ž þ֛w%`+†ÚyčÔ%õ˜Ä~ëÔÖĩšúŲũô·wë,Ŋˆå.áŋîX,ÜĖL-NŧēeþėÛČáŊS[+Ļa8ˆīó-^ &ôk6ĒŲõÜ?S<ž•ÝúÆxKŲŲ‡iž{Ó=ÖaÃNw?­đω–Ŋxr Ķ*üĐ*ŌšĨLÕY_ˆšØ­3ŒwuÆRwfíÜ;â~“_͇ąE7Yóc|7'†õ›Ņefs§™ų(Š;Ĩ†đį―Ūë" †X1ël: fĨđ·4ÛáiĖÍΐ_žxņįÄ^ģýfĻ3óŠeāūo î6Ū-wŧ"ÔÕՒ–ÏüĻ–Ö‚Æff―BĖAY7NLAĩŠåg\Í]„(xö#i/ĀlvĪŊAr? ƒãkLÃõųôĪÏJ#[t˜-uÃpX?ũŪė°čÆãŋ˜ĘūėCފÏuömáËĢŋݑpe"ÚģũÎ'ŪEcî-jliÏŽŒŊ4Ņ6uŠvĶvũ| @wîŊüāŋ?š@ HÂÃũģŅ=äÛúÆ`·P•áæîÏ.ĻA€~RÃĩ"‚þüïöÓ](aߏˆļbxœīÃāŠ.7#í>Üęá$~~n7[§ĐAxöģÕĒ܃āóÜŪøtŦŧšĩŪĻęĘķ03ŦĸÐÆIxIÍüþų!qüĩNJĀWåjķþ}Vu„CP&ŒhŅīÎgVŦ•á&ˆ]{ÚnÖđ|CôUm ‡WÍó[Ÿ'ÃĖÜ3 jw‹5jâŽĖ^ē}ņâ/R-œáąï}Ēŧ°Qã›ŧ™ŧŦ}M5wëéđ7É+ėP'Hڔ{Ãčd—`:AY—Ô’úÉöVëóïSUĨ&ÍWHĖÜãZûy<Mķž|h zwWĶđ_'ķÔf<ņ€ÓXÎŲTŽĩNŒáîtŦœðoÚ7ĨLfvdž·yPVÝÝEø ŊŠî27ˆģ~Å $ÛŧÜ(ŅÍšÎZƒõäïķwgæšÖ”øŪp3Õ î^'ōË@§Đßž™?‰oáŲ™ŋŒæFXũũHïûĐ뉭ĘãûšÖi)wzļk W|;fí @8dmRgåðlv‡ûšÖh°fáÐÕYûŨÍÕĀdøÏ~`FgŨŲ"ĻęýĪJæ–UįmbÃÔú\—‘đũ8ß:ã óčjuã0mUúZŅÐXlãø)š hŠ­RÕu­ĩb?šûōûŲRŸĶ uÖ0iUÅrÓŊ"Ÿė†ŧw– 7?#vŠ‹xņâŊá­q$Æ f=mƒFfhĩŧ‡{·D á pÃ2ܜBVžĒÏÚӟį™!ÎĻÜ)čätî>!°Ũ~žyŠã­šŊų.Ą;† 6iŨ(óėjŊhĐv‚tĸ.v#ŦĪ™° Iš™ÛWÛ}g aš;ŠĘlâÄ]ÝhЭŠ[ãÐå„$Ė aeĐÛÃ=þeVĮĢsÂŋ§Ø&s{ļIĢ=ðĸ`]Ւŧ /^žøƒšÚï—h›˜+ˆ#Š2cDĖL4Ķh7?„Xø §ĐáØ ÔiÜ{WĶō˜I :ėšV>[-7_ŨW‰UÝ ŧęüÜÆÖÝaÖŠęŨÚy.ˆøĘķ(s'yŪéˆ"wrrrŸ=ë‹óx›OÍ䔛[WeAÅ·a`Ŧ‰1ŸėęXņÍ*ŸĐ\u8ú”ŒíúFš'3ŨėðŊ/y`á|wĩ/^üMŠõSHþÔCōšÂĖ4ĪuvŊc*u(uÉ{?EsuïgWi­―aŸOíꖅnOĘLJ}‹Ęݧ;ĮÃDŒyĖÍf(ž )íl~…ÏuŅ ð(Æ>Ģ9#ĀtĪį—ĩ ģrÍÜIŅ`­†qš~Oz™ÜiÎŽĸāŋĖĐ Ļ•:ÂŧΚä71r$ëœŨí4\Ũzž;ŸrZŽØũCƒŧ›Ųl$ÜŨÞûþyÜėZk?[§Â2,fïņâŋ?§ŦõåųlUĄÓÅ ęóđ4éXčiūÉ.;l˜U’"͘Pf ģĘŪ'ÃčĮÁ%6ÏõĐ9…ĸĶš”ÁîܒŒĸĄvö.ˆnžđđGKŊðg5&ļ–ŦLĐABØŋi[Šge|˜ýŠg?ŠŽðk­į~ZõųwuéŽ_eĪpÉųđ.H4cīž.^sA•Ų•ĸþ­ņëzd{ ?$;ģ0§ÐfæaBÓ>yę ĨQßû1PxņâÅÔÕfMī j cå5§7·ÛŊEōūo@ą.7ë.fYj‘{ŨN5N Á>‡iŦŦ[z2=ŒdïVcR†>Å·ËcöķŨ·9Ķ`Œĩ€ģ“Å—ŋršÝéäō‹DUÏZāŦ78ŋÏÏÓjwšū|]ŨÞGۘļņÚeÎ0ßĮ;a ŸLĻ-œag;u]‡É@„ÓŒDØōˆÝÛėĀĐdžsbn>Ũ•đŧ{VÏ1’‰xņâÅ_{U빖…ß?·‡­ļŠ%õ!”Ói˜mîlė<Vt>?N A){—Āx IļŽĖ JëZßpØg}2ģösęÁŪûyLpssÞÏã$&üЁ0ƒššžũÏ6ÂĮ› ékĢŧÐĢzšqÍÎt,j&БÐ ­ęST“Öæ#‡˜/ýQ]Dđ;ũ“0]ëڕP XŸUđ NÝÎÏĸ~Ėé4Uß{Ožƒ(š-§ŧwOđŧ}ŋ LČ$žxņŨðÞDøø@ļGf‰R)â·K2·ûū)ļGS}g“—‡ĪÞíĸ}Ūįyh8åļŨ›lü-I%YúöåîĮŠÖ‚yø}&JĻEÜOCúũųītōeÜ˟įn‰ÓtÐ[„ÁFK0õ3UU]+ÂΈzÞáyĸü⹖›U—‰fÞýÍŲr_SŌNKđđ­åíûėåWfŦeáÜ?”…–ŧĶrƌ)BYīsÁyįIh<= đbýO'fÄ‹/þ–Ø ęÎū>…y]ŨzN6íŽēÜĖhn–{ÓđĖ Áœ<ƒ\f1AYF§šs?8Įn­VÉĖč–OŠ{žaWĢc- {ïaęĢd 3NŨΈ•ûĄ™A0t6i6ģð„ęæÞ4ĒYW’v] Āýl7ó‰Ŋ†Î_JŌÜÔP—9 vaį6§[L'…7>ũ#€ā„Õ–ęZ!Mr/[wíÚmĶŊ#PCĩÂcŨŒÕ>Åė| o^žø› „S)[ëģT=å0Ŧn1‚'gxüõY9ÍPęėĖ-kwë›ļødw›y+ 4wƒ šUÖ!č0ũîúZ‹3už’…V 2ũ.ĩTĪMIÍÜÜ$tŨÔęø3Š1ÂĖîŸͰ čRV-ŧPmÄõųäiʅxEŒb­6šyâ/^ü=ŠĨa­ÕÕ:Œøų\•{{˜„ŠūÂû†,|ÆÆÝ`8ŦÎÐý+)Ĩ R%ÉësAĖL•ÖĐ Ŋj4ž…ƒ_ŨĮĖšŠ[vũIâ3•å ‚™ÉÜ$U%ĮÍŒ Į$ĶŌdÎîŸý―ØxNÛūK†Ė<Óqd6ÍÆÁqÔl<ŒÝÏó„ĸ‡ođŅi}~•e6Tl;ŦŠOūA?{Ó}]'”6Į›ËgïÉP°ģÜ-ÜßíÁ‹6Dq‘jõ$ŠäއvCp•Mē A0 7lU6ë 7ŦL“ß:Qaæg°…Ķw+ŸÉr wo5Y§2+óš.ƃlēoÆ50Ŋ$ŧz,j1Ūģá_óŠl!Ö:.ƒē0A“ģÕÕ§E‚“?kæ­6CW‘ø.Ihŋķ.3Zw ]ĨĐu4ŪųŧL[wÍúķ-xXw“ŒËf„ðPkzƒ@æ‘ëâŋPė5CŲčüûĀÍ|ĪNš5ŊAíL3Æōę”zŠ_ŠóŨŦýMĻņYŒJ0Øþ―ÁŠ_SQUuØ0;Ÿ}K ™ĶvwŽĀq@xuƒĒqlÆcΜ%ōtĄũĄČņ,ėÓø›™æ‡­Z€ûôHĘÍĨ>ãŠĶ(5"|ÆŌÎ>+Ũ2§Gô‘ëΚÁ$īÃĒŦI›q{ô>ŌãnĢ_Ũ‚Ô-‚xņâÅ_\ +"ŽæīéæËI3·u…ũģ[}’›Ūš"@VuįĪ šë9Đę4·ÏéSĻúė"ŅT7ŠjÚÆūĢīņīČĻŧâôƒweu˜é Ņ*ŧîÝUՇ+ÃOąiöČ{§đuWí=[;Ė~ŪĘt§Ý#Ŧ=–Ũ°ģ{ÎÔܘMÁ+–đ?ÏÓUĪŧ{VÆåŸĢ‘Č]]"mŨSU*ØŋžfT· 4íĖ*‘Œx[^žø“T[YFÍ&“EęĸWV„ˆëė.Ė\PŸĘÅk­ĘĘgCÖ­CĘpũ§ģŧ#ĖÂrge­Výšģ[rûŪ_Mæą-€ĒŧŸ°Z­å’έĸkυPÝl|ûÏŦqÞîĸnzr]hîvf·ŪXE˜@óčJ;R‡ģŦM#Ŋk ūl5pޙœ‰ØĮ…|–đaņdæ“îËģ ™…ŸüI#h$Ķ\rųęîŪ–ú-ū!Š/^üÍ]íŠĐų‚qŠmZm4YIĮTÕBËÃOûwĩĮ—Ũhá2ŒðĀ$ŧK-Â*ŦUū‚UĢeæføß}wj-ŊcĻ2Î24+ÛČá/ÂNũíDsÉČĖáį&ÏóīÄSvðÝáBnĐëģt5ĸþß§Q­ķ°Ã˜u.ž@pÉ@cĐ+Ó wëR7šiaŧ ÝÃÃAXxĐũģ;ۂô F+Ė3Äi$K5HjŋÜÂ―Šv&ņâŋŋ§ŦÍŽRŧĀÎ63ZĖð(æUýۛ0­yxÖšĘ(ģ ųsß0Ԍę|ÐZWÖV+<Ėyßœ$ŠŌÍÜ|WÍ3ĶšŧĖā}?f6eˆų€ëšJ‰FKŸSon·|vž]‡ƒßÚð+>К­…{œ5ą@™;yØÐ=žý e+ î”ŅhÜORˆÏ„€0ę=OÎÓEã„=VæčŒgķÏ­JĩŠ{,s0ĒlĘÞU‹/þâŪöpŒZ/ááUãļühĪēû ˜ß(Ïo&ÜbėŋĪĐYŌčæ$ĶąũZ ĀÏS<ē­ÜICxúZđÂ+ŦģčĨîo–îšö\3š}ël-žĨšS†ÓûPÝínöU\ĖŨÆĩą<šuüAŽ6-Ō8ë6ķīgÃčZEÂūG‚đÂs—ÔnîfÓofNžxņâ/îjwũð―“ß,Ä~îŧfĻŽÎLĀÆžëfq-š ˜ör YiŋõYģēĩwR<í ·žĄŌõ•_%ЁĢCØŲ;"p" ŽĩTȝÖYóúÐ}›ŲZá•ŲęC‘]5aŊŠąœ™ýĶ›H˝đ‹VI2ĸ6ĪÕáÜ1VŒąx #Æ"qrņųũĄH@ Í;v‚ąï=ƍŪÎ| oþ$^žÆ\&ënšKøM°ķŪ& ŠŊoDæžrƝ{ï=™2'Ŧ%#üšŪÜģĻDĢó\āËO‚ÍÎ3ņĐÕį@vnqØ|ķ‡ąĪģų­4[ūô.þŠîįg5ÜÕíqxķēĄ*í/EZŦēĘÝÍSŅ\ąÎÔ<‹ˆ˜BßąrMŲY†teĩp}Ū>Ï)ˆøš{§ü>q6§rĒš[Â){Rĩ>ßäšF“&Ļûõ/žxņWwĩĮŋÐkÅŪ4f­Y-ģĘܛŽnðėnk­|žŪ›uŨNŌh–•jDĻ’čîįE|õtšķ ëšū,F˜Ûģ· œVĮ| ŅOéŽđ‘s•ëŠģ:% Ėé?Ü&Ūŧįė΍ųl7?†·A7·ŪŌۈĮÎK3‡”Õ |Œ.i]Ë=ęÉĶHsgŦ„ūŪkĪĮß uŦvZXœ=ĖŽJ†ĩ ôpDuÄËĩ/^üÍ]­đÍ1‘ ÆßiŪtˆãęĘŪr‹‘jÏ~ģŦ['ąĀATæ Qð}?•eá$Nˆ•<\ÐŪ$'ŊK­ˆk=Ïí'-ÐóÜđÓÍrŨh}cļÕüxbšA‚šŸ‡„™sd…IjĨ;c…ZĀUfžUaá§­WßÜŧR€‘]CþČŠŧUGsæ4āl„kŦø‰Yxž=#vþĘĐwŋnīMģn › Šģ‡°åo‹ã_ŋŨ˜ŅÝĖÎĘ"`aĶ“ŅÏ(:_ĸÝ­vuNiĢÝũî†ÓĶQ†Ģ@n5—4{ŅqũJ߈™―óŧÉ­Þû9ŽUgĻĖüđŸę6О8ģáĻēĀžŠ‡Ęt;ŋ˜­ŌōËĖŧŨZĨ&8Åbģ.ų]Oú—Ïšxđ[[Ý}­Ô:—û)Ï|–‡ŧĨšôð%SŨïAšK}ĒÓ;K /__?ŪđŠUjȌ]ÝÝxņâÅÜÕŠ :V•Ļ$tՔā+m˜ĨJjAūNP)õiŌ} 9›VíāœPnyøˆVŨZ>+ ˜5‰2·@‡•ÔĄŽ1l Ė'UĒ4ƒ$V,ˆ#m%••B[˜Ed% ŦZUŧvđ9Īîvó+ģqd°ĩĀ;éĀįóOÔ―·â0Ū}'z‘ ™ˆÚ5ëėŽ*HŸ8<+År€™'%íˆÖį"―ģ žxņâ/.5aŒ‘ÄԜfyoši'1–43ŦęC‚ôģũœeîČđ–GÄđsKmąŠZ%=lgjĒ]Ėj—áô#)KŋâDÜæ&œœVÄĖ=ķZglîúÖ=0ÜTv,·ónŽ^+ū―‘ļâä)fĐcųÞYđ!:ŋÂ/3ƒ,ģH°GÕŪe>AæĮŋ0*7 pšó·į§ģe0sVeuÓÐđĖLdUņԛĢZę†Ö áŋj[æ•ÛHs7Z>[$Ōö0‹{Vĩ:Üc}^öaĨŅ dĒt”đGÞūüRölWÄÏóĢH€ÝoŪXvuĄ§[ĄÔffĘdnģ+hČhRW·§ėxÎŲW<§á‘f-íýĻqŠ!g Ģ Č9Å:j–t– ĻJĻÜÜu5ä~ åëöĻ6ũ>lé@KŠÍTz*AššbŸŒ™ŅÝ*ģ%ķ<,ßdŊ/þĶÁčę†[wƒČ―1b‚‘ĻŨuMNöŠÓ+þ<ĖĖhûēiTũVŸ~Ú[+|Ķc˜§ ‡ėHIOÞúF.˜[—ĪáVIī0wïŪÎ1˜MGŊ€S­xŸė˜°*“ä:ĒÚę9kâ;<ÎX―ý9ÅéÝ#°]UĒŧĮ1ķIíá +‹”9ûT“ŲyîΟmnŋaŅ0ę+.sŦY>^-đq cZáþ― IÓŠŨRw7ĖÍÏN Ã_ ũĐØ0Đ4]mnžOvOkÃŧŨŨģp c,ÐÕfâ~žÃ›”PÝónšû\ė՝•ŨšxęÍ!ęgïĸýL6n—ēŌ-iœˆņ­Ö+özņâŊîjĨ&}…šÕu sÆUPĢŧWP­iÉÕY ŊļĖėėgĩŽ‘á°!—51û^Ô$ߚ}‰ˆcĸ†DóĻCĀ4ō—%iöÜG 5īLXáŨéÛrw‚đS­0‡0ŽPũýÁÝ'ÆÛ"ÖĩF/qœgá°ižÖzž§ŠæâįŲÕu2gQUCĐ$r—ÁŪë'îĪ…M·Í|Üũm`ŽØųdĶ õg}‡w7áŋ2ÆÜՂųŨ‹Ĩn› ðŽQqIš)ÕÜÕ*ĩ vîĄM§#t­Ŧēϕ6~\Đá<ī{y\Ũgz=Ė#ĶķÖÜIåÞãâ=ßĮoN@ø!5ã\œ™nnæ™Ų•ņ-ķŲ°ģųíŠ.Ũšj'9ĩÜÏŒäđ{›™ĩÆbð<Ï·ĮŽ}"Aât=€ÜYæü|Ū}–đ'ÉėšÜk-ĢAĻ.M^Z~u~čæ‚2‹xņâÅÜ՚)FššKāšbō dXą*ŋĨaFî_ŸX·2ŸðM8‡Q'_æÏmČ&ûjfØQ•. Ï*Ōšę8ļ‚tJÚ5V.’6ķ4Ø?7Œˎ$‹’ExXeAˆļō°öČ` SxpïãĐýt+w ZŸyï›âįúwŌ ÛxŽī; ;ïĒÞMp}.'ë‹ dwUšûŠļïį;ãïįC•Ų]ĸŪ df}bļũ&4žæĘņâŋŋ8Õæïaĩž―sŨŠ ÐÝRŧEeu7Zã8č.wĮÛ RuõýŽģÐMМhUIU’Dį“gîė<Ýâ!â9ōr8ĖxB` ļĶŊĖÃÍö~t~čîʞ‚ę’Äģ2ķVWúéݝôœu… Š={îŪL6Ėm?š ļïMŌÃÜ=ï Bŋßú!5Ð-íĸTbĀó<ģ_ĶqŽ ŸÏ§)ņÔŨQææ*™ŅHžxņâ/Nĩ€ŧ9=ŧÍéūFcn ēKÝߔÅįv„Å"”ĩĮ]ÔÜCЉalōį~.‹Xad•î‡ėƌG Ę}}–Áî}îļ|:Æų"+!ļMŨ!u‚fcĨ ĸD››ydÖHŧ–[ï:ėHŽ9Kfá<”Ÿ]ŸëģŧÄi”ˆ3ŠŽyėŋg6Ҍ2t—‘Y}ZŧNiØ ‰Ãû­zÎĄˆn哿t·įy؀ĄÎ"dnnûâŋŋEĩӘ[Ý9ņ+ūōĖ€f€›ŠÐú.Fw‚NcC“&ájŽã—įXQãoîp§“fųäũŦAðļ`ýÞaqēęķ#ÉōÓÉčîĄo*˜O™3mgŠĩԕ9udQݝ 0ŪĻ―!†=2ũū@•@]ëÚU}Ŧ‚ģH[A–9Ãvå!ņY ėÜ%ŦÕ>Ņ‹ ‡}y_ǃËX=Žínđw˜‹ĪS]8ŲRžÕö‹Ŋ1·[;SÝqrFÂegŨŲîáčĶD†ÎõĻĪ‚óĻ*ŽiÏĨG,I'ËU~x·šôÍĒM5ÔĒ/#Íe%ŋé…5Þ°XvžMacwCZW8Ō8ŧ U>fuf7ĉ”ĨĖH·;ģŅ+” |]p ›čây‘9 3Ÿ Ÿļ<օų-wĢ)aæ#üęCŌ{ŨÎ4Ą9ÓöËՀؐ™ĢĢŋxņâ/Š―š xD—ĶEņ|ų―UMš3“SKt6IwĒ5ūŊ8SįsĖŊ$ũģÃŠĄŧŠö˜zƖâ„ķxX=IŌŒÄ‘@īȝÕŲ§hV…đKП{gÖđx< ĢņēÚuŌ^bn:)6gąŦoĩmmīhžũóä3Á:p#L đ·‰k­éģ‘ús}`įÖ@˜WUvŅā4Ūt3š{ZÔš…sksËs1ĖxĖÄđ7Ē;3Iđ9^žxņĐvv5)zP‚Ņ|-wÏgũĄ–`T‰°u­*ßBïį~†õƔÅĢ 0;d ^ŸgĨJđĄ2ũÞM]ŧĶūŽ„ėåŨR”3"Ö>™în‡—ĮõY‘—}žXëūŸR›Åk5IûÍŧ1ØŋÏ?ĢCpšuŦŠA:í~vuOZg™ųɜ}: Ī*u—ŅhķOoåšūŪîZ+&—ķ!w;ņ:}B"cïÝ­X1ßVâŋTWÛÕ#É:œ+U-§­ë{œĨrs_Ū꩒āágóHMŠĘYø2ėÄdïПÔ]U~˜šöu—eīpwCƒ:‰Ö( Øu*ž―óZŨŠÕ“-ËïiûÔ>šųø\uĒvĒ9ï(ÂĖMÕ4~ŦŧIŪáŲĘu2Æsį˜_ëîîļĒu ÐĀ“&ŽŊu-üûb­ļ– ‡ũ|:z-ŽO~„é^Ęc‰Ÿû§šÜÍĖ æįO€ðŲBŒCŨĖæ/üęŌ†ŋÅš™ĩ3'"ŋÛÞßī4 F{™öŊâÅ[NÞݧ\öÞO§ϟûĪđ‡ũ.Ģ]ëÓSûâīðįy$­I{OŪ• zöcīWUĐaán–;Ey„ŊmÖŠLŋ"ŧp`nĩsȋ˜5ą‘TʂųréÛ)'‚k‚Įh<ģmŪuMVÃhiŦššĮQÖ­)Äúy3žéļ+'ĒŨūxLŽ0jãęėY,ĘLHÐfFð9%k] 7ė7-!<ŪjœJŧûËķ/^Šõ°Ïŋ•™>ŨT—fWŦŠ.đđ9ŅßÁöč·ķŅd ÛósŸ)Ø(vŨ 'tAh šÐ,soįŲ#ŒdUëԆgAĢRþ–Ë^ký<a"c uO`‚zlæõ] ĩ{­*éf;kZ$hč NΚpzw}W Z* ߔۊŽp?~637gåŒÄXŸÏðlĩÜŽ*&Č|ž‘dL.šŅbFéÆäüēÉņŋwāYę,#Ï ûÝ’DüNš]OæĪžTVC$–GU°s]vBļÖÕBeÓx^ë7É0šÚŒķHēŦ Ä=?§īqĶ―ËcÖĪbŦÅĖšŪeDķ@žĮcŦÝmđ čūæäŠNĢeWW­ĩ"l?™Y*‘|öīÂÍВÁþ]]Ē[\ąũsrđÖũCå@K“i@@“Mū\R}3v}ï',ÂMÄáYN’YdU7š ÍĘ:áÎO‚ŠēVwGxĐ i\^•Ļ6‚Ɲįđ’ ™ĮGœ§#óŲ€`$ąïÛĖcEĐ8,"0Kw|―v2Úy6YØdKÎÄÝZ}žMfþ ^žxËÉiîüđԾₘÏCõųZ­Ę2€nÓĨ(‰>gbø|.‹P‹ŅR§ [þuũzĖiû›,žU]eîGģĨĶ>ĸVguAÝ$JęŠpĒŋ―žŨuĐZúvÕė“ËåÆXn šiVĘ3T†‡é0ĐĶ }ūËJ ŊFė|Ķ莥Žė.sΈMģˆĩ3iF’…įIļ{˜ŠØBËhĩũ“éæ rÂqÆÂĶĶ@ãĖŨ}vĘÝ:Ŧ §û~’øsxņâ=Óaĸý3wį ~53f–ô%ãįÞiŒģ"ËZĮ]6Ų1;ģ;ėB°'Ē{ žŽēõԔéģbô}ŠtrWUųĐĸÎ]AîL vˆĖÎOû°›@Ð~ƒd\Óý­ĶyW·pļëÉ=û ™öģOķė§ĨĘŋHĄYՒâßĩž‰ÃI oÃH íÎkų}?3ō;qD]FšxÂeĖinÄŅÏb]>óōH#v 4+HŲ†?ˆ/ÞĐķŠ'šķŧvΗôo•VŸ%)E4ų ČĖ'ØÕ$,|ô[ ,LYjL'6ė6ëŠŠF·Ņh˜*AØ<ĸLûIw_ß*§/2Ŧ[8øųß'§ÕĮiæîöĸ—fk]r‘ąu7Éé4kĩŸŦĮ·6ŊŸį1ũsuĄ[Ũ;YUÄßËŊa4ĮŅŋŠjŠđi|ž{įCŌÃĮ‘ānV‹ó…―ûDsō š{–Ļ#ž…9ņ˞k}‹‡zĶðÆÝĮiFÐÝÔUYĀ7GfâoÖQbApŸĻŠJ#!Ø3ÏxÜíájĖS…GŨ!ÜX$Ŧ ›IšęŊyþŪz†w3Ģa\jũyŒž ]%‚āޙģ.čþ „ŧUý$ÝÏmWðTóîóÖėlĢáâŋũXĖ܍tg Ēƚô,;åēÜOšguIÝŨ=JRwëRu…ŧ[Lfë:4Ú:4ŅĨîT7"ēęW}§xÜÝ’;wf‡{˜?;2w:#sŦ@ė­þ|ŪįŲęí˰ šŧĖĘÊPŦÕ݇OūŒ6ÏÜÝí“nž“qhýüüÏ#ÖZuāyâÆÝݝ‡0yï=+‚ąu8dOŦ<œduO8™Û ޝÔÛ?‰/^ CĮēŠŪLĢ­ˆŊtĸ7嚠›WÕÏsWïX1ėEÁÂîįTØšŸinŸaīköŪóŽ~-tS‡ÓÕĸ?Rƒóû…=SjļA>ĸÃܝ(7wΜsïü°fÝ}öZ;_9/aÏÏ5æ^cSĀŧ ØÃnÖ”Tī…}ŠôKó$Ķzjķā$C-›bRŦāLŌ\ĮjŪ}Õė*z€éËÎĄÂĨ]S+ōmä‘åTH&1FæN•ĨI’W&î|9ÅöRŋZ+ް VŌŽ*ĩ5vV`Úš(·%ø9BBCųeģÞX2^ðŨ)  ÄÏoNōčņBՉ“§ījÓ SŧnÜ4ûjÜe *ŋxÉý7Rei6N1‡^†°K°YûHJæ•ø? Øsā ėĢݍ}t[6ÍĂ[ØĻŌm–dÁ‚Â<™M™&#M;2QĄ\ø,+ŧHCØĩXÂGļóÃ-аM,x8VĖå:™MĄ6]Ļû`*V3EęūčaĻ1 m/`WĨbXFyŊVx_X mÔĻŧæÔĒ‚ÆHÅĄÍcÜîœÚŅu‡ Ņ A °qúĖĐ9_}åß+<›ęW1ŽŅ.0Ēč-ŠTĐōČQĢY0ßŪ*jPkDęšĖž‘ūŅß? ß€Ū?͕j,ĶRŦ;ĸb&) B\üyIŅ6ŋHŊČaQ=˜-ĶÔ=1M›jpL…hZZÆ?vAņ–P·é ' cæ Õ "CЖ?Ø3š2;ÕmĮøŨĻãĒÛNQ°ŋ‹Â€ÆØžIš‚Pu†Öƒ‰đ—ęÜīĩÖKh›TŌØB?+ް‹―”ÔRÖĢjm~Âg ÜđyĩĢëýšdY=Ŋ]― ÂÖĻQ3(8„‚ūT­ZíšurrsíūRšOiWų ÏÕŪ[7''ĶÉ/ģåeįÁ7kárđē3ģ?–hqĩŧbĄ–îē:§kDEU}áE\xČÚÖŠS·vtžü\õ'ČŊ^3ŠALãĀ ŌÅyšĶ°+YÞ<6ŽĀTžĘZīNnŒō>ŌŽ,)•°šQĶ}ėšūgqeą„1į€Ī†QģAČÁĩÉpĖÄą1ÖŪ.ĩÍ'i2ŋĶ„\V‘”ēųÝē ĩ·°Öà ‹ÕJšĀôXÔ§yČZŪ;Ÿ}­wN^oUŪ\yé’ÅÓã§aĸô8õq…į+ÆÏ˜ņrýW@“óįÏ~>uę•ˉݚũütĘg€fō͛™@ līsŨnq#G• KOķjÅōeK—hyŽv!4ÂË?7eÚįŅuëXĮŽŸ:ųđįžĸtęīˆČŠ é…óįūŸóÝŧ}ú4iŌ š™šúdĘĪIĮŽý%9OÕÚƒ]ÔZŠiüŦ!RŨkTpRÖ ‚ø Ąîú‰ŪŠ 7hčQŊ 0$īŦŪ Ą\šJ€ā 0ÕâXĶ}Ɍ-š]):?FũáÐn†iQ°\1~4mwI āČĻđ+ްÂû ÄŅjęސˆ›"° âT­Zr·gÏŪgiÏāώO§NmÓķÝęU+(Ņï?`PZÚÓ9ß|=qōԐАE ūXíĨ°ŽbĨJ fVVæðĄï7þã‰į/œ?ųŨ1—Ÿö°!”ØŽĸxÂmÛLš8ÁßÏųÅô™ÉIIĪš5oągũîÓ§\ېā^o―“xņ–͛#ŦFþûïqxҜ›[ÐRþ4%F,ĀžšŠõ ƒ k t Aģégdĩ6yՖïÉc€ÓáύģégũvZŪŒēgæÐĒ-Ū—$ kņKĨ€Nū…ņe`Øl”åļ% ápĒB„Õ"―Hj!ˆ})XVŧ+ްÂ+…đ†ڍiÐԂ‚~þAœČÎÎ;+Ų<·ógÓĶÍnÞžų՗3f|ņųųóįIã–)[ķRŊŧþøãŦéņsį|—žžHEDF–/_Øĩk·°reŠT‰ô(=.S……•‹ŽŪ Î7iÜļAƒ†ÐØ^xņþýû\UÕ^:—-ûíéÓīŽĖĖðððȈˆeŋýöėéSXŠöŠ)Ō 7'kt @$ÕüYcuĻþģâ ãQÁqg eið‘ ŠĀU,ü—Ļ&&d0ýu4Í Ö0Náęü }€ėR”ÆĮŠ <&;9P˜vČ%Đ Xūę%Ķ76ï +ްX-ÉJŠû}IÏJgE GŸOĻ–– þ<_Đ…YúīŽŧõ~Nõ咊.2ģ@•ŽėlŠė-lWĪ―`t`PЕËĸžJ8}ũÎmV1%ą6‚uՙP}kËŕ+–Ÿ:}j͊ÏŌ‡ >fėļ6íÛŋ? ï·{ Ũĩ{6íÚ{ÐáCđN“ŸeÓIŧã(~o, žUĨī~ĀĨ6·ûų:)0r^§i~ĢÍH=PFfÚ;jI™ÛĶ=Øđr!ŋLhŠqqó‰|W…N]:_óģ"VP;Ó$n›gSųÚ_>$F*­ķOEýLŽ°Â /4Q”­!N†‰AČĀP yôļÏ;oŋÝģ{€+ðý=ŧwþøÃņ+V,ÔŋßšÕŦ˟7rÄðÕŦVŽ1üĢqc6Ū_ËyĘĶßŧWĮ.Z8ėčQgؘ"Y$Ē#—ŌUéy#Ę5ĘQũB0W‹™ÎiUNpR‚K5œ”āŊōQnß 0z0é-fō-2›†‚ĩ6ãËM3 – `ą1$#ŒÂcõpôÚ°ÂĘÕ  Ø €€ŧnŲļRþ…@€Ö^'ŽýûÄ q{qįû’õũOšA\ģĐMŒĒO! Ā6o\˜˜–íƒ[uæT…sįČNŦ- ŒL~ýúUrÔ?·’o&ÝļîĢa:mZ·Ößå/ĻW$ôÏÝŧũíŲ­đN;%šgNŸ>sæ4øUHhï/ãš+DŌøÝĻo7ˆĐÚ QÄųĘą;Ojž”u2PU]ÓeŌ#bÍBhóusÆ\ÓĘ 0œ:üŨp4eūÚWXģÖ§lƒų·&EĀvãŒn‚ˆ%ŲĀËDŅ;à +Å3Âņ”ņ3…Ÿše$ūšifÜ^ {`Ŧ04F@N9ŌšUmI 5ĸœqh0›]ĸ†9Ī2D+īĪŲŠī‹ …TüS=čúŋÁĀūTųrI”ÄП ĩą d6]͗“*~aą>›k―ÃÉn•ÚÎ2;ŲÕ|Ŋ‘ŌB―õŌĖv–GW>k(§’xq{Ð,Š–Ä S.bšyāĀô­―|!þ‡īT0—kĖÛYō·ÆĮ 6l< Œ øJð7Þ7vH1!bþ·)īBž5ąKÎŽ-­b+ÜU·ÚĪ{ŪiēGÅ}Ņ—ŲœŪ"é,Nš˜[S/ã&Ū@,ƒ5!Îę ņʰ +ā0e^ā(ĢUMĶåLMš2åį_~ûÁøŅcĮ͝ũstíčÜÜ|NŦ=ļ|<ĘøØĶwįšsēģ€'Ü ģē3!·dWģssÁCR·ÕŦÕØģĸ`Ýz/ó'ƒÅõ€ƒŦŠûŒ)Gõ(`ųЉ įMģ2~5ꓧ\ ˜Í/š“ë!Úųš_†3ę–+ðē3—%"]›6\.íŧ#ļhëqM4;T9ĶuÚ’Ærht·`+ŒæÂNˆ 4Aqý anÔeÚ a wÖÏ,šAáj%a­ūæ\™7–{Ya…ĩ`xĶyEN―ô=33Ģkžƒ‡{pĸþˆļa#ã†/\07™Žbû ]·ÞČqãPÐÛŦUë6ŸLž6|äȗŠŨČuįđ\AíÚuøðã‰SãŋxýõÖ%J•ŒˆˆødęīÚŅŅqĢFýāçééaåÂû?ýŦØN]‚‚CI,€Ølđ‚ƒß1ēclįNą§~öÅ[o―Qõýaq“§~Öū}GPD,SĶėŧïõ™?}Øð(ŲērēðĶ™4eZ•*―Þyũ­wûļ\Q5kĸņ”iņøÔÓPlM@Ë•G›ô@c)NÞ j…Īó ˆCčŦ]u9ŊW]˜4‚M’ðYé [ÂåôŨ<ŒŠÁāķŠÚā;ļl<Øo”—‹ßÕáĮ +Žð>aŪXķú؍3ŽĻz<ę=øÚŦŊÁpį|;{ųâ%ĄÅB x°ČŅ|ˆ‚%XppðgÓĶÖŦWÜã<ØęõŨûô02nØåK‰3ŋ™­nķŽþýĖþfVŌĪč:u˜52ējNvN…į*þ4!6ŒĮŽóãž={wÐ2ų,QĒäŧ―ß-UŠÔƒ‡Ņ, <øÎ;Ų99åÃà ~ŋk§7/_đ4ũįŊūöÚåK—úôéũvïÞÝŧÄVŽP­D‹–ŊÖĻQã·%KžķnûåWģnßū•™™Å ãĮ]·fđZ°•Œ…1ņ’ķiZxĨûrųfũÏto}ĨÐ@q–!ŒP†,U d ÔŒ4•ķšo‘ĒE֚ `”ēéŌĘÁĶ*™ØÏ–m+ˆ·[Å^VXᝎÖ#PËĢī$ųã;iPØhđðōčÁnßūZ,”$ĶËÏnð þūc{ģÆ Ï$œz§ũŧ”Å~2éc<q\lÝšíĢûúũ}7nČāI>rŧsc6>ī…ą°ŋéņŸ <ļA *߯ÖŊŸėÜĨ+ŪcŒąkŲeR‹Ķ=ųSMĒŊUēIŌAzŦcY@æZs0_í ä„ęjH9ÏN#ĄŊ ąkރt/!Ų†qʈĮlʎuĪAgÚÓVXaÕՂ­ė)9Īj@ eP?(æĀÏãŸiucZ‹ƒ'Ûķlđ’˜øJLCF–.]zӖí˃§i‡V­[ĸhBå*U°—ˊ/bean·ĨĨĶ>MK faĻq§NąL ”š`“âÅe—HKK=røP°;î\ÂÉĀŌÉSĶ–ŊðžĮSĸœLJū””z• +{ãú5N:tðïG)([š,W5sÖŨ\0ŨûėŲ3ō"đŲ9tV7 ÕĩŅ9ļ)―lĨX“âî1y[ą°‘‚īädĨKZ@hï5ÓĮĖŨ4kå–Ļœe03sČXm`‘'ĩė7ŠĀåCČČĘT@·Â +ž°áZXa ppŽĶxceĶg^ŧ~­y˖­Ûī>tðOĖ―J•.Ã&ūôQ“íô%Ē2|p‡ÉÎĘt<|øāó/ūhÜĪɘŅ#o%'ŊY·A$ŋÆC[{Œģ"î_üûöm˖,æÕ kWŊŠžŠI…#)‘NH>Tåg†§ĨĨņïóÏWâ~\.FįææĘīšÝwd,'7ļütâ„ÄÄDč-AxŦՌzx?%#=ÝGĶ֙%ĪA#xjlwŒ+8 ~Š ‰VņņâVŁVŧ=š† ĀģÓîäŧÁWeiLɊœ7ļ u*ķjIF‘Ņԑū 7áĩÍÉ­°ÂēŨ=ŧذj·.?ÖüûøoÝēc­þEՎæŅžNŽxBũ#k%zþė™V­Þˆ121ņ" Ų_ųÅíäjØĻQĮŽąô[ÄA:ˆƒßÚĻq“];ĸĀ”ķK—Ū˜‚ĄåʕëÛûmÓ"Á2Ī·ļ’ûЧŽĖÚΖõÎüóČ>zÜU"#›4m†‘ÍÞ={ČäÏh†ēßå{ðĶ͚zčŅŋþŠˆŒ8wöÖ6K–.ßūu )‹"qË= ‰…ŅsläsRĶ›c\‰Ûē=vÁJč|[>oSĸFžqÝE ŊEÚ‡3 ·GÃWÍŌT@l”ūÔZa…æjí"aø?Yú°HãENþ}âxܰĄėĮd‹šŦWŪäæä^ūtqïžÝä@AQ2đŸO™ēaýZœžŧtënÓš…U+WœJH`ŧ ņį~äðA jÝꕇĻ^ģfĩęÕAįO&N ŸÂmÚÆ4ltĸ~Š(_ĩņ.Ū`ûĸÜĮŠ *Kėß·ũüđģ<‰gfeØĸ'ođzíę˜Q#ɰ †e8ĮΝM}ō„KšråðĖúēßãÜđkŨŠ+ÝŋÞû,“’â#•m”ņŠMŒĒž`. žsãĸïICQĨóc>Ð,,Öīdg˜n‹1'DXŧį‹qŌŲĶŋƒņg0v’€ÖŪ FĄ:7+ްÂÛX-û9*dA øėĸo’XGJ8y",ž ĸÝ―īøŨ… ~þ)88Čå@2áÚĩŦqƔ/_ZzûV’Ã)ø{*áŸÐÐÐGfef1ĻĮ°·{õĻPáųĮmÝīiïî]aááY` =`ŨPÂÔÔGcFŽ€ļpëŅ­3l388äŋŧwŧwéĖŪŦÞŋïtÂß%K•&œ™žÁÝĐS Ýb;!ŧIÁëY3g,^ī72ô2ŧÆv øÁĪJÉ  †PĨ°Ü#čI ōV_ý4D[!ÅķFyėģĀĨšĻōXŧjrk\$Ÿ`ÓšŪBeƜä&ųk„ΊÚÛfyiŪÖ +Ž‚čý‘Į=Ķ ÁĮAÁÁ@į―ŧw9 †\ÔÍЃxĄl ų‹dö~Ę=þ pAã@/ōķđ99@  ąmol·hÄāQI.iV8⭛I Xɚä@› ˜‚qš:Î‡&ØĨmļŸōMhęīeefgf&ģ[EOžÂÂ<€ŽDÉ…‚†vÓږÁiĐOžĨ=Ĩ΀įŽNO ÓýÜnŒaÂ$rÖÜËÚ ›ģ|õš5Ē ą°|ÖR"yGy\Hū$ÏM­°Â‚Z݉’‡t)TēK~)ģŌū6’^)ŌžĶ^đ|éäɓhü]ïõíŨ­{ũogÓ3ũ+lĮ<‘œ@é2eZ·mûûï;þéðfĮþĄw0Ų‰ÕĢĶÏøj‰ĒŽT―zõ_zĐXąb-_}õęŋWžeĪë•­Õm&LúäũíÛ>?.īXąø/gž?ŽBÝķí:„…‡5oŅ{„sgÏL˜ôi­ÚĩĨÛîōĨý |ģS,ßhÏÆŒû€ŲvíÜI5.ĒĩJ•+UŠˆĻ_ĸ•Ó ĸ\ū|ĐfTķ 111Ī8f<Ķ |WŊþËýÖŽEßÉ-7―ðâKÝŧũ€A?―båĘãÆŒÞ·wïĻŅczūõ6Ü622ēoßũþŧËýū;dč°˗q%/žðâ§SĶĄaƒĖK·GŲR”__>Ûm5fģL­°Â[·ÅH>Ŋ&ĸ.R―Ī­_ėÆúKÔP~ĶT Xh(ū\ nÚŽië7ZȟŊX‘‡e&:wîlügSþ2ČÃĢ֝—+Sååņ”]ždÉÛ·OŸ>õ“Ipį&͚oŲī‰ýĨ7;vęõvo\WŪX‘öä €ŋf~ŠV―ÉStđĖĸïå˧OÂ§ąjddŧöí;ö %Ĩn―z ^llė+Ŋ4āę’™w…mÛzvížrųŌߖ,&9РaĢũúô#ŧnݚ ëÖq|ŊQģĶbÜxóW_ŦU+ęØŅĢäRyÔüøņīɓü4ïîÝ;ÐUō'ŸA§ØØjÕ^âCxŪ\8iiŽ9ÜĐc{ViÚž‹ÖĐS§cĮŽ\mdÕHŧC<%€YėlؚĢBÎCUŅg h­°Â+…đ ĐĪ, ØĮKl}Ž0ÂSÉÛ/^"ŠV-ø° VrX$cŧzåJčēTí^#­IPrčĢķ˜GfSÛĢvÅĩKĘû\wîÞÆŊķALÃķíÚ=xðāė™Ó>š@ ,Õ(ĩŠÔLëÏ―{ÖŊ]‡ŲšÕ++VŽØĨ[ōåŸ[ąl Š\ðŽ‹į*=zždŅbš–;ī•ÃĢĮ˜*8$čøŅŋ·kŨūE˖ČՒo&Ģ@ËĖĖDC\ēti›:™GėóįÝLJš=gÎæ;6oÛŅŪݛîœxĨĶë·neŋˆÖ kV­TŪ k‚#þüÓO-ZīÜĩoĸĻ1c:žmóFĮ”u‚kîۛ‘‘á œRšjųē―{ũ’ŌÝķ㏍Û+əž;{öÔĐē“Į_šxņijęÜïūÅ#qŲĘÕlO­ÛļĨF͚ÂĨ‚ƒ~ģēģöîÞ ũũėÞü åþÁ?ĸdÛ·nÝLNš˜˜CONū ,:ýäĮĄ†; !|ðaļyãÆũsūŐwÅŠ5›·ýūjÍ:ŠLš‚›™Û―n͚„„„'LÜļuëĶ-Ûz―õNå*&NGõB‘ŠÆD$G€šüZa…7†eĒ(Ø$Oû(댃Ņjïnĸ le@L-ã—Æą)ũïaŽã™Ó.ąoūúÚë°ž„ŋOüóũIšd .%^˜7ũûę5Ēî߇ Xh‰â­š7ËĖHũsųψĸlÏŪ?Ļ‘JNJ:rðĀäfŌ<ũ―{ũxÉŨÏ4€ð ĒHM}7dP˖ŊEūøâÛ·Ž9B7ĀóÃącg•ž~/%Åt”Ų·gOÛŨ_Ĩģ…Y'Ž=wî ÛŠeSū ęųĘĢé".\€Ÿ•Ö3vŋkŨ;z$-5 ÍÂęåËöíÞŦŽ]þŪĄCI1Ŋöę֙ĒīāАUK—%üý7ubÜ8،ä›I sËfï āöŧRRîõ{ï&M›GDF^ŧúïÉãĮRŸĪŽ †sR`Ŧ Ę­°Â ïëÂp@īĪšÞGB*aĩ} pĸA IRpVūŒ:Kæ8p$\ŋn PVÂōîG8X?z””:ԜŽėK—ĨEŪ{{þ>y@Ôž§ yŋƒÛķiS­Zõoŋųš\­ [[íaŪĶRą[ķll„ķžuųûcöā~ ũ”ūĘŌųyŨŊ_OšqƒÃJzŨQ˜x!‘ÉM?…'MnÞĒĖwíꕈ8 o}–ņėáŲ‡\ĮOŸĶ‘Ø%u€Dâæ$ÞÅåķŨŊ]》á<ÝŌŪ\šDjÅx~ģtâŋŽÂ qärYÛ·nfY ÔtœsIkˆŅ ËhÍ2—‡€XŋHˆ[a…ũ…U`šmsē˜cuŦōQi–M――ĩs‹ÝŨßßĨ[@R)Ëy—áĀ|2þãĮ]ūr™óŠ1Āf3ŋ#Ī5ÏÝZ_VģF $°“?™H0ãŲČ$Ōã€Ĩ{ą k/Ĩš„Čcí,ÁlāĮā/`Į1#ļ ŧZÝØī@˜WéęøôéÓŅ#†Ŋ^ąÂnœe]ÖóĖ1!šüĩ…Ĩ” ðFŒūŒ ĸÍĩŪ,-š:EbpUC/đf™ÁÏeüeļHŽĨd;Õ [“WÕbc›VXáÍÉ5TÆðĮjÃ*ļP šUq}‘/ÂÆĀ& ø3] åØG~ –ãF˜3ûķՌҁŲ­-čėtk­,ðÐ%fúgŸõîÕsþžÝ*ĮRŽðA8́Q` ~MʘģĀģÚwœå ÕĶKrĖéTĀĐ ’ē1bøûïė·ņĸcï vegrädfÝyۏmøá܃îþO)I†ûJįŽ x;‹N}Ŧ‚ 1ĄÍWDHIýŊĸđö^Ŋmļ§ą^Ŋņœ:ū•ęqD 0ĻĖÛĪüÍïĨáöH ęđЈÅĖ·RAœsÆģũŅũq…žÝe"BI|øðáĮdÏäJRW]2ģÎ[ÔÞŦĶĮAęœKÚ―Cx­]U$ýíĸm< qLŌž§7YFH6ŧŦŧŊŨúŨŒpû™uŠØĮ)1nÅŦžŠf†ĄĖ<įH‚™Ũõķąũ‹R]ŨŠļ^câ_ójÏiEFÄsaF\ïk<ĪökW7EšĪŠ+B†{íꊊŒPDW“&@ā}7žŊĩmũԓŪļf`<NŲ&ô ũǘúĻö'ōáÓÕÚ+—ۏ8(M7Đ―v„ĘSÝAIėnЀ]S{­Ũ<ņn|§ö˜TOŸsˆÛ_3ķ13UÄÚŊSŋWÉÕđWŪžę‚!)ÎÅŪĩfĀ' 1ðôžá6Š  g?―-Œ™đ=ŌïĘü |[2ko]-*˜yŌ’ˆļ/7Æ4A‚JāGdW“ī‘§ 4ݞĩ„ũ„`͟ø8Båáų 3 \Ũ#â3áGōáģ1·šV.‚cȈéŪëØ~­]Ýu:$ðŪ"­•§ŪîyTØÝUwēo"ƒ’ ƒ™ŦĮ=!ëĀČĩžë°r“ôÍwLdÆĘ <seWÏ8ïÖôAŧũ–hC QÝ-ę7žuI†vwÞkÝv†@į\ķŨÚ{ŋfĶŦ$y|ꈔt§Õ83Š‹ĐŒŧÅķïEÐ@0ėđŪë™ó;3°Žĩ{ĶŠŨZ0N•áČīýéj$>YmŽęŪ)3ģŧoŧ-I§kjöÞQu<“ó'߄Qu`„ĻP͐€!ifūÕÓUu$ ėSĀH Đû€åtŋŋÞ3ȕÝ5-…ōÔĐ˜Ï[Y r%‰ëzÃ|íŨđnNeĻŠĨ†dÐc]uŠūôM+ä'Иjáņœšf&˜Ū.’‘ B!‚Rtĩ)žN|Å"0öĐ9œöz­H{l(BÁũû‹Ō#ßvxí_uĘãØÕ~øðÉjŲhÄļ΅!čXAģ{"eāëýÁķĮbŪžÞD‰ë\o›’bEOƒ č6Ɍtî|6’ÞŨąky0€Ļ˘%%ģŦ0ØđsEÄZAčq.ƒ§īSqÝÁîÎeļgð$b]ŧ{@bÅzŸ7ŒŠĻ*IĄŒs.ōÉ){Ŋsō.&NíĖ,ŨŒZņ= ķ”U§gh"Üv„0Øį€X‘=ʼnŸĮ‡ÕÓ#ų•ŊŋŪ/đÂōÔ|ÚÕgƕVÔĐF3ŪŊËô<āœS§ŋŸüCðļ="”ņūŪÛđ™įëĩ§ŦÆsåtũäĘėéķ35ãŠ/Û1 ûØÜŊŨôLRKyúŒąVΌåJ†B]M13OÞĐÅýƂ37 ŧŨJPUgęø–uOO9―Ŋ3ÓĸökWã~Úvį3NEÕJ†Ūũ;ŸX!T]ī”āœÔĘ|ßi ĄˆÄOäÇÏn1@Zïs …$†)RļÎĄīÖvŲÓÓßé$„ļcÍŪ―_qĮš·ļÕ= †ēŧlÊęĒxŸÐEefOU·ˆëÔĐn‰$Cjĸúe ŧ<Žˆ:•'ŨŸ=4ŊsQLĨĀSįŲY03Č―f$C"Þu ðäģ(~s[Û$)Á†+GwöZÓuØ{Ÿsz,I}Û\qSÝ Ķgڄ:ÝķïL&ƒŸČ‡ŸŽv-?fŲ+Ýãa(@·-*r°đâԙņ^KĐÓĐĩ·‚į}ŒŒš‚‰AdÖ9Þ.Sc`ˆŠŒigdóüȕïëkÚAQžęˆ uŠdū–i‰ÆėÜ]5c˜Rī[ŽßHį6qfÞŌ›æÎđO͈ÛėÔԚqO7āũVīé•ŲÕ5•Đ8Ũá―;™āĖĩîĸÐ"a_Ũ{ĶũJ’5ũ6åõK`ŨÜįĮTΕzŠ&>|øðŧÚvWÓ>SķEuW―/ØĄ8__] 3"lũÔī{ĶOÃĖļmxG!M5z@C|^öšk‡a)Æ3ŒH-##3rEUëŽ\+“ķ‚ }ú@ŠL“€Į&YSӝ™Ņ=Jf&! 62–iũP‘‘Ý53ĄĖŒs>đ~Fė™îjÃÆt·‰î!1hšw5 ÆŊĩ{šŠ=ąÎĖĄ$XU„ųƒ!ĘcžŽļАZkŸS6D‘€ĻÛģÓÓ3x‚‘)Ï ^Ũ{< ķ{ČPȞ>m ôą”a>|øiB(2Ũ9‡ĪķŦK+ũļŦïë‚ýĪĻí@ =íADTWÕe{Ŋ=3>-ĄSWUEd(Þį]§3“üöM†ÎđŠzŊÜŊmdĶlüĮT°SÕS†%Í4ā WŪŠĻ„ëýžž šï"ÖÞ5%ƒöÜœäđĘå•9nÏØÆĐz$ļ2ïČļ"ōqší§Ža k­35°VŌ8]Ķi €Ý=™’äI)s―ωÃg† ~>|ÆÍô؃6ŪjŒcEՁ-IÐ9E03!Ø0 {Ú%…Ra\k_uyŽĩRŠ*Ąp͞yí=ðéNiïŨ=m@„18ūė―lwŸˆ%ōœC‘ˆĩózÑaû}ŪŧÚ/clŊõÍ=p(u§―Ą`áúzÃXđ@œ:ÏŊįz& ßC ŧ`D(UGā=„ įz‡ĪØ3sÎE0b]ï·ŠĐŽ>3–ĪÐŨû―23—Á:gÆŋ^ŊëÄ›Ī{„>üČŪ–ŌĖDætŸë€ØŊÝ=$"s­U]5{ėŒ€Ý1L·§oÉėę)‰Āõį•R=]U{ïą§'DR=Íû‡2ÚOó(IӃ?2€ˆXU%x­Ïņ™―Ũ―xOB§NÏ(BĀu.‰ãånÏäJãī'#íīs­―Ÿ|VЈuUu21ÓSĄˆČé6L+s}oņˆLEuÍxå @ŪTJÂđ.Ãûõę™HeĪ€žfČ6~">|æÕb+‘‘;vũø7°}Î;ÞŨ5Ýš’PėnI§P<ŧÎDF€Šųú·_ķį.,ĘpŨyúÁîē-…įiUîUU$!FDwÏLū–oegŪýzėÏŽĩĶ{ėˆ€QžĮāëžē83k/ÕûžŨÕu‰PÎ<·Ę zJbîeĢĮdͰcåx(Ī’äĖČĀíïÕŧæ^;wÕ!3}/MA?s‡ŸÉ^A|lLōqkdˆŽSOîЈg›FLUÍØ+OŒIūöŦšIØōœ ãÜŧК•+âÖÓ}2ÉxšRJIūßïČin‡ōn<Ïuõī˜@Ōöđ.)r­ĮÝ"Ēį{ėËãVJ‘1vWIz­ÝÝ#7ŧ‹wŪ䒐™Iōôa@Œƒčî˜.‘‚Yu)#!۞!%ĒŦ \Ŋė)ī Ԍ"hМüðøðáà xcû?GĩŊëúũŋĸÕÝkå_U°Ĩ˜ÎžęîÛTŅÝ]MIĄ?ûYģŦN5fúëë @D8ĸžÉŌÓïs<ÝåĐ+"Cųū;SQk­ŊũÄŲ·XûŠýÚ_ó5ÝaāOĐIp8É8dÏtÕ^ûë=Ý peüãĸxōČx‚…ˆð—ŧũĸÁ_įŸwA*ršŦ[R\zúî˜Ûéã‰{Ņî )RŨûýĖå™ŋĶgBzr’ŠąÖę™:‡ŠĩÖ?ĸų™y€ŦÆ;—ÁŋýóýücáĸÁößĸþ}JēŧņáÇĸDäŋ|ōëŨŊĸ՞ŠĸößĸĮ9Ũëõ§")ęœ €ĪPÔ EĀĻ:AðxÆ^đzچˆˆ5·FŸÞ°§ ’˜ņwžŲ=„mįÚ݇ŋĀŪĢJūOV< œnJ?•hÃö#ūįë Šę―_ÕƓێ=Ó $õ9ŠiėņLÏZŦŦūo\1Ó2ŌpUgåÂDÝ)°ž`ätå':čï k cŠ˜Ä]ŲóÚŧóčĻŠ<āũūĨ*•JRY+{"‹’HZhČp ĢĢ"ˆ Œ­" "ˆ Ļ!aSV‘°FPE§uh JÓ Ø­f„0š–U0ĘbR’T–ŠJÕÛîýMĨrĶgĶ’Óp’éJÏïsįŧååúž{î}uoŦÛ=p`žŪŨįlLLôđsU NJBŒąÜÜ7:ąĨN§3ØŪX,–ĀĶ9Ðö’öÖ$ ðcÐrr}ƒNþß1čü·ÜØČŒžœÞHGrã#ïHĐŪéŠâŧ~bëÏzIHHAQJ ƒéš~ãQz„pĘ"„nxʼnBĻ›aÔ"„F-BaÔ"„úëG-B!‰ü5 „í™ĮŲAātÕsBĻûp€ö HÏ"Dn?R‹vgÔRJCų­ÏÐT„ÁĀéåĩnÖė㜓žÅ,“Ä)1R°šĻ_·D-cĖápÔÕÕ)Šú9–˜˜˜’’"A…RΞĐņT]muĐ·‘HąÉyķ[âLīíęĻĨ”ÖÔÔīīī 4(..Ž„žúúúïŋĸ222pn Ē„xuōc―rūš9*Ԛ}Kt˜Éԓn{hr{/ýė9%&ÂBþ" x[‰& ĪKÅĮĮ{―^2Bš>[Â-1VŲfI§4ģšÖIˆ9&..:&B 7„+Ÿn•Ō…JcĢŽ4*:0čžm1čôX\Jæ·7žüÖÎaTąÝõĘįŊO3]ߚR0ÔF—/ĘeÝįiQ 6&RÏ Ÿu?ļūøîī@ó.äiCB(0S dJ‡ÓQýHų'ë_}óM’nK^ņÛē{2‚ũļ۟ŌÚâÓĨh›•ÕŊ>―莒wïím€ŪzlÎofÁCš‰ģ;|hAŌŠ+^{ĸäŠēŊLwþqÃhŋJ(JõšcŅÛĶ­{Ģ wÜÕ?n/~§vÓoÖ%<|üdoß(ÆÍŋ€RÂoë(FāāŊ"Ā9 %„“ö?š3@B BX DIr~ũÅ3ÏčĸĖæōĨ‰ÞÚo+ŋMĩ'ȁgŒ·įC 3‘AEéø†•›ĒïÞY8†@ôýS'ĨĮŠŒņĸΕķVmÝEʇĀ%ßâéÏqÚQīmÝÖ―Q œóŽî Ē kz Ŧ#—-ąĸØDĒj†ŨqrÃúđ_ŌóĮū:ũŊö|RöéĮ‡/ÖÍYPtawŲŋāžöF銿óG]yCJįnæYÖÃ{Ęë3zgÝKŲņbuÅo—•ŽýÁõ‹üAą™‹ŠÆÛQZēíwnĘfo*?(1ÞŲJMBáĀ ÃiTĒÚūōßyó§Ž]:-1’Ö^Ģ~Շ3ãŌĐ}›ŨžömšyûČEŊ,ČM„]+ÖVɒóĖÞŨr—n\;.ĢeËÞŲ]ģúąÏ‹ŨĖ;wh?ÏūKŪúd]y}Rģcũ—ŲūļvÁīČÆ?-|­rÆŠ™―ŽęîÅ ų‹Ÿ‘ôŧßÞļngu|Öüå+ÆLbœZƒqčÖY-ĨôÏiK‚âŠ9-Æße-™6ķbbáœg§äĶó’Y3f?·Ŧ,ũéœY6lð}đđYņĶ/ZúÐđß]îý‡#öUŦå&™ķþŽ>eō·ĐĮö|tyØcŦWŊÝ1{ΎĢÖÞé™ûÄĒð‰Ĩe32ŨOžņu}þœÁĨKķÂâ_ïÚbŽmŋž ĮE-B8t0{ĪŒwëĀÁyąáTQ5D3dģōãėY‹­Ģg—mZ:Úīųöƒï?å8}čƒÓę+ïž™ūuÞ{Ÿî·ōÁŧûũđ6xÔė9$‡{ŋÞ·Ï6yyfKõĮ֏˜īpýšYE/­ýæąĮGRĮ#G&éÏqÎ.žþÓå>šRõoÏ.ųhŇ;m‡—.Ø4lũę81ØģQœ·Ŋ}tï‚Ķi?N…ȧÞûčöŠ/7Ŋxaō7>ØūĒŌÓX{ðýįÃŦ~r6ķĻ‘zEÛS33lķČäŽþVە^}3Mī‘ą0]ũŠ$ĨŨÜe+ïėcđÕėdĖuåėđÄ[ߟ;:%†Lœũðé\:D?0ņŽ ĨÅÓŦ&,~arlxgŦĒ(„Pč။·U5xÔ Ė04Ū(šĒjSPŲ,VŸ>ü“’ķņ‘Qv{܋OŽÝĸâá‹î‰-ŒMX°ęÉŧ†•ŒŊ ŠÆM‰―­qrjïôŠVĄ†ŪК3iø”%…vōŸyÛ[[=ZD gmq&AŒãû?mŠql[üžāQ]?Ņk5Ę_AfĩŠ cÝþÅ\@:ĀTŸĒ…9á­4éž1óŽ^qGlā?Lzé‘Áaē965]ĐŋĪę…ëeŠĶІC儈Œóķ‘įŒ%šÏ_c€Ä‘dŌė8sîįÞ}•ƒû* ýĨÎĪæ­öČŲ’é/ýԌ’Mo―ĩi݂ÂeURΘŧŽonxõMŦ_~û9SÍ §þČÔ5F(5TŊWÕđA“ûõ9óëwķ}ēŨÍðĩr „ ^·˜ˆr­-aĀŨ+ÕęÞžfåō9+*k é}FLČÏS?ÜúņĄCå{vhæT ]I"7ˆ`€ņ˜Þŋ‘įūÖč‰ÉūĸĒņY"/ß2boʼnkǐa2,qŊ”lųōčEn0kŊ‚ kŨSÅ >î…ÕJRŒ"DŪZ’ ë øKŒôX…G>ûî?8~Ė–œk-Þ{Õf‰ÎÍésēŪĐ hųØ{†s”RœÕ"Ôãt&„éRlÖĘ7wúâ'š5äžaýS“‡-}―ïgŸŨÔ{Ķžžîū‚ÛUŸþðÜe4­ŋáóÝöŦâB!iÞŽŅEĨ›ïĶ“ė3W•$Ø…Ļˆ‰ŊÓ0Ęļh~UIb’އũy}ýÆōĢ?d>þÐŌĻ&Åh―ķnŲúŞŊnÕRߎ „=,xM7D-é°ðĪ“ĶßÖþb–ĄŦšÁs†OĖ+B]õiO|ïĖ!”1UÕ oÔĪAĢđŪĐôÖQÔPTF‡Žm(>RöðûĻĄęĖ―cÆó2åšÞýâxËĖ’‘ĒfþĔŋ „û‡`B=F­H tPGļ.E$ÜûÄô‘œS* W| § žœ”sUS™AsšĶ)ywf€Ąę ‹{ôņéœųh ĢîcŠEö“F|ŠNhäÐŅþÅā$sÐĻYCFsf‰ĄŠL7ÅÞōðŒY”J™ÏŦ―čZhÚ@E7ޘ[QQá?!::Ú0 č<‹āĀõ?Āõ5Vwųûį.y4éķ!ÃĪEŒĸŊƒw”$ĐąąņÔĐSøvm(@ˆRRÝĖ]p5ņýâeJĄg=ŋG%•?zÎÖ4 °N3‡›(‡Ū~ŲËn·Ÿ={6''Įfģ‘ĸ+ĐYƒ2ģ‡PĖ0YĸÂW€›ššÎŸ?Ÿ””DBBH”YȈ‘ëšN^t6ļĒL&‰é1(iņ(—k“ĢL‰‘Ē$Bšüļ™ĖĖĖK—.;vLUՎڄ΅ėfģ؟ģééé8Ĩ !á&ŌŨæjšPÛęhn „’EĪ<)RĘˌöG­(PčŽ?ƘŪë=â8_AdYÆWkC B:ƒûđEkō2ÆIÏ&Óä(9Ų&G˜Đ_·ŽÕöļm}œÏ†&„8ƒÆ{ÞgT DĐH ĨÝģ-†BxĢÜÄŲ^N„BÝ Ģ!„0jBĢ!„F-BaÔ"„Ðĸ‚Ãၠ„ę‚ˆŲĘĘJ—Ë…iÛB˜ģþ€õĮ,)**Úķm›ĸøŦ††gA!äUīúÖģĸ9ŦYÔAÜIENDŪB`‚doc/images/ifw-add-components-selection.png000066400000000000000000003347241325366651500213100ustar00rootroot00000000000000‰PNG  IHDRÎbðZŊrđ›IDATxėVŲSŨõĶ\åW—ĸû5ypÅå‡Č•H–”û'YÖ"ˆ} Z,•„äڜ @ēÄXČ Y[ąČĀ0Ž3ĖÚÓËtOOoĀ€PR)ûį’e§*IUÎĖ5'íūĄ3å’\•ïárúÜï,ýõ9#Ĩ šÝîÅÅÅĸĸ>ūüōKĘc:~D&Ŋūú Ŋ’ðņLžiÓŠ}†§ĐÅüŸ>Ošzė•·9éI^Ĩ>īV+Ĩø? >öx ú ýlë?­ðÛßßĸÜ /žðŌK/=ĸüó_/ãņãĮä$@ŋåę›oū!'z,@>9‹WôаdĢ“c'3™ÆĢGĻV­†ŲÆäOAžýaôüõąĪĩyīÔ]―>ī“–…œöú ß^üúĖü'ÓĮ&9ės"čĮNĀ3ŪüīūüōË/ūøbŠŪë<€ŸÞĨĨ%8ðļ°°ð°ü%čÓ d"Áœ™Ô"€H E1ܒpð Ģðīɀ­š^ÄV},·6úÐɑđŌ•ĨI{},dK„ð,ëþÕčcßUōú˜C,'•dī,īąúųA<™>(ÂęõA#MžY}Ŧԇt]O™ŸŸŸû>b †Ū‹Įx=<ÃĻŅh,&\Á €G°!x ÃE spjšFnÍĀ*pUy 0ūĮ(Š-‘[8‘fđ4ņ@Eôcoū%„†đŪ nÉԁÖcŅF&Ý$ð$ô™_\zļô`‘„šą°øžáõYÎ[ĸĐO &fqá?ęOþðOq<\ZˆÍÓú`ķÄŋē‹ä"æÁīÉëC"b&Î~, ŨŌÃ8āĸ &ÜŌ _Ē ‹†6ý ―°Zƒœšƒú €üäõAgōú  ËXRúĖ>>ÉïœŦŅÎ'Ð8ĸV­"EO"I<Ą‡<žķC‡jĩņČk?;öÆ/KЧĮqÝÝÝ---ÍÍÍ}ô‘Ûí&Đ0ûðúgꚎdxëĘŨÓj6:UîŠßĖÎéËĀ@<Ác~$@@Ó4:hX’Ģ–(Þ"aiĖþĘBĀĖŽHÆÝ\ŨNŸ8Yq9î;'=Š>›pœ{bČ1%ƒeŌi?<æŠĻ:Ý6ܐÐú€_b―.ŋ–ČFkū<9ó1]šŨÓŅÞqõV+ąđDóņņÄBqŠáĮ‡úûîÝûŽhTŠ‚ßúÅ!D™õÁĒ},m'ęęÁi§+†Á§įų˜ EâÃƒ}ýý}ũz@=5N1( 1õöĩËŨĶ5ƒš jPÉŠ&ķ@ũOŒđ|žNū#ÅīŸz#ĀÆýGf2ú €Œą6úÐã‡6­Oōû…}ŌZ<-ė‹Ēą’>`$ĐÆ@•'Ð'EQ”hŠŠ* \Ū23:―Nj<õpjDëh―ŧuӉoŒũõA”,˄Ųá ƒ­­­]]]`@^ŽãnßūÝÔÔ455ĐHN ÅmEuyĶũÔf•5ntð1ï(ßsðJFúū·†œƒšŠa'`@8ÍuøˆNBC" N4,NæA ôÓĀ–,ú˜ó˜3[rǁMâ[€ÓN…Ŧĸíš •Ž`æb^ (HûŋīšeÎÐT|rHĢęsáņO2vd$øš +ø#ø§ï}>!+āUĀ ōØüXgcVM #ĐøAÔG34ŅsbAjęŽÚ3 ŧSS3JõÏDf5ŲŅŨ;æg t­wJ·lČ.ĐÜ[QRyė”›U ý_õĒðvQyĘ1pĖĢúēx€ïÏC‚mĢ· ýucNmŦĖĐüðþ,IŪĮ—$œ8TÓw‡5 ßLߔšSšwoyqÍé6Ÿ úō‘æ  në 1đū|Ũ–Y3€@Ú#~—0ðÂ-į›čca^>ŧŊčw—ï€â˜%ŽåYJ|Ŋ•æ‡|}z~,||MzÆ ŽŪý~Yö‚ÄfĶũ gaé^ĖOïÞRûõĢŅ<)‘HD’$9°ÁõI]ý…W~úįÃÕĸhŋð·Ŧ—ĸÞqņë“ĩ·ÖžÖZ\,ð|$A#dĀ­[·ÚÚÚ†Á_}Qá?𝝝‚ `ÚD‰čđË ÛkŨܕ>āūč þĨýĒwxū5ŋyã‘ģ‚ĀG–!/=č''d‹Ž,Gl Ūˆ˜Ö\ü0ĄE Aƒūēī!% Ðdģ>X Ë!óØč#‹Ėé]k~ōJj۝Im~>äčÞžöÕ·ķôČz˜õ;##Ž1?+Ä_-ĖMÏDˆāŲ`ˆ ų§GãūŊęāųÓ[JęúG'N ģąQD8Æ% x‚!IކđÃō!ßī#‘īŌę†2øĮ3ëąåƄ°ļļĻîį­]_VÛ·\5;ŌŦÚ> @8Q[Uƒ=åđƒA=6kϚō|^–c§'œ.o8•yÏŲęwóŦN8= |‰8G†ÆĶf(‘X&ĀrĄéЉ)_ˆ Á Û9::1ÍIĀ°0tMø<ïũøØp\+‘s;GFFž °d5ņLŽ ;ÆA‚Ąˆ,7æïïĘÞstˆŅįg ØeÆï p",|>6āe1äsƒ–ÎI7/Ęss‘Óû k[oČŠâũúYpEĢBČ qð‘ļ Ũ§ŽŲ°ĶŠÝ—ęō5ôųÃR„ņĖY.UdI˜žqú2" ū@ˆįŲqįč$|#)b??ô:9ÉÏú Ÿū5—3oî—%-=ÃčĄũ‹ŽMbŋ?}Âáp īȇ$ĩgėt―þęĢ][ŋ-/øëþōo+‹g§E7žÞķ~­ßå%‰ðIHGGGooŊĮãa†ãļP(äõz‡‡‡/]šäũûIJAâ!aqßđU=[Ū„ôHõ·•ģ7ä†ö―ÚÁ] [ž>03 ÁÞā‘įyô`·4ŊĖðæØDXrĖQæsQâÁŪð{cB@[:›đ1LKNÚ°ŅG`}gōß^ŧnCa}ŧĪÉŨ›ßߖúæöĖý3rôîÅúžĒ’‚ŽīÂšĶ Ļ…F>É)=>ÅEœŨ/Vė)LÛūđ°š)āwŸ;õóu[K*~ędzÛNBT~<ęQ]MĨĮσáėn).*"QÕįü|\%)"k’·éx^ÖÉkēĒ‚GQԁ+GķæVßėjŲī~ã;;ēÚnņáļ0†ūîŠœ‚Î`(`BžĀšÏ–§įWîËÏMßē)íBÏĪ0Ýģí5ßÜ\QÛæóL6­Ę-Č۝ĩû|ũ`4"īŸŠČ+ܓ›_|æÓá+ĩûvgf•ee―ýöŽýuŨ$=Üzī:;#=kGIįÍŅß/ïĘ " ŨΝ؞ž_ZÛÜ3ĢĸŲĩĶüœžžÜėē#įÝ!1"‰€H4ęđŨ•]røæx ‰o?–[Õp5Š*œwļŠ,ũúÐč‡ĩ‹‹ Þݖ~ürŸĶK'kŠŽ^žĄDž‡Ëũ^ž>5īŧĻ.­ŧ,ĘáŪĶúâü‚Ėôī=õ^ĸÄáĖÍë6mË+kp…đæC%umwÂaîãßŨü&sw~vNQųąû3Ž8óųÞ=e{Ŧ*33ŌÞŲ™sõóïÖÍ<4p~,#ą’!H†1F―_čΧ77ˆfâ•ý~Ņ'šũ‹Þ §Ļ y}žû'+Ö[Åq…gvũūöÚũâ'~píšGĖÓN\ž  ĨäGR’&Ĩ D­Jy„„%)oĩ4(ŠPĄķQŦ"ĩ•ÂŋR5JDEHA!y„’0ŽãŨÞ}ĖĖÎô\rīšJ Šú ŽfgÎũÍ·Ggg.„Ãāœ‡R&óNCÚķ…ˆ{nĖó⁗ Y­!)ĨD !$›ÍZ–e~—––BB|.YäS‹UŨUXIjƅa…ðĮLŌēŠlÜ&ŒŒs ðah.šRJAÔŦcðĻ5Q\įtfσKHA U Ņ—Õ@o°„ųĪhŦøĻ=GßĒĻ>QLˆÎ€ÂĸĻĖĻTíŽĮ:ĸ>Óyí“ Wo.xöÛY[ &FO_īiËÖÕËæö?öiŸGĻqÅėŲÝLz͎—ū—ŋüϛēīuâÔæĐóW­Y9edžþ‘…›·m]―tN߅ã7úƒ —U*ĪÖŽg^ÚŽ+'Ŋũŧœ€·PIĸZĨåû ˜›Žm yĐÆiSëg-ûņóߜ,Ӟ‰#ýýÓ{~ũ‡ũÏ܄ķéïëįĐqë7m_Ôlœoo'U­ [sĩ-ܰrQŨG‡Þëð—­XĸøŒšc‡ßŧĮHčÜQ%cV­{yéô:'ðâ5ŋŋuįŽs:Oþíbwž‹@UNxņįëgĩŽtûOƒ8ú86fö†MÛ=ÜÜûÏßß>ÜôØSëV.Éw|ÐÞqS(ÅŒKӔ]įöĸņ=ŋß{Ķc°uÎôk>čŧÎé šÆ5ÖÎxōđÍÛ·<3ąâŌåN_„Ä0 ĨBØRäeRŠÐ# Ķ&Ė~bã–-/<ŧ ũÚå{Šbîó'·Í{qÃs5q5$ …žÝqbĸĐĄĨŦ^ŲõÚæ1ôÚ_ž>ŋ{5VûȎ];ÚŠ9üg%ö °ýîÛ?ĢmĢã~_<ԄˆÉĻ[c—FĢHŅWƒ>aĮhĘĒm\Eį8ЖpĢŊ\ŸĒÚ~ðú@ڃŨĮ‚“ôtēū>(Í5uīi ?ŽmĶĩ9QViՍR†otfÛ6ĨbIII&“C6NÃ9 3x ąa€ßō’‘1+ģ3#íęQéúšôČr;“$ņēT•a˜`,ęvq0  ĢéhÝQ"ĪE>œĸÂÓ Õp€>uų^TŸâqĪu~ĐÚӏZžĻŸpßĒéŅVþōúpÁATŽkÍe8:ĪŠÆ6Œ Ð|Üë8wdÏëŧß|į4KĈ€Tiļ+*[fLŽÍðP&l3ô}r+Đ(sïŌđÃŋúeÅã1"ĐTŒnDákf%ÂĪmJ惔6@TØ7āKðX0Žï|Fģ‰dŒZqŠ”ðōū~°ĀũĖĘqKļvãš5óÚęÜĄž™Ū™8sZ‰âF*f™l’tŒŠÐsóŪāŪŨóÖū―‡Oũ4æFĄÏ ŧĩĨĨŌŽãđ!É57—PYŅÐTVEoļū"“&ØDø~ Ļó~zņó‹+oŲųÚŊĸqîӁþ^Ïq.~x`ïū2•ËÆM6ŒPJîÉÜŨ—-_ŧqí SÓVEËä”ĸz9uškŌž9ąÞÎcïüųõÝŋ=zĨ;•NLЈdBj)č„PPJ Ĩ<ČĸëýýŋŲģû̓'h:e„…Ü„)˜ïûLIĄã8~*W7\s|ãρŸ9þ ĘŒī­%ŪĻČ$ϊöŽœÄþŅ­øeý=‰šÁŽÖŨ9˜ŊŧŦĻKĩ>63ûŋˆ‚"E>a€3HÄLnŠÕŠŋŊŊZŸĒ―ðãúŋŨï.―üĩ[ū^:ÂŦEĮ5Óņ“āĻđĶëv&6ĨÕJ&5 ï øI “L&í\Ũ…hš&^;Z𡾙ݕ ‘(OT”§ŠË“•i’ō{‚úĖX“˜ZčDÁĻpož"oš‹ų€·r1VuDqއĻ(baeõcÔ9ĒŽĒÖĮ„Ļļķ"šrŋúHŪ*›VŸ9uōB"ŨVn1eÄÔЍƒï8óŧËW<5?kQIˆ Ĩa`KBÍ0Čŧ.ã‚ÂT€HïNožŦ ŊóÝĢĮ]šbåÓóK +@)°Ô’Á ãŒƒŽ { 9MįšÔ~âãë―Ô4œžkĮNž-ύ)1Cxƒ·ï1Ĩt}ô+RÊpôūĮDhRRðâ[„ĀŌeá@ĀÃDĖŪĘÔ/YņōÎWwþhÉ·,î—Ā…l mvũVũ įÞļte(?bt• Ęw<ŸAa ĶĄ.lH–eŨÏæÕō·ĩĮėōle͔đ‹šmûŦŋXÝԐáLĸÎbc–ÁCÎ|€+UlęėI'ßÚwÖŦųFkõÅ㏝õ°ö' &5'‰’ŠR`JĻßPũíū[í—šÞúäÝCģŋģfų“Qæq"ĄHŽWļe ðĘ0Ąq;[Ūš{îõs·ũÂÕ[™Ķ\ TšyĮó‚€)Š ā‘þøāýƒ,Rîû}!°í1ŋ/"ĸ’om=q]W˜VĐęCÔDNë:`lãaļ6v‘Mq\_Jūabp}ƒņ Đ!f8sŪûÜąûŌ<åĨVĸDŋáS—NķNÃ`ÕMĨތ–ŨY—o­ýĩÆl*æg'Īd<”ūRBŅJīËHCäĸ0?ôäōÃčŧæF›mێãļûâyīrœÍ™éÆĨąï–þYÝøGéđšze}âS·^w•B>ŌĪD)…ŋsf~!öæÍ|ŸQüß[$0M2aXķųðÅô­ōČF<~ũlįõ―đĩóģĸ`˜5ā0Įē,{_ĪJ@Аta> 9DėVĒQÅB #MéŨZÐ#Nm \cCó đÍN’MÖøAæũø1k“ƒ§'ŸïūēŨ,.­Û‰ņŨđBï˜XWϞė,ttôôõ ÃJëŽįBšƒU–ŠGßï,öc°Bß§å ÓŦ­õųŲÅđr&ÎڃžŽöŪâÐð`ïĮ 7ŸŽO]šļý(H“…ÏƎýæhïāÐŲÓý“_*ssėãÓíĮ‹ƒŸ=Þ>4õtûåóâ‰c―gÆ·<ũÆčāÄݧQ<úüĖĐÓ]ýÅS}į.mځ__čđøčÅnšzŨ~;0yï fĩųû#†ėū9ųûģ?LËŊŌï—­DĒLÐЀ#!Fsē*ĸ~å3ðÁt.?’ÓfíKĢŅ@6 Ã00QĻ”S*mOņÍä•ęÔMëņ_"ÛF5ŌLӔV…aˆGü*lccĢZ­!Š" #Â1‚Ð_Ûyþ°|óÎģK_–þXŲ\r|Ës=bj%īĨЄ e:2ÃH!’C|$@ķ4•č§!~AŽ6„dlÂj§ā+ÔN§ņpÔŌÃ^RŦņ“Ņ“$Qā;–žÚKCe›n…‘k;QÖ ã4 m;36†įųqŦ'{(u}D”m5°ïIaƒü(‰ßķßSI “§ü4,9*NRßĩ…Ž<čž―S}‚ ŞâõųåpïU ,ĮƔä‰^GūŊü}ql7Iã PˆbĶØũ‘ëČðhó’―T9čb ã5s|å_Yį~7ŧčāĐ(MCôP~”†ži"Ý Â0<ÓōB?°€ĨÂ4öŅd$‘ŋŧŧëļĘģåÕ[ŽPJyJ5Į€ģ%{ąŒ„™,ž=wUDIāāLQ„a8ðŧá4 l0‰A~ï…ūvTÆĄĒˆ™„.°A—‡sYv Š<Í"č: Óöý(ūr^4Hîþprî mh‹°DԐ#w_%$Œfï…DĩȧĀR$ũ~1oDœė’{ŋēsķȏ@iüPōÃōӆģÕëõŽn†Å1ūû€oA§{{ČfīVŦÁ3icÏð[ķæ·UœĶHFˆSÂ`4ūjc6Ė$N˜üíßŋM’„üB…f hâCØ]BbKËķ“9ĩB†"Kēčl =đZĘœËDZde Đ&<Ðđü°E–ˆÆsxÃFā>ĒĄžeÜÎ,Õž-ĩú. Ž ęQEtēC?Ų—xšØķ§Ŋĸ–8OVŨŦFÃÄĮlY~ ģî[ÂÏÕPsĒý*..…\htæúÕÏ>5,n#ģ6rv OA1ė ‡(]ŅĶķT Š ^Sņ‚M QøRč1äŪâxųGŽĮ2Ų5ĩzÍ~2ûĢ-ėCĒY˜ÝĐbTߟƒîĩ€ÓŸŧÏēo(į;…!Ë/ÂG _ÐÄŊÝŊė)īĖ—TĩČOšmmmázss?œâ'S8a°€6œô@Cād!l-=ЂH Û–ø6A˜#‹Ãr–HkfŌÖZģĢ6jV4pĒI‰ā3Šņ#'j…â›ČåGr é~Ðú@~„^>ŠG?{ËüĀ mé(€ųüÐOØ]ӏ"ÛĻÕË™Ėðƒâmžč@~xöÍę–mŧūm°5ųar.?Ųņ4~$Mģ-ü1 —ķ <”ö"äU ?ųûsx~˜Žņ#ŲýÉåÆ[óCÉå'züðh%"PđüZ.?o―?@h…H[ĨR)•J/J/ðŽVVaŽ,Ŋ†”ņOĐĐá)­”ĘårĨ\‡xP‚l Āā‡ãŊ °3üĀ€€I†“8Oå‡~Úä‡ <8Į&ĶÆäüð1ŪtÎþðžđüĀÐø}wÚþ@rųAB––H2œŪũÛņīø!r†ö°üÐāEhŲKôvüĀ)ü@þ?Ðmsĸ–ûûc~~žFV4OnčÞūā‘›~‰Rį>jųš“°š“Ģķ2UnÂÝŧwóĢšž+~āųIņĢA‰_įG<ĸ=~hgMĸ}qķĘėVø§@ýų!Âó‹Čĸ˜ŸąSĮ4€@Ä.ø·ú#;"Čo­ˆvXÖ, @ĩŠ@ĩŠP-ŋN<öÎ<(Škûã§·Ų˜qXe1â†ĢcLōbÔ$2€,*A &MŒŠŅ$ŠKÄļDD\"‹ŠˆâŪĻ[\ƒ ę  ģē Ėôôŧ=hâ{ŋōUŠ^ý^%•þPýGŸûýž{ŠĐ:ÕšęþÉÁ0aØņø|€O`ĸ/›Ûvįāāø/hųóÂĄŅéôÍŠ†{UčœĀŠĶfæ9*UuÎ֔?l—ŨĐuHôŅh4Z­æ9K:NÓĒŪŦđ‡NīŦū_§jRĢÄ546žŋÎÁÁ 8tõÕ―ÉãüzJœœ]œĨ}ÞÚĩŪÕĻûũvĒ5•eĢý<í{zĪllnų?u-͏l‹xĢ·ÔÞĄ§[/'ą―Ðm…7ôFCË_ussƒRŅ jzÎ:7@āˆÎFųœ™‰ę!0âÃØčWúõXh+ƒ–pūÐN"‘ˆíDށ m1ÓfŽûŽˆXXHØÓ„/ī%B_HY*Jö%Ė™sIaņĐg7æóð›%YaQŸė/Qžø~üįsc"ßöčŒ4ƒr‘Ø–ĮŽ"ðn Ű㠂īģCKb>E Yw<R’Á#IĘ&SÝqÛO ’°Zd$ÁNRBĄ€" ž€­Y$äað‚âŲŲÔ(Ųģb’x"ō)$&ĒÆōcI_ÎÛ'WKšíl Čʂ6€ƒãÖ m‡æqĨÉėúԈÍËW}·>Ģ o݈~šÁé6uΚø   éģÝTīãķv‹Ų@F'h“rorĒ,(hjėŠŌZ’$0œ„N݉Žul02 @+^‹SÖ7ķĻŅ WÝÂÁÁÍjƒfðWC*X“MŅÆ}ÉóŋÝQĪŌjLKvO ^U€Éëī8† œåý: ƒûeį§Ę&ŽÝ_  ~ØüĻQÓŌ û$b°—Ôl!Ī<Į(^·…ĶáY0œ mk8ö{”Ąŧ”ĩā~!úū6ÆÓÃŪ­―TĄąX‰nĄÓã‡öņōękû_NŸ<ØÛÛÕÕ˜ŪîVÚ*vũ™úqˆ—ũ€q1,†6mģķŲ„ ?‘`|õô4w˜UęVģšy.oÏN―9ĀÕÃŲQ―mzúą0æûÏ"§.ÜŽ4ZLæōf#ƒmáõŒY27hė1û@īĨíđ0Ā04!t+›&…‡kÆūûž,5įį.†Āđ 7Ŧå° ŽũóÅŠÔŽä8sõýúNÛŊĖíåI˒7gįüåę•ïBuYŽĀ‚Ą‹Ī,f<}’ūߘđŧøņDŒfŠz ņ…ė§Ø^ŠŌÛô|ŸÂ~ۓÁ<ž|}yĖíüž"9ÆŠ„ĄHÄ'1šķ0Ž…čēbčĖFsgÐv<›Į·Ðfôƒ$ féė>ԙgūÉŀA:’Āt Ų­Bw ÔwPæ6s§•ąP8Î0ČHâ3JÔíFa`ådÔüo6Ĩdž-đ\˜ĩޕgͭŠ `žŠŧaØþzE(ā1˜`üž”ŸÎ^óšâæÅ_Ž8'W°ųĸÖpp­–#LÍ·ķĪfæįŸB‡ēĘo=D1ąXčõÂP'ļ[VvíNM'mšwįú Åooĩ4ęēCߌčPũāQiđÂbnŋ§ėŨĮũ>―āčÆ-›3ē._.Ę+žÔJ“RRˆ<Įrs/–Wwþôݓvð ëg‹•'Ð4mßkāĻW^ĶóŌđFoÕ7UU^—š0VŒ,ĸfĩš;Mp13ë„ünG›áüޜZĢxüīøIC1ŦđĢĩƒŪÕr_ X[ë–,ž7-2,8drōÎS/―ũŅō' 1)wįcių’YŅÁÁ!ŅąŸœ―Õ@8Ãāá."iįAÁŧ3ŨøÛk—΍…D͜sôŠēāäė­Ŧ†ũŽNZ;yrøŒõųtwޏÝËþxÆŌ…ßĨãĀÂXMŒč―ĻČ5sû‰š6-‰ ›ēhe†ĒžöulՉĖ0ŲĪ9+ŽŠ\˜ēnšnåc8†1@Û_­ Hø"… "ų`zHĖ6ĐĀ-zíö‹§DÎ9§r™ątÍ;Ã=ûÎJ_1Lq&>L65>ŅexhFچ@O~—…œūíe–Í ÆXx.S>[õuĖ„ۗ…O‹Y˜tČHÛ ï~áežˆqĢŧĖÆFrXģ3ne–ēÍ\’›ÉÚĒTHc,zËßÃjĄƒƒûáïFÝĪž!/―PrÉyáJi­ĒIŊÓi4Z―^WĸOöÍ^ĮM â]Ī”)Ē4‘RĪą”.ČĨ9é€l#ŒÍė#` ˜ŊåCéŽbe%}īŋjX†™ĸ6Ð,>Õ ‘eÍÜŲiV€•ūį:nPÔ ā]ßDGWŨ‰hĶåÄi Þ Ï†û-!ŠĒč;?Ž8gĄŧUUuk{7ŋ‚%Ļ_ž*?~{øú(ëŲ;õÃKjēĒj++>ģŪå urðž7É+°*:PęóŠ:ð) *ô]šþžxûæÃ§/ŠgčęŌĪcÃ8XāXD’‰ĶOYŨ·`(Ԑ:û É9Į9(ĨQšąŅÝÖyē1–’"éëëĮÕó)+8fæĄāĮũ―NTÃ> ó$ĄoŽī§'imÚÐ\Wâ‚| đ0`~cäšü[ĸÅ00]›ÛpŊ|Ú>É&ÉģLô4žï’åâýëWï>>žī?ŧ›Ý°;ģŨ+ģ.Ė6ũóÄĖ{ņŠüoŸŒ2þÅÞŽFSqžgššgv'n ‡/—Ü?ČĻ/Ēød"Þs ((>E âŲÓ"Idw§ŧĮßtŧnð FXĄþŧÕ=UĸŠéKÉ··77·ß<þú_aÎd2ԚLë—ßŅËÚ8k2ԚL&“ÉPk2™LĸK‰3ĪĶir&“éTõOĸ\ūüÝũyßũ"2ՅÔgKÝŠ+ Ņ_ØÔā0ք}ēšïïã}atĐĄîŋęĻÕZį'Ō@Očpā}|”ðúYÐŦPįƒîGÎuĄ›{~cJÛÍæð‹cņ§Į[“É ëRĘã8ūj‹ÅŨ_}ųãݝˆ—KÎmÛæœž^§ÉąôޗŠđĶĀ•RB)įRēīŌī͘Fßzpá}Ëd˜Đx!}NNImŅn·ÃiŠû$+wtGÛåœŲĄ#KŲÔXsHĶ#ÃHSÉxģĐÎę@[œI`éóNDX0ndâĀ@ÜĀŦ­ËT‚í›į”œö'€3oäS[а›ļ]QÏtÔ({ü\ÚĻ:ģš0y~yþø“O?ûü‹Ífó'gųuĨ‡‡Ÿį™M&Ó))į|}ýaĩúhÓŋE­ũþ§ûûūĸn8@ ˆķ3eŠ #XÓbˆøq·]ŨÃ,@CvŒ#˜ Č4•YpŌ$"8ïķ^!#M ĩÚ4‘І1vð$īĨÄ YɘFĘģƒ+ÝÜÚÁ\ÚĮ.Ķ1qOAÄÜ―ĪJjBe‰ž)„TóŌ øū96Šïð˜"R9ū6·l’*Uá"Ž2ßd[įiĒjÕnJðu―ŠŌÏŅÎģ1§™YFXgėų@*k0ŦÞ"Ï9~ö=OGV–ŸSõFŪÕĘNĶ{ß5 ÚŽS?” V%ĩ>LVK Ā>›$ ņsČŠÍĘNØÝdh­ôGØZ ėû&ĐdūP%čãÎŲ{+)„;:ÏFķĻ*­ãN`5 !%Ķ+ZË d7}+#Î؟ĄY&ųū-€ïBĐáïHxRíoÔĢ'ÕVģÚh€ÐĐnÆåK ]ļ{Ōe™îffŊėÍŠĪēÖ2d†â§\%Y(RkԜ`p°ĐŪëē"—ZķÁĨTŨķjjēgŋ!oˆĖĻAJ?MZ‘1ŊfÖ7s­WƒÝ37Ģ2*ŲyüĻ ĐĐķ'›—€ĪûIdáCTüZkN.)Ķk°Ā›B#22T™•=N\Š*5Ÿ§Œã͔5Ŧr‚-ÏxjŋQŦEÛÍļ›™}ĘšÕAęŦýKJĻ\kĨ‡Ÿ†SÆ[$1íŨ5+Ќ4ęŌå~üœïz–ŧŸ‘Ėûū3c]W‰L5õZ—”ā§ņšQ[ÝØu™ 9ˆv™üT šöf–%éý`XĄ€’ŅŲÖTŧ |*sĒk[ķŪËP%P*'™J%‰q@”J Ü! ĩ_p*ųęÚÃūïQcþŽiĩÏ›){ŸŠTPĪ*SĐōčŅ/ÔÃj%%Û #ĢŠÆ€'NN9tü·DöÞY5–TuŠēm™S}õȂ<ám|čý…ČJÚÞzŲ59NÝSĩŒÏ <"T1m1ūôˆXkEdVŠĐĐöKJ3…jŨz<\ÕėDTÕOxwIQ%&kũÍô·"|š#aĀÚVû_Ũ*Đpũt"ŋūþpÏ6ņÓ'SÕÔ<ÜLŨĩ<âė%ɒG~ĄVKąuãT,3ÝÏLī*žJæßm)é Ų=L†=įbk•ĪïPęZëô ÖĩÎņŠšŪ‹‚{o[‹Šá'2–v†yŲäwk7XĨĘũé_|‘,T­îųâ-ðp˜.Uöä j:ŽLŌĶ ‘RËĖÛÂŊĩDéûd‰*ĨŠ}{0qĪĪЎïO”þšUwđ2#ĻüēuÞ' ģ5{„‚čM9…ōøÎŽžĀ­ýNņųYo‡˜þ™HFXËĻĻ*kī=ŋu5áYÞĨšû―ŋA^ŊŨqũÖžĶ,œUG~ŸV•ëZ=ôŋA\ëjŸ *T5&ÏķËųÞaŧĖ>ÛÃgLÝũZfá!‰Ę|Į@‘˰e%•™"XŨ+ģöđļ^_ū=3@BéÝūĒj›ZÆāÎö/3.DeNßŦÓąĻšˆxƒÛķ_ĐęĻUefŠ* ~"""Žwü4$]Ĩ:é\ —' €™6h.[—TEļ4Cð~§S"ūïï)Ĩ•Č4‹ģJ ģĶ:ī"8Žķē”\ϗxýN=zX­’R‘2ģ̇UĨjY9Áڈ3Ë ė―Ŧá#~ęý_ŨũCē(ËÖũū!TÓ>ŲĨĪ*!,IŠ.ĩã{|֖…G‰dĨBōū^ējւđlß;šB@rPĀČ ?dœâqL§M2sŽø2J*ãŊË^ Ú[АĸBTōŨû˜G üý!s•ĸî,RGó„ýŧĸ›ņyƒsšü·šŠŅïēÚÓëýÃ^―Y‰ę,§j“æf̟ä}ßĮG"<ÂČũEĻBgŧŽJĐU2ÜPU“”5ósaöiŽĩH­,ëÖū—Šjk5ɈOvpŠūÎŲΟ4Ĩšu˜·@˜iDTĪ™RuŸ™Ī3m>p2‹$1|VÚŋlû† ó'5ïs2Ģ $ïŽĨúã˙ &Ūų‰Đ:HášŪȘÏv֚EðwôKå?Øû)Šåï{9qųāČ9gœA@Qr$YE@ 3&Q”$YÉ9ĮËyw'tũ·]―œ§ūŋïųéãQž0ĖÎÎÎîÝÕTWýBúÕûũï?zęœIųoŊĻąïÛMŪđDKüŪA@u ý:ņy‰wO=)[7mđæB þo͐Bšã?ļe)ðþ0æ^7ĘŽ\Ïŋ–eÔßMΘ™ĮŋßtâBaî=ßl:ŸiýÎw‡ÓŽīĪkŨŪ^―v--+‹ú€üŸs%ÏÝ―eÓđtóŋkĘo.†ÅĐ™Ęĸ•’eÔmĐ'{Ũ†Í—rĻ{uËæíYÖo,˜ 3ßoŲ}4YUČĸŪãü9ãLwˆŽŠe™:*[“RĀE–iD„؆ý*Č` BųŽ3Ių—Đc,ZœˆĐš 5Ž)Ą]’V+ÛÄ EßĶ+h”ŲōåËÁW’_x ksF.  .ĀķųÅŊeˆ#āsŅ|ði{sä“hĀēŠG"ļå“ĩU,ЁīP`Ķ‘ˆĢHč.šÕ Xs0ų "HĪ3* y‰Yö}t á:}høøþŊšwï6dø ‡Ÿēãl–ĶŠĀvÎĮÓĮŊ:™uũ_TL++ųzZ6GļĀ“YÚõ+ĐđBÉĖÎ=ŋ`Â䓙\ýoŽ; qÜļ™”kˆ.Нt?ĖžŒkÉiŋK>ÆĸxƘåëöqEE<3õfFŪüÛĄr7ž=síwÉ:MgęĪm7Íß~w0fž”%/ôøÂ”iÆö}īũĪYŦŊĨÛĸ·Ēg,ŸüÂWr•ŧ?‘ģôÔΌ<#ĸRąŠĄôĢ#ŸŸũãųž?Ė}œY)7ŊįyØoîdbi˜quÅīÉŧR˜•|äĩg&ÛŋŸ’Ą†ģŋœ=bÚō―ŠĶâĸT Ŝ"%ļT@\ųŧa­-Ō“ĻāļĖ_rÐ/ûĄRÐKL@ÁE•c_3Ôöõ:.é°RėՖí]ž dFĐr+)šÃáđēIj™Ö-mr"ëDŅ€t)S*ČįZē@ģ KD:TóӟBÜdûÃŲ)ԝ?C“ēa@‡ãĒS TūxČTU5ŲēČũ†"^’JïdČ!}û@ĒĒ˜ `ÃRč Ék™ýϚoĻ>Ē;ȏ/3tč°æMę6l;gõaĪ;xÖÕE#û7iŅšÅÃ]ßŲxNq8O|1ïÅ)Ó&ūðėĩkó­:ėë'æŒíÔēMógĮ,đæBy—~xiō 3^ĸ@ƒÚ?ŋ$ÕÅ~Þ―þƒ5ë&<ÛkęŠï“n}ŪÃC­Ûīšēôv@ƒ„˜Yįßyú{.îÚuօ<|ųčÖOīmÕĶe·§Gî―œ•Ÿm5üîŦ―‡Ŋ8tūþå)ã'ū0úІĩšô\đó’C'{ŋœßáÁæ­|ėģÉîëŧ§/ú|þäONx/=åÂóOvkÕĶy뇆ūâR5‚Ī1’AŋālTĒx™ŌŠt0qÞČ'–ŋsÁÍ/þüõŽm[5o=áÝí&qX'>ŅĢY›6―ŸvÃt(4{õžqĩiÓĻũˆ]—rÅM~O0ÝģnqÏĶ­wėýÖÎóšŪŸĸyÓĀލ›ķhŲäĐWūûþ›YKūxmBĸ~ß7|c^…å$­ý~Ũ ‹gúnÆä Ó§iРæSĢVĶšFÓ>œ3Žu‹6t,•ßũņĘ/7Í{ôá…k~ļyâŧÞ·kÕĪQ§ ËSME%–·â˂ mĪš9ī_ë6ÍûO[–lbÐŸÍïXŊņ#Ïôũu*ō#ĸcÄ\ŒUŠ”ņ–LYYozwBÆ*]spMUåŠÞ†ŒEŒ/;K„€JĐßĘØ/•&9] šR)č%ÍÄd)1Đ€@@H–ä†qĻ}-iŒ(ÉlN§(œôšdđÄ4+Î/6!ĸBÁ !(^ԖŊũ JČ/y_ī/ ‡J<ŽĖđÔķåĨJóoę‡6·)WķĘoepŲáÅŠ_É@Μō§•Š) iĸÐėĮUM;?ąüģ­6ŅNũÎȗwŽ{ë‹į œ7õåtŨÐS|wäâFīvzމ“ÞōÐėéOõߛU歏>y{éÓ*ToÞŪuŧķÝgNy(ˆÛÐa‰ĩZ6ŪXúÉįĶ&ąėėëWÝ›þúžb'>þähvځũFŋy`ėGk_Ž—5qԊE%råÉŌ^|ēĸž o­þtՊįŠh7Ϝt6ŪĮš/?ŽnČsC_O3Q^Ō™/7ėlŊxúŲá‹R<>Qoœ9úóđë)Ŧiį^óųķÏū9žBÎĮë·1#}ނĨÅøųįoķ.äŒ,Ũ­fĐv―^˜5ī̆īįÆūüõúUÕņÚOŨbšþ/~mËÅ:GÕĻŠíė cGÎė:þģåÃ7ŋ1gˉĪËŧŨ/=éœôÖįKgN Óø‰Í‹'Ŋū°ð‹uƒK]?õKӉHīÏđŊOzŊĮ›ŦõŠž;eÁÍīsãFŽý)Ļû{ķjl·ęe*=R­äCO˜>ø!'â\–­vōá―‡ģsĒék>XĩĮŽūüõá'>ĸpũÉ ŨĩïæÏßú؄åŦÏ,„ãĘ>ÐūZŅ~Ŋ.ęŅē",2eŅûŸ/ŸâÜžę‡Ŧ.]U~Õ%ĀŪw&ú*ŧÖšĩŦÔ/ޘ―îž}uóĀIŸv}}Õ#ŸHˆ!ÜæĸSĐCQ`ę“Ŋķ,_Ŋėg,E„ `|§ +0aQū[ĶeIĸBHÖ ûDÁ…ÔĄ/u0Ā 92’eŽ “^a üâāH”Š4gy~GBUË —äËzĶiČéaƒR8§LVÍē―ÎäĐ%õ@ÁâŽoVéҐé•KB—,?o€Å̌™ĶĐŠ*č0PÃ4ĄœÛr&ņūÆHþuBsŪIĄô'C$Äb-š}īaã˜!Ŋîī`ížŦ™7Og]˜3āņŨ>چˆnÚ6ô‘ųĄm_ä\ŋ0―ï؟§ŅžŽž”}Ü|ė…įŦ&Æ-YŠPHĄØ˜ˆč˜ØÄÂąâpÄAŅÁEŠ•ˆ‹*Ä,S+Vaôä)u*•/YBWqÂėkį§=Ũ{îÖĩÜ,ÛWĀóœ›? ŧũ ÏW)]ĒLizýØá“éO>Ö%&"ķ{Ũ.!§ÍtŧlŦÝsš7Ž^§Ã€Ņ–ŠėN3mškYíŸßąnÕ―z&–=čøđŸoš:ïSĄpdõ6= ŋtüZ&sەšš7°mÅēU‚‚=یôýŪŽÔCŸ=ÝŦĸs)”Ëå‰jÐyþ‹jUŠEē]y…ІĮÆFÅŽŽß·WÝË_=Ų{Ā·'RuM~ø\Ņüã GGF*Q2>ũÂĄ‹Ū˜~=;EGÄ |Ē“ßąŸÎgˆdŲv§!/>ŅļfŲ QÁþķĄ†=ÕëŅ=ïūúÄā‰g2-- :ĄtÉÐȸĘPĸeý‡ {?6qÏÅ<“ert{Ã$Joß =XęcŽÍ䋇ož?ŋí­—ú[róĒ–á.\ŊeįŦúöœąþĻŠŲ‡ŋû"óęÉĄ―[ö­ĮūžåRuåčŪ/3Ŋ]_4ęéņ+N{ŪóÓ'~žrÜō€'Ę$ƖĐX:"ēp\Đ⡑q‰ąaŋF h‚äōÄ5ë>Â5ËW‹ŒäYđY~ņ{v)īhü“Ģg|Ky@dLLIĸļ"%ĢÂBÖõ7g ~rú›g°ęOnC4ЌŸū;qéðg}z üîjzęĪcÛÖ8JĩęÖĪBŅĘm{Õ)„ĸ­TË!qØšQPŲ ”>†V”h ÉÅ/ã’~*ZۜrC$ ‹H0Ėö9·]øĀĪ$”аad‰%p FCØp{D.VU*.įúõHNdd!^ŠZ™—ß~íģTý2EbŋÄķOŽ~sÕŠý—wĻĨ†—xý‹ĩoëšqÚØg-^>,ūÁˋ–}šîƒuïõũ3=TÜ5QTB -ĻôāisV}øîÞ]ģŠMÐÃr~šx™#ÂM1Š5mïĨf)ýęõ—-Ž ‡Æ-d[ĨĒū jäÓģÖ­}wbÎöYK>ڏœJNV^Ū !óōށýpéį^›0ļL(7=ƒ_ -$2°Z‹ĮÞXþΎã?-|ĶŪ#(Āļt--Ũ63ϟžœ‡”ĸ1°—Āļ2[’ũ…ē `ž`h.Tg  ōŽĒŠĸŚ8ZRN…‹Ē"’5—˜€_ q5ęYĄ/)3(ĨēĨĘ!Éđ‚æ‚ėübӀēZUxíHy0$™D:ß`ā&(š…Ī؆Ž*`žnõ‘1&ēꄖ+#·\p0’Q(Ô%Ņ’ö\NŪDYŽŋ$ÚQB2ĀŅ_uõ'ˆķPž‹YšJÄĩŲpŦ\Â\Ö třĸ4žŧRV/˜0tÜī93'}ąĸb›æĩë5yīc„gþ+óß}ũíŋX›Ë4jļ܆YķÕ5Š^Ÿ7eþ‡,{ՆôĀ"#zpûīAÃ&Lð‚ģY,>!~ëįo/Xųu.†°@ũ9  ]:gÖgß@šÂÜn@þ0Ó#NXū͓U‹\ņžðƒũ—ŊzoKķœa1›úÖū͖ÉęĮž~]/ņPũFïū1qÖŦ3‡OŊTŸÞĨBœÔðPéíl|g9·\Øf…莕‹įŸ>{øĻŲQõ;W*žø`ëGÎ~ąx܌W‡œQēGӒ†éĘs{8<͕gåéÏuícoýpÁ’·Þzsþ–Ģãn—åļĮM=6U‚â*ÓÖ­˜ũҚŸwžũæËŊŊØ{*ĐhBáP?•qy&\(:þäþoĶÏz?#Ī|įŽ…Wž6ņĨWg {mgų~―K‡jŒ#ĖãTŒ8ĩmHÆôļ,ę`™+ߜŋā/.dōÂņņNÍQĄjĨCëߜģxŧâÐŌNė}ïí7NžļūïÛ-gŪfQ͘âírÃíĒ\ą’Ôéáݗ ‡Ķäãa=)§WΝ8á…ŅÏõzâ;ģÔžĐO'ŪōHŊęW,zëÝũ–,Xu2%ûÚÁÍģĶŋöí'üÂbœUzēdČąŨæ,ûā―7ßĸx‡Đ(Üíē ĢHÝ:—LûáGo/^ī:/Ēr·žõ^{qø˜I/Ž~éýKđZ•jŽ]6ûÝŋÕsGÄíá6ÔÏ<ņaŠ,ėv‹*)óâũsĶOýôÛïÝZĄÄ˜p= 4ÂÁ–N|aĮéĖ°ÐˆŸvŊë“oļ˜ōÎWÛR=Üōļl?Ū<›DõßĮĩwŲ‚åï――lŲ{ÛN%ÖéZŪОį;ić/š†îÅPƌƒn ]Ũ7mÜxöôi?§cTęS1é+–Ē@$ģĐĶÃŽ#éV t/‘[tåC‰Á|üÉ9ϟ#I%C° G (Ū[@*Sn5|–֐”A&{pģĪ!yi°ģTM•Đ_Údm‰†ÆēĀ”zcÔpŲ€wåAËØf1€æƒa‰VŽČ§JpæpčÐÐ@$xd)-—Ũ úđp%\W•RžĒIŨĖ8>ŪĘ ģ ―ŽdĨ/ŪY~ĪUĻXĐqÓf–iæþ2225M ýCÏ8Ēę~~NÛíázx›Gûwi\ÃÓčáÆØƒÔ€Ē%ʔ,V4ąt•JĨŠFDíÐĒa1LâWŽd…’‰1åš5ŦW+Ö2•č"e*”-^Žl…’ŅAš3ŽTéÂR“q­RÝڅ$8"ūD‰bå+W/U<ÁĄĐ…KWŦTš˜ũ„[6'ô/VŠBéâQ*‚LLq™æÍëֈą-5&ąt… ĨŦÕx D„ÃåF՚wßŧĨÆXxlą ʄų‡Ä­SģRœ·O_ŧvē Gvl`áÅëVHŒ.ŨdÄĀnĄ Š,Sđi•čÜOtŲĶCGũw2ĸ˜UŠTփbŠÖ­[&1šxÝúÍZ•4]f@ht…ōâ⠗-_ąx\„Ķû•ĐZĢzĨbAÕŦŨŠðð-^ądĻËíņ/Zi`ĸĮĘGųŲTj&̰øŠG+\/YĄBģFM‹F8ÜÜīkŋŅÝč2-Wē\åŠCýžUŠV tŖŠRđJ|T8·Ü†Į.U―uŋnm‚U_ēJ‘°PŪÆīéÔūxdĻVäąáЇĮ‹ŧzųR !AEĘT+]žH€HŅēu˖.â§ųpÖXs+]16"","šBý–Ģ' ŊįgZJ―ÍŠ ÉöXQq%*T,ātįåĒĀĻG hV͐Rīc‹zĖ“ôĀežũĐØ’åŦ–)•čÝĶEģX§íâzBbŲŌeJ4iÔĪiąЅK•+WšHŲōÕBC°UūBŒ€@Zūb…ēÅ …E—ŊPąXlļæ(W―fÕōÅ‚Ėp™,Ļaįū›WQ°ģ\ÝAftąj­Úķ `1eéÕ2,0ĪDŅbe+Ö(UīppPXųę5‹$Äė­ŨĻF—+Ïû*UĄ\Éš>P?ŲÅę<<āņîMkVŠ õûĮōÔ !ééé!Ŋ9ųĸIҧĨĨĄÛ" `ėĻŋŲ žQ۔˂Eąhŧ‚=ļčHï[ęVB+€ë‚Ū4Vī-Ms°[ې™mZ2#ÐīÕ@âVRė d &ŠÚWHb ÛÐĩðɍ Ī-ˆ%b°&à}mTAv°™ÍDâÃN@ 9ä}YuŠk†1k@K"ī0Qå!*ĨķLvr€&LTŸf.$s,­ÉāMÔãK}Aló”0Ð/–C–ëÐîՒÄÃðtéÖíÅĐ3rss‘ŅÜ8wî‚7-šø/˜“cMŨ,OĖĨ*đhqËâˆ3Ã0U݉ĻaQNTí֘‚‹ËÞ4Fū'bÅ!nÔ4,Ž|!ŸÂĻИԝšmxĮšÃ,Ãb·݊_Ÿ™!âtčē-.ČÜB„H'\ –@†Ķ‰æP1"æGs& n6õéķ.fš6…Ä+*p6[člp"z7˰lLT‡NLqWÊîāTj™6Į: o'§FáYðĐÎ,КüĀéP~@ƒ^čļ{āÓphō3ä‚Öý °Đü "ęq=‚] gķMCŧˆŠëŠ0o2‘&6l™&V4,z1Hs8˜åĄœ8âãÍg @ēu(Č–iPÆ}Ÿ­C'·v2Ž84Á›1LQë(ī”] A―'að\,:^*†C ÉąxEųOáþGäĢķáąų/Ÿ*…Y XüÁS4jš6‰šĐ0+?öXWÍ6EüØ1UŨ™čc(šCĩ<—?ąÄũí4l*ŋ/ˆ‰Ï 1˰č?؜üĖ™ģœó"ELÓúĪÚqĢG~õå:BnÕģXrUEßV|ϊjQ q„!Ģ0€ßfe”Ér.@ý8 ÍTÓ2Eį#đį€1Ģy_ž Č[íWYHÂ,MáLVÖ ï‹Ðā61b>ýCŸŽĒpþ`2J"ĨА—•e)MÃīĻ7h~Q1(Ģâę1’h‰vÛ0ļcŠĶɞ€‚‰ĒHđ^&{ÄuRŪJ;Hų ˆĖO$n<€ũÕąfZĶlF빖Óå‘ŪSgĖšKŠý_ĒžēôŨÜZHLD0ūŊÁs?î…T{ũ^­Ėˆû(Šē?IDŅ€%gž^äXIæ/ÄąōÞrķä1 ‰fgœŠRTó†j›ĩĐ\°Ëq‚1ģĐ(Lôŋ k `ņŪĀäFÔ§ Q9ð\amNLS€ãeZį Xd ąĨ8ƒ*^N.ƁÚK™h‡REAĀõ‹Ö đļÄHø$z)å*ÜLÛR ÝëŠÝ™KĄÎ9žKŸÐŒH RgFĀ“ĀΰĀßCûŒ!ļh7øĐŽƒî‡Ôü ML(ôËģãĸ3„'ūŧā|ýĮâ~Ü7'—Þ·Šf‚›€ī.Į°Ą\eLĪ!ŲūĪ”_'*āĒZ $˜ãCŸ’ėJ0S †% €XbCž5 ?0-Ãmx€–ÅÜn·Į0„.ĩÝŪ<ĮMS’įrQfŒ ð Ë ”•ĮcĶ„^y·e›Āķ,—ĮmˆU$óļÝ ėÃxyƒĸÛÅ\ĖņlðŨŊX J_tUjãJüƒP{PäpcĘđ4°A>…øDģDÔé’Hf3‹Š%‘8ĀÖa‰Å’^МAð**!bĸ=R<áE n?ÚSĸ§<‹,wnzF.Eø_æQ~—ˆžtjį–g•œÚķyûéԂOC[+ïÆæ[3LüŸ™“ߏû"Š !€ŧZĒ7‹|Ĩ(§6ũCp—‘ýYĀW.ØĄæ]“2b\ŦsĐ>KÄČČCҜ~Ņ11ŌNFō‡DvËNQ’#p ļ‘ Ōē-y ûšĀū&ĐÅĀð3B:'"āÝzܞôÔdfS.•ÎÅĖg Îd!,-#DMÁXLJŧ˜SęŦâm†āUЎ‘Ý1$UĻ–fXBĄÆét2qR ĩ2 ÛRĪ‚!bÄĪ Ï=WžĶÝž†ýÂÃuv‡ú”SÏõé~…bƒ…üïŋģĶSNėúô―ïÍáEbƒōŧeeOvZĶIĒ#B~ó’XŅ.ė^4}}ûķÍËí^0u}û9-ËGQæĢ;f§Ý0qPĄ°OÆĐų“§ÍØÐ"Æ(8ĸf܏ûĐV…\f[LÓĐ/#ĻPpˆ|Gˆ$2Ļr9šŪ ī,M9˜ÓaFm*åļd6‘tą `§āāāΏtóŽ’―MaÆčÜwðþ ÁaÎô‡ĐŠzöėŲŨæĖūqýŠŠkRßZŌ+ĖBXĖ^ W+ōūŪčr(Šô[^7Ēđ ÜĒÝ)b?8 ΆEN‘6 ·4\‰n ļ͜žI·1Ųũ @cûČņ‰É†Å8‚‰ˆÂĻ AO‘)šÞ–Įīp@€Ÿā\K§ÎdŽ;ļs߇)†iˆ(ZCîģ-ҌÖį’ęï1-EØ$8DÜŅD߆ˆ~5ÍI[4hļ9dΌ†ąâ6ʙhlÁ‡™/sāÎ<5qĜÞóV5Ž`=ĖĐaXX\ī|ŅßýŪûķ0&\Ė”$œÅVlØ-–a›ņ_=„Ģ>JAŪ9đéÕY_ų/[11pŌ–mƒö§@đ*Ž0-Æ!ä—Ļw(ųļWĒá /ŒþķŲ°·šUÅD q:{ W †Ï>. °[vũĒÄ°ęÐUË2(ÃÞĮømĨúýļŸjÞ@j_‰55Gū–+,žĨé ` §LhĶI|.ßķŽ4Ą‹)S*,Ĩa?Ų=ģAŽ ”a˖+ïÁ>|ĻS§Nâĸb”†ŸŸ_›ví?úā]j 9 °?uŦO^VÞHč—"]ŅdóŽ‚ #`đDuŊĐ O”č 1p•Bf"ÏJĐ3q„t~_Uh6r’dé&ûĒŅōïHÕēĖŦoϝķdÓ?5fø+ Z–HžøÂÔýįŌ5Rã…yƒÚTIüúWūđäw姍ŌĖ>ƒ§}ēlÝŲ”fƒ§ŋúôƒ7~XũúëkHÎÍ-Ūn5`åÔ§ŽŦoŋ>å―oN2Uytä+Ū{}ï npF]]ŋõF™#ޜø˜’}~ÅŽií=é(ĸāÜ)#*…æž?cܙ€RķŽKΉ›úÆōōYkÞüzŦãDŸ ĢuŠüöī‰ï8Wlڂ•UÂđŰĶ_ΛúŲĶ-{z š=kbË ø­y“>Øxškj‘Ŋ<ÛŪ&†Š_SØî/ÞûōpČôĐÝrŽŧäƒŊ; Ÿëâ‹EîŅÕã_mKiöÜKӞn‘uųįmûÍâU+&ïþâÕß;Ôc^@gģþ/O{ŠYîų-ÃFÏš’j”ė4õ­gĒf-XýõWĘĖpðƒ™Kū>hŧCžžüęģˋÜĘ|ˆ}Š|A4GÆŅuK6n<šåܘģO>ŨĢǚ›þÁË}~ØvUzâÝ9CâõėĖ\ņÅÞŒĻŠ/ϚŅēLĀ7sGmũD^<ļõÐQĮčéOn[ý΁ÃGúN^=Ī{-fĸŠūũS-֓*۔·ä‹ļH"8 €€kfŽĀþ ú— ÔŠ€§ MĪČÎ*˜ˆĒļXņ•+Wžkß[RgŠ*þÓ"ŠÕ^·|öž5W^ĸðà aëÖÜÁýODvøüË[į2tL…o?Í9ûÃęU9 Vūâųiá !ƒĶ-y{JĘæđŸžÚŊs'ïÝēđÃëËފĖþÔÔOšÔ.|eųK_›ëŨŽËþá­§ĶūPūôšŌfúöõëÎ^öFÝóSæŋs|Ā#ÉĖ\vHĸvÝ'‹vŸōZųÏ_izrßgïļ:/ŸóÚŪ—ž[šð›æĩxļę›C^~š}…äÝïŋöņąY[ūljÛþ>u ÛVëuëÛ`ãÍî3GĩŽōÕÂaģŋå_­ų2mįēg͌/Süã僠Ä“Ūžúų\”ølóēNž:CąÓ•üõg_ķ›ųÚĒÚĮĮ|žædŨfJÚõcgs(ņ“īáģ/ÛÏzmaícc?þütįÖŋs=Ļú;+'éđyķPŸFÎzâæÏTÖ;%9åâųsðžqÍ § *ūÕ―ĪHBĪ͌.XžĒ}Ž@ˆĩ!Lĸ0&Tŋ tWĪeŽïŪÃĪĪĪ\ŋÃŲ|T•2ΠĮčŸQDvgę<û\Ŧę%9å4}ßîVų%ýâÃ#zīÏ'ßNúélŠĐ;›<5üŅ6uŋ―öu­ƒ‡=ŌæÄĶ‹‰ä2B(Ïí.RŦé“=ŒFŲu{į‡#{/üļŋa—y• ĄāNÞÚðóņKąĄf‘šMĮ>ÓÁïČúÄby2íސv2ōÉĮú%_tqdÖSX™~Ģf›Úû‘î―GĖč[86Ą\ŋïE‹Į—ÏîžôĘR–~ƒá$|–·‡ŸúŠ)_ĪHLxÚyÓŋlĨqßŦD.–(Ū)ØþųŧĩYWR―šî4‹úįšf$ļÓÐ)ÝëUßsŠBuÚėĐŪõ/î~HýâgÉđTļ„üÉļũĮb0tʗ4žĨð‚MČM ,€mHI 7č›|HkB°W Ŧ†aPŠ`ĘÂķ|3‘q" EzŧīĄÄÄÄ 6Lœ81<<œĸ5PJ诊(_ū|```HhHūũŒęÃĖ" Η!b؂9ĘN'ÐGME•\ččĻČʗˆûÛž%Bæ“Ó}ė2éÜ#ō,b`Ÿƒ…‹Ôũú·ô9=Ôp騅,…ŦĖœÁĶš‘†PBú•Ë)ŲŽˆ0ĸ,ĖŨËĀfÖĻč­[Æ-„SŪÛãēēÓŪ_ēŠ=X:ėFäÁã'j`e'_ĖČN ö‡‡[)‚åtÅ/,šL\ųz ߨ`ž—­bė'ڛ–7ÖēssórÓÅ‹ĻĄ‰Ã—ūĸčÏ_=ÖsÂä"e—=RÚãąEak™đ› š ˜čČĢâEëŲ7/ef ô# 0F›))Y)g'vï=uNņSEV039"ķE˜FÃ} ]Ģü‡8ĨQeš}úEí=ŸÏ0apýŧ 2=v Fčڏïŋ°h͛~‘ējØī īÅŲïƒq8ó˜îäd·ls,Ô2,jRĘŪF._úåÅÓâœ:&Š?3FbŪ ú søīáGe§^ËâAqūŸóûq?ÕBúdRhFĘqAē°D8ðŽ Ó”R0FbŠ"ëYHŊ:4mЁ ]æ#‰ų$Il\|bŅĒ–e*TČétÞžyóƍčŊ JĐjðx<ۜėĪŨ$aŨûšāī`šŽ˜™č`āë ΒO•FV/‘FAŪWÆŪĨ@@ņËō}Ėôē9â†iŠãĀ'Bô ˆĀHČtޗęŸQāíÚôøôņņŸO-Ī7xĪŨ3CÎ\4bÆõfG6­)ņPÏj1~'<ØM-Đéër[UķÛà ôó;đwĮðá/VķOïļĩ°YíĀ"ŽžīrÔītŨÉí'Ģ›LĐSĖÞéĶ 8Ā™íņļ(ņkÞįɆū1w_áQīQïŪåLwą($Pę2˜ĒƄ,š4ŪuŲéĨŒÓo.Ý’@Ԑ„ŌQÁ·ĶC\wF8ēæL™õBĸ–õX3íí1ÓSģ}w&ŪŲ”š‰Čē!ŊЉe*đÞ\8bðāĪMëÓ •# 1ÛĪ ›ŨcˆjŅķ˜ā2!^ā!·Į ˆÝđúíïnæ^L,Ž"ŋōuÉ#gNĸĒS%h\^óîÏķÓĐ'Ū}ĸóÍ`ÕéöP‘" —aģ·iĩH™’›_šú^õYM îv3. W.ÃĒ•Ûũ+ũņ˜y/ŋQđ˜ŨËÞŅ6\n“B‹ŸŧAĻžQÛåæ Akfũ{?―qNWÛĮŽŧũŦZJ5UƒÕ1„"ōŽHĐDđĨŸ-Šķī(ÍW2Ä0)’kp)L/S’T{‘ų—€L”·K›•)BJĒŋ&d){ĩaaa^g‰ŦéüiĪĒ€åræÐtūĨņ8QˆāķQA.Ė]Ą&šđ\“čé, ķ˜‹šĶëŲM‚}aYXŠĐsŠą#ésú "­ĸyEÓNhŌåoĒķė=ÏH WŦĖSŊG•ÝtðdrįÓ[4o–}&Uc1ĶiŨlóøË5jš…Ŧ?4iVƒpd_ðeʖl]ŧīf–~ýųæ5âüqlŊU ĘlßwT-ųėð͊ōŒjí'ÎjJMœXwø„ļø`Y§Į§ŦĘnØuÐÆ~%KVÔāĮ'," ĄĶEÚöŸQÝ*„Lĩũô•…wîu"[ún^3ŽW=ÕĪJ”iR‰SCJN_úÆî—CU›?đŠPđûŽĐŒjÞ,1[ė,›WlÜó ‡Ŋļk=?ˆeeÆ8mĨN§%h eââ &Œ(WĖ߯u: /ÉLģx―.ÃK3ņP‰GT(‚bUŦ^Ķ6zhŰVqšÉ+tXēČïįK~ņ5š/]sð\nÅÅ]Û9ĶØÅ› SÁÏ4íƒ^ŦZ8Ū"ĻMč;{i–øŠ4iþÜ"~ãŅOM\RXÓÂęļjųW›wįX(ūhIÅd-ŋ^;ī„išå[˜ý@ĻešŅZ/œÝX3ÍF―ÆÃÅ)œú~üŊÄ݉đĢF ÛļákÝé”ā|ĻÔļN Âr Ē‚‚kÚ>­YįĘnŊtę:°‚Ý€˜čÏ·Į›ōĶÏz9*:ú… Ÿ|ōÉĐS§öč/KĩRWĖÛ:ˆ‰Ž:~äe™ēelč>jŒßô1nÃub"™ ĨAšĻm}jØqŌ―\ljmļ+ ø&Ŧ˜čĒ4˜Ķę§ ė2[þœÜœÎ]šNŸųŌŋCĖUu§Īĸ N9ÚĶō q^úÐBÄ@=c$á"É=īyÕėU|°ĻB́·<v0y°ð/M —E! âDŠ8&Ótē ^KMÉÄũÉčš}0Ė_UsŠæP ‰~óĒč69I–ą-C(/@KŪGðîóũ`ũŊBDœ8_îā–€ĩ,JT]Á’Ŧ/l{Îô†IÝ!ŪŧĀ5`Ā―10 ŌtÍ2$‘ßÁle>™TذōŸîÝB T5hrŠüÏsüïĮ=ρ0jøÐoūþĘéôS5UÔģTԀŠ‚AqÅVĀXTļ΀‰7·a§*vÂÉéKŋākë3C8_ xÉRģįÎK,’ļoßū_|ҧøõŨ\’Āt%NČÎHOš~MÕ4Ë6Ąë†t‡æņŌņHQEØnRŒzÃápږ Š ĘUŅSQ4 t h(ˆj õU]wØÐí%SđlyŦâ?Ō…,ę–ĀŽĢ7ÏåęŌĩÛÝRí_cWvrRš™Pްvöų—ÅýļŸjÕŧīj9‚ŠŋÄĻÉ9bgBŅ+Hɂ3䐙DNu`‚ qMĘR&Ä9å°Ũʔ)ęņxNŸ>íýӛÜĸš’6ßNœZ–;/c!}‹%?X# ‰ eĐPo„âĀō45ĒbXĀĨY:Žp V‰B ĪĐš4ã”+•B耱7m„Î ―%P‹ĄiËaÄBeCpÎýƒĢK„âŋ‡ųt?îĮ} įb†cIï"ē’I FđŪj Ô"ð͚€š 9I$ģω€›€K9i‹ä„ƒĘ"WSĶl9/U,)éĶ—Áåt: !iĢVĻz+ŠÔ–Ĩî1 5RÛ[2ņ…=#g:čFĘ*[ļ+‰ĩ6M[CšH#.ŧâĀXŨ}ė2ÄphāÔ]ð7îŋðDôÆEc>/H#%ĸßĀNFũã~܏ŋ‡Â ‚üŠ/E•.Ē+Š "ŒInRTK•ˆQĶXƒƒ(‹eš€*―NAB…†ŊÓéį…Ó2FsrrŪ_ŋ.ížÐ_b%ÁÁAyđyBÄËéā‹~)‡<ëT`§ŠË™˜ MƑmĀ–%Ίāü`'Øû‘ǁ ;—ä/Ä*Dˆ(R”iâUD;XN°ÕEB)Œšýų_Œûq?ÕB†RĀ.AŽ”Ąs jĒDĩĻ%2ĒJ >–XZ€Ę6Aý ސÔÖR,š Ķ4a·mÓÄâÅžô0·Û•••šš*éaU ”–šŠ p„( [Aß@ LĪ―‡H—øēYĶE……,š:'€ –Å;C\vsP{,8‚-Ã@Bß@ƒ ĄļE‰ĖN9ĩ-`/ô q•ˆķ…e›ŌËݏûņ?ũS-€cEJŒ" ƒ€†7“žUaæB3DS„uđ4 “âĄB;Ë\&å*q<ÁĪ|ų !!!”Ō#GŽH‡ðßWqD ~ð“„ģīš"€RĸōŽ”Žõósž:~ YHC FíD*%č<4 „8ZZûHīžôĢdų9TāšĻĶIq/KŠŲ"Æ-ÃôI,rãQˆŪˆK”ūÎf‹û–Tīđ_Öþ/ÆýļŸjeŽÝI‘ }f\RR , pQ čuaųE-“!ą(†UģMF@YqEýƒB‚cbb5M3 ãÐĄC‡ƒō›üĻ b"~M5ģđ“ûE“  ŒmÄóT5]á9 ·$+ø“ ;e^Ëēō…Ę á?gkB`Ũō9ÖbÕ―ðS€jšĻŧ‘‚͈”ČáTU*ėpų‚Šb&a. øP™á›EŠtŸdAôÓ}šhi †B $%!ecúoôj1š_ĸŊ‡zã~ÜSЖ s$ŅXT4Uô ,ąÎUš-ÐŨÏô1PąB˒ˆY„đĒI3)§‚ Ã#ú•˜GFÅTŪRÅēŽK—.y) ’ûûK ÜĘÓ~™éÎô ÅPF/UŅĐRĻ.ŠQÅEŽ…å%‡(Y ÎVH͊2–Aļuë4ŋ˜õcƒųðĩôôTUŅlfK--…C*"”õ„û!ųŠļаÛâ0Üã’úÅaēžxÐîâˆÃ›Å Ø:(ŪĪPôĘŋĻu+ƒÐ4cp—ų]ÁėČcgE97,žk0ĄBp?îéÐUč  +ũŌ­õ~ŠĨ *8QÛ"QšāIÃ5ͧÀsŽen…üâ Lą„aŒi úŋŠA“Á'åéĨ-/^ÜívŸ?>ŋ{Ā!Ī>ĀéĀĪ‹78cŅeýË6Ō‰ØÉŽ–Ÿå@ČļiĒ4Ķc+ÚyBӋ n=4sT Ôg8ŒŪ&RĪtĮdŊÉUĻŽĨę9Q$^J :4G $l4é‘ÅŧĒ$û@―R5‚‡†ĸïę‰ĶÍofӔ,wĨ(^ŦtáûĐöŋ:žh PŠėRA?Lz5É `ïɛÉYž›Ų~ ĄÜĄÞær ŋJ ģîvü.ŠÍ·Šb.J)T äøA[ŒÝv éä ÔËÏ*čz ‹BþGšŅâ\E|q{‹q.ÖŋųrVPÎBegŲŠŪ‚J”øh5]ˆË0ƁÃŪHŸ)úe˜&ē4”ķ+<4,<66–sîÍģ)))Ԡ˧ąĀyvcՃ9á˜ÅušbRõ§2!bōŲâoĘõp Ø܏ęČæHôļËØię:b˜?X|ú<ĶpBÞ8MUĐ Ķ–K:ãĘQžišpR"Dú}ü4Ë;%SÎ'ŪHˆ„ˆÁí JrĻ’ĸ\Ģ6ÏdŲ\61 ݏĸæČHWýœNęÄééĐ))6ĨĄĄĄqqąŠÐā·ÏÝĖ=wå™ėwō(QÄ<öȕ›yŽ˜"•KÕļÚ §vÁä‡ÍJKÏqyDmâ*Ē’|ĖK={ü\rv–ŦP>ØĄLÄŠĶg'Ÿ;zúF\ņŠÅbƒ-ÛΚ''#3;ŨĒØ?842"˜YwÓÁŠbĸļ}3­RĢdâüNØ'wVš›ûE†iû·mæqUŠ—ŠQ fðFîAĮ\Ö/ā.ÄŧĪLÃc ÉUæĢī W}^‚ĒO9’R"u°Č=RM\w8K•*íįïĮóę%æååišFä=š2°1@4âÛĶĸLp„E?—sëbˁ‘?"X‹UÍõĀÎþ*P5lÓBĀBÆ0 „›°*KrļaøŒÓ=xؐ_$}å9†·mYR LˆĩHvŊļĘl8ģĶé˜( Œ@þMĮ}Đé{ lĘ,ï—Í’SŌΞ;Ÿšž‘“{éō•‹—Ŋ†IÅŠōNɉ(VÚ[ ĶöčúÔäzķï:uÉŲTU|ãjŪ›BÉ ÏZ0}čSÏ ņüĀ o|–ę<yĘ|ĸå)Ģ'LņÜÏMĸ膛į?‘(Zöų Ÿ{rĖ„ąOũąû\ŠBˆ<ĢæÐŽŽy­ßa“ĮčÛũđWūøÉÆäŽÉ_·`æú=Į1QîtŒæÐZ9具ūS5žnÁŒõ{O!j^ŧvÃeą{Ӝ\!DČ 2 uŦHR• 2‹Č8 ÓJœ`KcÛó"‚1$h[ŌJ:˜3N™H8šVķ|y/•Ā <ð„ŠxĶ9‚Û—7_öG&,ūDü ŋõ4%°‰,îŌÚĮ4MiĀč ųôHv!]Ũ(Ü~ąūsÆ'”į\ƒD‡Wâ‹}2ŒœËö Įž§1‰“Iü?:æāķ;5éfRZĶÛ°Ņ? āD2îēžōŽî%õˆ0ÆēÄ+øÏÛCʰí_5}ÔļÏzN}ũ›Ŋŋųbz·/Ÿ~ÕŨ'đ+åÕþ#ūqŪwN‹ėÜwøäĐӆöjæDūï g&õ{xōœŊ7nZŋrÂáÍŽ\Ė!Š/ kŠëĨiÓÎÅ=ēiÓĶžeOŽ^ðqË?+q];΋7?yrëÄ óFÍšĮˆ4ýīlQE`nx<ãP‡āMũw:,ÃcX6‘Ęp"°ŠķMŧLëÞ}ÛVCˆ{p:húáqýn>z!|6ˆŠ2$tžUoˆ1‘‡(ŠîЃ%ŧĐlPbĉLĘH† Æ%æsïBþ~ĨK—ĄTðq― Đē˜ĸ#Âė_œĐÎĩĖ<Ãðú€ŠÅĐÅm] aÔ?K匙&ŒÔ@dÐķÁ1ŨЋʞ2ŒDŊxáa‚-Û&ÐãQÓm2īI5q+04đXÕEA!Ē߂ 2M[H/`…sĄĮ‡0v8Ķ•Ãþ=ąųÛÖgJÎņm“§ýņf˜‚ėB í­ĢþēR‘yýöŒ˜ÓÐ*ķđ@"x–BpLn3⃠q+ĻĻ`šrÓRsâã9īôd―?ĐĸG7ę~ēâYãŌ§ë·­ÖĨV‰€üģa™t›ĩđĀ J‘FųÖîõ™ÎĸÓ0Œ‚Ó4ïööąĒXÉKūÚ^eā˜į:VÂÔŠÜ}Ððv|ķ}S5ÎV}ó]âĨ.ĐĢĶ îÞszëÛĐЎbk†čÄôxŋõ‘ŋĒqNÛv_ÉI+íâÔ8—™VĨĐG>ØåÕvÎ€ÛuZ8dßŨ3enõ°“PŽ|ųЁ-[-ß|$éä–ũ>ߐ’yÅÐë-šûôÚ9S|ĩŋPņړ§MŠ[ĖéT”―_.ōÞé)҃ĮÏÜ!dōÐĨĩúô}ĻvâÞåCŨâ–ÓúĩŧúóĶ-ļ~ĢĘŅ6ÂþÄØđųËÏ7Žßs!ųø˜đãûÔžé=”jĄæbšŸNĀߐqQŪĘēŽ@)'ðü0âË;ür ]AāßjŌĮsãķmYņ qqqÞūĮãņķų0/øSÄŋbü‚Ąš―v>ÝÂąõ›• u8]Ė}Ãļy"ûįd+ÕIá;fÕß­& üDŠ˜R‰ũÅđĒŠÐ(°aJA`‹dŊ–Je^ΑFlÓðķ8•  UQDąĘ„ž/B(ŋäįâķdЊh˜ˆ‡Mė"Ąĩ"O1úóa<)ˆ–>ý™7NÜð~?šsýFzfŨŽ~ĸéĶ}aEJ?üP%óæ•+Ũ2’Oŧš·lžˆģ—>ŅēŠäg‘qöëÕ[ßëŨę™ę(ӝįēhö•ƒëūږF‹īĪMb ëĀÉ4ė9ŋóįŒķ›^ÛõÉyTĒcûV…üøÉÝßmØŠÁÃ―jÄYũt“D"pþ$ƒ…QÍkTNÄ KÏ4―zúo―wŲŋw—NՋUŸąļKõ˜3_>ÅÁõŠåŽ}ûĨGöņ|Ëje0ĩų­™Xæ™ï?þríĘ9oTô~Í2!ĖðČkģ ·˜†ˆPH\)§cŋ)HųA(:đþÃÖ>šķvōWE +Ēd~õųÚļg^]ÐŦŲË&OYëZĸõÚ }ž8wí†=tŒ.\ʘđä―ÖÏ3xÂÍ_>vâPŅ\7Æ8ëڅĢ8cœqņ§IIđjĩĢVĩš•b+ôœ2·G“ ÔĒüSö"ĀâįÂgA›3ŠĻÄDŧPϊ―Z[°€aŁC%―čĮ•*WņŠs{'­^o[·īØþ/]Ū‚n$y.þėŠ_šĮä:=Ŧ)… ÄeŨđ]i[7$ŊI2S4Žýb #Ī˜Ķ “S+Ęí―$ŸÔ‘v ŠĒ"âƒa!é -!†`™ @ČÁíŅĀDœMB@>F4DĄ§hqË͘ÃȕJĸso`)ō anEAŨ…ĸ›‚žĒwdŸĸ~ŧĢÆ›CÚëVžÉ•Ø"‰G>0mMæ­’Ũ-Ÿ™Œ4vÎxšOPËg’ŋõÍO;>XëâŪ·2‹Ūm{í­~óčõX·ÃߎßãåđMŊœúy―æ­NŪ}sf éßPŸņTŸ€æji76Î]>ū^ĸ‘cëwčöÅÄį/Ï[ŋü……'úŦ^yļëÃûv~‡#*Ģ}SŪoÝžÆŌáÃ―đžžŋmąûĐöũô5<9'DÎsį9ƒýÂcb#K8cã‹ĮD8Ī™bÔ$ĄC'ū>Ā•đyöøÏÏyïó…Õ i‚ŠóŋĻÄZu+4wŜŊÚTéP­0Æāî&(<\đ|ӝAŰĖöRۜžÞhÜK]iií_ŨŽÛüÝKķOū–ü|ŊĮRŽžcÎTŠx–M;ŅŠjyÛũåý_uĐZ€ŠˆD䀆‚Ä―á[ĩ6ÃAQ‰Ą‘ŅŅ ‘…,Óūqĩǚsŧ…ėū&JTÓ°Ášîže7 `Áôg âUŠJĀ$IÁ|HŊ\Uu((DpxxXttŒœĸœ9s&{Pā'ŒĢŧQpĘMÏÁÍYŊt™ŲĸŪNå·ģˆ‰žþ%Š„Ôxåėdo‘ŦaĄüo#üóūԒzƒ·Ÿjó܅į_=’óģNô‚m[iy  ˆWîúAm+Y Fâ]‹ä+VėÂÂÃÁ €ZāyĒ: lذiZr–ˆ„d„Orčŋ@6SĄW˅ˆ"Q [ ĮþdH3Įcadļ2âÂ6Œ3.&ûŋޙPĶɀgûÕqûyŧÛŪ“XūËāŅ|ÏŠÓŨÏy“Ø—ø§CIđ†WĪã°§ïp%.ëŲ7Ží2Ž&–i6ā™~îŠĻýëŧÛ$”JĻÐuÐēĸãe\;y`ˏĮ’ëķũ\på\HÉqäf·}ŠëĀá sŪ/OJvU)YŦrÖŪq5dey8&ķ+ËTŠ=4ꁔĄý^ú gíxd›ÜęˈHŋ|%5Ï ĩPí!O?[üæŧûīō}úÉ›—wíæģߞššŨ˜ōSŲéŨŌrK*ČdčQvü‰āŒ;Ck%yéģ Ci\<Ôi͟ûtýščŠĢ"üQVfnV.Ýû7{ĸ@ĸ€Āú7ōŽ]“™iŌ`Ïś9‘Ņ‘NLI`LÕj…ŦV [8Ų'ŊwĻuöĖ ˜øļÐļØPωëI;sø€Ĩ—ˆðĸå7ŨķXõ–ŒÞÛ_?ņ9 TmPaPáWļBĢWŽ‹ ŅÓˆqŒ2Ó3BéįNŧ­ŌÅ#MÏĄë™vÖÍm;ŪØĩT\ДūG†}ÅÃ,…`ëÞQ”t/Í!æZRPU$Ӊ1(Ð|eƒšC“č%đj6MK;X  5ó*TšL/CĖ›žfbŠĒÔ톔qw+nüÐÎä6Õû<ó@—‚y6Ëíņ03: ATŪ>ļØč—ÎLvÃLąxú™ĖžņO@Eč…gŋ]‰Ü*lvH„ŊÃЛ†=h˜úÕĮûäÃÝëOxû•'ęíÕēĸœiŨęöÅŅ#_‰ÓĀAPÓ#\E7 Ku89ĩÜn…dŧĄýŋ:nō+Aĩ ëŲáug lЙ֗.Ô2~>ŧqMųvCËÆEW.óŅ‚—RÖ9ũž>Rc8Ą‡€‰§ĮÍ,Ë^Šr"oáK~IÚv,eÛôÞ"æRKPþąhōäsÃįÔ6JTSä UVĶ–-Ž*ĖJ ‚ãxŦ@öË4Žččč’%KzëYi‹ ĢĖģū<ūSCUUȉýiqīČƒ”_.ûDÎÏS―zęûëÍã[Né<Â_åjͰ†Fu|ïú*Õ/3Û°\Ĩ‹V@wƒ+ˆ― på*ÁŠ`jP_ݍ0ä\ĪŠ‘)UQ]Ä,aGĶ 2`8ZědCô•Jˆ7ƒ”#ķąðŪęât–ā†Hϟ x-=áÅĐ'ū:ðĄ5…uLCĘwŸ=ꉓ†tn―[ę“_)ėZõW 9-Jr7•pMsûbeŊ=KÎ{‡ĖiT+ķBŅï‡wjý ąÔ~“fĮ;NĻ~HT$p·MĻŅå‰ķ{G=ûlT>ð•y~*qƒV™Š 'I,^=RyŧÏčeïLîŽ"yŋQ0 LĻ=süˆ wۚ361æĖöĨïå…æ?šöðãþpÂ!šøæÎVöûþäôþýžŽJH4~fĨpnģ{2ö'‚ŲV`ņ&‹V~üõ†­éyđÁ‘Jú‰Ũ^ÞĐũ þsßаk_AŋŒūœqņņŪëŪ‡û―ØšuĢbš‹Ô{õÅâe iČQĨZĩ“į“PîËGķ._ÔÏvžŧrŪá"ĶaÖxvėŠâßl?—<íåÍę”įĖ–gīMģęc/NäQ„Sé ^Đ͋Ã&_ãÐēm­(ņíö Ķ”+UĖķHĮqŊÔóä\<ĩÜð9­[Ôa>éõj;ŌðļÁĢžÏPĒ-ÓŽÖkęDmšĻŨ‹óPTI“„M˜óÎÖ}§ÃBœŒó{-&Fð„( É‚Ĩ›wÉīˆÁ@L!>öˆĻė$YKũF„. ,Yų Eņš7z)Ĩ[2n%\þŧ­ĸ―rÆÓ―NóİØüý™Vú^MbGĨŲÜuŊĮEŌļ§ĪÂ<óČÖô ,óøđ”˜°„ØBņBīĩļO ŦĀųČ2j‰þēŠųīÆrĶjŠžBÃc**ņ™s͊,"ï4‚ĩ![+œ ,­d=ČėĖĄtEĒ#!&l#™æ(žŒ"‚pôjŅ;/ĻXįęõtĶ:Ã#bBC‚GN_qųzš^8>‚›‘OŊåāyQõێŦÁˆ‡>Ø{f[gęϒJ>úÄôķÅ GxbLˆBĮĖð>+ãÖģ =5ūŽƒŧ"ī{ĄSmįs#gĩŧzÃÂzlDĻú䓌hy&yfÐSŠęPĩˆą‹?Iui \ņëøøLŠøgåyJ4}îýÚ}ôgéasëõHu†3]jhDýŨŦ8M\šÝŽâH1që§ŸâŠæïį˜6kÁĩ›)Š_pLðÝōėý`ķáMčÞ§/įÜōô}účŪwVïÔŅ…K<úX°Ŋdâͧ-~Ē˜“ĘýzhB“F‰–iRÐĻuŨƈƒL@ĩī€Ú äķpÕ4MĨlŦNåå"EĀ"é{D”ŽĻiSŲ[u†Ū °z.TĄƒãJũė]N^ĨĮīKÖŠW–ÆMÄŋÁŪ“ûG—íÔģˆ‡ DõĒĨ|',^Ģ>ĒbéQšvÏru™PUaũžá gāØ(ôģĄŊĘ Ï(Š(L-EB]ӑτœƒ­Il$(ĸTdâа°Š+y1^éUK ° B~·Š%e&› (]ē:*Gēü”}0\ U#xtE{Ō–U]k>˜†ŠvÄÔR[/>úvŦȖĨĘ *„ Bī5Ðf6ƒÂÎWÏ0 „ĖđATÕpDÞϜsÉ “>2EJŪäÔI"ƒÔ9Cb(Då;óÉøŠúŊ‚ čK@.Ķ $kh ĸɀ̈́ÂA„!ˆÐĻĒaQÅāÁí,4ĶlX ’ĸð tøÁ'Žk~ð·N5Šd&ņ%K”)eQĘQ -{§g!5Īd™PßĢČÏũŲęN•ČÐņBÖœþâ‡ĢDpøŊåÜā@ؓoSU6,Ęũ*ũz€āįŸFm…J“č%Šī˜^―ĨķeÖí ÓøõjTnšŋ~U<ųĸ° ã‰Þ4}Bģ€p ĩ-ïWjĀĪŋ―rËC­ß=Ąmŋ<Ëš‰đ0š‘Ú]ŨĄąČ@xP•Ø~UÆ/ ZI%”îßâ`čibsÆXdT”éåņx._ūœžž.S>'ÚĮ͕KGüû1óͧ†åÂJā…U&Böa8å*bĖãv+ŠęМPÐSx·Rþ[Tũ‚G'@ 8?į‚Č"þļ 20ΰ@2`.O‚}ÍÎčŋã N)ˆ/_1ý{ĨåýøÛ‹4ĸӉ4Ž gN\ž™A” ÂE GG†ŠøïÏ[āĀô.‡0cĮĶ5[ö]ŠÞøÁV ŠęÜæĸSŽđ /Ģr蠈ī Û î…ĪŠ !>Ra˜ÆAah8@ Č;âģ}ÄQŅ1Þ<ë…ÓĘû6‡ rC• é\―]N`”ía™yvtąōĻ@Ėcq #ė[D; ÖЙī뭑/JDD•(šâІšÅj֏) ’]é6e!zpþü ŋXÓu˜O1,ЊŪI%‹‰šUӄp-Ģ2ÏĖŪ åįw?;îLķ!’ūluaž~åtXŅv+æÍîÛ­m ē-ÏŊŪ˜™Ėp›Œß›Đ T§röë` Ęl ųüĩĀ^‘é*…ƒ]ŒĶjų―>EĒ`ž–•‘iŲv^^žTŠ5 LÓī!|ÜÜ|ßóÛČØbČb•BĒ•jđÅ8ýĨŊ`Ī kŪlt+œŠŋSs"Ný $ē\žžl_šė9éĶyŋáïbYÂ2Ŋ˜xĪ7›īfÕŠîũUÕTpĨĪÂo\U č†Ua6kEĘAHƒæĻ p)Ip ŅJ°ĄOõ7EfĶû›­įĩϐĶ]*·čVđĨøÍŧVöOÛžërjjúĢČM;ĸýî]{~:’ņĸđCx?ĪÂŨŧŠöŸęEÕęDˆ/ÝĻyģÝÜūvņc7kŨšņ쓿_ĘC:I^4`Ā„GöîØžvŊ‰žĪwŨfĩj<õåél‡Î6Î2bōäޏĩŊW­åŌõ?(N-eïWšvmŨūõÃόÛ}"OŨØ·ó†L˜õĘ―;ÖĐŲęÝM—õúž5Ï<ÞėÁVí§,ýÖRĐ?}2ð…™ģ'ŽnÞĻöĢãægi‹Þüxã7Ÿô|ĪßöSé'ūÐģËCĩņņ^„ \ŧÆŌÎūšbÝņ― ŸzôéÃWģöŽ[ÔC\śӖ\qc ݜĸėģcõëÜbʉŦE!ũ`ŠÁZ‘:áV&ÔHtU—0M ĩ8Ŧc&}Ž,Ûš,`4‹ļčr*š€—JiËķ―åŽã–ކ ųˆZđßiFg mŲ0ĸĀ__ ÔLČGŊģĪŲÆŊV'HCŋfF(HҰî ‚IAŽĀ?ŸéABÜË2ALGĄTÖė–‚-§â nX†@\ĢĖ0-L„Æ’ŽÁ˜‹Ž-Ú)î˜Á)8Ī2ŒgqF!ƒŦú›"--/9ÛSž|WtŠųbD/Z6:õĪäŧ/lé‰}ëzu|tčø‡õíýÄ ņ'ŌÜ"ųf$ĨdļÐýøŦ#$–|žfÕb”O…ĩåŪó_ ónãŅoųŲ*óð‡cænE:ūðÓķ.iS–.Ž|ėý1KôōÛųŋö'Līgv}žïjŸÉoūú|Õå/Ž=rõĖ”gGXĨŸýęŦÏ:‡ž<āåLæH>·cÅįûšŽœ7š―cÅë_žKãNÔÛ/þâíáۖNÝuÕ"Fږ·–œ`åW.ŸšąyéŽkÎ^ÍŦ/\ÎëŊ4(ķų­1?e4ýôëõCŽÆ9ƒúČ&ĄEŸzīiąēŽ==*yýÓc>j=áÝĩŸž•ųÃÛãįïĀtö‡ïNŌčŲ<_*N—É=ö’ŒOÐčĒ”ëšjšQ}5=úĐ*2åY“–ÖáįК“ |‰ĮãÉÍÉĩ,ë7XZüë DŠŊ#ų%ąð'Pó( МÄÂqüŦōŲķųë>Ŋ‚8ĸ—ĪÂT!dŪxd-ÓDŒ;ހËo0x+ ýËēÅ bųģ.ûŪ—Ĩ],ŠuiYĶiŒsNW™(amΑzæøŅcaå_ß>,XëðÄcs.Kō0Ī…w;Ģ{ÃĘÛ•âĮ3ó2ŊühfŽÛû=+ÅĨ˜C,#Šá“ŊÍĐ\Šf8ķT逐ބâ…ýļÞ#c‹í›Ņ­ÓÏO~Ą{Óōܰæˆ-P8 tą˜ŸÖŽŨKĩíÝī\!ƒŠ5æ‹=Ū ePĄØ§ÆNŊ^Úi6įũ`ŠåD‚ę1‚)žä&`‚`‰ „1QüÎĩĒĻÞp{ ÄļP6@‚Š B_ĒjģD6QsssÜn—Ū;ō‰ÞōS+XÞr„åð…AâB60‡Ö‚Ë4ōóŽoÅĸ{Ũý/bQ ‚š˜Ï! Ði [įƒÎ,€‰ŦšJÅrZˆ5›ZŌ―‘3Ņ–Ō4ŅNúģš&―Ųᒓ…m‘ŠÄ'#pĘð1ŒĖ&ĒÓĨBŅĸŨPQ/1ïāÁƒ·æ–âĨxā;.D[9ĮāNŸâŒ| Xéē†Eņ]•-ÚĩnOHÍ]škĐ2ý͉mF~ēí@ÝåßmÚąáĖðZL˜3#Öݏŋ0Ï:ŠŽ˜ņÞĮ}ŠW7>QOFFZHą2ôĘáŲvDļzîüE^RCâ&ÏLŲ–ÉļGĀ[-ŒĪJ·Âm;9 w’‡“BĄZ晃g3ëEFĨ^>J­`'$rãC ŪÅAĩfýfyĻ$æØęHŋhaŒ…!–™Į8TiĖc_ē gVĻÂcïoęšqAÏŅÏMĻ~ðģ2N_}A™Ámađ]ēīņ晛y<$Č:wņ" ŊŽ A†,ģ5~oŽÅ0†å?xž"Å0‡ÛĖđ5U(đ€7gČļL–Ĩāō -p‘2ŒÅ Ã#ðĸ„Ü^ŌE‘B."Ãr[|Y·<Ė2õ€~*įŲŪ\ÄQūŨ°‚@ŋ%Yųĸ:Õ/Ö·:˜ËΑ ébäv„(’åEˆč–HĮuUSu§ÎĄpāÐC7y+Ē`<ŽĀ€Zķ$Ô,— ’ŪĶɝ€ ėĮRÆz6eļī†TÐ.ŠTĐRĶLÎ}ūp5kÖ,Zīč+zæķŽŦ™ö'z<ĸČCÏwíāûęŲåBJ^ķAņ]Íx\n#ĄHTt ’Sý2–ë”Ý―zÉöĮM˜:<îĮ_œZ4øÉ‘Ģ:xĪkŋi3gL3läč…j•î-kg~Ôī ƒæm·;?ßÉI ęvK›2îqÁO#ĄØãQļŨŋ2cÚtŊÄø”ĒuŊ[ģaßfĩ—O2sÖĪņŊméøð ęrŧ< ŽÅ(wđ %ŠÆĻö-6ΝļpŲ› Î=šjŦXsįI`g7ĩíȒĩŠ:ÎĖ›<ũ§ãiŧ>˜0ųÕ%ßM/VąbļŠŲ/ïđŏĐÛ―QåĪįœöÂā…{ÕG†ttXï5Svo;æBYŠĐŠiYú˜ÐpÝQ‘Í@*P*r‘eJS5QޚĪÅ'Vˆš••áņļóōō\đđÁÁ!—Ļ2K\*'ĒbfĒw Y‹<‹!2EœŸs|.õ&Ģz&=f,\ Č{ Šl„æ—_ųrn[ÔBœČ6ƒpįUTĻ”Å\―ėÔr§ÓŊ|ĨJ.··Ï‘-åhMËĪķtŸĐfŽĪ9ŪO;ZŦ–a",ĻP’ž ĻĒķĩ "–AmŋËJšđķ%j Ē(šh, !Û(xÏĸÉTët:4hpíÚ5ïĮî­pëÔĐsWbóØVWg/~3ŧ@ĩ=Šžmãŧ670ĩĖĒÜteTÜ?Ēړ‹§aná°ēŊ-XųõĶí9&YÜŧqŌqĶ™3jŅŌð"þåũēc.`f Hũ™h2čf[Ž+ąØ/ÚķTū†Ē;‰ ˜HÄvZĩéÚ---íĘåKWŊ^ebÐdyqÚ ‚@HŦ] 對ą(^Ą5‹(’ũ]‘ą@Í?ßžėņxļīPäH%š†ó)ēá€âýƒ|,ĮđFŽÛp‰„*āČåqĐAĶÅ-“c…‹ÝÐßĨ6 kĸč#†éķžĸŧž7…\—Ûex<ގgšŨQ ;ÛŧSĪKË4 Cūš!6+Vc\ôjuĄ€NS @ĖaQ݊ęXėŌ ˜C\8ßHÐWþã. åʕû駟jÔĻ|Ũï2uĻvdvVĐEpÁýČÃPšhųÜíÚJ–Žœvzų—?'õŠýöĢĨ—BĒ*ÆéĮÝŪäk9č~ü fyÔÐļVö(ŒV‡ÄuíҰR6Ðmœåę=ĀLƒrĩÜ E“žōļŌæ6åϊ ­XįáNå8<Œj·~īüþŽ(Į…+Õ'LČ„)_ŋ(7LĄ†YųGŠ5•ˆixļ⅐w?G~ååk1\īVŦ’ĩÅäáV―*€6ØæëŅ:"Š? ÏĒ–ßí1ß5C‘wë<üÞe‹p5%N‡\ëS)sË}ĐÎŦ;tƘč!4ß0 qɀ;=RŧNÝēeËVŽX‘"Qī^h­mÛÞ\æÝðzÞddf0Xšs„)á&˜w`§åšĐĘ@Ý#6fi—OĩŽa;UŽ@)ˆ ސ,ģíÔÜd—;YČô˜ž<—·ĸĀUâv[nã sLǘ™ ã ð Qęf‹9ŪåƒĢ˜·BĖÎĘŨïŅW?ŠÛlt?þĸą` âô_‘‡ēéÂ$ĮąÅË`Nc―Lˆ"=Ēõ<ÄĸPÉPŒŒÅĩ0@ÍûžĻŠĘ-Ô#Cĸ{ĄÞq,F™ŽgAĐ@č]I3KQ”94 †‹'+­q0pT™O3[žYŲYĮŽĐRĩŠ—Œ+eYžyJ–M „Ū‹dUēDÉ å…ūĒiÉđ™ŧ“OwæāđÃÛӍ45B‰BuS„ü”ŽLŨÎ GÚ:kŧ5iø­• Ļø}ÆūEĄđ܌ĻÎpŋ`Ã4sōrÝyž ЗģģōĮÎÉÉū‘ž’iäĐfJnNZŪÛĐp[ÁŠôS°Hh~šÓˆ@X^ŠĖé2áz·ĢĢĢÚī}Ð ! Xo…þÆÂ…Āęˆ1Áb} /hŠC‡fŪ1RuMþð äÁ‰‚øý!óėÝ#""īM“Š[·Øļí;”oB „ČˆĶĩ*DGĸaŋUĐÛž]]ØęÝŦÏį}–mjAAĄ-Û=‚îĮĸŊĀđģÓÝL DœÝ=%į\=ļįTvóVM4bÉrōöj3#- û‡€ŸĢDÓ3?oKw&ÖŽPÝ3ĢŠjÎ͓‡O%é!‘•*–Óąāę`+ũÃŲn;ĶH‰â…c bũS­/ĒÉČAΊCęYĄø']”A†Îcb4oÚ’gĖ6lÆí‹gÏĶĨĨzÝČoŌ20Đĩ,ëq)E‰ ęSąųð&Ý/eÞ\{üó“~Þsé‡TW6Ũý°Éī\;Ïk\ļQ]ӝšģJh]|uBØĪ<#—2Ŋnŋ_pžË•ž™™u*õ\fnž_(NÕÔ@Åß_s6ŠŦ_$"ÆĐú9ˆŪ)Іha$ī<Ŧä§;ž§ÕTšĶ)Š"“ĐÔIĘÛķM‚b(ĨØûý™S'øāäČEkE(mˆāĒüĐUģm‹1*Û*ąä- #MɅŋO‘šbÅŌå˕ļݜC;ýĸĪõ[æÉa/ ûņĸŋĒey_Î}ę―ŒoĖė_$SÆïÜ2"I§ŋ›ōĘþm[:„ ÉÔķėėÕóĶãÚC?RŌ#šXSØîŊWtķŪUĨšƒ‚ Q•ėóĮŒ~8ÃadÞč7}Å3ÖVÜĐo―:bîšóE‚mŋŠ,˜:4Æw…ũS-`>ŦwMØÁrË2 $_ƀā$íĻ(Ð1ŽĮrm]IŲī-Á.SœĐĐ)GŽ)RĪĻ„yáņ›Ę=Þ,é-H=–Q86îđęOđlŨ…ĖKkŽ~þÁŅuŲNĘlÏåóG’ŠķäŠÐp莒þek°'kr+7Ĩ=UïŅ(ŋPorOÎLŧ–q}Į―ĖV—<=ëÁuu$ŊœöéĖ(• āՅ„‹Á=RõÆ0 oõŊƒ#åĮōm} Āļëyį~‡ōx\ÐcšŪ2pĮy—*ýAÄVōŋ Fƒ’ZĶíá<îïÕĸ07t?þ{-É―r`ÁÂõû‹{ë˜X71‹‰.öļþÅ·>ëÔēnÖĶUolĘ\ōÉŨ “Ó—3ƒT›q|ŋŠýE@” Ķmqj‹Ö Q‡U°ĶxÃô˜DÁŌŌÕ4 ēS€ię1ĻöܙÓ^5ÕČČHÎy~†―}#ĸŸoŨcx Ó°mZ<ϨφcŽŋšgipœsߙŸũ\<ÞŌQMÕT§3D íßũdÎĨ=.ķHl5žŲãžžž”ŽīĖŽėnüp"ý|ƒøš”k˜“ž“`XŲ ”)6|ėĀ[ n+Š"Ëí‚Ų6CŌ1žni'ĩMôđ…9ŅI> A(p<ŌŊJ•Đũ˄z–üäîûq?ˆJ~øäCģę3ííïž―Ĩs'øÚžþ““ žÞ―ë8ĐŧtéŽFÅŊïútôŒĐģ+—*”‹p-ģ ‡ŽO8ĸãÉAŊ.­ĄŸ|fÄĖô\ÓáŽũĘē‘ĩâýöė\ŊT{Ē|ɀ=ŦfŒ[ö5RËâë:ĩÉïÛÚYgĶ Tč҉c;VßąlØ'—JL}qPPdīŋuøüņ“g“2 ŨĻŪ_}ŋ―bãĶ$íĖëZZUƒjýiqå{OŲ‹yĄ ―ŦHÛ6+LD6n:C˜`ôƒÉ#Ļ/j–eƒ†zåōåsįÎÉLúŧ‘/ ~ëŸâäŌs.FQøš\WC5Ēæ}đũŦĪô4/ Ëåh‚ōÁÕ§•Xørۅï<óZr$§§fgå\Éļü]Ō.bá~5z! ›ķU ƒ0‘/„odļÓmāö=2{ëßý{ŋÏÎĘŠ˜@ža,ž‚$(XN“LbN…3*%ĀęÜ=+& ēŽ―ũŦÄuöÝÕûێ7ģWûók>8šæQ―ðÓöíGé˜EïtÅ{Þß{Y1ŪL0YiÆ ÛŽ[ļvÍĻ ;.,žĸÔūđÛ ―ÐâąĀ ‡îPUĩlD‘ē…ŠdeeÝĖHIÏČž’ré㠟\ČžŌ0ąV“bMōē|ģļ‚kĸü=ōuĨ›Ī< Ÿ[Qðđ-›ë_ŊOÚéÓ§0$NŽ‘Dž!‚Ā™Qü§h*ƒŽĻDUUËōnËD ―Z 5fZBŊý7ÄĨKY;w_NJqĸęz9Š,äŽU#Ū\ŲBč~üĸ UWOŪ_ēóÆđ&;ÖŪÍ:–ÆwĖûúøGO2ƒ#†LžQŊ|Čą’Њ2Îō‹›ß§qDīũĄ~>ņˆ”>xdÐĞ Ęeœ\ŋũtā°Öíž”ÜGíąâ…ŊO]ÏQ %éÚéÜ uš4-^ˆ$tėÓâ{Ãõ a…’šŽđzÝóCF?―hG·ÆąĖČúėíĨ1MĮ­Ÿ]áĢåŊ šūtíŒ>ÜÂåęķý`îSÚĐoę·™ēđ]ݞu ËÁÚýŠ f‚iGJpåbcbšBĮ žĮ)R L#S’ iYŠŪ)Xą,“svėØáëŨŊkšz§^ííųŨãņØķíïôß}qŨüÝŊ9‚Z–jæā:VQh ŋ=§ūīáÍc—ÎđÝËī<ĶáíðfåæÜLOÚ{õû·Ï―{4㜃j͊=āäNË6“ĘoÏĄøVüΈ߭p―HÛûũyųæBYFˆ„ˆ  Ģ0NäŌûK ™@|zc˜ÜōÔQ%ÏMUÉ…rF†P\tƆ6ïZĨUũ*­Å—ØhŅ­J`BÄæ]—RS]ށrvŨŽŧ~<”nĄŧ„™—yíĘĩ˗/]đr=ÛMQ02’ėÚđcįî+)ŲũEUžöö;?Vé0ŽcÄr-ž|ū}ųƒŦ>L3ÁŸ™btBđ†a–wõčĩlDģvïÚĮ‘šû~œ"Nđke ÖÉÕģį™™ė”6]åđ)Ŋd!+ũōŽĐŠVlzzįWÉy5jĸéà —\”°žÃ‡Ž”ĻŲĄEÛÎ3‡ũĘØąårŽ+OpžŠ3$Ø/H+ˆŧŸj%ß A=+IbŠd@AÍnē· Ōķ™ý‹;bDė@mŅ\đy'Oœ0MČD·â·333u]wę~)y)^Ą†%ęÁķID—ô?—đcāg}ûÖĩī›ķiæđ\Y99‡ŪĸžôôĘÓY—°ƒbšVéäMÁû’ãję?üówĸĐ(ĘéÓ§;"/˜Y36lTÄ.ŸZĩay@D…’ŒŌˆA0z$T$‹ ĸ†HKËMÎņ-Ÿ@‰f!ߗíýÂZbŲxæÔ““ïžûčņÖôzļĮȧčũø“ƒ^Š‹9éIÉéŋÍ҇ķžŲ­ûĢ#GŽ4zâĶã™č—`§·―7røÄéã‡>ŌsÔũ—ĸ§9iŠŠĶ;°ûzÐãÏéÜđÃÃuî?|―ēþŧÃ7tęĢģچÛ0 YąSōãG6øÅŲïîvyT ļ…î įAqUŸ~ĒŌŧӟŸ1kŌ‹+Ö<Ü―]ņPÍ4ąaÚ1eWö 1yyđüØĻqãĻĻ(ÆXÁFÁĘ[QԘĶaõ‹ÕŊ[ÅĶv™čŌåÃKėĘ܇UÍmģˆB9I/ą@ˆZĸĄīėŒĐÞŪō7üs,nÓūuz -z=éúí݀ßÄoæuâö26ĸ`™g322öĸðCfz†ŠĻdpË’ÚLĄrÎ,›*DÍbÎČԘ&ô Ā‘A2ÍĪ $ú;”ŌõëŨĸFqŅŦMsgÅEÛmÚyÆLÜT~ŦļČðÝģ/ėũč3Á=ŋ7Ĩ3ūqtPĢ–ÏĨĮóqĸoGûĪÁāUVw8ɰŊf˜Á›6lj‡ŌØÂđÅļh“Į>nų|Ī#cxN_íž\ïą ĸÃ$1K‰(;wŲËe˄™†É9 +Óö“wcýc*Î[X<Č4qŧ‘o4 K4iČÐy‹*mܞiĮ>7l`z&UMûÁ‘o4 +*(šÄŋËð7cŦnū˜”ûÜĪ…ÍÔU˜ëŅÁÓPp4 ZúÞōíŧ(ÁåNI7M.™`Äĸ‘3ĘÖŊãį /}ņþg\ĖRÚ<37ļ䯓—’ wï?ŠUóxĄjmß_žiĸ1Tūï ķ-ãąeßOĩ28W5Íī,?Ī„H#6PģFPŽ āó JĮr S ˆQĐP^[ßžqý…óÞT›ŋ–G·âNM[iŪišÃv Þ,ãÏ5ėģýØâŌÚ9”)š†Ļuðāä2õ“ģŌ’R“Í;ÞĒZpLŅ2!Îß\EPXL€eΧ^ŨtZ$<ýgĖŊPlí˜xí‡=ÄYĨ~#fY(6Š[&e(ĄB=Ð.°ĩ€˜Ýã`Ïą ÕÄûâbžýķîÜK—r“"ĩXŲ ˆŲވ(\ū[ŊŠ—„ļÄ@–ŒŠŨ"Þ`!]ĶMŽÐȧØŲ°Mį†RðÚFRˆĒØę ŸĻÞзĮūÏ+þĪF—~‹ÅÏ9^ŠfLt!™$ïBžEœƒō·OtF”8˜ø((DŅt=ϕûóÁƒÕŦŨ$Ũ‚Uí]õĮétĘքaՊÕĻSáXŌ(n›„Ž;øąÃ?îŊpLåčzJRFVV/§W­^:ÂîÎsL‘2iޞpeÜ)óÞþ§Ē(^8ÚO?íÏÉɒl$Rđ0 –íšøļd/EUiį#ņ Ôâ -Ģ‚}M°ĸĩāÁŋÉŊ―6ÆÞfˆ4cŊUŦ–WGü.Š‹.Ëšœa=ß·'ĘÉųĨēu8^ß°/ÛĪw—sV‘"‘‘ŋ(.–2Þ=mÅP@qQ)_ȑšvdï-U;>üĖãÝBT”ܕþåįïŊ^ðĘWŦ[%Ēĸí⌊Ęœ›R7Ā4Ŧ]ĀĻ6o“5€Ĩ€ „mYūÃl‹ÚŋĸÚōĩĪ0‚į—ó˜·­‡Lt{ÜOĩRę~ĸąļ1ŪëG€…Ü*ä8WEgAąDcŠš˜ƒILjJ| ‘‹Ą‚õpîô)/ęŦRĨJ”Š2ðwëŲßėÎ]āP+ÉAŽ€ķUZøâ˜âðc˜įqåwåʅ™_ŪčZą‰áņļÜ.ÓēŠŨ1ô›žųŧ·o#ˆ‚HƒÛągϞ=qäĻiš* ž$åƒpõ 'DD Ã-ĘĻĻg°Ė@d]âyAðSŦ•―oô·(.z…Ã―sKïýÃ[áÖŪ]ûŪô0ęĄvRį,] j#b(~y6Ew%\bÛō°ããF^ķÂãcĒý‹û…ĮŽ)^ðØŠ-ŸýĒÉį·Ū~ķϘŒč3ۗøå1g`•zÍĒĢÃæŋļøÍãûķ č~܏ĸÂąÆDŊUČģĖį‚ŠTšĶrQŪRÁâÂ’ÕyÖĒ”fŠ* ņ0B7n\óŪđm†c·―~–@Ā'QŠÓJ[°:Åk5……Ģéá퇇Fú]ļqpņũï―tðræÕŦŲŨââĢƒCä}õwōĖ·ã n?ėö‡!đyyßïŲíõũU°*°\‚T&€™E 18ĖŽð[ÓÁï#áž+16įQ,P“ïLķpáÂ^6EQžŠ‹ĀĒūc0ΊBâ*ŨŒŊZ'ĸ+ŪJ­ĻP%ÐÁŋ[U[šT•īÓĮŨĸœ$ģö†—\ ‹ĐŦįš]ÉWsn§@iš_™6mcŠ Ŧ§3ĒIW/]ÏpC -QĄnˍŦĮėýéĻݏûņ_™jĄŠ%DBĩ€Č$ÆRčKV€âŒ…–Č­ŠœbQčZb&ėˆ+ŠĻō(gĒ$GœÃz§Iē^þ— PØ:ĐkcXfâÕ*%”3=†pƒ°ĐĮC-‹9ÂtšôcöūMI[ČÜUŽx,ĢLÖŠČDþëBČŲ3§O˜DEĖÜH°B8åB[VÍIąĄĻ \ Ĩ+3oîXęE"y jþÎĻ[·ŪŨŽÁŧæøCÅE?Í ņC:ũŨQĀ/_<Ô ÅErWÅÅFūö\õɃû―øĘŦÓĮôï7ëØsӖ–óC åËmyyŌk6š Čņíĸráô—_ũ܀C1Íût*‡hę+CšNøā(BhߚŨ_˜úōėiÃ|tų‘Û ûq?þ{•―Ä$h‚E*:8ŠĻÃ0)ïĶ.ÕŽ14jū)•zT❀Ė*ĶeǚĶÃŲģ§Ož<éU­–"â·Wē·ïáœŧÝn1܇ }°zëŸÎĨ&€_Ė‘…1Ą’€k^ąIĮ:―@ąÛÁģ2îÔR(·CōŊĘŧĘÞûýũÉÉIÂLŒŠŽ F\ÕuņŽÄ‘ØéÔMËR0§\QþÛ`ožĻ  °-cĶ C3…Ų†įĒŋ/žú–-ZīPå Ž–Š‹å‹+ŋV\žxō‚ÃuģPd…?P\đ8௯SW’rIf°Óþzösôzĸ§{ÎY]d+ Q ”īQEK'Ī\Q{?ÓēUąĔg'ūfE”AŊŅĻĒį˜›ÆNyg`Ó:%ÐýļĸÅ. X‚ ĻČ>ûJE˜}QfßJAg…_ŽÂ@úšÁ›īe+}]ĪŠ:øÓ~ïāÅë úš;LÖË^F ‘ëĘk]­Ų‚/WĪįd&öĨ„%­Ē hĸœi˜ōđŊŋņĸ†ô%ū;ĐLķkOœ8qøįƒōQ˜haUuPK`đ|td$Ņsá:!”\4Â(a4-oÎ@Œ†(Šåsp@…ŋa,ö'#CÛ6­šeۏ›ķ}WðÖ ›ÕŪþ‡ŊS§iÛ:°õXŊ>kVžc;C[īéü›:ąRŦ'*Üá(]­>‚ˆ)YŧgÉÚč~܏{@ŲKõéx1Ž`+Ž+1āgAļL@7ļysŽ`IĖ4˜ŲØĻĀĄ‚„%`§.wÞņĢG―æ !!Ąó‚Đö7„ŪßķÐÓT!ŧ™ÅcŠ—Š*š7-…+ ‡^†TŲĶ–]§|õ Þö‚iČgÝlp{{ũlKqđ\~ܟ)z ŠeŲ Œ‰æ,ƒ gãLSUéŠ9p―G>ÎųāŽKĨJþ_"ŠJ*T(]Ū\‰Û/cüU\ ‰)ýÄócŅĸp܏ûU­H1P2ĒøTŪ…+,~åNˁ$ī(*XŌ> ‰NZ)ržÎRE[ÓV0NMNņĒūJ”,Y0·Ęí;•·RĖPÓ4ĐäB°2äĄ~ŧũoÎpÝRt[%œ DL—1dĀÐĀïØJ>ë7uëh Rĩëv,íí*Ÿ^ÅáC?XÓ?M1 CĶ„ĪŅT #,o0ŠĶ“ƒs!Á4Î cKŠŽkšĀÏQSĀqĸKūã*šĸŽïˆ";9RhX ذ’šÝĢáž6Ą`”XH" ŋ21’‘tÁš%ƒ"NŒI1Ž*>RkB$ÅD„čQr™=-[üĮÅ;Į6Œ†ĪĘâÁƒē23åĪë7ÐZųÏÛEŋ|IÂīŒzåëŨŠ|Ā/Ywæ8T—Šzˆ•mÔŦ\Ŧqí’‹%OøŊ# ūôíšššīŧ―%-čczūƒ–ŊÆ`Ļ*Ēӂ 4U·)•‘ÞýˆK-`ĸ2<&ĢĒ-ƒþĮ"#3ûÜÅŦĸ~9?ÏŪžģ§O9{îōĨ˗/xUKÏ\š–|nũķ/öœĘ'™`‚Ý™ÉIéŲüŋyŽü/äYėĘHJÎČSšoËŨ?žKcyWŋ^ģ9)ÓÂäŸj…û`š \ŋī@rÃÂŌâØ‚õŊH=” 1΀"E%쐂ëÅ` Íŋtņ‚—žt‡Jöũįcų­RY„úûų·nÐÜš_ĒNī€võ@íšÁA6ĩ7·Þiãöęĩāģ $bÅŦK{ôÐ!aD lcے>Œ> ļ6ČĩĶiBžU)ePÛ"_ÅápꔊUõČāb‚}æĸ øįÆĖėŌoĪĮ0ŅŋũCŅīä įįLŸöŌŦģF>Ûkäčiģ_yyņÛëö}ļlîš]ö­4ĢéúĄU3Į/ßH‘ōÉï7þÂĀ(ý敛î?]ÄŠŪX9uâŠІū\ôō·.ážcŊūôΙ‚"ôÏQ‹aæ č% ŒdH6‚Jē†hÖZ”b&B!ĒÜĢķ­;tđö'PÉðÝRÅkFsäðaŊŸkūŽâ ļ@ČĨTŒuŠÕt:üĨ˜NiTtô“Ý3 ã.ýŲÛˆOÛRilįÎí^ĮÉ[U6•)Ė9ÚRáækšķžx,Œ0īķÅð€ĨĨÔŧ%?ĄĄ °ÜÃîÁl{ábæÎ=WRR\Ļ@L~8ēųãOŨ=?ĻŊŪkč߈ûA-+Šx‰Ņ/NQXö’ĢyÕĮw-‰ĩĀco}ių#ӓkØþþķi•nÓûiI@ŲvđÜÂŅŲá(ðų žíÎs™ ‡ŸŸCSĐeäđôōŠõâŦsQÝ<=i`Į–­Ú=ØųĐuG2ėœS“‡<ÖĶCĮVMž}ÓĒé‡ŋšßüËO=Ų·y쯓ßüāåįz7kRïņŲxTį‘Ŋæ?;nVŋ'ú4iPwāÜÏrŠô·į ļýCmZ5}ņ­ ĒyŪ˜ðØS“'=ņ`Ŧ†ž#ÅTą‘šbú°.;6|ØwįrøæëO?;aڈ>šÕíÜš]^^ÞíkĸĸėöŒ,KT·ÛíÜžqC%ŌxœIč–ļŧbyýÄŧ_į˜ā+ˆš—aų‰Ī+ŽeōŒšĶE #0Č2\ØūŨ"5-7%Ũ“X.^q8æ0đncGķËZīdđSCÔnîЂŅŋW~X2ýĩøŋŊyfÓāgzM˜õęÄQýßÞr>ĸ‰5KïúÂélûw%Éޝ1yïĨĖ‚oîĩÁý7‡-åÚå4—ýũf[ÂWņ€@Ąâ‡ÞđÏŋöĀ7Æ5þféû—-œ{õČϗSt…m^ļx{LŦũŨ|9葖 5å Påî/–ŽĸürÅųŦŋøtå+͋Ąw_ķ-§Øį_­Ÿ;ŠÄ‚ŅCf2–vníŠuÍýú ÏũÜÖŪũ‹+įÝõîÔ ‡ÓÏ~·xŌŧŨ_úpýGoLøfÉÔOv\ÓTÏO[ūI‹îžäõYa‡>úášyjãģŋÍXķîË%.―8ëcÛĄ\üi۞3Ž KÞíhïxÏųĻō ;UIė0rưg:*ÔQB …ļ?]ģÅĖ=ŧęÍķ~ņųMÃ―ýÓũCËWð\úéįŦĐáĸ&ķ`ï™TK‘T&ķ·lQ‚Á[‘Ú]šę`Š6$aO–Čŋ‚Ũ ‰S",ĩŊ9ŌTiŧˈ(ęðū―ß§ĪĪHßïüöķą˜ˆ‚ĐVnČôįMĩņ11ŒąwŠū]Õûw§[wãMÜē1}ņâÅ―{viŠ‚äubĒą,Á F>*f\ÝÁA|RČ|i‡)C”+šÆ AŦ`‚‹nŒAĸc”/ĒxO…eZn“f{ØÄÉ3§Ï˜›–íqŲčãÏ6|ŋ}ûƒjŲš}nN.úĢpŧrsrr=Uü"qÐbØ)ÕX9gâoNssóŽŠ yđ9nÃĪöoŒYS›[ýŲŋÚšek·ú…„ëڞŅã^ØīûüėI ÓáÔUWnvž)ģjXũ#ę ĶfŌôa6O1LŠþIaæŲĩz ŸÕ§qĐōō Q5anÚĪÅĀgęžúīGũgÖđĐ)DĘxzrnî[ĸSŸžý(_īdL˜{ÏÎë mŸ*Z·]ŊĘĄyŧĶ0Öč>ö™N$Ä&TŠÛo䓝ʕ(VäíMfÖč0âņN5j5jÛĢvėõËĐûķlĐÜķ_ýĒ%Ŧ7ïU)âðÁ39”‡UŠ9fpŊ*eJ$Õ6Žlĸ2ëōŅįíþæ[KsŅð˜į'O­SŪlą’æČ/"š˜_X\BldˆÎH­f݂Î~ģîÃ%…ïý@%ŨÚŊ>Û|ūú쭊› ÄĸuŌāBEÎUL~ ›E%*$ŅÂÅ,å2ĐÃ9’‰Ņ•[E@-8m[FDqGaˆ%Öā–e]8ník~>tČ+–ŠišŪëŋÛ7ČßFRéÕeŧvŸÛU$!ĄBĐē%‹ïÖū“€ē"tũôz·ėđýYBÄ+/oŨÎí9ŲŲaÆ8ī\‰EE/€@šj;ˆ;Ž„–KʑJU“ųÔįdŲ*QußLčōĀ,é yÏ ĮđM­\“9qbõŠåkŋ\āÐÉũWۈMHxŽïsy”ãŧjÓ0wö–Ĩ“5nŌšu‡)ŊïÄ~:†Žyã§ÏŸîŲŠM‹ú<7ášÁģŊé?büŒq̚?PãÁgĮ_ņ d&ŋũĘģĩĩė?čņĮ;õøödÆ/Ųĸō›Ūä=ŌĒâķ­ÛŽ&ó"Q’Ó›óę‹/îóüÐåųϜĖúváčĮÚ4-ßĻũÚ―éål}gåą4ŨūũæŽYŋiü€!+>?‚þþā(? аŐey8Wąls‚FWtíïŲ6õÁ„ąæL1U‚ÖČ’P!ęįӇÅšĻ!…žr !œ{óâ…$ę…9ā-Š …!dZ–áð<Ũ !ONŌO§Ŋ„Į/ž|øgņĻ;ýäõdĸð`EšęÚâYĒ'ËõčĒ›žņÎę[?û|U?Ýc؈cj"Îð6\nWōõ<„Ô[ÔD[ŠNđā=†|Óþ‘AW(4óĐ'ÔVmK9ЭūüSþÍĸųU-ķyiVĄLÔŦRn„kĄ\EÄĶBëĀáÐ`í,š šĒ*S ö˜ÂĒÄ#DąĘ.ę Ėōrģ?ųčƒWfNŸĸúkÛķmŧ™”dÛ4?áÞųÃ.MÕŌ=óvÍÍrg>Øļe“Úõ―ŲÖ0Íß­jïīóöūÁÚžĀƒ#GžE4Xs!ÝKÁX#0ĮšĶ˜Ë9sčÝtĢ-2)ž—-rŦ"úāRŊ6_á^ŽjAqŅīRMí‰A#ãŠkŅüÅŊÍIš~ĩۓÏf9"3\æÝo\ņüļU_z{ýúՃûVĶ“Ã~Gxɑ 6lþ|aÞūũwžÏÃVæîVĖ-ööûóČþö^Ė9øÞĖŨ>?ūlõÚŸjséôÎd—ņK—öú•ėlsŅâ•KÏîßýĐĨ›/!„~\:{UF­ÉãÆ:Hũwˆ$ox\.aęråy,.’Į“d‡OũãQe2& {É@ĘÉc‡“3Ųīb™į'―ÐŧCiŽþæ°(·ËWGt&ƒ„ãöē<ģ ÃĒ~·lþä™Ŋm}ÁŽ^ãЍ‹"ŲaąlTĩYïoß)đĸäeäY,ÚYēęļĨŅö―âĻ2ūqÓæaŠQđýļđƒMÓ,\­Ãâ9ĶfšjBioĖ+ĒÓëĖ]ŽŅ€ÖĩĢn–Š2Ēi‹"Œ—iąjYÜöïRĨúģMš”ŒTóüjO{cn‚jǐbCĶ-#!M>zwåæ]?æŲ$Ąhi Zž<īˆŋiã‡Į.ó„6MÖjÐÂUw:EN‘jžÜŅķßž­āĒa iÐuãwM‹WõiÖ||fŽ2Müøô8Š8 FŊ―R81ÁAĸ'sAa‘MōudĻ­]—3n[2Š ˆd@ ÃÔē}%Â&ôĒPQӉ<ėÝOmÛ§ü$8ŽÂ]Ņ~@sōē=röôéï6o.]Ķė TŊQ#Ō+!.;ė7YRWõŌ‘er]đ†ÛÝĻ~ĮS°―‹(mÁĖûÍ Ý-Ø=ØģgρûMÛŌ5 yY[z+"PĒ{‹tŧQr‹xF āšdĄ­(!X(.s$j, ,Ēęšm[”h€ÂÐ― ļĻŲŅAˆYŽ|­ÚÏķũŽ.Oö ÔüJqÐŧ+.†…„į]8x!‹— Á šYHhČđf>=ôŌÃ3–>3äáޚâT°pX=ęf.cL8ú;r!9ÕsfãšËŨËÐv-Zą3iš8d VsyqÅŧđ.BÓūûäģð66t Į^Zø°Ŋï‹ýƒÃJáŒĨĶd ē~ŨŽžÔŠGęäÖ/°•ë6õïïŽĩĒĨ*"&~aÄŧ+VđX­S-$ĄN}!pkO|ĩš‰5ęIÁo›ēüX*Z―a‰Ēvī-Ëð°â••ŠŠA#\ŽčÃâJF ÏæížÖŽÄ"§ûGTŽÍož$iÚšâÜöÖ*TĪ|ũbĨjļiQ= P…š1ķir- tåZĖ6―ÛΘ’š—FH<Ï4YéZu˜e2†ĘŨÆLœĮÝūCw ŨĘ}į<4ŪD­ÂČ[§q@ėYxņ*‘ð+ŨäT]ĢZ(h<ýģ‰đ‚ũß3 ļE \ ((lÄ}]]EÚ `ĄœĀE+"U‰DlYTrØ =\ÓīōsŸĪHčšč<\žpÎ+õýãþ―%K—ĐUŦN―úõ―>WRkFŅRÁdfzzzË-%ŦâNU­Lē·§āüũß9 jØķüÓ4MX9ŠØ―sgŌëŠú–uŦgĖL“b8īĻô­ 0 ŠŒ­ƒrU“`[ÁāðnÂG‰XŠ/‚æ: čž P\TÍĪĀßŪ[§î―:Ĩæ!->;§úŠ‹Õ>ÚeŲæÁ}ûŨ/WŪM G aØĐEŠG}―ųÓeyaWhöúOÞĐŲL·<.Ę}ó2GéÔŋĸĶ‘c†=?BGŋS+°ĶQckéØjōóÓÚ–ŲžéäÃčĄĩë7”2ûD―ĶĩUŒBÃýSzäfŪX4%uGĖŽuÛī(ķÛ ”Ï€bÅB^Ÿ6ĄðøQŠCgˆ‡d@vcų Jã·öPËĪw:ƒ,‰eČ û+3ÛĪųūHnY͉ŠÚqōÄÖ~Ēa‰íw―8,·lËĖŋNÃþģzë^Ð9âķ+AœýÍ[Î?điēūf € ĻĨIâ€BT·,,RŠfQ[v|z‰LTv·™HķÂDÁ 9—r„åės˜@‰a$øT;NŸÞ%tZjJrRŌáŸúėãÕÕž*ŠĩjWŦV­xņâ2EČA<Å0Œļļļ‚·ëÔ@ČįýōcĖËēõfę ïķišŲY™Þ=Đ))ÞŊœžÛ0ó\yޔK!ģÃ$‰œŦ ،V5]Þi$&ÁĒĻģEĢHÓ59…ŝÁķlą­ >ˆ-äB͔]f‚î―ˆˆ‰ gŽyU1"EđōӐNøĨ“œyɑ‘Ņ#0ąÚK_ąyëþlS)QķDŲøaK*;ԟ™ŧīÄöœąezvïyęjNhÅ2+晑N!ņ8rņÛ^ãÂĀ ĶŊžņé‰ģWBĩäag9JÁųV§Ŋ%Ūĸō`ēkČKËÛV/ý+-~sj‰@íwßʐĨïg0ũÅó7ŦŋöXëzĨ2G.]T2!íųŨ—WÞųshýŊ†ļáD.‰9ģĸ™ ĒĮþcVāg0Ęĸzĩ>ÓlÐHīĪ’J)`tHūŠ…(%ĐeĒ™kDd Š`„!ûČ^*n‡§i"g /‡•Ķ8!CšC•r ŠŪږ}ãúĩ›ëŊoßšÅŦ%å-r―ŊíUXX˜Ē(ĮëÎâMļbR-Á‚0MÓŧm†B—››•••––šæý?EüéĘËóļ]ðPvFFšeXDŽķMaÐįĄŠĒؖ­ĻŦ"§Ę ÔáÐm›YÔō5ŌüI ėˆą!ÂHįpį’žlâS‰eÚŌíß{đlâU\<°yĮ6ŒpÁ~idDhóš EýâĒXļM‡ÂŋØ …‰?ý‹īïTA$–Ý0ĄŦR]5—ķ ķĒHđbGlIĐ=ĪqЈߠ Š·ïZýw^,ļjĩ;á|õŌ5…fcÚŋė).^KD`d‰‡:—ļïofĢĸ\`ž™|9ÃÃ5ÎĸõėŽÄnOũXQģÎïÚ~DiÓū–öėÕJbū\žc°Aõ­‚1b9"I Ēž UiNSBЉŒ 柎įe.–âŠē†:—Ee"#ZĒų IZFË^§eģaxîßwâčŅ­›7–-_ĄYó^ģ€PÆÄˆÉ›vÝnwžˆÜĖĖėŒôô›7Ŋ§ĶĶzŦTčļ―KoõęýÓē,UUe&Ƅ80ÏŦy7t]Å>tė ŲũJĶi‚{ÂļOÞAÓ5!+%Ō1aķ@€U4nmÓ„ˆâ]ZŽkÎ84ķ„W“>9s žÆ{-@qąâå/]ĻņõĶĖ9uîfëÎ―ëÔ(ŦĢûņßXQĖĩ3‡^l>cFĮR‹þJ~HĻŋ'7CrÓod›Ž˜Č„øoRpÖéÕ3fjÚŪĸyŌāķmŦšF@€Ęŧ-åŽ,ÓēđĀϊĻm1á o€-ĐÄ Üu‡Sīj„>€&†f6ČŽHqEËæR‡…qQ3*XŨ–ePĘŐHQ|äWĄ&VîDrϜ>uáėŲßm-VĒDÍZuž@…ītïi))IÞDëvyrórži—ßjÃĶT(ļÁŠ7LÓd”9~X Ę,ާķ*SĪāŨĘFŊ(í‰P㌉Ōė)ąE-™!DÖ6ÅĮIYjŅ""$!!)‰Å[•íčP[<‘pŒ5U5-QņjðņRčĢCđƒ2dXüŸ‰BĀčũRА7ūÕ7įĸņõ/RŪfąrbÓēđÁŅEü;ýz"Fžœ7Ņý‚œ2…`˓—빐âðũsčŠbšō\ĶIÍß_ @‰Ē‰KĄ>ŠˆjyĶŃ·<;…nŸ(›LÃVjþˆŲðäwø8uEÔģŦ„åäļt§ŋŋCÍUJbæåzŸāD”Ųb6ōÜÄ{9þŠ‘—ëōØšŋũAU€ī<Ėŧ#†sŧ-‡ŸÖĪÏÐėč“R8ĘÍĖ!ÁÁN%'3“ŦþþB_…ŲVžËˆäįT•ý/Oy5ļŲGÏ·ós:5 {rēÜķ(ÆójˆŒ™mŪĐi ƒ˜ŋ?Õۀ æ"@,AZdp•XLąåÐ @ Ï‰RƛČ'2ÛVU  ŠâģFRP‘1ĒŠĐšM-EIPǚEq Õ1ƒ2#Ę98‘ Žyn^öҟgD`MŒ8—­ pŸÔ4‡Na™OĀ"ÁīL…ˆM ƒũO€ Ž0.Öų8ߒ€‰—FŲ6Å`Ïn3&ŠP.(†eŠÄIôd”?” Ve€aX@oXŽ:.@ũgšŋĀ_ðuĶП‹'ųę`ÎÔ//ĻĘKR5ˆíÉĶFŪ0ū_ČY6šŨÍQĢ”óOĩ5UÏŲýŊNŸŧáÜuÝũđéOu,s|įģ^™z&ş'6_6oltúÖQƒgœöð„Ō•^[žāОĄgŦų`éï?þæ…Z+F·ÝĸÉKówå™Wy}Fj>>kb Ú{Îîųó^Šé8īzÎKë#WūĸļfšˆčŲg·N›1ýĀy—ZĻÜK æWÓ.ŽŸģ såŧï6iQU&Ïy顊Áóf–yôðNņĖč9Ģ+õņčq;Ū%Ý<äę1{AÓПFNzãĒ;ÛQĪî„ŅÓÄ_œÜã…ðĮĮLęÝhó Ŧ~,ķā­^‡6tĨN|õøėWŸ~!%>ðԞ―įĢZŽëýņŧïŸđRaÖÚųË~đō•)ïnÅđŽųÓ/My,öíí›v_Ų?ôäŽ1óįųdôëŊ_đîßĪũĻ ƒ: $Ķũ)ĮÖÎXšÔīi•âü·ŊûáõjsFuV9û›S­ ŪÚ@ø„(E5˜i[bQJ 80PåfČzŒB3Aä5Ųūõ,8â ˉAļ‚·­2°2Ė—V@·žc)d@H—Ä‚\FDžbōlŠbcXĖōĩ%Îėp8 ÆŌŠÖát.ˆIe‘"ą‚"@™ °ĘĢPnëšÃē,ų›.“/!HW5/ Ē9ŠļýY”Č2î !å†!ÏŠŠ ŋðn0H\\ūUĀ3TqIÐĢthNË2cšĀ&#{āōĸyđŒMyŽ›i*ĸ‡’ÆþMc9Wũ,5ēŪ—zpVõĸ0U ĸĐ'þ­ ßīþŽ4 &:qÏ?|crÓOū\~eÝĪރÆÖŽ0cæøÉŨĘ?óņ›ŌĖėPû ýŸw?0ėģáÝą;/Lģo\lóրŒKG^ņ{éÕ>™5lĄn]>ČžtdßyåĨUoÔ-œ6°Áýáų&T{w\ŋĄƒgmó"œûWë?õđÐÜĮ?yũ§A‹„pûę™O•2ą‚/ÜyÁŲkŽĸŅķó‹yÞ~íÏz>ķxýĄ.ŊÚæË>“.}Ōų‰éŦ>·ļc•Ō™ :LÕ=’œ~rÐīB/|4ŋęŎݧ7ïō`i‚(EqÅĘ]=0óģúVjļtŲîĘÃŧ:Døwƒ―TU‘Ó*VÞĀõâNB˜%EŠ!@6Ą"U‰ęÉŲ‘hɂ1ŒŊƃ5&°6įÐU Ī hiDSu·á&BV,[ ÃCˆĒéPðŠDŽ ‡ZPÕ D4(ĩ9—$úđ‚–&vJûÆ9ÐŌ,‘R ҁ9 ›‰SĀ›‚EÆpÍķ,0 Tļ*°Ā .IBž`B.J^ŒÅrKšÞ)P íl]ÕĐĪ7p ŒļMÕTĄ°c‹ŽũĮíƒd ýėt83ÝIÞýũÚ?‘mĶļ]~đ:ģĢÏyúėÖėóÛ UčDíĩTRþcyčM ˜yX%扔Éäæ{:įL8Åäþéøó•=&ČLۓÔm|ÏÄB!EéũVŊŧw^?8~^Ïâą,&*ëÔŨûύ]ܧht€m‡b`L‚ārU%€Ępģz‡aOtЁÜņ=k­šš?ü‰–ý>\p ūÄÞ+ĩ>zĻ’y !F0ýnÖ˜Ų†q.Õ,ÍDģyLãĐŊô/_*`ÐãÍ6ŋ‘e –ÍP§!“šW,ÍĢŸþåÓGŪšōę4ėÅķ5JŧÎŊ?ZaŘƒIŧ>=æ>ŋōFŠÖå…gė7`ė UŧšŨŒģ념@ ôÔðØįÆMŠRęfxĄøĮi\Ĩ؁ręɔlÄQZŌĄ‘]įųY§LĒļ‘#Šx@˜U4>Ę<ĸÏFvĄ%#{iF–íg‰a æÔÔ"+?Ũ%qöÂĩ­Í +A5&4Ј8ûû‰đ0üĸc‚-ë‹ô*Š<) b™āÜ— + ”mĀ”`MŅĪĨ9Xj Z€RrŒ@ĖJmï—ô&0,–ĸ*B"WzAĄ\Z@‚ҰTýũëšĶ`Eä\‘1ȊųĘm!RN?MÎF’#‘’ÞŪØ–­dU1 Cž)Ē`…Hœ Ē]ŅeJ…ķĩÅĻĻ[…w/ô7Ī-ž;Ķb 0FšĶs†á(š+ЇeœÉá—/}ß RĢōŋũ†bĖc$’ŽĄ}J.šÝxÉÜFKį6ōþđ|~“§ KđīQó‹‰*Ûß=ÕräôÓ*Æëáú}œqŋ0įāa՝˜ÞĩĶ—.ĪȐrO|‘Ā‘‚#·ïšņýīšĨük—ô+B“Ũƒ+ņŦ[ÚŋVQgĻãïRėãˆ8"ØĩŸŽ\š9ŨgĨāøb%ü eí°›ē["—M„ŒėĪO]óŅ+·ė]ėÂæ[Úô™EU&­4uc՘Qküę/[žĻa‘HŦŠČÔI7’ģBûœ0ýTî\7S…ôDĘĐc.ŧL‰(qO2=†Ð—u:3Oí;ŽNūpˆ[Ą”yzëæÍ—ŠUŠôÅŨëÏfZū;Ų-" 3ÜČēMÆÝ‚kĄ@DT?ϕÏŋøúÓ/-œÐõ!?ėpčČķ›Wē)FDó‹bz‡įæ|ķnÃũ{ŋh˜ Z@ð˜Ī}ŊAúé}ÆŊĻÓåÁ’QNÆøß.7ƒ(P§°›ZųîŠb›sã"ŋĐDyÖ4Īč ` Ũ,Fd,  ŌkĖ@U‰g&ŠĒi>ž”Þeķā°R:ÖķiCš'ŒrĘ)&H.Ė!qs†Ä8‹C+@! (ŠÂ‰ĸ0öāb–―Eãō>ïŅįŧĒÂUt@đļg0iĸŘlĘ"‹ŠPU.CČD ĀFâú‘HÖ"‡ƒũšŠéĶeÚĖ–}"Ę ķ…Đ i›0UÓ$2MŅ܋`/jx ‘ĻŠYXÏģ4ÕÓēÍŨ―I\ŋv3‡Ä9ŧk“·nųþsq―*éĶuũÁ  õk^+žJČÝfĖ”ōķÍĒ^$$Č ÔŠQąíŠ…Dkœō;6ˆĶ ïģøŅČŨã‘ðbnqÔšAä;OĮMíĩĒ_L“ĒŠûï˜LЧÐÓÝzœþôõ‘/NvÔ%k>ŅēNƒŪ―›Ū˜9zؘąC§ŋ}C/;z\ë5ģ‡=?S_9‘dĮÆ%Ū]þ B!ššdîŒŅ“§ ĸn•―KŦķĮūŅTĐÞŋuý•/š6텉‹ö<ŌmhīûĖ„Q#üÛÏÚŋįģ&™_=?ma6՘å1)—38ÎĄūõļĨÛU ‚#(NÏ\ûÖŦ}uĀsųČįJĻUóÄ; g,~?7ēî˜ÍŋzeôėŨ^uî+GÓϊuÁ8ĩœ‰õŸĻ•p“9š·ĻŊS‹ĸíH.bø­ŽHĻ˜ˆp {âæĄ;dÍ/M:ĶŌT‘M‡îPˆjZ&FŌÓPˆÔŦ–eÉBX%Ōw pŒáåĮ ’ (ĀR,&[ŠeĐĶé#·ZUaâh@9TJ-Ș*ĪQč8c€^‡xĒ·‡XƒWä€rÕLÓPÄ߄qĀÃÂ^ą%õšŧm 7FTĶxU§œŠ^r‹2RĒ| UŒã°iz8Ðę$YÁPŅCÅŦk[•åŋŨĘZjŦ„eŧ龗g9œÎgžčũŲg_ïÝąĢkŊGëVhŎ<5 ānÄ^Gŧō~šŸŌī|ÐR’9q(Bš Ū87MAėĨ94áTLĘÜģļXËëÖœŧMĶjÄĄbŅ’2%įŧ=i'ĖĨŠŒä æy63(ô’ qBFđIE‚ņŨąm3gx@ũĘŽ•—ŋsŪQĩHúđĸ‰m‚Þ]wuų3퐖“Ëœþ›Ä*Yí§G}XŦîū7ĩ:s4ŠĻ gGžTŋÞŪĢŨ3œQĨÃJõ{õír;.ÜLŨCããCüKž·lÛOx‰ÃÆd™aķÍLî*Úp@ŧzą7ËTŅĪy‘ `wi-kTĢL$cŸþ„Iڍœ^ĸû,öøĢ=Î_JōGéf|ĢG;<LŒŨÚ=Q·j)„ÜYĶÕŧëƒĩ*Ŋ]ïõfj Óî3m/”ČÅ(&°ũėŨ+îÚzęĶŧúË}šÖ)Ëģ.uąžr­šL%óßüčЅtná‡Į,s‡Å›]ē"ވÃd1ãVŪŌãMwýV3­ļžpųG?žH.úÔãÝÝČvh%ęŽúčóY4sį#Ŋ-(ýýÖĢrü‚à ûÛ!Õû―9 9Å8Éá(ĄÕā=š•ē ÏßöIDwpŽ|eŨ(ކ1‘lđJrŒĪŋŒB ·pþB `Ã2„ēŦ*ĻĻāðąŠ‹ē- ‚yWĶ*æļėý`Ÿ˜ĐSęÝĀÁ؄.ŠŠĘĄ朊š&'W`/ĐøÔ^`ÄfØ&’Ô ēĄ*Î-óŽ 0WĐÅ%:ԈSāÛJ 0ĩ-QwCßŌĻB ÝsQƒ;ÜÏĪŠ(č™[å=–;"·‚!kŦPï"™Đ‰*4z@ðW—ð to†ĪæšüüéÓGü12ķHŲ •Þ[đ"ŪpáĮú>ûóþëwQôXžz9ĸ2üûSF―’þ% ĐįŊ̇ZFõŊ ā`-ȁ~ÜvsäVWX|ЌG#ËhĖHTÏõðMĒžŦŽ‘ ýÔwɋŽYCšĮ”āZÞą=eúwyĩŦ„ķó :e”Š6ïá0ÝĒXSĩ\wūōžÁĩŅÆ6ÐÝOŋ™ŒKGŽė0ëík;M;ŲĀ11~1–#'Ų8—LëU Rē­›Ļr1gZš‘–Í„õĘßÜēpbÕfÅŦc § iŠ·ĐU“å ĶÖhØŠ&€R…uŽmßđ;›‘sMW‹iÜēĢOĀ#wĘŲ_%w~ĨS˜J #ŋQÃī čæíM^ĒlóÚæHÅŊLå– +‰ßËōÕ^*uíf]ŧÔåŒZ ˆPļR ÄløQį6öŊÞŽSMŅf Ķåᱍķ-ė ˆ)Ó([ĶPŪ–UaĻtÍZB;KŨۃ@Ņ ķDíx.RH‘Š‹UđĻōuWĢæ9ïŨc9*ÔëPĐūžÜ/ĄV2)fĐ?}x4ļÏøŪjĸb.§ÔĒ–ŽtÎe (ãâ Ė•Nq&―cUQÁ0 xjƒžƒÄH™–)Rą‚Bā +þeŲ ™M†!ŅÕŌ·Pį*ŠJĄ$"°”›!ŠĻe!É1'XŋhæÂ°J4j5ĒË2Vũō‰TWu \Āh‹ˆ)įL6m„īhīlq€ĮĮÚnēĄ+úXą-KŪRUšđ0æN·@ RQÍIčiŪŲĒb XęŦ]9‡–­Ā<Áõ‹KEũ\)ĒhЏņÚī Ŧޘ_ĪTŲ”ŨO˜’ĨGfļ/ÞED‘s҉ŊY2О1e―ĩ|`\—D}ÛuÓĄáøheŲWĐņåÃZTũĮŧĖþ­#jkîV§'T-ôdM'âÜß'Dkß~—ōÁ ãą‡c+ø“>L+T"lr‹Č§]ÄЅ†XjsbûpërڌF‹Q­ƒóģ$ÖļõáŪŽ― îĖãJ_=žúãUFî―éʓãŸDhïŪ’xß(Eӟj](ŦÅ­ņ+“ūđNUýMÁĢW Õ ~uÛ+øÏ|éŒx•ö“&ĩpþ"b€1 Œôւ„Ä8Ëī~C Ë‘ fH)s‹†E ē%° °eáÞĢĶâBCL_ jļ6ãwŊI8þŨŠ\l@€þÁm †aüþKpj2Ґķņs_Y_2°Ų?BŊcöÂH4Om!č IA Ā,™›DķB3ā’ŪeĒo k0ļ—vÝ ŧčQūSÐE|Ves†(˜XhˆTUÐŪQDŅhš,qķóģ!a-`˜w‰áô: l&ðŌHHæ*rÆĄųh\9Üà š Ã{ŧ YėũĨNq0+”›P(bT&Rސ[PüB•ŠĐÔŨ‘į1Ӕĩ9xÏ1č‹3‹ý°Dúœį ũdUkP;Սü‹ EUUNlß\§iģ2 Ú EJADßĐũZ8ÆŅū–_PˆĸĪö!N5nŪc“›ĮŌgoÍ:–j ų(ņaÚáýYëOđ?ܗw3Įį󖕜=iCæöLĩx„rųZî—įOwf\SH™ÝwķČg„·íÉÞx*oõ·E1. ’sæd֌õŲÕ /oåLސ•ĐĒÆu Õō7§ŽžúáϞ*ÕÂ,Ŧ YÆËï^{|ųĩĻĸcU qFĸVnžE’#\‡ÃĄi*.@ĐŌšQ!øŨÍh•XĶhī įä!wDWŪP6<@ŋ]ƒŽR|Õßklč@€:Rˉ-V"."t þ'҇ĸČŌåJ81ĸ§HƒKŠ* ú-1!v‰‘Ru•cąÅ ķé“Î" §3 ’1Rũ@”„ Ï*š§šŪAũ•8–9ķ&FbÎeĐ A^Š™šŲĀĢučNÎļ ų2ĢĒRƒ,·@‡0æŦš*hũbŀ­í$sĻŪk@Í"Hô0ã>äTĘņŠ!$jTš%’›ú8Še ĀÔāDg8’ŒðĨ‚c9§LŠ,Rp•iˆ‹ÞŪŠ)ÍĢ`"Þ ;`’æc”Ýs*$ÔĐŲQÁČOeåjÖznذætéõĖ€Ā@­P t0Îï\Õ\ķlHIjl:éūtÕ―ûHŽ îZ„˜Q†ýuŽ ÛÅÅhE‚•zÅa~XŽŲžËAœzläP i8.Ō/’ð5@oÂīE―XŽV$TŊ_Ėņ+ėįÁáŽnĩŊĶX<< Cq6xđâ~Ėōlü9wîÚĪt―FŽ~ņœļs–íæn†(âĸ­‰–7/_ūž’Ī°ē;cÏÎï6løæÐÉËHĶEÁáÉ:ļņ›;eXĪ`ĘgÞžrúė™ģgϞ9w9ŨâÛŨÏŲðõŨßėø1)*øWgÜžzåFēˆ6q§%]žxÕ@ÄÎMŋ””Î0ÎIŧyþĖé3gϧdyĸų Úæĸ(bŪHP.bąJKlë‚9e"—!h2újG`( ßÐïÝŽČU3˜C§" ‹ÆGðӁÄĢĒJMÅ PŠĒkš<`ŒW: x9X–Ī~ųäĩVD‚ģˆĶ*˜pË4(X! Œ ë ÉĐ-4ƀÖ1ÔRĒÛ U…``Ģ!)s#}ØápØŌÅGKĖčč\+Xhâ "—ĸ €ŽTÆĩE›ÚĮuÆŲ-đt)Į#†&–h3ˆiĄ æJō‚ôLéø^‹ÞÔóCHôī҃]îÖëáÔ<āŽpÏ]r“SŨžjāčhꋟ§įäö/U,Ąé[SŠ X-a.ë‡ÓyÍGÎũŒˆöv2,L”ąSĮb‘aïïɝÖķÐė.Zhą ôËyû/[õ*b?NūîÚÕęÚ2&Ū"KŒuŠ9Ūük1‘ÚĸÁčš~în‹SÛ<7ĪGôäë{Žäõé<―Mq8ĢóĖ#W­―7ēö6óÚ%ĶNšŌ(˜vŌ•ĘņßÔ? JŸóԌuëöĩ|jƘ-†Rö3yęÛšÂnä(g-|ü2Š•ōÆė‰ŸŊ=•Ëō*īðęī>Á˜rŸ ývþČÛs‹ö7ĖāAŊūÞŠXÎG^ŽØųL#ûR‘š―ū22Låŋ€ ĢĒq‚MjÉ3(Cû˜ d+æ28LØl.ž%rĢeŲ0°‚!2#•ö†éAPĄJH{Ŧ"Ãs”^Å#ÚULčuÉÎĩX)ĖAF –/uj>óÆeËqQRHŨÃĖ';0ņ~°l,P Ģģ{ŪŠåÄ/čfĘųä“GKW*!ë*·5ä§ð3GÏ_:{™øUļî‚Q6įýkÉiĶĄ+~ҟ‡]ۓéŒqkÞ'7v”pÆvô†ŋȰDĻ„ïܗúíã|}kݍŸhW9št õélWƒĒ‡•ûøŨ‰"xäŠ+û'hü™%yĮoØŠö·} Âk·ļqéZznžāoZfPĐ&Ŧ>č0·s“ũ?ŲŲŦqų;ßYø^ΊßTČÚØáĄq>Ô|pý8ƒ29?ČJ:WâáĐÓ:ÏÍģĒ Đ& ~tÄĖŋĄãËjt˜üÝsÏv+hÉˌ°'íðŪĩîïšŊÖĢĒ™ttõgüxšĐmZ!å[î0qÝ8Еk1ŧ_ݗúô|}uÓ6Uz`$Ŧ öäeįÔ)4›2ŅT˕m`gŋãWí]ĒЂ5…đhG*‡F=yY.CÕÁAЍĪ–ŧ 4TX Zn7ÅõŸjĄYĢ`" [čiB›VņÐŲÔ*ßī–%*Ĩ‰rŦČ‚â–STTL€MĖē5BŋÕĒ3ęĀ -#ņBĒëĘWBÖ°Ōaō”G @Tsą[Ņ*e ėžlJEåŦŽÔÁTV—bī…E_U^1ī/ŒytUôĄĪ‹ĒtHƒ,5ÐĢ\”HXž"wĒcäCŋų’2čtĐĒŌGXR|R|tŠÂ f6āxŊ‰`Āa̰ €ūū8,f„ũZUËņ ūn}sų}~0‚Š‘°ā;―kÎčþģ6L;å͝ŧ&„„ˆ@-ˆ7RŦé 6 úÓɜC‚s#ūÄofŠq 76―ā:vI}x‡‚oĶŨ9Ōœ›cn=hŠÄđFŧĩH&œ<ïB;TL=Öũ§-E”Éččųžã\qjj!wķņá.SA܂=ý]Á(}ðÁ6™ĮwîĩĐáv†ÅiĶ™qíb )]*AÃčĖũŧ”’åËĮøĮ·ïTmúš-Į7NJEÔcx<™þáņ%J‡ØĶĮfJLbīĮsú|fLÉ2e ý íŊød}ŧoãÝ+\Û―7톭 ­ģŧ—mä՚”ED *WŪRɘég9C|dįgs^w!‹Ū2fęĖĶĨœïŒŧŨc™7ön?_xĜđO6*Ę(wčlÛōŲ+žýÚ31ŽŒŨ‡VÛ íZM›5~ĞsnĒęf,éŲ ð†·ŸsÖ}þ‰7ŋĸ•M7FŽšąå―nÍ;—ÞhėüŲM…jā?9ÕbøŸYœÜēózŒ}/DQaZ… ':;–8#&6ĢR•F ͘í›đI!$Yž ÖĸBcīd Œ" – Ëj7†i`,ebDēmdB,Éā"ō,Rl!ė…nYšM' 'øeĮ DD[@åĀÄŀfĢ[ĄT+58ôä8‹`ė”ð2›ŲŠŠJå.ΆōŸayOĶ `+ãÁ―‚:4e"dCžYŒĨ~cė^T]Á4Ļpn@ĘIm Aû3ŽSß&ûvï?~îŌO?ïj2xÔO[OQŽĐëėПbäëPÎ~õ؈3­8üތĶA6ŽpIîÉFωë1éë§ëū>깥cWėþdpƕĢ[OŠģV~XoÁ€Õ›ũöiXŒ―ģL•ō?.ZðãåÎMŒ}Ŧ~Ė™ų\اóGíW[}öÕģĮÖÎ~nĖËU>|)åÆđ+Áe„ŽœŽSWnšœXŲ7ūßüų‹ÚU+ įŸ]Õ„I8ĩ(@ƒ<—Ŧ~Ðó’y–Ø*[íK8aē`Ē―NĻUežÁÅ'aƒB€ļx"L4ĨLŒD˜æļ E8”'e(˜ŧ‚ochõA“Ã}ė €ũbÛ ø œMƒđt!ˆDYiđČlðÃÕZą[:*B3šIķą)ÅTUÂÝ`Ę+ TŠ6H;KąláԆVbĒD›5ļ ÎlŅųŽ>RÄ š H‚Ø[–ųo2‚֔$â +Ļx?_ĸV`ū)Ô6EŦÔyvjņČ_œ5ņšo=WŠýØđ™sį>Ûąhú4LŨBüD0 'Ļ›z­‘é9ēíƒŪÏĐØâÛÎåBœ…Š6{čąrĨâÆÍ_īšY―ÞÕĢLYsdQ\ĪUvjˇ+ßīJlví#›#XôJQ NYĄ’ėZ‡g-ŋðÓæũó‡&^ōÉý7PŅ:7 xöąöŸ?ųý5“å2ôčĻɝj–ßę%[ĀCŌDãhÝĄøË_ŽŲ’…ū‰iØīz<úxĸŲîãį%„…Ä=ØąäËcŽ_ICŠĻb!R0*ôļë?ŌïŅķX†EųAŊÐŽq%ÝÔUā€IčœĻ)1†ĩ9*­Ā<‹ƒīĖ‚išä†Iƒ[hw3ôYQÍÎTŠnU”ŌmŒ€yĒi™0/‚V&̉&hߨ·4nLÐÜ- J!o‚Ę$b°―Nī†iŠUSMӒļH”@r•Ā%&Pžs)æ đÂāâÃaõDĄøE’} R<ĸiĀSð˜e4ÆDu/č}Qô+ÐĶušô%Á_‘ƒ`īČņŋĄ,•įaíŠÎ}ē(šĸ͑“•fSĪô'Cĩ8!bø`baŦU(ątLbéÜüW|}ÚCYqķïûb›^/d^ÞühĮšTķ2oü|:ŧhŲÄ`1ŽųŨiXŦTðŽ”,AH-_9ē|Ő"“<“úx­hDąöĘSŠ<ŲēdóŅ3ëũxŊn7LZņHmZŠ\óķí/}ązԚË/õ- ōBHLĒ_NĘųKÉ ‹ŧpúdf`p°ŠEqÄL.xØLÁŋ amþø3íÛ>;n{‚sėŦSĢBõčȐóĮÏĒV‰Y7/]ejp`@.ŧz%å‚eļũ~ŧ;55ÞĐ ādˆ_t'ãĸc1‰Ŋ’(ü[iƒņ R€ĩ2ē"ÛQ‹IQZ‘YęĪišä}!ČI–HpX (e)KvNązēHzsI˜Ô_Ą”KnĀ­BSBŖmŠcaéýY,ŽĩĄ%9 q9Į“‰OĶÕs*Þ5ãĖ$qnčBܓ Ų^·óįÏįååýVÎ<  páÂ^w8t?þÁļĢDÕZÃËÕ0Hâä4ķbõZž€T1ũ‘Ķu+éĖæą5Š•ŋ~#ŧÔĀy͚ÖĄĶ§|ŧŨ”‹wX6.\Đ•Ë7Kv4ĒQÃÂáŠÅ‚jÔŊsęz*ųä“õ—‰–ĐŠ%ŌiĖ›í"bԈâ 6˜"w7—MÔM­jßŊpÝ4ŦöšąĪæÕ,Ã~ĪGĸĘUĘų#ĐSÍL0düëÕwíš–œÛh~ŸŠ–2Móņé‹xDË4k<1k**D8+ ā_*$ēÄüĐĮƒë?Ôy\•ãįŊŨŦÛŽiã: ŦÝö™a•ŪđĩĮđ\4Y•|:Ū™ęGMÆĸR-ÆHփ ŋĒ€™  z]„Ļˆ3Ž@GšL…OPŪ QĨ^"ĪQÁĩ•9„Ĩ“.Ó Ē EYÄ ƒĐÛĶ /§ų\M]BŠRPÁ † …}VLŪ| ā ‹M,[Ū>us›ŲfĨLô@0w€"TĐXW5î( Œ(ļē)Ë9‡–+W0æĒZEŠčEk6ŧtp…ČՌsïķB€ĖŠiB`ú°n6ؖķŧŌĒÂæú!Tü Á=Þ {úôiŊŦņí)5))ɛ‚!Ð]ƒ›Ū)9‘qŅþÃŨŧīnãuÚtˆöÓНݕšNņáAų{roüžų@ԃí[ëč?<;-)=ÏÍlZ‰NˆÔČ_…ö@~՛īļ5įcĶaĄĒÕ;”Ļ $ÉĻDHoŲķ3’Ē ð+­ÅÖŪSØĖTžy›RRú’Úā„čŽŅĒ]-ĀF3ØU ÁŽcËÔĀLHø(Eް5"+$ūF82-_Ąqb%8˜nĸE‡L hÔĒžjčŠá„ŠUŽïŠV*ęH ŠŪ^ÛþõųļčT”æ&ŕj5Ū fšBmrއÔiþF\6ålÛö‹-Šyþĸ7ŒÅDQJ ž)ĐŨeš&Ô­ĒþË_üŠÔc3‘qĀÓ>_ JŽLJŒƒRĪĐĐ:ÁXö19†ķĻg•|s-ÆAĢ $ÉĨ&5uUÓ*ĘDŧ؇ķ%ŨãŠÔ:ÐkŠ”—Þ6@1/ĄĄE‹ÅĪ*eF…|­øÎøĻ^ Ã`EڃųĪmˆā‹–-E")#†(P`4ĮĪŠĪâÐ-j °hī nčŽĐĒï!E Q]‚Ą ãb7&‚WöāCō:âSâũ\ó1'ĮķmŊÉ1š-Š)â5Ųôð‡ĐöÔW3ú―~üŲđ+ûTCwŨđYÓfĖ­Ý*:þnĐöô·‹ĶíĻōé‡óũd\Ü=åĨo›ýĐv˂‘Ŋ|“žX4Ðí2oaóâčŊ þkÅĐðۄlZæŊvð_Ô ĻeŅÛDÐïœ;ŋîôŲd2“ÞúÛ2čFĄŋVc€—FLjÉðý3 dëŲó;”\ î{›ŋ} ŋ‘S ü?TäũÂg‹%8T–œ°ŦĀzÂ)–BŒŠū'fˆYĒF֖ÆëÝķLËē->äPT yÓÅR `OTSTP$`–ekŠQŲßÔuĐĒíPb…"(Ž0Ī,^“ AE•qnš"q3‚ĪœļbŒą1MQÃãADÔĘ||)\BV õ üŊŠÐC]TNmËĄ heōĻ@ĸBŠįŠ€Ŋé―ĶišČÀWoÄĒ  Æh–ĨÐÁŒ-ãÜáÐIq€ã{QD*XąÛÂ'ylÛ؁xcþ{ķŊýčíĩ‰`44]YŲyþKVpĨgdšđâ (Ŋb•‰·ÝđnÜøĐ•™‘‘•íAÅvýDađģ22rät–!ŋfoæådfdf™6ŋ•wlFíœĖ<ņ· ėØđŲY.EqNvŪËCs_z‘9ĮĶ(ûƙb͚9óåYcŠĮ8Ņ_ûSÏŧ#ð í6ÔßS2Ž<þWŪë·vGŠBþĩZE'–)so UȝŠZ)þ ØOáš)ã°@`6Ū1nm1.ēxÄAä o`Ą7ĻZĖ–Ī2ɧ°ðÆĒÉĀÄ*^yʂ<W Ĩ…ÓŽmšŌ BFŪš„qJEf‡Î/ƒî+ˆ—›âLœY= š/Ēęī€åeģ5ŊŪëģÄclËwSŅø%~ÔĪ~Ū”u/%`4* 咉Āl ļ^ƅ,˜4Lt xiUv1 ŋ(F )Î&đvÎøģ{ģ‡ ŦûķųýQdÜš=Ĩژį{ųæã3)b‘ôýû  0ôĐĶĩę?<ïŦĒ–―pāÅ'›·nßuPĸE7’Lg ßÉĩģyŪGŊŽ‹>Ųmd]zqČĢ-ęØüÞKÖîąšōÓŨon?, ö|Þ·S‡zf”múŦŠöځϞîåí(4čō܄Ŧæ=vá3OęŨģS‹‰û_X5ūįĻÉĢ;>ÔžEóÞŦÞ|­SŧæõęÕŲ~2é›ÏM\ö―ļĮä^™8Ēïî+9:ĩÝŪ4=$Ūlųēaþ úkBQ•Ëŧ6ēóCøĸf•Ãģ7ŪýüdŠĄāÛS$:ķyí·?\Uė•GHÉ6þU–eđWŨúÍĩ4ãNđĘŲôå'§“Ōq+7ýâÍôąðäų―‹{3ÕBxŸ`Žë`Ld:đā5 SpxtMâŌm‘%F &ē/CD(b/4Ī!čÉ*rZ%ÁPĀ­pÎ-Q0€e">WpÐĩ!ĒQ E ‘ÔBĜÛpmē fŌaWŨlQØ2 /ˆf)`rcb Tā#E PNUŸđ‚ @Ú·ĪÄdZ† &'W &Øī`y"68ŒÄLŒĨ·‚-ū@ œ!ļp'` ĘHš/ØēYÍŅ=\ŽøŦĪØ5ū}kvýåcÎä,įÄécYČJžēBË­Ý*†ý5 jH‰Wc æÉMw[AĄApsró„ ZPC% ”@‰íq™(08S##=Ó/‡ųųé æĪŠĐ·Ŧš_!‡ŽsĪ4~|hÕČXŅ„evfv.W!ĄNxŒ WŠ7rrÜÂŊT!ųIQWFZV€n9üËhğ3­Äķ'Ïe„Th3vw2ĄOŪA…đz9dįef{T‡P€óö{큊+ä/‰°‡9& HT\&ąĻX–Ŧ rVâéō·Kd7øOjlÛbÄfAģF…-} Īī•ėãÂS|ˆWĖ9šåÚ ąr=~K8I ‚ĒMŨā•™Ēq°iHôPÂ8ØÝj\^ cR`hi"ÝKE vx/Ē;AE ‡Œ …9‚ å ~―€ņr ýY.-Ũ„üī5$eSVmæ]ÔŊĐ;ĀUn]8–CĮŦŠjÓ{ė…åõķ;Ņ]ã§ÍŽäÕҧ}Ś ïŊnüāûä<ņ\d!(ðđb)‡Î^áÛđëæÍlĀdĢÎÜĀ ‹R7/‹ãSΝšĖŠÄsqãdZ=ïʁKéånŲžÓē~…˜öˈëŽY8}|ĨMuˆžbų̧„Å­ZxõShŨ [hÁŠŒ{Ž]ģ·†<ąāRņ~Ý*úLÉTŋĘ k• MOÉĖý+ÉfŠįÄæącŸiÕĻÞØWūAšãüŪÏ{>úH—‡[=ņü›)5įüöņ#>5ļϐņ‹Ý4}Ŕ'Ztxxð€áë’2Ð/SŽ ãëũfwnŌķũ '_^wų‰vāÁoßÛr*Mã)Ë_øP—G:˜s2Å#j[t\Ųŧšsŋ ß_qAi þŽđ7ęö`ŧ‡ûô˜vü|š@PúĐ-ãFę7°ŨÐWÜžôãŠïö]?þÕóÃgœK§N§þí[ãÆŊü†g'-šüÄC;wîöüūóŠŠüOĪZ‘ĄÆA ĢŠ‚ÓdF+ä}P Hā,€° æ`z$]Eņ Š?ĀEÃ#Žâą"ÛŒ3XV1WŠË"QĘüE!ŦA‹žˆåŸ0 <+P\ī-ĐĢHLáóƒ@ Ģ>+ÎĪ5 hBœŒqAũ· NĀŲĸĀ’ãā<öšj-ÆÄ†6Ŧ4‰á$āÆD-GXōŪLߐ \Þ(ļ†˜hđp${ šü)ä\ČėRLý iƒ ·…š‰;·Rŋųbc™.Þ_ŲmZĩėÔwtÃb)ËŨ|åОc–a˜Ķzķo·ņÝyFŒ7gÝå&$’lC(ą‰í3ķ·ëÛYcĶM2xL@ˇ'ęSuŧMPšgóÚSF 6ðÅWVíÉs-ø+sxÃûo,{į*ËþrõŠ )&`ô%—Įãķ)tÏ(ē8…‰<•/ø@óÖi‡ömÛŪl°ĻHö,z}æ+ģ‡ũã)ZŊIđ(ô—…âO}ĸshƒÁK&īØžōÃóŲf@TđEoþÅŠ—6ŽÚ}<›9ßmܚYņ‰i“Z>måŊûdŧl!žO„Ð\į7ŽžõMŸeۜ4ībq &9NšpðL&ÅI‡|īuðKËŋ|}@‰PE|ũü7NlóâīVÝ:6*.;‡šŠö~ķxÅũŅó?Y?sdsâĘ˜ywƖo·äU{jĘļ§”Ėc'~šSīėĨķn8x‘đÏ-zįpņÅũ­Y°ė@ĖŌÏūčZ9wŲ;ëōˆ†ĸ' đnýK&ú›š—·fĘ2 %9˜`Óc€z‡ø B”…ßBiЉ 8‘č^B„@īéÁį†]Už3+vŠBÓb ęHŸũ­4:Į@Ę2E"Óu™^á~āŪ šČúDÚäbUúRHņâĒn5IDĀ'.ôƐt,:Éð›č Ą}#―šfĶčęq§į1Š*:ÁÂæGē0TzĩˆQÜzБ°E·"’2§{,ž@.EQ._ūYP`c|õęU/ę6((čÎyÚŋŨ„…þ%*#JÔëo­đ†#‹8KÔ !ĄJMŸma„š xq{õgŊ{*jxŒĒÄzz vB/ŅpØÛŦýðóEĩvƒ†Mš†hČŊÍØ7ëú#Üöâō[·§›Q#ƏČČīý/ÄCóÞ,ŋû€3ķTŸĮ?s5G'Ģß|Ïŋh„ļŪ€ØÁ/Ŋ ,"ā=†Î́ Ą=ŨáÁ"ĩ—oúÅū―ĄE*`xĢ…ŦT*záZ™2C‡7j˜ĶĄŋ,Ė\ŦvŊį§?Ö0uįO[Ü&wØI“Gžœ“•læYÔcš„Ķ]æ<óH‰cÃķ#îē}ŦŌ‹wĐû6bėVϗœØķF+ŅŽsýŌaJ™n5"ß5đėģp EŨÞģõī―ŋmÓ}Ō CÕ`%mĮĻ!Ŧ#KþāŅĖôXŌ!€šO\ŋ^ŦK›šÅ#ÔâVüė―ž\UÕþŋÖÞûœđ7=!RB H―WéR MQŠĒtÁĒ4QÄŠTAštPzB/ éíΜsö^ĸYϚË/žž€?ýû“„YúūŸa2sæÜøáđkÖ~ÖũYãæĒŅĻĪąô–ãŋûųݖëC/?I$ŊvČŪÏŧáķM‹æãuvÛhĨŧ.ysúË}uß―gO}mÍ ŧ͈.ŧÅYj­lä˜Ð‹yRˁ‘ŨjPFŠėHq­…WRũUĒž#Įi1ūȍšŧ  s“ *–~AJ VfvNî3č,øģ­^ąLÆLȐ{ÛÃŲ—úg―#”Ę(đ”„―ĀėŽëÔD2ĮđũõFÝ īz54é.ÏcY ia]"ÚÜ@„Ô ė(ļ,â+XŪ%Fʀ\U0/–q)˰!W Î\Y€ÆØęXĐ`ÄTËūĮčÕĮŌB5pÔjIËæ―‡Ū°"YåŦŊ·ĨIrŦšĸČjĐQãv5îĸ|ôĀcėByŋM·Ý™PÇŌÂÕ9`øķ; 'ԈQxŨŠ­O`_đbëņRKnÝÛÐĨí’äj+­'jéķØ{ĢĸGöΚÁSÖZžųŊ―ôÄó];ėžË)œ{WQ'ĨĒ^Q_ō―ļsΛ "ÍxüĄߙĩŪcB‰PĸĨ†“_|mF―ƒ&?öâ,Y #QÏ}ŽýÁ^Ÿ{zįvúáęë}gó2 ]ëË{ŽpÉ%ŨœyݖĮíēŨBøeäęÏO~yz"žôØ+Ŋž­ä’z”õ2õÉ †Soļö>øû;œ|øý·ųÜ)#ûuvV2rĨmÏŧøīQýĻ_+fOzíõ΁#ũĐĨ$‹­Ô Qé,ËČĪT*IÉx_„#”87ĶAe4$öŧ'iæ+d_Ŧ‰*HŒ—(ïšĮƒĸMŽp^é„GyAI2Ģé0Á…āėПE .D]‰Ï‘`já ÜâÜrœå蔯ö΂cgŧÂ-īķ3pã ūZõĨ5JÂl˜œHm ˌØ`ĶĖå uM3 ļ,úōŧã'4ĸdGĢ TĢķœļöiĮ?8ïĒ6H1m3á˜ŋþå ‡í·ßԃü˜øâ6ĢO?lŊ5ūzíŅÛ/[Ŋ—‹·ĄL)ą° >Jī˜B0D1ķ7ø2ÖÝú v\^ËUčĒxðW"VžēܒJĄä@nÕíTR"K"ØKcYŲ>•ũŪQŊ[TĒÅ=h?h.WÂāļ0žkėÕ[`ĪLPCf!SĨf"„@ZpŊeš§DÉdTÄbwœ^”eYËkNŨŌ먄Ś9ræEbŦbÏj@šfĄcÐĖš‚.é’g„EŠŧĕÏÔ·e\ψŅ'ÔY44ÏbYÎ9úOVŧĘĒXãģgž™úđēč·ú„ËąÃČÞý9åēÕ}10xÄŨÃËoĘÐ1Ãŋõ†ũNe™†Œpþ5#xúí‘ŦŽÚ·lôŲģJb‹aĀĘ'sųf{žcčč‘ĮÆFĮðĒH;㧟î7žėY~á€}ߞÞuðčÕĮ­6$|ũĻĄ#jƒVųė—žÓ1ÂzÏŠŒĮlņŧK/xzŌ[CFŽíÓ# Ú§mÖŨ7ŅKC|Œ= ÉIčéRYæÎþų:ï”+õŨӝfÖį7/ĸŲ}ÍüNJGY­ĢŨ€cŋsqÏრĪâ.Î]-k9ģšRbI:Y‰žÚCÔ !Lĩ€oôąL―0ZdĻ.ÚÖĐpW%‰ęJgĢð“_I"XÏ2~XŽreāŠÄĩ6ĩœ9*Á^CޑĮ2z§ÅLðŸa˜+-ãW-ÏAuQ_B|…{ķõpŽJ!ó”ûĩâ2$ŪcŸ•Õ+n ï‚Î@pÞ%ĀõēÓûĮÞ°š)8dąŌĖCŽYAĀŽaĮF#Ā ‘`ÁvĪóeý8ļÍa)ŦH4IīáeH?}ĒjڌYÓgĖZaÔČvoûo–ĪÔwÄ*HéYŸĄãÖÖ|^jÕí?3†„ɊcĨŽd‰%ØÂŸĘЇŪēŅøU)%˜âKô›ĻTÅÞKúĖnĢAös””sšÔ cY―ë=6Ųr{û<5Sf―VÓ7Všr°ōšŲ>iIyÅąŪī&‹$;3^rÕ~­ ĻæBĮØT–Q$öēâšK‘îy"„žį 1ÛíŽä„„íJcŨ“X.æģZ[Ėe}$QŅÝNý%ŌYrįīįMŽXá @͚ÎÚZ”wėúÂZ—'’Ē4ˆbNØ43SCe/ÎĀÁB>ä’4ĻŅđ–Ų€ÉˆąöÍ=ÅÔŅŅJxŒ’(Q,*΂s†› ”™gŲ`)j9ïÁ‘úķ–Ø•f[cL§ ™c}ąāGö° ›]ՐˆZcVQf›$7wŒTÝDzįļÞ(ö=@æ4ia SÖ07Ū@î@ŋÄü ÓŲ™ãŋðõ·ĶL{ėķŦ;;jÔŪŊR,Sũz•™˜EÜīpĩ^ņþ?|oĨũÁšóä=þh|)ü_đxĶ|ïļ?”Ø―ĒRë>þũŽĘbņw Xx˜ŧËębíގ-ÛY]sØ@%ÂĶŽ0ēķ,K\ĸĪ*KŧE,>€#“H\‡ÕƒX č_^]OB0ŦF§åŅ<ĶZš•*a&Ÿđ”H`]Č|(Š’ØðŒę?cįpĸd―p–sĢŪ nÚmIĀ>čÐV šĒ―6TC˘LîЕ&fþ-Ę2™3W†6ÂÔVY2đˆR‰X?.ģ–1eĄŠæå’æcÝ ķ•etúŒŠ°ÓâX“&ÏžûÞWߙVïHïėæ;oŋû˜#ŋPË3jWŧ>å>(Æ1%P;sƒĢųä<ŦXÔ &Æō‚Bé*H°ý+FöĒÚ;Ęhļn2Ú&˜„C0ÕšjG;đʃL“=ˆÄAãly jn4qljĨčƒËr}LøÞ€æBÉp2ˆu °Đ%v EÕS›ĻV…J?ö)Ė€Æ0ØXøÕŦĄŽ&ŅŧÐl=YÄ—ĨP ģEŦč}Kę]­–ÛÜÜ U4‚ļrJ0n úYLĶĄąžXNfL_pË_^ęŧĖĀíöYsĮýÖÜ ĸÝåsk Y%üþÖëûņ…}&|ä$WŠųŊŋþ6xQ QüÃï~óvWIZ]ģÞy}ú\ZĻæ―ųØïoļé?ÐA•S_ý•—ī^{íĩŨߘŌHÔŪķÔþϒnĖ*f0Œ>7Š)Ó{ †ģ"&ەō€ ÄJĄz>ŔQZ1EK$ófŋev‰ô ËNN`Ī vr…ēĘvŦ ŋŨ2‹ 'ą[Ð,vÃŨí8fÝŠEØ&rBĩÁVL–ÔÐÕhĻķÖr†_„3ÝNķŋ„Y­…æŨ2œJ?Sð.Ë4 ]˜U(ájÐB-!;XŊJĪvcĪĨEœãyģË0nøÝýđ Wf‹ÅY,įïL›7u^cÄ Ã*—’Í/|WĖޜ:ũėï^Ðŧgm›ÍwōüŅPÁĮŪ>jŦöúņ=oÓGւÉß>ó{/Ïh|•ņĶó9úfZĻfūúāég_V§ĸßkÖO›ļųîŧïsÐA[lĩõ„ÏþJŨG##>HdfûĮö8{q–Zô†YÕëÔËTxKÄēcŒ/+Hn”QR!ķV‹†;čwâ―3KŽmĻ(âāģĩÛÚ ,8耓˜a{°―UÛ Fķ‚QÁ ĒPÖ0Ŧā­&„Ö;ĸ'’I Žob `B G ŧđ0Ŧ%ûЙļĻŽũpW%ØŅÔ†E[―ˆÍm™’VīHsā\JÉ{g{Ã9ĀVĀûÕMN`,æęZI”īÎLâœz<ņÄÓūyúŲ3æÖëýá†[þþðÃÛî°ýúmÞĩ`ÁGB/ŧėÖįžüëÍŋđ)vÏ “HcÞĖYó°y‹’jÞīé3$ŨŅs9Ï^ŋ5ĨT.˜ģĀ ŠU1cúô™ģ[:7jģýOýō†„*š›ú3æˆëŅŅ1ō@įÍnug60VÄ5ĘX•ģgĖLąĒTΙ5s^WD&X  /õJČ*éÞïģ'œåO~ōãïŸĸ™Ņe5tãm‘…7;DĪG؆_LŦ-ĩķÎŊĐyŋÕ2 3+ã˜+•ąY§€3ÃĪ,ØðîŊGŸ ģ­ÏÄ&’"­NoD0Ē=o0 8Ųk§§Oâ`9U‰`„ũͰĩZ-IJÆķ5•L­=1 …ũ,°ÁšÜŦMũ_Â0áþX–‹nD3Ąég€ĀAՍŨ4[Ņ6 7_Ë‹ļ°‡į·bŅÅðŪzīï!ýđ†ôõdbÝ"đģ čĸ?ģ|oåž"―öŌKŋŋōŠß_ũ‡‡ûĮ/.ŧtØ2ËL<āđebJŅ>zû]ÓŨüƑ{7!ŠÏODņŠï~č`ŦívÞëˆCðæ”FĒøĖÎ:ėˆCöúėŪ?úÍ―õY/Ÿ|Ä^ÛîēëV~öĮŨÞS―účýå "šzïoØuý]>sȉ§]ó?!ŠĸöāývüĖv›ėþÅ^ë†(~ÐÄÝ?}ōCONūüø‰ĮœrėŪ;o―õ–û\zņyŧíīÕŽ{į3o+Dņâûˆ(Î}õÄĢøë‹]#VZkĢĩÖZķëÏzþ7îôĸ māýúõ#"ۑYjĐĨ,,•ŋjK-w“"E‡•ŪŪģWa4’pf$ÏjÏ2X€š–ĘŠ―~gŠzĐâ BJĒrë=ˆēIDHļ@+JL TUQš†'[#ŋ(%OÉj5“Ԑgž‘ŧƒ Đíđ:ðČ5?œ8he ĒŊTg‘ûЈUĘ2X ĘČ-8™ƒÂvp+gĖ6Ýŧ†k žQØC vƒ1Û6ógzú4y@"ÅrņvķpÆāņŸ Õ‰=[?nûÄö;€$r1–IO[­Ģ8·Q6sŸ<úĪ•ÆŽ―ôÂóÎ9ýäÓĶîsØŅSýÓįL‘>ŽęŨýîˏŸxÚ 'lŅóĨßÝõÏo.oþ}úÄSŊ?ÛžíNĒâ{Į}_ķŨ-·Ýð•}ÖÉËäĻņöĪ?=ðÂĶ_ųŅĄ{ŽŧüŦ_ļŦwómwüîŌÏüâG<85Éžį^|fŊýĨãî~Åí·ĸhũõ—–Š‹Š>ËŽ{öwÝyÃOãĢŋđįųY Q|ö‘™CÖû؟OYg…ü‰‡î|:ũã_þæÓKû ‡wŌ?SXĨknŲ­ļâŠ+ްÂĘ+ŊÜ\k^ĖíYmĶō• (ËCŠÔé;WFĖrõˆVdόE0ėMaę)ąĩå!Ð*O"ķ-l”ŧbl=@‹pŒŲžBwðxŽ’Į&ƒJˆbėtvZ"ęŠ%Ņr`ĮĀ•ĒĶÄÉoËlš†Ā­}ķŠ,‰:͒īŦmLyâÎŧŪãé{.ļøŠ7‹ŨtÓ―‰hA·œxÐ&+ 2žoĸ^ŽĻëïók[î°aĸŽÚę;m5dé>)QŲHkl7ņˆMVė™w=ýÄÔ^ëėØŋ–YkûąÃãߞĄĸ;úœŠ7^ȇîąýØžÖóņ[eĩ…ïDšĶL>wŊũ;þŧŊ͗$ļ™ÎÞŧpðȁ}I>hôįũļ°aCV[õ3‡ė―ĘōË,ĩܚ”æZo—u—xöϏ>Ë·ïpðā­ļï†Kė?öā ëÐĸE)Q°Đ°ÍLķ>}úīuvq‡(„o—į5[Kĩq§Peŧ­YČSŠØËrÎiRĩš6)‹ē*Jįđ, ðęžN~1AžQG ; JŽ\Q 1îīlVėî]”ÉâjŒóÝMÁ19§&aq8iRyîÞ&H*ņúŸŠ,cÕšíŒ.8ŽĸzįY·Q‘Ī<äDčĮ›•é ’DKŦŠB đ!D9Jīß4†šTlXų@yfĄđæ„sÞĩÜ\Ž­43[3°wfž ,ņüâUIR-ŦũĄÎV7î°ĢŲfâ„}>īWŊ0Ļ7õėˆI"}p=rûŸĸū`ÃŊíŋÓ§ÖÝâðcÍn―Ú Š,Åĸ€(>þÂŦBôÔ_ï~ë­DąG ĒØ1`ˆŊ―õ S_|öU9ė=Ň^šN4ũÖ[îzŊ ôíÓ9æíŽĸņ™'*DŅ· Šéĸ@å…(:7tŸí·žôËûĸÕQíĄÅjÎsž{ÅŪ>·l_ĸ‹TĻ”ĩkņÆÍ0kvl–%$WYT>° ™ `å"ĶÚäëķp9Ŋ2Jä4E@EãÎ[呎)ūԔ";ÉōZŠb―^døŪ•,ÉØ‰ĨMK1pPZB†ÅÜVf!7Ē ļ8ŧƒEŒ― ĪŠ§sƒ`ŲFÕR=&˜ĀāĀÅ▐^Ū/5<ĐŊ;•T’š!˜Õ„ŽyŊ’…›âVXą5YYÂËK/ôã* Ãn;J†ē§ZGMûÞ(Ž[.ą„L ĮŪKĒw,īļ•ũŪGÖčß)Ą§cĒmwßiŨ‰;ÍZ@EĨĘÕé„āčJo]͍ktáÛ­^ë–kÖđāŲ‡#ĮÔÚúŨ•힇tĀ6'|sŧ~݃Ëi 2DÝÅū_<ûë_þĘéŧísMŨËO.ŧïa[/“ŋð@G]!ŠŦ~yïíũĸü.w-·J―zĢ^,Œķé;våOđôŒož°üėÎÆežđ鏎• &-Ņã/ƒ(&WēA“.ýmšÃÎĮrþ'ŋēBéõ&swfŋc·[Ą-4íúĀmąhãTĮ­­„`0kŋĘėė;ļ%ŒaƒķÔ g8˜2ĀÕeI’åXj`ŌųĨsš° Ŧ€ F3Û„Ļ•žcr8Ž' hA1ÕE8c•ŠJõ7ïϙЭļb‡øLb‰^™CŨSKœ™RIúÐ{ŧ}ŠdÞXðÆĒ`Lá]„Ŧ <ŋ8`ŸMïŲžhQųæ:ŪôÞ@Ō ÞüdLė2'g―RbÃlk~ŊVz.œL.ę*Dp‹ãbî€%úé)ï<ũÔē+-į§’æ•”9–Wž{đĢkúŨ ŠÐï g^ŌđÜędÅ}Oŋčũoš#jcÖDqõ}ÏünŲË­ąĮá·/=vŌ[]cÖ[Ôë#;(íÓüĢÞQ\rÕ=/üÉ {âåÐëā6Ýī·§·ýÚOĒØ9þkg,đÖ_›Åĩƍœ5{aˆb―Ođ`Äýv Y~ԗ{áy―ü€c~ú‹#QėąÔ—ŋóó^ÃÕL°·B‡ŅÖ{ąžôÖ1ëĻõsÏßúŽX™PC>ĩËÝŋß|åū-4íú@ąĻ Yn‚kī"CÍ0ëŽÐâU ŽUˆšî†Ų’• „(䙁ē‚AÕëuvlē*õæŲ:ĘŪn6’ĄĖPa@î”Ēsl°ZģÛúIJ–i.7!įõF–gĩ,ÄĪEΊ+ģŧ9ôãØ­ ģÄa&æ 3ļž…:C§Ž‘?æ$^üo–ØwûÍĮÞqį#wÞ{―·ėŋõúcęĸÁÅÎQcÖxÏՖ[u Ã}ĄqĐåW Ŧ|Ė:›Ą… „‚Ú.7ö3ËĄiEõ\bø*vĄŽïƟމPÖz/Dąĸ°Oo?ŽõGËá]+ãQ\ztëņ’,ę―-5Â.IœŊ0fí…ŊģzĸaôAÕŪķÔÚ UBÖķI9ïīc—wä8tŌg˜Øķu=ŽĻĘØRÉ •eÁĻŽ­°kÛЏÄVĄ`>#æŠĄaĻa@/ΊS:ØÍ“5žÁ!!ąŒÄĖĩŽš‰ēķژâĖßh°‘OŠD’Þsī-ĨŽ7TX+Њ‹p‰pbŅU:Ģ1X*Ēŋ8RZFáÄZ:GfX žģԆdnÜŠPg.lp­6JáĢ™―­#,ē-–7ĖnåUFŊžĘōôŋWûī§]mģlēŠØc ÂēėEkÐē€=1Ö"Ēo€ē˜ÎføÖümĨĘ,°z kJč.ƒŦā‹F–…æc`‡ZgýŠeø*/€üxUíĖR‰ĄûĨ}ĢWýM)Ųú–YÍL"}™[ŦĮB†CDęĮaó8Z€X…%·Vč™Áž EÏ Ũ$ĮĪOã.<>‚…„É―x“Đ IuĐRã9ržē°w<_&ķÅļøþÛŪvĩÍ^D‰bÔĩÚĖ”ī*+ ņķäD,æŒrė ЙŨLnpĻîõgEtŽĩũe,xv8ˆFķŇ0Ô<āĖŲļŠT”B^éĄ8‰ÝŊE0~bū4ĩLH§čÞ0ao-*œĨC[iĸū;ÛsŽŠTĐ1’uúãkÁ/QÔSē„GīĻŽ[áfˆMS%CŽĩ˜ß$[pŋ!Ï>VŠŋķë\o1A‹ƒ2˜ĀôÉŠwĶÏ|慗Ō'ݙßŪķŊVÜݔ­ī!…K”.Ũ`ž{-æ.Q ‰IÉôŦÖY‹ĐīĖ)ƊœË †%@•І‹ē.vUb։Ž0#9fIĒ+ ,™jŦüÁĩĀĩ$,Xģ`iœC.ŨcŪĻ ŊÓÁhÂf.cōk‘đz†åū1ÃLÖĒ<ŌԌÜčq€FBfĩÕļ― 4ķ†Ue!’ątį)dzw.%Ëū $’lI qjdIމ‹F#8g€‹ģĖCČžųDAgŒĸÂąŧptĢQPŧÚõI> čė œ{ô‰ ļÚ<6žŽHF(0ÖDŧgĢOeĨÍ@ÍĶOcIA7œÏkĩēhÔģ–gQĪJ•ĮՊFé3Žg‘+céá!(‘â%Ī;ļŠzÝÐ/ˆ80‰EŅ-ÞÜ~ØŪ0Ī3†Ž{VĀvĸR!+<Ï;Ą`ЉEePG―U{šĐ59ņœ…ĢË9/ÄŒÐÂÖFJāðĖz4į2ôÝöW§W†q‚1č0Ÿ ēt’‘t-°yņŦ'ÍļûÞŨޙþ^ˆ"ó}ýųŪ;îųęŅ·!ŠíúĪ/æÚīÅ:@ĀAŪĒÅeĢ!ĀuÛ֖:™BP͊ėš! ˆ="Ð8—ĘdÁˆĪ“€)P;–wƒórĖW@Fģ<°\ĢįõžŽRĩ •uōÔZLĢĶ‚'L”›e.ݘ*Õl§!G–Å[oč.Cp!™C‚YßX‰*2Ũ' ĩsĪŅ’>ĪVv$ķ㊊Hp°B{þ"ä;Ö~œÅÖŌ į5‘d4Ēvë˜qkŒ!Ķ€áÆb Qžõ//ũ_nÐûŽûĖþãvÞŋųĸŨÜíók-đŠŋîķ ņų―ĮD16æ―ý曍ŠŽ^yðž?ýéOũ<øxE­*fO―ý–?ĸų–{^zc6-ŠÕŪ6ŲK˜i‰e™ŠĘÄ0ĀŋĩõŦ”+|QĐŲ‰(ģ.Ðī"jēĒŦN3° t‘HđīĶãĻ͓HLˆØbŨ°ïãø^]ŊãÎÜ ‰XjĩÜ1Р'W*jŠY56&aŌ}\-W ÆÕ ―ģa`bHĩŅąoÔëD8Ú1HMkn )ëĻ9]Ĩ• ý<‹RrĒoĐ; ÁmQŅðŦDbĩ—"0ÅÖŦ1tqË ‡ÐudđWŽ\°ī1Vž9kzmįH?ˆâÜĐóÃG-)ÔS˜Ũp]UöƔ9ß>į‚>―jÛlņOQœûė]§uĖßg5Ķ^}ÖxÜužþëG~þ3{žýFUS>úÝūþ―+zÖ>Gĸøw‹* ķ]mĐ%ļ_ËFĐ‘!Ī+%ÃŊ€e§=Ŋ‘F•‘’:L%á[ĩģ/ÐZ”ƒX6An“SVIÂ"Y’ZG˜Ģ@.ģ Ä2pdƒ$ķ<ÖJųƘ5‡'JÎ1Ž i9ã|ƒ&ŪģQ I―™ĄoítËá§įŽIW‰gaˀÐv;xĩ@ :Á€ÝēóŦ―ĀČØ˜ŋ–lÍy—Õ4ę\sÃPÚãëgƒŲ(‘Â1+-84ė ‹D16Ę8ŧO8á”o~ó,ƒ(^wý-ĸxä‘mwØa― 7ûpˆbŊeŨþŌ7Ž]ū/=~ĮU‡Ÿwϑ?žôĒþøŨŸųĖ]ßĸÖoŸyó‘+ođŦßåW^uÕïo=ýsÛZŦ]m‚˜=Ē€Ū”Ô”Jā`54|!AėTÖBPŦVĨÅĢ%„ĒjX…äSk*YČ"ķ‚SQKæ ŠS%ð1Í2* Ä(Á1V陒…uŨj5ąs0K:PÄf^Bž3VÂāĸõĖ”āQĩ\^ņøÂ^–-° ‘@ēÚOlũfÁî|göYÍ2xuÉyUCôÔÄú.ØuóØß·prïBk ė]ÞŲĄŸX€‡āŲāßlƒÜ ôÚI€ņ%‡ķ:Á+‹7DqÍq—]xþ9ßĒXu͘<éé9]ó_{göĻqŧæÔŠÕ7ŲiÁ‚ý˜ýÎ8eãķîöãŋqįSÓhQ­vĩ}ĩZ‘É%Ë čŸÃ—w·{J@TÖ―j+1R7\ļ•ÞQbáH(KÕS‡ð˜”`ðæį§`ŠW”’īÝÓĮ@xČļ…/Ā7}Óĩā\@šH#oË /ÁÆŊzg0*ā5)`áB™(qÆí5§Öú,šcDôØð·*Z ŧDœ$1Ą„ŦÂæĄÅ*GWnH&į2VFËgcG }ŦkôŨg^óÍPYî$īļBģ%—Ûũ°Ģ{öîõÆãŪŧŲæËŽđ ŠBį†þ=Ĩ˜ĸęęŪyÓßtÔĢgïþûÖõūuč›>xŨ“&Ϗī(VŧÚRëH^ÄB‡Ąđv·bĒ…ŲŦ-•ŠĘĀDE„­ÓFĢ,";/ -jTŪ qũnX­K}ŋ ;ļIĖåƒģ+ģîŠ 'Lķ<æC*mIˆl`‡ÉŊP-ÏŲ·öŽjyM{p1ĻE\ ›_.‰õĘŠģ*z.I"Į €äAˆŨ™+WQËŧЭœb€Z‘„ °dŽÅK:g– ôāHxtšd^ŠˆÞ@_.I–7l Ŋ$°ēQ/žnĘe*ũˆ° ÎRuiq†(ŪđæaĮģí>{5!Š={†Á Qė†yæ}ÖÝ|‹§o?įïS mŋúÓĸrØš;ũ™7o~AKŊšÕđįėÍóžš2ŊĒEąÚÕöÕúĖk>‚ó§üН{Mę;―vmUÂ>k@ū!ō·™ c –"ģĩi×pbĮŦŠHÔ"P5‡ŲŦÚDk™t2Zåē*ŽÛjE(Ŧe ņ ÞeLŒĨ†DΓP™ô>― ·DĮ ás%žØ/ _ïjļ 8UøTZžĪÝ­|†$ZÞļRYƒD_ώŽQķ……Ō@3ę`ð fŊĖđá0p@úŽĨOÚ_—Ųā|Ģ,-ÎÅ ÅmvÛqŨ―vœÕEŠbúPˆ"JR,Šz!žé>§;ų˜vŲnå–ž1éïŲč įŸļÓó7Ÿuâ%ũõļä”<ŧÞøCWęŸÓĒXíjCuuĮG[üBŪ$Ė;mŽ)œ|ž1QĢŅ`͐åÞqeqxQĢā*Ļ=‹Xåûä2MˆĒįXМ‚Lj =ú/;ŠûÜký-wXáŪA+lžuđÝŪÅ`17ød‡þ–A€™ŽĀáoYâv|ĪýŽ ‡eI*1įȀIĨŧΚŲÂqs}ą‡ųĶ$ŽyđųF/VŅ’ZąeéČpßĪ "Ž+āËx§,ÄF‚7A2Ĩ09ræL ģDm$ƒC‹Š+äÂÜĻw‘ĻqÂcäę@āmeįĀ~ĨŠm@Ī”ãÏ'Oķ>›ûŦį.—uÁĮΰZ †ī„1—āũ;žW6BĢööIƒ$š$éÂņâQ\iååWZyTĒøAÕŪö% >Čk9‹aåđh‹gũ­íþčáóQã9s.Q"KæÖ"yq.”Óŋ^öX,Œ%‰ðĩz=藔2ģ7”1‘./čG—Ĩ)5ūŅ 5 ĪŽ#Ä@hÚþĨ*1zlë… Š‹ø&˜ RްŦ–QC‡Ņ@ÐEØâ‡Ë}p’H’€åĨē*1X•^‘”cðrbŌįAŌ‰Ęŧį.kDlpNj>ƒKŒbÝ ‰ÖV{ fįÅ1ðæĢ%ĩ]íj;)ģZ&1UH{%í1š4 AQYóLLÃyĖKĢĪ*a<ęŠXķĖŠ:r­p:äSd°\>/(vÎ0`Ų‘ Ī<ËØšÎāó<3.ŒöœÁ‹$$‡;å:–pđ"Įž\Ý<Æä oIÉdŽôÕjÎJ’û@ÝãWã‹Xøî­Ĩ(†îķÞxWTÃ( žÅęøÂcLaYo“ā<Ū$Btl“cå3THĘ%áH_äķeÁ™…ŽÛ‚ÔŪv}RNϑ(#"J!PÅUūÛM –ņ>‘ú·Žm,U%-f1Ķ ÎÉÔm)ÅŲ―ÄĘViIX{ÞĻb]a•–p<ƒÏóZËޟĉ+Ëh‘šúéĪbí90qĨ-ÎÛĘl™u'„˜(ƜÕoúöb‡Ó6$stĮę?Ã`„ėė zK’RQÁkĩäØĮŌ7y­€|0“bdŌî8&=ōŌE8@i+ɂ°•ÆPĒēŠH&óŸØęvĩŦ QĖŒMˌ/é C -ķt†5ĩŠgô-b‹ÁĀVĘ9[‘z7KĄīŠ•6ęuėŦց~ DQlÖĐe&Ö<Ŋ―aŠ€ķ­ËĪ ŊĀf“_īÛÎ\AĨ>FōXæ0ē*ÓÂÄ ēæ•)pūzËŨąĩÚ֐„=ž•ū€ŠūþđÔō gwæƒÃú/ķ6ēĻŊ€ÃvÆ-đaØį†gŒ$vŸąĻ ‡1v…ŸtņäŌN›6yōäųóįÓ{ŦgϞ#FŒXjĐĨĻ]ŸðjÏj­) ™VByįČĩϑÁ’ØŠ+âUpXxM•MÞ]=` UKÓįf‰I[@ąu”`đęA‘MQĢ–šē“[zriy‘‚Ŧa-"Īð;ėĒŲZcvаŅûa°Ą­`Įˆč΂ĪU þY$J”b§Up_™\Š(ŋ1ËĶŌ‰X$Rú‚$BĒ]+―…”ĪŽ*oØRéGókƒ]G" /soņ9Ž}žۖāÅq~ÐTØįŸū)ĐK.đ$―·ĶL™ōŌK/õîÝŧWŊ^v…)ÏÞóð$îŅũSãÖŌ§ƒþƒ%/<|ĸóoOwyŅŦŽąė°%<Óĸ“jW{€Ā°œP`ĐIĩ'óŒąh–Ŧˆ=Ļ―ĄĨŌæ!ģvvU‚–,WéYį―ÎŧšķĻØ  0Q1SbIIĩÕŌÆŽ•&ĮH‘‘”bðĄY*{˜N˜D;^ĨØ(A8ÔŦ„ Áb9›#Ŧ{—Āč2ƒ­#DÜļˆ•ũŒPœPEīÛėƒR +šKÎÖyÝë5ŌðfÍRļLŪÉiIÕ\•]GąÐ7FąU`ßÚi&ŒEˆŠsŒ2_4RfΜųØcŅ?WsæĖ‰16Ĩ6{_ >ž)ēģggvŌã7lĸ™―/žęšÓÎ8ųī_=Aĸą’ú[î·óøCŽ―揷^qþąûŸö―fŌĸĢjW[jÁ{uæ1' vI þ韆ā€ļÖ―)r„xIˆ\ĖT•##.%(#2U€đ8‡ÕƒXE[Ŧ•ĪTŦNWâ]küęÔR…>‘ˆ]gŅ@bđ|īŪ{‘Ļ~qĒā|ĢŅ0;Ņl‘pãĢ ­eßx"Bēƒä!KUdrBėžŠđƒÕģZ}ŽF`ˆŧĐ!I"•ņ­†l!c˜\(” ^yŊJ]ŽÏŽÃŲf.L6~É|ŦŧA-Åđsį6ųÜüãg͚EU ĨÞWV|PĨŋßsË;}6úÕ/ŪøÃŊŪ:jĮ•ĩ`îŒ)SÞYP‰­“ÕëĨ@-ËFWLxPąėš5c^Bóg͘:uęœų]‚kΞ>uęīŲBïЧožčŒëĸĘy—ýėG^võ ß?jŋĨz>köÔ)SfÍ- Ķ4úX4q9ģį•D2kÆī™ģįŲ‡–Iâė™Ó§ÏœMV­·Oĩ·Û ŅžY3ĶϜ“ĻUåü9ÍŨĖiÓv?ąRkð#pVeí{wkķ+ödQĩ$)d>xĪk„xKŨÃčő,ÏÆb`˜TÁ“Ļ(ģ#‰*ΰĘRWĢ‘*•Ņˆ…*|ĄŨĮąBƒ ýbrČū54—8Æá“Į‡h§)Â;H֍ƒ ƒ"˜ČƝĩD:åuÁA1#^LŽ)‰ˆ# Ríą$€Zņą ی ė ° A&ÓĩŽ ­+(3`Qē‹dũ 3…=&U1â—wiI‹@Ų_{ģą―ōĘ+zčĄFĢAZfíĻÞWĶŋôaÅ};C|íĄGž{ā ĄĢ‡ö%*ûų{n·ûžã·›pčé/ūUĖ™|ËÁ‡|_ÕŪþöŽĸė-“ŧHf\zü„ƒ>xÏ}˜4·~ÏŊÏŲu‹­wÝm·3.ŧķ+ýüŽņ{ïö™mö8ïWąšïkŋwÚûuāĶĢ3ĶÎ~CŨ\etß~ûĐwß}Įæ‡í°Í—î~îuŠ3~öĩ GžøÕÝwÛn›­öúÉÎÝc—m7Úpí[‡ĻžîĒã?úĪņãũØpĢÍÎþÃ#ĨÐÛOݰÛnÍ·ïūÃķGÜûüDÅĩ?>áČŊŸtČŧlļɆ—þåúá­§ūķĸÄÝ'ėžãĄ§NžUæĐ]ŸĀĀ|ĩMÞĐFĨ*f“p„'œC“(^U,D­ĘĄĩӓ%•_‹K`ąđ.š\ø "z[}ŠĪ…w…XÁåęË"†§ētpT͜\ŌÕ^õE'FęĖÅÂŊ]PĒ -…=ËâlsÕCc9æĄq5bÁqVY•Ý2Wˆ:HØ-+KtDb?8NŦJĸ8óDķlīë.ŒÁ‹ŲQČ=;ŊžĮŠüޛÚëï-DþƔ4ŽJëá‹ļyZ4ĘĀ@óæÍŧųæ›õŦ_―óÎ;ÞØÖëõïŦŪŪŪ: äõvüĘŨwYyüv›ïsÄĐÏMM ^đýcļõŨ.―ãö›7=ņŋ›>ÞsŊž,0EŋõÚŦ3‰$>ûČ}o-đýų?ŧĻßĪ›?ũõ_nuōOnŧýöØuÁ‹·|áĪ_|Þ ŋþÁÞŋ>ûÄĶ,wŨüÄ―W_™Šj΋'ōõĩ8ëö;n?qÏpÚĐ?zuž<ĸũû_ėąÞOŪþíķCžûųMÕũ/ĸýۍšîÏå3ž{ęđ—zŸŅ5ŋ8cŸ‹Î9ûÞ§ž:ų°ãŨųÜ·ïļãŽãũāSNýŅÛ]~ö ŧį‘iGĸčįß9xã;Ūŋą‹äũįũÚ2ŧþéæë7iÜ}Ņ5H­ƒĐ]ŸžÅ\KgïĪJ AŽCΠÚUŠĩųO§ÎAK-Ņ)ÉĶD]úۘóÐsČ2cŨØáðßÞpË#O^wÜįÖI ęBíúäI-ŽJbyð8 Šū^DŠFéØwĀ{€-Y Š"Ķ„(ý>Ūúë ­’Ė•E;UŸîØðĀÞĄÉKYȅ$JrÔ /€1Ā5Ŧl”" u4ŽĒū1x[ĸerÖÃ:rž―hwĖ „G6„Âī˜õÕžāŸĐĻ* É}Ģī‘Ŧ7ƒ-iþy&,EQYðxä J 5G|Nzœ,7‘JyndÛ(ð ŦĨŨ%Â`Õ(FŒ›—(Ýþ_lŒ‰SÛ/=„…J)5[ŧíķÛøņ㇠öQEoýŊ{_ĨĪaÞû‘ôÛ.=qŦOïīïūãûáÍ{ō…U?õéoėšæYûnģϞ;œw{Į1û9zOuūđũö۝üƒß§%ŠdĮbE̊BDK­ģәŽ8ãāwÛcŨCūuYZvĮïļÉYïķũÞ{|ÔŨÞĻSwđ펾č [ŧ?―Õø―ũŲcįmŋx…ģ:V:å[{ĸō„ ũÝóÐï^}čQŪ<ĀuuÕą”ÃUrG§RÕÓŨJšíē#ũ?pŸÝŽúņg>īŲã”ņW7ūųö/ĸëÏēĒ­riBĒnģũ*jPg1ZBUA"šŦ‚ë‚WlTâĻ;ų&Đěãž_ïbn“Ķ”'aüúa6o,éŽ-qÝa-M…Ö1J†‘l 1gšL ψhKÃÜ0- ÕÔŲ‰'öíۗþ‰jΞũŊžōĘ Aƒnۙųí·ßnNl›ūZú â°Ņ„/ũ^åÅŲóЁG.ŋîļÕ2Ķ}ÏøîęþíÕYÕЕŨ]sđD~xųo}aęēŦ}ŠO9+ÜA~ЗÏýeĨzPģBïýŽ―hĩ-nðđŌ ~}v8ãžĄÛßĸæŒFßÁC‡,dŌÍú,}ú.ßņÞû_›ū Ŧõ\vÅU–Ø#ėt—Üމmė?t™ Ö\•ĪøĘđŋč1TßķïQįŞęÞfß#7H=)͝ŸeŸÞûĪŨŠuŅn8ŪÃŅÚŧžüÃa[ž2uހaËŪ?vĒļũŅg§ÎÁDīę§:qCęAÔg…-/―rČߞ}™|ϕV_–Ĩd9}âŠ=ŦEWbáäLķ€_â $DlaÁALXûr$$õĖBí°Nå’E…;'Øt`Ņ“+"“*ŨŽîģj >OÉ`čgËhī-vęūbîFĘbVëžO ˜{ûŪIcf0ÔČÐ2c"í$Įļ\$@͂rk‹ ķŅ›ųĖ~čq]+‡BhyfMЙđ,+[WƒmF âÔ,$ũīēÜ%öÛČž Ĩýĩ-b˜-xļ―€§E#ð&GŅ?SX =zô /žÐ\{ŋ Ԝ—ŽXyË+ÓÂúŪūáÖŦÓŧå-·Æ6Ëjˆ=3lđ…Þ’õŧþæ ßŅØ ķKĸ[eýŨÝlûuiáōcÖÞd uįï^yð°eÕðP…<Ķ9ý:{u_mƒ—yÏÛŨŲÔގōƒ‡-MĻý—ėŅŸŽ–5fŧQ­WécFíúÄ-æ’sUŠTg1ūÄD2ĄõšæC–į"v—8gĒūÂjUˆĄŒAD ­8ƒból‘c‡t"†ķök™g,Ármmp!ÁV5K’-ōâ‹9å:…hDIyG"ąp•ÎHØŧT­wÏ"Ī ĒÂÁ?āFāÏڟW†ŠŦ°f€Ũh‹jŲ·HoĪ”(ƒU‹IG :”gĄ; y­–Ę…gv>aĶaéđ:Ÿqâ팱Z o˜đ9dø í[į-åzOøÚb֓þĩjWĒčāŦIē U0Īķ9bGЈšÁÂŦY<,Tɘ2’4w+ÏBëh^ßF‰RŨÕanÐ,öū#ËīL S]Ļsöðü{ä>0[ó ô8€^y"ŌŦÁ‚›D@ŦÁ ĀŠņÁB|IB‘ ŊåĐŠŊð įFŸ\BLčÁ=ē_`•ËĒ&1@“NXKÓD†VĀÂŪÞ?5pAïIíD+„)Ŧy&°É)Ų|Ûīļ$uņ.îҧ?ýkÕŪöû zĖUbe‹lŽqF„Q&ĩ„SRZ‚w:9- ûÎX?UADĢĻßÁ… aŒf`áĮŲ8ļtYe(ÅRHðE*ÆŸl‰Ĩ.,Q† {„Î:Ua;€Štg+Ë#F§vÆ]XĀu†ëNvvį[rÞĘ*U=Ð0hÞîMŧcvÎ畈I?Ū(‘Þ˜)ÐŦĻ bkX^JzÏØ[kMĻ“óļ #―DG„lCB9đlĶ:n3?ĄÕŪķÁygĮDNČąus*DŽ<áËŧ`V­Ā> ‘LˆØĄ2ÏŋŠL̈ú_Æf„‘_HĘXī€Áh,hčŽ-Hšm\á薘ƒi+†Y”…h)5,ÚH2Y‹ŠŊ„‰Ð\úFÐŋ4ĘÁ9Ŋóō4úøWŧÚ!IŠ6Á,PŒąeÕ(mŅĮ+–[‡NAö˜{Ú0Wí\ŒĻîë‹E’?­û  ŊPk%,–Ņí*}Ņhψįņ3• Œ+U­Z˜Ôüokĩ„vä1ąļ―ˆ3{yČžÃÕ8ę|ķ•™(ä!ED΃•ūAIž[dE•9ļ_€‘kĖ0Ø­Šû Ō/zėĸš>XļųóvŲ}O:ņč“N―ýŲ…þT7þėØã~xíŨ_:q‹}~wįīp͟|ÆWķŲbģÍþޝôņŊvĩĨ6I ęņ*UXąļZĨĻƒÄ eį4ę+ ī†ŨÖĻ a%L„Ņ)EAJyĐ[šŽ›K•l œ…P/ę’0r%ëWŽ&sŒIrč$ĨŠB‚˜ÅŅ:vúmQõÚķ+€ģ/]Ĩel,$Å.Ē!m pTvŠĨ’Z&2 *X0ÁĪŨÆlA=$™ÏZ°Gėķ%ĻĨGé"Ž%J+‚w‚ 'Ėd°NæôIxÎT—}Э3þï5Š7ÝtӍ7ÞØÔÜĸ0‘f>ĸŨsūō­õþÖe?þÁŋšl­)žpäÏæUåŸO<îč?<Kf&0;$óÕ2q2øĒ XbÃš”@P‡fĨ$ļ­;f3„ĨĪY[60õÚ=;ĶŲfá[ķáÉ1"‹ŋ…ÚŠÅĘ?ôXĻė_uŽxĩlÁJĻÁęKˆÏaljEĀÄˆŲ eUÂïåĒĘhŠ!8=%3ØjN‘upbI ’@M䈆ŨyN`Đ8܈.›ÖKį]7ė‘A:įz―‹°âÛt$ÎY”øaޓ°ü·đˆO<ņÄĪI“Ö_ýÕW_―ĢĢãŸá"ūŧá#'ΏÞũ§Į‡nxņ—·U†Õ Ņ'^|Ōv_ųŏlüŦ‡îŋïÞ7O|zëŋ{ÆRÔŠŠ1sÆīŪÐ{ôð~īpqXj™ÕH+ôïÛãqï―þcŨÞÜMþÍÕoķĨķ]‹‚Ô".Ą’ˆãŊ˜’(ãJ‘16|Ė™iÉ;† !sÛÛ °ũDÄRuCžáīJĩŌCg`Le;Ŋ7ü " [[[Îca·ĻlS6`Ũ6e‰IOۜ3°€ūØybuģB7YāļēŦ‘fēv§;Š€ˆ7'Ą€~ķŅU76nŠ:äÅК“$Ā2TÁÕŠēē%„îmōžÁŸ…R{‡˜5ĻđÏôĘÔĘ^ģ†˜ˆ€OTIÂošDlˆņR’üwÍ^Æiķ··ÜrËÓO?―ýöÛ<øÃļˆ(zoE}hÕËīôÐ~:ÉjИuFÕ:YF°ņŠCƝvöëuv,īmĩęęãþ~įi{ėÚđÞf§žqʰũéĸ=—õÓ[åÜ7āĸ%ï9RŧÚĩHH­āˆĮggå1„€Ý*Ƙ5’%I,%WDõ†„ÂŨ@˜XޒNĮķJ‹ÓÔLŅ!(·DÃôeôðĒbÍŽÕ6*˜ũ/.H,Ū‚˜ŽueHYÏÜļū˜ Đl·G­$J–FĢaÚjŋ\Pūđˆþ„æ‘HXĘYČ]V…$ÜîÓcÝjēŸĨ*aKŨ9gý/îYÍķšôÐhĻ$kþyQ)ŧGŊ†_KhÞ―“ąđJŧÜrËMðá"šŠ˜[O‘Z•æÏœáâČ%äƒēýúõ2Ąm^w·óÖÚéĩoøÂnĮœ·þÄsw_a!―Ŋ~wŌQgýõoþîōMVéOï+^dâÞÛÕ΃ˆ<ÛV§DÄĻķ.ĨŽXP\­KÅÁ”DIÐ<}’™,9þTÉuģ á•ājk%•jJ SÖbhhąĩļĻęĐ<…€ïã 6Q3dqĀ‹ŦŠēė^]_ˆUh۔ä0ųMsŌÛ(ŦČÏsY˜!AõŸĄāĀV ŠĒhĨā8—šũ,`;`BÖ1ÉĩZ^b˜ ’Y^Yâo–9vóW+šĨj ž„3LôîŋĘEôÞŊļâŠÍED}Q‹>īV^e퓞úõýŊVã7—ýā­%GY2›_ŸĸÖä™ôūbŸ-―öÚ=‡ŅĖ7æ•ŊžðôäКšðĖuįžxãŒģ.ĸŲvk ÆŨyóÅgĸ1iž+xĢ ėĩŦ]‹Ā ƒsR%;à .·íŅÏ3“Ã7}Ð―ũHu,"š”t;ĶÚ ˆTJ8č*—ąŊl–đý‹1ÍīÉ/áķˆØGȐŦ‚ŦJ g+buŽïfˆēÏ\KĸÎĄ­Él° .ŽžÐb åyÍa3BÔqĨ fĢlH"óϖ:ЈYp ÛĒßD  gĩ<ä!–% T!XĖ4Î6F^ŊSfn”Ėxcšđ;ōÜ!udž―ކ†―ëŠ95Kĸ—đˆŧîšë{ėņ‘ŪXÚ4ŧ… óTb9›>ļ–ŲxâÕgMüÎaãwŸļÏø·=ø;oŸvþ%Ģ;hĨķļųĖC9ï’đBÝUÞþģĢĮOÜg§mw{g܁Gí―*UÓÏ;rÂ)ŋzšˆîŋᆗĶÜĸ­/îūå[_üŧ‰čúï~øwŽð›·Î=áó_úö ĸņŒüÎSŊ. vĩëc―˜‹žĻ@)ējōĀB6dôĮ\0øn‰\gN,ēƒûD!ëhÂĻ“)IŽ–ÄfÂÞUE"ķÛZU‰™ėī ąÞrÃcY‚7(%gŠx“óāqIՊ4o!ll%!aa,x›);&œ!5Ø2Đ)ÍzUNĪĄaX9cĖeŌ{ÚļšÜœũķPkÓjįļu/’k§ßZĸ%ŌF>_Œǰ:skj§Āt‚ˆðÃR<9Œ/vsĸK5dȐ–‹X—ũþÕW_8pāĸā"ūõÖ[ĘEDđÎ öøúõËnôâÓæM}þŲįņý/ïúÜgŋräÄãïü˖e>hĄČŋĘĶãũð–ï°Ö†,Ų;§īÄWŋwuÕgíōÕ+VßëzĢâáË­HÄŽûņ–ĐWO‹m?á ëíð…ÜĮŪØwÄ9ĩŦ]k‚ŠšęY•™y ‚wÄT!Ô°EY,ž=Ā$U,AÕbb§Ú*lk4 cbŪ7ŠÜ…‚8ŋS@CdŠ€ÄŠĖk™#Ũ(ņ‰™·Œq cЊŠSÞŲ0JX"°UڀƒĄUÍڕy—ĘČ#IÖŊC™á=ĩ2Đ.bA. ônzÏNØ9F*eŒWŦ˜ðIԜÛR’2&Ä멚č~’XāÐ Üq=!Ä_ĢS N"r”ˆŨ+8- "‹ą™ŋð!\ďZÔ*ã6\eœ>ØcÂÞüýõ3z.ŅŦģįZëmAï)·ÔōëïžüÂOd#FŊFĻCG5ĸKVö˰ĘwŽüĐuĐ]íZds3ėÝ~ÅgvϜ °“â*Ä Xώ‰ŅÄâMŅŽã3o)^.@|ŦRĨgũUQAíœ 25h,“ `!ÖcÃÁ’åšW€B vdV‰·%1ī„ŪÄZ­ū^[&}E럈ŧŋōģ!TKëOIbUąÎ‘|Cb?8–\XHŸĘœįā‰ûÉĩÆŅøßÖÝ{C/&ōäZš/ž8s ũ˜ŨrzÔæ<2ŨXRÄÄ6%‘ ĢíE‚‹Ø24·ÅޟÐÃĻĸ;•ï3lŨĨvĩë“ QLI—GkY yFÄ-ĀvČ t„/ė*ZøŦĢT$âoP_ė\°Ü‚Č2Սš(` ĄBdŊ!{ÉfĩŽv*Œð@bXWáÅäc™ļ* ý(ðzØ·]íjŨ'Qjm†Í#Ŗ_ Ē‘én˜b[!-N~Ô}Ĩ“SÛä=\P=[ĘbļœƒX“f°TUe(Vũ /ĒËhņeQĻJąx݀ }ĀęY(Á,ĀUP]b5ĨĮ–EË^f žôþģF̰Þk`ŠāÝžGŪĢÖáØƌ\tƒˆ{v ―,N-Д{0g ‡‰D"2Ë0―p%œYÞÚÚH)šWD°Ņë€ŨI€DЭ`ß$ɈéŸĖjWŧÚŲb0üįp@Ô(‰ÃaTzĄg‰DóAĒs0Ū+3·Âŧ0ðuÁ%i%ŒyïÔÄfaČ dp­ÉþÁhÜ,ŽWNzmĮė3W™;dķz /ĀļÓ|ūy–[Ũ ũ‘ū8͘ųĀčFĢåý$WFmŌ°‹ŧ0Ē‚EĻyTóąJŊ#a)A·ÝĢ:8ÛôOƚ…€]rĐ4Î$ģÞFŌēÖ>$ Á!ÂZpĐĘä<úv‘ 'GÖK/―4oÞžĸĒh1šĸ|ĩŦ]íŪÖlĪY`ąáĄjVŠāŸ…͈ˆ„Á6yT’Čhâ8mČũNÓ2 %Q,+J- ^{ “Á™X;Jh Á‹Š˜ŊŠáÆjJ§*u’TĶ’ ‰Sku m‹đšŒQ(tÏ[FŊJ?øW˜‡DL˜)Ũą–†ų*9ĸDÂm ašZøÁmQ –ČŦŨûģ`]üuéīĀ9KÕĩđíŧī‡ÄLö]AˆbÓ~Ð<Ûä}ĩôŌK7Ãqĸˆâ;Ŋ>výĩŨþáÖŋūųáÅŲj{晧Ÿ}vŌôũB‹đÓî―ãĶkŊŧþ‰W§ŅûМûÆmšîÖŧžJÔŪv}ėĨļjÉōžQ֍K―ŅûzwƒF”gĩ„ØŽVQˆ† .+ÝĶũæóĒÝhũ’nÐkÄēÂkģ\…3#Ž*V*‚N=XƎąoý&^°îVh.ŲrĮõÏ3/DP2pã–ķŠƘNF­Đ4:AÖēô&ü PYī Ė7 Ë<ģĻFAŲ*mJ,@jeYÚ`AHôÅ"øB“aw°Ņ›Ðƒ{ŽEˆ]ĄÅSÏ3&WęD%Ą7_d ŠM4mx_5õũŸ(ÞöÛs·ÚėsŋšåŽ_}ü.Ûïtû$]›úŌ3Ïŋ6‹Þ[ßüĢæZÅI'Ÿtô)§Ýņ^ˆâúõã/šþî?^ąÏ–ûüö/ï(ÆYÏ·ßîį_~ÃũOûâi—ĸĐNíjŨĮ[j}pĩŽŽÂA-Ŋá‹0ŲũnÓsGhKY›XĨ,: cŲ;īr^‰ŅƒãÅāÓR2Ę­ófkÕpY4đú&| Á&#‡į+ĖguB•ZąĩėH Čīā‘Ļe53Ė•ŧxuŠŅ@šeÁ#v&ä˜ðēT3û”Z4[ĄdÍ;öt=F< ģ:@(LÁk5āt'Ī'c„ΘYĐįbáŊŅ >1―$Aü߄(Þpà ӧOĸOCg<Ũ9GžĩŅĄßūėGßŋüW—ŊûΓ'|ÅūqäuMŸ>saˆâīzmôßļð‚ xÎé[ŊÚoaēŨúŧuÕO.<ïâË>ŧVqÛ}ï(>xûïïLcŋéO.ų΁7üîžü1ÛvĩĨÖ„Ģla· ZÎŧ˜Ē.>A.U ANÉ|@^7Aëpī%JHĒĻėĨ›dZ$šŒ™áÃqZ<ķÐF!­Ü_ŨW WUĖóĖ1U0$āöįrN™ēDæfvrûŊģ­-,čn˜öÁ…™đ@ǐ[ŊbЇfy„ēB4ņWH[Q‡_*(Û3͉ąÉ3‹ā5ÆŪĶžã„uĸÂ1L RÕjзÂĸUˆâ“O>yÕUW=øāƒ†ėúHˆbųū"ĒĒxÓÃ6:öKŸŪ1Õ.üÅ'Ößšþއŧæ‘8ũĪSN9oęÂ^5Ķŋóö<ęąėŌ#ûuBĄü’#ĮŒč—‘ëÕ·wŋĒØxúĮÖÛhįåzų!+oïÆ yaäŨ‚^<ÎéúũÜŧ,>Ö „vĩĨ–ŦRELõKŽÝËļúØs"ą„˜đX&3ĸģwRšþŠP'as_eÉ"qˆƒ$œ5iDĨ”Û2ۈg`abŠðNũn+ÆâÄfÁW :U@tģ ĩŒ_>T`DG>JLʋĐ%‰PjdęÆčˆ-œ―Ú@ƒ(ūņÂ3O―8]8_aĩ_xņĐyD Þ|ፖķĩŦ]cĐĢä&@ +įh°āYCŒuSŦŒĐĶKI•%Ѝŋ –Zžģķ—)ļÜgY*+Iâ3ϐ!åæÎĔI Qŧ€ÛFŊÁb)dÚýE0 ,Ð ÔÚĖBwČŲ"—-uS5M‰čāŒqĐHbG0*D@g44ŨöÐȑ2&Bó+ûčs>b‹Õ 5ÝBNĒ‹đ$Ē;eÉģY†―į<ójoHķ/nÔ9sã:Ģ:ēŦJˎôĩ5EĐݗ*x$ÝčuĸEˆb“i°ÛnŧíūûîÍĸYˆâFŊþöÞį>a·―öÞc‡OrΔoDqã-oųÖaGŸûˆâm?=j='îļíîÓŨ:čĻ}ÆP9ýüĢö<íšgˆč{ĸøōÛũŸqðn[nņé‹~ũÝpÁáGœ{Õ|ō›O8bŲþļãg&|zĸÓÖ;âЭĮ vĩëã<Ŧp{aZjÃDqūEÏ2ũ+%Aß!H–S@lgôN›Į’š\ß@™Éâŋ`ÛâĪrĻŲą­4•sķĨg5:ïm%8ÞŠ5"(%ąó%‹r GæšeCÆtg2"4I’oeę _Į9SIíŽá3ĶĮk-,Ũ°qœa‘$˜ĀXm[·HΏ\ņ_ĩĖĪ?ķÚ=ĮXņĀnqBWn3„nŧąNT’n—åÓC>þw!ŠüSdŊBÓ?;hР ĒØąþîĮÞļÜÆ“Þœ6wĘó/Lšę‚#v~æģ_9jïãĸzŨÖeūÄÂÅ1›ïĩĸ ·|ãÖ_oHŊŒŌ_ŧ⚨gY"Úõk?_cïiu”ŧĄËŽDÄ{žpņÖĐWQ4ę;Wüčū§^H=–Üxãĩ:ĩŦ]kĐ5ĘŠ…Ę8§Ņ„P vč%-yL%É)e1EąĢmÁŠĨÅÂĄĶã0Ūzģ‘šāuōcĢ*ûš!Ä@T@+­ou9tVđę?%’ēQ:įē°[ĪaāštP–HM'bVe”Čjé%ðs‘ų2Ŧĸ7Ã\V2֌q"BĖĒ^ĘD9ę‚OÉˉĨ‹ ĖR)7']5Š:Ll&Ðp{WĒbÂLYÏÍŨUE-;ûJ]m·mo ŋ–c {Ā^ÃĒQ=zts‹áå—_~?Dņ#ÃrŽVŧþJcõÁîãũþÓĩŨÏč1ĻWg5ŨŲ”ÞSnÉQëî4ę=ÅáËŊJĻþK-Ûü/YĩžݟZÕcøŠ[ _‘ÚÕŪE@jQŠŅÁYŸkĮ\† t˜ÏŲ( Ģ–W)™ãĮcä ĮĨ›üĸ5į‚n-đ&2lÞĻÛ2Ýsĩ­;‡q­Á(A/SeäÖčn]Ö} )ІӸ•KBB; R†0ŧŒ”…žŠeÞᆄ`ÆZ—ŠÞÕĀā$―% ļ…hķN-ÆĒįƒDbß,ÓL?Ŋ\Ęk|ŽũxK‘R’<eŰĮ}⠊YïĨvÞïjWŧ>ÁRksh&§ÐVįT,°2ˆ… Ö`ĩHjČ[KVÎāßÉDo ”AĻ'ø,„hÛnv WސWVK }Q‹#C–éŪ3’@YD[˜Đ{ØŦj§$ļ%˜ŊАbēQ€Ø=“Ā&ˆþB?ŽeabÝÃŽ@•Úįėđ݆Ã|6EÁžsdÎÜBëwFũ̓ęŊķĐ æ^Ï.ęˆ#faĨ/ēûũýĸoĶΧ‚ú—ïĄ]ö/… ė˜‘Q‚b‰íŋÛÅRjEu\DŒ:Î|Ģg)ÄË;.ÁöÎCÆĖ &VÃu›“Ô„8‚~íNH2oЎ‰-`Uے”(…ŌB1ž ŨKčBGppô&,ôúĒŽëô īėąTęÜ ÕjyQ”zYvča+‚j31\l{ĩ†,ÐÛõÄēģ{Ö'―ŅÍ+[K ”Ī^ïō!čÕPU•%äČŲJ/Áä†>ŊÃh[ë€@gJ“hđ;–‘Žŋ$Wbr-$ĸ†öđŪzÓŨ4―Ļ-ĒÕ.ŽíÕj5ú€ fB§@íZėVRČÂZ„Š–uŸ”ë ‡íΘ­pYôÁÂBČ1,YZ‰đÍĮ‚ŠæoI˔N„=ž ë?PRQË24Œˆ2 ― Y./ąÃi–øZ‹XHL.sD\Ö+aąķ”,þ hđĐōõÄcÓޙÖ|/9Đ1VåŧKŸ5õ‘ųUVS•ņųįžÍ2V:bðŦŽY™ž|â1Ģ.Žžę˜æÛĸņäãEŅ(ĀcqÎS…Ņ„ų%Ęērč°ôú‰°nābu8dūY ŽjšwēV4á0 rläā4\6*"}’RRŦŊŨólˆˆƒjÛßĪM~Ģ$;ĮĢD>ó†—Š„(Ë3ųũ1ģfÍjf†Ģyw‡iŦŠjĒŠŠZqÅՅÝŪ­ķÎ~rĨVˆûˆžnUC―QfÞw5šÆïđWSgŊļėŌ3O?ÕđŽWϞÓfL>béÓŋõ­qk­íœ{ōÉĮŋyĘ)Ï>óÝũ˜pâɧ9ï^~éĨđsįQR"Ô.ŧí~ø_―vÉ%+ŽībÓÜÞÔēĨGŽl*æüųóûâÁ_<ôðŊ~ýļ'ŸzōÁ{îëČ5Ã&5 íWŋþ­·ÝæøãūQËģ3Îüö˓'û6ÞdÓ[nūųŅGîėččÝŦũž{íýŋ?uÝĩŨŽZ~ÔsÏ=ž'ĖĒD™ēYlYŠ™Āų&IŅ!=ĨĘNąē€‰0 /’Ļ”ÂéiUÍģŦ vÚÝ NÛ ŧËZQĐQėŪ*UuÞŽpTč0FæĒjč"tTŠĻ%é•ó˜*Cņ:O(^úũŠŲÜþueԇßUŧÚÕ^Ėĩ>Éy=kJ)āī'Ŋ5ŋýũŒ1.XÐ%D`č‹7ÚhãfäÔŲg}kÁ‚ųãÖZ§9Æ4xðČĨ—ūæęŦÏ>óô5ƍÛd“M‰iđQ̚øf“ÛÜđēäāË.;ę{îIâŒ^c2dÉO}jŽ$Ųpƒ \_ĢGŊpßýũŅōĢG?ųÄãW^yųāÁCæÏ›ŨŽZnđ+/ŋ|öŽYėŽãĢ”)ƒf<%„įÂ–č‹ þlQĶ”ŒĒPĘ)čĖkÂŽKbėҁÆFĢČóœ―KĘ9KXV ÃP\ĩãSRĨ}n–8IEmvΓ ī Nå2žcIÂėSŽ ņ1ÁuKĸF%‘Nygš­Ē}|ŒĒ hķī"ōwÕŪöŽķ―€Ī%[W VmĘÐņđđMU›9#Ïó#G6YömŲå!”ëŽĐ9™ÍóĖäcþ‚IRÓøŲT•5”õčŲóŲgž{äáG_íUįžYb‰Čbcūœ€ŋļęį<úČŊŪšjîė9‡zؑG―ÍöÛ|ЁûLÜóÐÃßmņÛl·ýĄþŊwÝÉėfC„āH‹“ƒūŦīđw F į>Į‚ųl—.q9x.Ĩ™ŌDĪl4ā§ ėŽ#nđqËHžÕF–(&Í+ŸóYĮÎBz,$‚‹Š ŽÏUš ĪŽ1īÕĪņėD ]üũD­gŊ>#GŽüX‹õíÛwÉ%—ôÞðýīŦm0h˜“Ά~}æĖû„ŲĨ%l?ðƒÛí°ã~ûíĸΔ)ÍÖēŲ―>þøc/M~iô +ŽŧΚM%nž„LzņÅ9sæEąę˜UWģÚ:ëŪŨlpbŠÓĶOk ņœŲģôƒï—Uĩä!o―ųĶĨ„Ę97}úīwĶNmÆþåŽ;yčÁ!K.ŲÔéå–õęŦ/õå#NĸÖY›lšisXņĀýũ~ę)O=õÔŨŽýúF›nVTåZk­{íoýæ›o°gOØ|•Ō·ēŒĨ.’PZÆ<Ī* ķM‡˜Šē >sžĢ˜!Áuũ°ŽH‚:smÃQ:–Bæóē^ gSrFÉyã1Ū#TÅŌŧ`kuėÜc%!oÝfôŊ–ĶÉP"ōąš?âĪą]ĸĸĖj…Úĩøm‹9'Il_Ș­ČŨųË_\9tø°CųâÕŨü†ˆæĖ™ŧÃķ[Ÿzō _ré/ú‰ÉЏ~páĪI/üė§—4ÏkoøãëŊŋÞŧÏÄŠRAī0óŽÎŽĒ,.8ĸÜïžwÁũøÃ·ßz{ĀūûģûũëwČa‡O™2ĨpóÍnęø 7ýyúīéÍk6Ņ'·Ý|óþÔäþãO―ņæë™ËX 4ÃJ$B™Ķ;b‡ĸS<#ķ―,Ļ’Jš_üāRaóÁØ-‹ Ȃ,ĒTŋ-ËŌziĒĒQbi83°zŒą–Ũ„ˆÜt'ËóBÓ)°·-dė†ü_æÎĀڍ ĖLÚĩČč#æHĨÃshŧm1lАđ5Ô ËĨóæÎ=įŽ3ođéOkŽ[ŧQ4îšãöŨ^}õ…įėđĮn›nūEČē›oúÓëŊŋ*Qūs֙Ï>óĖĀAƒnøÃĩĩ<Ŋ#QüœoŸuãõØtģ͛íí}ũÜýėsÏÔr%ÆNgÚ~{O ™ïėčņ—ŋÜ1a]6ÚxÓAƒ=ųøO>þąüýéŒĩüĪ_ļũîŋ6―MÍæwĖjŦ7Ewßų—_|ĄŠŠŧĸzŨc?ŽĐ9B6"ČtŠ*­ßŽ ĒjNx7*ëĮÜ(tĖŠóŲĻe™h ؍ôF?Ę r ú—Ōl‰1iáÖō›―QŨ‚EÍ­=`‰ė3;î#R­Q ŪXÆBþĨ Gāwi‰^ūŊüąŨgū2ãE^„›žvé)Eþyô•ˆfĖ+ôé·D/Ÿ9jŨâ2ŦUAą_ĩQ„SĶ\ŨdŪ<EĢņĀ}ũþ큈Đj”JEŽÕ&OjÖ @ĩ:L{cĢŊýÝŊ‑ÕmˆjÁüĮyøĐ'žÐéP[ÎąūĒŠ^|ņųĒ(ō,oÖ+/ŋ4yŌ‹ÅHûýŊŊĐuÔTõ’Ęßí7ß|Û-7cÖÉÍā€Į}ôąĮ >_œ„q^gŅd-Þ ģ&į°[A„°k4 UH‹ŸŅ klƒņs% yØķ˜=;{Æi!ŌĮa&TgĨŨÛ(JÏNQčØ+ÎôÐŽ–āëē Ķb|‰< ËcN'ĸDQ(ó<ļ·6 ģ?­ˆL‹pĩ‹™Īü°ßŽ=õë|po=Š^līķ-ĩ Ęē/ÂĒL™ÜŒŸ82ōfã7ԖŠ‘cï™'6oރo^z@˜]@)9ĸ 2ÕRjŦSeYāā*$Č1 !H ˆ*­Gą É}ÁģQūĄo᚝ó Į„E)+‹YĖÔcžŽ'_ÖKrXĨE!hŲq–āaĀ;€Ņ…‰―ó6E!ā\tgAÏŧœØDB˜l‹ųí>ÂĀœ·―aĪU*QĄĀúŊę:ĖúÐąûo„ČŧĶԆqKwސ/(’PŧÛbĒđØËũïᱎÞŪÅh€ BÄāȀɏ"†=ôÎwOC"&Í{Iđũ Éc&“†2C7K 4įËqĒ„']DZAËþ ó Qē#ļ ÐDņ  `L•1óN1FŪ!]„‚O^|·Ø™ŲË1‹ĪāU’-5‡=g.%`%·!ƒ &Ÿ"‰Óįm7ģõ3Ï Ų&}ė°§ nō–ÔË*Uđ†cŠe ĸkU nHo7ĻWi7:‹są‘.ÚsÚÅo€ ’ :cœu_Eäū8ž;wNŠâž3ߒ$Šé‘‘J‹sōÐčj8r>ïæ·’ [ÖįęwQ1ĘóLώž#Įķƒ›9+7 åíõĀxVEĨķVJ:ũlV,ƒ Äl.Ŧ< …ĘĨĻåĄŅ(`l`p_9ŌbŠŠh™’ J‚Šę–Iî<Ų|íĩiĩŦŽÅ+\†ĩīķŌSoÞJ PK_ôķ𠓐į,ÜŌ˝įFY%–'[Œ‘OŸ€j—PŧŧëX@y.ÚÆ@"õŠRôŸW]Š%oŋÃNŦj f~ōÉ'îŋïÞF}ĢbF‚Ē€t’3<+'A8cUUĨÓÎÖ[^·€~ĢAŋV'ĐbE5 ÜŊā| 1ķeđÃČŠÚÎijCĻ^F,Ĩ5ŋŽÂW”%%!G[ģ ĒÕ1;ΉÄĒĖjy^Eä›áĀJ=^N›l―°(&\ÕL0tÞA[™ ĶQŌ(īĔĻLe āģŽJ@ rdƒo―NĶŊ‡ë‹#EĮâˆÜ'óßĀvĩŦNĖæ% ôp5Д0ŽB…?ųäĸä§GóÕŊuôũøãO­þĐz―ôėmDĀä_–gzĢkÁ|\Åbþ‚y‘U‡ÔëQU·ZyĨUnđãÎąkŽÓް\ІȎɕðdžĐŌaHĘčYīU‚LU–ČÔ%vļĒ ĶbɅĐŦÞÍ:ËJĖ— 3~Ŧ„ãŠjŪīųā„’đĩØ#w0EcÐŽ<dØŽ+!6 ;­―[ïƒīv.ZīüĒ*ĄâR%lsHōØ:S―u>ģŪŦ―N”SŪkpÍKmą™ÚÕŪv}ŌĪ6ÅÔbb1’’ūĒϛ7w·ņūðÅCĸ?öū.ŠŦ}wfwaX:Ō5E‰ėą ķD―ũÞą‹ØƒÝh,1QlÄŪą7ŧQĢØPQŠTiŧ,3ģũyÏäÞ_îáKōqŋûĸūpā§ģģgÏ93;<óÎóūïóͧĨ3jܘŅ[·lFĒmįÐC†úÕó7yrPãÆĻíÕŠMÛŲsŒ7îӚ>úbƒFcŅū}Įi3fÍ_Üšu[{;Tý›=A?ŋ1ã'Lš2-7/ÏÉđō€ÁCÂ#–‡†tEúą lFV;|ėļÎĄ]BBŧĖ_ļļWŊŊŠUŦ1|ԘđóvčÐĻDŽTÉąoŋþóÃ#F‹LķB]!īiÂæ-ðôŽÖóŦū―úöŨhĖ|kՙ4uÆžáÐĐL rT+UÉžLĪL‹āŠ­ĀDu–ƒKéģŠ Žc?c\čr ĶÉ(mF’ā\1øjŠu&Éũ-ØŪØÉ .-k 0Ūįšð―Â6­híoš˜K’­ ^V†Ĩ€EÏ{Ë-aáŪ‰üzŨŽïŽŽ-Š0N˜2 •Eåԅ æûûŨŸyŌŋĸĀÞ}úôčęáæ\ ðúøøėüîŧÜ6í–._ņæMRAA!F˜:yԁ}{JÂVPēēˆi~đeŋœð+‡ WoúR ÃYtAf!STƒHž@=åL-tæČ§§`đČāFö@Ī*ļŽUĻÄĀĶJJíÄó4įßŊUīŠVaÕ jņ(Í’ēpÖĻseäƒAÕĘÚ $ĶÆTĢPԃ<@ðä‰ãÁ‚îß―ũUŸū‹6‰P\lÓĶ]FZú }ĮŒ6szqą>0ĻŅ•‹į ë/"|áČaÃ6lÔčĮččđaģŪ]iŨŪ―_]@>æGIsé{=rIx8KÍ*2 ïšÕ‘n$ä6lœ]ŋnmįŽíįÏ ƒ„nHhW€Ē,‘ŌąÃŠeĄ]ŧBúdýÚ5Ë"A+ KŨnPc…Γ+^ æâ%u4Q°TZf㓔'‘Ū‚qĄŪDþ@ž‘ŨFÚ/Bƒ€earúó›€nØ%'qĖ:f=etRWЭĒýÝbĀVļ†,Xe\2FL?˜xÅz=āÏã€?Ķ|h”K‹OŽ9ō,.. 0=Ą5učČqŒƒī.ЭÚī™:}fUOOČËĖllށX… įđœėėܜK­Ã4 ۀrsÉn•8žZNN6ōÁīŽÛ/Þ―uX:wÞ|7w!˜ø&!!čåč䈞2ėžråō훱ptpÄŠ–­X‰c―>|/Ē/ŌĄēš\P]ÖÔö1ÉŠæÂâĨĖے„ u Ī5Pėî”Ŧ\G.ÚĻäÍ,r‹”öĒ3FÆ&úēÄrŋ/˜ŋ'!ŋ°@ú›ē­ĒUžaVĶĶjEˆk#'äÄŋŒoÚžy›ķmŪ\ūq/{‡JœDQY< 8`•ūü ÝĻà Ųûũé‹/nÔļņÄ ã’ũø‘R~­^ËÉB3PĸÂŋĮûáŧxŨÂŌ2þÅ3ŠW& ÃpžœJ §NĘŌÏd <''ĸŧŧWÁņh4jôÖëõ4,sũ“ÔĄÂĻÓ.į˚óÁāõŪåû>-5?/OACģ‘ĐI@Tōqą#bŠ`rmV ã Īã…C-aqŋ@TđāāŲ„7Á―AÉŌŌ0$fdē\†Ýh°•…dHrN]ą)́Ŧh­Ēý-ĨÁ™ÏŸ7R˜DT&Ëz’ÔfęĢGCXkАĄūuüðhoie CđjŸœÖ >Š­Z}>fėļļļĮ d·}ûmq1!WPƝ;‡Ē3@H'â°á#6j|úÔOiii]ŧvƒh!0ú{úô&0•ːzkĘ*é*īf5Z9j@―ûwîŲ'LžâY―zã&Áē9wö,˜\ßBˆ/ųŧ”—/]jÜtčð‘ąŨŪUŦ^íჇķųîû]Įe!eĩÜ_ ÛĐa“ŒXXôؖÓį(­–9Į°ķÍ3ČfÕ.9‚č„{Qp―4”K ^YIJēuY•ujr”&ĨPbÖļŋ]Ŧh­jy^Ξb(ƒZ/öÄÄÂ%ssóÛ7oŒ5rÂĉžÕŠÃā}ðó=―NĸôÉãsgπŠ‚É]4ohĸÏę{y{CĘžĻÝîîp—Å\đēaýZŒĻ:°7ĘŨ·VÍZĩ`C›föŽ™ÃGŽüžm;`孛Ũ)ó• \ U°‹ÎkÔj *ĶļxþÂņ$^PXpéâ”`xĸbâøqÓĶO‡ ‚šØ~ôðÆÔKzö뉒XTã;wŊ;…„téÖ |řӧ pOMMUøvSá$8ʘ„ Tb~&Ī+‘ Û)WC*ÄĀģŠš8 #\P1s/åŠ3ēïKEÂâDËb:3[”S–A7c6_ūõ`X Lņ?ĄÂ˜„ß?^^—ûlkDŧRÁÚkū—Ņø 6W@-ü9Lš€ÔŪUTSëĸˆĪȐsåN•ðîís3‹Ûķnųf“VkĄQ›Mâã_Œ5ÂÅÅ fé›Ī• áï―ŧwP?5#ã}aA!FÓZjŅ­wÏ/ÜÜÜ333ĖĩG:wæīSåʅųųĀ@ÔhāH`[ĖÎΘ8n,l@3ŲãGŋčÞÖĶVkųîíÛ]ŧājĮ,1Ïĸ|ũķ―ļā‚ž|øčP ―{hˆ…đOÁ āõŠeKvlߊĒÐ&r·ÐŽ~ĐRŅHJ ÄĄr 9Ģ\ÔGÍÎF F‘aTdá―ØKĀėģĻ ŧčÍōqy–žËąļ.‘YĘØ‰Ęä/ãd$%‚`ÛÂräjąÔ7oóĮeūI.!üĸ1“ÔÞVãýНOM{%SX/cÁøjpÃËÎÎ&^û?Šáz€F0 ‹Āt(ûqŧMcJšþ76üÅđšš"LˆŦhŪā Õ+‚™ŠŽÉQ2 `ÛBŦtĶž}‹mK +Ā F šÚ8t&ššRfÓRSðŌĖLƒ‹ čÞVŊÓbCĐīŒQ–ÝB!#KÉÍ ę3éu ßҚ‚ė˜Ri4„qŒ:’Ū6OeÃM™)„Ï*9Ū° Ļ  Þ*ÔäE€ĖÖÎV$4äåŌķ蜓õ!'qĻpŽŲaÃô”•ÂĐ,ėSF°Ü6RpĄü –(!Kũē7ēF a(+S†Î2óĪ)C‰<‹ãE 'ïJFôiý&j`:ÓrPĘ<8‡ßōhX֛äžgyK ûNĶJfÚb‰ĸÆĖQV―ÃĄææč.ÞHÕ õýËĀ HOŸ>T!Î<öüÏ,OðŅ…á€†2Öïëë NŦīĨb?یŒ Ðb(NņŅĸé*æÐ5…ēNn<æp*Š“ÓŊQÃK™f–ĻQ  G™‚Yvž’p ïĘŲĻJüČUĀÕJ_†§BE [‚*Cč @Pœ%üÓ_.ĮĀƚ Ėf”5š˜x.8PR‰e•―€›ēĨÉVĘ|wČIÓĻĖäbÏïē2,! –AV'A!Ïīoðˆ ō—”҃­^ĪC†ĐšŧÎtü<Ĩģ|0š…l[–)pŊ”äÆ:+Y)]Yq‘nī 6řÁÐ̓qp rNģÁĨŦ ]^ÔÁݟSŒZm5WÓFĀ™fšcĸūfÄ|,ŒMkkĨĩļ}?ÉÏŨÔ!>ÖX€JķnÝšĸĄOÖ(pwũîÝĖĖLÔ )Í*ĮõöîÝŧzõęŲØØpĸĨ G°·€ķļü‰Ŋē"Ø ļL‘„I`›ÝwåP'Z‰Ž`["72ēV€$;ĐDjÂÄ\ BYĨŅc"|ũjrčĸęRēRálŒLģ`jrbņR Ķd䖁‰fÁ1â[Ø% osJ&ã—áā ;y4ŽŌąUÁŋOS ØqóĀ’ˆfeY°č€Đ‰ ĩŅ:i6l#õG„Θ\ĩeD4`$ŧ Ā}ĖX,Ð;Ņ… Ó(Á^fĒ3t0Œ%ڔ‹5`ė4•ïÓÉ5QēŠ åeÕ*^'~°õБWdPÓũ"Gnüc“™QyNĶÔfükAŦĶâĀļ‹hÍRŌuA2UcĒ/Qwø+ý—þ8RNFongĢՔÏÝBÞ§Ĩií],Tú”īˆ$Ũ(Ë:įX?ʂ”qĒ(‚Œ"{öĸu3ŌSßkė\Ž5åó$cČÏ~_(š:W*ČI/4N•Žų2ï:>üsU;+ŽZB öd+KÃČÎ’”D <ũ[NA*„ũÖéŠ`q Œy„Œ—‘É,{dĩ {ÂŊ`&#$ŧxÏj5šŅóŲ“ļĆ˜LƒÖ ÓB$€Vē’9ORϰ<†_UšRÓÓ:ý(Ïr‚“æBH, žž=9Ęeūč]tĮԌÅȀN‚TĶ É|ē”9l‹ˆ+ ~#3đ2v‚$ąķąîņe/üŧuÓ7z]Ž=éļ^Px€)- ĄSR‰Üāīąe-ŌÔ<;âäŒĖ}Ë /ó‰˜ŋžĪö€æx(ðÝēˆ3üü#Ίr;s ĄkFSĮĀV―jTąãþ,ÜâØÓIĐ4‚Äؐ8-eÓ#rŲ‹ēg‹3ĒV„oøéŪZe9zÉĶÞÁÕĸïũ‹ŽŽ—į―d€OËGúĀũû;ģ3û‡5k }ūŲą6ŪüôUCGŨ^ģŋŋÝãĢV…EĻiYöĒņf|Ūģē;PCö +ïfĐĖx‘7ĩkÖĄ[sĸjĘ? ˜ÆŒˆą#š‡ïîVËöŸzóčöĸŧ%é2­\Ý~蔀O诐ýpýĻĨęý'g>\9ąĶՉy_ĻþŲyøÓėAEķ° e ezEÚ1‡ų iģį/]žŧuĮũ=z}œ0 ™˜čZ"0S‘‡þ, Ęc@aO€dĢpës8iRHHŒG €HA)+sÓð Ø#ˉb~S•šÁņ,‹+ÖéY„€ë%ōq“ã…\F žB D‹bBŒLŧK’9ÉØČ4h°!ß{eïr|yÞ dD–$ÉöŽÔ0âÏúõ@ÁģD·Š%L‡ü`°ØĪ P(ð–cąbøÜ((§‚R á]™Eá)LŒĒjéĪ’Þ NnØ_~TšĻĪb‰ûõWäôĸðkTåžŧ{隯Å=ųúÖusįūŅyAiƛYĄ\ĨüŊÁK^co/‹*Nƒ—– M œ*3åÁé#Ņj›ãïÆGgęÏØ6°í8ņ O?Þð–ņþžĩËw― [ķvlûEáû’ßúđIZMČ đMô›; ų‡"’oNî_uč:{S”ß•;žģlQøč…;SuZ‰ŅĀ›ĨŠ "l‰’ôÔ6°XJ“WþųMąī&…ĒœkgdkÝkļ9%^ŽîÚĪÓŌ#OčĄã7‡JkþÍk:ˆßžÄÅFĸΧ2ß|LdûÛ›Đąøjôš3ũ’åADđ1]ĪĮ'ÖÏ_ūýØUë. ĐžÁ,ÝB*@ •ÞĘ&s+ ķ”FV=ƒ ĀuD€3 0äääTĨjUdsÝžqÃŧfÍõë7Ž5ĨhóŸ_P€Ī€:uëÂÐ477ŦĻĩ°īĩĩmÜŽÏ1Ï;ƒqū~Œ&ŲXÛ ŧŽē›;ÖŌĘķ~`P-ŋ:Ėo&đđ{4kŲĘŲđ2šĄ?đŽ8ŅÕÍíģ ÔsŽ„jņ5BGPƒ––VAMûøÖFOH84ŠVĢzŪŨfjÔ‹·ģ·ŊS·^@`Ã*žÕaąšŠM …ÔĻIpóæĩýü€’ļĩ öÖŧĶ:[ZZĒ/4€Óðæ!ĢQps33s,ķ­NŊƒ}ÔÐĢJU9OÁÚÎæģo°sĻ„(7 ļÞ ŊVĢFý@$Ąė.ÖÚ!Âr.@[Mڏås#V œNāŠ$úÕýî·XRh­5ė6yĀÄМôā]HņÁŲ ‘ÓŋÜsôLžĪ,æŒI\7Ģgdøžįé:AĨÎËzķsqĸȅcoŋÎ6—·Í<žįë͋7$åŠúߍOó vNN}–[ŲFŸPJã8Ãŧw:ĸĶ-šô [üe›0óųÕI#ú5|ŌÝä\vƒš&pÉKŠ™9ŠWŊ>ÓoÄįðdz—ÄFoØŦgïƒŊ?{ųí†s§Ïž2~ö“4―‚a1û: /;fáÕĖ9íÔÁØ$…ėĀ萁réJ(ĩýA;ÎȚPzÃĐąrîܭ߀‘~ļxqnOˍs#_Krâ7LÞŦũˆ·yX†žƒ+æöîÕs|ØÂ4$î N|·žgÏ^+ö],f!™ĻqÍ,VņŅÅĻ^={N‹ü!_n9I{VÆž^ģ·Þļ}vōĖ9ã†ô™ąîXĄ°e įŊ0~UÔÅÆū~ðېđĮI€_d"IäĘjÅĪ­ X}CŽU m˜r„ üc8­‡þx`Ïn@Õķ;‡đgŨ.ÎB;Ĩ„átßŋ/lÚÔJNÎ_Ŋ^óč—_>ņúĪJUÏëąŨĶNž(ßũpyÏæ'MķĩģÀûöîY·:2•iŌ…­cįœé5đĸã-Ŧq›Æ4˜=eÔÞx‹y#šŠ%æZ•ŌUĄēó ðũvįEQþUõËģû_älûvÄÔîÝOÞųŲ Yč# ēĸåõ—jÂĸĩČčŠ`/‘=Īkˆ‡äĻ~•*d Ę,nÃ9đŲϞ>đuë֗={ŠÍ5_~ŲŦ{ÓĶNn.[ū"{ÆŽïwî@đÜ6íÚ!gŨąSįļĮŊÆ\•Ų ŸšūK–~ČËÅŧŸ0qŌ[·ŪÅ\;~‚Ũ§Ÿ4oŅâųģ§ōó8VĒŦÕįmg†Íþfã†ŦWc€ČáK—mýv uÛĩïxëÖÍĶ͚Cááƒû3ÃæÔŪSgėčQ>ĩ|fĖ {ðóÏa€Wtâä)wnß:}ę”ŦŦ’ÖŠT­‚˜°úõqamĐėęÚīi3Č&‚FĻåë‹þmĀ)xņâ9Ž·V-ßí[·Æ=zmҁ“ō?>Šž ÞqÝ`9ˆ@X+%ĒĀÉŋkôvŦð™‹úõ:9aíŪNâģŌš5)ČÎĘ;2æÉ˜/L/øýŦGÞĪ ČÎČ-.I{—–ýîĀæSíæÎ‚•Æ,§ŪŦWN@įЎjĢAOÕä€J渘]·Š zó’E]‡§Ņŧo$ ķXô4šH%īMŽĩŋŠUÆ1âZÃþ$šI(æLŒĘõúîÝŦOœÛ}–^ ĘyĸËģäĘÚüũ /S‚š„Ø+Þîßt2ÕĨÕû”ũ™ņϝš3ąE}ŠOŠČ?ģóčýŠÎYié)gß˜ÞÆxóbĘÜÍûĩöáT(ųÐ8ļAŠo›Ïk ųElMJS1kώ“Ŧ`ŪJ'Ûm;ύ b>ųķĊ‘b‘P[în1ōČģb\€%Ŧ§@ŅK’$į!ąHX%Ó”čņŲĘ š\ĀĶ&ÁMLUtÃu§Ø=|ø |áü.]!S‹ØÉóįÏŅPžēmėėĒĒvE„GÜš~ãôų ƒ›þ1bļ:‡x}ępčĻÝŧsēēÔj \jyŨô†Ã.ÆþôéÏũîA§1jŨÎö:Üļ~==5ĩžŋ?žčCCCmlmq HÕE6Đ+;6~ô9aÖoÜÔ ĻaÝšõĀÆ8°ÏÕÕ―cĮNĀw 3DÅĀK4mŅēvmßëąąāRaÎ#”gÁÜ0WWķÚ!Äü "BBC===q\+'ž|‰ $ũčjĶÖŽ^·“"†ÉģjUŽķzęžŠ4%$‰älXoDcĄhŋPN~1äĐå•pĻĄ”>^ˆŠđz…KH—9}bf ―vęĒõ€Ķ:)%y:eåþŸTm˜!š6ĩ5þÚÁ‹ÛÆŋčąĶķđR‡+ĐôŪ ›LëaĨ5ËJ,/ž[Ė TaããP+Q ÁáZߕð’’Ō`C!ėÅĩnŦ›Š?ué6ŋáž6æfæFŅŌ§áÚ^ĩí•/ LL^ŊįMČÍĐąô ‹čéjŠÓ•H*3SÁö’Ô*‹…’Ė%ÔBōŪĮŠÖŠĸāĖaNgå!$8uïģ/ĀY1žœ-)Ð=+”a’ĸĄ„ĩŌ 0†3É.ļp î_zĐu5Ģ Ÿ šljÃī…aÁÕí*9·ýaĢęø‰ƒƒûœ^ģkžFĨtÔŠEƒŠë’e^~~E…:‰LÁ>ZKŪÐā?bJgïF C‚ ōˆ―„ãcT!§Ø§‡ĸ™ŌÉ'œþ8ædBūK5ÝĄã1^ÞūNFŸ{6:ĀHšt"˜MŽ ĐjË91—A*(KĢŊČ å .h,‘ŋÄ@ž­­oíÚČĢeÁZ ĶJĢcŧ7**>>aϊ!ō ”ØT°B[rŠaĖĸūLĄÚEáýfšä·oðü ‚ÃÜĸYÁķ€ûrĶ–ÄÄī.œ;›“ûb ûöF}―zmŨî_ļļļB>ą° x‡Åc•™ßm߁Ēå*VĘ!#3CiÍ57bŊ―~ýš}ûĀVĪŦ%ūNDqAAAŨî=ėöDE9;9·ïØŅÉÉšyy(nÁ`@ĪŽ{5 5 ÆPOŸ=ũX­ŅāÐēē2p[;VFD‹'q=räÝŧd˜Ūļ\PÆ!hFFßō,F€Äzʍ@ĖLĄŧ‹YņQĮ>_‡0 ―ÎÜ1ĻÍČÁ›ÖˉņÚņY§Ö §XVäl) J+.=>-ĢZP§ėWWŠr3ükÖąĖįĖ>ų,X—›Ŧ51š˜›éēßf―~RÉŦĘĮ€ gE"EüŋČÞņČôŽŧyóE_ÓŦŌëäBGŸOęÖōؚĮiƒ›―O+0T㎋ķUę·M uNM;yįeįIŽ{ýī3b·ŋ™ŧ2—w ēŪlõjëŲØÎ͂}=;ļõĨÄ>ũŠÚŌýKP0Žũķ.XzōHŋC%ą˜9ä$ōŊþŧōëôųũï^uĖ2}yýÜÆïīė[?o'o—79ÜČÁ†ŒLxORã―:tlwōāŨIųöū š|üeúyy{Î p,4Á=Z|ŋėGĩûčājšī|ÉÖÝÏ'Ā|ÎÚuŠî†<. ĀËÉÕ>öYėĢĮ>U­xÂ|ņ…3'%ïÎk"§ZKÅĶ&ŲúÝ6ïļTŊōwŽ'ÂPþđ|PËŠ`QœŽ‚‘ķôÐÉęnņxĨX؊ĸgþ Ó{~Õy2ßíØþæMbJJ*`.æōe•œ˜Â,•@„Ýį_Ŋ>ŽHŒ€ JYC\DĘŧ·§N?ĸz~}úõĮN°™)ïÞ9xpÍšõøÔП~éÉփ‘8<ĄĢĸãGŋėÚmĨÕÂŨ“•͐Ð9pÐ`dv>þlCJj °ō`ôþÄĪĪĘ.•ÓRÓ==Ŧ2Ϟ’*'r ĖûÓɐÂpïÛģ'=õö歛-ZķÂg>Üš8Ą]) õ–Û û]~  ˆ]D;ˆDXG'%ūvquwŒôb9~ ģäį%ŸÄÅAX)Čhķķ6…°ŲĐĮč^†aYvŠ2–ƒTËĨEsŅ\ÍSÖÅĮÜÁøNũz5 ­Ŗ‡ôM}óäÕE—AóB•ŦŊ{ÂŦZLóõŪ–~gũý·éĶN Z·iagoÓ~ÂÆŸķ-ŋðÍÞÖįóás<ü>oÐävÜūMžaKœ,MĨßÁ)ŲVĖŠÍæÅņČxLFōþ‡ŠDÉĘÃoÝžþ•\,—/XžzûīkUƒįMéįåŨ:ÛÉLe[uéáĨË6Nå=kÎ^ôYÓÁóŨîÞīœW™õ˜Rŧg둭.-ŲŧóÛO—,Ŋjaä•bZrþįÃúļÛŠĄØŽóKé7r‹Œ> šđX)yĩ]“ĶÁÖ&å“ÍWķk^ĐąŪSŧæņcÛãäjíôõáC5CąrŲ–…ŦfO―ŠR~2eųĖŠ™ÖmÎ0ōõFOëRÓÞĶւ ŌšU ĶņžÚ·ũī Ú š6w5WTk;b…Ī]ōí’cžÂŠqŸ:õšE,_é°`ÕÚeKUUZUóŊÓķÛāë ŋ]đäÜ7›{˜+đ’b„lVÚ­Î„Ļ‡Eä1hÆðĻŧ õāđžëK|đŦ}VTa()Ō2a+b‘EĐ\" ĀÚÐa#ð ˜ÛšuKÄĒ…ČpÝēyc@@ĀŨkÖĀĢUÉŅiӆ ŅûöĀtCēCôŅĢö(°oO”ÖŠb!MpõŌåo6m1bÄéó‘Hs%æØáƒČ ļÎŽN@í…óįōóóÍ-Ėåâ`{výk”nïÞ_f―sįö/ũï?|ðāÞ―;?öė“Į4ëWGFŪ]ĸCÔÞĖŽŽJŽŽpPx9QPŊą°Ļðܙ3ÂëŲ3§ĖÍĩéĐi—/\hÝŠõ›ĪĪŨ‰ ãâBŧvKL| X41ĨÜÁā(ä†ŨŊ^­]đ`ÁĒÝ{ö[@„ ĸĀ4fĨÚÁĻ嘆Ŋu›ķ3fÎ0hĨÖō䉐jœ9+ ũ†C?îGl2)‹óJV[’<å jMyDp˜*9‚ōß[ËÆŦ}4â™―Ķ Ų„GĖ™ÞĐûŽĀ/Y7ĐâWģŋĢ €röô°ÅÍ=ęYmd ‚OĐ.í'lëĀSīPõ·süZNä(…Č&ō,ý+:,W't`t— ēð ó ?Ž>­·|ߖ‚ 9 å”―GĄ€gQĄÁæ“Öë~héz}Q‰ŅēûČð^cŒF^%âĄYtYŋ-JdÓÄčŋÞ3xĻëOšmYÓÁ0SVJ†"ÁXsâĘƒN'KĘ2Vþgz"ģÅ~āŌÃÔō+#/3*{ïé[wMDdöāk’ø6ۏw4’đƒ>08-ŧZÔs, ðã"ƒ4di$œkŪŪŨ~ČąÎÃ`.)qfô%œ“oø:v˜zÔßģkŋųPgž1 DŊÔvŸ8bŧŠ OZõu:Ž åôiEvēï3ëq"#ļų á/4i‘ŋß “qø'Ž17·―EÉW$PĨ(.Ą Y7w]eBČčâę‚XfÝžûðá}€Žg5ÏLīŒL XŧöėęõęŅMvËØXŲTvsÉÉÎÁá8Ø9ĢaÕ➡Qk*9;ÂP}›œŒŋ^ŒŒŋCė‡uėUÓqbHÞ›‘ø:ËC ĖöԔØGHžģw°oÜĪ)˜øÏoÝļž•ÝĨ{XÁØ"SŪ3ą(<Äqž2rmA~>ĮļÝWŊ^C;ĒjÕ*D°üáíģČÕ?<ýÜ\ ėĀęKrôûĮ ŊßpēQņíŨŠUŦ4*.ØØØ&MšČÄô_›ĻŒ#įe9úŌūÐRÆ)Ũ3Á)Yūû͛7[ķlų_Œđøšãã_âŽyxļáō.‡Ä\œ,%]Ŋ &‡`ßĨĨ§‚$•%Ŋ˜8ÓˀEÂčûx"7)=ÝMŅLL ux#6”‚‰ÚTWXôäi•Č%aoãí[7ˆŒũ$q”C†ĩkÛÖÛŧf䩕āj5$kKh™h€ Tė‘#?*lŲ˘‚ēb 6-ķB_ięÃ˗/^―RQē–HôŪJŒ{‡Áåz 3Âæ6mÖ ęāûũFÁá!—7ĸĸáýƒũXķsss@ėŽ637CŠÄëW ø–Æöe|<6°ėGĩīgOž€Z‘5ŋ15â+0 FPǃ ĮēŽ=Œi! ŽĮ(į‚ÖY°Ë!ĩhG`pI*™ø‹――ÝnÄŋpvķīu°3%ąîß|Ų“CŒUĘÏ/Ė|ųĒšŧ5ÎiÎ"DځÖwttD 3Îí–ð ūMܒaŊxyy•ąr|áO‚ ‚››.žĸÅÞģFqüýįíū=E#ÄŌB‹!–Á|‚ ßÄ";‹~‘ (~ÁÆT6,RÅÚBc°ÉKÞøŸ™K§ ‡áŽ›Xė-ŧ3órŲߎģoĸoþ&ŸāÕqxxČoóŊœ]XwŲ]Ô5PíīEŲ•’úûQč†Î—ÏŠI•ûÉPd#š/€xē·ũ‰õ ũ+ņHR"ð•n Õþĸn[ŋú`mĨôŦ—/Þėî2ī#þ„ĮHžýŨ:ÆË™Þˆ~$Ä"]Fēr›ü%ĸÅ·G&Аl>pUGŠ=ÛÞzĸî-ņg‰.Ä@捷›; ĩÖ,RŠ –SĖ̐ækéŸg=ĨÆ+*dš8pOiÄÁšũĖCãŠs‡ŠjÛÍóœÖĩĮä7Þ?>þüõã‡/ėóE éŋQë íģÞYūąđđÎóãŋæ<ĒjFĨNE„ũ vĀ8– JĄ•••ƒƒƒo|ö0§€wÞr&;mÚæ&@jöCs.‡ ^LPF:éƒ}·’ŒĄlےKËįO·E2š†ÐÁųS DôĻš·„2ŒčMĄĒx―ģCo|ŽÏpŊ–3Zf:Sĩ[QiZJÕÁ ԐîZ>u!óŌ cĐ‘ņ ØÞz ų2Bow‰9ęŒE †*˜0 Ąé#â‰tH&ãl€îœ’€6 æSâBg’g2ŧÕRmĨ”(9ýN’Œ"`Š–ĸßedÄĨĨ›mŽĸqüëũÕVOĢkÃōmÎļŧuAę:jŦŦŦĪįąŒ…fÄb5 vØOã(.fĖPd‹Ė› shĨþøÛ˜ėīÅd/B―@\Ÿ%NB@Ų*„šÄõŠjui+—,8ĐIÉ/ïäXŽ~5_MÖi â/T ŘeXT9ëöCÏ~*Ą‡ėˆČ҃‚ģUŲ*ŨDüŲ̐kQDŨZC­ŠĐ˜V–ĶRˆpeï%ā˜N™zĩĩž‰Ŧq%Ī8‘8ų’ĐR†Â„ €Ĩ,„5<Ŧæđ/ÜI393}ĀøË.Ž“! ĢŧĒL“™Å‰Üi°–ũ‚ĀœSžþųį~ũÞõ1ĀõęHäÉ\Šû -I”fÖ.Ķے[šk›ðîū@­šJė™pÍ―æÕĨ wĐņƒ$ĘØ;ƒi’Ü“”"ŦũmlŋĸKؘéʐD†ūÎaŋūSĄ‘<4K`„DJbuÃH…íššÖQ5’f:Ccx·aR=Ýŧ"’TÍØŽĖ3ċŌZŊ]•D2bßoEˆâBU‡âGga;Öō ‰ēîû&‘Þï·Ļ•ó~ßRÄ#J;"Ŧ ēD…ÜM0"m‹qÆãvwFž—’DĻÝ―ïu­ˆčąÖõūßWĶî‰P(LôÞ W.…ųZŨyëĩ’Āņ‘IõLÏüÝĸųüųý·ĮŸn1ÏL /…fflÂKyï陠2ó”~`€ėnBz\{kEŪŦjc°ÖĒÐ5]“k…bũ˜ĘÓU+ÖĐ:ŊĖHu ČĖč7H Žýi Ī]ÛãĖÆî™ĐQĩąāqUE2ŋÄWø>öąkÎĀüýÞđ22Ķŧ{2âÄ"€ˆ13VˆŌ‘ÅČĸKū!ϧ~>Č`UŅĢHĩw(”qÜĄØĩIЌČûū=^Ũ"ég{€øðáÃo7ŲË[Ąé6pôŦ~ĖÐëZ=åq(OšĀԄr<㉕Ą°Ýc… t?’"P}{$ė]Ũʕwݰ3$ÅŪęŪĩÖLƒxĖÐ%…{ÚĻ.€ž|"`ĖĖĢģ!å]7?*ŠuœÓjQĄĀĖqK"âļšÓ "(3C "ŧšĪŒØU )čŅY{@ˆpOHfššg&BåáņyŽJÜũýÏn|øðáũ’ZēšV.‚cȈéŪ{Û~­ŦškwHāģŠīVîšŧįHawW=+ý”lĖ\=ißFŪEpß7€•I?HėîG›båLÕ™+ŧzƙë$ŨÚūŪKĒ )Du·Ļ_ð‘K2°ŧÛðĩNØûķ―ÖõL*˜Ū’äņ9(“ôļÕ8OÜ@*ã)ąíg4 {Ž­ąrÍ lk]=SÕk­sBh82mŠÚ~?ĐĩWŽęŪ)3ģŧu[’vŨÔ\ŨUÛ31~Á€Qĩa„ĻP͐€!ifxΧŦjKØŧ€‘RũĘé~ŋg+ŧk0Z åŪ]50Ï­,đ’Ä}ŋaūŪŨū5œĘPUI É Į4šjWņôM+äãTLĩĮðxvÝ3LwÉČĄA)šÚ€ Þŧ@ūb{W‰ÎŨz­H{l(BÁũû›Ōßvx]_ĩËãOUûáÃïØ˜ÛhÄļũ!čXAģ{"eāûýÁ+m'Ä\yŋoˆ#Öūß6%ŊžAŅm’!i?þl*$―ïm+Öō`Q™15KJfWapåīũkĄĢđ îÞÐNÅýŧW.Ã=ƒãˆu<ŧ{@bÅzï7ŒŠĻ*IĄŒ―oōø›‰ëZûÞäģ˜8=į―\3fh…Nm)ŦvÏÐDļíCĪũą"{Šŋ>|Ī–˜ÉŊ|ýu“Č–§ā)Wό+­Ļ]ŒfÜß·é•ë„ĘÔîsōc€`·G„2ÞũýhnFæÞ›Äz]ÓUc‚đrš{ reötۙšqշ팘†―m^ŊŨôLRKđ{ąVΌåJ†B]M13ÏÝ^>ŪÅûýœy‘°{­Uĩ§ķąîé)į ũ―gú_W5žÓķĮĮ˜Áp*Š6 P2tŋßyl…Óĩa)ãä§ĩ2ߏ[B("ņōáÃįXŒ€īÞû I4 SĪpïMi­ËeOOãļ“Vdāą5ŧŪëuRláVũ€Ęîē #ĻŠ›âó@•™=UÝ"Ū\ŧvuK$9RŨŨ—îō8"jUžPÜĩOĨIóÞ7ÅT ÜĩqZf0 Ũ9y#C"ÞupüY ŋxTÛ$)Á†+#ũZÓĩļŪkïÝcIĘčG͕ũÞÕ bzĶMHĄÝmûņdb0øųðáãÕþô˜æĩBŅ=†tÛĒ"ŨxK‘+ví_k)ĩû&ĩŪKÁýū FFMÁÄ 2ko -Sc`ˆŠŒigdí}>äĘũý=í (Þĩ DÚE2_ËīDcŪžšjÆ0Ĩh·ŋöĢęųˆÞD0ŊœįҌx”―ƒZZ3îéÆ<­h˜^™]]S™‘Š}o>ÝÉNæîz~@‹„}ßï™ūV’Žqf~­/]ó<SM8W č)šø ųðáSÕķŧš ˜öžē-Šŧę}ÃÅþþîj˜q:ĢĶÝ3―fÆĢ†§ģ@šjô€†ļënOdūžLÃ]ûôŨÞũîЕQ.à EÄŪķM DÏĀ– ŧęŽdæšwĖX=ĢÐĩV(ŠvïšŪÕĩï.ŸdõHA.Æ#øÝ2"îÞũ}ƒļō āu]đŦėĄ-žJÏ·ú―7Ĩˆ8w3"âĘŦö(†ÝÓ}LOQĪǧL>Ï`p’Ŧ ~7>|ø\öšë ÃSŒg‘ZGFfäŠ'ėsŊ\+“ķ‚ ―{CŠL“€Į&YSӝg>K’™Iˆ‚ŒeÚ=TddwÍĖ™]ģïÝŧ3WÃįŨĖITķ1ÝmĒ{H šæģ ãk]=]՞ÎX{fŒPōīĀü…AƒeŒ1§"ŪjĪÖšö.Ēȟ+ūÓÓ38ÆČŒó‚ũýOƂí2ēįd§„ƒޖ2# ãwãǏŠĖĩũ&)†íęRÆĘkÜÕČ'qÞĮEmÏRĀči"ĒšŠnÛŨšfÆŧÅ īëŪŠˆ Å{ŋkwf’8z“Ą―ïŠūV^ŊË6ČLŲ8E.Ĩ]ÕS†%Í4ā WŪŠĻ„ûýžž šŸ"ÖuՔ Úó,ž$ũ].ŊĖq{ÆF0vÕÁ•ųXÆ‘GÓmŸu SXkíЁĩ’Æî2MSėîɔ$ÏHJiïû―wDÞ3ņáÇßnÜL=hsáŠÆ8VTmؒí}"ŋ‚ а§]R(†ĀĩŪŧnĩVJU!ŪĀ3Ŋëxwį3īðūoI„1Øū\ŨēÝ―#–Č―7E"֕ũ{Ž Ûï}?Ŧ}gÚĀ1š{āPęq{CĀÂýý†ąrØĩύŨ}ŋ ÐŨëkwÁˆP ŠķĀŨëËÆūß!)Ū™Ųû&ąî'„ęœÂUïKRčûý^™™Ë`í=ãŊŨëÞņŠ‹Ī{„>ü–U-Ĩ™‰ĖéÞũq―Ūî!™k­ęŠĐȈāØcgėˆA`š=ýˆĖU] %ļŪ”‚8cgŊë{zB$ÕÓ|>(Ģ}ŠGIšüTĘ"beT•āĩŪ<ŸđŪõlÞӃÐŪÝ3Špï[âxDđÛ3đŌxړ‘ö@šr­ë:þŽRëŪęebͧB‘Óm˜VæęÓ♊ęšņĘ€\Д„}߆ŊŨŦg"•qÎĚ!ÛøðáÃox,6˜ĢJcdäWũøøÃęTxßũtGh~"†@ąŧ%íÚBqšÎDFĉÅųúĮ—íyeļkŸz°ŧlKaãœVåĩNÐÄs053ųZ~$;s]ŊĀžņÏ€Ýą#FyŽ‚ŊgĘâ˜t€ęÂÓŅÛÓĩ7‰PΜWeP=%1ŊeĢĮdͰcåx(Ī’äĖČĀíģ{tÍģ ŪžŠ6‘˜égk úĖ@øðáũœėĀĢÆ$ķF†ČÚu|OEœ6ĸŒ˜Ššp­Ü―1~b^ÕMÂF{ßįuUUÓĒråQC<ōô…įūïžÃ“Č“š~K‘kí†==3:ÚJ)2Æî*IŊuu7ÅȋÆSÅ;Wō˜$df’Ü―cÆ š;3§ËFĪ`VÝĘHČķgH‰čj‚Ũë'āÝ6P3Š AsfðĸāøũāÇ|°ýũH-Áûūĸįŋš{­üŦ ķÓŲSÝý(UtwWSR觟5ŧjWc†ĄŋūŋDýĮķ-Đįđ#œęrŨ‘Ą|?•ĐĻĩÖũûâŲ3”úŪëu}Ïũtg„ŸĨF$Áá$c“=ÓUŨšūßÓÝWÆŋĒ?ĮXˆŧŧņüüĩĸ|Ī"§ŧš%Å­Swgæ<š>žx6힐"uŋßg.Ïü5=ŌņIŠZÄZŦgjo*ÖZþųO™đŧ7ÆW.ƒĸýįûübáð`û?þųŊÜŠîÆŋ>|ˆČéÉŨŨŨß#ĩŧę?þóŋöū_ŊŸIQ{ßĮ–(âĖ—‰‚€Į3ö:Іˆˆ5ŒžÚ°§ ’˜ņņâDŅĀķs]ݛŋĀŪ­JÎÃįÎ,)§›’ĮGF%‹ãßIgˆPU_ŨŦzÃ8ūíØ3 JRרּÆÏôŽĩš įÅ3 # WÕxV.ü ęquŒ‘Ý”ícqœ/Öþ—―{ŠŠÚãūÖÞį Ę;”hĶN>æŌõ^MģĒrî­ėÞk>3M󕌋 Ļ* ‚`DjBYĶˆ%‚ZˆDāįyœs8ïsöc­u7ĮéŽ3zļĖÕfÖĮö°{ģÃĖũ,6ão9ÖÂR–š…ŌĄãÎ[LͰ°1‚ ޙģx]ŧVŦŅhEQ= Bhô葄°99!DڑMæ˜ÕA'rŧúŽ―LîųupWÓc'HßĨûwvRāī'ÅÝhķûwvÞ,„/Øíķ;ķŽÝYBEQ= „@‘ ũĩÝ…Ē(§þŸ­Wĩŋ3ŠĒ(ŠFíEQ4j)ŠĒhÔREQ4j)ŠĒhÔREŅĻĨ(ŠĒhÔRĨsč5ŠûÍä!Eõl·'.555ét:žį{þÓšđđøûûKĮÎjôz―JĨ2 ݔi‚ŸŸ_PPËē ÛhÔRTO‡1V( ÃLš4ÉÓÓģį?­Z­Ū­­•’HJ[ŒņÝ9k·ÛëëëĮ'Íi‰ZSSC2d!„þĮÜ^‚Ē8ŽŧtéŌīiÓĪMšÁCĒÁa„ Ē(Þ5‹U*•FĢqōäÉāá$ÅfEE…ôÉš=EQČĄ;9Kx“ĒĄîƍ›Ę–N$ĸĢÖf5čtzÝAę*Î^l2€îņöö6™L·_ ÜsUëîîšÁnÔHýÔÕ)Úufg 9‡Ž&3xÐhģŲ!ôBoCŅw]!‚ķ5]HÜõÝeÍí-ðnƇËgŽ„Üŧë3ķF}WÓæ&·ú|~ÝęEÃ|Üšž?:ģķ|nÖÁžÝ‰˜ŪægßŅˆÓšÎÎæîښz“—Éíļk`ėŸ=ėęä’;rÜb° 2/O7(*·ŋõþ”Îæhí~8éŪF-EQÎĸ‚ §ÜųŊY%Ë>ū)Ô\,-ÁýEĮˆgĮtfĀ0`ĮFІ•![ŲųâþS"WŒÞ^žÞ+tÜķ9á€H:+ącŧ}Ķ  ÊŪÁp0 ˆtžt™DĄî|x8+ce2mÍÉÅŊ.ą8ĨpóŽĩ参úB0 ĸm!,ĩ":‚B€dewĮ%{MËYų ^/.˜3h +ÕuÖ;z—Š:/e!rtÁ0,$ĀJÝv‚ާ#ÝëÕŧĒ–Ē(âāxïy #S_>}äŨþYßČ2@ÄðˆŨ°( L”ŨKũ%n;Ũh4æŊĢ7ŒōqEAdKW=6ü‰gf 52 š@sKíšũÖ]iÖļy?“”ýd°{kýųļũbŠÚMŽŧwĸ^ĖʑáJBÔî>ÏŪ\óĘhGΒc|Aä‹ ó­į%n^čïD·ĄÏý=#ąņ—Ē”„í”ÜāąÓߏŽíOŽÆ&ÖĘeÚËĮKZGoNJ|)ذĸø—Ęâ·*ŋ[Ÿ°öZY1~bŠž6wga{P~Rpę‰WV$F.ôÐUmÜþãŌmï u㠒Ök&F- 8[p igNģÏð 1ąģóX–ĨŦZŠę°ƒģ“ģŲóĸĮƒ=EŧGВ‘ŲŨ,ß—|–ø—ÔĻEóŨffŪpgąT[ەä^zäšÂåÏ13Ÿæõ óWÅ &[Þx9ũëŲĄ‹|·-™søžĖĪŲŽÝæNė° nJ^–PŽ™ņQüHÞ.ûøýZ*pÞ$H4˜,aãĮ ėí āEy{ýŠeQn3W}ķ{ž nð+=4_U]vļš‹þ uPúÚŽžâ—â^™6"ĪuüsŦVŋØÏúCQ‘į›1ƒ ͟ïÞ>gãŪ„eëÖ$þôÆ?§CUÉđss„%̆ę*Eˆ`ŊýęíMŲąŸæx–ĮތLž\ïÍ:]Ų2 sŧMĩ― EWĩ<Ïsî…a‘Ā!,ŠžKŒĀʄOv&â§^}Þ§Naˆ›ýb€ŋߊ7_:―ĪžNŧčI/ˆ ’a!ėááóäø™ '‹rOTŪ™ęĸ}ÁÁ‹WۚUĶ)\{ûŊíeÕ}ߋ_6ČÝĆpDfĖZā‘ýÉ*_†ģs8Zg ð;—―œÃ=Ï3HjÛ펆‡”ũa›Ŧ˛ė&―öœŸŸũŠđÅ+ĘLĸ0 4;rÛÜГ K}Î@;]ü‡đy˃† zrĩ@Qā9^ð§yI;Vúš1™w Û#žV:dċÅyJUFÔrÆĖ›`Ŧ™ë/']―ý ŦZŠęeš^P?/_J™_^ģ>"ĖՕQ?cė?yVˆÜjjkÓģašV•E.“CŒ0”þIˆžš>}ųœI-…y‡ ūTԗ· Üđ:gS#€@î"íyejRŦŧF Ņ* ‹}Ã#^ï8—søógĒ͒a:OŌûYՊ ð/=PZ6ũõGøēXī™õļïđÅØĶ1ēĄ>í-*‹‹\°@€Œl;Â"đŒ`„8Ũ­Ļ3Ŋ1&wâđ?#Úė"gŎ­‘`ĩ^ԚąUõŌ%ģïËÄÓ7Hî1|Áš#―<åō~~2Q@@ō Ē–E=äÁūĄÓÓÞ}:+z՚ļ=éĐé§ŊĻĄÜÅ#hˌižĪlOÞĸ~ÚŲĮæĖæ!ÃH ϝú4#jíŧųÆˆ)cûūCq­äÔ1ÅuUUəvöņeïŒÍNX·9qObJF]'Xl^ãįí‰~§2}ņÖĖĢ<”3ðũjˆĮō‰ „ÞZ4éŽäīī䝑+·ÔĘF―0Õ-uũÖ}Éņĸ>p~ÔžŨũƒRd <J)jå,ÂĀАË?ČČ=nB ąY:ûŌÕÄ9–Ļ˜·Z"}ž‡đ™RâbVĮþØĒ"ž>{âîÓôÏËĘ ŋ.(Ņcø€ÃQÛģQÕÜÜ|ėØ1‹ÅŌU‘ŪÃ`МÉ?ēckĖ–-ą)i‡*Ū*tzCĮ­†ėöoßūãp~q‹NŊû­ZŊUŸúöˌýi))Đų'ËÚôƖÆ_§ïMJÍ(+=―ÏáŸoŠõ:U~væî] ĐsnĻÚ~.ÎĸĄZaķšŠKŋ:’“ŦÔčuΧĻÕęžž<ĢŅĻ―‹Á`ĻŠŠŠŽŽ4›ÍZg4ÞĻQÞü*k_llLÜķČŽoZõę›gĶJí|üõmRĮMʼn‚óÕuŌáõģߞüĐZĢëÐÜŠ?rp_úĄlE›šī ïŠR{ëÆųc'*[ĨįŌ4•äį]mjëÐkkĘōũėڕWTVQTPvĨÉ`0ķ7VIIŠO8õmS{WcĪūŽ=JĮÍü§―ŧWUÃ8ū,ĪũTÄ[ô^ž íl­ėEÐÂĘBD˜˜ųČ·8ÏÄb…,9YØģhøĸŠdœ„y›')&Ŋ@Ģ6„~!s^œTUUYš*yō*Ծ˞ÂÃĪ7įÃG-þÓ·ðĄk]Ļ\ŪŲ·Ė ÝAg…RķȌIK§ߒDY–u‰ÚæÃĢųøÐTŽ+‹ëå<ęqņ4.ÆUŠĢÜzWõeqŽwN‰oCČo(ƒ/·tõˆÖŦ2gcđ6ŋ-[Ó>ŠKvšŌBõ-j(ķ‹…1FÉõEOĐÛoÁÎ=ŦžĶ7oÖå&JĒý~ŋZ­”ŠiCžįŧÝn―^Ŧ§Ōĩ į‘…tšõß]Ļ_ĩlĩþY.—―ęė I’Á`p8ĶÓĐūg}ýMi—ËE †ÃĄŽĸ8AåœN§ãņ8âĮoĨ( =HÔÜ+îiĢÝLŸ€&ŠÖZĩÂŌ+Õý~oųÎõjŲXŠ&ŠęÚ5[Ú ^ŊWĨ­ęŌcÝ+z…ęôlPÎN&uSÔ)Q ôĘŊZ|O|yJØ$IīÏŋ}ģŨĢǘŠoEuĐšĮS„ĻúųzûF{VQģ:6{ĸ-D-€ĻĒ@ÔQ }óó|>ĮÆ€o €1ŧŲlĘē$mā;rVŦ˜ý1›Íæóųvŧ5ÆĪ€DĄŠhUĀ*f?‡öBžxRÓIENDŪB`‚doc/images/ifw-installation-finished.png000066400000000000000000000500071325366651500206670ustar00rootroot00000000000000‰PNG  IHDRÎGæąmOÎIDATxė’ÍŪÚ@ …QĨ,‘xxĸ-â"]ĩĒ *`Hf2“Û-EÖ5qïQ°U‹ ĨRÏšĸäƒÁl6Ëó<ÆøýRŧÝNū˜Ō…ú §Ö~ŋŨ)[äGžÞiŽjwøŦ|Īó?Ÿ~ԃðb“7Ēþ'ßČGģúÝPéŋ'[įƒó“ó‰1æy>N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/fŦ>6ŽĢŠÏĖîí}Úw>;vė‹]'ņWįĢNQ Ĩ* īI!*ĄŌ’‚*·ąĒ(óA\*"þ*BŠ„Zņ! ĪP"’šŠR+Ō€‚Bl“ΉėæÜģßųūvgfw–9Ŋō4ÝX%*ņģ4šũæÍ›ßüÞóĐúņVMúU­T DļÕLĸķū|#l„ú‚˜ĸ'üHÜ>?ęįÜņ DĻTž# sf˜Ĩ€Y6Žr˜ÓÆH0Î-NĐôBĀ„P<Ũu]ŧ 9ÅbrīmûfČ įXg Mĩzk›hŅ-„jęâF1NĨƒÜÂį 'jVŪëĘŅģʑ1æ…õ’‘ð{Į›ĮĪËĒ+ݟ˜[iyóø+éĒ‹0!ČļC4~éí“Ŋþ•õ ėÛ·ĸĀúU-z8đņņ­ûķ­IūwîėŪ_ýāęEí=îčÝÆT˜EaÚĸrüä;ÖSŋĩũévîčŸGļãޘĖĨÖlÜÝύŒ\›`ÜãT’čb‚„#ĸÄDz6õ‘MÏîíŊʎ\›˜žļza*īþËÛwôn‰ãĀݟŲÐÖŌÖŧëĐŧZ“mwú™Ý{úŸØ4sõo“.™*‘t—)ÐŲ+ŋúí‰ú{·ėęĸbîâ[gĮg áåĖdûš-Ï>ņĐÚ ÃäPsņýoþxėg?øöó/üüJVä.ŋqôĩᇟÜūõ‘îó§~ŸÖęÖķ·uÞóp_ßWššU2%!6·^pŠ~ĘÅēUÝņôÎÁ};zýØðDYÃ.HWՏ7ÓžúQ…Ą*d㍊›ŠX”•§pˆsģzŠš4 0AÍJHłdĒVČ&rýoĶÛįGšÝ>?„R û―åP*ÅŠâZcn]Š;ŧðŌÜÔlŨÔR‹Īå™jŌápXŠ2‰ČöZ]]]UUF€\FØ„ëÖÄęv &RÝĐOÅR Ģ ÉHu5Ṋēũe)“s:ŊËĻBQĀËGšÁCJĀš_·š78Č8°ÝĮoŪr y!2|zÁ}z~ní˜āOþüĻ)qÛąŠųhÛGïYÞđėūûãĒd™åsïėiuĖž­ŊYĻåË&nP#2Ē:ŅtÆ(ģx]gÏēa&gķÃįēĘ\;ĸōũŋũÂK‡gLĪcGžĀtcIGŧÎhSgŧ,MÎRŪÛš: ›5vīĢ89k!L\Qq–ō@›É•E+ęÃļX,Ę Kđą?yųŧÏĸdhZ3*šĄĻA„°L“Ų&ržIëÚZŦƒÔDŅ‹ëËīh[<ī°m͊%ØqŒH%—›ũå•V‹°—1il銞;[ãÁĻn›Vý’5›zĒGüC/ū:U°„ƒt fQZ9ýÚúé‰3H Øs\™5ķįĒ!$hĐ`ðÕÓŊüčĨã$ڐ0c…PÊĩËȎYĄÜ{PþäæūŧŸ4&ÎþæŨĮŪū›fþOŋxņ—§FŦ“‹0c’5Í@Ôīå“F•øØĀ’𛄗,_'QĒôŠ\1ššÄq1Æn…WLlAâņ™ū˜Î2VÎÎäFÎŧPŠ~mŨķĩĐ$"•I4ÄÄ,•É{}Αp$;6öîtĐ<û^úZ&Č`ŧēYSÎ’é Üær‘ķʁHĮvĐTī8GĻŌē Ô^ũØÖoėz4?|æėč d;w(ãfvôð‡>öøöÞÍD‰†]ls!“–ÔÉĪå·kÄŠ‘Þ}ßöxnð™eu†-ķqĐP–üĀkĖL\ÏĖÄR+š·ÅĖÎ4ĩķ„Đõ_ęÝ?øõÁ=.0\.ßb*[b™ØL0Îō™Ëį.ŒĢŠT …ôXÖĪÓãĸĆVÕ·…ÐŒ †Ðøû$įӏ„ŠūS ūTĻu„‚ÏßW)jbÐFĀyޟĒūŦĐjø”?/?^æóōãY?<~ 7ôÆŋHģšŸĶĄ8úŨéŠ| _ý\Ûu-[`Ķ g‰ ƒCˆ'Ë Žôcë+Æ€„øļøGxĨæjÚü=ܜÜ{ÎïޞœûŌŦë†a4îŠŲl‚Ņ4ŒęŌĒ:=yóņÃÏZõ‡īkåē_ē3 Yn˜&ā”˜ĶYĐTEobívûööŽ‚ŋ·™hšZߖRՖčÞėČßóų“‰7ÅL]•AŸ?M#Cl(ėųð+Bøþō·}|•/ôð' áߓėOÜĖ€―|Ó †þøĖ{üđ<AĢŲOįŽŲ0LŦQŊĶ1” itÉĖ–§–‹ĢƒŅa‚a4‚??7œ“ũóäÔkY3aËķ6_>íŽ"ņąT‹āÉĐR]ÉŌČÃĮý,ïĻ-G™&F<ęįøÄ@$V(ОgįÓdJ<ðŪTÂ–ũÏНî-#ā\ŽŨĪŲäØúÎୋÏ&FQGŠŸŧlšōÞzŠfwj–Ģq#ÃK[eÓķü‚%z,ģwaĀ$gvÏ,ũŽģž]ŪHzKð,FL+ķĨ_Ėēƒ·ĩ_Ę%ņ4ÆĨƉÁNaiãīzĀÔąŅ*ŋH~Õųv­H›L_ɰ CoT Uz…āđšō7ĀŨÕY%(žĢĢ―čJņôúÚųž6ŨŨ3Ä&XŠŽS+į‡šŧ!ûUsÅĖ“ČĘðņX” ŪgŊĶG::ú ‰v-lHŪk…“ÎOĀü°úßüäî <ϰ$|_ũëģÖóEDóŸæō<åfŒŠ?PDĖåCDĒ‚‚c„xČ&þ=ÝÓÝÓģxRüs|=ĩëd]âÍBšÚŠŨŊÞž­%‡IX ų\ûûš8ðOų؉? Þ2c(Ĩ€F"Ĩ„Ē‚}8ōøôÎíģGõ›ŨŲÜLkMHÎSJø8ŸÏgģŲųų9Pŧƒ`œÄ>]ž=øðāĸwŧϏîÏ^đ`|ëIÆ*’súˆ.·p’ LD‘…QŽâO^#ļēŠgT቗ý™čA0fUÆä)č+œ<ÝĐã.óós!ŸøÃį&ĪÉ9ųЎ<Ķ qQ’B[?, ØQé‚Zį2”’ą>t}I4ƒD›{īĪrQėü·―xęīt]J í—Z9:kĩ4>•ī2ІĘÆ.åÎŲ6`ˆ· Bŧ„Z‰ÁJãjŅXČ ĐÏÁIũ],ĨÅîķaJ€ĪÞ(*CEŲT†Îō—o})hÕk}ąüÓ`ÐōÕLûūŦ \Ɉ "*”óąĪˆA>æŪëīŌ!—>Ņ4UMk}ŒĐï-ŲCþh;Ŋe3Ÿvá"^RĀ ĶmžŅ>õ1B―>äÜZe|·ø:Äĩ‘ûŨķwžžtÎĻķ ČŨïŊîÚý!<žɆýá%a0žÛLˆāëŒįÅ͜_“žĀŒgĐLēáũ%„ā"Ëc ÓâžĒ?<šĐ&þPü•?|>^ҟ-<[ƒĨ @>p})xũŊá `…Ž]žŽ„Dšœ3þ— Ŋ}x@M$h!á[xđ­f€ü9ųK]bcZDœ'?Ë&āqÔeĖÉ€!fóIÁ øäë\YëÅË$,ƒoáœpōʼn?Ą,óą&tĮķøÔ|^%ÖâãŨ‰Ŧķ+Ųx$=ŸÎüN|üþu~áp<Ýĸ|â7ęýÝÝėš|\―ʇ âƒzFKð‰ųŸz…ßZå(ŸķY™k.F%’'ž7ÉâQšĘ§‡ĮßH]ÍGESĐųð§•6ŪōÁ·ÞŸ áM|<ķøĖ˜|N+_ˆ->Ø2Ÿļ§Į?įÃ6ōŲ•R†a‡ŅG|ßõãģMãÄßuÝÐÓ4•ĐP·åPĪP(#°%ē(Ž"PT1fÓL1[ļFßũÄņTÚ“-Ķ€FF"xg>ÜĘ]ÞA=īĖ―zVāiôz~#ŽÅ‡Ž&[2ųÐH™7Yá“î„Ė1ø˜Zāƒũqd>‰đĮ-|(>‡Rhž―ýRÆÄ§‘Pķð!>ÔûÐ{ Ÿî>ōąXQ>XžœßŸ(ÆäôæĮa,FëŸCýþÄԚ˜·ĨtŸåS|ßdŌâ31}ðAßȇ@ÎÁ#ĀŪã̧w;ÄļŒ âý ڙŠ|P–ųŨ|8^ĮQ>ąÃ_ðÁï>ūاŧđđÁĸÖÉpl›YĘ><[Ė!ÎeõõąŪÏ"žųu%W]ūUވö°ý~ßZšƒu>X›OĶ—ų(þ>ߛî>õ(ģ5•wäƒ]ðÁŒ·ó‰8ø ./ ĨÅ'vĢüg|ˆW—/óQwøûōÉâ/öÞ0Š$ïĸŪö™ļ‘"°IÎâN‚ÛÂâî. ww· Éâ°øÂĒ‹‚Ē!!@Üuܧí_!9Xî―wŸÛŧ{xļþÜmŌSÝõģšþĶŧč™úÔīĸ­_()"ÖGŽČ û7#"""""J­ˆˆˆˆ(ĩ""""ĒԊˆˆˆˆüuĐĨVDDDD”Zˆü_AČŋÍ%‘J(ōO:€`#úŨAPÍ~›XĪ*ōûEQhAþÍA#/ČŋÎ"FIĨ’€&ĸ‹}ՈČ ĨJ!ËÉ–nƒ p—ÉäJ…ü_íD^^ôî|ðŽ“W2 ä ųß·/Ŋ bCV\ WûyŸ™_ýE߈R^øû/!ÁÁ?Gū-R)ĸfJĨZ­R*U6•*|ýĩ ĨR–}ëôჇN$g•*Ę ā*(oÓēKËåōĶ^pĖJŌRSãßd–ÁĶšü*ĄvŠŠJŠÔ*EĨĄŠ yAæ‡øÄ7đųÅ=ĸåņ.ÎN<ĩoKč•9ErˆxUûŋ‚ā—“ôpfŋŪ­ÛīíŌĩk‡Í5ëēėĘ[ЄĸJPÞXvj劕‡Îęy–V%âĀqŽ(éÁˆ€€ĄûnļŋtaÆsËIp<@P3ß?ļwņÆÝÏsĩ$†þÉČI\{|æ°ÆMÚl;ĮK%Š‘ˆėāĻ~=O.4âÄŨs šÛ{v.Þš?UÎÕŧ=—‡fkŽýŲ yžË`9€bR ÜžbJ@§nOsôø?•&Šbēũũ[5n:ĸĀE=Z[ņ—VÏiÛīÕđŨ%3ŠÔ9;_ĸ6lc`āˆkQŔĸ—ÔÁĪČ9ždõķËO4, ĸ/áë•Z ÃÓ‡Žé;ô|ÄïúmÆMœ4°cŦZÕėĨGÔЕuÅ"CÖ$Ž'(+);J­áúC6ÁÃŽlláQpð<@ÞžÁicą&*”AjfXš††ø YzAցv)D}xÛēõ{ۚ­m0Ø:yAéĻë·QœZYFJŽ`+ ‰€āĪTðm#%IR"ĩ"?WqC?<=špŅŠļŒ  V* q‘ØØB$$^u+ą‚ņBSÖØgg'G󎝆ķSgĶ9qčõ…ĩĩ$ö·ã{ÞUk;ŌӖg8@Ālí Rōq6‹`đfN$R‘5l&I‚„ÎŽ­ĸ °b–šØÚÂd-‰ $-Iœ°ŪØŠ2q ކ°"Â@°ø€ĮœĄ…"s.q čÜĨK_[piϊęÁŽa`|ŽŠ]EÎuāQœ’ĨÜXšxņïąŲR;+•ÔkŌšKŨ@'’ã,ģ•™VNĄĖ–ƒņqXI>MŠĀÆąšwiÆÕ‡o ÕfĖ {û6ã}XDJâ猨kï6nķnžÍšvoïé.åXڑÂ{Š Ą†–P­? Ušī’Jp€ĩx–W8ĘÖZˆČĖb,ũĻ8 "‡wn‰9 0-$ŧÜ`Ðë FCIANVVžÍ+NÛąfÁG[výé…ZĢ–FÜ9―qûЄø;LņĖýWJF^˜uûĖÞݧ~ ŧ2{ā ÁCމLWŦTå9Ï{Kc“>‘ir―Nû!jÝÔqÐøœåG2JEņŊ›§H`(N>óũOÉ,Ėûw2xï•G1 •<áų­Ý7>ŒMšydÓ AƒŨđžW.܄Š呷ũô·HxŦPŠKóÓ~^7Æ3yöēØī…B!ûĩ,wĮĻv -įŸ*ɊþąūĢ}ۑo d čCYōčԁņĢ`ŨÁ[^Í-UĀŧåĒôäcû7îŪ ZGdĐēĒní =œ­’—<ú5tëÎÝûũ _ĒrāĀîā·ō•ē7/ïï\ŋ\uŌŽc·"e Ž"ņŅÅMŧŽžŒ‹Ú=mĘ•§Kĩ†Â·OÖÁįoßūÞ€šƒ—ŋÍWÂũƒøÂĸ.ĒŌŠscÎ8a€øŪý―7%z­Šj6 NLæ'üÖ­Ū·””ø6ōģ–JlœïļôĘĪ-ØđxŠ[ŧšÖĻf/H˜SõZÞÎÖ$ÜŪÕitBūV›Ÿ°hPmÂÚŪZõÛ―^/Öž°HmLĶ*/ņúÐfnķV6uüý$”uïE?åĶŽÝEJPn úމÏ{ös múÓAđAu}g Š:VsŦîę "Žuį‰eLę‡ŋŽquĀ ŦŨĀËÛÍĒ$ã?ŅŠ*§UšØ Ë]­pØÃÎĨz5Ķü%­ëĒ8áâ^ÃÅVˆ_ZŊoØÛReNüâ‘íėŽĨuümĪRïģ Ę>=C•jMzÔđ†N(°ų~øČ^IíŨÕZJ•ũóŌnÎv”ÄŧŽ‹‹”īųaæ–e†Ü˜Ŧ­]PēÍâeeÚïMüŠđŨk[ ËŒ8Ŋ?­mm]œŽk6i™­Ō(?ÎZ–åĨ.ičWãûĶ-<­‰sË “ŠUÆļ[âåäâîTQp+ÏæÃޙ5%Á?ĩÅā°Tũp– Å·uó9ü8ÍĪI­”ÚBEfX;óļ0!O#ϊÝŧĐŽR'G‡ÚMÖŦËvĖííQĢ}ŧķ.ÜĘĩæūG™yÏ7óķžØ;6ķŧīÕė(G\|{§(J·Í^†Úķ“·Ūáö^ŊÓÞÝ;%(G'7{RÚĶįŠžžˆNÍý` k7n0–z_‰ÔŠsĩ"<L(pvqtīĄX†ĩ4r,ĄôāÍaïō[Ų™xëØ +MŌēÐ_‹t,ėÂ1š:&ß ŋÓŦ‘§V)û.`äÝĮ·ŧđ#JyđÞÄ †Ģu’ĀđûĢâŸnôúüĩ8@ČGú° {Ŋž.·õØģŨgôrŋŧëJ&ï5uþjWŌģöˆGÏúÚēfe9\ppžĢ9ÎX―ŅæswŽÎ āŲYRuĶ’kķ•jŽW―·jd Ųd$ÁßāsíAk§ĩĀaÝÁkįŨü€˜€Ā‡æ?\6§ģ“!5ĩ ŧ,éåā /ýFlJÛúSŋ܈ÃũŠ?F`Æģ~·ÁCŧKõ‰—.„Μ5Ž“/ð’Ļ ۏ…l[Üž^wwÔwč#GoGžCú§iP1u‚·ôŒ‰į…mc&lÝfo=qýLĻ—KsžeŠĄœ&ĸr=æîík§î-ę!‹}ž‘#ã1ŒáāȘšvû4&r[ßĶúüWaá Ž‚ufxí=IŒ›3Ī™Ą$óø™g4þ· Dex eâ]ūp7ÞĄÞð§ iiï“o]3`Āė Ï^Æ^―yyûĪhiNfviĩã·. 1eéҧ'įØĢ< ―3fŒ”FžÝ~ü9įŌ~Õx^Ė­Á܍āÐûq‚ŽWKÝįþõĘæŅ6@‘y;^‡VM„ó,NÕóĐï‰sbÓâ3Sé=Gtō(UĻR“bï›đš;;"4H‚į9ĖzØōÓņ‰ÉwOŊŪgo2KÜF,œãŽâƒ‚ķĀEŠŪÞšž}|gū(+§Pƒ"Aa/ÚÞĨëĄŦNOzyjGĖŦtŋÎãßdĨß9ĩÎú_ â\­ËĀ#cYÉX@P ŅåžÕc·€Ýí PũûÖ]j.ęe‘Úˆĸå[æīlāßÔÎÎÉ­fÐĘÕßŨ­ßĶ>Æó(R)rāŧþ›'õðöi:ēĢ7mZĶÁ‘ ÝáXSþ‡ ļy'dÓāQ“ŊEð:Ŧ\K`8Š`"ÅĄN!ÅâcPžðŧÏī•c›6iÕĻPJ“^•›hŪ›7ýN€zš{HÁAB`8Af9õÜÎcėėĨíÖkŌĒ1j―Y_&/5õčØĀÃv^| ‹É6cËsz`;kÚX?k·Ú3ƍĻF00ݒė,ŠõŽŅīŽ·ęPŦó˜žÐLVi~Åjūƒ·dUU^š†N2r°_M/+X3ūŠčLRÄų‰#ũ0ôČï°_e1náx_ÏÚ}Ķ„/4™‰KƒjÖÎ^ŨŊÁĀúöčS>h8ŽēWUžcËåĨÂÆ6Ļn0+7wg Į c./ž6ö‡}·\xiĀ­pTÐ&Ģā§P@,i`R”™ĨÓoæ~žö„‹_āĻ.Īg–ņ―šŒ\4­Oûï[ĩ°·"ķÜÄVĨ X­Q§ŪŸŸmÂđ_Î^šÛūS·Ãû‹ēũoÞĒŌĐûwjČ1ŽĐĨp‰­6ý~Ðė g….ã·]76@‚ðų‘įįOCÝz)Ƅ[chåیōøiÛōa―šų~įPŊŅÄ+p„·=ZÍĢVčZ”ÚŊžįĨö5ęØáŠw‰^ΰΕ„Ē(‰”$PŽãYģĐHĶÁI †pfØBÛW>ëHō<ÍrÐŽ!Ę2,Ͱ<Ļnڑ%pŒŨčHĖʑ@?Á‰oóðUë6„œšņčņÕvÞ6 ĮņpŠRR)”đ/ßF.ĘXôĀĻ5éMˆŲXZRnÚŋL“īŠ’P$!ôĶpĖE† ­Vč .ȰGó> Wm=ú \ke/?3ÁOáXГ&ĄĪöRÏj6,Ë ķĄ °ŠÁ2‘ļQ] â(Î Õ^"‘”äôÆÏ–‹fŲŠ’ B[’x}ō­eRŸÕ;öŽęV 6bQU؝$q]y™P ūBj )`”dfā1ÖÎÆÁæ?€ĸ”—hxŒÆX*Qg?ßīn啯ÉËķ-šÐŽ2rpļ!%h'`:D•sá?–áĀņBĶ$šÔ2KĶ–ŅŪF mY?A>ŽcÜ}Ŧ{ûrĨÏÃĢäþ5›ÔiŌ’2•D†Į(éNMýŦĘA1ÂT–ēdņēŧ lŊ™[ũÍėãd-Qd„oXŧæZ8;m厅c›ZS&žĀQËXĮah Įō Į Ęn0ā$Å5Jð• J­ϐîÍMë!Ĩ‹Ö,Y°ýČđČW‰1·N„đx3Ŋm“Gūð4ōõó—ÂӁũ 6VB ŠĮó–íÏĀpøčÂĨë1ŊŸž‘ pÛú=S&šŪ”°kðĢ5))Yr­ÔÞ”ä}(Ó2uËŽVž―uý~ąĘ$œÝā#РĒq•BĢ`q›ÍÚ{E|HðæuóæŊÝy–E ô‹$9†Đy~7ōu: mVôþÔ‹`5jÕu·)‰oså:;+yI>Ü@ūÐmA!7„ôÍŽ_ûÞĩŦQYéŨ.]šõäÉåß„#-›ųŨĨ‘`\ÂËK§Ž Ÿū*­Øuėovļ? "‚ ēž·jðtkbÕdĶg MÏĖÐŌ}æ*x=ĸfÏá$ȍųõĖųϧwN]~­ Aĸæķ,KWYæcųžĩjÛāāÜĶ}'.ߎ ŧ–ĻQjËe^Ó­šYĪŌq)q/”FEhŒoâ?|šĻį>ÆK›ŲÚ~ðĩGÓÞýz .ŊųøâͰĻSŧĶ~~ÏY†æ·•Û8–ģvéRËÝ âäQģîwŪnuš8HõžžV(Š@PģüÄö%—ŸĪŲ:ķŌŲóÞ­_ŧ'Whdr^RÓÝݎyYŽŅš“c_Ļ…P‘Š7' " |}ÝqĀŋĸíxčÉÓŨmHĀîĨök@„Dϟö†Ūíbx―eÁĪúöëÛwxÐâõiÅ|Ų[·,RÜŊG)›ÏļôŸ~aÕ0g T\ƒ –‹(Žp‘)·iŽá?^kĀ_úŦĄË{ šø,‡›šrãÜŪ>FĨ Ė<īíĀE‡—OÍ ;9jPŊ>―{ŋæuŽĘÖŦɘÝåųOæŽqûU!AR84ŒA V„·<' †{œržļýØÄÎM_<š'NÞe@wkþģkk’­{oígufũâéAGĻTĘq4ËpĻEĖH@5j?ėlČææeįŽØŦOŸ!#ĮÞK.þ{ÏĒō4CWĨĖģ&ŦïũîŲŲśY8a`ŊūÓd·Úþó֍ŦŲ{4îÛĨ%n|ēpÎbŊv=jļZÓīƒ9ĄŦ·ĮąÏT€ãß6CúķĻ~ûĖŌķa5ú8‚=ëöĪ+iEāþ_ÖnÔsPHxaŨĐë& jšbÍūžÜŧßÐËqŲ=Į/ß8Ž™™EHĻ팠Ђ“°€iUú:ú\$ũ^ØãČĻ”žâr9lĘÏx—šž[\V”›ø!KĶ€ÏĨÅEGŋË)”ËåE9ĒĢãÞūËW(åđ™o~ŋųûƒGᑑWNę ˜{2Ēō „ʇåï“âāōÍ/ĢSJēĖĪø˜ļŨY…Ĩ0ĒėÐCBn~iÅgŋâĢÂ*ŠˆŠÎ>đ$ûđŽī áu\Üë„â2øÂ‚\ĢUfĨ&ĀĨĢïޝðAĄ>―e―}W2]XZš”øúU|B‰L^V”—ŲOMÏ-ĸŌšBžĸčÁý§Qo‹rē^ĀĩĻÃ_ĐĘŌGõmąûrDÜũčyfat */<0ģ ðč9<þY؃°§‘™…rØ.——e$žŽyŸ_R―Ž~ōF+D%+I‰}—X…ëũG'§ËŠüĖda9õg1ųEEŅÏ?|™UXŪR+ģá„R؃'Ï^—•Ķ&'DĮ$Âáķdš‘oÉ4&1]UĘKōē^GGŋIρ ”d―ŠM|“‡ėđ•垎‹ŽOzS*öe§%GGŋĘ.€%‘ÃĸdĨFĮūÎ,(‘ËJ>ž‰ķ—\"Säe$?Ŧy?…ĄF=}ôðiTN‘Ž([č•UPÓķž“‹s>< {pĸņ“ŽÜžũ‰ŅŊÞĶ•–‹_7óõ "—+?§ōc“§(•*O…BuŌōî†Tn GðÁĐ9ÝŦŋņo å•` RUä°§åøÏœŠ •ŠŠ„T‚zXÚáķ匒 f #h\þðÄ" oŸzÞvÄŋíŽļŒ2ŗ"V™ƒ%fUU‚Ðę§áUTéë >ÆüųūĘĻ-eúĒUQ™Đ%kÁļåĻ/ĻęĐDc’§ ėRæv<2[§Q[ŽA+Šō‚ÝęĮîŋÅįéu˜ReĀ• ZĒ­ĻÔĢ,4~V—Ę]UaXŽU·ĨĮ™V SÕ6<üK*ßK•™Z:UŲPTŲ€{>:úûĄVöēØĻŠđŠ"AĒ淊(ĩĒjËKrŸÝ9r!,Ŋī\þoþF~J땐ļþéĄÃ!!g/Įį”~[g—\%+šwërHČÏÉ9%ŸiŠŽ4îŅŐS·Þå ‚%"ō_'ĩ"r…Fg4īōĸÄė‡Ú`ŠÄhPÃ+ o‹r™\§RT}q!ŽÖLFݟ>_D”ZQjEDDDDĐÁÁW‰A8Žņ<øŠAĀ0,MÓā‚(ĩ_ĐΊTŠwïŌ0 _ """<ËrõëŨĩąąfæĸķԊ (*“)ė===8Ž_"""†egį( ;;ņŠö[A(ĩ..Î,˂Ŋ‚ĀĄÎZ.€DĐýFā+_"""ĮCÄŊ›ŋŲKDDDD”ZQjEDDDDĐųĮR+"""""J­ˆˆˆˆ(ĩ"""""ĒԊˆˆˆˆR+""""J­ˆˆˆˆˆ(ĩ"‚ Ā øēbãc;ōũŽøĸēô āëâO†dÉîOðyŽĸųŒĸ0Rĸt5þ‰.–dŦķ-?ĸz""ĒÔ~ŧ (I`zĩ<';;'·H­5„N~Un@ÞĪŨ›îg›YUö*ę•ĖČyrð,­ŨhU*•ZĢ53ÁâĪ)ĸÕÚÝ!gíčŅķ͘Á%fĮØ—ũu ė;ffB‘ģHŽ–ū ?ķ}ŨūÝë'Ė8ĨÆˆÜøßg ėØqþķcåfýÛí?šýÛÄN―‡‹Î#)sØÁÍK·^a$Ū,iÞČMoĘiƒ2?̰Ļ0åŅɐS{wŽ0óīĎ$Ņg'vúåĘâs^HD­Ī˜YũûáY―;·ëŋôΜÆH”?ŧĢo§NÆŊˆ+6XƒįhÜąŅ‚5“_‡GÆ>Y&rĘæE>ķ˜67aÝô‘: ڜ\H$ĸäčúÓÏē)’L{z|Ųņ’B#ÎŪßzpŨčnÄeĢ8­a8*Ï|1~@ŸNí†n<ĢĪ Fžš{ޒ3ĶvoŨĶßĒÐ2=ŠšË.î\šïĀÆþ=:të;+đHՋį/Ü×§FŋRÓčgWvôíÞ>0°ÛœÓņtYŌ†Ųý`0ÃĶŊΐkbž\?qá|ИĄÎ̕…/ ģhCÔđ'ŨÏéØĐĮØ9ũ“äĪ„J―~`ũĄk§ĩm°û| @…„9†uôiėcŸ•’™Qšĸ^QĒ7Ī&é·ÞtlfËęĒrßip>ïųÕ―{›:eÖÔ1 w\ļØë;čÖĐ}ûŽûŪž$a(NÜīxÓä1Ã~šŸ]ŊŨœ MˆŸÏ†ę―j6ōũFxC  6ÎÞ-ëÛ>r,Ŧýúc]lH‰NšbÞą “fģŲųûVóĖk^Į99#­éˆA}ZŨé0jTóūfƒĄōø!J­AÖtuūq;üLGI$‰3fƒ™å?cAOŽĪ:eÂH8 u™5ŅVÎ5čÝ1+|˃ĪØ |†b$ p’P~ˆyYŌĩksĮíx+œBÆÂ<­•Ģ=ƛÄUŠr€gŒĮ” 1·ZßϘ―hũŲˏO-t!ĄcPg4Ó,ĮcØ8šðr…ĘĀĒ(äĨŽ„" I^ÐK+;gLZk،đŦ7m ßÕØU8]lxރ“WI™âCĐęc2 ‚A€Ģ•° lޘRA…c-åÆ C#xÜÖІ­kóđ˖ï<|õ—đ6Œ zīĀÄŅÖĐA?GĀ ý CQ~™AƒšĄI…ķYV„Õgæ$rV,p'p 0ĀŌ;°o,9°pBÞeáô õ]Ķb&FIsp7Ģ,6;Û{I ^fģÐĪŨęĪ&ųė2<âėåĶÔæŠ8 L·ĒĶ‹k—Ɛ˘3§ĩ—ŦÁaMÍLĄ™§ĸV “0eÅJšŅõž1 œl$<hÚČóŠT‰ŦÕš+—~ûú“Äæ­6j]ïÁŅÓn^þ5VȄ‡ÖxštóœŲ·ĘÝ=īÔ˖TfEÍßå=pڔĄ=Ŧ àG!PËĀq8ÐËJ3&YĄBĄÄpŒgiĩFĮ@kßĒԊðœ™ĩî9rzOâyŊa3ũ‡=žoëōE;’ĘYÄhyŧóīNŦ7`M~ŌQ6sþʃvđúÂh0jhŦĄ+ÎOkĒ^<á‡ĮĐ%†Užî\ZBėÁvoZ6fî*Žóœé=›5éÝË·øÚš­{Žl_}]!Üģ!Įð:•‰f&FŦĮ›ĸ8Ļđí› ·ŋ‰đþ$ÁĀð•1g—‡7.žđi]ŊsÛúÅŦWo9ēsÍŲī6ÓŧC9€53îul;s3)%áņƒ'*†‰ļ°~_„mdJôÎîsg/HSškŌM,VŊî|ĸ·sįoDÐ(fyð‰į%ÎNԉ#ÁŅ)đEh­–å+T^Ŋ5Ōl“ū―2nÜ|þ&éųÓgŊ–^•0fĢV́ēeum<ŪUÝāõ+?ījóéķm'ŨīFqŌņÎŲÐõël―ø^]!"īAŦ3Ō|ÞÉĖåd~xó4ĢėōĢ—ZŽ,MK]ąfëą―[OÜ+ėÕ+ЎäŒ%™ĄŦ6?ķûÏįš ė[ËÓi5‚óŒY§cXĪų€é„ņŲäyëíß{ęî[ŠBĘJóãb"ӕå‘O/âý=Ė·Î…Þy\ˆbĻNg4K\ĮwéôøøÞ]?Ÿ\ē|›ĢĮ öĩlFFgĻ(Ŧ՚ŦåY†ōhÔŝĶx7(ŊžõšãFĘß·Ģ xč,†ą1įũŪ:}ŊmmɓSwđYfu™ïßÄDƧË2"_Ĩš ‚ëtBIËŅŋ_\šaËü۟%‘eęíþcįĮ1ė’QjE8ÆlíÝæøđ›û‡4VįË TÓĀî>ö6Ģ–oęVϕĶĐqŦ6uös <š>~yāũÕôfžĶ—ģ}ÍfS‚û:XÛ|ũ’Đm&ÕĶq§ {ŨÎwĒ8ĖŪæĪĨÁvĸä›m}š<~ØÏÚŽBÝw‡žųĄž æÞnÓō‘.V<ÍH&ŽÞÜÉŨ–ôlyðčđv^d‰Lkãâˆ}T3ÞLƒķCĶ-hËhĐįރĮzŨ·/•ģA;obķL}@ĄĄj8xtZWŸŌ‚bTę$áŒļ[›―G·úVs>wû–QíMSVƒŒėÉÓlĸyŦŒčhPĻđŠ?9žÓOÛv jäĒÖ(Į: 6­õ’0á Đ­ÓAQO—OŨ%ïĢZÕÓûõ:ö#ÆBjá`"þlJ~'ļ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jG ZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZę4TT?0ĪUg’Z"ĒmÛĶir.i€ÉdŌķŧŲėâRK)eđ\l·oŧÝ{ įēZݖ'ŸZšŪ›ÏŊ6›įˆ4(@UĨūÏ]ŨĨi €Ī@jĪАZęp:­Št|)Îō @DúĖŅ—%Ņ{wUÕpüyÎđģt:m§-FĶ@ K Ð* …#‘€+*HPYJ VŒ4 ‰,n!D bøA‚!(!Ņ ū…7ZQ EÚīÐm˜éÜđË9ÄÄ!h‚Н<ŋœũËÍ=Ÿþy’›œ#x$JDN-c,ģhMŅ$5FÝķ.åhčF~úä―sdЇ)œÚĖÁwķ3Iŋī˜ĩįĢí "@č&d …óŒō~Ą’^Þ {~jc ’ .t:ĩ Rw ĘfûĄŧuÄg[S› ø ģōŅ#‘zzjc LG7v:Ķū·ŽhėĀÏ€ë@!0R詛ÝýĻ$‚“õ­-1É3žõüÔ2ƘŦĀtHH#?ÛʒBĀuÕ‹›–ÖdxųĄ üōsģĩ'R”qŋÅcŒ”Öˆi :ą=o,üāÛÄĀÂ@[KSÉčæ/šWZ”GD7qŠÕš SgŒ1ÐZ+W @‚ŋ@!ĩyæĸ?õ1ŋjÖļ–Ó_/X\}ðhâØ—ÕųīëjR ÐJ !īŌ„B")R‚VJ“”ÖDiƒ.\WiŌ˜ZÆC ÐĪ]íĒ›>ĩāšÁHßaĨÃĘSkE§-ÞvėėóSýg7ūēaßÉsÞ[K—/Ŋ™V9ą}íūFŊÝ\{čČé™ËÞZYYaû~ÃÆŠÃĩfųäg_ŠzžĀGD6ĩJk"ČØÔ2Æé”ôS-čŌŽmY@Úī‡MxhLáŪSŋžĐßķpĸʼnūĐnįŠĨOŪ\ŧãró‰·ßųyÞÚ-ëm­zí“9ģFn_ščȀĘ=ŧĮ͟VđsLŲsS†k­Ō&Ÿī&"ĘČÔ2ÆđŪkYĪEúĐV9–ŌĪ”cۖvīsđ­Ļ—č<ОõDõĢ‘‚‚âĮææ|úĖĐßĒyI]1gõšĘû.üÆį;―Øt<ÖŲ|lûܧwŨ5tŒíēlËRJĨj-‹”R”ĐS-cŒ‘ūÁTKZ‘! ­Zl­iŌýÆ N6ž>w'ĩž5cŌo€r Ũ/“ ;i™D’@l{Ä=ģŠįÜíũx‚ĄüëÞgƒ 5ĨĀ?$ C0Ƙė8īũýõ5ëV­Ļ|ó°―Ļf͐pøĐ)ũýpãŦ›7­\ģePŲė1ÅA;™č2m@Ōņx2/xpz㞏vėÝŋũģm'ę/! ļĐ `ŒąžŊ€4H9/ŲPRŨhŲnĸ’Ų ^œ4ø–,ÓT–ŪyoôĮë.õžņōÔĐwæđɑ3W/“rÍÂē›_‘*]ōÂŧĢūúŪ ~‘‚ "`šïĸą33ĩŒ1FWK'ŌAÏÐQËÆI "ĮēŨ•š<å“đ}2 hÛē]W‡GÜU ŪåļÞCĶ Ëī‡Ž>|<Ēk%ĨÓö\ā•=dZjcːāũ đn4áÄ-ŋWĪC`™†‚dþĖ6Ŋ=ÅŨÞí‚A„ļ qSĒö{PbFĪ–1ƈ ⑐§Á?žo™Ą@–š EãVCs{8ĮÛ'Gˆ8ĩ=cŒ|"!Ģ|@č‡účĸęÛt‰:œkÜÖ?T”#Ĩ@âЖ1–„Ā<ŋ( gýÞÖ.Ũq šß#ÂyFŸ\Ïß?Ž6ÛÚÚā?1ƈĀÕĪ4hęî o~oïŽmˆAŠZĖė]ž=C\Į5^ ‰nƒHq•ũ+ü‚†xŪīŸR xãxĀëŲŊkp˜ÔH-€Ô ĩR ðâšŪˆņd63ũÞj pĒģ{ïĖsÎĩVUuũý#twU­ĩæœoˆŊcDšŽIENDŪB`‚doc/images/ifw-introduction-page.png000066400000000000000000000437611325366651500200430ustar00rootroot00000000000000‰PNG  IHDRÎGæąmGļIDATxė’ÍŪÚ@ …QĨ,‘xxĸ-â"]ĩĒ *`Hf2“Û-EÖ5qïQ°U‹ ĨRÏšĸäƒÁl6Ëó<ÆøýRŧÝNū˜Ō…ú §Ö~ŋŨ)[äGžÞiŽjwøŦ|Īó?Ÿ~ԃðb“7Ēþ'ßČGģúÝPéŋ'[įƒó“ó‰1æy>N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/fŦ>6ŽĢŠÏĖîí}Úw>;vė‹]'ņWįĢNQ Ĩ* īI!*ĄŌ’‚*·ąĒ(óA\*"þ*BŠ„Zņ! ĪP"’šŠR+Ō€‚Bl“ΉėæÜģßųūvgfw–9Ŋō4ÝX%*ņģ4šũæÍ›ßüÞóĐúņVMúU­T DļÕLĸķū|#l„ú‚˜ĸ'üHÜ>?ęįÜņ DĻTž# sf˜Ĩ€Y6Žr˜ÓÆH0Î-NĐôBĀ„P<Ũu]ŧ 9ÅbrīmûfČ įXg Mĩzk›hŅ-„jęâF1NĨƒÜÂį 'jVŪëĘŅģʑ1æ…õ’‘ð{Į›ĮĪËĒ+ݟ˜[iyóø+éĒ‹0!ČļC4~éí“Ŋþ•õ ėÛ·ĸĀúU-z8đņņ­ûķ­IūwîėŪ_ýāęEí=îčÝÆT˜EaÚĸrüä;ÖSŋĩũévîčŸGļãޘĖĨÖlÜÝύŒ\›`ÜãT’čb‚„#ĸÄDz6õ‘MÏîíŊʎ\›˜žļza*īþËÛwôn‰ãĀݟŲÐÖŌÖŧëĐŧZ“mwú™Ý{úŸØ4sõo“.™*‘t—)ÐŲ+ŋúí‰ú{·ėęĸbîâ[gĮg áåĖdûš-Ï>ņĐÚ ÃäPsņýoþxėg?øöó/üüJVä.ŋqôĩᇟÜūõ‘îó§~ŸÖęÖķ·uÞóp_ßWššU2%!6·^pŠ~ĘÅēUÝņôÎÁ};zýØðDYÃ.HWՏ7ÓžúQ…Ą*d㍊›ŠX”•§pˆsģzŠš4 0AÍJHłdĒVČ&rýoĶÛįGšÝ>?„R û―åP*ÅŠâZcn]Š;ŧðŌÜÔlŨÔR‹Īå™jŌápXŠ2‰ČöZ]]]UUF€\FØ„ëÖÄęv &RÝĐOÅR Ģ ÉHu5Ṋēũe)“s:ŊËĻBQĀËGšÁCJĀš_·š78Č8°ÝĮoŪr y!2|zÁ}z~ní˜āOþüĻ)qÛąŠųhÛGïYÞđėūûãĒd™åsïėiuĖž­ŊYĻåË&nP#2Ē:ŅtÆ(ģx]gÏēa&gķÃįēĘ\;ĸōũŋũÂK‡gLĪcGžĀtcIGŧÎhSgŧ,MÎRŪÛš: ›5vīĢ89k!L\Qq–ō@›É•E+ęÃļX,Ę Kđą?yųŧÏĸdhZ3*šĄĻA„°L“Ų&ržIëÚZŦƒÔDŅ‹ëËīh[<ī°m͊%ØqŒH%—›ũå•V‹°—1il銞;[ãÁĻn›Vý’5›zĒGüC/ū:U°„ƒt fQZ9ýÚúé‰3H Øs\™5ķįĒ!$hĐ`ðÕÓŊüčĨã$ڐ0c…PÊĩËȎYĄÜ{PþäæūŧŸ4&ÎþæŨĮŪū›fþOŋxņ—§FŦ“‹0c’5Í@Ôīå“F•øØĀ’𛄗,_'QĒôŠ\1ššÄq1Æn…WLlAâņ™ū˜Î2VÎÎäFÎŧPŠ~mŨķĩĐ$"•I4ÄÄ,•É{}Αp$;6öîtĐ<û^úZ&Č`ŧēYSÎ’é Üær‘ķʁHĮvĐTī8GĻŌē Ô^ũØÖoėz4?|æėč d;w(ãfvôð‡>öøöÞÍD‰†]ls!“–ÔÉĪå·kÄŠ‘Þ}ßöxnð™eu†-ķqĐP–üĀkĖL\ÏĖÄR+š·ÅĖÎ4ĩķ„Đõ_ęÝ?øõÁ=.0\.ßb*[b™ØL0Îō™Ëį.ŒĢŠT …ôXÖĪÓãĸĆVÕ·…ÐŒ †Ðøû$įӏ„ŠūS ūTĻu„‚ÏßW)jbÐFĀyޟĒūŦĐjø”?/?^æóōãY?<~ 7ôÆŋHģšŸĶĄ8úŨéŠ| _ý\Ûu-[`Ķ g‰ ƒCˆ'Ë Žôcë+Æ€„øļøGxĨæjÚü=ܜÜ{ÎïޞœûŌŦë†a4îŠŲl‚Ņ4ŒęŌĒ:=yóņÃÏZõ‡īkåē_ē3 Yn˜&ā”˜ĶYĐTEobívûööŽ‚ŋ·™hšZߖRՖčÞėČßóų“‰7ÅL]•AŸ?M#Cl(ėųð+Bøþō·}|•/ôð' áߓėOÜĖ€―|Ó †þøĖ{üđ<AĢŲOįŽŲ0LŦQŊĶ1” itÉĖ–§–‹ĢƒŅa‚a4‚??7œ“ũóäÔkY3aËķ6_>íŽ"ņąT‹āÉĐR]ÉŌČÃĮý,ïĻ-G™&F<ęįøÄ@$V(ОgįÓdJ<ðŪTÂ–ũÏНî-#ā\ŽŨĪŲäØúÎୋÏ&FQGŠŸŧlšōÞzŠfwj–Ģq#ÃK[eÓķü‚%z,ģwaĀ$gvÏ,ũŽģž]ŪHzKð,FL+ķĨ_Ėēƒ·ĩ_Ę%ņ4ÆĨƉÁNaiãīzĀÔąŅ*ŋH~Õųv­H›L_ɰ CoT Uz…āđšō7ĀŨÕY%(žĢĢ―čJņôúÚųž6ŨŨ3Ä&XŠŽS+į‡šŧ!ûUsÅĖ“ČĘðņX” ŪgŊĶG::ú ‰v-lHŪk…“ÎOĀü°úßüäî <ϰ$|_ũëģÖóEDóŸæō<åfŒŠ?PDĖåCDĒ‚‚c„xČ&þ=ÝÓÝÓģxRüs|=ĩëd]âÍBšÚŠŨŊÞž­%‡IX ų\ûûš8ðOų؉? Þ2c(Ĩ€F"Ĩ„Ē‚}8ōøôÎíģGõ›ŨŲÜLkMHÎSJø8ŸÏgģŲųų9Pŧƒ`œÄ>]ž=øðāĸwŧϏîÏ^đ`|ëIÆ*’súˆ.·p’ LD‘…QŽâO^#ļēŠgT቗ý™čA0fUÆä)č+œ<ÝĐã.óós!ŸøÃį&ĪÉ9ųЎ<Ķ qQ’B[?, ØQé‚Zį2”’ą>t}I4ƒD›{īĪrQėü·―xęīt]J í—Z9:kĩ4>•ī2ІĘÆ.åÎŲ6`ˆ· Bŧ„Z‰ÁJãjŅXČ ĐÏÁIũ],ĨÅîķaJ€ĪÞ(*CEŲT†Îō—o})hÕk}ąüÓ`ÐōÕLûūŦ \Ɉ "*”óąĪˆA>æŪëīŌ!—>Ņ4UMk}ŒĐï-ŲCþh;Ŋe3Ÿvá"^RĀ ĶmžŅ>õ1B―>äÜZe|·ø:Äĩ‘ûŨķwžžtÎĻķ ČŨïŊîÚý!<žɆýá%a0žÛLˆāëŒįÅ͜_“žĀŒgĐLēáũ%„ā"Ëc ÓâžĒ?<šĐ&þPü•?|>^ҟ-<[ƒĨ @>p})xũŊá `…Ž]žŽ„Dšœ3þ— Ŋ}x@M$h!á[xđ­f€ü9ųK]bcZDœ'?Ë&āqÔeĖÉ€!fóIÁ øäë\YëÅË$,ƒoáœpōʼn?X8âëˆe“ĒŠę(Í~ó·96R0ûĶũ'ë2\―}Ÿõ„íßB”ęhõŧ$›ã?Â‡Ū•|.øæ)9ü<Ïx~™"GŒIáPœ8Å*zSŠîÂ\Ï.‡ļš2 Ô3 KŧGįÔ@KAZôĩŽ­ĖÆ\)z|ðXŠŌãe™5ŅÛãcÐōy•X_'WíWēņ–ô|8ō;ņáûŨų™Ãíáîį#ŋQïnnfŨ­äãę“|>ĻGD°„Oæ[>í ŋĩĘ­|úf勚:А<ā―I8ÔGiŠū?dîï~|#u6MĨåÜVÚx’ūũþToâãąĮgÆäs8ņũ…Øãƒ-óÉ==þ9ÏÅ4MÛív·Ýųáˆ6ÃîÉÆÝČŋŲlķÃvĮiœĻĮ[ēŸöĪP(#°%YG(Šļˆi͘ĀĮ-\cbŒx*mÉdD‹) ‘‘ޙ·r—wPŊ-sŊžx―žßëņ1‹ÉĮ–ĘGE>4RæMNð)wBåƒ>Ķøā}•GbnÅq  Âg?M4^_™v…ϰCBYÇ8|ĻũĄ3ö>›WøČĮbEų`ur}RŒÉ'ôÖĮa,Fë Ÿ}ûþdiËĖëiÚ|–Ïäû&“Ÿ ŽÄTôáƒū’A8Ë#ĀÎã̧w=Ä\ƏyBŧōA‘Ę2ã–Įóø Ę'1ü >ø‹ÏöéŲŪŪŪðŋu2ûFÖēO–9ÄĩĖĢū=ķõUÄ3ŋ­äŠË·ĘŠīĮ.//{KkpšÖįSéU>ЁĮũæƒ;‡O;ĘlËGåų`/ø`Æëų$ÄåĨQz|ēå?ãC|r)ņ2u‡ŋ/Ÿ_ė\ŨúũÏī]Ī7ąaŊą$VDԘX[5{-jbbAbï ;ØÐhląW@Á‚Ĉ(R”"Hß6Û§ŋgąÜĸ}ï›ÜûÞûáú?ßÏGœyæœį<į·á—‡Yցü'õÓĸVėĸ äĪԁ@ü›AV‹@ ČjY-@ Õ"ēZ@V‹@ ĸ: Š8†ÛÁ0€øk”BĐRQ„]9'ĮþĩŨÁ‘âŸĘ@hUžŅ–—eŋČĘÉ+Pkī:ÝĸuĪցæßVŦcÃčߎĶÓ놁§U ­Q'\>ūnýõĪF[š™þ4õizQĐæ/ÕĐuā8Pŋz™™ōðņóž"xĶA PWûÁ@` ûæĄąýÛwėÜ­K‡–Ģn>7*(ü=$Q`mŽ ÉāßFŦˆ'įötjĸŅ#ËE‰cJžrtSŨv]Ēģ{ŨXe hėßŽ.\žčtjĐĨŸ4ēÛĢbsÍNPĀ?‰ c9jŠĀåĪË;ƒzíM ˆĪŦEhõĨą]\h…‹Wũ ģæLØmäŲ;/ =ėnMV–įyŽĩ1:­Z˘4đÛV|?~Ęė„Wf‹AÍV‹ÉĻÕ@t&ģŲb6éīðąĀ°Éh0™+Ķëĸb/ŠcĖ%1Ýüh ô·įĶģ=―9 ĩ‹ĸį 2Jė+č™ŨOlą?b*L\ÖŽÓjacÎLVû%†…ALƒڙAŊý›vÞlĩq<„5 ÚŨŦá\ƒ1Ylð‚Õl|‡ų8ÅÆ:âpžÖÂjöŒëG0-"5ŋÚ7wę 1߯Ķ™ûÚĢ™sd‡zčāhŧ2VÂq&ãČi*M‹^<{ėŽ%‘Žĩ0š[w2ø—˜$­Þ>@oxģSsÅNaļQĻŪž1°vy­ ŒWqĻŦEwh­šüd3[ĢEį}ë6oÝqðâ™-šˆ2.[u'7Í ™ōݒô2VĨs’ĢŨŪÛtðؑ}{þ‘§åÍę+Q[ķļiq‚WŸ9| rĸ™"–ËÓönÞyęüÍÛįũ>üÛ+^šx‚ĀÁŸFl*ĸŽKįM$me‡wo\džēk΅"ĸMaüœœ" Ro„Îk[ļnïs GÂä›pzïúČĢ:V‰Ûß:·uãūL@ēå§í?u>9ņĘwSGĮeˆĘÛ Ž•eĸąomØäa!!#æþrýĄĢH\~õäæ–ĩ;o'§_2|øˆõ‡ŪXp'ˆâĮ1[vH}žqtÉw0ūųā]VvÜTÅė1{Ëü;öíŲĄf5B”1’ŊGMûõČÃWî<]b EĐŧŨŪøzøðác'š›!âÎ3§vl\u0rã–}į t6_ßú=;ũōqs–$‰Īð—Ŋ/š3îtÉĶ9:îT4ØūþDtbΓÛs‡ŸôýúÔ"+Žã ę‚@]-BËg\­å­ýIČėËũRËõFØ1ĐóR~äæŽroÚÄYĨōë8%íUnäō1 ÃHĘÛŊ型 Ób‡D›™OŠsáƒŪ­ý=ęķšœÅ=8äéĒRĐÜjVũĨ Đē*―č―ۗZÞúö›ïĄÓ•ĪNíâ€Į ‘_ŧӊÞãv•ÛÛV&ņÂÚÖ~. 7/·€ĶJ‚nøIϓũ Lšœš(qŊާ~ϗL/WÏý’Āé―·s,…I[ÕĄi'/o77Ũí72*[-cÖŅŋvÝúÍ{ķŪéŪRy6ŸģũŪĀ2įöĖ&0ŌÅͧ†Ŋ§ãv†Gĸ• ëČi*Zá^ÝÏŨÍÕîÖ ÛϛyfÏøþJĶGÜãÍY3>nDŨhz<ЀcŠ#–ŒðpĒqRéææâë7":!ûúáųþ~ÍšvíęŽ"œ<í︐)LÝ―ŽBĄôë89îÉËsŧg1wïEĢÍryũÔæÕ)7ĸ:JB ˆ{+―\“äL99ŧųųŨr&í&_ԖÂōŠÜŲ"PW‹•ïĮ‘ëæ5ũQ>8>°wðũ›~-ģI/\ÜvävíKŌoߨ4wHQbędÓŨfķmâãä[?ōڍ1=É"‹áĒČËļĢ=v< ›•eû1ËY 'ßÛÎ&^<øiueþĐIđš·ïŠc8g(€ĸčņJNœļ˜SlÆÞkÍ$ÂĐîØïCkđęÎýz 4ę9ĸĮ!NĒĀ–gÛ‘ZD-Ú{Đäņ­_ĩ}ņ >bûŊRARœÄIĀQ€$Qb%Y†ĮœČq2ÖuČīč›·ú5ņŽžŅ Ë—Ū…ÏfŠ9zuŨÂ~Vm拄?Žļ`@”ïz―]―âûnĒîcŧ_šqIóŽäŅ|ÐþøĪÓ;ūsšˆČčb37û”ĀKžHw{ؚãœK›Čsŋŋ|™w7nE‹úÕZvŸž{æä/+§YuÏ3ōóeK~šUĮ h5.áʖNuŠqĒ Ęnj|ÉÓ]‘ŋ=+sZūĸZéÓ;+û7Ëž{ôTžĪi^īUÏoWßļ|°ņō\b‰U„ę"8ĻŠ D™čōÓđģ§Âū ‘-ÚĻģÂĖhÕ î6xؚ#ŅpXŌ 5ESA`€ķ&Ėa°ŽaŽA€ ;vÜŋK‹NAÝ äbÅЁJ0œW§m^°äįev–/ ›ûãš[ÏJq’ïĀ‹āĢĀ/&}Տ™Úĩķ’“dģAW’—ˆĶÝÚpēsð7_â@RŽŽcÅĀ2H ũ­<7ĸ3\ÚĶq€‡!ËĀŽÓūš?ę7ûũ›·í"Ž―U2ðÛo:7oÔkĖāÚ~N,ũĘÂJÚg┉ýÚ4ėÔ­GKoĀ?Ë,·žįqX%mIĄCö l$āãëåėĪx‘7cŌĻþýŪØ} 'āL™$hŧxļ’ 0ŲžĘĄ'Ž™ŪL čæÝÚÖcqŊžcŋ„’j2M6\$Ÿn#æŽiÕĒUíú’Pjd€@ Ŧ­ÚȒŒŨïÐsņúÝûWO€ÍČĘą9ZÕm>›ŧhŲۈ#ð)NË6įyQ’e Ãī’"pĮģ— (ĨJeÐhĖFë›ūUķÛĐL@9“FN`oïÅ:Õ:]ų”’č;qĮwŪ-ōÂûuÉ$­RyŨ„‡ÍúȞ䊒`B#U*š3ëā &Ņ8$X +”JÞl(),•n‹Á#L–DVÅJŸA}æ CÏÞy>lÞŠE>…Đ1‚ÄqŽr„L)0ĢåX)ûÖdŧZB8+€ĘŧmĸOpœ„_Z#ËáīRE+h^—đjÜė˜lyô‚uĄS‡Vƒc(Ļ"&ËĒ}ičĩ*%ņvuŧJĒ„Ã%aYNp§&­=-Fá8ļ*R”·Æ@åV Š&Á–ĨF]MŊß Ąu"'_ cJ'šVýæĩÜ@fęģÞýýܕYĐðeeđeŒs {wý~+ũQŋž,ęÞĮßυbYËâ~9ÜɧßÓ'RĘAĀčķ~NĸĶnōëå\k4j^ Ü;z|KíZĢūhV\*4öĩöGÕkšÉĐ1Ï d§ä– āq\DÎRþôŌĨÛ―;6­øÉšĻ{æ-$Ý8tāWą‡|8 tëfÝ(jņū\’ D•DŦ3ä'vTūÞÕā_ŸŽŽIÎ02ęļ“ë:y+aΚ›3ü:~GŽÕÂ\Ú8Ŋ& “·œb ę_– §„č6t\=wWĸFŨē …đ:“p^ Uux•ōiīæė}­^ĸŨ‹ÓÛôy+įôƒ ėŒ}Zņ+Mz#“ũ0~ƀO†ŋøþqkJūÚlÐß>ēĻž@šöŅïģáADBŽĨ(đU€—G―îq9:ƒî―äMnč„``Įûó/BÚ(āįÞ+ÉĖŊÖvv5vöQžÉhšŧ‰95ĻßÄŋŽŒ· úúFJ6+0‘cûÃĐS"xKÎũ­ëb'“ōY óðjxP}/8ŌŅu>;u'åį‘=á)ĨĀ?2ļA \nßÝ<ĶðņĒ1=€€ŨŸÞ= îl၃Ւ{zrßķö)$üR{|čŪėR―úyĖâĸÅėGŊL†žĪ>đbpRūʃ@ Ŧ­ēhÕĨŋ'Ä_ŧr>îę[iyÅöeéôFĸâqRŽýĄđąwîÝÏ-(†ÆŽ-/|t/þztėÓŽ<ž)/Ėđ o.ÄÝÏ+.}úä҃‡Š5†žÄC4ZöŸð>āønŌÝ?û)/Ķ<;+ >”>ŊĻôMÆĀ”ægݎŧqõZtü―”Ēr―ý— īZmYaĘý[1Ņņ™/‹_æeÁYų%åZuIĘ̇Ĩ—Ļĸķ­ÞöÍž ëÏ/JŧsóÚõ[ųŊŠÎFĖLXq0ņέkŅqĐÏ ŒŒ^ĮïFÎ*ĸЃq)IwĒco<Íķ ĨÓi 2Ō’áĒ:]yöãGIɏ JĘuZc0äŦĩG+*ÓkŨ*kc5Āëš7ģÞßÕĸȁN ŅÛ1ī§ķOLßqZgēVlĐâģ·wĀuņņ$ž·VŽō6IĨ>:{ēũ>L\\AëÉžVéõ•ŨĒūs %}ŦvŔŠ9owŠ…ËÁo^;8\ƒ@ Ŧý߅―U|ągÏéKŨKī­æŋ čˆéâũėŲsûqÜË;-°>'%fOdÔ―'yČÚČjUÁl–e­fģVĢÖüĒgL°~#Ģĸ[ķĮmFF§VkdĩŽ@ Čjâŋ Š$E‘$!ˁ@T)0 ‚Čóü‡ði1äģ Ãdd<' ˆŠ‚,ŠRóæMŦ ‚€ŽöŋĮ5ŧŧ›ŋ-I’Ļ‘—ũR§Óđš:]-Ã0hĩÞÞ^Ē(Q5 (úlEôX-BŪ ˆŠ$ɐи("@V‹@ ČjŽ@ Õ"ēZ@ ŦE dĩ@V‹@ ČjY-Ē*€aXåŅ{‡g$Á@•áO—óþv0;ĸi‘qžbŅbéŪZ8 ·óĸwŦäŠøfkĸJbĮ‹‰@Vû"ŠĒÅl•ß&"gcY8ŽYŦUdðōÓĪf–áþïq|œø™Ģ0x ‹ė“ÄÄŽržþãžēĀ™L8ģ∞Ųl•ĀYČKMĘ-2ā°Y-PX ü?€ŧŽÜ—dģXxQ‚$x+“ĸōeQ™Ö&Č_m‘·Ym\Å!œÕÂqb…ĀŽÅĘ žÕf“äōĸC†pl–€S5Šö&d8@dĩ’$9ûÕkÕŌåYæmi^nzūü"­ĀhIŧŸŅŪMË:žÔÃóúõ î3pæÝįz{ŧ!ˎïL=kô  āî_OÜo ŊÍéÜ―ÛœU{Ë9œĶ,ŋ†…m _;óŦĀOÆŊū}ïʂņ―:ī›z=“Ą("'ųü„AA}†ūŸ‹DÅũžd,:séÐŲmū ŸcåÏV~;f8cŲãWėk&–ü‡ÛΞųeÁŒY3––ØH'L(L>9ėËî―úNŋũÂĪΉ‚Äã“C‚‚š†œNČ­žŅ!ó’rĖÜđĩ3în:}ëŨ°Ýø€å}[ypÖōýëæv œqųA4†Žļ öÝ"h…!3fÞčå…ýōî‘%["gOųjãE^vļëōîÜ/8Ļːe‡ī<qę“l _9ĻO·žýĶ=)2’ĒîĆ·†Ŋ\)6$ĨÃdX‹5ïyŠÖÂC—.Iū4r`ĻáÄ9'uęīÕs ›ē8SĮ•ĶEŒ8žqučÄYĮXJ|™œX įIœ‹;ąĐS· nFí<ųPīþéåÐ͇·­ęÞđÄåûī"† D›„—(@;ĩĸâŦķMęÂõ3nðy_†ÄĪ— ͜CŦ–î=°eČЍ%ÝÎÛc+húŲ™ÕŽß$iųĘæukWޘÔåãÏĮüzįUÅË$ ĒG@ë·ÜīœėēW™šR‹5ýq‘Oļ˜ÔVÉŦģrsD―rp}ŸþĢ&O†ēZm“„ÃkįuîÞcÂĨäš"Óbö‡nÝ7mėÐ1{âxKéúÉ#ƒƒšO^s8[ Õ~ m­ÂŧcÓĶYÏģ>ÍäüûôjgJ{ōäÁ+}óm‹ÏZxięö_ÆtâW.^ÃāN„Ģ?˜ĮÓúN,rëqðĀsƒ•Ú'ã'‡Ņ―>īCýûëŪ‰žyë|Ôđ‡}æŊjþôČŨ3· ëMýąüÄɘ>yöڏïØ=·ÓÆEŦ3ĩŽ―‘eÜŲŦ]ģ֝z|6}éÂ6ĩl+ŋ{·īÃū#ú(LđŠP 8LiŸzŸ6ĐóŲĻ)ÓfóP›öųÅSw‡Ė\ø‘œū5ÞÂ<ž9aûČíá ;oþé§4#IáĀŽÄ;Õí6įūë―Ģ ÞÎoÉveãž]ņÅÛD…Nð\>mĖS=āJ“~ŋũ ĮpŦņՅôh“„YŠo_ūÍ·õ7Ŧū "1@d‘Į=Ķ…n;ķs)qaÏí\#MŠÉŨöFüÁN]ßÃëņÏv—ēāŅõ―‘ð0ė™ōóúÝåž‚°ũę–Ä[ž_˜8ĐïTŸ.cũØĸÝÔO$›ÔeôēÓĮ#‰ĖÓĮŪ%ļÕkßĨM‹Ï>4V -™nĮ\/ķIOOŽ_°å·yöėXŅóDØĻ)ÜRtj}čé°bÅÔŽßÖß~Á8fß­H1}lÛōģ]š<|-ÉCÆÍ) Žôĸ>|ų—MW/ÚúœõíÖw@`›āĨaCj(ŽwîÞĘUp ÓdĨÜĘ(Ā0ž(%þØÉË]–oœÚ„]:ę§Ģ€Ûõ—€“_ŊŊ”ôŒäԜ6_ŽéTŊ(ýiZtđ{û–-…ōįWï'•ÚwïŧvíŌQ―ę]8w†§dˆM?îôĖБ­l^q•'Hcރí+wÕï4iuČG§w­ļ` ß··_+/Y– Ædĩ$2/S-:ķ+IJūĸÐ9ø‡Ü=}0ƒŅīýĻYaQfšþåþÅsũ_ļgĩÚlœ„aĀpsQZš9Ąs[ÔoŨĶĐúŅM­Sŧ™#zŨmØnņ˜OóŪÆŦÁ*Ķ­Ø:8ø“:žžf„ÎÔģE+J­Év§,·ð·-a‹·Å—>3ŠÍžÝd Õ5ÔĻŅĀŋuËĶ>bi,ë={ÝØÆu}=}šMˆÍ*HƒVK8yÕkîRģAӍëÓ@ā\_ķaäį=[6ŊoaK4EŲŋJ’#~Û~Ö`īčLü›{Í" z ŨĢA―ĄģĮ7ņVbRiüÕgūýŋëÐļ^ÏáS?ņīÄ=.ĄhŠÄïŌöõ€(YÛ šģxþ &ūŪ?ŲʀtĨđčckæn>ō\4Œ‰‚äÕbé?  6rTQViĄÖ|`äĮAA!_*Ė*-flXe%4Ž;aļ&?õyãĶ3gŒnÖ  e‹FîžEįĒ.Y‘§ã8NRVŦî_ÝĢn@Ãfę@Æėũ ŽIWï›ZŽÐĄeŧÏĮęTótL†<ڏÜĩ3ŽGïūž―X–ŽEDžóëðõ‰ģ—ŨŠŋtæĻoũÝ,ČĖĖÉ}q|ûÏk]JÍ|i`‰€Fuüjúĩl  1ŒtŪļŋ’É ’4ė‡ ÃzN\<ÝŧfōÃĘkÖgÖļ6Tđ―–Úu[5Ū ĩq†™pÂŊIÛzĘÂ={ũ _s`ÁĻ/ Āh3~šz)đŒ Žð2ũIČũ göŊE1Éũ}õÍď7<āˆŊü%ēÚ I|·wÕß?r&.ĻOŧ–]ÛÅGÎ*­ÖķĄŦdc―žëOøî§Í{ŽÚĩÔģŠŽg"‘*'`ē—NI@VšûÃXyķLP*( 2 d•ā1€LzË#ŠđxâtÝaSf…Ū\}3ns_Ĩ$ˁ(ņ†•íÖKPlņŦr ŽcVc‘Ā) ˆÃ”YŽŨy˜ Č@‰“n*ČžĀó8ĶĀeŲMéųÅøï—­ ĸíĖÞķ^2'ÉoÚwZåėíįWÝŲؗP*Ė…·€ËV}đZrvUĘܟó–•J’d_ ˆĪŌ‚ø:&óå;ū—šÏ›>ū….P$Ā0%€ã―x‹ÉĪĒ %…È\1š`„Äí.] ðãÏŦ~Ûš1ËÛy"ýĘæof,/ē9ÞDE Ŧý€8^9|忈]ŧ”MũhÐē2ōßFbnZšėUŧŪŊ@­eÕ°Z ĸŧÁšEûeI|㊠#í+#ĸuČâĸaߎqˆ0ŒÎ’tD"Ī&=-E3ląĄtIïy<>e–ÔŪ^kmŋ8ÞŠĘ4ÅõÚ[kņ#3ŋLjĪ@jZĐøķðĶ)ĶXĨŠ(' Āøō“―ŧ‘Š:ãþ<įÜyŲŲŲ™}ŸEYy-Dk›"PēM+5"ĒÖð&„âb-ĨB]^X6š`Ã5MˆˆĩUޔŠEKZĄÚŌ"bJŲeWva—Ũ{ï9ÏÓeýÔt(!ĄÍLr~™'ũdnæÓž\2WUĮZö<·ų[ģV<ŋuņ‹6ydfhýčØąŲŠˆôđS'Î VĐŋŋõÃUŊ­uwáGu Ú8öWKdĶφĀDĖ Ā&j ÃČvÄėļnÚ&ȅr•CɔcێTķfôøeóɏ›Ýū‹§N,**^øðũÖþîóØCÝZÏXšæáq_;pĻø}īí)đ5Pä­č-Gû4šŪc;Ņ1ģ6­]X gFCÉdÂņƒ~ŌŽãš„ęŋŲsåBËóKæa_čw1n‡óHs†ŠÖķYkæ†a0S„ PIŅŊ2úaÓĄC?˜zĸ° I*ŧŽóKžņŪÖ/ŪXƒKÚZÎĮý^°Ë ŅI:ŽíjËb"íšv{WBĢš˜Đ‡ƒVŠTZŲIbfMÚMĪŽwÄĐOĮŸ‹EïãHE•'Ô9ÅęáEa!―Eåjȁˆ{Ā `†‘]Ø!Ï]ũΞ?üŌ‚Ų ÖnnlÜüĖŌÚUŸĘŊLŪ 7mZ―Ĩ~õŠĮFΙQ@7™TŪDm§RŽ"…ŅĄƒþōōϛvūŅ­§“$“1GóՕ›Ljb_quU0ŅļþéUOŪûĪ―•§úîĢG9/5žļwßŊî:ØĨQĀuä@Uk†― #íZ‘uŧkÞÝĒųúĘÆÜ{ÏÐ>ĢWnļï@s{|^ÝæšŅ#ė”;}ŲZŽĒRĐáӗ/ÆJí$Õ,ynÛ°nķĘkŸĐ/)áāCÏ.G?j’å‹6ԗVxÜ@õÖ-ï}ōYÕĖvi‘“_ØÔīýāۇšnÕmýó%fœFÖŧcĒÖ0Œœ‰Z‰ĀŨØrE~ņ„3' d§mÆĒĐÎd`í8ŽV8ôîņ ]ĮUŅacú‚ē]þȔiģ˜Čvœ’‰“īĒā I•ķ]Āāßîđ’V}GN˜ĮDԚQēēíz ŦĶĖĈ”NŲ ņ°öūn^n†ņįŧôáOŧÛŧSc‡”*õ ^oÂUüïWþŪsDˆ;pälėTKŨØ!ewÞâ x‘Ėƒđ†ad†OTE<;:~ébwČįģ˜!W ð•ļ}ŪírīĀ[Q -Ė́`F–a€€”įu'çÛâÎv ä‰ Y·ũ+,+R ›^­aYČXĢú…Ë#yI­5äŋGDÃVEČÓ;ŽÖôj ÃČbÄ 4qNþá@‰7VŽĸŦ―;6Õˆ0hŪfõĒŽ”Ŧgrr ø;0N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/fŦ>6ŽĢŠÏĖîí}Úw>;vė‹]'ņWįĢNQ Ĩ* īI!*ĄŌ’‚*·ąĒ(óA\*"þ*BŠ„Zņ! ĪP"’šŠR+Ō€‚Bl“ΉėæÜģßųūvgfw–9Ŋō4ÝX%*ņģ4šũæÍ›ßüÞóĐúņVMúU­T DļÕLĸķū|#l„ú‚˜ĸ'üHÜ>?ęįÜņ DĻTž# sf˜Ĩ€Y6Žr˜ÓÆH0Î-NĐôBĀ„P<Ũu]ŧ 9ÅbrīmûfČ įXg Mĩzk›hŅ-„jęâF1NĨƒÜÂį 'jVŪëĘŅģʑ1æ…õ’‘ð{Į›ĮĪËĒ+ݟ˜[iyóø+éĒ‹0!ČļC4~éí“Ŋþ•õ ėÛ·ĸĀúU-z8đņņ­ûķ­IūwîėŪ_ýāęEí=îčÝÆT˜EaÚĸrüä;ÖSŋĩũévîčŸGļãޘĖĨÖlÜÝύŒ\›`ÜãT’čb‚„#ĸÄDz6õ‘MÏîíŊʎ\›˜žļza*īþËÛwôn‰ãĀݟŲÐÖŌÖŧëĐŧZ“mwú™Ý{úŸØ4sõo“.™*‘t—)ÐŲ+ŋúí‰ú{·ėęĸbîâ[gĮg áåĖdûš-Ï>ņĐÚ ÃäPsņýoþxėg?øöó/üüJVä.ŋqôĩᇟÜūõ‘îó§~ŸÖęÖķ·uÞóp_ßWššU2%!6·^pŠ~ĘÅēUÝņôÎÁ};zýØðDYÃ.HWՏ7ÓžúQ…Ą*d㍊›ŠX”•§pˆsģzŠš4 0AÍJHłdĒVČ&rýoĶÛįGšÝ>?„R û―åP*ÅŠâZcn]Š;ŧðŌÜÔlŨÔR‹Īå™jŌápXŠ2‰ČöZ]]]UUF€\FØ„ëÖÄęv &RÝĐOÅR Ģ ÉHu5Ṋēũe)“s:ŊËĻBQĀËGšÁCJĀš_·š78Č8°ÝĮoŪr y!2|zÁ}z~ní˜āOþüĻ)qÛąŠųhÛGïYÞđėūûãĒd™åsïėiuĖž­ŊYĻåË&nP#2Ē:ŅtÆ(ģx]gÏēa&gķÃįēĘ\;ĸōũŋũÂK‡gLĪcGžĀtcIGŧÎhSgŧ,MÎRŪÛš: ›5vīĢ89k!L\Qq–ō@›É•E+ęÃļX,Ę Kđą?yųŧÏĸdhZ3*šĄĻA„°L“Ų&ržIëÚZŦƒÔDŅ‹ëËīh[<ī°m͊%ØqŒH%—›ũå•V‹°—1il銞;[ãÁĻn›Vý’5›zĒGüC/ū:U°„ƒt fQZ9ýÚúé‰3H Øs\™5ķįĒ!$hĐ`ðÕÓŊüčĨã$ڐ0c…PÊĩËȎYĄÜ{PþäæūŧŸ4&ÎþæŨĮŪū›fþOŋxņ—§FŦ“‹0c’5Í@Ôīå“F•øØĀ’𛄗,_'QĒôŠ\1ššÄq1Æn…WLlAâņ™ū˜Î2VÎÎäFÎŧPŠ~mŨķĩĐ$"•I4ÄÄ,•É{}Αp$;6öîtĐ<û^úZ&Č`ŧēYSÎ’é Üær‘ķʁHĮvĐTī8GĻŌē Ô^ũØÖoėz4?|æėč d;w(ãfvôð‡>öøöÞÍD‰†]ls!“–ÔÉĪå·kÄŠ‘Þ}ßöxnð™eu†-ķqĐP–üĀkĖL\ÏĖÄR+š·ÅĖÎ4ĩķ„Đõ_ęÝ?øõÁ=.0\.ßb*[b™ØL0Îō™Ëį.ŒĢŠT …ôXÖĪÓãĸĆVÕ·…ÐŒ †Ðøû$įӏ„ŠūS ūTĻu„‚ÏßW)jbÐFĀyޟĒūŦĐjø”?/?^æóōãY?<~ 7ôÆŋHģšžĶĄ0üëôE>ŒŊ~mmŨĩl™‚ œY$B‚X  : Pl8YeĨ[ŋX1þâ%ņGxĪæhÎ.ļð―8yrúžÏ9}ōžWĮ0LÓlÜUģŲŦešÕĨEmzōú㇟ĩęyŨÎfūdfŠŌ°,Ā4ØbYVĨRQUž‰ÝÞÞÞÜ܀ŊårŌ ÝÐōōâÛbēڒžëå{.w<ņ͐Ūk ÐųÃŅu°‚vØE öƒS þŠïŦāˆ@tÁe ĸÞđp°‰ļė€ģÓˆó.Î^0xæÓ™k5LËnÔŦ)§ÆŌó·åkĨÂč`x˜bY‘ÏÏLũøý|dęĩĒ[PÄvė͗OŧÃXl,#BdbŠXW3 öðq?ĮĮú{ĢkZËU§Đ‘úy!>Šæ‹šï;đT$)ø—šHËû§ūgŽdcŋu2ĩ°yļ-ĄNšð4ÕӉmŨNö–1ðŽ?.Õkōlbl}įüŌ1ĪgĢ8Eâ-Ė]4=eo=Ép;5ÛUø‘áĨ­’åØA Á'f,―wîĀ&fvOmïŽģž]ŠČyf”ˆ AM–UĮ6ÎgđÁ‚ßÚ/fäå“ãÔ`§ļīqR=ā(šlķJïĈ°ę~ŧRåMķŊ+Âr,ËlTLM~…‘Ųšú7ĀŨÕY§hgÂ―øJáäęĘýž6ŨŨ3ÄÅ9šTK/冚ŧY1óUwĨô“ÐÎ ąh8"æ=ßYMttôÅã<îZؐ=ÏnOf{~óÃęócšƒĖ <Ļöt^Ðų‚"č°ĸbÖz^Ģh‚hþÓ\ūÓwĘí‹QņŠˆđ|ˆHTPPbŒŲdĢÓŋ§{š{zOŠŽŊ§ÖbÝYcžYČP[õúÕë·ĩäā ČÏÍŋŊЍ?xnô‡Á[f ĨÐHĪ”P”B°oįžÞšyöāū~õ2ƒÓ€i­ ÉyJ įóųl6;??*`ŨcŒ“˜Â‡‹ŨïîýĸfũéŅíãŲ Œo=ÉXErNŅåž$€Qda”Ģļ†'Ŋ\YÅģŠóÄĐ?kzŒY•ąv ú Wo7õä8Ëü|/äSðü?Ōäœ|hGӆļ(I Ą­ėĻtÁ ­sJÉÎXšū$šAŅÆæ-ĐŽG`b;ĸlïž:-]—‡’BûĐVŽÎZ-O%­LŠá…ēąKđsķ ⭂Ð.ĄVb°ŌļZ4ōBęspRĀ}KiąŧmX@§ 釅wŠĘFQ6•ĄģüåÁ[_ ZõXßG,Åôī|5SÅūïŠWrÄŅA"áCÄĨĄB9KŠäcîšN+réSMSÕīÖĮ˜úޒ=ä6ąóZ6óųGWŅ.â% jŅķÁíS#Ô+āCέUÆw‹ÏCŒQđĸïöÎã“ÎÕvCųæýáÕÝļ?„Į]\ē?ž$ †w› |œņžØ”óïkm/0ãY*“\ōûBp‘åM_x^ŅÍTSäá؁+úģ…ŧ5X Š1GŨ—‚wðÚžÃbސĩ‹—u€ČA—sÆĸēáõą/ω-$| /ũ UĀ ð7€ŋ"'ĐKlLKƒ(óDâgŲĢ<ŽšŽ“9Y0ÄĖ`~r0ZEōqŪlôâ”„eð)–ĜԝúÃ#6úÃķc‚  rĨc—h%­Œ5ĸ؟FŨBŽ+%/îėíόąāĻʋŧŨũž―?ģÆ*ĩT}Ču *dŧ6Ļá_=…a@€Ó>ŠY äĮēĒŽI0)!Ixēoˆf9‚Ū@ JR,ĸŽ’–†r]ac 3Š0šļb#Óh(€RtØúŽ,čģzÄ ŽĘ(§–:GÖT€N jīāÉĩĸnV>Kē9þ#|2a•Ï?āyžÉá§iÂÄãņhƒ1)žqЧ˜@EoJŅ]ëŲåWSf#zĶaiũčœ2ÐR}YVWfcŪ->x,Åiņ‰ēĖĮščŽmņ1ĻųžJŽÅĮĮÉUەl<žæ#ĸ'Þĸø6=s8Ė·ŋøõvŋŸrU>!žĘ‡ âƒzDKødūÅáSŊðĐUōi›•e͋QÉïMÂĄü*M•ï™ŧ‡ŧûŸßMÆGESĐųėũ{+m\åƒo―?%„wņņØâ3aō™W~_ˆ->Ø2§eČßóáoäs1Žã0 ›aã‡#ūïú͓mų}ŨuC?l·ÛqûX·e7îHĄPF`Kē(Ž"PT!pÓL1[ļFßũÄņTڒɈS@##ž3nå.ï ^īĖ―zVāiôz>ÖâCV“-%ųÐH™7YáSÜ­ï >!>X›ïĻðņHĖ­8ŪōÉ(ųėÆ‘Æ››ŊãĶāÓoPVųØ>ÔûĨ3ö>Ý+|äcąĒ|°rrųþĪ“O č-ŋc1Z_ðŲÕïO–Ö|pĀžĮîKg‹ï›LZ|2°$F Ēô7ō!sø`Øi|ôöū…b.ãĮ‚ž?Ą]ōA‘Ę2ãšĮÓø †“1ü >ø‹ÏÏvõlŨŨŨø?: 3KŲ§'ËâēĖĢū>ÖõĨˆg~]ÉU—o•i]^^ķ––Á:ŽÍ§ĪWōQü>ÏÍw Ÿz”ŲšĘų`/ø`Æïâc>ˆËKĢīø\5ĸãÕĨ!ģōþ8ü||īäó›:ķ†Ą j}ï?jšļ‡HïpÅÝjĸÕĖėOčĢÔāeĩ°ZŦĀjŽĀjx*Åi’îNˆ{įÕĩũý]NŠÝDKĒÆ–˜Ükr“Ļ %æ5XĒÆ$bIėhDE‚―!"Jl`C "ˆÅBSQP@é‚ Ō{™FfNyÏĖ8ž&OÖzrßõÜÄä؟?X{o~ŋïþî3sūsVV^f Â4M!MsNQSQR^Ï âŸmĒ%sĩЇ@Ē–`CņÖIį‘#Į‡%V`–2ÄF­U§Ü&[ϊ;wķ°°0&8 G!2Ļ#„)cŦŒe$cf,‹ ~QfnÎŌ͚uîdAIKJãNúÂN ;"\_`ؘĨq…ae2KaĘŌR*ïÄŌXR3·”† Õą+–™[ہ™$n:‚tJSīĨņ BŠåjÓïXŋóÐhiICC%@Ē–ŋwLjØ#F}öņ@2<årŠ›gĸ`/áäéÛ 1EQĨ)W6.‘Vfŋ|_iŒ@MnÂþŸ|ngæÅyϘ1c_@Ī Iķ7Dųï“Ú§/w:Ļo—īušÏ°·_ļ|wFyMĄ_ŧ\fbdtūVŨ•Ķþ|&›ÓАģZų“Ã[gÛÛ;ø‡ÞŠņ;v>\ہMÕ9ûö\OÎŧséŒE›ģj5,æS/žgo?{ĄcäƒDŅa Úî^:Ïåjf-–Ž‚4·BýO\xō,gíŠų3į, ŋWŠ­ÏÛë2cĶÃÆĻ널Ô(ķ”îßd/Đ­9ôĻVK!Ä·U‡úė9“\œ›ļz֌EŦwgUihūéâŊ]‡Îœ>đÏûTx…J#uƒ_C Ļ%@„uʘ@—]Ũ”~9ČĮ}ŽghĢš6ųq>FYz:āf‰Š#QK QKøMDą Ō<ĖXü(îBHTĸšÕÅûËKrÃNmí­)Üįå_Ī6[{8ęYœ·–ēÛ1ÁåjˆäENöęĀý?_=đyhUf–ĻĐvEdîĢFÎō“EnQ)"‚Ý_Å|nŒŊũõ‚V“ĢîĶú8~ûav% (`bšîŅ ÜNcí–9,uŲffĶ?xZgfÎäÞū”˜ÕóýĐũ‹ĒÏîî§ŅŠ!Ā`AhĒŧÏßéŸë7Ķđí8ßÞwLtDÂĩĢkÛ*Ŋ%Ķ—e'>­dŋŠÎK :ę ŠŠŽ_Ʉf9ū]' °{;jf?ĶYŅüú—ˆ˜K6C-ęÔé„ûūngöĩ1ĐÂī™îpUR?L˜óÍdïKŲԔÔWpÏA4Õå*‚Ķœ{'6yėoDP““˜ĶœžīQōóŠÕŽ―hË^ýú2zŊĶ0øį3ĮōĨSÕcÔ^‘đrþ·+<ÏhH/ŠSÖ7T+ä>eåî 5uųuíŠGuígŋÐéƒ!ƒÞĪÝFl]2kä=šw@‹ĄĶšŽURĻĖ?ýíøiGŠAŦķĄĶQÄHÐņ=?ĩwY4ë―wÞíũfwĀÕķp…Y1Df˜†áƒ„—ŠaÍÍÍ8„!Â|+ėˆ\ę#ŽĒĩž‹Eóæ8Н?Ÿ―rú?š‰ÓæÝ†vƒéžT0ÐęxN  ę85!‡ķ~ĸÝ܅ .˜ũÝí9äy€õĸwčĢūö>ļõ=ĶĘcĐýøI“į:îČkėņÓރkfY?>įa7nÜø‰Ó–đz+ĩ=fŒ%HfIIS!ĖŦ‚ݝí'M°›8;ęQŔ%Ŧ—9ļû°Đ]‡ąŦ&Ų}1þëY .'• ÃYhŽļ)ĩhŌ{―9}fólÏ!v§É°7ēŦÕĖ―;{7Tėßúoũõ‡,āõI1Ä: ŽÄËzý+(ðÄĖũŲCŲ}a7áŦ™‡ÂîZôĩņā‰ŊŽðî5óíÆO˜øíŒcŨrDš– ‚ŽŨ Hp:˜S2…!žât#&:öŲ6HöÐaú„qã'LŸëxeY†œČ‹†Ļį9āšSĒķë°ąsū[[ŧpÚĒŲĩˆÆā@^bJŸåĶžHjjiEeAnFJ֓چđBÕĪjxš™ŦĸCŸ7SŌrŦęäęÆfyU҃ŧņŅ‘Ņąņw2rrë•eRóģŌ*…RY]ö,-%åqI•R^W•q'þfdDdü{ĨU j•BŪP65Ŧōs3ânFGÞļy'éaai\ĄëQČëkžf?ČyVĶ0ŽHH‚u•Å’āÃī‚euŌíøČ+‘·cB―{ÐóƒĐiĨ*eMYvJJV~ąäØØÔØÔTU˜›pëfTäø;IųEĨ ŌūęōžøØ˜čĻčۉũŸ–T(” EYĐӋŦę$·ų’JÆ#éŒĘ†šžœŒ”ÔžšzýEhV+žæ<ž›˜š^VS§hĻÉKMIÏÍŊiP(ękrģĶĪ>Đ­—jUĩåÏîÄĮÆÆ%–ÎE Ŋ›!(”*õ/‘2N)-Š”Ķ …~f˜† ÃĒþ§ŌX%õ*ŠįĶąR*1TvüÖī­Ģ@ĮšĨR’TvL;UÍÍ5™cŋ Ākï{ Hž2ÔÅ#LŅÜ,)…~­#aÜZĐøíEĢC•ĘhÁx*ihrÞĄĻčč3ë,ųüŊÅ Ó$f $j EJyõ―‹!ĮŽųúøø=æwîvz­R­”ĩ„ĸIM­mí&ښ•ŠßÕF Ļ%‰Z@ QK  ^JhšĶ(,Š€@ žT@8ŽŨétäæþrV­V?yR€1ðē ōžðöÛÃ,--8Ž#QûŨ!$—+ŧvíŌ·oA@x9Ā—””*•Jé‹čÉSíߥĩ={öāy„—šĶΜ5>‘Ļý› šáå@þ›[’|Ý @ oö"ĩ@ QK $j D-@ HÔ‰Z@ Ļ%ĩ@Ē–@ $j Єq‚‚/ƒ+„Œ–~ÓðŋáBlāŋŠA$;ĸîEûĸ:‚/t ž_^U‰Úŋ!<ŨŌÔĻV7ķīi)šæšäiÉåAøgÆŽØ^œ—VVŊ‚―˜˜’)„‘šēø~N 'Âß!„AŦüÆÅģ!aųõíÔ y*é4ä$į”Šž$ø;ųķ–6^üũÅĩ5―EA“ˆ‚FDE)ÜQ1,Eąý‘ØQZ•Š43vŽÝ‘ÝFëEš‹―š14’$õîŒ'ĄLÛCøÂ].ĻÕ*fn?ļßëȗÃŧōz%ĢgÃîŊ]6š ,B€L§ )%3ØĻ/ôʆ…ÓĐï—ßMë%y(™jĪ0’ïč―[L „;éį|ߑŸÏē&rBĮvFqhšJ[ã^ÃõFqÚpÕ)ĢōóŨˆ1ļŋÜËxaRúJLI”ĄÁĸ'ŪW†ŋŌ1:aŒNŒÛHtSšéîP6]1ƒC=X_Š_A, ƒ―ÝBîæK+āEDA‡šÚûðfˆũéû• ÓāęYŲûÓwú˜ó"Ēõč·0ģãJJķ0úåI ÞLïLLéŒąF:áóad(~þĶEKƂöš^H.’HÔþoķíÝï-ŦQŸ,šŋhjjlӉ‚>qøvEčî5ŸŲÚLYwĪĪ0ˆ ũÝjecûɧģÎÝ*Ķiš$ųĘžącĨßkɈa[žn5ÕÖúóå>ŨÛmJgÜZ•ybßFOĮđKķ*(œrņ ĩí—ß-ÍĻnaY\˜þÕ8[kŦąËüĒue‘.=l§­Õ7_mKέfhĢ–Ũލ 8|aó”)į“ËõųŠxvtŊã˜1c<Ž'˜Åm ĸö6ķÖóVxÕ·ã FaáQÜåāŸã#|Ø|2^ÐĐBzôG+ķSņ h­órœgõŲg3VT·34j=å9ŅÖÖjÁÖĖšvŒ€ŒQĘe_kérŒ™ž/øþ`°%*ĀýãO­W­XŋtMrߔášïˆŨ֕cŨ­lÖÆōœ`kkģČ-ŦūÂļđðÞš…ŸØZO<ðs bØĮᇾ|ïr;ę_ãýŊÞ:ĩgíčĸé~ėš) [oxJoŒÎ;ōÕTîÚïpĘoęčQS–š4s•§ŸÝåūé‡UĄZšy1']{+‡ußRgũų܏ņ Ügó XA›w7tÁÔOm­GoņđÐÐøČm­oi+ 0sÄåLrK!Ó3wËÏõŋŸÃlŧOŽÞJNX>wÚG}øó"†nøļžKĐb*3ÚŨ5(•eéœČ3Ó?ĩ•ÞpaéEeŊûŸ öt]ŋl]˜–‘Œ‘ĻýOB@4Ĩ*ȉŒÝĩå„ÐĸóÁ―ÍĨðĨ äųSŽōDČé·+ÏoôŽiؘõé°ðUãKļ]jÚģvĪ}ļ(äė™I―CkëŽynŪxgzāŅÕÕá{Ãî•Q4ežÄ6ElÐŅ‹ŠĄ?Ž\Ēšėļëîķg ā6oTk9Ôy Į‘ĀÃN ƒ|Ÿ4ō%·Ïýļ*|ŪĮņ ĸ!Zi ˆ˜2ô}›ũÞZāąÃú^€Å™áŲ”­Įĸ'tũ‰ē֖Øc;| ?|üuÕ-ŋKI<ĶĨ.A€ýGŒī5ÂjĒ‹ÃäaąGķy7z?ãjĄēøöí[Wãf?ïVųk‡OžÚë9ýUPąÚyc‹Í —öyFį4 A|dO@^7Ï­óRÏø'äÔ”Ý―ž+Yã|8pßF—N4x–pjĨ_ōÎāsó{ĨŊr Ī 3l"Š:‹Οĸi1°ÝŊšCY!;·ŸˆÚäåį0ķ_llHy+Į7WüžoÏýōáûė›’Ŋ<žúSðđïš?Xí~Aj\į­ŽúŨöĀ .íސXÃĩT$ïÝïS9dÖŠ/ŧnžŧ8ĐæŸ;œ?ž|8°DģÏíZí—āėŲKc7{]ÕōÚÄĀ―{üS—írGYÁĄ7ŌšúpĖ?†O˜ô͏ŽĢ0Į‰"xQĮÓß/óbsƒÆĖ<ýĨÛʑ=Ĩ˜˜ï―ÍÝsÚwgzØî ~)”ķîÆÄ&^DfÜPÕ0,Lõð‹īZīyōkéģĶmņÅē%ã,|―ÂÚ S”{Ŋš"TW˜ôāA„õ‡ÖíĐĸfÓÅ ĸQzõ4rô{Ã&NîüÃGX'#QûŸ„€*,ʍŽ‹mnļķŋ9äđÆŽøpyaΚ•kãģiuAÍļöŠí.ëBâ+ī\™’§ĮΙÂ\ØŧÜÝŊRƒškŸ<ū7;*ČeËņžV^ŊŒwüŋėœ\ÉķĸŦe ·āÁļ;O€Dˆŧ'$@܉oÜÝÝ7J܉  îãÖþï$ėÝÝ{ũÝũöÝÝũïï' ÕÕ§NŠž9ĐîĶ~€ÔjEÞö,™Ũ­…SáįĂŌôËĢũßz•ü=OE"&Hõ‰ËW_~RÅC…4™š:vōo; ,4ž›V­ÖđÐ[89:y™xų6·7æãĖkČĖCK†wjÝÉÄHĄ”T%TæKŌ.™ŧôņįL…ŊųÚÐ dbãėėėæčŲÄՆũ.Ŋjāę)íüÝNŒīwüžX(n˜iaŪĖþšQm>}ÂhgïÓÂĄ€GKūŽâՓWIɒ0?(íÍÓį‰Ï\‚‚EĢîK1ꖃ–ŽÔ―Š’ĪøÕ―BÛþQ­žžšŸÞÂ@HýÎs„'bd™/irƚŭ=Ý[īiN–$|ÏüpýpôšģEßą*ąŨb–]ÂnŒęŅą›5U%SX·čÔKX°%:ō؇RC!•ðøēĪ(oudäđ—˜ė{ąĒö]& DdĻÝīxņÁéjŠLÃāŸâī~SúĩkŌ~Ðä 'g˜f-ĩl;bũĄŲ­Ü,Rž]æŪŠŒžðА§—)Īđ/åUŲŨķGÅ-АI•ĨĨ[ŽZī{BH·ÍÝĮoÛ>ŪGÐHsÔ|)*ýķ.zÞÕÏßSRr1RFÛwÚrpGĸ ^ÝÜ= •Zdbįhmæâîéåá„04`ClC“fžÝæMîŅĄŨ°‘―Û øöā*pę3kp'·f=…ĩKūýL†ŠøĻąþ9,@ļĄ†!PŦĄ 7NėÔ&°u›a æŒëÛ9pÃ(؞6†jq!0€Iĸ‘―ŠũŊŠ<'ÁaCs‡FfnޞnŲĀîĮŸ ށĄK—íÛ°jNsgŠĒ` ˜o`âhëīpɊCgÚ2ē:ņōüõ‚ÆÍ؝‡ōa€t™°āÖíCˆ”ņ+Š`#‘C‡ā1KWĮ^š}xtw 'ęîšC‚čÜŌ<bíŌlúޘ­g.ŋ8·Ø°ę͚Yó ýFÍÚßɘĒQ•ĒZÍR^QRR‚ HÃ'{2%†Q`Ą€…‘€ ī4Â0Ė#p·Vý#Ŋ ŲIíU0 fí˛M^œŋíÛDŦ Œõðį§2.ÕrÐÆĻ > S„')†&5*ŽIßÞ}úņë‡Įï>f€ ĨEđĐŊ>―HŦúžð.þÛÍCGnLŨĻđHhdãÝwdĸũq?ŋų”ðöŃĖR)Ļûø2ĐR*Iš Ü*4ŽqŌ…ŧϒū~žņ,AC#„F›“þíÕŨīøÔžŦoR}[A_ĢVŽ_―ņĖÃdŪ}VËЌ‘-T^p`Óķ,)ÎP˜BĨĶu'•BĢä[…vĮžÄÝņņËûįŸsŠ˜NĮ 'Čē_·>ÎíÛ|āøÂÅëø–!VļV­ÖâšÔĐ•*ĩŸî&GöÄ>~ ú§ûVCÃ]--ŧũu-Í.o6:ÐŋK\ThlŅÜÁ‚PĐØh(å t%­JNëŅó‡TÝZ9}þ‚č g„ä/ŨJ4‰Ũķb”* 0m2Ŧ]ëss§ýīgï– G%fM‚Ãü_Þžņákâ‹Gs+€Ą•JĄKWīZ…“S”ðėčĐÛÅ2ˈo €‘Ö'Ø|ĸųöëĪ„W/_%āpíK$šÄ(yiqÖĮwOŋ)ē“ïßÍę7kŽsɅ™óæM[ēGR.GYCĻdŚfÚšh#Lŧy'>)á勗Ÿ(‡vs:ķ{uėôĮĪä7ÏïfËIgįGː”R­/C*•˜Z+č?k‚'!.þۇŨ·^ĨRËq Ũ™ājAē=Āvvæ?Ÿ?uãņ·ŠoWŧvü8O̓AöBȔjš † ÝšŒð·OYīlÓĄ+6ÝĐ˜bjboc ßĩyÝō)1ũģs4ӅFē'•ZĒfš•*]g|ãÔūUŦĒķÝČSér~Ųđ­{ŸĶæ3@`i ĩĩ3ŧvæØ‡hĀ_dŅĒE€ã/ŠĒ‰”ĮCÍĖˆų—ïÄx†æ.î^nöāÚwýöŪūn,^-ûķqÏËÍVa°―“oËví›ŧXWVĻ;ÐÍŨ‘Įޙ›R™đ…|6 §ģ66ômŅЙ“]TBÁÆūM›Z› jz‡ų†önūnnB˜â™7îØM^š'Q†nm[īiŲĶ…J^áОßĪā8dօ}CÔÆMUVáÞbāŒiCžÝ[™ =|#[–þ„ĒÔÞŧĨĢĩ‹›·Ŧ%Ę7rõöũónėŌŽUŨŪ–EyĨjxú5sī2úÞ€˜[;y{đZ˜lýZurįåæ•˜xö^6ÕÍâ™Úxú6sķĩ™Ųzú4uēwčÚ5Ā R•HZ‡Œ]3ĄŊÐf>MÚØÜķ™O‹~#{9›DäėîÍN›Đ™ƒ'{ęh !Bgw_wWĸŪ}šī24ũÜÅîÍÛäāą“ņ(ŠĐyF!röðņ`[™;zz{ļ:X5íŨËÏ•* ˜šķhÚ+(ČۈĖ..ƒųÖ~ÍžYZ4vņôrķōųön>-[úXōé‚ĖĖ*œ<}΀–ösũ-ËóģŦļĩ―Ŋ˰P<ĮÖþUerĸCCŧ·`aû^ÁÁ=ŧ››Ų ęÓ*íÍËfĮ5mėäėéííŠ[’Y{ėÖĒ,?ŦZA°~ž=Z‡ôōrPäæ‹!ūqÓfÍmmí<|š9[›ĢB+7?o§F*tróņóīģrjŌ―sŧŠĒđ†1qņjîéæėâîïmo(@,mýš5ą23öhÞԘÖˆiӖþ^6þ>~fB„ųÅý„…ŧ§Ģ―BS°‘CŸ€ŽŌĒ&>#jz€;…˜vnæĨ‘*š =5Ēŋ—·-{ ęڛÚ8ûøúؚðEšKååjcĖ™zųøzļØļ7oiŽĐây͟2ž­‹­‰šĻ0ŦĻÜ6hČÂą}ŒøĻWóĶ"REó,ý|á?ĸa- ÃbąXģĐþöņSýŨƒCĐT&&&eddâ8Ūų`ÅДķîP‹ Ãāúc’fę Ų34óšf›ÕBXMKĶaÍo9dÁ8Ĩĩmý­oĶŅjišų-?,ZJŠĀÄüÃđ–ŽũKŋlEŨųŅþCĖ8{LS Z gęĀk‡Eę;eûАÄu•ZĶëŸfOÖŨ8‘ýėhXøœ;Wtjļøī†ŽCÃVĪū [SĶýÅ!‰ģæD]=VSƒõĢŽ―dEĸCMÑÖ#Kŧ3~Ääí;ķE·q_^(Sc8Õ°IC?ĸĄ­™R_]ŨŠf,úöŲ ­ŪžÆõ'tĶ$^ûÓAbõ“ųKp’nø!Äkí™úĘÚhšýIģ3ÚŠŪ† uĨšîĻ:?učNiéÓ[gY7u>AĪĶĶĨĪĪ*ōęĸ ûüÅāŲŲđ"‘ÐÅřĒ(Āņŋ ’‚ø·ŸJŠÔ–Í:4A óˆ hŠ>ü˜™W!ēõėÚĩ•Ąĸqqwœ™™Y Ã899â8Á=ŦýSáĻÓø?EÝÞ}†æ™;õ0tüĪņ!]›ĸó< ëEþm]…ßÐm€~ĮC‘eŧ ą“&…ũïb)‚ĸēyV?'ĸŌFŋą†8 „ĸßáR)CŋąŲb+!—|þðĐRû‡öĀBúfĨqÁŋuP„V‹õy$Į ’bþÉ.=F›üõc‘Xņ#;ŌīFŦĨþEü!Q™‘ô!ĨЏ0p6 ōũžĘSĪ>ŠŋĖ/ę§j84W%ūĸTĶ þÉEG *ĸëËÓG?K*ÄĨZŽĸO €Ŧdb…†PƒJF!Ū’iI‚ yæÆĻI†ũ/s(ī*ĐLĄ…þŦV\̐ƔŋÎS| ņ놅{ojiäŧCø|ËŠywÞ§Á(O_ÁƒĪßĖßĸ]ÁCað{ÐĪD*ÃHšĮã?ōÓüpŨ‡‘pzÝĒwTJ‰B‰ģ[ŋy\†ĸÞ @0Rųlrð„'O'å3€Kĩ+8ŊĢN=FPū…ō†‡ĩ"0ĒW8ĻąG‰ŋsâčl ŠŨ|ûW/z’-ðķ! óBˆmT·ąĐņÉÓûރ­zĩnåē'Ų!ú•^“e#Gā‘ÝqÍVw”Į:Öę„t ŋšŨFX;=0Ôā°^Ą~Ũŋ@ĀÎ*dÝ5ûČC’'dĻ™_gLdãÖ―CšĢóĢŋÎ ëü6Ô"`ƒÕ‹< ĻŒę~›Æ6aĄ=Ý,„€ęĪl8ЏDÃÕŲëĒæ=û^ÅVÂvŒĩ){je*Ø0œÚô éÚtƒN‘žÖüÚõ ėQU ĻF°‚Ō_\~Ýė5üĻühĻ‘z€‘eūÎÚÐ3ŸW' ÁšąĶšéÔyCyð―eKÖ―/Ôkî0€A…l…YkīîÖpŲēĪ4SÖ*øÐ•ë1aŲ jNé:Õģ1°ðë,ęrĐö? ÂãË Ƅũ č2rúŪ 5$@ĐïONũėÐkÉÆG0ų|oĸˆÐŽãö―@ųЇë{ššĄ1€nÝīnҞ‚{šS™l㖝ÛbĮL=!†tß6•Ĩ<_›ØýðóޏŊ ŌK[uïŌ- gŊĮđ*T]zaS4{ÔkôŽŧ •l>Čļs`Įū;ÖÍęÔūëæSïHyņ‘Û—Ūmß8#bĘũJq܁U†LūôЄM2ōŠėėÂRūz°gëŽ];WMëÜŪïä˟JØÄ"Î}ŋ$,8 [ŸÍ?ĒØ„ áĖęŲ­ëˆĄëģJ”õ"nģōsaĶb9ïũîØqxĸÚNÚÏYsAJÔ>á#äûg7Ž;?oôÐõt̃ŦÓwoœŌĐCĀÖ3ï!‘•gæ—ąiŽ:ýMÔ vÔąGŊiáÚÝý Wgŋ7°o@ŨÎÃ7ž—’ÂZßģK·sĒfG.y“)EÉʋŧ—îØąhİ)y8Č|~.īw`ũß|Ŋā „Š’ŊÓF b%,ķĸüMrÚōoÛį/ßĩqzÏ ÎãwÜ|zyCïŪŲ_A"|{xd]ßĀĀ ĐkSŠIJ=?žjßÉSÓÆ†īïũM.K―·óڍ“s'.ÝtJBÔiå°Y•֯ߨŌ―Sĸ!Qé•j6/+ ?M‰ЭûŪ; @™ūsÓéŌÄŧ“Ē6dTęōĉXĨ‡ĄĢwÉ1>­šuvįރB‡ßöŠT‘ûz~HāqI ;V.Õþ'á`(œäÍZšåėĄųøĮ―ũß”å――69bYû‹Ú12ĖŊ<á|čėƒ‚W;ļg^GáëK36<[~ð„NÄ`Í)%@dų‰gN_ö‰^―(Äs}DT>ęÔ%Ļ[P·þ æ2Ã0$IÛ7íÜÁÛ-|ėčIM`’‘æf=ø(›ģt‘ðÅĄó RIâÅéÛL=tfķ{FÔēã:yý*O`íÚÃËĐwÄäĐs&ŦNOÛúpúĄ3ģÜūG-;‰ÁüŌOw6―iØgN`S‡Âũ·ķĮîï>tnXszƈĄLQ“úŧÞ=~ RŦ:ŋiV5äÜåã}ýlęö@ūŋ}þ’‹ ķïßŋ}K3GôĖĒ™ÛÎČķ<1§-=dÎį*BUöqûÆÍ_„AKF;Ÿ]{ —6ïęåÞĄ{ßiKĒ iSßāsW/N0Ė?u>N Õ)Oî|Ig īôÛË―ŧŽ7 i™{üĘcJ[đXÚiü‰“ŊoÛð0Đ<ýŅĐĨû §l<9Ė[RT Ģð'ÁDé‹ļ—eU4DTžŲŧéč7ãĩËGŋ;qôņįRHoF2Ļ·‡—o#·ˆ™KFuũEðĮŸŊĪö^<ēņĐĩ‡ ļ2™ #‹Įw7Ž? ĩ>pōÔčޝázŲš"`‹+wŸÚū@{u_|!VøöȔÏįė<ÕÛų֕+ŸóĪ(Būŧžũp:,2Z”ûlrĖؐŧVôu_ŋlwZaÞÆČ‚öӎm‰|ī|ÁÃLĩČï?ð•lķtÁ„ũ+&Ū{ÆD/œ[ņ設IþŦĢó}ÞxæėPãøĻ5—TûéōƟÎķ5ĒiÉÖ՗…ž­{{9ô0{ė°^!Só‚^Qšy;L_ĩąxšjĮ‰ĘęâĩsužudãŒļ%ŅJˆwąuí:wüðFdÁšY“hßāc'5o/Ųt\øđŊŊlÛõ:b|Ė8?Íô‹ųƒĒ­ ?―~íˌjäïžķEÁß@Ķ–†ũïÉĖ-ʒČԘ4#û‹ŽmЌqÁ`BÐ[ËĶ;Ų\;3ؔ xž-!ŋ$cũŠ…ęôĪTC#5E( F@†Ęß.[~ļ0ģHÐĨVcŅ†Ø‚Œ ‡D۟7°W|æÉrš"ĸ[Áũtĩōø‚WüÂ4­ļĒō[všØ !ÝüEĀŠÝŲ·u"õŊžx“Z­E—° ›bÚ"ßvY_+ ÕH:˜Û{Û8:;{ųđØæĻą&ÃĢÆ Už.3ޗjuéÚ$ĩ č―čPĖĒĘ) ŧ:ÁM0b*ܑ*ÖæA!>ÜšíÓuōāv~ĪL4°Åa’†gl›Ž^ēth“oWâēóē.퍖g“úįĨžš$S(yĻúRPV\TAxÍÛ,Y8Įĩōą“ģoԒ˜~V%ėī˜XüåõUIĄ|õüųōBÖw‘ Āj>dɖYƒÚŌČĨé”isė,DFÆÖV(D1Ãõŧ!RÆŅÆTĪ ePКTŪ‡!ÕŽNŅÕ ah§i†ÏĀ&æv†VĶSĒįŲó ,18]ŧÉÆ \ĒĀŲv§–‘•Õ”čH [b(öž9e(‚f€_Ƈ‘1bíq‚Ätđ íąõl‡Ī#Ŧ'ÄDBŨnĮZÐ8Ͱūy†HUe1`Ķi Đęēr‰.R ŧĶų(ÂŒĨ‰ YWà`0ŠËqTĨߌ^”yl]\ō€Ŋ U7Rbĸ%(ŠGĄH'kÐuĀØá>ž•&^֏’ĐT*­H$0‚ü֝cfhĀcMh) F ÖĶnFhvNĘp€X˜ˆJ'Ą€jà pŌ7dÂĸÎwm™ĩx‡ÓđM-ŽP’Œķtįü EŪ1‘“ŽvĮŪ#Ą…ĩ89ŋĮąÜäo ”;Ę0 ĄˆPᆁØĖÎ#büė–ÎĶB#+k(—ojÞ/|ԐvŽËųNmÔåßuĢf ‚‚íƒDA€á›Ų™Ų{G.˜nŠ™Z i-DÃFÖ-A`āC€Ö`˜TICuģĮ@ĀЀ õ/ÍT†D€ĒC'nãĀvęâÜĻč Æ04ÅÑ€@Hķä•6 €ōD(Û ā˜B<ĮžC'·qYūĘĖÉÚĒhîÂ@ÃPęjIQöŨw/Š3óÞ<þÔļIŋUfôœ%{wí>}éđ_Č sŅ·‘SbîÛđũrRËámM’ŊÜ™žüõöŦ$ M› ĮÜrāDėō͈oßķÎæï\9w㕊Qģf†oe!Ýīõėã·~ý'ŧXå_ŧô<ĩ‰ĸĒÖ-ýTJ ôZZĨÏȁ+UkÃÕ W\?°îöĮlšņ ‡"€õĻ#īObĢ6ėÚģmýõŊę ―‚ÞžÝŧåðĐÅKÖ ,ƒŧđ[aZ•BĐĄõ™OĐÄĩ$cíå’qöėþ eZžĒš2įëŨ—Š{O?-PBSŦĨ QáØQcŧĶ}ûŋūsëKrʛįOģŦĩ-Û~}|}ášM1Ņž|*DýģÚ:Ô‚Ī†bgHЗ>ĶĄj š˜Ų{Øãw/yøš„f06BÝ9šR)qBßĐZKðyÄģģ§.=þŽ"yf‘þƙŅĸA-QŸóáÓį—Y—Ÿžõė?‘/ŋ5~æžčŲkË­\í-’ÔĻ˜>yv îí[}óöÝī”Ī{O?ŠMü&MoóúÞå/Ißß<QĶ@`g/%ĢŨP°ëzQ* Ú šh…ĶÞ}ø!-%þíŧ$BBU3EĄÐé:ĀÖöqë6Į}É Õū9@Šŋ'ėYķéÄņÛN]ë=°ŸŊ_û1“Zžž{åsōũøįņ™å Ē•*’íÉÔĐÕČæ§Ö/9qęĀšÃŧ‡tw3hÕĪFK˜:·éęþ䯭ĪÔÔŨOŸJÕôËWœ§Ā0ˆ+)•Œ•ëĻĐcøJēyŊžýÚ’2™4võkÚĒMxP[CYE•†ąņôoŅŽY€ŪŌŌÜjđÖĀÁ―_ãĖøRÆÐÍÚąm―(fĒ―ÏÆÝÓÖ(1‘3OūþKN3|ĸÖM(Y5jhëããÞØÉÝÛŨÓEĖí\}|žížķŌ–į‹ÕæÖ^þþŽ<ĶVbÆÞ§―Ĩ…BDÍ;öØRSž_"V[Øxųų؛[Ú{ú6q47`ljåęįįîdchhÚČĮÏÏĩą…@dęæÓÄÕݙ–įdgWļv›5ÆV„Ðu9ËÖŧCŨ^•Å…bėŨĒuŨāÞ\ÐŽėB#þŅ+Ķđ™@|kwßfŪķ–|Ą™‹_óĶūŽŽŪ-Ý ī*mįÐ m}MËÅxȈą=Ü  æž.ŪÞM]l--ŽíYý cĄĐŧOG;ûn݂žŒ‰Ž‚ĩôiâįÕĪe[7SŽZėÝ:lÜØ~M|œ-…€Ņ§BÄĀž―Ŧöķ755utfĨė…<>;C­Zz[ņklžA3?_JĄ<—Ö}Ý―ØN-x"sWßæMý›™ģ6sĩkD‰Ësō 0 Ũđs'ķ°7ŠYÐ!œ„ tY^ķ1wslŅ4āø;ŅĪâËŦøŒ‚J•K@ũ.Ö€f§ĀņWƒĄ(ČÆÅÛËŅęæŲ?ūĮĸßsģ@ĸšÝąŠ7þÜŨŊ/üŅĸ|5fÅĮO™8bP`Ģŋ`žåāR-·Á БRÖÝËŦą?øÔ‰Ą1Lû?ĩŸ&1 Nę‚ĐÛJÏPXʧOYUĒ?üM\VųņëwÆü‘ĪF`Z‚ĶÁ°üĩÝ΄ä/ŸŠĪ$j  ýķŽ[`(Bw3þŋųŸ%IÖ #ppЖãŊ‰ŦÄb Ąˆ2ïšØĮ b>þ—Â1°"wϚØ'‰küßFøöÍ^ó€á̘JĶPbš%ŪF|4zþÁũkĸīR&Qi„ÏSĨž]ļþĀ7%ô/EFs}OėÉļ|Ą€ũ/FĮú—T)0 úÕ°ų°dïÂȧY2*e ÏģÄ ýZÖ'i„/&^ž:ïD5Æ@€ƒãĸZŠåÐm{čÐouŨïsŨðy5ŦÅZ…ūŪRŊ0€Ķ>]ŧv§Š‡ĒFA―ûy[R4ĪߏÖ4„ônŲÚfÕï‰5°Ô72 mŽë…WsČŌ0!ĩÛ1ۈ-Ô,ĐíštÖ՗Ģ/Ũ­\þ4‡m `č,P!€x<ŪwHĢ8ĩč`Ķ…``D[Z̆°fÝՋ0 ĩ―7žÃ§!^Ŧ€~Ü,‚f jD X+ŠoRsˆ„ŠÝ+ū,Ð tYž_Fw°!`x0ôTwFëfAxˆ&gCôü§i•ŽĩĄsŦ‘};"lÄš)Šq‚Ô]–šJ녃Kĩ#ti‹ÝÞŋĒkŨŪÝš ŧýĄœVŽ_0ą[@@Čāõ_ $|„yy}ĸĶcW–Ž ëÐëØÓïUvfÝēWO ī!ĐP–QœYÉКâĪ“[V;ōSŨNGNûĐ įĄšĒ#v―Ζóųšļ]›]üJ“Äũnj*@Ŧ‹OnYsėðfÖxÔôŸŠ0D€P_ŊoėÕ­kXØÂ+Ũ?N,…QDŋŪ$âÎnŲt%A @ޜÚģā,!šŧsížüēēoj™šäËÎëWN-œ9weđ5€ĻŌŊŨF ę2û]ŽŠ&%Á0/ũýåí>81bëé'ÄFTýôĘú.;ÎY}NL lïODũ š ―B[—~DSų…ų˜Ķ$Ũw.?yjxhPïsÓ+J??ąĨo@``ÏūïJ•ņ'ķ?{9fÚė}—’PđmÞ„Ā€Ū]§ŪJ,Į!ā ]áiJ6F č6búĘ,ąōÓŦ[GΞ›7fčŠSï yų§Šæēäė†ĻŪŽ&ÃØČ‡Éb61ŋ―Ũ#;bgvęļõÜ{vT€ƒKĩ ]Ę8ōÓâmÏĶ­ÚķwŨâfņ―óĮ?,3Ýâh_ŨŊŅ3į—‘‚ĘÄû?-‹ĩî3.Ē‹ééÝ[Š!ëŽ}īuņ_š<ÜŲPōųY\a1Óâļ3Ûwŋ"—.Ÿ ~wþîëB’§ūQRNĀ0“œũâ]i>DV}vŋ°„€iÉ―3ÛöÄÓ˖Wū9ĸāCqö›ósg_[°kÉïį§Ï§Ë š•,Ĩą/^•“ŠŸũ-9wđļJóöŲÕ§bĪęŲý{éBk—î{ Ÿ4iús!Ī­ÎļyņYČīhėųŪíOʃô{!đĩėéã2+zpϖ|!ŊøíÃKŸáeKĮ~õþÎŪõŌÂgEŧ+Ÿžŧ–„k2cWk―xŨÅ;\Œî]úĩóp6aLp€#Ą&šÏžt~_‡ô›Įž|Hi. ƒ$™ķ×_<ŧŸNđtîŅGwŋFnç-Óģ Vþ.îö%Î,œąóžú§ƒĮĶ7“GŽü&Ĩ”%ï6ŊߐhØcÁpۓkį*)ppЖãï".KĘ?æ}k;sÞðžíýšĩr4?z!k>2ŠĐŧŨĻɓÍyŸē* ”4acôØA―{9óÝ―\mÍ-[īö2åÃŒB0LiĩÏö{ķ,ïÐÝËÝ\Ž”ęWĖ(ÕÜ0ŨūđŠĐĄīĄg‡―[–ũ bĨōŠÔŒïP·ūcīn7 lČČ J­‚ôy‰†øm:t2Æŋ=|õ8UÖÍÝ"þéóÏï2ƒ'… pŌ õYļų›Ø{ų5óõā’0õ˜°zËčþ―šûŧ*5EļÞ CӍŧų5önénoAkÕ&Øžī_· F"ŊÎ*Í.-M<šbá…Ger–Ð/äŦ@ī…ïōÍkGöïŲĖßYĶŠ§ýšÜ]ģhųĩ/ ŒÚzx8X˜ú6kîlc.DyŌô+Qëũū”ĻŋZ{24chi]wqåšMRBŦĨÍXYƒÆÎÎ>þ† PD€ĻŦâŠĐðuSÛû{†Ooeó-ąPFãtÓQ‡ëÜū‹Ą°ZŠĄ =€ƒKĩ`TÄTWJp 0 ĘãÓ$ƞŅHĨrj`ĀcXhhÅÖā$Ič—fA`"šƒ:đU`$ °gH]E"0Lã*qĩ Ö%:c  ø:cŨĢČ+K*ļ,ĸ{ZRĘÖXŌãāÓš‰-žfô‚æ1‹įũqÜ;?^ÞkH I=K•$ÐåtFóĖ ø€ÆŲaˆ:RĐÁ]ZŊ0Āg{'qš‚ŲÞa·uk3nVÔæ―'Z8Ō`4ęĻ)1"5dL$Aøæ3v:īftņ‰sŲÛy@Ŧqð9„ĒöOßy^;yâĪ@wS’ŨļωČŦk—îLĶĮMŅķąĩ ėyŽƒýčBduYĨ Ōų­Ä44ŸWŦÉÐ@ņĒB­e—j9þâ0 ›ô P}zĮĒ5ÛũnßóĄČhėxßø ž=ūvÛ~“ŽÝ[;iTĪRĄK‰kÕ†&Ķ–UEЇÞĐŌÐ8F$hRĨTúÝú˜W“4X6ÂeįŽlYģþČí/(AÂīĄVTģÆl+ˆaÜšCHkYZôôYóÎzœPÄãÁuR°ĨK‡”ŌĘ―ü›·r)ĘÎð5Ā$ÉĻ48۟“Í“ÛÎ?y‹1ĶVĻ1@€ĀpĨĶV/Š™ĒäØMō*†V*•úPĩ SŌHێC$ß―û.3=éÓũ4 ýã݁ÓAą–•B_ÐyVSeßv4KĒ5@†|>€„fž‡öÍ,ĶĪēĪô{Âįį…ĘũîÉĀΠ^LP+)F—#ŦŠËS’ūäJŦÞĮ?(ĨĖ<ðû—Žß{^Á°R…áÂFc‚š>?ąkĮņ3K–­7° íėj†iÕĩš īN“ˆ„‰—Oß&Ņ —ƒKĩi'=ú=}cģHŦĪPS#Óðč#ąsCĘóJ›†Ė8ļiĐ1Lu ‹šîE„c“óÏ1Ē û–}ŨmŒáKĘTžÆĢ§Íoï!âŲ44g‘ŧ1MðmFÅŽ imGōė&.YÔÄÜ­Õ kŨŪŽ lDŽq;w!ßķ9kėfL|;Öļ_s[3ïĀýoN<%jc?Sˆ ˆĐÉĩ8Áëĩ"ÔĮ —NSVLëDD§ņ[V-ęĪĐŪdCįĆø$Ōcü’EÓzčÅĶX€š_wxÚ0;ąXfčÜvÎäņ.†$!tˆ^ŋĒ—#3ĸîGnnlãˆUD<Ôj0ĀÂÞŅ;ŲŒÉ°đąí…‰öœļdBhS97ågWŽØī}xsœ0›―yK°§Qeĩ*pڞĩ :ĘņF[ķïél+UOZđŪ‹› AšN\ÛÚΞ_LėŠˆÖ ­áŠ-‡FīuÓ&‹cwũņĩŦ,•šx‡­" ™ž ֝X9+Í·l7įčÅX”ð ž9ī;B&^ýÖo‰vЎ­C#Gö4„8þócc.·1â 0ÐA‘8AAõŋFJâI3<„Ģ`”ÏGaLŦe X āCšEōy4 "äĢĶĨX ä3$Ž“4* õëCŠÔ’īP§ŒdAąPČ€ŪúúzóʝüÞ}öÆýŽž8åiŐSũƒ5‡p­–†66ÖÁķä ø­ÕÂðtŌ|€Ņ+ð„lĖ{ԁō(Ņ$AÐPmïfãĄkBå Qļ~ŸÎÔό@Ņ8;<P ûI3|ĄĶ -Îč"ŨCāZŠ5hŠ (Pëáói]Ė@(°SJC<~Íē]ŋęfkTģb†ŲyÅīxý ģÔDˆð…<ˆÖb8„ð<˜ ˆ­îr€ĸ{psđTËi ü {Ïh­"åó›/Ųe|#ÛÎÝ\ ëó,—jQð?ũÄ5ïÚŊU ŽWz"đ<ËÁņ'ĨZNđ†Ī(ĀÁÁņw|-ÆÁÁÁÁĨZ.ÕrppppЖƒƒƒƒKĩÜo p@„ øïÁÁÁAQÃ0\Šåøí<‹ãxqq1MÓā߅ƒƒ†aGGGEđTËņÛČåōôôôƍƒŽ‚‚ SSÓß]ØrЖûߨÃÃÃßßüŧpppðxžĄĖ―ã Iü ppp_ĒŋqŠåāāāā~Ų‹ƒƒĄðĸôŋqĨ9ļTËĄ—Ĩ&'§}OÏÎÎÉĖøž’œ’SP–xũæÏ…āQúîáÚĻų[+Sƒ?LQ]\.ųÝ/8C$<ūōðe[——KÕāχÁÏ_JŠÄþÓiOûņÁåOĐUĸ}Géîūøšþ)„īäĖÖ +–.đŸZ ~Eîģ{įŸ'ÓdöHŽĪ(}1›WL‚­Bœ“žÁ~t‹ĘŠÁoA)Ŧïœŧō]Ü RųöîĨ„t xYqĄ §đTûŋGAŌ‹Ý;vîŲ;uø€Å+·íÞąûüíWï.í>õ.üˆē˜9ģŌ([?7ú—™Aš”šĐųË"R#ËLûŪĀ]„NÎÝvYEĸn*~uëĀŅ›lŠĨnî^ĩj["øóaīâ[‡6},'ĀFýėÚþûïÄĸýTûþņÕËũ?ĸóÎ^^Ýšãf‚O3/s^ÝÕWĨĨĶK4$[Ėđ{č`Ü ü$™KĶ…Ž›5sH`čžÃō/ߒ/Ūl 7ký†ĨÝšũZqōþŊ?!”ŽčęÁ- âAŅŌû—<ų*ŠžŸ"g?>ĢDĨ%ÓÐXEĻØãjМf€ėküŠyŦ_äUipĘĄsØōIýE°ÞF+cmX#üGū†xÆæ pïņóæōz4JYEy…XĒÖÛ1ōƊJ‰’Ūígã"ĩŠĘŠ~0đ„u,WijŽIĩBwĻýÔ@Fކ†#W‰ÚZeEyy•Xũ•gH\Đ­OÄīRĨģĄĩ2Ö§§1Zvˆĩ1ā* 'teđīŠœ範&0-ĨoDā^SŠŧĐĨЌ§”Š+Ŧõ'”21{Ĩ°ZŊīZ.)ŊĻĻąw8†‘8&ې”íŠJĘŲxæā—ĻRve BŨŒTįÉZö84bb{ģÚ!W}[1{æÏņĐ Áģæ[™9;ŅS—ė4RvȚ_.! .ÎãEĖ]ģqå˜Ä#ÛßdHô3Ūb?^Rđķ>OJŦ**Ŧåĩ$NЌR!+ĩ ŽēŌžF-ÖŊÛēl Ïé㷋aúëɰEP Ã, MiR\U!VÖ á™Z 08L]ķĶ­Ŋfâ%Õl˜•2 ÉĨÚ? ‰­Đ𱅍[ķ27ášėÕЙsÆķí°éÄWÖŽâÝíĐû 8kåqéDžļ|!)éãķEãÞͧݛ=qPøā~'Dåj(žāÝīđŅËĢcôę3!šDĢ|pįæóWæŒ›zîyū8íî,qßÁĢs5 )ûūqÎØáÃ&.ØPĶf>―ŧvïŲËícGßJސĪŋ8ó ž†@qÚëqÃú2`܏EІ2ã€ÖeWũϞý ™p~GXĸ>ƒ‡†ÍŒ>§äģËÛÆ† ę? |ýÕOlč)W6Ė3wÎüé=:·šĩü*@þëk#ƒ† ÛxüšiEęĒ aCõŸķ"S‚ƒ_ÁB­8îØŠðð~]ÂÞĪģÁhOmŒ4lxŊ€þ{Ŋį`yũ{M‰I“é’Ô·Ŧ[ûŊšŠČųēl΀!CŽ›ĩ9KŽŪ‹?ŧkážOuáŦēf[|ï}Á—ģ;ũ2,ŽoĜ9e8ĀKũ.™ņ<@}~ïŌÏōĶ|>ŸyĸøČôლv:ņ<­*úrsˈáCö[ļý’’ī"kÍŽ)ÃÃt<ýQÛoÅÎ ãfŒ1,xmJžėýąBCF ™ęŌ[>_P”ðóāA!aƄôŸũ.§TUøöØá›ïŊÆÎ^wNÍÔFĸþų{ϞĮΙš?.ƒ/Ļūݙ=đgį6ŅîQČR^ĮĖ :`Bäö|™ķaAP;{WŨÆ^nĶĪĪ|į’ŅĄaaƒ†L›%4öüðŠ!aÁ}Fļ•Čööõį­Ģf.=,bÎé7 ÝXÛØ9;đuhIHÔâēŒ=ËfĮ‘€‘ÛąäܛĄU•ŸÚ5(īoũ°åEÃ@Då­ GŌ%$ĨŪÚŧrrï~!aƒG,ŋø…Kĩ2E렀%ū}— šĩëčŌŽ—ö_â…ó€ íî+đŧãFbĻ…ß-<žĨ—EëOčé*hÔbý‰g/]eÜ}™&E€6ņöÏrŧ ËũŊ;dß{”Ķ éÕËßđóŪSĮĮwwX·Øpâ9k,ø~;>SĐHŋ}ęzaėákŨö­ą1€Zķï30 éō‹W·ąSUf”ЁŠx_lŒĖ7æþã'Noje-ĸ-)I-Ę'ąē—ænk;qÛã§OíŽĮG­ļ<öøÏ—w;ŋnsķęž›Ï>û[~}ũØwįÏäāÔË#GŸˆ:œđýdåäĄ"€ßÜą4Į!ôîÃ;― ũÎŋŋF`™ĒŠ øï=}}„ŸøĘ­ũ ÁãŋzņđŨVųpÅūë*.-LŦŌ- ÉęâLI ęPX*Î˔ÎßsfӔ.OoßˋL^å3vs܅i[cŊ}Ûë?yoœðó6M3Eé Z·'ŽķW>™đöÎÂãânŪnn+ĒhԁU§.™ķīËô­OŸ= ÁVŽ:Ļuė2yÚā€ð5ÛĢÂDPmũí;uoïÞzņŽãóúÓ0™”RÚaÔš+[‡>9qķL#ßī(2ĨŅäGũoðR͌ûÚ lŠæo˜9Ļ[čR° ]= _\Úp,Õåė‡ŧ0Ž_ÍNŽ›―ų^ˁļ“ëzŸZŋĒ„dcVüÃīŲk›ęð…Ý]8eĘðõzÍÓĖ ÎËK­ÆYĸDUAĶīš\)SŊgoÆEõDŨl:*ÕÂĖ@j ē“ešōóšuWŧŊ<|ōøæš>œÂĸ*Áë5avhkŨŒB;_…“ēLJ+;đīĸļPnhĀã:„Æ–‘ą™……P€Vdįėˆ™Z`&ʕS €%7h9hŅÜÁ"ĖÓĖ ŒČŠ‘ĀÐĀÔ܂χ%åŲÛcĶšä(i’И49mčĻŅýÛu‹ØōÓ\‘đ‘ąĀÜԄ‚ŒPXR’‘þŪj˜~–Āþų- a„IÜ] ë,BČ„—ĸųģžž`ÛŽÉĮ.$H5ÕrāčYAūŠ/ކrĻotdčԕÃú ūô§đýē^Ë-üíēļTÜú­ĸ˜HžđûĪic}++‹RchvM}ėøÍ,T]Ą2ē˜86xüĨK)ÎL*ŌqW;ëŲ;ąžG_)ÉÃF™Ô;õé<Č;rûÉ}þƒĮ7:ĒÉŋĀ^í­:ôiũüC–â#ˆ‚ā̈ÁęĄqČ$|Ftk/g^ЧY>ĐQW•Ĩå~Ûóá€6›/BīZB-ŧēcÍĢ2ĻĒ@Ņ_ ŧp„‘éĀą- ē“J ·–Aūf`ÖđoÛg ÔA*ŠÓJŒ‚[ĩáÁhPŸūąũÎ3ff–BÆÔÄPę[ŠLŒLLų TTŧð‰:{‰ß:ˆ rL™EYũ†~9Y\Dĩ24õ0šķ<>Šģuá–-ą·úøŦquŲũWӆ$ÉŦKڏŽ’WdI 6MīūyB54đg;ÐÖūÍ ßĢW'<…#X?QĻC€$ lÜG†ũwē­ÚuÞu\ŽÄHPkˇąÏũ_7õ‹ėčlÆÖXˆļgĩĸp0 ˆBˆf 1 CBtXóÁģԌW›Yýr9LéWʟfDåöŒ>}`wK 67 ˜bhšÐđQŅ4@•Ŋe ņô˜ü> tÆfF(€`īҜ]/Ÿ]XT·ņâÓB˜Ä…úĮ/[;Ųû=MLÔGJģÝþVälá›6âUžĸXŪkHf^SïĻ§žūŠKKŲác† `€éF§eGĮ˜ąwÐĐ7ïOïŧrëû īąGŦæýf\đųāsâ͕Ó:S„"'õ‹\K7ėL’Ž’$ØL˜ĸõôęO\;>ŋ{7BQžûŽnZ~iüÄ;ý†27YâΧɗn=HĘø2opËúˆ!#ũÉÝZo7›―3>īĩ°ÂÏiR]…ĐI†|+JŦTÃڒOņI_#Ô1C$Ä5$AåYšÚŒŠÞ|ïŲ›ŽŽ7ílŪÄF\Mlĸóą]!^ĶâškË04Ķ›Y> )Ę33ä ĄĘ{þę3Ęãĸp,0·6QĻ+u-ē“Sĩĩ 'I†úåĢz"ĻBĨ,Á S7Ŧ*h„“Íg_đó(9ãóŒūūā4Mņ}Ûö éÍ(óÞeVØÅŧĸū ·Þųķe^Ļ`:ŪØwéųÛOoí°AŅ€â_BāXóŪ=gGÍïß؜QÅNCĘÜũïŌyÆ€ X-Ŋ(UēÆÅææÆ†Īᧅaøžšä―. ‰\Šý“á` ‚)˜Đ[ÕbAéŋË0†QļÐnÓēE™ĮĶ9jø°ð§Ųâ†ï8p Ņ+ŨZuōûprýœÅŦ%Ôé=ą‰>MÖþ&Œc Mōl›vuV,;îøųü=›ū?ÁŊ‘Pįo}üōōÔƒį,;ΰôoãi)rðĩá‘#Åka†ŌjĩˆĐÛä ŸíœŨwPXčϘÏEęų†áUSĀpœoðÓčÎG§ö6túžÃŒ{ÐâČ–ÛÆŒ5jĈˆõ…Ž!‰c8Ąk Álð12bøĻSwSpMúüáã_įk΁c$ÅčÓ‚QŒĐ­kc"-rėˆëŸŠ%ŸâŪ=ÍâY5éâ•ČũØŋ†‹–ÄZ~Ý7t;CÎ―J č5wZS!ŅĻįĪĶ–ĀĖ'da°䐐QÃCöĮ›Fjl`ÔČĘ|ÕĪÐA weH-ið‹Ũb4-"HJWĒHˆ$ ,=ÖlŸ~wۜˆQ̆Žšu7­šiû>Õ 'GÍ\ũVÁŋp`ËŨ2­þ?]vķk: ‰ņ°ā~áak^dĘ uØ6_ūjðÉa#Į ÜwsÎüņEIB @Ķ=Z˜î^0yˁ˜Ga8ÉčgÓ2*ūåŌeąüŨ›‡ąC:øŌûĖÍx;#žW‘ļ{øŪ-z„ÅBņ‡GDD Ûqųū}ŦakÛŽ?tÔψYKWWŅ… 0Œŋ„Ō]GŽ~:PS[KSƒ%ãú‡/?˜§hDÓ@EQÖú‰cGīúÂŦđóĮš‹`’’]ĸęŽõ°đýzģ€Á_ŠjŽŋJĨ211)##ĮqÍ?…M[%%% LCHMA^N•’`ôˆ Ōr+älA#.JN-ÄÅéŊïÞšũāq™cęĄ5iĐđr-ÉĩōŌgũï>‰ĸZ’—úōõÛ đô{Fąþ Yô=ĨJĐkU›wûabŠ”P•?­7ŽWRUôîé―[wî}IËaô”g<ˆŧ]Ļ ī’’Ė‚2’Ņ‘ûåÍ­›·â^|ŪV“L-TEQ~vą‚ ĨĒ(O_`Q$ŋrë֝·ŸētÁÓęď~ūqëÉÓo*šQ–fg–QlXŠĘ”Īl5MWeĶ<ū{ũÎý'E ̧*+éÞí›wî<Ę*’Rę‚yc;LRý4‰fĶTŦ)ķXžŸ•_*gå~{uũÞýŽĒüÏO_ĪįWģfėípbN1ÁÔ"-þwûÖ―{rËĨLˆügšûžĸVÅԀ‰ß<ūwóÖÝ/9’Ú†eđîÝ~žœST”S"Ö2 â(+Ė+ĐÖÅŽŠ.É/(Й”ī/ņwØŅ<Ž/’a ĄøüâŅ―į_ rŋˆ/SjŠŋ§ˆëfOQ•ĸðîÍĮÏÓJJŠ JĨĖ/ĀÞ>eg;þsJMgUĨEųĨ2æ—(K3݋{ĸĨRQž•U\ĐŽĒ<9)§Ķ‡ęÂÏq·oދ{_%o0`uFĘį—ÏŸ>}ņ&ŊŠvÂĩ’ öÃsûÎ―”Üb}o’/îßūu;þcÆÆYYø=ïÂĢ+‹ģjŪc=Õ%YũïÝ~•–WX”S&ÅR“•ôųý›ŨwîÜyó-]߈(ÉÏ+“°§Ôų™Đ-­ëš*ĸŅÝ[·î=NĖ—2ĸū|ųRQQĄÕj5ĸ‚ RSÓRRR yõˆý þbp‚ėė\‘HčââLQÔ?Q‹ÅėGĪyóæāĀĄgÛübÄÆÉ–0ø`^Ūŋ>ÅáōųĶ0øÁņõëWVDŅÄÄäŸ+{Ą(š™™ÅÚ899â8Á―ûm88„žģ6{‚?Ķģ–žį™py–ƒKĩđ…5ø?ũZŒC^Éj ĻA*qeyĩ‚6DiAūDMŋ9šęü—îÝ―ũ Ĩ°pppЖãũxą=zÍ­o Ž—gķï=ĸøÏNēŒŧ}zôÝýü(ËNI/úŊ%J5ŧ_"Ŧ įËýCĮŪ=ļvdxИ/JÁoÂÁÁĨZ\]ŠÂPG›ãÆčˆ4ĶĻŪŠŠ’Č ZŸ_dUUÕrĶæE4 pMĩX‚SuŪ4ōŠĘ*đŦąapÛZĐ&ĀŊxyå@RVęÝû—Kī †WWId \‹SīŪœĪ)RS%•ëÜ3ĪīŠŠn'ŧ\%f+jctÆė?˜nĮ=ÉvNI*+ĒÎ3ÁŽBĶŪmKâŽZŦĻËÕ5z§wÄŧõZ,Ó0u īj IÕQ˜ĶF.@&ĐŪ–Ö ŸJxyeۑ˅bĨs‡ŽÞ}øPûoâÁoÂÁÁĨZˆ `ԑþōįÛOhJ3ÞMØ't`ðĻĻũEŌÔë;GŒ í9ø§3ŊqrŸ5wŲĘđ3ƒšķš°öˆœ’īŸgÆîš_vė! €ķ5xĮÂ- rëĄÓĶ ?{>ŌŲĒeÉ æĖð˜ģuęP?ĀÁÁýĮïAPÃ2‚ĀĘĘĖÔ7eĢ†42 ”•I3TŨVŧĮ( ‘3B ĻžƒgÎ ëÄ(’ÜB í†nėņ|ƈÞiģŨĖ‹hQŽUū;74ô~uIYH{Sļ,˃—O’ŋ!vîýŊyóænîúqå9EÕÖ=|ėŒy U3;˜a­0hŲîœî–|ĸþĐī°dþ°`\ŽšøĪ$“Ōj. >*0Âķî?gFw[*ÓÔšįėđ3]Ž46ÆYšWŠ(z;#l€"'›iÖ 'ĻŽĘ‰Ã'Górˆ ES3CCP‡ŨÐŅũ\ž~ĸmÕ×=ÂŨ›…ŠĒÔÝ1ã„ĘJÚ &™ąĄ!bij„°ĸðšį›ŲĢ„€ƒƒKĩŋ DCHC=šbDæüŒ_$%Œ ę šø<‹qKOLîeϐ@AkDërA* ąęšĸNfÖĢĩýG/ęÕįŪy·sfÏėF†N# ŽŠīįw>/\·ą™―ĻПk~ōÂS2‚m, % ™Ååpų‡ëđ‹`„ F>ėäÝÂØÎyûÉÝÍíLq 0ĨVä;yӚ)í„l°(ŸĖ$ÃĢĩŽ{’ĄagKFģ!Ēf–ÆvÞíöÜãmkˆkHĄÎ/€\9I# `Ņjq„nCxnS&w0q˜Yۀ3}ŧ^·pņŠŲttHÛÆĪyŸUĨ†€ËÞCwX›îĮïuÜöyƒ °dĸĶ ’€E“.ŽßÓopø€‘1é‚Ū;§ũ;ŧpðØqc§FÎĸŪ<ØŦIO ã8*€ūœ‹24bÕî{~݃ėLÍÃ'.qÎ:5rä˜Q#‡Ÿ|ō™5Ŋ\—ÛŒ‰ÚŧWÏ>!×Et9ĸÓ1‡!SÆúUEN{ð‘ë0$°Z ĸÐų”sÂFŒ3bÞâģr—­KĢ“N5fėČQ#^á(,Äĩ€ŪŅdĀ!JWb`LĢV1-§iR1Šcäˆë/á ôī^  u§Ķ·Ūž·ę”ü Í€iîYPŨ°ÖB›6Ą‹"6EŽ;ftØüÉbÂͧiöû‹#F­)!4ųųýĐ 88ļU-ĮïyôrpđJ—s S{/+Ï6ī@ÓŪũ.š$æ•óL<ėY.ßäØ7>ĢPebeį"‚ Ļ“ÍlsžĐï†Ó'™@tĸéģė yûV(>v^?ûæC.Ąū-ÜášÄÞ}Ėēvãø –ū‹·:“Z:LYž+0ŋT+Ēŋ>4@ Ä1ðôžķķFšv<—uÛžyóĐRCŲ:4ā7rÍŌ%ĻČĻ™ÂGšŸÞÓÞZČÚš=ĩÞÉZĀ6Š9rNØØ2,úét‡øũ*1tôEđfadÚ6―~i ģ30qéiŋ>Ԗũ ų)ŌĄąsþAšPQãŅ ũ{u}[*ÅDV.ŽÆyþĪøŋ§ĀÁaá·uφ‚âjžÉŒ&ÞÎøÏãÚýĸągĮ6Ã@EO(NĨQP2D&`bĨŽ“*r@ļč=y}ũĮmÜQgņ‡îtđūNüŒņxŽÅĪ`7Ī@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ€.þÛķÕZĢļDRû }ß/Ë2Ïs| XŨĩ”"ĩÃ0LÓÍĀ`›™RÛĀÛ°ZĪķ €ÔH-Āģ―ûŠĘû~ÎđwJÚĪLz BMŒ‘*š‹WÄšę⚎ĘÚŨ‚Áĩ ËbYuwE–"ˆ”@ HŌ{o“i·œsþ7™…müŋ ïðũyt<9ó›9ũ>>Ïũ9sæÞ3āĮvĀ}PÞ`V .BÆØn··ķķ644īīīØl6Wĸĸiô_üYŒ€Ļ―ŠĀdV–åōōōžžžĒĒĒ3gΜ:uęøņãÅÅŒ$]*҈N'b|…’_äYŽĩˆN0Ē øœu8ZČjóŲääänļaúôéSĶLIIIŅr677W›Þö›ķXĪÆÏ?ßŨaW]%DuĒH0þ{[§ĩĩ2ŌÛ‰Ö)ˆ――}Z§ØW@°†`Ē5ÎMVąĻ7Š–SkūĖvPQ'ūųĩÐũnŊQlmß<óf“­ņ›ï‹ þm,ŨKīÞ^Bï+þņrÜ{ÐĸËD-DmII‰^ŊOOO ÁĩnāïïŊÝģëååĨÍmû[īÅDÔĶÞÍíī2Œ4īą4/ûØņVŦŠ…ķڒ}ßįīØļ@­'Ž<’WÃDR“Ÿw4ïŒ"rOûŲâÂėýy+?š{õēÕgÛ%ĒĄķũŸþģ―…V§―ķšĶ ũÐéēA­­åûëēŦ―qÉbJsOŦüLnnéëHŦđ;/'ŧļÎ* Ž―ĄĐļ ũdq[Wãá낯.IÄJņ‰Cđō D-øßYĒíęęęččHHHE‘ĸ+ŒąÖßÓÓÓÜÜÜÁĀ!―ĸšB›ŦĨyŧ>xgÅg ëËO>ģôŲw?üĒš―ëāW[ž[öÆ_VœØŧyÍę5ŦŨmúĶķöð†ÅŊnø`ÍĘå_æT—ÛšuG^q Ejm>―įŦ/tr·’=>ųýË/žöQYSýëŦ6ž·þÍ[óœ ŧFÝ=ˆģųĀКĘSGŸ^úĖúŋŽmmÛ·įË5kŨŊ[ĸj…Óņþ“­ĸðí'ŸyãÄĐĶŊW<ąęƒ}5Í=%;_zsãÛŊ=Sß#\Ū8AÚÚÚ|||<==EAĸÁh4jÓÛšššððð~ũZäȅQ§ę68ŅŋŲ+nl \°õ­›QSÎŊŽyüÝ?O‰E[_|pó™0ÜÜØÔÆģXķxdãøĨGnŧ1sņ‚Āđ“ãÓiŒ—1cðŌbäĢŅŨMZĩxÂWÛĸ–xŨæ―ݟVÚÚĪPdÐ!ŒĪ›åāĄÜΐŧĸúÆÔqōá-ë}Bn4Ŧ֖n‡Ã}ØS+Íþt{[SG ąŧ†ŧÓ}›ßęÆĶy·6v:Ä(oÎ)ãQ ŪčęÕjuww§”jQŦýųŸYŽĨ°6Ŧíwđ–1ŧ*3­AÄîōïV}ZúðäØãõŌāX?ĨáPvn`ļŸ)< āЎoŊ‹0DĨ‡X2ģĶĮÆ• ÖǜÃM QÁ^:ĨęLQY“%ÁŨČ·u7æ*ŒŪxx‰ĖÎ(óðŽ>$5+:ŲMė[Ïā\‘$ÆļĘPp@€ī/ûPŪļŸgTĘøn]ōŒ1ņÃ|Ø_yĻĄHĄzQ—ðӟfýaÓ_wėŸĻ‹NMŋ9qépöwaņņÁœ1tEQ k’$9NUU/8í՞ÕŅqÆÄ€9 FšÜ gÔ;dätóރíĪ]Mļcú]ĨÜðFæĒ5wÝ6sųĘÏ·”ÝõÄà 5ŦūÝĩĮ254Ŧ'wlī§,øųÍC‡HŲ‘ųųĩņ“‡P•ĪÜ4îƒïgŸ4)Ƀčžb‡ ũôƒĨ›?ûĪcŽß˜ ĒwOHc2šŊ.fĶĪw·ū―ų―ĩ―ēpÞŽå+ĸķ§ūÅ?éŽëĶÄ X=|ˆ›Y<đgKŪ%ööq“3%ŸyiÓÖMÕæáC ósƒsBTØŒZ jcĖÛÛŧšššRŠ1ūāŨ_Ú‚VyĄĪUUcÄSŋˆ‘%‰q†―cŧá=Ũ3ēŽÄ=žrūÖRe…G―ĩé­W‘åa+ßD} ·~øĨgg ņfT–UôÐ kz å%Nŋoí Ūõƅa§ÂŌÓCUân~dÂ- Ķ:e•s$―'͟ŦJ꾉XR؂_ŪZ€§ēÂ"ÞÚ8­w,I2žėTĮŽOãœ%%­ŸƒÓ ÔĀeŊþN+PeiäĢOsŠJŠ‚Āp{{;ŒÁ`(/Ŋts3FGGQJ/9ĨĩZ­ûũïŨūþ þ·‰­(ŠÚĸâ“'OĶĨĨOÛ˅Р%ĒŽ`ôã€(ŠĨĨeœóČČpYV~\ģZ˜ÕjKą111ĨĨĨFĢŅŨŨWëq]{@ąŲlÚí ‘‘‘fģųēï ND=A~$Q i§ŠŠvC@@€vĨūZČ644455iÆĮĮ_™Í8úņŪŦ‚ $&&Ž=š1Ķ-ėÞ―ûĉ’$%%% 6ėōü>fĩcŽ­Õ†„„ ÂÏAˆZpY@Špõ/ €Ļ#Q$z―`0ÚĢֆ a€Ļ—!ļ§G>˜]ŋ鏧Öū}|ãŠöïŊmkģ F—€ĸsûpŽ―  SŧČÁ`,hOŧjŪ jFčĖ™ķ/þrķžÅ9<,ãĶa1)5ÝĘWߔī\ôŠ,ŒđŌŲiĄŒĸsī5”t9UŒ~(Ũ&ģý—° Ó‹øïĐŨ‰ĄMĩÎ G)&ÝMgš­ÎöÖN›]đRq D-RYŲđįp]PbxęĪ„ĀƒŧG@˜yĖ„ø!ãâöå6””īi5肈NtTžøÂ—ÅS:―QŊ7yýíSÍAÐû|Ū\ Æ^‘ „õŪķ^‡$KS]đ,\oBÄŋ?ƒÏ· šóõ"ÁŪN}ŅEkŊ-nĨZA_vÓîšN^ýöË=Å=ƒ6fo·ëÝ0r Ī+ĸöõå §ŽÕ7[ύŦĮŸ?r­Æ5„NÐþÎŋ\К{›Ē\ÁØf“ũŽ ÜeW-nq2íąË͚‚|Òí=TÛÞn'ũó‹4Ž‚ēŒī&ÁtŨæWæß}Ũ†JĶwwo+Û·ðÖûį/ú]i‡*,ęuŨĸîЙw,úð@ĩ^gýüõįfÎ|hóŪĒō#™wãŽUŋĨH ‚XŸũ՜ŧýęųėD_—ýųÂ3ûánĪX7―štæĖEŲU–ōƒŸßų“›7í-冷WŋōÐí7.~nSkÝÉgÏūï7/ķ9zŨšKũnšóÆo~qÜÏÃđ{Óģóï$ŧFrÖúÍė9ËÞÝfeBïq.ęƒ ĒĀ#ąTï~øŽ™ ŸûÄfm[ųÄģ-?ۃ*ö~ĩî•'gÞqßŪ3rÓąĨw?öæÆ=vQ_ķûĢ;gÏ{o{!"Ē\„ ’úšN L!ænŧbWÐņ‚Ē}|8ũļMAÝvŲ3ĀWŌJJšôĸŲŋ/‡ąÐÓptõ§Ōģ+ž™äį­o?ûŅķ]é‹™;Jxų…ÏđÞ ĄótNĐÕĸéĩŸí(:öųĮ]ÃßY{ũŊŌf›wû7fĨę0cŒûF$?2'’ŠãE…›^üà ŋ}å—sFïßþŲ–ĢšŨÞy!֑ũÉæßš2ûÔ;ÔI];ũ9þäSÞíÕóÉ-?›:įöųf7ĶR64iôOî―er’lŦō91+)þÓģ?[ö’ύSóvåî;R/ŠõâĒĀÎäT7–Wž~e؂+ÎĘŲņį“݃fh}iwYûŅrØý“Į•î>vōø‡{tþ™iÅöÓïŊúhŌ]?}gwsL`å@Ԃ‹ ·ķu!ww™'ÅeU5˖>ųþ›Ŋ=ũԒÓÅ%N*8)LĶÚÚÖ~Ģ–#ÄP/ŒĩēGtBĐðP";Úŧœņ)ÉÇŠ-Õ2BQY ™”™™œ”!t4”ĒbbũUZ* öōÆ‹‚óëĸZPZАŽŲëÞiã‡û{ũX{ÜCFĮĮ„{aĐâdus+sïOLŠ}ðMĐi#ӆ†xËvÉėo Ī#ˆsdts3… ņÕcŸ”ŒÔņ#G Šóķ”5ķæ69fß:nDĒŊŠēœT§CŠT2ŌF‡Ë=u ­ĒŨˆ‡GXÅļŽ‰’’‡øúHÃĶŪxÐŦøÅvTķtœmj=ÓaXø@ŠÉ(Ļ EýĒ`Ld§Ô&“ZŪą’Sg+;:ڐ ÚŧŧŠJĘkzpMj•‰ÝfÓjû‰Z‚"ZƒųDĐ||Ë= ū:ØiN™6,aՒÛŊý"óî[ÝĐĘúÆS)ÓpŠ OXÞ[ģoŸOG§IˆŦ-:šî;d. k,ų67ŋđ"ŋôd-š™åģäĶ›~öú_ÆĶ›ë7,üųM_7,ļojySAYn'Ō{m}íî{ïûôTWęÄŽŽæw^ząÉAæ‚{€ēó―u_ä`‘PΘĘTdþŲCw•ä–4t؜Ôõ%˜öĻÓÜđ!0dâ͙+~vĮ܅ï˜GΉ3XJN6Û$‚‘JeąŠā“ÎŅ%ø%.šcēģčxi›ĖZ>ŧémv)p·ļ0Î𧗞:­’ĘE I3v\ęŲÓE‘ƒ%ŽJSW’í?_ãŊCāTQ܇n~/ÆËCĮ―bWŽßÐÐ.ųxø˜―tS†›J‰GTdĻŽĻHE‰3nJDæ?‰wóōXņô ĩöÐðHowýĒ'Þī07ĖI6>ôęûu͊ŧ›h4ųxMY•TWGÝýĢ‚|ŨžĩŪĩÃęcJ9ĄIg u—Î ɜwÏŽ ģ·9,Ð-ðŧœĨ*enQËßxŲĄ3ųxˆ 4({xĪ/Mžæĩõcoâs%~öãƒE7öčODƒAŸžôÝQõNâáĸōęå]=ŠoˆŊĮ―ũs^Ā|VĮÔūäWÉfĸ _˜_ŊHŽkFžþnūšÅ·Ä™žĘ8öŦ…ýjĸ“( U• |]|ÝDO“‡(/n­Ŋ8gMŠĘívgíÁ―?Ɉ=zĻŌÏĮäÞScŨÚrޛ;ØõecėßJ]ĸÅ}ļĶŊíŠ~W•!Þ đޑ3­æ\ÓÕÉ1AÖúĢĨΑÃcô‚Ŧ@ƒØđČÄ ū7ü—1{_Ų[üχ„\ĮåqÆþeŽ3BäÜĢĐp qØŊöŒZˆZW0ýéŦ}ÕÔ+þúTƒ^Ô::$Ј3ĪPV’“āhū}Þ―^?݈U…rŪYĩ]åįH„I㓞mU9°ģÓŨy›'TKÝÉ\c{eÖÄ$ƒÁ8@'mœ)ģūc, ÐA'V>pœEܚ5 s‘Ũ›<ąĒöūä|ýųbWÐĸGÏųÆÅýŒZïÓ߇VBĆÂÝ[Íß&)Â[QhęÔđLU(į•ŧßX―*ŊF IĸÔóŋM r§Œ#‚DÆAŒ‰’6!"!>Ft­g~õŦ_Ũķ}Ō_[·,%ÚŦņlöó-;ŅađöÝĩ„!&čԎüåOþÎkæãß4\čŋ‰ZM?e˜(ΚŠSŽ ‘™Đ)Þî„!Ã`Ÿ­ž―æôÞýĮlŠŠũ‰ČČí‡rvínbĒÚVß,yĪfMîë,Ž(­ęŪüīŧnbfJÕĐÂ`Ï0\ôû3=~F{Iyƒ)2yú”ëÜ­åÛ[‘6-+ÐĻÛđݝ‘ïs6ĸÐáU2r=arR”ïEŌ–rmF-€õéïI›Íæ0(ĖĪ82E”Q„ģâŨ>#N~äOk'ū―táĒ_oøöýĮžDĶÕ#ƈ ėŲōá―W&,Ÿ6Zĸ7Ŧb"Øs·ÎüÓÖ;ŽļQmō}?~qØ=đ“bÓĘWą:Į2ëýŽDYęwũ‡\TËÏéįYŠČēh ›žĻŠŠQ,GÓwß}Ï"Ŋ›–·wÛķ]đ‹æķZ;ËPzVĶĐ0;Ŋ°*yF|°ŸđÝ+tüõI>FĐŪĩÍCfþŽžšģĮ•Äë23üö*Ļh3œ[:ŧ%ÞŦģĩÛĀ ųß唌œ6Į­úÐÁïDÝ5Ûó‹ü5ĩĀŽV–eI’Ð…ĘUUrJ QQųøĨ•tÔmģËjė!+~:ÝßðН7þíþeö$L1™L)ó5‡ĨŽŊÝš}óŨ9KnÞöéš§Ûękmåķ֚ÖėӏŋrWBˆ§UöC\â‚åģĨũxÆÜīeÓ#fėtJ õĩ—üÖČ5Iïg#DåJo̘rŽîhinïrŸ6;ÎÛŨ”Q}ĻžÅ™Ēp=:#uXl~U~­Ē2Ēũt3õ>Af_Ī4p„8Ĩ*“=üß0ņzoܔwŽL–e*"„Eí9ÆÁˆ`Z]Z-Y­%Göc§…ŠnÝóÐ1†.ėšýG j/2Ŧåˆzúu6ÔþéûüĨsGB㉜ŋņ†!:ŧĨąąCHjnĻíŅéõ„Q†fJiōΉũ-HŦÞöÉÆŊŋ Ŧ*=Ōēâ…{6=Y0Ōë=ÜEKUm2"Q•í +LqËžeAŨÁŋߔķėÁyzæ ãĸéÂEį†ânōn>|ķ.qP\ˆAÔi·sAŊ#ÎŪgΟ§Õn—ąNG8Cˆ\VĘæˆ3Ķ5d§“r$pþQļ›ĀU•ŠqÔ[FUÕfąQ?GC“UöáØÓËM0˜’RÓC= a7R~9ïp#p•㜠ūaÝc雖=öËįVŊ}míÎÂ:ĪÓ{†Ž˜1ÕžaíŠß­Yūt]NÂÏoņwÍđ,IÛĸð΋úK•uîä1ū:Þ^–ŋõÏ[*Jrũėk&q<2vËę%OŊ\―ęõ·ÏķĘŠÃaqįŦÏ/.xĸËŨ"a€ŅB‘.lÐĻ‘Aöm_o;sôhÎÁ_ĸ­™‡Ä™ŽdĖÉþþŧĒæ€øQþ:D…ŅÞXgŠŠĻ”sėmöīU?\Xb§Z§Â{ϕ)ēʑ†ũÖ3&zļ!ųØūo·ï<ÔÚce* ŠK 6“܃ĮĘĘÏ[9Â?ķ+øÔ&g>ôvÔ°ŲE=vīāĐUЙI2w{ėéWwîŠjē,|zÍԌĢũF{?þėëU5M’Ę_ž}_ÚØ$֍Ėß·ŦÆg>Ÿ[tžž1<ëoxĮm/oč0˜ĢžÜ&?°jīŸwDė/ßû8Ĩ°ÁAđë°ú=Úĸf3ÎĻ–†ã§ß]SÓfącbšîïkž~ŠđšĶË*ĨŽŸŽĻtÄļLænĶ HI3qwĶŠ!‰ãĶyÛŌRw|æ$vs9ɓ‹ˆ1WĀ AYYSęZŧÜýGŒe öôTîS§ÍŽĻŠģIÔäoÄŪ@\ œT•$Mž3bįĻ’ĪP|gÏ[ uaN%YfįŠUdH˘š!„UI’SäÍw.ÂSUM5N–$ĘÉäY·eõF3—$'5™PŲi—#FL49N~Y’ĻĸīŒĶ؄ĪÆ1&#ŠŠHôœÔ{:˜S•2FÃǧZË;8Ėq­ë―b‡j5ŒQî­Õqc`ī7RUĘą1":šQ•1ä6ČþũëŋĢŅÃ7!ÉĖ9rÅŅĨÁđŨ7æ=z4##CŊŨŦЊ.WpaNWÓGÛīĄŋsívŧķ%ëƊŦëã!D’ĪĶĶĶČČH˜Õ^;ВËÛÛûôéÓąąąžžž<‰(Ĩ QQQŒą ÆąŅhėęęęėė4™L‚ \]QŦålkkŦŧŧ;ĖjŊ5j­Vkyyy{{ŧĒ( ŊĩsŦ‡‡‡ķe—6éŧHŒjgäÚt†s~é3Hg'‚v‚ūūūĒ(^kQ  ”j9{Uėî*‚C„KžŅųiïU·† Yí5 ĀôöjýÁÐ`ĀĸŒ#ˆZ€ĻˆZĩQ €ÔŨŨBЀū˜=tčÅbī€+‘ģZĀj1‹–,Yōîŧïæįį·ĩĩĩļLīPÕĒU X-fĸ@Íðš ŦxØIENDŪB`‚doc/images/ifw-overview.png000066400000000000000000000752031325366651500162520ustar00rootroot00000000000000‰PNG  IHDRûi‹ŨtEXtSoftwareAdobe ImageReadyqÉe<$iTXtXML:com.adobe.xmp \[!ðvõIDATxÚėÝXWĸöqzDĀÞ **bĮ{7ą—Ļ1Æ5ÆXc‹-šä‰-&jŠ―DĢF%öÞ{AP:žŋpžĸžû,ˆ€ÔÝïįōÂŲŲŲ™3ģgÎđgvvĮ466ÖĀ ™ą ‰€Ä@â ņxH<$ ņxH<$‰€Ä@â$‰ĀXOllėĩkŨvÆđ~ýz æ”#Ž ČÃĻĻĻĄC‡4(222- öäɓtÞJ[·n3fŒ>|øp·nÝĻŽ ˜ÏîÝŧ·oß~îÜđčččø„††öëŨ/{öėĶĶĶ...‹/–‘‡ōōō233ģ°°ĻPĄÂŪ]ŧĪk(_ūžzÉóįÏ{öė9}úô4*sHHH@@@:oĻ3füôÓOjXä!•'“&y{ *TĶL™&qJ—.íęęšbŊäfĶ 82 ïÝŧ7wîÜüņæÍ›iQæãĮwîÜųÝįóōå˘˜˜$N,éMvo5,‘îÕŦWTVIð<<<Š)ŌĻQĢæÍ›K‚ɝ;·täzđgʔ)‹-’CÐÚĩkįɓG^"jŦV­$!ÉkkÖŽéææfii)]ƒŒW/ŲŋĸēeËÆŸF%ĸã?&Ožüîó LÖAxxxļ–yHJ"‹ô\ØįŸ>kÖ, SŦV-ÏŊ_ŋþá‡Jš;vlĘf+ĩ|„ ēoļŧŧgæm=iŌ$)§Pí@YžxqïÞ―e @ 6īģģ“#Į™cƌđpáÂęÕŦĩ)·lŲ"ŋýöÛĄC‡Š1ĸý·äggį+WŪČ UÄŅđD(™ļdɒ™|#Œ1âįŸĶ2NâŲŧwŊŠ;’ˆĮgjj*Ã111_}õ•Œ‘ ÞĒE‹ *č―*44ôéÓ§...ošģ™™™ĖäMÏ>zô(22ē`Á‚æææņŸõũũõęUÞžymllŌz œ>}š š‡8Pšté"ŅĮÚÚZ_ĩjUũîÝŨŽYÓēeKí3ýāā`ų[Ū\9íåęzƒRĨJĐļ_öėŲgϞāSrüøņcÞ!þģąąąōlTTTþüų-,ŌķĢ”e:uŠĘŌïS­üQþÖŽYSŠ;*ŽLš4ÉÓÓSĒÏ?ü  ‘ 6nÜøÉ'ŸäȑÃÕÕ5gΜuëÖýįŸœs`` iubP=ŽWŊžÄĸ%JČqCŅĒEsåĘĩpáBÝWíŲģGöœÜđsËüe)rĄv§·jÚī郚víęîî.Ŋ•ŋ‹-ŌžõóóëŲģgņâÅeŅēÜÆ;vėüųóÕŠU;|øpíÚĩee›7oŪ&–1;w.[ķŽ”ĪL™2j―UDD„ý”.]ZösyáōåËĩ§öíÛ·`Á‚mÛķ•/_ūpá:uNJČī~ýõŨ°°09æüå—_īļ#𯑁yóæ™Ä}āíí- Ŋ 7hÐ@Zø^―z9rذa2FÚXÕÜŋ_oþûũï—ņŌŦ‡ŌËÃÁƒKw/_ūBqäŲ;węūjéŌĨEŠ‘ūCþĘÁķtI ^WĪGziüåČķI“&Ō8+VLŽáĨŌ&ļvíš8éä)é;:tč – ĪņŋzõŠgĩFbýúõŌS”‰ãååĩuëÖĪlÏgϞõéÓ§dɒŌI§#=‚ö”t ōð§Ÿ~’g%äM˜0sēüýýëŨŊ/ųIÍ6r0$ûáŲģgmmm}}}[ĩj%R$“ļkš7lØ s“ •'OÚS™™4ėō·uëÖņOŌHãžråĘS§N…„„<þ\úé‹/ūzõJÚ[IHĩjՒ6PŌ€īąŌÖÉĻžÄÞÞ>) •ĢDi*Ĩ―͟?ŋž\bGŧví|||œœœLþïS6)Oŋ~ýēeËķzõęŊūú*44TÚüÄg+ĘĶM›nÝš%íŋ‡‡‡ XŌҜ|ļÞļqãÐĄCĩHzSÃr@ ŧšZ/EöĐÐÚŦd3J͎€LŊD‰ŌðJˆĸ”DՒßļqCkBՙlmõu-ÝöYÕ)|xîÜ95ÏĐS§Š1rĻ)ąFS‘‡r˜-)DĘa°šāæÍ›ŌõHŧŋR­ĄV]Öm)ƒ–B}ÛFæœāFj%|“þöÛoZ9kÖ,5,ōP Ëf”ļĢû*‰>~øĄö*WWWYAcŪoéôЖv1đ••UügÕÉLíâsĨråĘę|ŒÜjwŅÔÎĪ/tĀ€ęã3IŧēKĻHdũĐC‡ɀ··ũý8Ϟ=Sßf<~üxRæŽĘĢ‘.)G ·jÕęóÏ?ŨRWē+VLJ’ø4þų§$9ícAQ­ZĩÛ·oKTëÕŦĮŲY‚tĀōWeý þïę­qKEýûũW9sæTĐŊwÉAŊŸŸŸ]ŲēeUï`iiY @éž.]šôÖŲ:;;kݖRļpa___“ļ+Š$ލ;6e_­’^FýK"䈷K—.šc5j$‡ÄÚÃ>ø@VĮ˜ë[:}Š%oķHð=S#ô*Š6ėččĻ’þín“ĸ―ÍÖÖV~øðĄúZŧ„―—<þ<)s–DũĄÔ!­`sæĖųúëŊ%?5nÜX}Ž–øIČe˖8p@Š­Îa&âîÝŧr ą{ũnݑēt)đ:Q™7o^šQY‚jöüIíÂJ‰ĐŧPétŋ Ģ{ýj™ÝÜÜô^õÖÃŅø―˜nn3‰ûŪ™$ž’%KvîÜđoßūŋOdVž–,YrôčQ •@6xðā·öÓĶMSßR"##u‹ā5Ú$žÔgffæîî~ýúõ“'OķlŲRïYõ%&ÉÔš#LýÉōĶŊwIaÔĀ7ß|ĢxŦWŊž”9ëî!zd†&L9räÚĩkûõë'đmųōåņwUĄ%rĩmÛvذa’ßsįÎýgœÄ-áæģÏ>ŦQĢ†Þø|ųōŅzČZʔ)sųōåŋĐĪFJã™øÅ+)āG Š:}.Įī_~ųĨÞSšßK''§Ÿ~úIâčĘ!qŦV­$hý‘ŪóįÏŋĸþûq&Ož,HŽ9&MšôÖųKï0sæLu5Rüþ&éyår‹-$ņüöÛo’tŊ/“·_ýaëÖ­Ó§$R$—Hø­_ŋ~åʕÓb666=zôčÖ­›„žĐS§&xɛTýf͚éþhHHHR /‡>ÚW ë’~A·nÝzûömÝsŅŅŅęŧŦ2A‚?,’FÔuEŌÆ~üņĮ Đū;ggg9jíÛ·ŊīĸŦWŊNðũô§L™"§gϞژŨŊ_ŋõ·ÜĪw§wHėäKš-IÞcGGG__ßæÍ›_ŧvMžxņb“&Mž={–/_> éS;;; Ũ&qŋˆĻ8•īcĮŽÔ]ėĻíÛ·ŋ{ũŪz˜-[6ݓ·?vuu՝þ­'xD˖-ÕuÓÔ]Y]§NÜÝÝĨųmÚīéĄC‡TËöÏ?ĸtėØņĖ™3VVV)þeڔĐPĄBĐRĨ$7H‡ĨĨ ÝŊy§V7$]ĄęĪkÐŧ ÂÏÏO7ļHþSŋū˜8éŨĪw ReŠÄS°`AÉōōÖJĩ.SĶLΜ9%ęJõ:}úī‹‹ËÆSýÃÚDĖž=;þüR’B… ÕĐS§J•*đrå’ÄýŽģ•ZŧtéR­îÞļqcúôéÚlK—.­ūtĶÔŊ__&VŨbŋ|ųrÜļqÚ/Ģ'ĒM›6NNN}úôŅ>õņņIâO5@Ķbmm―~ýzéîÜđ#Mąįɓ§páÂ6l°īī\ķlŲŧ–”žŅĖė·ß~“8"ĨõęÕóôô”ÞAû™œŧté’Īí~AGýå—_5jdw-„ôGš‡Üuë֝?ūúŽģú7™ā­‹B|ø0ÕL“Ūw™hÜļņÕŦW%mėÜđSúiĐ[å˗—@ iZũz[–$Ī{åēŠÞ&ĸũųŦđđđzĻÎv&þP)P €ŒÔ>– #S§NÝū}ŧ ØÛÛ/^üM‰G=Ŧ=ŒŋĘęlTÜÝŧwËlÕ·čĨŽöíÛWý€šIÜíßšwïūpáBY‹―{ũĘÁüjÕŠI9ØŋíĮ eÏŨJŦÍß$îcfI‡2+Ų%ä$&&&_ū|€īWém7ČĖäXŌĀÜđs%Üŧw/$$Īdɒ5>ĸüsõŦ"šSæČ‘CũĒuØŽÛ>Ëģ2FËzmmmåĄÞĮUōōĀĀ@íÛÛÛûäɓr°šĸþS§NIĢęååÕĢG /ŊŌjiÉĨ„zHZR?č/þųįÁƒKŧ­ū–/ŦŽ]9*-ĸ€† "3<~üļ>\V_^•;wîņãĮKŊĄ}#XšHmīųŦ••ÃxõĢūŌSDGGËd>ņ_eīLų|<Ūâ$‰€Ä@â ņxH<€Ä@â ņxH<$‰xH<$‰€Ä@â ņ‰€Ä@â ņxH<$@â ņxH<$€Ôc‘ĨKééūeįÆž ó7ė7)ŧMŪîžÓ*äm@}`īŒĪÁ§+I;ĶąąąY·ôƒ·x†=5†ũÉÜĖrHßžō7e§`œŒ§Á§+!ņ$āÃuđäï j‹Ž-ė øMÚ}į·‹Oöz3#iðéJŌŽ…ރĢMn {~“>(3ZþJM{Ž7Ą€13øŸŪ$ípårVx“LÍĪĶ–Ïû^tLĪÔÔģv°Mt%$j*t%$j*€ŪÄČšOŪĐįïd›čJH<^S>5„ x·Ūd°‘Žĩ…ĄŪØÞŧKO>Üm0kõZï‡BÂ6\™ųAŲQė―Œ™á5øéەž0’ŪÄ`ÏÁû+cbc û͋5‰ÝtmVëŌÃ,ĖŽØ-chðéJH] ‰į-\ė ØY:RĄĀāŅā#q\đ H<$‰€Ä@â ņ+ÎÅv ņ`°ÎønûûÖĒ=w–°) ˜›`ĖĒb"V]˜ ŪĖô.ÜÎÞĘ)ýËð:ōågĮïAWķÞøáöóÓō°Ru‹vŅ›æû#ÝiYŠ9WžpnÎą$ôxnϐá’{ ­thðĸļüĩ6ķųڜĄÞ‹Óhõ_„>þ'čZĨwøFšĖ!îô˜I^7gÛü)›‰āįSCB%eÆÐ•Ī_âIÁUrÛõĘßtáÉAG}֟ņÝjmnÛīDĸ ŲLKώ:ęóG ^øŲ֊Ķ&ĶšcĪ&õðšĄ7ō]ŒĻ―fųđąē‰œĪæOk ceķ?Ö$VïŠ)[_•üsNWýwĐlma—‚ĒĘkŨ]žö ð’úÜMrOaGŽåƕÉ]+đģ’H1ŪųĐYĪ]‰Iz^đœē;ŠČ>ÓïßžŲN†%sdÔ |XqšgūFŊ#_ęþKĘ C#ƒu_"3‘YĨbÜŅÝDąą1 þm,þ0€ •Ų|iuÝsÕÐS:WÍĪž0<ęõàŦōrŧdœhđýütМÕSPΝ·~žsī§lŸZߜÖhŋü[ÐæVÍ"íeäß·Ĩ`†R uڌŪÄ$ŋŦÕĪDß\“ŸBnRL^.3‘YĨ]ņ=2‰Tiðĸš17ĩĘóqåŲ…KŦáJš·-óų[_kŧüü˜°ĻWeōÔą·rNúēßLÁ5˗žî[eæļú[ïúHŌü{þÚWĘČ WfĘɝgAĮŌRš%―ïŦ•ē;ŠĻ·!nXŊރôĸxK· ™*îÄ/oČ Þ―Á?ųpģ ī,5äÝ “;[‘ÏkŪķÍ+§]ĄÏž—ūuú˜Øč_Nvčþ+s›nĶ$kYƒïäÏ^"đ%\uab— “ 9–ųûÖĒU&HÔÖčZaēÄĮ.ūZy~Œ&‡’5Ï|ÅĨ0t%ĸ]ŧ ŲŒíLOZĮ#‰įēbčyĮ_BO*žéIĒϘˆųĮ?QqgHÅÚÉĄ$ |io唎—øūžðúQí"|ŊŽ đ+ëđv/•Ŧ†…™U Š!ļþė˜Ī(š’L—x #ôĪĸŲÅgū°6·Mžæzz4Õ ―omnũÃņ>î­ z­ēĶ–<öŪ9lō\zšß3_Ģž\rRŲÕzũûÏÉ>°âü—ßX蝓<íûW7Q§üd)îRĨSđņßęü4ä^Z/KņÝĄ.ˍ—…Ō•$kccc3m―ĸp].ųŧ°ím;KGŠŊRŋė I þ"&6záŋŸ/þ{  3ë˜Ø(yK Ģ]ˆ5QïiÂOÆĐ[ī[Ÿ*ģ“ļ‰l,ēÍnq>›UÚ\ÆÓāĮęī™itķGkŪMÓljkajbšēē|Wb–…öÝāŋāäĀG/o&1øũ­úƒŽŠ —·DÞ]Ãø§S#ãĸûŊũWūõĮ*Ô&rēÍõęދóīžŒŠÁŨm3MŌļđNË!VK0Þ•Ü 8— +•E–Û,ÍŽW]œxäÁ:ųgin-ßúŠČ˜pmØŅ&ũøú™›Zvccó0čę–ëón??5ïØĮVæķ‰ÞÞåß Š—úšŨÁĶ >ÞĨ+ɗ―x&\ŽWš–ė_"gÕ-Ũį\|ēGķŽüKÆ-Só 0ŋ“7ĩrĸĖākjîlE<ó5Vį#ĒCå_âÓÛ[9u­09§]!vr4ø0žŪ$K&_7gŊÏž—ÆÄF…ųGĮDūuzs3Ë!•“cŋ4eíĨi&&ĶIĸ`8ëŌ―„ÞÔÔLjaåüÍßī‰rØäNįû‚ >] ‰'ImÞd―ä=·žą&ąÉýÞĢÁÔÔU&d·vá+čhða„]‰Ņ}š™ēŧÖLMåwwÐāÃ8ŧ öBÐāÃāŧ öBÐāÃāŧ öBÐāÃāŧ3#ßRð[æPSđ­|ĪVWręaÖčJĖØŒ9ô,<ųéŦˆ@ö^4øHqWēčTÖčJøý•þ–yVŊĐ}ŦĖËa“įßßqŽ:€ߕðÛÛĸÝRð[æYÚĸĸ-p‡âT4ø0øŪ„Äó_ę·Ėĸš>ũ“ÝÉý-ó,ŠÛJ Á7’ŸŪ„Äó?ܜ―†z/Iúo™g~ÃķyÉßņõ·:ÛæÓ{ŠÛJ Á ó3ŒŸŪÄÏãāÛnΕŌmq)ø-óLÎŲ.'rd éÜāKWm` >]Ibý{æ/âķóĐj` hðaΉ§|Þũäïɇ[ķ\ŸÍ[ŒFxFÔþ――ĮX“ŋF8ßNFƒĢN<ĒMéaü`|uâ1áW2ĀhÐāÃĻûzĢH<ė@čŒ"ņ°Ą0ŠÄÃ>„Ā(ûzĢH<ė@čŒ"ņ°Ą0ŠÄÃ>„Ā(ûzĢH<ė@čŒ"ņ°Ą0ŠÄÃ>„Ā(ûzĢH<ė@čŒ"ņ°Ą0ŠÄÃ>„Ā(ûz=žĻ@þņ6€a‡|ž•™aïZð7ęTkfEE`ðĄ‡ŸŪÄxî>`ĖŽĖmØWOč]IÂĄÍöK3ë•ÆËðg[+šš˜Ė€ÆÖ•XÃûÔīdĸŦ~‡Î=ÞlluīFáŽĖmŲW‰|ĸÃįým„ >]IâLcccä óŒ3Š:jinãh“‹}€ą1ÂŸŪ„Ä`čW.x‰€Ä@â ņxH<$ ņxH<$‰€Ä@â$‰€Ä@â ņxH<€ÄƒäˆŽŽŽŠŠb;ôHï }ہÄc Ο?ōäIķ@ôŌG°H<$‰€ÄŒƒill,[6Îņ‰€Ä@â1ÜW  îŦEâ1(ÜW  îŦEâ ņxH<$`LļŊ0|œã$‰€ÄcļŊ AÜW‹ÄcPļŊ AÜW‹Ä@â ņxH<˜p_-`ø8ĮH<$‰Į p_-@‚ļŊ‰Į p_-@‚ļŊ‰€Ä@â ņx€1áūZĀðqސxH<$AāūZ€q_-AáūZ€q_-‰€Ä@âøĸŌð.‹-bû°^―zYYYą$WDDĒ%KØŽoßū™°Ti:ũŪ]ŧŌ)$Z+ï~ÔÄF ƒ0œÄc‡·ŋƒ`# =q ņxH<$‰€Ä@â$C`‘%JyâĉlŲēyxxčŋråJxxļ——Ũ›^xýúu™ B… YũÚ·o_PPÞČråĘđđđ%wVmÛķýóÏ?Đô Des<_ýõ€âïÛ·ïÜđsyáå˗Ϟ=›øĖwîܙ™Ũ=8880Ώ?þxņâE5,{)ÍÐAÐA$EV)há…Ož2>,,lÜļq2[sss ‹Ųģg˜ežŒ,]šī”Mæ#q{ƌډÓ+VŽYģÆĘĘJæ/ãĨ‚9ŌÏÏOfÞĶM9.Iâ69zôĻÜČreVüqĮŽÕøŧwïJ^―z%‹Ū_ŋūĖ\ŠA]`Ļė >üðÃuëÖĨz1a™OR:iŋüōËø„Œ,SĶLŌ;ˆQĢF=}ú422RJNņNbӌT8Ų Đ2+É/^žĻWŊžüUcĪ4iŌDj’„5æþýû2RĒĢĢ5jäãã#Ã7n\žxą œ;wNŠï‘#GÔÄ˗/—ZŦ†ëÖ­Ŧ-ČßßŋF?–က€jÕŠ=þ\†‡Z§NĐĘ2ÕŽYģ›7oŠ—\žxQ Ȏ1hÐ 5\žxņI“&ÅÄÄȰ”ĪvíÚjž.Č4RqeXž•YÉ@‹-dĮS%ïÖ­ÛöíÛÜRY íá7ĪäRH–]TVvėØ!ÃRĨØŨŪ]S‹š=uęTm3fÂ7€ą‘ÖCڐԚ›ę Ī=LëâŲģgÞÞÞoí š7ož`1pāĀtë Īz„zĄ^1~üø4ę 2gÅËJßÕęŅ̇TD-K~Ũ}ķH‘"Ųģg—IßRE._ūŽũrБRYÕpÛķmĨr$xStÞžyeØÉÉĐC‡[·nUOIe•]B$­·lŲō… j|đråÔ@ãÆĨ’i9rôčŅ*8*TČÖÖV*ą /X°āŧïū“Ð-ÃōŽĖJöGGGu  %ĸâ‹/$ā'ek,ZīH*ŦR†mllf͚Ĩ>ąÞžyóûïŋïîîŪņÕW_­_ŋžC@†­WŊ^ïŌAHėÐ:iBßÔA|ōÉ'oí dþ v·oß~kņí·ß&ŌAŒ1"‰„d―bÞžyņ; ^FÕAXdĄēvęÔIbûāÁƒexÆ RÕ$kkφ„„Ž^―úęÕŦÃïÜđ3räH―—.\Xķ··—<ēH>þžzxãÆ YĒ.ZīĻ6™,N;s(áÝŨŨ7,,,00PĖ“'ĩĩĩ6―ThIÖē?H –ʧŧDĐÐrðņŲgŸĐ‡ēô‡&ekȚŽ3F{X°`A9ūQã%Ûiãe'ɟ?ŋˆļļļÐ&0T;v|—B"‘6œ-[ķ;ˆK—.―KĄščÄ;NĸBķIΜ9I<™‹T#OOOĐ@+VŽĻ[c‚ƒƒ›5k6hÐ ĐĮĀįĖ™ĸåVVVo]DLLLŋ~ý\]]ĩ1ÎÎÎj@wq Î’† RēdIĐĐZMēīī|ÓˆņGJöïŲģ§6F/―‰,Bonęa‚ãĨZÓ 0`‰w͛78pāŧw}ûö-VŽXÖí d5đƒ°ČZŕ8ōÝwߙšš~ųå—šãũïßß Aƒ.]šĻ‡<ÐM܉Ó}û˖-+ š~ýúI|íš5kd“`.ÃOž**JtÔđM0`ýûũĸæ›oĪA;vŽ^ņÞ{ïĨJáëë+ģJAņôéÓĖÐA”/_>~Ąå6ƒ—Å’žýRáŠ/Ū;ūpáÂgΜ‘7Ï$î,âöíۓ>OĐÐVÔpŊ^―~øáírœG%^GĨĒĻÏkCBB&L˜ [S$ƒ “áČČHĐÜr,âįį·eË5ĖGV0‰áoÚīiRBu3xðāÏ?ĸ\†›5kķkŨŪÓ§OŦÚ ýnÁäɓ{ũî#G‰,2·å˗O:UęąsyđT™F^+sÐ^ŦŪ\3‰ûRâĻQĢĒĢĢĨ*=zóæÍz3Ũ+ŋ$*KKË:ČCŲ‘þóŸĸļđđ­]ŧVʰpáByĘÖÖvøðá ˆŋôĘ Ó,Zīč‹/ūÝC ›ĨnÝš&q'37lØ {ŨôéÓÃÃÃ[·n-U_+m"“%:ЂK"„•••ÖAČĖ%ÃŅAĪ"ÓøĶŲÜęýc‡4<žđR,""bɒ%Iĸidđ"sūđ\Ð ‰xH<$‰€Äðvķ>;wî|ýúĩIÜÍo+UŠ”· ‰ŽŽ^ŋ~}ĮŽÓhþ>>>&ĸ{ãwqöėYwww;;ŧ   }ûöБđsįŪRĨŠvšÃ‡ŨŠU‹*ošgϞ[·nY[[KëZ·n]u?,C;Į3vėØĀĀĀ/^H&hÓĶÍÏ?ĸœÖK”€õįŸŠáÝŧw§úüũÆŅ9wî\???“ļûÃýøãēĘþþþÛķmŦQ̆ŽļšfÜļqÔoxŦýûũKÄđyóͧ§§ŦŦŦDŸ]ŧveÝÕđ~ýzoŋelēÞ9žûũïKŊw_――}Ŋ^―Ôð°aÞžž:wîėāāv呙ŊZĩJ /[ķŽaÆéžAʔ)Ģ­ēŽėðáÃĸþûoj6häļīmÛķ >%ĮŸ}ö™Dœ\đrĐ1͚5ËŌ+ŧsįÎ:uę$xû-OÖK<âM‰G—•••΁G•*U*::zōäÉįϟ—ņĨK—ž6mšđđyHHČüųócbbNœ8%Õý›oūQ•^MæĖSSSGGĮ™3gŠÚģ{ũny‰ „‡‡ŧđđ͛7O†eo™={öˆ#þúë/>ūûîŧœ9s^ŧvmüøņ2į°°°æÍ›2Dlƌyóæ]·nĖžf͚ݹuÓ>īš{ũîúõëeV)Û8OŸ>ĨZ@Ï?ü ­―wt―zõj˘1‰ĪG(ZīčŨ_-݁ŒŸ0aB5.\(݇ģģó·ß~+}ÄõëŨcccĨĐ4hIÜu?ÞŧwŊüˆˆÃïŊūúJ}Ræïï?jÔĻgϞIï öÔĐSÕ= Ĩ+Vüå—_ĖĖĖdŅŌ}ÔŪ][•d͚5+WŪīķķķĩĩýþûïUie†îîîrČ-]‰tISĶLĐ\đōęÕŦĨ?’ŪJ Ö§Oooo)öącĮ,--_ŋ~ÝŊ_ŋ>øĀxëAlš‘Ú ïAŠÏvßū}‹/~ÓģuëÖՆeé•*U •áŅĢG/X°@Ÿ3gŽÔ0xņâ…T ĩkŨŠņ”ĘІŋøâ‹YģfĐaÉ=Ri"##eXŠã˗/ÕxÉz Õ]šĖžjÕŠēŦȰė-’Š$Ч$Mœ8Q oÝšUjđö*ĐâÏuŨhq―ÕėŲ짚óđsį†Š—‡;vŒ_˜Žōæ0ŌzH’žK”6óMOiÝD|ķmÛ͆å€ķuëÖjļM›6kĪa—aIr{āĀ–$ČíÛ·exãÆųōåS1HHėPýŽL#ó‘#GÔøŸþYRˆŪ_ŋūôƒd8 @â‹ŋyóæ>úHõAōÂ-Zh%;vŽ*Ɠ'OΟRãĨSū@{­ÖGHðzþüy:lít~s“.˜ãņõõ•->”ĖĄÎÖtíÚUR…îdÚU5ōöK˜ ccc#eŨŪ]§OŸVÓ 8Pâų—_~)Ãnnn:tPã%MKĨ|ôč‘ÄgĐū'OžTã%›ËS{öėiŌΉTV‰ÞęcēÜđs'R` RēÉa K`—#€š5k~úé§ŠjéBæ9cƌqãÆIH—™Kž›4iRēķĖ;wd•e'‘)Ī;Žį@üúëŊ—.]’iÏ%LȀģģó„ t§‘Bš‰øŊ}ðāīŦÚ'\’3dnŌĖJŊ!?þøciØe aÆŌÔĐSG†ĨoÜļąĪ5MÛķmåøV―|øðáÕŦW—–ĸøņãeʔ‘tĒÆũéÓgŲēeÁÁÁŌ­H “Đó@NNNŋ—ŽLâ”~ĸþûïĸöŨōBÉgrD-Hņƌ̊‘'OGGGyĘÖÖVw-ėėė$åȜe2KKËtø6ŸjĨyŨU8|ø°dõ^ęM&ïk``  lŲēE‚ģDrõQ‘ÔUÝíĢ•H4RÃnÝš%‰DŦ͊DékŨŪI:Yīh‘Tô%J 2ĪXąb‰XĶïŌĨ‹îGl*{I+RΈö*Đܧ:$;ŒäŲyTõM:9ZRŦžtéŌĢGæČ‘ƒfLâ.mlŨŪ øųųĐ$~+Íō›Úp―Ë'*WŪ|õęU•f ,øßNÔÂB;īęö/ŌæKũ$29ŠÖmųōåĨߑ‘ÖÖÖšģ’‡aaa2pûöíiÓĶéĸ I<’īėííu{éôOƒ Ū\đŌĻQĢöíÛËAxüN“Ä“I2P}đžÁōž―Đ_ŨŪ\–7XōuÓĶMĨÄÆÆJ•ŌM<ŸþđˆŽŽÖûŒOaU5uƒ”ÚOŠV­šuëV9\9rĪŦŦŦ:픠ø3QGj]tGJgΜ)‰G"‹nĩÖV\}ß^WDD„Ęû&:W.ËåØeîÜđ4s`ũ3%ZxSŊ!đäÆzGđŠíßĻ6\ĩ·š­}‚sŽßŋĻ’ĻĢŨïȀ^XŅäʕK·ĸ’áüųó'x˟ 9>ïßŋĸÚĩk%ýLŸ>―~ýúF[ ö (ÐĄC‡ïŋĸ^†‹+öÏ?ĸÕĄ{Ĩ°îŦ$ ŧŧŧKĘVÄjã?îááĄ=”ÜóĮœ?þɓ'zûƒnl?vė˜ö044T7Ķč*QĒÄÓ§OŸ?RĪH―geđÔ[ʝ;wōå˧7eŨŪ]/ÅĄ™€$ęÖ­ÛŽYģâũôô}:þĄēQ1īß\ÖýØU‚ÎŨ_-ąC"ÂÔĐSį͛ŨŠU+kkkÉŋ=zôpuu•iš7o.•ŧC‡Rŋ‹/ŪýbĄún—ä‰ ™ČŦĒĢĢe†Ïž=“ī!UîÜđęĪĒķP IR5íííGŒáååĩvíÚ &žxņB2VĮŽ?üðÃø…TĪĢFŌ~ŨGėiß|óM˖-Õ!‚‘ĻOĶMâ>œbkSĘūT·n]ĐÖUŠTɛ7ŊĪ4íĐĶM›ĘJŅð€Þąâš5k$ŲȑĪDé8~üņGŋ3gÎTŨƒ–+WNK?Ú {Ðý@^íÚ9Ð]đråīiÓš5kÖŊ_?5~õęÕ'N\ļpĄô)ÞÞÞÚÕšóîîîŠÍ—Æ_: iųՁnõęÕåĐøÓkĨ’nĀ€{ũî•.ĖŲŲyėØąŌÉęH\›3gŽQŋŨiw>`ŅĒE―zõJüä[ƒä+%Ô~199rDbMš'óŋđ2-9Â\ēdIßū} xĨgđĸūîõ7ÆC:ˆĖųær'ŅLáûïŋŨâ?Hul‚Œ5eʔ#GŽ”/_ūlŲēl H<ĐÏŅŅqåʕ[†ącĮ†……%~Ų kiŅĒEü(‰'ؚšfxÔ077'yӏô q ņxH<$‰€Ä@â$Cķw™Xēd ›ßĒE‹ØHOĶąąąi4눈#؈įϟ—•­ZĩŠQU+++öt‰;yōĪī–žžžt†œx2 Ūã$‰€ÄcĒĢĢĢĒĒØ=Ō;HÁv ņˆóįϟÐđaÃŲ―{ũī(ÆO?ý”7oÞ īoßūE‹’ĀJ—.}ïÞ―Ėžé6oÞÜŪ]ŧI“&7Ū[·nRfŲb^æ+VL:U{øäÉ*9pŽČ`2ķmÛķĸ~mLhhčdāėŲģ~~~đsįVãÕ4ï―ũ^Š—aïÞ―T3ũôô|úôéÁƒ_ū|™ųOóījÕjöėŲj8::zÖŽYē ŨŪ]ģąąÉĻ"uíÚ588X GFFNœ8qá…Ôs€Ä;•`Nœ8aee%ÃwÂÃÃģgÏ.™cĮŽ=zôPSJ ‰Ÿx|||ōįÏŊ#=2ŦÛ·o[ZZ–(QÂÔÔ4ÁiÖ­['ÛĩkũĮĻ1ąąą=277ŨL◌trr*RĪˆîø   ™ŌÞÞ^†ïÜđ&QIō‡ĩĩĩ­­­6YTTTHHˆ 8::j%đwïžžžPĄB...ڔōZ 6q$4ÜšuKæĢ>§K„”á‹/ūXŧvíž={ZīhĄû”„Hݒ艎Ģ6~‚yđž0&&FũÓ4ݍséŌ%Ųþ)˜m‚sð.øT Č`*Tș3§Īíúũŋĸþ[þvėØQþnßū]|ōä‰tüfffõęÕScŽ?.Ŋ•þĩbŊyōäĐ_ŋþýûũõĀïŋĸž/_>RĨJ•,YōM—ØKŨ+ ( ‘DĒûPrŒĖ_–"Ë*Zīhđråtg%eðöö~øða­ZĩŠ/.‹›5k–̆ ę.åįŸ–‘UŦVU%—H‘Š+&ó”-Оysí 2åðáÃ>ėææVķlŲ.]š$q{Ęôĩ‡?þøĢŽ{õęÕe|ßū}_ŋ~­ÆKš”TtęÔĐÚĩkKeĘ—/ŋyófÝYIžëÜđģĪ7)ģü=zīĪRÝÕ)S͌žPŠ'[ _ŋ~jžĪÆÉ“'ËĀwß}ŨēeK™§gœ•+WŠ ž>}úá‡JČSģ1b„žûÚle‰ēšwïŪ6Ž,E’ĻÞ:JLlÔĻû<ą2š 7SĶLQÝÝÝåáĩkŨ$ēļļļČąūŒ\―zĩŒŽ\đ皿úõëvvvЃœ1cFãÆeXŌ†ôčōŽš–E^niiŲķmÛÁƒËS2FæöėŲģøX°`ÞjŌΉ,K–(Ã9räøįŸÔŽŽŽÖÖÖ^^^ČĪkŊS§Ž<ĨÎâHÓæ#yHÆLŸ>]†%jHŲdú>úHæĐžŠTВZŲsįÎÉÃŌĨK˜ĨT’ >ýôÓøÅ^žxņÐĄCõFJ9räˆþöÛoeËĻU–õ’‰ŧuëĶ­”―――5ɑjĖÕŦW%ĖíÛ·O=”H$oÄO?ýΊ,E•āĨž=xð ,HBžz(Ïj Õ-ÕÆ{öėĐ[žWŊ^Éz͟?_Í6$$äã?nÝšĩ6AÝšu%)ĘÛ­‘Ävũî]ݙŒ?^mFIGâ2ÞĒE‹Īƒ—Ģv~ð⁠Ëņ― ŨŽYSË‘áþýûËðĻQĢÔKĪį–‡TcbbÔđ“å˗k‰GLžÔ―G Ŧ&zF=xðāŋO™2åÞ―{’!nÞž™7oއĘ߯Ņ}‰Þ5đ•+WÖ}()$GŽ—.]’’—-[V%ƒüýýÕ1ņŊÎŅ―ĮÖÖö­ŋjS·nÝ &Ļ‹cZĩj5bÄÉvęĐûũïKļŅ.{RŪ_ŋŪ]Ԝ3gN―KģģeË&Đ(((H}fWīhŅø >|X ,(q˘1ēô>}úôîÝ[斔ũÚŨŨWÞîøāŊŋþŌĘ6Ũ}6_ū|@ũïßŊ.Zĸí·ßdqė5‰ȒĪ3“ĻqöėŲC‡IøPýīš$Ybú–VõęÕÕĩ;Z?-ŅDš^ÝųĻëu4ĄĄĄŲģgWÃ*dĻóo"]ūĪŦ:”/_ÞÏÏoóæÍ}ûöU‹8p`•*Ut'Öë˜-,,ô"T·nÝæÏŸŋf͚îÝŧ_ļpÁÅÅĨuëÖšSΚ5Kē…îŦt#N"_žŌČÚĐ\"ĨĻ_|ņ…v°:Å5iŌ$―—ĻmhũÕņø3üũÔwÜ&’ĨĮ?#cīïîîūqãF F ,Í5cÆ í[u‰Í’āl_$).]šT*‰ĪąĢGĘ0ŧ @âÉęĒ―æHAâŲķmÛÓ§O4h zeSSÓÆ/_ū\ž2ŅųHK*›››Û;wĪ›Oü eÉLjøęÕŦ*žĩ0ōéÂwïÞ-ýŦ<,UŠÔíÛ·%šôęÕ+Y+%ÓKâY·núæđ bråĘåääôâŋ’%K6oÞ<ĩķáØąc%0IdTŸUɚ>|X/QézþüđÞũÃeŒls•%;^ŧvMï|Lü1… š6mÚ'Ÿ|Rđråf͚Åĸ NïgŊåå2õοƕ+ņOüčR§ŊĪīëŨŊ—ĮWÖģĻĻ(ĐTzg‘!ļŽ'p_-ĪJâ1‰ŧČÃäĸ>ŌRĪ5‰ŧĶÄ䉧kŨŪōw„ wïÞÕÎXČd*Ģh&MšĪū-UTýÖN‚ cäȑkŨŪõũũW=īĖG]îĢ>ĻRWIĸðÃڕ=2‹[·n%ūRōōråĘÝļqCB›IÜIĸ·Ý13S_ø=zīŊŊŊūiÓ&Ýïi'—ÄÄoŋýöÓO?Uwö•€(…T·ĄHPLLŒú QģråJmãŋĸþûŋþúŦîģŊ^―Zģf:MĨGŌ•$$???―ņ’·$ÂęŽųāƒԅęYå+VīiÓ&‘U“ˆ#lÞžyÕŠU}úôaÉBļŊV&ÂÅÛïîôéÓÚSS_Všßxzöė™úžÃÞÞ^ũ{ã’cŠWŊnũ —‡‡‡ŧŧ{ķlŲLâ~ĐEûŪ–t·Ųģg—ņŊSßŊYģĶ:%ЧB… jŅēíüAŧvíī/‚uęÔI”B–-[V8ŲļqĢîwĩΝ;Îßĸ―zĄ———îø€€™ęËeé2[õ+É/^žÐūŦ%ģM|‹%øítɅsæĖQÃ?ĸüģlœÃ‡Ŧo™IšZļpĄöíôžyóV­ZõōŽlYsóæMí‹]ÞÞÞ#FŒxþüđ<”xŨļqãáÇŦgWŊ^―oßūĀĀ@õIŌ*]šī$-―R=yōDÖB}·\fĻū™Õ AƒŠŲĘS’ąŽûíô·äĨK—äŊUŦ{JÖ"―ƒôl‡Ė€b€ĖĒwïÞÛķm“ŒĒû'—>účöíە*UŌýäÔÖÖvïÞ―óæÍÛ°aÃ$ÖHnhذĄú KԃtœŌC·nÝzÜļq§NrsskŅĒÅäɓž^DĶYĩj՝;wüüüd‰%K–ėÚĩŦvUŠĪ%yV:æe˖I 9Č’ŠUŦĶ&n8$$DýæēžîÝŧoÚīIúöíŦ;ÞÉÉéØącģgϖg%ˆČkŦTĐŌĪI5ų+åOp†š$ č]·$d›Œ3ĶĸþVVV}úô)X°āÔĐSĨØē2―î).kkëĩkŨŽ?^ō‡Ä‘ōåËïŲģ§D‰Úi•;wJbŦWŊ^XX˜lu…ē ŋþúkŲōēâō–É4ōvĻO.tK%o‡Ä/Ųō’VÛīióÝwßÉ4ýõŨþóÉ=Ŋ^―’íÐŦW/õÓЧ§g‚+.ŅMbî!CØS€”1Mέ•‘ 3g΄‡‡Ŧ/ŅČ%[čýJuf&ŅJ‚Ô­[·ī/Ģ!K8zôĻdk9baSd8Ūã€,`éŌĨ-[ķ$î)ƧZŲĐK…tģ‰'pš@ŲģgÏÐĄCFåááÁÉrļā!óā:Æ(6îöŸÚÏ3ffáááæææüâ@âx Ū\$LâîŦĨ~Õ]Ō;HÁv ņîŦHũÕ"ņxH<$0&üæ20|œã$‰€ÄcļŊ AÜW‹ÄcPļŊ AÜW‹Ä@â ņxH<˜p_-`ø8ĮH<$‰Į p_-@‚ļŊ‰Į p_-@‚ļŊ‰€Äz,ØRWllĖӐ{ĸ]{øōúģW>Ï_û†ų…DDF‡…FĮšÄZš[[šYÛZ:ØY::Ųæuą+˜ÓŪP!ĮŌ…r”‘6 €ĖšrLbžļtáɞώÝ~~Z’M"GF‡ËŋŨ‘/Ÿ›øþtUũ){+§’9Ŧ—Î]ģlî:’Ø°H<2EÐđųėÄqŸ§|·…ųkã­-ė f/]ÐŅ=―kŪl…sØäq°vķ6Ïfc™ÍĖÔ<<*4:&24*äe˜ß‹°'/B?ūãxÅũ卐ˆgm—2“Ü؊T-ÔĶjÁÖŪNØÔÞũՐÁáÏũÝ]ūĸÞrĸW>jŒEķ2đë”ËS·dÎę’u$Ų$wž1ąŅ/]ũ?&ĸŪøz­ÆČîÞЭW­Ēl,ėŲōH<ŌÃӐ{[oĖ;ü`mdtx\ÐąŊZ°UĩBmËäŪeaf•ZK‰ˆŧōô‰‡›Îøn ‹zĨU―bķ(õi›<ž H<ŌJ@čĢ Wfšĸ{LėŋŋĻæęTĄaņŦlcma—v ‹ 9æģaϝÅ/ËCKsëũŠõl[fļ―•3ï€ÔúŨõy[oĖ‹ˆ“‡åōÖoå>ītۚéY†ËOũoš6ëšĸQÎf•Ģ―Į˜úÅz˜›r1"€ÔpÅïāog†û…Ü—áâ.U:•ïžŦFFæšĸ‘Õ&Þ{qA† į(Û·ĘErxð ņ·ččh،kÂÐDD‡­š0aϝÅ2œÃ&OŨ “Ŧ~ßÔÄ4cKsøÁÚß/M ó77ĩxŋėˆ–îC8؃Ė)**ĘÔÔÔÜܜMAâ1gΜ ũööfSøūž1ũXïG/oĘp=Ũî]*LēģtĖ<Å ‰X~nėQŸõ2\ÂĨĘïÅ\ҌLččŅĢÖÖ֕*UbSd8î2 'n™ļ§ąÄk—áĩV}\yV͊;&ĸþVĄó€j >ó^šÍ*Į­į§ÆíŠóŲ Þ8$IĩųÚėyĮz‡G―.™ģÚŨzæk”i‹ZĐ@óЍöš:y…ųO?ðþĄûkxûxžELlô’ģ#Ũ]ž&à Ü>[wĢĢMîL^æœv…&ž·ĩvŅÎŅ1‘‹N Þvc>ï#€ÄâΓƒÔuĘ]*|ÕËës3Ë,Qr 3ŦOŠĖmSús^}qŌï—Ķðn ņH@ŽIŽÄĢ>ëMMÍúWýąyɁYŦüĶ&Ķí=ÆôŽ8C†ĸš>ũÏŦĸá=ð?­ßÕ –ž―ûöŊqqgūwáöYwEöÜYžäėH“ļÓTY.·H;œãðïI‰;2зĘÜ,wLâ.?ęQņkX}aâißŋxsxüëŒï6uáKįōjédkÔĻxŸf%ČĀO'Ļ_g`ԞßYpr Ôĸũķäƒ f―:—Ÿč•ŋYDt؜Ģ=C"^ðF ņÆ+*&bîąÞaQ!nΕzÄ]ók8M›Đų€j?ås(þüĩï/§?‹5á‚E€ÄƒwÅv@–ģöŌÔ‚ŪÚ[9 ņþÍÂĖĘĀÖÎÆ"Û§Õ‘õ:ãŧM}åȀãŠĻ(é#Ø$qþüų“'OēĩÜz~jĮÍ2ðqåïmóä:ÎQķ›įŋ—(­đøÕóŨyӑþĪw>‚íp·aĀ(;c"Ôg=5‹tĻ\ ĨŊi·Nüóįuĸc‹ÏŽøĒÖę7MõęÎóÓĸ]{|; ôQhdphT°đĐĨEķlV9rÚĖįPž cébÎ ïd@â`°þūĩ(î.ĄÎÝ*L5ė5551í]éûą;ë^xžûÔÃŋŠüŸxũüĩ›N?üëNĀŲ˜Ø·ô`enSÂĨjåÍŦ~ßÞʙАxd^/ßmšö― tð'ĄĮāŨ7ŸCņ–îƒĸžúŸß/MöĘßDÝ:ãšĸ‘m7æK Ō.jÎm_ī˜“gėî.vēYå°ĩtˆŠ‰‹|ð4äÞãāÛwÎ…ų_ņ;(ĸV\_)ӖîC]*PĢ€ĖhóĩYĄ‘Á…s”­įÚÍHVđEĐÁûî.“āēũîRW'ÏU&Üz~Ę$î PŲ|xõęÕ5j888ĪĘ ƒƒƒ;VŠTĐ"Eаy‘QV_œīíÆ|7įJ“ė0ŠŽđ̆_Č}SÓÚE;w(ũeRNę$îIðįŋžðd Č^jp_ dw§ŽŅAĪļƒ(YēdŅĒEŲžiÄ|ŌĪIl…ý?öî.Šúĸ8{pß§Ü ‡Ļrˆũm}Ņ4Ŋ43M+KÓGą1r ë0ģ—ëx•qgïåĮ’ŋTŦ•æõÁxßejĩH™˜V߉”­™e‰Z G xÓuã0 ߎÐÐļ™xÞüĩ\.CݰĢDxüo_ÍŌčb(Ô$ô”Ôäþ/nö­ü 4oeā4ÎgIˆÓ5Ų)döQŸŌšjĩĶĖõm7ŧІ%$Ũ’ĮfGļõß gŸð·úvāF]+ÍÜ ęĪĒi‡œŅXÔ~“ÏšÅV͞ËLYoK0Ð6AéĻģé‘īēędĩHü}ÂGUâ2þ+æžÜa†úI™šVãļC!ėĨïđšų^ūĸەœĢŠZ*uK`ĸô -^w§WÂ~ŧģųHŌ†Ŧđ§ÂOũ›Ųý­{hbâ‘Õ2}aûČTžžĻģ깇s°4›Šō·Zw°Ũ ô<#™\úóõOOßý†æÝ,üßŲfkäĶnMŦņÅQދFtšK3ĄÎĢÖþųڍgQ3y æëžÚų̧_Åž“Uvëó ŊNėšrĻįŧ; h ÐzpB€–†žĪĪĪĖĖĖVýC5uëĸšČĝæ-p\ÝâÎĢžmz)æ}ÚõGĩ4ÃÞØ3bĀĐþnoŌ[į]ŨÂw__*ŨÂoJx4,ôTˆŠWžvýÁÞėÐïĮtųDĀcÁ‘īŌJŪ6šGĐ4O[ ;ĩÛýVŅü‰”Ŋw&,DčP.@9Ą§•ūÞĒļóų…Wï—'™čZ-čý‹ŦđŊڎƒžÐˆĮã+ūāßwsĨTVįbîwĸhLvĪĘÞÕąę@/yN7ŨģýúōûgÓv ųړü>Įö€ÄĀýÐ#’TŊųsÅs}ŧð~‡ÛĩWįA lábÖEq0ąīvύ•/•ģ™7ŧj)Äi„T.ŲvyÆéŧßPúÖq6ķ/åžFa”z”øõ–\KNïõ)@ëZ,î{HÍã#Ėë,RËõpýVĀZšŲ—øŲõž?°q ņp9ôüšøy|Îq!_gnÏ]ķ,9ívw§WGy/äiņÔaah1hah‘ØXKýÝÞęųŪ\.Û;―Ļ:@Ëá[-凞–―u+ĸÂ‘Ī4óvāOË ĀŦØŋ›YP•Q'ŋĀ#$æŸKk˜ųNÖ=õŸĸĻ]ÚCŨ§‘.f]Ø[Kš.Ï,KžSõŋËģ>éwHMr$(-ôTŠK·ĮÕ3Č}jO—qŽĘ-‰s(>՘跊ÕĐĨ%<á{Á_}|ŠwRáĨÓwŋŅäƒô ņ§ÄĮ7ý쟟Ÿ@ PįÛīRčŲu-žī恭ąû„Ū( Mfiā8Éĸóoâfļĩ:Ôy”Æžƒ‰8E$5đæŅ“ūĐÛmš{ƒ.ðų|‰Dōžãp·8îRæ~Oïïuú( ŨÛuüđ{?ĨĮýšøSŧ}@âÖëŅĢënó$ŅŅŅnnnîîÏ·Įą\KūëZ8Íôu}ÝÝĒŨc­ĪępŌú莃ĖyÝ[hÉï/x°fKĮPįQ#:ÍÓēzŧāiņÞð[qfčųô]a^°â'{ę ŋÕP2EÜņððxÞûÆÝĸí^ÉUjŌcš|ÂŌļģęÜðĢw6+%îī--- -Û+ĘÝ" Ða˜\.;–žÛëã9–ü%M_îðūО Ÿ~äíu™e7Õgyhah‘8PWÃ:ÎĒéŌ―eĩųØĘxØwîFß+đŠ-Ðėņ6KG *ëW,Rkp·čhÝC*Ŧû3c/64$ĮōGÚ·4íåōšąŪ%G N**­yðϋ OðŊ™3Bķ9SáR5^$VëŨþ š"ņ ņ°;îÔJŠrOŌĖ`i,„:Ymã‹Ģ―?žāęíúĢNZHWhāg7„fŪæžÂh ņ°2î+9'hėø gF&­äjĢųx”JËuĩHÓÎ`(^ŽĮ úļ#Ũ’'EŅL€ýKė =ĄĮ—ËeĖÅ}7WJdbWsßËũ‹ÉŽTŲŧ:ž€3õæk[Ÿxî•\­•TŌhc@â`SÜ!9åw*D%šBsöe ģ.Ĩ7˜‹biíÞËUūTÎfޜ)9]+kCįÂŠŽŒŌ›­Cą <ßk†@ĩqGŦáĖ4õī ðØý&$ĖkĐUĩ7ïJÓôŌkؐxXwHnÅ]­†SŽģ}dš;å―§ÅS‡…ĄÅ …ĄEâRí9›Õ ˜ó0›!Āó·ZϧēēRđq‡äWÜĢi;#7ŒÏŦtģĸOLöĄ‚ŠŒ:Đø!1ĸžXZÃĖwēîĐŊmōž -Ðą1t qɁŲ„Ą M‹T}$îŦĐĐņōōrqqQâcæWĶ7$Žœ$ŌŲĖŧ%{ÏĖ=PTÍĖOô[Å―ÔŌ–Ž4-ŪĘÆP ņī.kkkĨ?fĨļ”Ķæúķ^hž™~;š>a(žöãP=æüÞšB 4OW ŊÕð;8 ĀóÂg<ŠĮėķĒ'4äFz;œī>:ë`ą2ö5Yōû ŽŲŌĀ1ÔyԈNóļ1Š Ú]šJdbl5H<,#“Kĸĸ,íÏĨVRđęÜðĖēD•/ å­Ģw6ß|pvIĸßp°>āxâ‘H$§OŸūuë–T*uss:tĻĐĐ)V9Ļ›F–ģýđš―NâŽ- -ŌßÎT ó}–Ŋƒ }žûŖŦOŒ šwïÞWŊ^ Ĩ™‚‚‚}ûöąũéTTTüõŨ_ĻWŪb>„Ļ—ąý‰DgĀ"ĩ*‘ĪZ ŧ|)Ŋ/ėßŋ}Aspó3žŠŠŠŨ_"Ž——sMϞ=YýŒâââēēēzõꅒå$=ŦÚĘʇĩ…ĖŅVXŠN**­y ļ(ā ‡v˜ÞÞÜ/îþ‘ËũSÕR5^$ĻŪ{HS}mcl5č WŪ\ÉĖĖD_ÐôÄC5ýÚkŊ)Ęš1ĐT‘ l°fÍwwwš~ÛķmtGąX,—ËŨ­[wāĀŠÏÚÚÚ[ķlđđđ?þøcqqqjjŠD"177ßļq#ÝŦþeĻšú“O>đwŊþ8rÆÆÆtw{{{š_štiũîÝwėØAótĮ7ß|sÚīiĖ’œ9sfýúõôøôP+WŪôũũgnOáÖ­[ų|~iiéôéÓ'L˜N pþüų!C†ÐFûÍ7ß:tHGG‡þî-Z„Rf5K}‡‚ĘŒžŠTË@'Ųŋ~@4Ę{Ņ+æh5ˆyݟãŊãŒßĘĀėnĄoĄxū0nÜļč ûũï‰D2™ė‹/ūx–ū°aÃKKË&}ÁÄÄdíÚĩĘí ‹/~l_ĻĐĐ0`ú‚Ķ$*š>účąĸëÓO?ĩķķ>vėÍ'''SÝ\ļpA__ĸÁƒī=œ8qBWWũΝ;}ûöĨj;~ü8Ýlɒ%‘‘‘ĢGĶ0ī|ųō“'OŌĸĨëOŸ>ýÎ;ïÐ@ó3fĖ ÛīiÍĮĮĮSÞšxņ"ĮËĘĘĒ­ˆnCKÕË/ŋŦ‰'nÞžđI aRpaaaã*Œ‰‰éÚĩëģ?ōÝŧĸÚÓ"55ÕĢÁõëŨ_éŌ%Ÿæú–ŊŊorrēY#ۛ[O|ūâý„Bŋ~ýŽ;öÃ?°wMåää`;ĪļãeŠUÚð Ž~"a^ģ°H­įnŅe™\jcč‚S°Đs_ ‹L_ļqãúOŦëÜđóĻQĢFŽI‰„đĶĒĒĒžžœfĶN:þ|&ŅSŽŠŠĒPüėœ••ĩgÏf~˖-!!!ú žzõj™LFŨGFF3ûØ?É|°dɒôôtæbbbâĢ…Û˜‹‹KllŽbsbvþ'”īLLLXšš233oÞžÉ|™ ~vƒiš{‚ÕÏĒŧÓČQÞ Õä‹9Z ZZ$ÎIRaýÞ~Žšc{QŦū@/eč ėxoÉÕ'ķ`ÁŠ-Š*hĄPhddītéRSSÓéÓ§ĸüóÏãĮ§ĪïęęzðāAæƒĘŽ;ZYY)îÞøt/úŋĖüØąc‹ŠŠFMo‚ƒƒŨŊ_Ï\ŋlŲēmÛķŅæ$—Ë―――Gĩ P|,ĐÕð]ŽVÃÎ:;vė EŠŽŽÔjØWnŊĖíĩĩĩ·W,UïÞ―9BD[ėōåËiãĄøokkŧsįN–ƝĪĪĪîÝŧ〧Œ`ĮáŧŊ}šRtųAå=[#7ö>‘W;/t‹Î:XP•Q'UÍéŸī:6†ŪĄÎ̜L;sĐH˜ÃųÛÁöŌÂūPVVFŊīûÂîÝŧŅ8GkĒíĸęĪýõûŽm™j ÍĶn—‘‘ĄQ•ÔJkJwĖĖ˰*|~~$―‰ÞņÃq>K0ÐtŦ)ŧđäũšBƒ­Ŋ$ëô8öėŠëʧGÖïaóÓØBv-đĶõöŪ)-ŸeÔũ…qį úŧ―IÓ?3öâĖØðĻčŽúCžøÛ á^Ü@âÄÍä8ĖLŊ]YmþÅôÝ hL,­đÐP=\Æb4xZģģóÖ­[1ˆ;­DČŨyĐÃ{4säÎF|Ė]ĖØS).ą5róģ„Ņ@_$žÖ,>ŋņîf€ļĢtÝß2Õģ)ŪÎ9›†—ÂßĪrÉДí43Äó/Úč €Äˆ;ė§'4ŲĐþhũû?įØIŋá…]HĸųAå=c]‹ÞŪ0H<€ļÃýÝ';™vŪ•TîšķĢÕuMü?­†ŸýS ƀ ņâGxÂĐÝūāņø—ïĸŸsĒáßþĒBTlkė>Ā} F‰w8ÅÃ2ð%Ïé4ģãƜâę ˆÆJ)Š=qwÍžÞuEa O‹œK\ĖšTŠK·DOÅïķ4S­ĪōëËïËåē§þv8Î2úúŠ­^―úîuøða âNëōuf…~g mšV’ðMÜlđ–cĒiū―2ŊĻ*ÛLŊÝdĸ5 ôPÂë*†āÅėŲģgĈ%Ɲøøø&Ũøųų uūMŦjgÔþƒîߎûkBTÖ3}Û ū(-Íq,yKLö!ĸ~Č6c] ú ņ(ÓŌĨK{ôčąmÛ6>Ÿ_XXøîŧïNš4‰ŪŋråƊ+„BĄX,ÖÖÖÞ―{7]üã?ĶL™BĸwÝšuVVVüqrr2ÝĶķķv͚5:uzŌ_‰ŒŒÜąc=Ž\.§ûzxԟ ĪššzΜ9yyyôŪŪŪĖ)|5H$jrÍĢ'}S·Ûī6ÛþSŧ}ąãƜãÉ_ xBœoKCPÖŲ{Ģþ’ã}—uķé…A_ÐØū drUxcŸýŦ—ÉÕ@ßū}™™É“'3†ŠŠæiÚ§OŠošïÕŦWAAs›’’’&ũbĪĶĶ23)))Çor›?ü0==f.]š4zôhæOÐ]BCCe2ÍO:ußū}ĖĢĒnjŒŒŪ^―ŠƒóÂk*##ãĉĨĨĨrhąĢw63+bũõe2đ Âmņ9'&ĸjGŦûÛ+s5įYӋ SäjēöŽąąąGŽĄ @ äææ>éņoÝšõßĸþ—Ïĸ;hR`/..ÎÎÎĶ*o80˜Ėģ Ðƒý—•z>0Ō1ĸ.~~LöĄüĘô{ėī4p°pĖow6íŧđŠfÂf„lįó5ï 43sæĖÖč t/Nö$5ĒĻđÆwïÞMõ·qãFŠË0a\áįŸ>zôhxxļ——ýŊaÆ=éņémÁæÍ›_cdd”žžNqŠņ•mŋŋBúëÛ~Ē…ý—ŅïĪ—^ ĸ―ßہ†aXļĄVRđãƜØėà +úõĐÝÖ#î /4é Í|JÏš*1ÏČŲŲyýúõšĢĒǚ„’]ŧvmÚīĐK—.TÖ555ÍbÁÝݝķģF„B!=ō―{ũß,--3ã†Ŋ·”ȧ]ĸ•ƒÏļ˜ųT‰Ë6E―ĩ9ú­’š\ ÛĨņĸޟâĮįþvā&ÄôJ<ÏØ<==5­/ ņĻ)‘HtîÜ9&\ÝšuËÉɉÉāĖ·đLŽ9{ö,ÍPYōÉ'úúúOzī>úhöėŲyyyĖ푐@3šššÛ·ognÉąĘFčQ"C—ˆ'Þ> îwĸčĒ“=§lKk12lT!*Ų™°pÕđá•ĶzÖ {ĸ2žã Ŧû;w˜ÛP :sæŒĒ/4s~õyóæÍš5ëŅūÄáū øVKŦWŊŋüÐļ()ĮXYYņxžĢGŪ^―šęĒĖ‚ ˜ïVWŪ\IEŽĢĢģhŅĒ+VÐtïÞ―Tî~øĄâ'ˆŠGöņņaŲßßíÚĩóįϧm€.víڕþ(ÍlÜļq͚5#GŽ}úôųöÛoMMM9zīðõ–ē6ZūÎxßeAŽÃū‹ŸŸUvkÏõeĮ“ŋÖqö·É:=Œ+TŨ=<™ēíäÝm5ut1Øqø”€ĩÆš–ķũ…Å‹7î ŋüōËģô…uëÖ=Ú6lØÐļ/|ũÝwë m§’]F&í·Ķéö‘ĐÚXjMđk bV.™\zþÞOoŊ-Ŋ- ‹&šV―]Įũiĸš―ą'GmåUĪžOßu1}OĨļDŦá8“ŧŪô·Šņ°|zdý/›~[ˆ"Ášj­·‹XÐfðIrņy‚îSzđŽ?“öýɔm%5đĮ’ŋĪ–AþvC|lûŧšųðxøæZ-ä>įØÂhæK‡W:ÎĄ„*äë`|xĄžBG ũr‡ũ‡xž}%įøđôŸnį_L-ŽĢû?3Ō1w·ėfgėáhŌÉÎØÝXŨŌHĮBĀĮ^ą­K$ĐŪ‘TÖÔUVeæ>LĄŽ“V’P\ĢeÐ iÁŽÃ‘uxĄž€Ŋâ4‚þ•Ö<ˆÏ9–w*đ0šR\z=ïú‡ņQ9>Oāfāo7$Øé[#7  ô@‹˜ëÛō˜FĸĪrIFéõôŌëũËï<ĻžWRS!*ĐŠ+“ËqTV§'44Ōą0Ó·mgäę`âåjîëiLWbdx@sCOhh(~€Ð<ĄŧE7ú‡Ą@⇁@P[[‹ÄH<ĀeŽŽŽhøå* ņ ņ ņ ņ ņ ņ ņ ņ ņ ņ ņ ņī ž\.oûŋ:iŋ5†žEzšŒ{/ø+ŒīąīvÚA'Œ‹ôqðNÐfv-ģj>ãáŅĀ—2ũ-?û2ÆZ‰ķ@}].fėa]_PÍg< O†æUĪ*.Žó čþ H IduįïíڟøÍ{X.pcč č lė ŠI<ŌÞqeNãkĶŽAqŦ­3ißïLXˆÐč ĀÞū ˆˆˆhûŋęjî[.*L/―ĶļæZÞïĶzÖnþ(#5Dë…Ö­Ģ’šÜ›ųįúĩcč č ėę ŠI<ÄÏn0ŠÅ €ū€ūÐ6Tó­CŪ%ߙ°ðlÚÎÆW>ËĮ˜Ųå·ÃOũĢŧĢā”ÅÞĪÃ:ĖėÛþõæo†Ŋ·}}Ĩ}AeŸņh5übëÅ=Ý LTÐø^ÐBĒâ„Ü“u2Q—v}đ”č€]ÐÐļ™xZRÜÞ Z.Ĩ(ĄXzÐÐÔ:ņžpq?ö^€â =č č ęžxPÜ(nôôH<(n7úú‚F$7Š}}A#ŠÅ €ū€ū ‰ōâ@_@_ЈăâFq / /hDâAqĢļÐÐ4"ņ ļQÜč č ‘xPÜ(nôôH<(n7úú‚F$7Š}}A#ŠÅ €ū€ū ‰ōâ@_@_ЈăâFq / /hDâAqĢļÐÐ4"ņīĪļýí†X8äVÜ­—Ē(•XÜAŽÃhðz}Ôđ/ðär9TŪ%ߙ°ðlÚÎÆWN X3Ðý­§ÞW,­•ČD(JĨōuuzÏrË3ißÓ*ĢËĀeN`č}}Ą-û[O ‹TĄÐ@U}Å‰‡ŝ]~;üt?z/üö&þÓafßöŊĢļÐКÁūýxþ•Ũ^čŧ[õAËY&*hÉ~sĒâ„Ü“OÝML}`Ÿ@_@_PI_`wâá@q+åĮÏēopœ@_@_hÕūĀĩÏxšOôBūŽģ™·ķ@WýŸ‚€/Ôč―Ø?š/ëV>éôô…Ví ÜüŒ§™DŽ€Oz}”ÛļųO3‰X=ĮïdÝCč  ”ūĀį|qO X3Ā} j…ubēa}”Õø(nPOyĐrđ ãč  č H•Ķ @_E_hŅ#pxÏeÞC ņ ņ ņ ņ ņ ņ ņ ņ€zø hÆ>――‘IENDŪB`‚doc/images/ifw-perform-installation.png000066400000000000000000000510251325366651500205510ustar00rootroot00000000000000‰PNG  IHDRÎGæąmQÜIDATxė’ÍŪÚ@ …QĨ,‘xxĸ-â"]ĩĒ *`Hf2“Û-EÖ5qïQ°U‹ ĨRÏšĸäƒÁl6Ëó<ÆøýRŧÝNū˜Ō…ú §Ö~ŋŨ)[äGžÞiŽjwøŦ|Īó?Ÿ~ԃðb“7Ēþ'ßČGģúÝPéŋ'[įƒó“ó‰1æy>N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/b­=6ŽĢŒÏĖîí=í;Ÿ;ņÅŪ“ø•ÆyÔ) īTĨ6Đ D% TRPå6vBđy’”Šˆ ‚ A…*ĄV<„JDBS!ĩ" ((Ä6Iš8Æ&įžíøÎũڝ™Yæ<ĘhēQ þ"­fgūųæ·ŋý}ß­ĨčþQŨđžôŧZŦ•anĪZšs}ĐŦ•ŊūTüŪÄÝëƒØ,(ĨL‚óPĐxO,ĶÄēKŧl9å0Å "Á8u(Æ"’sŪķâņļišÆMˆq,WŨuoĶŽ€P MŌÐXk† p‘ÁÉŒĻЋ[@(b …čŽ<ÏWđ*Ū„™V’‘jĢ{+TĩĪķ((Š2^č+Ąļ‰%Ŋ oKęVr֟ÂŊ!*ƒ ÐĨwĐÅ%Õ~vÃÚ §ŽŒ8-ÝŨæ9ÓïžþMšvÍŪ‡^Üö™+ï=3< Ļ[‡2ņ%–§ó8v'gŒõ_ÞýԆĩ^đāVDĀņæî­;ũîŲŲ› Ģ—Ó9Ξ2ÅuËéýúĄŸūįíGÓeäˆ™ŪÍÎ4ŋ}âõtŅ!āq ”!ƒ^~ũÔ!―ŧũíÛ`ÝĘf3œÜ°yëþÝÛV'ß?nÖŊztÕÂķîGwôŽCĖí"·ÝK>qę=įŲ݇ūąũđfrþ؟†(ónLäRŦ7ÜÕÏ Œ*5"zÎÄ?>žžI}hã {ûŠēC—FĮ§ÆŊ]œ ­ûâö=[â0pĸ§Ö·6·öô?{_KēõþO>ŋkOßÓ§ŊýuĒ@9T‘ƒ{ ƒÎ\ýÅŊOÖ?ļĨŋïóđKAˆ–3mk·žðô'jƒŒ0Fą―čá͛>ûÉũūųŌË?―šåđ+<öæāãÏlßúDŨ…ÓŋMukÚZ;xž·ũKM Ã)ŲB—šŒXąĒOđXvŠÛŸÛypߎ'Þ:>8^6 §ŽŦûGÔŌ\ĸøŒĄ;_ŲF^õ0Ýą 2§Č`ݍęhåRýV/_ÕȜÂąz€"ŽRŦŠđˆųĸ‰>ūZš{}DØÝëƒ0ÆjŋœĨRĪ*n,h„-K`G'\Ō›ÜšÚ@jĄp 8S'‡…+#‘ˆhŊÕÕÕUUUŅh4ˆĨ™ũžšX}Ā ÔDŠ"õĐXj~ī!ĐAŦ&\Wq6ŧ…Ĩ &ÆxēËčFŅ_€ä#ÂԋPóūnuÛl*@æQiu}|c]kuŌGÆĻ[™Üį'ĨÏ܎ĐbÔ+ŋƒ>:%ę2§˜ķ~øeKz8ÎKŽ]ÎïÞîfį―h}Í|#_ķ)ũ‚b•G0‘a‚‰Cë:š—Î 1re•đðÚwŋóōŦGĶm`B&N Ķĩļ―Í$ļąĢÍ–&f05ƒ­í–Kī·"Ŧ81ãˆ<^ öŒLįĘÖÂåõaX,ÃRnôwG_ûöK?˜2ŽJĢ& j!ÎÛ&.ƒČËÛļŪĩĨ:ˆm·ĻūŒ‹ŪCCó[W/_ ģ".7Ÿ—VZ-€\<ŒcKVvßÛ FMŨvęŊÞØ=öÃo~åÉ‚Ã0 ȉƒqyčĖ›ß?|øĮ'Ï#ā2Â<ÁšģŲāļT°ðڙŨðę mHX€BĻaåšeóDĮŽH._(|Sï]ÏXãį~õËãŨþ™ávþ?{åᧇŦ“ !!B5ÃØvĶĒ•üЂBą…/[G4P“ Á‚#ývĸĻIĩĪŨ—jdsŪūtĸH7*óČ―Ķ|ÔëKŸũÛ^[š›Dņ”Áä&üu­Aë”ÔAĸ―>úYŠļþú đ,Š\>’8-ēbÕh,a/h‚흰ģ ķušM‹Æ"ņ@Ũ 3Ō?Ö‰DĄP…B ķm‹l†.–@sē“Lō LkkÃõÉP] Dœ NUĩ!`ȄĘr ™ïÖũ›&û”þ+$Ą‹(Ī‚r^Ļxđ*Ŋúköé# ïR/OõJßoĐ⎍ýÖW™åXŅГˆ-ĸ‘>âÎ.ŲaË4 h @Ģ*ļø·ũōØÍeFō7X2j2—aQþåėåŋæ=Zų q§XķLļ€ ˟<ö–ÝĩĄŋoKs"ĘJ@dŪ§m‚‡†Đ8ōeŪ—0ū*fŽÆĶb„ÐŦč ‘ËQ<ž@Sƒ—ŌYBĘŲéÜÐŲãKŅŊôo[“JTi‘Č„Ï`A^ö9GÂ‘ŽøïšSĨōĖûé‘L2ÉôD쯔 čr ęRą‰5ŊÂŲ„nĐTt( Ōē Ø]ûÔÖŊõ?™<{nøp™C&ÔÎųýĀG6oïŲôHЃ.å‚īNũžŦfŨCŸÛ―ĸĀ‹Ÿ_Zgđ@– eĄzÓãŨĮ2ÓąÔōĮlķģӍ-ÍáDjÝzöüęÁ=OÎģ<ʙ=™-‘ŠM\ %ųĖ•óĮ@T*ņBz4kãĐą@Ëωš„šüV(“čuÝŲ?*Þg9ŸtĸčðĒęK‡^G5#ã}•ĒSmDßöSÔũhšÛ|Îŋ­>’ųŋÓGĸ{ô_ŽYÛOÓnöŸûy"ƒ=Ūíš–-0S3‹DH‹Á!D‡ ƒ 'Ë [éaëaŽĸâ%ņð“šjzÃÍï―øōäýžįyß>éÝũŋįãîö@UUMÓšwÕëõĀĐkZsyIž™šþøágŦųCØ3Š…/…ŲŪ(vuð͕čšÞh4$Iob···777āķ^Ŋŧ4— €ĒĘeaém5Ûðýë]ņ{Đt2ųĶ’ïČ"ðųÃQpđŦrM<åô).ßý /ūŊœŽP9BgO>āþÝÄģ°ÓôĪáé€YNh{ōĖ{ōđ8A…Oį–ÞÕtĢÛiæ#I8å*ÖĀ–k•D$Ã(Š‚ýčósÍ:yŋ€OŋÝ51LcëåS_JgSˆÍLW;R„>ŌL*8–\?”–4ƒÅĸ{dØtȟ,WeÛ6K9<Ëڗ2G +gv_]-Ķ~ûhnqëh‡GüÃ…36: íœīNũW ðŽ?ÁwZÂ\f|c·}iŠüģÉŒĄ0D°ó―ūļŋ‘%éݖaIĮL<ķž]ÓMÃų Á9žßo›P™Ų―3ĢĮYŲĐ5„2™@Ō,`SuÉ4Ôö !ĖöAĩ˜ACp’ÉN`‘anyóīyHcD]ÔÞq8ŧf}ŧ’„-*0‚S4E‘› M^Ahą%ýý@Ūks4Œ,C†ĮāÕĘéՕõy}>0ĨÓ4ApĮ’ŪÔJQŸâ _ëˆÏ?ņGaŠM%Ã8WîÛæZ.>4H§(<ēø‹YŦg‘"ĒũO/12šÐD4PDžD D”ó4ÐāövÕéïŊęŒŽoķĄXfØãĀÄ ššŠŨÕŊÞVGÛŊ/KIëÉ<6?°Åüð°Áþq~dĖâ~ņĀÃÖ[Öũeqŋļ“á ŊGîŨRĸGÆ,ôað‰Ų›R h8RJ0Ę1ڏŧgOŊÜŋ~ōXŋ{[ŒÁnĀīÖÉ~ΟŧÝnģŲl·[T@ÕõÞ:ŒĘņë·ũŊ>=zþáÞˡŸ7o\4Á‡NãÉ~ĸD–SX;n8A&Ö}øŪ5Œ#‡x&Ðã|âZŸci,šč?áĒŧ…>(Ž―\Ÿû‚ŋŌ‡ŨãúHSJŅïëijY Ąm§†ęˆĪč„ÖĨ­glˆĐķÜÏčhcKEJ*‡í ‘8ŧsz~qåītЌ-GĸcŽ\^{-MČ­D­Lž-e)咜õ‡Ŧ@4eÄE+›ƒÆ‚^ĖĩD'ÔwԚĮėú8§(RĮ)8FmĖ{R6·1Yþņ mh Đy[­„áŸ1ĩƒT˜ÅTTkšļV "âxBÓ`Ą\ – *)%­t,­æ(†afãm ĘĩÚ.OŨGJAËa·ûîf4AEVôZãÝtïß\~Yī|―ņywöÃFƒËUųĒÅĮ #(įīÝm]ü°Œ ŊIņē’ÅLvWB‡ÂŽT‘Ŋg?GņÏęnë„øŠĶÆ!ČýbN,1چ°)‘ðņ=åþ><@ƒĢŒŋþL  s ˆđĶïʧ‚É?ŊÞ~]=îĀ@ÂāýA+qÚÁБû“*ĢãïvđCķŠïģڄðÁ°°þ,ÄSęJų^ÍņáCÕH>|€ųAJ Ýķ-š_Ķ8ŧŪģ@›Z;Éą“ŒĄGmH§ģ Į3Ë&Ž&ÍB ýé†ĪÜĢ}JCIBJÔIëÍĖÄŽC|ÐH’m2Ä'žã|ˉßķC|4j>―Ćøøvēęp&ĸ%ÜÞvüN|ļĸŅʁũ~{ũß#ŋQïnnZĮäãč“|0*>x;œ` Ÿô79|ęîģ7ä3,f>Ë)[$ũG7 ‡ōQ*ï‘Ýãîaŧ&t6=ŠžšĸqšiáI>čþûSAÏĮ㟑Ïí‰Ï—þ^>Čq>ŲÓãŊóa‡‘|.šĶ™Ïį‹ųÂGôl:[dđXrDO§Óųlū\.›å>m>žUģ"„‡4 KÅc+ zīD7Ctāå֘ÍfØ ŅdZ’Î8M&BZâpg^lå,wÐ_64ÍđjF )t=ß2ćĻ"KJ>zäC!inr‚OąJ>8ÃĮÐ>hGÉĮ#6[qDŽðY5 …ŨŨߛEÁgķĀ…g ėðÁ ߇NÛsøL{ø`ČĮdōAĘÎåýI2"Ÿ$P[>m1š_ðYÕũ'Ck>(`^7Íô›|ï›L†øĪaI C:|ðäƒ!įðA0óøĻĐÏg–ņeBîOh—|ðČÏq>H/ŽįņÁ)ŸLDÐÏø />>ɧ'™L&čŸ~"‡Å(i’>ØešGu}ŽóK'šþu&Ŧß*#RđššZ§ų Ã|Jz%ŋĮ—æƒ:‡OÝĘhÍGÏ ōAžņAīĮó‰>8gˆOfãųËø`Ÿú?{ũEĩĸüũŧwʖT:ĄKPD)*XPąāS" *"*Uʓ* *]ņQž" ˆ ‚ M΃ĄBI()ĪnÚîÎĖ―ũŋ;I„ þU,õ~N wîĖ|gvēįëœÍŲlņ•ų™įÏĖßĸųcûģŪ ōþĐė?()Éë#Ŋôg€Œ?˜$I’$ŦV’$IV­$I’ŽZI’$IV­$I’ŽZI’ĪßNV­$I’DāŊ@B„ŋ{0Ĩ”øÓaá‘ņđXš#@†ÅátjŠ‚ð?%IŨ4)3++ۓ•‘x&øŅm įÎĨedfgeþÆĖ {žváČĄØ#qņĐiöÔÕ'fåääxēģŋ_ödgýôöéįâccž>—úk›iûéÕŲ™į}0wÎėw?8‘œzâۙ“§,]ŧõ|zöí$IōŪVBT(O<ļáųÛÜŌėÖÖmÚīļĐIƒÆ­‡-?ętĻpÕ3|>ƒq@"ōÎ ļŦUŦžÃŽes•ļ*„Šg·-lÜĻQ·AŊæ‚SQu–vļÃC­ŸļUŠðCčtÂîåsnoÕrÄÚãšBāWœ™~ŸŸ‰ŸžKGĸ…ĐSĮŒ:=“ĐūôÃ/ŋ2tņúoüD' Iĸ3ŪUĨJüæđ]Û=þáķ•ë5ëöTÏö-›V-Ķfy…Ē8]N]Ũ4=øQC8œî°€—RÜoTҜîādĻ[S):ŧuųþý·_ðŧtJīˆ··nÓô† đUwJœRÕæþū'‰ĒŲ1n]Su‡ÓĄ)p9ΧŸ&)Ų$„úsSOÆN<›ûšœNUĄšÃŒpéÂâĪ|•:w·nÝļbc u:JÁã†^ĘGĒ;]…Ī8ąāypς™Fūöfšp9U‚Hŧ†††ļ"2L#ð…Āo™ģ°øĸ .{k—S#Á)T [WÍé sS„IV­„„įy{ô”ÃđŽGzM ü5ĄC‡Lþ`ųgŸ―?ð‰ëóÎÆū;åíåŸmújå” :æQĘŌ·GvčÐá‰þo8ïS"ðSû6M}ePįĮ;<ųĖčuûNMËNÜ7kŌ›ĸýčĢï―ũ醖RįžŧÛÞv}ļNļ7ûë•sį,Ųváäž1/õ|ĒÓð]§=” ĨJÚņõĢúvėðÄðU_n]đ`úûc-p‰@⠔°!Ŧ•DBSmš:sÞ·ą ëūŅąc§W§ŽöĄ­\―u›;nŽJˆïŦåóįÎûāșÓ3=Ũąóģl<ŠŠJĻ"ž™ŦޛxPOõč1cņŽÖÉ]ë'Mš>ĸÃĀ+ïĮžÏöå'<ÆSÝštčØyââ 9~vÝĒ ŠŲã@Ī’zjĮk―Ÿ ŽŸģ6Û Ėčy‰ßÍgÖį;m˜ýÚÓOŒIČãˆðO!ÉŠ•iޅØÏŽfĻ՚ô~Đ}‡QāõähîðR‘!ÞĖãã'îólŨ§L\ûÕ΋™gF=Ý―ĸĻũö|}dÃ{Ŋ=ûL·™ĻōžUC†Ï^ŧųLÖþ5KĶ?ýėoOd$žØûÕĄ °mųÖn:<ã―}MžŸ”O4æųę“·‡õíÔæþîs? üš·† |' đņkÛ=ÞëÍđ+c~8ōåįú 1ïËïL ?%ãÄÆŨFîøpۗÆLĸtåŠ‰Ģ†Ŋݑ ‡Šņ_ŊÔoĀ{’œšĩé―w†îũðýíÞXīlåōEý_ymïyo(dŋ?søsƒĮïÜîČĄíï,ÞmúrŋÝđÕc"äe}ôîâ“ųï†QÝkæœÜąIÛ§>ÝF-ü|ÖļÎÔë%†%LF)€b@ÓûFmÞžŽaÕÐS§WŸÎ,øō―7O$zšwžcĸÉĸŒééō3ʁĀ/…D3ü†ŠÕ›ōß/æūÚØé}'›œĻœqÆU@·˜a°ðFũšycßÖĨžĮŽž=“åófžŒý6Ũņė°Ũ?]ŋkũGƒœzT§ž/Ô­^ĐîĮߎ}ôæ˜ōĩï\óՎÕ/ûāý7Šø<ĮÎĪs?ÎĖY·xęŨ'2ŸúþÖuËÛÔSVŋņÉŲ|ƒÝ4-nDũ›īdÓæYÕÃ(ðO"ÉŠ•˜^ŸUøIŪP’égŨ5kó\ũ.uŠ•cŲ9i‰‹ŧī}pڗĮÁ°RÎĪ̆ĮũŪėÝ­Cŧ‡ÚO]y@EB‰Ēi jJáO.…s Š=:á'ëV­ė D ‰―€ĨŠ5o_ƍe*T. Ū@T‚Bačũ7ĩŌwŋ0ä‰ķ *ÄÄØ+ŠĸÁĒM,%žŌ“/ k~}Ýnj“ïÏ'á1wĩkŠ&}þąöušóé/EU %HP…j]<6ehŊöíÚuï7é<EøQˆĖō? ŸNõhŨįvžČ؛îE ôZ ėßŦM­ę•C( øĮdÕJBgx…ÚaŠ'.vÓö#Ls9t]w85• Á!Y–Á-a `ĨŠĩ8bˌĀk°›V|ēQĘÏžîũę$>ėõi=ïŊ ˆŠ.„]ĩšŪĐ?ęĶÜī,&8·›Ppb°ĖėL@ČJJÉüÁ“FpUąē0MTq84bßąZŒÛŦ Jš°‚āĮĒÐö§ã$wÜŅéՍ_,ïÛåĶļ=[Æ î―áx%Ā„‚šÓI™įÃÉ]gŊ8ÜĻí‹oŒïS-üDQ~Ēm š?ÜyÄŦã/ĸbó†e7Up2. q€iXÁ…IV­$,­\ã—{Ýã4“G 8iÎâûbwo[=îÜŦâT‡\0.„É՚ë–Ō0#>ötr^D„+')9å|'9îb>/[šI8f:yĀúĖÉ‹„P Ö}žrïĄC ƒ’L&ÄĨeÚĪåuYۖŒ?~\ĸ§F$\ŲfBč‘j•‚Ã;wÍ^ąæðÁ]KgĖ?™š_―Z4f‰āY ( ø~ÆÁmįÅÅ+‘įĨėørY2ŊØŦĸā'ïo`åĪy ?QõHAēÓÓūøøó“É™ēÁUšjĩČ3‡âSģrĮđę·=ÞąeÕßĖx°íp_ôCUÁ3sė›Ré=ũ?~s™O§yiė[9âd`r&ģ,  P f1ÆLf‘;_œ8ĒG;ϑ―#gŪŪŨ―SuKˆ’Mk…Vn1îØf―o=ÓņŪ6ũ|ĸËÖŋņ|K°į ļÉŽČ0€*€ĀBHtÎMË,\ə āPU—ƒŽý`â#ũß{ÏC]|qēmŊá­Š†šŽ˜=;FeþüĢóūJžĢË3áūýCŸybęöŒŠu+_Üž`Þæ3šĒ˜Ķ^ÚĄ`a{;”p$īÕãC&―Ð!nÍ܎ī―ïūštw$Õ$ ,ËīĸX’|·˜”å HÝŧsýŦ·j͗ßîÝw.%-=åÜî]ŧ9™^ôķŊŽžœĖ‡ũo ~âᆭŧöž>Ÿ’••yöxė– ë·l?tálâūÍÖoØy.5#++=îĀÎĀ'ïØ›–q1nÏŪ―ą‡“Ó23ÓSOŲŋ+öøÅôā8öĀū=û\HËĖH=ųeāĢrŨnøjûöĸžþžЊßėä OfÉ·påx<'XŋæÓĀ<ŪÛôíéĪLOvV`>9ņÄŪ=ûŽđ•y>!~ŨŪ]'Îgfg%ŽßŧkW\brā|N<°{ïþ3.fef$œ8ļkŨwg/Īee͟:q$ðÁäŸŊYŧiÛŪċYöÛäē2/žß·mKāĢ :›~ņüÎoķŽĸōŦã‰I‡öï <ÂïN&gĨ%}·ßūĮ.fdĶ%'Žx,þlā1.ffڅ―;·­<žM_íÜ}ôÂÅĀęýûö|w"Ņ~Ú?–$ĸ܌”™™]’Ý ™ö_ģ.ß.+ŧˆ';;ËÞ(Ŧx*ŦxTTĖÅģ…m^4ooėG[ņ&žs{G8ÊŨŦĪ€ŦÚíË6ÍÎÉÎ(áRlņŽEÍeW\ŅųØcOŅ8p°âq–}ÎÅĸŨLíMJ<ĒKĞɾôPŠ&[Î}ĸáËOĶødgĨyė—$YĩŌĸLfú…ø/æÏ›3ûíYoÏyį―…Ÿï‹ô”lĶߕ$ŦV’2ģó}þbū‚\OÆïM’dÕJ’$Iēj%I’dÕJ’$I \“$UU… ŨI’Áē˜išðk(pM’=ëņxâââ)%Ũ I’cž^―:!!nËēþÚU+B22ē""Â+UŠČ9‡kƒ$I”Ō„„ÄŽŽŽ°°yWûw€ˆŠŽŽbŒÁĩA’$UU=[x$ŦöoBƒkƒ$Iœ‹€kņÏÍH’$I$I’$Yĩ’$Iēj%I’$Yĩ’$Iēj%I’dÕJ’$Iēj%I’dÕJ’$I H/ˆHAD$é§ !8įBYĩWC2M3#=Ýï+@$ IRI@Ëē\.WddĪĒ(ējĨ_—˜š‰Ąe Âå$IB@ŋ/?ëtlđrå―^o:uęŨŊÏ“U+ýęŠ―˜–ĶE_Ŋ†Wð“þߕW’92įyŲá ĩkŨūpáÂŋ4h ŦöjHŠĒxļK)ð^þ`ÁڋB”\wŲֈ%'ðÚΗíôs9hOcņĪĖųu96ņŨÎAæ7N—Ãá ËÍͅŦ#ŦVĒ”úZ‚üxÕ Á…]mƒ‹W<3‰ŲsŨxްB°Â;.Ëąw€$8&ŅBüïsļüŊƒLPE!„(ŠB)•ŋŧZ’T "žt/Ëz 9ÁDpĶĻėJÜ<î…E" JæpÁágr‚!RNáūșž( dąH‘æ$B ~6'8Q"ĮÞKØß‘ók ‡ĸšHŪ؜ā2Ĩ”ØŪŽŽZ‰ ú9H?9đœƒā0`œđėĐ  īðųmG\™ÃÃŊÎ!X2‡3ŋCŽ]Ö ˜œs,Vēē%Á@Ļ@ČoÍ!€øŧä "ųđĸņ@~"‡ !ė‚Û9Lŋ"É@čÏåÁŲĸ$  Š(…=+ŦöŠIBóĒā(,z)” Ā ‹ļ%s{"ˆ $ˆ‰‚ö€â9‚ q åÁ@pāVa ―(Jä*€;„Ba PļFrAø 9„ þ\į ė;ŠDk;‡"’9_MŽ č`ĪĄĒ€ŽÚßJōpā–@Xü!ė"cÁ1rāÁ•ö<„ï @@\ÍíÅā—ļ–s ûDá" °Ãƒ.å ĀÂ°w$öĄþn9ð rÄUåp!øïšÃA\–#~Ÿ`rPœĘoíYYĩ!XÆ BEB .p{ĀPŧØ~é.0ļ  ėÅ` â“ÈߚÃđ°+ƒ!„°ĄD{_DBÁÛ!~k‚LG.~qAB~.‡#įvNpđdNpŸJ"89Qå7ŋĐRV­|ųąœ BÄā—íû›øþŨýB ―― —mTī/Áā’k:‡s@°sŠūЁËsŅŽšü—Ņ ð/ÃĀÏåøŲāŋGŽá 3PĐ*_ŦýM$‚‡Ŧ€ŠĀ+bQž`Ã{Û·,ZFŨrŽÅ­,ŅÎ)R2§xđ0ä s~Y Ļ(Q!Ā9—U+]DÁYyĩĀ B€$I%QĶPƒrÎ „ējĨŦÁ9ŊPūBΙĢFĘI…R@’$}O4rΝ;Į9ž=—s.ŦVššŠ‰‰‰*É- " ’$C@Ž„sŪ(ŠÛí–U+]%BHhX8"ÂO“$IØā%ŦV>‡$I’x#I’$ŦV’$I’U+I’$ŦV’$IV­$!RJė %þ‰RŠ’$ŦVúQH…ü 5K˜/ïü…TāËÏššÅ@āï EUĸŲ{€ĖŸt!ĐĀ`’$ŦV* ÁĘųlî”ĩGģt…ĀĸO~€(Š?iCĸ^ģŌ-=yÍēgĶ|ėWņŧĩ.Yŧ—þúD‰ŋ@N˜ũėœ1#†ŋúęč‘Ŋž:|äŦŊŽņĘ[ûŽïÝwüŽCyŠJāũ&@ÎáŊ€ ’ŽZéG0cßËvĪøQĄšÃĄi›N ƒK6ĒhĸÎŽYĻ;4J”â5š"‰e%s‹ dzķ“įAýūÆMΚā°iī0•8.Ņ.ƒDwą›ĸŌ–ÔÞNÕGW6•Gņϰņ/ĨęjaœZG oĢë—ö @―ę žEs-ߚđsũįÃ!H Ņ*ÔĻZ­ZMqnãēõÉÕŊŦ^­FL„&RMÓOU…ŠßŸíũWI%ÅJžŠzųEVėÍJ΀ĒëI_~õd§Î{ðåQ HĻB!€…&xŲJU*•.ĨRvâ›/V­]·hbũ.―†m9•MUøģæëŨĩgï‰ģÞYøņ7>ŽXÜ@ÄÜb ąóKãbÓ,s?˜ýZ§Nú[šâE­=[>ýx˞‘C^čö°―Ō—ŽØđGŸĨŧ5UM‹Û―ô?ï/>ļs—gĶ­9€ŠĶŧvnNzõ’éŨ5zņØÎ•ŊYĩ`X—î―ïLDUUĐųųīq›ŋŲĪ*˜žŊ–/ÚķuÍË/<õ™i~šīï“Ą#_Öģį‚OķrĪ ŅË=ŌýŲįzväÞ[*6đĸĐn={õęTŦ”“„:2ÎïÜ­Ë˓^ČŠĶeŸÚ6āđnOö›Z ĐlTÁ”#;–/ųhî !cÆ}ëp˜Û?žŲ9p’ĢgËķ(Ĩõmģgž3#.H~ĘÜ“G1lāč“ŽĻ‘P+íÃYc›=õėŽdS!ųILz)p­^giWT•ïXöáę5kfëÔuČŽĢ)ÉËĶžØõɑ[eĻŠŽúdåĘĐÝ:w8qįųžÂƒ~ýŅô@ZïWgũ0MUÏ|ģbՆŊ>?ūKŨįWn?DQIÁšĸ \Ŧ9Ëv UË;{ðÓUkūZóŸÎ]šÎZšMÜũÍōŅ̆?ýd9ŸÅ*ö‰^F’U+ @ĀĘxĸ•~/ŋĩžBíjŧf_z0ÛwfóÐÉóšīmßšn4ZJC7F6hØĻJ”#%!ųæ{ĸuwĢüÉ/ÏJĖDļœŪ‘C_öųÖXJÕ û6ö{öđ]ŽfųĖýoÎûœóžũgœą/§í=·žųzQßÉ+ģ hßÏbއsFõyũhãÛïmQŋēÓĘY2ņđi+wÞûČyįô6&qÞëÝ­GĐäČØþXÛŧūĘÕ"Žø)ÓĶgSZp!ö́ý7æúĨ­·Ÿôulęîĩowģä†{*Ãâ; ™Ÿ ÞĪC#zũZðUӝ3{ú‚„ūcåŒĐ_'ÜßþŪcKF-ÚxJÓ­/ßéÛíß iÅŠ§ŨN_üÉqWtT•ˆúUŠ…B@0 ð„ßðrf2° “qĶ&ýÚ+o…Ôūnëc>ØkĶđïëFĨ[›ļÓ'ž6/!ŸÝYSâIØ?âĨūÛōņ†f‘_ū6t`ŋõ·ÞûPôųϞ~x|2›' ØĸËĀLÔđÏz><• ―ŪbL­ŊwĻ`ŸQ‰gΓ=&ÏßÕží}Ín*ï‚ĖQCúÎÞk=øČ―[—ŋõôŸ"Ĩ‡>ŋï—RJ5ČýfÁÃũ>ļ>ģžûčÚïŽŪ~ðK=ĩiû?ëóč›Y6Ž<øå-Íï{("aeÏG&$3’z`Eß§{ĖŲĮŠĐûÆ xÏ̈/Fýy~ŧö·Ŋ˜6æã‰Ķį䛟~aþÁú5é;ŊžđãDNĪ[ åîÚõ›Ô(öã/%Hēj%Á-_­ß˜w†õïŨĶžĸBŪĄ(UðÃņĐuęŨrjÎMïĻRĩÁ#];5­SŪîm·W s2ŪäĨíŋgáÓEŧYž–ïķĮ_~cÄČgŸļcvÆđÃ[Ũ}Ũûå]Ÿč>Ļûƒå>‹ƒ]@ī ųČW+6õ:iĀs=ž}šKõėÂĸÆ5aJŽ]Gë‘ŧgGė…lÓ$Mî0uÂŋÛßqSéjÍ_Ÿōzï'ÚUũ\ôsðú ŠÜÔzääIÃ'ŋÖą­ÝöM?þžVËūzt5rxÄþoķėNð2­Û;Lĸfĸ§;DšēŌsēw­^lq=?ÏtXÚޝ‡ýHó]žþÚĪW?ØŠæĐsņa5›ĩhVúö=ïnz‡ðŌ@Ķ(ÓgÜ̇üWƒšÞīÔÓûūYũÝEÕfݧ[â’<…U‹>_Aƒûš™2Đ}›Ļßėk>ēŸÆÅ}Ÿí„Z\ UŽĪƒ OœzzԌšu}ĶŨŋô”ý[ũx_8žsĮÓ=|rņ'§ ,3îé3aüaũ]WŠ\ã'fŒþxŦų‡æƒðZþ‡―5tøāsëĢØļS wžsĖ€ÞÝaäŪØv:WVŠU·SFŋøT'(8‘žžüáþTQý9–•v<öÄyff­VSߜ8üŗ*„ĪĮ§dŨhØĒV­†­Ûwlss‹qdÕJ?$„@·ģBÅRÜ2€ [XŽũ.™?§6?útÎkŽgqÁ,–aĄFŌČ!―–n9Č ŽÓ­ €ƒö—ÁĄ\LiĀ<”nY†N]€ü Ŋ7‡ 2\æÖ„ā&Š!ĄNphn$Ük˜Â­FUŦNÁĘ7]eĢë‡#–e!ESÅårë„:e5ŸO8a@ÕĄX&ģL.J•/3]Uā^/ŨUÍôZ-{ĄWû[DŨí ŊSļi1Ū„Y „ÁŪ8ļ——ĐUŧZ #H(AÆX„ËĄ~GÝæģfūąŒfq6DXdļK0-44Ä%ˆîP4äĖž -šĄðĀ”`y—ýjN@…Ķ8#\Ā °@:]š.·CŸÉ…_`ttó1]FCpT=L!€^ĀȈp@Õ݊Zž€ā@CíKM *„ðC­Ú5JĐ`˜r΀;jNï0hü“wßĀžFå*•c"]–iĻ *Œ1ËĘ0đ)@’U+]IX†ßnnú|ĖâE3‚˜ĐGŽå†wėŌQ+865/$ǜSK<ðÝĄä3§öė?Ðīuóš!$ĸÜŲ IÂoX@ou,ā–iYŽp`úM°Wy}FxÅú77Ŋ6zŌØĐ“Æ·(ĒnP•‚ΎЊõnš·áø™ÓÖïØûíÖŽŌÝ-pï‚wŽÄþāÃ%đek5Ļnþ<ËNóû}ÞÂ#š†!Üšv0öðĘÍßíY―lãžĖVÍoŧíÎF·žģõPÜęe‹ũ…_wýõ1Ä4LŸÁÎüĶIīð›ÛޟžįŊßâþûÚԊ΅éũš&@8q‹Õ9é[?ų:9ß@@ļ g–ßbPÁĖ3üFpŅg>?iÐīnÅlÓ]ᡇÚÕĐ…œWĨ}Î~ŧ―ÕĻ{*GŪøï’oŸXą`ZŪ§öõĩŪkđ|QáĖôüÜz +†;Ģ]ņmØu,Ņäˆöi™ÚļBį žýĀĄm·šåÔАþáēeGŽ7cÎį9M[Tw+Ķá7íŸ3|_p`X†7XîĶÐE+Ũė>t|éÛs―Ĩo―ĄVÕ;+E,]°tį‘ËLÜs7ŽØÝįõ ûp>Ŋßï*ÓŪJ­Üų7ÞŅîî–õ4•sÆ|>ÆPågL )–y(vũųĪ$—Hō/{IDŧŪQ Ĩ v[Ëæe]„cpF‰v0Ó3ï y^vóÃm\aý;ß?kæøðÐá};M>6ūa―.OÜīqŅÚ;ĸ]§e3Á ĘÕhRW.*VŊƒR‰ÛƒÕJ‚swđšMęĻ|~äÔčEóãrÂ{ôyxųy• ā ļIJõzybÆāO>ŒF7|mōŦýßœÏÆMčû|ŋðēUĶL}―ŽÎ*Ũ―Ģ į\”ŦÖðæüÎyD™j͚Zš@”_ōēÉCWXz‹ÞcÛÞPÔ|å\Îcúŋ@BĘž;ýõV1ŪøÄ*7504Á1Ž\ãõ\ mÜmxrÂĻ žÄõXï1ÝŽ\§qóōĄ įĪڍ­HT9#îÝôĖ+nŪðĖ=‘[`‚EÄ4―ĩiÁ…ĩ2MoiZ6œr.ęÞØ47&ÚUđņīĐãGL|ũé5ó°úGūP:,œ‹ČrÕon tā†Ĩ>ųú,ëŊöÂę[8īvXČugšo /šY4ī^8úï}ĐųúQsfL­:ej539ģx™ĄÎSÆ9tˆ;ü–Đ Įū9mŌĀQoö}þÓŌ7Üŧrčģš€ÚnņU-ÃđļîÆ[î,UQp^ūN“6fe„BĻ’ðõ˜A‡™ŧÞ[o G―ûäYÖäWFõ}ÂëOX4īŪž576csgéÚ­î@+ßš3ð !ĪĮ˜™wEWnŌÐp+0ō–;Ԅ+ęÅîOL\ķpfzåIãš3à ]%ĖČȀkŒĪëúĐSgœNGÕŠUcðë æÐÁô u‡fų}LÏpâÐΑũÛwŽŠî ‚sn ĒQ@ÎX͉šNý>M#Âg˜TÕ)0Ãīūā*āÂŋmÍŌíņFJļ`âėęÝ&Mč{ąL(„#Ќ Jˆeø,A5MÁ !ĶáĖŦZ ÍōŒŠšJ…ÏgŠŠ*n~ûņÔŨæĮūĸÅâ(‚3ŋa ŪkB˜a1N”Āķhø ŠŪRÃðóÂË9"núMXäöm~ðÁgX`'…Ē}›Îá24˜Á|~ëû3· #ļĢÁĖ4LF‚gI ‚0ý~& Ðũ§!ė5]Î Af™ĶÅK˘ĶÉ8 ĩ<°$AŠé*pA>Ÿ TŨTÆ8E4-?ã< Ë4,Pnų ‹(`Á+ĸj ŒšÐó.ao"~xPŠ9Tî…TÕUbøƒŨJÕu"8ØÆÔûqčšeú9Šæ  „0 “€Ī(J|üI!Dåʕ ÔUûO­ÚŸ‡ˆ „ļ|Ų^,/ĸBŠŠņ{ķ,_―ÍkŠˆŠšvjWڍœ—(ų‹Ž@)&Ÿ<°ûhV›vũļÁâ%wø™€ŸßÆū p5 OĸWoTrĶäâ7]+TëÛ5kY•Æ·Ö‹AÎÅÓ~Óã)Þ@’U+Ŧö į\€Ē*`~Ŋ\‚ðKŌ$D d!^ËUŦ€$ý6BˆĒZdėũ͕5ûË/\Û${’$Iēj%I’dÕJ’$Iēj%I’dÕJ’$I$I’$Yĩ’$Iēj%I’$Yĩ’$Iēj%I’dÕJ’$Iēj%I’dÕJ’$I$I’$Yĩ’$Iēj%I’$Yĩ’$Iēj%I’dÕJ’$Iēj%I’dÕJ’$I üIˆð"üĐ$IV­Ä0&8ðO€@ˆI’U+<-ÏJÍąō ĸ!T'åÂÕh·Ē)4I’U+ų,žėąâR| †§Ā„Bþž@!ĒÔ,­Ũ-ï,BUŠðĮ‘$Yĩ’ß)ŲV\ēũØOfķŸ3@ŧėo7Ēč?đ<§B@/ĒČķýIējĨŽëDšq$ÁãÏ7onXąl”SQ@ø‹nĸ|W2@0ā|~+ébÁŅļÔÃ*%*)åĶBĀB’dÕJ9^–šgĶĶÔŠVšT„8&‚ŊŽ)BU…"8g–É‚ ļJv"Ą„ ØgœsüĩÁĒāB€ĀŸN…Įá‚–‰rŸp%Ĩ{ËF;ŦGņ(7ðĮ$Yĩ’ɰĀ[`ęNÍëcLED .'PQyʑ] įR]ŅUŦÖŠ§åÅíøö|ã6·ŧuWGˆORvŪ—[UÝĨ˅šuË2áJH(œ !ۘ˛••éˆ,Kģũí9zݝũ•q.øĸ_Ï\ÎÁ°@shÞĖ|“ƒÅüŅ$IV­$€ûMî·@ $€€W”JŽ~8rÞûÛĒ\—ĩoÏ3ūjŠÆ.Yī3æ–ÛU…ðK%XīŦQīTbPlũē!ïoIŠ_§vÆéíyĪfŧĮ·lÜļ€h‡öéÅĪģŽč*án‡Â^e# æÆ­˜ōví'&Ü@ŽŽ[6GŧųH1™@€KįP@ŒsÆÅí‰?‰$ÉŠ•ļÁÁĀ’MŦņž­ŦÖ(wíĸÂiĮŋÓ*†û’5%:Ââfzš‡šÂš€(Ž €%ŨZHH˜ûüé}'’nŊ^Ŋģ €ĐÜLXóîČ{ķˆjwözerE—wį’ŅŸŽÛíž|žCģ ‹g—―sp‹Ú°zü ύ/uéØėĀ’ĐįÝÍîiŨ†0“˜ÂápUrGUVÂ+wîŊŸéÓuÏÉÔŧ*ú>™óâÁ3^WäÍÝFŒ3oذn‹v0=9}Z‡ŧJ2}@\Bž…Ū#ßoíŲšåĢũū\“ĢÁĀ/h^ÚĐÕÓFî9—QšęcÃæU ŽdŸ.pĶ&·'þ’$ŦVB!„ÅĀπ €ā~ŋŧÅK.Î7Ąûš[šýûĄĮW„Ë<ĩ+ŋáļįgõZ:āąc‰ƒĖĪK6žë5imÕÐÔųÏ<žæšĨ…įðþïŠĐļ{ÃĘĸ­ũÝęÞ{tgĩGžâ‚ (€%€1V`zýjųj–Đ’˜pzûöĖ{ûOëšuzïuë·uhÝúĶëo(õØĖŧ[ßä72š=ŧčĄ(uÃ[ÝũėÜYģmUÏŲđačČX€ĐGķlÞzęąwŨ6.Ŧ›”˜?ZĨvÕ2`\ČŠý3Iēj%žåf( ą”ēwÝ5ĪNåo~ķxčEĩÆcõōyÕûšīë(BóBœŅđ>oÂéÓūčjHT–Ģtĩ1;ĨĩŊSïÔķo·›áäú‘'·n`™Ά1•S<>&€äšÂÏ 5ˆĐ)yžģy•úđüÜŽÃKæ ZŸŸ‘R―vH!Ĩü4,FnþūđĢōr―ŲįĒb””|ð2ší…‹ ōMļ˜ÃÕïđųķ-ŦFĩßÝžũ]>î \ĀqĶ%r ējĸ<’ŽZID(üBðEØtWdĨƝFdÅ~žëĀ!ÖÐ ānZÂôįĻ)‘ájþ9Ÿ ĄÄsîDšō@ų2 *ŅŲ=ŋÞÁ[MÝhÍoũŲĒÍÕï™P&\°ŧh‚pĒĮ֎Oũ—ŧŧn­sûyD­Î xŅ­Zęā?eB†^G8_4mģŊnĸ1}―Ó' TĒĒ@‚@ėsD.\enļ°Éų͋ ÚXĨq§6u|†W@īþ’$ŦVbN]„:‰J AD(*Šw˔~‡=>—n]ĖĻzŨā{"ī]‚ĄËAÂ\T·ü**õÚõžõH?Ūxü•n{ŽuÃðrx}uūÃsËM +æÝŲhCÜŨ―'Ra†‹AÕí27ÎX<čæM"áÕۏy·V•r•šōÚ +ÆîŅ­ kÝō†ú7Ö_―p Išx%A]ũΊɧ"7éäôĪFĸÖˆÃĄaĻ”ĄnžýbõĖyf8ånšąNE—ŽĨp!‹ņ|•û‡?$ÉŠ•„ýÍĐBĻ& [J„ļníÖ'æ|ĒÁhtífÕj”ãų-žqcéRĻQWŧ1 ”ēŅĄa! {Ŋþwąjë7/íäBÜ7|Ų­Vé ŠÉڎÞ0­lu—Š\W‚ųˆę-ÝfTi“bøýĻšĒk4.f™fhÖON[xúX"CĨ\Ýę.MoÚyBTýC4īRõ û…VöΊU+g_ˆĻRŋڋĢõH·“īė8ļVXīPÜ·ī}Vófxõ›ŦTŽÎ@AļL“Ū€þ,’$ŦVB öŽË‰š‚`pîr4ŽIóęMšC€āŒYUŠY,ļŠa#ÁL!,,W―ô5@°Â·~AX†€3Æ Š|T™ŠÜ2\ ŦVŊRú`ãÁ]LPĀ ĐÚĪ|õ&—rĘVūåūŠöĒTŦûƒó‚cÍœYHĒ·DEF•Ė!åĒîiWx’öđá•oĖœ˜\˜Ũ{ýM’dÕJNC4îôf§ŧcBW%D "\Ʉ8čWŽē~z/üŌĄ$ l?w,{ß^Jāžä øÜJâBcų>îÏɊs†hDW‰€?Œ$ÉŠ•"ÜJL„’Z62))åī*ĘĮ”QUJQĀß ‚ā8`Aū/é\Š/?ŋv­čŠĸĮÞýė$s…>óA°*iŠÆĶ‹.šï óNML·uĄ‘ę UĐsĘĶqeÚ.ø2Āód.āĖ$įũ†ÍËa>ėĶAk‘ZÖį —þxÔ}ŋœ„Ķųý›ŧ°õbĖōž8\^|ĸËYq1éėwÖ9W@jéĪÉwŲÏI?ÏOŽO&þĩ#ĸ‹^zyÔýéļ[ėgiÖ Ī–ÛuģĩeíĘÕYÖ-véOuowßæ‡ošO’ū“wNéü`0þUŧxĸq‘fģŲd2 ÍB0Æø―KZaÄÁ`PDę$üUękÖĸ‡ä5FĄŲårÉĢÁ%’}J.—Û[ý―6& fŲD<‰Fcąĩ­Ž*útNWađėĶ0ždr+ĢbB0ŌCäN,-NŋŒ}ä!vB-ĨT'„ëÍØv:E9`üA;%ŠBÞŨŊlĮ–ĶgàøŦ…™p|7šeA"Ų€†Žß°Täaï苘ÍaMŪ%üM]WÎ6Z…ũ/ĮØōäýĖ ÔPāk;Ũ^YîFœí˜^Q^ü5:*!>ųŒ0Ķéĩ;ßũÕv^=RfQđÞÄi&ĩ™áN§ü‹æÍ呁!ĨŠŪjytø1Š#ų‰D‚ôW‚;ŊhsÃ=?yđ.TĖó*―ĀđÔZĸÞŨibī6ķvÕ‹@Ûģ3,.Øæt+lÎ{6E5õ°ũĮŲX8ĐoéōͅęáņŲ…ÅØúþÜqū-5Mˆ~{é!ŸiĐ·įvՑ†fwąŦČa2'‹―ĢQÄ&G{ïl%ÖķĨģĖČ9 `ŒjWÐ/ ČĮ8J$]#āœXė WgĮ§ë} v“!›ÞXmóĢA‡ŋMĶ(‡l6ĩÅĢ‚!ķ­ķ—”Õ54BôOƒs‹ÖŨŋĖÄÏt\Hū}ū…01Y ,Ldh15‹ Œ‚―ŽŌÍVž=}âu5Y€sīÛ ý   âœĐŒæËÆŨĩßŪÎ<ūõKoŊ"ˆ8ƒŽ J.6ÖĸhĪķõzItĻrƒœOïäúw­V~ØK"‘CöP[ÓŅĖōÐw7û†GFžO…1Q &kõņڅ‰áūþûũ‡˜ĸx…ŧāŊčüdÏO†ĶüUĄŦ‰Ĩ7_͎ÍEķÞÆ"ŦqÜôEõÖōã›ũ Üo`Nđ5xĶëēyeøįŧũŨ3ÚIUUâ†S•Âų”Zėül-5˜í6#rzžFķ5:8ōf=K˜]œ˜z•ØL&à +@0U™HÆĐČÁŅ‘V+‘Č/2čųgåu§ŋđÖ]ãuloįĘ6žųžÚˆ‘ŋöTũé‡A TyĢģŅi!ųĩ%pøŦ - Þ2߉ÖKWN Ô4užm)r7uu·Ÿj4Ąœ·îô•îŽÛæ*ņ{ -ūú“gķĒōÎëß6U{1€ūÚÝĖŦŦĨĢÍįR öĀųķ–BÃf­§Ð`,ðŠmŠÝßŅŅYéÂĪč`ĮųŪ€C1—ÔŠ›`‘ëHK[ũ…ģ…JÎlloĸ Sjl>ûyÚĨfY@H$œqgiĻĄ4„ÎãHņWÕ*aĖE“Ōí·ĸĻ'˜o1Ę8(ĩĮˆ†óđHGQiāPY šØįB@)ģŧ―5%>NUčÓA –`ÕÎ(GŪ…B!K(ßcöÔ/ËËÂ―•5å•Ā9Įn—7„6&N$4ŧžþ""ýGÁât/$‘Hþ_ÖģápØãņX­VÐØãj !Ųlvuu5 ‰æ{Ôŋēē E$hė͑HDhޏ'—H$‰DbnnnllLؓļßûj…ÉŽÏĖĖėĮ'&&ͧ§ĢŅψß#šŸ={&4ïÓój% 8NÎđXsQJwŠŲ;åcBˆÃáÐ9" cėvŧ…Á---qÎĸ›ÓÁëü(4ïë‚D"áĸ+Gƒ]Ėhj–„ŋĩw‡F1ŊģrQFâÄ2=QŪ25ýcÝA o1Đ@jĪ@jZ€R @Ü{#âyßĖvũîŠ-ĀÝÝî~2ģŠÎ93óũO˜™sNUeæ˜Z*•bQIENDŪB`‚doc/images/ifw-perform-update.png000066400000000000000000002464521325366651500173440ustar00rootroot00000000000000‰PNG  IHDRÎbðZŊrLņIDATxėVŲoTį·*å5Ę_žķQĢ<”Ļ…mœBX毯ÆûŠmlĖRlķīÆ6c°ÂĶ/ÄöxžÎxÖ;w™{įÎÝlÏӇŠJ’JmĨž™/>šđŸ|;Ŋ RŸÏ=įw–ûŧįHrŧ݋‹‹ĸ>ūüōKÚc:‘~D&Ŋūú C)øxĶÎīÕūÂSŌ‡fþ_Ÿ'Ý=öĘۜô&ŊRZŦ•šŌüN{<-}Ð~fõŸVøøÉóÏ?ĸâ‹/>ũÜs_/ãņãĮä$@ŋ%ôÍ7ߐ= €ĖÅ‚ö ,Õčâ8 ÂLĶņčŅ#ëĻ”aķąøSŅĢ?„>€Ģ>–ē6–ūŦŨ‡vŌē Į^tÚčƒßAŋ‚™ĸúØG‚}MýøÃéxÆõŸÖ—^zé…^HÓuýÁƒðÓŧīī'ãņøĀRð—xH O3‰ --z 6ÅtK5ÂÁfáiSG5―ˆ ŽúXĒ6úÐőđRČ2Ī―>ēe Bx–õĸjôąŸ*u}Ė)–aÉJqZÚXýþ žLaõú ‘Š&ÏŽ>ˆUęCšŪ§-,,ĖÍÍÍąxÜÐu‘ãŊ‡g5ÅbĀ„œx‰Į0 Q:§Ķi$jvtå6ĀøBĢĻ ŒDĒp"Ísk⁎čĮŲ,|K s_‚e$Ļ­æĒLzH$`ôYX\zļô`‘Īš_|ÏA}–ëĖĀĸ?ęƒYŒĸW}Åþ%‡KņØ­VKþ+ŧHN b,›š>ôš!bÐ!ģ0qðca"ļ–&ĸÏ1!BH+|qČ2,Ú˃v|īŒĨš?õA'ųĐëƒÎÔõA'ÔZZô™{}Rŋ_pŪF8Ÿ@āÐú`q‹>ˆ4= Č$ų„ōxÚŽĸõÆÃŊþüčëŋj*+$4Įõôôīķķ655ĩīī|ôŅGn·›”œÃëŸ=Ņ|8kĸ›oWŋ–Q·ņāĐJĮÄp"27Ŋ/ņ>" =MÓčt>Ēa)Ž XēEÂ2˜}ČBĀĘėHÖÝÜŨNŸYq9î;§<Š>—tœ{rØ1-ƒeŌ i?2îŠĻ:=6DaIh}Ā/ąÞI—_KVĢ5_ޜ…˜.ßëíėčžzŦŸ•Ø|‚ãÁ_dU‰ð#ƒƒũî}60<&EÁoýâP ēĖú`S‹>–ą“}õāŒÓ ÃâÓûƒ|, †"ņΑĄþþ{}ƒ žš HxL―}íōĩÁÍ vƒZTrU“·@ũOŽŧ|žNū#ÅīßúF€ũ™Đčƒ2æÚčCŊÚī>Đß/œ“æÐúāi!✨•ô#E}0—š<>iŠĒD“PUUI"ār}Õõ›uRÓЇÓĢZgÛÝ­›Žo|}ĒŋēdY&|Ļg0lkkëîîęrwûöíæææééi(Ej-a+ŠË3ģŧ>§Ēië` “yĮøÞWē2ũū9ėŌT 'šĀiöĻËĀGtœipĒaq"°Úh ŸŽD`ŅĮ\Į\ŲRßœvú(\ÃïŨmČŠv̰ j`QÆo3ęf•yCSðÉĄŒŠÏ‡'>ÉzũČ#ÁŨ„âЀ„āāŸđũųĪŽ€W?€47b ã]M9u­ŒĪāQPÍÐDÏņ}Eéé;ęÏ4îJOÏ*?80™ÓdGßļOœ3429ŒtÜ)ßē!·ŽzOUYõŅSnV1t ûEáíĒōīcðþļG7ôeņß߇$ÆFĢIýuc^mŊÎŦþðþ)Ū'A’NŠéŧÃ5 ŽÜĖܔžWūgOeiÝévŸ úō‘á ! aëņ˜ÜPđskŊŽ@ ã‘Iŋ+˜x!Ęų&û‡'Dň/Čgũ–üáōPŦÂĄ$°žKÉïĩŌþŊOŊIïTĀÕĩŋ_–{Ar +Óũ wa™ū2XŸū_ĨîŨFðĪE"I’ä$ĀŨ''.žüģŋŠýwĮ…^―üŊ΋_ŸŽŋĩæÕķŌRį#I!nÝšÕÞÞÎ0 þę‹ĒĸÉíęęË&[DÏ]nÜ^ŋæŪôũE_ðoýė}# m…-Ÿ­>ē yčA?9ĄZd`;bc LE$Āēæⷈ€-ú` Ö5†” @“Íú`/l‡ŽcĢ,2§wŪųéËéíwĶī……ĢgóÚWÞÜvĀ#ëaÖïtŒŽ:Æýސxĩ073!ƒgƒ!6äŸsNøBžfĻCįOo);106Åpb˜ Œ9 +Ā °.áPĀ Ir4Ė…–ųfɂd<V7”Ą?ŸYĸË-7&…ÅÅE-pŋ`íúŠú>äŠÛ‘YÓþYŌ‰ÚŠę­Ė/ ęą9CՐ'āóē;3éœtyÑĻĖ{ÎÖūSXsÜéaā{H|Ā9:<>=+@ŦˆÄ2– ÍLONûB\0ÝÎąąÉNn„ øƒÁ k|ŌāyŋĮĮ†Z‰œkÂ9::ęe€%Ŧ҈gj|Ä1Î,Edy0ĸ@wîî#Ìū0gĀ]füž'‚ÏĮžŽ †|nÐŌ9åæEy~>rzoq}Û YUü^? ŪhTųƒ!>ô:ԉ ÖTąįŌ‰‚ƒãþ°a<ģA–‹DYf&ĮGF>F„ _ Äóė„sl ū‘ąßú:9Đïú ŸŽšÛ°Þ/KYz‡ŅCß/:7…ûøŅč‡Ó`DøCÎ$Đ#ë]ŨkŊ<ÚđõÛĘĒėŦüķšäqnFtÃkíëŨú].Q’ŸĪtvvöõõy<†a8Ž …B^ŊwddäŌĨK~ŋŸ”!‘ũ~_ÓŧåJhŊÔp[9{CnėdßŦÚYÔļÅëó+C NEyžGNK31d~„7Į1č"Â2cÎ2§˜›N…ß+‚DéjæÁ°,9iÃFõ)|kíš Å ’&_oy[úÛģũÍĘŅŧ JƊr2ŠëšƒĒý$ŊüØ4q^ŋPZ\\ĩŧ8cûæâÚæ€ß}nÎ/Öm-Ŧ:ðГék? Y…‰ŽsLDqt7—;†ģ§ĩīĪ„d՞óó •ĪˆŽIÞæc9'ŊɊ EQŊޚ_{ģŧuÓúoïČmlŋŇ"Āú{ŠōŠn8ƒĄ@€ ņë>[™YX―·0?sË͌ ―SÂLïķŨŨl|csU}ŧÏ3ÕrĪ&ŋĻ`WÎŪó=C҈ÐqŠŠ xw~aé™OGŪÔïݕS‘“óÖ[;öļ&éáķ#ĩđY™9;ĘšnŽ}ü~å{W†YļvîøöĖÂōĒü–ÞAXýÏŪ5æäįV>ï‰IDĒQÏ―îÜēC7'‚L H|ĮŅüšÆŦQUáž#5ųŨ‡Į>Ž?PZZôÎķĖc—û5]:YWräâ %â=TđįâõŅĻĄÝýSmų‰ËĒînn(-,ĘÎĖØÝÐåõOĘÞžnÓķ‚ŠFW˜k9XvĒýN8Ė}üĮšßeï*ĖÍ+ĐûÜũ―pņ>RĘ1˕–c9š&S3ãá /A4RÂGJ™5Ķd1=|MĢ5Ķ6ljƒ Â.ČÃGMaNÏДŨ―ũžÏžĸrjŊģΚĖ՚ųėõŸ―ĸĸßßþÎö>Üŧļģ>īÔA$ēlözĒlVŦ(ŸaŒc‘øŦĐŸČ žē,“ŠĒ‚‚‚ôz―î'€­(Š?Ĩôü‘ž0ĻŋތtF‘ÖKðĢ3Ģā° Ģ•âŽú†šÆZeZžįáRÉI’+þÅS ’‡ ‘BUņ'ęÅ* Ü`ˆøĻC`ˆ\*œÕŦÐčĢÎ@Ô=áú@ķDŒŸ6ÅóŊ3ím_ĸöĘsfYąČ‹ũŒ›š_°*#éąÎ/ęūëd(ÄH―0Ėy―ƒũLÖÚėï7 WäĀļccĮN^œ•>j 1ōÁ)/Ū^•‘8ąó|ýå.NâET‘(‹5þŲė5uąņR—O&ĀMÂ22þ&TÏöņäxŸyĀ=ú^ÆýÐØčČņIKį?yŋ,ō gJo šÚ?Ý]\ôîÕgŪ@Ųtuv –ĄËōŨLĨŋhiĄÂâĶÄ9î3eyúÔŦŸ—WīēIiËĶ=<ĻîdÅmž’<7qÅđ/$Žäácøˆ…Ŧ ŨĶMlo<ôõuŊ r8tø’õËÆĮ ôuy‘Ē)ÏįM_†LXžŋzę1Üí;r2æņYđéņÞÖږÖ+"Æ€dNūzîāŪâĒũķŸi퉛8Ūí|m{·tõ‹Š.fhtÄÃOÏ{qMÁģ#ú_øĶ%ŠĶi(N˜RôbÄË2–Šá%ŽĮÃ'LÏ+(ȜóÄ­ķonãþMŸ|ĸ˜IK–Ï 7â^Ņ/äÖS›zŊذņÅ!Ļí@Ųiˆ~øÖņāÚ kĮ āÃA’Iý€AĘïëZuå+CšÚÓ<_‚ $'īęä'S“*U_ŠÍSCxŒÄAM˜Ð&Ģ„91 _=ŅŊÖGcÚwŊļÝ―>4ĮqÄIé6GFrAtø 3 ŽîE18T?h0ĶiX „fVŦ!m@@€Ýn‡MÖfģÁ> =dâû|CC°Õ>Ð:`°-2Ü60Äj7SÆ`KM뀘š%Ė6ī'ĪÕš“üЂ›zã#ýĸu·‚VcžŠ|$\ĢÆVŨĒâ/Đô!ôČ Aîdž'@sǐpu)ĸž>‚(@v*thœÃÞ]VęęÅaũFõĢ øĶõ\UŅæ7ũ|vš7(\eØ @ð‰ĄĢa$ŲdÕI,+H‚ÞŒyČ,0ΝÜō†?J0(™“16PþQd!ę”(™­:™g!•B€ÂRg7+G?aÜsó{d2Þˆā,`žŽēžã9–Ņ…MtæäåfM3ČŨëÕŲÂG<ōPh‹AŊ“&e3 Øļ|^Ÿ(ø˜Ž’―ÛOžîˆv Ķ%V ­qĢG‡ZE‡ņI”#66ÉýĢb‚ÃЍn‹Đč‘í”ČēF4Pôēķ™óg†^Ū*ÜļíÄđïšŧn1ÏŨîŌí{Ke‹#ČĻûņĀ}g9ģã·I)9y9™ĢĒmúþĢï·xāŊ—ĶÓWGNšhļÕ^ũŲ_6ŋųŽëâu‹Íâ'I!LÉp_)ĪĮP ’ˆĒ8ï?Ŧū]ôæžēSČfĄ%ŋŊI'ō,ËÂ}Âā$y<Öâ0ŌÝ>ݰčÁݗŋũ°=Ø>ėŅ1ĢYMvÂęúQZŌIęG)şŦõNPŠ耡9ãĄUŠKSĨJ~RĖ‚ Īþ5!$‰†'Ī &ŽYĶ2)ÉĶyū~ĩ>šđČÃõ?Ũ‡l 49ŧ”™āŨũĀĨĀ~LÄ`44  [­čˆđdĩFÅéÍf%ŒœðJ c6›­*ø|>háU…;JæĻXþŲ$šBLýC,BĖĄ6ĘÂvp‘ö{u”NIH˜(ŲČČ%…ô““Gà  ČéGb‰?Œ*­úpVŸíä>iLjēĘĨš91ÔQšŌ'ęä ’D đģ> w~(ŌÞÔxÞäĒį1mĀ―—ˊݏ<—’6krÉ%K2­Ģ Z͐Nâž>ė“š@JfnÞō ˜ël?îŠ41-ý™Éþ €D^æz<Ž Œõq“dsDÝwĄåԗ—n!íéhŦk<â ƒ―•éđq›ĮXŅGY"Ēiû·ØvyQŌ!Ęυåüī( Vˆ ˆ—š9A2ŽaöČøī _)Lĸ―^`€ įoŽ (ģŪ]ïa|—/\ėõöŧ'Ė 0ëaX„ņ“]ø^9x؊ ë&EGĘ[ ֐ ÐðQÍ\đzÍ+/gŒŽē žōžÅû‰éi8rxā“ąaė„‘%{Ï2áŋ‹ðu}YÝY69įų'FFq#SXÆAĪ úõ^ŋŅŲ}­―åÂUŒŒ=Ũū:^Þ:avVĘӏ#ž(Dō0þS„Į4)dī…āë·ŧß­óß^ģĮ8L4…dŨÃ0p|b„ApUý@{ũõCĒ$äŸ/Röğ<_ 4y”IIâŊfĻ@ýzHæ%! Ô!šĨgÂðÎú(=?§Œþŋõƒrđ\555ĩ}ĻŦŦƒÖ]SSšeóÉUųwíļyėčwû>rŋžîðúĩĩĩn7øƒ qŧÝð?g•••ð…XGGG[[ŒÂ§·ā ļO0Š]U|ēđhîŅĶ-§.î=~öâ’å[?\SYUy~ôĐŪvõD‘$Ä CdÅ ē ÕŠî īĨ2ĐŌŊ™B―F Mr ͊”đmÕLÔÎ}üÔúT•f.,üčÄWÉû;wv5Wþ―895ŋēĄúåĨó’æÏ[šž4kNnÁ[Gw8―tŽšĄäϛRóĸTQŨė:ēcQÂĒËjęïK~nķ3wÕþēã–Λŧ`~nÎâĪ™Ïå­ÛķïÝŨÓ 6Ÿp7*~ ŒJwKõ‘íÉ ÎĻUÖR[ßÜÔ°{KÁŒ„Ų99 ãg˘ŧēīálS―kįĶåÓþ0įŧļjýđëë+>ý07~Z’3-=Íđ(uÅÞŋ}š.cQá‡åÍõÕÛV;3Wlv·4}ķwkōĖé™ËķTŨÔî,ĖKJœ›šâĖ[ŋąžšú­—–l,:ÔØÔÐØXŧĩ ũÉÉOĨfd$<ģāÕâCð ŲŦyY/mÝY[ßānpo[î\Vt°ĐūlCnvzFƂxgá;ꛛü 9!!%-ݙœýþūÃ5}ũÓOėĀ.įė§’œé@,9míþĢUuå%Đ3_YžĸÔĐæš’ũSâ8ģē3S§'&ŋöŨÛVd­kwCKóŽŨóį<;;53ké„ôÂũÜUĨų‹Æ':33–ĖNŒÏÚī§ėÐGÎđsRs þQWŧ1/sýÛ{›vŋ‘3á‚tįüīeŦJ] õgĪŽÜýɉ––š Ïgü‡œģų‘ĒėĒøü§„wã֝qĢh1j4d Aó Ę4Ó,4fzš™ĐŪŪïŠF7ōũøë9ņäɓb(&$˜péÜđu?Î―ÏĐûĀb:Üšũˆ™ vzlxâ͙ÞôÄþ(mš*ū_6ĻÄQ%Dhā8äa"§ŠĶïŨ4ĸ ~ÐWðƒįhdģؐ‘$ ĩUU,ëŊŋúãÖ'Ŧ/îd?ĸŋËsŠIKÓÔ%ŠjۖĮõzͧ°Z­@čšt2•ØhÚú·įŋ>8ūóÝ/ĸ°ø|yōSQgåķfT"ÛMB{„ŽJfĘéᓀÛM=€ü2ė7°Ņ<ylÁF§Ð+ŒNņ8ĩōĻ—kc~-øa蚹ČĘæÅnlŦ<Ý6]ÛmóĒëZ`ËķÛ6OŲ!Ė>M6eYũCŦŨ;J·5‘*Ï6ėûÐwlPÝ }SįYQ—Õ06t*Ŧzŧ,%§ę‡ąÞææ‡ÁCįõóÕ#þŋqãæĸnÞ>|Ōî^4UVäL)~H,ûūŦëŠū”"ßcß4QfęëšÜē!ĢÜ$ÐVŧą*č’6ãísꩄŸšĘ>ýðƒūýąāUÝ8ķôĻęnlË4%=kÚķiĘ4+ÛšÉĀŠÚąŊéCW_\\ÛŠĖ3ŋkÛŠŠ*Ŧj?fv}M #1S–ãŲ•|[5M74gę:†-pðŧáqhr8ôßôŧķ.a§jÚū­!ŠČrĖĄÝ‚ ]%įĘōŠĘœfn‹MšŨuŨ7…^đ.2đ?š\ûƒíl‹JœFŽïūōĢĒūa4jAŽ†•dō~)™7b§šLÞŊ`Îđü*âG2ŸųŊÁÏg;??õÞH’Ūïųîß:Cŧ؊žaĪLŲėŋeÛ[…äq$™ĶÄPšŊÚĪ›tč%ŋüûå0 â!ŠV‰ īðuwČķs4XØÎsF…Œ!;Ų:,‘gRŧܞI~·=†Ŧ` ÂăžäG-B~ˆåčc#ļ4ô[æv†<2Õš-gįŽ õT ]ė õS}…gč’įE?þųŨP<~øðŲïŦd“ōI7IČ:äðŌ2C:Ũ &”-.J‡"ÍAŋđýŲ—'YާŨÆggx ÅØ‡ tWZ`FKĨ $žS邁æ#šŌA/EžÄw•xõ#Á$pēT}Ļ9;?KĖO°?ŅōxēVaļ?ŪR4ޟŨÝ/iƒË?đÏÞ7ĘõN1žüŨFøFģ?š_á)ĒĖwËU3ų9 Ųéé)蓓“õz―Z­pbĻ@6NyÐNbGÉhųB\‚`Ļ―`…Ŋ1°Ē4ŒGå*qkeĘķķA45”\h.1ūĒ?>Ņ~ Ĩ71ɏsÐō›Zŋ–ÓŦG{ĒģÏį'.ŲîhĀi~äėEZw]žœãšäGLüPžūz„oNV§yū­óD­Å’'ų Į‹øqZdĩ6ŠâŠ6A‡Š^„_Ĩų™ĩ?3øQrďÂý™äãÚüH&ų CoÍPCMócīüĖßæðƒ,—ËÅbņtņ”ōlų ãčÉ6r˟Å^ãY-Ž—ĮK<Ō|ðPBi*qp0äĪŲGGG<*>ØÂ$„P‹f•Y…$PčQųL ĶĐ<ƒ3 ЉhVĄ<ʙäG€<*‰ø t!ßgŋšã7?žĮü(t?čI~ôŅAæðC‚ųĄęMø‘3āGcü †x3~xe `ü` ðãdœĀā:uȏüēŏtp-ˈÉŨōĢĮÉýQ:ڍũ*~0"~°Ãwí2É !?ōxDë}=~@ŧš!‡üč°oʏ ]„ųü`„—čzüā4?ČŦøA|ĸŊÜŋŒÃÃCĄDžÉÐ―K‚-ŋĢŌ“Q~äläÔĻs͚Lļ{ũŪĢsä­óƒį―âĮP‘?æĮž·ĮėÐđũß·s.?ØsøąÓPï!?Bļš@Þ?ĸ°SĮ81E=cīë äþ§LIBKƒ’úïuėâū3@,€Å ĩ@jĪH-ZĀVü+đÕĻÞŽx‹”™–Ÿ gM·yūNÕ­x%É+ýÞ2;Ïž[—ÖGĩÍ(óļŽ/==ØûĻĻŪŪŅsÛ4ŠÄÄÄŪą%ą$ƒ&1b›Ą( ŠĒąą@D•Ē4ĶˆŠA *J—^f˜rÛ;wÆBŒßúþïĸÞ[ë­Ä3ã―wũ―Ï>íŪu>|ýï(YUC\O@T\̜ÅþE>!oÂŲÕÏM}ÕÚÁųĢŨĸ@1ĒģîSÎ+XŽÞëųJFuoԚR…(díõuuÍÍ2éVđ)ų ü{#ĸÎa•ŊŠ+ËŦ_)(öËĸ}Cþ3RZ™Ÿæ}æZcŊþSÁĻ–ē!åðđšF)Í"Kí?{TÄv„lž0fęĒ›eRã<âŽĸŌ)Óg^*lčŋŽēųÜ^į}§ĒTČŋ(ĩ,MŠ]]j†ÅpŽŪ8ÕzęT Ŋ+ bĸseh•RĄRÓĸEäpLQížcýÎ#Q·ï§ĮœÉŠĐU Øëa8.{UzÚeõļŊ'˜ĖøiâÄŊgĖޔþļ–Åq!ŅH'éũ†q"Ī}ũ&›1cÆ 2č‹Aˆû)xėÃĄęÆįĸvV ŲÃzÞģY5*!ýį^[=v˘‘Ç zmŠ$õq ĖĀĸéP”Uæ]ũsņūZ+§Qô? - HU§BÉ@5T^óĀ~ĘTS—ÐFMųį–ڏĀķåUMmeƒBÉ ‘75TÖÔķv‘(Šņ5‹NĀ9ŠžŪĄÝį­šš:]>ŸĶšÁ!-‚ōø]UOG3ŸeœÏJCýÜv8ŧ—)| éi0Hba9ïŦA8„<Ãpž.Įþ]“ƁöAŽ~°9‰īÂt…8'LŦ•VÓwŠĒ8!ã‹tā#Ÿ€zō…"øÐÃ@āáÐLÝîfj—G &w“Oāokœ@Նč|ŽĄP€ūS›§ĢÁ†ĖÞI p 㠄ðÏIÄĒúü„}ÛcŠZõD|­&zZM8ΝÍϟæ? ŪĘųãi^ÖÁ1ƒ„4Åhj&Á4ŋØĩxÉNßKÂA“WnØ4ũ‡ą59ᖋWÅÕttërŊšnwž\ÜŪ'äu—…ĶĨĐĄķķvĀčI&ÓĶB7šÂ0΃V`œŋņqšœ8—í[bKĪzBBÃyë.üĩ‚Ï ųÐY02|xWë=ãņ…œ1B.Oī^jž 9č…,Š7mÖ\ģŲ3čáÃpOuī‰%"ð7‘Å8ž„Þ_=FĐ:jjkŲ†SĶ™|ϙ2ī·.L4ūHÓð5:ž@ZMāD"Ũü9ÍYȞC3ðâuCÐÆÁ ŸŊ•ˆ<>l& ŒĀ”õaĮvnŲsFÎBMzÆó,,%“Gč`€eX˜īB‘VŽ&i5€qîåCũūIKüoXj?ŽĒ ÛĖÓÎ4„UŨ_ð=tþzņý$ ņBû7K›PŒ{ŪjzzlŨÉ|{ŸĀ”"`:Ē”ŨƇų-^īP,6ßtė\S#äŅÏî'9râ\L”ŋo@nY#ÖĢũ°‰ãūųĒ/ŽáŠ–—A~^SýqũŠ­™dđƒoY…c(Ž2%é§™K–­öJJI ðóžVøęÏ#„†gŲ{—ÛŠįK,;˜[ŲNy Ï|ö,‹ÅKVožųĪŠ ĸÕÝð<~úfaIØïk%f–{CR”ĘĶ`ũm3ģ“oą(U6Dœšœ}7ųWÉÂĨkSKj1XÝ1‚jŦū|rrī[–ZDĢÐvTQWxÜÝ;îfNvb˜D"qr Ļë`PÃqęQæÅufbądéĐË94”ŽbĩI‡―N=-‹;ĩ_"ą8–EbnÄŧ•Zc ŽÍ5]pâRbÁÜ[—^žjizžēŅvlu…uíi׈>™ø #øme7ŧî°63ÛŸš˜āĩuŲØ1&‡‚.Ī$&îÝžâv…ŒĮHƒÎú8ýūfŅ’7ne]]ķf۝Š.Ðö|ã*ÛUן”ÖŋČū°ÍÞÂöh<‹ņ-ĨnGöŊī–,s<u+í„ÛvϘLL@Īî°[ė\üėœŧÎëmÝĢ ŊåyúĄ;æüdęä’‘rÍŲqwR^š―šēĒԘ—‘’Û.o‹tĩÞâ|&_JķÖäg?V°8ún*ËĢ›ó7XYýîęũēąĶļðĘš%Öû3e‰ņi­ ›*bÂÂs Ëi Ũ’0jŋ§9 @,_ĩtž‘^GG‡‚čkþŦÝøþXÅãÜēōgՕŊ ôšûņįSóĨ*öý"Åē•>|TŌÖI"(ÖXzÃÃÕÉtÆėÝĮÎĨßHØģqWęÃ:UkE•–OÎÕóĐdēæ įåë†æŨ7ÜŧqÉÆfsfYŦWĮ^<ãėšÕŌÂ.*ņÞ+YGóóBow7{óYŽnwŌ“ünýÍᔠČJöyļ,·6Ûx $ŋø#ũpŲēïÄÓ6šl*Ų4ËbäõæÎŪ7⠞VāBaenī―ĩ]XF^}mÅqį•›]#?TgdõUE%EEåŊš0 ­-I:ėēųlÆįĢĨÞŧ=2ę{ðĨg}üŌîQÍ iĄÞë·>jbøoĮÎŊģöaāA·Cųjgŧjž|öųŸ•ŌžŪĶįÞ'*[ÚäeDÚūåø…ķ.UyyI7?ėH‰I~ÚŽĒĪ/NlqÚwþĶåUåģģķō‰Č*Ūk―xĘÓÚ|vä―zŸĻ-ūææēuĶɜƒþŅiIq;~۝UڌĒč?§Ô~Žĩ"I~['Ÿ9)›ÍÆH““ _–ÞKČÉ.é;^’Vð$+áÄį<ĀhÎ39#&%3&<80øÐD\õēŠĐƒéĩČÞaĖP}]ÃAIiķ&ƒIb ĀY Ų%#þį“ÛC’Ģöã ŦŽ*ģąĐ.îäŅ6UÏÝþ Ųwó\—ÍdĄ °Ýô";kŽļŋhEW:_ZVV’Ÿ:w\ŋäÏļ‚. §Seđ·nœØÉ“Õúŧų―T"8ΧԠïĀ™ĄĐé—ęNÖŊĮoķ›Ū’57ČTŠP xËuGïßđąfŽ~Į“ī܂’wâ/g<0{KzáýäHßþ"ųÝāCųĩ*>‘4%4čävÅßĨ' U·uū*ŠJ(—Q3’‚œ yb`ŠÃ1”O‘LĸOįÄ$ûîąbÔEž—öņÓÎMó DĀÎ-:ųØæOøō§yÅjÆhåâ 1é%§ķÚö` ûÚX>d„úÅÝbüýæīôÜŽØ“ƒxu'C#ŸĻûEÛā“ŨgdßÛ°ð[ PēÚ.’ėĻyö €á„‘ũhŠî­?dĀ@ZqŧÝāŧ=›æę‹ĀōC―ŨĒËŌ û§Q-Pí°ųqÂĪɓū™”^ށĢA4Í}.>{9Ų{ŧ„V<ÞgŒéÎ ģûАž—’|6ðk3<üS{M]ž—ėūJöōZšÅhÍA‹į.‹OÏZ9ãKŒĶĀ⟍ũ‰ļqëĒĮ`=6ŋøęĢęNį0{ÝãpԈG"$h†Pió‹īúvvČũ›\}n=ČÛ`>™jŦžqĒ ķcĢ_äKAßÂŊų&žR3č_fl7‚wŪŲÉČ,’Å ™oÂĄŽ/W’Lĩ˜Õ z}ņ•x,žÔÕ61Žâ€cÅ0l·ˆb8_ßP€ęYŦÖŲ0jĨžKÖã^B>Kkļs4ôûųĀē!―CŊ%\ˆū0ŅPČĨCąĢ•û7™Nj`lŽĩžą0Ž―ą -…Wí­$[―/Ā[…m ŦŅ ŽÜæ2aØį}uZôo8Ė0hôtóiF}X’ĶÕ#ˆúƒÍęÅbÓό t Ë|ņݚy“@É5›ųÛßŪ—4Z]_^ YEÜ,YâPXՅĄ…•Íjšũrû;‹_câ“/ÅDoZ0‹UĐGLþáëąÆy“<ŽHļŲ1PbõíJ!ŧķŨĘ|þė96QÅmþĩrÍ6ũ‹ĢPˆPķ―"ėĀJɜ9–v[r倏q!åđdįĢ(ËtëĀÔʎúŠJŒ§ŒŠ$y“ÎéĢÚ;óÛ:Y”Ą€`āz&ëkņ?ŪÕþ ‡ŪDÁ­ąV\(ð@cĨfhõ›’‹ēðC–i“ĩĻP!(Ms9DËeŌ i# ļ %ö\z4&ï[óíūŧ&n@‰Ģ€ei–…·ņ„ý5…!Üiøobpšd]@ŲYSõJ žŠˆ…JZßĐh€Ā  F2ψģĪ\­RF‡GhZ0ĀcĄƉ$)0šī °o%gŠa@ZW /p@ \uRÎīĒ“Tą,‰!Ļ–eHJsLôëKŒeĄ(nįļũ؉ ôĖŧąaG 5ŰÁ![–"ßŲĻõ•IIĻ;‹L·œĖļ·{éũÕ%YûķČ(ŠíķWŽ”ģ­ēĶļQĪîÅGyÂhûŦũ#tŒ?éÕëé$& tĖĢŧ)Ĩ þ&ƒ 47 R))ð>ÔĻņióŽLįĖĩķZ0ÄPïĩš,ĒPõž4ÃĘΧ‡Ũ―G˜8<““ēöG”Ĩĩ1Ĩ(%IC\ömAGųÐŲ^§RQC―Q‚ĨÕjRÍ!ū“Á`zŸš_N œmŽ“―Ú#ļīUMð9ÎĶKžŸˆOĘĖJo€w§Ó ŊĶ˟?ËrĄÕŒIc„‰Žē?AĀÖ{íÚv%ĸåâ•æF=EW\·ŧ‡W+'Ÿ Z>Ģ/ XŒĀšŧ…e5Š#„PÄŦ,ŦPĐHÍR/trĨ[øÍ!Ó֝ uÔu‡Ã—cQ4‚wO -Îä_†Ó•ũ}ž~;'%áō―—ífÚACLļBōõȘŠzjðŨ ‡õÁ)–ðþhÚ üŌ6xøýV.ŠĄ- ŨccŌÏEÆÞÂÏÆ 9hð°Þ}î\TFftxxfy­Čpôg†"Šb8rÍ@~ҚAqŊOFM2ЊŽÛ…­2FÚXZT\$ŨöWPÍžŨ―1 ĐR‚Ž3á‰ÏŌ[Q/;ôLmŨ̓0ĪĒCɀ7Ňdø_O­‡ƒ'QįŪĶä$G„$äĢã‡éÏvŊIÝ„ Læ-ŲG''ÔuÏŅ€ûđΜp=tHÚóģ.6ŸŠŠ!•Jp;(,ĐðđúŊR7Îųs‡k„ÆÄ7rÛVČ_­€vÐ,ĐT€LȧāĐnĸ/G5ÅÅũōKīēüiq~yÃí˜îÃn €œīËŅ)™į΄æžjŨï1ä C!Å0ÂcÅūî‹ĨÕA17˜OĮŊÚģJ’76 <ýï~ãÏ―Âōj•RZõ°0ŧ^NĸõeŠį9ég‚OÃģ Nûg>ĐQ)y?ýj1°Ŋ°ü^Z#fb9e4Š0rzÐixQ|ũ~ŧĒđŪ ĻDÉžfÆMųø:(UæĶųzZ­ÛŨÔ‚AeĨu,čĐßœ—ĸĻÔýqĸŅ‹Ze)šVw•ÅœŋTÖ JirNØó“I?ĖāqXP`rĘÕāĻó-j0ük CKkRâü_öú4@§,ØqdƒUYRč"‹ųævĪ:_đņšhŒ“4 P (šRNmĩē]™TŽķw|iôųĖ…;6ˆũũĸūū°ÓpæPŒ Ęë9ÃvÕ@Qå-+NåĘFĸ4Ą-ûâŲôRDô™ãŧO•þŪŦnäĢ„HÄøwÓU–ÏÂ`ŽvšFôģ?tæ·9ß݉‹zJηšĢ (šíž…,‚ëŊô Ýķdú―k ÉB›e1YS­·ÚđüÉĩ€âųũ_Ÿb―ýøaŧūÉ0šäaoÆaXũ„Ä0TSj1u[kÐÖ]Ö63 –ŧxüǜßZœņÚ2ū:eÍņē­>ý'.<å=ēŠĶ :üÞ8ŠðD!h@Q|GWïíËf%9›KĖ,-íÄÉđdÍžįī†6rQ„R“#LlĖ&ũŊ|zv­{D]'u3â°ĩđ™Ų‚%—öēĸÝiú8#†Ē_—Z9ÖÜņôą]Wė-ŋŋ, u[7ē/ÆĒ"œX‚yŋR’īþčþps|Ö7!n[ĖĖÍ­áNKøyÁ’ÍfSĩräôEfú―|īÆ#Ē^žÂi"€`PfĀþ-ŦŨýķ~ýúՎΏT<؟Ž@1Ί/gØJūÖ/{°Æ=BÕïû QgæÔßĩV"6ģą_}ûąf>ƒjĻ+Ā߯ Ģ^>ōt”ŽqņW 1usß?ļKŌ\ tÐŦD˜(\6°Ã:k ÝŨŊ4‹Ŋ= 2øü°ËÚĄ†―~īÝíđŲæqÂé%ÖfæÖŦ6VHŧ—Zm AîĩČ kýÍa=„ãW Xšė5ræ’GÂGóėÅĢõV+Ļ/MÏÓŦčšįŌ=Q†’ų@Z|ņFp^!͐ý†O5ĸy<)MuÚæõ­ÄFŋ—(‹?ÅzõðūM~ûÖļ\~:jÞ4Eqē|n0ÂÎĘRH–ļ:,‹ÉxŽ ĩ ÏR˜hÞŊ{ũ/7Mõ0›gæÞ`éāáūKĖSŦNYhi·$Áþ·5ģå#ü ĸ‡―Ŧéq›ŠĒ°UBĨPÕŪąBlø;Úŋ€-,ĄĒHü,ĸÍT)TT@ĨĄ‚–mâØ“L’Æē8ąŋ{ŸdFėxš6/ĢŠŦĐĖY―Üũî;įÜûlg9H)æQ·)_ûíŨkŨohÖË€O:óôãÏūvújÃܔDuŦ#E@JbK‘EđîúĄïčší$9@†(Šý 혒 n€@b쭊ĒėļAž„=Mģ]?'§‘el›ƒ€°ÔdŽģøNŸ š―­ŸÚøãfMŊžûð=vŲ}ē.N $K E ąŨÞTjÕŠÚh ƒ‹‚bÚm7j‚$Ŧm/‚ržIājÛFߏ!dcÍðÃ"ÏÝÖīq^”ūúúĐĢÏŋüę%ÁjȒēĨ%dî“- ïo·ÅjMT6QXN FÅĶiƒá˜fÉØŌ5›ų"Θ”Yė·JUĻÕ7[Ķíe v/L ô‡,qD„Ĩ Œš(6š―8#ūk7TųæÍęfŦ›Ā|–,č„f=Ģ%Š‚Pßīýļ`öäiÜ35―7ĖČAĀtR$þ@n°ŋ"ĸö›/NžtüÅĢÏ9ōܛg>úŦ3J=ģ.ˆ ­įp7‡d}Į֖ V˜’$hÛĶ1$ô].vtųö>X–åļoĻēT­Iö­Q2;ŽĨév”儐ɌÖŋûŠ}3}ëÜySo UąÓÍ!iäÏŦ: Ėõd#ËÐM;JIΊ|ŦS—ŠŽ]Ã!Hä‘Ön bURšš>ˆų*Ė7äᰖ1ƞÃ>zA û\dät%Aūeđq(b­Ąũ“ŒxK3ûq–#ÎSTđŪ6u&Į2·Mŧ—@’ZZS{ČzëČĒØu@$ÉļÝTdYÝavē˜‘™ý+- ’ø&;!’šĨ3,ũy}V^?ž—7ðsmaōˆūnæˆHũûšÅ·Ų}îä;ĩîNKDļģp°ýF.šXŋī˜nbuŒ0)áÏĩÏŲ3ý…ã'Ž=õãåÝO[nv—›Žj8HīāZ āĀ‹iéÕOō‰c'Ŋî”ģ’ÞYĀ9âĒû›ðHb€{°ŋ5ðÂWŒÞW Ü·ŦEYĪžuáëģœ~ĸãÏ.FXÐ%öUpïō.VÓ`EâdVë_ēߓžqv hÉ āŠÃ%ōĒ8{žNü,ß&þ„Ž\šlze3XŒø<þÄ,Þ넮\M@øōþonĩ‡’zŨ/^ø~ãw7"ðXBŧsĨRY_[[?_Đüø‹äƒãÃÔÛļüÃĨËn‚ōÃtšûũîtZN'ĸ•KĪā™íK•ĘÅ äķŠCūDņ8ÝÛۛMČÃÐbķ·ĀîŽĪšgŧ 3ōČþaį vÜ(‚0<ÉÚÁ‘ö@.(wĪžð" ž !)D"qπˆ=ÓUÍŨ]ZbvÅVÂBý{åT—ŦĸŠ™_g―č?~·ęŨß?|ā›J<4P;444ô@ĩCCCCC:]Ĩ†j­ÓUjhhč|lLĸîŋs>įĢŠĩ/&L‰pŽąŒT_EˆþZ@2‚Ë8 >·ôƒüE|ąąEąŒ0ō:†C·Žų‰"ˆ;t9ðe|YððZÐÃ;ÎÝï9·E$/tĸÖᷕr>.ŋyó<ŦĘāíÐÐõAv*Åķm{Ôōë)øþŧ_―RÍģ›Ĩ”ĖŠˆōŦïÚĄiįpÅ=į\ĖÜM“îRû˜Ĩ$"‰É0 ðBy+.%lŅšŪ8՞§8ļ™hgfdčČ2b’G ÅtdÍđšáM2œÃķ8SĀČđW",7*q(ļđã•zGŊž5G_kĻV>ށæ,B={― ŧĘËõLĮžށŸG4ęÎŽ*&ï{ĸŗ_}ýÍ·§ÓéOÎōo$oÞüÜfš&™Ų˗ŸŸl[ų·Ļ‘Ÿ^ŋæĮæO ÅfŽQĶKDÁZfŪ*Ûš‚eyR†*ÕóžLÕA qÔ°‚ā[‹ŠâžžOcß444tR•·oáįĄŸ=;>ÂĐéœoöčŋ —äđ:šį>oĻ…JāDö{I;–äÁ„j†t –8íR1hˆ…ėtģ$*éĶz{,ˍf ļäœÁdħuÄë9k—žÞ;:5ð ;ę9ú‘”n=íāD•ļđ s’d€†rzģˆũFníÁ›$ä:L32ZHÂqÕĐÍļĻåÉō„ZxĘß(ôÓ* YyĶ ÓÄɚs.ƒą–cHŒyÜŦތ1AíýáĀüņ~,ũ­NOũ7‡ķļ‡Ãþx ]‡Tõp8ļ;ņã VDTG˜€;@‚˜Y2ėžûĸqxįubđ4(t`ØT+Éâ]’°n§Ð‡ …ļæ*8oį@‰Ģ+æŲ)ŽŦŪzžoÐΆ69Û§ĘS#ĶĨ+éļg)_+Te˜Ûß­nsžÖÎĖq7w‹rĮo%όŠ[Ģķcfq fŒ8(óDēå'€ŪqHŸsf?&―ãlfwÅļ ÜSŠ<3Ũ§­ŽØÞS―Ó444tŠ]ųëf`ô!8CO8›$­ÛFÚĐ­s–@5SxԜE„˜í •?‰ãļjļUkÔb;°3o‹nĒ žņ‡JŪĀ1ˆ.ípڒQIÐÛô įāZxiA5YœŽÍOąķSržŽĶÔýÜI ‘5'yXą‘Q ŧ’ …Ņƒ~w”ýz%ĐdÜāP%ļ›ēïŧ’ˆŊ…Ōģ^Ģ-ŠJaŪE`”;ABRL‡†HŸËô†pŸs?†fý!!ĀÚ'Ĩúš}A’ĀŦĻjOœ8Um–W[OÄ=―ïqųHÅŪ5éēũĩė—áÍ2%#Į0Dļâ1\%‘HRģ՜ í`SÛdz ĩ(‚ ɊmeĮdKÏÞĢŊðÏĘ™Í5ËiEx_3ĖŽþ1Æļ”ą;û=Ã3XzüŅPRÝCKlYÉĩf,G$Gemcô+§$HÓŅķĀͅzMÛT5NŠ*ŲũÓú\å)kdF;6VøŦ38qâĪZÝ4ŧ™ŲÖÍ"8wR/Å_’Bå#–Ŋđ:á~Itú5EÍR2<Œ:tŽÕį‘ŽŠg­ĩf/yŋ^Ŋ>ķ-E:ššMRð˜xpˆĻ ģ’°-$ÛĒfDđ―ĐDJtlD V( Ī—ķ5ÕĘόhéÚÃąą2Je+SÉ ‘"k9 J%ā•E#ĩ.8”žTėaŋ^SDņXÓrŸ%·ÉŲũ™ J呕*'Nœx―Z ‰bC?&n8äd‡C›SdßũČlJʉÚQŲĒĖŽū.„œūŠøPýŊā-īļuģ­)ē™šĶjáî+\_î. ’ðN 4ņÅrũ1†{D†ššj]R˜)Të|‚ģ,W5›ÕzxïKBT ‘§fDŽÜW*#aĀaÚf݊}Îåkl#%}­ DŸJðîîÅ\îkE‘øŽWĶŠĐ-_f:ķąÜį>Ą$™râĉWÐŦĨØØæ\T 3Ũš=ŅĘ*ŒõãörïnĐĐVέaįœ„Ø)ąvWęcÖ+€ۘseæķm\ũÝÆ ŠŊéáCKÃöžˆ,įwŨJ°JæÚgýâ…d.GæĻœ/n €å5ÂŌĄĘš\AM›‘)PZ$Br˜­Ēðm QŪ}FŠ*%ģxŧmb SmÞo)ý44Ŧ,W„;•w6æí’„Ų螥 Š)§PÎĩGdMāÆ~SņqÔ|ANœ8ņęyĩ[W§Â)P\&ÛÖCóĖRKsNe ÔŒ€ Ëj G;V; ĪÜ]^†™JŲĨ(ÁœÍ­Ō°k_3ĘˆååThBVŽ,?!ÛQ…(Qéą(ÕāĐ īŨŽäĢ"/=;ũVÁ[vÃãrđÄũ™"Žëžą”š]ķōœģeęž° „Į‚RîĨˆĮØ:’aĨąå(iÕõ蔂u‚øTĩ'Nžš^íZģ3I)rÝũČę†53BīFö‘2ƆŠH‰Ā†™ŽgĐR EOĒC§ÏęėššÍz4?ÔeĘŅ_ėsz7ząŲ°1ŪUvhĶNwķĘUõđP3ŌüW2%gĩŠaRtŸýFmû‘óEįī:Ĩp―z8•R/́œevũvsVÛ}Ÿčü™Heŋ0†Q‘™ö&°•Ãë]ÞĨŪĩŪû=Čír™k-oŊytX82!'Nœxõ ÏÛPÕëõū(iä ŅĄ{w‡$Ļ"Xûę ä>ũŽqVÖš—ÞãË%ép–Å€Š6jŽäq5Ð.—yÝĘyÝUĩhTŽüŽÃ(3Á~―  MūkĄý)đ܋ !ĖĻkSũÕŊHQn"„ßwÅŌ–tfQ}%Í,2 Dč>+Ō;6/'WęÃĶG‚―Ó oÁŽÎ‚–\UJÃTĩ†xYuaŦÕ6Đäxū%^úĶÉįãĉmfæKĶZ%%ĨCHŠę%3SÕ"ĢM†@]#Ō ėĩ1ðïŋÛ^Ô:&eØļßŊŠĐĪ4Oe„)AÕĄVV̓īaūúčá/;ė<ÍÁ"cŧ\Š+Ķ67E638,T23VS§ZԚÂÖÆO­ģĢō_ÔĩI6mŒbCeûcU]]ÝFóå―uPMÃcÅėw,)*T{đPáŠ]ð'6ˆßLgU ôėĩlÖ#ķĨdŅ―,ՌYíÞķtæZĘÃ―­ŸÃPËÎû–ĐŲíyôëzŪØŊ\9_Ų"]НKë?„Ę”įÁ°ņ?üb|sÁ/—ąmōœ8qâ+_ųĘĮüōĮbVAĪf@S‹å­FÝÔ:ž}{á@úû€†z:Ūß*eĻ‹ĩ–Ŋþán:ð4Žą=ņąåvUâõrÉâû6Iį>å(đĒuĒŠzņeƒžš­LWxļŦõLĖ›þLĩ>ēƒk͆uýŅ3īÞ9ÛÛÁÓ― ‚‘‘îþĀ_sNÚîŋ:üð­ŧÛĀMIĨ $Šmܝā}ŋvļ"sō< ę/•;Ļ֕ČgpâÄy&Ø!_úX,"öëUĐÛÆžþ7KÖ^PĻUP‡ˆôøËLûEðÔčí=]pxꀁxäĐ0$ŧ­;ÆĀ fĮ2zo?ŅƒëëÕũŦîÞÓ<%$'ĐĻvVo;1V‰|ėuß{ƒmôøŦeuoĸZu=-K%ĨžŽęģ…kjUÔžGj―ôdŧėÝkę )­ÁûÜũpŒ‘YuŠį$D|2%Dîh$NŠý,NœTÛlÖßŋ|ÁnÐĒ%،Ãv2ëõŠŠÛļŽðėUīŊ^ÂmRT˕ŠĢ ÎĐ,ōú•^čz —:5…zw@š:Ņ?|” GļŠā7Ėâß~ö?PĪ_Gj đĻ2Š’QDŽäS&aëšŅQ‡RŲĢÖxëï“P22ú!ėŲ]ôŠÞš6ķ‘™sîuýĢ…đ!ŽÚâ…(@øŒgˆ‘øäSņČ7ë.“”Ïāĉ“j /Ÿjákę‘|\_=įÁđ­aõ[• (öēӗo6„€šŠI&Š=:§vðW-'žlÛ*ߓP𿊈&î~G-mÛ985Þßß+ĩļĩˆXnÃ}2FkÕD’dũ€:ïÉ[1øœŧ4uJfWÔM[5ÜF*ÂEԌ”RŧOw`Fė%ĀÛ͘k™ÚSðKÛdxī\(ØŨŽë<æu{QŽ<2ĸþđG~3#ōTĩ_bœ8Đ6KŪ*l?qe$( öĩP+š~jjžXkŲÓ*lz’~ÃŌĮ%Ü―U‹ÄõÓ{TŨ‹›Ų+ļ*öævŸõþzíôBÓĨЂėE3 Šūģ-Ō3ä6Ü+ˆČåŅĶŌTûއĪRŲ5™9} ’éÝ>PíuÝtčÂX—'znS9ęU*“![ûkf QFGvCēîĖģáJđPøyÖĉ'Õ~)ÚbËWÄ"_Ę>v9ŠTíĘК*ôš_ 9„ä)uW#+o`j(ĩ―ÓIJl5  Ũë^LĶRgíˆôŠņð*A' ƧSuqĖð~ŌoāáHäíëčÚ KîeėˆĖÃĒ…‡§‡ÓĒÎdG/2|B8†-—ĖØLģČWŠgUu. S‹jßúa ēƒ :Ũ\ūžŨ@xãu―WūāĪÚ'ūÔ^-@šÏŠŌĻ‘ésąĒu|Öۈ īD5VoĒ5aŊÕ0ĮŪĐČÁSëA$Đ֖kŸ ^ÏãTnŌá0Č0{<Ā‘ĶzŋSaEIkŪ§œ’ĒÞĻ˜ą—ãtÞUŽ%―oŽÄī_î.ᑒrЗƒĪ,Ũą] éË3ârĐõfėĶFõĘUoņk֋Æ0ĻŪë~ úŒŨûkJŒą‘ęEĐÛķeĪ—Ũ1ķáqCM–Ïeßy Kņš åĉ_VA”Žcb=âß'HĐëL‰D ĪÚ[]Þm •ĶäōršGŽ9Æ%ŽBkķĀėãl_'•’Ō•UvŠ[KTĻ]ŸMÉŌŨ·/36Ö\‰4U öëÞy1mqt•vđũ6Û|­WN‘AKĐuÝN6y7ŧī͚)6ú^GÉę#ÐĶtŸ))ŽZ>+ ÔÝčÅâÔAŋđUÝĢÓ éëW蒙’gŠ=ņÆkē^\T!å38qâĪÚP)ŽqlmhzčD―Q+ļVÓÔE„`%^ũåGÂĐŊ"Ax Ŋg{óÖķՆ­ĩĻPÓĒ\Ŋþ(ÜÝČÚwĩætĨYáŠčQRĮЉjy%V”eQ2|Ūę°ĄfŦÚ\ÛØd] ĄŅĄ+VÝ "AÖMŊA$ŨžÖēdxŧG.-Ü.㚉ô'JfÜÆˆúŦŦ6Â=SŒÔķ/D M„yøËîģĩðVނ‡< d~g|üÆWm”'NüŸ°Öúä“OūŠv•Ĩچé>w°h”b’QDeŨņXĻ$úDŊޓu?ŊÖk·byđĨÃt^§? Ė#cTE"€d)ÐLQJĨ&6Á.Ūu€ĄžÍĨ_Áqãr'ÐûÖîžA3žūŌ6óúa9Ūß#R@1ĮŠ^O€Šæūâņšy-Síoą>JgįvĪ— IŽ}ïüƒ”ôA„ĖŽēFŋē§ōéÄ_'ņ\uÃïŋýöĮïĸÞüāý?ZŸþ›_Yʉĸ;NUÛÝŦüāņē―Ú>ûÖFŒĪĘė>”Ięs­1Æ6ķcҌŲņpĸĻÚÜ'A’îŦˆ˜sEđĒ{?€ŊäšËëð˜=Ū™Čļ>ýÔÃTŅë^.°šŊÚ°%ijŨë5%›é֚đąŨũfC"—ÏéģPũŸ~Šj7Üš—! “Ė5Z™3/,c!ÓŨ3Jʰ 2ģÔĩÖfĸŲ$­ôx}˜uPÁS7-›emc S"EĩrĘyCÍî,Ÿ‰jŋũÖ[ßþö·%CĀøœ&ˉ'úT­—Oĩ!R]ÛŲ3ą $83K_TU7X^ą'%ŠshoŊÐB^ķŅY"uXŠö^Wáļ{ņįŋóS},nĄ—ÃVĩáĻŌģ(LI$VļŠv)Ŧx ‘q°|ûÓ!GY€”LÜzĸßĸëGą<MįáĮņ‘(ZB8ģÞИ}Í9mlĪ{‡Ã|…Ô;Úķĩ? 6XsĨGŅŽ^į‘ŧŧŧČzie†ĮZSÍ œÕõ $2Kž?\îîäĉ*2óKöęņҚ1FĢē+ĸQ;ļ QEëđKíu-=ļöÞrŧ•?ëËKb“þóŠYŋúÕŊþŋûũÞ{ïŊ=ėŽŒðÏ;}Ÿũ? Yĸ†§ĸú9?ffïŋĸþ?ųĮĸčƒ?ø}ۆdÎđÔLÕz{™ãr‰ÚÕ`ÓmŊÝ1fÖŨÝ0(Ģ™ÛÝV-7A ëhē9ýr·If„·Ëæáõ!Õ­Øë2ŽĶYŠigr_þĸ‡Nœ8ņĻķ*^―ûĘÑĮ“*$HU._ÓĘđï­s—ŊVš Ékb­§)‰XQķĶfĘ_ýÁ_{íĩßýÝýãĸļ˜č É/^ü­ŋýÛĸüŸýSŸŦöuq˜yõgAŒþ ‰$AčZÞþī”ËĒj>LiÖi0č°(N―ņlŊ:öļ1ēëcŪIU‰H ï!ĪÓrâĉWj“ĪÏJúÕúĸ@wˆĮ°Ųk·ôxšïõ:ėĻŲZĩËQ"\ ŦRbfÐ[ü*ĩgROčv\› õjOÝ HÖ<0žŨĒWJėĉŊÕFķią“Ø‹›$u%’yĖšĮ6úa<Ó ëʼnzÃĻTV·–Įð­7ūýāŌBäwÞųÅ/~ņ“Ÿüä›ßüff~QGķ~øÃūþúë_ûúŨé?ðiG"ūžœÞļæɞ2íŨ]MŠ”k@ĄęĄ|éæ‘‘Ðū]ÆãÁ6fĮz3$jĪBũNœ8ņ*RmŅgh›UՊ,Ķ SŠÃZË[Ŧúa,°õlŅëVĶ­‚ė6DxذĻÖnāwŋũÖ;ŋõ[sÎ7ÞxãaRĸá‡~ðÁōÅĀÝ{QÃýýýo>þõ~ð_zĢÍÃûÎ9{ŲŪŌÜ=’î ƒäę­4fûuïóÆØ:M jŧS ™Ōņ Þīžė”žîŧÚg ûÅt4ÝŨ~“//@EzäĸÛïý ü$*nũgyŠx„œøŋݧLÏüģ8x*›/ Ķ,3-óÕTĩîÆ/gCđVĩķŠ^{GĒiįaÝÆĄ CRET­ŸÁ€HSR—q›ĐĖĖ—öŨŋþõĸüĢęøŊMūīÆlŊößøÆŊ>üƒïE+>ݝj˜ĩūāē]žö|KæĩN\ũ°m›kĶĪĩŪŋ[žý ĐĪôŠ/ÛVĖū•ūĻv€Hī.ĶÏJŽqœ<+šåĻŠdFü/öÞūŠckŸŲvNNrâFÐ NąB h"EJą"%EŠw·4wŨRÜ]Á%4 !n'GķÍüÏĖ„öōū·ĸũĢßwï{ŧ~ũWÎÝŲ2{Î>kŊYëYÏóāō Öōž>Mņ)QÚÓþäi sÞžČ‚>ĨĖ)‚\(ųUšl .æÏAü?óŲŊ_Ī)RpĐĒmgãýcƔ•ŽTx~7ŸäâM 'ÏũÞqjŠŽ#z8Þĸb —,<]ô3ÝüŨ[á‹ŋĸïØņ".Á­Hi_Wã?Ņ!þû -ņéηho3FˆM>räF_ūž­š>úļķŸ‰û7xþðđīö8ĻÓNUÍ/ŅØ–%yFn"J"­°“Ø–ņcQ6nĖrŧ$6ÖãoĨ/.Ė‹ĪLäęęVąb%§Ÿ}ņâE||HãÉąÞDÉ5ŧßâý6ĀAķÅ(Ýß9yÅoÄý9} ņöžĄ†/T …uÓ{ũðãCü0ör‚™íóäÔĘOë7Ŧ_§ûˆŲ/,jáŧ€|bí”f~ŌĄßą˜tžĸãu9^ß<ŋsëúaõkw=ũĨE{ïdčŠâP4Z&áÞ<<;Ī_ŸˆÁû uęÎÉ üõ ēE&qÞë3‡OIpBáQBNĀĐsGœ{iß7zȋ(ëÅôá]ŧõîÛ·_DߞówEČýŽūJÍ\;ąÍĘßnAI„t›û~™Ôsâ/—GÅfČ<$›ĸã\- PåZúQ4•Q ĖUÔ=!ŠS"Ð}âMhƒ,v ū•Ð4Âo 0Nrų!ÞūūEŠ•.]Úl6;]­Á`āþ2cųbâj―n"€žAvžáíÎ<=œš$ō  o/$ō9;ƒ$莅ĸJZõ|lž.€Ž˜œëØÕÍÛÛĮSœũ!äû_v~ČąĢ …Ū…10yGÞĩžNˆ9—å]ģúGÕ*”v8€ öAŪūĨįEmØūq~öĐõQĮžHũÖ‰)·ũu}Ð/[ĶŨS†þ09Eø‡xQÏ~im?rÕķĩó-įWM^{…bÕ!đjጠŌëč-‘+9· L{|ûJ\fÉŠ5?ŠXÉĮl|O ųÂóFū;Cá/âÝwÍÆÂ‹ėÏę'!ýŠĨ‚-‚$^^ūxõÝ,I’8Ņ-°ļŊ‹ß}đŌfįc!þ‡k1ÃP’ÜžŠ)ZĒ”—töĀá{IŽsWĄŪF|ËЇ6ĖiÞē՘Ką™‚ f=ŧ0ŠïÎsŽþy[.ÉZ ™qįĩlŅĄoß5'S‘DÜĪ§Ųį ŽĪ;‹6ė=ķkuû-†FnwîĖóBÆÃó[ķhũ݀)SĶl8ö€Ïũ_PāՓëf· oŅĩĸÁ‡’$%ß;ūrËĶczŽŸ·3ð§ÖÍkÛ"žeëķįŽTh>uĮŠ1͛ĩâŨÛėáå]ÂÏðčČÎĻgíž$Љ9ówūŊŲĮ ęáþōcw9öŪ Ïđ{`ŲrĨĖVĪÚÝūíũ}ÄПU/æR•ÄH/Qû‹æu*–ĐZĢ͟|̓œ _ØŧÁ5ėÛ.ĩ*„ũ›PŅýęáÛi‚$Āwt•k>ufÏf5ĘÖøĒS݀įÏt‚*ÏÝš`„s2'˘ąhöęÔÔØ)ģæÏ™4ôŦÁóŸe**PkÕiuŲĒŲýÚŨŦРëčoÚ^]2ī{ũþãÆŽ6|ލD{ņråýbĀž39Ã"Įļϧē;jĘė3ÚĖ1ã\ŌiČÜ=QJ{BēïŋQ iąÕu‘ķxņƒpqÏįwžR’„Xoā‚KE, Ã$‚ ÞĻTč›Hi”DDe4š8áīé‹%))I’ĪŋÎÕ2ˆļ›ÝŽyVw „/†|ÔÏæ* QXMŒĀ4•Ūî!I·Đj>Ĩ$/ō„Eæũkd;‰a;˚ŋƒü‘'ÉmM§…2‘\…ô}ˆī'XQ(‡šĀqãþĒÚ‰/Š–ŨĢ/?é7qzĢOKÖðĘ>Þv―Ü;ĒdíFôÃäë·j„úŋļ“—tQĐÞą”›&”Ž8jÆēŽåqŋ­ÉQdųF.įč֋ū_ÎéÞėså“ĒįmŧûÂó­Ŧ{\ÛwMÍ0īČ8ýŅųdĩ‚ïŧTô1Žøbų‚,yŠī‹ÕûyÅüOK)wÖlr(jō―sÉöŠ‹~SŅĮðcŦČČ,VJĄÎ‰sŊXFy°ŋ”1$â\ĐÖģú|ÕDU ũæY lÚ{팁 aõŪ―é6‚ÝvE Ô§jÖ]šx/ÐtÎ\þģjâð>ŅāŦ3įåĮŸÜ}’Ô24ĻˆÞ›ÁUÄšŒ›N\Y--yĮÚE=ÚÚmG―’F―ĀĢ]•mŪÕŦœ{qâü‹­JĢ·Þ–‡ž‹Ĩt͖AôGŌšŧúīN€ØģHĐï rn8ėįŠĮŽm[kŊm‰)ÓkUŦ†gƒsĶĻ‚oŸy'š›†Õ0)Ž’ĩÛnßûŲãŦ[ƍ ;/Ï܃ƒhɏ~}X&þÞYՑýæå‹Ôž2óX|ðāÉ]ššˆœ[‘âĨ‰§ōïÔЖ ?đ=;\>q"9ËãՃ[y°ōðy­Ëw[ÕŊŽn2-æIj隁āßĘÕÅ*6TÖEÁ 4ÁD‰ē肗bð!ÃŌRĻ,-ląž`Ä1˜Qąˆ͊1€ã)ãĒ^ēt°ģ=Ėn·įääΧ§ģv‰ŋ2Š%æL gΧ@Ú 0ųéό}‘xĢ·ōeÄ]ČŅĒŠĻd95EĶ:æ(.˜ïˆIRF*É8ČIö_–á7 )&ņ@<ŧŽuMĨÝ ú0æ!o …ûðē4ĮKĪ:•Ø‹w“LĨý―]0FAĪjX6­Ôļjųœ:ŧz\ŋÐ4`æÄ,‡>zmpÜÁÓӈÔŒëÛJ26L"Ī‘"ųvƒ}õ°ąÛ,ĄC:W+âb”8 )6Í!åkģåéųGēĢąƒ 2‹ÛtĶšFöWŠŽPö“;6=HGŒON’RomėŨsC‹QƒJøûÆaŽĢc(áí% D'Ļ@Ĩ P|ŋ2.Q4Ā€Mĩc,ņŠtɏ݋woï?zĮœXSüg–Uō$ŲĒāReËWō‹ü­mĩjŠĒýūԏ]<Ëų?wJĖ…ð˜gÉ K•Ö°žŊŪ/šķëéüßúūÕîŋ4 M9 ęŒāČė­=ŧö ôųŒƒš`”||Ė’GŅĩkąŧ7yú˜>åþĄÎgvŋ2ĒEMӉSÐČWË#3!p(yŠ.ģš€‹Ų'Ô'0īŌ”Ïލ‰U[V‚@e'dóÏPĪtHH9đkūŪ^Rð3ZŧģÖÁ=kwí1cÛŪ/ę~đsŧßŅ_·üømýĀ“čj0đ‡VýĻBíîAåLŠC!PŠŽą+dï2ÍĒRŽi–ŋ‡ió#\ðŽũCŧŌ’D+šÄtÕôŲÐuŧ>þmŨšA{ÍÞĩģYEoJ“9AĘ{z|FÔoK/‡ E‰Þ5&bä҈uû:7*ĐȎÂãƒÖķĪ :{{”Ĩč ŧļˆLO 0#‘§ =Ÿ‚…„P"RŲQšUú`Ëį,á+„°ĸkšZ Ž%.•Öūx&XPūvĄŽ ADžÍp"/AZĒA ™ ĪCķ‚ĶąĪ'Ë29'öĀÃÃCŨõ{ũýûY-M  k”ÓÜ2Č3 ĸøYFëâb|üð…d=?ÍĘ ĪŨiˆRß"ĘĸEéÁh2ãóÐãā‚ųīíL†2}Ģ‘ŌëĒČČ―TJaÄU™*§ŅŨ8™%ž“x2]Äoaz6ž·ĢÍ{ päžđ~|ŨŽtŊW7ÎíÞ3hÖÚ•Ę~Ú(dãšČ@ÜėĘöõķzMËņþ<4núŠðå-ŠÔPmqcōŠŽüĒ‚›ãĶX­*uĘēÝJˆXWlyÝŦuÏ:'ĢæŪ)ãÐnï{āžüãâ—Įį–üÄ X-é·ĒŊīl]·„aņÔųK›OŸwā* ïCJÛ6›F|°e9ōæÅv›ÕáPŦ7 -ūūŨðQáŪ9î>Ŧ,"ŽR\”lÉzųĘâ)‰Ï2ģ>žv3ĩ§ŧŪäŲė!xvÐōÐÜ sËĘĨ„så/\u9ÏjĮô'jģĘ*Đ ÚíēÓ@ýöý…Å­ ŠÅŧZ:vŊēíáݖŸtÆđ™đöĒ~Ū…ÃŽĮą'Ož‘ŒĐ1ûãecßR>P@ŋÍé·ĩß6ąÕíC{ã4wœļ)Z/Û=˜üÍ}sûFĨî=šŽ~‡ó"VŪų5HūšøeŅĘSŦšđvéZ―P:'yJwdýuLԒóūĄ:4+Ó°aāŽå3֛Û^_š0Öî.`P$ļž#eõÁßn~Y/ĞõäæÃ70ũņދķÖMĘ ˜öüž…L EŠw/ēïØÝ֟īÓs“rīꙧvÅdš” ‘ÔX]Uî]>w/―tųŠ^ķ˜;ŽßĶ›Ëž%IŲBRjvVē 4›UĨJuØėÄÃ:_Ū†ÓóW]ûdLŠ͎‘–œä(á Ō“óRïÆÄ|RJģ[ĄĀ?8đýzĶ[™ÐQ‹C:—óøh'ÆÎ˜þYiÃþe‘ÏówĻ(Ŧš ÞÜÜÔŧũ\Ð|˜§íöÖ-wj‡5(áíúû"0ĄŠ4Œõ[ԝŧ-2ÔŧóģĢk*}^=4āÕM‡l7‚‚ÝIúŌŋ[ĮOœžXĘöSä-7hYgþķ!^ãŽŽIŽRŋf…"ŲŊîūžÞĶ]k/ûĢ5;n†ĩkWÎÛšyåŊšíšW+1ú—sĩĖGė$Õ4Ė—™a;ŠËDÃu7ûŸŪ*”'[ ŦfĢ ĒÍāI"8€ĄŲÃ=0°AAÉō;wöāþ‘œsŨŨ‚Ė! ąKg6CĻl„L[xŽęŽč9ð_y^UU ˆĘdŧ]'ŧ!ņBųL]dH”/†0ØŌŠ"‰ŧÉēņņÓęcØÁė‚<ŊĘ G‰đtJ*CYf0…ÍžūT _Ía“—ũ[^4MS( ”ž‘Ģ”4dę>\Ū ĶšMZ&Þžē/ŧ˜}gėØŨ°z MĮ=O}=sæšåk<ƒkïÓĮčÆ*͍üĄY“âšüqĀP{ívîšj7øŅĄ}q7ĻsƏ›u‚ŒHôûēCŧ ƒV雙ã”7ہŨđ‹—Ô ðp7ōéâ{.W8lðåGį^ŧÎøyöĪĐQ{ÏĻþ42Ó§ŽŪóÚv 0ō.Û·6™%ŽëRãķ[4ߍ›ÖnßīW/ÚļŸk22ļĶĶhEktw|įþj Ú4^ž|/õ›ēĩZ@ĢRqíÎĢ#ąŨúßķ_L+ķ,óQ“ŲĨxĪ‹TïØÍÅ_BjÕĶ-,ÁŌļ Ú“‡ ŧ ë…x"Esí6nyæÜđë—/ä\Š|[šæG4*. FJ―t2ęÖcÞ0tÎĶ&eOŸ·rãUĕŊŲ­qóēMÚu,ëgÐuXĄa;ÏēÞšöžR7iŲ™Ë·ŧ7 nÓþ ģ! ïäÓW8ëÝøĮÁ}Ģc/Ī+UZīĸšŽŲ%3ëōoû3dŪûäąŠø*/ÜŦ†ŧKžŪ;\J74ãSž:4‚åņ kûm&zģsûNJÔŽ[ŌĮ üî>8·š ۈåÜtoóÝø—Yģ·­Zaô/ŧkÁÐŌۜRŪVSģ› ŌÁXVļÖÓæÓķØ…2!ÃÛũð2kÖÖU+\Ëí^04ԓK}n˰Z5ōÓÍˈÏVeētMÏÍpC*vÁĸ+ƒĸļÕÉķ5jÄðßöï3đšpĪӟrwA(HĒĶhËE#7"΅piSgAųŠtâz˜žđŽ-^ŌĒ?ʕ*]nÂĪÉ%J”pb–.]ƚwßĄį’iĖĖâŪĄRđĘFW_Þ ‘h”·qĀÂ#+Į[8>‡‡đ<—-€,eqX}{ø7nģÛ(Ä;ƒhŦ%;õMІ5ø–@‡ĒxQ0ĪÜ]ô &`ČM Õ?į9ĶōMDŌhe‹ĨĐZ%ĀLt’ę˜i zlIZ NŪÓT ‡ˆéĒ(AH6’]hŧGNvVûŽ_OŸ5ĮĐĀĻ †gÏ^8ÃðRĨJþĸ'…€Ģū TEÕŲt ÛĻ)2‰49Þ(‰:QÅFĢ ā:!o4ˆšėÐ09ÐdYį oųĄ•Ēhˆ ŒÅL•P4 \ÁÝáМg&—ĀÎkņwŒ!ŲB'ēį™Â—Đå’y/jÁÕIGwĩŦæMFõûĄēá*d1Še‡‚A>V‰]kŠÆI"‡8 įˆSĶ;ģ“ŋ’―é­j˜NÁÃ Ģ‘§,l*JŒ'wĄŌTžbU–uĒjy ČDú D ptēĸ1n"· Œžrý|ÎÜyÎDíĐS§<˜Ÿ'ĢF]S^š―Áų•7•ĸ܃ į[ęQOČ@ü€:Ä:€Tnk@Ėų#|-‚\ŠÔ8$ck– ÉDgĄLh騨{YY™ÆÛ@ûÜ#ŪEŽï–0ýŒĐÄŪ‘T&Z肉DŽhKß."}Ņč .ĘbKÁ^˜Ļ9Pĩ4R”Ķ=L?K0Æü[[MÕ%Q ŨVk^ĮN]ĶNŸų\íŸh úësVäuų'š}ĩ[§E?ˆWtĐzĢ–ŸT- ‘þ_۝ÔĢĒęŸ9ðÏ_Ną&_‰ŽŦüi=?’Ïïå fK{ü"™ãōĩõ\}ŠðåĸëŒ=ŸÝQÜÕ~ÁDÅ U XŧĶWâ™î [yĘåJcCZŊgûˆ’J`OÄ—6u7‰mrĨJ•+BjjŠŪëŽë!ētô\īū0ĨˆîbņęfüþˆéŠnĄØ<€i H@s“•RÜ*ȀÏļ<ä@!MŨē Ņ"čĒÝĄXĄ O; &ž$ Äį’1ðãŒđœįyB.ÃHu §Qí/ž+ˆâ)"%§ÕžÍ`QŲ}é:Ņķ!bĪƒ5•ļu’HŸHþ`‚G.ČųRÚüŋ‡yĸ™}ÎWoôeÍ0ęPĻĶÛŋ63?ųӇžXr”h l„ČÃũ_ &ŋ*Uüßý‰ú[ĄðQĸÎt3Ô €— ã‹Ą|ƒ"cŦb}S$îĢÞą>+bÄģPCÂ'€•ēΧâp8›Ä*WŪę i‡ÓÕ2Ä+Ŧ02Ôę ·$žŨą ƒi銁ĀX˜Ū#ęėIŧK0ÖsŽįĻãÕ9ę? ǁ€ČIéaĨ?žįt:Ĩ ÔKYÃYáQ'KSðg,þã ĸ‰… ­•ümĸ;éfh# Āļ€ÎІģ4ēS5Ab­Pd―-J„\!ÖņÏt ĨŽČcĄa>ɀ§—w‘"E0ÆN?›––FĘČŽÍ!ÚĄā€˜Ã-eTtð?t@ÔĎ&ĸbO,y‹ī[ŧčÐ0 ųŽå!ôD·Ý“•k* iGčP€'Ø;c‚ÖbŽ`ÅraŒYžËt7Y恑‚Ó?‘tĩР֟Ķ*d#„ Š™þƒˆŅŨ Éi” þķŋíoûtĩXŨY•J5`ĒėP •ĸ&™6FĒ( ų-žΘSâOyAä`>%ŦĀ7ÄX]$ƒą\đ“ BČɗhĩZF#KŌ4,Ȃ”1Œ$Ô9ĸáʅ˜€ŦÞĄL8hu‡ÃP4ó 8ϕã­Ũ‰ŦÕhō…6"ŽKĐ#– Ģ/ ]SQfEÏPú2í^–Ú&érÆÆCŽ ž€*92VL5ÍsāoûÛþķĸ@šžã ŌiÜ*ō<ĮēiŒ•Š ĮBžÄĄhL؆"™qł@6rž(H­žc’ĐDI,_ąĒŲlvr'ūzõА›#‰1‡K Ð8Ķ þ ŪūSlÄĸįËcļ]Ļ V™KňĀý˜ĢÓØ?4‡({$R#/BčEvÆ Fžcē ^†/f4Œ Ø@N=ND %æÜÎ` Ûßö·ýšZN <Þī•gøY‰aeĻ‹"T­‘–*âziM‰Q$PHϝųĮė@“‹kHHĻŪ“~\gðP{[CüģëhŠ~… fČy]ÆXWlÕ­*Öþ‡õr„…p#Ðv\Ž•é$ĢPęn*g)2Š^–rU5!…Įąn^♀#•ƒ• f΋ΑČ+ŠŠB‰ūÎ Ģ“―1øÛþķŋí?ÐÕԘ `œ (Њ(â??ŽĢÜŽ„•ŠÂþąs;-Š 4õĻČō[ nކŒ?›ķN-V,((ș7p8ÎĀ–Ü @-øÏ‘öCęÄ_?ÏLO4W7…åßí›Ē―:}ÓČ·đ·āiGŽĸ&ȅāý†iøI"lĘüĀj}„ RŨ—óĶhUrÔ·ēąBFCâa ‡ĪQ?"úš Šœ Ō$ĄģáXŊsū­ķ d•ŲļĸČĻöoûÛþ'§a,!- ŅãœšŪŽ` ÍēˆÔŅYbŒN…tļÓ+äēŽ^D[ŦT­ææææä=pjÛhĪqK,„=Ājļ’/ÂčY°Ną\ïz$‰:_rãį[ …K^šĀ%t―Ž>ÆHž0 Æ)ēĘœĮę„6 Ý!3' "‰g‘No˜ ņü‡Ejf§<;uäbŠUýƒ €”ËĮö-\ļčWíāpäĨÝļpîė™3'Ϝ{ô*“ãá§äĨZNn™?bĖČYÛہgýÍÖĖ„ýk~‰\ļtï…2ŪhlÉLžtáĖ•G)ĸ(CÅļ”îžHą9áxœ“øëēE —ozâ8îÝ~Øþðú•čûŊ Ā…0Ą”—/\‹U!—õōþ­ĮņĒøGwϝ<}úÜÅį)đ€/<ÞēUë]&=ÍR ŦŠqP?ēlȐé‹ÓSīïŅwÞÉį’$ĨÝ;ŅýŧĐKĢ-øåðĩQþĀÆ(H„.đOŊm]ŧĢZxŨfĩ‚BīÍ]=·gێÃ1–”Û3|đöÔ-N’‰§vĸĒÝW}ĢŽþŽitũüų˜{ &w7ĢĀa øųy› iöóü\DƐcO―sėėå,ÝčivïŒPL)–ėg1įÎΉĩ>Đb€ ņá―˜ŦŊ‰ãžŦ5 5_ļōž 'ōðęŪ™kŪ+3MŽä."(<>ģfĘĶ+<íį€î°åĐ\‰2E<ˆ˜?ē_ŋĸÔ%°z)OYâ2zâžKW=ËÐ=gũėņ#fΙ8bøėUG‘ŋģaIŸoū›:oޜ9[^Z…/úĩxzîv’ód…j–oļæBO””™ōúÚņÓV ÍkC„ŒþåŦøž_ĩtõú­GCëÖ-jPA ĸxϘˆgþüóŠÝg,X”L9düœyÃ~qčÆ+åí›4ĒįũÃæĖŸŧbÃy厊œ0dôøų3~ę5lÂÅŧȧ.íÓk°AÓĶîŌuÐĄG9Ɗš?f𐉠O^x<ļð§\:ņĪå€0ëODü~\däėį)HjÕŋųÃŨSōß!P4îí™Þŧ_ũ^#ĮžŒy‘ôðøĻ!Ģįĸ<}ÄČiwÓ4&/ęÝã‡aCfMÓūÛĀI3&Mœ:ŪKŦïŨßJî_ÞÕŌ!ã.$IŨtUQh6€Sœ&+’p$ûčĖO2bQĐÏ‚(Ę5įv?'ŌËáp8Åq333Y\`õ1šÞ|ALI–ƒxŨoBęÞ~9ý”‰7!‚3ƒÞ>™˜’Rtģ•~|[ØjÞīv#\‰mï^žĸÂ*kįU^QÆ­‚ŒäũÄVũ’ÄļtÖģĀpēŽÃÂyŨ,ĨŒ*kÞeð 6~UU‰ĮĪëŠŨ@&o j$…] ŽÓ—ü íÖýpöþ–yŒEũWÎ^tûΕģ·Ød5ŨĒ–ĸī[ÏwwOüĒS·­7’%1$–­œy5ŧށc‡öŪžörũ’ßĒswˆĻRiÉĶ™Ÿ—t+Pp"žïĀąkŅOŋÜt ėÓÚÉėĄũ ũbėÎĻgsgÍō0"ŦFxüŠWûÜ#ïÆÆýįoŠZwųL_;‹’ĐưūßĶÝ=våiÜÍã/ûŽæƒx„jMËĖÉÃäœŌo‹4 ïøÆĢzÛ59Žą„`öëd^võō2ĪŨïÚ?ŽžiÝôŪF.yž‹Ōt˜uëüagÅj{†°†Me[ûåĐ]=š5Yuâ6ä…B‹Wýþđ=ãĮY{WĐQĨÄ BƒVĶŽË›\žšoŌščXUS) QYWNä”E Z5ĐîÏļŒuGrNēJō-‚zjքöÍŧóŪûÍįÁš–”øØ[Ũk,`M{pøČ°ã7یŽdJž{`Ėęsý'þē`üGW/zþōþ葥]Ķ;tpįķáAvó)%H—^[dēˆ§&―|pĸþbwž―ÉÆūû ÉÉ7/3rā·š!ĨK M7úŨä sæüÔkø’ÐÕMÆĖÕōI·ũœ‹ą ›ĩ,rø7EŒĘÉÝN!īŌ‘ËW}ó™aëöƒŪđúÔļĻ]GŒœŲ!óƚykžŽ]wčðáMũ'NØŦKbFZ‚[đvŦwTúÕŅ'Y9ww-ŲōfÎĄƒ["~[ß (räþ Îą\ŌëÐĖ~[oĨē §&ŦZÏÕĮ{bŊVýG/MÉS,XÉxqadĸ^Û.%TŽP^ĩĶāŌĩ>*puqysÃXöãšÅÍHōhP=4ïÕsæŲ3žÝ—ūnF“·ŅÛČ?9ïRŧjwÉĢTĩ WĻ­QŦÎĘ ÕÐõ_„\üŠ~ĢkŽ*ôéȧðŦZGā“°/›…øóО™‘―Ĩ{ũÁq%+û’hF7{7nŅĖÅĖū ›6ô0ųTŒ ë- Ōŧ,1I6\€ĸ: „Q>ŦF+ė‚ÄR c*ÚĻ4ÔĘ2ĨĻ& fĒAĻŠhīĨŠ―čýcbbœpZ§ÛuwwwĒk\\\ʄšHŒĀÄþ1Žå Ču lŦÆŊXxŧŒ*&=l,,%ŋ<Äe$ėPÆĮŋLqÏ5wŽ\N~V°1Ֆ™dI·’1$ˆûĢŧXÓãg.’§a6%QäČŨƒ!A€ã‹9mÔ4•îcDJÔLžš^3î.zZâÃI2—°ŌÐt—B"eV>Žx#%ýÁ,°|ë≋SIđÏ―ü°e›ofKzmþωŋŅđ§‚0ODœŽēn7ĀwŦfsųJeŸž?f ýŃkI–€"ސîMēÎļ PEW.―Õ:~ÁôPģžiąc@ß%HÕ§ÓÔķ%ųņĄs7rŨ/{MýmïÜwŦÔå‡~ĩJšŦ{Ņj­W8ģtD―wLšÖ€…˜ŪŪ*4ĻžyíƚÝÚyŦ…Į.þÎĩE_Ÿ@Nŋũ$pSĮâQ,ˆũāór1ÉüpžŦ4 H8uĸĘ ãũĒI‘SüažsĖä'%ČLSˆtÆÓë‡Îݒ1pXŽHpņō ð â,Ђ0Ðd{ņZíŨ:;Ķm‘GũÝŧ6ųi…ÆŽŊM›Ütõ€ˆeÛÐÂ~ëĸ!o2yø– ‰ŨîŲ Lxxče™ŊƒÝHÆ*/ŨVĩíŽSįw˟Ýēũ†ä*›ģûôŲó[WŒ ðõ)jUNėŋä@ؚaÃĀ‘“Īz•sš/”/Ãĩ5įÄų|›Ó―,pÃ@Ïģ:Œf/Ŋ bE853-ô*.‰Íūþ­úŒ2bãģ˜W‚PÏΕ:?xrÛø{ÛWœš—îg4—,Þxņ–ãį.^üĐ{}_/ÓWŪ% Žgåæ–üļžþėÚýD §d9ë‡\Ų2FˆˆŧPeĀ…„*ąŅw^eĪ>9yâN:‚:ï_ąķ9íYŠƒ‡Š”ķ9zåOå·ÎŠŠ—œóZũ)â& Â~@%ÜÞšKU.RĐqįC'Ï\đzú§ķ•uE#žI–ŽTŒód9<%ė8#)>-Gæ ĖËJ~‘Cä”rS_ū|##ĸEĀ^$VĨÓÅÚÃÚs—$U!‰Š-Fb^ˆÍt‘VSVyĮ쀆tôŌ@8%5%66֙:Hû―ĨĪĪ|ÎĖČČēä"Œþ!BHFHÖŦ ų}ýGVŅ[ŽEDjÏ=ÎL)ØĄ‚·ĩÚæá[†ŽļļĢ`ãĶÄ_?č1čAŊØžûFÞX@d­ĻŠŪk"ÉϊųPž`ĩtÚí& šŠŦŠĖôë™o%Ë~æF!€A,ÓĶ2ÆþEõÆ)›8mŌåXË2ĒļZŠĨŦ*„—äghƒø@Æ \ō―Ģ][ũ―ûøŌĻ&,؏%mįüáË=Ep~CŊÆaÍ6nõĪȗsÆt‘ĸT‡fM›4ýb·―ÎO#Ú š‚ØŒBC›ïGWtŧÖĻIŦ>ӋuÞķN1Š›‰$Dņ[‚ïœKŨŪ§f<úĐ[Ŧæ-—đ$IKđŅģÓŌWÏoÖžW”ˆéwü4}m†"ø•ŠPĐrųŠUŠ•ŽT4YÔĪĄš4mÞž~ûņŸþ8ó›:EU―°·ÉĶļ4íðýšÅˆ•–5QðsĨšÄ<+v\\/Ô8ŽÉŅ\ŸųÃ;yÐuŌuŊÐO*Ģ—ĮŸ…<·cŅį6é4ĶdįAm>+īŽgïĨĨßĸąsxóð–ŦNޅ’”óhOßÎM›7iļ4Æ4az?“øüÔēŸfmķ Ōģƒ ŧ…7kÖŽŅĀ=Ž>―}]$Þúh`ŸŽÍš7küÝĘÆ#įtŽPxĖ$EĻŠ˜ķé—æs#lŋþÚdĪ!4JĘĄ9Ãšn~\dÔäî įܖģ-ŧ~&j*$%õæĻaÍÛ|c/ýeXå"Ą-įh>ëŧ†aMÃūðc’ĄĘÂŲĢķôoŌīYûÎ32TëÚe[Ŧĩ+é.,ŠtMUä|+$yĀIÞŋ`R‹ĶaM4;B‡ũnĶë°åáÁņÛ4kÔĻŦ!ŽĮũaĨ 1ĻōŦó :…ŨëÐ?2ļYëÚ!EuŲŪøýn->kÜpÖÆÞĩ‰ÜxÁũ­7mþãčÍ^wŲŊ\ï6áaM›ē?DÎ)ö|s―M…ĩ·ũ/‰nėZ:?ļŠ$2yK‚‡ ęÁđ#—G'K’pcũĪ)ģnH’ôúîáAã§fa dþöT>xÐčŒvHÞģņhHgt‚$Q  î•öé"šßĪ(ĖTpTHŲgŠVŊååëK–áÔX0BWëš,ˊĒ'ĨjqĒíĩ"ääAÍöū&7Rčx˜ĐÜØŸ<Ģóütz?PŌFÜïĸZIâYœeZŅí)Í>éŋũëŅáEũ˜ï6­=Øŋþā_:M`›Ķ<q*íĻÄD(rĢŋf <Ó^Ïy^C:@ÔÏBŽļK Dâ ušđfÁ/Âī;™ēŲĘcƒŅ@Xd•.ÕE]Õói!ãųÕɃ ðHĢï+@ŽeiQ"ӕgÉëÐĐËĖŲHð]ķĨ§įð”Ž]rqũörĩådéĒŲÝ$Xģ_ĮŋÎÔy·Ōe‚ÍĻéXuäÅŋŒ·!1ĻT)wĢ^(:ãyÞaÉLHJå ŊxŽ*ēÝ&Íæ‚ĪēŒ6›Đ‰ļzxŧŧą&§§g (pDÓāï[Ū]wŪg8˜”Íf‡‚sæˆĩĪÄøŒĖ<ÉėWķ\1Ą0ô ëđ‡ÉÍ$ <ĨŌuUķČČlrŅÜ\ Ö<‹ÍjWr5{zyļĄ·Óƒ5öŨþ#O];̘–ŸœÆ=K—-aâÉŌ$/Ï";ųcöt–õ Xɉõ:Ũ‹• ô5#ĐöÜ<…óðpÓl™o’Rģmšg@ąÎОũ*Ŋ3ģōL>EK• tõ]kđŦ‹ŦŧH~$š3„]=]xÍĶpîn’lÍÉą8G \Ý―žž=Āë;o^žkioQĮërfjrķMuõô ôó„ÄOiÉ ‰đVÕÕÝÓÏÏŨ Ēڔasõō‡ņ§ŋ_yÂÄéû‹šŽĸi–%=ųMjš A%‚ýÝ]tĘeÏJ~‘Œ%ũ’ĨJš œŽ0Û[ģg§ĨfÚtÎ/0ČÃ$‘ŌŪ’ó:1Mց—_ sžĻ8ŸĪ<‡nöōóũõāu9>!ÞŠpE‹ųšIŠŪŲróDWģÄC‡5W]]’î°$$$‰ū^&Nƒ.Î9Ũs;ķZŊ_:Uåâ2čZĒTņŒkÛúmū=sōī*Þ|ÁxT›E’›‹Äž=7ýuj&ä î~~>Ū‚5'Ots—8dÍĩ@7“Ä;lđ 4đŧÖŽLÍÅÃÃ(8ō2mŠÉÛËĻĘķŦÃ9ûĀĸ‚7?>|čŲ݌1s€‡åŸå(ލzJJ:CØWąÎQė:ĨĮ&J2HšJŠøŽN\ĶLÅĘÕ \ 3V’Đ‘ĨīŠĮ y‰ĸčj˜•ĒÞø5iyß=j·*8―cú­[·ŸļÚŊĮģ-ӏ:“~‚ÄģÆõ™–4áĸcïÍã䚊;ņsÎ―ũUUŦ7ĩöÕē$o’mYķžŊØfą‡ÕBX2 Áŋ˜,ŋ„$ŒK YY†eˆÉ0ؒmɖĩï›ĩYjuŦũĨŠÞŧ˙—s[ýĐtõ}‚þ@Q}?Õ­ûn=―šUēOwîũ|ŋ*‰ö_ŒbĻãDÁĄ`į1B!ÉĮČĖ>x­T&ÝqJČâŒc;!ĐXÚæø $báLJü!AZĖrˆïNĨRyË/―íņ~ô<Þ âÛ`ųP “ã ‰dF0ņ:‚šÓëĖEdĶNâę›1kiÂĒęžLęfΞĨ‰/TŌÉĐ][ÃÂËįN1Xŧ]8ؚeĩŸÃŲ?ĩxvœ™|Íĩo3ÎÉqMÚČąõߨÞúŠ›Jĩ âDŽÚ(ōGėËŊ˜e3įLø‡žėAýë֞ʂ‰'Ô}zˆ8>Ļý€kjÚ œ>q|8-]šdķÐī™ÐÞ·/5ģŪZ:ÓZ7i/oýZę_ŦöŸ.Îԏ~A oP– QĢË{Ne)I΋ķJ”hƒį(Ën{ĄT0*‘ędę5UŦՀ @đZnŅ-™e›1*æčĐčÏlúĀ›ß˜f›ĸ”ËųöTđRNŦÕž‡ļŊ·gxh(Ÿ‡ī,KÓøji>B‚0MYļī&îž)ˆØd$áĻFEÂ0cē,CDuĶ]âüŠÔ!\\;•  4B-ģW{*Æ4DŒ‚ŠąŸ’‚—ü‘Ķ8ōҘ íÚđãĶ›ođōĘ+Ŋūúj"Š,ڜZëœËcY>Č=oúúģƒĢ'ÎDx€`Īâu@Ž‚ؘõ۟‡ŋ Å9Ņ`‚E"Ä —=/lØā\ÏHwđ2 ējV-įõÖTĐØJ5° "`ŽĀcČŽģÁ§4ĩĩĩiRŠ”óÎZ›e™Ë!`yb>48p`ĸþÛ·f’įģYL] EîVáōąÏg7‰AĪϜ‚ļāˆ@ĨÍ,Äz1aĖĢá<ŽcëŲ‡‹B|4aĒQ…‡æä!jģ’löX  ÖŲxSė―óÂC@$'š‚ČĸbÜuęęė(EĒŅcCð 5Ųø…`”–ÏáįEՆývωōþÃåQkī&d#ĸ{ËO`ä,ôŒT]%œÂœ6“(„  4jĩBĖįČFéĀlmF|C`!~‘čˆˆ—6(!Ũ%*a€ĖYĨ(QޞÓ;vėXļpMĩãL%ó„ījÓsæūįú_-ŧōáĢßÝųũ_ßųCE\õ؋;šŪ[ʌI Iaiӕ·O―ãŲÁ§ĄĒNíęýÕ[ß<ģԞũîÞý'ŨvnNĸųüĢW-đ%ļrĸ–ķŨïc‹„˜ý"ŠũWĘU/šæišæŲw”õŠŋCë—ĸ“ýļ­—ïûmßū­Z-°w!It`vÞ+QK”Ô6 JóXp>xB­œó6sc$\ĄĮÁύūQ 'Ûst(ØpÃĩó:ڋ†ˆö.ža4ĸ"ąÞ.ĢA~Ē56hzģū€ŌųĩÚ ‰)dÎēwZœŧË]°Q9ējF ĢAÔ30ÆH4 1Ņ͚ĨųÁĄûûûûf˘ÁĖãķ~0~H@y·šVÓ,uÎ/nđô·nĸbōÉgĸĒunņų[Ÿ=ē{MaĨ6šĐXlÓío™ũČÞáĢÏn:ēú’—āÞwTGGOö =ŨųܞūęWÝ9Ü7<č, BāHPÓ`ÁĪAJИnŨFÛņAlĮČÝŌöîÚé2GDIbAhČL !=#ŠÕúāãGŧ{DÄáįÆpÕũŒļÓ=å+Ï($ĘVŽPa %…’ÖÄÞgiŋËņϗûĒŌ‰ó’}įd|Îö™‹.J ŒgÏjc›ļĶLIZښšzĘ=3›*YĪð]h jYÔTЊˆL$Õ:—ĄÂ3**ČČbå‚Úh`Œ~b]°ÖEĪãĮŽ:t(ĩ“FØIó\į\ĩZÕâ”SNËMIéæyŦĶð—đčäÐþŨĩ3’RđÚjR(,k―þ#K>ĸBó‘W.ZáËöx߉ĄÁáãýĮ~Úõ4Y|Ũ ŋ 3gk*ēņÏ@ĖTÛŊĮĩ iqįëg”Rų·ÂÆ ë‡QQė.û LŠF"ą$ÏŅöŅ‹›Ė„=æ ˆþ j†Ÿ>€ ĶI3fMÄ ąîŽEcuŨ3Oė{ņxóŽ+Ŋđõū–ŅM?þáĄ;ßøÚ֒ú·FĐ0ptËɞ!ðžrŅÐy—Μ15Ww5$Eņ[nb&› Ÿ8~ŽeÞ…Þ}Oþôđk_ũŽyÍœ1Ž;Ö3ieÎ3øĀpĄFV7kĀ­ãޗC$ŒōŽÞ1TÔ+`BÅ™ƒ&ÅĪØ§ Fęí9―{ĮŽ+VäÛô Ļ°ąžP{˜Zóíþ(įššė–7­šđ|íąM͋Z7î{þÓONĸāę·ķ5·’‚ÖúĘi ŊœūpppðTĸéūþã§þÝáo8~į%7Þsé=̃åxŲÚ@g"báx<’ÆŒÏDÄq,ÔFä}ûöíßŋ ‰"ã ęQäŌōŅ‘&ųōČĮ1ÐJ­Ņ$&TCfģóWPekCæ8 S šB-ŠnþóĮūúĢãËïŧđë;„ųĸtƒ9øÏĸüÜ5žūiJ5nEŸYi”50Da-f@ĨĢ(BKíø-ßĸÐŨÖöŪšņ†ž―ĸxĒŋýÞwō5wǷό"ŒÅtÃĮũïlZļrF[I$Ø5!JgøĖJˆÎtˆŌō•ß|pŅŦßũÐ#6CâNm_óØÃĸĸŌoūĸU‡;ŧ‘ĸņÃ=Åĸï~0·đįëï^ýíw/iŅ;·mZR0Û7n8­ķ>x{óö#ÛŊļų·‚ÖE[Z†ŠcLŦIĮåóÛž„QEž‡lûÖïĶ-Ũ]ąléK[‹móßðúßĸ`kR-W\’UɈ SZÃáuģgęšüÁ{·~îí]Š$bmÐԄZ3Bļô5Zūæ?üí{ÞųÂ-ŊyxõējęęėC įYĐqX \8Ą6šrG_k+ÞŦIb„1*ąÕ‰”&ĨŒēųdðÚ…* "cŪč)<íߗģūŪđæ/†Þ“æģæÓ4õâP› Z S^đbÍĶïíR…R@eß1ģtüøá=ņŇŪū'­Vs9ŪĖÚ܍ũÁ[€âæĪƒú1j™õ܃ƒîŲą3Ë2-%ėØōAL $ķ ÄáÓkmBä "ë‘Ï+‚ŋčEŽ–HsÏB)–"ƒ„XW9QZW·ę·7wö$ÚgSV>øūĶšu…bSk‘ÚKŠ5IĶ(}ųýŋšúČûŋũ{ũąÜrý›Þpũō–vXyÝô ƒwŽžlúč}kÖýé-ÜŅ͜+‘ėۚöĐÅîĩųõĮþŅŧáöËWŋõņßX8ģcþÏ}úßxė &,{óÞũē[VÜtÛwūú˜?ōЗݱšíãûÆï۟5ģĐëéOŋīâŋ67Mk)R[Ē›3ĩŲ$'žþÁ'?52E…ŦļsåĨS4‰ęĸCõLÎ†Š† ē đFV+ĒŨÚZĮĖR7ā õ°Äh/ŠZ’†1lŽHå“6ŸĮ|Z;7æę ģóD~Ï}ÕUWEĩYĻ/ÚÖĮß<Ú&IĻ›ßÐB›9"•9D í3J‡;·üŲpũēŽ­I“óîÞ+îčhmšĀӚ”Î59;áīI“âᑑõÏ>“ûû*ÔĘČGXIE›C°Î‘thŲĖĒ҉2Jļ_h †ėĒ~d”“Ėóų ĩln‚DĄ„Ķ ą66­þÏÝ4Ðc-5ÏūĪĩđė=~ôÎΈHMŊųÄw1i2ÆŽ~ĸ—Wuu:ĘmũæŒ ĖŦ?øÄÝ\(PuÆ―ĸåņÛ§PŌ &^^ßō+_\ųKUï.īL+%Ęû ^ņƏĸhāt/“ž2uVāš7}lŅ=ݘtīMkúí/ÜXõIk{KeĻ?i›ąōŠ•d î{ÛÜЋ§ūüíŸđūšŲâī9ÍÅB„WĀi2ĘD \hY-‘ĩ™1š”@›eQC1†"D”­3į―ŨĪã.28s ”–,™IqķoŲrũÝũä„ŲXš<*Bjs Đ`pjģŊžfþUl.&E‡ĀQaajRåŪ†:―MšÞĩøĄāC}ÞZ_Ĩ­?G<°ÏÎÓmā VŲ‚ãĀ(õŲĻįŦ‘0ņ‹ō11REï+jÍh}ØÎWG+)0č§$˜h$B$@ĻCizĮĖãy!$šđŲ@ŒeĨx([–,–@–Wly6ÉuÍdPƒR3ī7C„\AF’Ö\ūæ:ŖŠAŽ[,tī7ą\V⧁ą‹'--óĪð=ɖW<æ@ÎKŽ1(‚Ļ)Ņ@”ē— J‘ŅڇeĐB"…}đ“eP ĩ€g(ĨQOĀ# ’ī‘9Hn+=éĖîßŧwïüųósįŌ6Ģ[ĨRI’Ä Ú[Ú_uýý›ėô#C‹H>v@ũ]}Ïkn~MNŦ'ÏFL^RĻÄ’ÂøŠō*í†õëŧŧŧīŅÁƒsu’äƒЊÅ$ģV!ą4Ë h­ÅÂf“$Ė!dN6ÍTˆ{į|ęŒM ĩU{{Ëh_wq^sSQĘĩÂ?› cĀĸũ€k'?mr Îí:xîkˆģ,œ‡Ė†áQ[ė혚{PcŅC \@. 8Ķl| @T8–*’’ĪĖ AgĩQAĪŊ°&`EÁŠĒąŋ"íÛēyã7ޘį9ãŽŊģ§·1_ÎeQ0R―å―úÄû†û)ēĮˆR!°Qę―ž'Kģ Ņą\‚úRlm[?3Ą\ŧgϞí[·ÄgeG ĩ.øH1&EĒ#CŅþ—9“&]e(øÂĄ•’7Ģ!Ĩ혃ƒŌZĸüÛbSÔĒÓ;ŊãÅOėÝšoÚŽĐÚhþũščIĻÅj5ëíęÃÏi]8ÕīĻQHhāBRöŌc:^jTŒė8 ðgĢ(ļ6xk‰ĪąA,‚‘] į"E'Äļ―ž$Iđ2š{įÎÜ|Ą­­‘kCí„†Ū ‰mõ‰nŲâŲ‹/›đhCïiVŠ1Šl{ën^výōųWeYĸÖŲÉõ ėŲĢ-åŠ_›^Ø8ÐßŊĩēÖ)ģÎbė…ËR8­ÅŅ= !BŧïĄBN@EÉŲóãÂ0%ĄK:LæÚ!„#ý'ķŧ6ŠXk=­ĢųŠ%ӖÏkžßn ÎWV·"J6HĐ\#’–›ß8‰Œ.8DTą˜kmīςaŽEJÅíu yïbOũéœõĩdéŌÚØšÏ"Å 1ĒÀ„ęŅßõĖÆ'ąÐ‘X•8ML”•ÓGã―íÍm#ĢĢD4™|Ėäm !„IËĩP YIΠØūm3@°– I•ĶĐŽ_D2Ú `ü‚ŅFK'3-Z”ž!Ē"ëÆ ‚õœZ­"ė˜Ē/Ÿ‰ž>kFÛHƉ Ck/éHōGs‘hā‚* pqÁ !D3ocL8S7Њž b"KĀđąÆ\DôācôŒ}JQį=Ęœ[ķlZ―fÍôéÓk#ZýķX-œsã–™Mo]vۍ3nß{ðŅš Ą’Ĩ·^ãÝ7ݑeķ6bž#ĄķāP_7ĻĐŌ>“§īQŪWI­ >…ĀY&Gˆ[,} Z›Ø― ‘QWYiåžgiƍ[ðą<*Ý–@”hAqðGÞķmۚ5kžũg—z‰ãÚRi dMĨĶûïļoĮî=Š{ĨÕí7ݒË*ŽŒŽL[qŌÁøa―ˆWí!"åšī;·mËŌ,vÛĖŠÔ™žYëâڈ(Ë2Ĩ”VÚûĀž!æž,(“,ĩ!-‰Ú8Fa>OP„-lđ˜ō;–†ļCmīðÎą°—$)ĢˆØ YžũZ ÁË{ ĸEĘ9ïK Ižũ'RHâŧĨ”ĘÍhvlß~ë­·FYÅšdvōô6ÖĒ-‚Vęæ•ŦŠ…’Hå{?sÖŽ_yÓ[Ó4ž>[‡ú„úėmcÕjuÝš§rĮÉOó“ÄÄ­-­ī֔e..>pô>@)m‡Ā>ō ŽĩÁû|?`ˆŽ[1žÁFči ‹NDŅû`­CĨ•Q$ô ™BÁ9{·čL7 IEŌCąPŒŅRēžX…c:g‘Á(́7m|îčŅĢą7!BÎŊŨþÄúo°öBý~ló§{˜˜e IÝsc’ęĒĘ-°Ø:*E,<~œ˜ĪšĶ‡_<ôýï}ũķ;î\tÉ%ą[OEĻOT1ÃéðÆcÏßūôŽå—]i”~ÓŊ3Z§Y6)yvR:ÁļÉMíLÏŒđ‡Ž>―îĐáĄ!B…"ëēĀhBĖĮ.@P$R;ŌųE ·Ē3ĐP{ïäzŒRpЉcw)Š­Ė eëļ ˆÎ;ÏAØĘgcî8 Ũš$A į- FîY™ą՘Ŧa5KIEĪ\ðÂKœŒatdč[ßüú'>öŅÏ}öOž|ōÉS]]Îų$Iˆ'Ãx4ÚôUû?óô§+Ŋš{Í=7ÝķtŅâT$ë“âúÉģ”&-;Ή;väIīļ229/ŪĀĒöā„ņ–˜$Æ\æPH !x+UctÞ9g™―ŅI>&EĻÐûõj0fúŽö"E hŊVS$YDĖ猐ŒIiĸ‡(â9äŪ8K3†1KÚLÂãΏe8hežsHXÐT`3›[—?vô™ĩO-ļdáÍ·Þūråõ9JĨŌĪ·ų1óÕZ/h›ĸąWþQ~‘‘ĄĄÕw―l‚V˄\õėƒ˜ÞÖÎÔ6ō:įÖ>ųäáƒĒ—%"áH-äc" RÁyÄ·†YB(æ“î―DĘ\ƁM!aĒúĒÖ&2(ĐĘFV{ņĄFVË@J1 D"EæœÖĘ$˜]f ’–œĻ"‚—Đ$IīR6xBš˜õÞ#Cb‰ģr}éa„RS“RjxthũÎ_û›/ú?ņáĮĸÁOäķÖÚBĄ@“I”&:đ|Æ.õiĨrŨmwdY6!ÎÖŲsd ÄlZ Œ1Åb1^ØĩkŨĶM3g }ÜÎ8ïcšOz–$wŽ‡Ø“"“(e„ý–ąÔūƒ^‘–œũ@ ―!hāâCƒĀ„ÄŅw:Š3ę0$BHPüøWĸöoóN‡œWūã‰ę˜r__ߚÕkŽĩõĒ\įÂ@ˆn4FÓXį\^(ČMÎ;;;sņï-[ķ<ģn]WįI­DßĀÚĀA#Y#ÄaBEēBÄ Š VޔžEO6y āŲ“R‰„A „Q* Ð@ \„5kizEˆŠt`vÎ"‚VÆzŦ cz‰Ōƒ{ĶĒĘ(ŅŲ{= ó!*ĪhÃŒ6€āŽCÂ<…Ó[@"T―=§ŧŧšķoÝüŋûŸ+W­ZuãM+WŪ\žxqŒķÞ{DĖĸJšĶsįέ·hÄ3` ŪkėBÞe›Gę~A>Îēlhp Ÿé9}: ŧ4-V*åĸËÞųīFŅa|ę_Ï{4Ÿ H@ŒïՐƒ9ĐŲ“hDzð5āÉÉî&$û'™šÐ>ÕôCdjØĶ™éØ=üöaŠęéÛFö('Ļ]0Wš|­VōŸ†ZÐoĢ„ZæðEĪΊåæļøi ÏĐ#c^TÓ>žŦĪ&)ɉŧ)͘â>{!ũ“EŊĢ{ @gsfĨÁ·Ą˜ypOáILƒßpÄZ7AG>XŲ™ĩRq›ģÉ#Ū‘l‹Gc•õęޘUþuōóôÕÉû÷vwũööŸlooomm‰Ú·`1āþítȈ$T sāx až\BŦÎįģ9>g1ŪWŦëÍš]šøXĢö@Ĩđ ąy:ąķއÁ=˜Õĸį!ũЊą ŒõšæŠ>æ‹]`;4ĢP)―û āĢf&rŨüj†ëā< Įïßūþ8>þðîðáÎÎÓ―}*Ė8æggŋÚÍúzđšúÃÞĸäČēõ@āP„í“õ~°+aĀ:ˆ0€5‚ÄJðuæąðŽŦŋ{čŒûžTjeWæ―ƒ(—Oüųųų™5AîįþđģęšVfšų9ôįyšúóųHí˰€RVÆPä.ðŧÜ­.ĢÁ؍V››‘ŧ6‰āŲO’‡Š9Yī°Ģï―înģ~éfY›0‘+âŲŲ]k-ã”9æ[ÕōâÅ_ĪZ•šËlĮä‡įԇŨšēKgâ>Åk­þÆxÛübgF,ŦššĖ cN„B„ŽíËÃcēÝĖý{Žt­%”Dæ6 QĸŊĸũĸóĸņûŋŸ*Ģąēš“G‚–đ!Í*Ģŧc­õđŠķŧ›đ…?ûq3óŊõ /Ëęîō§·ŠÕÜ}:Í †ĖâT.vS”z­uïĮDĖ|ÚÏ8cöM [’š}…zzÖÚÝkÞ͝ÂÎ-ĀÜ!žxņâÏe äiÕ&X•.A{?‚>Ÿë0g‘”Aģē ŊŪ‘ÍšGueUø22ÏgjMr>Ķ…ŒtģĘÄũkĩUŽFĘ9ZÕæF BøZŨe4PÂ­ŧ@}>ĸüóųt€ĸßK3‡Fę`]M:đžg Y­*!]ëÚ;'–{ČWŌušēGčĒv™@"Ŧ:‹RDôá\"Ö) kŽ›î<IžŠ ŅÕĨéþtÕLÐä‰@ë>Ŋßļ™/þĪØ+Âįī*<ÖZŲ(Ví„{åĪēNžŒG@Øã2·Z <<Ŧi”äfÝ:tĨ:­š4sĩJMģ“ŸÝ23AũýCp]‘đwîs7ŊĖĖt]Ÿ‘=D„ī—Öŋ?œō˜–Ž;ŦŠÎã™ĩ揊óyÂü›šKõYgUouːđ[-b$|ÝfmÍð>ũ>ÄíÏÞŪļHVũÎیFÓ CļŪuxîæķ~~þÐ=8^ŧģÐ2ŦŦþĒ[ėŋ·1-uKMũ9Ŋ?Ũęũ8qV·‘>ĨÛ<[Čý܀™sųÚϞ&X·UÝ܉ ŧYØ~ķÔFũ{ߐü ž­&ÍÍKSéîÖŲ-D„đíį>>€Å3ĒJ=ūĩî2Пį&ū`8īIAf–ÏÆyVZðû>A9ætÛÏ3·[~í܂@Q0æ—?;ŽXļŧĄ‹> 1âRĐzZ~žŋcĩąŪē ôučĩSûŋŨ-V’™}>ŸŠR·qęj6Ü=kC2·Ruu˜ÏJ0ãđ w=@›; Œ”0å7áūĪÔÜŨúęĨŪÓ]–ųĻ+VČ'ĨĶY—JEÃtčâVCY)éZ7—Ú-âšôÛ(cfó…―ŧÜĒē`Œĩ$ÍÕĖîį>tŲo„_îNNaOOýŨĖã0ėú~ãÎL3‘•Ā7ŧŦÕæëzö“€"Vu˜Ęĩ'k”i<$Ž/^ü5Š=äRF;2RšyĚpUPîgfÏ·xuVŦé6ø9čŊ“ģōđ>Ũú͉›SŌ°ķđM#Î!GÏŠqƞŸˆīÉÛđI-jå&Ý a­kzŅŦÛÜZ•gôö`ÕVwxý!mķĻ;7k]€ŠęôÆ53ĩũ#Æ·’ý{ŧŠjHfDŦräÆĻCņË—Ī―7đņĻÜ~ÕcôãSøģœ4V:ģ°<ïŠkˆûÝÕūxņį:{O ßąŽ.aĒšI›rĪ ·ŽÁFįBhžũMŅÊJy&â]Ėūýįŋm_Ãî)Į H·Iír‹iî2Úđ˜Ïģ§ĮLÝĨŠkeUŨy<ũoڋŧÓî|0փRGŒ‡­'‘ÖÍ'‹+ÂûqwđûPžđŊ뚄p7Ģ7Āu}HęĐðá#y˜];Ÿ>–e3vĩ[˜CÂ0ĩEd&ÁŨČ3ðâÅßÄ[xĢŠ]ā|̇dd·ž{Sróg?{?ÓCēzW%0)‹_Ôģ€t6Ð5uWÜŲÕå‡ŋvîŽt3AđũúWŨŽÖÎîÍÜf<|m{§Ņ|NÕši'zž5tųŦŦ+Ū–ēöt/v–ÔģīífĮ);á8ęýÜ8ü A§NÂ<ōhđŠea{?ß~ õs?æîn‹xîēNŊå?HÎ?#A|)USI Ð@·óüŲ]xņâÅ\ dëšēUÍdĸĘ ƒ­ļ>į[ó~nũˆkĩzōf!ė'!Þ3uIm‡z*ĮÏjÝmīŠQ‡ā딙ÍFĩZt_ÏswuD€ĻLĢ6ÔÎIÁŲCÕFoĩ$|åeóåŪî<Ũš*Ŧ[îS[[äáB3U·@óo›™EÄUY Iû|ŪýėęI;s~^uw͔šĒū wģý<3›ŠlÂėžó~žÂÆ>·Ï{^žxņũ8ĩ5ëļžØFA•ô™@3<`ļó™T*šŦšĒ…ę. c-eO âšVvvFęˆmÏ= Ï쁐Ā_û™ÃąkĖŧs€FēģĖÃĖÎŧ•›w ŒĮČð[ÔnsWë‡hŽę™ÃCRĢIitĩ’{,5ÉõđšR)?ÓčT–‘HueĐËÜiÖU ŪuåwŌ4’]Ųę ~ÝĮōë†ĖTÉčŸë3D#Mûđó7rėix,43Ë +|gF‚§Âķ[îUE˜_kkŠëč"J‚#ÖŪ2b­EįąĨfŸÜÕ_ÍïŨ?fևgÏø\RāûÄÆXqßOW‹—eÕyŒuĪZû7ĖákĨĨ[ĩĖŽZVY]Š)AubŅIXøšðIH.ģúŠ+VVf%ÐkEvýŨēY ŒîánŠ}ņâjyþŸŪ™HVõ·œŽĖĐڎˆgŠÃĨ>ÉķZvM*Íg]ÓûÉĶÄ“ P Áļb%ŲX~Íl ;›4PFÞÏMNLŒå1é.;ƒj3@­ž“+6pŒdķÜņ[inËŧĨŠóĸãqœLđÄZ!ĩ™ŅÄ~ánäģӀˆïq–ŅéyYvFÄÄøyļŽ4cOÎ.$š’4Đ>ëSÝsũ Nģßֆîŋ7ÕūxņNĩH{ž•F—šįh+œ ~yķJ†Ó­ĩ@ƒ ÆLšÕí~ža]{.ÎĘ.…N„͉’RK―N LV‚PŦ$į›>ũ~ĖRvĒG,aR6=‹&ö~†—ŸįÔn>æ1:Îį›/#iNąfI=*Hg#Ņ4Ŧ1õėĶq@ W\ōĖė­zö-īÞUädþĒšškŠ{ĒÎæv4 ũ―Ÿŋë`xņâ=3·XkX <Ūkí―ģķĮrũĖjåu]įõóŧ”ĪX>å46đÚę>‡øÓYžđŽý_qëģŸ8)^8­Ū:Ą3æÕĪŅÆ”5!{?4ĖÞā9 þđŪÉĩ-։j<];F0Šą–šŋ9 ÝÝĨ s8wû*ô_?îäŌ’&Šåf;“īˆ‹vz+ģQąą,įn •ŨÞRøüĩ$âM›yņâOR­Z~đLڙāömŧÁĪbw[TL™XŸ]į5 ‚ä~F#ĩ@͙Ō764įu"l@˜ŲL ˜hîq=€äÐSŠÏŧåCØĪf•Í.Ā}ïÝ]N7ē*ÍĮÏ6”w]óHýßá·Š=œįõ™ŧ­~óšÎĀÞ<ėūrzkÜwU Ól6Ī)ˆÆ''ĄÆ}‹‚Ķ>'ŸgįÄãšWŨũiu0}—ïŪöŋŋXxca& %"v>_:°oĶĖ lßO˜ÓŽūķÔ)øzĖðŅÍÜĶũLĘ1LwėŋWžWv oîŸԌŋ·>'TJmnËCpîa< [mXRíga땝ÝōXã.ËJũåöíëuĢÏ―WÄéūÝūŒßsšU– įĮÝī3ŪR―Ó+âūo‚nôąØŲR?iFfggŅąŠšŅņõ°•Ūëjõî"ī"þĢÎ|[^žøĢq3 ĀÍŊÏŠïūáޕfaftï.aėn4ÔšVtĨ3&fpæD:ITĶŧųwø‰åQŧŠK@ĕOšųu]tķZÐY_Ŋzč)Ŧ܌Ωþ^lÜŧNŽ5īw’rĢšg1jgš$čîîû~rOï“iäkïDC·‚‚™KČ' 9)Ue‡EøšŠrœËũūAD,˜í|@‚8_ūÖY:ïï)œ3+)MEEUŽĪĘ~Ũ/^üÕc1`ė;ó°ĪÏëáP‹ĻŠŅÞggždîļÖ1` ‡Ž5û͟į13Ï―ŦRˆR}FWĸ–k‘­ęjU…yWý·š;Īã XšG鞀]grũITũ]īkUWg“ßÁį‚ģr͜Gz·ĖxöģšĘ5SŦj>™XU,BíŠŌuJÝ+ŠčjÃ#ë{ýEžxk5:ĢyxļwõaÆčî1ïŸÔðΝ>=ŒYĒNé!ũÞæ~]—9!‘8 FŸĩ@ŦŨšbŪm4wŸž‚áŊ:ŽF0üü"Īŧu5XAģÜ ÁhÏÏ­CÖï*ýJĩúĢęޕ­Z+vn5Ķq}Īŧą|8ÍcÝMPĻ1ķˆAâyžžvô~n‚Ó·6’?’ŒĄïÓiöH‰Y. Ũi)ŸȚÆ:œ{˜}ÂÔĸ^žxäņ°ŠFīv%ÝÃl^€îņ ÐøüÜ0ÉTe‘tš~UcËŨ!jH·ã”ÅlNģ:ÜaP7hj™™€ŠžũŲ9AāŠÎÞ-Ūp ”ėÛ} šÅëė‘m]ŨÐëų<øLkÃ2'lJSč[SSōwIÂęn\ŨGnđđŒųėEÞÏmnq­}? NcTĶTá—Ï~ƅnY Č܁ßŧ„w)O*‚9ŸCĘŠņ·ðâÅKĩƒY9ķ Á‰a u_ŸÏaF ECUĨ:<ŒxžÍÆõϕUhð|‘îķB­YæķĘ-Š’įÍU-uļ…Ŋģ po/šŧ™ĩÐ6Ý<ïlVõėý,k]] ”ûÞ|đpÂj#"Ū=R\išŨT-Ā=Š‹p’YYj?y7“1æ+Ü}˜OvOg‘U’^Ķý›xņæÕ’4ãXĮφ_‡ģJnf`ޙÕËÝÍšū ™õ<›FÎbWšÖeĀQ/ĀI;ĪėîáÖjMëŽ;é\ąšÛÝĖ―[{ošÐ–ęVe[DIF ó0kɏ›Māø†?ŨÕ]]Mp]ë܃8ėŋ+Oâ-Ð:|(3ëŌøŲhČ܀Y؊•ÝĀ|ĩG·h˜0„ŪvãúŽŪíÁŲüHaQ™Úl'*íė—[uÕÍ­ŅæŒëšq›ûkĖý“xņęjĮT Šö6ģëšrIŧĖ€]MãĄ5;<[9Å6tģšðï倞―A^낔Y#jČđx…‘Y[Ýf&aïíf_ą1‰ąÕĒŦĸųįš!·Ôhԓ\aFĻŧq0âYNXŨŋ0w:s—Ũį:„rũë]U­ļ–ŅšJ(?‚ÓÍū&döŧf•ŠŠģ7h|?HxZuëš`üđĢņû9ņ=VŒ@R˜ĸ<ũĻqÕŠ* sóWéõ'ņâj…S­(~Эž-ų|ķŪÃpfķÖeþĘŽĩZĶÁŧZBÄ"Ðęîž)˜€Z6ķÔ-·0 u‡;ÁnåčRÍ+k?)a8ŦS]šU€ ÝíīÉɍ5ų )雓ĩ.œIHžĪîÆċocũԆËhĩShsŪũÏC0ÜšõÜOe­ŧÆœþÜwîqÄŠžŪ‹@VŽUŽšÎš“““Ýq…‡UgWšü„1 ›įâŽîō°uyerũŋï‡FwŠZúŪ‰iD7Í<"K f̚ÏÖÎ=4ícnFŸpE@ÓŲ3MĀ“RķwRļwV ĀÉëڕånCâĪ}>Ũ$Ԍ’!+ŧ ˜ œ=[iHUefF;ûŠjĸ,^ž- Āĸ"Ϝ[+p| gbžRÓaSYąâú\* 3’ČŽŪi$ó‘ß’ÖčÎ]­Š#ÂÜÍl?Owšqzwhž>kŠÂĄđļHŽņ!wZļ{@h-muũÎÔaíîšíˆĸÜ7Œës âō•SOiFÃļā΀ėU‡FÃĖ Dļ­ÏóˆŽ*;k֖Ūϒ0ÁŋģŠŪëŠðŪdæcœãy`õų3ÁĩÎĐÅ!ņņâÅKĩä9@Ŋ]ęð˜ú–ݏŧ˜ąÎŒ-UåĄïjībÅxX'{rdĖän#‰—ĪÎðuĮšĻŌTy,õk˚•Å2·úMq?OW9=wNZMÄw Léw}Ņĸ=Þw77WũxŽ€ĄŦx<ģįoMðųyhķېP= –™ÁÍ5{Ûąóöė  ÍąÞø†Q ĢÐøMNā1æjĖŧĪ /^žÝbUië ŌFęūÜ―vņsõŪŠúÕueuû2ĸšxÝÃŋb[_šĪô”ŦĶ(ũŋ?W·ÐH—šš1udgÃānj}õe' ­ÏįÓ*Ąq2ķgĻ$ū>ąŪ&ĖÍ8tĸü|évfUđđ/ĸöĒà }ϟÕ=ņ1ĮyœÃŲ–}þģëÖy㉗LJŨõųÏÏOf™ŧęŊjw7ZfYŨlgif.•úu‹ýIžxuĩęĘ,UAösßđE·Yŋ ídVNX@Äęt._īx~6É8aݚfōęúmüæsFQ Ü;óŲÃĄ““―"4‰­õų ĨÆĩœ~v˜=ÄĊ'·Āøk?ROïÃ]Ųk-Ģõ.~ÃÉ,ģhãÁMI˜OM\ ėĒč‡'ėÆčųu[ļW5wûږÜ}ĘĖN9cøųqļšé4·!b3ÎgÔŦÚ­WVû‡ņâÝÕۈ9XīŪčŽ:­n ĪЇ‰B”‘ÖUæf4H%Đ几ŦšRļU—ZÓD[•„ŪÏ%!§ ÜžšŲoņxeŧđ› ÉI~1Ÿ$E“ĘĖ'ØÅ–›ûÉ“‘"šmácfÛÏcüúŲro §áŅIÚ7͚Îė]%Hv5ŽŨŨŠ*tŊkMŨÔæÖšô˜Šå4NvWÐŦr:z ŦJ;ýĩkþ•ÂOĢD5DCÖ/^ž- aþ%ūëúĘΙ愎-3[qŪ°š íûū}ļĐ}o#Ģ―KZôކÁÍIŧX‡€Ž=!ŠßrYũÉĖ–šÎðļŸ‡ä ŨŦðß`Äés§EØHzĄO,€]MQĶÚŧ…ĸ†!tÉ-NöÍãæÏcLŦãÕP·ÜݏëlĘŌ‡7Oßí1y]óISYÐY_Üd\KÝÓÝ;e:ý/āÎę$)‚ÄÎüEÜNû$ņņâÅ{,v6ŠÍnĻũ―ÝÎNķõm e%Ü~s Ę>JXRŨĩԇļŦ!8ý ÝE; .üü<ŋâ" ӚķĮLœ…ÃMÚŠ]i"Uį1€1’2ZÄáŲėĄRWC=yä#ǘ„uÓ`nŋ‰‹FÓs?IOåŲøĪC Î!~fíĒņûŽŪv”m4’ÔŊjä\ĸų\­đĄ"ÍînũeæûyĨRûŽų ŧRG˜ ũļŪŦŧÕēũčÖļæĻíĖŧĄĐņĖĶņ§<þEuŠúw_ŅđÓ`YĘÝŨó+FNųDíĖĮĮĒ–Ĩi~ō'Í}đŽēĖÂ^cîßċ7TD@ØđIóå EŅčgŨ™OõÜwuGœõë.7_A€ļVIļ‡ų}߇%ā·Ļķ;Ŧ‡Ų}ĸ·_‡|îgša:•ūü4š[ęĨ[ððëZÏÏ3‰bø2.†Šzî§*ÝüŲ;+‡gÍ:ÜÝõ4ėdgR–ÔĨ9ÜË,IAðþđi>íđósÞuúz2w 2€ęšT‘L@ø[pK$Fœ€ņríŸÃ‹7ŊVpóîŪĘnEĀÜöģvýsM™đė*ÝÜhŧū,™ëÄYefŦĖž[Y7€0‡ôä6cø™ũ6ãT9ÐH`ˆÏ WWUĩ‡ Ž]w“üüóRö Éęœ3ĸîŪ.ęü0K€ŅŪ+ēRč§ųs6ŋnhĖĮ%Uķ‡ŧĮĖéîøNĘ4FK@ŊøÆ0ŠÍÏģ F„Ûī6ôĻqóyh6ņf’ĶEâyœ[<ģ­A ôÞüIžxDeuũ„ €œ"Žé^IčŠ~Āæ ûĖķUÃģËü,mA§ŊˆĘrĸw„Ðj3ŧÖ%Õ~îĩâZWf ßģþ“cÐuķ '„pMЈ9GRõ™Ww} P>[8YŽčŲ)ŧû} +–™Ũi= @eĐk Äō˜ÜūĨgĸg\­A :,îËŦ++ óߝē Âþ[xÓSĐ;5æŲYƒ9g˜=ūŧæŸĄ'%Rø“xņâ=kTuy,’“á2%ÞӜxŒđŒ6QĸÃõš1ģņS™ÓÃ=üLF7ÁoFŒ„“°eS{ð°y<Ï=Á‰4Ïl3 ũĘ|ö4‰ Öįšb3óXŸýč[­Ø™ōˆuaŸŠ*Ķ3?ôÉo ; QŽÍÆŲÝĖŽšü€īûų鞆Į–Dã·ÜėÔĶ]ŨjôDŽÍReJnŠę”ßĀ镚Ã43ĸđŸęÃÅųėnāÕüMžx oĪ_ŲÖNá^•ÜÜ-v%†Č]enîQÝóÃÏĸðÉįé†ub°#|™Ũ94ÓtVæymæĩópÍÖüЍjíį1įš>Ýa^™N,gĪėZŪ.•Ėð[â+·˜HrĩÜÂÍKÕ]‡ˆģšÍū§U,|ĒhfÆę~žMŲ?ŸkWN2OPëÍŧ°Cí[ˆ ýšhv=ũϏge·ū1ÞRvöašįÞtuذÓÝOëAîsĒå+žœÂ…Đ Њīn)SuÖĒ&vë ãß]AąÔY;ũŽļ„ßä—éalŅ BÖüß͉aýft™™ĀÜif>J†ęNĐaîyo€ëšH@‚!VĖ:›N‚Yiî-Ívxsģsä/^ü9ąŨ,@ŋYęĖžbļï[‚ŧkËÝŪuuĩĪå3?ŠĨu… ņ†™Yï†sP֍SP­jųWs! žýHZĮ 0[cĢéŦ@ÜÏ#ČāøÓp}>=éģŌÄČV'fKÝ0LïÏ―+;,š‡ņøï/Ķr§/ûƐ·âsyxu[øōčow$ÜC™€H€öė―ó‰kҘ{‹[Ú3+ãë#MīMŠÝŸĐÝ=ÐÝE„û+?øĢxņ.(’ððýltųķū1Ø-Teļđûģ j ŸÔp­ˆ ?ĸđýtJØũ#"Ū'í0ļŠËÍHŧ·z8‰ŸŸÛÍÖijžýlĩ(ũ ø<·€+>ÝęŪn­+Ššē-ĖĖę_īq^E3ŋ~H­Ļ"ðUđš­>Ŧ:Â!(FīčZį3ŦÕʍpÄŪ=m7ë\ū!zŠ6ÐÃŦæų-ŒÏ“afî™ĩŧÅ5qV f/ŲūxņĐÎðØũ>ŅÝNØĻņÍ݈ĖÝÕū͚ŧÆõôܛäŠvĻ$mĘ―atēK0 ŽKjIýd{ŦõųįSUĨ&ÍWHĖÜãZûy<Mķž|h zwWĶđ_'ķÔf<ņ€ÓXÎŲTŽĩNŒáîtŦœðoÚ7ĨLfvdž·yPVÝÝEø ŊŠî27ˆģ~Å $ÛŧÜ(ŅÍšÎZƒõäïķwgæšÖ”øŪp3Õ î^'ōË@§Đßž™/þdáŲ™ŋŒæFXũũHïûĐ뉭ĘãûšÖi)wzļk W|;fí ŒÉÕÜĪÎĘáŲėũu­Ņ`ÍÂĄŦģöŊ›ŦÉ ðŸýŒ0ÎŪģEPÕûI•Ė-ŦÎ3ÚĆĐõđ.#sïqūu6ÆæŅÕęÆaÚŠôĩ"ĒĄąØÆņStÐ[ĨŠëZkÅ~tũåũģĨ>MęŽaŌŠ:6ŠåĶ_D>Ų wï,An~FėTņâŋŋVãHŒ,ĖzڍĖÐjwũn‰Âā2"†e$ļ9…ŽyĸD/žĩ§?Ï3CœQđSÐÉčÜ}B`Ŋý<óĮ[UÜ]Bw lŌ,Ū PæŲÕ^ŅRíéþ]ėzÄx―@Ō Iš™ÛWÛ}g aš;ŠĘlâÄ]ÝhЭŠ[ãÐå„$Ė aeĐÛÃ=þÉŽ:Gį„OąMæöp“F{āĸšŠ%w^žxņuĩĸĒ›˜+ˆ#Š2cDĖL4Ķh7?„Xø §ĐáØ ÔiÜ{WĶō˜I :ėšV>[-7_ŨW‰UÝ ŧęüÜÆÖÝaÖŠęŨÚy.ˆøĘķ(s'yŪéˆ"wrrrŸ=ë‹óxÏT™MÍ䔛[We“–Ï·a`Ŧ‰1ŸėęXņÍ*ŸĐ\u8ú”ŒíúFš'3ŨėðŊ/y`á|wĩ/^üMŠõSHþÔCōšÂĖ4ĪuvŊc*u(uÉ{?EsuïgWi­―aŸOíꖅnOĘLJ}‹Ęݧ;ĮÃDŒyĖÍf(ž )íl~…ÏuŅ ð(Æ>Ģ9#ĀtĪį—ĩ ģrÍÜIŅ`­†qš~Oz™ÜiÎŽáŋĖĐ Ļ•:Âŧš2ĸ›9’‹uÎëöNŪk=ϝO9-VėûĄÁÝÍl6îkï}ĸL‚~ÅģUGøĩÖs?­úüsuéŽ_eĪpÉųđ.H4cīž.^sA•Ų•ĸüģÆŊëí]€ü|ėĖ<œB›™‡ MLûäЃ”FA|ïĮ@áŋPW›5Ņ‚Š1Œ•GԜސHlŋÉûūŚ܎ŧt˜QdĐAXDî];Õ85ûĶ­ŪnéÉô0’―[uJúß.ŲÛ^ßæ˜‚1ÖÎN_þjČiv§“Ë/U=kŊÞā4þ>?OŦ=Üijøōu]{lcâÆk—9Ã|f|2Ąķp†íÖu&oN3aË#voģcd§’=<ōΉđų\WæîîYe<ĮH&âŋQėU­ëZ~ĸÜķ⩖ԇPN§aķđģąóĄYXÐųü<8)Ĩė]gā%tNŪ–Ī*­k}ÃY`ŸõÉĖÚÏĐŧîį1ÁÍÍy?{˜ðŦ ęę>ōÞ?ÛGl&lĪŊîB_ŒęiÆ5;ÓąĻ™@GZ@3īŠOQ LHX›būôGuYåîÜOÂt­kWB-`}Vå&8u;?ĸų1§ÓT}ï=ņĒčķœîÞ=yäîöý*0!ðâŋŋVxáãSá™%JĨˆĸv‰ËÜîûĶāMõM^’z īĸás=ÏCÃ)Į―þ―ØdãGhI*Éúз/w?VīĖÃï3QB-â~Ō?ŸOK'_ÆÍüyî–8M―ElīS?SUÕĩ"ėŒĻį.‘ũÏ ŪkđYu™8á^“ģåūĶ Ī–ōsˆįíûėåWfŦeáÜ?”…–ŧĶrƌ)BYīsÁyįIh<= đbýG'fÄ‹/þÕęÎū>…y]ŨzN6íŽēÜĖhn–{ÓđĖ Áœ<ƒ\f1AYF§šs?8Įn­VÉĖč–OŠ{žaWĢc- {ïaęĢd 3NŨΈ•ûĄ™A0t6i6ģð„ęæÞ4ĒYW’v] Āýl7ó‰Ŋ†Î_JŌÜÔP—9 vaį6§[L'…7>ũ#€ā„Õ–ęZ!Mr/[wíÚmĶŊĖĪĄZáąkÆjŸbvū…7/^üMÂД­õYŠžō˜U7€ Á“3<þúޜf(uvfƒ–ĩŧõM\|ēŧÍž‹•šŧAÍ*ët˜{w}­Å™:OÉBŦ™û —Z*ŌĶĪ‹fnnĢåúÍclã1žõÎ,-"ŧšuyĀøė„zōŋĨ)ŋņŧKÝ]ßčņ0·'’FškjuüÅafũÏ SØt)ŦŒ–]Ļ6âú|ō4åBž"FąÖ ã‹É‰/^ü=ŠĨa­ÕÕ:Œøų\•{{˜„ŠūÂû†,|ÆÆÝ`8ŦÎÐý+)Ĩ R%ÉësAĖL•ÖĐ Ŋj4ž…ƒ_ŨĮĖšŠ[vũIâ3•å ‚™ÉÜ$U%ĮÍŒ Į$ĶŌdÎîŸý―ØxNÛūK†Ė<Óqd6ÍÆÁqÔl<ŒÝÏó„ĸ‹ođŅi}~•e6Tl;ŦŠOūA?{Ó}]'”6Į›ËgïÉP°ģÜ-ÜßíÁ‹6Dq‘jõ$ŠäއvCp•Mē A0 7lU6ë 7ŦL“ß:Qaæg°…Ķw+ŸÉr wo5Y§2+óš.ƃlēoÆ50Ŋ$ŧz,j1Ūģá_óŠl!Ö:.ƒē0A“ģÕÕ§E‚“?kæ­6CW‘ø.Ihŋķ.3Zw ]ĨĐu4ŪųŧL[wÍúķ-xXw“ŒËf„ðPkzƒ@æ‘ëâŋPė5CŲčüûĀÍ|ĪNš5ŊAíL3Æōę”zŠ_ŠóŋŽØýMĻņYŒJ0Øþ―ÁŠ_SQUuØ0;Ÿ}K ™ĶvwŽĀq@xuƒĒqlÆcΜ%ōtĄũĄČņ,ėÓø›™æ‡­Z€ûôHĘÍĨ>ãŠĶ(5"|ÆŌÎ>+Ũ2§Gô‘ëΚÁ$īÃĒŦI›q{ô>ŌãnĢ_Ũ‚Ô-‚xņâ/â] +"ŽæīéæËI3·u…ũģ[}’›Ūš"@VuįĪ šë9Đę4·ÏéSĻúė"ŅT7ŠjÚÆūĢīņīČĻŧâôƒweu˜é Ņ*ŧîÝUՇ+ÃOąØīiöČ{§đuWí=[;Ė~ŪĘt§Ý#Ŧ=–Ũ°ģ{ÎÔܘMÁ+–đ?ÏÓUĪŧ{VÆåŸĢ‘Č]]"mŨSU*ØŋžfT· 4íĖ*‘Œx[^žø“T[YFÍ&“EęĸWV„ˆëė.Ė\PŸĘÅk­ĘĘgCÖ­CĘpũ§ģŧ#ĖÂrge­Vý/šģ[rûŪ_Mæą-€ĒŧŸ°Z­å’έĸmυPÝl|ûÏŦqÞîlļéÉuĄđؙݚbuaÍĢ+íHÎŪ6žŪ%@úē!ÔĀygr&bōYæ†Å“™Oš{,ÏJ€f~ō' ‘˜rÉåŦŧŧZęīø†(þMžxwĩ+Ķæ Æ)ķiĩŅ@f%SU u,?íßÕ_^ĢY„Ë0Âgė.ĩŦŽVų T–™›á?ũÝĐĩžŽĄĘ8ËÐŽl#‡ŋ;Ý·Í%#3 „Ÿ›<ÏÓOŲÁw‡ EļĪŪÏ"ÐÕ4þó?|Õj ;ŒYįâ — 4–š2 r·.uĢ›ķëÚ=<„…—z?ŧģ-1A_`īÂŦģgSiDï4:œ f·(Yõ–@áŽ_žœDĩ(\ëęŪ*UÕú $Īļ"Šz fT7Fxۚ‘ÐĖŠ[:,Ï―fnFæũbžw­`ļÛ3Õ8$ïį1Ž_ 6stïÎn4Uî7ĮĄûÃõž{‹ˆsątnNĮâĩN*X·›O„˜™…ŊoY: Âޏ›ŅÄSųcîn†ģųús-eÞ·oāoÉÍÐ- /^üAžS- }oq…Ó*+ÜýrIÏ―þÏŅ@f<‘ÏSÝkÅ|ŊJÚDŦ4t܍ęôðu­#­XA·Vuö:UáĨ6ũˆ/Ļīší{KðåîŅęۊðFëÏigjNīŸíäŅHøæÖŠEĒ!‡™yU?™’ÂýÞģrupJmõų,QϓUEĒŦÔ-ŋ •ÓĶ>á^SŽÛhĢ]óARdUi0XŲÏÃŦĨNÅCæ–!ÖÐ-žxņâ/&{́=@āĐĪņŋN*3Ģý&ÕԐwënvrwuŸŠ—ððŠq \~4RŲ}Ėo”‡į7n1ö_ŌŽÔ,itsÓØ{­āį)ŲVîĪ!<}­\á•ÕYtˆRũ7KwM{ŪÍūuķÞRÝ)Ãé}Ļîv7û*.fkãÚXÝ:~„ G‹iœÆu[Úģat ­"aß#A€\áđKj7wģé73'^žø‹xwĩ€ŧ{øÞÉob?ũ]3TVg&`cÞuģļÍL{đ„ŽīßúŽYYŠÚ;)žvŠ2ģó ’ ũÕęiĻ5·―KÝ$iØ;ŧڜݝ{ŧŲ$ÞÚ·Ó+šUgdŽå€*k†ĘœīšÖš.u›tķËWUwĩŧĮŦIˆéūýejšÍcĖjuJÆâKĐ ģĐVMwÉĖúÜoē#Z–[œyV­öÃ[xóâş{Á,;OŠÂýPO›ųŠČ]ŨuM* ÕónļK=#ĄGHúĶt;›#ášķ]I_ëjxÕîþ&yŸ[ÏPéú+ŋJRG‡°ģwDāDXkА;'*ŽģæõĄû6ģĩ<Â+ģՇ"ŧjÂ^Uc9ģĸĶ›H˝đ‹VI2ĸ6ĪÕáÜ1VŒąx #Æ"qrņųįC‘€@šwíbß{Œ]ųÞžxņ'đLÖÝ4—ð›`m]MT_ß*ˆĖ=åŒ;ũÞ{2eNVKFøu]đgQ‰FįđĀ—Ÿ›gâSŦÏ €:ėÜâ°ųl'8]:gó[;iķ|č]ü'ÜÏ ÎjļŦÛãðl dCUÚ_ŠīVe•ŧ›Ķ0ĒđbĐy1…ūcåš ēģ éĘjáú\}žSņuũNų}âlNåDu·„S&öĪj}ūÉ5&MPũë_øŦxņîjĄŨŠ]i2ĖZģZg•đ7Ý2āŲ;ÜÖZų<]=6ĸęŪĪŅ,+Ո0:P%ŅÝ)Î9þŠøę č„umAŨu}YŒ0·go8­Žų ǟŌYs#į*=,ÖUguJ˜ÓļM\wÏŲóŲn~ o;;ƒnn]Ĩ)ķ—f)ŦAø:]Ōš–{ԓM‘æÎV }]ŨHŋęVíī°8{˜Y• kčāˆęˆ—kĸ&^žŧZs›c"ŒŋÓ\éĮՕ]å#Õ žýfW·NbƒĻĖĒāû~*ËÂIœ+yļ ]I"N^—Z+ÖzžÛOZ įđs§›åŪŅúÆpŦųņ2Ät/‚u? 3ĸæČ “ÔJwÆ ĩ€ŦĖ<Ŧ,ÂÂO+ZŊ†?đwĨ#ŧ†ü‘U;wŦ<ŽæĖiĀŲŨVðģð<{Fėü•7R7 î~ĸÜh5šfÝ6TgaËßĮ/þŠ17Ēŧ)˜•EĀÂL'̟Qtūþŧ[íęœŌFŧïÝ §MĢ 'F<Üj.iöĒãî•ū3{įw“[―ũsX;Š”ÕĖüđŸę6О8ģáĻēĀžŠ‡Ęt;ŋ˜­ŌōËĖŧŨZĨ&8Åbģ.ųï:ØÝÍ|ÖÄËÝ"Øęîk}ĪÖđÜOņxæģ<Ü-Õ·/™š~ĘÐ]ęÞY}ųōøúqÍU­RCfėęîÆ‹ïŪVÕhÐ °ŠD ĄŦĶ—āXiÃ,UR ōu‚ēHĐO“îÉyÜījį„ętÃČÃGīšÖēðYYĀŽH”đ8Ž|ĪuŒaK`>ĐÝ $ąbAi+ĐŽÚÂ,"+­XÕŠÚĩËÍ!u·›ŊX™-ˆ#ƒ­ FkgŸÏ?Ēî―mćqí;Ņ‹ÍDÔŪų[ggUAúÄáY)–ĖÜ08)ig6ī>éáŋŋˆw Ļ cŒ$–æ4Ë{ÓĖH;‰ąĪ™YUΟ―į,sGÎĩ<"ȝ[j‹UÕ*Ņča;SíbVŧ ũ IYú'â6'0Ąģ3sZ3ũØj œu°đë[ũĀpPŲąÜÎŧąz­øöF6⊓§˜ĨŽå{gå†čü ŋĖ ēĖ"uÂUŧ–ų™ĸÂĻÜ$ĀiÎßvœSœÎ–ÁĖY•ÕMw@įv23‘UÅSoŽjĐZ+„/^ü=Šmu˜Wn#ÍÝhųlAHÛÃ,îYÕępõxAØo„•F3‰ŌQæySøōKŲģ^?ϏZ v7ūđbŲՅžn…R›Y˜)KđÍŪ !ĢI]Ý~œēã8g_ņœ†GšĩīũĢÆĐ†œM€Ž* įëĻZŌY2 *Ąrs#ÔՐûIx€–ŊoØĢÚÜû°Ĩ-ĐZ4SéĐénhŠ}v2dFwŦĖ–Øō°|“―^žø› ĢŦnÝ "ũƈ FN ^Ũ59Ų+NŊøóH03ĢíCĘfĪQÝ[}ļg:føiKl­ðY˜:Œa:œ&ē#%M9NŲ8ÜqʎÁÁÍ&X ŦÍÄý<‡7)ĄšįÝ tũđØŦ;+ŊuņԛCˆ3ÏÞĸų™lÜ.eĨ[x˜Z‚Jöɝ_‚›î<ųģ‡ĐÝúž3Éåkï €ÆîžŨh1wžŪë|Ō48ã[­WėõWņâÝÕJMú uŦë$æŒŦĄFwŪ ZӒ Šģ@^q™ŲŲÏj#ÃaC.kbö―ĻIū5û:Įþ; ‰æQ‡€iü/KŌėđ@kh ˜°ÂŊÓ·åîs§Zaa îû‚ŧOŒ·EŽk^â8ÏÂaÓx­õ:Œtu—ĀuÅäȰbU~KÌÜŋ>ąne>á!špĢNūĖžÛ MöÕ˰Ģ*]žUĪuÕqp=é”īkŽ\$mliąn#–I%‹ð°Ę‚qåaí‘ÁĶ.ð0āÞĮSûéVîī>ōÞ7ÅÏõÏI7lãąŌî$ėž‹z7ÁõđœŽ―'ï;ŧŦŌÜWÄ}?ß7x?tˆŠĖîúįš@fÖ'€{oBãiŪl/^üEžSmþ~VëŲ;w­ Ý-ĩ[TVwĢ5Žƒîr§qž­!UWŸŅÏ: Ý͉VU‘„P%It>yæÎÎÓM "ž#/‡ÃŒ'–€{`úĘ<ÜlïGį‡îŪėI!Ļ.I<+ckuĨŸÞÝIÏYWŠÚģũčîĘdÃÜöóĻ›ˆûÞ$=ĖÝóÞ ðßoýRÝRÐþG•ð<Ïė—ikÂįói`Ę#@<õu”đđJf4/^üEžS-ānNÏnsšŊŅ„ˆėRũ7eņđaąeíq—5ũjb›üđŸË"VYĨF‡û!ŧ2ãĻr_Ÿe°{Ÿ;.ŸŽņCūČJnĮĨéĮĄŲXiÃĮ?Ņæf™5ŌŪåÖŧ;’cΒY8ågŨįúė.q%âŒĒcûũ™M4Ģ Ý%AdVŸ;áŸÏ­]§4ė„ÎÄáýV=įÐ D·ōIsšÛófuf7ĉ”ĨĖH·;ģŅ+” |]p ›čây‘9 3Ÿ Ÿļ<օų-wĢ)aæ#üęCŌ{ŨÎ4Ą9ÓöËՀؐ™ĢĢŋxņâ/Š―š xD—ĶEņ|ų―UMš3“SKt6IwĒ5ūŊ8SįsĖŊ$ũģÃi‚ĀŧŠö˜zƖ‚ū<Žž$iFâH ZäÎęėS4ŦÂÜ%ÕÏ―3ë\<ž…ŅxYí:i/17›ģØÕ·Úķ6Z4Þûyō™`ļ& †ÜÛÄĩÖôŲHýđ>°sk ĖŦ*ŧhpWš Ý=-jÝÂđĩđåđfS°QėŪNč‚Ð4ĄYæÞ*Ïģ)FÉŠÖĐ Ï*‚F;Īü-—―ÖúyÂDÆ@ęžĀõØĖëŧjũ0ZUŌ-ĖvÖīHÐÐ œœ5+āôîúŪ2@ĩTū)·Uá~ülfnÎʉą>ŸáŲjđYU:L.ų<#ɘ\4ĢÅŒŌÉųþd“ã‹ïÁ-ēÔYFžöŧ%‰ĸ4ŧžĖIyĐŽ†H,Š"`įšė„p­Ŧ…ĘĶņžÖo’atĩm‘dWˆ5z~NiãL{—ĮŽ/HÅ:W‹™u]ˈl<ĮVŧÛr'@Ņ}ÍɝFËŪŪZkEØ~2ģT"ųė-h…›S%ƒýs} t‰nqÅÞÏÉåZß•-MĶM6ųrIõÍØõ―Ÿ°7‡g9ņ`™UĘÏguŦ%ââŋ7ž‹'ņÎûy„6#ĪÎ ŽėÝÝ"Æ4%õ2p?7Äõđ uĩZ~ĖŊÓz03+ShŦÄčšv~ëu)üúV­ĸEA‚ŸÄXģĩ.U?ũ]ЈČîCōEĻΕt`ŽhÝfÔWPEiČa43VÉt™įvN ŠÍÜÍ*·ÔĪ™‡ZųlHf‘UÝč.4+ëX„8?ŠĘZÝáĨ&ĪqyUĒÚwžį6J‚dqžŽĖg‚‘Äūo3ĨⰈĀ,ÜņõÚÉhįŲda“-9 wkõy6™Aøƒxņâ-'§đóįþQóŠ b>Õįkĩ*ËšM—Ē$úœ‰áóđ,Bý-bDK‚lųŨÝë1§UėoēxVu•đ͖šúüģ:Ŧ ę&QRW…ýíå―ŪKÕŌ·ŦfŸ\.7ÆrÕMģRžĄ2WvAĀ}jkی+VuĄ$+Ó,Lėj5Ô°āđááfg;Ąk]RïãTUö]ŒBŦŊĩŠ›ßÕ)Z:e6Ÿv›ĮuEUĢÛh4Ė  lžĶÎýĪŧŊï •ĀÓ™Õ-üüįŽƒÓęã4swûĸKģĩ.đČX€š›ätšĩÚÏÕã[›ŨÏó˜ûđƚЭ늝Ž*âïáŋW0‚ãč_U5ÕÜ4>Ï―ó!éáãHp· Ŧ=ÎĻÕý Ē Šîž%ęoaNüēįZßĒÃĄž)žqũqšt7uUð͑™ø›u”XÜįŠŠŌÃHHc öĖ3w{ļóTáŅu7ÉŠäf’ĶúkžŋŦžáÝĖh—Ú}Ģį_@BW‰ ļwfįŽ šŋánUĸ IũsĮ<Õžûž5;Ûhøƒxņâ=3w#ÝŲ‚(‚ą&=ËNđ,ũ“€ŪÃY]RũÄu’ÔÝšT]áî“Ųšķ EtĐ;ՍˆŽúU_Å)ww‡$Á€ÃΝŲáæÏNĢĖ€ÎČÜ*{gŦ?ŸëyķšA;3l‚ænģō°"ÔjuũáÓĢ/ĢÍ3wwûĪ›įdÜZ??ĸņˆĩVøAžļqw7GgGÄ!LÞ{ϊ`lN ŲÓ*'YÝNæv‚w'õöOâŋŨÂÐąŽŠ+Óh+â+ÝĸMđ&čæUõóÜÕ;V {Q°°û9ķîgšÛgíš―DcĢëžĢ_ ÝÔátõÏÏāü~aϔnÏX] ž―ŧš3üÞ{f^€ûgÂ―bb ā6d75Ó]î>zâXŋ€ĖŊĒJߞsģ]'ö0ü\PáqÖŠŽp·‘:4Í äóÔÎ-hBí|IčþÉ2oIaˎ-­ŧ—‡›Ýû–īb―yĩ/^Š]ąjŽķFwŸåĨ9ã$ŪîÜ~Åĸäú?ýĸý·ŪË~Ã^ úŠŪ6p2ŋŦúyîĸåĸúõþŋü_ĸ7ĸÛĸ-ˆŨ “}mTÕÕ]ĩĸgĸ‹ĸųĸîĸø?üĸOf„ÐU Ïc”$6N$ķV-óĸËÞyĸGU=ĸ7uÓč‚ÔĨB Ĩ Ą#"ĪWQĨh "ŠĒH3Ō{ïĐŌ$tB'„" -ô !û}ÎßßĮįȝDđ{ũė9ũîÏĖ3óŅ4āĀ ėĶݍ|îķlš‰·xĢęnģ$ æūΑŲÔÓdĪiGåâÏēĒ‘‹4ŧ&KļÉgcļ%-l Ž•đ\'ģ)úņĶ_ë>˜Ŧ™$ U_t1Ԉ…C[Æ  )ØÕR,#―Ũ2Ë,ˇÛb$B™]srQ!RJpŅéĖŠ>hčð:u"ĀÆđóggýøãõŨy66ŲŊ"Ģ]`ĪĒ7OTŪ\ų#Gýą`þ=ŧM§āĢóĖž‘~ÐÛÛ§Oŋ~ū>ŽsgK6SĐŦ;ĸ"&! LTžy ĖĐ8Ŋ”ÃRõ`vķ˜RũÄ4lŠ=ā˜ĒijĸØ‚Ē-+P·éNŠņTóJËīåōŒfĢL ˆ=UmĮčŨĻâĒÓNR°·ƒB€ÆČžI˜Óę Íq/°lš1‰:Â9ð^*_ÓØBŋË,ģ,?&{)”TRÖĨjm^âÏæ:ģsj„Õúsņē.]šÞšyÂV­ZÍÏ?BáVŠ\đFxÍŽėlŧŧĪî“ÚUŠô5ÂÃģēēð4ųeķœĖ|Įúš8ŽĖôLĢ%ĩļÚ]ņĩĶîē:§Ŧ††V|ó-Tød„ķÕk†ŨŦ™ó*[õ ^UĐZ'ĒūŊŸáâ SØÕYo OS–k2^eMZ€“Āņ ðfeIɄՍ2í›`Ũð…(‹ŦGü3ÂáÔ0UĮlrGøÚD 8fb’ØkWCmóMšČŊé!—•'Đl^Ķîƒē •·Xk™eųŅŦ•péąĻOó8kŲÎWėke9ģštûĻ|ųōK/ŠŽšŠü‹ŋŸßģägĨ˔š6ííÚï@“K—.|;eĘõkq:wýzō7@3áîÝīītƒ7Úūc§a#F– LM}đjÅōeKkzŽvQÂJ―1yę·aááëÄÉãQS&―ņF™Ŋ§L ĐI/_šøëŽ_>éÕŦAƒFļ™ÉÉÏ'OœxâÄßóÔZ[#°Kĩ–Ö4HüU…Éë5Up’Ö!>4Ô]?ĐkĀEÅ74tĐVč†˜vÕāŌT‚Caē DąLû’Ytŧščü˜š}p3L“‚傌ðĢiŧKEF―Xf™eųĩGģý]ZgEéĻ)‚]ˆSąbEœŧ}ûöžLy‰™–öp|=eJ‹–­VŊZAŠ~ß~RR^ĖúéĮ “ĶXóVåJ°Žlđr3##}čāO6îË —._:ýũ ‡—ö°ÁÔąũåøZķ˜8až·—įwŅÓâã 4jÜdßÞ―įÎÆú8þÝ>ú8îĘå-›7‡T đqãš(žhˁÍ-ÜR^šS,ĀžŠuÐá k t€ Ųô3eĩ6yŨöʕÃOonœM?ŧ›xũÐŌøú`”=3MÚâz ’°ŋdztâ-ŒĮQÃfĢ,'Ũ)QGŪGX%Ōó$B@ėNÂēĘ=Xf™eųē0Ũøv#š@-ôōöņõõãDff–ČYÉæđ— 6š{ũîßO›öÝ·—.]"Œ[žD‰reËîŲĩë‡čĻŲģ~IMMRÁ!!ĨJ•ÂėØąS`Éū>>*„Ķ—ĐK†……ÃųõëŨĐS7öÍ7ßJJJâŠ*ūų&č\ķlɋ)ééAAA!ÁÁ˖,yųâ^ŠöŠÉÓ 7Okę@$ŲüY#uĻúģĒ ãŌ‚ãÎ2@ŌŌ<ā#$'ĀU$ž0o‡đ0&d0ýu4Í Ö0JáŠü }€ė’”ÆŨŠPƒÉž(Ķ=ô’Ī X |UKL5nl–Y–ĸĖōj Všīļߝð,ņMrÔãsKƒj)Éð§Lđr$féÓēîÖ{yŠ.—dt™U™™Šč-ÞŪ”öŠÁh_?ŋëŨnœ=ũðÁ}V1)ą6ŒuU™P}kËŕ+–Ÿ=wv͊i/S :zĖØ­[ÚŊoî݆ Öąs—­ZųtĀŅ#‡đNŸeÓIŧã(ŋĀ ĻߊĨī^āŊ“ą^îž$˜r^OÓüF›/z ĖīwԔ2§M{°såâü2ĄÉÆE!Ė]J<ðwĩ°ÂS—~ĨņY)V0rÚ$ß6ĮĶåsÔþō%1RÝj[ŪŅTÔïÄ2ËōĄY"Šē5D“ņÄpČ`(„‚§Nf@Ŋ^―Ûī‹ŒĻWŋMddPPĐŧņwK—.Q'"<<žRĨJî?ĀyÕBŦU ­^7Ēn‘"EāŅģįÏqę˗sŸ=wÎė―ŧw&þûŊéČLāâųógOŸ<y‡œ9ã‡õkŨ\―r%88äþý„1#Gœ8qĒzhu‚ÎėėĻĐS~ûu37lŌīnƒÃGŽáJÄëÄ/ŌiĀTõnpŅíœÓ<2tÅŊÔxāã-(ÉĘ|PS ,õÐÝČËڅ†š*ë!Ē VQpMÉ@ý‹Ž2ņÔ,7SSGČXœ`LÃÞRÆĶ™ïšv’ĖB..ˆæĀÄ:l–YfY>ŽÓ-{—Iø‡ĶîĀCôW}V­\F^Á AƒWŊ]ÏÐÔÔī6-?˜:ųŦ1‹æĖĸƒ3Ī=Íýý·;wn-ü3Įsóö>ĖČČðóóŧuýúēĨK8đmįîĖĖL8ÓģGw<;uuyOóŋþ2󧟝=gNRbR‘ĒE~šņCáB… öøņã%JėÝŧŽoß―įųģįĖ™p`ïÞÞ}ûuėÔ).îōŋzōĀîŌ’_AĶözP3L—Ļˆþ' xīÚË4Š1ÕąFøFo\šĨëhėbPɂĸŒiV­”ðŠ/ÍIBHĻ­Âꌡ'›K8šé*ęōŦíåĨJ4HKJLCSĘŽÖbųÔ,ģŠÅī‚V}[€bęŽ` éii?~ÝĐ}ŧčĻĻɓūnÛŠųƒûũϜ>Ý­sĮéÓĒúqFë–Í<€fëŒïĢGþûėßúũéŲĶÅû æÍaǧßĶesžÚys~ïÞĨӕËAņÎįOŸõúļ{ũŪ}ū‡ėÚđý—Ÿ[ąbé€ū}Ö­^ĩ`þœÇŪ^ĩräðĄ_Œ―qýZΓūý]Tn]N<ū0fþ˜Q#ÏĮÆē1%Ždž5˜r Đ PwUzÞHe åĻz!ĖÕÆbĶsš‡VNpRŒK5>)āŊúĢÜū`ęÁ\Ķ·˜‰·člb kmÆËM37 ’ °Ø’aĶÂeõpĖ·f™Ŧ Ø1@ DŪ[6ŪÔĸĒ@€Ö^§NĸįÔ)Q{qūr'ęíŧeS™ĨÏk€mÞļ˜˜–Ú773ãüŲØË/0R[ ŒL~ûöMbä?aũîÆßđíĶf:mZ·ÖÛá-ÔËôŊ―{ėÛŦąN;)šįϝ;þø.­ÐÞ_FuWIĢwĢšÝSk+Ī"Î]Ž9’ãĨ^'ĩŠËÃt™tI1ƒF!īųš9cۈiå 'Á%?F5œš2wí+ŽQk—§lƒyƒ["` ŧqĶnÄl`LþQīĖ2+€€‹g„ņđhxc?uËHttÓĖĻ―āėÁVņЁsʑæ­jKÍųįŒ‡ģŲõælë6m'Nš<áŦI-ZĩĄĻ,#îR.įä(:Ur…ÓvMõR4ũ6xšęÏŌÝKۈXP>B•ęÉæ‚cčŠâ2°Î4ååĨ”ƒi6/SCmLßčåÎŅ?ĖÅpK…đ\ģ0Wdg‰ß/žaĢQ`T`ŸoĢ}cĮ)ÆĪ€˜„ā6E+Îģv‰YؑĨUķâŧęV›tÏ5MðQQ_tg6OGžA:‹&æÖT Áω+ˆe°ÄY!ųŌ,ģĖ x˜4/øe4ŦÉī<`jâäÉóþøsĖgãF;{Ξ°aŲŲŊ >->ŪÔãc›Þ™íĖĘĖOĻfdĶãÜ]ÍĖΆ‡„nŦTŪšïāáðZoó’ÁĒú āðUU}ÆĪĢšXî*âÂyÓŽŒ_ €šåč„ŲüBÐŽl'tÄŅ~Ĩņe|FÐĐjđ2!€—đĖ )ŌĩiÃQáŌū;ÂE›Hk ŲC+Į4A[X2ĀÅXMÝ-le€Đđ°cRŠÆšP\ĸ„07ÕeÚ a ũ4^?ģh…Ŧ•€ĩęšseVšW~4Ë,Ô xĶqE§ZúˆžžžÖąKŨƒ‡lȈaCcĖGMĶmŧČ>ý„…Ũ1v,ÕôöjÖžÅW“Ķ1ĒR•ŠŲ·ÃŊUŦ6Ÿ9aJÔwïŋßžpŅ"ÁÁÁ_M™Z#,lØČQc>ûüEjj`É ÞýúGEĸŲۃŸ ›-#‡ŋĸ§ÃGīlß.ēý”oūûčĢƒƒ+~:dØĪ)ßīnÝęAÄâÅK|ŌģŨ”Ļč!C‡Sɖ‘•6ÍÄÉS+Tîöņ'}ŌËáð ­VcĖļ/'OB§LãbkZvĻ\ÚĪ7–äé­ Rč o@ ŊvÕåžF\šLš‚ ’ð]éķ˜ÃÓ[ã0Z †oŦԆïpŲh °ß(oį ßUá'ĸ™e–Y…đ"Ųęf7ʰš ęrĐöā{ïū‡‡;ëį™Ë-.P0/rÔgŸSA@%˜ŋŋĸ7S§ÔŠU{ėgãŽ>Üėýũ{õé7bؐkWãĶĸ4SÕl=úöí7ó§ņwâÃjÖd֐ŠY™YĨß(;w~ 2Œ'NŸõûœ}ûũėŨgįģpá"ŸôøĪhŅĒŸ<Ąfa<ČĖĘ*4`ā§Û}xíúÕŲóžûÞ{ŨŪ^íÕŦOũ=:wˆ,[š,ĩMšū[ĩjÕ%‹ŋhÞōûfÜŋ/==ƒƍģnÍ*bĩ°•ˆ…ņ’ķišxĨûrŊĖîŸéÞ}%Ņ@9ËFχ,Y DpĐi2m5Þ"=,ĪYs6Ā(iRĪK+›vĻdb/wžlÛkAž=&{Yf™åÕšĩãG.˜ë}ųō%q‘ėĖ,:Ŧ›†ęÚčnJ/[no”äĨ‰ÛŠ„ Ō“•L $uhĄ―ŨL3whÖĖ-Ϝe03sČXm`‘#đė7jĀåKHËHW [f™eų°áJX! 88Ö SīąóŌSÓoÝūÕļiÓæ-š9üâ^E‹g_úĮĻHvú’Ē2tP‡ÉĖHwøøÞ[·lFXŦoĸĄ5Âxī§“#†ļ{˜)k%zéÂųfÍ>6|D\Üēþņ‡Ó)äŠ[Ŋ^Ûķ‘ô[âÎq⧃ëÕo°gũ.Di;tčˆh! -YēdïÝĶyÂ2JoQ%wWMYI˜Åī-õΟ9ŲGýŽBHHƒ†ēŲŋo‘\ðLÍ0›]„:Ô°QãŸ>þũßÁ!Á/\DÚfņŌåÛ·n!d‘į2jđðP€čãPœXhŅ2Ēn―ĪĪDžĘMã]TÁþu€Ą*K<°ĸŌÅ <‰§gĪ:øđyëæč‘#ˆ° †d8Į—/^H~þœKš~ý*xæ }Ų’E(„ųø·ïØąlŲrIIČáMb™ÄDZōŊrđY•Œ10žsãĸũĪqQĨóã+Ð,^ŽiÉÎ0ÝËcNa힛k$Æ g›þFŸÁČApWšŊö_„ÖđYf™eųÍŦe?G €,ĪŸýĸ")Ād?z$öôĐĀ ’$$üûðūŊßĒ?cĖ›ëïïįðö!˜pëÖÍaC•*U·ôþ―xOáïŲØ3 xúôIFzģA=†uïÖĨté2Ϟ=õõũÛšiÓþ―{ƒ‚2ŌŌ` =ðŪq ““ŸŽ1ÐĮáƒX—Níņ6ýýþ}ø°s‡öėZąĘ҃ÎÅþSĪh1bÁéĐiėŅ=Û)ēŸŊe^Ϙ>mŅÂ>Čč2;Fķ!ųÁ„J‰ P Ą•ÂrÐ“õ[ÝõÛÚ Iķ5•Įr\ŠČVk`WEnŠ‚ÄlšŨõZ=eNōAü5…ÎJm’m3ŽX­e–åӂÔûS zÔL‚Į~þþ óŅÇøCōf}}UAüĩl yKÉlRâ#^úø8pã qÛėŽ,CaÛöFv‹F .-É%ˊxïn<䂕ŽI TŲ„ƒ)ŒÓЁt>D0Á.mÃ―ÔßtđASO[FzfzzŧUôäyý:.RøĩÐÐnZÛ28%ųų˔äÐáœÕé)áfšŸÛ0‚Ëہ)=x•ƒģ*õZ(aĪ{MÝšŅˆ†j›23 úßĒ&n4"4D`ŠwđŊ\uŠ―=―a:Æß0Æ(s5ÝÖ2Ë,ˇĻĩËŊˈ˜ôR8ðÂīĮ4Á•ó„CBpɓ1í}ˏMhC#DŊōT•_úïĒlöĀĄƒĮŽĮ,ZÚųĢqkUėāŽ1 ýYņUFZ‚;ģr4` zcô˜1íÚĩÃyâH'āe”đå…ØÉļˆĖô ÖũōðVý-ŧ‡ufeeUpNžFn„ëeƒ fx‚ dŒqÞļØÚĮ}SdėR oWcũ_ŧĶũ⁊d—ĘėB{rŅ “ÖģgoIžÍĮö•Šā€mÜÜ ŪÞē$ÞjŠTÅŨUtå ĩ/šįQŧΉIV­ę&@xūQĨą[Ë,Ë—Ļ•Ú|Ø 6`„f@6ĢĀP```đōåĐæ:uō$IZģgÏE­ölĕ/ĶāĘđP6Ųƒ˜›6n5lpŸž=Ļþ8h°ŋŊ‘ÂEįĮ,\ūrÕĒĨËæĸđ°x‰âå*ÏüeÖwÓĶŊ^·aŅ’ĨóbbŠ—(aždŪÝÞđëG[wėŽYīdįŪ―Ļ|eefFE?wÞ%ŅiųbüÄ?.2ē„7О=öó[ķÅ,\žyëv„5Xķ|ehXØģ”õę-[ūŠš‰ B6mÛū`áÂUk7LŸ1“b‰°ððųÄ 1rß_‡FŒ―āυQßM (PČÏĮïĮ™?OžúíÏģ~‹šö}Ņ"Å:víķdųŠ/&Lä/G§Î]—._Qū\.•Þy1 ųāŽ=û>hŲ*3-―N―úæJ6lÞúńŊ‰cÔ ŊõÓĖ_ƌ·iëöņã'ģæ^ÖnØÄ˜åŦŨU­Š‹—ïŌÜBĘâČãžSyüšxIŽ%Ēh™eųĩ<ðš‡tITēK~IģŌū6^…)/’Ŋ_ŧzúôijž}={ũéÔđóÏ3é™û2‰ãūœ@L XņâÍ[ķÜđsGlė™6ķíÛoõ&:QĩJhôīïq-ĐhĀG5z 5c=RĨJ•·*U*X°`Ówß―yãúËīTÝĢē5û Åø‰_íÜūí‹qc ,õýôK—.’ĻÛēU›Ā ĀÆMš"pņÂųņŋŪ^̆tÛ]ūīoŋþķ‹äŊĩgĢĮ~Æl{vï&—Ēĩrå˕ Ū]ûsągŪ]ŧZ-4Ų„ˆˆÂ3Ņū‚›7opŋÕŠ…ŌwrËÆMoūUĐsį.xÐßFE—-_~ėčQöï9jtŨšãۆ„„ôîÝïŅŋđß## ēbų2ŪäÍ7ßúzōTjØpæĨÛĢl)ĘŊ;ßmŪķģY"Š–Y–_·Å>‚W„ ˜d/iëŧ‘þ’j(/“*P°@tđܰQÃæ4Į#.Sķ,ËLtņâ…ĻoĶÄü1äĄQëĖɖĐrrxĘ.TĪȎíÛĢĢǧ|5ßđAĢÆ[6mbéÃķíšuïjâĘ+Rž?øøŨĖÏB•ŦT&xJ].óßļvíÜŲģč4V iÕšõÉ''&†ŨŠõ"##ßy§ũ@Đ.‘YQWØķ­kĮö+—/]ēxÁ:uëõėՇhėšuk6Ž[Įø^ĩZUDÅļņÆïūW―zč‰ãĮ‰ĨōĻĸėŲģГ&.˜;įáÃļŦÄO0ūƒv‘‘•+WâKxĢdaiŽ;ÚŪmkViØļ ‹ÖŽYģmÛķ\mHÅŧ‡hJ€Yälؚ#C΅UŅg ī–Y–/ sЄ,•ėã‹$ķū+LáĐÄm *Z―:þ؅•œ‹DlWŊ\‰ŧGYŠvŊ‘ÖŠ(9im&ģĐl€ĐvEĩKŌû}ÞGŊķNDݖ­Z=~üøÂųsn@ -ÕTjåИÖ_ûũ­_ŧą…5ŦW–-[ķC§.ĨJ―ąbŲbŠráÏU>}úlņÂE4-ũÐVOŸ=e*ĸŋ“ĮĸĶ8ļUŦÖMš6Ĩ\-ánhéééÔ)V ÝįOŸķjÓ&0°$šĐĐ/đi؍— đkn_”ė&—˃Đ.\x9ÃÁ­=þ”Mą‚ “ĄųdŊ‰ŋlÝē…šd‰―äå9_9íÖžÉf“-ģĖē|V˜K^­6—uÓ ­@AÚkkó.Ā'IZĩÞŪÅ^{·{„††.^īðþý„GÁÜŅÇ /ĻŲZŌąB… ĩÂkãE2Ō…JQláA›yjÔŦÖĢg/NÞūyóŅŋĸnŲļqÖoģųÔî]ŧÜŧ§ŨÃL6‚ ŒŋrųŌę•+ øûģŨ†ËI !99đOß~HÜļq“hÃĢÄG%Kn\ŋ6áÞ― RAI‰+T(/w$=Í<)š`Ý];w …ļŨŽZõ8ņ_nöÔéSïūŨŒÏnÛžđš8‘íSSSãïÜÆĢŨj4“ü ÜœĮÉvx-ëõũî–zĢôÓ'O(/6šŽ’–öŌH8^‹CX’dŽpáB―û ī―{Į6MrãĀ–ė°k,ģ,ī[Ē”HsîŨÝ\Īî+—#˜… 8ˆM°°°š11 ū™2‰Gėóį܍Ÿ9kÖæ;6oÛŅŠÕ‡ÎŽ,ü8RMŨoÝĘ~­ÖŽZĐuš6Ī Ž:ņi=cũÚļví‰ãĮR’SĻYX―|؁―{ðjÁąÃÛ1xPSRĖŧÝ:ĩ')Íŋ@ĀŠĨËbĸų‡<1nœhFÂÝxčÜīQ}ðíįĀ~WbâĢ>=?nаqpHČ­›7NŸ<‘ükæOlŦĄģ $ĩķÐŲ%t…?ĀZ3 \t‰‰þæ›ݹΟóŧSËąīFVüA|FƒQ°†MȘ-―=6āí,čzVAbB›§ˆ’Z{ŊŨ6ÜӊXŊŨxNßJõ8"„TæmRþäįŌp{$õÜTD„bæ[Đ Î9ãŲ{ƒčûļBÏî2Ą €$>|øíødÏäJRW]2ģÎ[ÔÞŦĶĮAęœKŋÚ―Cx­]U$ýóýs< qLŌž§7YFH6ŧŦŧŊŨúŨŒpû™uŠØĮ)1nÅŦžŠf†ĄĖ<įH‚™Ũõķąũ‹R]ŨŠļ^câ_ójÏiEFÄsaF\ïk<ĪökW7EšĪŠ+Bŋ†{íꊊŒPDW“&@ā}7žŊĩmũԓŪļf`<NŲ&ô ũǘúĻöÃ‡ßąŦĩW.·qPšnR{í•§šƒ’ØÝ0RŧĶöZŊ; xâÝøN ė1О>į·ŋfl+bfŠˆĩ_§:3~Ū’yębPŒX9nQbΌۑ‰1 ‰ýĮ܈xŋŋGÍŌžÞWHŠļÕД2Ōd‰ đ›`DÚ%iŊeûđ/ÛϰGBc\Ũ%ÅÞŧg DÄZŧš-æZ”š'âŪ@Ô9Ž\Câĩwû'k%‰'GØ3=ƒ>ü†o Ø3SÂKĄ™›ðR^gz&ĻĖ|Z?0@v7!=Ūsī"ŨŪ:Žĩ(tMŨäfž>Sé™îŽŒëé:wfĪšdft$Öyƒ4ŌĐãqfãô‰ĖT€Ļ:™WU$C?å+ie@qÎ嚟ÁŪ}Þ'WFÆtwOF„ĒŠ@Dˆ˜+DéŅbäĸýō AݍðýCŦŠE@ŠsBĄŒ']!ŠS‡͈žŪËãĩIßËÄoĮ‡ŸÉ^ÆØ M·Į_Õ Ī―WOyúþšĀԄr<㉕Ą°Ýc… tßJ Ļū<öĐÎ―råUė Iqî(v­5Ó >a茁§ũ ũīQ]=øémaĖĖíؐ~VæOāےĄX{čjQĄĀĖ“–DÄ}đ1Ķ DPŸ8"ŧšĪŒ8U )čöŽ= Dļ'$3ýÄĮ* Ïũ H™Iāš.Ÿŋ%>sŦkå"86Œ˜îšŽíŨÚÕ]§Cï*ŌZyęęžG…Ý]uW û&2(Ų0˜đzÜ3ŌđŒ\‹āđ.+7Iß|Į@fŽÜĀ80WvõŒónMŸī{o‰6ĪÕÝĒ~Â[—d(`w·á―Ömg$pÎe{­―ũkfšJ’Į§ŽHIwZ=€3ĢšĘļ[lû^D Þ뚞9ŋ3ĀZŧgŠz­ãTŽL۟Ūö·äÃ'ŦUÝ5Ĩ`fvũm·%étMÍÞ;"ŠŽg2b~å›0ŠŒŠ0$ÍĖ·zšŠŽD} I!uĄœîũŨ{đēŧĢĨPž:Uóy+ DŪ$q]o˜Ŋý:ŨmÃĐ UÕĢԐ zLĢŦNÕũƒūi…ü$Sí1<žSŨĖÓĀÕE22A(DPŠŪ6 …‚Ũ)ŊXÆ>U"‡3Ā^Ŋi E(ø~QzäÛ.ŊýĢNyüvĩ>|ēZ61Ūsa:VÐėžHøzŋEp§íņ„˜+ŊũQbÄ:ŨÛĶĪXŅÓ (šM2#$;ŸM…ĪũulÅZ *3ĶÆcIÉė* v.@į\ąVzœËāécíT\w°ŧsî<‰€XŨÂîXąÞį #‚"ŠJRF(㜋|ōCŠÄÞë\‡ž‹‰Óc;3Ë5c†V|ÏBĢ-eÕéš·! ö9 VdO1@âũãǏj‰é‘üĘŨßŨ‰\ayj>íę3ãJ+ęT#‚ŨŨeúNpÎĐÓßO~‚!xÜĘx_Ũí܌Ės‰õÚÓUc‚đrš{ reötۙšq՗팘†}lîŨkzĶFĐĨ<}ÆX+gƃr%CĄŪ͘™§ ïÔâ~cÁ™›„Ýk%ĻŠ3u|Ëš§§œ €ÞŨ™éüØÕļŸķÝ9Æ €SQu…’ĄëýÎ'VU-e8įĩ2ßwZB("ņ;ōáÃg· ­ũđ„B Ã)\įPZkŧėééïtŠ ÜąfŨÞŊļcÍ[ÜęÐCŲ]ķaUuQžOhƒĒ2ģ§Š[ÄÎuęT·D’ƒ!ĩü0Ð]GDĮĘŠëŨšŨđ(ĶRāĐóė,˜ ä^3 ’!ïšxōY ?đ­m’”`ÃΕÆĢÎ;{­é: ė―Ï9=–ĪŒūmیˆļÎĐnÓ3mB nÛw&ƒÁïȇŸŽv-?fŲ+Ýãa(@·-*r°đâԙņ^KĐÓĐĩ·‚į}ŒŒš‚‰AdÖ9Þ.Sc`ˆŠŒigdóüȕïëkÚAQžęˆ uŠdū–i‰ÆėÜ]5c˜Rī[ŽŸHį6qfÞŌ›æÎđO͈ÛėÔԚqO7āũVīé•ŲÕ5•Đ8Ũá―;™āĖĩîĸÐ"a_Ũ{ĶũJ’5ũ6åõC`ŨÜįĮTΕzŠ&>|øðvĩíŪ.(Ķ}Ķl‹ęŪz_°CqūūšfDØîĐiũLŸ†™qÛðŽBšjô€†xęęïąŦŠOˆ˜îë:=ĩ2ĘeXĄˆ8Õķ)čØ2aW]‘Ė\Óãn€ŦgÚk…ĒęôĐ―WŨđšÜkÕ@ ŠpŲ0náwȈļú\ŨbᯠāĩwDž*{h‹O§ŒûŠ~ŸC)"žw3"bįŪsŠ!Fũt_ÓSĐč)“ũ9ôtw@ün|øðyŲkzކ=Ķ8Ï0"ĩ<ŽŒĖČUuŪģr­LÚ *4öé)2M›dMMwffDũ(™™„(ØČXĶÝCEFvŨĖ„23Îuútæjø9ągšŦ ÓÝ&š‡Ä iÞÕ`(?ÖîéŠötÆ:3c„’`UæO „(cŒy:âŠ@j­}NŲEĒnÏNOÏā FĶ`<7x]ïņd,Øî!C!{úīPxÐĮRf„a|øðáw B‘đÎ9$Ű]]ĘXđĮ]}@€|_ė'EmÏRĀči"ĒšŠ.Û{í™ņi1šŠ*"Cņ>ï:™ä·o2tÎUÕ{å~mÛ 3eã?Ķ‚Šž2,iĶđrU@Ĩ \ïũôÕ}ŸąöŪ)īį.ž$ÏU.ŊĖq{ÆF0NÕ#Á•yGÆ‘Óm?u SXkĐĩ’Æé2MSėîɔ$ÏHJéœë}ND>3ņáÇßnÜL=hsáŠÆ8VTؒS3‚ а§]R(†ĀĩöU—ĮZ+ĨŠ‚ Ũ ā™ŨÞŸî”ö~ÝÓDƒã ĀÞËvũ‰X"Ï9‰X;Ŋũ1ķßįšŦý0ÆöZOÐ܇RwÚ ŪŊ7Œ• ÄĐóžņzŪwaú9ē F„QuÞCČpŪwHŠ=3į\#Öõ~ ˜Ęę3cI }―ß+3sŽsfüãõšNAÜąIšGøðáÃoŲÕRš™Čœîsûĩŧ‡DdŪĩŠŦĶ"#‚c°{ éöô-™]] %ļ~―R ǧŦjï=öô„HЧyĸPFûi%izðŦS+ĢŠŊĩó9>ģũšïéAčÔéEļÎ%q<ĒÜí™\i܂ödĪ=vŪĩũ“Ï*ąŪŠŪQ&fz*9݆ieŪï-‘ĐĻŪŊ\AȕJI8Ũexŋ^=ЌÐÓ ŲÆ‡~Įyĩ˜ĮJcdäŽÝ=þ lŸsÆNũuMw„æ€$ŧ[ŌĐC ÏŪ3‘ Š@ūþņÃö܅Eî:O?Ø]ķĨ°ņ<­Ę―Њ$Ĉčî™ÉŨō­ėĖĩ_/€=ã™ĩÖt0Ęó|ÝSgfí š`ßóššÎ!ʙįVTOIĖ―lôƒĖ4vŽ…T’œļý―zŨÜŦ`įŪ: DbĶïĨ)č3áÇßsēWP“|Ü"ëԓ{*âŲæŸSU3öĘÓc’ŊýŠn6‚<įÂ8ũŪŠĶEåĘĮ†ļõtŸL2žĶ”A’ïũ;2BšÛĄžÏs]=-†&ī}ŪKŠ\ëq7„ˆčųûōļ•RdŒÝU’^kw7ÅČMãîâ+ų„$df’<}cÆ š;3§ËFĪ`V]ĘHČķgH‰čj‚Ũ+{ m5Ģ4gĸüþkðáÃÞØþÏQ-ÁëšþũwũZųwl)Ķģ§šû6UtwWSRčŨ~ÖėŠS†þþúÎß3Yzú}§ŧ|ø‘ĸōɏ?þsT{ŠþûĸøŸį\ŊŨŊŠĪĻs.’BQS0ĢęDAĀã{åęi""ÖÜ}zÞ&HbÆßyBfũķkwþDŧŽ"(ø>YņL$€@pš)yühTĒ ÛøžŊ3DĻŠũ~UOn;öLƒ’Ôį("ĪąĮ3=k­ŪúūqÅLČHÃU5ž• ŋu§Āz‚‘Ó”Ÿč ŋ/Ž)ŒA(bzHweϟĸþïĸöoĸíĸīwïOM]kĀŨZ;Wr1!!@„*ũ›—ZŠP[{=Îœ™ũÏčĸŌ?ĒgΧūķöĩÔ īUiQĀ Ę-{đėĩÖsö$Į3H„ņ0o:<Į=Ę<ŲŪgĸðuąöÞ,Uå;sÖétĖÎ> C!TL„ÍÍ ØŌP(Dvģ؜ŧi4ĸ•|õŸ„žōëdgÁž. ü+8s‚B―Ѓ|čĖšĨTÍŠétjįÄVËzN E!D)á\ĻŠzðĻ->Š8eBū‰B!tČH!„F-BaÔ"„ÂĻE!ŒZ„ÂĻE!TôQ‹BHGBeBˆX,–JĨ„Īč kNWŌét<Ïd2RJR|t:ÅbąŲlŒą|Š „þē ‰0Æü~ŋÉd"ÅMJ™H$‚Á cLK+ ŧĻŠ‡ĩ,.//ŨëõExÁSĐT ‡ÃAö„Ð_–”2N{―ޒ’R݉žÆîĘÂöYĘs:ĨĨĨÚ4œRúĘ-Čī„õx<s–jôŽėëļïN­VŦvÁ‰Ņ…ĻEÉĢŅXð{mĘã™íh$މœYӄdÓé­d2đĩ­JÂ}]úĀęģ…›ÛŒŌý ļĪĪ$“ɐ=pÎĩvE!{ĄDŠl*•ü_€§ķ3ēðeęųųÍmđ{€Œ)†] Î9Ņ„äˆÎdpqøÆÍĨļīY$œjĸ‹w›*ä+cYņ‡ŋyÕ)ÂUÕÜÛóv™Å°ũų)ebfėNīþrŨ"8ėgīŊ-(БĒÐՅĐá™ėĮô:Ė:5ōÛõïŸö|rÍk °ŨDT„oßoļâóZĖ`į•ŲŠ…ļŪÄjŌ“ƒÃĻ=ZæŽČŲsɍŒüãëkýĩÏ{ËĖ<°ē3œūÜļ$Ôäę UÓÁHĀVũ΅ãęČð­{e•ïwTä?õ`įfˆP DOAH š\ARĘ7éˆR’N'ŨcRåBHÆģ™ÄV0Ëč(Púį&)RF)A˜ÁČ%Z―^6BôzõÞÝaQÕ=Ðā•lũÕØƒVƒQ{!LÛ―ō‹R–X{üxË6ô^o­Į–åōäi'!bëóããSąlÆčŽęîéŪ°ō!*szŽėø[uÆ?O3…ĨÂŦ##ĢĄmŪ7đ.]yŨï2ÅŨŸýßčD4­ZÝūÁ~–[ĪāÉĩ›7ƍĩúÚ*…Øk9ęŽðŧVƧž%{Ŋ )G›Q ˆņԙūrŧøî›=\lî=qÜã)͖·žïĻYúidb.yõãĄŲá#wÝjĒ…‡‡Q‹ÐŅÂ9ßûÛmˆ”"WÂhvúÞ=ÕÝxÚ†ýC§].gûĐÏï,R*MDR RHĢÕéũ–&–<^ŋXk ,Í>úek3ĨZx*OÍG-—:Š<Ö,—”pÆÔgũūcöÚÏ>ëą3ÉžáBÁŽˆ”’2ЎBH­$hGÎÁyĒóRg=ÎЙåĀúúöZDÚęĘ˔ō–ŸgW$W™IZx4ŸÚŽĨ‰=Í%‹Õhriykøu=’LĶ&oߎ„<ŧ‘õú‚Q‹ģZ„ðķØN +ąĨ"ģK‹kĮ›ýF…EË VŲtÂĻc™ÄVˆÜN§ēD§Ģ€<—ŋæ\gYčÅÂï3O\ąāÓDYïđ†Ųä*ĄTQô&’Ž',‚Ŧ„IŠÛ_KÂK??šë?sJO$Úm1 ŠÉhc[ÏSY^f7jÖbŅA˜žáR—I ĄõL€Ī$“BÄ#.ī~'ļó0éųÛ@ÃÄp€œKāÓ+ÔtĖwîíNģA!TŊȃī€{!„)lõ4ūÛæžž}óŧą§§~]ވ*Œ•8='kmũ'îþ41>úpÕqŠÓc`’ä|ifōæĸÞxØ:QSŪ#$ÛXüãņj„Ŋ?[ÚĶî ­ŽéGGïü0~ũ§õhVĻžÄŨ1ÐÕ˜š1:5ŦFÉa‘’sWø /nßþeúÞØôēĩēÎÆĻĒÐøÜýņ‰û·ū–Ō†ZMĨ.O}ûí‘- LÏ(O†įfį#‘čæ íņ/b7éŸ=œ|8Ļoi,•‹ũÍ->}ōۓeIĢ!Üį @Ņ5t]ûtðm—A$RĒõ|ßŲZ·dæóÝC=-ÕRę:. \ëz‹ȗ f9Ó5ØÕ\ã:~âŌ⇗Ï֝lë§ĐÖvŽžĸ“šŠĘ(ĄÚ ‡šš™ .ođÍllļÐÓQe-;}öƒ?Šu…,4T͛tRėÞūŦŸ·TXÉLÝŲ‹C―õŒ€`ōŨyl:{uû•Á+nŽh>?xuĀmĩ6wõ}ôÁÏfn:ŨÓßQiē—ũ^kŊrĪđŪí|oOK•äY[uëÐ{WkÜ&PLî2=øðp!ÜTįŊmô”ÛœR0“íTc+aļ/ŦĐÁWýVU>zsËĶÄâljsB @u%­˜°ÚúÖüģU\p{u ‘‚ŦŌ]Uį!Ā9rˆĪ›ÛŨæŪŒQ)$Bg°—·uī€$ đ@ˆÁWÛā;Ihn…W&Oc›—ä‘’ƒŲÛ\z@ ÎmžŠvoUŪVŠ\Ũ9*;æ"„8į+++•••Œ1Č)ōŲ7c,‘HÄb1mĖRĘÝQEĢŅüŦÆžũڍ1ß\‹CI…ÛNv ™Lfmm­ššgĩGBŠĒ˜L&mÂäp8 CņOŊ·ķķī0ĩÛíðĘģŲŽq$ŅjīîČ>qxýN€2-g777- . !WiNg8^^^–Rū2žŠjųXŊŨ[söŠZ­@ëHK[­Đ―jþ[PEËY‡Ã G BRĘüSĻÅĩ”R-Šī#)H‘ĸŸĢĢ6Ÿķ~a,„B7žA!ŒZ„Bĩ!„Q‹Bĩ!„0jBĢ!„[YYaŒ‘C€Bˆą\ĖNLLÄãqLÛÀœÕV‹Yōå—_~õÕW<ƒĄĸ„BZĻjŅŠŽģĸ;9HTgmIENDŪB`‚doc/images/ifw-ready-for-installation.png000066400000000000000000000536041325366651500207740ustar00rootroot00000000000000‰PNG  IHDRÎGæąmWKIDATxė’ÍŪÚ@ …QĨ,‘xxĸ-â"]ĩĒ *`Hf2“Û-EÖ5qïQ°U‹ ĨRÏšĸäƒÁl6Ëó<ÆøýRŧÝNū˜Ō…ú §Ö~ŋŨ)[äGžÞiŽjwøŦ|Īó?Ÿ~ԃðb“7Ēþ'ßČGģúÝPéŋ'[įƒó“ó‰1æy>N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/fŦ>6ŽĢŠÏĖîí}Úw>;vė‹]'ņWįĢNQ Ĩ* īI!*ĄŌ’‚*·ąĒ(óA\*"þ*BŠ„Zņ! ĪP"’šŠR+Ō€‚Bl“ΉėæÜģßųūvgfw–9Ŋō4ÝX%*ņģ4šũæÍ›ßüÞóĐúņVMúU­T DļÕLĸķū|#l„ú‚˜ĸ'üHÜ>?ęįÜņ DĻTž# sf˜Ĩ€Y6Žr˜ÓÆH0Î-NĐôBĀ„P<Ũu]ŧ 9ÅbrīmûfČ įXg Mĩzk›hŅ-„jęâF1NĨƒÜÂį 'jVŪëĘŅģʑ1æ…õ’‘ð{Į›ĮĪËĒ+ݟ˜[iyóø+éĒ‹0!ČļC4~éí“Ŋþ•õ ėÛ·ĸĀúU-z8đņņ­ûķ­IūwîėŪ_ýāęEí=îčÝÆT˜EaÚĸrüä;ÖSŋĩũévîčŸGļãޘĖĨÖlÜÝύŒ\›`ÜãT’čb‚„#ĸÄDz6õ‘MÏîíŊʎ\›˜žļza*īþËÛwôn‰ãĀݟŲÐÖŌÖŧëĐŧZ“mwú™Ý{úŸØ4sõo“.™*‘t—)ÐŲ+ŋúí‰ú{·ėęĸbîâ[gĮg áåĖdûš-Ï>ņĐÚ ÃäPsņýoþxėg?øöó/üüJVä.ŋqôĩᇟÜūõ‘îó§~ŸÖęÖķ·uÞóp_ßWššU2%!6·^pŠ~ĘÅēUÝņôÎÁ};zýØðDYÃ.HWՏ7ÓžúQ…Ą*d㍊›ŠX”•§pˆsģzŠš4 0AÍJHłdĒVČ&rýoĶÛįGšÝ>?„R û―åP*ÅŠâZcn]Š;ŧðŌÜÔlŨÔR‹Īå™jŌápXŠ2‰ČöZ]]]UUF€\FØ„ëÖÄęv &RÝĐOÅR Ģ ÉHu5Ṋēũe)“s:ŊËĻBQĀËGšÁCJĀš_·š78Č8°ÝĮoŪr y!2|zÁ}z~ní˜āOþüĻ)qÛąŠųhÛGïYÞđėūûãĒd™åsïėiuĖž­ŊYĻåË&nP#2Ē:ŅtÆ(ģx]gÏēa&gķÃįēĘ\;ĸōũŋũÂK‡gLĪcGžĀtcIGŧÎhSgŧ,MÎRŪÛš: ›5vīĢ89k!L\Qq–ō@›É•E+ęÃļX,Ę Kđą?yųŧÏĸdhZ3*šĄĻA„°L“Ų&ržIëÚZŦƒÔDŅ‹ëËīh[<ī°m͊%ØqŒH%—›ũå•V‹°—1il銞;[ãÁĻn›Vý’5›zĒGüC/ū:U°„ƒt fQZ9ýÚúé‰3H Øs\™5ķįĒ!$hĐ`ðÕÓŊüčĨã$ڐ0c…PÊĩËȎYĄÜ{PþäæūŧŸ4&ÎþæŨĮŪū›fþOŋxņ—§FŦ“‹0c’5Í@Ôīå“F•øØĀ’𛄗,_'QĒôŠ\1ššÄq1Æn…WLlAâņ™ū˜Î2VÎÎäFÎŧPŠ~mŨķĩĐ$"•I4ÄÄ,•É{}Αp$;6öîtĐ<û^úZ&Č`ŧēYSÎ’é Üær‘ķʁHĮvĐTī8GĻŌē Ô^ũØÖoėz4?|æėč d;w(ãfvôð‡>öøöÞÍD‰†]ls!“–ÔÉĪå·kÄŠ‘Þ}ßöxnð™eu†-ķqĐP–üĀkĖL\ÏĖÄR+š·ÅĖÎ4ĩķ„Đõ_ęÝ?øõÁ=.0\.ßb*[b™ØL0Îō™Ëį.ŒĢŠT …ôXÖĪÓãĸĆVÕ·…ÐŒ †Ðøû$įӏ„ŠūS ūTĻu„‚ÏßW)jbÐFĀyޟĒūŦĐjø”?/?^æóōãY?<~ 7ôÆŋHģšžĶĄ0üëôE>ŒŊ~mmŨĩl™‚ œY$B‚X  : Pl8YeĨ[ŋX1þâ%ņGxĪæhÎ.ļð―8yrúžÏ9}ōžWĮ0LÓlÜUģŲŦešÕĨEmzōú㇟ĩęyŨÎfūdfŠŌ°,Ā4ØbYVĨRQUž‰ÝÞÞÞÜ܀ŊårŌ ÝÐōōâÛbēڒžëå{.w<ņ͐Ūk ÐųÃŅu°‚vØE öƒS þŠïŦāˆ@tÁe ĸÞđp°‰ļė€ģÓˆó.Î^0xæÓ™k5LËnÔŦ)§ÆŌó·åkĨÂč`x˜bY‘ÏÏLũøý|dęĩĒ[PÄvė͗OŧÃXl,#BdbŠXW3 öðq?ĮĮú{ĢkZËU§Đ‘úy!>Šæ‹šï;đT$)ø—šHËû§ūgŽdcŋu2ĩ°yļ-ĄNšð4ÕӉmŨNö–1ðŽ?.Õkōlbl}įüŌ1ĪgĢ8Eâ-Ė]4=eo=Ép;5ÛUø‘áĨ­’åØA Á'f,―wîĀ&fvOmïŽģž]ŠČyf”ˆ AM–UĮ6ÎgđÁ‚ßÚ/fäå“ãÔ`§ļīqR=ā(šlķJïĈ°ę~ŧRåMķŊ+Âr,ËlTLM~…‘Ųšú7ĀŨÕY§hgÂ―øJáäęĘýž6ŨŨ3ÄÅ9šTK/冚ŧY1óUwĨô“ÐÎ ąh8"æ=ßYMttôÅã<îZؐ=ÏnOf{~óÃęócšƒĖ <Ļöt^Ðų‚"č°ĸbÖz^Ģh‚hþÓ\ūÓwĘí‹QņŠˆđ|ˆHTPPbŒŲdĢÓŋ§{š{zOŠŽŊ§ÖbÝYcžYČP[õúÕë·ĩäā ČÏÍŋŊЍ?xnô‡Á[f ĨÐHĪ”P”B°oįžÞšyöāū~õ2ƒÓ€i­ ÉyJ įóųl6;??*`ŨcŒ“˜Â‡‹ŨïîýĸfũéŅíãŲ Œo=ÉXErNŅåž$€Qda”Ģļ†'Ŋ\YÅģŠóÄĐ?kzŒY•ąv ú Wo7õä8Ëü|/äSðü?Ōäœ|hGӆļ(I Ą­ėĻtÁ ­sJÉÎXšū$šAŅÆæ-ĐŽG`b;ĸlïž:-]—‡’BûĐVŽÎZ-O%­LŠá…ēąKđsķ ⭂Ð.ĄVb°ŌļZ4ōBęspRĀ}KiąŧmX@§ 釅wŠĘFQ6•ĄģüåÁ[_ ZõXßG,Åôī|5SÅūïŠWrÄŅA"áCÄĨĄB9KŠäcîšN+réSMSÕīÖĮ˜úޒ=ä6ąóZ6óųGWŅ.â% jŅķÁíS#Ô+āCέUÆw‹ÏCŒQđĸïöÎã“ÎÕvCųæýáÕÝļ?„Į]\ē?ž$ †w› |œņžØ”óïkm/0ãY*“\ōûBp‘åM_x^ŅÍTSäá؁+úģ…ŧ5X Š1GŨ—‚wðÚžÃbސĩ‹—u€ČA—sÆĸēáõą/ω-$| /ũ UĀ ð7€ŋ"'ĐKlLKƒ(óDâgŲĢ<ŽšŽ“9Y0ÄĖ`~r0ZEōqŪlôâ”„eð)–ĜԝúÃ#6úÃķc‚  rĨc—h%­Œ5ĸ؟FŨBŽ+%/îėíόąāĻʋŧŨũž―?ģÆ*ĩT}Ču *dŧ6Ļá_=…a@€Ó>ŠY äĮēĒŽI0)!Ixēoˆf9‚Ū@ JR,ĸŽ’–†r]ac 3Š0šļb#Óh(€RtØúŽ,čģzÄ ŽĘ(§–:GÖT€N jīāÉĩĸnV>Kē9þ#|2a•Ï?āyžÉá§iÂÄãņhƒ1)žqЧ˜@EoJŅ]ëŲåWSf#zĶaiũčœ2ÐR}YVWfcŪ->x,Åiņ‰ēĖĮščŽmņ1ĻųžJŽÅĮĮÉUەl<žæ#ĸ'Þĸø6=s8Ė·ŋøõvŋŸrU>!žĘ‡ âƒzDKødūÅáSŊðĐUōi›•e͋QÉïMÂĄü*M•ï™ŧ‡ŧûŸßMÆGESĐųėũ{+m\åƒo―?%„wņņØâ3aō™W~_ˆ->Ø2§eČßóáoäs1Žã0 ›aã‡#ūïú͓mų}ŨuC?l·ÛqûX·e7îHĄPF`Kē(Ž"PT!pÓL1[ļFßũÄņTڒɈS@##ž3nå.ï ^īĖ―zVāiôz>ÖâCV“-%ųÐH™7YáSÜ­ï >!>X›ïĻðņHĖ­8ŪōÉ(ųėÆ‘Æ››ŊãĶāÓoPVųØ>ÔûĨ3ö>Ý+|äcąĒ|°rrųþĪ“O č-ŋc1Z_ðŲÕïO–Ö|pĀžĮîKg‹ï›LZ|2°$F Ēô7ō!sø`Øi|ôöū…b.ãĮ‚ž?Ą]ōA‘Ę2ãšĮÓø †“1ü >ø‹ÏÏvõlŨŨŨø?: 3KŲ§'ËâēĖĢū>ÖõĨˆg~]ÉU—o•i]^^ķ––Á:ŽÍ§ĪWōQü>ÏÍw Ÿz”ŲšĘų`/ø`Æïâc>ˆËKĢīø\5ĸãÕĨ!ģōþ8ü||īäó›―ŧâZø~ÎČJ\ ! HHā  ļCq/NŅāšāâ.!ļqŨM6Ŧ#į|ģŧHî}Ûûķo/ýč―įũÐtäØĖîógØÍœ‘Í+ӄ’9?äü ĸ•A$j ‚ HÔAĻ%‚ þ|ÔA$j ‚ HÔAøŊAP4ŦT*d,þnh†U(•, 1€Ž\!Ą ˆŋ ß0HŅė'æG]PðO4•ĸþîĶ-Qgo―D O“ßÝ>ĪiþļŲdœüōVÔú Ņï åīņÎÉ};wLU AĻ%þ$âk;ĶÔĻäïįëëW%ĪE§=žs˜ĸ'fóß^˜>mÚūπLÁĸcžãŒžøŧ†ĘŌ™ÏÎvŦQcāš“þcˆM9ßĀĘá‡Û§ÏœqäY–‚å/Ž[5%båŦ\̌ĶŸG$j M^j|B–CđJ>Ni/Ūĸ~Ѝũ*ËĨ-ÍĘ­mí$J9  Ī…ŌĘžÍÖēŅ‚‘)ėL"ē’ąržãE™>I°ēRČeŸ*Ō ĨRÆPāŠ‘Ŧã.ÍŸžãhīÂÞÖžēR;&ķVJAI^§þŸšĨÆZ”Ëš•ŲJl”4üÜ2kmkkjÅڊĄ #Wj’Ÿ,ž9ųðã\[+9„@Šbemyx’ĩԀ)‹!`( 1 #"Ï3€ HÔĸ&‹ĀÞwÎæÃ7/ÛŊ ‰é…ÅBša’œž1žG=·ļg€,MÁâŽŨûÖ/ïũýũ=ú Ú|*ڈĨmĄņ‹kûzõė1zĘėĮ‰9PÃĻâîŽ[ūúā‰kZĖÐ Ëį%ïÝšîü“tKˆK­ ęÔm‹EíŲŋcÛōM?]Ó"ČBþŅĨ―{I=Xđý‚ЧiJĒ(Ļ0ĸ”ŠōwoYęæŧļ˜ģƒzö9qkRąČH=Ó ûÝ―…ÃöčÕsČĖe/Ԋ…âĖ―WŽ‹:°aËÖ]Į.Ѝ8þáåUÓ&öęÞcĀð—ž%ËgæO,B3@$j‰ äĩšü‚Ēü< äÖV,E3/ÏŽíÜgäķ ũŊßŧ>käðŲ›Ŋ°Vē„§g­Û›–‘uíü‘i? Øwų9kŦŒ―5|Āøc'Ξ8°cޚŸ@@š*ZģdæļkŸfčllā‹‹?͘YHQŸ.rYA•pũ]šīœõáŲ–Cũ9Ī9ģyNÏßžõü͝đáC{ũ]o ~õ_Š’‰…ņ[ŨÎÞ§M‡>:ĩqÎŌ§­UæÓ+cÚũ^tč\rrâ­ýŧ^Īæ ’bÅ`ŌnÞxð\ŪJuzęŒMo$<>s`ÕāaÓbâÕ,CƒŊˆ HÔŒ %m™;đcũŽ›N§Ôûay‹ƘēzUTBĄbōú ņŨv:iSn^ܟ”ü ―}ãøŅŧ—šŽũéBaöŲsĮc Ų‰;/ÅĮ―ˆT#ŅÁŦþˆ0}ÜĢGOãī9ɧNėWīúąE7câõJïÐEÓú@‡v˜õāØLpoĮ†­ytݟ.>{zã|Į ÛûŨķ­<ýFŪ`ÁŊbD^ŒN=Įnŧ|d>ŠcãŊé@ZÚŦŦŲLÝnŦĢŽQ?G_―rbÏÅ――ŠÞ?‰OĖA ĩÄWƒ1 eÎel5/3tŅĄ›œÓŠ38 Ēý‹Fī83ƒĪÜâl —ņþÞä҃Úķë0sõ1ÅP:u^fŌ{Šķ­ĄMPp}Œ1Åڇõ^ĻwŸ―yåĘņ·ĀôáK))Œ?w‹ZMÓJ†ÅÚ|Uz&ðéÐ:ØËŽrðîŅĀˆųęGu4KýÆĩļ€eåێ›3­ĩW9/Đ Ą€a@fkWäoFõėŌąïÏ3 4ÅP€@Ș–ôęþá}ŧ·ëØuýéw@ÚAĻ%ū2QÖn}ÆÏ‰Œ$Cąák7Ä,K "Ž]óÞãDŪš,=X+ji%6iÁ€Ņ'ï&vđ`\ŸŌ@KC‰FLĸ)tVVž)!]ąF“uRN­6nYЍÃjųbAøĮĨÍ((RĶÄËĘRq‘1/Hŧ ÚŌcð Īm”ÏóŸšE‚hëUkÃáSÖÍŦ Nŧ|xCĸ5ĮÔžĐ(B XÃ+•ÖųŊÏ ņ2—žđtuĸVĀ4ËB@$j‰Ŋ  `ĢĀ6ë7rD Ũė kæŊÝÍŲ” ö-tųOžūAJkšŨ$fd늋ēÔĨ˖.įDĮÆūÎ áiĒ;”ņāᩕũíX1dÁ>LŠDdïáÓĻųw,WT •7ï2ÔËNچK%žô'%áþ/7žČÝžýŦØk.Ÿ=tėÞģ›göŸŽŽ}ĩķĩeũđFĶĶKžˆÁ4ĐoĢOßz])īÓļYCŽ0&gFX0ęÁ―‡Ï?{›™øFeÄeKۀžøļxĐH|Bž†Cüåzü;‰Z‰Ā<Â<ë8<<ŌӉ>ūlZäéŽ%Ŧ7Né{paŦ°­Ût5kEąŽ|ë~mrßDčüݍtėgyûœË)Ļįð)5]íŊėX0xúķĶ- €Āž†Üū.=ãûnJzú›§wĨ§}ÆĨįĻ UïŸÜ—žÝyûÁģŽĖä1^ĮĶĻTZ―úæÆIŽ€þaãÍââÂüĸAUĻJ‰{uýęåk7ĶåäŦՅIïž\―rņ—‹—ï―x—WĪ),(Qš  '=ųELĖóũ‰yŠžŽī§>}—$mÏÍLy m›[ ĘJIx|ïÎ%éĐŧŊ>~ņVUX$ĩĄRВß>đréōĩŧ2óT)ïž]ŋ|ņÆ―Ũ)IŪ^ūx5&#//#!îaLĖŧäL•*/þųӏžĪfåŠĪĘAéfˆ) >ŪŠĖ#ÅĨīV(1ýT™ãNeYW™˜–,ĨE3•TĀR]Úũāx# +Ó-&IĘŽ_-•đw‰īŧD;ŌÆ_Kđ‚‚Â"Ëórá—eˠ͋ŸÆlnļ DGGhYķŽ|Z*øt°–qJ[-į„ HÔß4)ģÞūļ―nÃæ“·ŸååŦō ‚ QK| …EÅFĢQ§Qįņ D-AAĒ– ‚D-AÁâ›dž œÆņM˜'ōäÁņF-ÉŲĒĒĒ·oãhš‚ ūX‘ŋŋŸĩ ïĻ%(ŠĘÏW98Ø{xļ#„AßšĶ“’’U*•ÍÂU-!”ĒķT)gQAß–eΜĩ\ý‡D-?A|Â2Ý A™Ų‹ ‚D-AAĒ– ‚D-Aņ{Ģ– ‚ QKAĒ– ‚ QKAĒ– ‚D-AAĒ–€R&þÛÛßĶÓÁBS;þŽ|3ũņĩ þ[…’ßXûk@JĸoC ‰Zâ!E#C^FJrJŠFÏĸzšœČīF|ƒĸîɃÔ|#ýÛ9IŅĶĸ ųcņðÅ-oÐxņ-c”ĘüýŦ…4…â^>z“ĶBÆÂĮ1OōŠy€E―ހ0øý04j€-‰) ­jE þ2MeĮ>‰M F―Þ(üžhĶ)Ę2zĢtE ‰Úĸĸˆ9ՆÅZĩkßĩCóÁËÏóOŨ<§SЊ1ÅČâ/o5û<’1|K Ä\Þú1ƒÝHaeėŊH][dáŋj†2äŋY4iZŠžfoÖB†Eį–Œ_vþ―œĨþE™ģ‹Į/ŋ'•ųßč óō‹ ĸs+ÃG—Í:xå%Ô―š;yõóDA{ujÄĶ5d~įh!†Ü­3Fl>ĸČäŽBþäÔĶņ‘{ ÅŋėR2Žėõ–E“ÜĪäōĮQófėļŒ þ%ƒV]PŽ3Ū ÚģtÞár9KĒöĸ3RŒ:þڒŸâ§íđtáÜÉaUą€ E1Ž ÃÐŽL–sųÔ荧ĩīœ‚€Ēš‚æŠ4KS˜ķ˜Ie%/~ą(– íÓĒ*ESa+Ą)ęŸŪČ>Õe?6 -]›PԊÔ:KėŽe?vĖ”Ėn(•š°ėĩ“˜FōyU&chúcaÚ|LŽÜķ …M-S–~ĐßÏÍRrđfÝėĐ7“u2ÆÔŽT‹5‘ÚĄþéʆĄX9Z&U‡Š•|>"ŠbLϘQQÕš6ŊZVąųÔŅæĘĖ…-°ˆĻ –]›üzŠþ|2iđœúiÍüýŨ_3r„aX3ÆŌģ’dž@KÛ۔õëô]g%útÚ?ĪMŦĶšæęÐrU(·soPÓuӄUS81?zÁüÃþ ŧ(ięsO4õņUĢih ĮOoÆLƖ vh9“R?…1cM][€%ÞN–Wråm(ċÞõÛī ņ‡CŠ’jXš‡XÞ­–cĄhöõŲ%Ó#™Œb­CZķŊíå ðĒåEų\åãۆf,í@HĒ–øÚ ĀĒŅŠl)čVūz—Ð cúóy̚††6ÜqÍ É9qnŨ‰uŦFwp+IũxĸĒWÞą2yĘ―=“·^ĢålĖÞČÅևîTŊfëݗ^R4ý9† Ó?ÜÏÍĪi6ņÞÉ~M›5 mēå\  YË~đ?=đgÅĘUóG5ŪÚm_t*#“kó>DŽîØ$īIŨþ#ÞĐtOÏėŸtH”Ë3˜:vLŠŽē^n_·"N… Z.É^ūdÝâ^]ú―+ģ_]îÚĶE“ï:ïđ) ’yq~}ķM5ĻŋxßU‘aČ_ÛŧĪUĢFÝÆM>öČÁ‡åF”Į24Wð|éøéŠKA™ŒšĩsÅîýG§þ0fÃĄCÝ=ķŠCX“F n8-Ō4üĩ4ÔŦ,Ö ~Û ›rīPÎRIwôęÐĪQĢ^'bR–Õ~ˆžÚ§V“Ё3ÃįĖYüģfr“_%įÓ ū5ÝÎÝ#vĻ[ŧýņčŒÏĐ!ĘJz•’§6—™g*3ĀTæÄýL)đÞ];ÜŦqhh“ÆûoŋÉxuyŨķ}Ë#g ›ðsAÎëČąíCCũøaVlūJ3ŒĨFÚœũ ņ„IũÎ,‹\đrV§ú!-–|†ĪœÆ†3k†5iÐļWÏð Ý}ŸgyÄ4î6КÓģ]GÎ?°—oÐaHû`FÔ^ÞÖĪqĢVíķ_z đęÝÍU›ũgdīúýė۞gņ°(~ÉęĩëVD4čþÃíŤĨLđ Z6Uzqëvýáä‹<đ[đxÉ……Ôj=ðČ― ™BþúĚEŦWϝÜŦ~­ÐĨŪc`:„ESЌwŌē(†ÉýpÆðfš4i8tų‡ýƒKÚī iØxý‰Į øÅܕ{n]ÐaؒĪ<í‡ä7IF=CƒÄ[GŧKoŒÆM&n8ĢeH“z`ŲôíQŦŋkŌūgxRbiHĒö+"0|š Ļd3ĶgˉKw%æōrĶxÉБOÜÆÜđāæ–9’ÄÚÕë…69gZ`9ëäįWo$įŅ,JpĸN éŽWį—í>iÓrâŒþ>ë§―’lY"ƒĒÕi·Ū]N’Q†ÝÓ#žŨvčĀžïjú"Qøüð”—[Öïth3o\Cû=‡Ïó†œ-ÓnÁĶŧöílę­ë7aģHÞ<Üũ!ŨøčRÔŠÍ^ĶæÄ=šuųBžĢĢcËۑrjËú_â;Ÿæœû ||dÃ˖õĐŧoĘô§Yœ[Õ Ŧ~Z7ĢËņMóSôLö“#×\ŋn{x—š˜á(ŧr.0nÅ΃˜aŸŒ:”(ss‹ ŠÜĪ}―Ęå{ Ô)ŽbĘ―ĻūģÏZžcÓĒ‘;M>t;]&g€#crR–­ĸÜ%sŲģ›6î―ŦÉ2fčĘFSĢVOŽđ2|VĶ>wŅĀ‘oāŧĶØÄEŸýåĄH1ņ1gn$RúâäžĨ{ŧLĒ^―āρa)`BQ0îþ™‰ER™”įĶ2ÕšNØ@―jÁq‘Vo›™ØzÂŅ―;›ø•sôŽÚ šŦV&ŽibƒþĮm…ąG\}ðOģøCŠ6æÆŸ~P 2\AâŪ ›3Ëũ›ŲŦö™Ÿ%ëðË_6Ï^Ÿ6rÉķI==Ų<>Wkųø‰<(å7kBûÓÓOYü|čĢĘ(Å''7„/ŽąpĮ†Đm6 }$:C,J―ōėYĄHA.+æÆC•Ņ@{ïÚ'4 Â'𰂀HÜjw?zōpöíîĢŨ!ĪŌž\ĸéį‹ #WŦĻÕgF ‹“î­ŲĨ9>ô؂!?ĮÉhh9'Y/._xÆSĮĸ‹ĄųúmÛWkåLqÖåj/Û)ý}Ũ`ĸúųY Ÿn5ž|;͛ÐÛÝĘðúޅøƒ&éęˆnģęöZž{ëŒô―ó–ížXņΉõŦÏež5Ų9õÜáģo +#Qû5ĨĮÔÓ­hLĸ‘ã?dæÜVį§_Úöãä%ņĐy…zÖĩĒk؊Uýý]Ž (;$4eMÓriCŠ6#ĖïÚôŧCˉðQ|EÃߧҐ5ï۞>žtôümé:\ōōĄ˜ãڍš;ĐSX­š•ėPn⋧/ēF éWÉŧōāþ}dŠÞ•=˜ŧWï<{–Ûë;ĸwŅŨn<đ†õwW csČĐĘÐðˆÞÍŠi’ĨïšÞžØąõ؋Ôė|ĩÞOEL]*šGÐÓK?—kÔŋmmĸÚM{t ’Š­;MFÝ;ō8ņuÔŅ›]Æõw€<Â!Xķby7G{ߊåĘØ=<}ÔŧŅ ŪuýŠ7ėüC2ïžâ  /pÖ~ÕĮOøąa“Öƒ&Ö}–“˜ĸ@yzÉÔ›OĐuÆÂÔØhčôãÂū•+ ™?ØĘ QĶÔ lMõ1l;†/ŨđypP dЗïËéÏe ]§iËÆšĘT…t-°iÝ·aGÄøu§ē@iįęáâčUĄ’Ŋ‡Ģsú‰Óf-JRņ<Īšāam)Š Ø ŦŨ}øÜzÔ­XÆYDœáE\\ð Þ]CęuïQ·Ÿh0|ŪÄųāáÝZz ÔĩŠtÏÞ'Øąi•ā–}kÔŌ!酠āÖžiAÔëy9ZŅā ,ý1h88üĖé-uŒ/ĖÛUČ}ySKŧä2ČI͉‹€ÜÚÁŠe ōÔ―Ķ@°›_ĢŠ^›ô"Ķĩ=ĩpýų‚ūÝj!ĸ܁œĄliJZĪhĘŪīkÏc#6FÝļ}ĻĶ2nüČųEõ{ŽėÛރĶšR*i}z‘Ô§ÎÉÉG ö•›ĩŪ&›ÐŦGŒKģu=9}lVtžĨ ÚŅĨŒ.-ÓhŽ|ĩŠVĘ!øBDóešP˜Å9ŲyX1ØNYŠŨČð…K7;šą‚uyïãsõÚüû7ĒAöyØCGkđ)ēy#ē͖(ƒ ĢâSVDlؘ9§OŽqytx@ä!`=Į# ĨvĖ ßøA1bĖØšå\ä˜⒍•øŸ(•—A$ĩ) 4Ī NKNÍÓrÞ>OMÎgy‰ŠÉkz9TŽV™A"€Pôđ9ŲĶ0F­Z/cdRP” Ļrâ øJF đ4nA-2röåЙáócšÓ9ĻfMá)`ä8Ó˝—ˉÞed`[ŨKu8―–­YŒK(íXkMJ~„4Āļ0þ֏“VWčöÃāŪ-]EӀã4F1ËÔ:41,%r…Đ…Ķ‹U. `%Í*)(•† _‰Z‚bdĐũM ŸģegÔÚMýkT üĄqÓ§{Ü~ō*úæ™ØlģWđϏŽßq8]lmK_ú)jQȅûžˆĀʄKËWŽ\·mþīÉļFXŧęey^fHZ=bĄæÄ†Ígŋ7Œģ•uÉßtDF=gäL žQĢÓY•ĐÖ>xᯅ[wn™đËĄKŨŠ.Ĩ‚VɊÏŪÚŧIÅŠõ˜ŒtëŌ―š–—óéĩÅzÎÔĢW­°ï;ŧž:qėéģ§—ŊÜN/€hHNxwĸöĢUŌč—åw[ĶĖŽœ·pÂą7y%čÜš[Û§Úvčâa#ûØ(Ä+œd;7Ŋð:%ĻÝkíÉŲK7­Y<ë@BĐfmk0ˆĸ˜ƒæÆŋZūfõö5‹wœKmÝŠIEĸF}ÝOí?õėÕó›·ÎŦŠ ĐūoęøÉ“,Ýy•GČpz­Þ|Š8―FĢ7ū kyÁgܗ2Ú/etˆÂyGVoūø2a™ģ•ŠŽŦÓŲ#{O]‹A •—öðat|aÞýÛS‹ XŊ Ó‹ÂXäuzóFNoÄH[īzƒĘ5î(ŧrÂČŅ“gLy›ĶĒYJÂĒĶXmÐqĘ5j§šu|Β­ŦįÎNÓToWŦšĩCÂÓû–ÍŸļpÝËt@3ĻSsĀķ0736æŅõÍŲKîĶ•’3kÖŽ\―5jęī Ū!ÝKŅī5}ûĶË·DLŸäR­G=/VĢ3ĸĘ `ÔIo;ÏÚ­;øÎ˜6qéšĩ‘Žg!Äk>ÄūxóôCއčGqîĩ ‰įķlŧQP$ð2p‚W@X=:oæŽå;Ö.Ü6ĢK“–6īQŦ՘ĸfŜŨqƒoū1Ã0*U!Ë2ô™q ē3ōē nÕ&M Ŋä ũ kT‰ON’ÂHäéåWĄēŅ ŊāXŦĶ“PĀ;úŽ5ĪQ`ÕJ^NŊ/îKŦÜī–―ÜkMš:Öۆ-ýcްsũņ­äãáÄådžON—WŠ3edO7[Ö2Bé‡Ģ‹Ŋ_Yg;ÖÚŅŧbårîîՃCœĻâīô‚ŠõÚFŒėd °ĩ“§ppĮĶĩmėœ}ŦĩéÝē‚―áÏĢgKđWôũó-mEđC͆­†ÜĖ<5m[šzݐÆõ}QAŽGðwݛVevA ū ôÎÉÎ+ß°įŪ-jT ,ëĻÓ>\ĖŽ QÁ‘FÛÅZU @EųveĘÔĻßžv•äï8ÆuLøä°ĘÎ1ĀYÁ:”­äčåâ tt­äāQĶl“&ĄÁķ(KUlUķBúõÔ._˜•ëŨļK‡z>Hé\§q@ŧĖl…_u_Ï •*ûxđļ{7kUېŸgīþnØôîÍ*DđxVŠTÉĮVÎ8đV ôwuRbŒŋŌÍ2Ō‚――("ðûå{FóüųËũïã8ŽÓĸAF^ğˆžđšÁˆðG‚Ņ ­b3NZälLÅ,ėéŅŠýö菅9ã?ķŒĪmŌ‚øĨý( õŒDÞ\Ōīd0w?áĪŪ%FNZæMËsĸt€dÚføļb䱅đŠ€ðĒ _,đįxƒzwxËÁsÖĐD,õP'X*™šJĀ?vΕlÏŌ€åYHûóČÁ]'G.TŋŠûŌÃŊÞhÚmiöÓg(*Ņŋe—ąÄ‚ĨŒh9^ ĐĐ/ŸČ•|i,‡oŋé”"Îh’ępŸÎķe‹€„ŒÛŋôíÜwöšuCÃüŋē01·œüÏLåJžvŸO,>ūap œŅÔīđSÃoŸ.‰nÆũ­#ÝÃfR—"ÆįGũl4įúøŠņĶMÝ|\so\‰ūŒ"úrØH>íEFó.Ë[Žûō Ÿßfæ=Ņr_ ÏóoÞÄū~ýĶļXĸG@é?ð!äry||ĒRĐðööEüU ÓßžPY— ôrDƒŋÄĮ―|Ūtóó.k'ˆčkÜTš—ð2úyŽJƒ+ŨhX·Š;@|c E šĒ'1wÞĨX9z5kâlE‰_ýՄ4%&žŽĨJ{–+m0ķüĘpvÜŦ äXÍĮüó/ÎļļcOOŽãĸ@E@Ÿ`„=Š—ÃHDüQŽop=€EAD_įü RŠuŪTB€Dá+õōįI+ėę5ïBAi…„ŋäÕÄ"ĒĘW­BøcwH]|Š–5íB€|*Ē$ŠßėÜDýoãūr‰<Á·‹/~oüiAŋ‚€œAS Ö}+ŋ.I$j ó&,Ã@h™æCZd,›J†EKÛŋÜ5OSðŨę2īþļDģŽåÖwËv Ët暎ióįUS%øev–•Ëe4M›öĘå M}†đļ\fŲ͝Y†•ķ1ē”[ĶĖÚŠ—Š@Ó^™M}.l‘ĨĘWG$j Іy/یýūch“†Ã–ėÍ(ĶĄ>qýĪ™[·/čØĒA›IÛRĩ€ķdĻ _Ý1uŅþÛCSH{hýŽĻŦ)đoŊŽí!ÝŨÞh’íy#2Mßð:ÛČĘé{{Ķmđ'—ë͛ģbþôū ÝzQĖĘĖYIŅUüš CB7éÔoČëbæžY4͟ÔP§ą ^dp29uoäšĻís~ė^·vÏóũî,Ÿ6Ž~í į^°ræþޅK7m Õą^ÍvûŊĮ22ų‡›ŧ÷߄rYqÜÕÉý–Äe%/ZģũÐÎe회y“cĖ~|ôûNŌž=ŽßOfXYÂíýģŨFÚŦëōÓĶ øŠ‚D-M3­Œk?Jë1pũŪåēëGMØĮ˄›û6íŋĐ3cšÍ­íûį°2Úüõ‰Âĩ4―sÍĶWZF—pmËŪį] žI7 ßĩ6ëÎö1Ë.‹bņÕ7rõ<ĐÔįũïĶ@ˆß\;yäÆëŪcŦU” Ū`ÝøþQoáŠM;LčŪ,ˆė;āfz­­ŧw„Ņ1ƒŋ_”ÉģŲ/ÏÎ]ŧŸi8žõŒþÆåŲķžÐÓcÛōŸō “ųüėŌ]'­Ã&LíåķjŌÐëĐž1;&únŠÔĐĄ8íÔëģđTĐÎ5ĪV[Ι9ķŒá鈋ëþļmõ”:+§†§ō”1ãųšyŦœü{GôiĖBŒÁŨD$j )uęÛXï:St­āSŋũðķIYŋäaĨOÕY‹VķjčË~þ> ž_óĄ5/ė~viû—6#}ŠîgÉjŒëÝē|åZģú‡%žŋ–‹Y–ĩ5ÏGåĖĮë ëĄ 7tkVÃÞZ†6å{҇K·õmFĖ ĐVĐjĩj.\ÆeCĐņËúyWî3j”žŋüŪ@DĀŠíļE zķŪß,8°Ý„%sz„6íM3z[ĩ1ŋ[ģķƒ~p7ĒG …2Đ/Jöqfhbhã\ÝÆŅ―ZM_ą0þnaæ/+Ķ/ÜtŠH­-Öčƒ:ŽŸ=Ĩs•ēöðŦ?+‚ HÔP âīŽB$­i ģRАķē F'",Ÿ!ÖÖspÐSŦûÍ9Ÿßw`Ļ­ĩPЋõ‚T̰0WË8ãs•V†Ü—Ï‹(Á”b āQĘaŠV(X>'/ËÔ‚Ž1+=WGQÐPœ)pތ6% ĢĐ= ˆĀēŌ'ˆĶ`4Ũąi‹hÐq9XÓĒ("„ĪB9™9Ō’Đ0ŊįÄ,а―ÂĐû°Is"Wĸüó– ֘GĢpĪ€ Œ―FŦA|-AĒ–ĀHpŽŌķ,=wÚĖ=;VŪÛÓšéĀR ÎPŽ1Ï‚:AĀā2õ[öŽjˆËŊÝķĄ·­cåÆõræĖ[ĩ}åŽÝoj íė"ÍŠT mYģbÁļI??{eū`ĂVÃóĻdŊŽc•‘cë>Ø<~ÁÚM+W­ž•é8Žaĩ-‘sķėÚ1sáöāZƒŠ•ĶuڏSˆž Õq–{ö5ZSC,˟_š|ÍÆí f†ÓĩZķ t’[ŧŦnl]°xŲĻyŧrUjŒ‘{å`˜seûúÓFĮ^.į~:ûú]ė―čŦųFÖĻXjGÆ$ÞÚÖoøĖx5ÅR|AĒ–ĀX{Î?ļc\ˆsZŽÐcÎŪųáMiĘuÜÂųŽP€ķ]ÆFt °įŋLyí\ė=ĘMlæ,…ûŠõÛÛøÛgdsĢmÜÜ;ˆÃŽK—Ūn]Ĩtđ–=:õóāÆ^<Ï{7:gtĖóöåC"fēĮHl@ĮƒĘđfĸ ëfšŅžWčˆeģŧąĀvųÆ=Qk–øÚ ÎÕ:ŪY8ÓJ]`yÎ:ŋ·W‹Ō™)é˜ķRŌBųæŒĻx$ˆbĐĘaÃúũvQZfeüVđ1ũ?ðÆ\Šae Lgä r–7D å 9ŒÆOķ,C?úeí°wö:ā‚x›ŸecЋĢTŽ‘)ĘžyĪFÏņ"%AS/ĀLä<Ē>ŊōœA*Ë*īĐšHģr–F-KXØÝ―͙v‹N ŽeŠki™Ē2|b<”YĶD˜‘Ëh`Æ @ZcL Zæ6“1g4 â›ļ1—D-™‚8'16EcU#Ļ"eþˆáŊGQ0åÕģ"[ŊÏ/™ø‚0,]!° ÄâįœýË!„=ƒĄeâ‚øÏ@Ē–øïRĸãC ōĩAAĒ– ‚D-A‰Z‚ ‚|-F@3ðß›‚ QKüeēēē233ÕjõŊĶÏß7’ „ĸóX†ą··ũððprr"iKĻ%þĒ0ĘÎÎNJJ*_ū|pp0EQā?Ņh”þ^yĸþ}ÕŠUmllūzÚ‰Z‚Ķ锔)g===ĸ{îÞņņņŅét999Ōå­ āk#ōĩĄVŦÁĸÆĻ+’>g(Pk MΓĮo ‚Č›ūŊÕhõƒ?#ŽãD ļâžįOžýÁ6Īīý‹Ū⠂\Õ!áŋøwīī7ũÉĐÉSV,AĻýK‘8Žû­ôÁFcĨ°ąg…Þ―đߞĢVïĨb+ķZĩnn-wÃu—ēĒ!ũõ;ŲÞ=Ú7rķ­Þ/dݑ›™ģëųM‹đu‘ķӖę„âÞ\;Ōótkųð:‡/"„DĐ_(ýAš|„Ý(ÝÃôxU\ÎÂiï_ūŊ[ŊXîäíåëãQÁŨËՖ+ÖĐŪîX–•’ĄŊĪ7Œ<”Č Íc„ŒzƒmŰ*Š ģÂãū9Ūģ—Ņø›iŠĖAĻý‹‘ŦÚßüA‚ąN§sņŦū<ˆËĻ{ôÐÝÂĻtQÁ`4Īš#šķÃzAPÔįd耏ĩ_íÅ+ŽŧJu]šĨ`눉wjŽR4#„!†4ES(öÆþg`hĮw ^՚öïf-gÝ*ø ú$#—§õ đxcˆԑ3<8ļA-BŒÆĶū-—Ļ<ĮÉ]ýgmŲsųČôyÓ Ž;‡yk8ņ·ūûĶĒ– HÔ”Œ)>ļr{ķmi7áúŦÆcüYņĶČĀ‹BlÔjtzYõÖ-žŽÏZšÞĐŠ2ëd=ĄqekOš†2ã„ÎŧycĸŽĘĨw_ũ[Ņڊįī@ņËĮ§7­§uoÞžß~ââš~^Πڝ ĸ)šZųōv†ÚÖ#ÐĩBy§‹§ũU/eGTP˜ũæM\jZšæVrÓķœQäEŒ‘ Ũé sbï>õnj+mGģ2F øÛ ĩd**ųzfžMøP@õ›ĩĒmó@ŠÐvæïŌրßOžiįmkëÞlõĶ•ĮÎFgė#ÖîĻ_ĩ”᱑+š*xģÆ2="wULl\ģīžG–Ž`}?Uޜ`ÔiŽË֜ķ6<Ί‡Ļį*w°ÕÍņÜ­„ #í)gEÖĄĮˆyô™ŧi)úŪ݇ÍÂĮēĩLĸIKâ_=/fÜū2Ķ”·\ÁT0âG/[Ļ\\ŽÅ”į>xßË™" øVDm&ģĐ[M6ÓĖ2ÍĶ™|d™ïóeĐ9N5Údĩ +Ó, L3Ņø@SÁ―ũ<öÞŋđPÍÉkÍŽÁ؟uŨš,Öfßßþįˏ}ÎŲhEå# ’$ýœ ‚Á‘ @­Ĩ`QˆB€ĩ’Ô†B°Ņ­R-ø&ÖjX^ËŪŧļ) 5°ŦĪF)6âÕĢV’$!DyyųÕŦW5MƒVÏjĩFDDÄÅÅĐŠÚdÎÖhXRé9ZV[åFlŧĻŒ Q{ķw$…[­„ķĩ’$û؊ŠŠŠŠŠīīīððphõžĨ•••uėØņm8Ð8\Š1^ĻQĩO'g`€―5lgTŨđϖ_óVåo ós‹B°­E­$ÉĻõķīééé·DÎz…††ĶĪĪäįį'%%ý ?$<Ķ(Ŋ1Ŧ="ŦGdß›ÚüÁ†~2B(Ĩ ļŠ4ūcÓÃjõ@D8Vz­ēŽĮ…ĻVeÔķ1’ävŧàeFé™sĩ.ÃjwÆķöģ€oÜðÔÖÖ3$!Ž[ËĄQQxā$FgĨ%h™ÓéÔuoüSœqÐL$ŠęýT‡ŸB)4 ™ŦÞe Q)üdۊĢJčĖ”Š#§Õļ!)ņā gpĮ*omÛäe1I’„ØĻ™ÎWŦ)]·xþßō á•g]w=5'{Ęo|ü !üÐÎuģÖĸ+"DwÛ;=3cfFr,4;ņÆÜŲþĢ>MMToō"^‹7!p!šÞ%ÄļvfåüåƒĶ­ŌÞÖÜT„0O]•‹‡‡…ÐjļvlÃSóĒ RŽ­Ï™ųp~Ęo›\#A .I’‚sî#ģˆÅĒoŸ=vÞ[úëïîšzūč‹+<MÎ)@ÆĄTĄĪ1ēŠ*ėôÉCõŅ]쟸åúYã—žÜ%õų‰þ ‘xRžņ @Î9ąZ-ĪíĀĐ€Dp.šŋ̀RŠˆ-Ŋˆ5xã8B)Ó\—ÎUå߯ `CíßOKÁ†Q-–ĘÛ&­(YúĘŠ i˜UB‰‚$Pm H,íh#\ ÞDĩXÃŌČĻ•$ĩ>ËZņōá•ûÎOČ}ãÎô8ÓdÓ3oCΘĻûúÂڜ§óūļj yęųu#zÄ#gBŽ@ÛÅÅöï;ôdŸÔũ-aļ^Z8uËGE‚ÅL^ļxːDwõŲ,øgA‰-88ûÕ<"@ŠbÍæđÓ w.œôk rŸĪŠ-_õGÁ#ĖGÔ ĄÚEý…đÏ―fŊzwŨņðˉŒ Š/ZøÜ_ũ}U<ā‰wr†-Yýę;o—Ü}}åŠeøå–™ëßÍ>jâüi â\  @0ä*gŒųŠZ."ČĻmë$Й B„Đy„ÍÞ'-ÁÔ4SÆ9%DÏöÕsÞŪėūå'Jv.Ÿ<);bóēÞņ6.PĄX­Įöîž4îĀūÎ,ÚŧÁŧŌēžïéyÏ^ģxŨ衏l›ûįÕĮœĸØē5’ļ‚éW-vWÞÚY/åY–―9Ė‚\lĶڛY‘ðjēŦØ@ āZõūí[§/^ē`äÂ9kóËÆt>ý֖ýWōÞßĒ{uŒØõ―įĒÅóŌbíĨUýķíœxņþīhËC D@!þ3›ā}݈4ŒjŽÛtÔJ’d†ÏĻUPh&gĻđ5Óī G?ÜúáyûļŅ―?ĸŽčþ‰sŧÆF$ŽÕyåŒ/O^ėĀ™ œ†ėëÚ°aTm{}gÚã#.žÍ_īr“ëōURQï*ßûŅĨ~ģtŒŽÓ„E"€\—―ú/}íÓ~‰–z—Öō/†æ É˜ŪĢ Mwĩ†nÞ*ëX―3ĨũôŋŒKúæPR{bxī˜Û33âw?öû?Ü;vʄvíĒ;w 1ãcmÝõÍđã_†šÓ:ZjęđqÁđŪÞwŅðqšŊŪÖ[ įž­?˜+IŌwMYӘf%暭ŧGd?hÓۜ;YDؘŽPGÐåŌKŒ$ŧŠŊ\ãh·ĐBpŅØÕzģŽ}ÏÞ㗏§îZĸafBؚ—w-Y―ņÂæš§ĐĒÚAäčđnŅ„éŅÁtąäĄ#ĢŦöūšieÚžéŅv4ßÛÐÍvĩB|ŧd&Ļ0™ŪézCræÕcųKێíÝë™Đ 6ū™yGF‡`TŸĸ(ï•Ï ÎVÏ?X cgqíɝōlnnnÎû…W‰îa‘}gįn(Ü4yÆģnTüĄÐ=n&€ÆÜD‚(4[­<đgފM_–TøGķwZ!<)- ŽŊ_ķáx‰+ŊÞũ֞=GëÏíÞU„ŠÅ­1 ĀtĮ`Ð2ŲÕJRÛFČYōˆĐÛ;ü⃏VãIýÕÃĐē,ĖxũãK‚;;u1æŪ sFó“!` eð―şōč,uęŌĖĄ™ÁÔ―<ĮQtQąvýðÂ36æîvįĪMĄiGNĄ-ūSī_ÄĻi“ET`ŧŽK·žýéĄb‚@!ÄgĩÐ,lãsQˆgâØi9QÔӟɞĨę4$qÜīœČ(ÅĶwJIļė1×/™Ô'Š*|ĀŌeŦ?+Öüē–ŊXýɑĘ.3ē‡œ‘‘AųAĻ}íâ‡J=…įŦÞŲ3ÖnUþK§•üé'įÖPpķŪļžfðí‘―âlþV"äƒđ’ÔĶ bLLĖĐS§Eq:ÐęÕÔԜ8q"""âÆÁßBc–R;—]Ŧó8üýŽðĸFkëõ WŠb‚ŽŅAŠJQn ī=’ŒÚøøxÆØ‘#G<!ĪÉ1­įPZŦÕ•˜˜xcU`S ÎĄöLt)­-,―Ž@ĄPˆˆ VS‘AŠB Ę Ij›8įŒ1!Ä-ąãĄŠŠĒ(Í ..U›Ũ\Ėd­€ÝBcBÔč`‹ïĢÁeÔJRÛ@[Ä͜5Ãr[Í?žĄD!nÞŋÛŧc†ĄŠĒæÏŽ]ī‘z áNnþ xC\åx+Üö=H-€ĮÏ―ĮŲŋ^€ÔH-€Ô ĩZÖyžk­ãŽugķŠöÞj ðFgũÞUuDDfvũĖ\?ĀĖtwfFÄĪË ĩž&S"IENDŪB`‚doc/images/ifw-ready-to-uninstall.png000066400000000000000000002531151325366651500201370ustar00rootroot00000000000000‰PNG  IHDRÎbðZŊrVIDATxėVŲSŨõĶ\åW—ĸû5ypÅå‡Č•H–”û'YÖ"ˆ} Z,•„äڜ @ēÄXČ Y[ąČĀ0Ž3ĖÚÓËtOOoĀ€PR)ûį’e§*IUÎĖ5'íūĄ3å’\•ïárúÜï,ýõ9#Ĩ šÝîÅÅÅĸĸ>ūüōKĘc:~D&Ŋūú Ŋ’ðņLžiÓŠ}†§ĐÅüŸ>Ošzė•·9éI^Ĩ>īV+Ĩø? >öx ú ýlë?­ðÛßßĸÜ /žðŌK/=ĸüó_/ãņãĮä$@ŋåę›oū!'z,@>9‹WôаdĢ“c'3™ÆĢGĻV­†ŲÆäOAžýaôüõąĪĩyīÔ]―>ī“–…œöú ß^üúĖü'ÓĮ&9ės"čĮNĀ3ŪüīūüōË/ūøbŠŪë<€ŸÞĨĨ%8ðļ°°ð°ü%čÓ d"Áœ™Ô"€H E1ܒpð Ģðīɀ­š^ÄV},·6úÐɑđŌ•ĨI{},dK„ð,ëþÕčcßUōú˜C,'•dī,īąúųA<™>(ÂęõA#MžY}Ŧԇt]O™ŸŸŸû>b †Ū‹Įx=<ÃĻŅh,&\Á €G°!x ÃE spjšFnÍĀ*pUy 0ūĮ(Š-‘[8‘fđ4ņ@Eôcoū%„†đŪ nÉԁÖcŅF&Ý$ð$ô™_\zļô`‘„šą°øžáõYÎ[ĸĐO &fqá?ęOþðOq<\ZˆÍÓú`ķÄŋē‹ä"æÁīÉëC"b&Î~, ŨŌÃ8āĸ &ÜŌ _Ē ‹†6ý ―°Zƒœšƒú €üäõAgōú  ËXRúĖ>>ÉïœŦŅÎ'Ð8ĸV­"EO"I<Ą‡<žķC‡jĩņČk?;öÆ/KЧĮqÝÝÝ---ÍÍÍ}ô‘Ûí&Đ0ûðúgꚎdxëĘŨÓj6:UîŠßĖÎéËĀ@<Ác~$@@Ó4:hX’Ģ–(Þ"aiĖþĘBĀĖŽHÆÝ\ŨNŸ8Yq9î;'=Š>›pœ{bČ1%ƒeŌi?<æŠĻ:Ý6ܐÐú€_b―.ŋ–ČFkū<9ó1]šŨÓŅÞqõV+ąđDóņņÄBqŠáĮ‡úûîÝûŽhTŠ‚ßúÅ!D™õÁĒ},m'ęęÁi§+†Á§įų˜ EâÃƒ}ýý}ũz@=5N1( 1õöĩËŨĶ5ƒš jPÉŠ&ķ@ũOŒđ|žNū#ÅīŸz#ĀÆýGf2ú €Œą6úÐã‡6­Oōû…}ŌZ<-ė‹Ēą’>`$ĐÆ@•'Ð'EQ”hŠŠ* \Ū23:―Nj<õpjDëh―ŧuӉoŒũõA”,˄Ųá ƒ­­­]]]`@^ŽãnßūÝÔÔ455ĐHN ÅmEuyĶũÔf•5ntð1ï(ßsðJFúū·†œƒšŠa'`@8ÍuøˆNBC" N4,NæA ôÓĀ–,ú˜ó˜3[rǁMâ[€ÓN…Ŧĸíš •Ž`æb^ (HûŋīšeÎÐT|rHĢęsáņO2vd$øš +ø#ø§ï}>!+āUĀ ōØüXgcVM #ĐøAÔG34ŅsbAjęŽÚ3 ŧSS3JõÏDf5ŲŅŨ;æg t­wJ·lČ.ĐÜ[QRyė”›U ý_õĒðvQyĘ1pĖĢúēx€ïÏC‚mĢ· ýucNmŦĖĐüðþ,IŪĮ—$œ8TÓw‡5 ßLߔšSšwoyqÍé6Ÿ úō‘æ  në 1đū|Ũ–Y3€@Ú#~—0ðÂ-į›čca^>ŧŊčw—ï€â˜%ŽåYJ|Ŋ•æ‡|}z~,||MzÆ ŽŪý~Yö‚ÄfĶũ gaé^ĖOïÞRûõĢŅ<)‘HD’$9°ÁõI]ý…W~úįÃÕĸhŋð·Ŧ—ĸÞqņë“ĩ·ÖžÖZ\,ð|$A#dĀ­[·ÚÚÚ†Á_}Qá?𝝝‚ `ÚD‰čđË ÛkŨܕ>āūč þĨýĒwxū5ŋyã‘ģ‚ĀG–!/=č''d‹Ž,Gl Ūˆ˜Ö\ü0ĄE Aƒūēī!% Ðdģ>X Ë!óØč#‹Ėé]k~ōJj۝Im~>äčÞžöÕ·ķôČz˜õ;##Ž1?+Ä_-ĖMÏDˆāŲ`ˆ ų§GãūŊęāųÓ[JęúG'N ģąQD8Æ% x‚!IކđÃō!ßī#‘īŌę†2øĮ3ëąåƄ°ļļĻîį­]_VÛ·\5;ŌŦÚ> @8Q[Uƒ=åđƒA=6kϚō|^–c§'œ.o8•yÏŲęwóŦN8= |‰8G†ÆĶf(‘X&ĀrĄéЉ)_ˆ Á Û9::1ÍIĀ°0tMø<ïũøØp\+‘s;GFFž °d5ņLŽ ;ÆA‚Ąˆ,7æïïĘÞstˆŅįg ØeÆï p",|>6āe1äsƒ–ÎI7/Ęss‘Óû k[oČŠâũúYpEĢBČ qð‘ļ Ũ§ŽŲ°ĶŠÝ—ęō5ôųÃR„ņĖY.UdI˜žqú2" ū@ˆįŲqįč$|#)b??ô:9ÉÏú Ÿū5—3oî—%-=ÃčĄũ‹ŽMbŋ?}Âáp īȇ$ĩgėt―þęĢ][ŋ-/øëþōo+‹g§E7žÞķ~­ßå%‰ðIHGGGooŊĮãa†ãļP(äõz‡‡‡/]šäũûIJAâ!aqßđU=[Ū„ôHõ·•ģ7ä†ö―ÚÁ] [ž>03 ÁÞā‘įyô`·4ŊĖðæØDXrĖQæsQâÁŪð{cB@[:›đ1LKNÚ°ŅG`}gōß^ŧnCa}ŧĪÉŨ›ßߖúæöĖý3rôîÅúžĒ’‚ŽīÂšĶ Ļ…F>É)=>ÅEœŨ/Vė)LÛūđ°š)āwŸ;õóu[K*~ędzÛNBT~<ęQ]MĨĮσáėn).*"QÕįü|\%)"k’·éx^ÖÉkēĒ‚GQԁ+GķæVßėjŲī~ã;;ēÚnņáļ0†ūîŠœ‚Î`(`BžĀšÏ–§įWîËÏMßē)íBÏĪ0Ýģí5ßÜ\QÛæóL6­Ę-Č۝ĩû|ũ`4"īŸŠČ+ܓ›_|æÓá+ĩûvgf•ee―ýöŽýuŨ$=Üzī:;#=kGIįÍŅß/ïĘ " ŨΝ؞ž_ZÛÜ3ĢĸŲĩĶüœžžÜėē#įÝ!1"‰€H4ęđŨ•]røæx ‰o?–[Õp5Š*œwļŠ,ũúÐč‡ĩ‹‹ Þݖ~ürŸĶK'kŠŽ^žĄDž‡Ëũ^ž>5īŧĻ.­ŧ,ĘáŪĶúâü‚Ėôī=õ^ĸÄáĖÍë6mË+kp…đæC%umwÂaîãßŨü&sw~vNQųąû3Ž8óųÞ=e{Ŧ*33ŌÞŲ™sõóïÖÍ<4p~,#ą’!H†1F―_čΧ77ˆfâ•ý~Ņ'šũ‹Þ §Ļ y}žû'+V#Åq…Ŧš{ŪžÝödf7ËáåÜĩ7œ2`!ø‡ėØ! [‰B8ŒM€`‡[ 1ōŸČD(‰•(HI$ó/Dąla;â1ī&8fĀšk―ĀēĮôôQU]•7”üÔŲYy‚§šŠï{õõ›ŊŦÂ{Á9uH™,8MiÛ"îđ1ϋ^*dõvb˜xRJ‰BH6›ĩ,Ëü<`\^^YņyÉbžZŽķĄĘJR3. +„?f’VÔdã6a<`œ…ß ÍEUJ)Čz2c >ęšX\ã!42X—‚R5҇ՁÚ` ņQ ,áG­9úĨý‰T@@t*üþŒJÕÏx|ūóïSÝW>9wųúžgŋĩ•`bäÔ6m^đdvĸŲ#Ÿö{„zaž+Ū`ϞÞ`ęÓŦķ―ô―ÂÅ^—åíã'·NžŧbÕōIÃ㍏ĖßļeóĘÅģúÏ―6„Lļ ĪR!°f<óŌV`]:~uĀ• ī…JŌøŨŠ-ĸžÎ€đÉڑVÞK5O™ÜÜ8cɏŸĸæD)˜ÖLŽčþëŸÞØõŧ?:ul3Ð?ĀScÖnØš Õ8ÛŲIjÚį·įę˜ŋnų‚―ũ~—ŋdŲÚĮ§59ðþ]FBįķ*ĩbÍˋ§68ŊĸýÍÛ·-›Õ}üoį{ \Šz܋?_;Ģ}ļ;āx‚ÄųčÄĮąQ3ŨmØēāá–āîþþö–ĮžZģ|QĄëƒÎŪëB)Áļ4MyãĖÞ?ūąëũŧOu ĩϚzåÜ݃áģ{ƒ–1ÍõӞ|nãÖMόŊšpąÛ!1 Ė [Š‚ĒLJzÄcaĀÔļ™OŽßīé…gįõ]đxWUÍ~bîĎ9/Ū{Ū.ŪōĒØČ[]ĮöžČ/^ņʎŨ6ŽĒWþēĸ$ðųËąúGķíØÖQËá?x(Ņ?0@ûÝŨ?ĢÎGÛčü…ïքŒ`Ž[ĢKĢĢ/HÉ[ƒ:aĮ(ËĒl\Eå8€ųčF_đ?%”ýāý؃ũĮ‚Az:ŲØ”gšÚ2ŠŽGG·ŌúœĻĻķF(À§ *ģm›R đŽŽ,“ÉĀ!›N§áœ…<†Ø――•eÃcîu9ļëõoūs’%bDT†îŠęķiãë3<” Û }Ÿ‡ÜJ*•đwáˁ_ýēČâņ‘T*FŠ7Ēð5―ČaŌ6%óĄ”@TØ?čKÐXކnFģ‰dŒZqŠ”ð ū~°ĀũĖę1‹ļzýšUs:Ü|ÁLŨŸ>ĨLq#ģL 2I:FUčđWpŨë}kÏî'{›s#ŒÐį†ÝÞÖVm ĮņܐäZ[ËĻŽjjĐĻĄ·]_‘æ ãl"|?Pԉ?―ðų…ÕŨníŨĸ8óéā@Ÿį8į?Ü·{Ï>™Ęeã&Ó_”Ü’đŊ/Yšzýę&5§­Šķ‰)þõrâä sfÅúšžóįŨwþöðĨžT:UIĻ"’ AĻĨĀ Ą ”JyPøŨĄ―ŋŲĩóÍýĮh:e„ElÂĖũ}&ˆPč8ŽŸĘ•ÅAŨ۝šÁr•JŲmïģ/kïoý%(Œ56óģRē?J_)Ąh%ÚeĪĄ ōßÎ=đü0úßæF›mێãļûâyīrœÍŲ™ÆÅņ.þģšņŌsuåōúäįn―î*…|ĪI‰R įĖ0 üBė͛7ø> Ēøŋ·H`šd°lóÁ‹™›åŅx6üáŲÎëŧókįæĸÁ0kĀaŽeYöūH•€ˆ!!éÂ|r ˆØ­D̊…FšŌŊĩ GœÚ$﯆æA/r›$›ŽņƒĖņcÖ͆š§žïūēŨî/=\·ãŊó…ūq#°Ūœ9Ųyęä™Óŋ96:qŦūąXėđTĩ‚ĩ{Ó}ã_Õ―Äþvą§ĢgyÛņwJ…OŽG/–·wŪž9yĒëÔčȧ2vųviá‹þ‰™]―œŋÃPĐõíŸ ÅåęŪËģļ~f'Žttén?räąÍāuėÛKÓį>øõą?=ŽØn“"åûõoķÐYėč/öôķōÍåÁžĐĮĩÄ·nĸqčüŒJãïžÍ>þhčėŽåļKScĮOôõĮ&ŊŨ,ëÖĨÏŪß|ÅAđsĢŋúå‡}ƒƒŋíš6ĸ2Ižkc׿–\?Pš}Ūxöæjėo_==08ØÕ^œš[ņ“Īšú ÐŅŅÛ?P,œū_Zw<ŌŽō°xôÃÎâ+ô^Þ0―ÚZߑ_\˜/‡aâŽÝïíhï*ŸęûčxáÆ“õéóÓ·iēøÅøąßí>ÓÝ10õĩ27Į?ín?^üėčņöáé§Û/ŸOë™ØōÜëcC“wžFIðčˑSÝ]ÅSýg/nځ__ė―ðčÅnšzW?8uũ fĩųû#†ėū9ųûģ?LËŊŌï—­DĒLÐЀ#!Fsē*ĸ~å3ð?Át.?’ÓfíKĢŅ@6 Ã00QĻ”S*mÏ|õÝÔåęô ëņ_"ÛF5ŌLӔV…aˆGü*lccĢZ­!Š" #Â1‚Ð_Ûyþ |ãöģ‹_—þXŲ|čø–įzÄÔJhKS AËtd2†‘B:%‡øöūli*ÐOCü‚Xm ÉØ„ÕNÁWĻNãāĻ|fÃČōåp߆xŪ€šíWqqĄx(äBã ģŨŪ|ųāĐaŲp™ĩ‘ģcx Šag84p@éŠ0ĩĨbPHð:˜Š hē`ˆščĀ—B!wĀËĸ8"hd8–É>ĻĐÕk†ð“ŲmydÍÂėþHĢúþtŋĻœþÜ}–}C9ß) Y~>jø‚&~í~eOĄgþĪü ŠE~ÚÐlkk Л››øá?™Â ƒīáĪ' akÉ0聖DJ 0؞°Äį° ‚GY–ģDZ3“ķ֚ĩQģǁMJŸQ9Q+üßD.?’MŋðƒÖō#ôōQ<úŲ[æNļhKGĖį‡~Âîš~ŲFŪ\~Čd†oóDōÃģoV·lÛõmƒ­É“sųɎ§ņ#išĻháQļīMCi/‚ŊRøÉߟÃóÃdAČîO.?0ޙJ.?ŲÐ{įGŽ&P"•Ϗ ĩĖO+û„VøīU*•RĐôĒôČjeÆĘō lHĸ”šžŌJĐ\.WĘxĻņ%Áƒ4,‘(<āA'ÚÁC{eeĖ>°‰‰ljĄ1K™…0€B"“ƒq*™A2!0ĀO„Ð,·9đüP–hüč‚|žý@~ŋð#ó? ―…č\~øáAZá ŠÝ?â‡āÂōa8?xe@€áüH2œ@Āyę,?ôÓ&?LāÁ9615&䇏yüp… sö‡įÍ冯lýÝeö’Ëēü°D’á„p―ߍ Ā‘3üð°‡å‡/BëüĀČ^ĒwãNáōŸøn›ĸ·ÜÛ 4ēĒyrCwũØôK”:ũQËŨœ„Õœĩ•ĐrîÜđ#ŅVä―óÏϊ Jü:?âyüÐÎ:›þ{âl•Ø­ð#NúōC„·óŸˆŸœIþŎ―Æ4u†q/įÚ û°}[ēl^>i“mÉÜ>lKöiŲío›ÎešĻx‰Ó0ãm"8-Už1XĀ2Tˆ›BĒPҁc8ÛZ; Bmią§įŽGŒéæe!3ŲóKŋœä9Ïûžï‡ޞ‘>ä0ÂP/€Q ĩQ Ē jĒĀŋGøß ”côl˜PJ Æ#Кp‚Čs Ž-Ãņ‚Āģýg čYð‰æŌӇõür.'wįáš…aņcŊýĪÃ&~LTėĸãzWwÏ?&ø‰CšüSk%pëPþ–ŌĢBïT•íĖ-;ë̇_ĒPŽžķ|c?nė˜QĢFðà AOSķŊģ&ýÛī―å͈{tÔŠŠ2 †Bč10,uŲŠ’ †99GBˆÁ˜*ūŽyŊĶ/Iũ" ʼnæaøßNo|û)ģėŠĻaŅ“PÃēßļVPb8ėw§­5íŦîĒŽÜw`·yĩĨn0a9ĩ`pĀítÝ}ŲðæÃ+ý] +fÍ>ÛŅŋÚR†ÓčôÍ1Ąž ęĢtÂaʰž^§Óh4ËËá°"+„ēĒ(ōÃ5ą9† ŋ†ũvZ—-MÝuð<ĸœžÄģ‘ããÍE{øŠ‘<äw8v·WQãIrŲ·<žpt0F#ŦÓČ$‘‘ĩāHœôŌ[“§|ôúøįåPl$Č2”‹m!2~āS@tĢZml§*CÉÕÚüŊ—d\ú} 2u|0­VŦuŽ&ˆĘ +EPE!rX%€D jVB(iĖƜŠ­å‹ūx!ŧËëÃSJš.T,ýRúD2æĻTYJ°ŨŲš{ŦięÄIŌäé۝ *$‚ĄJSõž‰Fiöâe·"„aú:ę7gf•”ŸšŦFó/ÔÓ]ļcSÕ%ĮýœĒŒÜ#ßd.Ýw°pWÖũÅÕ>…0hĻņXŅĪdI’Ķ™óŠÄôH„ïÍö‚ĖMóĶ|0-Õ|ęLuŅæU‹ ęAčiŊZūÜžĸ‚KiũĩúuYéŸüþŠ­{ęjŽëW.(mpˆ|0oþÜųiÛÎvŧï6]ŽĩÖú17āliëS­uĩĮ.Þ̓ŋZ KÎۜ―îë{ē3Ė_ãąēwįÁM\wĀ{HB>°ą ` N R nJļÂÄ05J „Ģ@ĄĨīÜL8†Ã%§&…L!˜ŧ>$ÅvbÉēedˇ${-iw-Ų’-iũu-Í ÜBþhËïóßūëũĪ}Į#ïxi0 GícÃ(98ŒéŧþžpþМķëŌfJu7ôņį&WØÆĢ_kūMîiUŅhƒqŊŽĘ-úîlfÆÉýëh·―a|\[ÎÍë†ŪÐͧoÖę*­ž Qˆ>y휞†ŠTčmÆk_]ˆLÚôę˜!B zŧ•ÏūēïÏoP3“ķ•|đ]lPgfd9B͟ŋQVzûÛŨ'FWŠģ>úĶZŅO!pũÛøņĨœŽ}ƒ=-9vD’Ī _@@Œz~ÂÉŊo|ēy)ļyÉA{Ø;ƒ›ˆ˜ôÖĶėÕÕÏvFz;GÎþýŪå/Äî>‘}v[‚O>æØ™+đߝŋœ™69ŪŋŲTÁv‰OrW†áĻÅJ9|dŒXgbÐēĄ!a O—“ņyAāģöū—øöķfLl·­Ŧ§Qsgõēß$ÎIÚzä24érÚۚõã'Ž娸‰4Bˆ”õOXīj(83ŊĸýÖíė/ÔԖ)QýH„î—IB@„HŸ‹å[•4;>6D?)ŧģžÜEĘH胠!‡‚ė~"ĀOSŨۘ;õų1?ŽTøD[·āŸØ7SŨl3qė Čˆ~JEĖïޙO{ÚoėY6knęöóęn$ųÏ&§(B$HÁÕzėŸRæ%&ŋģĩūĩSŪ  0 G-ö$„Yļfû‡‡V‡@íĶ#GuVŊ\NydsVė8ž~J]Ž.:wtīĖļwé†Û?Z—ŋŸžķõ―gH…œ&E}=] Q’MÍ €@Ž|aúėiŅ–ë‡WŪ?3ãÕŲF!ŸŊoČK—ˆĪiĨ”§$I˜›m݄L)#ŧ{Ī!9A   Á‹žŌ +ôþe+Ôۊ}B)å4…ü‰%ÍöúŸÖŒH‚š=‚OĸéDÔÜ *Õ·ß_úšénAÆþ]‡óCäTālī"īuí/ié'/)'-=}ęhBÜ@‘\N€a8jąĮGøP‡šūhåšÄ8.7cwÚ§Ū°áSÆĮ‚ËŠĘ/jërņ­eÚZÎårqہQáĄĒ­ĻĪ„Зčy!bČsņ‡Ķeß·rĸ9č!Q‡Žœ>sŽĖįr Ąģæ/&"z ŪDðõŲ7‹åąÏÅĮGŧōūÉ:Ÿ[|+ûĖ5Ąˆ|)yĒĖã Žˆ7zXļéŦs'2/V–įzėĪ ú )ũ øD1XIWð‚ŋ[:Iï‚"=ķ‹_fWrōĪÕË'Œ íĀuŠ$ ÞģŲón^)ĻÐu°@L]WQRÅØŧx‡Vkö R€ū`ŽZė?|€žô€ą+79Xqãøžۚũ§ýuŨŧ‰MWŽĖOJ~íõ7·Lw)FĖ[žjŦ-[ýæ‚JgؔøyYûs[ÄÅŋÝ<%n`Áį‡6|pq^ĘŊĨÉáëĄĒfNŸ1#’Wžņ‚ŨíA„|žg'―ądöøę’Ko-Øb ûÅΌŋĨü’ŲģaQōÂUyĘņ{?9ąvFŽŧG?Ņ+DŽ™ķóģôY1íĸøŪt Áî3đ)kvlZ7 š=)S ōþk"(ō…d€ZTXŋpȐ…œDQĪÐqåÃ-KĪÎMY’ß`[žqûšĪŸõøĻiŋ^1nšp|ĮšM^\:lpĸóûÖïË*x1á%SĸŕŦ"44°%ψ@Īöĸ †ŲŸb˜đņžĶšÖĖXY–ãYë=}uU•ķķÞÄ;:YĶE[^V_Ļ.+ŊĐ3Øė\{kSyĐJ]Zia˜{šŠB•ĘØjíāđFݏEEwŦjŽí­Fgh°°įrówŽmˆųúS*§“·ĸ–į,ũƊU%ešVŦÝŅÁ›šââĒü"õz#ëčäØYĀņ<ߎŨä}ŸsãVaiĨ™‘VqԈĩÍ\§Ņč-v–ĩ1–ķFßÛfLFMĩŪūĨãX‹ĐQĢŅ6™–csĢNĢi01ŽÝÚĻŊđ[Ķ’žw^V^eaė<ÏöVâØ:íj•ŠēĘĀr6―Ķ\Ĩ.Ū•62ŨVÕŽ6k―VSc0ķÛĪ’í}ĩĶÎhĩ? †á7ƒI +aËēþ!é2čÁVh—ņþĀ0ßÁëK.Oþ „ _|·É&õ<Šz@°|ĀĢæð―傏čgÍ`ŧo?Ä>Pö~_°ÍJ‚…|į‚ĸg0 G-&…Q]uņéĖģ·JŦĨLēĸ7`†Ģãx‡Ûíîrv<Æ\ ÃpÔb†áĻÅ0 Ãhx*a!xZaF< úQŸóēs>đ–4G?'"ēîëŊM[ōOįHÞ°+Cˆ]€X…ąXb`–`[6öŧ•ņ‡[j·Ô#{ð‰Ūs{/•/ēn ~:}2ō―――™Yõ Q1ã^ˁöāŦ=9ƒ/ãYðeņTýzþËøË/öā—áĖ―ãTčŌÕc’3čüRv4ãŊ|ý]Züjōså/ŧUđ˜É/úúՑØîÏũwNåÖqfú}ãí­[·HļĮÞûπÚ>üË?ĸÓüėgfjëȉpU‹ð*ˆˆŠf‹/‰ŠĖĩ–Gd†‰Qļ}ŦhŠēũ6ĩĐS ŠŪÅîSöĨóŸ$Ž™Íâu,wGÁÖ"™ÁÞ(f R™îŪjóĨ2RMIŲč*5ó―ũÛßþčG?ęâïh‘üÕŊ~óãĸåO~ōW‰[·n}?dĶŋüåĸúŨŋųá?ýÉŪķeĮúðÝKũÞŠ*Áīę8|ŠV@šëwßĐPÕܝĪŲÚūQER([DU”ũ5••Ũįņø`Ë.k-QņíŠÖvUÏóđl`ï˜k­ĘÜÍÍã8Ü]ŧ4(ĄfĶÚ4Ôô8!” åTfžëó I*ģaš^ ũÔqŦšũ–~ˆŠŠpōņöxËLx<ÞDčūM"Ķ<ŽqÖįyĮ!ŠÂ―s€5ïjŲņĒöw?’œ ·nÝú~ĻZÎ?7SYE<|é•sïŠU6gEÕlUaðdkM†[YĮqąlŽĻĻ„*U%:Ė%yŽĢ9•]ÄÜ―P…SYĀ•B :/ Ą\“ƒQ―Ėc•áÚ^TfĨˆĒ*|‚ŦĖk ŌčܝĻZ5M…ō"xUúÞ*ú’GL53Ëk=/Īē&fxIŪGō}ŪÉ g;‘Ęō―ŦŪČĩZŲÎôÅŲmÖ۟*Jb‚cÜšõ ęþË^$įÐ&Z(TĮĐ(˜-1ę$wŧGzÁåyI;ėŲdi(Ëų<ŦBM đ&EÍ ,ÔDęîĨ/‰üáŋÏ’™^UĒŪ9%ą>ŒÚ@]#ģcā>?žUUDPd™ŧ$KT_įNU)$Ū‰‚Š îŨēŦZú2#0ēmcÔA˜iv"–ŧ ŠÔŦˆŧ‹ȧïÂĩļ‹$ÛŧšŸ$M āj#˜wK*nÝšõĸW†ĸ[ķl2ŲŽ@ĩaĄMNŲî•nv€ŒÓMÔoŊ€5/ũöĘČíΗDÛü…kYsÎÁžįY™Įã˜S~ŪÂÏ]Ļ5™ŽhĄHžûR øÞ"Ō‹%+Ûaw'CkÍA„­EÁų|ŠˆŠäĨ ŠPįÐ,3EŪi~n U4üú%äíjŋEÝš]mŠœÞÏqųŅmÏ>éēĖw{éą"BHģ%fUĻž 03īK•Čb]ÕÆÍ›ę: )R‘K-p‰ęķ­š6ŲöģïÄ%!#3*&jûîĪ•óĖ4ģžĖĩėĘĻ”öãŸUˆhDj›íņ抈ûNf“ĻøąÖT.ELŨÄW ‰ČČP•ŽėãÄĨŠĻyŸ&”ØÞ™ēfUN‚`Ë3î6ƒoQ·nÔēq3t3ģküR5ā"DôŅüšfTÖZéáÛ§Ã)㒈pš_ jVό4ŅĨË}ûÞÞíY~á @æËgÆ:ŽĶ5õX üÜņšÔ…ÛĀɉh—™P|Z :íÍ,Ō{1Û=Tēsƒ—ÂTŧxWæXŨFķŪÃXŠĻŒ3EĨ\Õā$TTČp'(ĒýĀĐ"n{8ŸÏÔ$Ŋ"―/ëÜm·E2qžŧ"•TeŠ(nÝšõ fĩHdÓ02Šj5ÓīŪ`H@ØÛĘT’ą(°GdÍeŒ ŧä4––ú.›ČĘĉ„n5ĄĒPåÄīfįhŦ‹8ļoïî]ŧũow4n€Ũ&î—ĘóC'Ï ÃáÕeU{ÏÖoôŸþŊÞg=/ĸ7PAiøØ–o·ĩJōØĢ žū%DÕČđ“ƒgóğ,Ū GíÚđóĮÞ_FņæÁ։dxpðX_ߑūÇ~ë?;’Üļ}vžš,ËitāT+‰ĢkąŊ_Ę(pJ.šZQąDë(J“pAšÆjT#ĶɌcŦ2Æ:åÐCDZât94‚å°%Zŧ@ŦE™8[ĒX{PW‚ ī*'Lˆé,ŨųÕÛ7âü’uÂēÂaüáįÁ~Pū óæ!Ô=v‡Ų0]ģÂebbË|ÅxČU°ķŽ7„ņbóFŽr!8ņŽHml ôå°EöI†n·įÄŸnßžiã’GĶŧd’ô ŽÂÓßó؜·Ũ@ÆržaãYi"Ė~lURĨÛî|ðŲGïĒĪ{]rĖã\y/%؁+’Œ‘Þâ‰ū;RŦĄĸO0Ï>‰ĀÎ•yÃwēčīÆÖcŒô&žYežrkÁMFÆ-ÕH”ÜŲ Äö”iũ-|ōþØ RēOFb< Ue‡þXF(Z?5oQŨÔ–lŲīqÃįŊī6/[õþ†Ôhėýn]ũ_œ‘@n‹úĄwĮ-ũ<ītIŨō•‹îv“čŪĀũnûĪûÃ/í)2z‘žbUŊS‘Cðnā―"öÓg#ĶëoūcÖ3O-Šė^]6ĒMŪÂŦKDĸOĻÍ9[―xϒĩĨ(i"Š8B›…pmX'ĀXY ŪÆŊJŅđ-ēŅZ%V ŒÞä―Ĩ{f(&üÎÞw‡Gql_Vu˜åœQB Ąˆ`„Ād‰lrÆ0@ä DÎÁ€‰&Gcc“sķąÁ&G“ƒČ„$gĶCÕšnīėūÏŧŋũ}xũýÞsý5H=Ý==âôísÏ=GUUūqŲ= ŲDŽŅh€&64ÐmŌWĀVU€fU',|ŠŠ°*°•wąĀD†-îdÆa”ŧšB(# LØåÁó›pĀ%Pļ’LļÞȁ8j€ÏĪ-·ð˅ õ‡0þprģ>Ë{ņŌŲ·bã„ ný2ē_ÃäĪĪô…{ óģöĸöĚeiÝúüüīäÜڌÅo ÆĮ?ŊLYtD4~]>ję”ÔmŠ4XīýĘŅÜ!ģïÝyãž(É·oîаIģä&_8Dĸ―ŅH/þ°*cüøq_ÔŦ]·õŠũ%Ģ1įÉÕ1}[$%'ĩîÜëėóÜËŧ7 īN5šžœ^óEŊ>™EēåųĨÅģ§ÝËC2;ĶJîÖYFOŨĄ}ï{…ÖGgwīižÜĻYëåû/ ēteĸÂέ›4Ļ[+}ų.•%ۏoÚ°Qį!C·žĢ&üpZ›ž›Ng$ĐäõųŒ~ÃaYĀF#:ĩnÁķ]ƧĶ-ĸþ:Æęū5“[5mŌ°^âÔõ‡ÍL™]VđKĻ {CzĪë4ė=ûI1čïû—wh—Ô ~Į Go‹ųíÅÃÛÄ'%ũ1 mø„-VYztņČ…Ė·’L\6jōü}>ýäĢøÆ›ŽfJ’`ŧr2=đjÔči:~ŌþÐõįy™ŋöéŌ.ĐQƒÉkĐĒAy{cúgƒĶfôiÖļn›qŦķ­ÎhÖ N“Ö3›ąíü:ĢUr“õëÎüæGÅōxŌ'ƒvß.0Hė†šÞyĘ݇·w]šŽHÆžŦŧŌ&Î]45―Ab.cūQ%QŧuAZBýÆ)iýöøâįĖ(ąAĒ?oݚ_ĄûīĄ]$ą\lģĐÃSî8zá֕oįÏÞøÍēÎ]æ=-FķkŪ&!aĄáá#"#ƒ}üøÃø‘é“GķJŽÝpōwósl™;{ã–Ĩŧ-xĨ’ ;—5OnœÔšãÞËOe“ᏍĮ NKIî0núųÛûį1<­Kj‰Ëvi’1įĘö!fd éŨlÂÚ'YOÞ{0O“ÞÞܝŌĢmģĶMĶo;+HFËãkÓԚ''7Lļ>ÛLüoJ p֜ÛxóIY^oJ’ˆÃ:TŲHaɒğč5@Ļflč ĘÜŋļ+!e•&h 7tÕđĄãĨ4tąØčD™—#W`ņŲ0 j_•#ōa6“É ûŋ.MÓđ…ĻYqYą ø /,6âĨküčpÏ`…pî3ú0”ëa9æęšÆO•‡GF đþ4‡{i)‚s†#(~aņ 4Œ„•!F‰æV?ĩNÅÉÚöũå­įÅFņÝô”ĄÏĒ'îûáŦk[g~LëVýĻvӖcĖĐėøōþ…ËŊógßž}õ€…·~ÝpėRTY‹Óëo™2äÐÙCKēŊ\=—eĀæo§/zŌ(mïžÝcĐŪ–ĢEoėÜq,ōģÓڅíØ{ÜZüęëYïûuŲĩogũ—ÃV 'éÅýíũ^•\:ąiãķ_ōęîÅ3ŋþöÎÛÝDĸsÔoýī}û5kĘô9^YŋąĻÕļ•_JÞ1aŌŗVŊðšÓÖÜ8ŊßÉÍ_>5KŊ.|;jåĩßlŸóyk7 ;…V*W°|ÃM’.n\{Ī$ ČUÖŲs kŅĩ^TxŋCŧ·Š”yjeę‚ cVïønÕÔ#+'sâ‰l, …YŊixŌŌĩKCŪ}·|ËĐwYŌ†­n3mÛÚЍWOüĒðåôA#ßOØđkFë“Ó§o,―ļuúĘË"ŒÅއ?-ÛtžéĀYĢÛÚ­š·Ã,ĘBie—u㧍Įn·OŸW/P›š:Æ3yôÎu3/ϙžĸfūI4Ÿ;ļëWōĒ/Ó_.ģöŽÏœ/įŪm?p#įŲ™5C—\ŋnŨ7ËÆïY<þ‡ŦBĨ€—_m>N%ņĖĶu—―c}譋§čëæėĢkŸļråĜã+/ž,đļiÚâÝ7ŋÚðÃČÎ5n\=öÆĖŲdŒõņÃüĀøZž2aF$EƉo,‚oãvŨjķ`A_ŧũž$ņКŊ'ķ`Õū\ET _ėÛūÏXgÜ”ĪSûvž“ÛwlœÐ|ņWŸŠ· œz°ßü-s;T™;aÉÃ"Ē”G<2ųĶ|ģ{߆…Ę/3zõĸâÖóė‹Ö‡ŧįöč3æŅ‹U7xyxz• r7‰ ÎîÅA€úT%ĶVý' OŠ–ØŪk6\š'øýnī†Œ­S{Ø;ĩÓ ÉŨ_—žß)ēŠ-ŒĸĒQ͈Ļ`Wü6óƍ;đ}{tðõðëÔą―ã­ŦzPlĨ@ûS‡]đaéߡڝӇO\:jßĒ—ŊX`GdßáZT/_øėŅŦũ7Ï1bÁ–{æâ3Ę}ūjpŊЛŽŠHÖ)đzlwhýõ"Ģkĩle°ššėowiïŊ7ÏŊÞwŪûānN”MZ‚=ü<œ‚ËWðrsļtpOXƒ^ɑå+7îûQĀõ ·ËÚ`šĶ8DÄ øĒw\•ÄŪĐ ·ž^~ōäÉĩâŽ-ãú ›ĩՊLÅ/ï_1øĶĪâįSĄÛ˜îöö„‹üÝАsŧ13{5Š^ąEo­6~%8·MŌ§aœA}}ýôí[gö<ųZ~q^vŪĒëÎŅU‡Ķ~V9ĖÏ?$jô°A 5+…Đz~ÁoGG&õiáŸÔ;ÞûŌoO› ûL?đíÖÃó]ý<­•QՐũ ’į‘Ðsņüaņ%Æŧ{cRpíä]kôguĒ‚bwK ĔžOn[U ÛČlÎČŠŠF“ģ9//OïÐōåLĒïEGWų(áãją!v"*4›:õŲĩITT˜·;d'?Ŋ?VX°OÎÃŊžß_11uüŠÝ_į–[Š,–ÄNý>kߨßÃI5“„ŪĢfīMĻņIį*žÞWæŠ(ß§nđóš—ũtvŦĶ%īQÎēžkįáŋÜÍBjÞ9sA֙õ―zĶýþ4_Ó1Ēĸ†U-āÐPŲ1•>ʉųð‡_Bųø)Ģ\ĐNÁQ_DCoŸbHۅ€LŠ+Luķ4P~a.œ‚Öķš- ‹%IဠïbRp•Í?”Õ\/JX—ėÐ@Sčėī ĩ“ý8wf€Š“ƒ Ņh䇆›'GčÜǁSŠœP˜$Eėė7ävŒ›0ČļtYU­ŠĶóy ―,2‡Ûã"ާ ų*=sĸBŽßøĄøĩļ ŋXˆKlđtÍJóÝ+7^+ž:­ÓnÔō5O_:Ų.ÎCŅ,oēŠt@ÕŠZ éE—oœUėX}ĘڎđN~n‰F|œˆū?°MuJ+·O=vvŊėÞŨäYĸ'ųĄ#$ š„ŒDƒ›·›I~öô5Bčݛį9‚äPĐ^å SšŸ3œ0ēéÖ5ӗRûķŊLJ“G)FÛQiF·€€þ#gnŲsøĘųņÆÛ)g›šĶOIíVމüWw‡üûO‹*Ėš—™ĨaŽ9†~Üę#ĮÔínĩė čīęībŦôŸčúîîbvšEģ^œ…ũÚgŠU)6ģOĸōAžW˜‹Iô4y œļdËķÝGöęf” _œ=ĸ0ïÍÃý;ŦŠņýdz"G#Ð2bdxĸaWĀČ œ`töõlĸųļ5ßlŋtóT·šþ%Õ(*R4ÍJ‘ŲŠ2ØU Ôāœ{įŽ…Ý 2ßž18ŲųEÖŦ›9Ļĸg/kôháČî$ķĨbÁUÔUŠŠ%˜jr·3žŧĸ*įÝ­ÓGeŋe˜Ę[ýÔŪNrØÓÛ~~Z`2™D―āÐþã%>ŅĄvVģÕŽčēĀÎö}"1æã:-Ûĩm\·Šˆ4ŠDLā+VŠĘę ģB$ģmtŒH˘ũícWϏņ”UBÓĖŧŅ"’ōÞ„Šōr‹U_WĄ*ąjÕāSP8–o|Ÿ­GÎõŦ5ĄÆErĩŠq >_īrýЋŋôOŠ šþoH 0+Ņøð>sV4͙뮀0œŋ+Äíe1Ŋzt?wDÖÔF~—âĘÔģ `F_Üf[es—C ų|æ ČÎübÅ eĩ$"ČÚáö`ˆmŪC@€1`8 ąį€ŋ0Š ņ*å‘1xÕ ”+JSp0’éPĻóŽ…Ęã'(ï\1™·ŋļ֐K2ØøĨV: ƒķPžģ^š$°sÓāVš„)'pE>ĪûĄA’ŸĸöÝ ÁÃf͟—1eąo:Õbâ·nsyõž•›ū]ŧrŅ…į…Þa_øaƂU™EČË'čč†ŊFĨYšĸIaY6hĮ͟8yöČĄÃęĩmį­hzi#–XĻí˜7{ŅÆŋŋ0GČïUĩTąjІs*6› ž-:}žyųÄYógŸūĨ§ÝĘ{ļVŪ[ôķļVZåÂ?ō((ð­ÐĨV€Īęīl–’b‹ĘÔØA5’ût [ŋlþĶ͛–ŪþöqėåŠ/ßýýŪ“O 9q>Ļ~w§Â̓LŸ‘q43Ou„\ŌúņÂ6­[ųØK°S.Ĩģ/WÎiÅüé?]zÛĒO€|rØØéĮŽØ›[ĄyŦ*ĒŪð-E„ß=ū5kÖä9ĮŪĸĐ Uóš!‘õúEŽ;wÓÖoÖoYũÖ)ndŦFgŒ9nðÆŨļl\ģ–XUũ­ģEƒĶ)6ëä=ĀR-%fŦ†(ąũŽ8žņąÍ VoÜšråúó™đ‰ęf3ĨWR‰+2[(‰mþĐ'9<,}fƘQ‡‹#›ĩˆ•dÏĶ’NŸ|ÖąUKg‰5J,:•TRŽōWšđĻÄjl5øóJE‡RÓŌÆ,ø^Č·–ԚĒÅwHoŅĀ~hĮ^ģŋ\4qxĘėåįgč$šû\?ģwæžoߊ—}ĨŠußÚéãĮ9zýwXß :áDWÍŦN°·_đë§ũN[ļÃ-ąMŧŠ…Ë/Üže゠_Đ,5@QxOÎh/^Øôõ”ņ3G˜S!ąSB9EÅœ3!6âK7[°ĻŸ\7~æō-ŨUĻ`ïā—ŌĐÛëŦ–ŊÝžnÕW§ï―䕸Ó%Ž3ýÃ2 ĮŽ}pɎûSžËšÜ0HĶé@rQ.aA…!1]ŲTbÐ߇|rJËúHÜÉbÊT*K _„y4ĪN‰!\Į,Hþ^· Ķģ$YâКlP›.b^`rŋ1]ŨųiƒÞ• (c ܰsÅĘ*;™á)‚œ 4 ^JģÍųy‚..—ëJ܊—‘$p΄ę0+@væŨCŊôŲ9óKJQtLl― UE)kýåå―“eÉÕÕõŸĖŒĢ{G‰ŠšBýĢë 28ČA(ß aBuwsąîâUÉŋ\ĨŠ5ƒírHXDTZá.ĒcPÕԔ/’âĢýœoÜø:ū]‹ŠnnQ†Īöö3Qƒ &<*ÄW‰ðr1­Ālõ‰Ŋ7īÏ'žv?ABŧhttĪŧģ―ÉÍ?&*ĘŨÓ3ĶJí0Oc‰UmÔ~|Ï&FĒÛ{„ÖŠŨ°y­“ƒ[͉­:Ũóģ3TšCpĨŠąáĄ.FLe§Š‰Mœ Éށ!qņ5ëŨ‹wHDíÖݛ%šœÜĒâë'ÕŽL4ÛĻį€î­â##<œ æĖ?ßēOÝ?ØRzęØŪōG5< ČÃ/ĪbTÕĪ:ÕKōs―"ÓF N rV4R*ÝrŠŪQģB9g ŧķí?īy|€ŽėkwLōqÔT] ‹(Û Ab­ððęm̌/=íÓë#ïĀČļČpĢgHtå?ÎÁAvô ŦQŦZˆŋ+ŨKSŠ―BcâĒØ6:#Š'Eúŧ(ŠîîX):ÂÛÕ―R•ęĄA~ĢSlšŅÁ&ƒ]…ļę•#*ú• o’X­ä]Ū“oô°ƒk•sP5äҰqÛF\@‘ĘNAUŠÆ„ú9áE€+Ær…čŠ―"6hÞŠnØđÓ§kučánäå#‘ÝZ7MŠõōŽŠĩāí“7n+o ˆGý†Į†z Ø.<*ĖÄĮ'‘U­rX ››§Ŋ`ųØĻȘ˜wgƒ“WTtL°‡OpÅČ`OL •ŠÕhŌļЗ%‚]@…ðJåž―Ģcc|<\$I|p|ëí :­Ŧ;„ÖJ:Ļž‹(:…TĐâïÂ,íÜbŠU,įdŌ ·ŠõéággLĻ[Ŋ~ ĨHuróûãļžNĶĸĸÅĐ äææ"„\\œĸ)Gœ““ƒþa988Œ5âčáC,ôŪ"årË,x(fī+ă3ހgßjŠ å'ƒÐuËƒ5U–Ī4õ‘hŠĘ§­ ·|$Éeí,đĮ0ü‚!1Č#°6ŧqĶīģD Ņdžo€FeÃŅ>LĐÆîė`Ž€(­:Ų9C›‹Â3Wha–É +At]ã`Įh,”AēyæbŪŁKČŽĘ0ũĮaƒm6™†ņ Ķåāå:Ð―JöŦÕŌūS§ÉSg!ķđņðaĶ)$$øŸ 'ĮĒl”ENUkĖ’‚p—ˆyQcÕ2™ŒPƒZ)–Œ !Ę= FÛzuÜYĖö>ĩJ7ĶĻt1Ú\$E—&ū7ÛþK—(f}ĄcxeŽÉhā$2›á͈ĸDUŽ:A“i ]ŲÂēŅČx BđúĘhëYQÕŠ°ŧÓÊD˜|M4Yc~B4…č–-“ŧ\uk7côįÅōūv˜q=’ ģ7é’lāÃ>Āûę ƒŅ ”Đę>E-JĨÄ:5ïÐĻŽ?ëģ™û>oHƒ@ا &þBdƒˆ,pÛæ‹ýJW~­ €ËūŠ FYĩX)ŒFƒfĩč”%4ÝŠéôN•Ņ Šlg”―–ąÕŠĒŌaŲdĪ„–ÜÝũÅØï*ÅWļtúāÓÐGtyÏfģĩŒ(Ką~ņāũï6~ëœØĨWģj&6įA‹BʆŒÆŌ‚TXD&Ïb—EbƒÔöqˆÕĒ Q2ri3 H”%LUU—MÆÃzÏuk~xbG!sépōČj…_0ãW-VA6Éð€Įŋ)î‘ÂStĶô$ĸ áäũï? ”•Sõ@í öíĩ··„ŌzóYUÆÛēž(йʀƒ"_M‡‰ũĘtfþsAóGĄi&ÁRT…1ŋņy ֚·á,D@–ŌŊž„^šH œ 84°2–hbDlþ‡ķQĪâm7>ðF€ļ•˜―,/KF2X™ē†XðüŌYĢLggW ØÔ ‰ŋēĖ9&ÁŪ—ðävž:•x$ŋ ųŪA†č―Xu,+āå(Ā_|AAaû§Î˜õÏCퟎÄo2›žC}ĸ[†ĒSõéý‡vÞ!ūöšNþŠ9؂7™WoÜÉ)Á•kńļéĸ’ė!ŒÜ―uãþã7Žþá 5ĒíV^üÉD,ķɁ ýë*ÁœįrˆSX9Ïĸž―­ĩԟrĩ€<ū›%ĩðD,@%3 €AčZ^ Å<Ņ‹ũ–,ŠU➇ ˆ kŌK"ӜC ™ 8wa‘ ĀĖŽĮĻTŒX}Ę{ý„PN[-V D™-‡QãųfėpšĶp —ČJ_>ģ`tØŲÎ_g{ƒÎ+ØķYôęŒ2P.ÍŅŅA(&APũ―𘠊_ ž $ԟ‚9(uŪŦ%Д―–ųžu* e‰ŋú‡õāĒūŅĸϘY&;ãífŸōōāĸïĻÃßõϏ ó^+ų+ ËĄQ1ô/ËjĢ”8û”o.`pý—íŌPŠŒÎŅ5ęĮÕøÔĮŸŸ”ŧŒĒŋrB<ÞÕþņ{ųÛŲ‹Õƒā8 (Œ$VUtĩLĻĪj3ؖÍĘýMøŨƅS0 Âýn˜„‰ĻaY‰’qØß-„Į(ÄJ)ŨĖ šŲĖ:oėy^ī*L:ÆP‡-Ä ķ­,‹rĪãöŽ`„Ũ’Ä›ëQyĘb6c }@CįJ’!ÖAÕĖ&Š4—: ČÉÛ}Â&ɒA%Ķ`˜úĶë|"đôŊ•+sUUãĖ5h$T–ļnĄo§ąĻ4ПņŽȔdKbÃoŌ‡RóķČŠ°Oƒą‹ŧģøįƒa8ũŝËũÞÕú8ŅAÐØđéúÓ_―Ūą^Ĩļ°XsvwaWðŋÎm`ýîđĢO_ũŒmÚ-)ýĸņؗõW.JðO;ŧāĸį(C)ĢTô/ēȟÝýþ†Z‚Č]UĖJY :ïQ] ýŌe8? â–:ÔčSNīÉ!ęœŧÏ Žed ›ė||}yœ Ÿ; ”°:ģ4N‘GXĀE1ÅXpZpŠ―Ŧ åŲ ÞĀ$-•|$#‹Ų’ûö ŅtʝÎáĐɏ@(†•{ÜP8 ;›@•˜Ó.Ŧöļ8ŒĄ Qï Í1Ä]tÛXšUe5&“‰°ęP+3I’đo!‚ð Šũē0Ō 7Oíŋũ­{EgĐÛũ›ØĄšôôĸq1ÄqãĒ…§#ëÖsÆpBūĻnyõ*Ũäáëî =8ŧkŲ–ßĮŊZā#Xuú_UG?:ĸÕĪá…ŅmÂ―K({XøũÏdĮ6dZ%OgŒþ^Cí?þ°LS‰,‹üŅ’O ĨÃðŽOXƒd”ĨÂo`čƒéšÎíļ8šðqöÄNÎÎÎí:tŠ\đĘĪ0!úŸĨïā?ĸ'üeeŋý“Í$IzðāÁĒ/į―zų\2ČÜß<yV˜Š0ëÉW óÝĒ:P°—fÝ0rfQ0˜áĀ~ÉÁšÂØ~̉đ†[Í,..nŌĪIåʕŦXąĒĶi5Ú&ÖĐ{ũÎísŋþĘļ }’põ‚ĒŠÜ‹ÕøĖA˜XÝČŽÐu†Ž”élPbÖjąðĐÐWĐH$îã‘ķŒ@DŅ‚-Ú·Ôę zlˆ~8eĩ8{Ïōųâ/Š[TÞĩ+œÝ•qĶg·OžÍ_wmðĘ4ĢŦû’oöŌۆLÝxë‹ÎNĒ-æ2ûÅÝ+]˜đĪāΝŧ‰=ČÎŊwú’ïėÁmlŋÜ­_·>u<ë0ctĢj^ũŽ?ŋ|í–jy·løā{þívîpdÁā)ÃĮÅܖĸô7›ß.Z·ļΕ•kŨŊøŽM]OĖ(QvŪüqGĸÓŋMújxUˁ_öÉnķrõÜĮGM?[ÜrĘēå{ęßuØĖˆ=óë?đrę'τYģWĘ―; ›9oÞōĀqýüð[§Š­‘ĒÁĨjŒÓüm_ũ”$Øšá&ÚãeŨsŠđVŋ=˛ŊžØgāðĨŋ}óųï7ŊČ M1ƅ/3Ŋæø#,äd^=v6gƊõb|Ucäģõņ„Þ)*uývį@\T䋟õ>YŦ3pϊä•zūė·cs3Ŋž7cõZĸ/ÓŌG˘ībõðĒiã7îÚ5*įŅ•SŨŠĶŊŲ”īáðÔ1-Z,~ņûųcĩðÁ­ uîŅ4ķRføGãÆũ ą6Žß(óį+w7ČoßaĘĐķIŪŊn}ŧãJĮ Ëš7­JUý―þ IâžčeÁ (áŌ+p,„Į"X”ēLCU†Wđß pĨl{Ļ Yïˆ{ļļļšúųûUĻXQ–å%K–Ėœ93 āŊVÏüqÐnÝ{fŋÉ~üč!LC°Þ;g°SDÛÓ=‘{ĘØ/‚›[šÂ†Ä`žBÐyņ+ŧÂ#slwÂ-%9É{ģ €PúDđ.pĘŽBMF‡ęq%$OO§ĀĀîõĩ ûΆ_öĻÔ,đJųÂë{fMĸž čĩl2áũ{^FYdŌuQ`~ïLdŠįßōÕÔŨúU ށþnöĄ*8ŲŲr:īĒĖ3WIė ūþî^mšôÜubúÕGoTƒ]ã~ģ?mVũ&―~üį_K,pþB ð wūY1,Ė^PÜʧ§LŒõđā;ģgBß5ÜĪa]|šāLjĪØ;ö˘Õ0ÞxČÕģ]Ŋô ŠmŒē“ēó4ŒĀvÔęŨ?a[ûÝ?^ČÛķŊzĮtÂ{Šž2šķõvwôi›ÝÝ>Ė·Š’ƒ ÆA䚌Uo™2Ą}―x\ë˜/Gnæ 7ļĒŊüÞ]ßõļÄgRŨ6ÞîŪ)l?‡3‹­…šÞ:uRûÚÕēW9ÕnŨcDëÚgnđ;ČQ(ÍŨôOR&·Ļ+„ö™ į•Į…!’čĖnâ`+!X°dō)ïäQč[!4@zqôŽđ@Þ2ĐŨNýe>ą(Dģ’øÎÃ|ÚŦLw(JsŲü{ýgĩÅ éTfiXęð‚Ā&€5€$°īqh<šât3…Ĩē“ē𚉠COŊ?XZŒPppðĄC‡&L˜āîîþÉ9 +**ĘŅŅŅÅÕĨ,{Fēif—ؐcĖtQĢÉÄČhŦ"J|X3*Pų :ŧq7Ļåö` Eãƒd\ Œa!ņ9˜ĨøØ„Vôƒ*’$‡Ļ†IIu]™ō]G]>íóMŊIiå<Û›cĸî·áGTē}°ÏĩŒ…ßk’d(“čoģóŸ)ˆÞýí·ŧDz·txÏÓâ˜ŊįTß§oą$b]-bý5lã=Ü:š8ZĨž\„üģŸ=É.2zļÚe!ABMĩ ũÓkDÕXŸ ē 5gŋ@šuúÕ;k ƒôäÉcÅ%ƈĻFeïB-*/cŠ‹EÄQĐstïžĩ{õïiģīY}WöæBæ·įöęŲĢ\ŲčČâ5™›MJ ÎüōÄRŪŪ. XģZU"ØæļĻdïâlQoÝ|†ŠzQ]3ļųؙ Ÿ?{ƒb\_=ËĖ3˜eQƒw1=!V2G‚8'ŋ€"Tðøaą94ÄÛAĩ(f"HXđ~ýLžÎĻlÝĘM' ’ŅUĮĩ{LLk!bÉŲËþĄNēX4Ģ€ĖïÞžĖÃÁ!ž2ú;Ōó? j> 7šáv\Ј sVV$ąPŦę6bAāõ,ĀŦH[†É|”–čÄ6$fģüü‚CBTUõôô4™LYYYŊ^―BÍŌu5X,–’‚ŨŊ^ð]“É*č†ÁŨ02c†y]ŒĻfsĨaŲ‘HQ"[[_ÛÁE@ųŠÚĄYÝČÏȁ3’“ęØýņŦÎ#Ũ{ĸøīPþŽoŸĒ"–ķŌ`čúïbÞ1īžŧ0°ÕÁKwó Á~ÝÔ1ã°ũpRTYũũūPݓ0 iČ9G1 ˆ9gŜs@1`@@AEdÍŲ5}æœsƀŠŪ"’$3É3ÝU/Üó^ŽŪndŋĸ~ĘŲųÍÖtWWÕŦÆ[ũÝwÏ9“tĄDcíĩۉĸÓ―ē•ØZBø]ⷉđ_xÁÛožÄãdm“ī 5ĩĪ  9:QAœG:­āó\ðŌ0ŪžŦđ`D`ŽA@ÉD2ōĶ]w}QëÖ+VŽxæ™g/^ėtĩĸSĄÖëŠĨJÅ­‹Ė›ŦTäKÆa:ÝGiĐiĄÏ’vŨ‰éå" ‘{…ÆÓjčāØqÞ―ÜwkÏŠsĶÄQ cۊŒ3)c ĪmĖĩąÖS*ęęë>ä°iŨNß2Ä\'#Ā<މ"e…Œ f“N-MŦ'kieQĶˆžVoÚ?iš~0āRs°‘RLÄG…ļ”MŸ’Ašá/ -‘)ËļHŌpÏŪ“Ą&auÚŨ›ļóä.—ÜŽ …Ô˜HiËeL€•ŲÜĨ;ïŲ+.{lÉÏ%$í^œqŊŦ8éäŅûģz1„Íú]šuŋáÆ›:–tüōË/§L™â ĸ9^ ũCkߥ}mUeéšĩBJĨ# $ AL&“!:28w<1Góõjd&…X,Ū•vŠ äŌUcīåR:]ĮÐH80æ,Ą(bÚU{™‹ĐžgáXČÂÓe zĢBƅãđ546rØáĸTĻÝ DĻXõCöčÔÂZúĸÃN`vÏ+mNË✭õÕ[5ˆœĒāÆZēāHb&"r‰˜$KĘäžþOÖ ’‹Lˆ2 ÃĪ—74Ö;ˆ“!7—={öĘËÍO&“K–,IýNũĸ\JŧŲNÜ(•hh@Ä(ŒÐóƒ%sĄ —–k\Þʁ(Ԋ{˧‘(™ĀĀYQHo–îfz 㛒ā EāÍÄȏ ã8…šHGŒģL“@-šĒ-YŨŽA[ŪĢí—NeւgÂŅĸ=Ū0'NH`Đ9ËŋU§^­Áþį Ŋč(}?ŋ·m-wîŠNQųßúūČ0@ûŋ2–ü_Bó!ĸ›$ŋÍÁgTVƒĨßþ§K`ė…Â@„œqå―gpÎ#ZCNĻÅZ)cÆX­ī'’)ã&Ĩz›Xð+ėäU€;Mŋž―z§ĻbĨĨR Ūx<Îûj9įį^[ÖļpŒ;ŦðtÛCPJēÓôY62ԑFŽRĶ q‰Ā/ô!Xr žÞe!Aš]2&S8IFFÆhcũÁPktË 6í Kų·īt„!pœV`rsņŦ/ĻũöÔ! ÁĸpŦæóåIģà HÖb„”›Í`ķ;Lpóo’HaM‚2ģ0Q JRA6 @Ē• œĖ·ČŽ€•åÝ Ęĸ‚ #;z0øÂ*ŽŠĄœBôWw› XrũfjŽÁˆQ^6ýëÜXY…0âXūVŽ@—þ”ĨĀu ƒ1€ĩUXßdgP‹Bāø‡ 0Áĩ1ŌQžļð-\,ðŌVāDŽõÜ*āB8õ ë=ĄĢ0BC$TQäšJđԊ!ZWðMÕ%Ríī֚šššuëÖy;/øÏĀ·XųfƒÜœė†ú"ÄchīąäâlšĄã"ðkbœsoÎH– TJ#ĒģxāVÂ4'X+g4) ;WŒT‘ ā ÓÆ-”IÁœIO 'ØĐÓs&Ccô›~ü!ķd)˜Z(Ŧƒö%PŊíĄGã]WӅOëÎđðĻ!ûúþ§UvÖýøáĢâÛŽč‛īū5`U#ú°ōŊ_.üTžø>ãLöúí|MĮhüĐöämŽŠ—>íp`æßz.Bđ˜>ŅŽŧ›–?Ágm?‚bĸÉ)BíjqųæâûmÏlPægą|ˆ\ĘËՀëŠĐu[Č@þÚ#lI–šā4€č_ mÓ§Øq2sqÞŦâúļ@V@ËÝÔΉÔĩj*0ÔēÄŋišøh>åHČŲV_uū-ĘøëĖũũhxãxŦād—UYmEӜŨ‹QXŊ ž6‰Q8ĐY—:‹M\ ēä5 }ŸĮ.Sô°D"QSSSQQ!„`ĸ1øŽ–1–““ģąĒ€‚ §OėĨ žîēŸ\)Ĩ­&*Rș”\ E!C@ æš.Ž[Ņ"_3^đÆä™[5ԁ ŨZ€ˆĐČΐGSÆtˆ· ™pTf†ļÅĖÉUdÆĢ.ĒÚ’mõ)Љ—›]Đ @˜C#蔯 9\Þd@)ĀĶ đŨ#NUčōũ۟ãŦī4Čý2 ĩ—t†(tEl:ū;B`"ˆÔ/‡Ï Qq…1ˆ4 ØEïŋ0ëmß@đlóĶŧ!øŦUD!0Žß&ŊŧŦTzÞj5D.^øņ6Į]ķ?ŧûm š [ð%(N™Ð°ęÖ#8Ž]ˆ*ŌgÔDų+wãÜ[M·ÂwëÐm„ xð― Ĩųķ(, ōĀÍw€ÜĨšæĨõæ+w‡Ō ôæéķuä>Ōėþ'Ó€0é^#Č,Ō'žMEX7Ü(ëŪ™Y>ųB>k0Ā Ó&b‚&;x7ģÏ(•ūoZųïņ7GJTÓeø.x Ę[ØĒlĻ]*/‡™ŧĐįßVŋ`åōšŧ1BþéãâÉ—Ā á’oėÎGęËŪŅgAđý1DSāLpáz ļ7,§‚€ÆzÞŠ’;Ą&y€ˆÞ4Ė…6īý Ú;p&˜[ŽgČúô雗—gŒ™7ožwĸuG`@ŒëŽ €0ü'Š^:6ĩʔKD0..0d‚qmRðŽ–Čįģ†,sh^zkŸÍ–s` 2âqc­ÖFJ/îĨļSöK*Œ@)89ÎÎS€€äŽĶóŠ6ī…VĐm HķåŊĨ.=ĐP@Ēl#Κ/ļïMĻlĄ§þŲnӒ―vä!ŽÎ2g\cöÖx·öÞ]|ÖFÐËðģ/hïKôyĮ@r‰ļðĖEZÞO?|:{ņ.öü+ˆœv:O}@5ŋïJþڗPØēĪ9íRÛzƒļįn ëÉv7NáŨž?Ô@V+}͟mBþÄø—åXö –›ŽeŸþį4Úq7éC†ņŨ qÅg⒠τúJĝ &`Õ7li†܅?~7Ÿ=—æ•Ų‹oķ]WŠë/Ãu‚ö§N=ĖFþÄ-ü‰ŨAJ{Þ īð|ã#đę sŲ=6wИy –)ę<ȌŸjŧįðëĶðĩĨīīŽÚØu„šāȰüî‹1~˜>~$Ī…ÐÂzXļČöŧļq1…G @r–ŋr#ė0ÅæÂ›Ė]pþÛbÆLŽĻĒ.W'ˆŋ—þ y=ÕíĒoڇ_}1[YAÝ{ékîĨL ØŊ°(œ@ŽþVÜz [XO#ŨįŸMȞŋ]<ö,°\3þģ]/öþĢü•ŊįáÜZ{Ü }Ėhöý›|ÚõX_Ž&ŽÃÚŊÄw@qˆß, a§QÛ؋PŦô“ˆŠų/ŲöÃ(;!îž ßų öÓSĸĩoē/ßÅŊVāŠĢiņŦėģ/å‰ãõˉ°ö ķ8FKØÃŨbC~õ ֒™ōŲĶ'Î}UĖž Ŧëčā+ÕąûāÚŲbĘ―Ðūį,„~§D“φļAėõ—aŧc)Kâ;/B]Ė\~å ČïeÎ?—ĸrŧãŽ)ūŊŌũ d€ÔąÆÝc€~ĘülDŋÃPëc„1.oi3.ôÖÆ6p\TæĨŧýs΄›5k -ÕX˜ģ_ôÏÉË-.n#Ĩ ÃpîÜđąXŒ1ö‹øČE@kEČŽĶ8eīf99ˆĻAˆJNuœ4ļ`å~3ø­ČŦ”j Ģĩ”1 lZĐKéČëÅhcĀPĶķĩŽÜrLë#Â+ė?!į*Œ˜ærŌÞÆĐːk›îÝ'­WÃ$ÛĪ‹Ķuäm+œ<#cÜ'ČÎZb ÁhāīŦA+P.}k\‡_­0“nÅÛNį―K={óÉ·ŌĪLÞĮ⚉īí;ķĀsāŠdÏŋoΟjöÝO\z‰č6\ïĐðŦ·č óÍí'ᜇøõO›‡_ĶėđüėÉĒgO›ý1ŋãóč‹dįˆsŪÆšz(h€^†}fšą{&ĖÉ7SI.ŋú(þņ§ķįÁP―œ}ö…đá>üð&qÕÕfúŸmû§Äc/č}†€tégļžßy= ?E4šßu>ĸÂC,[ŽËr9T/ƒo˜ëî^‘<þ|Øëf=A‰ģ/å{îIŸÏäũžnŊ}Čt/€ŽBđ4Ě+Ūķ%‘SÝīŸy"Ÿr+=t–}s7šûĄļô^~Ðņ&ï[þėŨfÆ%`<Ąz[žĀßHÁ/€Ē öÄ4jĒ9jobjVˆ‰WÐ>Ũšļ8v<9šŠ“øå;tîõú€ýŲ·ðūĸ9ĘŌ 4]Ęæ[Û XÓ_&Åuã™Ü_?t€8ý0ņņvš[•žņU=ãaXųŸqŦ}øVˆĘØ[ošŦnģC?įoūˆûlGųÍīĮ‰æË3.cŦOĨü~þ:wģŲũqÜétÄÕzōõâī‹qþéÔ7ÂųŸC’ģoáoŊRŋŒ·Ëg<ĻokŧtÂ]Ū2‡ ų|EŌLŧšæąïVãš8 Ã%ïąŊsôu3ŲûŨąw^ģÝ2ĕ“éЙf`―8e2ÛfGĘMāĮŊÐŲŌW%&_ĮVŸhûä€ZÏæ#ŌØ_B‹}mÛD8§vƒlŧˆbvŋÃŲ’,=îxâ–úmÏÞýđā5sčųfįá€I Ú,’F‚ýC-9sÍĀ͟PЈ!Š˜Ô‘&°^úÚxõi?­Fd›ād‰KofƒÂ{܆I)CjUT<`ā@ĨTĘd?EaðÜߟ’P\’Q]ŊŽâÉŽžAũ~qޒ› €72UĮmãuŒŨpŽåŽZ@·UŒTÓað§JQhÃÍík••‚Km―rÆĒ0bÎԈŽÕčƒŊŌ ]WLDaHäũČSŋˆ1_ŽķÂiw,rgëĀã’^Ÿ‚;Á/Ã]ÞjĪˆÂ‰ĸ2RÚKĸҚ”ՐŅySöšM[ņj{ŽÞˆV1[AC[‹”I?uŨ`ęėČsôé{ƒn°Cå°g.tęgΚdÛhþîÚí@ģmg€bķßļjfŪ„]v5#:ڃރ0a:T§D­VŪÂgŪåeeļĄŒúĨýČčļËĖöÃøw…īۙĐē3ģčåωÎqų\\TaN:€Š[Øíwd_üÖE=ÜüņËíĻÁļæ}PĩðÆ4þaÝĀūüzĪwĪA hՂZįÛN ėeĻ(2H- ėˆ ^ÆúŒĶŪ°#{ƒíHýïgž ­^ĢŽû˜ūÅ` x gË?E5†J2@'€(―†Ū…ö€ąâþ™ļp–ūh:äŽĮï–ĒŽo‡PŸÄōJjmĄĮ@sė)”Kwe­_äįiS­ãũ.1}2;Ø^ýĀ4TaX ‹žä― ŦŦW-ƒĩËømįĨ‚>6ôĮúFˆ’öĀÍáŧÃåėÓ ÉĩėÎ Xg0­ Ũ}ÂąPõĩíŦO=Ãķ^G-Ö4šZŪpÖK°Ū^\xŽO@Q5ÄZQÛ hÓŅķ.Æú6”™iŧv&Đ\"CIāyĐīݎę‹eÛā· kVáž%ȧáˍؐčU”ĨĄÏ0}ډ”\@E\€þ rŧÚn=€ē8ÔV‚E`žĖaÄmv!īiE•YÔРЄ9mŠ=%ÄîÓ.„’lqĩļz<þP Ŧf\|·Þķ7Xó{ ĩÆš Ž ÐZ!cĖÅ&ë Äž‹SØ$/đŌäA–Ž'bcĒH Á™ŪȐv0MŅšté’H$–/_ūđz@^`IvéŲëÉÚÖ―2{íãÜkÁ&]ˉ 5īK@Éļ’°EŌ Ŧ$[Įĩj*†šŲ ˜bZ™%-ĘŨŊs†ū…žÏrL›ā:ĸ™ī XÁ„V Ņ@0Œ"!˜ÓŽąÖšÔUHãĒŊÓVäÆX/‡æ;ÆČ!ĨÕ֗>‘qgKgđ ÕNÜžĶŽ 0Îa‹ƒšo0āäjŽ(Abųæäíð" NYĶŲ‚’Ú ŠÆkĐg6ŊÁ…@ „ÁՋ0âU°z5 ȅL‰Kįēĩ•TúΞGÃ\“G·ęĒŲÓŦŠĒų]ĄáéU‚{vĨ0Ō@ ĀÃefkÄu•Ð'W.ÃĄ}1P`›hčĀĻ0OäįĀ’yX“Ī|‰ŽÂĩĩhT‹kKĄOŽ_"ŌNĄ+Äõҧ§ÍbíÍ5ã@ōŸŠÎņý'hiDۚ,X[  XWƀBsĀ%f—ÄĖSÅ#é3ö†V-͑—ÛÝ’ą‘ƒ•+ÁZˆ4(]õ/áŨ/ŠqWØACiÛvļ` w Y@ér8p a#OŌ㐗Ÿ|Cíšësn Þ­A#ĩˆ3m! ЀÐļZL8‘ÆÜŦũČ^ÂE[k@8ũˆ ŲÄ,IhßÚdčëĶR ™† ØØāęÂĄÖ|!Į āNŠð8ī,2G_iĮô!M™‹ëūF`Ü,›·ڗPÛLÐDCƒŧ.ā~Ŋũ@øņ ļ6Ú톕IãĀ4@F&‰lą$Úzę^Ē/đžę5ŋÃŽÖÓ ČwÕščācđũÝũ*wZŪ>7ÔJ“ßG*Š|töZÚä+ˆQ2ėÛ·ŊWE(++sÝNÂYr|ēåēaEfĐ đēÃā øģÛKˆĀü@(í:;Œ:EÄ@„ĀkŦPƒĒÜč*ŒŨ #ÉĻ#@ŽH[-Ú9N"ĶýÁˆs…ũĒ:ČīóþâĖÓä|—…ĢEx™.ÁÐ É@úqĢ0dš Œ{ˆā(Ĩ4J[tē֛kū.‰&ØĒ°F7…[‚dCzÅY[HFÔyīéŨ“ßr%î;ˆĖáįS!€uûō\6ĸ^1ĢꗱŽmÕÞ} 9’  ˜y(æJ9iēËØü|}æ(ĘhcýT\t+qm‚8u 2–8äĩ€ĩŸˆĮî‡%+qÕóėÐŅ C íÉREā퀙ŸðV}ėÎ}ÅįēŲņóg ÜŌ“ōŸĶÁĩĻwÛO<4ũ†°Ę~đ9þt\:NNg;g@—ĖvqårÆD=á|ŧoWþį‰ļ` {ïS:ėR›°0Z§ÏØOÓú Þj;ĻÏO…äŪ`_ šÞŒ.ėķ‡ōÛ.ĨíÂÄŨæė€Õð‡ïÁZŽ5D]ÛQË>æÔÝų33qÝÎ*ŧÏ Ô‚ ™DČþō.á *h„žP š–ý°zœLE  ąVfßãÅMŠžjbuīëÉvøþ4ôqqß ;ī7„EæĪąnŅĖšJQ2ž -ŠØė—Ą&“•o„·ßÓŧHŋæŋˆĶ–ĄdšĢ6ŲĘØ}OĢw/į7ÝFâÕ_Ÿz uï,î˜ýŊ5íڂZ/ĶßĻĮVŪT Ōþ‹Ôf 9y4rŪÞ ’Úî"Å  p§pĶ —/ģ=Ï IŒ ũ>æ 7ø '‹EG€^˞Ō\õ„éTĀ–īÃÏãwĩ7'íË^| ŨWÃĒ ÝžķC §Ví<ČþNåf\°@ū=ĀëÅ8―A™ķ°åœ\ëW,‘ō’+ÞS ė&=ïq‹D%“…-[öë7 •Ō&“ÉTĻõŊ^ŸÅE:X‘―ŽsC ōzfåōĪ„ŋrûXÆlSé#ßPū†B†™ĻŽryÁð ]ƒ*7D°QšJTn0@Ā€9·ā)0Ąīp ôM`RJïŽã‚=·ÆšĐUZõœqæŊa.~ÆdŒĀ: ð&=>yįNŌßBšĐŨŦ†ŧ…GC–ķtDívŌSR6"yæē;Ą]!(fNšÖŠBēYæŪ‡čÓwq}#ĩ…LDāaŠĐũÉīMg(ïūĩAcw}Í­T 4uÛAÝ{ûäs ^úØ1ķ[>č!ęūĮØŌ ĻįŨL#ČĪ.#õåÓ)ßB„fï3 ŋ+$ģėž‡á ČXÚĸ<‹…)ŧßđīKD ;J_Ŧ(æŸ D9õfčųč :ðP1ŌlŒî/ 2ö€q–·‘öŌԘ7Øō*ĘčDŲH-vŅ7>_|ŽZPŸŽTÜ.šįķäGbq}ölöûļķJ_r›>"cΚ9ĀjWī  ĪÐ ßßķŽC˜!Ųė åvĶ]>XŦĐįhõØlÎw15é>;°7@‚…ĨëĻÏ%vԎ„Úw#uŨn„ŒBj•Mđýõ57R†EÔĄŊ\Æę;Îģ]są|>~ķĀĖFÛïXšŽÉ(:fž28U$%PQ.e·VW?Ãf€Õĩę§‘Gę>@Q―vŅã†SaūęöųĘëiũÞk3Ą°DOIąZ Ō·ÞAm%Čb}íCÐĶĢŊļÚIĘÛQÝōgöélPŒštĨÍ)·PÏũ!`TÔWÝp'[X ƚŽą9ņļ9c*äCŲQĮÚ!œĖI·RŊ·q}dī –Y”9TOAAžDOžÚĮpÝbüjĐ=šŋËū‰”0ßM;ŋÏæŊ€Ú8äeã“wŠUæČąúē"ŽÍTÜ7&hĮÔ.ŧS~žizAŋs „ ĮŸĸú+/eddÆãņīœį~ĶŽ#%܌Ï:ĨÅ&Đ@â‚q–ÖHdMÛč ŸN(+ŠÂŪÝ{ÎļáÆü‚üTõāH\?éöIb…müŪåšY2vÐá­ōōl z [ í§9ɐ۷v‰iœÖ|‘@ŽÂđUũó$cdÜUQīāúĖŨ%ôE‘7ÖZ­\™ÅQ’UĪÁũn•d‰ÉEImuZ—ņ(ŠœÎ·Ü܇Ę•ŦHx%I'‘ž 9O6é<ÍT…Éðð#Į^5õÚĸĸȰj-ÖręØúv]ĨlŲ<üa-äĩ7C‡@&ük;GÄęuXĶĐGG" Í០Æþ§‚Ōð{Åß į΁1 ŋÎØ&9Ak\Þ*ÝäŨxá.DðÆąČ™kNÐÞØFkƒĖâM/2.EāLČ-+ėÕ§OŠJjājKéųúeįŋ-qģã? ōs0ŨŽ,Yoíģ)t`ÉĸŸŦ!€“ė‚ Æļ1ƀ6íL–bą8cĖÕ"0mï/NË0ųō !ļÏIëŧĈ˜okø_cé ĒųŠY*lo;ĸĢíDĀÛgsÐafĖHČf` ÜCĩIÁĸúĪĸ48†ŋĨöðoĻp?*ho{w"ēð ýoÄYü;ƒ%ëÃÐŋv1ÎþĄ]ýŋŪpæķā߄kČAēĻ cø›ÃŨú'ΚAýģŘŽÕ4—ïŸ œļœQœ ēVEŠ!đ5%Ũ^ęÉc€ šzþÉ03#ŦGžÆlâãĶ ĩBtðđØ0%•“ 6†õI™Č† ĶA‘þŨ\ē6Š"cČŅqyŠ øâŠ”ŌKôú’ŦŌÚw óÝĀũŽÎR€ģ &cđ+&x3o‹N?ŒČ8Đ_‹ĮÉ îÁĸ&LČÞz­mŒp}4?ŧ5ĸd|!Ð Ē$ÔlÄ Ū[ >ú,ā—oā·k@pðЉôI9þs—úîkļī8ƒæāĀf―Í—þJfĸ;ՕÆ5~gΒ €ėoėó—YoÏYšÖVTTÕ'ĸ―Ąó†ĩË^yoN­ÁŌåß~8gQôïip!ø?)™ÛšƒLueEmĻņŋ4ÔšœËĘ āBDJyåtZĮhĢ]ë>"ĨgʀQ6Yp3kŽõúŲŽ:ÕŪ}ûķmÛ644$“ÉTbëÜü- KôĖYÁÚå•krgîuPŅ1G·;ņ°ķGïÜrB‘Ÿ°Éŋ‘ä"ü:ČĨŸœ3æ”üZXã”tÂЭ "sąÕ_+0ÔQNÁ˕z ÖX-Īd‚+Ĩg\†ĖsÓF nĩMlÚAYcáõą‹ ëØÍSØŌzl\*'\Ā*áߕöœ}õǘzðŋĮï― ø‹ÓųsËAˆôIĢj~ÓöCöO\ŠŠäSÏįŊ.)~öšDžŸ―ü—!֖ã†J øM0õKø-SXuˆ°ÅÁ6.ä7ÝÃV&~yy@Xķëþ:#P=ę:öyŲß ĩLˆï_đuüīD ðo‡ZIŊÞ1ýÅYó€ņŋ―ÏK/ŒðäÔKyë[āþUp)ëæ}|í=Om đėģzâõĘgîQUķķž:ņæÚõUĨkËk!x ãŠę‘SŸý`}ĸĄ] Œsc,#Īp.šŽæ ™ ŧČļÐJïĩeŒĸƒ€œlŦgô˜ú˜įNÅŲ”·ÖڇZOaøgŽ‚CYyōË·+ÛŅØGÜsįðëÏé~Á1%gžÖiüäž3fô―ë”gķŠ4Á+C'ĩ5d­2d}"LčˆŠ,q.ĨÛ°Ā{‚1N™7rïMāĻTˆŒsæ-Íļ+>m8—ū Č5!íVS@åZâ˜ā\ rķ›―Üu€‰ LúíŸj%QØDh+‰!e”čģÏ <–*$ãĻôŽIûÎøķĄfņˆĀÚĶÐ@ĘPŊ6ĩ"Ą&H @„ŽVüīCj'!!ŅčV{øOTú0ôTúŸāĸ “ ZÅ CĶuT"åŪ  Ī˜"§DåDÜBÞƒžûˆ üė-hRhl @އ{‘š! !LĶ%@Ôt:ôãE‹šÞ ›nŽgRbPPŌ}ŒˆēĐįwŸÅ_ûĀ―ÅĒ&A `Ý 2X îT]û&ĢŧŽ:äŽCZĨ-2Ô* #ãC œŽI&“ĄLϒAĶDwĒôĮݖK/Ēd"Œl~,#[RĪųčcOŲk›n`°ƒ( ĩDt;3•úÛÕʈ 9Ț0ušH‘oZĖeZ‹ ÁevĀ›IÆ9ô;ƒ›ÞY•úS[āî™DMɔqíCúŲņãŊœĩĀ:‰Paęģšąô΀Ėj: ÎpÎģS.žōþzBØėÕ!sö<âļúīP‘&†īiP†ņŋ$Ôú^&%VHé4^•ĩq"PZ3ĮOKâœ)§‚ ‚“Õ–,s aÚĐŦīn]ėïŅŌĨKĨ;æÏ IôũfXū!ü杚ËvžęÉCŪ\Ō~Ž’ĖŪGu8õŌîÓÚĊ}īEøí—áž>ũ|öÏé“Ý/ēŅ/ĘķŒqg­%pIškn3œs$°–8įŽ°Ŧ2wÏH+…„Üąl)Ýkƌmēdgčvv·ÐEb`Č<ý—ČsÉ’Im0Ît!l!pbï<*Û/8j—āē™ļFcr™<ódyãÁØQÁyŨa•†>æbī?õ8&~ü€œzƒœvN°ŨyĮãĀ$Ôý(Ķđ·œ4kuÓRFĢļý:ņŌ·0þäeōÚû!ˆģ9ÏĘ_Âåßō_Eâ€Í`?žTÏßš18vLpāüŦ%‹áģäø#ƒ#ĮČûžŧųģĶ^Üsi°ßŪÁeįąE 3@ģŨî”'ïū?ë/€žĐ˜ãÜ·å9c‚Cwũ―ˆ•køįŊã7įÎ*4ÎGžíÞšũ9`1X3W\°GpÄ~rō=XĨ‰ņŸŪMWņ‡&ŽŽØü˜1ó’`ėîÁŅ{ˆGf›”Ū/oėŧœ~ģxpFpā6Á° f…ž~’˜158ltpŌ|I91–f—%ŦşN ŽÞ+8ų4ķŠ–­ų”?õ*ŋųü`ę#ˆÄ_š-8~āøƒųœĨ ĪûˆdïßLū1,ä7_%ž™ ņ42ūjއÏÎ]&%ūwËųWNŸqâqŽšëÃoý DŜ7Ï=ę€ÝöÚë€CŪY§Á hãü &ßôÕęDLTÍ8éôŧ_œHœõøÕŧė2ú°Ã}hîŌÆ &đ™ũæsģn ĘĨWNđjúĩŨīãĻݎ>įëõ 1i?|rÚč1;}Ü᧏=ęŅũ"įþZb1óŅã·ļŨžcÆė>é‰ŲBúaaþ˜ā‚Ŋ_øÞĐûíĩë{0nB™•ŋxõĖCŲgŸÝ:sâį‹Ĩ4oÞxޕŨßpÜŅûķį“—W/xýžwßų’ÓŊœųDƒē_>5㠃öÚuŨĢĸ`Ќ‰o^ūõÜŦnšpō1ۍ:ņáũuÝũŨÞöėŦOÞtøi3VŨ*ÆP5~ōéߔŨaÊû&ž6uúÕ{íūÓîûœđ`}$ûoĩäé^āœÉS0ĘžķrņYž3ņĩÓNôĨXk-ūJ!8;Xkˆ ÎæīėŅģgŠĮ+å՘2Ûž Öž\û7gs†vîĮe{{Úö‡Ä9ƒ&Ô$’Ĩ ĩЄūđCÎí|I.ÏIgxš*—VÕmĮ’Ėö]ēzĩZZēŋĻ*X ãzđ~â2X‚Ā_#8gfģŲ ŨZ‹Ū€ë<) ó>@Ú ‹Bå‰ ‚3ãú ãˆčÉ RHk,y[ kėc‹!Įē9ââ)°ÓdõÐĢīá%qí=Āŋ~+ŠôīÛiå‹|^ÖŅqŲũ kVē§ï„ĒŅæŠqøÖmŽZó;Ŋ`ËÚëGžĢÏóĮ?Ádĩj`Ÿ~å+ņƒįØó°†*üøŠe‚Úˆ+–āßZ’Ū[åVOú;Äð?MÁŠ21årluļūí|č>kmšJĀ›ózūđý}ÂĄ”Kd8–ΖSïĨãŅÄïļ“ HŪ/ÁaŨë['ģį§ąqÛŦ7írĪžt=åŪ—^ŒÃŊŨ·LbÏO+7ˆ™—ēŠQúÁ§ė€"Ļ­þébÁŸžÁĸü𙸀šįI;ī{įNþė23õ9=õ|vë1k˜öö‹ßA_=Ÿš‰}*ôĖk`á'|þJd?y‰- ôŸĩY øM v·WĀĶã|*õm/ہV<ņĩazwģ_ŪÎ= ū)n~ՌØķ#ŋþ6,ŨĀČR·Ū0ûa\T •‹øû/Û]Āę_ÜÉš ßĖĸķ‘mX>ëū§?;čü™—”yĸÍ/'“k.?áœ5ycž~á…ûï<Ą€“ĸÚĘeß-­ŦšW9geTS1ïi·ŋrĘīûîžqE— —―.]0oUY5ĒžũęÃÏ}žqÚĸÜŨąėƒ·>_°á›7§ÞüôĐWß{ĸŸŪĻZųŲ+ŒŌ­úïõČ+oÜwî_žôŌĘß*Å#S—^}æ9õÃÆ>ýâK7Nš4?đðâ/0―ÏzõÕgö͘{îŲ3ë(ķaéûũ?;gėÅ7Žß“Ý;óđĖūĢÐqĸ ŊwúAu _wÅó'ÞôÂ-ã‡Þ9õęõZ„ĨóŸšûŪŸwãÄÃf=tãúxŸ“Gũí;ôĻÛĶÞ.[ĪSwHŽ]ôՆuŠóč/ģž{yqæô;ĸÔŨ|õ› PČĸŠŽÖåĖ‘,h•憑%íféޒ_āSZų‚wģ bÞՑ,"9hÝšu·n݌1ÞŨ7ŪúPëi%zü­bāßTĩ5%·ïNVģZŌšoϘ{Ę^ũxņÓŨ5ę†ŒÚŧ耄MbumĪģ{tę ģ]Ā70k4s=ZŸwãQĪČZą ÜÕD)Ĩ―ē­KÞÝÞÂÅ_DðĢ3dürąE°éܖGQĪ\_ĪŌŦŲZĒ-Ö­ĩ~tßNŸ6†Zõ2Gí‹ucĨŽ}õų“mŋÞTā/# ØZŧÓÅjÜņvðķ$ģÐÔēŊãōwøųĮēÕëŲš āé‚FØíw‚eóųŧoĄjG%ų;ÏģÕËí6Ûģ€ėïH1du3'^nöĩû å Xö#~ũ|ųŋėRhHēĩëđ‹á€ï―dmtēCũĶö1ˆ+~SÆ:—ßúb"ãWų7ņņM-ž>•_5ë,؀ÚäPq+ÛąÔŪFÝôV#RMÖEöč}l›"{ÜŅÔĐÓ+ā 0Ĕ„Bï“ĖNƒĐm fāę5öļĢĖÐvÐ>ī Ã5K ‘Ī!éӎĩýzPĸ=õ%įØ!ĢĻĪ3ĻPen§/ŧĀöëkŲ•JŦQ‡°j”Íæ—ÃÞüŠP€,„Ū] e*ĘÁ5+`Í2~ûđüĄ§ąŠü“—:6CóųĮ/ó7Ãi`3ð‹Ĩ!ũoUfų‡NļöĻ†tíڃXuCõĘoēb'M8ŧsQˎ]šf"5}BHæH5‚ Áíšßþ˜ąÛvÛ kÛsÔ.fDøD˜:Ûq§é·\ŧíĀa#JšÄtbÝÂ?fï=jŧa­ŧŽÓop͊č§ÕŪ7|tņY'\ôø;Q<#Ž@ŋŅSW_šdÎųGŸpB—6­J:•ČŠesóû{Åūy%œpT]øqi‚@ķ:âōk5ĪKįn֖YYTÜ5̰mĮķ- ę*W~WŧæÞ Žŋė–ˆošÞÔČĢŊ:ĸˆ] Ųcí{õČČiŅĐC+!šN€N˄QÐ~ÐUW_2jČĀN‹ĢZ ˆĸl1 âcÜiē wpÂ]>,Ē3ãž(eœ―Ū‹đÚgÄÞ―Ņ5}KgĄhûôíË9Ođ7Ķ1ūÁ š~ĩŒ€åfõŌäÛėŌą  4ĄZUÞđbfĐý>ÞÃÞøō-m‹JÎÛé(_ÚŧøÐũ+ßĻēÕ –•īoÓē82šŌ]͎oŒĄ5Ę7Qx­ï@Fk!…ŋÂ0qÁŌ&äd…Kõ•VÖÆļwr$KBpï….„d ÉĨŪہEač:ŽCg”ÚäëN[Š_XPąˆ­Mw‰Ŧåƒð‰ŧ…߀Ę P \§;dQŧ}ô'SŒ#Étg’ÖÔqĘļTÜðķ9âIÛęvqŕ4ręYˆë,üôˋĢŠŠZAŲ:ddA‹BÚë,}ü2x B@@ ZdáÜeĻÖ/€ €K-RĪ&;Ž%XAYŽŒƒB{xJ a€CČeŊ‡XZ)HũÖØ+Íþýe„LWâę2Āïū†Š:@Öt ó`ŲwŽ:īų „ÂÅKQŲr(­…x` °I0aZĨ›ÛˆŊ‚Ę€§ö)}ƒ?QŸbÐHņiï‘MûÉۘÆ"23ߊŠKŨÔanû–9Dô‹{Ë,f /b" ™™—Û~?oåá}JņÓî:Ų@ĩĨŦ7”UwdĨČPåaĻumékKów{yhĀn,@Ôh “)Į”ðŦukŨT­|å‡åÃvžž—ąŠϏ;įŅÓîēgÅ3WžYiˆßø 3ósƒęÅËV@Ÿ~&Rbąš%ßþP;bDaÅęųVåÅ82‚,Žn‘9DL"ŲXQÚčBk“Û~üuũîÐ5ÃČ‘jŽŲ)• Įų0‰„^C‚Ad~yqÎļÓAvvčŋaYŒˆŽsl!}; ߥeŒ&WÕ&k!§ĸ‘{Ũ(ĨšúO­1&Ŋ ŋ_ŋþĐ/ßéå;ĸY3 ûu=DÆ š,’,ŦG·!Ð ójŋþKí7Y<+ŧEvë~…“Þ{x}C58īŽ{,[’Ø―Õn§lp§œ–ā'óōD6XcÉü”ÏŊuäĘÆA@ ĄÏg―+%ãĖ[øĢ―SY"·ģÖįģ ‘ĨôČóÕÞ N>9cB — wÁÚhƒ[HċšėHÝ;ˆIg‹›&ðį–Ų―NĨ DυGÉ7ÔlÃ1ÖÁJH(ÏaĮd™}æé°ú)qį]âÁ”āÞÜôJ:iEv›N@1Úw˜ūÔ&mŋ#Ixv|ée.@Ð"Ýü[Ä ËÅĖÉâ–ÉâÁ‡éāĢlį~æī}Ø{7ōûĸ,îŧ‹ĸĨĐ3ĖŧãQxRŽ'gNdĨUˆŠšîj{wæ7Oäþ™?ųgL2·D:1„ý؃WóĮîįĖdëĄ_üčyïý;Dïą{ĀŋuŦ’vĮÝŲS7ɉ—ˆ;ŸÆrEˆMqH˜”Þ`Álqéy⹋ä+sėvG‚ũå”)bŌfw0ÛB2 €”ō·Q;ĩõޟ+îŋHÜZĐ#ÞØĻÜÎÚ$“– Û€mėŠŨĶN{i]Ĩf !­ÕĮ”ķ@6™hôÞö:ēĄ&øßŸ0aü‚XðÖŊ/Yž()Ĩ3ÁŌāč^J)í8 BHK~ĩĨāâŽ38HÁ)ˆ§@ÖĨ·%:tðĄZëü1e&FŪoĸ';[2v#j2Š īí“)Bāú… 9ĩY3šĻ0#šðäęûV‡k0Æ1JP͒ŠÃ†ïÛ6·ĨŧÅ)ēu؃o~ôÎéî?h—€qpč‘Ý{tËÝwnĩįēÄēŌppšsĶ’jßO é" 'hI)ý”߉3ÏNvŦy^^–‰Ķ}…oÛrfŌQæÐbÏĮu"ÃÝū霗đŲ\†}ûõģËŪQ5WTUUK)ōóó‰þ)Čŧë* -ėØņf~`° •ī!ĐŨ0ęÞ 2b~ƒērĐĸęPLE=hPOh‘"›Œ ö­Đó`;lÂō‹Đwj™þB,Qũmi§=mïNŨvÚ͎ !Ŋ- ކZķLoQ§þ4 3äÆĀ%õÝÞî0Óî'č}ũRÔ?@ŧĢ1P܉zõΜ8Y(îm‡‡˜ī;g>ÐčMy-ė>ŧoŌÁē1ęԝ:v‚.ĻwČČĪwąý[ĄâÔĶuíN]‡Ņ°Ū›^/éEcvąýšÞęÖŨ߁zīxK{Ävĸ]ĻMŦteŅh՝ķߕ2v°ýPŸAvŧÞ`5īĪÏ:›ÚgAž€úĒârĻčT2F=ûSŊąĘė^ûAAŽÝï3z0ˆ,ę7:ķĪķ]ėžĐ0Č,Īž}Ļe ę2ˆZīÄXKęßÓnŋÄ@fQŨ^ÔđõLÝÛCVēŨžĩ‡žc‡õ@`õkŋ~ûóĒãčoĩõ6ŊļũðÁÝ[fķî6pPïî™"§Mm·ŌūuaßÝwÛfpąĶĖŽÝúöîÖĄKïúõ,hY<|ø€žüĖa;|úĐGnÓ§{ëķÝwÜuûœ#qøč3Ï8eXŸîđY%―účß+ŋ uĸýzwiŽĪŨ€þýzķ(đÃzõĩ݈_ŋÝbøūÛũ-kÉÚX^‡mG Ę͊ßõðãv퓟_ÜŦ{ý”ķh]’:\ëÂFÄdŊáŧéRņŽ=úöíŲ{Û―wЊ%({‡CÎ>õčm˜VÅÝęÝ-?Cä·ëđi8E9먧WvA‹ķŧôÜé]‹sEVįn―šunߊ]ę&t+*ˆËŒÂ#ķ)i[Ôē}Ïá}{tïÝ>#`@@,Öąû >ÝÛæææuí7ž{Įvq”ô2d`‚lIDĸ!Ēēēōōrą[€˜{Ņã^핮Žė4 ŽŅŽÝęŨdhŠHĢ•öj­ÎՑīDZGN›PŒÚiįO>åëŊŋ^ąb…q0›―lÃ0L$Đß:ŒAÍԜĨĀ,rzHËĖlîo—øÍ+å-Ãöß\ũ>4€.øîøE ‹9r!ą|UrîÏ_øä=·‡g}>ö‹6Ny=/3þ ĸlã<9„?ĻõŦx\ļ+"dˆ€Md\ÖFÎ[AHéŌYÃīÖĀžbŽą„„(Ð;’ų@lĮôU‘ēNåV°tWsÆZëDcÃĄ‡9íš-́ÐLß@ŧž’AāE 6‹!@ÓĶßbÂqÛÍÏDNf­ú™—ĀŲO,{åÔ˜pÓķč§ ! h iļģ3Ÿ„A4ÉčLģ=ŞKŋÅļSNðî;ĄÓ1pdųæb^ņ@Ęī üÚ[˜vözÍĮՔV{)€ĀՎÉýéÎîœ1ÝAļӗpw–~(ÆĸđėF;0Œųi­ÁŌO·ŅhÐMęþŦņÛiō˜!Ājā‚Í~VL~]ß}Ģí”Ë€>~pÜĘ.įŋS7Õt3—’ŲP.cĖ*eėĶW…Ąg“ĘŅ‘"’b`”6–ðâ^ŒvŊČ@pæÝóŽ{Eø-’kĨÜĩĮk–^tþÕÉÜP=įÚÁßvÅ âĖĶ@ƒ›NĮÐņpXĨ,JŨ„\8‘ÝžÆsxŽcZ`A0 Eît2†6R†˜’Qid"§Aj‰ĀuzÎ )Üވ\\…Ą? ‰Beisã°ŦÝũXBHīZiûß =þüsÞ}ë­ #ĮY@ÆÜ„@3ĐHmNKĒ•{Q;GB/ƒíjũˆ\ĘC[ÜĶMŠÖŠŒCÂaSo]ĻSՂĖE„ĶyĻeušū~ąīgį―ŋ:ëvhBŌ&.œwŌōÆN1+ËĢo_-›~ôįlw8|Sū|ė“į>먑‡mWÜĘ+ĩąyAî5Ë.þžjV*Ô―L—]UŊÁkšļaäú/xڐ<ˆ@LéÐĨĨŒœoãFéŸÎŽ“éâ ĩWŪáŌ’ŨýB!ĨnŠBpÎCGũ0FzØSĶ^ó›Ąöŋ[:ąĐÜš en G,ŽZ‡ĩÜv(F@Ā„dĪĩĄĸ_dV­Y>þĒÕ&ĢÅČíGīĖ”ÚXøC◡vK8æ —ÕY؜ĩ! C.ĨŠīOú† ’E`äúĨĄ_B@`Îf&Œ„”ĨeĨÕ55>Olj•RaúPKĘTÉz›aņŊVČmhmhķïÍ MĻl“ÆĒ&pķļēšÐŧ°hāðœ wÜčóķ;ôrpxdÍÝģ+?Í ōŦõ†8§‰đŪēNĖšĪ“ųĮ5㈜kļŦ†Ĩ-)O7pĩ€Ėđāhíe ˆljîÄ―ž@ļ/2˜īažēl}_-Ž*ō)?ŊŊOęĸLzŪā ™aéōÛ/;xĮGsÕã Ô­úōĒãŽØgÏ]v?kúÂjķþëįïgVjįëî{3žUĪëÞ―óĒ=wßýŊxįóeņŒŒdՏžóÚÆKŋ}kÂ1';ð°s/xGŨ­þӄCũÜwÏ#OžrŲFąĨŸ?wäŪŧîđŨ^ãĶÝ<ïŦŨ~wÖģįœxýCïnøāūé{ïūį{íqþ ÏÖ+ŲļvÎMWœrÞÅ'ŸuĘŲĶ]zïĮ?ČxÜløęäCŪ]žQKÁlÅVüB-ĒŨˆq6Ü‹!` ­öēčûšųÖQëÓRįōMN$Û&Ą5Â0Ec š€M`œ{!DCĪ7ýϐTŌŠÐšĪ5š€ĻķąČÁ§–~IēĘüyĻ5d  Ma$_&ðp­#”H„ŒqÏōbŒ"ðŽëBŠ  ‘ąiį4J?ŠŒ3d@d”ö„:/ŅëIš>Ag\ø–8ðjF@nYO‚ÖÞ’Ö]ÜgĮu}ėh™.Íģ…ï=vÅ _\zï ·LŲWTUgšĄbþüŋTknåģ?šÕéäqÓĸ4ō;ŊyjÃvÏūôôāŒï|ųģĘe_\~ÎĨmŋøų_œtöņ{ØPĮC'Ï<ãȝ―s߄)Ï_tóģ/üųŌõO_ïëßĢI|ņök‹Zė=yÆÔĄ9Տ?þzdõŽŨž^Q’Ņ.?ncc ~­ØŠßĢc.‘āB:ąZpuLWpīāäŠĀIzåA"2ĘpÎĨū ßŊĪĩk߁3QSS•L&R bõõĐ.âæģBŽ}ĸ?9•B됀H%-šxh99\Vąī1`―î"GˆÍä― ,ĩ›3_"­Œb@. .OœÝÕ§eÎãņŒ>ýû7&RuŽZã–é"íÝ'! C ðæļZ+ŊÆëˆ: ąE‘ožĘ)ØzmÁóëĒ(WĖÕŪ3ŒqHi œ# ą…B-Q,·Uë.qÖĶcQ~ÎúDcŧa;^rę~Y ÞčØž1•ūųā­“Ëíęqđ-S;Ë6Š[ä$“ÔēÕ+âÛîžĮāv™°û|%0ÎP4$’GŒ>éČ―ÛPų’ę į}}ʑoXĩjŸÅšÅsVu›päÁíŠQk0XÔ9žŅĶcŦÜØGŦÖfízðÞÛuˆAË1û?þ—Õ‹Ýâ9―G^~Îą=ŠXņÁco{ų‡_|ýÕo>ëžūÞgdķČEka+ķâwjˆ( C,$—įōÓĘđ3į#k`l 8XŨG:tøˆC;<Õ·ŧzÕĘ5kÖXK‘R)EpëĀžÕ.štX#jēĀ‚dĢ1FLh€o7ŽÚÔæ,Á@0)Q5ú\ĐĄ]fŽŋr Žëa#‚ĩ`Ą1ؘ#r"E*"äD^GÐh*Ę-ØwėĄa”Pa*°Ī õ)‚`˜LĶ8ĕ+ęjkS/:‡ī( Cķ0ĩᚷĩŠ,C4ZkđƒóŨĩčj“Ž_ĮđtŠ4 ˜”2Š"GPöķŧķˆL]}CTž@Ũ…FJŧV6‹\WßqŅqŸ‰‰Ü8|ÂąĮÕ3 ĖĶ/š< üŨ‡qnܰĶ\AáÆÅß/øĄßˆ iņÐÝKŌ*āóÝdA8keĨ•ŸãúŸCdÚé "‘ĸ béúõ?,]ZXX˜RMdŒÅbąÔvJJŅo;WDJÅÜUŦW-XīhųĘUu5ŽŪžŒQF!ACB3â`‰2 o([―Šl}aAĄ”‚1&ĨĖ‘9”Ķ9$ (ęÞĒ­Ö:‘LęĪ^QąĒŽš4K44–VUT%jBÕÖĨþŌ!7„þaÔ`MD ”óRûđî―"pūÖ)ĸˆâââÆÆÆ–.ÎÏ/ČĘÎb)ĪjņÔ[ëÖŪųþŧï‚X 8ũJõœ kˆœz—ë;vÓKĐ}œR8—B%#c-ā–Kj1ãč)ũwøôólŽ%Cũ›rÃv&bG\8å†ķ-2Ošŋxį/j3;{Ð>óV7‰uÕí>ų<_rŸđm[öÛãņĮŠ>ģ  Í ŪAnqfváESfīÍ1bčSŪ]ā;Œ9øá7‹?œ―\ƒčÓŪÆ[;áþÎ#Þ[W•ČiÛĩ07ũ˜Ŧ(ųė‹ly―FÝüÄ]ïž?§úï}ÖÎÃŧg'6ŽžhrIÛ|ŌˆŲŒÜ‚ö={wŌŪĻ#ˆÞv§…ņ­qv+þĄŅ·āx+k)éHŒI$īMÞ°īfvXkkjkæÏŸ7pР”Ē‚Ũ”itÓpāA°)XuëÚ­oŸūœó( ËęŦ?-[ōÁŌo–}ũQeļąŅ„1Œ‰„Čā5ՍŊ˜·W|DB WG–―ģúÍŪú2‹sCИ°LÄ 3rÃ(ŠkĻO4$WTŽŠ­i€ĪŪŦŦ]_Y^ÖįˆĻžūnc}"ÎIs Ýâj°\ˆ˜”2žnˆô—ęcšļŒąÖ­‹öÜkïļƒ”’9Ī2ô;oŋ@x… đˆqc­ņ―\ąĀsĩĢûKGŲÁ8óKpÜeҌÂ%ĘmÕųāÚiĨ aĸĒE”ÕŠßā6FEïļũa]Ņi3ôH‘R˜áwÞL"m }ÏaGõát"ŽÖ6§ßābĢ"jŅąkĶ‹šŠtŦ.ĢŽę6 Š”V(rÆės("Ķ)ęE]>ŽûĶ#GÉŒĒž‡Ó―ej™[Üop;Ģ"K(yôîŊöØõÜKĪ -mudķ†Ú­ø#dĩ^DÕŊáA@ŪëōŲīĄ“Į†2B ÎđŽīj!kuĻ-锊Ũƍ)7rpĀf°Ö:…l^ØĨEvÎqýv?úˆ•Õ^ZðüWĨß~ķō‹ŠÆZ 20ƒUŦƏ—ÏßĐC_&x ƒļŒĖ‰kîĀČPU―ą…đ𠍍•ĩÕÕĩ5‹+–UŨ7ˆÜĒv-Û )ēyf͌ïØvŧ’Åq‘cäB"˜,`ų}lĸŒ Č@ŠMRrÎ}0ĩÖŠfÐZG‰DÂŦÍ~ņųėĨ‹Æb5]Ũ’uú;ĖÎ8ú™āRke­áČP0åQ’ wķŽŅIĢÁAyŲNēĘˆú·š@~įæpv>?‰Ōũ…Ú&šŸ—‰ÃÐüꑭVĄVÍÝĻŌ`"=ōōŦF·(ĄHSúRC؊­øcÔjÁĨ4H’ KĪTÄ\ðĩ–ŽņüŪ&.$šŦ@Īį,āņŠŠōyóæ•”tbŒý"ÔþâOĸJ*JĶŌĪ ;īi{ƐSuãŠę•/|ĸüãßŋ\7V'W-ŸW:h7âLĘ ÄšeöÚū`‡Ïj>ß0ã)ێ-ĘČOũēękŦÖÍZĸđÕâîSŊÛŧëČü•›#óķÆxАcu9ïŊ’ÆŊ4–Ęū―·ĸÝ\šĄY{ųe―ÔšßwßÍM&]ląA ,‘6†;—ÚZD`\Ŧ“Áĩ6*ŌR dLŧö8øƒ‚,Äšöč ÖlMc·âYŦĩ@ČXĪ-œs·%7 –<…(1ŽÞĀ…##ĨtŅĖrĮ4MFĄ5fŲŌ%UU•­Zĩ"ĒæķųFó?°T7đÉ3ÔÚtÉé|ņöベŸÝ“Û6þåŌo?ûqÁnąÁBŠĖx~ĖņɆ†ōšÕ5ĩ_ŽĸbaåōÚ ;īũĻšĘš­ČÁZō i1j84ßvĢĢÍ|Üæïú )·īEóŋŨÎÅ3$"xž Øæ†D°Ū=ÃKJkü-œG.Ÿečž}2ZÃVlÅ”-ÆœĖ‹qÚ+ĖģH#K„€ë€-sd°ÂųQk-YÁĨ›€"įbõŠU˖-€ßæãēôoŊ2žL&]˜ģaĢ5°MŧaYˆ|ÉDÃ+ŸŋVZđ1ՆÕļ‰šč“;djŨÛŊßëöĸ9íæˆ•UVÔÖÔ­ŪZõAé'LáÉC…‘VÍ*€ĖŸĸZá·ŋú į<•ĸÎų|vmM ræÝ*—ņ}HGYFDKÄ8'kždƒģ:Ũ`œŒ8ÁVlÅVüņēZŋXãt­ļč ąŒ)Ģ üŠ5CN‰Ž`œ˜ cĩˆI|cEų‚yó˜ZĶ€_­4//ø?S‘4ĩÜE‘ĩ6ÔŅČ#†õĩęëėNđsyã‡-/ßõčžėœXBôjQŌŦeIMM͆ŠōĘŠęÕå+Ÿ^ņˊęÕĢ:ÝytCMz-ŪųÜßŋâá Į›ÕĘ7s+~ą—1Ö<ĸMųĪ-Yē]ā$ßņĖφ-YâRXwëœyžPJ#ú@ëjĩčj’6RÂVlÅVüŅäfŽą&RāĶôZi?YFdQ’ =ŌŧđÃ' ~čõÕĩR"đR‘?ĸŧuëÖI)~aĸFÝv“Rļ֙ņĖOüäÖOoɉåėÖ}LŒß5ãģÅoMzãūų+—%IĐdĶ*ž5õu*K?_3ûĄe|_ĩ,fä˜ÎÛĮ)Ūtô‹PþŨ1›ð+Ŋ8üj†›ęīýzΗUU‘3tKˆĖI‰ƒ%oNæė€ž‹Ÿ$ 5yęÏss*V[ą[ņ ĩžo.Ÿõ$1áP,ÎxSm1@m―(8#/m•0ŨۏœËÆú†E F‘‹DMøŧÛÕÕÕAăŒō†ō”įōĻn;dđ:2\ēÖÝ2—UÏ:ûđ+ïxëÁĩ7č(J­§ÕÔÕÍ]ũí=KXRģ5tĖ)>lāĸcïKí:ŦŧŨð<ûܛ;†8#$qdC'$”ĶL>€„АhĨ ņUzĸÄûŠWĨ”öC?UUĐZĩTT Uj!MHBĮđņĮā `ûzÎėŧũģžĩšïZĮpåã\ŌÏwĸßėģÏpÏ‡uÖ^ë7|Ķ/Á€0Y%'jëj?Ï{“™ũíÛũž]ņ†ĩBWg! !ŽÝŠøåu]§nÉČžÏæá/*ļõ8 °1ðjcÍUΐóEsvÓ5B äŪīČ)yOĶ`ĢÕ* 9Cg”šfķ?{ßýũoÜļQUW ÞĻ―·ŨjKŧõš­ï―â=Råݗ―ëæ Ũ?~ęLųŒč[ßváÉųŲ?ûÁ_§·^ņ§[?y|îäĄc‡e.h/Xh_7Đ_Úō…wŽŋöāėÁÉiĀ98g_gŽÉ6vĨg 3Ÿ~ûGOœ:AD“ģāIÄ*ƒ…7zb?Ĩ}ņ…Ý)s|Ģ4đņ‚<94P@ˆO# ―RĘčá )qNYŦ†ŠŽÉMŸÏ 0`­•Úą@ÓdNĐ.C Ī”*ԟGĒĻ.ŨĶ3ãŪPŦ˜anšÞŨëų™™R Ū9ވŠÁCˆŅD[ÚÍŨ―ĸ–Ëo)mąĒgZ 4#ÛģóŲm/í98{øā‘Ų“ŊŋūÎ6ܔoýŌ_xëÔĐ21luŽ:Løƒ… <ũÜķųųŨCÍΙĨÜdO°ąŠĘiœŲNË@7Ļ†ŨÜįwEƎéknV;`Ā0@p;ðvÝĨˆŠ5M6pÆĻAJIjUģDĖ™―)Ŧ)gFV§—’TAFÐjZõû~ÞģūnŧíķZk$„sηmrˆ .]øŅũ<ļý{x4­h‹V7lœ~íĩßøáß~öÖõ~(―WWĘE]øÉŧ? įÎ{0y Ž•LƒIîÁþýûũîÚÝu]r†WīēddŪbõ*mĐZSĘÚMÝd=øžnø‹ÕÍjcö k ]­›^'Ŋģ:NÁrWŠ&'ÐZ1đ` L3§˜”Z )qŠāGĒąĀúuÍ-ēb9617˜Žŋ}ĩeæļÉĀ[6―ĸbÕNŽh·dēdë/>phæožüÞSŊĖžzęWŋšûõ•WmÜð–uŅ•ŸW1Ŋ<É+˜|Øä]Dī°ļøäϞ8qü8câœ"Ėœ3 ř†Î™ĖĐņžG œƒģ!€HūSՔ8F1°Ö0`ĀPjÍ,U˅LXšF_†ˆQzUĨÖJČąÅŠRíÄŪč7NžXnY‡}ŪÔΙ™~›ýō›Ē"xc;ÂŨĶ-Ýû7mūí꛹ĨDEęŌR-EG—4KiöŲđgþköá§O=~ÝĶ+ījôŠŋS‰?ß<ˆöŋīoïî]Æėœ-ĸ"A&UQąl ÏÉäYÂZã[‡Üh\{8ï8ü"!`Ķ0`ĀZÄāė%ĘL9ĨŠÚu-##ļ• 5áfínŠx–R~5øN.fU­šs2W:ėßŋïÅ_žúęŦ%HļŽÕgĶf֋šĶЎõŊĸØûzîĨÝĩ3$4(ˆTCŠÜúĄOmųTO›ÜqÎ?R˜ĀʑÂĘēۋ†ŸzōÉ#GfSNZAD,5ˆDÚÔTӕÂHūghJ ÍÄCsӘĐvâK3V Ĩ° „5‰C89Fš"hUDvQƒˆŨŽ*ķ ˆ:‹ˆ)3"ÄP.’ĘĐV­ĩ2Ĩ*2óÜķ^ÖÅĖļ*Vķ·J†Ž…Ó‹mþðš ÖkUˆƒj*š‘ŋō‰/wmwNy%"X‰ "ÄęB†@áLĶāūft[Ŧę_cYŠHBN) kąĩˆƒģWûx)2&dCStþl˜‚§œ@Ą–B„Äd~IŽŲ·@ūý§HBŒõzÓ4§Ï,ū°{wū°nÝzD[YŨÎÏĮr„ąKJ)ŠÛĶË7―sãĩO?jĖæģŒpŲŪEķÜüū[úņBŨ"âĘūuēą““ ė*^"ę‰۟ÝvjyÂĨ3e§jbhášķĻiN jU4 DPƒąfl!!ąđSÂpŠ40`íað@0DŽU#p0\Ū)ųÅoœDs‘˜ËǜĻP"> Âŕ‰c―n ‰ĐVaÄcGŽöŽŊëoļaem%ĒUėÂĖÐFADBþę'ĸø‰māhCSļ‘DFԝnŋúūēþĒuýڊˆĖ1YC'e áÚĮ++ėd#Ü3(vîx@K1BJ™ÛķõũÏa€@9eüm§Ÿ\ÉafšØhĶ„ˆ%LÖsF„R;fĩŽÍ‚õð,„(:˜sV ē=ĶhxÉĢ K)„ī $‹ęYdųĐĄøj(\gfķŋ~ęTlšÎŒÅÍIĶíļČ:šŌÞ}óÖ;/―gúH35?J§SZĒ2ŨÞ}ûũßuohąâß aōWOÎ VLiŸč[ZũĸFvÏŲņƒ]Fg_CMCÁ„āČБZÃ"21ƒ…0šZŧÔi5$†Ö&†RKHZk•jjqýîԂІ) O< ‹_ĸrbĐĩëZ3M)‰T/ƿˁĒŪÞ5 S{åå;vė˜ėd'ũŪtՊ&ô‚é šũŌuŠVM‹*'ūįŪžåĒ‹ĨĘykëLvŊč8į&ũūīŧwėčÚ]m,%rÉ? o„ä^^gS­ę―-xŨæM51đN=Ü ĮæĩˆEōÐl ­-3J)DÞ zÕ $.ĩĒ.ƒ‰EjiFM\û“1’įn1sFģkįÎŧïū;l'• “gzÄ ĄÖ ã–ÍwLĶĩV$ēZ7^vŲ}îÛķœÏŪî{°:?!ŽÃiėąĮí'ÏvŲ ­ir)zmM‰šNâÍŦŦÐGÛŠ’eįŌÖĘ)Å'œYk-Ĩ „ĩˆƒ‰b­ZŠ 'ÎljaНG# šUs’O$ ĶFSļŒXņĮB‰É/· dNĶķ}ÛÓŊžōJÓ4Ŧ0jÏŅéÆü7nķ]wĮíï}Ï­·XbVī?ųüŊžü ĐēŠkÁĘ3DīzŦŧō€™ũėŲýüsÛÁīÉđŠÛë$æ +xG‹ĨTptú€QP☠v])U)qd.'vō ŽQaî€ŲËžFĐVĻQžŠÄŽ 'ũîB°œ2h.1ņɁiĘĐīŌŒü‰Õãsr5-RŽ=Ō7ķ!šÅUąr–ŠŠ9gp}ðš·Žûƒû˜žšF°Ëßvé§úxï;yíĸŋP‚9&ÏQOė퉇JÁ㊞iYĨÆû§þžT·BĐ  Gë€Æ+69ģóÔ―č ėĮk ÐŠÉ IĄÍ Ũ"†æ-)Œ”ÓH­šd–(§Ūk{Ī,R!sĢŪ§2ƒœšÐJ1>óԓŋĸÐCW]uU˜dĸNEC”Ú8]C_jŋũĢïŋōÚŦŸĸôgŊØxYÜ;9˜T"ŽÎI8įa)Ĩ—_~ųП=žš!`ĒĪP‘‚øˆĢÆ=šjM99ûB°* rÎĩ5cb0+Uél fõ_ķFM xĩ–ÜčÏRuę'ĐW b6R)BˆFf`ū…_Ū2‘žMŠ„Ë-˜Į:2“iUPSkrģÔķ~ų‹ĸÁũ·ÞûÁkŊđfÆ 12wÞém œ^įÛųmŊ>sÏ ũÞōÎ3§Ï}â39ĨķëņüåuâL(Í+ÏÄņĘg1sO{üąGįįæP՘™ŠtАáōą(hX@8…ÖüR %bLĩJÉÐÉ+qĻCˆÉÉÂ%~3Ž- 0 řD)%Ū*%zOĩ0ŪÍMƒ@R €FYŠËĨˆōXĒ―.u- qf"­ÎkĪŠĄ..Ėýë?ĸÓ7ŋņĸþę;ųČ#žĐMÓN`åē+§|béä·ĸÖëgN}ėþ?tŨÖŪÝÔvÝy%^'W)œėp 'ėÚÕ7Ņ!lH$ÕS™ØƒĮąÉMÔ\35#·Ýqv T)fÕų^BLČXÝŽķĮo\ÖhW;`ĀĀ@@€D„DKK-ōōąkd‘rnÔÔåĸœÂÐüŠļk; W'cóSL"I·Š á(ĄtĨ.íÕWžøéĢoŋæ[îūgóæũõ˜žžĮdÞbÐĪÞūîęo|ôĸŌhanî#ũýÞ9^-ҟNŽ_W‘0œóŧbXCáŸ>ōȁý/E–%:ûMúnoFDŽR%rkĖž„á2šŨýГÎÔōĻąŠáū˜R79ŊÁŪvĀ€!…Á-―š1y™“”87 ĖĪ+ĪĨÐ) Bu‰TÓ4‰đh|­*Ĩ֊MnžÎÂ2\à Ó\ĀĖó‹s/ėÞõĸwßúóo~ýk_ûŅØĮ>–RFĢMVÉ&5ïšôÝŌÖöĖ™ûķÞÛuÝ9uve‘}“ „čĶŲ‘sžšššð ũėŲģ}ûķN "`u–Cj6īŠTó&wŽ!ŪĔ›\U €9;û­3Ÿ}kq^QōH­@āÚ…5ˆ†GB2՘W2ōY25'$0§š)gU3§ëG_Įn!číĄ…I‚Hã|C‰‚8ŪÔÕrjDäåŋøÉĸã;ßþ‹ĸûõŊýÃwŋÛ+z^A0|ÓĻŽýÐNœ8ņāG,ĨLjÞ !Ōhē#ÚXé―ÎĄC‡zó™'{löÐÁÄîoPŠZ@ŧŪCĸ™Á„L<ŽcpG†âĸ(ĸ™lþO€j•˜›œc0rBCŸ0ĀĀÃՐB8Ā”ÔLĪ Bâ\ŠÄTaė—ĻÆLg'І^k^FR­ČHā’33Í)‚AÂū…û- ōņcGĖÎî|þđûÞŋlūãŽ;îžkóæÍ›6mŠj[kE\~JÛķW^yådD#ž…ÏûíĸTĩWŲö•úĪĢ?îšnîõSý™cGöĸÍ/ÎKÛ§ð.ž9sšzeŊUÐ qB†F§ÜÄ7 "RЅ˜ˆ€TÔr“ tEMûö‹,7)ũGÔ9Zr$X‹0` {U“^ŅLÄTĩF™ËđņâëĨ˜Ļ+%O$ėJK†)gWzõ‰Y*‚sĨüi™2ÕR™ÉœõTM!RØ)p“ĪČĄƒŋ>ü̃>ü“wÝtӝwnđįÞ{ŊđæšK.đ„™{ųÖå—_ÞÜß("R]Ũ‰H_ŽúÆxaĄïU?vžĸ{tųįéÅÅĨ3§ýŪđ“'O”ķSĸ`‘ę‹ū‘VEGb–"œ";ÐŅĻŅRKīÆRŧ ėš rӂJd5R1Lĩiu'*DÚ ŪŅZ;`Ā`ĒčÂüļ l―CUŧŪëËnáŋļŒ…S§æNž8qøðÁcĮŽõ]j_OOũ֍ ó}ũÚĸ,ĨĪ”Ē#ŅČé€ņeNVãĶI8v@Ũ)œŧŪóôVÛ;ä&wm7ķHGRĐ@‰ Qš‚D9e ,R°É#s·ô”˜˜5rr 7`Ā€5Č@I9“P…·'.]HÜÔZ‘Ėí °„Ŧk:7TlFSĨ ˜%ÎÄXÅmVÂ\ąH vAMŠ0cӌJikĩ”sf‹_sV4 7É}ißÏėßĸÓĸ~øšëŊŋãÎ-=Qáø‰þÏņĢGgûB{æôŌÂâ|_vcL Ĩk—ZĐĩiēˆļ68õčšNŦŽFӈVD% 3ВĒD– 8îF.ŠS‚‰‰K-Ÿ]JĨėAˆ^ī@)r)…‘™)Æ/€ČDR bNĐ+ĒZģžUjŽÁ€ÖZĐĩęËtŠå˜qJ^cëƒMnDŦų(Z>pÄ°ņĶxĒŠĪ”‘Ð=+1Ą„…B"0Z8sâԕĒ՘ˆyžGjr6ĻfčtЉ„Ą-,Îí~~Įĸ°ũ·ÉŅ#9Ï0 LU?ûßëK™$ÞhēÜũ؃Åk:ÂãKē XH|t‚Ņ˜'Ŧ[‚vΆ4TF·OŪõđ2·{oÝáÏ~ü+ϘūEúēÎ Kpz)KE|ãļP07ÎI ~Š(JĩÖšũc" ÓÉ(‰€3†(U’Tå+TPI,wÏy5w ûlæáwÞųs§[ĩ f—‡ ―AŸÏÕș$eÐPķáY9ēYïÆÜ“ūŒ<―ÁYJĀčR™•$―ĢðýXmyF#圭,sĢ!|­ë2Ļ^–­*A}>ĸüóųT%€Ïŋė­C#u°Ę" Ūý 4 ĢeŊÛŨšö>Ë=ā+éj„ÎS#t‘;M q2ë$Ĩ‰‘dãltoMĐ8nš~ļ:t "<ĒēRÕ}ÃŸĘœ šėīŠþúOÆÍžóÎ+öŠðŌ|äĩÖĐ+ŒvÎiAxLžŒG@ØãÎŲ*čۚÓ07ŦRÃQĪrï=9°*ĨŠiÍÎ(™™ ûþ!ļŪ8gïģûnžįœéHŋ>U5AšÖi/Ĩŋ9å1%Mwf?žYiþ)ë<ÏC˜G4RSÕ4ņÉŽ­*NKDŒ„€Ŋ ÛÐÖ Ŋūw·?{ ļâ"™UûÜ]ŦCļŪÕļws[??ĸčīŨîß,īÓþģŨ-öÎ;ą1%UIEũ}Î|ýđVõļGĮYÝFZ8ĪŠbģûđ3įōĩŸ­†·•U܉ ŧYXwž—ŅÃýÞ7ÔLX*ŌÜį˜đˆ“øfw•ĘÜc]Ï~N@++ALåÚsk”ilĮ;ïüĩyĄķÁ%Ö2RšyĚpUPî―3Šý)^uēTt› ZiŲ9+Ÿës­ošļ9% j›Û4â48úÉglGĪMŪØnĄÕhQól’áŅ‹$ÖšØ,pV™[)OŊÞĖÜŠ †ŅŌ†EÝgļÖ(3 Ũ Ū™yŽ―ũ1ū•ėßÛõZ*™Ĩ<#7F6Ä/_ÝôļųȍGíāöŦĢ·OágĖrŌXéĖÂNož+Ūî—Ŧ}į?G ĻyO ßĩĖJa"ŦH›rĪ ķŽÁFįߥ4ÞûĶča™)ˆėx§Z ŠāĖū†Ý.ĮÝýM·Iír‹iî2Z_ĖįŲÖÉcŠ‚(e\kú"I[îßīw§ÝįÁXö‘*bY\^ ÄÕށxs_Ũ5án{ũ/x}HęÉðá#ŲČŪ}žjËē+Ë-Ė!aÚ":އ+Ū‘gāwÞų›…7ĘÜđÎ'zHFVéđ7%7öģũ3=4 ģvģNĘâW#õė T Ā}*+―ņkŸ}Úā+čtee•ėßaÓŊšđÍ"Ųxm{ĢųœŠUŅ:zž4pųŦË+Ū’NnëîÅ:)ՐķÕŌŽēŽĢÚύÆ_ę: ó8­åʒ…íý|û)TÏýX“‹ØwYÏÞĀo$ýg$ˆ/Īj*)čÖÏŠpÞyį%N%ëšN)š 8õ+€ ķâúô§æýÜîŨšsy’ös 4î™*Ĩē†ž<ãgĩŠ2ZUČ8ÂęČˆQÍÝWÄóܕÕĶ,ä9Fk4Ô>“ƒæŽĄ,ĢO=8ĖŽo óåŪöŠĐɇ&Č=HŽj ÍÔ0ÍŋmfWžIÚįsígįŨÜxUU9[ęŠü2îfÓéëî€ōaÖŊžŸ§€°ąÏí~ ÃߛwÞy tmÍjŨË((OvÞĘl '<`ļÏ#5:›+‹Ē…ę5­b-š@Åu­S§r4R-ķí3zžg !ūö3‡cŨ˜w˜7žīæaûÕRæU‚ÐŽÞ62üĩFÂb…„ÁÐ5‘4@î|ö‹iīg7―–T•āaãM˜“·{ïsōZ—:PҌ00Ö,ŋpũß}| fŪŠĖsýóAáT:ÍÝũ>Yđz}GÞyįŋfaĻ*ĖšhôaQŦĖ]Ĩob“Œģ;†‡ĪB‘ŌčjÕ;#˜*’ësUyoĢSYFâĻší!͝f• âZŨųnšFēō”j‚_w[~ÝpÎQĘčŸë3@#MûđÏoäXƒix,ÏI7Žð}a$ØķUōp·ČLÂüZX“•­‹H ŒX;ӈĩmKÍ>gge|5ŋóĻqN–ę—þė,đ/§ē*ŨĩÎÉlʘßÎ4ƒ„wÞyįïĩ0ðTýㆎĮÕ*sVQs{ž'3Ũšb*ļĨðP•ēÜ\ ŠŠĨĢė°D $ĖýŲóņ߅_k Ļé&'Ũhķ,ŦÐï“7Ø@Ö1Œūd|rÏ+ļņŲÝt°.QyĪ8_û™ÁØ}‹VU늧+§þā~~ëÔsĖ-Â#Ï$ĖÍzï^ŨUƒ2-lØgJëŠÝ†ąˆõ-_(Eø””ŧsZîgĢÍŲwpˆ…ĪąēþāVûÎ;o· 9 %Õw7Ž’úÚĄ›õ=mį―ŸSIû $ĖhŒ'wÐk’nëÚgāáÏsæāWåę.a7ڞ…œ:ŊÆĐHî6$}yU*ĩŪ€“éū>Ũ?fVģ―>§Tø>ą1VÜũS™mņē“ŲąZŠĩÃūVZšeÉŽÕ°­ÍÃĨęd[íTN*Íg]ÓûÉĶÄðˆŲŽa\ą€†'`,ŋf6†}Š4PFÞÏMNLŒ6é.k‹A– R%øïÐF2[îø­4·åURfŋĀĸĮÉtöąVHef4ąŸdļųėc@Äũ8ËčtŽžėԉˆ‰7ð~ļÓëMÎN$*iR~Ö'Ŧæîœfŋ­ UoŦ}įwŦ@ÚóėŽ—uĐjŽķzÕ/ÎfJƒp`gØZ 4*ĖĶ™íâõö†Uîđļk蛎’RIĩ:æäĄRJú“>ũ~ĖŌЃú9Ĩ"lz'L "ėý .?OÔn>æ1::Ï7_FԜb I=*HÍHÍōߙzvÓ8 P+. §wöR>ûJŊLr2‘ÃÏvÂCVMÔŲ܎fáūũów ïžó‹™[Ž5(ŨĩöÞ'·Įę~°,ëšúëæ>v)Ią|ĘilrĩUՇøÓYsÎūnhB°öâÖg?Ņ)^hœęō‚OĐ:1Ü<+!‚4ژē@uųCÃðO#øįš&ŨīXÕØ];F0Šą ~œ†ŠŠÔ„9ô]ā>‚ ýįĮ\ZŌAXąÜlŸCZī%áė=Ņ1ÃĻØX–Ï. ”įÞŧĐíų•$âOĶÍžóÎ ĩ*ÍáØËĪ}8‚}Ûm7˜TėŠķEŔ‰UsŨ ’ûÔ5gJߨ@ԜWG؀0ģŲ@uČq=€äĀÓQõŦ‡°I<™6\€ûÞŧ*ndæ1cīŸm ïšæ‘ęŋå7ģ<œýuïݖŋy]―°ÃŨ™ÞũYÂ4› h Ēņ9“Pã€ūEA=SŸáÏģÏÄãšÏ’;FŒė™ū˗Ŧý‹óÎ[xca& %"öyūp`ßL™.ØūŸ0§• ä|=føhŒfnSŽÛ›r Ōĩýũ:ý•uáÍýóCšņũÖ}B!Uæķ<$}c'l•aIđŸÝÂÖëÔВĮwŲÉÓ*ąo_Ŋ{}î―"šûv{§ę~CpĖé–'9į8îĶõšJÕ>Xũ}ôpĢÅÖȒę9æaäĐS'éŒX™UψøzØRŨu•jWZĸSóG[Þyį›)n~}V~ųR„{å1 3Ģ{UŌcUĄ ŌĩĒō8Ģ‘ģ'ŌÉķxđ›—_‘Xđ3{wļÎsÜüš.:K%Ļé ĩˆĩžN͛ҙYߋ{gįáXA{RnTÕĢÖÛ%{Ģ„pßũsöĪņ>MY\ąö>(H`{+(˜đ„óBNJ™§Â"|•þôå{ß "Ėöy@‚hˆO_ŦIįý=…sž<”ĶĒ‚Ē2 –Ržzéƒŋ:ïžĮb€ÓzŸ=’>_†ZDfŽöþÔ9ĖŨjSƀÐ`­á7žĮ<ĖüėyD(āĻzuõođYĘĘRf˜WæÏócnáQY'ÓHĻ“UIā9yuxđÓvcÖq0' „yîMŒ™ ŧrĘŌÕĀj GÜũCŒÞŅ6Y ë§#Šë"­„Žī&&UŌ;_&'û ƒÅú1ÃČFlƒiï{šŌ§… "tϞuÛIZkEüÁ›wÞy·ÚĐÅĒÜįœčOŋÏ― [S‚[YáÁBíۖz4‹ûéę:ÄsŌĖ— €“ –UUđ<:‘ ö>+<Ķw‹ˆëšíO|Ėýé&ņė'+éQŌól’ePU;hiîКFwŋ~`n4ŠîÞ AÜœfīþ/\Á0kUyöįZyrX”ϧÕŧuÚéŧöģôĩ|ųtŊ­ĩŠ+Ė,Üsg•˜ÛŲ{™_îĢą-éóđÜŽ ĢŅø'CßyįÍ@ø ĸĢ7„ßænæ%åII}öu hk•™ó+ŌÝX>tóXUr5 \)KâyžšvÔ~n‚Ó·6’oIÆĀwwš=’@bČÔę–ōá@Öüģ1·‘}ÂÔßyį?F ía(í_­ ІČĩ.óoTvŽUę—v3óʒą”ŠŠf & ’-uË-ĖBUáN°JgtĐæyrw Ų`VUjĻŠĘieˆ5ų GR??FėÎýlŧ§ ØÃōœĘ‚DŲóó ZŦ°wđŲęWSĐRýHõō[Ō UgVWk!m˜ŧ™íįĐ:fPÕLÅgMU84'É1>œ},Ü= ŲVŦ+lkŸĢFíŠvÄĸîÆõđæü âōuŠH6îa\p― {fÃh˜đ·ĩây‘™iMģ–t}–„ vøÝUu]W„7#!3ãûUýo‚kS‹CâwÞų‹PKözîlÍėÔ·ėzÜĀŽuflúō4ôxeĄ+ÆÃ:‘Ø“#c&wIėļ $Õ)_wŽ€LM˜Į™ŋ†ą“MYL·Âo*ˆûy*ÓégŸIŦ‰øēĀ”~é‹úïxßÝÜ|Āī†Ęd#ø95ŋ5ÁįįĄŲšBBÖ4<Ú9ĮÃ!ļđ†·%ęßŪ`Ņ ÚëoÉð 0 ßäķ1WcÞ%MøŦóÎ;o·Xæ1ui#u_ÞIŽėcŪÚŲp3šŪ“UūĖã?Ŋ{øWlëKÃHJßM9sŠrŋ?a0%€tĐē SIÖ)ÜMĨŊūŽjQú|>Ĩ ą=K%ņõ‰5)lîhƁûįgŽéšÂ6ÓÍ}ų·Hφ~fÕÄĮīóø fGØĐ~øÏŽJýÂ/y(]Ũį??–āÞПYînīsjĀ:‡Ĩ™đ”ŠŨ-öÎ;RWŦĘsR™ýÜũŲĒÛÐŊByŦG‹`yÕ>t._īx~6Éč0„*M3yVþ6~óéU {ŸóėÁЂŽc5‰Ĩõų ĪÆĩœÞœ†ghGþœ-0þĩŸGŧũáÎSk-ĢÕN~ÃÉŽ%ãÁ=’„ņ<9q €N%Eo$Ņéįë6îĖï,ö‚ü īMđŧĪþqN9cxĸ‹hŽ.:Ím€ØŒóUĀ2wé•ÕūóÎæjWÄŽZWÔÉėÖ·RĻFǐD'eĪĩoƌ)%•ž:ģ(…[VŠ4MÐõđ$œi7Ï*Aö[<ž§ÜÜͅä$ŋ˜íߑŌĖĄĮ:;ĶsÅdΈbYø˜Ųöóŋ~ķģ7P―in8I#1qtžÚ™B‚deĄýļūVfĒj]kšnĪ2·^ēO“ÔNãdw=óLG/a™ĮšŸ"wÎ_)ž%ē z4Xĸ―yį·…!ĖŋĀw]Ÿ^ũÎlsÂÉݔk\Ý–Ī}ß·O7ĩï­>2Ú;…E?Y0ļ9iũÏŦð–gåžÅođė|֞ÜgxÜÏCvÐL‹U‰AØo0âôƒđÓ"l$―Чc·*‹ĒLđw ŋasF}óļ9Ä~Œiuž ę}Ü―]gS–>ļŲ}· ÄäuÍ;M&4}qoq-UMwï”éÔŋwf’"HtŲûŋcnÝ>IžóÎ;ðXŽÕbTûÞnÍɖū-ī“nŋ9i%,ĐëZŠî^Üvį"ĒQē*iāÂÏÏãð+.’0­i{<‡=M8Ü4ŽĐڕ&QŲŒ‘ Ņ"gO­ĨæCú䑏ˆbjTEƒđý&.MÏý$ýč4āSč;B<žwíĪņ}GW;Ę6IęW5 rŪĸ|ŪŌÜP~öÉ*‹&L”nÞyįŋĮÕæÐĐFs?yHšÍTIÚ Ģ1ïZ°ģÔäϚ­9•ęš„ĩūJfý1^đ.ˆ/~• <ũ3ZZũ ĸÜóÔ~ve^ĸ|ŠUUîËĖũó4ĪRûŽy[vAaFÜãšŪŠRÉúĀŪJãz˜ĢķÞwCSã™MãOzü;YGYŋ|E} ÝUCđûšb~ÄČ 2ŸĻyŧâXÔNŠDšwþĪõ ĢÁōĪYØß4æūóˁ*ÚþŋÏ&͗O€EĢ7Ũy&žęđhúu§Ŋ @@\ëīĪ?š’ļïŧQ2~‹aŧNÖ ŧïĸúuČį~ĶĶŽN_ÞŠ’jVéęMóšÖóóLĒØūŒ‹!3ŸûÉŨ󘙷ÞāŲūՊuŽUŅQN,gĪėZŪJĨĖð[â+·˜Hr•ÜÂÍSY• Ä­°ïi$ Ÿhš™1ŦžgSöÏįÚy&™ŒÔĀßzóĘ؊€!ũi8uI5&ąß:ĩu­Ęzö=ýíUß:Ë+bđKxįwþÜąXôf§ Ýî…S2 ömäāWŨÖĘ#r"­?ŠĄ~ #z*ŸûgŠĮOSĀßoéÔĐFšįÞtÕhXĮÝŧõāė>ŅōÏ™Â…Đ 2“ī*éeÓ=Ē&vŦ—ņ/W―–šóä>{G\ÂoōËô0–čáä|_æÄ°~3šĖLāŲĮĖ|” YÕl‡đŸ{\ŨE ąbčl: ž<æ^Ō°ÃӘ{ęĖ‚üÎ;ïü9ąŨ ßŽÕ9įŠeāūo î6Ū-wŧ"TYŲÄŪÏþĻ’Ö‚Æffĩ BĖAY:Ķ KYō^WÏNB<û‘īÚ 0ŽąŅZú*$ũó28ūÆ4\ŸOMúŽ41ēY‡ģĨ*%ņsï<UƒxüũÎ>ūėC^ŠÏÕžGYøōĻow$ÜCį"Úģũ>O\‹Æģ·ĻąĨ=C_iĒmēŦvĶv·ß ŧ‹ũW~ðÎ;”@ Hv[âģQ5ā[úÆ`WĮFÓŽÏNĻ@€ÞĐáZAþw{wJØũ#"Ū'­\YéfĪݍ­NâįįvģÕM ’ĀægģDđÁįđ\ņĐRUVi]‘Y]‡Ó4ëŋSÆIx9 (šųýóCĒýĩĒ’Āðfķþųė,G‡ s`D‰î Õy†ZÍģn‚Xđ§ífõåb YķŲažßÂø<'š§8'Ąr·XĢ&>™‚Ų_ÛwÞyĄíˆÝũîčn'lÔøænÄ9ŧã§š;ĮõôÜŧíģ+ŽĄ$mĘ―atēR0uPÖ%•$‚ÞŲÞ*}þųdfŠHóÏiÕĩöóx8Š,=įĄ1čÕÆ sŋ:6UfėĮxĀi,į0k­Ž1žÃny&ü›öM)“™ĩŒũ6ĘēŠ* _í ŦJsƒ8ô+:l·‚XĒ›U Zƒųœ_ķwŸsÖĩĶÄw…›đϚPpũėČ/ĶzófÞyįOޘõþe47ŠzÝ#ažï'OMlÕißŨĩšĨÜéáfĖ!@›@Ļ*í @h°6ĐNžÁŲSîëZĢÁÂĄēNî_7W“ā?ûaŪģDPYû9J™ÛÉėgī‰ Sés]Fž―ĮųV§0Þ°ĶYU…FÚĖãkEDAcąöST&PKĐĖëZkÅ~tũåũģĨęĶ ÕÉAŌöū>î ÓŊâ<§ î^'đyŊØG•Ä;ïžóŨj‰1ƒ…YMÛ Q€JåŒX%QcCčîDÄ Œ7§p2GÞ?Ņ‹M{úó<ģÄuöÔ9uvuėĩŸgžĒ―Uó1ß%T€}ëÖ˚Åu:§đÚ+ŠÛ―@š‰ÝvdĨ4ķ"I7sëuĩüŨgŅ,„AŠŠčéßzâÄ]U(Ð-ģJãÐå„$Ė aKtŦ•mĸœ“ŲGį„OąÍ9ÛÃMíĸ;V™%đŧðÎ;ïüA]íŋ“„MĖDIķlw,Ï9(L"Pnހ˜ø §ĐĄí*‚4î―óëō˜Ié$ĻØu­ól•Ü|]_%VVŽĖþū‘œƒē0+eV ŠkíÓD ý ĘÜIö5mļ8ûprrŸ=ôE?ރFóЙœrģūuæĐT|v–ŠčÏÓ―+ūYåģ•+Ģŧdlį7úĀP5™đfŋūŸÜY% onMĘLJ}‹Ęݧ;ĮÃDŒyĖÍf)†”ÖĖŊðđ.:ķbė3šģ1dŋZ;ŋŽ”(4åzÎ> V*§ë·ÓËäNsžüwÜâ95ĩRuQXÓ%ŋ‰‘#đX}^·ũĄášÖóÜįI§ÅŠ}?ī&p͆‘p_{ïûįqģk­ýlu…eX ïņÎ;ïü9]­/?ÏV–ę.nPŸÏÕ TBMóÍĐīFÓ―ĒÆ6ą`–§ō9aôvp‰Eās}rNáŋĐ.i0‚ûlIÆ'ũНÛüšđGIŊðg&ļ–kSC„°Óķ dš2î&AŋâŲZfp­õÜO)?ĸ\•júUF ýHÎÏuAš ĢÕtņš ęVóóÏ?küšŲÞ ČûdŸ“@kØ(”™y˜PÄīOvĪ4 â{? ïžóÎÔ՞œhAåÆŌ#rNoH$–_‹ä}ߝ]pđYUŠ‘QdŠ@XÄŲ;ũQĄkvĶ­Ę*é9íŲ%k— ŦKŠ‹o—ĮðķŨ·9&aŒĩ€ædņÅŊ‚œf`ÕqrųE6ÎNGäč šņũųyJÍĻŌTðåëšönlaâÆs§yŸ‰ĩwÂ@3>į@eá kvëšÉ @4@"lyÄŪmÖFp*ŲÃãÜgbn>ŨuÎŪŠĄ2ž6’‰xįwþĒØ+KŨĩ,üþđ=lŕ%ĐPšÓð”đģ°OGXYÐųü<č‚ÔНgá%$áj™AĶÖĩūá,°Ïúœsr?]vÝÏc‚››ó~ũ 1áW„TYÕōÞ?ÛGė9°‘ūŠu1ēĶŨŽ·cQģŽī€f(euQ lNÏ`e>rˆųÐYiAĪŧs?Ķk];TÖge‹ŌĶnįį?ætšēî―'ÞAݖÓÝŦ&ÜÝū&äÞyįŋVxáãSáÞįø”Rĸu‰ËÜîûĶāEÕ}Šž<$Õhĸïs=ÏCC—ã^ĸ^lēņ#”$ĨdÕðÝ]žmE+Á<üî*ũSþų|Ję|7óįđKâ4Ôa°ŅLýLûrEXŊĻý —ČûįŨĩž)W'ÜkrķÜŨ”ƒīn)ïC70‚\īILĐɜÝ?û{ąąOÛū$Ã9§·ã8§h6ŽVģąŧžį ĸwūäF§(T;šl ØöÉĖę|ƒzöĶûš:”öŒ7—ÏޓĄ.`Ÿt·p؃wÞųģ!Š‹TiÖØuönØāōÉj‚`oØĘS4Ū+Ü,Ï9ų­fދ-4―[į™,ŨpũRņuōœ<įš.4âA6Ų7㘅WŒ•5ĩŨŲāŊy·x!Öj—AZ˜ ÉŲŠŽn‘āäϚyĐĖP™$ū$ íŨÖeFŦ*Ą25ĩ’Æĩ1ŋËô°Uåð°Ð·mÁʊd\ðdĒ *MoČÓr]žóÎ;Pė5KŲčüŦĮÍ|ĪN{Š4_ƒÚį˜1–gĐĶú%ëüįˆÝ߄bT‚Áöï-fþšēˆĖĖFÃSįŲ·šijwW EļÚá―ĀŠÆąÛ |ΐČӅ^ ‘ãYØûLãï9Įœ`{ØēļOĪÜ\Š^W­‘Z TˆðYKëTSŪiNĻ–ŦSÍ`ƌaQYĪÍš=zéq•ŅŊkAŠAžóÎ;‘@ "VDīæīčæËI3·u…ũģKuuÉMe^ 3ŦÎĪ ŦõœTÖ1·Oũ)dþFV’(Š ™9mcßUÚØ-2ŠĘč~ðŽ­Mģ4ķEâޕ)PÕXÞÅbh8ÓðČ{sŦĘÜ{ķÖČÞŨGžãN7šĮÉ^·éą<ÝÏlͅa*^ąĖýyžÞ‚Ýŧŋ=.ĸīFâėŽi;ŸĖTjŽĀþõ4ĢiÐī;A‘dğlaxįjódcYNŋËdēH5âĸ<aq}Š3T]đx­•'Ïģ!ŦRƒrW@Öé0ģ°ģOž[­ęßčÎ*Ցۗ~5™Į*”ŠîÞaĩZË%õ­ĸmυU,|ûÏģÐ/ũ 75đ.4wŧ5aWŽ:I˜@óĻ<ÖR‡æj‘ŨĩÔā>hÐŊLÎFėãBn27,žsÎÓåËO€fÞų“FÐHLđäōUU―…W-ū!Šïžó7đÚSóã۔Ęh O:ĶŠŠXÞíßY_\ĢY„Ë0ÂgŽJ•˓ĨôÎ4PJfn†ĸÝw­åŲ†*ãĄ'O9øEXwßN4—Œ<'Axßäyž’Øe_·ãoŒ u}›ū ņŸĸũ)dĐ,Ž3ûâ — 4Ķ:r·JUĄŠķģvš„…§j?ŧNY0b‚ū Áh‰y†čFēĢIMâ—[—ķå>‡xįwþžŪöœL•›ŽSfF‹YÁ<ģ~{Ķõā4ÎZee$îFšQužęCĸëäVkKÍyß'8Id7sóĮh˜ņĻŠŌĖÞũcfS†xžp]Wę PŌ§ë͍l2”Ï>Íu8ø­ ŋâs”ÃZļGÓÄŲiķ‡†îņė%[UÆý ņ™ÐæA5ØģspžJ'ė1Ï]˜ąwÛyßĘĢRVeFĪMŲŧē:ÞėwÞų‹\mcŒJėŦēōäÆÄĶv~KÚ]ĄčŒ>IKr6ĘC2ÆN0%7q}í°ýŋį9Dxø9‡ôö/€ ĶĢ··f‚îv?·q°L§_$"7'$aÚgéNzf‘˜ûķŽLæqz{T*æ—ØĩÕ蔍›ĸ+•ŸERų įÍ,sƒÓÍNÓîî°EX4i{ŠfĪĩųhîũsįIþ;@?öįZ*u0‚ŪkU• [ŊØëwþj‚ŧ_ŸU§†Đ4Ēö1:œ †[”,kK Ðô끗“Č…k]­SfŪß@BŠ+"ģÆā`FUa„·ĨY Í,ŦĪū`ųŲÛhæfäų^Ė~Õ †ŧ=SãAō~žFN•™ĢjŨYáFSVcŋ9îë%<ũ}ą tķũLĮâĩ:ŽĘÍ'BŽyØõ-K§AØûq3šØ•?æîfhæWĻÏĩtŠqßūŋ)7C•$üÁyįwŦīï 2ŪpZž wŋ\ŌsoĢĸÓÚȌYðÂÐŨĘž'ë$Ý#"5i į4DŌė[gká%å}dčއŽ*wģŊâb\ŨÆōĻRû‚m,JĪqŨmliφŅ=$”ĶëlŽr…ŸRđySÛsCžóÎ;‘ŦÚtę{~ģëđïœĨ2ëœؘwÝ,ŪEsÓ^.áäąßúŽĄ,Eí}(v;ŠĖޟAäūJ5 ĩæķwЊ$ {ŸĘ2gU―Ýloí[Ž‹é ‹UĘ^™c9 <9Kå™īšŌš.Uý6é~ķËWfU–ŧG‹Õ$Ätßþ"5Íæ1†Z’ąHm ŧ‰‘,ÕØtį‘ĖŽú~“Q*”ÜĒũY•Ę# oáÍ;ïüIąĖNNÕBļ7ô”™Ŋˆģ“āšŪIĨDĢJcÞ wĐf%ôIߔî`ĒDãHļĶmWŌŨšžđŦjˆāūõ,•ŪĸŦüJIīaŸÚŽ, °ÖRâė3Qaurūnļ/3[Ë#<Ï)UCdõ;‡õ­s,gö_šđīģOG#‚@)%™Ōē1wŒc,žÂˆąHt.">ĸ|(Hó:­ @ė{qĢēÎų“…7ïžós˜ŽŠh.á›`ݘE”_ß*ˆsö”3îģũޓ)ÓY-'ÂŊë:{ˆJęôūžlöéOĨę/ŠŅđÄAóa'ąĪf~ģ―–/ĩ“ŋâ„ûđÁĄ†+Ŧ<gK ĘÔþBĪ•ōī–Ā,0…ÅŦ·æ!"b }ĮĘ5dM†īsMļ>Wõs "ūîÞi#ŋ;ÎĶ+'ēŠ$t™Øs”ëóMŪ)i‚Š^ĸÂ;ïüUŪķý ĩVė<&ÃКY8TæÞtTɀgïp[kįĐŽąųgUîCÍZUŠĢ™ݝâœãŊˆŊހNXåt]ŨÅs{ö6ÓęxƒAô.57r Ž‡ÅšēĐS’ĀœþÃmâškÎîÜxžíæmxÛ§NЭđMąØv^š9Ī“ÂWgtIëZî‘Ï)Š4w–RĻëšFzüeĻKđ…Eó0C• jč―“4ōÍP|į?Ę՚Û™`üÝæR Wõ)–[ŒT+Øüfe•:ąĀAä9Ēāû~ōĪ…“č+yļ ‡Dt^—J+ÖzžÛ;-ÐóÜgŸÎÏŅúÆ`Ŧy{bšA‚šŸ‡„™sdÂ$•Ž;c…JĀŋUf~2-ÂÂŧ­VßģwóķFVøãdîģKéҚ3§ÍįVðŽYxž=+öų•7R î~ĸÜ(ŠfU4”ÍCØōŋÚâøÎ;Ŋ17ĒŠ(˜;ö€…˜NFïUt>þŧ[îŽ3Ĩvßŧ N›FNŒŲØj.ixŅqũJ߈™nųæļö~ĩ#ģ—JÁ˟ûÉ*-ėŲ―Ž* ŽĐxČsÜúO)ĩü2óûįĐŠĩ@ĐNąØÐ%ŋtðĪųÐÄËÝ"XŠŠk}ĪR_î]<~ÎģžÅ *Ō×L•ŋeĻJUG§ŨIū|y|ýļæĘRŠ 3VVUáwÞųƒ\­ēP `fŠABƒ,‚āXiÃė(ĨäŦƒēHĐšIũäl7­ĘÁ9ĄęnyøˆVŨZ>”Ė €D™[ €Få–:dÖĀóĨčÍ"‰ âH[ÉvdĄ,Ė"ÚŦ+K™;wš9ĪŠróëœđÁæ#Ž;ԁÏįQũÞ6bˆF\ûnô"A3đs~ëS?+}ĒqVj-ÏŲ08)iŸSÐú\ĪŨIïžóÎ_$aŒ‘ÄԜfįÞ43Ō:1–43ËŽAzóžC掜kyD'pïģĨēXŲۍķÏŅDŧ˜åNÃ=č-);~EGܞ LĻÓō2R}7ķZ‡6w}ënōT,·~5fĩĘJęM2Ūč<œ•ģũÉģ!:ŋÂ/3ƒŽUę°GåÎe>Aæí_°†~ ―žó·§‹ÓY2˜93OVŅPßNf&23ŲõæČ’Š ĩBø{óÎ;/Ԗ*Ėól#ÍÝhįŲ‚ ‘ķYZsZŠčØĀYxAØo„•F3ÐÔVæķž)|ųĨSÃŊˆŸįG%Ŧę›ÏŌ ÖDM·BŠŽ7IdnÃd4ĐēĘÛ);^>ûЧiVŌޏ ] 9L€ZpæŦÕ%ĐId(ÝÜUäÞ Ðōõ {T™{5ZzP’ēD3Ĩž< Ý Eąš“ 3š[kÎLJ7ŲëwþĶÁ詂[U8{cÄ#'P­ëšœėÝ+þ<ĖĖhŧA،4ŠjŦ:{ķc†w[bi…aę0†Đ1Mh°#%M;Ņ0Š+.sË!Ŋˆ–Ü8…1Ĩpĸ^ĩA@ÓŠŨRUĖÍg'Pã—Â}*ķÐ6„Ą,ĖÛ°ûœqĘF{pĮ);7›`Ę2sũó4nRBVÍŦAĻŠūØģęäđÖÅŪ7‡―ÏÞĸû™lÜJÏ9ãakԞåÝMxįwþdBgĘd æ_/–Šl‚ĀOŸ›ĪŲRÍ]ĨTjŸ=pĢét„ŪuåĐūŌÆ+œ]@ŧ—Įu}͇ŅÃæŠBiUéNãx[BĘĘęÕÏę$ŠšĨĖ$ !S’č|Nïuš›@l ž#/‡ÃŒKĀ=0}enķũĢþĶŧŦ_Ã=ēR›2ķRåņðhķīUV!(sïQ­d`ÁÜöóϊˆûÞ$=ĖÝÏ―A2āŋOýRURÐþO%<Ï3ü2cMø·I˜ōO~enŪ”Ä_œwÞy·ZĀݜ~К@XĢ17§RUߔÅįv„Å"trŧ 4Ļ(ÃXäÏý\Ņ â™*Tļ7ØĨ[ <ûú,ƒÝŧïļ|:Æ|qō@p›8Ūu‚fÔ ĸD5—įäHŧ–[ílt$Įœ%ģp6äŸĘÏõŲ•â4JDŊĒcûũ™M4Ģ U)AäÉę;áŸÏĨ]ÖĄ3Ņļ_ʧÍ@Té<Įœîö< 0P' ™Û„Ûþ­yįj§17ŦÎÄŊø:―šnĘDéKŒî:Mšx„CČ9Ž_~Ɗ ūg‡;4;Ïųaķ Öïý…ø“·ĩ$Ëŧ“Ņ-ÜCßT0Ÿ:2 f4Ú>ŲIZK­ĩ%ØŧmÕ9ãŠÜbôv|ΞĀ(ÏPŨšvĶAý‹7@ÓVP€„eÎp€•§A|h„õ]N–ĘÍ&üÛa_ܗčā2f jŧÛŲ;ĖEŌĐĘ œ,é4ĩýįæw^cn•ö9ŠŠÎ  —E4ŨYî)SE‰ ujÔĢ’Î\Ë 4:Ķ=—ą$u–Ŧžq7+õÍĒ=*ĻDë/#͝<üĶæxÃbY?›Â ÆŠ‚īŪ0pĪpV%2›X˜ ó*ˆ)K™‘nwŨÕŽX,P‚ðuÁn6Ē‹įyDÖ9C˜˜ųTøÄåą.ĖOđMf>ÂŊj&X;ũIAš3mą\ˆ™9ŠýwÞų‹bŊ*QĐiQėŋ·ēHģ`œ(ˆ:EŌ(ï+zë|ÚüJr?;ŒÐĻŠ2ißX€Đg,):īÅÃō9$ÍHīĒ„ĀŲ'ëTÍ*1w9ŠįÞįd_<ž…ŅxYîėī—˜›NŠMŧúVÛæF‰Æ{?Ïy&Xn„‰‚áėmâZkúlĪú\X߈ķ!œJœÆÆJ7ƒĄŠĶE­Jč[›Ûé‹aÆ6ŸýļU],7Į_œwÞyĄv8„=(Áhū–ŧŸgWCK02EØšVĶĀoĄũs?ƒzcĘbŦĖŽÁëóáPŠ”ōœ―wQ@åÎĐ/KáÔåŨR”3"ÖîĖw·FĀåq}–{ėgäe_/ Öšïg―WÛĀHÚoލÁþųüct4ĄL·*eH§ÝÏÎ*šĄU3ïĖŲ§N‚€”ĐŠ4ÍvũVŪëëÚĻĘĩbri r·ŽŨЉŒ―w•bÅH|KˆwÞyįęj+k$YđR–Ņ"|ÚšūĮYJ7ũåĘĘL ÍĒn’š•&|Ö1Ų{ęg Uuĩļ7RÓūîē“čĸãn(PÍH”FQāasZĩbY##EwĘ"+‡˜ųkÅnŧÓÞũ]*ýCŽUý_ú"ۖö›StwëöÜÓŊSÀdĐę·-Om ģ’äu]j•Åüē*Uiy„ŲüIaūRģ‰ū}ËÉßyįï&{ĐJŌodÁԆĮ9įÔ1ģŠ†ĀĘŪHxö>ŨšVޚlY~OÛ§öŅÍĮįÚЉÜÅYxGfnĘĒņ[­XEr ÎæY1~öók+ąîЊ+J]€vš8ūÖĩðïkÅĩ hÜóéčĩ°ęüÓ―Ø ?ũOVš›™Ėû_ģÄ{Xl_øÕĨ þ: NąnĸđrŸ3qįËöþ—öP$$íŊ"í;ïžåäUÕåēũ~ēZ ~îĸ‘æfÜ;v­OMí‹ÓŸį‘ī"$í=đV.čŲŅV\™Đ‚…ũ1ŅåÞ^ÛjÔĀĘsüŠS‰sË}žˆĄ‰ĪŠ! æËĨo§dtŨŅØŧíY뚮†ŅŌvĘBĢŽJSˆ+Ôó*E·ļbï§sđÖũMĨ@IFßN“Mū\R~3v}溜MDã,'ėœLÏgUĐ$âwÞųƒŅā•ėÄ?:ïįƌÚ`pœÚU%bLSR-sũsC\Ÿ RõŠęm~Ö€įœRŅžWl4ŧžģ‡Ŋi?ŧģe?%åĐð‹Š™%)þđöÏ3bčĪð2ŒĒPßũ3+ŋ-ę2ŌÄ—1§đ­ŸÅš|öå‘Fė“4Ð,!ē0žóÎ;pŦÍŽ‰Ķ­Ę}æCú·JŦš$Ĩ€&ï`sN„Ž,>ú-Ķæi'žõœé­q#’°Ū+!îq]Ũ°Ĩŋ=•Uč§€/ozAUEcžDivð“gbČ}9ĀédT "$ĸvęĻo]G§ŌW|E„ ŧ‡ėGÚ)#Ýh―ðR+ÂasŽw]ë™73Ŧúšý–GÐÝÉØÂm8„‚äðī°š"ĸŪ…áw^·X§ŽN}VoĢn4·˜ĐŌÎcná+ŦŠ qmEęhŪĘßd`a;ÏCÃu]€îóĻøY mũúBgĸ ū<ũ7ô ÂģcMėVĮßļÅŲ  s—́†8û”āáējĘqŦ† (õq€ĐYĖæ Æ$vöcÆ1đUĶ“ĪúŒë ũóSÍZHŽŠA·'wå13eígWÕü)F3;žõFŧïRoĮhšBŅŨžóÎ;@č9=4FÄėđ20ąFŌlĻ€>ŨĐÚŋƁĶƌ+VVĒZĸOæ9fabeĐ ‚ûb„‡›5;Ąk]Rí}Æ5 ĩÉõKŒBĐŪĩēŠ_ę%u™Í§cÃnóļŪČ,T†9Ąb ÂæųgëÜÏqũõ=ĄØ}‘§‡žŸĸÝŅ18Ĩj§™ŧÛĸ_š­UĘEÆTU$§ÓŽTÞWŦ_jū~žĮÜûŦD•Ū+öafïžóÎßS Œ† ý+3§š›ÆįđũyHzxĢžÜmÂjŅØŦūA4oPUCĒŽðæÄ/zŪõ-:č™Âw§AwSeG.|sd&þfĩkzAœ.;ó0Ōˆýœ^Ŧ<ü›ŽØ€^Ų€ŦũŲäf’Ķúkžŋ€šåÝĖh—Ę}Ģæ/ Ą2EÜûœ:CT}ÂUĘúw$ÝÏmWðTóî~iÖ)ĢáwÞųƒĮbænĪ;KÕĘüIÏē.—åîlïŦ1ŦRКļîQ’öÖĶėôk·˜ĖÖÕ0ZjęxÆŠĢ*DœĖ_õUtņļŧ;$ 4:Ũ9îaþėc”đPŊĖĨėÆōSŠÏįzž­*Ðz‡= đÁ“§Q*•ŠŠņīõeīyæŠ*Ÿtó37ŌÏÏĸā#Ėʒ’ ŦeÚ7ÁŪá QWøf„Ã9 ĻaŠŽŲ äŽðĩ‰pĖÄ$ą1ÖŪ*†Úæ›4‘_ÓB.ŦPRŲ|LÝe*=nąÖ2ˊĒW+áÓcQŸæqÖōÜOŲŨĘuįöėýf•*U–,^;ų—Ā€€‡i+>W)vƌ—ž MΝ;óŲīi—/%vïŅë“ĐŸÍä723ģ@ Þh—nÝcF.’‘ņdåōeK—,ÖôXí:ƒĐ…–Ŋ0uúg‘õęŽĢĮŽÄN›RĄÂsŸL›^ ’ž?wöŧ9ßūÓ·oÓĶÍq3ÓŌM<ųčŅ?%æĐĩķF`—j-­iøŦ !’ŨkŠā$­ B|hĻŧ~RŨ€‹Š'nhčQ­Ð 1íŠ+44ĀĨЇÂdˆb™ö%3ēčvuŅų1u}p3L“‚傌ðĢiŧKEF―XfYŅ3 ĩGģý=ZgEéĻ)‚]ˆS­Z5œŧÝŧw>I‚™™ųp|2mZÛvíW­\NŠþ€ƒÓÓÏųúŦISĶ-ŒĄú‹AAA°ŽRåĘ3;;kİũ† ™ðŅĪsįϝøóĻËG{Ø`ęØNøhâëíÚNž4ŅŨĮųyÜĖäĪ$BÍ[īÜ―kŨГ ~.WP`Pï7ßNžp~óĶMáÕÂĸųįē(žhˁÍ-ÜR^šS,ĀžŠõÐá k t€ Ųô3eĩ6yŨöԓïeãĪ/7Î͟ÝKž{hi|}0ʞ™C“ķļ^‚$ŽÅ/™^x ãq”Á°Ų(Ë/pKÂá*ĒâŦDzĄäBˆ―IXVđË,ģŽHæ?ÐnDģ@Ļ…‚>ū~þþœČÉÉ9+Ų<·óēYģæ7nÜøō‹3>ĸėÜđs„q˔-[đRĨŋĸþe\ėÜ9ßfddаððōåËãvëÖ=Ī\Y?ŋŠUÃ=JBSËT!!å"#ëÁųĶMš4lØ7öųį_HMM劊=ĸ<č\šôŨĮÓģģēBCCÃÖþúë“ĮņRĩWMĄnļ9AŪĐS‘dCðcddÔĄęϊ6ŒG Žó9ËIKsĀG2 HN€ŦHø`ū.saLČ`úëh(šŽa”ÂUųAûŲ%)ŊU34 <“(ĶzIRŽūŠ%Ķ76Ë,+zfyĩ+=ZÜïMx–ø&đNęņyeBĩô4øó\åĘ$féÓēîÖû8U—K2šˆĖ‚*ƒėœEôoWJ{Õ`ī@ĀåKĸœL8uįö-V1)ą6ŒuU™P}kËÅ˗āŊ“ą>ÞNL9ŊÓ4ŋŅæ „H#3í5ĨĖmÓė\đ8ŋLhēqQó–ü]-ŽpęŌO5>+Å FB›DāÛæÛī|ŽÚ_ū$FŠ[m+0šŠúXf™eEPDQķ†(p2ž …P@ðøņ čÛ·_ĮÎŅQ›tŒŽ -#éFŊĢFÕŦWïÅ_ž}ë6n,ČŦQŦVDíFQJ–, >zˆ3ž<ųņ‡đ?ΛŧkĮo)ĸþkš„2ļxôčáƒûũAÞþ}ûfÏúrݚÕ/\ ŋu+yÜčQG­Q›`…;//vúīïŋ›ÃĖÍZķjÔīéČŅãļņ:ņK…t0U―\t;į4 Cņ+5øx Jē2ÔK=ô6ōēvĄĄĶĘ:Dī‘Á* Ū)Ļ‘UÆ"NÍr35u„ŒÅ Æ4ė-…alšiņŪi'É,äâ‚hLŽÃf™e–Áj1Ýēũ˜„xaęĒŋę·rÅRō †ķjÍ:†fddvlũúôĐ/ˆ_4oþϜ!íéĮūŋ~ýęÂ_âq<7mûíΝ;ŲŲŲW/_^šäWNnýmGNNœy·Ï[xvęę ō\~.žæŋûvöŨß|7wÞžÔ”Ô’ĨJ~=ëËŋsïÞ―ēeËîÚĩŽoÛąóŅÃGĖ™œœžwŨŪ~vëÞ=1ņüŋwï8y`ũhÉŊ S{=ĻĶKTDĸ“ƒkFĻzbäāãeĒŽ] ^!T·ÛK°ÆîĶuŪxsėŦy+f .!GBmýtӟ`°rą<“(ĐĩįŪŠ ĻtũSÐîėkČÕ4Ģ5ĶSyžUÐKĩlP™ŒBFíéiāHÚAÐf?Bj“uk6Â8ƒŲ5zĘYŪ–Ix_―TU/3ÍĶŽLÂŊšĶģHËĀxÏ4$įž*.˜ð…―@6Å XBõ' 9°Ė2ˊbķŅĢ3ąpba þ'áEffļ4yK §Üj˜tQWÝđnÁĐÏšßð•ė+ ÖøđĖlũˆ é#j/Lۑ=YBŽ={ÔŲd0(S~ŲĨs—2mėŲS‡—īķ5YVž"cT|đ w>8‡f_y™û―.ÓSR•™[Ļ*U"Uƒg­ņYuŊÕsUw• ŽÏTĪKËԘPŦŌė…@ķFāËÚ깚& ęÛjū0o1Ôä՚î垃ú/\/š―Å,ģĖōjņ:Õ3―jāõä‰ ŦÍŪb„ø°:vš.ļÁƒ<ümûķæMN8ųvŸwH‹ýxōGh$ĒļØĶMŧĐũô{'fčÉ?tŧóĒ59ļo/‰ąxqąŸ2ĪaUūMÖŊ[7eōĪ?jŨŪ}dÝú _Īŧ Tß{Ԉa3bcĩ4Ë=Ļß;sūýpŨoÐ Šq38;ũûï:ul?í“ÉHčvŽî DnįŽf͌‹îÚÕÏÏoîwsfÆ}vĸþý.]ŧĄ:ÆŧĶ=6 đhÚc‘—*ęč”úZu6 :Hou$ ˆ\kėÏW{9quĩ Μg§Ž‘ļŊ ąk܃p/&҆qʏŲÔ;֑†ÎÚ XfYQĖŦ…­ė)8$k œ2\?\<ÄĀÏãāÏīš1­ÅáÉÖ͛/'&ūՈ‘ĨK—Þļyó i@ØĄu›6>œXĨjUäeYņâÅ V6’áv[zZÚãôô Ā@Æ5îÜ9šiAđŋËoRīļėbééi‡ ÔaįΞM8ņ7,2uZųŠÏ8žüûDRō­ĪĪ$čU6ĪėõkW9yðāŋŽ!} léē\ÕĖY_qÁ\ï“'Oˆ‹äåäŌYÝ4TÚsWzŲr;xĢÜ /MÜV$ld€–˜Ždb ĐC í―fú˜yÛ@ģfn @å,ƒ™™CÆj‹|Éå`ŋQ+ð._Bfv–Ý2ˊ Y oT AĀÁąf˜:yøÍĘČšzíj‹V­ÚīmsðĀˆ{•*]†M|éĢ"5ÚéKŠĘÐ=@&';Ëåįwĸþ―Ï>ĸžIÓĶcĮŒš™œžzíz)ų5ÚÚcœQĸâĸÛķm]šxï]―r™}$æ„kË”˜Ō ‰‡jų™ņĀÓÓÓųũđį*s?.—/ĢóōōdZÝîĮwd,7/\~2ibbb"î-áðVŊq?5%3#ÃKĶ֙ÅĪA#<5ē;F ÎÂOĢ!Ð*:^ÜŠ(Ðj·GÓp<;íNþ6xkYSē"įM.CĘVMÉ(45u„/ˆMXÍÉ-ģŽČJƒëžŋ]dXĩ[—Gsþ}ý|·lބ°Ö€Aƒ#ęDōhO'G4 qũ0SÖJ<ôܙӭ[ŋ3rTbâēŋüüģÛ-äjÔļq§NŅô[âÎqČ{Ã7išsĮïˆŌvíÚ ŅBZŪ\đ~}Þ˜ Ë(―E•Ü[5e™ĮÓvķÔ;ýũߐ}ĖøũŦ†‡7mÖ!›=ŧwÉÏÔ ģŲEXāĀþý͚·üÞ°#þvöĖYĪm/YķmËfB…Ģ– ˆ1ʼnÅĢᨔÏI˜nŽq%zlWd{ėÂJAôSÛS>Ķúhãš Ŋ…Ú‡3ÛĢĶ―„mZ@l*}-ÔZfYŒÕÚĨ„á2ēÕa‘Æ‹œôũũĸëøą˜áÃė߇Č9W._ÎËÍŧtņžÝŧˆBQ"đŸMš~Ý”žŧvïaÓž…•+–ŸLH`ŧ ņæ~wøÐPĩvՊƒöŨĻUŦzÐųãIé§ðzÛvQ§ĶĶðTnïĒ ķï―ŽUYbßÞ=įΞáI<+;kĸū?øČ•ŦWƎEĀM0$Ã9>öLÚĢG\ŌåËÁ3OčK]„BX`P`—nÝ*UМšz—ÞT–IIņ’Ė6ŌxE&FĐ'Ė…Ąāĸŋ'‹*Ÿ‚fņbMKv†éķX!sâkũÜ#1N8Ûôw0ú F‚ŧŌ|ĩĸ* ˜Áf™e–9Ŋ–ý-ēöĸ‹Ī qäÐÁ„ĮCBˑðï[þ~‹~‰_ðӏ._?‚ WŊ^‰>ī|ųŠļĨ·n&9œÂߓ ?xp?;+›Ų ÃÞęÝģbÅį>|ā°eãÆ=ŧv†„†fgfÂ@z4ā]ãĶĨ=;j$> ŸË%°žÝŧāmý{įNŪ]Øĩb•CûöžJøŦdĐŌĂģ22ŲĢ;y2Ą{tįĸĘ. )žž5sÆĒ…ņ|1Ðdv‹îHōƒ • B+…åĄ')ę·zë·!ĩ’lk*r\ŠČVk`WEnŠ‚ÄlšŨõL=eNōAü5…ÎJm’mģ­X­e–Ņ‚ÔûS zÔL‚Į óî;ƒ!yģþþŠ þL6Ð|Ĩd65å./ýü\ļqЋļm^n.ˆĄ0ˆm{#ŧE#–äfÅGžy# rÁJÖ$ŠlÂÁÆič@:"˜`—ķá>ęoōYo›Ó–•“••Ėn=yž=Ëd%J–x&4ī›Öķ NO{ô$ý1yt8guzJx™îįv#Œāąkā@JžæãŽJý†Jé^S·f4b„ĄÚ͌ÁÄL‚þ·Ļ‰ ˜â]îŦ@b_§/LĮøÆeŪĶÛZfY4 ĩvųõq“^ 2^˜öƒ&ļrN8$dQ—B)ÓÞ·ü؄64Bø*OUųÅ ģUŠ!Ã)ÐâJüh3D-KÓķfę3ŠF—ϕŲ‚ĸz%ĀMãię•ęÞ5i.‡ÎĢvŦ•šĢ Ë\žÔˆ 7•’@ðW4dôf™MŪžzíüH˜•ÓīÕ>ä.īLVßVëb ö*En:Ø[[éĘŌ·&ÄŽšbĪÏē dĢJcjęļM7ÉĪCĀjöÍ,ģĖē"ρaŠy›ēTxĒ\ðNÓøĩË·binĐŲĪō‹MDԁð€JpIû1ũ2•ĩä°k vÂ:íú呐Ĩ,"Īóq9Mø’åŒ ÃÔÅöHMĀD;æĒÓð ß)›Ýŧßá#ņ‹–ôxómÜZ•;8kŒÂFV_ÕßēqXwnPVEpįÆkäFļ^6ļ`ˆ/`^·›ðÅÅÖ<î›"cjÐpĀx;ĶŧÔøÚ5―T$ŧTfړ‹Fh˜üģwßíįpðŪ8ķOUlãææqõ–%ņVS *°ļŪĒë(WĻ}ŅĸEQėL㐎ZÕM€ð| ĒJc·EŌ,ģP+ĩų°lĀÍ:€lFĄĘUŠPÍuüØ1’īæÎ7lx ­hČųÏĖĘĒ( NÝš<šMŋ/G•(QĒi˖v›îž+ƁðųŠ+NuYhÅį lPp‰QjEÖŅ}ģŠÏUjųjëråBÆx›GžË+TŽøRÆū.Ū„^ž 5ĄÏ#ĄÓ  āF͚֌ĻÍHT _nØ(ŽZxVn6ägõFMZpņ%K•ŠS·ÞËQ+W Įcõņu"…ÓĻIģæ­ZՎŒ„’üi!ũķzš  Ē/pšÝ< 1š4oåįįÏĨŠ››—KlƒĪīJ•Ŧ˜:…b%‹ŋÔ0Šô†’ĨËÔĐÏËÏ ŦV­AÃ(čož[â \Ī›ZQ:w8‹ĻŠ—e–YĻ՞ļ’âEÂ\9ʁÂ&ûcsã†õcb†õ·Õ_C† ô,YĒÔüø…ËVŽ\īdéü_–)[ĶrÕ°ŲßÎų|ÆĖUkŨ/úuÉOņņeʖ5^ēŨnïŅëÍ-ÛwÄ/úõ·ßwĄō•›“ũŏ?ý\6$–'Nþyá"S Kx“ꉹã?ذyküÂśķlCčYƒĨËVDDF>LÕļņŌe+ЛĻZ5|ãÖm .\đfýĖYģ)–ˆŽWoþÏņ#FÞýĮþQcÆ.øeaėį3‚‚‹ø|5û›ĐÓ?ûfÎũą3ū(Uēt·^―]ķüÃI“ųËŅ―GŊ%˖WĐ\•K%ũ§ø…|pûÎÝŊ·kŸ“™Õ°qs%ë7mųpŌ'Ä1ęÖŦĸõėoĮM˜°qËķ‰'ģæ^ÖŽßȘeŦÖÖŽ‹—ïŅÜBĘâČãžSyüŒxI~QQīĖ2 ĩ<ðš‡tITēK~IģŌū6^…éÓ._šxâÄ j|ý]ïöëß―GofÓ3ũKd'|4‰˜@é2eÚīkũÛoÛþîøF§Sï`Ē5kDÄÍøŨ’Š|ä1cĮQ3öįĄƒ5jÔxáÅ‹+Öę•WŪüsųIf†îQŲZŋÞvâäÛķõà ニ‹ýbæđsgIÔmŨūcHhH‹–­G8{æôÄɟÔŪSGší.[2`ā 7:GóŨ‚Úģąãßgķ;vKŅZå*•+‡…5hðōЄŋ/]šX+"Ų„ĻĻ(Â3Ņū‚+Wþá~kՊ ïäæ ŸáÅ=zâAWĐJ•ņcĮėÝģgô˜ą―Þ| ß6<<ž_ŋwĸ―Ãýū=tØðå˖r%Ï?ĸÂ'S§SÆ3/ÝeKQ~―ųn ī՘­ĻŠ(Zf™ĩ-Fðžšø#\Ā${I[ŋ؍ô—TCų˜TbÁÁčr1ļYófm^oƒGü\ĨJ<,3ŅŲģgb?ĸó|‡F­;?OĶĘÏį)ŧxɒ۷m‹‹›öņd|įĶÍ[lÞļ‘ýĨ7:uîýVTW,_žþčĀĮŋf~Š^Ģ:ÁSęr™ĸŸK—N<‰Ncĩððö:;zô^JJ―úõĄ^ttôË/7ä(Õ%2+ę [·öęÖeÅē%ŋ.^Dp aĢÆïöíO4víÚÕëŨŪå |ŊYŦ&ĒbÜx‹W^­];âč‘#ÄRyÔøðáô)“ü8ïΝÛļŦÄO0ūƒÎŅŅÕŦŋȗPĄ\(ai>ÔđSViÖĒ%‹Ö­[·S§N\mxĩpŧC4%Ā,r6lÍŲÉSˆŠč3„Ú"i–Y…đŠTB–JöņE[߁ĶðTâķŋ—ˆĻ] ėÂJ΃E"ķŦVŽĀÝĢ,UŧŨHkE”âÁ1óČl*`Š]Qí’ô~?Ũí;·ÐŦmÕĻ]ûöũîÝ;sú”—HK5•Z…*ĶõĮžÝëÖŽElaõŠ•*UęÚ―gųō–/]LU.žãâđĘ.^ļˆĶåmåðāáĶ 8väOŠƒÛ·ïÐēU+ĘՒo$S–••E qÉŌĨŅØ}ôāAûŽCBĘĄ ™‘ņ„›†ÝxŲð‘ŧæöE™ÁnrđLĩpáÂó‰|].níŅĢlŠ+V‚ŒÍ'{FüeËæÍÔ%KėĨ°ÐýÔmũÆFXó$›Mþ-bf™eVa.yĩÚ\ÖKƒķiŊ­Íŧ$ũ ĶÔĐ>{í―ßîąxŅÂ[·’ïÞMs‡ PĶ\ˆfkI ÄŠUŦÖŊŨ/’.Tʈb ÚĖS§Ndýz‘}ÞíËÉkWŪÜýũßÍ6Ėų~.ŸÚņûï·oÞÔëa&AÆ_8nՊåÁėĩárCHKKë?` Rĸüs…hÃݔŧåBĘmX·&ųæÍÐōĄĐ)ũŠV­"w$=͜M°îïŋmG pŊ^đō^ĘŋÜėņĮ_yĩ5ŸÝšiSc4qĒŧddd$]ŋ†GŊÕh&ųAļ‹ûũî‘íðLÖën&ß(_Ąâƒûũ)/6šŽ’™ųÄH8^LLDX’dŽD‰âý&ī―cûVMrãĀ–ė°[–Yfī[Ē”HsîgÝ\ĪîŦ€#˜… 2”M°ČČšņņ >6…GėóįÝHJš=gÎĶíÛ7mÝÞūýîÜ\ü8RMŨmŲÂ~­VŊ\Ąuš6Ī ï?ðӏ?ķlŲjįÞ}ĢĮŽ;tðÐÖM(8Š^'\ûcïžĖĖLæ”üӕ˖îŲ쇐îÖíŋoØ*Ž$fzöĖ™“'ĸd'ŽŧxáÂãīīđß~ƒFâŌŦ؞ZŧasÍZĩÄg”nøÍÎÉÞģkn,Üß―k‡ŋā―”ÔüÁ:·nÞž‘œt!1=9ųXtúȏCw !|ÐaļqýúwsūAwųĘ՛ķþķrõZ’L˜‚›•:2·{íęÕ Mœīa˖›·ö~óí*UÃ&Nš<<&†ė…B-“"9 ęō[Í2Ë,EaOûRA`g­öîö ðCVbjŋ4ŽMI―‹X8Š_DNŧFŋņĘŦŊáå%üuüïŋNÐ$bp1ņüžđßÕĻq`ß^dĀ‚KoÝĒyVf†ËwFė§ŧwþNŽTrRŌáûŦ_`€›IóÝwïÞå-oo ĖQD‘–ö0fčāV­^ á…Û·n9|˜nĀóƒqãf•Žŧ›’b:ĘėÝ―ŧÝkŊÐŲŒÄŽãGœ={ČķnՌ?Õ|ōÔtaįÏ㟑Ö3vŸ kÖ=r8=-š…U˖îÝĩŊŧ|]Æ2%ÅžÛŧ{’ŌƒƒV.YšðŨ_ä‰qãD3’o$AįV͛€ï€Ā öŧRRîöũíĶÍZ„…‡_―ōωcGÓĨƒŒcĖeH‚­–`p (/zf™eVĢ%ŲõiķÂ^ z/… )œUĄ/S%ó Ũ­] J`%^Ã}0§ĐÃcGŽR 57;įâĨDi‘+ÂޞŋNˆũšũ4Ī]ÛķÕŦŨøæëŊˆÕšĩÕæ*ā ŧyózčdÃīõŽËŨа{Đ)<ޓúĘŌ”K\ŧv-éúu†+á]ĮģÄó‰Lnú)|4yJ‹–-ņ|ŨŽZAągņ[Ÿd>đæ>—ÄņãĮév øųûQ"qãzŸâō íĩŦW9āb8O·īË/Z1šß,xáŦ0CÜy\Öķ-›Xu€Žr.a šaÍYæō( Ö?ċžYf™•`šmsYĖąŠUyii–Mĩ―ĩs‹ÝÛŨŨĨ[@’)ËyˆËpð?HĸącG/]ūÄy°b°™ÅĖÆŊĮԚįnÍ/ŦUģ&%°S>žD0ĢŲČ$pŲÆŌā^dÂEÚK]]LĘcíj,Álð‹cø ė8f—aWĐ›&ó.]?~ļēīÔÔ)‰á>A/―f™ÁĮeôeļHŽ%e;U# [“wUbc[Q4Ë,ģš“ŦiÃĸŽE†UđP uŦĒú"-^ăM ü™Ū„rė%?ļ–ãGœ3ûkķՌЁŲ’Z[čėšÂ`­™šÄÄ}úiŸÞ―æÏûÁ­åXZ#+þ >ĢÁ(XÃŋ&dĖģāYåŧ0Îr…*Ó%1æt*p!ĩ!)ëa#GžũÞ þÖĸ?öÎgg–%7îAföŅÎzųÏcû…ŋ…Gš;ũëJ’á9UßðV §ŦF!‹‰ÚDQÉĻĸđö^Ŋmļ§ą^Ŋņœ:ū%Õãˆ`POė!ø“Ÿ[Ãí‘ÔóPŠ™oIqÎÏÞDßŨzĶËD„0’øðáÃog =“+I]uČĖ:glQ{ŊšCĐs.EüjũáĩvU‘ô—ĸý—ņ(Ä1I{žÞPd!ŲėŪîž^ëïáö“uŠØĮ)1fÚø–ōК†2óœ# Fd^ŨÛÆÞ/Ju]+âūxˆŋį՞ӊŒˆįƌļÞŨxHíŨŪnŠ4IUW„~…{íꊊŒPDW“&@ā}.ŊĩmũÔãŪļf`<7NŲ&ô„{QĖ}ĪöwäçŦĩW.·á 4ÝĪöÚ*Ou%ąŧaĪvMíĩ^· Év|ŧö˜TOŸs’Šۊ˜™ŠbíŨĐΌŸŧdžš#VŽ[”˜3cĀvdbLBb@ĸˆÁˆũû;j–æõūBRÄ-J)# @–ϐŧ FĪ Q’öZķŸįēý„=ãš.)öÞ=c "ÖÚÕm1ŨĒÔ=wĒÎņ`å"ØŊ―Û?Y+I<>2Āžé|øðá7<`ÏL /…fflÂKyé™ 2óiýĀŲ݄ôļÎҊ\ŧę`°ÖĒÐ5]“k…âô˜JÏtwdŽXOŨđ3#Õ5 3ĢkÜ )°Î΁N3#§OdĶDÕÉˈðļŠ"ú)ū’Vį\ŪųiėÚį}redLwũdD(Š D„ˆ™ąB”YŒüŋ_ū!ĻŧūČ`UŅĢHuN(”ņļ+$Bqꐠ‘Ũuyžö"é{{€øðáÃo—ėeŒ­ÐtxôŦš„īũę)Cß_˜šPŽg<ą2ķ{ސî[RCŠ/ƒ„}ŠsŊ\yÕ;CRœŠîZkÍ4€:cĒĪp@ÕÐ3€ŸÞÆĖÜ:ŌÏĘü |Ŧd(ÖÞšZT(0óļ%a6Æ4A‚JācGdW“ī‘§ 4Ý:kî ĀL?öq„ĘÃó 3 \Ũ#â·Ė@øðá3˜[]+Áą dÄtŨulŋÖŪî:’ÖĘSWũ|žÚXÕ]S ffwßęķ$ŪĐŲ{GDÕņLFĖ/FՁĒB5C†Ī™ų–žŪŠ#Q`ŸFRHÝD(§ûýõžAŪėŪÁh)”§NÕĀ|NeČ•$Ūë óĩ_įšÕp*CUõHjH=ĶŅU§ęûEßīB&ŋcŋ=†Įsꚙ`ļšHF&…JŅÕĪPð:ō‹ĀاJäpØëĩ"íąĄßï/JøķËĀkĸĻSĸ†]í‡Ŋ–†A@Œë\‚Ž4ŧ'RūÞoÜi{|øH-1=’_ųúóú"‘+,O §]}2ŪīĒNE0"˜q}]ĶW.įœ:ýýæ ‚ĮíĄŒũu‘ĖČČ<įXŊ=]5&˜+§ŧĮ WfO·ĐW}ŲΈiØĮæ~―Ķgj”ZĘÓgŒĩrf<(W2ęjŠ™yŠÐČĩî ÎÜ$ė^+AUĐã[Ž{zĘđč}™þ§ŧũÛ6æĘžÁp*Š P2t―ß)‰TĻšh)Ā9'Ļ•ųūÝB‰ß‘>Ób€īÞį I4 SĪpCi­íē§§ŋÝI+2pۚ]{ŋ"ĢšĶGRũ€Ęîē #ĻŠ‹â―  ŠĘĖžŠn;ŨĐSÝI†ÔþņÃ@wyuUžP\ŋfh^įǘJ§Î3Y03Č―f$C"Þu ðøģ(~rŦķIR‚ ;WtÞÞkMŨa`ï}Îéą$eteDÄuNuƒ˜žiRčtÛū=™ ~G>|øxĩkųQ–―BŅ=†tÛĒ"ŨxK‘+Nïĩ”:}‘Z{+xÞÁČĻ)˜DfƒĄ""ÔĒ"cÚYįï:™ä·ÞdčœŦŠũĘýÚķAfĘÆ?RÁNUO–4Ӏƒ\𩠠RŪũ{z‚ęūDŽ―kJíđ‹'És•Ë+sܞąŒSõˆāĘž-ãŠČGÓm?u SXkĐĩ’Æé2MSėîɔ$ÏHJéœë}ND>3ņÛņáÃ'nĶĮīđˆpUc+ŠlI‚Î)‚™ Á†EØÓ.)”CāZûŠËc­•RUA…kðĖkïOwJ{ŋîīÆāø°ũēÝ}"–ČsE"ÖÎë} G†íũđîj?Œą―Öc4ũĀĄÔíö†€…ëë cåqęPZđÎõ.L@?C ŧ`D(UGāB†s―CRė™9į"ąŪũÛÅTVŸKRčëý^™™Ë`3ãŊŨu âŽMŌ=ÂoɇŸŪ–ŌĖDætŸë€ØŊÝ=$"s­U]5{ėŒ€Ý1L·§o‘ŲÕRëŨ‘R=]U{ïą§'DR=Íû‡2ÚOó(IӃ_2€ˆXU%x­Ïõ™―Ũ―yOB§NÏ(BĀu.‰ãånÏäJãhOFÚiįZ{?þŽRëŠęebͧB‘Óm˜VæúņˆLEuÍxå @ŪTJÂđ.Ãûõę™HeĪ€žfČ6~G>|øäÕbU##wėîņO`ûœ3v*žŊkš#47$ ØÝ’NĄxĶÎDF€Šųú§ķį.,ĘpŨyúÁîē-…įmUîUU$!FDwÏLū–oÉÎ\ûõØ3žYkMũØĢ<‚Ŋ;eqfÖ^Š öŨÕu‰PÎ<Ę zJbîeĢĮdͰcåx(Ī’äĖČĀíïÝŧæÞ;wÕ!3}oMAŸ „ß“ŸdŊ >jLōŅÖČY§ßSŨũBLUÍØ+OŒIūöŦšIØōœ ãÜŧК•+5ũb’ņ4Ĩ”’|ŋߑŌĖx†wãyŪŦ§ÅðĀ$’ķÏuI‘k=Ú !"zūc_mĨcw•ĪŨÚÝM1rÓļŧxįJ>& ™™$OĘ1ˆîÎĖéē)˜U—2ēíR"šš Āõʞ@Û@Í(‚͙Áĸ>üį€7ķĸcĪ–āu]ĸöŨ?ŧ{­üģ ķÓŲSÝ-EFtwWSRčŨ|øÏADþ]O~üøņ#ĩ§ęŋýũĸqÎõzýŠHŠ:į )5C0ŠND<žąWŪžķ!"bÍ-ĢOoØÓIĖøÛOČėÂķsíîß`ŨQ%ߋU‡‚ÓMÉãGF%Ú°ýßóu†UõÞŊęCƞiP’úE„4öxĶg­ÕUßۘiiļŠÆģráĒnXātåĮ:čïk cŠ˜Ä]ŲóĮŋĸûŋüË=§ð ÛĸüÏĸå_ĸõýöî=:Š"ÏxÕ―Ý~Ķó"!Æ0<‰"H\‰ʘ5‚‚:0>XĒEÁ°Šb N$žŅõxÆt˜Ž(#ÃjÁeÁēA!ðH óhŧ;}oÕ­úmú6ĮģƒÝ™äxæHzęs’ķđ]U]•?ūįwúķUO3š–‚ĀËÍÍéna‹›››Ņ€ÅbŅoš_ ·þūBŊĢŦDytþ.Ý9ęhQŪãîtDÝ9ZGŒ)ĄŠüĸ…mčP]ƒ €Ū)‚ `Œ4QJŧĩŨŪᔥÛ7QšĸÁAĩ‚ "jADÔ ‚ "jA~z$‚ðSĀú/F= čŋ€šGD­ ? (|Z4ęA$ Ã[jaĩ‚ŧ_䀘ŠBđ'ĀýĖŊč­zŽ8#JģŌ’Í„;ˆĻ)‚ČYŊŨ{þüųŽGMÓŪųäøøøŒŒŒÄÄĈ9J4ļčcGÏ~WįQ‚îYG%p9Cģœ}“MzڊĻ„˜ÞÜîÔĐSĐĐĐųųųVŦ]ÛEĐŊŊ?}útNNŽÍfûáÆu^ŽŸóŸ―äË蕘ž/K=įĀ'€V_{݅æ#g―Sē;e "j!H’ÔÖÖfąXrss{Ä͚S8p Ïįóx<‡ãŠĻå€Z‚üŒGq:Ģũv'›ĒïZ.\õü*Xïü#Ãc #āú–BOPß)Ž=Ũžņ4úī §l2 ĩ‚‚Á`GyؕœUýÍ ›929“’S’rg=˜â*„`“Ųi·Ēŋ‡ŦþßþozþŽ.åF|||sssÄ9Ŧ‚2ėķŦl“ĢOP °[ĖĻÐ`;ĩØŽ‘ZĩŦØh3ŅÐxüó:ÉU˜“þõÁ―‡ ĖpĒ($Œ“âmī(ˆÛb‚[āĘcÔúOû|Ï֕ëŽų%+G '―øö'dÛ"wÁĩՖ—­hÐļŠ‚qôû? ―ŽlåÂŲĄ‘đE’LˆqõTŌ"Æ&ĒVbčĒ|ý0ÝũáŧmÃĶĸ~éœĖÐļ{üälĻL#ĩûķVŊÚxĶÏY4ï#0@$Čg’?ęÎ!m/m9 åÓķ•UlðȐqe•‹ĶĶÚāËßZąėĩó ÆÍ|ō‰ŧŪį<ūŊĸkQÕŽÉ +Æ H$”ĄHšr* b •Đ!HĶïãÏÃįģ"ý0C@_ū·áũ'ĖōņkŽœ,Y°zŅôą 'ü*’ŌvŪzĶ6Ū_íĮïÖ֙Ÿ[ũĘäQéjĸžüÅĸ8ÖÐ6~^õ/Ýõožþ‡K{Ïûhöšß―õÂsoė=H°xÉē{nČ`h†#$ý(ƒhQŦ1ĒŠa‚ˆZŪC`Î4Ŋ/0døõ)IQ $ŽŒĪîO3[{ïōõˇðyÍ} úl.Į‰>q”(Ę[ՕŊŨügZņâĒþÖs-Еë6Ĩ(”oÜ_ïQ.·Ŧ-'ĸúI“óö_M―ģ“s@j[3ó~ĩdæ4ũø—ķUŒgūítķŒą.ވŠj”Ā RÆ9Q QĶŒœĐ 0eÄ―Ï.|ĻøâZöĶZŸB4†0`Š)М2ĨžōÁņCwéŊ6œûްü &|ų‘Gū}xþ“ĮdĶ$8 YYŪÞÉ,čņ~ónéĶú ß6ĶįaŠQĘ4J(a<ô_UUĢUĩŠ c ŠZAøįŦjã895íķ80súĪ<—Qƒ“{wũžZ>*=CkŠo ÐīÞØtŲ\Ũã€xŲdž}úī)î1{†ÞņæÞĸÞsh5Œ˜ģpŠĨėØF°ŲjĨū†sžķÁ‰N% E5X]œs?Û_―uũČcú%€pŦÚč+BǜáÅmūo?Ŋð‘Đŋ·é‘dÄ€lL`$ĻĻD ŨžĀ8ãLâČa’BŊĻ FFŠY7=úúÛw―·ĄtņĢų‡6*„š5fēX―YūΚüú/_Sšs„€AŨį́wRÕę+ÔMęųA `}Į/zéĐŲĨ+Ölܰîĩ/šˆŒxÖĻ)ÃÖðėģËŨ­|neũÖŲĸÏ(G:āmžK›V-[úø {ÆĪëÝę­?þŲîvų/Õ}īý“ÔÂIũäœ}Ą|ÁĘUUkķÔī4_nžŋzɌaëŸ(Ųö—Ø`Bĸ0ĀHZ^Ņ€Ėóó,yåÕu/Uo^xŊˎ(€_Œ€kĪ―!Ė5ÔŪh# U aÄ –ĩ=›—―öÎÞÆVÚ+=ÂŪŒĪm›~ûöÎCē5Ĩ―đþÐŪũw7j>ÜųņŸLƒAę˂íœqÔuĒŠ„ƒu("Få„ėįŨþņķÞ?rö’_Jœ·ŽęĶL3wäŽZ―ņíŧ|ŠīdõŽqųŨĐ˜”0ļęĨu—=­ õŧŧôæ‚Üū―6ėÚĸ•5ÍĩąĻøD―ŒîįWĸ―—ýôšþYÎ^YũÏŊHN‘û>ôęË}Þ ˜™Æ‘ŒqīĐvyAQšr͐<ėåõŊlßđįrsûˆŧÜUrŦAQō&>þLa RgfÁ‚ŠgReÕ>aîŋęĨņÄŌ_·§ũVĻ6ôÎđ•#í&‚úVĸE#öŦUå“\žē°Âüþ>ðúųĒ5hĮ_=rųŌĩÓūųš”ž{žž‡úĻ ūũĐĪ> âž0ÖQ+ĸ”Gb LÃæ”ĒiqŪŠÕ`1jé•ýHiFUBŊ ™ÓŠK͆ĸĨQĒš>hėÃyãįP(q%HpŌuũÍė\!Úð1 Šļ㈕„~DÔb$é?ŅV„@KČōoeyIÂTU5ŪÃݒT˜51ó_&ö%A%~@A–~eāM·K,Ôȕ3ēo.éÏFÝ7x4’4ĒŠÅ§š5'—3JŋmęŽņ8ãĢGÞĀĻĘĪą.Zcî-ÖŅ–E\™„ŊĪ­Nœ˜+1D–厍fššš €1ÖÅ<€ŋ―ÖÕĸó!nūĨkT’$ †ĢGZ,–~ýú]õ5čĪGÛuô’Ýl3()#^ęY'“ûUtð”ĸŦs­7įĪdÆYM˜ƒĻjĄįãœ;Îššš“'OšÝnģ،Ūm„úúú–––žž<Îų Ã$ŦäNŠ;uŅûųiĐ>Ņ&KÔ3`äõ+g[ŌãMiŲ !ņ‚ İZ­ŲŲŲáíē8įÛ\#Ÿi€,ˇĢoßūvŧ=âlq87ÓN(=ãņÛԎ0B€z óÞÃPwBGÔĘņBŒDājšą5ļA‡1îdkð&ŸvÁK} @=ˆŲˆÓãéNĢ=w@"jc“ îŒõ]Šē9 ʀqÔģHd,wóæØĸĩwĮD0E4ĮKĨˆërM œŪâ=ŋܕZĀãg^_l{7āR ĩR €ÔH-Ïã8Ū3^îų–ŋ2[UŧŦķwtvwŦę™ŲÝ3sð#3Óݙo™öĻ“‘·O#IENDŪB`‚doc/images/ifw-ready-to-update.png000066400000000000000000002225031325366651500174050ustar00rootroot00000000000000‰PNG  IHDRÎbðZŊr% IDATxė•OÚ@ ÅQĨ\ŸūĸņĒmUeËV–eB3ĸč•"Î5ąxĘÚe!ĻÔw°fėįąũGĪĖfģ<Ï―ũŋ>jŧÝĘLRšQ_áÔÚív(ĩų[;å>ígݍtþįÓ]z2iō‰Ļŋäž|4ŦKC…ĸĶ|Ōšœ›ũ>Ïóétúi8ŽĮã,ËÞÏ:YȋŌņx䈌ü$xЋĪ3zŪūē° K˜ĩöû―ZUšg<~>Ļކé_ä#žM\ÅÜþ|tW1.Íþ4üþšþ|ŌÃ~ŌŨÛņ!=8Ÿ,Ë&“Éh4cBÞûĢŊEWį\ œB-W ›‚Jņ,šƒ†Ē]žÆ”Ð…˜xŦ6þ„$QMðA„āžTKĶųģXƒ Éæ>||Ríų4[D„DWË4}čĸý@ÝøB>FÃó°| ž|Ø`ŒTUĩ^Ŋ|”uŪ4f1ŸŸŸ^ŠâíõÕZKN*UĩčJgnĪkY–‹ŲįWŦW›Â*Ņԗïßū_žįÅōmI+q•"lBÍҜĄ‰Čc7á-ZÍđ ƒXI 4ôâ §^TqhÁ§ōqƒįķŌ”ÎšS|ÎïØHų+ųXį‚wåCÆļųyŌ&:[i>x­þ/ë9’QðĄóU|XšĨ Î 'ūüXhĪTܜ"AōTaӅ_œšJÁ0ąÎÎK{íũø I‚ŋ=$ÛóAŠŠÏš(Éþî|(vāCž?ō!i>ÐĀÔĒNîgûóÓojŽėđĐėxóį$S™š‡@%lNÆĀÃ/’ũÝ

dĐĒ7ŅĖĪ=m؅&ąŨü* F(+ĢfŊųvįln%•…鉑á‘'“3žžÚÚČ$ŸnO<(M54qŲ;7;;7=ýfvaIM€?įÅAŒDc}ð"đõ!Āīq…žbÂþ#CãÛûų( †ĄŠ~ïüĖėėĖôÔTÏLSR6ũ[æËņĄņđ°•ēõ†­QÉĻfĶ I‘ļ—ÜĄĨmýóÁ‰į™S1v‡úØÛm{}>~ū0O;Į^\í’'ŠÆ‡ęÆGÖc ā”ĸĒ>ŧ ÃHd`šĶ‘‰Ü))ýõ>ĩũƟB‹ÖČāëãGŪåí_ž™(]Ũ Ôaefpppll Ðáå˗}}}ĄPΈ&ÐŌķaFĒáWi}ïņ9zD܊-‰ĩZðÏ[Ķ…™€§Āší1·Ÿč$44˜!Ō`E#Į‰@īŅ@ŋ˜AN}ēuē•íšč$Ā$ņāÜĐ>†Ðýŧ}‹›}LúbÓĪÏ; SØąflĪ,O2frC^~Z|úō ŦÂk‚8@ķāD…§ßužøäðÔÖf`ī·īc€UM|ëcĨ,%zíœ3?ĸ”ëVÏŲüüâšÎŲ5mÝŌ}3SļēžēHæ ã{UWp°ŽķđĨĐķųʍUÞH%-_؈œÜė#ƒúöųÂ]Û|ýhęž]šĶĐŠŠg6ļžvußĸä§ūØöĸÃũĸųdč_#þvÝ5đûģÁšIĩ “““n·›eYüÕWÅãņŒŽŽJ’„ē™#w‡zNšvŋVïßM1™áŋ›ōn:úó.Ýn’$Qۆū ô ŸŽ Ķ}xą1ē"%@YÜ%·Î)Bķ&nĄ~ށ[ö4Č €ėäėúāYxrPg‡úč {óĖîŸ|’ï~ĩbmnr>ÏŅ―Ÿ:q!Š'ežōû}Š—ŌW“…ð­@„Č3ÏQá%ĸrœ­”9ïfAmŨėŌ +(2O–|E ī‹ĖŅQ†Sõ„,p,/rņ°/#HԃŌ&SÆüoøEÁó ĸĨēč·•{Ôŧ†E.ŌqŠĻÕý††pRmÓĪį'+œóLrk=eZ”‡ŽĮxýÁHLÖš―Ýö…Ģõš?ĘÂ{Ļ"í_\„Ö$8JSy–æ. †âœĀÐ4ÃŽú—–‚aAŪÆÓÃ0‘@0N‹"óršVŠYö/..ÆX`éfB‹Ūžū+ĐÐÚvbÔėXYÃå6đđž‚YfĐ(-(0@ð|<ã%…‹ŊB-ý+ŦĒĒolh7ÛŦ\ƒÏuÓ bŪDBâ(†ā‘&æOS—^ķLÅó°Ŧēģg6@ÉŠÆFŨ^ІŪJá`Āŧčģ ĪĶHqšE~ŲŋīoĪj;ũ}ˆœïôū}7ûļė‰Āųʑĩũ0zėóeýˆųühę#Ëō.HþĪWU.>Ųóé_ÏĸūŅųsß7Wĸ―Ž0qpûĀ^*QT•ðIČČČČÔÔT4eYVŽãbą˜Ũë}øð!EQDR’ĪtˆŽīßĐh(xĝŸPŧ_·Ÿë=#ü—Ūų3Ξ‚X< T&!˜|ŠĒˆĖÖÎÄ­ėOļ9Ķa‘ķœėĻėėC‰ģÂũFB@]ŧZvb(KVŧąC}$>~ËqxïūƒUÝÊĨ?ëĸęDþį'Kέé‰Ũš+ŦkĨ…U}Œbq‹OËëŪ†Íĸė~MUUSCUáÉĢUm}4ĩzũ|éÏũŊmšð­Ÿr_‡(G:ę.ŦūąūšŦũĀð{jŠŦI”ģí.%ĶŦĪjšĨÆúŪV–^Ũ <†aÎ=štžĒíÅØĀ‘yĮN•õļ'E9]hÃøœ§ĐÜųÜÏp4ÍrĒÄŊÞn,r4·;*Š ŽޟX‘Â'öïÎûüh“ˏŪô_n­pVž-={Ï3ŸÐĪáM•U Žš[ßzđÚϖ”Ö—–>|ę\Ũļš”/·••žŠ}ąôÍW_>š7tiüîĩ“EŽ:gEĸÄīþ›ņ>GyeeEYýĨ{ŦœĒĐ ~§ĢÓceĩ_,3,MKŠ8|ĨĒĩįIÂ4„˜·ĩūâŲÂŌŨŪ 55Î/N]šą’ęõŽęËžZėbc˃g‹‰”õúmu]CŠ.õuŨ8œ%E… ÝĢ1*xąäčū#'*ë{"ēÐßYÛå~%ËÂ7ĸŨņےģŽēōęÆ+oŨxeí]KC}KksIqáąÓåOÞý0nŲÝhöONK|ČĀ$CĢĮ>_čīw5N N‰[;ϗ}ÅĀėųēOÐĸŽ>ķ™ý7-V[Õq…gî―oŧÏ~ŊxáŲ5KĖj'.Ŧ„B)ų‘”ĪI)(QŦR–P $eWKƒō§ jĩ*R[)ü+UĢDT„TB™%”4(8lŽãíūŧĖĖéyåčę)ĩŸ`4wæ|gū9úfæÁ―ëc„Cāœ‡R&óNCÚķ…ˆ{nĖó⁗ Y­&)ĨD !$›ÍZ–e~藖–B+„øíõõÂAŪļ‚5ŧšƒĐOŊÚöŌũōĸy]–ķŽŸÜW‚Ð*Iã_Ŧīü!s“Õ#­A/Õ8ercýŒ%?~þ›Ĩ`Z3ąbĪŊóŊzcŨïþpčÔu°M_oOYŧaë‚fãl{;Đjßš{Ļmþšå n|tā―ÉēĩOŦ;rð―ŧŒ„ÎmU2jŚ—O­s/^3þû›·o[6ŦóøßÎwåđTåļūvFëp·Ïņ1ˆó҉cĢfŪÛ°eÁÃMÁÝĸüýíƒM=ĩfųĒ|ĮíŨ…RĀļ4MyãĖÞ?ūąëũŧOu īΚzåܝýáģïwMck§=ųÜÆ­›ž_qáb§/Bb˜–yE™”*ôˆĮ€Đq3ŸXŋiÓ ÏÎëđrņŪŠ˜ýÄ܉ms^\ũ\M\ ŠB!ouÛ{bpņŠWvžķq―ō—ý'Ïï\ŽÕ>ēmĮķķjĸŲĀC‰þÚïūþ6ę|īnŋð|ņ0'īŒųqitiô3z@ŠN ę„Ģ˜eã,*ĮŽ–„ }åúuPöƒŨž>F·“õõAiÖĻĐĢMĢčØqtt3­Í‰ēJŦn„2 Ø PP™m۔RhKJJ2™ \ēétîYÁkˆ ô–— ņX™nWHŨŨ·—Û™$‰—ĨŠ ÃaQ•° ôƒ!EGëŽųĄ…°čŇã_x[a6ė N]>ĪÕ§ļņĒŽ#õAyúS'/ōŪ[ôĒ =jå/Ŋē“Ę1­đLĸþ}‡UÕč†aĖĮ―Ž3ïïz}į›ïœd‰* ÃwEeËīņĩʄm†ūÏCn%ƒĖÜŧpæāŊ~Y`ņxŒČ@*#…QøĀš^`‰0i›’ųJ *ėíũ%h,V·?ĢŲD2F­8UJxy_o„,ð=ģrĖâŪ^ŋf՜ķ:w0oĶkÆOŸRĒļ‘ŠYĶ™$Ģ*ôÜž+ļëuŋĩgũÁ“ݍđFčsÃnmiĐī…ãxnHrÍÍ%TV44•UŅ[ýŪŊHã„q6ū(j€ÄžŸ^øüÂĘkïoíŨĸ8ói_į8į?Ü·{Ï>™Ęeã&B(%ũƒdîëK–Ū^ŋú…IiŦĒebʁ―œ8ycœYąžÎ#ïüųõŋ=|Đ+•NDЈdBj)pB((Ĩ„Räĸuhïoví|sĸ1šNa!6a æû>DAPč8ŽŸĘ•Ä~ŨÛ8ĒĸÚgŽ? 2cmk‰+j'2 ŠĒþŅ-Ņ?ڊ_æŸčMÐn9:ŋŽÁxíŪ"—ęühfúŋˆ‚IŠtBGˆ‘"―(f+>__ĩ>EkááúŸŨŊß.―üĩ[ūZ:ĖŦAĮ4ÓąāŠđĶŦv&6ĐÕJ&5 ß øI ŽI&“vŪëBkš&>;:sCy3ŧ#"QžĻ(OU—'+Ó$åwõ™Ņ&1uBTĒ;x‘ágQQpD)UĪMs1ðõC.ÆÃŽn19ßCĖ(baeõgT9vĒŽ"ëc@4đ–I4å~õ)\U6Ĩ>sâøđDŪ­ÜbʈĐÁkû}8ýŧK—=57kQIˆ Ĩa`KBÍ0Čŧ.ã‚ÂT€HïvOžŦ ·óÝÃG]žlųÓsK 3@)°Ô’Á€ãŒC˜ŌršÎ5Ā•*6yæ„ãoí9íÕ|ĢĩúüŅýGNû?Xý“y‚Ā“DIE)0%Ôo°ëVoĸÍÎö 7ÜüäÝ3ŋģj铏Qæq"ĄHŽWxe ðĘ0Ąq;[Ūššïöq·įÜ囙Ķ\ TšyĮó‚€)Š āĸ@ûāþA)ũ=_ī=ÆãųŌ(ʃĶEĐZkDâšH҈RŠķ†ÁTxïúč‘/ŦĖþŋëó_ÆĖ­' ŽâýfMš>˜j°ĨČ˰ā.Ŧ%J°UÁx)ķÔJ[-—x-Ē–K"°eoģ+,Đ_ĀÄôŅôKtd› ĩ/ý?LN2ŋ9sÎėã"ņ€ã8žį…þˆĒˆVČóÕxėû\ð:“úUŦüĖÁĨÅRx^h60í„°\.3 ƒ~ˆÝÞÞÞÜÜ ÝBĄ€ÓH$XŪ―{ÛČӕnüōúðâj;YœNė―bÚ äó‡aYŪ?Ú)ÍDږv‹Ę#Ąĩlô?Ģ:ĻŦØ$P˜ÁKĩâší`Į{Iî™ßËĸo#ĩ82L2ūJþõ>íF„ēGŽZ?ļâN:SâæK'‚ŒĖ.ųĮÍÖqŋŨm~j C›ÍJ 8jŽ\|%‚o›Ē•SĖq\įĨ‹>Ķô\ū~ąė·ØŽīoÂ<:XÜĘ}Xw…b-Ø)$Ũ``-Â1p\k jARšr6Ōa6ŊÏg7ęt–ŲŠ|Օļttzč‰áuæ”îžJRó|6™Ëíbæðóų"éˆė5‰Ýz Ļ™ėuŋ&ðŅj*ÎōB:0›,„ÂŦ –Ý\˜\Ý(tšr§#$BôãGÃIbÏl+É‚Ēˆ+ÏB"-H2”áÖ4˜Ú8ëJõeÚë&I›DķO%EНíâætđîÝɕøþũž všúa3pĢ`ļk>_i‹"Ą{8›Ė_^*|qĮ‰mĀãĨˆūķ_ŠÎx›Yđ§Īփ†įz‚ōøí˜;ōķŦÁ ŧŅ(rRo2zĒõ°_č›(ŽĻðŧƒŽ"gßøŽv›X]SsUN–šĮĪs6{ŌęõÄådd{eúMÎđüÆQ5QÜĸi6lŲ!6|€Ē‰@ŒĒ€€|Ä; ōxb{ĶÓ}û9 ų{ø8ššę8+H)FåęzœŠ{šŪYxĻÞޟtsĶũ=ą?J›ŪJï— $)qT 8y˜ÄĐŠéû5ÍĀ‚ô5üā9Ųl6dcðK“‰Úš.‹ÕŸĸþ҇ËOïæ?ýØÕĪeYæUĩmËãjĩâŸSX.— t]:™ˆJl4møõŲ/Žï~ýĸū[|rrúCōj[ 3)‘íĶĄ=BG%3Œ åtŽðI@Œ€íĶ@~öØd‡<ķ`“Sč&§KøœZyÔËĩ ?Ö?å0tM(óŠyūÛšČķMŨvÛĒėšØŠíĮķ-2vģÏ֛РýÐÁęõÎŌm Rų†}úŽ ÝÐ7ĄČËPÕÃØÐĐŠÃ8vyFNÝcØæ‡ÁCįÕģåÏüŋuëö;·ï>nwϛ:/ Ķ?$V}߅P‡+)‹í0öMSeĶ>rŦ†Œjģ†ķj؍uI—ŽĐoŸę ~Bĸï―wßĸęû’TwãØŌĢÝØVYFzÞīmÓTY^ĩĄÉÁŠÛątŒĄ ———åķŪ ŋúœÁöšŪŦšÞ‡Ų†]Ha$fĘ <ŧŠƒoëĶé†ĶäL]Į°%þ6<MÁ‡ō›~Ũ† vęĶíÛQe^`ílčŠ8W^ôPT4ë p[nē"„ŪoJ―r]4dr4đö#ŲŲ•8ß}:äGE}/âhŌ‚; +1ČäýR2oÄNu™ž_Ҝsų1Td>?ō߀ŸÎvqqë―ą^w}ÏwøÖzÜíČVôüü eĘfÏø+ÛþÛ*$#Ʉ4%†ŠÐ|Õ&ÛdC?(ųÅ_/†aŋQīJd …ĻŧCķĢÁâvž3)Ô`äŲÉÖq‰<“ÚåöLōƒļE č1\ &ô$?jóƒ$ü(G7Á}ĪĄß2·3摁ĻÖm9ŋļÄ`eϧJčb­Ÿę+<3@—Ē(ûņ?‡ōŅÇO[Ū7ŸlģŽųAĮ^YfHįÚīŸčŠJ‹‹ŌĄČEsÐ/ï|ü؃GëžĀÓkãģ3ž„bėˆÃ5tWZ`&KĨ $ž%S邁æ#šŅA/Ežĩï*?ðęŽŨą€“gW}ˆ@æųÚüDû“,ũG!kÆûã*EÓýyÕý’6ļü“ûė}Ģ\ïÃË/qm‚o4û“ûŸ"ÉüwųĄj&?4;;;ã}zzšZ­–Ë%N `Ë)ÁĐBė$CīCˆK ĩŽð5ķ@”†ãĻ\%n­LŲIkuLF%šKŒŊhO4‡CéMLōãīüæ‡ÖŊäĮôęŅžäėóųÁ‰Kķ;pšų{™…Ū+Öļ&ų“?ŊtĒëų1 §ËģĒØ†b­ÖâGɓüÄã%ü8-ą‰Z›?Eq%› C%/ÂŊŌüĖڟü(9áĮņþLōƒqc~$“üÄĄ7Ώf(‹ĄĶų1Ú ~æïsøANNN‹Å“Å>Čӓ§G°‘cþ[ė5žÅŅâøøøäø4<”ÂC†JÅ# 9i‡GöŅŅĘķ0 a#ÔĒ@%FV! zT>BSƒi*ÏāLt"Z U(r&ų J~]ČũŲŊįGāøÍį1? ]Ãz’}t9ü`~Ļz~䌸Ņ?äcƒ€Ą^^Ø?ü8§0ļNó#ŋlņĢ\c 3aō•üčqr”€NöGã―ŒŒ„ėôÝEûƒLōCBˆ<ŅzߌÐŪįGČ1?:ėëō#Ca>?ņ%š?8Íō2~Ðßþ#ũŊãððPF,‰g2ô͕Á–ßQéÉĮ$?q 6qjÔ9SM&ÜŧwÏŅ9ōÆųÁóVņcĻğōcϛãGvėÜûïÛ9—ė9üØiĻ·!\Ϗ™ų›}3ŠęJũøw·ÞØLÐDQc·$FLeÏd4Î$fÐQP3‘˜5.ĻŅhÔ$.#jŒ’ļo ļEÜÂ"öFš}énšĶũ…^čūũūKwÅôƒyOßžÉÄrÎŊšŠsŋsū•Šn5ĸþųpü›įøÍo @ Ô"’Z@R‹@ $ĩĪ@ Ô"âĸĸ1 p‚<Š„‡ 'ų!Ÿ"āဠøBĮ0øW€@ā€x˜Á<Āq‚Āą^ËumÎÞȃŪ—1$…=pîĸką=ïŽczYYÜū­ Ũ*iŧOüÅsrîŲaęÉŌWOî8|QŠģqĩÁ# I-‚ĀØŦ‡VŒ|qÄÐ!Ï>;xčëc&špÓÆāðO”ķþįõkŨ%$•ïþRË2ŒÝfĩÚðĄĪL9rÞދ 1Œ`Ėu ^9}Ųz#ˆˆû%#H\ßXūgÝĶč+eNŪïĸĄ §ÝjąØî EēâÔ]#GŽ|nØPnvoŽļå`ēÂęÄpTā,ŋrxãķäF­“āĘyīA ĐEX JđÂôôČ7^ųŒþŽxÕėđYuZũՖ y"oŸÂ~đ·ōBŸ.žbJR|oo‘HÄĢøNšfœ NPBĄïŒË‘Gâð 8É7Ö§―rųá3ų|?ŨFōøîāŪŋŽŧĐ8í2đ\Š42Ž[ ĐžU­Ķŧ #B!")ž€óũ îÉNðD^ÞÞÞB‘€"i–bsYB‘+ŨéNîÔU…―bûŅӝ_ ‚âyųpļÏt+lzđ\. ōĮ7Fĩ7ïXķ1ūˆâóļN―zvá<gū7OnƒbhïÚÂ8î0æš0Ŋ§ŧËÎuÁYų܈ŧóÝ[ܜݧEœû―–IŨąkBōaļ2#Ô"0ÆūCVïËLOZ2ãOR…ŅŒaAāw Îۜ<)8$21ÛÂRŽååGwnþpĘÔāĶïKÎą18I0ř?N žŧôëÂú6ÎDjëē·oŲ›tŲÄ9’”CÕtlĸķ”[2·ˆcéÔ7Øqâԙc‡·î9žifpė…Đ1S'•ĒķáÝ^fāÆājŧ'v|pÓÉJGl>ŸSv%)288dņúĻ;z'ŅÕĄĻHýzþä‰ÁkO\/—Ü‡Ä4ŌĘĻÂCļTÓgF§Þt`$ÆÔãGHL8• åĶ詜äϧp‡fF%öž|š+™ētSJFÚʉïāĀÔ·ĻôŠšÄĻ­a\čY[\hïÄp ļ›*iĸŠŋ…O™š7áŠŅÁyã\'‡9-.2"ō‚p“Žötänũˆč‹*·;IX•ÕkÏžz41ýtüÁm‘‰­6–$ÉöÚžoNžžqĸmgŨĀļOsQúįŸ}ĘÕýÅŠM’f=õûŋžF ĐE`,fÓōV™\ „ČĮ›Ï)TÉŲ!Ÿ,I(ŽŊŽ+Ûī$lMd:)â5—gė‹đhítHÄĸ|Öņ å-ĻH‹›―<íZ~vzŌ–Čģ ÅģÚýíōÍû%2ģ7”^:ūa·wïvL:R‰T š–ú“ieNÚp~ßę?[%.Ŋ’)+ŋ_7Zč–38ÜN’-UŅ["Â>ĸyø>qî•cÛWϛģSJóõõĸō·°'.‰K4đôšĮc$Ų‡NœĨš1+mmØô#9-T§&%OFYãđ) ŪY=ã“Ï/•Õ7ÖäoXōqÄĐ’Äĸá[ģAï°Ų__Q}âĐãJ°yCæöu‹oÏĀy<‡đaÜ?…­;|ĩ°ļĪäæÉœâv+ŪKވ7N*BKzųˆjFĮ—āēۙÛÖ.^ē3“sïÔTŊų8dOLR‘\ųýŠ/ŋ\ĩí‡3†ŨœúņŒčŒēŠĶúß.[K „Š[‰AĄs’.UZŽÆËĐYUFwŲ$ĩˆß‚ÆĶĻð/ƒ§M=šÞ6fņŪũFķ–―ûãeïå;/TĪFųÛWO7ĐaÄ;aŨō~Ž‹‰>īy1iÕ4*•N][úĨ”†ŊoŽ]Š­–l[ðšehŋo~>þ™Îƒ›%uĶķĶ‹įNõšīōÏÏõĨY‡M8čïūž@ŒīĶðĖZĶą &:Ö(}2­čFfęß^ó—ÄîýЊ/ Ļ ’č;|ËŅņåãO ĘŠ.äÕķ]‹Ų-UÚĮĖXWŨ\ýcÄ"_†e­6öÍā°ë9ųq'Üĩ~ūÐĪjiÕ0ÞC·~ŧŪĮĖüÓC;k%üÔdplIžš~tU?ū9íðeAáНėäĢ C§EœËōyjÔÜ / ĸhŅđë9Wb’n{‚4Ý―^hÄų5ĐûfČžģ ífííšĘīˆƒžÁIģā7•Ĩ„ŊŠúãÂ-_Í|·Xžûtņųޜ+?&Go˜įOï^ŋaŲgâJTĢgŽ)ĘgŸÚúBÂj/FóãÉŌFmčú„ú+Į_îË+ūSÓÆčšK”ÚŽ—ƆlÞwJRrųĢ1­V Ô"~gXáĀÁý˜z`OĖz·Ŋ7ŋģÃĪt:€ÖĮnš4cM 2­MÝaoŪļē`öGAã'­Þ‘äz'ˆ[Lšķ–:N ‚^FÓÂ_ë=–Å)ßwCÃĀ“’•‘™œP@ޚōļgŲ{iĢ ãîīhõ % 4.°ŋˆöé?õ~@kLÅÅžŧša$†đ`éŽ.€‚Ã&ŋýüā—ÆþyÐÜÉ06ÃÝR;āþOĸaâã|fĀ“Ð\‚PUĪ/1í/ï_ū-ÁŒSŪš\ÍTW|cë0č j-`ėŽ%ĄÓŋÞßjvÚ·T&ķį4Ī 5<1ėŦM;Μ>9åÕþ†ķŌ+?ûkÐļŲkĢÛ,,E8NËï4ą!ï}é)Møy{‘8 ÚrÖŊø*_4`Zðøþ>8‹ãŠē Ë]îëŦÜîÝÖÚĀ{cäëOøō}û ؋eÚnmģY°ôýËߝēļ°ÕĻ·ŌMíÆįߙúŨWzįžĸþƒ ïēpEeŧ‰ @ü@Âà ‚v‚Wßi‹ÖúūBMYvp厝;FðÍå7~Þß?}ĩ7ƒá”Ā/€jZ8ki&;h߁č!úœųóÂq>Ä`œöš ņŧ-J0 üŌčqoûĮĶlŸúųpÜ+CY§íŋ‹<įČâ$)äôïŌ0Y‹Ú†Q}(°Ųđ-†=Fb\t,ÐÖ oÕŌO ô-2ąFBI˜ÝjËNņ ŧEaģ:1ʎqzKÓéė°Æ3›uF€^$i–îßļô­ŨŽ\Œ|ĒåėÜo"d—Úē Ũ3`.āÉU†F~ē|ÝØgü€ð‚'ygo'L[ībcčŸ8Iäq?–Ö]Û7Į\Ž\wôÜ[ėÏa+"Œ$đÐD'čM6Ā(ĄP°4mŪxü… ũý’ŽĶėßđkԞ́ūš[·OĐ?vîM:sÞrΝ Hh;Ŧ1™i`ĩŌ†V…'pā$žŦė­ÐeËĮ vĩ.|2€GxŽđXpý|ÜÖČ/>*zúÅÝŦø`ŪðßšÕ"0'kï$F‡Î_ô”îZôÆ]‡-Þßzą?XTųŲym―ĒđĻēVgąXt–ޏûx1ęžÂB%@]ažöëûl @ÉövEïûnþæŪk&§ZīOĀāŅcĮSN‹‰özoęœÞ4Ͱy9hGS]Nō%1Ŋĸ쁁þ–ë?Şž&ÎH>~ąãũz9ø5ŠÓáöaóïûÔ°>ēs'"cNKŠģï9dÁ€aïöâ9X€Ōė '.];Ÿx*ŧQę#ėp.ņÐŪØäüŽË?eŠuvĀ0Ø>Ąó7.ýžäBÔúïãÕf‹RĐbļ/†UJnč-`Ð5ޑj‡ž:Ö q;7„‡/XīŽĻ™|ā?öōˆ§ ` ģóîjeqy…ĘĘČn%'eˆūųÁšE“Āf1ē€â?Íà BŊUĮ~óøŒ:‘ÛÐé°ÕfÅ~’‘~+coéĨ7Â?›ð.ðqðņįĒéĄKY`|hččĀÞÞŅŨk* Îŋõ·žũ9ģ‚1ð8#Fgï0Zí û@ŋįį–ķ Z'Z­NQ—;s܋€ GgßQ–œ yÕĀŦ—HCßþîXr›ÎÓIk4h .Į―ā―ý― dŅw…MJK‡Ą<í@W ĸĄÃ‡õâó0ė™1{ŠõfSëíėOĮ―.>øhę+Áč/ķ·5ø*ˆ\ų}ߞ:óĐ~~˜ß íé v}ËŪŲA" üŽˆeâĖÕģ‚\ýú Ixėؐ[2ƒI§qc2hÎüū9r^k0đl:ģIĸÃ|ĸŪ^Ģĸōņ_Ÿí€-Ý~Î`3\Úģvp`”>nQY“2zÉ[ÐbZ™B^ýókÃļö ;|åԎ… ŧ*ôņ„gúāËv^Ð[Ôg"ūčG§K–―:؏ \R§4j[J#ŨÍč*Q$  Ï°Ô õ­øEЧ_ĸ^"čóę„ģYÅ:ƒQŦA<ú<ÔR‹7ßŪĻŠ•+UœöéĩŠÛuUåå•ĩwdzĢYŦ”VådįŨÔ7Ļ5švÅÝâų7$­Jåíę’Üüü&…Ę Ũ5W—åååÜ*ŊQĩ·VVT74ķju:‹UeÏR?ā}q4ßdŌkz ÕëZ›o‰ó ‹**Ņ —7U‹ÅyŲyĨuMZĢYŨM!ļ õú–šĘœëWÓÓ2roHäJ΋S}Yęį^^ļŋĒē<7;·ĪĶ‘fΛshmŪ/ĘÏË/*QīĩÞĐĐĻm’ju]Ü‹ ŠĘĪmí\K…Åwd*N§RܕÜįĩīķLFu›īäfavnķļĻĪĶŪEåY‹FÓ*kŪĻĻļ+oåžîUĻQĘËo‰ ÄERĨŽZ’›[PĮÝKõzĢQ[_YZ›“_PXÝpWĨÖȚę*jZÛ՝ķĐĄķĒĒīōŽLÓþ‹ŧL)­rŧËÕęÖÜÔģi™—sóēũ‡ũ%Āoâšŧ*nFƒĶ­Ķô&ŨržļĻēĶŪ]Ĩio“—•įdgeįV7Étznŋ7$ĩNa9~• Zm—AËíyāiŅrļŋšéÝvũķÞ Ŋ+Lzói œ~ëۚģü/Ų9<ŌŧééāQĒū‹{uLÉų}ÏÏÝÝĶ5\îÝSxÔÖ­|vįĢ‡ÍĮŪķ[Ĩ=GĪÕölŪÛäĩũtŨéÔōŠ9ϓĀó{fŨ%øŒÜq6OĢ3xĶsÓÂdö‘I-‚“Šú*ņą˜ļŒU]w―ß­N+oĻļ!ŊŽ]ó(Š‹Zq#+5.66&æx||Â5qÎ`č’kI-B§7Z­Ö“AóÛĢÕ:ÜđMīFs‡õĖĶGōI-@ ĐE  ˆ‡–e@<Ž`ö/øo1–eI’Žëļ Ü ûeÍÁ=šMîtî]čvĀíkũ_w™{Ø=ÖŽ€Ý‹ók^č‘ŅÁÚ]?·r/Üō,Øsíy g/='āŽė‘―[dŨƒÛčA·ŅqņN§Ýfóüåņx<’$Þ>| Č‚ÓI;ŽÔ …˜cGJ%’$þ‹óŲÚ‡Ēpâë?™ļĪŌ,čuÔ7^Äk‚xŒŠE%åÚŠƒ4“ØÎïģïO{š˜ENĪÛ9ũÜë‘ú­Öų’ģ1&į$bųäˆ”Ķu}+Ĩ8įRÎĨdkloú%-b\ˆ&#LsĀ öjNIcŅ<Ï$­íģrGOī]Ι:ēÕ5‡šVf:2Œun-™l5YhK2ķ@Ū”•ŒANˆd™ÖąŽÅY§}s― %ũå ™/üԖ&âV^7Ô35Ę^?6jÉėVB>~úüåëņx|á,ĸščîîWyÓĶMįΜóÍÍûizģ,é_Q+"?oo|ĸ/"H1ÐķRĶ 2‚5]@ pžĖ3Øa€Y€·ũŽĀ)h ŠXApŌnHžOG>!#M ĩÚ4a%€@ČčC`<ņ‚ķTAːŒi!AĄ\šjëæŌÞŸ–Ä;ņÚpĐJĻÃÃHtĪĩ/€āP‹yØ2ęétĒÂ6ææÆbį#a`x’)äZ,ý,ˆÅ†‡9õRô+žY é#Ör—Ãóó~ŋoáĸĢ–ō‡‡§ŦŦ·ŨŨïĻëÎC›6mwũũŋŸ./§ŨųïfŽwŧEøUČ+ķ ïøJE-T'2Žbz휃 Ļé@-kӛ”Ą!Ōč–ÁĨ˜ä agUļ8įĀd Øņ|rցKÞ·Žü"ŋũžCiŅ]_đĻ$ŲČ`N ĒœÞTņÖęņ ÃĀYƒĐC™ÄAܚov1g.†0ā…§! ôÓV,då“.Lƒ“âyžŒZXN kÂõ·r–1:Ļ=ÆØWÖSÓûĩŧwąnþ†Įišï6m:mĻĩ1FPĀúuP gā‰0t€1üĮÎÕíHÎė6‘Rđį{ĸw=;vé'ąÔ=đ ,‚Œđk4ÜåZ/@°)ēˆÏïq#p^(ŦoˆˆfņpTe–.͆Rģro’Įq\Ũ5ĖržŽĘšŲÜÄ>ûзt…Új’•‘Ŧîŧr|ƒ[†Ī‚Y"•%eķ2RŠ@hÓ}Fęox‰ŽĩĪŅĘ8֑Y;Cjôxd9LÝŌķ Āmk7kgFÆbũ ô%‘AĄ-‘~Ž•U™ŲO<"bnîWw’Č*#įsģUū·_€üÔōïTãožėUYî!MUâáK•ŨÞUIU4ÏRÕlU―éÉÖ·ēŽãČŽĖđRĐĘh3ĀąŽæĐėEĖÝKŠĪFT–ČíBHĩ_ï‡Fõ)B!•Yõ#xۀMRĨ*|ŒŦL(ŲÔđÛQĩj6%ø:^Ué{këY˜ÕĖ,#Ž5öžĘ›U7xoÉũĩƃžĮ‘•å{Wݖk5ē•éu5 ÚŽU?” ČĮō ņāÁsˆ"€ÚDCJŠíT)1[‰ĄĄvr·{dHßpk^Ā;›Yš”yWUĻ)„UEŠ™ JjŽuũŠŌÖŦþü+ÛîĖôŠju͔ŒīFm‘ēÞMŒ9ũŲžUUD€\fĢ.ĒęũũwUŠˆ”YÚóŪ{ĩôe‘Y![Ø6š@Ė4ۑ†ŧ—UĄŒ w')ĀéŧÚÓéE­]Ý/ĶKJÚĀÕ6FdÞ- ōāÁƒßy4ļ-Oķ-PmēÐfNöop7;ˆËjŊŊóâÝ%E•­ÝĶ߈ð)8tF€·i[ý*Ū―=|ŦĪÂÝÓAˆHxüúúgß x6‰ï^™ŠĶæáfšŽåûÚP’,yðāÁ/ôj)ķŽ―Še& ûž‰Vual~n{„HI ÉÎųc4ėޛ›|%ý å-wŊbkoŊŠã8(8ŊËÖĒjøŽŒĨ­ag^Dķó{i'XĨĘŊÝ_|‘,T­ÎųâxôK—*{r5FĶ@iHĐeæMáĮZĒôkg‰*ĨŠy{lâHISÞ)ý34ë,Wf•_ķö―B‘0[Óģ D7åĘíWfõn]·ŠÏw―!ōāÁƒßįÕSĘ @s™Į ÍŦ2ImÍđAķ-Ðs0‚jŦdūÛą:Đ) äëõOÓhšĐī]ŠĖ5Ü i ë—ïl[ =ÚĐЂxzĩŸPãĻB”čôXķŒnpTPÆkVōĢČ[ÏîkTðŅ…ÝŒ|―^‰k—{ß;]y'ÚsŪi”}Ô=DãXˆH‡R™­ˆŨ:&’a­ąå],& Ęšë1)›ņĢj<ø^­ûžLR‰œŨ•ÕݰaFˆöČ>KÖ:Ð)Ø2ģÕ|TŠHӓčŌũjũēûĘã­.KÞýÁĩwLĢW™‡-[ëėēÃ0uEpTŪjlG ĖČðŸ ãVh9Ŧ!ĶûšMïœ/&§5)…ïóŒ *Ĩ+^FđÛėhîæ.Ž+ķŨĩ1ų3‘Œ °–QQUÖÖö|ëh‡7ĶžKuũóúyž^ÛÝcžæ5aᮂ8^_~yf€„Ō;}EÕ&ĩŒą;›ŋĖtē•9yŊVĮĒj"âmÜ6ýJUß V•™ĐŠ€øŽˆDˆØÞōÓd,é*ÕQįá°<ĖīæēuHvĨ=ï鄈Ïóŧ>'~Mē8Ŧ2Å0Õ Áņj+KÉõâõāÁïôj•”’ˆ”™ĩ8Ž*UËĘ1މ8ģĖÁu]Õæ#>ņþŊãũMē(ËÖũuBĻĶ―ēKIUBX’T]jÛŊáY[%’• “ũõú'ŦĶĖeŨyEGH ™áûm§xlÓ 5üEÅũŋūÛĖ53‹ QēM†Ęų.·NŒŠ‘ÜŨÕ7ĪũđE‘Ÿƒf*KũÞ(iíĖĖ„Ķ ŅŦ­ŒĖĒQĶþ ų…xðāĄÚI>ųqžžŨ·™Ų2ß^•­ŅŽˆā 4Kn€vX•DI!Ģ"EÐÍŪœ*›Qö>Ø:КLŲ5vFŠŲœŅ5ÉÜņdĨKšŅhŦÔĩ·ÏÔ~Ųņ>qņ]ëÚJ Â]ĄJī ‘jFŒč…Ų;lK!`ļøĩVfTĄđW2rôĐô6ØþEf-Ōuí-YÝđļŊģō°U"ÞO1ĩÝÆ@^Į—‡ũrĘÉLéÕ+㗆―‘cÅFŒdžïðėđjŠ4ÍĻŽTUķ“Ö%ķĐoA)%{_ĪŽlL)*ˆØ6-&į[fĘ·—ÃV+ôíėþķríÍn=ˆČågϐĖT35kn €JžW‹_Á,5GyÍ/ô9u0ĩã8"cûîÔpyŋĮ=ø­xðäjŸ9XVŊWfí}MttŽImA8(o 3ûsˌŒĖ&ąŸÖŲŧōßÔĀ·—Ī-ŧYÝŊ‰vM­vlâN8øg…Đ EúZϞRŌýārwyŸí]"ėáĢ”oÔpNUQ iŅ[UĐ4SË đíŒWDŒ[―ũ֞ƒÍ;43(ũnņNĖōûÕ)K~~č‘Y­OpŲÛo3Ôß^§VÖ|ËÔÂ@‡Ŋė§VKp*Įë…þî˜ĪûڟģÉ9:šlJÉVĶBŠ·‚ĶŌĖ~čOɌŒŽé€õjʙVÍ ­é>{{Rĩ·$XRÝCëØĀė&„sˆ{5ð€jú~ß ĨTd·û†9čKfū7äũáÁƒ‡j3ģ3öĨĘƊĖaÉŦÃIÃ}-*CĢíÕŪĩæÃq>Ô)oþŠ4ģŅķĢF}z_ėXƒjË―F+ÍÜ{ÏÍŌ03‚{;ŊŨŦ :åFÍē"hnÝė ›Z6†øŪ}mßÚÜÚLÝÞąßÞó›FĐę4?Ą`iŊãūaX~Ė“!ÜŽ§b|I#ôĖl“dæx9ŊKDēRųxðāi‹vC{âŊäGŽ“ œįĐŠĮzyFM‡Š :|áķú°óÛríë'1ö҃"íä} ŒŌŽŠ‘Ō€Lubn~— sÞB5âÆngUēki-ĀgécČE•™S2ĀÅ~4øÞ›ŠÕ$>Q‡VŲŦņnÞoīŨĖŽœ<,9ģŧœ#zûmØ:VûWïĩ.mÜOiViĒ‘ UU~Ĩķ Äþþr/M åé)<Ė/ŦðÏ Ð|ĸ/ ­P]QTĄĸžĖ'݈yž[ÍÐ h†ĨĸÅŧ E]þØ›eĪĸý9BĩšžĪBũÓĒĄPw=îVđH ҟgAœe†ŌĒRAųĸóm(î]ðßÚõĸÉrBŠĐĘJŠI–:ûÁÝčÄWčfņ·ŠUĨï™ĸïôÕJēĪ()ÞG™X8Į Íë, ‚í,Í0$ĸ ƒ5Ԛ”œ"Ek•ä|Äe%ŒgqR"ú"4ƒ„*›°ŠXÔ\aįņüBĮ°šĄÖÚ!ô`Ļ$Ų–(ß@\›@ģ,’č3öĻ"šEQNf‰AŠÜ‡ký–eņ,KÁĸįëMŋËšŋv^@6Ïþ‹…‚PÔWdįË~œĶ"ýėęāŦ"Ã͟ŲþŧBQŸ=Ļ_•›W,ʔF# Z|îÆSHÓĸ…ĶÛGH ÄüT Î^B ‘E–D ŧVbÁ•EÞIRKŦ8Ķ„#ïÞâMĸcÕĶ#.WĒ Ž* (TÔĀÔŠ~a-,Ž‹~ t%ˆÜÁØLJ\›~l)ėý€Ũ@ū1l1‘ó”TūQ€ÂBđĻâÞžTŸ5$qÔ) ą2HĨ†,ÕTô’›āíĐH`Ĩ bJFQˆL/Ï ē,’ŲbĄr‘åXBģ€fÍą$™Ĩ™/EĒiæÝËÛ‡Ģ 7^š―ô›Ū@ÂÜŠDRAXŽËúqúŽË%ˆ[â/ßvė Rā'ļJ•%ûN~ޝĄ$Ģ’DĸÉßâRXëiŠŒˆ1•ČðBJĶŅÜLý“ÖöøŨÔΈļGÜN­ųRä.4–þ”ĘĖŽĢ4ņÕÜâãüh†*ÛšbqÜó2ĻØeœ7þŲĐRīđVCÓ(ĄBŌ'ZÜĐBõh1Uô3šZŠŦŨôŅ]$Y%óÅ KølôŊ­*čr†8quÍYkü\ãųŠá.ĨaņĶe‹nŋŠŠĒĩjþßd,)ļÞßÂÚcj―ÏŋUýl}Čąũ€ŋkøYûO Ï_VŒ­'ĖhÃ)’Bč˜Ņã‚?ĄĸĪw•#q%)gįÏZõšZB+ î âołm`ŽØĻ˜Ķ n āÁČč›åØŌ{qģÎgTŋŋÆôũęúu#EéŅd y?q;eG<ļĸïgjiŠÂ2ąd}h“eÄĪEÞ õÐÆ°­„mfŅc㗠ĩŠáū’Q jūŸŨ S€)X―JY"d€P*’Eg‘đGĀ‘Č)ĒÆ_cŦ‡ƒTē(žo ðŦ6E 7Ą—D‰Bwãð &ÞÅŊīĘ`3)XĄ ͋å-KU &D#Ábĩ4ī"#El9°˜ 6ļ?BÄáËad•8†ýŽ".4ĒIĄŅ~1°äģJģrĖm› ėԌ°"õŨÅÓ]ÜÝÜv\ĐŽČŋzýüÍcûýĮMųõ­îþą•ŧĢžr͛;‡æėŒĶ5ÜŊõ›éŲĢcĸ?þŠÔZ[HS…Ïĸq9%fØÔčÓc\\‡ļŧü%Ðų―FĢ&þxxe`āŠ™ýzöūĸzĢŅg>Z6u˜›ŧÛČąãģJ“Ŋœœ?û˜ĻŅfÞ=:sâ”ŨUŽ!ûáî Ąe€ÅÖFJÏmœđ,$ā›1“ŸWōŊâ/6xāБû~~HąLRäöą#\]zw Ø. TDÝĨ]ƒ ;ßĸÜ}UK― å{ęn>Į0š‚ûŦ§/|YY j4āŨÛ/^ųeÕÜûLPļztÍð!Ūûũ>qM>ú )šïŠNŒwëÛ{ĀĪ oJ-§þđĸO7—þcNÆĶŌ[üāÚė‘Üܧ.š5áŠs<Ëž|óāU1Ê·ö-YģeÛßQÝ:<óšaϚ•c•ØÝKÞ|ĨŅhRcũ,Ø[ĢĄnî]ē<4tĒϐnÐ2R +V·ÎóčæšdÍÖû2`):;ņōīąƒÜ\zNZū―‚;G·þx5jÅ,ŋÃВTx+<ēLbŸÅ\š{å ߞÝ:Ž8%ąœXžhܜĀäƒV#]^ĩzMā‰}ŧļLž˜‡ÍČßMÆąVŅ€ĄðgF@Ą&dĪ•jø_(œņ* ý7"đPØ*CäÜDvJC‚fē_˜a Á@dė°ĸSFTâļ‡(Ėw…Jh á=žA*D,Ô°"ā3,)PĄĪ`—†áHĮŒĒY†E\šļš‹c9 b4ƒ†Z̆_4flRi€6ĒRہīðxBīāë5, ŠĖhXRJņn$ÉØtB ’ËRéGŸedņŪCķ+Hã æ*É 1iË1 v’|ådEēhŌ^ŸæÁģ=gŽØú4ŦZC—‡Í]˜Ý&(ōĮ])ßoŒ~ĢöéØ―Ũ`;6uidš›‘˜TPAAX]ü4íQĐâŨņ§Ū'·öݰ;Ðå\ð‚č—:–ĄˆÅÐ%?J(ā þ|ØŪĖA Ū^―ōMïķŠ,~8UūˆŋŅzō0ŊĶá‘ŨųęžC‚^4ðđ1Ą—ÅŽEû“ũâōó<ÝÃgN]Œĸ#3/=ņÞ―û6uĩŠBGųŲíðË)ÂܰMÖųņËïpčÐũðUksųúÎ}CODÚ:ãÖŲíoõL^ÂųĨ‡SÖ}yóīV4urvxwāä ‰aOŅ7tŽÃĘIĐ톍ëŨŠųŒ% Įw~}įßöÄGÂ8{(øÜLrÔ Ore~!pvßw|_㔋ÎÜ.ϰ`áŅŅĄ…ļ ÎĐĖ ›ŧĪÜ)(ŧ›œW!ĸō΁ģ7‡ĖÝļÂËäðÖKzšĨj_Ĩü‰ŠÞ·ïōSŌž”ĒÆé·ÏÜNó 8°uA‡#Ŧ>)Ó]Ûp§ÂéBxøØîLącģtōßyõę™ Ų7Ýy^Öqä„ū-›Í^ąxŽŧ_^”˜˜dÐPvnÏ>kŨéÛŨúþvjGŪžXįĸkđý…ˆˆ‘íŒâĮÔSeĘēqëĒý§ÎH sïĉ{lģ‚ˆÍĄį’ķŸūtîÐØ˜-ģ/<*§õϒÞ"Y*Đ2ūā^ą(Ŧú˜c§ 4n'—ųØĐ$Æ Ę&&óˆíī2þįðŨ&î;·†'œđ—eČMúųÔoyû~ˆ<á?0–cûĩsrčđyĮ†nNĶ֍{EÄÜÚ―ÐâĮm§ēųÏLÂïõÚ>}5ęā,—ß.Gžĩ];ôėŲŋßĒ=ŧûķī|ō()ģĪŠŌ―\â&õ\|5ōRK4{ų = ‹2Düô ĸĒ~óÎ]ŧ§ĻðïæŦE'H™Čw#Gü“zí 9(a­V’‘cWa T(bzo[2<‰#ė‹í)Ë2’ ĘĶĀ”+ØJĒ ]ŒFåÜŠĒė+,rQBÅĻčâLYJP…Y ņŦ‚ŦČhĪɈÕQwbMmĖ˜„Œ?Í2Īö ņc_ÞĶU’#QCŅ+Ŧ?cgŪ ‰Ļ7< I–ˆģ‚ðޒĖ\<ËĒ’ TLuĶČD=WÆCĒÐBAĪEÝĮX5ŠdĒ(ņĨTęÖvÎŲ+?ŸÚ!ÞÝ0iÆĖÔėLJ|åË+›'L^þАdÎĘŅĘŠĄucGĮšZƜœæh ãSQŅŸąz‘k§^žã).%ģėãØˆ/hGÎûÎä|ˆũŽĩ tô'1*^ô˜8c`Ũæ-YՁÅŊŸÆÆ*yL1F“€ųčeë&štnÖī… JxâDĐ}ÞČ:3īEąĻ1eá97čÛîmúzúØTfAf­lëÓÛŲõóšÐšcŅĢ"ó——L_īõdĐ@ņ<Ļë`WŨôý,šZ[˜ÁZž:{­˜8ļS§žÍYN’Jãï”6p›ådkÛ{„oŽ“?Ú2“Q‹§€ßÃï§Æ‹M™ūhdZ\ßÁũý6ïáíÝÃ6âæSŠEĄ^’ĢI–UđĖšŨø-ÛūklmŽˆBĸgŽ„ uÚv\8Ĩs덝UVėÛŧma4mÜØcŨSx@7hálZĮšQÓŊĖL8Ýŧ—ó|'mý>‡ÅÕ"ø4 Fü‹JáÝåóĶ.ŋxS2Ōj(ŠŽ}―zŽ–_5úĘBÃė0,zv?Wvšîí^ÏŠá|ßárbBŽĻTĐĀ'`ý·=;īhUÏÔđ;ôßÕí[†Å7ĨÁĀKēŒãõČ AĖ Ž}—ËrƒóĒ„ڐ•‘EBöŠŦŽd §]aW Ā_#EøP ÛYGé‚ČëyƒŒ}Xz―ÞĀóčô-Kz]ĩÁ GūVAŽÖédEĒ āy^ÄVeY "/ÔŦũŸŽUSMgÐó"Ōŋ1čõĒ(`ũ+‡Á%Š F§{Q†ČÛKKX_ŌˆÞčĒ3„į?ā\ZšÜÁdeR§€ĩ!ÐE -ï猠2ŅÚeHa,ŦHÐėcĒõĀē,QQcvgÔ/ŋŪĻφm{zė9zPĸ<ųIPOV{{.Ûüô―ĪĢÛY ’Ą0ŋJƏĒȋŠrURJž`$âmĀ’2ôUWKŠ…|ôéĒņĘŠÜÎsnėýŸĮÕ/žtīŒĸX~!ĀP’€―@ŒL,m,ØŽĖ@Yav1ÅXÚ;ũkïļ?xü}Ģ%Ŧ9,t_”8Õģ―"J5@ĀŅÐH•‘/[ĢĐko?cŲ†sWĢ“ÂŋÖĪųÍŲhäģ2dž:ļPuęšV<S@eþóWų„’‰Sß=Lįzy>uîóĩ― ŨŒ\•ĪjćŽlœĘÓÓŠŅpŦ^įpæĶÔ'―Č Õz4û܌Rë&FīĩÆzNÐÞģŠþų “Ĩ†­Ė‰OxQZð"ōRī(j>õjR*0ekŲ”?ŲE^0Ð,Ð?|ōŲgEÅëŽ/+2ĻÐŌH 8ĐBWēĨÉ@sŨÍ^ōšŨÔõóZ™3ŒH"ššõĄSėē3@DŅ€ÜŊœĐ%óŌĘ+Š‹Ndöã.,ŲķrqkGÍô“éėýM+[`ΚWČ5^”þžŌÎÁQáņz†é/++õXŠO„”)xIQĸėČÅŪoU–aâČS#ŦV;N\=ļÔãäÆyQÏuP• Ō[‰aΜ›~+7z. ]ęåaDq4 îJV[šþӂđgMÞ4~°–‘đv‹ō+™x,ŽĻĶÖöšĘŌėür@ÎÛŨ)üÔAQEOÂ"B>Pžĸ*ŊôüßĒ„AûUQīŠNHρĒ(€Õąš N{›ˆˆSq ;8ŠÍ0ɀJcĐsÂ>‹BIžEĨīF6ķķDN†Ļâķ“JM)-âĸÆĸpėH…Čc€C[Øõ‹í$ôß8=û °ā ŪĪ•‰r"ƒÞPZ\ˆ9nÓ9Bą5ōŠŠĨ &’AË8þC\ŪŠ,Ũ xIļG†ã°gƒVđĶ,€VŦUÐMeŒ•)Œ°Eš!āZq@Ëõe s)†Í‰ŋr1ĩM+ĮÜĪHŧŪ―;·iWoäĻ€#› ̅ēūSÛ4i–óËšuÛíüįOģķm{rŨ’Œú7cētÐĀrRÜŪ­Aš”‚ĮĖúyo__ÄZï­3ĻŽþaóö7ÆfĨ9š–ØO3yD^DāTĻÖëđz͇yũ] Ļ:ãëĮ·b›OœßØŠŽYÏķš]ũ ÝZč­ÞUš~=Ūŧ=#ÄŊ°A_Í!aOĩaũÉ>WOėŲRÔ·CoôÍĻŪÖuχąá—fVū‰đ‘0Šĸxóģsü*Ėų‡qŊËÚÓ2ƒFŽ;ē pļ1̐‘#,fÜÐÁėĀ–°Ŋ·6Õ>rųÂaķ†īČēfÛFtĪexŧh@•ŋI]ŋam7Óēˆ;•KõųĘLoctÓĶ7.ÍŠ›0kÚâáƒ6­[þ&šyþ8K6ķUx/)xö:―AÂRĐÖË ü`•Ļ:V‘‡·ø?āâ⠌âÔĘĩyÃúwņwÏÚ žōuãĶĀŧŦĸŽEKJ\ēŊEeōP5Š_ß4îũ˜ï Ú·Biô• n-F;60Ųŋ1Ôq…ŋģ9Ĩčõ*DŽ‚ˆøD Ŧ– õäåüÂ6Ė~}=ĸuŠ `åOŽ#cZßÕcðöóÛV† 5V™as'\›ŋÃ?P°-üØŠåÖ>_ąĨÎfÏÖ,—Þ>‰“WÆ0(mRWýy„ Á ―A…ĪS―ĒĒKž^'Š8=nåņ‡MŠŒíšÕ7ĒŽ›tvä6ï Ų;s@ýúšōÛ?OKĻ{“ĸsÄS/kNo@Ģ“―^8#[-Žžqåô}áÕýøĖsw^ĖoÚĪęþŪ Á›Ũ‡Í„ž^ây‡N#=ęí\øĶw“˜+ÉÝgŊkĀ@QŊSÅÚû(І“Ï.—ÐÜĸäŠq@‘ĸĶ–ÁķL–%Á(ĩĶspAt|!… IŌ0Ŧ&ĮË-‡ýģē$cpĖkBĘÐgœėdnnî9Æŧ}ûÆÆÆĘ_Ž Ú9ĸōG|_ƒ~û͆yņâÅÎm[ōrģŽ%üØĀö2tHŅh”Ú:Gsˆû—É‘q“0L‰‹"lœ9ÐuŽ, ČŦŽŅ"ū.^O!ƛ€‚k+< –Ļŋ!™ø—ōÕÖoÝwdŪļ\gí>gņPFvZ―á˜kTÂ%”ÆÞÁXڍ8~Ņ(ģH€’ÜŨoÃū&7Ęĩ6“ĶL+ÓŨU"ˆTĮïFuhlVÖ4ÐÍÃÆ%…CÁĐßžÎÆ’ éÔŧŧô:§á·3ý 4Eŧ$ú― €žÞ~­)+EzųĩĶ)ĀŽœļŌÜþڋŽRÏ9k‡šöbÁĶįųČŊšul ŦûÎ\V[sžôᐠ9ŦÉŦö2Ž(Did3iåq‡˜čœR―…―cÛ°#{ï$ĪÚ:wėîQĪaęĩ}ęˆõ­Ä4kgïï&Uš7ēWTŲ‚1nŲaÄā>_Ó@VjETëĖŲž§É­‡ę6ésōЁĻ[ĸ@Ģó ‡īĩ7D<Uaëī\ötĄē° *äÐB—Ž6‚Ž, ?ÝþÚõŽbūŪĢ 0ķfKģ1ŠunÜēÝ7h€2Ðog§:MAuóÛŲÍōýÁyČü}íXcIĀJ•EjČŌ­TĖUÓé3į•óÖē*I*ŨyâčĶvŒí„!CūũŒ‡šE%ŋ5xôĨ+ŠęiĖĶoÛãp3ÁÔĄÕą!i9:Iĩœŋ}oģÛÉ, Mę·Ú―ŲžlÜoꂖËÄķ#đbî2ïDĢþÏrdmé­UŧŪkˆ^ÂŅsjýõû_uŨü—~āwÞRø L}nÎRŠ>DķķČōÖo&T4 jós,}ÔĮl1!%N2þå_~ÛįpŲ9ļã;?ļïWí͗­*(yŸØÍ|AÖZe€ ~yhŨEĨDáóŊxÅũŊļú‹Ŋ8ô§!“_]%Þč[e? ·Uū,iâŧˆ \áBYâÄŋD㠛bzðšOĸæ5w\šyÎĸ―öÆ5ŊšęŊßņœäĢdm\ýóũ\ąsÕ/^õëŊĢē%?Č*:t͆™ļ§ e‹Ņ6”eP(ōŨ e)hŠ|ĄĮØëû6ï†―Jâ\ĢaPBh Kųߕʹ"6īĘHŪa!úĀŪQ―ĩd­,#7"úē$Wčß,þņ_ f< ØFô?KãĒĐz„øč< °ÖnßūCDN:iđũáĸjgýöoþú·þí_‹FYô—o؍B˜Ëā‹ĒÁ”bnĖ„č=Ļ+”•\MÖÏÆĀ"*ģ‰+DcÚųßoxÓ[_øĒ―ï}ï{ë[ßšvíÚã“Íķ‡þüŸĸŲ­7ÝÔNāåV$š°ŌķEœ˜P5Vô§ÓšĪĪ‘’*!0Ú&æ[%E ˆŽēhnV/\#ä’"gll äkï˗žėŠũ_õŅĮĶÚ'HxhũŽŅÆÂՋįˆ<å qïö W-^Й? ę}:īëŪ­ôŽâú3ÏßīŠû?öoîÚQÎ^ēęÄŲ,ō„>C Çîūûžý‡ĮŊ?óžÓWILÐJ*ũėx°kÉę…óš) ÔøOĪZûŸą―70DHyěŒ&Č)Ý’ģī%Vˆ sVÛ ŽÉyƒĻkĢō~―o§Îíî^ētÉ)kŨ:į>ņ‰O\}õÕ˖-{ē™ĨúĒŋðšŨ>txũƒ;QįnBrÎU€YÓūŧÏņ:”Ĩf1VÜŠo2ŠĻ*1 ÄĄĪ•Ā9@‡™3įŠö›į8›ždÐϖõsYc)Fæ”EŠûĸ?„eŅęõ(jy*íę ›„Ó“Áģí{įE'_ķlĄöœĶô։,Ysę“ņsÛĩčüg=ŸYŸâÔ7ĒiŽŲ˜>5þŦ‹M˰™'^Ð+7ĩïGĨ$DČ]…r5!Š$B[–å„pDēzïÁqÁ 'V[ZXđråĩŨ^{å•WΟ?ĸIĶD$)6lØ0{öėđÝsģŸ8ũ>Ī܋^p2†‘bˆŌh6Ā—ÞØlĀÍ.1 ÓN{`Jˆ|€‰ēv<ØDąĩípTk‹‚– !k„úpŠðTFŠžL'ïÓéŸģp˜v')Fø/…šj•>9Íä8.%‹`€DÝýZņbōŽšÚ‹ĘóŽŌkĄōƒDŲ Á‰­ģĖQĢFMĩhL$ījæ…R…˜—ŒÎ‰@î쯸3ĢáD‹*„E{p)UČÖŊÂUú֊ō6lØXņėŪ]ŧöîݛ•dOÕæðîîîēŌmŽ ë’ÔĐF-ĻÎčFUŠFCXōZ čyXŌÄbÞ'JČUįËbn/w"âË2/yuW …-ĻR["ēÆåė›ėnˆúÓ0Æ@ÎTœĄĻQĢ^ 0kėaĖU.…1˜Rn$t–L ‘0†Č)kČØ,#5ÔÖbū;VåhD˜ K–-]yŌĘ#GŽTTÛh4ˆžälŒą{^ũĄžƒ)ÆĒh„čAÐ8įZ­'jcÔN$M,Hf^Úæú2[Å8K}Yj%°Ä@ŽÏdIŠģGĸņÞ# +\;L’‹aFĒFšj•i…5J­ &ąäEP Ʌ%Ī„ QJÂŪ($—)€큄eéÄ9›Óž4ÔÐH―wëŨŸ:wNwĩ9Ýķm[õē’—=Đ#-Š-…0>:ŠJ=ŲėHÃČÉęXšc‰ ˆhBãĩŨ‘ÅBŦ(\Ģ`VA(Ą%CeŦÎđLL’˜†MŠÂđ”ØGO†*Ή€ZÔĨ­ä FcjÔĻ1ãĻ5ŋЄÜ=C`ŒņĐäĪI€1ĐϝkĪÄ1äq’ĘŲWZ ų„]ŠFƒšėUížþÔÓ*ŦXOÏÁĘÁUMŅ“šĻ5ÆęZ Á'Ĩ{ĪuؖmB1 Ū#Kb}DƒÎĩ qsČg–hF—Ë-ë„Xmwļ†ÓͰÖj„kŠ)úeŒšÞNģķ@˜"B5fՊոĮLÖ›%\TÓvžjȀķ kCŒYą"ūôÖZÐP–ā―ŠJMˆYETÍfG%§eN•QjĸþýđÎ žd‰UĖéš=:2*ÂEģ!€)&åŲķ ›ķČgbYƒÞÝ#Åڑ’ÆĐá ۞`Í)ãī†]ĩ‚+ø `‰ĒęfģĪKŒÂĐ'Ø{@0d‰Ô23wĩ5jÔ „Ģ!Ęáߎ›-_YcģUK›Ĩēd*XïäāÉQ,Îj)M2FW™+O^―|ųō*ļkpp°ÚÕfŧē8Õ*šššzĒ( ĐēUó į’ äÓ?uz HYHč}Đšę‚óðÎ y›ÛlhÚ'c,†ēc*áeö$I―đ*}ądɚ=!Öåä5jĖTBCÖX-.œ°œædXĀÄQé­uFƒfșsiXŽā„"m.@āĻ,KBŠīsįÎM)mŲēEþöŅS@R†Ä`4Ū žÍŅą•ÏõûķĻ$ Rl‡š[21UāĒ(bî9ËG[šL u{ïõKcöÚæ,›ÜGÉÍĶÆ§€CČiķĀJߎXã1TS]Š~ķ('ÚÔĻQcæQmæˆĪąÖšvÍLnÍã `Čdą-æ?)x0dõŪ9€U[jbÖXq“ýŧæÎYžx‰sŪ,Ëŧïūûx큈 ō°-‰Ģ4Ĩcuu!FQkûŒ ‰ dĨ/iÚhĘÂdPY9>žbtŪH)·“šīĪģZ ÚĄ ƒ† ærēÖŌķ4}՘PzŌ`.öN€• Z;–Û'9î°…'rŅbôŠs`ŦņŒDF)Ó ÝÕÖĻQ+Ē…sÆY ÁĒmļčĢkmہŠY4Š+K: 㜚ŊÐæŽÛēåœ%”.>ãĖ3CUÄ}ea0Sü•Ũ@·u ô5ûúMkÖúbíĶĶ9ÁĪŒQ6ųäjQûāƒNnD‘óķÍîŲ=û€0/:ĩóԋŧ B`ā–ΰFd~„€$Ą$@­—ŪßŅū&>ė`ˆ0FâR`T(P Đjy9|`ˆ1K jY:Ė1d([ŋļ°ĨŽĸÍ ,―·–4;†™utĩ.›2T,lrčeŘĻŃ#į^R$“[WRĩ惧"۔c2æ ?ôÐC===ÕOx:†§&ŽïC͝˜ÕïŅ*ĻhŅĒEPĢÆSoW hŽ•ŽŠUvĘ~ÝCڔ"čĢF>ōlC”ü>ŪÞgvÎYڒ7ˆūUnÜļ1§":tHÕN6“Žˆ@âÝčŪÎ+āæļgu9@~d8&€4QđA šB…âėŌŊōB`K0C–F, Ē=œb?6‡mrã-?ŠÐ ƒDŽÖšĒ°ˆ+|Ûý`bŒņĨ79T)r"/HG‘UĀĖí tk4hF\áōũ•RÔîHŠ’  %cH‰­Aį\ ‘QŒ!™Üųę-OÐ|W):Š4‰õëŨÏę‰1VŅ;wîŽŌåŦ€§Ô,_ĢĶZWÉY‚1ķó[Õ<ģĸJt€Í§@,b0ŠƒhÂoĢhäxėžôô­VeÛīéŒĢĨa­VEĩYņšĸóČŠÕ]ģũ“$ĀÜõ į˜V„Įƒčû0įŨ v'éŽp’šÁ(ųC}Î;·#6 ʃz]ęÃЛâhŌķ0Ȇ˜ģĖ9—Ûq”ė §vÄmķØ’!%ÞDƟ Ũ`°\Ō“‡wcHÉmQoN ŨƒĮ$,øQíū}ûN9唙3âYkWŽXQmĒ>\%ÃÕT[ã)֘Ëj›hëĘÜŠ1ïB4†rH6'MHĻāƒ+\…ö-6 5ķôeĶ-ĢĢŦ€tϛŋdÉĐxķúo#”ĩÅDzqm …aŲŠĶO€ð2b$ð%rî–bë<ŊŅqYŅýŠŲÝŋÔĩā]]‹Ūžŧč݋ßŌÝqķˆÄ–oĨ”Hké(‰˜9ïpąÐ‘4Æ{/ Ķ‚2f}râlŦ‹!H‡bېo’ūŌþ-ĄS2 ö――ýÍ9óg7Ý#ބˆC;>öŋ>―ų—þāâōŽ_ýĐß:įŲ‹īhú'AÎü}Jî kÔTkˆE#‹đÂåÃôvފp.ŽEĒ,Ļ2–4ģ5ķ—žeŦ•› Qí ZäėĐ6TV‚jĪ­äEQLnōR"‚äáqzՔšÆ,ĀúiÉ7ŋČš]ô8į"ēīÐP$Ãđ|–§{į}Čz mÍōÂŌh4Ed‚smä˜đ• •ŠĖ5dŽË=†Č1ëjXvžØq,iÛÐīÕcvôsWýIųŽũ^ÕÏíūõæŲ‹)(Üēy„qtĪÔķŪ,ߐjÍ#@U2:AJ,í…―Hf7˜,―Ōvč‡wėāÆ9ïzũ[†šýÃļúįŸŨwËíŸZÕe9–­2ŲĒQŽ·}ę=ĸsŲWŋþēMœ‚IŽuEá cŅK^ûšKšåþīdđëŒ €R eéMþ(Óïjž­ņTĨZ疁9&[Á_ķČØĒáT_ ‹ŧS^P"éą;ƒĩ9BPGShkþQІ€ÎŽYëÖ­OéĻ·b[k-*ōšLĀ?Ų ÖáHðĢeŲi]A&H  j8ī?ýDÂė―Đk-ÅĻđÍ"oHÐ9§ žúfÂcþ-"É@4*hËi⍚%Bē… >ē‚ÞG2hЈĪ" VDæÃ0'y2B#§ą\G˜ÕÜÞģëáÁxę%Ï€}$žĸ |åußü—Žģ^øĐ?~ĸšYþŦ_øā{?ũíNžsÉŋōŧoÚôgŸü뚟{įKN—Oýæĸ8ũmWūņÜo}æïë<ũ-Ŋ~–‰!24æÏ[ģfÍ:ģfݧĖ―ėoü·­WūimøČÞþÏ· ,<éŌk>ųþū›ŋvÍ_|ģãš}üöĮßũōÅüÝw\·ufø?ýüÅ' |ëŧŨž5ĸ”3ōnÅŲý‰ũūïoïÚĩhŲęŦ>ņų3įsdyŒĐž’ĻQGƒWHė: "ō!°)æøZ‰)Šž býŽ1ZoiŒĩ8ąkÄxŒ!,[ū|éŌĨÕÞ ÕjUį“2ŊüēÂORü‚šÜ|øÁū€K.\ųė ݋ŧÍ1?PžčŪCáH“Ó đ8Íd$đŨ YS“ÛsS$ÍãR-XõÝĶó4 ŽĒ/ÛG[’ēčÂjĘbâhÓ‘ŋÝe))ÅŽqwËŅ—d iČ:'Â'64rZŠ•Ô*;_ûŪ_þÞoþņģæÖũ^õŪW>ï|ÆŲ­ŧŊýá‚ĸņąŋøØïūáM7lĸUęûƕŸūîũ>û·›įė{ãÏ―þ‹§ĸmc`ï7ođ Sūōũđ{üéŋþ‚ŪŊßrÓúWūīMØfNū€ãå‚ug]ērųv<ø/ŨĸõßïŋāŦ_yݟýöÛ?ý7žû9į^~ÚÉŦå~õŠģc8øž_ųčŧVuýÉo―îËßšióËvmđ}Å%:YrēïÖk?þåûĸð[_}NwLāĢLį| ojÔÉ^jRŋĪdÕ—(œŒmCbQÃŪÎģG9N%‰ :Ž$Ĩv™˜25ž~ƙUŊW•{PБbŒÎđ)ځŸÖžÖîŧÆ.\ũęũmþ…ģNZ S°wlį ―Ũ]{č+=þ°C" 0$vžĪA‚cЎúĒ\—`Œj˰@Õđ0 }ð "hÛc‰d R–&‚Ұð“1@BL(*BHđĸžæ_ŌŠ]NĘZä.ð„AŦ3qÚié—m~ݗū|þ7ŋôđwŋãuwþÞßŋĸô>·ágĸðęß8}Ņč™ KŲŧ퇷ũŸüâóŨ.›ßĩâ՗Ūþâ û?tÁ†ŋýÖõ_Mspݛįîđïë_)–ŨžuR92x{KÁ—S9>°ŧŋĻ#n9ēŋwëmo}í ={ö\v>ÓŽE'Ū\Þ―`ážđÅØđóÏ>üąžÞ#‡z7=-ķZA€bˆÞáÔkÍßtákŸņõ?xóŦūõŌ·_ųÆË;Ķóy$ýC;ÞW đDĀŧ~pä”ââ/žáũ̌|ōÁlūŦ bęږČdžAc({ĀÐęl›]!ÆHH@,1B}gVĄ–vÞä­īÖ°Ąũ0Ŋģ1q;r!ÛEģÕđ‚$k 2eôĸ“€Āý――s­ûÅw}ðā]ßþ›oÞQžáŧ ”Ĩ/GUTbgwv`ãā―ũõuŋtþús.Iïûĸ+üÚĸņȟžō7>ðï—žðÝëšŲ°Ūŧ5oZ cŨéĢ tĸÏÓWÝōÝļæŽ—ýŅ•Ŋ›ÛÛ9—ËËxd,vü/Ÿĸ؟XøwŨüÏo|ø=’!í됉q<…€ÝËíšÏŋüžųÅ7žĸЧ|æÅk‡ZąžjĸŧĄQĖv/Ũ(2_ŽÉN'fÐÚcƒEÃeõRūkö>­ƒÕ°p1ÖĻĖë„uëŨWąj{P•‰cĶæv>NĀ•”ĨÜýýCÏ;ûõoŧčeSyv°éē_4k(6Î9ûŽ~Ũnßļ:ĖLūíŋðóÏ8Đs9,(N`ácķ ŽMĒķ°Ēd…oĢYøēԝ@^˜$ai;™óiaˆ‰99ë€0j5Ž@ōeČąķVc"Āæō1"0dSL"IŋaáÄd ü'Á8;úåx›™―Ļ+\ŧ­ûÅŋ{n‘îK‘uŋ0>ÖjŲģ^øģ§íï~ï•ë‹Ã7˜%ĸëŌun m^2ōĨÞÓ.9{õÁg,ŧęæŧ.yÕ3‹ŌA3vŨÍũÞw?īõū=ozĸđjéüË_óÍwüņ5Ÿj­čōŦ/}Ų+.XđéīĸįŊ>ą\~gÕâ…Ģ{nøŦ?§ÝŧÞũÐ?Ý{ņŦ9`dŽeŦ…†ßýgþüßŧ–€·|ÝÂđņŋį6ķFmĖM“Ē0LzÃ$qŠ‘ŒSĒdɓiˆ“ŠôÛB!õ­ætc/C%ŠŊĪõÕ<›kq­ĩ“<›Ą*,œnĄj Ý[ïŌtŌ'îŨf ļøŪßŋũ#ÜžĸēeÏý―—þV§-āœy—ž`áÏĸõþŋėīCe›―nÕFPČt’M8ÔqUkpbLíđP9Žu”)ÕXS˜āCˆą(Б+čŪV?ŒÉyIrÄ0QjŽÆ{ÏIlaQˆ‘SzbŧÅP1­ŠO:.|þe{ZŅüîG_{ŅŲkŌāė^ĩiq”ĄómüXĮęîÆ +>ôé?ĸöõw”pÚ5oyö†sĮĢžã#Ÿ{ą_š ĮO|ų~ýi7mšÛŠœŋƒ}Î[ĸpáĨ{|ÚŲozŨæÓWĖ+Į[+žý’ÏþÓĒïÞš+Ē=u邈ģ_ööŦįũŨŅxÚÏžųšŪU=厗―äåmŧŋ˜ŧæmï|OŨŌfĸŽwŸīlŽļþ’g ė/Ãû>ûķ‹6ž8ŌtßT-§­ņ”v‹aŅ,ˆŒfē`nÐā.Ž Ģ_0dēQŠ‚ģŽ”8ōD,,YoT.„ Â6n4ÆTęúx Ĩt|>*•gāðXÚ·―õĘ͗­œ·&0úþtŨGzxksôŦŨ,]xŌ;.ýƒ/XüōëúŪíįûv^æ3ąĸ~T[Ģ.žNSŦ{UN)gië !ænía€ÂYŦņ˜*ĄŠEå”Rũžy›6^ižēŌkŠ—˜AôĻS- ōŽf­;ål˜‚-C·ß1tį|ŨmČĒMņ―ßųË+ÎyÁōŲó`Qcņđöōĸ―õ/~æÄįŪ[~ÚŠŪ@1Ũ͝kgGŽŽņ^íyV…iÂZšŦ›VĩåxI]ŅÕϒ!dEnÏ–ėĐËF†,Ī$Iiō;Óß jĸ5H*=V.NFÉZWÃO4ÃŽÚÏT–žãôÎ,ŸATâõw ę›ōcÂŅûGpY~•ûŌ1Į)īR8žþōGe Ą°~Ž"č[&Á1ŠĀ„cāžY eųļlšßRýĨęčPÏCO%c.‚H;ŧ (ŠSð^ûlČWÐDgŦb?ģIö 8į”ģÄ:k­3ŸļpaĨôŠķīU9n__Ÿĩ–ĶóKœFËOýÁrЙõšu›a n:ōNÓÉ"Öāü č{Ļg ˆföØÉÜJ{á‡ŊzéoÏē(^ŋâ—>vúį?žé3kfŸ6ą(“gpΊˆ,@Â<€ûŌOŌkŒręyö†!fwrŠ ‘ta-Ųڛ§{QxP <ÕÄkXÝO”yĄŠ·Ø―{wĩĨi'Ĩý·"zïŦï·úKuâ‰'Ö:„§ęĐ6[˜2ˀ ĻÝ6_Ū°9“Ž^gĄhÍL1DRÉ(ëĪ!SÍYŦWŊ‰1Þ~ûíŧvíƌ'ayĒī•r9ÆX™ņķnÝ:Ķ7PyFN8#Kí oar·€ö]ę·iTSBНĢ(ËR‰ØYkž@ęYĩjU5ÛÎÞ1 ĻQãĐؘkU­Č€˜§6A@=š>" ģķķHBa„ŽE:›\Ī53Ĩ·ÎõęĖúĮĐTB(Ë2g*JHýn„;Ķ ļd.әËŨÁÄTžČXd G`čū˜ĀióžynŨoýõŊŋþ™ïļðåïÅúôÍ}7Î)šâÁĶiNZČ|BėŽ%CŲ‘@Ҙ\ÎhŒI!ē„l7ЀZPÅsŽ—e4DdÚá”g[5éĒu.qÖՂ5žŨe‚iÅ'ôÎĩ)fžšó{Ú˜›X’8›Óĸr֌eíÚķ*Ó)RŧwËč<‹ÎæĻ`ēzAÏÓûæpBuŽŠÉíAFió/<šØ 10>}î"˜‚(%AÂÁĀÃcC0Ķélš&Hę˜Bd#Ōw(îāƒ!Á`žacM8Ðæ4[čA0ęð›7ķęäÅē,AĪŅlģ/#šÂΐ‘ôbŒŽ^;ē†#ĢāQĻÁA\A)Å™YjęĐQcFŠ―4ÛY'ÂĻ­méK$rւV>–]“lK }3ęĀ$ÐþĢl‰0æËIžÍMöžáņq,ýrŧ! Ĩ‚Æu Ēæ‚c ‘#k&%F5ææöF([eķíNÆžŦ5ĒJ]RõZô!jå%'Ö}‚X-gCíÕpŠ jÔĻ1ótĩcbaõė›\,†@9ąĒvĀ‹šq9ĨÚō-Ī%1­ņ’SB€ēlyï‰čø‘–Æä Ä$EĒ„RB‹CÉĐÅ) ˆ €€(Ā 8ÖdÕųHŠM’ŽY_  īw!#g\—D&ŧžˆĻ°En\·ÎÍB*$‘Äíæ4iĸ*JZ<ŪI!fC]ĘŦ"ë\ÐIWÛú8æD=֋‚cŪ†4PĢF™˜ė%buÖúŅĢ G2˜“E$…dŒqÖyÝÆZ=I[ķ|…!;8ØßjWĩc##sæĖ*ŠÏtœuĐB„ ÂIYP$ī•ŲH Iw9(1%āÜ1cÐâ”xo–Ūcrō‰!Á,ï·ÆBĘSģ$҇•~›ÍŽ §Ÿ>6^í9†r­>ÅÜ> zöđ·ĢŦÕPz@(ŲHŒĩПõ€mĪØöŨeĨ=šÂÅE˜Œ)œcZk F32!kfK% —sYØT ŠšýĄ~0Nœ,$œt‚2KŠņéįžũō+^ŅÛÛŧozˆY|U"8+H‘ŦvQĮáˆDW쐠5–RˆˆãQāۃ{jÂīB,9‡“–ԅ,ëėj°‚#åðx9‚Ā c­ą.Ûåƒ/hDr§`ŠēpΞ―ęåĨÕĸÆÆŠ&ÄąņąēÕŠėž}―G†‡†Šĩ!ÍûēĖ_­ |Ë·FĮŠýƒXã-Ã`Ž !{ ŒiÎęŽ|GV­n1Å‚ũ>VH DŠÁ|hp`ûķm[ïđËëœ 1ø<šR !áģ„+đÂ!Rö,TmÁã >čĪ 0YëPĢF™ĐŦÕÛpRõk[X1ä›â”bR"å\AÉ"€ØsāĀŽíÛŦjþʏODFĢšŪĒóĩsND*ÎÝŧoï}?ú҃{võŌðˆĪR@ŅņHb€E: ,Ž=īoïĄóįÍwΊ}Ëuđ.iÛZ ‚Ž]°4ÆX9"b+î:ēëÐ@Dëé?Ō?>4nÃÐpõZ,M„ö’QN^æûÂ2ĩ"0ÛŠþˆÅ‹íØþ@wũžYģg ÕîĄY―iĸÃm―įžĒQXcJ5æj]đ&UkA―-`ĐÞ'MԒgCË'f@ĻQĢÆĖĢZĘ8ÏģĒBĨÂ"}ŨpĻ…‹“Ų+ ĒX✙­úÓÁĄÁ{ïÝræÓžV™qģ[ĄâĐžŦ5ŠĒ8JV§Ž9eㆍÆïËC#7ÚöïÛïÜyÏwûĘÞąT6ÐŲņÐaÆūŋkËó›į;Ŧ{dwÚŽM7ũĸ`–1I`lœÉ6įwĖ)―mí:ēwhpZqxxč@ßárĪËúÃ#Ã―#ãM#Ņ Í}b#°FļŨl "óS͜ž —ˆ-Zøžįŋ ĐpÎ‘ĒšÐĸô“ŸĖŪ…Ō—„Æ6LbÖôEg….s#"ØÂÅĻb/2”āŒNŅd`&ĒFšœü((ŸáE!šÕy–īA@ˆbŒ)&ëÔ0æ#*I s,#KÜ―cGoüx!-kImar/lŒY0ŧëõ›.{į3_đgāā?ßũåöÜuӞ[Œ Iҁ4Æūĸā―—ŪØHÖŪhšæ™ÝįãCŸ@ŸĪ$qĮü9sFĮÆú††8ēs`dÔÎYļė„ĨÖŲŲĶģÓ5Ÿąô“,nڎÎX‡Ķ 7š7ðéEĢp…ģGáœ3Æd2eæ01FŊĻüÄŲ9vë-7oāþFĢP?‚69 ĮĨvwbcČc`N -…ü+ Á\˜yĻQĢÞÕĻqJœą,‚'%_ÖĐvÝ@Š"`Ž@ĨÜX˜B| ÆPašGŽÞēeËI'­Ę2/œ‚üę1T,Y Ī­PŪXēô—Ï~ËXÛ5°į+[ŋü7[ŋ:ÔL[{ÜŌóīįŠ!įŠFŅ8Ĩóԋæ]|Óā 0nÞÛû– ^ĩ°Ģŧ"ũC―ũïĸށ[8ÚOŋõƒ/Xs~ų™§WÏ})§”- ęęLĖĨīręMY–Õô=™ƒ3ĩ§kōeöóą^uîwÏ=w·Zc’"…emĮ1p Ģ-kˆ­M“†Ņ€51ĶāĢs‰ĒĘã`fĒFšœ Ũð1HŠÖ"ËĒwÁÎTð-OsAÎ3pÎ)›ąQ§i˗œŌÎíÛúûf‰ŠČ$Ã1ų*U ÜVŲ*}c:đkõï\ôNÄâ#7}fÎŌæķßuÓîûžÛ8Ë:ÛŲlÎĩÝŊ^öÆ ïđéöÝÏYų3ï|öZĢ̇{‡n=pëý}^žėœ—ŸvÉpßð` Ē`–,PČa:™:3@1õÚ“ĮíĐl;y‘ĢģŠķīÝŧ5úHDEá!ûĻ IAbÎ―n‡Õ&NųGjņ:ϒ!˜‰ĻQĢv‹‚šø9ą!Ģáߞ5ēŦĒ ‚ Ē l…\ō(l AģWŒÝ·wïΝ;§ÁÔhð|cŽD]Js֍C}G†‡ũõïýũž(ā›ŸþZčcEævĪü…ņEóõtŋõcL5ĸÞvËÍCƒƒhČXCŠxCÄĒYdQp>!dÎNb!c„SŽlЊó ĀHhfjÔϧÚ|X‰­5™þ0/b5ÐK@ar^aaKFČ Hâh΀é=røū-[Î<óĖę˜Įï ĶŪōŦ“VĮýÞ{f.Ģ?Åyį,Üø――·Ï^5įķ~ðŅëOxÏs^3wvWĢhXkO]pŌĐ'œ488x°ĸp_ĸĀūÃ{þaŨ—v ėŧdåđÏ\ýĖŅÁöYÜÔ{ĸüHF^OæMz+ŽyŸüV"š:ĸV=iÛķ=€Jœ‚o@ĀI―v,ÆYÖYēֆÛDbÐĻ0Į-öÁ# Ė0ÔĻQOĩȉ“ꑂbūYÖ›R”zœĩÂZxc°ÓdJ‰!ØÂ4!xūũÞ{öïßnW{<ĸVSmŒąģŲyãî>~ã5]ŪįŪ}vC īÐ―ĶãĶþõ―Ũ~öÞ=;ŦœÛāC˗Նwpdø`_Ï-Ýü;ŋļĩg#đgŊūĻ)Íý1T~<‡âåÅĢNļ•ŌööÛ~Ðßߋ†ÐPN5CCĀ’ûŠĒȉD€V+ @˜MšāœÍ>7k @ F3Œjģß tžÍ&1›PđwҐ™Ø-FŽ?nĮĶ @ŠíGcÜØČčîŋß{eĒ <îuÕEXEģč8G‹NéÜ9ð―_ýĮ+?õŊ_xļũ`ôū:Oū{ĸ]ŸŲöųmƒ{0ÂĘŪÅWœų’Š‚Ûä8ÝLýļ/õUcĖķmÛîŧwK~Â! jdĢ–7īÓRÔÛōÞsLD&1BQĖ Ņ〠F3PW›đB ĪzQį4t†…%jíŦÎd yĩš"ƒ6<Ķ =1ĢĢ#wÞþÃg\zé… ™yęĒ`šņ4^Ŧ å…Ŧ/|ڒ3cŠë­Û0Í ?@ëÆ#/8aVĸpÏýÓ'ė‚%ŋqáÏöõ8r0Ž@gŲ9RJLoÜüړšWíïŲü6ās^'ŠãĮØÉwÎ<ÛßßÛ­·ôõ[cSŒ,R9Ų6s­‡˜Œ | í \ïuo  Ųi–!fjÔϧZë\ûp\c­#4Öę°Ö>įaáÜXž*cŠë ĮýVi†9áÁûwíz5G1cę[‰(—;įķ™s~ų’Ũ§ņŽ> GS8Hpįý·ęí=täpϑC~”ÏïØ|†;õÔŲ'ŋðŒįũ ôŅņŧācfÕĮ^,LũÕ–öGũmĩÎäß(…+”ÐģNōO#zYërų‚ĩÆYĮ‰ģ‹ŽpEˆąîŽQc&RmNĒĘeÝÆÚtLz,CH1V#QfZēYŸ0‡Îī§BNQ]QTđ^wÝyg§€ÓI ëōjĒ åYŦŸūqņÆP <^J#BҐ{ïųámÛïÝßspĸĄžþÁÁđ2ĸ4·éįžvAs~LņøekÆô˄Į_,dáÁwÜ6<<˜Ýų&@5ģä § 6’˜Ĩ\į“ŋQ ĻÍŊhÎïQ„&Ķ{…5ęB.åÎqÝ!Df) ' ŠQkmL‰E,ãŒeÉ:gÐä2DH1E0œ„ïÜö@Ĩú:ýôÓSJyĖ<^„pĖãeYæI9› šģžæsoĸ§{M̃QF%Í_ØąoßŪŦŋöđ+6=ģlĩŠ8.ÂėŲģ~ö‚Ã1ƒG―8þS•ĮkvėØqĸ–­Þ{Ŧ Ŋ<ʒ€°rŦĘ(”!qēÖq֐õŽįÕĀ_LVK4Sđ5jÔS­†^[åYÕĖ‚°ĶRÎ §dÉPnovÆæ]AH‰Žą‰'&j⁇Ŧ{î§Ž·78žËē4ÆäW ˜Í'?―‹ÉG ė[[Ō}bĮŪwþï›ĸá–=wîx襥‡—.[8ÎÜ<•?Šc"æãuĮŋÛņo"Ē‘ŅŅ›ošąŊ·Ũ 5ÎēHJ Š™­ŦAÕĖ:[XgU@ÎeÍFDŌÃ1fķÖäU ÔĻQcĶQ­ˆXĒ,ÕR#rЗ bĶDæ˜R"4ų+ÅĪj'ĢŽ~1V˅E„Žo•ũÜyguš”įåŸPŠP–eĢŅČđ6eðO?ųŽÓ—Ÿæ[%DŽ1ĩZ)nĖ+Zķį‡C?øVÏu·ܰúä%œ8ÏŠëDČ/ríØūíþ­[ÄÕlé/4Ä™óZÖ͘ó|­ą\ŦĢŦsÕįžHČï ÂPĢF™˜ėŲrÖ&fïKƒDAĢdĻČi֚Ķ€’Ōœ'ēÞIÍŽĖ‰ģĒN‡;ķýčG?Zū|yĖ"\ÅcïLEĪ2…E‘Ý]Ý/8ûō;ķoM^ Ē@@Ī”]Ā|ŲĶgþüæŸŊ„bĮŸqMbšMBÆņ’ƒĐī[™†ođųæC‡zŽģœ ƈ ķ(bŒđ­Ų,|IĮ@Ul­ÕNˆČ"Ū(D˜}ÔC3Ã1;…eĶ)jÔϧZæ6EāĈFM 1*'`â(G™gŅ:ƒyЊ Ô~-›§” ŲãwÜVŲšŒ18Žos)*FÆF/?ëŲs;ŧ9ąD/’pd‡æí/úe_úc蕈` ŽB<ū‘arĪ―ĸþûïđëÎü; DŊ‰ĒF˜į\Œ‰ō7@āuĸbqŨcF-$OúkĖÅcŠ„ÆZ[‹ÕĻ1“―l;Į‹Ņ E#(QU?›CÁ­ģB mó―%f§§@zúOđ 1ŊE16>zßÖ­UųÂÜđ݈2•ŨĶÓceRӝĶÍėvōâ“Ũ.\uKïa1Ft—‘SķSˆ›7œ―ąZ/ørŠûvęXš‘|Ôö1,žDT nĸámGw &„h 9•jböÂų2°°ģVÝX í›"H°&ƈ†,R u C3“jU­•įÂÁœrHVo~óƒ(jS[” BŪφœâjČäãua°†RŠņČĄÃ•ękÍ)§LåV―ž6!‡ęĀĩtĮžãgß|ãmŨcc~L- +ßņ[oïž=·:ķ""QÏĄĮÛrjWū>ža§•‚âžŧïℐŽ3eYęó79œuøãIߊ“CDØje&$@ 9dÝ9DÉÃĖÜÕÖĻQ/Ī‚ķ`MÄ  sŽ9‹íŅæ‘īš0„@˜ÅĢ$ ™=C<ú°aÔĢĄœēxį· ä“Ūc cųÕã•ķm’UøP^°áÂsOžĻãPŅnØ1k[†Ę Î8ũŌó.ö>L~Ÿ\0õKŋ7˜ēĨ―ąi5ĸfÎķßY=`“Ia,œH g‹˜RŽˆīƀä,`–ēå9 ÖbŊ5f,Õ§”b–|ĸŪŌ‚ė c@Ýx ―ĸ5ÖĔž/EØZcRRUVũŪ ËžÝŧîūûîé&،ãų1ßwį!īģĢóō‹/ Þ3KĖÆš‹Î;ÎėۘâĢrëtĮOŊĻ8æU"SåŌn―ûn_zT·q đ‡‘ô§ĄÏ4ë@yÖĶÄ:ۂNý ŠFģțk[Acp‘°-aŪQĢÆL Q$-͎QģŨÖĘ!č$ĻŽAH&Ī„|†LŒ)ÅX4Š|ïOdīwËS•Ņlđįž .ļ`2Vq:GNAÞ!Ī”ōĀļųŽsšN ‰$Ĩ…‹―éŊ)ËrÚýė4 „ĮÖ'äëœ4öýï·jœœ˜ē0JQļ<+·ZKÞĮüäY] ĻŦmfɖeÕŌĶdŽÍ?0ÎpJ!„ZPĢÆL QL‰CˆhŽqFXrĪķk4bŒYf@n.@ŌdfĢ™Ų2W!J`2Ī·ÛœąÂrûm·îŲģ§(ŠéĩS^*ōþ7ŋZzÎO;sÓFąÆ0ĘÛ^ýúĨ‹—Ä#ĩ`ę#DôØĢîÔ cĖ―ũn―ëŽÛAļp.ąÆëXcēô˜A'Z !æČ Ę7„ē$ĐDïCHLÖäÎ0ÖĻxjcn3Tė%Ę$Ė RͧóŪĀYÍîBgp\2Ēn„­ģĄŒ@H?0i}ŽKÂ!†C‡Uƒm6Ýâô8f—ĘĖÎ9PðÜ9sŸũŒË:šēø„_|ų ŦôØãïý '˜âøĮ‰ĻöVƒƒXĘÅãĖÚi™b"ĖϟŠĮcŠ Q1&`Ė?"@­ÖΟąp–ŒQŦĢ5AÁčõŒCĩ…8ąUARöŒšÂĪEG61$g,I-ģDÎz_ Đ`]Œ 8S°úĐDĀŲ"{Ĩˆ þā–›æō˗-[–CēŨҐĐ6_d_CEĩĸðõŊėŲ·ũÕ/ūbÉÂEų­Óí Däx]ŨãŪrÄîÝŧođég ĄVþXē B~bĢЌŪĪýÁdRŠ˜s)1d@$ĪHH…˜IŋXĒXĢÆ ÕՊՠ?ąIĨŸÄĘdŒĮ QHDOáēLnÏÍCjĖ)·H Zëh '–­ēÜõāÎþ§Ŋ\xņ%ŦVۜ?~ÞÆfš{ÔímFNz.‡oÛûƒ‹NđxãÚSąŊxŅKœĩĨũˆ8-―N}D‘Đyę#ųzęGc*éØ ßĸîðÐ2‹1†BôĖā,áŅëČĀ9B%īB ·IȒA›RĖB2ԅƒU&Îî2Ībáŋ2Ė,ÔĻQ/Ģ*ˆŽĩ&q yödÉÁĩŪ((Ķ †Ó{g1DŪmQÍģķ| HÆ"9Đbވ‰ytdčKũ7šú>~ÍĮŪŋþúƒ==1ĶĒ(ˆ “$čŽëkõĸÉ xÁĨÏ}æyžēęäŌûé,^Į=8ÍJašĩÃŪJx°eK5Dk…° QLÚ lÉhņ8ŪȜ+ÂĒĄą;ŠNŒ)ÆD’ę―"BƒIÃj+LĶ"ÔSm3T€VKe[­ 5Q=ēHÎ,Žö0Ææ@Ðŧb_zÉ.擟 17éĶ‘°ah øPU—ïÛŧįÆï}wÅʓ6_pŅYg]ĄĢĢĮũ-f™ÔŠđËŊ~þķ124ôœg<똎–cfÕĮÖäņö˜Ŋ•—y)ü―ëŊßĩc{îēDUŋąJß@ã͈€ČpL1ũֈ(… a…vtŊæ%’^X\̐Ä9}ŅZ—†LáÜ œjkÔĻ[4P؄ȐŌ\īÖļ‚Hôēh)û!ĐEŠ( kLā”ûĩR )%(\Ą< GĄV@ččė4Æ Ý·uËĸųË/|ôÃúýũŋĸë_ûZUûBh4Dt”$hS7 ēOÞĨ"ĻVJ?Ė‹†RHƐĻę) ƒkا` C<°ĸáƒ_ßĸÝëūģîīÓÎ=wóE_žråĘyóæc*ûÖâŋ+t:dăÂ{c,ËrĪŒGFŠYĩ·ũHoõŋÃG_ŽŽķÆĮôMCýý}Ą dĻzį“ô581*Ž11Dc -"ĩ'ÐF̈‘C y4ŽéĮ'Þ— āŠ8æŪFĘB a.Š‚Dd,sÛ Ö\[ĢÆ QTc~ū=G”ÜYëŽVæ0”M ,,dHWŅŲFî+D=5rEbð™‹sļbž„uÎ2–cŒeëē-ÐZSzotl.Ë֝·ýāþ­[Ŋûöŋšaãģ/{NUPÖ­`fï}EŧՍĸčQŒ õũõ<ļĸȑ#ՔZņéXÝ82\MŊÕË‚ĩ6315T(đūLÅjĶ(,ķÐÛÂ5DĘIÞ{mŊ1,íxW8_úvD:ĮDd !FČY„!$,\CXDKnČÎ=9‚‚*„›ĻQĢV BŒŅ:G@cĖqVÁ‡(Ž)RJHĒņrŦzž*fHDŽqd0EYÉáŠ!æÅ.°ÄÁĒh„PĶ$Ö9gLÛüęKDԐÜíÛØĩcĮũþýšÕk֜sîæJĻÐÛWýÓ{øpOEīãc­‘Ņáĸ―ŋ͑؁Fą’ĐšwĸģŊİïi)I†1ÉęđÞC+žs€BC]Ōô(3>~~~fMûđîŽšŪ•™n~ýãyžŪþ|þН0BĘĘŠÜ~—ŧS‚ c7ZmnFîÚ&vg?IōōdŅÂ"ŒūũvšŧÍúĪ›emÂDۈggw­ĩŒSæ˜ ņâŋŋFĩ*u—ŲŽÉ#-6‚ŨšēKgâ>Å'#qbžm~ą3#U]]æÄ*v[„ŽíËÃãŲŧKnæþ=GšÖJ"s›„ĻĸÏĸũĸýĸúü?O‚ŅXYÝÉ#AËܐf•ŅÝąÖú\UÛÝÍÜŸýøWP1}‹ôeYÝ]áôVĩšøÆqĄan0d…ÏnŠRŊĩîý˜HÃt2J"āŒŲ4nIęöjĻ%ķŧŨž›;…[€đCøsxņâÍ@ČÓŠM°*\‚ö~}>ŨaÎ")ƒfe^]#›uęĘŠðedž ÎÔF—Ęę"éf•‰ïŨjŦ”sīŠÍ„ðĩŪËh  …[wú|þųįóé.Ÿ··ÔÁšštr=Ï@ģ0ZUBšÖĩwN,ũŊĪë0teÐ Dí2DVuĨ‰‘äáŲ8―5­æļéÎÍÐá*Â#ššÔ§oøÓU3A“žYÕ}^ĸÉļ™/^ąW„·æ+Žĩē PŽ0ZfŽī <&^Æ# ėņN™[-žÕ4Jrģn:ŠRí―'V­RŸĩæÉh™™ ûþ!ļŪČÜ;ũđ›WfNGúõéî Ōĩ“öŌúũ‡SÓŌĪqWĮ3kÍ?Uįó<„yÄajŠÏš8ŦzŦ[†ĖÝj#!ā+č6kk†ũđũ!nöpÅEēšwÞ]Ŧ†p]ëð>ÜÍmýüü {4pžvĸfĄeVWýE·Ø‹oc.Zꖚî;s^ŪÕîqâŽn#-Rwól!ũsfÎåk?[‡BÜVu p#$‚îfa§óžîũū!ųx[Mš›—Z‚›ŧ[g·æķŸûøψ*õøÖšË@ ž›@ø‚áÐ&™Y>į5Zuh=Âïûå˜Óm?ÏÜnųĩs qDÁ4š_þė8báî†:,ú€ÆˆKĨę hųaðjPüŽÕ6ÆšĘ2ÐWĖ0þGĩ/^žną’ĖėóųT•šíäždmļ{ֆdnĨęę0ž•`ÆsAîz€6w„)!Ŧ†û>’Rs_ëŦ—šNwYæĢŪX Ÿ”šf]* 8_ĖqŦĄŽ”t­›Kíq]3Žlū°wOeoÁkIš=Š™Ý§9ˆŲo„_îNÚÎ=ą$f‡aŨŋˆðówfšđˆŽūŲ]­6ũXŨģŸėąŠ ÄTŪ=ųˆXĢLã!qü5žxņRí!—2ڑ‘ŌÍ#քŦ‚r?3ĢxūÅŦģZM·đĀÏAœ•ÏõđÖ7Mܜ’†ĩÍmq9zV3öüDĪMŪØÎMÚhQ+7Éðčnk]<[āę6·Vå―=XĩÕ‡FH›-ęÎMāZ Š:―†qÍĖcí―Įˆņ­dĸÞŪŠ’ŅŠđ1ęPüō%iïĮÍGnDÜ]î>oîëš&!ÜÍãč p]’z*|EøÄHfŨ׏e،]íæ0Lm™IpÅ5ō žxņâoÞĻjŨ8ßč!Ų­įޔÜüŲÏÞÏôЀŽÞU LĘâW#õė  t Ā]]~økįÎJ7”{ÏĄuÍáØaíėnÐĖmÉÃŨķwÍįT­›vĒĮ[C—ŋŠąšâj)kÛé^ė,ĐgiÛ ͎SvÂqÔûđqø‚N„yäŅrUËÂö~ūýęį~ĖÝÝ&ņÜe={ĸ}œ?#A|)USI Ð@·óüŲ]xņââ] dëšēUÍdĸĘ ƒ­ļ>į[ó~nũˆkĩzōf!ė'!Þ3uIm‡z*ĮÏjÝmīŠQ‡ā딙ÍFĩZt_ÏswuD€ĻLĢ6ÔÎIÁŲCÕFŸzp|åeóåŪî<Ũš*Ŧ[îArTk hĶęhþm3ģˆļ* $iŸÏĩŸ]=ig.ĀÏŦîŪ™RWÔw#án6ūîĻē ģóÎûyûÜ>ïaxņâïá] āÔÖŽãzbUVxÐgÍð€áÎgRĐhŪjŠĻŧ€Žĩ”=ŠëZŲŲ5Đ#ķ=gô0<Ï:<@B;|ígĮŪ1ï0? ˜efvÞ­tØž[`Œ8F†ßāąĢ‘°X!a8tMd ;Ÿ=ÆbíŲg―–Ô]āaãM˜“·{ïĖšÖ%°2ÍAcÍð wĸo77ĢŦŧ*Ŋ>hd—ÓÜ}ïŽŪ}exņâŊáĩ0t7f\4úlQŧÍ]­oĒąŠgv I&ĨŅÕJîA°Ô$ŨįęJĨüLĢSYF"ՕĨ.s§YWļ֕ßIÓHveŦ'øuËŊ2S%ĢŪÏŒ4íįÎßČąCĶáąÐĖ,7ŽðI ž Ûnyļ[Ta~­ ŽĐŪĢ‹( ŒXŧʈĩĮ–š|rWW|5ŋóĻ‘Y­þÆĨ?ŧZî‹@vuŨšVfÕYóۙfðâÅßÃÛÂĀėþĮ ÕŧŌŦuՌǿöēĘ}}ŪĖŽϞņđĪĀũ‰ąâūŸŪ:/ËŠóëHĩöo˜ÃŨJK·j™ĩŽēš+R‚ęÄĒ“°ð5ᓐ<\fõWŽŽĖJ ŨŠėūžšY ŒîánŠ}ņâjyþ(2‘Žęo78Y™SĩÏ5†K}’mīėšTšÏšĶũ’M‰7& ‚qÅ0JēąüšŲv6i ŒžŸ›œ˜ËcŌ]v,Õf€Z='WlāÉlđã·ŌܖwKUį þïã8™r'ˆĩBj3Ģ9ˆý< ÃÝČg§ßã,ĢÓ9ōē댈‰7ðópYiƞ"œ]$Ht%iR}Ö§šįîœfŋ­ ÝoŠ}ņâjö<;+.uÏŅV8Aýōl•4 žL[kA™4ŦÛ=üxÚö\œ•] œ›7$Ĩ–z˜ŽĄVIÎ7}îý˜9ĪėDXÂĪ&lz'L "ėý /?Ï ĻÜ|ĖctÏ7_FԜb͒zTÎFĒiVĸbęŲMã€@Ŋļ$ä™Ų[õė[h5<žŦČÉüEuuŨ$v)Ią|ĘilrĩÕ}ņ§ģ <<­Ū:Ą3æÕĪŅÆ”5!{?4ĖÞā9 þđŪÉĩ-։j<];F0Šą–šŋ9 ÝÝĨ s8wû*ôŸwriIaÅrģIZÄE;―€ŲĻØX–s·†Ękï )|þD-‰ø“i3/^žTŦ‚_.“v&8‚}ÛĮn0ĐØÝĮS&ÖgŨy ƒ€ đŸŅH-PsĶô $Íyf6(&š{\ 9ô”ęónų6ЃYeģ pß{w—ӍŽJ3Æņģ å]Ũs·Õo^Ũ؛‡ÃWNoûŪjašÍ†4Ņøä$Ô8 oQÐÁÔįDøóėœx\ũęúþ"­Ķïō/îj_žx o, Ā„ĄDÄÎįKö͔Yá‚íû sšÕŨ–:_™>ÚĢ™Û”ãžI9†éŽýũĘóĘNáÍýóCšņũÖį„ BĐÍmyHÎ=Œ'aŦ KŠýė#l―ēģ[kÜeYéūÜū}―îaôđũŠ8Ý·Û×ņ‚cN·Ęäœãļ›vÆUŠwZ`EÜũMÐÍ>[#[ę'ÍÃČėė,:#VU7:"ūķŌu]­Þ]„VÄĸԙo Ë4nĶļųõYõݗ"ÜŧŌ,ˌîÝE#ŒÝ†ZŨŠŪtÆÄ ΜH'‰Ęt7ĸŋ"ąĘ0 BU p\v>ĖέÆ)ģéМ‰°ĖœBr˜eXđ‡Žy äaju €Óæ.ĢŪýųŋ~~íė,vļ>ũ^+’―âōu˜]mN@ĮüĶĩŪ‰NwssŽJlü`ã§ dí§JŸÏ%`gԌn;7Ð Ýý] žxņÅf-°Âþ$Þ`@nßîXúYđfŦa0Ņ0’úü›ŒíÜđk“‡ §§Āš›ß=.$MõÃÔįüķ6<Æ9™óYK€˜B_w;AY­nsũ|ntwHĮA°4Ō=ŧ:Ïäî“Ļî#šh)ÖŠŪÎ&ŋƒ9Îgåš9Žôn™ņėg5•kĶVÕ|2ąŠX8„ÚUĨ딹W&€ŅÕ$†GÖ+özņâ/Ö8jtþFóðpïęÌŅÝcÞ?Đá;}zģDŌCî―Íýš.sB"q:>kVŊ5Å\Ûhî>=Ã_uX`øųEIwëją‚fđ‚ŅžŸ[‡ŽÞUú•jõ7>FÕ―+[ĩVėÜjLãúHwcų8 pšĮš› P+bl+‚Äó<=5ėčýܧom$~$Cß§Óė‘ģ\@ŊÓR>;5Æ:œ{˜}ÂÔ_žøcxäņ°ŠFīv%ÝÃl^€îņ ÐøüÜ0ÉTe‘tš~UcËŨ!jH·ã”ÅlNģ:ÜaP7hj™™€ŠžũŲ9AāŠÎÞ-Ūp ”ėÛ} šÅëė‘m]ŨÐëų<øLkÃ2'lJSč[SSōwIÂęn\ŨGnđđŒųėEÞÏmnq­}? NcTĶTá—Ï~ƅnY Č܁ßŧ„w)O*‚9ŸCĘŠņâŋŋEĩƒY9ķ Á‰a u_ŸÏaF ECUĨ:<ŒxžÍÆõϕUhð|‘îķB­YæķĘ-Š’įÍU-uļ…Ŋˉ'ŧ§ģ@€Č*I/ÓþMžxójIšqŽcTÃŊÃY%730ïĖęåîf]ßĀ„ĖzžM#gą+]ë2āĻāĪRvũpkĩĶuÖŋŒtŪXÝínæÞ­―7ÍNhKuŦē-Ē$Ģ…y˜ĩäĮÍ&p|ßëęŪŪ&ļŪuîAöߕ'ņh>”™uiül4dnĀ,lÅĘn`ūÚĢ[4LBWŧq}VWöāė ~ĪŽ°ˆĻLmķ•vöË­:ęæÖhsÆuÍļMČý5æūxņ'uĩc*U{›Ųu]đ‹Ī]fĀŪĶņŠÐ‰šž­œbšYMøũr@ÏÞ ŊuAĘŽ5ä\žÂČŽ­n3“°ũvģŊ؀˜ÄØųæÞÕĸüs͐[j4ęIŪ0#ÔÝ8ņ,'Žë_˜;đˀësaBđûõ‰ŪŠV\Ëh]%” Áéf_2û]ģJUÅŲ4ū$<­šu]0þÜŅøýœxŒ+F )Ėž{ÔļjU•…đųŦôzņâONĩÂĐVH?čVϊ–|ū[ԇ"ŨšĖŋQŲąV Āô"xWKˆXZÝÝ3PËÆ–šåfĄîp'Ø­]Šyeí'% guŠKģ Ąŧ69đą&Ÿ!%įĮč‚ÝđŸmĀpũ4{Xev5$ʞŸgØZ―ÛÍÖy7ĩštĐŸį‰“a‹Öa|œÂ›F ĄÚÉó§ëŠņ/ŽõĐ{…ßgsēօ3鉗ÔÝxņâÅŽqėžÚp­v mÎĩâþy†[·žûĐLĢuŨøŊxRßûÎ] ŽX•ŨuČĘąŠUŨYw2brē;Ūð°ęėJ@“Ÿ0æąasâ\œÕ]ķ.Ŋ,@îáũýÐčNUKß51čĶ™Gd ÄlTóŲÃÚđG€Æ#°}ĖÍčŪh:{Ķ xRĘöNŠũÎj8y]ŧēÜmHœīÏįš„šQ2dew”ģg+ ĐŠĖĖh‡c_Qí‹ķ…ø/ņŊ!ōĖđAąĮ§0qV Æ+56•+ŪÏĨŌ0#‰ĖęšF2ų-iîœŅÕŠ:"ĖÝĖöót§LüÂõYSÍÅErŒđÓÂÝBkliŦŧwĶkwŨl'@üïūa\Ÿ‹ —Ŋė&'Þ ™ĐžŲŦ†™ˆp[+žįYUvÖŽ-]Ÿ%a‚~gU]Ũá]%ČĖĮ8ĮóĀęóo‚kS‹Câŋ‘jÉs€^ŧÔá1õ-ŧw0c[ŠĘC=ÞÕhŊņ°N$öäȘÉÝF;.I-āëŽ5PĨĐóX ëŨ0–5+‹enõ› â~žŪrzîœīšˆï˜ŌïúĒĸ;Þw77WũxŽ€ĄŦx<ģįŋšāóóÐl]!Ąz-3=‚›köķcįíŲ,:A›c―ņ ĢFĄņ›œĀcĖ՘wI^žxņWŧÅŠŌ<ÖĪÔ}đ{íâ9æę]UõŦëĘęöeĸđxÝÃŋb[_šĪô”ŦĶ(ũŋŸŦ[hĪK]ݘ:Hēģap7ĩūúēP‹Öįói•Ð8Û3T_ŸXWæNfš~ūĮt;ģŠÜܗ{Ņa€„>ÔÏęžø˜ã<ÎáėË>ĸY€uëžņÄK&Ĩëúüïį'ģĖÝõWĩŧ-ģ‡Žkķģ43—JýšÅ^žø“šZue–Š ûđïÜĒÛŽ_…v2+', bõN:—/ZžyúnˆÉëšOšĘ‚ÎúâÞ ãZęžîÞ)ÓéwV'I$væŨ(âvÚ'‰/^üÁcąģQmvC―ïívvē­oË-+áö›cPæððQ’šŪĨ>Ä] qŸ\D–ė.ÚapáįįqøI˜Öī=fōā,nÖTíJĻ:Œ‘ Ņ"ÏfŊ•šęÉ#Å$ÔĻ›sûM\4šžû!Hz*ϧ qîAðð3kŸļpØwtĩĢlĢ‘Ī~Uà įúÏįjÍ áđģš-ÎÂDåFáŋŋ‡wW[ģN5š{V’4šĐ‹īY,ŒÆ üԂåГ?kķæTŠŦ3ŨZø(E˜UWåoÄxŨš<"ūüՂðÜÏhi݃8üį^ŲûŲ]uýóivwŧ/3ßÏs(•âØgÍWؕ‚<ŒhļĮu]Ý­–EļG·Æõ0GmgÞ Md6?åņ/ŠSÕŋûŠÎË*Pîūۘ_1r‚Ė'jg>Ū8ĩ,ĩHó“?iîËÝ`•eösĸ&^ž "ÂÎMš/Ÿ-ŠF?ŧΜxŠįūŦ;âŽ_wđø ÄĩōHúÃ=Ėïû>,ŋE°ÝY=Ėîûŋ~ōđŸé†éTvúōCÐčnĐg”nÁÃŊk=?Ï$ŠMāËļŠęđŸŠtógïŽBœ5ërwŨ7Ō° IYR—æp/ģ$yÁûįĶųīįÎcĖyŨéëČÜ%Č`ŠkRuF2áslÁ-‘q ÄËĩ/ÞžZÁÍŧŧ*ŧsÛÏ6ÚõÏ5e6æF°ŦhtsĢíúēdŽg•™­2óneÝÂŌ“ÛŒá dÞیSå@#!>3x\]UÕv‚ēvÝMōóÏgH؃$ŦsÎüŧŧšĻóÃ,FŧŪČJĄ#œæÏŲüš9 1”TŲîg0§ŧã;)Ó-―âÃ(6ĸE<Ï&nÓÚÐĢÆÍįĄŲěIš‰įypnMðĖķÐ[xó'ņâ] •ÕÝ&rаĶ{q$Ą+øM›/ė3ÛV Ï.óģīū"*ËýßyBŦÍėZ—TûđŨŠk]™)|ÏúOŽAÔŲ6œÂ5Đ æIÕgf\Ý=ö-@ųlád9Ēg§ėîũQ,ŽXf^§õ€4•ĨŪ)Ëcrû–žý œqĩN-č°ļ/ŊŪŽ$ĖwʂûŊðͧRwjĖģģ sÎ0{|w!˟Ą'%Rxņâş<kTuy,’“á2%ÞӜxŒđŒ6Qĸįú Ý˜ŲøĐĖéá~ĶNĢā7#FÂIØēĐ=xØ<žįžāDšgķ™…{e>{šŒDPësM1™y,€Ï~ô­VėLyÄ:ƒ°Ïˆ U•әú䷅Ð(ĮfãėnfV]~@ÚýütOÃcKĒņ[nvjÓŪk5z"ĮfĐ2%7UuĘoāôJÍaš™ĸÜOõáâˆ|v7ðę^žø›…7ŌŊlk§€pŊJnîŧà äŪ27ũĻîųáįĸ|ōyšaFė_æuÍ4݅•y^›yí<Dģ5?tĢZûyĖđŪO·@†G˜WĶ‚“ËAĐŧ–ŦK%3ü–øĘ-&’\-·póRuŨ!âŽnģïi$ Ÿhš™ąšŸgSöÏįڕ“LÆÔĀßzóŪlEĀP;šĪ“ØoÚšVW?ûžþöîoåąÜ%ü9žxņ‹…›™ĐÃiũBķĖß}éö=a‡ä5 ‘vūŠĄ~ÍF4ŧžûgŠĮģē[ßo);û0ÍsošĮ:lØéî§õ ũ9ŅōONáÂT…?UEZ·”Đ:ë‹Qŧu†ņïŪ ÎXęÎŽ{G\ÂoōËô0ķč!k~ŒïæÄ°~3šĖL`î43%Cu§Ô0ũž7Āu]$ Á+fM'ÁŽ4ũ–f;<đŲ9ōŸÃ‹ŊØk ßŽuf^ą Üũ-ÁÝÆĩånW„ššZŌō™ÕŌšBÐxÃĖŽwCˆ9(ëÆ‰)ĻVĩüŒŦđ‹Ï~$­ã˜­ąŅŽôU Hîįdp|iļ>ŸžôYibdŦ“ģĨnKâįޕÝÃxüũSđӗ}cČ[ņđ<žš-|yô·;îĄL@$@{öÞųÄĩhĖ―E-확ņõ‘&ÚĶNÕîÏÔčî"ÂýĘ^žxIxø~6š‡|[ßėŠ2ÜÜýŲ5ÐOjļVDПĸÝ~š %ėûW “v\ÕåfĪ݇[=œÄÏÏífë45HÏ~ķZ”{|ž[ĀŸnuW·ÖU]Ųffõ/Ú8 / ‚Ē™ß??$ŽŋVTøŠ\ÍÖ?‚Uá” #Zt­ó™ÕjåFļ bŨžķ›u.ß=‚@UčáUóüÆįÉ03ũĖ‚ÚÝbš8Ŧģ—lĸ"^žT gxė{Ÿčn'lÔøænDæîj_SÍ]ãzzîMrÅ ;Ô ’6åÞ0:Ų%˜NPÖ%ĩ$‚~ē―ÕúüóĐŠR“æ+$fn‚q­ý<Ž&[O>4―ŧ+ÓÜŊ“?[j3žĮxĀi,įl*ÖZ'ÆðwšUNø7í›R&3;2ÞÛ<(Ŧîî"|…WUw™ÄYŋâ’í]n”čfÝg­ÁzōwÛŧ3s]kJ|Wę wŊųe ÓÔoÞĖŸÄ‹·ðÆėĖ_Fs#ŽûŒ{$ŒũýTöÄVåņ}]ëī”;=܌5 Ð+ū ģöÆäjnRgåðlv‡ûšÖh°fáÐÕYûŨÍÕĀdøÏ~`FgŨŲ"ĻęýĪJæ–UįmbÃÔú\—‘đũ8ß:ã óčjuã0mUúZŅÐXlãø)š hŠ­RÕu­ĩb?šûōûŲRŸĶ uÖ0iUÅrÓŊ"Ÿė†ŧw– 7?#vŠ‹økxņâ­q$Æ f=mƒFfhĩŧ‡{·D á pÃ2ܜBVžĒÏÚӟį™!ÎĻÜ)čätî>!°Ũ~žyŠã­šŊų.Ą;† 6iŨ(óėjŊhĐv‚tĸ.v=bž^ iˆ$ÝĖíŒŦíŋ>‹ģ…0HÝUe6qâŪn4čVÕ­qčrB愰ēÔíáĸdVĮĢsÂŋ§Ø&s{ļIĢ=ða]Ւŧ /^žšÚï—h›˜+ˆ#Š2cDĖL4Ķh7?„Xø §ĐáØ ÔiÜ{WĶō˜I :ėšV>[-7_ŨW‰UÝ ŧęüÜÆÖÝaÖŠęŨÚy.ˆ˜õ+(s'yŪéˆ"wrrrŸ=ë‹óx›OÍ䔛[We“–Ï·a`Ŧ‰1ŸėęXņÍ*ŸĐ\u8ú”ŒíúFš'3ŨėðŊ/y`á|wĩ/^ŠõSHþÔCōšÂĖ4ĪuvŊc*u(uÉ{?EsuïgWi­―aŸOíꖅnOĘLJ}‹Ęݧ;ĮÃDŒyĖÍf(ž )íl~…ÏuŅ ð(Æ>Ģ9#ĀtĪį—ĩ ģrÍÜIŅ`­†qš~Oz™ÜiÎŽáŋĖĐ Ļ•:Âŧš2ĸKŒÉÅ:įu{' Ũĩžįל+öýÐāîf6 ũĩũū7ŧÖÚÏÖа ‹Ų{ü9žxņęj}y>[ÕFčtqƒú|.M:zšoēËf•Īˆ)&”ÃŽēëÉ0úqp‰Mās}jNáŋĐ.e0‚;·$ãŋĻ― Ē›gn@îŅ’Æ+üY ŪĨÁ*SjöoÚ„ŠÆYŸ&AŋâŲŠ#üZëđŸV}þđštÖŊ2R8äü\Ī š1ZOŊđ ĘėĘþYãŨõČö.@~>HvfaNĄÍĖÄ&Ķ}ōÔAJĢ ūũc ðņâÅŦŦ͚hAÕÆĘ#jNoH$ķ_‹ä}߀b]nÖ]:Ė(ēÔ ,"ũŪjœ‚}ÓVW·ôdzÉÞ­Æ:Ĩ }Šo—ĮėmŊosLÁkg'‹/5ä4ŧÓÉ削žĩĀWopŸŸ§Õî45|ųšŪ― ķ1qãĩ˜aūwÂ@3>™P[8ÃÎvëš“7€§‰°åŧ·Ų12€SÉyįÄÜ|Ū+swũŽ2žc$ņņâÅ+öŠÖu- ŋn[qUKęC(§Ó0ÛÜŲØųÐ,,č|~œ‚Rö.3ð:'WKR•Öĩūá,°Ïúdfíįԃ]ũó˜āææžŸĮ=HLøUauuyïŸm„#66ŌŨFwĄ/Fõ4㚝éXÔL #- ZÕ§Ļ& $ŽÍG1_úĢš,ˆrwî'ašÖĩ+Ą°>ŦrœšŸĸý˜ÓiŠūũžxQt[NwïžÅėü›…7/^ž „S)[ëģT=å0Ŧn1‚'gxüõY9ÍPęėĖ-kwë›ļødw›y+ 4wƒ šUÖ!č0ũîúZ‹3už’…V 2ũ.ĩTĪMIÍÜÜ$tŨÔęø3Š1ÂĖîŸͰ čRV-ŧPmÄõųdíę‚xEŒb­ÆĸŠÉ‰ŋ‡/^ŠĨa­ÕÕ:Œøų\•{{˜„ŠūÂû†,|ÆÆÝ`8ŦÎÐý+)Ĩ R%ÉësAĖL•ÖĐ Ŋj4ž…ƒ_ŨĮĖšŠ[vũIâ3•å ‚™ÉÜ$U%ĮÍŒ Į$ĶŌdÎîŸý―ØxNÛūK†Ė<Óqd6ÍÆÁqÔl<ŒÝÏó„ĸ‹ođŅi}~•e6Tl;ŦŠOūA?{Ó}]'”6Į›ËgïÉP°ģÜ-ÜßíÁŸÅ‹7Dq‘jõ$ŠäއvCp•Mē A0 7lU6ë 7ŦL“ß:Qaæg°…Ķw+ŸÉr wo5Y§2+óš.ƃlēoÆ50Ŋ$ŧz,j1Ūģá_óŠl!Ö:.ƒē0A“ģÕÕ§E‚“?kæ­6CW‘ø.Ihŋķ.3Zw ]ĨĐu4Ūųo™ķîš=,ôm[ð°î&—Ė*áĄÖôĖ#ŨÅċŊØk†ēŅųũ›ųH tk^ƒÚ™fŒåÕ)õTŋTįŽØýMĻņYŒJ0Øþ―ÁŠ_SQUuØ0;Ÿ}K ™ĶvwŽĀq@xuƒĒqlÆcΜ%ōtĄũĄČņ,ėÓø›™æ‡­Z€ûôHĘÍĨ>ãŠĶ(5"|ÆŌÎ>+Ũ2§Gô‘ëΚÁ$īÃĒŦI›q{ô>ŌãnĢ_Ũ‚Ô-‚ø‹xņâ] +"ŽæīéæËI3·u…ũģ[}’›Ūš"@VuįĪ šë9Đę4·ÏéSĻúė"ŅT7ŠjÚÆūĢīņīČĻŧâôƒweu˜é Ņ*ŧîÝUՇ+ÃOąiöČ{§đuWí=[;Ė~ŪĘt§Ý#Ŧ=–Ũ°ģ{ÎÔܘMÁ+–đ?ÏÓUĪŧ{VÆåŸĢ‘Č]]"mŨSU*ØŋžfT· 4íĖ*‘Œø“- /^žT[YFÍ&“EęĸWV„ˆëė.Ė\PŸĘÅk­ĘĘgCÖ­CĘpũ§ģŧ#ĖÂrge­Vý/šģ[rûŪ_Mæą-€ĒŧŸ°Z­å’έĸmυPÝl|ûÏŦqÞîĸnļéÉuĄđؙݚbuaÍĢ+íHÎŪ6žŪ%@úē!ÔĀygr&bōYæ†Å“™Oš{,ÏJ€f~ō' ‘˜rÉåŦŧŧZęīø†(þMžxwĩ+Ķæ Æ)ķiĩŅ@f%SU u,ŽTĩĮ—Ũhá2ŒðĀ$ŧK-Â*ŦUū‚UĢeæføß}wj-ŊcĻ2Î24+ÛČá/ÂNũíDsÉČĖáį&ÏóīÄSvðÝáBnĐëģt5ĸüŸOĢZma‡1ë\<ā’ÆRWĶAîÖĨntÓÂv=@ŧ‡‡ƒ°ðRïgwķ#&č ŒV˜gˆÓH–jÔ$~đ…{UíLâïáŋWW›YĨv3€mfī˜áQĖŦú·7aZōðŽu•QfAōįūaĪÕų4 ĩŪŽ­Vx˜óū38ITĨ›đųŪ4šfLuw™9Āû~ĖlĘó)Ũu•–>§ÞÜH'øė<ŧŋĩáW|R5[ ũ8kb42wō°Ą{<ûAËV@Ý)ĢŅļŸĪŸ =aÔ!{žœ§‹Æ {ŽĖŅ…gķ=Ÿ[•jUũXæ`DŲ”―Ŧ:<„ŋˆ/Þ]íá7īxŪŠŪŽ‰=,íú6$ķīO…Ē3ÎIZ‘3Q&É;Á”ÜÄõĩÞĸ=Oáá™IúŠiÄ!ĶĢ·j‚îv?·qļLyÞ$"†7'$aÚgéNzU“˜ûfUCæ‘gzÔ TŽïbŨ֙@§lÜÜîŸ[R|IÕ7œ·ŠÍ N7ËŠŪrw‡5(Ââ,mģiFÚŅ#§æ~?weqDįą?ŨR++%]ŨęnAķþŠØëŋ7ÁÝŊÏęėŲTŅ;'ĻŲ-JV―%P8ëŨ/'Q- ŨššŦJUĩ~ )ۈŠƒƒՍÞķf$4ģę–ÎËsoĢ™›‘ų―˜į]+îöL5ŽÉûyŒãŨ…ĻÍÝŧs…MՇûÍqčþp―„įÞ"â\ŽĮƒ›Óąx­“ Öíæ!ffáë[–Nƒ°ũãf4ņTþ˜ŧ›ál~…þ\KŲ‡ũíø[r3tKÂċïT hßd\áīĘ wŋ\ŌsoĢĸsīOdÁóTũZ1ßĮŦ’6Ņ* 7DĢ:=|]ëČc+VЭU―âTjsøũ‚AŦŲū·_î­îŠoô·þœvÖĐætAûŲN„an­X$r˜™Wõ“))Üï=+W§ÔVŸÏõtßfķ–GxeķúPdW@ØŦj,gö_šđīܙŧHh•$óoCZÎcÅ‹§0b,'Ÿ> ĪyįŅN ö―ĮļŅՙēðæÅ‹Ũ˜ ĀdÝMs ŋ ÖÖՄ` Aõõ­‚ČÜSÎļsï―'Sædĩd„_Ũ•{•htž |ųI°Ųy&>ĩúžĻÃÎ-›Ïv‚ÓĨs6ŋĩ“fË€ÞÅ_qÂýÜāŽ†ŧš=Ïķ@6TĨýĨHkUVđŧY` #š+֙šgSč;VŪÉ ;ËŪŽŪÏÕį9_wïī‘ß'ÎæTNTwK8ebOŠÖį›\ÓhŌuŋþ…ŋŠïŪöøz­Ø•&ÃŽ5Ŧ%pV™{ÓŅ-ž―Ãm­•ÏÓÕcóŊîÚIÍēRĢUݝâœãŊˆŊހNXŨt]Ũ—Ås{ö6Ó꘏Á ú)57r ŌÃb]uV§$9ý‡ÛÄuũœÝđ1ŸíæĮðķģ3čæÖUšbņØyiæē„Ŋ“Ņ%­kđG=Ųiîl•ÐŨuôøŧĄnÕN ‹ģ‡™UɰķŽĻŽxđöoâÅŧŦ5·9&2Áø;͕q\]ŲUn1R­āŲovuë$8ˆĘĒÕĩ–…ÏĘf @ĒĖ-ÐĀaå#uĻc [óI•čÍ ‰ âH[IeĨÐfY hÅŠVÕŪ]nĐŧÝ|ÅĘlAlm0ÂX;ø|þuïm#†8Œk߉^$h&ĒvÍuvVĪOž•b9ĀĖ ƒ“’vfCës‘ÞYþ"^žx‚š0ÆHbiNģž7͌ī“Kš™Uõ!AúŲ{Î2wä\Ë#bÜđĨķXU­ķ35Ņ.fĩËĀpú‘”Ĩ_q"ns:;3§1s­ÖĀY›ŧūu 7•ËížŦŨŠood#Ū8yŠYęXūwVnˆÎŊðËĖ Ë,R'ėQĩk™Oųņ/ŒĘMœæümĮ9Åél˜UYÝttn'3YU<õæĻ–šĄĩBø{xņâĨÚV‡yå6Ō܍–ωī=ĖâžU­ũX߁„ýFXi4™(eî‘7…/ŋ”=[āņóüĻ`wã›+–]]čéV(ĩ™…™ē™Ûė 2šÔÕíĮ);^söÏixĪYK{?jœjČŲčĻrNąŽZ %%Š*77B] đŸ„hųú†=ŠÍ―[úВŠE3•žJî†ĶØg'#@ft·Ęl‰-Ë7ŲëoâÅŦ@0ššáÖÝ roŒ˜`äęu]““―âôŠ?33Ú>ĪlFÕ―Õ'{Ķc†ŸķÄÖ Ÿ…ĐÃĶÃiÂ!;RŌē·ū‘ æÖ%iļU-ĖÝŧŦs fÓŅ+āT+Þ';&ށĘ$đŽĻķš@Κxį3VïFNqzũlWUĢčîqŒmR{8ČĘ"eÎ>ÕdvĪŧóg›ÛoØ#OŌ9~þ0"~[sdæ_§ƒÜ! /^žTÛ­ŠT•O4ŒúŠËÜj–W`DKnœÂ˜Vļ/€ÂCŌīęÆĩÔÝ sóÃģ“čð—Â}*ķL*MW››‡į“㔍ãÁ§ėÜl‚šÚĖAÜÏsx“Š{Þ BwŸ‹―šģōZO―9„8óėýŋŸÉÆíRVš…‡Đ%Ļ”aŸÜų5!Øļ9āΓ?{˜Ú­Ï;“\ūöÞhėîxæsĮëšÎ'Mƒ1ūÕúŦbŊ/Þ]­ÔĪŊP·šNbaÎļęjtũá Š5-đ : ä—™ýŽÖ126Äáē&fߋšä[ģ/ņĄã  qėŋӐhu˜ÆĸX’fÏ}ZC[Ā„~ū-w'˜;Õ sã8uß?Ü}bž-b]kô‡Āy›Æk­įyŠj.~ž]]'sU5”J"wėšŪqâNZØtÛĖįÁ}ßƊOfÚPÖwxwþ$^žx3hĖ]-˜―Xęķ Ï—Ī™RÍ]­RjįšŅt:BŨš*û\iãĮ•ÎS@ŧ—Įu}͇ŅÞÚO·r— õ ũū)~ŪNša•v'aį]Ôŧ ŪÏådí=yßŲ]•æū"îûųÎļÁûy CÄPevŨ?Ũ2ģ>ąÜ{Ose‹ø‹xņâjóũ‹°ZÏÞđkEPčnĐÝĒēš­qt—;ãmĐšúŒ~ÖYč&hNīŠŠ$„*IĒóÉ3wvžnņņy9fôĻJîé+ópģ―šŧē'… š$ kuĨŸÞÝIÏYWŠÚģũčîĘdÃÜöóĻ›ˆûÞ$=ĖÝóÞ ðß·~Ёn)hĸ·J xžgöË4Ž5áóų40å žú:ĘÜ\%3‰ŋˆ/ÞĐp7§g·9ÝŨh  DvĐû›ēøÜްX„ēöļË@ƒš‚{H51ŒMþÜÏe+ŒŽRĢÃý]™ņTđŊÏ2Ø―Ï—OĮø!_d%·‰ã:ĪNÐlŽīáãŸhs3ĖiŨrë]‡É1gÉ,œ‡ōģës}v—8qFŅ1ýûĖ&šQ†î’ 2ŦϝðÏįƒÖŪSvBgâð~ŦžshĒ[ųĪ9Ýíy6`h ģ™Û„Ûþ-žxņRí4æVwNüŠŊ<3 āĶ*īū‹Ņ ÓØÐΉG8„šãøå9VÔ8ä›;ÜéĪY>ų=Âj<.XŋũCXœ,ğšíHēüt2š…{č› æSG&ÁŒFÛY€b-ueNYTwgŒ+joˆaFĖ=/PåPŨšv•Aߊā,ŌVP€„eÎp€]yH|Ö;wÉjĩ›Mø·Ãūž/ŅÁeŽÖv·Ü;ĖEŌĐŪ œl)ÏjûÏáŋŨ˜Û­Đî8đ#áēˆģëlũˆptS"CgzTRÁyT•FĮīįŌ#–Ī“å*?ž[]úfŅĶjŅNŽ—‘f‚ē’ßôÂoX,;ÏĶ0ƒąŧ!­+ iœÝ…*Ÿģ:ģâDĘRfĪ۝Ųč‹ Jū.8ÐÍFtņ<ČΜ…‰™O…O\ëÂü–ŧŅ”0ó~õ!i‚―kg šÐœi{Œåj@lČĖ҇Ņĸ"^žxÅ^Ý<ĒKÓĒxūüÞŠ&͂™ÉЌ%:›Ī;ŅßWœĐó9æW’ûŲa„4Aā]EûÆL=cKA_VO’4#ÁÜÕ-rguö)šUaî’ęįޙu.ÏÂhžŽvī—˜›NŠÍYėę[m[-ïý<ųL°ÜCîmâZkúlĪþ\Øđ5æU•]48‡+Ý †îžĩnáÜÚÜō\ 33qîĮčÎLRnŽŋˆ/^ŠBFŠ”`4_ËÝóŲ}Ļ%U"l]ŦJā·ÐûđŸa―1eņĻĖYƒŨįÃYĐRnĻĖ―wS@ŨŪĐ/+!;AųĩÔ匈ĩOfŧÛaĀåq}–{ėgäe_o Öšï§ÔfGņZMŌ~ón öÏįĢCpšuŦŠA:í~vuOZg™ųɜ}: Ī*u—ŅhķOoåšūŪîZ+&—ķ!w;ņ:}B"cïÝ­X1ßVââŋWWÛÕ#É:œ+U-§­ë{œĨrs_Ū꩒āágóHMŠĘYø2ŽU;ũÔÏ@ęŪŠ ?LMûšËē@ZļŧĄADk6§U+–fĪčþ Ļ­ŪŲNt·™ŊûØ œFðūïVƒ4Øčb­>Ïņ]_Tv·ĸæ”ÝÝN{îƒéŨéŲ€dŦû·­ēŒ6 ÕEōš.ĩĶĖÆÝÔęÖōģų“Â|Ĩf}û–“ŋxņw“―Ô-é7ē`jÃ#3ģÓĖšvŠ„gïžÖĩbõdËō{Ú>ĩn>>ŨCĻhÎĀ;Š0sS5ßjÅn’kxķrŒņÜ9æŨĢÄšŧ;Ūh4ðΉãk] ĸūX+Ū%HĀá=ŸŽ^ ëŪʄ0݋s,ņsĸT—ŧ™đÁüüŦ>[ˆqčšŲü‡ŋšīá_'ÁoąnDfíˉƒČïķũŋī‡&!Áh•i_žxËÉŧû”ËÞûĐãÔõsĸ4ũ0ãÞeīk}zj_œþ<Ī!iïÉĩrAÏ~ŒķâŠ*5,ÜÍr§(ðãĩíÃzCX•éWdĖ­vyģ&6’ęCY0_.};%ãDpMðgķÍĩŪÉj-mUWũ8Ęš5…ļB?ÏcÆ3wåDôÚũ‰5Fm\=‹A™ É=ÔÚĖ>§$b­ŦĄã†Āi áq­PãlTÚÝ_ķ}ņâ/R­‡}þY™Iás}@uivĩŠę’››ýl~kMš=?ũ™‚bwpB„–  Í2ũVxžM1ÂHVĩNmxV4Ú!åođėĩÖÏóˆp@&2Rũ&øĻĮf^ߕPŧ‡ŅŠ’naķģĶE‚†nĀātāŽY§wŨw•ŠĨ’ðMđ­ęũãg3ssVÎHŒõų ÏVËÍŠŌarĖįIÆäĒ-f”nLÎ/ð69ūxņ'n‘ĨÎ2ō˰ߨ-IüoŌėz2'åĨē"ą<Њ€ëēÂĩŪ*›ÆóZŋI†ŅÕfīE’]M Öčų9Ĩ3í]ģū ë\-fÖu-#ēō<[ín˝Eũ5'Wt-ŧšj­aûÉĖR‰äģ· nN• öÏõÐ%šÅ{?'—k}?Tī4™4ŲäË%Õ7cŨũ~Â"ÜDžåăeV)?ŸÕ­–ˆ/^üÁhð.žÄ?:ïįڌ:08ēww‹ӔÔË\ĀýÜŨį‚ÔÕjų1ŋNëĀĖŽLĄiŽjĢëÚų­ŨĨðë[ĩþ ~cÍÖšTýÜwĐ""ŧÉĄ:WԁąĒu›Q_AĨi ‡ŅĖX%ó Ƨj ė,’ãāĻŽ)EŋĸũĻÅáÖjGÄ6B‹–žýˈ:―č+Öd2øÉMP;Ï­gŧ2AæûĐîÆËī/^üIŠeîėi­>ãÞÕĀsXÁüð‚čīëš"H7̈́ĶC6ĒöV·ãxŦĪvók]Ï~2·ŧЙ%'§NáH§úÐežÛ9Š6s7ŦÜR“fjåģ!™EVuĢŧÐŽŽcāüD Ļ*kuw„—šÆåU‰j#hÜyžÛ( ’yLxÄy:2Ÿ FûūÍH<ĖŽû[hö[Aw'G` ·Ų!4$? •›ā:õw- /^žną“ē:õYguĢđÅ [ŧŌÜÂWuw)"lE:Ņ\]ūO…,Ÿ‡†ëšÝųĻųY Rf~Đóü" ūžö7ô Âë<ƚØ-°ēÜ"wĪdíÖ4ÐđģPÝSŽÛ=ˀÖ9Î05‹uvcËý˜qLn]å$Đę^q…ûųéģĩØÝ+‚nOíŪ43Uïgwũü)F3;žĸíūoHg:ÆYSČ#Î5/^žøc „AÐ3įNČĀÄIģYüóđē{ îS[Ûf\ąŠ ­ð Y™fabWŦĄ†ÏÅ7;Û ]ë’zïŨ€ĪŠēïbZ}­UÝüŪNŅŌ)ģųœØ°Û<Ū+ŠÝFĢaNĻxaóü3uî'Ý}}OĻžūČŽnáāįwœœV§™ŧÛĸ_š­uÉEÆÔÝ$§ÓŽÕ~ŪßÚž~žĮÜÏ5օn]WėdU/^žø{ „ŅGĸŠŠĐæĶņyîIG‚ŧMX-ÎöîoMxTuũ,QGx sâ—=ŨúõLáŧÓŒ ŧĐŦē€oŽĖÄߎĢĂā>PU•FBągžņļÛÃ՘§ ŪCļąHV 7“4Õ_óü] ô ïfFÃ0ļÔîó= ]%‚āޙģ.čþ „ŧUý/$ÝÏmWðTóîóÖėlĢáŋðXĖ܍tg Ēƚô,;åēÜOšguIÝŨ=JRwëRu…ŧ[Lfë:4Ú:4ŅĨîT7"ēęW}§xÜÝ’;wf‡{˜?;2w:#sŦ@ė­þ|ŪįŲęí˰ šŧĖĘÊPŦÕ݇OūŒ6ÏÜÝí“nž“qhýüüÏ#ÖZuāđ7Dw7GgGÄ!LÞ{ϊ`lN ŲÓ*'YÝNæv‚w'õöŋŌÂÐąŽŠ+Óh+â+ÝĸMđ&čæUõóÜÕ;V {Q°°û9ķîgšÛgíš―DcĢëžĢ_ ÝÔátõÏÏĸāü~aϔnÏX] ž―ŧš3üÞ{f^€ûgÂ―bb ā6d75Ó]î>zâXŋ€ĖŊĒJߞsģ]ŧÔ~.Ļð8k UVļÛHšæōyjį4Ąˆv>$td™·ĪøĸąũÞĸQ•Ïĸþnē›l*DJ‚J „ :(JBGEéHŊĒJŅ"ŠĒˆ‚HïĨJ•ĶĄ@JEBM }Óö{ÍÜŊũįņýöLǜœ―ũūÏŲŪ3ũĖs|œ>Z–&A^ ˜ļóå<.ķĨWëĨf™…Z ģšR[•ģ‚A*ëĒū-F遟#$4”_6ëĪ!ã…ŋN2ˆ1šßœäŅ;Ⅹ'OiŲš5 SđnÔ4úŠę‹LNˆĒxÉý <|Ī–ĨŲ8Åzâ]Âfí#)‘Wfāĸ Øs⁆ öŅn‰Fūw[6ÍD‚[žQu·Y’ ód6õ4iڑAFEđøģŽhä"ÃŪÉ>2ĀĮnI ÛD‚‡ce.ŨÉlŠ~žéBÝÓb5“DĄę‹†ąphËx4ŧZĘ!‚eĪũZf™eÞ@ÐFškN.*$0B*P .šÝ9ĩĢcQŋ~,Ø8}æÔœŊŋūōMöŦĮhĐč-UŠTyäĻŅŋ,˜ŋoŨNÓĐļĀč"ģgĪoôũčÛŋ`€kÁOs%‹Đԁ՝‘ “&* þžĪīÍ/WĘaĐz0;[LĐ{b6Õ pL Ņ4ĩŒėAŅ–ĻÛô‹„“˜sė-CЖ?Č3š2- vŠÚŽŅŊQÅE·Ī`7„‘=“0ĶÕš&â^`Ų4 bu„óā―TŌØB?Ë,ģĖ“―J*)ëŅĩ6?ņg ÜđyĩĢëþšdYũî=Ū]― akÔĻĄp_ŠV­vLœÜ\ŧŊĪî“ÚUūÂsĩcbrrrð4ųeķžė<|ĮFM›ŧ\ŪėĖlĢ%ĩļÚ]ąPSwYÓ5ĒĒŠūð"*ž2BÛZubjGŨÉËÏU}‚üę5ĢęĮ6 \œ§a ŧ:ËāMáijà LÆŦĖĄI pļ1ä>ÂŽ,)™°šQĶ}ėūeqõˆ 1#ÎA SuĖ!w„ŊMĪ€c&&‰ąvUq0Ô6ŸĪ‰üš~rYE’Ęægę>(ËPéqod­e–Y^­„ LE}šĮYËuįģŊ•ãÎéÞó­Ę•+/]ēxzü4ä_‚ƒ‚§>Ūð|Åø3^Ūũ 49þėįS§^đœØĩ[O§|4“oÞĖČČ1xĢšt>rTŲ°°ôôgŦV,_ķt‰ĶĮjŨL-žüsSĶ}°Ž??uōsÏ=ĸéÔi‘U!é…óįūŸóÝŧ―{7nÜ735õɔI“ŽûKbžZkkvĐÖԚ‰ŋŠ"y―Ķ NŌ"憹ë'u ļĻxↆÕʝĀÓŪšBC\šJ@p(L6(–i_2#‹nWSũáÐ7Ã4)X.Č?šķŧPdÔØ‹ũ™e–YĻ…8šíïŅ:+JDMėBœŠUŦâÜíŲģëYÚ3|ȌŒg€ãÓĐS[·iŧzÕ Rôûõ˜–ötÎ7_Oœ<5$4dŅ…/V{)$$–UŽT bfeeōþĄÃĮ<ņü…ó'ĸ:æōÓ6˜:ķã?žðF›Ö“&Nðũs~1}frR!‚Ķ͚ïŲ―ûôЄ—+$8Īį[ï$^ž°eóæČŠ‘ĸþ{E^4æĀæn)šS,ĀžŠõÐá k t€ Ųô3eĩ6yՖïÉc€ÓáύģégũïZ_ŒēgæÐĪ-Ū— kņKĶ@'ÞÂxe0l6Ęō Ü…pļ „Ļ8Â*‘^$đb_–UîÁ2Ë,óĘÂ\ãڍhhĩPÐÏ? 00ˆŲŲ9"g%›įvþlŌĪé͛7ŋúrƌ/>?þĩfŊŒg郇3v\ëvíÞïßŊŨÛ=‡ÞĨ[ũÖmÛ }āáCđNŸeÓIŧã(ŋĀ ĻߊĨī~āŊ“ą~ūNL9ŊÓ4ŋŅæ „H#3í5ĨĖmÓė\đ8ŋLhēqQó•ü]-ŽpęŌųŸ•b•ƒ0M"ðmólZ>Gí/#Õ­ķMEýLžÐ,ģĖQ”­! œŒ'†CC!zäˆįþ0 ï{í[ŋūāįyLôõĖ/Û·i…WûóžßîÞõâ…s ‰xį“G{ŋóöÛ=šļØßĢ[§?ŋbÅԁýúŪ[―jÁüy#G [―jåĻÃ>7fãúĩœ'}aúņ―zv?qüčĒ…óĮŽu&!)q$‹ĪĻÁ”KHšŦŌóF* T(GÕ aŪ63ÓZ9ÁI1.ÕøĪ €ŋęrûf€Đó˜Þb&Þ"ģĐ)Žĩ,7Íx|t€J.Āb#H†™ ŨöpīĖ2+V+@ąc€@‰\·l\ĐĸE­―N;úũ‰ĒöâÎũ%ęïŸtŧfS™ĨO! Ā6o\LLK‡öÁ-ČÎ:s*áÂđsD'ŒÔc“_ŋ~•˜ųOØ­ä›I7ŪûĻ™Îc›Ö­õwų õŠÄýsũî}{vkŽÓNŠî™Ó§Ïœ9ÍūG+$īũ—QÝGŌčÝĻn7ÄÔÚ Đˆó•cwžäxĐŨÉ@­ęr˜.“)fÐ(„6_7gĖ1­œ€áä!xäĮφSSæŦ}…5jíqĘ6˜?ļ5!ưgę&0@,ÁÆX"ŠÞi–Y\<ó ŒÏEÓøĐ[FĒ{ ›fFígķŠ‡ÆœSŽ4oU[hÎ?gjĖfŨ?Ā0įA*C4CKš0 Hŧx1ĄļĘ?­§‚Ūĸ ö%˗K"%Võlȍe ģéjūœÔâW),ÖgsÍ7p8Ų­RŲYf'ššÃkJiq―õŌĖv–GWÖļœęÄ{ŒÚƒFQ4%V<å"Æó6uMDBI§vE'JŧšĀE.—]ÞņÜ$ÐæIųŊŽÂ°>Žņs™ŲîŌOÔ^ ˜ ķ" zē„{8öĻģÉ`PĶüēKį.e"ÚXaūÃGZۚ,+ ^ƒ1Šþ\†;œCģŊ|Ėý‰^—é)ĐJ‡Ė-T•*‘ŠÁģÖøŽšŨęđŠŧĘÖBéŌē4&ÔŠ4ŧF!­øēķšŪĶɂúķš/ĖK 5yĩĶ{9Ŋ þ Ũ―췘e–Y^-^§:bĶW8°ž\‘aĩŲUŒķ]û“&O™øÉäÖmÛSԀ –ũ(—óō*đÂiŧĶú)š{cŌQ= ,_qážiVÆŊ@}ōtƒ Âl~!hNŪ:âhįk|ŸtŠZŪLāeg.;KŠtmÚpATļīïŽpŅ&Ōãhvh嘿!h KxËĄĐŧ…­ 05vLJŅØ@ŠëWsS]ĶÍÐÆp§ņú™E#(\­ŽUŨœ+óÆt/Ë,ģP ƒā™Æœjč#zffF—î= úāþýǎ>lá‚ųĻÉtčŨwĀĀ蘚#ĮĢš€Þ^-[ĩþdōīa#GūT―FŪ;Ïå jÛķý‡OœĸÅëŊ·*QŠdDDÄ'S§ÕŽŽ>jôØ>|šžV.žOĸņÓŋŠëØ9(8”ĀÄfËČüþˆ‘â:uŒë4õģ/Þz띈ˆŠï>yęgíÚu€ząL™ēïūŨ{jüôĄÃFPɖ•“…6ÍĪ)ÓŠT‰čųÎŧo―ÛÛå ˆŠY{ėø§L‹G§LãbkZvĻ<ÚĪ7–äé­ Rč O@ ŊvÕåžF\šLš‚ ’ðYéķ˜ËéŊq-÷UjÃwļl4Øo”—‹„ïŠðc™e–y_aŪHķú؍2Ž&Ļz<Š=øÚŦŊááÎųvöōÅKB‹…āâáEŽþāC*Ļ þlÚÔšuëû`üĄƒ[ūþzïūýGzųRâĖofŦš­Ģ_ŋþģŋ™•t#)šNfŒŽš“SáđŠ?Í_ˆ ãącGįü8oÏÞ݃úũÁÄų,QĒäŧ―Þ-UŠÔƒ‡ĐY8hН;wēsrʇ‡ô~—Žo^ūriîÏ ^}íĩ˗.õîÝũí^―šuŽŦXĄ"ĩÍ[žZĢFß–,yک͗_Íš}ûVff3Œ7vݚUÄja+ #â%mÓ4ņJũåōÍîŸéÞ}%Ņ@9ËFχ,Y DpĐi2m5Þ"=,ĪYs6Ā(iRĪK+›vĻdb?_žl[Ą Þn%{Yf™wzĩA-Ōšäc„ ė„AņF˅—§ėöíÛĄÅB bšü\pƒy øûŽíM58“pę^ï’ûÉĪŅHDqąUŦ6î?čŨįÝáƒMšð‘۝Û ŅĄýûHŒÅû›ĸؐAƒęĮRåÛhÃúõ“'MüëČá6mÚFŨĐ ōĩAšŧ@õ―G2#>^KģÜúž;įŧowÝzõb6ģsøūCûķS?„„nĮļÎ@ŅHävlßnÖĖéq;Ėý~ÎĖéŸ?|ø°Sį.ĻŽ1ÆŪiĪ H.šöXäOutJ}­:›Ī·:’DŪ5v€įŦ=œļšÚRÎģSĮHÜW†Ø5îAļ“hÃ8eŠĮlęëHCgHm…,ģĖója+{JAÉZȧ ŨņðÁó8ø3­nLkqxēm˖+‰‰ŊÄ6`déŌĨ7mŲÎĒ)%0ĨÕō3でĨĨņïóÏWâ~\.FįææĘīšÝïČXNn6ļütâ„ÄÄDÜ[&Âá­V3ęáý”Œôt™Zg“ðÔČîU08 ?UŒ†@ŦčxqŦĒ@ŦÝMÃðėī;ųnðÕē4ĶdEΛ \†:•­š’QdjęÜy öĩš“[f™ŨJƒëžŋ]dXĩ[—ŸGsþýü·n،°VŋĢjGóhO'G4 qũ0SÖJ<ôüŲ3-[ū1|ÄČÄċdýå·[ČÕ aÃâč·Ä!â ũ‡4lÔxŨÎ?ĨíÜđ Ē…0ī\đr}z―-0-–Qz‹*đŊjĘJÂ,Ķíl1ĻwæŸ ûčqT‰ŒlÜĪ)B6{ũė!’ žĐfģ‹°ĀÁš4m6ðý!Gĸú+"2âÜŲsHÛ,Yš|ûÖ-„,ŠoSýFīqÝEŠŨ"í‚ÃČíQóUą4- 6•ūj-ģĖ cĩv)aø?Yę°HãENþ}âøðĄC؏Č9WŊ\ÉÍÉ―|éâÞ=ŧ‰BQ"đŸO™ēaýZ”ž;wífÓž…U+WœJH`ŧ ņį~äðAPĩnõĘCTŊYģZõęÐų“‰č§ðFë6ą ÞŋŸ"•ŊÚxU°ýîcEĻĘûũí=î,Oâ™Y™öĸÉ[Ū^ŧ:fÔH"l‚!Îņ…sgSŸ<á’Ū\đžyB_öÛb‚C‚;uéRąbĨûũï‘Ã{ŸeRR|$ģ4^‘‰Qę sa(xįÆĸĸ'‹*óAģxąĶ%;Ãt[ވ9q„ĩ{n‘'œmú;}#!híĘ`* īÎÍ2Ë,ó6Ŋ–ý-ēöĸ‰Ī qôðĄ„“'ÂÂˑðßÝہA‹]ļāįŸ‚ƒƒ\þŪ]ŧ:|čāōå+ā–Þū•äp O%üúčŅÃŽĖ,fƒz {ŧgũ žüøQ`pÐÖM›öîÞž•‘éŅ€wK˜šúhĖČø€Ū”Āšw턷ōßÝŧÝ:wbŨŠUïßw:áï’ĨJ ÎLÏ`îÔЄŪqƒƒ(ŧ€ĪðzÖĖ‹-䍌þ ģK\{’LĻ”ČÕZ),ũ=I!PŋÕW? Đ­d[SyėģāREīōXŧ*rkT$ž`ÓžŪBõ”9ÉMðŨ:+ĩIķÍēbĩ–YæĨĐũ§<ôĻ™,Žƒ‚ƒAį―ŧw9 C.ōfUAžP6ÐüĨdö~Ę=þ páÆA/âķđ99 †Â ķíėy–ö”<:œģ:=%|LũsŧFðØ5p Ĩųy8ŦRŋĄ…FšŨÔ­aĻķ)c°1“ ĸ-jâF#BCĶx—û*P§ØßéÓ1ūÃĢĖÕt[Ë,ģĖ Qk—_0éĨ pā…i1h‚+į„CBp)’1í}ˏMhC#DŊōT•_œ0KPĨb0œĒ !N Ä6CÔē4mkV >ĢŅčRņ\)-ø_Ŋļi_ņbÅĐ. Ŋð<„ -Q/ķAÍčÚšoVTáųŠÍ_kYŪ\8ÃÏ=Žïs*ž\ŋūŋ++ĄoýčóHč4$$īA“Æ5Ēj1UÃWę7ˆĻ™™“ųY―AĢf\|ÉRĨjŨ‰y%ķaĨ*‘xŽ~þNĪp4jŌīE‹ZŅŅP’ŊroŦUŊÁ␐ņR §ŲÍĢĢQӁ\ Đš9đ9Ä6HJŦXĐēĐS(VēøËõcIo(YšLݗëAðܞ܈ŠUëՏ…þÆŧ%ÎĀõð@аĐeĨs‡ÓKU―,ģĖB­ölĕ/ĶāĘyP6Ųƒ˜›6n=|HßũzQý5hðāĀā’%JÍ_ļhųĘU‹—.›ĸëĒ2eËTŠ1ûŧ9_˘đz݆Åŋ-ýyáÂ2eË/Y€k·wëņÖÖ;.þíũ?vĢō•“?ý˟~þĨlX:-M˜ôËĒÅĶ@–ð&ÕcÆ}ļqËķ…‹–lÞšĄd –-_ý8íilÆ˖ŊĒnĒJ•ČMÛķ/XīhÕÚ 3gÍĶX":&fþ/ ‡ĩįÏ#GYðëĒø/f„„ úzö·SĶ}þíœâg|YŠdé.=zþķ|ÅG'ņÍŅĩ[ĨËWTŪT…K%ũį…‹xãŽ]{ÞhÓ6;#ģ~ÃFæJ6lÞúŅÄO‰cԉĐûÍėïÆŽŋiëö &ģæ^ÖnØÄ˜åŦŨÕĻ…‹—ïŅÜBĘâČãžSy\Hž$ÏE-ģĖB-žæ!]•ėԃ€_ŌŽīŊ„W!EÚÓÔ+—/ōč1cĐûëðĄęÕŦŋøŌKŊkņęŦWĸ―ō,#]ũĻl-ßh=aŌ'ŋoßöŅøqĄÅŠÅ9óüųs$ęķiÛ>,<ŽYóČ#œ;{fÂĪOkÕŪ-Ýv—/íŨĀ›ãøķ ölĖļ˜mŨΝdãRīVĐrĨJõę―r:áŸË—/ՌŠB6!66–0ĮŒGīāęÕđߚ5Ģč;đeãĶ^|Đ[·îxПĮOŊXđōļ1ĢũíÝ;jô˜o―oŲ§Oĸ{ĸÝå~ߌ‹ČCĢ֝—+Sååņ”]ždÉÛ·OŸ>õ“IøÎ›6ÛēiûKovčØóí^Ļ&Ū\ą"íÉ€Íü,T­z5‚§Ôå2ĸŋ—/Ÿ>u ÆŠ‘‘mÛĩ;~먃””˜šuĄ^\\Ü+ŊÔį(Õ%2+ę ÛķõčŌiåōĨŋ-YLp ~ƒ†ïõîK4vÝš5Ö­ã |ŊQģĒbÜxģW_ŦU+ęØŅĢÄRyÔüøņīɓü4ïîÝ;ļŦÄO0>ƒŽqqÕŠ―ćð\đpÂŌ9rļc‡vŽŌĪYs­S§N‡ļÚČŠ‘v‡hJ€YälØšģ“§€UŅgŊ­e–Y…đŠTB–JöņE[_ĶðTâķŋ—ˆŠU  ėÂJ΃E"ķŦWŪÄÝĢ,UŧŨHkE”úhĢ-æ‘ŲT6ĀTŧĒÚ%éýŪ;woĢW[?ķA›ķmü~ʃ*U*ËIO3'EŽûĮï;ÂÜkV­zō7{âä‰W_kÉ{·mÞÜMœļNéééI7ŪãŅk5šI~îâáƒd;JĀzý­ä›åŸŦðčáCʋM….Ŧdd<3Ž—–Ī+QĒxŸþ mïÜąM“Üøâؒ―v­‚šeVAŧ%JY4į.Äčæ"u_Á,lā Ál‚EGŨYļpÁgS'óˆ―`þž›IIģįĖŲžcĮæm;Úķ}ӝ“ƒGŠéú­[Ų/ĒušU+ĩNŨ†4Á‘þé§æÍ[ėÚ·Ԙą‡Þķy#ĮÔë„kîۛ‘‘ᜒšjųē―{ũŌÝķ㏍ۄ•ÄLϝ={ęÔ?€ėäņã—.^|šš:ũŧoŅH\ķr5ÛSë6nĐQģĶøŒŌ ÁÁŋYŲY{wïÆ…û{vï ~rĸāŸēÎí[·n&']LLÄCONū ~ōãPÁhH#tnÞļņýœoä]ąjÍæmŋŊZģŽĪĶāfĨŽĖí^·fMBBÂĮ&nÜšuӖm=ßz§r•ˆ ' >œė…"-“"9 ęōk™eÞh–ˆĒ°‰§}Đ °3V{wû +15_ĮĶÜŋ‡X8Š_DN;Į―ųękŊãå%ü}âŸŋOÐ$bp)ņžđßWŊupĸ>dĀBKoŲŽifFšŸËFüg{výAŽTrRŌ‘ƒk@p›IóÜũîÝã%_?_ ĖQD‘šúxøā-Zžųâ‹wnß:zäÝ€į‡cĮÎ*=ý^JŠé(ģoϞ6ŊŋJg3ģN;zîÜ ÛēEū ŠųåĐéÂ.\Ā'>+­gė~ŨŪ=vôHZj5 Ŧ—/Û·{^-8vųŧ† `JŠyĩgŨN$Ĩ‡†ŽZš,áïŋÉãÆ‰f$ßL‚Î-š6ßAÁ!ėwĨĪÜëûÞ;›4‹ˆŒžvõߓĮĨ>I5b8^0Ž1—! ķZ‚ÁĒÜ2Ë,óū. €GKēë}Ä$VÛg…ûR’ÂYú2ÕY2ā@‘pýš5 Vâå1Üs:‘:<~ô(!ęPsēē/]N”đ"ėíųûä €ĻqOĄy߃Úīn]­ZõoŋųšX­ Y[íaŪĒBą[ķl€N6L[Ïšüý‘ {p?…Į{R_eéüžëŨŊ'ÝļĀða%žë(LžČä͟ÂĮ“&7kÞÏwíę•;pŋõYÆģ‡grI?}šF`—ÐA@`%7o$ņ..Ú^ŋv.†ótKŧréĄĢųÍŌ‰/ē 30Ä]Ëemßš™ePč(įÖĄ–Ņœe.bý n™e–y_‚éķÍd1ĮŠVåĢĨY6ÕöÖÎ-v_—nIĶ,į!.ÃÁþ iüĮŧ|å2įÁŠĀf3ŋSHkžŧ5ŋŽf”ĀNþd"MŒf#“H–ũ".Ō^ęębRkWc fƒ_Ã_`Į1#ļ ŧJÝØ4A˜WéęøôéÓŅ#†­^ąÂnÎē.kÃ|øqŲ3đ’ÔUĖŽsÆĩũŠé1d:įRğqï^kW @ĸøßĸB“īį™ E–’ÍîęnĀëĩþÝn?Ý1Q§€ pܙcĶo•WÕĖ0”™įI0"óšÞ6ö~QŠëZũ›Ũ؁øw_í9­Čx>˜ŨûĐýÚÕM‘&ĐęŠÐŸ2°ccŊ]]U•ŠčjŌžÏāĩķížzŌŨ ŒįƒSķ =å^3BÕþD>|ĶZ{årûĨé&ĩŨŽPyŠ;(‰Ý #°kjŊõšĢ€'ލï”Ā“ęésAR5c[3Su@Žý:ՙņ{—ĖSƒbÄĘq‹sf ؎LŒIH č?kp#âýþۚĨy―ŊqŦS)eĪČr7Áˆī!JŌ^Ëöó―l?e„ÆļŪKŠ―wψˆĩvu[Ėĩ(uOÄ―Qįx°rl ‰ŨÞíߎ•$ž`Ïô >|øðï@°gĶ„—B336áĨžÎôLP™ųŒ~`€ėnBz\įhEŪ]u0XkQčšŪÉ;Í<}ĶŌ3Ý+Ö3uîĖHu ČĖč7H Žói ĪSĮãĖÆé™ĐQu23"<ŪŠH†~ËWŌʀâœË5ŋƒ]ûžOیŒéEUˆ13VˆŌĢÅČĸû䂚áû… V=Š€Tį„BOšB"§ šy]—Įk/’ū·ˆ>üļf/cl…ĶÛĀãŊęŌÞŦ§<}?M`jB9žņÄĘPØîąBšoĨ†T_ ûTį^đōŠ v†Ī8UÝĩ֚iŸ0tÆĀ3{†{ÚĻ.€žüĖķ0fæölHŋWæoāےĄX{čjQĄĀĖ“–D„mØÓ "(O‘]MŌFFœ*Ðt{Ö"Ü€™~â㕆įŧ(@fļŪ Fďė@øðás0·šV.‚cȈéŪëØ~­]Ýu:$ð^EZ+O]ÝóϰŧŦîČū‰ J6 fŪũLD„tŪ#Ũ"xŪ ĀĘMŌ7ß1+7ðĖ•]=ãžGÓ§‚vï-҆Ēš[Ôoș! ØÝmxŊuÛ œsŲ^kïýš™Ū’äņĐ#RŌVāĖĻ.Ī2îÛū7@Á°įšŪ§įwf`XkũLUŊĩ`œ*Ñiû3Õ~øð#ģÚXÕ]S ffwßv[’NŨÔė―#Ēęx&#æOū ĢęĀQĄš!CŌĖ|Ŧ§ŦęHا€‘RũĘé~―g+ŧk0Z åĐS50Ÿŧē@äJŨõ†ųÚŊsÝ6œĘPU=J É Į4šęT}ĸŅ7­ÉïÚoáņœšf&˜Ū.’‘ B!‚Rtĩ)žN|Å"0öĐ9œöz­H{l(BÁũû‹Ō#ßvxí_uĘãÏTûųðÉjŲhÄļ΅!čXAģ{"eāëýÁķĮbŪžÞD‰ë\o›’bEOƒ č6Ɍtî|6’ÞŨąky0€Ļ˘%%ģŦ0ØđsEÄZAčq.ƒ§īSqÝÁîÎeļg@ĪXŨÂîXąÞį #‚"ŠJRF(㜋|ōCŠÄÞë\‡ž§Įvf–kÆ ­øîBĢ-eÕéš·! ö9 VdO1@âǟĮGĩÄôH~åëïë‹DŪ°<5ŸqõéļԊ:Áˆ`Æõu™ū“œsęôũ??Á6ũë5=SĢÔRž>cŽ•3ãAđ’ĄPWSĖĖS€wjqßąāĖMÂîĩTՙ:ūeÝÓSÎ@ïëĖôüÚÕļĸmŧsŒ §Ēę %CŨûOŽŠ.ZĘpÎ jeūïī„PDâǟČįī ­ũđ„B Ã)\įPZkŧėééïtŠ ÜąfŨÞŊČĻŪ[ÜęÐCŲ]ķaUuQž/hƒĒ2ģ§Š[ÄÎuęT·D’ƒ!ĩý2Ð]GDĮĘŠëÏšŨđ(ĶRāĐóœ,˜ ä^3 ’!ïšxōY ŋđ­m’”`ÃΕÆĢÎ;{­é: ė―Ï9=–ĪŒŪ ŒˆļÎĐnÓ3mB nÛw&ƒÁ‡?‘OVŧ–ģėŠîņ0 ÛđÆXŠ\qęĖxŊĨÔé‹ÔÚ[ÁóūFFMÁÄ 2ë o—Đ10DEÆī3ēÎy^äĘũõ5í (^uD:E2_ËīDcvîۚ1L)Ú-ĮoĪs›83oéMsįܗfÄmöjiÍļ§ðû(ÚôĘėęšĘŒTœëð>LpæZũïh‘°Ŋë=Ó{%ɚû˜ōú%°kîëcŠ įJ=E>ü@>SmŧŦ ŠiŸ)ÛĒšŦÞėPœŊŊŪ†ķ{jÚ=Ó§afš#‚Ķ= !žšúŧ`UõĐÓ}]§§VFđ +§Ú6%=[&ėŠ+’™kzÜ 0cõŒB{­PT>ĩũę:W‚{­šHA.Ý2"Ū>ŨuØđ1xí‘§ĘÚâ3)ãþTŋÏĄÏ―ąsŨ9ÅĢ{š/ƒé)ŠTô”Éû zšŧ >|øi|nöšk‡a)Æ3ŒH-##3rEUëŽ\+“ķ‚ }ú@ŠL“€Į&YSӝ™Ņ=Jf&! 62–iũP‘‘Ý53ĄĖŒs>đ~ތØ3ÝՆénÝCbÐ4ïÕ`(ŋÖîéŠötÆ:3c„’`Uæo „(cŒy&âŠ@j­}NŲEĒΈ˜žžÁŒLÁxūāu―Į“ą`ŧ‡ …ėéÓBáAK™†ņáǟ „"ssHŠaŧš”ąrŧú€ųū.ØOŠÚžĪ€ŅÓDDuU]ķũÚ3ãÓb:uUUD†â}Þu:3ÉoßdčœŦŠũĘýÚķAfĘÆķ‚Šž2,iĶđrU@Ĩ \ïũôÕ}_ąöŪ)īį^|ø‘S-Ĩ™‰Ėé>Ũą_ŧ{HDæZŦšj*2"8öØŧb˜nOߒŲÕRëÏ-Ĩ zšŠöÞcOOˆĪzšũ eīŸáQ’Ķ&eą2ŠJðZ;Ÿũgö^ũæ==:=ĢŨđ$ŽG”ŧ=“+[ОŒīŌÎĩö~ōYĨ"ÖUÕ5ĘÄLO…""§Û0­Ėõ}Ä#2Õ5ã•+(đR) įš ïŨŦg"•‘zš!ÛøðáÃOėŦÅ|ø™Í^A|lLōqkdˆŽSOîЈį˜FLUÍØ+OŒIūöŦšIØōœ ãÜŧК•+Ȉûb’ņ Ĩ”’|ŋߑŌÜå=xžëęi1<0 €Īís]RäZŧ!DDÏwíËãVJ‘1vWIz­ÝÝ#7{ŠwŪ䒐™Iōôa@Œƒčî˜.‘‚Yu)#!۞!%ĒŦ \Ŋė)ī Ԍ"hМüĸāðáÇĸðÆöj ^ŨõþųwwŊ•WÁ–b:{ŠŧoSEww5%…þœgÍŪ:՘ačïŊ/AāüóؖÔÓïs<ÓåĐ+"CųžŪ*Qk­ŊũÄŲ3”úŠýÚ_ó5ÝaāÏR#’āp’qČžéŠ―öŨ{šāĘøëŊŋž| 2ž`!"üåîÆý{ðũųŨ― 9ÝÕ-).=swfÎíôņÄ―iũ„Đëý~zyæï限@JŠjk­žĐsĻXkýë_ČĖ\}0Þđ þã_ïį ĸķĸųÏŋþý(ÉîÆ‡þ{‘ĸöÉŊ_ŋþkT{ŠþĮĸü_į\ŊŨŸIQį\$…ĒĶ`(FՉ‚€Į3öĘÕÓ6DDŽđ5úˆ=MÄŒŋó„Ėî!l;Ũî>üvEPð}ąâi$€@pš)yühTĒ Ûøž§3üėÝ{pUEžð_Ÿsî+7ÉMHHx%A ‰PdģrEeœYĩ%%3AÃD&š€( B”Pwp(u@FGgYGy y 0ˆaț<ČûqįŅÝŋMÎõjÉ!dw­―KúS•ŠpnŸNũ?ßj~}ÏiYîÎ>{ũÛŽY∜3 Rf’lۚŅ?ãÝ)Ié—dÎYð01Ī”rä]žâtaÉŽKÁˆÁĻL$D –8‚7RNcð!\n–ž ˜=#ũuvŽ›jôƜŽŽ:ūĪĐĐA%Œą1cFõuaKš››á*ˆčrđĖMs$Á+ÁÖðčņ:\ÕĀâwKØÛ_éKÏÖ―Y\'}đúÚģՍ„šĄŠ+ķ݇ę*2"‚RJ™a}Z!ô`§Ž }ÞDiA~dĸũQ+‚ ĒVAQ+‚ ĒVAP@!Đ~Óš?}Ģ€ B(áˆÁģ!I؂ŊÔ""jûA|Įo€ĄR†Í~^ßÉÚœsAć+ņ’ÛNšˆĻ―ņ BðeLUUU---šŪ‡þhÝnũ Aƒâãã !V9{撷ĪÎŨĄ!B(ēI8ÄcKMô ą›i+ĒöF'œóōōrY–'NœčņxBīĩĩĩ%%%ē,wĨíÕŦVŋĨęũÕm‘îۆG9íö; ąĩÓ_^Ûü·Ęv—=&)šØd‚7öƒđ‚ hšvęÔĐôôt‡ÃĸO”™&L˜@)ý/[aÕ­ėÐŎ–N-=uØčĄnëz(!ņŠß-ϚBø_ÐĄō}gÎV4MN8~˜#ĖN8ÞÐŦZA(ĨŒąëÉYÔ:+kęuJܑ1ąąQ…€%Ôý€ŠrÉ!CŊð‘bï1ã=pbbbNŸ>Ýãž †~ŧÂ\Ü6K H5ÕlfþÃðûĻ;ÜŨ€Þߜ<öî!áüI„ ˆtSlQ dØ?ķÅA@Äko4BŠþšú…域maoۃ…›ĸ5+c4 öܚ·mZąlũđ·ÍÛO—,šwsŽûšýÓÏ_~ū8sˎDŲŽ―ūZ›áh9%BHÉÞũŨïõ<;Čôō,Î=œûÖōá.@Ŧ?hÔŊ]üėė­I Gžōðķ6éķČh·ŪāÜĒŠqÃG­ ˆĻĩ~…ā™ý‡Ï9OÜ/%DRóíÏG$‚œq„n’Ž0ĸÁcû<ũ-]˜Ķ͟ĸëč”q+~1ŅlÉ9ãĪn€Ýˆ$3g’”$ƒÉėéš9ÛûÛî‘3BEn­Đû\uģ;öÞŨ7,ŋ=!üōÅĢ…Ųmė6*mýoŨKļlcmį^]ūÖ9}ŅâG:ģŠZÎyŊÏpä”R„Č "YF)c c KŠb“Oní“2Ķ֞8xøōƒ‹ō—<1]áÍۊō‹>ývÜÃķú§IJG顂…G.úâRĶ­^›oŋðŲ{[ķ7Ļ9ģũéuŦŌw―ķþO‡M/ĖÏūu€ƒsė1j)ãØWĩ‚ p“Õ‡^ŊŨ3$.9ÁCUUgȑHŠZū8k)Üŧ`ۚĐoæÎË\ý—·šes"Ëû·ĸ~ÁÖó•ŽĐ…ãĩķŌĖE7'ōžĮf}ôosF΍Yą óâČđïž>GVá\íŽ;^UôŦWũ·dünÕH]5ðŋ[=BŽÜb‹#1ĢŽ™­Aj­8ēņƒō§^~síäCÏį=|įĐG^ycį…5ïžo?ĸáą}ß0Æ ÉņOÏž™;˜Í{xÖĮû\œ~ũ”;RxÆã9OL>š9?o‡ĸÚķsî#ˊRþðō?KœÃÕ >\ý%jA&—ŪëšĶAO$™ãÓUU— Y1ķŪYisî))ũ Z9į§ņņ>ųĀ—O_h™wŧ‡0ÂŪsÆÃ#boŸšQšgßGŸnZÜWŸm9ų]CuMį}ZccååC߆eŋ:ol‚ŧSC(Ûs3#’øpëģ‰ĶjüĶ”öZ 5ĻĄi-ĶĪéÔF8§šĶ1ĩë‡QUW~6ņąœ•MF:tĖo>=uņb랓|ėüŧ’‡*É?ŋ덝†#\ßþfÍ{U›‘ęŨėŅĢÂm‰IIC];ū8YSãÍ]° öoÕ°2ŊĶ99ÞVĩš†ôĘↈZAŦZŨTSýéáģKfŽu:IÍŅŊ―žIÝcũw64ķÉr|S}ÏĶØ 2Žœð.ŒēīéÓģþåŪÚ/>yïÏ{•ĸýÐåØümË+GB{˜SöVÖÖŅÔ[™áïnm8§ĖœÓúÍķßtϋófÉ<Þ Vw#=0Ļ-nHlģïčåvýց1—JNŒ!‘vŽBpø}^ _{‡fĪļlšÄlz‡Æą―ĒŽŅŊ:ęÖe9ÛøÄÍK§oČ*QPnø4#œ2Bd—[I5qIÎ\OļÃéō(Ô°Ļ€įØúHAn\ˆ<.eúoģî|7ïŲŅë6nØôÕđ:bģG MņžÍE+‹ÖŊĘ}ŦxÄsnА9Ã5]ÝóÁÆeŲ ĸ\Ņ9óÞīpÐÛĘŋÛĸŧåßŨœØĻYđ5ë—Đž–ŋzݚ76ý―AĢ~5ę'™Ŋį-8öÖS+ÞýĢNl §?úî ôë•/åmÞX”ŋáŦ·'Æ*’-L:ķåíĩŦ‹ _xū1qüĖq#§ĖšÄvŊqÕ+Ï―tžŽ@ ģ7T~ĸåį_–{[‹ė*mƒ„a1o}įÃÏNßŋčĐáރŋûxρŋėÜąĸ %z'Vĩ‚ПX@Ųõā3›†ŲuølYC3ųYvÁÝSGkčÎÎ[—ē{Oe}ĮãK_ŧĸÞq22$ŦÄóÜōŨË*ętŠ+Ũ=9ib*ķ&bôþî\ķýý3'j(%ģžy32yWim‹cØðØŨīų…iąžáÉŲoŋ—öm­ĘšĮCŪwĻ=īËfœöØ1ŊlüdŨîƒõMí“2_z(cē‚œŠ|ÄŽô›b”ķ?+Z6+Éar6D9xæōČĨ+õy‡F…EĸšėÞës^UôÎwßWzdNNsŨŨ,ā‹―cö†‰;ũŅđ2,1^6g`12ļjn"jA)ˆœr)mÆėq3dY6tÕ ļbfĸ|.š 4Mㄜ“ïŧŠ,™đF5UÃč›Í)™'ÅþdčŠÎPšņðã@PÕ@܄ ‰iŠ_ŋiüôwĒPą/CĩHd‹<#€œ HL›—5Ž#(5ݰŅ- ĸdÖ#„sÚ=A†`›þXÚ?"2NdÉP<"ų‰ų)ˆĀ9w§dhîIÉüåÎ M Ä%ĸÕĻ „0ÝŊV_ö Ķ­Iœ˜+ýƒŠŠĮŸ2eŠÍfcŒ!âõäąŲėʋˆx]iŽ$ØšđŸ|;Ŋ RŸÏ=įw–ûŧįHrŧ݋‹‹ĸ>ūüōKÚc:‘~D&Ŋūú C)øxĶÎīÕūÂSŌ‡fþ_Ÿ'Ý=öĘۜô&ŊRZŦ•šŌüN{<-}Ð~fõŸVøøÉóÏ?ĸâ‹/>ũÜs_/ãņãĮä$@ŋ%ôÍ7ߐ= €ĖÅ‚ö ,Õčâ8 ÂLĶņčŅ#ëĻ”aķąøSŅĢ?„>€Ģ>–ē6–ūŦŨ‡vŌē Į^tÚčƒßAŋ‚™ĸúØG‚}MýøÃéxÆõŸÖ—^zé…^HÓuýÁƒðÓŧīī'ãņøĀRð—xH O3‰ --z 6ÅtK5ÂÁfáiSG5―ˆ ŽúXĒ6úÐőđRČ2Ī―>ēe Bx–õĸjôąŸ*u}Ė)–aÉJqZÚXýþ žLaõú ‘Š&ÏŽ>ˆUęCšŪ§-,,ĖÍÍÍąxÜÐu‘ãŊ‡g5ÅbĀ„œx‰Į0 Q:§Ķi$jvtå6ĀøBĢĻ ŒDĒp"Ísk⁎čĮŲ,|K s_‚e$Ļ­æĒLzH$`ôYX\zļô`‘Īš_|ÏA}–ëĖĀĸ?ęƒYŒĸW}Åþ%‡KņØ­VKþ+ŧHN b,›š>ôš!bÐ!ģ0qðca"ļ–&ĸÏ1!BH+|qČ2,Ú˃v|īŒĨš?õA'ųĐëƒÎÔõA'ÔZZô™{}Rŋ_pŪF8Ÿ@āÐú`q‹>ˆ4= Č$ų„ōxÚŽĸõÆÃŊþüčëŋj*+$4Įõôôīķķ655ĩīī|ôŅGn·›”œÃëŸ=Ņ|8kĸ›oWŋ–Q·ņāĐJĮÄp"27Ŋ/ņ>" =MÓčt>Ēa)Ž XēEÂ2˜}ČBĀĘėHÖÝÜŨNŸYq9î;§<Š>—tœ{rØ1-ƒeŌ i?2îŠĻ:=6DaIh}Ā/ąÞI—_KVĢ5_ޜ…˜.ßëíėčžzŦŸ•Ø|‚ãÁ_dU‰ð#ƒƒũî}60<&EÁoýâP ēĖú`S‹>–ą“}õāŒÓ ÃâÓûƒ|, †"ņΑĄþþ{}ƒ žš HxL―}íōĩÁÍ vƒZTrU“·@ũOŽŧ|žNū#ÅīßúF€ũ™Đčƒ2æÚčCŊÚī>Đß/œ“æÐúāi!✨•ô#E}0—š<>iŠĒD“PUUI"ār}Õõ›uRÓЇÓĢZgÛÝ­›Žo|}ĒŋēdY&|Ļg0lkkëîîęrwûöíæææééi(Ej-a+ŠË3ģŧ>§Ēië` “yĮøÞWē2ũū9ėŌT 'šĀiöĻËĀGtœipĒaq"°Úh ŸŽD`ŅĮ\Į\ŲRßœvú(\ÃïŨmČŠv̰ j`QÆo3ęf•yCSðÉĄŒŠÏ‡'>ÉzũČ#ÁŨ„âЀ„āāŸđũųĪŽ€W?€47b ã]M9u­ŒĪāQPÍÐDÏņ}Eéé;ęÏ4îJOÏ*?80™ÓdGßļOœ3429ŒtÜ)ßē!·ŽzOUYõŅSnV1t ûEáíĒōīcðþļG7ôeņß߇$ÆFĢIýuc^mŊÎŦþðþ)Ū'A’NŠéŧÃ5 ŽÜĖܔžWūgOeiÝévŸ úō‘á ! aëņ˜ÜPđskŊŽ@ ã‘Iŋ+˜x!Ęų&û‡'Dň/Čgũ–üáōPŦÂĄ$°žKÉïĩŌþŊOŊIïTĀÕĩŋ_–{Ar +Óũ wa™ū2XŸū_ĨîŨFðĪE"I’ä$ĀŨ''.žüģŋŠýwĮ…^―üŊ΋_ŸŽŋĩæÕķŌRį#I!nÝšÕÞÞÎ0 þę‹ĒĸÉíęęË&[DÏ]nÜ^ŋæŪôũE_ðoýė}# m…-Ÿ­>ē yčA?9ĄZd`;bc LE$Āēæⷈ€-ú` Ö5†” @“Íú`/l‡ŽcĢ,2§wŪųéËéíwĶī……ĢgóÚWÞÜvĀ#ëaÖïtŒŽ:Æýސxĩ073!ƒgƒ!6äŸsNøBžfĻCįOo);106Åpb˜ Œ9 +Ā °.áPĀ Ir4Ė…–ųfɂd<V7”Ą?ŸYĸË-7&…ÅÅE-pŋ`íúŠú>äŠÛ‘YÓþYŌ‰ÚŠę­Ė/ ęą9CՐ'āóē;3éœtyÑĻĖ{ÎÖūSXsÜéaā{H|Ā9:<>=+@ŦˆÄ2– ÍLONûB\0ÝÎąąÉNn„ øƒÁ k|ŌāyŋĮĮ†Z‰œkÂ9::ęe€%Ŧ҈gj|Ä1Î,Edy0ĸ@wîî#Ìū0gĀ]füž'‚ÏĮžŽ †|nÐŌ9åæEy~>rzoq}Û YUü^? ŪhTųƒ!>ô:ԉ ÖTąįŌ‰‚ƒãþ°a<ģA–‹DYf&ĮGF>F„ _ Äóė„sl ū‘ąßú:9Đïú ŸŽšÛ°Þ/KYz‡ŅCß/:7…ûøŅč‡Ó`DøCÎ$Đ#ë]ŨkŊ<ÚđõÛĘĒėŦüķšäqnFtÃkíëŨú].Q’ŸĪtvvöõõy<†a8Ž …B^ŊwddäŌĨK~ŋŸ”!‘ũ~_ÓŧåJhŊÔp[9{CnėdßŦÚYÔļÅëó+C NEyžGNK31d~„7Į1č"Â2cÎ2§˜›N…ß+‚DéjæÁ°,9iÃFõ)|kíš Å ’&_oy[úÛģũÍĘŅŧ JƊr2ŠëšƒĒý$ŊüØ4q^ŋPZ\\ĩŧ8cûæâÚæ€ß}nÎ/Öm-Ŧ:ðГék? Y…‰ŽsLDqt7—;†ģ§ĩīĪ„d՞óó •ĪˆŽIÞæc9'ŊɊ EQŊޚ_{ģŧuÓúoïČmlŋŇ"Āú{ŠōŠn8ƒĄ@€ ņë>[™YX―·0?sË͌ ―SÂLïķŨŨl|csU}ŧÏ3ÕrĪ&ŋĻ`WÎŪó=C҈ÐqŠŠ xw~aé™OGŪÔïݕS‘“óÖ[;öļ&éáķ#ĩđY™9;ĘšnŽ}ü~å{W†YļvîøöĖÂōĒü–ÞAXýÏŪ5æäįV>ï‰IDĒQÏ―îÜēC7'‚L H|ĮŅüšÆŦQUáž#5ųŨ‡Į>Ž?PZZôÎķĖc—û5]:YWräâ %â=TđįâõŅĻĄÝýSmų‰ËĒînn(-,ĘÎĖØÝÐåõOĘÞžnÓķ‚ŠFW˜k9XvĒýN8Ė}üĮšßeï*ĖÍ+ĐûÜũAP!3|Ī”c–+-ĮrtMĶeÆÃ^‚hĪ„”2kLÉdzøšŌSŅÄAáäáĢÆĶ0'ČghĘëÞ{ÞgÏŲĩŨYw•Ë5ÉŋþŧũũýûÛĸųũ>W\Þđ>ŽŌg’$)ÄTÕävEY-Y6rĮÎŽˆáŋ~ē  UUĨ†aõz―îgƒ8 ž,Ë?§ôā‘^0ĻŋބtF™Õ+ðGgBAĄF #J‚(I@‘ú ­*Œ1x2 ^E’–ˆ!ÁƒĪÖh:E)ÔĻT‚§íf‰Qm0EņÔ(…HĨ‰fí.|ëĢÉ@ÚČp‡úĀ6‡Ÿ6ÅõÏ3ím_ŸĸîĘģgZ°,ĘũŒ›š_°2#éąÎ/ëūïäÄ)―–0Žy―Cũl֚ėũ· WԀļccĮN^”•>j 1âÁ)/­Z™‘8ąó|ýå.AeR‘ŽĘĀĸ\öj`]lžÔåQA hS°ŠŒŋ Ņó}:Ņcpū—3G?46:b|Ō’yOÞŊĘ"ŅĖč LWûgŧ‹‹Þý úĖh›ŪÎ.Éõęå­|RÚŌiŠ;Yq[dŨMė?dQãđÎ6bÁĘÂ5iÛ}sÝ-ÉūxÝŌņq=].NfXÆõEÓW†!–åŊšú@Œpû_ĮŽœŒyü™ÜôxwkmKëc L”TN―zîāŪâĒũķŸi퉛8Ūí|m{·rõËŠ!fhtøÃOÏ}iuÁs#ú_øķ—†eYhNXRvc$Š*V8†AÄÃ'LÏ+(ȜýÄ­ķooãþMŸ|ĸ˜I‹—Í 3â^Ų[Č­§6õ&.Zū~ÃKCPہēÓĀ—~üÎþāšõkÆ ā— ’ĒŌþ€ķߝû‡xmįÓķ!þϗĪ1š<ÓütiÚĨڏÚâsjĻNXQ  i‰§īĘi@$Ņ…þãúøTöÝŨ`w_V "Ã͈!  „b† aÃŅ―ą(:þ‹·xŸ€ę$åĢtŸúøÆš^$xES­<ˆIrŸ~ĒëŌ'ęŦPÓĘŋ^I– ;24.ŌÖ]VęčÅĄũFõc ų$Ūõ\UŅĶ·ö|~Zô302@Uļ€ yäŅ·Áyöģčž—IoÂ"d–ļ įNn~Ó˒ŒFTŒ Œũ(óĀzÄ˒“E§Š<Ī"Žtvó*hô Æ=7@~&ŌÆ2įæÉFDAxN24Ņž“—›5iĖ OŊ[g ņČCþXbÍ―N™ŒÕ€°ÂyÜYōp%{·Ÿ<Ý9˜Ux‰ĩčb‘].ÎĢ0‘ąąþHíŠnt{xĖDnadž0bAĒ›·Îœ73ärUᆭ'Î}ßÝu‹sđūq–nß[К#šŸ:! #”Ļ=ŨÔČĒ4›ïųú/ęĢ]‹Ūĸy}čĩĀŌwY ~,q\ čĮ…FCcҰ‘pÕʑ1—,6ÃĻ8―ÉDhô]_iĄcL&“EcžN§ĢŊ’9*8VüQõ“ý‚ýú››Bތ™ï"lũęIH•@@đ$ žč8}óøh#\ŠĐ 6'ÅÃ,ņ49Ņ@ëCŸ“‹V–|Ô*§–åÓú MNdÐ$„rįúíôP„­Đņž_ä˜`―ˆYî―\Ví|äų”īg&ę‘Ę0ŠĒē:Ø*ƒtŠāöxDIF0`Tîæ-·„…ÎöãŽúGÓԟāaĀŌŦBË%ˆ$€đ>mЄޑQũ]h9õÕĨ[HĮš:ÚęÏGņŨ)ĒĀõÜļ-bLęCķˆXVÆÞŦŪ]QVtˆņjáŊ,†"•nARü –P[D|ڋ…ŊĶÆĸ^/q Eðš“dÚėĮkŨ{8Ïå {Ýýî ĩ@Ėŧ8^„ÂxEC]Ä^5hØōõk'…KGĘ[ –␰QÍ\ąjõŦŊdŒŽēI"ųž%z…éYI‘DĖĢbÃØ #KöžåÂ~7ā›úēšģ|rÎ OŒŒNe°ŠĶ õë―~ĢģûZ{˅Ŧ{Ū}}žžuÂŽŽ”§G"'1*ÉÅyß2PxĖē˜AFK`0ūÞqŧKōÜ:ĸÝ5[LĪË Uįvqœ ˆa(ļĶĀß}ĸP˜–rįóEķ=ÅÓóEĖ'mZ*•hð9ÚŊ‡t]J!ĶĨøl‚ĐQ…wŪųĩúĀėĸŧ>0‡ĢĶĶĶķÏęęęĀ;kjJ7o:đ2ĸâŪ7ý~ßĮÎWÖ^·ĶķĒĒÖé<Ā(ÅétÂĸ9ŦŽŽ„ëččhkkƒYøí-Œ"!ĻvT}ðéĶĒýđG›6ŸšļũøŲmÅ%Ëķ|ļšēŠōü„ĐŪvôeŅ$4 St‚‡€îBŧĢŧ1Č@=°ÄÐEÉļÏÚ=ú(ņIîĢßgGd-R[­-ا>^ ÚúT•f.(üøÄGÉû;wv4Wþ­895ŋēĄú•%s“æÍ]’žôĖė܂·+Žî°/|ųXuCɟ7ĶæĸĐĒŪŲqdĮ„…Ÿ”ÕÔߗüü,{îĘýeĮŨ/™;gþžÜœEI3ŸÏ[ŧußŧoĪl:álvðƒä„„”īt{röûûŨô=OŊ°ŧėģžJ秃°äī5ûVՕ—ĪÎx|EņþS§škJÞOIˆŸoÏĘÎLž˜üúG‡7.ÏZũöæoäÏ~nVjf֒ é…ï9ŦJó-ˆOīgf,ž•ŸĩqOŲĄísf§æü―ŪvC^æšwö467ė~3gނųéöyiKW–:ę+>ÉHYąûÓ--uë_Č(ÜöŅŋÉ9—Þ8Š ûŸFaÖbÃKQ„D FQ@@ žxē!Û=ý˜îÛÏ lČïásŽ8ę\ÝāąeĪĢru=NÕ=],y3AmzlxâÍIï:ą?JKWÅũË•8Š„ ‡Ūø™ióSc߆ŠĻÛgÛĐkĘ|Óö]ŋ)Ŧūŧaęš2g‡0‡<[ŨuÆVo·”n‘Ķ,Öėû8ôlPčĮĄ eQ…š§–NuĶĐ/rršaœÂĶ4?Ž:?yšú‰ßļq󭛷övÛgmST%SŠëačCh ĐĘÍ8 mÛeĶ!r떌zA[=n§ĶĒKÞ6Œwžš~BS|ðÎÛï~ų]Å júięčŅ„~ęę<'―hŧŪmëžĻŧÐ`5Ý4š@ÆØ‡ģģģjÓÔĨ_}Á`BÓ4uÓÆĀÃlãvĪ03%žmÍÁ7MÛöc[qĶūgØ žÆķä€ãųí°íB ;MÛ ]€ĻŠ(1Įn6t՜Ŧ((ŠKšõļĐÖyB?ī•^đ.’ÜMŪýÁˆöGķE%N#Įw߀ųQŅų―p4jAŽ†•$yŋ”ĖąS]’ũk6įŪü*âGr)~ÐWāgģžžÎõđ‘eý0ðÝūu†žķ[ē=99ÁH™ēŲ3þĘvþm’§‰dBšCUhūj“Ŋóq•üüŊįã8Š_„(Z%2ÐÂGÔÝ!ÛÎŅ`óvž3*Ô`äŲÉÖóy’Úåö$ųAÜbč1\&t’ĩ˜óƒDü(G7Á}ĪĄß2·sÎ#Q­Ûrrz†ÁĘPO•ÐÅZ?ÕWxf€.eY ÓŽÕƒûũĸķĘÖ9Ÿ|ÍųAŋÄafĒs­b"@IÐâĒt(rŅô‹[}zïAV”ļqzm|v†—PŒ=ã0ã€øýŽ0ĢĨRÐފĐtÁ@ó‚Í頗"OæŧĘžú#Ál.āyv&‚!ó$3?ģý‰–Įû̐ĩ įûã*EĢýđð~I\þä>{ß(Ũ;Åðō[ôáÍþč~ÍOgūV~ĻڑŸ=šó€>::â—S~3ʼnĄŲ8åA#8Uˆ%c åwq ‚Ąö‚ūÆĀF3ŽĘUâÖʔmmƒh4ę\"pĄđÄøŠFüøDŧðc(―‰$?ÎAËo~h}!?ĶWöDgߝœļdŧĢÓüČ/Øģ<ô}™âJō#&güPüD'šýhu\–›Pfj-~”œäg>^ďÓ"›ĻĩųSWī :Tô"ü*ÍOz.Ϗ’#~Œ0ߟ$?WæG’ägšv~|4CY •æĮhi~Ūļ? ėÂē·\.‹ÅĢÅ#>ČãåcŒƒ‡ØČ!ĸ,Î5žÅÁâððpyļÄ#Í%„ð†ĄGņCNÚá‘}ppĀĢr@āƒ-LBØĩhP‰‘UˆA…•ĀÔ`šĘ38Á€ˆhĘ̜$?īāQIďA  ų:û…üŋųņ<æGĄáäGd~H0?T]†9_âG⿇|l04Āåøá•€=ã'ãÔŨĐįüČ/[ü(AŨØÂŒ˜ž=&ũG čh4ÞŦøÁˆøÁŽßÝl$?$ĖųQ‰“q"ZïŦņÚüyƏ{Y~dč"ėÎÆü]œæy?č―oþ‘ŧ/c_Æ\"O2ôõ áQ‚-ŋĢŌÉĮ(?r 6rjÔ]ĶJ&ÜđsĮŅ]äÚųÁóFņcĻČócÏõņ#{î<ũßĩsW~°wáĮNC―üáUüäuņƒüĮüüÍNÂ@ûÖŋՎėˆāšp‰ˆä°,ģ €Ė+T  ZÕ ZÕÐáW՗―âúũ=Óvv°"11ą›Ø’XA“ąíRkŽhÄX X  4vP@šE@PƒŠŌĨ—]ʖ)įŲÕHŒũÝþũūũîKüáēĖĖŊŸïųÍ)óqDb‰˜áØŋȏcøïĄũé'ÄRC##Đ˜ÆąwæÃÂþĸ‹ Į‰ĸ—~cΈ–ˆEĸeāĸïÅņ?šÔū'LGoĸk’PS}1ð€äŚfHāØĸF?š†>L[Ӌ— -č™ßgýß'œ Z+;ęš`šÍâm"^*Ųö]ZWŠ0•ēĐŠēēŪNÉaí*öZĮþDĸyØĸä@õËē’Ēē—*þŧúą·čŊ‰ręŽøPŸ—kZXtôW ãzΆiFo+ŊQp{_jĸŲĢ"Øžføā13Ŋ*DIŠĻ[ĮįŽ7álN“˜Âĸó–UŨÚæšýhdĩûJ-ä­Š­MËC‚$*ó’ėĮŒąņū âũ?— ÏiÔ*–û/īIĻĘ\7ŪØī/ōæ―”čiå*œĀõW ’Tū,8æķxčÃ-Æ?bÄã'­NyTIÃätÖî­aœkÚēÚaðāÁýúôúĪ—@ēY~*‘˜xwS―ŅóߎJ ÔyŲOmĩ ­\#Ąˆŋ~"+r̆ <°ŋ^ŊB‘'=ŠGüW‡ĒPyÅÏÍįRE3‡ĸĨËhZUjđáÍåũFąt ĐŅwlĐ}OĀú—å%Õ*ĩ0|ĀpŽđķšĪžĒĄÁq‚Ö-$…æĻF†·Ÿ·ˆ išã0Ž'‘,†‹hąĄĀjd ›ÏBŒĪĄ"ÄÏcĢŦgĄZLXG“^rÛПũ"1ˆð,KDA"CAý›.MPbýŠĒgw’ęJHÁ˜Þ+―§o\ÅIJ"S$AK Ð%)M!?i‰ý-Qú`h1"‰Â4lĶ~yq 'iŠü―Ɖ%ČmÄN %büÛ"7RöÆšDL„H,A$ī`‘KŦēb·opŽÎm0’Š0―A2„―ŦúЂ'ˆOôš[ëž=ÉšÏõ\”þۓĖī]ƒ{I8–ŨÕLŠŊ{ūyÖėMūg%―F-\đzĘ·CĘÓÃlg-ŠÎ-Vf\rßā|.ŊÉH"jo |}muEEEA#-ƎA4tPŒį1R„Ē „(tyĢI]’Ęô‹n֞ÏWI(ý(îũt‘ŊrˆQ4-‘Ð(YĻehtVŸ=’@ģs!‰€}V%šŽ"  čÆNœb5i|#’åyኁXRŠÄ_@ЉMóĸœ1VÓR^Q;˜ŽkņJßΆhīT‚`øŠŠYĄáĪzý‚įЇ€â9žęú6Å$CÓz‹%ĒQ7ät0ĀŠPW…Üīvë‰f‚%™Oĩą•`@ČCZ‰ToGZBzi”ÞŨ°$ߗÚŋ!a8ÚÍ< ýLàķęWß=§Ŋä݋w°‘ÍpZu­ '„ëšÚ'7O—Os:˜ËĮЛ+bBýf͜!“YŊ>xŠķ—ˆļ§ũâũí;|*:ōļŊFa ŅĄsŋCŋüĪ+Išú~Þg’þvû‚Ģ•|þrßÂF–$pįóSŽÍī–Ï[ėŸ˜čï·ũrÎË?Ž0ŠĀПÞÝ6ßQ6M>}Þی’&J"jĐ~z`ë<™L6{ņŠkŦŦč_eîÕ―‡Ž]ËÉýeĐÜĘv[pĒZ]äđ^neuäĖuˆ‹puuĪĸ1ĸĀswo'üä Ÿ1wiR~Š;AąeįŽl•#sV„&år8ŠWUæōôđx-ýn\Ļ\.wņðŊláqœ Iöá3ËŽd2ųÜĢįŌ9d'*ēãũxË}RxņčđÜfhCˆī Ežũ?}ØÏ78æN3GrŠŠýÎ6V2đlzh\–Ģþ°Ēƒ°Ĩ"ô› éķ^™ô€ÃEPSzéE- Ø['üýr_ķJ ÅB!9@›sō|îsņį3Ã#‚ÜÖ;‡†đ. JîzŧD”–îũÞëŊģrųn3ƒŋĩz0ņ†Ã—.^<þÂ^·9Rž+ɈõōņÏZxÖw›\në{ú.GŌęÚįÞûöœŠ:|Ø7ôJz+/Âđķ?™lÆÜe·ŸÖĢúEÁæ„ϐýÞAé™ÉëæËÂï<­)ĘōÛåy%ýŅå°}2đ|gpY+D™j.ÍðŲå›zįRĀÆ%ë·Vjhó/ūüæËþ]h9ÕTzÏō(|ە›RrĘ0 ^ö0eÓėé2™ÕÎcW”<öö€îÐÂqyäEʅóįýF˜—ƒļ{Ÿ*lhCaâȊ =čŦ„l^ę…Më–ːįkÜn ČÁõ*ЂPkeaŸĮ‘ów9œ„Ч'ú‡_i†$dę tš; .ņō/oԊ0æÎđïč°C;―=*U ŧAïQŸęÓ]„œ"Ŧ~ŧ―cÝ4™ėį žYÅ ‚ '…wÎyú=., Ûã"·ē;v6‹ĮŅųũĨöŸS„™ús>ûŨŊ˜g;Ïųú­[—"æĘVÝ)ÓÐę"ôŠŨą·2Ŋ+ÓžÚ{Đ*ĖöņņŊnQÕ?Ė r_ģédËjēr2•,ŽĐ[SϜ~^ŲÜ\–įýˆ=į3Xœæ•EÁG<—Í™,›ģ1åFRÄɝ^{Ï)~ÉG6Û%6õÆÓĮa.Ŧ–đlõˆ{TCķ+ĩ$ģcNąœ~øl\öƒœôëgŸŋŽŊ}–ļĘq"ęu9•MÉgÂfË~8ũ” čÆÂk{Ü7Ú[Y툊IŠ‹õ^7oČ`‹Ýŋ&ÆÅm[ģāfąRÄ+OpųeÉĖŲŦŪ^Oŧ2oÉú[Åm ņŲŠEŽ‹\=.Ļz~ũŨõN6Žûc !RÕxėÛąÐ^>ÏygÚõäÃöFß ÄTJĀÆ9ģ–'ä=}þøķë GÏĻJ$Š–ē{įÆÉß[šx§&^vuÞŸYŪm*+).GÕdƆ'f457FļÛŊu=‘Ĩ`Ęģnä=RA3•quY+íė~qũ{QSž—saŲlûí7”-5q1É ,āj‹ĢCÃŌsŠ8‚Ô‹ðZÕó'é*@Í_4w˜™QKK‹ŠęjýӜa݉âG…EOËJ^"ë•ũbN'e)4ðí"aÉoyŲŲ9æ7ķ2NÔ\õrwą?iËÁS)Wc·Ūڜô RÓP\ŠŨ“~étŌ}Ĩē.Ðuþē]!YUÕwŪžupXs̰ABjϟ9áęūÎÖfNdܝ—Ę–šg9>žN֝=nĨÄßĩîįåG•„ļđ:ŧ—Û|{ŦU;ƒģō~ÓðÍ1^nk·~ŌČ1ĩųŦ'ÚėˆļRŨÚöüjLö“bR")ɈrēŸššYUQ|Čuᚃ—™wÕeUinv~vnnŅËZ‚Ā+ōãũļ­9™ú'ižĨĀg‹WljUZqō€_ō―\ķŪ:9Äg™óš‡ĩ<ýûؙĩV<Øåą;įEÁØôŧų]‡É“ģ§ÅþXx݋óLŽNžī{ĐyQZÛÂwšéī|p_cCÓ^þņɎ―Ã< !Ðí’Q<‡þø`Á†ā„Č$h+,―QS[yņČþFMĮ-ĮcïÞÎtŸ7"€íübZËCũy>oĀï>]PX˜Ÿ•4eh·„ā―ģÛl\Žf\ŋzx“HYqÜÃï…#IšÕ‚Ū='„$ĨėšÛh[M؞đģfÎ8ēŪZĐÁpŒå Û.ÛïÖÕ%CŒ['gdįßŋs.õaIkSrî%Døv—6ßڝUĄĄ)‚áX‰IOßÐ ĮÝ:æaYcëËžĀČØ"%ëu&5>ÐŔjŽ Hl&H§Y†ïþáĸčß­vž6ũþģ‚ūßīzЉĖņˆJ8ļæšųIfž–7[8ketJþŅuޠЇŊ‚ĨiâwņÞcóoÖ$§dĪ?ŌKTy$$âąĶ·_Äū!9jEęÝ;+g|TjĻŋE2-åOŸÐxïžrčĮrûôčiÂĐn6™|―uõc)˜ŋûLœÏŠ !ĮÃ?ŒjfĢÃwÃGŽųåȔĒ.æ8ÞėcŲÉs >äœ&;ûŲģ.ƒ-7­œÔUŠ-Ø{6þĀJš"ÕëxR§1ó“.Æy.Rūļ{+Ÿƒ§{Ņâˆ)óbRŌŽĸŒāX @ōĢaÂŊ^?ãÕÛfå]zXÖJ‘góū[ý"Ģüũ›‰ŒaĮ‹ŪĻ{ž\Õû|ģÚýĀõû™+­Gą%—ÂgWīŽō‹ļz6ðŦ^äeߨ—Zĸӌíj —ň!#GŒtņ‹dŒĖĮ~?ĩ;Ĩ9u2ķĢ bÎå4ąÓ–XŠA§-gŊ\>žøģÎuU5ĘVæÍœä‘.Žļ~§‹GÉ`QP†fÃÂŪÞ8{*ôD„ï81óĒžN…wqXæúU_#šÓw1Égm>7Ņj8’‡Dĩ·ęB°_V)đtÏéâĖ” åSÅÞ'ꁈ$h”Ī}fDÄ&ė\ų#Ŧšũ īYú[•Úũā $Ā~_ŠlŨÄĢ1ė;ÂŅvŌ'ýĸøíD1ŽķĩĄDĄÔ‚nߨfBH?JĪĮqĶ:ĸčæS-'9.۞Ŋ4‰Ą Â^€°!Ļåyv bųŅäŸ79ĸyL·(ÆkšĘ"íŸ üĐHD™u3ĸi—Ũ4–zh_5#5crÐÔšÕØ qžâž+žVY ïmlfŪįĸŪÕūšRÄ@‰ķÆH‰T,5Ž–įīŊK.҇oTÖkÐ`8Į ⚕€”p*E „ ÛĩwîþčĖŊŽ7øni„%‰9%l<†0&Ą#ž ĸÍÖ @›ē­ĨĻ[ËK_ŠöŽGI Ō(ŠZUbQã —Ō$dšĩ-ā D”Ū‚§ˆLˆÁ$Ã2€7$PTþÞ)… p‚ŠĘ t@ ĘĻTĻHZLqŠVF!C`ļ^į‘T$ļW‡„ČœœãžíāáĀ”·Ï‡î3Ąī,F"ĩeÞÄ(üU*ä;ÄĖkĪ^ŋļeî7eųiÛŨîLÍ­h·WŽ\ˆ­Īž‹…Q’îDã" ãõũŦ·ˆ  Ė?„ÅĨ+)­ !‘ˆ ÄüÃۉŲ*ƒî―MĜ0  #‹ÞÕĐÉąSí,'Oą·›ÞĮÔč•ŧÂqd‹}+ އJ+ø%"Ņqį.ŧö…D§ß―žô;rú6eY5Ã!^ø{AĮi1hmŠÔhXŒg_;9­–Ņ Œolð„҇îAį’Ē&™ÜMŠZėTРĨhAģåÜå>‡ĮÄßHKŠfBęåÚSŊÏGO›6Ņv†Ýø‘ƒ%Cš~j?lļVyo^!ëÅŽ…ÖfÄđÜ7x†•ĐG œ?ū+‚3AíÓ!Ŋ_—HE%…Å ƒ Xáâ,ôŧÖgėē“ŧ->֞؏PFKt;loˆ‡ž ……ļTĪmŪE(Äx)I`‚ pŒ,Jûþ „ŋ%Aœ0ö• įŦ"BŽÄĨĄ—‡ųÄāDï}šh9žĪ(eéã ó—’c/†Į&CÐmTŋM{˜uĒ@E|Č>ßcî7ßUƒN3mŠTĒc—Îðþ­Ž‚JPōāæ“ēzÔĢšĒđĄî|HøĢēF„Á·ĄÄīïW249ŪPäÝïūqųJ·ƒ‘FęԞgԏýđ’{ūŧw]đ™ž{î΋Ķá$ ::ėxl•ˆčāâ*ķũ3úu!Yúū„>ƒ~é;<úýŧ]œĀ[ęŦŊœNŽ;qþ|4īĸĀ^―ûuƒĻS§"SoD……Ý(АšúČTĘēž Î nĢ_œnPÜéƒOGôé8MÚ͜%ŊĻ)ČÍËmÖßŊ!ŽëöÆóŒF ŌN„Åe?Uĩ*ŪG†ŋh1ēt\6m0Æ3Š5^†§ŋ7Ȉ#O]JLOŽÍfã‡õíÓķŊIíDˆ-ĶÎØÅ =Ä}ë~ĸ{ũcOvß―[ŅņĢnJ)–gÔjp304>į™öēWOû^ŪS#l[aŠÆAF­7žė'†Ý?dN•įåÝÉĘWqęĒ'yYEÕŊ―ãÛŧ Ō“ÏE%Þ8u"$ýe“q‡>Ÿ˜JXžĸ,|u/V”eF_å?ķhë"c$^S‹ŒŋþVF"=wrŠĘ4jE郜ŧUÍró-z–žrÂ?čz—Áąã7—kÔĒïēéŲURt'đ†°°=ĮøÖšÖf:ũ0Ļ~žwû^“ŠŪ2;7_ÍŋR&LųhœÆÕɁū{í–mŊmA AAðŠĘV:w#ef=,•ŋÝ{øžÃ!ËqÚķÂčÓg Ŧ•ÃYæ$?ųíd…$$^ Š<]Ŋýŋ°1•BNMÄóþaŊŋ3q=}ãū•v…ņ!3mĶYÏYĐ0øÜcŸũs’A@Rĩ%]gįļ0>Oëäę/ĸĖėã 36Ū”ĐjîíøeENŦé„ūx\ÔqžãĒžŌ’kÍPú~xãÝ3'S 0éGÎKæ|ĻŪ9îūØĸjNIĨB3]…4D_8  ~šFusÚ}âįÉ_ßšø„™f7Ų°lBˆ‘Æ ũ†ŽŸ=îNôq{ų ‡yŅiÅcėŨÝ4ĸņeĸéēiŦv\mŋáО9]1†įuŊĮaD{@Ū+ĩ„ķą!pÝf{‡UĐÕ&óÝž~øēĮ€ŊlNxŊV–ļdšlÞšÝGL8ę3° Ūå00š3‰c"ĐXB ē,mâėîģaÞÄļ@Wkô ƒ­“Ë΋ÍÂFēn^@ ^Ģ‹8Æj™VĢš—<9đÔ3žē•―ūĮÞÚĘjúės:9ýâ2nĻÏrŊJ­Šbí|ėāF“ķ NķēYÎû˜į…x,ؕ€ļ”ĪĪø·+%ÃmŽOü2Øc­•ĩĩ=Úi)?LŸ―Æj ŊU7Ójx·—x…WаŨC8ĻŦlēéŋcíâe?ŊXąbąģëĢjˆÄņ‡(pBˆâģņŽō/Œ ú/ņ ŨtûæŨČSMŠm^*—Y98-‰šųH7Âfj(&!|ģ„ĘūxļŨYūÄíļŠĨ‡įŽÞ Ãá蒁eUĮˆ€" ‚h­Čņ\ąÐJ&›ĩt§Æäã=nKûšvúÎqËÞ5bÍķ·ēķą_ī* XŅūÔęd\ŽXđô§Ÿ—Ŋ@tčB6ä˜N'Ėþn š4ÕIöiŊÎZû™åŽ ƒ;å^Þ;wkĪĐ|P”đZŲ „ŽP€ã™nýĮXĸ0ŒQ$đŽũþJî`ÜI ēEķ_Üŋk­ßö%nįž|:uŽ*/áxL&i2`Ž­„Éw_>/:õ)Ņē„tęOÛvĖ·L ņēšjåVmŧÜËsģLĪÕð‚ģ(Ōv !þݚYĸžþÓĸbïZzÜĶĒ0Ž@ E•PéTĩ+@ŽbGŧā°…%T‰Ā‚â ™*…ŠPiĻ Ĩ@Iüˆd’Lėø'qb;~ßsŊĮˆ7—ÜĐF]ĩ•†|Ŧ›sÏ9ßųÎ=ķg1ē1!8 : ņæ/?ßžu[1F8Ïr„ O>úô+įoÔõšĀË{ ˜ ÐݓD^ŽÚŪïZŠjZQ ZŧÎóōĀ‹Æ}]āÍĻ7 PoÉÚæ§Fk YĄÓŠI•rYŪ7G^„ģŒāļÓŠW8A”[Nų2"ÏVšÚĀ 1ŸŪÍõcĀ8vWQ&i–ŧōŦįN>ûâË;œQiO‰ÐR'’A:čķør…—jÖØÏ§ĨNBOQ”áh‚'ŅÄP“ęB(›æIč6ëR™ŦTkMÝt’‚Ýņc āŧ#8ōDSė ĩ*ÏŨ;―0AŪmÖe‘~p―ÖėD@wąÔ}J’žÖäyŽŦÖL7ĖĻ|@iötEít7`6Í"w(q·é§Čŋþęģ3/œ~þäS'N<óú…þhcGŊr|]é…)üƒ’e*kP5ÏQä •ŪîTō]›ÚĮ“BE_ĸ̓ó<Ÿ 4YĘĄÞÚ{>­ąoŠjIJóOįĪúÍô/Ó7.]ÖÕWæÛ―ņrØŠwŲÕąĻ'@264U7ƒĨīÉûíŠPĶįÐŅ,„ uÁYFįMi58ū,H E†l– YXôČ(‡ŽE:^+.*jluNÜ7ė0ô$ūRWQ‚œĄĄčƒ0I1^†ČbUnĻīCïęf/Ą‰Pl( Ž“Ė=[KäųŽåÆ(šī’(Ę}*' -…ĶӐ,C‘ÛnÐ ä=u’ā|uЁ3 íuÃe{=gļŽÍŽéëf6Ā“0†•…Ī#þMzŸ;ûVĨ‡ģœ:ÜqžŦ˘ąþkÎ1‡ŨĶ9üūõ)}Ķ?wúĖĐ'Ąü/―ýqÓN–n, æÃDk\…ąp€ŧÖ8›åNõÜéĮ;uöF?ŸįäŽ̈ąŪ’°˜’Š€#Ø į57`yÜĢ ļįĐfy;ƕ//ūwþÝ?đāŒđŽĪ)GũQÁ†J™§óŒÛþœþ?Ék·€älLsØ@Ķ(VS'v—=&vB.Y}(+6Žx€uÂ#&äðÕˆiïĸįVŧ ØđuõĘ·ŧŋÚˆƒoķŊ—JÛ[[ۗKĨïR―4Ãpĸøpėė^ûnįÚŪa€ãü0-þZĖfųlú°TbŽÞÚ)•ŪKÚrĀ6ØžDqƒðėāā`>À$€dóƒ‹yNî7=ž/(æTæąP<ܑ"Ų‚ÎTN6WŨßėœÏŽE‡{ĶŠ{v=Á‘ö@.(wĪžð" ž !)D"q‘EØÓĸøšK&È=øÐ?KvuOÕŊjåK'ąw öŸ44444P;4444Īî&5Tku·ŠĄĄĄĸúĩ1ý·ßį|ÏGUk_8L‰ĶKŒXÚV_YˆþžĀĶûØ>%›ëõþ%ÞķȖÚþUGsčÖ6?‘ąŧ3ÛĮû„ë{AŨï€9ïš?qî ÛÜéÉ[‡_Lé|:íņBŠ2x;4t{u)åã3 –Oųӏ?üüö­ŠĻ%įyžsN"Ęģ}­^DJŨ„ÜTāJ)Þû”s)Ygæ)Ķ(ģ€ ‘™É03ðBzKNÉlŅķm8ÕūOēqĮvŽ]Ι:ēī˜M‹-‡d:2Œz_KƛMs6ÚâLK WJ%‚1p#â^sïXjņę­on7Ļ9}zpæ…|jKv•ËõLGM /āįaš3ŦŠÉãï_}ýÍ·ß}:þâ,ĸGōþýŊmæ[ŌÐÐPÎų͛/ĮÏbLĸĩ"ōËŧw|l~}ą‚”Ü0“ešDŽY1T%nØX–;˜hČaqĩ€,@SPà ‚k”&UÅy;Ÿx†Œ4‚ÔZÐD*Bư,Ā“\Жˆē“1E ĘÍÁ•Ĩĩv0—öa )&ŪˆkĮĨ-(ĄVfŒDGZ‡Ō Ĩģ˜KF=ŸÏThgnî,öÁ3 3…܁ĄŸ[ŌČaNŧ)z‹ WĒšVQå^?~|xxčæ'ŨE9+}õęóŨŊŋ Î ݆TåÃ‡ßø<ô˗Įg8Õ" þþ€VøÕđ, Ŋh@!tð•†ZĻNäpybÉ>˜€ÔÏӜ24ÄBĶšep)ó=äą,ũęÕāâ―O!ogŊ\r―w,äĀ/ėČ!°)ÝÚM pĒJ%nĨ sēÉ åôĶˆũF%·ĸHÂ^‡ĐG™ØÍ‚ļk^YĜđĐånđ#žō' ýÁīŠBVžéÂ4v翜Ë`ÔÂr ‰1·ũĘ+c8Ļ}XŨ~f§ĶOÕ―8ÜŊmą ëz8˜ŧĄĄĄ[A­Ūë ˆŸĩ"Ē*8ÂÜÄôâaįåïã ðÎÛÄ|OhPčĀH°ĐVvÄKé‚ģ€5ž"…Ð# …ļæ*8ĮsĶIėčJ ūCÖŲqĀÕâ:ÛŲ0ŧBYÁĐōԈY2Đ+éļg)ߜȂ§Ā.ĶTK>Zfķóxˆv@îø­ė3ĢâÖĻ]0Ëv f ;(óÄfÛw]퐞§“Þ1äœ/Éļ ļŠö}fŪœbÚ°}Ēz‘š ÕŪįüq3° ú œĄ'œeÞb#€vęœ%$žÔ{!ĶĪōJlĮՌ[-Bð'{g“ä8ēÃ`Ė”ŦïŲą’?Ï$ÃoŲč<Ŧtˆ•áâŠî0·°t_íSŅCŠˆ™ČĘ*…@v^ęÖáØĻ<@ĘņĩÞ`ƒTdšMąęĒd[įéDueŧ)…Ï ;G›gÍ}Ķ­ĩÂ}5cÏ’‘ģRb]ÉÎ―'ƒž?GfĪÕU+rÍV4™~|6‡ ŨjęĨŠ`‚cüŧzôčĸVečtÕ[HdĮĐHŽĩðąĄNrO1ĄOÞZĖ+ēŪõngiSæýū3]— X‡,o…$rĒ-ŸõÔæÕÏ{HŅqg„ef§Ļ{ķdäęeÔr5x·1ÆÜũzeĢŌdãv$ R7ųė2ƒ"Črp$t°īũ]5-lŊ%ĀLˆÛķQƒ 0ŋ īęÎUEkˆ™‘„ČÛNvĶÓCBš]­ļU–n$:ĀÕF0ŋ­ˆâßÕĢGÖßūÛk2ŲŽ@ĩÍBÛ9yĖ2l­ "~ÛĒŪŨÏ'…ާÚ/ÏąŒäDNļBmøuĄė―„göūïŒøXÏlų!Ōé*ė>‰Ü“ÉRYXzn’"*€CNËČhÂî&Ck7™î-Äý~“T2J.ŠZøļ3Ÿ•„7“æYïÕUŅ:fŲNRÏęöVy4éĮ––Âýœ{–fAÖ1Dė>BQŠ[}I‘ßHĩ=T›ÕzMuģ.ßtmāݛŪánķ>z•ݰb,ĻDFy( Ūō-W!RēĶ ÍA&^Šûš„"dzl]ŅČŪm5ڊ4Ïþ'(QÄ#<@;īJøÜYÖZ}{ŋ:Ø=sčYOąyüĻ‚TũІíaó„4;a.‘‚ITėÚ{&'ēwb{bJ!@ïm›*#ĢŨ‰[U‘ó{.JqgĘ™1 ÂÚþÔ ~Ģ=V+m7ãnå,SÖÍ68wR_í_uĒÜ{‡đkrex‰ĪLû5Ąk%2<uë6›Äšžeeg">Páûšrvó‰k_HČ·ņáčÚÂ"e’Ņîĩ(īĐ@tÚ‘JIÄÔfĨéQ *ĒĪ7Û.ÕtmËÖ}-Ʉ(•CĶČ`Mƒ™‹ ū)ø…Ú%_]{ļßïŽŦ2jHĸ]Éû4n“ļï“*2{mˆGýÂŽh7ôðĖœœrčøoĩĄĘKʆDŠl[æT_Í#õ”[Ÿ”ÁÕiˆˆķ·^ë‹§î­Zųķ…ĢL­|[ĻθĒūØ{ŧGdčŌĨÚWŠĩTT{€_ûEaī”õũĖüÂŧ! JÖîð’ŧ)Ëūŧ#ąDč=!îsĖm_;‘^nR3āæþüü95ĀĒMüôdj]ÐÜÖŌjæšn+?Jý`aušKæîžŊ”("æ―ÂŌ­ĘÞ\‰.G&DđĶ Č]ũŊóko(í>‘P%2Û·'&ö@,ÕņýAéĸ/ÍšËU_PųģöĐ IVŌe5PT(ý͜ŠōØ‘―ÛwQ|Ėë ÅøxôčŅïËjŦvÚ.BČGíeļŪYšgFÚĖy„ėX ũ`d‚"$cގĨŪķ'ĪHâįõ§mīĻ5)΁9Į[ͰvۉŽÂꜩ)°°jÚ$ŠĨxÝ3 ëžH!UˆÉš•üyóėđ‡‚Ŋĩ ~=^Ŋ~ŸØũ>aĘjtæœĘú|é^>ōÖĩ/Ąx˜(Đ[4ï}M%ci}IēĐYęŋĖĨÓlÃj/S~ĻöŅĢߙ՚é$%ðūïHW]9Î(åYDb—ÝTːUĢÝ~”Š„ í šõxMËô2;ûȇ.31ï/ä>…īÝĨ•ˆø{WŨ#ÉqóŦz– H’ü@|ā‹ųäĸïa˜äGČË~āÉŧÝU™écDßĀį#e·Ā-āÎåÍeOMÏŒĘŒˆÚbÄ;ĖDęÎ4ē\ũœKA0KTˆǐ[)čŽÃ&€ûæąMœ§ÎWĐÓĒJáißģŌÜ/8ØDģØKįŪ,ķĮ1•ú3hŋtŒ0ŨîīķųŠ ÞĪyŨ|­ĩOjķÝns­•ė5Š…Ŧ[åŠŦŪúĸŨ@ČŪą wßũ'@Ō ŸĒûĖTi5ŅujNIė1Æ8ŦũÂė˜\)ĨÝEIUƈšĮĀ)kĩļÝæ~&Mę6ũÃÝĢręgýFEļˆû.ŠNð]K1čoé• €VQé.Į™‹+Ļ ·TELó‰[ķĪŧýøi‹ˆ‚WAB IïØ\Áĸl8dĶŋ.SkxtÕMh sw ņvá@īMŧŲxū/=y*W]uՏۀÝý‘ĄÖ!0Č,áėä°ŧÝĢšØdĀjŊęUŅōØÝãųaûq0Ö&#ÆÓąŦöÔBœę.k)she͚ň\Ų"ÕåęlōÞnŸT7mÁ6âØlŌÝÎhŪĩŽĘ5ϖqÉĘNQ0[Ãæúô_OhæÆ›ĘJÁT ~°RģÓLŒˆežę*21'ĪýŒ]xÎÉāÅ{ÐLWû°9§ķ€;š$ĒīBāb •QÕ&īĸŠūũ_ßĮÃþ urÍýŧŋýŦ/ŋŽ*đ꩗QsÎĮĮĮŊūúę›oūYk}LĻ°ũŋ ũÍÆ0ÝīÁå ĸ·„Î?b$ąAņ,JīÝĒráfSÕÉ.ŠÝįøŠÍŨJķb^x—ņgéjļ[xeZĒŽ&DLlīoЛīĖy˜9icI›Ŧ)ķ€ÅPę|;ÂíäãēÅļƒC„/PTC—ä˜Óāz‘cÆgVĐ*ð`k*hûō+ĘĨåŝ€"G‚R‡ðØķ-+įšP 7$Ú-ÏĩoŽŋúëøųį?ҁÖ"úh~Ā::į/~ņũ_^PûÂębĩŊ^―zsŠ~lą—ę}V]ÛíޘNlŠ&2ŌuĘĻ­îZ„NBL!đņÝuvZþ=U…$ŲŽ1€†ĐðŦē?0ķëęĒ-·Ÿíá•ĩjōAEÖÍ3—K!Ū:ÜÝßIĸūéėĒ >ŧĢÍzĘķÜ pŸŠÖŽî^bƄ-˜Ý[Ü#jF†ÛvŽ;Õ#ūĄþ:Îđ2tūœķMnĻKã1·–įĐã?ó;ãâYŽ ûëîwîĸĀu*įíá*iy1uÕUŸ~úéwß}ũņĮb!0D„ãŊį/EôîčeN w˜šūÅЊfF·î›Š&*â C0iwô@pŋÝnļŪČ}žĻ ’ÓĖî, oĐƒFĒï„ß;l‹ã/ŌjĶ-<i)Á#~ķJo‡E-9RcčÂ䯕)Ę ĸXZČÁÏö…Úq,UcĄJž†T_§ŽÏTÔÅ0 SÜąšĨŠøĻUŌŲBŠô;Žö”OČO_§Kŋˆļ öEÕĩD3^üB|_čq3#â;™Šûūŧû6nŦēéĄ@įbwŠÞwúaæJž„Ūįp‰Š)ÅŧŦ ­žų4;Ϝâ*0yđũ?P„ëbČÅÝ – ą›Ý5 \ 3‚R oŋã>zÍV]ÜĪ<`T\8œrŪîžóĀósGD؝ä]Q…Rągģ9­RU#<ņœ‚ÐúÛnĶj™éþøĪ°ņ&Ā‘ČžkRÉä̟õißĐ^ \†;}kâbaĻķęė’ívK”ˆÜÞvÍ-ÜyÖCÃÛÉnŊvũĖfCš“îwÆAÐé@Ãƀ+[™ŸK(WŽM†lė{Ž Đœ4ė–4ū™gŦ]% (ĐũŲžX‰j“ŲJó{—jBŠ”joli•{™Ļ釮cmÛČKƒÚŦ.Ļ}ną•Ŧj™}r(ûփ”URmn…)áōÞQ5wZNņ*ûą7f\*BôĄĪÓ*3č Š7gŊ@T€tÂÓhČFÕ4Bũý’đāŽFŒW €nU ŧŸŠŦ'ÁŽäNŸ}dU;;]]MŊ-Ãˁ’GUņΉyķho–å#ÐŲFéEWN#VJwmá ðāŽŧϕ"ķ—gËXī)dðđæĘõž „_}ęvB­‰9 а7ÅęVÎēšīK*•Á•üĨ xũŸģNuˏŽcHŋHíŨUWŊVÕT”~VG­îœË -ĀņYklĢ & E ƒoUÅ1aG4Ė™ÕBÚsw=ˆīy°åĘSÁą7·M(Sopīpz:Ė5IkŪûŽģ‘€~lÆáY ÞUÎH捁LįíáVY-mb6,W*Æ`hđŠ2ķ›JįĘŪšÝoĶŌtj§€þģ'Éoā ŠûڏsÐķ?í-5Æfæ HÝķ­Ŧ―ŽąŽï‹G“õsĩTŋø™Jœ{|%VRK ļ`5)éĸüGąØ(ë|•~Č:ÖšÅ05ą–ŦŪšïsqLlö ž’ĮT3ÁsķTk‹ Ü[4ïē?Ðnáf+Kĩ·ą•tf՚cÜę4ī6 &ģUęĻĪ 6Z܍F`ußHQ]՜öŲ–fneEøˆąæjíp7Ņc?ĻS3ķ8hĨ]™LģíîVQQĮĘ-2,ZŨÝq‚°ÉŧōmÖn‰Á#xSAŦOA›[æliQiŅ…ðYiU| WQôÛėT#ī Ÿß՗LĀŌ3”Â=ņŲÏĪĸ•Å+<ãŧÕøÃęû­ïԇŪĢ­[Eã\^L]uA-Ô/€Õ‚nÛææõVŊĶUšsŽˆÛķ͜"bjĘĢÂąįR-ėĶU4€ģk­@óhėÂ= C@筈&Ģî8R„ų!ÉĀŧ­X4y‹Ãu5Z ás-5āĐ ÅŠÛØîŅYĶgč RÆÜũną1əÂAYŪ5wĶ(dqšŠ-]8Ú‡Ų€43ŪŨÔ"‘‹#!ķ 7ĨÆKUā“Í3ŌyPęŧmãņĐ+KžĨTĩû‹ņÝþéÏEZ[Dĸ\Cmĸ)GýĪuīÄþhō’ęŠŦÖZŊ_ŋ~Žv9€#ĮD“Ô!Ž*˜Ya“^hŨžōXuiå‰^ĖÉzš{0vŦVĒ[:Âį>ó-Á<5аH`ĶÚ-nUĀRSĢqėͅ_Ņóƅ ó֑4t%~:ķHÜLũžĻa†€CôŒčÍF372WuQ€™Ũ w‰G8ÅdŌ%ÂA_w›ŦīŪã þA@―đĸŪ’Ų0kpål·û‰ŋiĶÏe7üËßþöŧü‡ų/ŋãÔŪĨåãVũ?ŊĸðęũýbXíUŦĨũęëŊŋŪŠÍjA*=FFÂ@™#1k‚‹lŪ5ÆØÆvÍDœ›{Š w55xÃ2€‡‰šĒĶÆ|€\ŦĖÖ\‰ÃcŽÚŧЙĩõø˜•Ūæâû.°G.$lI‡Įūï-MĪ[kVõ6Ɓëˆ!Õ+įĖÉÔÓãĢÂݰA^&Ý1BšŨLSņáМ%…îxÏ(-#†ŠB3kŽņWŌ‘Ė Ķ1愖6"(TČöÍŅfYÛØD•ózwč”ŧéc6‹~&Ļýõo~óŲgŸI—ž˜jĩ—†ģW]ÅSĩ>>Ԗžķ“31J€č\†Óáâ‡ėÉĄ1€—éā-ôm$ČRí8ęVŒđŪbãá“ŋøâ 'Cû'Ãa!ƒ?­ī#ĒP˜ĩķ.„„Ņ”œŌęšĢüšģT‡tf3é•§Į§ĸøũWĩēÏĪóĘŽóx„jGæP§ėkÎcSŅÎĪ8,W Þ1ķíl!ŠkŪÎĖú>yxxĻ.ąWķĘZ Į”đMx=LĨšAޟ§Tõöð ēŪšęŠî~b/ŽÖŽ18ŒjZþ \ÚjĶ02}@Ču\Sn7ôgs%Čņ šāãYTEíŋŲ{89Šã{žŠÃėîerÎBHD9ŒąÉ`2c° &˜œ &˜$’ÉÁØD“ÉH€BĘB9 p']Nŧ3Ý]ĸ™ę“|>Ëû'üĸĘÚįóŅ·;3;w}ÞÖVŋzŊĪĪäGG5rä6ąwĪsö[,ĒūÍŌÃ6Ïnð0ĨԂ îüÝ­ŦV~Ģ DQd’;–Ęŧ—Ēö―ZöN dēwŒRĘß·Ÿģ<'ĪŌOđŽ&‹"›J@äÂA*!b~pŠóuø64ķNš‘TŌkr7―Cyä‘ĮF ZņōÞWÖY$ðiŊÎQ :ÃÞ1‚ŽC)Ē0­)߯Wš„@ūXó"V~œĩ čŒãķĶ$‚Ą[+,,œ>}Úá‡ÎLôý~^Čd2|Č3|ĘF†ýš„VIÝjž‰­#á„4Ō§Ē‰Ö\ƒ―@Í9Ŧ•Jy5J­sjÂģÞęLŦuŽ‘~Ü#Q/)Á9ō“ĮÂįŸųQYž7Č#<6?Š%áĢœjåĐֈFžōú īÖþĖLĀņ_ĀíØ­ŠÍ “IYG”,A2åZ)[%Pý Üzë­Ŋūúę^―zÅ6―Ƙï›mw―ûŨsį|>qĒH€fŅ ŪsŌ‘^æß3 ȝX›Ō)vÉ$!ƒ õT2þĘeyȍG<âAå}žxč-PĘËu œ–*ĄuïčĩÎęŒũ؀ <ōØėĻķÕ@‹yám%ˆëdü<Č`žŨp "ÔZqš Žāöfœôņ_žwä=JËĘšũč>hð`­õļqãnžņƞ={~ßJ·øE;þ'k*Ũ,Yīý qrÏÞvKķ~šũö:^jfa/G6üf˜0tŽØGX_üJî۸ȜÖwį§ã|“Ŋķ~ķx?ÐYo‹ÎYmyä‘ĮæGĩŽZkXO‹Þ\ ™›$6QīÎnÐųö īĸ0Nd*oœ(hVeųĐc]Â8;uŽŧīзoß7ß|óĘ+Ŋ,//'Ēï+ē1lذĒĒĒŌēRDðŋāzDŒÁþđ~ķQ˜Čße sĄT’?é;į―Ĩl­|…EÄČk$žc™·[lĢTŦ―2Āą‡ ĄŸkČ#<6GŠeútÞhÆGÛ2YDϰēđĒŊUmkcAøz–é5pÎy ? áŽSZ9žÚÂĒ{ž}ûõ‹ĒĻS§NétzõęÕŦV­‚ïÖZoԐÍf›ę+V­ðŽ6ņëFQäÍvĨPcØ ĪötÏþ 6°sôųžÕJ"ä‘ĮĸÏU­ĩZikŽðÂpĒ·dzõ‰Jz=ŽUz]Ž d›Dĸœ|GŌ8hí,VDwiëëëëjpüWðýĐ:žnYŅĄC‡5ŦW:Šá8W<ēÖ ĐbDl_ R–}ū(>@HN\·N‰‚˜€”’ïßOņú…Ō^90ÓAĀĖē3ĪöskˆČYd’oÆįũĪģ]„āýĶYGĸ‡äãQÓÂĨ]ûõ-”‚þó*—VfÓútĒuė·k0)·láŠL—2ĸ·Ų‘ß–āyŠEÞûÖ9nY?Ãø&ĢÖÞÜDZIēÓķũĮōZd#ÎÁ6†ã!Ý@g[ē1å 6<æŲŋ/[ķĖ+Éū?Šu\֕•åē-M Ü$Õōޝâ–€’žŽ#ß@ ÉŌXö–Ņ Ïrņū΋Öų­ÂČøôrMDa.į›žÜ+ @(DŪđØŠQ·zßø`g―°īvĢEŧ†goŧęÝU˜Š­îąËÁĮø€"ĩąŪŊŌÁâũþøðtuə?*îß/h•›}Á9?ŧôŅWwíQjŋó]ĄJÍũšk^Ýû•—NÂlô-§Ų0g”Öĸte ęošüŒmNūņŽCFÛ0„ĸģ “mŽdÖb“ï0å!ū­.r8𖗎Õ\ĢĀÎULOŽuJh"ãŒ< ëýK…~Í$ĪŽž§Œ@ŅZâ!”węÔ―gÄaj1ÕĶR)ņ―=—u(ŦŽXma'C Ô:ȅĄ‰ŽũlŒL$―,JĒ#[M͉]u|^/JvŒŒ˜{!ZcHą—Ģ·Yq@Ü+@Г†C…Þa‡ _vŦáö mÔŠm8gŌûĩ]·ŲĶŧ~毋ŊynFR(TšĄĨ`ݞN:HĨâG)U,ԁōä΃ī‡bs·ø ëR*AĪÓ%;–ú‹ö§Ķīô/ŪųG†?ŦþДFY˜jÉö&'yūđõ•ü‘|“ -øGíRž‹ˆ"Õ\Ē…_ĨÛū(HÅG2Â%sn{čÅå”IŌŋĻ?ý‘:^+H§Â°‡‰ Ú6é_n{3ZĸZþąîYķŊ’ŋÄšŧ]ũËŽĸ;ð!Ļ’’Dm𘔒üĢ ŌĐuŊ EØžüūÛĮÕjū#”ëŪ'á yŠ%ž6•JÚäŋĖĄÖ†&ō< €‘ĩœ†"ȁÐA€ā­\ RŽØįÏāJúá1_ؑ%þ Ū·ØbhiIY6›7o^ü]k-ŋøØocĢĻĨĐ ‘Ýŋ8Ï7HiöēIÆ ’ZMĘĨ`oķHL(Ō8#1Ą–( ­ą:ĨČq°@%―wĒHþDÎo|I­þČøāëy)ŦƒĶ%~bōã•Oūdž°ú/üî՗9ųĻCO9w܂đN?î°ĸäŒų•ŲÃņŊ=õÜëŊžyĘQýøĪņóŦtrM+.ûåiņé§üėŅĩYԐýā•'^zûÝŦN=æĢNųdqMōf_·čԟŸ8vė!ŋo–Ô:ŽþúŅ›î~áņˎüÁ?―óPd_ŧíŌ›îųÝąG\ûáėZS?ũâģO8蠃õø$H„<þWFˆP ą R†6į,;ËFąN'RSįÝcĨ”‘ }HžA ~‡‚T`žp…‚-†nŠUTŽŽ'ļ’ę@ˆïĩQ+،Q*p%Ó= é­―lK D‘ąä­ ČÏĢ@”˜4m[•ž­ÃlJD^ļ–ü8ĄŌO—€NiNjHNÖk>1Į!=Rjg―ĸY€ūøÝxý=5͜üákÖžŋšï.'5õģÎ?æqæõįþōš .íũ—?še_\ņģgĮûģcÜâž#Ø}ĖŽtō…ũė}ÆUĮ+/íÛ+ûŽ:øĀ“NđEÉó—žýÛ%ÝîžæÂÛ.ļö‰^Ý/›ũ‹Ë>:úúˇTzÞK e ŨŨģŲåSÏ>í4uŌų§î’*í–[=åü}Ï(?åÚ+Nu·þę’[ï―ęĖ~㟞ęų’.>æ‡nūøoí}Îų‡Ėxé†'^œróyŧĖûā‰_+:ïW'ôšõė™—ßōōļKÞ―îĪijũKŪüÉëw\}éÍÍãŪþ邟žö•†s/úŲÐ/}āÉF^~âýŸŋŠũŅ—lŊŋäüþ/žąWĶú™ëŪğœuâĮÖAjéŒ%l fƒsZÅųá/Á!ęÞD‘uģZ+Áö’æ™ā0du%Zk€h#nðŦ°nŌswøÕƒŪxöæ#·Ž˜úÔ[k+ĮĖý|ĒZ^ģbáōę-Cv;ãž.–ÓŸđãŲĨŨ{`8M~ũá·Ôþõu<ä·'0:ÜĩįGŊþ qÉÁ[ŸĸÜ;įîīíÓģíŨî“?-BXuŲïĪG{n‡%đïĄ;ŦŨŪþhöĖ}.?ûČýwWŧt|üƒŦ!2þ†ÐT8{ÖÞgžyØūŧŦ:<ņÁõ›fŸ8§š~ú§Ūýzé5SjĒė?čé&|8°Ï‡oUŋü―wŦÖ2įóÏ`iÍ7‹ŋЁr($ĩ,ų›Ĩ];Ïþā3ŠYĩtU]ÕēįßŪęuÂeg_*J―ĶvÐĀ­VîžÏƒ:T\ŧhžÍŌ'ï|RUđü›åŦū\>Đį?ã˜ýJ݈įߜ•mi†T°tęø);ĸéâ öčD—}úāä^Ãßžô˜þQÕПÜ5ó―Š†Óre[\rųĩgïT}ņęĀᗝũÓmŸÏÎ}Ŋq­ŨrņÕžĩ}ãžâ·Î|tÖīÏ_{ŋéļį/ØkŧŽýĢ“ū~œ•5čc.ļåōsöųâđ5~TWģvޛ/O-8rëņįΚ7cþĘ=FšÂĄ#/ŧö7;e–õ—gĒô°·ÐéÍ}ÆîÕ­8ŨģũĀĘgÞ{ýÏŽ>ęH„<ÕnjøvEÉFāëĄŌ’ûōĮ6ŊÁGŊĨeĐŽ1|ßžq y+­ˆc@Hv\ī}ôĮÃZZZęęęÖŪ]ëĮ%ūĮŠ–7…ŦÖŪHÆ ĀFQŦû"k/óŧýóģOô ÂÐ&7K °í"MMC&”Ę{_Ō€kÍ.īÎÏ­*Ĩ%Í-Dä(ŸÚˆ3ÕĘeČœČårEŽ=(--ĩÖΘ1ƒþjÃ.ŽĀ;ŊŽĮW ų_>þ<ë­c3™ôŨģg°$ lk›U(!áØúÖąĸųz֒ē5 žķÚķûvŽæ†L:m3ÆjíÍ―"ķ°‘ā(ĘqrÅ`;))ã ņÕL„ žĢ mÄ}떆æFģÝ 1=îvŲ™§<ĸĮßS^>õÃųûþrũæšåÕđíÐRSmčlcSŧŲ°ĐĄÅ”ýāäÞđũ–‡įÜī—föŧcĮ^šK·wlüųC“ž|ï–ĒäžÃ–œ`9`ØØ$ Ũܘm:ėŋÃnÏ\vëe5ãW~õöž=ĨëšĮĨĢķÜōčĮŲētíŽw^øbzĩą™m;čÅóŋ^{ØØîĨ+VWZýųgŠE§A{Î^UU6āÔˊĶ~ļ`ïswmŠïy$XŨÔԂ…N:âĐ·ĶūûŅØļjE=îwꑏ{íÅ7výÁð‚æ’m\^õŅĮÏ=öė€s;nĮŅW<:―þÐý:`Ýę†Ķa[n7ņáįoz ž`á„Ũ?Y°ũ/S°úítĀÞ]ļþŠkjߥ6ę°ïČķ7ÞxįĮleĸÃÄ―O?­C:Š•+!‡ė…Ų0Į<…ņ2^ĪRŲ†gŸýíN˜öÆrčv;íļ[Å~é?ß}[įãvųëïŸūÛ Ãŧ}Ü57Güra6 ™ÎÎøÉ°įfÏŧˑķķķŠĄĨ]ØÔäđ\KSäHé‚ĶšŠ_|ģËvZ1ũĢđU™-†oņėžE„bęs7žņpíË―ŧō…ŦÎ|ļîåŋÞUņ•?}ļþÏoÜÞ7ˆ"yl2Të9ÂZKœiØ3ãU‰.øÐ0oÝíŋlēOķâOÍF(Kõ{MR$—–tëÖ]kÓîīiÓžö ?J!Ð •ÎPš2]Eq1ĒjRŠZRƒ$LVü]Ā?cÞ(ŠÖ•åZZŽ1ZÖFāˆžĘӄÞ/†kä2:^ Ĩ“čýŋywĖ;ėA)Ģ\(ؘ‹­―-øTGēˆ ‰e Ā=l`á=/Ĩ1!‹įb{F!$S6ڍØŦéQ)zĶŽ+<õęëŪ{zUMņÕđĸÞ[o―ïŽiå―ØõÐ΃ĮühŋÎ`mĶÓGŪ ĀŠT—ƒøQũ ÚúļŊoâÉG0UpóÝũíÚCöøÉugv]ūįv―­ĩeý·;ÐiđnĄÉĘēGĸÎR;þü'{|gjåžgŸ·ð FÁ^'\ðË%Ũ―ũŌŧ;ėrėCšĘDX6tŸŧo―áúqũ1Œ:â ŊŊZþô ÏĻēūW_vA—ōÞŨ―qÏýwÞ}ïS:ØbÐŪw:ö‡aũÐĨNäþÔļ+ŧwœÎ=bŋ­v;îū;Óŋ―ïÅ?Í;1Īīũ.§ýō°w'>?}Å~^q“xãƒÏ>č„<üœĄĮėwŌõ§Ž|ũWlqčÕŨî8Ļ[9Ũ*!ï2ô–qũßzįĢÏüéųþĢ|Č!ûÝþäosóCŽĸÝ9?ŲAd+vsDßÚ:9ú°\ŋRkiÐČÝ( ĶÅĶ;õ) Þ{þĄ:Ųũž+ÎîS^rÚĩWßxËCũ=ÞĩĸÞ7R‰ ·ýĢ\Ŋkm·Û폐 ‚“Ūx úæ[ŧïnYØãĪÁ; íÞëÐ#~XX2%ŧŒ9b@ĐÔ]Füüž#þðúËģw^˜Ŧåé'I^qí}3Öö°ÏKĨĩå}âÅēxŅąÏĀxĄ|ĩðO–VUUÁ? vÛšäWūöōK…Ą"{w!Š@›Ð8Á•›MāA*i"ã‡$&úxsKĀyđžéïĩbý ūōękúôékîđį?žû·"4ųó3ĩÕéę™-Ü"<"]ØIĶ$Ų, Aš&!„Ž“X/E­‚éjEë.ƒā›[šy€‚â"šĐĄķrU…!ƒë tX u ÁūæVÁ-ĐTČųįRHDÁvĩ·løNŽ$ :‰ë •äßb}Š!?î,·b„K`ĩ0Y["ŊSuĩ5?>ōĻnš9NāF*•Zļpq\†ũëŨũß7…ĀT:&—3„R§īt&ŒœLQ.K*þÉeģ ŊrŲ,ĄL§īÉe %§ĢįH…\þ+VÂ\ÖðÍæü"‰Ã*QD‘[ðĘģ7―5mŨûþô+ûŸûäígJKĀāëÃ:DaÖ:āŧó/EŲlĪR}YåŒÅD"ÚÜs:6ūįķ—ēIĪĐ ­üdģ9“N'å*_‚tJÚldýS΄Ąq°B~”û‘’ßE:Ø09HþžžŌĐīkš+oŋāh·Ë㗞<`ý-ĄPĐ@ƒĸnß ōYü[óß­Í/âĒ0tĸŋīk}­läÐßŋ‰BZIôũ Ôi-“ûÄv‹äĩāûCJĐųóQŸ>―Â0ÚTûŦ ~ųÆŦŊ•” ;C”~xÄ|6ŨZ7ãëEãëÆĨlF>f1ŒüPƒ€v}ó-·ÆÚũÞ{ïõŨ_ũŒC >ū.ŽXRë<ī`ččŌ@ 8pīN! Z$ Čqãd@Ũhđ<+4Ô ŒárÔTÓ7‘ļŀđsgÔÔT#‚ũmā97įkŸŧ•ŠxMņā…kRJ $ ])ĄuĀöģ\š*í‡2°„‹]lYėEÆZâg ô!iÖD‘qr‰­‰l ŋ455yôąŨÝpã?§ÚM(UÓĘyũý•ÕÍ%rØ] ۏNüŸ(·p#ï* 2 Ķ–>ĻÜyäĐößëÕúCŊŠd%~%és·€•ėåĘĩ!ïŨûctąė)†ũŌfšQÜįĘ >Üŧ"TVVZkýԃ €u‹tÓ₠E Ktïm‹5 kK5„h,/YLÜB…LQ.ė’•YŊDĢ‚:TkŽĐÁtƒēš%6a(yēĀĨd(äDēx\Œ{įr)eb.ãMuPÎþ’b}ïũ_œõ6]JēŅ é@ûßËÚ$Û&9‘ŽTB%ī.1Đô#ãĪīūįËķ›6Ț‚ný>ņLΛ'ŽsĢöĮüß0߇ ‡jāķ{ đ<ÏæņïS-ÃÅō^‚Þ/†ýĩwŦōsSDÎï9?gÅ0&â€ÃÄO€€Ž5ūéfģņØˆ[Į%m6›ĐÖ+^―?‹W­..Z)ĨĨJ·Čt)‘Yó6ˆqB8ĸ˜2Keú ”†•Õ‹:ė1u)ρÜęŠī­ÆĻƚ& §õ€Œ!Tdž! ų‘9k“―tÖų́ąR0ņZÁü™Ō)Į6āCzü֟”‚É­Ē^v ũ–!lú ēÖĀæVÚä‘Įš-Fä€h―—ģ\ŲEF~ŠČ9(ÎOĘ:)ĪÏ)ðĶ_đ0‡Â—†­&eĘŧwïND1ÏŪY쯔 !ü˜Cķ Ę" BŨģ_:ī€ð™Ã!P嚧(c0ɒitnžmž‘ ?@"fÃ,°pÂŋYÂŧ‚)Ör‘Ŋw}îĶïŧŨ·ķŲ-‘Äxž'ā$GïŠ@>$D Č#<6C)Db'č,Ũ­ZJađiĘĖ>8Ĩ€„PŒķ1Æ"‚@d‚6RH­Á;ïd’Jzč°aņ(AėļtéŌ Ī”‚á Ũ8n`zƒ*øš~äŊÖĮäOã… EÁ‘§Tr†Ą`\ï Æ=°Î!Bhk­`—B ä`r”JĨđ<§äÆYŊ/nĩa$ōíBāóīó*1"áe yä‘ĮfHĩB)ÎËÓĻŌëgŊMaŠ’R‘sQq ï)ąPAimáŌÖiþɟX)2d k“yÜ”B†Ÿ˜ ú۟ĢYýŠYˆŠrY›#ēĄË5Ų͈Ėd;ä\†Öã ŋMĪS€ā›ŦZkoŅë[Ū‘1^) BøiĐĪpä8HLėã…‘*^ €0dĢ/%‰Ž—@ĪŌiĘũúōČcģĨZŪđœĐTEŽĸūŽã1@0Ö°t‘€b(n=†đÜšnáŽsÞ?›G§zöęÕĢGļoÍfãÂV)ĩū #ÆwąUE&ņ‹ŠŨ~SžmÁØÃŧœp|ÏSŽėqüޝƔŦē—ý–"aà .?Ĩ‚ü^8kžW˜ËņŪ æVŊ Є!°1y·)Ĩg[Ķ*EÜdPJ ðģÎނ‡Û/Ļ’"g„<ōČc3töâ2„”\yÏY°ÖģRĨu„BúÆ%o§b‰'Ž|‘ë'zķnĩõČ8Ũ+ö=ˆģmŒ1Zë6ÚúN·+aUEvÉWÍŧ9öꝎÛķÏ`hƒeÍ ?ŪzĸÍʗ*Â55xYĀČ:-!$ ——íÐjõ%|\‚” DŦ |&˜āRW`…%"pÚcEr5/MÉÍkŽ”:!_c‘X„`}þy ķ­Č&>ŠQlgó―‹―Ögī.ū嘍ð2ĸþYß~2ˆÜri?Pē1î><6~UËZ&ķī8Ĩ5{žFÎQΉ 2F žô— Ȓ”"Š"J”’äŒ#'ĪäXŊ;”wčÚĩ›ßĸ™?þzí"~GŲĨļfunę;uŋÞãÚg~|ĨįŲķčS0ðļÞg\:ø†îĐnEūŒĩ€_}ą67c؃#_xxäSÊF„.lŨķ~ȘuĀE:‹ÛŽ”ŲÂ$^ðĀŪ(@øĶ %OŲRŦÖLX·.’] Ė"@뜷\ðãŋD~Ö9Ąåx‘žtâÆNðJySíVpÃđíbðOý?Ü’öíE―PJ[ßÐYÁE!ƒ@ĸãSėėūû Čãŋˆ<Õ{Xādō6ēJ /œóUž/ë {'úVŽcĮE–@EKā,I•\ĨŽC§![ląhŅĒX{‡‰­ßkÓŪývƒ+ĘåÜī*Üî˜3wûqZ X‡š–lES=ŽÃð’íÎíq‰,_iŠž_{Ü =úôP8īcÐɑkŨUp@–ĩ\›ep“ŊUR%äëŦoô=YžGˆí‹Ņ°ūâŦsëÃŋ{ ĨZöÅÓ=ö…Pō;܉˜ô܃ž4]ōÁyäņ_Šj­œ%)ĩΆ‘#Ãŧíū8õōĐx™Č“ËđHøTGrˆÄ ÐĩkŨAƒYk},ŪŪzŠõāŧÁÖPUR,˜ZÓÃöđįįJë0§áŦģĶ>öÁ^ôÜMÍ&ÆĻŧÔå°—ÄÚú0j.Ōo8|Ŧ\Ā „ģF°ÆĀëën2 #rN%ÜPA:EÆ;ÛrņČG+æ_Dðŋ%ë§ę‚k­me†Qd@H­īwģåjrĢAÁÚYŊŸzÔĄ?{hĒÔJŽ]ōÕ[ÏžØ"eýŌdŅ„R`FâāúĨÓß~öÅ&HžBęĘûW(.ý‘þD!ĪŊŲý@ V•Ž>ųãĢģWÖų?…f>Ïõ—TļþuųĆ%Ýóøkõ!ü!moNKüâíGũę”ÃN9i(,|č·7/j@Nqþ&ãĩЊiáĪßÞýÄü:Ąų~ü㈊ō뷞zsĄV:hsY!•?`}V"§ŅČ9ï>üčëӄĘ7Îĸ[ČS-ƒt Dbúįœõ5(Jö™Q،ŠcKĪs6†ßãŠX™Ŋu4Cē2—Č >\J§7ÆĢbÖZąíúql# šfŧ|~v§íöíÛĄ;ŽCmT}ßâÛ*ÜĖôĩ·ŋwŨï?~~―?ýAݎč‘îBÂÎ^ļĶ[‡nÝ;õ†%kČī#\„„FŲ·Ö°ˆ"ā0G˔ÎĨ•ä9…\6 ÖrrŠK}ĸNã­jx›ËúĄ ųarÖÛĻcŦ…Œ—hYP&‚Ą ~Â;,XÛ2õÅ?ĖŪG)Uā#žPō<<âĘų3>?þ›:›Ę$O)ĨÚå'ŒĸėËi9RŦ[0ŊŌ @ĘU,ųš>GĄŪj嚆lõŠ•u ÕóĶ|ôéœå þ7S ĒÔ.[ũéĮÆôeUh%Á…KįL?~ÂÔY DĄ|3cō„Ï'-Đj)čP(ĪÛ4mŌÄņ>›_Ņīž mÓڙģW– Ų―koÕq‹ã.žtHæš*ŨÖ4Ö|óņĮ­ŠuÎ ÚųĘ NRŽĒy3§Œ?þŦÅՈ$S%Å=ŠW.ž5áĢOWÕE‚ģäjW͏ïäëUĩJ‰„ð]óĖO&Lœ6ŦÞ™ŌÔs~8<Õē  uÆ@PÚË Ž)dÅū!V81%A ?þODüŽĒhþ4aâŌe#FlkžžŌËÏãŠ6@ąa?D! ķ2ÔĒpČ í  fÔOžR?ĩPu,ę:ĒüŠũžXÕT ŒŪĐn;Ļ1 įĩÐyĸÓwûQŋâNĀ(ÕĨĨŠœudĸVÏē0ÍEzC2O‚€˜kÉųzÖGē –ģ"ÖøĪJN">@8įëY(|í/Xĩāŧ―A Žģ(Q Ąīō‘k’ÉÚ‹°q Īh\2ãÕŨœwÝoš­þâųfō$ ^īUO=á៝yōMŋýÍ}?6WĻ”„ŠŸ_|áŲWßôŧKÎ;ã‚ŧ_X―|ĘŊŽŋlJ-Fߌ?áG?|čÃeU=pų…ï,Ū|æâs~úóģŪŋņēŽ>ë™/W Įņ‹ÔļrÜ5g}Åõ7_uÅ//điiĢÖ.xðînŧíÆŸžvüŦS—)õÕG/œyÚ9·Ývý žū,JĶ^ũˋ}Ûmã^šļDiå?H5VÎyõ•ũVÎųðŪÛnŧõŠkŪžōî•Y•ýæËŦÎ>ųôKŪ―čǟž|ĘM_ŊvŲ“oøÉĨÍļdʋŋžðüÛïžýĄŨ§ ‰JĶ—ÏųóĨ^þŦsûŲuã0Ļĸþåį]zëí7^xÁŸ/oÖTĸøļŦÏųųewÜuõmo,‹RųpÚĸ&ōTËąÞŧ kl†Ü aŒ\ˆZiGÄõ)zžåzV3g‘ŌJ)M į\į.]bĨW6›ÃqŦŦŦ•Ríx–ŋûöæ†7ÄÂÕđēðø!;AL\û^,pDJbyĮTSQ[ DYÔ<āëÏŝßzý~UĻ`œØû§wnõč­#~?°h˜ËƒĻõÓ}h"į{ē^ ዉS||ʎ‘Þõō ĸQ%ŒÉĐœŨĀ ŽŒ1ÄHZØ’Ģ(üĪŊBĒtncšįKĪÉž4:œ|֙§ïŅéĩŽá°€ n™ÆEw]2ŪÏ1·ūõæ_Ï=vL/g‰ėÝðYíčWßzãÏ_ŋäųŧ?XT:Žĸ”Þ[:oüããg,ûä­O*}ōöŽÎûŒė\đzĐėąï͏ŋsõŽõoN[ėĮJR˜úÂïžRøÜKýë;–N}ęŪWĶg:õ=ûʇž{úąĢvčōŅgÓĒúEOÜõčŋļãÕį_ļöÔý:ęs ?|ŋÃķ‡ßĸäCį48 ũ(é=ę˟=tĮcîýýý·\r\wģŽŅ A-SŊėŋßÏÞ}ųŅ‚?üj‰ģđęšįÜėÏßŊ.~ÃýOĸîôŅ֒3Øēžō„ŦĮ―úĖ]Ų_YÝTûÄĨW~”Úã‘GÆm1û―kŸTĸõ+·?8ũŠ—^ûÓļ{OÛŊŦČ9@Čãŋ…žØË0y–"$āq[_|é@yOHÖZŋDÞ!SĨŌŋؒąĸÆ˜É“'/^žx#ƒgaO:ÉÎ2AĘ™íārUQ§ h`›î­Ę­”Āą‰J2ÂEMk›j`JËšĘTaRÐåA—ø ĘtđßCïöāœĘ% !zÂMøÔđlK‹T*ĨÓ\ÎZÉāđ[ðžß>uĒD?<ĶīōÅąŸôÂȑCnj’ŋķ6ČŲ—OŽ.Ž~ũÕGjē#îýÍo+*–Ė_}ķĻūŸ–öĸËV-ĻL0"^ķU§?†Móf/ķį~EE}†nß-]Ų$wŲsïž{čŅlÃ]7þjÎ'/ĸþ‰†ēĢ~Ý' F>āĮ?čSV˜î.ÓbŨG5Ŧ+ÃÛu/ PũÞ{ĮÞũÏ^Z[ ·ĸėōÅĐTKíĘ­·Î4Ž^øMuã‰ÛÄŊ з4‹N§þüĘ)\sð/wŅo/b—ŪÝbžåī1í&g1,Ã/œsč õv‚Ą>ëj›LŨþà r.Q„€ū,iæW­­ØąËĀÞeL{sįþĢví6•ÍÕÆšŌ Äđŋ5ĒÃýTP"é/äZ;rVĄĩBg=ÏēĖ–ï 8īŅO=%œãŒá@úlÎŧä_ÓÏ/ø.„ r~ÜCkĩ‘v―QĻĩsÞû˜­oļũ歋‘)}ņ›oO>e°Ø“!*î\TÛēp~ķ)ýþŸ^_•ļé 1øÃ oĩ\žsÓ♟Ŋl8ągQývnūáøg‡\9íčÃo™ō‡Ŧo)~yÆķĀQ.Ėy•˜h―"Y—*ëÝĩÃŧÓ+ĒŌ‚Šw>[―óý&ýņÁũ;î?éÉs―ðøyY*éÔģk*œ2yæþýú}üŅĮ•QoŠZŌÝķzáÍũĸxãIŨ=8îÄÃöė*Œ%Ŋc[ĢŋĢIįLč °Ũ%—î|ëcŊ,yĸÎýÏûíé§Ŧ(‹ČXÛLä °ĸ°îƒSc^~øŌ ˜æ&WõÅxZôþW +FĨį―=eĨŪZĶūũWŨw·­û”ŧïßg;|bŪâŅˆūj#ä]…(4ņŌ9Nm!‹äą^Š aéCb@pĖL.TZWTVÔÖÕ9įځû–nL7ÞS‘"[Ģ]Æĩ—ōp9įrvdŊ!ÐÆæ"gĀė@hR|]]ë°ey—‘;_ø‡_žļŨ/v=â2`<õÍŸVR”ÕšÕi™^ïWF §•Rø‰!ĨôáŒ2^DÆQäĮ Ø Pp Ž1ÞրČår†7Æąa+‚ðĩ­5Vø‘eįuĩ $Fačũģf#ųK-?úӋÝ;ãÄývÎ ąÓé'þóW^ĸAÅlÎ+QkÕęŽMN— šāâcÎŋæä}žéÞC‹Â’þÅΚtÂŊŊÜwïýlSý#.Ûŧž°&S>ļPe_}äĶG_†MqĮ”Ģ<”.2ÓÚŌÚ§zH)‘_X%ņÚĸ+M)˜1áÉóo{3M ;ĸðī~ŰHu”RzxąJOšųæđ\žĮîo—HęóãË:íä3~ôÅYGũž―<1rđ­JŧBŠüÖƒœD°ĒđÖ!- Ō: d3mˆŽ‘Š+ÍŌZ·ZĢ(  ÐŨ°Æ€Ģää46a_‡\üúŽ-›ĨcR–ĨŌ)r.Ė”ī‘%DÁųQĨ•PŌ‡„ØÁDBÃÆmŽęɄđ~ußū™"—ÍæĶšâƒĢŒÚv·Ģ Â\ŲnĮßūýQČyÓëÄvĮ\õž§­mĒž=ŧ`dŌ.§{m{Ũ}O._Y)RĨ―zuKQÎöŧæĐÏP„þāüqœ/‹”Éå /yúe™)ĘåƒŊxæYËÉKŸ~%~Ä(}ö Oēte$tũž―‹SĒ|·#þōÆN QŠ{į’šęŠ‚.eúûŨ­k4ēKŨÎba9ė„‹vS#tIž]D:HąïqŋÜí(īđ\é°17ÝĩWš0'†pÏoũÉÄŋ xðÕ'tĶ ]ïų힙p{žü䐃"JõčÓģĀæķ<äŨόI‚āŌ]ķŋýÅgŌéŽ.Úî7ũ?ĸMÅ‚ ĪSgŦ ~zÍãö(ęØąHLåręĘ>€øeōiˆß?ōTë kę$ÖÆJ)$*öŸZ)ï!8â„mĐ9h–[·&H&ēė‘1ÕÕkšõč…a[i—Ÿzð‹Vúg{tÉR‡‚ĒŋŋAr@mTąÉUŠLîïeOþ~2B‚ÔĪDŠÝjE:Áf‰đÐ9Jé€ËRc§óĀ斜Ėą_—wKȅ‘āaoƒĖБ5ņ‚ß+­ÃIH·äsŊlsë(―ąĖđÓĨečÏü#ŠtyYšR)ŽnÓéĒ /ÚL-‹ŽÝûvōĐŋjƒÂēAC:pÛw›1SX Þé"SXėW€%ĨÄŦ °”/­8 Š ß Áü ~nYtéŅŋ+Ó…ÉëtíÓŋŸÅ‡QaIĮĨøŒÖ=t:ð(ƒĒâTōœ Š‹RÄŦâŌbū†(. ˆHĶKú ä[eČ  ”DĄŠJKýfŊ.(0`Ý1Îbé7hˆI~ŠĘĘōšyü—zĩÜbïŅe- ÜWwûĶ­d'ZgɁC"^ón{*“Ō2āîĪc žČfģ Qų­üķlÛB$_@āŋX@üCĀ-ętÛČqGÎáߕŋ`Lm PÂwą°á Ÿ\˜“1Øö0 Cp” ō%'g+YĮÉ"ŠŒ ũŠóýāû‰XúÆV“RkGÄÖéĘ%%Ž!°1QžPIgacĄ=GŽįē ?íŲ°ý9Œvī_mčA"ÚðEýĸ0ÅKßúĒ~Åo_lāŠíWÄø–ß?Oģyüũs…īNõĨ”ˆŒáÂQ”Öï'ËF_ÆÛ°æ’Ņ)/›ŦļVŋŦ„MTccCKKsŧŲ0Ïķ‚ÁðƒDüh Zŋü34‡đõ<ëÏšïĸ _Ęų„4NXðv݄Čūāh"㌓JŲ䆜WžŲ„[…äŠ\6gÉ)íõ[ūW+‰•š‚Õk&Lþ4’}ÎļŸā3" 9’Ēĩ5l…<ōØüŨÕĒũˆá\éƒÅP$‡g„äņJÁZ}v wū,uÎ —Ęķäœĩ åēaķŸ c†”ÞČŅ™ä+ĘQ”uQÎŲŽģ†€Ļūđčo%‰D!ĄýUÁßS­%ÛŪT!@ō]aä:ĻĨ%'xÎ9'„TĀ4FiĪŠaã/ŨšœF­oEv]ð8ŲČ Į›y‹^?Īë tÁ­m~œÍjx[Ï‚1>RByl†Čũj‘HIĨŲŽØœŽ„`Ŧ@ïZUgšr6„âīčYĶŧëīFČcSBžjuūž%*:ˆ&4ĪS Āzï`QŽðžŲ^į_W_7k֌‘Ûlãúi…˜§|ŊV2‚ !ŦA –ø+†aŪēąö“Ęy˟špúøę\UģÍĨP ĄZ,@FÖÕ6īxÆØôŽ-ZqYoY8âӚ/ ĨīÍ-NĻtyĶ$† M-MŲÅk—ÕŨ5AÖ44ÔŊŠ^S›k,VášÆ†ŠÆ–ī$#Q Ÿ’hĀąÅÎčīH€čoÕsš'ÜxÝĩk—Į”f$,#ŪÐïŧį?ĩ sĨJI›Ô°Fi­R7s "Ļ@óČ(!…ß‚“\E đQęŲæČ}SÍ^ŲēĻ2[[nŸŠĨKƒÁÝŌÐŧŦTžmóØīŠZ™@ø=œ ˆ;°\Ïķæēg6cŽąlį* ĮÁ"ŧšœqd–,XPUĩ6N#ĸG!­·sĒlá*;Ÿ8bß ö:ziíęWfĸųˊŊ&.ý|ms=ˈÚĻųĢEģöė=\(č ­Ó#ËvÆoĀÐRMĢu™ō’LISssu}mm}ÝŨkÖ66В.=;õPZɂÞĢĮŪ}:vKŦLJZ*2šƒ(æķĘĐ@Z%ÐZK)=™:įĒ60ƄŒxžØOŽ}þŲ§óŋž“JÎ:äq]GÎDF* ČZ'jÕÆDÎY‰•ˆü[‚–lđðĸŒläVÔDsV6Ï[TæĻ(`ŨþúyđĄuĩ•s[B-@Š{Đ$B›ō―Z@œ"-•#ŠĒP0ų:G,üЇq‰@* €Lđ& )‡&’R2―víš3fôéÓOŅŽjÛýč‰Y2.HģQŪwũgmwzģi^\ŧôĨ™þãĖWëÓ֙ėēE3*ķ؟ĪÐ:HĐACwë0zbÝĮÐ"WÏŠ:}—cšdĘbrŊŽ­ZQģrŠϜQœqÓAwĀßđ=ķôGÎZ?"ÄS]œý•ĢŽw―Éårqõí}püũķÖ mä ä·õâ}ŋéÓ§eģÍd 刌ĩ’ ļīuˆ ĪēÎX6Ģ%ąQhīV(„q7FųYÝdįŊ g/Ū Pėĩ[ïNåi-Q" øŨdë]þ[Ø7ãßûAKέ^ÓôŲäeģ–7(…E)ŅĐHmB]ę<ō―ZN…&"k'w;âOÁZÆģĄč󞟁֚ŲĖIž4͆9gíÂųójjŠ;wîėĘ3lûE›ˆļ›ÍesaÎ; ļĸEŧ]€Ü6ņũ%=Ō_Ėĸjâ’Ųû§ķUZĪÓĨŠė؞§ĖmX:qō’ýúpÁ>'e›šÖÔUÕÖÕūęó9ՋFũuĖŧ7T7ԙˆΑ(īšécƒĢDRJ_n·3Ęņ oq§Ĩ͝5ӄFüœ‚ÄzA"8–gxģZëŽĸ“Æ_ay?oøFCÖŪm4kŦ›wØšwaF™œqâŸ8ĢŌB+ đÞö[Ũĸ)d{2küŧÔŋĮŊZKĮ‰ËþžþÛēŊOYIŠS§’ekzv)l ―„/<6g/!xŠßYįEĩƄĀ3Đ­.*HHå‚{ ú<ą”Jr·Ø~I-_ķlá…1Õna7XįcēŲŽâΜæ\sAŲĐįĻBzŒĘ@ŽŽíģ7ķîÜGHYRTĪRÃJķŧ~ā=_-ÛoĪmŽ–WŊĻŊkX^ģ냊E„§mDšĻMƒļj!€H‘žÛúûņ ĸxûG˜ˆãw…IŸ}Z_W‡Rpį9~ƒīf&&ŋCč­,§9ģÞēÕc€CŠāĸցqf­TēŲĶ$ ؞ĄPaóô·Þž1oYqũ-GíąIӗ/ŋī`ĸ~Tš‘ĸąØŠzņWË+ęĀYЋŧôØ­kđģ҆’m%ņŽkû:6WĸõŌeĨ―‡fÖĖýë_?ÝþļSûĢuô/ĐÖY-I-ÃÐZ>%<6ŠÖoրuJų―/ƒūˆ^ ūa&P’C"§„$ĄØīÔĻ”– ŦÖŪ™=cÆČ‘#ãmz`ü#ÃúöBÛcAkžÝ†Ąs.g{ï8ŠËð Ë&õ+™ôõ·ØéēýŽ/­G‚”RjhĮ>C;õĐŦŦ[]ģĶšĶvųšĨÏ-~~qíōÝûî°Wĸ―šęšýeÛĨÄŨQĄ?`ýlEŧcüģBˆķŒüõŨ_ϛũ5F„āo €““ĸI­ĸé„üæŊ=ŅrŊQÚe]…O kÃČå,:„öŨEāįwžûĀKËķŧË7ļû|ļ‹žũÚëŸmĸĢ# ĩģÎ%”~—M`!ˆ­t8‘>ĄäíÕ%Ų/žųõƒïUíķËŦgýeyuéAįÝuÄA{ 3Bh!øŠhϙ^Ôoûî2‘qzoʔ/kr˜ãûoĸåŋ "Ę· ōØŦZ?oūžå~Ĩ’ėŽ  HšŒÜĮ€Ą‹$Ëô―!—ˆ‚ĩý(Ĩnnlš;gÎāÁC”’DômU-ÃŊkkkŧvîš2kšÖÔ6Õî>htŅ„Įęšëī],ŽžðóœīÃá§ėlώ]›ē-u ÓV~õûy6g#4зīۑ#)ØuíúëáïįÛŋ·;l―^bÞžyģgÍð^9.Š"!dý‘uĀ28r­óuŲ0”ˆ‚ÓwĒ0B؃Ęēwĩ€ -d Xō~<íŠZē"UÞŊó;ï>õö6―wÜf dmÆÔ}ųômKŦ–ÏŽËô?ũķ? ĘTž6îžũ§W— UÐoŨ“þãwšĨӘkÆlM/^ybí.WŸ{枟þÝŠÎþðˆýŅD  "c0#K|Ę%“ÆúåüUŠ+―ûŌĘ\Ɔ]rÝíÁâÉ_MžĐyX•mqÄÞ%Ŋ>~ÓԊ\íÚĩ‡_ũâčÞÍŊ?}ŨÐ#nÚ „q’Ļ[úÕãŨ\šˆR:öøŅÅũö/kßð?8‘cųMyäuĩ~›+2‘ûŨšMg 2Œr(•ĸ’ü’ģÆ'<Úërbšš§Nþr=ũėŌĨ‹sŪ-ÃþģōØ^+åvíŋë6ÝGkķč:dXųĀkŋ@Ĩ[ŒëØĐ°ĶĄâæ—ĮЎÝÏÛõЊúšUkW›F(Č4æęČØSv:ĄOYŋ•+Ût6ŒvûuÄøĮ2Ö/Öwikjj&}þymu’Ę㈂Ā;Û:™@đČXÉB„ĪYL­FļaČ}Ndð“fÞ6RĀErLĩ ‡tYŒūāžú{ŪđũĖß>ü'sn fÅŌN‡\rÚõ>qĸߎvóuzÁeũüĐwÉڏÚãĨIû .U_MžžEFMúdb…œrÔ^ESæOąÛeιȀ0K(ģ.č4Ī_YŸKĸĀÂ`Ė…ŋ>öƒ;ÎzûíONýáØGýđãąũžßČ([ŧßYš·ï8sú—Sķë>0[ģ6ŨŲ4aåÂ/ŋœšęð_=Ī; j‰þY‡Ú€q›fU›G~0WiFQF*.a…áœ'k–„ná!§ãX™*ȊQïPÃY[WŊZđxņǘj…íJËVÞēė?ÔZ§L:2‘stÖî'ŽŸ5AĪ%4X' 6š:gråŧVÖUUŽ­ ›Üɝ–ØeŲtóÁ[­Ū­ölËķĸļĮÕŪžFŧÂö‹âļK;wöLĨĨ1VĄ„ä—rĀ.9É|z9[C°k8đ0Œ”’%ĸĩœT Įˆx―ŅÆTÉUgÉ6’LĩĐÂaŧ\ø‡ýõŋ9ũöÂmÎÚŦŲ ŒēnŦ=úŊĘęĨŒŠ°Ð°dî[·^YŸÉ„õ+;ũK­l†fŦŠsXAØh`U=?ō þõšÃßðãžs~ïōô†ķžļŠĩPz;Œ<ō؄ĻÖ;Qų°n?ÅO ŸJDˆÂČđĪ éüðŪÏč&bįïVÓ(|ާ?ąŊŨWS§n·Ýö~ČõŸTĩíĶŲl6N{į…\”ÛķĸöÃŧ ŸU1_ ŲbDĐĀ EģĶ9iø,E°rMEM]])•oĐKķŲnHĮtyKSËzŠô‹õŨĸ–fÂŋl,xáÁ”)“ęü4Áŧ>NK%Ņj‚Ī”ôí?€f#’BōĀŪcŸ_ŸŊņ“MĄ„@Ä@ -“ oHīÖŽZ]ÐĐGßÝvÐu_ŪZ­īÄHH°JIî6]ÖŦGúģéÍ9ÛAŊýzúĘÎ'öë1bpáw}ņ%þôĪÜ―ŋæųÚmN|ŽG1DÍČŨJXr€ęŋþâЋrÅۏ1pņ™ōūG|óEĨA2i —É 145EÅ0ũGgv<ðڛϝxÓąŦ,Ķ!„SR •- ˆķ8úúm9üžcĸxŸqð°lÖnĻŠ% Ø|U›Į&Gĩ>”ÛÛuG‘qŽ‚@°b”đÕX눔RËČįŽŌZĒôB ÂXƒÁYrÖ-œũuŽúÚjŦ­ŽĩBˆ ÖģíÏår–jýpAqŠpėČý'ŋ°C41UPTœÁē,Ië%‡úÓą ~ú‡sũΈæ’ýɘĄEÄNĢ:~XŧįŽCKë:ð…ïîyÄîĨÂÁRUÖ)―ōÝûî;õ g:lđĸŋŧd@ũŌÞ'ßPsÝņũŸúF&p#OđåÐ1ĢFí>úŋ?'ZpũûP|ųuœóIũŪ™ïßēhÔ E…‹2Xœ’éTPV"ÔŌņ/_{KC‘ē[šßý 5íŦtŊnÆČ@­†rG›\UËĶŨ*Šâāūđ„f)ЊC>―‘Ŧ9!Īģ6ēVĄRy7q­ĩp@€UŦVğđ·ÜrKï6ÛNÝåĸČŋ1ÛAā” w°}ąHEĄB†ABįĖâUSïoĻVÞŧ$(0ÖėģÅčō’Ōú†úv:­ ĘđÚ)gÛķÁĒļĄąņӉŸTWUITRóŸČ‘DŊu‘1BBLnQŦ@jÉÚ/Ô] 2"ôö`\ųâF5áĒL€Ei øh l/‘*8ä†ûvŊZYŌ―_IIĘEû\qįžĐ ,<öþ70UĻĩ<ôŌ§w[đŌ ]ÚđW:…ÎÁ!ŋyûJeu>ðš;öž<]Ļ(jÏģŸÚņ”k )í” ĀZ*ė7äÄûÞ­ŪĻ"ĄŠĘŧĨ…Úņ„›ï_‰AĮ oxzĮ“*+/jŪŦM•uÞqÄvB§%îĸӛũÔiÂcöðĻ–0ĖtęY\"Ēīޠ؋B)ßËc­j…ˆĒPk%ĪĀ( ―‡Ē§"äzÉđĪ*SBų],$œĖÅ2~ŠŦ<"$JĩdsÓ§NÝsÏ―bÁŽoM~)BLĩą…w0(…ÛØvŦ^[~6J:HŽwD‰ĐA–*ūŽ_e­+PâīG:ë6Ø hĮžíĸ. !ĂųóæĖœ@ūÜrˆR8g8= đ?ëý| t,üJąãđWĪbōũšQŌį°m,›Z!!Ķ )B Ú3;uíŒëm^]RĻÉ_#SâI Sé’!Öŧ ðYE~”žFðāg;t( ?‹AiŒ6fJŠûŋtqß>þņŽ ų9ū&_œL•–ôjwĩķ ō$%)aŲ$h“3|Č#ïėeœ”B+e ÜD!$[ÉÄËĀģ rĢpĪÔû X”\6úĀU­y&hÁ‚ysįÎíÕŦ—1æŧŒyv‹]]’Í}FYqŲAۍ™2Ķ $ˆ…õSāÜū#ö:l§ÃbĄØ?Šg=6ÜRhö’ƒõwwi?ûôÓĘĘ Ĩ•ģ`LBø*â…O@K§ƒ0Š$ âz’UN)řÆé r.4ži&ņ“ÂaK§ ĨiYZZT_ą:čSZ\J"§[üOšÍø OĖ…6„MkŨt(-*M‹ī›ōȧ0`kÉ㎀(ąĩT’‹2ģŽ‚˜g•–Ž­ŊŌĀēƒ•āū.HĄŽ1S§LÚa‡JJJÖŦūū―žõõrlÈŒÆæĶ1Ûî3îĩGŠj„ó· Ĩœ#-åŲ‡œæBnÛÆkŧ-þvC_þāo*ó%íœ9sĶ5Õ?Hžē^b,Ī`áã‰BŌ•Z8ë’ûãmF?Ę,ĨRE­ R)õĸþų·žPö+ŨÕ=;.ZēbʧÓŧõčȉÅÜCøĨZJĖ3ģŦWT‡đhDĸŌ>tqJä ylJÎ^ŠÕĮËĄD…’ 9dýŽ7WZE‚Óž‰€#œæ] c ĒðIˆLX‰ėīđĨiöĖ™qøBii"ĩĨÚv]í [ĮîĖná€nwéũYÕâ`Z@ô.Û62; ÛnxŊ-Ã0įÏúvąÁ Øoa[!Dėø5ųËIĩ55JÉ(2’ Æ"cŅÏ…đȑÓJqĒŧCŽžÂúyƒÅ (…BaĒ“ÂPˆūå:2œĢĨ+Ŧ-]°\‘RšžCņ]†ũ,ęUĶR 6䑟CDŪF­.ŨˆBņ‡_ĸ —ÐĻôÍÜ(ōņYāĀĩŽHIŋ―N”Ö‰ļķrMŽú8hP[nõëVÞz3C­5û0 @ų‹COûdŌ‡˜*"%Hˆ°9ũ‹ Ï.+*mljōgĩŦ[ĸŲØ‚snƒíZh ū“XA1}ÚE$P(-sđßŋðB+€þ FiœDä{QZr FÞd]kDˆlĢW+–ŠÁ]AŦNÝš”5n6) %Đä=ĶoyP”ÞԚ%yäDäS°œs>Ė[kíÖõ ę4ŽCd90Ķu07!h°ž=ý܁”"†ąų ÆšĐS'ï·ĸþ:ujËhíúí`ŒYyFđ]†íšCįÝæ.XĻ•„Ą%ĖíēÝ{î8: ĢķŒųmĸØ7hÓĨý$.i―]Ŋä^ ÂuēŨswRÛ  1ī "ņ3‡ģ Amé,Ą‹wĘ2r‹ŪĒ{‰ ŅæAĩi-ŠÓ"Ģó<›ĮĶIĩ…ģ1œØ"žSPJûŲ0Ÿ]ˆ„ėļJ õXK<Œë·ā}{”§"ėD ’œ]šdņīiÓößkí·[―øuÛVĐ'ē‚LÁ˜ŅûΘ=G I@–@*đێ;ĮķŠMþ”o•mĩŊaĄ ÚžÕæ–DėK;sÚī0úiã(átÁŪĘï ú7˜0 Ĩ”J*kYB_ó#•’&ƒsŠĖÚØ*aÞH‹SXž9ÕwÔŠRČ#Mj}Ā5†X―ÄE™ððÓPĀĖb­UŠ^ÖĒK ERîYc‚Tā?û !Qp2Ģ™1}ú.ŧėâmÛģĸĪžõ=‹ ĪÜiÛQéT†­rYÛĨkŨS:>—ËĩĢŨoũ=ø—úŋöNc}4>Nœô|šÔģ)īßÚRR)%ÂÐø›wD,―Bnm;GÖë ĒΆĩņĘĸe€ĀŦn9ðóԓG›‰Ēĩ.Š J%ĩ$ĮԃB§RœÕ͚ÐuÓ\€‚;’ŌĐ4&ðS ū ŅŠ5&B-9š<éóĨK—úŲ>ūíšÍw†ïĸúsa8jëmFŽNÐŪC:óØ{tënŽi7ąAïDŲ šÉlО\J9kÖĖŊĶLrÖÖ9ā:Zēô˜îD‘ö‘A–oð’8 ) &QjÖ %…žĨ’,ž„|t@ylvTË@bŽäÜúöŦõ†ŠJ+öîB ­48oļBŠČpį€œŌ*ʙ@‚OīŸĢ-đČD•k*ãÂÖÝâ·Ēí֙sNk <\ZRzāûfŌęÖĐóĮ›ķīÓëoÐpö;Ðkûöq,ė…ŦW­R;/ÝēÆ ôũ/âĮ5āœĩ.Đyú? PrŽóW x$„uŽ'z‘S$Ŋ7;ä‘G~„œuÞ-Åόę@ÚČ—lôPh•rdydV­Â0€2†ŌI +Aˀ3“khđBHøÅgŸ0fLϞ=}Įö_N4xŠõ ?ŨSísŊŋītųēcxdũ.]ýģlīk)|ŧ&ĄÝaJĐ%K–|6ņc­$ @"TB9°‘^øˆĐ ~9ËųÁBZkÐ:@”ZÛ(r,žĒȁb] Ķå[gǘGylv&ŠJ;ëHY–~ēߏų įgHņ.|Â2>=ŨĐÆŧÜĒā,“äL)ąŽŸ:Čær‹-|åå—v―{ŋū}ËËË}7öŸI`žÓkCŪaŌē/v4zøāĄZŠĢ9\+• à Šg7 '`xj^ĸˆ_·;KJKĮ>þh|C}=ē B „Ȅ΁ũsIčœlĩÓ_„ÏwJHTÖū!73ąŸRøQĸʐGyl^ Dc%Įji‰|íéČŨę @ÆF”JiþėLRÝ:Ēęk5ˆ9@!ĩBgY1ë]ScýóÏüņ–oļûŪ;?üðÃÕÆØ „ļ!Ž'A­tuķæŽoŊkĐ=hÏýũÚqŨAýä°]+ĀĢýƒßÚRØ`Ûaq,<˜1#.Ē9•‘Pc9˜Ý +ÞxÎ%rĐ åœļk,5&aRŦUŊ…(ŅZįýjÐWú›iU›Gy(6Ïfs(P&ëÄÄV Ð:päxüž‰—Ũ7 )Ã\HÐIēAļßų‰Č8rJjk LĐJˆÂ(Ž._ūlé'ÆũîÛg§]vÛvÛíbd2™ ~Ėũ•ŊRŠwiŊĮÞ_Īąū~ŋ=önįÕŌŪVýv/oŨ?ŌnŨ3áÃ/˜ïģ,…c=ÏŅBž„ÎXã8·†ˆ)”&ðÖ―ė—(B’# Č:ïūĻ”ö )d uūŠÍ#ÍŊŠ%R ģIBLsF)ĐD&ŒĀ‹– ŧ"‚åĐ ””‘ģYę”Ä 0ÏBža„LA”ēĄĐ~öĖO?ņØí·ÞrÝ5ŨžþÚkqėcEĐTJlČĒ4PÁÎ[˜œÍĩīėąëč0 Ûņl[’ýŽ _MK†Ö:NĮÆ ģf͚O:ÄšŊð]_Ļķú! UWWïŋßþQý3SŪooø4ÍðeŽ1&nÄ!įŦV­ŠÍŋ§NúÉGUŽZĐ$ûðÖÃąc$ņͰpX |‡ˆŽ"þĨø%„Ïdã_,Y!“Ö7Ķ•VHČČ#<6ÃŧŽ―"øÁ)”#2I‰JęČßUhõKäÜuUBfgkL â%J>‡ČiĨÁ$%Æ%dŦß&ŽUĩvMeEÅôŊĶžøÜģێ5j‡·ÝvÛxķĩÖ"&§ärđ=zī‹hlįSãÁįýí?ÎđxĘ6fęFžðūŪ6~díš5ņWCSƒÉ…MÍM--͖™ÝZƒ įJðÍhĨĸNÃķ "ē‘@žMáŒ#hŽëČĨR)ŒLē”ōöá@Ļīô%9 Ø‘Gyą—u­ĒWÃLB 뎧9­&_Ķb!Â(ō…' ̜HDó4 ģïĨ"°VŠOÓ g Y)ą brA:ĨLj9+P&2ŦVŪXýúĘņïŋ7dË-wØa§ÝFîÛ·o‡Ī”Ųl6ķ wý΃‡1&b„aŊc:nŒ ãÆÆļV­ŠZ[ĸMō―đĐ)ÛŌĖOÕŨÔTGđHHlŒåū”ģJJĐ*DŅZĶR1.ē‘/ýÛNāĸĮÞŲŽFQ8õŨ­;_AēÝ*îÕ§‰āR*D ļ“H’1$3öÏ­žë=UóÓ'$4$ē:TŠN}…Ä[ÅØ9ĮøK”3ˆQó&[ˆ™EđĖŽZ―vÕŠã„(æb~þ{NÁĨVĩ įÎĨÔģ0TÍ™öĖ„ÖmLŽÃ=/szqÂģŽ:·ēčŽXÚ jđķ‚*–$Ęæi?úøíüüėÝÛ'OŸ―zýĶ({Ú[>ã8 ÃÚÞÞÞýÝl./___·*uøpØmï[õÚūÃAӉ‰đ8Ä î)DXMšNé@@?ŨOðRŒģL™%•=Yāq‘Nžũ1&B;›Å{žfęŽŊAKWėãbĩYå­Zĩę8ŅāîŪfL°ÎŠJ™‹WG>A:8#ŨĀPIkė>ĖĨĀģúå^U1ZĨŦ&\ąx6v‘+.B]Ũ—2-KU39,ŋšĩïƒú +ôŸ?ūĸšļøðþėņééó/[PáfÓ>nŪŪþ4ĢþÛÝý8ŽŲ&ð2OãäËŌuæîÂĒĒMó<·ŋ°ïÕâŪ þ~~qM‹Ä~m6wQÚ#ĖīH0ÃúĐ,…N ļöėÔÄøtGē˜U™Ā:ÎöKÄaĮ‹\‰Lu.Ļx͌)9úzŠå(ĩj%{-1LįŽUQ [ĖĐ’ĪV`°§8|`pÆÄxsžÜUIRؖ bõD((Ķjū1˜`ē…Yä0GęĖ0FŠqΎDĨšÝݝų§ŋ‹ĀNAsĮ5„le=ë?{›=’ó ĢÉTõģĸ―žą”Iâ&Ë}Ÿ=XžĶ#<đ$ûŠ…ÄĮZŸ+sŧũÖþėĮÍĖŋÖ€ūėdUĨG8―”Ĩ"āîP07ÎINåbEĐÖZũ~LĪÁ˧ýŒ€3†(U’Tå+TÓģVîžójîöŲĖÂ;ïžóį2N·jĖ<.A{?‚>ŸŦ‘3IĘ ĄlÃģrdģ'3|yzƒģ•€ŅĨ2+IzG+āûąÚōŒFĘ9ZYæF BøZŨe4P―,[U‚ú|þųįóĐJŸŲ[‡Fę`•E:\ûhFË^·Ŋuí}&–{ĀWŌ՝§Fč"wš@âdÖIķ[·siˆX]V7]?IvUĻŽÔô *s6hē#Њúë7næwþĪØ+ÂKó‘?ÖZ§PŽ0ÚéÄIex€°Į;œģUŅ!ķ5§anVĨ†ĢHe·ęŌĖUJUӚ!P23AũýCp]qÎÞgũÝ<ÏiŧšŪë3ē‡ˆ°N{)ýûÍ))ĐÝY™ŲgVšĘ:ÏóæßÔ]К&>™ĩU%ÃiЀˆ‘ðtښáÕũnāögoW\$ģjŸÛŒFS‡!\Ũj܇ŧ𭟟ĸtÚkũoÚiĸŲë{įŋؘ‹’ŠĪĒû>gūþ\ŦzÜĢãŽn#-|J·Ų,ä~nĀĖđ|ígŦ!Äme•7B"čnķŸ-•ŅÃýÞ7ÔLX*ŌÜ<5înuŠúžÎÜös·`ąWTĐÆ·V•ZøóÜM(/6)ČĖÎģŅ_Ģ” ë~ß”cN·ýŨįZß4qsJÔ6·iÄipô“9ÎØþŽH›\ąÝBŦŅĒæŲ$ÃĢŠ ŽuM/zV™[)OŊÞĖÜŠ †ŅŌ†EÝgļÖ(3 Ũ Ū™yŽ―ũ1ū•ėßÛõZ*™Ĩ<#7F6Ä/_ÝôļųȍGíāöŦĢ·OágĖrŌXéĖÂNož+Ūî—Ŧ}į?G ĻyO ßĩĖJa"ŦH›rĪ ķŽÁFįߥ4ÞûĶča™)ˆėx§Z ę·íņkØírÜÝßôp›Ô.·˜æ.ĢõÅ|ž==fŠ‚(e\ëdVöãđÓ^ܝvŸc=ØGЈņ°Õ$ŌšųdqEx5WĨŧěûšŪ @wóØŧÁëCRO†ŊŸÉFvíóT[–ÍXYna ƒÔŅa=\q<ïžóÎß,žQæÎ p>ŅC2ēJÏ―)đųģŸ―ŸéĄ™ĩ;˜uRŋĐg?é, ręŪļOeĨ7~íģO|îÝ2ģŽŽ’ý;lúĩ@3·Y$Ŋmïc4ŸSĩ*ZG—.cyÅUŌÉ=݋uRŠ!mŦĪY;e'GĩŸŋÔuæqZ˕% ÛûųöSĻžûą&&ąïēšŨōŋ7’þ3ÄR5•” tëį?U‰wÞyį§’Āu]§”Íœú• @[q}úSó~nũˆkÍđĩĩI6šĐ9`šÛĖ,"Ū< ’īÏįÚÏÎŪđ ðþŠŠrķÔųe$ÜÍöóĖn(OfýĘûy ûÜîŨ0žóÎ;@@ŨÖŽv=ąŒ‚ōdį­Ėz†û̇áy6PáXák?s8vy€y#āIkķ_-Õh^%0Fī‘á7xŽ5+$ †Ū‰, rįģĮXLĢ=ŧ čĩĪŠ”oœžÝ{Ÿ“ŨšÔŠf„ @€ąfų…ŧĸîãc0sUežëŸ §ŌiîūũÉĘÕëó8ĘðÎ;ïü5 CUaÖEĢ‹Zeî*}ã›dœÝ1<$Š”FWŦÞÁT‘\ŸŦōčČ{Ę2GÕmiî4ŦLŨšÎwÓ4’•§TüšÛōë†sŽRFĸ\Ÿ2iÚÏ}~#ĮLÃcĄxNša…ïs#ÁŪ°­’‡ŧEfæŨšĀšŽl]DJ°`ÄڙFŽĩčl[hð9;+ãŦųGsēTßļôggÉ}8•UđŪuNfSÆüvĶ$žóÎ;Ŋ…§ę7dí<ÖĻV™ģŠšÛó<™đÖSÁ-…‡Š”åæUU-e‡%J aîϞĸ.Ô øZK@M·09đFģeY…†xũ˜žÁēŽaô%ã“{^ÁÏî̓u‰Ę#uČĀųÚÏ Æî[īŠZW<]đ8õũóX§žcnyF anþÓ{ũš.Ļ”iaÃ>SZWė6ŒEŽoųB)§ĪܝÓÚp?mvČ&ļƒC,$•õnĩïžóŧÅÉi(Đūŧa•Ô?ÐÝŽïi;ïýœJÚoT aFĢ`<đƒ^ûtóXŨ>Û'xž3įøŋ*Ww ŧÉÐö,äÔy5Ný@r·ét éËŦRĐu}œLũõđþ1ģjœíõ9Ĩ Āũ‰ąâūŸĘl‹—Ė~ŒÕR­ýæðĩŌŌ-Kf­€åÉJÅ ‚” ėXtū&|’‡Ë,ŋâŠuōīðķ֊SųŸeáœat·ÚwÞųs[-ûŋ†8ČD26ØāšįLÕvôih.U'Û"h§rRi>ëšÞH6%ހGĖv ãŠ4<cų5ģ1 ėS΁2ō~nrbbėīIwY[ ēĖ•*Á§€6’ŲrĮoĨđ-Ŋ’2ûþï8NĶģˆĩB*3Ģ9ˆý< ÃÝČg"ūĮYF§säe§NDLžũÝ^ĸkŠpv’ QyH“ōģ>Y5wŸā4ûmmĻz·ÚwÞų{[­ŌžgwžŽKUsīÕÛĻ~q6S„;ÓĀÖZ APa6ÍlŊ·7ŽrÏÅ]{ϰ@GØtÜx”JŠÕ)0'•RПôđũcæNÐÏ)aÓģ8a!`ïgpųy: pó1Ņ Ôyūų2’ækHęQ@jFĒh–ĸÎÔ웯ZqI8―ģ—ōŲ·P*xxe’“ų‹~ķējĒÎæv4 ũ―Ÿŋë`xįũXĖÜb­AðļŪĩũ>đ=Vũƒeé\ŨÕ_ï0ðąKIŠåSNc“Ŧ­Š>ğ΂čė˜söýsC‚ĩĸ·>û‰NņBãT—|J•ŲĄ3æY ĪŅÆ”ЃĖ†7xÁ?Ũ5đ6 ÅęĻÆîÚ1š€QŒMðãä0TUĨ&ĖĄïũTč??îäŌ’&ŠåfûŌĒ- gFÅÆē|vi <ũÞMmϟĻ$oÚĖ;ïüIĻUiĮūX&ísĀėÛnŧÁĪbWĩ-*ĶLޚëžA@ÜÏhĪĻ9SúÆ’æž:„™ÍÚĻCŽë$žŽŠ_í<„MęāÉīáÜũÞUét#3ĢýlyŨ5Tĸ-ŋ™åáėŊ{ïķüÍëę…―ØčūÎôÖļïĖĶŲl@SÏ™„ô- ę™úœž}&Ũ}–Ü1bdÏô]ū\í;ïüÅ 0a(ąÏó…ûfĘŽpÁöý„9mŽ\ §āë1sĀG{`4s›rÜޔcŪíŋŨéŊŽ oîŸԌŋ·î*Đ2·å! č{;aŦ KĘýėķ^§N•<ÖļËNžV‰}ûzÝÃčsïŅÝ·Û;Uũ‚cN·<)Č9Įq7­ŨUŠöąĀŠļ‡},ķF–TÏ1#O:IgÄĘŽBEÄŨÖšŪŦTŧ’ЊøŸęœ·…áwþhÜLpóëģō˗"Ü+Y˜ÝŦ’FŦ •Ū•Į ˆœ=‘NķÅËÝüŧüŠÄōȝŲŧkÄužãæŨuŅY*AM_īŒW5ðt2݌ÎĖú^lÜ;;Į ÚûrĢŠ†ĩÞ.Ų%„ûūŸģ'ũiĘâŠĩũAAÛ[AÁĖ%œįrRĘ<áŦôï /ßûą`ķÏDC|úZM:ïï)œóäĄ4•i°”ōÔKžóÎ_=œÖûėi”ôųz0Ô"2sīũ§ÎédîļV›0„k ŋųó<æaægïĖ# DGÕŦŦËĩČRV–2Þ2žs Ę:™FzDŽJ’ÏÉŦÃË œķģŽƒ9I ĖsobĖdؕS–ŪV=âūfôŽķÉJX?‘P\i%dĨ5É0ВÞų29Ų,֏F6bL{ßӕ>-dŲ {öŽÛNBÐZ+â-žyį?ļÕN-íä>įDú}îM؊˜ÜĘ j§ØķÔsčĪYŽØOW'Ð!ž“fūÜHœT°ŽŠĘå҉ĩũYá1―[D\ŨĪhâcîO71@ðˆg?YI’žg“,ƒŠÚAKsOÕÄ0šûýósó QĨpũ6`‚äæ4ĢõႆYģĻĘģ?ŨʓÃĒ|>­Þ­ÓNßĩŸí Ŋå˧{m­U]ųcfáž;ŦDĀÜÎÞËürmIŸÏåfÆ7DņwþdÂoøũýŧ!ü6w3/)OJęģŊc@[Ŧ|ðȜ_áÛž’$ ë; ÆOEšŧ‡ftÛgŌÝ_áwþâąXÓŦņkö ÛÁ€&&&ÜūÝąôĶ\OĐ`0Ņ0’ŠĸMÆöŲgį&-< §§Āĩ‡Į…ĪĐ~˜úœßֆĮ8's>īˆ)ôu·Ę*Uy˜ŧŸįV# ŧCjÁŌī@Đ֚bŪm4wŸž‚ÁŊlTkŠ FŌÝ*‹@Ž ŲŲ‚ŅžŸ[ ÖŊLýJĩęĢŽÚyJđVėģU˜Æõ‘îÆōq@ ›ĮЊ ŦYāęHYÏóÔÔ°Ģösœūĩ‘xK2ūŧÓė‘C. V·”ēæÏ˜đėĶþÎ;ïü1lŦhDiįa“—#ðÔļĮƒ@ãósÃl$S-! ÓõŦ[ūĻa ÝÚ)‹aNOVļà*ÐT23™5 ïģρ+ëÔ.q…K dßî[Ð},^Í#ÛšŪŨ~?øLkÃ2'lJSč› ņō—$aV6ŪëĢB•Ü\ÆóėYEÞÏmnq­}? šĀ1šy,Ã/#žýŒ #žđZČ܁ßŧ„Węt*‚9Ÿeexįŋ5/ÔÎ åX‚')Tu}>Œ’†Ė<ę†Zây6 Ũ?ŨÉDýAþđÚ •†Ė-e§qö‹+KŠp _§ŦĖ| Î>*MĪlj:—0„DÃýxĀĖÍėđošĄįdŽJtubķÎ―/ũŸûgBiÕ9ÖÅžđtÍOžÞ e6 a+OÓkŲÔðu]SŊŦJˆrMĮĨČö㞂>Ũ—ēmUeõųØãŊ1ũwþĒØëjčÜ; ]ëRÕä}­ŽŠ2ũā•YŊFƉÄþï$M(xîŧæc5pΆWęūoÎVIË*Ā`vrØ ―”z‘ÔT'DØúSðð][Gū PŽ·øâ+Æ]n˜ÓķŽ‹5Úx…'ZĄãÂ:nž ‚ŨûÞhn&í˜dV‚XvÝÏ ÐŒ4JU™æÁ۟č”ēŦ2gįFb?;"bÅÐÁÆ š9ۈý°*_ąŨ;ïüE ÃÎ]UÍ#•―?ĘÝT%ēu,d•JE·gåsčKūĄ-'ÓcuŠa ERýj֒Xd6âČÍr6^zØŋÖÁÜíW—á*IšTš’Ė<>+<Î>%ļƒdĐÎŲ-ķ}† po/šŧ™•P6ÝėWķ.Nö~–ĮĩŪĘʈ}o ū\č°ÚˆˆkWšî5e pŽ$œZ›*ïž›Éóî>ȉÝS'A€8MōūHûÎ;3Ŋ–ĪĮ:FüjĖJđ™į>'kđŧYå70áœ|žM#‡Ø•ŪuÐę8i ĘînĨŌīÎú·“‘ÎÕq\îfîUÚ{ÓŽC[ēýŊe)-šÄ-ÉÛÍ&p|ßëŠĘęu{]ŦïA4úï<x ”ef•? }Œfķb*`>ÚĢJ4LBeđq}VeŽö đ‚Đ","ōm؉<Öür)ûQÝÜ eÎļŪY· đŋÆÜwÞų“šÚ1•‚Ę―ÍėšŪģ“Ī]fĀÎĒņŠPGÍÎæ™bšYNøw›VŸ―A^ë‚tNŽĻáĖÅ+Œ<ÝfföÞnö“;ŸÜ+ëŸŪYrS…B>‡+ĖUzF<ËIŦüwĖÎģӀësĩ0!ÝýútÆX)ŪeīĘĘ[BÐÝėkBfŋ4Ŧ”™Þ ð}#a·ęÖuÁøs?Fãũ}â1zށ0Ī0ĸyîQ㊔™ææŊŌëwþäV+tĩĒ@zO•j(Zōų l]pfķÖeþĘŽĩJýŌnf^Y"RUÕlÁTēąĨnđ…YĻ*Ü VéŒ.Õæfô W4=Ó<)e{Š]šsē óšvžt·qŌ>ŸkjFÉpōT%0A9{XiĻũY3Ģ5ÆūĒÚwÞųģ- ‰‘―į&:F6Ð>…‰ģ1^Đé°É“ąâú\J 2’8'+§‘ĖG~KZĄęĖęj-Ī s7ģýŨœŋA\ūNÉÆ=Œ ŪdÏl 37áķV<Ï#23­i֒Ūϒ0ÁŋŧŠŪëŠðf$dæcœc?°ŠĸMp­sjqHžóÎ_œjÉ>@ϝ­™ú–]ŧ˜ĩΌM_ž†Ŋ,”bÅxX'{rdĖän#‰—Ī:%āëŽ5ĐĐóX óŨ0vr(‹îVøMq?Oe:ýė3i5_˜Ō/}Qĸïŧ››˜v`BĀP™l?§æ·&øü<4[WHȚ†G;įx87ŨðķDý;Ã,:A›c―ņ #FĄņ›œĀ6æjĖŧĪ ïžóÎ_íË<æąŪ mD îË;‰•}ĖU;nFŨuēʗyüįâuĸŠm}iIéŧ)gNQîßW•PH—*Ŧ0ud‚ÁÝTúęË: ĨÏįSJĄÐÛģT_ŸXeæNfļ~æ˜Ū+l3Ýܗ{Ņa€„jčgVM|L;Ï`v„ę‡ĸ,ĀŠÔ/<ņ’‡Ōu}þũóÓi î ý™åîF;§ŽsØYš™KĐzÝbïžó'uĩŠ<'• ŲÏ}Ÿ-š ý*”·zīö‘WíCįōE‹įg“ŒCĻŌ4“gåoã7Ÿ^EŅļũ9Ï -Č:1VSXZŸÏ@j\ËéÍ `x€ÖyäÏŲãßYûypD°{î<ĩÖ2Zíä7œĖZ1Üc IϓŨčTRôF@ ‘~ūnãÎüÎb/Čß@۔ŧKęį”3†ũŋˆÆęĒÓ܈Í8ïP,s—^Yí;ïüaŪvEĖÁ: uEĖn=p[ …j$ ItRFZeš›Ņ ĨĪ’7@gĨpËJ•͉6óš>—„3MāæY%Č~‹Įó”›ŧđÐ#œäsĢý;Rš9ôïXgĮtۘŒQ, 3Û~ãŨÏvöŠ7Í 'i$&Ž€ÎS;SHŽ,īŨŨĘLT­kMŨTæÖKöi’ÚiœėŪ gžéč%,óXũSäÎų+…wĢDDëwÞyįïĩ0„ųøŪëÓëޙmN8đ›rŦŧÂr‚īïûöIāĶö―ÕGF{§0°č' 7'íþy`Nî Qü–ËÎgíÉ p†ĮýĪOųˆ(&ĄFU4˜ÛoâĒŅôÜAŌN3>éˆū#ÁÃ{ŨN?qĄŅwtĩĢlĢ‘Ī~Uà įúÏį*Í ágŸŽēhÂDéFáwÞų{\mj4ũ“‡ĪyÐL•Ī ą0ƒðŪ;[@MþŽŲšSĐŪIXká{ aÖņØŋã•ëōˆøâW Âs?ĢĨuĒņÏ=OígWæõϧXUåūĖ|?OC*ÅąÏš·õ`įäfDÁ=ŪëŠ*•ŽėŠ4Ū‡9jë}745>Ų4þĪĮŋ“u”õËWÔŲĮÐ]5”ŧŊ+æGŒœ ó‰Ú™·+ŽEíĪJĪyįOZŸ0,Oš…―ÆÜwÞų› ĒíĸûlŌ|ųhQ4zsgâОûΊˆĶ_wšø ÄĩNKúĢ)‰ûū%ā·Ļķëd ēûþŊ_‡|îgšaęčÔņå ÐĻ*Đf•ŪÞ4Ŋk=?Ï$ŠMāËļ2óđŸĖãæÏÞ'ÏâÐŽČ]•ßHÃęíļ7eIm,^ĢrS'-žnšO{î<Ɯwu_Ŋ@ž‚  +'Ug$>m .‰ÄˆÐc ^Ž}į?—W+ļyuŨv•"`nûŲFŧþđĶĖÆÜV&nnī_”<=ŦãŽZ#•fÞ^Û@˜CzÎ6cøyîmÆĐr ‘ĀŸ<ŪÎ1(ë Žw‘üüóPö ÉŽ3gþU••TóĪĢ]Wœ+*”™jĪ3oøä·…Ð(Į†qv7kNŲ{HŧŸŸŠix,I4~ËÍš6íšVĄ&rlH•)đÉĖ.ŋÓóhÓĖüį~ē‹#ÎģŦ€WðÎ;ģðFú•mu LKũ7w‹ƒ äÎ4·ÎÆŪųæįĸ}ÎóTÁŒęė_æŲ‡fšîÂ&85Ų]û4ŅlÍ7ÝĻŌ~sŪëS%áÕ(āĪÁRĸN?pv-WĨRfø-ņ•[L$đJnáæĐŽĘâVØũī ’…Oī͢UÏģ)ûįsí<“LÆjāo―yålEû4œšĪ“ØoÚšVe=ûžþöŠoåąÜ%žóÎ;îX,ÜĖL%šÝ §dþėÛČÁŊŪ­•Gä DZŠˆ ýFôT>ũϏŸĶ€ŋ1ÞŌĐS4Ï―éŦҰŽŧwëÁŲ}Ēå+ž3… Sþd&iUŌ9ĘĶ/zDMėV/ã_Ū {-uįÉ}öŽļ„ßä—éa,Ņ ÂÉų6ūˉaýft™™Āģ™ų(ēŠŲs?ũļŪ‹$bÅÐŲtŸšôYibdģfKU04JâįÞy*,Šņøï}|Ų7†žŸŦyēðåQßîHļ‡ÎDīgï}žļgoQcK{†2ū>ŌDÛdWíþLínŋ $@wîŊüāwþ(@‘ėķÄgĢjĀ·ôÁŪŽ ŒĶYŸP―SÃĩ"‚þüïöî.”°ïGD\18NZ#ļēŌÍHŧ[=œÄÏÏífŦ›$ÍÏf‰r‚Ïs ļâSĨŠŽŌš"ģšĮĖ,ĸ2NÂËAŅĖïŸíŊ•†70ģõχ`g9:#Jt­Î3ÔjžpÄĘ=m7Ŧ/ß[†Ė2°ÍóüÆį9afîį$TîkÔÄ'S0{Áöwþ"ÔĒąûÞÝ턍ß܍8gũqüTsįļžžŧûbc…5t‚ĪMđ7ŒNV ĶĘšĪ’DÐ;Û[ĨÏ?ŸĖLiūBâ9­ĢšÖ~G‘Ĩį<4―Ú8aîWįÏĶƌý8åĶb­Õ1†wļÓ-τÓū)e2ģ–ņÞæAYVU%áŦ―aUin‡~E’íVKtģ*Ck0ŸóËöîsι֔øŪp3U îžųe ÓÔŊðÎ;nÞÂģÞŋŒæFXUŊ{$ŒũýäЉ­:íûšV·”;=܌9hUÅĄ} Ö&ÕÉ38{ŠÂ}]k4XC8TÖÉýëæ*`ēüg?0#ŒÃu–*k?G)s;™ýŒ6ąa*}ŪËČģũ8ßęÆÖ4ŦŠÐH›y|­ˆ(h,ķŅ~ŠĘŠb)•y]k­ØÏîūü~ķTÝ4Ą:9HÚÞ·ÓĮ]`úu@œįTÁÝëĪ 7ïûĻ’xįŋ6o#1f°0Ŧi4 0CĐžƒŦ$jl―Āˆ”‘āæNæČû'ząiOžg–8ĢÎ>‚: Îہ―öóĖSī·j>æŧ„аoݚYģļ.@į4W{EuŧHũ/ąÛŽŽ”fÃ6@$éfn―Ū–ĸú,š…0HU=ý[OœļŦ šeVišœ„9!l‰nĩēíŸs2ûņčœðï)ķ9g{ļIĢ=ðĮ*ģ$wÞyįÎŦŦýw’°‰đ‚(ĐŅ–íŽå9…)@ĘÍŋĄāâ45īÝ@EÆ―wžc]3é1äõŧŪuž­’›ŊëŦÄĘ*•Ųß7’sPfĨĖJAq­}ú‚ˆĄ_A™;ÉūĶĒ gNNîģ‡ūčĮ{Ðh>5“SnÖ·Î<ՂŠoÃ.ĀR=âyšũaÅ7Ŧ|ķrect—ŒíüFŠ&3ŨŽņŨ—ĮŠÓcá|đÚwÞų›Pë]HþäCōšÂĖ4 ÕÜë˜JcElHEōÞOŌ\UûŲ™Z+Ā_oØį“;ŦdáíÁ­IYô°oQđûtįx˜ˆ1đŲ,ÅÃŌšų>ŨE'ĀVŒ}Fs6F€ėWkį—•…Ķ\ÏŲ‡ĒÁJãtývz™ÜiΓĸŽ[ü"§& VŠ. kšä71r$ŦÏëö>4\Ũzžû<éīXąï‡ÖŪŲ0îkï}ĸ'ŒÞ.ą|ŪOÎ)ü7Õ% FpŸ-Éøïä>ĩb›_7 ũ(IãþŽÂŨŌ`mj(öoÚ„ĖBSÆýĀ$čW<ûQË Ūĩžû)åįŸŦRMŋĘHĄÉųđ.H4cīš.^sAÝj~þųg_Ũ# Û;yŋ‘ėsh …23Š˜öÉۃ”FA|ïĮ@áwþāžšÚ“-ĻÃXzDÎé éá€Äōk‘žïŧģ .7ŦJ52ŠL‹8{į>*t ÁîÃīUY%=§=ŧdíRau)CuņíōÞöú6Į$ŒąМ,ūøUÓ Ž:N.ŋČÆŲéˆ―A7þ>?OĐUš ū|]ŨÞ-ƒ-LÜxî4ï3ąöNhÆįĻ,œaÍN`]W#yˆfH„-ØĩÍÚČN%{xœûLĖÍįšÎŲU5TÆÓF2ïžóį{e鹖…ß?·‡­ļē$UJwž2wöé+ :ŸŸB:ĩSā,ž„$\-3ČÔšÖ7œöYŸsNî§ëÁŪûyLpssÞÏã$&üŠ€0ƒ*ŦZÞĀûgáãˆ=6ŌŨBUĒ.FÖ4ãšõv,j6БÐ ĨŽ.ЁÍéŽĖG1ú#+-ˆtwîįĀt­kįJĀúŽlQÚÔíüüïĮœNSÖ―ũÄ;ˆĒÛrš{Õä‘ŧÛũĢĀ„<@Â;ïüĩy o"|| ÜûŸR*âŋ.q™Û}ßÜĢĻšO‘—‡ĪÚíĸ}ŪįyhčrÜëߋM6~„’Ī”Žūŧ‹·­h%˜‡ß―QB%â~ Ō?ŸOI/ãfþqãYPĸRJæĶ‚*ÍI°ûlsšÅtRHpãs?NXm*ŊŌ$'ðēuįÎ]fúøjē;g­ö)fį[xó7įWÐ•ēđ>KYS^ģŽĶ&GBðœY}VN3ĪęÔ9ÚÉ]ĨoââsŠĘĖ+™'As7ĻĒYžl€sŊJ Ūĩ8[g—,”JđŊpĐĪ$mJšhքCŒ–ë7ąŒmxŦ}Nh§éáËÆgĻ&ĸ[šōßgWjĒŧg·íĢ-{ÎCŌč@UN­Ž?Ģ#Ėėþđa ó~%LĢJdq}>§›r!^#‡Xk…„nžøwÞų{PKÃZŦēԈøų\uōėía2Sø ï ēðYw%€ÕqVuJ@ÕŊĪ”&H―^Ÿ â9GĐÕUᙂÆ&üš>fV™Uē>Ü'ÕĀgóĪ3vž€Ė­)āÃqģ#ČE›Ä”šĖŲýģŋûīíK2œsz;ŽsŠfãāh5ąëyžðį›Ant€Bõ·ĢûÁŠmŸĖŽÎ7ĻgošŊŦCiÏxsųė=ęöIw ũ—=xį?ĒļH•f]gï†] .O‘ކ!ƁĄņ†­kijĘ5ÍéÕŌcuŠLB™1,*‹īY·Goā#=Ū2úu-HU"ˆwÞyį/DĊˆÖœÝ|9iæķŪ0ã~vĐŪ.đĐĖ+dfՙ”dUĢž“Ę:æöé>…ĖßÁJEU!3§mėŧJŧEFUÝÞąĩ `ķc€ÆķHÜŧ2Š+ÃŧX gyïcnU™{ĀÖŲûúČsÜéFũ8Ųë6=–į ģû™­đ0LÁ+–đ?ÏÓ[°{ũ·ĮåŸÖHœ•"mį“™JØŋžf4-šv'(’Œx[ÞyįOBmžl,Ëéw™LĐFüŸ'#Ž#ŪOUÂ`悊+Ŋĩōäy6dUjPî Č:æbvöɓcŦUý;ÝYĨ:rûŌŊ&óX…@ŅÝ;ŽVkđĪūõŋíđēŠ…oĸyúåþŊáĶ&Ũ…æ`·&ėŠU' h•ĮZęÐ\í1ōš–Ü Ąú•É؈}\ČMæ†ÅsÎyš…,•…5bf_<ā’ÆTGâBîVĐ*TŅÂvvÂNũĄƒ°ðTígŨ) FLÐ$-1ÏÝHvT ĐIürëŌķÜįïžóũæÕ՞“Đr3€uĘĖh1ËĢ ˜gÖooÂīœÆYŦLĢĖ‚äÏ}ÃH3ŠÎS}čÜjmĐ9ïû'‰ĖãfnūóÍ3UUš9Āû~ĖlĘϓŪëJJút―đ‘M†ōŲ§đŋĩáW|ŽrX ũhšX ;Íö°ŅÐ=žý d+ Š#ĢŅļŸC!>zÂ<Ļ{vÎSIã„=æ9Ģ 3ÎnÛï[yTĘŠąĖÁˆī){WVĮ›―óÎ;‘ŦmŒqC‰}UVžÜ˜ØÃÔÎoCbIŧ+Ņ'iIÎFyHÆØ Ķä&ŪŊķĸũ<‡€?įÞþÄtôVAEÐÝîį6–éô‹DÄāæ„$Lû,ÝIÏ,sß֑ÉF‡Ôp‹’em š~} ðrYĒp­Ŧ5`ĘĖõHHqEdÖĖĻ*Œðķ4+Ą™e•Ô,?{Í܌<ߋŲŊšÁp·gŠqk(KQ{ŠÝΆ*3ëgđŊRMC­đ흊"IÃÞ§ēĖYUgo7›Ä[û–ãbšÂbE•ēWæX(OÎRy&­ĶīŪKUŋMš_íō•Y•åîŅb5 1Ý·ŋHMģyŒĄV§d,R›Ânb$K56Ýy$3ŦūßdG” %·č}VĨōÃ[xó'įWėģS§Sĩî =eæ+âė$ļŪkRiŅĻԘwÃ]ŠY =BŌ7Ĩ;X§(Ņ8ŪiەôĩŪ†gîŠ"ļo=KĨëĸ*ŋRR­CاvD # Žĩ”8ûLTXœŊîËĖÖōÏsJÕYýÎa}ë˙ý—nn íėÓ҈ PJIæß†īlĖcÅ‹§0b,‹ˆÏ?ŠŌžNk'ûÞcÜĻŽsÞ›wÞų“Æ\&Ŧ*šKø&X7f‚åŨ· âœ=åŒûė―ũdĘtVˉðëšÎĒ…:}/ï›}zãSĐú €jt.qÐ|؉F,Йßė@Ŋå @íäŊ8á~npĻáĘ*ÆŲȂ2ĩŋiĨ<­%0 LaDqÅę­yˆˆ˜BßąrMY“!í\ŪÏUýœ‚ˆŊŧwÚČïŽģéʉŽ* ]&öåú|“k Eš ŠŨŋðÎ;•ŦmĸB­;É0īf–•đ7U2āŲ;ÜÖZįy*klþY•ûFģV•"Âč@ĶDw§8įø+âŦ7 Vđ]ŨõE1Âܞ―Māī:žĮ`―Kg͍œĻãaąŪlę”$0§ĸp›ļîšģ;7žgŧyÞöĐtkŪCSl#ķ—fédðÕ]Ōš–{äsŠ"͝ĨęšŪ‘ęRîcaŅ<ĖP%ƒÚzïã$|3ßyįrĩæ6ĮD&·đTĮU}Šå#Õ 6ŋYYĨN,pyN‡(øūŸþŧ[îŽ3Ĩvßŧ N›FNŒŲØj.ixŅqũJ߈™nųæļö~ĩ#ģ—JÁ˟ûÉ*-ėŲ―Ž* ŽĐxČsÜúO)ĩü2óûįĐŠĩ@ĐNąØÐ%ŋtðĪųÐÄËÝ"XŠŠk}ĪR_î]<~ÎģžÅ *Ō×L•ŋeĻJUG§ŨIū|y|ýļæĘRŠ 3VVUáwÞųƒ\­ēP `fŠABeN .ÁąŌ†ŲQJ%ČWe‘Ru“îÉŲnZ•ƒsBÕÝ0ōð­Ūĩ,|( ˜‰2·@Ę-uČ6†-į9JŅ šE+đķ’íČBY˜Eī;V+V–2wîtsHUåæ+Ö9%ˆ#ƒÍ FXwĪŸÏ?Ēî―mčļöÝčE‚f"rįüÖ§:VúDãŽÔZžģapRŌ>§ õđHŊ“ÞyįŋH *Â#‰Ĩ9ÍÎ―ifĪub,if–Y ‚ôæ=‡Ė9ŨōˆNāÞgKeąē·9=lŸĢ‰v1˝†{Ð[RvüŠŽļ=˜P§åeĪúnlĩlîúÖ=0ÜäĐXnýjĖj••Ô›d\ŅyŠ';+gï“gCt~…_fYŦÔaĘË|‚ĖÛŋ` ýzyįo;N§ģd0sfžŽĒ; ūĖLdfēë͑%UAk…ðÎ;ïü=Ļ-U˜įŲFšŧŅÎģA"mēīæīTŅąģð‚°ß+f Ļ­ĖmySøōK§†^?ϏJ VÕ7ŸĨމšn…TYo’:)Č܆+(ČhRe•·Svž}öO7<ŌŽĪ―šr˜ĩ*āĖ)VŦJR“ Č^-đq cJáþ―jƒ€ĶU7ŪĨŠ*˜›7ÎN Æ/…ûTlĄmCY˜·aũ9㔍öāŽSv n6Á•eæ îįiÜĪ„ŽšWƒPU}ągÕÉs­‹]o!zŸ―ĸũ3Ųļ•:yÜÂÃT”:aŸģÏŨ„`ãæ€;;ķ‘Ú­ú•I._{o4VÕ,žFóˆđãu]ýNSāDŒo•^ąŨ;ïüUŪVŠŽ5T•*;ąðĖšę*TUcUš–\Puä—™5?ŦÕF†FC4–1|/r’oÍūĀĮîalcĸ†DóČ`ĸCIš=w īķ€ +4ģa@ܝāŲÍöšCčĻûþāîãmëZĢ—hg›Æk­įy2s.~ž•9‹ĖH%qvėšŪqâNZØtÛĖûÁ}ßƊ}žsÆÃ6ĻÝËŧ›ðÎ;ïüÉ „ΔÉĖŋ^,U؁Ÿ>7IģĨšŧJĐ2Ē[ũĀĶÓšÖ•§úJ?ŪTpví^Ũõ™FóˆĐ­5wRgïqņöįņ›> f`ôÅį7ïė›Syâ[lģaÍüV6o€k]}’6ĩÜÏŒdß―ĖL€Jc1xžįۂcVŋ> ‘ Ņ] ũIs~>Ũn2·“ĖŪéÁ―Ö2„ŽÔäĨï ú›n.čœ$Þyį?Č՚é>jDšŠRāšbō dXąō|KÌÜŋ>ą.ô~ÂC4ĄĢ:_æÏmČ&ûjvØQ•.‹ũJŌ*ģ\A:%í+I[šGėŸƈe-ÉĒd–'›ļNĢöČ` S%Øļw{j?U:;­O€žũMņsýÓé†el+í>„õŦĻv\ŸËÉlc1S•Šģ"îûųîļÁûy b(ÏĐĘŪŦI’üÄpïMh<ÍyJÄ;ïžó·ÚóûAXĨgïģsEPĻ*ĐÜú˜Ŧ ĨqTĨ;ãm)+ŦW?Ŧ“Ļ"hN”2“$„LIĒó9―wÖénąxŽž3v,ũĀô•yļŲޏú›îŪ~ ũČJIlĘØJ•ĮÃĢŲŌVY… Ė=žGĩ’sÛÏĢ*B î{“ô0w?ũ!Ȁĸ>õKTIAû?•ð<ÏðË4Ž5áß&]`Ę#@<ųu”đđRf4ïžóÎ_ÜjwsúĐjaÆ Ü@œJU}SŸÛ‹ÐÉ=î2РĒāRN c‘?ũsYD'ˆgŠPáÞ`—fl€ōėëģ vïūãōéoðÅÉÁmâļÔ š•6|üenæqNŽīkđÕÎFGrĖY2 gCþĐü\Ÿ])NĢDô*:æąŸŲD3ĘP•DžŽūþų|PÚŲĨa:ûĨ|úÐ D•ÎsĖénÏó°Cu’đMļí;ïžó· vsģęLüŠŊÓ; āĶL”ūÄč> ÓXÐΉG8„œãøågŽĻŅā{vļÓIģóœïV`ŧ`ýÞaŅYˆ?y[Kēž;ÝÂ=ôMóĐ#“`FĢ퓝ĪĩÔZ[‚―ÛV0ŪČ―!FoĮįė |ō<u­kgÔŋx4mHXæ XyćHXßådĐÜlÂŋöÅ}‰.cÖ ķŧ―Ã\$ŠœĀɒNSÛïžóΟ3æViŸ.=ė܀‘pYDsåޙ2U”HĀP§F=*é ĀĩlĀ@ĢcÚséKRgđĘwģRß,ÚĢ‚JīÎņ2ŌLÐÉÃozaŽ7,–õģ)Ė`Ž*Hë GgU"sˆ…„­‚8‘ē”évw]͊Å%_čf#šxžGd3„‰™O…O\ëÂü”ŧŅt`æ#üŠi‚ĩsŸ4Ą9ÓöËU€X™ĢŅßyį/Î+öŠ"ā•šÅþð{+‹4 ķÁ‰"ŒĻS$Ý‰ŌøūĒ·Î§ÍŊ$ũģÃŠĄ*“ö˜zƒĒC[<,ŸCԌDK Jœ}ēNuŅŽs—Ģzî}NöÅãY—åÎN{‰đéĪØ4ąŦoĩmn”hžũóœg‚uāF˜(ÎÞ&ŪĩĶÏFŠÏõõ­hÂĐĪÁilŽt3ŠjZÔŠ„ūĩđūfl3ņŲQÕUÁrsžóÎ;j‡CČŅH҃Œækđûyv5ī#S„­ke üz?ũ3Ļ7Ķ,ķ*ĀŽÁž>ĨJđ!ÏŲ{TîœúēNP~-@9#bíÎ,pwk\ŨgđĮ~F^öEðb­û~ÆŅ{ĩ ŒĪýæÝėŸÏ?FGĘtŦRftÚýėŽĒšQ5óΜ}ę$H™ŠJĢŅlwoåšūŪŠ\+&—ķ wëxęČØ{W)VŒÄ·T€xį?:ŊŪķēF’Õ˜+e-§­ë{œĨts_ŪŽĖ”Ð,ę&ĐIQi—a“―§~RUW‹{#5íë.;‰þ?î†ՌDi6§U+–52RtũĄ,ēr؉ų˜ŋVėķ8ā}ßĨi°Ņ?ÄZÕÏņĨ/ēmiŋ9åAw·nÏ}0ý:5ĖH–Š~‹ŅōĪŅ&2+I^ŨĨVYĖ/ŦR•–G˜ÍŸā+5›čÛ·œüwþnē—Š$ýFLmxœsN3ŦjŽėŠ„gïs­kÅŠÉ–åũī}jÝ||Ū Č}Pœ…waæĶ,ŋՊU$ŨālžÕãgŸ1ŋķëŪŠļĒÔh`§‰ãk] ĸ~ąV\K€Æ=ŸŽ^ ŦÎ/€0݋s,ņsĸdĨŧ™đÁžĸ5Kž‡…€ĀŲųŁ_]ÚāŊ“āëöŸ+ũ9fÓŠ;lïiEB‚Ņ^ĪýŦóÎ[N^U].{ï'ÛĐęįþiîaÆ―ÓhŨúÔÔū8-üyI+BŌޓk傞ýmŕ™*XxQáíĩ­F―Ž<ĮŊ8•č1·ÜgĀ‹šØHŠē`ū\úvJFGpMð―۞ĩŪÉj-m§,Ô8ĘŠ4…ļB=ÏcÆÞŽ+ÏDôÚũڏÚ8ëÔ ‚šMPfFð针ĩŪ‚Ú ĸŌÂãZĄB3*åî/ÚþÅyį…ZûüģÎ9þ]ĐJ WŦĖJõšGÔwąmýÖ6š t{~îނbUv8Ą BIЄf™{) <ÏnŪĀHf–š6üd4Zƒō·\öZëįyD8 Đj|ÔcģŊïƒģYrģĖã0đ@žįIÆäĒ-f•.LÎ/ð69þÉyį%ÜâĪęĪ‘―Ã~c·$ņŋMģō9$ÛCU‰Õ/ÖŨ:Ūu•§hėŊõ›d•eF[$YYbžŸSÚ8ÛÞå1ôEĮuũÕâ9y]ˈSŲĮRđÛr'@Ņ}ÍɝFëÜ\kEØ~Î9ĐÉgoA+ܜJėŸëĄRt‹+ö~:—k}ßTz”d`ôí4ŲäË%å7cŨũ~šČŅD4ÎrâÁÎÉÔų|V•J"Þyį? ^ÉNüĢó~ĄĖĐMĮĐ]U"Æ4%Õ2p?7Äõđ UŊĻÞæŨi=xÎÉs„Ē1ģ@ŒŪkŸo―.…_ߊÕŋ“ākķÖĨŽįūS§ŠA> e_IƊVeF}U”́F3cĶ˃ŸĖ H4°N’GžœRôûJlÍqDl#ī(éŲÏŽĻӋūbM&ƒwnš€Ü§oíhve‚Ėũ“U…iĸäžóB-Ï>U"­T―î]< æ ĒÓŪkŠdd Ý4šlDþė­*7F{ŦĪrók]Ï~ÎŲîĶâI99u -Š†ËÓ·s™eæn–ݝCš51zž É,NfŠÅ<Ųáú;AeZĐŠ"gbø|.‹P}‹QRAķüëîõ˜Ó*Ö7YžÓÜ[ģĨĒ>ĸŽ:―ĪV‘HĐ2Éúöō^ŨĨ,éÛUģ;—ˍąÜ@UŅ,uzĐėœïFRMúNjžë†ĮšVąÏ3Egĩö+Í9+vSkŸC3’L<ρŧ‡)“%”Œ–{?åâL8N ß[hœýššSŪRSNũýâwþÜžĮb#ė?ĸ˜ŧģƒ_ÍÃZą$}Áøđ7AĢ)‚ކąRŧË&;fŸSv!XŅ―‚ CŊVķvM™>+F?P]ĨsvfĶwýũŲIänmVĂ5YŨđÝ‚ö$ãšîoÍ{ÅFļë9{ø ™öģ;[öSRž ŋHĄ˜Y’âŸkĸ<!†N /ÃH åÎkų}?ģō;ŅĒ.#MėpsšŅúYŽËg_iÄ>IÍŌ) Ã;ïüÁy·Ú˚hÚŠÜg>ĪŦīŠIRŠhō8įD8ÁĘ"aáĢßaajžvâYϙÞ7( ëšéŨu [úÛSPY…~Áq øōĶTU4æI”f?y&†Ü—œNF• Bōo§Ž:ņÖut*}ÅW4@øŨ°{Č~Ī2ŌÖ /ĩ"6įxŨĩžy#ņ0ģŠoĄŲoyݝ-܆C(HÞ ŲI Ŧ; ōĩ0üÝyįu‹uĘęÔgõ6ęFs‹™*í<æūēŠšŨPĪŽæŠėņðÝAf°ó<4\Ũč>ŠŸĩÐvŊ/töÂāËsC"<û1ÖÄnyŌ-Î>i™ŧ4 4ÄŲ§USŽ[5d@ЏģLÍb6W0&ąģ3ŽÉ­2$Õg\WxjÖBbU­š=đ+™)k?ŧŠæO1šŲņ,Ļĸ3Ú}ߐz;FÓōˆūæwþÚžBÏéĄ1"fϝ‰ 4’fCüóđNÕþ54Pf\ąēÕú2Ï1 +K,Ø#<ÜŽŲ ]ë’jï3ŪĐMŪ_bJu­•UüR§(ĐËl>v›ĮuEfĄĘh4Ė [6Ï?[į~ŽŧŊï •Āî‹<Í8ôüüÁ)U;ÍÜÝþĸŌl­R.2 Š"9fĨōūZýRóõó<æÞŨX%Št]ą3“xįŋ7Ŋa4íč_™9ÕÜ4>Ï―ÏCŌÃõänVÛÎĻUõ Ēé|ƒŠu„·0'~Ņs­oŅá@ÏÞļû8Íš›*;rá›#3ņ7Ŧ•XÓÃâtŲ™‡‘Æ@ėįôz\åáßdÅôĘÜX―Ï& 7“4Õ_óüÍÔ,ïfFà ļTîó5 •)‚āÞįÔš ę+ŪRÖŋ#é~nãļ‚§šwũKģN ïžóį=3w#ÝY‚ĻVæOz–uđ,wg{_Y•RÕÄu’ī·6e§_ŧÅdķŪ†ŅRÃPĮ3VU!âdþŠŊĒ‹ĮÝÝ!I0 ŅđÎĐpógĢĖ€ze.e7–ŸR}>ŨólUÖ;ėÍÝž<ŠPĐTU§­/ĢÍ3WUųĪ›ŸÉļ ”~~þįk­ėņžÓqãînŽ: ˜žũŠ`l:ė)Ĩ‡“ĖŠ 'sëāÝI―}į?9Ŋ…ĄbYfå9__éþoĘ5AïĖ֟įÎÚąbЋ‚…ÝOWØšũ6·{­Þ@ĒąPŲŊčŨB՘ŪúųųœßėįH7ˆ―V'ĻgïĘâ,ŋũžāþ™pŊ˜˜ļ ØM`M7ŠŌÝmIīõŦy’‘CdęÛsnķģcÃû‚ Ķ5”'ÃÝFęP47Ï“ûė†K–ĘúÝHBÕŨHvÎ-)lYÛŌŠj5arïŧ##֛WûGįjWŽŦ­Ņ݇ž4gtâę>ÛŊĸ―óþŠzþĸnē›l*DJ‚J „ :(JBGEéH ]T@)ƒˆĒ(Ē`Ī7é(UŠ4 RB(’ÐHßēßįĖņý}|þ‡ÜI”›ŧgÏđwxîÜ93ŊqóËf―‘4džðŨ)P1F󛓒yeþ"=hČ`/í–hä p·eÓL$ļÅUw›%Y°ðiĖĶž&#M;2ČĻ(–\ĪqØ5YÂKļ Ã-ia›HðpŽĖå:™MŅ7ýTũÁīXÍ$Q°ļgĻ ‡ķŒ@S°ŦĨ"XFzŊe–CģķÅH„2ŧæäĒB#Ī•āĒ۝W72j舑 Fƒ“§NĖųōËKĸ\âŲØdŋŠpŒv‘ŠÞ"P•*UGóӂų{vl7€ Œ.2{FúF__ŋþúûđü0Wēą˜JXÝųŲ0 Y`ĒĒāËK`N%ĀņxĨ–ŠģģŔš'ĶaSíĄĮīMSËøĮ.mYšMŋH8)Ɵ šŋī A[þ Ïh6ĘĐj;FŋFÝv’‚}]Ü4FöL˜Vgh>˜ˆ{eÓ4ˆIÔ.€ũ"Pų”Æú1Xf™eÅ1ŲKĄĪ’ēíPkóķН_P7ēþϋ—öėŲëĘåËķV­ÚA ‡ð…5ęFÕËËÏ·{Kę>Đ]+=S7**//O“_f+Č-ĀwlŌžĨËåĘÍÎ5úXR‹ŦÝŸję.ŦsšVDDõįžG%€ũAFh[§^TÝČzOōUŸāIÍÚ Ģ›øø..Ð0…]eðĶð4ĩa…&ãUæÐĪ8 ÜōaV–”LXÝ(Óū v _ˆēļzÄO1#ÎA SuĖ!w„ŊMĪ€c&&‰ąvUq0Ô6ŸĪ‰üš~rYE’Ęæcę>(ËPéq‹ĩÅŅ,ģžZ ˜‹ú4ģ–ï~ÂūVž;Ŋgï7ŠV­šdņĒøļéČŋ={ö,aÜråËWĐ\yĮïŋ7wÎ7™™™@*,<žbŊ8€ÝšuĐPÞßÏŊZĩp’ÐÔã2UHH…ČČ(8ßīI“† áÆ>ũÜóiii\UõįžK—þōčQFNvvhhhxXØŌ_~yüč^ŠöŠ)Ō 7'Č5u ’l~ŒŒŽ‘:TýY҆ņhÁqg iiøHÉ p‰ĖŨec.Œ L Es‚5ŒRļ*?h ŧ$Ĩņąj†”Į`ē“ÅīC.IˀĩĀWĩÄTãÆf™e–?Ŋ–`ĨG‹û― Ïß$ŨI=>Ŋ,Ļ–‘ž­R…Ä,}ZÖÝz§ęrIF‘YPe𑓛 ĄˆÞâíJiŊŒöļtņŸ‰'oßšÉ*&%ֆąŪ*JĢomđļbųē'OŽ^ū<ëqæÐá#ƎßķC‡wčófïá#bŧõčŲķ}‡áï>x`?Ũiâģl:iwåxcõ[ĩ”Ö\âu2ÖĮÛIB€)įušæ7Ú|ÐidĶ―ĢĶ”đmڃ+į— M6. aÞRâŋŦ…N]ú‰ÆgĨXÁČAh“|Û›–ÏQûˇÄHuŦm…FSQ?“bh–Yf‰(ĘÖNÆÃ!ƒĄ ;vœ}ûöëØ9&šq“Ž11ĄĄŊ'_ŊTĐRtÃčĻĻĻ^xáÖÍ[ļą ŊvDíÚuE7*]š4,,üæÍ”qĢG9rĪND‚îüüļéÓūûv37kŲŠQÓĶ#GãJÄëÄ/ŌiĀTõnpŅíœÓ<2tÅŊÔxāã%(ÉĘžQS ,õÐÛČËڅ†š*ëŅFŦ(ļĶd þEV‹85ËÍÔÔ2'Ó°·†ąiĶÅŧĶ$쐋 Ē90ą[14Ë,ģŠÅtËÞcþá…Đ;pˆþŠßĘKÉ+:tØŠ5ëš™™ÕąÝkÓ§~ī aŅžų?q†ī§ūĸîÚĩ+ NĀņÜļõ·Û·oįää\đtié’_8đå·íđđđpæ>oâŲĐŦ+Čsųđxšĸö›Ų_}ýíÜyóŌRÓJ—)ýÕŽ/J•,9tDėÝŧw˗/ŋsį8ūuûއ2gJJʞ;û Ø­{ũĪĪsĸÞđíäÝĢ%ŋ‚Líõ f˜.QýOðhĩ—iTcŠcðÞļ4KŨŅØ%Ä ’ĸ+ÓŽZ)áU_š7’„ P[…ÕCO66pôŌUÔåWÚĮG•h–”˜†Ķ”ÓÖb–YfU‹i­úķÅÔYÁēģēūü<ū{—ÎņqqS§|ÜĐ}›[7oþ}üxïÝfΈĸęËYÚĩŲŧwš­ģ>3jäũsŋÔĸŽm_]ðã<&úræįÛĩÁŦýqÞũoöė~þܐDžóáý}ßzóÍ^=ü\þûöííÕ̎ïOXū|ÉāýŨŪZđ`þžQ#GŽZđbôČŒŧ~ÝΓūĸY\ŸÞ==ž0aþļ1ĢO%&ē1%Žd‘5˜r Đ PwUzÞHe åĻz!ĖÕÆbĶsšC+'8)ÆĨŸ”ðWýQnß 0õ`Ó[ĖÄ[t61…ĩ6ãåĶ—PÉXlÉ0Sá)ķ=-ģĖŠÕ Pė P"Ũ-WęQ @kŊcGĸuė˜Ļ―ļŸxõõMū†]ąĐLŒŌį) Ā6Ū_ LLK‡öÁ-ĖÍ9u"ņܙ3D'ŒÔc“_―z™˜ųO؍”ëÉŨŪzЙÎcÖŪņuų õŠÄýcįÎ=ŧvjŽÓNŠîГ'O:ÉūG+$īũ—QÝGŌčÝĻn7ÄÔÚ Đˆó–cwäxĐŨÉ@­ęr˜.“)fÐ(„6_7gĖ1­œ€áä!xäĮφSSæ­}…5jíqĘ6˜/ļ5!ưgę&0@,ÁÆX"Š–YV<ļxæAŸ‹†7&ņS·ŒDũ@7ÍŒÚ Îl8§iÞŠķ$МÎ8ԘÍŪ€a΃T†h†–4;`@‘vņbBq!•ZO]ĸ ö%˗K"%Võlȍe ģéjޜÔâW),ÖgsÍ7p8Ų­RŲYf'šú‡Ũ”ŌâzëĨ™í,Ū(þŽq9ՉũĩĒhJŽxĘEŒįm$暈„ ’NíŠN”v5‹\.ŧ&|ņ'/“@K8˜IX…aî―dđðâÛðÆ2ËŽ‚ĮĢlõqrĻ­„ë3ļf„Š'F>^&樅ę2@uŧ―kė>aZįŠ7Įūš·bBár$ÔÖw1@7ý ;!Ë3‰’Z{€Jw?áí.Áū†\M3Zc:•Į[―TË•Ā(dŲ.‘NŽĪ dmĶņ#Ī6Y·f#Œ3˜]̧œåj™„ŨÕKUõ2ӜŅaáČ$|PŽĻų`:[Ąļ ŒŨLCrÎŦ₠_Ø eSސ%TēˆË,ģŽ8`‹1: 'Æāŋq^deeâņK“·ÄpĘ  †I%q՝įœúü§ų _ÉūÂðaŸËĖvļ>ĒöRČT° IГ%äØÃąGMƒ2å—]:w)Ņ(Ğ>qxIk[“eEÁ+b0FUĀ—Ëpp €shö•—đ?Ņë2=%Uéđ…ŠRe R5xÖŸUũZ=WuWđ@ĀúTEšī, ĩ*ÍŪQdkūŽ­ŪŦiē ū­æ óCM^­é^Î+ĻĸÂõâŲ[Ė2Ë,ŊŊS1ÓŦFXOūȰÚė*FˆÛĄc§ÉSĶNúhJÛö)j@ˁ{”ËŠN•\áī]SýÍ― žĶúģtũŌ6âTĢ€·DĨzē…āšŠļŒÖ™Ķžü)å`šÍËÔP›Ó·zy3Bô 1ÜRa.Ũ,ĖŲYâ·FĮ oØhð•ākīoė8ŘóÜĶhÅyÖĀ.1 ;ēīĘV|WÝj“îđĶÉ>*ę‹ÞĖætĪģ8abnMĩŒšļ‚Xk@œÕb™e–˂ÃĪyÁ7(ĢYMĶå€S“§Nýņ§ŸĮ―7aĖļņsįýY72?ĸ ĪçÅĮ•z|lÓŧóÝyđ9ā õœÜlœ[ĒŦđųųðÐmÍĩvíÝUĸEþd°Ļū8|UUŸ1éĻ–·ŠļpÞ4+ãW ^šÁa6ŋ4/ß qīŸh|ŸtŠZŪLāeg.7GŠtmÚpATļīïŽpŅ&Ōãhvh嘿!h KxËĄĐŧ…­ 05vLJŅØ@ŠëWsS]ĶÍÐÆp§ņú™E#(\­ŽUŨœ++Žé^–YfĄÁ3+8Õ ÐGôėėŽn={ 6ünZÚČØáĢbG$,˜šL§Î1ý ŽŒŠ?jüxŠ číÕšMۏĶL1jÔ 5kåŧ \Ū€öí;ūĸáĪiqŸ―új›ReJ‡……}4mzÝČČØŅcÆ―ũþĢĖː ĄýŠ‹ĸ"ĶsŨ€Ā` ›-#W`āŧ#GuŠéŌ9ĶËīO>{㍷Šŋ;o—)Sæî―{Ô, 2äÖ­[đyyCCy·[įŨ/^š0ũĮ/ŋōĘÅ úöíĸfŸ>=šÆTŪT™Z‰–­^ŪUŦÖ/‹?jÓîó/fÝžy#;;‡&Œ·võJbĩ°•ˆ…ņ’ķišxĨûrOĖîŸéÞ}%Ņ@9ËFχ,Y DpĐi2m5Þ"=,ĪYs6Ā(iRĪK+›vĻdbožlÛSAžÝJö*žf™åÕzĩžŒ„nį˜Ū@ŅHävîØaÖĖø˜Ū]ýüüæ~;gfü§ũîÝëŌĩŠcŒąkÚi’‹Ķ=ųSER_ŦÎ&A魎d‘kāųjO 'ŪŪ6”óėÔ1ũ•!v{îÅ$šĀ0N™â1›zĮ:ŌÐR[áË,+Žyĩ°•=Ĩ‡d­dS†ë‡‹‡øøāyü™V7Ķĩ8<ŲēiÓĨĪĪ—Ē1ēlŲē6me4 ;īnÓfÂŦVŦ†ž,0+YēÄĘA2ÜnËHO”‘ČÂļÆ;Į0-(ũwųáMŠ—],##ýÐÁ:ėė™3‰Įĸ†ĨSĶNŦXéYĮON𙜜 ―ʇ”ŋvõ 'ØĸŨąÃĪ”/[žŦš9ëK.˜ë}üø1q‘üÜ<:Ŧ›†ęÚčnJ/[no”äO·  Ī%&+™HęÐB{Ŋ™>fÞ6ÐŽ™[P9Ë`fæąÚĀĒ@r9ØoÔ <€Ë‡•“­@·ĖēbhVÕ°B@ppŽĶN~ģ3ģŊ\―ŌĒUŦ6mÛØĸâ^eʖc_úĮĻHvú’Ē2tP‡ÉÍÉvųųÝŧwũÓÏ>kŌīéØ1ĢnĪĪŽ^ûŦ”ü mí1ΊĻņĸ­[·,]žˆW‚‚Ū\ūÄ>sÂ5ČeJ Lé„ÄCĩüĖxāüûėģUļ—Ë—Ņųųų2­nũã;ē–—Ÿ .?ž41)) ũ–‰pxkÔŽļ—–š•™é%SëĖbŌ žŲĢ g᧊Ņh/nUhĩÛĢiļžv'ß ÞZ–Æ”ŽČy“ËP§ēUS2ŠLMá bVsrË,+ķŌāšįoVíÖåãҜ_?ß͛6"Ž5`Ðāˆš‘<ÚÓÉ CÜ=Ė”ĩ={úTëÖŊŎ•”tž€ėÏ?ýäv đ5nÜĐS ý8Ī3@ōî°ÆMšîØþ;Ēī]ŧvCī†VĻPĄ_Ÿ7ĶEÂ2JoQ%ũVMYæņÅī-õNýý7d3þ―jááM›5GČfũŪ]DrÁ35ÃlvØŋo_ģæ-ŋ;ė🆅‡9}i›ÅK–mÝž‰E‘ĮĻåÂCĒGŒCqbņč96ås’Ķ›c\‰ÛŲŧ°RýÄö„·Đ~#Úļî"Åk‘vÁá äöĻi/a››JßbˆZË,ģbĩv)aøŸŒŽGuXĪņ"'ýýýĸ:v4vø°ýûö"ēEÎĀåK—ōóō/^8ŋ{ŨNb P”Hî§S§þšn JÞ]ŧũ°iÞÂĘËO$&ē]†‚øũsŋ=tp?ĻZŧjŁýûjÖŪ]ĢfMčüŅΉôSx­mŧčFÓŌRy*7wQÛûĮV„Š,ąwÏîģgNó$ž“―oïžåō•ËcG"bĀ&’áŸ;s:ýáC.éŌĨ ā™'ôĨŋ,B!,0(°K·n•+WIKŧCoËĪĶzIfiž"ĢÔæÂPð΍ĸߓÆE•ΏO@ģxąĶ%;Ãt[ވ9q„ĩ{nĄ‘'œmú;}#Á]iūÚĖ`ģĖēbg–WË~ŽYH?ûĸER€Č8|ð@âņc!ĄHHøũöMŋ€E?',øņ‡ĀĀ—ŊÁ„+W.ĮZąb%ÜŌ›7’Náï‰Äŋƒƒƒïßŋ—“ÃlPaoöîYĐŌģÜũ ØžaÃî;BBCsēē` =ðŪq ÓÓï5ÐÏå‡XÏî]ð6ƒþ―}ŧGŨ.ėZąĘÁ―{N&þUšLYbÁŲ™YėŅ8‘Ø=Ķs€e^Ϛ9cŅÂÞČč2ŧÅt$ųÁ„J‰ P Ą•ÂrÐ“õ[―õÓÚ Iķ5•Į9 .Uä@+5°Ŧ"·FEAâ 6ÍëzŠž2'yĢ þšBgĨ6Éķ9VŽÖ2ˊiAęý)=j& Áã€Ā@ÐyįömŽƒ‚Á‹žYU*hūR2›–z‡?ýü\ļqЋļm~^ˆĄ0ˆm{#ŧE#–äfÅGžq=rÁJÖ$ŠlÂÁÆič@:"˜`—ķá>ęoō^o›Ó–“›Ân=yž>-dĨJ—z*4ī›Öķ ÎHø8ãyt8guzJx™îįv#Œāąkā@JžāŽJý†Jé^S·f4b„ĄÚ͌ÁÄL‚þ·Ļ‰ ˜â]îŦPb_§/LĮøcŒ2WÓm-ģĖēbˆZŧüzŒ8€I/€/L{ŒA\9'ēĻ€K‘”ˆiï[~lB! |•§Šüâ„Y‚*Ńá hq%~īĒ–Ĩi[ģBõF—ŠįJlá―āĶņ4õJu4—ÃįQŧÕJ]€Q†e.þԈ 7•7’@ðW4dôf™MŪžzíüH˜•ÓīÕ>ä.īLVßVëb ö*En:Ø[[éĘŌ·&ÄŽšbĪÏē dĢJcjęļM7ÉĪCĀjöÍ,ģĖēbρaŠy›ēTxĒ\ðNÓøĩË·binĐŲĪō‹MDԁð€JpIû1ũ2•ĩä°k vÂ:íú呐Ĩ,"Īóq9Mø’åŒ ÃÔÅöHMĀD;æĒÓð ß)›ÝģoïĄÃ ‹–ôxã-ÜZ•;8kŒÂFV$–&Nþiá"S Kx“ꉹãß_ŋiKÂÂÅ7oEčYƒĨËVDDF>ČxÝļņŌe+ЛĻV-|Ö­ .\đæŨ™ģfS,5ĸ§„ĢFïúcßĻ1cüž0îģAÁ%üūœýõÔéŸ~=įŧļŸ—)]ķ[ŊÞŋ,[þÁĪÉ|stïŅkÉēåUŦTãRIāý1a!oÜķcŨkíÚįfe7lÜÄ\ÉŊ70écâõĒę5û›q&lØžuâÄÉÄŽđ—5ŋn`ĖēUkkÕŠĀÅËũhî!eqäqÞĐ<~JžĪ 8Š(Zf™…ZxÍCš$*ŲĨŋĪYi_ ŊBŠŒGé—.^8~ü8ĩ ūþŪwúõïÞĢĮŨģé™û2‰>œDL lđrmÚĩûí·m‰‰w|―ӀƒĐw0҉Z5#âg|ŽkIE>ō˜ąãĻûó⁚5k>ĸ %J”hõō˗ĸđô8+SũĻl­_k;qōGŋmÝōÁ„ņÁ%JÄ}>óėŲ3$ęķkß1$4ĪEËVČ#œ9}jâäëÔ­+Ýv—-0pÐëcøķ ölėøũ˜mĮöídãRīVĨj•*aa žt2ņï‹/Ԏˆ@6!::š0ĮŒGīāōåļßÚĩ#č;điý†įžĄGžxПÆÅWŪZuüØ1{vï=flŊ7ÞÄ· ïŨoāosŋŊĮÄ 6|ųēĨ\ÉsÏ=ĸņÔéÔ°áĖK·GŲR”_o>ÛBm5f+Ū"Š–Yfm‹|Ŋ&þ0É^ŌÖ/v#ý%ÕP>&U Dp0š\ nÖžY›ŨÚā?[đ2ËLtæĖéļOĶ%ü4äĄQë.ȗРxĘ.Yšôķ­[ããâ§}4ßđió›6l`éõN{ŋŲÕÄ˗g<|ðņŊ™Ÿ…jÔŽAð”š\æĸįâœ'N ÓX=<ž}‡Gđ›šUŋ>ԋ‰‰yéĨ†ÜĨšDfE]a˖^ÝšŽXķä—Å‹4lÔøūý‰ÆŪ]ŧúŨĩk9ßkÕŪ…Ļ7ÞâåWęԉ8rø0ąTõuŌKĪĨšJ­"ÓúcũŪukÖ"ķ°zÕŠĘ•+wíÞģbÅg–/]LU.žãâđĘûũ,^ļˆĶåmåpĸÁ}Ķ 8zøOŠƒÛ·ïÐēU+ĘÕRŪ§P–M qéēeŅØ}xĸ~ûŽCB*  ™™ų˜›†ÝxŲð‘ŧæöE™ÁnrđLĩpáÂsIį}].níáÃûlŠ•(QŠŒÍ'{JüeóĶMÔ%KėĨĻČýÄmũÆFXó$›Mþ-ff™eVa.yĩÚ\ÖKƒķiŊ­ÍŧŸ$iÕą>{í―ßęąxŅ›7SîÜIsũï'PŪBˆfkI ÄjՊՏj€É H*eDą…mæĐ[7ē~TdŸwúrōęåËwþýwÓúõsū›Ëŧķĸþû­7ôz˜ÉFņįϝ]ĩbyp` {mļœÄŌÓÓûˆ”Á?ĸ\&Úp'õN… ëŨ­Iđq#ībhZęÝjÕŠĘIO3'EŽûûoېÂÜŦWŪž›ú/7{ėøą—_iÍ{·lÜØMœ˜.™™™ÉŨŪâŅk5šI~îâÞÝŧd;<•€õš)Ũ+>Séþ―{”› ]VÉĘzl$/$%!,I 2VŠTÉ~ÚÞūm‹&đņŰ%{ėZ„bh–Yí–(eԜû)F7Đû*äfaƒ‡ e,2ē^B‚OĶMá{ÁüyŨ““gϙģqÛķ[ķĩoĸš;/?ŽTÓu›7ģ_Dë„Õ+WhŪ i‚CûöĸøÃ-[ķÚągïčąã8ļeãz ŽĻŨ ŨþØģ;++ˁ9%ĸtåēĨŧwï&ĪŧeÛïë·+‰™ž9}úĉŋŲņĢG/œ?ĸ(=}î7_Ģ‘ļtÅ*ķ§ÖŪßTŦvmņĨ‚ƒ~srsvï܉ ũwíÜîïx75mĸ°ÎÍ7Ū§$ŸOJÂCOIđ>ōãPÁhH#tŪ_ŧöíœŊä]ūrõÆ-ŋ­\―–ĪĶāfĨŽĖí^ŧzubbâ‡'­ßžyÃĶ-―ßxŦjĩ°‰“&%{ĄH‹ÆĪHƒšüGģĖ2KDQØÄÓūT؇GŦ―ŧ}ü•˜šÆ/cSÓî Žâ‘ÓŪ1ŊŋüĘŦxy‰ûûŊã4‰\H:7oî·5kEėßŧ°āR%[·hž•éãōũÉŪŋ“#•’œ|hĸ>ĀęāfŌũ;wxÉÛĮsQΧ?ˆ:ļUŦWŸþÖÍ‡Ē›ð|ÜļYeãïĪ͚Ž2{víjũęËt6#1ëØ‘ÃgΜē­[5ãËĀĄš/Pžš.ėÜ9|âÓŌzÆîģ~͚#‡eĪgPģ°jŲŌ=;wāՂc—ŊkØÐAĶΘW{wïBRZ`pÐĘ%Kĸú‹<1nœhFĘõdčÜŠyðÄ~WjęþïžÕīY‹°ðð+—ĸ9~ôHúÃôŅ#cņ‚qŒđ I°Õ åÅÏ,ģĖęÂ8pī$ŧž# ÂÂVØ ŌîĶ$…ģ*ôeŠģd"ášĩŦA ŽÄËcļæt"uxôðaB ÔĄæåä^ļ˜$-rEØÛóŨņcQãžBóþƒ†īkÛķFš_õ%ąZēķÚÃ\E„b7mú:Ų0m=ëōõE*ėnZ*ũĪūē4åWŊ^Mūv €áÃJxŨņ4é\“›~ NžŌĒeK<ß5ŦVPėĀYüÖĮYïūĮ%qüčQ]B~þ~”H\ŋ–Ėŧļ—ĩuóF–E@ ĢœKXC„fXFs–đ< ˆõ €€xņ3Ë,ģ2L·m ‹9Vĩ*/-ÍēĐķ·vną{ûúšt H2e9q>ðIã?zôČÅK9VŒ6ģ˜Ųøõ˜BZóÜ­ųeĩkÕĒvĘG“hf4™.ÛX܋LļH{ĐŦ‹IyŽ]%˜ ~q ĮŒā2ė*ucÓa^ĨŦãĢGÆŒąjųrŧM8ËšŽ óĖ1&š|ĩ…ĨĪ ðF„ūŒ ĸ‡æÚW––š:%1ÜĄ*č%Ũ,3øļŒū ÉąĪŽq§j$!pkōŠJ,pl+Žf™eVsr5-cøßąĘ°  ĨnUT_Īŋxc° d‚?ӕPŽ―äŨrüč‘sfÅķš:0ŧ@Rk =BWøŽ5ģĀC—˜øO>éÓŧŨüyßŧĩKkdÅÄg4køŨ„Œ9Āc<Ŧ|ÆYŪPeš$æĀœN.Ī6$e=läˆwßÔýĸcï veg’ÜL2"ģíĮ6üpîAwĸ§”Aũ•ÎðvœúV!• mX•IýŊĸđö^Ŋmļ§ą^Ŋņœ:ū%Õãˆ`POí!ø›ßKÃí‘ÔóPŠ™oIqÎÏÞDßŨzN—‰`$ņáÏ㠐=“+I]uČĖ:glQ{ŊšCĐs.Eüą{‡ðZŧŠHúÛĸþÛxâ˜Ī=7YFH6ŧŦŧŊŨúWGļýtĮDb6ĀqgJŒ™6ūĨžŠf†ĄĖ<įH‚™Ũõķąũ‹R]ŨŠļ/^câ_}ĩįī"#âđ1#Ūũ5RûĩŦ›"MRÕĄ?e`ĮÆ^ŧšŠ*#ŅÕĪ xŸ ĀkmÛ=õĪ+ŪύSķ =å^3B?Qj?|øļZ{årûJÓMjŊĄōTwPŧF*`ŨÔ^ëuG’lĮwJ`Iõô9‡ Đšą­ˆ™Đ: Ö~ęĖø―JæĐ‹A1båļE‰93lG&Æ$$ô5ļņ~WÍŌžÞWHŠļĨS)eĪČr7Áˆī!JŌ^Ëöó\ķŸēGBc\Ũ%ÅÞŧg DÄZŧš-æZ”š'➁Ļsü@>;ė™)áĨÐˌMx)Ŋ3=Tf>Ö ÝMˆ@ë­ČĩŦk- ]Ó5đV(N€ĐôLwGƊõļΝĐŪ™]ãIuÞ „tęxœÁ8}"3 ŠNfF„ĮUÉÐoņ•ī2 8įrÍï`Ũ>ï“+#cšŧ'#BQU "DˌĒôČbäĸýō AÝFøþ!ƒUE" Õ9ĄPÆ“ŪÅĐC‚fD^ŨåņڋĪïåâÇ?ŪŲË[Ąé6ðčWuiïÕS‡ūŋ&05ĄÏxbe(lũX!Ý·Ī†T_ ûTį^đōŠ v†Ī8UÝĩ֚iŸ0tÆDIá6Š  g?ÞÆĖÜ:Ōï™ųøVÉPŽ―tĩĻP`æIK"Â6lŒi‚”Ā'ŽČŪ&i##Nh šuÖ"Ü€™~â㕆įŧ(@fļŪ Fďė@øðás0·šV.‚cȈéŪëØ~­]Ýu:$$­•§ŪîyΰŧŦîČū‰ J6 fŪũLD„tŪ#Ũ"xŪ ĀĘMŌ7ß1+7ðĖ•]=ãž­éSAŧũ–hC QÝ-ę7äː ėî6žŨšÕ œsŲ^kïýš™Ū’äņĐ#RŌVāĖĻ.Ī2tęØū@Á°įšŪ§įwf`XkũLUŊĩ`œ*Ñiûãj$>YmŽęŪ)3ģŧou[’NŨÔė―#Ēęx&#æOū ĢęĀQĄš!CŌĖ|KOWՑ(°O#)Īî"”ÓýþzÏ WvŨ`īĘS§j`>ŧē@äJŨõ†ųÚŊsÝj8•ĄŠz$5$ƒÓčŠSõýĒoZ!“ßĩßÃã9uÍL0 \]$#„BĨčjR(xųŠE`ėS%r8ėõZ‘öØP„‚ïũĨG|ÛeāĩÕ)?Ūöōá“ÕēŅ0ˆq CÐą‚fũDĘĀŨû-‚;m'Ä\y―/ˆ#ÖđÞ6%ŊžAŅm’!éÜųl*$―Ŋc+Öō`Q™15KJfWa°s:ኈĩ‚ÐĢđ ž>ÐNÅuŧ;—ážMb]ŧ{@bÅzŸ7ŒŠĻ*IĄŒs.ōÉ){ŊsōžLœۙYŪ3īâŧ ķ”U§gh"Üv„0Øį€X‘=ʼn~Đ%ĶGō+_]_$r…åĐøØÕ§ãJ+ęT#‚ŨŨezå"pÎĐÓßo~‚!xÜĘx_ɌŒĖs‰õÚÓUc‚đrš{ reötۙšq՗팘†}lîŨkzĶFĐĨ<}ÆX+gƃr%CĄŪ͘™§ \ëÞąāĖMÂîĩTՙ:ūÅš§§œ €ÞŨ™éûĩŦqŋmcŪė §Ēę %CŨû’H…Š‹–2œs‚Z™ï;-!‘øðá'ō9-Hë}.ĄDÃ0E Ũ9”ÖÚ.{zú;„°"wŽŲĩũ+2Škz$uhĄė.Û0‚Šš(ÞÚ ĻĖėĐęąs:Õ-‘ä`Hí_ŋ t—ĮQįQå Åõį Íë\S)ðÔyNĖ rŊ™ɐˆw]<ų,ŠßÜŠm’”`ÃΕÆ#wöZÓuØ{Ÿsz,I]@qSÝ Ķgڄ:ÝķïL&ƒ~"ŸŽv-?ĘēW(šĮÃP€n[Tä`)rÅĐ3ã―–R§/RkoÏû"5ƒČŽs0TD„CT„`L;#ëœįGŪ|__ÓŠâU‡@dÐĻS$óĩLK4fįîŠÔĒÝrüF:Ýcdæ-zÁÜ9ũÐŒļ•―ƒZZ3îéÆü>Š6―2ŧšĶ2#į:žO'œđÖýZ$ėëzÏô^Iēæ>Ķž~ ėš{|L5á\) §hâÇČĮÕķŧš ˜ö™ē-Šŧę}ÁÅųúęj˜aŧ§ĶÝ3}fFÐĻ;"iŠŅâĐŦŋkVUŸ:1ÝŨuzje”˰BqŠmSŅ3°eÂŪš"™đĶĮÝ3VÏ(īŨ EÕéS{ŊŪsu!ļŨŠ)€áēaPŅ] #âęs]ˆ3€ŨÞyŠėĄ->Nũ]ý>‡RD<{3"bįŪsŠ!Fũt_ÓSĐč)“ũ zšŧ >|øi|6{M€ĩðĮãFĪ–Į‘‘đĒŠÎuVŪ•I[A…Æ>} EĶIĀc“ŽĐéÎˌč%3“Ëī{ĻČČPfÆđNŸÎ\ ?#öLwĩacšÛDũ4Í{6 ÆŊĩ{šŠ=ąÎĖĄ$XU„ųƒ!ĘcG\ÕH­ĩÏ)ĒH@”ÓÓ3x‚‘)Ï^Ũ{< ķ{ČPȞ>m ôą”a?>B(2Ũ9‡ĪķŦK+ũļŦïë‚-‘d{FO{ÕUuŲÞkόO‹AčÔUUŠũyŨéĖ$ŋõ&Cį\U―WîŨķ 2S6þĢėTõ”aI3 8ȕŦŠ*ázŋ§'Ļî{@ÄÚŧĶD`О{ō$yŪryeŽÛ36‚qŠ\™wd\ųhšígÃÖZgj`­ĪqšLÓŧ{2%É3’R:įzŸ†Ï Aü8>|øÔÍô؃6ŪjŒcEՁ-IÐ9E03!Ø0 {Ú%…Ra\k_uyŽĩRŠ*Ąp͞yí=ðéNiïŨÝ6 Â_ö^ķŧOÄyÎĄHÄÚy―áȰý>Ũ=Û/clŊõÍ=p(u§―Ą`áúzÃXđ@œ:”VŪs― ÐïČ. DÕx—á\ï{fÎđFŽëý6@1•ÕgƒúzŋWfæ2XįĖøŨëu‚ļc“tð#ųðáãj)ÍLdNũđˆýÚÝC"2ŨZÕUS‘ÁąĮÎØ=ƒĀt{ú™]] %ļþl)ŅÓUĩũ{zB$ÕÓž(Ģý˜GIšüqĘ"beT•āĩv>Ũgö^ũâ==:=ĢŨđ$ŽG”ŧ=“+[ =iĪkíýäģJEŽŦŠk”‰™ž EDN·aZ™ëûˆGd*ŠkÆ+WPrĨRÎuÞŊWÏD*#ô4CķņųðáÓW‹yTiŒŒÜąŧĮŋísÎØĐ ðūŪéŽÐܐ€bwK:u„â9u&2"TČŨŋýē=ũÄĒ wĮv—m)l|š―‚øĻ1ÉG[#CdzrOE\ßĮbŠjĀ^yú`Lōĩ_ÕMÂFį\įÞUÕīĻ\ųĻ!€Œļ“ŒĮ”R"Hōý~GFH3ãÞÆó\WO‹áI$mŸë’"ŨzīBDô|Ũū<ÚJ)2Æî*IŊĩŧ›bäĶqŧxįJ>! ™™$OĘ1ˆîÎĖéē)˜U—2ēíR"šš Āõʞ@Û@Í(‚͙Áĸĸ€ĸ|øð7ķĸsĪ–āu]ĸþũŋš{­üŦ ķÓŲSÝ-EFtwWSRčÏyÖėŠS†þúúÎߏmI=ý>‡Āã.O]ĘũtU‰Zk}―ŋ îȞĄÔWíŨþšŊéÎĶ‘‡“ŒCöLWíĩŋÞÓÝWÆ?þņ'ˆŒ'XˆđŧqĸüuþyOHENwuKŠKïÎĖ™cï\ĸöÏũó…ĸÛĸû?þõ)ÉîÆ >|ø‘ĸŌ“_ŋ~ýįHíĐúoĸýœs―^f$EsŠš‚ĄU'"ÏØ+WOÛąæ–ŅĮö4A3þÎ2ŧ‡°í\ŧûð7ØuA Āũ`EÕ!ātSōø‘Q‰6l?Âũ|!BU―ũŦúĀP„€ąg”Ī>G!=žéYkuÕũƒ+f@FŪŠņŽ\øƒĻ;VD8]Aų‰úûƚ„"͇A)þO{ũþՑÅžŧï<™‡3Ė0#oĒ â+„ĻÄÄ<7•Š­Ú?#ĸKþˆlíŨÄ,ĒĻ!F ‰J‚‚FPÞ óæqŧÏŲ›™5e•Î(åR;)ÎĮō–އKŸûÃŨĶïi@H%š.ŸÍYŊŨ35u?Zgå„Ē”jooÛėĖŊŊŊģį ĒÝnÏß4G^xĨPýGc/|=[PúÏE`éïēĐ3)(Ņ ßĖēMžđhģœë9=“I?;ą5ēÞdŌYY!„pÎĪTšŪo>jËAJŲ2FČĶoĒŽo1B!lBE-!„PÔBĄĻ%„ŠZBĄĻ%„RöQK!ÄÄþĖ!JĐX,–N§•RŽėY,g^‰·d2™x<žÍf€•“Éäp8\.—b_Åþī!ˆ‰D„uuu6›•7H$ĄPHaĪ"ēį蚇,ŪŪŪ6›ÍexÁÓéôĘĘ "z<öĘ#„üi@&“ ƒŽ$þ,öE+‹xÅR!„Ũë­ŽŽ4ĶáœóAf$l (™ģܰĐũČū|€ŊÜĐÓé4.x"‘`ŠZBķČģZ­%ÖæB™ÝˆF†X<™SX2MX.“I%“ÉԆLþēôÁÅGžŽmÎ_eĀŲl–!Ĩ4ÚŅ4ÃĻ\:ÅÂ_PĶ7ēPz€þmffmž ZéakAJÉ Ûg‚y%Ē3ššxi.Ŧˆ„Óßýėíý5ˆðÂXÖTüûĄŊ'GMšō5īũöžYå°??įBM^―m=Ýt(‰ŊĸQJ˜WŽLÓøâƒņĄÉ܇ïõzė&=ō˅oö|t>haXl"ŠÂŨŪīĐ :ėņŲ+“Š­KS…Óff›GQŧ―ĘY•WtΙ‹\þû— ÎÖóŸöVŲåĘÂzėRIþtã’ÂҐŊg\τ"+Ū–·ŽïÔ/]đYUĸnW#C`ĸ­Äg7#āȔfafŽ ų‚āu:âœe2ÉåčR)2—MĪB9ĐÐđó?>šsÁ9*ńÅ*gF―Ÿ6ÂĖfýæ!Õpb -ˆâđŦQŒQCQŧJÛbųÅđH,Ýŧ—r ūÓÛpå$ėÞëEĨ1ķ<322Ëe­Þ†='jܖBˆB>@wTí|ĢÅzûĮ Ą‰txņōåáõ iķųNyŧÎg‹/?úũðh4Ģ;ýĩgûE~‘B&—.]ą6ïëŽW Š…Ô+Îj‹uPØ@‹Áï@áãûųėø•;!ĐĨWC™Æƒ'ûïÕdrâÆÐíĮq·Ëēš‘máZryęۑ‰DFŽšÓïôiĄûŋNÎlüM/čm―üólØäĐë;uĒÚi.1N!EíöB>UėßõlÆæqy=v]Ï)`œsÁ3kÃCWŌÁCįŽ4ütõâ…KĶŋ}|Â*ŒzĀütpaōÖ?~‰ĖËúÏZ‰ėíęŪô˜Fūürzķ―Ęąãę… ĸÓýû„’vĄ€qŅŦ_­ŠÚw[k”TXrĀŊģ$‚ČōŋžîþZøÍX2y0ģÞņÖÉĩó#cÃޚ:ßÂČøĢdï™AmåîÚjYũîŦvŦoūúįŲöÞ];Ę\uĮąŪĶđ.N'Ï}885tņō ĸ_ũsÄŌÃÛ^QK‘Rĸq•D ō%‚į&nÞÔýûöÚÂĄ°ĩpŊÏį=ļgŨãëVŌĮëm 8rD°:―uÁĘăđÛũ–O6ÛVæĶîþ”ZKë™NÆÓ3QĮéŪ†€3'3)„þčæ7ÂÝüÉ'=nRák. ”ėˆh•ĢĢ”čÝučÔĄVŪžã“ó+ËËKp·ĩTWiÕ~œZĐ ›yýÁݙôF,ÃÜYi­ð8œV›ÏČ[ËÏˑd2=víZd=!sŦYÕj.ĩ4Ŧ%„n‹= MŪtdjnvig{U‘•ų„ĻßŋËjŲD*‹ 62é3™8 rdXāŦk:zĻjýɃ_'ïûbĄ‡‰ŠÞĢmSÉEÆđĶ™m,O$$:”Ô™…šŋŪ™…į~ž;Ýx™nŲm1@Ífu‰ÔãtNVđmĄ•U=įp˜0ːĄĖJ0e3JéVģPœesJĐx$+•ŅïØwŨï$hŠ" †JJ@А ģÆm;jūyČnŅ7k°™ča/B(…}owúĮŪ]úæęũã?ÏŊF5!*žÝÍŪ[Ģ7~ūģčŲs(`Ā P)979vé_ŪĪv5U›KĮVgŧ·‘ˏæ6ļĸx‡gâûááëßÜøa9šSšŽĻíč>ī2~qx|Jg‚ģ­Āvøkj-Oی\ûiâæÕ‰yg}‹KpMãņé[#Ģ·Ū|;Ž•mÍuMõĩ|~lhäęŨ_·Iýž§‚Ëdxzj&‰Ū=1ĸbn›ųŅą;Ó+­öUÂė­ŧÓģïĸrØ&PÔBû\!jĶķîóŸ}ÓgQ‰īę8ÖwĪŲÂ~ėÄ`ρFSŨņóÝop†…r%‡ŧÏv·7ųvî:uöýÓGZvwöŋĩŋŲĩĢšĸĢö7Tqƍv·ï°Y|Áj—ÝÚvž§ŦÁYĩũČ{ïÐėģ*(5TÃët„ ,î`ßđOÔ8Élˑ“ƒ―­‚ĄRhŦk ļLîÆƒgΞņ› ĶýØŲs~§ģ―ŧïƒũk]öýG{úŧęmîęþģį6x2ŌÔyŽ·į@ȜŦącðsM~j6•‡o~xī€@í jŠkÞWŧ0ŋ9?*Đ@ ›kÏūD&8JĨŸVsKmã …čÍ/›2‡w§1ÎëQÅL4·vž­’Jš›(ЃŋĄ%ĀPJ‰l  Ë_ÛéŊD!8(@† LwugŨ†  c–ÚæķÚ݌įWx•’h ėë ēdíÁöʝˆ Ītōĩ KÅ^ŽvĖ%d{R.,,ÔŨŨ !0ŊĖgßBˆD"‹ÅŒ1ĀóQEĢŅÂ[K<ïõN? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/fŦ>6ŽĢŠÏĖîí}Úw>;vė‹]'ņWįĢNQ Ĩ* īI!*ĄŌ’‚*·ąĒ(óA\*"þ*BŠ„Zņ! ĪP"’šŠR+Ō€‚Bl“ΉėæÜģßųūvgfw–9Ŋō4ÝX%*ņģ4šũæÍ›ßüÞóĐúņVMúU­T DļÕLĸķū|#l„ú‚˜ĸ'üHÜ>?ęįÜņ DĻTž# sf˜Ĩ€Y6Žr˜ÓÆH0Î-NĐôBĀ„P<Ũu]ŧ 9ÅbrīmûfČ įXg Mĩzk›hŅ-„jęâF1NĨƒÜÂį 'jVŪëĘŅģʑ1æ…õ’‘ð{Į›ĮĪËĒ+ݟ˜[iyóø+éĒ‹0!ČļC4~éí“Ŋþ•õ ėÛ·ĸĀúU-z8đņņ­ûķ­IūwîėŪ_ýāęEí=îčÝÆT˜EaÚĸrüä;ÖSŋĩũévîčŸGļãޘĖĨÖlÜÝύŒ\›`ÜãT’čb‚„#ĸÄDz6õ‘MÏîíŊʎ\›˜žļza*īþËÛwôn‰ãĀݟŲÐÖŌÖŧëĐŧZ“mwú™Ý{úŸØ4sõo“.™*‘t—)ÐŲ+ŋúí‰ú{·ėęĸbîâ[gĮg áåĖdûš-Ï>ņĐÚ ÃäPsņýoþxėg?øöó/üüJVä.ŋqôĩᇟÜūõ‘îó§~ŸÖęÖķ·uÞóp_ßWššU2%!6·^pŠ~ĘÅēUÝņôÎÁ};zýØðDYÃ.HWՏ7ÓžúQ…Ą*d㍊›ŠX”•§pˆsģzŠš4 0AÍJHłdĒVČ&rýoĶÛįGšÝ>?„R û―åP*ÅŠâZcn]Š;ŧðŌÜÔlŨÔR‹Īå™jŌápXŠ2‰ČöZ]]]UUF€\FØ„ëÖÄęv &RÝĐOÅR Ģ ÉHu5Ṋēũe)“s:ŊËĻBQĀËGšÁCJĀš_·š78Č8°ÝĮoŪr y!2|zÁ}z~ní˜āOþüĻ)qÛąŠųhÛGïYÞđėūûãĒd™åsïėiuĖž­ŊYĻåË&nP#2Ē:ŅtÆ(ģx]gÏēa&gķÃįēĘ\;ĸōũŋũÂK‡gLĪcGžĀtcIGŧÎhSgŧ,MÎRŪÛš: ›5vīĢ89k!L\Qq–ō@›É•E+ęÃļX,Ę Kđą?yųŧÏĸdhZ3*šĄĻA„°L“Ų&ržIëÚZŦƒÔDŅ‹ëËīh[<ī°m͊%ØqŒH%—›ũå•V‹°—1il銞;[ãÁĻn›Vý’5›zĒGüC/ū:U°„ƒt fQZ9ýÚúé‰3H Øs\™5ķįĒ!$hĐ`ðÕÓŊüčĨã$ڐ0c…PÊĩËȎYĄÜ{PþäæūŧŸ4&ÎþæŨĮŪū›fþOŋxņ—§FŦ“‹0c’5Í@Ôīå“F•øØĀ’𛄗,_'QĒôŠ\1ššÄq1Æn…WLlAâņ™ū˜Î2VÎÎäFÎŧPŠ~mŨķĩĐ$"•I4ÄÄ,•É{}Αp$;6öîtĐ<û^úZ&Č`ŧēYSÎ’é Üær‘ķʁHĮvĐTī8GĻŌē Ô^ũØÖoėz4?|æėč d;w(ãfvôð‡>öøöÞÍD‰†]ls!“–ÔÉĪå·kÄŠ‘Þ}ßöxnð™eu†-ķqĐP–üĀkĖL\ÏĖÄR+š·ÅĖÎ4ĩķ„Đõ_ęÝ?øõÁ=.0\.ßb*[b™ØL0Îō™Ëį.ŒĢŠT …ôXÖĪÓãĸĆVÕ·…ÐŒ †Ðøû$įӏ„ŠūS ūTĻu„‚ÏßW)jbÐFĀyޟĒūŦĐjø”?/?^æóōãY?<~ 7ôÆŋHģšžĶĄ0üëôE>ŒŊ~mmŨĩl™‚ œY$B‚X  : Pl8YeĨ[ŋX1þâ%ņGxĪæhÎ.ļð―8yrúžÏ9}ōžWĮ0LÓlÜUģŲŦešÕĨEmzōú㇟ĩęyŨÎfūdfŠŌ°,Ā4ØbYVĨRQUž‰ÝÞÞÞÜ܀ŊårŌ ÝÐōōâÛbēڒžëå{.w<ņ͐Ūk ÐųÃŅu°‚vØE öƒS þŠïŦāˆ@tÁe ĸÞđp°‰ļė€ģÓˆó.Î^0xæÓ™k5LËnÔŦ)§ÆŌó·åkĨÂč`x˜bY‘ÏÏLũøý|dęĩĒ[PÄvė͗OŧÃXl,#BdbŠXW3 öðq?ĮĮú{ĢkZËU§Đ‘úy!>Šæ‹šï;đT$)ø—šHËû§ūgŽdcŋu2ĩ°yļ-ĄNšð4ÕӉmŨNö–1ðŽ?.Õkōlbl}įüŌ1ĪgĢ8Eâ-Ė]4=eo=Ép;5ÛUø‘áĨ­’åØA Á'f,―wîĀ&fvOmïŽģž]ŠČyf”ˆ AM–UĮ6ÎgđÁ‚ßÚ/fäå“ãÔ`§ļīqR=ā(šlķJïĈ°ę~ŧRåMķŊ+Âr,ËlTLM~…‘Ųšú7ĀŨÕY§hgÂ―øJáäęĘýž6ŨŨ3ÄÅ9šTK/冚ŧY1óUwĨô“ÐÎ ąh8"æ=ßYMttôÅã<îZؐ=ÏnOf{~óÃęócšƒĖ <Ļöt^Ðų‚"č°ĸbÖz^Ģh‚hþÓ\ūÓwĘí‹QņŠˆđ|ˆHTPPbŒŲdĢÓŋ§{š{zOŠŽŊ§ÖbÝYcžYČP[õúÕë·ĩäā ČÏÍŋŊЍ?xnô‡Á[f ĨÐHĪ”P”B°oįžÞšyöāū~õ2ƒÓ€i­ ÉyJ įóųl6;??*`ŨcŒ“˜Â‡‹ŨïîýĸfũéŅíãŲ Œo=ÉXErNŅåž$€Qda”Ģļ†'Ŋ\YÅģŠóÄĐ?kzŒY•ąv ú Wo7õä8Ëü|/äSðü?Ōäœ|hGӆļ(I Ą­ėĻtÁ ­sJÉÎXšū$šAŅÆæ-ĐŽG`b;ĸlïž:-]—‡’BûĐVŽÎZ-O%­LŠá…ēąKđsķ ⭂Ð.ĄVb°ŌļZ4ōBęspRĀ}KiąŧmX@§ 釅wŠĘFQ6•ĄģüåÁ[_ ZõXßG,Åôī|5SÅūïŠWrÄŅA"áCÄĨĄB9KŠäcîšN+réSMSÕīÖĮ˜úޒ=ä6ąóZ6óųGWŅ.â% jŅķÁíS#Ô+āCέUÆw‹ÏCŒQđĸïöÎã“ÎÕvCųæýáÕÝļ?„Į]\ē?ž$ †w› |œņžØ”óïkm/0ãY*“\ōûBp‘åM_x^ŅÍTSäá؁+úģ…ŧ5X Š1GŨ—‚wðÚžÃbސĩ‹—u€ČA—sÆĸēáõą/ω-$| /ũ UĀ ð7€ŋ"'ĐKlLKƒ(óDâgŲĢ<ŽšŽ“9Y0ÄĖ`~r0ZEōqŪlôâ”„eð)–ĜԝúÃ#6úÃķc‚  rĨc—h%­Œ5ĸ؟FŨBŽ+%/îėíόąāĻʋŧŨũž―?ģÆ*ĩT}Ču *dŧ6Ļá_=…a@€Ó>ŠY äĮēĒŽI0)!Ixēoˆf9‚Ū@ JR,ĸŽ’–†r]ac 3Š0šļb#Óh(€RtØúŽ,čģzÄ ŽĘ(§–:GÖT€N jīāÉĩĸnV>Kē9þ#|2a•Ï?āyžÉá§iÂÄãņhƒ1)žqЧ˜@EoJŅ]ëŲåWSf#zĶaiũčœ2ÐR}YVWfcŪ->x,Åiņ‰ēĖĮščŽmņ1ĻųžJŽÅĮĮÉUەl<žæ#ĸ'Þĸø6=s8Ė·ŋøõvŋŸrU>!žĘ‡ âƒzDKødūÅáSŊðĐUōi›•e͋QÉïMÂĄü*M•ï™ŧ‡ŧûŸßMÆGESĐųėũ{+m\åƒo―?%„wņņØâ3aō™W~_ˆ->Ø2§eČßóáoäs1Žã0 ›aã‡#ūïú͓mų}ŨuC?l·ÛqûX·e7îHĄPF`Kē(Ž"PT!pÓL1[ļFßũÄņTڒɈS@##ž3nå.ï ^īĖ―zVāiôz>ÖâCV“-%ųÐH™7YáSÜ­ï >!>X›ïĻðņHĖ­8ŪōÉ(ųėÆ‘Æ››ŊãĶāÓoPVųØ>ÔûĨ3ö>Ý+|äcąĒ|°rrųþĪ“O č-ŋc1Z_ðŲÕïO–Ö|pĀžĮîKg‹ï›LZ|2°$F Ēô7ō!sø`Øi|ôöū…b.ãĮ‚ž?Ą]ōA‘Ę2ãšĮÓø †“1ü >ø‹ÏÏvõlŨŨŨø?: 3KŲ§'ËâēĖĢū>ÖõĨˆg~]ÉU—o•i]^^ķ––Á:ŽÍ§ĪWōQü>ÏÍw Ÿz”ŲšĘų`/ø`Æïâc>ˆËKĢīø\5ĸãÕĨ!ģōþ8ü||īäó›―û€ŠâĘĸ~ï”WÄĆ-vM4‰‚nĎEcŨÍÚPąĒącÄØPDą ( (Šb†Þ…Ũž2å‡H6qģŧ'ĸėŅä~O˜yŋ{ïïņ{ïĖa ĸŦ~Ą$ö?đ>øú`PüÁ0 ðßĩ†aŽZ Ã0ĩ†a8j1 Ã0ĩ†a8j1 Ã0žį0 „ā7"ąD"“ðĸ{qâĮÅÏĆ―ŨQ‹$YFАĐ4NČ=ð$-ŠŦgO;§ãÉĸ"Ąā_f,E„AĢ’É”j―uu>4ĩ|7 ÃQ‹‘äÜŸ6ėóÞ.n.Î―šö›|;K'ĒÉ_Ĩ IręāÍk·í(2’ĸ)ž5f–ûuĖ’ÏJŽņņîõEogũúõųŽkW ũJHšžį-f“°ÁwÃ0ĩ$Mi>Óüã^Wv2vœį€Õ#L _…b›ęĮ ŲÖĄjÞišģÉb‚čČZ ­#ØŲP$Žd‘DZ}ŌN*Ki6ýÞ9ĸŋ”˜l$4øąˆ|•9f⎈Ø,‡Ū.“gĖåŅĢuSÃøę–ÚØUŊnKS„ujšú㠊ĪęØÚĄWÄ4 ŠšG[ąĩĩ$AÄ4-Š.ąUŸŊyÉĶnugŠ”ČF*Ĩ)RTýR1?ĒhąmuĩTLĸž˜",–JhˆĘ$RYZė–Õ~įŌvRŽ^H"\ZH8ƒa8jņ'īUŠÂƒÉąKß#Ûv}·ïØũvũų؎å ūJīČËËëï‹Ũž.7’Ī·°H$o‘EïY?ÁËkÆžĀÔB- DHPÐĒŧyzŊprúÔ―‘·KģŸ†lŠˆŠ:v$,æfKÓ5)M™äĮC7ŦØŅKw]=stã*ŋ PôĻHÏœPQ‘ŋó7hõŲ –Ý{#')4―ņîųðĢĮĒ2s^ŽX8sâäo.>Č7Ë3w.™0qŠÏ•ô2‚ĒIĢ,:<ėØņK)É·˘ðõŸ„WåŠ[’Fõå°MĻ1Ŋis#^ðh?$ųöyüîā#3ōãŽí˜0Á{ýîïMmR4Č||ÉŨ[XĸÐĨŽ$!A–eÜÜ|äeNÁõĢÛ'L˜|ꡙYÔE{wmßđïȨ$Cņ&ųА“„•ūŽŒK5AB€aĸ#ŠũĶԔe^oÖHHqO/ŸŦ3dj^§•įgޜâjo+­Ũąƒ­TÚīŨė”"•ĄėiŸnMl›ĩĸþĩR_ņŌodûzķŌví;HhI[7ïĪ…ĶøÅšŲmiŠÛÔŊW·u'ß3‡ڊõZLY,ŦŠR)j­&óV„3 €Ý°ŧYrF8ĻÔj­V››vÁÛŲQ"ÛĩûԆĶë7i―íü3cUŅ—ŪbąÔĄ‰Ģ­Íi[ŋqģ–N í(@Ã~ËxŦÕ'9ws‹m>úĻI]ZXũӑŨ^”k 3VNu—J%íÛ;IëÔwēáī\_õ lžDd_ßąĬB’mÃŪgTY ÎoîØČŪaĢÆÍ;ØØ;.;šĻ7TÞ;üĐXTŋaSk1%ýâø­Ũ%qn­l 6 {O]—–Sš —”ĶęõčÛĩMýQþA؊JĩRaõ›―0ĩRv-lUgG{U§Áī€Cđ2õãs›mč:ysYEņÁU“ŅKcžËS­Q{5K›đ€ ŧĮøŽrÍŅåhóėņÅ­uhðągüģB•Ē,33W#ËŲä3 ßu=CĢ’+jhīęôŦĄí \e–)”ĘŸũóöß—ÐcW†)+õÔ§@óŪģ^ĐKV;wPÔÓkQŌÓ;“[Ēīĩų|~Ðã‡ßOrn Û­,Ĩū$ų‹ÎI‰íŨKff$ÎęŌÍ~ãÎéh;͇úŋ– Ó.ĩvI›u>•Ķxzb aĢÆÏ^O>ļn<`þþEQʂá­ ÃîįfÝnÓ@ôņg+ËLæ‡a‹([ī—|`í8Tž $ZkķÜÜãÝĪøæĀZ-/y1ŊzÅíKoååē ™\“Ã7{a€åIŊ1Ï­›įÅW*o\ļîhü[Ĩž€Ē{ãGßzâ*KΖŨÞ F 8+‡„ ?5qĘØ;ϧPž_QX.—=oR6 9 jėØ@"|*BChõ‹Ï‘ $Ņ)^Siáĩž5ĸ@]—Ï?Ąq7ũ–ÍęčôŠ• ißÔkķÏíÛķlkõšĖ™ôYÛķM›4žŲ: ĮrvMÛN›?ąmÛnCf ”·hôĘ Ey%jlX_ŅĻí§ýœĖ•Ķ’  yFô‘ĮŊÁݚ:ĩ@ÃIę5Ęō‚bÁnôtĮ•!%seUēLĮ€eÄ3ŨûÔ­i ĄBX}1Há?" @Iô:ķ(Üēt֘qF'3€Ä·'`øģZ á9žhÝkĀęíĄa[f`Ęü!ŨČčĮîƒ|W­ßčzŠÓzÏΆ(DeAŲī]7ĸ€ĩAû"ããã§õ _] ,Gą$’ˆ%"šŠžœĒ–‹Etmä ë7iÖąÉŋŒū˜øŠK%húCĮp,ÅR"ĶSÉX,€“PĪ0š€€ä8 ká8ሞ3ŽðÖØģ‚€‡ IĻ“W­Ē„Â`ĶÄbēfÆĖó,M@žGiR*Œðëš·8žäҐąs|ūÝô},z ífĘÂrS4Q[l…JQ3:=ƒšįĄtčŌýqŨNųŽéþúa|Āâõ·ÓJ š†ĢöŊ~‚ėeXXä•ŦwÓ_>Ï-”Ģsqģ֝›Ųƒ7Ŋōå:;û:*YiBWf,cáŧõÓ€ĒâŠÜ"K={Ž°HĄwlÕū‰ļļcĸÁˆóĐĐũŪÄĶXŌŽĄ1ąŅŅO^äYQóޓmØšĮș“IFđuÞÄ-ĄŅO’Ÿ=š,tũå'ĨÝŒƒōč3ĮnÝđ}!æD^ĐŲĐëØ hŠT^o`År,'LxFČt RŊ’Å_ŧō á\Tt<5íÖŪsŦ6;ڂӑ§/>Lš}åbBNĄĻaûvNuY–ŦūW Íû#ÛõšvęųY_Ā›Ó3 HQšSåH@(f­ËŨ‚,k6V‡Ï&žĖ5Vj]ūĪ9M˜ë?ū'dŒĩÁ0 G-ūŅf/ņýfôČáÆ ÛzđÍßū^;ÍĢ{ϓ‡·õŅ?X:mėā!CÆNœr9ĩ˜$H†áxū‘”d>žŪ“ĪĖoö0AĢ―§GÝ)ėøåøˆ€nöÏWÏó>|ø„oŲšƒ†Œëé`ģĮßgÃn-ðœ؎œžî›æDI ß”!C‡æđpųÎ-ŨßËoŊïĪgįƒ‡ 8guTυ{vNoHrË1,ÃA40H)Д‚$) ðuŽ7V$aQ)öÎ[ėé9įzū―ũęMƒzķčÔgtxðŠNŲ—§ð=ÝŊnŨa!ûvđ8‰fp–æ…+!Ė‹pœļņĪ›|'šĮė[î1hðĄãæ.?Ąķ…9ĮpDmą€ĩX:ļŒöyĢžĖÐ)GKôæļƒÃ>jâ™$›‰ó—ļ}ڔgX€aø„ŋ6Ĩžžčņƒ;ąŨ.ĄGÃ]ŋuũe~™FĢQŠÔZ:;=9^xhnü―‡ōŠÞŠäåéÏRSR_•ÉJĨJŦWg―Lŧ{-îfâý”œ‚·JĩFĢVe>}tCxŠgüÌ7 ĨJ)ÏL{„]üčIšLĐRüDŪD‹(*^>{t3ö2zļÝåk7>yVT&SkījeYōƒŧąqčAáē‹å:ZДįf<͊ė2ąÓĖóŋę\ĩaފ ŅŅ Öå!!IQ4BQīūíE"š$ĄõÝ-Ē’ ŽSQt5ę!æąRÐzh‡ĶŽodDN •˜āyž@"5X;PX4/=nãĶ=•"1IÔþe&ï]>rôÚSTN@ tŽ+ôJĄ‰­ ŸMø‹ŽjfķöĄuĢĩ• (í}A"”UPÄO/ -‰ŽæÝÃÕ5$Q{ëŠÖCŠæzh”Aęŧüz9'“„īОu#5 U;ŋpĐ)øÓQ'$„Öšš―“ĸīk’ĶŽ‡ÂšimH8 ë 5ßĩÚÆDՍ@# ^ž_Κ>Ŧ-ĄÓVYx‚`MĘĻm~înýGú‡ä€ˆ`böŊuéïöe?ïS·óhšÎ|iڀĻ`ĸũ ‘Ø ģÖÛÓÍuāü}WLM@`c`TžÞļÄÕÝ}ôäYŊ5ΈdOïÓß­ïŨþK $*âWĀđObfšŧšöŠx”‡Rœ0*Ïíöwvqéïę}ųnRäķõáį#<=7―V0„•DÅëÛaAŧw~;}A” JH‘îōąƒœûLY"ģ’$r’.Lwsuâ™THdíÏÃP[ĩÓŊo?W—9qÏäŅŲ0s@·Ŋ†ūYN‹éâ”ļíķŪïė6tũåÄ û9ũu]ąũŪ™•>Ûē~ãĶ cúöîŋ6⥊Ņ; 4|L› 4ž(ųúöM;wŊŨũKũ-RYJ,‚æë‡|đđ ŋ,ģÂDZįvŪ8rlïčĄn_ðųAÎå'ŸýîĖĨĀņ#‚N&2•Š]óĶđļŧ{͚_jē@ŠĻüa―ŨÜKéj„UЁ‹W‰ü'1ģGũssíŧbũI5'"õŊŨû‡ähYŠânZvŋE!€„˜ÐÍßt6ĄEŲåģŨž %âœØŧĢßf=˜ę9ØÍÅy–H5CŠ·gvŽ8rt§‡ËāQKr”|NŌ™―g.m7bwä=’BhÖߌ;vþČa?ÏŅ1Đ2MÁ“€ŲCÜÜ\g,ۜ­1Ũä;$HBUú&Ŧļ\,fŊmÝĩ'hՌū―ĸýR† ]Žg7ðpuó˜t)]ŪÉO\ŧ;rÓŌģ6Fidy ĶŽAģyzoČU0$ęŸį­éĘsrĘâĻÅšRg=ŋļæ0į4°Ģ _ r 'ķlW>}Žsə•ŧby:ut=ģdpÁwë.T‘–“+6=í9ëôÉãÃzuĄÍķŽ.î2>"tiYĖŽč‡…MUŋ‡‚ĨIWÖš?sWxȆ•mȒ'‘sƒnĖ=đĻCÎŌÕG+I9+æTÏgûö\ēqïŦ U·~údi`ȁ•Ý;:õ4žw›në֏oQ—@roßžK?wŨŋđXķļŸ aŦK>{ņąaÅ6ĸĒ+ŧnŋŅęįģíp^âÓsÛĘ­9j3BLąĮ—/Úpč™ĸö}{æwoC_=šZ/ðHÄÂQįúlH.0ršĒ‡ë:zû{8kęyUũuӇF†ōÖČh ĢŽ„CŋÞâ;&jŲō+ß\ßp ―Áķ°ˆų#;Ė]ļ!ĨČČꊎ‡„8zLq‰=}*[Į―ūvpÕ)ĸá“#ÛWė =k Đ'ŨöíˆÎ›â·Ē!!ō|Z“nÎ]š{ŊÞ8ÎĢGABÔÖGFŸāˆ UKėH†ē†#ë7oĀžÚyô, č”ČÃŅĨõīũ&ÍŲÚÔkKøĀ7Ũö, ū-ēøŧ‰ O’0}īYĒ%xŠė2ũÎ'<ēĻrÃÎDo Ž7°†sGŪé /qXðíūß-7Eïŋ_hP–GßïÛS8cųōʧ.Ī7ûäËA]œ&lðtï …īã-éŌąGïÞ―þūiģkKýÂoü2;~âHýŠ[Ӗ…éXû#Eųóøëéy’Åiwöï;ÝÆ;pĒý›ÃĄYķqkPÛaó"Ž:·ŨųĻíЎ-<žûMu~ōž€3§v6. ‹š” Ī4ĀpÔþ9‘“û2öVžĄ“GxÄ2'’eīé 1Šœįþ>Ë2hMV1ĨŒĐxƒŊĸé„b3SĻbéSFŠÎí˜ŋþ`‰‘Зgū~t/ãú ß5‡^e‰25 49Įō :ũų™ý^ŋEû ëHaz|”ŠļpïĘEGoč5ŊJ•@)Yōō^E~iôŪĩköÞĐxĨW+åĐEŊœúŽvéŅūKŨ–MšīnãÔļ~ƒnŸv°Ĩ ðá%v­Z6qjÕĶÓĮMEœZÚŲ3dĸÆŊÜöuhĖqlŅ 4aITК€āŧåŊīōJVČôĮĒNx›ßoņ’QýztíŅÝQbypóŅļ)súvl=ĘkJUÞÓ…:ģĐŨ˜YþsžšĩmÖĄũĻ ~sœŧ4æUZĢÞXÕg˜y3ĮôžōĨģîîÓÄÄøŊĐsút†wRæ>}YŒ†ũ3ëۅÞ}{}ŌļĮY,đåđåĨĐû–ûžšņDg0UU™ÁG]ŨŪĸ•{—NNƒœnÔēUĮĸcï­ãĢļöĸĸsÎØnÜ=„$$HÐ!Á ÜŠŧŧkq/î ”hđ-ÅęH)ZÜâš6vÎųílÐûĄßÛûŧímï―ó|ė3ï9o™Íƒģģģï·KxdÅūãŸ.;rëųį ŨrDUÝÚMî‹ŋÝw)õ—­ŸüØy|ß'_íýękŸP*ĶÞĪÎuî|~š1pŽ+ˑáõĮy*CģnõŌ.\>ųÕŨ!ÕS„ûß~ýųũŋĩjúŽÛ5oėš}Đ( Šücf.šÓŪQRųȐKŽāVB+ŽRxį‹ïó)XÆ/šB ›qõA‘ĸÞÂKFMÐVųņĮGfŒāë/-ݐ&E„tžīdPr―Š•Œ‚ް^Z5=ūeé”%[r1kt)íZ&ē„Ŋ›§óŲŊöŽĩúZz‘EÉÁw=Ũ K­ÎŠŽ”KJ™2u݂™Ã+–pØ@!âÜBĘԛ0yúÆ=›6.éš{õĀčųëëõÚ5Đ>Įō0uúŒ?úŲÆšĘÞ3ķX‹ā\ģy)ģæėĸlSũúe°Ž8Īý#glßĩ|Pýcģ'Ė9™æâSi℉‹VŊ>žŽ/ PJ€“Ŧ2”ė0pÄôy NYä,°|vzŽD‹&öxŠĻR–ÕÔō%D’eL( Ī 4úDĐāäæÅJuĪ<}fiއ)Ð`X'Ädgh‘ qînnųéđŽoފ U•8 ‚„ (Š ‘,ĘaƍR$ŧ ™ =}C=Ý]‹ÝąXTˆUÞîNÏCÍ]UY Ą"–ĐŅĖäU[ö-ßŲ…Ø8Ä83`YUrŠfŦd‘(Ô9ī⚃Îï^ãĀī ŪįrޛΔρå5Œ#:wžÜĪGœŸā@ēóŠ$Mōóē°ŅĀP"JW ŽTÉ{xíJG p@50ŪelŅįÃæŽŽiķxH'ÚÐl—’å+—— ïõ\h8ĶÏHoĻē,ĀČpN ŠŽĻ*Y@ģUīĘôÍï„(yEnjŅ݋ĩŲōó-€Ēü,Ëð Īā5ÅÛ* &<%2æ; šwxĮR·į_ÍÞīŋˆŠ’'Q āČķY§ŌāðŅCŦ‡GCAôũ7gíÞĶĒ|•ĻK­ÎD‘ĻUáY„QV1%ŠÍbÁÐ)ĐkoXpöØéŸ._8yî§ŧ*PԂīG7ŋŧøÍ­œÛWÎýpíČÆÍŸýtĮĶēžFƒ‹đĶ]›ĸâðŊœýæø―ôĮĀ00óÖũWėzœgs杜Y&6đw)ŋ§û?<ņËõïŋþæ' €Šh1[ŽÞ‘‰IInŸî>ríÆå3ĮOĶcŨõZeíX:a֒UKVĸp+ÓÃ; ;í—uk?͑^HŒ·ÏW‡ũí=vŲXkĄĻhjJ%‹Y’$ŋĻĪš‰ÎŸė:zíÆ•ÓĮŋÎ.öĒ„ ũ·xūyŅÄ9˖-Xs>UlÐąõ7G—.Ú°eڌEđqęT-D›,ŠÄĄū6‹•PMéĖŠ(Sčf0ûdĸĖyëVLŸóÄP!đ^|Ãķɧ/]Ēđ/ÎŦŌļv\(ĩK“Ms§XąØlāj&tM―úÉ'gŪ\:wö—›V‚$‹IR1€@‘‹Žē77FÞđ`ņ…Į™ÏŪ~―uįgi…ē įläYP ĨŽģrÛĶ—Ï?jÝĶ­/KKÔęPĐôĢÉÓnZ3sîĄįuķtw tģ­Yš`zŋqŸßū)}鋉kDƒ:.Đy8ķ~LL|ÃÜûéņ›8ÛģãĮïþxþÂwũ3œ9›'Ûk7k…ψēE&œ“‡+ąí˜·øüĢl‘ã-ĪnAÁߞY―h}Ķkl“Dãęås6o[7vŲéÝ:–vaČËīŠdĩ:öÂV‹,+/,2攜]{vūüP坝Œ<ï\.šÔÅ/vî>ð“$Ķ=ļtîÄãë/s6-ËŠ(TR Ä^͌õņŨýš7;ûLxþg`&Nœtþb°,›Ÿ_Āqއ‡Į?œG)åœ=K–ŽōE@[Œ8§ RQáv"*7­Z:õŅ‹„‚ÂĒ*WŊQąĪ_v–ĩVÏ>uĢB87ĸîøÞ̧|™ŠuôsuŽŠTŦ‚}ö#ŨĻØX?wR !‚XÍyœúĖJöÖ'Ą4įÖš^59ëIzÍÛŋ\td§wpDdt°oP“‰îķĖ'Yų‚KXLųŌ%b*ŨMÎÎČÃŽkl… eʕ‹ũÍΰFV*ïÂ9îNPX:“mŠ1&.ŪlxxdHĐČčrnŪ>MÖsĩÚ\Ã*V*íäørcV3!ĄvpVF4úÆT(_ĨzBYîŲã4·’ÕįŽíSƝeŒîáe#C}F(ûØþŌANF—ðčōUĢKÜŋp7ĩ°RL b >nx\˜[HŲ*>|šÝ=žÆžą―KŧąŒÁ―t„æÎņÎ!%˖ ô+Ó qKNFžI -]ŪL‰€ReËFļ Œ‡ĐĻØō!ūÞe*Wr†GpDIOã“{ũrdķųāa­ė­bār,~|įëŦNÃ's‡ÔÐ$ĐVQúÃ|‹ÐvĀĻa # r­]1R,0E·î2Ļ[JđČĻ@w}ņ ,_+ĄNĨgęUkÖkTՅwŽŪZ“*ŲŽĩá]’tŠ(^Ēd™ēeĘÚ ó /kĸŧø–‰‹r†gpDĻ”Rbp‰ˆ Ėܐ˜:m›ÔwRóŸĨÅ·yoFúÂK§”=ËFƆųyx„DWĻāãádôī[*” ōĘÉxšö4#ļ|ƒAÝ[yō(ŽLyUɗÛvnî+ãÕeDwKDF„—Ž,[ķ„‡ģOH؈ēe\Nî~‘QåËļpˆRðŸB(//āîîøøø›đđđ@į/† <2 %K–ø-ÃÉËó $™ū|–QāYE1œ`` p€EQ |­Ņ*…,D•e•ˆ Þī8€ 'pLąY–dBÃP‰JĒÄ–(Ē‚!à  8 €žA(NUYÁ@xøâoTŦE–1Ïģ’$Q íuER0ý?ÁKގŽRƒA°bODīũ„’(CmÉĒh7īDĘŊŲzŽ\aūLÁŋˆ„ĻŠ5)Áĸå:ŦĢK­ŽĨšĘþY`ŽýđZ]juttttĐÕŅŅŅŅŅĨVGGGG—Zý ŋB†aĀĸ:cJ)øũĢĢK­Ūģ’$eĶ?ãY (øŊEY…þÁ‚ čjŦK­ÎŸ€,šũ|yí§ OŅß: JT🃎B`ĩ€üá]ÜíRû+ĸįþŦ ”P BTÛxg!%„ūmĒøC„ ÐJŌĨöEĮĀ3Ā%ô–läx |Ũˆ.(Íŧ{š}/ļæ{ˆáĀ:6UsyĶý­ĸ/`YR)xö· [”žCG‰ōčÆuÆ·\Ļsýį›>å*đą˜ÐWã(Á‚žôO‹ļČē%x€iąŸŠ(ō< ĸœđ™(ãöõgĀŧrŲ@Ļ.ĩ :”H1T’ŨFŒ‰ŲFd…‚Ũ0 #Yåüž…w 怌 €þÆ Ķ,: !Hņ;/9ŋēkÁĮRå™ýšs€üŋĸS!ä FžĢoƂ%Ŋ™Ė&l˜ÐŪ3Ūö–cý―x,c‡—j*īp‚‹›+wãÔūW—.ž(­^@íüėŪëˆ1=݁ôoŋē„,Į__;wžWÛSģŧ ‚uĐýwĒ)V―mđuĒĨ}žĒ Ā4}ΊâĀĘ]މ ޅĒRúķΚđ°‰Áėõ49WĒ„ÚRþ€Ļĸ+uB€ĖiŨoÉÁÔ1Ũ:F\"DUEÁ„"ŧ‰e ĨŠŠlý|ÝļüĻA#:Ԕ$‰å8!ĨDUU ró€. `°WõðÕĮs’›ūzžJsŨķqåXËō€ĢķĢŌ€ČꍂxP)D<ĮBˆ=&oęļVBÛó`€{û!@Ŧ–bĮÜI(uļRû.TKĶÅcÔĐæ]|jU!Je&˜–dĪvcņTKāČCô‡―þ`t Ā61ÜCmÕ6&ĐIxÝFáĩ’īWbãðûO/ä< ­čáU–Rð.(2VöŲ?8dũ íC>ēoHЄšN“:ų”ö„ ųýŋa ‘…ĸB„#û―Ū‡ƉEąjÉŠĩŦĶũŦU#yāamØ"ó˙m­›&&6ęōé•ӓo§-Û5lŸ>ģ?*Ė}<ĒwûÄDmØâƒåŨ†-"„n}ŋwՇŸÎoŨâƒO`9ÁúüæĶ)­ã’WŧgËI―{ï)a™ĒīsýŧķHJj8zû…Ũũ ĒķôuӇ؍^uÐĘōé>iŨ{âų'ŠÁ Ų0}ÞĮŨÎēïýņukvrėfū āϗ―ŋrÕōņÝęÔč8ųŦsß-Ņ,ūZũ―?>b?ītÁ‚ŅujWkŅïðÏĻx„(ˆeģî};&Ĩqb―”ÕĮ uĐýĢŅĄŠėęŒ0ĮŸ8sýÔ7·ŌģqN!8qææķ-{ʕ‹ėÖąĢ"ŠŋŠQ„Ķį*Oēe֍ a2ēå§đJžˆŒT1ķHD”Ļ*5‰Äþ’ßž\"ÉÄ*SģHŽ ĨX%mY"Š#ĄÝôĶÅ,ę1ž ãEÂ*x ÆZĮ2JĀäÅŪÅą[ÖĶP›Dd…ĘŠÓþ’ČËC2)^Ŋ8’ōVR€Ī‹L,Ž5"~ąĶxŨĶRZžŦÅqxÉZ ÃÚNA5Iā/‡&’(íŌ™Õ+ķķÓÁåÖæƒg œ=gÁâāïmÛž0>Ôāä]ŠIDHÃķÃÆôĻ$ܱ߄={–ø§oýčð}`āĀŧ „–‰kÚiōŽ QNwáÔéė]&īŽųdÏÞĮ"Č|tý‡ŸoB?Û―ę‰sÅ {v I •ULČ eĸÜą{î–Øĩ{Ĩiįû‹>đSĒFb|îĨ1―æÚĩpéĮįÔ,C%1 Jŧ}ïéLŊoąŨ ™'OnØôq•ūsã Owï:ŠIÕÎ;mΖŋĸtbũþŋՙģĻgPöĪ.ÓŌT€4=į81uİ)B뱛f·Ú1wîwũōéRŦó‡!%ŠĒä[ðŒĐӇ xņęį9Ķ7 1ĘÃ?”`ųŨ|‰,-<”9|_ÆĄÛrzķeîÞĖ{ģ>IÄĀqÞŦû.Lņ 0åËŧŊėļĒ“O@(âWđaëDïŅ Ýgw /ðNėČ6Ŧz-iįSÎ **uváG―ī”1ĀÐ@įþIîĩâ}f·ôŠfiąLK–pžÝ%Ðūli{? ‚ýœtģïÎLvuCÔālîÕĢ–ĮŌžA‹SÜâÝįv \ŅÕ§rvÖõIðZÚ#pyGïxWĻ*ÔÛĮ0š­–tAKÏēFhU`r-ïIõ=§t \ŅÃŊcI†ĻÔßÏ8ĐĢ–b@ŒAĀvaušėŲŋĄÏŠžģšúŧ &5―ŠÅxLéÐ7ŽS0ø ("ļã„ÅÃSUŊd$™ņhßžņĐ]Ŧg,ߖYƒ[hDÅōþaŅáþn^Ūŋ>4qėÚkÏ ,r.Ą ï‚RŨ€Ð’Ņ.eĘĮ…ųx˜læÍÔĐV|_/Žbāø0TŠ'$į]>>vˌÛĄ—#wqæũŸ_z|ĸÄøąģūŧĸäæõ{€ óáÆÎÛũ;>tޖÚa.™žo&O›u85“gY í2myũ&õÂ―=šN˜ÚĨiĨŠ>|V𠀉Ō.S—wh0pæÏ/=•2,“yĸ§ī;ŋÛŧvōŌϞÝīfį[)ÔÐĨVįB"ŦēĢö:™‹ W/[žgũžs§OĶīmWĒB|ūÅ‚_Aā ‘ƒ9hā KĐjÚUq ˜æ |šųqå‚ÜVtōr7É%Ã=&7ð a_ķ%„ F%Ï!M}ŠûŽ Uäm9rDYũÝÝ]Øöüūētrũ2QB ŦB5w‚ĐŅË0―]@ûr)’Œ,õ&t HôĄE…Īq‚ßôxĢ›“’ā;>Ų#L 5ü–vũ‹qƒÕË{ŪfpsfZ&øŽj⃄8Ŋ9mݜ]ļÁ-üÄpÖđfeïÜxV‰píŨĖ'ÁV,í6ēą‹ā láß.XLpx'ŋšþŒ‡3ÛŦ‰ïÚ.!Fķk3Ÿfūœ"Š +cN&‚ŋ&*ūķ(ūýāy‡ķ,rI;>{óG…”W”|‰ZŽíœsüĄÜØĀšĨc BLņ°ÅwBU“Uķɚ\Ļ<Ďé–}-Š #kw9°ksƒ°ÂIS&ĪZ9ŪXš!kp1ÄÖN=vĘÞïNŊ Íļ}íŌsŦ›gęƒûũÃürdęĪ9Zũ xÍG€*ŠD†ĀxQ䘀ĄĨ–€˜—‡q ?7ŽJ)08đC.ļaĮ>ãĶNĸōôÚFå}TY2[ޘR]juþ( &Y}T#Ûu}oÐýkWnZë\Ŧuï&˜'ŠŌRŧĄðš=ÏFÎđT€b|ŲęąŪÁ °°°ĀDÂÃgHčŦôðᭂ›Ó?GíŠvȚq4{áWųnĨ=û…šÅ―ķøDxFšÄƒW䜇E OÝĮ@Vit˜SŽ?\·ýųäcųĢ?.”|ÜãÉGs&}œ―ęĪ%1Þœ… %ĮŽĶũؗs>—f^Éî°1óøÅÉTˆũïÏčą9}íY‹kļK’ŋÅn۟mOšâģw„g3wĪbó0Ŋýúôƒ7mÐȄúuĘōŲ…ÄĀâdhČ1šÂHKķ?óYnŠÄ•s…—îZŽ6qÍÉܝ·ņ_mü cØĒübØĒøjØ"á”Ü―ûöþpãtr5ðÏ:GD•šüÕî=‡~ķYō‹2žÜšōMęÕÔ+?œ=lhÃ^Áŧxļ(Ķï/ŧþ<Ļēl“(Ë›Ļ­V$E’Y@~þæÐĄoŪМopâPąncŒŧmkúyĸũWïýōÓđïnᘟ|1ēëŠÓŋšžwðąŲ)ŦÜGˆÍÏĘH―úË·OÍGOĸ)―ŠYV‹OĮ&ĘP“rŦÉq íÁ å+—­ŲķsŌäĩu:TðF’―\‹Õ―dÕF-ÂO}zėÖÝŧįūų&S$ų7ŽĪtw5Cfԟ@ÐųC ”pŒęnTå’ŧôĘÏNĸöŦŊz ė_"LÂĀĀQJ0ĸ IđeÃ*dŧƒDRŸĻÏÓóŽÉOmÐnw`ß O‹”l eĀģJ1ĨĒB\(0ĀĪhIĄ*AFb1äoũB@Í6Š) ÄaĮTV)ĄTT ā Ēę/yPä„čĢ4U!!@Đ|ĮŠEL%•Š„r äĢ @E-52@턊rdĮ < rsÉõõŌÁŒ;ę+`ģ|KĄá vĘĀ@Ļ•þZϊ™2b„Ē*īå°)jpŽÝe·P–s ôqŋrëiD>­;ķæˆÚ°û”"áxÖ3KũžģUŨcŲØiÂÁ=—ÏĨ™mLJÏqÄÛ[eA·‘ģý9E%Åmԑ!`āâMŸ}õ}Nū)ĒvÛþQŒ (ĻlݑïUðaUŨÚmúGqF€=|ƒčĨ Äŧúúåí‚YÁīxē}ՎÓ6U;qîfrŠðr­īÝže-ÛFÐÔõëĢZM-ĶŊ\þņ/9ęˆ+›ßđ‘S ķ5M -§( ųČĐUÝ#E)ßbĖ’šþ@DJ›V))f?/ÕzâäŽÉžĒD·=•’ĐûŽũ?Ļ}ð“ŸSS NĨƒ* ïëęÆRJuĐÕųC  Žâã T™ēŽÓāqãûč#x‡Š€šqÐYP)ĀāÁ0Ũ$OB ðˆEÚĮŌÛũ-ĶH=ûŒ”v‡FŠ^_‚§‰WvĄrîūÚđĮcČÖŪîîlēė~*Áj—†O[§š›ĄÐtÜD!ZŌĐŠŸx1[•āXx;ÍöÜä5 ƒ7øŅęî/>ĩÞĩš ŪîęâJŧÖr}t;3ƒ<*N‡jŽ‚ åúÖó9k‡X'œž.GšųtičžÆēm*9óĶãEī<‹―:ņðYž|鉯ļZÄųð ȃĀĀ#56B *”š:uˆp2›-Đ@āŊÁ8(Ūi Į°ÅŠ[BŽJŠTĨØĒ&·é‘ÜæÕ°El ˆî3:VÛSAïaŅÅóÕIŠ"3aI€Čēę4mŦĘĒJ(p€ (›0ķb]ŽČ•AT’d.0*9Œ‘EFTFT”äŌLŽÜ°8“Ļ`ðUE•’ZĮ%ĩvÔjÚ5ØU Ŧ4í\؋$-ú i4&&ؗ€ĻVÅ##c›īŪHĩóō‹jڑ˜ !_%đûøvÕīȊĪbZ―Y) ­!‚wģūCš'•% Ģŧīý#‡EęRŦà &ģ…“MÁAŪđÚ_*öWnĶĩ +“r>€þƒ;Ï3Ž?ĐŠäļīŲðwl…2Ā đ|ĮbÉ'WîŒý ŽĻî5.äeX~|h%"(Ĩ7X<ō0bĄ­HÞv,ÃĢđOĸæ‚$Ëģ>Ė―ŸĨn?–áųŌ2óÃÜ+ūqŊčjY>IæÃŲ—dČ107Ë:ĸHö D·Îõ yEÖŋý˜?k_ÎøÆn#€)―hėqKâÏßķ<·Bč•f\Hî>ģąēJē[ą‹Ąg#Î7M;Zô,Ŋų,k`=Í 6‹eÆ'đ9 ―ĸÜÆŠ*D -C< TŅ$Ŋú4kpCAmüÍ*9ž-=ĩĻßߓĖ2P ąįz\DŌrď.ˆÕ*y4Ė7üÅî!`YÄ@CÅŋ숚å5DUDUyąFUÞ ‚Eð E–^úHøÅ†"b‡MqXŠ…Oï‚ĘoÄĒúÂ.‰Ŋ2bð&/+Ņ_gƒ ‚='ÎcüKŠ’(TéE}-b€ĻRWŠPþ͈*ęõ|lŧÞÉãđÝ b@ĄTNþ§ĢK­QEĩ(Kð6ˆ7r.ÞHpþ―~Lĩĸ/0H[öŦBĩÁŋNFø›sQJ.ü°úŪÏœĖ$ āߕņNũf ĄÅ5čRû?†>œ\ `jē…#ā5

n℠óWïýzõ’1;N(žĀ_Ú1wōÆ/T€ĀVKĄÅ*ĸ#đ„œĀĸīuÆÔmg8ėž7áāũũaŅÅÉ#—ßx,#éRû/ĄĢ̃Õ+$īïĄƒ{uð 9ne’‡ŌĢ}cïPŲ˅ãNk2Ã"UÁĄU4ŽB€BÄð‚`08–o!ËņځwīhŅvĩ―ŧ1Ž6ÁÏ14Á~TƒeÞ8ÄŋÖnsäØbąƒˆãĩU‚ÝÄąÐaēŊŅ–ðœ#dĩŽ"–CĮĶNZøsŽ―øÆÖąšáļâ\1 %Ŧ6nT―,ĀH)<õqƒ„”ęRû/ĄĢĢC)qrũĻ_ŦZĩ*%K—ˆŽŦ\đJ…Ļp'aÞÝ5 ։o°úĀψaL9ŸeeC†ËŧũãØ6öq ‰óķ–`ą 2Ě·cAŊúuęÖmÚãÓëy*Øļ`TBbbƒãŋ―•ÉqÜÍS;n>0zX߄šõw|ýóŽ ęԎŋóáų[§vĖßļoäāĩâk-Úģ, ”ïö/Ni˜Ô`ŨĐëįléWWN˜ģyՐFIņÝ2S–ƒŌÉ-ó““ }ĸN!å@֎)ģķėXÚĢyíšý–Ü7ģyŨ>_~āЖa}f,Ų]Ļē"Š>Z2úÐÅlA Į>˜3cû7Ž Ü?w`ꆓ…EÏÓģr‹§&ĀÁï%ēšÔęččPB$IR%IQdU‘Ģã˜ĸxïuCýQm―7ÏÜôŒ ĖŦ_þų>ρĢsæîAUÖnÞÜą~ ˆU‡AIG6O™uTų`Öâ―[vįüõÞeénA—Žūōõįjuttþ=(6%ķËļMã;~—aÜyē@!w ãÚ6ŸēmōŒž~ãÆ—a!&(e|ð›>ï}Ô1)NU KRđ]bä―‡|~ĪÛŨŨģcX\ĩýԙCÛÝ=ŨiÎÔĄôî—7K(ąR-eâäĄ-ĻĐâų“_=ļŸeýþhtĢÁíâËŅŋÏÁÏÏýxģn+ęUĄúüéĒÄ_ūÚŋSĄōåûóžJó'LĖOĨE.­ ‘zŽmŦ8ŨgąÎ7ąĘx”*íŠĒ*”/,Š*€6ūQį― Ž}wė†Ðļa”’{ōûS'Ū–™ū$:w9pcô‡―tttþPxŧ;E”CÅFŧYÁZ8øé–ÆžYC&Ŋú%OeīÛÖāęï”]­8. )eļb ˆ­Ķ‚ht(P9Ģ‹ãŠD`<dE‘€„)Oý―Ž9ŽApóôē<ÏÖLT*°!žÕVą" Ŧ %”ĢŒ“Ŧŋg`ÕA#F.XĩbũúũIT0ðjv°M– -ø•RQ@B"k–aė:üÖ}&vĻė6`ïšɱŪHĨ€‚?]jutt$Y’0TŲj2ۊŧs›Íē Ö&5Ļ'ŸþpũŅów€]5‹,0ÖÉ·IÏîŸîœ?aŅÚĩk6ž}`ė9,áގéëwïzî\sTÍzeøų*ŠJŒl1|8†ŠâVŪŲÜEՂÅŦųða86$ÐïÆĨ[RP•mƒ:Dz0*ƚīa”ÐfħBč™kÍl€`dãz,]æ{čôOĐîą)[;uâeœ4xbL€Ē(Ņ fN°ēŠâė;jÆÄŽdBŨÞgĪđUØģū{ŒžmwŪņ>üÕY“!rįöņáNæœōĢfLðgāÜmøŒ _číÓęČ6ŋýĮūđũ…GÅ@äÝoƜ  ĢĒĒïÍŠjÏ’GŪÉ þ,/;Ÿ”+ĻvĶ*rjÜ}Áúli_ŠÓōÃ-åkĕŌ&5ī5…+ l;b *]ļÓIãú”õį ĄšÔęččü>$TĐÓŠēäļ* Ū–\Ō1›ĀPĄS$‹"Ž–\Âa‰Nn›\ė̈2ÅPĒbĶVŦnĩZ‘$ Ä'ũŽåXé‰@‚ĢJ2Š(ŠîÁŅíK"Q”X’Û•ÁŠ*‹yÞŪ-{МĐcÞ ēZĢ)Õ9üUQQ žĨĩ-kŊ„ž›ĩ·û(ēę_!aLÅ ADQIlŨŅqīBÃÛW` ōĢÆÄR‚eYĄ/Ũ‡Æ&”ŠH%Q>QÞ‹Ņ\0ŦŅ<Üq‚qÍÚU‘1hĒČŌïsaŦ·ŨûÕæååeeeUŽXüWĢsõęU???//ŊŋZŋZaþӏŠ<âbC(&zŋZý^­ŽŽÎïĨÔ3,ĶjĖïŦģüŽ@dį?ûk1J&ü~`EV1ýįåūËÎØēï_ūúëRŦĢĢĢó†6ʇæ _}扁Cŋý&†,šM&ņĮr9WŨĩßFÔĨVGGįĸ?ÐŅŊāuûŌ š…ģ Dž 8ŽóÐŅ€}ŲրgŅM ī5Ú1Äžæøb Ïž‘ëEdĮĮ.gwå^Yė0œFņŠâDÅÁ 0 'zAāY―ÎEØ I)5Ęx*8z.8|9iUŲĢūĖĒ%ĩĢayáÆš%ĢũŸģ› –qd0‡e3ņubėFŽƒ/NĘ^4ĢK­ŽŽÎo"dÉØŧhlÝÄĪä>ãūūSČō|á“Ŧ F%ŨMLLxoŅÍ|"åߟÞģCBbRįAĢ3óÝķų{ÆóÜ­3Ûf~xįų[gv-ÜüҘa=ëÔŠ―ô“ËywwŸēęą0 ûøÛCFVxMĪ ÃŲž_ž3ĖÞBĄn‘ ™Č°lÝĶmÛŨÕIĻÓaČž›yŠĀã/V-]đdéøÄ„š-ūœ-ÄnYņþžũjwýätČŧ1cP{„îÓÖĨą°ðÆŽnÃ>úáą]ï|đküĻ}–ÍÏz˜[$2 kįīY›·,éÚīnãņ[ŪüôYŊÖ uLŧ”%ó,üþĀjûÕ­ÓaÓßîâüûë?Ų·oÞīÁ―G>(„Ō“'ö­›T·åķŋ]šķR–cģn|>uáÚg&Ę ÂÝSgoüRH—ZßĪģNÝ=nāŠ=–…kÖũ(>Ī̈́ĐãGÝų$vþōUs{4ð“ÓÞÜõą`Śąý{š"å΅cgž ÄdÞûúûïÓBũÏ-šÜ'ēãļ~ÍÖÎņc—óÍĶ]gsœuû†KzŧĀ>(ŪÞiÚŪ­Ë /l;øíUDm'·.[ŧ#mÚĖYÜ݃í#ˆI=|Ëö•'Íęč“>ąãô LŸœ?ūqÛĄZc։ɟԾÛUKŌ†KK=Øß·ũ*ŲģBâō ―>;ņņÄđ;ÚTwGĘÕ3N͚+;ļuûą[―'Œgퟰ§ÉĀ)%RÎ;|ąÔÉĢôę{'w0­ē)ĮÉŋvx‰ÚSú āĮ>žÐeÄóŠÓ?˜ÛjëĖņį3 ‹!ŒŊũŨûķđōŒ!YŦ( n<õįjutt~“Ô9ûØSĩã‚AņĒj]óÅ{_9uĸ\pht*þÓýŊŽŸ…#?—įĄ(!Bä 7+ƒxM@‰\ĩńi#RĻđō…S_=Č+5ąkĩņû· (UæØõð­kjĻē  „šøäėX5???݄E"šËÖMiãQ2ŋaįmÏl€˜ í4uEŨäšjŽšûōÐKOĖV@;N_Õ·m’øčo_3ak–ô‰ āzïõŅļ―iÖąMF,yðļKÛÆúŽ=>8đ,Æ!7ĄØ'hÔÂ5ÉåsųƍžÓ­yÄóļ3ā]Üðē9sĨŽGVI0!Ũ21žALDÕļōĘĢc_åf^;ó„-#37§—„ĻēshŪM|>Úpžpđ!GŊl\RĒ_ÕęččüFórōL,æJ"uvũ`l™E… H dxŠyĶ" „PJ0Ąˆ˜öü5jrCĶu|õoËĖ)€ŸŌÃóŌĮmúŽĻÜŪsī·€Đ–Hā•ýģ'p—éÖĢw\ pĶDՂdedËBU@ ÆĐ ãūn‚B(‰B Ų2ģŠd€dÎ!˜g äĨÝŧų\pōš”úČŽ€—ŋÖĀ€rEU( š^ĐŽ?@ŽQÉü~ä”YeķëÜ ‘‘sáX ĻjūEPó4B>ĄuŸa̧î?úaÝ0NŅŠ§6•ģ—mþiÕ é›Ë7kęBÕĨVGGį7@0áýš'ÔøbóŠĩŧ?š>}ŅĢIrÝIÍĘL™>aåÆMK7|šgˆÜŊÜæŲĢVlÞēfý–;’3ïúŲGÛߟ;fþÞ[&ĻĐ$GwNūŋ|ûîK}+GÕŊ` ŠŅ!đÔÏ·]ÛīIā!Å@ϊÖė‚üGn=*Ėūðãé4Ŧ ũėôúõó>ÜģyōÞ{õZŨuНeWŪ^―aŨÞiSW{WoSÁG-YV!|PåöūïϜĩwïæËԊïLÓŒí•ZbØÃGÜū[1rÅ> °Šh5a&ŠÅŽjBIdģYT0PR, TŲdÎyjĘ{ðË7 ï_ĸþëTßraW6lÞ~ôŒXcPlÔw{Ž>ÉĖžuý‡§&‚(ĩXĒ(nšw ‡?<.l˜ŌĀ…Šü&tĐÕŅŅĄē›NYīuRrÁÃ;ŪqC·ė›åãä6eþÚ―edPƒ‘urï:yóė>mYÏĐŅÕÅčÔbŌâĄÕ^Š˜&RÏĄûvL•zëVn‘ęÄĻå;Í™,@LĻGDbPĨ˜6MĒ|EÕ{ čččüf(QĄĄfŦþņÅí TYVíߔ~ÃS€†"‰xīï5žÜĨî•ÆNŠ/P…b9ŨÓĨEŊ­ވ*•ó|ūíjó9CŒ@_ĶÂØ58fИ ĀAí$ <ý5ÓøÆÃë–4ÝwôÖ*L•äî“;Å;‚I ĶqÍÚÁ—―sPÛ“ÚūŠÍ3ĒwÏhÕîĐ*ÁöĐ dQôjØŅŅÔëÐI•E•ļÕëܕ:ú#Ä$tŪ@Q&՛õŦŅ h4ÔÎB’@ßÁãē$ĘÐ7đëød ĄÚSļVîXH2ĄųŋŽ;ßrxwψčRŦĢĢóÏ@‰$ŠoßY―æí]M―Ā+ D›MŸVÛCRdŠIą .]—/‹ŽĨĘ xōĶ/DÐ-nÖBßPY’ĨÔnRdØ}ÜčWJt˜€ųō(QÅ7ÓÓŨŧZė·Ũ‹/6ˆķņöĄ·OYãuXúv ,‹XŦV.SfŪ‹Ūæ}]juttþy „Å ~ÝōîŔ°ØjūþšˆRÎ9 !1ˆ`L4A„ŌwJ<<ŦVũ&ŋĖB1Ae*U„h.)(aÜBęÔ #SýŨb:::ĸ4”ĻŠ"Úlö—Žāâ·BĒææä˜,"Ë0ž d`-ĖÍ-īPû!Į1Ąh)ĖÉÉW)zaĒc }rãęÝ 3BðWs á[G5ĶüqœÔ?_™.ĩ:::ˆŨ§Žč7pč°Áýz-ÜũAŸm;īQJËÆmzo;û1ž„aÉĨO?hÕžq“Ví—í>‡!‹6ũögÃŧķJiÖbôžm*„ŊD™#ŸO=ûŧĮ<‹~ĩų€l1›E ó_,ĩ:::"Šäž}"īîØĨkŊ^Ŧ—xzjŲŽĩ?ÓOūørRë€c§ß+$k'/>:>iڗí—ýÛŌÛįŒĸęAŊĶ7žŦ3ųø[č•Å3>―!ðĖŦņކ5”3­óĀ ˆXŽįؗŠÍ ‚pgýōQ~Ï Ȱ‚ÁŽðēÃ4þūá„,/4_tcxÕ7cÐÛ-øŅ`qčč~ðšÅ–…w”äĻÁ^ZqV—Zß Ē0ą ‰õ[Īī­WĐ4äËW%ĩčáïÛągũęõ+OMÅjÅrčæÉO2JÄuŦZŧW·Zōî3,ūđúŽ|ß~}ƒ*ujXóâÓôú"–A(ũΗƒŧ7­“ÐõŦ›Ũ,ž―á°ÃĀo.[ąý쭟ïßŧ`æā^Ãï僧įõJŽW·ÃðãwōXžÏŧwv|›”úõ’îü\f"čPFbËÛ>ŧoRR―–{]N3ņûāŧĢŦW~°fzë„z-֝ļjڊÔÂŧkfýņĄÉ.›–ī ģ—myœg={hYJƒĪΆ?úö6Ãséŋœžŋð°ĖóĪðáŪ•ÓŪfS9óÚŠÉ3— ėÓĩíNDP—Z éoƒúöŸąlÛÃlŒŋ/žĸÓđ‰<ŧų(§°čþ“ėâ‹G€)ŨėVĨ$OT•P_ ōŸd˜ō39b0 ‚UÕ§d ē>_Þ}…%ĸóÏODwŨģĶ8sÄðBƒppÝÚ 9”ÉŋļôýO}cck– ‰ŊŸÜsä`ÏܓÝF­Ļ7{õ„Z…“ĮŊ(‚øČėŲ{Ø*ŦÖКNPUJĩ<?^;eÝũųŦŨĶTā{ Ÿ?‹Y·ŨžŋäQpûÁ JėÛņaЉ )gŊ‚‡g·|ú…ôëũÜxŠd]ÚÝm֗=įŽž> ņėņÃ>ŋœoÉļņų7ėT,:ûý™T ERÖĄĩkîúE™T—”ęģÅtttþE(V€WåMÛ?*L―ĩŽß{ýSÕ#Ëûĩ?þޜšĩęâęĄoOgZüԁäE ;\ÉnĪ rč ŠZ Ŋƒ*AũVĢÞžRą šė;2ËZŠ}ģČíŧ7ĸÄw˜J·jYĩäÅr^!8ĒV\ôÅ­KŌĶX=—7e\ĢÏ-°JŦĶĄÓw͘_ÐoĖØr,ÄXŦAĖOûþëKÝGŽ]!ĒšOŋ>œþó―ĖYŠÝi؜!Ý-_KûžÞTqqz‚œZviŨmꑟ”8väFóũ'Þ?Ô+ŠŅðĩ+[@ŊOŽĸøÃR1.įĖ8Ū‚‹ï)HēäWĐæļIĒŠĻ`ý‚ŽŽÎŋŽŅĮŨŋLĩĪΝãî^ļQ$)Íg­Ų<{ęĻžŠō>ebJxЊląH*Þ!†Žkũ%ĀēHķĐÁážþ! ĖΕÃ2ũîp†RFøú 7 íĻ’B)æŒūýzķųaûāŅÛŪĨôl •<ŦBpvóâœJĩëÕ{äĪ™GĖ AļbûÁ~P[xœ\GGįw1lŅĢó‡N5 ۊcjīëîeāóïœ>q)ÝÕÝŧæÃ ÖCj„šeŸÝŌ°Ũū…Ÿ}ÚēųĻØý#f,Û\ |ũ…âķŽiycpé^ą;§ŽŸ?ķĐįŒ#™ÃĶTU@đ(sĸŽ%U@ókĮV9ŨlZ%ÄāęÜЎēôã2}›ÄGR |ÃŊØū%ūtĮÚ]+Į^8~ėb—ĶåÞO/]ÉũÜÞý‘›“웑B°C)ïPŋUŌė}‹ƒ…6NíË­ÚļjĪĸĢ‹VÉĘSĻjW_‘ÐŨŨėÐ3j`ý íWïmģny +·čhŧaŅF'—ĖfŪnëo~jyūaÕĒ%™ķþl6ðPbb!ü>čRŦĢĢC@ޚ‘z+ÓzŽYÚ:đT†ĮŲOo_+ ‰ƒįtlVaÅ%ŽÚ”éΌ;ŞqÖ­Ųīë“TđlËûU8IáŽĸĀoÓ۟ï˜f-^ŨŠvˆüę<†uz͙Þd>ļįYĩŨÚ=ÝН=Ãk‡öŠÚ"ÚÏ(ËrTóÉóĖû 3Ę―wnßt`ũ§WŊÞöð‰c äãyõō-9 nÛ ŽeÝQņøg™°É=§·ƒ—oÝrŊØúË.ÂĀÄ·î-ðŠâ^Ūî°^üŒŊ~AUĘDÖõ.ýi“ņ)ąŠ$‡ŨėēsĨߑįóđē;wLŦUĘĻH ·.(øæVNÛÕ[;͇x!'RqĔ1~Ž‚)ÕĨVGGįw€bÕ%,aÔäDðŠŦ€ œÃŽœØ8%‘``ˆíŅŦ"vīp‹›2ŦjąŊ}Øqé4qF'‡I’ðB`ÅäqƒIĨ0óîS}–ÍŠÍEĪ:ö4Ū8ð-Ũg”Wŋ -ŸŌĄB аŊ”1p@MZu~ŊÕ˘ŠŠ–ŊÆPQ”‚cSJ"­f 4 bqÞšËĩíÅ`LËĮ'ĮÖJ.v0ŪuÛ Ž[üߊ,bÞĪ}„,jAtĐÕŅŅų}pô%€ūþ .UĨŋ·`MŠÁЌUĮÁ·ûž6―FÓnžBjôŒXūduÍēÆ2íð}gw"jūï€ūîiðJÄąōšÎ·  ízÏó‰Ž0yõŸĮŊG+ÆQŌïˆþĩ˜ŽŽDvþoŊjü*Ú1ŧ„ošÞŽ áŧ܈[`dbí  āáˆņ/Ï=g\Š'Ô.å펝Ο‚.ĩ:::ˆAđî\ŋŸAĸRÛíëŨ2L*‚ĸũĖzpãÎģ"AŒUL(xŒĸËĩ›…6BøkáąÝųåz–I|s %XÁüsPüį6UÐĨVGG‡ãø›,ķåļøí@āœė}›jbĸ^k!ÃáC3_þ ĮƒĢ F­>™*pčÃ<(ž8iøâËũE†ų5Đey?wŌ Ũ †gČ―ļwЄ―&!øÏA—Z Pž\XŽáÝ^ī08x:v9;ž œĢwoā9dtņ͘€ūýč˜`XdtõÆ (`Ēë4­ZÆKÅTóŌā°ØËrB$<óķX3œfN…Ĩ1B„Ų5›KÉ ­ëT40Z=År‹XŽg™bŊâøęRŦĢĢóWąE7.Z·NãuŸ\ayþųåã[5Đ_ŊÁÄåĮސ7Ĩž[·aíŠyƒYĶðč›į%$Õ>nÖĐ ôæ―ˆ‡į>鞐ØvðĀ­§ó A@ rŌ L"ÃØNî]PŋAƒz―ßŋ•#kÚJ)âÛóŦýF.øæAá ĩ…ˆGĘĐ-Ã&ÖëÓsåÓ\Y08<øÁž}ŲėþC&Ū)0[ā‚Ė[ĮŽÚ–avņŋq|íĒ―'YˆOnŲĻ~ýN}įĨ™Ë@]juttþZpÎĖųý;/ÐøA͜6LÝðÄĶJ2?xōâ5s[žX―üŧë&jyū}ņž]9eŧtowũÐĒé2cqJy/Ŝũæ]õųũ݇.)óÞäIjYĄ„ĐĶwŨÏė=žjAį'žŋđóøų+FĪ)!ō9įÖĖņ}Øāļ’ÞÅßYq<šþ鯉+õžąīsĖ‡OYAP ŸlY4ĸĢĒčΝS”g§žäæá~|ëΓŋī^ ’Hqg/WJ€{éæ5ĢVΙ}øü]Âó€qc‹.ŒŌåŧœöŦ&ôvC ĄđÆķŸîތíÜĄCƒ*MÞë]­ziIąUôĐŨiĮô‘u+—ä8—ŽÞĄ!·ïoß=øáļ Vė\Ŋg·ēŌŊn™7ïøų_róMęRŦĢĢó—BĨ>ېE)08áœ9SG§rĄÝÚ·ô €Øņ4 ŒĶŠX”G*…@)Č7K2€āŽHūĻĻ€*æüž„R8—ššxËĘŅ­|0iã‰ŦˆÔ3°FÝø§éOÝČ`yž‹ŲjUĀĶ<ģɄ $xŲģcJÁ ĒÂwíŌ)ïøîé+?-ŨļAd +ĩŲËVëØŧߎå›g ieÄĒ$Ë*ĄšÔęččüUĀēÕdąQĮĻģE–ą\˜•*šóŌî\+|zĸâŨ-’l1[0!”ęĪT_Ū^ļvýŒ>Ģ_ū 0&aUZ€/ÆÏZēlþ°U§n˜‘ĶēÍ"aDsŪn=ðĨ N ŲTāÝ}āĒU}ýšũôŅđĮ BŽé]BÕJ5ŋ=šöŠ5ã',8õóS–å)VĖf+vč&%ÔbU°ĒzWmӄ―đïÛŧÉ-0։oå~óęų+O ēÓî>K S§ôkŋįlšÁĀęRŦĢĢóįƒĨlōāÁ-ŠļE49wpgÐīkkށWí\R šų5†ôęčDYĐÔaÆÚĩĢa^fĨc6ïÚZ)ĀP,‚D•Ë4Ų°þƒŅnӖmÛTPZ·ŨԎUTƒ·3(ž|íqŸņ+5ŽĶŪåÆïQ2Ð―Ã°e;F'ŦĐļۀ,ƒč&ývÍîáZ˜[1iÐÞ―ËbƒŒŪáĩ†ôėčo$XQ<ĒÚOŅĖU…xõÛ°yÏúyĩÂ]dYKę°íÐäúô—ۏ …œ“wģv]Š„đ+ :úsutt0ÆĄ5RJ"Jē!°R·HEŊŠ­&Tk4TQI‰pÆŅX€`ŽŅz@ÍÖ/ÝeI!8 * ŊÖxZõÆŊG|KjÅÆ–EÕģĸąŊÛ,ÚķUeQĒ\ŦŪÃχ QÐĒSŋāķ  |ËZvŒąSp\—’@eBp‰ļfĨŦØ·EQpXåķãâÚ‘d5đcĒĘēŠK­ŽŽÎ_­áÁۍˆfy Ž*ã ^óëČ/‹"~3ÞËÝ·ûžÛLjXųŋ­Ôĸ=;Fu((:ExîĮý/Āí‚čü:铐ũÏéėDáĒĖó&_–sNc1ޕZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ€GðûÎ9{ïāî·H-ßr]ŨĢũ·ÆZŦ”Ÿ‡Ô’™ĩÖÖZüķ™éĪ_KĀX @jĪАZĐ@jĪАZ€?öî<<ŠęŽĸįœ{g&Ëd™LBö=„,ė‚ k4. U[ÅęÏZ•"u)T@wÐĐZŦUŦEmݐMA@Ö@X kBBVČ>IfæÞ{Îų|ï3ŅßómŸLˆUû…éyý™;į9gnîû~ÎđæL€R!āHÄÎačcųáÃ$„PJ}à LÂGFm ‘ššš:;;―^ïđđ5‰Ē(!!!aaaVŦĩ—Bc.—ËįüžŧÛŲívsĪp6555æ)CĀÜy.666))ÉbąÎeÔĘzÖ Ķ–––ððp§ÓI977looojj2Ŋ@J)øÁ9onn€˜˜ó*=ïęŲĄþ"†ŌÐÐPYY™šš…­y ĐŠŠ*//ïßŋ?ĨTF­ Ė3sÖžn…?<đĐ €Ÿ}ï>B)éã|Ųfģ™_mm­ĶifŅŨã[!æŦšŪ'''ŦЊˆĸņA!~$Ýãgopppũé0Ģķ—[ĢYϚ9›––Ęūų đcĮóÞcþ …­|,&ŦZŊŨkÎ[Ϛģø-ĸ‹Ą„ ÓŠÜ―ũ`‹Û ĪũÖØéښÖNƒÐģŊ­"ĒÕĮ0 ð1―/21œ =―čnmĻ;Ý}ĻIŸŽģ―3m^JŒ†šš6ëåMBóD˜§Ģ—– !­­­ņņņp6ˆÂ„ðÆŠōęĶNø‘ ÖY~üd—!ôŪÖã5S/w!„ĶiSķËĻ•‘ŌKíž%ŊŌØntwïûāo,˜õÚŠÝp6Ė0žë酚'ļ{ÉÛßķ€m;gßņXi%ïØđöķyˏzúþ„P. H‚sîĸę%@ÍŌŽū‹D]˜Û|ĶþØÖUĮ+uÃeáŠĒŠ EÁ ÆĀ™Ššôė‰ŨOÉ7  U(AÁã Ļ*rƘ?œ3$ ÁhþbÃîüņ“3íLgé%bÎZã "ũņwÏh<ųÍĶŌ3#/-ŽÅæfbáœq*Ė.QBgL‚!(€ ÅbĄ|#@ĐĒĻ ņŒgę6ė­=aLl0j†øŪ_DxOïØĩïĻŧ~`~fnb.ĻA‚ČXw5Íör"úø€ž1=#Њ•ŧWWØoøÅļä’u+_}ä–/žúÎŨ…Đ‚|8c Š*tœ üĸĸSpΊP―nbh  ĸŨa@ˆŠ(ðŠŊ^{ï›øßßw5pÎū GĒĻJݖwŸ{ņå°lęõ#m ļíVģyû…éAæįĢøn (āČĻ•…­ĸB $ŽäüŅĢģD~ZÔ{ĸÜyäÔ°iae;7–?Ũüø “;Öŧ<õ›VEÂČ ëŪŊv6j<<ŪĸĔG(îŌÍŧlY#&ŲģĨؚUc ”č%›ū.=pžŠŅ3~âļė$Ņũ’Öß@„ĸF4ĢģËęLHLʎËAÎ ÆžŪ3[ŋÚvšƒ%įÍ@_Č;ęŋÜüU}Ķ }an2ĨĒŠŽdįþãL`úˆqĄ5;ö–Ô55V,˜8<Ó!„Ŋ&UԆÃŧZHT\˜kŨĄŠĖ؁ŠÂŧD.8ŠnØKĸð—hĒP‚F^qåĩųW^{ÃU—,žlÚŌ/ĶNļf°åÍGįŋ·ũDúåӟžkr(Ņŋ^ņōŌ·W{ƒė<ĸژëÎO^[ðʧÉN~dÖé‘Ļ dBSŧWÎ{ęWpŌÏ=1*=ŠđÛ?zkᛟÕėé3fM]ņÔsíÔJË+ž|äÎ §Õw#ĄŠŌđô/ũ[ä.ýęģ­'ï*JįœĢ‰›…āœ3.ļŒZĩĸSð;~ĒL‚éšæåHc’2ģúmïr{N–îÚxĻ}Ōļa6lÜĪ„^<,ÞĒkBZBĪĄ·‡ĮČrxŨŽ- é—^”­œ8z,Ô1hP‚åLų7õĄQđaūY8:bœÁÁĩ iĐÎ0‹āü\`”Ō~Û@ Îôûūũß|kô„ņ#ōӂˆöõꏛ"ōG RŨoÚJ‰oČ­>]SÚtŪūyËŠČø;ĩ_ŊÚX;°p`|ˆ5,˜D8BBŧRģēûEÚ8ū^EīîŲߐ4ė’xdÝΒÓ H A߈ÐôÝŠ {đüāo #„ Ļ{Ühx5°æ.ŧŠĨĐéãWĸüveÐüy3—Í_øjræĖQ]ýöĨ‚g_š!#ÂĘ>yþžÅ{æ.ŧkŅCs_čũ·W"UԎō­wÝûâļĪW}ķpÞēW^~xĸۏÎ|Ĩâ‘yũ§aJ”chLøîÔþ7þl‚Ócˆ XÔķĢ;JĘB^þbųēWŪ[5uÂĖ Čį(ū\_ÔĘ/d’Q üÏŧýWĩ |ĨũĨgãĖSQvļĢĄæī FSc +JtØ!8­ēÓë"ŽÐÖšÆJŅ7/§… ˆJ‘Á}™Hã3’ėĄ'SsóĢCÁāž—™ucˆsîũBÂ_|å5ŽÝŧönüĻķųŌˆڏœr‡evÖ5°ŪĶæÆ3MÁ”˜ô–ÚšŽ6jí8ÝĖ<^­đ­ĩųdË–sá0+ŠÐvOŋð0wzVNB8a\ø‚ÖŌZ^VÛޜĻrÝ#i(=ژ4Ė)ū X4ýHU-c ü"H93LHÐЁjގš’õ+=ž‹ūÜļģ­Ývüā1ÏøžŽĪ°}ŦVįÜð‹_†{_oãąēũŦ'*kķv•īÏ+FDBôō=öT4%ÞÕ\^ąm{õąŠcÛ>ü|ę-Ëoŋn‚čō€Ē5ŌqĶßøqƒƒÜ]šÎĨ|ŨÆĩ]6Ïé-.uՇ_~sŨ-C)Ø=‘`(„Ŋw~ĢVQDYÕéėRhŠˆ€ÜÕ\wŠ5ī Ä֎ÔjģĮÄõ‹đĖá šĮwí Á<ŧVxÜë>,+D­B„ā)ntyt°ûĶč>œ\ÂđDėû31ĮŸ^†jˆ/š|uVĘöŦöV' ĢŠ%"›`ŸrÀ˜ØČÖŧ[r:ãB/Mʍ ށƅ €\0ƒ1_ŽS_·‰•šũ—ðÚb°ąüĄŽKõ7û]ƒ‹Č·KčKŌģD­ĒũÅ^ŠZ.ĪūcīŠ=ĨĮ―ąÎX{-·FÄ$Ķe ČúÍčėüPOžýįMk>^rï]{ĸq˜…ö‹ŠJ‰K0ĸé;sŽLg‘sƒņČðˆÔøĪôA7ŋ3--ŨĄŽ`šĮ02đp{Û F4ƒ[cūęžuÔ~žfMJÞ%wóĖŽŪũĸņåžžņđ`&.}?0ŋQË“QKAúß#ļŅRwŽô›?ĸĮg۝đCģ“ēöwŧšÛŧ„JôÎÎN$Īû Aájsé`EoKc§ŦūķÚ+ĻÂĩÚã7}ūrßĐADũ“k.+ũļŽ:äōę@ü”(…ķ†[ķė(;VQY[§D;ÎÔĄY! § bã^·Ûc C7”ČäŒ(gKEZ‚ļæjņ*ŲŲéý›U›wýfïžoŠĐ5˜īœüæČáf ūBÎÝ\_^Õ:|ė%ŗ]|qņe“†šĘ+ë]ãūđģa„ĸB˜·―tÛŠ|ðÚģógĖųóÕwÝ]<<ŋ`ĘäS'i4"Ėęępwđ[ËWšÓ™—Ū -ęÅŋš2Á8PŲi‰ķŦ gÚóz<^‘Y0aDʙÃUMŅÎȖĶVCqL˜úģ{~ÉïüáĘC'ےērÝ ›ŨŪÞër3B€RĨĒdíĮåņO.Y8cúũ?4wÎä+þôϏÐ4ÃWŲŊöï…$ŦZ ÄedĨÔ6Š<@†Mļ|`Nšðø…—‹―ĨĮ֠𐕛A•~éy6[8ĒuøÄ ÚþŠŠúāQ#óŠ;].Ŋ킱Ãv–Õī†&―(VŒ@TŌrrl+Øėc‡įŠŪĻmMƊ·ÁO[ËÕbélŪÝæXÃŪ(æ?Åŧ}{ŲþPBG&:ôWíB„ŒđžØēmGŲūÅjˋÉJîÁÅB)=ręh9‰L Šŋ`L^y}ECzĶ#(AčóFå$:… áɃ rt•ģ˜ŒlKx(GkVĸ3äþ ĩ"ÉwEę†ÃŸŪ,S,Ö{þôæĨcrMšnöÜæßûÛËHÃŪđ5s`ŠĩaŨį+7éaWOxJķӑû낐g_{ïÐF1jęd6pÄĪ+ôŽxküĀÅÏ,yæo,_ū]Íg>#žøšYO7<ĸéŠũKCRnÉ+,}íE‡>}sã ėīl;EÎm·ßô@^? h€Üzéô'Þ-q“„É—MŠ #A$ûâąöëĸ^Õ*ĢV"&ŋ3tŌ†fĪā#83ų&͖ŒÁeFB@ŒgKL8ӆü,c(@„!ļd@Áĩ9„€o!‚1†#&N@f0 Šr įBüMdšûßËaB`xLƕ?ÏäŒE%‚1ƒQ[äEES€Âđ!ČР4 ÖĻŅ—\…‚ŠtĸÏlIFĶäŒâ[abØĻĒáĪ{i!4:­xR†aBøšĄ„N.†IcĻāĮ‘…7ļĀūQĨ}üLAČč_=9é >šŨ­ë†tÕėŨBđáõ0įíŋÎ)·—˜;nęŧ—܈ PŪuļõâĶi^Ŋ3{ė^ˆ@änGGõ—.š ‘(ÔðļuᜱäÝ{ŅlQũŽįOvïęŌ‘2-ēĸĪEO{<ÚŋÉ3[q…ũå)^óBˆŋ‚ž*}f.[l6[yųÉāā īīÔïĩÓ ĨôäɓQQQAAAįėcßî]ŽÎœ9æï/5 !æ> æ6%Ý+ķg]Õ=‡iđ Bzzšŋþ+ŠēaÆüüüˆˆÆØv[õݕü}„b>3â;ý―í >_ýõāÁƒýĸ―øyCUÕãĮO bJJ’ŪēŠý…ˆvŧ―――Ýâs.oÅÂ3ï(―ßoE1 =7…2 Ãår™§Ģ—ž#Ēđ™ŽđKnnnppp@N°t]7ũŧ0Ïfũ~r!@ČĻíŪ8įþŽųo^iþJƒČČČ^ö7@D3^‡9ë2kÛĸþ@~øE1ÓÓ<―ôSaÖžĮŽÛđs'įI’"ãĀ8 Âų…R°(D!@ˆŒZI Dˆ›ŧD}ks Žp~ R!.L Sė6b’Q+I‚âõzŦŠŠÚÚÚ Ã8ũ{ßc1_ÎĻî8ÚÐÕЁóŠã#Ô!)‘éŅVŧ•"ĢV’‚ĒēēŌbąŒ3&<<üÜïm}}ýáÇUU‹‹BüKtPÞä=ZÛî·Έ ķYΈ­îōÚĶýUm!6g°ƒX‚2j%)čšÞÚÚZTTdģŲ·ĨLš˜˜höųĉ ĸĩ@ CUÍ:ĩ701”úO*bĒ”’îˆC!"|„R‚>ð#!íšT6îāIŠUDĩį?Iâ>}ÉYái;~ēÚËHXDŋø„č` ŋÐÛŲŲåöpjq8gƒ‡·lr% +H„>p:ĨĨĨ=ÎŪ †^C„„E†ĻaAzåik8Q}IPtŋؘ˜…ĀũÁŧÚ:,aáV…ˆ€#<”c‹Ũ@ŽôXL’$!DïÅ! 'w,~pÞGÛĢ#ާOũžü—{/ˆ=-ږ?þāšCMvKgtöĨ͙žÕÏÞkû|ÝLJķþęõĪGöĨHėK-)z9†ú–Uï<ûÄK§ ØęåŠý؇ï_’bë―UBˆ§ĢĨ˰:vÂjžļãÁ Ïž~iFâVÕ  ·—ĪĀ.oĄg―§žšņÚí17ū·jnĸ(Øŋm3Ī8į !(8H…ĻĒrÏÖ=_EOúÝý#ųm·Þĸ—‹nËCDJ}o@ Bp!€P‹…§*i*˜š$„ô’wBˆģfąāBðëBTTKãÕÓ§Þ;xæō/æ^ŊxJwîKé~įE!\ß œ!Q(AEQKžYļÄQīâū)TD^=ýöD§Âđâû RŠ  „ DBšP(åŒU|ã=ŒúvP(dÔJRBƘŸåQĩîāïÖ;ĸúÚžAɆÁ ŠŊĖāˆ'|ņÂӋKNiņycžĸŧĄą!\ PP…P5=+{Ôļäܜt5XíŽ?ō›ßýäþ:#qÐÄß͟;,üýũO”QÚzxíĶúœyKžŋ6Ãĩ|õĮŦj6OÛģîĄgæė[û™·ĸ8ĨlÅĒÕu™PĸÎĘuĶÜģdÞôð–]/Ü2óĐû2ėÚ?Ÿ™Õ8úŅ{ÆÅnúāOK—üĢÆ‘þĀ‚EŨ Oā‚COQ˅@ĩ’˜„ŋŧššq1‰á†ŨĢsā‚Q,ފNšųÁ‹–=rëížēþĩYaŠ U” ïū~Į‡Ŧlãŧô―íäŋ]œž,―þŠVޔw{ĖãwÞ^=xڛģ§RÛŽ^NÂO>?ãéŪ)Ŋå隁þŦÚ>-2BôĩČYG§gÐÐaÁDÓu`0‹Õsü73įG^5įË –üö–iūņÕ[·Õ—mĸû7lþŦŊöéþŋ~ōåĩ]SœŸÕxáåsf_—Ōĩm˖čÛYFGí‡xnüŊįþqéėYũ>ĩýæ[Šiý֒’[™‚W•íŊĀđ{Á‹Þ{?rëüž0öÓ§Ē•žúF…ðeÔJRVĩšŪkš=Ą Ũ5Î Ķy5ƒ2E5Þ|r>uJŋ§ÜņĸâēhgĖÝ7M^}į–-îA‘„Ū sáp&ŒĖĐ^đæoŸî˜sqÜŠËũ•5ÕVwMЛO5n+ ýėŊrãíz †Šë―Gnģ§_õî[3ÄëÕD/ Œąģ.Ôę†á§B™Átáö˜CÖĶq$– ĨæĀķ#yöϋĒǜ3nšâó™_WtÜčâ|ęoŧi܈uœ_Mį–čī(k\j|,ŅŽ †ĄkzS|áŊŸ}|F lwŧŧô  4HpÝėƒ”Rķwã‡íuĩœ3tuĐg:ĩˆ`Áą‡ŠVӐsŽēЕ΀ŒÚ^ŠZnŠm­Ŧþ`ÓþGŪĪÔïÛŅ5֖cqŧęë[”Áą§ëŠ;,V+\A„‰s>xâ„i7ŠZõũŋ~š1ąōøÎÖøĮÞöÖCU@Āj Q]•Õ5Ɛ<Ķŧ1„<þڛÛķþõÏoZ0ý:Ŧðp ä?^@€î!õÜShjBüæe6ü?ŋ:?NĖÝŅÂCĢ­mõ§ÛÕŅ ĩ՝AV  Ý­ëšÁU…ā†Ą5ķuqB)ˆ€ÂĪĩa/ÓÜđāF—g_S§Hl:ļ§Ī#þ*tÄĨXÂ[ïœŧpPTUŽQÖóę!Ðß…óœ$Iˆ"ĶĸÅËgykÁŽû}úÅ%/Ū;XŦ=aČåÅÎW^|üđg{dųŽÜ[nH·ŦŧuM[óöKÞũĮ•WOá°`ó‰ý+?z·âhÝî ›OÓŽ™3Gūûôœđ‹ž^žtŲąFy<áCnz~þý^›ņØËŨˆE!ðÓ@]XFNūõÎAÍÓoþøs/žðÜâßÎ\pLɛRąėŲ…Ï?ģpîŦ%Cn›šB ·›ášĮĢ3ÁH|nöĄŋūžė\œĒŨ-@(îÝWˆ óx.ÐæĖHąw―°čŅ<ąŧą^čzÆESĮ ÓßXōúę5―·âó6N(ôNVĩ’pČwĀĄ]qũēÔüĩÛUtļáæ‡ŒĪcðŽđÏį­ûžēÁuëÜgŠ ‡(‚#!€ÂK"fĸ~iåЍáSWN5rhOįĶf4ïýŦí­‚L™ņBD֚ōš›35&,xŌ]‹/ˆŠHΞïÕ7‡ŽópðÓ#ĸ]íóaÜPYó_xŋxýÚý5ÍÄÖŊpō„ÜÄļ1ó–ö_ģŪĶąsÚüįŠĮ Ö<Æõ>Nr˜Į3čú‡g“Ūŧģ‹įüáĨ|Šą3?ݏFØo|úaDļPbï}ō™˜8‹’ąôųūØ}"å–_ÞA[ĩ˜(=4rŲēåŸķĄĶËHÉLU =îFæ{EFíĸ$IBÁ4égC&"P…2M3ļ6Į•Ũ݌đĶëš!ÛĻÂâB•g†ĶéJxĘ57ÝN€pƆŋPŨ4ŽtŌ”ĐE„ĒĶyéðI”ë^·žb…ŽØ`—;âhCg]yóŠBD|ļ:45ē_˜ĒP‚ī€ IįÜ0 D2s­UUÝ}ýÝ]Uk­Ė| ÝYx.ėęIENDŪB`‚doc/images/ifw-settings-network.png000066400000000000000000000206371325366651500177340ustar00rootroot00000000000000‰PNG  IHDR|iĻ"cÐPLTEßßßķķķqqqYYY‰‰‰ĸĸĸðððņōōœœœđđđĖÍŅQQQÚÛݖ––žžžīīīķķķēēēqqq™™™‹‹‹āāāóó󑑑   ÎÎÎeeeËËËūĀÆĢĢĢýýüŧŧŧŅŅŅ­­­ĒĨŦoooØŲÜÞÞÞūūūĶĶĶ]^^ŊŊŊââⓓ“ėíïŨŨŨÓÓÓÚÚÚÄÄÄÕÕÕõõõÁÂÂŠŠŠėėėĻĻĻÜÜÜįįįÉÉÉĮĮĮTTTåäåíðņ``attséééĀĀĀŅØÝĨĻŊlmmäðôÍÕÚÆÔÖāæëvĢÅúúúĸóÜ:™ÆƒÎâ\ÔôØÞãÕÜāĮ§óúüæéíߘ‡úĸĸíĸĸŪŅāõĸĸ]V‚ĪŊÍÖįïōÔÏę͚‚™ÁÚîų_j”Œy€ĘæņĐĐ―ŌšŽvvwĸúîÅŨįŊĮŲýîÎÜ̚­Đļ\K­A1͔‰XW]>ąÛ~QD·ŨæþøåõïãC:=ŸžĻPMvßĸþĸčžÅšļæÐī|{€ÐÝéēīŋÜÄŪwƒŽlZUžD/Yg̨ɟМ•ÐtY306ÃÍāėãÕŲŲÉÏÄđpŠŊÍŧĻšĻ—lnCUq\C]Ŧ€ZtHBČX?Æā·ūĘčØÁ|”˜MUˆąœ‚ĪĶ~Wh|”vh”jUæũþäÞÖĒÂЎē˃œŊ—™ŽŊžĪ•žÍ„yc\rŧ–q:CjG=Y85R~`QÐ`MFJJÉA)ęúíîäđŊ퐄†„ÝŒ{Ѝp…pjydfžn^ĨcSZ?Ez‚Ąkx‡ÚqõōŨĀÉÔI`ƒdNHŒ·ÛÏÅ×ķūŨŨļ·ģϟ“ÅîþŋßðąŋŨņëŌąļŅõâĮŨÍĜĶūŪ›ēM€­ōϙåŧ†Ĩ€{―ĮĨũÚĄššt‹qzTX^9/áóÛēËŋHg˜˜Óõū―ôŠÍóÕÔęg}ĶÂ|rʛc~ūôŪÎh˜ļLuvˆqœhE|NŸĻĪŅUˆ–?ŋ|CT Xþˆ…œvģbĶ`–`î@ž`đxå˗·óĪ@ŲēiB’$ŧ―žŦ`0Tá18(’Xė;â äd fÂŽ˜SÎ#îÎ[īŦhH Æ7ŽÖĻíÎĖƋŊĪū ”M™Ų••/ïR†,Ęŋb˜Ž•Ч‡,T„UŒ•+PQFVjVēdiyZ^ÞžX™YFĢҠ˘9C^mÚīÉDig 8å2gΠÕĶM–šý>Y \ŠąŠ°lƒ§įĨ‚t†A!g’uրvž&ƒ‡ė(TPŨ)~<6ũaō2vÁBæž%=ųƒQz"ÏCųū, ŽŸčs~<= íQ?/ę?ņ‡€?Ô_ ŋx~HČräøŒų}>_:ė;ī|ÉĨ]/…Ų‘›`)ĸ}ž`üĻO‡žÚŦO=$ė)>8 >™}Äg_ÃņáęCTŸŽŋŽß―%ŋ˜ž‚"Ž>čS~Ô§Ã/ë|ŦōWũ.Ŧžđ<Ėčá ūåž­ķũOœ)o’rþ{[EėĄvƒ I’5wž…ļ]ĸ[·îÜđ}ēī]ŦqąĄįðōÅFū!Ū4ü °ļe} § ‡ŌSy„ĮÉWÁĮҧú"ø*ģújþōðŦN?Įwŧ  qqÆrMA^Ÿ ū{ũ˜ð>ŅvGõęyrÜ?[ΚøÎ2?[ÛC‹6ž›/ó{‹iŒŋâ+^ŨO_X|áÃï‘õíÄ> ―ŌžŌŦá#"Á'útí‹sĘĩúČŊréĨW^‚ïŨ؊@y9=þ\pŪýŪ{xëDĘĪ+j˜·.hųA%ūŪÂÉʕ+Ũ›:p‹†ãž;'íŒiÜzôŪ…Ģ'-rķߟ_Žŋ‰ë3|;Ø#<.U{:ö"ø*ĢŊŪįðb-f_ ›‡ ?ęóčîŅAĄúî6đīüzkœ?/ÜöūCGÖ`ÖÖāÎĐӕē ęļ~2ļ#oį5%ū~ڃ^šÓðYō|ðwŒeKæðŌ,ÓÆ]{ŽóđiíŦôé“ĨUeįą\ŽL…úd<ĢÏũĄyŅ·ĘĢ=ā“}ņ@Šøx0T?ŠôÝãn“Eün[ââüZðyĸjâŽ-NũÐgũQ|ïS†2}ģÛ/ŸegøĶņƒî=ЌyÎņ Ũ&ôxŧhŨęÜŦïdîÞžO'ó#_Rĩwð€^Å>Dņy Dœ}#ÕI%>Õ?Č6Ž}Їūwú5:~ąīüķ‚qâæ°ũ;ƒ“?…ĩ#YēŦ ÉüïŲíÁĸÞnŸĨŸzįž/OõJ·_ū40|ŦsÜÜļ‹FŒqäíĄŅOŠføÍGÏÛóúQĄIÍ=þjV2ųBBzčĸFðQ?úâŅ­OņÅÄW~\ý*ïđÜ~[ęðĮ6Äí˜:ÐŌ];ßw€Ķð:ėÚՁ}Į~?ƒî…ėüTųO|ž}ûîī‘wOÆņÉϔ|ˊeŲUvīŒŋgô.†ïœÛžĘ˜ĩ“›,ūóĨlęŦŠ>ô…áÓí­ŠOÁĮ38û*ãÏ"ŧGčĢŋe‹ÞëõfWæõÚvÝÛ §íøŪ:•;ßÚōÓĮÛ·oW­žÉĖð_ŪXf΍yŦ+>3í:pdå„ė[ ūv*šoî·XŊvČ[*ÜõŅíačSs{ņ#„ˆ_™aœcōVKEß_Bf?š>Ų=ĘáįþĢ1[ĶðúLjįƒęDČmfQā; öûôņåËÛÍ /3ÃK͌=Ķ“1uŦĘczL;ÝcZûý}؅6ÓéņĐ·7/˜ŽĮ4ņbG’ÔėqŲG{`Gyü(Ŋ7UßjŅSFqĐ@}5þˆ›_Ðs|IĘÛ&GŽŠ!å0_›Ļ3m2īvXAŧĀįkŧˆūf•…c_ÞđŽ“ŅÃôņ=Æîģ4ãūðÅÆ—$ōÂw=Ā+čĐž@ĮÉG|ĒOðĢë+Ž…á“ŲGō™ðÓÝ#’LųŋĨa™$IākX­!‡ŲįŊhÞâ€O€Ÿ|<~‡yOr —_ęāÔĢ=ĨGû,Ę‹Ž §“úQßjáÚW{ŦEõņãújËĮ_íe$0œ&<ų#4ÄgéseÉUĖV,—-•îpEnÜôúˆöļëAžŒ=ØAz\øņĢ-ˆęÓSâ.Ų…gP_ą|ēåC†øuˆ/Âg%°ÃĄO@0OãĩašˆÉŋ)ų§˜ =ę5‚OÕŊđ_ý`ŽB|:üĒ0~Ôį!>ęŦŊ€DX/yĪ>;:,ÕĪ˜ģ‘@D%ÕcČHĒ„duG– ũÓė,ûDŌĀ·“ÅúĢL2ū)M’XīēÖþ·Ãĸ—Šáĸ(ū%†ĸGC|g ?j1üĐēĶø9 |gé’Äú!|†āgÏôy“Äúeø‰ŋŊęËž?œ-ÉĸÛŊÁũþáē'ųûEøIīÂĸóÆúAüŪKÛÆð/~ïkj_R—ÝĘZŦi}%~õ”øB°ÛaÉÞ/ iAÍQ•óÉ·C‹áŦâŊØ~įԐëà 7>™ðüT·ģ―N7ękxķņðøŒþzÖFü|PßÝ5o”/ŽĒãNŌHūú`úØÎgø:]üg m>lŲ1—IÍ/6:čÖ°‹“Ï{4ŊyĸæõęËøŲ‹B}{6Ú4ņÄôĩëĮ]qpýÔékW/Ū:iýÍgž|ļ9nũę…ã·ŊxÞpčîՋø/Đ GbøęøŨ/Ėn°vDũ5Sk5­=éŲæ―ËZ™iõÞz5æļĪįø% ūĖ:ðnîĀņe.ŊŠWkÕ ąĘĖ^―ð•ą­g|ĻքIŠlÛ2ņųŽUWæė?ÞŽ$áŦâũÚīöĀö‡|į­}ĸ@•/ėÛHQĀņĸ^?4Œ ú)œGÔíķۆ­]QŲĨ+Ļ jģ@·:ŽŠŽkJŋ”&̚s[TÛ2+'Ēsþ`IS*Ese?XVĶeDŽŌ~õGïfzģ,Aū/·qƒýõáņāÞ{wÅŲŽóĩéĻVk[ 6ĸÄß' Ķgįš‹›/FÎ,k·'ë;ÍMÍķÚ9ū^—ĒŊč1ž%ėâ™ÕĩSœËd\K~č9ŸÃÏté―—Ė™‡;_3štëëJë%ũ-ÄW•kŌ⋂AáróӋF‘ˆą's#?WË―ÞF…Qázl{zž"ÂtČaQ‰ļþ@ø ?JL2]‚a%Fc4 h‰Þƒß?ņ‰`‡ RÄåæ Wûƒ[Ĩo0žxØičr8Ģw_w°î&‡Sčp2ŽÁ|†5Ž\ þPēö ûĨ°§Õ"„ÏáO >ÞŊã["ðA…ð‡Œ?Nú·áû&ŒķĮëīƒÅĒÝōŲØoðQa+á:„ĸ?…ðÂĸŸ /FøĄ!üh„æøøqqĪâü^ü8„ēˆ?Ÿ4 —TØðá;Y áëĸ!ðLŒ!ˆ˜‰KŨÞuJl †ëŏŠâ㏍ÃgåÔ/ĮĢĶÃXĀ}’;ī€FüĻĻņ ųMąßuH.âðđ~ˆÏ4čēĶŊ]X˜ōîlqUƒNĨŊKgœžį{øęŊð‰PøQïĸļĶ;‡ņÓŽŊīՖįŋ h6YW°ÏķŸxŲvýęYۇÍmþû]ëęŊņ—ôĮįtýÚ]‚GũâßĐ--ĨćNM HŒÃg6Š·k3ŽË°€ÕŦËÂą'õu›ĘWm`ęÎÔ㠂øÎÃ4ERðĒAŸxG7aīĩMųŽ/ˆÄôRxŪ‰žŽŪíGÝEhøhyßHā‹’’~˜ã㠓’þČ_*EøáŽ/•~Į"ü°ĮĮ'8|’DøaŽ/–’„âã“ŋ§ãq<žĻ°á‹ ūŒ$Å||‰€Äq* éĸ|:*ZKD ‚ú­EėĨ“ā{ؚ,ð­ÖfÂ+#Z‚čƒO|Į§(>ūŒÆF$GL į\:;ŸœïËØqДp[ïvĶ„[­óóķ™6*ÁžIy3T§oėŦĖOØXđãæę)ąĘÓŨ”āN^éÖÖ–Ō­U õ“ę›b™fdfä'”nÝ?Ģ$ŒĘúāSԀørql/FØáO~€Þf_7ĖŌ>ŽxßbËŦŊv?ۘV– ē―NđÝ|O_VR{AqÄTŨ\°āĩN—ęč* AíØšðÐ(,—Љ/ĻÂ7(•R„ᘸJĨá'$&ū\­ĶņÕj{L|ÄŨãaņ1ĄB!Ä*.øSä8Ū§ņq9ß"Ņ6nŽUZ@]xí Š*›ßD/lЅoPĐh|•Љ‰ĩę&MÔ&`“3`Á)ËÜfäü'ŪfL2O+-/ËÉT|RĨ2Lųß`Â;Ĩ7IŨ™ô4ōŽÖ/{æ”ĻGi %jsˆŒ5n†øYĒŪ‘Kðҁá˜^a°y 6€ðQÂÜė‘—āð] Į 5p|8iÃHüæ|uÛĢėaƒāÎQ6ę'$Å՞šøzŽÆŨ阸"ęF–TŠ–Hhü6™ŋlúvīã@ŲÚ!į^lĶŪÞ/î˜{â[‹zåŪ9īoõó­ÓOŽģiŲąm[_=Úä•Į:.\2―Wæä}Cū~ģtāø _Ï·Ũö ũ­žđŧãđóc–Ɇ;Ũ^<60ũlRüĻ…/•’ūTŠgāũWuę!':Æßņņ@·ņ囧ÏZļöęÜėB ĸ`îŨũ›)üõÖ­6đwÝQūnÍvøĨ7oáCV[‹ÎO;^ļÉuūø|hžëÞx îÁ&į>ŧ[―ó~öõÓ>'ÅEæšøĪ@ŠŊ)Ĩ„GŠÕUkû―ĖKã 3CGįS7Ī þîÃkŨ/Î.„ø“Æų‹>ĀW eßÛ4įČūóöā—ėŦÔClÖIë§žČQ…ôEņ―ņSÆMû\ũdϜLï,Ė,ūŸ―þ‹$#>ņ‰pøŠÍĪÔmÄžOŽsĮęÞ;—Æ?4ĪwîÆã‹öPøE§Û>īČqfĸÂýïg>\}Íŋõ. x|cÚŅ-“NOþĸüéÆĒN?Ų ņÜųeŅ§w=‡Ú1)9ņå4ū@ĀÄŌÖj’Ā[ U~ČüĪ/mƒē"Ã_Á[™ķ!LKŲPē[‹öžÉËý܎[^ϧõ5eâYÞ/GïĢūd·æų+4Js0mëÝüËĐJōė–•i9yö lƒ>Š\iö(“âŪn-|ų|’‰‘ýSSû RÖ+ŋī―] L–ģö$ųËukâŅâËåätyYžņŸ=„ÏĨ˜øô.sL|ú ·ĐJk’ 7;JDˉĩĶZ~ó–Ý>ģxâ·éŲĨKŸ^ šöėÝ^†ðãŋE‡& ŋ=ÂO@L|-ÄoðãŋUÂOPLüV?AąãwFøņ‰ß“ÂïC|‹^ʑô`íϰ$Ÿė/•s"•VXKĮŲÓÉĨ$#áw%ū6äDÂô:ðM<ö4ļøG"! üÔØáŦ„gÁ‡5ՅqxŲĄįv‚>·ĢNę…ÏŒ]Ŧ]p}—ÏvęņKVĖņýŒũŋĖ/æôĐæ_áK~”SüČéÅš™ï˜óųŋŪ>—Šß áŨÂįR?ú>—BøŅ‡ðŋģwĮ(ŽQEc)Õ6B‘Ā ƒÂ^ĄũŋĒĶ݉Ëŋ~QŅ ũ.ádRõœŋ?ðŋ?ðŋ?ðŋ?ðŋ?ðŋ?ðŋ?ðŋ?ðŋ?ðŋ?ð_^ĸüjā;UÅĸÁŊūSāKËņïā7ß)ðĩåøøē"þū͈?‚ß |§ üüVā;õ‚ĸw\– ø’"þuŋøN―âÏÓūψNwð%EüÛ>ā‹Šø'øĀwŠĀ?~ņðü|Mĸĸv\ÁÏSāøŠJüu›ÁņWð5åø ŨŦïTĸą,ā ø·ŸUÐ ü$ þ ~% þ~-þå?I€ŋŸ7ð+IððŦ)ðïĮ~-þļ Ð *ņoë:?ņ7ðkIðŨíøI ü/ðøøē"þö‹ŋ‚_ |§Āï|§ĀŨ–ãāˊøøĀwę ĸ ūΈ?ïā·ß)ðÅåø;øĀw |m9þ ~+ð*ņÏņ_Q‰_VŽÏŪĪŋøN•øøĀw |mųÓŽā ‹øwð5EüüVā;Uâ/ā7ß)ðÕåøðóĀw üþĀwŠĀžøwðëïøęō‘ē'þ~5ðŠâž―P |§Þ‡)ÁÏßĐWüüfā;UĶž€/)þX_Rž4ū°xc |U9>ïj**ņgð•ÅUPðeÅUPðEEü|Yq|Ių<ße?Iŋƒ/(l#‚/+âÏā‹Šø+ø’rüā')ðOðEüøy|vē’øøÕĀwЊ?_ |§ĀWŨÆ€_|§ĀWŨþ―ð`]P8F_YĶdý?|§Jü |]ŸŽ<ðŋ?ð*ðð[ïT‰€ß|§ÞðŊāįïT9Ï~+ð*ņwðïøýïøßíÝOHÛPĀņęiš-ĶR5CPŠLįŸtëœë ŽÃ­ā?ØÖJCă°Cœ ^ĶuŅš?(CŨ ę†ÔĐxô &XÁvŲ†zņЃ0d ŧÎk^›ĶöuĩŽö}9…žäKō;äō’K>>ƒâGƒâ§’ð͈%ņ/ §F{BüėKČĐ)VŠņ5`ü %rŠ01>ÄG’ÅĸÄ'PüØ øĐ$íã['ĄMô`-NA+0ĄøGzööAÛßęÝf1Xæ4‹â Ú;vjĄítīc<ĩ%W -?ŽŊSĨ{üV%īÖĢøļY ïfh|CzĮŋ MŒĖm3b 7ŸBņĨ^ëõ^wïcwŋ3jü{Æ.―ūŲŪ)PkĄø`|ĢT7ųnú·Ï9åq#ã›Am#cęį­æ0M3HŸJïø ũėۃmōųķ<ŽH6Žãģũ‡]–Ív­|ų9ōg`Ï?;āyģ44° ÅãwÚ$ü>ŊÍÖ62ŨÕh·EŌŒoĐļFžĮĄþÅđ­™ŅVĸ[ÏâĖV‹ĩÄĒø@|ŧ„{Ũnoâļąžf{$b|Āŋ=a2 -xôÜŊîúķÅŪÕ>zrģĨÎ焯ÏJëøNŦÔÔðÚüԘÏ;>üՁ3ŋâhjz\Žé+]=Œŋé)_ {§ZęBΊ P|0ūÔ8Įí~›l/üņ$züĘ ũęélŲJĸr―kųå{neýį™ĸTW BņÁøuЎãgÂËFņÅøĢõÐFƒņKŪĸc|<­ãvė?„ķß1ˆņ ŪZŠa•TŌ(þ‡ũAžŒgĒ)­1ĢøGŒ q0bÖ3ÄĮųøũÓ5þYŌPüXĄøĐ$ŋŠ H?‰Ā Ŧ(ĸ$(~*ãS(~ēéēPü$ãŦƒņË)]"]čHRˆOŠĐËZUĖ4 Š$5•ʐÅ`0ðKÉ^œ”ĢI$Ï.+4Ĩjĩ–S\ԗâEąb†æå‹rÂčA99ų4Cĸ/D—EÞŋJNdų2čÄaâ%…ËPC) įÜ­Ļú_ ‰c}\–pGĖ™Ög@Շk_T–ĄPœã˜"` Č;>įäaS&ðKĶ‹ŒāˆVB&’ðÔQÅ9īDŲy…"S‘1w'I2Ū+C‘ų/ë ĩ†E~îIENDŪB`‚doc/images/ifw-settings-repositories.png000066400000000000000000000270241325366651500207670ustar00rootroot00000000000000‰PNG  IHDR|iĻ"cÐýPLTEÁÁÁėėėyyyĸĸĸðððôôõíííÔįĸüüüõũųŲÚÝöööķķ횚›ŽŽŽōóõåņþ°°°ģģģĖÍϝœqqqšššââãŌÓÔęôĸŋÁÆÝėĸ’’’žžž•••ļļļ‹‹‹Đ­ī—˜˜ČČËPPPää厎ŽÕÕÖŅÐÐęęëÝÞáÚÚÚeeeĢĢĢĢĶŽ·šūžžŸĮĮĮïïïŦÆįĢÂčŋŋŋnnnĀÁÂRSS\\\Ą  §ĻĻŨŨŨÎÎŌÃÃÃĨĪĶčččĘĘËųúúėóöÝÝÝ```éņõŅâëāįëŧØãuuuÝÍ·ũĸĸvĢÅÂÅČãîðÅØäĮÜčĖÝįŲÕÆÐĮžhqœŌïũÂáøÆŦÓž˜>ą*ÃIDATxÚė™hQĮÝ8’H’ZjšĪÕībŠĐ[kM8pTˆ"‚qÜ{Õ=ęÞuïũĻļGŁ‚WÅEÁßŧ—ŧwÉw—ģ*"š_ĩūäōĐųÝŋĸ{―æËŸ?_õqÉ”r%?õ&ÚÉųƕ#æóHÖ%ÅaRS……@ETYĶ9)ņ #Á͈%$ ļD’’œ~L”H3V&sIIfØ$d$ ^˜H‰%ļFBžNJ°ŽRķHDĖūKTč{#^—\ ū|Éz—)RЉĮžĩā7i’ĀĶÍЁGÏcĐAIüĪĪĪ8Žˆˆ­VDó”t‹Ūltt ž”Ð6ÄÆd$&FáHIĄĸbPƒbŅS áĪ ÐũgHīÔbɈ_>þpUž[„ņ78§!Üë\AæĐlW0ų$' „z€`Āæfžš˜Č@û1Ô>’OĮaöĐ~äúgö™~ņ=Rų€É XSýōAïÝ[wýŒ=šųtW€ČŸÅE&EËWÎz’rŅ5û•VĀ>rÕ!ÕcųZ”|Ņa|dp)ĖPĸŌč#ûüH„ƒØĮúm~ĸzø…Óoö ?֒Ęņx õšųĩ{ų-"\`ÕšnAŒU·ÖwÉpðÖŽ8œŊ €&ŦAcÓĖ|“‚mڂŒxRÚ Ÿ„?/îõT― ë9ųČ>ŠūLą9d^öôf_/~ŠžŨŋýō―ņą<ūÚ֘ÚYN7!}ÁhĘN^þúĘĢĘŠĩŽ<ÚIĖųŧ@>W7_rŅVyÆ―ûũsŧŨ3E›IčA<―âā‹ý4ü‚{TõļėqÕ3ņĖ―BōˆŽ}˜aŲ—/ | ûذ~î‘ïvņd=}šUËîį‚Ņ;ĨŽ·ÖŪ]úÆDéSRų@óAsįN;XDįÕ@ėÝÆôÕöÔdŨuÏûËdéßԈö“@ū|Ų°z@Ąp(L>*ýÅĢeÅÃÆdækß ? ë—m~$‡ÆF)›U'֗Æ/ûܞļ…pĖϖšĨ‹xũ;KåkŠw?xðāց^ŊŲđOïųTûĪ;qíđ=ŧy‚ëČéûą‰Īx˜zzÔ8ŠągŅWŊ ąã•‚}ÜúðÁæØ–‡f_!üėŌKå+ëŨÄ6L‹Ķø 5}^ēˆ‰™’ģe:ÏTJGGéōî]þGôČÄRų†GũîĩĸļļęđėJNü'ĄdŌ''<ĸîąþÝĖĻ>5™ŽŨ'Íw(å •ïōËĮęqęCšwæŲŅ XäģŊPûšāAĐ}‡šõî‰m˜Mņú|>ĸ2HþŪ„:z/{C°å[Ýŧ·ļ[É2đē+‘ÂÏÜįíâéYwÖØ)/†YüîÄíŲuWĖÍŽn—qĻš}·(ßĪ{=J}ČÂaō™FĻž}Qūš}ŠhŸĘ—/óŧ'@~ŠÏÚī]iAū4ø7īÚ]û-Úë9ĘÉxĸu›ŌE‡ä~˜káŊķ{úöūxļáá•w‹=9/= ?Ãģ?{åîļ­―M8Zú đĸ™Øcų2›vĩoĩčšVËÉû0Bå+„Ÿm;‘}@”ŋÃæ X|>Ŋ99gbNÎūãá|‚5@ŸÎéØųĐ­ŋlÏÓ―Ëœî ĄvŒIÆņ§ēšÂUÖCä7!ōģ=·ģîæzfÔL?zÄhäw›|ð5ĻëUĖ#ņØ>“ˆ“/ŧßD­ĒĘ*‚ę§(ûĮú™·Ũ`­ĖäÛý&NÜ21ËwqŌäsõŋ~ĘÍÍR;ĘN:ĸ‚―îØGûWTË6æ ŧŌj‡ĘQÎ~)ãP5{Æa·ĐpÍŦđĮōĨõģÏ4ēaöņ·Zl* xč†3”}ōgÛN~â?Q§ŦŒđį­Ņ”úüōõSÜųzWýoŸ?|Č-S2ËNvų‡RÓļô”ˆm\zÂĢtGå>]Á…6Ę39ba†+ÂE[‡ė4QáPũĖūĒz&•·ZøiŒŲn_9ûčŒQûĖŋō•WíÓð›LŅ Ŧ3ë@GŽNIt2ųf›~č™ËŲrfUŨÁÝŌúœ éi)ĮÏsņÂFSꞚ§(íëąyėõG^wē/sŌðcö‘~Ē^j7?–1ųH$/@7”(õĀï0ŊýžØ— â)üõâTĘĮ͚~CõcųðzĀ˜$/–ý͊nq·õJæą}@ĨxÐn_ĩöņ#ōRûXŋ^9üä ú4ųÓŊ|ˆwC#ņōŅ9˜X=šu‰ÔŦļÏSï`‘Ę?XQž#í3ý•ægōÕēŊ$ߔhúyÐY1kBcČz„ ĄTՆ4lYT2Ą.Ė $:Eųn6ĖÅbåK óG)–ĸĢ„åĸK„åĸy°|ļ3–ĸGĀōđ°|UÂōĸ%~ƒü ō…þWDųšŸ–W)ęW04-ôŋâ—_.P~Jy !ķāŊ04,ŸÓh$ōãĒJü8Đaų?-ßl–‘ßīpÉ&.,ĸ7Ë/ųãEXþï–_Ļ 0 ―e•‹eߥUĪôū>P\sĒüÚĩÛÝ9ØnŅķ—į/ë,ާ5ËW•_XzāpýųŦČrøãfE$ô^7P\KäŲ8ŊkŊþS,Óz§”ĩī†E·Úm-{<›ú―]=J—Uß3yϙæ“t–ý3-Ó‡å+ÉŊÂË?uúÄü ÏŅq'7ŋ{ø4ûÕšg}ĩŨéŲ”ÓcŸ^îũxˆŒü7.ß3Ų—9{ðÍ#Oy:þԕõVŋūø|ĸî3û]<·ýÎú+™­2}VËÆ°|ųQjKO=o˜yņô„Ėš—GœžvjûŽ&Ũ·/ØoØŅģž;2ZÔĒ%ĩģáíĀ‘û7ī[qaY዆mđþÎĒ“Ý·ŊžŋŒœ—ŪV~ýRŧvÞ,šs0œüBĨŒōōKŨÖt:ģ`G§]Éöą ?nåÓ §žÛĮlZ;{ė•FG74Đã§Đ$ųßŲ7ũ˜ĶŪ0€ĸqĐk­+֎ nš••A3Æ&…m(KQķ ‘–HĨ%XZËĢķ…ī•ˆERyĩ J—”yÉÛ ŅčĖ&Š…A2ÅÄāŋûnÁÖōÚ| ëCóÝsŋsÎ폓ÓÛ{B@QGe~‚Ā"ŸĮĄĪNbŸŸÕæĨV6þŽčŨŌĨŧF°‚ý2ų„EþrōĮOŨŽo˄P|ų–þl“j&'A8@fŒÚ59ÍąTþ%Qlk™<šVæÜՑ•™#ƒJIĩ6ūđU–“ã/ųZiđĩZĐŽīČ_nŲŲïč—=žī6īW74·fĐĒÔãķ犧S7‡ƒnK1FÜ@þ '˜‹æŋņaOîþÐD~Q.s1ؘ•ĸūųy9Šđ’šįĀļ„hĀKOjņŠwØëõ`ģ―€äCözŲS(8låRHbxƒņĪ―Ĩ<'Ęö0c+xžlļā@Cf ^@rī·ŽũĨÛã){}y›)Ð šM;ÕįáūL„É<_&ÏWÃ™Š™‚†ņķ^öĻTw\s!€!žÓF‡)<ļMN›qwüÕ!&Ũ1ˆT )§™pj+Wp$?w!Øļōß7#ŸûC=ÍO“M% ŧēõóōÝ™ėœŠĨeũuS ŧĐŲ’@–MŧũvEÄÚę‚člęŪKÝiCs­`bRƒjģ[ƒ"&;gÐljNÜš^YžÕEUąsjzššÚĄ[Ý`WíŠĒá|MId6ė…]`ŲļÃ=ųīė{Ų\Ž(ž fSmīlBÓÜ5yïVWc―НöõfrųTsņ8ė^Úī?fÔŌ&ŅēëuuTō=Ų|°åï4ʧ˜“ŸöüũØDVĮvVtįÉpųžü]͙>yÓÅJ=­@”_\^UÖ8(ĐMÖ$mĄĘąÖĨ^Vï%o•7FæTĨu+ŸÎ ^ĢHsŠEŨneēƒóŌFûåųšė$—ûIĮ―`Óō$Œ‹&WĢōÃå•ŌĘŦ§ÉJuOZđ_QnžĒv‚h•z>:Oj8ÏúŽdđ ÎÂō5ruđbv°XųīŋSÔ?lÄegų†ŠÐŠûŽūHÐ8a”Ÿz +ísdöËÏYņoV‰ÐRÍ 1ČkŠXÚ vōā―ØÖîú"ų ÔčžØVB›ÝxŊĪ•Ð‹đ†ÔKČkĮœüfä‹―#Äq2Ņ―‰ĮļpėAśë%ā qĮ^Š7_,já]%:{Ý)$ ÄXū8ÍØJÔ-ų[ķÄØĮņãˆįļGķŠĒ˜aW·†SØâH@ŧbĢxBōķ-1Þ īžáãÍĨx§ Ãyq)^žŦÐQĒhÏü-paÛv—ÂX1gá ka†ĩāá1†ŧ$hŲ 3ä[ūd―lLåŧÎÉwĩČɘʷuuĨXäŋ2ĖËwĩČ7ËŦïa‘ožŨ&ŸēÛ‹ƒn"ĸāĒ|[ų;NãWg“ųę/1øõąi}Éoŧņfäŧđåŧđ™‘ĸē:óÕx,ē>0ëK~ã0+ß>Ģü}ų&žlų”9ųNû,ōĸƛ!ßúÉ·^_ōĮK“ÏHÚÎÍÚc"ĸ— D—ī}Í>ąšFũމUö '“Uï˜&pŲđ‘ČdųNsō)fäcWąB°Øēā­aą‰ØĨ`čX,ük"–žŽÖÖ$o.ņgۘÏëŅd,–Ë >[iš·Öš(JĸÔ.*ËîŽoWļī(Y„\<Ã.IGš‹;šË#$―ÃOĸü0ÃŪŋ3d>Y ŦũfØ] ļšÓŪŨÍ)‡ą‚ÜÔcõgė> áĨ{ūw —Öë…Û˜•ïâ‚ĘßïâB9ļœ|úōeĐ|Đę[Ÿ"UÆĢsP―D>[âyŒmóÅÍĐæĖÛÍ­jýQAz~ĮČīŠDĸĻä~åqõÃ%ō}Kę―Š9=ĩ…Î …gŒðĄFY`Č,SžÛÝ728>RwĢDĸ—ü1rĮá”Ņ+ękMúþÆcN™ÄóT%ËÉýĢn\}Ąē9Søũäúœ|úk,+Ęw_V>ĘgūTrVÐ4Š7Îä/1 ōuœ\ąp:īxøÔ?‹AYŅ:rQðŊógeųŸ|bN>fUčßá0~‰’ŽKÉ9wŲJrŦTã6Á‹ĩLI–æ‘süíäŪĐųąž0ó…ĄäĖ…~ĀįŸėÜyL’aðķ7ŽÖ•õÖčJ°kĨåĨĪĨÎȌ`CiĮĶ•–VF­ËđÖōŠV†•Ņ1;f–.sÍR鰚ÍjŲeYvýŅŋýÞÍę}!ĒbÏwoæó{<ïöƒ—Įũåƒąŋ!°!­ŋõAîķėp“e―Đ*?nj_ëYYĄ—Ļd]ÍōÚú Eāã þķ )Ųó‚ŸWģ1;ĶŌSŽįŨĀá›·at‡ÐáûŒJāϚåã~/!ÄņhŽP„‡ôˆÂCH|üôÅEá_>‡#ÆqĶ8„AnB\ ė)騠å>Sâp/LãKÎ)$6xx8ŅĖ‹ÂÅ!]ÍP‚#Ŋވ‚Ð=L8ÔáĻ“‡[]gfpÝ īøģH|-þÎ/øte“Ūjg›7UĪÛjėđ―0j§[ÄþNâ zS‹NāÃ<ŋÓĖøn”ļPjŦąįÂŅųœî‘I4øĄøļ·w(?˜ųËôÄ{Ō•}Ŕ’óØnöeþoĄÅũöŲÄ(@ų[ĄÃy{ã6ņEė#õCq6åīøC|Á!|ĘšTĘEq6ëhðņ!øĒ_âûEq:ĢéðĮ'ņĮ·/bw⋠[Ž“ Š^vN—ŋ°ĻĻ1zÜš]§døô"?ĒÎåq=hĢÃøøø>>ķņ‡ģ‡ô™MāsŸwōršáĘ―su€?ãÆÍúÔ''S`˜Îã―kČŪ/{V˜^|Đ_`úųÚįđÆóå~§wÜhŽŠŋ|ĨÐR_ŲþZûP“ŲïJÔyøb+ūĀ6>sŽYņĩ€õuCó1Ëéē…eĮ>–jŸœÔ>4īĀðUŧåoŽ3ZXĢ&;ųbæýŲé ~ģ1Ž:hÖÅ7ŧ”užG…_Jā CCmãũŸiýşøë5íõÚ',hĻJSŸÄ4XņYŽŧŠÝGTŧYauÆ0C‹æ€,'S6ĪšðÉš·ÄŠ_ÖÂbą ÎōĻ8‹?SĖîÄŊÔóŦĖųšüvÐ>­Tí6} lüU í$þ‚ęžĀĻ:=ĸĶĸƒ úZÝy~^Ûés+-OŦõüķVĸINߒu–G…_$ZņÅôøÖ/ ėĀgÍWŦážŊV›óeÄRø%Đ,_m†š”ÅZ VÏ7‰.ĐZMė•Aį|5QOfI‰xÄbŠ=|‘Č~Wõ 3xØģÚY|!Ž3Ÿéþ?ĮRžîōģ‰|ĸ5>|c)ŠģM‡/ØÃGų—WŽ1ŋá þw醸ōËĮš:ėOÂ.đ"ˆėeÝOÆ<#6ðí―áR—ebšļåž"NÁ§}Ãõ™ũd9ŠŋаvÏĢ{Ģ‚Vų+?{]ˆ‚JÐ3nDaė‰š ōóŲû:,ëÎė‘áĖ\ų…-Įs&–]á*eēÖé"&ŋą„Ë/HĄóöįd˜íV"Ô1ũŽk5§3`ЃøĘ‚ėUËîūđþþã­ũ7O~jÐ–šŒ›ëŌKTjFLŽŠõSÛ KäûĻHloƒ.N{;%ŠéR ėÝ1_“]bŲB\ŪfŽÓúÉk[+ÛöĶ–&]ÉÉ\\ĩ!sïÐā ˜8qfP~°aýÅ_SĄŸ—œ%ï%ķÖÔÝ ou†4ė} ė:’Ī‚Šē)m”àa_ģdÂmļz<Ĩ%åܒˆe1ũ‹Ô‹cßA}ËņÞÅp9ßĢÔR˜-;bP‘sïÐáŸ2%ā7ņ•MF€íÄĸĪËJ-mjSÔĀŪ”sûŊ>öB•É#{âō˘ēĐ}MRÍÆ\øęžD“‘ÄßŊJŽÛÚJâÃlÔ1ũŽüáŽâËïpąļˊŒŧ_’ũ[kj Ē%*WÉï =RПŨjhÄãkØ ) ÅõĪ@EoSąbŦlŸ"/M~':>C”ĒЛa#f[ uĖ―ãūý<‚įyWhÏ áĶ ÅUø ō2]$Âĸ ü~Îá왑ÔbĒā‡bÏ^ =ū‰ßðŅōBWū;ÅÎËÎ`ĮðÄKā_$åe†É&ũâpëÂ;˜0ĪOb4ûŧiÝōÖŲïåՁ?ŽŸúö gïô > ïõå;ý0Ė‘·ZķüÚÚŪïïÝôŲý{þ->P·ýT|īžö—ņĨŊėhø”ÔŠđ{Ë*VHb‰á‡7Í5šy_TÉû/ó·þ^mm) [ În˃æZsVĨ"]ū,þ ņQKĄ{_ûļŽqĸåžë›+î{RjäĻßĀP+Ãâž“Ú1ŒļÁßTu—ĶÃS―äfXJvĮ0+žĀÏ3“ÍM&ÜŦË]œYb9ÄþķČpĄĩRGŽðddlīÂÜ6ŽŊŽØÅĐÂVÕ=;’ũxb}R:8ē$–ZņcđÖáī{Ĩ0ī.ŊYũ&t,ŊųėĖÕu.Ŋ%v,ŊUgNŋķzgŪû.,ÛÁï3j”#ø˜ÉrÂdėŸ?sWŧ't-a‹•l]ZQõŽvá“C“q{e›u Ž6Yb iÐŽđîœb$ðKãīiō'I|SUčúÄāũ]^û øĘ$Iė’Žģn-‘įJjVųAiFōüÔPë0aՊˆSC#ˆá]ÖŲWĢüČ/~aÃðvŌÉõĄĘƒîžÜ/Ų:7âZˆ)3\žŧ5Yų™Ø6JēåPĮÜ5tøƒÁw>h…§{ākÜũõžÛã{zūãq%þH„O‰+ðûGøÔļp?„O‰ëðû!|J\„?áĸ„ïNAøŽĮ•øÃÐßp)qþ0„O‰ŦðG |J\‡?áSâ*ü1ŸWáO3b§ÄUøc>%.Ÿ‰ð)qå3ŋÂĸ.ߝbïláĸ„ïNAøŽĮĩø}þũAøî„ïD\qЉð)q~_„OÉ?ĮÞąžÐŨ áSâ*|/„O‰ ð―>Mū;Å~§áŧSláĢģjū;Å>ú„KËðŅÚÎWöîĮAŠĒðˆĨD#*ó#ŠYÂėiÓ―dŠÜâęœ%|2lŨŽø+óü’ ĸøIņØüĪXí܊u~LWoļ|ÉR1XËüņĀO üņĀOŠyþxā'ÅKÖ―/Īô ŸÍ5ð“ĸN<ós<ð“šZį?~6ðKŪŲø§ĀOJáÏāËĀO üņĀO üņ<øðU&üĮ~͂ŋ/?)‰€ŊēāOĮūȃŋ‚ŊráÏāŨ,øŊū‚/?Đøø5~_äÁoāŦ,øÏÖ'ðküŋgkā‹Žøü:R2āïāŦLøû üš Ÿģ”k6|ķÕløü([ģáģ9ĒfÃį°ĢøI]ā/[AOŸÔ~ëā—Løsãô’ŋĸÅô<|ÛÛ/øoŸ”ÄoāŦžø;øïŸ”/3á7ðEā'þx.ü üøI?žŋƒŊ?)ðĮ?)ßÁYð'ðeNüüöî·m ĀðBW'ĐC­Ø*ðÝÓXün}oŠP“bŸúŠė3 ÔhüųWŋŨs†ÕüO ĐÄZų ^bóډĸî>7/PĶOjVC ĩũĨūČįķ ãŽ>ĨD ūJVģĐø ~ÍĀâŨ ĐōQy„už1ņWŸ:6?}ĸóÖâ·ŠL‘šįģöyiRŽn=â?ėįSņåãĄĩø›t+qÚÞĮģÍ·å^âÄųÂ#þŨ>ˆý_Uygņ›=ßá$ó8ŌYĩÃ)Ž=âW?ķŒMÅ7 VĘâW]oxģŌ1/p:ņËvøÂ?Ā·ú?‹7HŧĒŊ;npjŽņƒaüüū:ĐJâ"Ėī^”āŊ‰øN2zđĩƀęŋõĶðäž :Œĸņ3u­ōáĐ ™ëkGXSŨëÆ‰ŸœãSrÎF$|”Üø‚ðrã üßėÝYˆaĀņ‡oÆ,5q·ŌBÝT6-)wĐĖÚĩ"‹;fš‰,§”p6֘mŦĄēhd c‰ęĄčĒÂȊÚí°":ąûĄōĄ ŠĒĻ ›zí›ėĪĐ7%Ė?ŧģó}ÂOũÛUÁ)MÂĮFŽā—*áÃóéīøęú/! R1ņĩ?ãŧFįžŸ)-PÚP%(ŨŽ(Œ? ũ–Ęã&yAl,øÚėŅĢ@ÁyBïŨËā§ĪXÐ Ę4ƒü_‰Ē2 Ä_•ņŒüfpīB ZÅyŋ^Ūā âŦ!―L6räWüHÜØî‰+SˆūՒ”‚ķ6$hķNÓāÔ_‹QFzï –dŒÆ06Þk…Ež4Ë5ąFŽQôÐދ:œ}ą=KNŸsÓĮÁÝē$Ģ0†Ģ 7 4SÆœAŧ#;Ķ6ģA&€ïÆGQHĸþāD°‘ŅÚ:2ģ@ĀáÁęXyÃXjĶkxžÄ™åšÆuÎ. y0ĩãštžøT ‚ģ)CŊšlŠũŠ:U°Y…ŨyšrmWŅqŪ+6"aK·géíjŊƒ38œ—ÐDpĮ,åXÞøĩðaĸ>Đ&eiģŦÅû ?ãę֔Btm˜.5+Ā>%ĨXUjV3Eb4AÂ―Ļzƒ€Ū-Žļš1œũN"Õ &6ĨSYˆŋ$5kICq JšØq–›W)&‡ÛSD0DĐ&‚r,|ĸ?ĀN@UũūČÕc˜~€šĐĢ9üA ą žJđ!ūj!ÖÎã7'îvœSeR0.Îá&ŌDéČč ―kÂÄ5 â›Ļ°z,NâķހIđ(wäNz(Į„ðõBøžžT*åņAīF>a6!tå/xáuŊąu;.ģŽ‰áÎ*ÛöŲx :îŒ|0Đn đßcxYĮ€!ïÃ(ŠúĖ 6O(-Ÿm‰‘ąÛöh‹4ÚŌ\#DÓ5m|ž!p\Ý"Å"åų7W_/€ï1bD=Ž—üūæÔ,ðSLø;ūUœ8ËŲm ,Ë_ú5ð§ĪBÛĪŋŲ#Ë3aüŠĘk;UðËĐ ~þUðËĐ ~•‘ˆ‘æÆŽ,`°đdöË@“ÕŠ;"ôNÖ·Ģĸ āŨú†kDįëøĪf“øąÃ}€­Ū?ļڍ€Ē%ŦröS^Ã0‰X|_ĢL|’Á9ü>™øjÝđ{}ļR&>ÍPīú+ŅN$é§|7‡ŊĉŠOÖ'‡ï“ĄdĘáwCŧTŋE:‘Ōa) þHß_Ā_ˆPqņ•Hgú=ū^$>~\‰ÆúŒųðív{Œå$vqíÜ1žþ-üX"< ža%Må‰ïŪ§=ĘÎā‡0EßîŠ š !HeĐņCóý{VoØŅ_ōC/Ÿí™đ/·MļiŦĮōg_†ĩƇpŽ9Ļö<ņ‰‚qcšt}ÆšŸÅŅ"“bĮGÅ41Bw|T>ø9pxŧ$?zÍ  ĨÆ_?ũ^4ĩáœvvsN2Mb)5ÕŌÚū\‘―9įšz͜øÛ9ÃÂtĪÉÆžÆęķ$;nZŋųˆƇ)‡Ē"ý&1ƒ|ÔŠ#čüðˆĮTîīĩ­đÞq-ž! ĢŲ6&Cô!ŠZōÆGp›ŧĶž} ORO‰ņFߜäšķ]é~äGOlxxŧõ釉ęi“Þ>‘íąĄuÍÐ-·>é~ðÜõĢ/:vm;ąąuþšŊŽ<š°ėÐĢ]wÕšŋ‚mHēa,‚`5ôvˎ63žéŦŨ “â&U5)ÆNˆ“éä­ū}bþøpų2>`SÉþĨ_vÖjâ§On|ˆoV§bwܘļ­a Cį―ėDûM`číhL>‰€øŠ`ŽßĪĶŽLŒâWqøÜ46€IŒ‹GF•ŸĄ­;įöž„o|?sÁ˓ŊÖķ&N=·Z!Ŋuį‘3ÓgģmXw7{ęĘþóg7Ž―{ny79đh)yŧõzĮĶģÝþ ~(D—8“ŽĒîAÜčlÏŲÁ$ĨĻģUņøžN‘cũĄLtþˎ2Ę*đ=hž—ĸ˜ßŋšä?Úķįþ%lÆáéCŪ™Íߌoó/‡|ëFŋĸėÎMg^ž|0ßï?ąxE2>Ãŋ|ŲiĸÖÖ_áËÅā+øûnį?…á?!ÏĸóUæ JĶīKļ•ŸJ"Qæ‡ÏOÂïÎßķ‘ ĨÆíŪ7 ŊķhŠ-–z<ná_ɝԛܖúúj ÜCãæ·YÜĶO?wŨŧŦá·ÆõüʓŽ?ā[:‘đ—x|™Žī//ČÏøŪB/,&ūģķ3øjđh|MuĢDlý5ĶÏøæþĒ7š\€OâSKDWëkE 1֘]…7Â8@_û|āԈÏ,ˍUš5âër5j ĻąZđŠUđĀ/ð+ïd•  þ?P_DÅÅŨVðSŋœŠālïþBšˆŽŊ˜{ˆ]âyž>Üþxī™›ÃXD‰4œ&2›iAųRųbž‘ųr!ōO/õĒ=ôgb"éSâ&öĒ"A$ŅK=HЃ{ëÎÓÛoí·??™—lŋÏÃ`ėî~ŋûŽûą‡LsðøF?)?›ĪŒ_ˆ˜R0ū9>ūãvpōcâ[Ôø˜&püC@ŠoÆņӃãg?}8~ĶųÃMČÂ~―‚ĪXd.'ŽŋŨ~+rYdKĐo?ÏŧQj>ŸÍéøĄ'W}uče”-/Ya‹ų?ܜŽŋ~Ųú^ür=š< ĮÏL|wj]Ý1p|0~DŸÕ*tU>í8]oˆņŌj]lũ@ų1ņYĸ_­‹ß–Úü™Qâ·Înžzí…íÏéq| ū/ÎÛđnŸïŠ(nũ4ø`Ôø<Ļók˜į‹ûæƒ=3íóÁŋæ‚=CāāĢÁzä&p|5þ†7N‹(x―3ģķ-/ԆúkĮhž#HŊ-S'ķG„WķŠïĸü8"íUö€l8~ōøÓÝ^ïÐââfÅJŠøN@ãtØélé­ģ;ø§>I[Įo3Ģ―—Į Œï7ķžúûųĶ(L. ~5~q5čދUōÍÅþĮórüå%ątæÆ Y)mŊƒņ‰\ b’ãš G'Æ?7Á>Æ/‹ŅËq w‡đwUkã}k_žqýä0'šÞ—”ĀøtÎĮoF_‚ÎߔÛņĐņ=%'‘9XŨ­Pä:ēHĻY/sąyP9ĘˆOär|^ļķBđ^æd Á 8þ.Cý>ėåãmû`Ðãø‡ŽŊ90>…ãkĸ,Ëâø)āøŲĮŨ<~ Áq|™p|mÁã°E˜ĶĖF%>IU$mI—@ĶE:ÐBÓ&$īĂ0ÄÁ°Hč8Ķ 0ÖR)Å·ÖËēæTÔc˜Įģ‚Īũ #ŸLPi#$ōhŌ(ϘLRn{‘…ĖÅt:ÎîĒŌŽoV Äę#ĖP‰ĸ?ëŦ—RōCãģ ä/Āeįtš#G9ÆNËËN:aĒõĶ ’”O†=ątbƌ_›Ô†rã2Čęō„ĒĀĖUHņušĀđ Lc\›”þ/؀ũÍŧŪ#IENDŪB`‚doc/images/ifw-target-directory-page.png000066400000000000000000000575211325366651500206110ustar00rootroot00000000000000‰PNG  IHDRÎGæąm_IDATxė’ÍŪÚ@ …QĨ,‘xxĸ-â"]ĩĒ *`Hf2“Û-EÖ5qïQ°U‹ ĨRÏšĸäƒÁl6Ëó<ÆøýRŧÝNū˜Ō…ú §Ö~ŋŨ)[äGžÞiŽjwøŦ|Īó?Ÿ~ԃðb“7Ēþ'ßČGģúÝPéŋ'[įƒó“ó‰1æy>N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/fŦ>6ŽĢŠÏĖîí}Úw>;vė‹]'ņWįĢNQ Ĩ* īI!*ĄŌ’‚*·ąĒ(óA\*"þ*BŠ„Zņ! ĪP"’šŠR+Ō€‚Bl“ΉėæÜģßųūvgfw–9Ŋō4ÝX%*ņģ4šũæÍ›ßüÞóĐúņVMúU­T DļÕLĸķū|#l„ú‚˜ĸ'üHÜ>?ęįÜņ DĻTž# sf˜Ĩ€Y6Žr˜ÓÆH0Î-NĐôBĀ„P<Ũu]ŧ 9ÅbrīmûfČ įXg Mĩzk›hŅ-„jęâF1NĨƒÜÂį 'jVŪëĘŅģʑ1æ…õ’‘ð{Į›ĮĪËĒ+ݟ˜[iyóø+éĒ‹0!ČļC4~éí“Ŋþ•õ ėÛ·ĸĀúU-z8đņņ­ûķ­IūwîėŪ_ýāęEí=îčÝÆT˜EaÚĸrüä;ÖSŋĩũévîčŸGļãޘĖĨÖlÜÝύŒ\›`ÜãT’čb‚„#ĸÄDz6õ‘MÏîíŊʎ\›˜žļza*īþËÛwôn‰ãĀݟŲÐÖŌÖŧëĐŧZ“mwú™Ý{úŸØ4sõo“.™*‘t—)ÐŲ+ŋúí‰ú{·ėęĸbîâ[gĮg áåĖdûš-Ï>ņĐÚ ÃäPsņýoþxėg?øöó/üüJVä.ŋqôĩᇟÜūõ‘îó§~ŸÖęÖķ·uÞóp_ßWššU2%!6·^pŠ~ĘÅēUÝņôÎÁ};zýØðDYÃ.HWՏ7ÓžúQ…Ą*d㍊›ŠX”•§pˆsģzŠš4 0AÍJHłdĒVČ&rýoĶÛįGšÝ>?„R û―åP*ÅŠâZcn]Š;ŧðŌÜÔlŨÔR‹Īå™jŌápXŠ2‰ČöZ]]]UUF€\FØ„ëÖÄęv &RÝĐOÅR Ģ ÉHu5Ṋēũe)“s:ŊËĻBQĀËGšÁCJĀš_·š78Č8°ÝĮoŪr y!2|zÁ}z~ní˜āOþüĻ)qÛąŠųhÛGïYÞđėūûãĒd™åsïėiuĖž­ŊYĻåË&nP#2Ē:ŅtÆ(ģx]gÏēa&gķÃįēĘ\;ĸōũŋũÂK‡gLĪcGžĀtcIGŧÎhSgŧ,MÎRŪÛš: ›5vīĢ89k!L\Qq–ō@›É•E+ęÃļX,Ę Kđą?yųŧÏĸdhZ3*šĄĻA„°L“Ų&ržIëÚZŦƒÔDŅ‹ëËīh[<ī°m͊%ØqŒH%—›ũå•V‹°—1il銞;[ãÁĻn›Vý’5›zĒGüC/ū:U°„ƒt fQZ9ýÚúé‰3H Øs\™5ķįĒ!$hĐ`ðÕÓŊüčĨã$ڐ0c…PÊĩËȎYĄÜ{PþäæūŧŸ4&ÎþæŨĮŪū›fþOŋxņ—§FŦ“‹0c’5Í@Ôīå“F•øØĀ’𛄗,_'QĒôŠ\1ššÄq1Æn…WLlAâņ™ū˜Î2VÎÎäFÎŧPŠ~mŨķĩĐ$"•I4ÄÄ,•É{}Αp$;6öîtĐ<û^úZ&Č`ŧēYSÎ’é Üær‘ķʁHĮvĐTī8GĻŌē Ô^ũØÖoėz4?|æėč d;w(ãfvôð‡>öøöÞÍD‰†]ls!“–ÔÉĪå·kÄŠ‘Þ}ßöxnð™eu†-ķqĐP–üĀkĖL\ÏĖÄR+š·ÅĖÎ4ĩķ„Đõ_ęÝ?øõÁ=.0\.ßb*[b™ØL0Îō™Ëį.ŒĢŠT …ôXÖĪÓãĸĆVÕ·…ÐŒ †Ðøû$įӏ„ŠūS ūTĻu„‚ÏßW)jbÐFĀyޟĒūŦĐjø”?/?^æóōãY?<~ 7ôÆŋHģšžĶĄ0üëôE>ŒŊ~mmŨĩl™‚ œY$B‚X  : Pl8YeĨ[ŋX1þâ%ņGxĪæhÎ.ļð―8yrúžÏ9}ōžWĮ0LÓlÜUģŲŦešÕĨEmzōú㇟ĩęyŨÎfūdfŠŌ°,Ā4ØbYVĨRQUž‰ÝÞÞÞÜ܀ŊårŌ ÝÐōōâÛbēڒžëå{.w<ņ͐Ūk ÐųÃŅu°‚vØE öƒS þŠïŦāˆ@tÁe ĸÞđp°‰ļė€ģÓˆó.Î^0xæÓ™k5LËnÔŦ)§ÆŌó·åkĨÂč`x˜bY‘ÏÏLũøý|dęĩĒ[PÄvė͗OŧÃXl,#BdbŠXW3 öðq?ĮĮú{ĢkZËU§Đ‘úy!>Šæ‹šï;đT$)ø—šHËû§ūgŽdcŋu2ĩ°yļ-ĄNšð4ÕӉmŨNö–1ðŽ?.Õkōlbl}įüŌ1ĪgĢ8Eâ-Ė]4=eo=Ép;5ÛUø‘áĨ­’åØA Á'f,―wîĀ&fvOmïŽģž]ŠČyf”ˆ AM–UĮ6ÎgđÁ‚ßÚ/fäå“ãÔ`§ļīqR=ā(šlķJïĈ°ę~ŧRåMķŊ+Âr,ËlTLM~…‘Ųšú7ĀŨÕY§hgÂ―øJáäęĘýž6ŨŨ3ÄÅ9šTK/冚ŧY1óUwĨô“ÐÎ ąh8"æ=ßYMttôÅã<îZؐ=ÏnOf{~óÃęócšƒĖ <Ļöt^Ðų‚"č°ĸbÖz^Ģh‚hþÓ\ūÓwĘí‹QņŠˆđ|ˆHTPPbŒŲdĢÓŋ§{š{zOŠŽŊ§ÖbÝYcžYČP[õúÕë·ĩäā ČÏÍŋŊЍ?xnô‡Á[f ĨÐHĪ”P”B°oįžÞšyöāū~õ2ƒÓ€i­ ÉyJ įóųl6;??*`ŨcŒ“˜Â‡‹ŨïîýĸfũéŅíãŲ Œo=ÉXErNŅåž$€Qda”Ģļ†'Ŋ\YÅģŠóÄĐ?kzŒY•ąv ú Wo7õä8Ëü|/äSðü?Ōäœ|hGӆļ(I Ą­ėĻtÁ ­sJÉÎXšū$šAŅÆæ-ĐŽG`b;ĸlïž:-]—‡’BûĐVŽÎZ-O%­LŠá…ēąKđsķ ⭂Ð.ĄVb°ŌļZ4ōBęspRĀ}KiąŧmX@§ 釅wŠĘFQ6•ĄģüåÁ[_ ZõXßG,Åôī|5SÅūïŠWrÄŅA"áCÄĨĄB9KŠäcîšN+réSMSÕīÖĮ˜úޒ=ä6ąóZ6óųGWŅ.â% jŅķÁíS#Ô+āCέUÆw‹ÏCŒQđĸïöÎã“ÎÕvCųæýáÕÝļ?„Į]\ē?ž$ †w› |œņžØ”óïkm/0ãY*“\ōûBp‘åM_x^ŅÍTSäá؁+úģ…ŧ5X Š1GŨ—‚wðÚžÃbސĩ‹—u€ČA—sÆĸēáõą/ω-$| /ũ UĀ ð7€ŋ"'ĐKlLKƒ(óDâgŲĢ<ŽšŽ“9Y0ÄĖ`~r0ZEōqŪlôâ”„eð)–ĜԝúÃ#6úÃķc‚  rĨc—h%­Œ5ĸ؟FŨBŽ+%/îėíόąāĻʋŧŨũž―?ģÆ*ĩT}Ču *dŧ6Ļá_=…a@€Ó>ŠY äĮēĒŽI0)!Ixēoˆf9‚Ū@ JR,ĸŽ’–†r]ac 3Š0šļb#Óh(€RtØúŽ,čģzÄ ŽĘ(§–:GÖT€N jīāÉĩĸn6ķ QŠO Õï’lŽĸŸLXäsÁð<ÏäðÓ4á‡âņxīÁ˜Þ8ʼnSL Ē7Ĩč.ŒĀõėrˆŦ)ģ‘@=Ó°ī{tN īĪE_ËÚĘlĖ•ÂĄĮĨØ!=>QNóą&šc{| Z>/ëņņëäŠýJ6HOó‘ĸï|›ž8æÛ_üzŧßOđĀ"Ÿ^äCÐðA="‚%|2ßâðiWø­Uō雕ĩæŲĻBrÆ{“pĻ?ĨĐú~ČÜ=ÜÝĸüNęl>*šJËgŋß[iã"|ïýToâãąĮgÂä3/ü}!öø`§ųäžĸžwx%Ÿ‹qŨëõf―ņÃ?ކÍĢm7[ŽøÕjĩÖÛív܎Ôã­GŲ;R(”ؒ,ŠĢUŒYÄ4SLāãŪ1 1Æ@<•ķd2ĒÅÐČHïˇ[đË;ĻŨ–đWÏ <^Ïo„õø˜ÅäcKåĢ")ó& |Ę݆Ąð áðÁú|ðþ•GbnÅq‘OFÉg7Ž4ÞÜ|7…ϰABYäcKøPïÎØsøŽ^āC ‹åƒÕÉõýĪ“O č­?‡ą­/|víûÉŌ–˜7ãļú"ŸŅũ&“Ÿ ŽÄTôáƒþJ>rŒ;žÞŨóAĖeüXũڕŠ|PNó1nųp<bø8Ã?ãƒŋøüdWOv}}ĸĢ“áØ7ē–}zīĖ!Ūeõíą­Ŋ"žųm%W=}ŦŽH{ėōōē·īË|°>ŸJŊōQü>ߛî>í(ģ-•wäƒ=ãƒŋ‰qø ž^ĨĮįŠÔüg|ˆ—†ĖÂûqøŧōÁ>’ö›―ŧ€ŊâXĸþĖėî‘ļ 4āîÜĩE‹•â$ļkqw—`Å)ŪEŠCˆ’Ï9‘#ŦóîɁ”[ÚÞĘíýsßηôÃîģ3ģģËáĮ°„ēĸĐr_(IŅûCïõwƒ4ŠĒ(ęoFĢöŊĢ(ŠĒQKQEĢ–Ē(ŠF-EQEĢ–Ē(ŠF-EQ…úGB˜Õéī˜åtz=ĮâßÛa†a0BĸĩÉēœFŊŨąøįgd9­ƒ―þyĢ( Ÿ1 a†ãØ?jČ~ Æ(+ņEðÚe'ožB Š{qsÛę5·BSŒ~{@ûŸ™›f4Ģŋ’qą,ËåbYĢ_n–g·ÎlXŧýYĒųã[ˆzóØō ÁÏße ŒĒhÔRƒČ•­Ģ+WnyäfĢeᏐ$$ø˜Áož-Ÿ2séé{„cĒo]œ2mjðĢļ_]%į}ôrÎ Sûæę’†æï{ĄÕsþ Ė鎹ũŧĩŪSąbyĸR%KĐĘL;ę į>mŦÅüöMš2áyRöĮdüôâÆ“–>Š0`–ŠĒQKý9Ųi ‘‘ĄĐ™Ų€NŦŨë8†aœ]\9įŪ|uŽ.*'G­†Uw‰)ųðâI‹ŽÞGŦ]YĩsN ö}8Ú2K’‘,#„"˒Ä* Që7ÖØ3 “~}ÚøĀCwÂ9―RdGŊŠMš6(SŌM‘[öjtÎ9õZ„ÞÏIŦSiY–ģMÖŲéįO'"ĒųMDx\"_ītđ2ĨK—ö-áĨÁ2„Y}Îå8;;h>Ļ$KĒdý)|uęt:Ėpē(`åýI5Z―‹ĘŲQûþtHĢÕéíÓprvÔqð)ŠĒQK!ŒcÄgÞ8ķy}ðøČ‡sFÓ―ûä# FjkZøîESztíÚ-pĘÁs!Ā’Ë7Í^ķsã֝vŋL–2ބėX<{PĩÅč]§ïš}Ÿ‡?=°íÚFKzēcņ,{ãÝgîYL1ŧWŊÚžïĀÞ=;öšl$ČÉģxĢĶĩĘr’Ä éÅÕā}šuíÚkÚĶŲbÔųZSŽėÜķ}ũÉįĄũĮęÕ{āč[aĐ,ĮĀŋ"ų”ü"øû gŋW_b·kÔþĒ@ø”°Õó'víÚĩĸāųWŸÅcŽCđī?&V2Žl™Ũ­[Ũ[ÄĶ™pNaäŒëGVwW{œ~*$YƒēŪÚūeûwĄ/n9pōîŽA@Q4jĐ_K\1ãúņõ“GõhŌŠÏÆ}ęŧãVLģ>‰h5vf§n‹Ö‡Dŋ}ú݁3oIX~}ót:éõ•MÛv=LĖ~žvÕâmûïeFÞŧķkėāá <Ņé9ø%Œß]ūbņÖjãŧWwūčÐSŅðúČõgzåÄþ—F—ōâdāļŲ{oÄë9áČĘqíû>ņô˚iCYõΊXŰiÓŌņcvęÔcũĄcvmœē`q‚ō€+DÎČJKWģ%`2".toÕiÖâMO#žŲŋôŦŽ―v]ŠD,›ÛžeųýSŋ6qþ‰3į7/[°áČcæˆqíĪ―†Íŋ~õåíýk‡öhs>ÂĻã,7ŊŸ0~XÛN}ķí=rëv,ÐĪý-Z 1€XŅU›O―|ųpåâ.Q‘'^'K’1ęÄó(ŦGĐásÖÞ~ēaF/,iúÍ]Úķ(”ë2õÆÅ“ÝËŧU5û‡›·.í;·qZGkFxø‡fĖaŸ’đvОnÝī5žÚÁjx~ýžķdÛ%Óz@)+˜•xidQ!ĀYboÍ^°Įä\vßõnßžŲŋy‰ˆāŲŒá,ČOHÍNƒŊ]>P……ˆ˜wé&é_OŠXŽÄ„ïęÝđKũžÝGOݜ͑+į\LŪÓsņƒ;OŽn§Íx}[vŊrônXč­ã=ęļ( ‹ž_?°ĸīQßųÂË[ĶķQâ#ķœÁ,Ģĩ=ī– 7ûúØå+ëÆÖ“™Āo (ĩ”,A‘Ž —ö-SްÞŲ‘óWŌ-BĶũïÔđŨð­7ĒY†ÅĀ1ößS#Īîî ëÓūmëŅˎ0ŋþecdˆŋ;aĻ­ņ˜ĮÕÆjâtā4ŽZ[a”eHŠ• tEŋJÅóę=}ÖĻŦx“Ą$ēėZ°Ä°ņSJɟ#€ĀĮQĀÕĩT‹f-›7iP§īždE‡ZY'ï†í›8qļ\Ĩ…Ažu+![@€ėš—žô6;Ãâ]Ļn•âyY§Â•*VP"&ƒŅh$^öeûéŧŊd$―NķEŧ"ą…ĮÝžRiŋĒų‘Bā7QZŠ€“#ĢH’$ĒØ+ ›7pÕîËĮwķ-ėvóÂĄĐsŋ―oҰ23yÎ‘ËŠ˜4uúĄËĄGϝ6Ļ…Z·Ĩï/Ķ-ĢAájãÃWÃū3oęĀfđ‰Ēä$§Ói>îŠ̘yIF à E4‰\î!ɂ,É ü2YŊ€aAãĮŽŨŊGKE!H–kbz6ËéÔņ đi†ˆĖj#ā8„w‰ÛųˆÚ€q­0dÜÔ%k‚/^ūēyb$Å6mFŠ ÉēĒĀŊ (ĩ!ð(“Ü=$Īŋ>pú!đq‹'ÓIŒģdó  AÔą‹GŊþø&&..!ÕÃģē·ŧÛŨ!RJrt’ŅBÉðžB€`ĖX2cRóxV)äĶóú™­qJtb†E§qÔÜđ|ņ‡›OŒžO[EqöōŦX…ÝđŋåÄų;ŨΞpMÁ^åýō(„ØW­ēmh")ðkˆ"ðVŦUāÕĸdäT―‰?oïíšÞÍĻQUũÎn;}#Ē|ÝNýë—yxlCß!#^€dQÂĒÎŦĘîë:ú*‹ŋųĒyĮ^į_*S6ėé^ÃG’A” yī°ÖŅMü“‡Ī„HĒ$ÉZ„Ûū(ā–Ģ—ÎŅ>þĮåm›5žô`ÎCũMęė‚lŧnw=Ctޕ&š/+qyÐā•—“;7+āĀãZļÆüÕŦ;ԃEãúķhÕšu—n›NŋNĢUQ ‚ĸc•öĢb"žß―û(:6)=-9üÅãŧ!aIĐé†ÔΧOÞø8>E-Į?ŋwįęĨsߟ9wûÞã„ƒŅn0RâĢn^đ|ņōaqĐ)ąá?\ūpéęíĻ· /o]9wþęë7ïRbŸÞ―ō*:͐žþāîݰ7ïԞIo_į4þ1úíŧЛjãkjcc†1.âų•KŊÝļĢž4ņíëŧũūŒŽWO”‘™ũęéÛ?5záÎãg†ĖLƒ!==5ņÉ㇇&ĶÚķÃžÜ―ō"1%-=í[9îņƒ{žDĪĪĸT633RÞ>žsýÜđs—Ūß‹KÉĖČ0ĪĨDū~qũî“8Û†ĖԄG7ۜ=áéë˜ø7/ïÞ}ö6>Y­ggc#^\đtþÜŲóŨnþø*&>ݐōøÞƒĮą‰)†ô4ŠĒŊ›Ą~YšÁ˜‘aīg‘AÝ6ėuĢQÝ6æ”ÓÕMĩ]njĨŦ=sŌ?lŦÕ~ßĖVUûŲÛŦ‰ĶmgĖmlČml?ŧ―‘ÚÕÞ>ũ]îų?Úĩž{(—mķšá“ēÁV·3äžÂ{r:ÚØ§˜[ϝŧ*C―œœūöąÔ3ŠĒQKQEĢ–Ē(ŠF-EQEĢ–Ē(ŠF-EQÅÂg‰ēŋ,›øŽP…H’,Š"ü,PŸeÎfdd„……3 @ðđ (ŠČēR͌ŋ““Ģ$IĸÛQKaŒÓŌ nnŪÞޅEÏEQ Þyc0\\œþXÕR!5jóäņ”e>EqŦæŽ}ôĸIÔRäø‡nZ8vÏUNŦÏŋãĖcĀ,ü&ޔ•žeÆKoVLu=FÐrþĪŅjl›9eëeNƒö/œōݍp”ýxúļ•ÏĢyûoTŦãƒiãV―xĢVhÔRĸua>9"A[ļĸˆáÍĘXæ ›ô4ËęUđDbX–SąïŨy˜ąíj4Üû/f8 §b0ķÅr9؟}–Õqė Y{Xąķ!Ւ:RÎČöĄTšCaÛPöą%•ĻŅļq ?”ð§ÏōÖžzAP—Jē$Û{چ}?ËXßî˜?ę\H‚ZĀž$Ÿŧ–ÃęđlW`ŋZ–ý0CôŅēYÉļBšÏ]―|ŲššÞz‰`MÎdXÛÆÁŽČËáœāemŨŪŅé͆ œ;āûKˑ{]*æý0°ķf9·ƒąÍؙäɧqĀĖŠÞwg>ÜAŒ}W=fŋo˜aļˇĄ‰ĒļŪ\ą€qĘę=É"“xįÔÆģ 5ęVÕ``ØÜ üáÖäÎÐ6€­bÃĐŧ+ũr>ĖßÞČþCĨði…Ļ•BPÜI"S­m§}€(ķĐjrÏþ~2ķöØöyyvņÄđ{u}ž&š”ðäD…0ïŊ\“;außÞïg'†ĩó~XÛ?ĩlЌõGA§š°eņÞËá4æĪģûfÖŽUsÄĖ}‘Õ áĖæ‘ÍÚu z•Ė3RęU—-ŸØýËo"­$üZp‡–ĩírþEǚ1`CįųÕļŅėUÏ^Û―}{õŽšWðadóåí [4ĻßžÍĶsĄĀj3ÃoŽØ°;ÞĒaģ"f.Ýô8‘7――ģlųĒ9Óúõ­™Ó`ˆáĖņgoРېaé‚VŦœYþíĒđsĮ5ĻS­EŸý7ã5:bŊŒĩUúîŋ‡@Eí͐„į^ŋ50óęæ‘>M7hØpöŽ“<‘w-Û0  aĢþ§Ĩi2ŸÎ\ū{ĸæđí,O6ÆD<7Z%L„+Á Ú4iX? `ËŲGĀq–„'KĮL]·tPӆ5ŧÏ>ĐØs1(ëĀ·#ũßIÔéČéÕ3'mšĖhu‘?O\wÁŸ’mF9ĄetüđĢëjÕĐÓiÐ˧ĐV5ĮÃŪŽiÓŽaãķ+Î>% ‡hÔRĖąÆðggÎZ4m‹Rļ‰Ÿ—!„EĘÕ= ^2lŲŋĢLüÉËÏöo°ãČņą-cVÎÂĘíā5ũ%N?ūŠ—N’dÄđæ‘ođzÓjŒŲyðŧŦ.dJĶ#ÛÏdfËzW‡ØÛ—Nžt]0ģßýÝÛŊ=M =·yjpæÄ­{ۖH\ūņ ąnØō„í2&Č%ö‡Av· \5·cŲo§Žx‘Ķؗz’(äóo9ĻMÍq―ŧ­ó1 ‘–Č!ßoš㛙›ŨOn·aðˆāąrVüĨ'OŌ%ŒÅĪŧŨî§g1%î_ŋæŒRcÐĀŪY燒,ãúýŲŋ‘ž8xøĘC„ļø'Ũ‚Đ3wéāRüŒž“ãåݓŦjĨŪZ)iQ+o„ũhX n^yŸšvmĀÐÉN-nÞēđkĢęēÅä_­Íū̇{”ŋŋ|öiŅ―x§Š…K–m?+°W.ûÖĩ“Ņ&6æÚÆŊü0lŲö“ŋZ=sėŅ{)”yvۆ2ĘO žýýú1ŽÃd Ýšĸš"ÆlX°üāö=)ēt=xUzŸĖ§įÏ>ųõP"Ā:@øÍóWŽ‹—-q{{|äŌ#ēõÍč‘ÓšLÜ1ĸË=óæÝxÆ|Ūk[Ļĸ} ƑQ/Î]ÆŪĨ[ė\Ü·Pæ H–2CŪO‹ô 5!þ%įqŌK|ܜĀք8AzkđÆ―ÛŸüvÉðīၓ'įO {ųãgsāC}hļķ~ŠQM0PdâQĶzû*ûWýŠŨˆ žū’Ir,YqÐĖEõ ČŲë―é|7+9kËŽHH3fŧĶŋ‹đ.Xū^8ĐAŲ‚Ē#AÎÓSVëVƧXEKąRe+ëÖ}wĸ%ʝI‰I{0iTȋðØBfA@j/—üEóįóņö+ëëíšåĖ-ƒïĩ+ļhĘöĐŋýȅ§›b„kNŧÝ~P·jþū5'Ürk=ÉLųÁlˆ:4úŅóˆļ"&‰HYRþšsfLk]+/oE]˞7Ė đr(Q­ÝŦ77Ū~“õV·c=ãV§Óž…ãj0O ä?˜‘™žôîŅÚ @ÃÛg^ ëZ,<ä/;sōäŪåݞŧqpÝėÓĐoBR‹Ŧ· y8 /ęúMŋĸŒŧ—iüýė‰ó/G9jAVxIPŒ‰VGĮŽœâŽqkÝoÜė…ŦÝR­€Fmņ.Ņ ‹@!‹’d%ÁĒx”oØÁÝŌ―Ũč'i­ʃ$åþƒ"‚˜"Tx8é‰Ēd%„ÅsĒč[ĩíčIÓũŸ=;ķK-$‹ðĄ›Ž(Š$ƒJÏš*&ÅŲïâLŽŪˆØæ@p’!ɐïbĒēģĖģžũ­ÞnLÐôgÏvŪē4Î:ŒY&öEÉŦJÛJ–K#g/-ÕbņĻ^ėaģ4ÞĨj”v‘ŲÍQĮ)jA‘1ƒFâ úÕ4vōęmOčæĪX8k‰íäęa·ū_õ5ãö[ß/­Z@'ÉJî?ÁŠ+Pޜ§gŲbîēZDH –”ä$‹íXķ)ÓŽaĩDQD‚°2’ĢŌ…$FËnZ$ųÃ8XˉfN\í8tøĻÞy5„@2žš–,HEž\u!ž KŠZ)ęåĶNāBNï ˜“R2Mˆa…ßÚ5uíá#†wŠYW@ģy9‰08§ÁîyófÅ&H [Óē39― œeŧ ‰DųhMG”%k–ŌÝí1"ļmïņÝŠxÎÐ'ŦVËr.XV€ĀϐŸïéEÞ*@lL‚Ā ―“æž›w8uöžKŨŨ7/—W–ešŠĨþ.ŠČ3Ģaą,ZeĖEē˜L2rčŲoëØß_ņ+î"éóUŦï+JÆļčÐōõ—Đawn=%/%:ŧ[$Ö]ŊsĘ_ŠeÏÖówũÏŊp&S…fjø{$3 J―|ā^~pÔ88kuˆaŒ1‘'O}WÓqŨŅŨ-&/ZŌĢoQŊ3›ũæí_‡·>dŧũYŋöĖņÃQL73Ûm\oÂ[DæE$‰ķ)ƒg]–u]R{ĖøRųYŲ*ŲSãâåį›įôÁ-eÜō"BķÉŽĻuBēģ,ؚ<íqläŽóĨóvČ,P―uóĘ%Č`C,fApˁNŽ^=c‘cqëó3&—9MKË įL&AŌļ·Ŧ]­ĸæU^æįŪœ y­ōlŲ1pņéóĨōúč3 ÕîÔžcÎA†\DVœüš4pŲķ3ĩ|“råâ[ĪOœ]Ŧb+/b%1;Û$*€°b6ņŲ Ũū^—BŦ&=~­~­ĐY%/ޔÍŦ-ŠŨiÝŠüŪĢGsM+$™œūčÜēX=!đk=)++˔sdĪ­[ŊÝę‘kĶ/ÐûZï―Í,?žF%'!+úÉ―uKæXÂN„ÄeX ēĻÞų§”$€0ƒHjRėýû·#Œi7ÎŋíRÍSË-\―"ÄGÚš·ÖWåóéîģœúĮ‡žuB­ôޘ―e5›E[~If“hQ|[·ïäŧmŌØą―šø[JÖv7‰†·aOŪž―‘”,†DĪŦPU\·jãĶkÝę;ðõÎĒZízÂũKf,Óļ§=8šRxM‹ œõ2Ÿm›QŽælIɝ%qð,ZđR‘uŨ]*—ŠČ™ËĮDíØī* ņf3z? "ÉĀl–%…@.ÆÉúúėŠÕ.15Klþîm‹‘‹û·h_ââwĮ hY“…†Ý;—-čBĄŦÚĸžļ‹C›QkŽŪ ,ႝŠŨ ØÛۑ("î0qՎwÝkæeŅ*“V5}Qvj‰AÛ tb•å–5ýPVj‰Žje€^KwÓđū$’ŅQߟq-ķpÃáĄ-ËY­Š·wūōŋY9',äË5ëįŒë eeĐÝkųĖņ$Ý é õ;§J~œ·B‡Ýëš‹éēGĨ―;W·(Šg<+Œ›3ՋÁąPïĀđUžXQ" "DBšf_-Ū|`ĪÜ2Ï †ØÛ6ŋ™úūbýYûĐ­|îđԁr&(ŠÍY-ËęaIąėîÝūaĸUfųýđ„ũ“ėŨe+ýtEÖÜ+R$uĀ\֏įœ{aŠ,Ųįö~žžðūN”ÜåŸrî€"Š~iJÖ3ykî%Ŧ›öû&‰žýÔđįķŠYõĄąŠĸŲ-úķ–ŌGUéãYŲðä#o?ŧb;/~8Åûîæé_ĩž―ũ’CM|BL3ūj5gߍ[AķFݐ ‘sNĮtđŸJΝ“rï§m,ųÃ4D!į,ŠðņŠ•\ŪÝjŸ^îåüÍDQ }ųâEhVVfځÔĸúĖhĩÚČČh―^Wīh‘ÏïŲRDcXhœwŲrnQȟ#þÅãg\ĸ’^N !@ýį!†‘Ģ^žDž>…óđ!ŋTĄþ–eÃÃ#!… { ‚ø{€@!ôû‹ðöïÁũŠÕ*đē:gUj_MđŠ5Jøûr–"ēŒ‹•­PÔžŠŸV(úŽ–úƒï@­žHāߨa~)GQbC<OÁHzqïÞëknģO#Y0ÆÜđũÄú—_ü!Ŧ'JąÝcōËŠF-õûqZmėõýã—íÏĸ†9+Ãhâ?}[Fü…Sŋ]ĸRÚwŪnä8ü+IËdD_45h0ŠĒþQKÉĒäYĒjûÆÕī A Ê8ŠÅȞŒš,ƒĪ\ß=eîšïÕtÆ―o§á°šĢ6tr q- …0u°œãþðj%ĩË2:―3B%0 (ęĩf°Ųø.*6žÕ’S{—-_9oD@í€Î;o―Ucō]čđnštœ<\Iŧ7méîmË'vŋ95=aų” ķėt+<“e1bOĒ€bûNūuheįuzOžobVxũxæ€: t^ļî‚Līþ1(Šþ c&+6äÔ=ÜũŦ֙oŸmXuzČŠUĢ<mÜēG­Ŋƒ·ŪĀeÛoÖ $;;ķ-ã}ųbĘāķŲRŦi>k5[ĩŽ~Ãí-ü1šôGÁƒŸYžĮxhTāÔ]7ö \ÚØÝ_oÞÖčäī@Þėĸ,EWĩB.3ŲßjÐī /[UŊîïę$ņČ1 qëgßï?sEī XĮ%+UpÉSļž_AWwįwŅũƒÆ|ûc”‘â$`ÐĮIËBČåC†ļļuÓw\Ę6†%ĨeÅ_ĩ膎üēī_Ų3:8’ûÞ|ŠĒþ!QK°‘iĩHeICAbŦ·|lÏš’ō“‰ģįĶHYĘ”TāæŅĨKÝüzäðFĨ+fX@ÆPB°ģGGϊĮ›ŋbÅņcóðiəB$&:R’‹Áh‘ PõˆZúģ•'ęoŽ‚―b犜œyý܁ģw_ÉZG­ÎÃØ·ŒĘÓĢũÝËĘ2šSbC_ ŋýöų݈„LYģU ž9ÛbĘ·ęW,_ÜwŪū~yïÆũĖ.ޭ˝6sú·‹ Ÿĩ79`―>ŧĒFĀ€'FĪÁ@QÔĸïÏjé;ŠķôFŽ$WmÓŨō{Å9h5yōā_=ó*ßy]‡ÎDT_–0ø…%;ö˜ļÐņTX ūũŦ;ŊĖY|@Ũņ%Ä"ĒėðÅĻyļĪ#S þá]ëŽŋ—R°hIBœG.[ëđĸhŠŲ}ųúIÉĒģ,j+·Z4ŧL!Ą [Šúĸ>m|ð(ém\ķ$Éü/ ƒ| 9UДŊĪŊ;Zęũây^ŦÕVŪ\YQøPô—󘘋Åōņō•aPttƙKoœ xVię­Õ0„ÃįKą/fāE“~öę[Ģ’~îēLhÔRŋ‹N§óņņ!„üM‹ÛČđĸúâ…Ū`YŦŨÖōqđ°`ø•Ę37öŦ ŋ[fbtl–Ķī_!  åø$ķ^"ž €ýøŊ- \ÆÛûĐN•:”:·,(ØĄôøo:3ˆ@!Y1˜eQ$YV–%ēĪ(ģDdbX ŠŽËqķeQma†Ãåô’üąĻU) ˆ2Ļ“ĮDĄ_WKýIÖôÐā0ĻUÚŲjɌJ~'ĪH‚ ƒĖ›’“S­ØņfCbbĒ1Û D‰9q`āŠCo’Óx‰(Ē59)11)— `WÖĮMƒÕ ÖĮUÃ(Ÿšœ””’f’ˆšũüÖŅ™s—EĨdŠ{5:öïÓķ&›ŒôÔÄÄä,ģ*" ‚H@1$'§gšėÓL‰į[t6cîŌ5KįDüxrėČÝꁆCflæ’ĀKēd6͚yÉÖÚdLLJē*`GDkJ’:|2/ŠOŨ.īæh\Jš(ÛŊËĻ^—Á$€ęÃ8™éiYYYŲž 9QīšEøK(DdÂÛ2 Žō/}A Ø1ŋđ€nå|Ëd[sZÄņƒ[.X:gęϧIbRčĐ-AMįiüÝą3fÉxrÉŌŸ&Č,ÜÝ3âØũWxÆ!öÖķSWï§'<Ü2ý‹ųÃÚî;ýHd4VSâųMcækūûðÉL Ŧ'úĢßx x‰ČðûŅU-õņŠ#öÎiĶzį2:ȆcY†ÅņŨ7=įŦ$|ëz™Î7ÍŠ„›0eSDĶĨBį +Ô;{ëDȑÔÉqO†ŪÚėþd˰•ßaŦŲ/`ÂĶoŧ!2›œ KÂŪ|7vöFcfvÞ#ũÍl}bíŠ !ÖØū…ŋûnŌÛkßÝC5ĮömôüŌķoflÓ1œÎĩÞę“Jé2Ö-[—åzíLœÆ}ÉŪc-―Ė8åņðö*TLývrýģŽ3v?N"žÛzÓwhŲBåŊŪķ;ĢĒúÍÜ\ß5rŌ”UIIe[œ7ĄŊģéå Đ߇$éœôÓWŊ=uũړG7&Æ=šēysþČsĶ­y#™dWŋ‰–īŦ›įĘš!{"ŲÄW‰Mę=[ðâÆÉnŒpxÞäŨų{LV Ļ?üû˜$)D”üʒM–AÆ8=äâ‰åŨŪžÖwęŅT‘Þ†ZnÐuę4jDÓk,+Û{^€7xí#^ZØríîŋē>ĨŪ=üZã\ŊU―;ĮÎ0M|Ÿß™Ž-ûÅāH^°>Ø>ųvrĨ~úœ:üŒgŲõŠ‹’„Ē€ ƒ-i ZęOþæN<}čjŧ}1yĪŽøŽo°qÃŲĀÎ―{Îy>n(:ŧāZxĩówVâFĮ5Ŧp™Ņ,[4:ŋ‹6ƒiwáę(KČŌm6ūœÞÍ !øˆĪ@þrMï‘}oũðiûí}ū2š{æ[ ^Ņ*z‡ãõ­'7Ĩ—ë4‰sK~vã‘ÁÉxjÓ―Øø·ŽaQIÕJʂ˜ûE2`ÛĘÝøe €,!“•8™>ŦĨþÔOÓËsąåø‚Č`*ŲfčČNĩ ûyQ^āĄfŨÅ-Ü­yËۃgŒëÛXïĐwðÔ{ļ9h@‰‹ļ1yÐX“žYþä4DN ;?}Åþ7`WgŽÅ.ŽZÆÝÕņÃScČHˆŽ4y—)[k eˀĩ›Ã ŲĪž%†YĒ€K ïb ēLþuÚĶÔč†ņ-ālÐįÔÖ·ė>°uŲ‚rúãwĄo^Ëßķŧ$EąĩģBžĨUčÚÛÛM9ój'O'=“šĄó_ÞŊ.ƒØęÍÛ ­3 2 Öąe·u}óļõnZü…ï| =‘\z7)ęÔï†rü,ŧ‘x…p1á_,[Ý**š MjôéYØ1úÐþåk"Z7@([Tį|RfF–Ž–dš"}ĐvîiO;UëšÉ7vÂņ• Öíã”ŨÓ!oũŽcËŋ>·äėŽéŪS!―N_še™†ÕĘvžĒs+’mąV‰Õé0RdŦ 8Ç ŋD! dÛ%ÐU-õg$üxQĐ_ēh'ø2ČzDŅLlŧČ―ÖęŊĢŪÎoŅeRãNũ<”™*@ÖËA3ošŲFžÐmŦ?Á —{kÆÐ)—Ümåv}Čė=<AČīˆl!Hãę‘O“šmäÁMûüņ+WŊĘÎZĀQ‹$ɜ=be™•9=rížqŪ%ŋŪZđ*‘ũ)  •%ۘŒCÞbų }īqLN™_Ž9ûðŠŠčAąð@@LˆL4œģŦ)ėú“„zÍ}ÃïJVwĮœņĩē6lËý–ôŌ=•™sb*…ČäŽÅ,F˜ôIÚ2zėĀŽÜŨD――tĮĢpŊüΌlåu,ļh$Įę]ō]{óĀÚR^æ;ũ}ûīĖë^ÂŊ4óü>ņĐ^ÉÓĢ‘åā:ßŌ­žÝ…7wNūIÎŅEŦE.Î>ĩŧw8~üxē_G’mõvó4žÚđčŧV‹•Ë›|bvgݗ‡[ÕdÍîĪûróÚūĒĻĀÏYF’Ž -"ôY-õ§ X/ÜHlRĒ•‡TD‘^y^ą—° °ô$xô‚óF=SķYób:ЗŦ™~}PïÂŌy#ëaŨMpÕčēĢÖÎû~D-ÅœČ“%^ôîÅ˔)šqÞ  ūc“bfmŧēŪiy&{mũ/§mÛ7“•E"ʅ+7vqzŸVÛ :Å%fN[3CÏfʒVĘY$#IDDÄa&uÍÔŨķåÍĘN(XöëAó/ĘķĐĘ"/HķöŽkĐŦĮΚŲíÎ>Žhę˜6hÅÂĮCF7k}+/'4―ąg@‹„ysú Í^ģrö·ã†ŒœŲåŲ.ŸČČÔĄc—zģ`ĩZíãĻōWoŅēÔüŪMūŽå Ed―–8ëa€E>y€ąxÓíō†Y%đZßyū5ŧBjËÞý Ō9qxUé=e݃Ŧß Mëņëʖ)„TîĩÉ- Ģt9öüzVyŸZŜȗÏ+)úÉS­gĮ΅yÔzZŊ‚į^― Sƒ›‡6g­öCXoė wŊÖy*įëĐŨǜžŽYôéÛ!Ē„ˆ–Ðgĩԟy€ "ŸkĩŊ„ÁÆÕ·ÕĐCUŠđÛhėîŠķôå\K/ ޝĮ+mG‰ĩ‚Ū|ĩęžZ€"G.ėKóyœūöøgq…|<ĶZ’xŨþՖWĩIáÆ–ŨĀ.ŪųĮŽÝS'$ÜĨ65VĖ›ŊdÕ;ũ…ūąļąLĀŨSŠ!gÄšīē"Ų[)ŲRĄĨ+ų{ƒb0gcWhÜj-ÆīŽcVíŸÆ‹ēÆŲģBå Î ĻZMÚYÏÁû㠕Ģa[―ë<L`4ŪEËpËįŧ~ûáGÏ_KŒĢĨ<î_ļT2!CqeŲBýÆíŪXãe\ķģw𹕋@›Éŧ:ú@RŠĀ|5Ļe Pý‹―€ČpÐ" F˜ąĸĪ…čTūe‘ŠmÕ:Qd‘·($_ífeAP š+ŨĄde… Ī*pņoPļŽ&Č_žag‰7Ë )Vą‰_Õ„ EēH’ €ýëu.ÓBDīˆ2nTī’Ä›dES­cO"Ļø°Ą OūØË6ŽC$ ĀņY-JKKę3ĢÕj##Ģõz]ŅĒEdYþíŋƓ’’Ų AÓŧ§§Î^mðÕč‚: >•úātŋ‰įĶė\\ĮĮĻ?"44cėëë›ûšNŧkį™(ð(ZąCc„#øĖ)ˆ2QdĀq/_įˎ<ĻÕ*ĀïÆēlxx!ĪpaoAĸqŦZščPE_ Bũo*EÔ§KŨŲzĻū§›îŊß šĪ•$ĐB…bQ—Cēãž}‹hõ cøĖ) Č@^yõFŒ]Đ~9éĸąwũ~úüáøļëâ2Æ)ú.GŽH)vî—ïÞ<~þâ‘ÔrՅ͛››‡‡‡:ˍ*ŠĒišøÃt:ŲûōõŦ'?/."ĮĪ˜N§ģõĩņd|―đ‘Zc· rŽQQ”eƒ”/ áā ĀmåĘGĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jG_‘~  OS™s^‘Ô’sŪŠj>Ÿ·mýEQUgeygRKŨuÛÛũNNūœGŸmÛíî>ėš<øÔRŨõÖÖ݃ƒýœĢW€”ĒiÚšŪãZæ7 €˜ ĩR €ÔH-ĀĘĮĸ ĨH1H9Gîĸ€.įĶĶ―ĖÖ°ŒF1)R‘"%ЅU”s^Eu›įßŧŊįÍâGŨæ–õq<ØïlåZúEjaEĪ”–ËåééébąĻëšĸ_;›ÍvþcįÜÃĒŠú=ūÖūĖ Ã0Ã0Üfžkˆ—LĄ)ĖšûR ÔýlԐ›ķøČZ oņíþâ’ĪY]ý5­ŪĨ/9ûúŽÏžÛŌ$ķĨ—$­ĖqIZvïžQ ņ3kzÅ˜ļöäúBWu” Ąk@!lĀ2 FČYvS݄72M'(JdÕĘČt@hĄâQŪōú‰ÝU†ÛWDë$ ŽC„)―{íDöúŒËĨ‚ąũÐĨ)ËûĻ1Ą€!N+…uí>dXpŊžaœgĐúåí·Þ,ŦUyGeåĶöëėUuû\ʂÔ+uķN=ûoy CayT—·zÉFŊ‘‰‰ĢŸ’$ü§U ĻËûΊÜvÝīüöK1zÚÚå“ jŒ<‚GžŌ™`tįŌ‘ÍëŨæUJA#–§$ũ7Á=ïŽÉg˜ú›GOUõ\‘ĩi\xcîá/•Ÿžq騒ĖĪ+GŋqtÆæïM?\ŲT}zðXQoe­˜Ĩ­ŧļtՙ„uóÂ5Âg.Ļų·•o 8uāýÍYŸ•ëÃĶĶ`ÂwŠÅM‘ąŽZ™Ž iĒĨ›VŦUčĪ•v—& Ë;ŠÎ]ÁÅ$Ø2IŒgė6 u`ČB|wӜõGmí- mđŦmSČ@(!Ä­j)Ff‹=Ē_D‘P !^aŋóvBŠũčĪÏrg-š2cņĮ?ėšV•~ÏĸĒ”mÛšmŋóŦïĮ­Ũ§kÍ3/%%Ž7Đ­įΜņŽÂÍŸŋŧņđ7’ßۜļ`îšó“§Ä1Ug/_žŠ!ļ$?ŊĪēĸōÕėÔéûö{ŸM™ģ,{čŨë|Yw{ƒ€ŌT•UÛ‘‘ŧZQAî`X, IHpƒXNúdm:?Ęŋ ÔfLûËŋûüfOzųðĖ3uķoˆ!戈1Ņ‚ęYvðČ}}!éųĀC{sŊäŨV”Y‡‹ĩ5Ĩ5įō=7žÞËĻąˆ>€ ”mÜ·lš&lôî] čpĪ•!ôČ V”΁Œģ‘ØėΒE ˜B^Ŗ_;W.'ūëãc˜3iäņ„ĸ)2OlÄ8~ŅęIÞ>vŌð=DĖûvVû(CPļE”$QkŅolH›ãîôÕhm6ŦĻ Ģ"X%‰uôówŸ?ŽŽx/iīJ–ĘÐûAįA0uÓÕ ÅSđŦ•‘鐊mĨŦĨk|ę+ËœĘ[6f€JÅV]đ`öŠėîĮÛŦŠęØūũ*ËĖžBÁL óãcÜwÄ𓇔ÚģóëïüXoL[5mŨ’BáĐæ‹ËĘĨČÞHī(Éãđq“ÎîüûŪ!ĐģÆ+ˆĸt€šKrŋb™P“ņtÎɓĸųę˜>,A6söôUXŠî=äzøVW”YT % ,mĒ(H˜ã(!X’„š+† ƒ Ĩ€'"äü!ē;`#”Rėœeĩ_Đĩ Úë—.›ĢĐ>0„ŨÖÏL^áĢcX…Ü§#ōg~=Æ™v/bâŨíųÜQŧRĖ[đ~K֖cŨËŊИ"_Š3|ļ%mcæęeđzMųk˜†#īŲT„#ĸØšxþė/‹-cbžÖóôAAÞÁ/vÝŠüéäé{LŨ„„AŧŨ'%§ŊÏØœsŧFDvŧ6rŌĶ”ųŨķÏYýÁō,üW$~ÐËSgF<˜5uVÚÆėė‹Roģ―GÅér6ŽÚ”đ*yÛåČiņ!j(Ųl.)Bˆŧ]DAcŊî7v~ó鍘Ą›Kų„ĩ™EL]#į|LĻŌĒąf§ŊL]ļæ§š*"ŠáÏÆGõ?ÎÚqøČûöo°u9ĘY­ŒLþ hŠFÎÎ ísô܍"ģ L^š1øđ‘z,HÞÔûØņâęÆĐəqŅ‘,ÁB@‰ęßŲ\\Z- šî•CE‡áÔĪZąĖŸ+Ģædëš)ŽŽSBýžŊū6ÅU!ÅĒ(b{=;`I”ąOt0@‚„€J?vü”g―ūą/`ÁN4Ý_0‡ ĻøžóŠ3s@,Á˜B–"AÄï2vúL@(„Äa(nėÕlÛ? t.##ó#ÂŋĢĢĢ Bč ϔ!„<ϗ6á<īáw†”5ā3·kíC{ú9Ãdul^ú·W(xŽīuqEį Íų C{úėĪT+ ĄrW+#ÓþqšK§ÓåįįwéŌEĢŅ<áAÆļēēēĻĻ(44ÔÍË1 īJ&DÏßŊ­ŋRôā~ĢVĐä(íčC‹PR]gôRzą(•™ËēNÉž?^’Ī'åۖƒOOO“ÉôõjîMÕ Ð5ĀĢŅĶŧUmĐ,ŽuéŦ]ÁBbÔrýB―ý―X–Tdd:cI’(ĨíâoĮq À@˜ÖZpeĢXoÃö…ŠgŒ:.PËĸģ―;ķa„Â0HÞĖėÂFô á7ԉ‰â ,…Â֝äūæYü—ž—Ú;ÁŒ8fzésĖ[ÞÄ^kÎbfįųÍtŅūi<АZĪ@jˆmÛ""-@ęŲZëCmVtvŒQkM9įRJk­ũūĸ ―ũÖZ)%įüiž ö,ßXIENDŪB`‚doc/images/ifw-tutorial-files.png000066400000000000000000000062331325366651500173440ustar00rootroot00000000000000‰PNG  IHDRą_đÖPLTEJ29K?CIFPIN[KSiQSpYPl_JadGXqLQ€TPˆXP_Q‘hS‘l[‰nc}monq€erbu•b| dƒŠg‹ēm’đv™š~œĩŠžĻ“žœ™œœœœ  ĻĪ § ›§–Ĩ’ĶŽyЍoēŽjŧ–eÂĄ\ÆĐWČ­PĖģPзUÓšVÔ―[ŨŋZÚÁYÛÃ_ÜÅeÜĮkÝĮqÝËxáÏzäԁäԆâ҈áʍÝđÕū“Éš›ÂšĻģđžŊﯰđČŊšÉŪšÉŽšÉĨŧ˚žÏ•ūԑĀܒÅä‘Įč˜ÍęŸÓęĨŨîŠØîģŲíŧÛėžØã―ÖŲÂÕÎĮŨÅÎŨÁØÖđÞÖēßÖŦâØĪåŲæŲ™éܖėߕîá”ïâ–ðä™ōįôęŸõė öíĒöîĪũîĻöíŦøîŊøðēøņīøņ·øóŧũņūóðÁïíÃîėÅėęÆėįĮėäÉëâÉæāĖäßÍâÝĖßÝÏÝÜÓÚÚØŲÛÛŨÜÞÔÝãÐßįŅÞįÔāčÚâčßäįåææææįįįįįįįææįåææåååæææææåæææææææææįččęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęëëëííííííííííííėėėëėėėėėíîîîîîîïïïïïïïîïïïîïðíðņðņņōņņōņņōōņōōōōōōōōōōōņōņņōņðōņîóðæóðáôðÞõðŲõïÕõðŌũņÓųôÕúõÕúöÕûųÖûûÚûúßúųåúøčųũëũöîõõōôõóôõôôõõôõõõööõöööũũõööõööõööõööõööõööõööõööũũũøøũøøũøøũøøũųųøųųųúúúûûûûûûûûûûûûûûûúüûųüüũüüðúüėųüčøüâöüßôüæöüðųüøûüûüûüüúüûøüûũüûöûúõûûöûûũüûûüüûüüûüüûüüüüüüüüüüüüüüü;|Á pHYsÄÄ•+ AIDATxÚíš_LZYĮ/ pTŠNĨōŊVEž\[ÄN7*š`QЊōGhýŌZĸT'jhgēéÆÎƒ}ņ͘8ŧóÐØdÍ$[ŠZ[!úЗ>ī„dgŨ.ÕīՙdÂËÞ{iwĶ;NģKQïMü搛_Î>^ũ|ïđ_ įĖ™3ŸF,pôÉÔMâhvf8:;óäiĒZęr:ŧu0ütŸ„OÍ?JX]CíöxlþGû$Œøöã„Õå^ŲV–ŧÜօĮûĪ8ņzÂršÝkŠ2G'ž€•ß~ģþÞŅRŠ;ĪŨ“Ē8ņ|Ârļ{r•Í bĨ™nϗ†ĸčëį“Ąo0âŊo'ŽŽžŪ‹…rĩ‚°Rdœ;öųā1ĀohIãÖÞæekhsŦ.‚õ·“Ąų8ņ­„eïrvČ!Ļ­ ++ČÕfšŅLYÍL(Ĩ‰Å71uÃZ1ŊŽŋ• ÝÆˆg—Õaƒ KKĖeX‰Ė īŅL ―9Å8ÃŪfÕ\fÖhHÕÖÏ$C·0âŲé„eąÁЉ‚’mV"ļ­āYMŠ Ô·’ôCŒ*VM?3‹“UA3†édh#žN| ‚aH…įT`år2Ų™ÚĀĒTÉ@ðJ\-Č\äoHޚ75}=a•—)(qIŲõýŅTœx"aœRækĖæÖ‘‰ýŅM”xzęOņÃ##cû'ŒxýžÛ4J[Ÿ&Œ°kEėãyýúGTŊ#ą=V$9đ ó‚ ķŲlÖ~ĒßŋðāžĨÝãqY BŧÁ―bUYÝݖ]ŧßĖ?þö9þˆ-JØŲŅū[ï튜Z/âå .OŧēÃðÚ.―ŦŽ:Ü͊egWOyĄJ­VõĄåO6)ó+‰lXaĨ‚ €\-6 pČÜę-žßëpZ• ĨōĘ Z9‚XŽæmĢ™][J#Ŧn•Ĩ“f~É⇨f‡KO””–ĄåS‹äGRMāŲ0N,ŪIņCž[Ôōˆ…ŦÁˆŨ$™_Ŋ·Ņ^ĸÏÄԌ&~ˆ—\xĒĻĪ8g(þ[ãsųdjtE]ázEgĒģCĐÚüœã2eiJŽZúÐ(fõũx!ÞYhUj–ü~ßģßē­!‘xQÜ\ÝöO‡ÄÄ!Þ ,//ß[\\ÅmÎũÚՎN§Ģ8þØī–ŧ=. ąüąĨįünnóÍðÕ-<?h“ÃNûnÄÛēĖMüđÍļ?ķĐÕ hųęoœûîÉôÍõØģĐÉ;ēˇģ|“s‘73SYÜ™œûōÚ:Xbg—*TŠĄrĖmØ4RŠą™MĘxXRtēLĩAJJņšA I0ÄĄÐš˜”Ėč#þØĶ((UžõĮv†Ģ[ƒÔs`=2+ČČōŽá:IÖ]ķ  ‘rČĩ폭6Ô—”6ʼnu!éqf·…EˆôĮ.Z%ģF\”ōĐMĢ“ĢlþÁžãE5 !Þ­(ë-15Ö áIĻ^_xU–)ÉÐP>!Õ§% Ė`ķ°I›Įŧv°þ‚âþļ">+8žœŠGWōē?}đ”—clnô ―9BŊ_˜ĮČ k?É1 sĸy Ä~HQ\pĒXĄ(ũĮ‰uŧú)üe3Xów|8ĄÅķ“#áwÕZ^ý’ĩqč݉ņNžÜ‹Čír]~FâH§§ŨŧzÝÖQÎq§{%Ī„{ݖ5âŧƒŠ2g—úĸ ä}`―æũ–ØévݗŦėvČĸŋŋg…aøíÎ_näŪr É'vļš.*`5wöŊ†…ÂĻ_˜ÛøhÄ$üýˆðôËØvģ7ķÔčÓæ6ĩ•đÆØĒP‚ÚÜ3›ÃÚÓ/bþJ­ðęšķēaPˆtú…BFíHÃӑ†įW„B-q5éÄoà íPÜnĪōrŊÉxbZƒ”"đ"°æûm™ v\PÁĨ7Јø9‡ÄV‹šÝNŪ@ËûŽš &ĩZƒŸĪ~ÆĀņå·áyFüú‰ę+H)éŅßQM X?bHĐ@à % /ā‰84Ð./Ð õ ,>%ŠŠė„ÃOö$ÃyčÝKüchmm y…vˆâ6ïŸïķĐ;ö~Ē/wz áÞ+Qfę6-*ŦËI,Œ„{í–_ũ*Ģ8$~ëmjØũŦūīČ{Eėėꆐp/]Žß€p> ūlf’xl€jd“Ļ/qæÝ°ð‚B ÅÝæ*+c€$ļ$)bš9Į­â„ įžųc\ŒļMå;â:Žé8<áé~Ðx‘‘ÃĀq/Čģ~I\+Ģ6ũšST- gėÕņðBQF.ኌ8›*î-..[ Čõ8:Đjņ…B;‚šÍũ‰ĸ }Īɰá†xtIENDŪB`‚doc/images/ifw-tutorial-introduction-page.png000066400000000000000000000521351325366651500216770ustar00rootroot00000000000000‰PNG  IHDRÎ&ëņp_T$IDATxėÖQ ‚1FŅob“Œ=ÂJĸômu a+h &œSâÞü(aJĩ'9ēÖ=IÏ6(mÔ0åsŧ^ēŌóõ67›9å€p€p€pįĮ—ó’Ŧ:óýï†Î=ӓ4Ģ‘†‘„2J !°Áƒ `Āäd Ö[^ۋkĐÚũ^―ÚįÚ}ö:Ŋ―^ōÆXˆ` ™Œ˜$Y`„ĨĨ šÔáæ{>Ĩ[ÕuŠoĩģŪýŠšnßŊOüßïœ/Ûsū#ššščîîĶąą•„hšV“_ŸˆęŠmĐ<ĩ݈Ûf†ōzĻõ]ķÞi·ĘĮAÏßø‚øĪá•ûKâŊ^u\J―Ošü.Þq8Vó ĸúϓčũĸúí֓•˜ú?þIŸdüĢûŋ:üĮÆÆčééahhģĄĄA*ÍD"AĐTŠž –ÚąRĪØâ6ĩ\­>Ģ{ųĐ/õM5OSô Ã0ŪŊÃôî†c™ÖīË/ņڞû9*—ËĸLĸę|u]Wæĸ=jGÁō“"ĸ‡‚ĮaãÝ$þQ―xüãŋŨÅ_ýŪÖQûŪAH~= âžð‘āŅĸWüãųþĸŋԑ“&MBŪŲ͛7‹t:M@4ÐęÍMCŦjīš p0–ę)Öz`QŲwDēĒ Œ*âÐņWžZ>ķ,+CA  h˜ “ÐũuņäŋúMÃ4eMÓ1M]ķaœŧéDøĻýFÏ!ĪŌ$ Ņ5―^˜:wþf"!Ÿ5<Ũž·ŠýPûáÉŋZ>šÔwuŲØõŊh>zük?ÞųˆÃ_Ŋå,|øGžĸčū.þu”nÔG þQųü%ör ›ūïŦ…ĢžŲ·kk{”oū?<„‘ÏÓfΒåãĀ-‹lذÁÁAđų†!sĻģfÍbüøņ5ÃTííÛÅÓŊŽāݝĪ䌑Nd˜Ō6›3O8Đ“fĒĄÉ6#@ŠBœUQÓE‚ ‡:ų‰~?ÔÜÞĩsNwÓ,T Aā…˜IzΆ*b”^Ý2u[MĨ§zaj(ˆÅ_5H$ð­!vl`üī#iHtœÁķöđL™ÞERPðŊ5ŸZsRËEŋGžz‘ĩ-ÝÔyũđ§ŲŲ6ģį·ÓûÞŒķ ī哈?膎øAˆæÛlÛ;@Cû i3.œ…đjâāYÃl(ŅÖ1žŒ "Æ#‰ęVá/P A TBÆ:^y'Ïý~#Ÿ:c þûϰūŋ“3N? -ĩ<ÖØP˜ŪCÏÆ?ąqĮ^Ėd#3įĖĢĢ)EXĩEĪ&:Bþ&jüÔ M֕E.%ÆOIKDüXüã##ņø+mÔą!ûjžšÆ"ŠĮŋūüŦ8FŨĸęqŦÆŽŠÄÓâņöƒÆŋšMu‡‹ŋM@ Ŋ ēęį?Ģ­o—Ÿ}-Į-ĶôÞûžýŧ'xîßįīo~‹ŪŲsð<7jL*Į d^n͚5är9N<ņDZ[[ebõð0kŨŪåØcĨĢĢÕĘÖÐęį'þƒ 0ÆU—]ÄíóŲʓŊŊāÎ?âÚsŋÎÔ#fаæƒQ…HĩTĨĻZ! ÐŅžb°BŠrܰëUĐ8wî`ãķ·9}ņyHßDžĮÍS“‚ŋ"H1 ByÞęoJ™š› pPø 4pFxéŅÃ>æZn8giQâ{o§§ãLþazA•Ą ÂQ…\)ō’DU߇ę1‡!č:ZTFÓЉ,MyQ5~Ųžn°ãõUŽ9j"į.Čó›ûîfō%ËY3[‘UÂ4ũ+CĀCt ölÛBÉleÆäņˆą<ýā#ĖXz='Îi" 5 D?„5 uƒŨ ƒą­ïpũĘwđäšk˜ÝŽr§hîûŸüDs ÜQ6nîeÂĖyLŸPv š&ö?? §<ČËÏ=ό“Î"Üþ2Ïŋqų$ūwŨãė8åhwŽbÝfƒŊņhð}ėō0ƒCE„™a\{+IŦ„åB6›FÃ%‡Bcx–/ö(6iZ››Ð"eč;ŒPqōMm45Ī!ð°J„ŪQ!H50Ū­ íÏüēĶAex˜ Ý@[kÉ Ų˜Ē€Š“äâŊÞLēĄ7ßfp`ĮÉ7·QČ&ØŨ?ĀhÉ"•-ÐÜ܀éōęïî`“8š/^yų \pÝW0Ó9遊ĀĶŋo74)īĩҘ6ņËvI˜‚Ą‘2ŲB …\É­Ü| kQīCšËÐh…tCB6ƒŪAqϟ‘b#•Ĩĩ­•ŅݛøåCOsÞۙ;Ą-°#ÐL Í­4dd2ĐĀīDŽT.…ïhø ũíeĖŅiliĨ)›ÄsJT\0…Íū Œk)2446­y ģð9n―år‚‘+ą5,/Ä.{wũáÚÛ[Ië>·’‡­#øÛ/œHG[3ļe|=M6Āw*T|ƒ†\·4FĻ›ØÅal‘Īĩĩ”!Óĸ0 Ģk$Óõ‹‡-ĸUҜØüŊš_ŊáĩFûOd$ŞëPÃĒ*ŊšT%V/Ýãû~ėþŅ_3þ2õ(™ŠõîfSdzvā‹hé ÂsŅFGXIōÂû›°‹EēÍÍÅĄģģŨuåwÃ0ÃPÞ···ģeË,˒ ī(BAïČfšŽĮ Ó―Û!i$ņC’]ĒkFŧßėĄbUČgU™Ļ GNåŦ–}ÝXšj•Ô#MŨÐ GÞø>"FRãP(2fĒ z~ĩōņáĒš‡QĒ~™ˆÍÔÅ_=ĄT4Ž\|2 VŋƊe“-ndņ—Óݘ ļ{3ÝõKöčI)+Sæ-åē 3ðÖjyßāšË—č[Į,ŸëŋzÞ{/ņĀ3oā8“įĖĨž…áÚh†ÉĀæWųÕëĄÅĻĮ—nü 3Z\~wĮŋģÕŨI–+l…ģ/đŽóNjâ‰;Îß %ųK.ųKN™„rŠbŒ•ũÞÁøsnäīIÏwį/ÜÂĘÛūĮï'ÃĨœFCĶĄš6ïūūŠWþļD#`$ð—@HŲÅũ=D(€§\ÄJhl{ý1~ņGžë ) nĐHšcĮχ]ķpý ’ôD†Ó/š™ĮūÅ/o€G–g9ýČBkŒįž“DÐØŌ…æš`jIp,Sũ 5„&į ZMG>{ô GΙOAũmžHÚHQī˜:IĶÆéURCĀ)OCŨÁ!‘“ŊxUhãój">*??5ų'@@]ücú9ÅLŌCÃrÐ ™c[ũöfN˜5‰ÄÐvÆöīäL‚þĮq*Ã머c"‰ŪƒôÐĶTąHkrąĶcc<―ōŽcŪäÏϏĸųG„ ?Ē čÛĩŦĢ›mķ’Ht2ą ņ!>äïĄü!˟ųÉ ŅĪóŽÐöžMĮu …&ôÁwxoũĶķšTĘ›Ũ>ÉĶr·~ëVýäóž|î>šŪ3ŠãûčAˆB’Ëdîéaį`™‰ú^voïĢå˜,Ą°Ð5!ēcļ€BŪQĐ8ߓ9G]—ó!`j>år [ũИ):>§]ugnäĸüÓ}ž9§tØ^€ãzXå­<üĖūðÍ[9&ņ?šãy4Ąá{‘€Ð04HæiÄdâI—põéÓ ]‹Ņ’ÍČ hūFđXS@īĶ4Ųģ‹álqįqæ‰Ý,{kˆ “ŧÉ4õs֕7püĪœJ‰ēã1XÔ]ŸœéJCÓõ\Æú>`ýĶ^ČÍÛŋBŠŧ{ķšę݁–4hΙļžO(P)VþÕT„*›ęZR"SąëĪÚģ‹?KėáÄzû:6u>ŠIüխޏÕČą'^ât„],þÕĘõØMŨuÕ!|Ÿ–yóą-Ö ÐíøÐĻø!;ĮŠ tN íÓ'â AčyčŅ@hiiaßū}ėØąCþ+QĄPÞæÎ;fÜļqĩ lcJĮlÜ=eķžÕ‹69E[‹`ī8Âæí―čĢmĖî^ )luq@“•ņ@ zouĸFęã&5Ēz}q1újaŒ 9ŦüŊčt\ÍvŦïUüŦŒŊúø{ū'ó™.&GŸ°”mũ/ã?o lsäqį3·-‹eMD{é îđgų„M"1‰Āód?žëa!…fšyÓ'ēōõåü˖f]Öŋü ]­g’ÓŊ=þkúÖ·°oŊÏé—_MÚąI†!ŊüöAúŨ·1ļŨ•üŽo„žïcÐ}˧YŌ·åũÝEGĢAnęg8Ąë(oŽâ§?ü>É0ĮލëŲØ3‡î9ĮņڃÏrïÝ-\tV'šžÁó&Ï;“·þ†‡îü&>fÃ<ۜ3ŋw= iĪ@čI‰§øŌc%ē”…^·íúh|Ï#L€Ðč ũūË}ŋļģ!Ý9]M„ĶÉäF›ewßÃŲK?ËąSRjsÞ9K™4ģ›_\ÎíZČIô\ˆĖï!=Ŋ>ÁîZGihĮœqSō!ķ ’ÎSũ™jVåŋ:ēŅĄČœB‰;ÅĐė?uÏ/Ļ'âĸGULę ŨšŊœEÆ:ņûOÍ}åPņũ<ï/‰ÖņøG΋ķzõjQmuD?ĶIÚ4qzvPŲŲ‹_*Ą§Ód;'<Į0ð̉VݧR)y‚Öē,Ų^ÄËår ÕšĐþžÎ$Ų;ē•þŅØ^‰Ī™Ķ5ßÅøæičĄ)7 .>û·qq‰ņˆ§ZCÕĘG­ĸ­5ß8āō뀓ÛoæĖų—°uïFîxë&Ōyģî?ýðøŦųÕÂRņWįĨū·VS•puu((z~ú.ہá@.ŸÆó=ÛEū0ŸL‘MjŒŒ–Ðiō)J#cé,žþė;öą=fr­ûËRÚíßݧ2{(ĢŪÉöŸ;ežæyÞ­ŪNåã5w ÉÓkzO­5ÎąĐQuæįŋïÝŲfŲųXČ?—Ã|—û•'z§Ûj‹ÆÅöoŧïķuųÝÎåÐößčžļ6§gó4;<ŪŅåuÃöq§ýũ<óóĮ0ÂåŋÆßô‹ų''';P˜]ßü`Ž?·Î~Íē9ē|œ WÞ)åØÁ …“åÕäؚÆ+ũ›ŸŋžÝqēfóⷛrņëŦÂzįÃwķåíĮŠ7ēîvœß~ô]nøÚĄ-“ü―ZŸsøĮ4ÆÛĀ ü­Ŋâ§ęĨÉ_ý9ÏþY˜0Šķģ>Y“UÏĐš|åĒz7VØá%ÖóæEđļĒó]E[ņ?ü™üSYĸĄ`;+ōyВ§Û"ĸ憌XOthƒü;Aý―SZr;p`“GųĄÉIŽÁþĄžYß)eĢÔÅå+âîƒï Qþ!šęsų·ÖėAųƒų+ŨãïúiX‡_ŊÖsÚŋņ™“sęôeþ:øÛg,æŋaŒ• ėJdŦ; ųņÁT)‰8îsž'IïĶâ-lÎÔ@:ōáJØÝĶQũwE(~'M|ˆc‡ĢėąąSųč4mĖ}čBVĪ·ŒōˆÓœrŅÞ؁WSč,é()þf;tÛ3āĄ[&Ý øÃÛreþܗž "ŋYüˆÖlˆ‹ü­ŨČ_ų.ĸ8[Aˆü‘ÕĘEyÉ7ōį?ōŽuí5xq$ūđūæŽXŒ ·˰Ŧ—ĩ}žÅÜþ§øËFþØL›']ōþĸ(Ÿų“öQNd”ž*ņæmū„ÐþIŊ,é‘Ĩ\č5mõõ*ïÚéýÝ /ŊÄk[%?í7ŸČe”?ŅWW*‹ÍMvœäG:ė„7úĶų9XfđķÏaÍÓĀKųÏ=ëWþČÜĩĸr?üÓÔóAü}æiŧÁīÄa—ü­įĖ?zpÚ:‡ŋmĩÅßįÛ}ð—ĩü Ú §ĖŸgęæėė,đäísþP.ž^P Yā{mÜë[AĶļteŋ„ÜĐÎ TÔօÏŌp~~žë§Ë?ŨSö,Ã―œŋml)tæāĻm:JÖ;ŋý·cÕÓāïũŨΟXĩŽ#;˜f`vŧ›g[Ęc’ŋý~[ÏĸüģGÕģ-ÍJÍkĸ;þýmíßðŊâĸ'{ũ%E•>lüđ·Šš{†˜†œģÁE@2kŽ@ɊJTI% ’3DÉ"Š"HP@EPEI2Ā09uĻp?čðœ]ũÛ]ýęwNė·šęĶ:óz+č;wÆõĸoÁôyüÜņųeŋå•Ï.·ĸ]Ūð\ŋ_ŋŊņqđ\.—ÎÄõD,ų|ņŊ^Æï‹ËårđDJJŠÂårđ\.ŨŋEârđ\.—ëϗ8].—Ëår§Ëårđ\nâtđ\.—ËMœ.WÞCÔĨ~\šáÁĄ‰žįkšDðŋ•ũĖY!ļ–Ëå&N— ĐéÛ"5å>˜Kŧæ‘ZP9>īPB⍌Ē`Á‚DG'$%tždētÖ8^yõ5~ FaH/’Ŋ€Äëóáó" u""#ðz=^_Þ65!øÏ)LÓ ZۉÏMdû–iĖ_―o2~ú–Iģß ―ō=Ô.‹ã(4ÝCĻ‚áē DFäKĶÃAd„) nx‰ ĸáķāóy1 QŅ14ŨÎÞÃï،.xąŒ(|ARũ„·g:ވá:Dú<ų—‡ËŨ/nĢ@ÔĨåáõQ Į&üpĮÁ‘ļĐ^Cš7ĐOÉhËQ€ĀðøˆŽ.Hx݈pŋ#ÂýŽŒŽÆkč—ĘóbûšÉ#_bôīuˆč(ΐ.s=-ĸނÚåâ(4#ŋųōú-Ü/‘x=šnÓHt)pđÜÄéúÓBâO9ɞœ Åk4`ÞØ‰ž1íMÞ_3‰ú•Ģą•DųÓXņzZ·nÍSÏ â`Râ§=3öuÞ|{ óf―É7ĮS1s’ųpŅ$&/ßBŽ-ŅĖdÖ,^ĀÜųkH jØį0{âtVū·…ÏޛOÛ6mxķĸNd›hšä?åX"Jߐ~]ŅI,ž9•}‰Y|8ĢëKóúÐ.”ŒÔ†ÆĐý›Üïąp{ŒÍ‘”š&Á ēsÕlÆÍ]ĘĐī >`ß§kycÂ<R,ôāyV-œÏ›‹ÞcÏŪyŪ{{ķÍž*é )H:ú óÆ åÉG[ÓúąūžõņŨø….?·…IcĶóŲž―,›2ˆ6mcÜÂɕRÓ8ģo“Ķ/`ĸ‘C,ô\xųÄ7wT—Î7 āØPšA#îjQ4l%ЄŞŅĩããī}Ž #§Ŋâl–Efâ~fŽÁãmÚÐĶc7Vî8„- Ī™ÁĘi;eoΝĀĪyïq*-@ŅĒhŅ %E Eá8š!9ņõĮžŌ§Mļß―ū€ŸŌĖpŋŲ9‰,Ÿ:ŽåwņÓwŸŅũBÝ^ĮþD?RJ\Ū?Uâtđ”Rx — Hœ“ŧ7ņxįųpŨ!,ĢŅQØiĮyýåNô숭`ÕÂé<Ôa(ĮÓÓØĩk')~lÞ_0‹ïOĪcgž`å„Á šš‰tKbØéĖž9–W_Ïņ,•q˜c†ðl·<Ųo$ïŋ·†y“Gqß ó8“ë WR ŋhýęOš&øGVÐææöOÓĩa Éûķņڀįxa~·ÝۋĩËĄkŠ„mģhŨæ &ŋĩ-û2sÄËīmû8ĸWúŲ2gÆÏ'ážIĪaēcËr }‘―‰9N:ģgMāųūOōhÛ§xkÍGœLp%74ā‹ņ˜°pŧ3ōåæ7yîĐŒ[đŸ‘GŋßĖāáhÛęM\š5ŦyĩßÓtŸþšĮKJÂFF zžûîmÅ đKyũÂōýÚ1ōmØB㠁Ūåōþļáô~y ß%…ˆ~VLėÍÃúētåûlúds'māÔÏŲ$|―–Isß%ņÜ96Ū]JvíYąãSøō‡Ã8üi'X°h‰Y ߎįđþ}ØðíQž‘^v,―ÐOubęōílŲý5Ó.ôIÛöØ{ÆB3ϰhÄHzu|„{ëÎâuïēxęŦt―‚\ .ŨŸéYĩ.—ēˆ(z3sĮöㅁÓŲ―b Ž‹výÆ0īwk’vŋÏä%ŸQąÝ(6MęČš7^Ēûč9ŽŲӋ~]z2oŲūOaî†ÍÜQ1‚ÔŸķ#Īm*$€˜ķ…iQŠðũ`ȏUšþ“Ōžāa^čü ŸŽœÆW}Úð@õ8lĨ’Pæ)ÖŊĸËtļLˆHnžĨ!åŠGĄ‡+īČrt|a0tzžĩïžMLÍ;yþĨ‡ˆī-‚) ž=oû ^]ö/ÝS‘ų] įŠmĖ™úÍf=Žn„pB\ŠŦƒíq” Ų!BJpëCO3 ûc-—ïüŪÂBĢc؇ĄÃW‹{ÓęĨĩüļóü]ę‚[YėoÉĖÃČxg(ÝßXÆ7oÏäDũ;ðč^Ė Cņę2eboB;gŅĨßĖ™ŧ‘ūO܊@G€a™8&čhdĸŒaŊ-# sįÏæáFeH:—JčPúv~ÕŸąyŲÚ=?C'OĒZÞÏ —{ącĸK;ąņà Ũžo[ØĘFI óėũ˘ŧš’"ymõGômÏīōšOXšrC;ĮkÚä<ôėhzÕUtoߍýkwqöĩ'Ļ%PŠßŨâoąä›_ĸÜętt§ëÏÍV[ŋĖÚęÍXšd#gŪ`҈^8Qq<“L.pjûbmõ?Ÿü€Ŋ~LÆļ5ĄišGG!ÂßÉ{áĩ@“W_ykáú–·ÐꞆ”2‹ÓīÕÍ|2kiđ~ų‰™|€‰ý‘ęŅ€Ā!ÝĮÐóčTšvČ!?ӆÝK·‡W0ėÍ]tč;˜[Ëø؊œĖ4Î? Z=ŨĐHHEŅė‰û+ū$9ók2ýíR@ūũęB^s1TĄŌ•čųŌjĩð›VūĪ š õį/;`'Î͐rö  ]sũgŸ Aõ*˜ZQfÅ:N‡~&7č p°žEčúTWîŪ]™ģē95ãß`į œÏĩŊ‘ ‘ũÉL=Ëiîhý-]N6EŠF Øŧ}+ã§/åėŲTrē3€Ô Ð5/BhHéC3 îėŒŌ’’ÁیÆ7•'(―īčxŽû–Ó) dJâXEZfN&gO'Aūä)HĄpė –mĢy„f@z/ĖŧۏðhŋQžŌåï€Bhz8 å Ã#dĪ †ķAļ iāõ Ұ­~ˆˆ§€7žüŸ’R +5‹`HâõEāõx1ÓÕĐ7›Ž*ÚũËāîP†&JŲ€BH‰/ÂŽå€ca;ÐPB#ōbŋe§ …” ôhčķƒe98Ž—˝qšþ”WÕîgŅGĐPĐ2Ĩ‹iüt2_Ī—RŠSŠ$ėĸãÉÍĻWĨ(I'“Q ŨĮx‰Q’Žô4Ö/[Mé6·cD Eyāû/Yđh6_­›ËþãéĐTšËtC’x4-Û>ĢpâGlÜü=ĒčíÜPš0ŽâjšøR‘ĨōfBB(ĮųošwplË2Q€ē •Žū uåÏXī`)ōŽŠŽ_―Iĩšw!ŅĒc í$/[ĘÖs˜ž|ŌqÍömGâšóąô3œ<“J‘ø{(ããÐáý€IRŌ1’2üŧ·~ĖĩbØ―č-NœģĻpĮ”.(9†@ó'ąõ­ÅÔ/r7ß/_ÎÞóPąýM”Œ40óúP`Đ+u*Xž ÕKÁįK—1ĐL)ÚÝ{=gÎYT-i‘‘â§ĖÅ(QHąÓdGũ#é~)-,;DîųïųāƒÏļí–ëōŽ8–ELŲęÔĻT‘]‰›YļāėæEX―z5āå†ëPȰ1\;&.—›8]ÂŦj­ĖéŨ·;AŅxIRrĸޞ!ï v•xޚ›Ė€g‡ō|ĮÍ(ENF6§}7üÞãŧ'2e@üū•LčڜûjÅÚ}ËyųđŊhüČ”/}’Ô`]ĻðŒPrîāA^zļ5iþs8EŠ0zö0j–ð…ÞÕT8öĸ)oĄJŲØū"<Þs8§S^dúk―ygXfIÚŋ4‘z܆Ątnoó o}2šcûr]ËĮļûö›ųpã4]Ót‰'Ō#ÃÉ*?Į Q tmÚßw+#Žå‰{wpĮ―-ĻíYÁĖc㗭)ačė|į:―ŋSŲYTjô(ĢĮdŨÐIĘ ]X^2ņ‘äfYÜ9`Ÿ?;„čâeÉę<ģd-­M(Q:"<•oíÄÂu 9v.•)ˆ.R‚ŠåJrëØY4ëv[‹ĶjÕēœ;҇Ôl‹J#q"cč3jwt>éDPąj1Œj―ØĩŦ#ņ%Jaæ(UĸvFĖŽEûcûMUĻUŠ”%Rw0 ]Įī]ŧDÅRŌÂRe™°ø†Ģ))p6.ũEÖ.—ļöĘžÃ‘ĐIDÞĄŌðŒ.ïJSMĘKŦ8yį<Ĩy1qyđ’ĪZNÅOR­ųƒ|ļvÅD6–ÞŠĸ)ĩplÛB)Ū"äĨú…Ŧķïcļ= .Í(RĘžíhZxÎÅuČsíö…Ę ‚ éðáĒ—yīŨž™ķŠŅîAXAP Ûqš—ï–öĒÉģŦļė^}ļ99Áp=GåŦŧD]ęĐiHĀvōη†c‚0*Ü>ĪDáķæŠ Ŋ—WįžH4MæóĨē/8yý&Ðt '܎Ëû“ Ërpý~Ü6ŊåŊ~qКÏSŲÔmģ›8ĸ'\.!13OēnÃĕŪ@̆ ð(Å_ƒpöÄw|šëÕ4ĢF™b ōßfĢ‘~r7w'rCýĶÔ([čĸ$ņļÜÄĐĶjlcâ|%H œaïųĩŋyâtÕš\ĘÁSĻ<;_‡cYø~‚ŋ GA‰Š7ÓđzB?!Ë&?Ĩlb+4 óõzxyÐt—ë7ÕĒĖ3Ô+u7^=’üš—{œ·žÂyĸîĄZ—Ëårđ3N€xoešÝ4éRŌžÖáäoxûā‹ņįžqJ~Ëårđ\JAbzö―M ”ËŠmģi7îF–oÍeUãëāØĘ}V­Ëårđ\ķé°þ›ŲôÝŠũūšÔ%ŲþL.;“v+hŧ·Ģüĩđ #üâgũYĒ.— Í#‰ˆ6ˆ4bc#ˆ‰.ÆÝõÛpŲÞwb[Š_âóųøŋ X–išnâüõļI3##ƒC‡Ž iü5đ\.!ĀðHĒã=DÆDGĄkI‹.Āéó'Xýõëė+Ā/Ųĩkŋ―ð­WTŊ~QQ°,ËMœĸ{.)%))iÄÄĒtéR8ŽÃŸ˜ËåÚÍŋ$ĪĀðiŌ ÓąÖ덚’4G/ŠˆXÝü’jÕŠð[Ó4ãĮO––~ž;ãüÕļ„áÄ_Ûķųksđ\‘za:Öž’4ũÝÍĖõƒ°#R‰‰@ũH~IáÂqüÖ C'MĮqÜsœŋ>—R*ïãrđ.s“Ķ?˜ËÉsGx QgŒH‰î•HÝôKũrþŸü-q•WŪ›8].—Ëõ›(yC^ŌˆðFroÃĮÉ/1;ĪÜÂ―å/Ėårđ\ĘQ˜AÅ/ą‚J)ũv”ŋ6—Ëår9|{bŸėXƒ?ÓD)š@ÓB žtbKøˆ*ėé&Îŋ8—Ëåri†ÄĨ_J”Ũ2žĐKũ.—ËårIM­Ģ8ķú§ŋ1|ÚÅÝÄérđ\.ũšG†?ŋũ⠗ËårđÜÄérđ\.—KĮårđ\. Ë-]Yðųü_ŧ 7qþ9ļ\.—Ŧë-Ý—{ĻÖårđ\.7qš~ĸ„ųŋpõWÁ?#ĪDJņ‡~ŦŒĸeåõ™āũHH™W·ĸAĸgíB /í?‚ßÂÕc$þÉ~þ?ĮKíüiļ‰Óåēm›Ü? `‡ƒ\úôûąÅ?8œ<°›ý IHMō[BĒý–+Ά&ĘōÝŪ]> ĮþÍBQVˆėė\2/†m’“ãĮáwFYßĸĮ3‘üđáąüûĪvĨÏĀ!›‹i+þšŪaú38yâ‰IĐ,õŊĮYJþ[ķĀåíŨ!.Ą}Đ E0Ũi™øõŋûŸ3#‰Ý_ĸ@fP!tnâtđ4]'åðô}…„ ð!6/ÍóÓŨ<öiÆ=?‘ïÎæ"… ?)B|ēhãfÄcˆßx†l…rIKËB)þ=üŲiddÂë‹@oū؏Ų?Īc’‡Ð ė”úu}–Ÿ'á5$†īŲ6w$Ŋ/ÚNHó"ŋ]å°nÎ+ŽØpJfzĸžl>š‹ŨóoķW@Nz2ĐY’xĢįģl:œŽņ6Tj‡ķ2č‰ķ<К-ũ6šÅ["tkË3Erzķü§ ÆĐ/Ũðüˆ9œhxe6ģ‡õaȒí y™ßóÚK3øæó ž:šĮ@þcŲžŦËV–IDĐęÔ+Á„Q9îũvøcF-ZKÃŋ7"FÚHÍļf]qН—ū„ë'.Įup―5ų3­Kmņ„ûS Ðt]ŨÃË<ë,ÄŋŽGĮ"Ēt…hö`kŠņaÚ ‘Ŋu]"„@Ũž˜ŊWē|ōpoþÝk`SˆmÛPŦDlšĶãđÔV=_ûÂc{ą.áqį2‰Į°yoÂ8>5ëōþGY4mŠÄ ĐåsáŅ9ôå{žąð}‚^/2\Į+c+4=Ŋ\yiŸŌ Oļýš&ļĖA§X‰Xö}ē'ē1ÓNģņ“ĩĖ[þ]pbĮ[ŽÏQūR-îŋģ)q€ŨÝÐŊjŸvĨÃu$ß8†Ŋ]ęˋm" xã5” øĢs§Ëå8Q%ËSŦf4?$|Gęđã;xõãNeårā‹CÔĐ]“ēq_ŋ7žŧ[6ãÎzēãH:š&A)P‚äýéÕþAš4kĘã]į“ix8ĩo=Z7ĨYÓÆô5›ó!‰ŨČåĄC™:ôíK™C;x}Õ§Ú2yĸpîŠKČžųcûŌĻi3š4ęÁúÝĮŅ ‡·. ĸžOŅž26ŅŊýpN‡žœØą„A“æŌŧKkžð>Ķ’áūM;þ[ÝMģ& yhØBRM JfÅøþž1e$Þؘw?Íw‰YčvËĮŋt!>‚V—ãg2Ņt ”B(@ų9~d?Đđ&š&8ŧįÚ>pgxžšöYAZōFũđŸf͚ðčSIH qîĀFÞœģ˜ ĢÓĩŨÛ ›{vq*ÝD—!ķ.ú›Ðļ~;ĶŊØ †—ôïŨ3xâOEÓõč2|Đķ@p™MĀ‘œõhāĪî―sSĩrčÚē”ûïhNóûZģé`VöO,5„Ų &ņÐ#8››ÆôSŲôí<^/?ŽÍøe[Ð―Š'Žeˈ<ßĪ!7ßҁwķĸœ·Ï8–MlÅĻXč~:JŌÏ ĪËÅp‰“ïĒI“›ð™É>öķäÃ7Įqį=íxōÉÞtiݎą‹6p,éGÓfÍhÚž ė9‹ŨÐ9°i>ƒß˜ĮÓĄÃŽ­˜đįũd[š5iʓŊ-æhúÅdΟ†›8]î”ÓÏ-Ũ]Įá#‡ųúûBĨïĪel|ũŧN§z›8ģs1―|@ũĐoŅĄūÉȁŊ‘!#Ņ 4ތ}<}WW 5eΛ xĐo3|ĐßŅųÉĄxoĀâ…ÓHþr=ÆnĀÖ$ ŸūĮĒĩ_sįóĢĻþýï9•Š å6ã†/ߍ“u'{áæÓ˜Ų·>^MBj)@)…Œ*Lëo ~óÛyfČj— 0ōņŽė8WyKp§g7]ڎâīĨĄ PŽ·Hyþ^­,··{Ч{w"6ĐGxåę9€ÕNĶžąÜŒ}ôė2–˜ķS™2 _~™Y:†$ Į$ē\cú―؂ĩÏÝÆīSå™þėýāøpB?fl;Ãä‹Ü%ŽáOwāûtûŠ/?ĸ)$þޟYwp#ŲŽ 7qS‡OĶč O0ę‰&čBĶlLËӃ'óöô!hëfņŲą,žšÍž ģ™óM.ƒĮŅžð>^?“sAøöãŲĖýÆ Į›ÅíåÕq39ozÐĪXđėútGēĀ<―‹nwu§HÎĖ[0Ÿįšĸ 'āаý0V-›‹–°Š·7ėĪPųš4Ž]ƒÛoį{5ÂëdóŲĶ9pø~ų8úOZMŋņģ˜6ǎķcųÞdn"+Į fÕ^1Ē;‡Wãģ3ÐĨLÛË―ÏtäĶóïŌ°q[oøŠ\42máĐþKļį…) ŋï:FŋōG‚Ei|Ũý4ŠÝŒ!CĒļĮÏöŸr,9))‡ũōéĄS!IÜŧ·Wާáð tŊdHŧ—ų)ËB†Į߁ȒīŽX˜―ągĸOÔūŊõË'rðûl<Cݚ5ąÎáĢ/ūâ\ĀÃÍMïb˘!īkYžukŨ` ‰Íu7ßÃŌ5+i[k7G|„ĐédßÍԑ3ĻPŋĢ[ßČŠ#X—U‘)ófsw­Â(å nâtđĶ2ĻqKÎ~ĩ‡ũ·}MTģéQ?†wV―ÉĄŒnšņzN'&p ýóöeþšÏņûBB’œÄ(]–>ƒûRĢbęÔūŽäo·Y‡žÝFđĘuØáïĸhÉh}‘<=â Z5ûeãâļŋĮ`z>؂ĩĒ1’S8ųÃv’ŽfõĪĄ œžs?d‘œc" „ôQĒRqŠW*Í 5Ŋ̈}ŽO‚ņôۑŠeŦðø3ϰ>áp’….E8qj‘…)_=úÂzŨQĢjžX„ VĄó°ņī―Ģ5ŦW 7x–”ÄĢ|™y–=s^ečÔwÉĖĘ%-ÛžęŊAˇ:ŅžRyéݙjņ>„sŽmý@Ņ{žĢ^ÕōīhӝŋÅåēußY Ŋ.=yŒhšD(°?ĩėÃĀįĪZŅ‚ ‚Þß~ū—pDWķ…SļC^z™VMņhÛv$>ĮéÔl(r1þ^ˆ·~ž§/ÄÏdųę핒H!I9đŸ#UŊĢgö\_Đ"5kT!&6ŽÄĩ‹0hĮÓB„BūÅ(],–r+s}•ēč(DøðޟŊ>ú‚ėšļŋ^MęÜŅ™võK°jÓ„Ė$ķn[fLJóÛîĒ^|a‚ÁÂl3DÉzģüÝõŒmW!=Ûņėž-œJHā§c?ēlęŦžķðö'œ 3ĻQņBđ%K”Īæ ņčĄG]>þW—d:ū8žG[6ĒëĀgˆ/ą‡Ŋgå%lKųhrW]>ÚĖGßįú;{ŅĐķÁœ%ģÉ-SŽZUËŠ(!ÐĨFÉj7QÞwšYģįŅæĩôowQdĪâå^ƒų`O!ë~ĀT!þÖúôž‡RF{>ĸ–‡ŸčʍúļÕýũQ9Æ"`^đēũÎMœ.—c9ĐZ—‚é_°dÍVšÜY‡š·ÖaÛĒÅ>W€›*Ä )_.Ï―ĖÄYKX8cąÂí8葐Ë™ó™ ū˜"–A†ßDiĐIX> KĶLĀ"č\ü.…Ŋ@hˆŽCzËņčS―"[ bKVĄ}Ũ>ž6ëMv~0ž›ŠzČÍ bŲ CHló’ģ2Áôs`ïyB9°€`(@ å<ĶUŽâ1ūž~ĩ-‡ō·īÄųņÖnÛGÝz5ĐuËõlœŧˆ˜beĻZ\Įq šNî™ýôėÝŧQ_ÆũkOĪæ°s1§­ĪmÏīšåV„ô`݃@ĄĪ“ĖĪŌϟ'' ÐØĄēēģqdnâtđ”m!cËs{YŒĨ~ĩ‚Ŋþ7"…Ë>BanŽĸ·yēY―r{ŋÝÍöŊv’ãčØ!þÃV \:•ˆfB·nLž>)SW"Ŧ6ãÖZÉ :ŠđģĮ3hŅ!ęuŋ°=L‚9ؘ–(ޜl! „Ž2‚Š^Ũ”æ- ąö­ũØā[ķlÜÂų€€0GAl™Ō[ņSįŊ"ŅĻ@Ũ†70{ä`f˟Ã+ÃįQ·^jÓ Ų P(ĨQž| ķž6ž%ï  $Ü,üA3"+'HáĘÍčTķ(Ė]ĘÞûŲū}#§s@ĸØo&9™Ųäm„ē QŒ6=nį܊áL˜3‡‘ƒ‘\ĨŊ‡·@)R·ÎbäðQôą˜”ô,āXDq5!4ÓáäOGøâ‹/Ųv4™›w’fëX)'˜3x$óæMaėŽÅīhu/•‹Fâ?wėB|óóÅ+ÆøýÁ(đŲ„,‹ÂU›ðx!Őgú0eú4æ.ü€Ķ‘šô3_õ9GÓÏóåŽ Ë0)Y2Žõ+–°üƒ]˜Rbgg’ëũqŨS­(~ä-†ŒÂ΁/°)OŨ–Õ0CŲ,Ĩ'<ÖķâŨcģiþ †ĮÂEģXķ3›z·7äĶĶũpg4Öžŧ–―{ŋáýOvq6ËĪp|Qūýb#3æ~H†KĩrY―`:Ã_ė˛[wŌ$ ˆ5tĶN™ÄÄY čĸŌ Ũ˜KúpԕýÜ[Š-Š›xU1Š–4(}}]ô€—Š•SP€m…Č ‘vK'>ĮēOLJ{ü,xc<Ŧ?Û‹ß J=ÉĄ―[Ø·óGÎÜÅþSQB†ĮzdnmY‡U ^âÕQŊ2ô ÎtðÐ8øáDžč1œÄ@ÞEpLnâtđBĶ6#'2gÆ *úLŒR·ēę7yĐW,3DÁjxýÃų4ŋÞCŌų "# bhÍÛžHï֕1­"ô_ũ6C{7 Ïv ÆÁW ŊO™Ë}ĩ “”bÓóĩéĖh[ÓôÐiÐ(Z\iúh7d4=nŦ‚iš4j?ŒQO5ÂĒ“.Žûô…xŌé3hąDęaV(DŅfâĖžÄNnČÃŊOcB‡:d$ĨPũąąLŸŌ‰hĮĖ[Į6MtĮˆAͰ2ÓPÞ8Ú<7ŠûŊÆī$-;ŋ€gZ e^^ŋ˜ŧ\ÏųÓg‘žh yŽ"rlTdi:žü*wŨ-cŲ8Vˆ›ÆÔq―ąSRˆĐyógūA؈åšugüÐG1|Ĩy}æ"æMCIIđFÝōtƒp’T\Ķ@ĄÛ˜E<ŌŽ0z|=VΛHÃĒērLžÅŠscĢŠø3‚ÜöÔkôkß͟ƒ^ŽĩU!ũbž{8ķäÎöýyāÖb˜"†ķύĪ^)Ķ·ƒ7,ĢëýUÉÍĘ%"š$ū8ŒaPqž:q.ín­@FŪÍÃÏŋJŸ‡ë““žAȉĨÛ°Ņ4(ePėæŪ˜;‘ŌZ.Ąļ˜:w-*xˆŽú#û?Bīî`Đ8ž|u4·–+˜7óģlĘÕŦâøOIÜũÜ(fôđ#Ķ<#§/Ģm̊$%ĨS)4Š4~„Ą}ZcdĨōņÂĀ1ĻwæĖęC•X‰Ō •vbōðþāW<2uß++›ęïÅĀŪu0M wuÃĻîâÓ ņXĸ…Ž˜ÜƒēQÅjÝGũ'ÚkØ8Jņës‰””.Ũĩž^/G#"ÂGųōå°m›ĸ_šŨ‡.Á@%4|^e…ZRũæ;ô Á@ žŽ†I h#4ŊĄå_Žyōb—ķĀëóâ˜AL›ð Û$`Ú螋u° M„Ķã5t.3ƒlÅBâózŪ,#\g.3Cl‡üōÕQ…Ïŧyž^” ÏJ‹mđXvĀĖûÝĩe_S~8a†,įrÏ‹$,<ƒ ÏŽĨ†Ïc_(€pŸ^,Óâi†C(@ &ýD߇šð·77ÐđJĄP+õÏ?Ōzo~t% ā(n#NˆĐÂ}m_ü―̐š‘ïjaÓÁ04ōˆ âH.‡`Āēoáņ’„)Į"ēšŊÁ@…ļŠĖË Ÿ Ūé_yÕþríļ†ûĖðâÉß/ŽéghûGŅîz™!í`™áCžäđŠl‡@ „^ÞÕĨq—„‚A4ïÕuDŲC6^Ŋ'…ĮØÁĀļ2ŽyۅŦÛĻīðéƒðöÅ?áŌu#G~D)EŲēĨ …L7qš~ûÄéúsPv„ï~ ķR-J2ōfqĘšĸþā…xÍžø_‡@ÓlŽ8ˆ*\†rE Rār§ûv—Ë%4/5ęތrėŦ’ĢÐ/ÆëæÅĸZķ-)_ãpœĸĮÎâ6 aýW)kĪĶÁÍ9 rĸCD=çÐņNYAI@e`ų=ÉÄĀŌ’ýȌ3Dáþþ[øųûýû9?†ƒ@8@8@8@8áááááá„„„„„„NNNNNN@8@8@8@8@8@8áááááá„„„„„„NNNNNN@8@8@8@8@8@8áááááá„„„„„„NNNNNN@8@8@8@8@8@8áááááá„„„„„„NNNNNN@8@8@8@8@8@8áááááá„„„„„„NNNNNN@8@8@8@8@8@8áááááá„„„„„„NNNNNN@8@8@8@8@8@8áááááá„„„„„„NNNNNN@8@8@8@8@8@8€—Ā­ĩß`ËũXU­NĻŠLӔÞ{–e`‹‡CĶéžãņu―pÂ#įó{n·ŊÜïߨēeđ\>2F­N˜į9§Ó[ŪŨÏT`ÓZK%ó<ĸï;―ũ `Š„„„„vĮ: €ÕŒ–ýĐJJ8øaïÞcĪ*Ï8ŽŸsæķÃÞ/ėYtUÐRPīķĐĒUĐ4­Öx‰ĩoŠh-ĒĩbžŽDŅ6þĄĶ‰ņZ/­ĒĻT-ZÓ*ĨЖSĘÂ"ŧ ËÎîĖžËûūm§ûŸėfŅāÝį“<ÉädæóüõË;oæ<Â:Gl(–sŒžI_ðD48•RJ Cd]}–ö\LwÞb#F& e ęË|JÓC? NĨ”Rņ@hnڞcËŪ>zCa$p8âhŽHpLs%‡ÕĶ(M "œJ)ĨöC€ūķvöģeĮ>ŠĘK™ÜRII:…͈ÃÍ―đ<[wtēq[7Ųt %UBŌœ§RJĐÏČ–m]!^2à øę˜Qxâ†7’Ëóð„"įÖZœã PoÅƒc0?‘ĪcÓjæĖļ–Ésââvņį?l )ËĀ:cūï#€ąvāg]Áš'>ž8|?ÁúÖÅ,ŊšÆ“Ũ…g+9gŌĐņ1Ƃâlq-ÏóqÎâ ļNņĩÄÄ9,ā{&ŽÁóņ=°Öâ„ýóFŠYg58•RJ G1@â8Æ1!!yÖūþáÔ+ļë–KĐÉ⒱œöýC‹ĄøŅŊ°âžŧØļ3bĖĪÓļeáĶ4 Oül ›=―•·ÚâÖåũq^K­~—ÛÞfÖkøIë|6žúýGž‚ŋųI–ŪÞÉáīóøoÖ0áŽkX~ëĘũü‘›ŊcîÝŨŅRðŦÖëéøæm\sJ=o=ósV,–ķŠÃļaŅR.8Ū c œæóžËjp*Ĩ”rÖa­28‰Éõ˜tė*J„ ąĒ8IŠð?žŧĘģįóėŠo°üÆ™ĖšéQ~ũË+hßüOü-fáÃsäƒ?âąßäžÛÏeúÄ#čøúw™?ïšē}žŧnĩWÆīävðÜýËøÖe x`Å<ŪŋönÞŧt&Ó―vÞYŋžËc‹ĩ†m›7ēmBLá/ōÃEϰôЧĐ|g!Wĸt%'ĸúnjý!úpÅāt€Óā<0J)Ĩį›aŅX'xqDZō… ņãã„dƧmÓŧīEc™wþ4ŠŦkļú’ïņÚÜßóIîbzŒaƍ·sÉ)_cÍÚޔ€Ð$Đ=4KuЁqõH°‹E!AØIãÔËļũŽŦĐã#&—–“ÏũfĀó2XïŨĮÃóbþôÛᨷs˟…ôEôîĮîހŠ‹qƒï8ƒĀaŒŅGî(Ĩ”Rg-Ö ƒbßc\S#oŊZËÚœÏ9ðmL>·3Š–To7íŸî#1Ą–];ķӛI‘Æ9ð%$†‘!‘pXkˆĒ€Žî>ŒxxÆâũ"‰ŅH\ ?Č[‡sk"ú čėĩŒéü ŽÏŅxķĢŠĄ™dų^f/XĖĪę ėĄĢ§ĀÉGÕ1ū.‰ˆĮðĸãösÝņ%:ðïÞÞۚcóŽîĸô?šãI“M Vđ§”Rę3”§=šŦ’ėî܈OšØÝSN:39ĮūހmŧöÐX–ĒĄĖ'áąĸÞ58•RJ9 ›‚#ęKčÉW°eW/;·vÂHá‹Ĩą<Áąã*]æã{‚CƒS)ĨÔ žP“õ˜2Ū‚úŠöæ Æ0bd’^qgCyrøģ8õŒS)Ĩ”u[0Öá#†įAŌ|„áҧRJ)O AI€ãßíÝą ƒ1 …AĄýįņžÂ•Ā xđû Īŋ[âëÞģĀĸ^π`9„„„„Č―wdf|t+o3įœqÎOøÍne73ŨZ1ƈŠúxÆšUu[ŲÍ|‰’ Ô9†íIENDŪB`‚doc/images/ifw-tutorial-license-check.png000066400000000000000000001145141325366651500207410ustar00rootroot00000000000000‰PNG  IHDRÎGæąm™IDATxė’ÍŪÚ@ …QĨ,‘xxĸ-â"]ĩĒ *`Hf2“Û-EÖ5qïQ°U‹ ĨRÏšĸäƒÁl6Ëó<ÆøýRŧÝNū˜Ō…ú §Ö~ŋŨ)[äGžÞiŽjwøŦ|Īó?Ÿ~ԃðb“7Ēþ'ßČGģúÝPéŋ'[įƒó“ó‰1æy>N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ Ų_Ÿýį Âĸ/} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?ؐ§ó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įÆō/fŦ>6ŽĢŠÏĖîí}Úw>;vė‹]'ņWįĢNQ Ĩ* īI!*ĄŌ’‚*·ąĒ(óA\*"þ*BŠ„Zņ! ĪP"’šŠR+Ō€‚Bl“ΉėæÜģßųūvgfw–9Ŋō4ÝX%*ņģ4šũæÍ›ßüÞóĐúņVMúU­T DļÕLĸķū|#l„ú‚˜ĸ'üHÜ>?ęįÜņ DĻTž# sf˜Ĩ€Y6Žr˜ÓÆH0Î-NĐôBĀ„P<Ũu]ŧ 9ÅbrīmûfČ įXg Mĩzk›hŅ-„jęâF1NĨƒÜÂį 'jVŪëĘŅģʑ1æ…õ’‘ð{Į›ĮĪËĒ+ݟ˜[iyóø+éĒ‹0!ČļC4~éí“Ŋþ•õ ėÛ·ĸĀúU-z8đņņ­ûķ­IūwîėŪ_ýāęEí=îčÝÆT˜EaÚĸrüä;ÖSŋĩũévîčŸGļãޘĖĨÖlÜÝύŒ\›`ÜãT’čb‚„#ĸÄDz6õ‘MÏîíŊʎ\›˜žļza*īþËÛwôn‰ãĀݟŲÐÖŌÖŧëĐŧZ“mwú™Ý{úŸØ4sõo“.™*‘t—)ÐŲ+ŋúí‰ú{·ėęĸbîâ[gĮg áåĖdûš-Ï>ņĐÚ ÃäPsņýoþxėg?øöó/üüJVä.ŋqôĩᇟÜūõ‘îó§~ŸÖęÖķ·uÞóp_ßWššU2%!6·^pŠ~ĘÅēUÝņôÎÁ};zýØðDYÃ.HWՏ7ÓžúQ…Ą*d㍊›ŠX”•§pˆsģzŠš4 0AÍJHłdĒVČ&rýoĶÛįGšÝ>?„R û―åP*ÅŠâZcn]Š;ŧðŌÜÔlŨÔR‹Īå™jŌápXŠ2‰ČöZ]]]UUF€\FØ„ëÖÄęv &RÝĐOÅR Ģ ÉHu5Ṋēũe)“s:ŊËĻBQĀËGšÁCJĀš_·š78Č8°ÝĮoŪr y!2|zÁ}z~ní˜āOþüĻ)qÛąŠųhÛGïYÞđėūûãĒd™åsïėiuĖž­ŊYĻåË&nP#2Ē:ŅtÆ(ģx]gÏēa&gķÃįēĘ\;ĸōũŋũÂK‡gLĪcGžĀtcIGŧÎhSgŧ,MÎRŪÛš: ›5vīĢ89k!L\Qq–ō@›É•E+ęÃļX,Ę Kđą?yųŧÏĸdhZ3*šĄĻA„°L“Ų&ržIëÚZŦƒÔDŅ‹ëËīh[<ī°m͊%ØqŒH%—›ũå•V‹°—1il銞;[ãÁĻn›Vý’5›zĒGüC/ū:U°„ƒt fQZ9ýÚúé‰3H Øs\™5ķįĒ!$hĐ`ðÕÓŊüčĨã$ڐ0c…PÊĩËȎYĄÜ{PþäæūŧŸ4&ÎþæŨĮŪū›fþOŋxņ—§FŦ“‹0c’5Í@Ôīå“F•øØĀ’𛄗,_'QĒôŠ\1ššÄq1Æn…WLlAâņ™ū˜Î2VÎÎäFÎŧPŠ~mŨķĩĐ$"•I4ÄÄ,•É{}Αp$;6öîtĐ<û^úZ&Č`ŧēYSÎ’é Üær‘ķʁHĮvĐTī8GĻŌē Ô^ũØÖoėz4?|æėč d;w(ãfvôð‡>öøöÞÍD‰†]ls!“–ÔÉĪå·kÄŠ‘Þ}ßöxnð™eu†-ķqĐP–üĀkĖL\ÏĖÄR+š·ÅĖÎ4ĩķ„Đõ_ęÝ?øõÁ=.0\.ßb*[b™ØL0Îō™Ëį.ŒĢŠT …ôXÖĪÓãĸĆVÕ·…ÐŒ †Ðøû$įӏ„ŠūS ūTĻu„‚ÏßW)jbÐFĀyޟĒūŦĐjø”?/?^æóōãY?<~ 7ôÆŋHģšžĶĄ0üëôE>ŒŊ~mmŨĩl™‚ œY$B‚X  : Pl8YeĨ[ŋX1þâ%ņGxĪæhÎ.ļð―8yrúžÏ9}ōžWĮ0LÓlÜUģŲŦešÕĨEmzōú㇟ĩęyŨÎfūdfŠŌ°,Ā4ØbYVĨRQUž‰ÝÞÞÞÜ܀ŊårŌ ÝÐōōâÛbēڒžëå{.w<ņ͐Ūk ÐųÃŅu°‚vØE öƒS þŠïŦāˆ@tÁe ĸÞđp°‰ļė€ģÓˆó.Î^0xæÓ™k5LËnÔŦ)§ÆŌó·åkĨÂč`x˜bY‘ÏÏLũøý|dęĩĒ[PÄvė͗OŧÃXl,#BdbŠXW3 öðq?ĮĮú{ĢkZËU§Đ‘úy!>Šæ‹šï;đT$)ø—šHËû§ūgŽdcŋu2ĩ°yļ-ĄNšð4ÕӉmŨNö–1ðŽ?.Õkōlbl}įüŌ1ĪgĢ8Eâ-Ė]4=eo=Ép;5ÛUø‘áĨ­’åØA Á'f,―wîĀ&fvOmïŽģž]ŠČyf”ˆ AM–UĮ6ÎgđÁ‚ßÚ/fäå“ãÔ`§ļīqR=ā(šlķJïĈ°ę~ŧRåMķŊ+Âr,ËlTLM~…‘Ųšú7ĀŨÕY§hgÂ―øJáäęĘýž6ŨŨ3ÄÅ9šTK/冚ŧY1óUwĨô“ÐÎ ąh8"æ=ßYMttôÅã<îZؐ=ÏnOf{~óÃęócšƒĖ <Ļöt^Ðų‚"č°ĸbÖęYĪ‚čýÓKŒŒ.4 /å< 4ļ―uúûŦúc#şã›m(–đeŲÐ ššŠŨÕŊÞVGÛōzð~­øŊôÁzDDÎĖΔR@ÑR‚QŽŅ~š^<ŋyôðöŲSýþ]1ŧÓZw$û9g|ðŲlķÛ-* ‚ęzgÆåøíû‡7ŸŸžüøāõõã/›·.šāC§ądŋ"Ë)Ž@7œˆ ë>‚+|ŨƑ}<čq>ņŪ>+>0ÆėÓXuŅÂUw+}P{đ>ũ­ŊGô‘Ķ”ĒßÕ1>ŌÜēBÛ0Í ÕIŅ ­K›Z+ÎØSmđŸŅŅÆ–Š”TÛ "qqïüōęÆiéR™ZŽþįđūõZš[‰Z™žXĘRĘ%9ë# Vhʈ5ŠV·―˜k‰N ĻïĻ5ŲõqO%PĪNspŒÚ”wĪlnSēüãAÛÐRËķZ Ãŋ`jаˆĐĻÖī0p­DÄņ„ĶÁBđ@- TRJZéXZÍQŒãÂÆÛ@”kĩ]žŪ6”‚–ã0üp š ")ā qÞĮ`tȕėðąo• iþ5‘6ōōþųÅŦŊÉåÓÔPüðüðčœŸŽG/pŽĖ ÀáŲæ‚0ÞÎxlØþýZÁĖxĶĘEŽÜ/!™ÞÝû…õD}øh.uWØéúŽķãóD}ÎÐۈĄčķóaĀÕÖðöÏΰNó ŽKu #áĢ\)ĸēáųØo€§ Ĩ{Īāð.<îAŠĄ2ĀþóûēÛM"ŠÂ(o­Voô ŠrĄ>€ü õĒcĐC[ĖÅŽöó· 6q‡ėėģĸYs [ūFí–ķRē›#6bš#2]YŒ;'9:’%Ÿč”Įģ—‚ģn’5RUŪ”nčšOFėåėLX+6ïž{ĩíÆ+f„‹ayɇüãĩÞtWjģz{öōÓė’―Öx7ŦwŊÏ>Ï[ūÞøžŧ ûa#ŒÁåŠ|Ņâㅏ”sÚÞl]ž[æ~aRžŽd1ӅÝĕÐá†0…#UäëŲÍQîĸŽn]ƒ_ÕôÁčBĖØ-æÄĢmˆą› ŸŅC~ũũá}däøõg]˜AĖ5íxW>}Œ_―xóeyŧ- ―ũ­ÄiCîOЌŋ?ÚåŲŠūÏjÂÃÂúģOĐc(å{I4Į„UųŒøóƒ”šm[4ŋLqŪV+ ī Ąĩc;ÉzԆt: Áp<ģlâhŌ,ÄПnHĘ=Ú§4”$ĪDī―™™˜•ÂĄIēMúøÄs˜9ņÛķFÍg/ą>>ūŽÚŸÉÄï„ÛŦŋoþhåĀ{ŋšþuĮoÔëËËÖqų8ú(ŒŠÞN°„Oú›>õ ũŲōé3͉”­ ’ŧĢ›„Cų( •ũ‡ČÍÝÍívMčd>z=5þã4ÓÂĢ|Ð{ïO a8}|ZD>WG>_8ûø ‡ųdOχō5M3›Íæģđ/Žčéd:ïd1_pDO&“ŲtķX,šÅ.m>žeģ$„‡4 KÅc+ z0D7Ctāå֘N§Ø ŅdZ’Î8M&BZâpg^lå,wÐ_64ÍđjF )t=ßŌĮ‡Ļ"KJ>zäC!inr„OąJ>8ÃĮÐ>hGÉĮ#6[q‡„ðY6 …ߚyÁg:Į…gėðÁ ߇NÛSøLöðÁÉ:僔Ëû“dD>I ķ|Úb4ŋāģŽïO†Ö|PĀžhšÉŨ{>Þ7™ôņIÒ†uøāČ#œåƒ` §ņQS;œÎ,ã˄ܟÐ.ųā‘žÃ|―|8žÆ§|2A?âƒ}x2ŅüD8ö QÓÞw’>ØešGu}ŽóK'šþu&ŦÞ*#R9??ïZĮų ý|Jz%Ï‡ãSóAÂ§neīæĢį ų ø ÚÃųÄœ‡‡ÆÓĮ'ģņüg|° ™#ũĮæOĘyN>Čč7;ul AМû/•ėSņbĶ„ výÕĖ,ô҇WÔ < ŽĀjŽŦ°ZŦāūŧIš;û õa{įÕĩŋý]Î93ŧ‰&–D-1É5đIԄó,Qcą$ąĒ‰―""Jl`C "ˆÅBSQPzTÞË4ę0sĘ{ΌÃkōËZ7ũ]ŋ›˜Üýųƒĩũæ[ž}fÎ3{‘xašĶ„Ķ9<ŦŽĐ()ŊįxáϖFŅĒļŋšéC€@0BŽ–€ĄpûĪóȑãCã+°Œ2ØF­U§\'[Ï}™ÓÁ)]„ŨÕ]<č6ÃÞ~ÁēÝåM4…~­ °™ņQų:q\Wšüó™ lNCƒÏęomoïār;*ØũØų0…6UįøïÛs=1ïîĨ3nΊÕĘ0—|ųð\{ûŲ #RJEC„1hŧwéðLQøÜ•W3kąļĪ―âwÚĸÂã§9k—Ï›ųý’°ûĨšúž―+gĖtØ™]ƒ…–ō€ý›ėÅjk=ŽÕQqmÕ!Þ{ÎG'įÆŊž5cáęÝYUZškšxÄsŨĄ3§Oîó:VĄÖŠŲ€@ VKø5a―*:Ā}åŪk*ŽĒu•.óŋ›ïž)(<úÖ­ÐŦŪ4cyîĩÓū[z"*)ö~ėĮE[Å0ōŌ'q.î›fOāīmØĨKŪk—Þ/ĩ0Óû­Xæ°vĮ•ŒÜŌ[7/ŸŋÚ -ĸÉá‹wČÉÍŧxf—ʍęŲ_žm!§­―čšŊÍt9žķîþýs5-Ōų·.^īx˞ w\óŲãžØiýNßĨ7•åvu[4}ԜUŧŊ]OĻŦS‡x9ÍtrÏɍšīxÎō›yufTËĨC[ūsÜû(-ėī㎉‘ų*ĢŋuōčęUK'Oþæt°øĻAŋ%ģ?܌ƒWBö/Û ÐcN‘đîûïŨmũώK<æķx‰Cą–Âíĩ;<œæMŸdŋčLØå@o·ï=B5ĩ‰ō9īŠŌÓþ7KÔ,ąÚ߄@Ž– ˆyæX`&Ão]ŽLā^ąšøāQyInčĐ­―ĩ…û<ýŠ4fkG>―åŨ­ĨėNtPđ"9•ŋ―,;!čði•ėËĻž„ĀĢΠŠčø•LHaËĩëųķģbîDÎėĮ4+›_ē2<ú’ÍP‹úø”:=ĸ ĀįĖ>Ģ6&”'nĸŠnrúÝŽJ™ 3zķUÛ,Þð~o\_à vŲāôz7jˆđ ûĮžfÁē< ˆÕ~!@#ēRÓăąßŊ;ž++Ð=zõÂmšJ―pš@%Sæm.@I}S­š Žo_.Ý<ÍúýW{ô0@8–éõÃŽ)˜UDlŸ7aÚL· ),@•…ÅĐqß=ŲëR65%õ,Á3MĩGú† )įþ‰MîûÛyÔäDĮ§éŦ(mõžl5kD/ÚēWŋūŒĪÕôížqæøáCÞ ôęzŒÚ+2WĖûfđĮéEuŠú†j%€ÜÝåÓ§ŽØĻ .ŋŪBgĐŪýė8―?dÐkƒô Ûˆ­‹g8°GũÎč0ÔV—ĩŠ*óO3~ڑļbЊkĻi0âõ\ÏOėW.œõÎ[oũ{―;`k[XžÂ21Df˜†áƒ/<Š‘™››ąC„đÖ‡€”,ŽĒĩžđ #Šæ(Ėē ;6{Åôwŧ ÓæÝ†vƒéœ0Ðé9–į  čYkIŅú挐Ô\K đæiT@ēÐó•îfŠ á›l’*sĘzbmMM5ßáf’ΗhÄéYŽ‚cžïÜkðĒÕ[_·0Eué3åGpbýÎ#~Xĩī—Ņr™E·þXϊ‰HĖāx―$\ōzŪc9Žįi" ōĒļūC>_ŋqÅCŠ‘ũ{―o{[>@'Sœø[žį IeĒB"$7“ãV'Î äTK^ Öä{ŨC‚ϝųųb‰Š Bx įčo îFƒėS[ö^ËĘLŋŊļŋüÞÐÞ U‘ž‘Į›Y`}sqeU‹žSDDƒ’~ōĸėÏūĘkáQ%üKģŨ:[č ÔÍš·Æ~Ó€ōōúĒ2}—.fõĨ•ŠFÁä™ršOIˆ~\vžXøøIIBdäü Ŋ5eû_ÎT y{H7 žFîvÛëåôÕĒ =Ā†Æ‚8Đ//Hcūsï!oũĢÕ%đO+ĖšvŌ77äWĐ^4xÄŧÝøōžĮÅõ]ŧpŠúōĩ!'åˆY&OäxŽ7T”J íœė―ą#š`Pĸ$Ŧķ‘ïÖEŪŪŽT4hE―,,֔("v‡ē,§Ũ4ä\ŋ[ÛσĒ<X-—LQ|hëwßÎY°`ÁüđßÞ)P@ŽXúŋC_õ•ŨÁ­ï0UîKėĮOš<ĮqG^cŸö\3ËúŅ9wŧqãÆOœķÔÅKĨÃH˜1– đ%%N9„0§rsķŸ4ÁnâėȇSŊ^öÅāîÃĶút&ŦXí0Éîóņ_͚9ĄPØhÎ|cōĻųĻ…“ÞéÍJžÍÉzą›8M8ĸ―]­fîÝéÜŧĄbĸ֟8ŧŊ>Nr:ˆ!ĶÐÞ°%NÞ럁'fū';īaĄÝįvūœy(ôžEŋQžøŌ ï^3Ïnü„‰ßĖ8v-G i9Ïë9=oØŦ€9%—S!BĀY V?bĒÓaïmƒäĐÓ'Œ?aúĮûO52™Œa+p‚Áę9VØî” ë:lėũ_Ž­-ŽY0máėZDcðĮ@ (/0ĨOs“ž'9đīĒē 7#)ëqmCƒBĐnR7<ÉLŒ‘ūčófRZnUBÓØŽĻ*Jđ{7#'·ūAYYV &?-­RŠTÕeOӒ’•TĐuYwcoF„GÄÞ―_ZÕ Q+JUSģ:?7ãÖÍψ7ï&Ī–Ö(”J…„RQ_ó$;%įi™RZ‘ ÖU‹SÓ TÕ wb#ŪD܎‹ņę@Ïũ§Ķ•ŠU5eŲIIYųÅĒbcRcSSUanÜ훑7bï&ä•6ˆ}5å…yą1ŅQ‘Qwâ<)ĐPŠŠē2’SӋŦęDĩųb•Œ‡âU uy9IÉy5õŌEhÖ(ŸäĪތŽŒŠŠ‰ON/ŦĐS6Ôä%'ĨįæŨ4(•õ5đYĐIɏkëÅXumųÓŧą11· Ë û"ČãfJ•ZóKDS‰‹j•)B)ÍLÃaX”~ЌQbŪJĐ|–aŦĪ)ēã·ĶķFĪëFT*ąĪŠcÚQPÝØÜ\“9vðËžōö°W€ČKCWš‡*››Å ÆBŋŪ#bl­RþöĒQĄZm”`ܕ84)ïĻĻėČ3KuþÏ`ĨĐąYąZÂ_ĨJQ}ĸbðąc>ÞÞ>Gųžŧ“^ŦŌρX-áeSk[ŧ‰ķæF99Äj X-@ Ŧ%ÂšĶ)  /–åôz=ų‡đŸÕh4ū؁ðĒ pĸæ›Ã,--X–%VûŨ!ĪPĻšvíŌ·ožį@x1Ā—””ŠT*ņAôäTûwB(ZmϞ=8ŽáŀĶ)Ņg bĩ€@ žðüŋļ%Éãf<Ų‹@ ˆÕX-@ Ŧ%bĩ@ VK Äj @Ž–@ ˆÕąZ@ Ŧ%@Æ BūŠ2JúMÁĸ†FąĸY "Ø1øw/ÚĸŨž|.›%~yU Äjĸ†@ĀslKSĢFÓØŌĶĢhšmRĪ%Ķ*ī‚ðÏīYĄ―8/­Ž^ zÞ1EQ#MeņƒœV€ŋĢ­ŠÏ‡æŨ·SÏųĐX§Ą '1§T@Ø8āĂŋkkiã„oSl[CJRZĢ zmk;û_äcóÝŨŌÜÔĶŒŋ„j[›uŽá'ė8WŦ%Ð SūÄŅaå*g‡ų‡Ēē5ŧ7ļĶkƒáŸčī jđxb{h|6Ēi`€įtj•RÏ 4ÍTßYuėj‹ð/ï$ĢØčc;Öļ|˜uŊHŲŽžË hĶāīįĘãŨu - N\oþ>ymõŲÛW·bæwßĻPĖRäî\ģĄR/ˏ<ää~Kĸ;ūw EÝ lÖþÆŦŒu—žV­r=Ŧ…BTciÜæ5Î*ŽÆð&īĻTÍíÄj ĸq DíuOŦeý,_6îÍķíK7d7i{ũ‡(@ˆ)ŠĄž–Ķ Cc FšĄE0BÆRm€úåĨ<Š’ę@SEm,JáŽhŒ1Q”Ž?:C ˆŦböQƎĩ;ēÛhĐHwĄW7†FRYQq'”Đ=„ÏÝåžæa­ræöƒû=|1ž+'U2j6t 芰qÐadÚM‰ ÆÆúâHŠlQ1ú~ņíī^rƒĒ1FÜ#ę~^‰Ī͘b!ÜIšs}G~6Ëv˜ĀōíŒÅĄi*ķÆ^ÃõÆâīáŠSÆĘÏ^#Æ üē—ņÂKI‘˜)C<‚ĸŊļTþŠŽQ cR"ĩ‘0mSœI wT6]1ƒB ,…J+H&ƒA^ŪÁũōÅð<ŊG]íÆ}p3ØëôƒJ†iōwņĻėýÉ[}Ė9ŅR ã6;ŪĪ( Ģ_îTŌÖņÎĔ4`Œ1⟠#Cðģ7-‚XļŨåBb‘˜@Žö€<Ųķwŋ7ŽF}žpÞÂAĻĐąM/ð’ãpíʐÝk>ĩĩ™ēîHI `æģÕĘÆöãOfŧ]LÓtIâ•đcĮŠ>Ũ#kixēuÖT[ëϖy_oGīɝqkUæ‰}=Üį,ÞŠĪpŌŃ6Öķ_|ŧ$ĢšE&ʼna_ŽģĩķŧÔ7Je2ĪOÝigkõõ—ÛsŦšFŦåtũbüO]Ø?l>ËëÕÁŦ­ŽmĮÍvžšV+Eð€˛ĒZeIšŧãd[›9?n+Ðī͆žŲëĮËd‰ëķėŪÔ2šŠLŊ́ĩʂÔīFŽ*Š tņÚåžpÔGïŧå)ęŠ―][ŲØŽ\ŋrË*ō6L™ŽAlĨ3|&5Tä')j1ÆÚōĖíËūƒ?Ÿ°­\G)‹îoøjĒ­ĩÝŪ°T’5%ōō<ęí6zô‡Ë·Ss h­ótœkõé§3–W·34j9å1ŅÖÖjþÖĖšvŒ€ŒQŌekņrŒ™ž/(EÚl‰ôwûčkĮUËŨ/Y“XÁ5å†ŧė;âđuÅØĩG+›u·NyL°ĩĩYčšUßNaÜ\x݂m­'ø9 1ēGa‡<―ïr™3ęŸãýŪÞ>ĩgíčþávė) [oø{ˆoŒÎ;ō5˜ŲÛū.‡ü|fÛ}l―āvbÜē9Ó>üðƒŸï12þ†·ËđĪ*CeFųļ&ËdtNÄ™éŸØŠoļÐôĒēÔë~'ƒ<\Ö/]ŠcDaÄj ĸIMĐ r"ĒBvm9ÁũĸlposŅ|)ČĮzüĢ:|úÍĘó―Ēöf}:4lÕøŌŪ—Ú°þė†i, >{fŌ‡oŅššc›+ޚptuuØÞÐûeM=ûĢk›2&ðčEåÐV,Vß rÜuoۉ3ó°›·ht,ę<ÐýHĀa§ …>đ’;į~\6Įýø†ï*žB͇ }Ïæ7æŧï°~ŦáĖð°lĘÖýĮĸēûDYkKĖą>‡Ÿ :þŠúķïĨӆ'1Ãþ#FZa5qĨÃäa1Gķy5z?ã<š_7Ý)ĨN‡ņ_d(9[ŧ~ŚDųįþgOõiš?gÕ Āīފŧ\Ü MŠ:éu0<ŊēþIb\Þ#……YÛ­Ļ›MžĄm)ƌđ_Ý Š+LHIi€°þК=õ_ošč7j@ŊƒFŽ~gØÄĐӝøëEaÄj ĸI0B…EđQ·bZ†ÛųŽíoŽ9ķ1+6LQ˜ģnÅÚØlZSPÁB3ķ―bûĘuÁąĸ—·€"Ųú@Û{f"ww‡āDqdqX`qY<BЂmŨāîēøÂâî‚B€ļËdŽ―_ũdÂåî·ûÞûî―ėýæĸ#3%įœ:]=sĻŠžóϧ˜Ü*Ü ;š:~AZĐJ2ßÜŧõâÂÞĐsķžÎÂ+ĘŠ9Š - AH}[ŽKžŲØ%ïÉóÜĒ·Ŧæ$n<}ëeæ'5›Â;WÏųéȕr‘pĖëė7@ËØÞ1MŧõęÚ;’ÐhD" 'SĸF&Ĩ%}zþ˜–ÜŊUX+ScĨŠŠ<―,§ęíĨäIģ.?ÉRjĻÚŊ ĮƒĶķŪŪŪNÞAîķčýOåÝÕ,ÐĢûðÉN™Ïó*FZĘēŸ―Ŧ0;l—Ŧïô1―ÁGŊPLQzëĘ­Œ—U―Ā7wŊ^~Í-&ÎUĘōšs_–Ũ„öH^4u@LlĮa˜Ę[įóėš$4ņņ‰í7ķąLÂþÁđ ŒJyEÖMŽĩ`f˜·gãðFLáËÜĖŽ‡'ķ$.ؗŸI–W*)‚īlÓkóŌ„ķ-#ímØr…ŌĶqŦö’ÜÔÄÉÛIØôËGŠō?ý4yōþ›Ī"ģ@Đ–É ,5"–Íœđųä[ [ŽåĐĮŌ‰€Q›5ï12ÆÅâx€ŦķlÚmڄ&ŊŪĐĖû8ō䃷蚷ÅĘę7kĘģŊLXļ-ŋTQ­ĒY‚ ý>ií°øČķ<LJŪXųCۘĶæ2Ð<žø4ŋčEJâ”cO2_―úH2 ÎĄUęæU]bÚGzzjÔÔÞÉÆĖÍÓÛĮËæ9ā˃žcĖž#§ŒlÛĒ}߁ÂaxqņāŌqüw­ų†Q2žäqK,Ú'YŽ@ā4-Ģ0ÄÓDeĨJ°ö`ÄÖɧ‰Ŧ\†Ã,Čmí˜Ōr% @ _YUÎIĨ0ĮjÉg @—gŋLWaúí ȅ?€cXBPĄ…bXP҆åP `”Ųđ€‘0*ÞmڒÓG–›ÞØ5tÅIA…,@†ģZþåāh’ŨÐą4A1,Ï1Zĩše҇‚ÕũÎ]}ôėáåûÞ1ÍTį|}ëņ7å™éũïž8•ķõĖĢ·Z1—JŒm}; ėōāÂŊw§ßŧq1ŦĻĻûøō,ĢVĐŽxĻIŨ^á&Ï]Ëxöčäĩt-ÓZâÃÛ·ž―đóúÓąŧŊýĮ€đÏæ.žžļtïĨ—Ī?Ŧå9ÞØÖ,ÉÝīlÅûjŠgIĨZÉžZĐUaV]c{“W.üvãŅÓŨŸ|Čį?8yŠĪIŠáAËΑîß°|ӎ3S0Ëø(/+ŠÐhJ ÝĪFĨÖČė;ƚn]·pˎM‰?ĸfÕ§·ŧĨel'ũĒ뒐AҁmâĄü<ۍl`’Ķiĩšŋ߀ІäąDĻkhĀfÐԞå§į:=qÉ^ óåZ‰c(―ŊRkyÐøfaû'ųyÝúÔ%ÛŠĖ‚âzÞS  1Áa݇ŲË2=s;#ýÖÍ[éĪˆÄ1$[STðþŅýŦ/”Ų/;ũūóø!Ū…œ2eLōšŠ’Däĩ‚dy€ãøĶ=†ÛJޜ:{'#ý捛YĮf[6ŧĩ}Ï̌—wŊŸËŪa8J˜‚žaUÝõōŒJEjžËøaÞĖƒîžxxûô­W jj(’E(šf„ {{ó_ė>yųEé‹c-ŋŧüIƒ~îĶļ]@“VžčĮO…ĶÞ]gö0Qđ­·ˆŦĨÔĖÎÛ/ØÅÁ1""Ę TįV…ÅY0Ž“āĖý‚›w71· ņkÜy`{WSœ‡ĨŪžūÂīÉÍ―… Ļ“„%Ūžþž^înÛ412ũųŪýÝ{/ㆌ°FY–Ŋ=̐šzųy ZæNÞū^îŽVÁÛļųE*™™Kpãāö11ūÆLvA1„Ų„øZ[Z8ŧyûļÚK0ĖÁÃ/4ÔÏãrģēĘ)4nėÄnĄļđgũĻВœė %eãāįïgņXÔÔ),ÐŦžļ&°[ŸŪąy^Ōž}\\ŧXs3Û›žđ{3ĪûÁÎ.ŪÞūūîâ’ÜØÆ·{dãâœũJZ°ãëíßÞĮQų1§ÄL‚CŲŲŲ{ų…ļژ#+?_k‘ļxøxÛ[đÅķnVžĸĄF˛šų4ōöpuó ôu0ÂaK;§€ +3ŊFÁ&œ–†åÁĄ^>ķ~f˜ĸb?aáéíįä`s,dėØ1Šeuþ)é7.al”' Ë[‡øhŦ•A=ÐÅĮŨÏNļuúr[W?ĸ;SL*Þ*w[T*ũņóũrģõljĒ-G―bĶŽęŨÔÏÏÍÎT“Ÿũ>ŋÄ.ĶįŒ!1ħQ°”QsĻe€ŋ+ôŊ?Ž… ĻēēRôYŪÛ>þuTðíAĨR=žņî]EQÚŋ’fyŽ%ęŠ)îõ(]áø:0BĮĮ jz°4YŦÉŲō•AÔgĢĄ%ękœNMKĮmG‚ÕuŅdŸ?'˜zŧ ýĨWg‡øŸ)ĄÎą Iņu ô—ÅčÆÐ] C‰„(FŠãsBg} MŅŲŨķõę=qÓęųQ­BĒgîŅ2úąŋÐbôZZąE’øĒĘP‚8]ŨNÖķtýUęoÍrĸÐŌðJëĄxsvhĸ‘+W­îÚ{NžBCRŽ^å+;ĸāMÔÎ#6ŨkÕ^‹NŸfčŠíĨëEJĸÁõ“ų%(†kø!Īôō|}Ģ~ŽÞđúՁ­kĄąT7û…ūķ‹āLŊ^R?uĸÐ4ýúõ›WŊ^+•5ĸo €ßpÏÎþ(•JÜÜ\Y– øw„čŠÜ;ũ–k,―BĒZKAžĸžīåO=ĘúT*ĩóŽˆhf%…đĸ„_†gVÖ{žį]\œ(Š6œÕð/…žāŋ uđû<‡šŧīëÖg舥ņþį8 }EōðŨþ9oøxZ6‹‰2bDï.m,ĨÐ7gusō§2šÄ4p ð=”~ðÁĩŋŅ9ŠęÉÃĮeÄ_ʁujßŌuAĸŽ`i‚ éúļÆÐ4EQš'BœĨĮ/Ÿ=ĘŊT~ŽŽ§%öO‚āg"ˆēw_åqøŲ Š þˆZAÜËëžúf~@ AĸĀ?r”úųƒĮÅJöļé0Čæ<ŧđgë–kyB­ĸG‚ĨVT*ĩ<6hä••å ‚AˆŪÉZš0=̊Gĸ4†‚ĄŪV( ðÛXĩRZeĩBõuœÂpüųū%3֟"8ø/›ƒ1°&uþ”ģÞ@Šk@ÁęĖéSWÜÉTĒô‡ŠSU­ EąĖ­?OÝy‘ z7ŌũĪ$m:Kó0ðw€ZUĨTQ Ö'#°•yëĶMŧ\LýŅ €\‘umdܰ+W/g|*āCĻý[Á@ƒ€Ē˜ˆ:öF0jXEá†U=‰ëjåaĮá;gwn;ûP(õœ/8ģņ§Ī+Ų58 F„âPPŠKl‡kmĒ:›ušnĨĖ›}%—`āW2:N1߆jIÄzmŠ;‚ †ÅBQƒøŦ―6,Čé ŠõôõYĸ8Ž ‚ãï·­™°õƒJj'ĮDa†fœÃ:tj„€üg%ŽÎ‰vrˆÎŠ$0âÆuŋfcÛ^]ÛyXHXŽĢ>ĀŅĖ+ „Bšė”„)Ũ2˅FȞ·‘c(ΧĐÜp ï\ƒAážŨ˜ūĢžBAĻÕģRčÝCÄ*ĻŧđXÝė5üĻ|ĶhĻĨz€āZfLī†4īŒĄuĒY§SÐųŲÉ)ŠņzÎāĀÂē‚%‚4Rũk8BđŠ(KŅ$.íč‰i―Z NÔv‰ƒę„aX7ouõІPkĀ0Š)rÓũîÕfāØ5ĨGØĖ+{â:DGFĩO^ú;„ÂOÎoėßĩeTTÔn øðÄúĻČĻļïĮéH  ôÓû–Ĩ,InÕ"ĶûöŦĘÞßŲū4uõŠ…ƒGïŽÅo †AŊ.íÝąįpōøI3—üĘ$(Ģđwx~ŨØVņÓķåkA boZÕ32šõĪ‡…š:ĒL“ûdõ‰c{’&Lš0·”āï^õ(3óQ‘V°yïāĒĨVMŌíÄíŨŨOĶ-M;8Ļ_ũ˜Ū}Ýz˜:ĪG‹ČŽKO=F04ĸÉņqâĒĢc·\ĸ€~þĘA ]}ø—ĪØ6‘QíÚ_þĻF4E—% ĩöƒÆŸK/ÃpüÝŲMŦ6lZ•2ūUóˆåŧï35[Ï>ūréļĢ2Ë*/lšÕĒįČÏ Ū)ÍÎÎ+Â$ėÅuŋŽZģzþ˜ÖÍ:<ōļÅðʏ’{ÅEEv\þëcÁqšļi|ŧȈþ}ŋ/TՓ<€bģęI^VČ+?qÁÁjZ ƒÁ˃k'·ï?0ePŸÅÅŦƒ*ÞŪ]:ŠU‹Ļ_ö>aXQ’õĄ DЊ·wzWĩpÛqŌgũÃT‘}û‡îĒ"Zũ[z šQž˜ķļ]›Čþ&LNū›U0e‡ÖÎZĩ*ĐßQŸ( ëúþŪĒcŧõŧ’YŠáuáģ1ßũ(,Vþú@1ĒäÅĘĐsÖ,Û.ĶõÐU§ŪYŌ!Ēõwƒ7–20‘—ķĶtŠŽŽ―čUƒ"ėõ-ó7ėÚ=fH|ó–―.žĻQž>ŋúøÉ]“†ÏZķŧŠŪãʁ ”#îœ\ÛŠKτ·eĮ”yG čŧæl: zŧzŲžĒįįF$,yWJkJžÐM`zč3hM~ ‰qęÓûVŊßžĐkŸ~+n)?ޞß!*&nõ… FACĻ5ā? žĨtüŽÔ}iSĐGëŧ[üéÞņ‘f7ïŸī%mÕĀ^%éšNØl7oûæuSbžōn·äڜÍ;EƒŧUŽČyūwÏŋğ’â―HČA\ÚÄDÆDv™>%ʈgyžgÎ!ļu _ÞC1|õĮũ)&ÎJ’ÜH;^]õüÐØG§íāų.aö‘žE·ĘÃmÜÛúļt0rôđęô=c~đ46mïxĖ„ŲŧH+z|vųķSF'F;æ=8―ráÆØ>“z5âÆõï÷ý~Dũs;6•ęËÆŋc{î?ēĢS€m]>ĢÜo+§&JŸūrãÆ•Đ!NČÞĪWėUĪnÞ9ą)•Øsâ“rZ]ühåŌåO%1Ƀ\ũ-Úô‘3ðņlÛiLr‚Ģ'ũÛėÐ0̜Ý.TöęĘŲ§oy)zqsýšÖ]īüļãče–(ž6yfuŦĄ;W<ąbÉĨŒ’·ŋïžĩ!oÔŌīÉ}}Ŧō äs2?DÝļpģļœéē―ë—m{aēhΠû;·]~RęÄņõōņ·öðcōũąþ0=úõčĢ3:ï^ī%†Ę^ nžGQāܒÅû°MŧvęÐЧāXē7oíî•Ó‰cîä‘yũķŽZ}}âę- \O=úäS53ũŽß’Žôœ(ýxmäī}ņÓŨĖíäđxöÚ7yŸ–NžŽ7ģ=uōïsĶ_ĘŌā@Íų›ž1!ģĶ{0wxĘ5>qÆĪŌß·Ÿ~U•skÛÔíO–îÝŨĮäN‚Ã<‚||dÞÏûBû$ô.üå§#ï°>Ž†MŌ·― fx‹‚(‹ēÎdcį/m \ŋjgYEÁlj3Œ[ßšt܅äÄß Íšũncį1ih?k&7eüÎ?nûÎī čLōē5öņöŅkn:í‡íØq3ą‰ÛSzïYžčæŧ øïžķEþæāPniôێuYóßW)4dõŧ맊Ķ1ã~ˆģ†FNÏëbŨ}ŅqršQāōŠôœÂwkįÏОÍxmdŽaiMŏ›?8Ū-íļîx§— §‡c>ïčïRšü"4·wqī”û{đÚ)Kr0 YK~é`WqÛå!>ýĘ‘Šžž sĶ’Ĩęjua9ĮąˆÔÂ=Ā4ĮË?ČŨýÔĄ#ųųë™M•ķ BŒ;ēļ ‹R‡DqD•ÄۍNÕŊݙĘkám҇ũË8[|óΑEvëu~õŠĐÓ?%Λä Zšm/WtéôːūkãZŅ €ōÅŋåģÝŒnä;OþåHßįyUįßgRÚĖ~Ô― céi+uóĩ°Ŋp ô3HÍ―Ųsķäeåãmô‹Ķ(4Ósōâ)ÝÛßÉÚŭ+Íy‘›ųVĢÚ1ý–ũ†Ļ,-{‘ý6°Wž‘RĀŠŲū{u$õžP„‚°hÓkÉēiMáklŽTŠ”<ÖR:˜;øÚ:đšúļŲ}АAý6OëĢž],“üVMˆáZ †ũî6-ĐlÔô™î.Ëqâö„åRjûæUŊ+‰O (…é‡§ÏøEŒüŪY̐vož…á žĶxŧ Ÿ’gõ 2}qôBö§ũ‡Ũ/””dŋĻö/øôęÁÕ …jSá%õÓÜâ‚üRڋąhžuËî™É)ļe\ΧIĻ•*-§‹|*E0žÛŧ}û6žĪ PeEŲ‡gÏnæ*Ï_đ|5W ÐZ=—‚VM‘ŸILėƒ;}xûėé§/_Ý―~5ŧ‚ ~vųČËĶ%.đō8ugĩuÐhiFLtfHĨĢ>ījRËęxŽÃÍžĻs·^š]ČņĪāĄØĮąjEëÕ4†ŌŨöí>|ų‰šAÍpĐnãĖëþÁ4Aįæ|xøøÉÍũĨGŪÜóî2Ŧ9=ôĮ)‰•Øŧ;Xð ĢU+I]üōnŨÁŋâԙso^eœŋúHc0bløíóGžfdÞ―~û]ą†xJļ•žŽĸA)8,ŽĒĄÕZï1Ü y}îŌÃ7ŊîÜŧŸAƒ0­UŠk§ˆĶ•"Ŋdã`q!eų…§ïõOāŠĖôuģ—íÜąjÅîãšwöh>xDã›įŽ>y™yįúŽ% r*ĩļ –ŧ4ØÍ|ũâäŧ7Ĩl9ëaŽF+ō!ðrŨðŽ]=Ŋœ<ņúõíŦWóŠ5 ø‹WÏÃ2TĐx+ũïGÆTLĢöíšôjCW+ĀŲ= ļqxï˜ĶFŠŌr-oëØ8$ĪmTDuŅĮŠBæčāœuįb5oäa#ƒíÂ’Ķ wĄķžÞVEJCž1ݗœãąĀ° VQŲųųy:ŧxúú{ËØÜÞÝÏÏŨÁҧ{tĒ$§ Rcnãč„ōzŠŋæ––6jŲĐOtĻķ$§°Rcaëāį`néāíäd.ãHnåāébkd$·ö pwķĀĨrŋ wOWŪæCvvĐĖ=rJÂ`;)ĖÕÅ,;ߍ}Ę ōhØ$ qXD\‡nČûė!Žærgw‘B‚JÜžýƒC|Œ%ļ―§w ‹[`·6A…ģĘĪĩƒŋŋ·―™•Ģ_―č&üB Ü .ÍÍLj0đ―§‹ č6båîïęlZV^Úéûá―ÚĒ ÔŠ“5ĒÉ+,å1Sßā@g[wuÂÝ„Ĩ#:IÉēž"eHlïņßw“@œ™‡Ŋ›•)Jóēč˜Dý>ŋÄlƒû™É€þc0p 8(šŦĢ5_HĒ!MĢÕþqUq`ŲĪÄĩ'õÝ4ĐKާë(>ƒbjÛBKÖģ†XŠĻ·ųĩQ§F|%ÃčéDˆ=U—ŒÏÖįķ bâ[ßūbTÁŅäÓ#č} ĪūPŊ^/Ž{§Šĸ­PK_{U'I“„8bõÁŨžŨÝ.ęK^^Šoaę}cĐ/tëæ°ö­"įýVoÞôClDTlRŽŠĄ)’kĀQ@Ru–ūœ^wãjŦd­+éŧDR…Ïēz—ę'–gës,C|žqô†Ī_O nrļ:qúKa‚­ŧ}ĸ@gA‹.ý9 |› sşēk`s'kã€ŋ 9Fųô֝wđeļ•[Tl™ļķ4ð­ÁžeA[7_'ŦŋgĸzŽĸĸ7cðÏõDĐŋ<Čĸb^ŋŪðįþŧĀCˆ‰@Ū8tÔðþ=Ē­ŋÁ8k€!ÔĖāZ*eq/Ŋ!ĸâĐϑ$ņŋ•Ï1Ī–bDgęRéy–|õøņûrÖUĸ ƒJQöčYĶ‚äĸJPĢI‚æ8ā/Hþ[ƒ˜™ðōéãüjj 4þSZąĀģīļĸwþgÉ0õÄß 0„ZJ]YYŀŽĘYŋ`áåôJ ƒþ”8R~\·`á•įUĻ(üĸ Œ―ŧļa‚‹<†j…REŠK\måķÄЛ_)PôkûœJQĨÖŌ0†Š_ݛąxÓ ø§$ 0Ŋ=ąnáŪ 9ý“ŦėW•+Iüęē1ĻjýŒÉWß+PĪZ(\_ CāŨīÃÁ^ýüČč);+HþÛ`€!Ô Ķ―ã"tĐîšów? kJ—įŠœŠÂ}K"N†!“/―ŽÄp<óĖÆ5›ķŪZøcŦŅŋė@ð7†Pk(†Œ­?Ï\qmĖüëŨĖ qĶÖOzĐXūqįķNîÏœZĖāeÏûyöB›Ž? h#ßģ6ĩīiŲą[S·ĀYszŧU=đv!Ŋ€‚ļĘ {WŪ―Å˚3LsĸĀđÛy0XóúÁÂ‚ø—ŸnÜ/Ę™ŠgŨ~Ë+Ī!ŪęüÞëîpģį UÝ=pņaAöÝ“&ė5}Mō0ßë{ž.P€ĩ+Ya+ó.:VÃĻݐžé‘‚rí―kĮŪVÚÂåŨ~;ĸVbãëåÜū߈c›K@ĒâÝĐCŨâĮ$ú‘ŨŨŽž (Ļ˅°ömįį?>ņŧvĄ˜-ļwéðhöŽ!voŋōĪðݕIŧJ&ŽßÕÕ§lÅÆC5 ŪįäčwÎŋ|Scėƒģk|Ó{|ǧęęþã”6káÜía3ŨÚīĘÍũlÓđ™—[ßaƒãǜh ũãáZž=ĩýJ&7 æD0 ßīߜCû6rŊïĸý‘—§_€ĩGŋ13· "Kî_8óBč―3Æ­> ųyóöą!5“ŋ›üǚUÞ_ūxÉsĢķÓûŲíZ°å̊… ĄÖ€ŋ đ,SóčӋĶ?NéŨŪy@H'Yåï7&{ú|?rĪđōÓãũe4‚Å [š8ĪGûčöŪ4IA˜§ŧđeã09BKļwóuĐsšDÅúxšWŠŠu+fk7Ėú'Wĩ-,Ą•x·XŸ:§sTŒ \]Sþú]&ŲiH·°fÝzõÃjÔ Ęs ÞĒ• õâŌ­ËŊ‰^‘žwŪ^r?+nDWœĒA‘ĻÏÂ#ÐÔÁ' Äß Zî5ė§ÔA]Ú7 tWió)žãdÖÎÆÎūĄžĄ1mŅ}ÓōY#cmųКŠũEŲEEϷ͝qðũGŠ-AāôÕ0@3œ…ĸœå‹vičŠP—ļKũÎAį$Í9þ”‡;//G đH#W[s ‚Vŋ=š°xýÍ* þÕړįx#K›ō ‡æ-X–[Mg&Ð8ŧšúz9‰9Æ0kĘ/°―SF7ôî=zŠ•í‹įy Žâ‚û'l™Ņ·uó6F’Šj- ęü `€!Ô!R Ŋ(ŦĒ8@PŒ“1ĪÐĢ­ŪŪQ#2ĘÐÄČJhĄ†Ö-Íhš&YÅJÝ*`Œã8ČBÃÁ ,ķą AĨŪŽP@bÐĐf#…)J'ŒāPSVXĶĪ9™o2> ĻĪV’cyGŋ° ;hÁ éĶ͜ÚŅiãÂĐwjÚũ ’iЁĪĐjˆ1Į!ÔL†‰Ü 4 ԁgTZRĐU8Ã&ŒÎPœČ0€@4eįþÃø„åëwýŠBuēĨ[‹@ĮWEemÛ6j▟ýÎįûnvļ Wk)a<[Û+KWļräaRĢԐ 4IĐīzū(ˆÄXŽ0'.{øĐ”į9‘a@t•ŨĻI7mŲÓ9ũåđsũģÞf<Î|Ģå>?Ģ)ŽĪYARd Ēe5 ęė+ķ―Ŋ"d0n„a(ą0CwĶm|–UĀņpYaQfú“ëyŠ“įÏå+h@˜A™­Qąž#Ë+J^e<ýX]þāÎÅ"ÖĖۑúíðŽóŨ ARĐIJb=8&âúÎ5ŦvėMž―XfÕĩĩŧIhôœ œČÉH%Ï͎ŧĒJžÜoB­5ĶoÜĻ„Ĩä0 Aū6ÖR4ÚšįčGO7ĩB,ƒšnÚģ{nW_ŠĒÝZš;ĶCÓ­†ĶÎOjĨ­(ãq‹>Æû›Ð ÜvhrԘķ:ē)<€˜ MŲ2ĶŊ}eĨÂČĩéđC݌Zâ˜ļxnû@kģĀØ­§–†;‘yųe2\ŠÂ€žƒ’tØ―•-͛öī°đ“„fvӇu FcWkiNviÔÜe+û5Ēhģ ËS㞍Ë*ÔŅcÖ-šÞē†ēN]đqZkŧjĩɈy)m|ÎÂ0{óÎÓÎĶ$ŒæĶĶõoęXəÎ\ļķĢŋ}YQĩĐoŊEÓzâ ßnzĘÎy=ÉĒËf·ZčˆÐÞq&ũ‰…YÚÔ§óâÔDWœs ë:y`;#āĸrðߑ˜kHĖ1 éJ,CŅ,Xĸ3R†"ŽGq ÐÉB†!I<áļļ=§)ÁPŽ&i–`M,áŒg(ŠáGęŨ‡,C0œĮXšdxŊ–H0āƟÝ^>o5ÖĄĢōũOĻ€m;w{[ņLĢ„â E ū hAÅ1˜#„Q…@Ī&ĀpœŨ1 ÁgVčꀠ8ƒCÓĻ€ļZW1 ÕįYP|ýĖā8ČQ$Íã\|įxL"8š xŅshŠ`9 ÖĮŌ4+ÚĸØđ{Ũb0ãï›\r7‚Pč CÁÍĄÐĩôcí?Þ­Ý:8t„ÎvҐFĪNķÉCíó›ÄIÞāsDå[ ÁzŋQ’ŪkëHģ:ïŒTED·CŨ6šÛ1›ÖëföŪvŸÐúÎi^o‚Zß:S§Ąõ#Ûå3ĀsI-ĸpbŠ&Ŋ?ß^ž_ß?üāōöáþęĒÛw ĩ (%k;ļū{šy4õuŠ‘Î§H-PRŠ) ŽÅā/Ķ@j€ÔĐpŠj­•ãH)•RH-w6„°\.sÎŌcĖx‡/g1ãF!j+/-­øXY[[W]õąŽīŽŪĐ­8!æYA3ø{ZßĪÝtübT› |°\ØŌ.þ§ßā$Z”šÓD5E힉 |~$,O―ĸ°ĪĸŧÍo)Þ• þįUūMČ~_þ*áÝ9ņóîۓË;Ā?Ļ‘x?Ŧ”ø;Ŧ‡ÁS4ō›áfúLÐUV”WŠÕÕŽKļ|)é-þqõöÕÁĄë\g9lžŌ$ÃĀ–}Ä>`ýĄŸwü8gþî›Éĸx…āRîãKĮ‹D―’"$É/fž—eÃąĩY͘ŪÔęü› īhïÁ{ķ~5č‹yž!ûî[ãkÛĮšfj +%|”Ý…€Ï øoïžzÚ ï5ËíÚ[p0™ļƒÏïPÁÚ ’Ā`Œ qDØŅĄėüŠī!+lÍĶÜ*žRƒēw°Ž*ĐĮB‰Œ ô}îîûēj·™îķë';­3F#ĨbĻ äS―†˜Ffė>Lh –mÜč7 tR+ĪüvūHŽęŒ#eüą‚čĄōÂ4ōÁ§~`™˜ęXĶTw=ÆTríC þ2``@'%"HŪéîA­ā·· DJCt#Jm Ą‘R}Šâ ĻĄĶØĒ„TۖIíÔĘ`]! kð΃PF:[ŸænbŪgĘTHDBųïO(Ī"ęLÁÝ―*™ļÏŦāŪîÆXėĒļöY15”@ÂäXą™fā”r ĩ†R9Š= SÕsĨã:yøŽ˜2Čī{ʂŧ7Ž{ž[.WĢLK–…Đ>Ķ–Q ’  Ū–PSVĸaĘŠ–ĶOðþÃ{–_=•W%LI]^™tÃ%~‡P~ĸC!А‹E čŅÖÚÐwüĖCïtyûz\‹Gaļó|’ÔBĄ8I2!ó&&ðEŠŪéә&}ú4ĀķYĩsĸ$k&ÐBÄB*ÍĐӕZÏ…­olmmÝÏÚĘÄĖĻ• Õķ03fĒíå­uAþģ&M=rã=€ĸ&nՊEnNëũ\—|*@höĢ%%ŋÜšėRBĩĻ"qà gw—ENËCëÕ8Ōôfuðæ]›ÃĖķ]ū™§VĪÄĮd―L Xu/ŦQT‘°^žÐeÅæz5‰I?ōwrũ\ąåį6ųî͓Ä9§ü—ÄóŕŲwRr īTž ðœiįėęþWŲ{›q@hŦÆËäŧwßÖ€ݏtģģuņp[·ųž`/ô·wķst?ôø@YôÏÁAÁA›ÖĖ>aýŪĮ(Ŋžø.vrũp;|ýĐ ŋ|ër7gŧÕŧŦÅøGMO#JŠÚëîūhÖny•J4·‡9{z͟iũËÓ:ļ!yþʰ ĐķH}x|Ânïcy]áÎ GW§€õGkDŠžĖ‘ŧgÂ7ÞČÔĶŊŽYįđ-1ŋĐðîi;WO·…>A‡ëÚ€īþē}mV €ęþ/;nžhė]ōY,2?ýę/įģĶÝČŠp c–z{đ:- ?õPB^ģýJ/wĮï]ÖĪÕĻāŸ^°v™·įâe ŌüĻݎöÞŪ›n=|Íbq@/ÜĒį.ÎönžŪövßÔĩ*›_G]‰Éąáā=Ų}~V|⋮ˆ U’ŠXķōCüÆÍóĶ·ųįDiŲŦ°ĩnŽËCN5J5―kalݰÍā/ū>ÄÆD·ŸÞūÄÁÍÍŲuÍë1 āŽ+{]}ÜÛz_Œ-&xĸü„ßšmK<}‚nįõîÆŌĘzā—CfOŠUĒķŠs;7är1@JĒ"·ßËãą 8 eû­sĄÎ įĖrËŪj  I@íˆ}pĩRŒá*Á/{,ēwsņÞõkĄŪÔę|f8Nhá Á@?|”:Ŋ?smĮũ/<ĄĒð[ĀėSiņQž„ČgÅm ëGwũņc~ØzðĘōyƒŲ}ŋ=tãENZ4§*!§BBšâļį2ëŲ’ŸÚÔ&ĶUhėįÏ3pú™[Ũ—ÍĖķüöįYT0ûc\nĩB^wëisĕ'OÎï·Ō‡ÆOąuš9vŨŊŅ.ßY+ÍU­" l9&–œž‘vûČK6øŊ1ŊœÛˆÁm9˃ONZq2=3óōi/FKnčîGþŨŸĸšëĮûÖŠĶjˆyQ0ÆsŨÓģþoîßĐCðœŦŨ28SïÄeė ôā$&rGCBjü|(ĸâý<ðhtšT.hc~đýÔ{ī(:6úâeÛ^fgEx)nž|@úÃũí駞–žíÄųێöÎl[ŸĮōNI‰5Đ}õü :AėißĘūwŦL„~Č|ZaAïÏų°6üō†ČĮ™‰“ĐŧÏ?U’īÖæ B€ [ŠÅŽWÅgÐ$džjÉĶswŽŽü!3.^$ãn Ü;ŌĸhԃÓ'"ž|ŅØVk]ÉĘI  =ÛÉ­,Ō ™{3e_EÆšņáŨӒböëĮÁ ô€…åÛWïøa͉Ė™aöðî―—4~\í2Ó}ĸĐP7Ô=ü”isĶ ļ-ōú&§1 +)kęw0ú„GƍŧmjŲ‘­!e}ӒŸ1ËîÝLzß+mƒŅøó:įv|åķ|Æ0ƒė‡?G•šŸšâōâõĮĩĨIŽ&†]LšypÁ­Cŧy@;jrS+6ėŋĩzčÁféý––ūrĨŨĄó7.ýƂÖÐP.D€PASĩDH:­CŠÃîÆ$…Îcė?rMĒĄNĶnŠ-•ĒŒēįûFËÏF§fĪĮėwĐÛAįß G™ó—op˜8ļŠŲšÉR"˜īŨHoĶ5Ë ô™LÐCÏȜÍ12íÓGÍāŨÖE†­j2åÔËpÐ.Óïž5ØÅ†3ĩÉąčË6Ð71ëÃbŅÄíĩ§ÂV5›éŨ) UíŧÚãYčŧÉ?ú?Ė134ēb›™ģ @B!ƒ&æUUū, ZdŪÏÔĮŸ :FG%\ņÐÁΞÓ9 Ā1f6ČڛNŪũab,D>#ĶÂĮ;-Y?{”ēp€ūÁ[đ Zļ9ÄaÕO[GŊĮ‚ŲÔ―úPÏsųðHÔĘY<úģLÓlčOŦýG [Xôi%`’ îĐĢŪĮÔ0T|ˆ!EĄ>+ü/{ø°l YNĸþĖdË §á†ėk΋Ģy ðCãß;9Ýy|5.#™•’>óûÃĨęŅģæOąĪ0ÕvrÖÛ52›E§ģ!,]Õ6zˆšcėūvóÄá™eÃL1ĩJÐVQĸá\ØÛ‹šZ‡ŪŅ *itäþī6ˆß$·ckOjhâäŋb@ýڒVtČøŲĢLŲĀtúÂI/ä0čÉ[*x†‹'|ĮĪ1fÛ.ŒH~Þ("MMÍõHc―OÅÎȜcldhlÂĒT‰Ov_ą|úpŅkŽ~ +jī&õ‡Â›-\|‚øĐ&+—eĄÓ-›ˆĩĢBTm_Ūv-‘ ySü\”2~ĪéČJg ģĖĶB@éøXįĀy“^H °5nŦËēđó§ąeeLMë\(†>Dƒ†ę[ õu·ûŌL˜<ýĖu‘Æ@*˜Eƒ ’_ōý@S@Žîwĩ:ĸ$č@úEa’ĪÓ =+Ėņ9“ōĒžęĨÓ7žÆ;ĮֆÖÏÛ|ûâŲņ}ĻÚČĶÓp’Ð'Pm7J‚ IÆ5$Ī ^ÖhŧEljȍŅ7čL΋[ۓĸšŲLc_,W}zƒ‘‘å—ýĮf IPÃöΞWƒd™ôeqųųŋĩkÄQS›ál“Ą§oeūLŠ(‹ĐOĨ ôiÖÎNC͎Ä`Ģģoå―đēfZĞų|Æ_M·hmtLJAqĖžÕÓqT^W^(Ó―d@Ã0P CĐJØøþöū™Ÿ\ß4įGb0ąpÉØö‡ËVÄ/ōô3Óį˜#ؘicSJŠ 7šŒĸ=cČphāOlÜp;xyLÔ'Ü\P!ŅŽŅ\^bĀē`3ĨFh4 ï]nI!ݐ~‘Iƒ0ˆjŠ1ƒih>ØĘoóŅÄy55y>“­Ē#|OyuÆ~ļ‰Ļëܒ$Ŧ€ķ Aōöę*‰*ē^0˜ŽOģÍ,åŠíĩĨå@ßŌԘ…`‰ã ‚PžY‰kIöŽ*Ä`ũE°qģ6DĮ§•VŽ]8 |B8kÔäYóėŠ†7Õ†€6h„Ýųąų…Žot0ävŸ˜õú]zlĪ  8p&ø#Į͘·!tƒŧÝt3&„Ģ8ĩP( RÔįŋĐ(fŅŅT2~ŦÐŌÔdffdĀĶũūZH’5lęŨuÜW\P0ՕZÏŒ„P’†ÓȞŧZFņÎïe ㈞õ‘[ŦĢV{ûúyyšg֊Āï €ĀôΝkØĶ~{óPÐķ}b}üöđˆb1‹Āšß†IC`’Ā˜ýÆÎ(ũļ~ŋqęžąų7Ļāý}üþ•é9Vyŧíž,7óÝ0sŽÍĻ~$ÄÛ9·EC#qFC7ļ%üÅé Ýü ļŠOõ†ā (ÞՀ„e=óؒéŨVÍuũôXģņ 9töķņ'—zûųy{ûjĶ1FÐŪŋÚK%Ï4™‘û=\―Üz1uúô!}Œ7í ßEyûøúxųÝJ(Cԕ›ž–―jTƒOH1œė,t'Mú þ­ņũ~úŪEü.éIf Óâk[ûáÅŽQNvS0Øš=Âüýyoj]ï―,―Ė^=Ví;ï§ąæĀtĪ}øâ1!Ūö~^örMB<Œô ûZ˜íýÉÁ9üL•Äœø"ŠáÚŽAŠoþÕþSkNųøųyø­OĻŽb+,šé·îāk9ëÁÅãïÛ4?ýīÕŲúĮŊ</rw۟]-#q:čĄßoÜŪ―.7·ļų.õ 9īiŲà ƒ@/ÉWsŋ59ŧ%ðøÅ2‡Œė\UXC*Yæ;vF°^õĪĶėáō0ŋšŨaLœĻŒXë>Ū/2ÔÝuÆ·sÝÂfAđ+ž|||<#%ũŸāyĀýë=Ë<üü|ÖïØ' ƒĶÃ8ø#\{atc˜ô37Ņß`įūëRƒž/Aā€N—sk­ð_âëžïÁËāMþf†éa8ņûKg=wÏīĐ_ī`1uė~P þĢĸóč(Šâ⒩ŠjAÔ‰*[<ŊĻψė S75Ô (ŲIÔTQϗQ ĩˆ[ZތZ-•Ŋbc’RŌÛä0ų;B]Q^/Ó`TS#k}‘œ‘ûžŨPžóę5_&ųXÕŌų ÆýX&PhÖ'ÅĨ—KPe{æïÁđoxî›ĖÄØøÄŠ:ēS{KUJR\ģՈyÕMmĐU_˜›”] Tad7œÏmŽm‘SĐðđ چ–ž4?#66þõŧmō„Š8;íų쨌ĖJ‚TīÖV7·áTZʎē’ZAŠËŌâ“3ļb5ŲIPS’ŸVÕāŠĶþS/•(?Mƒ›Ŧ˄*œjķ7Ö4ķĘĻÎę?žLHLŪá6dfW6 Đ0™W\Ũ‚’Ý$-ï“âbSęÛ%d/hã‹iãFÝĸ ŧĀĒžôĘ؄Â:qũmõ)‰qYĨu\nOĪ!{åŅÖÜĀjsV yMžŪEĐ(Ė§f“žË•Â$*/ČNKĖzßÔT—žÛĶP·|,õŽž\Иš“žUÁãĩ4ĩJČ?@Š^gRŦ[PÖ5˜ •ÛØ*%ĸHŅZ•–˜”_Ø!oŊĐiéÐNGÞ^ZRŨ5‚°đ ).&1)ĨQ ë5aUUYANVffv^ƒ {Á5â*ę≋O,ŦoéMü6;9.6.ũ·"˜ĘģĢųcÃIčhĐé<Ÿy5ɉq/+šđum˜ÄÔ5%ųyŊâããó>Tv„ōÚÄÔSŠÆęrą†Ð-hLKˆML/n”ĸŠÂÂB>ŸŊŅhÔ EŅōōŠēērđ\&üW@Ô'øĢÃfģkkë9―Aƒâ8þŨ›(ŠD"ę7nÐų4ĒęĻĢŲއÍiā3 sl:Tfóčþø?DįýûũÔ&ŠÆÆÆ―ģƒÁĻŪŪĄbūür‚ š—ÅþœŽŽ^ŸaëŸÍØõ;î3uuVGWj?ČŽ%ÐŅŅ―,öĸ{ēŽ–vą ôPŠ:څr|nhkSĢX…ƒĸãÔÂÆœīĄĔēf!øgtttĨV'ûÔæýą@œ;§~🎃ÏKZ•`;Ũ.âl:øÐV[VÉýŨ ĨJĖ+ŊiFĻ+Lūõ$åÉUŊŲKŸe·‚?ĨĢĢ+ĩ:ˆŠU Ģ ĮwNþŽßӁ˅@,C @QIĄŒėz G1āˆZ(#xOWj™ C SÁ]1$"§ŽVĻPðrĒ/–Ô”'$?âi@7 bĐŅ 8ĄÁS $2mũ$&D øSÚJõ…ŪÄHm0õŽýũ .îčŦŅžžQjRUũąBõOhäB‘LÕĩ_ÁíȈϨW"Кė9@ĢRcxũ#Vwm  …’ŪéãE9Ņ'Ŋ>j)Nõūuåė•Ëvý?Īåƒ?ĨĢĢ+ĩ:‹ÁĶA GeÎóļĖ"€ÖŠ7Þģlœû…æs%åOO{û.v˜įrėÎ+€úŒË~Á;ũŊ›=cÂōWeW<ßāįéæéš3*@Ó^ģ/ÄÅÁÕŅÃoK_zÓTŸŠi Ýn]–ýĒĻPPé­#ÓæŲŊ]ëŧÄÉ-ĐR4―^ž)hÅŠ…ëÖvHîÛėåč6Ë}Ųãũ|@{^ĖĘe \C"îČQ iČ[ē9,tËÛy>›\>î―`þTߐ"1Nõ|ïčօN.nKŨĶvîþshĮšĩŦCV͟:Ýųtr•ļ1'öqK'ÖoVũäw-pYȑ$œjbu˖§VķæÞ;áäčdïāt#Ģ E[Ÿœ>õäŲ%KÏīcc:*jĻiĮGûčččJ­Îß!āóZ„R*žt ”7h}RZFÚÝã#4Ŋ—…]žąåéģÏNė|/@Á͊Nīšŋ2þáɖØËĨrPžp0ģjčõļÔãķL yraoŠÆ6)%~vŋúËģpðIyü3žŊ]ÄūݎÞ6žĶJc.žO•D'Ķ]=đOÝ^*PÃĄz—f4nyĘåāí™}ũđGRO;ØyIŠBƒ·°mI‹ŧRũôXB•’N* b28ݓãŊ7%yĨz9ųųþË\ÁĮØË[ĢÅw“S8žðŨ4H[+ Ë$K"âNÛÄ%§ þÁaöũ~KÖ]<éĶšp؏I{|ĪJ>^―ðPbc-Čûé`FāéG·B]"ÏÉ,=VŊqœí~ũ~Č@Cˆ–n ZûUЉÚŽŽî:ņfÛÞm:ĶčĻ.Ïk[rÍūŊĄzŌ6I•’ĸdo@"Đ@9érÄâuÁnÓHyÉķ ƒÉ‡įf­õ^PáģaĸFŸo[4ōæ7ũ<’…ž6û)―ÞËDˆSr2J?Ð/žþåmĢ://ĄþP@{Wh9wĪĩLøÆÚ†F’€ë· šcÎđų™’fÞ&ÏňŒa`< ÅÄÕļFs-|ņ}ĻYaČĶŅĐ`ƒ‰vAkįôÃŦM,įm^7ČBmĨoĪOĮZZåÜŨkÝåuĩä7?d(>Ũ‡ÚåÄæÝcģvˆIÓãčģMŒM @áK͟ûõiōkAjÎ\ũCÆhģœ[~6,@OŅ!gL a@ßÔČĀ€nnbČ č•b^Þ?=ðOéččJ­D@ôÞû 8É1ûÂfīQvI‘ßė HˆeÃė°ãFāüþ$†ĻĒ‚mmBQTĐ킠[Ėļ_]“vĀnÉÖųķ }HhÄĪĨwÎmčk!ôāWdÅĸÖ~ðð7ý9cGn, ŋ•RĩØĘÜ@\TÝŌNkûīūi H=&‹öåˆoŽžšyvœĩ #lēÕ…FŲŋrē•,ƒ…Uc€dF4ĄZ(LP)2L͍ŽGL>ó܈~ˆÓÓö ŅfŽtP4DĐÁ@oĖ!+t\ái:i杅3ŒšŸö4<ėČ5ŨI_`„­Į,PŠj”īĖxDÎī4::š_ čüÕU%ÚčėââāāļýBIÃ! Ĩql~ ߖ{!l‘‹ŧĢoX%{Æé5‹î†ŧøøŊ ŲôQ ˜4}ļŦ<‘ AlĻð^ˆŦ‡ÏÞģ‰ĢįĖķ61s_ą}`Í-_ßĨ~ū^73 HÐK~*ģZā°`þ<[{Ŋ>?Ü?eãšŌī dÕŠˆKiCĻū AlîÞBaŒÃ&ŸiŠ 7oĸĨÞ·Ý•é:ącsÉĨ@ŋĨþū~Þ/đƒĶ‡hŅĩ'áÚIƒÕ*%ų­ûęĨ_óýĻ<|――=D Ģ0čŒ :7C˜8mlėĨ}ũÞRƒOūs\=T,=ÃmĒžÕw[}lŽ„,õ_šÄmSdĐ2rlmþŊÞ~ûy”f%ŋ-G ðOéččîjuf…\{īļ]Đ­9Iĸ1Ã-†}GBŒá“øë â†v͉ÍWÖ}Íw°0·ŠYila=ˆØģCoŽc˜&Ģ~ū}ģŊ1DØ­YßŋI鍛2ՂĀČЧŸÞÍ{[BŒQßĨõö9KwN°an ·č)éc`ģrۙY­z4ųæũĐú ”>`Öís“újc:xęb^Þŧ5ÞÏækĢ}ƒĢƏ+Š38†ßXÐYô9·ÏMąÔ ūvëЗ–lę °Ŧũôū0ƒ Ų[Ýžš›ŊDéFŅðÝ5īŒ[rč$fDĢV`ŎÛĢmՌ~Lð A`ĸ{ũ"Gpü73;7›·É0&æ4  A=ÐEÄDDRX؈Ērf“ĘJÁB0"D,$âæš`@ QQ+ËKOÐ`!‚‚5 rBØÛĸ‚™5ē~>žzšûōöũïŪÜđkßþ;Šˆhu|âĨko;uöü…öüŽnޛ߷<<úÕ·Éæ46Ý~ïŌzŊ•Fƒ` o ā „s§—‡·í^8ûåĐ/ķßĸÞóƒ­E—ÔéŨŸ|ęÍŅņWÏxŲ°åú#/>3ú.ß|hÏuŧ’ļôŪÞûØō]Å’ķæ·_SŊøÏčnÝÖ ˜ņc1Đ@jĪАZĐ@jĪ@jZĐ@jĪĀ#ŠĀpååc+Ãhþ`Ð?H-ðugïđuKSŸ’ZĪ@jZАZāÏ\Õ―iïÎCõšåЇŋėÔy―kũRüäĖųÏOŽ^IŌ$ĀŪšēáņ_:[+Ũcē^H-4å‘=GoÞqwüJ9Đ&ã2@jĄuU?ŒˆoÎ}ýÁGïÄÏŠĻŠĐ…fLÆåûŸ,ŋûákOŸxô‡ ß8ƒÆ%i’fÉĐōȊ$@jĄqY+ŲØËëČVåÔR‹ØÕnhg›zEŧÛrŧkŠĪÔ6ËÓ4KĪ@jĪø;o}úÂÏÝxß37<ûöÃ1 H-˜Øílãåyg.ßÐɌn§Ãe/pëk.Ŋk[UŅʓV‘Hmó°Ŧ―,ŦW€€Ô ĀĸՁþāØĘ°ĐOR üÞ °^R ĩH-€Ô ĩR ĩH-€ÔÎ<АZĐ@jĪ@jZĐ@jĪ@jðs)Ër2™ÄEDRûW(Šbmmmuu5þ)`<įy.ĩŒŠŠ:Îââb\4°ą­ŠJjĸ%~ûŽÅĪАZĐĀ ~lï>ĀĢŠōþŸs6)“ÞC$„Pc @Ī ‚îbÁÅąŪšļ.ŦbE\ Ũ‚,‹eÕÝYŠ R BHï―MĶÝrÎųßdķðĸ.žo`ŸGĮ“3ŋ™sïãó|Ÿ3gî=ƒû >žĀŽ\.„ŒąÝnommmhhhiiąŲlŪþĸÓčŋøģQ ŪĒÉŽ,ËåååyyyEEEgΜ9uęÔņãĮ‹‹‹%IšTĒNÄø %-ūČģkĸ(`4āQ 9ëp8īÕæģÉÉÉ7ÜpÃôéÓ§L™’’’ĒålnnŪ6―í7mą(H_|ąŊÃŪšJˆ ęD‘`ü·ķNkkeĪ·%­S{ zûīNąŊ€` ÁDkœ›ŽbQo-§Ö|•í ĒN }ókĄïÝþVĢØÚū}ö­&[ã·?9$üËXŪ—h――„ÞWüýåļũþ—ˆZˆÚ’’―^Ÿžž(‚kÝĀßß_ŧgŨËËK›Ûö·h‹‰ ĻMū—Ûieihci^öąã­VU/ mĩ%û~ČiąqZO9x$Ŋ†‰Ī&?ïhÞEäžöģŅŲûó,6V~tũęeŦÏķKDCmžðÂį{ ­N{muMAîĄÓe-‚(Z[Ëũ8ÖeW{ã’#ĔæžV2ø™Ü :ÜŌ;֑V;'rw^NvqUX{CSqAîÉâ2.ķŪÆÃŲ]’ˆ•â‡r‹&ˆZðŋģDÛÕÕÕŅŅ‘ Š"ĸgc­ŋ§§§đđđ7‚/€#Bzĸu…6WKóv}øîŠÏ֗Ÿ|vésï}ôeu{ŨÁŊ·<ŋėÍ/ŋ)Ž8ąwóšÕkVŊÛômmíá ‹_ÛðᚕËŋĘĐ.;ķu뎾â$ŠÔÚ| {ÏŨ_čän%{|úŧW^|ýãēĶú7Vmxý[+ķæ9v&š{góS5•§Ž>ģôŲõ}UÛÚķoÏWkÖŪ_·þĩ §ãƒ'[ĸŅ;O>ûæ‰SMߎxbՇûjš{J vūüÖÆw^&§ūG ļ\q‚ īĩĩųøøxzz*Š‚þŅhÔĶ·uuuáááýîĩȑ ĢNÕmpĒ1ēW<ÜØļ`ëÛ7Ģ͜_9ōø{š‹ķūôā6æ3aļđąĐ:g=°lņČÆņKÜvcæâs'Į!§Ó;/cÆāĨÄČGĢŊ›ījņ„Ŋ·ĸ5ĸðŪÍ{ š?­īĩIĄČ C.I6ËÁCđ!wĸåÍ9ĻãäÃ[Öû„ÜhV­-݇û°§V>šýŲöķĶŽ ĸ@cw w§û6ŋÝ#ŒMónmėtˆQޜSÆ9Ē\ŅÕŦÕęîîN)ÕĒVûóßģXKamVÛïr-cvUfZƒˆÝåßŊúŽôáÉąĮëĨÁą~JÃĄėÜĀp?Sx@ĀĄß^a ˆJądfM*@­E9‡›Ē‚žtJՙĒē&K‚Ŋ‘#nënĖ=T=\ņð™Qæá3$<>|HjVt盨·žÁđ"IŒq•Ąā€i_öĄ\ĸp?ÏĻ”ņÝšäcâ‡ų°ŋðP7B‘BõĒ.á§?ÍúýĶŋėØ?6>#Pš~sâ Óáėï=Ââã#‚9cčŠĒÖ$Ir:ŠŠ^pÚŦ=Ŧ=Ē âŒ‰sŒ4đÎĻwČČéæ―Û#HŧšpĮôŧKĸ°áÍĖEkîšmæō•_l) šë‰‡jV}·kejh2VOîØhNYð󛇑ē#=ōókã'Ą*Iđi܇ßÎ>iR’Ņy%Ä 4î™K7þiĮŋ!1!AD>Æd4_?\ĖLIïn}góûk'.zuážYËWþuO}‹ŌŨM‰°>zø7ģxrϖ\Kėíã&g J>óōĶ­›ŠÍÇæį æ„ϰ+ĩ@ÔƘ··wuu5Ĩc|ÁŊŋ:;;ĩ5­ōBIŦŠÆˆ§~#Kã {Į>―á}Ũ3ēŽÄ=žrūÖRe…G――é­W‘åa+ßB} ·~øåįf ņfT–UôЋkz å%Nŋoí Ūõƅa§ÂŌÓCUân~dÂ- Ķ:e•s$―'͟ŦJ꾉XR؂_ŪZ€§ēÂ"ÞÞ8­w,I2žėTĮŽOãœ%%­ŸƒÓ ÔĀeŊýV+PeiäĢÏpŠJŠ‚Āp{{;ŒÁ`(/Ŋts3FGGQJ/9ĨĩZ­ûũïŨūþ þ—‰­(ŠÚĸâ“'OĶĨĨOÛ˅Р%ĒŽ`ĀQKKË8į‘‘áēŽĀŽöŋkVŦ-ÅÆÄĔ––F___­Įuí!Äfģi·3DFFšÍæËūƒ8õq€Ļý/IÛļļ8UUĩŧī+ īðÕBķĄĄĄĐĐIû3>>þĘl†ĀāŋįšZ BbbâčŅĢcÚrÁîÝŧOœ8!IRRRŌ°aÃ.ÏïÓ`V 0ÆÚZmHHúü€Ļ—Å՟ŠĀˆZ€1EĒŨ ƒ =jíËđ!,Ē‚{zäƒŲõ›þpjí;Į7þūhĸþÚķ6ŧ `t øß·ĮÚËð:ĩ‹ ƂöīŦæ Ē`„ΜiûōÏgË[œ‘ÃÃ2n“QÓ­|ýmYAAËEŊĘ˜+Ęø?F[sAI—SÅčĮrm2KÐ :―ˆĸþžz(ÚTÛØŅážp”bŌÝtĶŲęloíīŲ•+·@ÔA ••{Ũ%†§NJŒð7ļ{„™ĮLˆ2.n_nCII›Vƒ.ˆčDGÅK/~ÕhQ\1ĨÓõzÑ7Þ9ÕląÁįĘŅ`ėe BXïjëuHē4Օ˂Áõ&DüÛ3ø|Û ;_/ėęÔũū 1]ôąöÚâVŠôe7í.ŽéÔéÕïūÚSPÜc0ôŽĐuŧÞ #Ũ@šōïÞØYÞpęxQ}ģõÜļzŒņų#Ũj\CčŒˆpþåBo·ÖÐÅQ Æ6›žį`UĀāČāčā.ŧjqp‹“i]6Õä–4hïĄÚöv;!ļŸ_Īqœč”eĪ5 Ķŧ6ŋ:ĸîŧ6T2―ŧ{[Ųū…·Þ?ŅoK;T‘`QŊk,8ļþ·OÍžcŅGŠõ:ëo)ĐãGŽįm)klÍmrĖūu܈D_Ue?&ĻN‡TĐdĪ –{ęZ+DŊ °ŠqY'$%ņõ‘†M]ņ WņKïĻlé8ÛÔzĶððT“QPŠ€ĻýÁ˜ČNĐM&ĩ6\c%§ÎVvtī!Aīww•”ŨôāšÔ*ŧÍĶÕöĩDīó‰3Rųä–{|}°Óœ2mXŠ%·/^ûeæÝ·šS•õ§RĶáT<þž°ž·gß>ŸŽN“W[ttÝvČ\ÖXō]n~sE~éÉZ43ËgÉM7ýė?'ŒM7ŨoXøó›ūiXpßÔōĶ‚ēÜNĪũ,ÚúúÝũÞũŲĐŪԉ#ÍïūüR“ƒĖũeįûëūĖÁ"Ąœ1•ĐČüģ‡î *É-ičē9ĐëK0íQ§=ļsC`Čě3WüėŽđ ß5œg°”œ*lķI#•2Ę8&bUÁ=&ĢKðK\tĮdgŅņŌ6™ĩ}nÓŧ'ÚėRvaîœsO/=uZ%•‹’4rB“Îę.’9ïžYfosX [āÝŊ$w9=JUĘÜĒ–ŋųŠCgōņ4A3hP öðH_š<Í!jë%ÆÞÄįJüėĮ‹nėҟˆƒ>yé{ĢęÄ3&Âĸ•ÕËŧzß_{ïį:―€ųŽŽĐ}ÉŊ’ÍþAū>1ŋ^‘XŨŒ<ýÝ|u‹o‰3y!•qôï€ýjaŋZQŠ*>üĶ øš‰ž&Q ^ÜZ_q64:Î.š•ÛíÎڃ{’9zôPĨŸÉĸļ§8ÆŪīåœi=Øõecė_J]ĸÅ}x­áŠ~W•!Þ đޑ3­æ\ÓÕÉ1AÖúĢĨΑÃcô‚Ŧ@ƒØđČÄ ū7ü§1{_Ų[ü‡„\ĮåqÆþiŽŋ3BäÜĢŠp ĀeØŊöŒZˆZW0ýņë}ÕÔ+þúTƒ^Ô::$Ј3ĪPV’“āhū}Þ―^?݈U…rtÍĩŦįH„I㓞mU9°ģÓŨy›'TKÝÉ\c{eÖÄ$ƒÁ8@'mœ)?g€ŊÅc, ÐX8Žææf‹Åâããƒ~4Üŧ^;€$I:qâDVV–Á`@W‰ĘĘĘēēēÔÔTUUŅ?#„twwk'ņ#Ũ"\§ųLą/û !ÄjĩÖÕÕiĮíl  }~LÎ2GWÉéÂü‚ĒŠę‡ÂÐÅp§ĩ§―ĨĨĨ­SųqkŠÅöĐėB?ŽŲlÖōĻŋUUĩÓ]&ÁŽžîmŲŌÚÝmĨati˜ôūõbvŦUĶ_îiŧ퀠Š*ŽÕ^ƒŽ!\ü$ŒqSeΊ'žÝ]Ōíï­oŪQ^ĸáĢ3’įŪf]ëV<ą―ĻÍSgõ2íÉ%Æz^ôýéΕOžëĢ/c|~Ė4‘ũđdĶŋņ&W–œøaï!›Î$Šv›ę–uÛÏRt*ŋÄ4Vęér0ÉÓŦ-ÛŋÞ;õÖQÁz•ÁŊ0ühĀôķŋ„áΚWæßr8`þį۞ė‡ōģũĄH_•RcÎ(eœAˆqĶ#"ˆÔqðØ~ĸÉO/KïYļøÃáĐ+Œ§ŠĘ9'ĪïELŦ"ˆ1Šĩ0ŅéT·(!ZDŨbŒ/’wŒąKf1íƒ.D…Žš3ßþy§ĸ˜iģS‡Ĩ§đĩÓӀÆ9cĄóŦ ŪFoŦŊOЉ‡g·f ÃÜcdÆõ&OŽĻ―/9_ūØôĸÖsūqą@‡Ļ―ĀûôũĄ•ąĄpũ–FóÆžMŠðVš:u.SĘyeÁî7WŊĘŦ‘BĮ?õÂÓ)Aî”qDČ˜ ˆ1qCŌ&D$ÄĮˆnĒĩņĖŊ~õëâÚ6ĢOúëë–ĨD{5žÍ~áąe':ėáņ#Ũū·–0ĝڑŋüÉßzÍ|üņ›†ký'QŦé§ ÅYWuĘ4235Å۝0dė •·ŨœÞŧĸ˜MUõ>™ãĢýPÎŪÝMLTÛę›%ÔŽéÃ}…ĨUݕŸuŨMĖLĐ:Uė†ëþpĶĮÏh/)o0E&OŸrŧĩüŊ{+ŌĶe•c;·;Ģ32ã}Îæ:|ĒJFnĢ'LNŠō―HÚB jŊM°>ý=iģŲ|ƒ…™§C͈2а sVüúŅgÅɏüqíÄw–.\ôë ß}ð˜—ČīzÄ„=[>šũãâjÄåÓFË]•ũĸfUL{þ֙ÜzgâĒ€ũ/ŠMūï“Įo#ŧ'wR,`ZųÚ/VįXf}•(KýŪņþ˜‹jų9ýÉĮ(ÕĩķyČĖßŅSwöļ’x]f†ßūCíc†sKgWĢÄ{uķv;˜Ģ!ĸûœ’‘ÓæļU:øýĻŧf{b~ņƒ‡Ļ―˜ÕĘē,Iš"PYĒŠĒJNI!Š *Ÿžž’ŽšmV`Y=dÅO§û›šóÆŋÞ ŽÃžäƒ)Ķ"“)eūæ°Ôąņĩ[·oþ&gÉ ÁÛ>[wât[}­mĒÜÖZӚ}ÚãņWïJņīĘ~ˆK\°|ūôϘ›ķlzȝN‰ĄþĢö’ßđ&éý, `„(Ģ\é-`SΑ Ã-Íí]îÓfĮyûš’ã"Š•·8SÎĢGgĪ‹ÍŊĘŊUTFôžnĢÞ'Č닔ާTeē‡ßā&^ōŽ•ÉēLE„°Ļ=Į#LŦKŦ%ŦĩäČ~ėīPÕ­[b:ÆÐ…]ģ?ãDíEfĩQOŋ Î†Ú?þŋtî(ĢQh<‘Óã7Þ0$@g·46vÉAÍ ĩ=:―ž0Ę0ÃLC)Mž4ņūiÕÛ>ÝøÍũaUĨG:CVžxÏĶ'ŦFz―‡ŧhĐŠ­SF$Šēa…)n™·,č:ļņw›Ō–=8OÏaü?\@ļøÜ#BÜMÞ͇ÏÖ%Š ņ!ˆ:ív.čuÄŲÕãŒôóīÚí2ÖégËŠBÃqÆī†ėtRŽÎĸ> wļŠR•"ŽzËĻŠÚ,6ęįhhēĘ>{zđ SRjzχ!ė&PĘ/įn]åœģ€Á7Ž{,}ÓēĮ~ųüęĩŊŊÝYX‡tzÏÐ3͚7Ž]ņÛ5˗ŪËIøųí1ž"ãŪy#—%iûïß}bņCŪēΝ<ÆWĮÛËō·þiKEICîž}Í$î‘GÆnY―䙕ŦW―ņÎŲVYu8L#î|í…ÅübųúO%Ž0šB(Ō… 52Čūí›mrŽÍ9ļã›ŋ6"ó8ӑėƒ9Ų?|_Ô?Ę_‡ĻĒ0ÚëLU•rŽ―͞ķŠã‡ KėTëTxïđ2EV9ŌðÞzÆDŊ7$ÛũÝö‡Z{ŽLĨAq‰Áf’{ðXYųŲ‚b+Gøŋí >õƒ ƙ―5lGvQE-xjUjf’ĖÝ{æĩĝŧК, ŸY35c„Ā(Į―ŅãÄޏ?ũFUM“ĪōWfߗ66‰uG#óíŠņŲ/æŊg ÏúśÞqÛË: æĻ/·ÉŽíįûËũ?I)lpPî:Ž~ö?ŲL€3ŠĨáøé·DŨÔīY똘†&G†ûûŊŸjŪŪéēJĐã§ÄD+*1.“đ›ĐB#RŌLܝĐjHâļižÁ6†īÔŸ9ÉÄÝÜFNōä"bĖÕã#0CPV֔šÖ.wĸc™‚==UĢûÔi3+Šęl5ų1‚+'U%I“įŒ˜Ä9BD Š$)TAßŲóh]˜SI–ŲđjŌ2Ķfˆ!DUE’dÁyó‹0ÂTUSF“%‰r2yÖmY―ŅĖ%ÉIFM&TvÚ刍DN§“_Ū$ę'mĢ)6!)†qŒ ƈŠ*―'$õžæTĨŒ‘°(ÄĐÖōóE\ëÃzŊØĄZ c”{DEkuÜíT•rlŒˆŽfTe ų† 2‡ĸíú/ÄĻFôðMH2sŽ\cqtiŨԍđĀđGÍČČÐëõŠŠĒË\X§ÓÕôŅ6mčïÆ\ŧÝŪmIãšēâęúxA‘$ĐĐĐ)22ōڙÕīäōöö>}útllާ§įO"JiCCCEEETTcė‚ql4ŧšš:;;M&“ WWÔj9ÛÚÚęîî~­Íj€…ZŦÕZ^^ÞÞÞŪ(ĘÃk@íÜęááĄmŲĨMú.ĢÚđ6áœ_úŒŌŲ ‚  ŊŊŊ(ŠĩŨ(ĨZÎ^ŧŧ ‚ Å!ä’gt~Ú{Õ­!hŪŲY-0――Z0īð?ãĒ jĒD-@Ô õõõ„ā Ī/f:dąX mŊ䎰ZĖĒ%K–žũÞ{ųųųmmmí— -TĩhÕV‹ŲĸtéA6I_GqIENDŪB`‚doc/images/ifw-tutorial-select-components.png000066400000000000000000001113751325366651500217100ustar00rootroot00000000000000‰PNG  IHDRÎGæąm’ÄIDATxė’ÍŪÚ@ …QĨ,‘xxĸ-â"]ĩĒ *`Hf2“Û-EÖ5qïQ°U‹ ĨRÏšĸäƒÁl6Ëó<ÆøýRŧÝNū˜Ō…ú §Ö~ŋŨ)[äGžÞiŽjwøŦ|Īó?Ÿ~ԃðb“7Ēþ'ßČGģúÝPéŋ'[įƒó“ó‰1æy>N? ‡ÃņxœeŲ7GŽ,ž‹Ôétâˆ!øIð Vtf!ŅM7Į&,aÖ:zUqčžŅüņ|―ŌŋČGī5Ūbîí|ô#ŪrœÉ~›~ HBŨ߃Ýŧ'ĪŊũãCzr>Y–M&“Ņh4pÎĨ”bŒMÓÄVt­ë:‘šģRŦš :v' ,Κ7Ņ ī0åĒ{BĒŅŦv>Ēā#ēDHōQ)ąĪÍG˜Ålx<øm>0ũæM]Ë6œŊ É*ŅßVwī>Üþĸúņ› ˆ―GÃóœ| ų°Á97ðޗeY]*ÔõÖđeąøüōē˜ÏŨŦUœ”ō­čJg.Īëvŧ].—EQPÜl6œí S(ES_‹Oó_Šųz―Ē•8K6Ąîh~Ą‰xĮnÂ/Jīšs Đ2šjq†S/ ē8ž5)üšÖąiRôæã+^%Ķčt=Ï%ïãC-SŠ!h>Ō)þ?žŒXþÞʗÕųé_žÃ'ĪæõÛYŊĖ\ðUýþ?ð -GÁ‡d|5JzóÁ#,d5ðÏōĄØ›Į|ČĢų đā \+Š,ĄŸÔTíOTéũßé‡ĶÍ~ŅdŧXÖÆÆUtݕyãMQWŠ ŒÚ î͐bA^”Yƒ@Ũ­Úd^™a˜™;sïÜũũ{gÜĪMÓĶÉ~h›mŌ6ésįą'·Ü­ýī'ááy~Ï9ŋsžß=gŠU† fŊ^đvĪ~ā­ëÞëęĖŽŊa7§q·īītûöíąąąņņņP(”Ëå0!„@E2?<>ÐÔôøųC~Tåģ@<ąn^Tą3Z!ÐŽVąŲųMӄ‡æÄȆ ú8°ķũÕë ÜîøV§>€Āýi GxÕ°āU%žGķԚZœáoĶhîÔ§úēšš[Í―ŽšųdēČŦûéƒĘī~š­“&gŌiV1ėyąáĪN} —ÞĘJZđâМAk0Žĸ ~KEå3“Ss”þ5žŸ ­WvŠŧzØ^6čƒČō‰ĩPh~aņŨF­b7{ĢV­ÉģG§Č{wZũøâxēqęƒ ũÐĮn@čœ/ ŲOŸýį ÂĸO} NH ›ŨéUíŦÄbCYÞ@ŸšŪk53 CŊ•ˍžm―ûŽôËÏ~·3îM=9yėFýátø™ÕŽŠŠý;Zišžžž^XX ( ņrũøņcôƒŧ――mÔ óĢÕЍ‘í―ÞÚ=Ö&ïq;D‚[îŸuđ>:IŪ›†‰=ą3ʂV;büÛā vƒ 2Ļƒx…}ĩðĀ6€; JÂķK;Ųɉ0(^ņ}õŅ SĢ“Aß{ýwžh•ęNYūäëøÅĻÁ‡™5ƒXؘ8Ĩ­6 â@ė„QŒá 0ū+WËӝžņðowøŦž–é5Ēbūb†ēQ ZHM=Äą_ßHŽz™h°ŧka•|uģØ^ uBaĻĢKkËîsÁ0ЕM ‡Ũ˜xBŧ*•Â_}E+˜ÞBpUˆ†ËĸÆuŠ1ĘïlLzÏxg^ž|ÔÄSd‘‹ņKąf9·2qúäņÞŦCūÆÃWf#;9BŲ(˜ÚŠŽDķÐÎ:ƒxČ?e·Đįė0 _ßŲ?ŧú ôqöf@·ûÎŨŪđĀąØ˜ó…mŨdÁ į|aÜ9_ĀöÕ!E‘eY­Ú#hqxxâûßûãĮĸyoōï sĸ˜Ÿú˧Ũū<øÖÝŪ.į•švF†~Xgffhš†xI’}?xðĐ<ĘģZúS˜ûf%šsŨ;^?0brl€ŽWÄĶžÆ ðā Š Kīp‹_―K;§ý Bœßßva*lę*™ÏrĒŽ"7‰ÏĪ’ņxœ(ąˆĩ˜ßŽÅb[ŲĒ€žÅĢ—Nų.~’*rˆ*YkŠÂmĨފI^BGIā(’ä˜âææfķŠyĨ†Ū“ÏšÏ „‹š.ó%šå™b*•"JŒR{]Č&ãąt&+*j6ķäjn[ÚČqžĀ3d*™HlĶ)VDÎŊz›=1Ū™ ī>7*hV2ŧ™@°"š$Yd)šeKD"C𒂘uS ~Ðčūýõ~_5$V°^jĻbŅĶs‚ĒĶöðęų†ūŅX*ˋ2O“4+ ’EŽ.–8―Ķ–Íg67·ēž„ôýúĮ9h·oÐ?Ȱŋó֞Î>öųēƒÎÄ9_ÎØýį ÛwDQ Ņ?žZ&Ëũš·þðÏͧūíõĸírāÛūÎŋšÏh?>4sø2›•dûãûũïŊŽŽAÓ4Įq à …h4:==M’$ĶÁ"ĨKĢžūåsLĸē2üXųB―yŸýøÚzģĸæ‰B‘D ˜qN<ÏÕ:=áĘ~D/‡2œ$Į>ö({ˆ=)F *øÞĀ€Ā𭓠’-887{č󈎙Rþē§!04rĨÏssiÃ(—™Ėƀ§Åãó5·ī ,óš‘x8Þsí%™\rŅ}îz†U’îøýž––ÆāČŊ›ˆKVÔÔÓŧíū_ÓoßZž34ōf—ŨįũwŧšŽüīi,Ó „xžŊæ[ĄˆRVï_vũM†M­xĢÛ3ý,§ņ…‰ŸN7·û―·–ŨĻlü“KÞvĸ\N Û˧ęÕĸäXopŽĘŪßčï[z^PļÜÔĀŖw[[ģïÂHŽUŠąGíŪž@OScƒË;ÉrĻąŅÃQ_Ïú{‡ÂEĩ{tąÓÛÓÛãú°ĄÉ;+ˆlöy—ÛÓęņũõf8ff°ëGuu§[ûŋ\O?šđámït7čšU’u&·â9ëēæÆlĀ˜Ũ+JtqĘï>ëksŽ‚ž:áiïėëðķ'7IQAß]ÕWï\8rŽãWáĪŽš"+õųíën·ÏÕØz}â‰Hg~ÖzüPÝɞ ƒ‚ė ÎG ][đÚ>RMq~ĀŨÚÕÓŲŅvōčŅāøNR$G‡@7îÝ?ö@įæ_ĖV{lĮŸ™ÝÛ{Úw>û°cĀ/‚yÄĪĒMĨĄM j)JiUĨĄĪUäŠÁ<ŠÓĻĻĸīĻŠ6Š*EŠõĄķ”ī)*PĒH‰JhEDą] `ĻÎ9ÛøÎũڝ™ÝŲÎyŧɝ…ĻÚHýYZÍÎũ˜oóû>ŸĶĘ$•šŽT5t dĻôÓíûŦō Ð_óĸ„‰;į‡8sāœ;„ōwEBAÎ ģā3‹†U rÚōGđÅ)•žBAEĢQ]Ũĩ[ëH$"ŸķmßJYã뎥ĐV`ÍgÍ!šĢPM]Ô!ÆĐt!\bnĄVåšŪ|zVųdŒyi―b$ˆóųžŽ°ûÓŅWūĸÂφĶ5Ģ4Ļ DË4™í`Ē!ŨɚīŪ­ĩÚOM^°ļūHóķÅ ÛÖŽX‚Į•jđõ―ž4jōcLYšŠįîVÄļ?ŽÛĶUŋdÍĶžðąŸ~ïðKŊMå,á ]ÂY”GΞþãÇ~ō,Ō|ķÃWVíđl Zș>|íĖŦ?yų 7Ä ÄX.аjíōrb–(ũ.?―đïāž§Œ‰sŋûíņk龍™ýËŊ^úõéŅęø"˘dM35-FđĀćQ)?6°$FF“ā’å+Ģ„ûj͟ģ<―ÍĢØ“Ú_0ČTžzVU?žA<Ð#pĻŠĀēþRũ+e›•I NϙÝBe_ĪģZôßóĢžÍõŅņC<ģlrï“äiĄ•ŦĮ"1ģąwtáŪnÜÞe7/E}Ý+õ@@ýą&‹Årđ\ )0MSŽ`MÓTē$Zâ]lJømÜ_[Žę"(dĨhĒŠ ÍK‚ð0ČĘ^Ëþ§ys FĪRI”ƒÞūįþžÕ{ŠŨ\Ə„—ģēė)Ô ëJéCfp“yʒȐĸˆųfĖ Ąk6Ĩ°Vô]üĮ{YjgRŨģ7xXwl‡ZŒӗĸ9œu9!ČEˆ+_4-ʄ„‹°“=yė ģ{ãîþ­-ą°p°dŧ(u#i2::4ęó5&ĒXŌ—š1Q ôŠÜ1ššcÄq1Æn‰WLlAĒŅ™ū”L3VLÏdFÎŋXcũöĩ‰8"ĨI4ÄÄ,•Å{sÎĄ`(=6öþtĄ8ûAōz*ÉdŧrXSÎ’å Üæ2ŽH[é@ĪcŧPČ[œ#TŲ9jŊ{bÛ·v?ž>{nô&ē‹;”q3=zäÏCŸØēĢwóCaĒaÛ\ČĒ%uēhųî‘jĪw?ðĨŸ|vYa „m\Č%?p37ÆS3‘ÄŠGîo1Ó3M­-ÁXbýWz ~spï㠗Ëŧ˜JXI&6ŒģlęĘų‹ã(€JHD.9–6éôøŋ°ĄÕ„uÆmĄôOÃmôþŠä*õ#ĄęGEŲ)jÔ>BÁŽį_Ö)ja0FĀyޟĒeŸĶŠPĶüyųņ*Ÿ—ÏúŅņĩĄ7ĸ͜ĩõĪDáþđöÁÖzAÅÛîē°ŧ,‹ĨA+*†ÔVkąąXQŦbĢŽĒ`Q‚"Ë^`ö"Øô˜˜>šþˆNŲfj†Ī}čKÏÃäËŲïœ9ûå;OĢŠšĶU›QŦÕā 4­°ī(OŽßlü^,|ũõXôstŠZ*U€|HC%€|>/I|ŧŧŧŧ――…_sđĒ!&Š*'ÅÅw™`Ą·nöJ_‰“ą·ĐHY.Á>ŋ8ŠOXŽŠP,ėž} âĢŋhÅû ØĮŪē ía0}0€ũ'ÁFÉûj`x—-ÖaLČü‹>•‹Y–Œî\˜ Š―Z.„(’fYrˆĖ§ĖzCÎ͆ûƒ4ĮqĪÃ5sĄ™'æÝoJ @MtCß|ņŽÓIxG‚^Ęá LdĘR”%=éåoo·gíPŪ›Ō$=ôðqŊāũõ9<ɌÜh‰;?l\Éa†Z>8oXęJĖûģë -líÆ)G;Ãđ†îj'vOŠgéeūãÆËEq:0ēūwyeĻņįcÃ$í" Æ?WĐYĨôzåũŠš) CƒK[Y`čķ á'v$’ū4 āSûįšÕä,ïfób’Ķ|~žĒĮs’ĄŦ—Ó|%ldbWé‚Ģt{xiãŽpČÓLNŦg߇ÝþUóËĩ$nr=nŽį8v#ŊÉâ+Â+Jŋýu]æIšņ Žģ›\I]_›ŸÖæzšxÏ0ác (ŲÄ@g'Žž*æQ<ōÔ1@r~ŊĮé'­†ąjkëņųÂŲą°!Z–ÞęĖVĸ €ų™ ÆŋúGÓÛ/dx­%­û‚íj‚†AIŽ Û/LĸJÄÁôAäJ3*• dC IœČ@Mï_ŸÎÎæ_Ę;Û?(ĩz)bĘ?―†ŠęJJ ĘkBĒĄƒ –]`2ųNœÄQø9žŲHÖhGw.FûÅ~~ëĐ6ÕZœĖ3â—Rðz:‡ÃņxDDPݜmĀÄĄ’>{ûúÃóïžūš}öņðÆ'C4ķHņĮ+ē’Âs†Ž ąá#xZÃ$ēÅ —Ž{}.øĀģĨq1Åø ·ÓíõAqœ•ú2ü―>xþGŸŲÖZb į:6$ZļĖJûÂĻŽHN^Sđ3Wo]Lđq=Æ ÆšÚšĩĮq†H]?ūšyĸśŲįÚđĪðcÜ~ fķąpMFÛēZTÚQ.5{šD§A4ʒ›­_ƒÖ^*­&?+Ļï‰9`wCZĀS+i}‰Þ‚ũr&å ũėäĮƒķ‘ĐõXk„å_1A*Ūbjj-Ŋ ÆRŽfžN§ï~ETÄ%4š&BŠÖÄԈĀ^Ÿj Nۘ—ŸˆŒož\]ŋü”―Õ!wnîûģ]Ýýþ ŌZJíõ=H9>^ïĐÏ#Ė6a)†Á?pwpí Ïū,ĀŠyÍâē` $|”Ŧĩâ_6\ûpï(=Š GNárRŒĘĸø7üĄï_îËn7Đ- ĢžĩįToô ŠrĄ>€ümz.š-=Ļä8`Ο+,6ė`ÓgČĖ\óŸą”ĩ[Ú:HÉn>ŒØ ŽČ8tđgzf1rėœäčH:”|ĒSÏQ>ΚIÖHú §ŅšOFåėLX*6ðŪĩíĘ+f„‹ayɇüãĩ\íŊÔjņöęŸO“[>VôZâ]-Þ―ūúFPÎi―Yŧø~^’âe%‹™.ė&Ū„7„)Đ"_ÏnŽâŸÕÍÚ5ņUMŒ}ˆŧŜXbī 16`S"áã32Ÿ\Úo·Ðāč##ĮŊ?čÂb.iĮŧōéƒ`øïŦ7_æ0ÐyÐJœv0tâþĪĘhĸûĢ­Nsýõ}V›>֟…xJC)ßKĒ9þ!|ĻęÉgĀ˜ĪÄÐmÛĒųeŠsąXX M­ƒäØIÆÐĢ6ĪÓY†ã™eG“f!†þtCRîŅ>ĨĄ$!%ęĪÍĖÄŽ]|ÐH’mŌÅ'žÓ|ˉßķ]|4j>G‰uņņídÕîL&~#ÜÞ-øļ}øŋ•ïýîþû#ŋQïoo[Įõäãčģ|0*>x8Á>éorøÔ#0Üggȧ[Ė<Č)[$wG7 ‡ōQ*ï‘Íãfŧ^š˜EO͇ĸ8Íīð,ôņûSAčÏĮcŸ‘ÏݙÏÎ.>Či>ŲÓãïóa‡ž|MÓL&“édę‹#z<Oũ2›Î8ĒGĢŅd<™ÍfÍl—6Ïž™ÂC†%‰âą†N=Ē›!:ðr kŒĮcl„†h2-Igœ&“@!-qļ3/ķr–;č/šæ\5#Кžïéâc‘%%=ōĄ479Ã§Ø %œácčīĢäã›­8öáCBøĖ›†Â››ĸšiÁg<ҧėðÁ ߇NÛKøŒŽðÁÉ:僔Ëû“dD>I ķ|Úb4ŋā3ŊïO†Ö|PĀžišŅWų4Þ7™tņIÒ†uøāïÉCÎáƒ` —ņQS۟Î,ã˄ܟÐ.ųā‘žÓ|Ģ|8^Ƨ|2AðA><ÉĮ'‡č_~"ŧ…Ļiïũ’>ØešGu}ŽóK'šþu&ŦžÞ*#RđūūîZįų Ý|Jz%/‡ãsóA]§neīæĢįų |íþ|b‡ÎÓCãéâ“Ųxþ2>Øg‡†Ė™ûcóg僞$dðã';uoƒ` DAðÞaũß'Î@Ÿ>‰™6ØĩũūĒ>üD]ū €ũW €ÕX-€Õ`ĩV @—ô]ROôyŪĩæH}Vú!I―č:0zœrcï<ĀĒļÖĮ}Μ™YPT b#Qc‰%Ņ$6"æFėÄ†Ēą››kÆXŅØąGą!ˆbAQ0Š (v‘fCïÂē…-ģ3ĸ3lāæ&đíųĸî}4ũžęãΜï|įûfå}†e™SČeJĩŽ―ũŧ:DČ$ŋvþtčÉĻ­€þ +B‘ŋëXQ”N­”ÉŠËęïžo$ˆj ŋ‚ ïv،‘ŸõwqsqîŨsÐÔÛ9Z–Aŋą Bž*(pëúŧJtQā_E0õzĢ™ĸ­fr’Ģ}žû}ÞßŲýË!ƒ|Úģ§ĮĨûeˆaĀŧƒ &ĢAl@ Š%ü>ŠŌ|føÅgÕö>~‚į[6Ģ œPŊB‰uýöBķ6tÃĶ‘3Lˆ,R1 ‰5`Ģ’Y+ĐeS"ЕDʘ3î_ð[ēäa™ÁÚęo*aŅŦ{ã&Ï Ëqčé2uÖÜŊ=úthÃr‚NĻũ°ÔšIýęķ MYR3õ_ЈķąÅCM$ ‚]_Ģ­Ä)‚c†­ąežkēnZ_™Kƒz(šĩ–JąõC6Ö~†f$ķõŅR óË`ý,ĩb ģ’ĘŌâķ­õ―&o"eaýBVâeÁ A@ ÕÓBŠN^œĒ38öx|ĮÞ†þxi߀›˜JĻSFîYėååõį%ëē*õQPŒÁ) &YÔþ“žžfÍH-Ö`!BІ&íÍģē3§ˆļ]žû,hįžðČČÐã!Ņ7ÓĖ Ó`iÚPy2xKĐŌwáŠûŊŦÓëï^ ;™ũbÕĒŲ“§~wųaĄą:{ũēI“§û\ÍĻ héeQa!Ą'cR’o-œ5é›o}_UŌX·ˆzՕ-“pÆó"_ļ„Þ—ĪåhÁP}&hÕqĨo"âS ĸ5Ýō·‚B]‘}―mK+€$}―|Ū=ʔĐī5ZMuaæęiŪvķŌfÝšÚJĨmúÍM)Qę*ž čÕÚķm—ģ5U/}Įtif+íÜĨŦcÕÉÍ;)OŪ.}ąnî—ķ MKŽ›7kÚáĢĨįŽúÛÛJÍÚO[(ŦŦSĘETuö­pg€&#ïæTkÕøīˆRĨŌh4ųi—žņÍi“ΟX3LóÖv\LŨŨ•,sé)‘HZ;6ąĩÂ9m›·jûū“}ŋķīō§7mi’s/'‰Äú―ũZ7ĩfÄu?ûĒR]œđzšŧTjåāÐĨ‹“ÔĶđãðMgŦkę†Ė·ķbíš;ķni/úu đžYgŌ=žļĩ[Ë&ö-[ĩkå`mįļâÄ―]íýc‘JØæöm,Áīôó“·ēĘ2ãÝ>°†X·pė?}CZ^qðÂ~R†nÖg`ώÍŋöۓ+ŊU)äÂĸúÃ^•BēĶŧĢ€ķi1ÃĸhūLõäÂVkzNÝZQUzdÍT<ī9úđū2ÕĒÚk9š”ˆ…―=&æTŠOŽü€ÖÁŅéO.o· Å‡ž éÅJyEvvūZ–·Åg8žūũzĶZY-o@­Qe\ î åš8ŧBŪPüēž7G}ŋ€ŋ:DQ[óčˆsīë9į•Šl­óGē}―'=ŧ3õ}l[ëÏėyōčĮ)Î-tŧ•ĢĻ)Kþž{+deûÍōŲ™ũæô°ĮyüÃnÜ9ŧ·Ón„_–LWœÓÁ•ķí~&MþėÔ2–­ū<=ųȆ‰€‡Ēå%) Gu…<ČÏđØąûá§Ŧ+ ÆG!‹iÛwšŸ|xýž0(Jc4ÝÜïÝÚ|wøŽZĨŠ.{1ŋqZw8ĶčMuĨŽJV-'Ķ%‡―Ā, ŊUŅ—/l˜ï%Ô*Nn^īáDÂEu-%ũÃ'ŽļýÔ –œ[Ýø$…@iN‚ 0õÞīņĢw_Ė ē°ŠļēZĶ`ėü)}:Úó€måØÂŠĄËâ) Kãyŋú9>%ĻkM€ J0JĘ ĐËgӔĪŦ›ûûmmī5K<ēkã5Ũįó.ÞïdÍzø;åÓNÚīķ‚Ņ’„7óMÚtšą`r§N―†Ï–7ĐkUōĘZ\ØČ,ÕēÓ'ƒœŒĩ†ē*-‚ĮūįąÐÏkXŊ6NíņtDÁĩĒēĻ@°ïîļ:ĻLmŽ­K–i ˜9‰Ãėū‡öjÓ^ †Ö_ $þiDZÚbЈņ6 xÛō9ã&L>•ĖDOĀČgĩĻý†ŽÝēm6†ėŸōõ…{]šfãĄĢ§ð.N=ŧ›8Þ"IQIī +ēMį^~þëũŒĀŧ ÍüęcĄ>@Q‰ĩÄZIŽX†ŪO.ˆŠe$–iT^ąyëķÝ:#áeÔå{ŊDj…'ā? Åóoæ ÍYIhĀÕr&ā­,?yĢ @cfOEœbûüÉÛ‚Ģž&§?ūŸžïĘÓōÞC&J@eÔđÐ[wn_Š>UPntę9ūk ƀ•*ˆōĖž™“D( Õ(e ąW&^ˆŒJl›^ŧÐņCG[p6âėåGI·Ŋ^NĖ+fíŧtvjjÆóąnM8ïÏ`m7kóQßOÁ˜‘Y„Ø Ŋ,))ÐqĒ€lķ,ß4›ú:ððØųėųúZÍã+1*ÖiŌ<ŋ‰}!§WŦt&@ Ւ'8MîēĨߍ3jäȑ›‚ŊtüÓ7ëgxôāyú؎5—Ï?løðņ“§]I-Eâ8^ZJ‘Ųáãņa>ēŠð;ŌcĻĮXwŠŧ}11<Čŋ—Ýóĩó―G5isøsÓĄÃ'ôu°ÞïįģiŸH( ˆž؎™îļáŧvTY€ïīá#Féđhåî< ?ØËũĀŌ)éGýōÛĩ‘}<íß=Óņī™įĖņtĀ™Ė@JKĨ4DĪi ØXŽĒeRĘĖ_âéųíõB;ïĩ[†ömĸŅ€ąak>Ę―2sīĮؙūM{Ž :ļŨÅIĒ7šo23‚x%Äžž—īšēpËŌÉîŅWz 6|Ąy+OĐLÄ2į9žj 1›L]]ƏüŽeAvð4ĸe5Æø#þ̆ õõäsI֓,sûĪĀ™Áĸ6ōAQ]YōäáļØž5Üõ[w_VĻÕj…RĨQŦr3’ÄMsî?z\PōFY]™‘žš’úŠB&W(”šUÎËī[ qąņ7ï=HÉ+zĢPĐÕ*eöģĮ7Ä]<eūŪ’+”ŠęėīĮxëâĮO3d ĨüŊT+ð"ōŠ—éoÆ]Á›Û]‰―ņčizI…LĨÖĻÉïÆÅãÂæ–VkÕ*…Ē:?3=9%5ŋŽJ)—åd&%Ĩ=/Ŋ’+åU9YIIŊ*åMiōgÝš9u?u#ínÛũ“Ëd •R!WŠÔJeVĘĢxŧŲ›·^äkj4 đĒĒč§Ī§Ïē ĘqLiaÞýþ§ÂRđŨĨĐ*+xpũöõļë wĪĶįâu*Š^ãāž’7ŋV)‹ēŸ%Üļyũé \OÁëĖ;·o^ŧ›ø Đ·Ļ[&Č…ę—(į•* jŒR!ž·+~ *4fk<ũ7aJåï-ŽãÕŠ_`IĢĀį-ā, %)ëC-GJœŋĄĨx T*ĩš’']ÛZ1ãō4ĩXÏb°âwÚT*W6VŽ_[ÚlčMē4'üã`\€%­ÂRŒeĶeõ?ĒZÛąŠ0ōLXXÄųÜ7ĒöĒZá?ēŪNŊŊŦû]ÏDĩ@ Š%ĒZ@ Ðā­„Ā0 M#AoBĀqf“ÉþÞRÕÏŠÕęėė„( x[ ‚ŲĖwïÞÍÖֆã8ĒÚwŠĒäreģfvíÚĩåyž„Paa‘RĐlÚÔöpWK€bÕķlio6›Áہ@`{ÖrôQ-Ahžž0äŋ›y „·Wĩ@TK ĒZ@ Š%ĒZ@ ÕQ-@ ˆj Ļ–@ ˆj @TK€ X(Š‚āŋGýzĸŋ ZĘoˆų?øįaIBáDĩ„wx3§ÓjÔjŪÎH3 §•?{’"Ũ úûBQ°8#%ĩPņO—ƒBÁß|"J1Đ˓ž<ŨsxgÁíÁ·Ņ%ðw ƒĄ&>8ƒūÎ`ĸ–·üģ17=ĐļRļÚô'É5fGTKxũ`Xķ"õÚ_æÍ]šlņÜYsÄeŠsŌvŽÚŠ,‚ā?D,ý `õ†Ų,MgĻ‘Ë&ÂÆÉjÔrmĪcE‚ïŌÐęZqøDā5*…Þ`†oY‚Āá þŊ…AČsĨRÙðwŽ„M û~õ‘x3 þéZJg0ýšwHŅžâbāÚË·dL]E Ŋß­JCSDĩ„w)CUn…ÄiöÂ_uŊÛ4UĶVïč› P€Ņ4ƒĄi ZnŧÄC–e,7—B Ë`EYR‰ÃúW’Æy,4īZō0īåF†uH(A(œcIˆÅE 2â7oŲ_ËJոŌî_9~"ö§ tl*ÖJãĖ…Ā/þŠĒ†Ė– ī4Ú )ŠÆāþņ „ĄYASKb-æïN§ëcÕx0–-‡tÃõĪð,2xéĘ땾D‚Ä„ŒXžĨ‘ ݘ_žÔxĻąA\ ‚ÐÓÐ;ú›ŪC[Åuid)H<)K††w­ą0ķū0 ĻeKVÆËą°zËÖ<{°x[X>'aqŠņ}·īfAāLæúíԁĒŊĮÏUQČŌĒļjb,=ĸ‚•H ØŧBAlŅŪ9eK$ØŋZˆ3ÕÞI°ãÛč2ā‹93įtĒīš:“ĀC@Qfƒ"r‡ŊŧÛā1~A…:ĀR\ôĄõ.ƒÝūä}ævÃ0…Obf ‚ýø„b%šęŨë―=Ý\ŋ\pðЁb(,:zÅŲÍË\ÝÝĮN“ĨF,âîÝ;n°ÛĀoüž”éâ_/āü§ŅģÝ]]‡y…?.§ôŠ ûüœ]\ŧz_đ›ąccØÅpOÏ-YrŽ]IUeÝ Øģoũũ3F*ĄbĩWBw u0m]ĖĒō’.Ítsuî‘TL!Ôøý0ԔEîö8ČÅÕåÛøôjČiŽoš=d°ÛW#ĮÞÎŪd$LiJüÎM;VMtvąïĘ―K‡;t]uāŪ‘a˟ÅoÛļyËĪqû^þČ%ļÁcßÏÂӇŽ—øš O/IūūsËî}& üÂ}ÛĨT3-aĄņúQŸĄnnĢ&ŪČŪ2 “üÂîUĮCŒáöÕhŸŸŠųÂäó?œ‹ ˜8zÏé{\­|ïü.îî^s”D7HSĩ?môš“ĄÂ"ŽŦJ XēķHO>ž;v›ëĀUûNŦxÕdmô ĘӘišŋ}tYȃ"ŽBܰ„RïY°å|bVŲ•]s7đ­$yq‡ũEÞ{“ópšį07įIÛ"T ęޜÛĩęø‰ýž.Ãū^–§ō’Î8ģ}Âč}ũˆ„ÐXs3>ôâņcūžcĢSeęǧþs‡ŧđđÎZą5Wmlð;ĪĨ,SZ)‘˜cũlßŧϚYû ûsLĶ _Žô›GG{ļšyL‰ÉĻVÞ[ŋ/bËōYs6GŠe §ÃŲ<―7åË9„ë‹]þ]ķ,Q-bhUÎóØļȀuĮx§/;;ZcųԐO<ĩm{‚ōØŲÐîeįVïčÔÍ54*zŲ°Ē6\ŠCĶÓŦķ<ë;įėé“#ûõ`ŒU‡·­-í11˜―7,hÓęVÖĻėiÄž=7æ‹XÜ5oųÚĩQĒg%žōų\Ÿ€ūë‚-éŋ{m@ąŪî֑€ïƒŸ.:īšw7§~CGõïØkÃÆ‰í›RļBžėÚõäîú'ũŅ+– ēĶlĩÉį/?Ņ­ÚáWruïíŨJõ|Îâ]΂ƒ|úîX―=OeΆ’Ðæ“+o:šî·3čāþ―;2ŨŽų§6 8ūčëîó|6%éymÉĐĢĮīÝžýž? ˜3ýĒēũ†™#"ÂN>}ĢįÔőĄ§áˆoķ-đbåÕGŊŊ‡m:œŅbGHø‚1]į-ڔRĒ7kKN9zųOs‰;{&WËgÅYsFíwėô˜.U{ƒÏëý4öāŪĻ‚iūŦ:ë#.ĶĩîåėŅĢũÚÍ<ú%FnŽũ ßģfYÄņĒkxÔž] îÕîįÍĪD‹*oî đ?åÛímžķ…xŧYāh’%Ü―§6 ÁüŅf™†Â= díėđû›”ų!įĒķ&čĖš Įcktœ`å°ðûƒ§~Xiˆ:ô XgE›ĸxpwtņŽ•+Û+nœđ”Ņöã/†öpšâŋÉÓ―'m'ÆŠG·>ýûũûó–­ŪïŨ,úÎ7ŧՄ“§Ž7Ŋš5cEˆ–ƒßRT>OļžQ!*MŧsčāŲŽÞ“í^ŧte›·ïé4r~øņįö›ũ:čÖÞÃk‰ïtw`ĶÎũ?wfwŦōČ˜\ eĀĒZĒĻžü—q·ty„…ŊpēF€fN“‘-Ï{îįģ"1“Qį”rPĘJ7-õ;›Xj䊕ffČī1ė…] 6)ÓS5•ŲYïg^?ĩtÝŅW9đLÅC 'įÍB‹î}Į|jwĀwņÁ{Å6R˜‘Đ,->°zņ‰5ęWå €4‹Ę^ÞŊ*,Úŧ~݁;UŊjTŠęԒWÎĸ―óŠÚþþÚûī™ôÞB IHč-Ðŧô*]šôÞ{‘.MéUA@TTô"MTP)Ō{' é™ĖĖi{ïwfB‘ûęûÓũ§ũzŊįóĖsÖÞŦœÉó|9œ9ģÖ°QmS*Į”O(ZēTT°Ŋ_bÅr=ZĀ™ޓį6―øï{ø‹/#bjī’n~õå§ß˜‚jÖŪ`ē}ķ}î˜Uŧî"db@5 ‚f,œÝūQZų؈|kķUÂYXĨRaūā*Z&(ūBŸqþVaðÞKEĮMÐNûöÛ;EĢ_ZzaĀ…”v™ļxPģzÕ*‡˜%Uãý:·nzpã’É‹7æÞėQ:!>8ēLl‰@/_ũïūØ9jäĘŸŌ ­Z.PŒ~éđCj þóÐT­\ZŦÉSÖ˟1Žb /B(CXtóŠ)SoüĪiëÞYŋnq·œó{FÍ{ŧ^ŊŨŧĨÕx—ÜgÜĮŸŽŦĨ]ę=}Ģ{HáĩZô˜!fĮĻŽœË·1-ũöOį ŠĀÕôÐ*Ŋ$~:töĘ„æ‹†tfýÍōˆ._đ„štXŊĮRÃŅý{Æú#į1€™Ü8šŠé:‡x`Z‘MķĐ Ã3(Õr e•€Ųۏ·ÛóōŽP˜ũÄÎs"‡ž ø― @4BEFU"v4ũĢ­Kž1kýîB*iZŪÂŽ`ĸæ™GĒaĢ^ŊQ*qáū9ëðķæé!ĩĸYPMa6Mä1ŅdU'ŒęvŦ• ·īn―QþwŽ~öôá“ß_ŨAÓóÞđüõĮŊd_=wōÄOûŨmøäûkvũ5›<‚Ë5íÖüÔį}ûÃđ‘žvE™WūY·|û―\ŧŧčæÎs‰Íz— z°ûÝC/|óåņנּtŲZdĩųĮĶĶĨy}ļcĸO—Î;x8xķŽŨúÉÖ%ãg.^ąxå‰+™>þ!Y/ŪYýaķōTBpþ_|īkįģvāmēæTSĶX‹E ŠKŦ›ęþÁöštîčÁ/ģŠ―ĨØįĩÆ-oX8aöŌĨóWš+7čÔæøĮKŪÝ8uúœ*’Ŧ•ŲŪĘ2uĐŊÝjĢĖĐtEšŽ2äe2ø`ũŒđk–O›}ßTĄY―ΆíšýhÉb§ûĒÜŠëT‰diē;ÝŅŽvŧB­”Ž‘wÏðÁąsgN~wņēbÅjQt4EģŠ:oōōâÔmóū—ųčü—›ķ}ō°@õÜÍ"Å0Æŧ7kŨôėĐ;mÚķ äY‰Ú+•ū3ię‚õŦfĖŲũ8yā+ޞaĄ^öUKæOë7öÓŦŨcÏ| õŒiėq7—$ÖOHHj˜s3=ĐbÓwGvrïöõoOþúfæžcß劎ڋœ…ŌdÕŠRÁÍĮ“Ú·Î]tęNBØõ2Ŋ°ðüŊŽ­\øvĶgb“TóĘeģ7l^3féŅŊv*íÁŅgiuÅfs=ėElVU՞ZT"hŲÛßŲ~ôėm]tww3‹Ē{đø’?|ūmĮžïđ°āá­3'Ýŧpïėņï>ąiS4 Œ: #ŒģÝûē_‹æß=R%Ãßn„ `ðƒįųžž|Aā}||þĮ™qŒ1ÁÝ7štLа@ ÎÍXp +WĘALåĶÕJß―sËŠā°ĻļĘ5jVŒĘzbŦÝģOÝļÁ+ļ„7đqįXĶÚøA‚<Ýã*ÕŪāĮn=|L°g\bb·ÄC#ĒgßŧûČÆöÚ'ĨīāÕĶ^uõÉýô|ŧpđøØ0_ĸð˜Øøð°& R―í™ũŸäIQ åK—HĻ\7%<+#—ðž‰*”)W.ĶT`V†-ķRye8ītŒ/'ÛusB•*eK•Š-ÄcQ26ūœ—g@ӆõîecʖņ4ŧyÅÆ•/ã!`Æā? Œqnn.x{ŧþûø›A999ðÃ@’Ī[·î˜ÍĶččŋe89æE‘EQŲģg%‘Ũ™0$‡Ā‘e]2Ič…Fë ņœP]Uu ›$~nq8AļbģŠĻ”ËP‰)ēÂI&žjēFĮK.\ Ņ$áâôšŠ$=]øYĩÎČ*E^QƐĢNŠ)aĸW@xÆË‘uf2Iā‚hŽDÔų™ð Č*rūÁŠ,;L&g"íÄûŦ6†7vŠqĶ@ŋāîúHÁU€ŠČ”/šøâ”Œ*Š&JQ2ŅdÂT“U Ï!Šk:`‘ú‰9jx~Ū<Č[§ī;íÝ~ÉÔ!’*3Ž…â ŒĸyÉÄ#į1š3<ƒLŠ,3Ũđļ™+ĐË1ĒjDEĒĘ:u~DˆjŠę°HvĶ>ýį§ŽŦ2qäņCN4 ˆČŠæ8Įâæ™Eņ‰đþR˜\§L4ž{z4U刊:ĸúŪÂĀYžæČKĸ#/ƒnÜļÉ‹ŠŠPU͐Úŋ‡Ôþ~ B…Ų2 qÉR‘# þĨ0ŠÜ8Á=2ķD°—N(ümĪ–‡ŋŒ1Ŋ€(Ÿ@ ”0øWƒ°W­ĢÄÐŲĸr Đ50`ĖĐēĸ.ŅáŊ‡ņ‚!ĩ†ÔRk````H­ņ‚Bˆã8ø`@aŒÁŋCj U%3ý‘Č3``ð_ UGÁĄá’$jkH­ÁŋU.zį?}Ÿá+`ö[gfT‡ĸ 4ŠŠ‡ä ëęíÚ_ų7ũŦĀc`”1Ā3į›_Lƒb”ē—ðo„1gI†Ôþđ˜D<"ŊČáfÂ Ð/čBĀXîõCķŽáĩ^Üĸ!Ø5\Ýã‘óoýƒ€ĻŠÎ°$ōŋeØ"0ú :Jĩ;—.på"ļ ?^(W1Ė‹'”=ĮĀ(ArÓo=(b˖°b?]Ó(EÁŋĖáŒŦåēĄĖÚ?Æ1"b]@ô…‘Zd§ŠÆāĮ)ķ;Ų?n―ÂP1Âė7ĶÐã9„ü[ #F~ņâQÄsÛįŋŊTžŅŊ…ôĸý‡īZ h2‹ûy,„xšĸþŠI|ĘÚņmŅŠŅcël<ÐßO$*qyé–Ŧ yxy —ŽėÚzÞ}ÉĒ 8‹%üÁmŸ\ũ>š§7(”ÁŋÄ â…Õsæúĩ;2Ŧ+ĶĐÚ%ˆÝߞ“oŽ)Āqðœü|ËīŲ[™NB+wãD7F üšÎØË:ëåÁ§†óŠ9 û3Ԗü Qĸ.u"ĀE/\QÙkŪ#rļĘéšF(ÃÏ!Æt]bûtÍØžļAÃ;ÖR…ŒcTŨu„―|ņ‹b~ÏNaLsŌWŽŸ[iÎęķž‘Yāy&: ‰­Ņ(L4Î! ŽL„þ\Į`L‰#Ė9Ē8ÞQ ÎjŅ u͝ÄĀ˜Ë•90gr „QgNïâ%äŽ 3æ*ļpÍaÄÆâЖāĘC‡―þd ŧ\ĘGoÝ.!­IĐšJÕNsūR—šųāt^þýČȊ>~eƒ_‚a“éÍþŧGėąe`Äŧƒ#v _Ëmbį€ŌūHĢžxq2óčá_B`æĸĒŨõČÄđņØ$‘ÏW,^ązÅī~ĩk6ø‘sØ"wņØæ6MSSuýð\ķåþWS—nŸ7ĶOŸYïäÜÞŧCjŠsØâ­líŨ†-bŒŊ|ģsÅŧÎkßō­Oð‚d{|yýä6I)͖ļX°gß―~ãåđ‡'ûwk™–ÖpԖÓ/î' ĖėékĶ q$ĩbŊÓOÐūũ„Sũu“IßŋvÚÜũ/H‚uŨãęĶĶÖę4äĀåtÆqyĐÎĶ՝}Â$fQā/ˆCøá™c+—o m7ŧĢĮ• {!5köüEá ^ÛžaARĪÉÍŋd“˜ˆ†í†ŽîQŌĐßøwÞYœūé―n‚I€_‚RZĶJÃøČΓfķL‰s„ÓGŽf•č:ūMÂïėž'Cæ '~žŒúdĮŠûîŨūģ}HjĪŠ‡ĩÝsÆžs―ÄöoZķ―ąðƒk%jĶ&åœÝkÎūí –žŠA­2L‘CŠķßõþ;]؅Žšwĸ‡ÃkŨŋ_ĩïœĪ‚ĢÝŧäRFķũ8{ãgĄ{ßÚąûģäŲ {†eMė:õĄØĐį‚ ß>tēÔfĖúY­·Î™óõ\ŽÃ†ÔüĐ ÄĻĶiyV2}ĘīĄþpþŌãlËšu óó‡ éI‰úkūTUėËķ+cßU5=Ë:ggæðO>xH(E ŠøŊėš •oˆ,ʗũ~ģWčōÎÉĄH&ÏsĢ6ĐþĢzÏę2"IÝømCVô [Ü> œŌtæî!Ž|f)cB‘ĄîýÓžk'ĖzÅ/žgÅrL ‹.á>ŦkĻcے>A ÂƒÜæŋę8 ŅĖÓ 3“‡0Ē…_Ú>Kz†-jå•TĘ}ÎŦĄËŧ4ðÃîîÎĨ>)~Kz„.ëäŸä‰tų˜Fĩs&ĸŠoY3ēiĻYmĸ‰õ}'w]Þ#ĻS4GudžØÉ™b@‚I"außĖ·Àå=Cg6ô öĀMjųUOð™Ü)ĪoA#ðRŌiüĒa­ÕĻf–•óéÐĒņ‘í+§/ۜGx“WdLÅōÁQ1ņĨ‚―ü<ør߄1ŦzœoUs(ãüŒy†DFĮ{”)_%*ĀĮb/ŠŲqЌAk'Uô#ŪĐ:cÐÔHi–{öā˜ŅÓŊ挟Ü%™ß|zæÞÍCãÆĖüúæýËn€2úÝu5ÝũvčwðõđëDyPdærOš:óĢŧ™"σó,XŨĐËš7ĐWĘß'ĨÛø)]›VŠ >IW,ŒuēŽcƒ”3†xø~{æÂqˆãđĖ›ß?žvûëŦ'-ųäŅe[Vž!'†Ôü‰ DU]-"ļCįÎE…+—.zgĮ;'nÕŪ}‰ IyV;ÁŊ Č, ‰CBî2 ˆgL7IíŦzDI\‹̓„ra^Ë;ûy[ÔčR>“øFðÏÚ"ĻYÉwHӀ@4ÜąQČ ŠĒ=[)ë―ž“··ߥQðĀį–ÎÞ~&0̌ōĨĖéN 3û™ĶĩéPN …Š™‡Č`i|·ÔVX@§MK2{đq­RĮ5ó‰’ ĨfВîA ^ĻFyßÁÕM^îÜ+)#›xEˆRÅov;/wapËā ‚-[­UŲyG/‘CUc<û5H DK{hė!y[·Ŧ ëT7˜óqį{5 RĮ#ÂĖwkÐtÅÖéšgû†Q'ŋk„béFžÉÔX§ÕĻ1“w~}tŀ$BYÆÕŸÎ<ķyųÞ―uó&pÜÅýS&Î>ÝĶũ€V‰á :}ttMĄ” L8?†]0œĐU Čđđ„”ōeŒÉÍ á ;õ;eÚ?ŽŪnT>@W•"Ŧ0fH­ÁŸfQõ;…(&ĩ}·ŨÝüéÜÞõŦÃÃk·é}˂re#ö;ĩI@V―óhÄGŲgōqB _#Ņ3œ+ō-īT”āŽ({žÝū’?`CúGqûjĶí{žLĸ8kÁy^Ĩ}û•25ŊōÂãk‘ũžSģo.8Zx“ @ÕY|”[b0ZģåņĪyĢÞ/Pž„“IgO|?kÅakj’‡/4F|œÞcWöĐ–y.ŦãšĖƒŨ47O :"ŧwgôؐūú;Ŧg)ī`)5Žßž;ˑtų‰"ĸßæÞX'};·ÃÛé{/ۑ™‹ ’ËŠYÔ$Pęfj*pN…Qoy4ú“œŧŠPΝđnĩŲåU‡sķ]%ĩņƒŪa‹ęÓa‹ōóa‹TÐrvîÚyâŌ#äæi%‘w‰+yö‹ïėûŅnÍ+ĖļåÜņŧįïž;qęŰEp[Ô(0ŦÕĘý71æóždÜ=ņŦE:ø}z!’­ŠŠŸŽ]V‘SĘmŨ5―đtÕæm'­MîXÁ+Žr­6ïčjZ–:ōá+ŨŊŸ<~ö|†Ęad<`ð§@8ÝÛ Lšu핗•þÕ_ô08ļD”BĀ$0F ‚ßF hWėDG<‡@ĀNJþ _œž{ P}`Gŧ Ä!ú PËēĢH$ŠtF“5ŠQðāÄEsZé›9D0"Š/i‹‘8„€Ųa@ĐËN˜Š3ʘŽS€™~1—Ãn˜ÝyĻk0ƜQ0%ŨlD&LŅ™N™Ā!ŧĘ&;ScržPaķŠ‘âØĀayČÉĄēõ3{3ŪÝŅX DŠÔ+2Ąœ€œ§Ė‡ģ$økĄkZlŦáÃõ]cŊ Ž‡':,q ä·ÐïsWÄ$ũiÓЍ@õ†Ý'JŸ<ēvï9Kũ<EÜÆï}įėɇEvŪUÏąÔß_įáÕģ‚BM§ÅmÔą)dāĒõŸ|ņMvž%ĶNŧþqœĪiļlÝŊUāuÏ:mûĮ f >aėĖię_ãíeíÃ%U#Žxē}ĩNSŨ†U?tōōėãį!ÛXûđK_igÂSÞ~;þķÍÓrڛËÞŋ˜­Ÿþf‹k—ēóõv#§j‘å4 ZŒ˜RÍ;VÓīō-G/Ūl*3Öījīœõļd› “:55-ūÍĻŨqI•yÏ|ã­:{?øņî]“[I #ũ° ÃúúEzņŒ1Cj þ#Ŋx‚Ū2žwæ“kx™ ,-īf‘ŅnՂäēt@āŅՇöĮŋýá[›·úáíšÍkp OOÖ­ķįŦ™Y$‰ļ8–Dä|ã*€jLč[ÏGlÝHzÞÉlåōcčÚÐû!Ï·­ä.æ[ēō*öâ8ä&ĒGđę™ûz•(nÏųB!@ôá IÄ@N6#Ð5Æ<Ý:ÆļYï*€áŊ%$ŽJÓŪa‹ŋ‚ˆŪhzXÕb‹ÞŽmfmŸ[$æø>ĢG:ô_ü0_r hšĘEĨUU ’›ķÓUY§ \ ĨSÆTŽK4•â˜)Š*„Æ5‹âTYF15Â1“ĩt…*7,Î$kžĄëļRZ›*im\ĩ:BDv‹‡ĢNPÕĶ]ЁĢHÚēϐ–āĪajŠc Äĩ.™ØĪMEæ<Ŋ ļĶ8(*@bÕfÝĮĩŊ):!‘5š—į*ų7ï;ĪyqRU!(ūk‡Ä?sXĪ!ĩžd)ē Š%<Ėcā°§ãĨpžr2mųO2™ėļcð8Ãö―Ū+Ū @ŧ|{Í^ ŅčŲkVk=w3Ė'hx ŋąą›aýöķŒąK·Ž>đóČ^Ļn>áÓ"  IQՙïæÜ|Ēo9áûĖ2ãݜlđtĢð|YŸ>i>EeQ‘ĀĄœ'ķyûģĨzuĐgĘ-ī}ömÞĖ]Ųã{ KzᘃÖB,žšj}lJŲđ[EĪ€rŪ?ēóŠN;ŽÄÃÔ쉙äYĶ~\ø(‡ŽúäÉĀz>Z˜ėVëôrē5v󱝗u„áa†üčēE]ņá“Á }ĩ .ŌéÁ,åUĸæ†RĪ‚†Ļ#Ũ―Bú0[~ïī\―’OÃyí_ėQeNTYþ'‹ėīž€ęšŽkā‚čÚKAˆ ŋāâDS•g>Š+ŽKO‰ËĶ[\§Á/ÁԗYjWäį üœg•8_dCF='Ėå‚ĢeEÆĀ…Ū<Į(y9ŽŦÂ?™ŋËÄ\cb.BHĩå/Ýw}Ũ•0OÁ#š[ÁĨŌ^Á&Œá9”BVŽ|7Ï\ā]ņ&øĘ­Ū3$ äPYsūĮŪĮĪ€Įvnx$!°čŒãÐó<ŠÆ‰GیLĨ(ÐESņ/Xa‡x=ËN ä'@ŽĖ81Ũ UcŒ+.ƒņ<âąŦ›0Ļ:S ‹ŠôØ>dH킁%ĪČb)*ēÚíēÝf-ēŲ5óæåģŨ3@P ŠŦŠFā? W{ô›z‡ęŠŠS“[—ÎÝĖ,Ērîđ/Øu„ Đ500øßÁ‹búõŦSF7y°~݇;aüøy+w~đrņ譇4„Á…(‰gķΙīîs0üGaģXmęĸ$—HÄï7MŸēų˜ Žđãũ~sþ0iÄēKũTĖaCj þWMũ‹ˆė;äõÁ―:ú°lŊ2Í^6ĪG‡Æþ‘ŠŸ‡ p‚ģÉ uDVmÐ8)C˜%Éd2 <?!^ ’čjŅâĄߛVdŧ{jáĪ9~å[ëœ0ĒsËó~•GlqdãŌû6lđsrŲôašŽïßâíy#^)žĸÍÆsõ™ũÖü‘mNąĸûōũŋõæÁŒR̆öÉú`則ęí/ŨÜüãœÍ[šĢÃĢæė‰žþ`ó[{Îu;-æŌŧëŋžé_ēBZŲð&=wn›fƄ1†xO-ĸĖšũūbúý·f.ÚđqG6%_î\šî–æãÎÝĀČxŪö_ƒfŨŧŽ]?ŪsÁŨæm‡óe@{cЊŌŪEĐɛ'MÏí7v\ \˜ņÍÞã}^{ŊSZ]§<―;ĸÕ#vԊĐč?äÓýŊ~y!+'Õ:L™ņzûëG3cŦtž=åuvý{7|EĻDŦÞjÂĪŨ[2KÅS‡ŋļuó‰í›ã nŸTäā>{?=ųíåš­™_…óĶ“/~ą{›ÆÔģ‡vį>P损w—zÜģA*óũÆęÖU<%š_&:įSēLž'ŽŦPūLļ,ë €Q>ĐQ—ó|}ā’Ôļaœ–sø›#‡Î—™ķ8>gxqÆÃ^ĸB ˜þÞn@AÓd`.6:ĖĐÐfĀÞ76ö}2dԊ‹đ:Æņ&Ï`·Žü,ÍuYȇÍāIDl–ü|döč‚ŲdJœ jš.0a"]ģedįó&ÉËŨÏú8ËibJū­‹žsUŨeãÜ<ƒ}CŦ >bþŠå;Þ~MRd ƒI *ĄĖˆ]U ŽäđR1 ąĩĘðßvöî+}&tŽė5o`ï‚ZÍ=ąÎ€!ĩ2ŠŠ(„‚ ]ĩYŠėÅÝđ‹ŠTqNjÐ%A=úO]‡jbcT,`ž[`“žÝ?Ü6oüÂÕŦW­ûî–đįД[§――cûsæÅÕŠWÆĮnŨ‹ Up@tK‘Í\W‹Ž@óÕCó—mÚķtŅ'hB―Šá•wÔî˜ģzˊ…söf„§6Ū Č6Åą™0į EG5ÚõņB—žļüāÎŲ3įŪę;j-nIŪڋė‚_:Є“_ywcųšUJ:'5īų:.ĨiĻÝðÉļt4xģ‰cû” )e†Ôü1P,UMn‚tUq]†WoíšM` ĐÐđ+VeUoVÂe‰oÖ.ąYą&ŦŠaT'\íÖŊÖn]žĒ( $5ëYÛĩÓ5†Į§Dsš,ëÞáņĒą,+žOtãöeˆŪŦrŪŋį+=8™kÞÄVo4đF#—ŋ.kšÉ·dĢve•P“_jóMՃ+ΌۘNĻ,kĐí;đ΀UhØĮâV~äčDF‰ŠjėŲāúČĔ’™"+Ũųĩ§ !Q5[”r`•æí@ŨTmZEhŠōĮ\Ø­Á~ĩđđđOž<ĐXą"üWsþüų   ??ŋŋZŋZ„PރKw }Š$F0B~ĩÆ―Zƒ?ƘoTBĩ„?Vgü ėĀøZĖĀĀā?F)Ąþ8ˆĶę„ý~9EŋdįėY7ϞŋE Đ5000ø™6Šûæ[yėūIĀŋý&†*Y,ō/h/dŸ{ÄČÝv†‘!ĩĸĸ Wŋ‚í aė48-‚À%Éĩ."WwþY[‘Į?kbāÜã\Ü$:ŸîđŸåzŲĩÁu(8\…NpRžŦ8QąE2I‡áâD/"H"Ïáđ(_!­UÍ2ūWÏ—ŊĀagUŽĻÏē8“:pŪðĒtiÍâQŧO:Lșse0‡å°; tãFA@OOĘQ4gHíoÆĀĀalÍØđpLÝÔīf}Æ~y­€łûįįlV755åĩ…—óĻ’wsZώ)Đi]Ę ÜŨ›į―wō‘( WŽmžņîiQŊÛū`Ã{̇öLŪ]gÉgsŊė>yÅ=pïŦ}ãG~ΉN‘Bœ`|vöPG …š=FĖŋcČŧĩtÍúÍ[Ö$§$w2ũrŪ&‰äóKÞ\žd\jJ­W|t6K’ĻÃēüđŊÕéöÁŅ'{iú ŪŽݧŪđ[ČĢ‚K3_úމ{ežöíãFîēō|ޓÛ9…2OķMđaãânMë6·ņÜũŸôj“R·ÁÔ3OT‘GßėYé8ĢšÉŨväÝ|ûƒ]ŧæNÜ{Ä­ĪÜĸvBßšiu_ŲüŲ@ĀxréÓ) V?ē0A’ŪY7kÝ?t†Ôþ 6 úŽą—ŋc]°ęíĨӇī+ãîļŊoŧŸ8o؊9=ĐßÜ펙ŋ|՘þ==ąvíôcō1æ2o|ųÍ71Æų7O.™ē, ķÓØ~ÍWÏþmķ}|ýöcũÁķeíü{Ņþˆ14…Ôč9ôþ„9ÛǚÔðÆÚųc{ß-žrrïĶ-Ūô?öÎč0þ&'—ļûņ܏.`žđų”^đuįĪŽ–Õ“ŨgŧŨ)UĒNãV}† âïï:üqĩioÍi―iÆļS™”Į@)čĸåŪMûÏ=â蓕‹ÞU%/‘CÆsĩŋ„AÍ:ð@ï4PR…ļ:ácV}þÚĮŽÜ<ƒæï•\5˜°ÜüâāwhħcSŠøhÅXE{"{rXŠÕZŽŸ:ž+Š|úČ·rKNčV}ÜîJ–9pĄÔĶU5uUF™G`HöÖóōōŌ-D– P™úÔ]šfrĢŸčž†]6?ē-ĒŽó”åݚÕÕõg_?sŋČŽÓī}ÛĨÉw>û’‹ZĩļOlˆÐ{XŊũÆî|hÓdøâ[ũšķkÜąÏꃃ›•%ÄĘaė…(#a#ŽjV>g_Ph•Qģ_móø-áÐ―LŅË,=GyrĮĶHėY&Á7Œ‹ĐVĨžvįĀ9™!­žqȞ‘™“]HĒĒšęYģ[“€ũÖl—Ôø7WEŒþ­ĨÖĀ`݉ĩëOރ?‚uŽ3þ›Á"ÉÍÎĩ "į(2sũöáÝė™…Ąˆéˆy$įZ ü€é”1J( ōÃĮ—˜đ0L˜Ä€höĖė|_AHjÕÃwåĀķ}ĪĘígÅûK„čXĩ­ģ&­}ūxtovóĒ„8 ļ3ŠSx’‘Ĩb_HF AJ~>!%―$2Q0ĀöĖ'…*„IJQ6%"ρ–ûðÆåĮ’›ß™ŧwŠ4ðæ_Ėv&Ā  é @įƒóf-ó›“g6ļ4!Ÿ―'ð ézžŽrzš‘˜ŌĶOó_ÉÍ?&J(ļŘ]^íŅ{c›đƒ•o5,.ԃ*ōßZj :ûJmŋ?*ÔģÔRBÅ î)5ĮoX-Χ{ÛėÓĪYÝũšïšŽÖPxtôí·įf§Ä―ĩóF―ūýÜyóüī•+CđôŦûÖøŨčZ!@úÄjĨŠ”Ša•;Äū1cĶŌ:öĀ–=ĩ“&„ģôųczÝ-1ôöڄ.]úXîąn\;MķĘNa͚ĩH' €ŠEEēFĒY­ tÕR”ýĀ’{ëâ‰Ë7ģŋųōnBđĻsÃ6lĐŅđaÍA‰qŧÞų8aTS―čŠ9:އ1ŦM̚æSĄE·RóĶ^+ÜŠÓeãkąß†S5ÔtōÂM›åßūæYåõŧfļyMž·zįĀF…ĖdæÝžŧMÚ0ŦOcû“ĮĖėéavk9qŅëÕ}Yt“Í;vNnW‰J8YНŋöPq‹ÛąvEÅ@8)ļTé”Ū-k”a„€Šj\ŧ)óÆ·,›i1Ï\ēķeyßk OKŠ ŧ{7Ģĸ„Ĩó{Ī…ēÃōÞi1Đa•Ú6‰ Ð4Ýčð›100`TGĶZ­û'·/ÐUUw[õÖ œhŠL§CŊ!ðėyW3ą cE,Đņ°šå%§îŧzkåWĩYũI“\Á°*ÍÛĢg―°{XŧÛ=ŊÍ7ĶwÏxÝáĐkáöĐ Š,û5ėäęõ:vÖUY§^õštcŪþ )]*PMViæýj6' @Wč;xϊŽĒĀfÝÆ5'š#…gåN%@Q)ËŧļæhÞ+Ãzx#Mfð{0ĪÖĀĀ€QE–_6ļūģzÁˇNõ‚į „+6Ÿ6ĩŽĒЌÐb‘<š-[[-NW5øôįū#Ŋ*3FŦŠŒ1ĪĐĻûØŲ(ĻĪė2 õgå1ŠË?OÏ^:cŋž_~ú†:ßžžä<å—y–―œ‚ĻÎģGXÉ3ÖÄŨŠr‰þïĮZ„PqG‚_·üōfÆĻOTbuÄ^ôdLpII Ģ„P§ eė%žIūÕjøSJžea„â2•ŠĨN—ŋŒr^ÉuĢ(!ĖøĩØïÆĀĀ€Q]ÓdŧÝņR5RüãVDõœėl‹Uæ9ÁÏAķ‚œœ+s,đÖ0Bēĩ ;;OgøĐ‰1B vĸŌųëEĢ_ÍMB/­:š1ø Âų]:kH­A”gĩú‘ƒŊĪÐðw aĘð~_:ļ_ŊŧNP,0ųŅĶ9Ŋ7jõJãķ―7wsžÁņôˇoĩnŅļIëKwœ$ˆĮŸsõ“aÝZ·jÞrÔÜÍų:BÏEY ŸN5ëë{"ĩų€j-*’ÁZƒĸ>ōÚ§â”ŊXĸZŒ2øŧ‚fZÎwũĨ6švëÕŦqŌ’ČŽ,đúGöÁįĸ˜Ø&dú˜i7 (FÅÚ)ĘwNœú‹?þlIĮ-ģĮ}qŦPÔÓĮŽ'$O:øųFvnŅô/I".S„)˜ĀŲų@â0ž ü3Õ$Išööē‘ï~ÛLâxÉä@z֊MNþđáBž(™œH=íÆðžo‚Āá—[.ˆOĢĄâ&ČÕýāE‹gŅU’Ŧ:Œžz9‹3ņ†ÔüŌüAF‰*ŊÖ.Y§ŨŦĩÕĮnYï?ĸĻ|ß~Ã*unXë‡=G­øÅE,‡qÎĩ îÞ49ĨÛ—3.ė_4kíG‡ūÚŧtų–O6}š{įüƒ{ ŧ‘Nîë•ZŊnĮaŊåōǘ{ãŧqm[ÕŊ—ķ`Û§*įA—2R{î–Y}ÓŌę―ŌĐŨŲ‡Qâo}ýņĘ7ßZ5­MJ―–k]N@a―āúŠYcū―mqČĶõáéYK7ÞËĩ}·oiŦii ŋũÕUNŌ/·ā#UiÁííoN=ŸÅÔ˟VLšąx`ŸníķŲF†Ôþ^ ú$ŽĐŅ~%Œhþū0Äų„(Ÿ ęÛúŌÍ·ģ,\p šųýÉl…>š|'ŧ ðæýŽâ‹G`É)ōŠ-R]§,@Þý K^Ķ@M&L‰ŪD'`ÛcųŲÝWīžO?=ßylÏZōŒáà LŌÞ5ŦOg3.ï‡%o|˜˜X+:"Đ~ģž#ûæ~uäōzģVŽŊ]0iÜōBDöϚõ_uÅÛoĩIŪšÎ˜3ΈåũWO^sÎ{ÞĘÕ­*ˆ=†Íŧ™Gä'WW―ąøNx‡Á JėÚúî] Å1“ŧ_þíï6~ø5CėËũßđô@{rfĮŦ3ĸŅsöĘiÏ7ôÓģy֌KŸ?íp`ráwßŧkeXyēoõŠëAņC&Ö€1Cj”°‹ū€ĮŲũŽ|ĸ À1øÛˆ~•ŨoyoųĪ~YÛįõŸ―ŦHڎW‰žzĩu·ێûčÔßŨ?uā‰ēB\ۚ1B€`—ęš €ūLAÞ­Gū1ŽUÃ.ƒŧgdÛJvh›ģcÃũ_mŲb)Ýú•jŅ‘åü"ĘÅÔŪïļĪMŋūgåœ Ÿgeœ{ü؊ŠķnųãūéóÖßģa3Škó~óå™îŊ ĐSĄ|ïūý"ï\üņFf‘ŠÔéļiAhĩ†ŊÕ1Ï[š'Ūß>\=áüÅŦV~ŋfęŌõûMė*™Wĩ 4ŧ^ū˘ “ŧŨOĐgģrm,(ŌïÐÞ-‹WÉą=*­š]ņKíīmú˜īŠ‘ßŋÔZ~`ÛÔę{ŋÞ 2(<Ï]9ōž”6ļkĢęõŧŋ^/+īļģ—'Ģā]šE­ļ7gÏúčÔu*ŠĀyņ…§Į éúuv‡ã{{a2—\ûũŨ/'véØąAÕ&ŊõŪ^Ģī"ËÄ&Ôëžuڈš•ĢJũč ŧ>ûú։ƒTąK― —]y’~~ãÜđO]ĖÉģ(‚ŋ†Ôč,ĀĮQÐ4™ÉdϞ2ęŪųj‡6Ąū!ƒ_ģÄ9U•(ĀDŽ3Z~^‘Ē‚įðÐãÞlÓ8žĐ™c†õˆõŪ7tĐúžÕf)î6 ŠßĪßvŲóĖ•‡%ŌílŠÆþvî/ī­*āøđ'ũ&đYãÖīŪķkíTÔ ·unEA‡T u­õO]‡ĘÖļáøįAúėƒ0|ĩhû$„ |îAÐLņŋ‚Ã*lāDNÆhïŋsŽw‰ÉÔ˧\Páû!”ü)·Ą_~œœœ7oFö?rCŸŦUŪšņþŲÆĩĒPjŊūķþodĻ+‚Ōí,ÎyįŦŊŋĩ6 Žsr=Õûvö–V†ĄfŠýũPJ+ũ\#īįŲþŧ–įnvËļĻŧK˜0ŌåĄTý`Ģ…[™ØŧmĒ5ûĄ6ĒNGfhËčėÖQŅ`"Ϗ6Œ>$UāEݏíęŌ1 ŲŦ&'ŠQāųÆŸÞ'„úã+ļFG"36U:6AŽÛQūøŨ•Rđ5Ã;Ŋūh­ĘÃÕĩ›ãûžB‡ŠīiōéáÉÆEü šûÁÝ: ‚ˆÔþ7ˆ|/úëAšųL‹ŠBQWŅíÚNZ ĩ@jĪH-€ÔĐR ĩð?ĸb.˟x}áÄžHžĘĖLĨFjĸâÎî)$sĐãóíĐe@j€ÔАZ ĩģ{6^9ą―øx|‹ï$ŧŲ âžn]SÍØ9ŅtGyúož;ŧžČT›čÍ\ÛęlËŲž;ËûŒaĄc`Œ8óÛÉc_žéKo―?·ë… ‡Þ›uŨũkeHm@…úč§sOÎÝûöG/K[^X>/ę~:w:ōĐMĪŌŌÍ;Ú=Ũ=ā–Ũ–ŠÛĶDÝį‹ЈĐ:fYÂIË|oš0čŸØþRĄ$„øņėĐßīĪEj–īœlаēŊķųÅū|ąŅŲįíuóŽíXlöJ 6Õ>šþāęŪ‹ýōû_9:ŦÜ_ŧWđvZ2Õ& ]ö—NĸüÝø­ŧ§n{ĶzӁÕ+ŪcŠM ænnt6æfrc#ÓĒéĖ…“ŋ,-ZS-tĀhúFüƒČŨÆĶZčˆÖâģSĮÞýāČōųÐ!SVĘķ2+ėîþlWOFHR I92Ûe·ïër2RڒĩZč”LYnÞķW+Óþ’“MÅ?I-tÄē„–M]āc1 ĩĀėĐĖ,ŸOęRĪö2`ĶR‹oüX@ø;ļĄ 0<'úŅÚqfŧØ}ņūŊ’?&€ÔH-R ĩH-€ÔH-R ĩR €ÔH-R ĩþŦåįœsöÞq;ŧH-_)ĨĖ9ĮÁÕÖZĩÖ@jy_fķÖzïÁl3Ó:H-FKĀĩ€ÔH-R ĩR €ÔH-R ĩæōīw'ÐQųŋĀUũvw–ÎŌé„ė{YØY k4. ĢÎ(Ž>ĮqAQd\F@tqGGÔ7dA} „Ű&$d…ėĪŧï―Uõ{ũuô7sč€ ĸGzęsΜ“ĄoßSÕ7ũ[ŋŠkŠ{ â0ô ŒūüônB(Ĩūn&á#ĢV 4^ŊũÂܚDQ”°°0ŦÕÚM !Œ1—Ëev‡sÞãFŧ   ŧÝnöÎĶĄĄĄššÚždˆ;ÏÅÆÆ&%%Y,–Ā頌ZYϚÁÔÜÜît: !æf€mmmæH)?8įMMMcÞĨ=Ūžíšę/b!õõõĐĐĐŅŅŅYؚCHeeeYYYïÞ―)Ĩ2jĨa͘™ģæ}û3LŲĄ&~ö―ûĨäįË6›ÍŽøjjj4M3‹ū3ū…būŠëzrrēŠŠˆøĢ{@ ð3éę§@8kƒƒƒŧ.‡ĩÝ f=kælZZ(ģûæ/äöíÛÍąĮü!@ [ųXLVĩ^ŊŨœ·ž5gņ;þC AĶU;°kρf·AHũgc§jŠ[: BÏū튈VÃ0Ā?ÆtŋČÄBp.žéEwK}íĐV ΆœÓ1`žïtŦ—ĢūķšÕÚy“žæåčæĖ„–––øøx8DaBøŅ°ūēŽŠą~&Ļu”;Ņi―ģåXyĩÆ æĻ#„Ð4-pĘvĩ"Bš‰'ŠÃ›VŋýÎÛïŋũũwÞ}oÕÆÝ§Ûž„øũ·Päå{ū\öÅĶ’’=͞ģĪ'sóŲę'\ ^Į8‡Lčjdũ9û9ļ{ũę^]úęŌ?ýųģžĻ’/iëŽ[ŧöPÝBÁ―^í?Ģ  8šųģŨ7ˆPąíÚŊœĒĪíëĩŦöŨuŌsŋþųlÓjķöĩ?þvęī™3ĶO™|ßĒ7?kh3ššũCð·æNcÅ.8f^ƒé…ę§šwŅŧߝ[wĖļ뉒 Þūcõģ—ņœÛÂ@"$Îđĸŧ—5KģšNuqvlÓéšĢ›WŦhøÍMW„ŦˆŠ*7C§ËŦÓģĮÝ81ß0 $TĄgŒ#€ĒŠČĸį‚s†„"M_­Û•?fBĶéL!ÝDĖYkDä>þƌ†ßn(9=ôōĒXlj"ΧÂl%DpƄ(RļP,JĀŨ”*ŠŠ_ÁØqšvݞŠcGÆĢfˆïÛE„ũÔö{ļëúægæ&Fā‚D ŒuUÓ\`7âÐ3ÆāˈŠbÅŪ•åö›~5:đxÍōŨŧíëÃŊŋũÔ aŠ DÎĻŠ ]gĸïĸĐ"TŊ›C čĸsĒ* |Ŋü›7>ø6þ\ œģï‘(ŠRŧéýį_z5l›tãÐDnŧÕÁlÞ^ašÅ@‚ųų(ū_ 82jea끁‰#9Ĉl‘ŸõÁŋv>9hHZXéŽõ%ĮN…Åõ3æbãÄöu.O݆‘04Úó› ë}ÉðÁŠŧdãN[ÖÐū‰A‡wojēf ËąJôâ [KöŦlðŒ7:;ÉBœ{IëŊ#ÂĸI4ĢĢÓęLHLʎËAÎ ÆžŪӛŋŲrŠ%į+˜ūs oŊûzã7uí˜ÖÄÅđɔŠĘŌâûŽ1éCF‡VoßS\ÛØPÕwØļÁ™!|5ĐĒÖÚŲLĒâÂ\;VfÆöUÞuBä‚ č‚Ýīĸ'‰!Š%hhÁUW_Ÿõõ7]sŲž+&/þjŌØëú[Þ~|Î{ާ_9åé{&„}ëēWŋŧŌdė…7FĶXw|öÆÜŨ>OūxÂcÓïJDM œÜĩ|ö3Ŋđ‚“}âĐáéQĖÝöÉ;óÞþüˆfOŸ:}Ōēgžĸd‡VRVþôcwg8­ū„ŠJĮâŋ~Ú{TĄŧä›/6Ÿļ§0sŽ&nB‚sÎļā2jeÔþWÁïų‰Z0 Ķkš—#IĘĖęĩ­Óí9QēsýÁķņĢí_·~ƒzé xGˆJŽ i ‘†Þß'Ëá]ĩš8ĪWzaķrüČŅPGŋ~ –ÓeßօF Ë óÍÂŅã ŪIHKu†YįāįĢ”þôaF8Ósė{?|ûcĮ ÉO "Ú֕Ÿ6Fäé§Ūݰ>:&.”_—[Ö}ūŠ.īũˆ\}ãĶ‘ņw9jķŪX_Û· o|ˆ5,˜D8BB;Sģē{EÚ8ūVEīėÞWŸ4čē>xx͎âS—ôI A_ÐôýŠ ŧN~ō7†…@Ô=n4žXóFˆ]ŅÜØøéëy·"hÎėiKæĖ{=9sÚðÎG~ĸō°į^ū)#ÂJ?{áūŧg-žĩsþ#ģ^ėõũđW#UÔöēÍũÜĸŌčĐĨW~1oö’Ũ^}tßŧO{­üąŲĶaJ”c`LøŪÔÞ7ĸbŽÓcˆ XÔÖ#ۋKC]úRŲ’›—ŊY1iėī Čį(ū‹\_Ôî2ÉĻ•üĖŧýWĩ |ĨũĨgãĖS^z ļĢūú” FcC5 +LtØ!8­wēÓë"ŽÐ–Ú†vJŅ7/§… ˆJ‘Á}™Hã3’ėĄ'RsóĢCÁāž›™õ9ÆįÜï*„%<ūčęëŧvîYĸIMÓåW ī>éËėĻ­gM §ƒ)1éÍ5Õí­ÔÚ~ЉyžZSkKӉj—-ĸւAV TĄmž^áaîôޜ„pÂļð­ĨĨŽīĶ­)QåšGR_rĪ!iS|°hú™ŠZÆøE.rf˜ ĄÕžíĩÅk—{<—|―~Gk›íØĢž1yYIa{WŽĖđéWŋũūųÁúĢídÏ7ëWToî,n›]„ˆ„čeŧũï.oL>īģĐŽ|ËķŠĢåG·|üåĪۖÞyÃXŅéE8|ĻãtŊ1Ģûđ;uJųÎõŦË;mžSÕZ\ꊏŋþöžÛR.°k"ÁP_ëüF­Ē(ˆēŠ ,ŌŲ'ĪÐđŦĐödKč°[RŦÍŨ+6æ {„ƒčß―ƒóė\ųņ1Ŋsð Žĩ‚ @Ī ļŅéŅÁî›Ēûpfp!įB!ð\Ÿ‰ų?FøtÓUkD|á„kģRķ-[ą§*aU-ŽØûěúÄÄFķėįØu†Ð(§3.ôōĪÜøļā}ˆ`\ĻĀ3óÅ:õ5›XĐ{_éqŊ-ĘNęąT}ŧÏÕŋ|·$ū$=KÔ !Îq\ėĶŠāB€@ę;F+ß]rĖëŒĩŨpkDLbZVŸŽßČÎÏÅņÔŧŲ°ęÓEũßģgÁŸYhŊĻĻ”ļ”>sž―+1ĮĘt&97 HOJïwë{“ÓrĘJĶ{ ƒ!3 ··Í`D3ļ…ų€°öšOW­JÉŧŽüĀ.’™Õųá?ŋޝ7F ĖÄĒïæ7jÍŨdÔRþûn4Ũ-ŲŋĮÆ/ĸųÅ6gîĀė䄮ū―ÝŪĶķNĄ―ĢĢ éz‚DPļZ]:XŅÛÜÐáŠŦĐō Šp­æØ _.ß{ē]Ņõäš $ÄĘ=ŪÚž:įĨÐZ|ÓĶíĨGË+jj•h§Ã™:0+ĪþT―AlÜëv{ @a膙œål.ŊDKŨ\Í^%;;ÝĄŧbãŪ#ßîŲýmĩ“æß>ÔäÄWČđ›ęĘ*[šŽčēÂK/-šbü@W}YE‹ `Ü7w6 ð?æm+ŲēbŲG―ņܜĐ3ĸrí=ũ Î6qÂÉG5fuĩŧ;Ý-e+?ßáĖÎKWŒúfõŌß\`ėŊč°DÛÕúÓ­€y=ŊČ6vHĘéC•ŅÎČæÆCqŒô‹eaŅ[~úņōƒ'Z“ērÝõWŊÜãr3B€RĨžxõ§eņO/š7uĘÝ>2kæ„ūË^ųW―GhšáŦl WûϏB’U­„â2ēRjOVœ ƒÆ^Ų7'ÝFx|Ÿ‚+Ş’ĢŠQMČĘÍ JŊô<›-Ņ:xÜXm_yy]ððĄyU.—ŨvŅĻA;JŦ[B“G]ŦFF *i996‡löQƒsV•Ũī$eÅÛāüÖ2DĩX:šjö> Ö°ŦІĮF‡9ĮLônÛVšŊ”Ðþ‘‰Î„>―Uŧ!#Ŋ,ēlŲ^š·XąÚōbē’{_tĐPJŸÃŪÏFø"c8dÜXdØgXaÎ0DÁđ?e™Ūöws˜“qõ/39ãDQ‰`Ė`ÔyIáD @pn2°  ƒƒ5jÄeŨ ā„*]ĸ1[RŸĄ)9C€øÖA˜4žp0éZF@N+Ÿa†ūf(aŠ„a@ŌH*ļÆqhÁXÁ .ð§o”C)E?S2â7Oŋ‹‚æuëš!]3cîu€„Pnx―Ė|ũ„sJ„Įíe掞ôþe7#(”kín―hęĢ„išŨëĖõ§ŨĮ!PđÛãŅQýõÃóoA$ 5æï/5 !íííæ> æ6%]+ķg]Õ―ŧiđ Bzzšŋö+ŠēnÝšüüüˆˆÆØV}Ģ’ŋ‚@<‡ÏŒøD?oû)ĻÏÖ­[ûũïïĸïÅ{ UU;Žˆ))Išnü—Vĩ"Úíöķķ6‹Ï…ž cĖQšoE1 퉛B†árđĖËŅMËŅÜLĮ܍%777888 'XšŪ›û]˜WÓÜïBîė%NÔvÕGæ~QœsĮüOÞiþJƒČČČnö7@D3^‡9ë2kÛóޑóÐGEQĖô4/G7íB˜5ïŅĢGwėØÁ9Ôå,ós0·õ2gZ2j‡d͘Y(išvÁÞš„ģ‘fΞËĶP]ŧŌôÄ[TQģ”Ģ”žĩyyyf7rėWUՌZßū―EF­D)5đ !ėíwîĄlõéū/=š›]ûœ9Î€ŸoÉĻ•ä/·ėËų%;(Q$I’dÔJ’$ÉĻ•$I’dÔJ’$ÉĻ•$I’Q+I’$ÉĻ•$I’Q+I’$Q$I’dÔJ’$ÉĻ•$I’dÔJ’$ÉĻ•$I’Q+I’$ÉMų\€$I~GŠŒÚ€ˆmmmMMMœ ļ0H’Ī(J[›Ën•Q„QQŽ#GŽđ\íp!‘$‰s‘œœ$ĘĻíņ ȌŒļä’aˆpA‘$‰`Œĸāï‚k:Ï$I’$h’$I’dÔJ’$ÉĻ•$I’dÔJ’$ÉĻ•$I x*H’$ýĸ@č‘åŸ0\ø$IˆŒã€= Ĩ`QˆB€ĩ’ˆ106 086uŠšvÖęĄg R!.L Sė6b’Q+I‚âõz+++[[[ Ãļð[Æ$būœÝ_Õ~ĪūģC=Šã#Ô)‘éŅVŧ•"ĢV’‚ĒĒĒÂbąŒ92<<üÂom]]ÝĄC‡TU‹‹Bü[tPÖč=RÓæ·ũψ ķYz ĖvwYMãūĘÖ›3ØA, Aĩ’t]oii),,īŲl=a)“&&&šm>~üxBBÂD-hŨDe“N-ACûÄõM Ĩþ“Š˜(ĨĪ+âP?Ą” üL@›fG„ý§ÚyR„jUQFmÏ'IÜį\rVxZĻō2Ņ+>!:ØBÁ/ôvttš=œZN‡…ĀŲāĄM\Iƒ†ĨGÂ9p:%%%gœ] ―† Š QÂ(tËÓZžę4’ č^ą11 ‚wķķ[­ …ŸGx(ĮfŊåc1I Bˆî‹BęOl_ððėŊŽīEGXO4î{õŊũ_ŲÏ|īh]úäÃŦ6Ú-ŅŲ—?2sJV/{·įįkæ?ēų7oþ3=ō\ŠÄsĐ%E7ĮB@ßīâ―įžzų$[―\QǟøøÃËRlݟ•âioî4ŽN‡°ę§îzxėÂ7/ÏAüŲŠZ!äÖāM’å-œAïÉgnū~[ĖÍޘÕ; ömŲ)ÆđB ÎREĄŠĻÜģyũ7ŅãĸðāP~Įíþĩï°ų·ŽâŒ!"Ĩū7   !ļ@ĻÅ‚S•4L]'$„t“wBˆģfąāBð3օĻĻ–†ý+§LšŋĸīĨ_ÍšQņÔ—ėØ›Ōõ.Î*ŠBļū"8CĒP‚ŠĒ/œ·ČQļ끉TD^;åÎD§Âđâû RŠ  „ DBš~P(åŒU|ýgč!õí (PČϕ΄>Œ1?ËĢjíŊÞŊsþíŲý’# ƒ+šV0ƒ#žØĸՋÏ.(>ĐÅįztÎƆp@ABQÔôŽėáĢ“ssŌÕ`ĩĢîðï~ũÐĄŠÆ Č‘‹–ΘVwt˜ésũ6ŧ“r―ôĘKT€P,Žyߏ<vՌŨô5 þĢĢ ÆØŦn•ļŨ­ýT/ļãé?Üę á,8yÜÕi‚ģcŧW-~öé}ĩFbŋq˜3kPųĮŸ*ĨīåÐę u9ģ―p}†kéĘOWToœž{Í# gî]ý…·ũhĨtŲü•ĩ™PũÞō5}&Þ·hö”ð杏ÎÛ4í™2ėÚŋNoņø}Ģc7|ôĘâEĸŽvĪ?4wþ ƒļāpĶĻåB ‚ŒZI LÂĮߋŽļ˜ŒÄpÃëŅ9pÁ(oųCũÏVĮOûčĨąKŧý·^[ûÆô0U‚*Ęšũßžë­C•ķŅO\~‘Þzâîß/HOßxÕGËoÉŧ3æÉŧïŽę?ųí“ĻĮmG/' á'^˜úėvŨÄ7 ótÍ@ĸUí9-2Bœ1j‘ģöOŋƒ"‚‰ĶëÁ`ŦįØïĶ͉žfæ?— [ôûÛ&?üÖ7ïÜQWšíßē9ŊŋÞûåĸöŲŨŨ?q]Q~VÃÅWΜqCBHį–M›Ēïdí5ĸéų1ŋõįÅ3ĶßĸĖķ[o+Ēu›‹‹ogB^YšŊēóþėÞđÍĸāÃČÍsĶ>öâĻϟ‰VÎÔ6(„Ŋw(Ģ6IēŠÕu]Ó48Šp]ãĖ`šW3(STãí§įóÁ“&ö:~ŌĸäŊۈvÆÜ{˄•wo:ÞėîI8áŠÐ9gâ°Ą9UËWýýóí3/[ąléÞŌÆšŠÎązcÃɆ-ĨĄ3žûMnž―CÔPq}ðØöôkÞgš“x―ščf1vօZÝ0üœ„Pf0]ļ=f—u…i‰%HĐÞŋĨÚHžņËÂĻ(įÔ[ŪúrÚÖōö›]œOúý·ŒēfókĒéܝeK%ÚÄ0tMoŒ/øísONcýíánw§” Ū›mP€RĘöŽĸļ­ķæÏ3'“NĢĢ6õt‡,8žĄŠÕ4äœĢŽj%) Ģķ›ŠÛĢb[jŦ>Ú°ïąk)u{··GēeĮXÜŪššfĨėĐÚŠv‹ÕJDaâœũ7vō­Ã+WüãoŸŊOŽ8ķĢ%þÉywžóH%°ZCCTWEUĩ1 énA ađþÖÖÍûË;ÃįNđÁ*<ųŅ ÐÕĨ3Ÿ€)45!~ã’uëþŨ/Ŋ͏Ss·7óÐhkGkÝĐ6ĩOt}MUGÕh (DwëšfpUE!ļah ­œPĘ" 0éDíE˜ĮË4·@D.ļŅéŲÛØ!ė.nŋq)–ð–ŧgÍëAk”…yu„€h‚ˆB'ILjé}éŌé#ߙ;ýĮŸ}iŅKkTƒÅjOpe‘óĩ—ž|~á-ݞ{ÛMévU`Wb ŪiŦÞ}ųáïýīĒãÚņCl:ūoų'ï—ĐÝĩnã)š5mÚÐũŸ9kþģ /9Ú 3'|Ā-/ĖypĸSŸxõą(Îԅeč„ÛïîŨ4åö)O>ĸâ‹Ï/øýīđG•ž‰EKž›ũÂÂyģ^/pĮĪ”bļÝĖā@Ũ< FâsģþíÕ%ï}ââ―nBq·ëūBT˜Įs6gFŠ―óÅųÏ}čĐ] uBŨ3.™4rþÖĒ7WŪúäƒe_ķrBĄ{ēŠ•Ī€Cū~%čŠ{—ĪæŊÞr°žÝ ·>š`ؘ~:OŸõBޚ/+ę]·ÏZXT0@ ^1㏋+NÖk ŸđzōðĄýD[874ą Ų^{pOdâÔ#ēV•Õ6ۜĐ1aÁãïYpQTDræŊŋ=ð@­‡#€ŸųmęđÆ Õ‘5įÅ‹ÖŪÞWÝDl― &ŒÍMŒ9{qïUkŠ:&ÏyūhdÍcÜøð“$!‡y<ýn|tIāš;ŧhæŸ^Îw ;mÁÂč^4Â~óģ’ …{ĸÓ câ,FHÆâ^üjŨņ”Û~}mŅbĒôÐČ%K–~ųÅšęN#%3=T!@€œq72ß+2jĸ+I Æhŋņŋ0€*”išÁ °9ŪūáVD Č5]Ð؆Ļ834MWÂSŪŧåN„36pðÅšĶqĪã'N*$5ÍK§\ũšõäc3ŨëEĸzŽã‡Býž ĄÎņ“n/ĨDh^ IÔ/}"r]Ũ9#đ—Œnč‹Ï/HĶ ‚ŨÝð[Âėotáe\ó{öe āÕ ö‹.5ĸÅË$ũāBÁ9™ĶsÙrݝwƒ@B„ŨĢ!œ!P)ų.m(ŌÔÔ0I’4MÛđsgAAÕjeŒ]ākʄ‹ÅrŌĮÜīá?LTĩōMG] .ÏϜs1™ģm@šNýïĸ‚pūt{rB C‡meíĨ5­Ģrz]”d ą!ĸ0W’€™\ĨĨĨ™™™vŧý_čāœŨÖ֖——§ĶĶžááBļĶ8,§[ö–7v…Ûl*"ô°­CŦŽoŽ쯅)*Dđ€ IAQ3dËĘĘķmÛfÆ…ģ­ŋ…‚ÐÐPsĢ™ĸÜÖËBސėrGĐïĻ-k ÐĢ(DćŦS#{…) %($)pÎ Ã@Ä16ĻŠJ)?ĮÆ^ëŌ[ܜsčY‚,4>B ·|ŋ5ļŒZI ,„€ųĀp=ō o(Q9?Å$I’äŨ8Ēï?PÓy&I’$A“$I’$ĢV’$IF­$I’$ĢV’$IF­$IRĀĢ555”R8$I’$J}1ŧuëV—Ë%Óö|$I掰fĖÂĖ™3_yå•}ûö5666I’$I?3TÍh5֌Ųĸ Š0ī†îe›IENDŪB`‚doc/images/ifw-update-finished.png000066400000000000000000002226341325366651500174570ustar00rootroot00000000000000‰PNG  IHDRÎbðZŊr%cIDATxėX SYFݚ_―ĩ•­ÚšĖDŨGAh@ūð5Ļ›߈Qš†núýāádēIvvy‹đ =PëdSŧ~UÞęūýsūþ<ũT–X,vqq!Ëō?ŧĮĮÍWsēyfėš[=€'ŨƒÖoߟgÐúėÏģ?__ŒV°0f-įįįÕjõÇ?wŧŧ;lí]–ë^Ï7€gžýyöįŲĻÆŽE„ŧ&4 ībû&而§EēÐ>FÆÖ'Ī5)ÔŠĮĀŨŨóMûóėĀ\Ïsĸüßû`Ėö―xņâóįÏĸú5zzz>}ú_Â0’§}}}°Z~ Äĸōå 0ų刂ýÞÞ^ØifBBŽ2ÃŨĩņVƒopPNX[!žéALS` °Ē°üæĩZøxõvŊ€•îĖKoߟzáö1Š·ŊŊ·Į,ÜKO__Duã,PÕÜx•^(üPÚų›þ@ķūÞ>Č€į`þ1þ€Ë}îŽûšũ^}/lÂbâV·Ŧþ=HÕSôĀܟ?â|Ąý'ũ…ĸoøëwß}gĐ=>qŅ āy>‘HÄbąH$F‹ÅbÁ”|ttäfBs Ÿžž6“išŪŨëÍĻĒë:ÚiũâīɃ`ÂG„VUØūđ<›ySÂïûSÕhō†*–ĩęý,BĐp›+ŠÕĶL—…\ūĪčŋ•§^o4ę­þŸÂ3yš5‚ÚųņŠ|{u sr‘ÔÚ]ýþQÐĪ驔ÏfŌéôõõU:K*Z‹KƒĢk čMŅĐÞbvķ4E$ģ7éL&}}•!é{amû§ÖĻëïOöO3―YC ·}Ūģ…|‰Ŧ]ö ÛþÁŸ/@įįËü\ā%Za.ûŋįā ýąčÐ4MĮqņxüýûũ‚ C–åL&ÃҘķĀlæW* ÃïūÍd˜Ą…Be|–e126 CfDÆō7ßbœf vLʰ=픠ĩÝ>&Ą5]›ų‹Ę‡&^ 8g)N…™ŨĻVžöÛ|IƒĢ}‡ĢĨéĩ†”O+NF0öGp%UčôM^ÕŠōŪA…]ó;‚‚U[ÞšV•™ĩïāāørhrÓeĨ^UÉĖuž•aÚL˜^upųĶg§ü3o6JžfīĨQ8ĀĢÉĖMž2rĨÕp 7]ëz“?ðĻÞÐũf=sŅ(‘õĮМî+ŲũŽŨÃnĸĖė”/ļąËˆU$BPĪĄõŪĄŪÏļ"ÉûWj# ?l l!ÍËzýį†ú.č í]@TóR‚bŸēL0āĩš?_æ4sBįįë[öĮĒ( ƀ_pácSUÕFū*ę°B7ĪR)øT‘$Й 2LRõPÖd2ų›äƒƒČlp(?šÅōŠöŅŠČˆÐ†ƒ€øŠÕÂüÁb[oģY‰æfÁ&ēÍüEá6ßĸų/ƒņËbĩŅāÉģ‘þ:d](Đ5EŽä)2Gæ+‚3L‘ļ\Ū()Š, ž •Eå+ĒG>ģģ9æ_ÏPENTdĢH’Ī ―"ēeŠČ**„ ÜCT.GAŌÓýæč]ĸũ#'7,Ė,ąrŋėŸZݗx:huãW'‚aš°7gS„7EKš,?üëeŪÂBVš/”Ō‰ĖOsÖÉđ·ÃCÐLåēųbYQuMU$ð%šHsĒČqÏÃ;Z|0Cā*üýNąÂIHȈÐöŠ"Ōy2—Ëąžo6°t<á%äóý0eÓ'„o隖TI%‰cNRô‚B‰gK…,”$Ek4”Ė{WĒIUŨ9ĖP ‰,T*œéDŽ―/H `UWÎâëžÅtžƒSÁĻÃA=U‘čĪ ŊĀl…[ˆ•eą@QÅ2§`ĸ}Ó~îĪÐÖ?čÚĪš@ÍÜI?7G™C,Š™Ó*øÛũmZāf"ŽÆ><Ą#ĄYEšM‚U„RĐīŧŧ ß°߄ĖēŽüƒž··ŨŽ įEiąXTCë> iMˆ‰o}„ŨꒉņąŠíTĩŌÚū‘‰?"ŧézųō•wý@ŦĐg‘·ĢVį|IÕ/w7·›°[}Ám^­ dÂX+‰j>óû|3SÞąŨCÞ`„Ŧ”ÂAįß^ūžô“”ÚÛ$<bb šNîVw―FEý>ĸĖÔäØČwnŧ"ĐFgUv{ÕãX;”U äĀxšŒ.X=óįĮ‘áû‡GĄXR’ïÉÐzLF­/IqÏ ‚ĻˆĖŧ)‡ovÞíē ŲbgE™NŒþð}ĸP`9VaŠŅ•Y'át:ˆÄ LÍý͙Iß4ážÜLfũWƒ.'°O üÝš°žPëĘޛ Û1á|í=8#ÞÎ,Å3UMJDގÂđČĀ ―JD<„Ëép@~FP Wa3Ũ§„wé"ÜWTiÅ3·qŽUu‘ÍÍMđ“·dtuÞãv ÚWã™Z]ؘũū‰žë*ģ<=K抍úåV0ÚS4ų$ō„mÜ:ĩq˰ų%ĮðÃĢ._ˆ–ÅČ"P.aúo-X'ĄÓ;ĩz[ævv*077;aąŧOnËpP[ûÖvýcÞÞôų˜ 0ÉÐáųj-ņŸûëWðĮbŒ<Đ 0ø`üŅ Dã‡؇ĄGíČ0UËå2"ÃÅ’VIØ#tÝ9°(,!šÅ^ģó*ømg€ríbÂÚú×ŨîųPČndÉŦÕO8v{ƒEIeé"/ŦÅÔöhŋõē$óŲ}§}‘æUō422䌧ŠåĖĄ}ОbÄl|Ë9ŧUâDI–˜b†MņĒÆŊX){°EÜZ͞„‡‡Wt9}hē_ԜüðšÄ„WžKą3QVUōbŨ:æŋ" +:-jŠlĻ–)ၑWãNËã fT™]ē9æ"å ŧ=mũÐngÚæ_ؒt-_LØĢ­y›'ȈĘÞĒÝØ$iAŨĨðǟ˜^#Y‰<ýilp,Ub#‹þ ßrŪĖÉŠõĖDÓ5―īÛ–öQ|x·ßøðęîy™špūķE“YžĘŠZÎ$ƒƒ6Âíšô§čRzgÄ6™åī›ƒ7ãŪUV–J4i–üöāŽĒKóūå3Uǃ>W†x­– ÍđĢ‚Ėē Jd"2î^ūeĨŦãwþå0-ĻU]XzWvÏó—ņáQÏé-[ÓųÏîY;âéŒß:0ģãDæMĀķ=ģšęðģsåOqWļ§go@bÕ‚Hāˆ(’íēTr)N*:\qĨ*GURąō·åð/ų1Ū”uX€ˆD ’ĨΌVŽ@Āė1ģBH„cŲ™žŲ‡^mË‹PąŠæSŅn―þú―Ŋŋíî]ąSfŧšmĪݜ/‡đ4Ë9!;_ŒïP‹ÅYÚ]úCØk$>`-Ā4Müē úGUUlBl4úrŲëõBg+úŠĒ˜Ŋ ÉRJŦĘoŠ"mØâ(‹`įØ,”JC˜“°Ē!2€ųƒ >YAhô‡éĄUdþPœ"ĪZg`‚QSö:Ú\Œ>^5ö9Ø*ęÄÅÜ܍ëŨ'&S5*ėŲBQL0*FkïĀ@wüũú{@ Õ+ígŽÚbnvčÚįækŠÄ'Ti™PĻjíë}k ŧCõĻŸ€ŠĻƄU[+`Ę.Ŋ­(AoĀį‘’*ķōMÃĄļl9øþ/}ôáųÁūpĨ\ūķūÁ·Úƒ^O@J(d)Ō/)aUkåråå?ÆoMĨ–ÃaÅŽŠïØ@ĪÝ Ŧ–9ōpČßŲ}ļuŋēžVĐZ"Ōwt_Ї_ōKÓĻÓžŨōtúÓ+7kKŦk卍bėáØDīæmũ{UČcnę2ž]GAØO.üøH§?>zÄ[Ōē™xōyïÉūōr":~ýúČã⠟ßÛBQ@ °ë܃rŠ"…T@W!692t}üëĪðyU (ŠGUðá|I7JëUd‹Ï”Á##k‹KeĢ,‚Ý'Ž·-čZU؎û[Û?ÐaûÚŨ8_ Ų~Ķ>–dk‹ŧÉØ2 Č!0œÏUü/þ öÞIeĻRKK ürAJéiüRÕ[GĢ#H†ã4%C)…BۑaËŌåB’XËž {U0Ž (yMĢlĮæŅ %ӛî "#Øņ`ۂö éACH ›;cwLCķõwã3)5rŽÕc E­­?NÍžŧxéėÉ ēĐFJ‰OĐHŦ†; S˜ÂŠŽüŦl*æúâƒéđã?ļtņĖÉ DŠÄE€DÅ4ʕ \`АčŠ][ú;ŧ“1ýŲŠęõlžXœ™K…:#0*åŌƚąéŪÔá ü~ŋGÚđTāz‡”u–RŋũUQ5ŨA™WúÚį.þėōå/{GĩŠ ÁК@5 låÅËRÕX*,”J­^RU|€QÅ.Zõķũýō·ŋ9đßø*šūP°Ĩ―į{g~ņŦ__þøÃĢ]ĄZ­áuą…A?Ļ20pž{îþXŠÔvŠŋ3?ó`&U9ĸӟŸęíŽT+Pču”Ōō‹ĩõĨ'‰Â3SņŽ?ŨĢQ}ðý.œ9!Ābag/UІ―r D)°._(d.­Žn˜•ÕÂÓĨ@Wށ0e |ĻÂL|MA˜óþqøÜĀŪģÆMÕtąīA44ãÐôFP!;_p—ąóÅ&™€úIÁÁˆüī’ĻdA8†VŨuxō"ðī,þŠ6‰ ”F­`Y#đP(Ā}ÚÕÕ%Ĩd:ȰUą:ķT…Ðô†ĨˆÃÛ KÂėEe7/Ļ%ĶÇbæ)Ieģķ‚­‚­―CPPÅüÁRŠeöž:}þN6ʆi”j9Мū32ņu.ĻŲč\Ix„€ë 2ÔŊW€%Īô@ÎķHÄģĶO,TB‡Ú‚ÓÃw%,Ĩö8:·l 7"@w\Ԋjū’Q1Äáþ·?ðß― ―•+<{úmŸ”zÂécŅÔĒaZ&š&ĨX_ž76<<2<4rũÉō†bę…QXŊ*@ĻzúBrtäŸûúÞ}§Û3>ô—ÏŪ|61ųhÀĘ^āBiČįSÕâėäčČįĢũR='΄á†ī­ąčĄĻ/scöþÄĩkÉgæ`§Ĩã―ģß/NÝŋzõĘÕŦ·2OW^™ R­åĖ―Û lhčæW‹+FΧ?(ĘÝĮCÂl?Ø Žßūq#ųĒ\ZLFÓK^U‘ŠĐðąīŦðøþčũ7LË'­@Kļ+â01úå̌đ^˜œÉÛ:kO“ûëKĨšķū}‡Ž―Û'Ģwn^ŧvã‰Ō~îTŊ°!… 6˜ŠĒH[―î;EÓH; MGŲýBĢŧ9_gÚHÏ7=_$•ðøƒÎęČårÐæóyėfffāixÆ ˜šš‚8\ļų:€}"9@2<Ë—)đC4œ"SføN ‡(3FĐ(ĩ”,N vH<… )!?LõiĖFc‡ĄÆT$ŒÄ4ņ§XÐģšž+á yĀB1ŸÓģY=|-ÅR™Ž–N'“)˜“ÍčöD]Ũ`†Í€ˆ††h™ųx,ŪŠy=Ÿ‹§2Z6•J$R9ģaMSČkY―‡?čÄėā|lnzúŅ—cCüÝïĸðÉ'7ĸþ(ÁDl>“Í“íųžžIÛ_åßDJÓrķø|˜9MÓēvXm&‘ŒÍĶ ûdĄŒÁģŪsétÖ^5ČÖ튋‹ųĄOĸôį‘ŧ‰D<žĖž€?,ĪÖ+ŪĖĪc3ģņų4ˆāžNÁc―ąØžV'ÓîŅāqD2‘H$“i]·%kZfÓã"äIÅæ 8•LĖgmĩzÝBð+sæÓķïš―FĻ NÂS™T2–Ė%›JÎÎÆ€‹ĩCŧâ°°˜ĶŠvu°ĩčõĖÐa[·)š9ėhÎŦå|ūØųĒ8;_ėž;œ/&@‘=õ‡īíÞ!û m|a…O?@ ŋÓEDĶ>f„KFÁDšķō!ˆ™‰ –™ÎšNė7ķ[5ģTÛņ…Ø,ę° ›H-“Jz(BA§lΧđ?yÜÅxÓÁlĖ?ĻŋņچöĶ Á­.QÁĶz`ÜũE-yo|üáLëĸúXÐÆvþ ’ÍĒthŪūąáŦ#wÂ―EuæIC6ęÓ1 ÂęÚ([Ģ?6ð?[ũ'fcA2{Q”†›Éũóũ›HAŠ3Íį‹Áa“ģŋRia|‡!Z séÝųÃ+î?z­$‚Q@Aâ4]40˜=üý'Å/ĒóK[CÄXߚÆŊ,)|‹Ŧ+™ĸj”B-Ëfð†ĩ $__]ú*'ŋBŪF8°ųŽž4$ÐĮóüjÂÏPû7ÄY2yO"O?d؈Ŋ'}c:xP?ãąk‚3%ęÏžēōÂÖ­›O^,mäÉ:(x Q+•*’$JSoۏc{(\ÉýW[ža …šåD$ĄūsâøÏ{Ž&žkč?ÚJFyjû„ŅCGŽŸdj:Âdˆíɐ„}„[FĢŪW`ø-‰Ī‰ĒĮ!“з·Ņ—_aęÝßãFžˆüĒy^­R(Tę?=Ė mŨÚąĶĢwÜy-&‰ĸœAĄŠ}öÓčÕĢɔ).GJóĸM–ΆnÛ~ôQ~MĢĸ,‚ĢŽŊĮ!Ä\(PąÛ~†ĐÕĒEj õ ÁAĄ+ģnڌ4ķÜĘ"‚ áíýK7ŋōīBŠ#eĘ2Mís75.ä˜ņĶIđÔR ąäüąí;w\Ūf‚3ĻýLAäÜ91ßjþĩgÅGOýŅe…ÃŒ)&― õ5–b‰TĶŦŦŦĢ#Ĩšæ'$)‘D""R,ÕŅՕŅ$šÉð·ŽčãЇĒÅbüƒĪ…Κļ/ŌŽ6@2,`YBU-sÜKW&#ˆųŠ€üõņ]n[ũ_QKup―öF–6ķÓIBáė)ÆØOX8î"SÍcĸ‹ [&Ķ(J,–ÐŋF1’@Ųą'VšŪúĶV,hŽ C(’é`jŌYŦŸH‚9aVŌßÁ$€2|›ŦÃÚÃE|ŧ ]æÍü‡Ī<ág§ų;Crī”fjۜÜībõ‘ žĒØb!e}aqqƒÎĢ'ŒƒÉÔīgg= ‘X8K’$%Ój%b™šw^›VˆHÅfĒĶ}8)“i―$išxÁí°+hRë ™„@ڈˆiĐŪÐð“EŌĘ 2Škkc=Šk+m !FZũëâh6oÍią–nĸŧuķēΰļēÎh°é„ cĮ˜ŽnüĨTL ĘPdDāāūˆĀJ6ąQä'"ŽåīŽĨ’Ķkü™|îJBqþ‹ÜĖ\–ĒJó=I’·mÝî‹>#llm†toÃóžpéԂ0g(ĻJaUĩĒą‘ …ō)ˉˆŌ–à eR<Ô°đbžŒ†Eŧ „Ï[ũÚsëҜF‰ó‘Ā ĸ5ŪŪî§/\ 9ŧŌŪĢæ•ï_ÜķÚÂÂböž ŅO IÓ@™žû@`^nōÁՋ­ŽœBß**ōv-ýÎÚÎ!ø~.ĒiĻω―ėy,0.+1ÂŲÂÂĘvMlęûĶÓ:ÎgZž&kKÓݗĖÃĖWlô­PCŠ­ŧsÁËãx`ĀŲ;œ)(oĪZĩî3lаnmDũ:ųîŅÝ{ēōoûíąīēÚá]Į‘:!.5æÄ+ËNĢnGûxîŧ‘\Bür0$+2ïîܰũęĸ‡ũE§k·ÓØ (‹ö8}oeóýŲĻt@R†đšũįÜ›[X,t^Ÿõ^…ÔōôW‘ĩýD(Ûqôöþ~ŧÝõņũ\CqÕ7{?L+Oŧ°s_Ø%Ŋû―ČŊÂüšũéBüc íęŦŨÂCCCŊ]š8kDMMþEï>gC“Þtœk3{ĄsFq•gî?}ŲĮŨũĻïÅw5ƊęŨIŦæZ˜[,ũļV̆" UūyæĩswDRzԅ?xž|_—}ÎÃ7ōezŌÆ™8"Ģģ‘pD—GûŸ8q*8=9Öqņü5g’ôûö1rxŊ6އoˆ9>ķ%öĸķýþU " 2‘^km-,fÎwJČŪD$ÁŊˆâYš]ÏÞÁáááØ”+ė˜üŧŧķí ‰KU e*3}Žėŧ›YVœ™xdįĖÜŌaŲđ;Đ,l‰–XÕÓĻs[ũÎ(Ŧ§iþiTБ―û_ÕR$,JO8ēÁmîiër1&™!huåKŸ‡ÃÂÏûôŋrOĐƒ‡ŒÚĮ@9H ķņîÅýöÖÖs^đŨĀ (ÏŨæ=vüLĀõäĈv–ó~Ę)k$?­ÁÃtéŲū{_MÃÝgLIÖýŠĖ7™õōÆīÔå}'›toŨNŋã°#db1ÅÕGû>xÄÓÓ넷·Ũąc§ýŊÕ°DqÚ―ÃëēÃ#ÜÎ58.•E™œ‡‘îĮï=M:ķnöÞĀŽ–5ŋXå8w†―ô#…š#ŸĄöoxyÐP’v=·šî1lГyRŲÐØØPß@ˆtôôujóĒėÍŽv9_T]zïöĐy6ģ=#ē(xœđkۊÉĸ˜å}õÆÍ›į–Íš4tüôÓ7nßÚîqðM ĪyyÂußM+f™-\ų(áfĪįēÞĖkl>5Q‘íbgq22ŪžŽ$Č{―ÓÎ@y}unAÓXyãōĒzŪî]Ę~·uaÏxšxũ$nŨŪöfß:{øÞ―ysÏæõû"^ęƈΰý–óŨ^ŧŸ—q~Ëōĩ›vßĘŽ ™ŨĻĶ(%­ŠĮŸéq1‘ ‚I)+ó·-ą;ró֍K?mØõŽPÁWåėtŧÞ+"Ģž,ęēŸÍüõ ‚ŋ~‹ģ’Þ0ÉŌÜjŽąR^W§āMl,Ŋ[WšQP\ôķ ­†EŠãCnĶigõŊIYžŸúüyrj2.ÎpĐË||Ŋ]ĩtæ—ë1qŨýķx],­UĶÞÃlØWŪ„―“3i!öÓæ„f•gdíqÚäuĄž”ÖūN=ī{ŨR[ģĨ›O&&ā*čŠĖ„@ũ5 'Û,JHÂquœyþI™„VÄøž\ŋfųtÛÅ!7âr^–?―rfÍj·Ôr‘˜Hðs™·lsôŦÚúúWq ·ŦÕĐŪöÝēČiĮđ'eïÞ™ýk|n9"‰ßŽÍ@Óø:ãEZō‹Ô—/Ŧ4—{Úūlį𜠕DJ? õōØsL-&̃Ï…q,“v=ÐÍņ;ĸĮĨ-OÐĪÄøísũÎ-nIŅ‹kÁÛvėŒĘŪ‰ļüĒîWRe™‰.K]BÕWäÜN~‹{eÅ_ŠIF”"lŨ–ÕۏgWģ"MõÉÍKū[é‘ðžĪŠ n·ËÜÉkNVģPũÞĸÓ ŧEŦĒãâ/ŸóÚzęŌõ§qÁģŽnûІ](͇‡OŠJržcŠhô,ĨīžŽ,+éžé΁Ý:Hž'†šmüé~N)•ąqáįýýϟóÛíūiýÆõ‡ýCˆŋŋgßé;‰ȒÔûÎKW†=)ÆĮĄôį·Üũnœeiģ? &ŋĪĒĶ8yí,kï€ð§í;”ZP'Ljŋ#Ô~&B5zz2šeđOĨFyĐÏ{$TŽœĩ7!þqļïĶ6ęœ-~ŨJë!3ÁІM[q=.rú CEcã éKĢb"§vuĩĩJ5  ÅŠ ĖŨHz7{‚aivÆåЧžˆ‚Mî‰ĖŽžįîy+ŦŋōĒĒ{9õĘē0ÏCÕ*ýM>I‰Oķ~ĸ-§Q·ĖïrŽšŨd—-‹GÐzëņŦ›Ė šå1qD§ņģ"cÜVý*·ômEęÃČSĄÏ{Ïږv;bŧãīē§gĒ“K›wĮˆ` ss Œŧ ԗr,/LW€ L†  ŊŠjŠÃÖÉ& šþý8CĨBZĀóó;LGâ=åČE0€2œ†ĢÄģÝöލōĀËwï`[Gũ―ƒZƒĄßïxpóō#öōžÝÏËøe;žĢSÂėŒ—"žå•ÔŌÚRcFŦŽąlØYÂp$ŊŅ:gSüóĮŦgŨ|(ōŋð@C’ u1ĀvEäݘÃ+F!Ĩ‡–æąpÍ۔dđĖøvķïĨ‡·ŊøIđēžXŸÛzĶ n‡Þ:·ŨQþúFă4–ƒŋ~B@ŦËs—ÛN4døļIvO •]ûŒúŅēģâĐP6”œ―õ”ëūhä—Ö.ŧâ$<ģ}îŨdŪĄR~Å (ÂĘ `q<āąb<Ŧ!ĶzœHļ{;Č;|ũōQŠŠÜ‚ĖŨ†Úí\m›9î=ëÂEŽV’ Yކč}NĖ~Ÿ(ÁvĄïĮDGčĨ›íŋ#ôEđˆD ŦæÅ:ßo8|'üT Óo+Tė§ĖŊQ‹F˜™ččUėĢį3D§Ø[˜TūČÍÎK”ĻÖˆžrācbFAīÝy<0ųŋkū›;ë#–ëj>Įđģ™8•pįV wφ*ËģßäžÓ ’‡ļÛmð$ŋk1ÛĶŋŠ ĘŽ·hĮ‹ÜŨQgķPxþ-Ąö3ą āBĄŅh8á/fØX”Ŧ$ÄÆY|-ClC'tāņÃŌ:,}Ýķ:ęe4HGŨ ƒákÖõëŅsX‚į?qā4 ĮŒ-ó&v24ķ35 ŪáUsČKó…}J„§ŧĨÃÕGeĨūĐĻ# pچP„ Į7ÍHÐÄQ ļf‹ÖŲ›ŒõÖðĩj…ž(ƒí˜ ėOÓt§vÄĸ,Ņ„ %ÜؐüĮ ĐŨežÓęá}zˆuShU5•*ōoû˘nģ/0·O~SÝ ĩxM`˜<„ĸ8…žąū‘W‚"üHøûߛ)t qęHDąJA5D ]ëØŽ„ M vŽēqw6VDÓší U ũ jaĮ_IAHÜýûŲólÆéá~•™1WÃē=ŠWgCĘy 4Ie4ĐVj„ï–Îčd<Č|†ņ1™ŪÔĻī ˆP3BrįWTýAE‹Dˆ!9BpÝd{Wģ^ØK-ëŅUZôšÃ ĻViȑžV€qHHNÉëÕĖ[·―˜ä8^°”æX,"ŪÅfŸë7mUtĘ?ƒOõ=}ņdvŊŊFŲ ”ņ<Ðï3Îmã2C)'$vô;<û+C8Žę›:mâ—"üSĢlPhø#ŋ6îŨöÞ­=kŠ ‹ûNĸĮčĩŊbvšïļĸþ› š˜ŦžcYę7i6, ›ĶâH’TÕUŨh BĨzwoÅÚm…\ÏSÎÔÞ^ëļũ)ASAžÔ@BšfZÞgØR!WōE"NĄndCáöŅ0Žā^†ûgOĻŽ}Ž:ëMÉ*â)ũý{e7BžóļÛpģþFíĶūđ1"HE~âZ‡UY… .û}vüd&fØĘžëËÝķW‰øœ?WéúãÁtŽjó+\d•PWŸ6ÔŦT4ßWWá•›óyWûũ#žĄ;˜ļ9MŨaÞïÚðӎC~qĮß ö–Ï’:‡O45Đ^‡ķ­_ūbۑ`QčwFjŧÖÅß ‰MĘVÄjuhĄ3ÏB°‡ąĄ>HōôENŠQdgĨf”Ę›ŸûsœĶë虋Gũ-IÞžeËÍO„]ÚėķōitŠóœŅƒ:ōŽPĖ0ïB‚.ŋ,ŽŅ"ß- aqrĖÉS>^^8Ŋâ™ô4iÁãŸĀr Čūq1:ķJŲz‚å`äx·øĶžNSũ>91ŧZ.ėķØ–ŪĀDR ðqïéøčk§ƒŸÉ0ąĶĢaÕ-åų>d°ĻĶ+z69"8.­b„å’ųŧ Ž‘Ŧ[ؙ*NOOz–ÞČŠ rŌž―.ãlđb°ļgCEDā)ŋSÞØ–SÁąĨĩJ―Ū}LGũ§ÕĩņũsfNûf@]ĨžąVčΚĪú}|ėy“–[ÕČ7›Eéīë@ÁĨow7Ũ“ŅÏxJŠQ_ųŪžZĐŊŨĘßĨ$§ y•ũ›ŪCËąĄWcĪ*XČ} l›/úönâÃÂNE܊‰žz=%č ÔŦ ËōÍîÅö3āũ„ÝŨfœÍWBl$Ã|ņE‡ū=ŋč!ÁŠuę:Ų°5dš‡$uŲAŨÕĄ/ßĩëÛŋw{*äLĀĩčx\ķŠVe ŨÔĪ$§ ÎTmz„Ëbökē[ĸQ4\ ð<ęyÜuÝÆįoÅâĸ†Pû™8žüvŲÁÓ{Ũtâ_ÚøĢ­íĖ™?o=PT ŋqÜ}xÞú,; sįƒWšØŊ Úho@^(ÝtļrŲ<@SÝzČØô&ęânŦï–%QÎÛũ:OėĶÔ ĐЉgy4ŌjÕ)w—ō‡KæYY[Û,Yv ĢHŪc8x‘ýīڒÄu‹E%— Ę°†XB"‚$1SĒ™đ˜$ôIÚ`ĄĮéefĢ„^ðËcÍíĖdœæ·g^Džūd\?―`Ï-ŪkÏÖ ą‚XІ,:õ=3Āũ i}šĨs­Žm,úáAN"`3\CI· ë™?9eoke=Ûá|\iÛîFgw1 •ál;ûvē·…įŊ§ą~š §% 7ęŽģĢ“ģ@Ëw\M!) CÄ$”ļU;ÜĮ‚Õïjbõ‰Hu{ųŠ5Þ2æÎ‡n\œÏgŪĨĨĩíOŠM‰D][îøaJÜYkŧyĄ)e–ŽîÛmMT”B­ĨðĢ&H‚ à eĮ_\8ŨÖÂÜ|ßåĒņÖ+íˌ[·r1ÐÏžCųņNVVģƒïe@Dīļˆ!e$­Đ.<žÉeĐãrÁ” ~ŊŠTjØjņŦNú$ÕŪ·ŲäodŽBŋĮð)“Ų7asæŊŠ0Ѓ$îōéĮïT‚ąÏ˜•tĖ7ķý:ëD>čuSe1m*NÕvĀÔE–#ßfÏēr+ӝaBiŪx{ÝËĻ4ýÖnzŋ.I—Ž:oØöž•īBˆ.jXƒžã‚ΟHdŧ/ī·žïRŨzč‘sgĶõÕŨ°įIŠė(& ĒÅ­ô§­- ũï3bē>Į ëŅQ·u‡Ū]ū€ļqþÝ~Ė|U*­‘DOX ‚Ī'Å%W°·„­Ŋ.Šo―=úøÃũŊ-ž~éĢî„"œÚžĘžĄdŊÔŪ„d/pÎģā+_ūĸöëŊ―õIĮËðÄđTYĘPÚZrÁ@Ë^Ëåp›iā4üė―'7xeģ=?Ø_ĘäNĨ:)į’árpjŒåäJZé€\#éHÆN-”ĪOéŦ|ˆĘK$§%@~Ĩ–$·x§[•Ŧå#-đú·ŽŊÚ5úú*›ÛaJá?•BîŊ•ĘåK—.]Đüø‹RŽpf.ũ‹ß!ïė#žĨ―ÚO•ïøIaãŪaÍúŠ]SÜAG‡{p&o?ŽVðΞWŧģð―ƒĢÃýüOvÎfWēåĻÂąVDî:}[\Kā ōÉožˆ'Cˆ·ņÆb`‰y8ĩ3~ϊ8Į-5FmĪæöþj’•Ęđk·īīîĘČóĸ^g/.Đ―ļļļļĪöââââÂäŦäĒŠäkåââĀïáķXU―žž˜YõĪDð>~P"3™Ŋ=ølAOÎāÓx|Zî―U•·i)RĮŅ—Ąj%’îúÝwJϚŧ0[Û·T čąIU*(îA5å‡Ęįįvû`ËF\ÖZTúvUkŧŠįy_ķr)‚Þ1ŨZ•đ[7ãpwíŌF„š™j;ÐPÓãļEA[K@ĐĖé2žŸGHR™-ĶëAļ§Ž[Õ―7û!Š*ÛËí%3=âv{!áūM ĪĐEŽcœõyžĮqP"áÞ9šwĩl äĄÚß}ü`ōX%Jþãgá,€ŋûþûï#B...ūĖėãĮ™)×K­Ššifķ›ģĖōØKˆũĸ7ũóeõ‚ˆhõGUféŌl”š•ûu“<ŽãQa”å!Ģ•õTsSûÞ@ߎ+ÔV‹ŽŒ]uߕ“<―aH*˜%RYRf+#Ĩ „ķÜgĪŽø†—ČZK:­Œc™ĩ3ĪÆGfĢÔmmŦ€įņTíĖČCėū’‘ņœÚē1éĮZY•™―ãģļ_ݝDV9ófŦþíuû ČgÔ;rqqņuPÍïóÏÍT–{H‹E•xøŌEåđwUR­ģT5[UoōdkM†[YĮqdÖXQ*ÃC*Uæ8ÖŅ:•]ÄÜ―ĪJjLe‰Ōú0j‹ÔsdÖ˜#pïgU —ŲļK Ļú8wŠJöO‹H)Qrä~-{VK_f™ Yõŋš ÄL3‚ 4î^"T…22ܝĪwßՙNIīwu?˜.)éW;‘y·€ĘÅÅÅ“ĸ[6™lG ÚbĄ­œÜî•nv§Õn/RDŽõroŊ,š°Ýņ€Úæ7@Že=Î9ŧŸge>ĪgNųčtUüÜ%ĩÞ2Y-)į>I ߛd/fVķÃîN†fÍA„­Ęyŋ“T2ŸT zÆĻģ@ÎóTR  wīŸLw§Š4۝Āęt‚„”˜Ūh–ČØnú$#ö>ßÍ2Éįī~nJ ~ EārĩßĒŦ-U’s\ūŌmwMZf„ŧ=רּ `ķhV%•ĩ–‰ 3ïÍU’…"ĩÆÍ &6Õu @VäRËļ”ęķ­š6ŲöģŊ'"3*ERÛw'­Č˜g†™õdŪuë`wÏdTF%ۏŋŠBjDj›íņæ% éūÓYxKTüXk*—HÓ5ąĀ3…FddĻ2+û8qĐŠÔžO#Û;SÖŽĘIlyÆÕfpqņ-J-ZnFÝĖė9~P-pĪÞZŋž3ĘĩVzøöépĘxBÓýZĒf%•‘F]šÜ·ïáövũ"’ų0řąŽĢDĶ5õX‡”ā―ã53 Ēķ@lw#9í2#øÖŅiof)Q’Þ‹Aīa…<Sí^ā]™c][ēu†*R9ÎT*I”ˆ{ĒTáĐýĀĐä­ÛÎû―DԘïŨ4€:wÛm2SÎsWĪ‚"U™J•‹‹o+Ŧ•”l5ŒŒŠ;9ÍĄĢŋ%ržgV$U›DVŲ–Ėi}õȂÜá-|x Üs6hkëaĮHä(uŸŠeDx†á! ’Ïqfž _zDŽĩ"2+ÕÔTû‘ŌLĄÚâX7‚ŧ#W5ÛUõnÞ]RT‰ņÚ=™þ$Âį‚CũHĀč ypîíáëX%îžBDƒāˡý,āŲ"ūŧ2UMÍÃÍtË#öđĄ|Prqņ reĩ[ĮÞNÅ2Ð}ωV…W‰’jæ"%m$ŧÏãaũބØZ%ég(u­ĩŧˆuŽ――ŠŽã <;l-ІïČXÚķ1ČN~O%MMŠüÜýÞH–ŠVũųâ xļ@L—*Ý―j:ŠLŌĶA"Ĩ–™·„k‰Ōϝ%Š”ŠÖ퉉#%MutŽôoÍš—+3‚Ę[ûYĄH˜­đgA(ˆū)§Pn?3ŦOāÖųtņųv―!rqņíqeĩĮ\Ę āAk™ĮšWe’ÚžsƒėXĀ+‹UB€dūݎUky’J^nZFÓLĨãRīaŪŅVH{X?}gĮéÏyŠÄÓŦó„šDĒDDdĶ@HļŧHeēf%ßyûŲ}Ž >úÂnFÞn7ÄđK„ýÜ;]ųėčĖđ”ÏÏŧŧĮƒhŽu€ˆt(E‘ŲŽx­cZ2LŸyŧXL @•ĐđÏQ›âËÕ~›\\Y­ûžžĪđŸgVĻZ2BÔL Yēցū"%[fķZJ•iy]šãY­*Ôė9ōxs—%oũįÞ17z™yØēĩî}Ųa”š"8.W5ķĢ f dôOāî$ÚÎjFĪåūf̉‰ã­ÏÓ§5] Ŋũ{dP)"án4ŧÎÖÞđđ ë+ķįđ1ýg"A`-ĢĒŠė―A˜ĀŅ oĖå]ŠŧßÏWĮíķÝ=&k^Ó,œU‹‹o+ŦĘuŽ>ôŋƒ8ÖŅ:TĻjŒŸm•óó,[eÎ}zøEũ{-ģðDe>m HfØē’ĘLŽã–Yįū+pÜ^üôĖ ĨwũU[Ô2&îlý2Óé…ĻĖéũjw,Š&"ÞÁmËŊTõĩŠ| Š€øŽ€ÛÛ~šL$]Ĩ:î\ •' €™vÐ\ķЊpé Áû—>ā3EyĶī™ÎâŽČ\ Sī"8Yme)đŪ?âõmrqeĩJJÉ4!Đjī9Ž*UËĘ Ž…8ģĖÁyžÕá#ÞÛû_Žî›dQ–­ŨóĄšve—’Š„°$ĐšÔķŸĢģķ,ũΌ‚ž·-Õw]Î `bâšŋ#1­R8Ž#2æÝÎĩfTÉ}ųwîū3ŽŠczŨģE)J7jÄl‰“€ŠíÚ5“Ļ1‰Æ *é Ē!Š 1QPąž XŪØ+6z‘~€SvýΞįӛįđ7_üóåōKÏģËĖė―ŨžģÞõŪaÉęŌ·nÞžuû~IEƒLð7/Ķ?*oĶ(ESSÓĪ—þí ākĘ.Ķ]ŽÐ)ôÞW‰ŨV—W””””UT6jNÅūý+MŸÛÛûދúĶچf”wĨĸ‹$ü†”Ļ­ĖHÏŽåIúÏāÍē„6ĸÁãā=Ūl`XŪA!ą ųÆÝ ͚WåĨƑ)**ĻŽmúãĨLg―՟"6ÔÖčåO^‡$mC}EiiIiqQaYĢ}}ĸ þ”ĶތôįĩüŸĪN„rsIŨTUß$ĸŋ<-YÐWŨ42œęękšp"HŊĐmÐ4ŨÔÕ$å/rӕĖß k5€ķoĻ\Áy0@>‡UAāątS“ØĀ‡iāS bB!ÔØĒĄ4Uá’ =čģAĨS2Ã2ÃōXڅËj1M,C ØzČķAM’E|C|;˜qņ+ ũ!am.S‰oŨjÉțĶW7(§‡:ˆČðöXÄ $Žâņ‹/īØÚŌŠĄhĪĀ2ū,ÔŲÖOAŒŲj”ņƒ<CTˆ„kā7s2Žô!:§”·gYE{2!v†ŨTŸ5Kæ-ønæĘžFCJtČÅĮÕ*•n—ïŌÔ{åĖk1ēƒx™ŧ=*úū†`Þüāšxz%iÆŨsCÖŊ_ĩÔûÛï—ïË|(“ôÛÅۄķūŠŽĶQ!ÞęÃ`8UaFĒßö“‚ÖT•–ÕūqA’kƊŦ›ŠĪø†§ÛB֖ę(čõiœÐXēü›YÞþK?ĸõáËđÍ*‚ķžĒĘðÞ1û05nžũÂĩQaĄ~;Ïܒ”Įz™on9ë-Kj,ÛūfiæKĮŌf–åHÍOÖ|óýʈĻĻŋuįrKHŠúŨgˆš―a·Š›éĸ68EÔŋ*ĐŽoĶXŪîË5ԟ}„­ŦûFÝ*hä8æŌO‘óĮ7(4K6î øíęĸ[s īe&iVĐÛråQ)EÓ{Ļ…`ŽÆ\ (ŪZŧa­ā Ep Æ/œčĮ|(6ôBY ppAFå­ką…ëĪqĨÎVI"Ķ#Tč.€ŒØå—q ŋ7˜$xĄÕ›œÂq"â0\Ī‚}Ū€Ã(”°‚CÞ`Āô68§ąQ·)‡†mÃP›\ŠIAäŦ@ēĀl·gą‰.Ĩƒū‚ 9ĀwËŠč‘fŠálôEā6 dĸŦŸ&ŦRe^š ōÃđŅF7ũsįBWÏč`N™Ņ ‡FT=þûŪķ’ō;t+(ā° :2m ”~ÄĐ;SėVZpĸđ@/\åčënSæ3crÚÓZú%A9 ‰æ ÓRņōĶ’8ėä+ėÏÔBKdßĩįô5ž aUD)ēˆ_Œ7ŋv=ôÜežšŽ…Â%v„Œ$Õ}‡7i''m ĸéŨA›Â/–Ū‹ X™þĪŽm •šcX 2n,IÐD(Ŋ·Q‰ŋF,ݔPsðlzúų'8ųM_y!ŋA›w}IøžÜŲt,IPšŌû—>+WŽ›=ķEČÐcðĀ#I†c5wŪ,ßsOƒÎ"Qßdč&.TÄã9[þ ^ Ø|“Å’ÖĻÛú݂>Žœ )$HÔEIÆgĢÓAV^ë:‡ŋ‘{ÕÞ}âŠÕkŨŪŌÃN‘äÖ’8Ų‹^1 8 ’Æ6,ËŌoøn˜þo~ 4ËHû6%däOnÛc˜ÏŽæd + %Ųô&`vÆũ}JmėŠ .]Î+$ĪšãéSíyĄ!„ōëĮ.jÝúõ›7kēk{ēÝðČ_[ŲIpu”\6ą$xīGČóz‰ 9õ{ iíþ›C­,Ë ąWāÃ@’~Œ’`LjßÃĘ ą…+ J-Ð D ~I}pl‹ĢŅ—XøƒŠ^$/3 W1™ >Ø4€Ą"Јå8ZÆ/ ū,A€­ †ĻS–1ðo€ĒTĀVœÅÂ&2ŽĄÍFąûĻ)ĻVQ0„ :Ģš°%nũöĻđ‹hhúޅä؟ 3zøũ?ūl”ðm‚‰ėóûg9rü”ļß3œáíŊį8ūøÚü/ÂÔJš’ž+ũ—ÜÏ<œē+6bޒ„fšĨH’ãˆKû6§œ<ždŲÎ#ũ( sŠđîðĶYcGģd[ĨŽbåæq!“ĮŒ<Ã'óYC ļQ,Y•ŧ-óŅŽčˆ‘}ė(ÆæÓ ĩžï–Ķf\þ=óÐoÉÉĄSĶūZĖâĢĄmÚwėÚÝøįÖõ;Í‹Ž aA8{eüŦzýå‹Éi‡ÐYGoWČåXí5zėČþņe:‚jxą.r]xP°įīEŨJŠŪ^âŧbÆPÏũ',ú=WĢĶŦã—- ZūpōÐĀۏ*rŪĨk$Eû"jÕ·cĮýtÍÁ&…ĶĪÚÛC‡óų|ĸÛ%ī)tqKgŨwŧwïö^ŊÞfåŨ――žëYĩšš›ē;&&õIÎŊóĶ=ÜcŽl•ˆ^5u]]›sdþúĪFEEÖÞY1yŅåA_|=Ä{ԘŅ#žfŪxXŦ+―{îpâ‰Ø Ą‹ýOŨ”?:~;‡'(JŠ;šÃß}ØĻaÓcöĸ.Ō\uÎqŸ°bŨúôáŨÛŠŠ‚Ŋ‘īt™ÔÃęæ'šĘ‚Ęæ:3ó†ė‚æĒK)”ûāîTíņkYŊDĒđðÆĒEÓūš;ßĮûÛq—fÖW>Ęøfō„ŅÃ<§ĸ\ËģJsŅÞu BOĸfYI}mbØWǍœ6=2ŋTƒ?Ÿĸ§ °7Ū”ÅņĶņG@”ĄRąœ,Ãā―ˆÄ. sŦB€Ąhėß*ËĶH426t•°ĄÞL ‡ŌÅBŊ†ÉË+°pmū/ÄūÞģĐÕjPļøuÁ  f%MÁ&Æ_\ ĘĀw‡9Â&ÜGô`(ÖÃbĖ•D7oÁqÐÜ<^ āâ^ĨÁ1ÃK$†xï[„EŽãQIąíÜԄŅ'LÛ°v’yuö‚eÛšÏŲzúd2óôÄō g% 0Hš•Ŧr–øïŧ%ņ`ČØ]k7=Ūã†|9ąîJØšŧÖGnV{zôq4kŽČÏzøŌšËĀáŸô›4iFDč85<2APúNøjhÏîó|—ÍïJņR}Qq!ëą16Æ)7ųR‘ūâÆþ€ýũ7Ÿ<íßïUpøUQ8"x™Q ėÛQBS°@X:čáú2ŋ­§û`OŸ;FõueĨ•C`rÏ[æĸėO+š3‡dÖŋ·wŧĸóÔ#ũjú bīÞå·ü”fô‰S‰ú[ ûÏf”’{&éėunĮ†õî˜YéŲ5dčÁýs­_/ߎĢé72nUąë–õr‘îÝū\Š#+ïžÝ›ņr{ō™ƒŦ'XrÔ­ŅAIe?Ÿ:9ķéNČÆĢZ™Ä­Âĩ9iŋćŊ^ģû”ÔŪģ\ådÖ3™Ŋ9•rFėŌÍÞąóōØ3§DĪïš^Ļ!Ж€^l|ōðN!ē€ŊUüë•Á@ąVŸųƟ?{ČĶ4íDfķ―ÛāáûL™ö]ļĸĨænöÕ4Gßø92ęHĢIŧūügô”{õŒĄęܞ­wšŧėŲR™ķëZAĖMēĻpîãÝ*î?Ęŧ}§Sĸąs&ž›yûÞđó•Cö┩Ükũęj ÎýB~Øąœ+Ï+iķrī5§Ûv^—|<>”ýýĀÍRšîĪËÐļ­Ž .<·å7‹˜ĪAóðĩ5õŋ @ DhX Ō8,„ŪĨ 0ÄI' xŠĪeØ mQûnņ‚€ũ/°–IšÂč†M  ā€pĀ)#Æb“0žW8ŋpøM‡P‘ÏYńārÕRC“ZPį āŽņ D”ÃQ3Ž@ŨĄČøŌļô€&ҌEA:‡h Ŋ .čÂá'>O<Š,;h<|$ÏC8‹þ5sXoÐBŒ˜Ú ä īŸĀF9oĩ!ZƒüzŪũMƍQ1üã[Ų„Û·3Įđ;9wčdo­ēTë‹oޜ9ŋÍÜõá‹H<ŊüûT ųBlnY ĐāNN]ŨĐ#úvu~oÄŽtû5ãūL4ZôēmKāGý>úÐޙ–›^ÜËŊŽČ Y8{Óą‹%åUzANFQ™Û89ķsttéŌÁūüÎ%]ŧOæMüĻc·_ /ŧU!3PÃM>ČŠ/-û%dąwÄYMĄRŨĻoÓŲ#6zõfïÔRģå^,ī˒$-,mímÛ:8đtęāD LØd;'ÛķšļÚŲīƒYũ>ūÞõqsíĒĒ)1ũÂ)Méģ•ģūŲžŠ‘J›d˜ŽÓ‘%‰G=.Z”k +Û66.öí;ŲtęäęЖÃó+^tëäæþą‡ĮĀ>Ö ߎuðü|[؂{ũģ·#5z―•“]ûŽ6:wąĄĩ7šŠēĖųfq΋jIĶ I#đ ZŧmĨGŸNæ4Đ·ī™9 gßŊýĶ5ō—‹5Šlm;78ú“^Õ,Ģ")N‘í{ýãÓ>VógL?ŸKRâÝK_•įŽž=+áV~ié+Ãke/Š,wėųþGžzw2.kū˜>ðæŊiyŲé˅Đ^}mãņ•ßûnNĻįi^xm EĒWĮC4ĻŨY3.{ûšy‹WįŨð’DĻĖÚÓđÃ;ï8ØõĢbiCvæ]ąĸŽĄ=:ô4íˏíOf<$iÃāŲ[7ų øhÐÖķĻ-aĨ+w8…Ŧ8ŋóĖm[ŨQ_z ŦH؟\äôqÏũhäķ`ĖÚtusū°cýŠONčegÁčKã7,™õó ’5̉įŲwú†­\ęéæøīļčĢĐũpîį5Ĩïy―5 T_ĻU8Pūc„$!•>HˆHä{€ŋē‚ËOåŠH Ø!Rā‹H‘ÛWHTOĸÁ2)Ž0•hAųEbáΆHƒN°˜a$ ļp’:€Ŧ,hþeš€°ëĨ@ ‹áÝh ÝB1Ą6dŋ*œÍSAԉAã8|kąņÎÐĶ·HĖˆˆ@vbH ŒŪˆîˆí 7ĄbąlÂ@ƒ JļžB2m™ƒíq Œ§$„AōÕz äþE |ã_GZVÝ―ĢËÉãgŪÖqƐžĨīšZ-ĸÚwˆē˜ī•ucÅҝą!z/hy ·9sŋxvjŲĄ+Ï9Ž#ߔúĐX‚aÉĒ[Đ?fÕLïafÆYé_æ4?~TgįbO+A[ÓH-hŅðŅæjGŨáŅۏf\ĘJōkŊ"Z1JŌxƒ(ŅaíôŽRYZŅ` ˜ĮŽÚš[Ŋ,Öv.Œ…ëüč}‰nßÚōū―9!ÖĶ9ĸŪëûu•åYų-݁Ãĩzže†z-K#ˆÆ%™ ąq%…ĖäDT‡­Ð kŨĄ[ĮO6ÄíI9učė1KãïĀūģ6ßģ2K9/0*5§Š{v3õQ~·žï2”X]ŲĻÞøŠe^ķéÕÏËkēŨĻ!ö–Œ,*–K„,d‰€YY0ž…ÛÆ`čéþõæ]ŋ\ļ™ĩlrEÔī9-Éx1'ÉJMĢ– ˆŠg…ŽYïvĪ +Ī —Yieð8ëî?ė=09éŸÓOlۛ9ũpŒŲyæfö…Ÿ·!áČփ{ 4éģÏFyö7c9ũQ#Ŧž%/ w˜ÐÓē,ô{ßÏï#–ļYšą4‡æŋV€ :š"ž=yÞØh07ü} n›6EęhŊ"X‚@ &^T(Ž’AϓæŽUV͕tĖ CĀ*ĸQëOžë3Վ$ĨƒÚwóœ4Þî— [’%îޙRmЧïM|ŅõrÎĨßš-ũyP­'eAgā%…zŨÕ%-eï΄4ECxŠČ„ų;ÚžpûÁPēV'Cũ :­AûŒŸÛËņųÖö$'í=˜|A3ߊ(p]ÂÖųšem―2tëæČ9s— ýŨ,ųÜĢ―‹‹!ũjLphnĨþĩ”pĸÜ?ĸ 5kV­;Ũ$Š:­;Ąču’^PÚtčŽÏ―š)(üQu›ÅÓf6§ÜþSÂūŸķ^|ð’$Y―V §ØTšĶPߍŅk·fŽþfV;†īZ ƒ§"Û,tÃó ŦŨý˜qó‘ÚÁՆģģxÁÕĻØøĪý{·ÏŌ›„W(;mHý%6 (Āo•ßŪģyízŒleļr\:uˆ"0vvŊĶN>]hĻ9wúh―@ÆĶȒ…}ÏķNŪņũL|Y^K’Ž­ƒmuþ„ÄħMõ—Ï}Z/švu>ĩ/nßɛ­ÖëD―ýtÉŨÎ/“—Ū^ž4Ŋ}ÏyžïRÛ$ĀË&GÄ4āÅÆĮŸöåŽÔý]-:õīzđõhŨ)Ôuz…déŌk‰Ŧ‚Õ;ŲdŒŒyX%óŲYŋî?–v―āUÂ?/Öč ãh )”P<.ïũ_ŋeŦväŠ5;šķmĀ6” íQžëZ tœÛũžþðųŒŧ|ËŠÖdzļų{Å7úO_|ÓQŊØfšĮÔ*tGņ-šģųûÕ͚æë( Q2ôuáv:.ąĄĖV7Š?œÎĪr]VęËģgcoû ü§ūųdū@rđūÚ1‰|ļĄ•ŧîŲ@õģÃī[ktŽķ’(jiŠ7Ėí– ĀũÐ;—ūEϐîG›―I Ü7X†EaîcĪčø“ÅQųÏyÉʎŸļ–ūS–ŨĒāâöxĸâõŦËōâåoŋĸņë//ĸúÅĮŠPš^ Ķ|7įZŨKGÕ4Û°ƒ“Iâm–Ã|7û@7óƒHËŪCããŲÂ~ģz+i­8ŽuÐ&q9-a dęüįÚ;]ZZ}túäŲéuIÛqN[ܞÃ[aáÛHŠrëóWīÐ}xöüÐVŠš?ŸN™‹#ŠV…HßđÞ?ÅxŅîÐA ­Ųj·š}ŒÚp˜|úðfĐRĖŽn@vÓI&?ŧ̊Ud‡IšûŪ Œ`”Y%[ŋĸՓûËŧÛNöoø°ĘjîãG_:@löÃýƒjˆž Ēü kŠYŒoo^Ŋę(}þíwĐÁÓæąe(Íđƒ"ðlýŸNe ―étšy― _Ė'‡c(3ŦÄI`PåAq<ŋ• ĐŪB―íĮŲŅlŪIWípōÍ UR]ŒƒĪð-SĻPĀJ&iO ØĢ“_?šCpK’ëqƒŲ{(MÓŦ<ė.ïûýõô>Ģîž}fFiīŒ„Œ$„’ˆcÂą’!Įņ9‚mqlCã;†„ˆY >Š@h ĢYzFĢéYzÖÞŧšzĐŠĸ{ßũޛŊïýŦU§{Z#r<>>ĐïQOņÕ_Ë_qÎSũŧïģÜzéæ)ü{-..ĀPNþJôĮãĮÃÚwĸúOüøŋþ>‘s6°å”R"2 ‰ÔSŋž\L5šo›Ï­ŪyfŨuų7Š[ÍyĒk­ΌÚJ "ÏīÍq–f2°æšĒėŲWbę6™jó­Åŋũýėŧ?úҟþ™ŋsæŒkž}đņôÓûĶŨ]wíŨYNNœšœlžDŪýŽĪÉÔjßÔōÚĨÜđ*ĀĪąœS+―M&‹ŊÄđË •ūTóKōĮZĐ §ýĻÁWŸâÜŨšô}ĩuŠóI—ÍĪô8Mr:ļë2ÆĪÖÎj+MâËkéSGÖŨ†þMâæ?ĄīŌäinģ^ódĒu& ņōį Y…uð ™­œ<öÅ?ýÝ?øôņĸė'~ô—oĀZzQĨŪKsH)šý|X&Ó,ĩ4nŌÆWAę&ņéRKU˜tÜjĢÎ^úký‘ũþėĸųÝ·ïSWÆ<h<z0H™ ōīËÎĶ­”æaó9ĮKÐÖû#ė&B˜CęŽ įIfëgåÜW™ú·F(}IóßÉŲ'e€€j?SōĸŨØÚ#eþˆi+ &úū€óˆfŪ.þKkÂn:‘~&kˆ_;õģ>>j­ÔĶ€4éÜŪÏļ†Zz äŊQE‘AúŠĐëīÄũÄÉĪCD3eąģh-Uôß[9ų“O>efŨ\sU)õßÕ·"ĸōĸÅĶM›ˆÖæY Ŋ*ŠīœsŠRÁ œ|›xɍ'†Š sýĪÆüÐ,9J-͆q#oū•G™óŽW@Ū­_cäÄLcēf"P51Íū&įÎ{t˜ ŽÝÂðĶDRîDÚ\Ą•së{3 4€<óKšŪóÜGAĩĀ\íĀQ”kĘ9ĮN€‘˜#ŪWæI4*–Ē2~Ėgw8ĀIE= 2Ïr$g–S§Nũ‡?ō3įg_j_}ŒˆÍûüpÂ^ĩ'ŲóO―ļõęëķOĮ˜ĄWĸARíÅwĩÁ`s‹jė')†gÁŧ^âX)ø+!aõxï8[š•>žIjŌTRÎ>IUi7ėqĈÚĪô%lWæ[FHūC@Ā”3S25UŸš•RkkAëĶî"cjΐXMęÜ[Á~8ŌĪ į$­Ã~Q5WsYh$æ―bč[[)­ēŊ{įB1D°đķ&”đfkA3!óĻ3 ;ĢJL1”艓C§yãŊü50"ÔÖnÆy5ŸĶŨÝúÚ#Ï“―0šoS.Ĩ"‚ïak‘zNĻT[Ë9wđsmĨ”‚$C8Ĩ2 Ōó=% ð21õ–<"ÝĒD­6ņō˜Ē―YhfĐ­ŪŠ #1p_* 0'iž°–8õ}o`žõĒj]ÎÅŊSĘ ÖĪVĐq5[]E_ûv]Ũüä*eŊuĻBœđ‰˜‰[8–!)ēqį’Žė*ąâDĖ(áHŽ—í“ēÉZlŪ]#QMŒ;ösŧÖå\ĶĘb3–D” FlØüŊþˆ‘jĀåŪ Č óQÔĪ‘·zŧLėg]cāRÜĩ„īI—c@5öŠóHŸĨ”ú~F@yš°{ÏžĻ“ ߁šĒ­Õ)†9B^ĀehØ<: ÖÎS8ßϗĪU―ðÜ1·ušïvķ:[Å7ÆäŲ1ąƒHuđ-­Ŋņ•š‰ÏĘävåCˆ#D@1bãaĪÚä\ÖŠæĖ‘/3w z§ÚÜíîq0Ās]}l%æ:?“&Įlv€"gĪ­[·~Ũ‡?zįo–Šr1…>^ė]‡ųcxîĢųī”ŌSO=õ‹ĸãÏ<ðbęräø* šÂ* ĄHkLjØq')Ĩøđ}ĒÏâÞbęĶšF ČŦÉj•Éīģ~5 :qƒš·ĒÄéYīÅÞƒÜÆ#FlDŒTë9ߑ}%*h0_𩠈ŌŨ‰"S-…æ-ß-&MCXK_ôLœüė^›FD€ÜúšÛ†#ļGyøCúP)åÕVi,,,|Ë·~ÛoýæoHmĩ5&ō’ˆ6—?$j.ýâhE‹-Šx ĢkđšŠäĔ’ĖÕČ9EŲYžĻ윘ĒųŅc|SŪ­3ĻZ8)úÏ0ŌHFŒØxĐÖ(ŠTĀæ<å§RĘSm-TĒ(ŌČï™ žþË ļÕӊܧJéÕ#ĩØ)W˜šŧũún|ÃÞðS?õSW]uÕÍ7ßÜZ{ĩŲöũūó‰Ŋ<~ßį> \~ŠĶĄ^(ĩ†Q*ÄjáyS@ßÄĘ$O<ļ Јŧnþsâ~6‹SCôė( J‘ãåšÞÎķ~ĒĶŲE ójßĩĻ씨 <#Fl@Œ „”boāƒÉ9‚ˆӐ^yb!r"u˜aÎĐßð&ŽžÁˆĀcfŸ )ĨyzáķíÛ/ŋâō›nū9įüËŋüËĸøĮŊžōĘW[Û4<éũ}ė?9zäčģÏ<Ūį%ΜSõ8…Äóŧû°HQdĘt“‰Ļ ;Zq“XJˆ$1ü2EĀypŪyŋyDJƒÁŋÛ<Á… ~Ļâ"„1bÄ<ģų īļ–ð‚Åđ‰ýļ9%!ÂđĀÓĻ&ôz!L}ßŊ…YļSÖŊ[mގƒ—^öšaK‹Ũ^{í'>ņ‰ŸüɟÜđs§™―Z"Įm·ÝķyóæmÛ·ëžIsÍ,„pø\ 9"ĩÚl2@é §0āúրbĢâ“/ "ÖÐHD@măuswYJs0:@―>Ąë&‘ï5Îī#FŠuúÔÐrG—“Ee sŸU_ ĮŽ*óÅÅ<ëôÚĐjhøÃJŦĒs“ØøRv‡į­ĩķ°“į:Ē"y”š_ÁÚ<•&•ūDŸBv“ŪĮõē/v+;|øÕs=fęŧlëKaBïÃ& į {ïūL0bÄFÄ8Պä”Ĩ ˜<]_™x-?‡VRžÏ† ÆÞGũāI§Ī”ģ:\ŽJf6liO:uré,"ŌðÕÓĨãZøáŽ;Ž: 6@sîæý4œÔū˜éĪ›ˆį|Gņ81•ZL4u]mÕĀR"fēģPæD ˆĻÞĒžŧΙ―šúĒVŒ0uÄϟ ûœ!"E†úˆ#6Ē…ÁÏū€P ]ļslÍ)+^§Ņ–˜KkÁhļÖâ…SSQN$ÂúÕåAß:PÞm·Ý>ðėþýûŸþųP’―zT-dCzK?[]>sڗĪŲ5jÕ=pėUë&“8ýs ĻĐ6a·ņFB3Ŋ•kÖíåŲĖJßĮ’ŨwÖĨ‰ú•UD ĶÄŲCČ,$ÍĖ ‘Đ8bĈ š@PõØÃU.3šJĢíĩÖæ:%lĩĐ4öhŦ‘2Ĩ8@Äļ;m.—Jސ;/ŧėō+Ŋļöšk;6Píd2!"xupŪ|ûŽíG’ÖšnR[CBÎ9Ïf=ŪÔšÓ,F™ ˜LĶąīú2Ÿ[s"ÆŌũLˆ(ĩļ‡"j+ ÍĪ!!'NœJ)˜ŧ,M q"7È#՚ybb§Vp“˜3ũķfSŦ"ČāĶXSī(SëŌ ûū XŽxCŅh71ŋÏŊ}í­Ûķn6§ûöíÞęÚWuĪGÆÔššžŒˆĨ/þāLžeCÉĮRQ!Wρ™ ā≙v^EaĢ,―ĐGt%bęg=0äÔE™˜‰ņ$‰Ģó„šŌ 1 ĩ€ZôĨ­)€‹5 FŒąņ, fČÄ5šg˜đHŊb]ĘÔĒ€šóDD[qŪRĀ― @PkÅω…8a·HYôpĘ]ũÚ[_7XÅ>48ļ†ģ)"zUĩĖÜ1GķŽ8Ý#ąW…Ïe[„PkÓÎs#ÃŒ„­4dĖy^ˆkc2óÞhY'ÄŪ›ŧË Oē75€G2’‰4‘Îŋ°ũž1æŽó.ČÁ<ĸlĈϐ7‘ė/N!áĒŽyîxõÕðV§T[ Ř•ūĪ”€ Ŧ—VĨÄĩUBTQM§ ƒœVUNŸ>}āĻó‚W!ą ąÁÖ-›—Ï,›i7 4Qsž *pšš8 XŦ~w@ kmˆčŽM įžāV―h2{ ŧA$FÖâ͒LHÔ\7›s"_btŲ=ÁĨS"B‘f0bĈ ļ@hî[USßĪĖčAY”8…]KRYâД yØŒ“—ŌģŊ2Ŋ―áúÁķššzōäÉaWv‰WqŠulŲēåøącÖuˆ+[=ßīEÅlœþđ_Ö –J.ČBÂRzŸÐ\Ãŧ‚Å6w:ņī‡pÁÖū5NŲ•p‚Dg™]LZuũ‚KßĀ%J\[!ôž›#6"FÂĶÄɋ 9 ’aE÷šRfšĄĖ"Fi˜giĄ šÍđĖ}‚Ģūï iÐlÛķMDöîÝ á/Ÿâ  4Re€Čĸ<ŅąCŌö_Þë’,pŸR"n2 %Ôóŋ,æY1%/@‹8 ŒjŸČē‰>J5X˜NEĩ5É9Â―jĪŲ‚ZíKâ6Āãx˜:æáÐüŧĩŠ@‘hc0bÄÆÃHĩÁ⹁)ÍËļÐ9Øt0qˆm0þI- Ā”üŪđ@r[ŠĻzŽ8ĮĄĸ–m[ũėđ<įÜũýÃ?|ĄöĀĖĻ€―”zŌfS[ØM[ķ 6°å”ŲNģ5pēō·cÞZ+8sŋš*­å܉TÐyRWm%ōbšø7ĖŲ%ŒŽŒ€fþ )EŽÅ2Ũūs‰‡Ęxƌđl8Ú'UĀw؀Ķkđh­Ũ9hōxF"vĘFwĩ#F@+­Ë™sĀZ !ĶInĨĻG_‡C4ę+K: 4ÎQfƒ):nûYΉÐ^ģkÏîžģÖúÜsÏ †ðþ‚Ãy pßÂŌâtņÏ.ymwóSūŒĨCāЧY—‰OŸd<Åī”āë ēšömHØkŪÂgqņXâÜ4 x‰J_ˆ8wÉTųÖFîxãI*}oæ‡{Ö/#Šuī&Ïî20ąČ^ëĀÞû$>ôÆî_Â>·Š#į„číŋ›rÖeõ^#FŠõ ŽīVƒE―“Æržį°0€ap랃Ô4ŒX"RJM‰)e_2`t8îÚ―û†nĩÏ<óĖđí9"`ßæÃÏn>hŠŧoÝtë―[ž―tæ3,›ílpρ šzÝļ5Č'2―0ŗ2œ"Ž„ī7X6ŠÔŠ\zÍĨGðˆ@ô, ãž—)Ž_:4Qčc‚}))‘gĮĻŠŪ)‡)ÃÅÂ,LJŠ1s‹‡63ðøE/žSvŠö|péÂĶÜ* 3l@Œ1RmôZĻjBÃï{Č$ŌĀeÏrŲ°Õfņ9đŦĨ;G–ķÅFąĖúĄĻ9RŽ9âj§$kf úL^Þŋép2Č[óÕwmɀ*āpLj_Í{ÃĄmîËuÅR|*Ņ™'1•v§§“äÕYYÆČĻ`M[JÜu š7N"ÎûÁŒ™K_8Buš y|œEĻ,@U%bš{Ќå.Įëi]îЙ0`"n­ŠhbĖ9KmŠÆLvnįëCīÁFÈ#Õ:tXd F^Œį æ™Ų|€S 5Ŧ­ēĢĩjĶ:é&Ņq‹fVfģÁ$vĮoFÚŲl6Pm(^#Ÿ%TŦû7`Ŧ°íĩ ŧķōŽÂ+Áüs”HŨVÛÅķ7ļÆÝ`$åČâ3;ÞžÐNNāô‡ Ïēˆõļīeōķā”j‹CåœĢĮɞU|:‹-19ņ 9NōÄ@=ÂĒĪ'†wfrAō\ÔëĐáqð(͆°!1bÄØ-fĶ`v.ÎĘĮY–ĩĨ.˜ˆ™jîR­UÕr—™8z "ôŦ/=RŒ†óí;v^~ųåf6ðėŅĢGĢ ŒˆÂæpW1ÍÐČPŊžnZþ?Á™ÁĄķÝšŲÝķ 4AkgTũÉĘÞūÜWgeŅæÉ ‚D‘ –\ËÍļq”WJ!ŨiD(ļˆˆą–đ?­–ę3;ÅPý !ó?W"e…ˆ#Æc1‘°QU8Ä~V#q‹Ŝæ^BŨœŠqʄóHÖäaÅũžÝdzóÍ·,lZPÕ!/qyyy°˜YLĩhpkōóLF3øw445Āăū ó†Ŧ™næåû+0ķR}’ÐO=:2ų:ĖcŅ ŒĶ†ū… Ķs‘ūŅ݋>ž{Z"ÖI`ėÏ šsĘđ3'k#FŒøĸ/.&štQņđ53“øŌԙĒ8™\œÐĒØĶ5ABt‚nLœSį%äjljSîō­·Ý6X áÁ ?蹎™É„ÛÔ,VÅ Ŋ ëĮŊ66üEaþeqĄ€\\ƒRM­”ŒįRÁ|‡ĒŠ]—Eü@ĖŧxK)Ķ6™L‰Č_Ŧ"PS }ņ<†Ņ,Ö/†ā_—5Tbfē† ˆ#FŠĨ”T›ļ•C?ÛMōœ§Ā˜“ĐÖR―†ÁϔD<Į+Kk>šÂšæßâ 7-\rË-Ŋ9ëĮ)%t„cÂėë―võ+ΠïÏĖĪ7“Ēýē,Wkð˜j)EÄ܎KqLŨM'€Ë՜sDôÆĘĩķJa P_'ŽGŊƒLāUÁ&ĘĖĩTĶÄĖHPĘ<čËL<ęW'ÓЁŧę6 FŒĐVˆæŪã”J­ęŠĸųXGMšKũÁ l@ōÕcéûĩ nRQül·N]yÕUW\qŰ7˜ÍfÃ`›R:7Йãë‰mE'ņ—žY<öâ–ŧ6}āCŧūĸcWþāGŪøØ_šėý;ÓöU}!áåa>~2yōCœõ™Š'é$.}蓼Hέņģa+<Ā"큙TīĨœ)q­ūdH‰ÂëÆ1ût&)'opiĶÂi Ē†ÄąX°ģ€ˆS3t‡•‰ĖËĂР_ĸ†;‡^Ŋ!ũ`čķi­åœŨi ū$†ƒ‡gÏ>īōö[ūũ§îųūŧŪđÖáų•§?süO>qäũ—Ģ3˰Šf†bŌĄ—įaõEQ—Āœ€æ2,ˆN0ōQ—°ÔÂȈām=3RH}ų M˜3€Õ&h.Bč?€ōë!>IEÍ―Ā4Š―6&FŒSmDe—ÕUM9{ÆkUĩŪ›šAmÜŸ j dbĖTk5ĩŪëRbÓĶĶ䂰ÖDMwėÜą{ũž8ĸyōÉ'ŨīëŨ”_ÄxôPĸāŋ9ųßŋëø­ïþÉõ<ļfӍßwõ_ųë7ĸíË'{ŠÕcðĄ/ëũÞöïüŋĸŨ;ãķÍw-į­m‰8xVÕ‡t· 3́Š1ģv!ųïĖZ­hČîēĩđ֌DŨ*Ų ý“ýW(Éķö_ģð:›šĪ”‰Đ•‚°á0bÄHĩv/ðfōR%11‘ĻƔc]óėÄXÅŠ'.šŠ"xށŠqb"Üūãē[^ûÚÁ!6h†21f&ĮšuíŨļēūŨ‡?}ä[Þô=?üŽïž2ÁNŪÎ/Ÿ‚5ÜūõMĸÕõĸÝVÞ1i6[|réûnzŨ5›Ūšá’[/í.SÓóķ &ŪåúŠ—AĄë:'_IL`ÖZ›Ŧĩb'ë \mq>!^*} cBb’æí þc$ÂœēŠšŠŋ`SŅ ę1bœjEЊĒ)ī:ũ†™ZóÓöNC>EDĩÕÖZJ‰LĀ(ZMÍ`ũîÝ7Ýt“ˆD-nWƒjþÓāÅЉéĐO\!ŨüĘô_^ Öðøé‡~äáúĀ?þΟøíŸ]iwïxįw}ĮŠÎqéTĐ+›ođîvøšrƒJ#Ũī&1wq)Õcw°ïR7ÔÚ"Ųևw@ĸėäü‹ņęÄ$\uŠ óŲ–K)ĩ6 Î)GšíØ-ķ1bĪZvӎ(ę^$fPôN FåąŽ*âŒŦŠD ™)4ŧ2ŨLoŧývfÚŦ˜ˆÐ` čxŲ5]‘žœÝóĶũ\ŧãrXÃR]ü‡ûþ°>:―åØ/üņ/ýęg~GĖĀņÁ=ūbšËHūüôŅ=;ö\~Ų•ā“fí<ÂEP1ÔV›‹(:/sgOTm)'vŸB?+6/!7M>ęĮ_pGœsI˜2\fS‰uœGØx‚hÁ†Ãˆ#Õz  h“)‡œ@ĒĶÐûÍ\áä”]îÂþofþQĐĩŪéOUDķíØ~ĮŊ4^Ąô ?.­čåó‰`éHÉtÉ-7― ÖaïĐūtęÁKø’Í—nÞ}ĮÎŋųĮĸėāō8vOöž9―ĸé}ŦßüšũýÐ;ūëš-—c[Þķ-m5ųę<ëÂīŠôĩ€ bŋÚĮ<•ėÄ>"-šL ĖBĶó,’ĐÍ_Ql{ŧ.‰ 22QĘÉ+#…ŽĨ ÂFÈ#Õ"˜Íģ šŪ“&ĩßPāˆ9e5óųƒg}žÍÎY–rJ)›CU_ģkŨ ôšÍfC9îââbJé<ž·x-?1–Cý|ÉĮnđÖásĮþxoRģÄļóŌ ,ūxxé08yóĘ OÜGŋø­?ũ·ūëŋ―$u⸁Ŧĸ‹_|ý?ųđ;~õÆÍŊ;ąh6ŋŧïšČ:ð,„N6žj6/'o­†y7äˆQgŌ‘RÎÞŨā†ĩ֚9JĐhā·‘éåJ2dUwën8Œ1Š―ÂÂ,fhāvÛūr—"“ží†(Α,žÕ‘™X―蛉‡ŽØëŊŋąĩöĀėßŋ‘!X8H§ë:2č Őy ?^/ë6ßļn{``û ^›H°uī.[>kØķ}7O.Ų”ŽÃÎnŨðķįq>†‘ö J‘ēFL„›RÕŲę*§4ÉSg…îŧ…ČüVw+ !2†y,åÃq8}kĐjŠ91ĒÅ7ÁųōÁTÆžÚ ŠĢŪÖóe’ ·ĸûĩËø!RTˆ(čēï %fô…ƒĄĩ@ĻÁÜû°kũžg9m”›CqĄŠĻÐYš0NNÍtiđíūþ6X‡^gÕ*ÆXJS O?ð~˜ãÆKwÝxõö_{øo―þî·ïđGV›čķnŦŠ€Ã%ŋ˜ŧŪų>ĨZ+DžlU!ǜŠÏRΘxicļ(‘Šw wÝ Þwé/3ü ą… >‹>ė9§Ä°!1bÄØ˜›ÜúŊ€S›đnŸsŪĨ! Š·ķ˜ )™ëĨ|æ(‰ōš™ūΜ9žtōd„ ާÚZkßũ‘ĐhUNä3𠈰H ―j/w^u ŽC“ūęZÆĒeĶ'Ã^·sŨoÞōãĸûýïþŅ·øo€ã7^üGŸ_üėÖnûR;4åé|Š(ĩiN‰˜D4ķČåŒĖ,ĩĐÕ°x@-ļZkk`Ķ}ߘˆxŽ@1ÛJ Ëē†Ūc-%ÎϜ™5؈1b Qôƒ#Č^1 óŽ™ĪŪNJzƒFóÞ-öyÖĢ:W0qé !ŠČÉK;.ŧlļ GĮđ큈īÖæü{Ūũü|ybUĻúúmŧašU59LáĨ•S°†)ošæ)˜,Ž#ē3ķxĪ=·Ī‡2fB‚u˜a[/Ģôæ4ƒÔu"Ē čÃoll=,ûūģÉtbŠĨoČKC$ĸΰBΜ(ą6ECœWðV3ȉ4iŠj°1bÄ î)Ø9esm,31&ϟĨœ’Û]ĒGÍķ9{ŅŽŊn[7éZÏ €ÚÚââŅ=W\QKY/í ŨC\Ėčbgt MlĮĶͰ Ķ`ëTąˆĮ[%Čó’É50pÆnBXƒ­ý<Ým –hU‹ŠMr'ZՉ󎙉iė=Ŋ+ŌúRÉ „#8SŦ4DDôčp#ö~H0š7Š™z­C—2l@Œ1îj}Å ‘Ņ%b]—Jé)Æ@*ʞDŦb Š@„Ļ~Ú>Y˜dî|;ĐÞÛHÃrāĖé3ĩÖ8Ę_ÏķëAä“ǁĸ U€ų[5ļ$OŨWŽŦЀázJFh­Ā:2|=6^äӗžxėa-Ô&]`1rz·˜Ļ7;P­-QÂäŧbÕØ€ĸžÄhæŌ·Ļ,ËYÍ<:=i“ĶÍ â“[ŦÃEŠEA`bĈҘKLķĶúJ‰jk>8!‹īȓõ Ŋ1Ž}mĩķá<\EįyW­ĶœÎœ9―ššrž7,ؖþú?@1˜ĸsķDX)ý9žŊĶ—ûđá•q>ýęž! Í,âš ŅsÁąÕĶM9%A1ÐPž‰jī7"@?ëÅ4Í;cWËæJ]rõZ+ĩyåĨŠú>Á’—ģ™Ó|5,M`ãaĈQW‹‘cDÅbHMqJОĮÛÔ<5\c,õ–o ŧÔlĩWčûY)å|o˜ƒĖä‚(fÍŽYí­Îīö*3•f`vjå ˜‰á|“Õ&bX15Žm„Ņb+l@Ÿ[WW{r+œŠQ—šh\O9uÓΈ™čž9ÍæŠÄ‹Į}ã`R)Ė#zÃĪ:ųjÛGN^ĩ&ÍZ‹jH†ˆ#ÆÆ\ģÄ){X-x:ļ/ ˆ<*0’ÍLŠ0sNđø66ųIڕW]͔Nž<1›­ĩ+gÎlÝšm―E!č8tĐF„ ž[gŠÎ‡Ę&UĖðéc‡Ž‰€FĮ #âšxoĩKóÂđÉŨŽUĐ`†á5Hœ@bj61Øéw:]ļíõŊ_Yö§ÄéJ-ŌĒ}üė Ē·ĩižPûÝdöNÉõģpiáŊ+ĨaîrŦÍ<óŽËY Z­HļqÅ^#FŒđŪ™í,灉Ïđ<€ĻUogHî­RIÄ蹁āP5iíÞü–äĢĮáųį^|ņEU+ĩ‰āę GTíĒÃ ą˜ŊfA`ķ""@ qĩ|xpåķÖ..†Î›K'Ô˃ÁQȊŧÓÎŽJR5FPŊ1;þüūþ4đŒ*c‡Ó„iJÁL뚭—ÆŦ­;sdeõ4T(ģ2[^ö–huĩŪÎÔX=Ā@PKmUezÉĶmC@‚[ݚīZk)Ĩ ð°a0?uréÉ}û}äĄâs.īZbt%ĪVkáCÂ%đˈž… Þ‚ã•ĩT°h”Ā˜ĢaĈSW­ÚŪ~­ĶšĩÕļ)i"Ę)ô§ÂœÐ,ūĸÂó_þĘWžynĸ‰Ó'éôĐRŅ`yĩ‘1( ŲĶŧÄËG^xþČÁ;væœÜū•·ä-6·9ĖŠÝ|é­ĩÁŅfmĸąýG–ĄŅęōĘáĮNŽžZMõÔéá―ÖģÂüŲēJą U5b_Ô` )ĨŪë`čØģgÏĘĘĘSO>ą}ûŽK6oî&†ÝÃtøÐ—^|ô‘GšI—˜{7æ2'3Wk°ĄßĻ ŸãIáœSQ„‡#FŠElĒ1Ϛ •šÜ™Y+ÍĀō$ĢĄœË^AÅRdf‡ÎĸäГ=ķũÎ7ūq0ã†[aāĐØÕēĢëΒÕM7Þtûm·3s)ý‘3KŸ=ēïOŸ|ðéGþlą?ū"ýQZ€>đīōéý{?0}ËjNūGÎŊŧäŽÏŸøâ%Ėb°ēŠ”Ķ;ķöĨœ^>ģš<ÛėųS'—aÖNŸ>upņčRfK*GϜ>~fuĘÖĄŪaõˆƒž§sAĖeÁéAļDī{ũŪoųĀ§Žœ39† ýþĘŊ„kĄ/=!§ ‹Šī–rN“Ηđ R—Ýē‰˜âŽ}Š&† ˆ#ÆĐ6œþq†ÓuųÖįŲyĄgfCkMšxœ+·ŌÐIĘT[ßÔÚģO=uüøąĄüB!mÄđÖZÁáŪ|éæ-?pĮ{~ėÝĸņsK‡þāËŋûį‡úÜsũ[9eÝ.ÐR]ųô3}ãÕ·Sâ.wÓ<―sû[ņÅ_Ā"vâŒčÂέ [—WVO--:ųÄą§—Î,§­ŧŪžėŠ”ÓfÞī)OßuÅÛŊđtÏ4-LĻ˜2rGymŋM_ŋÐMšÜåt9gf2UÕš­ĩâüÄáŧï Ÿō‰Į'“NEŅíšjÚjãDņâD”™įÖŠŠ0&Šņ' !ģG.Œ1bãíjÜ8e™“šÕZČÉWÕ\øEÉÍļfĀ) Snëļ3€Ō*3u<=vėčÞ―{Ŋđæšyá:Äŧį=2°ä0ÎjõåWüț~hĨ­ė_zîũýÝß|ô_œšŠķŲóÏė=üÆũSÎÝĪ›ÜīéÖwėļũs'?Ŧ|čąã?ôķïŲĩ°} ũ#KĮ_:qāSŋ -ýĢŋōģžņ­ÄO.ßŧíŧT$,`îęōîŊÞf‘zÓũý0}GNž]ݰNÞ`qŽ7œû=ōČÃģŲ €IÓŪKjÖDØ|īUD NĒM<Œ·&ĩīœ5—ĮÁˆ#6äŪVÁ Ë“ŌŠIKÞÜ­æwÁ™”Y!Æč#ˆ<ƒœģģ™ē;MgĨW‘§ŸÜwâÄâk^ó3[ϰë/ÖŋK@ÃwÖÏúŌ·&7lđþ'ÞņcˆÝÏîW·^1ýâ“}îŲ/ŋorWĘiÓtš-mĸÞ+ð+§ŸûÜÏūũÚoþąoúOgËËGO_:yęūƒũ=ūøĖ―WÞýáŨ―óôâ铭šCÕB a:ApŽŋfæ·Ï ʉ‹ˆļÚŌūōØĢ­4"까áS ŽÎ A]žaĩĒŋŌÄ\|ž%&؈1bt‹‚ŧøU”‰=üŧĻGvÍSTĖPÁž‘ASN€(Šžū˜kõėN/<ĸüÓO? xŽ‹ÖÚl6sšÓ•~EîđōîKŽKÛ3Ĩå?üÂŋ<žx|a­ŽžUÜķõMëÆ_ųŧø•_ĸá_Ü“#‹ĮN<ý‰įĸôðgĻâ_þ†ï‡ŠĨUp·#Åáú'ë‹ýxŲG˜y˜ïĸÂįO<éU@EđˆØMŧĮ Ąj8‰˜M%"žęž(&ƒ‡#ÆĐ6k@4%úÃXÄz —ƒ#ŊĀŲÍ4%mi’øøąĢ_ÞŧũÎ;ïŽéÁqáÞ`ýz!Þ˜t8î/ĨĻjßĘ[Ŋ~ËÝŧnĸÔólūnëýO|ņ>yŲßxïĮķmÞ2é&)Ĩ[/―æÖËŪ9yōäĄGO,―pôđßÞĸ;û—^xįĩo~ũõï^>9?‹[ïbq|.o뜷âžÏ‰Ņúųũ‰'žØ·ï tâ4„PžŠ{íÔ8'õ_%J)ÕÚįD[[õϰŽ3-ĩ l4Œ1v‹ĐĻ”ę)hĩÅÍēÞôæÔ“S2õ›ÄĶL”dÖjM]fäZ‹™>öØ#Č9]lW{!ĸSmkmÓtÓgŸýĖ?øė/m™lyßÍß4ąlŋqásOüŦŋų‰ĸåąįžrnkĐģŌޓgNZ<ü…?ĸŋ=ýžxz"ų›ŪĮÔĶĩ•óĻüBÅ5žĖ#Ž—pĨíũņĉãȄ~„M— ý ]ŨEĒ ‡Õš†IrNásK‰ 6FŒĐ6üVāól˜ÄR8 hŊ툛ķŊķã€Ō Ũö#s^9ģü•Į/řh Ŋx―īīÔuÝī[8š|tiyé7ÝŧđÛڊpĶÝ7mzzéSíŸĸäĸôŊþéKĮĩR†óī“§O?|āĄ_ÝũOö|\ŧeÏGîüÐ@Á€sržØLýŠo_ö]fÞ·oߗÛ?°ÖJˆæyčå óīi~{@ĨmBÄQÄ]ŨЂG3Áˆ#6 Ū6ŽđjŦäzҜ=tFđÔ9%ŸÉ bĩ*MÁeĀZOĖōō™øów}ã7îÚĩKUŨ/ .6ނĮkõĩûõoãåw6iŊÝ}Ëm;oüĖŌ1åÕĶ—^vɉӇĸîïĸršôōĸæíß~üԉƒĮĩ3°ĐßtĶ?iM~ðžïŋfûuļppÎ;Ŋ3Į…cėđOž=qâÄýũÝ·īx"q’ÖÔŽë"Ų6ŒđÉLkv!Bil„[Šï ž‘!œf €°ą0bÄ8՚Ĩœį‡ãŽÖÄžŽSōam~ÎĢjĶāí82 úšŦũ'§U!ÂCėßĸ žlŽb`ýGį%ĩĨäœ'iđ ?ōΐՙVmENŸnÜexðņŽ?~äØŅÃĮŽ”e}ëÂ=oČ·Þšų†o}׉čÂ]ðyģęŨ^,\ė ‡-íWūühĘQšÜ9Ą‡N ⷁ^)å(_H‰sĘ*.ē.wĩų~fĢaĈ‘j#‰*Ęš9%9 %?kĩJkÃãHLK)ô #tÆ`>Š43Ė]7äz=ôāƒĩV\r\LŠĄCˆÕD_ûŧŪĸ†ÛũÜ^ûjUW{›4č&öØ#~ĸ“8|čĀ‘Ã'NžÜf;_—ïøÁŧŋĸŌéÎ&íÂekāâ˄W^,„ðāK_šĸôé“á戛ŨĖRîē7ؘĻrĒĻó‰ęĩ1ðšh‹ŨY[]›î FŒąĄQĘqÝĩ6UëšlāŠQƒ”RQģDĖ™}(“”3#G1R“†Œ b*úôū'ÕŨë_ĸz‰1óBÂyũ}“r˜ ķL.ųĀï{āũãɂĒ-›ėÜĩð û?þ‡ŋö‘;ÞÝÏfCWĐuóæKūýmß į ^öâÂkpŽW\Ļ=xęЧßûh)%đÂ+FY220unuÕūŠJJY Đ‡Ž‡žŨQ<Ž–hĢsGŒ§Z―NÎģŪ™SOĨęr2IÄíĶ™Sė Š!%NĒĒĒDsƒ<øŌpÏÝÚüpėeũōoßũĖï2ð=7|ÚHiVĩĖŽÍlûkö|ðþüoáđŸ_zņÅS/]qåŪ[·ÅTþēŽ‰øÎę .üī ?DDg–—?ĸđÏ.?Θ8'51PpÍ,x]šf0§.åä Ę94 ÉĮT5%ŽU l4Œ1R­™%ǐjđ‘ kĐôeˆԃĻÚD„ãKšļÚÉOØÁ8ycđЙQJŦģþ‘N“b^þšĪ>ØN&“Čĩékų†îzýUŊ+ģšķ&ģ™ÔŠ“Ý,þóS_ü7‡ĸäūĨÏ\Ãå*ģę+:âíŨŊ@ ǧžÜũøĢ{ŒŲ5[Ķ€LŠM5ÖēIZäų&N\ëĢŦëŨG^$Ä'˜)Œ1b#&{5e͜’Ļ–Ō31‚GÉ Qi֞Ķ€k’ŌȐÐ;đ™UU4įdîtxęĐ}_ųĘWŪšęŠ"\ĮŨޙšŲ` ëšNÛ·lĸā›ÞĸĨ'•bHÐР"’„+TßsĮŧŋãžï„bžq.ķR8ëW ëiw0 áóŸ?räpĘIZk–šŪĩ hÓiWje$?WhJÉ;!ššåŪ3S-ÍÍX[8…mĢ)FŒ§ZBtpĐ("ŧĐĄ5įmv<‹ˆ)3"ÄRčýZIDE„)Ik~éþÁÖÅĖx1\0ÞF):ÎŽ,ŋĸŪoÚķiŧŠZ3hq!ĶM3ō_ýķ)}9^c]°ëŋĸ+ņU?þø#=j+ž1‘Ú<œsέ Å (ūáLĄā~Ė`ęa49åV[“FČ)ĨņXlĈ˜ė•æ9^ŠŒ ŲК)š~6BÁSN  ĩ’·y›ßköS ?ý§hBŒãõŪëVV—ŋüčĢCųÂķmÛm=Ŋ]LĪæ;ÍėvÞnÞuÝŽ5fó]FĪlKmũÜöĶۇõBéŨŧoŨĨxðeØŊaá%ĒAxðŸßŋtvÂĩ6fĘ.ÕÄð•ūŠiNÉÝ Ô`î['HHÜZCĶ„ÔęÆla1bĪZWk‰hFĘ5"%ŋųŅÜ$æķ(*ÔĻÏ…Hqeâ8^7…Ä$ŌņØ‘ĢƒęëÆ›nZÏ­~}Ņx„3ôąyéĸč·ĸåÏÞĸIœėė*w-‘••þGüŊnßžm8ķ""s\ČĄÚ"ĩ+ŪŨ3ė…ƒð  xäá/h­FH)sßũþó3@ PNŋ:é'wr˜™&ö4SBÄ!ë9#B•Ķ0bĈ ļ@0ģhÁZ‹AÁœģjˆí1ÅĀ‹H^MXkĨÉ"X°gmĩÕfjDØüh(R|ð“KKqŌuža,Þ―Pi;'YGĐýÛn{û›_óŽ…#Ýôô$­Ī4ĢzŠÛÞüođ7žXņ ŋ~Âú§ūpo°nKûŲaĪõüodϜē{ĀÎ%…Đi889uM$""3XdĢĐõģĒbH #FŒTKH*"ML-îß]ZÞ0ô§aõû_NÜDJéÍ4ĨԚ8)›+Šš{Ũ€Líđgũ?üðÛdōcÜwĮšiaÓûï}O-EÕÄīŠrâwžå­[7oiŌ^–[/vqáôŠŽóÞ%â!—ö҇.}Aw·=Œäŋ ĸŲÉģœg“ˆúl >õƒ9&Ó.6Ũi€Įā"á\Â!Ū#iėӟþģĄqrmĘ0PīŪËĩunM‰JiņÃŦŧÐWÛŠ–eŨԊpJņ›ÎŽ"ĩÖQ0bÄF QŅZrâĖĶ‘Úy2i­…Ė€ÖÜ\€äÉÓÉ4ØŌ'ÐØB(ylvk 2'S{āþûž{îđŪë.ŠĻ]ĸÖûßx·/åî7žņÎ;n7°ÄŽh?ü―?pŞ˛īŊ‘Z°þ"úÚĢîú f~ėąGúŌ`Úå,ęņ:‰9ĪĮ >Ņb­ž#ƒ.ß0 ISÄRjĨÄŅđ`œØÅs€0sGŒØ b/sŽ S 'ią+ČÉģŧ,§ ‹@LĩųæĀ4åTû6Œü Åësē˜ÖV=2 ķašÅ‹ãž]ŠŠæœÁýÁÛķnû–w―gaš€`{.{Íwūĸ[‡ôØ ïýĸN0Į…Ņ 넇LÅãŠÞi)Mãį§áņ& < Ą5Åø‚wėh|Į.'bv‚šĢ=AýzÃaĈŅÂ*š\žŅÜąÔfh>ēAm•‘ršĻ‰[f‰r*Ĩ@rk2wę~*3ČĐ Ŋāŋðųo~ĸûŊžōĘÉ~EGCPm\„Ŋa ÚßþĢß{î…įŋũ;?rųŪÝŠząõëú•Â+jÎûī”ŌģÏ>û…Ï}&'$4CĀDIABøˆ“Î3šÄûƒ‰EŠ"į,ĩŠƒY•FHk…˜âO6†(ŽąAuĩ–<čϒļô“ÔYƒ˜īÕFˆFf`~ –eĒ=7†Ô)·H`^ëČLĶĒ ĶÖånÖũûŸyú~ĸũÞ~ï;Ŋŧöڝ;wÆ66hîe··Hz=ݟūĸų/ūãĶ{oŋųÖĖéĢßöĄœR_ "^œ^Ũ=âj^ĸH\Ŋĸ*fĪcŸųôŸ>u U™ ĄķĒ 9ž―n .Ą5ŋĢDŒIĪ… }ᐜ‰ÃBL.ŪņĖ0bĈĩ@@lŪ< J)ąhŦ1{ŠEpmî:jRl2ÉMÜ.E”į՘ÕpVz@âĖDÜT\1Ö5QC]>sęw~ë7ĸÞĮĸö?øĨ_üä'?yčðáÖĪë:"—Ã9Ė)/ÎNüýÏüÂÉÕĨ~ãûÞý–·ßtÝ })ąx]ðāEö[;ė„{ũCīW5ņV`&$öâqėrœkĶ“nâą;ŪNlŌZŦfâzŊFLČ(V;ā\*Â8ՎąAžTv6ë‘ÐcÝ#‹”s§ĶnĸæāwÅĨ/ŪŌ*ÆÉOĩMšŌNŌjĐCuų Ï?ũŲOýŲÕŨ^sÏÛÞqŨ]o°°°Ž ûC&uõķŦ>þŸĪəS§ÞûŪŋt^VËyģęŨÖÄx{ÞsÅē"–Ÿúä'ũ?õdtYĒŦßÔĨoāņfD@ÄÚĪEo™SĻ˜Gũz^"•VL-O:ôŔr((˜ļËyNĩ#FŒ-  Ļ"Q%æ4ŨRâÜ%0kĨ"@ˆ–§€âĐŪësU‰~-iUDР˝ó,œ…{XaaÓ&f>―|ęˏîý?þŲ?ý…Ÿû{?óÓ?ýGø‡Cíc­u2™Ņ…,ŲĨî–ŨžķõŌŊŪūëíũ–RÎãŲõ$ûu*bšfGÎy:^rÉ%=öØÜ_ZE$”8Îr4‘CĨ51rįb!ĶÜeŅfĖŲÕoÅ|ũ­ÕuDÉ?Ą‰{C6 FŒk ÉTc_ÉČk!2dj.H`›ouSΊf.ŨđŽ=BÐĮC‹„Ö* ˜ũķ Ä9S‹åÔĩ֞ÝĸôĸÛOüŌßĸųŋõ3?ýŋþëƒÓaŲ]ķĖ|nPį! -..ūï―ïŦĩ^čAøzŅF“1ÆķֆEÁ†sðāÁ!üûÁüė§?}øāÄžoPŦZ@K)č?ŋFg0!Ïë<‘Ąú‹ō§ čdó—bBĖ „Á „ÐÐ7 0bĈ ļ@@õ$„00%5k­"Bâ\ĨÅVaž—ĻÆLkUCgg‘†Šb€Œn93Ӝ2 īڐp!įy H„|üØŅ#‡?ōЗþųoĸ_wÝ}ũÝo~Ë]wÝuà 7ۊ;\‡/éûþŠ+Ūļ°ĒŨ`|õĸĻęāē˜ú„cļ.Ĩœ:đ4đ5ņƒū‰ŠĒ#1·Ú8&DšO “IŨšVĐ17ųęI`)=äŪmŅÕH!Ä0ÕŪëÔADœĻ–m78ríˆ4DŅųq{ŽhŅY›S2uæ` Sƒš1ųŠ å4‰ūBôSĢÜM Õ\áŠ1 ûœkÄI[k}5„”Á@˜ũĨ°Í}?{ðþ/>þčĢōoĸõ­·ÝþMïyïPPķÝĄŠĨ”v‡ĸåģ8ģītęÄââĄCŽ;6LĐŸŪ ŅgNÓëðķ֚R &FljË!-ęË\ŽÆ]—pž€>Ū!R$5–Rž―†ÕæņđËĨ/óˆt$mB”˜[ĐH”SÂÚ*vybjæ%7ÄŽŅ“chčBļ#Fl@ īÖRÎäT‘­Å‰kĐÍ$îDÉ<Þk$ąšũĄx b7™Vi`–8Ģ4Y‰pÅÚbą j­6fėšI­―ˆĨœ3óÜüšģZCōÜ'ũ=ąĸЧ>õ§rý7Þýæ{ĄÂņÅáĮ=<íęĘėĖĸËÞŋæČŽô0à ))œýîc3ĀŽdnOŲ!‰ƒeõ7{(ót‰‚+ís~0• ^þ?ĸŊŸŸŸYä~îŸ;ŦŪkeĶ›ŸCĸxž§Ŧ?ŸĸCjg†Œē2†"wßåî”āÂØV››‘ŧ6‰ÝŲO’Ī‘œ,ZX„ŅũÞNw·Yŋ€tģŽM˜ČņėėŪĩ–qĘ$þ^žxĐVĨî2›Ã1yÄĄÅ9õáĩŪėŌY€ļOņÁÉHœo›_ėˈEcUW—91ŠÝ!kûōðxöđϑŪĩ„’ÁÜf#!ęĸýĸųþ?þoĸũS…`4VVwōHÐ27ĪYetwŽĩ>WÕvw3·ðg?þTLß"}YVw—G8―U­&ūq\h˜ ™E!Âģ›ĒÔk­{?&Ō0Œ’8cöM [’š}…j‰íî5ïæNaį`î^žxņį2ōījŽJ—‡ ―AŸÏu˜ģHĘ YŲ†WŨČfÝĢšē*|™g‚ģĩ€ŅĨēšHšYeâûĩÚ*G#圭jsĢ!|­ë2(HáÖ] >Ÿþų|š Āįßí­C#u°Ū&‡\Ï3Ð,ŒV•ŪuíË=ä+é: ]Ų#tQŧL ‘UEib$yx6NoMŦ9nšóp#t8ŠðˆŪ.õéþtÕLÐĪgVuŸŨoÜĖ‹RėásZk­ė+Œ–™#-‰—ņ{žS@æV „‡g5’ÜŽ[‡ŽĒT{ïɁUŦÔg­y2Zf&čūŪ+2ũÎ}îæ•™Ó‘~}š{‚tíĪ―īþýá”Įī4iÜUįņĖZóGÕų<aq˜šęģ&ΊÞę–!s·ZÄHø šÍښá}î}ˆÛŸ―\q‘ŽîũDŨę„!\Ũ:žws[??ĸč ŊÝŋYh™ÕUŊ[ėŋŋؘ‹–šĨĶûΜŨŸkõ{œ8ŦÛH ‡ÔÝ<[Čý܀™sųÚϞ&X·UÝ܉ ŧYØéŸOUĐÛNÎKÖÆŧgmHæVŠŪóáY f<äŪhs'A˜‘ēŠ`ļï#)5ũĩūzĐët—e>ęŠōIĐiÖĨRŅ€óÅü·ĘJIŨúļđÔnŨ5#,í_ĖöîĐė-c-IģG5ģû4G1ûðËÝIÛđ'ķ‘ÄĖã0ėú~ãÎL3‘•Ā7ŧŦÕæëzö“€"Vu˜Ęĩ'k”i<$Žŋ†/^Š=äRF;2RšyĚpUPîgfÏ·xuVŦé6ø9čŊ“ģōđ>Ũú͉›SŌ°ķđM#Î!GÏŠqƞŸˆīÉÛđI-jå&Ý a­‹g \ÝæÖŠ<Ģ·ŦķšÃãÐčiģEÝđ \ëTU‡ Ũ0Ū™yŽ―ũ1ū•ėßÛUUC2#Z•#7FŠ_ū$íýļųȍGíāöŦĢŸÂϘåĪąŌ™…å™xW\CÜïŪöÏáÅŧ@ÐŲ{JøŽu`usÕMڔ mļuD6:ĸz@ã―oŠVU‚Ķk+wI°Y ü đ›ŲŨ°{ĘqũųĄ‡ÛĪvđÅ4wí\ĖįŲfę†(U\kú"I[îßīw§Ýų`Ž;ĨŽ[O"­›OW„ũ!âîrũĄxs_Ũ5ánGo€ëúÔSá+Â'F’4R;Ÿ>–e3vĩ[˜CÂ0ĩEd&ÁŨČ3ðâÅßÄ[xĢŠ]ā|̇dd·ž{Sróg?{?ÓCēzW%0)‹_Ôģ€t6Ð%wvuųáŊ;+ÝLPî=‡þÕ5‡c‡ĩģŧA3·$‘ķwÍįT­›vĒĮ[C—ŋŠąšâj)kÛé^ė,ĐgiÛ ͎SvÂqÔûđqø‚N„yäŅrUËÂö~ūýęį~ĖÝÝ&ņÜe={ĸ}œF‚øRŠĶ’ nįųģŧðâÅÄŧ@Č.ŨueŦš Čþ• @[q}ηæýÜîŨšsy’ö“ĪķC=•ãgĩî6ÚÕĻCp„uĘĖfĢZ-šŊˆįđŧ:"@TĶŅjįĪĀāėŽĄjĢO=8ūōē‚ųrWũ žk]•Õ-ũ 9Š54Su 4ĸķ™YD\•’īÏįÚÏŪŪđ ðóŠŧkĶÔõÝHļ›M§ŊŧŠlÂėžó~žÂÆ>·'; /^ü=ž œÚšu\OlĢ Ę úL 0ÜųL*ÍUMŅÂuÐą–ē'Pq]+;ŧF#uÄķįŒ†įŲ@‡HH`‡ŊýĖáØ5æÝ9@#ŲYæafįÝJ‡Íŧƈcdø ; ‹†CŨDÐđóŲc,ĶŅž}ÐkIÝ%6ބ9yŧũÎŽk]+ӌ00Ö ŋpũĸæqs3ššŦōúįƒFv9ÍÝũÎęZÐŨQ†/^ü5 CwcÆEĢÏĩÛÜÕúÆ!ŦzfĮðÔhR]­äKMr}ŪŪTĘÏ4:•e$R]Yę2wšuˆk]ų4dWķz‚_ũąüš!3U2úįú ‘ÁHÓ~îü;d ÍĖrà ߙ„‘āа햇ŧEUæŨšĀšę:šˆ’`ÁˆĩŦŒXkŅyli Ā'wuÅWó;™Õęo\úģŦåūdWw­keV•1ŋi /^žø{- ĖîÜP―+í°ZWÍ(jnÏóTÕZWL·ęVĩ› Twƒū‚Dî92‚đ?{ūþŧÐÃāk-=ÝbĀää͖U7ÅŧĮä šĮĄō^ūd|jÏ;ļņŲ§é`]Ē*Ĩ2_û™Áxú­ŧŨOfWMýÁýüvRÏ1·ĘH˜›ĸœđ{]ԓ‰caģ}ĶīŪØ;[ąūå ­Ÿ’rwNkÃýlģCuŧ{p EcWŋSíċ·[ œ†–ú;vKįŽC·ú{ÚÎ{?ŲEû $ĖhŒY;č―“Ī›Įšvn<œ`>9įøŋ*Ww ŧËĖÜ­ģĶÎëðÔ$w›N’ūžŧTZŨG@VđŊÏõ™õáŲ3>—Ôø>ą1VÜũÓUĮâeYucĐÖþ søZiéV-ģĢ€UV—bAJPXtū&|’‡ËŽū⊕•Y ôZ‘]ĸY2 „Ņ=üjĸ^žS-ÏĸÓ5#ÉŠþvƒ“•9UÛņLQcļÔ'ŲAËŪIĨųŽkz_ Ų”x1Ó1Œ+€Q’åŨĖÆ0°ģIeäýÜäÄÄX“îēc1Ļ6Ôę9đbĮHfËŋ•æķž[Š:oðĮɔ;AŽR›ÍAėįîF>; ˆøgÎ‘—egDLžŸ‡ËJ3öáė"AĒ+I“ęģ>Õ=wŸā4ûmmč~§Úŋ‡ïT+€īįŲYitĐ{Žķ ę—gŦĪa8ðdØZ 4jĖĪYÝîáĮÖĩįâŽėRXāDؐ\+HJ-õ:)0Y B­’œoúÜû1sHŲ‰ą„IMØô,N˜@DØû^~žP ļų˜Įč:ŸoūŒĪ9Ś%õĻ DÓŽþÅÔ웯^qIČ3ģ·ęŲ·ÐjxxW‘“ų‹ęęŪIxĻî‰:›ÛŅ,Üũ~^ÃßŋũXĖÜb­aðļŪĩũÎÚËÝ3Ŧ•ŨuŨ;Ė#|ėR’bų”ÓØäjŦûâOgAxxxæūnhB°öâÖg?qRžpxę”|Z]ĩÝÃÍŦ "HĢ)kBö~h˜―Ásüs]“kZŽÕxšvŒ&`c-usšŧKæpîũTč??îäŌ’&Šåf;“īˆ‹vz+ģQąą,įn •ŨÞRøüĩ$âOĶÍžxņR­Z~đLڙāömï=rTŧ-*ĶLŽÏŪóAr?Ģ‘Z æLéHšó:6 Ėl&Ð)Įáļ@rč)ÕįÝō!lRģĘfāūũî.§Y•fŒãgĘŧŪyĪþoø­jįy}ænŦßžŪ3°7;‡ŊœÞũ]ÕÂ4› i ĒņÉIĻq@ßĒ ƒĐωðįŲ9ņļîÕõýEZLßåŧŦý‹xņÞX€ C‰ˆÏ—ė›)ģÂÛũæ4ŦŊ-u ū3|īF3·)Į=“r Óûï•į•Â›ûį‡4ã·ÆZ BĐÍmyHÎ=Œ'aŦ KŠýė#l―ēģ[kÜeYéūÜū}―îaôđũŠ8Ý·Û×ņ‚cN·Ęäœãļ›vÆUŠwZ`EÜũMÐÍ>[#[ę'ÍÃČėė,:#VU7:"ūķŌu]­Þ]„VÄĸԙo Ãŋ7nĶļųõYõݗ"ÜŧŌ,ˌîÝE#ŒÝ†ZŨŠŪtÆÄ ΜH'‰Ęt7ĸŋ"ąۖ>ŸËÍ22Dņŋ7á7ü{Œþ§!ü6w3oĐē$ģŊ4āXŦ|øČœ_áÛž“$ ëŧ Hã§"ÍÝfĩ‘~­îĘęaXÕm$ÝōyØđýQ_a] ĨÝI·))ņšþÝöZÐ#hĶÝ<ŋpTŪæ†Ö―óŠðë:Å=›æ ïûīáī]IáÝ­™Q§`\0ŌÍZēpĢ5ÔY˜ŠąKRW43XøˆȈBįó˜äĪAŌĖÍēŠúÝ!üIžxĻë”ã~ĶĘÅÝI<ũcÆu- 1ģ18x8i-°ßČŦÖéS(M€K„ÏiWT$í“Ūhî„}[ÁĐĖ4n&Á7QĘól3ũ4ė^ëĪ―īÉėØ ēöđÜr?tŨŪ4'œÏģKqdgqŸõŦ{˜ŲaÚ&‘•srĩÜÕŠ,4~?uýäCN·B’ `fY q}#lڈqb°1å Y5ë/^üE·Øžų“!0éŦëðĐyHÝ~ČNčsïðÃó<+VøĘ.üvŒK0\îþÜÏ4{ĪßÝG†AĻŠN€Ë΂ŲđuÂ8e6=•3–™SHģĢ +ũÐ1tģ)đāīđËĻkþŋ?ŋvv;\Ÿ{Ŋ€É^qų:ĖŪ6' c~ÓZŨD§ŧđ9G%6~°ņSÐ ēöSĨÏį°3iF·hîþ.þ ^žĮbģX‡ŋFaŊÓĩUhbbÂíÛK?+Ũl5 &FRŸ?“ą;wmŌÂãôôXwóŧĮ…ĪĐ~˜úœßֆĮ8's>k Sčën'(ŦÕíaîžÏ­Ã€îÉĖW,ÍĢtOĀŪÎ3đû$*„ûˆ.ZŠĩŠŦģÉï`ŽsÁYđfN„#―[f<ûYM嚇ĐU5ŸLŽ*ĄvUé:Ĩî• `Et5‰á‘õÅ^/^ž5ŽŋŅ<<Üŧú0ctũ˜ũOjxįNŸÆ,Q§ô{osŋŪ˜Hœ†Į‡NĢÏZ ÕkM1Ũ6šŧOOÁðWV#~~‘FŌÝšš@Ž Yî„`īįįÖ!k‡w•~ĨZýQuïĘV­;·Óļ>ŌÝX>œæąî&(Ԋ[Ċ ņaę/^žøc ōxXE#Zŧ’îa6/@‡ũxh|~n˜dŠēH:]ŋŠąåë5 ĪÛqĘb6§Yî0Ļ4ĩĖL@UÏĀûėœ pUgïWļJöíūÝĮâuöČķŪkčõ||Ķĩa™6ĨŒ)ô­Đ)ųŧ$au 7ŪëĢF·Ü\Æ|öŒĒïį6·ļÖū§Ā1*SŠðˈg?ã·ŽdîĀï]Âŧ”'ÁœÏ!eUƒxņâÅßĒÚÁŽ[āÄ0…šŊÏį0#…ĒĄŠRF<ÏfãúįĘ*4xūČ?ũ@[ĄÖ,s[åUÉóæŠ–:ÜÂWž*3_ÃģZ)[šNÆ%ĖBâÐýxĀĖÍėđošá Ŧ Ö-šÆ:1SįÞiÆËýįþ™PZAi§˜·v ĀĐųĐŽŲHČ―i[•ÕPLŊåY _Ũ5õšę‚(Ũt\Š<~Ülčs}W#°íîęsGgýöxÄkĖ}ņâ/Š―ŪC{Ąk]ꞾŊuˆĢŧÍ=xU5Áë0ãDbĸw’&īG<ũݒŧŌĄWéūoÎTIŦnĀ`–5Û ―īÎ ĐĐNˆ°9ô§āáŧ·Rū P·ø%â+Æ]n˜Óķkīņ OīBfš‡ļņ†^Wė{Ãčî0iŨlIVˆeŨýÜÍHĢÔ]e<õ$ŨššjfÞŲHėgGDޘu°q‚fÂąvŨ_{―xņZvíîĶhĨ:óĢÜMÝ"=ܜęVŦéö ģ|.į’ohKVyވZh’:ïfnŠÃ8rģÚÉ×öŊõ@0wûÕĨFļZ’&•Ķ%3Ï ÜŲ‚;Hķ:sąí3ÛũøöĒŧ›Y mÓ Á‰ãō°Šgïgy\ëęjP ŒØũ–ā˅Vqí‘âJÓ―ĶjîQ]„“ĖĘRûÉŧ™Œ1_áîÃėxē{: ˆŽ’ô7™öŋ7Ŋ–ĪĮ:F5ü:œUr3óÎŽ^înÖõ LČŽįŲ4rŧŌĩ.ŽzNÚ!ew·VkZgýÛÉHįŠÕÝîfîÝÚ{Óė„ķT·*Û"J2Z˜‡YK~ÜlĮ7üđŪîęj‚ëZįÄaĸ]yoÖáC™Y—ÆÏFCæĖÂVŽėæŦ=šEÄ!tĩŨguÕhÎŪāGę‹ˆĘ4Ðf;QigŋÜŠóĻnn6g\ŨŒÛ„Ü_cî‹RW;ĶRPĩ·™]Ũ•ŧHÚeėjŊĻŲáŲĘ)ķĄ›Õ„/ôė ōZĪĖQCÎÅ+ŒĖÚę63 {o7ûŠ ˆIŒ­.]ýÏ?Ũ đĨFĢžä 3BݍƒÏrĀšþ…đәŧ ļ>Ũ&”ŧ_ŸčŠjÅĩŒÖU@ų‘œnö5!ģß5ŦTUœ―AãûAÂÓŠÛXŨãÏýßωĮčąbÂüįđGŦVUY˜›ĸIĨŨ‹ïT+œjEôƒnõŽhÉį+°u†3ģĩ.óoTvŽÕ0―ÞÕ"VwũLÁÔēąĨnđ…YĻ;Ü v+G—j^YûI ÃYęŌŽdčn§MNnŽÉgHIS 9š`wîg0Ü=MĀV™] ‰ēįįķVcïvģuÞM­.GęįyâdØĒuQÝĀh!T;ĐSMV5þ…ĩū"0uŊðûlNÖšp&ý!ņ’š/^žøƒ5ŽÝS.ĢÕNĄÍđVÜ?ÁpëÖs?•iīîĸOjøsßđ ÄŦōš.Y9Vąę:ëNFxÍÉÕV] hōÆ<6lNœ‹ģšËÃÖå•ČÝ#üūÝĐjéŧ&ĶÝ4óˆ,˜j>{X;ũÐxķđ}ÂMgÏ4OJŲÞIāÞY-'ŊkW–ŧ ‰“öų\“P3J†Žė.`‚rölĨ!U•™ípėŸÕūxņķ0ĸ%þ5Dž9· (Vāø&Î ÄxĨĶÃĶēbÅõđTf$‘Y]ÓHæ#ŋ%­Ņ3šZUG„đ›Ų~žî4ãôîÐx}ÖT…Csq‘ãCîīpũ€Ð[ÚęîĐÃÚÝ5Û ĸŧoŨį"@Äå+§žŌŒ†ĖTπėU‡FÃĖ Dļ­ÏóˆŽ*;k֖Ūϒ0ÁŋģŠŪëŠðŪdæcœãyāĸüsfÎĐÅ!ņâŋŋHĩä9@Ŋ]ęð˜ú–ݏŧ˜ąÎŒ-UåĄïjībÅxX'{rdĖän#‰—ĪÎðuĮšĻŌTy,õk˚•Å2·úMq?OW9=wNZMÄw Léw}Ņĸïŧ››Ŧ{Hæ8s8;ÂēÏÃ`Ý2šã%“Ōu}þũó“Yæî‡úŦÚݍ–ŲCÖ5ÛYš™KĨ~Ýb/^üI]­š2KUýÜwnŅmÖŊB;™•ąz'Ë-žŸM2Ö"Ų­i&ŊŪ߯o>gÅĄĀ―3Ÿ=:9Ų+BSØZŸÏPj\Ëég'€Ų3Lø@Žxr Œąöó Eðô>ܕ―Ö2Zïâ7œĖ2‹6Ü4$ŒųÔÄ5Ę.Š~pÂnŒž_·ą…{Up·o mÉݧĖā”3†Ÿ?‡Ŧ›Ns"6ã|@°ŠÝúÃēÚ/Þ]튘ƒu@ëŠÎŠÓzāķ@ }˜($ŅIi]enFƒT’Z~šŠ)…[uĐ5MīUIčú\ršĀÍŦ[ýWķ›ŧđp œäsĢý ĐĖúķÜÜOۘŒŅl 3Û~ãŨϖ{m< NŌūá0Õtfï*Ą@ēŦqüļūVUĄ{]kšnĪ6·ÖĪĮT,§qēŧ‚^•ÓŅKXUÚé§Ļ]óŊ~%Š!zē~ņâÅßkaó/ņ]ŨPvÎ4'dm™ŲŠët…Õißũí“ĀMí{ëí]ÂÐĒg5 nNÚýóĀ:<dí Qü–ËšOfķÔt†Įý<$WļŽX•†ý#N?˜;-ÂFŌ }bėjŠ2ÕÞ-ü†Ð%·8Ų7›C<1­ŽWCÝrw?Ūģ)KÞ<}· ÄäuÍ'MeAg}qoq-uOwï”éôŋ€;Ŧ“Ī;ókq;í“Ä‹ïąØŲĻ6ŧĄÞũv;;ŲÖ·e€–•pûÍ1(sxø(aI]ŨRâŪ†ļO.âô3tí0ļðóó8üŠ‹$LkÚ3yp7 kŠvĨ‰@TĮÆHČh‡gģW„J] õ䑏ˆbjÔMƒđý&.MÏý$=•gāS8w„ xø™ĩ‹ÆO\8ė;šÚQķŅHRŋŠasýįsĩ憊ðÜYÝgaĒrĢðâŋŋ·Ŧ­Y§Í=+IšÍÔEÚ,Fc~jÁr čɟ5[s*Õՙk-|”"ĖŠŦō7bžk]_þjAxîgīīîAþsŊėýėŪšþų4ŧŧݗ™ïį9”Jqėģæ+ėJAaF4ÜãšŪîVË"ÜĢ[ãz˜Ģķ3ï†ĶÆ2›ÆŸōøÕĐęß}EįNƒe(w_WĖŊ9Aæĩ3W‹Z–ZĪųɟ4ũån°Ę2 {đ/^üÍ Paį&͗O€E̟]gN<ÕsßÕqÖŊŧÜ|âZy$ýáæũ}– €ßĒFØîŽfũý_ŋųÜÏtÃt*;}ų!ht·Ô3J·āáŨĩžŸgÅ&ðe\ UõÜOUšųģwV!ΚuđŧëiØČÎĪ,ĐKsļ—Y’<‚āýsÓ|Úsį1æžëôõ dîd0Õ5Đ:#™€ð9ķā–HŒ8âÏqí‹o^­āæÝ]•ÝŠ€đígíúįš2s#ØU4šđŅv}Y2։ģĘĖV™y·ēnaéÉmÆð2ïmÆĐr ‘ĀŸ<ŪŪŠj;AYŧî&ųųį3ĪėA’Õ9gþÝ]]Ôųa–Ģ]WdĨÐNóįl~ÝИJŠlw3˜ÓÝņ”iŒ–€^ņa›ĸ"žgŒ·imčQãæóÐlâÍ$M‹Äó<8·&xf[ƒ@čOÞžxņ.ˆĘęî 9EXÓ―8’Ð ü&€Íö™mŦ†g—đWA§ŊˆĘrĸw„Ðj3ŧÖ%Õ~îĩâZWf ßģþ“cÐuķ '„pMЈ9GRõ™Ww} P>[8YŽčŲ)ŧû} +–™Ũi= @eĐk Äō˜ÜūĨgĸæVY™{ęouXܗWWVæŋ;eA„ýWxÓSĐ;5æŲYƒ9g˜=ūŧæŸĄ'%RxņâOâ=kTuy,’“á2%ÞӜxŒđŒ6Qĸįú Ý˜ŲøĐĖéá~ĶNĢā7#FÂIØēĐ=xØ<Î9˜E,šgķ™…{e>{šŒDPësM1™y,€Ï~ô­VėLyÄ:ƒ°Ïˆ U•әøma4ĘąŲ8ŧ›™U—v??ÝÓðؒhü–›ÚīëZžČąYŠLÉMUō8―Rs˜fæ?ũS}ļ8"ŸÝ üMýÁ‹oáô+ÛÚ) ÜŦR€›ŧÅŪÄpđŦĖÍ=Š{~øų?Ÿ|žn˜Q';—yC3MwaežŨf^;ŅlÍÝĻÖ~sŪëÓ-áæ•)āĪÁrFP@*ĀŪåęRÉ ŋ%ūr‹‰$WË-ÜžTÝuˆ8ŦÛė{ZÉÂ'ZffŽîįŲ”ýóđvå$“ņ5ð·Þž+[0ÔÎC§.ĐĮ$ö[§ķŪÕÕÏū§ŋ―û[gyE,w /^žøsĮbáffjÁpÚ―-ó°ðgßFš}OØ!yD ÃAĪåģ˜ÐŊ؈fŨsĸLņxVvëã-egĶyîMũX‡ ;Ýýīä>'ZūâÉ)\˜Šð§ŠHë–2Ug}q jb·Î0þÝÔKݙĩsïˆKøM~™ÆÝ d͏ņݜÖoF—™ ĖfæĢdĻî”æžũļŪ‹$bÅŽģé$˜•æÞŌl‡§17;g@~ņâÏá{Íô›u ÎĖ+–ûū%ļÛļķÜíŠPWWKZ>óĢZZWo˜™õn1eÝ81ÕŠ–Ÿq5wĒ@āŲĪužģ56ڑū Éý<‚ ŽŊ1 Ũįӓ>+MŒluŌaķÔ ÃtņþÜŧēÃĒ{ĸþb*wúēo y+>—‡W·…/þvGÂ=” ˆhÏÞ;Ÿļđ·ĻąĨ=ģ2ū>ŌDÛÔĐÚý™ÚÝó1PÝ]Dļŋōƒ/þč" ßÏFũoëƒÝBU†›ŧ?ŧ úI ŨŠúóŋÛOwĄ„}?"âŠáqŌƒŦšÜŒīûpŦ‡“øųđÝlĶIāŲÏV‹r‚Ïs ļâÓ­îęÖšĒŠ+ÛÂĖŽþE'á%AP4óûį‡ÄņŨŠ*ģ70ģõχ`UG8eˆÝAë|fĩZđn‚Øĩ§ífË7D PÕzxÕ<ŋ…ņy2ĖĖ=ģ v·XĢ&Î*Áė%Û/þ"ÕÂûÞ'šÛ 5ūđ‘đŧÚŨTsŨļžž{“\ąÂu‚ĪMđ7ŒNv &ŌÖšĪ–DÐOķ·ZŸ>UUjŌ|…ÄĖM0ŪĩŸĮÃŅdëɇƠwwešûuōgKmÆó8åœMÅZëÄÞáN·Ę ĸĶ}SĘdfGÆ{›eÕÝ]„ŊðŠę.sƒ8ëWœ@ē―ˍÝŽ[āŽ5XOþn{wfŪkM‰ï 7sQ=Ąāîu"ŋ tšú͛yņâOޘųËhn„uŸq„ņūŸĘžØŠ<ūŊk–r§‡›ąfzÅ·CaÖū˜\ÍMꎞÍîp_Ũ Ö,š:kĸšđ˜ŽĸŲĖãė:[U―ŸTÉÜēę<ĢMl˜ZŸë22ũį[gcža]­nĶ­J_+"‹m?EWMąUŠšŪĩVėį@w_~?[ęÓ4ĄÎ&­ŠcĢXî`úu@ä“ÝpũÎäægÄNu/^žøk5ŽÄ˜ÁÂŽ§mÐ(Ā ­vũpï–Ļą!œ.#bXF‚›SČŠ‘ũOôâY{úó<3Ä•;€ÎÝ'öÚÏ3OqžUEĀÝ%t'Āð‘Á&Íâšež]í-ÕNîßÅŪGŒŨ $Í‘Ī›đUUwûŊÏâl! RwĮAU™MœļŦ šUukšœ„9!Ž,u{ļĮ?™Uįņčœðï)ķÉÜnŌhü_XWĩäî‹ŊŪö_asqDUfŒ€™‰Æ íæ‡ ŋĄāâ45ŧš {ïĘīS3é1á!@g€]ŨĘgŦåæëú*ąŠdWŸÉ9( ģVU— ļÖÎsAÄŽ_A™;ÉsMG,𓓓ûėY_œĮ{ĶĘlj&§ÜėÜš*›ī|ū ŧ[MˆųdWĮŠoVųLåŠÃҧdlŨ7úĀÐ=™đf‡}yŽČ įŧŦ}ņâoR­ŸBō§’ŨfĶ!­ģ{SiŽčŽCĐ3HÞû)šŦ{?ŧJkøë û|jW·,üxp{R=ė[Tî>Ý9&bĖcn6CņlHigó+|Ū‹N€G1öÍŲĶ#ý8ŋŽUhœ•kæNŠk5ŒÓõ{ŌËäNsfý ·øeNM@­ÔÞՕų_bäH.Ö9ŊÛ;iļŪõåīXąï‡w7ģŲHļŊ―ũýóļŲĩÖ~ķN…eXĖÞãŋ?‡WWëËóŲŠ6B§‹ÔįsiŌąÐÓ|“]vØ0Ŧ$EL1ĄĖf•]O†ŅƒKlŸëSs ĸMu)ƒÜđ%ĸEíė]ÝĐî5ō†]Žĩ(ė―‡Đ’A‚Ė8]"Vî‡fÁÐŲĪYØĖŠ›{ӈd]IÚu-ũģÝĖ'nž:)HsSC]æ$؅Ûœn1ÜøÜ‚V[Šk…4É žlÝĩk·™>ū2“†j…ĮŪŦ}ŠŲųÞüMžx§RķÖgĐzĘ `VÝb$OÎðøëģršĄÔŲ™ ZÖîÖ7qņÉî6ó.Vhî@4ŦŽCÐaîÝôĩgę<% ­dî+\jĐH›’.šđđyŒ–ë7ąĮðÖ;ģ īˆėęÖåãģęÉĸ–ĶüÆwî.Mtw}ĢĮÃܞ|HčŪĐÕņgc„™Ý?7LaÐĨŽ2ZvĄÚˆëóÉÚÕņŠ9ÄZ+Œĸ“/^žø{TKÃZŦŦuņóđ:+ũö0 U%|…ũ YøŒŧ Ā:qV- ûWRJĪJ’Ũį‚˜™*­S^Õ h< ŋŪ™uU·ėî“:Äg+Ë3 ’đIŠJŽ› A.ŽILĨɜÝ?û{ąņœķ}— ™ )"2›fãā8j6ÆîįyÂĸÅ7ƒÜč4>?ŽĘ2*ķUÕ'ß Ÿ―éūŪJ›ãÍåģũdĻ ØYîîïöāÏâÅĒļHĩzUrïCŧĀ!ļĘ&Ų‡† ‡†Æķ*›Æu…›U&ČÉoĻ0ó3ØBÓŧ•Ïdđ†ŧ·šŋŽS™•y]ãA6Ų7㘁WŒ]=ĩŨŲðŊyUķk—AY˜ ÉŲęęÓ"Áɟ5óV›ĄŦH|—$ī_[—­ŧ…ŪŌÔ:HŨÆü]͇­ŧf }Û<ŽŧIÆåģ BxĻ5―A óČuņâÅÄ+öšĄltþ}āf>R'ˆݚŨ vĶcyuJ=Õ/ÕųŸ#vj|Ģ ķoA°ęŨ”ETU6ĖÎgßhĶĐÝ]#Âp^Ý h[ņX3g‰<]č}(r< {į4þfĶ9ÁãaŦā>=’rsĐÏļj‡ĐJŸąīģÏĘĩĖé}ĪĮ:Đf0 mưčjŌfÜ―ôļÛčŨĩ u‹ ^žxņDĊˆĢ9mšųrŌĖm]aÆýėV_§äĶŦېUÝ9)ĻîÃzNŠ:ÍísúŠ~#ŧH4ÕŠšķąï(m<-2ęŪ8ýā]Y]f:hīĘŪ{w•@õáĘðS,6m`š=ōÞinÝU{ĀÖģŸëĢ2ÝéFũČ*@Få5ėėž357fSAðŠeîÏótéqųįh$rW—HÛõT•JcöŊ§Õ-M;ģJ$#Þ†/þ$ÕV–ŅFc@ģÉd‘zÄĸ•a'â:ŧ 3Ô§rņZŦēōؐuë2Üýéėîģ°ÜYYcŦUĸ îėV§ÜūëW“yŽF  čî'ŽVkđĪsëÛs!T7ßþóęIÞúŋnzr]hîvf·ŪXE˜@óčJ;R‡ģŦM#Ŋk ūl5pޙœ‰ØĮ…|–đaņdæ“îËģ ™…ŸüI#h$Ķ\rųęîŪ–ú-ū!Š/^üÍ]íŠĐų‚qŠmZm4YIĮTÕBËÃ#+Uíņå5šEļ # h­+kŦæžï NUéfnū+f€SÝ]fðū3›2Ä| Āu]ĨDĢĨÏĐ77Ōà >;ÏŪÃÁomøŸTÍÖÂ=ΚX ĖžoßĀߒ›Ą[^žxņ§Z@ûÞ ã §UVļû咞{ýŸĢ=€Ėx" ž§šŨŠų>^•ī‰Vičļ!ÕéáëZG[ą‚n­ęė— R›{ÄŋÔZÍö―%ørũhuWExĢŋõįīģN5§ ÚÏvr]1…=™ĩb‘hČaf^ÕOĶĪpŋũŽ\œR[}>KÔódU‘č*uKāoCåīĐOļŨ”ã6ÚhŨ|”™GUZ#ĖV6Ā ŽCĐSņđeˆĩt /^üEžÉ^s`x*iüÏIefīģßĪZRƒānÝ­ÃNîŪîSņ^5ŪËF*ŧπų2ððüfĀ-ÆþKڑš%nNb{Ŋĩü<Å#Ûʝ4„ Ŋ•+žē:‹îQęþféŪiÏ5ĢŲ·ÎÖÂ[Š;e8―ÕÝîf_ÅÅ,pm\ËĢ[ĮähcŅ"ÓļncK{6Œî!ĄU$ė{$+tßfķ–GxeķúPdW@ØŦj,gö_šđīܙŧHh•$óoCZÎcÅ‹§0b,'Ÿ> ĪyįŅN ö―ĮļŅՙoá͋Ԙ ĀdÝMs ŋ ÖÖՄ` Aõõ­‚ČÜSÎļsï―'Sædĩd„_Ũ•{•htž |ųI°Ųy&>ĩúžĻÃÎ-›Ïv‚ÓĨs6ŋĩ“fË€ÞÅ_qÂýÜāŽ†ŧš=Ïķ@6TĨýĨHkUVđŧY` #š+֙šgSč;VŪÉ ;ËŪŽŪÏÕį9_wïī‘ß'ÎæTNTwK8ebOŠÖį›\ÓhŌuĸUĸ‹ïŪöøz­Ø•&ÃŽ5Ŧ%pV™{ÓŅ-ž―Ãm­•ÏÓÕcóŊîÚIÍēRĢUݝâœãŊˆŊހNXŨt]Ũ—Ås{ö6Ó꘏Á ú)57r ŌÃb]uV§$9ý‡ÛÄuũœÝđ1ŸíæĮðķģ3čæÖUšbņØyiæē„Ŋ“Ņ%­kđG=Ųiîl•ÐŨuôøŧĄnÕN ‹ģ‡™UɰķŽĻŽxđöŋŋđŦ5·9&2Áø;͕q\]ŲUn1R­āŲovuë$8ˆĘMš$įqÓŠœŠÓ #ŅęZËÂgeģ Qæh€âІėÃSW,ųĪJtƒfÄŠqĪ­ĪēRh ģˆŽībUŦjŨ.7‡ÔÝnūbeķ Ž ķ6aŽ |>ĸˆšũķCÆĩïD/4QŧæoUé‡gĨX0sÃāĪĪŲÐú\Īw„/^üÅ‚ &Œ1’XšÓ,ïM3#í$ƒffU}H~öžģĖ9ŨōˆX wnĐ-VUŦḊíLMī‹Yí20܃~$eéWœˆÛœĀ„ÎÎĖiEĖÜcŦ5pÖÁæŪoÝà @eĮr;ïÆęĩâÛ؈+Nžb–:–Ēó+ü23Č2‹Ô {TíZæd~ü Ģr“§9ÛqNq:[3gUV7ݝÛÉĖDVO―9ŠĨnh­^žxņũĻķÕa^đ4wĢåģA"mģļgUŦÃ=ÖwāaŋVÍ@&JG™ŧŦĘ—_ʞ-ðŠøy~Ô °ŧņÍËŪ.ôt+”ÚĖÂLY‚Ėmv Męęvģð5^söÏixĪYK{?jœjČŲčĻrNąŽZ %%Š*77B] đŸ„hųú†=ŠÍ―[úВŠE3•žJî†ĶØg'#@ft·Ęl‰-Ë7ŲëoâÅŦ@0ššáÖÝ roŒ˜`äęu]““―âôŠ?33Ú>ĪlFÕ―Õ'{Ķc†ŸķÄÖ Ÿ…ĐÃĶÃiÂ!;RŌē·ū‘ æÖ%iļU-ĖÝŧŦs fÓŅ+fvß';&ށĘ$đŽĻķš@Κxį3VïFNqzũlWUĢčîqŒmR{8ČĘ"eÎ>ÕdvĪŧóg›ÛoØ#OŌ9~þ0"~[sdæ_§ƒÜ! /^žø“TÛ­ŠT•O4ŒúŠËÜj–W`DKnœÂ˜Vļ/€ÂCŌīęÆĩÔÝ sóÃģ“čð—Â}*ķL*MW››‡į“㔍ãÁ§ėÜl‚šÚĖAÜÏsx“Š{Þ BwŸ‹―šģōZO―9„8óėýŋŸÉÆíRVš…‡Đ%Ļ”aŸÜų5!Øļ9āΓ?{˜Ú­Ï;“\ūöÞhėîxæsĮëšÎ'Mƒ1ūÕzÅ^/^üÕ]­ÔĪŊP·šNbaÎļęjt7A’jMK.ĻÎyÅefg?ŦuŒ ‡ qļŽ‰ŲũĒ&ųÖėK|DDč8hûï4$šGĶņ?–ĪŲsÖÐ0a…_§oËÝ æNĩÂÂ8@ÝũwŸo‹XŨ―Ä!pž…ÃĶņZëyžŠš‹ŸgWŨɜEU Ĩ’Č]ŧŪkœļ“6Ý6óypß·ąbį“™6ԟõÞ݄/þ$Þ sW æ_/–šm‚ĀģFÅ%iĶTsWŦÔF€Úđ‡n4ŽÐĩŪĘ>WÚøqĨ†óÐîåq]Ÿéaô0˜ÚZs'•{‹ũ|ŋyÂ‡Ô Œsqfšđ™gfWÆ·ØfÃÎæ·Ŧš@\늝äÔr?#H0’įîmfÔ‹Áó<ßģþõYˆ‰Óõrg™óóđöYæž$ģkzpŊĩŒĄš4yių=ÔųĄ› Ę,âŋpWkĶl|téę.ëŠÉ+aÅŠü–†đ}bÝĘ|ÂC4áF|™ĸ=· ›ėŦ™aGUš,<<ŦHëŠãāzŌ)iŨXđHÚØŌ̟uš š­Š" ĄJ’č|ōĖ§›@ Lyˆ§ūŽ27WɌFâŋqŠÜÍéŲmNũ5ƒp‘]ęþĶ,>·#,ĄŽ=î2РĶāRM c“?ũsYÄ #ŦÔčp?dWf<Uîëģ vïsĮåÓ1~ČY Ámâļ4ý84+møø'ÚÜĖ#ģFÚĩÜzŨaGrĖY2 'I(ŧ>Ũgw‰Ó(góØŋÏlĒeč. "ģúÜ ĸ|>hí:Ĩa't&ï·ę9‡f š•OšÓݞįa†:‹đMļí‹/þÕNcnuįÄŊøĘ3šnŠBëŧÝ : Mšx„CĻ9Ž_žcECūđÝNšå“ß#ŽÁã‚õ{?„ÅÉBüĐێ$ËO'Ģ[ļ‡ūĐ`>udĖhī(ÖRWæÔ‘Euw&ĀļĒö†fôČÜøU>u­kWô­ Î"mHXæ ؕ‡Äg-P°s—ŽVŧŲ„;ėËû\ÆęamwË―Ã\$ęšĀɖōŽķ_žxņįŒđÝڙęŽĩŽ„Ë"ÎŪģÝ#ÂŅM‰ =ęQI9įQUӞKX’N–Ŧüðnué›E›jĻE;9^Fš ĘJ~Ó kžaąė<›Â Æî†īŪ0pĪpvŠ|ĖęĖnˆ)K™‘nwfĢW,6(Aøšā@7ŅÅó<";s&f>>qyŽ ó[îFSÂĖGøÕ‡Ī öŪ%hBsĶí1–Ŧą!3GFņâ/â{uðˆ.M‹Ēŧ=Ï­jŌ,˜™œĘXĒģIš­ņ}ř:Ÿc~%đŸFHÞUīo,ĀÔ3ķôðåaõ$I3Ė]ÝB wVgŸĒYæ.Đ~îYįâņ,ŒÆËjŨI{‰đéĪØœÅŪūÕķĩŅĒņÞϓÏëĀ0Q0äÞ&ŪĩĶÏFęÏõ[a^UŲEƒÓ8Gpf0tũīĻu įÖæ–įb˜Žđ7Ē;3Iđ9^žxņĐvv5)zP‚Ņ|-wÏgũĄ–`T‰°u­*ßBïį~†õƔÅĢ 0;d ^ŸgĨJđĄ2ũÞM]ŧĶūŽ„ėåŨR”3"Ö>™în‡—ĮõY‘—}žXëūŸR›Åk5IûÍŧ1Ø?ŸŒÁ=čÖ­ŠéīûŲÕ=ujeæ'söé,ŠÔ]FĢŲ>―•ëúš6šk­˜\چÜíÄëô ‰Œ―w·bÅH|[ ˆ/þ(^]mWƒ_?U•TmīŸķŪïq–ĘÍ}đŠŦJ‚‡ŸÍ#5)*gá˰VíÜS?ĐŧŠ*ü05íë.Ëiáî†u6­Qx؜V­Xv˜‘Ēû7 ķšf;ŅÝfūVėc7pÁûū[ Ō`Ģˆĩú<Įw}QŲÝþ›Stw;íđĶ_§gs’­îßbīĘ2Ú$@VÉëšÔš2wSŦ[Ë#ĖæŸā+5›čÛŋ[NþâśėĨnIŋ‘S™™fÖ}(°ëT$<{įĩŪŦ'[–ßÓöĐ}tóņđęDíDsÞQ„™›ŠiüV+v“\Ãģ•ëdŒįÎ1ŋ%ÖÝÝqE렁'M_ëZøũÅZq-AïųtôZXwU&„é^Ęc‰Ÿû§šÜÍĖ æįÏŲïŲBŒCŨĖæ/üęŌ†ŋÅš™ĩ3'"ŋÛÞĸŌš„ĢýUĶ}ņâ-'ïîS.{硫S ÔÏý?ŌÜÌ{—ŅŪõéĐ}qZøó<’V„Ī―'ŨĘ=û1ڊŦŠÔ°p7˝Ē<Ũķë aUĶ_‘]80·Ú9äE˚ØHŠeÁ|đô플Á5Ác4žŲ6Ũš&ŦaīīU]Ýã(ëÖâ ý<ÏtܕŅkß { ZáæTÉ`ĸ\]Ē[\ąũsrđÖũCå@K“i@@“Mū\R}3v}ï',ÂMÄáYN’YdU7š ÍĘ:áÎO‚ŠēVwGxĐ i\^•Ļ6‚Ɲįđ’ ™ĮGœ§#óŲ€`$ąïÛĖcEĐ8,"0Kw|―v2Úy6YØdKÎÄÝZ}žMfþ ^žxËÉiîüđԾₘÏCõųZ­Ę2€nÓĨ(‰>gbø|.‹P‹ŅR§ [þuũzĖiû›,žU]eîGģĨĶ>ĸŽÎꂹI”ÔUáD{yŊëRĩôíŠŲ'—ˍąÜ@uÓŽ”gĻ AĢĶ }ūËJ ŊFė|Ķ莥Žė.sΈMģˆĩ3iF’…įIļ{˜ŠØBËhĩũ“éæ rÂqÆÂĶĶ@ãĖŨ Ex·Î*ÃéūŸ$þ^žxÅ4FØþ1wį ~53f–ô%ãįÞiŒģ"ËZĮ]6Ų1;ģ;ėB°'Ē{ žŽēõԔéģbô}ŠtrWUųĐĸÎ]AîL vˆĖÎOû°›@Ð~ƒd|ĨĶyW·pļëÉ=û ™öģOķė§ĨĘŋHĄYՒâŸkĸ<!†“ކ‘@4ڝŨōû~fäw√Œ4ņ„˘Ó܈Ü%`]>óōH#v 4+HŲ†?ˆ/ÞĐķŠ'šķŧvΗôo•VŸ%)E4ų ČĖ'ØÕ$,|ô[ ,LYjL{~þöýÉɋdéŨïŸ//Ŋĸ\Hũ+Ā1܃‰^‡?žøįÃŨŦ/?ū3eše]=#þá4ū:;ŧsxpõųÜXqqá9” îąJ=Ea‚ęAm›#ãÅ8ėtPÔŲ öÄX6-…†0K:K "ķ,DÝøE–Nđ‡q !ÞI2ŌvÄŊ!qqNÓto­Âž‰ô˜8ŅØ-#įÜļڇ~ îŅ1*—98ūlkŽąÆ>š―(JDʆAzlD>û—―óþŠzþ–ė&›F‡: J  Ĩ Ą#"―ũ"*Eš1ˆ(Š"Fzï―#Uš„N)!Ih Īoėį9s|ßĸ!wåæîŲsîÝž;wÎĖkr\YŲÕÂjþądyįÎ]nÝž a+WŪâįĄp+TŽX­FõĖŽ,›§Īî“ÚUžd‰j5jdffâiōËlŲŲøŽõ6v:iF‹`.Sģ]ĶrbnNW -ĸÖÛĻð>ČmŦVŊQ-ŽzöŦ,Õ'xUĐJhíðzū~>„‹ģ5LaSgž)ģĖ2 ĩGģýÝZgEéĻ)‚]ˆSū|yœŧýûũūL~‰™šúp|5ujó-ŨŽ^IŠ~Ÿūý““_Ėųáû “§ä XývÅ °Žt™23==mč⁃‡ ũå„ËW.ŸųëĪÓK{Ø`ęØŽûrü‡-šOœ0ÞÛËņMÔĖøļ8B 5Þŋoßųs1>Ng€@Ũ?‰―zeë–-!åCþųį†(žhˁÍ-ÜRþ4)ĶX€y5›G@‡ƒŽ-ÐI‚fÓϔÕzČŦŊÜŲ pØ―đq6ýāÝCKãëƒQöĖėšīÅõ$a-~ÉôčÄ[Ģ †ÍFYvŽKĒvgŽGX%ŌßH.„€Ø“„e•{ȕf™eVaŪņmF4 4€Z(čåíãëëĮ‰ŒŒL‘ģ’Ís6hÐðîÝŧß};cÆ7__ū|™0n‘ĒE˔.―wũîïĒ"įÎų)%%H‡„/^°C‡ŽÅŠúúø”+âVšz\Ķ ,VÎŨŊWŊví:ļąo―õvbb"WUþ­·@įōåK_žHNOK ^ūtéË/ðRĩWÍÝps€\S§"ɆāĮČČĐC՟m՗ąes–’–f‡d@œW‘8ðžĖ…1!ƒéŊĢĄhN°†Q Wåíd“Ī4>VÍЀōLvp ˜ķË%i°)ð%rÂ{…ō–Y–ûĖōj Všĩļߓð,ņMrÔã˓ Ւ“āOĐ2eHĖŌ§eÝ­ũrĻ.—dt™UéŠč-ÞŪ”öŠÁh_?ŋŨĸ9sþáƒûŽbRb=0ÖUeBiô­-W­\qîüđĩ+WĶūL4dčč1c›·j5°oŸîÝš:ŽC§ÎÍ[ķ2°ĸąĢGļNŸeÓIŧã(ŋĀ ĻߊĨī^āŊ“ą^žL9ŊÃ4ŋŅæ „H#3í5ĨĖåĄ=Øđrq~™ÐdãĒæ)%øŧZXáÐĨ_i|VŠTÂ4‰Ā·ÍöÐō9jųĐnĩGŽŅTÔÏÄ2Ë,˅"Š6hB“ņÄpČ`(„‚§OŸa@Ϟ―Z·Ŋ[ŊuDDPPņŧqwK–,^;žF*Txpĸn,ČŦZĨJhÕ:áu ,ž=ˆS^ūüíŨđŋ͛ŧoÏŪ„ĸ5]™ \<þėé“' ïðĄCģg}·aÝÚkWŊ‡Üŋ?f䈓'OV ­J°Â••9mę/?Ïa捛ÔĐ_øČ1\‰xøĨB: ˜ŠÞ .šsšG†ÎĄø•|ž%Y™7j ƒĨzyY›ÐPSeí*ÚČËĪU˜” ÔŋČ*c‡fđ™š:BÆâcö–Â06ÍīxŨī“drqA4&Öáa™eđÐŽj1Ýēw›„xaęėĒŋęģzÕrō  žf݆ĶĪĪķnņáī)“F/ž·āwΐöôÛŊŋÜđskŅŅ8ž[vėzøðazzšŸŸß­7–/[ĘÉíŧödddĀ™ÝŧáŲĐŦ+Čsú8yšĸų§Ų?üøóÜyó *øÃŽï äÏ?hč°Į-Ztßū―p|Įž―ϟ=gÎøøøƒûöõęÓ·CĮŽąąWþ}ôÐÁŧ[K~™ÚëAÍ0]Ģ"ōŸ4āŅj/ÓĻÆTĮá―qi–.ŠąKˆA% þW0ĶYĩRÂŦū4o$ A Ąķ Ŧ3†žlnmā˜GWQ—_=h//UĒAZRbšRfĩËĨf™U-ĶīęÛSgS8HKMýþÛĻŽíÚFEFN™üU›–ÍÜŋöĖ™Ū:˜õÃũģZĩhvčÐA4[g}5jÄð_įþŌŊwÖÍ?X8}?óÛÖ-šáÕΟũk·ÎŊ^đ’ˆw>úŽį'Ýšuéäãô=|øP—Níūü|ÜʕËúũé―~Íę… æ>tÍęU#‡ýbėčMÖqžô…Ļo"ŧwí|úԉEŅ ÆŒy!&†)|GpnΔKHšŦŌóF* T(GÕ aŪ63ÓėZ9ÁI1.ÕøĪ €ŋęrûf€Đs›Þb&ÞĒģ‰)Žĩ,7Íxōč•\€ÅF 3î\ÛÃŅ2ËŽX­Å† %rÝēqĨþīö:}ōÄß§O‹Ú‹ë•'Poïļ;Ø-Þ Ô”>ŊiķeÓz`bZØĩnNFú…s1W.]":aĪķ@#˜üöí›ÄČÂîÅߍŧs;šé<ķyý:o§·Pïļ îÛwpĸ>uÚHŅ―pþü… įy‡€,­―ŋŒęŪ8’FïFuŧ!ĶÖVHEœ§ŧē%ĮK―NjU—Ýt™tK1ƒF!īųš9cۈiå 'Á-?F5œš2Oí+ŽQk·CķÁžÁ­ 0†Ý8S7b 60&wŠ(Zf™@ĀÅ3Âø\4ž1‰Ÿše$ššifÔ^pö`ŦxhŒĀ9åHóVĩ%æüsÆŪÆl6ý sĪ2D3īĪŲÞh/&Rų§õTÐõŋÁ`_ē|đ$RbUφÜX2›ŪæÉI-~•Âb}6Ũ|ŧƒÝ*•evĒŦŊpxM)-Ū·^šŲÎrëŠâÏ—Sx·Q{0QV'ZÍm3ž·‘Ļk"2H:ĩ+:QÚÕ.rđlšðşžL-á`&a†eŧēõ’åÂsmÃË,ģ0AŲęåāP[';8ÖgpÍUOŒ|žLÔąsÔ+d˜ ‚5vŸ0­sśc_ÍS1Ąp 9jëŧ ›þƒ‹å™DI­=wUM@Ĩŧ_q€v—`_CŪĶ­1ĘíЂ^ŠeƒĘ `2‚l§H'HGŌ2€6ÓøR›Ž[ģÆĖĶŅSÎrĩLÂëęĨŠz™iÎh7pd>(VÔ|0-G‚ \ÆkĶ!9įUqÁ„/ll”‘‡ËŠ?ų†Ë,ģ,7`‹1: 'Æāŋq^ĪĶĶāņK“·ÄpĘ  †I%qՕéœzý§ų _ÉūÂðaŸËĖ6·ļ^Ēö’ÃT° IГ%äØÍą[Mƒ2å—M:w)ŅČÁ^ŋēį‘Öķ&ˊ‚WÄ`ŒŠ€7—áĘæ@gŨėŦ<æþDŊËô”TĨCæŠJ•HÕāYk|VÝkõ\Õ]åëkéŌē4&ÔŠ4›F!­øēķšŪĶɂúķš/ĖK 5yĩĶ{9Ŋ þ Ũsgo1Ë,ģžZžNuÄLŊq`ÝY"ÃęaS1B|ØV­ÛLœhV PÁāāāIS§U 6rԘÏ>‘’X,ĻWß~‘QßEīmïįŸ—ĀÄfËČéï?pøˆ6íÚFī›:ý›?þ$8ļüĀ!Ã&OÞŠUĻ‹)úižS#̆ N%[zf:Ú4§L+W.ļë'Ÿ~üiO§Ó'īJĩ1ãūœ2-0‹­hŲĄrk“ÜX’Ī·‚JĄƒt>QH€ūÚU—óqUčL0i 4HÂgĨhxØbN‡·Æaī ßVĐ ßáēŅ@`ŋQ^~#|W…Ë,ģ,ũæŠdkp1jÜĶÛ,;ïïŋũ>îœgŊXž$oū\<žČQŸ}N•`þþþÓ§M­YģÖØÏÆ=rĪéôėÝwİ!ŨŊÅÎüaķŠŲÚûôé;û‡YqwâŠWg֐ō™™%K”þmA42Œ'Ož˜óëžýö čÛ gįģ@‚ŸvĸīPĄBŸ<ĄfĄĸ€<ČČĖ,ÔĀĀm?š~ãÚÜų ß{ĸýëŨŪõėŲŧ[ũîÚG”.YšZ‰ÆMÞŦ\đōŌ%K^4kņíwģîßŋ—––Î ãÆŽYŋv5ąZØJĈxIÛ4MžŌ}đWfũÏto„ū’h œe#ÔC–Ž" ļԌ4™ķo‘R‹Ž9`”ī)ŌĨ•ƒ‡vĻdb/OžlŨ‚x[îLöēĖ2ËŦu jy”Ö„$•.Ā‘# Š7Z,Ļ8õ`ũïßϛ//AL§—nð wíÜҰ^ 1į>éþ)ią“&~‰F"Š‹ÍšĩxšøļOŊO‡ 0qü.WVxzG$1ï/*rúāj‡Så[oㆠ“'Nøëøą-Z†UŊ ōĩAš+Gõ―G <#2RKģ\ýz}:į§wÍZĩÂë6€ģsųđMë–Sŋšˆ„nۈö@ŅHäķmÝjÖĖψöí}||æþ}‚ôĒ…‹rU3g}Ïs―/_ū$.’•‘IguÓP>Ož<þú›oęÕŊ?zԈ{ņņkŨoZ8zü˜㎈úĸßącûō%‹yÕ/ āÖÍė#1'\ƒ\͔”NHýú‡V ãŅžNŽhâîaĶŽ•xč勚6ýpØðąąW Čþņûï.—ŦNÝšmÚDÐoˆC:ÄŨ­WïžÝˆŌķoßŅBZŽXą^Ýŧ LßË(―E•ÜS5e™ĮÓvķÔŧpö,d5öģr!!õ4DČæĀþýDrÁ35Ãlv8røpƒ†ú|âŊŋ‚C‚/]ž„ī͒e+vlÛJČâÛĻåÂCĒ—Cqbņč96ås’Ķ›c\‰ÛŲn›°RýĘãoSýFīq]oŊoī g ·[ÍSÅŌTúæBÔZf™ŦĩI ÃĸddÝŠÃ"9éëëûũéSÆ >rø"[ä Üžq#+3ëúĩŦöï# E‰ä~=eĘÆ ëPōnßą“‡æ-Ž^ĩō\L Ûe(ˆĸ:ũįãĮŽ€ŠõkV=rļR•*+U‚Γ&Œ§ŸÂ‡Í[„Ũ˘˜ĀSđiž‹*ØĄ?ē"Te‰C\ūt‘'ņīôīÇþä-7oÝ=r6Á įøĘĨ‹IϟsI7n\Ï<Ą/_š…0ĸĸv:”.]&1ņ9ž‰,“€CKþU7Ŧ’1F†‚wnüĸ?i\T@IÚ—†kZē3L·ÅÞ0'ްvÏÍ1ã„ģMĢÏ`ä ļ+ÍWûŊBëÜr›Yf™åÕēŸĢ@RāÏþ?‘`2N;sæt`P1þ}xßŨĮoņŅ įĸæïïįôö!˜pëÖÍaC/^·ôþ―8ŧCø{.ælÞžyŸ>}’ž–ÎlPaÝšv.YēÔģgO}ýýķmÞ|`ßÞĀ  ôÔTHžk\ÂĪΧĢG Įôqú Öđc;žMĸ€>ėÔūŧVŽrėÐÁó1,T˜XpZJ*{tįÎÅtŒhëįëGŲ$…ŨģfÎXž(š72úƒĖ­I~0ĄR"TChĨ°Ü#ô$…@ýVOý4ĪķB’mMåą]΂K9ÐĘc ėŠČ­QQxŸĢ„ÔSæ$o4Á_SčŽÔ&Ų6ÝŠÕæRģĖ H―?åą GÍd! pėįï:=|Čq€_^0ä$oÖŨWÄ_Ëš·”Ė&&<âO'nô"n›•™ b`( bÛÞČn҈Á­%đ„Yņïݍƒ\°’5‰*›p0…q:€‡vlŌ6ÜKýMÞëéáðHOËHK‹g·Šž<Ŋ_gē žÚLk[''=™ü‚<:œģ:=%ČũŌH°FpÛ4p@éŠá8ŦRŋĄ…FšŨÔ­aĻķ)c°1“ ĸ mF#BCĶx—ûĘQ§ØÛá Ó1ūÃĢĖÕtÛ\h–YfĄÖ&ŋn#`ŌKAāĀ ÓcÐW·„,*āÂŦdL‰‹Į‡Ð†Fˆ_åĐ*ŋ8`– J1Ä`8EBœ@‰m†ĻeiÚÖ,G}FÕč2âđR ›ó_ŊļirLڀîŒÁ~Qãe€ĖcÓ6‹ÚîÜ4VāŒ~yšÂ}DÍ]ðð{9čŌË$\†ŅNThóŠŒ|87ÂRčī3‹N.S°Ļ›ŸÜh–Yf•0ĀQ}ē0FýG7n ŋdÔ.\4_þüyō PóĒSx*ō_*î…ßÉûØô1?ę2"Ųe+\~ÂĪ)í;t2ä‚ÏĸáRÏ< A-„Í1+ŪK GĪL“G @ē,ëBqÝhzĢÚ1ÄCJ7ˆ7Md@+K›Ā(oĩS”ƒĐŌ‹n…đxĘŠ ÃX!-3óÁ};`Ė—?_ïFö9’:yTqøķš–';ËÅFī-`ŠŽ93‹§Ï5kKsđLu…ïÚ ŨÄūEe\Ü­ųe@.4Ë,ģP‹‘ĻäéĨŅLīa.Ú1―ûũÛMŲėÁǎŸˆ^žŽÓĮŸāÖŠ ,ØÁYc0úģâ ŠŒīwef3ð *1z˘ķmÛâ< qĪ“ð2ĘÜr€B ėd\DFZ:ë{Ų―UËfÄa]™Y@YÁœ Ŋ‘ázŲā‚Y ž c܀7.ķv@āqßŧUƒ†ÆÛÔØýĢÆŨĶé―x "ŲĨ2ŧО\4BÃäŸõčŅËnįUql_ĐØÆÍÍâę-Kâ­Ķ(@U<`q]EŨQŪPûĒ;þ‹ĒؘÆNV­ĒWônøD•ÆfrĨYfĄVjóa+؀šu،CeʖĨšëôĐS$i͝;oðaīĒ!į?5-Ē€jÕŦóh4}}}j―îïP @úÛ~ßÏþqĘīŊœóKäŒo ,ÜĄKŨĨ+V~1a"ß;uYķbeŲ2åļTxįG/â;ũîĸ°EˌÔīÚuë™+ŲļeÛū"ŽQ―FÍfĸ4fÜļÍÛvŒ?‘˜5ũēnãfÆŽXģūråPÜXž|·æRGįĘãŨÄKē-ÅÜh–YĻåŨ<ĪKĒ’MzðKš•öĩ‘ð*ĪH~‘tãúĩ3gÎPËāíëėŅŦwĮN~œMÏÜïIũåb…‹iÖĒÅŪ];cbÎķþĻMŸūýĐw0҉ƕBĢf|‹kIE>ōĻŅcĻûëØŅJ•*―]ĄBū|ųšžũÞÍnžLMŅ=*Ķ6?qŌŪÛŋ76oū|‘ßΞ|ų‰š-Zķ lÔļ ō—.^?ņŦŠÕŠI·ÝËúôíũQÛū-Ļ==ö3fÛŧgŲļ­•)[ĶLpp­Z9{ýúĩ*ĄĄČ&„‡‡Fā˜ņˆ6ðÜžųũ[ĨJ(}'·nÚüÖÛ:uęŒýudTéēeĮŽuð‘ĢFwųļūmHHHŊ^}ýûûý("bÐā!+W,įJÞzëíŊĶL̆ g^š=ʖĒüzōŲæhŦ1KD1·šeÖķÁGðjâp“ė%mýb3Ō_R åeRōå͋.ƒ4lÐėÃfxÄĨJ—æa™‰.]š9}jôï @ĩŪė,™*;›§ėü îÜą#*2jęΉøÎõ6Úšy3ûKĩiÛĩ[wTW­\™üü9ĀĮŋf~ŠXĐ"ÁSęr™ĸŸëŨϟ;‡Ncų–­Z:yōqBBš5Ą^DDÄŧïÖæ(Õ%2+ę Û·wéÐnՊeK—,&8PŧNÝ={]ŋ~íÆõë9ß+WЌĻ7Þč―ũŦV =yâąTõŸ={6mōąŋÍ{øðî*ņŒÏ mDDŊøJ ",ÍÁņãĮÚķiÅ* 5fŅęÕŦ·iӆŦ )bģ‹Ķ˜EΆ­9y Q}†Č• ĩĖ2Ŧ0W‘JČRiĀ>ūHbë+°ÂžJÜ6þĄUŦâŊ]XÉy°HÄvÍŠUļ{”Ĩjũi­H€’C<8&`™MeLĩ+Š]’Þïã|ðð>zĩĩÃëīhŲōņãĮ/œÏĢŌRMĨÖÓúóĀþ ëÖ#ķ°vÍŠŌĨK·ïØđxņ+—/Ą*Þqņ\åӧϖ,ZLÓrŧķrxúė)Sųø:ņÅÁ-[ķjÜĪ åjņwãĐ@KKK̆ļ`áÂhė>úīeëցÅЄLIyÉMÃnžløČ]sûĒĖ`3đ\vĶZīhŅ•ØŦÞN'·öüųS6Åōå+@F„æ“―&þēmëVę’q]ųZr―rŲ<1ŧÖžÉf“-ģĖē\V˜K^­6—ÍĢA[‚īŨ–æ]šûSjūS“―ö۟t ]ēxŅýûņ%€đcGŽ(R,PģĩĪbđråjÖĻ…É H eT°•mæĐV-Žf°î=zrōö͛þýwëĶMs~™ËŧöėÞýāÞ=―fō ČĀøŦW.ŊYĩ2Ŋŋ?{mļœÄ’’’zũ鋔Á?ĸÜ$Úð(áQąĀb›6Ž‹ŋw/ĻxPbÂãråĘĘIO3EŽŧ{ŨNĪpũÚÕŦ'üË͞>sú―ũ›ōÞí[ķÔE'Ē]JJJܝÛxôZf’ä€ŧxōø1ŲŊ%`―á^üÝâ%J>}ō„ōbSĄË*ĐĐ/„ãĩØX„%)AÆ ČßŦoBÛ{vnŨ$7ūxķd/€]+€`™eđ0€ ÝĨŽ@šsŋÆčæ"u_9Á,Žĸ€Al‚……UŽ^8}ędą.˜w7.nöœ9[vîÜē}g˖đ23ņãH5ݰmûEīNXŧz•Öéz Mpüð‘ųŋýÖļq“―=æØŅcÛ·lĒ@āĪzpíσRSSí˜CōOWŊX~āĀBšÛwîÞī]XIĖôŌŋįΝdgNšvõę‹ĪĪđ?ýˆFâōUk؞Zŋikå*UÄg”nvøMÏH?°on,ÜßŋoŊŊĸã„Ä#þÉ:ũïÝŧw56=>þ.Xtxɏ]w !|Ðaļ{įÎÏs~Dwåęĩ[ķïZ―v=I &LÁÍJ™Ëĩ~íژ˜˜/ĮOØīmÛæ­Ûŧ~üIŲrÁã'L2lŲ oīhLŠä0ĻËŊe–åFģD…MōīOqxīÚŧÛÛÏYˆĐiüŌ86!ņbá(~9mņŅ{óũéģŸ! IÄāZė•ysŪT9ôČĄƒČ€å-ŋĭiĐ)^Nï‘ÓũïÝMŽT|\Üņ#‡ŦŋŸ‹Iģ]=â%O/O ĖQD‘”ôlØ þMšžōöÛîß;qü8Ý€įįcÆĖ*õ(!Át”9ļ‹ÞĢģ‰Y§OžļtémÚĪ_vÕ|ōÔtaWŪā_”Ö36ŊMë֝ /Į˜Ë[-Áā@Qn™eđÏŽ. €GKēë9Ō ,l…―@!ņqAR8ŦB_Ķ:Kæ(nXŋ”ĀJž<†{aR‡§Nœ Ī@jfzÆĩëąŌ"W„―ÝŸ9 5î)4ïÝo@‹æÍ+VŽôãßŦu"kŦ=ĖU4ĀN(vë֍ÐÉÓÖģNooĪÂ'&ðxOę+KS.qûöíļ;w>Ž„wíŊcŊÄ2đé§ðåÄɍ7Æó]·fÅœÅo}™úōÉÅ'\Į/^$Ø%tāãëC‰ÄÝ;qž‹Ëƒķ·oÝ​á<ÝŌn\ŧFhÅh~ģtėÕŦŽÂ qådqY;ķmaYÔ:Ęđ„5Dh†e4g™ËĢ€Xŋˆ[f™eđ/ÁtÛæē˜cvĩ4ËCĩ―ĩs‹ÍÓÛÛĐ[@’)ËyˆËpð?HĸĐS'ŊßļÎy°b°™ÅĖÆŊÛԚįnÍ/ŦRđ2%°“'M  ˜ŅldéqĀŌā^dÂEÚK]]LĘcmj,Álð‹cø ė8f—aSĐMæUš:ūxņbÔðĄkVŪīygY—ĩaž9ÆFj­ķ°”īވЗQĒáĸÐ\ûāĘŌRS§$†û T―ôše/§Ņ—á"9–”5îT$nM^U‰Ž=,ģ,7šÕœ\MËþw,2ŽĘ…Đ[ÕÞo·16LðgšĘqųÁĩ;røœŲ?°­f„Ė.ÔÚBg·ÐþkÍ,pÓ%&júôî]ŧ,˜ũŦKËąīFVüA|FƒQ°†MȘį·ŋfl+bfŠˆĩ_§:3~Ŋ’yębPŒX9nQbΌۑ‰1 ‰ýĮ܈xŋŋGÍŌžÞWHŠļÕД2Ōd‰ đ›`DÚ%iŊeûđ/ÛϰGBc\Ũ%ÅÞŧg DÄZŧš-æZ”š'âŪ@Ô9Ž\Câĩwû7k%‰'GØ3=ƒ>üĀ7ė™)áĨÐˌMx)Ŋ3=Tf>­ ŧ›Ũ9Z‘kW ÖZšĶkōN3O€ĐôLwGƊõt;3R]23𠔜ëžAéÔņ83‚qúDf*@TĖŒŦ*’Ąßō•ī2 8įrÍï`Ũ>ï“+#cšŧ'#BQU "DˌĒôh1ōĸ~ų† îFøþ!ƒUE" Õ9ĄPÆ“ŪÅĐC‚fD^ŨåņڋĪïåâĮņáÃgē—1ķBÓmāņWuiïÕS‡ūŋ&05ĄÏxbe(lũX!Ý·RCŠ/ƒ„}ŠsŊ\yÕ;CRœ;Š]kÍ4€O:cāé=Ã=mT@Ï~z[3s{6Īߕųøķd(ÖÞšZT(0óĪ%q_nŒi‚”Ā'ŽČŪ&i##Nh š=kî ĀL?ņq„ĘÃó=(@fļŪ FÄgäÃgcnu­\Į&Ó]ŨąýZŧšëtHā]EZ+O]ÝóϰŧŦî dßD%3W{&"B:Ũ‘k<Ũ`å&é›ï˜Ȍ•xæĘŪžqÞ­é3‚vï-҆Ēš[Ôoxë’ ėî6žŨší ÎđlŊĩũ~ÍLWIōøÔ)éNŦpfTRw‹mߋ `Øs]Ũ3įwf`XkũLUŊĩ`œ*ÑiûÓÕþH>|ēÚXÕ]S ffwßv[’NŨÔė―#Ēęx&#æOū ĢęĀQĄš!CŌĖ|Ŧ§ŦęHا€‘RũĘé~―g+ŧk0Z åĐS50Ÿ·ē@äJŨõ†ųÚŊsÝ6œĘPU=J É Į4šęT}?č›VČOR1ÕÃã9uÍL0 \]$#„BĨčjR(xųŠE`ėS%r8ėõZ‘öØP„‚ïũĨGūí2ðÚŋę”Į?°Ŧýðá“ÕēŅ0ˆq CÐą‚fũDĘĀŨû-‚;m'Ä\y―/ˆ#ÖđÞ6%ŊžAŅm’!éÜųl*$―Ŋc+Öō`Q™15KJfWa°s:ኈĩ‚Ðã\Oh§âšƒÝËpÏāIÄšvũ€ÄŠõ>oQU’2Bį\ä“R$ö^į:ä]LœۙYŪ3īâ{m)ŦNÏÐDļí`°Ïą"{Š?>Š%ĶGō+_]_$r…åĐøīŦό+­ĻSŒf\_—é;yĀ9§N?ų†āq{D(ã}]·s32Ï9$ÖkOW æĘéî1ȕŲÓmgjÆU_ķ3böąđ_Ŋé™Ĩ–ōôc­œʕ …ššbfž*žS‹ûgnvŊ• ŠÎÔņ-랞r.z_gĶĸíŨŪÆýīíÎ1f0œŠŠ(” ]ïw>ąBĻšh)Ā9'Ļ•ųūÓB‰ŸČ‡ŸÝb€īÞį I4 SĪpCi­íē§§ŋÓI+2pĮš]{ŋâŽ5oqŦ{@ ewŲ†TÕEņ>Ą ŠĘĖžŠn;ŨĐSÝI†ÔþõË@wyu+O(Ū?{h^įǘJ§Îģģ`f0 {Í4H†DžëāÉg1PüæķķIR‚ ;W:ïėĩĶë0°ũ>įôX’2úķđ2"â:§šALÏī )tšmߙL ?‘>YíZ~ĖēW(šĮÃP€n[Tä`)rÅĐ3ã―–R§/RkoÏû"5ƒČŽs0ž]ĶÆĀ!ÓÎČ:įų‘+ßŨŨīƒĒxÕ!4ęÉ|-ӍŲđŧjÆ0Ĩh·ŋ‘ÎmâĖžĨ7ĖsŸš·Ų;ĻĨ5ãžn Āï­hÓ+ģŦk*3RqŪÃ{w2Ā™kÝĸ EÂūŪũLï•$kîmĘë—ĀŪđϏĐ&œ+ôM|øðávĩíŪ.(Ķ}Ķl‹ęŪz_°CqūūšfDØîĐiũLŸ†™qÛðŽBšjô€†xęęïąŦŠOˆ˜îë:=ĩ2ĘeXĄˆ8Õķ)čØ2aW]‘Ė\Óãn€ŦgÚk…ĒęôĐ―WŨđšÜkÕ@ ŠpŲ0náwȈļú\ŨbᯠāĩwDž*{h‹O§ŒûŠ~ŸC)"žw3"bįŪsŠ!Fũt_ÓSĐč)“ũ9ôtw@ü4>|øžė5=ÖÃSŒg‘ZGFf䊩:ŨYđV&mûô™&MēĶĶ;33Ē{”ĖLBld,ÓîĄ"#ŧkfB™į:}:s5üŒØ3ÝՆénÝCbÐ4ïj0Œ_kũtU{:c™1BI°Šó7 B”1Æ<qU ĩÖ>§lˆ"Q·g§§gð#S0žžŪũx2lũĄ=}Ú@(<čc)3Â0>|øðÓ„PdŪsI1lW—2VîqW ßŨûIQÛ3€0zڃˆĻŪŠËö^{f|Z B§ŪЊČPžÏŧNg&ųí› sUõ^đ_Û6ČLŲøĐ`§Š§ KšiĀAŪ\UP)Ũû==Auß'DŽ―kJíđ‹'És•Ë+sܞąŒSõHpeޑqEäãtÛOÃÖZgj`­ĪqšLÓŧ{2%É3’R:įzŸ†Ï Aü8>|øŒ›éąm."\ÕĮŠŠ[’ sŠ`fB°a@öīK ĨÂļÖūęōXkĨTUBáš<óÚ{āӝŌÞŊ{ڀcp|Ø{Ųî>Kä9‡"kįõ>†#Ãöû\wĩ_ÆØ^ë š{āPęN{CĀÂõõ†ąr8už7^Ïõ.L@ŋ‡@vÁˆP ŠŽĀ{ÎõIąg朋`ÄšÞoSY}f,IĄŊũ{ef.ƒuΌ―^Ũ)ˆ;6Iũ>|ø‘]-Ĩ™‰Ėé>Ũą_ŧ{HDæZŦšj*2"8öØŧb˜nOߒŲÕRëÏ+Ĩ zšŠöÞcOOˆĪzšũeQ’Ķ:eą2ŠJðZ;Ÿã3{Ŋ{ņž„NžQ„€ë\Į#ĘݞɕÆ-hOFÚiįZ{?ųŽRëŠęebͧB‘Óm˜VæúÞ♊ęšņĘ€\Д„s]†ũëÕ3‘ĘH=͐müD>|øĖŦœ―‚øØ˜äãÖČY§žÜSÏ6ĸŒ˜Šš°Wž>“|íWu“°ä9ÆđwU5-*W>6Ä­§ûd’ņ4Ĩ”’|ŋߑŌÜåÝxžëęi1<0 €Īís]RäZŧ!DDÏũؗĮ­”"cėŪ’ôZŧŧ)Fnwï\É'$!3“äéÀ3Ņݙ9]6"ģęRFBķ=CJDWļ^ŲShĻEР93øĸāðáÇĸðÆöŽj ^ŨõïĸŦŧŨĘŋŠ`K1=ÕÝ·ĐĒŧŧš’BöģfWjĖ0ôŨŨ€ˆ pþþ=“Ĩ§ßįxšËSWD†ō}wĶĒÖZ_ï/ˆ;ēoąöUûĩŋækš3ŸR#’āp’qČžéŠ―öŨ{šāĘøĮ?þņä‘ņ á/w7îĸƒŋÎ?ï‚TätW·Īļôôݙ9·ÓĮũĒÝRĪŪũû™Ë3Mτôä$U-b­Õ3uk­þó2óWŒw.ƒûįûųĮÂĸƒíŋĸýĸú”dwãÇĸ5ˆČųäŨŊ_ĸ9Š=UĸíŋĸsŪŨëOERÔ9IĄĻ)Š€Qu"‚ āņŒ―rõī kn>―aO$1ãïÏįoYæ]JhP/Nl+Þ{wC―TŦÕï—æ?eLEŊÅyņĢy]ŪÜ?·}Þĸ+q=Gôf_ŊÄß(%z6ožŽ3SEH~=ąUŪŨj[ޖĨÐ0ÕÔMsļĐ69`MBʀ‡_Ēø?p@ŠR- "ÕHĩ@АjžĸJ9žČfi‡j{r€âËý~ŸįđN\ōT[XŊŨkĩڝõöú8ëõZĮËÎéŅŪ­VŦÓé\ŠÛ-ÅķSŦÕ*„ =CR5v€R―Ýn§ąÝív5Ū\ÚÎįóņxĖēL+Û*øÛ$u:‹EŧݍFŠđwéņÞÏf3=ØúýūY*Đ_-—K=TÆãqĢŅøßP§ÓЂėõz:R˜ ”ŸŌn·[ $ýSd%ēØęL{ØYí–fûĻĶÚęĻøJŦgÃmš|m6›Âd2q ûbïĘ̚šŌø}/1B$ėûĶ€ÅasCTP§hĩĩh[ĩS§ÖŽZŨÖĩTGÛŠĩb[ĮĨgščT[­ÛÚĩHąî" ī ‚ēHØē'o―oî#mϜ#I1p°Ok14^*.Žlę‹ *dpÆĻaūrÛícļVŸ9Û5&ŌߝeļûaëøĻm3äÔÜÚļfyĩŅkÕ–8kõēé ‰―f1Œ4ęMPĪōŲï(ĘŽŨSĀSá!œý(þ~ž°ÓimøvĮ’O/›Ã―\{:ÕÓ&ŋī`NīJΟâ<Su^jtBhû™X]ÉŨGZ\Ģ'åfúĘ šĩ*Ī,Ë`ŋĸ­ŊVåÄēKĢ–EĶŽ§,>Ŋ žœ‰õęō–ĸĩÂ?Æ1ŽH$ÖÚĖ<ÐŦƒĀö~GüĒø”æÚų†‘k–ŒsŊ3wĐjČ žiĐčī?·ĩlïđÆÅݚvž“ĸÍÕ:Ũ„ŨVü='%āęMEj)Ųvĩ°ļþ™,›™NĩT}ĸjá5sĘÄEyËĶũôœíZ "!Š āĸ1€Ā„‹BĶô’14?H !8ádwIqą^ų؟ģ#+Ï]˜=mīTÄ7xÁÅÛjĘū­Õ5‘AđŅþŅ L>L)ųųČᛠC|åŠŌc:E|VfžˆeÜp9ŒctĨĮŪķYüsbƒ!Ãrý[āöÂÆ1BĮÛ Ô^pÛ(ųĈĪŪĘãóŨþ{áŪ#ýüõŨĸ{ðÍģ_îiũLŲŧ{fĒ(·Ü”čvIÂæĩs|$pČȧūZžþ‡õcvl:>åÄB}cŲĮųŨŨm}7wJėYŪJ]'_ĩ:W)fiց N_ĸ€5å E$ā …NČz2UõuEÝ­Ķ}‹Žīfė=øvÍĄ5KfžQą_Ũz1į9ëwŽÚ·vÓgģgÄï_ē°(döáCió§Í;˜4xþ„Á6?čJĩ&H­ý‚5zēuŊŠa_―r,opŒūvų‚Y/7vvK2ēâžUĘĪAáM?ÝT›‡‡Ép8Ž·ūÄÍ3(ČÛPßXY{wô YŨŋT˜šLī c1Õu=ŌˌĪ0І`0œnšrâŽü~Ũüy·į,Ïó öëÖčé'1uĩ6•mßsĀÐŌI{4ë(ÆĖÉO/]ôŌX1CúúøjÜÃ#BEīžäœžķY ėļ0œĨI†/ĢEó%ąiHĩ§šÂ.—™ëž ũõ˜ýō›‡įU6ë7ü…5[æåī^“H*ī­?ëš;îX0ĸŧęÆî4#A‘$Ëē{[Ú BTˉÝäĪVÓØÐæŸė:Ũī·j˜ĀDzKéM@ģÅBū$2iaœĘāð”ī͝úĘŠZ_“öĶÎ+35ĩÖØÆL$’ļÂ`02PÎ24Ā9ȉUAQ˜Ķąžō†WZŽĀþDĩČĀnT‹@2ŽęɗÖF{vݖaãÂc΁Qųۊ”pOąDæëKŸ8ī?đ苭ëŨ­J8z2f ÓÆ‰ÄuįömøgÁ;ïxį‹ŪÏęøĖ~‘XI3!A-Åņ^\ŋ†›‘ƒĻēp\ aωÏ6ĩ0Aéƒü$ÚnßéÆDbcOĢÅ$r–æR1bGfŽqsĨĻøŒgWÎ!āâéãk­gã˜ęƒLö @Pa(õŽÉęWvödaÉųŦeŨ[ÔZ\„KޑQW/œŋxþLqe‹<:ŲßUÄ+øēæ_ËN;^Ũn  8BŨÕxŦĶUK·76Yå°ĮTŨ/•Ÿ9Wzöâ]-iZðXÖĻ”žëEŗĄ9fģEŊ‡ĐÓ>7ZŋxÉô ŽÉCī‡,))þâûŸŧMækg|räTŦžVxûKq76ŸÜĩ― ģ‡Âô͗Jū.ŋpŧ―úōõj5b“‘âøAd\åãwý|ŅîhYėŧ€ 9uôÓ­[ÞÉ[ųb~nÁÆõąģģĮšŋmįŽÕëvE ž™âN&“…äõ™ƒAļÎ|jË7‡Žž()úþËōæN sBŠjĘýÛS[\;|’"ðV[‡Æ`b†Īö ņ…ļkĘČ ÞMM=‘”>.2<ð·§ÜĮân‘Ūģ í čĄ!!þáËđ7‘@ÆOfą°YŊnðIþá\MGâ”7ÖædxēDüÓŦ–ā!C(ĶmË7øplĖō7öŒ8VZÖÄÞ Yï‡`@‚Bd DA‘që­xÏą YÜE>0.ðš~@ũwkL:0üëctĐįāD%Ú ác^ÔLdL:ą,B#pČ24T…DúXsø'Ë9䍋Ũļœ,†"štņ‰ž:=Ž",,üĖ /Ģ3qŽĩ3ü•„QČH“$É2#wî2éÅŅ8Æ2 ž<ŒĶ)Ž‹d ‚‡đx=žû†AŌBÂþePá8nÏML›:1q8nÝĒH‚ĒÔ(ÃI’ÆÏJžĀ!A18 -IÓRŋ؜I8"FQxė° #8€á4iĄYĮģ…ŠđôwbnKKKpp°X,ķæH=â8ŽãƒAŦՆ††ö91·ĩĩĩĢĢ#99ŲF簃öï•kë†uÛšyï›{ą3uĩ^ŊŋråJFFFŸsFcyy9:Š,‘ Ö͜ðÃÆ‰6xÚ ŠšÚJÆųĻV€8ŽĢUfPĒT*Ņú&þã;ģŲŽŅh<<<úÔĄBĄhhhļyófXXšp =čtšššībVŸÐôČårde]Uį!R­ŪŪFT1 ëŨr3 iý4, „°Ou{XŽ­ä$CžžžvîŊÕjuss3ŠY–ĩíŅCsA"‘øųųEDD Iĩu""___ŊŅh†qڅþSEŨ*Īģ}PuBj`ã^ûQ”Zī_$ņGe # 7æšpļ,Ó 'r­þÓÞœhƒþ‹°ÁfÄsŽc ÛÄüKïU\>!ųŨĢJ-ØŽļų%äÂ+|6k_Ūĸ[ АZĐ@jĪ€gfvũývwfžŠ:įĻíįvũœSUODdfwŋ|Šŧ33"þFģ|jũËIENDŪB`‚doc/images/ifw-updating-components.png000066400000000000000000004242151325366651500204030ustar00rootroot00000000000000‰PNG  IHDRÎb88% AiCCPICC ProfileH –wTSŲ‡Ï―7―Ð" %ôz Ō;HQ‰I€P†„&vDF)VdTĀG‡"cE ƒ‚bŨ ōPÆÁQDEå݌k ï­5óޚýĮYßŲį·ŨŲgï}ŨšPü‚ÂtX€4ĄXîëÁ\ËÄũXĀáffGøDÔü―=™™ĻHÆģöî.€dŧÛ,ŋP&sÖĸ‘"7C$ EÕ6<~&å”SģÅ2ĸĘô•)2†12Ą ĒŽ"ãÄŊlö§æ+ŧɘ—&äĄYΞ4žŒŧPޚ%á̌Ą\˜%āgĢ|e―TIšåũ(ÓÓøœL0™_Ėį&Ąl‰2Eî‰ō”Ä9žr‹ų9hžxĶg䊉IbĶŨ˜iåčČfúņģSųb1+”ÃMáˆxLÏôī Ž0€Ŋo–E%Ym™h‘í­ííYÖæhųŋŲß~Sý=ČzûUņ&ėϞAŒžYßlėŽ/―ö$Z›ģū•Uīm@åáŽOï ōīޜó†l^’Äâ ' ‹ėėlsŸk.+č7ûŸ‚oĘŋ†9ũ™ËîûV;Ķ?#I3eEåͧĶKDĖĖ —ÏdýũĸãĀ9iÍÉÃ,œŸĀņ…čUQč” „‰hŧ…<X.d „Õá6'~khu_}…9PļIČo=C#$n?z}ë[1 Čūžh­‘Ŋs2zþįú \ŠnáLA"Sæö dr%Ē,Ģ߄lÁt  4.0,` €3pÞ „€H–.Hi@ēA>Ø A1ØvƒjpԁzÐN‚6p\WĀ p €G@ †ÁK0ށi‚ðĒAАĪ™BÖZyCAP8ÅC‰’@ųÐ&Ļ*ƒŠĄCP=ô#tš]ƒú Ð 4ý}„˜Óa Øķ€Ų°;GÂËāDxœĀÛáJļ>·Âáð,…_“@ČŅFXņDBX$!k‘"ĪĐEšĪđH‘q䇥a˜Æã‡YŒábVaÖbJ0՘c˜VLæ6f3ų‚ĨbÕąĶX'Ž?v 6›-ÄV``[°—ąØaė;ĮĀâp~ļ\2n5Ū·ŨŒŧ€ëà á&ņxž*Þï‚Ásðb|!ū ߏÆŋ' Zk‚!– $l$Tįý„Â4QĻOt"†yÄ\b)ąŽØAžI&N“I†$R$)™īTIj"]&=&―!“É:dGrY@^OŪ$Ÿ _%’?P”(&OJEBŲN9Jđ@y@yCĨR ĻnÔXИšZO―D}J}/G“3—ó—ãÉ­“Ŧ‘k•ë—{%O”Ũ—w—_.Ÿ'_!JþĶüļQÁ@ÁSĢ°VĄFáīÂ=…IEšĒ•bˆbšb‰bƒâ5ÅQ%ž’’·OĐ@é°Ō%Ĩ!BÓĨyŌļīMī:ÚeÚ0G7ĪûӓéÅôč―ô e%e[å(åååģĘRÂ0`ø3RĨŒ“ŒŧŒó4æđÏãÏÛ6Ŋi^ĸž)•ų*n*|•"•f••ŠLUoÕ՝ŠmŠOÔ0j&jajŲjûÕ.ŦÏ§ÏwžÏ_4ĸäü‡ę°š‰zļújõÃę=ꓚūU—4Æ5šnšÉšåšį4ĮīhZ ĩZåZįĩ^0•™îĖTf%ģ‹9Ą­Ūí§-Ņ>ĪÝŦ=­cĻģXgĢNģÎ]’.[7A·\·SwBOK/X/_ŊQïĄ>QŸ­ŸĪŋGŋ[ĘĀÐ Ú`‹A›ÁĻĄŠĄŋažaĢác#Š‘ŦŅ*ĢZĢ;Æ8cķqŠņ>ã[&°‰I’IÉMSØÔÞT`šÏīÏ kæh&4Ŧ5ŧĮǰÜYYŽFÖ 9Ã<Č|Ģy›ų+ =‹X‹Ý_,í,S-ë,Y)YXmīę°úÃÚĚk]c}Į†jãcģÎĶÝæĩ­Đ-ßvŋí};š]°ÝŧNŧÏöö"û&û1=‡x‡―ũØtv(ŧ„}ÕëčáļÎņŒã'{'ąÓI§ßYÎ)Î ÎĢ ðÔ-rŅqáļr‘.d.Œ_xpĄÔUەãZëúĖMŨįvÄmÄÝØ=Ųýļû+K‘G‹Į”§“įÏ ^ˆ—ŊW‘WŊ·’ũbïjï§>:>‰>>ūvūŦ}/øaýývúÝóŨðįúŨûO8Ž č ĪFV> 2 uÃÁÁŧ‚/Ō_$\ÔBüCv…< 5 ]ús.,4Ž&ėyļUx~xw-bEDCÄŧHČŌČG‹KwFÉGÅEÕGME{E—EK—X,YģäFŒZŒ Ķ={$vrĐũŌÝK‡ãėâ ãî.3\–ģėÚrĩåĐËÏې_ÁYq*ßĸ‰ÂĐåLŪô_đwåŨ“ŧ‡û’įÆ+įņ]øeü‘—„ē„ŅD—Ä]‰cIŪIIãOAĩāuē_ōäД”Ģ)3ĐŅĐÍi„īøīÓB%aа+]3='―/Ã4Ģ0CšĘiÕîUĒ@Ņ‘L(sYfŧ˜ŽþLõHŒ$›%ƒY ģjēÞgGeŸĘQĖæôäšänËÉóÉû~5f5wugūvþ†üÁ5îk­…ÖŪ\ÛđNw]Ášáõūëm mHŲðËFˍeßnŠÞÔQ Q°ū`hģïæÆBđBQá―-Î[lÅllíÝfģ­jۗ"^ŅõbËâŠâO%ܒëßY}WųÝĖö„í―ĨöĨûwāvwÜÝéšóX™bY^ŲÐŪā]­åĖōĒō·ŧWėūVa[q`idī2Ļē―JŊjGÕ§ęĪꁏšæ―ę{·íÚĮÛŨŋßmӍÅ>žČũPk­AmÅaÜáŽÃÏëĒęšŋg_DíHņ‘ÏG…GĨĮuÕ;ÔŨ7Ļ7”6’ÆąãqĮoýāõC{ŦéP3Ģđø8!9ņâĮøïž <ŲyŠ}Šé'ýŸöķÐZŠZĄÖÜ։ķĪ6i{L{ßé€ÓÎ-?›ĸ|ôŒö™šģĘgKϑΜ›9Ÿw~ōBƅņ‹‰‡:Wt>šīäŌŪ°ŪÞˁ—Ŋ^ņđrĐÛ―ûüU—ŦgŪ9];}}―í†ýÖŧž–_ė~iéĩïm―épģý–ã­Žū}įú]û/Þöš}åŽĸ‹úî.ū{ĸ^Ü=é}ÞýŅĐ^?Ėz8ýhýcėãĒ' O*žŠ?­ýÕøŨfĐ―ôė Ũ`Ïģˆg†ļC/ĸ•ųŊOÃÏĐÏ+FīFęG­GόųŒÝząôÅðˌ—Óã…ŋ)þķũ•ŅŦŸ~wû―gbÉÄðkŅë™?JÞĻū9úÖömįdčäÓwiïͧŠÞŦū?öýĄûcôĮ‘éėOøO•Ÿ?w| üōx&mfæßũ„óû2:Y~@IDATxė]€EÖþvf6Ã.,9Á€ųĖs=ã™=ã™ģžžáĖ9ž ÅH1’EP$į]6‡ ;ĸũUσf\ŌwįiĖvuÕŦũ^―ŪŪŊ^UuwƐ!C’Mš4AĮŽQPP€ôL&‘‘‘‘ž ĨoLð—mˆWzšņõ—K—ĢžúúúõJ§]ŨđÉĩãščŌӍ>ýht–nį{\WđõĨo oŋ╞Ķsđ†äĪ—kˆf}iVގëĢõį}úŅh,ÝÎ7öļŪrëKßÞ~;6Ä+=Mį þr#gC4 ɑ š?ū.~FģĄcCåM^CyÆ/=o}éFŧ>ū–į?Z9ņV°~:Ģؐýuí=Y@„Ũ<”äucšŪ@}Ē™l ‘PŊoÜÉ4ūĶs(æ,Hą(ó™hú›.Ēk(ˆÎoĸ ^Û0/],žøđŅ*Ãø{ņ2Đs&Û?’õˆÅbHÔŊ čĒ—.VΓĢ6åīeûbûŊ)·šÎ§ŸÕÅōĖvnzųé,nyŠŦŸ^éĄH͌Œ8íltþrf+grWóĒŽšOÕâëUGÚ N9b§4?/' •&~úéÕõSðÓ*/ý|ĩLGíŅ›^aՃíÆęaüLŽx­Žâeü-ÝhtnqŦŋÉöÓĶ—·r’íÛđņÐđ?Īëb|EcōÓåúyųó,nüýt–fu­ņWž_ŪōŌÏÓyã›~ī|ãeš™\Ë·rvî§7ýLķņ­~ –æ/§tË7ãĄs°rJóĮM†õĸĘ·4ņR―~ ĘģļÎE#,ːĮĐ#^MȂĨK—âëwßÅlj/-AļQ#4íÖŧu4ÚvïūCņð‡ŠŠ L›6 +WŪtĀf'Īiáî,ŨšukWÖ_iSNĮe+–`ÄØAø~ŅwĻŽ+GNf.š4ïŽCö< [tęænÞoN 2‚ņ2ýuôĒø*͂›\+oų~ž–ĶĢņOûÏMķņ4YĒąāŨEiĒ1:;­ĸhō­žhÓCC4–æ§ĩītŌÛ‚Ū››^:Zščüķ2ž:jā•ŽbɜŸP—ÕíÛĩBėÜ*•K`iušīGÁÆl%~Ąúš ĖYVí[#‹c$“íō­Î<†P)€XKķC$Ū-[ŽU!īkÝ!jâāÝþz+îÎ3ī‰J|?}2,ŦFf“VčĩÝ6(ĘÉ xð&"_‰ĐW†yĩXž`*ĢâC(ŧ1švîĖķ+ik:mgņ°ÔĮ L^ÝDĨ |ŋ-ŌíïQytF/ŽîĄ$JÎCmnKīoֈzŪĖŠÆ‚Ó›'VÎd*=^[‰… Ē*Ęv Ė+â}ŨYa•ĸe{O773Ŋ?ý5EÛ`í Ŧ`ú‰ÎBš.:ŨYzŲ9Ģ+æÍG4·Z·h,Ģþ‚ņĩšøy[žŌ,nĮô~Aé–'=L7ĨYÛ6>~Š‹ÖĘęčVÖøųóŒÖʛýÆĘoĸđŅø&Ģ!zKkˆ^iĶ‹ŅŲ1>=ÝÎýG•ą:ųuRýTĨŲŅâ*c<”æ/'Z„‹Æ‚Ņčheýųé<ýåDgīV~Síūøâ‹oS!ýÄÄâÍŅO<†‚ŸfĒïĄcĸsÎÂ6íÚ"úÝ|ųémą Zīäh<ššŒ”‘wĐEÔ/ŋüŌ­›îēË.Øi§Ðķm[TUUaƌhÜļ1réĩ*Hž ãäó†YJÐ|uČSĻČ^ŒŽ8ĮrzvïŒK&cĖØOÐąeW4iÜtĩLÓW<Īŋ~ÆÏņLĨYšŽ&WGKŨđŸ‡ęÕŅôsK7~–ož”ï§ņĮÆTūŋž:—―ÆėßŅøë•Ūōüų*c:éhņtŅ)ÍhüųÉú þûxeÂrlŋÓÎ(Ę ˜JðÜcaČļÅØí€ÝŅkDÞJ’ðZ·ä[<úô—čđkO}’+ŨmŧkÁAÕŋ1sþ*4)lL0S9ÞXԓhvø\“X4n8žģ;õÚ™ė”―:xmĀęcķá-dÍ2žôėøáwhÔŠ1&ūû6>ĸa!ZmąZæÕã§éÓQ–Q€Ķųaį Éþė P1^yėLZ\ŽÅsĀË+ąÕÖ=Ð(‹mĮé”jëîz%°øį™X\žDģ&yĐk(Ýdoï^\ËöšPĐúËkwüx§í§Ë čg„ęņųsá“šŽØ} Þ'’M[)$žwïNR?^?ņqu)Ÿ;=ōf-+ÃĒ9ßc^e[uí‚<ÄN͈7öŪ}pv!ĮĻ7_ÁžŒVØaŦöt%3Õ>XD6Wûð‡mĒôĘ⅘ĩ° ‘‰aÐKÏbVžÛtnÅę{ú‰ÎŧūŌÃŧÎî:2}cÛŋ_ÕE?édíÖņcšŅđú’ŋĸÜĘøË+?ÆŌ,]GãiåuītÅÕŪŽŽę%ÝėÜĘč\qKũŸ[šÕÉĘ+ÝâVÎú+oų&Įx)_y~=untëģ:oãcåuīļҚ<íg4V“­ô†h,MGŅX°:YyýõZŸýÅ'b Ų%@ņ™Ģ?Fîī)8’ ŲŽ 1ęįĖŅļmwÝ ‰!Ã0cð`4ãBĐĶáÔĻe4Mi)Ė!­xlŧíķЎ])ՈÞj=PWW‡üŅĨkąUÁ*§éŊ'ÁRü„Ó=íÛķ@Ž~9åįãāÂ}0 úŒųv(ÚķhÏÔ+Ŧō’­`Æp'ū?ʗ U_Ģũ‘ļĻÏ譌Ž v.ōütĘ7ūFo6ÕQACq“ãųGôFk2Œ‡É­ņWyš•‘ý-nåuo+ëÏũÓXšäXšédiâá—m<íĻ<+Ŧ2 °H$ŠĨÓgáĮđËÐđy'”Îų3gĸ„úf]~uu)–Ŋ,)ŅĪysäŌûhū.ūĒ;šærMĄŠĩlóĄx%J*âhÜīE…đXðåįx~z įŸī?Ú6o†Žd V—"ž Ģi ōáekąÃ~ļdû0r(uÕՈ2/#ZŽâō )Ŧ1AYzŦí…BqüðÕ|öõ \pŨ]؛ĀSÛ{gÜuå}øpčVčx\O }é $>§ïÚ yy9ÎĸrõŽÖ!\Ô'Ÿķ-Ę@#ĄĪļNKWrÆ&ÉGģĒ&ˆDWaܐgņCrœvĘŅhÓ4IÎŽ,]VŒŒœ4o֔ _ÏeŽj°ĄĄ’Ņdv  Ä(1ZVŠšp6š·h†:ŪÕå•H4kyĖä rÏ3.ÂN™P§iðDJVĢ:–@Aģ–hœ›LzĨ+—-E9―ãæ­Z!Ÿ.―BīŊY}īYvÁÏ=ݛōūNÄPūj%Jęsáī7ëVÍ„Ų8Ð(CqY=ė|ę]Äå{ēōËiv$ã([U†ĖÜ<ädsIĻēQd!:Ԗ— xU9ĄL5Ga~ĶMøĢ~áøCû sŦF8ļßŲHryÆÝģŽĮō+QMĒIģVhÚ8 qęYQG'%+‹Ę-DģÂ|Î,ŽiÃjéíßÚЎþ6í,ĸØ― <‹ÛŅĘØđĘŽŊýûóW]ĪĘ§ņV0­ékyÖĸč\Áx™–f|Ä#=Í%ĪþĻžhÄŨčt4~éüՏ)ÏŌuÔđõĸ~ÆÛÏËęcķīĢĘ)?“á?­ŅˆÞx_Ĩųƒå+msØ_õ˜0)ǟcÎcÍũÓ°S~6rįÏE”ÓŪÚ@ĪĩPÞīÛįfá“Ų3QËôžĶMW§ĘŠGYYÚīiãÖ)KKKČHŽÖ-[īhŸ~úÉ-°Z…ĖIŽ ”ýˆ=Z`eÝrÔ.ŪCV„7§ū*9ÖnëVXŠŲĄ4ĘkLũ.˜äšAӍ,Þjéé*Ģī†Ę)Ïlat–&^ĘS#1ýýž­œUÎllō”'>J· 4Ģ5yĒĩz˜<Ņûõ/?›K·ēĘģ <Ĩ+(n2oϜŸ‡•óëaü\ý 9MОIÆÏøûöj‰™Ófģ3mŽ,zpô™1ë‹á' hÚ0|>gņRÍ-ŪAÏ=ŽÁ9§îˆqï―†Ŋæ,%āâ ~'!cÆŧøūíá8gßøzÄŧxï›h™_îˆū;méß Į QSĖH §Í.8ý΃Ð4›íT·?ۙ›ųfĘĘY&fæeaÂOcZÎøsŋ=Ŋ\„_zŧs:ŠĮĮ”e%\^ĐBŊū§âŒÞ[ ^ũy…âŦðÎ+ŊĄÃÞGĒũš`öčŨ0ēš.=i/Lþl$ÆNË} å(Üæ0œŲ·+ĶŽýsJ3ņNpîÅGâÛw_ByŨCpÜÞ[bü°—1dŌ 8•ÞļŽ;ítäŧĸŦ™/į@ >'üé|ėŌŅë“ÔžÕ– tÞP°öjyjÓ֞íčOKÛ}ĒtýŒŸäZš?]Ŋ‚ŌÄßî7[wņhåí~6―DÓP9+o2L§t>*Ŋ |ĢI?úuPyãaeTÞŌũ‡_ģýĨ§ę°ĐöW™Õ§Ë*SU‰ŽųyČÓEÖČW2NāLDŅ&/MĘŠĢũ( Ĩ.ļĸbšETyfæiQUÓī:Šņøī ŊM#ôNZķm†Ũ”™ōÐč Ðk óži‹BŽĖŦ䆒:þ8Ú_ÓÎ~aņS ŧð:7C9ylī:úãĘWPš?Ū4ŋ,nG?ĢÕQAy ÆÏߘLŽņq„Đ?J“þþ›EY ņSšÕSųÆWvķ†aōEŦ`|,.YĒ―•w„Đ?–§Ģd‰Î‚?Ïä+ÏMÃåķÁ}·Âž)1ũῘ6g!:á(Løf›T]wëƒkûķDÅĖÁļũĄ1˜·Š7šdÔ QA:&Y1,YV‡/ŧ―B3pσƒą°ū7zõÜ““!œ/ðĢ/S0ŪëÓeÓáūG?Ã‚Ōƒ‘CþÕnÝQk”qĮįäËĸ‚m0 ũ<4óKũÅV…™īģęCYYôŠ#Ļå ”ĀNpáō@ĪbW‰š– Tbō7ӑđÅaļâŒ―Ýīi]ÉO:čct9ô\Ņ-Œ‡xãč†ķk‡=ÔŊžũlýĒIxûËéÍeâ ÃOGŊ}vÃā?ĮÜūŧ!1å,Ŧ낭:ĩÁ–MOÅĄ­ōņÅģcčėđĻÝŋģЊ›Áh9ōŦ‹W ĩœÃóv%Ņęč5öØûėvx#ޘ2’äŪØũˆQ1#ŒģN; Eeψs5#Žå?|…·ŋĐĀY—\=šfâÕ{ïÂĀápé!m[9y;õÃM—܋™?ÎCŊv…”ïÍ~Ī·ŧV܂âÖ)î§3-}cÚŋÝ':Z9íūÛ}+: ~zKóóą4Ek|šŨ•§`ōÔÛĀßdoG˜úcôĶ‹ŅĪËH?7}Dŋ9ėoōĨ–ÅÅÛä*Ýî—Ŋ:˜ÞvT9ŦŸŽ Ę·`rtī`åuŒhęÔOäÉ(§];Ô-ã=К-’\ˁn"2–/C<ī Ž`“T@k$ąTį.†R*//ÏņÔQ;i5%ŦF)YRRé:ĘĻ*c iÃDQĢVȌUĄiŨ‘2‹n„ZŪ“­Š• ĶdžSw-(#L~kĶ LâŌ‰ž•fév4øó,ÍôĻoȐŠĢĸ&ĨY9“'žþ t+k:ZÝíheUΟ&z5x‡h·z§ËSūãkGã­|\ųV7ÓŅhtTū~ĘS9+ŦĢéaåīæ–ä8ą Í·ę…úđS1üÃO)°öéØūĒwŦÁ“>ÁĮrŠ•ŧĻģ @ ÅØiššqgjŽ:ŽæÛĸ=Û ķļŲyžþl‹šBŒäd!Ęv•ŠÅŽIcĖĪEČŠ/GŒ>ęŲÞĻcn°ŊŸÝ=> ˆäĢiIöïŽja‚%ŨJW•Õē]sÝUK™(_ą…ŲÜč“ÁiH ĻâĻĐb9ÚAAŧÎÃÍ·Âɧž-›Ņ†äW]Á uų­Ņsũ]Ņ(Éfn&§iŲî9…‹üLBđëžš ÕŽc5ŨVßïĸĒÜÐĐóv%Č?”‡^Ûoæyq>c]ƒjÎĪuč֍'w%wėäįXÎl-›Y§mz€ŦĨžE9Ø ÝÃܔUU›ĢO;Þ‰Û―úŽ^MŠÝ.ų™_|ˆ%ßēþđP˜vģAšĖlķ§Ú:ätØĮũ;]ščZӀõÛcÛÜÏ0eĘ$TMX„mö? ™Åsņņð‘˜U–DtŲävßŅŦ›[Ôá ïoŽ„ÝŒT,ģŨ79}+Ģ™!ŽžžM―LĄC‡n§ËēeËp*ۊZð ™DĮĒn˜ŋr<ēŧfD›qCB!Ũ·ŠØņÕ –^G;NŅ‡]ÝHTNAuP0]<~k<"ËsDüc@G+ĢļęÓP09Ē1Y:ęg]4ÆŨOoéFŊsÅe‹ÛŅđ~:øY3[K?Ĩëg6T Š+Oå­Ž[YĢĩĢ••QžxøƒŸNévnt:·Ÿ8šģKŒŠ“Mąkŧ<8lö9öE*8ðâ͟Š>æ ė{ÎíØ):j˜óâ4ûāŠģĪļĄĻŽÓzÜåĘÎÝíHĨZ5\ŋŦĒWО‹ŸŽÃü;ÔLÂ}O §\OsVÉÕCā!˜–ģm†^ԏDލÏQ8Ũ ;tÜÃG~…é=:Ą'=—Ęe?cėŨߥĻÃ~hÄ5Æh] ĒËKÝŠŦ§ėŊÝÉmÎk'ĻFđ KÜį;PĘN(ŠkģÝĻšyááhīŒë dgæĄEA;œtîUÝŠ*+PUąŠ”\+­V―éŠŅƒR{Yšx Ękķ@õŽQQÕ][äaé’Ü [Så#4Ž ģ^‰hę›vĮÕÝ#žžïŒ]Îڑkŧ­Ņ~ßĢŅïĀ^\[­BY)õ =Ī›6ûđšD ˜Dk ôlYüîÂĐéŨßïXÓmpĶÜG>‹ąßEq̓Ũc΀‡1„zē+ã%gÛĪü$í W`ÉōU([<ãg-Bēóķ(_ü=FŒüĮ\w3šTMÄ#ô8c,‹ÕĢ’čYĮõâú,o`NŦrJŋEÉąŽĪmļĻ;mÎbtŲÄxŪ‘‡iŊÔÔž)—ĸÝ`^;Ÿ7Ôþ­Š•X;VûŨςŌÅĮŌ­7tŸX9ŅϜûų+ŪtãiyFŋūþĮøéhzĐžÝŊ–n5‰xÏB ”Č߁”:åP=•(Úuî‚ėQoá7ÞÄĄ‡í­9•ĸŅ?ĀwäÃ9GŒõvkÃΛ^ˆžRíekð:Vzeⓠ8$âäN/N^îÜLt7ąžóúËø†ü*–ÏGiĪ. ĸĖd9šnÓđÞ6<ŋö$xЃÓ pÅ\ zýy°ąd3ôî―ą™Ûu4ÆëĖ =Ü â<:vÛß üĢ)N>ū7]úžĸû-TfĢ°Ã–č{Ā^Χ=8pÐîYmīÉãtðŽą#ðŌēéĻ\^^―C3ÚKqí†ģ?šĒ”ĩÜĩĐÂØ0ŸëĶĩŦâØgߞČhÜG{ÞØw}1ˆ3YØýÐCÐĢ}ëBģą\’Ũŋ~É$|ðúrօšĢ?æ īíÔ MCĢhũݐE{ķëķšN§îý; ēĢÜ5#'wĶŨČ:ÓSŽ åsGlG §œ—gĩBGBđ‘5i‹.[4ÂĸxÔmōŠUŽÄ°ÏŋĮŅÝ8]üÉ{xãÕ œpōá\#æóÜž&Íšn‡Ã{MÁčÏc‰ItÄIûwãlÁÏT˜›ŧðQ.éĻ ēön0ÉiyMŦ [{T›WHoÖníŋĄvï 6P֏Ĩ·•ąûĖdŽ‹t2]EŦŸÝ[VÖęáįĄ2ŠŊŅčč§ģššžĶ“Î-ÍdûóLŲğnqÓÏÎu4Y7ū:*˜Žét–ŪþPņĸ–ýMõ[—ýþcƌqĩ‘]x+Ė)ËþĒ|6­záÄ8 ÓÃĖkÓđŧðĶāKUÔЇ‚øhŠV`ПëðHŊ4íŪXŧÆMچŒ˜Ã]wËĘ~æ4ŸGãĻ9+’‹fÛĢUÓۜŌáú(§u낸yøÓÍĘ7íÂ8&)>JSHÏó7.GĒQÜ_oãŦ4“į§1ųĘ· biĒ39ŠÛ œŪŋōLOãaiâåOSšŌDŊŸxúå)_ÁŸĶļ~ō ý:(.Ę3ųV֎Vo;*ÝÓ?Œ|n2‹s]šŪ>“;f3éÅTĄĶžŧ+đóR€–ÅéÖZN†ģs‘Ã.·Š†ņĖ°Ûą]SÁĒŲ9ČĘΠVã1Ņ‹0â|Î3ÉA6;âzeÜD–ËéؚūՃųŪ‹Ö02#|‘BĐāNŅ,N·fs—jwf*N―ĻK”Ālö—7ŦGĨb|ųF§_—ĸ4Ÿ~ý=ŨýBØbŨƒŅ{·îôî8'XęÎáO6QđN3{íÚë ãô&s]ÝcŽ_Ųųáīl ëÎÉ's:ĩ†3īCn~§iųĒōÍÍæô3=ŋLūôƒóšô$ëøō0F |ĩvÃáŧlIo=ųŲIŊ^ī[v(ÆĶQŠC^đ9ԋ`YĮq §akkŦÝŪŨžĖzzĐä•ߘ`å€īšvÍå#ÆAðšķâó”yÜmÕË'TGnSŨqm)ÞĒŊ Í#ÜI›ÅČÕô°óđĒŽ;mđߐŨ$ĒŠ8•šä@ ‡ÓņīpĘŧ7%8 _Eú\^óšj^ŦÜ|dpPUMYŲžÎÚM\ĐtÚ33'w5ņ’”ƒ„FÜ'‘āõŊŦW]sy ŦÝrX>’Qįôq—†ĘŦ/RÐõõ·u—Č?–Ķķj4*cũ’Ō­ýŦŒÎýAåíū°2vīþTų v4YvO)Ïâ:ZyŅ+ny&ێ*§ļÝŦ~>&Ëht.> ~þÆKiĶŊÚ°â–gzč<‡ø™Î&Ó_N|Ä_ų–įųĮŌ”nõ0~Jģt“Ŧr~*ŊŸŋŒÕÏęĢ|“ĨļhTÆō,îįĢßĪéV„ÜŊJŠN? S9 QpŒy.īēšz‹^Œ ļ•ÓA<ēø†"•ÍÍËõÖG9âŪâZžõ:&\dņI7ļøIÓQqĨ―ëĻ <ņQūĘ(˜NVŦŸōüžŽ.Öā”o:‰Îø_å+(OÁŸŸNŊsĢóëgt*kōM?ĢŨđŋž_–ĨKgãeķp*øå‰—Éąžtû‹OšLŦģÕŅdˆ‡âÆWž4Óģ—t!xĨŧĻh'Ō閗ĮĻD빔N‹mÄóõ†™l‚§<< ­4M*Ŋƒaœ+ëĐ-xŨÆíˆuÞ5‘ŪaęĶĮ#ŅÚëÔÉģÛĒ-ƒðž?–O%y~û[]eg?u@”œRĐžZĸ ˆä-Õ ļI {Ėģ“§·ģl*}ØĸL7qnTÚyëŽdŪõXÚ"eCÉ3û{Ũiuĩ e˜Ũ6ųv$îFÕõ7―%/,o‘6ģ:_é―ĶJžR7ģØ^âĐû˜WÄa]+Ԉ§l`ŨÔņaý#NVŠ2Q3XaĶÖ)žįU·ĩ‚xé…Ė“ҖsŨIr\=”Čļ‚ĩ-wÂ?žm―<ÕO?Õßß=Ž―„Ēr–.^:ŨOmV<<{züDgžýīŠ+ÝßþuîŨÉdčheýžüi*gzKK[62=ĢÔŋ,%ųåûó·|ĸŅĩgß5~ŌC!―œt°<˗LŦŨÛþ~—-uTP}LO—āO1bÃôÖéT aŧ Šž(OĖtTš~Š+˜AtnÂĖĻVVGĨ™B*g4ŨŅxš •“—ŠsËWÚš<#“#ZãŊ‹Ū < Fg4J7ÝėčOģz§ŨŅøŲŅd_ŧątŪ`vÓQiúÉÖĒoŧxfãgrMã#žé4V7;J†ėĨĢņQ9›]·4ō§ō­+ßn ĩŋøHĶ‚ÕYqšâú™–ŊĢébõ6ýÓéWÓŠĪę"yĶŊé`åLņO·ŋŌ”ïdfæp™ĮY—rî ÕËž<Ëũë*YâĩZŦM"PˆŊÕĮhÄĮâv4ģ‹^؈+ý@ŽŠŽu2LgŦ‡ŲĮęgžėÜ_'ãë·‰ŌŽŒŽéįĶ“ąûĢkį]'ĩ ПÆô2ۘLÓ[,WS9kũ–ozϜâÆOųÆËhL†xš: iÓíïŌSēÞT\|õKoĸŌą!û+ÍÏÃŊƒâĶģņõëou0―ėÜøųyųó,Ū|“aī*kö2ۘLģŊhĸgí/ųúIĶÉO JW0yĶ›?]ųv.ýŽNÆ{cí/Ķ‹ņīsņWūņWšô5:ZíÜŌLoGô–WØÆņ,ŦQŸŌœÁyģ+Ð4ŦĒ SCTšĢó7bŽ CQŠēvĮņ${…GãcĘē xŠ{6gg•2&Ï3RüÅCšHɋqL|ô_į*ĢgCe,Pyéc7“Ž’+ÝüÆ1>z―Ÿøøoņ•ģēNwĘv2ГÛāBŲēƒ@FųîU”gH2ExðÛßnDˉFÜ*oõ2Ûš:1_é’Ĩ)mgęx(8;ÐkŌą!ûˆ šVWģ•éïdûíOđ ēŋl$[ëÚÏ_Ø_|X]cåm’ýYÎė"û˛Søįė_ŽŌ•,ėŪõÚö7Ĩ§Ųß؜úJV„ÓžJŨOvV]-l’ýyŠ9ãBFŦ[ēÅŨĩēv Ŋ™Ū—ģ?óD#Ý\ûIÉ6›JŋýMĩýŽŽø*HŽâV?ĩ)É1Z-(.~’ĩ!ûKÎjŧEƒí_ŒÅžĪîž'_W/&™ë—Lŋ†ėŊvî䉝úTû_ĨKïôúŽÕþžį(ú_؟z)Ýo§8þ”ŋÚþŽÕÁî?ē_KģŸŌ7ÆþŌUž6Ęþ)Z]GÕßéÄk*ÛYŸ—ĘŲdû›mtīþGšÉ mŲŲú–ó·9“Aķ^ûßTûŦcŲÕöOÅ­ 4dõå‘å˗ŧJļŠĪūŧð)Ž’Ŧ‰1ĶþīôxX#­*,•ģž†Ō”ŨPyņōįĐŽdš|žh•'>ë *cåM—uŅ‹—ÉSđM VÆŊ·ÉöóōëäOũĮý4ÆÃoKó—Qš•ó뒞ĶóßŧýŨUŲÓo;›­ÍþvŪ<ØÜöoŧv·öŪNË߉ųõðĮýåM?ËO?ߘöŸ^Æôō§+nũ‘âéa}íÏøéheíø[·ŋęĐë*Āģ6˜n;G“ę›ėÚúf·_‹ýMĸt―ŨuĸĨ· Ŧ›øXž‹ĸ‹í?ƒë†ŋl™Ķmp ,X °@`ĀÖē@D›}‚X °@`Ā lœžEŽĢ Ļ ,X °ĀïÞpþî›@`€Ā Ø Āđ)Ö h ,Xāwo8ũM 0@`Ā lŠāÜkī ,ðŧ·@œŋû& °@`Ā6ÅÞKI7ĨD@ûûĩœv-įƒÖz yÃó“P|a9Ÿ5v=w/EĸXOïuÏxoīžĸĨ üf,įoæRþ;+Ēuó{•ü Eq)ŋÉŊÕ4âË―õVĶuČågķÂŅø`āpŽĻËÅ~GƒŪ|•Ũ: ރ’‰‚zë_Wīn°f~Xo‚âkËKVðküØ3ŋæÓ˜ ČâgÍþąëŅčWœ%[I= n~ÅjŠøķ@0Uû?|ņþSЇųЧ™ÃžÁûôÂÎ{í‹―vßÝþÐĢfUðk$ÞK­ÓuÉp֗áЧîÅíũ?ˆ…Õzi:ÕÆ' †ĩüøt­ūÝP ‡æĮē§}ö.<~GôÚåØĸƒ°įŪÛĒ{·ðög‹ø‰2ï]ĩ ĸ-ĨéûuüZ]ÔûĻÁoĐnA] üZ,xœŋ–+ņkՃX·ė\z΍˜’ÕûŸō'ėS‚ ÓâČáËæųZLz„üÖ*ŋčke>„ïFž‚ÏšK+2°Ëa§â°―·ãwdĮaÂä2~LšÆ};SßĖ”ž™üÞX’/ŪĢž1ūä›ā7?ģųy*}\<zŠt^ųÝÐ~ŦČÉåũ=CIŌWŧ+ŧúr šoėå‹Ĩ“äIzzxúîĨ>†ím’ÅïCfó“gŠLīŪu1ïeé!~Zéî{ķüŽh6?óŧ›î{š^ņ0?­—““íøÆXķ–_eQ°ēąĻ>Á敍G™O›„ųŌ…ŸĀŊ ÅV§ÜŽ‹ũï€JņÔõáw7ģøÝOW/ę8ĪÎÁŸĀ›l87ŲdŋŊZ/Ž)YˆIĩ1īŲn7^{þE”EZâŒKŪAï; ™āWp*—á<ƒ7GMC2·‡Ÿz>úī?Ô ,™4o~6Þû~Ļøî=ô5=vë‡Ŧ/; đïŒpÓ?{/<5‹8 ß98ëØ? ‹6\g.ĶŽ) ÂeįõCãp •UuŪL(§1špŽxųŽaļæ‚ 1ôûšlĩǟū‚aC‡āÚGáÚã[áģ—žĮc_ÏFã&ŌŠ‹ËðņĮÑWŽšUXZLÏwI3ėöþmhW_Šį_xãgŪ@ã‚Ķˆ +J*ņņÔex}Ā‹Ø·p îšö<>hÚķÞM2ŋÄĩŽũ.{Ū?Ĩ?~‚ûþú,ŋĀ]€&ē°Šx)† Ÿ†nŧn3úl…oއ?]tj·vuųôŌŅXT;;sw”ĖúũÞņ<îŋ§ E\ŲĢ í–ŊĢoŧŘũó·kų·ƒņZŦ6Ø}Ũt/Aų…(úÃūčT1ŸNž†ūŒ‚ Ýßî―ÔėßgrÕéß§PĀųWfN§æīÜ Ïýý:ėÐĄS=‹=WÜó U&ðãøņäĀŊŅų˜ë1qøüíŌĢąrr ŋ€kšk€3ÄoØ}?â)<>ršî}Þþ ūl/Ėĸr&M_€ïŋýũĸ…ÝŽĨg43ŋŸŠaï_ŠC=Wüég”›_Bũd„|SÁō$ëęŠPLõ悿yˆų>WÏĐãúxF―ö ޛPcŪ~ģŋų#Ÿļ9‹ņô]áĮšō9uŊĄĮĄgcÔ'Cqz—ŪŦÆŅýäë1tøÛ8}ï(ûf"ëw•âõœ.æŪŧð|5ö#\ØŦ*ŋ…o&NÆÄ/ãĐ·ÆĄ]Ÿ+0jâWöîëh›_q/ރģ*9ĩœËiÚz6ýžzcŋõdNįÎÔųsQĩd:žúæ”ÅpŨ›cčóŨĢmv%†>;ĄmšÍéázīęx8ž~sŧđęĢ”ųãL4îv þïōĢÐ*8íÎ70ü‘+Ð!§?ŒŸĖ)į68ûã­QSņÔÕ§Ą0éM_ĸĘZ[ N`ĸ Āų?q™þŧJj:vcŊÄŧï―ƒŧŊ<™‰ ļï/ļõ…‘XšŠúūÎü1/â˜#Áß^Éģ LœŧĘ{t%Ĩš<ÃÅô 5U9gü§8åčūļũ­IĖ]‰ų+`YI1jxvô'aێ…\+ ĢĻY!ēč-fp-OA<øMë_­]jëOrU%ǜzôá5Uáęc"ŠÅ?ýLŠFØģWO† Þû }Û|TUƒE%Z?äĄ Zãļ3.Äö]:ĒcWc“îļîŒãąm§NhÕŠˆåĒnúY čȍÚtÆ)į‹Žšãā3e*wôV­ÂŠU+œMŽîģŠÂI4íž-ökßąš–ŪŽäZj=bY-ÐûÏWâčšĢUÛķbéUeĨXđp1õNâþKúá”kÅâ ŪĮ&`EŋSI1žÕgÞ|9ŽÚoMYï1!2ᔷŪÐIn˜ į6ãŽæ?Ēóqũ5gã°ÃæåsÚ)žÖõq  Ø( SĩeĶß9=ļ―ą;íƒŦ·Ų]Úįáä+ŸÁė9óPĮõN…Ö;‰Ûþr"§ĸâôĻ2ŅļÕÜH3?e8ÏóŒdrĢ;ôö=vÂ_ïū^^n”iŲū-Ķ}øąĢ]:Ŋ‰Œläįčą  å5*D2ģ‘“‰*ß&šz‚zģķŅĢGfLˆ7GžŒkO܋zðq ‚f}ŽÎmŽŅnSBĒ–žėĘb|픋īNÅ Íļ!nxâþ  “ˆŨÅéá%xN]ļyÉĢtäŒ3…"ÜlTšdĄKŒÂCÞmĩļŽÆ=“ɍ /ŋ7 ·Ø ۄ1qšˆåĒãÛĢs3p:p<ūą#vïÞófĖ@fžˆô3ô’ˆ˜―8 ]ðærî› ‹°Ïv1ÖlcoīéŌÜO„Á=GÛgbŋ티pEŽėÓ…|nTaČkŊaëŒ#°MÏ.Ü(ã=ÓYOīiįpĖ9g`ÐÕ\ûŧüTT-ļ}wŲ‚ ·3&}‡&;†>yCĮĀWŸÆ–ųGcáW/âį%1t;úDt/ĘÄPĢ6ĘhMwuā–amš%t Ĩ˜ėb.[*Š—ãÃw"Ųđ Ŋŋó ÛÛwÛ]ë—Ģ=ņn`ĸþ8h‡ÖhģlÆüīyíGÎ_$~”Į)WŸ4‚sMÚõäšäÞøÝ'øâ쉨ūh?„ã3ąx§v;lévûšēn7ðjMSîóå27ábėsŊ`č~…Øđ}Æyuí·EŸ?^ˆŸ' ÆŦßÖp·­7Iįœ,°a ĀđaýŪ)īŦķūr.nūá NæĒyģVWĢÛáįâö?õÅvÜûÚ?Á ވ›/:ų…PUV‰sŸþĮōŅ‘$―Žpļ ō" 4ßæžüÜJÜðČcļåō“ļđ%å•5ļāÁqïyĮãÕ§WāŠKïÄ_Ŋ:dĒlŦģ0Ģũ8äˆ~ØŦĸH|øĖ-XēðgžųĘh❞/ŪMÖ&óļkõF<ËĮ>‚‡o>ÏäRvތSĶMpëkGāļŊēKŦqŲߟà ÞG"ÁýŪÁ=MCõČf=‡KONhVŊįlg#/$_þáĢ%H ‡/SPq 9^VŒįŊšŊDǘŧjÏšånēK{4Æ1xåЕļōÏwâÂ>G^Ē]w?Ü|7ölŸ)|„…;€lqâۘč +čq˜8§pOūä.ŽŽŧw?FūTHķ 9ÂčCđū+BóJé[Ų=ß=ÛîûõÃ1ŧ†ŋúÎŋģƒļc^ŧUŽfMļiU!ΞęZļc[Ūýā)s!°ĀĶZ Ģļļ˜wp ŽËœLÔbņ…(-/̧GĢ&ÍÐŪcg4ÏÏ"x% ŒœŠ\š‹W”qc7Ú<[ĩhÅ)Ņ ŽXQŒx2 Í[4qӔN‹–,]‚%+VrÚ4ŒFų…hŅĒY dņ\Ū9Vp#(hŲm›šg(ĸ’*ä7n†öí[r píf›Ą3Ü ŧbų",[ūÜU›™WĀuŌæhÝĒđ›âÍHÖaÁÜyXUå ōÐūcG4É qj6ę•+9}›D!éóđ`ZΝŪõ9hŅŽYV=~SQAëv-‘]ý=öï}8~Ūi‰gúŋƒ.‘b$ē ÐĨS{dsMSkÂ|ĐV.úKVVpQ&Zĩã:iQ>â|04VS†åœÆÍ+(BwÕÖV•cåŠ 4nÚ ssæ3ŪņšrĖ_°ĩu|”;ƒ ›ĢyóFܙė•- mAN&jŦŨ.ĒŦK–`þâUܝÜÚ4CŽr%–./æw y›ĒSę) mÐk]W;Ō 0 Āi–Žëą=-ÚŸĶųŦũWŨî<_ĖcĢ (;Ī7 0ÔûvšŪ‹VtFŊļ\?įų1ķĶ˚4GâĸCýÜ&ŋžâ’ŌEúŧüTũūÝTŒ2õZ?;7―íý·IŪ=F*§c·=œX[|ðÅDėے/8pS―^MĨĩęâ·WJÓKÏđę·–>Đ4ãĩÚSvõĩðleš/“kįÁ1°@`Mģ@œ›fŊ€úũnNé†âûÅWÜœƒ]ö܇ÏiŪ ˜ŋwõ,ð[·@œŋõ+Ôïß`_×ívÕÖÕÖzŊü7H X,ðëī@°9čŨy]­~Õā†$ū„! ü>-ā-@ý>ëÔ:°@`Ā lēs“Möû*°ÖsŋŊŠĩ ,Xā7jûðÄ?[―õ§:M}b)ÂũŒšÍĸځČ]}îD[(í\Ō=FīûR J[O•ĩtERaõđVY)9.ÛäH ~=”frÖ”0þVÔō—;gųÕų#ŦēôpŧŒąŋŽ•óëЭĨųË*MÁéHÞkåų䊯Ŋ§§ĒR― [ˆ‰ãc‰þĢ/_É<•ĻX<Îįk]ýüÔYYYžþú˜?5ˆ,XāÏęëô&°?ņŊ„õgnn.^|á9Lúî;ŨyFøÎP―ĐEÛėüÎb8ĖïōĻNUia>(Ūíûúđ-öė―õnmņÏäģlq–ÕëÓ"|æ.ƒ7ĸư0ßĀĒ>9ĖĘU™y…MW–ŧɐGĢ|o(­ pWūĘÚ(ÂŌM'―‘EAzyž―sékįâĢļĘJ/―\-BūzU› ­<“ŦļĘJ='—Ĩ …ôø„eó§Î *'ūŠŋlĄG"ôP―ôŠįĢ™|휂ôK8[éûŒkÛUõU•?ģģÄw^ĄŒHūēĢÞĢęN]’lĻĸJņtņä*QÏ`†ų%‘JėÅw·þņÔÓųMI―yÖ ͒’|ĸý,Ŋū– ,ð?hõ}ÛnۍŲwĸóŊ›\/pŠS:y2F ŠüFųvöėļž™úø.ú @ݛKRq=X-/%ÆÎ^v6ßš"Q/āĖĘâ+S'hļgĖxĪ:ruþÞûAõ.Sņæ;Fų1f ,4==„bŌWÏĻI–€/‹'Vžžā’ŽĖw&–HgfIņUp )ū„ûl§Ģ<1ž ģøáâļ -Ð#(ŠV &{(íųĀxj0āÞ,ãÞĖ"`ũzîQð%PMĄ™{RuŽŦŦkÖŨŦŸėę|ïŦ`TÝ%Wö+.]DëâQę#zÕÏŪ‘€TÕËÖš&€ekʕ=*+*PTT”’―8ÅsÕŠ2ūāž%:vlO9zUO ,ðŋgaԜ9?Ģīī ……ĸRÖ œâaĮ›—ĮWå;°8…HĶúˆ{į™BPEu؇zuÞ,§OK)Mųęˆ #§ëėŲ™ĮxÉûä)iéáąL8”ë@KŦ/ØGÖ *'/ӁÁLü^Ø(ßC:Đ§—įņ Tb)Г7å†įj vƒ‚I„uTČ pôĘ ‘Æ·”ĪN0*ãt8%HŊĐmÉHŠŪŌSÞc=e Ĩ§@_ā%øË“Îā‹Ís8ĀÐB ĻÁ†#byágHRw•hĘU]ďļëdyÓŦ™žþ,ãę+9Ō%“^ŠMūT€)ŦĐĐv Ąüúzo-U„NĘé%āĪō ʓžZãĀ øÜī)AkM< T\žũœÚtC}Ī·6ŌļACĘFn zŠ—―֝ę%ē“ę&ūléįĄÖOeK.˜'ðĩā XĘĖР‚6UšĘ ųęäM“Xe=ÞÞæŽåŝ'ĐoFŌ;&‘ĘÉÞTķw͔žÁχ!°@`Ā6l5―ózh5-ĻŨÛÕJ! %8UŦž^ÞĄóˆ˜/ÐR\yBkĖŨš ‚“+Ōk7“ž$TŧX―õOÏkãÔĐó4åáz3ÉÎK%Čę|ŌeuĮOū’į6ÎpJSxÜš utÓÂŪ^ĐiQzl›Õ@O0ŨÔr6ilŌĘŦÔšŪWߔ§-€u`åM#;O›ë…â%håMßP„ yŽŽŸ<šuvÓąü” <ļ5O–€ÉŠþŒŦēЛú%­M:o–õŌtm$âMÁš ų HÅKvu<Ũ›ūÕ@ÁÛžä Øīŧę$ýĪ—ZĸĪWŠÎœ`ÉĀĘ{R§Opb‹:Ðô@Q"7ā0ßyOt}9HĘĢ#_ÉWy ēuVD&§#5í*NŌcx ` ō ĒnÚS ,ōEž*HyÄ8ņV·ŽI]86Å)Q=žZ?Ļhģ·ã”^XʓtÝ;AO;DšQ„Ā\­‡ę(ÝĨŊd;–į Ū<:7uJ=l]SįóĖŌŠŋį=ԃ#Ü(OÁ·Žü'āũĶgšÞtĩęŊÁ„x T”'[erŠÚ}O’ÓĢē‹ó–ɃÄĖ—MĐj}—:“ÞŲ™r%Ë (žŅ‡“)•5xQđ0?Đ)`– ôqæ(=mŲA6§:nšZmŒ’ąĪĢä!°@`Ā6lÏ Z;Zí"UĮŦĐOPĒWŊĖþYšĶÕMË#rk…ĖrӓĖ3`0/LīóՃģƒW܁$;r’óĮĐ\Ę@É[rD‘Gå<7ō0dqÚRýūlÞKäi9ŊSžÉK^Ūže9€ ‚RīÎÛtdžWBkĒMušŨĨ]ŧPÕA3ŽĒu@Oäj’óðfä)=õ1dŲ@ā%Ā•Ũ-%ÜT6cžįL`cžՑ=ĻĨ›&6Ý`ƒšŧē4†dóŋۀĨAE6r™Xžĩ@v„Ïҏę(}ģ8-î@’ZļŲʒ^ oÁĪžgÕGƒ·ąKzžåņKũ ,X °Āú-°aādoŦ7ÉãÓŨåÕá ąvQį9@•wäMIjũĻCUvþïá~‚ËĘģØož)UyqQN7ØęØÅW€”ÉĩF·* $°ŅOušČCóÖũč‘Ęë$p9ÏRrHzÏfÆčiI'=ĸ(ŽsŠUž|(äÕņ<“į/į•ąn6HjiŠTā§|jÅ:œ ķÎsÖ97đ5X„äÅ:j€Ą|Mđ’ĀÉeķã#=4M,–·,ÏRÓÓ瓸ŠtÕđĀR;šėnãyæpP )cåi@ A‚―Ü!DOÖyÖīģø õļ‰Ā:“ú‹VŠäɖĘpžýúÛJX °@`Āī€œuy îŲDuޚf$P8Pq ã=ú Â îpuS’?7Kð“‡æMđz^š:wį‘‘ĐžT—xš—°œĶUrģ8ŧČMEđŽ;I€ĢŽ^<ä%:Ô)ųÉK‹RŪZ;`qz|äĢ2 šįMz "J: 4ôš:Ïc­ũÖy™ÜŦē.°Ō-Iޚ"v?‚ĩ“Kýt’aø" qāåžp={IÞZÓt^,‘ĘŨ@ÃóøX·vęĶ6ŌčœTZ:8ŧ2.^n Đimrž=ÐÔ c5˜ËÛĨš&õ8Þ ÄÛt$]ÍCVWEÐĘîĐëėiē™þRĻžbÕ]urvSšÅ7“˜ÍÁF 7°9˜ý‚‡ė°Á[íĨ‚„Ā뷀î%ofmýtAîæķĀïfuæÞ# Đ95+/IžŽz_yGžĮįíZÕÔ :GíĶÕī uÚōÉ=â"P kŠ—īZ{s,‚ÏÖüô*8‚@Ïóp= °ļ<<€tqk*Ïļ‰Ôó bœb–NÚž#žX p Ũ2ГĨúh-ؚšũÖ"ķž3Šrē”­ú‰ĐĀ^vĀ ― Ø{EšIoņ—}4ņŪ‘w―\įÎN\yēŧdIūyøŠŋĀcsvëzkÔŌ‰âÂK. 7\‹óÎ<wžõV͙‚'þvĶ–ÓVüũíȍ`_=~îĸt6uÚð-!u}bÕĨXžh Ē”økĒf\UšKŠ+ÁŽ+ķü3ÜrË(á`aõeĸïWü?§A2Ž•ËĢēF›ôü–úÏаđ$%“1Ž\ĘšÔŪ]7PŒVaɒ•ŊČ6—äĢYētnšéV,Žb•nVŲ9^‡ÅK–qĀÆū(=3ęō{dĩÁ^BŪ@ĮóhžŒõð€„/â(ŨīŦŠ:d†ōÔĐŧũĐō܍Ėčý 4mĘVāĀK@’ĄĘóh=/KīBå PĨ—ÛņĘ2J“‡'9Úð"Ðð@ĮËðk Uz*.―ĖcQN§€Žž$—ˆG^Þô­)NtāÄNUr]<T)Q‚õōĶ”={8€UČ[Ö›&ö€^>žt`ĒŦŋtũÓž]Æ;™ž9uÔth ÜĒ’Đšˆž~ Lóͧy]XNĨôðHýólåyÖķJ­]é)~LPtóÖ§E›NØeŧ^8áčŅ13ŽvâõÜAMÅP[ąÏÞügôé{―äL[CV8†·đŌûíu žų`& ™˜9ē?N:t}ÆSƒŋvS_9oŪ>ņ(ô9č`üéžŨPYŊuiŊ ęhę+–āéë.}ûâĪģÎĮŒRNßĢ<};Ž?ļöęũgŒüa;ڝ& Nsg60qÔË8ģũA8čÐãð؈éi7wé"<{ӅØkĸqpïÓ1čÓo0āÁŧ1pč@œpü˜YâMų땇Ëf|ŒW~O=ú7œsɛX•‘Į6īŒi·ĢÏÞ{āø+îÆĒm&&Œ|gHY‡‡'FÍ |iãyą%?ā™ÎĮžûˆƒļ#&Ŋ@Žj)þ~íqëÐûÐÃðö·sh,,øznüËuļåĘãąÏ‡âÖßÅ ũ]„}öÜß= U|ÓÔĒo†āšËĸ‚Ž?{ĸao\þðpT%ē-_€ûŪþ#&σ;ïŒĸaōœĸÕ ÜtÕ ļãúã°ũ^ûáÆWÆĒŽ|"ą xāô9Ī?æL˜Wpžýïļũ>ð7}ØØŋũؘš0†ų߯sCFá‘SÅýo|ŽZÚōķģOÅž.§žwÕq ,PÉāõŦ™›Ž8Ŋ}―Ō=]đhn:ĸĖŊIbŌG/áôcöCŸÞûāÂÛĮÂZķũŠĐļö’û1ŦLÏRŨcÄĢáņ1?ģ,/fvļũž{^1›Ï†n?W>ø6BüāüôũĀ}Ŋ}ŒE3?ÙĮCØG]ĸ4VoFÍž|ëEļũþŧpäĄûá >įcú’8~þv žō>åX<øÆXŪ€ðzéÞĐŦÄčŅý1ōõþļéø1čŧX8uŪ8―ú°Íž|Ņu˜°Ī’íÓî3:īïä7îÆ=ýG#I,<ÜõjéLx/Ūŧų&\yų)Ø{į=qÃcï Ęýņ“pį…ĄũÁGãōë^Aiû&–­œ;7^t$ŊĮ8úĖ+ņÃĘŒýÞ<Wu*î}ã;dĒo=xčÓ'^x#&-Ņ@fƒ`Í18ú,°aŦąQĻ3Vg.`s4-ŊGģ€Hy^gïmĀÉ⅝~ÚÁé:ovōnĪãĀóŽT^@ |y‹Zs_•s É^Ð/ŽīĐīžW‚ĀIÕ<<Ņ‹—hy`ūFĪžũh€*ŅÉ­é§sïK/’āĨËËĖá%:KSTõ€+]ú ؔ/đЇdĻ*Ūzé'ā­éé "”ŋ6čģûv Yt Úel ŠQŊÕQSŨōœü Þ”ē§—óšgēŽUNkLēôŌđWϖžŠNĮl3þ ņžÅÓūÆëo>ĸŧúIäö8 ÝZį;;„3ýōÝxōÛ ˆ}jFâÖ‡ĒžÓĶÛîy,Þ9 ·\Žįï}•Ą(ÞžóaütĀĨøāƒũŅoŋ‘YģÏÞsŠö9ïöĸ+ĒcžÅ;_Ėe'â šÂÜŅ―düH<4` .{z ^}øntlÆü/_ÆĩύĮõ>Āí{–ãæ[^ŧ3Z€XæĪV~‡KŊ{‡Üũ ^―í0<{įý˜[^‰Ï܃Į-Áߟoū~öčĩ%ö;ĶöÞzg<ôčŲčRč œę‰ūE]v&ø‚#?wÝz‡ōP9a0ū^Ø―øęÆ―ˆOgsŪšx".đþöÐkxõæūxúöû1{=Ew øMŨH/Üpžøp%üĮxĢĸØcë yæžŧĻž{gîš°7nļúNŒûąÉÚe6x(íũÜ{qo<{ÍUø2ũPÎØ=Š'ž} ™˜öÅ[xýëZ\s߃ØŦŅTžųÁīÛé ĩC'œ}ßøÓ1ŧcáØũņäīnxá^ß[Ņ”ƒ%ũø―ŦP“.ØŠÕJ<ųŌ+H°Í~õŌ Ø­JGãœËŸÁΗ>‹ũß|5ûãš?A2^Šo'‡jNĘ;\2û{ĖXQÅ~‰FÔh(ģ[4™ŠÁŸ…šģð:üÉÆ ,ZŠ·ûÁ2YČjŌ·<Úïŋx7ō*ū\PMÛ'0éóxc|7<ðvŽLÄ[ƒ&ĄÃΊKgœsĸã8í°œįĖÍi <ėØkOėĩßÞøËOâāŪåøËĨ·ĄžŨÅø`ðÛØ>2 \ýJcŪ'rWXƒŨē…“ņõüĨ`ū­ėLŸ°€ņŠ–NÁŦƒGǰïõžNgāó'čąOþïŒ*?ožũ Þ2uŦ؎Ô;fâøkžÅČ_G“ÃðîØ°ãŧ`ûÖ=pņÍũãĒĢzá‹gĸŠ[ß-ÅËž‡,þ7?ðô C6ÝNošBV4éuôžwč6ŅPž€Aā!ÏRāĄ݁;z€^ĸ&FiyŠËSuŨŒ ÆdzAJņ10r A)ŲU§hīŲE€% r •ybŌSÏ|zðCRéĄĮFÜũ3=@1°VĢÏæc%^ր“Ķ2’vš74mĻ—Ķ+Í=úBÏM: „ĖtÓŅšaH'U_―PÝMąŌ^ĒÕMĢúļ)Z1§ķōŌÎä§ =Zo€ĒĐXŲJ<ūŠ·^ö [ œ]XNĢHé%đ ŌO<:W\`ė€_ųč7ÔgsÕmފ…˜üýhqșxåå+Ð6GuTŧ*ĮôÏ?DÉėoqÞiįāÉžķËVÃ'ŽX43íŲa  §VšČÆQ—žŽÆoށ~݆ÉËkQą|6fOøß|g^|ĶüÔ5åUœÄōlWώīeŊ}púþ­qÏy§âĸÞO‡&‰)cÞGųĒ9øë…§ãÁũV!ķ°eēĸ…ģÂøöū°ĘbųþĪũÞ i@č― ((*‚X@ŧQ@@Ei"E*vŸúŽX°Ą>T°! H“’^ĸįĖ— ā{üÔŋ/Ŧ—Üû•-ģŧsffggwþžŲÛwâé)ŨcÄô·‘ģŲ ų9YX™ą]FŽ@–õhjNE|d4ĢŒīšIðãøē^d_zû‡!.61q HŽ‹€gE6üZ ĀsnFûVíŅ6*–ýQŽmŦY֎X@­{Čw‘ģĨ9E;j‚Æ@i–fíÄĐŨÄé-ę"đ^:"žŠņÍgßáü‹‡ĒIB$N?{šėĊĩÛq€cĄÝy—áú‹{!•å7ĪrÛČ Ņže*b^03ļþŠĒ•žq96Éī-ģą@Ïž’I?ŅLš·VϊNjƒęŦ’PÉŲÓ*­Mvį]™ßYCū_åėdOŸœļqįģpÓļņˆ ÔØ'Ūj%!Ėa1éˆmØ<:AlŊ—oö.cg?‡;,„ũۓpóÛÔšŲ-úĮ‡=āÕéqÅmóąâáūŠĻ‹n―oÄø!āQî2årÄPį^āÍ?s͖Ã*؝ …Šb‚JˉÝŲfëĩŌ{%ÔZØŦįXî{ý lþæM đōVžØĶ%FvŠĨ`+aŧ Ņ šãŽ6`ø€óáÞü2<Ö4yŧęĒ|įJdāýlŲšåĄ)ðâDĖ/üû”ã@Ņ*,ų*ū§°UIkÕŅÍû Mq/ 'ž˜|ĮÛhÜz,Îđf:Î6­óņþ—ĄžëØ{~ n=&Q-éPPįœõæ\'1mŲEmáäæäãrHû]UëNĮĄ=Ī­ÆHT|ōs°}' X?ŧķmFĮŋ—#`đÞ)/.EĄæ0uŋ•+—"ĮģĨ•Íi ÏĖ.?…ŲČÎ/Azļ2ĘöbݎLŽÖJŽüþää–  O_3ĸô<ŊÞuf]q1G­öĖWĒļt;Š*4·―î‹:MšbÖ}ã)ĀPhįôg_íÏØŠBŊ0D‡JĻM'B‡ŦįI™ĩ‡ÓÝÝÏŋmĢ tiZķAÚϰīA2,Į,(æåhšbîĖKI@āÁ‘aQn˜—€TCÝÅüĨ™iâ (īfĪžMÓĒf%î+ ÉŅžø_tĀHė˜õ`^­• Ô„A N`"āÖûŌŌôĒã+ įģV/ŪiJ āOŪ/Y9ųMKcŲjĮAA€`Ĩö9뉎†'āJĻ86™i―Í7ÐúĻÂN.!ÓÞN˗u–ĐQBƒåÍũTķčZAĶkBï“gYđÞl Ō֊Ŧ@SýRŠ8!lMVīāGm”ĀŊ&°°VlžuÖRõÞÉNÜbSV@š3cyŧ*9–Š ĐIúĄûĨWá™ŅsqĸCHŠ@Xr7œÛ6EÛņáŦÁ{ÉÏČ^ã…Å.‡Į‹°žcpĸÎ4χ čtôFęĮðxÉx” óĀKqjģx‹SŽqģó§Ïðøü’Čņī0?4ę8 -Ÿ‡9ÓæâÔVQŨ„ PÁ­3ųĐÏuŪ>ÏãĐÜ‡Ý―š#?ÓÃnŋ}{_„Ũ†MÁHÏÝHóũÂЃŊDZ\2r2žÆÓŸÆ„.@Īc1 8†ļ„x|øøSxˆÏkęĒ|…ˆZQŦ*,@);"šwåúŲ XĀēvõl†žL\rÓ0Ī:ëÔåî!ļæü‹0zŌŒô؉ŌŊįeÃp搋pÝŽûqsÅOØŋō Ûy8­Mr?(ēýŧÂðJŌū”ąw9˜šą/ØįÁ~~øčõĮpMËõđ>˜Õ gwk‡ēÐþļöŪûpkŲČúi1ÜšũĮĐ­ë ó”sþ8ŲpšZe1™r§3.Eú|šoïyų#īá)8Ŋs]”æSĻ`Мß:žĄ€ĩ<}Bä‰'&߆šó&"1kžü5Bƒi M@\ˆŋͧ Šþ8óÜģqũóģ0aJšr+Ō~ Ûø9ns3šŨ-ĮŦïįĄÏýâ†ô7Ü;m"ånÆŨ{ķá4Ōø`bÛËRŅ―[ž~ņZvŦ‡ī―āķû;œŌĶ'"ؗÁ!áXúÕ{XP€Ŋ6ïCųŧŸrýÛÕ'ŊrķĨŠ­ Qþn\›Ÿ„Ī9pJjīÍEÍҐÄT|{/fMš‰SŊÆĀūąxčžIØĩž>zãīūv2Ō(ĀHĻSŌ\ ‹Ŧ‹oŸx‹~Ä·ĸþ ĄíėžO€'ū3S―iŠÝĩ{R:`@ũöHýą †Î› ĸ_cÕēϰ%Cå{!"&{ūüÏūõėuïŋ„ÁÝo@˔Jžôð]ɞˆūcFâýŅ7ãÎđĄhŸāœðö˜6ž^žųb,IÅ5ÝK(x9uģJÔþsL xLœ8qęąî Ā>úðClX·~ūŒū#ĒŒĪ‰3&Š‹IiŽĘ|)íÎ~pō HĒN”J“ž#f­ûÓęȰŒ.åYÝFõūÖôô]ږ§Hō%ļčžþ*/Ý7—uÔ  (šŌhõ] $ĀÕ;ˆ°Fž& ÓVðëžižlÞQýTķĀĮŅÉØøŸi›Um–&lĀmZ­<€ÜlWUŲ%”,•t" /°ŦÞ>tP>šÆĸ-ä  ž§zlïiû‰k‹Œ4gw‚đy5ēĖī]Õ^iÄĒĢŪ T%üĻ/YAûmôP{ĻęC^oŌīNë~:îæ):dgÓ<ÅgCCC­íjÉ$ŅÃ7,M›4Ebtˆ§8…§_(ę7k‹ÔÄx$Ķ6CŸŪ­P˜Ÿ ŸĀHÔoЍZīA‡f Ų'ūč~ÅU4ų5Bpdę'#'ŋ1­NŘËÏE(MS švBĮzČ-,FXT4mÚĄN˜BÕQ'ۀqz‹ÉļÏ―v8.l›HÍ+ýzvåšcJļö˜Zŋ ęĨD"&đ š7j€ČPš*O?ÉÁn((wĢIŪ!ÖKD|ÃfčŅŦ)%‚OTš5m„”š ļŪU%!š1ý5˜**ÜWŊŌĒé‚Í[ĢMó†HK c?{ ĩqK4jŠ2áž=z )°’ZĻŧ•ÕĪącöĩ|ļ^šÐĶN?ģ ĻĖ <>•å6@ӖЊn ýQ ÔütLq!ꐙ{‡Ä Iģ͈ hÚĩm‰z‰ð GËvmŅ:―öŪý ›ũUāĖ3Ú#4Ū FÞpĮø#.­%Z3ÏBŌ8đŘ2â$øŅ.$MŦōôf>7C™tH| zöi7Žï`4hØÉņņHmØõS“ĐYy"6­ŨŲR“ EÃö]R†Č„šHŠ Ĩ q•‘8ÄôlmŽĸčĀû‚ ?âËuĄ5þJ$>•ÞQčŲ―|ËĐV{Įbðˆë1ŽS2Ÿ@œJģp jÚPpšîēAhÚ "Š-Õķ‰Naŋö@›uā–‚3Î8]š6Ÿ§šv< A•ˆJïJgžÞAZJ 6mÅķ$Y[âę5GËjKu茈ā2D%Ð\Čģ~šËÁ‰hu Į OķAömbāŠ―ÐãâŦ0þüNīL˜(cMÕüiÜÍCā^WóŧķAj|6-þ'Ö$ž‚ū­’˜Ü#oŽ”`Ôoß퓂iŒˆÂÐŦ†cāy‘FíÚĢ~Ũk#ęáŠa—!•Úidý–čqJGq)*(4Í:u§·’< á‡MPŸK ‰ÍÐēe .?Ļ-ß$^š•E SHH0yߥūø―­vËĖĖž5āæņãðáû‹lMMĀ@$ ĻT…hcEĒŽiæ.G[tĖģĘTAÄÜ pČČM[ģ&·ķõOæiÁP‘‡Ķú­ä€ĩVšüĪ Āt2ajOÕq00_^0į‚ģŸ7Pa~kĄwUš˜ÚâxðRŦ#[XUēN*Gm’†Ŧē øXV%P'ŽŒtʈ\óĨ9 ōU[€Oï[{ų]kššgfR–mā-ÁÁ&Ė•ōڕyŠŠ―dĶĩ °•7éėlÁqh.­T4V0šĶÍgôūōķčHj ÛŠzđī_—9V•Q ĨųGíŨ5đķļāL~'ōōōô„%™ž7nü~~ūHIIķšđîČ_i˜Þė2óžŪzAÚ·€ļŒĻšYņx(@IÚh1%{/–Ë%+ŽĮj ģtĨŧLķN&j§\ôÕ?2—;oӈÆHSÚÂâJōJõövĖüŌšŠ)žčŪ;i'mÝIž^DAŒkâ4ĢJ[RĩïĮíU4ë[]ɀаŅ)‹ÞÁ„<8n\Ï*›ëmĖĢ’å–”Đ’Nų|‡uŪ`]ÍšqŒē\ųĻĮĶárBėFØĨI§ ËļŪĒdBï ŒÍžŠũȕ„zį`pû*ðŦ*ĮŠõ*ĩĩS1g’–ï–k0ōy˜‹˜ŧĢĨžĨŦýd€ĶYģ\ULÁ4)ÔÕI o{čXcŅUī3­”M&eŅCT,Æ@H‚Œîð;dfELė1Ã"ą\ ˆÂ"Gˆ0"œÄ*9xnøaImÖaâŪdĖįˆņ+ĶsÄ%>NÍŲõ’ë/\Bs䱒1Ũ͎LžŠøĐžĘÅDŦ.ĻŽÅÜā~d*#=<7Ū ĩŪęFžŽS-ĒƒmvÞqå}Žē\ũ™ņČr%ė(åáIã‹r‡“øýP‘:ĐĮgéúal :ŠP‹9ÖlØ\;jž WžĒËÁėn°g1‡%ŅÕčŪ$Ÿ—߁ŠčČ]Š―œt§]zΎM7ýPņdôGyŊ\BČáÝy07Ũ- ëę}sxÝ($T―TqBmq• ŋGÐðˆrŠ?éú~ä8Ņuwũr45ũWŅÜNĄĻZ/IŽ^wē'‘.GĢ čaī9ē­zđŒý~D7VeZûįXĻ8ŀĨĨ‰íŠi‹éÚzĄ™/‰ ž'yUĀĄ…x€Lš. Р'F%ÍË@†($&_ÄN5 #@)Ē­Ã$™–R;P øôŒĪ\Ó•?%X‹i­MIšĘOŠ‹i^,Įc†þcM•dÉgU?“:YgG;æ čøWe&aŦ™5…"AFƒ\ԜOJð+―Ģäīß1Ët­šIÂT=ļHI”r‚R= pI—GŦīI™TÕVŨ< ĨKĢ”^+:› ™yéš ~3Ŋō7û€°~“TŲ$ Eœß;˜eĻ‚”ŋčĻþ’v[›þÞИ ‹EP8Aý~4ĸZívĢTMüšo:ëũ-‰G†ŨĐK§5Yķ4ëkÓ_5§ØĐ˜đT|;#’œ5KIĒUŽ371_ij2Ą (’ $1}‚ÐMŋ"N`šÉėm‚  ČøåKEÖīZŸÞU=TŌĶx_ĀĢõԒ yĮ:āRIIĩŒn„ÓÔ;ÅRĮ-/A„ĸ„dŪ•„-ĀF2Ŧ,kYÂ:ę·LĒēÓétiÓ#xË1pf{ €ųžĩOĶ$ķSĮŒ ‹ÎM`,“œL[ΘhæUþÖ5`’GĶ]ŧ„XŌU-—@!m°hB€ŅJu­Cf:iĒĖ[ĀČ­Hi]ę*Ŧe&G™nÕŠ˜äÔCkÅ2_ŨĶŋ;4v\Ü_·­4CJ(ýN&hĸ·ĸŊÞôSōŽĀD@'ÓŽVÏO[j1ü-ŽPhĶŋ@F Y Z€gĶOjķ.'P!HĘ#ÅąíSËÓ$aō(ÕwGģĪĮž-īËôĒsR ũ. $‘Đ!ĒĖŊŌÅô͉`Ã2G!iSČ ĩýEÚģ@HÏčžTÚÖÁĩU^W›džx›ƒ Ë8ĐLiÛ|ŲĀļˆÛaēöí1 XĀ(MŌ Ð@ jŠŽl—þ0 k—LÆzÝYO˜+Lšā& ÖĄÛŒĪŒôĒčïI3ļĩSBŒ2ĩū”HU_ĩĐX€Ëû Þ ö€&ŸrķŲÖ ųœ@UZĶöËŠO”—Šé˜ß%ÔĶZ ÔR –ĩω5§€J S&WljOJķîHģ6ɈũJ cÎFNŅ2í u9ŪWT™]HĖ]Î/ĘGëtzWĖ[åņ_į·P‡Zgpp0ÎxZīh î>"ˆ/)‡#āhŨ”‡@UOŧžŊþ\õïz֕V6lĀýũރ];·ØfŽ·í5 ˆ:‡@ÎB(`XÜIþöf,I3嚿čhĐVïI–)P•C”·ŊcęĩûüGŨä0UJ­Ö‡ũTŪkOŦœą° dÜU=Í҉Wūvlۏjī:jĒĨŦÍĩk)PKZ ÔR€žôøDpžg…+ō„īĩ3qo&i˜Ō„ŌhĪ]J•‰QޞĶÕpíÍĨ‰:ZŠĢácí=J>/ā<čd#fNæŪ@ Ę[ïó6jĖūŒžąýûũ?Nmþøe*õãžļÞįôÁK/>ĮøŠMŌž.mW '­€Jū&`^j›œŠĪuK Ðo%=o=…i’3­õJëķ(EBŧīrg•žšM>jļžnÎ[ÜŠ~éŨķî°|fH-WæZ§ŋī>jöŊ>Ó§6ÕR –ĩĻĨ@ÍĻ8‡;kSž 4 æĨJf,ÐÓGæE™YĩÞ'‡1o1}ÓXųĒLf‚%Dd ØĘÅ\áíd.ÔZ#s#P8+æĖC°ĀĶĶÕEóæÍ1eĘ$&&Ē~ýúV^ÍMüŋ{BāŲđKWüēv ūYš”íӚ­ęK3(éĄï*,ž Ŧa9K—aë·lŸĨĩp3 ËĮ‡ëĩĪåAZ‘nŌ‹éEę*NpŅ‘Ņŧ–āHϊ–Šd$ĪPóÔšī9ÁSZĶāÚtĪá:ïVuïI!˜ōg1ĩЖ Hø>™ãû/ŅĻÚJüiĻ8eÞs1󮀎š‰k―Ï0đĢmý ŲÕPĮÄ*..ïR™ ÍlH0•ÖŠý`ĶY‘YËtĻwĨíðI{ßŅžĮ­)ŠŽn Ž‹C=‚ĨÖQ|ðA˜9 vĸOĢ VÝđ{ũėÅæMŲčE'k/AT*ģÖJ]fRĮąĮ Þ öŦÍڏĻÉ-u #eÔØm}“} Ió4-•ý gDWˆĘÜ,M_Ĩ„Ū…úÏ抛ã‰[åhÅēt_æsëӓDH93î[^1·ÔČrp’ō­ÍĶ–ŋ—’ݞȇÕɇáîþ*‡§ĸÞvÔ>ĸŨĒ@Ā)íE ZŒÚdbÎú-†\BmP !/V1ui@ōh2q1r§Áō€u™.ÍÔû)åÕŠ< p‚ŋëEý6ÏYz„ę~Dd”­mj$''cŅĒEļýöÛn@ņG“TíÐčÓļqc@CŽÍ.Z9€ähéjƒ@SĀįČ―rĒi›&YýöĄ3’6ŦKŦtĖĶ8x™đZųš†J‡ågŌIŅJõ0,ŊkCū4^=/MR}ĄßVÞWĶkМÞÜā­ëzödĄ›ú;·°‚AĀK°G= wěâΞâ ū ūxdŌ“‡Ýį\“%ïhIЂƗ0{īg\ũõŪņ>wôœ~›ŧņ\–ŦũN$™RÃÜĩ\ô{ß=‘üĸ~ĶFāԉôęDrc" ķ\°ÛĖtĻßĶ-j―ŒĀ§>Ņ ’iVā§#Į\`Ģ0vŪ­zFs2"hh=T`cžŧdĩÚÞĄ}’ĶÆ F,ÐüõŨ_ąuëVëDõŸ‘Ô”þ*ðyqQ!ōyL“LkķJrčÉaĮYkäšĢÖ0Í Č1đÃ8189ī‡4tįρ&óVÛDWŅÍZŠ Zū[*P”öČgUūâĢꞄõĒy36Ĩ|1O !y ˜Ĩ]ÚPūĢže9Ð~V‰-FG>wīIúŸÐWë›ųŒÍšËps­˜―QJĖ’Mí;ĩ8Äø]zœĮŠnmÍ,ÄڍÅČãļÔøÜ!ÐA>œ‡âŦՒb+W–æcWf6=ˆ° 4Ï]Iüē(o?rä1‹Ņ Edxc;{ē]ÏýöŊš2|ûų'ψkÆ`þŅāŋ}îЕU˜›…ĒJ_|āœ[Ģ2ū9ZÕã^zō …w<’‡ÞþûŦ8…„ QIŽ9Ō4õ[ڒ [Č6€@C@!P• RÛVAÛ~Ajš8\2]ŠcLóâ_—™Paũl―T@ m‰s#œĶYŅ”œ”Œ}ûöp*îŦãÏHjƒĀF@Š=ŧÍ,­õB›<ŠĨņ‰VEŒ3)p2ā#E;‰cÓ^Z ĀŅĮĮũāz§ÚĪüM’@GŠz’ciŌÔX‹šŽķĻ―īOMpŅ_tW€Wč? 3ē ģŊįW&uúÉNĘQĄ kS-N&dq’oDLˉ d‡Į‰œĨ™xúņûņč“ïÁ7&[·âÂÆaü•ƒčVŒ―ŧvÃ78ūÎĐBÖ&ÎEŸŠ,Ė›1_ŽËEˆ7C6ï‹ãŪ@B –Ęž}…įgMÁëēPq`/’Ïš ũÞr1b|e rjĶl|sæ>‹wŋŲ‡kf>†―ꒇhž+Qa ëåÏÍÁī·V#Úß™ųÞ8õŠ›pÃ9ÍáQ `įŦĸ+[‰…sïDeŸÉhS?ÎxDõ'ĸŪēžąäþĐxÞ­Lč…·øŪ[ŋ;Ð*-;vg#„þ%ūTvþWS-—™Pš‘˜°$$~5f­í%r rÖâĪiĨ!Q“2̰–WeBÔšž˜ĩōōæ‰ bö™ž­―Š’ŠėT>'PUČ8ý'0nР!B(Yi-qÝšuöW $pųĢ?.°č[œW‚Wa~ū Z—Tƒeęņæ7€åĀR@{–ī“đFfV]ÚĨ&•ŽI“ĐÖN^āĘ\Ëß8MĢīR%wŪ‡hËôËLôŽhŠúXŲķNé8.i/ĐčĻzĐÞÖIEu2šņg[Œ3đcޘĸÓ*Ëmioöfåðd†ĸÞ<üZŲĢd.!Ģ:ŠŪåņÚKd;.0·ŽCÝ·Ïމ›nþ.šú―·oL„·F_gÞûn…û0{Ä8žĩ%Ÿs°Zfšw•Xą/ý‡ÞˆÉÓĶá†!=F_? Ša–”ûĄßäŲxũÏðö‚Û°âĢũðÓæ&ʍüÁ;-;uCĢä8Z‹ÜQ°ãgTĪuÁ­“'ĢGļoüLl. oēBA\ÁÄWtƊ„m3Ó3đpųĶŋ|nĸӉ5Z&RŠ>ží‡ø1OMipæ%ļŽW+=aï(KÖJÜ:|$ŊÞÉŦՉaYýÏüSpj-€ !MŌ€ĪâbžåUڒEža'HsŅaËŌķĪ…ÚþBv€žs'’ð_æĸŌ4™ģå+Ā• ]Ķ“:äq+—ŪÖ14äyŒŒ”m‘zäx# øģ>šl“/ĩ^…ū3ŦzVĘ ü@āģu\Āl i#ÁAu–@ DßM+·ÎÉŦõN Ķ•kÎč>'Ķč$æ,!ÂĨŋæūZ/ÍyO[ ë˜Ė-TæZ•%OfþqĀÏëq™z•ŽNl‹Ę0§!ŧzōþq1•Ģ.ÎýųsŒ―īúžįöÁËgc·ŽþՒ2Ũ{Õ.üjũþŠúBģģĘĻžœ|Š_ŅwG@Ŧ~ÕņtŪ~EC‘ĖŪāvmÝi7”—å_^„&_ßú;ļ@IDAT‰ūWÍGČ^īŊūþ–oĘ?<ŽßęĮ›q“žTý%VJĘŋös|hˆFš#JÖå•;0đo,\ĩï1ŦūiIBIq˜™ČŊXÉ#ËxķjÝæíÐŪI}ÐqŨæ™S&_Āžš?ž“ŸĪh?„pūjđ’ÆX@bW ščīJįŲĮ.EÓõ€ë/ˎåŲMšīÂ―z!ÚĢŧŨ|Œ)“ÆcÔčÁ>aO―ÉÃËũނ3Î: ƒŪ―?ėÔQäœë+ߞŅįwE›Ó.ĀcooĒĢTþ1~>\‘a'8ýðÂ-˜öÂbļQßņÓ'øtÅzūËå8ūïå^ŒÏ?zŊ―ũ6Æ ŧģ_úŽÂ;­‡Ūšýý=ΈĄĩaŸÚ˜=ŋ ,œm%ōNÓácĒ?ZK3#đ$ųķiZ’€Ī•šVā/ ČĖ•&!i[ĩ'ūŪr,Ę AAōĢÁ}ŸV{7•ၰsįNSÎŸ‘œuD ŽįlpP Ũ7óŅzûęā\šg(DˆņŦŌ0OcZj–.Į fbÕڄ…īs§Ã‚™đŲ~i /M“r‹%A™Áų[æ[[gķWewž–­3dÎåoõ‘9 ™ĐŨ1-+x―fīV­|ČP$ƒáõ“›H'ķMAæŲ 3iÝqõuØŅå6,zv°HžsW&ļþģcÕŨxmņ2„ÕIĮy}zÁ}ķm߁ý{Öa5§Ïčy–/^„|†ŒÕå;Vcˁ ė[ó~qoŠ‹{uīmÛV-Á/GX’ōé ·ýŧ°}Į.ČZ‡ïđÆÕđŨyh„=ëūÅëï}‰ĪÎgĄw›Tl[ũ+ Ë ðÓ§ŸĄ"đ1úöŽéŊÆÐ{Ű-í\QējT­Į;/-ÆóWœ‰kÚT"ŧ ŅԜs·ý€·ßû72˓Ðg`/$āûĩ\Ó*ބ/~ĖFïūÝąsÉŦ؄šč{NODúU`íŌÏðÁō_Ðđß%\4ä“ÛŊÜ4ï5]§ŧ{)Ũ3đŪ^€M+Ö ĖŦßü9B[wF·-Q”ų ömÝĖđ˜~tbh~‘ŋŌĐ­m‹ļU a:ãģMĮÎxúų­ðŋdÎkŠ63Á€6uĻŲđ„‚Ē[:Ķæâ§fâó—c0øĶÐĢe:Ï=īî('Áœõ_ᕷß‚{F‹Q/ mÃT?ķL•Ŧäņ†e:ĶŽĶę˜ÓßÔ؃įšî\ö)þųōNž5ų]$w―u<öãŨ^GÜÕģņā%gāŧųS0íÍ|,|ï-žųyļýÞ7ņރÛmÝøë>Ė|ô9œųĘ?póõ·ĄßĐģ°jõHâĄčâŅ9Û7á'·Lûžõë·XîžÆ:ˆHTxhtnÛē5šÅ6ÆÅSïÅÅݛpɈsÂîþoýSp:Ŋæ FÚI3Žā!ąŌBå1+‚:'†ÐäČÁAyƒZ[.dF$Ā’ŅÛš%B^ĄŽyV–m_f_‡čmޗËn€+ÓmrZŠE *äI$999ķÆi Ŧ,þ„$Ā4-,((™\sXI3ÖāąuG6ÞęČ :ũQIĶjĮĄJßøïi{Ž/æ%MRŅ“œ-#:H/ÛãÉōôÝ QĪ―ĪmišĒœ}DS―Ŧ3@yŅ„ģvOsjÃ:ƒOŽYŌ_M+etާĘ7‡#Ąą>'9I30­˜|îĶŊðoŸ6xü†syÆb>:ó@|r ēWūŒ3^Gûn=°ëÍųøyO%Fœæ‡™W E`Ŧāŋũ+Ėžĸ/œ{V[lþrē“ßÂYÛāŠđß`ðāAøéÃ[ą|Ũ]ļŊÛŒšö:twōYģŨŨuņĖĮĸtžoĨę<É}ņwČîčĀ)­’cÝŦ‹æLÁ“_—MÅ#·>… đ‘đäižņCÚ h|lïČ/ ˜āi4ËŅ ųEų\ŨôCxlbęú".1 ą>\.Š2“p–ļ‡áÆÛįbDA6>ū{û|6ž{mZGR‰`>æČųę•ŒN="1,sžūïÕįķL侓pÉå0Î_%S0œoöï‘ĸh–nĘØŠŸ7† ËÍĸĀ€AgĒlųğ~æŽļ BŠņþgŦą{Į^Üxé%Øŧr#Ę}öJäCų€1wĒW+^üåļûĢXą―€Ž˜YLeųˆĸV*å…KĩU‰ãœs!8&ĄŅąˆ­SŅ‘äcUīp=ö?ō·Fā4  #`īý"Ž1v‚‚1k‡! Å(ĩ'Pu‚ŧ3zƒ™+1PĮđEÎ2bFŌ܁Œī5GĒG(@f]yӆ„„`Ž\đŌō?pŠ LĩēÔčhLJIՈpįƒƒŋO0đ@Óī]ķĮÏÏŋüžŌGŲŧL~Š·œĪĪņÉd-P•0ĄķēÅ5MiŸZc°í(Ôšy‹ucÅøŋRI—DSUWóʏfj™y%ThW`'“ļÖPÕ'zH[_ÔW*PeZ?°Yô]™É‰ČĘ  Ę ēĒŦĘ;YI8ŽvhmšŒß‹ČX*LŊÁ2įālցĮ.bų{Ÿ#ąþi9üj·õ@Ÿ9KÐ+ąĒ“aāč Ļ\ú,ÖïØ€ącŪĮ;î?`Åî<2ļ|Ôéw#ŪÖÛrpíÃŦņeŅj$5莑Ũ]ÂÖîčóĀ—č•Бņ1hÔtx,―ļkŋýËWg8‹ą)?ũæÂ7/―ŊˆQãNEÞØ'‘Q€õÛĄERúØPšKïBvĩԜĪõ€Söވ+g―„‹ÛÅI bįD Â?ōë„áÃmûĻ…ĒíqýUŨĄnÆģXæÕC‡AlÁäïØo7žĩÛōq*Įéšý{ą}_äėHĮÏÚt hîÉŅMņM“JzÂjN”ōlßýėĮū̆cÔÕMðÅgÏēóáĀó}}ũs.ÃJ%Þ7m“ęāŪŨÞĮNCjĻ/Jē6áõ·ß@t‹›ánƒËCnžæĨ˜Hu° eĖߏqīŅųün(\øēũģN°­_7úÅĒUë:hÕ"Í{Ëށs›GaӆÝŒŽFD0C•r6zp^›• •Eąø°yĐÃ6ڜ9Į] Аâ7›96é@Ļģƒ•B"|‘ÔĪ+îyāfĄČĮćĸ'DNvŽ=“―i= Kë#5*‡ÁŊĀ.:]•åfāß_lCYÛCæŨęxÏūK1hžƒ>8 {úaé/ëÚIÁ)ŦČÝ*ļíŪ’Bšox}4OŠĀóóî‚ƍØŊūÃӎ ëßýgĀĐ.“‡•ÖĮbjrˆ‘IÃS)žGmZÅ–Ļ$†n jërUÎ<”ŽL3#S—deÏ*š õ‚ÎÞļČ#,Šæ€æ-Z°ŽRlŲēÅ ™2œIĢĖŽóۏ,ß,d{! 7ę7õ…G$ëCĐLmð(āD"hVäHð“Þr ·ûŲĶl^Ï&Ļ”:õæãjæÁ$sh1Ėv%Ō’•ĩšÛOÖ0Ó32i;^ŽōŽĨĮ<*ėūš'š9ÛmôÝÃG‚8Á9ņÍDËJŠ:|Ú<äøŪ&§ķęčū؃C;§Ûĸ)š1ņdíë•W ŠúH“”ÖŠhëF@Ņ= 0ĘӂNäó]‰’'9ą™Önyû•{'bĘôļ}öœûF|(Í7šw†4šLÏïýÜĐÍ]~Û]H XķD$]E7 īxyųbõOâ’Ĩ`ca۟}*ÚÅ5ÆŧKĮáž^ï[>WLū ÞkāĄh†”ÚÔ?P§í íý5Æ_{ǃ―0ōŪ9tæpĢ™•ϰ<ũ–K°KNm(ũ§0tÂãxvĘæ0Qŋ)ĀG1‚ęīÃ?n‹ąó– Œžâ1IŅXũÅ|<ŸL'ĻUxkåeô`tÆ5߄ ―Ïj ŌÃg^x–Ū™áW]ƒ˜Ä$Œžu&š…ÓyĐVã<æ(ý\IßõSŸÎõ‹ČMRÛļņˆâô)Á[ß‚Å9­‘ÚąŸëÕßü­ PT·~ę_xũݏđÍ#ūÜGéžđs§sL]:Ãï{ŋü~ĖĸāLáütũ Ī€‹ž…č{Ådôî}BĻ&uÄÝSR҈&[øD/[ģ1h9í&Đ~(+ރŲOˆ?Èj™Į3(uĩĮļĐ](ܓ߇Ysœ slPÓn9x nŦŒ6ARŨĨ”„7ë)cšpÞÐE­3īŅŲxxA],úėk cž<#•–)wôŧųnt(ĖeLíH3gõėhNHc'?€ĸþ\B9nē=cÍĒåiļÍ-†>0dĘ}@L}3MOšũ,þfw;čÜßƒÔø ]ĸÎÜ233ŲrÄMcoĀŧ "ž­“bî­bĘķ^F†k`'Д9VŨųŒ4G:”Ī7d+Ō4Éžĩæ)ÐTd"3#ōŲՕ:œŌwÝ}%ĐB,^žïžóŽå§{.ĐSßÅ ČĀæĀ]ĶųF5ôGÃ.!Ô.X&ïÄ‚ąYk˜=ŋR‹  Š)|@ëXŌB―ē)ló…Ûö\š09ØÜĘ~ū˜›úģ ėoÃë6HÃÚĩ+ééKįĢķęŊ1I€ø>”™Ū9ĄĨáĐÎúm`%40š‰• [NDŠ$ ÖN8ļ1O9céYĄXķm+Ą XënÎvyÄĘ⠐Ķ$P§š Cí_ÖÕ/Ūģ:Õo‚ĻS ŸŒbЅcúŒ™Č˓ž“ī_vãÆ_Í,’ÂÉÍrŽ—Dōý…åørcūݔöqčŅ<–Ūïl7+äéåŽûķcëŽ,Tzú",2ąŅÁ4íĩkžAáHJŒD%Ĩjíõ Fŋ3Þm@} hÂ7ÞŽi{ûa^ïT‘)ĨƇZĩJđGnˎlxY>Q˧ð·ų„Ō֔‡­Ûv Ô͛ëV ð"ãŽpũâúą;ō đÖLĮ…gËÍދ―^HŽ 3MW(.ĘC™GC·‘KS(ĘĢs˜O įFIŨöÂ'ˆĖŽŽ*^Ądž|>Пã‰w>Ü KE%Ë ðũAAn&ķïÚ O?YWb ,kÓą) °”“ „鈈ŋšäų9īK°*ôƒ˜/…åÜübö'} 3ņŅÏ9øĨ ­Sļ.XŨßÉō…jÅÉēĢyĄyYĘõÍ +?Į3/}…î—^ƒž-ępii„[Į8ĸ&ō+YÅÄĩ4_Í˞™Ë§ĀGKVœŨœßZēQ€>ÅđZF~įðQÍą æYĘ<ĨÁúPā>˜Č ŠĻUVOît2ôt#ïÔûUIïy™sĄcŅÐeņ mõģÄōŠĻāčī%OÖWõÝägĄ―Ķ ūâÍšoTýhĄq'/ōdôݓ^üDikŧ懅͊ ™ÍŦŅÂ)í/ûŊ”ˆõë7X;“’ þÓĘVëĨĢg!9]khrޑSŠ:ÞB 2CvŽœT\ŪßŌdÔ].S 4/ŠÃžggČŦSÏ pdú•‰AwÍ,Ė^-!ÓlŌĪ ;˜•`žgÏcÚķGfĪëędūˆM^ųøÕ?ƒLāĢQ§UMZCĩâýÃÁËņÆwœKVŽöJq{ $jeÜߔâė‘ĪÅđÔ~óHĒj{i ĘĶwėķՋ đ„û7ĩdĻÄÚKó-žåžÍüfú-AĀI•Hí’†īRi‘FÞWŅTŨ $5‰” _úmÎ<ėB~9_đč#Ú+§Ö;Ę8ЙģģÎJÐÔ:Ŧ'ëĻÉ­3C ÁöŲZmœüUGĢĐCš“ōŊšGý+†AÛųÁ<ÃbRél‘fĨ;I‡Fü(YŋúÂĮ?H?h” YõÕ;’“ÜŅuÛáīøúĻŨ(ÝŽ―Ãw―(17æ§Ķ|āŠú°uÞå"VU õvîëg$ĢĮDōŊ+]ó&cæ}eōBļŊŋsŸ^Ŋa„s™ĸęëB{ÔŪ·ŒUO…Į°ūÎFþęeĖĪöËo( ĀTŠN/nï°ß\käŧ)&ŋú„Ī dį6”Ó ŧĶ$aīˆKîÞĻŨōLĖlÓË,=Úûx8„Uå&å―#“ŲĒbĒYõ(ÏĻ‚:&ЕĻ\ø+í˜lį°d@|ÄE9ęS=•“ïšjTýšœÛŠø92 Ð]Ų–UŦ9MáGyüČŨĸÖŋkNĩ^Ė[ā ā34M“ YŒZ ŲŅ<íIԚiR|V !U^ÛLô]­ÃL;TėVŠģūæ€a ’Ē5mÚÜīM9—8ÅØ-?ÖEÉ#āŨĀžN‹ýŌĀæ7ČéMXx‰ĖžŊ ˜WÍ/MŋēPæĘĖ’$Aē|>äNŊâ=tkGSKĨ°l*0ŧ9ž2 LY4Įf`ó9<™4DmĮĩöS2ī-7Īa%LB‰kŧŠč(š čôē"IóTÅL!íD'™ŋĨ­k­Ô]ŽN|VįxĘ\.Ú2kë#ŅUýĶ>PÚú(Ŋ‰øZ·3šģĸ$ޘįŸĪä&XÍŠLiäŦVŅ頊Ķ5ŋuč õ·RuYuæÔÓÐ?Ī?üīōĪÖŲōŊýįoE·á0äŊđ]U™n4Íxō`gÚ`uōHB ―áˆY' XŒĀjÚÁŽŨŨJŠö§yCßéÂîW.u•ĢÃĖŋÔ>ó8X֕Ģ`%5ØoˆčB\æSDģĢFĶflŊ*5D 0}—" 3S+Û'€S{•dōhÚ}ŌAÞīĒŪéđRšLLļ`ÉĢ{Ō$]ZŽýV^ė *O!Xjũd&•ŽÆMwķj‡ÂôŅ‘m.ýÝÕūPKR`GŲv”o=šŪU<j}đ't3cĘnaH9w $&'rËE͛.{YĩįĸÂ_%t“―šĶ\3f‘ĮÐäúåĮoâ“e›ŅęīģÐģ3ũ-síŠæwMwj ?~ôōŌ:âÔzĄīĀý7đŧœ?ëNĀiXČlÅė„f‚dŊˆŲÉäJh‘IRĀ(æ.PŽZ'Āj]ĀŲ—HíĸÉöÎK–—˜ķöŠ1*0:ĸ˜)Ž~ýtøŅė"æūkŨ.ŪÁq?·bˆųëšĮ2ēÝļ~§ïüļsýˇ@ĀGþoó—ÉaÎ4Â6Ղõā=Ŋ ŽÚ:t<Šïüåp–ÉÄĘ$gąÖBdâvE5­‚ Æ6JĸSüŨĖĀH Ē•iÛĒ%û@Ĩę·Ž“‰V}$:›™˜ũĩvŽwD[™ķĨ•Šī–*R ŲCš—NZR}Õoŋ7Đ=K–,ĄVÎ;ï<{ŋ7Úįk)āĒ€Æ!ŋëįoĸ hJö2V탘ĸä;ðˆŽ ģOĒëĩÆýÏ?ŒĨ&ĸöÅŋāNôRnýÚy  1ŅáķözžZjÍ4—{ĐŊūáqīęÝ)Z3fÁ·þc^*ŋß7 +Ū|§77Ü8^=þŧW#pŠ9ÛqXÔVŌ…rVQŌ Óģs *‰ëÄįüHj™dÜ ­—ĘŪ/MH€*ģˆōŌŠ ‹Đ7äáÐ 0 mSĩZērŠ@ÓevdĒŠÞeþ‚Tõų‰€§pDIo hT·ĸ*UUÃþčiŦīā īV&D(†2ŨŠžŽģKg…õąuH‚†ĖŠjŊh-*Š­+óōP`x―sDđ–Ė5HŠ/dVEkí­ĩČDĒ;SĪųë}þcųë―?*ÉáËŽûČ2UíÛ·ãįŸķãäĶwäSĩŋk)pē(@AŨÛß<1ã&ū†‰Oŋ‘ý[ wÏfŽ]·abđÔQZČõtĘĮeþĩĀņ6wxŠH~―äýāĮ|ŧš?Č3ŝy­ų[Jkítœ·īN‰—r~——Ōņ&2OŪŦûPðŨ2æĪæn­-ōVðĄũ―æŧs­N? ÖÁ<+x―Ró™ÚaQ1éh$_‡]+žÃõwŊÄĖgæ"ÛīĪØh]REžïÅü&NûŽmŋ <å<Å}Ÿ\þ‘TËŨ‹ŊŸ fä3*‹u—(ĄĢ‘ ÅÓ]IžžœÄZåŽÄj!0•Žul—’֏uOG>Žōēķ’6eĪMđ›—9S9í'ílΈNr€ïŦV”ŦČ?õoĀĐý…ZáÔöi2Ņ–pۈy q;ŠEžPļŽ“kýMĨ Õ@Q€§wųčDO>"ÆŊw]Éß/éé ėš<čžzχ|úNš›yÖ^úĸ(OzLæqpäspøSÖþūRšLK9―đ+ßŦjāïČö7Š tlŦLŽōšĩČJŽŋ7&kĒXP B )aCŧÚŦÁĒõQ—p!ārs ’ðÁQÉŦ}ģŽŸqŧLš|Væa‹LÄ<ÄJJé­ú™RÔĸôä†ŋŋ?t(yõĪ~•Y~͚5fĒmÖŽ™õ{õgŽũ=/{„âfw?ħĪōī gžïÝŦ,ÎÅĶ-;đ6ŪPЁˆŽEŊ3kz·öþ_•ČÍÜÍū§—;·ÅŨĄCŽÍĐļīÍŌ=xô―/Ðrø}a'xЏ!2Ąš1G9=ĪŋXļũ?ôö0BýӇbōÍŨ#Õo7æ ŸŽŒ8?Žųü lˆ>S%áå'āį­ðŨįĒcž}ŨøðOYų%ÖŪaTŽŲũ`øyąä]Ü>ó!ŽÝÏ―Ë Ú>yÂíčŌØoß}#––&aãwcõÚRÜ4įi\Aïņ­K^Įmũφ­8}čÜ2ĒöóĶūūĐî™xýýPįŒŦņÔĖĄxhþËø`Ņ*ü: ũ>0-=–ā–ɏc=ƒ}tđr&Ķ éBĀĨzÓVdŪĮė'bõŨŲļō ļuÎlä.{ũĖ{{éŦŅøĖŦpۄk‘ä―ũ^7;―xHýqļûĨÛŅ<Åßx˜Ž[ÛWŒI7ÎĪũr’šķÅóî5‡Í 1ŸĘ}X0} ^øb%öû$`ÂôŲÖ% /Ü< ?įmü ŸmŦ‡[ïŧC:%ãĀÚ%˜qÛõø<#-Óę Ð+Ó‡:<}FĘŅ_!ąUĮObČbîB~i‹2ĐRø0Æ-ð’vdZ&ŋÛi„Ī™ e “*ķ’īoQÉ XÎu=‡Ėđ.MŒë§ ‰‰ˆ7óŽƒt€ĩ$Žį§ÞwI+ŋwŦ$먔…}ۃÐĘĸ,ôūC.Į ø!čŲ áôļ,Ž(ēšŦŽãĨšØēh`Þē”ðäkÛFÔ^ŌQī“&iT@(ڈŌĀuM híą Ž2ƒ ,Ā΁QáŠ4~'EnŌžLóž•dĮ2UŽyÚŽd9æÉLŠLęĖ‹å ŽUŪĘø#’ Ká<–HÂBõ€S.t?™{ÛäŠbĐ ÛÖüSƏĮ}?‚{ģÆ,gžågïÁūýÚĸ۔ģküĮTL9ó‡[ĮŒÃӟōŠß>zÂWJđW.cũ b'üJíƒĸ ļÝįóÍÃ―?‰đģ&áŽG_æ6ĒėA {ō0…ÛkÛ*ÕöGЧÉ#ĩ„ÂdÁĶũ8ýtĸūú4ŠxïûĖf6}û ūÞä)>ˆf+ŸĮøGūƈŧžBߕxāÍïČ―°“A ^þj+.™2ũŒn†Į§LĀĘí0ýš1(Šw ·Ų―ŠþÁ?cԈŧąŋŌ>ÃãŊ~ãîÃMg{á‰ûßáZĸVLq;<Ï~o<=Ÿ<: KķSČ.ڇž|ŦËáÉĮ§"óĢGðų_\rzKĪ%ž‚Ų܅.õÃðņ‚ ø.Ŧ^]ôFŸÝŌxŪČ-o\ũÐ\yQwĪ6ļ7ß=1{Þe0ˆŅsŌģXøĘ“Čújnû·ųëŋþ?—Eáîn@ýxm)ä'0ï[‡iÃGĄ ý`žō曘=i,BxÛAYœN—­ûŽÂGŸ.˜˜ÝxmŅWdeîØģá{žģč'ô˜ð,nŽ—~°Œ:yæN‹Ŋóáå…ÏĄwJ–,þt.!{øËĪÄ2ĮĖĻúš*.fNĶfëtž(ÏLđ]{Pʧ‘:ŊĩNĮ‹ģŠņ“ķzWš†‘uӇĸ)QBĶ89šĻÄž›5oaچbÓîÞ―ÛÞsu­mšiRĻó;1ŧ2аųĮœ’~1ĶtŒVIõ“Ëւø2ó,Úó2ļîáEÂÁÄrĐãŲZ>QBÍÍ[ŦĪŠĢ%ÕÓžXĐiēĒĢ„·ÚĢÁC”ī6JøPJƒÔšĪ4C ýV81Ģ+Ÿ—–i,ũO8։ýâœx"3°cRø*þĶĀZy›kf3‰!P`ŠLUL€ýûĐzīFĸšÚí|ūþúkÚīicÂŅÆí°ō  aÆęĮÏÍđ{`įϘĮĀ ~=Gbō° îþ+f―ũÍ Ä―Ó‡āŦųáóz}p{ßfÎØ­Ęīē, ÏM›‚/*âÎŧ'#5Ä ;ķlDū_<ŸÐ§‘Jíœ@›#°h͚kÄUæ.Ģ9Į―ģoŪ›~þ7žŋcobšÚÔïķ=€g.Xþ6ØN :Nt(ÝĐMŋ“œĢ§œwš]‘€ōŒŊqÃļXŅŊ?zpÏæ %ÎÁw€ÛG4§4ŋ$ÄyÓBôÝĮoÁ=ĩ.ë،Bĩ;Fũï„ëŸ_‚üéÍQ‰ënŧm›æ "<_ zķi„_yãû=û™ įĶGŽšÚ6BnôEˆ|ävŽgĻÎUa 0į"œcŪïåCpßČųČ(dÉ^tóL\tjs|öS:*Ũ0Œãþ­X^œƒ€·bč %Ô–”ÏVd#šëåļÖž’ē-BÃxÝ qæĒNZüyægĮ‘ēl&.°WŋtkdÖ0pž‚B =2 IīôĨÅâûûކw―Þz:—ËH‹Ņ}Ûaâ›KP0)ûģâpõÍwĒM_–CÞÏũÅgrvŊÅ·Ã1õ‰ĄH‹ówŽj@ãéĒiŧóŧg0sî6ė]ŧƒáX'§ ūyuhŽE –@į~•įCAc Ō"̐vÃĖþä)ÛoB}ų=TpЧ:P ŨĨīþω/N pObÆÞīϋ€F™eŧ—GËŧ1m­sJŦWÖZ%_ĐJ1#:ϐņX:ÞãĩąÖjĸúõëM#Ņ 6 `9‡Rõï‡Ūí›L”{wᇏspũ€™ÞyØ9:Ø%ųŨÅ`~Z„īÁÝĶ`WņnOņ:Zïņãē}ĻįÝO]6Žīy˜·i6Vø‘&^Ū]TKŠčaklŋÏÆ2Ĩ… čDW1c5É<I[9đ P™GžW f{/:ŠĩVåíZķĩdŌÏÖCųŪģÎėŽŦJĶuš ÂWųŪ4~ģ$øÕî\Ï &ĻŽĸˆĪņĒōåøĨmȋúŧïū3Ū€TÖ=w"ióĘOą*Ļ)æ>!dˆmˆáŨĀ„'ŋÅŌï›âãUßá‡Ĩ0ccg y Cø9đæo]ÆĻ2Áļh܍HvöuĶĪ7·›y;VāŸ/ž‚ ŽóRŊ:}ÓUXýÚ#xųóup ŒÅ‘cŅ‘øúÁ‹ã­Ŋķļv4—ž‹wüßo/Ɣ[*qËôáðØð!îĒÓIIQ%Ââ[↉ÃWū/―øOjFnøigÎđtú7?-'Bĸĩg8Âcë ŽĻ;SÖ;"á'jŠåŦô @ŦÄüãųW1ėÜSО'č€ZėþýYINGųķUØMg›Č0OlÚžåauđƒ—ŽvzWûĨrŪ:įËR@!‹d”8KQū‡ûa˜ 2ũ0ÆŦ;"Bá•ģ?nĖA§č(dn]Í5Č`ãIÚ`\™B0fF áüôAxđÎļbŪ?·.ŊđÃ?ŒĮcofYî!ŒĄŽÝ ŒV$ĪâðŨPQŲVø ĀLĘĻf—ā…áƒãækoCë^E:}ö/þeTīĘ2gGBL―t”<ūŧó+TĘ%ŒÍŒ(ĪĢÖȋČ;‚Ëv‡‚j†zų…"Čk?~Ųī`|q­uVp—ƒ’ũ9ŊxéFL™ëį>ŋ_M‚EvV5ÞįŪ~A…OZ5Õn†ú +ÏÂŨ_Ū æAøę•7‘•íXâØp>ðŨH5§Vī§C• öbĘZ?Sˆ+šíŽŌjjGUZ–Ö-õۘĪ.Û,æ.€ðÖNvīķŊČT)@ ‹D:ĩM›6㔉Ë9ÄÅlUĶ’ý­únŽų73ōϊ/ö wëĄļĶó€ß€fã<qāÅ0R+5 nëS'`Öú)fšÕ€ô í-ký~ îw*’ümܔ/só‘Iĩ$Ė$­:N—“^·ð‡kœ2ϊžĒĨ.š)—4‘ °338Ÿ•“ī9ô(Ęoš°a§ŽPšDNŸ(?9$))āžTW•É۔ρyŲ66JzĶ™ˆ%ðüAI}ĐOûöíņŲgŸaŲēeķÞĐ3W;wîl@*OęMr‚ŠI`õ`glč―čf­TēÛKÃÐ6% %q}0|P„Šu@fÅPkaA”īĢ~S”âŪ_ķŧÂČū§"ûÛExtá6ÜrïØóޓxlþ{hró9HkÛÓÎŦU ïÆ?iŠęßcFß~ӑčÏ-l˧Î7Jš—ųŒdÝãÏ9ģæL†Įž3xbBz_; IÔī ’Š,”‰üƒ‚ƒøMT―vHöū sĶÎÁ˜Ë/ÆÎÏgãÓL†é[ŸÔĶÝÆ(CâMŪDÐJ띇’:^€.ÍFãÆëĮĢsB^]ę gõƒĢĘ™IŦ:Փņāäv{ã)ļ}Æhd/ëLūŨ^kģŠhM ‰HCÅþðâĢOaÕĶB|ēú ,ūĪ3<åTĩÉ[1xV†á+c0ę‚ 0éá0îŨWđy=LĻï( Õ þ“ŋŨœ’^īFf.æKĒÉ<Ŧ$&mG1’…í”í’I`čŌå=ŠÎ—fĨŧŽ'.ó"ów™xšģEEŒ?&&õęÕ3MSšˆ’Ģ•9Šōu}t0Áõ94t―zˆŽYž‰xž“8vö™JŦĨ5Ô§Ŋž_ūډ3zbÚųãč0äLšķa]qvt?<ŋó^óÃþ\F)DzJ“ƒ9ð—tWŌ§ĘīG‚žâôJËv҉ŦQgýWšžîËŽëAï< *rŌAßf2gØZ?vō"(VŸÏÆ<Ā6Î)ļˆNōpÖĢúSûn―xßÎK•p#dýƒ’@Sšĩœƒ:uę„ĸûß&,ĨĨĨAÝÓ3'–øĮĻzWĢ’lÅt‘óČãpgXʒ’Jô―ņQīóĻg^î ðßáōhœĖøĮ îBŊÞg›1ąßs?Ō?yŦãöĘĐOĄŨĐML{=gėĨhŅ<îėC€qÃysæ"åËũąbÆxLG‚?ĐųP<<ĢÜúQŠhŒc}Â)XķÏ=Ĩ? Ēš‘.YËw Ēîx\Õ­;Ēčdã@ĩ=žÕåxä7ø3ŠŒ[X#Ü?oÞýð3Žiw<4d:5ˆg}`üžGžäGAĢÚLã<-wũEŋëį!ĶŲĮø•ÎyūI<•%}ؖFzŠ1 žk‹uûÜ0dO4Úī~•^2õ~TDŨ3ÖCï`\\ÆČ%Ðķŧō&<Ųíė8ĀeŠ_ĄƒT5ZrųjÅŊ?ĸˆ{‡ĢČQKt<@et˜;&F§ØąöiЉ‹1 dm&žGíHā( I \ Zgû ŪbŽJ™r%đ4f˜=1ĸ°°0üōË/öþoMīöZ5=65UŦ―åØķūv8É<ĄĒzÚ_š…‡ŒŠUðMŊĀ} @|tFŸ6˜Zą3ŲΎˆOē!ŧb?ÏĀۋذDÄE&ĖF Uf›…‡OPÉuüüŦ_YijއbŅjBKK7­Ôėz„›ĸđ?VæWŅBƒE­ydöu„'ˆ„ €$Žčū4{ŅUĶXŊ@]‚‹úF―æœÅ*ĘėŦē%Čč•cĶ⃭úŋĸĒvŠŪ iVێZĩjÅzîA]sMÜͰģ^X„oVŅŠÐ”gsŅĻþÍûor­2ucüð+Aô@öo5XŋĻxxō°ë…o†–WtƒŸ†RĖ-%-™‹D”›‡™ -ôīž‚NI<ķЂ&·ĖŸß§yoÆß9‰kQŊâ…ÏÉpU:Įs!·h4xų0Ū­g.Ïv:{aŨFšŠLâÁæbtļsĶïÕĶĸ”šndę6l†šõcņÞ3/cÛÞĖ.™{SāJqžü+h‚ô K@Ŋ‹‡U ĸZĘaœV:,š…ÄãÂ!žŪ (īĘA’aOÐø”ŪŒĨ,'O4îr*;^1g+‘Р ’ÉĘDûczNGœßŋ‘Ŋð|fIâŲ­ÎŒNšJb--uÉÍŧp=ω]žÜ„ā-‹Ë$·<õī>ÍēáĐI4™ú§€ÎĐ|—‚&ëÓĪsU}čH“Ōū7ęu”`XÂéß —6ux“,Qķ4ædÃķōŽÐˆščÂȏÅʇ<܇<îĒKœöĘOÂøģ[ĩüUĻúĖ̜ÞC]Î<]9ŊÅīb0žYg ŪPĘút8ë|ķÕQ€Z6mĖyÅųĖÖģ<Åþä1~ąœu•äEKĶތ;Ö@Į|þáRœ7þIÄÐCžėę/“jN2_AĖWģâ2đŠéÉIÅ~“҉ųšĢ›e œÏ“—Ûž"ĩÔĨAˆ™ LmãRN?ēĮëZ(ÁēiÓfÄYŋĩEåˆĐ+%3a„uÝ>ün•p†ī=SýÝÞŋ§„šūđ™ųČī’ ŸûÂ―BáQ‰˜Ķe˜žø j{6Đ0ÅøÄĒg/<ēęiœÕ鉍thEˆWB<MšŽ`―ĪÕ)ilņ—ýŦu\˜öXĐmj>ÚęSĖ âZƒõ’éÚœ!Hoŧž L€QųŒ \”‹4@•Ķ#‡+‡F}Č.P”āĒļšŌŌÍ1ˆå›V[ČΚŦjþ›ÔoJjŦ’ڐžž9đÚë–\ÏڃĮų'ĐiOŒéģųÜ{ļĪo[:†žƒæß‡ĩÉþø~Ųjôŧ|üņŠ”ÚÉaíVĩĸü^ Tp;҇ÏrûDhŠwþ„UnÐŊ.Ĩl/žœ:ëë^äôäģ•—{ąøÜIāq0†íÁ{%.ĩ8ĐúwaÁ‘74~ÏđsÎ ä #|ķšUGŠ‚ÆŨ‘ĐLëšUЂ GĖt’ž'X–ÎÄõŠT―W4}š^%$ ä]{(™ˆkjïQēbýéë°TEqÃĢĩ• :øøÁžäKÍG݈Đ+Wc{fÎ:­( ýĨP“ĩ>>pšð uHđÅps-{œõ1mA!æz™cúÓĶŽö!ēáb†Z·ÔĀqīI‚†ÞåuӎĻ]鞘§3߉ĒIM[QäēuëV‹ãuÅPõžþ ĪTþ9f •™Åó4‹!é~óÜŌ}‹áïáoZÐÃyšûĩۑą?ã p*|U`A~ųÆŸÞqâ‚wāZįZô„ÂōB<ēåŽ=°’§Ļ;^c1Í4kMŅ@É|lƒ (\á E+%I‡by1+―ŦgõÝ]īėž4IŅFÂĨX}§!•ã°ō’0Ģgå―+į$YþˆĪvŦu ‹ör*đęĐïš§õMĩï„·Ģø„ Įe“ßd6íÎDAĐ?O7ĐÄŠwÂ{{ā‚ģG`rŌũ(fЈÃ[é†įŽÁÜfŦņýϛčāáŽĶ]ûü?öū0‹bûþĪũJBBĄũ. M‘&ˆ(ŠbCštô‰€`}?åŲPi6Ī Ezï ŌëĸœŲløˆ4ý+}øēmvvvvæžđwnAÍšå虅~Ã!<Ðę`Ą1üċØðëF:qBéÐō(]5ž ÂÎc)ˆjw3š$A.YɰhÚåõÃ^ö=7*u{ôE”ýņœĶ]\ï!Q· ‰xnz ŋpFk)Nĸ_-āÂ0s1ĩk 'î,ō+ĩÄ ÝëĢJi)øļĢĮ QXqÜûR.ƒöÓ9P5ņŨ7J‹Gį“&ņ–$­`Îwþ’ÞƔݟĨXŠ|eZpēĖR9ÔÍXsČ^ž[īH›žÂŅĪUâ ?!é”ô;Ū·tyādQG($îâ›ļ&Đ„ wĨu:É/­@TbD%ã]ˆũŲ@)ÕūŒîEž%ÎÕėKĀ }iUFGW0Ä\š•4@!"ŦdƒŦMðv.ŦÁ 7Њ‚‹&uûĖ3Ų‘§ˆ˜V`l&ÅfĪT.ü)ŦËËN%Kž ž€Ā0:ņá:įo›+Ø―$ôS t æ{YƒÍ >íģ#Č­žę.€’ļ•poĩ Û@ §<ŌZæū‰€s›5N‰P-[K―Ķ:qāÐ:o&š8€ĻÚŲÏJ:`9BPœļo^K„Ūē ˜ķčŨpĆģ2Ŋņ—þŅ7U„ 9AÐïbImxõĀi ĄļŽAST3ûųčÐĄ3ūYļˆbWWøøĒ>9ÆKĨPŪY·sX·ķōy1ÚɅkžh֒Šī)ŠjÐNÞJĘ^)[ öRļKÞxSá5ģà [TŊž+>úc-@­ÓŠ[ã7­ÉóŅ ą'óv'Ÿįn.úŽQũüTėÞļą)ų)ƒ†ļģVÎÅ$Ð7vĢ-rę˜#“NēĩÎJ,šAi jÄę―gN Cæ %`ķL Þþu šD7@Óð …WãÓĖpg<@!ž:rOrĄ°af]ƒwÉ7Ŋmž#­XÕ?›ŨÕVŠk*õ1‰ÁmŦÞ_5Ôû0“igqŪjsgŪ[ę™f­”JD [3kqĸ*Gũë9š čđKĩŧĘ·E+ŋkĶ}-’ÞŨŸą]åž]uŧXRÓÏļýcÉ ~ĄåÐýAėöâŧþQ-`‰Û/óJä4]ēðÆkÏböįkîËņPé.Ė6€ķŒv—)ûÖ``ĸ™čųütki<Đ]XĒôōąh֋Čhû8VĄD|T4`ÝÜIx/ģ^Ý ‰ąGáˆ’Q‹^XRņŅõßWN‹#§$bM*oŪ“ôÝĖĪĪ!šÍEemš@VbB…“!ų#.øīūFĘ/nÓÞĖ)PŠpĮÅĮ!‰NT†MPĩÕO )Ē/B/?§ÆÆĘ6‰n)Čóâs.B_%ÍĢ‘pšk—Ūô›/‘CuōlĘöYI+ņõä(žƒÝ qäŊ\ĩúaäŋ‡âūVCÐīû…Ũ?86ëÖŸ!)į$M],§æ*V°` hœå& cŲZÓUŌÂøŦ$ðíYķ…‰—I0W†šŒh_ū%&2kÅæXíĻ5Ë#Ę5íÄēÔæâ4ÍdƒœĨĘQûfÞ+Ž’XZž‡Ķ’ĪĪp=„m|­’€QĀ]œŠ[āzhW۞%“1ųÕ=˜ūė{tvÁþC‰ŒÛ šG™qģŸîųË=p{AĒ‹ŨÛ >,Ëŋ։4ßāXófðrxĻÔķ7D)ĒČÆŦ“Ÿ@VÓG0Žk3:YļxIÅgŊï R\>‰ĢÉÓzģ‰+1Ę<:gļ"Š Iœ-ĒL’Mb˜­Y–@ˆ@aė3IŽ…lŌŪåĶ0‰h-]žTYɉIFóVë[68 (õ“(R a+ĐēA•Bä+,Úėȝhj@tDÍ+X°cĢņÆũ:Ÿ(V! ČnæxÚŲó§đįIąš'EĻŌ~ó*0)ų ˆÏ9Œ#ŧļÎÉāÖЏm_i™~HĖŠĩH™‘ ĪÝQŪVBØēqUc \Ī!ëJŽQí  ˆ"Ôŧrbītųzjã<ÚB“!–ĐēÔNĘ+uĢ6Ļ1âLG *-­yÚbc­_˖JĮŨSJ>›‚CGO˜ ÁõTŊâšüÓZ€Mš‡[õŲ64ŋŧ+Ę'nÅēÕ /Ž? Å/f?oēj`DįķðJįdM <Ņ;IFô“TGɃãoÛÂ71ī{ 4jŅ3>ÝI“2zĪÚü~ØēG6.Á'óūÂĖ)ÏāŅąŸ “ ƒFšdî.þówi+§é%$š>q5Æî’„ߝķŽRŧŨZĨļ)—ö™PSڛÂČlŲxōŸŪ§â,Gœ ‰ĪŅ)Z-r-@IH8ňœ­tFˆ-ēģÅv:VēŪi­ð2‰óeÛGõð ïß*`ŊeMσ…öÄĨēpœÉ)šÂÁES]3?‡Į’gĢg!zü§BœØÉ.Yug-Q­˜vqÚ2đÕS@-îFÕûÛ\wŪđboj§Đí5Į˜IāŊĩdÛ9ŧÚ_Ï2­C›ŠjRbđÝSžRÃņŦ |†@U­/éîsįũ―^Rĩ=1ïFƒð"š„ŨK%‹ëņÏhéœs؛’†+ūĀ[ģ_ÆDšJþÄLœá˜?ūø=<đ( OÏø%č=ƒ.Ý8öĪQ›ÆI~ZZšųĨΧR)ĩūƒGqį‹â….Ĩ0cčؗN'ôG7ã§}'VíFīĄ‹đîw=„Iß 7ŌPéâôũj+ –ÆŲ·ÄÁˆËӏ;å ēÓŅOĒ.,ÛCWvDsō0žÆŲ˜Ž â‚<(ķps‘ĢÖÞ(ū$ņØHôšr.ÅZƒãĖM á˜, ī€ÔÞįΟÁ>eįO·YbemyNČLĶŌGÜbAēŸuâóÅGŦþįø?‡€_43“"õ*š§čąĘ—Ū€ÝD+6ĐÖ)Y>ë͉‚*ns{ôi^E`Æw“\›˜Ĩôöa*ËģÅŊzaĩīÄūŌā3đŪsb#š&5*[Ę[’ (ė˜^AĒ[íˆã5ë›qĩÆĩJGŽ$cÕÚĢÏĸÖņšūéFúzýŋæĢ_ύŋA@IDATß{Ļ di_Ŧš?į°8īē8|bÚā™7ƒßĐŊQũæáXģŧ=ÖʁãûĘaõĖÉørýwČߏĻO߆Įšixâ•o‘H k%ïĀvøāŦŅ8K:ŨuČDīŦC° 1y҃øõH:Â8þ}™ŨÛ'%|ቈRaÆ=Æ|qú{ĩĀ€ÓŌ5–ýC #Cz˙E‹ËgI"o*fVĪ­ÛIl˜žAîMěÄZwˆX›rx] !M\q?))į(ĒM3bJq\vēAT€i‹Eī_yBó#°°ĸ)UXyĖ–îĪ ė…lÐīĘVm.žãîũ&=‹ž é-4áč؊–"ąģ2Zœ5éĄF ÆōäCĄ7ÛČuWj)åÐ8X #ōf›d°-5ŲP{Ņ.' :–(XzTü3ϰZģ­vŨĪF *äÕwx–SųųuWiÚ^‹”””%K (ŠZßX‘í ZYIŊ-[váĢ?…fšĸníĸ‘zÚ― ŽÓSOõÂōĸH9—ŧ'åĖqlŲy€FÛîĻRģJøZfHÖ=y8ŸĸÁ&ĀúÅËÉĀŠŊŋGH6Ļî萜Jmņgā ?ßß/þŽōYJeR“q:A\ĮýF— y›WA,ČËūđÆĨ‹?ę• Æî<.ɐ,đR‹>Â͋ãÍw?ų2ZÄŅ4Ęéū^ô j·đĄŪų(Ųz^Ŧ{ŋH›IÎÎ GĮOE[$ąīōöï&íЈč’^HaF+kŌĐŨ!ũvâ\E# Šļl5‹/^_-pyā6"UŠųuíu9íkNČcīeE„‘ژœkdGą\ė‘ “° <ōĻōx,Â/Q9ØLŠįtėIQŽĀis˜ŽĀĐ{Œą?KXęYę}ŲôIëbϒ'rĩ:ÏʞMK1ŨAXܜLQ˜ã7„Õû"Āiđą;ĸņl€Ŧ=TŽ\V―§ļ―t:8ðōĢý ßßÍō}ĨņJŽWm%īÅĩæ=xŸ@՞`Ðd‘jKģŽÂzëR$R”3Å(Xw€*ōj˜jōĒķ—‚’ÞŌ*K5ĩū›ū™ę$ąėUŊEJHHAÜŲtÔŦ^†^u$E°ū›8ė,úÜýÆ;œ,ä Í žĻpõGÓąŸgãéZāüĢe\îūĪË0rôÓøõŒ7Ũ―ŽĒÓcĸÁØ;lûāäO ðāčxîģŨP7äR\s>xö)4|ąvāLÁŦC ÚØYč^‡NĀcÓVŠJXÞŋ/W­ĸŅkųøyŅ,ú7]†°Pdy”ÄØg_ĶįĮÉČĨš†ĶīũlŨŋ7ū4•"ÚøĮ2žULW4ĄŦđ(ŊŌĻI!7§Ģ˜3ýkÔjÖQÁrƒ„ðR–“ŦdŽ=:TwįX[ôŸYtÂÁx˜KŋD•Q=ČŦHß2xĢåc"ņâŧ3éïڙ.čZÁYkT^ŠŽÅįŊ·ļUĶÖ7ʼnššōV)þĻŌfr@04JC,BŠAJŲrëÁrÝŸŌÜÏ5Nž@Ũˆ}ų>šW?Ã92Ŋ&#:6íÎķŌzĻ€Z"r­qšQ.”ƒY u­ĖQTđĢKĄŨœĐSĶ™ũxĻoš§xáËųßaåŌĨč|GWÜÔü6zâ„ŽÄÉ4Į%ĸdeR“‘ýAn ]=KĀ=ÂWÍfūM–<Đðš'fšóé{OOėœÜļzxQ$~ūč J?831ídĩíųk@*挃ļʏ`ÕóÃ-õŽ;ŋŋύí‹%+Ŧb’™Ų8ÞëļïĖ€Ųė'tНGŧCOãóŲw Š ōüž§1yäĢĻ=l6îŠÎąäPIĮĒþ§ũsp81uÚõCĸŪÕĄˆ[ŅáŽÜĸå'ã1ĪN/ĖzŊ-ÝTđƒ:܄~RKۜ”Æ)ON~Ae"9fDtHé8fÏ'1Nč2f 3ëáƒ'PcÄ4ÜrScļ‘!Ļ}Ï3˜ÂņšnĢžGpƒõČógĸž°óÅï]Ũ-pEāTíEŽ q7Y!e,Ũ$ —ĢÎ$#b/â,ŪG‘9D”-1ĄÕâÂõ6Bũ=üŅ#‡qėØ1ÃĄ œžÚˆ" ĀŌL­ŲĐ<Ã―ņY†ĄdÂ$‘ŒZãôK+æKœ_Ŋ§+Óæ“ī›â:j>ŨU–@ʕAfÝĪjŪ,†šxŒĻt LŊ#LzoeJ&Åș$ĪÜgÅĖãŌHčü\ý‚l'ĩđlŠ€Qœã„[ïęÎ5Ît~éT$ĮE@Čd}äV0áĖiœãûęš:b5Eā;ĻݰÆTk‘Ž“Yã$ŠĨtdūŋƒÖŽÍÚ2+ĒũH™k$ÆzĩĀ_âZŠž'î]y4ļIÓI)œ;·mÃ6F ĶCíšuęáý·ßDýßũð@ėÝ~†o­o{å”ÏhŦ>œŽŅŊÁwņAó.poMũÂ~q[ū˜gĶ`=ų”ĻÖģfMBČÉ1bæDyd`Ņâ%ðŽG'ßŊ>‡ēiøxæX<3ũ'ÔĻZ žĐžčųė,tŠQǰ"y {°ø@2úŽhˆŦVÃ?Šj–ĩú üaúÄą8\ą#F°ðÃóūKqœrjjĸ0gūŲĸ+V§Įā•ÍBŨ&ôŅ9ïÄ ­ĢŦ^§_|ũFŌðq|w―ÂzïØ-  4{ū“'‚ËTD˜ïĨÛÛūĢč6—“ŅÐ mðpLkC4ķÎ;(`ų 2]·a͂1'ĒQ41åīåë7F%Žģ-yƒNftē# ŪPnØé“–ģ7ũ 2ļĩG4/Ó­ņ3[īŽâãë―.œüðâlD93VąWĢ­ÖßD eĮĐäNEžķeh/3ŠMÕ9­jŧ}ÛV4jÜÄ,ŪQ̆)įÔĐSˆ‹‹3îö홄š{ÄĮ#1)Ņ€†‚aK<šKtĖŌ`!mu&PĻ+§ĪŽĩČĀŽ*˒Ĩ˜v‚_ųjDmĀîes1häëüŅWļĢaýŠšâÔŠÏ ßÂ- ,†ýk!ŠûŸ@§Ö°|ũhÜ闆5ūŽcÃĖĸ›ƒąwũƚĢāũLýðgĖøũįôBô+zv{€ą Ó ËŌNڙ8NzēņÚko!2ë0ŽqBŸg_AĸķŅØōÞK˜sŽ:}Ä0cw†;âØģŦŅRģ ĒdÄŋ^Ē8žČ KņÂssþƒf/ ÆSƒŸC[Þĩe|Ï0čú­ũĄÍkKPyėÜŨķēéúlÅÉą\S&óÞy#Ö}„F=îBŋ{š@ĄYOĘĨ6ýĨ§Ī7Ũ+%…ŠŧX…ūXY€Ž.æũJe_ŋ~ZĀ9.^)ŲqŠĻå`Š,ÉPebãfTĒACž fRōP~­ŸiÍGÏ:,K;ŽaÃöíÝËāÕÁ&˜ąXūIuŽðböą€Aψ9z;víbpՃH<— gŠōīÖ)1ŠĪĩĐéå|­ÐóžĮ›õÝ)úL?Š#ņą &ÐXë~*ŨÏ͏ŊĀ™X]äe°Ë“ƒŒ)a-\aNFž>ˆxĩE’ —x‰ĮĪŧ’;>G1sÝm‰ã,üØSŲvYrh@[RiĩšqLPwLj7Û7­žé„‡‡õö}{wÓí#ģóœDīôģęéåi<îœ8~ŒĀšÅLTŒ e‰™ÕÖŌhÎw"ā›ï@N“ #ЧŊ‰8TrUFÉģ90įLåköWíÓq ĮiZ™ø–­…~ĮËOÆķūAƒŽ·ĒzËÛpŠ8•FÛRk råz<Ę@ĀÍoBïöu Ą6ïi·ī3―D}6ę~ž@0'8:‡m䔗 Ÿšwbæ+#đ֔‰ú ;撗‚›w"ąÜ=h^-’|bZDW‚snÜŽ 8ųĢÏĢcŅĸ†Hޜö ú?û:zÝ8ã§―löøüŲq˜ũËblŠŊĀēĮú㭍ûŲõ9ÉĢļįøw0ôæ’HĨ+Į‡žxŧŽōc―ņŊÞŊbįYŲëJJĀ€Å%J!ČÏQŅåā+O3Åé·-₯ãó›Ī#ƒÐŋĸHŨhŠ{ëXÁ$Ą)NÅ-ðgķĀ偓ÎWXœĶĖ`r-ÚÏ1bjnŌwĢC$YR[VÝՈЄ#NTG"Gí'ŸMÆöí[QŧNãbOįÄáÉ.JÄEɈYž€E ZąBETg„q—9L|JÖÄïÁē―›°Ë $dŌ$=y“”X2  3’“Ō°ęāVtðl„t‚…ĩf놩>5°.ņ'ÆčĪĻ’uKKįúŸ+Å= GĨāČįRS”Î#ä–Š zîÜYÄŌæ4)3…ĒZrNÔ>“’N' lēđrN vW&"ŒËn8v‚īMbÔ.3›ïĻsq@uVí;täÚÁ’?ÃUš{ þÚĖ™fbĒvĐ‹qOíK ļGcbBÔ7’(HrÕ§á,%Ķæ7’bóÓ‰kyîZ$ÐöpÍAIJ1ó8ĄĻJ‘}ŋaÃąnųRôxh |ýÝŦáā<$MļØÜý·ĩ ĪH<o%įĢZ‰ÏH›‚ŋ Lyd(ķßô8Þ~ †w―ÚŒüD,Ûɏây•—ÍI–“.Ï@?x'ïĀ‘S p=ĩ Ã‰-’wXY„xž%7i­k–āwĨÁ%ŋþ'SœšĪoÃGûĢMÛĶðcHĶOŋ„ö’ÍZ|ī=Žž<Šë“ļ;ũÐëLmDųQiŦp‚Åšqr“ĨZœ.ŲŌŦðāļ­yk'„–z qĮļīR'q'b9>9F4.‹Sq üI-pyāäC,ðēÖËīv)Ó€ ežbĀ$T"úFӖZįĪžĒû°PýĄČĄŦļ<ĘũíÛĮ5ÎÓðóģŨ‡X$ĐžãOÄ]"M‰í$@Pđ%|ýp_›0Ē՝8œt_îøŋÄmÆÚÃ?â4―ĸäŧ{Á‰ŽÛ“ē œķĢe™ę0ÞKž€j6Óą·Y,•hH˜SHp―‚éėݟ\dÎ&1xu2vŸÞĪ”Tļú3‚Kc8ō―|éMțe–]‰pŠąSôF5x7)RQņȝkĻAÎĻ–W“ÚĄŒ,ÃgJÁJ) Tým@Ô;ęý9―·~ōĪĪ<šįĮõë°wũNcß(ŪRœ―ļN[KWÜū•ČiŠë$Ņ—ŊŽDŌ&vbI DJŠŠģcûð—lDĀ<Ý8˜RXąįüßąGWÆ;ėĘoĶđ 9fæņrĩ­]M%j5ë‰îK0äþŒ^аJmҘþŽ3 Pđ 2*ŸŊ\€·ņŽdŸÅ’Ï>Ā ÝĐĻA;›ˆ‰B°§gļ ~—^hķø <Œq4Ų^4ŸQĀoĮäƒ!··ĮļბØĨ–-ތN§#ÄÍ!ÍÚXYģ#ðÜj4ŧĐ JHfčÉā֎…˜}rūT{wæ$œYu\ËėÐwJ:ģ/PĮ‡ÛūÂYO‚aC•؊ļ:mđ),ÁŽĒw4 jŽĩÉŦĐÓ႓THyø†ŧPŌ+Ðz|ŌO<•ąÔ€ËqŜū“ŅąBŠðėũÎEŊ€nf X/§ũ–ŽVÜ·þ‰ĖĖgLG­•j_ÞFXg üí­ŪéXÉqŦũÔą&$MEŲēåW*:aø@đʓ3 }­“ZŠ><&@æHÔÉfræ7“]­”ĩŒČĘhíRžMP)ī-02ø‹ĸ–ðG)Ú)î݁čę8‘`%ųę”vSī.Į,ų8ēû0ÜSbjŪĘUÕÆ§Lmž°äK|ûí:JœQĄZ4*G ÁėĘRqÃÓ^C™ĨëX†k–Ýąëp"žĒę⍗ę#ÜH?―1dÆÛðĐ7ĸpLó9ķí>öq{7ąé…qE—‰/#âŦ/ą16<ũš4Šđ0‹K˘3.§Ýéƒ3ÞEÏžt>pĩĶūƒ[oŽÆrr0|ÖÛðWęŽG_z•Wl€ŊÏåĘšðņĸSGœĻ–ŒŠˆS'ŅæA nw Ē‚(mČ EŋqÓ°âdŽq§8·ĀŸÕ—N>ED]„͝^mzZØ@šPYäEœ‹S„]Š3Qj_8 UD\œ•ņÔ#ˆ7Č―[EŒR:Úŋwj@ÂEm/ĩŊ—·Ŋː_ëŸrÓ&ąĨ€šž_4F7Á<î˜ķöuøSŊü§―äDí@[š†côĶ4Ā5―čÝcŨđÃXŧán.{ FīéCåŸTœb Īäģø1öGėL8€æ‘ Ð―ę8—pÉjū ~†óÖ>ĸi_ïŽd_ŨÖNö9ûØj'‹{. žö}ښoĀ›8€]Û·^`ęnHȝ_ĩîâþÏkÚęÛHđIÉrč.%h|C}GËÆS{!Wenú þĒ}ë:Xš|ū]ąL įI‚ŒÐhݰÂÂÏkē^é"ŪūeÐą[™ ŪÔ/°dņ .‹.=Ę^‹Ūlí– īcŦš B݆ÖÉ3[0xĀ‹ˆŽRĮwŊÄáZýКjXá―įw<Ņ Ëhpþą{ÎÞĻŨāJĀïŠĘo0ũ5ĩ6eļĒB―‚úðŒoɊļ―GÅ Ë/>:ß\šĐPŊ#įO™=WoTĐŨŒþaQš%íæâTÜN \8Åíī‘ËŠ8K[–qKĮkƑņxģNâķ ˆyڀ”ļ0ÃuT<\=ČąŠ˜ `]q”AŦũïßo€ÓC―ÚÅöísÚ:æŅģen"1ĻDŽi4ņ͘ķ1ÁÎ'ĸ]r†gą`ý"Ô 2Üē?ÅžRšĐæ_“*ĖÄ/ū‡ÐĄ\mÆÎÆŅ„ã\Ó<‡Ģ‰G°,n5œé*ëĄú―đF‘.AÓNV}HéUn$Ņ“B‡ zš^,ulŨßÞwĖŊē‹žĀjrð3ÅīgAƉ §sú6z_•įN[@‰ÁJŒĀšįļš&ÎWmĢļ›Š§>ŒÎKëŨ,LSŲ™ÄG—þęĪįVŊ^ UŦV0õ―ØóäČA‰ĄŊy ތņÏ ÃÎýąpoŨģ›ÕĢͧŋq HŽ8·ĀŸŲWNq"$ÐâFšó‰  ĻDÔHIƒđ^FP-ėšŌĮЉ·”VōI”Å…I!EڄU‰]ĐPD>g(ŪÝAqmíÚĩÆĻýr"°EšĶį+ŲŨīŊs牔FŠÖ™tØĪL#4(Y+PĖUÎ?ïþ //Á7ßC E?#Ū T)…*!QH&(L<…Fj9zę0>>ø ÅūGqcŲ†hÝ ĐÉį—ô\G€S}“ŽmāēójŦš:ž·ïąïW™ĘcƒĐ}ïîÝŧągÏn#–ų į"…Ķ>ZW66œlgĢ]Ë5XqšúFō'lM(tŽc™™5NÖŅ8ŠįZŸœ2Hž|-’ÞU“&Ģŧs-ø{žĄāĮ B]ýũÜTœũïÜęĒsŌÄ·é˜Î‰O8óžĖā“™ÜZváyĮ<ÅûV Hq‹Ttĸۇ> oĮm{!ēDUþӑLŪõŨ›ņúžw–ÁĘŨ+Žĩo7Āj=Ûæíz™Jüąëĩ[ÝV4Ŋ}N"Ų={ö`Mwôîz^'Rô1ßEã™āéLÐd~#ŲŲZļúF2žŨšēB’)iĒ Î,-_ģ>Ksm=CßņzIēŸôø Øē}/Ö/ž oËâõRŋâzüsZ@Q„âîĮīiӐåí‰ģ4krĒŽD`€+Ë5B­?āuŸ›Ðö†Šp§™“’LęÖŋĸÞÉh†YĢz0ä uþÏn[CĸėrĸPyŽKBÜ1äP/$4þÄ WL4Ïc[m|įüŸsGžóX[,˜ųÜï|u[žÂ”É♘Æ(Uƒ“{‹ŧb‘ŨI†Ë§ˆ4đ)ũHī'b-‘†•Ē"žš€H$›EÍV‰ú4+į"e n QW) Pü֊#5Zž$ėâzRÉímÚð ÝTĩDIF5°E™6(Ų€Ą6Ó9ûįxŽ}‰iŧēitSÔ)UÛhóVŦ„jÁ°:é'rntׇ!>t ‡ŋ˜W˜kÚgÎ&"–>ms8‰ôÎôĶ›―dãßöÆ―X'âNāēëĶį].Ų@Ļ ctQDŨųN|'Ðä΋ôžl>6 zŒĮėÎUXūX#%Iŧ,džžĮr-eyšjĨč*îĮƒÎĨMnŌ-\Ð( ît’ï"ïYôÎdø›üđð ­4_HÆôâ$―ø1 €Ū†š™Ė+{F%Ãåðj=ÍĖDÔüŊĩ5Ŋ:ƒŧ:˜e?ș?žD―šŪt2ö<`€Ó‘Ŧ4(øc 2ŽųtN€#ð0xäHī*ÛÅ|ôŋņ>ŽØū’n)>fYįôĄ).ĶzlÚđņ•›"žZīq§ã‘•š‡&^q(—þm=ÓÐĐV$Ðký,mÁÓ9ŧNŽĀĻjÛĮv>ģëęļĩũíëÚ*imsŨŽmlkMVÔVė”äâ­:°|3áŧsHŠ' 4ÕîšØč„| šfšJâwóM(N—D@Ē`_‹Ī°b_ĸpÁeKāĶ–1ŽŊĐ6MI%ŋŌsÏž)Ÿ1W8ÜŨËô“Ŧ­Ũ9ÚÛþšãōiNTkæ%ŪÂeiVZNI%qa{Ņö60$Œvž†Š>6+)ŧķQû›ëõåŠÔBŲ’þ…ŨŠwŪ‡ Ķ3”x†“ó ĒBé}Ė‹ZæWJ—^Þ(_‘ŌŪėÓTR D^éh”-mnÝOiN&ãÃ>ņøbŽøv Ú>ø^xŽ#ŽmúKēęĄAō8ēę<þÔ Äēĩžkëwžói5@bĢņxöä^L{úQ|ŧ).~Ĩ1tĘLt‹IÄÓcÆaéöSīŦ+‹Á/ÃÝmāŨųS1{=m·oĀö{qëāĮáđú|ši'ŠÜõÞzĒö˟‚™km…yv먅F}&bęðîðÍ<ũg=ƒđ_mĶ[Ëtīë7 cčˆÜckņėčŨáZ> +–í‚Wƒūø`Ú`ú΍Į;SžÁžķálXMž8y2n.—„ûN@Bī/vŪZƒ―7âí·§!&~1Þ\ķąbüއ1rčýrӔ< ïL€ė6S0īcÞ=küÛãõ§zbëWÓ0{geˆ›4\ ŊDģžlÖ,ÅÓukA] jkßZÜĖʘO"[•Ŧ”'Ĩ"Ę%úHečŊ͛6j‰*ýXčgkkÛ[SŌZ§:Ŧž§kâ@ëFŨGõðęœ4RŒIo5é ?æÁjđ{äcû–_ðóÞíä(OâD|Đ ŒŠn5ð@ƒÞ4^6œŦ]ūžaïkkĸt^É>vÜ=o_nŦwÍįÆ?Sņ)Ųp‡|ųLM6 ĮÎo`ĒžH㙝Pâ9@°EęzMJŒÂŌâJ•Ū ęn$ mk:ģ*õ&;ŽXđjeāJ?ŪΜĄģũÐũ°'gÕđxíõw(ZÎEģFm(rŋÚ°byØųó—ļũö^5áYŒ~ļô8ķŸ’―+}'œ4DõbŊĩõ‡·qįwaÔč‘ü­o·b)žÏ›―+?ÄðAcņüSÃŅýîøbë™ó—‹ũۃ`XąÅŊážûú`ÔĻáōø3\rĄOĮŦLWšükÜŽË,AICĀįå„M+ŨÃģÁ@ĖÛ‹gĸ›N4øáčl:OÎ-ßÍx ËÃnÁ‡ó`Pũķpɕî†Ήk~:æÏ‡OVÃôy_āÓ·§āĶrĀûã†aŲŲēølŅBž4Š<ĶĒwŦ$Öãô>|ņÞhÝkþõDWL3y­û`öīá8ēā=ĢËČĖø―XôËÐážņxûåąX5ũ,Ų’€}Ëæ`ÜûĮ1ųÃøhÖ8Öõ|ē’š#Ūøåŧ%8z;^{åylš‡OdaÏwŊaę’žņÕ ,žĸ9ô@vpÃ2ŽÞã†qģßGįėøũÚý(ISžÛk•EįQÏbø#]éÆZķƒK ‚RņéįK‘•ēsßø7–Îĸ 'É ŊøôßðŊZ‡6`ÓŅÓĶ=Ô*ĸ„tYāT‡ũ"P'‰īÄŦ<ɎFn’Äp8ŋŠã‘TS a‰cE”ÅąJ<Ŧu7õ&‹˜s“Įę[ĪëPōKãý{vÓû™v^„tŽý‹muN`#'ęü<™Wœ”ývĻݖŅS(*ĄžSýŅĶ2’J0ĖM>ˆįž‡öãô™ViQxzđĢó ·ēr‚Ą>š]ŸKí[íu>Ÿ}ŽüJvýírėëŽį•oßū}Øđu›™ž(ÞɀÛLíŽIˆ&-úNžlLƒ49Ō•’Þ=‡ ))€íkõí4ąŅHĒ\ĢøĨIÚŽĀz-’”šė°bO3bÉĪį^ÆéätãáÓųßbÕŌÐŪSīïØ)&ŽØ•kuîð:<~įÃðn6ß}û –.úĄ?ž‹þ}ßDßũûą#1hþ&+BÎÅåâDR&|ŦݍI“žÅΧFĄEå€ rč ŠE/žO—€ß,ý­KėÂÜÏvü&Oņ‰ĸf d›°bõÚÄĪgŸÃÄqCßôÏq‘•šƒ―‡ã…>­PĐbuŠ?S‘*“PŽ?ŠMģčŋú́}ŅxŨ'ļûŪ~Xļ-ŽŽbHV95f3ΝÄO 7 ÏÝĢyĨDFWDxp:ÖÐJwč‹ēAļáÖ{Q+0Ŧ·Å#ß-õï‹~ݚÓĐ c6yĢę†jĒQŽt—ÅRw$ n‰ûŧ5@Ģ–pWÃR 4p?-ýĩ;<„fŅ!ˆĐ3zŨ,-›öPÂÆč,ĩ`ė{Q§JE”+Įņ-Ë éČ6 ļëNžđ”"\šî"O\‘:ašTŦJN܇žÐxŌŧÂĘ{!8Ē J‘+'91ô'?ύÚÜ ß}K°ā?ŊĢô}―ŅīFūZ<ßïŊ‡þíĘSbI[dUü”. œúðâl\hFb&ÍJŒx–@GÐᖮ^(sã•Yž‚$C”§!dqHŲž..ÕŽÓņXDÛČþyģˆđÚ5–{wïÞepHŠöOí^\ėkÚÚŨž6pęœĖ^—Ŋ?ú(Íå:…ļÎŽ Îîø õÂÁØM˜―îcŽ?ž G’ŽáØŲ㈈,‰`Æĩ9n•c?ŦčūŽU/ûžãÖqßņ~ĮóŽûĘĢēR(>]·v ý˜ža'e;ē€đs›âhqôlCÞgė2Ų’nŪZGh]mb­#[ÚÎĖkʗFģÄØ–‰‘žiDėĶĪŋö„Ę9‘§Ûũė܁ĸžõ&>ĸr6oۋũßy‹~FÃpïCšŦ5ūāUĪÍŦŋÄš€xæÉtÜï ŸŌ51éÍņH:ø–ĸēoŊXŠŊŸÃ&"ÖbB KՄ.+Ũ~ô?[ŦZu„ųQ„Ar‚_`8"üč'øôq~‡\T*õ[p―ā–âƒkÞųĪ'ŲyŪˆ(ZUŦ"€Ë2,‰š9$vA_ö)’ ކ&Ė#a]WÎ\2Ĩ߆Ũ.ÄvóäËØt*‹īP=L†g"Ŧ—ÄŊûķ‚.ŒđžGÎ6Ûôíáqú„Î õäaŠsFĐpāĖޙÏPĘTÓųŅķP\Ûēe+c“iŊŧŲ€ãļUŲŽ`e_Óy§üßęYz‡LÖŋ~ųšĻYš*ÖïÝH 'ãuōÓņ81 wdäĮá—ģąŽ{žY·‡Ęũ0ānqs#…eÛĮÚ*™ũ.r^įŊ;Þc_sÜŠĮÁ›ÓžÃŠÄVXņf­kŅTÅÏļęPXą˜ũýûyÏ<4čq'úũî JYwʖŊd‡ [.—zŌé͌ŽĢt‚Čeđy4aËd@vNĪÍ~k –N4ŸŠRĄƒDh’ËGóįä„NũũĮûÃĶĢoę6”ĄbÎ-ũ @ß'zbðÓOãņŒĸË7šĨ Z”õÅfŽ‹ôũ€N,$‘*T)ý)g˜zļ1Ёå3ðä짐ēoŽ•î€võÂ8ųŧ! 'að˜<ø'nÅZ—†˜Ýūý-ÃČO–Ų hH-ôžõŧ=Œ˜OáŲ‰SQ5Â0hP 8ņšĐ?›M‘kĶ; ŸQ1ҘōܓøĻú$tn\ nô'šåÅøĒujW‡ëĻUĐ*œ’kryĝZÔáī]Š$ôęÆžJų\J“ä‹ō3Ōi^ģdŪþ}þļŒ3æéKUWžu/\HWoûāEM05’Y#įh…ģBÆĀ"ØFËNe–ā!­M€īą, üFäČÞ`š(‰ŧ”VˆĀ†€§Ð–2š|ETŽXŅÏh:ƒZs ĐFāéÛҁķĐĮĮndÅ:čÔĪ*|9þÂËUb„§ōĻR· ÏĮĀĘMÚcH_*ÛļįĀŋTu4Ū_ĄAÔf ŠD“&uJ/g^AĻMũŒåJ‡ãāÆOąÅĐ+j… Čúų(EūÎð Ј[Z4"ᐁ’QÂōûĩō܂P‡ũFq’čĘ@1ĩ› &Š‚Ã*ĒëÍÍ›˜öïĄĻR­ĒX~ÕšQđBŪpFDåĻ[ĩ<9x7”ŦÓĩŠ DhiD– &ôY­˜G‰Xiš•ÜÖģ5*s…ķíî@óz R―ē5Ņ V ”`xžčęuYF ü)ÅĐSŊ*—+AŽ}ýíųsÏßY) Āß0Iô EįÖŋ-‡ï$­&2âU’bģ^FÂlĀ‚\ĪÅň˜ëv‹ø}RRiƒŠŧˆjUSâ\ƒæšÏ•/#.OũJ$Ž5ŧMTˆiذ!üýýÍ―6 ˜ŌyŊ}lou^b_ŨVœŪQĮ.ȟ’–ŠvėŽ3ļž™p.ÎIÝÏ·Ō$LÕ63!7Ɂ·ögikPy,RĐÖslNØ:cýÕóó™ķ)ļŨzŽ•ïRįuUŨTÎNÚīnŲžÉj'žģxÄYRÔL.Zeėc9á0šķzGÞ'ĩn‰tôUĪę]2JV ŽŦo)ÜÐĶ4đUŌRĒB]jðrđŽũ”ŦK`įÄ_LlƒzA|oé`Ļ þ^éōĀÉ’Ųˆ…bBŠ5]Id9GÜ#Ŋ‹{īX›õO6ˆˆŧÖŨÄՐ.E éĻIIH Đ5PãŽŨÕp°Xķ‰i ïīcÛ6ÄĮĮsVČk„XJÚ:‚Ī}ÞūfįéY†æūžþ”/˜’å°þĖ)FIŅ{˜ ^HtËš4ŪVÕ)Ō•G$•eĘŨū’ã3Í þąŊŲĮŽyËÐuûØÞÚũč―ĪIŧᗟ‘DûMqâZ[Vû+Ē‹YÏdÄŠ| `™šˆsðkĒo"YÓđŊ*Ŧ\qŽzžöĨņŽc‰|]Ipô tíZ$õ Ŋ"aÅ:tŋ =ïđ §MČSÂŧōüްbåVlúĀ_Ļüð0tn ·Ä―xå­_1öãEĻFĶã\X6rfVz1XĄ„bÃâ9Xž-ęĸëą1ī^íV ™€—†ö@|ëxïŅÆØðåŦøtKÂ<ņÕŋĒĮˆ§ŠAóZt–Ŧ}†ÂŠ}9ËũeãôžUØÝ“ Š―8ôũ„#]#Ø8& IIūž33%ŪâūÃyuÖ\šČŌ‚TŅdĀēČIYŧLŌó oã87KdžRũķ ßÎËx<+Š:nqAŌ2Láó9Î ũ™KïĮ(ĪlŌ>;éÝėĪúdPóýb)z MÃBŲNųōH{í6XÛ)+Ë>kŸųûl/œœSˆ@[ĄÖÄÎKðÉ+„>Š~š&ïŌâÔ=–Ļå„\û.ä<Õz3sLÎS劐óvī8O‰p:þ”1MĐ@q­Ū+ĐLýėcĮsö5m•L™ė ZīÅĩæ^‚þÎaÍÏËđö ũlÆÍdļ0g:fį]ČJËБ)Ö 0Ę9v9Ž RėĖųĮņžžĨũīïÓą}ÝÞÚũ9nõ<9žßōëFžfg§öŊQĻbÛiÝÖžÛM)“ģ9}MFØ2…M^ôÞÁęŧHK\eŪūóó&%ĸĐmÔdŲRÐGļIaÅÂ}óļg;Å6įÊĨē ÞÔËņ`@ðÃŧ„Ŧzu5r @ũģQķÞ·Øu$)ng蕇%Sû#?v é9ŸFü€lŸĀ ÅŦlÐē•.eïAߛoA4cPķ…ūO―‚Ž`ëųŅõZĒaævĀ—ÁÓsûĢe“ŠWWŊâ\ŨĶ8ŪÃō&<áĒnyÃnn‹2&ŽXI xęe, ÄQÅ­û$Mo}BƠǘ—ŪøŊ|-ģėFZ&ō—&ŽEgŠuE·þé Āi‚Ed%·—ØO/&ŪGÜãyņŽ>ō߆ļKņ‡tXģ7#R$ čž|JXÄß Ī,߈ y]ņ$čØ!6mÚĀ5·ķ )ŧAU†’ Ē:ūØO\•ĘS>ýī–wCĩĶhÚ ŧöí7œš3Áž M$xĄœZ6jÎYž%îīAÎõ\ûž―Ŋ­ĘWûĻŽŨíc;ŊŽ“cŲZÛ\ŋná6%f•øÕĨ@Ŧ{Ė;ēý%JÔ­s6HÎS/S0)”v­ÄžšĻ°Œô@m..UeKđHĒ]uV)n]‹Ī°bļ>ļtų/ø~åruĨ ’ÆghpÚ4džkx‘ĩÆ r=pFÃVíaãę}Ï}˜ĸņgHĘņ`ð–ŨþöĒ7ðØ Q5ÚĒO"—h@S§iáÉ0*|ÜÉ_qšN[€klåëīįŊHý\ūRčĖäî?ÏEÉõ·;įwMĶįäx*Lz!ȗK=Óī,DęxI wâõäk°jŧ n騀tŊĸtUĀ)"­…rqˆJ…âO]yĶŅąLND A'‰ b,ÂÏ{õą.Ękė? 2J:—)Ņ.oæcļ•™E.:ˆ_ým žFĢ”y p€ĪîĩAčRį•GĀĪgØãM/!íšß„­;vPŨD@“TģFM pc)th—킎 wđ}Ýk_WėäXfŅsâ8€m|g­ŊŠÎÉßēIþLÛQŽjŋķZË03~Ÿ| ūCŠúyxš[Ē]ķŧļRÓœĩvjÛuú+·znĩj1ĻRĨüEÛHÏVë-iÃЏî<æÜZ|Ï?ŽrþŽ‹hĸõo Đaūx~Ýô,žŧ-\/5IĶE5ó―4NJnĨ&Ä"™&8á!2ãúm^kōžy˜ôbZvhDe%1hŨwš"eaķ íĩ/NÎr•gqr–Ų_ī€ā‰Rī y’ý&3ŪHĨ@Se‰Á(’C'ĄīÄŧ F-ð,ķoÝēÅh˜ŽĐüAã<ĩ í…[åĩÅĩö}Zģm\·<=čϘÏ6bLÖ'ĪD(ėyOĄHTeÛIûWú)ï•ōØe:æģï“ĮĢUŦVāČŅ#…ĀĄķRR7’r8xq‹zų ķ':6]UVŲ–‡ ­-[Ž ŽĩRq–Öą€Ôú†ŌžĩĖQô y·Šó—'ŧŽÖĪKýāŸqÏČWœŠ[āZĩ€IŌÆRRŸ4.úÏg’ˆåįd˜ó)ôŧ* 6KVĪS&/ĨˆČį‹–īŽ l°Ö>§ēœ§Óĩ%}ȊENtNï§Q—ÃgМó‰Ō Œ4óŒÂž·â](cB*ë)") )ilk\gqŌ/ó,é/Ļ\û|&Ÿ--™Ék&jįlÍþļÕD5!Ëŋ–,ËT ŲŌ2DÏŲėô"RØRRn•­-ŒRĩÎęę?%&ŸÅþCGÍÄæŋ_›âü“[@OƁ ˜Üï!tč|:ÜŲï.ÚÆ Þĩ~!îÕ í;uĶïŨĐØ™ƒSŧ—Ēïméአ:'i>õÍ+Ã0{ųaŌ9wl^ô†ÎZaœđo]<ý?p?Z7o‚G_]€ãÛĒã#O`;Md$UÚ>&ð)ry/‡+i­;Î\ƒ§í†.]:ĒË=ƒ°)Žv+‰{0nü8<>îiīi{3nę1ßėM€§g.>Ÿø†ˆ!”Ð5ly^ýd/ä™óÃyũķėƒŦâpæ—EčwGwÜv{GÜþČXŽÞ‘gšĘ›ÐĢĶ~ēžæmXõÖó4ücdÐŨïÖ>ÆŠý ôtÓû>‚'& Į―]Ûâ†ûÆãĢĸ›Þ=ÚĒqÃ~XīĸmZĨs§ĒíĄÛĩꀉoüˆ,†f|wų·X=ë íĸœËĮ‰•ĸÁC}nAįö]ðėß “tMīGčSÛŋĀ€ĄOaïÚČRJķþ̉=}íî-šu―ôÃ+ŨFHƗņ‘u\§,äB 8lp)ŽhjĶ"Į"ÖŌÕbķ8&‰ ģ9ÃčR…ԀމÔÁGÐ`­ÍşŠ7\§ĘļØ@sĨ­ŨÎ#@x `”ôĖzjßâ&ÚĶz™ú†‡„ĒkŧNÆŲđ}ŸÉ\ð§č9+]ęž}ÍÞ:æŨ9%ûœę—ÎYŦ4iOÆÆsŧÚÚüc›{Ø‚Ų Dšnž3é{°M gJóu<šŽž5Ó5=R3žŠz) Y3U‰”uŲgŦfĸýŋé4Ā4öÜÖg ĀŊÞįčŋæÅ5øÛĩĮ“ŧs:持oTÄĮ_-ÂÔ‘xaÐlÞ·ž}ĨĀĮ_|žîƒĻ܃xĶĸ`Ī5žŸ~õͰJļf#öāėĪíēfĄįâwâāždCûŌâöcņ‡ËŅþž§ðÎôqX=w֞ GÔÉŊņî’]\˜ĒŊØÞA`ŧęð%į§ņČÁ 'Ïôzü|ŋä?;ģ‹Ũ*Ž`.ķ―›ö—dô yháģOžô!Ļŀ„C[ðËî“č=wĶt‰ĀĖOâ0M^y~ýÎÓčĸÞ4tĻ#ï—ÚCąxŅįč° C})ĩq_ŋÚX8ĩfĖ™Bļ č4°}vįā螟ąë KóĀÆØ„įߚ‰Š›æbüŋwbÔŋÞG;ũÍxmÁŊĖã„č:·āŦï—cƈøü•ũïW·ÕŪDįũaÂī‰(ãž#MB`ũ70ĸÝaø~ö$Ž‹•ÃŌ9ōŅUqôįoņŲOĻxrs^_MĮ á!ݍë(]~“u{.QŸŪ­Y寘42ݐYŠ^G?Ģ„ÂNãF[Ãû^ģs‘?*ĢhģïužĨčyĮ<š&qõĄC‡°~íjū?…m({Lq€ōūĪЊ:$ÍwũÏÁCbp}ķŸ&"R’Vēŋ—Ū+Î Ė‚ôÍtM œÍëZƒķĖ‹ĪÁfq­z–&4æ!Ž•ĸ‹öNÂĘ5GG'ėVkŸūŨ/ۖáĢŋĀĀþũšüųŦÅ{Å-ð'·Įēč%Ų=ĮݍrôÃEÅŦņ@IDATÝãa”z·7V­Y…ŧ|1î_ũ <q䅇!yũbüīŨcg߇č0_Ōē ŌižÓwmqwuņå9kĒžÍ()õŧŒĀÝĻ\–^―ÎÅą„HŒļĸf<‚­ĮšŒeÝ>üitlPibk`už5ķ#čBōÖ>―ðŊĄï –f`õšCŋ›ÉųŽÁĢï­ĶĸÛŌ—sf‚müĖ’Ę .…þOL@˜“‰Ä=CÆ UhlĻâ†Ý§Î’Æ0fgüVŒî9žŲŧ‘Eš“†móa$•0”ĢÓŽ?âg†l ™3 ―Ý2‘”Í(VY―˧vŋ[h-úé-‹i3ŋBŧ,?õ­q­Ļ―gčӟüíĸ?Šŧ<Įi;ĩgIŒe+.E„ÞČšJqHüg6ó HõOļKĨ8 ^bGēĒM‰EĖ"Þ9”ũŦÏI)H +nIœ§ÖÝ؏/ŋ˜ÍTš9wîœá%šĩÁÐqŦ}ĮcŧmtN\gZNÖėį,Ķtivū*ĻXŪÂIčA>r‚n5žšÄÎB-ęāÜãØīíđ!-v+’Ï8!’īÁ;$köėãĻĢ~AĖŲÝAžgąį4^sÉȧl6Ũ3HÓōÏÃڍk‘G“+%'`jÚQr…ÄįsņØļį8žýÝQ›ņ<Ģ~‡ŽũžŽV} Øūš$+1^§{&Þ3 Ÿy4ÁœY3q#ÝI2TĮ―–^N!öTēÉđaãNdŅ―))ó1ɜ?ģg'é\%T(IįĒryĮ]=á™īŋėMäN‘ÍË„ÝP&ï]†ĨßæV |IŪv2? ÐÁN2iËcŅŅ<cJ&ČOeÓŪôØw2á<ôütŒëޙöŌ|Ių2qōč9ãŠÐŲÍ ĄyîčŌĸ%|ōÅ"Ž[ĸ%n,M $iūRFķ3:ũũÝŊâūqoĢŅh{ïičĒÉpüđĮÉÎB.ÅØō̉óTpdwy"Ęņ:Ž/#E #6Ī!Ž>Ē\ÁQ,óˆÐëz&‡ %Ią3øÔīXŽ–‡æ%Ā xóĐ]•šrŸĖûŦWŪ KĻęhÕš ŠVŦ† Ā x{{óynO `ĸė6ķ9D‰2ņŊÕ/ĢFd tlŲ!% xĨ$Þ {‹r‰*Į>goíēíkÚ―Ũ~ŪŨŪ—}^ĮP“vëVà 0Y{6‹ÃW{Wsl™ōļ“+7NóÉmJŠîÁ°\Rēė4-.Ý|3~MPÜÜ<Ė73 IÎÕŌŒæ„…ÕŽïrĒĒýkÅq*ŽXüđ ÔŊZšDHý@^žâė’˚ó6myVŽqkūÛïsĶūïëĐxpÚ<üŌŧčSĸw˜ēdŸÄóÏLÄóôä^ÖrKhģKmũ3$ÓSßUÅ'ģï`Ï=Ÿ’ŪÅøgŋ@ÎméQôZĨ$ĖĀ‹ÛLÍ)gÏŅíYLŸû>*‘ĻýóS>~bXąqģ–!ž$3x”ÄØg_F“rWņ4vÜCŅ·Į]öÞtŒNûqĄB―>hÛĪ9NũĶûÍįF#cKsäļ”ÃāÝ1zl[Œš2 g74§2WÜ;pÂ#ĒðüėįáīÚ +ÖĨ ā nÜĀ +gaÜĪ3HßOŋēŒüÓ n~%ÐýŽšX4'nŠo\đS–Ä$ÖÃå"qú‡ðęŽxėMOÁÎ%1ŽWOļĮmÆÛŊŽÂîïËaþ·Đč5š#Ÿ@r”:MüŨģtĸ þ°ĩ:C%Z0Č7,éĸĀ5œŅJnđģ&<Š„+`õ7ŦŅ―Įd„ĨïÃčŅ#āŲi*6ŒŽ‚>\2)NîOŸžœ€ ÔX/–eM°I›åϖejį’ãÎ ĀÍĨÜą`îË(ÉwÍ8Ï?ڂö ëcgŊxū†7ũ邱Û`ʔ1Č9ÔųTššuā$DÐkSŅ]Ž%<Ë5CŸĨ1rģ3M›Á]ÎXüõ”.œŽĐæ2r‹'Ū%ƒĀ'ÛAĄķ2…čO^mDx”Ę/­HšzSæÓښL,Ô%˜Ú€ķ‚J6åé*ÃՅ"F•Ãû<(ö•HW&ûũîálú0Ö@˔BãšĄnÝzĻWŊžžÔuŽt1`@‰ó• (į;L6e§ËŧđEkvkķc—Ą­ ‚öūŽí}ģSäãs•W`Īäx^Įš8ØŨÔF+—/ĮÁ}{ÍĪÄâNyu5““.XĮž?*2‰›g‡ČŠ+<›ō $õ\•ĐvķüÔŌ\…^;Īh寉Œ"Hč›ÉtEëĄ*Ũ؊ē\qøæÃĻ’a’—";ŽØ “_4}Ĩoĸtĩį/>ĸŦXFŸ—ÝÐĶémqtúš―tÕņúŒ°zÅ>øžûzŨ3qŅ ‰š)ųČHA‰œ7•ÔėBčq&™aZÜĐéįéS^”ŽðwŌôËÏNįŽÚ…ëāRķ·$Œ„_ XË4%ô+͜Ė:H`_wõåĪąJáyûQüČ WŽlzũôfĻ&‰ß™,ΜiÓų†ûą4Í$‚ÏsĒ_QO*hĪÓ]d>EÞŽ‹<Ã8ņÛŲ`O[ę<ÐĐ'Q'™!ôãôã§ïÂwžQú*p̰~ëlúGf8ŪNƒ0 kuà EýްbŲäN?ō>lØ?[ƒihŅē!cNýG―ˆĶMVbë‰Dx–ŽŒ ī{ø%”Ļš‚Q•ÎĀþd#éĮ6fôŋ0gŲZdWĀða!9‹q|)IcHj”k>čōîdÅZÞĶ-ĘúéÛs”šųáæ{Z ~e:õc_ī5ZģœŅiôsža-’Œ{/vĸĄ“ô܀ŽČ–ļģs'†4ËÂKÓïCÓš•x[:Ÿ—ƒÞäÎTFƒFŊāĶV óÜ―_E~H9Ž}ę”ÐßÕ}ÓĶĢÆĘĨØu’z“{ĢÍ U‘Ÿt݆―‰Ztĩ—G&fƛaÓÁrĢNļmĖëH.Mðt˜9o"ˆþĢģōÂ0öíũāV–įŲį{>öZŧ•‡K‰Ėzk~ڏčïCũAą8Įøžn?­‡ä\?Nö=Ņ}úLÄŽýÛĶĀÓ/ežĐwRï!žņ<•H9iÎËõ€WŒęį߅6ŽC\îu–.œ8ž$Ðņ&`rĀëŸÄģâ@EĀE˜s(Vĩ€P6ōM+WqŌâd^KuwŽō4Eč™ÁÍbĀšŪûD8LbÛĶŅËÛĒįRÏŌ%ßVėÛģËūĸ•*WAģæÍQŊ~}Fo5þi689ķ·@L[ĨÐĘHIĢ:69œM››860*ŋöÏq_Ũícm•”_?=ÓūŨņžō ÏĐmÖŪ]‹ ~6íĄwgIæýđąÚŽí E ģöÉŦjģfiDŽÖó$BŨ7Ō·P^%N>Ԇfm™UÔhÕĮŦCÖÃĩĀīÅæhÅá_Ž―Laōķ,ëa…Û·{7ķŽ_á‘ĻU§ū +V2"܄Ûģí4Ûō·šKUįÜÖåXzžF i„yK>ÆŪļûQ=Ė?Í{ ŪßCâ°Ë·ø ÏģŊadįęČ9sSÆÜ‹ų;ØB"püd&đ{áÄOaōž58sb/JVïĐ#nÆīņ#ņņz†;W }ŸŽŧހ[ūÃ;[+ĒIãH$ŌÉüˆ§ÞĪĘĻĒvŪō›jÆnšĮ&ū„ƒņ™ŠÖŊ͘„2^i˜CneWnotÃĻ·žÆá/Æcģk4ķ.ĸįēb0čáĶøŋũ?āšXfÎûéß>‰•ž}ð|ŋæČOÅħFㆇfâ–ęMŧĶoų cKāĨ—Á[3‡ĸ‘$sLq_†‡‹°]ý_íËsýŸ QŪÞÍĻP_ũð’f)„_Ū“'jĩé„Ú<ëÄɏėÓūŽa‹vhÄ1ŊÉŦŅxũŠ@—îwņXY3’é–TŠ’.ĘēhÝî6“WKUī AúĐ}Xđ0ÝĶގ Ũk1iúvŒ~~hî‡Ol#!ތųoū‰8Ęõb7ÏĮŋ—æĄZŲ@SrÏbĘãŊĢ|ŧ§ÐŠ3õĸäÆh!8šéĸ0šũxyî|ĪĸîþĖ Ö+‰`™īƒ, wĨqhÎQÉ~Ë!ÅonM“—cTįΟaX9žd{).Ŋö­ã1þņFÆŊlË1KPlūaxôݙčÖ Ō€IŅoĨōô\ŅO݋€úxæå‘ĻR†šõšĶ1ÏēúũŽžˆŧnŽlōĐþķ"§Ę[ i=Á€ïĩęŠû­ũŅąy7ĸĩ|Ū.gðÖKģžÖŧōmÍūmŦšKЖÞWįåWe‰ŪŠîÆlŽíeęËöS™jŋlŠ€M žkޗkÅš–㉗ĶĖD!!ÍtÓõ•.Ïq’t‰ÓÔGčTÚRöŅš%đžŨG%ý6"VimÚb\K…‡.-N% Ä„øßīcŠ%/gŦðXúšjx}dcÚÂōšŠƒ'ۉSŌ5š"{œ9} ņqqŒ(ēŸ}üÔmÐ 6Ē(·.ʗ/_ð\Ŧî(•Ąa€ÜíÏcƒĒķŲ€fÞΰL] ËZ`œHįėúéXį, ēÎĸ?öÎ@‹"ëÚg"aČ9g$#ŠTĖ‚Y1g‚9Į]Ũ„šš†5ŧęքiMˊ%#9§™aóŸįöž€ĻČ~߇?ŪS03ïÛ]]uŦšûž:·nÝZļ`AüŽČ]aÅZh‡\Ïéå…аMŽčG;ïx ðņ”eÂDãáNīđČ#@þŌĮü[ÍKę>Ę2“$1ŠÃė͚,>/2ßq b„ėŨ°s 2’čöß&ų%ōsēČ֗*Í:ëī ŊÔðËÏÓ7ĢÞÐ6ûíŊŽ―ũŨŸËw›68‹ÓËŊ?Ū•õ*čūG~ÐēŠģ4üų‘:ŦWo˜]ĪýNŋHGïÐUS—4U­îąŌ<ŧØÎHĮĻE­jjqČ!juĮ'fô~ž­tšF=kũՋŸMRéOkï“Ŋ[c’ýėå{ôBZýóī=ÃLý!þkĪŦŨ>CôLß4éõĮtĘÉįŦN—uLũšŅâ5Žj?ƒö„,øðžųŧ_Ŋ5)ĨyibÅЃ‰5hM&°Æ°sQSĩó{:Ä:ĮI>gWŪ§Ū†‰|0fþQ’Äk™ŌđĨ™•ÕĶmuėr™Ðœũ&Ø­Ú8Ŋu‘ GAëĪßeēžĘðŪDí:Ö/ë›ÍģN?-Đč3æXÐv)ūŲgŽ3đđšÏ[ĄÃžRŽ/æC=!.lĶ™X˜ ]6Œå’x8É`ä€@°€?$ņ0û<uų’Ž ꡜlÏŦ\įĖžĨđ/ÍÖÛoÔ:x{ē^aĘmÞžđjÖŽér2Â4Û AƒPdĪ üMýZ”›5ņ ež‹ˆ0ĮE‹úg‘Á0ų›įsŦ „IžåÏÅķÍ[6ËÏĩąÖŌ6`Æd=uōƒƒrĮþĶޗŽö§`â`åŅ™2Åfé;Ũú—Ï#QĮųŠD)ŸōčŨdø%€į'î‹ÏcΉÏf°Ü‹ß"ašĐYŽúÕ,Ŧį–ÚõčĐÓÎ>G~K•m+†Q~AdÝ8ÆųÕčëóĨ=uÎ9{{OCĮŸ=óTsõ^v2]d…’X248†žû+C9ZĄũĶĖĪë4ûkÖÐ.ó―ė ÚþÍæl1W―NĶ*-œéĢ=ĩzņ4}?­D;ĩŠ#Ŋzũ`ÅaĐ=•ógjėô%ęÓ2ÛsÖc|O(eÝī\ŨžâÐeû_ĢûiŦ!ûčē“ĩ.–zôMBqzJÓÎļ‰Ā<Â/Ûr"‚øhzz= Üo]}æņZXÚROޚh-ɛĒÛnžGûðīÚÕÞðkM]ĸuÉÏovÅ*ęļįvŒš^ æxMĨïûœfyލÛVŒ(Ã[zÍ·gvvÕŠfwÓâÜĨšģxY˜*Ó<ðī―J•žweœŠœäę›>Ö\ÕWTÍós%ëĄ_†ļߥo§ÎWƒVÔąUc+ÕB-[0KKópÜņýũÜz―F •ãũ?õä°+ÔŌyö.ÍōTpēĒSþâyšŋžPu?-w‰fŧ™ ęÔЊEóīxé2{”û™­]_ukT*Ó9ŋÃ;m―•XnŪŌoø ó]„ Ąā7”„%0F9ŒÂ2|cQÄ%0 Ÿ4ŽCėŲ҇‘ĮHōgŲ#”„9!ļŠp ̈́‰R–YĐŒ€:`FÅ•šã`đķĀæēąŸ|Žo―EŲ[oūđŧõéŦ.]šÄĶĖlĖL]0.ÂÝaJÅěëUK—øĖįΝ­… cóōœĮ›[Ã(ųKŸÐ?ē.G› D„iÄýØ%ŠÄĖmpG^F„$ýš0vĀüKæ0Ą†#P (0e㕜ó―4—:]€û#YЁ(Ëė ‚Ĩŧ|˜&õdÛģ–~§^ú‰‘,ýïŧæJ-e Ïŧļ›öÏۊU·?WFE7Æĸûīŋ9bĸõķģëF™ rC"/ŅŦÏžŽ6^ŠĢöï—ä\ÝS/>ÞKũ>ûž5PÓOĪØúȃ˜ĒôĘÚÝfÝ?_tŧ-ûL‹ŋŸ )sąœ`aÁ,f3U<æ•4ðĒcôÞŽz’öV;Ré;ïĢÃŧVÓĘv–Ë7[ÏlĻĢvÛVį]4DswhŊÞ­å…Û'rŽų­æ­ę镑ĸŌ]Ŧęj†MŠ/ýëÚĨãqĘ0hro’äeģ–˜―0ŋÛQÄō“ð(,[gģ]Ÿ~Z|Þp5&Ŋ­…Ž/C9ÃD”l‡āÃī‹†Â“6œˆ ļ ,,ô[Y’ä7:'ĖĖefp‹Ė`™žG.˜ÞĨ<ą,ü_<þ @`øýwö\õdüĻ·Ôēuk›q{…'îĒÅ‹īØŽqÁ‚yĘ7hæ{ļŌĶT€4~˖l:Ë܄7„5xŅvdKé0[ąF‡ OĖĩSdž­xvWķŎA~xvĢËóMė •S‰éĄÐ€! žÜFíxWÜãĐ}XR`+›ý2<8ŊZĨrč•d*Íš ŽuÚ_ ũ§‘ĀKdōíĪä ëyá§éW%Įá% 끹 åXꐙŋŧžBY&™Ŧ™õ%ógáĩéã0<ö§Ę‚AâDÐؘP€  ģĒóSƟXđV8ŅX.@ Ėļ98ū؅Æ2$Ę8ØŪ<€U­īGîļ/ūô\čX3ËĢ^ƒÛĢņ“3 ‡°æ|Č 8{9G 2ø8l–WhĶ󌖁:Mžá($ýF_…CĖĮ‘ģEņžļûŅϒë5{v;Y‚ģ·)}W#ũũĀXŨ HŌÏQ–čč'ũ…Ģîųū`.OØ%#hƒˊČN{ĢÚįú`Ĩþc9Ž8ø°‰S-oķũnÝc[ą‘üĀģģnBöšĩkh·^đ­˜Ág‹­ķ^·ˆø\§Í–žyLRjEgĨ:ÍÔ1uАÓmûūJ ë\^ÛyėÝ·njØĶ§ōÏš)ĮeuK•åõĶŧî3`Íi{éĸ$UŠÕTûôO@“ÍÛ&YjwŲjmÞô jŅaíũF-ÖēĮ:šŊi ö;vÛaíuþTÉKzúį™2íÉé―T·\Ŋõ^”ßvË^šRþ°>―øj+3,ë"æ–W{=ïũo?ŽæÖÕð#öēӘIAÞjÕëŌLYžGŦ­ü”žĻÆâ°ęð óέ.ÍÖN‡ŦĢšTŸW.Óę&§ëóëķôÔAĄÚvÝYo―ĸ Ž?ý5mŧýŅ:ëīlŧ@Û%‰ëK3jęĀvŅ'c>Ņ·“§ëģąïi—Ąįëó·ūó{kݗ7QÆr•uƒÃ NúũÓ:wŌšĸÛjzôęÝäņ{€q―•tņų7Đáá—ë•“·Õ­ ԙ= ũŸĒ%3ūÖČ šöþĮĩíßéĐ7>ÐŅ;ķ°Nē^3m·eG}|ĮíúlÆAÚĨā#=ôņr ;ĩĶžūý|}”ūŧž~ų4}óü :ÝS]ŋN fOԌj[„5ĪhÅRMøaŽ 2°hųųpœÎšýoÚŧ{ûčÛēæþîþ„ūþeĐχYđ&&ÔdĐ`ĘĢsqVāĄ€ €Œ0ū[A3õ“pŽķ2ä Įīķŋ`ꅙŪ0 ĀÐDaâiËŠ„PĪöLó9}2WŦÕĮņüį5Đ% G!3)ØåPm–S/By€sˉbrø| GūĮŌFĖÁ!ĢeāZǘ­V–0+úÆmu–dÖ Ŋ„‹úx-ýéį>Ķnė9]&ß4úÝŨHšh?L›‹ĩĻ1hðų"‡ģb°AðĖãž`B‰ŒôąËĖðâųU6ÐéáÁėO0xŧÖÐ/ zâĶÄŅMý+‘ƒÐ€€üš?CÆrÐÜÔũ žüčŋWYÅó4üîŧ4Į›]šõj=öákvé5ú`ZnDH+Zōnžæ>õ9ó mŨŨ1'[p Ÿĩ„wÕęL{uÅqëx·š-zWŸû˜ ėÖũ‚[õϧþĨ+Žņ\üM草ËÔšæŪļ0‚e4ßí õŽ1Y?pŸūųhĄ:ÐûU–$z›Œã~Ý­ jÛ\―ŨA'éļ΅šýÎgīÂ^âĻtĀü ŸhŽZ8fn_orQ_§ĩŊŌūt$#;č­īŪ:ėü?iĀÖÕj‹Zr,’DøOৈí°§öo•§įėĐ>ÂāÜ`§]ÕĢąôéĮ“t؇ŦiÍęÚcŸÕfÉ4ĸa‘_Ĩx§ĐO~S’VXípȉ:|ïÕ°ķ§ŽCŊiÃĀi qĄô°V˜ĪĖõųĐ °K)ŧ]įk3X0&įan-–Eø&”Yūá€E>”;ĀDWÜ9@›žn3,5Ŋæ@ Ėžâ:+Y> €k™WŠE>ŪOS€Ė20' óâLŧ˜QEØ%õ& ŋ–=ãĩĘúSX+õ˜Čň6†§1MāŸåOØt2ā`VØũ- Č:(Z†LŲļöúb @u= °ė Ž.$I?YŸ@õĩÜĖË=ŪG ĖäɜĨû€ëÐČũÃuS~âT„‡3ŒĀtǰÍ(-ķƒÖÄ)3âūnFb•‹ōßÖ~yŠŠ+ę˜ËĸЧîęˆA]”Są‚vÚkw5ö’#hӇOÞĄũ˜ýø―zØږ6ŋēM;6ÔⱟkIĄĄd&O^Ū-·éāĨīó4vėGō1.›ĶŨĮ}ŊÞ{ï§>œĪëŪÝ^_ýÔq~üŪ *§šķÛđ—ÚU_ĪųK―ö|―wđ™Ít|ßÖšë/Ã4ūö@õjdï5Áā-HYb)UÍ-:Đï>ûëˆ~ 4îđg5ÃŽUŠnŽÖ đ*­X Đ?,ˆ+ĶMüNKsŠŠš} Ðéķ RZ‘'Z3Ũk•âĨ/éĩtėÉûč[.֟žúP8HõŠWņžluMųvr”·lÞtÍōöjÕŠä8`Â,Í\8Õú._―þū— fĐĒûŅËæ5Ŋųô˜ýŨŽQčfükÃĶZw v~”.Ë(D|ŒœnfkÁ1…„rgÎĊĀïîð4ķš2hÂ,ĒCøZ&ŒÉ ĀĆĢ&n–)lËE0Ģä+tÁÔ 0ĀÜåÔ‹p„q@ŧĢ$€ vz˜+`M‚$809ŦŸ‹ĩ&ÖĀpþYËRa|!# éö„•iÖrø   nüÐ_ ø xó=Ø­ÁŌؓAD2‡ hãčDŋx\ā~ázŲKW,k,9ąp Ūpį‹ ÏŪ#XĐŊĨžļ>É?˜\âôC^Æ}öÚĩ™ˆÁįŠü96>N_o.)Ï΃/þ‹ū?IþûŠâ˜ÄåĐž6Iøý)ÉĻĪŪÝw°N°E-mĶîþē:õÚZMkų}-™Ŋü[­ķūN-Ŧ8(Á*+kþÖŧ­―Ÿlg°‹ÔYŸi\‹îšŧߖZðÅÝęč=žŨ{K‡ĩjĄ]ķéĻaį ŅÔí[čĢÞÕ.{^eg‡Úüë-úĀloޗïĻĪÕvÚa‹:Ö/hĪ$Ą­Š ó”ë9ĀmãMÍØOýķiĒÕv|ÚįĻ6ŧÞĻ‹–―§™ŸVƒĮh›öôM‡Ū~ýÃ:ï3Íyįmö8<ü)Ō°ķÅīÜšĨĸ>?§y-"õģĐJ•*2čT―ņęŦžũs0q”ŧ•3 œŽÓė Ķcēú V($Į +X*Ž4œ_˜[30`Ō@ųĢėCŅû;Įų 2”1§e™Ĩ27įē’”ˆíį>RČäs\€kG"L4Ø^xĶ2?č<|&@ aą8(ŠŲád t{aŠHB†|0Fœ™°æXĘĪ ‹ïîŪ`>€+q’bā PÁ\“z-°“ú“9ŅÄä^ŧČäå< ˜ģĩĖ”čōščwڟ F’Ĩ-€7rƒYÓ^ËÏ=cN–@ ÛÓsØuŨGāįŒpOž<ÕÞoÕĒEó(;uîįþ2 Xš_Ēũ&įéo~ŧģŨ5îĩu˟d6-ŲVlū·[?ŅŽOÆ―Ĩ'þõ°Ÿ~Œþzõųņž­ŸŊü{yllžōÉT―;ĩP[·ŪĨ[WVJčŊŸŧ VĻoŋ™ŪꍛĻaÍĘüįkÂãUĄe{ĩôwØ[$3FvByųÕ7ĩ2­švē·u‹Ú”įČS_7EMÚo­U= ēbŪÞõķæ.ÎWÝfÝĩÛŪ]U9­H“F―Đũ§;þlõFÚŪwo5Ŧ Y+bîÄŊUhÏïurâ}Eį/ŌWß.Ō]Ú*+w–ū™YŠ.š(wÖ·úzÂZūŠX5ķT·­:zi S=)y=X.^ĐwßyG3įŊTĢÝĩĢãÜĶ™iÎ?NŠÓ\MęÕВéßx]jĩkЧwJ_[:Sgo―ŊŠ=ĪŧmåĐë$ŊQýōÓ1?yķÓšđmÛʛžØkv™>{ŒfægĐįVÞÎ,ŊDÍZī°i{šúX3°ÏÐã„ÞŸ8qRšf͈ģkö?Ldœ(]æüč{ĀÅĻáýJŠRŽ,1ąH–‹ÅwÅlHâÚÄŠyÖlŅ  ÄäŠ9Ŋ/”~0Eē|ƒž1_`°B†0}˜Ĩ€Ã€ãēÃÔ鷁ë2}~MÜVs|I@ú0ýš^:-6}―w•:ôLâÄúï?~ij9ĻK][áŽô―aÅÖv/īžŽÞĪģvĩ[1O?ËhÂĐ ‘J>V’‘ĢÞ{ėį#Îå~ Ŧ—ëiÚĨB„ĻŲēŦa“Á{Ē·đĸ‹Yïžâ ―ëjØÁ]Ø%aĪĕíÚkŲq9ęE_ŅGiö2ßnũâXLčœJ [ĐĶAh(øwœRúäš`eí›ÅšÂlĸKLŠî+k@3(Ž4€BâžRfõMĮ ĸÅ1Pbm#Ņ ļ•Đ%aVt·8ÕMy.ÚwđT@eŸ˜M įæF1æRęáæ„ Õ2Ĩæ)9øPW2‡éŦ|Ó`ĢÔ_ŪDGÝð€zÔZĐ·ŸúŦÎđō)ÕhU_ ž/åûœ§aWé=įéæËy‹9j˜6Kõû]Ĩ^sŽŨSþzå96į(Õ'ÞVė’;GyCzķŦëmÅnŲļmÅ~ŌŽ$ÜįÚÃþSEkĪ>Pkæ“Ģsuc莂ēčOĐëøË&ókĄiÝ3k?'6•ŒGē.(ī‰7’Ë^ûŅ[oY7m(ĄG°`­ŸÖ­‡8ĩëĢŅy9­ũԍ·ï§6Õ­ÓSēXĢü\ß$õütųÓbë—―ū,ŋ—ïNë2ŧ3'#æ ĘP–ÁˆÛˆvĒ ĸģęFCĄßĘ|(h .ôáÞH(~ę ķéŽÍŠceŲHˆęŒŒXn^ēųīÏĀ(;ĖĶ1RÄÄjįë1“qÎN7 š$į§\X/ Ϝ-Įņî,ö(ĖĻåōƒiģ‚Ĩ%ó SlÔG~œŌ0þû8ĢJĖC°>bІŽwĨ]üŦ@Á•zô ÃįZ—`"7ĀL_E^*Îq,dĸĨ:§ PIũ%įaļŲÎÏzQFÖšū§ŋEb •l+ķZŨŧ>îĸ)§‰mŞ‹mÅÞԁ‡Ž]·ßĢ·ËiÜEĮ ė-ãŌ4þíGžÅÖ―:ó/ëĒ~ž‹ÉkžW:YĨíķÔu]ßÕ}ŊÔSŸŽVįŠģõí‡&”™šilĘs…6OĀķøá͚_š\_k€Äl ó!ŽāhRO€Ī- Ķ™D―i:ÔO€å ķkxʉ& āVČŪer/ãÁtÞ3· (ÂĪ‘3Ø#-ĶóXS(ĻĮĩ8Óāė“€Ûï|d§m=#Ā—Ūø]Ož0WŨíöŪ]?‰ë8`•°TĖӉ9Ö=ękÂóØf΄Y'Œ7*ĪđþĮo~č;ÚJĸ… ÖýE?ŽČA―ī‡c˜@“6:ŊG_đ_đ֝äóø7úӗoÂÄӃ“•·ÁŒ}HŸ~ðA=óÜóûõũzäûTŋq# tĖÚ\ïø°ąÛŠ­ōMŊžð/ÍsĄŸžüOĩÞ흷W§ð@nķå^šmH'}ōĀsZ⭟ŌKsíõøĩ2ŠĩP·ķÍc­Û&lnyŅŋQĀfō`ļ’ tÚb UŦČûļq‰ÁëcFę™ũūõÛÉûõ?MÖĨËõæ ÏkÂBû5üBQÆU}3ō―þņLŋ‹iŽI;K  aýŨýēzĨįQŸyMģmDž]ŋÛŦWč͗žÖũóaYÓôÐĘŚîãĸÖ“.Í5―ųëîPÄF‰B+g- É(Pð˜_ŒĘ; öî/~(€[F,e`õŋX—é'†Č<°ŸR3ĪPîŋ0“Ĩx Š ,0Kn`ĄŸūX{čsĩό…A­’ų â%'–9€ôCō›ĩÂ.+0‡ëķ0>āË1?b/qāIžq& %<åāT„#_—‘zÄ<ŽÏaJ včóĖ:/r08XË]­įåÂËÕm°1g‹ų:ú5%āēÝw\ KOÖĨĶÚH_Ų\íöRýƒŒ`i?ï/–X?ƒōŪ |@?šýŒF ãFnš_åSۊ3č5iÛVÜ}‡·ŧAófþPķ­X}-ÎĮRðkÆŽDN&&LøĘŨäjúœÞSņ /&_›šl·—įeÆŦ ~o\+]{b/xöp›ū|mĶōOŋãČŌÍëjŽc·^pôņþÐģĸŅķbž{SŸýŧn1FÅÖ žĮ?N âÖ?öã|‹õœÞzúąëŊŅØŲđ~ĸæŋgÞsä]Węú‡?ö VzéšsuÛĻžŸį=§4ĢĸÚ)måxÝ|ã?ôíĖÄ*~á@›•ķ\wÝrƒÞŸ0ÛÄ#)ė&Kū|RCÏ}Ōë6“ōĄ˜jë/]ûG9ūķgĄÅt1&―˜—4ŧ‰Č<ūŦ Ð%Ęv„Ģ 1 ‹Í6a@‰Յø`ĶZ°#ß\0Ā*ÆĖ ֊ /XœËÅïïþė‘€°x0•”ƒR€Ķsð€Q –ÃīËyŪ Į!&aF…ÍÂî`]6Ïe’Ÿ|ÄžMæN 0~Ē&ÍÚĖÄąp-ēÉ-`œ°R€'ŒŨĮ‹ "ÜdĀ+vÂð9&āÅÅɃDČčš @00ĖČėŌâÃeéA€Íž”·Ú놋門Ŋ“-ilī°fž°NĒú°4„üČĖZJĖ + a†ĪÁĩ””Eŋ„—Ŧ{0,)—ö˜ÔßéwŽ`nõPÕý €FûÜWĨ–9 .Ķ Āōƒy8.yRøH›(æM_‡Ā‘cÓýJķ+R=Į]Ŋä#ÚuïĄÓÏ9W} ĢOėđNoĢä­/sþƒmՖû—žUU[ũîĢ ĢoŌļeÎ …sõÐݏŠÎVûĐqÉ*åų6īėÚO7 ?KéËŋÔloŅTžþ zĀz'Ëۊučë}QŽÖĒēmÅfϘĒËVý<û[§ŲžÞýæâ‹OVß·ÕE7―†ÛžĶ8ū둇 PĸvŨqgÝį―b3ĩbĘh]zþ<ô ―äNå—,҃WŊÝöÝßËũÎֈyKŽžÖU­Ö7^æōęc7j@ï~8ø]ĸâ\ŋ֕~7ĮūņO―õÝ"‡ï[ĻûŊĒ}ĪýOŋIž1­ąŌ/yēšá‡žŌ€/Ũ?ä­iūé+įéïĶ―öÞ_G~ĩÆO]ėā Uĩäŧ‘šÔkHO|”Ξâ>͛þ™zûSÍþæyîĩšždĩ·VĖÖ]ŽË|ÕĄ}æëŪ?Ŧ}ûũŨ€CÏÔ'S—Zũ­ÛŽu:ėþqÃÎAtN€]ŌKĨ6cp€`bÞģ įŧMŽ ā3ČĂ}?Č(cĀG"ęM„ëģ–Cýģ.Û(=”6 0aq€&ąV œ Pa~° (üv ӊQ‘;ā‰&GĢ#r1/Éw"/€?ËŪØnGāŦe Éĸy”q^bNÁ/#OŪšžô\Šeƒ-&ŽO>N=ÎOÁžé7ŠösœŅĮRÎ;Áx].#=ÍÄIŠĖyĮ/1ĶÚ`ŌvZb>6–öXÆžļ,Ú(ļÆŌHÄžõáøÁėM›Ļ`ļ–{†œČ}įĖŅŋþÎđļ0)j“ýÆŅĄRV‘·óóRķ­Øöĩ[ĸūą­X·Î"ø\[ ûŧîÞöQxÅ?5uh?åÔkŊÛzNĩō_UŋÝn՘ŽÔnWč­ŨÞPãS†ëö}õåý—ëūwWčþ§^P•iOkâĮ_'#đē6§™häM|Iį_ûŠÎŧĸŸÚŧþ0ņ­*Û&nޔÏõ}ŋŧóūÐmÔeũŋĄ=[WSE{㔰ņšŨQϝ0RũÜ}•v?î6íÜĶ–uc2āģŠŅ˜gîÖ―ïŨÕ]O=ŠÚ“GčāãïąēþČ_â5øoŠÕ [të‘}ĨŊŊÓ7ŸUSýÓöōvg7ëÕąGĐClýíá/ĩįeĮéĢgoÓݟ6ÐãÏÜĢ17 ÖߥÎŨœėiēÝĒ6å=ü•―AāÐPū˜2æX'ã ģρ.ˈë€f˜ ­™QėücN/•Ũčá åï‚ý1X™0°•ÄûķŒi °+Ũ͓eÁJĢ^Ŧ|ÏBa•ÃÛabĩ|%0&I&fęá€/ZR.ļ| pÂø(ŒzcŨŨ&õxOLũ€ۍų^h˜MXËS\æžėˉcĶP",7ā€ĪȜ%åēAnâ) ĻY}8ųZōÂDųØ!ũķóŊ@ͅĨ.ôaČL^ƒ óĘĖĸ2H 7ļÎå Á­YöHßDJš,ųž‰~Ũðî( ŲVėŧqjÁķbRF*+lņŸÃ"iÆwÓb[ązõ7b[1ËYĩõŽšä/­UßŦW2ēÛëâĮ_ŅŊŋĐis—Ŧõįë‚ū}ÕзŦjm­ÃLÓėĨųęÔáB]Þ··ęæļŌōôûîOu4jã5đ+gŦíƒuÞnŧŠquŋ%õ4øÏ·kôŽ*š–› ÞĐĄ…ÞÚkëgiØĀīðÝϕ“3ŌsņÞÁáôŪ:ïF-[ūTó smâôÍÛh5Þõ ?õ/Ï(ÐŦĢūV~ûãĩ•·‡Ën}ķmð0#Ņ5UÁÚūýœ2[ũŅ€íÛĐff{ÖĢŪaĪį„~ĖąųTõzčŽ#öÐUƒÖëýŨå— UóĖjĘXôķÎ?ã Õis;bwoŋĘVsĨuEIūū=K[ī—ķmí Ŋ=hčēåëHÚOÃæÛf}Ņð“Č8Ži_ų+œŽú]tʁĩuˋ#ÕÛ2dÔÛFý―—ė;ũÍŅĒi랁GjŲüīÕ!ý­ß€ūYLŠĸāiƒĀIßKą"ĮLÚŨ7,ži­Ž |sÂ+Õ 9;†ÃŌŽÃð˜s4$ĮcŌlc(z”9 ȍaþÏÏC°ĐX‡č“”ss.˧ðr^ęÄîÏzÐ`l>ęÓn˜E ĀaböyÂĖáŪMĨ<°Č@ØpŅ`™+LĖģĖkĀõœ@IDATr&ĻB°JįĢ €(ÅÆŅīÎm§ĖøĖœIĖ Ņ~_ÂØ™ĸ Šû’sČĖ9ŋée,5·zļž0Šû4õîŦˆc>“Œë€Ņš$ú‹m ïŲᄉ3(!?99G šû2ŪNŠˆã›ęۊíåmÅÞýĐF―ũîOŠą(Šë˜}ķíŽzõ―š{#RfåZjÓ~žéUĩsŋþÚy―k+Öhæíŋš­wīüëïŋ2ÔĒk_ĸŽŨ‡ŌkÓekM.œĶÉÜV,^Uö(’qoQ Ŧ‚*ÎօžĢf‡ ŨÍ[géĀþWzíąõ™ JžAT‘ĢôˆÁZŽ-$ÞųÃrīäŦÏ4yŅRõō{šJ<Ó5ÖSá”ÉšđīĀ nŠū˜žTĨ]ÖÍã·<―ŠŽūЛhŸ0Np îčē­nØÕdĒQ °…îŧĸ)];ĒŊ.:<Ũ‰Æ€TN_Ĩ‰S§k1X=å Mĸa^ĒGŽž9í°X ‹W8XDAšö=ōýmß?ið5īĮ WĻE͊úØ{6oßÏïŊTë&2Ĩ^PļLSf:2P&ŠëØ―‰žr1ðôŦĀ ã"ĄÄ“ų2ņ“€7) ―ÏR‡XX뎘â"oB0?@˜\QÐPēį%s–Îėį&ašPtK^ę fIý€œËž`\@%õĶ&ąđ @^ö€"æņ(Â/ é’ĒNęIœ~|čü”Í5ĖSVŽņĻ‚i— &Āå3āž pØIœŒ,@ī°æ\˜I—zŌžfīųģo–š$#ðxeŌžcôKēs‰Íā– đ>bÍėé+Ÿãsô§óu[:Î%ÎóĖÓ2&r$ƒ€”žŅܓMœhۊĩoߊLæŸVH1ü-Oå=ðŋíðGø•BV;ŌÏŠē―!ņƒ l‘#ĩĻĐ―ÄéķÏėÔķlĶþý؛Úę§ÁïKéęlíqô~zú”Û5äėųʙôđū™ąĀoæÚg VĢîÔģå ‡‘ĒŪõęĨņ3Õļ[ōūągQĐēKÆéŠ›žUM[AōŠķaïŌ;7A–zívŠvk\Ē}.Њi·ihŋNaų*ņŪ;ôÞ_ŸöW 2GįÏŅ·ģōü>c-+K\™ wx—$ rPĐõ.ÚŋÍ*]>nĐnÞs[ŒeÚuïôôSįęŠËŪV—fv–ęŲO‡lŨ@ÃÎfĪ’!`ÛA1šY1Įį°4ĢøîÏáDSļ(ĸÄÉ&aĐāNDŦžXĻËsY€kŠ'ŧĪĖ,#ēPšn@8ķččpēąY7dφ ÄÄrđ\ ÉŌ–{v•–;1ÁbŪDiŋM°aÚN<‹Đ“~ĢáޜųāXÛÄęϔƒi•H Č}-ë=Óðž-cĩt^xŧmĖĩ"ŧĀ07JßÎ%ĀŦĨ,ŽŅôgŒG,+ũiđ―=ėH]?ü{ŪXóZlŠXĩk*(ĸPÞŋAüZŽZÞŋe?|Ŧ9Ŧkj‹f TēbķÆOZĐ-šmĄâyßęýÏ&*ۛD7ËÉÐÔ9ŌÖ―šjÎÂ\5kÞŌ,•AŠôÃ7ęƒņÞļŦSgU3c­áØķurÐ;IņßX1kēÞþč UlėøģÕ j›Đsóššã˜īų5ÛŠUõB―ũÞGšŧ(OķØR=ŧķVVár}3iĄ7of‡š ûė}-ŊÔ\=Ú6rÁnlõũÝč›ÉsÕ ŲVŠ–ģZu51^Š)ósÕžEKûXį-›­ ģJÕą]Ãä―Ÿĸ―Ķ-(T§Î―þÓp[ WĖųŌ{f~ŊÜâ4ĩîŌSšŨÖĮēÍiŌEM‹ũũĖ8ŅŦŋIŽZëfwüViӟc`šĐ‡ @a3šaȂ‡WĖĩaĶ€ir­ĸZŲ6m†s#!—“T‚đAÔhĒäyČļöČņ,_ƒã‹sĮĩNg”DĶ‚ú,Œ6ØĪå„ųĶķč4+€%ųl剷0á c€ Ŧ9˜—B@‘ųAĸ€R@ÏŽ0Wð*øĄ#oÂ]ŊóÎ\ÃtjŽËdĀũ%}’ĖKDÝHLÜKLÄxĖ2KÚÏč• \Į9Fš|gĻOđ=xÏbæÍŽþKæ,k€?UFŅī fÉZY–gˆqWg^: ōïÍ%-^ēL /UۖÍâYŲ\ä*—ãŋŊðYĻáĨQĩH…­YÕŦį6žķņ8­QgíÛĖ6`ŋ'žwíý"ûýĐ]]āwÔÝQdiŌy'ÖÅŲ‘ZoƎĐ„·ÕFmtāÁ[DYÁHK―žÍï^Ģv=ž ÄËŅVWÖ.ŧïãKxé“xģĨYUīe—ęဉã\Įž;cÎ 9“ē!ijß}'učIÞå ,j Î5Ëäī0ŲÕ;$eÛ}S―AûØW3ķwtaÅīįÔëŠ―ûÛlÅCī3–ūtčūÅIօ§ÚóGĸkÍûˉûî[ļ&  Z.ėvãM/­Ā“94=? eo–ShÓ‰Æü`€Š;Ę!­rY8ÁJĐ9;Ž™Ô1‡éü”Apb‚ðuáøÃwH$—S † aô I@ÓõøZ#†Gb€đŅ „Œ°[ķōađSÂāĢóĩĨķņĨ LĖŅp2G‹T›:-Y’oũý)ŠŨšžšïQßÏWY­n+1u?ûlœ.žņyUŊÓH§<,žŋ•iŲž úôŦ*­TS]{n­zëÆÝû…B ŋsîü~$üė9ÄY­úTƒ‰ŦuRÁâŲúzėx{-zÞ§SwĩnTsģÉĮĒüåš?wąÃûų·9―fúŠéýĸŦRqcĶ:M­ZĘņ nóIl+ķXsŽð;ėųïrTŋqcåüĮۊm>-*—dóîkˆŸ‘X‚â\Ž øPô* l„vÃÉ{ĨfUØ"s‰°-æI˜Äė˜?4ÛōƒŽĐ08+…Má ŦóÛėˆŋ€(ĸąē5{ōua~4[„þļáīä2Ā9°DáÁÄJ{(;_0âĖMLđĖĪ—&Kl0P6s‹”æZ_”[!öOŽņ4ōë6xE^Ë P’―Gcý§û†:Ē € ÛíuY,9!č}ƒ ŨđÚė5úNt8 Ý~ä‰Ae Îq,v?p―ܗpŠį’ūä6a<MĀßEþ&iɒ\-XąJ=Û5Ž„žžą™+郕+óô·ŧî7KNķËÎÜØ―2K4îƒįuÉyŨiaĨÚöx›§ÝúčšëþŽ.õsībálåzģáĩs~ŌÆŊG? 3ŪyNMš5TAFU <ũFŌģÎ:ųJ5iĖSšøŠg”UģHóÕQ—8č€-ŨÍ#ý ]õ‚ÚvmĨžysĩ4­–tNØ―“‡H›c*ÕŌsURąĶjĸ"ĀŊ—gųũšØÞ•G:ĪݞÝjlFb[ąŧž­Ø[ÞVŽÆĸr[ąÍĻYåĒlķ=°Aā†ƒ€DBs †ÛĪ‚0IZ‡âØĖt0‚RŠŋ†â9MÏ­’hBŪ ķåü"JÝûü@Î|z ÚČpē˜2ŨĪLð6([æ1ņŽr='2B,\ƒ‰4@ÖíÁq†Ïī!žr}žđ ”ŪM™㔇AéSrP6yWåáņ‹CŽO–Įýrrbú`s#“~už˜—eŨy‘?aégLÎūįŸĪß-“eæ<íyf:Čõ1Įˇ0ŧLœpē óŽÏuÛ`Û0Ü øÎ— „|Ó&æXSۊ ŧú/ÁęO<4ķ{ö™W5fÔ(õ?üPíēÝ~öðÍĩ0ëŽÏüŅ–OĢ‹8MĩŽđG_y ŌLÐŲ―ûęԓégęĩ ÎÖĢ―NŨĢGmíPd9ūĸЂJ4gY‘jlyžŪŧpWïęSĒšž0o―Ô|įĢôČۃԠjž.9r?=ōĖxįÎ?Ę5sš―2ģŦéï—_ĐŽĨ‹ôÆ?†éė#ũR―wŋÔíaĻĨZî ‚ĐzŠ~ RĐTđ˖yý`Đ*UЁūđĸ„ĩÄķĀ ËG:ï—/ÉðBybæTŦæF‰cĻŪTE·Đb° ČúÐK1ä"fĘ•!Y ĢØņ˜ pō1–~$sļ>ƒsxé@–⿇k™#d+š^Ú(XaÚĶ/Ü$˜32’Ųdäws°sY°Nū'y`ž †É’<æĖžƒáãP垰I1ĖŨ–“|á(ä6&ĶÞä„Wēë Od+‘lÏÖČLb^:%Þ} hbūīŽ_#Ó&úÅģƒéŸmÅĶOõBðũßWíMīeũžzäûÔ IcuÂiúîŦųn'}úëé‹ũ_ÔĮĩ·Ņ˜ ûË+Ītå―WhÏ3ÕĻOwŌ?Þ[oŋ9U|ŅW— ŋV*Ŋ-ģÔũ*Ũ‘`2ŠÕS‡:?ĮpÓTĨz]UöZŧģĶxũŒulöSSmšû°ŠÁŽY“&ŠæŸvŨÞĶŨÆôÖ°'?Ô—íΧnŋLW=öĄŠ—VŅ6ýOПÏ?RÕŌVčé;ŊÖ-ũūé…ôuÄÓĀ6fÝũ<ŊC.đIíŌ'ęėŦŸÔ€“ÏÖöŲ_éšá)·JšF―øžķė}ļķoąZô1ÕiÝGwüýŊj[iĨîŋa|kĶMüÍ5ėî[ĩsŧjqïŸ4fvE-üx”>ž―HįÝņīk5C7üýE­Ļ2A“g-ŨWė­‡ĸ2X―5KųÞ4þœ[þĐZLwž—īēęwš2įJÝnO=4â|Ō›ÁWęák/ŨÝŊĶRŋšŧv•.>iwiæŧúËíOĐÐ!ėÞüũjđ•Úßv–šx)ÅĶL8ņŽĖ-°U ‘Ã(Ūsƒ7eĨåeĸa{`ÃCA+{ˈą^Ќv‰y… ˜ÁĶ…`m·I–TPþHâd… 4‚!šiaŪ$o0*ŨūF™cMLˆ~É|M0@Ũ<ĖEÂč‚mZáÃķ Î';ģ DYÎK€#2·9ó•Č`Q^!Q8,wäeîŅ ãę$ųËw–Ōp,›yĘÄÄ Ģ5Ð[ŋ>ü–1ĐF˜ĖņōtaæĶŊ’a&Y_래ŦųLĀäa°âZÝ8Fašfp‚iØq{|āĢOč ØsQ@X ō_ÎÁQbNŨ‡9E>ęŠ{æ6Ēï,m§?6uōXģ­ØŅÞVŽiÛ-ôč=wÆķbógÍÔ!ĮžŠëi‰ÁĖwxĢÄÉ/(V“Æĩ>omöz]·Õž“›XÜÐáÍÚjŸ!—éOÃ.7ĨX'eĻeÓŠ‘ûĶ.9þ$ ŧëQ-YĮ€ą&Ģc~ūóâÃ:ëāþz|l[xĪŨüLâJzÕ'3čØ^õUÉKj&ūóļŪyėsÝþÏôŌģŨjÂ?.Ð?>ĨF?Ķģo{Sƒî~\ŊžüĒÛūĢŨ.ÐÄißj9M/]ĄéĶ*wĨï]ÉJ―þÚŦ_­Ŋh˜ūúnģā*zė‰ŋ)}ōX;Ý;ĖÜŊ›ßŠĪáOĸ[ƒũĻĻŧï{ŌQFģĩ|âGzöųtÐe7ëōĢŧëĩįžÕęúÛ脝:é€ãĸĪÛ/:D•<ŠÝņˆ+õĘČ7uņĄôÂóÏ+­á6:Þyö?Áy.<Äaá–hŌŨ•ŸŧZãGÞ­›Ÿž­kzI/Ī+íĄ.šHO}ū`M]xģ—§ōøŋė gŠĒdy…UąŸ?”Ï!€ ŨJ:Ė™dö‹Ujóšą(UϘ9Ė Įæų›[ ģĄ6ŨĒøÃŨ āCÎ1ęōXŊH^LŸœƒ9@đšl’ŽjØĮ2ÝŨ ,đÜbé‡ëYmÓf˜}Žzų pP>lģ!ĀKÜļēũ,gĒNŪ+cžÎ (Âǐu|í*{ÏŌΖ pĖ` É#é+ūÃbc āž;}ƂsȅĐ3sė<ã>F^ú7ØūågKړswÛč+ũAĄ8.‹~Ģ/Ė f7î_2?š0ëĖļMŅÁ–gS&žģ3ŨŲVlŦîÞVė<õ=ôāØV,§Ęū­ØíškÅĪņzæã™eĒëÅGîŌôšÕđa–r­ļįĖXöóÍĒßž|ĐåÎ―UŧYšæM%_ąfM›ĪŽC*t˜ēzÛĻûN{hûn õ駟é' i|ï4ņDú oGõ˜†}˜§#ûïdSoŽj,›ŪĪīUúô‹ųjÖŠ…ęŨŊ­ÅS?ÔĪĨɁo,:ÏÍó3l€_0î]M[:S™öōõcĻĘXaXōg'3éŠ=é/ņŪ1ĻóĖĨš·ícÆõ”ÞýhŽîļėUvðŋļéŽđrZåg­Ðm&—.ũþ‘‹âóÄWÔĢ–éĒüKgļģrJ<,˓[–'yû-‰{5ģ‹ôÝ7ã”ĀÓlÍsĸVŦ\#ÞÓĘ~ŨV—Éã1îMxÆ+äĻÝŪŧϚ·[<ŨĢ/ũÍŽi“5o#ķÛÔ╗ĸßÕØ1ņēEË~›ĻjLŽ˜ĪâMæaÁú™™ÆũÄ$ HHēVڀŸIVé@€ āðz‡ō‹îïxËēô%–kpÞ/ ĶRxĐ 6@ĪĖʐ1}úmu=°6jGįÅņ‡:‚ņšQ°Ë"Gę ‚N†—@€1ažü%?ĀÃ&ÛáýāåžnsâŌ7‰Ų—ŋč#ōŌF@§)Lžķá"Ų.ŦČʑĨ―š{ЋÁËe3· €Ãu9YvĐg[īÉųéƒpnr_S.RRvŠ6ûxA&|"eFNx*ām‚á’ĻXŽXFSTûEySęĶMī3'ŧP5+y0āy0ŠÜcĀ>:ðð}īÔÄâ wޜ‘gŲ:—øsŌĩØņ=>lŪŽ;í―Þđ2žKƋoŪÐ]oŋŽ•ÜO;ėĒŨōŪ9įéĘ3OTÕ5Í,Öč‡/Õ=#įĐ`þwšÓņÝq”ͰŋuóÐþšŨį^=zÖķzĸ‰?ëæ—§ŦfÖ }õÉb >\Þ2ôG‰g{ÜÛŊč°þ‡ŠbņrÍsŽ“ŊšG{4•Z ~ĢORĸýQÌÅZÖb'ÝjoÛ6Ųu4čÝĢuĀ^{iËĶ•ÕīÏ ]―oe.Ņ CöUÍZÅZīŠŪëa TqG"žýßK]ýÅD~Lō‹3ÕkŊÁęũĖŅ2ðĩlRIíúŠóŽÝßŨVŠUŒĨÂÏ'Oýķ―wŅ=W\­cgVПŽ3ë]þ.?õDĩīÃðįŸŋŪ'>Ø[―véŦ{įč6ý^ÞØïXĶV•ĪŦïūCÕiÄ šßŠŧzۊ[ŦÓvïŪīž]+§ý!Š ý~DśðŨj/qšï=6f‰–Íķ™{ÛtČ΍Ý)ÓuýЃ5ĐĮMÚiëV›P€ōĒĸh=°ÁXĩUŠTҐÓOŅË/―hsš―­ôRFĻ8kž`^VúėŸļ†rũ› ķ"P ŪÌ #ž0ÅÂXæųßXböâĨĘyæíļ.Ė …š^Ā8€ð4ƒâZLŧHpī S'Ÿ 2äMĀË:ĮĸøFYÁīü—(?ŒØ}™ŲŦÕų1o† Ų€xâQ;kžki+`G›`l!‡åÏ2HN0í 6͆S‘eĨÍth€ËšQt`ĚĩÛļˆH˜kQv–• ė CČ=Ûx-·/ôqú6Ý?8ĐPĖð&⃜čēšMÖiŪ7Đk™w‡?üˆß&VíbïņāŊŦFŧöjémÅ2Ä āKf|7]sūúZĮē›486&•ÚŅh”ūĸaūV,˜Ī‡ĸKõÛ4PŋC4čā]ôõĮïĻĻR}íÐŦgb"K5kÂ;zũó™ĘðÚÏ^ŧėŠf5īĶKSŋýJ…5ÚĐ]ãŠZ0í ųø[åĨŦY‡^ÞÛą…gœ–/œĄŊÆ{>Ō&ÕRīéŽn­ę­É”ŋxŠF―ó… = Újû]ÔžN2![°|ķÞó‰cƒĶŦi§mīUŦúš5ųk}6nēxÍh5[pę7lŠjZĶÉs–9fiKå(OßNįĩŠÍTŦrĐ&~;]Uę7TÃZ•U°tĒÞĸā[--(UŦNÝÔđms-üaЊ*6P“š•”į5Đ󗗩i‹ÆJũÜíGï}ĻÍÕŊ_[M;FßÏĖU‡­šjŲôņÞCëdę“w?ЂÂę·{sMžšĀėŧąjUņ†ÎģŋҘOlžMŦĒî―ÝwÕ=0\đPSæ­pųŽãj^>á8,sýę?š\^Ó/ĸ7J4}ÜhųbŪēŦÖÕŧîdžÕlZö`S&|ĢQ3s4uešš·ĻĐ[WVJžVá+OĻ€Dü_ÅŠÝ pæ8ÏtЁó%UŪh…pZïĮRŠŠ6+ÂBÁƒDŽd^ŌÚŊfBÔuP2—†â†™j„^@Ōzŋl&ʓđ8"Tœ7ÓʞxŠĨ„ïóR įPóAïøî—Ãe{ō#øŦÄŪëBM˜Ī/0Ėv~dƌ›€lr<æ]ŒÐäPč€`NQf€S0f ž0ŌŸ' û{Ō>ŪĮ, (ĮRË Ó$,åbĘÆüŒ9Yɋųh;`‡99)GĶdn4ËUpž Į&įež’ó°rÚ 'bP0\ÚëzÜü™0ú–%ƒÚkŠ—oSæ‘f)Ũ^“VzÉB*mŠ ïôᄠ“c[ąyósÔļWuëÔÐvÛtR]ĒM?ʰ‘_ōOŨ O?§%9ÝtĘQŧüč6ē˜ōlĸ=ðÚgÓ4zrķn]Ŧ8ĸ îįĸī ĸ—ĀđAS-:-QčþL“ųĀt―gM+âtGį0œÚӔđMƒ„QE Ó"ÁŊ`w>Kč˜Ģp@>œ!LŒ  ›0@LeŨ†YÐċč0!;$?ŊņœEhį{ĘėÉž`EƒH˜n]Ž ô4Ō(ŨrđÐ`ČLī­ŋ]>ĶP@POĀ˟#–zļ=ô‚lG@Šžūž―°GūS}ˆŽ0MÄLíT§,76ÎO_ōĒšĄ˜_)‹ör –î6‹%Ž'dN…<āpŧ‚ÝR“Ŋ‡yē'mc^3tī§Ė@ĸĸ‰į C‡6þiēü|Čōŋ“§r­æ:ü”ģ~ūøōĢĻؘmÅþPRÞØĸu û1LÁˆPûVxC ,Vîa.õņBæņĖū`“Á4ˀ žœ\ïĸĖđFއųŌ F=xĀĒ*ƒmųú0ƒÆ>†’(ƒĨ9Ę Œ-Ųo§™ĸáĖCŲ֞ó30FŲe ēÚsšv03D‚@S—)dLư:ØQŌ~æ öŪ&ĘÜ!Xãrhy™ŋ gËMÅiŪ pUēÄÄđðXKJےāIÛ"bRīÁ§UvH‰yZ—›€=ýÃuđÎG?R_˜Ė]?2Æýð=á|xÞÖÎà ƒíÞ |čó”#Wj.@̧~ÛĝĨîįšŋm;Ęk+ïōøĢõZ뗓õ “b€ ˜Ūᕉ‚.00\É2ė!k Ķe0MiŽÎ °%œ`gū$ūó‹ŠL–MX•RŨâübV ‡YÔēāŽå9Ë ĘSy”ũ@yl>=°Á9NÄL˜—Ās‹ĄžmREɇģOxn& :ŎŊdžS§ÖųQæ>  HĪĖ € JœēüŅÅú;ĸ|ŒscŽÛ„Z€0―’ˆÁZbđfĮ0I4}Θk@fį… ŧŒēËcŪ†‰Œ1((6úęM―õÆŧ:cČņöæÞ”Þ˜?ŠšüKy”ũ@ylTü*p&N$Þ;Ōû_f8ˆ:āVėĒ –d‘d›­Äđg5ŽÔÚ0Ā,€.aĐŽ—ī`€#ėV˜01X[r%Ž eJ^Ā IĀÐ8˜“īIÕĮ1“"úŽA Žz>ĢxðLĨ0€ĩËfŽ0@uY\{`ÚŦ5)+qœÁ3/ĸˆT‚ŨŦó'Ė ģ*ė•šģpŅ*’Á ž/WÁ$ čaÎĶ^Ā3I‰Ė ð›QRåä{VĖ2a·ÁØ,<€}ÎLËk€ŦÍū”˘A„ĸ‰(U~˜^c`ā<Ņ—îĀ?q*J™úĻ7€Ō€É9žzüŨGą2Đĸ/ĸÄķboMõR‘úęđg=ËG/:Y ķûôÓŊõėM#T―n#2ðPŸOõÝŊKątÎx}ōå •VŪĐ-·éĨúįŧ`Å"͞ŋÜũ'īlÕjÐX5s~üZŽZ4K_}>ÎۊĨŦMįjÓø§į‹ō–iÞÜEą­X‰įŌ+U­­ĶMęømųmŌŨ/?ŊéM{ißŪ › 7ŦZž­Øōä>z˜éũЂ4nǜ ŋUïoVQ.ĖoÐ?ÖëUˆBÌ#d=!kÃ3ÓJ.ŧ",Ã3ŒĒŽ™sôEÆ%+Aģ8ģ4+C‡ke*…ŦˆˆƒĮ§ŽÔg›$ s˜(t@pÆ\ úÁPØæZ@0'rJ?žg jÖý‘ētpEfžZ‘s+)Ėŧ°8ŨŚČt˂cSŽw4c…€aöĨÞ4ģAíĨžÄĮāŒ71`är#œwœ•>ˆĻ<– gŠēØŽ3Ã^Į˜ĨéĖ·ėĢÉĩ%[`&&U6aÎī‡'ä§ĸiÕÕØýj€Ķ}4>˜Ã2YŽÂ@ĀfĀA_b&GöðjĶ/]Ÿĸ$ƒËœ™æđÓ2KĮĐoS§Å‹Wjۊĩo ãŋ07Ė}^é]BŪŋņ6o^Ą~}ö·|ë‡ø%éŠôö wëžóT‹{)}ĘŨš‘^OŨÞsŋvmUS‹f|ŊÅéuÕķÉO*|þÆ]:õOOk‹v­•Ÿ^YĮœwģÛzÝĩĢŦ5ņƒgtݝŠQýUõeą†Þr§NëÝäGŒ}ý{é Úv§m”íå= šėŠsÏ9Ęë0”m“}ųô‘ëôŌî·ýaó―·čÂŋrėáúZ•][—ŧMÛĩ,öūÉļ?xÁNúÆúÔĶ=ĒÏ›6QØŽËÄÂlheœáļ­huX[€™―Ba„ą“J,ŌGđ'^œįžœ0ūL;ëõ(0M1-@ “)l31܎đQ˜aæRŽĀĨfRwõ>]õŦä9ėūzu䔟į‚đ3TXŧąÎŋüÕtŒÜīŽŠĘö.#đ~ŪrĘä–Ø’QčPv•Ė„V9r{ŪMö™•ŦŠz%îŧaķ8d8FîŌ%Z]ą†ŠWÎŌJoQVœæEûÕ―ŽÚĐÄÕ4?ƒŦV,SÁęlÕĻ‘ž +ĻIÅdڄ|+—,Va†côV#_’ W.Ũ $ĐP9G9•’ļļe§~įŠ4/Ŋ‚whđDCú{[1ŋ;m„đáwÞęrņĸĸõhó‹ 5ŠēFŅ&æ=œPėÜ@`‘FÄ:F›$ ,ĘP#üĀdÍ}#’?û6Āâė‚ētåĮ<Œ#pķT)€Âk7æí §QĻ—ÓÓ-`åŸÄņÅādýŧŒøļTPیŠI˜ŒŨ°á@ÏÚ`ąū.ĄPaÐā†ĐÖC@D@ƒ2™‘/qlĒŊĖâÂ\ËühĒ炉ŧ0pļÞĄJlYōēÓ ĮÃéĮĘ5քš^„MxÓ&ƒ‘h_Ï'áûøžŽ5kM͕Ō–-BôÉqŋæģĸ',œÏeyaôÜŨMqŲVlæôézþ‘č_Ï<§Ïūœ Gôķb͚čĻãOÕĘXڋðŋžÆŽyIŸÔÝV—€Cõ9Hzýöą­XÞLïĖņŲįzäƒw4úš‹uá…ŨjîzAfKÝß+VæŠ4§ķÚ·iŦ:9þ(ĨyïË:Š“]Žų?Lô~–%ęÜü§ĶZÖWŦ^M7PýFÕ nmMiÆÛũT―=ÉÏņ4]2øh‡œåÏ3õ—3NQĸû{ÉõĘ·XĐŧN;IgsšN;bu=`ĻîðŊ:îˆ=ÕģãĄz8‚–—ę­ŋÖIįœ­ĢŽíŊšî aĸxƒ€|ˆ„˜īÞ.]ĢþuģîđŊöØï`]ũÜXũļ4cĖģļũ~ęßĸ ŧïYånú1’kýíRЧ–/ÏUZĩújŨšŠ–›iŧÎĸÖīAāĪ?PĶá8#4Č@qPØDßœp JXy™ĸīYÔJ;™k3ã1ˆZ§ a‚4ø°øßEEŠ9üŌã!JY gĄŊ%Ī`ÍÛŋĘ jŪ”DÐAÉûĄë(7‘!1“b'e/N°6a,wDr}˜41F~Ũ„ãPÂJÓ/ÞĶóĢ€fxĸæeānü@ĨzĘBfĀ—šŦðjuäGvXĨr›aքÜÃŽš0ƒXBâžĻŧ‹Fú?ƒōÁÎí Ū+f@BnØēüāKÚėË,GbŽuekŪgđJâ`/ũ Âe§Lđ‰ƒ€ ƒŌ6}J7û†õĖĖKŨqg]Ķ=zčá;nÕõšT įÍÓĀÓÎŅĒėzZ˜ŧ ūžQ­ēIžIãš?ÚVŽ.ۊ­ŌĒĶ:vĮ:ðÜktÝM—yÉu‹Ėðî"Ô­Î]á-ÃN―ājÍþ9'Þž…zãé;tŌžû蟟·Ōą‡v^·ø\Ąr%M|ĸmzÄ :ųȓõïoVĻã^ĮhÐË5ô ĢuĖ1'jZÍíuBŸf~€ŠéXï―ųÎÛ#uzÎx=ûþũ.#]ģŋûL3KÛéŠGč€e#5üųéú“ũĖ<―Õ\ÝņØ;Î“Ķ…3ūÐČYYšð–§ôė'jÄ5įëĢ……ŠˆY>Ó1i'=Ŋģ/{RĮđŒ'.ÞA_u―Ķ›ÉŽšį^―^q[=úŌH]~âAúQ7üĪ5ŋ·YÚŠKGUXōŪ.Ú ―rļ–mÜĢó{khđž›Il8Qč*`”+`0Ķ0+ØJē”ÁfB+sØ,æ™iĮ!=sw(dL•É|ĻY)@č29OļXū"ų`aö|.–ŊĀž\æYÖ`X§@Å@Aáaržüf†ÜČ^ļĀXÎákēíĄ‰đÃąÉ€KYa“ČdģÛ$ąũf˜‚-G0fc(u°ü#aŽ+ŸãŨ’ð\ÄbžŅu 3g|ĐSļų/ ‹7p2q= N`€ôŦ:Xžjvð`ƒö`ūN• ™c0i—eĨ:ôAD52ø" ƒdĪ/ļYþqMŅĮąU”ŋ‡ˆļ íb.F™YŊ…>WUÍÔfŽýLÛx‡’Ö[ïŠefĢ…Ņ—§ýŌĖ6ōVųYZGîŌüåZšūZMĸ{įhiQÞïđŧ·lca)K‘ētdĪ„ŽX""Æ*vEA{ŒĢąũˆŠĒŅĻI4úWŠ`Ă4ĪėŌD: ÛnŲōžßœo­Ü‹š(Â|pũžó•)ï9wžų―óÎĖë”iģûËôYk—™Ó§ņÝú›xđÕlÁõū”·ýë‹Ëü?UÞĸ-!ö;ĮÔĩĘŪ8ļžõ“_(OÛáŌōöwũ{Hwýķ[Ž8úEåČĢ_XvÞŨęĪ™åyï}SyČÐiås'M+Įūþåe-Ė<ķøĶō͏ŋēøÔį”ŊšĢĖLäðĘē”õ ~þ ËķÍ.SŲiä€CŸWvØtÃēŅÜĄ2°°JåėœyāSŸ_öœ3ŧlŧįÃËz4ÝļĀE3ø.āöŸĸ“ï–…7^SÞwô3Ę ßþý2zįŽēh˜1ãW]ZpF9ôą‡”ãÏüŸüýé˜TæîũŒōÁũū§ü˛+~ü?ĘĸžóŠ Ú)lGģĀęīĀļ?Á ķ ŧj$°f Ð5čˆŠĶ  ―‡ŠÍ…‡Ï ŅnXŊWŠ#O”§0BU%æŌpšv+܄„naa,™ÚAĢ71 *Í.ðĮŨBCˆÖ€ÔŅš/-oę@ję‘z˜Žíģ / ļĢhŦâ­āĩ“āsŠ:;ĶݍšĻ€Ũ…œeģ ,ƒó―įuGW·6Āīū(ÜÔ՛ )îIð!j-GØX[ŨŸŋÚhU™QīÚ\[™™aó:Æ+=ŠÂ§ãBZÓgM$ %P^;0Ņžššúœ#ĒđŅ7ũ⥚ýÍmÅķyčÎå%l+vĀÓýímÅĶØÁđgāÜfŧ]ËĒy?/_9ũš^ÉĮĘĸøŅrÍÚ—‡l0ý ožfÁÝԊψmÅæėŧ_™ĩÉ$tŋ‹û–•ëį_QŪîķY^ÖÛhËōÐ}UöÚqƒōÓó.ø―mÅė͘―^yØ>{–ÝũÞ―l°†ßÝåĪN,?YąuŲaãģʗūzJ€{Ō+ŸųÎÖåóŸýDyō\ķã{ã'āŋ+FŨeĄ>§%äëąršņþÍņđŅņ[r î^ŽÅ7_Wé;›žôĻP„ēÁÖ;”5·-Ŋúā§ËißųVųųÏ>PĖž kÎ}4.딏―`·rė›ÞWūĸ+z'ũŦOĖāīēÍþûe[ą;nÆ=Žýo\0ĘýŠš­2U øŨ}ũ ہ%6ūq_ŌÓÞ@đNO ĐxކßFDØøŽũĻÚ<:—Š ķctQKžvŒÏk*ķeËýÂÞC‡D―Šô„o@›Ïũ>ŸČS^Ûäw€‚c@5H5ēTXeRÛ=üļcx.ŧ‰P]ĶĐ/ķc”đĶj3ë‹â˜*ž:ĨĶyŸõ>éH'J&āÎQ|@Ïō[w ^ŪšN­­.bóē#’ýMIŦëŽŲZ[ĄčēƒÔēŠBɆqcÜÂĀëÖÝžS.>—|F―2šFUž‹įtŌZ'nĶĖũîáįŅm+60eMv>ø ōħôÛۊMú#ķÛįéåŋØVė°ŨmÅúē­Ø‹Ęql+ķÂolßG”SŸytyõôW—·žâˆßŲVė åļÓØVė–_”wxfųčģëķb|Ų!åÆG_þÓmÅūø–ōþÞķbĸäŽrĖ>øûۊŅüŲ™_)OzŌ!eˆčÛý}yúƒŊ+ĸúņ›ĘĮūsNŲíÖO•xâģ;ĢËŋ‘rŽûŨc;ŠŧÔÆŲFX—ŠP0PÆOÄMŠ‚ĢÁv!Š+pīÐT1f,Uh2ĸ“Æ\0ĪaGą ,?Fq{ū ‘Ūûz nÕU€  šsĒQģ 5@ €žåžLcðÛþč $Ęä'eR-vā3pIŸ^ĒxQŽ–YũŦ°ËíÆšZvaĨmŋ‡ų qݟ–Ųky–ëĶãû•Y€tĘŅ:YrËäÏoï&#$4?Ÿ'uŌVáVũ­ðÍgÂuïu|6‘Įä5ė3fĀϚú† ·*O;ė/qŽĩöĖēģLnđôâ2‡mÅúą3sŠë–8þíķbC‹o.ëÎbũä˜4ĩėsčëĘĸnšÛbÝ\ą­Ø…Đũš§•ŦŸsL9úĐŊ+§ēo2ŧüör “Ë6{03t=ۊ=ĢėąĸÃËÆkqĮŠYå˜w팭ĩMrßá‘ļ=g^Vģoé‘ĸī{ŲóĄ›ý^Đö|üŦËwķzrV:Ō–ƒkmX6[sĪ|ōësĘņgķŅ‘åsĸ·[Y8°aŲnŧŨ–/}mïrýðīēãö•ËŊžūĖY{­ēóĮ?_76ŠxRyå§ūXVĖÞ"ųxôgËN+7Ėë‘áIeŸĢ_ZßifY4pTyØÃũ+†œøÚO—}flĘĢ3Ę ^óŪēËîg•ų·/!`i‹ēQï{ōĖ20w^™<­ėžŨūecũB―ŋD.oŋïËãŨ`[ą™Ï){ïĸ0?õ[ūayÕ{N(g\?ĩĖ_ȗŽÍŦÉã‚Ó<œP, xínWŅĨ@4 М'𔆨ûĒ4ČTOČzĐž°™° `ŠJ‹ ”†ÞiŠÕīëĶ PiũMa#ž*øWp_ úĩŌPæ“ų”ĩš\q}2.é~š–oĀeĸøÏr%XĮP\äõ•ä/Ží0,sģkō”5ZTũ”eTÅP!’h:ĩp6J֜gIËēÔą^•&õCMÆĶÔ% äýST•vNėhä<ÏŠ ã_•îØ:OUHzh;;Ž; T•x: qĄWē PÜėĶ‚Ý-Ŋ; $—ũœ―ŨYģÖ,=jWķûqųîÎŪæú\ýŽŨ[wVųû―į–Ųëĸ~ôęoÜú;/ûʎŒųíØ;ûô§?ŧ|ýūVn\ģ˘>Ģėĩĸŋsŋoû—ÜŊšíï\4sķßeÕÉu7Ûą<ŸņŽ™ëlRöÞw“ņn)svÜuÕõ‡îĸčōÐÞŧM7ŲžūZ{îŠëoĸëNÃŽķ/Ý ÔÉK7+ģķÜĐ<üąŋūŨ‡Öģ}aĸéz0uį‡PvîÞó{Ú6;”øđ“ËĶsŲô·MBÏ~ {ūî\6^zuđrAõdÝ?ëßjõ—ķĀ„āTŲðg‰J šĻ)^ Áļ ižWĩ‚4Ęc4āŠ;gúîÚÐÔĄĻ4Oéē*úŌrŽ&JŒīŌuú‡ųŠęē#H`dđpÁöŪ%*ðė# @J°eūš~īa2U9NXĮßėí2&šĪ‡īęî(âģŽZŨځHäį{mÕ…č`dų>îĩÎé$·nQ3ŪkâĖCū$lý―/.XæŧNæ*t-―Įú4;"–ē.lŊšįĢĢlÚÃküX•§‹ŠŽUДåíųQ6ŋÖÜÛØÁPÕŧœÁB=3Ë―whŧíķےŸ-&ČäÏÃø”Y›–CxŲyüí]>č=. ĖüÛ+ø_ąÄÝr Å"īŽïgœ6Ô.H „„ õ`ĒeuVŨ­ēA*&ÆĖŅ “{ęIååq‹F…’ũų>Ū][lþŨ―ðĒAcƘR’|đwˆÆ_Ļ&ا—ÏĻÓb€œÐ4FÔUp„Fį~Œ‹™„ ēĐダžë)Ģʒô2ķH]ú€Ķï]„ v ęø ŪMÁį˜ĐKÂyčŽu>§`ÁÎKTé N p3Ÿ”óšhÝóÓ:8õ#u`ĩN$D―âbôŒËЌTDýuČŠlUĐޛ-ČHĘzŪ.”āĪÞg”|]‰(ð­Óہ?ķŌ5íë°dé’UŠÖúýeŽ?Œ™2ÞũrYcíß\ÕčūWūVĒf‚* îĶĶ6mÂG(Ũb *Į,uþ+ũŠ Ōų“ĖĩķŲÐÚØ $į[šFT‘ï hņ?{zᾂĨŨ„K\ŽŠ!ōķqÏząÜgÏQ@ŦïZð‹ßL_čPuå8a76+œ‡õ^oī Â9 ·vTšÖAX GQ Đ"4pHOŪ Ũī}& ōÞĸļ‡f@M:1iՋ"ÔMŠMŌI°ū(ËĖÕ$-UĻķ1OĄ&ÏŽãJęŊ1HŠôę§kčfA: QÚTÞrøyØ10kmUĄčgWĮvG4ņPĨ֎@=o'Â)8qUįéÜÖþihhhĮã*NŸŦ‘Ĩ4æ4ðq9ŌØÛ– !Ę€W ­ķJM…jƒ< ØB4üžGé:Mã …F—ĒRmȁĶÐr\q€ę‘ĮF…ĩÐt’~U^.Ī.Ø,“`fú†ŨņšDuL3āæu1Đ:Ā0!A5‚Ōr‘Ŋc–YČ ŌģÃā9§ÛD- 3ōÓ&šGT|žēÆ­ŦjĩÜæ•ĻUËDŊŠÓP€ŒíŊŊ― +Ð…Ņ öqü/jQ!ãTOÞ+ütEŠž$”c‹Š'Užð žÉG•ę”Aģœë™Ü/€đ'ãv”3Ŧë ,SH ÓĮ35ˆĻNį°ü–9J˜čS ™Đ.—@БķŽŋ:eFgu ÅŦR3a›rRnÝĢÕU š~`í5Ę!Ī]Ð ę™tKŒŽI gĄgūą5įęR~ԝkæÝđyĩ…ÏwėĪkW`›§?ÚŅ•“Ž*9KûQvUŪĀÔVąŦųR.ÞīĢY Y Y Y` Œ NÚÝ(+Ģ4› 4ĸšÂ„Ö9į]į5îJháRĢ:ë6[BĀÆÓГ† nÃĨŧ4&ÝÎõÉĐīßæ%Œ›ë éî"Â*îXŌxiðUO‚ÆąFĻXî5%ĒÚFĨŠŠ,ŋéE•ņl^ólķô2ˆ|[­ZUjŪãXĢ`Ž °3šTĻÕ)5QļæM°O‚(ŋŠTĐîXŸ,—ƒĒŌÁāJ ŠË˜ž]ŒÁĻe]ĘąåwLsDw,6qÕ"]ÏYh_pR/Ż™‚CGĮÃ:‰Ė͌;ëúŪŸÓö ėüŠT>·ÆÍ˜­ýÓ,Ð,Ð,0ŪÆuÕ L… Ý}ŠĨ( Ą&ä*ĶFq 4ŊgĄtŪŧ`ĀQՍŪqÆ6ØŠÕJˆp―FŌ F~ ėXi§‚&qģå°ąŨÕ 2*UZLŠI‘ĘKðņ>cxäģ‚1ĘĀ*eŽcī*Ã~$™―årqUšĘMręŌėÆGUmõ>näšÏûlԝāįĮ|íļ\ŸyGýb“ķ3ŊYWŠSČ8c§€Íé8QœØÕ<„Ŋ‡öÚFņ&8É|Ē&§Ī\*H﷓bÚ*XaĐk7—”ÁúsaL^‚Û:ĮVyŠýÓ,Ð,Ð,Ð,0žlýĮ=lėU‘Qš€B8:ū͚áUvA ˜luÉöĄķFYūÎ7Q54ŌuŠę0―A)>íó.…ĸ !!„‘ĘËüĢRQR>及2 ĻÂ(“ųä=ÏՀ RW1ŠŨ ïŧ ĄtPxŽZÝÏcÔīĻO7N)áU–>ŦŦÓīkĪρ9§s]ĐeīĩNÖŋ›Đå!Ÿ(ڔ‘īPŦŠfÕ ÁNÂ˄íŒT\úvU-ĒŠĢŽđ&d…]uu“ð3ÏtH§‚•ëvlzJŌëY]‰{ģ!öóóōGŨm:õ#!Óv4 4 4 4 ܝ&§ãjšGYH[xĻĘl„LUFd,ckB!ŧ ;Õ6Âuā Ülô…Œ)BQB Ó4Õ5m…sĒMÓļWŨî XëvīĄ·BFĢÐð<|É5D)ð‹[”{ÍGˆd,84Ī2Kæ`ˆHÔ(k`DâVœÏ QŌÏø ėsĖÐ|―?ABšŪ)ŋŪláh=ž–ąIōVÓīœĻEíæ}qĢÆMj§@ĢØĢųĶNtLK°gŽ’2{=ÛQ—Ž­K S6ËĶrÚÝ+ëĐm|6õ§6.hïk·hËįIū=åÐenDpįžūŧ/J;ß,Ð,Ð,Ð,P-0.8 Šįešs‡^CþŽó U8Uý8z°„ās>Ķ ·ðēAŽZTՐžŊu?f—óëÎ5ā(kģrŸ0S9‘āęF'ÝÞeķó2-AĮîÕ!APÕg"O)•ĘQðXNóĀ–ÖU€ĒIOX /ĄÉĨ,Þ ŌŒä―nd„dՕät2 †ë‚ÏՆę„Ë ^tuAŸĩœ*[]ĢÂTx{>ÅNqŦōÞš8þé―–ËͰaĩļ ôAŅŧĐįtÁfĀV[ĨėČŊŸw;ššššÆ·Āļāt’čžīuoĘļUiĒm UCÞ#plĀmČŸkÄ €l`Í9PéÄåĘ{ï•\7PČܖK5(üĪs#ģč:00JÕēęVLšAuO ‘án|TwސĨāTĻv €ĩŠXåÆ9ēČõ(i•5€Õ};U€€ÏĻ^ĄMSnŨðM'AŠyž‰™Ū'ËŧžÕuíóvLŽeÖVQÚܛõr)ģ0ŧĩœ9žœ‹r°ÃK‡ĐéR?―nIæâízĘ-ŸÁF*RáŪŧŲǘOV*ŠÞ€ŪNđ8ņwĶÝŅ,Ð,Ð,ð€ķĀļQĩ6ð{ã…ãz°+ Ht)ē ÔëÂ(Á% ÁqN•Ķ wÜĪžXęY8…-ũŠ1.ĒÐĶūĻ!`,pœ·™g ęH …Ļ5ž›ÔSJ‰ž^uÕ"õĒęQ—#ĘËB ~ŒB;ž›âŠ@üüBÃįVz.å&ïåĻ6ŸQw:ŽZƒ ‡ušFU­ŽwúŽjēĶ‹ŦšgG†Ųd˜‡ NRY {Aæ―ÏŒØÅąFÝŊ9xÎēŠZ―6dW Ú3ÖJ9QžËw ˆÝÜEd ÐkX—ïó鮞D:I ÅiÚB^*[—•x ĒN{u2_•ĐęŲ@(ėÜÚĨØ+Kúl;ššššÆĩĀļŠsU3ŠęĄw›-ÏĐŪl uýĐmÜI7ÍĒ ÎņũJž3'`ĀŦkSeĨI:< d樛ĀqŠ„ŨhÕCu… $|ĘoŪ;î·.[pA D­JŌņBÝĒrĀĀ&A—û)Ģé(c:ޗ―:ã)č4ĸTW&e$Ÿ°„t„[Ü <Ëcõ<å֕mtŦã―Ú$s-áuę@rþšUí4TEw,Ïø^[øŸÞäļgyÆ "Ý­B’"óū§Ø3:[Ŋ;f[įmR.ō5 °jYDW RÉŧꑟ‘õ°Ģð“ķc§}\‹ĘæžŅĖ~íhhhhßã‚ÓGU_ĒTĢ+ÐÆZčðO\ŒÞĢęÔĻŦ֖^•fcîę:™ĘáýĻÓė H%nMU īUwņĻīå=w%ũå:iŠ:ýOUƒ†„Yuđ:Ž âÚĖueŌí[ƒy@5"VX95FØYnę3ÕÕK|düá_ęC™x“Õ“PĨŠíč=]ĖäcTŌBk”g―?AE>J™=tŲĖTW2HŠŠCŊ XĄ<Š‹™|uņޔ‹g3VJZ–IÛĮ‹*OQēĻŦŲ°L|0'QĘY€Þst óí"‚ýŒ ĘŠvåfŽúo^ķššššîÆ‚“ķ8 Œr]4āŠ+•–Á- °Ąņ֝)؄†m9x žš―5Įlž9l&Ýë†ô>8šjŪ!™QC4ôäcƒ/žu ÛØûZe+oÚÕÕ+$‡•Ī ĖTąߍ‘=j PōdwWá?!H….ë%TŠþĩÓāøĢÄēŪ–Áú ÝļMŧg)G HúĩkŨršv"‹UÝv8„ŸĘRĨi&*GëāĶmįA[ÆĨŠņ9ĀhŪéúķÓā5ËāøĨ.W푀*~gšt\{7{mēОŸņZ‡ZĮY}֎‰y ív4 4 4 4 LlqÁ)rO>*7ęÚŽ9I@u2ŋQĐRQõԁ@ ųlîĢ·aÏx €ĄŅVVx ;ßũT§ķqWáÛũ<+d(“ũvMūc|þčžõdœš‡°þĶSĨæ§4ó|ĀÏ[óQ•Ū‚*ã”UoīQđ>oãŒBÞCð›†ã…Ð:[·ĻgŌ’™Ýē /ó|ŠU+ž2…į°4ųU@@Å―ŠaĄĨÍ–ŅŽÍ7€åšïcĘg'Á2h›þA,Ÿų{ĘYŨš°ÎÁ=ĩ3R;6õdû·Y Y Y Yāî,0.8’jÆÆÞ†X7Ąŋ;0Ø@Ũq9A  ÂĪūwŒ.ãĄĀIwe"QI` NąĩŊjĖ{+Ü[ôˆĘ%9v—ėĘ *ŦËQÅÄ―4<=xđg§{„ĘEÓ đGčyģųxÔE,‡Š’2SNĮY3HđTÏՕ+­ŋŽ`ó―Ū]ĮP…ŠųĄŠ&ëXŠöÓ"–Óīī ÎU=Ō)Œ-,(Ķ(š;uĻ{;nbUholX›–4` yMŠ~>”™ņQíEJ]đíæŌځð3UåÚĐé"˜MÏ;ÛŅ,Ð,Ð,Ð,0ą$Éļü !Ŋ‹”;Ö&@tV·ĒjP('_ŊŦu›ÆŲWD šql.S0H<ĒCæYĒI…—ũG‰ŅØŦ"-‡Î`į+Ą5$ú8rƌ€t“mÓnÂ&Q―Ī›Tƒ`°Â3Dš‹yå*€xÖ(Y“-ĘļVASÓwĖVę2@uõrŋJSįnæOæųšŠOėÂŽÅv 2ļqķ`4ˆHŠī—ióâYm`9Yö4%?ÕĄhœLÔq\ŪšvQ’ÖW(ŠŽ―?cĐŽð$'ÆNiOfŠKVmēþ֑î’ëĸVs|-—ÛŅ,Ð,Ð,Ð,0ÆUœ>U„[Ņ߁&€ēÁWYК\8@•äõ~hTZ}€Š īęŠĸuÎĒs―W·Ŧ-xÔŊië“îĻsiÜMKĻŠčž_ØđšjÉëĒÅ9>ŋ‚q<ËVÁ€ãžu… ïSČyo‚bYŒðõ^áAęeÜ(TāpĀîī•› 8/ԎŅŪŠEïKG€tŸ‡ē,nðH[*o…ĶŽ9݆_t*ę{Ÿå ({ðŠ1)wōå\§`ĢRÉŨąTĮ$U–.š`@–uš‰Ä5OláūĻ5ð‰ å—ių{ājÊčq1SŽšĄ5iGģ@ģ@ģ@ģĀxœ6ęšc*@FubƒŽJSĩŲÛ` Š2ĩAķņ·!WõE…ĐQttT‡Q8<îýÂ5;Ī%Ė„Í€éxĒĀ5+Ģz%Žũ“DY*Ü ~!/Ué˜iĐīIïô}Ub)#iFō°ĘSÐy=ã™ÅįúPg*žĖņĪĘšBLÆ@Ą™åÆ THiĖË\ŨÍ;e*óE]2AÛý ;ÕģZ9æKš*ūļ…đÏrŠXÕĐ.`Í·‚ |~ õČ;Ū9nY]ŌF Ŧ 3f‹bvá;+v.8 „͇úEŸŨņPímÓļîį§ĘöČØąķâ―ā·.íhhhhßãšjmH…Š ĩŠŽfPŅXóšÅ# âúó…J ĀĻž2–fãMƒŽ ķBS0āb8“âbí+ÀO Ų°ũ‘Į2, -•™Ó(Ļðulo„E`tÞäkš<'œÆĒč %9Ō[FZXĀZfũ ĩĖ*+!čÂčđŠ‘OP™Ŋ #ƒÔgb"` ēéÕÉ:öģ0ĮuþfĖR˜F]Į‰æ·ā1`ĘrЅ›y ^į^ \ÁXUŸ6RMÖ|žĮNäCú^Cߒ'ðīĻÍĻa:/él`/ËlpŸų8v:RóĶā1+yšÞna>ŋx8—9Ū)ģ oÓQōjĸ4 4 4 Ü ŒŦ8}ÞÆ[—ĪJģ‹S°Ž°ąÖ?KcĐ$6xđ?d–Ŋó-?uœŌ•yxjģ1O0ïQx*C§_ĻN š‰[SðAŠxéRM  *•Z””.FÎŦ(Ģ~BîuĖH\ïJQ€Ŧž%zVĩHžý€ÜûG‰īķv›SA|Fv[f’ ĢbIg,ËÕéķĨÖBņZÐôœPĩúÜčH]ÎÏ (Ĩ$ÕĢ#BðO–ÕŦ‘ķ>>ϚD•ZG•ī6̌uĖŌąaĮ•-…ōũ Ú>Ējiv9ķ)tKEŧVhōųUčó,ĨļĄÞk™RAKŅŽffffņ,`+:ÁrĪá—.Žn0j*öĄ Ū`ĒSÜ Œ†Þ "8@ÓÅx\ÝŽFyf øL[œĻdŠåvm>ä|YŨšk+ïuÚ{­öáÖLĢážųķĢY Y Y Y`b LĻ8mdŧ@Úp6īŪ O…ã!lØU}‰âÞ߇v=0K$, }U `Øxgü˜% $…Ž.Ée—k·[_VĻęnĖô Š`ĪkÜģ)KęâĢ<ŠķJ8Áã1ėĶڔ;+rXĨ)„,G°LĀ2ē3ųôāHŅĻķÔ_ej*@uĐjƒåŒË ';‚WėĪįģ5Šx3–l§jĢļą·åÔVT‡4uýVĩ˜ *Ōü‚Þ„Ī|īƒásÚ-“0TŊ ļÔCÛĻLYkúzÚŅ,Ð,Ð,Ð,0Ą*ýÆđÍæ4cm4šŽĨŝéx J‹āAcĪi‚ntĸĄÄĒđnÃÜ42‡0y Z—ŧðS- !˜1AzUYōŅU‹Ō”*ž^qIĀĸÜmÄqBáw,iđĖžũ hZ–M0›,hđŒö<"&xsO$^Đe0B8P~Šģ“ øtŧĶy <ëŪQîvꇩԾīĢõ·ŪÚ@ŧyÎ1ČÆvHš2[ŒŧZæU`ŽãĘՕmĮÆzTĻvuÐVÕ6ŽÁĒāЇïų‡úZ'§ÂØÉĄ\|žXŨ*ķĢY Y Y Y` ôHô‡ïJ;JėJ<*>ÛņāĪá§ą86ÞŲÞ XĻ)ZaÕQR\õpøl]ÎŪBĮ[`#HuÅöqÎg…ŠnT՘.Fï~QcĀ`yŠũYJJŲjŧ(ÖŠj―&TM#ÏswŪeÁvm7Ý<‹jS‚Áõ%-ØyVPúū‚ÞšÛAÐM\Ë(€*Øęԕ:§Õ2’ë˜ų@ËŦM·n–bēZNAo}Ž RŠŦQDĘŊJUĩĒ4IÍ2T·+™—ęąõ!gōĶcƒ]MėIm…+ÛēĨLØ_ûô8Í íhhhhļ; Œ NÛX°h€UJČŨIö<*4i đÖkŒUm6Ð6î‚KÅãRwnuwUšnUáãԐęFŽy:Đ:’ŲF …ęģšo―_ [&_›QæxōJwĶGÜĻKДŲē ĢaUvŠAY ū‚Úš™jŲ―Ũ*cįī.'ßļY}RŧŦJŸõĒÚ[õŪâ7b9@ÆYM°›VxĒ,ÞŦ:ÕS`ĖÛëގffff‰-PåĘ8ũE-ö‚[ĪlÆŌt Ģ:mœuŲšŽœc•6æBLHô îÉŪqJš…’ŨĒž˜„Ŋ‚ģņ· ŨmčýÂ:Ū]îũžJ,ŊäïĨjtdd8 J` ĐTĪ’rj‡ę.ũĐęŠeÄN“qōÆ‹îÔŽ—ŠĘ ōōßŊ`ąóŒB_…7ĀÂĢt*&Y~Ōrđ>’ œ”áuŠKĻŅšUI w}ĻÛJÝ·‚€EHW š.Cl‡-ÆĨāóYĮ•y0ó]­[”5ïã§$Ú]õo‡ÃđĢFáۘŒ2EŦ4ÕĘ ^ō3ó^lá.õoGģ@ģ@ģ@ģĀÄœ6ĨB" .o„}uu‚<Þ{D•Ō >^Ũ˜ŧÁģnCĄ  ņų§Ūôę2K―ĐXUBĪU€€[VĖá9Įę2ĩÔÝU˜ęSˆÖߌ…ōå+˜ŨHž_7Šp—‰Þ›r(Ŧ Ú@ÔByŋú.TÛgu°ÎY›āÖé6€y—ã0nQ@Ģ+Ũ4ē16ŋ―Fá8—Ӂ—Ó뉀Œ™ęB™ ]YÁØŠ6œB:UeûļË ŽŲĄÐŚ|ą3ũđ0ƒõ]éŽ(ÔŅ< nÂqLĖū–ËqYŪÆÍ{XėŦ-tkĮÕÍN_‰Â%ív4 4 4 4 LlJūŧđÖÕąCÚT'ė ÕĒŪPo`ŽPŽęŅhŌDÓŌ ;Ū@ ĘžĀ.…Į›ĻĐėp>ŽLݑ4îĮóšų$A@Þw.dɘ&?"€Ð”Ņ€œŒS E`7 Ftȇ=gRŪ‹#ˆ?ŨĢÍJ;QiĀ:úއģÔ-j€’áe]*p šr Š:Úrhfm§JåPŽōcÄĒ MōQmbkZđUÐsŊ ãRŌLj―IĩĢnî!ņĖųšžŪŅĀN_qš 0ŽíëļŽ%`ėT{?oÛŅ,Ð,Ð,Ð,p,`‹>îaÃOl :ā@užž FÔĢŋmė73øĮF_Eéø_ ĘyU.VYāxĻ.X)āœE]ŸūöY:I ЈĒĶ|ķŠRJÁõ”$ĻĖOũĪîā:-(mj:šQŪ‰&Øæ!p„ˆŒ0]ӈzķĖŠ8(æÔÝĮ>yļ@ÜŅ―|ú(›å;ZĀōŒ CXWĮ- ōþž&/3XÝômãäÎÅ*ÜTé˜: ߌbŦĖÍĪ\ü~ ‚Ŋškí€xLŽâĻnFîĀÍÆŸFŨ]LlīmÜܕķūįmž3FÉóBpÕ―ÜT `üeÆY]ĻäTČļš99løMØī’ =i'‰+Bģs!›‡Ð1]AĄęr…œLÏøH š–ĘR…§zĀÅk·ŽÐÄ@’t<gÞ'ÝëÍ^Ŧ‹Ÿ3õ…2 :ļ ##mUÏĢ áIËîóQcžŠĘĨL–'Ķ,Ųå„ōÔž|ˆĀĶÅXÆĻGíC} &2čIH[áœÕ‘Ļ·å°<švB#üŧgĸęýuĸL]ŧ5ļ)u xYD1ÍLýĄþĒT)Žõé:™CŲųØ2ęŦjĨ<æÕuzŽēcšŋÍÍÍÍX`Bp&4ð6ÖōŦ›ī/ô„–ŪCváĘX0ôc šQYy/Ĩ2^ģąķĄ˜M‹ĸTnuWŠ<FŊIe”—―s- *t'āĒbtŒNx;>hzuî% ũᘎ@f^ĢĀLo8yÝg…JÆœŊ 2R-{Ī>X+cĐäkÞv(jĪmUÞĩœpÂÉN…åõyĄéØą ø|ĢuŌEŪ‘üáp|ŨŨc™<†čp_°þŸÍŋ)ģŸ…0ösņžy›§oōŽõĨNd‘kÖWÛ'X)ũĩššššÆģ€~ÐqՉ:eĻŠ°É}uMG2]3Ն> “v:S4€Ē€rÁ1N•™Ôíŧ ÕW- ŧ v‚lcS™.uÓiQNÔiސp‰z„šĶ3@ NÜąÂ{ûóšųĻ 6p =ā#džO0ŠŠâš RÜ1ÉŽ$ø.éa;ë,؃ģFRu?Žęķ\•ðâ^WJýč`XŊĄ š‘1`Mýœ§ĐË{Bv DdūĶ—ÄQå\ģŠðDâ’v€Ėuįpúž!šĐ<>ûXã7Kî‘PKŠ þĄœSpϒHĀčT§―8Ši'€éŽPÄv4 4 4 4 Ü ˆĐqėÎĻRŠ2sę7âą§#@8‚T’Äč žqœN$äT:6Ö.Ðđ: šVB˜ĸ„iĻž‡dqA(ÝĸŌīVā.֝™€n æ›< #’Â.h pļĖ5@‘ú•jī,.cĢc{Ũ|ÖN‚ÓažĩfžB†ũ.Ō  ܁uĀŽč@ËnžfĶŦO‚ˆ0‡cŊ ’JýȍÂļ„u‹Z&_§öxÞtüP~ӍÅmˆeéĮŽsæ?UŦvwLWw./s s^厚5}ĢnĩĢŠ3jŧvKïÕ§ÚŋÍÍÍÍãY`Bpzƒ ēŪCnĄĶ"ržo”Ÿ:·°Žá ĮņØīy ĨE3ÞSuFÞö"@ŲüZ Đ‚üŊŸā–1ũ Ā0ėŠ94ö*"U”HŧsevPÂ―é §ĘÔi#*ËÅ[7°vģšgŧ…âuI #bM74(Ų(›<O*.~WīĘÔ$ÜŎV +—ïđOŨŽJ𹉭9QąÜoĪąðŠĒã^Į]ę’qUî~ŠE―Ņ.XÁgƒüĐŧŦ™– ļžŸĮįŒĪÅøļ_u'ũįĶĒŠI+TíÖ`ųš*ģSžTjgEPûų’YķĢY Y Y Y` ŒëŠĩ^6ōYSÕqJ@Ũ-”ÁCĢëŽ(dÕø @…DQbu,2;™h ­·Ûr™ŪĮ$܍<`äKP”iømÜÍĖFŸĸ›ë­Ž8·(KîJq/S6]Ū–G åðP$pÅ „ŠA9ŪÎ#p„dōP#ÔÏŦŽũFÝIFþ7ÝEņ[ði#“uģ 3ÁŽ]ÍWÕũ,ÏõĢÐûQÖ!ŽéØi .ï}ŠEŸÁ đGØŧMší`^ ėē#`ūtVޟā·ĶÁ‹š>ŋÚŅ,Ð,Ð,Ð,0ūƧ LhP.F•‹ ·*f”`]–IÅ(Ėlļi“åQ ™q<d˞)*@";•pŊŠPĻũdžĒ,7aÖQD4üQ[\ÏÎ+ŪŪc0ó6M#ðō^Ā7,ĒM—ē€ŌŚép°+Į2ōvũÎ}Ŧzí;’ūŨëģ™VÃïļ•)§Ũëøh·6ˆČ ÖA "=n<éđ&-ÉV;b(į‡ęž­ ؅pĮr=eĪæŠDÁoYÄ5nĮÄC[xŊķēŽvRoŪ›ĶųŠč}ã?ėąŒēđý7Ũό2é-hGģ@ģ@ģ@ģĀÄŨUëã6ŅqŨŌûZWhæ"ĒRĻ[RÅČØšĘÉŨýˉr5ˆ•Ļbʔ jx]ŽÎŸÔõ*|ēڎ€$ņNY d••‘Š4ïĀ EÅ ƒ@€–ę‚ļ2›iYļptÆãæ ŽģŦ{2Ó7[Ô ũĻRÚ"|Ĩ―ðĶ āŠš$1#`b‚K™ëō~šŽR>:‚Ėņ_AëÂÖÕ{„™į|mīė0ųˆeëïa`”ĘÏE2Ķ"Üg' (UŊõS1fZ éđđu< Ė‚Iq{Îi+ݰĐ/'ýüšũÂÓũŠL åî+Ą/ŲĩĢY Y Y Y`b @ŒņÛÓDŋōBũb…(6æ6ö” 2uÔOoVržOÅdĢŪ"ŠciŽïU§ĒMÔ§‘ĩ<'*T…Y…†Ï#DânåuuûŌčĮ;ķ4pęGÜÂĀĨ‹Š­SL į„fuo’ũ Ŋš–ĘÔĐ-ÕMl=„™J3ó2đf ”āQ• Ŧä­*ü”CWķîOëdýŠâ3ßTãžÅåM‡ÂōŠ>íTxÔđĨ>KĩyāÚa°äŦMīģŊų(ÖōzŸ !zËŅŲÎũĐ/ķęķĢčaĪ9ĻĘ-c JÝīA;šššš&ķĀ„āīq T.ŠĀ–ÆœŨJĢÛZŲ`{> T!#\mā]Ø@`ĀīÏQ­^ĒāR6öiĖIW€čVtĄōĻRŌlœNÚqļ:!‚LBÄi2"˒gĘH:óäšðøļ?ŧš  ģŠâ&%}Ÿũ0ōvÕk ØŊŠË”ōZfŸĩîŪ{Ŧ2TsKĘaÝ|`‘ï0Ŧ YĄéóü›4ī•ũf ”:E‰ō\\āĶÝģĨeē>^ÜõsåUÕÞųLLËτ+üčŠÎ―Īeųýø—ô)ųãÆM­ÛŽfff‰-0ĄŦV Eđ j·SõØxë[YšÆ]—Īô˜nSáÁ挂ĒÓEŠ"L ­sĀÆojËÅG3.d}v€E„ˆāŒ’ĪąÏRwš\…FōЋ˜VåķbÏëÕ;@"Ÿ(ĶŽÃĒÉÛA–ó`Šc ’ąFĘĢÂՕëaPUžŲU~x^hzĻJûĐ­ė\xh3U\,ęÜÕUŅrï2ęÃ―ŠáUũ’§ų9iŦ.W…kËΕ+ø,Ģ…ÕîĩģáõІĢ`đfÅĢv†Ŋéځ°nƒ”Ygœš;ó9ųv4 4 4 4 Œk § ûÐ7eĶĶaU•ÅÍ(؝îIƒU Rež0@2˜Ep>Æôž7sy_Õc˜4ÞÁũ ™ė‚b&QžĐ΍ôÎ:OąŠP`žîWyFI‘ŽwT“%ũ…*OČ UóąŒ§d—Ž€uāš1ŅÁœ7ëāᘭK'‡þŅí-jŦPå‘ĪëoXL>@”úþ ­yÄÍ ĩT}ÂLõ,s?å°ÎT-+UĨĻšŊŪÚæÎéþĪ_ŽPčĐcąąŨŽęëįÂOŠåg†-Ė#ę˜åĸ,}­jîkĸ4 4 4 4 üa tøøÃW9ëŠ@Ë PÉøęQåĨZäV6Ė]{…\ŲĮ†Ûó*T! ÄQMzūĮĢļymģuįÂî\Žïë8ĨÛØÛЛĸĪŌæœïMËq:ŠÁ!č*ō–3ƒäéģ? č-jXĶ‘ķ–e™™sū։ī›ጚG5ķčDI!a,D}Nļđ§éf#nŌtŠD;.áy]īSŒž%K;X·ąAąe2Ÿt8ø­šô0ĀH]ĐŠĩL.ý§•3ÅDāRfT§ĶÕÏg—ÅÛ­ā‹œk~&–ŅĶ!Ŧ°ó‘āĨäŌþihhhÏ‚S…Ī4EXd•P(ĀĪU]€d4ËVÔņO˜”čQafDŽüyÉģŽËE#Oó,ā‹Ō|*ŦN‰ @A§ûUU&HUHŌÂ|ýOÕę €ą|\ dš iE=Z€^4íJîÓ}Đšs5ĄåĻĮ•{]‡ ĪÚ5Ï!ŌÕZë›@Ķä˝ÔCHQžŠĶSF‚t·ō,ïGí$ðFĩ'ðė|üX‘ŊΜb 2W"ß’ÚÆOŽ|âVŅR.]ŠBNŨķÉ4!ÓâĮqdÕtM•Šû™ŧ+Žâ§Ú];k'Wm­ãŸæÓŽffff‰-08uũÕhT  ŦęĐ.#7Jc홮-k^4ūFžĒžlÜm膊*F€!SK€Â îĩQŨ=UFZŽ_&z xŸĀQy ĢqŦ‹˜2é†6}“H°gŪĶðãœŦÕ2UUW/é —æ3ÝÝ8AĨŠã$@VY XHššƒĢĖēT` Ū0W*GÆsUj<ČbÓÕ:)·Ū\T7eĒJ”SЛķ`šÔÜ<)CĘĖ5ӐŠÂ”Iëp/oË Ï' aŧaYÁO…ÓĐČØ*eąą;ŸC­ųSUk‚°đyiK?OģÜōÚtÛŅ,Ð,Ð,Ð,0ū&gUS.ŌnDčJ`â~šrą}4žÕåˆRĢņ7ą@ēį~īwuÄø(JÓՆ–cs‹ó5csü›9ĄŦ–ó‹r8€­{žW°B‹tĢJđæžEĮaUt2OH œåäŸ5o)ŋēŠ/3}CE'ä( ÐĩuãŽV*5Ÿt..ÍŠâęœOÅYČo!dP”Ô<ūŨ-Šu —äjRP’–ØóšķÃŅĐe•́CQŌžķŪ}$;ęfß*iā'ŽĩÄ#]ʅX·xÆ/ÉĮtų•{MX0šūe­ŪjT/ų[ÆļgIÏeýŒt1ûl;ššššÆ·@•Rw{O Xq\rʔš`kæZ6ÝĶϓžw%™u#( ‡Tá(ãô §~ ZQģ.fÐ? ģōï…ŠÏ >AzPä•POC\ÂV8ļšó-§ŅąŠ/ß[Ęs–0Iũ†ß)§ŪÜ|Ė‹‚GEš*Ï]\Č"u#ĮUeWĸdęĖ;MJŽņNĢ`ĸ%ē˜ú:fÛ?Ų(\RÅØÔq ûiŧ„ꏏ{ģ`åRIšĒzÞNË uę`0ęØ~€eN$-åĩâšūĶe1oęOŽØĶBŌÏÃCWŊę—ĮøūZŅĻ]Ā܎ffff‰-0ak™čË(8rT’Ðī‘~Š9fīi{Ĩ‡.ThÝŦ^Wá@C :Í$°eÏ{s%4=0’ÏęūTÁņ6īą7!Ģz믉ų +ĮüjÚ>O5)ŊP­cĨŨ:qN%) ―GHE ’‘ĸyŋ‰Ļ4$1­#ïu[FÞFžĨœ+™bb°ûvZV!ë횉uØžôŪxī’üUč/ŠýŦÞíĄ ^riÂt0ŌYė$Îa}ĢÉĮrZw•j\Â―|Ŧŧ6äO9t‹ëēöó0/ëoÔąulGģ@ģ@ģ@ģĀĘœīŋļXYúŽF:S.hīãĘ4FmęęäĮÆÚ)šT :ÉüO\ķ^ģONãlTkIšQˆB”†Üû34âĶdBQ0čęD­ ņŽD Ž;ð ÓQIŒŽÉrĀ*Ë ŠĄlÎĩT Œļ]Óä^5p–ģ‘tvzņ,Ïø.< 9Sž(`ĘžB{—ŒŌäĮd…WÆZRāDY]ÖΆåÐl§#ãīĀŌtU’Žïjï‘ŅaęPÝČ! yÓē̌đ:~ŠMĩĢųz―ģq7/3Ũ-!叛˜Ï˅<‘NųĪ“€mtWŨû9ŲŽffffq-0Ŧķ>+Ļü‰û•FÖCå&\„Š ēĄW XdݞSÓT)u Tę5z€­Š/Ð8äc Ž{SFą)–øé‘WyŊ*Ūš‰™ëķÖĻÜĪCÚÂB :ZĮy‡-§āV ēĖÔCØJ-oðŲš^ۊķÎy„NQĨŠC;Þ# UĒ]ý;ÅįxĄŊųq3iÓĄā^ËÜ)Fí8žtĪÖuë―ÚŠšo fr,īŠwÁ–đēØĨO—ģVLzJ“Âûžv1o; Ýû,ĀÐSĪ ŌėÜcýƖ z;íhhhh˜Č€SĩSú UZI'ÚøÃš­4āŽÖŲqGU  qä}Æ!‡JUČÆ Ļ„+@\ïwfžƒŸÛ} =a'„nÔ#0‚đ/c~ž· UI%‰€W)‡`ĐŦü@ pBŊĒB’|ŽCāĖ{ËkĀ'tk°pmĻŋ 4õå·Āōaį!`ĩ“ũąĢ―vîÆÞŦ-=ú ĀâčŪÛ91UЊž'j$2V=vËŪ›X š—i \§đāūĢžĩŧ6JP‘Į8-Äj>Rĸ^9VsŌ-đf{dĸ6ýķĢY`uZ`pŌð *ū|Žĸ)b DAód,R7ĄŠÍĘģž @c(ø…ĩąŨ-iÃŊb‹”ô:ĀŠ6ëÜKU#—‰ëipœøŒXhyKZœčâ(Â2"–Đ* ēĐóý; Ē<Dt!ũ8^8‰:X/#Ą`§5Cį8 X#€Ý4yZĮ(KęĮkËŊ „ŋýßt|Ūv"LÏyšØ'åŠc–^ÓT3ķ°žYĩ‡g‡‡—æuōBS°ûc@Uz’ čânM%ÝÂŽŽ;{.ęŨ 8Ĩ‹‹8+‘ŊŸ‰@ÓīŠíîŦóð{ątxļÜyĮmPņđĩĢYāŊ`A‚Þ†ųöõéĨiGģĀęąĀāԝ‡Š!"V•Ē;TSÁ§ž”U]9(Džn̟ Ā•ČZžU…ÂŦŒcŽ\^ áá{ûSG!ƒÅĻŦF\Ę"ëˆg…ļđŊĻĀjU•9Þiđ’x•`&mEC.4ãî2x§FĒ ―IÔϚŒ  ÖĐÃHš@â!rdŅz: Š4ÕxÖåĨL)uōšĩĢāóŽËšŊųÁ-Šáâö*K§öP^ŲiÐŜõeÜn^§@@‡ņÔõŠŌĪs"ĖyLũ@û$°ÉĻåt0T™ÜŊō^Æ~ĐÚm’Ŧ#U•Šât'ÕįˆSpH# 'HäÕxX_Ó]cäÓĀđMےú#,0ÄßÅ@ĸ‚üýýĩ[›ÆĩĀ„āĖ8$0€ īđQ@‚҆ßyˆ*FŸ ˜2uj™1mzûEKšęæfÕ―;-ĘģīäQ<Š:AēíöۗCŸöŒrÞy?)gœz*_vT›Ï­DÝéšåƒal‚]ô`ֹ떃~j€ųéOĮIøBđ“`č\·}<ārvÂķĶþvQ…DīōœðŌ]ký\‹Ũh`ã14D’CCSˆxUÁ:ŦÛSõK:œUÍM‚Ú„v1}m#DT–ÕMŠŧ•Œę˜nŊcBŊXĩ8ƞŸÎI͜UŌ0ŌV°Å=Í3*ENpoípĻø+$0å‡Ąąģ•ĨOG‡ûcː›JĒ.ɧĒ(K—.ĨÃ!čWïav™"CėΰzģļOĨ–ï8ßŋæžï|,z>üķĢY`uZ`Bpv ĢK’،Ō Ŧ―„ƒ ōŧĪėļÓÎåȗ]vß}ÚöūōÓóÏ+zï{Ëeŋļ `ðÍåG˜đžk…cou™9°ŲfsĘ1/{yųÄq/§Ÿ|D!~lÐ[TY…e―͖į=ĸųeÚÔ)åļ}Ø&ōr”ÉÍķĨLĶūąĖ[`§Bôá% MĩoLčõgá#4ÍŧĄĪIj2ĘQ•vņlĀj}ĢīŦā6ga—Wx؆Ôs öáöš`A—Šã[Uš !n„*^AØ-(ïÃÃDÄŠÃ…·ŪŨ1  Þ—ąyLóŌΎsŠ`uĮŠ–ýÄLÛĘdA ^ĶÓģUŠ―Žïę>,‹Į­·ÞZnūųæēdɒjŊ՝Ņ}$―)SĶ” 6Ø Ėž=;Ÿĸ}ĪXčbøwXŋ‡Ŧĸûý€6ėžō‚ӝOdƒãe6Æīŋ4Æ4ð4Ø#,v`ĢžãNŧ”OžðŲ2gΜrúi§Ņ°Ŋ,Ûo?·LŸąF`18mjŲz›mãj=ïĮ?*Ó§MK‘ÓN6á™éÓgDõTí§:ÁÕđdīLåū=öÞ§\|áųåÖ[nMzHÐ2ĖúŠuųžŒĢB= Éí·Û!i_°X ÞqTÏÏÝaG™Q."-ëĢÛųÁswHš—\tATŊęW öĄrõ\ė―>§+ØņO]žĪŠČ ĘŽ‹›ÔëNÕņUWnōæĩ‚đŠtZÕ`V9"ýŠpë˜ĶvËô ékZ€ąÕ`―ÆIƊŠ%]̃­ŧ@ā:œ…ģ‡Ÿ›Ÿ•ŸËôY&áĐdwڎõ0}EōlÆĐý―:íī`Á‚ríĩŨöTzûęĖãū”–ciÖUïÁúëŊŸÏäūTūdYøæøYųVé{Ë‚ÓÆt™BĀq<•Ķ*ghČģĩųÔ§=―Ė™3§|ö„O—·ŋí-€e Ė˜>―Üzû­eãM6-o{Į;ĘŪŧý]€szëąĮ–Ë.―Ī<ų)‡–7ūų_⚾zþüēpáĒ4ė*Â'ōäōŌc^VfÓÝuŨå Ÿĸ\9ņģ'Å Ë%ü―áF*o~Ë[ËN;ïuwÎÎ.o;öMåAÚĪžņØ·”-ķÜ*ðļøĒ ˇ?ôÁōėį>·ėģÏ~Qw·ß~[yóÞPÎ9įŽ€OЊℎ`\Ž€PØēä'Á” =pŊŪh•ąËVᅱ/uqÕĒĀ$þ 9Uqo “PUŦÓjŒĖ­î[•bę\gg\Æö[Č:öŌ…njYĸóĸnq ]°“ŒŪĩžÜ_į°ôÄþ·Nū°~Nåéï§”īVįŨŽš‰ïškAmžĐįęĖâ>––ŸŊ; mīQ^ßĮŠø,GþqīĢY`5Z`BpÚ !Ā‹@dŠOÅ"Hmė·ÚjŦ(ŠSO=™(Ę;iØ'Á;Ó@ŋH>æĀĮ–/~á󙀸ó(wÜą |č}ï-ŊÓąe™k”O|Ųfŧm $Y#ŲtģÍŧŋ•—õĒrԋ_Z^óOŊ/]|QųáYį°>.Á.€ČFJĻæ^óOŊ+>ð1å Ŋ*n üëÛßUŪž7/.Øýķ9õ”SĘO?Š;m ”ðӞþĖrÉÏ.._ûęW˖[mY~BÕu)Ø·ŧ§Iė9cĢoãŠX›d~ĻfQÕōŸó4þívTk˜đļ­d!þčg_SÔí0ãŧü"mƒŠƒäPA $™úÓÏģYW]ۖĮŸeFCQÝį>_ƒąøŽĖˆ4F âũPĸ”(ðåė\rɗU‹xÖÆ$€ĨĘBßŽŅ―qô9 ›>PëšŌĨ­žÍ@ LNĄĪPąß­2.˜ĻNŸãpƒŒ5N# Čû–,Yšf[W mĨįöÝwŋ2þüōîwūƒë‹Qžŧgt=Ɓ6ÛtÓōĨ/~ąžûío+ÝuŨō0g6[lđezėŠÐCPžëo0›ąĖĐeóÍ·,įžu`$ĀĖFĘ<Ö_ƒēcŽŽÓíģũÞ4\Õ]šõÖ۔s~pN>Ö­ķÞ7íåÄ?ÃÔúeņĒEeà 7,[nąE9ņ3Ÿ)wĒTŒ.fŊÂnŠÚ,āÞSŲYre&4Ū"ĖTy0ûiâBõœé8ĨKU7éTŽ\]ĻŪÄÂĻAĮ4]VÕéØĪëôúކsjIq IÏ;^jPeō―Ę;Š“ņUÝ­•Í6ڐÐt(ŋŅĀ"Ŧ‚åLšÚH˜đúP{u ߍ―šu âšé–ÛŌišŋT;ûyŪ·ÞzųÎÝßë›Jþ üc[”žëß@Y[ĸ6,08ëī•• ō*ƀ”~„\đbRYxį‚ēāŽÛÓ`l‚Rt>Ē_TÍ$ĒYãé8œQžšātĘFŨP/&`D…æœ?—„Kä+°ĻcrĨLÃå{ŲĨŋ(įýä§åúëŪ­å2<“ƒÆĘ`§gčÞÂô??ĸđrÞOÏ+ĸõųÏSŠSB$@IDATÆŧʑ/~IyÅ+_UsÐAåEÏ?ž<ëO+/~ÉKË!OyjyĖc*/~Ņå{ßýNęgų…”A46~4ÕC•æ˜fU’,‹Ū$ŸĀDšm·tŸîlAŊa\Üa 7wœ―ÓÆÖmÃ$ĨÞQМPáZO•'tŽÝdĒęÐĒZF•$}LŲâūMųųhq Œ™kÂBÖeĸ„ŋÏUuŒRüÜŦV]‡u›>c&_›Ýüž䨿Zk­•ÎÍęīeKëO·_CŋåíhX=˜œķđÂS7Ą.E`ßDhՔĀ:ũÜ–Į>îĘsŸ{Xđå͛ǿT”\p~™?o>A۔=ˆķ]°āŽēíķۖŦŪž’qËŧĶđ;Ė%pį!eũ=ö,kŊ―v pëm·°wÝygųØŋ$îÎ h~uà ™ŠW°zāÛļĸĒ67EÁžyÆåžĸ•šAÜū[lą%W—WūėÆZßYķĸþqŸ‹}Û[Ž-_|qųĮŨþSŲwĸ‡g§“ÝvÛĢ|õŋŋ\nļá—(A'š#Ā·Ōý@yėÜŪLÕ&§A$ÏMĘbãĐûZ@ŲÁĻ‹˃ztėSęģ\Ėõ_+KÁŒĒĪCą‚…âëT•ú‡^Ģg§d‡wSQđŌ%‰j­.d"z-—c­”ĢŠKî!·hŦë ó™ŲÓúŽŧ ~ā-Œ}ÝđĢĩįę:tSęš2ŸŦŸmĘķš2ļĶÓ)LëyŊë}Ðü°H·ØI囨ŽfÕf ĀYöïĸVŪšęŠōĐOÕũÕo|ģ\ýõeņâÅDØN/W\v@ŸÉĩŊóĪLc°Îģž…dYüSLS˜ŽâœĖðýå}øpųČG?ZnüՍeíuÖ.ï{ÏŧË,zĸGĒ,očN8唓íoœtrđíÖےßÕW_]Ng ô°ÃŸ_yō“Ë%—\\~yÃõe€ 'ĪZķî6ŒŠæî(đĶ;åwfŠ9öésŠh•·*ĩÚŌEęę@"Ō(eŨļuüTEŪjöG5í4;*ŨŽŒ‡Û·ŲĄŅ\ˆëÖûŌÕ65ļ–ĢŠ}čÏáÔįÍú9öSN?KŪïkĘÞõ§Ö·Ÿ2Nu:cĐËÉŦšŧĸô4ۓÍŠ&­ĪÝâ{8Ŋ‘ßËŪsó§ĶŨžkЀÓá2Ô% ī_8ýūå*P]…,ŽÎ{ĢO-\XÞûη—SOúVŲeŨŋ‹:ýîß.ŨšÅ/–”§=åēĸ#ũā)ÜsýõŨ&°å=Ģ>TŪϘ–ŠÖÏŽF%ۀčM j—Ō_ Ôū|e“ËŽƒåüëï(ŨÜ~%ĨņJ;šþōÐóqûLYsVū—üÝķoã_þsļŋåØ8îö{4ƒčÓ#?Ž|ó_+3ˆx5šV ĶjąĄķNcIkÉÛ^cOËI#ž Eä‚ãqmęBÄYč•˜ŋŧ•jlė]*Ū6ø\Pd:‡îP]‰ÕMĖk!ĒâŠsëø ãNĐÐUiĀKĒúQMQX7pE ^+܀œ[5c|DŸ2>e4-ÄUŠĘN™đ) pAHzčžÖ-JBœc >ōÏb\ŦSGÜÂŦn#–hYōįĸĻF!šĐ=œęjđĻ—P Ņ,ĸ™Ŋ6ÐÖ}ôœ-o  ;@Ēiy.KRūe(Sƒ~ļ5îŲ”Ó2cÁîy]ŧ>ŊŠĩN‹ Úzîa‡—ũ|āCéqK·“ŧęŠųe*Š~ΜÍzîŪþþoÓ^FnļsŽ\ôËárõmĢe1ßÏ·ĢYāŊaÛ§élæ>gÁēãƒĶ”Ö$6€ŋ+Ï·ãeYsÅWŌžŪdXoãŽ?Õ(NUŠ·TW ų ÓhpÓŌĻg|Œ†3*ØųmĮÆßš‰IōÏ:ĖÓqÛŲkô—]7Z6[{°,Íčꟕn{ļYāOĩ€ßéiƒ“ĘšzAĶņŨĘũÓïi;šþ LNįŠ–l€mĖ}Ŋrsbŋī@r McãŸÆšđXðĘßŪĀ3ĻÂņ^`fôkČx Øā›,ôT|q}Ųļ,M!Ũ;Īaį9áe>·Øčŧ>Žũð`/XÉņAkĘi>ĶŦšM™ˆ{dŠŽ…•ÉUĨöë?%Á&v2ÅĪ$q+øUŧq‰’ĶAR_EkŅS?TĻÂËį;Ũ.™ +íc9Ė;Ņŋä]ËčŸ6ķâ‚iSl`ęz$%]§ 8æI9H+î[Ōōģðu·J‘[‡ĐËëTËdúæ·0ACÎĩ$ÖLũÏĶ&ĩčŽ!ƓÖ_cRYo†‹TˆwŨÚïfŋĪüëâÏ þ-øĶÍŦÁ€ģ‚Â|2·Ķ ­U[&šųã5A·pá]QPMĩĨÛUPļ ģŪAïsÎã8ƒĩ2™Ũ6čՍčsœå―ĘēSĒ™2áÜMLZ |![eáHX†ž乆ŦšōŲDáŠŪČ[5(L) ÐaĖð}ęœĮü…Q/§v RĮŅ€ŪBuČ2ĢŌĨË]ëKԞōÔ6ŠNŠ`GĀg„ĩ|fyÄĐ=ô<ęî+ĩ*Ŧã_+ƒ1ÚŅ,p_°@ý+ū/”Ī•áþ` ĀYĢgue  (K݆aād{*8 RZÎBé=îņŽ[ûPøÓW.biŧœsv^RĄIƒí*$·ÆŪ$ũšŪnYĮD75/Į,Uī*ŊŠzkðŒFWyft]šĖJ”į—Õ2ŦpEEuĨW·09ĸëō œyFwŪ@Đp"JTW/Ę' tuFRlUŪũZ.a†pNÝÄD?iųNe- xēBėų+iŲ9P=Ší€čöšÎx/é h!éų@ščRĩĢá{Ø_ÆX§pĄWEzDSNĄ™Ž…é;ÍóŧÖgEyö&å?Õ}īÆju[īĨŨ,Ð,p_°Āā4К5WĒ,†žS=ĨáķĄ§–Doxó›Ës{^&Û8ŧÆ {NųÞũūWĶēĢĮē#Q98H^o9ļĐCe˜Õ†–ĢøĶđð;ž.ˆ0Äë)Œ=.YšĨĪ:[^v`Ūį‡?úąō†Ũ―–Å~\ͰrOrļ.ŽFX@aŌ$!‹Âe9;!-@„—ĀpÏKcp‘Wēčn2mp‘A0.ąŨ ęū]Š"Žŧv* yĸrīûЁ,ĸóĨ/”Í7ßēô„'u:­œüÍo”ó/ø)ŪāqŨF-:!…―č`Ô"JYŧŠŽí€øSįpŽä{Á‡Â•ŋ@Öĸ́D.ĒeE+p *Š.a!œN Pô0-Ąíyï1A;Ķ%|UΞ§ģ!ԓĄķĢY Y Y Yān-08ëÜAÛTĮÖ ÞąáMĪ-ðÔ―iÔį#ņČ4þúĀûËį>}B™đ&SWgĪĄųŦĸ1‹ ļÔތ3Ęŋ°ZÏ.ŧėV^õęŨ”ï~į;åQĸũåđÏ{~9æĨ/.—2ßō]ï{nTd‡äũŋï=eÞUóĘN}h܌ÎĮ\ŠJÝøA›–}üøēívÛąŦÉŲåCĸþŅręi§”>ĸ°ŠēāÁŽYk—g?ëŲeuÖ)7ąē‹+ņ–ëéĘģkÕņ•CžðåŌË~^>ōĮ•G<ō‘”ãįŽ‚ôžōŒg=Ŧ<åā'–M7Þ4‹18§t{ ú™N( 8°žóÝïaUĒkč<,NšŊyÕ+˗ĸë ãT ŋŨ퀒MБëˆCIÜ+`čĪ]1rĀTö :l› "Ļ6d­Qž ÆĒóâs‰Ā%9Ÿå”·Ó`\ķu 4ĄŋvxTåŠa;ABr!úŽíâ­ ØŦŠ―ÛïIŧÐ,Ð,Ð,Ð,ÐģĀ„C[ĢvÍSĄi#­BĒyÏ8ā,VčŲ`ÍPLĢŲ‹pæš33^7epJÆ1u… Žoþŋo”ýöޓÅÎ+Ïfóį]Uþų ĸ”­ÂæĖ™SD·ÜxS9ü°g——ųÂļdGXp}=ũ.ß=ãôrúЧD9―ýmĸRŽ~ŧïąGŲ‹Ýĸû+_)ozÃëËYĻØŲ…e§‡î’ņRA0†ûÔņBÉqĖKŽ*ïxÛÛRíQāōōųÐ?—é.ŧíVöØkß@ó#ĸöáōøĮ=ķûÆ7dOŅ'<ņ`ÔktlZQžðļƒĘ{ÞõöōăFiN-ųð‡ĘŧÞþÖŽÅú΃Én/ÞŊÏX(ęŨq[Ë#°<į1WtæžbI€w\՟QȎÄäÜc­H4ãÉĪïupšįðŅēæĐûÕĀ%ÝÁ‰Ķõ/QĘáŪâ[šį:ø&jŲÛÚŅ,Ð,Ð,Ð,0Ū&PœĻĄ‰"šNƒŸÅ hym€U1Š)Uh_œÄøæÔ€*:ŠÉ5WUMŽ―}ýk_+—]rIų;ÖĪõ9wÍøßŊ}#éŧŠ.ßGp@yÍk_WælūyqL]–k­ĩf`―…(îļýv•ŋƒ­ÁfĪb.‘ũÜÁæé&ÂÓ(Cv&!*ŨB:Îw‹Ðŧ"ŠŨã"\―?ųáĢęÞôæcY.p“U@sÛyW_[æÍ›ĪÍ^vVōđïâþŅđgÁþ2{ÝŲĐĮŧÞó^†―“ĩu]MČq[Į`ĩŪíDá 0Tz]?ˆz ϧc"TY•w “Švs<īę96;‰ßYükޛi=„ ÏPšī}áüWŸ­cš>Õč_ÝÆ‰†ÆNz„ąĩ#īíÛjŨĀZķĢY Y Y Y`< LÎ:Ķi(Í k Ú@Û ÛØO&(FŨßĒŧ•+ŪžĒ<ėá/<æÜŊßÎ)ëŽËÖJÅĐ!<ũĄM{`ÁïëŊŋ> Ŋ/ĄŅž‚rŧųæ›Ę[ĸõ_Ëތ!ūâåĮ”kX?öŋūüߟ (8&'Ʉ‘‡ŧĐx|ã_/'žðéÜᘿ—_–`ĖaŒÔōÉ:ũSļy8Þg(Œ*.õđ{lēÉfŒÞ:CæH]†kžäm―ģ―þÖĨD š7ēĸį%tTŸ‚^UšÝÜĘÍ7þŠ,d!{Þ•ŲįUŒ Jr “Ãrvŧ­čįî§ģÁ­ <…ŽŠ4@I%kŊę0Ïōz (e: 2Y]]ļ–ËÃōh3ËŽ›WHÖf IZU—ræō|;šššš&ķĀ„āīąMƒNë%öh„UŒīĮÔ‘ąĸũĩŊfĮ‘Ã_pDŲaĮâ"]cæĖlՕQi.Į]CöQztyéŅĮŸeüō“Ÿøãs&{îĩWyü㟘hW-Œ:€―ðEGáĒݧœĖš·7Þxc9ũĻÛr Á Øå°gđ~-@ä!ãVO.(`ÞéxE;Ä{Áč!œÎĸņó—ŋęÕeó-·,ûėŧ_"ƒO;õԌ…аÝBĖųžš\ŋs晎‹û°rå9ûŽģØCt‹r!ëãščü Ÿýkï~-.dƒ”T―šF Š`@TŽ*xÚ­ŌΊ7?jđë{ė0ã$äcF…ō_‚ĩpÏJÉŅ1dž{ ÔýŦÝCÏ@YpW/‚€ķl’Z·Ū?íhhhh˜ØビÖÛ]xÖÆh2Ũ2ŠFŲkN#ųŅđ?(/}ņQååŊxEŲ|‹-ĢJ/`/ĖáĨÃÚüŽœÆøäÍįļųģcĄoeęŠ.Ũ]vÝ­lCpÏžŦیŦö ĸųyßĶ úÞwŋ[þý#Nú‚ãË_üOĶĪĖ-ž;7ŠÕâĸĩũĒĢŽ"BöĀ@î‡įž7ĶÔąĖîķrÆ·OĮm:8Z†3N?-Zëķ\„Ú=óŒo—_üâērų——Wžėķ{m–wckß_ĖæŨSč(X‡Ë RĐéÖ<ņ3Ÿ”ĸĻÚ'rHÜĮ§œ|õ ÐõŦ_EƂŲ&ŽŪéëļĪæĒũðZ°kWWKŠŠī·`wE-ōÛ―8Ĩnoí íąðV=zNŨ­@4-ÏũY/Ž=ïģþÎBéÄļä{€rÔ2ÔqԜļ—ĸąėÝÏ―œÕ}*yíßýüąëėåïvÜ―:ûúŧÍũĶ&\äýČ#/ĸû?_ Āę$ý:ÖæŸð*$ GEĻ’[à iûËëoœ:ÝN†ÓļϘ1žŪëøœøFmuxí5óø]Ąš&ۀÍD­ÞrËÍėëđ8ŠpÆ3€1ꈆÃĐ-·Þz ïĮʒEKŲčzynXģCˍļG§á2…š šY‰Ēe‘q•ÕTÆ>ýƒrK-]ĨSPē–ĄŪ%Û ū!ä―ö:ëfŒu[ĢMenĶÁvĶO›NäŦÅ ýīÜŲÅÅð“uœÖąË™kŪ™ČߌYėœIYˆ›ŧen) ĩQD‘S·:˜u Ģ<*NÝđ.ĒÐ5^ïÆGm"Ví‘J™TÏÚÉ-Í:­eí\ėBÖĢN…ÞŒÍ.(Ï|ÎsˇĸíßYýiaŪûÏŧČûŠĮyáũdŅĒEųūtõįöûÍ%ĸV ’sœ―ëčLT9ŋ~nÚËN`ū=ô―Ū­ô.­ÁpßÛv4 üŪüŧû -ōîø˜›ŽņjÃOãín―@ģŨąóýtÕä Ũ_ŸëkLŸ`úežŽ*ĩčģ|ŋË ĘÓáÆ_ݐsîūĄ[ $óTQģBœŋØíLrýuŨ ÂÄqHĮýŪ™?/c{6LĮ#måI”{ʔŠķ4NJŨU€\įÖqÏ!ęT]”Îo„ˆ0gņĒ%4TW' g*å^ÎĒ ŠŋYkÏĘ̉u}Ö1ÃI(įÛʝw,ČęEƒ”`ĐVU—ÞéôjĮX%õā§Ū4ĘkĄ'4ÝŽ.ĢgđŽ{ĘÏ5!ŧœy—Q‹ÄÄúŽ*ÓÄMÛđ˜cų™Ð! ›tœūǚÔ>YđČúSūnĄéýPɐ;î―ÃēÚđõÖ[Ó 38ėäö3ģSâtĄõčli‰; BpAr?ãz%ų"Sųƒé n“JJwĶé˜F͆P>Aá*>BJéZTkhE…HŦėL韚ÆĘ:Xuݟš[c:”H}…s—KúÅe čOul‘âÅvYČ))ĀßĘÚëqŌŦïĪü+)_‚€°›ö°>ė°ĨŨŨÅρųXïY]Ɠú€Ļeäđ@ˆ{/ĘÍŲÆmÖuk^– ˆv‹čkģ#ųLĻË'ÞÛĮ:*.ÁąšëïX8æŧ—mÝsmkw~līïÉaĮî†n<'jØŦęÜï†xTŒ+øëŋQï{đ þÚĶØÞØ!ėó{{>XÕĶāô{Ķ}ÛŅ,poY`‚Ö’†…c#nãĀßrX›'ø@ˆ/ķÜŦÛ§s HØļØ8ÛX/įĮÃÆŧŠV@čŠÐeöTYąØx;õEWîPddĢXw9qĐŧ^ڜK9(‹0r Ę Vĸņp:† š‹īûĮ7eŠ QËåý™ßé*<Ô#*ĩ]Ģ^ģáĒšq/ûÞz}š2R.;Q‘Ž%:™ŧkÔ°e”ėšTxõšupĮë"óuŽrTw,…pŲ<§žhdóķS`Ā5xĢ)Ũˆsi9ï5§§|”KĨË­…áÔ|.ýĀ_•ęóĶ[P?3wmŅ6ðe [ÝŨđWãÝKGĄy•€eŨn―2 }ëð[ũŽĪ.wÜyWšąV™Æũæ§ĸ[ ý7―üųņyŨÏkŲč’rį]‹ËĖYë°čðÂē`ņēx#úý ĸ@ŋyJøųcĮðž4ėÞįýšïœ;{Q֕žÖXÚâÏ·‡6^ZnY°ĻĖ\{BŨĪWËaÂGœŽļIú=Yrį­eņ ĶŽÍšQîžõæēb`ēÖ ރL…§ā4{rĸjĐDKägõÓļ‡_f ĸ‹ōBMUĨf   Îß\—yŸ\c͌3ÆÝHƒ“5QųãÐĨČÍŽ&TᕠœQĐNĢš‚Teiz“hø7ßbŦōú~s9ø§ķųC€‹ķČš~UĒĩYuÉšĀÐ8'T,Ã`·@;uPYåYęāĘ<*{ī% Ó­[ÚFĘë–;“žikMŨttĮš0„Š.‹Ļs/rø,õå҈iĘÄÓQĒĶašƒäĢk{MæĐ>^Á KÓCõąĖæ ŽÝ1ft˜ąYÎzÝņF"ÓY!Ģ(|ëŦŌĶ~fzLĮĸ(t€imüüŽڛw*Ü:Õ%oîÅīŦ žGuŧãšōÍ/|š|üļãĘqĮū\zãŌÔų7ŸÏũbøķrî·ūVæĸjQūKŋy}žŨ‚XEĸ‡;u+ÞXNþęWĘyóïä{Q;4Kođū|ëŋ]nZžžÜņŦ‹ËŨŋ}NYīÜõ…ĸP*ŋ}Îēþ)Į=yÎïņŌÛYūų•/”/~ų+Ž`õ_å§~ŊĖŋéN{†Jķŋ~ÆÏbņuåë'^nf:ĩßŧ{vč‰âo‰ïÞ:ėĀÝvÝųåËĸóírËb=VÚxRų內•Óŋ}IūŊÕ~úÏnŠýĸPŋ{ÎïŨ=ędüîƒí}ģĀa ĸĒüŌÛČLîđŠøĢņYWĻcgK—,.Ï;âå[§žVN9ýĖrÆũÏ.Įúģå)O& KŨ* Ëš*0ŠÆ€ÁØ Ãý·, ŋĘldéhÏÅZpO=Ļžâ•Ŋdqƒ'DĨĨą§Wï­ęV*ų‡įë b ŒÓ7Ä‚Š<Yė‚oü.ä1āŠķ,ËjËý,E’uu9ŋĻK`ĨkŨĐ.‚ŌÅÐí<φWOĸČS&ŌĩÉkžŨ6þļóŠËúĐh…Ž ÐEâ~âú(ķĻUƒ™Róœį ë}Šr;*aaïėĢ›šxdÐԉēŋó:UęBŸ‚Ĩ^‚Ôi9ŦÜāļuUĀ+Ü<›4l @klŦG`2ŨĸGõ8Ôąōŧm —–ï}óĪråĒeoĶÍYw*uaQl•ÓųpOØtðZhÛ1<QĨŦŪûýëōŠógíīøŲûųÍûáYåŽKo‰ÝQėޕĐŊoYđnÞĨåG?üI9ũÜ ĘŠdƒžœŽŌęËïÓ2>ãQ>c”čž›čũÝAäîlïýĨéuË6ēpA™wÍ­eĘĖĩËŽ5§—›.ûQų§>_Îŋö.:[vŽø.RŋgþaÚņĖð ß í‚IrÎŋ%;…9įw^ŧ`ûQ6]ðoÞïœvą%-HWūĪß{Îk+–ÞQÎþö)åšKąy ~ëîõ·ęýōŸUÎûņŲåÂknÁķäĨŽX:ėũTOyynîáÏÝŲēoX]˜ļĩdøŅeâ:w%XHlckxëŊŋ~ŲlÎ֌=§ÜÄÜJ—ÃûkÐn0{ýō‰ãþ#ŪĀ‘áŅēÓÎŧÄÍxÉÏ.Jc?HÛģ@û•W\nĮbęįýčGüąT7Ģ†ĻÂqŊĄēîąm·{pđöúëĘĩóæ—ŲĖÛÜrŦ­2­äęŦæŅ  uŧYyЃ6Β~·ÞĖ# †?ĪÄĩ9Å(XŨĒu9ūđ;<ĪLĢ ó.ŋžÜ…{g 0vÞm›D0þâRïYƒ{•wÞÎT”Kú`Y{ÖŽēõvۗsÏ>‹čÛuHsã(śXĀaõ˜Nð7˜ÃcņÂEåįLÉąãąû~{ãFšĢ\~ée‰þû;ėäĶ6DđÄÜwb ĖųÝY§–Ĩ‹Ų-†`Ëq+ã7WÏŋŠFĻŋŽđöZĖÝŠ\EŲUnīQųŲD!oĩÕÖQĸ—^r1ʛ°ėïđÉĀjx…AGØĻЎïЈø3ŋy~Ķ+ī0pš•ÎՔ ķ);ĖÝŪėðāíē{ŒÍé͗Ÿ_~tņUeåôŲ@uïēvíO΃ƒÃūÜ<-í~zYéûĸ흀UÕĩ†ŨÜéC›a€ĄÃĀЛ€ŒŠ ĻHL4Š&ÆÄnôŲ“˜hLyƞĒ11‰I|–ØbŊQĢ"v††*„ĄÃÓûûŋuæqî  >Á―áÎ―įž}vųÏđëßkíĩŨn‘gÃG`]Účų­,ķYÓÞąÅk6[FN8 ·―Ŧ`ĸ+ę—YéÚū6fôþÖ&CWS–ÕÛÖÛâó­MĮķmý›ģbĪęĐđoĩŸÄ`ŠžB7"oÝCŋÖÏ~üTŧCœāÕ$f*›9íäėîęóXËÏMģ’/ ē'î{ÔĶŋ2Ü|ļUŊ[hŊOŸeĨąVĘ3ÚzvhmE…"ŪlÝÖëÐg?ÛhK)ß`oū9ÝÖŨZũAĢlĸ~ýw΀Åį‘ëŠlÎ[ÓlþĘÍÖĶû ;xDmŋģŠõKííw?°Ēō:Ëh[`;lķéoϰđŦ‹ô[SˆĖá=}4€ĢļĘEKmæĘrËËÍē9oÎķQíĄ S;Yôœcŋ ÏL“pUÃýiĻ$ž>šÔ8}t.ĄęąžbL!hCād͏aîc Ų=öč#vņųįÚiß9Ų#ĸœ}ÎđÖ2ŦĨČ&Ũþ|ÛívÏ}ũÛŧÛþü?·[ûí­G~/ŧņ·7Û5ŨÝ óŌ#vĮ]ģ[oŧMį:xg]Ð ˜$&ŸøM{ōÏI›―˞yöyß-Ĩ\^tW_{―ýéÖŋZ‘w’īĶ]~…ýõö;œ Ý+í‚ —|ĸ‡öčOŲm·ßi?ųīĮĄ%ÞėÝũh}Ļv]Ų(ÏØŅ žpũ=ũ{p†|‘ßcO=mđývŧĸÁGė†_Ýč{k>ÜþüŨÛėž /ē^zŲ.žøû‹úsõ5Ũ)NmķĩМ_ßx“ýüĘŦėĶ›oW_w―åķmgĮø ŧëž{íG?ūÂŌå |üäío:îŲ#Ÿ:ųß*Œ(ëĸ|ÁWÜÝ2‘î—øōöv?ōø“šþ§’ë1ÛOƒßÜø[ûÞĨ—ÚcęÏåę7sČāņā#y?ïųûCęį`Ũ.™ÓdSLȌė]k—ĪGĻŌýÄäüY°'‚-NžqBøÏũ…uha{ķ·-s^°ŋ=8ÅVoÜfõ"ųâUÚó/Nģ–]ō-―x…=ûę\í ý6ô˜XŅŠyöÔS3,Ŧso‹mųÐ^~cš•ÉĒðŊ)ÚK,ĩÜŪųÖ!§ĩAšŊ^™-[ëþ(ŽĢ(L$œåÍšþ#›·"Å&(6q^UĐ-ž7ߊ€5f'†ÏĸŲþϜŋ_ŧ+ŋäæáÕP,Ģß%š0ą”6Ýlh_yðVnąŌĒeöÜóSŽŽuËĐYcÏ?õŠ•KSŸ-|cQ‰åũ)°6YV[Yjï<ĸĪ­(M·ž]ZØ[/Oĩ%ëŲēVŦaždÆóöÆŽBËïÝŲ>zïu{m^‘%•,ĩ§ŸyFæô:ëÞĢ›eg0w™jiuАæXN+9ëĻmqLpþYĄbJûö•Cöģ”-ģlá:,4ę‹ŨÔ@œŠ’ßGüšDïqŽhiHO&‰­“ęËĪ―0ɋĩ‰Õ2!ō0K:ŧ0.Ú"íLÚÚ[o―eD€éú1ēOįņ“'ÛM7þÆ~ýŦ_ڄ ŠI{ŲÕŊzŨ˜8Ņžyæöîŧïøķ^§Ÿq–Ŋ›ô‚„ûĀƒíZPš4%‚"“öâKūį„ÞxíU0`€õíŨÏĩžC;Ė* ÁVirn'ÕoüáGÚåWüĞQxū]ú}_gyõõ7øfÛŽxÔWī4ÏÆrĻ{}þK‘.ŋâ§6dčPŧė‡—Ú―ũüÍN?ãL;Z1q‘•D(šD†ĻįŸÏ=g…ŦVŅŽzôėa=zõēýũe3՗ųÂaÐāÁ"õ6Z#|Lķs=qy1=-\ļĀņĪ0}l—öÄĢYŸūýlōä|›ĩŦŪūÖšũėißŋäb›ōâ‹v‘ˆúÄožä&îÞ―{ÛЧžaŦ W9~GsŒĢíÝî―įnowŸ>}í§?ŋŌÝôëeâÄ|Æ`Ó5/ĄęDǘ„ĢųNuîSNÜÓDÂ/~ŪFëoûp”}uüþVģaŽýíþGm^áF-wZnŦķj‘ötÅ"°rîB+‘™–TŊįtCá[Ŋį1ĐJfqõkýúZ§šĘÞú°Ø †j‡ŽnÃīNír-Oīí6ČF.Ã &É’…cåžũ­ĒU†Ėŋ)ԈjlųŠ%ķa+õ@ü(NäÝņsÃĩ äïĮÎïôWÓÎe$:v|úQŦMÝcIāĶÔÛęÅ* äVKOŌ\Ēb—JÛ[ĢyÅtí7›TĶøÏåÉÖą}[‹UŪķ™ ‹tT·\KÆVŊÞ ŋīMžššMöÁžB+Ŋϔ™_Ķóm[lÅBݓŠĩtĶގ8f’í7t?ĩĸëÐū‹ĩËÎī†Xß^í$ˆSŽmuåŦlæĒMōūkcēŽTŦģ>X =oÅĖ`ÄsĒ~€–›ã›Ā5ŽÉîāŧŦũ#ä4iŠåÁÅ< Q2·Æč1‹öĨz 9§Į]Ï:?ÔdũäA&ĩ1ą‹ $qÐÁiÞ1 ŽĩnÝŧû‰_Æŋþõ]ý‹ĸķŊûu'Õ^"Ÿ)S^Œę$ÂPvÛķvß}ũØĩ"’·ĶM·NyÉTČŧĮyØũ=úŦ_yöũ-Äîŧũ^+Ōú7öÅ؈úIýeîcý ^ŠÔŋ`þ|íúžomvß=wŲQ“&Ųôi27+ęÏð#œœŽeËwųÐCČdÜÍūō•ĢÔč;ļ`{Ø82d°M{óMŸcÄ ˜õWþė ]Óݎœ4Ņĩ!Ėß=õÂëõkjG~~ūcÛĨc'[šxą&$āäãņ}8ûû?xÛöÓlųšŽ~ö.číKl]ˆ 'Čž{2JÓä~áČË艁ũúÓNņįĒ9õÐÎ^Ãūl=ú °~Čfž&s^O9^éū*øĄu,b=†·· 60§/h+zFc"†d ēōz ĩ^9y–•T%ķzÉ@Áĩ%”ņēæđŒĩIsqõ%EÖēMw[8[{ŽĶæXlÍJ[ūfĢČŠ­']ŨĻ.Չ€ß.؛švU°“?jcÜÄŲrúQaâAĨ˜U“jŠlÅ:ÍҧvķdÝũšúTY;’­e‹>6ūË~–ĢįŪÛȃ­Eŧå2óŋkĸpĩ;ū‡ÏoēIAFVwĖDëÔEkēåAL_Ģ9dųddhú!ӆo9yÝ­jåÕĐᗾÃ=ž+vįĐ­c‡"aÜ@æīKՊŲslcąöĀÍYeÓæhtË Û°ržnę=@΀ąãåõFŪO”våųJTN8H„@bâԏĄāĪĐŅ%›"ŧÆĒ96œ]HãhÁNļ9„svvŽ 2Ä5ˆó.įq^)V”ŋßwŸ"8,’āæÖpzÁŧķΗučį'MrIîõˆdŨõŧŌ8a劏|?Î/iŨ•^"ķuëÖŲïÏôđŧČ[6r$Ąþ:ąäĸŌ‹/X‘扛û€BųÝøÛßŲŨ?A‘ŒšøvbĨj#Ëč;Â{ƍvįíwØû3gšg1‚iÃÆ ^GKiÔÓ5ßđlŲ2;ęĻIN’‹-Ô\är+HԗŊ?Ųڊ°ïWŋ;æuīĢū" Wïlĩķm›ž%*p’b^TâÁqSđ+Ôt[ŠŨ1ĸÃ6{î @6—Wâ&EQîm:trŊ`pAP DÅ~ W"Î\áiœĐčžļ°WÁĀĖĀË$mØ*lÖ{ó,UėÛēÆ6KŲËč”k;f[ÛÔ%VkiCšīī-Û"áĘģ9ķÉÓüré4ŦŪÏŌ Ī…•Vé|zž í‘l3gŋaģڎąXy‰evČW`íœģdķ-ŌÎ:·nĐį\xhąrÞVšÖÅ·—ËMÖó_WboOyÂޛĩÔš ĒX99‰Ļœ ? éAŅó]ēQ›Ŋ/–ĶXmKæÍķĨ2tė(ë™ĩŌZÏZb·ÔÛ ý:YIQąú[e˗ŽīšĖķÖ§Wg›7ĢØjbílPt[ZZaÃõ‘Vą{Âđ~;ĩąÐ+ŨVK[LÏf9)•ÂļÆ:ë·ŨBåO‘—ņaÃ:kę!Í šghÞ>æAQ6ådYNv–’ĩŠzŦÍ^īÜZ<Ў:ļŊÚ­ßđ+ÜųO[šl­uÔoíŸÄž-ÎJ!>O$&N V–Yø:L‘—ÏwJļ熉‹ÆëA1r„?čßøÖɊ+;Øîžãv‘Ór™zÖ8―öĘ+†iĩ}Į<]#âÕ-!??ßF ßß56Ęe{.ˆ‚„ ĶHĘ*Į™ÇŲÉ GZ,Į˜Õ……2o>j7ĸþ/įđgŸĩ•+Vøy~ŠÂMŧ\?gö,‘ö―ÖZ‹ð[I‹dMåŽYĸRļžÍvÚégØiš ,t3ïj™Ļ đG~ЖŦžN;)ŌŅ:ÕŅәŒĩ”xK xûž•Đ™ õôũß/­ĩÐą›ņÖ ;lÜxĢž§ÜIũŦĮ+ÂÜæ1z‰#90EžŋŅgpXŊA^ū,ÂŪXūĖ:wéjs—Ѓ$ð§ÅÅ[·o6Oŧĩ°Ą 8““m§Ęü―bųr{îOĐh "Ļ7JaÄ`BHéõųP ŠJ7Û|™™ãÎę6Ô> ĀZ+ČԄąĨ6cÎL›šKŸæ,ĮôēÎ= ŽĨīĐķi`RmÓß{ĮĶ,0ËîZ`í:vģ‘ŽķúéÓmÖ;Ó-9#Ûgũī‚‘#mqņŧ6{Ö|Ë9pīĩŒ1PŠ–éWNkÃúXŨ–„IÔ .%Ãú cE–ˆ„3e:ïŠ Øīhk―ŧ+•īŽĸoäxîR[ķąüž4‘Ú\[Ŧßhšæ3ŋzŌ(ë’CdŪ>vÔáuöęôŲ2ų/‘ƒX[;Ī{gŦ*Ųdģßĸ@„™jú%˓đ6oüŅVņڛöÆ+S4XÎēácÔĀŊĨõîŲMÚ}’us„•%―aïOEå&[‡üaÖct›tx•―:cŽMŸąÆŌģ Ž ß2xĪ―ũá"û ū…vh?yũFží-Zuĩ~Ãōĩ ›8hĀ’ÖݝoÛęË,­mūuŊoĢïÍreImĒ č>G$&N Ójiþ`K@ĢyâTĒßĻ;ßÔÖģžD ôE"˜+Ï:ûAD·ÝöŧöŠ_xøđŋüų6jÔ(ŧņæ›EĒ…rþÉģ?ýáöðũŧ&„WčÃO>iđŌĘVŪ\)âđO1c[9Lć}ýåWėÖ?ýÉÎ9į™h§Z[™m_{õ5Ņ̐`ZƒÆŋ$/ĄÍˆa+yë&Ēû5į‡FĘžčI'}KņgģėwÞķYïŋŊM>°ũ$dŲH{ŠĖžóæĖQ―õv‹6đūéw·ØÝũýÝ6Ęė‹ÃŌđgŸĨp|ríWÁ,í@KcĩīŽÔ^|þy;õīӅO―ðüsōīm)ō\kŊžô’M?Á>ų.[ūÔæˆÔŽŅŽ.ËE‚{m2WL ‰Hüj°"<ˆ­ŧlÉûÝÍ7Ų•r4šũþܜ›#Âŋðüó\› °cy ƒÖîM8âHŧėōÛЧŸŪ=K[IŦý‡oovđœ’$<öȃūî“VŠ>âõ$°ã… o<ÉĪŽðĢÆi#…'–ÖēzŒcAÞđĸ—lrĸQ‹)ĖÏu6Rƒī“jÍëåõaĮŒp2SmzFõĖfķ·?ÖtŨ4n=ŧõ}íøû:9Ũ˜&ÕŨÄlðØÃ`‚%A&5*ģCþ;Ķ@óøúnÜÄ~Ÿ-k ޙđ}Ma$ęNCGéũîĪæ\‡!3ŧ“9ų$õ8J JtÅ͗­:õ·cN ïtï…'Ë_đŋČwä—FÛōĨKíõW^v‚Äī–›ÛN‹ĻsgšĢgŦUwV†âãęŸGę‘ŲēUŦÖvčĄãŽwßūķR&Ú7_]fNE(Qðlī·ö"íÕŌ87hY &RČŽs—Îrâ9RQ^ēlÆ4­5à P„™ß+ßIlĢLļ%0Ų_íšGä9}sōqÞgÎeËÓķSŨÎ"š"ĮĪņÄ#ęOƒ’M!ú…ģå;ąŽ“ÓÎŽĨô6j>pņâÅ2§.Q?Ē@ôŧ.ĨÖæÎžëuƒ }žėŠŸÉ#ũĢö æL=&ŪÎAū[eB]ĸÁzwöá˜uœĖ2Ōfį0,[ēÔËĄOĖu.Öü/Ÿi;įįϛkjí)&rL‡ÄļĨsĨƒåŌĮJ­c]ŋn­ö}Ü­Šī́ëO19C| ĒāhZÓĐXÂôđiÞ§•Āģ1DĀ=įž~RDðiĩų“.—AŋúߜD>ōžœ8·!5Žē™ÂóÅsöE{ķG&œų4hÂT+nŅæ$ˆĩ„ĖËEQwäĻ#‚Üčĩĩ2­)f(Ă'LĮyĖFhˆjH—č:ÓĨĄiųy rŪ!!ðÝđGĮÔ9ģDĶ4&bŽ>hā@Ũ†ö“Û―wßí„C{kjīA‰BˆxPĶĪĪ‹Œ0OTAmŅ?æP!ú™ŪrЧVų9†T1/y;üXš’ú˜ĒϘ i+ųŠ ÄĮžø‚ód.ýŧdFđD<čķŦnŊKs;üÐŒāņ vŽŋÃ4>īƒhJ$Ô5i””õ!ÚBĖïÚ –Q2;/ŧĄ^Ÿ“V9esOt‰Cāô/~―ÂOáågkË8‚p3—ÜT}MĸšļĮE‚ĒÄó‚ķ !6Õ?ΓÍīĻĻČĩĻDe7U^Ēkũ–sMáëÏŋ~ģhˆĄėŅ†Ý˜ŠĐ/•AÎ3Įæ2Oëüg•Ļ7Ī]C `ķkx…܁OÄÄК!F–;(âŦkihh˜]]{”YķJAÄšĶkǐ ‰Ú'Ą I@ ‘°fOZÖ†Ķ Ét‰ų nÁMÞ]åu ÆIQ=>ũĻēðÅĄ%ŪņĨ‰D|‹2]K})eČŦÃĻæíķĀ9Ä5fQLĶ‘ó’ôEĩ“z fˆž†Ō.úađ“‘:YægÚä„ qCĻīQD‡f‡‰áFā{4ôČËUƒ™sŅčļ͑ō#í\EŠ">q"ã:ÚíĶL`Ô?wôYÄŽOŪĩëÞpNŒíļRGŒv)9îÔEÕé.dvô ‚ĶŪø"Þ8ŋ:ü Æ]5‘Ä6%Mō‰“ š)rpÁóPZ˜ˆŌAKÅyˆcL•ž’! sH&Úū"@Ћ4!FˆDĮlpíe!ņ!$iD―”< đūCce~Í.Œ&ëčO|>‚"Ąų‘ßũÕyi‹|vK&eH…úÏ ‚‚Ė˜ŧuSĩÚãMŌȍū0ŊIr‡!ÕÁwšØNĒÂ"*“BE{χĒĸŪĨ냛ŒUÄF9NhĘéú|/æYÝ-ÐĄMnÎÖõŒQčũ€Dĸđ/”–Xč Xš5€ÁŠũP—z9ÏýōŒœ ) E 1qJļc†ÄĈķiš‘0‡DÝģU$ÃüšF&WāHdĨČ!B&VÍĨAŒq₠þ^ŪęāØImKņÚ#d ÁBhmŌåM‹ ĮÁ†6ą4‰%ŲČčŦ Ę$Ą…r^īĒzu―ę„Ð (H•z0B„\‹S ŨÓˆ–vSVÔ~‘ŊŪwĒÓŧ“Ī2rs3&éˆ|#’ ŨJ•sk3Í:rðÐâNCā…F΃NŌnð§ LŠ \[VYnޅIuVÍtÍ2Ĩnp[ÚčÐyęGĖŨ>ĮĻ‹ dŸó‰:ą{MĘR@ 4Š@bâDØË,ęΈđVÁŅTœœ|4o ™IļĢYB8œG’F[‚l=?Ō]/'.―'‹˜ÜAGųT”ŸĢŽ8!đāWđäũđC uæ>!?ōAĒåDŨšF+â"h\áĪH›Č+o_ÎC8ĖŊ2_O—úqâ{_zĖ]ČK}†Ž ų81RŊŠExR! š68IŅ/ÂÄđŲXeā AAˆ "\LYéš*ˆQy1Sˆ›nõ†ØÉãä-ėŦčŋōĶëžPØR·đž”Õbõ-Ý―‰!QVy"~][YYგ8Ąõ‰ēÓÓ2D֑ĨĀo‚ū ) G 1qJæYEÖ"H7MĒĨ‰ œļÅ*ih–zÅŨ<ĶŠÎļ6éõŠ`˜ï„€X‘äýUõ Ũ2ŠÖÆ1xÐNHÕJ_°l&ō.Ö AÄWŲ iģįĨ ĀXÞ§IDATēFyuĨũ‰vŠėÕÅókĪírŒ—ē/‘ `ÃĀŒŅ }`"ŌCSF;'/íĄėeH^ĮJĮÚ J­ÔĀÅŊ‹LāIj Ąéč€/ĐąŦ[Þe) š@ 1qJļē@>&ŅtÖ16hVNJ‚?rp‰kā@‚;–&Í -F‚AÏ`U…æõ­ĖĩT™$1í"č!ČŠሀE 5îÝ#RÁT2ï‰B^*‹xĄN0Š0„V-߈–ÍD úĨĐy~‘ĶČ։CφVU]‘Œj„§!H)MÎ=īÂĶMéĖ—ōOä…6čŸi—ęӁũĄŽđHåÅÉ&šĢ™)ÎL8qqŠ1īKՍGkTVdžĨūNÕq†Ļđ[Ņ N‘ķ .”ÅüąkŌ"nÚ !BÎÜ–é U§H[ŽtÏT˜;MĄŅŌ^4Oĩ•Á f_ķwãģϧŠNæVC ĶHLœšYÎNî8ú™9=ˆ3%Dq0Õæë4õ=siõZx‰§&Þ·°B%TčęhOų>o˜Į‹óŦōŠpô#ČƒˆtT•;Ōˆ:iCd>E›‹G)MšVõĒ!bwâSy`Áƒ ‰Ï”Í=æƒīh•‰9˜@Þ? 0ƒMH€@@ Ð4‰Õ qˆ‹@‡ður“öų>‘ä•,ÐI‘/aĖ–dN0ĘƒÓ ÜÝCt:YTģĖÂŊŠøÜãT2͌€Ė;"蓕WTŽãˆ€ā4VL–”í8ę/N>hZ˜§BL·DˆĐ@úėæāDčDG{q„ŠuM Ē\ÏŊ~@ČąúˆŒðū­ōp"/7?ĢņAū*[}‚ \ģqaEĢÃs6UƒĘt XýĘŦųOՅũ+ÎK8î0J›]+W=1Úp” ō(Ö,ý0ļ7Ņ-X0gK_"ŌįūÐf_Ó*\"SvCߕW0zĸũ†ZÂ[@ 4Ž@bâÔu,ĘGëIeJÐûlš“Z&RæéĒ%&šÃ„ŽÐ~ U‘„é åļš%âp2Đ@€Qlֈ€1{JÂûõ”áN4N"ĖKjŨƒrÍ5ŠņðāEÓÄŅS0&ĮwtŅĐ\ŨâÜ|Ã<ĖKB.ī RcĐ ë2ŅĪčt> Ē—Y•žNÔī_íLđ"Tixh{„ÂZšÚÍķŠ ‰āŠÛ5mX™€ĘŦV=>(PŠ„•/ƒ)ŌæøōĖØãö%'īSć†ëš$óŠôW}e âÚ1Ä­|N–ĮôÁ óÐ pœb921C”š+ ƒ5ŠV+?wËųŅViIH€@@ ÄÄ) ąR5§ũū„XČ"öĪļ6%ÁîÚ\‚ߝw"!&ІąÐ=m!Ô"€‹ Mū'€:„ÉDËD"â þ-Î:p’6W:ŅAĶ ˆˆ/2íšŨŠLŠī‡Ā…;܈ ˜wÄ ŅEáûļ–Š~L™b4ęT‘ĩčÄ Š_q/šŊ° \‘(Ú$ƒČŊZý#v.s…ÔïóÁj››`L!T ÐÁN/ˆc°ŠōE!^―@Â9ŪĐW›ˆnD~îäN;ãĶl2ûÜąÞ]óÔýņ6 OČßqS~ĩĖ+K›g~”{‚kTqX‚ŒC Ä8e4šE‚Í)ōD„; š$Z(fOLŒÄƒEûDČCR8óļ†'a !°úÅ|‹lÆ\ĐŊœ(‡yKĘÄQŌ!õ@’hž„D[*­Ï=B* ũ5ŽŌŌ0IbŪ$&,eŌ4V>ri^”+3)­hvq3&a–lP'bßQ5CŽĨ."-ĖŪh€Ô ‘Aš―šęÉę\Twd6õPšũŋŽpЏZ„vŽ$ÎGóēQ!kŸwUæxųä…O1ĄWŦ^(8†yVE°,ˆfWŋąšŽëé?}Ī^ÞqĀÂÔ[Đ9Pú@™Õąß@oJø āōĩ‘sN:‘āËĄ‰TÄgN|ŪđIčB˜‰›%ŪŅJĀî+"€  7eĒņHU$/žĻ']kišĢ Â]ĪĀ\œ;)„‰VéëubäBßÉDNF&åb΄Ĩ!\7å2/ëdIDä‹S ŨKgslÂFÚ&ÅåÅ܉FGâ;ˆŠz]KS0‘âėŅavŌQÕUŽ%…ŽĻKõšæĐ6aę%ļųÁ‹å-ýĮŽyČR–sOĢHJž=ü &HlŠa ĄđémIBAŦBĢCKîJPCHQ t–Q0ÉīˆÁ!čģļWōMRÜF9šØ5G;$Ēcũ€Õy“dŪÅۖAXAķāvNî"IWåÕrōA Ū‰BĒʇ“Xâ<ö ~Ðþņþåú€@@ ikœš#+„9HΚ&•–‘&ÂyË;Īâs”s]‡đ6ņō"AdĪh.1ŌŅÂļ&2 3O‰Đ–ųBĐ·Jn6ÕqzĶ–‘Ļ.ęá_4_(áÖ§r!Šøō•HKđ5ÁŪýAęeGd,ŌÓöd,Ÿá<ZäÅ*ģÚėD(m“ūPŊ·I}ÆY'N֘X!\úiŌ™Ž;QķðĄjīXH߉JlčĶ_L―jmÔ%ŠWƒ }rŌãNX܍œĻ!?ę$ƒF)8sÅÛI°{(ƒ;Q '€[]čXQ.åaðQŽþ†#XãÔuöHrëMÖq’ôāį:Æ<‹Ðu-N8/Õ5M‘[Z‘0ubŽ•dũroįŽä8„ÁüĒk„:Ÿ‚iSŨÖJe‚ Ņ âäEY”]‹Ķ‰ÓŒī4Ũ<1vō]8€6Ē ;ÁĻhÐn&Ą’ßįlÕ<‚ļÎ:-ĐÁÔĩþ;áę:ĘcP@‚œÜŅIuAZ,1ČãĀîČ#­3Ōþ /õOýņ9\LÛŠ3šģUĸ…æpÚÁBt<įYūâ81ï*Ōen•E%ī’wS-ŨH+ HMGkįQ5―Q›•ēĪMĖ‰ŌŸÆí‹ŋËūŸgâ2Ė•–=lhBâDðB‚ÛJĘ]xųĶ\Ī€ô†Tęj#ėĒŽ!3>ŧÐnÐ!?H„9?…]N$ĨÝŪŅ1oÍSj †Î{Ų"Uæ+X’!­e"‘ŨĐĘPh@LĘuNŽīŌøwŅü ýŽSx‘UĩHÂMÃj+s€•‘đÖ H$UZZŠëæ0uėZŦLÖj}…HSýËxĖÛōęē†šD‘jäŲÓŋäŠhþ“žNŠ $ŽF Ņ{(—ž"Jæ4!h5[Ū6 CÎq=åųŊĀ Îđ†Ŧ{ÂqY™ÚŽ~Sģŧ,að‚ųÏ­e•Û;:ýX•””ÚķmÛĒūýŸģá ėĀČo6†ßӔ8™‡ÛoøŨqXŲžÖ"Žø|Ą„īŊk”„vģŸÞ™óƒP"Í+æ^žKîDŪqŪS‚ÄMĨ‘'Šk\)2@órrց|nU"ĄÎūžŽYŒFh€">L·Ô ą(mŋ–6ęZ™kÏĘ ™‘Ïį1ˆģ)mĢü8A9éˆČŅ2ÝsŨĩePCЉūđwŪĘwōW~Úá/įčŋČÐÉMØzrâCŧÄSMó,ëGåøĢü`ĩcũøO°%ád„e€öšF y+Q–ß“͕ķP?˜rO|ðĢ:J‹‹mčÐašŸ mōŦĢþįädÛüų lãÆM ߆·€@@ °w" <Āeåžô iÓĶMýŸꙙŌT"s%Ú)~Aü8þ]üøãÎû… 8ÏŧóĩwÜpŲĮū%*kįv$ĘÛT―‰ŪÝđžËJtíĮååŧņá8žv,kW덗ß^–˜ЊŠr€ÄÏóÉãáŽG!Ī€@@ °W#€Ō€â­ŽØýŪ$$ÎÝ/6\đŊ Āā)Ī€@@ °/!€…rORBSížŪÝ7ØÓlß@!ô" ühðßĮáS@  ęœp* ;#ˆsgDÂq@  ˆ38áT@ vF įΈ„ã€@@ $@ gpÂЀ@@ ėŒ@XŽē3"á8 اˆ/ĐڗŨ$ïjãųđŅû2.;?Čņ~ïiŸqîŒl8ö ’•ŠņüŅGŲæÍ›=ó>Ņą:A?[īha;vīžžžĸˆþĩSv?ŋeË+,,ī­[·zHŌóėŦĮÄĐíÐĄƒuéŌÅÓîn?qî.rṀ@@ās;þ,_ūÜãF3ÆÚīióđnïî6Ž~Ū^―Ú,Xādy6ķÛ$[ĄÍ5–,Yb:uē#FxXÕÝ­{oŧN!fmΜ9ŪeũėŲs·ĩí@œ{۝í š…;;ĄiŽ?ÞŌĩ‰ûūšØØĄ[·nuéŌĨNˆ'›O€ ę AƒöUHíWŧvílȐ!öÖ[oY=ÍŨԉāÔBá|@ °W"ĀNžv•4ëŦŠmųŌEķpáb[ĩķČ*kšŊđÞŠĘËlŦˆĐhKąE{í tõķč­7mæG[wåĒíysss­X;ÅįņퟨáC\ãlŲēåß6ïcÅķķháB[īhđmØ\b͆ĨŅâk­ŽļDÛ6ßF ÚĨmÛķĩōōōÝÖ6Đ,hœŧyČėMÄ5Ŋæ8ƒ@*ë?zÏ~yųöÜė–Ó*Í6­1ŧāÖÛėü‰ƒņĒižëš6Đn‹ÝzĩŪģÞZĪ–YހĢėŌKÎī^íZ4KH'%ÕÚģWþĀÞ8åėáîmšu͎ ĒýÍéįŽyvüžcYÛ?Ó/íâ;ã…íŨWßb‹ŦR,ĩĒÎŌ3:Ų/ūß&tÏhV”įĪ]šÕĘŦS,ŧM KŠYeŨžõc;äúÛėˆ^ÍÃh{ŧöāCsqJTE ÎDč„s€Ā>šgâ$ŌĐ\eŋ:ųX{đÕqvßÓ?ģ~đf3_}Ų’š·Ž6 ‰`Ēc{[ßģWs‹ėÁëI{ýĶԖÛkoOĩև\fŽĻķïžýCËî7ÂŪųöANšdå:öÞÕĨ>ũʖÁW}―ĘNŪąŒîIÖ#ÚfWÅj_ퟛ€ŪĢšõ2hšÛģoŸmęö?Þ4įŸvöäs­ĸŲŋģ~þMK.[kïM{ÏšdŌnuD ü7&Ōōé›ö-ŽÅ‰\ïęˆ%iā™ŋđĘnÎoŋh’ŪËķŊœþmëÖ6YmŊ‹0mĀ“KøŽ ““é[„{'ÕŦÕÉ>ĮQb§Ý ҌV{šâ5ïi9áú€@@ ðđC "Ĩhcx6‡oė…Ð-œýĒÝģĒĩýâ†+mxÏv–š‘m}‚з“3ÊđŊØĨ§L°ącē“ÎĸĐÍZWbõ"„ĻLģ ĸ+č;Ð>âk6h@KJKē’ĩ ėܓąą‡hG{™Í+,Ó>·1ÛļümŧāØIvĀAcíÄģ/ē•åÉWÔ%§ZíÖđvÝgÚoŸžmIąĶÛïßÃÆÍLÍÃEdW[f/―ðļ•>Õ~ųó3,ŊUše·Ï·#?ŅúĩMĩeĸzÉ.ųÖ8;D}ûöEĸmó6VŠoöčÕĸmŨ\•}ĸä/Ųðņ§Ûãģ6YÕĶ…öĮ§ą'Ūĸ‰õ mÁú"›ĸÚT[ąÍlÃŧÚŪđÅn―ör;`Ôp;ãę;lsmĖŠ7žg?žðũķžĪÖbõeöčįÚmoZjŽÖĶ?ņ'›üåƒmôŨΰGgę^Ô5zwžïX!šÔī›Ā1g…Ӂ€Āލ‚ē9ŊŌ’kÓ9ÏúČLZĢMÝq.ŠÔŦšNÚ`ų2ûÞų—Ûēî'ÛýÝc]KßīÓ~ðW+Š”VØPū>HÓJķ—žËÎýúIķŧîČzûųe7ÛšJb3îu§=Ņ<ƒĐvï– Ąõ€@’N€Zϙ(Œk­šR&TiĨU•VŦķä”jŧûWŋīšý&ÛQíIëéhW}ã+Ö1Ŋƒ]øŊڋįža‹6iCēeJÔüdJ]•›[ĩ’įæČ‰ķôŸ/؃Ïūcß—gSžļÃfÎ[o+ ‹íĘ ķaÅ{mVĶ}ĸš3lh·–V,ŽÕWZ}Ę6{ðŠÓ­eũĢíūŧ/ķöąJ‘UӚ$$€V՜D^’5ŪžO0ˆ8kŠęī„ĨĘI3V[)3i’ĨĶ'ÛĘYoØG]íÆŽÔÚČ\ŧð”Ģmę…oØŌâ“lŦLãßļė;eÜöÂŦíėõĪ ŦŠKģ<ÍeæĶvą^Ý:[RåYyÕîj(›Žã§ÚŨ_dl‘ kųW+-•ãPfßLYzEØÕš'2–'ĮjlæÔG­hUĄÝzÅų+Đīm%Ų:―·ÎŽs3nã}’FŊūïi įž"Ū>·4WģbŪ/+ŧ―m,\eŋ1Į,-Ý2ēŌ­ðíŨmÁŌBKo#ÓmYą­_ŋEsn2ģŪ+īŌT9Ę$IPï åÔÖÔÚ~‡nį_ô=Ûbw>1Åļã*{cmŠýāēKlt~LÛYjZĶeĶÛGZY“ģÚ‘XUˆ;Sæá­GŲ“vŨZimŠ›ƒwŽcg­pĮãæÞˆæâR#’ėÖ1Ï^}ņU{máKNM·ī”˜U”ląšĖK-Ýfë7nÓ #É6Ž&iЖŠųÆjM7ĶhðQŽÖ^]kĐ2…Ũ ãJ‘äÆMeŌŽEÞ2m3-ĩŋŌbĐđŦ)IWZ™NÔk.ģŪķÚĘĘÞ·M%uVķjž―ĸ~‰ĨĻð6íŧXjŦūvúĨ—Û 7ßdÏ=ýë’"͕2wļ}n.Nå gcȄï/ 8đīïwļýá‚gėĮ?―ØVÏüŠ ë”l/Î]mcŽJģV]†ŲãÛØ_~w­mž=Ā^xøM+øö­WŦ ÍoŠ å^$ōŦīįï―ÕRÞŋŨžZķ͎>ļĩÞ:ߊ–Ï·—ŸĖ–Xh%-^· ĮŸoįýŨpŧî†K­lö8ŦŨڎcŋuŠU—–[ö!§ÚÏ&dۙmWKÓúÉYĮYšÕˆH>ëÛ!m―.ÕF}šþōwíĖÓÎĩ‹ŋy˜ĩŠÛf įnīÉ?šÄ&ka·ČÄšæ>öėCoÛ Sj=dŌ­.+ģš*p‘FYYfe"Āšš$ëÔŊ·Íūč/vëķvĘĪ>V_^ęäiu1-MA›ĨuVUV*í―ÞŌsó­K‹bûÝ WYUØ4‘óŨŠŠ­ũaß°ŅÃÞą{ĸô€yPū•·ąÉįoyšÞs}ēiœ“/ŧėē+›Îr― j™öˆĻSPP !,GBžŅ]‹ĨŲ€1“ėāÞ9V\īŅķ–ÖÚĻ ĮÚÄÃF[›V­íË_kđ)ķiK•øõÓė‡'äÚäā~I D?Ā ōr-ģuž}ýäïÚГ°^CŽmzŠÕfåŲ7Î?Ë:dĪYŨ>6搉V›n•Ō°ÚvîeCöĩŪ= Ž Wū 9Þ5RZ[Ėzô*°ī$1JĒöë}\ĩj•õîÝŧQsd<Ýmßū}“ļHåģäėÐ “lT;9ņl-Įĩī>Cē1ûēqcĮZk+ģĒm56vōöƒãÆļ“T‡Ū―lā⁖Ó"ÝZīëfýķN9Y–›?Üzīmšôl}úôģ‚>ų–ÓÅúčg―šfË7Õš °ū}ó-ŦeŪ}yXy<ŨØðÉßą3N8ĘúöéeíUæÄqZVe‘m.Ŋąv äŒÕÍŌ°Ą6#/^l―zõ†ÝJI Aô™cvŦĨáĒ€@@ ° ”Iëyįwlâĉūā―y—ÆīF1ÍĩG4<„lĩœaŠe‚MŠĨXfzZīBˆnÏĘĘJØGúB$%bķĒĄ‚ïMĨú:;ækčŋĖÔ{”ĒïžíÂÁ“j5ðØ~åųiK•ČޓLđåĸÎd•ņïuēĶZdė@üsîóŸe‘CTÓũE4ÂĐĻĻȟ‡ĻâÝûˆsũp WŸsRSS-;;ÛæÏŸïæËÝ 3ũ9ïĒ7˜5kÖqj‰YËqc‰s`ēbÅ Ũ<ŧwïūË! +{oøžÝ`æÍ›į;Ī@ĪÍ!܏ëW0Õ~*áŧ€@@`ŊGÁHėVv!°ycæK:šŧôģ‰ū$Jmg·—ĢE6•Ö­[įä >‰4ÎÏ;.;ũģ)œÐķŲV,??ßÍę;_ßÜã@œÍE*ä öJâQcib{eĮvh4d‰†Í\gs„Įeo#ĮæôŊą<āNÍ\4VßSm"tÂđ€@@`ŊG€ų?„åūžv…ŅĖĀ„đß/ZÚœÃ&gcȄï}OBXî3`ėБ€Ë`ėÂĮæéõŧP`ȁ}@œûōÝ } O@œŸ8ĪĄĀ€@@ ؗÄđ/ßÝз€@@ øÄÄų‰C }@œûōÝ } OXaaaģÍ~âĩ‡€@@ °— @įĖiÓĶŲķmÛyî%7.43 >{ MļÎLîßŋĸ•DĘoÝšĩĩjÕĘ#ČöM 5€@@āó‰"ØëtęÔĐûøĒãŲSŧIENDŪB`‚doc/images/ifw-updating-introduction.png000066400000000000000000002416041325366651500207360ustar00rootroot00000000000000‰PNG  IHDRÎbðZŊrCKIDATxėVŲoTį·*å5Ę_žķQĢ<”Ļ…mœBX毯ÆûŠmlĖRlķīÆ6c°ÂĶ/ÄöxžÎxÖ;w™{įÎÝlÏӇŠJ’JmĨž™/>šđŸ|;Ŋ RŸÏ=įw–ûŧįHrŧ݋‹‹ĸ>ūüōKÚc:‘~D&Ŋūú C)øxĶÎīÕūÂSŌ‡fþ_Ÿ'Ý=öĘۜô&ŊRZŦ•šŌüN{<-}Ð~fõŸVøøÉóÏ?ĸâ‹/>ũÜs_/ãņãĮä$@ŋ%ôÍ7ߐ= €ĖÅ‚ö ,Õčâ8 ÂLĶņčŅ#ëĻ”aķąøSŅĢ?„>€Ģ>–ē6–ūŦŨ‡vŌē Į^tÚčƒßAŋ‚™ĸúØG‚}MýøÃéxÆõŸÖ—^zé…^HÓuýÁƒðÓŧīī'ãņøĀRð—xH O3‰ --z 6ÅtK5ÂÁfáiSG5―ˆ ŽúXĒ6úÐőđRČ2Ī―>ēe Bx–õĸjôąŸ*u}Ė)–aÉJqZÚXýþ žLaõú ‘Š&ÏŽ>ˆUęCšŪ§-,,ĖÍÍÍąxÜÐu‘ãŊ‡g5ÅbĀ„œx‰Į0 Q:§Ķi$jvtå6ĀøBĢĻ ŒDĒp"Ísk⁎čĮŲ,|K s_‚e$Ļ­æĒLzH$`ôYX\zļô`‘Īš_|ÏA}–ëĖĀĸ?ęƒYŒĸW}Åþ%‡KņØ­VKþ+ŧHN b,›š>ôš!bÐ!ģ0qðca"ļ–&ĸÏ1!BH+|qČ2,Ú˃v|īŒĨš?õA'ųĐëƒÎÔõA'ÔZZô™{}Rŋ_pŪF8Ÿ@āÐú`q‹>ˆ4= Č$ų„ōxÚŽĸõÆÃŊþüčëŋj*+$4Įõôôīķķ655ĩīī|ôŅGn·›”œÃëŸ=Ņ|8kĸ›oWŋ–Q·ņāĐJĮÄp"27Ŋ/ņ>" =MÓčt>Ēa)Ž XēEÂ2˜}ČBĀĘėHÖÝÜŨNŸYq9î;§<Š>—tœ{rØ1-ƒeŌ i?2îŠĻ:=6DaIh}Ā/ąÞI—_KVĢ5_ޜ…˜.ßëíėčžzŦŸ•Ø|‚ãÁ_dU‰ð#ƒƒũî}60<&EÁoýâP ēĖú`S‹>–ą“}õāŒÓ ÃâÓûƒ|, †"ņΑĄþþ{}ƒ žš HxL―}íōĩÁÍ vƒZTrU“·@ũOŽŧ|žNū#ÅīßúF€ũ™Đčƒ2æÚčCŊÚī>Đß/œ“æÐúāi!✨•ô#E}0—š<>iŠĒD“PUUI"ār}Õõ›uRÓЇÓĢZgÛÝ­›Žo|}ĒŋēdY&|Ļg0lkkëîîęrwûöíæææééi(Ej-a+ŠË3ģŧ>§Ēië` “yĮøÞWē2ũū9ėŌT 'šĀiöĻËĀGtœipĒaq"°Úh ŸŽD`ŅĮ\Į\ŲRßœvú(\ÃïŨmČŠv̰ j`QÆo3ęf•yCSðÉĄŒŠÏ‡'>ÉzũČ#ÁŨ„âЀ„āāŸđũųĪŽ€W?€47b ã]M9u­ŒĪāQPÍÐDÏņ}Eéé;ęÏ4îJOÏ*?80™ÓdGßļOœ3429ŒtÜ)ßē!·ŽzOUYõŅSnV1t ûEáíĒōīcðþļG7ôeņß߇$ÆFĢIýuc^mŊÎŦþðþ)Ū'A’NŠéŧÃ5 ŽÜĖܔžWūgOeiÝévŸ úō‘á ! aëņ˜ÜPđskŊŽ@ ã‘Iŋ+˜x!Ęų&û‡'Dň/Čgũ–üáōPŦÂĄ$°žKÉïĩŌþŊOŊIïTĀÕĩŋ_–{Ar +Óũ wa™ū2XŸū_ĨîŨFðĪE"I’ä$ĀŨ''.žüģŋŠýwĮ…^―üŊ΋_ŸŽŋĩæÕķŌRį#I!nÝšÕÞÞÎ0 þę‹ĒĸÉíęęË&[DÏ]nÜ^ŋæŪôũE_ðoýė}# m…-Ÿ­>ē yčA?9ĄZd`;bc LE$Āēæⷈ€-ú` Ö5†” @“Íú`/l‡ŽcĢ,2§wŪųéËéíwĶī……ĢgóÚWÞÜvĀ#ëaÖïtŒŽ:Æýސxĩ073!ƒgƒ!6äŸsNøBžfĻCįOo);106Åpb˜ Œ9 +Ā °.áPĀ Ir4Ė…–ųfɂd<V7”Ą?ŸYĸË-7&…ÅÅE-pŋ`íúŠú>äŠÛ‘YÓþYŌ‰ÚŠę­Ė/ ęą9CՐ'āóē;3éœtyÑĻĖ{ÎÖūSXsÜéaā{H|Ā9:<>=+@ŦˆÄ2– ÍLONûB\0ÝÎąąÉNn„ øƒÁ k|ŌāyŋĮĮ†Z‰œkÂ9::ęe€%Ŧ҈gj|Ä1Î,Edy0ĸ@wîî#Ìū0gĀ]füž'‚ÏĮžŽ †|nÐŌ9åæEy~>rzoq}Û YUü^? ŪhTųƒ!>ô:ԉ ÖTąįŌ‰‚ƒãþ°a<ģA–‹DYf&ĮGF>F„ _ Äóė„sl ū‘ąßú:9Đïú ŸŽšÛ°Þ/KYz‡ŅCß/:7…ûøŅč‡Ó`DøCÎ$Đ#ë]ŨkŊ<ÚđõÛĘĒėŦüķšäqnFtÃkíëŨú].Q’ŸĪtvvöõõy<†a8Ž …B^ŊwddäŌĨK~ŋŸ”!‘ũ~_ÓŧåJhŊÔp[9{CnėdßŦÚYÔļÅëó+C NEyžGNK31d~„7Į1č"Â2cÎ2§˜›N…ß+‚DéjæÁ°,9iÃFõ)|kíš Å ’&_oy[úÛģũÍĘŅŧ JƊr2ŠëšƒĒý$ŊüØ4q^ŋPZ\\ĩŧ8cûæâÚæ€ß}nÎ/Öm-Ŧ:ðГék? Y…‰ŽsLDqt7—;†ģ§ĩīĪ„d՞óó •ĪˆŽIÞæc9'ŊɊ EQŊޚ_{ģŧuÓúoïČmlŋŇ"Āú{ŠōŠn8ƒĄ@€ ņë>[™YX―·0?sË͌ ―SÂLïķŨŨl|csU}ŧÏ3ÕrĪ&ŋĻ`WÎŪó=C҈ÐqŠŠ xw~aé™OGŪÔïݕS‘“óÖ[;öļ&éáķ#ĩđY™9;ĘšnŽ}ü~å{W†YļvîøöĖÂōĒü–ÞAXýÏŪ5æäįV>ï‰IDĒQÏ―îÜēC7'‚L H|ĮŅüšÆŦQUáž#5ųŨ‡Į>Ž?PZZôÎķĖc—û5]:YWräâ %â=TđįâõŅĻĄÝýSmų‰ËĒînn(-,ĘÎĖØÝÐåõOĘÞžnÓķ‚ŠFW˜k9XvĒýN8Ė}üĮšßeï*ĖÍ+ĐûÜũAP!3|Ī”c–+-ĮrtMĶeÆÃ^‚hĪ„”2kLÉdzøšŌSŅÄAáäáĢÆĶ0'ČghĘëÞ{ÞgÏŲĩŨYw•Ë5ÉŋþŧũũýûÛĸųũ>W\Þđ>ŽŌg’$)ÄTÕävEY-Y6rĮÎŽˆáŋ~ē  UUĨ†aõz―îgƒ8 ž,Ë?§ôā‘^0ĻŋބtF™Õ+ðGgBAĄF #J‚(I@‘ú ­*Œ1x2 ^E’–ˆ!ÁƒĪÖh:E)ÔĻT‚§íf‰Qm0EņÔ(…HĨ‰fí.|ęĢÍ@ÚČp‡úĀ6‡Ÿ6ÅõÏ3ím_ŸĸîĘģgZ°,ĘũŒ›š_°2#éąÎ/ëūïäÄ)―–0Žy―Cũl֚ėũ· WԀļccĮN^”•>j 1âÁ)/­Z™‘8ąó|ýå.AeR‘ŽĘĀĸ\öj`]lžÔåQA hS°ŠŒŋ Ņó}:Ņcpū—3G?46:b|Ō’yOÞŊĘ"ŅĖč LWûgŧ‹‹Þý úĖh›ŪÎ.Éõęå­|RÚŌiŠ;Yq[dŨMė?dQãđÎ6bÁĘÂ5iÛ}sÝ-ÉūxÝŌņq=].NfXÆõEÓW†!–åŊšú@Œpû_ĮŽœŒyü™ÜôxwkmKëc L”TN―zîāŪâĒũķŸi퉛8Ūí|m{·rõËŠ!fhtøÃOÏ}iuÁs#ú_øķ—†eYhNXRvc$Š*V8†AÄÃ'LÏ+(ȜýÄ­ķooãþMŸ|ĸ˜I‹—Í 3â^Ų[Č­§6õ&.Zū~ÃKCPہēÓĀ—~üÎþāšõkÆ ā— ’ĒŌþ€ķߝû‡xmįÓķ!þϗĪ1š<ÓütiÚĨڏÚâsjĻNXQ  i‰§īĘi@$Ņ…þãúøTöÝŨ`w_V "Ã͈!  „b† aÃŅ―ą(:þ‹·xŸ€ę$åĢtŸúøÆš^$xES­<ˆIrŸ~ĒëŌ'ęŦPÓĘŋ^I– ;24.ŌÖ]VęčÅĄũFõc ų$Ūõ\UŅĶ·ö|~Zô302@Uļ€ yäŅ·Áyöģčž—IoÂ"d–ļ įNn~Ó˒ŒFTŒ Œũ(óĀzÄ˒“E§Š<Ī"Žtvó*hô Æ=7@~&ŌÆ2įæÉFDAxN24Ņž“—›5iĖ OŊ[g ņČCþXbÍ―N™ŒÕ€°ÂyÜYōp%{·Ÿ<Ý9˜Ux‰ĩčb‘].ÎĢ0‘ąąþHíŠnt{xĖDnadž0bAĒ›·Îœ73ärUᆭ'Î}ßÝu‹sđūq–nß[К#šŸ:! #”Ļ=ŨÔčĒ$›ÏųúoęĢ]‹Ūĸy}čĩĀŌwY ~,q\ čĮ…FCcҰ‘pÕʑ1—,6ÃĻ8―ÉDhô]_iĄcL&“EcžN§ĢŊ’9*8VüQõ“ý‚ýú››Bތ™ï"lũęIH•@@đ$ žč8}óøh#\ŠĐ 6'ÅÃ,ņ49Ņ@ëCŸ“‹V–|Ô*§–åÓú MNdÐ$„rįúíôP„­Đņž_ä˜`―ˆYî―\Ví|äų”īg&ę‘Ę0ŠĒē:Ø*ƒtŠāöxDIF0`Tîæ-·„…ÎöãŽúGÓԟāaĀŌŦBË%ˆ$€đ>mЄޑQũ]h9õÕĨ[HĮš:ÚęÏGņŨ)ĒĀõÜļ-bLęCķˆXVÆÞŦŪ]QVtˆņjáŊ,†"•nARü –P[D|ڋ…ŊĶÆĸ^/q Eðš“dÚėĮkŨ{8Ïå {Ýýî ĩ@Ėŧ8^„ÂxEC]Ä^5hØōõk'…KGĘ[ –␰QÍ\ąjõŦŊdŒŽēI"ųž%z…éYI‘DĖĢbÃØ #KöžåÂ~7ā›úēšģ|rÎ OŒŒNe°ŠĶ õë―~ĢģûZ{˅Ŧ{Ū}}žžuÂŽŽ”§G"'1*ÉÅyß2PxĖē˜AFK`0ūÞqŧKōÜ:ĸÝ5[LĪË Uįvqœ ˆa(ļĶĀß}ĸP˜–rįóEķ=ÅÓóEĖ'mZ*•hð9ÚŊ‡t]J!ĶĨøl‚ĐQ…wŪųĩúĀėĸŧ>0‡ĢĶĶĶķÏęęęĀ;kjJ7o:đ2ĸâŪ7ý~ßĮÎWÖ^·ĶķĒĒÖé<Ā(ÅétÂĸ9ŦŽŽ„ëččhkkƒYøí-Œ"!ĻvT}ðéĶĒýđG›6ŸšļũøŲmÅ%Ëķ|ļšēŠōü„ĐŪvôeŅ$4 St‚‡€îBģĢŧ2Č@=°ÄÐEÉļÏÚ=ú(ņIîĢßgGd-R[­-ا>^ ÚúT•f.(üøÄGÉû;wv4Wþ­895ŋēĄú•%s“æÍ]’žôĖė܂·+Žî°/|ųXuCɟ7ĶæĸĐĒŪŲqdĮ„…Ÿ”ÕÔߗüü,{îĘýeĮŨ/™;gþžÜœEI3ŸÏ[ŧußŧoĪl:álvðƒä„„”īt{röûûŨô=OŊ°ŧėģžJ秃°äī5ûVՕ—ĪÎx|EņþS§škJÞOIˆŸoÏĘÎLž˜üúG‡7.ÏZũöæoäÏ~nVjf֒ é…ï9ŦJó-ˆOīgf,ž•ŸĩqOŲĄísf§æü―ŪvC^æšwö467ė~3gނųéöyiKW–:ę+>ÉHYąûÓ--uë_Č(Üöhú79įōGÕDqĸ§Qذe‡ØðŠ"$1ŠōOfē!'ķ{ú1ýîžĀ†ü=üÆG]]uœŽ ĪTFåęzœŠ{šnēð(P;―?6ž?ņæLïzb”6]ß/HTâĻ"4pō0‘SUÓũkšĸ?čkøÁs4ēÝnÉÆH’„‰ÚŠ*‹ÍŸĸþ҇ëOïf?ýØå9ÕĪĨięUĩmËãfģáŋSXŊŨ t]:™ˆJl4mýëģ_,ï~ýĸū[|ē:ýĄĻģrW 3*‘íĶĄ=BG%3Œ åtŽðI@Œ€íĶ@~öØh‡<ķ`ĢSčF§‹øœZyÔËĩ?Ö?Å0tM]deó|?ķUžîšŪívyŅu-°eۏm›§ėfŸ&ÛēŽûĄ‚Õë‡=ĨŧšH•g[ö}č;6Ļî†ūĐóŽĻËj:•U=Ž]–’SõÃXïróÊāĄóæŲúgþŋuëö;·ï?nũϛ*+rĶ?$–}ßÕuU_I‘ï†ąošŠ(3õuMnؐQnh+‡ýXtI›Šņ9uUÂO]eĸ{ïÝũŋúūāUÝ8ķôĻęnlË4%=kÚķiĘ4+ÛšÉĀŠÚąŊéCW_^^ŧŠĖýę3;ŠŠ*Ŧę0fö}M #1S–ãŲ—|W5M74gę:†-pðŧáqhr8ôßôûķ.a§jÚū­!ŠČrĖĄÝ ]%įĘōŠĘœfîŠmšŨuŨ7…^đ.2đ?š\ûƒíl‹JœFŽïūōĢĒūa4jAŽ†•dō~)™7"§ŧLÞŊpΙü*âG2ŸųoĀÏgŧļļõÁH’Ūïųîß:Cû=؊žŸŸcĪLŲėŋe;|[…äq$™ĶÄPšŊÚĪÛtč%ŋøëÅ0 â!ŠV‰ īðuwČķs4XØÎsF…Œ!;Ų:,‘gRŧܞI~·=†Ŧ` ÂăžäG-B~ˆåčc#ļ4ô[æv†<2Õš-į—Ž õT ]ė õS}…gč’įE?þņįPx(!„‡4 •8ŠG8rŌė““•laÂFĻE3€JŒŽB (ôĻ|„ĶÓTžÁ™ čDī@ŦPåLō#@ •Düšïģ_ϏĀņ›Ïc~š†ô$?úč sø!ÁüPõ:üČðĢ1~ČĮCž?ž2°~0øq2N `p:äG~ŲâG :ļÆfÄä+ųŅãäþ(íÆ{??Øņŧ ö™ä‡„ y<Ēõū? ]ϏC~tØŨåG†.Â|~0ÂKt3~pšäeü ūýGî_ ÆņņąŒP"Ïdč›+‚-ŋĢŌ“Q~äläÔĻs͚LļwïžĢ3äÍóƒį­â'‚ē?âĮž7ȏėÐyðß·s.?ØsøąÓPo!?BļžŸŋŲŧï ĻŪþāŸsÎ―ŧw ÕöÔhÔ$ͨҘK4–'ŅhĒĢÆĮωXÁBĒbėØˆBĀˆDJbÁFS:ėē°l―å· Ļ!óĖ3ŋäųæIž™ûšqĮŲ;įœÏįÜđïŲ=üąÖIþĒýąúïî4‰D"‘čOu2‘H$ýįQ+‰D"1jE"‘HŒZ‘H$ĢV$‰DbԊD"‘ĩ"‘H$ ú›C[ „þ†ĨáÖŌþŨvBð_W6””a$4ųÏũŸō?ąý" Ēŋ/DśŒ5U5*‰ãĸÍC…Zü·ēŠPņl}uUeEec“SÖčjSĘŋ)ō9ø7þΆƒN]øða…ŠIôG'ų­ßsSimiö‘ÐoĢ. ‚ŅZaÄŨU•?zTĪnbĸø>‰ÄĻÄ$˜ņ֋CFŽ9|HŊaï&Þo”Jü g6éu:/ŸQ„ŧ{9ráÔąƒ^ýĘ+cž8råæÓÅ ,EPs)Ž^Ŋ7q|ÛA˜ÓÞ[:Âë…~}šwíÚĨŦÕĖ-įĖ…ā_c- ĸį Q4yœ7aäČwCâꠧ ËBĖÆO―úũïÝĢ{—æNúŽōI-6Čhōĸ5žÐ†ŠĖāí›öĮ? ôï,@āÍ―Î`äÉdÂđM+GŽđ)ąˆĄü"1jEˆŦē—Î_{ĩFžóÁžŲþÞÝ;3,g„Ĩ2…R!' ˆ’ Įũn^ĩ.ð‘Ņ–02™”F`%R†aĪ`ÂČЄĶ%Lëp„āw"ę^äþ)SžË(ė;gÉây―ė ‡ķ-ö[―§Ė„Ĩ)HÞŋlؚôĒzFÎĀŊņ抒’Š&xaøĻŅĢ^ķdÄĀž-‘ÉŠĶđ))M@BļÄ―ŦWSq2ƚG-]Ëm,2 ̧ïČĪŠĒ%6JĨ„`DhcJ*·Ne‰@īDj]‚ĒåJ !özøO™:~ A‚ĩ#J"W4Oސ=û”IhĐŌÆŠ‘PÐB QՔ–•1ýĮŒ5ŌÂ{°Ģ-CKĨ­ûl…$RkmČR$#k™ĘzGZŊJ8Nā˜Ē-ĩŅķŠ)F&o9XĀÄú›æû$Ĩ›kĀ”ą&wmĀēïâRyFÉóĪŋũËS§øs·ãxūĨ…ēe‡ĪļeĨæÕ--B7_’[·N$F­č„°^õ$Į`nßkhČÆĀ­ÁĒÂw ínà ˜kŠ=đå#__ßũ}ž_Ū—I„é ;‚BNFœ Í(Ž5kŦcnÛq⒖ÃÄ\q8tïþð2áŠóŋÝšûÔŲ I‘ûüýüæ/[ũPc"ĸŪ/ŋĻáŅĶßTpķo|üÁĀ5ĸü:6bûŌģ{ŋ ÍQ] übKddØÎ][ΜÏÚ>Ų4ĮŲũąũDtlLtôđĻĩ ŲIgwl Íš“·oÃ'“'ûŸ|3tޅ3k·;yôó›žõ@ē^ ԔīíĀŽ”•óý\ČĄėœû{ âီņÄÝψ XėoŲÏEKÖįU뭉MpAōÉïXޛō]L‹éß”! Ā?`OLĖŅįΝ ę!ÜûfįŪ}߅W0EˆĨܘĢÁGâ~ŅŠŸÚŧ}úī)ū“ßþúø…zßrÚðô€<ÉNØīmwbN f$uEYßnZuíĶqI~jHāZK~sđp“ÃÅVG =t:ōôĐĢûöG”ë8Ûn=‡ũęá åŒ ÓX4gšeȇ‡ËŒīD"hŦ‡FĶæ%8cę”éëēŸhÅī}NŒZ‘ Œ“›Ŧ‹ž4ýüīKĒRnꁹQ2lÝí+f­Ü}.Ŋš*1ęĻĸĖ5Tęėœ,#Ģþräé‡e\ciôîŊ6†^Ö°˜æ5‡í ÜģģD‹Ø†G[‚ŋZō9‹ūØõó•Ī“ĄA—í/kjsŒ0ĨP(EAüëĻm,{PP_kįÞcŅžĐīąIĢŅ*ûūōŲÔū‚đþQÁÍĘē۷딟’›VŋyŠtę‚Ü_ēģoæßĻ5ðRÂ^ŋĩqÓŠÉ|‚ÃĒããZ―:ārą^•ŸĪ7>Iˆ ŧō@,oæ[̊îT\?ąuáÂđĩeŠų~ûöeLõ›ûųĩËË ęĒËÛ6ޚ4iJ`D|ōÅøŊVÎúōD2Æ|Lôą _­š:ų―Sq??Q7Ô=Ėܰ:(6IŗūY0wþÁĻ\ Ļ2SĢ j 4ë'7úžïqÞíŒþcÁwq9˜‘"ø-UŅík'đĨĩ2Š>―gÍšM đ• %SSxcÛÚĀGõú'ũ2v‡†Đ ĶŠėÔāsŨŸšÂ!‚=Ûíęŧ ›ŋ žrĢ”RHUnmÞþ]Ęmš.Fœ>|:šįØüŸNþáŒc7*)íãØÔ\ĻČˈ:“` ųýĄ€åQyĩ ?Ÿøú­·? O(ÐTÞ>ŧkÍŦsūH{ŌD›ëÎßĩjņ”7ü>ŽKžw.čŸkk‰C ‘ĩ"e\Üąj`gŧžØïĶŧlkX™–{˜õÓūČtÏÉŦģÏĮm^ęW›v.ŦqÆėéŽhß%4þŌĖWzœ‰PÏķÜ]ÄņĮ›k"3k”Ųŧđ/æzė‘qŪēŠØýEjŒÐģ 5Ö…8vøĐ#GÂï?iDOӖĻŦ*kR öĢûšILf€7›Č€!/#€šútįŸŽ_8 ÝšČSë&"'Ās„‘Šo_öúð!^CĮMûĪĪQ ðÛũÕwĒãįūčQWUyŋīaüʝóžÁŪßkQIi§õɉˆMŧãöR@â―Äuþžrn]Ë}LK()Į%öo.؜|%ōí Fš‘›ũĖ)7"ö8PÚã‡+šXx^0|}úŊ~<~áÍHZ@„B•ũn—”éÚŧŽ ÚsöjZš_o'Ą>?hũÉŌ!ä@lVônđŪä|ōĨĘF„qÛ?NœX;{—ũ0ŊĄ!ņīó +ĶbĩEЗŊЍ\ÆÏ?ޗŋ6Þ{@ũ!ã’Ŋ ;šĸK7sSáĩÐæøY@H ‡@ŽŊ4ÏŒh꧛._ŧ~üô‘ģĮŌŠō'å*ė2lĮÆEðڜ€„ä#=•ˆâ8ÄóRDáúümÁGÕžKPTėÕīŽ ĮÔ\9z2!ƒEk°ņ›ŋ'åĮo<Đ’ å EˆÄĻĩāü’ĸŠsŅQ[VĖĶđÆï·/_øbĨZe(M9:Ųgō氋(ŧHE(Ö$0†ÖĩÞ[Œ1ÁČšąFčæí=ņ ŊžCFžėëBĩFŊGðÂŽęþÁŊ·ïīÚĩ+xý͐Ôû՘hÆó`cg+“ƒÞ˜Ŋj‚[áúÚ2K:H°€ @„9 -Îdēé:bßÉļŸÎE†ŊqUû4w>øėËá=:šH%4B žGĻų âyŒ Š­ ŽāÐīąūI)=ŊŠÖ sfÖyÄÔUógôïæéâ X–eœįĖ{l_ïaĢû:{ï~­žC`Aæ­Úāݧ›“Že{Ž‘íõúÛc<ß9ãĸæøiģūČ-֚īŠj]ÅĶOßóŋĄānyCÖ„jóĨĀįÓ―įããbŋŸ3ĘÝ`ā―}– Ąā§’ēnæ'ė‰?Û`mQƆÅģ&ūõ֜åAåü;€’ŠFk™OœĩaƒŸ‡kg à „ˆþˆþķxžãqįÁĢú ęâ&ŸūâƒÂĢ€C|6,Ÿ&GŽ%šlÚw3ą%œ ÂRÐüÓˈbä2UqEĢF‡ą4šį%€ÁÐPU @Ņ@=[‘•ŧŽLJä jÉ#–™Rə˭Ã9ÞÆÅÃQÉ<*ŧŸœëýŽŃ + ?sCIGŨáv Įņ,€@Ie #Ņ™ĖB›Oę<ãÜþõIzXËLz=Ąg0ģR^ZŠīB`ЙĶÏ#`\ûø|ýõ†ã‰DÚūĢ›QO@A!ÄZ<ÂÖĄ €°fC“ @Ņ^)E­‹› ,Ŧ Ãóvö}&ž:čƒK1A;·'Åh4ŧĮėũâĖ–9Ï]8ÆS& ,Q8vēXN€ķúŒýÖļūÖÚÍFNoÓiāôY/­<―mCŅ mŧ-oŒnĮ–ïÛ<óX|§UÁ[Įy–,žû™Ó…á9Ä#“ĩ6ÄKAĻ-+ŅP”DSx%ðŦ)Ŋ†Å†rɁ+ö]ähš`$ðœu ĶMÐóÜ'„âx0ĖM,Ќ„5jôÔz)d6ĩü€6ī!ĢV„ˆĄ:įxtŽg·îIö­RP*eîÝx:Â―ŽŽĖ;ƒ†ũîPrįÍ:té-sD”V]wîØI—YÛKī9Đ'öïƌ?~ŦTãÜ­34ĢhüäÁøóĘ’ïŨ‰;;ķųF‹%ķNmÎ%„įŲ$u{qŅdßôo#lXėÆlëÛ_Þ‘ņÐÅÛÆÂ—‰ÁČÐpåB”‡ïþžáéôp„čŦ O í Xž§œúMÝšqžÐ’ėÏjá)ÐÜ=y&’™íÓgØ{ Šš–ykðˆžNE9LŽ}ŧΈĀZÝģú°eęKG īp+âdn-ôž7ļ“BŌ|™įŸÂ[Kī"4Ur=>ģHßsțøĶæÞþąšē‰8tÖŊã݄âkŨŌûš Gõ•%\ÛŽŒĩŪ#€u]€œÄðƒõM&°bðĻWuwðöņiw*-%1ÃsĀÛãđu%ęJ(lÛؚÓS~)o„ú›)+&ĩíó€ĀÛĩũ0&_ æŪŸŲē§ €B`Ö5 @uRRæŠĪäbMŸŸ–\;ŪŧLfc ›z%6ú…ņã^ä[įā$–šû8D\đÎŋėt,"ÎNût&ŽXîŊÏXQčoIĨnxœyŒ€…ĖÉQ€zM\p%įAcƒ*5fϘŽJPØY_ėKŌé4BVŧÓþą+RÓXūyķõHĮÎúĻgWgÏÞ Ę2Ų*)›Nō Y§~ŧ~ĘRŨŨĸÎŌÔęšâƒÛWpĩĐž\ˆ ŠNoÐĻëÕęüäãĢûķ€ÞKïŠīõŠ–~,ƒŪOķcāŨ/-­,\î ā–VĪo*ĸr\O‰SŨîĩuįŋYÐC €č~66ÖFÝ:ž·ØØ*ƒÏä4TĨO Ā}ŌŌĖ"UC―J­ižvøS‚={ôswwĀ–é_ŸwõvĐYS2ËĮ  ÃĐô"ZĨŅjrØŨ`ÄōPĩΔžg‰=@;''ZŌeÐøƒgsՍÚōŧ—ĶŋF™Œðœļ$§ī^ĢníDgTœ;Iŋæļ>üĢŲP_’î3Ô€,ؒĪmŌhT51ŧ:P”ė…q3õå–&…ļ9+―–„5ęīU…ïé V/Ė_øn{Ŋ~~\Uųpõ;ØšųÎ~ŋ““;ũŠĘŠ0Ômžþ* äÂo~<hYXšNŦ-ÍûņĢ CKmÚɰÜéģŅĨŠ&õĢī·ØÓĢW—Ļĩę{ Ý:*]_™]þĸØ;Ÿ§(Ž{<"ĒâIðæ!^AžŦ'=ėāAÁĸÁ‹„u؃zRžøƒm:īÍögŌĪMĨM›4ÓĖ›™fYe―8­T&ë^DÅK>§žé{™ïũ <æÔė/V^2ēĸ@Ȑ@L‚fĩŽí~ßäÔôrJįL>ę˜:Bš†+æ$ˆ(0 3§VFŧCŸqÏüJQŨ‹õ)™ũší–íD°ðJ/ä}õęõÛŚƒPĄŌt™āŋŊŒņ}Aûvãģü–öûŨ›wnœ?{úܙS.]ūĸd'Īāđ. jĢKĻōö8ī,SÁęŽb‡=Ól$ þ mŲŽ?›3Ïėš4ėxĄŒÁ†nKGZ>ŦĶDsˆIß2íÞ0Š—Û,GíģÍ'/>~‰\ŦаÞÁĢó~Ŋ#·IžROįáÄ1͎7ĄQčÛÍJ^ÓōļØéû|!d  GĩēŪå6ŠÝĄÔ Š} z­”g`–{YūīģLJ‚ú^  / Ŧ^@ļ="ōtlŧÕņĶ a,īu”Ŋ4ú„„Ët―‰\ ýnYĮša†„4Œ".ÕÆ3Ę›CĢ„ ؐ'ûCÆpÁJó|ŌßÓą–Ëï™åi=’‡ou―xeÛą[vÛ]u:#ĩęL“ðō@ýaÍz`‘pļčÏå=éĘĩ[=þ-Lō‡ÚøŠ„Ó&zûčÁ͍ŧo>đ\pXK;Ώ üTКšv”ŽÕWŦéLėįžÞ“ũ‡;åÃÃTĨRĒl“jÔ/)nąŠZt4ŌĄâBĩĮE˜šĻŠSCՇZĒ:9Ō- ý‡ddĢ6X4nmom―ûļú7á"9øō5I.øóĖ3s[ÛŊ į;;įģ+KvTýX+bgÛŨī%ðyŽä7^ô=úÄ[€x c1°ÄĀHŒÛļmŸĘϊÐq[ ŲîkųŠ›ŋšėŧ•™•--­ģvDýÛÓÐ]ü‘ļļĪö۟>~ā_ýü?ĸãßĸ õĸļ˜v†ðŦ_ýōŋ.―ļĪöââââ’Ú‹‹‹‹‹ÉÅ'ÉÕm~qņ)ā#Œ0TÕËˋ™UĸC)ž­”ČlAĪ—―øāĖâ7ŨsÁŨOÕĸđ/ÃTŪßļū^Îþ‡wœ ]šz `}ū.;Ėúƒ >ü. >؜ĘMɇÏööģŲ ū:@ķûýõSđ9ŽÃL/――ļøÔÄ=öÞAjß―{ũwĸĸoĸé?0S[GFŒpU‹ð*!ĐŠŲ⁠Ĩ*s­å™a4Û·RKD•{oS›:%ŠįÅîSöÁyžŠũũÞĢ;ģ3·‹ˆú!9kUõ\<UîAr*O<+ HŠTfTŌÝEÄĖxDe`ß1+—­đodŪeá_ŋgeÕĘ4ģlĻŽŠÖRĻ2#[VGøûÓ7ęĘ%UŠúÕ/ūú‹ŋüŦŋþ›ĸũúúúk}1ýčGĸĒŠrqqņ)ßĸþŸųåŸėíßTjUõŸøÃü‡ŋĸ­ũĪFfFŽeŅωYDĪ™îó,ĐÛíeæŽ3ã8nR••Ņ:•Uú„îáfFęy%ÕÖyŠ`UÍ]D ŠUéî·›ŧg8ÕĻę{ĒÚĘč›ÔågÉÛóÖē}WÖq;|ŧ#ÄÕr9ĸũ­Tö#‰ÔZGķ–ˆ’Ō˜UfvŋßąÖÜh-^Įrw)ąĩd„G oģPVĶŧŦÚ|ЌTS€{šJÍ|ïŊ~þóï|į;]üU?ųÉÏūûÝ?ýÞũþ,"åÓāââÂLüãýéOöíoų]mcĮzũŃũ{oUåíAZŠÔqīðeĻZ‰Īŧ~ņ…ŠæîĖÖö-UzlR• Š{PMųŪōųđÝÞŲē—ĩ•ū]ÕÚŪęyޗ­‡\Š ï˜k­ĘÜ­›Įqļŧvi#BÍLĩhĻéã9#‚ ­% Tf„ô2žŸGHR™-ĶëAļ§Ž[Õ―7û!Š*ÛËí%3=âv{!áūM ĪĐEŽcœõyžĮqP"áÞ9šwĩl äĄÚ_ž`ōX%JūõÅŧũ„ģÞŋĸâË/ŋŒų4ļļļ0ģũïßgæGûđU5ÓĖl7g™åą—.oûy‚ēú‚ˆhõGUféŌl”š•ûu“<ŽãQa”å!Ģ•õTsSûÞ @ĮšjŦEVÆŪšïĘÉ žÞ0$ĖĐ,)ģ•‘RB[î3RG|ÃKd­%ŒVÆąŽĖÚRãĮ#3ČQęķķUĀó˂xŠvfdŒ!vß@/ÉČxî mؘôc­ŽĘĖūãsqŋš;‰Ž2röÍVýęuû ČÔōipqqQÍĮü―ÚĘriąĻ_šĻ<ũŪJŠĒu–ŠfŦJFžl­Ép+ë8ŽĖ+JexHĨ*ĢÃ\Į:Z§ē‹˜ŧ—TIĐ,‘g !Õyi|nŽŒęÓ<Ķ…TŽŪáí6I•Šð "Ž2Ÿ ēĨswĒjÕjJðĄāUé{+õGL53Ëk=/Īē&fÁ>Éũđ&ƒžÛ‘•å{W=#ŨjēéCgkīYŧ~(NXņrqqý48€9ī‰FJŠãT)1[‰‘ĄNr·{dH_ðôž€voeiQæy?ŦBM!|nRÍLPRĻŧW”> ? ÓŦŠSÔ5§dĪõaÔĐįĘŽ…1GāÞÏŠ*"@.ģq—@QõqîT•ėŊ‘RĒäČýZöŽ–ūĖ 2ē ŋš ÄL3‚ 4î^"T…22ܝĪwßՙNIīwu?˜.)éW;‘y·€ĘįÉÅÅõ{ĩķl2ŲŽ@ĩÅB[9đÝ+Ýė N7ŠÝ^)ΈXëåÞ^Y4`ŧãĩÍo€XËzsv?ÏĘ|HϜō ÐéŠøđKjM&K-)į>I ߛd_ĖŽl‡Ý ͚ƒþ[ ”ó~'Đd> ĻôŒQgœįĐĪ@îh?™îNUiķ;Õé )1]Ņ,3ąÝôIFė}ÎĄYf’ÏmüÜ ”þü@ŠĀåj?G..W["Š $įļ| ĪÛî›īĖw{p[AĀlŅŽJ*k-Af(ޚŦ$ Ej›LlŠë8@€ŽČĨ–-p)Õm[5mēíg_!ODfTŠ ĨķïNZ‘1Ï 3ëÍ\ëÖÁîžÍόJķ T…ÔˆÔ6ÛãÍK@Ō}§ē “ĻøąÖT.)Ķkbg !ŒČČPeVöqâRUĐyŸF0ķwĶŽY•“ ØōŒÏąÍāââ’Zī܌š™Ųsý Zā"H―ĩ~I •k­ôðíÓá”ņ„$ĶûĩDÍJ*#štđoß;Âėíî;E$óaŠ3cG‰Lkęą)Á[Įkf@DmØî"2Fr"ÚeFЧĒÓÞĖRĒ$―/҆ tnð Lĩ{weŽumÉÖuŠJå8SĐ$Q"îˆR „;Īö§’·n{8ïũQcūiuîsÞL9Ï]‘ ŠTe*U>C..ŪŽVRēÕ02Šjxėä4‡Žþ–ČyžY5’TmuZe[2§õÕ# p‡·ðĄį"+hkëaĮHä(uŸŠeDx†á! ’ˆéáKˆĩVDfĨššj?Rš)Tŧ@ëFpwäŠf;ĒŠÞĖŧKŠ*1^ŧ7ӟDø 8t„ŒŪPįÞūŽURáîé D$<ūžžÛÏž-âŧ+SÕÔ<ÜLŨą[įÓÅįŒ7D8äâââóËjĘ āAk™ĮšWe’ÚžsƒėXĀ+‹UB€dÎt,ÕZžĪ€’—Ûŧ–Ņ4Séļm˜kīŌÖOßŲą@úsŸŠņôę<Ą&Q…(™)î.RA™ŽYÉ7GÞ~vŸã‚ØÍČÛí&8w‰°Ÿ{§+Ÿ9—ōųysũxÍą‘Ĩ"2ÛŊuLK†éó#3XŽ„T™šû|ĩé þ<]íÅŕ՚ïéI*‘ûyf…ŠÕ(#DÍ’%kč)Ø2ģÕzTŠH˓čŌÏjUĄfϕ‡Žŧ,™ųÎ―c&z™yØēĩî=ė0J]—ŦÛŅ32ú'pwmg5#Ōr_s̉‰cú|Ó§5] Ŋũ{dP)"án4ŧÎÖޙ܅õˆíynLĸ™HFXËĻĻ*{k&ptÂ3žKuũûų ōļÝķŧĮdÍkš…ģ rqqņųeĩQđŽÕ‡þwĮ:Zgƒ Uņģ­r~žaŦĖđOŸcĒč~Ŋe’Ļ˧ É [VR™)‚uÜ2ëÜwŽÛ‹Ÿž ĄôîūĒj‹ZÆÄ­_f:―•9ý^íŽEÕDÄ;ļmų•Šū@­*Ļ* ūã BÄöķŸ&IWЎ;Gå (`Ķ4—­CŠ"\:CðþĶøLQ^§)­DĶģ8Ŧ2ƒaŠs€V'Ŧ­,%Ũįų#^WVŦĪ”L’ŠF›ÃŠRĩޜÁZˆ3Ë œÝkká­―ĸåxįūIeŲz=ïŠiWv)ĐJK’ŠKmû9:kËÂĢDēRĄōÞnïējƂđėžŸ‘aŠ$GŒĖð‰ŒS<ķé4O4üBÅë/_;Ėĩ‘!Ę"R™ u†‰K€ÓÆ UƒČžëę ŌÏđEZīɖoT–.î―QŌޙ™ Ė(D/VG™EĢĖø/äâââģsĩĘé|ōį:OÖą2Ģ2žëu‹hÃhķÔ"ā:D; Š))ëŽSUÍØdũö† 3zÐM`gxxŠŲ æúXWrïr―…ŧïģ]Šîí|Ēˎ酘ā WšŌTšēāBæ‰ÝŦûÜmŠßâŨöžm:ADfŽ?•é~Ŧššd.ĩî—ČĢģîoĘ1§UåũÓ}“ÚÕ$3Gú'_FIe|ĢfŊþâŋą„4―ú°·?ĻŋÓã@Uí‰ĸû QHųƒųmŸ ŋM}ō;ō{ýGŌō‘ø@~ýXč%øÍîD*€ĸ RŧøžėÕ;+ QíåTmÜÜLô“žßïGG"<ÂČį"TĄ3]VĨÔ*™ÜPUã”5sjf9§ĸk‘ZYÖ3Ž―—Šjku’˜þŲ‰SõųŲûpþgĶŠæ 3ˆŠ4SŠžûŒLR –<é|`g`ōYąj§ŸtūãļæsïĖ(É{ÛR}ÓåĖ&&Ūų‰iuÂq‘1ïvƚEP%ŋ'€ßïĸÍÝwGUuuߞvÏE&E,((ˆ&vƒ5vP,DcŒÆ–Ø l `Ĩ(BTė1ą$ˆ]P `Ã(vÁ† TA:—ÛNî―úý~/‰c|cžžü‘2Î9{Ÿ}ï{Ūđ֚ŧŠĒZÆåŧœķķNcĀĨžŠĶŠÚ(Ęĸ)ÁK/î\šų „fčĸô0Ã`mÂ4#Dã­ßR”šü/Jģjēī ũΝ;YO^ƒï":äûŨɜūēēžĪļļļ°°ĻŽZú“ĩ}ĸŪũŅÖVŨs(Ðų›ĶýœNS^ óĘS­ýŦ]EyzõâõÜjúï,ķ˃ļŪēJÏËĸčÝHšī5îÝ―wĸIEņÏ&ËýÏüģ9]mMM=–kŦ+5z/ƒXWUÃq\eM-')ö=ĢeíõËŋTkÐĢĸõuĩ)ƒĀĐČŽ)Š‚ĀCé(H“`ā4Âøˆ‰S^îąEKŲØu-ĸ=hÉQë”ĖĻĖay(í‚ķZ‰eLž€ģm8&É" Ãáíš_i<‰cs™"HŪÁ’‘oÜjž#PN― °·7†E°h ÅÃO ÎQƒbÜī€Ke}ŅË6|cUļ;Cĩeüp Ö5b Â5ė7†ũd`ú˜`M3Õ/Ŋ^ôļFQŦļ”č°ow%ō„š ÃWn\nxP(RH;ķygt.jýø/œĸÁë5ĨåÕēō—ÕW—•WëÐÖĖÕÆo\{äđ†aþš{RŠ.5*xęÔÉ~ŦƒūųrÚúŸS*’R„Ōē7Z>,|ĢÖ<ûmųĒŊü6„n ^ķ;A‡vÃĸąãðč.@É–Þýɝaą Õjæo]ŊV=IÜ=ĮgaČÆð ëWýxO&Ļĸ+úiۃwÞ*TŅÔ_!iÐT––ŨĄn!Ĩ<ęÛŗ_Ö3ų·‹‘ę^lY3oŌže ĶzšĻžïČčČkKË*yé$o–.ūujYØþ#­&ëvŊ_ēöð ’a•Š!~û‹Kēü}wdäëúŸ‡$­RjĒ6­ŧþī„Ēé=Ôb2Gƒƒ+ŪþpėÆą6‚'ÄāĀ/Hôƒ †^hÄ.ČĻž!ŽĸÐ:ičt€l•$‚ÁĒQ02‚Ë-ī°j5‰ï‘Tā…or x"Ō.1Ībû\â WÂR€?Øð.ōœÓ`ÔݘCÛ04g—"ER˜ų*ļYbpŲĖ2„`â­tč]lČŅÂbôĻfwã Ā苀9 dĸPK7[;J,vĸQđŽŊūuõôŊĮnÕJDåãŦ[s3Š"pĩ2T! • Ą8ŧ‡EIÆQØÝ }Žô; ‚yNÂ6@ Ę$‚] Sp‹‡ŒžųžÁqÉđcówžĐÐZâd&zūŽ ‡ŋÛTôĨuA~IOhīքđģÚVÅĘQSSî™|,lƞs™ĪĄ0ÔŨ'áö+❄BČÆēgY†‰ –úųÏó2ĢQšwūˆ ĄË ųÖ'ņî+…ĒāDÍO \dQ=ĘH$Žu4. ú&ė ņӆwĩ{+NÕ 8t#ÖG%īæï<Ĩ/Éæ{/]áúÍČî$!փy1f0’ āąð™fNŒģš&þ,äQðg') ‰ CēvE.8lDáĪõį‹öle*áĘwO‹$ßÞņë ûȆO„y‘šíh"y*)%9åĜQEÆũJ‚ˆ',[{óĘĒЃÏęá ðį ęã‘dqėT-Û:ä\KZĻå+ ŊĪ%ýrōŪ‘!_ĶđFŌ-Ûϛýy{[ĩh"IĘ{ïMO@ÎŽŊðOk„ũ‹Ŧ2nœĸå škÃĄ,ĐIÜ:Äsä„đþ· 4&jâܖ°€Ĩ‹ŋé7ådRFėڕëV.Ÿ6xPŨQ R2ŠY–ļrĀß?líĪņÎÝÉÍĖLË(ŊeHîôķÕ#=―žÆŒ―–ŊSÓōí Ķõė=ýĀ•äûī‹ĪÍmÚļvčāæöQ[KíÎĀđĐŲo_ŲĪâų%ĸðđEđ›—Ėō9Žß4ŋôBā…(ĄÉZĩxۋZIĨ’ÛđhÏÕĩJč9rDŸŅ3bn—*u…?]Lžī'rņĖos*k2.œ~^ÎĐ(éNŌĄéãôøÔ'lßk#Đ"ßėûÖwcdÐôÏšOðŋóÚØKĻÔV’(—”;ī4ļ‡ŦŠĄsœ_âí5ÂÃký/Ũ9ÞxéōŅ”ĢąkūüęÂýĮÛŨlMÎ(cÕęĖļĩĄG.RjuÖņˆ€ukW.ýęÓ^Vï9Å).­ÚõpĩÎÏ~ųōuþmÂį>,1ðWóF ųÄÔP~ææJY*ļv|ŅXï9‹˟æ―0,ķF/Ĩ ó5rÐĀþá1Đ ËęJîm˜9?hęäé“bycMĖú)C‡ ŸüÕ͜ ü|þ ĸ6ÞÐ) |“ah„88CÅŠÔ þGÅ0Ņ‹q…j@g ĐČý ü[eųÓTX€ĄŦ†^p˜PiœÅB­^ŽP―a0.Þ8šŲÞ~w1€âį`ŋ.QKF”w †þB0†T•(‰0:Þ3nÄ}$_` …zXĀ\IaŠpx„Z­Æ27Ņ4ũ*  /Iņ°Wá—B`ýÁŠ!ĄēîãÖņynÎÝG9bŧņcÜđė‡ï—iÜ>î^ø{ī_ÄïAŅg|G7ÝYC˜Ō˜• Õũ}&ŊĪšL=qúTDČX“ŠŒ…K̜ĶG=Ũ$?aéÆDĒ‹2ŌRïW~þãāŌsūOôŲû•C~ԉ{BíĢoūũÜ{$x끰-ÏŠŒDæöýŧŧũ5.0*ĒĢ~ÃŽ…yęq'ϞšåôzÉŽāBŽbHB‘„&>ëâkúöD#I>đqŪķĪäĮûŸh Ĩ:^†7€Äĩm§ū―ûũwïlai3ꋱYũ’Ó=Lŋð|ʔ ķīöQōÖÉK6\ĻЙŦ˜w[)"&Ķ JßŌjšÆŲgēîųņŊgÏ?˜UĻ6a)’mÞūĐ­c —6­ĖX$‰0j:+õ éä1ÛŦŧ­ó'Ë'ô{yņ†ž`$+ÛeĄƒšusíÐTE5Œ ‹Œm§―?8vxĮãŸ7L›™“Yņ2ĸŌÁðY~Ę^1:hŲĘîíšĩmÛÆŌBMRf›ĒVzüj‚Qé7Å?|BũÏ&ũ°mþ0ŊŠÂžVTÔĮļ?OH:ĸĻā#ÏESŧ1ßÚ+8ĩíėŌ’PĀžŸ4ģvpģ7Ž ßS?vÛ^ŋ‰VĶ_ð|ÜiÛOĨË$#ˆˆ*Ųvs_0ĸŨĶO ûLûĐ[Ëã&tíîÄ98,•ü7C­‚å‹#$ U铠ßIÁŊŽ@û)’\IÁvˆöEÄm]HDýTø(“‚ S īœ†Â)œ"9ƒa1ÃHpá.Tę€]eqÍŋL˜VC―Ū„ļDCc™BBC(ĻM ē_ēy,f‚jĩ†ÆNސĀĒ@@BēCĩĀč‰hD°ƒ@Ú‹h8žš p‚(A?…ÔxdØã€§$.Æ%_ CÐ *–€mė5QķqíicļæÚpŊŪ čs36>ŊÜķG;3’—l]"ũHL?īÁJ1ˆx­XsKšēöŲ‹2‚bDE2kîH”—–Õ"ƒĘĒÂWž…… Ą ]S0„ĀˊQŅfc‰ZĸŽm[ŅfíæŽÞüó‘ÃũïïėÞ\-5`œĪðåe:lŋƚęßFŽ5$E֕=ãĶĶ,ŪÍĀ Ėq\Eđ šÁī)ö ė$§Ē)Ǝĩ™žl[LÜé”ĪCݛ)‚„ErŌzðpŧ›ŅG2ë$ĩč ũŸËiÖĐŦ%Kqb‰Q6ĸņM–)ĶéŊq“&îælĨČA™ŦÐðžĻ:[|ýö.‚ -ŽŲŪ―C#ũ&Üšû{ô2sBŅā8į=đJĢ#Ē"7Ÿ1élŨ„B*ĐPhžŦšNÛļïüŅ0õŲ Ēï8:ŧКšú„l9{įÖÖN–tĢ )órģÎ=Įû|œĮ@SĘĨ‡GWŪ8xđß#ŦAž―mO„OûųR뛃―\-u˜Ð4hτÞxŋĒ^ŠÏp#―ŠĐÚėņé€ĀMWnÜ:ģ_{Ž–h$ßqåeZ™&H˜“ĪØ·s‹ ĘĩEČEE’•%CČH&‚@:2û~Đ@―FŦ4í=böū_UæĨkYÖĖiėž ƒ1‡ï=<čÝÓAŒ•oę9L>ģŽķFŌVÞļ^,ę4Qš kjī2Ačjjt| +5 Œ$Ęm{{(y‰IéOÜ{ŧuîóqÚOņö-Úĩ·§ąŪP#iōýf.xj>:5jąšŪz~Ų7ð‡AþßÎß"[.^— R_PRŠ%ˆÚ‚gee•臯i KʍĒō/PŦ,Bó>rVÁ5^8iŽ\g)‚”1ÎŌ8Ė{Y(„–StŠ"ĀZš€wâŠDĖgqQ2ú›A°Ę—CŸ=WX\å—ä9LŦšĀgí€=ú^!ˆDˆ)cÅá0>öã/ƒU\ãÕ #“$ŽKŪ2Õp ‰ É$LÔ! Āņ dېŽ é/(„’ —Ģápd™†ž`Üh‹É;Ę 0šJÓaRÞX—†&ÝÐû‹„…ãČvM,Ļ6Ÿī7ģwëŲL6kŅá KŠč6pĘ8ãŪïvÅÄFĮIÐ*,!ŌŨÄąĸâŪŽ{}ūÞūų(Ņ~ðčŌڀĀČÍŦƒŋđtē5!―§ķô`Æ&ÄŲļ|:ÎŧytÄö#qąûöƗč @F›ē­ģKqB\øķC”ó’1CŲžj㖈ĄņÃ<wą§hâČĪu'—ËßmÜ{2Ų 0‚Q‡ļ*AˆžĪŨó6ŪCuwŠßw4úČá|-Ua’@x.ÜÜÁ"{îóķïÜđtî܌7íĒ–O7·oÕřM<ī=:îÅÐ DÐĪķúJxŠÕAë~|­W8ƒÄãQ$ĢAäĩÅÛŧ˜„CÛbOäx-ņékþ bĮū̇î8þ;2·7$AbúāÕ~ĄAQ)žÓgÚŅĒŅ 7p"0kQ yI‘ĩ9ŧüÖÅ'ßĻĨŽ;ؚ9ļOíiącÔ/ņŅ?î;QÆËÔŧܑ˜}!Ú?pM@€ß†įõMg nsëęÕAsfX+rSkĮŠWwbbâSŠđS§~Í­IÁ`äyš}'{r[pĀߐÛE„Ī˜Y:ƚü„č# yڋɉ ÏkœšuÉ9z0â‡øjž$zįûLÔ―bÅēˆāe[S Ÿ-ýÜDâD―ōO‚QßXȰėãĪ―+—/Üš5dwēŦûĀ.=ŋ˜ÚíÂþÝŅ1GöíŽÎ.ŪģjÛ^w7-býæ\­yŊŽŋîݰtÅųĮO%‰SSú^Ėþ5›ý|VļôŸÔߙ—$øfŠ>áČXąíÚŲ3-;ôh"[uė0 )ķü3šŪÝąãü͎MËnZŋ6ōŨJÎÔʔŋr6îĖoéųU9—Ū?âR6131ðéĻŽ‹Į|WŪZę·'#ŊŽ51­}œøåė€Ė7CĸŋNm退€ĸýY–MMIÉÍÉibŌD–%ð§’áėXT†‚ÉDIÅ"΃(72ļI ]A•Ūà _V”Æ<8âcà l(Î ļЌn| ކÄėŦŦĮcĮ,8ü―lîÎbT @?ƒÛ0jƒá" üÆ$I‚iãzW…"°d,Ęļċœ€"#„§ĶāēZÍbAƒ ąPit9ĖûįbĀ…r]Žx‘H‚į,+îĮedŒėČŊG!€éĢ9Ò*ÄĮŧ :LāųÆÔ_MM­JÅXýÞŪĨĮm* ĮÛą}ýŠ8qĮo;~ÄŊt&nŌÎLœĮ jU™ŽŠ‚ú€ŌŌ"ŠÄïaƒøėø ,ŲtƒT$Ė Ô*S&ƒÛ6l`Ãę^ÝÕųtį|ŸŪÎĮēĸÂ3Ū@ƒhâíų;=.ƒÆdksvuÃĘ('m―đ %φcŠē(ŧiĪ Un°Øõ5ęŨĶZ~·kÎÆ9ŸBôâîĢÆN‘"57ōš&@+Še}'ŅšægëQŦ&NóĐB•–§(w}OÁačœƒšŊwQtwļ›'âÚģãĩpįîĢ{SP°Éŋbmđ#ŨÅĶĨuĢĀŌ ī Ë"ĐŲČöæNY—EÅðl/jßsšEKŨŊLTâôįßNžĸøôû§Oūýé XoíÖpÅēŠŊŽéV0)$ƒą"pž 4›’íũ’0ðÍŽ–%à cŸeđ4―Ā$AI―lc’įČïË2Î*ŽkKuŲðÂ(āh†~ųú‹ŊŌk Ģûožĸ`ąQz^ BŨæÉBԂEĩį92KâĨËį+tppûÞˇ~g6‘ˆÕr…HŠëuåĘڟĀ)É tåŠŊâ2LUč„Ņö|įÚåÎäÞeÏ ËœsĸÎAÜ$%Ű―0pLž­oRAû{7?|oß7U+ļØOåģ2óöÍ[W‚US’dĶ-Ē‚–m)€nÎÆÛ•“gÖžņāãwJĄųAâÚ:Ŋ zŊ@Áôž4Įļtú2mëûŸ<ūÎcXv)ÛTņkļŅ2=OŦ5ÔäĒÃbgm;ž 7Ú ÕHFÞƞXŊ>ųæËïäŅëą”ėĢOï›ėßD§g8į Ģ*L{:/y/ũaýĀw”ĪÍÉkÝvfŦž ûik3„OÎŌéþ^ÞĮčšĶšašv:ÍB äÕ`;lã%d4ŋqįÖUWĶęšū§Køzj‹‹ttTL†þOýĄÃÃÃŪ>üŲėËŊfŨQöëēÖÚïéôÝÛíîļ;íîī› ‚ĘÐđ‘Ā &H BĘĀ2% !ĪOƟð ”Q@ČܔQ)BKøŌNŒÝww§/ĮįÝk­ŠbŧjŸ#hŲ9qö#ŦÛįž7GĨzŦžú=ûŨÏæœÍýŪėAđĖB"uę—Įƒ‹ĐFömo-.w™Ø}]FČs°bo9šzā•Q{mQˆÁ™ķŲ·q’ĀLÖŧ qvö•ø‘;žûÔbƍ'FpX"OBgßø5ål]ŧvæ„f]Ä}Þh]§?ēųšËZoáÐBb0ŋāHb 4í‚ifæz1Įˆ&óČ& „“ßVmķ𡚚{9Ē]ũqŊ—'ïßĮqýЧžú―/~éî]·ĩûpãå—_ŲØX>}ę]†“s^e–qÝ iŠķÚDý^ —Ė0Ét+įÂÐĮĶHДķœq]ŌP’ĸŒÕ?“aemėjÓĸ@Đĩ+į!C_7AâRēĸŪÕqTƒYHŦĄ€Y­Ģ•ĄP|oküL‰‡’MĨķžōā/ĮRdŽým/ ęļŽ_ QJ99Hs}į;ĸøŊ}íï?ú+ŋõɟ>í#Ÿ6ŽōöĮ—ŋ– ũÖşÝ‡åŸ ™ķą Ĩ\Ē#6Ymþ.ýæSŋņ‹ð§O=~ßÎŧHeEZŦ·„)ÔÖ‡UÁ·―Óú|ÖĢÂ,gģd\·øØÛ8ŠYžlŊÉF­6ČK­âŸ<ĢCCdlŠÃ8u_ZmŠþÉëX[*ƒķQԈSÉ)šËÚš–Õ uôÏaEþČoaąFî­v1Āĸ|ÖüßzFÄ6ŪJÉ5@āôõßþüúÄß<óˆPþ°Áv•°ŊĮČŦ!7€4nĩ#įœfi­RI„ĶbČ ―yžKĮ@ÅË0ÓøĒŽĀCaĸ?áĸ\8ų‹/ūdf?|ēÖöc(ĩ_øÝßųŦŋü‹}ûömũģ·Š(ŌsJĖĐI4ðâÛÅ?” šŠ ―æúŲ€ųŌ,đjŦ͆ņEÞÜc€€2ŨY€Øáø—'™FgÍD jbš}L :óįf#Ï‹ ‡")‘>;īrîãh”&‘3ŋĪ”âÜGA·ĀėvāĘ5åœc&ĀHˁëU€™DĢb)â ã`ÆØÝá$#$u@eŪ­Æ0š”|ûöO}ú3_üŌūÛRŧČ’áoUģÝzXŋúōkNž>ēb3XtÏ.ïĩ Ũõ‡NÃ=‘ũ#—Ú{ÏjĢr€Í'Š1Ÿ$Ÿ‚Į͂Ŋģc­õ+!asžwė–ÖuŒč5é*)OJ―6é_ØcÄîŊcģ+óĐ#$Ÿ! `ʙ)™ßđúwsŠĩĩÞĢŽ›úS8Cb5ióm›Šö.]8'é\DuwsYx$fDŊúÔVDjoėãÞŲ(†6soÙëïr̈́AĖQg*‘ž+*a<0ÅļHœ<˜8͉ŋōÞÎÁ™…CÍvņ9`uúėŲĢK}'gũŅ|čÜĐũĸˆuv!{adßĶ\kCŸÃķ*mĮĻÔzÏ9—\š/ļĸ#F w_~D@“Ėį”4ÉÃÄÔ{XrDˆõÖÅÃcŠŽfá™Ĩūĩ%*ŒÄĀcm€ĀœĪ;a ,qĮŅĀØ+]ā JÎ5°/)ƒZ—ÖĪÅj―ĩ…>ö-Ĩtß\ĨėßF›gî"fâ§ÐlÜŲ’•Ý%V―ģž†âm{§ė‡d=&Ũî‘h&ƅ}oŨK.āþ35āp›ųģ$Ēd°č›Lz‡wŌ"ýïΐ.ĨVÜîڐ€Ü0·Ē&<ýÐÓeb>ë·ânjÐbdæc:ú,Ĩ4ŽkĘŦ|0âdâî@M Ņķãã8B]ĀehØcŊS8Oį!iSžÓųXĀOla―ĩūqýŠvą™tŪ":Į#ø@Ã4a3&Ŋė―ĩ–rA@™ŧøŪāϘœcān° :Č|–6ķŠŦÕJMÄ{eōŧqĀb…ˆ ô―§E‹–R›ž–õĶ9sðeâ5ĶchH‡ ).fëĘ>ēŽ"ū(ó=˜t WT“8č―ÅĩęÁƒ?ų駞xâ'§Ą°ŠÜ+}ïõ·.ó†;ĸö?–RzéĨ—þߟüņĨ‹ŊĨ’ƒoāĢ€Č k€˜‡ÁgĩŠˆ…KlSJņš―ĢώÄíÄTVEÝ;ƒäŅd­É°*`6nEāBũ1hō|ØžEÚXĖ=ČÏØ`/jŅĒĨÔ:į;ØWĒ‚óČUmR„ÎļĮ€L™Z­4§|ũč4}Š%•˜y^Fųî^ŧ"Ā Î}øüī‚ûö·Ÿ{ōÉ'k­ŧ=ŋßØØ˜Õŋúgĸ_Zo―3Qö֌—ev7đõ‹#-ĶĻâFũruUɉ)%™ÝČ9Čė­:Ļ윘"ųŅ1ū)·ÞˆT-.)ōÏ0 öž-ZJ­QD!Ļ€ÍuĘ·RĘ.Sí=\Ē(ŌÉŋ3xü—pŦÓŠØcukՑZ ė%W˜úuïųÐã?þĖ3ϜáMžAŸ•*û~ÉŋÏôÂC‡čøOœ9“sþĘWūōå/ųĉŧímšžôģŸûõkWŊ}ïÂËč~^BĘ95Į)$žŋÝĮ‰SĶ ƒĻ ŧzõ#ą”IĒųe ĀyÔ\ó|ó@JƐÁm ļQÁũ*aBpčĖÔĒEËZĖæ6Ęâ6áŦŨ&ö/ĀÝK’ãgŨMDzž‚ĶqîŜÃ^ĘfōŽˆũ{ĸ4ĨE€S§N=ûėģO?ýôŅĢGÍ vAf&ŪóįÏïßŋĸÐáC;Ų3iöĖB‡w0äˆÔ[°aĩr;}åļ>5 æđó%AÄ6{$8>.œÏŨe)Íat‚ŸüC)Cð―Ā`/jŅĒĨÔzųÔÍŽË‹Ec ó;ŦąV"Ž^UæÁE?ëåĩĻjxøã”VEį#ą!HĮ:qęôéÖÚącĮVŦÕå˗/]šŧ# PÃz―~óÎí+—^ƒÝéy[k‘5Éä\GT"G ßë"XŸĐ4Ў5ōēé:Ū—}°ÛØåÍŊîä˜ĐÏē lŽ• =C ŧt$HœĢÜ{öe‚―ĻE‹–ŪV$§,](ÄÔý Š‰·ųؘ8ü°’ōÜ*{ K|7Ä9=Ũœöârģ*™Ų4Ĩ―}ûö­žĨ@Âî(z˘Õ9räÚå‹j“4į2įÓpšÔÆjĶCÄ9ßCĩq;Å ]ĶĶǜH&ÅéWɓŋu*yįÏ?6ÕŲW^yåÕW_ 'Ųî•ÚH!›č-ãzkóî’fũĻ5ŋcŸĻZ†ØþđÔTŧ°Ÿņ!!8đâ‹Uaë‘^žÍŽŽc y}V`%$ßÜBtč(g‡YX2šĖ ÁTÜĢZīh Ļ‚:ö°G”KaFwCiĪ―ķÖݧ„―u•ÎŽķ )SŠ Æ·cŅîvĐä ‰pôØąã':õðĐëŨŊOĨv"‚ÝŅNøá#‡Ŋ^đ,―—2ī^ÁsÎëõˆÛ€Zŋ4K€A#“IðŠĄmėyßjœ1ÖqtX3Ēīˆqų1T4“Ž„œ8qŠĩ"`.YšLBâD$~X {S‹-ĨÖĖˆ‰―I5óÛÖljMü(ÖÔÁƒa `% H8ŽÁrā E#AÜÄü;x>{öÜĄƒ‡Ũëõ /ž0ý9đkwĩĨ‘1­mmn"b+Æ}p&gŲPōķTTČj`æ„I\‘˜)aņ( ƒaéŽw"LÄ4ŪG`ČĐD˜˜‰ņÄUœPS{%ĶIē ĻEښŊØTķũīhŅrÂ`†LÜ"{†€™ŦŒ*VRvP‹j΃ˆöqnRÁo€ ĩ† ąa· ,:\†r)gÏ}x:ŧråōtÁĩZ­ˆhWĩĖ\˜ƒ-+^î‘ØĢÂgÛ!īÖÅī872°ŨŽŒ9ρļæģ°˜x#eK™ŊË</{R8’‘LĪ‹ĸÅŅycĖYį,Ȃ`Î?Û{Zīh9aH‰ŧHö/NaáĒÂ<_ž:d ÐÝDĀ)ĩÞÃąfuŽ)% DÃVŦŧJđõFˆę&ŠÕjcēөʝ;w.^žq^°; ‹U˜ ØŋywÓLËj0@éĒæuv6Tā*•؉1s„3š PByFx`ís"ūõæA“ŲcØ ‚ŲŠ'K2!Q_”åD>Ä(Ųo‚kĶD„"Ė`jŅĒe€ÐýnTM}r2 ƒē(q >†—Ö­ēēR­ cbÉÉCiˆŲG™§ųātķĩĩuëÖ­iVįŧØÕš8ðÆõëVJ1gƒsÁ@{s˜Ylĸü^Ö ÕFnČBÂZGïЍÜÍŧ‚Å4w58í!ŪāÛ8‚§ėN8AĒ·*ŧ˜ôæŨ n}K”(që•Ðy7{Q‹-„IL‰“rxq†·ÆÝjJ™4C™ "Fh":ķÍæZf€ÞÁŅ8Ž„4y:$"Ï?ĸ|$„ĸŨG 0éų@ @øęl c'Ōöwŋóž[ēĀq—Iž(9gąÚōayZā €0Ē}‚ey”j°ąZ‰jï’sĀ―ZÐlA­5q›ä8ĶÂ\[4īÞ(ˆ6{O‹-Ĩ6j„ˆxĖƅ^ƒM'‡Ųã/iU˜’kîä qęĮŽįXú8tðÁįœĮq|îđįþģũĀĖĻ‚―žFŌn+Ûx€@ė`›)Ý`ŧÃÖÁ‹•ĸIpŊĘÛZWb·ķĪũœ‹HI]­ŨāÅtðĖŲ-ŒŽŒ€fþ )aĮ⠙ÛXÉÁ\âP§Ė˜Ûf#}R|†íütĢ9ĨžšÏA“ã‰ØK6Ę2ŦÝĢZī8zí%gÎ [Ŧ„˜†Ük7PG_Į*†iÔG–p!qX-įģÁ·ã:įDhïŋĸÁĮŸxĒĩöýï:aˆÛ_py]|aã7V7nōú}g˙Žø‹§[ó›Ôî°nß!ūÅx›é nēÞ$kÛƒBÂQĮŸ7Ū'Î]#€™ĻŽ•ˆsI† Ú1Šoëäo<Ī:ŽfūÜģ8ý2ĒGkrv—ų›EöXöˆ*ņĶ7Zp~ {ßŠŪœǧK8~7厛šø―öĻ-ĨVÔ;8N`Ð{‹*ę™4–óĖaa"ÃĻ­s9Ļib‰H­-%Ķ”}Ȁ‘áxĸ<ōČ#Ó ö… ;Ósā…ýWū·ĸ’Đ>pnßđðôZÐĩũ°lvīÃ1P4ôļqëofú·ūžá6a#ėĪĢÁĶQĢÞäū‡ïŧvéĒF„…"gį\Ķ8ýŌI ‰ÂĸkM‰œĢŠÞšĶGnf Z8ÆĖO<ī+˜ãĨ·Ö•―T;\Jœ)ũÄ ‹-ڃĨ6r -\ĩ^"8–ŲÓąĀĸ);Ë5zÃÞšÅÏäŌję,m‹‰b]=öXPŪ^―ęn§EÖĖ@ôBÞ|eߕdæ|ė@T—Ë@æūlÎ ‡ūާŦĪøvĒŧ naš&ý&Ūî$É[빉QÁšö”Ý'į|0cæ:VĻRũė/&/Ūó3ŸýØÃgĀzõ͗ŋņÆŨŸ―úįWęĩŒBfØD3C5)číåЌúLjK`N@ģ "ŒžÕ%Ž­22"xÚãˆÄŒÖ@`>Hæ `­ š›$ōÏ'a@~â“TÔĖq6‹ŲkojŅŌÕ*ŧnmhĘŲŊMÕJY™Aëü>ÔČĘĐĩfjĨ””ØīŦ)1{Ž‹Ļé‘ĢGxāÁØĸžøâ‹á=øá1Ĩ―Ãkežvyü§ŋŧõ…_øýŊ~ęégï=ĀĢŠķöņ―O›IOHÐB/‰T‘Š("ŠQT,ČU6ŧ é Šb‚Ø+―ũÞB‡Þ&sĘÞûŋŨÚaū\ÂåúýŪųîýþߎįŲģgŸÉ™ã3ïŽóîw―KálåĻŲøÖzw?ÕôåÚūWļ*e„n^›kokõN›ÏßmóAŦčt‡;gŅķš*2FÁ$ÅmÐW>‘“r€ŧžā ( ØZ!Ôâðō3-Ų5Š‹ņ(ÞU– ŠüWUë °,ð§as,áĮĸ9ĻŠÜ‹`grĖePˆ hÂU–§Ō:―ËŅq%P.%ØîÁ…nĀŧÄ'$5kÞ\VˆIíl&ÚŦLŨžw#LØ6ßēėô•íÞÓíFŋŪ‘3QTĖ.+=mÛþÁī'bõĒ2MOäï+žĩÉÅõ#ë6Šj‘h%qÁÏb8 ĩ\ĸUËĀåų[ūðÁ‰ð ĩ…„ \°ëUûcĘÄK Íą]U˜ evgÐTý1\9Ó0!Ÿå ?°ãęŪS &†šō$áøOˆpV˘ %ĸ`.C<·Ē6LpáánŧúŪ*ų”ŧr…įPÁ$“vuD[H­Zĩš4i"'U[\XŽŌŋ3g`ôĻmĸĶ‚:ŽþôëˆŌÐüŪ’ÍũnųÛUï\ĸø‚ņÏQ“zôMîWÅžī°ØqŅÍķ&į•+Ē„ðģĻ1@AęgĄ[š ķ;:ō†åũđŪ§œm1y'Wˆŋ”õé˜`ŠŠŽSÂ+r[č―n(ū”›mõõSöøņãëŨŊ—āŋýöÛ5kÖ;vLýfœĩVÓ+BĢôÏžĩ͘ĸĨGj!Ļå·4Mĩ{a*ĨļÃĶ*Pė„ķÐkK†ÚãrS]Č 1tTæ Á[ĩn­ëšėÞ(KÅ@)u&*ƒ‚ŒsŌr6'ĀŽî ^ÔūWƒ„ÚĄųB7ĸ­ŽĖlūÝß,wâ/SÞ^þ;ƒY}SÔņ' í<“’R;)U̓mŽðÎ\JFŅ·ÖC……ÍĒ'åÜsŽS°ƒŽŠÜ‚@YĶúꗆ`Ens1U”"0Uė 8S6ęīÂÂÝ Ęļ‰ęIfƒÁ↠öėŲ#5v]štéÚĩk͚5eį ‰žRįšĖÎ;yėÐĄCĮNä9\þ‡nčûķ―t‡.sû"aĻE@ļ;Ų–a*9Sm Qąï üū!$ËīTųŋʕ  ĖuÏčO‰ãâÓÓ3äw[)―T=ŪV)Ļ íÜ~ˆršðīcjQ͚īŊ<ŋ­xÃÆâMQzTtbt­ôÏþōþÉēB‚Q˗rĄŅįĀÞō+jöþ[·Æ$Đų83.Έ€`ĸ•ÏĒ0ME"-Ā‚!Yđ­ōYՒ]Óa%*b=ÕĐAĀAhœŦ|xlČý!āC)ķŨē čŽS’_[F2Áȇęé@ąkŨ.MÓzô葞žžˆŅžysųÔįóIģ`ĮqÎīm7'ķ―úØĢžéÁĄ7?ôōŧ‡Š=CĢĸj͜•‹_ųøû2ÎlĸŸ#aĻ…­å]-ģ˜Į\ĮA6@sd`D a S8Ќ$N!f %°óƒ!įk&'KĨ—˰dsÜüü|ųÚY8Ŧ)<œ{CĖ9eŨŅĢ7ŧĻōüĘÜ_"õH:3Z#ŅGōef‡înĢöŽŅ&_ýúK7ŒŠ2,5{―á“3fŋžþvãč–!‹E!*îî-Ky0Uģ tēŠÂ]Í*š“{žŦŠw•|Bŋ빀˜8ėŨ€ H^ķ x+ <―PIFuZ­:ĻY'"Ø.žðÂģšdFDDtėØŅē,I‡VóԜžrũ–ûŸyōū^[į>ŧhåaŪŅ tŦ°zķ!ÁBĀČBX€Î$Qo\Ã4Î‡mRA@'Ũێ'—âz œäŒƒō8•eSÁämËÉYä20Äąm—ÝÐ\Įv\ļ°$ôķgNCĀRM07hۂTLđ6œę`6<;t\õŋUÍļA9Ã*œ“ðï Ïq=FÂņo‹0ÔŠ&…2Ô[ā?‹ô+vļ1ĀøÕóT!™rb˜ļÉāï%ˆRŠ]xų=OKk,Į’%\·n"2"ą [*@ünC;ύąĒWb'í:~S#ąwËrË B ââkéūĻHÄPÔ°’åæXëØvņf ĩ?F•ÛįĘĻ›1eĻĻ, 0‹D~ÕĐFUđB$0'ŠdYí8qŽV0 SumPė6ÃXßĖ<+]äĞNuŪįʧÕáWË“}Û6lM%Š„ĶiŌĘ2;;;äž.­Ņú‚f=úl_Ŋ–(\0yôå}ŊđīGŋ7ŋÜ ĄôțĢï―ü’ÞWõ―úĮýÅš \3}Fųũӟųځ`ɒYOô―ęęK/ŧlęW+uŋ/X•ųð]}Ūė{įýũ ūë‰ŲŽ[rdúSũö―æŠkŋåī{&kĶÂÉĸøųŋðʀ~—ũé3ėÓÏ>šĐßÝzt[°ėˆezŸd>‰§qý;‹öX>ßū_ÞđolæčáwõčÚþĄĐŸ9Ķ?{ËŨũđēoï·Ž|ųxđ04oåŨ3ûuî9ðūûđí…Öø|dõ7ïÜÞŧOŊkMĸa‡áóïZœųøÓ̆ ―~ڂĨÄį 'æáø·éjŅ_Æ2–ĸãeüDđĻČI•bØ`° :QA(˜„šĶAŠí#MŪ•"C)§Ué•Ā`jų#'–0ŠÚ j”ya™W+­Uåy›]5l*'Ōü5Éūž}HE4NLn\/þ―-ßuNëØ5Ĩąš<Čũģb9ĶeyȧRū @‚({Y—žš&Ũ‚ eķxn›6ŠŠ͋/8ÆŌUoÜÐ39~LŽ_@Fķ*ũ€üÕÁ9—æäädĮqäļ*˞CĨĨĨj?Ūģe:§-þhæĒõ?Ŋ>ŌdPÛ†É nX|ßËë§ö|&sáЁŋžöDæRgÎû Z'pģfÔéMÂ4ƒ?Ï}îõé‡_^ô|Œá4íqÛgwžšō݇'Ï{{ØM—üöþË37ûüjŅņ2oûɂŌ#Ÿŋ2gGÔ‹?›vß-/MMĸōĩ4hãõg;Ũ.^yÛû“Þüîņ?˜ûÁÔ-Ÿ<üáܟôÚĄŨ ÅeۚØãĨĖEƒožĀËÏú|æWOM™ųö­]zfĘĄŧĨÔhøÐÄŊ›Ð-7ôŋ{ÅÁ‘u▍|æý›'ž{w†=üķ!?n8ųÜ?>4vÎÃģŋh}pÚ —&^wŲûvöÞÏū^ŨĸŲ7oŋē1u8Âņoë˜k`é?ð•*kĻÛŨMÓu<9äŧķ€A7”ü ÔKa"Ž# É9l3c;JēOg)3ĘPëšŪ„åĐ(\V`–ō~‡@5ÂmÎmÖĶnģĘóģ]~Æc‘ƒC Ņĩ=ųŲĄ-k$·đ0æąš―įČŪFŦÉŽÍ\•ŋ"֊/ôNųuEVKˆãšBã&tÏÕTE‚ĶÃ'WÍu9p=.\Unā:0Ā: ē{ƒ‚Øķ=ēÜ ƒpJ€“e2<ĶВeŪtĩÄũ/Gí+––AoûWRð+ŠVˆPīŠĻŒÂÕ ËKv:ÔšUŸ·îŋŠKãčï?YþäËShņqĶåŲĒ|Åoû/č?åŠķiŪᙆ‘ãÛ―`ę―Aúāī•—ĨĮ9å—ū<öÅr­€jMí@ášß6]ríŒujÖŋļWį†?ˋö.ĸ>wwōïĘ9‘Uė(ĒAY'6öĀØËÚÅėéÔĐŨĨ_Ųó"gWŊ_W”ęD;ēoų˜ņÓDŅIŪâ‘ēķ7Œ=ôŠō“ņ-Ž/&4nņİc5#N5ĄÓŋ~ã%tūģO‡ä:Ļkę ÏØĩōŧÂã'Þ~ęŸKKOÔ.qĻôÚx`âm― ÏF?åp„ãßeLjG‚[ ð ŊƒĢ:ÉÐ•Þ pËóYTŒrĨ:ĘPČ‹ ’’ā6CGí‚Ē2a&Ôũüly$–Äåqĩ*Ï{ÂÅ­-:éäx 8īĀŊGúM?,ǐ•ŠüÓÞáB~ĘĪ&Ô#T ŠrXzFĒÞ ;§ 萯}9ÅäW1ķ°˜PHK…ðų}āĒ`{Ā Z:sTúâ"U a˜Ð—ƒ'8…Ðω01- `XnQōŸÏ'%ĖR{Ā9ŊúŠŽŪÜÞsœÄšMoņâ%õLAčŅÕï<1iþ}GŋxüÉoL‹F$Ũôĸ°wcąŨ3†ēr‡z·EŊþŨ‰õ‹§ļĄĮįþM {cÉK_}Ī­˜þÖ'}ņuë§üķ~ÍĐü–ÛŋûmËýŪčĻ„Ú-ëĪw}gÎÃŅüŒ [!@…­Q?ós<æ`õ‰įËaÛüéÓÓŋœôŅ7Î'ŒýÅôšƒiZ 'ÂvŅýZök#ÛÓ{ˌ[Sp·.|ĐM[šŲ?ŊÚ{Ž‹ĸВïOŅ>zrƒ&ftÓû_ž|yģá‰øxšJhMƒ^„öï…Ųp„Å^č‚mâÖÅŪ–’~!L!€_Š\„EŠÄõJϜĮÆÝ @ÐM‹Äˆ€W―üüŸeUŌV­_úG{txË&DFĸý ųŊ’*Þ%Ïģ+:ƒ˜äï+#tĒ›ÔōiūΊ·PŪ:M'4 .ŨA·oÉ#dcUbÎ`‘°][Ž5tK°Œf,SĮPÄT—yhnxsŽo!(‡Ž+ž%þĒ Ü2Ėę€ZIHE­œóUé,ëĪq'zÐĘãÂs‡™ūČ8įØĒũ'}ũëŪŦ~{`ðØ’w―sۈĮŸyęņųŦŽčĖ)ņ§=øĘ‡ŨÕ9qß=ũvcâÍĒ/ߛņÝĘíNlþmSVŊۆE|}ϏMŸŧ$ķmrJäËoŧ+šð—ņ™Ógž9eÁ7„aÐP}JyĀö8vÞAæ)Ž ÚÂōGÆõþĪ~ßS°cõ·?ŋ<ā eô,g4ĒvjÂîeKæĖýėˆ[ôí‚đzúMýŊŒyöÉGžzbâ^QÜŽUíÔķýŊŧ:qæsãÞ}wÆĪ‰Ÿpˆpƒ CÂŽ;W‹+Q]Œ Ë2ĮÖp#C‘ķ::Ņr@ 49ÆÝv_„ÏÔ-d'9ömigiIĐëšj+ĸ,!m(4 þAÔ?T|Ä&āŒD™þĘ-ĮĄÜŠúwé/ņ<įïy^ü läc;ķ’ïƒTÖq>Ë"DĻ”{+Á8vvÐ\Ũƒ ` WĖđâžŊNŧ(}S-ËLj ušÁ!…õä"ĩØó\90 Ó0#uĻŊ͛7K/v)gMĻß9ÉŌfeeĩlŲŌ0#ïE§u~ėđÔÚ5ˆ ÝĖŨ―ývėæE-‡ ė·;‹Ä’Ô‹†Ė{ŋųĘ ;-ēmZ\Æ·ūЊÆÄ§=:í›NËVĨuļxęŧÉvlÕaČ “ŧžHÚĻmĸf§ï?ZĻ孟<ígŪëÍšÜúųû-XĩEâc㖠ĩ C3A}‰w=7CŊïxFŋŧ_žÄĻã8Ž[ŋa {ú’'ŋ=ó-YÅ­† š~įA=Z4Nxr+?s\_BëįߜTŨ}ĮŦÓęüūÆ_ŧyĸ~7î9ZČ#SŸ?åÆ-ûŽZQ ŧŊĀЅŋáøĖw–ýøËÁ‚`“ĶMb˜Óúæg^pâ}”‘o„#ĖÕáxFõe`ˆ‰ĢšÅv(Õ ųāsęRCj‚%pß G)ŋ+@ĢīīĪž<`YūНlĨŊ=î†QõþĢ” "ĸ)G1x$ĮáŽBHí\įMþD(iQ(xE‡4*ðÝĮQ 5VļDPÃ2aTíRÓcŪęÞ(8mØÖ2 å?+Į؛kø!4ŠėÆQ:ÆļŠt@éŪUhZ5ĖžjájSRRĪAv€—˜+ŦõĪ;Ĩ4 ĘýIiC‘””$ÖB‹Í˜äŒöĩ™ëĻŌ5N|­ŧ\wA7’Œô 8sÝ ]§YĮ-/m‰k3ÛĄ6ËóˆĪÞŨÜā:Žváe­/ŌĀfĻu+Mc;–-xþĨ[uoûĮü›]ýP‹ššcŧ MÚ iÞŪ$s—šŋyûnÂs<.4m“F`TŦAÓ:`;ÉŌŧõkÓOĢuũ\Æĩ.ĩ„í2͗Ņĩ‹gÛ4đé̓›Ģ§iŅFĢÞŅŅ#-Nnéî_ŋéxë)—4—gË} =oÔS€Š îÓÚ%QîļųwF8ÂPKĐōˆŽX:Q™Ŋād‰†ƒP[ĨŒīŅ5œ+ĖÄ.߂ž)—ŌučËmÛA9öû#ääY)­†!— Æ4+wpmĄëčÝčæþJå|Ģu ~ąŠß EäßC-Ž2ô(@ā…x&˜·JáfDŒmb *LŽhVÕŦ†ŦęjĀYB6Ė‚đžĘō0YŒ+åīrRB­Ü(kР| ËÞDˆ@pĘˆ/\'芥KTxŪãáÃåx`0#æ8Ī" S|à .úŲ”Žcų―æÝÖþ‚Æ–įĀ%tæ’*!\ĮVĢÐ} 0.8ĻrÜægėĒ„4ķŽŌ‡Öï{ú™m;š—ôÓåÂÚŅã\.ē+-‚sōŸáwĖÅīÔDģZ‚<&ŽĀŽšĄU r€2LĐJĮ7ŝīÔšõtÍ(**šwđ(-Ŧ\Ē ŠW•.Uh1s堀þ·nSŪÃû Aäž°Uœ)B­JMĐ2dā"Ҍø/TžË\"4…ĐŒÁnfĘðÏūÂæ\þīĘČ”KžĢ˜á6ã:r@—qï‹Ļæļ"ÔŠk;šøøTy$ũ@ČɊ€\ۛS•)#™ šcô<ģ€X ↯ĩĪBé‚[īh‘šš*+GĪG°œ”ō/Y3&i\AŠ'āŨČŨĶË%ít*˜õĘä&āęÖjÔšnģ ”āėr„#Ü15ģ6‚… Z.Ės‘ÍÔ<Žäy mØVĄm ÁāÄ^‡ ; ļéæžžžĢG˔JN:Ū+ŋíCÃP­v)ēīĨž"5K c{ĘųraóĐ#  S-14ÓĪĐHká@R#cԙAKí’r; @ũrbŒĮŪ#Ļ.`ų]æ‰ä؄k°rWþH3 wi‚AYj•Ÿ—[R\,'ąCšãØķúk6 P‹wÜĀÕZ&$bœë\‰œ­įz0eĒ+ ðŅPH͊bõ3åՉ=rÏ=611žV0.Üóā_õƒw]‡ļä>8ólæ‘ĸôGj…@ãjĶų}„T$h(ÐdļŽ5Ž>‹säPšoÛšē_ÉÛ·ušĻģLĐdõ―Úï–ĩI2·ō’{ð˗N?ķ}ëVËgAՆã`™ē—Ýŧ€ŸĨx[Ā…\ƒ·īžtMà :ð”V—ƒĒëēuëNnޕ“]#)JÎė:Xĩádë& íÛ§DD˜•ā†rAŘC~˜ĨnÉūƒ'SĨEšĻ‚Á‰þĸeņo8óp„·ÅWųŽ@Ą’eZrā9$e>“ *_yŊPĒū–šōĖV:ĸĒâĒ;ķĩiÛVãŠj‰SꛮcX€U“ÆMZ·EĮąO—Ū8―ũ·}›lý#ßÎ 0ÛGĄ=x9#$B/* ,ËÚv•ŋSđi lķŒJ_U°6JŨ™ rŪþąéJĘJË˂YđGŠ‹ĘHÐ+))>™ŸSh—ÆNNiI^iđ_žNAš›kÔ#PV+á?ÂôW"ðģ+B9äŌ Įĩj%_yU_?$°2CkútUĩ`;6T1ût9ŽZ.Ÿ…dŪG)1ĀÅ^ >ÐđRƒéðPMÜÁšĩĮķ.ËčÚ4đVŒaj(‚y9Ĩ[Ũ,,ΚŠOÔ]€IÛĐ-?ū9oÅcÆ6­á#Ĩ{^xqQĸG†§'ûϙĩBó_ûČcō―ÅÝëÅģĘā3ķaZĸ ÝlAÞŦë–a„ Âņ?•ÕęšÚÑ(#Å|ķĒ!zfƒMŸ|ŠvŪš‡í`)šzķĮ…whĸþžž\Y­TUHŦô !ŊīpÕĢcnOïõhÏ[žúzį—ëģ7Ŋ<ž&7P,ŽĄšew\RŊĩfčúýĶŋM|gzė=BĻÃDA)ã5b#bˁüâÂÂâĒ=đ KˌØäÔĪ:ō ĢõČHÓqŪõSüF„OģLÝ0Đnif‚ߊgDȄės T·͜s·Rxžį`ČzbU9ķfõŠ}{vų|Ô#`đŪR/č†Ķ>–?hčŅm€Ø64WýDQbę`đP šZzð@ÞĶÝų=/ˆNˆ(ą™î4æþļč–5ßžtĮÞ}đ-[$ãí )9•õËÏëŊ{Buņ þąbUáC5=~”â™yķ‹b5Ë^’Q~Ûm@„._ĩ|ĶúĻÐ9-p|ÁŧoŨéóāåéĩ%·Ū–‰ŨÁąƒ‡Ú”Z–OĄąk™ >ŋŸq0QéąĨHŲP•lš%Ÿ&_ĒĻÁ—lîũYúÛx8EcŠÐī`öĄsģmWP Ü€›R3Ä)[ðú”ÃĮôŪgŲķįSoČáÆƒ„#ÕÃÕĒ0î!…ĐŊ Á—s,pBy˜q"BŪgéʎįBu™îÏÍ͑ĶĻõë7TKīRϧgÍH”” iÐĩëÕŪsoûŋž@VááŊķųņöEÅ~Æ―ā‘ƒÛēÛöāO(qŅŨ$ēE·„î+‹–“rýԎžŋu˜/ÁýtaÞņ‚KOŪæž1óîņ}wķˆ:s6(îųĻJ$ˆĀŠ.ėýe‹ r―ąm[fßJðŊ+[7T’7ĩ­'ũýķnÝ"T°ˆ[\.1 0ĩ唂>ėҌ†€’đŽW!Ây\u8{ņu›Žųë$ɟ˜ĒČõvíØäyŽuz†nÔįKl\gÝĶÍÓ ÄItüW" fž>Ÿ/{ó 7Ÿ$§Ũ/ü5ŦũíO<0čŋWļpę―o-ÉMo֩ȖŦ,Vrė­7Æ,YŸ[Ģ~Æ ãžnųúĐį'Džĸû‡Æ―ŦëÖog?=eA\ZÛį^Û,Á§ôT7ÜĒs§?õåĘ\‘”ņŌŦÏ·ÏŸ4>óûU{’RŊ{æ•ÛZ$ęŋ/úh·]kËó—E|úąŽ·Į}yĻüú‡Ÿ―ïōæKÍ9Áb—~ģ +W{ė•ĖÞuíÂŽ§F―ļqωšuûÏžģn$ĸõërŒZŧ~ļ6Ž—Ųģe ž?fÔ'ëũÔí5,óĄþĒ=ïÎü6ŠvîW ŨGu>éŅsV}:öõ7SÝļgäĻÁ=k>óüļ­YyÍû™xOæ„ÛĮ‡ĢšļZÐuYĶÏņ\Á<;wswÁĶ.à :šNU?åg`š&ĒŨąŌ4čØōɁ}{ ōĨóŋB(…°UĄ§BĶ īÛóX̘īĮŧ=*ß/såÛąuükũm^yhgo_;Ã4"ýþ8#~PęÐÝ%‡Wn8tyƒ+―ėŽ`YYNQ^aQņš“kvåėžÚq@Ë%ų%E(†Udˆ((3*Fåą’ĘĮĘh(°ī|Ý―cŧįxšYĨ}„fi!A‚œä(ÏPfĩŒ3uIå?ÛB1Cu@í‘…uŧ·(sĀį‡Å_ϚôšāžïMƒîņ g˟ŋwã6ĮqMËwîžØ0Ëē·―öŌī î}ņþÁõ_zaHôŨūûüžāK“Į”þúŅįûO™dy n·!o Œįőģ—,{äÂúm“ę_7ôúéŲkŋþėĒ'§Ž+X0qĖøOfŋzW$ᐋü·2GMZ=uėØÛŪE ÞzüŽÏóšLũâOSŸųāķų―yxÅü1ä=ýÂĻ”­ïūšÏ}ĢF_ŧôÃSođtV֊ųÏ}é{ėÉĄMö~~ߘņ_LvŲļ;·—<;nøũ“Į>ųJų›Ŋ/zčĐ;n~ĸũįwšðČŌũ_þðpäļ ÏĖóøĖ†-ïmņÚ âÖw ðބĖ_ŪŧĒOƒæÓRZßqũeĸôéóëŠ^}}4e–†ŲpT_GtS*~ØÕŅt4ĸv8ZvUļĻ܀;2pĻ觐ĒûĒéšč―ĒG9pā"éđĢē58āÍáÆŧíėgäĒԎQÂ2âMÍ([žzIv~ž”aĘAMÐ*ķýK§Oļjúž{&ĮßéüÜâĒ’ĢG~Ë^ŪđtX‡!ÄĨ!Ų―ÂvŠ…ę€Ŧ2œûg ęŒb™ĸŪ[―ŠļĻ[éŠQ.ĨÐ+H‰‚Õ!įŠ’X@2Δeķ:ũhÛäÐ―ÔJäË{=ât€Î)]·f•]ZėĘķnZŸuŠčt€ +ŋ$2“„<ÐJ˜ÏĄˆ.čĪ\3|î#CŊzoŊu—­Øšbïîžŧíę‹;u_ģôšNЎŽYŋNiîŋ,-aþēâ`z­[·ÉhÞūKŧf‰;–Ūܓ_ļö—oŨn?ļ|ŲķbÛS ~éņëŋßüäϧûõęqŲ—&GýPÐåŪą=:vūwÄPó螭Gó]âŋäÖgŸļwЅ­[vôÜĢ#ŪūļK7Ȅt#ïõÜ#ÃoxāþaõJOïÚķnÉoĨŨŲ―c—ûF =īiëöã5>2aôˆ;nė{MliIIyɖ_—:þíâ_NDÚ}ļ„‘ĻæžðüËŊŧšC3RæēØÔf­.ˆËč|YŦFuĪĩČ?°}þŨ?“Ļ8VS„#œÕŠÍ°}ÂUD,zÉú”_ÐĻ.8„XB3@„Ë=ÃgęDÏËÍŲđm[›6mä6=ÁĻĘTĶÔS‰Īrŧ_YüŲžÓđ^§ŽÉ­—ŲÝ0vݞĩO}ųāļčŸå‰~býIõĨ7ëЂœü‚ÂĢ9‡d}–UxīGƒ {Ķõ,+ŠØ‹Ŧ|ïŊfT(âẌ́š„fTĻą" *įŋēgŨÞ―{(§ D)ވF°3#ÚĨ›ĮK§P5įšrŽ€đZŠ5A孄þÕ\-ņûhv~YT\īϊn~A‡uK&\4i•!"ãōƒDČų ‰‚úhZ\bRD|I~€QóJâã",NHÝŸPV’CT ė’;hûĢčšųSž·ũ‰Gnl°gU€‚ĢBÐÍuDP]·š‰)éÍ3âÛwļĢv“$ zˆ šĐYīÜ ž1ą„ßŋ͘ZÏvå~=*"‰·ÜõÅEÕēą]y6õq3§ŠŨFĻŠ?“ŠÃ]›cE‹/"9Œä]LJą§|TRj“vm;^ØþŠ&-[ŠōmÔ2"üšķAý5ž2Gb9—ËÛ_yĸÉ4ečý+ūúü“ĶĖ ŦÚŠ#Â―Å 2ÁqąF <–ÔÍ26žą ĀĮKplxVhLĢ I@‰Ža™:Õ]Ũ‚ïØąUķ0Mã,„=o+ģZÏó"ý‘+-ŸšbJŒ/ĶwÓË|ÂljoąrÏÏ~7kĮáŌįÖuÜ cËė­ĻīäT~öęcŦæø`{Á3/Këæ~Ũs΂ōŠJÏÄđf ΙáJĨí†uk ōĻŪÉXĶ ‚—…3ÜNDR{ŽP4ŦaUÍ\L? Ø8đÝLýÚ~žw,!ڈōé}ŪŋņÕ)Ó^™4y與"}FBīŪ—æÔ­iš–|ŠHLŧ SĘáÉã^üîįŸÞš2Ûé˜^7Î0#Í­~8kւOgūšž,æ†+:·oŲúĢđģĶÏ~ũá'&ŽÝ–oz°0?;Č=ŧôøÉ㛷ŽÎõĒԏYþÃgŽ9Ųîęk[ø·o™;ïģnƒýk.üėÂoŋĘ|ó]ŦM—6uǃen Ø…ëæÚēØP`5-xQŸÏ |3ĸ•ŋþæĩiï-ÛuéÔíÆūq_Nyõëoŋ|ý­ũŌ{t̀Ý@ĀÅ_`Ģ4_ôÅ7ßrdĸ–%„Ø'Nįįs"œŌ2ĀUÁíō287_”ß-ý|Úė'rv­ĸiÝáēzŌÜ`đāZ55Ŧ G8ŦUõVDåģČWBrËЂbE?ĖCÏB‡ŧ:Ęô•!, T@Ó3PZķ{ŨŪĶM›PŸįÍj!ÔX­ÖŠYËoEä”å–öhŌ=zéœĒ@‘ijĩšDČ_zĸûïļ°ĸÐރRk•Iß钒-'6ŋ―wv čR4ˆKđĐM Á*YTÉėY8ĘsÏĸxÖē^bïÞ―;wlSĖw]M. Ø—―ĘāŊĻŊ :ŽNІâ.:.ZĶ…ō†;éųŦC7ô6-ëYąßlEˆŅšKwJH™KäcYđ}tO›ôÓgbŧÏĻŅėĄįí­™ÓĶûjķœ6fh’Ar<žØūîáÕßlbÞČ :ÕŦ)Ž|üÐļ•ß-ïØeČ éeąÔhÚĸށ{2·ĶÏ ‡6/ýugĄyóíúÅâÅŧšžté”Ėņ/Oĸä―$ēCĸkĐú-授|ïØ`ÁŦŋ-úRŊ™ŅņŠkîxvŸ6éĢ9'Õï1éą§ã žÞ}€ŸÖbŒ7iÛãú˜hÆXrZŧëŪOō‹ræŦҰfܚ%æ‰SĮŒh=ä™wr'žöáœOk6ļô•GîˆÓÝ šßTš/ŠÕĻý•—đcÝŊhüĐÉ.|ŸÓĻëîhŲŪięõ$ų8'1]ûÞRŊ†Áô˜›F=l֗vũĖ0ė_ūú‚ic^—JRްŪVmsđ ˆē'5 ÐDw\(KE%āhŦĻUæq‚™Œ3}bĘĘJ7mXņ%—Čę{Îye„=wz‹héÚ]Óšķ­ÝÆc^óZÍZÕhžžp-5Ėr'&E”dOX8ÍHŽýpŨkóŠ NæžōJIĪYj  ―hHýø†'ēOTaΎģöëFÕ46īXąīëÖŽ)Ė/0tDB@ŋKŨ!ĀŽĮõ˜ŽB ‹UMŽƒžvdP•fĘ’üÕ‚$#ĢņĐėÜ­ŦĸhÕíÂĔ$]‡y?#…9ųGWŊkãķoŨ 9eĄüak4é9eöĨęãsÏárō$ã zŋ6ãáXLGƒŽC"’Įūú&ÁP:-ßj›óÆíoó[Ī ˜õÉyt0č4ļðšđöSg$ÓK!pÄ=âK1~ęˆ ßš 'ę =mxÅSÛaâŌïė%Ü ÍÚ^ÚŊc/!ߊNÛ+ïîHYyN ėtŧžŊŒÖ’VŨWĢĻīŅŊÎ ’ÃÍËnš“2ĮvÝzŪš­‘Ú&fߟđúAu.ā`1lĖS A#‰7ß?Ũōķ—ÜōáĨđįrÚoQeĖAĮ#ĸZ„#Tj•ŠÎĘǃQ>žčëŊ"üv˜æ*;mÕÁE7ÐíĶUý‚‘ĐfPĩ5TŅÓ@äNŅ1qũŒļĸĒ‹:WFŪšÞJ—U IAÉxŪ$~ßýÛ ™3ýQriĪN…EģũúöļņõëĖ.Ę;vúÄá“Įũæî9Tz$Ļ>ų^,‰q\'$ĻĻ,Ļ<g"4>kFŪfBОzõęįÎ.‘ŌaráRp/„ŠĢ`Žn 4ŠĄ<ƒũMuÕGÃncē:ų恃ÆOȔV“Ãįó8áoذ<ä_Đ ”–.Ûļ=ëT|jíÄÔZÕōOåæ9ÚžnRũŪmjğ]œ[‰īÖu=ĸОͧEŨvÍ,­ZJϐĻýïÂíXŒnÞŠq" ũŧ Įĸl†ąoß~!DýúuĮýWģZåDĨšuŦ*~Є ZrąáģĖāļ*ÞU=š…@įo]ál”9óˆĶ›–%}―6oÚÔū}UäZ5Ŧ­Jã*ŧ?°XEЀíÚíŌ:īNi―#{ŸĐéåž§QË'vl]ŋŪõC9ŲEEqĒFK3ķmûf‰þåeå!Þ@ Bï2០šŪK9ÚÆë$Ίj‰3Ø,ššĄ[pđ—bšÂYĨo`Ū@oxpîiT™ĸšgۃĻĢ™Čψ+zwi’utßūÃG7l‚ĪĪ$u―īm“& TļsSĐÕc|ýf—5 ÕŨ}KüŋĒ7ĖčJãėĸúĘ|ŋĸÔu7€š‚ŠQÄV°?k€YÐ]9əaÂ>˜2@ږjó€·Ī„3Ļz8°wT}edd0ÆŠ’ģjæŽyÛķvĻUÅ1ūĻŦÚôÞ°p‡î‹āT” V#9âčŅŽq‹ßŧ)―§ J;.ĮuĢĢĢŪír áä,Ü<į ęX *+ ŠjĪýëŪmÛĮ1ÂV%šx刭(CÐ\ÛÛ3Ãä2ĻāhēÎđPíuŅT >™ĘmIõz‰i͚5jŅĒąĶQ•ks!ãOÎĸEĨxRáøßaĨMŊ ÄY^Ņ ]Đ,Óļ‰ UüŠ{ĢĘæ4ĀY—1j@_bæĶi”“'ïŲģÛó`›ū*oŸ5/ŅVđÓŠķ`5ęĢų˜- ð‚"ūfDÖÉM3V-X}xӑÂcĮŠŨIMŪ§ēōsVLĻwŪŠ+ĻšŽęKÐ*ĶŽlÕĘųyy:5@ËEe‚p‚šYâĒZ9ĻVÃ2L1N3MĨŲð.:á―­Vƒô—prsó>zčБœœ\ÕOþ?ŲóEi64ŽþĮĸo# ĩ2VMIĩ° Úq+Ģ/•Âly °UWŧXĖcĻvŌąĒ_čv,RBėˆģuÓ&đ›ĪōåĘ% įKĻõų|ĘŨÆvÚeÔmémŽŦ`þïšÜ—`ėõÅkĘþuMáōīFĩ9ã*W=O%BÕųs?bT&=öïÛŧkû6ėȀš-ü!ĄPēėa94ýFÏIčŨbęŠFã\ꎕ_$Q ä<Đķö•7nÜšuë^ Y*-ŸJCË*ŋŊšių0,Cĸ·1ĨđíWpŊīļČaœ„#ĸŋnx)&T‘rĮąuČÃ(A+9ī”›5š)ĘÉ3’Rå'ĀäĢŽûþ€,ŒC6‡•ûũïÝ―{wÝšu•‰xÕLķęŒB…Áæ>F|L|ßö}6îÛ΂_* ĩŠÆTUážWzÏ~õ“BąŠâYįĶŠÄY”BčŽ$KŧzÕŠÓ§ģĄ™#ļ( ˂O+Đßo9ŪŦSM`[ITpÃ0°'LÛrîxĀÚbĮrČûņØę0Q]—‘ĸāøˆöp„›“Wˆ dd ÅĒ4Ä\O@…ģÁ™īŠTĨ‚`‘”iČ99Ŋk ŠÚīq,ëNāžQ9―u'ôī4PÖ§Ýeq‘ņ·z‚x„ĻÜã&ÕG\sŊc;gÁk՛åĘïĸg B)íŪ]ŧķnÞĪ~\ÏAGs=ī0'0%H š â ĸĒ›WĶāļ͈L#ß1ÓsårO՜ņ—[•6YâlŧvíĪA;ā,†Üi”]Å:vė(}„sss+ļMwsö-üâĢÝĩ`æa#Ï*ĩLM3|~ •æ2íĩL5cęšïĖHÝöÁ†%_ 2YöûäøĖË •Ēō:JßܜãEų…Tļ'Š ŠÁާōZýĖZ )ÃŊÂgâ*júü–åSsšnœ9yXkÁKĄ3<ëOû`Ũ˜ĶïÜkācâįRW@ÍPMļ§?ûA‘ gDõŠ—|†Fþy„# ĩ‚š TĪęPĶ)>QÕĘÅóäž9,ėķÍ)ÞsđXŪó˜ŠvĮw€€Ū åÁōÛ·Ëæ ”jįÁÖĘ0§6̐Ó4äØõœF)š&7Ž'<ŽVY‚ŧB8+åSÛī–ô‚cŸ“u­gA’ŠóĢ­Ūë2ŋÞ°~]!p :ó.-ˇ5`P_ ;„ŽŦ„n HBë^MŽÎĪ­ŠÄs]ÕĶ .‘Ū͉ŸąZäēýļĖI;vQ%d{ąúõëËĸÕĶČsę4öāč9KÞėÎ?TLuŦôøę3gÎų&ÏÖ Ęn_·nũĄųž=qÆžCÅÁUóįū1yæŠyšĄÔYþיŊŋþÞ+ʄ/?kÃįŦö‚//%wnXī|Ŋi–/ýrJfæÔ•;ģu] a§°‹~ýjVffæėO80Åå›é Ū-$mU ;ZyáĒŲoËÅ [ëę&ąOΟûÖë™o|ģá 5MâvŪYšwŨš93ĶĖ]°žžäčžw§Ï˜ýq^€éž|ûę?ķoY6û­‰oÏûūÄÓ`{Øß|6/óõĖ9ŊqˆIY`ûęĨûũmš;sęŽåŠh^úõ'ïffN\ū?Ũ0õžŽÍ+w^ņ͂‰“&/ߓkęîڅģ^œðjæäÅŧޔ؅?|wō뙓—ïÏĸSv―áÛÍPJQe$#\ŠxóŦšRl0 0ˆ ' .ÚD„WØ5ãL`ŌD <ŌÜÓ9RõÅŊlõrV’Ŧ 5Vf†ÐöCĢúČk‡ąŌ24Ėb_T^dL^tt^TÄ)ĸČŦFÄGĮ Bþ‘}Ė9ĄüÜtíđmĐ Øše#8˜šžú°ķm ü°„€‚< ÏŠtár™hĄĪ]:UĖ5AšÛu<”!ƒmĄËR \-cL&­’:·Jxž';“Kę\ŽC?čóōeÉÃÎ}vrRíļÂm?ōäąųŧ™Oū4‡™ū5 ^éwõĀïŨd­ĸaÆU―{ÍúyÍæĨŸ<ōÂkÅÜX2åþ‡ĮŋĨEû™3zäØ ų9;ėąÍ9ÔôōÞýȇ{r?9rĖǃQfΓ#]w< 0TÓÍ`ÞáûOFEŌŊf>ųÞk 1ĸÁ^™!ŠO{jÐøK…Ķ•–KŒësë'?œˆīō2ïúî{ _pÁØþŨ<2qã‘Ã<= ũŪÚēáôf}ą•˜öügûũ{jÚæ“y ßþŌ;ómĒ-|ãÞq,1ĒĖïÞ~üÅwæ…3ĸđþŨ {ymÖÁ^zðƒEM“-xý‰ũūßgą“ãîxsŪvjõGýŪî;õģå6/|hÔĻ“ •Œ—åxNΞįþGq\Œ”BŠŽp„Â8ONĪš`ĐmBD8ũä,B§Æ<ŽŲ™&8ņžŠÂ\hÂ0đ`ƒ-t˜Ö<Æ(ūƒĮøĶM.ïÝ;))Đ2OZĩ„Ąrxžjyāļv—V]/ŽŲmũþn iËŧKû /éÔÝqÜĘØĘ’üsʄæÕļ*{‹“!–v…Li•]ŊŽžģg 5`ū*YWB*ynPĨ KˆiXŪįb3‡Ģ †`kKέąį\š4H_vIÎV͚UĄ„ĖÓC%šßÏo~bÐåGŽÝ>ý‹~ą?Ïþö‡e/ŨŪs:ŲXļâ·<įoN9kŅyč[“ï]6gÔ֏Nū2}ZÞŊ3^œīž(Ũ{ïŽŋčåųÞ’~ đïžQŸäÝ=ëúĻ)3nnŅmëϧã?î“üÐôßj_ņX”ˆĀđģō/JMå@OđQuZ\wąsē$w―:ĮŽžúĮŌqr`ísū,œðËü~é5—ŸÜ8ïÓb}ÁÔÛÔô’íá3ĸęÆ+†™5o―ĸđįú֛V°sWĘ―ÓÆöZ8ŲýáØ7,ôÕ6ō•§Ŋhš9Ýũėĩ;Ūh:gÎÖ[į|û@ũzWĨiwNüuĮ•J}‰7Ýóė‹76™QļcĮÁ}ĨÞ{ÓŋŊ9|tÝZÁCŋÍûuóą+ī`­—ū:卄‚ßïžųųÜ`Dũ~ýÚžyøĢweDdĸX–[\V\/―į–5Âī?aŪķĒĀ ÃV=É•É1ĮvX€­„·B€ÆĀŒFp,‘B:Tærô]\>”ĩe˖JĸqĻW+ŧj) ‹ŒˆėÓ―— ū_2nÍŧšuęƒÄ=ŋlŦjŠģŽŠ”üęŌ—vû–-ŽíPŽ6ö\ՇQÃŦį†ĨŽãėëÎG›ĻfCē…Šđ6dæĒĒ’0ĸÕA1ĪÍĢ}ŪP]$*_Ŋ"éĩëčũEpÆ 7d,đV­ŧn,ä<Å`•ĢjB}þē1™.[ö‡ė8yÆQ—OBĄēÃuŽØjšãxęäå EĘë/āAé \Ča™Đ+Ü·ĐsžÁ'―‰Ē&SZɌKÉGՆ:ĘÆAî•ÉQ!å<*9}”ąoūåÅV ïŽß&Ã[zÅ-Oũï—[P}(ĶĜÛŪS&pŒÜbuïā­ĸüã#íýļ`ÁéĻīôņ‰―ŊŊû^Ÿ1ģšýēŋ ņÛ―cknōu>s°/pʎÔÔ­’Ą{;ĸqwíSFžõÔ°ÃŲyäãî*‚ )Ž9ŨR3.ęXoƌw?Ļ9ĪköiŊ~lĢä“f/ŲxwþÝįKZvÁÛöā 5f{^9W%‹P'bčöéc?/øĨË=ß.YÜ8ãÆ&ZwÍŽ[īäĘÚ=-ø<ąÉ­ëÆ- –ãáš<ĘaˊIë~eÃ}Mŧ{ėvVpJ‹ķØĖ.vŅŽYOŲ3íØwĻQ‹Ļ{ʆŋôV˘k^ûbåØĮúEXŽóEØD‘1`$ĐnčĶ.ļP–ÚĶÏįyž’hgŠđä<Ė@-ņûüBmņ+‚kššÝ†4ÉÔAÆŋaݚÇ[–uEmĨGÕÅK=ĩ§ãmÛĪ·`—SqÏ ÛëĪÔV)­BÏóXzŦ·=Š[ipđcĮöÍ7Á-ÓĀū‡š’s‚- R\-Ę7 ĢW’8"t |ÓĄ•~įUρæ[(žPŪEéïĢ6ūBxZđîÐĄC’ÆQĨŌ`Ō™ÐĻYKŋ°ã[öyå…1[>\bī―yęŒ[įz]ŋŊÍúÔÕ#ëĪujZ?šs‘XŧI‹&iœóčø”ôMLĖÃoĖh͗điČ;œW'―VŨôĒęe\zËÝŨ=uG›ä[Ä=ýÁŽŽQŸÞvÃu·ÞõāΜ †ĩČ.7;]?ĻUéo{āŲȌÞĨ›æĸt0§v―FMë$R#ūQó ŅÕÃĖsĢÓ.zeęĒ•Ü}ŨðYK–iõ/y띧7ū9rāmoōĀč}LŨk–‘ĐqŪ§4j›š ØkÔnÔ0ĩ–`܊76ĸøÝÍýŪ|ïčĨGŠ’ëuLoœNÄVL› ZGé"29ýĶSõێc;wkŋþïŽōWúõ„ãü훉~äĄû—,^Ë@ų)”G™(ÕtĩÍřgš>.g ØðšÁ+ĒBŽ=Į“ģ˜kÔ ĀEÁŅMsØß†2DačųÍBĄˆ E/&Ä'L™=sŌŧ38óâãâūũy­Īšr^―aUû5þ§Î2•ÍeB)­ü‹sįĖ^īðKKŨ=åÛ Ø ŲĻę…Ģœ%rZä^ Ëp= ãš\VĄĢÐág įQ†æ ōýođeÐKã_•tð_h7ĢØƒĩkŨJ‚Twa?yâĐ%"Å^;wŽå ōnI:W˜œÎuKãCE ÜŲĶN˜ãq\K  0Čų4Ũq–vyĖÓMŸ\ÂÐ5úæRn+g,EķļŽfX‚đLˆ~ žÔ‰niÂc„cëF9ãy Čæ˜ŲV{™š`ā%Žvõ XæŒÁJī› Ð­Ųģ‡?MĀ[MčpÛEėžŽQ7ÝŌqÆwĩŠt%ÐRĸ4* ÓĮqwmĶš@ĒšBšA€&ŅtÁåsÃŌļí2‚] ]Û_ õ9&gŅ}šp―pņEØnæŸCmtôCũßũӏß[ĶOÝÁY–ß* ÃE+[ÆpˆP_p —]†aĻŽåšŽ—Ę'b Ŋ S― t]6|æđįSSSCPr~ĩ€ĘCÕ―ž<Ä2­#ĮÝõćđč=OÜ;’Cüs§Ū?ïãĨĶiĘōŠW_yą ?bþN …Â8B*æģ'ÂgY`ßۊ2eáqŽÁ2 ĶM9I5rĶvÍ8Ð―Áōō7ßüĘŦŊWƒģHk7mÚäšŪ,aÞäŒlĨ.óY9~JöÏßE]™?jwņŋ―@ŲØ eũ'* ÄŲŸ7„]üÍ_ÔđvPįš‘žËE•SúŦ>ŨyÎ&agŊ*!˜Ī0. †ūˆš4Ūėö€ 6HiîÂS‰2Š{ŪRmyĘåÜY1åüÕžāŽDÉ mg<ðõÂŊšvïҰAƒ5j(6öIșPNŊ%vÉš#kŧ5éÞši S7nūĶŋiķãĻ•UÅUgBMnB3j|ÖQ`âUVķ|Ų%ÅÅrđžÃ91 Â’w…­JBŦM Ó—ŠRýÔáí)"ąŠŅt K!Ā8­šœ―âããeę*é™ÃîÜđķ­ââĪĖ+---""âOaBĨEĸōZXpžŲ?ũ—ÄĸÃųQ_ėĩw…ęB—Wýãáį Ãl8þ;\-Þ 2PŋÂN9ėĨŦ܊ķ ũY”hŊŸÏô–KišYQĒZa’tlžå“ŊčgĻģ←ōēŌâÏ>ýøĩq/O2ųũß?•íyĖÉšFŦFĨÍ.Ó0󃓖O,*/ė{Iš6iØHâė9%ąU'Ïc.ĢâŽ5YRx°m›žĮĮ—ÃŪĀ:lbãqč§ 0Wîģ|hŧãaŦ ļŒ )3 KŽ5ðŠœ3.#äŠēŊī•ÅK͚5ëÞ―ûĩ—\rI˖-###ÉĸĩáōŲpüį)Ļ| ›Ęƒ6“0ÆYЁØĖŅ#Q7*ʍytĮv­|p€C jįĮžęĪ 4ĨF}†ę`}+[—=rxÅŌ?ę5ĻQ—níÚĩ—!ģ-‚QĩßĒ’IՋŦ;îŠņōMJ‹‹/ŋøŌģžZÎĘUÏŊ1Pémå™›IÐ4réïŋgíß§zYRTŋ)ÃoÕ#KÓäb{ĖãØ·F„PĄQe݋~‰šã9‚ ÓgÉĘ}ïEEË4ŦĩŒ^Ý TþøĸÉW8†Z–€„š„|d˜gš Á…į*5°šßÃķ.”đ^EŠGσđ˜+pCÎ˕u–”:čãđUIYņÎíÛöïÝûÛÏ?7kÞĒ[ũîí;tÄĒ”"a—~JZ†ÕŽfóŌ@Đ]^~qŨî’―­œ‡žĮ”öŸ•‰ ]ŨWŪ\đaÃ:ĮsåÉŦí,B !@aÂQÝnäܙ9ĶĄŪK%ÚšnĘÅĀb (ã.Ā2XCXĶįđ h°6„“jŽĸDl G8…đÂQņ•čųÍ)ī\0éŽŪ`!ĘëtJ•kĘûaąį!…Œæy^û CĶĖÃĄŽŋüüݔI™/―øüóæÉJYïĪū!LT~‚ đ―ÓûōÞUĢ!x=?] ë˜XbāyBž.‰é†#iMéŌ"w“V,[–}ōÔũS e]BĪĨT5āR=ƒ!ëĮ3”3hîâ‡Â?ĄĐžløC ķP Ž*wĐjûĸ$ †#áŽóY.°č•U8 k†œi%†nšĖSŽB…_"ÖāžÉžEtfr „!‡ ‰Á2lŸÃ%<J<øI&Vø-P@­žÜœÓŲŲ[7oübÁüvԁęÂNŌ›ŠQĢF mQ҇Øķ-·wŠķhĪgĒŠô ÔRLĘŠ$R`Čąã8ÅE…r&7'Gþ+)+ņl§,PV^`ˆėŒ_#`ŪNm˜–úĨĄļč2P€Qu%ĶeŠæļ\pŸÏ6Œ-D`°P€šĢXf„#ĸ#ėWKgĘÆs]Ĩå’32õģ|ŠÁ”ĀU‰'đŪ­‚˜ĘŲ‹"ú(.•ĒUāa&Z71—2ÕßPŪ2}†ē­Ņ-Ãs―“'ŽŸúæÄŋþŌŽeË /žH 4*Q]Ũ%i’’"7Té 6Ą0Į‘c ĮĨ21.-•đj^^nžü/eēy_*.(ČwmWÓ5đB"#`šîđžnhÔLUĻÏgyw™ŦRc…v‘+@y&Ą―Â𠀩Ė™†OÓϜĄļkdZ>ėzí(,æ˜J• cž+@x ˆčĘŒĸ―+Øm*ˆxlŋ7~õ‚ā â|5BEâ@Š„€JÜPQÕRĩ É[{ŧØŧ)?ŅĖ*ŅFIŽwYöxŽ:TŦũ@ĢÖÉ –eũéã‡ŊŦÕņÛ7Ÿ<}ųęuBvDĀRJÐîvŧÝ$֗—W..NO…ĐUdĐŧmĻø·›õudŊņnf"2˜˜€Y§Ô™SëËšX§Ihï€ūŪQޒKģĨō9.ī,eo‘N)öGZš2ˆž™žƒĖãN“έO2ˆ0˜oƞœFqRwĀÜ9ŠíYĻŧ‹*(Đ3îDÄŌ-Y›§>3·%4&0“íWp§•x1KΚï[Ķ{MXÁT―ÛŽ03ļšÂnŠĮĖ™išfģĨÖ&ŠĘž~UïĐũčjõßŋý<9yĸîøŅŅŅģį/BĻp~įüėėwíöïn―đÚe·ēėŊušÔÝ,,RJ<á/}ņŠÞŽŸ"7w%Ī’483V ‚åÕX“‡— Ėû>ŌΚmĪF]Q‘hÔ֛ŦÕį/}2ēG4§.AËÎ[kĢ”‘ĄŠóTŦ1g`ûĮÞß&ĮŪä<Ģ(@2Uûĸ\o[Ę$qb‘åxî,ŪÞn‡\’ýÅBâãq3óŊõ /ë ēô§—ēTūĄsƒáœĪ᧊ĒTk­{?&Ō0Œ’8cø"0á*_Ą‚Jbđ{ÎŦđSØg 0wnÞyį Q<ÝŠ=/—‡ ―AŸÏÕș$eÐPķáY9ēYũČÎS _FžÞāŽG%`tĐŋÉâfyūŦ-ÏQ#'@+ËÜ(A_빌Š—eŦJPŸÏĸ}>U āó―uhĪVYĪģÁĩŸfaīėuûZŨÞgbđ=šĄóÔÝ@äNHœĖ:IiŌØ8Ý[S*Ž›Ūn„ĻĻŽTußð§2gƒ&;­Šŋþ“õTïžóŠ―"ž4ųc­u*Ŋ‰ųiAī!Ŧ<ÂïpÎV D‡ØÖœ†đY•Ž"•{z_ĖUJÍ:?ûLöŦ ûþ!ļŪ8gïģûnžįœéHŋ>U5AšFŧŸ§ôï›gŸ9ykwVfö㙕æŸēÎó<„MzHUÓÄ'ģķŠd8-1ū‚nC[3žúÞ Üþė-āŠ‹dVísOt­: ášVã>ÜÍmýüü {Ð^ŧYh§ýgņÎ;ïüĩÆ\”T%Ýũ9óõįZÕãĪÝÏmĪ…CŠ*6 đŸ0s._ûŲ3p[Y%Ā˜œ07 ëÎó2zļßû†šĐKßƜTIð‰;U}^gnûđÛ°Ø+ŠTã[ŦJ-üyî&” ›dfįŲčŊQʆõŋïĘ1§Û~žđÝōŦ?Ý D‹‚ŲŌöhÆiNƒ*,Š‡ÆˆKĐŽ hy#x(~Ũjc]ž4ÐWĖ2þĩïžóGkS2ģÏįÓ2Ž2N]ÍFŧŸÜĖ-••æƒģĖØœPæN‚0#%œo8–ï–”šûZ_―ÔÕÝeį<ЌÎsĪĒYĨRIúƒy· ę˜]ëãæRđE\Ũްī3ØŦĶē7aŒĩ$ jfw7G1üFøåîäöTw…aöqvþ›ïĮļÏ9f.âä俐JeîąŪg?§ ˆ••ã4sóį<"Ö(ÓØ ŽwÞyįŊAmƒK­eĪtóˆpēhÝ{gûSžędĐč6īŌ4ēsV>ŨįZß4qsJÔ6·iÄipô“9!āý‘6đbŧ…VĢEÍģÉN/Ž‚°ÖÅfģĘÜJyzõö`æV‡į6Œþ|õ·J -ķĘĖč5ˆkfkï=FŒo%ûũv―–JfD)Ïȍ‘ ņËWũ=n>rãQ;ļýŠĮčíSøģœ4V:ģ°ÓïŠk€ûåjßyįÏjÞSÂw­ģ@˜k·i+VU m°uDöŦóŋÖEã―oŠ–™‚ČވwJ°ßÔðßķĮŊaw­uūnß0‹iî2Z_ĖįŲÖÉcŠ‚(e\ëdVöãđÓ^ܝvŸc=ØGŠsˆAöčóļÉâŠðj Ūvp ěûšŪ<ýƒîæąwĸ‚Ũ‡Īž _>1’ėÚįĐķ,›ąēÜÂĐ-b:ĖW\#ÏĀ;ïžó7 o”đsœOôŒŽŌsoJnþėgïgzh@fíf”ÅŊFęŲ@: Ϝ ;îSYé_ûėÓ_A§{·Ė,+Ŧdĸ†ßļšđÍ"ŲxmSîsŠVEÊKĨË_ÅX^q•tr[w/ÖIІī­fí”%Ý=UûđŅø AŠXa§ĩ\Yē°―@ã‚{îĮš˜XÄūËzÚũņûFŌF‚øBŠĶ’ nýü§*ņÎ;ïüAáTļŪë”R ™€SŋēČ`+ŪOjÞÏíq­9—' a?BãžĐR*kčÉ3~VŦ*ĢõP…l€#ŽŽĖlÕ,Ņ}E<Ï]ƒ"Ï1ZĢĄö™4w e―T’`f}k˜/wĩWMM>ī0AîArTk hĶŽéAųķ™YD\y$iŸÏĩŸ#\sāýUUålĐ+ōËHļ›M§ŊŧĘS„Yŋō~žÂÆ>·û5 ïžóÎß#āîąVŧžXFAyēóVf=áÃ}é[úĪ,ŠĻŨīŠĩtjWđŪuęTŽFŠÅķ}FCũFTx| OXák?s8vy€y#āIkķ_-Õh^%0Fī‘á7xŽ5+$ †Ū‰, rįģĮXLĢ=ŧ čĩĪŠ”oœžÝ{Ÿ“Ũš~! Œ5Ë/Üýwƒ™Ŧ*ó\ĸ}P8•Nsũ―OVŪ^ŸĮQ†wÞųkóZŠ ģ.}XÔ*›°Ú‰Cl’qvĮðT(R]­zgSEr}ŪĘĢ#§E3•Sß}TÝöæNģĘq­ë|7M#YyJåūÜÚðęî†sŽRFĸ\Ÿ2iÚÏ}~#ĮLÃcĄxNša…ïs#ÁŪ°­’‡ŧEfæŨšĀšŽl]DJ°`ÄęN0Žĩčl[h†NæÍĘøj~įQãœ,Õ7.ýŲYr_NeUŪk“Ų”1ÍŠŠ4Hxįwþ^ OÕnČÚyŽQ­2gíŌÆ'3Ũšb*ļĨðP•ēÜ\ ŠŠĨĢė°Ä)é3ũgÏĮj|­% Ķ[ ˜œ\ĢŲēŽBCž{LÞ`YĮ0ú’ņÉ=ŊāÆgwÓÁšDå‘:dā|ígãÞīŠZW<]đ8õũóØšÂÜ"<ōŒ@ÂÜü§ũîu]P5(Ó†}ĶīŪØm‹Xßō…Ō”ãBpįī6ÜÏF›ē îā Ice―[í;ïüÁn1@rJŠïnX%õīC7ë{ÚÎ{?§’öH˜Ņ(Oî Ũ>$Ý<ÖĩÏ6Āà žįĖ9>ĀŊĘÕ]Ân2ī= 9u^S?Ü-3)ôåUĐÔš>NĶûú\ĸ™Y5ÎöúœRāûÄÆXqßOeķÅËNf?ÆjĐÖþ søZié–%ģV ĀōdĨbAJPv,: _> ÉÃe–_qÅ:yZxÛڕĀWÏIFũð?·ÕūóÎŧÕēĸkˆƒL$ģaƒ ŪߊóčÓ*Ð<\ŠNķEÐNåĪŌ|֕u&jËÂũ>å46đÚŠęCüé,ˆÎŽ9gß?74!XûWÜęÏ~ĒSžÐ8ÕåŸRubļyVBiī1eę ó‡†á žFðÏuMŪ hą:ŠąŧvŒ&`cü89 UUĐ sčŧĀ}úõãbriIaÅrģ}iŅ–„ģũDĮ ĢbcY>ŧ4Pž{ïĶķįOT’ˆ7mæOÎ;/ÔŠ€āËĪ}8‚}Ûm7˜TėŠķEŔ‰UsŨ ’ûÔ5gJߨ@ԜWG؀0ģŲ@uČq=€äĀÓQõŦ‡°I<™6\€ûÞŧ*ndæ1cīŸm ïšæ‘Ę—ßĖōpöŨ―w[þæuõÂ^lt_gzkÜwf Ól6 )ˆÆįLBúõL}N„?Ï>ë>Kî1ēgú._ŪöOÎ;o9y€ C‰ˆ}ž/Ø7Sf… ķï'Ėicå9_™>ÚĢ™Û”ãöĶƒtmĸ―Ne]xsĸüfü―uŸPAH•đ-I@ßÃØ [eXRîg·°õ:uŠäąÆ]vōīJėÛŨëFŸ{ŊˆîūÝÞĐšßsšåIAÎ9Žŧi―ŪRĩVÄ}ß=Üčcą5ēĪzŽyyęÔI:#Vf*"ūķÔu]Ĩڕ„VÄĸTįž- tÞyãf €›_Ÿ•_ūá^yĖÂĖč^•4ÂXU(Ļt­Ļ<Îh@äė‰tē-^îæßåW$–GîĖÞ]#Ūó7ŋŪ‹ÎR júB-b­§“éftfÖũbãÞŲy8VÐއ”U5ÄĻõvÉÞ(!Üũýœ=ižOSWŽ― ØÞ f.á<‡“RæĐ°_Ĩƒū|ïDĂŲ>H ņék5éžŋ§pΓ‡ŌTTPTĶÁRĘS/}ðWįũX pZïģ§QŌįëÁP‹ČĖŅޟ:§“đãZm ĀŽ5üæÏ󘇙Ÿ―3(UŊŪþ-Ũ"KYYĘ óĘüy~Ė-<*ëdéuē*I<'Ŋ/7pÚnĖ:æ$0Ï―‰1“aWNYšX ôˆû~H˜Ņ;Ú&+aýtDBq]Ī••Ö$ÃĪJzįËädt°X?f؈m0í}OWúīAdƒîŲģn; Ak­ˆ·ðæÎ;ïV;ĩXī“ûœýéũđ7a+bJp++—›d4ßÅ?9ïžŋáßcôï†ðÛÜÍžĪ<)ĐÏūŽm­ōÁ#s~…nóJ’(ŽOė<(@?iîFðdéŨŠĘ“5 !ŦŒĪÛyÖ·oõÖĩÚuč6eã"%^Ũ?ķŨ‚A3íbĸĀQđšJũ>W„_WũlšƒžïÐV[ewŊ*͎:ゑnV’…­ :‰Đx+%e/žĒ™ÁÂGœ@F$ę<9AN$ÍÜŽ%q/‡ð'įW`€*ŧũ3U.óũđ3Ūk…aĖÆāāáĪ•Ā~#ŊJݧš—ŸÓ*6g%”$íNW4wÂū­āÔ9Į<ļ™klĒtúŲf ŪiØ―V§―”ÉŽí'w_ng?t6ˆkį1'œÏģSŅē3ŠF€ļ›~u3kĪ-vÉœ\-wu@8 ŋï:3õœ‡œn…C‚€™,ˆëaSFŒƒ6†ŸĖĄ7þâžóÎëÛŲXÖ“ūJ0OÍCК€ð;Ąfy<{‡Į˜žįYąÂŧō·c\‚Ņā]åp?Óė=’~wefB™p\vg{›õ­ŒSfSS9aįœ)$‡Y+ÃŌ=Ôæ1ÔЧÍ]F]ûóĸûųĩ°NB°ÆúģũZø€ė—ŊFv•9ĩųMk]îææ•ØøÁÆOA'ČÜOĶ>ŸKĀ>‡ftÛgŌÝĸ"ðÎ;ïąXÓŦņkö ÛÁ€&&&ÜūÝąôĶ\OĐ`0Ņ0’ŠĸMÆöŲgį&-< §§Āĩ‡Į…ĪĐ~˜úœßֆĮ8's>īˆ)ôu·Ę*Uy˜ŧŸįV# ŧCjÁŌt}hR­5Å\Ûhî>=ƒ_ŲĻÖAĸ ĪŧUXAģģĢ=?·Ž^™ú•jÕ7>FYĩó”r­ØgŦ0ë#ݍåã€@7UA!WģĀՑē$žįĐĐaGíį&8}k#9ð–d |w§Ų# $†\@­n)d͟1sŲ'LýwÞųcŲV҈ŌÎÃ&/GāĻpÆįį†ŲHĶZB@§ëW5ķ|5PÃ@šĩSܞŽp‡AU Đdf2kÞgŸ WÖĐ]⠗@ÉūÝ· ûXžšGķu]Ŋý~ð™Ö†eNؔ62ĶÐ7â ä/IÂŽl\ŨG…*đđŒįŲģŠžŸÛÜâZû~@tctóX†_F<ûFxsĩđŋw ŊÔéTs> ĘĘņÎ;ïü-ĻĘą N RĻęú|)$ ™yÔ ĩÄólŪĸŪ“‰ûƒüsĸī* ™[ĘNã>ėW–TáūNW™ųœ}TšHŲÔt2.a‰†ûņ€™›ŲsßtCÏÉ4X•čëÄl{3^î?ũÏ„Ō ŠsŽ‹ys蚟<9žAĘlÂVž,(ĶŨēĐáëšĶ^W•嚎K‘íĮ=}Ū/e1ÛŠĘę;:ó7°Į#^cî;ïüEąŨÕÐđwšÖĨŠÉûZ UeîÁ+ģ^ŒßHėߓ4Ą<âđÕĀ‘b\Đûū9[%-ŦƒŲÉa'<<ö~PęERSasčOÁÃwmų2@9Þ āˆŊwAļĄOÛ&.Öhãžh…>Ž ëļņ‚^Wė{Ģđa˜īsX`’Y bŲu?7@3Ō(Uešŧ~ĒSĘŪƜw‰ý눈C'hæ@ļ"öÊü‹bŊwÞy- ;wUQ4Töþ(wS•ČÖ!°U*ݞA–ÏĒ/ų†ķœLÕІ%IõŦYKb‘Ųˆ#7Ë}ØxéaĸŽ‚đÛŊ.5ÂU’4Đ4%™y|Vxœ}JpÉRģ[lû āß^tw3+Ąlš!ØŊl]œþėý,k]• ”ûÞ|đÐaĩŨ)Ū4ÝkĘāYI8;ĩ6UÞy7“1æ+Ü};ŧ§N‚qšäý›HûÎ;o^-I3ŽuŒ*øÕ˜•r3Ï}NÖrwģĘo`Â9ų<›ÆÉxíāÄˀV/ĀIkPvũp+•ĶuÖŋŒtŪŽãr7sŊŌޛfڒí-‹HÉhŅ$nIÞn6ãþ\WUVŊÛëZ}ĒŅįéÄ[ Ôx(3ŦÔøŲhčc4ģ°ëTóŅUĒaÂ*ˍëģ*sīÍüHa‘įhÃNäąæ—KŲęæV(sÆuÍšMČý5æūóΟÔՎĐTîmfŨu$í2vW„:jvp6ÏÛÐÍrÂŋ—zöy­ Ō99dž3Ŋ0ōt?˜™IØ{ŧŲWl@Lbė|rŊŽĸþŧfÉM ųŪ0#TUčņ,'Žōߘ;g§ŨįjaBšûõ錱R\Ëh•)”·„ ŧŲŨ„Ė~iV)39žAáûFÂnÕ-Žë‚ņį~ŒÆïûÄcôX1aHaþóÜĢÆU)3-ĖÍĸĪŌëwÞ­VčjEôž*ÕPīäóØš jˆ\ë2ĸFeĮZĨ~i73Ŋ, ‹@ĐŠjķ`*ŲØR·ÜÂ,TîŦtF—jž'w· fÕQĨ† ĄŠœÖX†X“Ïp$õóctÁîÜÏ6`°{š€=,ÏĐ,H”=?Ï ĩ {—›­~5•*ՏTÏóDgØĒԈ.ž)`īĘ}ØšĘĸÂZ_˜ŠVøÝĖÉZzÓOĐŠðÎ;ïüÁĮŠĐ —ŅúhūĖđVÜ?ÁpŦŌs?yŽŅŠrüWėÔðįūÏN-Våu]NžąŠeeӝŒ˜œėŠ+<,ëT@“Ÿ0æąAsĒ/î@/[—įI@íjðû~ht§ēĪ/ML#ŠØNē“1Œęyö öŲ#@c ls3ú„+šÎžiž”ē―Å.Ý9YÐy];OšÛ€8iŸÏ5 5Ģd8yŠ˜ œ=Ž4ÔûŽ™ŅcĸŽĻöwÞ€ŋ‰‘―į&:F6Ð>…‰ģ1^Đé°É“ąâú\J 2’8'+§‘ĖG~KZĄęĖęj-Ī s7ģýŨœŋA\ūNÉÆ=Œ ŪdÏl 37áķV<Ï#23­i֒Ūϒ0ÁŋŧŠŪëŠðf$dæcœc?°ŠĸMp­sjqHžóÎ;jÉ>@ϝ­™ú–]ŧ˜ĩΌM_ž†Ŋ,”bÅxX'{rdĖän#‰—Ī:%āëŽ5ĐĐóX óŨ0vr(‹îVøMq?Oe:ýė3i5_˜Ō/}QĘÜÍÍL;0!`ĻL6‚ŸSó[|~š­+$dMÃĢs<‚›kx[ĒþÍp‹NÐæXo|ÃH†Qhü&'°đó.iÂ;ïžóWŧÅ2yŽ+HĻûōNbesÕÎĖüÕuŽōeþëâuĸŠm}iIéŧ)gNQîïũ' ͐.UVaę É:ƒŧĐô՗u@-JŸÏ§”BĄ3ķgĐ$ū>ą&…͝Í8pĸüĖ1]WØfšđ/ĸöĒà ÕÐÏŽšø˜vŸÁė;ÕĸY€UĐ_xâ%Ĩëúüïį§ÓÜú3ËݍvN Xį°ģ43—RõšÅÞyįOęjUyN*ēŸû>[túU(oõhė#ŊڇÎå‹ÏÏ&†PĨi&ÏĘ߯o>―ŠĒ!pïsž=ZubŽĶ ąī>ŸÔļ–Ó›Āð ­óȟģÆŋYûypD°{î<ĩÖ2Zíä7œĖZ1Üc IϓŨčTRôF@ ‘~ūnãÎüÎb/Čß@۔ŧKęį”3†ũŋˆÆęĒÓ܈Í8ïP,s—þ°Žöw^ŪvEĖÁ: uEĖn=p[ …j$ ItRFZûĶĖhRRÉ 3‹RļeĨJÓD›y]ŸK™&póŽdŋÅãyĘÍÝ\čHNō‹đŅþ”fýëė˜Î“‘"ŠeácfÛÏcüúŲÎÞ@õĶđá$ÄÄÐyjg ’•…öãúZ™‰Šu­éš‘ĘÜzÉ>MR;“ÝôĖ3―„eë~ŠÜ9Ĩðn”Č‚čŅ`ýÎ;ïü―†0ĸßu}zÝ;ģÍ 'wSŪquWXNö}ß> ÜÔū·úČhïýdÁāæĪÝ?ŽÂĀÉ=!ŠßrŲųŽ=đÎðļŸ‡ė ™Ŧƒ°ß`Äés§EØHzĄOĮnUE™rï~ÃæŒ.:ûæqsˆýÓęxÔûļ{ŧÎĶ,}pģûnˆÉëšwšLhúâÞ ãZКîÞ)ÓĐwf’"HtŲûŋ1·nŸ$Þyį?x,֌ją Š}o·ædKߖÚɷ߃4‡‡–Ôu-Uw/nŧsŅ(Y•īFpáįįqøI˜Öī=žÃž&nÖTíJĻėĮÆHČhģ§V„Ró!}ōČGD1 5ŠĒÁÜ~Ķį~’~tšð)H‡@ô!ÞŧvŌø‰ ūĢŦe$õŦ9Ũ>WinĻ?ûd•E&J7 ïžóÎßãjsčTĢđŸ<$̓fŠ$mˆ…Ņ„w-ØŲjōgÍ֜JuMÂZ ßĨģŽĮþŊ\—GÄŋJžû-­{îyj?ŧ2Ŋĸ>ÅŠ*ũeæûyR)Ž}Öž­; 0# îq]WUĐd}`WĨq=ĖQ[ïŧĄĐņĖĶņ'=þMÖQÖ/_QgCwÕPîūۘ1r‚Ė'jgÞŪ8ĩ“*‘æ?i}Âh°y‡f@îŠüFVoĮ―)Kjcņ•›:iāýsÓ|Úsį1æžŦûzōėd0Y9Đ:#™€ði[pI$Fœ€ņį°öwÞžZÁÍŦŧķŦsÛÏ6Úõß5e6æF°2itsĢíüĒäéYgÕĐ4óöÚÞÂŌsķÃČso3N•øĖāquŽAyXeíž‹äįŋπēIf9óŊŠŽĪú›'íšâä*Âiþ4óëæ€Æ| °)ŨōvCôbNwĮwSĶ1JjÅ7†Q,þ›xžÍ–@ļMkCũ<Í&ÞLŌīH<σū5ÁÞm Ą·ðæwþ$@īLķ&LäaMũâHBW,FØý,ƒģËžI[Ðé+Ē ŽĸöAĨ2ģk]Rîį^+Ūus„ïYį@5ÛÐ!„kR@Ė9’ēzg\U5ö-@įŲBg9džSvũŧ +–™g·―užT初6đ}KÏþ z]͎ 5ŠûōŽėÆ_ó_NYaŋ…7^SĐ;5æ§N& æœeķ}w!˟Ą&%RxįwþäąX!ģŌc‘œ —)ņžæÄ6æ>ė1ÚDüŋë3pcfã§2§·‘Ą·NĢā7#FB'l™ŧũMØhÏsOp"ÍÏ)3ëōóė h2A­Ï5ÅŅōÏ™Â…Đ 2“ī*éeÓ=Ē&vŦ—ņ/W―–šóä>{G\ÂoōËô0–čáä|_æÄ°~3šĖLāŲĮĖ|” YÕl‡đŸ{\ŨE ąbčl: ž<æ^Ō°ÃӘ{ęĖ‚üÎ;ïü9ąŨ ßŽÕ9įŠeāūo î6Ū-wŧ"TYŲÄŪÏþĻ’Ö‚Æffĩ BĖAY:Ķ KYō^WÏNB<û‘īÚ 0ŽąŅZú*$ũó28ūÆ4\ŸOMúŽ41ēY‡ģĨ*%ņsï<UƒxüũƒGg_ö!/ÅįjÞĢ,|yÔ·;îĄs‘íŲ{Ÿ'ŪEãŲ[ÔØŌžĄŒŊ4Ņ6ŲUŧ?SŧÛo ÐÝE„û+?xį?J P$ŧ-ņŲĻð-}c°ŦcĢiÖg'T @ïÔp­ˆ ?ĸŧ―ŧ %ėûW Ž“ÖŪŽt3ŌîÆV'ņósŧŲęĶI`óģYĒ܃āóÜŪøTĐ*ŦīŪČŽŪÃišõߔq^Šf~ĸüh­Ļ$0ž™­ĸ>;ËŅ!čQĒ;hužĄVól„› VîiŧY}ų†Ø‚0d–mv˜į·0>Ï 3s?'Ąr·XĢ&>™‚Ų ķïžóĄíˆÝũîčn'lÔøænÄ9ŧã§š;ĮõôÜÝ+ŽĄ$mĘ―atēR0uPÖ%•$‚ÞŲÞ*}þûdfŠHóÏiÕĩöóx8Š,=įĄ1čÕÆ sŋ:6UfėĮxĀi,į0k­Ž1žÃny&ü›öM)“™ĩŒũ6ĘēŠ* _í ŦJsƒ8ô+:l·‚XĒ›U Zƒųœ_ķwŸsÖĩĶÄw…›đϚPpũėČ/ĶzófÞyįOޘõþe47ŠzÝ#ažï'OMlÕißŨĩšĨÜéáfĖ!@›@Ļ*í @h°6ĐNžÁŲSîëZĢÁÂĄēNî_7W“ā?ûaŪģDPYû9J™ÛÉėgī‰ Sés]Fž―ĮųV§0Þ°ĶYU…FÚĖãkEDAcąöST&PKĐĖëZkÅ~tũåũģĨęĶ ÕÉAŌöū>î ÓŊâ<§ î^'đyŊØG•Ä;ïžóŨj‰1ƒ…YMÛ Q€JåŒX%QcCčîDÄ Œ7§p2GÞ?Ņ‹M{úó<ģÄuöÔ9uvuėĩŸgžĒ―Uó1ß%T€}ëÖ˚Åu:§đÚ+ŠÛ―@š‰ÝvdĨ4ķ"I7sëuĩüŨgŅ,„AŠŠčéßzâÄ]U(Ð-ģJãÐå„$Ė aKtŦ•mĸ“ŲGį„OąÍ9ÛÃMíĸŦĖ’Ü]xįwþ Ūöß$asQRĢ-ÛËs S€”›7 &~CÁÅijhŧŠ {ï<ĮšœœÜg}Ņũ Ņ|j&§ÜŽoyŠ߆]€Ĩ"zÄótïÊoVųlåĘÆč.Ûų>0TMfŪYãŊ/§ĮÂųrĩïžó7ĄÖŧüɇäu…™i@ŦđŨ1•ÆŠ:ؐ:‹ä―ŸĪđŠöģ3ĩV€ŋÞ°Ï'wVÉÂۃ[“ēčaßĒrũéÎņ0csģYЇ!Ĩ5ó+|Ū‹N€­ûŒælŒŲŊÖÎ/+% MđžģEƒ• Æéúíô2đӜ'ĸ[ü"§& VŠ. kšä71r$ŦÏëö>4\Ũzžû<éīXąï‡ÖŪŲ0îkï}ĸ'ŒÞ.ą|ŪOÎ)ü7Õ% FpŸ-ÉøorŸÚ ąÍŊ{”Īņ Va‚ki°65Hû7m BfĄ)ã~`ô+žýĻeŨZÏý”ōóßUĐĶ_eĪЏäü\Ī š1ZMŊđ n5?ĸý·ÆŊëí€žßHö9 ī†B™™‡ ELûdŨAJĢ ūũc ðÎ;ïüA]íɉTŽa,="įô†ôp@bųĩHÞũÝŲ—›UĨEĶ „Eœ―sš†`ũaÚŠŽ’žÓž]ēvĐ°š”Ąšøvy o{}›cÆX hN_ü*ČiV'—_dãėtDŽÞ ŸŸ§ÔŒ*M_ūŪkï–Á&nįš"Ūõt6­Đ€“nf47;{ÓđĖ ÁœėŒA.옠,ĢSUg?čc·R)eft;ÏQÕyÃÎBÅZöރԭd 3NŨΈuöÃþ?0Ô)öŽ=ŧð„ęž―iDēĘCÚu-ũģÝĖ'n< ę_J ŌÜTPĨ9 VbŸmN·˜N n|îGÁ ŦMåĩBšä^ķîÜđËL_-PC–ÂcįŽÕ>Åė| oÞyįo*šR6Ũg)kĘ `–5ÔdKž3ËãŊÏĘi†T:§@;đŦôM\|NU™y%;ËÕÜ ęh–' ÃÜŦĻk-ÎÖŲ% Ĩdî+\*)I›’.š5áĢåúÍc,cÞjŸ“ZÄizøō€ņŲŠÉĸ–ĶüÆũŲ•ščîÞmįh˞ó4:P•SŦãÏ(Æ3ŧn˜Âž_I'Óh§YF\ŸÏéĶ\ˆWÄČ!ÖZa#Ą›'~įwþÔŌ°ÖŠ,5"~>W<{{˜„ĖūÂû‚,|ÖÆ] `uœUPõ+)Ĩ RŊ„Ũį‚xÎQjuUxf ą ŋŪ™Uf•ŽũI5ð™Ā<é ‚' sk øpÜlĀrŅ&1Ĩ&svĸėïÅÆ>mû’ įœÞŽãœĒŲ88ZÍÆFėzž'üß|3ȍNPĻþvt?Ø@ąí“™ÕųõėMũuu(ío.Ÿ―'C]Ā>énáþēïžógCĐŌŽąëėݰ 4Āå)’Õ0Á804Þ°•§h\WļYžrō['*Ėž[hz·Î3YŪáîĨâ/ęä9yÎu]hălēoÆ50 Ŋ$+k,j1ŪģÁ_ónņBŽÕ.ƒī0A“ģUYÝ"Áɟ5óR™Ą2I|IÚŊ­ËŒVUBejj$kc~—éaŦĘáaĄoۂ‡UÉļāÉD TšÞ §åšxįwþ Øk–ēŅųW›ųH öTiūĩÏ1c,Ï:RMõKÖÁŊ#vj|ˆQ Ûŋ· ˜ųkĘ"23 OgßhĶĐÝ]áh„ũ+ĮV`l+ð9C"Oz5DŽgaï3ŋįs‚íaËā>=’rsĐz]ĩFjR!Âg-­SMđĶ9=ĒZzŽN5ƒI(3†Ee‘6ëöč |ĪĮUFŋŪĐJņÎ;ïüEˆXҚÓĒ›/'ÍÜÖfÜÏ.ÕÕ%7•yE€ĖŽ:“€ŽjÔsRYĮÜ>ݧų!XIĒĻ*dæī}Wic·ČĻ*ĢûÁ;ķ6Ėv ÐØ‰{WĶ@Ucex‹ĄáLÃ#ï}Ė­*sïØZ#{_yŽ;Ýč'{ÝĶĮōtv?ģ5†Đ xÅ2ũįyz vïþöļüÓ‰ģģRĪí|2SĐąûŨӌĶE@ÓîE’o Ã;ïüIĻ͓e9ý.“É"Ոĸód„uÄõĐJĖ\PuåâĩVž<φŽJ Ę]Y§Ã\ĖÂÎ>yrlĩŠ#НUŠ#·/ýj2U(Ý―Ãjĩ–Kę[ĸkυU,|ûÏģÐ/ũ 75đ.4wŧ5aWŽ:I˜@óĻ<ÖR‡æj‘ŨĩÔā>hÐŊLÎFėãBn27,žsÎÓåËO€fÞų“FÐHLđäōUU―…W-ū!Šïžó7đÚSóã۔Ęh O:ĶŠŠXÞíßY_\ĢY„Ë0ÂgŽJ•˓ĨôÎ4PJfn†ĸÝw­åŲ†*ãĄ'O9øEXwßN4—Œ<'Axßäyž’Øe_·ãoŒ u}›ū ņŋĸũ)dĐ,Ž3ûâ — 4Ķ:r·JUĄŠķģvš„…§j?ŧNY0b‚ū Áh‰y†čFēĢIMâ—[—ķå>‡xįwþžŪöœL•›ŽSfF‹YÁ<ģ~{Ķõā4ÎZee$îFšQužęCĸëäVkKÍyß'8Id7sóĮh˜ņĻŠŌĖÞũcfS†xžp]Wę PŌ§ë͍l2”Ï>Íu8ø­ ŋâs”ÃZļGÓÄŲiķ‡†îņė%[UÆý ņ™ÐæA5ØģspžJ'ė1Ï]˜ąwÛyßĘĢRVeFĪMŲŧē:ÞėwÞų‹\mcŒJėŦēōäÆÄĶv~KÚ]ĄčŒ>IKr6ĘC2ÆN0%7q}í°ýŋį9Dxø9‡ôö/€ ĶĢ· *‚îv?·q°L§_$"7'$aÚgéNzf‘˜ûķŽLæqz{T*æ—ØĩÕ蔍›ĸ“ĮJŠÏ"Đü†óf–đÁéf§‰iwwX",šī=E3ŌZ| 4ũûđó$ĸ Џýđ–JŒ ëZU%ČÖ+özįŋšāîŨgÕĐa*Ļ}Œ'Ļá%ËÚ(4ýú@āå$ēDáZWkĀ”™ë7âŠČŽ18˜QUámiVB3Ë*Đ/X~ö6šđyūģ_5ƒánÏTãxžŸ§‘$$@eæĻÚuVļҔըoކûÆz Ï―ED_ŽíÁ=Óąx­NŦró‰kv}ËŌiö~܌&våđŧšųęs-jÜ·oāoĘÍP% pÞyįÝjí{ƒŒ+œ–'ÃÝ/—ôÜÛčĸĩö2cGk(KQ{ŠÝΆ*3ëgđŊRMC­đ흊"IÃÞ§ēĖYUgo7›Ä[û–ãbšÂbE•ēWæX(OÎRy&­ĶīŪKUŋMš_íōÕÝļåîŅb5 1Ý·ŋHMģyŒĄV§d,R›Ânb$K56Ýy$3ŦūßdG” %·č}VĨōÃ[xóÎ;RėģS§Sĩî =eæ+âė$ļŪkRiŅĻԘwÃ]ŠY =BŌ7Ĩ;X§(Ņ8ŪiەôĩŪ†gîŠ"ļo=KĨëĸ*ŋRR­CاvD # Žĩ”8ûLTXœŊîËĖÖōÏsJÕYýÎa}ë˙ŲošđīģOG#‚@)%™Ōē1wŒc,žÂˆąHt.">ĸ}(Hó:­ @ė{qĢēÎų“…7ïžós˜ŽŠh.á7ÁÚ*‹ 4(ŋūUįė)gÜgï―'SĶģZN„_Ũuö•(Ôé |y'ØėÓŸJÕ_TĢs‰ƒæÃN4bIÍüfz-_j'Å ũsƒC WVy4Ζ@”Đý…H+åi-Y` #Š+VoÍCDÄúŽ•k2Ț iįšp}ŪęįD|Ý―ÓF~wœMWNdUIč2ąį(Ũį›\S(ŌU―þ…wÞųŦ\mûj­ØyL†Ą5ģ$pĻĖ―éĻ’ÏÞáķÖ:ÏSYcóϊ܇4šĩŠF2%š;Å9Į__―°Ę-čšŪ/Šæöėm§Õņ<ƒč]:knä@‹ueS§$9ý‡ÛÄuŨœÝđņ<ÛÍÛðķO [sšbąíž4sH' „ŊÎč’ÖĩÜ#ŸSiî,ĨPŨuôøËP—r ‹æa†*Ô6Ð{'i䛡øÎ;”Ŧ5·9&2ÁøŧÍĨ8ŪęS,·ĐV°ųÍĘ*ubƒČs:DÁũýäI 'Ņ!VōpA;‰čž.•"VŽõ<·wZ įđÏ>;žĢõÁVóö2Ät/‚u? 3ĸæČ4„I*wÆ •€Uf~2-ÂÂŧ­VßģwóķFVøãdîģKéҚ3§ÍįVðŽYxž=+öų•7R îĸJƒP*ÍŠ0h.(›‡°åĩÅņw^cnDUQ07vė! 0ŒÞŦč|üw·ÜYgJíūwœ62œēąÕ<\ŌðĒãî•ū3ÝōÍq ėý4jGf/•‚™?ũ“UZØģz7UXSņįļõžRjųeæũÏSUk-€RœbąĄK~éāIĸōĄ‰—ŧE°TUŨúHĨūÜŧxüœgy‹T·/™*ĘP•ŠŽNŊ“}ųōøúq͕ĨTAfŽŽŠÂ;ïžóđZeĄ@'ĀĖ5‚„Ęœ\‚cĨ ģĢ”JŊĘ"Ĩę&Ý’ģÝī*į„Šŧaäá#Z]kYøP0+en•[ęm [Ïs”Ē4‹$V,ˆ#m%ۑ…ē0‹hwŽVŽ,eîÜ鿐ŠĘÍWŽsJG›Œ°îH>ŸĸDÝ{ۈ!qíŧŅ‹ÍDäÎų­OuþŽô‰ÆYĐĩGíb–; ũ ·Īėøq{&0ĄNËËHõÜØj :ØÜõ­{`ļČSąÜú՘Õ*+Đ7ÉļĒóOvVÎÞ'φčü ŋĖ ēVĻÕ;—ų™·Áú%ôōÎßvœ.NgÉ`æĖK+X5Ý ĐēÞ$uRđ WPŅĪĘ*o§ėxúė+žnxĪYI{?*t5ä0jUĀ™SŽV ”Ī&y ts#TY{'<@ËŨ7ėQeîÕhé=@IĘ͔zō€t7ÅjNF€Ėčn­9KvÞdŊwÞų› ĢŦ nUâėŒœ@ĩŪkrēWtŊøóH03Ģíe3ŌĻŠ­ęîŲŽÞm‰Ĩ>„ĐÃĶÆ4ĄÁŽ”4ņäĨoä‚đUJl•D sũŠĶ‰ŌGį tĩâÝŲ1a…NÛ"W‹jģäÐÄûėðčĩzęÓÅéU#°]™…ĪŧGÛĪōp°™kƜÕÕdÖRUįg›ÛoØ#;éœ??ĸC{@~[sdæ_§ƒÜ! ïžóΟ„Ú*eeúDÃĻŪļĖ-‡|ž#ZrãƔÂý{ÔMŦn\KUU07oœ@_ ũĐØBÛ†ē0oÃîsŠĶĩaÝëëYhÃØ T–™ƒļŸ§q“ēj^ BUõŞU'Ïĩ.v―9„č}öþßÏdãVęäq SIPę„}Î>_‚›îėüŲFj·ęW&đ|í―ÐXUģðÍ#æŽŨuõ;M1ūUzÅ^ïžóWđZĐ:ÖPUŠėÄÂ3ëŠGĻPUTiZrAÕIW\fÖüŽV ŅXVÄð―ČIū5ûŧ‡ą4ŽýwÍ#€iä/JŌėđ[ 5°LXĄ™ âîÏnķŨBŋ @ÝũwŸo‹XŨ―D8›pØ4^k=ϓ™sņóėŽėĖYdæ@*‰ģÓ`ŨuwŌÂĶÛfÞîû60Vėóœsl ĸäwywÞyį?™Ð™2Y‚ųŨ‹Ĩ*› ðÓĀį&iķTsW)UF€ÚgÜh:Ąk]yŠŊīņãJgÐîåq]Ÿéaô0˜ÚZs'uöoŋ9á jF_|ÎqóÎū9•'ūÅ6ÖĖoeóļÖÕ'iPËýŒ ÁHöÝËĖĻ4ƒįyū-8fõëģ ÝõrŸ4įįsí&s;ÉėšÜk-ĢAČJM^ÚųŠŋéæ‚ÎIâwÞųƒ\­™NáĢFĪŦ*Ū+&Ŋ@†+Ï·4ĖČýëëBï';W…Š’Ę­đŠPĮAUšÓ8ÞV€ēēzõģ:‰*‚æD)3IBȔ$:ŸÓ{gî&ˆįČËá0c‡ĀpL_™‡›íýĻŋéîęŨpŽ”Ä͌­Ty<<š-m•UĘÜÃ{T+X0·ý<Š"âū7Isũso‚ øýÔЀ*)hĸ§žį~™Æą&ükŌĶ<ē_G™›+eF#ņįwÞ­p7§ŸŠ&Öh  ÄĐTÕ7eņđaąÜã. * î!åÄ0ųs?—Et‚xĶ î viÆ(Ïū>Ë`ũî;.ŸŽņ_œ<Ü&ŽŦA Y#ĩÂĮ?QæfįäHŧ–[ílt$Įœ%ģp6äŸĘÏõŲ•â4JDŊĒcûũĖ&šQ†Š” ōdõðßįƒŌÎ. ëЙhÜ/åӇf ŠtžcNw{ž‡ Ļ“„ĖmÂmßyįŋĩӘ›UgâW|ÞÍ7eĒô%FũÆ‚&M<Â!äĮ/?cEßģÝNšį|° Ûëũ~‹ÎBüÉÛZ’åÝÉčîĄo*˜O™3mŸė$­ĨÖÚėÝķę€qEî 1z;>gOā ”įĻk];Ó þÅ i+(@Â2g8ĀĘÓ >ī@Âú.'Kåfþí°/îKtpģĩÝíėæ"éTåN–tšÚþsóÎ;Ŋ1·JûUEįŒ„Ë"šë,ũΔĐĒD†:5ęQIgŪeӞKX’:ËUÞúfŅTĒuŽ—‘f‚N~Ó sžaąŽŸMacUAZW8Ō8Ŧ™C,t lĉ”ĨĖH·ŧëjV,(Aøšā@7ŅÅó<"ëœ!LĖ|*|âōXæ§ÜĶ3áW5HŽûĪ  ͙ķĮXŪĂĖՈþÎ;ïüEąWĻÔī(ö‡ß[YĪY° NaD"éN”Æũ―u>m~%đŸFhT U™īo,ĀÔ3–Úâaų’f$ZQBāė“uŠ‹f•˜ŧÕsïsē/ÏÂhž,wvÚKĖM'Å͉]}ŦmsĢDã―Ÿį<Ž7ÂDÁpö6q­5}6R}ŪŽo DÛN% NccĨ›ÁPUÓĒV%ô­ÍíôÅ0c›‰Ï~܈ŠŪ –›ãwÞų‹P;BŽFŠ”`4_ËÝÏģŦĄ%™"l]+Sā·ÐûđŸA―1eąUf Öāõųp(UĘ yÎÞŧ( rįԗĨpę€ōkĐĘkwfŧ[#āōļ>Ë=öó•— ‚kÝũ3ŽÞŦm`$í7ïÆ`ĸ}þ3:šPĶ[•2 ĪÓîggÍÐŒŠ™wæėS'A@ĘTUfŧ{+ŨõumTåZ1đīđ[ĮëT‡DÆÞŧJąb$ūĨ Ä;ïžóGuĩ•5’ŽÆ\)Ëh>m]ßã,Ĩ›ûreeĶ„fQ7IMŠJū ë˜ė=õ3ŠšZÜĐi_wŲIôĸq7Ļf$JĢ(ð°9­ZąŽ‘‘Ēŧe‘•ÃNĖĮüĩb·ÝĀiïû.Hƒþ!ÖŠ~Ž/}‘mKûÍ)šŧu{îƒéŨĐa@ēTõ[Œ–'6 YIōš.ĩĘb~Y•Šī<ÂlþĪ0_ĐŲDßūåäïžów“―T%é7ē`jÃãœsę˜YUC`eW$<{Ÿk]+VMķ,ŋ§íSûčæãsmčDîƒâ,žĢ37eŅø­VŽ"đgóŽÎ?ûŒųĩ•XwUÅĨ.@;M_ëZøũ‹ĩâZ‚4îųtôZXu~„é^lˆŸû'+ÝÍĖ æýŊYâ=,6ČÎ/üęŌ§X·ĸ\đϙ8ˆóe{ÓP$$íEÚwÞųŦåäUÕåēũ~ēZ ~îĸ‘æfÜ;v­OMí‹ÓŸį‘ī"$í=đV.čŲŅV\™Đ‚…ũ1ŅåÞ^ÛjÔĀĘsüŠS‰sË}žˆĄ‰ĪŠ! æËĨo§dtŨŅØŧíY뚮†ŅŌvĘBĢŽJSˆ+Ôóĸ­s… #UĐáj•YĐ^ũˆú.ķ­ßÚF“nÏÏÝ[°QŽĘ'tA( šÐ,s/%įŲÍÉĖRŨ†ŸL‚FkPþ–Ë^ký<a"c UM`‚zlöõ*ũ°vÐ-ĖöÉi‘ Ą 08hšpzU~Đ P%Ĩ„oĘmfE﷟ÍĖ͙gVbŽÏgp6Kn–y&Čó<#ɘ\4ĢÅŽŌ…Éųþd“ã;ïž‚[œT4ēwØoė–$âwÓŽ|ÎÉöPDbĩƋ€õu§„k]%ä)ûký&Fe™ŅIVXĢįį”6Îķwy }ŅqÝ}ĩxN^Ũ2â”@öãąTîķÜ Pt_srE§Ņ:7ŨZķŸsN*EōŲ[Ð 7§Rûïú@ĻÝâŠ―ŸÎåZß7•%};M6ųrIųÍØõ―Ÿ.r4ģœx°s2u>ŸUĨ’ˆwÞyįFƒWēĸ辟G(3BjS€ÁqjW•ˆ1MIĩĖÜÏ q}.HÕ+Š·ųuZžsōĄhĖ,ĢëÚį[ŊKáŨ·jõožcÍÖš”õÜw*#âT5Č'Ąė+éĀXŅŠĖĻŊ ŠŌ4ÃhfĖ”yÐã“9‰ÖI’ãāȓSŠ~ĸïQ‰ƒ­Y Žˆm„%=û™uzŅWŽÉdðÎMûô­ÍŪLų~ēŠð"í;ïüIĻåŲ§JĪ•ŠŨ―Ŧ€§QÁžqAtÚuM‘Œ Ī›fCSƒ(Āß―UåÆho•Tn~­ëŲÏ9ÛÝT<)'§NĄĨSÕpyúvN ģĖÜÍēŧsHģ&FÏģ!™ÅÉŽBUǘ'Û"@G ĻL+UU„§ŠÆå•YFÐļO?·Q$óŲTu?yž9õ#ö}›yŽH•‡E†dpĮŨk'ĢõģÉÂ&[rw+U?›Ė žóÎ;°œœæÎŸûGÅ+.ˆįyĻęÕʓÐmš%ŅįL ŸÏeŠo#JŠ#ȖÝ―sZÅú&‹w"cš{kķTÔįŋU§—Ô*)Uf8Qß^Þ빔%}ŧjvįrđ1–Ļ*šĨN/•óÝHŠéBß AwÝðXŨ #öyĶ莠Ö~Ĩ9gÅn*císhF’‰į9pũ0eē„’Ņrï§ģAœ Į ›Šģ_WsĘUj*ÃéūŸCžóÎ;îXLc„ýï?swvðŦyX+–Ī/?ũ&Hc4EÐŅ0VjwŲdĮėsŠÂ.k"šW°aHāÕĘÖŪ)ÓgÅčŠŦtÎÎĖôŪĸ>; ‚Ü­ÍŠX°2ëï:wĢ›@Ð~ƒd|ĨĒyŊØhw=g_!Ó~vgË~JĘSá)3KRüwíŸg"ÄÐIáe DĄÜy-ŋïgV~'ZÔeΉ.cNs#Z?‹uųėË#Ø'i YB:eaxįwþāV›YM[•ûˇôo•V5IJMÞÁįœ'XY$,|ô[ ,LÍÓN<ë9Ó[ãF%a]WB ÝãšŪaK{ *ŦÐ/8N_Þô‚ŠŠÆ<‰Ōėā'ÏĐûr€ÓÉĻDHþíÔQ'ÞšŽNĨŊøŠĸvŲīSFšŅzáĨV„ÃæïšÖ3o$fVõ-4û- ŧ“#°…ÛpÉ;á!;iauDū†wÞųŧnąNYúŽÞFÝhn1SĨĮÜÂWVUâڊÔŅ\•=ū;ČĀ vž‡†ëšÝįQņģÚîõ…ÎþA|yîočA„g?ƚحŽŋq‹ģ@Aæ.M qö)ÁÃdՔãV Pęã,Sģ˜ÍŒIėėĮŒcrŦL'IõŨî᧚ĩXU+‚nOîĘcfĘÚÏŪŠųSŒfv< ęĸŒvß7ĪގŅ4…<ĒŊyįwþÐszhŒˆŲs'd`bĪŲPĸ}ŪSĩM”WŽŽDĩþŸĖsĖÂÄĘRA öÅ7kvBŨšĪÚûŒk@j“ë—„R]keŋÔ)Ję2›OĮ†Ýæq]‘YĻ2 sBÅ„ÍóÏÖđŸãîë{B%°û"O3=?ĸŧĢcpJÕN3w·ĸŋ4[Ŧ”‹ŒĻŠHN§YĐžŊVŋÔ|ý<đũ5V‰*]WėÃĖ$Þyįŋ§@ A;úWfN57ÏsïóôðF=đۄÕķ3jU}ƒh:ߠІDá-ˉ_ô\ë[t8Ð3…7î>N3‚îĶʎ\øæČLüÍj%Öô0‚8]væa$Ī1û9―Wyø7Yą―ē7Vïģ ČÍ$MõŨ<35Ëŧ™Ņ0.•û>ĸCî$ĘÍÝģįÜŧ?iĘÔÖmÛ2•ëF@ĢŊŠūČä„(J–.5`ÐÐĄąĢī,ÍÆ)æÐËï6kI‰ž2ĸ‘Āž4d°—vK4ōļÛēi&Ü⍊ŧ͒,Xøī@fSO“‘ĶdT”‹?ˊF.Ō8ėš,á%ÜŒá–ī°M$x8VærĖĶčĮ›~Šû`ZŽf’(XÜ3Ԉ…C[Æ  )ØÕR,#―·še–YÛb$B™]srQ!RJpŅíÎŦ5tÄȆ ĢÁÆÉS'æ|ųåĨ.ņllē_E8FŧĀHEo‘ĻJ•ŠĢFųiÁü=;ķ›N ĀF™=#}ĢŊŊ_ĸýý\ ~˜+ŲXLĨŽîü‹l˜„,0QQðå%0§āxžRKՃŲŲbJÝÓ°ĐöP€cZˆĶĐeücŠķŽ@ÝĶ_$œãOPÍ_Z† -g4eZ@ėTĩĢ_̊‹n;IÁū.n#{&a LŦ34LÄ―ĀēiÄ$ęĀ{Ļ|Jc ý,ģĖēâ˜ėĨPRIYvĻĩųˆ?[čÎ/ĻYĸįÅK{öėuåōe[ŦVí€Ā …CøBuĢęååįÛ―%uŸÔۊ•žĐ•——‡§É/ģäā;6iÞŌåråfį},ĐÅÕîŠO5u—Õ9]+"ĒúsÏĢĀû #ī­S/Šnd―‚'ųŠOðĪf툆ŅMüüh˜ÂŪÎ2xSxšÚ°B“ņ*shŌœnŒų€0+KJ&Žn”ißŧ†/DY\=⧘ဠ†Đ:fƒ;ÂŨ&RĀ1“ÄÆXŧŠ8j›OŌD~M?đŽ"Ieó1u”eĻôļÅÚâh–Y^­„ LE}šĮYËw?a_+ϝŨģũUŦV]ēxQ|Ütä_Ī?Ļôlåļ3^lð49{öô§ÓĶ]š˜Ô―GŊ§~4SŪ_ÏĘĘ1xĢ]šu5š|HHfæã•Ë—-]ēXÓcĩëŒZņ™ĐÓ?ŒŠXGŽŽ›6å™gžýxÚô°ðęôÜŲ3ßÎųæíū}›6mŽ›™žþpęäÉGŽü)1O­ĩ5ŧTkiMƒÄ_U‘ž^S'i âCCÝõ“š\TÚÃSĮv‡_kŨvōΉū>ÎÏâgĶ$'"hÞĒåŪ;OžHôsđ‚ƒzŋņVŌųs›6n ŊþÏ?—DáEclná–ō§I!0ÅĖŦĄX/ēķ@'šM?SVk“WmO< p:|đq6ýė^âÝCKãëƒQöĖšīÅõ$a-~ÉôčÄ[Ģ †ÍFYAĄ[ĒWĄGX%Ō‹$B@ėMÂēĘ=XfYą4Ŧ0Ũøv#š@-ôņõóũāDnnžČYÉæđ?›5k~ýúõ/>Ÿ1ãģOϞ=K·\ųōU*WÞņûï_ÄĮ͝óMff& ŊXą"`·nÝC*”ũũóŦV-ÜĢ$4õļLR!22 Î7mŌĪaÃFļąÏ=ũ|ZZWUýđį@įŌĨŋ’Ar\EâĀóuؘ cBÓ_GCҜ` ĢŪĘÚČ.Ii|ޚĄå1˜ėä@1íKŌ2`-ðU-1ÕļąYfYņ3ËŦ%XéŅâ~oÂģÄ7ÉuRÏ+ ŠeĪßgŦT!1KŸ–u·ÞĮĐš\’ŅEdT|ääæB(Ē·xŧRÚŦĢý.]üįDâÉÛ·nēŠI‰ĩaŽŦʄŌč[[.ŪXūėÄÉŦ—/Ïzœ9tøˆąãÆ·íÐá݁úžŲ{øˆØn=zķmßaøŧƒØÏušø,›NÚGųÞX@ýV-Ĩõ—xŒõņv’`ĘyĶų6_ ô@™iïĻ)en›ö`įĘÅųeB“‹B˜·”xāïja…S—~ĒņY)V0rÚ$ßķĀĶåsÔþō!1RÝj[ĄŅTÔÏÄ2Ë,+†"Šē5D“ņÄpČ`(„‚ĮŽg@ßūý:vމnÜĪcLLhhÅëÉŨ+UŠÝ0:**ę…^ļuón,ČŦQŧvDFŅJ—. <|ˆ3?þáûđ?Ė›ŧsûoĐĸþkš„2ļxøðÁý{ũ@Þū―{gÏúbݚÕΟ ŋy3eÜčQGŽĐQ‡`…;??núīïūÃĖÍZķjÔīéČŅãļņ:ņK…t0U―\t;į4 Cņ+5øx Jē2oÔK=ô6ōēvĄĄĶĘ:Dī‘Á* Ū)Ļ‘UÆ"NÍr35u„ŒÅ Æ4ė-…alšiņŪi'É,äâ‚hLŽÃf™eÅÐŽj1Ýēũ˜„xaęĒŋę·rÅRō †ķjÍ:†fffulũÚôĐ-HX4oþOœ!íé‡ïŋŧvíĘŸp<7nýíöíÛ999W.]ZšäNnųm{nn.œy§Ï›xvęę ō\~.žæŋýföW_;wÞžīÔīŌeJ5ë‹R%K{ũîÝōåËïÜđŽoÝūãáƒ‡Ė™’’ēgįÎ~vëÞ=)éÜŋwn;y`ũhÉŊ S{=ĻĶKTDĸ“Áá5ĨīļÞzif;ËĢ+Š?k\Nuâ=FíAĢ(š+žrãy‰š&"!ƒĪSŧĒĨ]Mā"—ËŪ _üÉË$ÐfVaXŧ@/Y.žø6žąĖ2+€āņ([}œjëaĮú ŪĄę‰‘ƒ—‰:vĄz… PÝn/ÁŧO˜ÖđâÍąŊ歘Pļ„ ĩõ] ÐM‚ÁNČÅōLĒĪ֞ŧŠ& ŌÝO8@ŧK°Ŋ!Wӌ֘NåņVA/ÕēAe0 AķKĪ΁#i@›iüĐMÖ­Ųã fŨč)gđZ&áuõRU―Ė4gt˜F82 +j>˜ÎV(A.ã5ӐœóŠļ`ÂöBŲ+d ՟,â 8še–YØbDgŒÎĉ…1øoœ„YY™x|āŌä-1œrĻaŌEI\uįđ§>ĸi~ÃWēŊ0|Xãį2ģÝ#.ĪĻ―2lDôd 9öpėQg“Á Lųe—Î]ĘDī ą§O^ŌÚÖdYQðŠŒQðå2Üāš}åeîOôšLOIU:dnĄŠTˆT žĩÆgÕ―VÏUÝU.°>U‘.-KcB­Jģk؁/kŦëjš,ĻoŦųžÄP“Wkš—ó ęŋp―xöģĖ2ËŦÅëTGĖôŠ֓/2Ž6ŧŠâÃvčØiō”Đ“>šŌķ}GАÁ2BāårAĒS%W8mŨT Eso`ƒ§Đþ,Ý―ī8Õ(ā-$QОl!8†Ū*.ã€uĶ)/J9˜fó25ÔæĀô­^ތýÃB ·T˜Ë5 sEv–ø­ŅņÂ6F†|%øí;N1&Äü#·)Zqž5°KĖŽ,­ēßU·ÚĪ{Ūiē€ŠúĒ7ģ9]Eé,N˜˜[S-Ģ&Ū –Águ†KģĖ2+€ā0i^ð ĘhV“iy ĀÔäĐSüéįqïM3nüÜy?F֍ĖÏéðiņq%@ Ûôî|w^nxB―0'7į–čjn~><$t[ģF­]{ũGÕ‘?,ŠŊ_UÕgL:ŠGå­".œ7ÍĘøÕĻWnpA@˜Í/ÍËwCGí'_ÆgŠ–+xŲ™Ë͑"]›6\.íŧ#\ī‰ôļšZ9ĶyÚ’Ærhęna+Lͅ“R46ЄâúÂÜT—i34†1Üiž~fŅ W+kÕ5įĘŽtŊâh–YĻ…AðLãŠNĩô=;;Ŧ[Ï^C† ŋ›–62vøĻØ æĢ&ÓĐsLĸAƒ#Ģę?žjz{ĩnÓöĢ)ÓGŒõBÍZųî—+ }ûŽï8iZÜgŊūÚĶT™ŌaaaM›^722vô˜qï―ĸ(33ĪBhŋƒââŋˆéÜ5 0˜ĀÄfËČøîČQbštŽé2í“ÏÞxã­°°ę2í“:A=ˆXŪ\ų·ßé;-.~øˆ‘Tēåäå M3yęôjÕÂzŋõöoũuđü"jŨ7áÃĐÓãÐĐÓ切–*6éÁ%9Az+Ļ:Hį…čŦ]u9ŊW….Ā“ĶāAƒ$|Vš†‡-ærújF‹Áðm•Úð. öåå"áŧÍjÄP<Í2Ŧ0W$[―ėFVT=Õ|ååWðpį|={ŲĒÅÁ%‚pņð"Įžũ>T‚~2}Zýú Æŋ7áĀþý­_}ĩoĸĢb‡_ž4óŦŲŠfë0`āėŊf%_KŽŽWYÃÃŦįåæUzĶōóao“ûŅäŅHDqąM›vũÓîčũvėÐ!“'~āvįG7jr`ïcņþâã>6dHÃhŠ|›üšnݔɓþĶkW??ŋđßΙĸé―{ũští†ęcėšö@ڀäĒiEþTQG§ÔŨŠģIÐAzŦ#Y@äZcxūÚȉŦŦM å<;uŒÄ}eˆ]ã„{1‰&0ŒSĶxĖĶÞąŽ4t†ÔÅ1|`™eV^-leO)Ā!YŦ@ā”áúáâ!>>xĶՍi-OķlÚt))éĨčFŒ,[ķė†M[™M­Ûī™ðÁÄŠÕŠ!/ ĖJ–,ąr ·Û2ŌÓedē0ŪqįÎ1L Ęý]~x“ĒÅeËČH?tð@ ;{æLâņŋaé”ĐÓ*VzÖĀņÄßĮ“Sn&''CŊō!åŊ]―ÂÉöĸuė0éå˖įŠfÎú’ æz?~L\$?7ÎęĶĄš6:‡›Ō˖ÛÁåųÓÄmEÂFi‰ÉJ&’:ī€ÐÞkĶ™· 4kæ–TÎ2˜™9dŽ6°(\öĩāō!dåd+Ð-ģŽšÕðF%Žk†Đ“‡ßėĖė+WŊīhÕŠMÛ6öĸļW™ēåØÄ—þ1*RĢūĪĻ ÝÔarsē]~~ũîÝýôģϚ4m:vĖĻ))ŦŨþ*%ŋŠ fzŒģ"ę_üëÖ-K/âՀ  +—/ąÄœp r™RS:!ņP-?3xFFĸ>ûlîĮåōet~~ūLŦÛýøŽėååį‚ˏ'MLJJÂ―e"Þĩ#îĨĨfefzÉÔ:ģ˜4h„§FvĮĻ‚ÁYøĐb4ZEĮ‹[Zíöh.€g§ÝÉwƒ·–Ĩ1%+rÞdā2ÔĐlՔŒ"SSį.`°·ÕœžØše–4ļîųÛE†Uŧuųx4įßŨÏwóĶk 48Ēn$ötrDÃw3e­ÄCϞ>Õšõką#G%%' ûóO?đÝBŪFwęCŋE é ‡ž;Žq“Ķ;ķĸŽ(mŨŪÝ-„Ą*TčŨįMi‘°ŒŌ[TÉ―US–y|1mg‹A―Sĸ ŲĮŒŊZxxÓfÍēŲ―k‘\ðLÍ0›]„öïÛŨŽy‹Áï;üįŸaáagNŸAÚfņ’e[7o"dQä1jđðP€čãPœXlĸū―ˆl‘3pųŌĨüžü‹ÎïÞĩ“(%’ûéÔĐŋŪ[ƒ’wŨî=lš·°rÅō‰‰l—Ą þýÜoÜŠÖŪZq`ĸūšĩkŨĻY:4i"ý^kÛ.šQãīīTžĘMã]TÁöþą‡Ą*KėÝģûė™Ó<‰gįdïÛûođ|åōØŅ̈° †d8ĮįΜNøKštéxæ }é/‹P ėŌ­[åĘUŌŌîÃ›Æ2ĐĐ8īä_rģ*c$ `(xįÆĸïIãĒJįĮ' YžXӒaš-VĜ8ÂÚ=·ÐHŒÎ6ýŒ>ƒ‘ƒāŪ4_íŋ f°YfYą3ËŦe?G €,ĪŸýĸ")Ād>x ņøąÐ $$ü{ûĶŋ_ĀǟüøC``€ËŨ`•+—c‡­XąnéÍɧðũDâßÁÁÁũïßËÉÎa6ĻĮ°7{ũŽTéŲîûlÞ°aũÎ!ĄĄ9YY0 xŨļ„ééũĮމčįōC Žgũ.x›AĸÞūÝĢkv­XåāÞ='ĸ*]Ķ,ąāėĖ,öčNœHėÓ9Ā?€ē H Ŋg͜ąhaod ô™Ýb:’ü`BĨDφÐJađGčI ú­ÞúiHm…$ۚĘc‡œ—*r •ĮØU‘[ĢĒ ņ›æu=UO™“žŅMĄģR›dۜbŦĩĖ2+€ õþ”Į‚5“…`€Āq@` čžsû6ĮAÁ`ČEÞŽŋŋ*ˆ?• 4_)™MK―ß~~.Ü8čEÜ6?/ÄĀPÄķ―‘ÝĒƒGKr ģâ#Þļž đ`%kU6á` ã4t L°KÛpõ7yŊ·ÍiËÉÎÍÎNa·ŠžéĢĐ]ŧõ0ä‚ÏĸáRO/… ÂŨ%Ð#RĶÉ# Y–uĄļn4Đv ņPĢŌ âMÐĘŌ&0Ę[eā`ŠôĒGa.žē*č0VHËĖžEpïåŒ%J–č?dČØũÞGRĮKe҇oŦ9`^ųnÖ0BčĪmSd͙Y<}ŪY[šËeŠë,|Ũ―&ö-*ãâþÛīæ—–YfY1D-FĒ’·F0ŅN„đhĮô<čwĘfũėÛ{čpÂĒ%=Þx ·Ve`ÁÎĢp€ŅŸOPå`Ī%ļ;Ŋ@CķÐÐgƎŨđsgœG!Žt2^F™[Pρ=Œ‹ČÍÎa}‡ŊęoŲ8Ž;/(Ŧ"8€sã5r#\/\0 Ä2ŊÛÍ øâbkũM‘ąG5h8`ž]Ý?j|íšÞ‹*’]*ģ íÉE#4LþŲ;ïôs8xUÛ'*‚ķqs €ļz˒xŦ) PX\WŅu”+ÔūčÎĸĒ(vĶqHV­ę&@x>ū48oģĖ2ˊ!jĨ6ķ‚ ĄYÍ(0RĨjUŠđŽ=J’ÖÜđó† Ĩ 9ĸYŲŲÔ­WGs éïïŨāĨčĀ€ RĨJ5mŲŌnӝwÅ8Ðb>_É%Đ. ­ô,„ .Õ šQíČššoVTéŲĘ-_i]ĄB(ÃoóČsų3•*―ذĄŊ˟+ĄoÃFMčóHč4((ļQģĶĩ"ę0U×6 Ŧž—ųY―Q“\|é2eę֋z)šq•jáxŽ>ūNĪp5iÖžUŦ:‘‘P’ŊrokÔŽÅā   ņR §ŲÍĢĢIóV~~þ\ ĐšyųyÄ6HJŦ\ĨŠĐS(Qšä‹ ĢIo(]ķ\ý@ðü‚ü°ęÕ4Œ†þÆŧ%ÎĀõð@аĐeĨs‡ÓRõēĖēbŠZí؈+)^$LÁ•ó  lē?17ŽĸuLė°þïôĄúkČÐaþĨK•™Ÿ°p؊•‹–,ĸóÂråËUĐ6û›9ŸÍ˜đjíŊ‹~YōcBBđō南,ĀĩÛ{ôzcóķí ‹~ųíũĻ|å忯ÅþÏ?• A§åƒ‰“ZļČČÞĪzbėøũŨoڒ°pņÆÍ[:@Ö`éē‘‘2E7nžtŲJę&ŠU ß°e낅 WŪųuæŽŲKDFEÍĸ)aÄĻŅŧþØ7jĖØ?/ŒûlFPpÉŋ€/g=uú§_Ïų.nÆįeJ—íÖŦũ/˖0i2ßÝ{ôZēlyÕ*ÕļTxLXČ·íØõZŧöđYŲ 71WōëÆÍLú˜8F―Ļú_Íþf܄ 6o8q21kîeÍŊģlÕÚZĩ"pcņō=š{@HYyœw*Ÿ/)°D-ģŽ8Ē–^ó.‰JvéAĀ/iVÚŨFÂŦ"ãQúĨ‹Ž?N-ƒŊŋë~ýŧũčņõlzæ~Lâ„'([Ū\›ví~ûm[bâß_ï4`ā`ęLtĒV͈øŸãZRŅ€L,•GýLŸ2yÁónßū…ŧJüã3čSĢÆ |ÏT%,ÍÁĄC;węĀ*ÍZīdŅzõęuęԉŦ ŊnwˆĶ˜EΆ­9;y Q}†°@k™eÅē0W‘JČRiĀ>ūHbë+°ÂžJÜķdÉRuęāŊ]XÉy°HÄvՊļ{”Ĩjũi­H€’C<8&`™MeLĩ+Š]’Þïįšuû&zĩ ĢĩkßþîÝŧ§OôŌiĐĶRŦHÅīþØ―kݚĩˆ-Ž^ĩĒråĘ]ŧũŽXņ™åKS• ïļxŪōþý‹.ĒiđC[9ÜpŸĐƒŽþ“âāöí;īlՊrĩ”ë)T eggSC\šlY4vÞŋßūcĮ hBff>æĶa7^6|äŪđ}Qf°›\.S-\ļð\Ōy_—‹[{øð>›b%J”"#BóɞŲžiuÉ{)*r?qÛ―1‡ÖžÉf“-ģĖēbV˜K^­6—õŌ ­@AÚkkó.Éý‚)õ_ŽÏ^{ï·úDDD,^īðæÍ”;wRÁÜÁýû ”ŦĒŲZŌąZĩjõĢāE2Ō…BlåA›yę֍ŽŲįūœžzųōĸÝī~ýœïæōŪíŋĸ~ëÆ ―fēd`üųsgW­XČ^.'1„ôôôþ"eðÏ?—‰6ÜI―S!ĪÂúukRnÜ­š–z·ZĩŠrGŌÓĖIŅëþþÛ6Īpũę•+ïĶþËÍ;~ėåWZóÞ-76F'ĶKfffōĩŦxôZf’ä€ŧļwũ.ŲO%`―îFĘõŠÏTšïåÅĶB—Uēē Į IIKR‚Œ•*UēßĀÁ„ķ·oÛĒIn|ņlÉ^ŧÅ0€`™eVAŧ%JY4į~ŠŅÍEęū 9‚YØā!CŲ‹ŒŽ—°ā“iSxÄ^0ÞõääŲsælÜķmã–míÛŋîÎËÏ#ÕtÝæÍėŅ:aõĘZ§kCšāÐūý?þðC˖­vėŲ;zėļƒnŲļž#ęuÂĩ?öėÎĘĘr`NÉ?]đléîÝŧ énŲöûú-ÂJbĶgNŸ>qâo@vüčŅ įÏ?JOŸûÍŨh$.]ąŠíĐĩë7ÕŠ][|Fé†āā€ßœÜœÝ;wâÆÂý];·ûûÞMMÛĸĮŽsóƍë)Éį“’ðÐSRۃE§ü8TpŌȆëŨŪ};įky—Ŋ\―qËo+WŊ%iÁ„)ļYĐ#sŧŨŪ^˜˜øáÄIë7oÞ°iKï7ÞŠZ-lâĪÉÃccÉ^(ŌĒ1)’à.ŋÅŅ,ģĖQ6ÉÓ>vÆáŅjïnß?de ĶĶņKãØÔī;ˆ…ĢøEäīkĖë/ŋō*^^â_Įþþë8M"’Î͛ûmÍZûũîA,ļTÉÖ-šggeúļ|gÄ}ēkĮïäHĨ$'Úŋ°úļ™īĀ}įÎ^ōöņÆĀEééb‡nÕę•ðįŸŋuóÆáC‡čæ<ß7nVŲø;ĐĐĶĢĖž]ŧÚ―ú2ÍHĖ:väð™3§€lëVÍø2pĻæ ”§Ķ ;wŸøīīžąûŽ_ģæČáCéÔ,ŽZķtÏÎxĩāØåë6t))æÕÞÝŧ”īrÉŌÄŋþ"OŒ'š‘r=:·jÞ|ąß•šz§ĸ;o5mÖ",<üĘåŽ=’þ0}ôČXž`c.ClĩƒEđe–YVüš0-ÉŪįHƒ°°ö…īŧĐIᎠ}™ę,™p HļníjP+ņōîƒ9H=|˜uĻy9đ.&I‹\ööüuü@Ôļ§Ðžĸ !íÚķ­QĢæŨ_}IŽÖ…Ž­ö0WŅĄØM›~…N6L[Ïš|}‘ ŧ›–Ęã=ĐŊ,MđÄÕŦW“Ŋ]`ø°Þu.Ģ/ÃEr,)kÜĐIܚžŠ Û,ģĖēâØœ\MËþw,2ŽĘ…BĐ[ÕÞïo 6LðgšĘą—üāZŽ=rÎėŊØV3BfHjmĄģGč €ĩfxčĸÉ'}zũš?ï{·–ciŽøƒøŒĢ` ĸš1xĖ‚g•ïÂ8ËŠL—ĘÓĐĀ…Ô†ĪŽ‡ņîŧƒúŊĸėÁŪėL’›IFd֝·ąý؆Î=čîĸ”2"čūŌđcÞÎb€Sߊ Ī2Ą ‹ 2Đĸõ?ŨÞëĩ ũī"Öë5žSĮ·ĪzB ęĐ=ó{iļ=’‚z*"B1ó-Đ Î9ãŲ{ƒčûšBÏé2Ą €$>|øq|ēgr%ĐŦ.™YįŒ-jïUÓcČ uÎĨˆ?vï^kW @ûߏB“īįņ†"ËÉfwu7āõZĸę·Ÿî˜ĻS Ā8îL‰1ÓÆ·”WÕĖ0”™įI0"óšÞ6ö~QŠëZũÅkė@üŦŊöœVd„@<7fÄõūÆCjŋvuSĪIŠš"ô§ 먨kWWUe„"šš4ïsx­mŧ§žtÅ5ãđqĘ6Ą§Ü‹bFč'Jí‡WkŊ\n?ÂAišIíĩ#TžęJbwÃHėšÚk―î(@’íøN ė1О>į$U3ķ13UÄÚŊSŋWÉ|æVŨĘEpl1ÝuÛŊĩŦŧN‡€ĪĩōÔÕ=vwÕ=Ų7‘AɆÁĖÕ㞉ˆÎu`äZÏuXđIúæ;Ķ2cåžĀđēŦgœ·5}*hũÞmH!ŠŧEý†œ’Ą€Ý݆ũZ·:C s.Ûkí―_3ÓU’<>uDJšÓęœÕE€T†NÛũ"h ö\ŨõôüÎ lkížĐęĩŒSe82m\íäÃ'ŦUÝ5Ĩ`fvũ­nKŌ隚―wDTÏdÄüÉ7aT!*T3$`Hš™oééŠ:ö)`$…Ô}@„ršß_ïäĘByęT ĖgWˆ\IâšÞ0_ûuŪ[ §2TUĪ†dÐc]uŠū_ôM+dōŧöÛcx<§Ū™ ́Ŧ‹dd‚Pˆ ]m@ ŊS _ąŒ}ŠDg€―^+ŌŠPðýþĒôˆoŧ žöŊ:åņĮÕþ@>|ēZ61Ūsa:VÐėžHøzŋEp§íņ„˜+ŊũQbÄ:ŨÛĶĪXŅÓ (šM2#$;ŸM…ĪũulÅZ *3ĶÆcIÉė* v.@į\ąVz4—ÁÓĮÚĐļî`wį2Ü3  RŽëawHŽXïó†AU%)#”qÎE>ųĀ!EbïuŪCޓ‰Óc;3Ë5c†V|wĄŅ–ēęô M„ÛŽû+ē§ ņáÃÏã#ĩÄôH~åëŊë‹DŪ°<5ŧút\iEŠ`D0ãúšLŊ\Î9uúûÍ@0Û#Bïë"™‘‘yÎ!ą^{šjL0WNwAŪĖžn;S3ŪúēÓ°ÍýzMÏÔ(ĩ”§ÏkåĖxPŪd(ÔÕ3óT ‘kÝ;œđIØ―V‚Š:SĮ·Xũô”sÐû:3ýoŋv5î·mĖ•=ƒāTT@ĄdčzŋSĐPuŅR€sNP+ó}§%„">üD>§Åi―Ï% ’hĶHá:‡ŌZÛeOO§“VdāŽ5ŧö~EFuMĪî-0”ÝeFPUÅ{@•™=UÝ"vŪS§š%’ Đý뗁îō8"ę<Š<ĄļþœA y‹b*ž:Ïɂ™Á€@î5Ó ņŪk€'ŸÅ@ņ›[ĩM’lØđŌxĪóÎ^kš{ïsN%)ĢŦ(#"ŪsŠÄôL›B§ÛöÉÄ`ðáÃOä“ÕŪåGYö Eũx Ðm‹Š\ã,EŪ8ufžŨRęôEjí­āy_#ĢĶ`b™u†ŠˆPc`ˆŠŒigdóüȕïëkÚAQžęˆ uŠdū–i‰ÆėÜ]5c˜Rī[ŽßH§{ŒĖžEo"˜;įš·ēwPKkÆ=Ý€ßGŅĶWfWŨTfĪâ\‡ũéd€3Ũšĸ@‹„}]ï™Þ+IÖÜĮ”Ũ/]sĐ&œ+ôM|øðųļÚvWÓ>SķEuW―/ØĄ8__] 3"lũÔī{ĶOÃĖuG!M5z@C–2# ã§ņáÃ'@Eæ:įÃvu)cåwõō}]°%’lÏRĀči"ĒšŠ.Û{í™ņi1šŠ*"Cņ>ï:™ä·ÞdčœŦŠũĘýÚķAfĘÆī‚Šž2,iĶđrU@Ĩ \ïũôÕ}ˆX{Ũ” ÚsOž$ÏU.ŊĖq{ÆF0NÕ#‚+óŽŒ+"M·ýĖc˜ÂZëL Ž•4N—iš`wOĶ$yFRJį\ïs"Âð™!ˆĮ‡Ÿš™{Ðæ"ÂUqŽĻ:°% :§f&aOŧĪP kíŦ.ĩVJU!ŪĀ3Ŋ―>Ý)íýšÛDƒã ĀÞËvũ‰X"Ï9‰X;Ŋũ1ķßįšgûeŒíĩž đĨîī7,\_o+ˆS‡ŌĘuŪwaú]Ų#BĻ:ï2œë’bÏĖ9Áˆu―ß(ĶēúĖX’B_ïũĘĖ\ëœĸz―ŪSwl’î~$>|\-Ĩ™‰Ėé>Ũą_ŧ{HDæZŦšj*2"8öØŧb˜nOß"ģŦ Ī$ŨŸ-Ĩ zšŠöÞcOOˆĪzšũeīó(IӃ?N@DތŠžÖÎįúĖÞë^ž§ĄS§g!ā:—Äņˆr·grĨq ī'#íīs­―Ÿ|VЈuUu21ÓSĄˆČé6L+s}ņˆLEuÍxå @ŪTJÂđ.Ãûõę™HeĪ€žfČ6~">|új1*‘‘;vũø7°}Î;ÞŨ5Ýš’PėnI§P<§ÎDF€Šųú·_ķįžX”áŪóøÁîē-…įmUîUU$!FDwÏLū–oÉÎ\ûõØ3žYkMũØĢ<‚ŊŧeqfÖ^Š öÝŨÕu‰PÎ<Ę zJbîeĢĮdͰcåx(Ī’äĖČĀíïÕŧæ^;wÕ!3}/MAŸ„ŸÉ‡OģWP5&ųhkdˆŽSOîЈëûBLUÍØ+OŒIūöŦšIØōœ ãÜŧК•+5ũ`’ņ˜RJIūßïČif<ÃÛxžëęi1<0 €Īís]RäZvCˆˆžïڗG[)EÆØ]%éĩvwSŒÜ4nï\É'$!3“äéÀ3Ņݙ9]6"ģęRFBķ=CJDWļ^ŲShĻEР93øĸāð_ƒ>ðÆöŽÔžŪëßĸþWwŊ•UÁ–b:{ŠŧĨȈîîjJ ý9Ϛ]uŠ1ÃÐ___"‚Āųûą-ЧßįxÜåĐ+"CųžŪ*Qk­ŊũÄŲ3”úŠýÚ_ó5ÝaāÏT#’āp’qČžéŠ―öŨ{šāĘøĮ?þņä‘ņ á/w7îĸƒŋÎ?ï ĐČéŪnIqéņݙ9ÓbŒ'îEŧ'ĪH]ïũÓË3MτRRU‹XkõLCÅZëŸĸü‡Ė<ĀÕãËāßþų~þąðĸ`ûïĸĮŋ>%ŲÝøŊÁ‡"ō_zōëŨŊĸĐ=UĸíŋĸsŪŨëόĪĻs.’BQS0ĢęDAĀã{åęi""ÖÜ2úxÞ&HbÆßyBfũķkwþFŧŽ"(øŽĻ:Ī œnJ?2*҆íGøžŊ3DĻŠũ~UŠ0öLƒ’Ôį("ĪąĮ3=k­Ūú~pÅLČHÃU5ž• u§ĀЧ+(?ŅAßXSƒPÄô (ÅĸiïÞĢĢ*ō<€WÝGw:Īóėžy ‰d„,ēš*‚Ę:*Ē ‚Ļ€^ĘS$<#Ļ€Ē;šŒeÖœQģBØÃČS°ĘÃĪ%$é<š;ÝũUõÛNįėÎn'gā$;Ųß᜜“ŪšU}óGS§ęvÞęv˜§ëÆõ9}î\UCƒ“„„c,7wĀlĐÓé ķ*‹%°hī―Īýjxīœ\A‡ŋw:y—ïđÓނ”ÓiHnžįā )Õ5]Q|ŨlýY/I"A…J‰a0]ŨoÎ9éYĖ2IŒ#Ŧ‰úuKÔ2ÆG]]Ē(ĄŸģaaa‰‰‰)))‚ „B æė™OÕÕV— @zY€›œ—aŧ%ÎHÛŪŽZJiMMMKKË AƒâââHČŦŊŊĸþûï ###Æķ!JˆW'?Ö+įŦ›Ģ"­Ų·D‡™L=éī'€&·ũŌÏΓWZ,ĶļĖ*‹š6jāęÕŦųųų="gýėv;?~Üĩ$ „(qĐüJĢFeóÐ~I9ĐÖÎ'=;‘RBĀÜĖ,bWģ\Jä~JŋŧÜPë6RmĒI"]ĩ>Ÿ/66–tÔÚęj·Jã“Sb",ä/Š·•Čáaē@šT||žŨë%!!ĪāÓļ%Üc•m‘tJó8Ŧk„˜cââĒc"rCļâņIáVI ]H 46Ęj@ĢĒƒîY€N·ÅĨÔh~{ÃËoí<FÛ]Ŋ|þú4ÓõWS †ÚčōEŲĒLĒ û<- ÄÆD āų`áģîŨߝÖĩĸ‚Ü-B(FЁLép8Š)ĸdýŦoþ IēÂÍaÉ+~[vOFØu-‚·SZ[|šmģRĢúÕ§ÝQōî――­ÐU·ÍųÍLxH7ąWc‡7-HZuÅkïŸ\UöՃéîÃ?0níG)p %@Đ^wŽļpÛīuoôŽŧúĮíÅïÔnúÍš$‡ŸėíÅXāōĀPJ8ãm Å@üU8gā/Ą„pŌþGwB(Ä „(IÎïūxæáýŸŲ\ūô1Ņ[ûmå·Đāy"PāŒņö| ÐT”ŽoXđ)úcDß?uRzŽČĸï\iŧŠ­ĄĒHã8ä›c@ü=ý9N;ŠĄ­9ãĀš7j€sÞŅđTtMauÄãē%ö›HTÍð:NnX?ũŦSzþļÂWį>ð՞OĘ>ýøðÅš9 Š.ė.ûŨ]UœŨÞ(]Ņ|þĻ+"ohBCéÜÍ<ËzxOy}ÆCïŽ{);^ŽŪøíē’ĩ?ļ~‘?(6"ģ`QŅļc;JKķýÎMŲėMeãĨ1Æ;›ĐA… ļaÁ"JTÛWþ;oþ”ĩK§%F2ÃÚkÔŊúpf\:ĩoóšŨū­Q3ođč•đ‰°kÅÚ*YržŲ{āZîŌkĮeīlŲû/ŧköO?öyņšyįíįŲwÉUŸŽ+ŊïCjvėþ2ûÁŨ.˜Ųø§…ŊUÎX5ģ—UÝ―ąļ!ņs#’þcũÛŨ펎Ϛŋ|ÅøIŒó Qk0Ý:ŠĨ”þ9mIP\5§åÏøŧŽ%ÓÆVL,œóė”Üt^2kæÁėįv•å>ý3ˆ ū/77+~ĀôEKš#ũŧË―ĸpÄūjÕĒÜ$ӖßէL~â6õ؞.{lõęĩ;fÏŲqtÂÚ;=sŸX>ąīlFæúÉ3ūŪąÏŸ3ļtÉVXüë=ĢS[Ėąí‡ôļĻE‡F˜Ņân88/6œ*ŠÆh†lV~œ=kąuôėē CKįO›6ß~ðý§§}pZ}å7Ó·Î{ïÓýãV>xwĸ>Ũš=į‘äpïŨûöŲ&/ÏlĐþxÃú“Ū_3ŦčĨĩß<öøHę8päČ$ý9ÎŲÅÓšÜGWŠþíŲ%­øp§íðō›†í^'ŧ7J€óöđî@Ð4­“íĮĐųÔ{Ý^ņåæ/LþæĀÛWTzkū?ã|xÕOÎÆ5r@Ŋh{bjf†Í™œÕßjŧŌŦo͉6 Ķë^•Īôšŧlå}Œ#·šŒđۜ=—xëûsG§Ä‰ó>ýK‡č&ÞąĄīxzՄÅ/LŽ ïlAE‚ =2Ęn{ņÉąû_<|Ņ=ą…ą V=yŨ°ōƒņTŅļ)ą·5NN흞BÕ* ÔÐ5Us& ŸēąĪÐNþ3/b{kŦG #‚háŽ-ÎD"ˆ‚q|ĸ§M5Žm‹Ÿ<Šë'zÍĢFųë!ČĻVUÁ`ŽÛŋ˜ËH˜ęSīð!#'ž•&Ý3fÞŅ+î(ƒ ü‡I/=28L6ĮĶĶ+õ—TÝĄp] LÕTÕpϜ‘qÞÖ3㜠Dóųk 8#’LšgÎýÜŧŊrp_%ĄŋԙôĀžÕÃ9[2ý…ghüŨËÆsÞã&BøČS5€Ķ'%|ûāĄ'―ŋ‚Č Ÿ§™[bäVW]ƒKė__ëh5É2á:IÐ}šĶęL–€3ĶęZƒËËÚōšs ž*ȉ‚áS ÕË€qĶ{―'œîm>{â„'á°%ĪʑYS‹ˆķÉrļ]2tF‚ „ó›YHŨdS݉Ï.ZēĄītýÆí–~y·gįL9æâŪßė)ĸúó=;OþTßŦo‚ûãw·ýþˋĐŲCĒĩo·oúį —[đáS  „ÞVƈŸækU4=<í—Sfū7ÎĒ%…~u† „ĻŽk^ĸė›Ó^nI‹!!ô74.į:ĩßÕ§ŸšQēé­·6­[PļŽJĘs—õÍ ŊūąiõËoĸ1gĘĢ™áÔ™šÆĨ†ęõŠ:7hrŋ>g~ýÎķOöš™ūV„pÁëVQŪĩ% ˜ãzĨZݛŨŽ\>gEe­4―ψ ųyę‡[?>tĻ|Ïî͜ Ī+I7w : éý‹yîkž˜ėûß(Ÿe!Bņō-#öVœļF, I&ÃũJɖ/^äģö*ذvý7UÜ áã^X­$Å(BTáŠÕ) ē‚ŋÄHUxäģïþӈãĮ\aÉđÖâ―Wía–čܜ>'ëš Š–―g8į@)ÅQ-B=KįaB˜.Åf­|sŨĻ/þpŌŅωQCîÖ?5yØŌŨû~öyM―gĘËëî+ļ]õéÏ]FÓú>ßmŋ*.Ō™æÍ]TšųũnŠ1É>sUI‚]ˆŠ˜øZ1 ̌‹öįW•$&ÉzxŸŨŨo,?úCæãM!jRŒŅk떭_ėųũęV=!5ðÍ JhÐÝȂÔtOԒŽ O0iúmífšŠŒq1gøÄž TÐUŸfðīÁũÎBSU ōFM4šëšJoE Eetčč҆âc eŋŠÎÜ;f< sPŪëÝ/Ž·Ė,)jæáOLų{Bļŋ Ɓ Ô3aԊ”@u„ëRDÂ―OLÉ9ĨĒ pŧpš0áÉé@9W5•4įÎ{‰Ąiš‘’wgŠn°ļGŸÎW5-aÔ}LņąČþcԈOŅ :Ú_Ēœd5kČhÎ  "1T•éĶØ[ž1‹rB)óyÕ ] íQĻčÆs+**ü DGG†g1ü"ļþ5\_ÓQou—ŋ?qî’G“n2|@Z”Áøĸę0xCI’O:UPP OŨ"„(%ÕÍėÐWƒK)ß/^ĶzÖý{TRųĢįlMSÁûā4sļ‰rčę‡―ėvûŲģgsrrl6ųŋ’š5(3{%Ā ƒqņ/|ļĐĐéüųóIII$D „€D™…ŒđūĄéäEgƒ+Ęd’ƒ’rđķ19ʔ)J ĪË·›ÉĖĖžtéŌącĮTUíčšÐ9Ýl6ûs6===D†ī! $ÜDúÚÃ\­Qj[Í „PŌ̈”'EJy™ŅþĻ Ý1āĮÓu―Glį+‚,ËĄöh-BHgÐāa?·hM^Æ8éYÂdš%'Ûä3õë†đÚļŽšãY„bpÂxÏûŒ ”H")Ąī{–ÅBqôƒ›ØÛˉBĻ›aÔ"„F-BaÔ"„ÂĻE!ŒZ„ú@p8‚ „BÝ@1[YYérđ0mŧBsÖ°þ˜%EEEÛķmóoÕÐÐāė"!„üĄęVĀúcöŋãT/ģ<;ĶøIENDŪB`‚doc/images/ifw-user-flow-adding.png000066400000000000000000000051511325366651500175460ustar00rootroot00000000000000‰PNG  IHDRŪ|ŠĪ8PLTE " !#!"$#$/..555>>>EDEJJJSRS[ZZcbbeeefffggggggnnnvvvzyy€€€€€€€€€€€€ƒƒƒ†††‡‡‡‡‡‡‰ˆˆ‹‹‹–––ššš›››ĶĶĶŽŽŽąąąššššššžžžŋūūÉČČĘĘĘĖËËĖĖĖĖĖĖÍÍÍÎÎÎÕÕÕÛÛÛÜÜÜÝÝÝÞÞÞßßßäääåååææææææææææææįįįįįįįįįįįįčččëëëíííîîîîîîîîîîîåíîÍëîĐęî„ęî„ęîƒęîƒęîƒęîƒęîƒęîƒëï…þþþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŅTņh pHYs  šœIDATxÚíMoŦ:†―°j]DˆdéxUu·ôuT õ.îĒ;ŊîĸäOÞČG{’0Í;ŠZ ļ1óĖ—ĮQ*ȂD@Ā.pļ Ā\ā‚pA€Ŧi6#ËbqÅИM5Š,W”ЉŨī~ƒļĖĘ^ŨĪ*ŧEïzZWU‘=Û‘â2™™ŨŊUf{Šų.}Ž|0Đ—ė™§ķÎ^bõŪbÅÐLĩþõ'ļīū–fŠĘŪ{ЉÃŧ^ží>ŊLīÁÐþō֛ЃáÏ=ÕÄë‰ ŽX?oĖ•péóåãŪīÕÓÔļ6e_5ßē2aFÁý3K1iZ\úéÛōŨŸÐSÆßŨķŌ›EV†#7M–[ČGĐāŪŦáštrĀ\ĢāŌ pÁŧ€ ļ  gÄuÉPāۘpýwĐpíá:ûýÓāŌ_”o‹ëÜŋq Þu}LŠëŒ7Ĩ "WīĨÆËããoäŪÅārŦŧŧëŪEāz!ųýŨĮžnŨãzý3ķÜuŨI3ŪqZsŸ<GY!ŋ \“{Ũ?$?ïNņšĩĸ,yÝ Ū—­ĸ[!RãP^Ũ,”ÓWn‘…ü-V†Å)ZhņFˆë4-t5–šîB‹7–Bþ \hņ.Åŧ Ņ„.ā.āÂû ŊëýaPã<žWtģęÞãóþ\1ãڌ7aā‚wMk HAûį3âBî:6PðþųŒļP‚!pĄÔ.āZ4ŪG•/âštrbŠ.āŪøp― ŠĮŨ#nŨë`aĸðvxa\ÃĩŲý‘ 7Šëþ}Ø5;ž0;Ū·û#fÄõķŨšzÝ7z6ōąp―íãz‹Ũë}ïáï_\˜Ũ~cø~ßčYkcÃūXPj|ōš›c—PƂkÏŧ84CԈÞ5*ۘĸĘĩŌaŽö—>ƅģĪĘđëŨĒļ€ \ā.pA€ ļ Ā.āMĘDˆĪîΔüŌĐše|“ķáŠS!=–ũŊŌ™ēó<ļ“ôÂĐ:ŦõėhōC{Iӝĩ‰2|‚‹_ȒÔŨðlZįôq6•ĻhVėd„ŦTBŌÃįR$NAÚĘe&Hu"Ô Võ'ļÆō.ÁŸ0&-ų˜õQČÕB‘N„Uķš~2M‘NFJtí;oī"\NjŌPíĪĄ/ō. †9M+KXI|4ŪT‘ēčþŽB7Ī'áÉĖwÞ%Rē3ē{6·‹xdL&uŲú“2dÕɄ3:J •ĢWvɄYÏiÅVb}•ēŧĸĻC0ôļXiôÛĶIfÂE3īĒĻéGAvE†SŌa­d—äû\íÍíJļKf’mÃC*·ļ(ވRÓo‹‰‹C/Ë>.Jes`sŅėá*`:.ÉsąeëÜ]0ÔĒ+ã{+Ëæ–] ŋ>ewTk~ļR͑iš+ ÎnēÖ~ é"óŪÔã"ïøÔiM–ßĮeý―šË&{\>ąÓ§X~œLu-gũۚ’ŠÎ]d˜!w•Ē ÄÕ$!Úâš5wĩB·áÓJ$Úę‘q5ŠŠŊR:9s‹ËîpMŧļâáÐLĸPú'—\)&ū2 ļĻ2LäĖļ8ý+>5Ō#ļŪ yÝÅŨ2 ‘Ķ™ĮeBečqųŠZFšĶŪfX3ũpq™Q+ÍeŠĨÐlŽÃÚG‡+æŪÏ%KcdÕZڜÞEĶ,RGÉB8^iQāšj!ĸÁIö›B‹DŦïÚ3ünBËģĶ’ļ–âY‚;ĀõÝe!ļ’d›Ôw­–Ó['í]>í_dÅVŨŠsÅíŠۿ̭“öیö*u‰æ5p}AŒáū/bhu^ųĨ0ųUØ:ņ‹ĀŠQÔėNŧ~xĀV`EƋgę3PZpã‘}’ŽÂē{72lʄÞqāAļ.Y咗2ū™’øFá깅%ÅNĐšâ~xÁŦӀËv+yE !œ5ZæNŅžKâĻÁ8ŲnĘī1ä]FïļΗ"iïu…Ö!ĮEđÕŅU' acēí Ō―ŠësГpžË‰ õAƒēƒ‘íĶLÛĖb\R;Ë:(ž_’6a# Ý$ŲâōaRä-.j?‹WÞz—a•ĮĨ.ĸ'#{›2—―p-ŽĩKŧ%Æï9öuÂŧJĄMÕá ec)úÞĨNz—Ü)-.rË$Ū ŦjfásWšË]U—ŧĪk}DÖtK‹‹W†Ų6wípĨM—ŧš‘íĶL‡‹Ō\âšÄŨŲĒĘ6Y_Ö]eķNšú.(k@iŧuWÛ=č*ÃŪÄŋeĒ?ēݔiqĄ2LpÍ/1žŨ ļ€ \ā.pA€ ļ Ā.ā‚ļnLþjŲ!ˆÃōIENDŪB`‚doc/images/ifw-user-flow-installing.png000066400000000000000000000116351325366651500204700ustar00rootroot00000000000000‰PNG  IHDRģ )b9ĶPLTE!!!"!!# !$"#111433555CCCEEEGFFPPPUTTXWWaaaeeeeeefffgggmmmsrrvvvwww{{{€€€€€€‚‚‚………‡‡‡‡‡‡‡‡‡ˆˆˆ‰‰‰———™™™›››ĨĨĨĐĐĐ­­­ĩĩĩšššŧŧŧžžžĀĀĀÉÉÉËËËĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖĖÍÍÍÎÎÎÐÐÐŅŅŅŌŌŌÓÓÓÔÔÔÕÕÕŨŨŨÛÛÛÜÜÜÜÜÜÝÝÝÝÝÝÝÝÝÞÞÞÞÞÞßßßāāāâââãããäääåååææææææææææææææææææįįįįįįįįįįįįįįįįįįįįįčččęęęíííîîîîîîîîîîîîîîîïïïðððōōōųųųþþþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ-8Q" pHYs  šœCIDATxÚíKoÛ8†‡  †€%"… ļƒCS5P“ĨķPģÐ&ÓUWÝj“Ÿ§_ũCRķ|‰'–­‹íũ õ8šóáđčĨų­†šý†!3˜ÁĀ Ė``30ƒ ĖĀ f00ģ`ģž­‹SôiRˊ^­ģ‰ų4öģĖFöį™%ŨĖvŲü:ĶĘâ{7™8û|Ûōh|f{ÏĄíĖĖ~ķÓ%{ËčnãĮÙĮĐĮøĄåŅâlß9Ô{nžŧãzļN&“+GݙkZï=‡zg6Ëé%ŧ}˜%SaöŲī=?ÎöžC―33ŸŅ7yĄŨ?ƒūąßGøŲ`'õxy0;Af#ûf`f`f`vLƒŲÓÂđ§Iõi/ïķ 07N™ŲwžÓ7Ûæôļ:ČVdÏĄ—‰æÆWŠ-žÏķ†útzýW‘]ķō4å^ĸ$ĩ+hÄ'Ę 1r#˜xf`f`vZĖðýƓc6ž?`f`fÓeķ}agcëöÃĀlfÛP7ķn? Ė ː҃ô݃üėÕš1›˜O`v‚ĖFöĖ΄ŲsŧnÎ>}{Ó­ū­žôžđaxf+„?ĮƆģaķÚÞüšÓ­îWOúīđaxfŦöý– Ã2;Îzģį72{^gö<-fÏũ[6 Ëė8sh+ģoũíŽvĸÆÜļ~ŌķŦ ĖlÅö`cÈĖ:Ï!ô C2;Îģ3čAðũbNŽlúsĖNÏĀ Ė``30ƒ ĖĀ f003˜ÁĀ Ė`'Î,Zü/ĪqšķŦųĘïÖÔĐČW#„°Sp„o'…ˆŋ)ŧēģPî ČLý3c†&UÖđHĮw„,§ŧ—ĘžÂ,UĢꊔˆęˆ&ķR".ĩ ų"…9OgúWŅÎÔ Ņ{ŋŋĶG·(áĀÝÍŽcFwŨåŠ#Þí:—"ęqĒÓüŅ.ÖČÁũ#fđ’î˜arĘäžŅZÐ4#w•ēƒ0Se!2Ž3EîĐĻ.Ī­hĻĖ‚™•Ĩ•q)Éï/É}ÚÔÛ8 ã>zĪŦBĶ4(tF­:âÜŪĪsīGf…p3ˆî]TšFËV’ï_æ"̟ÔåFō RIEīcfÖÍffÉÂfN%;Û0Ó&ĪĪT„ý<õûĖãiĖqUŅTŠML7J4§ČĒåHp›­zeV”RT^k—}č–ÏUer#3Ģ4QÉċg–‹’ˉe‡Ė|žŧĄ ûûfÆCĨiØ"7‘Ųō–#ÁíāhÏ5­ŠdĨœ g"qė‡VčgĒ7ÂĀ vĄĖüSŦˆ^ë9ž`ëŽ1õVc‘âjjŧuƒÖ\TœŲzyÐõYũ/ÔöaQĮcV)ęðSŅ傿ō˜åÔŌCĩPÂõÞĐû6D Æ\TN/ö –%/Ą ~Œk$•[ý*;?Ãų'‰Ä.žLéâĪĨĐ2•|+ŊũW‹e§ų{Ÿågđô‚˜‘R^ÐØÓ@ĩ#Ļû­8c™‹™éf €Ĩ V+Ō…`ÃĐüŠJ'‹›õÎæ‚qmdJ2~J‰™―QeĨãÅ2ƒÓüƒÎOŽ’˜sČúĮIæFådē *u•Yę5‘°Pų­*m ĢĩN›UáÖŲČĪĂžáRGRŋ—e‚ætfv˜þxrĖJà ŦNđōę}PũÛĖ*gž0ÚŽxf­ˆÚÆĸÖÍLˆģÖyqš˜y§Ø Ú$ëĨæï™•>s^ ģJFĶ”+qŊũ <Û8skkœU"éœéޕŦjí8SŊƙŨü—ĖÜėÅ0+)Ûg­zÆëÔqŦoLßŊp1KĪ[ĪX,@p›p@†ō}cû‚ 3ŧĻgŅb™Áiþ 3*sj2ŧœÜHĐ0Šb7ęA―ũę~úFķdą(ëŨļoĪC’eßČĖlũÔHŽüóŲō‚õzŲ7ÚzĐųf™ïčøO\9D―Ē>žüUĸú„™Ķޗü(0ƒAo„˜ÁĀ f`3˜ Ė``f00ƒ ĖĀ f003˜ÁĀ Ė``30ƒ Ė``f00ƒ˜ÁĀ f`3ØQ™Ízķģđņj“Yql›_ß-™Þ―æPŅŦËŽ(ŪŠ"‹gw‡0mˆ†s(‰“Đ0›ŧ8›ßÞ>NŠYrXüũ1‰ēkö+)nįc3‹?óëÃuRL+Î9þÓŪņß3;įWs;Û?ÎĖÛė­žÜåÎËCt3ģŊí·.Îf·Ķkœs\‡ŠkOîúóčđŅ|vБĶgŨ>Í&“]9{ˆmü8zrP­ïÍŊ܅Ųãíž[üũ0PŸc~―ĩ…™Ųör‰jf {;•8›PŊ?MfÓëõ;3ëY=8fĢ Ôé0›š_`f`f`fO4ØŲ3;ö˜ ΐÁ ĖĀl’õlgRïÆėw>f=ļdg`6ģūūfˆģq™Ąž^réqöņææËԆh>Ÿŋ€Ųë—ywuõebqöåęęÝ īŦ­[ЄžžÚmðzöã‰ėýëÐ.=ΞŪ{š ģyãŌ+Ð.Ųũ6Ÿũ5˜Rßø~ē‘zý/ȍŧ.ŋY›Ų‘‰ÎĶÁ3õéõú]ó„v…8쁘 WÏĀ qf`†qv Ė~}ZYõýô Ė&߃ŽAúõ Ė&gģþĖzb†8C=;”ŲË_æÃŋüæßæoÔģ“čŋš‡ŧ?ų͇?ŋšŊˆģS`öðWýpį>ÓŨ—`v Ėþgþ2ŋÉĖó˜Ä3õ?áË_þĐÁ :HWfúNęÏ^m/fĢđ2Õi f`f`6afSó Ė.‰ŲóšâõíįÏok›ždÖū7ÝxÕ?vn fßîÛãqĸmsðĖ֕åûŸ?ïŨÅæ™ĩïM7^õƒŲę=ßĸž^ a™Í6Ŋīđi@fÏ;˜=O‚͜ÕY=8ģĩ8ãaYEÃÆY;ëļœģūa fŦõƒ2ôjõp9|HfŦĐŲ Ëæ&ô gÞ7NôïÅLŲtþ^ĖT {čô˜ÁĀ Ė``30ƒ ĖĀ fgkvTfZïvÎÔ&~HĪņâ7ĩ:B•*K‘ŊŸ’ņįÐĒŽë$IEkĮ–cũģRlē­Ð^ú‰Ēá˜JŧN2f„YœÓ0—ʞŽd0V‰ŠþI:Tgk‡ÁlČ8K’ˆĮĶÐBá5WBæĩŅRčĘ QœĐH šHđŅĄ#ðĶ0Ó.Öę:|[bæ}ŠSöI‘Š<•ī“Ð’Ëž)ĄĘd3<Šģā1Ÿ@GŅĮÐ]ĩĒč-DEgęj/fV°+UkúO™’ yŅk\-ã,Rī3ĐŲĨĢ ÓVfēH%đéÅO%MŠŌˆīŌ†ãŒ™ĐēYEģØ ÁŽQâSdÁ>(|ŠdB?g;) TŽyŽR?͓ĪNâLr’dfÞcá<&ÄĩQ+Š&DFWRɞĖ"ŠþĪVq]’/g‘ŠčŋķÅĖ—QĩĐ({õĮ,ãđ—šuxÍ(ÉÐ0ÍģīafyýJĮ ĀŽ.Œ*Ŋuâ|P6ø”đŽØ0Ŧ#gv혚ÞT‚þN·cķðļyģã™ĖŽĶ Ģ˘bÏÜhų>,TŲ&7–+ĖĪÛ@S…čE<§Ÿóý+ū– Y.V˜YÅé;ĶĶU‘Ž”s…ĮÞûä{‹6ģœ)FūœHĻ)Œ–Ė‚Į.ý‹4NâDĶtĨL›îɌëjÄI6ķYjhfĩ™EîØ”]ĘûcVņõ9UīãLúæcÄ8s™‘Ðøâó>íŒģ:扞hJõ f™›úMœeZfą’aFtbVWF í~…1ŲĖŽ }UēDúWŪđČĻ ļz/™ VÏ\…ŠĻôPGD>ųz–sAMj§ģV=ã>ąÆõ’YðØÕ3YQũBMUFYÛnĖÜ}Jú•K%ųīÉŽïÜĻܕ)ƒPĮH}™ĨK7õÔ(eūoīū{ã.Ž 1ōó™.\ęÖečyŒļ ,é),]0Ģ&MKŧėÉÝP.˜yCßH)™žŧŽ*š0Ŧˆļ›LT_eĖãĩɌ{ēÏdï3+ŦzbVôúí8ãđCôK)2Ūą–Ām0ë―Ũ߇;Ģōiã‡$[OÎŌ­ÏýÐ'j=ÆUZŨ`vJúąd­Ė.Õ~Û&ÆšųðVå>ŧŋĘĄũ’•ĐÖJ{AĖÞjRu•+ĐÚ·ŒgÂÉ\öĒâŽĒ,u!5Ŧ‰2ÜŨĶķîÅI§yuߐþö5gŅ%r#TđX Í―ä•pĨý˜Õ­{TAŋ/…–ë?ó"„ūi71RKÚ°ˆK§ŊģĮÏÞîļģdf%nEŠ~!S}~jÔŽ_WA[wâåqڂ‘eĢŧ7g‘d"SRĖ+9ËįgþJ{2#đïaTYé8čĨc‘ądËę‡R…SčIšĄĒī{Ž*Īm˜_ĢΒ™ÃRņ˜8|Zítž ­Ũ~UQ›:ĻŽ\ýūÖYž/2ÍĘ@äŪĖĖô~IËÕ3Rqų œï2Qx]ąYŋ Ú kĩ `· JÁŽXûpĮ&zÁŒ‚TįČĖUŠŌÕþ(|ÚŌeĨ ­ŧ‰Ï#Y­œĮÓïkÅ™fe`ÁĖ_ißÜč{#f’‹ÔëũŽYnxĩÓ13YˆģÔ1sĘŠ†Ų‹ĢÄYfqģ.#í2DtęĪĸvœŲzõ,ÏŽYX0ŦũėÚĖ6â,&)֘đˆo˜ĩâŽ8ë8ĢzF#ąž™fô…Œ’– ―ķNĮ*hSÏĒšŅݗgyfÍĘ@`ŨþJݘđz-ęį_Ú°Î,gņ6UAÍvõŽĒÚgüzö™Õ3ķ4ôAÞlâŒŧ=Óhë4@6Pöęū{Ķóû–gyfÍʀg–ųūqŸŽŋÍlŲ7jßWTÜJŧĘŽÎ#ĸ|ÆĖ|ßČUŅøūQŸSߨ]žAÝïĶß§'cGd6ŽšßYŋ3˜ÁĀ Ė``30ƒ ĖĀ f00ƒ˜ÁĀ ķÝþƜïī9nÍIENDŪB`‚doc/images/ifw-user-flow-removing.png000066400000000000000000000067661325366651500201630ustar00rootroot00000000000000‰PNG  IHDRÕäų6NyPLTE"!!%#$/..333777BBBEEELKLVVVZZZccceeeffffffgggooovvvyxx}}}€€€€€€‚‚‚†††‡‡‡ˆˆˆŠŠŠ———šššœœœĶĶĶŦŦŦ°°°ļļļšššžžžÂÂÂËĘĘËËËĖĖĖĖĖĖĖĖĖĖĖĖÍÍÍÐÐÐŌŌŌŌŌŌÔÔÔÕÕÕŨŨŨŲŲŲÚÚÚÜÜÜÝÝÝÝÝÝÞÞÞßßßáááãããåååææææææææææææįįįįįįįįįįįįįįįéééėėėíííîîîïïïïïïðððņņðņņïðņãîðūėï•ęî„ęîƒęîƒęîƒęîƒęîƒęîƒęîƒęîƒęîƒęîƒęî„ïōŸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ=HÓ pHYs  šœ œIDATxÚí]oĢš†-YÖX"䌮A:ĘVę]ą›j˜đāŠÕ\ eWęEŊļ˜ëó'ý ÏZ†ī…tŌ|ðaČûŠĪ jxXë]ķĢD8h~ļ  *ŠĻB  ŠĻB  *ŠÐŸĻŪzVŸ§2åķũL5ïUýR šíƒÞs :Õ![ŠR簁SMâdŠ·‹˜ŪÍ&ū·ß&ŦwËw-2V7 f›äËÛcϚÓtð?Û%Ã]Æ?†ĒÚUÓÄũïZ$U{ëOxđ:ûÆŨæ~‘L.ĸðMÞĩž ŠætnâĒbŧļšę*ãLąž_%SĢzgÞ·<ČX­îŧØR:–ŠđC ÜWëîb~\ÚÜŽP_aÏĶß1Õd~ņáAu–T‡lĻ‚*Ļ‚ę(TÍYUÄ*Ļ‚*Ļ"_ÕMĸõ)Õs|1Õï[ÕĸŦþoØDĐ$HЇ[6}ŠOiÚÅĐ=ĨũÍĸÞēūļxĢQýīeŧÝͅjŪĩsLöĢe™þy[sÉõŨfË~Šþų”ęÉïŧŒękËþ :VÏļzÕĮ˜Eąøý9ÕS/âeTw-ûō_=Ŧ\:uę oÎŧÜW7Ÿ@Õ󠎗đþ ęÄ}uôþę850úŦ  Š ÚÕ~†Ü†Ģ:žŊ"V :lÏTĪ:hÏT‘Ņ_EĩŠ :[ŠČĀQ―dtĸwžŸqjŋĄ9VýLÜ߉gŌhųõwĩÜÓĖýęMž^ÅKN/IŋúW œę/ƒįÍŦ(äå鉖Īڄ _Eĩ4Õþj·ū ŠaPíÖWCĨúrÓpô›Œ-K5āũ·0ū܀jX9îŽÝWýĩxæū2Õk‹Õî|5dŠðÕsÞļrtÝÚÏĮĪzm5pwo€ĢŨ~ŠSôUPĢŊ"ĢgŠ zŅîÏ―ę0Õ!ĸwŨū:RÛAĩW_ÕĀĻv’"AõÕmcäæáĻÃ>4wÚîŊ—jã„ų|ũVĖ€jģþYuØus§›ýáújģOŧþ`ŘT·­qƇvqȜJu{$Õm›ęöŠãújㄷëVŒIĩ='°n‡_ÜÏĐ>Žßßë#3p{§Žhn4•[š·bLŠŦý#íŊBĩ4ąjĐŦœ:šyðļXE5(ŠÍĪ·ŸKüŠÃTGúŪĩN|5čï‰ģ†zó‡ŋ'š…ĄÚ…ŊBÁQý óĢ *4Š9Pː*|UP…Ŋ‚*|T!P…āŦÐgT‹H•Ņw?4ū킞) _,Õ(rΊÂĐU•âjM™ŠØÐƒīą6ÓBčēJŽē…0ô›Ą‹(LKķRˆøíVlf—\đBdc6/â )úސÐÄŌdutŌ·då‚X&Õ3ú+"Ēe&ēRwJĩ+_Ĩ–đB™?PMÕØ7įAf[ _-YíïũúŽb–Ų+ՌïvC[7RíĘW­öņJ·&eO5SBRó!LFa­ŨZĻÂôØcĨpˆjAųŪĪęœnCþ―QBåUĒIčĘR{sʘ”eÚUūŦĨeŽ…áŒŧG52!&ā\DI•ˆóRjq)KE‘‰ ýĪ>Ę4”„č§bŠ”ĸ\Ž IJ{šnQD8-5)q’N!‘tÍšĄš‰Ōņ§QĮRF͐CÄjwX/āuÂw&ĩxÃgĢlĒę Ė­§+kĒBð'ŸŽBĩÔēāXt%U.|cIšžFhūð’đÃu:î(VÝE™Ž nœŲÅŠ}ĢŪŊēJ _Þ jqęĸ°&jSÍØÛĒÁĐúæčĖUíbŠ™GČT}=@‰…R0A/ŠWDöWųJčbÐQĢØSMŠØSåŒŊeˆūęóo!Š:)VŦf†ŦV†—ŋ–wmŠĨ”€‹óíáü€‹# žSFĘY*>"]ûjeŠd%Đ|Ģ:ĒŊZáŧ{TwØÅJÅU^’jFĩš.BĪęËH*-đĶV5p•x(—RžQĨXËqŠĨ˜5’9ŨBïĐrĩ”q/r#Øu­HéJÛAcĩã"gΊ9…j)UQ’ÏIۈUKZŋ]rÅJwĒ0nÂTGŪŦ‡k1„@Õ°|T§áŦõ CkXå}Z?œ ŠÁĘÏĘĪ­)‘OĻō˜ĒÕÐĐ:‘ųũsäŽŧþ=Ô?­æŠđŊQo"ލÅY%ĐoHÓ8ŪÞGEŠ7q/ÃøJ9ō}[J<ÏlAux_õTéFŧ’ÆgŽ,h:XYK8Ŧđâ’úˆ4iSÏéxŠIĖĢsÂÕû(UøQXž Ë-=‘4ĨË\Ķ~žTGōÕzR’âģ§ônīAņ !M•dõœŽ§JCÅV§b·įë*gÓ8ĸ‘D%õ˜xäũQ\{&%†óhEGIžۿЭōšžÓņTÜhËTßöá‘FCI:ĢüílTMŸD :2ÕH›,~Ušï*ęđâ]Žūú#Qi\–ĻÖûTT3a’ü-Vó:vAu4_e %ãˆ}u#rBCDŦđâŊVs:ՍÐôęÝ>ÕT4…œ―ųjĄPËWÕŦŋjšÓâ˜gûÝF–Õ\1ŨšB/ÞÍ:U&MTë}*Š„T$ī>’ÞŽĀ5p„X ZÖψŦRMnāęĻrÔĐãCŽF'Dė@u\_…æčŦÐÕú*ĻB  _Uø*20Š  _Uø*ĻB  ÁW!ø*ĻB  *|ūzŠÂþĶ8dā3Đæ― TįB5‰PŨWûˆÕ͂Ų&ųōTGōÕĻÚ[~4Ëbuž lNŨaŠ‹ŠíâTgT-y[―müTgãŦw1?.mnV :_EÏýUPíęĪ_Õ+ōUPEUPUø*ĻÂW‘ATAuūj.ĻÂWATAū _…Ŋ"VCŌæööTįFõņ‹úōŠÓõÕ§ôþtŽ Õ}zúnųŊn9íOUëVP <ïÅę+UõTgãŦŋ’þ!ĻdāŲUK‡ ‚ęT{6ßŅģ™UŒB€*Ļ‚*Ļ‚*Ļ‚*Ļ‚*Ļî_ö—›Æ‡éÜž€ę Ļķ0ū܀ę ĻŪúCЈUP…Ŋ‚*j`PUPUPUPÕó.ûsŊUPUPUPUP―ZŠÛÖ8ãÃóóCkÕT§Fĩ='°~~^·§ @ujTWûGÚ_ŠÓŽÕ-Įęą:qŠ놉Ū>ZŠĻAĩ+ŠøžļR…@T!P…@UTAUT!P―@™ðŠKPՌĨÕŲQuFŌĮ˜j!ĒŌY aŽšâŨ!S—HúӊēĪ-:ÕĐPĨ+Uä =C‹*ra]B[Øä‚>7OĮ.օ‹d ŠÓ šŠ„c‘>#OäV䎉:eœLh̊ų§Ї[ō&PDĩd|ēee–N‘2UĸËÅ)˜ §Õ+@uąZh•WÖĘúˆ*E)%āTĖŅSg›K% gkfïĻūf`'# Ņ|Ža:c_ÍEėJIEoÄĐö*WK–Ý4ėš‘ĖéiŠЁ-‡"õltֈU6[•ú—EôXFÔŅąˆUT!P…@UP…@UT!P…@T!P…@Uč(ý"1̐’ŒöIENDŪB`‚doc/images/ifw-user-flow-updating.png000066400000000000000000000051241325366651500201330ustar00rootroot00000000000000‰PNG  IHDR­}&ÁĖžPLTE" #""&&&222>>>EEEMLLXXXbbbeeefffggghhhnnnvvvwwwzzz€€€€€‚‚‚‡‡‡ˆˆˆˆˆˆŠŠŠ–––š™™ŠŠŠģģģđđđšššžžžĀĀĀÉČČËĘĘËËËĖĖĖĖĖĖĖĖĖÍÍÍÎÎÎÖÖÖÚÚÚÜÜÜÝÝÝÝÝÝÞÞÞÞÞÞßßßāāāâââäääååååååæææææææææææææææįįįįįįįįįéééėėėėėėíííííėííéėíŅëîŸëî…ęîƒęîƒęîƒęîƒęîƒęîƒęîƒëï‹ėïœïņķóôßöööøøøúúúüüüþþþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸo}Ņ% pHYs  šœúIDATxÚíAoĢ:Į-YĩqdiRīŠÜĨtÅîCĒ―žC/>ūOĘg|36ΐĪ݆&`šĸ(j€€qü›gP*ČrD`@ Z …!-h†ī  ZЂ€h ö67–1=ŒąO‘Њn*ãhEاđ” ī>KkĘîÝ­lec§•élZ?VšÆĶÐÏö[4ķõīîõ(NÛ*VĖ,ŦÖ?>@Ë\&ïÞØŪÚZĸú,­kué—~îõ(NZö‡ĸÆëÍĞðÛ7›įU'üåŧŌõč*īĖåō~WŲęibZ›’ {ýžÉbĄõdú=ŠÓķ‚BiKö?)-ó„˜ðr…Ōüwm+ģĐÁ™þķKņÍdMŨoý&ų™žë.mëQëïŅEð uâqþ‰-‚­3ōį‘äûÃļfŽāĸ6qÝÝzëũA‡ŸeDOk§Yh6_9DðËČeoĀBVũ/īæČęū YÝĨæ ‘Õ]-xÂҚ#Ę-Эi―lSæöåîiÅü<áž—í―ӊŦOGŨonŨeЂmÝ5-Ė[G „†ãxVZˆ .!WsžZ Zð„ˆ2@ ī>rýŋ7•wiMyë‡ī@ ī@ë·Þ –óÛýééiíqãîˁ{Ĩ5 Ē_ķ§ͧuī&;s`VZûĢ|ÕîXįYĮoDkLk­ýöˁYiä‚ÏšÄîNLOk·}Ougũ„›Ó–N!ʈ$Ę82$oüûil ī>éŽÎÎ[ŧũiáĸ˜,(&„Ä­L ĩ$-Ђ€ī@ ZÐ-hA@ ī  Y­2"Đŧ=e:UŨnjŊÓvZu*„ĒŊŊóþQÚSvžïí$Ý8UoЊE=;™üT]ŌôUŲD^· Åũą4j@kļ7­i ú•šJTÔ+61ĒU*!éŧįR$NAƒ•K-h8ęDĻ”ę#īnd[‚3LZē0ë]Ŧ…Ē!VŲZzg˜"Î‡:•˜Úëž1Šh9ih€j'3z‘m‘'ĖĐ[:á1â­9hĨŠ+Ģą‘B˜†ÆIøMŌōWÛ)ĐĐ=kۘĩaÏwB+5ė–͋ے`ę™nÕ§~#2ÛJ=-ēÁū‰w1Īø}Z֟k8bēWĄåįtú5'SSËŲmŦf•Īļ‡į-RË0o•Ē IŦI4:КuÞji5ųíŒw+‘knKŦQw•2Ā)Ȓ[Zö•ÖÔó;ė—)ļ!ĮbBĸÅ%Įˆ‰ -Š 93-žúïfėŌ3īŪé y―ÅÍf‚CĪĐöīēzZ>”–‘.ĨŦ–Ę=ZaÔĘp€jÉ/gįđ‚*4Ŋ})K@ĐeÁICŪBŌVX5'UĢ(Á6m5%TPļVĀ™6š·Ą…>­Ū ZŅ‚?š3-hpgÝB\Q6‡aš12wŠŠ%‰Ģä Ŋ`ŒrJÕ§Ģ|5Ĩ­ “LîĒĩ˜Ð–oĢé™Ptā3ÓÄU2­ËiĐÎÆ(g›%œLÉ å0™‚ēšŌˆN\…y'ڛ<eåüAäŋ_ũâƒüÁųāiÅŧX,>M&“Ųl6ýÕų|–(ŌyZš\.u†Ī~ˆ<ØE•ÉLĒjķļv""ģÕétē­R’æ(~_>ވô?ōĄēΐÎÎĮNZ,}>ęũųÐņę'Īþ|üâjðkŠėðv| ყu>ŸO§ÓŅn·kÛOoŨuˆ†u]·‰dRƒĐÔІīēœ%jÛN V0S)Š&ZŒDŋ‚ķš|ˆ'âCŦ>*nųÐ5éó!3ĩ!†ũį#~Ÿš‡ði\]Ï'm?XĄNh%ūčÚp2ðþzņQ>{z-žį ä#<ģĢBY–ÕKÅũûýfģÉó|―^E„%I`@.1„ķ,Ë·Û­ŽĶŌS°„SóoŲ—§Ï_ģ§ĒøŽ–dQmĪôh™Á‰:š’ŸķXĨį:Ļr%ËGũRŪNjR 6>!ÖP 2Žņ ķMxg>Ą’Vp%#ēëųŲō6>(‰ÛĢåÃNš?ŌLP°ō―U(Ŧ2$ēĸxÂ'ķÝó?zæÄ‡võŧ?äÁP"ņœŊF>NjBŨŌō!€ĸ–â>ˆ=øĀó*ČōQýĶķĘ{ÚĘŪxūS+uĪvReĶ“v ė!dē@˜dŌIfJT–„€A˜d’Įą 6ÆûóÛũũL SUŠ*UšŊÐcßčČõË0iúOįH\Ÿ{îïüîđįsÄĄ\AĀpˆ†ĄđļļxĸþýþþþÁÁA·ÛĮ Ė*ÅāĄĄ!&„č‚ĖéLâëĄîšŪĢĮÏtæō‘ŦßtÃþüÉë=†‹ÝaE#‘b~˜ïøīĢžÓn―”/k~`Ĩ9øč§CA)wrŠŦ5?hÁ-üiðøyÕț_ïilō•[- `ąšX~0įÖüė―Ųóš'§ÖâoöĖÄæfšW.?f~4įwšDXÅ(ū—đԚCá"Û1IËíZōŒ`ĖOū1ōŋ˜4ō–]•ŽŒNQúIÏĖĪÛŧŧŋWRÃ%ĩA K„^šÝ3ó OSŒšG`Ņš—ïž’ïh-Ôß 8ā‹“ÎB‹5?Dð€ü Zû IČÏûũŲþųÁ8ņRT~*? ŋg~ЗÜōų9ĪëšVÃ0ô‚Ð4=111??OQ@a’>{ö fčÎÎ0ŠŠJð Ã āņņņw‚‚~X ݈'cįnÖ·ö—{2đýdˆ[ꚮŪū|Ôŋé5 “ nĩØTDp‹FC"DŽĻ”QuTÐn ‰HI~ŠyŠ™­œÄ‚‚Aâ+ðčāüč†ĐŅ›―Îc]ÃÏĩÝ―ýœøõ%gÓ_C "†ļ™A_TLreQlÄH ˆX‰D@ØČYn/7Þ\eōü}ŸŋfŊ™Ü5ß2cØP‚yK!{ĀąôúÂĐ|žL ·ĩe~-óöœÜRôRŒƒŠÎū\ēĩõz2ZÎĖÛņu˜LŌ!WĨŽge…V}ÞBĒ.ņĒúDU€ßũ8*Ŋßž†l’.R|1,øQt3_}pŠâøđk7œUŋŋ:éßß5ˆaƒ3ĩí_õoƒ–ßcō@žPļģg­â‰_ßZ?%õ†ųąÖa€ÓŸíŊ’ū ūƒĖÖþ"RŌYļĒ‚z1ĸ;û Ų~ĄųË!EQdYV :ŽOŸ>…éIÓ4ÎlI’āĸÖŲŲYAC`DÁ sđž°°077'Š"ŌŪÐĶîžėýÍē|ĸq-û/ũãj`Ė1tĪû^‡ ðCrt,Œ؊íV ? Ē")@Úâ+ĀŽI [$īæ§$%GÖ0ä‚@V0Ų"ÁĎäyÏüėđūO~kðđäúƒōcM!F…Ï.0Đ­Pp3H…* ,•eeð…Dš’UMļ,ÃR‰ČN"B°øv8 %)ÜÍPMgw6C‘XZÖtEéL†fčž%ž–`įô™Ū† ĢSW3‰'Ę*Ā$>Þ ƒÉ, ŽéÄÎÆÆÆv,-Āģļä―K'œo‡Ó°Qé4ËąĶ*Üv8q†—`+ •ÉpLzkk+Eįƒy› ]ÏŽ?ēĩu{Ԛ.óYšå™t8NfĨð :Û nDĒ1QQc‹Õĩ ‹ū8Į <“ o†B[ŠáĢӉ5G­}ƒÛõOvÔwĖŧÐ+J&ķ‚X:IYŠfŲl2Mō’ĖšĐŧ{NWŲîĸðÏė+ä_jĻbh#qAŅ S{tí|yį―pŒežÎÐŽ!‹Îr žĶ–MD·ķķcžųý/ęĮÚ ãéÔ@âĐƒ%ŠėŊbĢĩ†Ņbí/ŦïûôČ/%?0 AˆðCVĒLOOŊŽŽ$“IšĶ9Žc&•JAWŒQ…xĒxuuĩ`Xg2 VY”.Ýģw.•M1]KĘŨÏôūĮęiöú—ÞZŨēT:)‰Č,ž ķ<ÏĢĢĩ"ņĻx /Į0Ž$`'‚˜bŊb—âK‰ĢÂï €BN­lx)Ō"ĀŠŸĸ°Čš)%ū°—wÜčŧÚiŋģč3r9&ęëķŨŲÎÚšš}Kžn„ ĩ9LI&·đ`kŧe•Í'Ã.—―ŪŪŠ·o”ŨMā‚A^knt69k*/œĄeîī8œ.WkuͧŽéwočXėŽ·–Ú·_ÉĐÓ_Ø:G<Ķ–ūÕj_k|ęÁWå§j]ށĨ—T,xûRģĢŅuúäŲ‘Ĩˆ°ģtâðGGþðųđÞ)*æ―ÕÕđø*ĨpņŅî‹uu톆Zį…ū8ŦĪ7žt7V·wīŨT•W;zü1 u\ä:wÓVģO.6;ÚÏĩWŸ)ŊqôlĪD6öŠÅfŊ·ŧ:ŧzĒ3ŅÓōŧÇOÕw}ï<™ļåhlķՔĩÞČĘ:_ĩŸuXÓ7Ųaë˜Ņw•ĀÂĻËvÖŲP}ūũ^RÐËėÍMŽúޑ­ŒĻĀwWõĩá Ÿ~ÞôgSÖ M‘š―ÓfsVWÕß|ð\ĪĢWętļĒýB?ÉÎôīôÎø ][›ļÖØíVMqĶÛYßŌÞÜÔPqôhïÐ')’ĨB°۟’’°*8 ~pĩÖĪĩŠąSÁŠÄ̃ûËšĒ#örþŸääýósˆįy‘,Ër-LÏp8 SķdšdģŲD"ņðáÃx<Hū Äå`€\ˆī,Ã^Ļ―vļ•+OĖ[Ëđoū7oÏŠŨŌmmeŅX„aXŲđ,%§*d% ę%J‰ ‚Kķ ےü”`~ʏŠPAABō@šĶ1HīĀéųa‹…ĄEO<ŋĸņŊUŲüUZÎéRfâv›í‹a1ũ7-ą\Uvzʗ đĸZ­?ÁëŲĀdÝŲî펜,;íZQ018–jŽį*I‹šÁo7ŨU >hršĮY{uhÓŒõņë'*ÛÂ\ķŨYseðąœŽl qĘt—ãâČšŪĪ/ŧę'―‘õ‡w>9ÖėKōĶiBéæ“ĨuþķņHSŨĻĻËc+[ŊiûoĪø‹/Ï9-ĮVĶoVœnÝæĀųŠ­ķgüyĖũļđėčÝYŋ”ņ·œĻ\ôņ‚ÏD)ķ:ßxþÆZBNûŋŦ:vōîœ_ĒüÍ#O7‚ÏÆ>þSëBˆ15•įE*þÂŲÐčctCēTZT͔oĶēķþUJĘF— .Ĩz':s ãi<]Ö=å“/ęϔy3ЕþĘãÃßméPåSųøĖÄ·—Ž}öYSįmo\Lžč?Uvęņ6~ÜwĒÂā”gūäč™T]V…Ņ &ž0Ģ_ ·;ÎÏČš8|ūúĖ•ĄŽĪ'―“åUKÁŽ* @{@ýÕZ?Ī*> ~`%F\‹{ÅÚSĻ#3rZ žā ?Œģ"Ģ*Ąú7­ÖW†Ï9ũ1sįN&“i2i;5Ęū[ éĒ>(Ē‚ŪÄEqQ7RðđĐĻ-š·ĻŠiDŽ RŽļ+]ÔķÖWZ[›:mšIfæÎđįí™9ôgļĄ‚”|‹áÜĸ|ũ;ßýōŸXßd>™…Û]š|cL\‡”ŌþFQ„1. ÅbąT* Äqėûū­#„ī֜sGVJŲ"!$C‚ĀV,SöĀ{ÐÆ Ŧ † ĨŅBĩVŽ-G+…R‡CŅ0!žR]A'kaŲ5ëÁūnŨÎ,°óciöDøĻ;XAW‘Åj@p: ›É§íČÎ3ų8<:qG)ČĮ1°•1ĶnœOŋ%!UÚnƓwÜĩqíúíũ ę$Ĩyn6L+Ú4quhđŨėPĄMÎ#VQkŸx>ጧbxíÔú‘ˆÛ•TĒįŠ~þįCoŋųîÁÃsųXŲļNŽYísķríj/—\Y`ÂÏMŪ[JūbÍ$ ÛWR„‰Ņ]2!)>7ß WmŪFļÝn[‡Éü…/?;ôÆ[œšõB’ ÆQ­SJđT˜xČĻ&eÓãĨĢ(đĩÚam™ŠüōÉ­›'°RaĄëåúũ Ĩ FX+))+ÞķejÃ8â"û’ĶՉ­OÅGÞ}ĸ/ŪķR­ïaÍSÆ:gŽûÎþý;ž@*یueO !͒ ðđãŸŋwð+–CÄy+?šeÛÆ‘4ĄÝČÝEũïxzï‹O†—O~úÉŅsŨ5m~óҁŋ›.UVaÎmj^ˆM9“ĢŪ>1AÖ/"ŅÄÆÛ‰†jy/ŨJ4FĶ ˜é^č­u† n·ŋ\7BóC3ũ 8€LÛCqąøtd~‹ï5Ā’û-ÁA7™Oæ,ļ\K—qÛÆårđÕjåóųB(Ĩv†zžCVũp#ēÁ–Ü–ÅXeŋŠs2WÉ-[U+ųá"*ĪuVXMįĄ!Ü\e Ôݜ‚‘Rý!Z‚KÐÕøn†8ÏäÆā—îu8ôá8XgZ”fu2"ö•ĸ•}Ē Bßó0eLco Îþô{“Éųúųæ5U‰}%K9ë4~ûåtÓBAˆčīÝĄ)ãÚÂ ŽšĮŽ|O7=īû™'ÆĘąVØ& Š_šĄœMŸš‚ĩAlãŦ_šœ0ö§­„+o)e0ÆĶ›+&R“ÁÁ2™=ýëLƒóNcnþˉĢg“øųÝÏnŦUéŽHâ!Ū˜5ïæœŅ…ĻаĸŪ;›tþ™9_ŊÄ+F°ąÃš ŽĩŦ„ö=bũš"Ë$i§B ÔŲ-&ï{ėĐWwïlž>qrú’*ŠqAӇŋ>uįãÏíÚqoLjîōĨ‹õđbmówŅÆÜĘņąĻ\{ðŅ]{ö―ēïå#ĄZŅŦ„wÛDrÍoÖĸøņėE”GÝHtkæBƒēŲ‹áЊ}.ĪîôüB3üGĸ?Ór™þąČÜ#@æļ_ýčŋG!Ļ8~æĶôƒ1dø„ÅWØa™ ‹óqÎo”Ý]š|Āúá_ÆĖĩ7i0ŠãŸN_Lį.lczĨ-ĨēaęC\ܝÅLpld Â`ƒ†K—Ūė…Ņŋ€ Ŋų>Ū͚ūŅįÅÉÉéïüÏÉ?ϛæQUUĮwüøƒĻiZŧÝES$tĸá:̈́ÖWƒĮ5ijŠÏ0ĸ―ë ÞðóuÉēŒlņ5ëFâ$s~eM”Ģtč—‰Æ %ņ,ÎØĘ"TzJïøÛüĻWMF#'å፡ðOw0†ÂÁî] *'1’.ũtSždüūÃģ†fčö-ŸČHĒ24@BEŸđŌ'wLĶÔhUód ģ4‚=iŠ†Ū “ô朝ŨÓQt2ąmls‘;<ítk4F4Õiã‡ģ9óÛ­X-PkK8ESyÚRĨękMũÄ?ũøšKŌ0F° é]…ŠÛ[óóņÞÚĘĶ ‚ŧ5đ‘ÝZ^ĶļT[6/øÄ#ÏLąĄ įōËČÅý ká0y—~2k5=vApĸi.œ8åF8☠B…pĄ%Há€ßģÏãųðĮØŊļ!~eŨŠôbŊũC\ԊFíîęęvđ'+%ûözšífîí폖 ö?ũAaVũK ۖlïËę~‰DÃ(Ļsį~­øxôÁđ§1WnąŠŠ€†cŒÁ9 ÎĒ(ē,;"‚ĀŽĩDĘïûyž ŒĀv1ÂäÄūýĢøųÍoß|ûˋŪŋ|—ýZŨÔ ĮX•Ðį#ģðyr:"ĻÁč#ļÂSk˜"—x Āļ:nõYÍ#rÆWúЈÜŦ·[érԊ_ïĢÎ}} ūcßīõÂãę6ÞĪޔĨõÍt“ĀŽHŨ†ŌÚ!M) ÁųĶíÆÔģ_Ä:?ŒH™* <cųü“gŊý3XšaJ}[ĸ5GŪĩ5ŪéÓÐÚĘõģ5eåcŨ]ðu‹&Ŋ0hŨ#–bë sÐAmŨöãÐSBýSŠąŧu{ƒ9Ŧ$ãtÓ‡‰ŌÔ/Cų>MŨĮƒķMJHÍeãąü3fLĀ ÕĖbVqŧy‚†ˆDށ/)ŠÐÄÔG4jâÐu­l;ĪąoËóyžĶöMŒý8zĘC}Ž‹]cÍ9ÏOaFGψEĢóđŽëķqķéĮ1}|‹{ä+Ũt7O1FëĖëOŸ=ĸî}\UwSýÎþpuũö‡x^Þ{öGK"0ÚmÂT.ž›ūîŨŠ…Xx*’{îWY– jžíýÂųH}hšđ[}hOŌGå||Ī>Wx·3–‚1āÆqÄœá7ÂpNÓ4ģøe8 tÃ0Œßl ˜$HÁQÕétB*Ĩ4ƒĸøøÔ—Y牖hšC> ķP;f5§850dX'M +}tМ‘=}`nI4†ŠpŪ8™ÝęĢwę#ŲŅĄĪ‘žâŪ KZÃ#ÁRhYþĄ>gþð*ÍēRĶøęՋ7ï2\+p•ˆšâëÏ_ýøûÁ;\ģÛI0|ØÜĘnbø3WĄāôŽƒ/ÃÜ —(ô䜄#á”n°óm ū*Cŧýąę8RÖyîó’By0vžQ4`RdĪŋņĀ‚>og ?0ü돰Ė}Ėė– ģ CĩHðýËÏūxûū†v€ÝýY-ö‡Đ{öGUĖ>iÔNäŦûE’釅Ûŧ°âWVÎę~)ŦĮD1<ĻÏ.pžįČá<8Į#‚EQ°€>R8éËXūĀpp*΃ėƒÃöčEķŒ…œ‡ĩâQ9ÉséÐP Ïž ėnSG$öôá L’=}á—PųJU1NÚ=}Īvqú@Ÿ;ÛӇŊÃāƒú }Ė‹š`ÏŌᘟššõæ”eķ{Ī>dXéÃ7š_‰YĪø –>Û|ë}ֆūMBÉQGN".?%S—ûƒŒŊ}p†Đ§ę#rÂŲę“e‘L=ĻÎõ~ŦRŨįðŧæēâF DŅNþðOļߓU~ËŊ'‡>pR$†d Ī,ŠRõU=ŪÕÚīųų9|ŋôwøAÆüĪN§Ÿį‡ÆüpÕīïûē,ëē:˜ĒįĮž^ēņ[VôãņXæeÛķ}ÛÁĢÅã9öƒGx€aļ$Oņ C§ ÍGD`˜…2æyÆFˆé’DÆ) )ØéyœÖcĐØNĩā*ę7;ŅĮqĀ/qĄ`I䰉Ä!gKĖZ+ėĢšJEy’Č]Ō$’þüëaķ·ÍYĘu„ĀKģ‰-c ZjĐĩģˆŠbAUû,3%ŽMĶČDÔjQ3-óc>äôOÖE–â zČÖ]•ž3ÛgíÐ7„ŋŲû 8)ŽėĸŪj[wÃa'ļŧ;Áu‘`!@‚!ļkîūļÜ—eYw›鞖Šũ,!đËï.C’ŧOþÜūĖ3ÕOkvū]ýŠę•ŠÅŲøŋ3“EBˆ‘Ó\%ÞĢd‚ÃfģZī'wQtðļfÉá+OŽĒ()ˆĀHršaĩņŦüPHR4Į2āßb –mĮ\ļãĖmSü+&éÎąõŦ^*üCĢfŠĄãoŸZķp҃ ž„āÃ,väÝŋqîÖëL€ß^e9Ž&á_$m}}gåōÅįŸeijĸãTĩEÄē„^Oču.ūMŋ;ĖūM—$Åęõ:‚dįņ“  ™˜kW'ųųĪņýŧ·mÛđÏØOĮš07úŅÓõNž{^āåëãïëk2X†Â ")Æ`4ęXæŨ°@Ódâ―cŸ~ģŅŊJM?―‰ÕLnĢNĮąIQŠE>%:jŨ);ÁŌ,g4đÓbvÔîM§§ Fw?/w‰4& dY–Ēh“›ŧ‡›Āy8#`9ÃPZûaĸ)›`į  hƑŧqîWCĮMšņ @ZĖIØąiÓÅë—\žōâãŠŌFÖĪŦ8þÓDs@Č=đgĮíWfɒžÅũąîĀŲ,‹BaåŅŲ;ĒŪH$“sqÚíIYÖÂŪæ%‰s^Þ_4á“!CFo:z_at,‰nÚ4f䈉KweZdŠDŠ6æF’ēs`þŽQƒ‡l<|ģz;sÂÎU_ŽųÅũ{Óâï3eLŋaÃĶŊ;˜nUŲ1sgLŒ6ýäíŨ€‚Šŧ/ŊŸøņ˜ĨĪĨqŋþš râï.›:yäÐa‹wžĖą#B°,ŠČxrqÃæĻKį5gQ&“ïžýfØāĐģW>M·Ó4LxteųwĢ#G~žpŨ™lŧBRī#ûåօߏŸ2íZtÆ:āȉڷa}Ô†%Þ:ąõĀåင •‘õúņMC˜ŧöDRy“ķ­_ūíôcЁOÎíß°į'‘*VūzïČōþIšSŸnY=gð  휰KøzÔę~ý63ęÚKšïŸ>0møÐČ!_žyĻÝ)ßüDÂÃKKŋ9zĖâ=gsÄBĮCÛ·žtóØķqŸ}ķįĘ3)(åœÜēbÔØĐ{Ï^Q™S―+Ēĸ`Ūķˆ ”1–Ēŧ/[HÛ,B—°!pĖ?oÜDÏvm,ŧš(īpœéQ eߎGĩ· Įyûúzx!%‘:S á°eÛi―#ûƆïøbôņŸž™-^ËfuÝ?­įÆĮ%ũŠŋũëYũ:Oœ;ĢÐ\ĀHUåfðĒ$B†’=cÏëįl~Ö―o›ĖGûûöŠŲļw6Й,‹ïDmž4}QÉöý3Râē•ę +e>þtōô Ŋ†mÃėËĶôũÜuĢ9•ļýĮđɂXP­É-æ.ÏOÍ=yĶSŚ5āÃSóVþ•ĸŽOĶ<ũčÝĒꉅSŊEũø~ōϘŦÛvgWíŅŦuâóKß,ŧÚ CÅâ@t>ąŠ™Ņkŋî·/§õĻ.UŒŽ$Ëâĩߍ[7`HÛW'Ķ-.(˜ōQ{–…HËēŽí_uޛT§UÕ;ĶÏĪtÅFķ%ŋî3ė„ĐTįÕM Āâ@DVķž&HD9ŌL9ū FËjū™3gÎЭ\[Á|dИEaĩÚ[îßČBĩĀŊĮ&rŌë8ŦGDƒÐÔ_ÏTũøūÕ Ŋ“Į>ūęTÖþScÎ>"舯•37.U@íœÞ.láũSwœã#G4‰ūų"GVĮÜėóK‡ŨžßQ7КõęÜ%:MĸQKŸĨĨßÏZtėvįA}nėýX€†ŨzrũōÞŧ$ˈ —TūšC–3‡Ö›ë…vĐëŧlŅ7;NáĐ_v"iJedĩą’™aŒņfL™Ðt|Ëâ$ÆâÛēī-=9ýˆÔUlT!eã‚QVvïôķ!ûíÜûSb―ZÅēŌ’Î]ËĐyzųý…ßŧįbƒ>‘ũŸĶdä+ hīö…ÚĒ- BŌÞýîŦ—ÓžQĪ‹Ëž0Išg̓ÅóōtœGÓĶP’\@Yðϟ~mKuCF…’ĒhkceīĸÅ|;lØá“Å“ëwŊv3õaFRđûSQųJFw'<đũč@ŽĢK('Ęëý"Úķ­}:ëUũÃÍWwEíĐ5`ŲĪÏz%fFN^p"úãv* óÓoÝ9ĸÂĢÃÆĐ“ýsnÆ<ļ )2ųÉÅGwï„ũjKTFjúđI­kŅ“5úLųajW­VbJÔŠWŸu#OÞū}7įp”Oý&e-wĮĪãac'MRĨÓsÓÁ‘6Šd$h‡ųkĄ”ģDtáŌ@s4f 2’óĪšĩB‚-õâ-„Đ™§š!˜3·ŸGvo œ3LöôgÛöå{6w3zļąÂó[ÏŊ6ôDû2ˆ)_NÝ&Üaw@šlRÏxïŽįþ]ÐåŲG2­=ƒ}ÜAĘÝëŅS…gëlŊq_|ŲÐ?/į§Ý üŌ߀.]ūnfîô\ldÍf‹€~uãC„˜O–^0geĸÆūóG―ĮUïë.ÆŲ3âÖÜIÛ wK&ú~|BĄ_ee&äÜ―tšÞð}S't―mŒąė”ƒôëÔŪŌķKÛũŸœž°―€kŲŪFĻĒ @RrnüĐû7_ŧUðņtįdáÄŪŲÃÛ|ņųøŒĢ'üĒëø9‘íë€Ü'A’Dh  Ŧ%3)GlßĒ”ú°_ą~Rþ|―ö=ÚÔ)΃ȷfĪĪpĩۖõonøH’˜z―[Fß{˜˜ü0. · ąLø7ųvýŌŽÃ&Ū―óâĖãčėā_}1IšnŠ™ī)˜øQQĄ(o #äØīŲsų"š· H:G—ŪūŠvËÉöúnVÞŅcˆ$ŅĄjNûG% ÔTžm€mšˆįy‡ƒ€PĮq”zj3ðKv‚4•,é­Ģh ‚ˆ°*'b”á <û}ŧfÚčÁ:ė@„ÓВŋŠ$MŒÁN°zã\ŌE@$˜c(,Đ iķ[Ėj)\įđÐčL˜ŠĘÍĘcüŦ-XĩqpÃR ÖԁĄ!Fˆ5č#5W1 +ÖėØ―†eþ7ãöD ―#‡ée hšbI@ēFwÂĄÕëiQ”ŽV!7#‡Ðé95jΜwՑßš3īÁõ-ßô2ïnĒÍ 2‚’Âķ6{Ųį‘!n€įeŧÝnģŲ‰' …`Ï[>ēKKÚaVÉĻĸi˜­ŠSdŒpá[mNM!ėv.ĻŌÂģÛWņá‰QÝbiņfųËÍ ,™·Õ{åÞģ„ÁŨÄi9nþã°  Oˆ SĖY‚C o?jÁڍ•uđËŋųxĘú 4gP•såĖ…F=GAÅ&hiEJ5ŌØ/äôâq_îâLėîÀ_­$„Ų&*d§ŅS~øĒŦ`ZgbH!BËëĶTŠÝGMøfãԁ‰—·õéÝįôĢx °ŒĩŦHâÃęMXšr[ŨrŌũ“FFÎ<ÄÞÁ!EX“—ýņīÍWXV=ĪiPxĐdé`7šĪICŲÁ˂–…@āeEŅBøPÔå $„Ä―û=7ü@:xô‡Ÿ’tËÉōZ2?ĸčqDÓÄŋœķ4Ģws32ī†UÚT•ž%kŽôzƒŅ` $m4M―Á`44D’Ē:YÎ`4ęU^IVXŽĢ–垊UÜ3Äj“-ˆóÖC‹ÍBŌ U+ŽŅ͍!ą Ę4ÕĻRŋC›Ëũ8x`ᙯV튗ĶX‚Ē ‡HąŠ5ĐŽ#‹—Ï›·riLŠdéjÍ+T.—ĸō!įW˃ēedŲTŧI2MjļOŠŽõz€‘WņrĩÚķ‰―û˜m1 K9SHM―ø›ûnßģkëŌ“õë•Ŋę­ÎÅ?yžxÞĖ•{.,‹HŌ™ Õŧ{ļÛRãΝŋčũAãF?Čįh ĻŨõ#:įyvÝĮ“ËÏ- HĮĘ Ãú—Ū9Ļ‹GĖŦóv#ģē2―+6ŊËåŊ[9{íÎĻ#GĨÛIwCAęëûNSauûP‰ÉÉÐč øė„Ôž’ÕZō‰/|3sÆĻ/O>ącE~ûlGFękÂÃCGËIˍg2lHÅHQd‰°ð’$KÐŦeóāb~ĘÝØ\ow!+93Ŋ %)3‡2p$ķeŲâÜ5Óa%J–9ētþŒ)_ŽX!ƒŨPqĄ―ú6°Æ? ­[ŋyĩR!§Q…ō jQŲÛ—dÞnLZ +ŸąrÆ7·C:ÍøēįŅՓÖūĶ :ȜzôÔuÅ―Tƒę”L/`šÕq4qõäąkRž]<~3ÝRąNĢÐbÆD›U)ģ;'%“3si =Ëĸð^ēUŅ6‰XĩÕ#HEY›ƒó *Y†đmú·ÓgŪښe+ZéõŸÚĒžĒH;wyŽ^F‰‚6&ýĢ$Sī{n–ŨŌųæ3gEýËA-„E―ÐBmųķļ§69ĶQá–Ąĩqˆ)žFӆՊ“Ð9=íQŽkĮÖ5Ã}ĶË7ęÕŠasĢÎ―ĸ·Į§õo|ïԞ-{Ž$dåęXš!IßЊmšÔ4qtúųÂI]B.‹J"Ë-Ýķčƒ NïÞēm 7ƒwįž#Įöi‘›œëW§_dĸÃ9&čƒK—uũ;spûþ“ŨdĪPFß&-ÚV)åõ& ’Ō<'0ÖđGø…û•Û·%ĨHưÚ+ķ.nZ\Ø―ûļĐÍĮ‹fN u§Š·ŠZŊdAAÐØąúwĻčÁ`‚p:H܌Ž‚W[ÖþðØītöĮuJų50}ҘÞYũ/ŪßžëUZ.ŦwŦÚļKÕrÁ4į;xöņĄÃnØ―}ĸņ,sūG‰zK6Ío`=―oÏÕû1ˆ€5ŧÎė^ŧĘՓwó™ĘK·/Ŋáe?°eÃū7s-ļBŧOūߍČ}ĨkÚqĖčÎĩ˗f!*Ü%Aē>íĮĖũæÓ/ū"‡§3g&Ĩå„VlОV8KÞĄÛķ­écĒž=ĒãŒyßÏņɰyÓķģ7ï1ūa‘nIĨÜK„‘#†G€WYLąqĶtŪã‘jWš:ļw§æaĀR5?ðō,Ũ―C›ðóz^äđÞSf|ĸyÏĪ['6oßw/Ž7gÄåÁ y“ÆŒ:îÓȎÏž*€šZ [Ö-H’dNęӛ֝›ŋĻeĨ Sp&2bÏ_Ęôöį_9ōÆ―týaJ7#,\—,Ó>ŇtlB$ÜOcôQ8LHķ j5ęØļDēGąšÍZöōũ ?n ķ5RĖĩ; éŨĢCéƒK;ī‹(įýĒėė댌ŒøøøčččëŨŊŦ'9þUšsóō2ģē’įĖĩE”u”-Η+ųį_R™bđ5ŠÅnݖ™——››û§]ĖĩØxŧĩāg‡Í·äeįäZėŧ͒›ĢūÍģjŦs-6Ŋþûk“ųfŦ`·ååjĘģĐ*›ÕjÉÏSŧ5/ß öü\MŊ*Ļ^ģÚŽ{†yR.>OVŦbģ―åF<srÔvėŌz[ĘX_°vMA•*îþïr :$åÜK'ŸŧŪۘ,õŅđ•žŅ™ƒ[œŧïRuĀÐnĨýÝē^=8u3ūJŋKŠAõ ę(Æ]˜8îóXĄō§c{ķkWũÉąMgŊÝĘäJwhÛĐiēīO‡ķuRëųÐYÏĒÝōČļtô1ëßŦsŊJÅÜøœÄC{öĸô ÖXŽÁð{„ęģü ËOGœŋ~'‹+ÛĐ}ĮÆ”Î}|éôýĖōåÎ9l,Y·[į6XCũâzM! J~|ņÔõī ý~ÚXŪØ`h—ޜüâæ™ {Nšló.[W"ŋ1øX2ÕĄĮļņĢÛdÜ?q*ę\2A7ęŌŋuíJ:D(R)·o!ˆ„ûŽŸ:t^ãé: ]툎ŧĮĮŒ›ęŲ|ذČŠ7ŦU[ô‘ !dŸÛĩĸÔĨþ4ėÚĐs)?æÁŲĻÁĢH=|öVÕV:4ŦHýÃĶ(@RĘÅõ>[~wĖĖeƒÛÕäGFJ–ÉÏíŽY’|iÏō 7uÐŽFļŽD/ïž=žóHæÚôˆlTĨMƒÜĪ''lūōJĐÚŽ[ŊV5ÜX*7éņĐS§Ū܎)۠ˀÛxqrÜÍ#ëwQ áîUĘÏĻȊ37Bdžļ·ËÁØKđĶ{ījčĨ#Œß uJJŠŲlņööbYÂŋ-ÔbEAåˇY–ĸ*Ļ-‚Z €]€[ŽØ\ðķÛl8;CčĒ-,ðV‹Åčį ]=€’ĪIëķÏ'-ļö"ĒM‡ŠžČjÎÜ`ÅėÍą―#Û§GïÐįŅ–ĻE\ú͍óŋ!‹Ũ ŌĢݧ+wôDJRžČēXČyp2Đ\ËæÔŲÕófGŧ­Ý’q{óž+újmË(/,™‡õÞ5ÂÝÏ\ya)p_2ŦûąåfĸTjčFL8 Iˆ&”íđNĶ„·jNž^5wö]Ӛm‰W—ĖÝj*Yĩ›þPtķūĖũ}+ðžĪųM Äû‡~ŒökŲĄUVėYßl(^,ČCþiĸÕððjuØØ/ŽrĸðĢúá~H°b…Ëy­čK ķ{ÏR ejUČđųýčĖĶ՝ÂiĻ D‘íwcŌ ek•ÏšĄōč·Ž,Ï[ō3E:ŒÕé)(ælÝ·Wø ĪGŦāĢĮíxnþðÃ6ŪwéĘŌĩ˓nŽųvWbDšōƒ[įŸe”Đēūš?åŅ/UˆŽëg^•.ŨbpÏÖÁīÁ î^~@ēŅŽ.åڑåB†1íîîGDԆŊ|N}2rĶo“nUMųߌčÛmöšŠR_úņ5đjŋF^QóG_}6sÓÄ&Ŧ–ÍÞrLšōEG’fŒŪF7§bŊþdėŅO)Ŧ§32ÂÐ*0Ø<ËŨ NZ5s&VŒęUƒø―XīŪ‚ŽŊŊŊ7Įý}§ÅTĮâãÔepnnÆŋfT[Døn° (;ûčĖͅ\ŨĄ@Ļ E5 tåļ_HąbĘ―ý4™īųŧ~& .ïÚXsÐÖĐSú%§Œžuâî„>zšA“ũ Né9xT\bAéõë–3—lŲ§KBvīÞęÁËDŠņS—ūLĩ‡īčp@ I˜%ŪA› +f4^ØģüՔĐCšKYiY\―n­07ĒpЉs/ÖaxŦ‡ŊiÖ7K­Ē›ZFŅXįÛkÔĖĘĮFt^`퉅xĻyNHēč€P6ƒ€!Ó7ī{1ąwĮG‰)õ*čd ĶĪf0 VŽáéŊoß·æƒÛÞýu pCÍÖyø$&ÅN‘ržUP FÎûyŠ 1^-ÔyøôYŠ†RžŲŽKÕn\ĩNXÆ-ûunBå\ĩŠÐ|ÜíÃÏâ‚Ú}7ųó.ąå冏]|šY‚’<ƒ*}ŧh-qņû‹ŊåŲ$†âýž,@5€Â Žžƒē„ L(ēŽå€‘äQŪųĒ…3ō/Ík3?Į!ŊšïÕøČįSxeūŽ―ĩjĸõZ8uN?øÅČ~Ķä—KķJÝԍž-'-whîĨS.Þ<MkęDåš_=xëõGýK„›ģ"|xåųÖÛéđĪ›ÎbķŠĐõæû‘DÐ;ÉËËëĶ)gBĸ\mā.õ,Pĸƒ JOŅ€ē7ӒAaŨ4hķ°Fš2W#UFVĨhڐ€PœB˜Ð8ķŒRI*ĒRđdI&äÄH!‚­"ĶŲĖ[ÛGM\Pŋo]7=Mp$‰‰·0ƒ›{™&𠠁ĄĄãØý^᧏œØŅ{õöÏæ.›üaYAI§ÝÞ9jԚ }k›ô eŨ‘$Âc0øøc9FõõŸ@o Īxû…„z3ðÍŠLø—ož`ώG­ŅtüxïÜÏ0’TŠb„ĖčoGuģUéßēž;K ­…íėą7Ę!EņéOūÝÍVu`‹pwŽ&XšÄX–‘\ȅ–Õ7Š""‚ )gð d,ó"(OÏŠĄþ†Ũ ĶĪ -ųEo0Ō…uČĄÞßOŽ}}áYfJ^žCŠ:`„Lþþ^]Žâ,pI`EÅŅ@+ŽËčôŠY%aŌĀ@RZ aE ÛˆÏūŽĻyyĸŪm―7ü°xϐ$ Ŧ Ų…Ru:oĻîÃȊö… Gîķi}NÚ*uk_ËČb†"ĩČßŧÃÉ éïé'Bïî[Ôš†}ĀųÂ.%ą’šJh5Đ(c%%ČesïLH‘uĨë/ąåÄܙ%ĩ}pPD•*;Î[đeŧ·ãÅÕs™lģÖՊKą’ ð’„Œīų Ed\Zģýd…*éŊģø`1ÎfMK‹yúžŠÖĢEjL oį^ąsPDËų#‡maU7ŪđýþÅ›ã-xŲēŌ_dōĄ>ŽXŧSOlE#v‚$)@ÁŠĒō]áœÛąģBķÚEPÅUN}qįڕ—55|zlÃKķI/ĸôũwGíPžĖČËRŸ51ČÍēžžxårÛēĩEQ;žÁ™öŅÂmyiyYþūÞdgY^\ļ|ĐCÅFîúSgGÕ k%QPÝKŨĻpęÄĒݕíVn/Q,Ļ^…ĀÄóvžwĄĢš(âėžĄ+ŸŪŲXÆĪH ’ą{ûGo=gʘãtŊ.î}Þ|X„äBAB’ ‡QlDõÖA?ĖÞšĢ\‚1įލĪÞÓëTĐ,Ö/ŋhöš-šôāCũÐí> 3Žūi *ŨļVås—ÎÛeķrĩfeҜŨ™ÖFÕJūzü:― īŸK`ŒeGZĘKX牁ÅɖųÖųĪÎՂ-+ZðŒjÖŨU~Qj–$PĩÉĐ4€ŧ*þGNŲFēLzŸ7/āĮ9į/Mæļ!ÕZülūŒžÞŋSō*>óؚA\zVÉÆ-šx0„žõ›4/fr(úö̆Äü°ûâ… õ†v›Ú*îėÝknmŧ O*9OˆRe[6mæM]@ŧÖMŠ•ņRŠLínČԐ4ȎÚī9æÓqÂė}ĐDÝ?owéîuÏvöO UO‰r-›ZB―(Ú­Xŋ6MJ—vWdüö6^ŧ%ĨŨS„g`D›ķœŊč]ĩq·Ā’Å%-þęÉÓŨICĩČ/Ž 2Q†6ÆŋZōā§CõëLí2lŌé;ũb}[ÖũYėã,G―ęu›å•FYĄZÝvĨËúŊÖičÄsŅwã|ZQybf‚n"{[6ŦhvĶN…5jęP˜°Đ‹þļāāŽÝL`Ãyã?ŪĀØĘ6ĻŊŦ‘ėÞŠ ég€–2Å‘QObŒÞˌ„Öėĩ-xŚ­·Oė@b%°Zį·Ðˆ:MýKē4.ņÁ‡­3L:*ĒYŋeóģķ=ŧËŪïúÅę!=ë›X~įŧßsĄô€Ŧz\čߗ.$^4žÛ)ïäėîZ_Zü-ëĨ ŧâoOEPëBĄˆsN đœ@v씕ÉJđ9ؒ‡).›C ĸĖæ7]ú坋bŋÃæĒž?fä·J] ōwÜþƒÎšÞĮøwÛÔÏïĻü}ÁŲĒ\mÂü)@Āu  Ít'%UŦČIɄ$ w™ėBø“ˆ8Ŧ „ä\…1˜PÛÔÆÂýõjíęõĸŊ<Ą%ĐÝĐĒe;\í-oŪ`üŨԇ@ē‚ĩކ……~Į2ø·%dŠĻĻBáwy―)šŸœ BIM!dz7 *?Qs…Ę~vaÉŠî$ÚÅŽ§WoF'YM‚ė˜óģ'<|ÚÅč;ϊ4xIÔ‚"‰?GĒX–āŸë'0 óŨ4ÃüSų’ŊîžþrôČĄĶ_‹ÍfŌ•į|ą ũöå‹ÉyV@Wp™aYšü?8IšNˆ>ģzåŽT‰ē%FŊ[―âblĸ9ÞÂ―Ë$Ðp֜üāĘ­û9ĒčxŲ"Ļ-Ē9Ė,\„āú‹ĀXJNVeĨ”TėõÞæ]{eckܖ…?ŪŠģĻüĩŸÛũ€Ŋ\ŲïøæoūÞr%,žlAuĢÉôËųþڄMëô BýĻ·ĢãhāžĖpËÐ4Ŧ3 IŲŌŪÞx"QzõÓ[|!”üGÚE“ŠJ§WM˜ :VeÁNĐFFU­ãTšÓ݌Ŧ6rŠN•Ã)bp3Xštã*Į=šŸ#ęXęí^-Ōŋrþįį_â°Ŋ›ĻzÂi"ŠŦopŸbXÍWVoÔą PŠ"Đņ;æÎ}˜œĄÝ!œą0œÞh4ÐtzNĐhrsÓŦJÔā°tïÚÍø‘e(Õ$ŦÓkQhį€HÓyOïÞx0_Ē”œį{wmøéIEQŋæQ]R,™Ũ.ß)@ZŸe=^3gý–;õĪhƒŅĻ2vŧÖąŽ;UčŒîEô?˜@("Ī―0~—ü)F%§i‚ĐéX–1M―ƒŽÓÖĀY ĪïYŋįV:*FÜ! Ėb™ņëŌąarý`ōÂĄ­GŸČA5Ų€€€ī›Éî~!^~Õ#{ŨásâwŪZwό›vКn„”—tęė ï2Å_>MÕ0ŽšņČķÝG.ĮŨlŅĩWį::)ïÜŪSvŸ2yŽp;wЧeíÐûVOž―ÓŦT‹ÉÓGÔ XXū~öāÅĐe[…V*Ó°]풖äįQ^}’W­qó;5ōdГ‹ĮžŲÝ…û§r‚?ëŨÁDbBAīŸ_­î‚―ý1Ÿsôč " ™~ûâS[ĮÞýj• ,H{~ðÔɋ·Ÿ•ĐŨ)ōÃÖÏOý8xÂʀæ>2ĒuYōØÖĻģŅIĄektü°M…â’Á$H-5Ã@HéXڑä`ÔđŧO§í#<åUóŋ\°ýFĢ!̧îëÚįÆ9•ÜH™ÏnlÞ}8ÅllÛĢs‹šåĄ#ĸė҃TņrâÓ+?ÅäuQđĪ!ĸOžC[ĩE[õõNéR%ņuþÕkō‹į$Æï$‹þČ.ČPü†é_ŪļōēEũWŊ%[ī6äLđđaįE]åú%É Dƒ=ËR`“ –„)Bnō­}ģ‡^G]›·Ï9ÚōCCŋäS›>―f-Ģ+^ĶoņīSŨny’ÓūmĢ―+ŋLfVӔÞļuííWBí*þ1Ï_=~AŪ]ÔOIÏäH üžƒ+ĒÍ,I‚"ëi@+‰WŸŲ„āš-6|jgķ~ŨģĘõËGfŽđ\"ĖģjËþŨvŒšķô~ąšÝ=/Ŋ[}&ŦËÄ2Š[§õú:ĪbƒPfÖŽ–ŲsŠÃ"‹Jþer KVÉacl$‡%þúÔÉ_ڃŧ6ÛŧōŦ —†­[ųúĘĘow'z— óŠč‰0r&K)ŲĩyŒhú‡ã؞•į^3+•É|týD’ÛՕ=W/ýnC”mâ”N4„‡,› (!Dˆ@$Ÿ—,põ딏ÚķęĀkáüēā7íĐqÉ]ƒ:ķŊ:˜(Ÿýš―”žÍa+Ā0?þŌšOĘ5nÏ&îëýņēŌuÚDxd|9f\ōW‹‡tðŧūũ‹ýņA˕Nđp&žZsÎ`•pØmēČ4ėŌąöW;a…ž#>î}(į§ļXĸ!Ã{e_Zŧûˆ―úGÞ^ÞČ>}-æq=_ņ|Ëū“gęŒŊôXt/7Ī}p ‘—}dĸýé­jÛeÅ#žûÜÍï|ŨfÎĨ iԗm›ļß>ï1hPß0/YtBĨhÝķÅÖ™múö­āįQÐ΁E+!ņ% uũŸ§HĻ’DHžąÆž­Ëę†ĒŊŧėf―kÍ^8=āåŪeg—"@Ĩ>8w8Õ^đzų‚Ô/Ÿ<—žqTĮ’KNīîÞŠj1ŦģH †Låzu+~_Ąa§~}[Ü_?ėé“}–­úīMh`څŲ7w?MîÞ›_đ•+7Gx3Âļ°cą(ÚŽHÁH–ƒjõÝžĪÏėOĸ˜'€ƒēŒ^€;ķ ww7”Ŋ_ŧt ―Z›žę•Bšę•PR|ē‘YŊģĘĸąaÏ―DÕy Y/Ó%C`ófĄG<ęÕo`ÅbŅ7Ā Čw­°ë=>™úyu·ôÜK―zđcãŪ„ÃRĐųĀĨSûøūßŌĪ ŧA"įô]ýŊäj‹pVAڐM!äę K Ãtëé;k–Ū_Ī cäē8ĀČjÐ4ýNKDŧÕYī…e(čdT˜0&€FŠJYÂHF Ąūœ+ú‘F„]ÍÐŦüØĨŧŨ-O(‚‚ý#ʄs4ÄÎ4Î3ó 9eÆw‘u°Ž`HąeËIŠAT5#cgzöŨĩĖUQ‘Ð|€”˜ĩzōg+ŽKŠc,­ĢīŒ.@/æ­CZWĐǐ!IŧÕTwÂXF8ß žĘÎYüÝGMŠņEiā/6КŽ9‰‘zEoā‚Ņëœ{dUíîQ-ÄßT86t†Ģ’ęgĄ­|BÔPH„Ô&,ÓGãgmZ>(åęķÞ―zŋĸ“„‚ H’zFÚą~^ŸČ•é ëi0čt4 ßšņÃu”ļMåžZãŅ9•`kķ!ųvÓrá"Y…aĩđDƒ;dQQdYņ /UŅˍVų9• Kæ|3/ĘøŊ­F/ėúŋC!ퟟÏĸŋ9'ÃŲRĩïßB/H’yšY=Đ(.Ū P$Ī1Âgöl·†z͜e˜~ðxzūøËO€4ųŽîß›žž”’E™Üõī%ĩ ééÃÄ|Q‘>O;|UD\Åž9Ŋ.Ðĸģ "(%°|ƒúd^f"įĪ#lI) ‚ÂPbę“ŦOßK~›ŠÁZ…r,)LŊŌÖÏ7tËwŦîüqé…lŊĀå‚ ‚`W#)Ü}UČŊݰâāUŋ5ˆtžÕjWÛĩ;Ôk§ÎÝ&ž"šÕŦ*§ÛŽ6…f8–·Îž~;îeR’]ä―°ÍkyôčqĒ gEr„$Qí!áe|"Ŋōx#ŦkyøðQĒÍ`â°`=}čðË+$­ĪšLVhÔےķ~ųæ}{·î|ŌŽZu_ÉÛÕþīŧŒŽj•…ŽĮ·îēáČmĀęĀi‘,+ˆ ÞđÐË_YŠūBB%öáí˜4 âsnßļ—g—Á››D|öۖũ*PD4IÛ·Oāųb;ķčEA‚ŋs: –eähėډÔë1Æ$˘:ķģDíSßý;ķJ1đŋčÞŦtóæ&ƒBāę_0’ŌũóņcåÅĒfķé4Ðëy’'StĐĶë‡zӈ0ÕjŲT*E(J@Ųęõî”,č‚j}ščžpÝÞMk!F]GWd‚ýŠ5lægÄē$Ŋq‹ú­ĸņėŅ·TķWZ_ŧ^#s‰$!ŋr ›ˆū> öí0íÃGËoŸ―_Ŋ۟Ŧ!—‚}#jENxøöë ›wí:üųúCŨžvúhøđˏãsųВ›Öņ$ …Ļ6ãŲ―7œyA6ŽXæBĖQõA5n]úã;vþ°JÁžĩ›ôÅú ØtėæĐŧąĩĘøÓZVŅFïzÍ[ öBĒāVĄíĒųšUË6mÜrÏXŦĸ—c#Ý É·lƒšl+oÆkŠ ˆę|ë6kâÅaŦÔjäę)t― r Ę04ĖH|xųč1’`Fŋ ÝōŒß€~“w^xzðlވAƒlķÝį/Ä4ę6šņęQ|në°Š 𨠄(sūĩë7 hзžeÏų ąŧ Īožūĸ"sxãŅý;‰ĮoÜxXŦiĩâš7"=9\šÅĻußÖï;đ;–é4iîĻÞõH)­X•Æ\ˆ'RpHųš žCš.Wķž‡/”ĸÎĢ›ėp8Ę Ģ%A HįHüūūŲʆDÁYŽ"ĸė`“dđįĮķœĀ5ÖMmŋkÎ4šÛÜ/šæMŸ°í‹5+šTadŠ!ōïL›°{ÚÚå+k-ïO „ĒsBĒ$åäåe8škŧA°Kĸþv*ˊPāÖú2Ĩąsģ€#3+eČ`ðäw –F(ŲË'đï€â͚úü?öÞÜēĢŽó~/UëēOwŸū§“4äΝHŒ$Ÿ ˜@DQBÔ ÃÄĻ#qfÐpPĮa"‚ŒIĮoĖD?ýž ãŒH€ĪӝĪIČĨÓgïuĐŠũývUģsŌݞn“˜îŽß>ÏÞkÕŠzë­Úëüw­wÕZkkUUygfĶĩLZð}Ó^WÁ·­Ģõ UģīŸGŨŨĶï°^Ļ5ô]į­,kÓī Āl ËA„Čhč@YUū›8/ˆ€hŠē@ ŠĖč›&Ý4@bMĶYÝĪ2eebûDōåŊųŅ;Ļ"Ád›šā]ŊČ*>„|5’-wĸųŊžĸwoÜļnaũĸýĸîîßó‘ūâ)š <Đę24ûé 6K œ—Žœ€\FęšÞKēÅÖĀū}KS @T§4õđWˆˆïšFëFĢQéÛÆ( ;í"5Å4ĐĀ0n]lHŽ7ļŪ ē*@Ņ`ŒAĖQõ} [Z˜ÜĸÄã↠āŧ€Ķˆy(ųz/TVVÃŊ`Ŧ’ÛĶY~š(ZŌĶëU@5îflŽ5Æũ­rąnýHšķw!GfĘĶ:wEoJ("A"gČ~ŪDĸs"b.2Ÿ’KønŌŦĐËč-0Sī6k-f#ËåR"„ļP]—| c)g[.‘}KĖD9ŋå„^ŅmŠōĸûýOÞZž|ņEO}ßĨßÃŊzïÛžßžäŋũ“ĸö―ÏĘ:„LvĸÁK~äšw―ĸįŋåÉë―Hūz‰9 ―JðA† sJˆĻ*Ë­›6…—į.‘'|ōã ͒'†ƒ€ÆčÞ;îûýÏ?ü&.Ëāý}ŸýŽĸŌ …1 ÅĮâæ[/đôÔ .ؑũČÞĨnˆBĪāŧvŒD1tKãÎõm߇qÓæ9§Íøū{;@eYĀļkš(+Z”•ĩĶYšŋ4NÁE]bŨĩ„•GBßvũß?ŪŠZÛýãķïÃŌØÖUIâûŪï‚pQU9ÜĮ°ą"šš.5ļhhęĀRįÉZkŽ[ũÄoþæoÐ[öÜyús.ųĄo{ÉģŸīØų0uxiÜ ›ķ+ xYÚ_jŌėSP횉sņŨĒŠ,aD‚·Ýīšūï‹ē. n—îÝŨ{UīÅđ‡ÄOöŧØ!^‰Ā{Ž ›Œũũs:išŠ* dj žj) ĐHÛ4hĒ’ö]'ĀeiÅ·ƒŠïïßwOðMbqíûĶw‘ËšÖ‰ŸV' !h_W\4>WõČö\]œ[SBߌŅT•Õfiė§•đ˜ÅÄPŦxïÅX‹ ­ ÆJöŪęjYė$t]ë| ķUU1oŧTÉ#ÅŲÆ†|·’RÆN%…‚HP 2ž‹;†ŠMs]›œád@ÛŪwTĄŠŒH@a p}ß9€ķĻ ËičŧŧÅūÅ,öÚ· pa™Ķ%Ð4wI4ûŋūg—Yˆ|éqÉUũßĸÛßöéëķ=킟yũÛÏÚą~Ï?þŅ/ūýįĸū)_þÖw_þâģ(8=JcĩEQlÛšeû+_đį’KĮEÍ"‡ļTŒ-íûāŊėšâŠ;>úŅ]?óÓũžį―F―ވ aÏâæ›ŋį’Ó.|áņé øp(hHaØÖ1QÖę8ÚmûüÜÆū‰ Ŧˆ\ÕĩĨŸØ·­KĸoL5%—… ]õ"Ðöu6Nû/0ĨšáÔĒ*ĀõMӇā'“Nļ,,víÄi'c þ‡•PÓļW‘Č 1eeAœÝô”WŋîMo}ÛÛôM—=ûé' Š"cT6kō*paięA2uļXVĨD)Xų—$"DŠÃtCĄïšŪOŌĖ.ßĒ7ŠÓÕÞAzĻ"Ļ ‚ôÍ$ Ņ ïâgQL(’Į‡Þ9BčúvŌõABŸrBRų4F6ūsÎ{7­šƖŌĩˆDÅ jŠ‚Ÿ4=™Â6“ÖÍ2‚ŅoĮž―Ē-S4žiąeYpė_T‚ >ĩDƒũŅ1ÉÜ$[œûŲ%SÔUĨūÏB,ÁũÎS:Bé&m 1Åå”6Ķ(čŅR$ˆwMÓ›ŠŪ ʊqđ2čb…LSL™ęõÞïúIįlY…éšÆyQ;Īē…ŨQTČĻ„ļ{›ÖöAdšSŲÞóÕŋýÓoSƒîåf7ýŲgĸĪý…ũĸâÆ[Ŋyóû~/ī·ü؛Ŋ}ũŋųžW]ýs?wýw3ÓŅŦ@ÄŠ(ŽÛē…_ņĘÝHĮüęõÍÄãA‡˜%öáSŸÜũ_?ÎG@Š)ų@XŅ[ķnĸÚkŋũôóÎßąukY–ˆxD‚hŅØ‘ĩÁ!ĪCn'ŧŪÓDĒ€ŠŌu.zUÁXãZŨŠ”UaŠsÄĢÎfĘ.ûÎl um+EQ!(0qT Zú6Öb‚ė;ČĩUGcWšŊ€DL‰Ų0čļiŧBįļĻóý—ģFĶ)‚@EYWŨƒH4‚—VEEHAAb Ņ.1“!ÂÞ@k #°õہÍō<ŊĀĨ5Æ(ƒ Š:ģÁåĢ)ĢįLģ#^6Üķ>I6•é’W- Ģzd5(vļŨÝ ―v BÖäXGĨ|ï%D=ũ ųÆ·üĀYũĒ,œ\”†ĒKŌtˆŲ&=‚i6y-Ú.ĶÆWîĨŠâ\/ATó`ULY66đ—(ŽSœ"8AļBlpTMÂ)ŠeÝy $]bR$&T‰RŸPĻ2™>Ę/@æ| žKįîE6FÛԋĖĻašāŒ˜p="„°/lþÖũčĮŸĸäõÛßxÁkûŪ[ŋôŨ·ÝtóÞŦ?ðĨší†ðõû&Š›ŽnĐÔķL‘yųËo9þw>ķ~žä™ĶķTLÉ;úAþķÅÍ_{Íe§ūā‚ã6m:b]Ã|ŒFˆ€â&Ÿ‘ˆ˜ĘÂĶ%ŌāÚķ3EÉĻÎĮšØ#âūcĻ…ŠȘ82DDà Š_@ČeUąëŧķCâ’ŧ@ĖĒDi(JĐ.Čb_ó${1;c&“†‰G†qþV‡Ųn‚ō|ĩø–·Ĩ›ė211#ĖÃü‡ŲBJ‰úIŒ˜sDD%z%A(A<3H’{s_Æ œIa‹šã@°36ibšēœ)ĮP˜mQdáGȎįF+"ŲTSÚHęŽ)$"‚Ķā7NÁđ+ c“@rGŜóCZŨķ9íbiïĖTW6KĄčõ<MŠhÚŠ ÁũmŨE) y\ýŅ"‰Đ ĖíÍ _e]‘ČtM+e]§gŽOĻĻģ ŦÐÕkõ4ŋ€Ýŧîčyg―n#Ų/šø^úM§XZ|ÂiÛBĮÂdŊ!’°uóæŊxÅÞŨū~ĐŠMúOÝĄæ`p·mÚšįâKO›Æg7mŠŠęˆu0ū0ļr1Ɇ ‰<Ų6Eé‚Ī‘S eŅAČĮß·§ŋ9ï5ft)&ã !4MÛöņX;* ۂ)x/ @. ĮdŨMģĖäģVâėģ( ϐąLŲlӀ68Ÿ—qÖĀ’ĩ5äĢ`QYÕ đ [ĪaęÛīþh įíHdØÄ`ítKŨĮv•Æ <`P1đ—ĸf "[ƒ‚h Ãķ@ÖhÅcœ ”1–sÏŪÜp sí)ļ!mó!ÉŌ|j&ķ˜ĒŪӗYcHEú˜ä‚ df­^ATó—BPQP˜–‰ßCÛŅōIĘå”všbŠ4|ĮY‹­ĩ–5uR Š$ÍÕ ņ?E™8ÅžŠâōžfAýī@Œ—D‡™ó gNŪ ›‰ 3€ĩlŒa(LôB|›gûÉØ{A…É$øų‘Ũĩ7þÁ/ýōŋųõüŌOþî­―â[N8õŲ―ėī?üÝkþöþþóŸûÃ[Ā>>dWå?ī)äĐį„1eõA0Î3+ŦŦÁtš†‘ņ°žFÄÃi4lÊËkÄU ĸ”{xH·ÖþÅáĄ[uč*TĄ(+Ā äú|Ęj{ˆ­ R)yëēđųï1…mŊCč›I~āMÛ˜GÃ|JF%&W‹ŧsĘrûæÍüŌ—ÞūyóøÖ[aÍðЧžņgOËÎ΃=VP…ųG‚Zw˜sôČÉgĒá‘dS'$eā(‘Úa'.ŠbËĶMö/§ļð=ëëzĄŠ<ža“™SGnayú:B‰øāķb^bFxäĪvĀZŧ‰yãÂÂa…ÕîÁŽŠw]Ðē,™QvķMÕw­ŨĒŪļoZ(둃…Ã]ÓųŅh!ÖöH300HíæCéY’x^žFĐ+Ó0mÂåÕøŪŪk=p]• úāKéU4įEAPQAĀ•™–ģû2jTPó: <>æÕ(,?þ6ô}Û6Mģīī4îû  âûņ8Ūķ^Ō „ŪkÚIӉHÛNöO7Œ'>f<€UŨ·ãýûũO3xɆfēīiŌvNĐ}Ą‚ó!ĐĄïÛϰŒ―syŌŧUUi Ð1›t=0›š*QӕôøÐ­ë›ķįĒ4ÚĶ•nŌxåš,0_:000HíãPoģDrQ–ea -π7FƒkŧNâ3Ōō“DBŨö!IôjåLę‚ô]ëBŠņŠz€Ē°lļĻ ÄTĄF````ÚĮ8æ`SÔuE*MŨ+䍚o ÛõÞXËÄ é•Áü†Ē"=6<Ý&Q“âF$Ė”]dPہAj/āL#Wn‰S‚wÞ+ PJF&Ÿžņ"ųŽMI+Ãüs}ģķĨ!ð>HĖ#€d˜šDÛ;՘+‚› pŒ300Ė@ĀĻ‚ķd Ɵˆy‰‰€ %ĪðŽeD*J Jķϐœ(T ĩDc+$B[–@Sx4BįžæĄ2qUŨÔĮ"u=B6U?}Đ 66ļ’b™í\p‹ŠUaJÉŽ˜mOyĀX €ŲlÉ&'Š"—Cd%Ĩ`Ģ0000HíãŒŊƒ^öp@t!<ŪĪŊJA8†Īv``āąøãĶ pŽ0HíĀĀ€ˆNœ.uę‚ÂcÃļŪđE&„ĢAj‚čūVöÜö.I딌ÛŨņ‰xóˆ #Õ R;00čėÝŲ}Ÿĸōímw6KK^ĸŏÛ1ū-Œė ÛŠþøZĀl=ŠÕvÚq§ŧï:{Įã­G§ï,x$ÂĢæwQļo_ŨŨÆ" 0*ŲlZ;HíĀĀ€ÜÛĘmûÂíwN6/ŽÎ8i‹eeB$ „@ŅmŨÝĖtĮ]ũŨ ÅÖZŽ áčcÚ…ÆéþN–öw'°ĩë] $„5ƒHĆōC}C~ž$HÕüH aíQ‰d\Ѕõuģûžým˜ô*ŠŒ p2HíĀĀ€& A=@@Aa­ qwÏMŨú#_øß76tö‹_wîYg2õ7~ö·î?ņ…į>í “―ý§ô…3ŋýē‹…Y›Ô"ˆ:čEEĢ“G=ƒÔ (Ļ Ú †ĩ뎥ý_ýŸû‘[ý9Ŋđü]ÍõûĖŧ^ũĩ7ĸÎwžčī={Í.óžģO―ýũ?ôæÛ6ŋåđÓņĐsrþ@čÃ1ĪēƒÔ H/Ð H°F ÉWĸęSŧšS/ûП:eۈžôn•ÉuŸųį~ëŋ‡ÚÔUøÜÕ?scýí—}ĸŦÔđðI›ŽĐFĩa&ĩΰ6PĀýŪ―[ÏÅyAŽWOũĩAC?Ųõåé;žÐ~‡Áā!]˜AB€ÖŦĻÎr(ÕĒ;HíĀĀ€h;čU‰ ŨöØ!dF1Ģ/Ý|Į7ėë7•dïŧņ/ö•ĖõÂ]ã…3.ŧjÓ?ūãš|ĸy?ô‘íČl"`NÅ Ŋ*ˆ€ũ 0Ū9ЊˆīÂ#>iLƒÔŪ•ņÞÃąË€*ô.â}Ïā “ËÚO‹=áY/=îú+ŪŋvĮyœïoĸË?žæ:zÎ[OX/{›}ŊūņēŒßĸęÏĸęå]þKÛ­ˆĀ*˜™ˆWR4õ^PBðŅđ!ĄūęEqË-·Ü{ï―›7ož.?\R‹ˆLܰaÉ'žļ}ûöÃPÛAjōpŽ]Đ5$–ęƌjcˆaíbD'Ÿsé[ÞóŲĸúõŋö9}ÖËŪ|öyTäŽ?ãœr{ĩaņ”―åãĸãÃ?{Ũ—ŋxĘóÎĨÐéC~Î)‚+5* ž@-wũY6lM†Ph>å”SžņŒglÝšĩŪkcĖ#7Š !ėÝŧũ+_ų MŦ; ĩĪv€™áØeZbafb\W›õ#2ˈ@k#•õg<ũ ïzžë=‹ žŠųĶKĨó!āqOûŪŦŪUßïæQ!é5ÎĨæt(ЄHgĖyŪŦôôÐÃ3ã•ZcĖI'ä―ßĩkŨqĮwØ…ƒÔ ĒŠ>4ņČĢYŽãÐŲņH žŽf0LEÁ$íĻÚ` ÖNĩE€TĢŅH;wĐí €WĐü”,ĩLąPFœ$€ķíŒ!ÃČŦī_#é0ĄŠŊvĐļéĶ›ϟAj…Ũ'UQ@5ÞFô€ĸ !xQc‚xUk ŠÂƒrjJ@MŽ<6įPh|I3Á%fBĖž?КÃų98öĩy}A›kÚļ8ÚįÛ6U<ÄFĒ#k6î˜:žŨZÐķDœ4Ýýw}mÃĻÜ<2ë+DŋĀĄï:įc RYU†).Ūág7Ķ>’`BDāHĪv@UE\ÓjYZ“Ö•um™fz·Ïës^UQ ÂyÉN›T|ß4ĖӅ^ ӜĒ+hŠP)†Ðw8Öž"ÜsŪXÕÐķ'ˆ"Ģv0đķ \օ™Đ6*čęûžũŲwřóō~uæ+G™8#Ā͚NÞÄaįĶŊÞúõ/þŊŊ"#ŌĢÕÕ ŠÄœQˆ8įˆŽ5'íÜrĘģeÄÚĮŌ΀Ž!Ũķ-ņĻ,óæ;ABPņ!1!xÐ ’UâV/€)9Ēއ#–ÚŽŽė ĩY5ôĒ%‘$ĻŪčĐŠŅl'!"!ApÝÄÁĻ*ōwÁ˜3 ų#’,ų˜!•EõŪïúPÖĩ!$6)Ũfyˆ’ôÍČÅ ī1eaYŏĮMÓðÂBÉķ Θ-ĒNJHđiaæ<œ}"ßt-āĻ\v$ĩ‰p–T‚Æ•d+&äÔĢJm+K',€Z`۞ŧÛqӋÂĢ…†4ŽebĀ™D*õČ4ŠË·T§Wï\äÚ"Ėņ@/[c„9ˆĻøÖuĒUYĒøŪïEÕØrqãÂ'>ø'_øœŨ\īþS?ĸý_9áŧüû^üĨÏ|čs“3/>ŊüÉŦūrGwÖeïzßÎíũíþðûŪú―?ŧáėW^~å~į†#č ":Â!í ĩ ņ^Ų0‚@&*iëUBTuUBðmÓ[”Ĩeį\čĨÕPFĮÄ=ˆ’)Ŧ‘}°mĀ4ŪhÚNTÉuiĒmģ/ŒJ >‘5ÁũMŨƒĒĐFĨAmÛ( âUõČ . 132-Ô2é\žBf^öOŖ5ƒt]DØUU †éFŸ†ŌSũ]pÁCŦRU5ˆoŧ^SÅEQ†\ŨÄõ Ė䁊’Pûķēqô}ô€ë ĸéŋŲĩîūwūã/ŋðå[ïýŧ/v/=É;' óūå…CwĶũ~Ú#xĄ’ų!sØËē°  Âķ2~ŌL&ķŒú‹ų槄y Gĩģ3Įø A &ÝvqŒĐSb‡Nð•PŌ4–ú˜—Đ( e‘,“ŦHK"ˆÉ•@žĩ…eõBÐŪ ĻˆF%ˆē5L˜Û†q%ߐĘ "ŪÓäē5EÁ š"†ÓDT@cãķĢ D°Œ…ÁG=þOКĪ6ƒYj‘UA.lČĶ(lĀĶé{1!  Ō,քР·õÉÏyÂ?öö·õÍoųЎŊūãM?üÞs_þ―§ŸļļųÄïųčŊ>ýÚĸüūwþô;ŸúąũŽЧûÂ7ūęlüŅß~ÜIãûũĮĄ^ĻĨk'hG…ŪÉ tĐGNð˜qÕÉæĪPÓ'UĢuÁuMÛXŧDTÓŲŠÎĩQ?iý|ä [RņÓ2TŒ,úķũ@1‹Ūž 9ŅHPDš?ۖÐy­•Ū됎a ģæoD•ˆ MY—6oé<8 J€Y˜E–]@Ä‰„ļ†Ëþäecl;n<@ĩ°žRĘŅHęģG{VŊh|G…yD“C‡"‡ó{ >YÂv%$cĐoŧŪgRīŽ žxÎÓõŋO{îÓwlyþŨ~á#ß|æ…'Œš?ĸėuĩ hÝķDfįÅoþö·~äšŋų†ã7ëþÝÍÆįîųü·ý§~ášOžxįÞ+/}õöË?ņÎō•—~ũq—ėíÚ8‡™Aj4 ģAJę’D˜Sšœ\Û92– īMÛ6UYë$PïBÁYófēEÄL}ß"Uč}ϘúIӍę2įdcËBúvâT ŠKMō;s( }ŨxOdëēdĪ€ąŽyW‘bdĢíúĶ ˆhŠĒŽ+iÚņxBķĻąÐ·M‡S eQī]ŽXD§QēĐôÆÆXF§Æ23wņÐ[Ų%Q(ƚ!ÔŠdš‚ĻdęšęĀŠŠJË"æž7\uŌwŲgĮĶūä·~ãĖģÎ;Åũ°ļy{ũwĢ‹gĸōUßyœiwžęĘĸđ?þë/ÝNĢ'Yl8þÜ+Š8u=nzņũýÄš36õJia‹=˜‡˜ĪvāÐ)ĀÆ DˆėÂ(ÉŪ-G&ŦœĐF#$S•TcfBDcG Ļ Ä\Õ !DZ–ÖkDāt~ŠËŅû”A HDĢ™ē4€DTT;Rt/J?hYqžŌ`Sž)ÕhAB*ËsČŌÖč1ÎĒž–#f4 oĖ9Z $6‘‘°˜ŪŠÚē"ãĢ3”MÎ fŅڂĄ}t ķ%ÛŊÍ3‰VĎØTlVT[eûĸÏģvlíyó)ŊzíSŧÉĪuúĪg]ðį\ΊÁ5­ |ÖųŊ8įBBD‰?Á'ūæŌģšfÉɆ /đXŧĶõ0[xŒH-Þ}ũÝĮ˜Þ„šĶ™ķŦïû§>õĐ!„Ģô&2ČLkČ1†UÄ9ÏEÉEžkWŒ*CSÖ|š^2áažų3ßpà EQlŲēĨŪkfž—ÂՓÂWæJ ˆh­ÝģgÏôNÏ{Þóœs‡uQïM7}YUŸøÄ}ï†QíãDēE™Ŧ!"Ö)‘!Dp@M#Ē•)Øĸlē”—3ēÔ>rã˜ŧîškũîÝ'Ÿ|ōa ņ€ęš.›Їï!XCX6iÓūįõŸÝ­EQüĸėÝÁnâ0€áÝÆc1DHģÞĸex‘Ū".ČĄ;ĘHĮ]‰neëĸČęĄĒJųc…0ØĻ­išž‡(~Ý’sþĩúۓ.ĐÅöOųĸÔú™þ%ŋĸ|>Û6ót:Đę—îj―ėūwæmą€ïM-˜sô’ƒŠ–RŽĮãóĩÚãÓbðųäG[ÝĪH-€ÔĐR‹oÕČû°B“}úđōï:öu]7Ÿ~ŪķgīÍŋŠÉïĨ%ĩÕØ"{―^/—Ëý~·qĻËēTôąw{þ·Û­ëšÝnį[ Õ—ŠˆÄK)ö ÝïũöR[ĮÁsŠjšĶižg˖ÕķĒ_!„ß1Æa8ĩ:x[Ĩ”Žģöš†AUmáHm|HZßũķķāZgGEĐ‘bŒ2Žïm§ĪVD|ct8lÍŪķĶÎv]BPUëTŒqY>TQj%Ĩ4Ž#ĐEÛ#Bq•s‘VŊ؆†/ųiÓ:kûY VEЍQæų͚l œoÆ[Rë##"öXÓĪ:RŧÕÖ-ēuĨ6ĨXJQM}ߓZ4?ėmeÍõΒÚjøÄb?„[aëJmÎjrÎm§Īv[oļV[“í˜ys+ęŽ Aėoüūā?ėÜ1N1†ŅYm:"R“sPä`9kÄķðR:öPAOdGïÝaūnþ`rX[>X/ÐōËxĖ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@jĪАZĪ@jĪАZĐ@jĪАZĐ@jĪ@jZĐ@jĪ@jZАZĪ@jZАZĪ@j8ÄĻX~0ŌUfæ“Ī–Ė,ĨlÛÖZ1빖ē/ϐZzïįóÛýþđï_1 ĩ~đžũžR;―Zëéôz―~dÆP€e‰ĮĢÕZãOķ€ÔNZĪ@jĪАZÖÛí32sĒĄ€oöî=*ŠëŽø―sg—‡+ÝEX(*Ôw‚Ŋ†Bi4*ĐVc=ÖÄÐĢ5F,TTDÔ(V^‚‚ŠŌ 1ÖĻąõ­­‰īb#Ĩ€…Ļ^ Ž ÂšŊŲyÜ{ŧKþču·œ49G8óųëž3?vįũÏw~įΰó?k\tÔ0 Óßþ1W&“Aív{ssģŅhEņÅ?[//ŊĀĀ@NįXŧŠéîînkk3™LcgYßáááĄÕjCCCBrÔö2!ĪĐĐIĄPÄÄÄøøøžøgk0ęęęX– "„<ũĘŅÐÐééé ú”ŪŪۚš―^ÖûĐ:ũke2Ų ŒįųĘĘĘØØXĮ<úˆÆÆÆúúúĻĻ(I’þûĮąZ[[Ífstt4č›i[^^î8ĸþóÓā2™ ũčMÎÎxŋķĶŠú^CóN$Āj·˜Ÿ>yōĪģ[Ī h]Ųĩ;FÐ;Æbą@]í‡ĻT*Ð ÖnCMuuuÍýöĮFŨ đ‚ÍÝÏėß7ĩZÍqĨTÞ@ÉúÛ‚û›HÂGåđķ\―ĸLëŦ|Ü"ŪÜĸŅŠ™€ŌįWcqö†+ũ:U ‹vȍЉÃĐÜ~>.ÉŲxóCgÂüz1ī‡ûĢî:r†īPvéØÎmûZ€—ŌŽŌfýäƒ=Ü9„3wYEĨÆ_ĨÖ܄ SōÍũĶô‡ŋEŲoĒV&“Į[W Cí-;Ėŧ°āÔĨīájPuëė/aŒ ĪcB„%ŽbĀ s7ï~ĨķyM$Ž_ēæĢ—ĢrM’D)e˜ž?"Ž*‚+Č(’Ũ4”ß~ „ÐMÞBzsņpŅEŽĒĢúrbÜŠąIÅWÓÞFÜĢÞĐ övÁÎoGA0!a –(D Īąų™þą'WÏfˆßÜÄĨ!„1ÐŲ&Îî(%Bį‚Bøí Ä0X’mũôíρÐuƒýð ™LF{H’äâŲ#ķ―æęqƒæČÁ-ĄūLjĢĶÏ%’ˆ)mŽūš7/·Ē…Ũ™ô^úæņÞ˜PĀ–„ذa#Ē'‡ŽÆząÃ7kŨŪŦÓwzúÅgŒ:ÐðāVzrFe—íGĢ&~PČ@BęŠĘÚļkāŽ””9/‹"þÎQëķ#ČB[éį…Wã·o^ĪņƒWčÔ_ %XzxũʞžíUíbHÄÔÍéi‚á‰ßoŦe˜îšÏŪFm)Ø=/ÜT|ųüĨÖ wK6æ§V~öWûðÉĻödÎåöÃą‹%#gŊ,ؒčÓõũũ2˒vŽWņÉOîøŲ֕“ŊŸþ`OÁ™Vĸ°u9ó'c‚Ýž‹Ažjeēþ‰ôpuÐjĩú„‡øˆvNĀĀ)ė ëVma§%.œRīiÉŌu~q0y Kõ€ĄŌã‡~wļŪŲcr֌ŸÆÆeësÃBÉÖ·gūļpĖŌ€ėeKõc>N‰c8›ŠÚ1D7î^‘Wnš}0vŒĀŧÜãíÍÛ·Ū;‚Kf 1~‚ŊäP J %ũpmRšßœÔ3EQë'l8üÕŅxCííĸ’Ōūo͑OŊÍËúõô—†uütfjĘü`oë­ē2íR)ÜÜvöŧ^{7íý=)ÉŦvÜ^īx:cļYQąD"„āæÚŠæ‘ũͧË3NįœúÄïfúŠM{']ØĄE„š}ŪB(G­LÖßĶZAxžÏà ,ðX%Þ΋Œ„Xņãí9xbÜėAõ-6]ö[ŋÔj–/|ãōēēú.[„ÄģD˜økBĒ"Gé/^ųó…ōÔŨƒ.,ŪŽílÓ[§-·jĪė|gīNeԀō™NmŠW…Í9~4IívžļŲ@øÏļęzėå{<ũ8ãhF 6ÎŅē€$SĻðD­Õ·ZÅД7cÕj͊…ģ>Oú[ƒy ãļõY 'ŋRRŠđy+īC―ÕĘ !š@Čß'ŠĒĀ šWßݙ―"<ŦōąŲŽ‚'`O‚A‘cÉHĸøōėģöķũS Uīīybá}―Ķîv?úÛT+“ÉÜ΀€ŽRv·ëO_ŊÚ4wǧ'2T–›Õ“•Éd/ķóA.= Qýņ€ĘÃð0Pk Đ@FEJLLOSPS^NMmRH]J„WSƒV]sOecSfZPdY]b^`d]af\cgWctSa‹ag‘hgikzmpwsuy}vvŒ}u™|rЂrĩ…mūjĘx_ĮjYÄ`NÄVC`ˆîA pHYsÃÃĮoĻd4IDATxÚí \Wķĸ!ŠĀ§ëC’gdKÔ1š|& 4‹ÍŌ€ĻãPYīãÖpmÆ5‚šA‘Ļ •mf@Dâà ãļ°ïf&Šļ&Œ@ģ) 4ŌŽÎäýÏ―ĩtuÓlŠÍžP?ąšŠúVÝŠoŸ>ũÜS·ŠĩÂŋk“JŸ LMMMč?LUß ßĪ_Ø"·BSÕ]ĐĢŨ?kzĶū’Á‹Ú5]?S3ßĪ| ęō™ęiáŌŌŠį€V>ïOMŌ*ųåd­ïäéf†}ËH!cSSSø31Aĸ2U]AĘŌŌÄM•eÕS|%áU–Ž)šy-õQ™ĒV8TcZĶxbŠ8+ Fčt #/ï2‘sÄéĪŅĢúSĮėtiÓe­ķÆHP ˜Ģ$;FĀž–ƒƒÐŲŲÛÛW­žZĘŌ’%KÐŽÅ”]ޚ1>ëž?ÖMŅĨÔþîŧQō—ėUIõĀÝÍb/P]™ŧ―öðÞėæTe~Y?Ø=Õcg>ž’ÅŦnČĶÕY <;Īņ%nG—(SũėÝÖAŸNr§Bņ€'Ņ/€? Š̘†t3ë)s‡̧= åãáBWܰz)‚ŠûՐ°ýH!YáĮåCðėų_~ų_ķjRfĖ:þDiÕË#UîsÂÝQÅāU„ŽŽlFØW­BÜcïÔûįŽX2 Į4Ŋíáō  ]]@—$"6YoēCãã#8ÚÏę7Đ ―‘Ô‘lioÏâN|?ÆÎNÓôĪūr%ĶūrK:ɚ ]…yðþ~~CŌPāaûŔ―CÔ°~Ã:e?óÅ(ŪÁž“ŸKÛ;îĢóSžïÉKY™*jŊĐŦ­­­ëUĩuõÉ|[[†ŧŊÄũgėw!Nõ ūgĻŊ&ГØiØ―š:ݏzOŠ!ûkr_īhæ9ĸþƒßēyÍŠ%nK)ðwčŽxáS$Ą‹ EæmnFÉ\!ģþdn Øiîd‡ßÅ4ýŧĒeUõ3+)ŅĖWŊfaĢzG}Q§úMlïáá1”ÜÉå%)ŋÐIðāiæ/uCāįbá> tIð+WbîdšÂG{{ŧēíKwœ]ôĨ<}ĸÖîā€cx/:ēÖËXČIÚ8C#ýwI• ûũPĀlZ)ƒG.Þ<ÔĩŽjXæŽāŊß°zņüĨsiÁîzSķ…ģÁ"‘ˆLxāĄ†žšč„ængĻÄĮ’‚^ũÜ]ž|ė+YķūzuČQÎu • (ũ‰íá)?ďC?ē&Uî_ėî[ÖŊYĩØMÁý•&Á“^ŦzðvâNžPÅāíz‚w ļ QSĢ‚}%mę}# ÖØ@ɗÏûĪÎöðČÁƒÁ‹‡ÞāÉŪSI"ōÛ)ð;Āí ƒwsSÏøR|ÎĪÅ3žf`ŊėgČū'î<ÅÛÛŦëĨ*bHž#ôįĀāW*S_Ĩ 9n#û㞧"ÁK“ũï]ĘäÅoÂÓ`ƒPĸÖlņ$x7Ęϰė}mņy_/|?&O.ąíęóSŲ&„·ë ^ÝQÜ―–õ0öŦ‘ĄŊŲ@‹Î/*sũũ8wŋ! iOƒƒYägÖŊ Ē] ÍĀo]ŋayÉDAŌM*fF‚GÐHîý˜ž<͝2xoøæ ģŠÃnĮP§ļøe,S'ąoDĖ7nØ  ^™{?R iPįÉcč=6øíÛOģuÝ1 Ïg.%0ČWĀ?āūRī|™· jZíû'O/*s_. >TXžßūÅE$>ö=―;ĶÎīŽ(a‡đŊZĩ‘bū‘Ϙ;ūē€Lޟ}-)…”J.1gåāq L_jõĘž$›;Ō09ŠûÆÓø{. ÁϟOgĩYØAĒMˆŧŊ· cð}‚g)î8Đŧ\ēŋĻ ý5 /.–ø8ô fHėä éáQSØ7*°xp6dË ÄÉĀ]ZÃ3eëėĖ;>`ePðI}8”\ôF< Í=ˆ %ą―tĖ}+ãiÜ`4Â|ú*1ųýĶĀ“ÁÍ2hZbņôĒˆâîãģ\RxðŦCŅ……EÅ_ï -ą,ž !t;ž=NKŌā‘—Yĩšö4Ļŧ„;OkPLģÆG8žtï‰dĖ\ĄRōŽK€dÖŌӓîčÂėb ―€Ėæ.x]îóX! éiø ­TüŽŽ}åiVÓ?‘§Ž”g]ą þ!ôĪÁ BžZĒļĢī˜HRz Ļ0?þō ‹ũ–ōqî™îvtŠŪ<9“āą  î­Ē…%(L•Ŧčœ0&‰HŦ\æ^ž_žBßeÅ ú4a]ĨÃæ#ōCái)bÉ ,îdüūc'nZIO3ŸēwũU8ˆûĒĀSAžPÁ―wðôĨ#†ûrÉŨ… óKKũî”Þ.(>PļÃŧg‡ Yŧ­5Č4ÍJšïDķēÐ―€ĻÖGũˆų2z  f•úd b­ó‚„ŧ„™ŋ`(cx&ĪĄ< eð;w‚ģ!›V ~>ž2,ø~ŊØļ Dü2dðŽ ‹·ëž=Gú,„–öŊâŦē2Zę,T6vžWĀ.@c mų|`ČÓ az\‡7Nąy;/G#kÐooČÃwŅ•é…RÛū>ÎôõKĄ#JČAĸM\pæJ}—Ģá%.ŪB!þH\ð5f„`Þkƒ§\ž˜ęPÄķåÞøÖ0ŽŨĀϟ?Äé1vËĘ2ø_lGžÆ}=ö _Lį[!€Ûī‰ÞŨ˕2ø~ZWî·Ü{PDК&{pïþ[;%ꎰ_{[[kŦk—ē.’V^4šf|9,ĪõđDāœ]Õ&ļ|­JnžŲ*üV*ƊägVfųdUe…——_štŸQŲ$ĩÉ+/ðm­ÚŌCēĨÁyezey–äyÕ% iDVšÅŠlAVë_ųÖō6sK˰Šįax[Ļvbį .~ˆ;OŽĶu; áɀ<€_ãđ˜Æū`>jņé,·ÂÞy*ˆ§Āũiō wSŠûÃG#‰·ß6—HNšō觇ũ%Ö*ØŅnŧeäógæģĨ#Ĩ‘mW ÛŌS*BĀ—„ĪWDķUFķ^šP~C~B*’F\“GJl#Ō+"䙿mé#3ä‘å/V Ž-Û.§ü3Ī­-Žņ”yú3ŅėðŒJģÆČŽËŠŌ[Ê2|ÖÎmá’Ęm7ÐķW`óō°ër—đntDóZÜį‘ģiŋžjZÉŪ h‡îâÅó(ę$ũÅtîO <^ŪŊÂ}߃>™:-ėÁƒ>|ô@æmŦĘö Q$Ÿß”id&f…eĨf]NiG,i}þĀ―|ã‚Ü(Ē-B*j oMąī&Ũ§ļô,ÃČķЏĪJÄϓ"7đ —īeŋ ·ˆxö ĘGd]ūüøđ<Ī1Äü‘IXã…>†·5‚Dn"_á4WaïîŊÝēĒ Jžf;ÕuډĢIKŪņtĮãžæŅ#đÉë dGQað+WúöäŪÞŨ(qũv–Ýpĸþý°SQEũïÝŧĸāÁý|+jNÆu‡m­MēӍgW…™5žČČ4oLOyŲã”FspÐrsųÅ UĒËm'ŠDԐŽ6ÁXŌ–aŅ–~ąŅįRø}I•Äšßv5Ĩ•üŽŽ”į晙ôýhKĸNę Ž>ĖV ņ i ŦŒ46m33ÍÓa·érĄËpg‡4b•Ķu;íßQŋm]šÅîԈ3ŠoąŠēölō+Hƒ:ögōž‚ŧ°øþÝ{wïÞ=|žčö·îÞŋ{?„ïГ;€·ƒˆ&ÅŌ2ĪâņEcIUĨôBX*ieSdVUEŦų…ŠĘŠHIŦHâýüq›Īąēi߅ŠôŲ%°þރ+SēŽÃēöWT„IŠä!•Ō6‘<øúw‚ķĮ•am!BĄýõĮ)V—W·UüŨ‚vŧ5Žó†llÝy `‚x2W@q‡Ķõ KRā1wOŌÞŨŽîÁö4ŽŽýš<›ŧģHvëGЭ‚[ĸøžÞËæ z`b‹·Gą;š OlííP$ ‰{°bˆ=ūkX œÎЧBĄ9ģpŽ}8˜ĀWgčėÚ lmmėŽmímlm…ķķöB[;ĄĢ ŽęŅŽþ ÚRZm­q<3ÄÜų`&–dļC·uëhZįąĀŊ"/Ŧü&ĖÝĨ‡ÁŦsōlîނýũ~üû?þŽõŋߒ‰t\Ī0v!õyâþd Ð.`l 9NŲ[Ðv€_lEņžï2VoŠįÝ708•%rð-yKŅ\EúŽŪĻ Äüķßĩ: Qü>oÞPbbē~ėl xhZŨx.ĀÉ!ũhDģ'ÎzV žj ZO‰ūâ§ļÖA%yՈ•’'G<ĄÉj4ÐRė &:ĪÃöč4MŊM+ö4âE‹ywōŽ1O^UÛL‹ïBųƒūČģļS&/ðą–HJųâ‘!ŽJԅlæs™ĄÛ<āūaū_‚ž"(YzYĮų Ļ\jõSŅįJĒîyÂýa„Ĩ§‡ĘEî!IÄû1A|POƒbÉõ~žČâ,Ķļ‹ĐÓÖoTâžÆkŪ eņ}š<›;ûr`ˆ <‹;CݍpûŊĮ—)é{'pķ#ˆ%†~ 3l WšÉBô―f~Cv ÃĀã+\JŲķÁCŊÕ_žČ'oĢņXüÆÍ,)7­}‚gļ+nß †Þ-C§ŠøÆØÝšįĮ@];ØNýĮRÃ}2ũ€gfÃm­C8@˜ôð‹č;oX—ü”šÖ/ķ­ũCMĪɓwH)îÔaƒ_ĄäizkîĘÐ鑯ä>„ŽBķWW2õųÔxˆwœWę!ķÕŊe+pĀbĘÅY1ußü"JîŊŠEˆšöïb<:Û{ūĀÍPĮMŦ'YuÛ*N݈đ݈ï;ތîī_Š QîÁŅą˧âo_|)ƒoþã+IXÖøų JÏT`Ą€Ū8Ų°dW[]é[Rž”ƒs&DW•–cßÂeĻōčLze6Ŋ#úĶ\zh }‹6y‹9ÎÎFčH ōU)Ū°ĢmQ_RýĢ)ĻįKðíĐ<°——0âŊC ŋõŊŋþĮk°Į=øÓ‹äÛSöîÅŋ:ëSNšŅŽŦ& î7Æœ4ĢņJÜÃҐ&q܇‰ŧ%Į}xļ;Ðņ Į]ģÜ8îÃĮó3wŽ;'Ž;Į“ļëK$[õO0‹ûž>ãúŠâĖMCŽģ}qŸžz;?ï‡Đ{Ø+ĐÅĢũwÃôíÔ{}UqŽ'ũ™{ ؋žS#–{/ņ;ï›RCBßûĖ]§·‰·ß!Ũ―CāEÄ―øëæÜõ„‚ïPï)oþæNŪĨķûmâč-Cr†úJĨÞ|‡ĩ8ÂėÝ[wý#· bTÎýŧŧG••Ý]H―]pg ZDÜoįĸMKÅoŸžSvÓjTAqŲÝ=ÄŅ’ąÄŅõ3ee·ŽŽÝN-ŧ·˜œZVvÓX?ĩ ïf„ė~ĐĮīüēēÐÔ݄~ĘîĢũïßþÞåü;#݂ēb'â­;NÄi'd§Gïï"ð"âþCønbBþÞŌĸ>]š—š{”ė&ïč]ĢĢ7ûhÛž?QÆĮîíÖK―Ĩwéķą^ÞÍą—îÂĶGoÓËđ9nŠqOÝÍK―I-5ž 2ÜįÎå_UŨŪō"óïė –ĘîZaq†æþÖ-Ŧ37g•z―_Zz`þĢĘbîČÞõRïė?sˀ8{ë·eŋ‡ŲR+@Œļž)sB†á~ZvËrDÆ3―q:ßÜ:uĮI7ï–čĻ*w―Ô]ĐŧF•Šŋđn,F!îw€ŧɝÐ;™{ËĩŦgïÎļģ Ž3ÜĮ―kErį‘܉ĐĐww@{7žëŠŽ;ÏĖ0ýxšĖitŲïõS)îoÝĨļo•–ØŒ*õ8T'3ÜK-õōŸŅs&tïýžä>;į& ūiHq7-ÛMŒ7Ký]†đOĩ„†Âr$q§ÚUõÜõSËJKoŊWš'õîí<Š;ZDø~$ôōv`ïāQJoŧ"ýĖäžēÛy?`ĸ^zįĶ ÉÝęt^iéÍđú˜ûhŲí…ĮrKKũŒ’•”î&ŽÝŧ~§ôÎîą#ÐÏx{Ŧõ3á)Ö1YÄŨ‰&Î6˜0­,B/ã-ā?„™ģĮúf"gýŲcņ‘ũD (N‰ŒˆņPtžđl#2&x“,pÖOųSDÎãM ý™ū“`#Ņŋŧšry‚áāîĖqŨ0w|Ïȅã><öîÂqŨ$wĮž a$tąš:‰Ē9{'ĮqpÜ5mïäógŒ„BŽŧfý ~ū˜‘ĢВãŪQîĪ―;:ZpÜ5ęgæ`{wpāė]ģÜÉxÆÞžãŪQîķĪ― ėM8îånËqîĶ$wÃ9s8îšänLs·í›ŧþÔwú]3xM ŦäMyÅõĶ’%õ§ð[;Úv@Ōĸ”4°Ģęw‡=ļ[ũÍ}LÚeô€=Bë…}0§E0―OąNęüåÃFo‰čEÝHāqö° ņVļ‘ĘŲG°Óėúįā)ršÆPMįœALŪ!.Đõ"ōļĒv―“æ―”―π9fīí€Īõt!sōėĢÂ'ÕĢFÞŲÍę>wÍÕÃÞmmÕqŨíÎîȧĖý\!FĀڝÞđ֌l^ÏÔÄKüų ^ũ#§ūļó{Č•ÔzqLQû˜ŽÞ>ģ˜Cú.Œ(ũ19Ėž•ŽJKmžølËŪÂąƒænjmMq7UĮ}LýâÝîJŲ‰ģ`ęĶ]SQRW^` ũēžQVûļp2ZIču:°ūŧēaoZmîđō†ĩžĪ'gkehŦ™Pō|1_Lč~ūĩæ1\Ύ/ŽÎąJJ{ZÝüWóî é‹ÚĮ2BŊ{ũ˜ŽāŪ Ųék6ZŲ6DtūvýÂĪāĊG Óvę^NËÓŪ}üÓ8“äīœÓõĄņU _Dô?>›ØpōĩEŅ5UĄ:•2§—åēc0ĸ1ĶŪžđęÕŪŦĖ·$tÚk{Ōü-Ú*Ķâąė<ĖCųíÕēŽŠĩSߊ­Č3$`š .ģÂĩĢ“z ûÏģŌš5ã2ø;‘öBŧķĒālūat>˜EwEó7ĩ•ųgåč4‰Ņh·9' 0*cφŧĩZ{'þԘu ūár}vvbÃåÚüŽ=ï‚sx™žvÍ-YZi@ÄÔd6ã}+­îvgáXô ëŸ+Ū~ mUŸ]JP`LfZÃÂØ;†DÜĩÄŽýŨš[LēsÎg[éÖęīÃEÛØŌ˜ÜÄ‹ŪŽ'īíßt„~þéÂxYæ^ÝĖīėÎBŌÞÓ2šZiUßč”ÙM ôš‹ÞÓŠÞĢS—ÝųČ):­æft.ž_ˆZ Ô+ëlM“OēKŦĄ―ÅmuĨnÖ‹Ū+":­éoO‚ëJ ‰„‡ Zō`‹Žę2Š―” Úĸ 8°Y{QL(ķzWWáhyZŪ!|yqŲÖĸĨwūģ™:MžÛė.ØU'”ŲĢÄ}pgmmÚKŧzîaZsÆÕoēŅô|‹ é°Ÿąó‡•؉Æ4ė?WĪ]ŸÕŅ’‰ļØ S{ô-.ðM*Isχ܅vZNrWqR‘”™ oéVôĨÖél? +ä-6ˆŧ^wÞųŽïÎįD?ÍÂÜÉbˆ{MÃۘЌŒÃāņ3ũžō ĮtāŠĩžfeĪÆĘ3ęKĒóÆ<y+t0ŅyãĒģ;Z3Ō—Ą2cęē‹Ņņ$<‘Ąc+& ü“Ėœ™_aëiÏ7BÓkøÃFĩgĢ=Ï O s'Ë`7[íÕëĖĖ į…Z,―„ÖŒō4yänOĪĩgĒ2Ą îF|>æÎįŦåŪuídtÎųÖÏfhŋ@Ó)ŋ”œšØâŸcÃqôÉĢ•Ķā=öŸéØ{.'Fæ“(˄vïÉSpĐzÓÉÚĻdZÎĐšBðŒČ.ĀíŽé*Uwš„s™9įåÁg:č"§ųvâ#§ļ§ÁWžˆčDžýŲēhŨx™ÛYݘ–ÓUýų™ī˜ãmGžė1yáâs°I‰~BþĐč§1uĄ3ĖófuF7œ@óÐØäÄķÆätåϘþĄÕÛ 6ŠĻļ>4Ų*ŸõbVĮ~8Θ?&=uŌ‡i]Ë^ݎïQíøĪšóOEfÖYtlRë‰' ąûķé΋n=ĸ4øe įR|ķ‰[ÂŅĄ’§‰w{1vUôā>ķ^įî7_‰Ö +ÅBžžĐDãÕ&]5äó þƒļ“5Ā@o:+î{+"ŌhúŦFĸQÜuϧX‘=Þ(ŅŊ:O@Ųŧ……aßÜĩÐu.Ŧƒwv=ęÄæ+6T·KŅ ûUrgaAq§–ŧþé(ĢĶF™ÄĘ|ðüīY†o4Ž xãŅúÉQQ>ëý™ĶÄä(3ô5|e8: ĮeþņLS―Ļ(ø?[ŋ;ÏÍÃNgFAG›7qZÔlBöņá{&ņĀÃčÔéF™C„09ŠŦˆĐnrŨûSScŅ9iuåž•gŸĮó-(Ð$…>'^ÛT]xŪŠZVsûtgS3DJqåÍįŦŦrųLéóý M ĄgÛŦsÍj.ė†y"›šwAüW-mþ Þ=Üy !O'ʈ)ŊĖŠ­j>žsĒŪŠĒ0žŪnrŨĐiŪyԑG$jį!į‘ŧ‰nŊ)3ā%æÂzŽšių12íiōJčŧjå|ÔŠÛ~Žũŋ‰˜f­ry̓š›}_Œį- ýxyåMwëuĪý,Ŋ‘ĨzYð lŠ›đ=.HČÎNË#âo(ŠûrÏÂĪ/îZOœŒJB]͘Ü1Ô<`š?‘ė›%eÔ‚>HqtęEž2GÞY·sę›!7;.Ķ!ōdTgÁ 9ô*ĩ`ÞĖ€wîEdW!ęgBo°ÖÄfæïڍr{Á­'īĒėđĖV\ÞTƒ_#ũą&͘ŧ‰éXĩųČį^<œXˆė―úP9.üéũe†ú0îZû•Ū‡iE1-Ð5}r%eJN]éjN“-&ô^ķ|ŪÅPŦîÚÅāŪŽīŠ=ņ )0ĸ•/ūõûZÔw…>*^ĢS{3šéŋQžooĖ“ŋ=O]ĨŨtáębPÂëWČݔänŠž;Ą“–Y|BBœ‘@6q9:™)ÁĸEs{öÉ}[čŨœ†LĄĄģ)îBqŸÜ7ŋĮy…!Ô&Šûuįŋūđŋ;°Î8ÉA€–3ÜŨų‰―'Ë9ĻÐ šŧKP`Üy3gÍV·ÞdÖhŲ#mÔÁpũÞšŽß7w―øĘŠRu9p­ēÆc&sÜĮ}[P?ÜĮÍČ1 ŌĮKģęe0’6QŽÖpÜÉ}§ o{ŨíŽĻ(9[SŅ|ĩĶĒĨVT~Ÿ—ęę`úŦýrũyņ^Ôz5orR•é΁r·XÁiåü_äÎéMˆãÎqįļszóÜ―úá~äŌENC§K3ČýâĮp:ųv€Üŋ›Čy…!T8ĮýĸwíýėO'˜ƒ:äÜĮÔĘŽúޟvŠuPÜw €{lv'ܰŊ?ýÓąÄäéŋ!&LŸþóž ŸĀ́xŸN‡7uú8î@ļ{€ŧ~ÂĄøîĘæßEŨT–Œî”ķøtVåžĐĐ*yŦŪēō Vėŧ' áŲcŦŦvqp‡ŠŧN͇V›S| <ĐĻîšKüđ­úoŧˆXxPIb‹aœ\ŧúwZOū­iŪ-ë.å~q€Ü·ũÏ=ķžĒĒ|OÜJŠŸy– øøLgŪylmIBÝÏaŅzrĒĶø˜dtRõnîĀļï@Ü-ûāŪŨUøÛōĒkRÃgw·$KbĨ)áģŽ%d{ĪŦ öiJg‘púŲ/đGöIŪ+áā†ŧ{ܓÜāq!‡ bÛ3tŧÚqíYĮÓÚh·gŊāÅ·_1Öđ`…þ:ۋbÛóļļf Üoũ9˜ŧŧ-ŨoŌ(ũíũaāîĘqî.ÛķےÜm8îlW9îÃÄ=h Ücâ…ģGčnҘï‡^x҇{t”Ļ·Hé'ķĩ·_ab‘žÆaqßÖ?wÚfüÔĢx怚äÜĻ“h|L*M_·ËC/‚ÅV?ú|yA“ģŅVƒô@ý^ô“\hýMqÜýĀ}ö/7ÄåÄĘôP:,.Į8>ü{tüð]wüuŒ!3b_vǟ2[Čî—ŧx ܍ãģâsÎÔ4JŋĪļT'3ë~\UōļĮV4>%3‘Ú5•đu?ŪäüĖpŨŸHžý.šNž>éņïà üM˜>Vú ‹ņïÞãM3o0a,oâ{Ä䟠õâūđۉņs đ~Į}dpũäļw1Įý?•ŧþĪũ9dšįþVüþũizŒ™áū.w[OÏþļC&’xkV=sÃX\Žðĩđ{ô˝—(s%Ë퉗ʜĒ;š—ߎI5ŠIÕéĖĻ åPū*w~ýŒ^âcļa2Ôhôũ5%qķ$ūðQņ/īĘKbđ”Āä#ÆjÖïŌí~^ ČPڀJŅčÖr·Ŧū1îúg—ŽŪ=TŋGëĄSW ķtŦØV›—2­ę]ŅOđŦoŒ;/ķĒĒðÝUčŪ|v öšą6 KíŽhūĶ]#­āîV}Ģ~F­`Ā’ã>bļëøō•ûŦŊΝÓës·ōāļk”;ó;qwÍr§'ŽãŪQîŽ~}ĸ>ĮýpŋŪø}>îųšäîčÏqîë0wą˜ó3šäîDþNœĮ]ģÜ·QÜđvUģÜ·ãßįãûsöŪQîū;ðó 8îįŽŸ?Ãðģįļk’;ųû|ցwrĮÏKĩôįļk’û2ŠûÚGŽûpp_ĮqįļîAk9îÃŅŪ­rÜ5ÏݖãÎqQ~&ȅã><ÜŊsÜ9îŋvî^wŽ;ĮãÎqįļsâļĸ_įūƒÎÏpÜ9îŋ~îÞ î\ž@“Ü·s܇‡ûŽ;Į}Äq_ËqŨ4ũ9wsŋÁq.îÛ9îÃĀݕü=JŽŧ†đŧ0ÜđëښlW9îÃÄ=ˆã><Ü·q܇‘;7nIģí*õûŦwsĮŋĮų sŌÜŨqņŧFđŊãļ+wŪŋ:LÜ9ĸŪIîŽũaâūŽã>,ÜŨr܇ƒ{ gïÃÂ=€ã><ÜņïŊrÜ5Ė݁ãÎqQÜúæ>žƒõ&ļSŋ{kØ ũ —.r:ĨÏ wâ]NCĻ· †ŧ?É=°îœÞ€ÜŨrÜ5Ë]ĖqVî―<ũ‡Ó›áNýÞpïí*§7Ę}-Į}8ļó9ĸŪaîäïÞōŨŪãļũuũaáÜđ˜Ķ4‘þg>ôWŊG͘ÉIšuúgĖ}æ'S9iBŸĖĪļ;^Ÿņņ'œ4Ąg0Ü8îånģˆēũŽŧFíݝã><ÜÝG.ũÉŊ )ÔķS&―‚>TøwwËĘý·Ÿœ ŽB‚ŋ%Á8óøąÁ+ęCUî#û”ý?ücÐúûM ?eÖåï ZÉ'?dļ“þÝäqĸīð‡ß^/™ŒĖ=âûÞý`zũíðä‰#žûo§ßúÃėÁ:čß8ýPˆđO~ũã)ƒÔĮO^Pp'ÛUĸÉ]Eð_yQY“Ü?PQŨqˇS”–'`sĮþýsŽ;čS3‹ŲæŠEssÕģ—*ļ+õü'Ÿ>ųí4•tĀĮÚ؟)Ĩ^>äļ“Üm•ĄþĶû§/u:Î61šm<1Đаwîã?e+ąēōŅþũ??uĘo>˜0í“>ø“>ø šE2]hŠîŸsÜgtŪš`zĮÁų‹ ĪâÎŪýЄß+ũ‰, Į>ÝóÏ_HŽt=ŋb]ĸ|ïđįųg_(sĸļw+ņˆäþ™’ĖŌō;ŠÛŊ<đŌž{ū!―uo§ė€Š‚îŸF)4Ŧ;Į,jÖĖĢuđĩēī†”Ú‚äī§OēZ‚ĢؚІŧÃäūųģl}––žĩ%ģžŠRķðeųnóī/å+>û’á>ã”B§ŧrgŠ:ÛYYŅÜVŽÝÞ!ó™Ų]^YŲÖ|Š­Ļˆž~F<đoųl[Ā}eŨO™ ŋ·qþŽSšŋĒ+oĄrM―pĸ6I:ëhŨÉĀýl{ÆÓ―3še>ÓcšCúán5BđK6ģ%I+p;ŅÚųsE^gîņ†’ðĮ·7Ŧ”PpŸu’ĨSݏ?JĐ}\ÕÜÜSš~ŪļXûø§Ė֐SJĨ8î˜ûî›·°ĩyĸõëWŊĸmËūŊv~ąeóķ/ķėÛđEY›YÜO°tōdÄņˆ“'ŽGĒyX:~""ŅKjļ‹Åö#‘ŧ ÕÃÍ ų[z—‚{Tä‰ÁęĪ:î#ÍÞ?™~û‡Ý*úNÁėî]{v3ų™i'­ėü ÉÝÓÓ~Äåĸøãāó‘ĸļĩį#Ģ2’ZáĮ?TĩwÏgïSĶ…žöQųũSÉŊ ãŠųwð3v#îzÓo'Ož&S8ņÔãzø;îúŠĶÄæ.ķŋÁqîœ―sÜGwOŧ3Ķ|ĖIšÂâîawƒŽąņĀũį&žŽ!Ũo(éúë°æšzÝP)üęš> ý_qïƒ>ĩ; qtw7yŪ%ÍsŸïdgggoïāāHI"§H.äT!xÓÁ6ąĩéEķvý‹Ž†ÚĢ+–··Ŧ—Š\]á@úω-;{Ø%ėŠÞ’-UŅ'į8`ąËÛÛSgaKÉfĀrt›į&’jUĶÛxļ{zzŠÅ~âÏ?ũũũ  \ļþz(i-žā'öđũ"þ%ö`í3h;[;íØī.PėéąČ―yxŠýUöÄ(ˆ%ĨS °čōþþbąXųlÜŪyólŌk=—ÛĖó@ÜşcðˆûZĻEÁ=ˆ(h;Z ð‡šÝ=^‡{ ―Sž;v*kĮķ u~:1ÏÏÖQێ;úâĸ ā™-^ž›MĪīJ+ĢQš.2ąBâÃ-ëÞe‹&PocŲ‹Žú—Ē[…æ(ÕņûĻHđRīOf'sæØöTogÅïG*e{œå@eė“.­lÓú>ĢąBúėYĨg &fЧžaáWfĐ5õ'ЈúâÔĘgJ‹ýŠĐũŠû;īþöÜóȟ―šĪŦÚ2ĸ?š‘ŅķōcPĐ §â‡øÂECÅÝšTyŲЁ@ՕŦÁkÞítÁõ7KoÝÚîÜ―wĸĄï!!•Ŋį‘°ÞIENDŪB`‚doc/includes/000077500000000000000000000000001325366651500134455ustar00rootroot00000000000000doc/includes/installerfw-examples-configuring.qdocinc000066400000000000000000000022511325366651500234650ustar00rootroot00000000000000 \section1 Configuring the Example Installer The installer configuration file, config.xml, in the \c config directory specifies the text and default values used in the installer: \list \li The \c element specifies the application name that is added to the page name and introduction text. \li The \c element specifies the application version number. \li The \c element specifies the installer name displayed on the title bar. \li The \c <Publisher> element specifies the publisher of the software (as shown in the Windows Control Panel, for example). \li The \c <StartMenuDir> element specifies the name of the default program group for the product in the Windows \gui Start menu. \li The \c <TargetDir> element specifies that the default target directory is located in the \c IfwExamples directory in the home directory of the current user (because the predefined variable\c @HomeDir@ is used as a part of the value). For more information, see \l{Predefined Variables}. \endlist �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/includes/installerfw-examples-generating.qdocinc������������������������������������������������0000664�0000000�0000000�00000001100�13253666515�0023266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ \section1 Generating the Example Installer To create the example installer, switch to the example source directory on the command line and enter the following command: \list \li On Windows: \code ..\..\bin\binarycreator.exe -c config\config.xml -p packages installer.exe \endcode \li On Linux or OS X: \code ../../bin/binarycreator -c config/config.xml -p packages installer \endcode \endlist The installer is created in the current directory. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/includes/installerfw-examples-packaging.qdocinc�������������������������������������������������0000664�0000000�0000000�00000001243�13253666515�0023077�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ \section1 Creating the Example Package Information File The installer package information file, package.xml, in the \c meta directory specifies the components that are available for installation: \list \li The \c <DisplayName> element specifies the human-readable name of the component. \li The \c <Description> element specifies the human-readable description of the component. \li The \c <Version> element specifies the version number of the component. \li The \c <ReleaseDate> element specifies the date when this component version was released. \endlist �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw-cpp-classes.qdoc��������������������������������������������������������������������0000664�0000000�0000000�00000005302�13253666515�0017254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage {index.html}{Qt Installer Framework} \previouspage Scripting API \page ifw-cpp-classes.html \nextpage ifw-knownissues.html \title C++ API The C++ API documentation is written for developers who develop the Qt Installer Framework. It describes the internal API, so there is no compatibility promise. Further, the documentation is a work in progress and therefore parts of it are missing while other parts might be out of date. To create customized installers, use the \l{Scripting API}, instead. For more information, see \l {Customizing Installers}. \section1 Namespaces \table \header \li Namespace \li Description \row \li \l{qinstaller-module}{QInstaller} \li Contains classes to implement the core functionality of the Qt Installer Framework and the installer UI. \row \li \l{KDUpdater} \li Contains classes to automatically detect updates to applications, to retrieve them from external repositories, and to install them. KDUpdater classes are a fork of KDAB's general \l{http://docs.kdab.com/kdtools/2.2.2/group__kdupdater.html} {KDUpdater module}. \endtable \section1 QInstaller C++ Classes \generatelist{classesbymodule QtInstallerFramework} \section1 KDUpdater C++ Classes \generatelist{classesbymodule kdupdater} */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw-examples.qdoc�����������������������������������������������������������������������0000664�0000000�0000000�00000003055�13253666515�0016660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \group qtifwexamples \previouspage ifw-customizing-installers.html \nextpage ifw-reference.html \title Qt Installer Framework Examples \brief Illustrate how to customize installers. These examples illustrate how to use component scripts for customizing installers. */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw-getting-started.qdoc����������������������������������������������������������������0000664�0000000�0000000�00000011720�13253666515�0020145�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage {index.html}{Qt Installer Framework} \previouspage ifw-overview.html \page ifw-getting-started.html \nextpage ifw-use-cases.html \title Getting Started Qt Installer Framework is developed as part of the Qt project. The framework itself uses Qt. However, it can be used to install all kind of applications, including (but not limited to) applications built with Qt. \section1 Supported Platforms You can use the Qt Installer Framework to create installers for all platforms supported by \l[QtDoc]{Supported Platforms}{desktop Qt}. The installers have been tested on the following platforms: \list \li Microsoft Windows XP, and later \li Ubuntu Linux 11.10, and later \li OS X 10.7, and later \endlist \section1 Building from Sources The following steps describe how to build the Qt Installer Framework yourself. You can skip this if you have downloaded a pre-built version of the framework. \section2 Supported Compilers The Qt Installer Framework can be compiled with Microsoft Visual Studio 2013 and newer, GCC 4.7 and newer, and Clang 3.1 and newer. \section2 Configuring Qt If you use a statically built Qt to build the Qt Installer Framework you do not have to deliver Qt libraries, which enables you to distribute installers as one file. The minimum required Qt version is 5.5. \section3 Configuring Qt for Windows We recommend that you use the following options when you configure Qt for Windows: \code configure -prefix %CD%\qtbase -release -static -static-runtime -target xp -accessibility -no-opengl -no-icu -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -skip qtactiveqt -skip qtenginio -skip qtlocation -skip qtmultimedia -skip qtserialport -skip qtquick1 -skip qtquickcontrols -skip qtscript -skip qtsensors -skip qtwebkit -skip qtwebsockets -skip qtxmlpatterns -skip qt3d \endcode \section3 Configuring Qt for Linux We recommend that you use the following configuration options for Linux: \code configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -qt-xcb -qt-pcre -qt-freetype -no-glib -no-cups -no-sql-sqlite -no-qml-debug -no-opengl -no-egl -no-xinput -no-xinput2 -no-sm -no-icu -nomake examples -nomake tests -skip qtactiveqt -skip qtenginio -skip qtlocation -skip qtmultimedia -skip qtserialport -skip qtquick1 -skip qtquickcontrols -skip qtscript -skip qtsensors -skip qtwebkit -skip qtwebsockets -skip qtxmlpatterns -skip qt3d \endcode \section3 Configuring Qt for OS X We recommend that you use the following configuration options for OS X: \code configure -prefix $PWD/qtbase -release -static -accessibility -qt-zlib -qt-libpng -qt-libjpeg -no-cups -no-sql-sqlite -no-qml-debug -nomake examples -nomake tests -skip qtactiveqt -skip qtenginio -skip qtlocation -skip qtmultimedia -skip qtserialport -skip qtquick1 -skip qtquickcontrols -skip qtscript -skip qtsensors -skip qtwebkit -skip qtwebsockets -skip qtxmlpatterns -skip qt3d \endcode \section2 Setting up Qt Installer Framework \list 1 \li Clone the Qt Installer Framework source code from \l{http://code.qt.io/cgit/installer-framework/installer-framework.git/} to get the sources for the tools. \li Build the tools by running the "qmake" from the static Qt, followed by "make" or "nmake". \endlist \note To contribute patches to Qt Installer Framework, follow the standard Qt processes and guidelines. For more information, see \l{http://wiki.qt.io/Contribute}{Contribute to Qt}. */ ������������������������������������������������doc/installerfw-online.qdocconf���������������������������������������������������������������������0000664�0000000�0000000�00000002040�13253666515�0017165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Run qdoc from the directory that contains this file. include(config/ifw.qdocconf) HTML.footer = \ " </div>\n" \ " <p class=\"copy-notice\">\n" \ " <acronym title=\"Copyright\">©</acronym> 2017 The Qt Company Ltd.\n" \ " Documentation contributions included herein are the copyrights of\n" \ " their respective owners. " \ " The documentation provided herein is licensed under the terms of the" \ " <a href=\"http://www.gnu.org/licenses/fdl.html\">GNU Free Documentation" \ " License version 1.3</a> as published by the Free Software Foundation. " \ " The Qt Company, Qt and their respective logos are trademarks of The Qt Company Ltd " \ " in Finland and/or other countries worldwide. All other trademarks are property\n" \ " of their respective owners. </p>\n" include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) # Add an .html file with sidebar content, used in the online style HTML.stylesheets += config/style/qt5-sidebar.html ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw-overview.qdoc�����������������������������������������������������������������������0000664�0000000�0000000�00000011500�13253666515�0016702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage {index.html}{Qt Installer Framework} \previouspage index.html \page ifw-overview.html \nextpage ifw-getting-started.html \title Overview of Qt Installer Framework The Qt Installer Framework provides a set of tools and utilities to create installers once, and deploy them across all the supported desktop Qt platforms without rewriting the source code. The installers will have the native look and feel on the platform where they are run: Linux, Microsoft Windows, and OS X. The Qt Installer Framework tools generate installers with a set of pages that guide the users during the installation, update, or uninstallation process. You provide the installable content and specify information about it, such as the name of the product and the installer and the text for the license agreement. You can customize the installers by adding widgets to the predefined pages or by adding whole pages to provide users with additional options. You can create scripts to add operations to the installer. \section1 Choosing Installer Type You can provide end users with an \e offline or \e online installer, or both, depending on your use cases. \image ifw-overview.png Both installers install a \e {maintenance tool} that can later be used to add, update, and remove components. Offline installers contain all the installable components and do not require network connections during the installation. Online installers only install the maintenance tool that then downloads and installs components from an online repository on a web server. Therefore, the size of an online installer binary is smaller and its download time is shorter than that of an offline installer binary. The total time spent downloading and running an online installer might also be shorter than dowloading and running an offline installer if the end users do not install all the available components. End users can use the maintenance tool to install additional components from the server after the initial installation, as well as to receive automatic updates to content as soon as the updates are published on the server. However, this works for an offline installation only if you specify a repository address in the offline installer configuration or if end users specify the repository address themselves in the maintenance tool settings. Create an offline installer to enable users to directly download the installation package on a media for installation on a computer later. You can also distribute the installation package on a CD-ROM or USB stick, for example. Create an online installer to enable users to always install the latest versions of the content binaries. \section1 Promoting Updates Make online repositories available to promote updates to end users who install your product. The easiest way to provide an update is to recreate the repository and upload it to the web server. For large repositories, you can update only the changed components. \section1 Providing Content for Installers You can enable other content providers to add components to the installer as add-on components. The component providers must set up repositories that contain the installable components and deliver the URL that points to the repositories to end users. End users must then configure the URL in the installer. The add-on components are visible in the package manager. */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw-reference.qdoc����������������������������������������������������������������������0000664�0000000�0000000�00000003475�13253666515�0017006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage {index.html}{Qt Installer Framework} \previouspage Qt Installer Framework Examples \page ifw-reference.html \nextpage ifw-globalconfig.html \title Reference The following sections contain detailed information about the Qt Installer Framework: \list \li \l{Configuration File} \li \l{Package Directory} \li \l{Controller Scripting} \li \l{Component Scripting} \li \l{Operations} \li \l{Tools} \li \l{Scripting API} \li \l{C++ API} \endlist */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw-using.qdoc��������������������������������������������������������������������������0000664�0000000�0000000�00000033270�13253666515�0016171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage {index.html}{Qt Installer Framework} \previouspage ifw-getting-started.html \page ifw-use-cases.html \nextpage ifw-use-cases-install.html \title End User Workflows The end user experience is similar for offline and online installers. Along with your application, the installers install a maintenance tool that consists of a package manager, an updater, and an uninstaller. End users can use the maintenance tool to add, update, and remove components. The maintenance tool connects to an external repository to fetch the components to add or update. You can specify the repository in the configuration file or end users can specify it in the maintenance tool settings. You can support the following end user workflows: \list \li \l{Initial Installation} \li \l{Adding Components} \li \l{Removing Components} \li \l{Updating Components} \li \l{Specifying Settings} \endlist */ /*! \contentspage index.html \previouspage ifw-use-cases.html \page ifw-use-cases-install.html \nextpage ifw-use-cases-add.html \title Initial Installation The following image illustrates the default workflow for installing applications: \image ifw-user-flow-installing.png "Installation workflow" This section uses the \e {Your Application Installer} example run on OS X to illustrate the default workflow for end users. The installers have the native look and feel on each supported desktop platform, and therefore they look and feel different when run on Linux and Windows. The example files are stored in the \c{examples\tutorial} directory in the Qt Installer Framework repository. You can use the \c binarycreator tool to create \e {Your Application Installer}. \section1 Starting Installer When end users start the installer, the introduction page opens: \image ifw-introduction-page.png "Introduction page" You specify the name of the installer and the product to install in the \c config.xml configuration file. When end users select \gui Continue, the target directory selection page opens. \section1 Selecting Target Directory End users must specify the target directory for the installation. You can specify a default value in the \c config.xml configuration file. \image ifw-target-directory-page.png "Target directory selection page" When end users select \gui Continue, the component selection page opens. If the directory already contains files, a warning page opens: \image ifw-warning-existing-installation.png "Warning page" \section1 Selecting Components The component selection page lists the components available for installation and a short description of each component. End users select the components to install. They can select \gui {Select All} to select all components, \gui {Deselect All} to deselect them, or \gui Default to revert to the default selection. \image ifw-select-components.png "Component selection page" You add the installable components to the \c data directory in the package directory. You specify information about the components in the \c package.xml file in the \c meta directory. You can use a boolean operator or a script to specify whether a component is selected by default. When end users select \gui Continue, the license check page opens. \section1 Accepting License Agreements On the license check page, end users must accept the terms of the license agreement for the installation to continue. \image ifw-license-check-page.png "License check page" The license check page is displayed, if you specify a license file in the \c package.xml file and copy the file to the \c meta directory. \section1 Selecting Windows Program Group On Windows, the Start menu directory selection page enables end users to select the program group for the product in the Windows \gui Start menu. \image ifw-win-program-group.png "Start menu directory selection page" You specify a default value for the program group in the \c config.xml configuration file. When end users select \gui Next, the ready for installation page opens. \section1 Installing Components The ready for installation page informs end users that the installation can begin when users select \gui Install. \image ifw-ready-for-installation.png "Ready for installation page" During the installation, the perform installation page displays information about how the installation is progressing. End users can select \gui {Show Details} to view more information. \image ifw-perform-installation.png "Perform installation page" When the installation is complete, the installation finished page opens. \image ifw-installation-finished.png "Installation finished page" To this page, you can add the option to start the installed product upon closing the installer. You specify the product to start and the text to display in the \c config.xml configuration file. */ /*! \contentspage index.html \previouspage ifw-use-cases-install.html \page ifw-use-cases-add.html \nextpage ifw-use-cases-remove.html \title Adding Components If end users did not select all the components available for installation during the initial installation, they can use the package manager to install the remaining components from a repository later. The package manager is part of a maintenance tool that is installed together with the application during the initial installation. This only works if a repository that contains the components is available either locally or externally. The following image illustrates the default workflow for installing additional components: \image ifw-user-flow-adding.png "Add components workflow" This section uses the \e {Maintenance Tool} installed by the Qt 5 installer run on OS X as an example implementation of how end users can add components after the initial installation. The Maintenance Tool contains the package manager, updater, and uninstaller. \section1 Starting Package Manager When end users start the Maintenance Tool, the introduction page opens: \image ifw-add-components-introduction.png "Introduction page" When end users select \gui {Package manager}, and then \gui Continue, the component selection page opens. \section1 Selecting Additional Components The component selection page lists the components available for installation and a short description of each component. Installed components are displayed selected in the list. End users select additional components to install. They can select \gui Reset to display the currently installed components again. \image ifw-add-components-selection.png "Select Components page" When end users select \gui Continue, the ready to update page opens. \section1 Installing Selected Components The ready to update page informs end users that the components are installed when users select \gui Update. \image ifw-ready-to-update.png "Ready to Update Packages page" The update page displays information about how installation is progressing. End users can select \gui {Show Details} to view more information. \image ifw-perform-update.png "Update page" When the installation is complete, the update finished page opens. \image ifw-update-finished.png "Update finished page" */ /*! \contentspage index.html \previouspage ifw-use-cases-add.html \page ifw-use-cases-remove.html \nextpage ifw-use-cases-update.html \title Removing Components The following image illustrates the default workflow for removing either all or some installed components: \image ifw-user-flow-removing.png "Remove components workflow" This section uses the Qt 5 Maintenance Tool run on OS X as an example implementation of how end users can remove all or selected components. \section1 Removing All Components When end users start the Maintenance Tool, the introduction page opens: \image ifw-add-components-introduction.png "Introduction page" End users can select \gui {Remove all components}, and then \gui Continue, to remove all installed components. The ready to uninstall page informs end users that the uninstallation can begin when users select \gui Uninstall. \image ifw-ready-to-uninstall.png "Ready to uninstall page" \section1 Removing Selected Components End users can select \gui {Package manager}, and then \gui Continue, to select components to remove on the component selection page: \image ifw-add-components-selection.png "Component selection page" When end users deselect the components to remove, and then select \gui Continue, the ready to update page opens. It informs end users that the components are removed when users select \gui Update. \image ifw-ready-to-update.png "Ready to update page" The update page displays information about how removal is progressing. End users can select \gui {Show Details} to view more information. \image ifw-removing-components.png "Update page" When the removal is complete, the update finished page opens. \image ifw-update-finished.png "Update finished page" */ /*! \contentspage index.html \previouspage ifw-use-cases-remove.html \page ifw-use-cases-update.html \nextpage ifw-use-cases-settings.html \title Updating Components The following image illustrates the default workflow for updating installed components: \image ifw-user-flow-updating.png "Updating workflow" This section uses the Qt 5 Maintenance Tool run on OS X as an example implementation of how end users can update installed components. \section1 Starting Updater When end users start the Maintenance Tool, the introduction page opens: \image ifw-updating-introduction.png "Introduction page" When end users select \gui {Update components}, and then \gui Continue, the component selection page opens. \section1 Selecting Components to Update The updater displays a list of available updates that end users can select from. \image ifw-updating-components.png "Component selection page" When end users select \gui Continue, the ready to update page opens. \section1 Updating Selected Components The ready to update page informs end users that the components are updated when they select \gui Update. \image ifw-ready-to-update.png "Ready to update page" The update page displays information about how updating is progressing. End users can select \gui {Show Details} to view more information. \image ifw-perform-update.png "Update page" When update is complete, the update finished page opens. \image ifw-update-finished.png "Update finished page" */ /*! \contentspage index.html \previouspage ifw-use-cases-update.html \page ifw-use-cases-settings.html \nextpage ifw-tutorial.html \title Specifying Settings Settings pages enable end users to specify proxy settings or install add-on components. End users select \gui Settings on the introduction page to specify the settings. \section1 Specifying Proxy Settings By default, the installer uses system proxy settings. End users can select to use no proxy or specify the proxy settings manually. \image ifw-settings-network.png "Network tab on Settings page" \section1 Installing Add-on Components To install add-on components, end users select the \gui Repositories tab. \image ifw-settings-repositories.png "Repositories tab on Settings page" If the web server requires authentication, end users can add their username and password. To display passwords, end users select \gui {Show Passwords}. To add their own repositories to the installer, end users can select \gui Add and specify the URL that points to the repository. Temporary repositories can be used only once, for initial installation. After the installation, only default and user-defined repositories will be available. */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw.qdoc��������������������������������������������������������������������������������0000664�0000000�0000000�00000155143�13253666515�0015052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ // ********************************************************************** // NOTE: the sections are not ordered by their logical order to avoid // reshuffling the file each time the index order changes (i.e., often). // Run the fixnavi.pl script to adjust the links to the index order. // ********************************************************************** /*! \contentspage{index.html}{Qt Installer Framework} \page index.html \nextpage ifw-overview.html \title Qt Installer Framework Manual \section1 Version \ifwversion The Qt Installer Framework provides a set of tools and utilities to create installers for the supported desktop Qt platforms: Linux, Microsoft Windows, and OS X. \note Report bugs and suggestions for the Qt Installer Framework project in the \l{https://bugreports.qt.io/browse/QTIFW}{Qt Bugtracker}. \list \li \l{Overview of Qt Installer Framework} \li \l{Getting Started} \li \l{End User Workflows} \list \li \l{Initial Installation} \li \l{Adding Components} \li \l{Removing Components} \li \l{Updating Components} \li \l{Specifying Settings} \endlist \li \l{Tutorial: Creating an Installer} \li \l{Creating Installers} \list \li \l{Creating Offline Installers} \li \l{Creating Online Installers} \li \l{Promoting Updates} \li \l{Customizing Installers} \endlist \li \l{Qt Installer Framework Examples} \li \l{Reference} \list \li \l{Configuration File} \li \l{Package Directory} \li \l{Controller Scripting} \li \l{Component Scripting} \li \l{Operations} \li \l{Tools} \li \l{Scripting API} \li \l{C++ API} \endlist \li \l{Known Issues} \endlist */ /*! \contentspage index.html \previouspage ifw-tutorial.html \page ifw-creating-installers.html \nextpage ifw-offline-installers.html \title Creating Installers The following steps are needed to create offline and online installers: \list 1 \li Create a \e {package directory} for the installable components. For more information, see \l{Package Directory}. \li Create a configuration file called \c config.xml in the \c config directory. It contains information about how to build the installer binaries and online repositories. For more information about the file format and available settings, see \l{Configuration File}. \li Create a package information file called \c package.xml in the \c {config\meta} directory. It contains settings for deployment and the installation process. For more information, see \l{Meta Directory}. \li Create installer content and copy it to the package directory. For more information, see \l{Data Directory}. \li For online installers, use the \c repogen tool to create the repository that contains the installable content and upload the repository to a web server. \li Use the \c binarycreator tool to create the installer. For more information, see \l{Tools}. \endlist For an example of how to create a simple installer that uses the predefined installer pages, see \l{Tutorial: Creating an Installer}. The following sections describe how to create different types of installers: \list \li \l{Creating Offline Installers} \li \l{Creating Online Installers} \li \l{Promoting Updates} \li \l{Customizing Installers} \endlist */ /*! \contentspage index.html \previouspage ifw-reference.html \page ifw-globalconfig.html \nextpage ifw-component-description.html \title Configuration File The configuration file customizes the UI and behavior of an installer. The file is typically called \c config.xml and located in the \c config directory. A minimal configuration file consists of an \c <Installer> root element with \c <Name> and \c <Version> elements as children. All other elements are optional, and can appear in arbitrary order. The following example shows a typical configuration file: \quotefile examples/config.xml \section1 Summary of Configuration File Elements The following table summarizes the elements in the configuration file. \note We recommend that you place all files that you refer to in the configuration file in the \c config directory. However, you can also use relative paths, which the tools resolve relative to the location of the config.xml file. You can use predefined variables (embedded in @ characters) as values of the elements. For more information, see \l{Predefined Variables}. \table \header \li Element \li Description \row \li Name \target ProductNameTarget \li Name of the product being installed. This is mandatory. \row \li Version \li Version of the product being installed in the following format: [0-9]+((\\.|-)[0-9]+)* such as 1-1; 1.2-2; 3.4.7. This is mandatory. \row \li Title \li Name of the installer as displayed on the title bar. \row \li Publisher \li Publisher of the software (as shown in the Windows Control Panel). \row \li ProductUrl \li URL to a page that contains product information on your web site. \row \li Icon \li Filename for a custom installer icon. The actual file is looked up by attaching a '.icns' (OS X), '.ico' (Windows) or '.png' (Unix) suffix. Deprecated, use \c <InstallerApplicationIcon> or \c <InstallerWindowIcon> instead. \row \li InstallerApplicationIcon \li Filename for a custom installer icon. The actual file is looked up by attaching a '.icns' (OS X), '.ico' (Windows). No functionality on Unix. \row \li InstallerWindowIcon \li Filename for a custom window icon in PNG format for the Installer application. \row \li Logo \li Filename for a logo used as \c QWizard::LogoPixmap. \row \li Watermark \li Filename for a watermark used as \c QWizard::WatermarkPixmap. \row \li Banner \li Filename for a banner used as \c QWizard::BannerPixmap (only used by ModernStyle). \row \li Background \li Filename for an image used as \c QWizard::BackgroundPixmap (only used by MacStyle). \row \li WizardStyle \li Set the wizard style to be used ("Modern", "Mac", "Aero" or "Classic"). \row \li StyleSheet \li Set the stylesheet file. \row \li WizardDefaultWidth \li Sets the default width of the wizard in pixels. Setting a banner image will override this. You can add the \c em or \c ex suffix to the specified value to use the \e em or \e ex unit, as in a CSS file. \row \li WizardDefaultHeight \li Sets the default height of the wizard in pixels. Setting a watermark image will override this. You can add the \c em or \c ex suffix to the specified value to use the \e em or \e ex unit, as in a CSS file. \row \li TitleColor \li Set the color of the titles and subtitles (takes an HTML color code, such as "#88FF33"). \row \li RunProgram \li Command executed after the installer is done if the user accepts the action. Provide the full path to the application. \row \li RunProgramArguments \li Arguments passed to the program specified in \c <RunProgram>. You can add several \c <Argument> child elements that each specify an argument to \c <RunProgram>. \row \li RunProgramDescription \li Text shown next to the check box for running the program after the installation. If \c <RunProgram> is set but no description provided, the UI will display \uicontrol {Run <Name> now.} instead. \row \li StartMenuDir \li Name of the default program group for the product in the Windows \uicontrol Start menu. \row \li TargetDir \li Default target directory for installation. On Linux, this is usually the user's home directory. \row \li AdminTargetDir \li Default target directory for installation with administrator rights. Only available on Linux, where you usually do not want to install in the administrator user's home directory. \row \li RemoteRepositories \li List of remote repositories. This element can contain several \c <Repository> child elements that each contain the \c <Url> child element that specifies the URL to access the repository. For more information, see \l{Configuring Repositories}. \row \li MaintenanceToolName \li Filename of the generated maintenance tool. Defaults to \e maintenancetool. The platform-specific executable file extension is appended. \row \li MaintenanceToolIniFile \li Filename for the configuration of the generated maintenance tool. Defaults to \e {MaintenanceToolName}.ini. \row \li RemoveTargetDir \li Set to \c false if the target directory should not be deleted when uninstalling. \row \li AllowNonAsciiCharacters \li Set to \c true if the installation path can contain non-ASCII characters. \row \li DisableAuthorizationFallback \li Set to \c true if the installation should not ask users to run the authorization fallback in case of authorization errors. Instead abort the installation immediately. \row \li RepositorySettingsPageVisible \li Set to \c false to hide the repository settings page inside the settings dialog. \row \li AllowSpaceInPath \li Set to \c false if the installation path cannot contain space characters. \row \li DependsOnLocalInstallerBinary \li Set to \c true if you want to prohibit installation from an external resource, such as a network drive. This might make sense for e.g. very big installers. The option is only used on Windows. \row \li TargetConfigurationFile \li Filename for the configuration file on the target. Default is components.xml. \row \li Translations \li List of language codes to be used for translating the user interface. To add several language variants, specify several \c <Translation> child elements that each specify the name of a language variant. Optional. For more information, see \l{Translating Pages}. \row \li UrlQueryString \li This string needs to be in the form "key=value" and will be appended to archive download requests. This can be used to transmit information to the webserver hosting the repository. \row \li ControlScript \li Filename for a custom installer control script. See \l{Controller Scripting}. \row \li CreateLocalRepository \li Set to \c true if you want to create a local repository inside the installation directory. This option has no effect on online installers. The repository will be automatically added to the list of default repositories. \row \li InstallActionColumnVisible \li Set to \c true if you want to add an extra column into component tree showing install actions. This extra column indicates whether a component is going to be installed or uninstalled, or just stay installed or uninstalled. \row \li SupportsModify \li Set to \c false if the product does not support modifying an existing installation. \endtable */ /*! \contentspage index.html \previouspage ifw-updates.html \page ifw-customizing-installers.html \nextpage Qt Installer Framework Examples \title Customizing Installers You can use scripting to customize installers by: \list \li Adding Qt Installer Framework \e operations that are prepared by the scripts and performed by the installer. \li Adding new pages that you specify in the package.xml file and place in the \c packages directory. \li Modifying existing pages by inserting custom user interface elements into them as single widgets. \li Adding language variants. \endlist You can use both \e {component scripts} and a \e {control script} to customize installers. A component script is associated with a particular component by specifying it in the \c Script element of the package.xml file of the component. The script is loaded when the component's metadata is fetched. For more information about component scripts, see \l{Component Scripting}. A control script is associated with the whole installer by specifying it in the \c ControlScript element of the control.xml file of the installer. Control scripts can be part of the installer resources or be passed on the command line. They can be used to modify the installer pages that are presented to users before components are loaded. Also, you can use them to modify pages in the uninstaller. For more information, see \l{Controller Scripting}. For more information about the global JavaScript objects that can be used in component and control scripts, see \l{Scripting API}. \section1 Adding Operations You can use component scripts to perform Qt Installer Framework operations during the installation process. Typically, operations manipulate files by moving, copying, or patching them. Use the QInstaller::Component::addOperation or QInstaller::Component::addElevatedOperation function to add operations. For more information, see \l {Adding Operations to Components}. In addition, you can implement methods to register custom installation operations in the installer by deriving KDUpdater::UpdateOperation. For more information, see \l {Registering Custom Operations}. For a summary of available operations, see \l {Operations}. \section1 Adding Pages A component can contain one or more user interface files, which are placed into the installer by a component or control script. The installer automatically loads all user interface files listed in the \c UserInterfaces element of the package.xml file. \section2 Using Component Scripts to Add Pages To add a new page to the installer, use the installer::addWizardPage() method and specify the location of the new page. For example, the following code adds an instance of \c MyPage before the ready for installation page: \code installer.addWizardPage( component, "MyPage", QInstaller.ReadyForInstallation ); \endcode You can use component scripts to access the loaded widgets by calling the \l component::userInterface() method with the class name of the widget, as illustrated by the following code snippet: \code component.userInterface( "MyPage" ).checkbox.checked = true; \endcode \section2 Using Control Scripts to Add Pages To register a custom page, use the installer::addWizardPage() method and the object name set in the UI file (for example, \c "MyPage"). Then call the \c{Dynamic${ObjectName}Callback()} function (for example, \c {DynamicMyPageCallback()}): \code function Controller() { installer.addWizardPage(component, "MyPage", QInstaller.TargetDirectory) } Controller.prototype.DynamicMyPageCallback() { var page = gui.pageWidgetByObjectName("DynamicMyPage"); page.myButton.click, page.myWidget.subWidget.setText("hello") } \endcode You can access widgets by using their object names that are set in the UI file. For example, \c myButton and \c myWidget are widget object names in the code above. \section1 Adding Widgets You can use component or control scripts to insert custom user interface elements into the installer as single widgets (such as a check box). To insert a single widget, use the installer::addWizardPageItem method. For example, the following code snippet adds an instance of \c MyWidget to the component selection page from within a script: \code installer.addWizardPageItem( component, "MyWidget", QInstaller.ComponentSelection ); \endcode \section1 Interacting with Installer Functionality You can use control scripts to execute installer functions automatically in tests, for example. The following snippet illustrates how to automatically click the \uicontrol Next button on the target directory selection page: \code Controller.prototype.TargetDirectoryPageCallback = function() { gui.clickButton(buttons.NextButton); } \endcode \section1 Translating Pages The installer uses the Qt Translation system to support the translation of user-readable output to several languages. To provide end users with localized versions of strings contained in the component scripts and user interfaces, create QTranslator files that the installation system loads along with the component. The installer loads the translation file that matches the current system locale. For example, if the system locale is German, the de.qm file is loaded. In addition, a localized \c license_de.txt is shown instead of the default \c license.txt if it is found. Translations need to be added to the \c package.xml file to be activated for a component: \code <Translations> <Translation>de.qm</Translation> </Translations> \endcode Use the \c {qsTr()} function for literal text within scripts. Additionally, you can add the \c Component.prototype.retranslateUi method to the script. It is called when the language of the installer changes and the translation file is loaded. The context being used for translation is the basename of the script file when using \c qsTr or the class name of the UI file when translating a user interface. For example, if the script file is called \c installscript.qs, the context will be installscript. \note The translation system can also be used to customize the UI. Use e.g. an \c en.ts file to replace any text in the installer with a custom English version. */ /*! \contentspage index.html \previouspage ifw-globalconfig.html \page ifw-component-description.html \nextpage noninteractive.html \title Package Directory Installers contain components that are either embedded to the installer or loaded from a remote repository. In both cases, you need to use a file format and structure for the components that the installer can read. \section1 Package Directory Structure Place all components in the same root directory, which is called the \e {package directory}. The directory name acts as a domain-like identifier, which identifies all components. For example, \c com.vendor.root. Within the root directory, create subdirectories called \c data and \c meta. A package directory can look as follows: \code -packages - com.vendor.root - data - meta - com.vendor.root.component1 - data - meta - com.vendor.root.component1.subcomponent1 - data - meta - com.vendor.root.component2 - data - meta \endcode \section1 Meta Directory The \c meta directory contains files that specify settings for deployment and the installation process. The files are not extracted by the installer. The directory must contain at least a package information file and all files that you refer to in the package information file, such as scripts, user interface files, and translations. \section2 Package Information File Syntax The package.xml file is the main source of information about a component. The following is an example of a package file: \quotefile examples/package.xml \section2 Summary of Package Information File Elements \table \header \li Element \li Description \row \li DisplayName \li Human-readable name of the component. Required. Specify translations for the name of the component as values of additional DisplayName tags, with the xml:lang attribute set to the correct locale. \row \li Description \li Human-readable description of the component. Required. Translations may be specified similarly to DisplayName tag. If a localization that matches the locale is not found and an untranslated version exists, that one will be used. Otherwise no Description will be shown for that locale. \row \li Version \li Version number of the component in the following format: [0-9]+((\\.|-)[0-9]+)* such as 1-1; 1.2-2; 3.4.7. Required. If a package needs to show the version number from a child rather than it's own (due to grouping of child packages) one can specify the attribute inheritVersionFrom with the package name the version needs to be inherited from. \row \li ReleaseDate \li Date when this component version was released. Required. \row \li Name \li Domain-like identification for this component. Required. \row \li Dependencies \li Comma-separated list of identifiers of components that this component depends on. Optionally, you can specify version numbers, separated by a dash (-). You can prefix version numbers with a comparison operator (=, >, <, >= or <=). Keep in mind that you have to use the character reference "<" to escape the left angle bracket (use "<" instead of "<" and "<=" instead of "<="). Optional. For more information, see \l{Component Dependencies}. \row \li AutoDependOn \li Comma-separated list of identifiers of components that this component has an automatic dependency on. The component is installed if and only if all of the specified dependencies are fulfilled. If a component has an automatic dependency on other components, the check box will not be visible next to the component in the component tree. The selection will be performed automatically. If the component was not installed before, it will be selected for installation only when all components from this list are also selected for installation. If the component was already installed, it will be selected for uninstallation when at least one of the components from this list is also selected for uninstallation. For more information, see \l{Component Dependencies}. \row \li Virtual \li Set to \c true to hide the component from the installer. Note that setting this on a root component does not work. \row \li SortingPriority \li Priority of the component in the tree. The tree is sorted from highest to lowest priority, with the highest priority on the top. \row \li Licenses \li List of license agreements to be accepted by the installing user. To add several licenses, add several \c <License> child elements that each specify the license \c name and \c file. ASCII and UTF8 file formats are supported for license files. If there are translations listed for this component, the installer will also look for translated licenses. These need to have the same name as the original license file but with an added locale identifier. If for example the license file is called license.txt and there is German translation specified, the installer will also include a license_de_de.txt file (and show that when installing on a German system). \row \li Script \li File name of a script being loaded. Optional. For more information, see \l{Adding Operations}. \row \li UserInterfaces \li List of pages to load. To add several pages, add several \c <UserInterface> child elements that each specify the filename of a page. Optional. For more information, see \l{Adding Pages}. \row \li Translations \li List of translation files to load. To add several language variants, specify several \c <Translation> child elements that each specify the filename of a language variant. Optional. For more information, see \l{Translating Pages}. \row \li UpdateText \li Description added to the component description if this is an update to the component. Optional. \row \li Default \li Possible values are: \c true, \c false, and \c script. Set to \c true to preselect the component in the installer. This takes effect only on components that have no visible child components. The boolean values are evaluated directly, while \c script is resolved during runtime. Add the name of the script as a value of the \c <Script> element in this file. For an example script, see \l{Selecting Default Contents}. \row \li Essential \li Marks the package as essential to force a restart of the \c MaintenanceTool. If there are updates available for an essential component, the package manager stays disabled until that component is updated. Newly introduced essential components are automatically installed when running the updater. \row \li ForcedInstallation \li Determines that the package must always be installed. End users cannot deselect it in the installer. \row \li Replaces \li Comma-separated list of components to replace. Optional. \row \li DownloadableArchives \li Lists the data files (separated by commas) for an online installer to download. If there is some data inside the component and the package.xml and/or the script has no DownloadableArchives value, the repogen tool registers the found data automatically. \row \li RequiresAdminRights \li Set to \c true if the package needs to be installed with elevated permissions. Optional. \row \li Checkable \li Set to \c false if you want to hide the checkbox for an item. This is useful when only a few subcomponents should be selected instead of all. Optional. \endtable \section2 Component Dependencies Components can depend on one or several real or virtual components. Dependencies are defined by using the component identifier and, optionally, component version. Use a dash (-) to separate version numbers from identifiers. You can prefix version numbers with a comparison operator (=, >, < (<), >= or <= (<=)) to indicate that the version number of the package is compared to the required version and has to be equal to, greater than, less than, greater than or equal to, or less than or equal to the version number specified in the dependency. If no comparison operator is given, it defaults to =. \section1 Data Directory The \c data directory contains the content that the installer extracts during the installation. You must package the data as a 7zip archive (.7z). You can use either the \l archivegen tool that is delivered with the Qt Installer Framework or some other tool that generates 7zip archives. */ /*! \contentspage index.html \previouspage operations.html \page ifw-tools.html \nextpage Scripting API \title Tools The Qt Installer Framework contains the following tools: \list \li \l installerbase \li \l binarycreator \li \l repogen \li \l archivegen \li \l devtool \endlist \section1 installerbase The \c installerbase tool describes the core installer itself. All data and meta information will be packed to this binary. For the installer creation process you will not need to call it directly. \section1 binarycreator Use the \c binarycreator tool to create offline and online installers. Component information and data are appended to the offline installer binary, which enables the file extraction and post installation scripts to work without an Internet connection. Online installers store the location of the repository that contains the data. On startup, they load the component information, not the data. You can also create hybrid installers that store some components locally and receive others via a network connection. For more information, see ###TODO insert link here. For information about how to implement data integration into the installer binary, see QInstaller::BinaryContent. \note If you change this configuration, you must recompile the \c installerbase tool. \section2 Using binarycreator You can use the \c binarycreator tool to create offline and online installers. Some options have default values, and therefore, you can omit them. To create an offline installer (in Windows), enter the following command: \list \li On Windows: \code <location-of-ifw>\binarycreator.exe -t <location-of-ifw>\installerbase.exe -p <package_directory> -c <config_directory>\<config_file> <installer_name> \endcode \li On Linux and OS X \code <location-of-ifw>/binarycreator -t <location-of-ifw>/installerbase -p <package_directory> -c <config_directory>/<config_file> <installer_name> \endcode \endlist To create an online only installer, you can use the --online-only which defines all packages to install from an online repository on a web server: \list \li On Windows: \code <location-of-ifw>\binarycreator.exe -t <location-of-ifw>\installerbase.exe -p <package_directory> -c <config_directory>\<config_file> -e <packages> <installer_name> \endcode \li On Linux and OS X \code <location-of-ifw>/binarycreator -t <location-of-ifw>/installerbase -p <package_directory> -c <config_directory>/<config_file> -e <packages> <installer_name> \endcode \endlist \section2 Summary of binarycreator Parameters The \c binarycreator tool accepts the following parameters: \table \header \li Parameter \li Use \row \li -t or --template file \li Use \c file as an installer template binary to which the component information is appended. If you omit this parameter, the \c installerbase template is used. \row \li -p or --packages directory \li Use \c directory as the \l{Package Directory Structure} {package directory}. Defaults to the current working directory. \row \li -n or --online-only \li Compile without any component in the installer binary. \row \li -f or --offline-only \li Create an offline installer that never accesses online repositories. \row \li -c or --config file \li Use \c file as the \l{Configuration File} {config file}. \row \li -e or --exclude p1,...,pn \li Comma-separated list of packages to retrieve from an online repository. The packages are not included in the installer binary. \row \li -i or --include p1,...,pn \li Comma-separated list of packages to retrieve from the installer binary. You cannot combine or merge \c {--include} and \c {--exclude}. \row \li -r or --resources \li Comma-separated list of resources to include in the installer binary. \row \li --ignore-translations \li Disable the use of translation files to make testing faster. \row \li --ignore-invalid-packages \li Ignore component or package directories that do not have valid metadata information (package.xml) to make testing faster. \row \li -v or --verbose \li Display debug output. \row \li -s or --sign identity \li Only available on OS X. Allows specifying a code signing identity to be used for signing the generated app bundle. \endtable These parameters are followed by the name of the target binary and a list of packages to be available for installation. \note The listed packages are included in the installer, as well as all their dependencies and all packages that share the same prefix, unless you specify the \c --nodeps parameter. In the optional resource files specified via the \c --resources parameter, a special \c fonts/ path can be used to ship custom fonts. These fonts will be loaded automatically and thus become available in a stylesheet which can be specified via the \c StyleSheet variable. On Windows, the name of the target binary is automatically extended with .exe, if you do not specify the extension. On Mac, the target is created as an application bundle with the extension .app, which is automatically added, if not supplied. Additionally, you can specify the .dmg extension, which creates a DMG disk image that contains an .app bundle. \section2 Using Icons On OS X, if the target binary is suffixed with .app, a OS X application bundle is created. The icon that you specify in config.xml is extended with .icns and used as the icon for the created bundle. On Windows, the icon that you specify in config.xml is extended with .ico and used as the application icon for the .exe file. On Linux, the icon that you specify in config.xml is extended with .png and used as the window icon. \section1 repogen Use the \c repogen tool to generate online repositories. The \c repogen tool expects the following parameters in the following order: \code repogen.exe -p <package_directory> <repository_directory> \endcode When the repository has been created, you can upload it anywhere. You must specify the location in the installer configuration file when creating an installer for it. \section2 Summary of repogen Parameters \table \header \li Parameter \li Use \row \li -p or --packages directory \li Use \c directory as the \l{Package Directory Structure} {package directory}. This is mandatory. \row \li repository directory \li Target directory for the repository. During an initial installation, the directory must not yet exist. When updating, the directory may exist and its contents may be overwritten or removed. This is mandatory. \row \li -e or --exclude p1,...,pn \li Comma-separated list of packages to be ignored from the set of all packages in the packages directory. \row \li -i or --include p1,...,pn \li Comma-separated list of packages to be used from the set of all packages in the packages directory. \row \li --update \li Update all packages in the packages directory. The list can be further filtered with the \c {-i}, \c {-e} parameters. \row \li --update-new-components \li Update only components that are new or have a newer version. The list can be further filtered with the \c {-i}, \c{-e} parameters. \row \li -r or --remove \li Force removal of existing target directory before generating it again. \row \li -v or --verbose \li Display debug output. \endtable \note We recommend that you use the \c {--update-new-packages} parameter to update an existing repository, especially if you have a content delivery system. This helps you avoid updating components that have not changed and this makes it much easier for a content delivery system to distribute only new files, because only the updated components are assigned new SHA checksums. \section1 archivegen You can use \c archivegen to package files and directories into 7zip (.7z) archives. The \c archivegen tool expects the following parameters in the following order: \code archivegen <name.7z> <data> \endcode Where \e <name.7z> is the path and file name of the archive to create and \e <data> contains the paths and names of the files or directories to package into the archive, separated by spaces. \section1 devtool You can use \c devtool to update an existing installer or maintenance tool with a new installer base, to dump binary content from an installer or maintenance tool to a target, and to execute operations. For a summary of available operations, see \l {Operations}. \c devtool expects the following parameters in the following order: \code devtool [options] binary \endcode Where \e binary is the path and name of an existing installer or maintenance tool. \section2 Summary of devtool Parameters \table \header \li Parameter \li Use \row \li -?, -h, --help \li Display help. \row \li -v, --version \li Display version information. \row \li --verbose \li Display additional information. \row \li --update <file> \li Update an existing installer or maintenance tool with a new installer base. \row \li --dump <folder> \li Dump the binary content that belongs to an installer or maintenance tool into the target. \row \li --operation <mode,name,args,...> \li Execute an operation with a list of arguments. \c mode can be \c DO or \c UNDO, depending on whether the step contains instructions for the installer or uninstaller. \endtable */ /*! \contentspage index.html \previouspage ifw-offline-installers.html \page ifw-online-installers.html \nextpage ifw-updates.html \title Creating Online Installers Online installers fetch the repository description (Updates.xml), in addition to the one stored inside of the binary. Create a repository and upload it to a web server. Then specify the location of the repository in the config.xml file that you use to create the installer. \section1 Creating Repositories Use the \c repogen tool to create online repositories of all packages of one package directory: repogen.exe -p <package_directory> <repository_directory> For example, to create a repository that contains only org.qt-project.sdk.qt and org.qt-project.sdk.qtcreator, enter the following command: \code repogen.exe -p packages -i org.qt-project.sdk.qt,org.qt-project.sdk.qtcreator repository \endcode When the repository has been created, upload it to a web server. You must specify the location of the repository in the installer configuration file. \section1 Configuring Repositories The \c <RemoteRepositories> element in the installer configuration file (config.xml) can contain a list of several repositories. Each of them can have the following settings: \list \li \c <Url>, which points to a list of available components. \li \c <Enabled>, with 0 disabling this repository. \li \c <Username>, which is used as user on a protected repository. \li \c <Password>, which sets the password to use on a protected repository. \li \c <DisplayName>, which optionally sets a string to display instead of the URL. \endlist The URL needs to point to the Updates.xml file that lists the available components. For example: \code <RemoteRepositories> <Repository> <Url>http://www.example.com/packages</Url> <Enabled>1</Enabled> <Username>user</Username> <Password>password</Password> <DisplayName>Example repository</DisplayName> </Repository> </RemoteRepositories> \endcode The installer works only if it can access the repository. If the repository is accessed after the installation, the maintenance tool rejects installation. However, uninstallation is still possible. A repository can be enabled or disabled by default. For repositories requiring authentication, the details can also be set here, although entering a password here is usually not advisable as it is saved in plain text. Authentication details not set here will be gotten at runtime using a dialog. The user can work around these settings at runtime. \section1 Creating Installer Binaries To create an online installer by using the \c binarycreator tool, enter the following command: \code <location-of-ifw>\binarycreator.exe -t <location-of-ifw>\installerbase.exe -p <package_directory> -c <config_directory>\<config_file> -e <packages> <installer_name> \endcode For example, enter the following command to create an installer binary called SDKInstaller.exe that will not contain data for org.qt-project.sdk.qt and org.qt-project.qtcreator, because those packages are downloaded from a remote repository: \code binarycreator.exe -p installer-packages -c installer-config\config.xml -e org.qt-project.sdk.qt,org.qt-project.qtcreator SDKInstaller.exe \endcode \section1 Reducing Installer Size Even if the components are fetched from a web server, \c binarycreator adds them to the installer binary by default. However, when the installer checks the web server for updates, end users are spared a download if new versions are not available. Alternatively, you can create online installers that do not contain any data and that fetch all the data from the web server. Use the \c{-n} parameter of the \c binarycreator tool and only add the root component to the installer. Usually the root component is empty and hence only adds the XML description of the root. For more information about the options that you have, see \l{Summary of binarycreator Parameters}. */ /*! \contentspage index.html \previouspage ifw-creating-installers.html \page ifw-offline-installers.html \nextpage ifw-online-installers.html \title Creating Offline Installers Offline installers do not try to connect to an online repository at all during installation. However, the metadata configuration (config.xml) enables users to add and update components online. Offline installers are especially useful in cases where a corporate firewall does not allow end users to connect to web servers. The network administrator can set up a local update service within the network. To create offline installers, use the \c{--offline-only} option of the \c binarycreator tool. To create an offline installer in Windows, enter the following command: \code <location-of-ifw>\binarycreator.exe --offline-only -t <location-of-ifw>\installerbase.exe -p <package_directory> -c <config_directory>\<config_file> <installer_name> \endcode Some options have default values, and therefore, you can omit them. For example, enter the following command to create an installer binary called SDKInstaller.exe that contains the packages identified by org.qt-project.sdk and their dependencies: \code binarycreator.exe --offline-only -c installer-config -p installer-packes SDKInstaller.exe \endcode */ /*! \contentspage index.html \previouspage ifw-online-installers.html \page ifw-updates.html \nextpage ifw-customizing-installers.html \title Promoting Updates Create online installers to be able to promote updates to end users who install your product. The following steps are needed to promote updates: \list 1 \li Copy the updated content to the package directory. \li Increase the value of the \c <Version> element for the updated components in the package.xml file. \li Use the \c repogen tool to recreate the online repository with the updated contents and to generate the Updates.xml file in the root directory of the repository. \li Upload the repository to the web server. \li Use the \c binarycreator tool to create the installer. \endlist \section1 Configuring Updates The installer downloads the Updates.xml file on startup and compares the installed version with the version in the file. If the online version number in the file is greater than the local one, the installer displays it in the list of available updates. Increase the value of the \c <Version> element for the component in the package.xml file. \section1 Recreating Repositories The easiest way to provide an update is to recreate the repository and upload it to the web server. For more information, see \l{Creating Repositories}. \section1 Partially Updating Repositories A full update of the whole repository might not be optimal if: \list \li The repository is very large, as uploading would take a long time. \li You want to deliver only the changed components. \endlist \note repogen recreates the 7zip archives each time it is being called. As 7zip stores the timestamps of the included files (which are moved or copied during this process), the SHA sum of each archive changes. SHA sums are used to verify the download of the archive and hence the SHA needs to match the 7zip. As the SHAs are stored in the Updates.xml file you will be forced to upload the full repository. This can be circumvented by using the \c{--update} option of repogen. \section2 Creating Partial Updates When recreating the online repository, use the \c{--update} parameter. It takes an existing repository as input and only changes the components that are specified as additional parameters. Only those SHA sums are changed in the global configuration as well. \section2 Uploading Partial Updates Upload the following items to the web server: \list \li The component directory (usually something like \c{com.vendor.product.updatedpart}). \li The global \c{Updates.xml} stored in the root directory of the online repository. \endlist \note The order of uploading items is very important. If you update the repository on a live server, first update the component and then Updates.xml. The package names include version numbers, and therefore, end users receive old packages until the new ones are fully uploaded. \section1 Changing Repositories To have the current update repository point to other repositories, edit the Updates.xml file in the current repository. You can add, replace, or remove repositories. \code <RepositoryUpdate> <Repository action="..." OPTIONS /> <Repository action="..." OPTIONS /> </RepositoryUpdate> \endcode \section2 Adding Repositories To update a repository, add a \c <Repository> child element to the \c <RepositoryUpdate> element with the following options: \code <Repository action="add" url="http://www.example.com/repository" name="user" password="password" displayname="Example Repository" /> \endcode \c{url} will be used as a base URL to resolve an \c{Updates.xml} file against. If \c{url} is itself relative, it will be resolved against the base URL of the current document. \c{displayname} specifies how the repository should be named in the \gui Settings page of the Maintenance Tool. \c{name} and \c{password} optionally specify credentials for a protected repository. \section2 Removing Repositories To remove a repository, add a \c <Repository> child element to the \c <RepositoryUpdate> element with the following options: \code <Repository action="remove" url="http://www.example.com/repository" /> \endcode \c{url} must match exactly the URL that is to be removed. \section2 Replacing Repositories To replace one repository with another, add a \c <Repository> child element to the \c <RepositoryUpdate> element with the following options: \code <Repository action="replace" oldUrl="http://www.example.com/repository" newUrl="http://www.example.com/newrepository" name="user" password="password" displayname="New Example Repository" /> \endcode \c{oldUrl} must match exactly the URL that is to be replaced. \c{newUrl} must match exactly the URL that it is replaced with. \section1 Relocatable Repositories Some projects contain multiple repositories. To create a relocatable set of repositories you should use relative paths. So if the generic repository available at the address \c{http://www.example.com/repositories/generic} and \c{Updates.xml} contains \c <Repository> element with the following options: \code <Repository action="add" url="../module" name="user" password="password" displayname="Module Repository" /> \endcode Then the resolved address of the added repository will be \c{http://www.example.com/repositories/module}, so that the repository does not contain information about their absolute location. If you want to change the address, you can simply copy a set of repositories as is. It is recommended to maintain the old generic repository for some time and replace the addresses as described above. You can also provide the updated installer with the new generic address. You can use relative paths for the arguments \c url, \c oldUrl, and \c newUrl in the \c <Repository> element. */ /*! \contentspage index.html \previouspage ifw-cpp-classes.html \page ifw-knownissues.html \nextpage index.html \title Known Issues Check the \l{https://bugreports.qt.io/browse/QTIFW}{Qt Bugtracker} for known issues in the Qt Installer Framework project. If you cannot find the issue there, create a bug report. */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/installerfw.qdocconf����������������������������������������������������������������������������0000664�0000000�0000000�00000000233�13253666515�0015705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Run qdoc from the directory that contains this file. include(config/ifw.qdocconf) include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/messageboxhandler.qdoc��������������������������������������������������������������������������0000664�0000000�0000000�00000007356�13253666515�0016215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype QMessageBox \inqmlmodule scripting \brief Provides a modal dialog for informing the user or asking the user a question and receiving an answer. \code var result = QMessageBox.question("quit.question", "Installer", "Do you want to quit the installer?", QMessageBox.Yes | QMessageBox.No); if (result == QMessageBox.Yes) { // ... } \endcode \section2 Buttons in Message Boxes QMessageBox defines a list of common buttons: \list \li QMessageBox.Ok \li QMessageBox.Open \li QMessageBox.Save \li QMessageBox.Cancel \li QMessageBox.Close \li QMessageBox.Discard \li QMessageBox.Apply \li QMessageBox.Reset \li QMessageBox.RestoreDefaults \li QMessageBox.Help \li QMessageBox.SaveAll \li QMessageBox.Yes \li QMessageBox.YesToAll \li QMessageBox.No \li QMessageBox.NoToAll \li QMessageBox.Abort \li QMessageBox.Retry \li QMessageBox.Ignore \li QMessageBox.NoButton \endlist \section2 Scripted Installations Sometimes it is useful to automatically close message boxes, for example during a scripted installation. This can be achieved by calling installer::setMessageBoxAutomaticAnswer, installer::autoAcceptMessageBoxes or installer::autoRejectMessageBoxes. The \c identifier argument in the method calls allows to identify specific message boxes for this purpose. */ /*! \qmlmethod Button QMessageBox::critical(string identifier, string title, string text, Buttons buttons = QMessageBox.Ok, Button button = QMessageBox.NoButton) Opens a critical message box with the given \a title and \a text. */ /*! \qmlmethod Button QMessageBox::information(string identifier, string title, string text, Buttons buttons = QMessageBox.Ok, Button button = QMessageBox.NoButton) Opens an information message box with the given \a title and \a text. */ /*! \qmlmethod Button QMessageBox::question(string identifier, string title, string text, Buttons buttons = QMessageBox.Yes | QMessageBox.No, Button button = QMessageBox.NoButton) Opens a question message box with the given \a title and \a text. */ /*! \qmlmethod Button QMessageBox::warning(string identifier, string title, string text, Buttons buttons = QMessageBox.Ok, Button button = QMessageBox.NoButton) Opens a warning message box with the given \a title and \a text. */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/noninteractive.qdoc�����������������������������������������������������������������������������0000664�0000000�0000000�00000042042�13253666515�0015541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage{index.html}{Qt Installer Framework} \previouspage ifw-component-description.html \page noninteractive.html \nextpage scripting.html \title Controller Scripting For each installer, you can specify a control script that interacts with certain parts of the installer's UI or functionality. The control script can add and remove pages to the wizard, change existing pages, do additional checks, and interact with the UI by simulating user clicks. This allows for example unattended installations. The script format has to be compatible with QJSEngine. This section describes the functions that are called to implement such a control script. It also gives an overview of installer pages and the widgets that are available on each page, such as push buttons, radio buttons, and line edits. \section1 Writing Control Scripts A minimal valid script needs to contain at least a constructor, which can look like this: \code function Controller() { } \endcode The following example presents a more advanced script that uses the \l gui JavaScript global object methods to set a new page title and welcome message on the introduction page and to automatically click the \uicontrol Next button on the target directory page: \code function Controller() { } Controller.prototype.IntroductionPageCallback = function() { var widget = gui.currentPageWidget(); // get the current wizard page if (widget != null) { widget.title = "New title."; // set the page title widget.MessageLabel.setText("New Message."); // set the welcome text } } Controller.prototype.TargetDirectoryPageCallback = function() { gui.clickButton(buttons.NextButton); // automatically click the Next button } \endcode For more information about the JavaScript global objects that you can use in control scripts, see \l{Scripting API}. \section1 Predefined Installer Pages The QInstaller JavaScript object provides access to the following predefined installer pages: \list \li \c Introduction \li \c TargetDirectory \li \c ComponentSelection \li \c LicenseCheck \li \c StartMenuSelection \li \c ReadyForInstallation \li \c PerformInstallation \li \c InstallationFinished \endlist The \l buttons JavaScript object provides a set of buttons that can be used on installer pages. The following sections describe the functions that you can implement to interact with installer pages and the widgets that are available on each page. \section2 Introduction Page Implement the \c Controller.prototype.IntroductionPageCallback() function to interact with widgets on the introduction page. Wizard buttons: \list \li \c NextButton \li \c CancelButton \endlist \table \header \li Widgets \li Brief Description \row \li \c ErrorLabel \li Displays an error message. \row \li \c MessageLabel \li Displays a message. By default, it displays the "Welcome to the \l{ProductNameTarget}{<Name>} Setup Wizard" message. \row \li \c InformationLabel \li Displays progress information. \endtable \table \header \li Radio Buttons \li Brief Description \row \li \c PackageManagerRadioButton \li The package manager radio button shown on the page while running as maintenance tool. \row \li \c UpdaterRadioButton \li The updater radio button shown on the page while running as maintenance tool. \row \li \c UninstallerRadioButton \li The uninstaller radio button shown on the page while running as maintenance tool. Selected by default. \endtable \table \header \li Progress Bar \li Brief Description \row \li \c InformationProgressBar \li The progress bar shown while fetching remote packages. \endtable \table \header \li Qt Core Feature \li Brief Description \row \li \c packageManagerCoreTypeChanged() \li Connect to this signal if you want to be notified when the type of maintenance tool changes. \note The signal is only emitted when the user has started the binary as so called maintenance tool (after the installation) and switches between the radio buttons. \endtable Example code: \code function Controller() { var widget = gui.pageById(QInstaller.Introduction); // get the introduction wizard page if (widget != null) widget.packageManagerCoreTypeChanged.connect(onPackageManagerCoreTypeChanged); } onPackageManagerCoreTypeChanged = function() { console.log("Is Updater: " + installer.isUpdater()); console.log("Is Uninstaller: " + installer.isUninstaller()); console.log("Is Package Manager: " + installer.isPackageManager()); } \endcode \section2 License Agreement Page Implement the \c Controller.prototype.LicenseAgreementPageCallback() function to interact with widgets on the license agreement page. Wizard buttons: \list \li \c NextButton \li \c CancelButton \li \c BackButton \endlist \table \header \li Widgets \li Brief Description \row \li \c LicenseListWidget \li Lists the available licenses. \row \li \c LicenseTextBrowser \li Shows the content of the selected license file. \row \li \c AcceptLicenseLabel \li Shows the text next to the accept license radio button. \row \li \c RejectLicenseLabel \li Shows the text next to the reject license radio button. \endtable \table \header \li Radio Buttons \li Brief Description \row \li \c AcceptLicenseRadioButton \li Accepts the license agreement. \row \li \c RejectLicenseRadioButton \li Rejects the license agreement. Selected by default. \endtable \section2 Target Directory Page Implement the \c Controller.prototype.TargetDirectoryPageCallback() function to interact with widgets on the target directory selection page. Wizard buttons: \list \li \c NextButton \li \c CancelButton \li \c BackButton \endlist \table \header \li Widgets \li Brief Description \row \li \c MessageLabel \li Displays a message. \row \li \c TargetDirectoryLineEdit \li Displays the value of the installation's target directory. \row \li \c WarningLabel \li Displays a warning. \endtable \section2 Component Selection Page Implement the \c Controller.prototype.ComponentSelectionPageCallback() function to interact with widgets on the component selection page. Wizard buttons: \list \li \c NextButton \li \c CancelButton \li \c BackButton \endlist \table \header \li Methods \li Brief Description \row \li \c selectAll() \li Selects all available packages if possible. \row \li \c deselectAll() \li Deselects all available packages if possible. \row \li \c selectDefault() \li Resets the checked state of available packages to their initial state. \row \li \c selectComponent(id) \li Selects the package with \c id (string). \row \li \c deselectComponent(id) \li Deselects the package with \c id (string). \endtable \table \header \li Push Buttons \li Brief Description \row \li \c SelectAllComponentsButton \li Selects all available packages if possible. \row \li \c DeselectAllComponentsButton \li Deselects all available packages if possible. \row \li \c SelectDefaultComponentsButton \li Resets the checked state of available packages to their initial state. \row \li \c ResetComponentsButton \li Resets to already installed components. \endtable \section2 Start Menu Directory Page Implement the \c Controller.prototype.StartMenuDirectoryPageCallback() function to interact with widgets on the ready for installation page. Wizard buttons: \list \li \c NextButton \li \c CancelButton \li \c BackButton \endlist \table \header \li Widgets \li Brief Description \row \li \c StartMenuPathLineEdit \li Shows the directory where to create the program's shortcut. \endtable \section2 Ready for Installation Page Implement the \c Controller.prototype.ReadyForInstallationPageCallback() function to interact with widgets on the ready for installation page. Wizard buttons: \list \li \c CommitButton \li \c CancelButton \li \c BackButton \endlist \table \header \li Widgets \li Brief Description \row \li \c MessageLabel \li Displays a message. \row \li \c TaskDetailsBrowser \li Displays some more detailed information about the installation. \endtable \section2 Perform Installation Page Implement the \c Controller.prototype.PerformInstallationPageCallback() function to interact with widgets on the perform installation page. Wizard buttons: \list \li \c CommitButton \li \c CancelButton \endlist \section2 Finished Page Implement the \c Controller.prototype.FinishedPageCallback() function to interact with widgets on the installation finished page. Wizard buttons: \list \li \c CommitButton \li \c CancelButton \li \c FinishButton \endlist \table \header \li Widgets \li Brief Description \row \li \c MessageLabel \li Displays a message. \row \li \c RunItCheckBox \li Text field that informs users that they can start an application after the installation process has finished. \endtable \section1 Custom Pages Custom pages are registered as \c{Dynamic${ObjectName}}, where \c{${ObjectName}} is the object name set in the UI file. Thus, the \c{Dynamic${ObjectName}Callback()} function is called. Widgets can be addressed using their object names (from the UI file). Example code: \code function Controller() { // add page with widget \c SomePageWidget before the target directory page installer.addWizardPage(component, "SomePageWidget", QInstaller.TargetDirectory) } Controller.prototype.DynamicSomePageWidgetCallback = function() { var page = gui.pageWidgetByObjectName("DynamicSomePageWidget"); page.myButton.click, //direct child of the UI file's widget page.someFancyWidget.subWidget.setText("foobar") // nested widget } \endcode \section1 Message Boxes While executing the installer application, for example, the application might show some message boxes about an error that occurred. This is fine while running the application on the end user's system, but it might break automated test suites. To overcome this issue, all message boxes shown by the Qt Installer Framework are addressable by a specific identifier. \table \header \li Identifier \li Possible Answers \li Description \row \li \c OverwriteTargetDirectory \li Yes, No \li Confirmation for using an already existing directory as the target directory for installation. \row \li \c installationError \li OK, Retry, Ignore \li A fatal error occurred while performing the installation. \row \li \c installationErrorWithRetry \li Retry, Ignore, Cancel \li An error occurred while performing the installation. End users can select \uicontrol Retry to try again. \row \li \c AuthorizationError \li Abort, OK \li Elevated permissions could not be acquired. \row \li \c OperationDoesNotExistError \li Abort, Ignore \li An error occurred while trying to perform an operation, but the operation did not exist. \row \li \c isAutoDependOnError \li OK \li An error occurred while calling a package script. It was not possible to evaluate if the package has a auto dependency on other packages. \row \li \c isDefaultError \li OK \li An error occurred while calling a package script. It was not possible to evaluate if the package will be installed by default. \row \li \c DownloadError \li Retry, Cancel \li An error occurred while downloading an archive hash from a remote repository. End users can select \uicontrol Retry to try again. \row \li \c archiveDownloadError \li Retry, Cancel \li An error occurred while downloading a archive from a remote repository. End users can select \uicontrol Retry to try again. \row \li \c WriteError \li OK \li An error occurred while writing the maintenance tool. \row \li \c ElevationError \li OK \li Elevated permissions could not be acquired. \row \li \c unknown \li OK \li An unknown error occurred while removing a certain package. \row \li \c Error \li OK \li Generic error. \row \li \c stopProcessesForUpdates \li Retry, Ignore, Cancel \li An error occurred while updating a package. Some running application or process needs to be quit before the update can be performed. End users can select \uicontrol Retry to try again once they have been stopped. \row \li \c Installer_Needs_To_Be_Local_Error \li OK \li The installer binary was started from a network location, but the installation over network is not supported. \row \li \c TargetDirectoryInUse \li No \li The installation's target directory already contains an installation. \row \li \c WrongTargetDirectory \li OK \li The installation's target directory is a file or symlink. \row \li \c AlreadyRunning \li OK \li Another application instance is already running. \endtable Example code: \code function Controller() { installer.autoRejectMessageBoxes; installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes); installer.setMessageBoxAutomaticAnswer("stopProcessesForUpdates", QMessageBox.Ignore); } \endcode */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/operations.qdoc���������������������������������������������������������������������������������0000664�0000000�0000000�00000027600�13253666515�0014677�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage{index.html}{Qt Installer Framework} \previouspage scripting.html \page operations.html \nextpage ifw-tools.html \title Operations The operations are prepared by component and controller scripts and performed by the installer. \note Operations are performed threaded. Internally, each operation has a \e DO step that contains instructions for the installer and an \e UNDO step that contains instructions for the uninstaller. \section1 Summary of Operations The following table summarizes the available operations and their syntax. \table \header \li Operation \li Syntax \li Use \row \li Copy \li "Copy" \c source \c target \li Copies a file from \c source to \c target. \row \li Move \li "Move" \c source \c target \li Moves a file from \c source to \c target. \row \li SimpleMoveFile \li "SimpleMoveFile" \c source \c target \li Moves a file from \c source to \c target. \row \li Delete \li "Delete" \c filename \li Deletes the file specified by \c filename. \row \li Mkdir \li "Mkdir" \c path \li Creates the directory path \c path. \row \li Rmdir \li "Rmdir" \c path \li Removes the directory path \c path. \row \li CopyDirectory \li "CopyDirectory" \c sourcePath \c targetPath \li Copies a directory from \c sourcePath to \c targetPath. \row \li AppendFile \li "AppendFile" \c filename \c text \li Appends \c text to the file specified by \c filename. \c text is treated as ASCII text. \row \li PrependFile \li "PrependFile" \c filename \c text \li Prepends \c text to the file specified by \c filename. \c text is treated as ASCII text. \row \li Replace \li "Replace" \c file \c search \ replace \li Opens \c file to find \c search string and replaces that with the \c replace string. \row \li LineReplace \li "LineReplace" \c file \c search \c replace \li Opens \c file to find lines that start with \c search string and replaces that with the \c replace string. Lines are trimmed before the search. \row \li Execute \li "Execute" [{\c exitcodes}] \c command [\c parameter1 [\c parameter... [\c parameter10]]] \li Executes the command specified by \c command. Up to 10 parameters can be passed. If that is not enough, you can use a JavaScript string array. Optionally, you can pass a comma-separated list of exit codes within curly brackets ({}) as the first argument to specify the exit codes for successful execution. This defaults to "{0}". Other optional named arguments are: "workingdirectory=<your_working_dir>"; "errormessage=<your_custom_errormessage>" In addition, a special argument, UNDOEXECUTE, separates the DO step of the operation from the UNDO step. Example: \c{component.addOperation("Execute", "touch", "test.txt", "UNDOEXECUTE", "rm", "test.txt")} \row \li CreateShortcut \li "CreateShortcut" \c filename \c linkname [\c arguments] \li Creates a shortcut from the file specified by \c filename to \c linkname. On Windows, this creates a .lnk file which can have \c arguments. Furthermore, on Windows, \c filename can be an HTTP or FTP URL in which case a URL shortcut is created. On Unix, this creates a symbolic link. \row \li CreateDesktopEntry \li "CreateDesktopEntry" \c {filename "key=value[ key2=value2[ key3=value3]]]"} \li Creates a .desktop initialization file, as specified by freedesktop.org. If \c filename is absolute, the desktop entry is stored there. Otherwise, it is stored in the location specified in \c{$XDG_DATA_DIRS/applications} or \c{$XDG_DATA_HOME/applications}, including the default paths for both, as defined by freedesktop.org. The key-value pairs are written to the file. The file is set to use UTF-8 encoding. \row \li InstallIcons \li "InstallIcons" \c directory \c [Vendorprefix] \li Installs the contents of \c directory into a location, as specified by freedesktop.org. That is, into \c {$XDG_DATA_DIRS/icons}, \c {/usr/share/icons}, or \c {$HOME/.icons}. The files are removed from their initial location. Make sure to add this operation after the operation that extracts the files from the archive. If you provide a \c Vendorprefix it replaces all characters up to the first dash (-) in the filename of the icon with this prefix. \row \li Extract \li "Extract" \c archive \c targetdirectory \li Extracts \c archive to \c targetdirectory. \row \li GlobalConfig \li "GlobalConfig" \c company \c application \c key \c value or "GlobalConfig" \c scope \c company \c application \c key \c value or "GlobalConfig" \c filename \c key \c value \li Stores \c value for \c key in a configuration file. The configuration file is specified either by \c filename (using \c QSettings::NativeFormat, which might be the Windows registry) or by \c application and \c company name. Set \c scope to "SystemScope" to create an entry in the system scope. \note The operation is using QSettings to store the key value pair. QSettings always treats backslash as a special character and provides no API for reading or writing such entries. Do not use slashes ('/' and '\') in section or key names; the backslash character is used to separate sub keys. On windows, '\' are converted by QSettings to '/', which makes them identical. Because the backslash character is used by QSettings to separate sub keys, you cannot read or write windows registry entries that contain slashes or backslashes. You should use a native windows API if you need to do so. \row \li EnvironmentVariable \li "EnvironmentVariable" \c key \c value [\c persistent [\c system]] \li Sets the environment variable \c key to \c value. If \c persistent is set to \c true, the variable is set persistently. This is currently only supported on Windows. If \c system is set to \c true, the persistent variable is set system-wide, not only for the current user. \row \li RegisterFileType \li "RegisterFileType" \c extension \c command [\c description [\c contentType [\c icon]]]. \li Registers the file type with \c extension to be opened via \c command. Optionally, you can specify \c description, \c contentType, and \c icon. This is currently only supported on Windows. \row \li ConsumeOutput \li "ConsumeOutput" \c installerKeyName \c executablePath \c processArguments \li Saves the output from running the executable located at \c executablePath with the arguments \c processArguments to the installer key specified by \c installerKeyName. Additional arguments can be passed. \row \li CreateLink \li "CreateLink" \c linkPath \c targetPath \li Creates a link from the location specified by \c linkPath to the location specified by \c targetPath. \row \li CreateLocalRepository \li "CreateLocalRepository" \c binaryPath \c repoPath \li Creates a local repository in the directory specified by \c repoPath. For offline installers, stores binary data in the directory specified by \c binaryPath. \row \li FakeStopProcessForUpdate \li "FakeStopProcessForUpdate" \c processlist \li Matches running processes against the comma-separated entries in \c processlist during uninstallation. If matches are found, shows a messagebox asking the user to stop those processes before continuing. \row \li License \li "License" \c licenses \li Copies the license files specified by \c licenses to a subfolder called \c Licenses in the target directory. This operation is automatically added for packages declaring \c <Licenses> in their package description file. \row \li MinimumProgress \li "MinimumProgress" \li Increases the progress value by one. \row \li SelfRestart \li "SelfRestart" \c core \li Restarts the updater or package manager specified by \c core. \row \li Settings \li "Settings" \c path \c method \c key \c aValue \li Sets or removes the value \c aValue of \c key in the settings file or registry located at \c path, depending on the value of \c method: \c set, \c remove, \c add_array_value, and \c remove_array_value. \endtable The Extract, License, and MinimumProgress operations are automatically added for matching components that do not overwrite the component::createOperations() method. See also component::autoCreateOperations. If errors occur, you can test operations manually using the \c devtool. However, variables are not resolved, so you need to use absolute values. For example, to test copying a file: \code devtool --operation DO,Copy,<source>,<target> \endcode */ ��������������������������������������������������������������������������������������������������������������������������������doc/scripting-api/����������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0014410�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/scripting-api/buttons.qdoc����������������������������������������������������������������������0000664�0000000�0000000�00000004663�13253666515�0016767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype buttons \inqmlmodule scripting \brief Provides buttons that can be used on installer pages. You can use a set of standard buttons and some custom buttons on the installer pages. For more information about the buttons used by default on each installer page, see \l {Controller Scripting}. */ /*! \qmlproperty enumeration buttons::QWizard Specifies the buttons on an installer page. \value buttons.BackButton The \uicontrol Back button (\uicontrol {Go Back} on OS X.) \value buttons.NextButton The \uicontrol Next button (\uicontrol Continue on OS X.) \value buttons.CommitButton The \uicontrol Commit button. \value buttons.FinishButton The \uicontrol Finish button (\uicontrol Done on OS X.) \value buttons.CancelButton The \uicontrol Cancel button. \value buttons.HelpButton The \uicontrol Help button. \value buttons.CustomButton1 A custom button. \value buttons.CustomButton2 A custom button. \value buttons.CustomButton3 A custom button. \sa {Controller Scripting} */ �����������������������������������������������������������������������������doc/scripting-api/component.qdoc��������������������������������������������������������������������0000664�0000000�0000000�00000032373�13253666515�0017272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype component \inqmlmodule scripting \brief Represents the current component that the Qt Script belongs to. A minimal valid script needs to contain a constructor, which can look like this: \code function Component() { // Access the global component's name property and log it to the debug console. console.log("component: " + component.displayName); } \endcode The \c Component class and the script engine both modify the script before it is evaluated. When the modifications are applied to the above snippet, the script changes to: \code (function() { [1] var component = installer.componentByName('Component.Name.This.Script.Belongs.To'); [2] function Component() { // Access the global component's name property and log it to the debug console. console.log("component: " + component.displayName); } if (typeof Component != undefined) [1] return new Component; [1] else [1] throw "Missing Component constructor. Please check your script." [1] })(); [1] Changes done by the script engine. [2] Changes done by the Component class. \endcode \note The \e component (in lower case) is the global variable the C++ \c Component class introduced. The \e component variable represents the C++ \c Component object that the script belongs to. The \e Component (in upper case) is a JavaScript object that gets instantiated by the script engine. */ /*! \qmlproperty string component::name Returns the name of the component as set in the \c <Name> tag of the package information file. */ /*! \qmlproperty string component::displayName Returns the name of the component as shown in the user interface. */ /*! \qmlproperty boolean component::autoCreateOperations Specifies whether some standard operations for the component should be automatically created when the installation starts. The default is \c true. */ /*! \qmlproperty stringlist component::archives Returns the list of archive URL's (prefixed with \c installer://) registered for the component. \sa addDownloadableArchive, removeDownloadableArchive */ /*! \qmlproperty stringlist component::dependencies This read-only property contains components this component depends on. */ /*! \qmlproperty stringlist component::autoDependencies Returns the value of the \c <AutoDependOn> tag in the package information file. */ /*! \qmlproperty boolean component::fromOnlineRepository Returns whether this component has been loaded from an online repository. \sa isFromOnlineRepository */ /*! \qmlproperty url component::repositoryUrl Returns the repository URL the component is downloaded from. When this component is not downloaded from an online repository, returns an empty #QUrl. */ /*! \qmlproperty boolean component::default This read-only property indicates if the component is a default one. \note Always \c false for virtual components. \sa isDefault */ /*! \qmlproperty boolean component::installed This read-only property returns if the component is installed. \sa isInstalled */ /*! \qmlproperty boolean component::enabled Indicates whether the component is currently enabled. The property is both readable and writable. */ /*! \qmlsignal component::loaded() Emitted when the component has been loaded. */ /*! \qmlsignal component::valueChanged(string key, string value) Emitted when the value of the variable with the name \a key changes to \a value. \sa setValue */ /*! \qmlsignal component::virtualStateChanged() Emitted when the virtual state of the component changes. */ /*! \qmlmethod string component::value(string key, string value = "") Returns the value of variable name \a key. If \a key is not known yet, \a defaultValue is returned. \note If a component is virtual and you ask for the component value with the key "Default", it will always return \c false. */ /*! \qmlmethod void component::setValue(string key, string value) Sets the value of the variable with \a key to \a value. */ /*! \qmlproperty stringlist component::userInterfaces Returns a list of all user interface class names known to this component. */ /*! \qmlmethod QWidget component::userInterface(string name) Returns the QWidget created for \a name or \c 0 if the widget has been deleted or cannot be found. */ /*! \qmlmethod void component::createOperationsForPath(string path) Creates all operations needed to install this component's \a path. \a path is a full qualified filename including the component's name. This method gets called from createOperationsForArchive. You can override it by providing a method with the same name in the component script. \note RSA signature files are omitted by this method. \note If you call this method from a script, it will not call the script's method with the same name. The default implementation is recursively creating Copy and Mkdir operations for all files and folders within \a path. */ /*! \qmlmethod void component::createOperationsForArchive(string archive) Creates all operations needed to install this component's \a archive. This method gets called from createOperations. You can override this method by providing a method with the same name in the component script. \note If you call this method from a script, it will not call the script's method with the same name. The default implementation calls createOperationsForPath for everything contained in the archive. If \a archive is a compressed archive known to the installer system, an Extract operation is created, instead. */ /*! \qmlmethod void component::beginInstallation() Starts the component installation. You can override this method by providing a method with the same name in the component script. \code Component.prototype.beginInstallation = function() { // call default implementation component.beginInstallation(); // ... } \endcode */ /*! \qmlmethod void component::createOperations() Creates all operations needed to install this component. You can override this method by providing a method with the same name in the component script: \code Component.prototype.createOperations = function() { // call default implementation component.createOperations(); // ... add custom operations } \endcode The default implementation calls createOperationsForArchive for all archives in this component. \sa component::addOperation() */ /*! \qmlmethod void component::registerPathForUninstallation(string path, boolean wipe = false) Registers the file or directory at \a path for being removed when this component gets uninstalled. In case of a directory, this will be recursive. If \a wipe is set to \c true, the directory will also be deleted if it contains changes done by the user after installation. */ /*! \qmlmethod void component::addDownloadableArchive(string path) Adds the archive \a path to this component. This can only be called when this component was downloaded from an online repository. When adding \a path, it will be downloaded from the repository when the installation starts. \sa removeDownloadableArchive, fromOnlineRepository, archives */ /*! \qmlmethod void component::removeDownloadableArchive(string path) Removes the archive \a path previously added via addDownloadableArchive from this component. This can only be called when this component was downloaded from an online repository. \sa addDownloadableArchive, fromOnlineRepository, archives */ /*! \qmlmethod void component::addStopProcessForUpdateRequest(string process) Adds a request for quitting the process \a process before installing, updating, or uninstalling the component. */ /*! \qmlmethod void component::removeStopProcessForUpdateRequest(string process) Removes the request for quitting the process \a process again. */ /*! \qmlmethod void component::setStopProcessForUpdateRequest(string process, boolean requested) A convenience method for adding or removing the request for stopping \a process depending on whether \a requested is \c true (add) or \c false (remove). */ /*! \qmlmethod boolean component::addOperation(string operation, stringlist parameters) Creates and adds an installation operation for \a operation. Add any number of parameters. The contents of the parameters get variables like "@TargetDir@" replaced with their values, if contained. The method is typically called from within \l component::createOperations(). */ /*! \qmlmethod boolean component::addOperation(string operation, string parameter1 = "", string parameter2 = "", ..., string parameter10 = "") Convenience method for calling addOperation(string, stringlist) with up to 10 arguments. */ /*! \qmlmethod boolean component::addElevatedOperation(string operation, string parameter1 = "", string parameter2 = "", ..., string parameter10 = "") Convenience method for calling addElevatedOperation(string, stringlist) with up to 10 arguments. */ /*! \qmlmethod boolean component::addElevatedOperation(string operation, stringlist parameters) Creates and adds an installation operation for \a operation. Add any number of parameters. The contents of the parameters get variables like "@TargetDir@" replaced with their values, if contained. \a operation is executed with elevated rights. */ /*! \qmlmethod void component::setAutoCreateOperations(boolean autoCreateOperations) Setter for the \l autoCreateOperations property. */ /*! \qmlmethod void component::addDependency(string newDependency) Adds a new component \a newDependency to the list of dependencies. \sa dependencies */ /*! \qmlmethod void component::setInstalled() Set's the components state to installed. */ /*! \qmlmethod boolean component::isAutoDependOn(QSet<string> componentsToInstall) Determines whether the component comes as an auto dependency. Returns \c true if the component needs to be installed. */ /*! \qmlmethod boolean component::isDefault() Indicates if the component is a default one. \note Always returns \c false for virtual components. \sa default */ /*! \qmlmethod boolean component::isInstalled() Determines whether the component is installed. */ /*! \qmlmethod boolean component::installationRequested() Determines whether the user wants to install the component. */ /*! \qmlmethod void component::setUpdateAvailable(boolean isUpdateAvailable) Sets a flag that the core found an update. */ /*! \qmlmethod boolean component::updateRequested() Determines whether the user wants to install the update for this component. */ /*! \qmlmethod boolean component::componentChangeRequested() Returns \c true if this component will be changed (update, installation, or uninstallation). */ /*! \qmlmethod void component::setUninstalled() Sets the component state to uninstalled. */ /*! \qmlmethod boolean component::isUninstalled() Determines whether the component is uninstalled. */ /*! \qmlmethod boolean component::uninstallationRequested() Determines whether the user wants to uninstall the component. */ /*! \qmlmethod boolean component::isFromOnlineRepository() Determines whether this component has been loaded from an online repository. \sa addDownloadableArchive, fromOnlineRepository */ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/scripting-api/console.qdoc����������������������������������������������������������������������0000664�0000000�0000000�00000004405�13253666515�0016725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype console \inqmlmodule scripting \brief Provides methods for logging and debugging. \keyword console-object You can use the \c console object to print log information about installer functions to the console. The following example uses the \c console object \l{console::log()}{log} method and \l installer object \l{installer::isUpdater()}, \l{installer::isUninstaller()}, and \l{installer::isPackageManager()} methods to display a message that indicates whether the maintenance tool is currently being used to update, remove, or add components. \code onPackageManagerCoreTypeChanged = function() { console.log("Is Updater: " + installer.isUpdater()); console.log("Is Uninstaller: " + installer.isUninstaller()); console.log("Is Package Manager: " + installer.isPackageManager()); } \endcode */ /*! \qmlmethod void console::log(string value) Prints the string specified by \a value to the console. */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/scripting-api/gui.qdoc��������������������������������������������������������������������������0000664�0000000�0000000�00000011332�13253666515�0016044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype gui \inqmlmodule scripting \brief Enables interaction with the installer UI. */ /*! \qmlsignal gui::interrupted() This signal is emitted when the end user chooses to cancel the installation and quit the installer. */ /*! \qmlsignal gui::languageChanged() This signal is emitted when the application language changes. */ /*! \qmlsignal gui::finishButtonClicked() This signal is emitted when the \uicontrol Finish button is clicked. */ /*! \qmlsignal gui::gotRestarted() This signal is emitted when the installer is restarted. */ /*! \qmlsignal gui::settingsButtonClicked() This signal is emitted when the \uicontrol Settings button is clicked. */ /*! \qmlmethod object gui::pageById(int id) Returns the installer page specified by \a id. The values of \c id for the available installer pages are provided by QInstaller::WizardPage. */ /*! \qmlmethod object gui::pageByObjectName(string name) Returns the installer page specified by \a name. The value of \c name is the object name set in the UI file that defines the installer page. */ /*! \qmlmethod object gui::currentPageWidget() Returns the current wizard page. */ /*! \qmlmethod object gui::pageWidgetByObjectName(string name) For dynamic pages, returns the widget specified by \a name read from the UI file. */ /*! \qmlmethod string gui::defaultButtonText(int wizardButton) Returns the default text for the button specified by \a wizardButton. */ /*! \qmlmethod void gui::clickButton(int wizardButton, int delayInMs) Automatically clicks the button specified by \a wizardButton after a delay in milliseconds specified by \a delayInMs. */ /*! \qmlmethod boolean gui::isButtonEnabled(int wizardButton) Returns \c true if the button specified by \a wizardButton is enabled. Returns \c false if a button of the specified type is not found. */ /*! \qmlmethod void gui::showSettingsButton(boolean show) Shows the \uicontrol Settings button if \a show is \c true. */ /*! \qmlmethod void gui::setSettingsButtonEnabled(boolean enable) Enables the \uicontrol Settings button by setting \a enable to \c true. */ /*! \qmlmethod object gui::findChild(object parent, string objectName) Returns the first descendant of \a parent that has \a objectName as name. \sa QObject::findChild */ /*! \qmlmethod object[] gui::findChildren(object parent, string objectName) Returns all descendants of \a parent that have \a objectName as name. \sa QObject::findChildren */ /*! \qmlmethod void gui::cancelButtonClicked() Asks end users whether they want to cancel the operation and quit the installer, uninstaller, or package manager. */ /*! \qmlmethod void gui::reject() Quits the installer, uninstaller, or package manager. */ /*! \qmlmethod void gui::rejectWithoutPrompt() Quits the installer, uninstaller, or package manager without asking end users for confirmation. */ /*! \qmlmethod void gui::showFinishedPage() Shows the next page. */ /*! \qmlmethod void gui::setModified(boolean value) */ /*! \qmlmethod void gui::setTextItems(object control, string[] items) Updates the model of \a control (which must be a QComboBox or QAbstractItemView) such that it contains the given \a items. */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/scripting-api/packagemanagercore.qdoc�����������������������������������������������������������0000664�0000000�0000000�00000052060�13253666515�0021062�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype installer \inqmlmodule scripting \brief Provides access to core functionality of the Qt Installer Framework. */ /*! \qmlmethod array installer::components() Returns an array of all components currently available. If the repository metadata have not been fetched yet, the array will be empty. \sa component, installer::finishAllComponentsReset(), installer::finishUpdaterComponentsReset() */ /*! \qmlsignal installer::aboutCalculateComponentsToInstall() Emitted before the ordered list of components to install is calculated. */ /*! \qmlsignal installer::finishedCalculateComponentsToInstall() Emitted after the ordered list of components to install was calculated. */ /*! \qmlsignal installer::aboutCalculateComponentsToUninstall() Emitted before the ordered list of components to uninstall is calculated. */ /*! \qmlsignal installer::finishedCalculateComponentsToUninstall() Emitted after the ordered list of components to uninstall was calculated. */ /*! \qmlsignal installer::componentAdded(Component component) Emitted when a new root component is added. \sa rootComponentsAdded, updaterComponentsAdded */ /*! \qmlsignal installer::rootComponentsAdded(list<Component> components) Emitted when a new list of root components is added. \sa componentAdded, updaterComponentsAdded */ /*! \qmlsignal installer::updaterComponentsAdded(list<Component> components) Emitted when a new list of updater components is added. \sa componentAdded, rootComponentsAdded */ /*! \qmlsignal installer::valueChanged(string key, string value) Emitted when a value changes. \sa setValue */ /*! \qmlsignal installer::statusChanged(Status status) Emitted when the installer status changes. */ /*! \qmlsignal installer::currentPageChanged(int page) Emitted when the current page changes. */ /*! \qmlsignal installer::finishButtonClicked() Emitted when the user clicks the \uicontrol Finish button of the installer. */ /*! \qmlsignal installer::metaJobProgress(int progress) Triggered with progress updates of the communication with a remote repository. Progress ranges from 0 to 100. */ /*! \qmlsignal installer::metaJobInfoMessage(string message) Triggered with informative updates of the communication with a remote repository. */ /*! \qmlsignal installer::startAllComponentsReset() Triggered when the list of components starts to get updated. \sa finishAllComponentsReset */ /*! \qmlsignal installer::finishAllComponentsReset(list<Component> rootComponents) Triggered when the list of new root components has been updated. \sa startAllComponentsReset */ /*! \qmlsignal installer::startUpdaterComponentsReset() Triggered when components start to get updated during a remote update. */ /*! \qmlsignal installer::finishUpdaterComponentsReset(list<Component> componentsWithUpdates) Triggered when the list of available remote updates has been updated. */ /*! \qmlsignal installer::installationStarted() Triggered when installation has started. \sa installationFinished installationInterrupted */ /*! \qmlsignal installer::installationInterrupted() Triggered when installation has been interrupted (cancelled). \sa interrupt installationStarted installationFinished */ /*! \qmlsignal installer::installationFinished() Triggered when installation has finished. \sa installationStarted installationInterrupted */ /*! \qmlsignal installer::updateFinished() Triggered when an update has finished. */ /*! \qmlsignal installer::uninstallationStarted() Triggered when uninstallation has started. \sa uninstallationFinished */ /*! \qmlsignal installer::uninstallationFinished() Triggered when uninstallation has finished. \sa uninstallationStarted */ /*! \qmlsignal installer::titleMessageChanged(string title) Emitted when the text of the installer status (on the PerformInstallation page) changes to \a title. */ /*! \qmlsignal installer::wizardPageInsertionRequested(Widget widget, WizardPage page) Emitted when a custom \a widget is about to be inserted into \a page by addWizardPage. */ /*! \qmlsignal installer::wizardPageRemovalRequested(Widget widget) Emitted when a \a widget is removed by removeWizardPage. */ /*! \qmlsignal installer::wizardWidgetInsertionRequested(Widget widget, WizardPage page) Emitted when a \a widget is inserted into \a page by addWizardPageItem. */ /*! \qmlsignal installer::wizardWidgetRemovalRequested(Widget widget) Emitted when a \a widget is removed by removeWizardPageItem. */ /*! \qmlsignal installer::wizardPageVisibilityChangeRequested(bool visible, int page) Emitted when the visibility of the page with id \a page changes to \a visible. \sa setDefaultPageVisible */ /*! \qmlsignal installer::setValidatorForCustomPageRequested(Component component, string name, string callbackName) Triggered when setValidatorForCustomPage is called. */ /*! \qmlsignal installer::setAutomatedPageSwitchEnabled(bool request) Triggered when the automatic switching from PerformInstallation to InstallationFinished page is enabled (\a request = \c true) or disabled (\a request = \c false). The automatic switching is disabled automatically when for example the user expands or unexpands the \gui Details section of the PerformInstallation page. */ /*! \qmlsignal installer::coreNetworkSettingsChanged() Emitted when the network settings are changed. */ /*! \qmlmethod void installer::setCompleteUninstallation(bool complete) Sets the uninstallation to be \a complete. If \a complete is false, only components deselected by the user will be uninstalled. This option applies only on uninstallation. */ /*! \qmlmethod void installer::cancelMetaInfoJob() Cancels the retrieval of meta information from a remote repository. */ /*! \qmlmethod void installer::componentsToInstallNeedsRecalculation() Ensures that component dependencies are re-calculated. */ /*! \qmlmethod void installer::autoAcceptMessageBoxes() Automatically accept all user message boxes. \sa autoRejectMessageBoxes, setMessageBoxAutomaticAnswer */ /*! \qmlmethod void installer::autoRejectMessageBoxes() Automatically reject all user message boxes. \sa autoAcceptMessageBoxes, setMessageBoxAutomaticAnswer */ /*! \qmlmethod void installer::setMessageBoxAutomaticAnswer(string identifier, int button) Automatically close the message box with ID \a identifier as if the user had pressed \a button. This can be used for unattended (automatic) installations. \sa QMessageBox, autoAcceptMessageBoxes, autoRejectMessageBoxes */ /*! \qmlmethod float installer::requiredDiskSpace() Returns the additional estimated amount of disk space in bytes required after installation. \sa requiredTemporaryDiskSpace */ /*! \qmlmethod float installer::requiredTemporaryDiskSpace() Returns the estimated required disk space during installation in bytes. \sa requiredDiskSpace */ /*! \qmlmethod boolean installer::isFileExtensionRegistered(string extension) Returns whether a file extension is already registered in the Windows registry. Returns \c false on all other platforms. */ /*! \qmlmethod string installer::readFile(string filePath, string codecName) Returns the contents of the file \a filePath using the encoding specified by \a codecName. The file is read in the text mode, that is, end-of-line terminators are translated to the local encoding. \note If the file does not exist or an error occurs while reading the file, an empty string is returned. */ /*! \qmlmethod boolean installer::fileExists(string filePath) Returns \c true if the \a filePath exists; otherwise returns \c false. \note If the file is a symlink that points to a non existing file, \c false is returned. */ /*! \qmlmethod boolean installer::addWizardPage(Component component, string name, int page) Adds the widget with objectName() \a name registered by \a component as a new page into the installer's GUI wizard. The widget is added before \a page. See \l{Controller Scripting} for the possible values of \a page. Returns \c true if the operation succeeded. \sa removeWizardPage, setDefaultPageVisible */ /*! \qmlmethod boolean installer::removeWizardPage(Component component, string name) Removes the widget with objectName() \a name previously added to the installer's wizard by \a component. Returns \c true if the operation succeeded. \sa addWizardPage, setDefaultPageVisible, wizardPageRemovalRequested */ /*! \qmlmethod boolean installer::setDefaultPageVisible(int page, boolean visible) Sets the visibility of the default page with the ID \a page to \a visible. That is, removes it from or adds it to the wizard. This works only for pages that were in the installer when it was started. Returns \c true. \sa addWizardPage, removeWizardPage */ /*! \qmlmethod void installer::setValidatorForCustomPage(Component component, string name, string callbackName) \sa setValidatorForCustomPageRequested */ /*! \qmlmethod boolean installer::addWizardPageItem(Component component, string name, int page) Adds the widget with objectName() \a name registered by \a component as a GUI element into the installer's GUI wizard. The widget is added on \a page. See \l{Controller Scripting} for the possible values of \a page. \sa removeWizardPageItem, wizardWidgetInsertionRequested */ /*! \qmlmethod boolean installer::removeWizardPageItem(Component component, string name) Removes the widget with objectName() \a name previously added to the installer's wizard by \a component. \sa addWizardPageItem */ /*! \qmlmethod void installer::addUserRepositories(stringlist repositories) Registers additional \a repositories. \sa setTemporaryRepositories */ /*! \qmlmethod void installer::setTemporaryRepositories(stringlist repositories, boolean replace) Sets additional \a repositories for this instance of the installer or updater. Will be removed after invoking it again. \sa addUserRepositories */ /*! \qmlmethod boolean installer::calculateComponentsToInstall() Calculates an ordered list of components to install based on the current run mode. Also auto installed dependencies are resolved. The aboutCalculateComponentsToInstall() signal is emitted before the calculation starts, the finishedCalculateComponentsToInstall() signal once all calculations are done. */ /*! \qmlmethod boolean installer::calculateComponentsToUninstall() Calculates a list of components to uninstall based on the current run mode. Auto installed dependencies are not yet resolved. The aboutCalculateComponentsToUninstall() signal is emitted before the calculation starts, the finishedCalculateComponentsToUninstall() signal once all calculations are done. Always returns \c true. */ /*! \qmlmethod boolean installer::gainAdminRights() Tries to gain admin rights. On success, it returns \c true. \sa dropAdminRights */ /*! \qmlmethod void installer::dropAdminRights() Drops admin rights gained by gainAdminRights. \sa gainAdminRights */ /*! \qmlmethod boolean installer::isProcessRunning(string name) Returns \c true if a process with \a name is running. On Windows, the comparison is case-insensitive. */ /*! \qmlmethod boolean installer::killProcess(string absoluteFilePath) Returns \c true if a process with \a absoluteFilePath could be killed or is not running. \note This is implemented in a semi blocking way (to keep the main thread to paint the UI). */ /*! \qmlmethod void installer::setDependsOnLocalInstallerBinary() Makes sure the installer runs from a local drive. Otherwise the user will get an appropriate error message. \note This only works on Windows. \sa localInstallerBinaryUsed */ /*! \qmlmethod boolean installer::localInstallerBinaryUsed() Returns \c false if the installer is run on Windows, and the installer has been started from a remote file system drive. Otherwise returns \c true. \sa setDependsOnLocalInstallerBinary */ /*! \qmlmethod array installer::execute(string program, stringlist arguments = undefined, string stdin = "", string stdinCodec = "latin1", string stdoutCodec = "latin1") Starts the program \a program with the arguments \a arguments in a new process and waits for it to finish. \a stdin is sent as standard input to the application. \a stdInCodec is the name of the codec to use for converting the input string into bytes to write to the standard input of the application. \a stdOutCodec is the name of the codec to use for converting data written by the application to standard output into a string. Returns an empty array if the program could not be executed, otherwise the output of command as the first item, and the return code as the second. \note On Unix, the output is just the output to stdout, not to stderr. \sa executeDetached */ /*! \qmlmethod boolean installer::executeDetached(string program, stringlist arguments = undefined, string workingDirectory = "") Starts the program \a program with the arguments \a arguments in a new process, and detaches from it. Returns \c true on success; otherwise returns \c false. If the installer exits, the detached process will continue to live. \note Arguments that contain spaces are not passed to the process as separate arguments. \b{Unix:} The started process will run in its own session and act like a daemon. \b{Windows:} Arguments that contain spaces are wrapped in quotes. The started process will run as a regular standalone process. The process will be started in the directory \a workingDirectory. */ /*! \qmlmethod string installer::environmentVariable(string name) Returns the content of the environment variable \a name. An empty string is returned if the environment variable is not set. */ /*! \qmlmethod boolean installer::performOperation(string name, stringlist arguments) Instantly performs the operation \a name with \a arguments. */ /*! \qmlmethod boolean installer::versionMatches(string version, string requirement) Returns \c true when \a version matches the \a requirement. \a requirement can be a fixed version number or it can be prefixed by the comparators '>', '>=', '<', '<=' and '='. */ /*! \qmlmethod string installer::findLibrary(string name, stringlist paths = []) Finds a library named \a name in \a paths. If \a paths is empty, it gets filled with platform dependent default paths. The resulting path is returned. This method can be used by scripts to check external dependencies. \sa findPath */ /*! \qmlmethod string installer::findPath(string name, stringlist paths = []) Tries to find a file name \a name in one of \a paths. The resulting path is returned. This method can be used by scripts to check external dependencies. \sa findLibrary */ /*! \qmlmethod void installer::setInstallerBaseBinary(string path) Sets the \c installerbase binary to use when writing the maintenance tool. Set the path if an update to the binary is available. If not set, the executable segment of the running installer or uninstaller will be used. */ /*! \qmlmethod string installer::value(string key, string defaultValue = "") Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is returned. Additionally, on Windows, \a key can be a registry key. \sa setValue, containsValue, valueChanged */ /*! \qmlmethod stringlist installer::values(string key, stringlist defaultValue = []) Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is returned. Additionally, on Windows, \a key can be a registry key. \sa value */ /*! \qmlmethod void installer::setValue(string key, string value) Sets the installer value for \a key to \a value. \sa value, containsValue, valueChanged */ /*! \qmlmethod boolean installer::containsValue(string key) Returns \c true if the installer contains a value for \a key. \sa value, setValue, valueChanged */ /*! \qmlmethod void installer::setSharedFlag(string key, boolean value) Sets a shared flag with name \a key to \a value. This is one option to share information between scripts. Deprecated since 2.0.0. Use setValue instead. */ /*! \qmlmethod boolean installer::sharedFlag(string key) Returns shared flag with name \a key. This is one option to share information between scripts. Deprecated since 2.0.0. Use \l value or \l values instead. */ /*! \qmlmethod void installer::interrupt() Cancels an ongoing installation. \sa installationInterrupted */ /*! \qmlmethod void installer::setCanceled() Cancels the installation. */ /*! \qmlmethod boolean installer::isInstaller() Returns \c true if the application, binary, or executable is executed in installer mode. \sa isUninstaller, isUpdater, isPackageManager */ /*! \qmlmethod boolean installer::isOfflineOnly() Returns \c true if this is an offline-only installer. */ /*! \qmlmethod void installer::setUninstaller() Forces an uninstaller context. \sa isUninstaller, setUpdater, setPackageManager */ /*! \qmlmethod boolean installer::isUninstaller() Returns \c true if the the application, binary, or executable is executed in uninstaller mode. \sa setUninstaller, isInstaller, isUpdater, isPackageManager */ /*! \qmlmethod void installer::setUpdater() Forces an updater context. \sa isUpdater, setUninstaller, setPackageManager */ /*! \qmlmethod boolean installer::isUpdater() Returns \c true if the the application, binary, or executable is executed in updater mode. \sa setUpdater, isInstaller, isUninstaller, isPackageManager */ /*! \qmlmethod void installer::setPackageManager() Forces a package manager context. */ /*! \qmlmethod boolean installer::isPackageManager() Returns \c true if the the application, binary, or executable is executed in package manager mode. \sa setPackageManager, isInstaller, isUninstaller, isUpdater */ /*! \qmlmethod boolean installer::runInstaller() Runs the installer. Returns \c true on success, \c false otherwise. */ /*! \qmlmethod boolean installer::runUninstaller() Runs the uninstaller. Returns \c true on success, \c false otherwise. */ /*! \qmlmethod boolean installer::runPackageUpdater() Runs the package updater. Returns \c true on success, \c false otherwise. */ /*! \qmlmethod void installer::languageChanged() Calls languangeChanged on all components. */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/scripting-api/print.qdoc������������������������������������������������������������������������0000664�0000000�0000000�00000002646�13253666515�0016424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype print \inqmlmodule scripting \brief Provides compatibility with QtScript. For new code, use the \l{console-object}{console} object instead. */ ������������������������������������������������������������������������������������������doc/scripting-api/qdesktopservices.qdoc�������������������������������������������������������������0000664�0000000�0000000�00000007573�13253666515�0020672�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype QDesktopServices \inqmlmodule scripting \brief Provides methods for accessing common desktop services. Many desktop environments provide services that can be used by applications to perform common tasks, such as opening a file, in a way that is both consistent and takes into account the user's application preferences. This object contains methods that provide simple interfaces to these services that indicate whether they succeeded or failed. The openUrl() method is used to open files located at arbitrary URLs in external applications. For URLs that correspond to resources on the local filing system (where the URL scheme is "file"), a suitable application is used to open the file. The displayName() and storageLocation() methods take one of the following enums as an argument: \list \li DesktopServices.DesktopLocation \li DesktopServices.DocumentsLocation \li DesktopServices.FontsLocation \li DesktopServices.ApplicationsLocation \li DesktopServices.MusicLocation \li DesktopServices.MoviesLocation \li DesktopServices.PicturesLocation \li DesktopServices.TempLocation \li DesktopServices.HomeLocation \li DesktopServices.DataLocation \li DesktopServices.CacheLocation \li DesktopServices.GenericDataLocation \li DesktopServices.RuntimeLocation \li DesktopServices.ConfigLocation \li DesktopServices.DownloadLocation \li DesktopServices.GenericCacheLocation \li DesktopServices.GenericConfigLocation \endlist The enum values correspond to the values of the \l{QStandardPaths::StandardLocation} enum with the same names. */ /*! \qmlproperty enumeration QDesktopServices::QStandardPaths \internal */ /*! \qmlmethod boolean QDesktopServices::openUrl(string url) Uses the URL scheme \c file to open the specified \a url with a suitable application. \warning A return value of \c true indicates that the installer has successfully requested the operating system to open the URL in an external application. It may still fail to launch or fail to open the requested URL. This result will not be reported back to the installer. */ /*! \qmlmethod string QDesktopServices::displayName(int location) Returns a localized display name for the specified \a location or an empty QString if no relevant location can be found. */ /*! \qmlmethod string QDesktopServices::storageLocation(int location) Returns the specified \a location. */ �������������������������������������������������������������������������������������������������������������������������������������doc/scripting-api/qfiledialog.qdoc������������������������������������������������������������������0000664�0000000�0000000�00000005620�13253666515�0017543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype QFileDialog \inqmlmodule scripting \brief Provides a dialog that allows users to select files or directories. Use the QFileDialog::getExistingDirectory() method to create a modal dialog that displays an existing directory selected by the user. Use the QFileDialog::getOpenFileName() method to create a dialog that displays matching files in the directory selected by the user. */ /*! \qmlmethod string QFileDialog::getExistingDirectory(string caption, string dir) Returns an existing directory selected by the user. The dialog's working directory is set to \a dir, and the caption is set to \a caption. Either of these may be an empty string, in which case the current directory and a default caption will be used, respectively. */ /*! \qmlmethod string QFileDialog::getOpenFileName(string caption, string dir, string filter) Returns an existing file selected by the user. If the user selects \uicontrol Cancel, returns a null string. The file dialog's caption is set to \a caption. If \c caption is not specified, a default caption is used. The file dialog's working directory is set to \a dir. If \c dir includes a file name, the file will be selected. Only files that match the specified \a filter are shown. Either of these may be an empty string. To specify multiple filters, separate them with two semicolons (;;). For example: \code "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" \endcode On Windows, and OS X, this static function will use the native file dialog and not a QFileDialog. */ ����������������������������������������������������������������������������������������������������������������doc/scripting-api/qinstaller.qdoc�������������������������������������������������������������������0000664�0000000�0000000�00000005724�13253666515�0017446�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype QInstaller \inqmlmodule scripting \brief Provides access to the installer status and pages from Qt Script. For more information about using the \c QInstaller object in control scripts, see \l{Controller Scripting}. For examples of using the pages to support end user workflows, see \l{End User Workflows}. */ /*! \qmlproperty enumeration QInstaller::WizardPage The installer has various pre-defined pages that can be used to for example insert pages in a certain place: \value QInstaller.Introduction \l{Introduction Page} \value QInstaller.TargetDirectory \l{Target Directory Page} \value QInstaller.ComponentSelection \l{Component Selection Page} \value QInstaller.LicenseCheck \l{License Agreement Page} \value QInstaller.StartMenuSelection \l{Start Menu Directory Page} \value QInstaller.ReadyForInstallation \l{Ready for Installation Page} \value QInstaller.PerformInstallation \l{Perform Installation Page} \value QInstaller.InstallationFinished \l{Finished Page} \omitvalue QInstaller.End */ /*! \qmlproperty enumeration QInstaller::status Status of the installer. Possible values are: \value QInstaller.Success Installation was successful. \value QInstaller.Failure Installation failed. \value QInstaller.Running Installation is in progress. \value QInstaller.Canceled Installation was canceled. \value QInstaller.Unfinished Installation was not completed. \value QInstaller.ForceUpdate */ ��������������������������������������������doc/scripting-qmlmodule.qdoc������������������������������������������������������������������������0000664�0000000�0000000�00000003125�13253666515�0016507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage {index.html}{Qt Installer Framework} \previouspage ifw-tools.html \nextpage ifw-cpp-classes.html \title Scripting API \qmlmodule scripting \brief Scripting API reference. The following table summarizes the global JavaScript objects that you can use in controller and component scripts. */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/scripting.qdoc����������������������������������������������������������������������������������0000664�0000000�0000000�00000030465�13253666515�0014521�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage{index.html}{Qt Installer Framework} \previouspage noninteractive.html \page scripting.html \nextpage operations.html \title Component Scripting For each component, you can specify one script that prepares the operations to be performed by the installer. The script format has to be compatible with QJSEngine. \section1 Construction The script has to contain a \c Component object that the installer creates when it loads the script. Therefore, the script must contain at least the \c Component() function, which performs initialization, such as putting pages in the correct places or connecting signals and slots. The following code snippet places the \c ErrorPage (which is the class name of the user interface file loaded from errorpage.ui) in front of the ready for installation page and sets its completeness to \c false. \code function Component() { // Add a user interface file called ErrorPage, which should not be complete installer.addWizardPage( component, "ErrorPage", QInstaller.ReadyForInstallation ); component.userInterface( "ErrorPage" ).complete = false; } \endcode For more information, see the documentation for \l installer::addWizardPage() and \l component::userInterface(). \section1 Installer Hooks You can add the following hook methods into your script: \table \header \li Method \li Description \row \li \c{Component.prototype.retranslateUi} \li Called when the language of the installer changes. \row \li \c{Component.prototype.createOperations} \li See \l component::createOperations(). \row \li \c{Component.prototype.createOperationsForArchive} \li See \l component::createOperationsForArchive(). \row \li \c{Component.prototype.createOperationsForPath} \li See \l component::createOperationsForPath(). \endtable \section1 Global Variables The installer puts the following symbols into the script space: \table \header \li Symbol \li Description \row \li installer \li Reference to the \l QInstaller of the component \row \li component \li Reference to the \l Component of the component \endtable \section1 Message Boxes You can show a QMessageBox from within the script by using the following static members: \list \li QMessageBox::critical() \li QMessageBox::information() \li QMessageBox::question() \li QMessageBox::warning() \endlist For your convenience, the values for QMessageBox::StandardButton are made available by using \c QMessageBox.Ok, \c QMessageBox.Open, and so on. \section1 Adding Operations to Components You might want to add custom operations after extracting the content, when copying files or patching file content, for example. You can create and add update operations to the installation from within a script using component::addOperation(). If you need to run an operation that requires administrative rights, use component::addElevatedOperation() instead. Operations need to be added before the actual installation step. Override \l component::createOperations() to register custom operations for a component. Each operation has a unique key used for identification and can take up to five parameters. In the parameter values, you can use variables as set in installer::setValue(). For more information, see \l{Predefined Variables}. For a summary of all available operations, see \l{Operations}. \section1 Registering Custom Operations You can register custom installation operations in the installer by deriving the KDUpdater::UpdateOperation class. The following code displays the methods that you must implement: \code #include <UpdateOperation> class CustomOperation : public KDUpdater::UpdateOperation { public: CustomOperation() { setName( "CustomOperation" ); } void backup() { // do whatever is needed to restore the state in undoOperation() } bool performOperation() { const QStringList args = arguments(); // do whatever is needed to do for the given arguments bool success = ...; return success; } void undoOperation() { // restore the previous state, as saved in backup() } bool testOperation() { // currently unused return true; } CustomOperation* clone() const { return new CustomOperation; } QDomDocument toXml() { // automatically adds the operation's arguments and everything set via setValue QDomDocument doc = KDUpdater::UpdateOperation::toXml(); // if you need any information to undo the operation you did, // add them to the doc here return doc; } bool fromXml( const QDomDocument& doc ) { // automatically loads the operation's arguments and everything set via setValue if( !KDUpdater::UpdateOperation::fromXml( doc ) ) return false; // if you need any information to undo the operation you did, // read them from the doc here return true; } }; \endcode Finally, you need to register your custom operation class, as follows: \code #include <UpdateOperationFactory> KDUpdater::UpdateOperationFactory::instance().registerUpdateOperation< CustomOperation >( "CustomOperation" ); \endcode Now you can use your operation in the installer in the same way as the predefined operations. \section1 Predefined Variables You can use the following predefined variables in scripts to facilitate directory access: \table \header \li Symbol \li Description \row \li ProductName \li Name of the product to be installed, as defined in config.xml. \row \li ProductVersion \li Version number of the product to be installed, as defined in config.xml. \row \li Title \li Title of the installation program, as defined in config.xml. \row \li Publisher \li Publisher of the installation program, as defined in config.xml. \row \li Url \li Product URL, as defined in config.xml. \row \li StartMenuDir \li Start menu group, as defined in config.xml. Only available on Windows. \row \li TargetDir \li Target directory for installation, as selected by the user. \row \li DesktopDir \li Name of the directory that contains the user's desktop. Only available on Windows. \row \li os \li Current platform: \c "x11", \c "win", or \c "mac". This variable is deprecated: Use \l systemInfo instead. \row \li RootDir \li Root directory of the filesystem. \row \li HomeDir \li Home directory of the current user. \row \li ApplicationsDir \li Applications directory. For example, \c {C:\Program Files} on Windows, \c {/opt} on Linux and \c {/Applications} on OS X. See also the table that lists examples of \l {Applications-directory-on-Windows} {applications directories on Windows}. \row \li ApplicationsDirX86 \li Applications Directory for 32 bit programs. This is useful on Windows, on other platforms it is the same as \c ApplicationsDir. For example, \c {C:\Program Files (x86)} on Windows. See also the table that lists examples of \l {Applications-directory-on-Windows} {applications directories on Windows}. \row \li ApplicationsDirX64 \li Applications Directory for 64 bit programs. This is useful on Windows, on other platforms it is the same as \c ApplicationsDir. For example, \c {C:\Program Files} on Windows. See also the table that lists examples of \l {Applications-directory-on-Windows} {applications directories on Windows}. \row \li InstallerDirPath \li The directory that contains the installer application executable. \row \li InstallerFilePath \li The file path of the installer application executable. \row \li UserStartMenuProgramsPath \li The path to the folder containing the items in the Start menu of the user. For example, \c {C:\Users\USERNAME\AppData\Roaming\Microsoft\Windows\Start Menu\Programs} Only available on Windows. \row \li AllUsersStartMenuProgramsPath \li The path to the folder containing the items in the Start menu for all users. For example, \c {C:\ProgramData\Microsoft\Windows\Start Menu\Programs} Only available on Windows. \endtable The variables can be resolved by calls to installer::value(). If embedded in '@' they can also be part of strings passed as arguments to installation operations: \code if (installer.value("os") === "win") { component.addOperation("CreateShortcut", "@TargetDir@/MyApp.exe", "@StartMenuDir@/MyApp.lnk"); } \endcode \target Applications-directory-on-Windows For example, applications directory on Windows: \table \header \li OS (Windows) \li Qt Installer Framework \li Variable \li Example Path \row \li {1, 3} 32bit \li {1, 3} 32bit \li ApplicationsDir \li \c {C:\Program Files} \row \li ApplicationsDirX86 \li \c {C:\Program Files} \row \li ApplicationsDirX64 \li \c {C:\Program Files} \row \li {1, 6} 64bit \li {1, 3} 32bit \li ApplicationsDir \li \c {C:\Program Files (x86)} \row \li ApplicationsDirX86 \li \c {C:\Program Files (x86)} \row \li ApplicationsDirX64 \li \c {C:\Program Files} \row \li {1, 3} 64bit \li ApplicationsDir \li \c {C:\Program Files} \row \li ApplicationsDirX86 \li \c {C:\Program Files (x86)} \row \li ApplicationsDirX64 \li \c {C:\Program Files} \endtable */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/systeminfo.qdoc���������������������������������������������������������������������������������0000664�0000000�0000000�00000010474�13253666515�0014715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmltype systemInfo \inqmlmodule scripting \brief Provides information about the operating system. */ /*! \qmlproperty string systemInfo::currentCpuArchitecture The architecture of the CPU that the application is running on, in text format. Possible values include \list \li "i386" \li "x86_64" \endlist Note that this function depends on what the OS will report and may not detect the actual CPU architecture if the OS hides that information or is unable to provide it. For example, a 32-bit OS running on a 64-bit CPU is usually unable to determine whether the CPU is actually capable of running 64-bit programs. \sa QSysInfo::currentCpuArchitecture() */ /*! \qmlproperty string systemInfo::kernelType The type of the operating system kernel the installer was compiled for. It is also the kernel the installer is running on, unless the host operating system is running a form of compatibility or virtualization layer. For Windows, Linux, and OS X this will return \list \li "winnt" \li "linux" \li "darwin" \endlist On Unix systems, it returns the same as the output of \c {uname -s} (lowercased). \sa QSysInfo::kernelType() */ /*! \qmlproperty string systemInfo::kernelVersion Example values are \list \li "6.1.7601" for Windows 7 with Service Pack 1 \li "3.16.6-2-desktop" for openSUSE 13.2 kernel 3.16.6-2 \li "12.5.0" last release of OS X "Mountain Lion" \endlist The release version of the operating system kernel. On Windows, it returns the version of the NT or CE kernel. On Unix systems, including OS X, it returns the same as the \c {uname -r} command would return. \sa QSysInfo::kernelVersion() */ /*! \qmlproperty string systemInfo::productType The product name of the operating system this application is running in. Example values are \list \li "windows" \li "opensuse" (for the Linux openSUSE distribution) \li "osx" \endlist \sa QSysInfo::productType() */ /*! \qmlproperty string systemInfo::productVersion The product version of the operating system in string form. If the version could not be determined, this function returns "unknown". Example values are \list \li "7" for Windows 7 \li "13.2" for openSUSE 13.2 \li "10.8" for OS X Mountain Lion \endlist \sa QSysInfo::productVersion() */ /*! \qmlproperty string systemInfo::prettyProductName A prettier form of systemInfo::productType and systemInfo::productVersion, containing other tokens like the operating system type, codenames and other information. The result of this function is suitable for displaying to the user. Example values are \list \li "Windows 7" \li "openSUSE 13.2 (Harlequin) (x86_64)" \li "OS X Mountain Lion (10.8)" \endlist \sa QSysInfo::prettyProductName() */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������doc/tutorial.qdoc�����������������������������������������������������������������������������������0000664�0000000�0000000�00000017524�13253666515�0014363�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \contentspage {index.html}{Qt Installer Framework} \previouspage ifw-use-cases-settings.html \page ifw-tutorial.html \nextpage ifw-creating-installers.html \title Tutorial: Creating an Installer This tutorial describes how to create a simple installer for a small project: \image ifw-introduction-page.png "Introduction page" This section describes the following tasks that you must accomplish to create the installer: \list 1 \li Create a \e {package directory} that will contain all the configuration files and installable packages. \li Create a \e {configuration file} that contains information about how to build the installer binaries and online repositories. \li Create a \e {package information file} that contains information about the installable components. \li Create installer content and copy it to the package directory. \li Use the \c binarycreator tool to create the \e installer. The installer pages are created by using the information you provide in the configuration and package information file. \endlist The example files are located in the \c{examples\tutorial} directory in the Qt Installer Framework repository. \section1 Creating a Package Directory Create a directory structure that reflects the design of the installer and allows the installer to be extended in the future. The directory must contain subdirectories called \c config and \c packages. \image ifw-tutorial-files.png For more information about the package directory, see \l{Package Directory}. \section1 Creating a Configuration File In the \c config directory, create a file called \c config.xml with the following contents: \quotefile ../examples/tutorial/config/config.xml The configuration file specifies the following information that is displayed on the introduction page: \list \li The \c <Title> element specifies the installer name displayed on the title bar (1). \li The \c <Name> element specifies the application name that is added to the page name and introduction text (2). \endlist \image ifw-tutorial-introduction-page.png "Introduction page" The other elements are used to customize the behavior of the installer: \list \li The \c <Version> element specifies the application version number. \li The \c <Publisher> element specifies the publisher of the software (as shown in the Windows Control Panel, for example). \li The \c <StartMenuDir> element specifies the name of the default program group for the product in the Windows \gui Start menu. \li The \c <TargetDir> element specifies that the default target directory displayed to users is \c InstallationDirectory in the home directory of the current user (because the predefined variable \c @HomeDir@ is used as a part of the value). For more information, see \l{Predefined Variables}. \endlist For more information about the configuration file format and the available elements, see \l{Configuration File}. \section1 Creating a Package Information File In this easy scenario, the installer handles only one component that is called \c{com.vendor.product}. To provide the installer with information about the component, create a file called \c package.xml with the following contents and place it in the \c meta directory: \quotefile ../examples/tutorial/packages/com.vendor.product/meta/package.xml The elements in the example file are described in more detail below. For more information about the package information file, see \l{Package Information File Syntax}. \section2 Specifying Component Information The information from the following elements is displayed on the component selection page: \list \li The \c <DisplayName> element specifies the name of the component in the list of components (1). \li The \c <Description> element specifies the text that is displayed when the component is selected (2). \endlist \image ifw-tutorial-select-components.png \section2 Specifying Installer Version The \c <Version> element enables you to promote updates to users when they become available. \section2 Adding Licenses The \c <License> element specifies the name of the file that contains the text for the license agreement (1) that is displayed on the license check page: \image ifw-tutorial-license-check.png "License check page" \section2 Selecting Default Contents The \c <Default> element specifies whether the component is selected by default. The value \c true sets the component as selected. In this example, we use the value \c script to resolve the value during runtime. The name of the JavaScript script file, installscript.qs, is specified in the \c <Script> element. \section1 Creating Installer Content Content to be installed is stored in the \c data directory of a component. As there is only one component, place the data in the \c{packages/com.vendor.product/data} directory. The example already contains a file for testing purposes, but you can place basically any files in the directory. For more information about packaging rules and options, see \l{Data Directory}. \section1 Creating the Installer Binary You are now ready to create your first installer. Switch to the \c examples\tutorial directory on the command line. To create an installer called YourInstaller.exe that contains the packages identified by com.vendor.product, enter the following command: \list \li On Windows: \code ..\..\bin\binarycreator.exe -c config\config.xml -p packages YourInstaller.exe \endcode \li On Linux or OS X: \code ../../bin/binarycreator -c config/config.xml -p packages YourInstaller \endcode \endlist The installer is created in the current directory and you can deliver it to end users. For more information about using the \c binarycreator tool, see \l{binarycreator}. \note If an error message is displayed when you run the tutorial installer, check that you used a statically built Qt to create the installer. For more information, see \l{Configuring Qt}. */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������examples/�������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0012710�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������examples/changeuserinterface/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0016715�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������examples/changeuserinterface/README�����������������������������������������������������������������0000664�0000000�0000000�00000000327�13253666515�0017577�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Shows how to change small things in the ui from a component script. This shouldn't be used for translations. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������examples/changeuserinterface/changeuserinterface.pro������������������������������������������������0000664�0000000�0000000�00000000551�13253666515�0023445�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README �������������������������������������������������������������������������������������������������������������������������������������������������������examples/changeuserinterface/config/����������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0020162�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������examples/changeuserinterface/config/config.xml������������������������������������������������������0000664�0000000�0000000�00000000536�13253666515�0022155�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <Installer> <Name>Change Installer UI Example</Name> <Version>1.0.0</Version> <Title>Change Installer UI Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/changeuserinterface examples/changeuserinterface/packages/000077500000000000000000000000001325366651500204735ustar00rootroot00000000000000examples/changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/000077500000000000000000000000001325366651500315165ustar00rootroot00000000000000examples/changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/000077500000000000000000000000001325366651500324445ustar00rootroot00000000000000installscript.qs000066400000000000000000000031401325366651500356230ustar00rootroot00000000000000examples/changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { gui.pageWidgetByObjectName("LicenseAgreementPage").entered.connect(changeLicenseLabels); } changeLicenseLabels = function() { page = gui.pageWidgetByObjectName("LicenseAgreementPage"); page.AcceptLicenseLabel.setText("Yes I do!"); page.RejectLicenseLabel.setText("No I don't!"); } examples/changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/license.txt000066400000000000000000000001211325366651500346210ustar00rootroot00000000000000The fantastic license, have you heard of the Beer Public License Agreement yet? examples/changeuserinterface/packages/org.qtproject.ifw.example.changeuserinterface/meta/package.xml000066400000000000000000000007641325366651500345700ustar00rootroot00000000000000 Dummy Component This example does not install any components. It demonstrates modifying the text on the next page. 1.0.0-1 2013-01-01 true examples/componenterror/000077500000000000000000000000001325366651500157645ustar00rootroot00000000000000examples/componenterror/README000066400000000000000000000002631325366651500166450ustar00rootroot00000000000000Create an installer that shows how to react on errors set from a component. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/componenterror/componenterror.pro000066400000000000000000000005511325366651500215630ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/componenterror/config/000077500000000000000000000000001325366651500172315ustar00rootroot00000000000000examples/componenterror/config/config.xml000066400000000000000000000005211325366651500212160ustar00rootroot00000000000000 Component Error Example 1.0.0 Component Error Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/componenterror examples/componenterror/packages/000077500000000000000000000000001325366651500175425ustar00rootroot00000000000000examples/componenterror/packages/root.component1/000077500000000000000000000000001325366651500226075ustar00rootroot00000000000000examples/componenterror/packages/root.component1/meta/000077500000000000000000000000001325366651500235355ustar00rootroot00000000000000examples/componenterror/packages/root.component1/meta/installscript.js000066400000000000000000000030341325366651500267660ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { var error = true; if (error) { installer.setValue("component_errors", installer.value("component_errors") + ";;;" + "Error in component: " + component.name); } installer.setValue("ComponentError", true); } examples/componenterror/packages/root.component1/meta/package.xml000066400000000000000000000004671325366651500256610ustar00rootroot00000000000000 Component 1 This component contains an error that prevents it from being installed. 1.0.1 2013-08-21 examples/componenterror/packages/root.component2/000077500000000000000000000000001325366651500226105ustar00rootroot00000000000000examples/componenterror/packages/root.component2/meta/000077500000000000000000000000001325366651500235365ustar00rootroot00000000000000examples/componenterror/packages/root.component2/meta/installscript.js000066400000000000000000000030341325366651500267670ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { var error = true; if (error) { installer.setValue("component_errors", installer.value("component_errors") + ";;;" + "Error in component: " + component.name); } installer.setValue("ComponentError", true); } examples/componenterror/packages/root.component2/meta/package.xml000066400000000000000000000005041325366651500256520ustar00rootroot00000000000000 Component 2 This component contains an error that prevents it from being installed. 1.0.1 2013-08-21 examples/componenterror/packages/root/000077500000000000000000000000001325366651500205255ustar00rootroot00000000000000examples/componenterror/packages/root/meta/000077500000000000000000000000001325366651500214535ustar00rootroot00000000000000examples/componenterror/packages/root/meta/installscript.js000066400000000000000000000050441325366651500247070ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function abortInstaller() { installer.setDefaultPageVisible(QInstaller.Introduction, false); installer.setDefaultPageVisible(QInstaller.TargetDirectory, false); installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); installer.setDefaultPageVisible(QInstaller.ReadyForInstallation, false); installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false); installer.setDefaultPageVisible(QInstaller.PerformInstallation, false); installer.setDefaultPageVisible(QInstaller.LicenseCheck, false); var abortText = "" + qsTr("Installation failed:") + ""; var error_list = installer.value("component_errors").split(";;;"); abortText += "

    "; // ignore the first empty one for (var i = 0; i < error_list.length; ++i) { if (error_list[i] !== "") { console.log(error_list[i]); abortText += "
  • " + error_list[i] + "
  • " } } abortText += "
"; installer.setValue("FinishedText", abortText); } function reactOnAbortInstallerChange() { if (installer.value("ComponentError") === "true") abortInstaller(); } function Component() { installer.finishAllComponentsReset.connect(reactOnAbortInstallerChange); } examples/componenterror/packages/root/meta/package.xml000066400000000000000000000004501325366651500235670ustar00rootroot00000000000000 Root Component This component handles the errors in the other components. 1.0.1 2013-08-21 examples/dependencies/000077500000000000000000000000001325366651500153365ustar00rootroot00000000000000examples/dependencies/README000066400000000000000000000002311325366651500162120ustar00rootroot00000000000000Create an installer that shows how the dependency solving works Generate installer with: binarycreator -c config/config.xml -p packages installer examples/dependencies/config/000077500000000000000000000000001325366651500166035ustar00rootroot00000000000000examples/dependencies/config/config.xml000066400000000000000000000007211325366651500205720ustar00rootroot00000000000000 Dependency Solving Example 1.0.0 Dependency Solving Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/dependencies true true examples/dependencies/dependencies.pro000066400000000000000000000005511325366651500205070ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/dependencies/packages/000077500000000000000000000000001325366651500171145ustar00rootroot00000000000000examples/dependencies/packages/componentA/000077500000000000000000000000001325366651500212175ustar00rootroot00000000000000examples/dependencies/packages/componentA/data/000077500000000000000000000000001325366651500221305ustar00rootroot00000000000000examples/dependencies/packages/componentA/data/installcontent.txt000066400000000000000000000000751325366651500257340ustar00rootroot00000000000000This file will be installed into the target directory.... examples/dependencies/packages/componentA/meta/000077500000000000000000000000001325366651500221455ustar00rootroot00000000000000examples/dependencies/packages/componentA/meta/package.xml000066400000000000000000000004461325366651500242660ustar00rootroot00000000000000 Component A This component does not depend on any other component. 1.0.0 2014-08-25 100 examples/dependencies/packages/componentB/000077500000000000000000000000001325366651500212205ustar00rootroot00000000000000examples/dependencies/packages/componentB/meta/000077500000000000000000000000001325366651500221465ustar00rootroot00000000000000examples/dependencies/packages/componentB/meta/package.xml000066400000000000000000000004451325366651500242660ustar00rootroot00000000000000 Component B This component does not depend on any other component. 1.0.0 2014-08-25 90 examples/dependencies/packages/componentC/000077500000000000000000000000001325366651500212215ustar00rootroot00000000000000examples/dependencies/packages/componentC/meta/000077500000000000000000000000001325366651500221475ustar00rootroot00000000000000examples/dependencies/packages/componentC/meta/package.xml000066400000000000000000000010761325366651500242700ustar00rootroot00000000000000 Component C (depends on A and B) This component depends on Component A and Component B. Selecting this component for installation also marks Component A and Component B for installation, which in turn marks Component D, because it has an automatic dependency on Component A and Component B. componentA, componentB 1.0.0 2014-08-25 80 examples/dependencies/packages/componentD/000077500000000000000000000000001325366651500212225ustar00rootroot00000000000000examples/dependencies/packages/componentD/meta/000077500000000000000000000000001325366651500221505ustar00rootroot00000000000000examples/dependencies/packages/componentD/meta/package.xml000066400000000000000000000007331325366651500242700ustar00rootroot00000000000000 Component D (auto depends on A and B) This component has an automatic dependency on Component A and Component B. If both A and B are marked for installation, this component is also installed. componentA, componentB 1.0.0 2014-08-25 70 examples/dependencies/packages/componentE/000077500000000000000000000000001325366651500212235ustar00rootroot00000000000000examples/dependencies/packages/componentE/meta/000077500000000000000000000000001325366651500221515ustar00rootroot00000000000000examples/dependencies/packages/componentE/meta/package.xml000066400000000000000000000005371325366651500242730ustar00rootroot00000000000000 Component E (forced) This is a forced component that is always installed. true 1.0.0 2014-08-25 60 examples/dependencies/packages/componentF.subcomponent1.subsubcomponent1/000077500000000000000000000000001325366651500273265ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent1.subsubcomponent1/meta/000077500000000000000000000000001325366651500302545ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml000066400000000000000000000004541325366651500323740ustar00rootroot00000000000000 Subsubcomponent 1 This component does not depend on any other component. 1.0.0 2014-08-25 100 examples/dependencies/packages/componentF.subcomponent1.subsubcomponent2/000077500000000000000000000000001325366651500273275ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent1.subsubcomponent2/meta/000077500000000000000000000000001325366651500302555ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent1.subsubcomponent2/meta/package.xml000066400000000000000000000004531325366651500323740ustar00rootroot00000000000000 Subsubcomponent 2 This component does not depend on any other component. 1.0.0 2014-08-25 50 examples/dependencies/packages/componentF.subcomponent1/000077500000000000000000000000001325366651500240205ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent1/meta/000077500000000000000000000000001325366651500247465ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent1/meta/package.xml000066400000000000000000000004351325366651500270650ustar00rootroot00000000000000 Subcomponent 1 This component contains 2 leaf components. 1.0.0 2014-08-25 100 examples/dependencies/packages/componentF.subcomponent2.subsubcomponent1/000077500000000000000000000000001325366651500273275ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent2.subsubcomponent1/meta/000077500000000000000000000000001325366651500302555ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent2.subsubcomponent1/meta/package.xml000066400000000000000000000004541325366651500323750ustar00rootroot00000000000000 Subsubcomponent 1 This component does not depend on any other component. 1.0.0 2014-08-25 100 examples/dependencies/packages/componentF.subcomponent2.subsubcomponent2/000077500000000000000000000000001325366651500273305ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent2.subsubcomponent2/meta/000077500000000000000000000000001325366651500302565ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent2.subsubcomponent2/meta/package.xml000066400000000000000000000004531325366651500323750ustar00rootroot00000000000000 Subsubcomponent 2 This component does not depend on any other component. 1.0.0 2014-08-25 50 examples/dependencies/packages/componentF.subcomponent2/000077500000000000000000000000001325366651500240215ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent2/meta/000077500000000000000000000000001325366651500247475ustar00rootroot00000000000000examples/dependencies/packages/componentF.subcomponent2/meta/package.xml000066400000000000000000000004341325366651500270650ustar00rootroot00000000000000 Subcomponent 2 This component contains 2 leaf components. 1.0.0 2014-08-25 50 examples/dependencies/packages/componentF/000077500000000000000000000000001325366651500212245ustar00rootroot00000000000000examples/dependencies/packages/componentF/meta/000077500000000000000000000000001325366651500221525ustar00rootroot00000000000000examples/dependencies/packages/componentF/meta/package.xml000066400000000000000000000004271325366651500242720ustar00rootroot00000000000000 Component F This component contains 2 subcomponents. 1.0.0 2014-08-25 40 examples/dependencies/packages/componentG/000077500000000000000000000000001325366651500212255ustar00rootroot00000000000000examples/dependencies/packages/componentG/meta/000077500000000000000000000000001325366651500221535ustar00rootroot00000000000000examples/dependencies/packages/componentG/meta/installscript.js000066400000000000000000000032111325366651500254010ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { component.addDependency("componentA"); } examples/dependencies/packages/componentG/meta/package.xml000066400000000000000000000007561325366651500243000ustar00rootroot00000000000000 Component G (default, depends on A, dependency added dynamically) By default, this component is selected for installation. It depends on component A. Dependency is added from inside component script. true 1.0.0 2014-08-25 30 examples/doc/000077500000000000000000000000001325366651500134555ustar00rootroot00000000000000examples/doc/changeuserinterface.qdoc000066400000000000000000000064221325366651500203360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \example changeuserinterface \ingroup qtifwexamples \title Change Installer UI Example \brief Using a component script to modify the installer UI. \image qtifw-examples-changeuserinterface.png \e {Change Installer UI} demonstrates how to use the \c Component() function to modify the default text for the radio button labels on the \l{License Agreement Page}{license check page}. This example does not install any components, but we specify a dummy component in the package information file, because installers without components are not allowed. \include installerfw-examples-configuring.qdocinc \quotefile changeuserinterface/config/config.xml \include installerfw-examples-packaging.qdocinc \list \li The \c element specifies whether the component is preselected for installation in the user interface by default. \li The \c org.qtproject.ifw.example.dynamicpage.node1 examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage.node2/000077500000000000000000000000001325366651500272545ustar00rootroot00000000000000examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage.node2/data/000077500000000000000000000000001325366651500301655ustar00rootroot00000000000000examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage.node2/data/data.node2.txt000066400000000000000000000000751325366651500326470ustar00rootroot00000000000000This file will be installed into the target directory.... examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage.node2/meta/000077500000000000000000000000001325366651500302025ustar00rootroot00000000000000examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage.node2/meta/installscript.qs000066400000000000000000000027541325366651500334520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { // default constructor } Component.prototype.createOperations = function() { // call default implementation to actually install something component.createOperations(); } examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage.node2/meta/package.xml000066400000000000000000000005361325366651500323230ustar00rootroot00000000000000 Example Component 2 Dummy component without default property set. 1.0.0 2014-04-07 org.qtproject.ifw.example.dynamicpage.node2 examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/000077500000000000000000000000001325366651500262465ustar00rootroot00000000000000examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/meta/000077500000000000000000000000001325366651500271745ustar00rootroot00000000000000examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/meta/installationwidget.ui000066400000000000000000000165651325366651500334550ustar00rootroot00000000000000 InstallationWidget 0 0 491 190 0 0 491 190 Form 0 6 Qt::Vertical QSizePolicy::Fixed 20 20 0 0 :/icon.png 0 0 Complete 0 0 Typical true 0 0 :/icon.png 0 0 :/icon.png Qt::Horizontal QSizePolicy::Fixed 20 1 0 0 Custom Qt::Horizontal QSizePolicy::Fixed 20 1 0 0 Common program features will be installed. Recommended for general use. 0 0 Choose which program features you want to install. Recommended for advanced users. 0 0 All program features will be installed. (Requires the most disk space) Qt::Vertical QSizePolicy::Fixed 20 20 Qt::Horizontal QSizePolicy::Fixed 20 1 defaultInstall completeInstall customInstall examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/meta/installscript.js000066400000000000000000000154701325366651500324340ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ var ComponentSelectionPage = null; var Dir = new function () { this.toNativeSparator = function (path) { if (systemInfo.productType === "windows") return path.replace(/\//g, '\\'); return path; } }; function Component() { if (installer.isInstaller()) { component.loaded.connect(this, Component.prototype.installerLoaded); ComponentSelectionPage = gui.pageById(QInstaller.ComponentSelection); installer.setDefaultPageVisible(QInstaller.TargetDirectory, false); installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); installer.setDefaultPageVisible(QInstaller.LicenseCheck, false); if (systemInfo.productType === "windows") installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false); installer.setDefaultPageVisible(QInstaller.ReadyForInstallation, false); } } Component.prototype.installerLoaded = function () { if (installer.addWizardPage(component, "TargetWidget", QInstaller.TargetDirectory)) { var widget = gui.pageWidgetByObjectName("DynamicTargetWidget"); if (widget != null) { widget.targetChooser.clicked.connect(this, Component.prototype.chooseTarget); widget.targetDirectory.textChanged.connect(this, Component.prototype.targetChanged); widget.windowTitle = "Installation Folder"; widget.targetDirectory.text = Dir.toNativeSparator(installer.value("TargetDir")); } } if (installer.addWizardPage(component, "InstallationWidget", QInstaller.ComponentSelection)) { var widget = gui.pageWidgetByObjectName("DynamicInstallationWidget"); if (widget != null) { widget.customInstall.toggled.connect(this, Component.prototype.customInstallToggled); widget.defaultInstall.toggled.connect(this, Component.prototype.defaultInstallToggled); widget.completeInstall.toggled.connect(this, Component.prototype.completeInstallToggled); widget.defaultInstall.checked = true; widget.windowTitle = "Select Installation Type"; } if (installer.addWizardPage(component, "LicenseWidget", QInstaller.LicenseCheck)) { var widget = gui.pageWidgetByObjectName("DynamicLicenseWidget"); if (widget != null) { widget.acceptLicense.toggled.connect(this, Component.prototype.checkAccepted); widget.complete = false; widget.declineLicense.checked = true; widget.windowTitle = "License Agreement"; } } if (installer.addWizardPage(component, "ReadyToInstallWidget", QInstaller.ReadyForInstallation)) { var widget = gui.pageWidgetByObjectName("DynamicReadyToInstallWidget"); if (widget != null) { widget.showDetails.checked = false; widget.windowTitle = "Ready to Install"; } var page = gui.pageByObjectName("DynamicReadyToInstallWidget"); if (page != null) { page.entered.connect(this, Component.prototype.readyToInstallWidgetEntered); } } } } Component.prototype.targetChanged = function (text) { var widget = gui.pageWidgetByObjectName("DynamicTargetWidget"); if (widget != null) { if (text != "") { if (!installer.fileExists(text + "/components.xml")) { widget.complete = true; installer.setValue("TargetDir", text); return; } } widget.complete = false; } } Component.prototype.chooseTarget = function () { var widget = gui.pageWidgetByObjectName("DynamicTargetWidget"); if (widget != null) { var newTarget = QFileDialog.getExistingDirectory("Choose your target directory.", widget .targetDirectory.text); if (newTarget != "") widget.targetDirectory.text = Dir.toNativeSparator(newTarget); } } Component.prototype.customInstallToggled = function (checked) { if (checked) { if (ComponentSelectionPage != null) ComponentSelectionPage.selectDefault(); installer.setDefaultPageVisible(QInstaller.ComponentSelection, true); } } Component.prototype.defaultInstallToggled = function (checked) { if (checked) { if (ComponentSelectionPage != null) ComponentSelectionPage.selectDefault(); installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); } } Component.prototype.completeInstallToggled = function (checked) { if (checked) { if (ComponentSelectionPage != null) ComponentSelectionPage.selectAll(); installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); } } Component.prototype.checkAccepted = function (checked) { var widget = gui.pageWidgetByObjectName("DynamicLicenseWidget"); if (widget != null) widget.complete = checked; } Component.prototype.readyToInstallWidgetEntered = function () { var widget = gui.pageWidgetByObjectName("DynamicReadyToInstallWidget"); if (widget != null) { var html = "Components to install:
    "; var components = installer.components(); for (i = 0; i < components.length; ++i) { if (components[i].installationRequested()) html = html + "
  • " + components[i].displayName + "
  • " } html = html + "
"; widget.showDetailsBrowser.html = html; } } examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/meta/licensewidget.ui000066400000000000000000000035761325366651500323740ustar00rootroot00000000000000 LicenseWidget 0 0 491 190 491 190 Form 0 0 Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation. true qrc:/license.txt I accept the license. I do not accept the license. true examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/meta/package.xml000066400000000000000000000012571325366651500313160ustar00rootroot00000000000000 Dynamic page installer example Can be used as reference on how to build installer independent of predefined installer pages. 1.0.0 2014-04-07 targetwidget.ui installationwidget.ui licensewidget.ui readytoinstallwidget.ui org.qtproject.ifw.example.dynamicpage examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/meta/readytoinstallwidget.ui000066400000000000000000000042461325366651500340030ustar00rootroot00000000000000 ReadyToInstallWidget 0 0 491 190 491 190 Form 0 0 Setup is now ready to begin installing Dynamic Page Installer Example on your computer. Show details true Qt::Vertical 20 0 label_2 showDetails showDetailsBrowser verticalSpacer_2 showDetails toggled(bool) showDetailsBrowser setVisible(bool) 63 37 85 89 examples/dynamicpage/packages/org.qtproject.ifw.example.dynamicpage/meta/targetwidget.ui000066400000000000000000000041501325366651500322250ustar00rootroot00000000000000 TargetWidget 0 0 491 190 0 0 491 190 Form Please specify the folder where Dynamic Page Installer Example will be installed. true 0 0 0 0 ... Qt::Vertical 20 122 examples/dynamicpage/resources/000077500000000000000000000000001325366651500172035ustar00rootroot00000000000000examples/dynamicpage/resources/additional.qrc000066400000000000000000000001761325366651500220260ustar00rootroot00000000000000 icon.png license.txt examples/dynamicpage/resources/icon.png000066400000000000000000000044631325366651500206500ustar00rootroot00000000000000‰PNG  IHDR szzôgAMAą üa pHYsÂÂ(J€tEXtSoftwarepaint.net 4.0;čõiģIDATXG•W PVŨŘ&1ąŅīĶĩ5ŠĶĐK­Ķ ĒĻüīŅA ŠVRDGë§6‚Jl2u2étL$NŽ#Ē֘1ÖÔJ". "ĒâēĻ€€, (ēœžsó_Ä6ÖæĖ{ũ―ûūsūåÞûý.ž%:9Ąq§ûũï{VWWI$‰D˗/$,[ķŽyóæÍ_ŌÎâ)BäēŊHŒ„XrÁEsâ9Ē?l>pā@̇‡FõCóÝÝÝ icĄp‹\Ä"Ō―8~ä ōþ{„>ZQ___]VVÖæp8ð(ðôôĄ ré@0íüŒčM(įē­hˆGW‘+  Ð@/fmÚīĐ)!!AÆ­^^^•111ąīáI(Ï ũó„-F›nËĨk'+@:Q―Ģīīījåʕðóók‡ŋï$Lôņ3ðóó}čāëë{ËÛÛûC~ïF;ōR+Ŧ'!råÞŪ4‘vÐYUϰēĪĪĪ~ōäÉ „Ū.Ą°øëąˆNõnĮšĒO8°4v<Â"'"0ħâWÞŊŋGŊŌÆb1А›K,Ļ&ÄûŒļý?.Ģ —.]j FPP^ tkRĸīD§:Š(,ųO'&‹;ĸNE֍Ãëiģ;!"‰ą­+·ð 1uĸþýe™™™ 1Œ2ūƒðâĮ&ĪMĒ%W:äđˆWéĨ›kĀðđggg.ZīsæĖ1MņxXC?þã_Ģņ~3?RŠĸöm:ønÕQņ UÎ)È.„`S!č~ (ũ[ZZ‚“““kÃÃÃ1þ|L›6í?"Ð{Å`N(__H6ũuMUæ]—đýÍØ Ē€ģzŠŪ("UQ* ŠLO ÐRéĮ­4þėŲģ­K–,Á… 1}útü|Šgŧžû.§į*Íģ·>ýģ4Ņs6ĸÞžģQ €‹zÝ›s‰ėŠxZF>ÛķmËÔō‹ŒŒÄŠ+Š3ū-B\{ĖMÍMœ ėČúƒyÖgųX3ßm„ú9äðևø!R9j‹Pžš>Ū›_AëŨŊŋĪÐ ģgφŋŋ?<ρđ–ÖVN>Θežu_øŠįUĨawÆsŋ:ÅË Čäp<ĄíWivC yn#`hóˆ*((Ļ]°`„°°0`HāhļüvVq`æÎ‘hĨyųįô· IŨŠ1 ī6§]@‡ĻâPKŅHˆGŦÂրĒŌŲ…ûó,ßÚÐÐÐaÞžyb™Â" î‡Ĩôþ7ąîĻŧSgD|’ņķ!é1ÂĘŊN7š››)Ö Ŧ(8účë­ī[ÝØØ˜Ã9ߐl7ą“XIhŦ6Å)cSΟ?‹/bŨŪ]P-LŸŒ—'ŧÃeJ_ˌŊābNššš°3;Ęp]扆Æė9ģ[îFQÉ5,Þ;‹ĸá‰Ĩĸ‡ØļXlßū;vė@\\ķlŲ"ÁÍlxōîÝŧÅû>Jïž={N§ĶĶâøņãHKKCzz:Ν;‡ŦWŊâdÎYøū?“>Š€ÕĄ(Ŋ(Gfy‚ õČĩČ―’wD}öâS’°úŦÄĨ­CjÁ>—–˜FåōåËČĘĘ… Œ]mv<ōkČ=C~A%_‘ļõäɓČČȀš!MTTB\ŋ~Ŧķ†ãIWK;ŠGĀ_'Ąžēŧãũ"pÝkļråŠ!ĘÍÍ·spSk'äō›œ:uĘ8xâÄ ]ĩ›%I€Šbb|||đ&Yr},Õ2VZV‚Úš:x-~ ÃÞĢÖˈH<šŸw‡ĒĻâ*XÄFD~~~ŧĨTNȞėĘ9EũÐĄC-tú4y#$@ËÁ/™Ló>”999ČËËÝ;wĀcsÝ0fžžxģ/.åå`í'ŅðŒpOPj+BßH„lȖœ‘m‰`Ú‘„ũCï!ýQSScŌĢũ§đ6ķ:ĶbÆ ·ÉFĻðÅŲ]Ė–čžNßļqc―õZFE*’[·n›ũę†ŅoŒ3ϔ‚Ž"$ڊ°Đ°"ļSĶ’CZÄ%ĮŧI€Îe5 zð,Ũýg4tßz-É[­†ĘĘ0ŅžÓ3+ÂֈM…DtŽvÚwYg‹Č!ïÅĨ˜U :2më䯿d/Ûģ&ÕĸÚqĨ(ŸņyûX"EáßëÁĨRA›§h[‡’MđŪÏwŒĀ‹„͍ęá}*ÎĶŅzî\m"Ą<ֆRWWg g)Šį71 e$-fÞkxÎT0­đī7ŸP[&ûâ|ŽxUDŪpčÖĶU§RG$ąƒų;B!gH–wũîÝæøæáLjVn0·ų.‹ë=‘ïÖqū?1‰PČC õúĮl―į"@ÔčI€`'ĻH-Aâ^dD†ņ8ÃpNä8ˆ˜įĖ뛄Z{ĨR]ūU4õ‡D-™‘*ō^ŽōŸ2Æý īĻ :OĀIENDŪB`‚examples/dynamicpage/resources/license.txt000066400000000000000000000001211325366651500213600ustar00rootroot00000000000000The fantastic license, have you heard of the Beer Public License Agreement yet? examples/examples.pro000066400000000000000000000004241325366651500152500ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += \ changeuserinterface \ componenterror \ dependencies \ dynamicpage \ licenseagreement \ modifyextract \ online \ openreadme \ quitinstaller \ registerfileextension \ startmenu \ systeminfo \ stylesheet examples/hidecheckbox/000077500000000000000000000000001325366651500153305ustar00rootroot00000000000000examples/hidecheckbox/README000066400000000000000000000002261325366651500162100ustar00rootroot00000000000000Shows how to hide top level item's checkbox. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/hidecheckbox/config/000077500000000000000000000000001325366651500165755ustar00rootroot00000000000000examples/hidecheckbox/config/config.xml000066400000000000000000000005041325366651500205630ustar00rootroot00000000000000 Hide item checkbox 1.0.0 Hide checkbox The Qt Company Qt IFW Examples @HomeDir@/IfwExamples/hidecheckbox examples/hidecheckbox/hidecheckbox.pro000066400000000000000000000005511325366651500204730ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/hidecheckbox/packages/000077500000000000000000000000001325366651500171065ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF.subcomponent1.subsubcomponent1/000077500000000000000000000000001325366651500273205ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF.subcomponent1.subsubcomponent1/data/000077500000000000000000000000001325366651500302315ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF.subcomponent1.subsubcomponent1/data/testF_sub1_sub1.txt000066400000000000000000000000261325366651500337410ustar00rootroot00000000000000test sub sub versio3 examples/hidecheckbox/packages/componentF.subcomponent1.subsubcomponent1/meta/000077500000000000000000000000001325366651500302465ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF.subcomponent1.subsubcomponent1/meta/package.xml000066400000000000000000000004001325366651500323550ustar00rootroot00000000000000 Subsubcomponent 1 This component does not depend on any other component. 1.0.2 2015-12-01 examples/hidecheckbox/packages/componentF.subcomponent1/000077500000000000000000000000001325366651500240125ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF.subcomponent1/data/000077500000000000000000000000001325366651500247235ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF.subcomponent1/data/testF_sub1.txt000066400000000000000000000000231325366651500274760ustar00rootroot00000000000000test sub1 versio3 examples/hidecheckbox/packages/componentF.subcomponent1/meta/000077500000000000000000000000001325366651500247405ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF.subcomponent1/meta/package.xml000066400000000000000000000004721325366651500270600ustar00rootroot00000000000000 Subcomponent 1 This component contains sub component. 1.0.2 2015-12-01 100 true examples/hidecheckbox/packages/componentF/000077500000000000000000000000001325366651500212165ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF/data/000077500000000000000000000000001325366651500221275ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF/data/testF.txt000066400000000000000000000000231325366651500237500ustar00rootroot00000000000000test sub1 versio3 examples/hidecheckbox/packages/componentF/meta/000077500000000000000000000000001325366651500221445ustar00rootroot00000000000000examples/hidecheckbox/packages/componentF/meta/package.xml000066400000000000000000000004711325366651500242630ustar00rootroot00000000000000 Uncheckable component This component is uncheckable. 1.0.0 2015-12-01 40 false examples/licenseagreement/000077500000000000000000000000001325366651500162225ustar00rootroot00000000000000examples/licenseagreement/README000066400000000000000000000002061325366651500171000ustar00rootroot00000000000000Shows how to ask interactively for a license agreement. binarycreator --offline-only -c config/config.xml -p packages installer examples/licenseagreement/config/000077500000000000000000000000001325366651500174675ustar00rootroot00000000000000examples/licenseagreement/config/config.xml000066400000000000000000000005461325366651500214630ustar00rootroot00000000000000 License Agreement Example 1.0.0 License Agreement Example Qt-Project Qt Installer Framework Example @HomeDir@/IfwExamples/licenseagreement examples/licenseagreement/licenseagreement.pro000066400000000000000000000005511325366651500222570ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/licenseagreement/packages/000077500000000000000000000000001325366651500200005ustar00rootroot00000000000000examples/licenseagreement/packages/org.qtproject.ifw.example.licenseagreement/000077500000000000000000000000001325366651500303305ustar00rootroot00000000000000examples/licenseagreement/packages/org.qtproject.ifw.example.licenseagreement/data/000077500000000000000000000000001325366651500312415ustar00rootroot00000000000000examples/licenseagreement/packages/org.qtproject.ifw.example.licenseagreement/data/README.txt000066400000000000000000000001201325366651500327300ustar00rootroot00000000000000This README file can only be installed if the user accepts the licenses. examples/licenseagreement/packages/org.qtproject.ifw.example.licenseagreement/meta/000077500000000000000000000000001325366651500312565ustar00rootroot00000000000000examples/licenseagreement/packages/org.qtproject.ifw.example.licenseagreement/meta/cc0.txt000066400000000000000000000156761325366651500325030ustar00rootroot00000000000000 CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; moral rights retained by the original author(s) and/or performer(s); publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; rights protecting the extraction, dissemination, use and reuse of data in a Work; database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. examples/licenseagreement/packages/org.qtproject.ifw.example.licenseagreement/meta/gpl3.txt000066400000000000000000001057551325366651500327010ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . examples/licenseagreement/packages/org.qtproject.ifw.example.licenseagreement/meta/package.xml000066400000000000000000000010021325366651500333640ustar00rootroot00000000000000 README (requires license agreement) README can only be installed if the user agrees to the licenses. 2014-01-01 1.0.0-1 true examples/modifyextract/000077500000000000000000000000001325366651500155725ustar00rootroot00000000000000examples/modifyextract/README000066400000000000000000000002731325366651500164540ustar00rootroot00000000000000Create an installer that uses the extract archive hook to modify the target path. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/modifyextract/config/000077500000000000000000000000001325366651500170375ustar00rootroot00000000000000examples/modifyextract/config/config.xml000066400000000000000000000005421325366651500210270ustar00rootroot00000000000000 Modify Extract Installer Example 1.0.0 Modify Extract Installer Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/modifyextract examples/modifyextract/modifyextract.pro000066400000000000000000000005511325366651500211770ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/modifyextract/packages/000077500000000000000000000000001325366651500173505ustar00rootroot00000000000000examples/modifyextract/packages/org.qtproject.ifw.example.modifyextract/000077500000000000000000000000001325366651500272505ustar00rootroot00000000000000examples/modifyextract/packages/org.qtproject.ifw.example.modifyextract/data/000077500000000000000000000000001325366651500301615ustar00rootroot00000000000000examples/modifyextract/packages/org.qtproject.ifw.example.modifyextract/data/my_content/000077500000000000000000000000001325366651500323405ustar00rootroot00000000000000examples/modifyextract/packages/org.qtproject.ifw.example.modifyextract/data/my_content/test.txt000066400000000000000000000000471325366651500340610ustar00rootroot00000000000000test test test test test test test testexamples/modifyextract/packages/org.qtproject.ifw.example.modifyextract/meta/000077500000000000000000000000001325366651500301765ustar00rootroot00000000000000examples/modifyextract/packages/org.qtproject.ifw.example.modifyextract/meta/installscript.js000066400000000000000000000031361325366651500334320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { } Component.prototype.createOperationsForArchive = function(archive) { // don't use the default operation // component.createOperationsForArchive(archive); // add an extract operation with a modified path component.addOperation("Extract", archive, "@TargetDir@/extractToAnotherPath"); } examples/modifyextract/packages/org.qtproject.ifw.example.modifyextract/meta/package.xml000066400000000000000000000005221325366651500323120ustar00rootroot00000000000000 Modify extract operation A component that uses a hook to modify the extract operation. 1.0.1 2014-12-19 true examples/online/000077500000000000000000000000001325366651500141745ustar00rootroot00000000000000examples/online/README000066400000000000000000000012741325366651500150600ustar00rootroot00000000000000Shows how to set up an online installer. The example uses a very simple web server shipped with python. Generate online repository with repogen -p packages repository Generate installer with binarycreator --online-only -c config/config.xml -p packages installer Now launch a minimal web server in the example's directory (admin rights may be needed) python -m SimpleHTTPServer 80 This should make the content of the local directory available under http://localhost You should be able to now launch the installer. To deploy an update, run repogen --update-new-components -p packages_update repository and launch the maintenance tool in your installation. examples/online/config/000077500000000000000000000000001325366651500154415ustar00rootroot00000000000000examples/online/config/config.xml000066400000000000000000000010331325366651500174250ustar00rootroot00000000000000 Online Installer Example 1.0.0 Online Installer Example The Qt Company Qt IFW Examples @HomeDir@/IfwExamples/online http://localhost/repository examples/online/online.pro000066400000000000000000000005671325366651500162120ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator --online-only -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/online/packages/000077500000000000000000000000001325366651500157525ustar00rootroot00000000000000examples/online/packages/A/000077500000000000000000000000001325366651500161325ustar00rootroot00000000000000examples/online/packages/A/data/000077500000000000000000000000001325366651500170435ustar00rootroot00000000000000examples/online/packages/A/data/A.txt000066400000000000000000000000421325366651500177600ustar00rootroot00000000000000Example content for package A. examples/online/packages/A/meta/000077500000000000000000000000001325366651500170605ustar00rootroot00000000000000examples/online/packages/A/meta/package.xml000066400000000000000000000003751325366651500212020ustar00rootroot00000000000000 A Example component A 1.0.2-1 2015-01-01 true examples/online/packages/B/000077500000000000000000000000001325366651500161335ustar00rootroot00000000000000examples/online/packages/B/data/000077500000000000000000000000001325366651500170445ustar00rootroot00000000000000examples/online/packages/B/data/B.txt000066400000000000000000000000421325366651500177620ustar00rootroot00000000000000Example content for package B. examples/online/packages/B/meta/000077500000000000000000000000001325366651500170615ustar00rootroot00000000000000examples/online/packages/B/meta/package.xml000066400000000000000000000003751325366651500212030ustar00rootroot00000000000000 B Example component B 1.0.0-1 2015-01-01 true examples/online/packages_update/000077500000000000000000000000001325366651500173145ustar00rootroot00000000000000examples/online/packages_update/A/000077500000000000000000000000001325366651500174745ustar00rootroot00000000000000examples/online/packages_update/A/data/000077500000000000000000000000001325366651500204055ustar00rootroot00000000000000examples/online/packages_update/A/data/A.txt000066400000000000000000000000611325366651500213230ustar00rootroot00000000000000Example content for package A (new version!). examples/online/packages_update/A/meta/000077500000000000000000000000001325366651500204225ustar00rootroot00000000000000examples/online/packages_update/A/meta/package.xml000066400000000000000000000003751325366651500225440ustar00rootroot00000000000000 A Example component A 1.0.3-1 2015-01-01 true examples/online/packages_update/B/000077500000000000000000000000001325366651500174755ustar00rootroot00000000000000examples/online/packages_update/B/data/000077500000000000000000000000001325366651500204065ustar00rootroot00000000000000examples/online/packages_update/B/data/B.txt000066400000000000000000000000421325366651500213240ustar00rootroot00000000000000Example content for package B. examples/online/packages_update/B/meta/000077500000000000000000000000001325366651500204235ustar00rootroot00000000000000examples/online/packages_update/B/meta/package.xml000066400000000000000000000003751325366651500225450ustar00rootroot00000000000000 B Example component B 1.0.0-1 2015-01-01 true examples/openreadme/000077500000000000000000000000001325366651500150275ustar00rootroot00000000000000examples/openreadme/README000066400000000000000000000002461325366651500157110ustar00rootroot00000000000000Shows how to add a 'Open ReadMe' checkbox to the final page. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/openreadme/config/000077500000000000000000000000001325366651500162745ustar00rootroot00000000000000examples/openreadme/config/config.xml000066400000000000000000000005051325366651500202630ustar00rootroot00000000000000 Open Readme Example 1.0.0 Open Readme Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/openreadme examples/openreadme/openreadme.pro000066400000000000000000000005511325366651500176710ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/openreadme/packages/000077500000000000000000000000001325366651500166055ustar00rootroot00000000000000examples/openreadme/packages/or.qtproject.ifw.example.openreadme/000077500000000000000000000000001325366651500255735ustar00rootroot00000000000000examples/openreadme/packages/or.qtproject.ifw.example.openreadme/data/000077500000000000000000000000001325366651500265045ustar00rootroot00000000000000examples/openreadme/packages/or.qtproject.ifw.example.openreadme/data/README.txt000066400000000000000000000000141325366651500301750ustar00rootroot00000000000000README examples/openreadme/packages/or.qtproject.ifw.example.openreadme/meta/000077500000000000000000000000001325366651500265215ustar00rootroot00000000000000examples/openreadme/packages/or.qtproject.ifw.example.openreadme/meta/installscript.qs000066400000000000000000000046231325366651500317660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { installer.installationFinished.connect(this, Component.prototype.installationFinishedPageIsShown); installer.finishButtonClicked.connect(this, Component.prototype.installationFinished); } Component.prototype.createOperations = function() { component.createOperations(); } Component.prototype.installationFinishedPageIsShown = function() { try { if (installer.isInstaller() && installer.status == QInstaller.Success) { installer.addWizardPageItem( component, "ReadMeCheckBoxForm", QInstaller.InstallationFinished ); } } catch(e) { console.log(e); } } Component.prototype.installationFinished = function() { try { if (installer.isInstaller() && installer.status == QInstaller.Success) { var isReadMeCheckBoxChecked = component.userInterface( "ReadMeCheckBoxForm" ).readMeCheckBox.checked; if (isReadMeCheckBoxChecked) { QDesktopServices.openUrl("file:///" + installer.value("TargetDir") + "/README.txt"); } } } catch(e) { console.log(e); } } examples/openreadme/packages/or.qtproject.ifw.example.openreadme/meta/package.xml000066400000000000000000000006741325366651500306450ustar00rootroot00000000000000 Open readme Show checkbox asking whether to open Readme at the end 1.0.0-1 2013-01-01 true readmecheckboxform.ui examples/openreadme/packages/or.qtproject.ifw.example.openreadme/meta/readmecheckboxform.ui000066400000000000000000000014511325366651500327110ustar00rootroot00000000000000 ReadMeCheckBoxForm 0 0 412 179 0 Open ReadMe true false examples/quitinstaller/000077500000000000000000000000001325366651500156105ustar00rootroot00000000000000examples/quitinstaller/README000066400000000000000000000002321325366651500164650ustar00rootroot00000000000000Shows how to quit an installer from script side. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/quitinstaller/config/000077500000000000000000000000001325366651500170555ustar00rootroot00000000000000examples/quitinstaller/config/config.xml000066400000000000000000000005161325366651500210460ustar00rootroot00000000000000 Quit Installer Example 1.0.0 Quit Installer Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/quitinstaller examples/quitinstaller/packages/000077500000000000000000000000001325366651500173665ustar00rootroot00000000000000examples/quitinstaller/packages/org.qtproject.ifw.example.quitinstaller/000077500000000000000000000000001325366651500273045ustar00rootroot00000000000000examples/quitinstaller/packages/org.qtproject.ifw.example.quitinstaller/meta/000077500000000000000000000000001325366651500302325ustar00rootroot00000000000000examples/quitinstaller/packages/org.qtproject.ifw.example.quitinstaller/meta/installscript.js000066400000000000000000000044511325366651500334670ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { var result = QMessageBox["question"]("test.quit", "Installer", "Do you want to quit the installer?
" + "This message box was created using JavaScript.", QMessageBox.Ok | QMessageBox.Cancel); if (result == QMessageBox.Ok) { installer.setValue("FinishedText", "The installer was quit."); installer.setDefaultPageVisible(QInstaller.TargetDirectory, false); installer.setDefaultPageVisible(QInstaller.ReadyForInstallation, false); installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false); installer.setDefaultPageVisible(QInstaller.PerformInstallation, false); installer.setDefaultPageVisible(QInstaller.LicenseCheck, false); gui.clickButton(buttons.NextButton); } else { installer.setValue("FinishedText", "The installer was not quit by JavaScript."); } } examples/quitinstaller/packages/org.qtproject.ifw.example.quitinstaller/meta/package.xml000066400000000000000000000005161325366651500323510ustar00rootroot00000000000000 Quit an installer Quits the installer in a nice way, if there is something missing 1.0.1 2013-02-27 true examples/quitinstaller/quitinstaller.pro000066400000000000000000000005511325366651500212330ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/registerfileextension/000077500000000000000000000000001325366651500173315ustar00rootroot00000000000000examples/registerfileextension/README000066400000000000000000000002411325366651500202060ustar00rootroot00000000000000Shows how to register a special file suffix on Windows. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/registerfileextension/config/000077500000000000000000000000001325366651500205765ustar00rootroot00000000000000examples/registerfileextension/config/config.xml000066400000000000000000000005501325366651500225650ustar00rootroot00000000000000 Register File Extension Example 1.0.0 Register File Extension Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/registerfileextension examples/registerfileextension/packages/000077500000000000000000000000001325366651500211075ustar00rootroot00000000000000examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/000077500000000000000000000000001325366651500325465ustar00rootroot00000000000000examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/data/000077500000000000000000000000001325366651500334575ustar00rootroot00000000000000registeredfile000066400000000000000000000000711325366651500363160ustar00rootroot00000000000000examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/dataThis file should open with notepad.exe if double clicked.examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/meta/000077500000000000000000000000001325366651500334745ustar00rootroot00000000000000installscript.qs000066400000000000000000000107371325366651500366650ustar00rootroot00000000000000examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/meta/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { component.loaded.connect(this, addRegisterFileCheckBox); installer.installationFinished.connect(this, addOpenFileCheckBoxToFinishPage); installer.finishButtonClicked.connect(this, openRegisteredFileIfChecked); component.unusualFileType = generateUnusualFileType(5) } generateUnusualFileType = function(length) { var randomString = ""; var possible = "abcdefghijklmnopqrstuvwxyz0123456789"; for (var i = 0; i < length; i++) randomString += possible.charAt(Math.floor(Math.random() * possible.length)); return randomString; } // called as soon as the component was loaded addRegisterFileCheckBox = function() { // don't show when updating or uninstalling if (installer.isInstaller()) { installer.addWizardPageItem(component, "RegisterFileCheckBoxForm", QInstaller.TargetDirectory); component.userInterface("RegisterFileCheckBoxForm").RegisterFileCheckBox.text = component.userInterface("RegisterFileCheckBoxForm").RegisterFileCheckBox.text + component.unusualFileType; } } // here we are creating the operation chain which will be processed at the real installation part later Component.prototype.createOperations = function() { // call default implementation to actually install the registeredfile component.createOperations(); var isRegisterFileChecked = component.userInterface("RegisterFileCheckBoxForm").RegisterFileCheckBox.checked; if (installer.value("os") === "win") { var iconId = 0; var notepadPath = installer.environmentVariable("SystemRoot") + "\\notepad.exe"; component.addOperation("RegisterFileType", component.unusualFileType, notepadPath + " '%1'", "QInstaller Framework example file type", "text/plain", notepadPath + "," + iconId, "ProgId=QtProject.QtInstallerFramework." + component.unusualFileType); } component.fileWithRegisteredType = installer.value("TargetDir") + "/registeredfile." + component.unusualFileType component.addOperation("Move", "@TargetDir@/registeredfile", component.fileWithRegisteredType); } openRegisteredFileIfChecked = function() { if (!component.installed) return; if (installer.value("os") == "win" && installer.isInstaller() && installer.status == QInstaller.Success) { var isOpenRegisteredFileChecked = component.userInterface("OpenFileCheckBoxForm").OpenRegisteredFileCheckBox.checked; if (isOpenRegisteredFileChecked) { QDesktopServices.openUrl("file:///" + component.fileWithRegisteredType); } } } addOpenFileCheckBoxToFinishPage = function() { if (installer.isInstaller() && installer.status == QInstaller.Success) { installer.addWizardPageItem(component, "OpenFileCheckBoxForm", QInstaller.InstallationFinished); component.userInterface("OpenFileCheckBoxForm").OpenRegisteredFileCheckBox.text = component.userInterface("OpenFileCheckBoxForm").OpenRegisteredFileCheckBox.text + component.unusualFileType; } } openfilecheckboxform.ui000066400000000000000000000014241325366651500401510ustar00rootroot00000000000000examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/meta OpenFileCheckBoxForm 0 0 400 300 Form Open registered example file based on suffix: true package.xml000066400000000000000000000010371325366651500355330ustar00rootroot00000000000000examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/meta Register a file extension Register a randomly generated file extension to open with notepad.exe 1.0.0-1 2013-01-01 true registerfilecheckboxform.ui openfilecheckboxform.ui registerfilecheckboxform.ui000066400000000000000000000014111325366651500410300ustar00rootroot00000000000000examples/registerfileextension/packages/org.qtproject.ifw.example.registerfileextension/meta RegisterFileCheckBoxForm 0 0 400 300 Form Register example file extension: true examples/registerfileextension/registerfileextension.pro000066400000000000000000000005511325366651500244750ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/startmenu/000077500000000000000000000000001325366651500147325ustar00rootroot00000000000000examples/startmenu/README000066400000000000000000000002361325366651500156130ustar00rootroot00000000000000Shows how to add an entry to the Windows start menu. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/startmenu/config/000077500000000000000000000000001325366651500161775ustar00rootroot00000000000000examples/startmenu/config/config.xml000066400000000000000000000006321325366651500201670ustar00rootroot00000000000000 Start Menu Shortcut Example 1.0.0 Start Menu Shortcut Example Qt-Project Qt Installer Framework Examples @HomeDir@/IfwExamples/startmenu examples/startmenu/packages/000077500000000000000000000000001325366651500165105ustar00rootroot00000000000000examples/startmenu/packages/org.qtproject.ifw.example/000077500000000000000000000000001325366651500235275ustar00rootroot00000000000000examples/startmenu/packages/org.qtproject.ifw.example/data/000077500000000000000000000000001325366651500244405ustar00rootroot00000000000000examples/startmenu/packages/org.qtproject.ifw.example/data/README.txt000066400000000000000000000001351325366651500261350ustar00rootroot00000000000000This file can be opened through the start menu, "Qt Installer Framework Example" section! examples/startmenu/packages/org.qtproject.ifw.example/meta/000077500000000000000000000000001325366651500244555ustar00rootroot00000000000000examples/startmenu/packages/org.qtproject.ifw.example/meta/installscript.qs000066400000000000000000000034461325366651500277240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { // default constructor } Component.prototype.createOperations = function() { // call default implementation to actually install README.txt! component.createOperations(); if (systemInfo.productType === "windows") { component.addOperation("CreateShortcut", "@TargetDir@/README.txt", "@StartMenuDir@/README.lnk", "workingDirectory=@TargetDir@", "iconPath=%SystemRoot%/system32/SHELL32.dll", "iconId=2", "description=Open README file"); } } examples/startmenu/packages/org.qtproject.ifw.example/meta/package.xml000066400000000000000000000005161325366651500265740ustar00rootroot00000000000000 README.txt A README.txt, accessible through a start menu entry. 1.0.0-1 2013-01-01 true examples/startmenu/startmenu.pro000066400000000000000000000005511325366651500174770ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/stylesheet/000077500000000000000000000000001325366651500151015ustar00rootroot00000000000000examples/stylesheet/README000066400000000000000000000002271325366651500157620ustar00rootroot00000000000000Shows how to customize a UI using stylesheet. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/stylesheet/config/000077500000000000000000000000001325366651500163465ustar00rootroot00000000000000examples/stylesheet/config/config.xml000066400000000000000000000006711325366651500203410ustar00rootroot00000000000000 Stylesheet Example 1.0.0 Stylesheet Example Qt-Project Qt IFW Examples @HomeDir@/IfwExamples/stylesheet Classic style.qss #FFFFFF examples/stylesheet/config/style.qss000066400000000000000000000011771325366651500202440ustar00rootroot00000000000000QWidget { color: white; background-color: rgb(65, 65, 65); } QPushButton { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(150, 150, 150, 60%), stop:1 rgba(50, 50, 50, 60%)); border-color: rgb(60, 60, 60); border-style: solid; border-width: 2px; border-radius: 9px; min-height: 20px; max-height: 20px; min-width: 60px; max-width: 60px; padding-left: 15px; padding-right: 15px; } QPushButton:pressed, QPushButton:checked { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(50, 50, 50, 60%), stop:1 rgba(150, 150, 150, 60%)); } examples/stylesheet/packages/000077500000000000000000000000001325366651500166575ustar00rootroot00000000000000examples/stylesheet/packages/org.qtproject.ifw.example.stylesheet/000077500000000000000000000000001325366651500260665ustar00rootroot00000000000000examples/stylesheet/packages/org.qtproject.ifw.example.stylesheet/meta/000077500000000000000000000000001325366651500270145ustar00rootroot00000000000000examples/stylesheet/packages/org.qtproject.ifw.example.stylesheet/meta/installscript.qs000066400000000000000000000032731325366651500322610ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { // constructor installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); } examples/stylesheet/packages/org.qtproject.ifw.example.stylesheet/meta/package.xml000066400000000000000000000003621325366651500311320ustar00rootroot00000000000000 Dummy 2015-09-09 1.0.1 true examples/stylesheet/stylesheet.pro000066400000000000000000000005511325366651500200150ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/systeminfo/000077500000000000000000000000001325366651500151105ustar00rootroot00000000000000examples/systeminfo/README000066400000000000000000000007051325366651500157720ustar00rootroot00000000000000Shows how to use the systemInfo JS API. The installer rejects installations on Windows XP, and OS X 10.6 or older. It shows a warning if it is run on any Linux distribution other than openSUSE 13.2. It also shows only one of the two sub-packages, based on the CPU architecture: "i386" on an 32-bit system, and "x86_64" on an 64-bit system. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer examples/systeminfo/config/000077500000000000000000000000001325366651500163555ustar00rootroot00000000000000examples/systeminfo/config/config.xml000066400000000000000000000006141325366651500203450ustar00rootroot00000000000000 SystemInfo Example 1.0.0 SystemInfo Example The Qt Company Qt Installer Framework Example @HomeDir@/IfwExamples/systeminfo examples/systeminfo/packages/000077500000000000000000000000001325366651500166665ustar00rootroot00000000000000examples/systeminfo/packages/root.i386/000077500000000000000000000000001325366651500203415ustar00rootroot00000000000000examples/systeminfo/packages/root.i386/meta/000077500000000000000000000000001325366651500212675ustar00rootroot00000000000000examples/systeminfo/packages/root.i386/meta/package.xml000066400000000000000000000004271325366651500234070ustar00rootroot00000000000000 i386 binaries Binaries that require a x86 CPU. 1.0.0-1 2014-12-01 false examples/systeminfo/packages/root.x86_64/000077500000000000000000000000001325366651500206065ustar00rootroot00000000000000examples/systeminfo/packages/root.x86_64/meta/000077500000000000000000000000001325366651500215345ustar00rootroot00000000000000examples/systeminfo/packages/root.x86_64/meta/package.xml000066400000000000000000000004401325366651500236470ustar00rootroot00000000000000 x86-64 bit binaries Binaries that require a x86-64 CPU. 1.0.0-1 2014-12-01 false examples/systeminfo/packages/root/000077500000000000000000000000001325366651500176515ustar00rootroot00000000000000examples/systeminfo/packages/root/meta/000077500000000000000000000000001325366651500205775ustar00rootroot00000000000000examples/systeminfo/packages/root/meta/installscript.qs000066400000000000000000000112131325366651500240350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // Skip all pages and go directly to finished page. // (see also componenterror example) function cancelInstaller(message) { installer.setDefaultPageVisible(QInstaller.Introduction, false); installer.setDefaultPageVisible(QInstaller.TargetDirectory, false); installer.setDefaultPageVisible(QInstaller.ComponentSelection, false); installer.setDefaultPageVisible(QInstaller.ReadyForInstallation, false); installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false); installer.setDefaultPageVisible(QInstaller.PerformInstallation, false); installer.setDefaultPageVisible(QInstaller.LicenseCheck, false); var abortText = "" + message +""; installer.setValue("FinishedText", abortText); } // Returns the major version number as int // string.split(".", 1) returns the string before the first '.', // parseInt() converts it to an int. function majorVersion(str) { return parseInt(str.split(".", 1)); } function Component() { // // Check whether OS is supported. // // For Windows and OS X we check the kernel version: // - Require at least Windows Vista (winnt kernel version 6.0.x) // - Require at least OS X 10.7 (Lion) (darwin kernel version 11.x) // // If the kernel version is older we move directly // to the final page & show an error. // // For Linux, we check the distribution and version, but only // show a warning if it does not match our preferred distribution. // // start installer with -v to see debug output console.log("OS: " + systemInfo.productType); console.log("Kernel: " + systemInfo.kernelType + "/" + systemInfo.kernelVersion); var validOs = false; if (systemInfo.kernelType === "winnt") { if (majorVersion(systemInfo.kernelVersion) >= 6) validOs = true; } else if (systemInfo.kernelType === "darwin") { if (majorVersion(systemInfo.kernelVersion) >= 11) validOs = true; } else { if (systemInfo.productType !== "opensuse" || systemInfo.productVersion !== "13.2") { QMessageBox["warning"]("os.warning", "Installer", "Note that the binaries are only tested on OpenSUSE 13.2.", QMessageBox.Ok); } validOs = true; } if (!validOs) { cancelInstaller("Installation on " + systemInfo.prettyProductName + " is not supported"); return; } // // Hide/select packages based on architecture // // Marking a component as "Virtual" will hide it in the UI. // Marking a component with "Default" will check it. // console.log("CPU Architecture: " + systemInfo.currentCpuArchitecture); installer.componentByName("root.i386").setValue("Virtual", "true"); installer.componentByName("root.x86_64").setValue("Virtual", "true"); if ( systemInfo.currentCpuArchitecture === "i386") { installer.componentByName("root.i386").setValue("Virtual", "false"); installer.componentByName("root.i386").setValue("Default", "true"); } if ( systemInfo.currentCpuArchitecture === "x86_64") { installer.componentByName("root.x86_64").setValue("Virtual", "false"); installer.componentByName("root.x86_64").setValue("Default", "true"); } } examples/systeminfo/packages/root/meta/package.xml000066400000000000000000000004541325366651500227170ustar00rootroot00000000000000 systemInfo example Shows the use of the systemInfo JS API. 1.0.0-1 2013-01-01 examples/systeminfo/systeminfo.pro000066400000000000000000000005511325366651500200330ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/translations/000077500000000000000000000000001325366651500154315ustar00rootroot00000000000000examples/translations/README000066400000000000000000000006661325366651500163210ustar00rootroot00000000000000Shows how to translate the installer UI. You have to call lrelease on the included .ts file before building the installer. Generate installer with binarycreator --offline-only -c config/config.xml -p packages installer You can now run the installer in German. Linux: LANGUAGE=de ./installer On OS X and Windows you need to adapt the system settings to set German as preferred language, and then start the installer. examples/translations/config/000077500000000000000000000000001325366651500166765ustar00rootroot00000000000000examples/translations/config/config.xml000066400000000000000000000005411325366651500206650ustar00rootroot00000000000000 Translations Example 1.0.0 Package Translations Example Qt Project Qt Installer Framework Examples @HomeDir@/IfwExamples/translations examples/translations/packages/000077500000000000000000000000001325366651500172075ustar00rootroot00000000000000examples/translations/packages/com.vendor.product/000077500000000000000000000000001325366651500227405ustar00rootroot00000000000000examples/translations/packages/com.vendor.product/data/000077500000000000000000000000001325366651500236515ustar00rootroot00000000000000examples/translations/packages/com.vendor.product/data/installcontent.txt000066400000000000000000000000751325366651500274550ustar00rootroot00000000000000This file will be installed into the target directory.... examples/translations/packages/com.vendor.product/meta/000077500000000000000000000000001325366651500236665ustar00rootroot00000000000000examples/translations/packages/com.vendor.product/meta/de.ts000066400000000000000000000012001325366651500246170ustar00rootroot00000000000000 Page Translations Example Beispiel fÞr Übersetzungen This is some text. Dies ist ein Text. installscript This is a dynamically created page. Diese Seite wurde dynamisch erzeugt. examples/translations/packages/com.vendor.product/meta/installscript.qs000066400000000000000000000041551325366651500271330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { // constructor component.loaded.connect(this, Component.prototype.loaded); installer.addWizardPage(component, "Page", QInstaller.TargetDirectory) } Component.prototype.createOperations = function() { try { // call the base create operations function component.createOperations(); } catch (e) { console.log(e); } } Component.prototype.loaded = function () { var page = gui.pageByObjectName("DynamicPage"); if (page != null) { page.entered.connect(Component.prototype.dynamicPageEntered); } } Component.prototype.dynamicPageEntered = function () { var pageWidget = gui.pageWidgetByObjectName("DynamicPage"); if (pageWidget != null) { pageWidget.m_pageLabel.text = qsTr("This is a dynamically created page."); } } examples/translations/packages/com.vendor.product/meta/license.txt000066400000000000000000000001211325366651500260430ustar00rootroot00000000000000The fantastic license, have you heard of the Beer Public License Agreement yet? examples/translations/packages/com.vendor.product/meta/license_de.txt000066400000000000000000000000251325366651500265160ustar00rootroot00000000000000Lizenz auf deutsch. examples/translations/packages/com.vendor.product/meta/license_pl.txt000066400000000000000000000000251325366651500265410ustar00rootroot00000000000000Licencja po polsku. examples/translations/packages/com.vendor.product/meta/package.xml000066400000000000000000000012471325366651500260070ustar00rootroot00000000000000 The root component This component contains a license and translations to German. 0.5.0-1 2015-01-29 true page.ui de.qm pl.qm examples/translations/packages/com.vendor.product/meta/page.ui000066400000000000000000000020701325366651500251400ustar00rootroot00000000000000 Page 0 0 400 300 Translations Example QFrame::Box Qt::AlignCenter QFrame::Box This is some text. Qt::AlignCenter examples/translations/packages/com.vendor.product/meta/pl.ts000066400000000000000000000014651325366651500246570ustar00rootroot00000000000000 Page Translations Example Przykład obrazujący przekład na inny język This is some text. To jest pewien tekst. installscript This is a dynamically created page. Jest to strona utworzona dynamicznie. examples/translations/translations.pro000066400000000000000000000005511325366651500206750ustar00rootroot00000000000000TEMPLATE = aux INSTALLER = installer INPUT = $$PWD/config/config.xml $$PWD/packages example.input = INPUT example.output = $$INSTALLER example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT} example.CONFIG += target_predeps no_link combine QMAKE_EXTRA_COMPILERS += example OTHER_FILES = README examples/tutorial/000077500000000000000000000000001325366651500145535ustar00rootroot00000000000000examples/tutorial/config/000077500000000000000000000000001325366651500160205ustar00rootroot00000000000000examples/tutorial/config/config.xml000066400000000000000000000005031325366651500200050ustar00rootroot00000000000000 Your application 1.0.0 Your application Installer Your vendor Super App @HomeDir@/InstallationDirectory examples/tutorial/packages/000077500000000000000000000000001325366651500163315ustar00rootroot00000000000000examples/tutorial/packages/com.vendor.product/000077500000000000000000000000001325366651500220625ustar00rootroot00000000000000examples/tutorial/packages/com.vendor.product/data/000077500000000000000000000000001325366651500227735ustar00rootroot00000000000000examples/tutorial/packages/com.vendor.product/data/installcontent.txt000066400000000000000000000000751325366651500265770ustar00rootroot00000000000000This file will be installed into the target directory.... examples/tutorial/packages/com.vendor.product/meta/000077500000000000000000000000001325366651500230105ustar00rootroot00000000000000examples/tutorial/packages/com.vendor.product/meta/installscript.qs000066400000000000000000000046211325366651500262530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Component() { // constructor component.loaded.connect(this, Component.prototype.loaded); if (!installer.addWizardPage(component, "Page", QInstaller.TargetDirectory)) console.log("Could not add the dynamic page."); } Component.prototype.isDefault = function() { // select the component by default return true; } Component.prototype.createOperations = function() { try { // call the base create operations function component.createOperations(); } catch (e) { console.log(e); } } Component.prototype.loaded = function () { var page = gui.pageByObjectName("DynamicPage"); if (page != null) { console.log("Connecting the dynamic page entered signal."); page.entered.connect(Component.prototype.dynamicPageEntered); } } Component.prototype.dynamicPageEntered = function () { var pageWidget = gui.pageWidgetByObjectName("DynamicPage"); if (pageWidget != null) { console.log("Setting the widgets label text.") pageWidget.m_pageLabel.text = "This is a dynamically created page."; } } examples/tutorial/packages/com.vendor.product/meta/license.txt000066400000000000000000000001211325366651500251650ustar00rootroot00000000000000The fantastic license, have you heard of the Beer Public License Agreement yet? examples/tutorial/packages/com.vendor.product/meta/package.xml000066400000000000000000000010041325366651500251200ustar00rootroot00000000000000 The root component Install this example. 0.1.0-1 2010-09-21 script page.ui examples/tutorial/packages/com.vendor.product/meta/page.ui000066400000000000000000000012071325366651500242630ustar00rootroot00000000000000 Page 0 0 400 300 Dynamic page example Qt::AlignCenter installerfw.pri000066400000000000000000000076451325366651500141540ustar00rootroot00000000000000!isEmpty(IFW_PRI_INCLUDED) { error("installerfw.pri already included") } IFW_PRI_INCLUDED = 1 IFW_VERSION_STR = 3.0.4 IFW_VERSION = 0x030004 IFW_REPOSITORY_FORMAT_VERSION = 1.0.0 IFW_NEWLINE = $$escape_expand(\\n\\t) defineTest(minQtVersion) { maj = $$1 min = $$2 patch = $$3 isEqual(QT_MAJOR_VERSION, $$maj) { isEqual(QT_MINOR_VERSION, $$min) { isEqual(QT_PATCH_VERSION, $$patch) { return(true) } greaterThan(QT_PATCH_VERSION, $$patch) { return(true) } } greaterThan(QT_MINOR_VERSION, $$min) { return(true) } } greaterThan(QT_MAJOR_VERSION, $$maj) { return(true) } return(false) } defineReplace(toNativeSeparators) { return($$replace(1, /, $$QMAKE_DIR_SEP)) } defineReplace(fromNativeSeparators) { return($$replace(1, \\\\, /)) } defineReplace(cleanPath) { win32:1 ~= s|\\\\|/|g contains(1, ^/.*):pfx = / else:pfx = segs = $$split(1, /) out = for(seg, segs) { equals(seg, ..):out = $$member(out, 0, -2) else:!equals(seg, .):out += $$seg } return($$join(out, /, $$pfx)) } isEmpty(IFW_BUILD_TREE) { sub_dir = $$_PRO_FILE_PWD_ sub_dir ~= s,^$$re_escape($$PWD),, IFW_BUILD_TREE = $$cleanPath($$OUT_PWD) IFW_BUILD_TREE ~= s,$$re_escape($$sub_dir)$,, } IFW_SOURCE_TREE = $$PWD IFW_APP_PATH = $$IFW_BUILD_TREE/bin IFW_LIB_PATH = $$IFW_BUILD_TREE/lib RCC = $$toNativeSeparators($$cleanPath($$[QT_INSTALL_BINS]/rcc)) LRELEASE = $$toNativeSeparators($$cleanPath($$[QT_INSTALL_BINS]/lrelease)) LUPDATE = $$toNativeSeparators($$cleanPath($$[QT_INSTALL_BINS]/lupdate)) LCONVERT = $$toNativeSeparators($$cleanPath($$[QT_INSTALL_BINS]/lconvert)) QMAKE_BINARY = $$toNativeSeparators($$cleanPath($$[QT_INSTALL_BINS]/qmake)) win32 { RCC = $${RCC}.exe LRELEASE = $${LRELEASE}.exe LUPDATE = $${LUPDATE}.exe LCONVERT = $${LCONVERT}.exe QMAKE_BINARY = $${QMAKE_BINARY}.exe } win32-g++*:QMAKE_CXXFLAGS += -Wno-attributes macx:QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden INCLUDEPATH += \ $$IFW_SOURCE_TREE/src/libs/7zip \ $$IFW_SOURCE_TREE/src/libs/kdtools \ $$IFW_SOURCE_TREE/src/libs/installer win32:INCLUDEPATH += $$IFW_SOURCE_TREE/src/libs/7zip/win/CPP unix:INCLUDEPATH += $$IFW_SOURCE_TREE/src/libs/7zip/unix/CPP LIBS += -L$$IFW_LIB_PATH # The order is important. The linker needs to parse archives in reversed dependency order. equals(TEMPLATE, app):LIBS += -linstaller win32:equals(TEMPLATE, app) { LIBS += -luser32 } unix:!macx:LIBS += -lutil macx:LIBS += -framework Carbon -framework Security # # Qt 5 sets QT_CONFIG # Use same static/shared configuration as Qt # !contains(CONFIG, static|shared) { contains(QT_CONFIG, static): CONFIG += static contains(QT_CONFIG, shared): CONFIG += shared } QT += uitools core-private CONFIG(static, static|shared) { win32:QT += winextras QT += concurrent network qml xml } CONFIG += depend_includepath no_private_qt_headers_warning c++11 exists(".git") { GIT_SHA1 = $$system(git rev-list --abbrev-commit -n1 HEAD) } isEmpty(GIT_SHA1) { # Attempt to read the sha1 from alternative location GIT_SHA1=\"$$cat(.tag)\" } DEFINES += NOMINMAX QT_NO_CAST_FROM_ASCII QT_STRICT_ITERATORS QT_USE_QSTRINGBUILDER \ "_GIT_SHA1_=$$GIT_SHA1" \ IFW_VERSION_STR=$$IFW_VERSION_STR IFW_VERSION=$$IFW_VERSION DEFINES += IFW_REPOSITORY_FORMAT_VERSION=$$IFW_REPOSITORY_FORMAT_VERSION LIBS += -l7z win32-g++*: LIBS += -lmpr -luuid equals(TEMPLATE, app) { msvc:POST_TARGETDEPS += $$IFW_LIB_PATH/installer.lib $$IFW_LIB_PATH/7z.lib win32-g++*:POST_TARGETDEPS += $$IFW_LIB_PATH/libinstaller.a $$IFW_LIB_PATH/lib7z.a unix:POST_TARGETDEPS += $$IFW_LIB_PATH/libinstaller.a $$IFW_LIB_PATH/lib7z.a } installerfw.pro000066400000000000000000000011671325366651500141530ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += src tools tools.depends = src requires(!cross_compile) include (installerfw.pri) include (doc/doc.pri) BUILD_TESTS = $$(BUILDTESTS) isEmpty(BUILD_TESTS):BUILD_TESTS=$${BUILDTESTS} !isEmpty(BUILD_TESTS) { SUBDIRS += tests tests.depends = src } BUILD_EXAMPLES = $$(BUILDEXAMPLES) isEmpty(BUILD_EXAMPLES):BUILD_EXAMPLES=$${BUILDEXAMPLES} !isEmpty(BUILD_EXAMPLES) { SUBDIRS += examples examples.depends = src } !minQtVersion(5, 6, 2) { message("Cannot build Qt Installer Framework with Qt version $${QT_VERSION}.") error("Use at least Qt 5.6.2.") } no_app_bundle.pri000066400000000000000000000002521325366651500144120ustar00rootroot00000000000000!isEmpty(NO_APP_BUNDLE_PRI_INCLUDED) { error("no_app_bundle.pri already included") } NO_APP_BUNDLE_PRI_INCLUDED = 1 equals(TEMPLATE, app):CONFIG -= app_bundle src/000077500000000000000000000000001325366651500116615ustar00rootroot00000000000000src/libs/000077500000000000000000000000001325366651500126125ustar00rootroot00000000000000src/libs/7zip/000077500000000000000000000000001325366651500135035ustar00rootroot00000000000000src/libs/7zip/7zip.pri000066400000000000000000000012471325366651500151140ustar00rootroot00000000000000DEFINES += _UNICODE _NO_CRYPTO win32 { 7ZIP_BASE=$$PWD/win INCLUDEPATH += $$7ZIP_BASE/C $$7ZIP_BASE/CPP DEFINES += WIN_LONG_PATH _CRT_SECURE_NO_WARNINGS QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings } unix { 7ZIP_BASE=$$PWD/unix INCLUDEPATH += \ $$7ZIP_BASE/C \ $$7ZIP_BASE/CPP \ $$7ZIP_BASE/CPP/myWindows \ $$7ZIP_BASE/CPP/include_windows macx:DEFINES += ENV_MACOSX DEFINES += _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE NDEBUG _REENTRANT ENV_UNIX UNICODE } unix|win32-g++* { QMAKE_CFLAGS += -w QMAKE_CXXFLAGS += -w } src/libs/7zip/7zip.pro000066400000000000000000000004621325366651500151200ustar00rootroot00000000000000QT = core TARGET = 7z TEMPLATE = lib include(../../../installerfw.pri) INCLUDEPATH += . .. CONFIG += staticlib DESTDIR = $$IFW_LIB_PATH include(7zip.pri) win32:include($$7ZIP_BASE/win.pri) #7zip unix:include($$7ZIP_BASE/unix.pri) #p7zip target.path = $$[QT_INSTALL_LIBS] INSTALLS += target src/libs/7zip/installer_framework_changes.txt000066400000000000000000000041321325366651500220060ustar00rootroot00000000000000There are deleted files and very small changes to get the integration process of new versions very simple. --diff-filter=M means "modified" and --diff-filter=D means "deleted" files === output of: git diff --diff-filter=M === diff --git a/CPP/7zip/Common/RegisterArc.h b/CPP/7zip/Common/RegisterArc.h index bc2a034..9b8cbd3 100644 --- a/CPP/7zip/Common/RegisterArc.h +++ b/CPP/7zip/Common/RegisterArc.h @@ -27,6 +27,6 @@ void RegisterArc(const CArcInfo *arcInfo); #define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) { \ REGISTER_ARC_NAME(x)() { RegisterArc(&g_ArcInfo); }}; \ - static REGISTER_ARC_NAME(x) g_RegisterArc; - + static REGISTER_ARC_NAME(x) g_RegisterArc; \ + void registerArc##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } #endif diff --git a/CPP/7zip/Common/RegisterCodec.h b/CPP/7zip/Common/RegisterCodec.h index 786b4a4..d53c434 100644 --- a/CPP/7zip/Common/RegisterCodec.h +++ b/CPP/7zip/Common/RegisterCodec.h @@ -22,12 +22,13 @@ void RegisterCodec(const CCodecInfo *codecInfo); #define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) { \ REGISTER_CODEC_NAME(x)() { RegisterCodec(&g_CodecInfo); }}; \ - static REGISTER_CODEC_NAME(x) g_RegisterCodec; + static REGISTER_CODEC_NAME(x) g_RegisterCodec; \ + void registerCodec##x() { static REGISTER_CODEC_NAME(x) g_RegisterCodecs; } #define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x #define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) { \ REGISTER_CODECS_NAME(x)() { for (int i = 0; i < sizeof(g_CodecsInfo) / sizeof(g_CodecsInfo[0]); i++) \ RegisterCodec(&g_CodecsInfo[i]); }}; \ - static REGISTER_CODECS_NAME(x) g_RegisterCodecs; - + static REGISTER_CODECS_NAME(x) g_RegisterCodecs; \ + void registerCodec##x() { static REGISTER_CODECS_NAME(x) g_RegisterCodecs; } #endif diff --git a/CPP/Common/MyString.h b/CPP/Common/MyString.h index eb3c52d..f483e39 100644 --- a/CPP/Common/MyString.h +++ b/CPP/Common/MyString.h @@ -7,6 +7,8 @@ #include "MyVector.h" +#include + template inline int MyStringLen(const T *s) { src/libs/7zip/patches/000077500000000000000000000000001325366651500151325ustar00rootroot00000000000000src/libs/7zip/patches/0001-Adjust-7z-and-p7z.patch000066400000000000000000000447171325366651500216140ustar00rootroot00000000000000From f643c01e4e8534f26a5a2d260caa566d23cdcb13 Mon Sep 17 00:00:00 2001 From: Karsten Heimrich Date: Thu, 4 Jun 2015 15:41:51 +0200 Subject: [PATCH 1/1] Adjust 7z and p7z. Change-Id: I3b96d2b02e5a0908fb4cf5b4262cb33516a10098 --- src/libs/7zip/7zip.pri | 11 ++++- src/libs/7zip/7zip.pro | 14 +----- src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp | 2 - src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h | 32 ++++++++++--- src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h | 29 ++++++++--- src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp | 2 - .../unix/CPP/include_windows/include_windows.pri | 3 ++ .../unix/CPP/myWindows/myCommandLineParser.cpp | 56 ++++++++++++++++++++++ src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp | 42 ++++++++-------- src/libs/7zip/unix/CPP/myWindows/myWindows.pri | 7 +++ src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp | 2 - src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h | 32 ++++++++++--- src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h | 29 ++++++++--- src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp | 2 - 14 files changed, 194 insertions(+), 69 deletions(-) create mode 100644 src/libs/7zip/unix/CPP/include_windows/include_windows.pri create mode 100644 src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp create mode 100644 src/libs/7zip/unix/CPP/myWindows/myWindows.pri diff --git a/src/libs/7zip/7zip.pri b/src/libs/7zip/7zip.pri index 823e3ab..85574ce 100644 --- a/src/libs/7zip/7zip.pri +++ b/src/libs/7zip/7zip.pri @@ -1,7 +1,11 @@ +DEFINES += _UNICODE _NO_CRYPTO + win32 { 7ZIP_BASE=$$PWD/win INCLUDEPATH += $$7ZIP_BASE/C $$7ZIP_BASE/CPP - DEFINES += WIN_LONG_PATH _UNICODE _NO_CRYPTO + DEFINES += WIN_LONG_PATH _CRT_SECURE_NO_WARNINGS + win32-g++*:QMAKE_CXXFLAGS += -w -fvisibility=hidden + QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings } @@ -14,6 +18,9 @@ unix { $$7ZIP_BASE/CPP/myWindows \ $$7ZIP_BASE/CPP/include_windows + QMAKE_CFLAGS += -w + QMAKE_CXXFLAGS += -fvisibility=hidden -w + macx:DEFINES += ENV_MACOSX - DEFINES += _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE NDEBUG _REENTRANT ENV_UNIX UNICODE _UNICODE _NO_CRYPTO + DEFINES += _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE NDEBUG _REENTRANT ENV_UNIX UNICODE } diff --git a/src/libs/7zip/7zip.pro b/src/libs/7zip/7zip.pro index 01b69da..70a51c0 100644 --- a/src/libs/7zip/7zip.pro +++ b/src/libs/7zip/7zip.pro @@ -7,15 +7,5 @@ CONFIG += staticlib DESTDIR = $$IFW_LIB_PATH include(7zip.pri) -win32 { - DEFINES += _CRT_SECURE_NO_WARNINGS - win32-g++*:QMAKE_CXXFLAGS += -w -fvisibility=hidden - CONFIG += no_batch # this is needed because we have a same named *.c and *.cpp file -> 7in - include($$7ZIP_BASE/win.pri) #this is 7zip -} - -unix { - QMAKE_CFLAGS += -w - QMAKE_CXXFLAGS += -fvisibility=hidden -w - include($$7ZIP_BASE/unix.pri) #this is p7zip -} +win32:include($$7ZIP_BASE/win.pri) #7zip +unix:include($$7ZIP_BASE/unix.pri) #p7zip diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp index 8af28b9..e20858e 100644 --- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp @@ -4,8 +4,6 @@ #include "../../../../C/7zCrc.h" -#include "../../../Common/AutoPtr.h" - #include "../../Common/StreamObjects.h" #include "7zOut.h" diff --git a/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h b/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h index 1e9bf14..82bd096 100644 --- a/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h +++ b/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h @@ -5,6 +5,8 @@ #include "../Archive/IArchive.h" +#include + struct CArcInfo { const char *Name; @@ -24,19 +26,35 @@ struct CArcInfo Func_IsArc IsArc; bool IsMultiSignature() const { return (Flags & NArcInfoFlags::kMultiSignature) != 0; } + + std::once_flag once; }; void RegisterArc(const CArcInfo *arcInfo) throw(); #define REGISTER_ARC_NAME(x) CRegister ## x -#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) { \ - REGISTER_ARC_NAME(x)() { RegisterArc(&g_ArcInfo); }}; \ - static REGISTER_ARC_NAME(x) g_RegisterArc; - -#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) { \ - REGISTER_ARC_NAME(x)() { g_ArcInfo.Signature[0]--; RegisterArc(&g_ArcInfo); }}; \ - static REGISTER_ARC_NAME(x) g_RegisterArc; +#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) \ + { \ + REGISTER_ARC_NAME(x)() \ + { \ + std::call_once(g_ArcInfo.once, [] { RegisterArc(&g_ArcInfo); }); \ + } \ + }; \ + static REGISTER_ARC_NAME(x) g_RegisterArc; \ + void registerArc##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } + +#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) \ + { \ + REGISTER_ARC_NAME(x)() { \ + std::call_once(g_ArcInfo.once, [] { \ + g_ArcInfo.Signature[0]--; \ + RegisterArc(&g_ArcInfo); \ + }); \ + } \ + }; \ + static REGISTER_ARC_NAME(x) g_RegisterArc; \ + void registerArcDec##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } #define IMP_CreateArcIn_2(c) \ diff --git a/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h b/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h index 4222a30..0c6662a 100644 --- a/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h +++ b/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h @@ -6,6 +6,8 @@ #include "../Common/MethodId.h" #include "../ICoder.h" +#include + typedef void * (*CreateCodecP)(); struct CCodecInfo { @@ -15,21 +17,34 @@ struct CCodecInfo const wchar_t *Name; UInt32 NumInStreams; bool IsFilter; + std::once_flag once; }; void RegisterCodec(const CCodecInfo *codecInfo) throw(); #define REGISTER_CODEC_NAME(x) CRegisterCodec ## x -#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) { \ - REGISTER_CODEC_NAME(x)() { RegisterCodec(&g_CodecInfo); }}; \ - static REGISTER_CODEC_NAME(x) g_RegisterCodec; +#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) \ + { \ + REGISTER_CODEC_NAME(x)() \ + { \ + std::call_once(g_CodecInfo.once, [] { RegisterCodec(&g_CodecInfo); }); \ + } \ + }; \ + static REGISTER_CODEC_NAME(x) g_RegisterCodec; \ + void registerCodec##x() { static REGISTER_CODEC_NAME(x) g_RegisterCodecs; } #define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x -#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) { \ - REGISTER_CODECS_NAME(x)() { for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \ - RegisterCodec(&g_CodecsInfo[i]); }}; \ - static REGISTER_CODECS_NAME(x) g_RegisterCodecs; +#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) \ + { \ + REGISTER_CODECS_NAME(x)() \ + { \ + for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \ + std::call_once(g_CodecsInfo[i].once, [&i] { RegisterCodec(&g_CodecsInfo[i]); }); \ + } \ + }; \ + static REGISTER_CODECS_NAME(x) g_RegisterCodecs; \ + void registerCodec##x() { static REGISTER_CODECS_NAME(x) g_RegisterCodecs; } struct CHasherInfo diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp b/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp index 03f31fa..5f94254 100644 --- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp +++ b/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp @@ -2,8 +2,6 @@ #include "StdAfx.h" -#include "../../../../C/Sort.h" - #include "../../../Common/StringConvert.h" #include "../../../Windows/FileDir.h" diff --git a/src/libs/7zip/unix/CPP/include_windows/include_windows.pri b/src/libs/7zip/unix/CPP/include_windows/include_windows.pri new file mode 100644 index 0000000..5ef72fd --- /dev/null +++ b/src/libs/7zip/unix/CPP/include_windows/include_windows.pri @@ -0,0 +1,3 @@ +HEADERS += $$7ZIP_BASE/CPP/include_windows/basetyps.h \ + $$7ZIP_BASE/CPP/include_windows/tchar.h \ + $$7ZIP_BASE/CPP/include_windows/windows.h diff --git a/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp b/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp new file mode 100644 index 0000000..5d7f6fd --- /dev/null +++ b/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp @@ -0,0 +1,56 @@ +/************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "../CPP/Common/MyString.h" + +#include + +namespace NCommandLineParser { + +void SplitCommandLine(const UString &s, UStringVector &parts) +{ + parts.Clear(); + + const QString cmdLine = QString::fromStdWString(static_cast(s)); + const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts); + foreach (QString arg, args) { + if (arg.startsWith(QLatin1Char('\"'))) + arg = arg.mid(1); + if (arg.endsWith(QLatin1Char('\"'))) + arg = arg.mid(1); + parts.Add(arg.toStdWString().c_str()); + } +} + +} // namespace NCommandLineParser diff --git a/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp b/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp index 96554c9..9ebfe37 100644 --- a/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp +++ b/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp @@ -32,19 +32,18 @@ ** **************************************************************************/ -#include -#include #include "windows.h" +#include + void FileTimeToDateTime(const FILETIME *source, QDateTime *target) { ULARGE_INTEGER store; - QDateTime tempDateTime(QDate(1601, 1, 1)); - store.QuadPart = source->dwHighDateTime; store.QuadPart = store.QuadPart << 32; store.QuadPart += source->dwLowDateTime; + const QDateTime tempDateTime(QDate(1601, 1, 1), QTime(0, 0, 0, 0), Qt::UTC); *target = tempDateTime.addMSecs(store.QuadPart / 10000); } @@ -60,6 +59,13 @@ void DateTimeToSystemTime(const QDateTime *source, SYSTEMTIME *target) target->wMilliseconds = source->time().msec(); } +void DateTimeToFileTime(const QDateTime &dateTime, FILETIME *target) +{ + const qint64 nsecs = QDateTime(QDate(1601, 1, 1), QTime(0, 0, 0, 0), Qt::UTC) + .msecsTo(dateTime) * 10000; + target->dwLowDateTime = nsecs; + target->dwHighDateTime = nsecs >> 32; +} BOOL WINAPI FileTimeToSystemTime(CONST FILETIME *source,SYSTEMTIME *target) { @@ -70,21 +76,6 @@ BOOL WINAPI FileTimeToSystemTime(CONST FILETIME *source,SYSTEMTIME *target) return TRUE; } -BOOL WINAPI SystemTimeToFileTime(const SYSTEMTIME *source,FILETIME *target) -{ - // TODO: Implementation! - // This doesn't seem to be called at all - - qDebug() << "SystemTimeToFileTime"; - - target->dwHighDateTime = 0; - target->dwLowDateTime = 0; - - qWarning() << Q_FUNC_INFO; - - return TRUE; -} - BOOL WINAPI FileTimeToLocalFileTime(CONST FILETIME *source,FILETIME *target) { target->dwHighDateTime = source->dwHighDateTime; @@ -137,3 +128,16 @@ VOID WINAPI GetSystemTime(SYSTEMTIME *st) QDateTime nowDateTime = QDateTime::currentDateTimeUtc(); DateTimeToSystemTime(&nowDateTime, st); } + +VOID WINAPI GetSystemTimeAsFileTime(FILETIME *time) +{ + DateTimeToFileTime(QDateTime::currentDateTimeUtc(), time); +} + +DWORD WINAPI GetTickCount() +{ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts)) + return DWORD(ts.tv_sec * 1000 + ts.tv_nsec / 1000000); + return DWORD(QDateTime::currentMSecsSinceEpoch()); +} diff --git a/src/libs/7zip/unix/CPP/myWindows/myWindows.pri b/src/libs/7zip/unix/CPP/myWindows/myWindows.pri new file mode 100644 index 0000000..0875fdb --- /dev/null +++ b/src/libs/7zip/unix/CPP/myWindows/myWindows.pri @@ -0,0 +1,7 @@ +HEADERS += $$7ZIP_BASE/CPP/myWindows/StdAfx.h \ + $$7ZIP_BASE/CPP/myWindows/config.h \ + $$7ZIP_BASE/CPP/myWindows/initguid.h \ + $$7ZIP_BASE/CPP/myWindows/myPrivate.h + +SOURCES += $$7ZIP_BASE/CPP/myWindows/myDateAndTime.cpp \ + $$7ZIP_BASE/CPP/myWindows/myCommandLineParser.cpp diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp b/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp index 8af28b9..e20858e 100644 --- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp +++ b/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp @@ -4,8 +4,6 @@ #include "../../../../C/7zCrc.h" -#include "../../../Common/AutoPtr.h" - #include "../../Common/StreamObjects.h" #include "7zOut.h" diff --git a/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h b/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h index 1e9bf14..82bd096 100644 --- a/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h +++ b/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h @@ -5,6 +5,8 @@ #include "../Archive/IArchive.h" +#include + struct CArcInfo { const char *Name; @@ -24,19 +26,35 @@ struct CArcInfo Func_IsArc IsArc; bool IsMultiSignature() const { return (Flags & NArcInfoFlags::kMultiSignature) != 0; } + + std::once_flag once; }; void RegisterArc(const CArcInfo *arcInfo) throw(); #define REGISTER_ARC_NAME(x) CRegister ## x -#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) { \ - REGISTER_ARC_NAME(x)() { RegisterArc(&g_ArcInfo); }}; \ - static REGISTER_ARC_NAME(x) g_RegisterArc; - -#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) { \ - REGISTER_ARC_NAME(x)() { g_ArcInfo.Signature[0]--; RegisterArc(&g_ArcInfo); }}; \ - static REGISTER_ARC_NAME(x) g_RegisterArc; +#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) \ + { \ + REGISTER_ARC_NAME(x)() \ + { \ + std::call_once(g_ArcInfo.once, [] { RegisterArc(&g_ArcInfo); }); \ + } \ + }; \ + static REGISTER_ARC_NAME(x) g_RegisterArc; \ + void registerArc##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } + +#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) \ + { \ + REGISTER_ARC_NAME(x)() { \ + std::call_once(g_ArcInfo.once, [] { \ + g_ArcInfo.Signature[0]--; \ + RegisterArc(&g_ArcInfo); \ + }); \ + } \ + }; \ + static REGISTER_ARC_NAME(x) g_RegisterArc; \ + void registerArcDec##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } #define IMP_CreateArcIn_2(c) \ diff --git a/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h b/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h index 4222a30..0c6662a 100644 --- a/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h +++ b/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h @@ -6,6 +6,8 @@ #include "../Common/MethodId.h" #include "../ICoder.h" +#include + typedef void * (*CreateCodecP)(); struct CCodecInfo { @@ -15,21 +17,34 @@ struct CCodecInfo const wchar_t *Name; UInt32 NumInStreams; bool IsFilter; + std::once_flag once; }; void RegisterCodec(const CCodecInfo *codecInfo) throw(); #define REGISTER_CODEC_NAME(x) CRegisterCodec ## x -#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) { \ - REGISTER_CODEC_NAME(x)() { RegisterCodec(&g_CodecInfo); }}; \ - static REGISTER_CODEC_NAME(x) g_RegisterCodec; +#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) \ + { \ + REGISTER_CODEC_NAME(x)() \ + { \ + std::call_once(g_CodecInfo.once, [] { RegisterCodec(&g_CodecInfo); }); \ + } \ + }; \ + static REGISTER_CODEC_NAME(x) g_RegisterCodec; \ + void registerCodec##x() { static REGISTER_CODEC_NAME(x) g_RegisterCodecs; } #define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x -#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) { \ - REGISTER_CODECS_NAME(x)() { for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \ - RegisterCodec(&g_CodecsInfo[i]); }}; \ - static REGISTER_CODECS_NAME(x) g_RegisterCodecs; +#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) \ + { \ + REGISTER_CODECS_NAME(x)() \ + { \ + for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \ + std::call_once(g_CodecsInfo[i].once, [&i] { RegisterCodec(&g_CodecsInfo[i]); }); \ + } \ + }; \ + static REGISTER_CODECS_NAME(x) g_RegisterCodecs; \ + void registerCodec##x() { static REGISTER_CODECS_NAME(x) g_RegisterCodecs; } struct CHasherInfo diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp b/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp index df86620..13d2ad2 100644 --- a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp +++ b/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp @@ -2,8 +2,6 @@ #include "StdAfx.h" -#include "../../../../C/Sort.h" - #include "../../../Common/StringConvert.h" #include "../../../Windows/FileDir.h" -- 2.3.7.windows.1 src/libs/7zip/unix/000077500000000000000000000000001325366651500144665ustar00rootroot00000000000000src/libs/7zip/unix/C/000077500000000000000000000000001325366651500146505ustar00rootroot00000000000000src/libs/7zip/unix/C/7zCrc.c000066400000000000000000000041351325366651500160070ustar00rootroot00000000000000/* 7zCrc.c -- CRC32 init 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "7zCrc.h" #include "CpuArch.h" #define kCrcPoly 0xEDB88320 #ifdef MY_CPU_X86_OR_AMD64 #define CRC_NUM_TABLES 8 UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); #elif defined(MY_CPU_LE) #define CRC_NUM_TABLES 4 #else #define CRC_NUM_TABLES 5 #define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table); #endif #ifndef MY_CPU_BE UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); #endif typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); CRC_FUNC g_CrcUpdate; UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) { return g_CrcUpdate(v, data, size, g_CrcTable); } UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) { return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL; } void MY_FAST_CALL CrcGenerateTable() { UInt32 i; for (i = 0; i < 256; i++) { UInt32 r = i; unsigned j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); g_CrcTable[i] = r; } for (; i < 256 * CRC_NUM_TABLES; i++) { UInt32 r = g_CrcTable[i - 256]; g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); } #ifdef MY_CPU_LE g_CrcUpdate = CrcUpdateT4; #if CRC_NUM_TABLES == 8 #ifdef P7ZIP_USE_ASM if (!CPU_Is_InOrder()) g_CrcUpdate = CrcUpdateT8; #endif #endif #else { #ifndef MY_CPU_BE UInt32 k = 1; if (*(const Byte *)&k == 1) g_CrcUpdate = CrcUpdateT4; else #endif { for (i = 256 * CRC_NUM_TABLES - 1; i >= 256; i--) { UInt32 x = g_CrcTable[i - 256]; g_CrcTable[i] = CRC_UINT32_SWAP(x); } g_CrcUpdate = CrcUpdateT1_BeT4; } } #endif } src/libs/7zip/unix/C/7zCrc.h000066400000000000000000000011751325366651500160150ustar00rootroot00000000000000/* 7zCrc.h -- CRC32 calculation 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __7Z_CRC_H #define __7Z_CRC_H #include "7zTypes.h" EXTERN_C_BEGIN extern UInt32 g_CrcTable[]; /* Call CrcGenerateTable one time before other CRC functions */ void MY_FAST_CALL CrcGenerateTable(void); #define CRC_INIT_VAL 0xFFFFFFFF #define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL) #define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); EXTERN_C_END #endif src/libs/7zip/unix/C/7zCrcOpt.c000066400000000000000000000033441325366651500164730ustar00rootroot00000000000000/* 7zCrcOpt.c -- CRC32 calculation 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "CpuArch.h" #define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) #ifndef MY_CPU_BE UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); for (; size >= 4; size -= 4, p += 4) { v ^= *(const UInt32 *)p; v = table[0x300 + (v & 0xFF)] ^ table[0x200 + ((v >> 8) & 0xFF)] ^ table[0x100 + ((v >> 16) & 0xFF)] ^ table[0x000 + ((v >> 24))]; } for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) { return CrcUpdateT4(v, data, size, table); } #endif #ifndef MY_CPU_LE #define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); v = CRC_UINT32_SWAP(v); table += 0x100; for (; size >= 4; size -= 4, p += 4) { v ^= *(const UInt32 *)p; v = table[0x000 + (v & 0xFF)] ^ table[0x100 + ((v >> 8) & 0xFF)] ^ table[0x200 + ((v >> 16) & 0xFF)] ^ table[0x300 + ((v >> 24))]; } table -= 0x100; v = CRC_UINT32_SWAP(v); for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } #endif src/libs/7zip/unix/C/7zStream.c000066400000000000000000000101341325366651500165270ustar00rootroot00000000000000/* 7zStream.c -- 7z Stream functions 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "7zTypes.h" SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) { while (size != 0) { size_t processed = size; RINOK(stream->Read(stream, buf, &processed)); if (processed == 0) return errorType; buf = (void *)((Byte *)buf + processed); size -= processed; } return SZ_OK; } SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size) { return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); } SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf) { size_t processed = 1; RINOK(stream->Read(stream, buf, &processed)); return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF; } SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) { Int64 t = offset; return stream->Seek(stream, &t, SZ_SEEK_SET); } SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size) { const void *lookBuf; if (*size == 0) return SZ_OK; RINOK(stream->Look(stream, &lookBuf, size)); memcpy(buf, lookBuf, *size); return stream->Skip(stream, *size); } SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) { while (size != 0) { size_t processed = size; RINOK(stream->Read(stream, buf, &processed)); if (processed == 0) return errorType; buf = (void *)((Byte *)buf + processed); size -= processed; } return SZ_OK; } SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) { return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); } static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size) { SRes res = SZ_OK; CLookToRead *p = (CLookToRead *)pp; size_t size2 = p->size - p->pos; if (size2 == 0 && *size > 0) { p->pos = 0; size2 = LookToRead_BUF_SIZE; res = p->realStream->Read(p->realStream, p->buf, &size2); p->size = size2; } if (size2 < *size) *size = size2; *buf = p->buf + p->pos; return res; } static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size) { SRes res = SZ_OK; CLookToRead *p = (CLookToRead *)pp; size_t size2 = p->size - p->pos; if (size2 == 0 && *size > 0) { p->pos = 0; if (*size > LookToRead_BUF_SIZE) *size = LookToRead_BUF_SIZE; res = p->realStream->Read(p->realStream, p->buf, size); size2 = p->size = *size; } if (size2 < *size) *size = size2; *buf = p->buf + p->pos; return res; } static SRes LookToRead_Skip(void *pp, size_t offset) { CLookToRead *p = (CLookToRead *)pp; p->pos += offset; return SZ_OK; } static SRes LookToRead_Read(void *pp, void *buf, size_t *size) { CLookToRead *p = (CLookToRead *)pp; size_t rem = p->size - p->pos; if (rem == 0) return p->realStream->Read(p->realStream, buf, size); if (rem > *size) rem = *size; memcpy(buf, p->buf + p->pos, rem); p->pos += rem; *size = rem; return SZ_OK; } static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) { CLookToRead *p = (CLookToRead *)pp; p->pos = p->size = 0; return p->realStream->Seek(p->realStream, pos, origin); } void LookToRead_CreateVTable(CLookToRead *p, int lookahead) { p->s.Look = lookahead ? LookToRead_Look_Lookahead : LookToRead_Look_Exact; p->s.Skip = LookToRead_Skip; p->s.Read = LookToRead_Read; p->s.Seek = LookToRead_Seek; } void LookToRead_Init(CLookToRead *p) { p->pos = p->size = 0; } static SRes SecToLook_Read(void *pp, void *buf, size_t *size) { CSecToLook *p = (CSecToLook *)pp; return LookInStream_LookRead(p->realStream, buf, size); } void SecToLook_CreateVTable(CSecToLook *p) { p->s.Read = SecToLook_Read; } static SRes SecToRead_Read(void *pp, void *buf, size_t *size) { CSecToRead *p = (CSecToRead *)pp; return p->realStream->Read(p->realStream, buf, size); } void SecToRead_CreateVTable(CSecToRead *p) { p->s.Read = SecToRead_Read; } src/libs/7zip/unix/C/7zTypes.h000066400000000000000000000131511325366651500164070ustar00rootroot00000000000000/* 7zTypes.h -- Basic types 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H #ifdef _WIN32 /* #include */ #endif #include #ifndef EXTERN_C_BEGIN #ifdef __cplusplus #define EXTERN_C_BEGIN extern "C" { #define EXTERN_C_END } #else #define EXTERN_C_BEGIN #define EXTERN_C_END #endif #endif EXTERN_C_BEGIN #define SZ_OK 0 #define SZ_ERROR_DATA 1 #define SZ_ERROR_MEM 2 #define SZ_ERROR_CRC 3 #define SZ_ERROR_UNSUPPORTED 4 #define SZ_ERROR_PARAM 5 #define SZ_ERROR_INPUT_EOF 6 #define SZ_ERROR_OUTPUT_EOF 7 #define SZ_ERROR_READ 8 #define SZ_ERROR_WRITE 9 #define SZ_ERROR_PROGRESS 10 #define SZ_ERROR_FAIL 11 #define SZ_ERROR_THREAD 12 #define SZ_ERROR_ARCHIVE 16 #define SZ_ERROR_NO_ARCHIVE 17 typedef int SRes; #ifdef _WIN32 /* typedef DWORD WRes; */ typedef unsigned WRes; #else typedef int WRes; #endif #ifndef RINOK #define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } #endif typedef unsigned char Byte; typedef short Int16; typedef unsigned short UInt16; #ifdef _LZMA_UINT32_IS_ULONG typedef long Int32; typedef unsigned long UInt32; #else typedef int Int32; typedef unsigned int UInt32; #endif #ifdef _SZ_NO_INT_64 /* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. NOTES: Some code will work incorrectly in that case! */ typedef long Int64; typedef unsigned long UInt64; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 Int64; typedef unsigned __int64 UInt64; #define UINT64_CONST(n) n #else typedef long long int Int64; typedef unsigned long long int UInt64; #define UINT64_CONST(n) n ## ULL #endif #endif #ifdef _LZMA_NO_SYSTEM_SIZE_T typedef UInt32 SizeT; #else typedef size_t SizeT; #endif typedef int Bool; #define True 1 #define False 0 #ifdef _WIN32 #define MY_STD_CALL __stdcall #else #define MY_STD_CALL #endif #ifdef _MSC_VER #if _MSC_VER >= 1300 #define MY_NO_INLINE __declspec(noinline) #else #define MY_NO_INLINE #endif #define MY_CDECL __cdecl #define MY_FAST_CALL __fastcall #else #define MY_NO_INLINE #define MY_CDECL #define MY_FAST_CALL #endif /* The following interfaces use first parameter as pointer to structure */ typedef struct { Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ } IByteIn; typedef struct { void (*Write)(void *p, Byte b); } IByteOut; typedef struct { SRes (*Read)(void *p, void *buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) < input(*size)) is allowed */ } ISeqInStream; /* it can return SZ_ERROR_INPUT_EOF */ SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); typedef struct { size_t (*Write)(void *p, const void *buf, size_t size); /* Returns: result - the number of actually written bytes. (result < size) means error */ } ISeqOutStream; typedef enum { SZ_SEEK_SET = 0, SZ_SEEK_CUR = 1, SZ_SEEK_END = 2 } ESzSeek; typedef struct { SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); } ISeekInStream; typedef struct { SRes (*Look)(void *p, const void **buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) > input(*size)) is not allowed (output(*size) < input(*size)) is allowed */ SRes (*Skip)(void *p, size_t offset); /* offset must be <= output(*size) of Look */ SRes (*Read)(void *p, void *buf, size_t *size); /* reads directly (without buffer). It's same as ISeqInStream::Read */ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); } ILookInStream; SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); /* reads via ILookInStream::Read */ SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); #define LookToRead_BUF_SIZE (1 << 14) typedef struct { ILookInStream s; ISeekInStream *realStream; size_t pos; size_t size; Byte buf[LookToRead_BUF_SIZE]; } CLookToRead; void LookToRead_CreateVTable(CLookToRead *p, int lookahead); void LookToRead_Init(CLookToRead *p); typedef struct { ISeqInStream s; ILookInStream *realStream; } CSecToLook; void SecToLook_CreateVTable(CSecToLook *p); typedef struct { ISeqInStream s; ILookInStream *realStream; } CSecToRead; void SecToRead_CreateVTable(CSecToRead *p); typedef struct { SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); /* Returns: result. (result != SZ_OK) means break. Value (UInt64)(Int64)-1 for size means unknown value. */ } ICompressProgress; typedef struct { void *(*Alloc)(void *p, size_t size); void (*Free)(void *p, void *address); /* address can be 0 */ } ISzAlloc; #define IAlloc_Alloc(p, size) (p)->Alloc((p), size) #define IAlloc_Free(p, a) (p)->Free((p), a) #ifdef _WIN32 #define CHAR_PATH_SEPARATOR '\\' #define WCHAR_PATH_SEPARATOR L'\\' #define STRING_PATH_SEPARATOR "\\" #define WSTRING_PATH_SEPARATOR L"\\" #else #define CHAR_PATH_SEPARATOR '/' #define WCHAR_PATH_SEPARATOR L'/' #define STRING_PATH_SEPARATOR "/" #define WSTRING_PATH_SEPARATOR L"/" #endif EXTERN_C_END #endif src/libs/7zip/unix/C/7zVersion.h000066400000000000000000000006111325366651500167250ustar00rootroot00000000000000#define MY_VER_MAJOR 9 #define MY_VER_MINOR 38 #define MY_VER_BUILD 00 #define MY_VERSION "9.38 beta" // #define MY_7ZIP_VERSION "9.38" #define MY_DATE "2015-01-03" #undef MY_COPYRIGHT #undef MY_VERSION_COPYRIGHT_DATE #define MY_COPYRIGHT ": Igor Pavlov : Public domain" #define MY_VERSION_COPYRIGHT_DATE MY_VERSION " " MY_COPYRIGHT " : " MY_DATE #define P7ZIP_VERSION "9.38.1" src/libs/7zip/unix/C/Alloc.c000066400000000000000000000152031325366651500160470ustar00rootroot00000000000000/* Alloc.c -- Memory allocation functions 2008-09-24 Igor Pavlov Public domain */ #ifdef _WIN32 #include #endif #include #include #ifdef _7ZIP_LARGE_PAGES #ifdef __linux__ #ifndef _7ZIP_ST #include #endif #include #include #include #include #include #endif #endif #include "Alloc.h" /* #define _SZ_ALLOC_DEBUG */ /* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ #ifdef _SZ_ALLOC_DEBUG #include int g_allocCount = 0; int g_allocCountMid = 0; int g_allocCountBig = 0; #endif #ifdef P7ZIP_USE_ASM // #include extern int posix_memalign (void **, size_t, size_t); void *align_alloc(size_t size) { // return _mm_malloc(size,16); void * ptr = 0; if (posix_memalign (&ptr, 16, size) == 0) return ptr; else return NULL; } void align_free(void * ptr) { // _mm_free(ptr); free(ptr); } #else void *align_alloc(size_t size) { return malloc(size); } void align_free(void * ptr) { free(ptr); } #endif void *MyAlloc(size_t size) { if (size == 0) return 0; #ifdef _SZ_ALLOC_DEBUG { void *p = align_alloc(size); fprintf(stderr, "\nAlloc %10d bytes, count = %10d, addr = %8X", size, g_allocCount++, (unsigned)p); return p; } #else return align_alloc(size); #endif } void MyFree(void *address) { #ifdef _SZ_ALLOC_DEBUG if (address != 0) fprintf(stderr, "\nFree; count = %10d, addr = %8X", --g_allocCount, (unsigned)address); #endif align_free(address); } #ifndef _WIN32 #ifdef _7ZIP_LARGE_PAGES #ifdef __linux__ #define _7ZIP_MAX_HUGE_ALLOCS 64 static void *g_HugePageAddr[_7ZIP_MAX_HUGE_ALLOCS] = { NULL }; static size_t g_HugePageLen[_7ZIP_MAX_HUGE_ALLOCS]; static char *g_HugetlbPath; #endif #endif #ifdef _7ZIP_LARGE_PAGES static void *VirtualAlloc(size_t size, int memLargePages) { if (memLargePages) { #ifdef __linux__ /* huge pages support for Linux; added by Joachim Henke */ #ifndef _7ZIP_ST static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; #endif int i; void * address = NULL; #ifndef _7ZIP_ST pthread_mutex_lock(&mutex); #endif for (i = 0; i < _7ZIP_MAX_HUGE_ALLOCS; ++i) { if (g_HugePageAddr[i] == NULL) { int fd, pathlen = strlen(g_HugetlbPath); char tempname[pathlen+12]; memcpy(tempname, g_HugetlbPath, pathlen); memcpy(tempname + pathlen, "/7z-XXXXXX", 11); fd = mkstemp(tempname); unlink(tempname); if (fd < 0) { fprintf(stderr,"cant't open %s (%s)\n",tempname,strerror(errno)); break; } address = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (address == MAP_FAILED) { address = NULL; break; } g_HugePageLen[i] = size; g_HugePageAddr[i] = address; // fprintf(stderr,"HUGE[%d]=%ld %p\n",i,(long)size,address); break; } } #ifndef _7ZIP_ST pthread_mutex_unlock(&mutex); #endif return address; #endif } return align_alloc(size); } #else static void *VirtualAlloc(size_t size, int memLargePages ) { return align_alloc(size); } #endif static int VirtualFree(void *address) { #ifdef _7ZIP_LARGE_PAGES #ifdef __linux__ int i; for (i = 0; i < _7ZIP_MAX_HUGE_ALLOCS; ++i) { if (g_HugePageAddr[i] == address) { munmap(address, g_HugePageLen[i]); g_HugePageAddr[i] = NULL; return 1; } } #endif #endif align_free(address); return 1; } #endif void *MidAlloc(size_t size) { if (size == 0) return 0; #ifdef _SZ_ALLOC_DEBUG fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); #endif return VirtualAlloc(size, 0); } void MidFree(void *address) { #ifdef _SZ_ALLOC_DEBUG if (address != 0) fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); #endif if (address == 0) return; VirtualFree(address); } #ifdef _7ZIP_LARGE_PAGES size_t g_LargePageSize = 0; #ifdef _WIN32 typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); #elif defined(__linux__) size_t largePageMinimum() { size_t size; g_HugetlbPath = getenv("HUGETLB_PATH"); if (g_HugetlbPath == NULL) { // not defined => try to find out the directory static char dir_hugetlbfs[1024]; const char * filename = "/etc/mtab"; // mounted filesystems FILE *fp; struct mntent * info; dir_hugetlbfs[0]=0; fp = setmntent(filename,"r"); if (fp) { info = getmntent(fp); while(info) { /* printf("%s:\n",info->mnt_fsname); printf(" dir='%s'\n",info->mnt_dir); printf(" type='%s'\n",info->mnt_type); */ if (strcmp(info->mnt_type,"hugetlbfs") == 0) { strcpy(dir_hugetlbfs,info->mnt_dir); break; } info = getmntent(fp); } endmntent(fp); } if (dir_hugetlbfs[0]) { g_HugetlbPath = dir_hugetlbfs; // fprintf(stderr," Found hugetlbfs = '%s'\n",g_HugetlbPath); } } if (g_HugetlbPath == NULL || (size = pathconf(g_HugetlbPath, _PC_REC_MIN_XFER_SIZE)) <= getpagesize()) return 0; return size; } #else #define largePageMinimum() 0 #endif #endif void SetLargePageSize() { #ifdef _7ZIP_LARGE_PAGES size_t size; #ifdef _WIN32 GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); if (largePageMinimum == 0) return; #endif size = largePageMinimum(); if (size == 0 || (size & (size - 1)) != 0) return; g_LargePageSize = size; // fprintf(stderr,"SetLargePageSize : %ld\n",(long)g_LargePageSize); #endif } void *BigAlloc(size_t size) { if (size == 0) return 0; #ifdef _SZ_ALLOC_DEBUG fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); #endif #ifdef _7ZIP_LARGE_PAGES if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) { void *res = VirtualAlloc( (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), 1); if (res != 0) return res; } #endif return VirtualAlloc(size, 0); } void BigFree(void *address) { #ifdef _SZ_ALLOC_DEBUG if (address != 0) fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); #endif if (address == 0) return; VirtualFree(address); } src/libs/7zip/unix/C/Alloc.h000066400000000000000000000007161325366651500160570ustar00rootroot00000000000000/* Alloc.h -- Memory allocation functions 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __COMMON_ALLOC_H #define __COMMON_ALLOC_H #include #ifdef __cplusplus extern "C" { #endif void *MyAlloc(size_t size); void MyFree(void *address); void SetLargePageSize(); void *MidAlloc(size_t size); void MidFree(void *address); void *BigAlloc(size_t size); void BigFree(void *address); #ifdef __cplusplus } #endif #endif src/libs/7zip/unix/C/Bra.c000066400000000000000000000063071325366651500155260ustar00rootroot00000000000000/* Bra.c -- Converters for RISC code 2010-04-16 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Bra.h" SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 4) return 0; size -= 4; ip += 8; for (i = 0; i <= size; i += 4) { if (data[i + 3] == 0xEB) { UInt32 dest; UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]); src <<= 2; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); dest >>= 2; data[i + 2] = (Byte)(dest >> 16); data[i + 1] = (Byte)(dest >> 8); data[i + 0] = (Byte)dest; } } return i; } SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 4) return 0; size -= 4; ip += 4; for (i = 0; i <= size; i += 2) { if ((data[i + 1] & 0xF8) == 0xF0 && (data[i + 3] & 0xF8) == 0xF8) { UInt32 dest; UInt32 src = (((UInt32)data[i + 1] & 0x7) << 19) | ((UInt32)data[i + 0] << 11) | (((UInt32)data[i + 3] & 0x7) << 8) | (data[i + 2]); src <<= 1; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); dest >>= 1; data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); data[i + 0] = (Byte)(dest >> 11); data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); data[i + 2] = (Byte)dest; i += 2; } } return i; } SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 4) return 0; size -= 4; for (i = 0; i <= size; i += 4) { if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1) { UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) | ((UInt32)data[i + 1] << 16) | ((UInt32)data[i + 2] << 8) | ((UInt32)data[i + 3] & (~3)); UInt32 dest; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); data[i + 1] = (Byte)(dest >> 16); data[i + 2] = (Byte)(dest >> 8); data[i + 3] &= 0x3; data[i + 3] |= dest; } } return i; } SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { UInt32 i; if (size < 4) return 0; size -= 4; for (i = 0; i <= size; i += 4) { if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) || (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)) { UInt32 src = ((UInt32)data[i + 0] << 24) | ((UInt32)data[i + 1] << 16) | ((UInt32)data[i + 2] << 8) | ((UInt32)data[i + 3]); UInt32 dest; src <<= 2; if (encoding) dest = ip + i + src; else dest = src - (ip + i); dest >>= 2; dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; data[i + 0] = (Byte)(dest >> 24); data[i + 1] = (Byte)(dest >> 16); data[i + 2] = (Byte)(dest >> 8); data[i + 3] = (Byte)dest; } } return i; } src/libs/7zip/unix/C/Bra.h000066400000000000000000000035761325366651500155400ustar00rootroot00000000000000/* Bra.h -- Branch converters for executables 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __BRA_H #define __BRA_H #include "7zTypes.h" EXTERN_C_BEGIN /* These functions convert relative addresses to absolute addresses in CALL instructions to increase the compression ratio. In: data - data buffer size - size of data ip - current virtual Instruction Pinter (IP) value state - state variable for x86 converter encoding - 0 (for decoding), 1 (for encoding) Out: state - state variable for x86 converter Returns: The number of processed bytes. If you call these functions with multiple calls, you must start next call with first byte after block of processed bytes. Type Endian Alignment LookAhead x86 little 1 4 ARMT little 2 2 ARM little 4 0 PPC big 4 0 SPARC big 4 0 IA64 little 16 0 size must be >= Alignment + LookAhead, if it's not last block. If (size < Alignment + LookAhead), converter returns 0. Example: UInt32 ip = 0; for () { ; size must be >= Alignment + LookAhead, if it's not last block SizeT processed = Convert(data, size, ip, 1); data += processed; size -= processed; ip += processed; } */ #define x86_Convert_Init(state) { state = 0; } SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); EXTERN_C_END #endif src/libs/7zip/unix/C/Bra86.c000066400000000000000000000034071325366651500157020ustar00rootroot00000000000000/* Bra86.c -- Converter for x86 code (BCJ) 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Bra.h" #define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0) SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) { SizeT pos = 0; UInt32 mask = *state & 7; if (size < 5) return 0; size -= 4; ip += 5; for (;;) { Byte *p = data + pos; const Byte *limit = data + size; for (; p < limit; p++) if ((*p & 0xFE) == 0xE8) break; { SizeT d = (SizeT)(p - data - pos); pos = (SizeT)(p - data); if (p >= limit) { *state = (d > 2 ? 0 : mask >> (unsigned)d); return pos; } if (d > 2) mask = 0; else { mask >>= (unsigned)d; if (mask != 0 && (mask > 4 || mask == 3 || Test86MSByte(p[(mask >> 1) + 1]))) { mask = (mask >> 1) | 4; pos++; continue; } } } if (Test86MSByte(p[4])) { UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); UInt32 cur = ip + (UInt32)pos; pos += 5; if (encoding) v += cur; else v -= cur; if (mask != 0) { unsigned sh = (mask & 6) << 2; if (Test86MSByte((Byte)(v >> sh))) { v ^= (((UInt32)0x100 << sh) - 1); if (encoding) v += cur; else v -= cur; } mask = 0; } p[1] = (Byte)v; p[2] = (Byte)(v >> 8); p[3] = (Byte)(v >> 16); p[4] = (Byte)(0 - ((v >> 24) & 1)); } else { mask = (mask >> 1) | 4; pos++; } } } src/libs/7zip/unix/C/BraIA64.c000066400000000000000000000034071325366651500161100ustar00rootroot00000000000000/* BraIA64.c -- Converter for IA-64 code 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Bra.h" static const Byte kBranchTable[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0 }; SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 16) return 0; size -= 16; for (i = 0; i <= size; i += 16) { UInt32 instrTemplate = data[i] & 0x1F; UInt32 mask = kBranchTable[instrTemplate]; UInt32 bitPos = 5; int slot; for (slot = 0; slot < 3; slot++, bitPos += 41) { UInt32 bytePos, bitRes; UInt64 instruction, instNorm; int j; if (((mask >> slot) & 1) == 0) continue; bytePos = (bitPos >> 3); bitRes = bitPos & 0x7; instruction = 0; for (j = 0; j < 6; j++) instruction += (UInt64)data[i + j + bytePos] << (8 * j); instNorm = instruction >> bitRes; if (((instNorm >> 37) & 0xF) == 0x5 && ((instNorm >> 9) & 0x7) == 0) { UInt32 src = (UInt32)((instNorm >> 13) & 0xFFFFF); UInt32 dest; src |= ((UInt32)(instNorm >> 36) & 1) << 20; src <<= 4; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); dest >>= 4; instNorm &= ~((UInt64)(0x8FFFFF) << 13); instNorm |= ((UInt64)(dest & 0xFFFFF) << 13); instNorm |= ((UInt64)(dest & 0x100000) << (36 - 20)); instruction &= (1 << bitRes) - 1; instruction |= (instNorm << bitRes); for (j = 0; j < 6; j++) data[i + j + bytePos] = (Byte)(instruction >> (8 * j)); } } } return i; } src/libs/7zip/unix/C/C.pri000066400000000000000000000026011325366651500155450ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/C/7zCrc.h \ $$7ZIP_BASE/C/7zTypes.h \ $$7ZIP_BASE/C/7zVersion.h \ $$7ZIP_BASE/C/Alloc.h \ $$7ZIP_BASE/C/Bra.h \ $$7ZIP_BASE/C/Compiler.h \ $$7ZIP_BASE/C/CpuArch.h \ $$7ZIP_BASE/C/Delta.h \ $$7ZIP_BASE/C/LzFind.h \ $$7ZIP_BASE/C/LzFindMt.h \ $$7ZIP_BASE/C/LzHash.h \ $$7ZIP_BASE/C/Lzma2Dec.h \ $$7ZIP_BASE/C/Lzma2Enc.h \ $$7ZIP_BASE/C/LzmaDec.h \ $$7ZIP_BASE/C/LzmaEnc.h \ $$7ZIP_BASE/C/MtCoder.h \ $$7ZIP_BASE/C/Precomp.h \ $$7ZIP_BASE/C/RotateDefs.h \ $$7ZIP_BASE/C/Sha256.h \ $$7ZIP_BASE/C/Threads.h \ $$7ZIP_BASE/C/Xz.h \ $$7ZIP_BASE/C/XzCrc64.h \ $$7ZIP_BASE/C/XzEnc.h SOURCES += $$7ZIP_BASE/C/7zCrc.c \ $$7ZIP_BASE/C/7zCrcOpt.c \ $$7ZIP_BASE/C/7zStream.c \ $$7ZIP_BASE/C/Alloc.c \ $$7ZIP_BASE/C/Bra.c \ $$7ZIP_BASE/C/Bra86.c \ $$7ZIP_BASE/C/BraIA64.c \ $$7ZIP_BASE/C/Delta.c \ $$7ZIP_BASE/C/LzFind.c \ $$7ZIP_BASE/C/LzFindMt.c \ $$7ZIP_BASE/C/Lzma2Dec.c \ $$7ZIP_BASE/C/Lzma2Enc.c \ $$7ZIP_BASE/C/LzmaDec.c \ $$7ZIP_BASE/C/LzmaEnc.c \ $$7ZIP_BASE/C/MtCoder.c \ $$7ZIP_BASE/C/Sha256.c \ $$7ZIP_BASE/C/Threads.c \ $$7ZIP_BASE/C/Xz.c \ $$7ZIP_BASE/C/XzCrc64.c \ $$7ZIP_BASE/C/XzCrc64Opt.c \ $$7ZIP_BASE/C/XzDec.c \ $$7ZIP_BASE/C/XzEnc.c \ $$7ZIP_BASE/C/XzIn.c src/libs/7zip/unix/C/Compiler.h000066400000000000000000000020071325366651500165720ustar00rootroot00000000000000/* Compiler.h -- Compiler ypes 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_COMPILER_H #define __7Z_COMPILER_H #ifdef _MSC_VER #ifdef UNDER_CE #define RPC_NO_WINDOWS_H /* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */ #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int #endif #if _MSC_VER >= 1300 #pragma warning(disable : 4996) // This function or variable may be unsafe #else #pragma warning(disable : 4511) // copy constructor could not be generated #pragma warning(disable : 4512) // assignment operator could not be generated #pragma warning(disable : 4702) // unreachable code #pragma warning(disable : 4710) // not inlined #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information #endif #endif #endif src/libs/7zip/unix/C/CpuArch.h000066400000000000000000000100671325366651500163520ustar00rootroot00000000000000/* CpuArch.h -- CPU specific code 2013-11-12: Igor Pavlov : Public domain */ #ifndef __CPU_ARCH_H #define __CPU_ARCH_H #include "7zTypes.h" EXTERN_C_BEGIN /* MY_CPU_LE means that CPU is LITTLE ENDIAN. If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. */ #if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) || defined(__AMD64__) || defined(__amd64__) #define MY_CPU_AMD64 #endif #if defined(MY_CPU_AMD64) || defined(_M_IA64) #define MY_CPU_64BIT #endif #if defined(_M_IX86) || defined(__i386__) #define MY_CPU_X86 #endif #if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) #define MY_CPU_X86_OR_AMD64 #endif #if defined(MY_CPU_X86) || defined(_M_ARM) #define MY_CPU_32BIT #endif #if defined(_WIN32) && defined(_M_ARM) #define MY_CPU_ARM_LE #endif #if defined(_WIN32) && defined(_M_IA64) #define MY_CPU_IA64_LE #endif #if defined(MY_CPU_X86_OR_AMD64) #define MY_CPU_LE_UNALIGN #endif #if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) #define MY_CPU_LE #endif #if defined(__BIG_ENDIAN__) || defined(__m68k__) || defined(__ARMEB__) || defined(__MIPSEB__) #define MY_CPU_BE #endif #if defined(MY_CPU_LE) && defined(MY_CPU_BE) Stop_Compiling_Bad_Endian #endif #ifdef MY_CPU_LE_UNALIGN #define GetUi16(p) (*(const UInt16 *)(const void *)(p)) #define GetUi32(p) (*(const UInt32 *)(const void *)(p)) #define GetUi64(p) (*(const UInt64 *)(const void *)(p)) #define SetUi16(p, d) *(UInt16 *)(p) = (d); #define SetUi32(p, d) *(UInt32 *)(p) = (d); #define SetUi64(p, d) *(UInt64 *)(p) = (d); #else #define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) #define GetUi32(p) ( \ ((const Byte *)(p))[0] | \ ((UInt32)((const Byte *)(p))[1] << 8) | \ ((UInt32)((const Byte *)(p))[2] << 16) | \ ((UInt32)((const Byte *)(p))[3] << 24)) #define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) #define SetUi16(p, d) { UInt32 _x_ = (d); \ ((Byte *)(p))[0] = (Byte)_x_; \ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } #define SetUi32(p, d) { UInt32 _x_ = (d); \ ((Byte *)(p))[0] = (Byte)_x_; \ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } #define SetUi64(p, d) { UInt64 _x64_ = (d); \ SetUi32(p, (UInt32)_x64_); \ SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } #endif #if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) #include #pragma intrinsic(_byteswap_ulong) #pragma intrinsic(_byteswap_uint64) #define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) #define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) #else #define GetBe32(p) ( \ ((UInt32)((const Byte *)(p))[0] << 24) | \ ((UInt32)((const Byte *)(p))[1] << 16) | \ ((UInt32)((const Byte *)(p))[2] << 8) | \ ((const Byte *)(p))[3] ) #define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) #endif #define GetBe16(p) ((UInt16)(((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1])) #ifdef MY_CPU_X86_OR_AMD64 #ifdef P7ZIP_USE_ASM typedef struct { UInt32 maxFunc; UInt32 vendor[3]; UInt32 ver; UInt32 b; UInt32 c; UInt32 d; } Cx86cpuid; enum { CPU_FIRM_INTEL, CPU_FIRM_AMD, CPU_FIRM_VIA }; Bool x86cpuid_CheckAndRead(Cx86cpuid *p); int x86cpuid_GetFirm(const Cx86cpuid *p); #define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) #define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) #define x86cpuid_GetStepping(p) ((p)->ver & 0xF) Bool CPU_Is_InOrder(); Bool CPU_Is_Aes_Supported(); #endif #endif EXTERN_C_END #endif src/libs/7zip/unix/C/Delta.c000066400000000000000000000024711325366651500160510ustar00rootroot00000000000000/* Delta.c -- Delta converter 2009-05-26 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Delta.h" void Delta_Init(Byte *state) { unsigned i; for (i = 0; i < DELTA_STATE_SIZE; i++) state[i] = 0; } static void MyMemCpy(Byte *dest, const Byte *src, unsigned size) { unsigned i; for (i = 0; i < size; i++) dest[i] = src[i]; } void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size) { Byte buf[DELTA_STATE_SIZE]; unsigned j = 0; MyMemCpy(buf, state, delta); { SizeT i; for (i = 0; i < size;) { for (j = 0; j < delta && i < size; i++, j++) { Byte b = data[i]; data[i] = (Byte)(b - buf[j]); buf[j] = b; } } } if (j == delta) j = 0; MyMemCpy(state, buf + j, delta - j); MyMemCpy(state + delta - j, buf, j); } void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size) { Byte buf[DELTA_STATE_SIZE]; unsigned j = 0; MyMemCpy(buf, state, delta); { SizeT i; for (i = 0; i < size;) { for (j = 0; j < delta && i < size; i++, j++) { buf[j] = data[i] = (Byte)(buf[j] + data[i]); } } } if (j == delta) j = 0; MyMemCpy(state, buf + j, delta - j); MyMemCpy(state + delta - j, buf, j); } src/libs/7zip/unix/C/Delta.h000066400000000000000000000006131325366651500160520ustar00rootroot00000000000000/* Delta.h -- Delta converter 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __DELTA_H #define __DELTA_H #include "7zTypes.h" EXTERN_C_BEGIN #define DELTA_STATE_SIZE 256 void Delta_Init(Byte *state); void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size); void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size); EXTERN_C_END #endif src/libs/7zip/unix/C/LzFind.c000066400000000000000000000500151325366651500162030ustar00rootroot00000000000000/* LzFind.c -- Match finder for LZ algorithms 2009-04-22 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "LzFind.h" #include "LzHash.h" #define kEmptyHashValue 0 #define kMaxValForNormalize ((UInt32)0xFFFFFFFF) #define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ #define kNormalizeMask (~(kNormalizeStepMin - 1)) #define kMaxHistorySize ((UInt32)3 << 30) #define kStartMaxLen 3 static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) { if (!p->directInput) { alloc->Free(alloc, p->bufferBase); p->bufferBase = 0; } } /* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) { UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; if (p->directInput) { p->blockSize = blockSize; return 1; } if (p->bufferBase == 0 || p->blockSize != blockSize) { LzInWindow_Free(p, alloc); p->blockSize = blockSize; p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); } return (p->bufferBase != 0); } Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) { p->posLimit -= subValue; p->pos -= subValue; p->streamPos -= subValue; } static void MatchFinder_ReadBlock(CMatchFinder *p) { if (p->streamEndWasReached || p->result != SZ_OK) return; if (p->directInput) { UInt32 curSize = 0xFFFFFFFF - p->streamPos; if (curSize > p->directInputRem) curSize = (UInt32)p->directInputRem; p->directInputRem -= curSize; p->streamPos += curSize; if (p->directInputRem == 0) p->streamEndWasReached = 1; return; } for (;;) { Byte *dest = p->buffer + (p->streamPos - p->pos); size_t size = (p->bufferBase + p->blockSize - dest); if (size == 0) return; p->result = p->stream->Read(p->stream, dest, &size); if (p->result != SZ_OK) return; if (size == 0) { p->streamEndWasReached = 1; return; } p->streamPos += (UInt32)size; if (p->streamPos - p->pos > p->keepSizeAfter) return; } } void MatchFinder_MoveBlock(CMatchFinder *p) { memmove(p->bufferBase, p->buffer - p->keepSizeBefore, (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); p->buffer = p->bufferBase + p->keepSizeBefore; } int MatchFinder_NeedMove(CMatchFinder *p) { if (p->directInput) return 0; /* if (p->streamEndWasReached) return 0; */ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); } void MatchFinder_ReadIfRequired(CMatchFinder *p) { if (p->streamEndWasReached) return; if (p->keepSizeAfter >= p->streamPos - p->pos) MatchFinder_ReadBlock(p); } static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) { if (MatchFinder_NeedMove(p)) MatchFinder_MoveBlock(p); MatchFinder_ReadBlock(p); } static void MatchFinder_SetDefaultSettings(CMatchFinder *p) { p->cutValue = 32; p->btMode = 1; p->numHashBytes = 4; p->bigHash = 0; } #define kCrcPoly 0xEDB88320 void MatchFinder_Construct(CMatchFinder *p) { UInt32 i; p->bufferBase = 0; p->directInput = 0; p->hash = 0; MatchFinder_SetDefaultSettings(p); for (i = 0; i < 256; i++) { UInt32 r = i; int j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); p->crc[i] = r; } } static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) { alloc->Free(alloc, p->hash); p->hash = 0; } void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) { MatchFinder_FreeThisClassMemory(p, alloc); LzInWindow_Free(p, alloc); } static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) { size_t sizeInBytes = (size_t)num * sizeof(CLzRef); if (sizeInBytes / sizeof(CLzRef) != num) return 0; return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); } int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) { UInt32 sizeReserv; if (historySize > kMaxHistorySize) { MatchFinder_Free(p, alloc); return 0; } sizeReserv = historySize >> 1; if (historySize > ((UInt32)2 << 30)) sizeReserv = historySize >> 2; sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); p->keepSizeBefore = historySize + keepAddBufferBefore + 1; p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ if (LzInWindow_Create(p, sizeReserv, alloc)) { UInt32 newCyclicBufferSize = historySize + 1; UInt32 hs; p->matchMaxLen = matchMaxLen; { p->fixedHashSize = 0; if (p->numHashBytes == 2) hs = (1 << 16) - 1; else { hs = historySize - 1; hs |= (hs >> 1); hs |= (hs >> 2); hs |= (hs >> 4); hs |= (hs >> 8); hs >>= 1; hs |= 0xFFFF; /* don't change it! It's required for Deflate */ if (hs > (1 << 24)) { if (p->numHashBytes == 3) hs = (1 << 24) - 1; else hs >>= 1; } } p->hashMask = hs; hs++; if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; hs += p->fixedHashSize; } { UInt32 prevSize = p->hashSizeSum + p->numSons; UInt32 newSize; p->historySize = historySize; p->hashSizeSum = hs; p->cyclicBufferSize = newCyclicBufferSize; p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); newSize = p->hashSizeSum + p->numSons; if (p->hash != 0 && prevSize == newSize) return 1; MatchFinder_FreeThisClassMemory(p, alloc); p->hash = AllocRefs(newSize, alloc); if (p->hash != 0) { p->son = p->hash + p->hashSizeSum; return 1; } } } MatchFinder_Free(p, alloc); return 0; } static void MatchFinder_SetLimits(CMatchFinder *p) { UInt32 limit = kMaxValForNormalize - p->pos; UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; if (limit2 < limit) limit = limit2; limit2 = p->streamPos - p->pos; if (limit2 <= p->keepSizeAfter) { if (limit2 > 0) limit2 = 1; } else limit2 -= p->keepSizeAfter; if (limit2 < limit) limit = limit2; { UInt32 lenLimit = p->streamPos - p->pos; if (lenLimit > p->matchMaxLen) lenLimit = p->matchMaxLen; p->lenLimit = lenLimit; } p->posLimit = p->pos + limit; } void MatchFinder_Init(CMatchFinder *p) { UInt32 i; for (i = 0; i < p->hashSizeSum; i++) p->hash[i] = kEmptyHashValue; p->cyclicBufferPos = 0; p->buffer = p->bufferBase; p->pos = p->streamPos = p->cyclicBufferSize; p->result = SZ_OK; p->streamEndWasReached = 0; MatchFinder_ReadBlock(p); MatchFinder_SetLimits(p); } static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) { return (p->pos - p->historySize - 1) & kNormalizeMask; } void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) { UInt32 i; for (i = 0; i < numItems; i++) { UInt32 value = items[i]; if (value <= subValue) value = kEmptyHashValue; else value -= subValue; items[i] = value; } } static void MatchFinder_Normalize(CMatchFinder *p) { UInt32 subValue = MatchFinder_GetSubValue(p); MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); MatchFinder_ReduceOffsets(p, subValue); } static void MatchFinder_CheckLimits(CMatchFinder *p) { if (p->pos == kMaxValForNormalize) MatchFinder_Normalize(p); if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) MatchFinder_CheckAndMoveAndRead(p); if (p->cyclicBufferPos == p->cyclicBufferSize) p->cyclicBufferPos = 0; MatchFinder_SetLimits(p); } static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, UInt32 *distances, UInt32 maxLen) { son[_cyclicBufferPos] = curMatch; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) return distances; { const Byte *pb = cur - delta; curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; if (pb[maxLen] == cur[maxLen] && *pb == *cur) { UInt32 len = 0; while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) return distances; } } } } } UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, UInt32 *distances, UInt32 maxLen) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return distances; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { if (++len != lenLimit && pb[len] == cur[len]) while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return distances; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { while (++len != lenLimit) if (pb[len] != cur[len]) break; { if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } #define MOVE_POS \ ++p->cyclicBufferPos; \ p->buffer++; \ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); #define MOVE_POS_RET MOVE_POS return offset; static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } #define GET_MATCHES_HEADER2(minLen, ret_op) \ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ cur = p->buffer; #define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) #define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) #define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue #define GET_MATCHES_FOOTER(offset, maxLen) \ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ distances + offset, maxLen) - distances); MOVE_POS_RET; #define SKIP_FOOTER \ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 1) } UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 2) } static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, delta2, maxLen, offset; GET_MATCHES_HEADER(3) HASH3_CALC; delta2 = p->pos - p->hash[hash2Value]; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; maxLen = 2; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[0] = maxLen; distances[1] = delta2 - 1; offset = 2; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } GET_MATCHES_FOOTER(offset, maxLen) } static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[ hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; GET_MATCHES_FOOTER(offset, maxLen) } static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[ hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances + offset, maxLen) - (distances)); MOVE_POS_RET } UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances, 2) - (distances)); MOVE_POS_RET } static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value; SKIP_HEADER(3) HASH3_CALC; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->pos; p->hash[kFix4HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) { vTable->Init = (Mf_Init_Func)MatchFinder_Init; vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; if (!p->btMode) { vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; } else if (p->numHashBytes == 2) { vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; } else if (p->numHashBytes == 3) { vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; } else { vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; } } src/libs/7zip/unix/C/LzFind.h000066400000000000000000000064441325366651500162170ustar00rootroot00000000000000/* LzFind.h -- Match finder for LZ algorithms 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_H #define __LZ_FIND_H #include "7zTypes.h" EXTERN_C_BEGIN typedef UInt32 CLzRef; typedef struct _CMatchFinder { Byte *buffer; UInt32 pos; UInt32 posLimit; UInt32 streamPos; UInt32 lenLimit; UInt32 cyclicBufferPos; UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ UInt32 matchMaxLen; CLzRef *hash; CLzRef *son; UInt32 hashMask; UInt32 cutValue; Byte *bufferBase; ISeqInStream *stream; int streamEndWasReached; UInt32 blockSize; UInt32 keepSizeBefore; UInt32 keepSizeAfter; UInt32 numHashBytes; int directInput; size_t directInputRem; int btMode; int bigHash; UInt32 historySize; UInt32 fixedHashSize; UInt32 hashSizeSum; UInt32 numSons; SRes result; UInt32 crc[256]; } CMatchFinder; #define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) #define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) #define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) int MatchFinder_NeedMove(CMatchFinder *p); Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); void MatchFinder_MoveBlock(CMatchFinder *p); void MatchFinder_ReadIfRequired(CMatchFinder *p); void MatchFinder_Construct(CMatchFinder *p); /* Conditions: historySize <= 3 GB keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB */ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, UInt32 *distances, UInt32 maxLen); /* Conditions: Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. Mf_GetPointerToCurrentPos_Func's result must be used only before any other function */ typedef void (*Mf_Init_Func)(void *object); typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); typedef void (*Mf_Skip_Func)(void *object, UInt32); typedef struct _IMatchFinder { Mf_Init_Func Init; Mf_GetIndexByte_Func GetIndexByte; Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; Mf_GetMatches_Func GetMatches; Mf_Skip_Func Skip; } IMatchFinder; void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); void MatchFinder_Init(CMatchFinder *p); UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); EXTERN_C_END #endif src/libs/7zip/unix/C/LzFindMt.c000066400000000000000000000546651325366651500165230ustar00rootroot00000000000000/* LzFindMt.c -- multithreaded Match finder for LZ algorithms 2014-12-29 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "LzHash.h" #include "LzFindMt.h" void MtSync_Construct(CMtSync *p) { p->wasCreated = False; p->csWasInitialized = False; p->csWasEntered = False; Thread_Construct(&p->thread); Event_Construct(&p->canStart); Event_Construct(&p->wasStarted); Event_Construct(&p->wasStopped); Semaphore_Construct(&p->freeSemaphore); Semaphore_Construct(&p->filledSemaphore); } void MtSync_GetNextBlock(CMtSync *p) { if (p->needStart) { p->numProcessedBlocks = 1; p->needStart = False; p->stopWriting = False; p->exit = False; Event_Reset(&p->wasStarted); Event_Reset(&p->wasStopped); Event_Set(&p->canStart); Event_Wait(&p->wasStarted); } else { CriticalSection_Leave(&p->cs); p->csWasEntered = False; p->numProcessedBlocks++; Semaphore_Release1(&p->freeSemaphore); } Semaphore_Wait(&p->filledSemaphore); CriticalSection_Enter(&p->cs); p->csWasEntered = True; } /* MtSync_StopWriting must be called if Writing was started */ void MtSync_StopWriting(CMtSync *p) { UInt32 myNumBlocks = p->numProcessedBlocks; if (!Thread_WasCreated(&p->thread) || p->needStart) return; p->stopWriting = True; if (p->csWasEntered) { CriticalSection_Leave(&p->cs); p->csWasEntered = False; } Semaphore_Release1(&p->freeSemaphore); Event_Wait(&p->wasStopped); while (myNumBlocks++ != p->numProcessedBlocks) { Semaphore_Wait(&p->filledSemaphore); Semaphore_Release1(&p->freeSemaphore); } p->needStart = True; } void MtSync_Destruct(CMtSync *p) { if (Thread_WasCreated(&p->thread)) { MtSync_StopWriting(p); p->exit = True; if (p->needStart) Event_Set(&p->canStart); Thread_Wait(&p->thread); Thread_Close(&p->thread); } if (p->csWasInitialized) { CriticalSection_Delete(&p->cs); p->csWasInitialized = False; } Event_Close(&p->canStart); Event_Close(&p->wasStarted); Event_Close(&p->wasStopped); Semaphore_Close(&p->freeSemaphore); Semaphore_Close(&p->filledSemaphore); p->wasCreated = False; } #define RINOK_THREAD(x) { if ((x) != 0) return SZ_ERROR_THREAD; } static SRes MtSync_Create2(CMtSync *p, THREAD_FUNC_TYPE startAddress, void *obj, UInt32 numBlocks) { if (p->wasCreated) return SZ_OK; RINOK_THREAD(CriticalSection_Init(&p->cs)); p->csWasInitialized = True; RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canStart)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStarted)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStopped)); RINOK_THREAD(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks)); RINOK_THREAD(Semaphore_Create(&p->filledSemaphore, 0, numBlocks)); p->needStart = True; RINOK_THREAD(Thread_Create(&p->thread, startAddress, obj)); p->wasCreated = True; return SZ_OK; } static SRes MtSync_Create(CMtSync *p, THREAD_FUNC_TYPE startAddress, void *obj, UInt32 numBlocks) { SRes res = MtSync_Create2(p, startAddress, obj, numBlocks); if (res != SZ_OK) MtSync_Destruct(p); return res; } void MtSync_Init(CMtSync *p) { p->needStart = True; } #define kMtMaxValForNormalize 0xFFFFFFFF #define DEF_GetHeads2(name, v, action) \ static void GetHeads ## name(const Byte *p, UInt32 pos, \ UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc) \ { action; for (; numHeads != 0; numHeads--) { \ const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++; } } #define DEF_GetHeads(name, v) DEF_GetHeads2(name, v, ;) DEF_GetHeads2(2, (p[0] | ((UInt32)p[1] << 8)), hashMask = hashMask; crc = crc; ) DEF_GetHeads(3, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8)) & hashMask) DEF_GetHeads(4, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5)) & hashMask) DEF_GetHeads(4b, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ ((UInt32)p[3] << 16)) & hashMask) /* DEF_GetHeads(5, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5) ^ (crc[p[4]] << 3)) & hashMask) */ void HashThreadFunc(CMatchFinderMt *mt) { CMtSync *p = &mt->hashSync; for (;;) { UInt32 numProcessedBlocks = 0; Event_Wait(&p->canStart); Event_Set(&p->wasStarted); for (;;) { if (p->exit) return; if (p->stopWriting) { p->numProcessedBlocks = numProcessedBlocks; Event_Set(&p->wasStopped); break; } { CMatchFinder *mf = mt->MatchFinder; if (MatchFinder_NeedMove(mf)) { CriticalSection_Enter(&mt->btSync.cs); CriticalSection_Enter(&mt->hashSync.cs); { const Byte *beforePtr = MatchFinder_GetPointerToCurrentPos(mf); const Byte *afterPtr; MatchFinder_MoveBlock(mf); afterPtr = MatchFinder_GetPointerToCurrentPos(mf); mt->pointerToCurPos -= beforePtr - afterPtr; mt->buffer -= beforePtr - afterPtr; } CriticalSection_Leave(&mt->btSync.cs); CriticalSection_Leave(&mt->hashSync.cs); continue; } Semaphore_Wait(&p->freeSemaphore); MatchFinder_ReadIfRequired(mf); if (mf->pos > (kMtMaxValForNormalize - kMtHashBlockSize)) { UInt32 subValue = (mf->pos - mf->historySize - 1); MatchFinder_ReduceOffsets(mf, subValue); MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, mf->hashMask + 1); } { UInt32 *heads = mt->hashBuf + ((numProcessedBlocks++) & kMtHashNumBlocksMask) * kMtHashBlockSize; UInt32 num = mf->streamPos - mf->pos; heads[0] = 2; heads[1] = num; if (num >= mf->numHashBytes) { num = num - mf->numHashBytes + 1; if (num > kMtHashBlockSize - 2) num = kMtHashBlockSize - 2; mt->GetHeadsFunc(mf->buffer, mf->pos, mf->hash + mf->fixedHashSize, mf->hashMask, heads + 2, num, mf->crc); heads[0] += num; } mf->pos += num; mf->buffer += num; } } Semaphore_Release1(&p->filledSemaphore); } } } void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p) { MtSync_GetNextBlock(&p->hashSync); p->hashBufPosLimit = p->hashBufPos = ((p->hashSync.numProcessedBlocks - 1) & kMtHashNumBlocksMask) * kMtHashBlockSize; p->hashBufPosLimit += p->hashBuf[p->hashBufPos++]; p->hashNumAvail = p->hashBuf[p->hashBufPos++]; } #define kEmptyHashValue 0 /* #define MFMT_GM_INLINE */ #ifdef MFMT_GM_INLINE #define NO_INLINE MY_FAST_CALL Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, UInt32 *_distances, UInt32 _maxLen, const UInt32 *hash, Int32 limit, UInt32 size, UInt32 *posRes) { do { UInt32 *distances = _distances + 1; UInt32 curMatch = pos - *hash++; CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; UInt32 cutValue = _cutValue; UInt32 maxLen = _maxLen; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; break; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { if (++len != lenLimit && pb[len] == cur[len]) while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; break; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } pos++; _cyclicBufferPos++; cur++; { UInt32 num = (UInt32)(distances - _distances); *_distances = num - 1; _distances += num; limit -= num; } } while (limit > 0 && --size != 0); *posRes = pos; return limit; } #endif void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) { UInt32 numProcessed = 0; UInt32 curPos = 2; UInt32 limit = kMtBtBlockSize - (p->matchMaxLen * 2); distances[1] = p->hashNumAvail; while (curPos < limit) { if (p->hashBufPos == p->hashBufPosLimit) { MatchFinderMt_GetNextBlock_Hash(p); distances[1] = numProcessed + p->hashNumAvail; if (p->hashNumAvail >= p->numHashBytes) continue; for (; p->hashNumAvail != 0; p->hashNumAvail--) distances[curPos++] = 0; break; } { UInt32 size = p->hashBufPosLimit - p->hashBufPos; UInt32 lenLimit = p->matchMaxLen; UInt32 pos = p->pos; UInt32 cyclicBufferPos = p->cyclicBufferPos; if (lenLimit >= p->hashNumAvail) lenLimit = p->hashNumAvail; { UInt32 size2 = p->hashNumAvail - lenLimit + 1; if (size2 < size) size = size2; size2 = p->cyclicBufferSize - cyclicBufferPos; if (size2 < size) size = size2; } #ifndef MFMT_GM_INLINE while (curPos < limit && size-- != 0) { UInt32 *startDistances = distances + curPos; UInt32 num = (UInt32)(GetMatchesSpec1(lenLimit, pos - p->hashBuf[p->hashBufPos++], pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, startDistances + 1, p->numHashBytes - 1) - startDistances); *startDistances = num - 1; curPos += num; cyclicBufferPos++; pos++; p->buffer++; } #else { UInt32 posRes; curPos = limit - GetMatchesSpecN(lenLimit, pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos) , size, &posRes); p->hashBufPos += posRes - pos; cyclicBufferPos += posRes - pos; p->buffer += posRes - pos; pos = posRes; } #endif numProcessed += pos - p->pos; p->hashNumAvail -= pos - p->pos; p->pos = pos; if (cyclicBufferPos == p->cyclicBufferSize) cyclicBufferPos = 0; p->cyclicBufferPos = cyclicBufferPos; } } distances[0] = curPos; } void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex) { CMtSync *sync = &p->hashSync; if (!sync->needStart) { CriticalSection_Enter(&sync->cs); sync->csWasEntered = True; } BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize); if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize) { UInt32 subValue = p->pos - p->cyclicBufferSize; MatchFinder_Normalize3(subValue, p->son, p->cyclicBufferSize * 2); p->pos -= subValue; } if (!sync->needStart) { CriticalSection_Leave(&sync->cs); sync->csWasEntered = False; } } void BtThreadFunc(CMatchFinderMt *mt) { CMtSync *p = &mt->btSync; for (;;) { UInt32 blockIndex = 0; Event_Wait(&p->canStart); Event_Set(&p->wasStarted); for (;;) { if (p->exit) return; if (p->stopWriting) { p->numProcessedBlocks = blockIndex; MtSync_StopWriting(&mt->hashSync); Event_Set(&p->wasStopped); break; } Semaphore_Wait(&p->freeSemaphore); BtFillBlock(mt, blockIndex++); Semaphore_Release1(&p->filledSemaphore); } } } void MatchFinderMt_Construct(CMatchFinderMt *p) { p->hashBuf = 0; MtSync_Construct(&p->hashSync); MtSync_Construct(&p->btSync); } void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc) { alloc->Free(alloc, p->hashBuf); p->hashBuf = 0; } void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc) { MtSync_Destruct(&p->hashSync); MtSync_Destruct(&p->btSync); MatchFinderMt_FreeMem(p, alloc); } #define kHashBufferSize (kMtHashBlockSize * kMtHashNumBlocks) #define kBtBufferSize (kMtBtBlockSize * kMtBtNumBlocks) static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p); return 0; } static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE BtThreadFunc2(void *p) { Byte allocaDummy[0x180]; allocaDummy[0] = 0; allocaDummy[1] = allocaDummy[0]; BtThreadFunc((CMatchFinderMt *)p); return 0; } SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) { CMatchFinder *mf = p->MatchFinder; p->historySize = historySize; if (kMtBtBlockSize <= matchMaxLen * 4) return SZ_ERROR_PARAM; if (p->hashBuf == 0) { p->hashBuf = (UInt32 *)alloc->Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32)); if (p->hashBuf == 0) return SZ_ERROR_MEM; p->btBuf = p->hashBuf + kHashBufferSize; } keepAddBufferBefore += (kHashBufferSize + kBtBufferSize); keepAddBufferAfter += kMtHashBlockSize; if (!MatchFinder_Create(mf, historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter, alloc)) return SZ_ERROR_MEM; RINOK(MtSync_Create(&p->hashSync, HashThreadFunc2, p, kMtHashNumBlocks)); RINOK(MtSync_Create(&p->btSync, BtThreadFunc2, p, kMtBtNumBlocks)); return SZ_OK; } /* Call it after ReleaseStream / SetStream */ void MatchFinderMt_Init(CMatchFinderMt *p) { CMatchFinder *mf = p->MatchFinder; p->btBufPos = p->btBufPosLimit = 0; p->hashBufPos = p->hashBufPosLimit = 0; MatchFinder_Init(mf); p->pointerToCurPos = MatchFinder_GetPointerToCurrentPos(mf); p->btNumAvailBytes = 0; p->lzPos = p->historySize + 1; p->hash = mf->hash; p->fixedHashSize = mf->fixedHashSize; p->crc = mf->crc; p->son = mf->son; p->matchMaxLen = mf->matchMaxLen; p->numHashBytes = mf->numHashBytes; p->pos = mf->pos; p->buffer = mf->buffer; p->cyclicBufferPos = mf->cyclicBufferPos; p->cyclicBufferSize = mf->cyclicBufferSize; p->cutValue = mf->cutValue; } /* ReleaseStream is required to finish multithreading */ void MatchFinderMt_ReleaseStream(CMatchFinderMt *p) { MtSync_StopWriting(&p->btSync); /* p->MatchFinder->ReleaseStream(); */ } void MatchFinderMt_Normalize(CMatchFinderMt *p) { MatchFinder_Normalize3(p->lzPos - p->historySize - 1, p->hash, p->fixedHashSize); p->lzPos = p->historySize + 1; } void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p) { UInt32 blockIndex; MtSync_GetNextBlock(&p->btSync); blockIndex = ((p->btSync.numProcessedBlocks - 1) & kMtBtNumBlocksMask); p->btBufPosLimit = p->btBufPos = blockIndex * kMtBtBlockSize; p->btBufPosLimit += p->btBuf[p->btBufPos++]; p->btNumAvailBytes = p->btBuf[p->btBufPos++]; if (p->lzPos >= kMtMaxValForNormalize - kMtBtBlockSize) MatchFinderMt_Normalize(p); } const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p) { return p->pointerToCurPos; } #define GET_NEXT_BLOCK_IF_REQUIRED if (p->btBufPos == p->btBufPosLimit) MatchFinderMt_GetNextBlock_Bt(p); UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p) { GET_NEXT_BLOCK_IF_REQUIRED; return p->btNumAvailBytes; } Byte MatchFinderMt_GetIndexByte(CMatchFinderMt *p, Int32 index) { return p->pointerToCurPos[index]; } UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { UInt32 hash2Value, curMatch2; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH2_CALC curMatch2 = hash[hash2Value]; hash[hash2Value] = lzPos; if (curMatch2 >= matchMinPos) if (cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { *distances++ = 2; *distances++ = lzPos - curMatch2 - 1; } return distances; } UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { UInt32 hash2Value, hash3Value, curMatch2, curMatch3; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH3_CALC curMatch2 = hash[ hash2Value]; curMatch3 = hash[kFix3HashSize + hash3Value]; hash[ hash2Value] = hash[kFix3HashSize + hash3Value] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch2 - 1; if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) { distances[0] = 3; return distances + 2; } distances[0] = 2; distances += 2; } if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) { *distances++ = 3; *distances++ = lzPos - curMatch3 - 1; } return distances; } /* UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { UInt32 hash2Value, hash3Value, hash4Value, curMatch2, curMatch3, curMatch4; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH4_CALC curMatch2 = hash[ hash2Value]; curMatch3 = hash[kFix3HashSize + hash3Value]; curMatch4 = hash[kFix4HashSize + hash4Value]; hash[ hash2Value] = hash[kFix3HashSize + hash3Value] = hash[kFix4HashSize + hash4Value] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch2 - 1; if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) { distances[0] = (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3; return distances + 2; } distances[0] = 2; distances += 2; } if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch3 - 1; if (cur[(ptrdiff_t)curMatch3 - lzPos + 3] == cur[3]) { distances[0] = 4; return distances + 2; } distances[0] = 3; distances += 2; } if (curMatch4 >= matchMinPos) if ( cur[(ptrdiff_t)curMatch4 - lzPos] == cur[0] && cur[(ptrdiff_t)curMatch4 - lzPos + 3] == cur[3] ) { *distances++ = 4; *distances++ = lzPos - curMatch4 - 1; } return distances; } */ #define INCREASE_LZ_POS p->lzPos++; p->pointerToCurPos++; UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances) { const UInt32 *btBuf = p->btBuf + p->btBufPos; UInt32 len = *btBuf++; p->btBufPos += 1 + len; p->btNumAvailBytes--; { UInt32 i; for (i = 0; i < len; i += 2) { *distances++ = *btBuf++; *distances++ = *btBuf++; } } INCREASE_LZ_POS return len; } UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) { const UInt32 *btBuf = p->btBuf + p->btBufPos; UInt32 len = *btBuf++; p->btBufPos += 1 + len; if (len == 0) { if (p->btNumAvailBytes-- >= 4) len = (UInt32)(p->MixMatchesFunc(p, p->lzPos - p->historySize, distances) - (distances)); } else { /* Condition: there are matches in btBuf with length < p->numHashBytes */ UInt32 *distances2; p->btNumAvailBytes--; distances2 = p->MixMatchesFunc(p, p->lzPos - btBuf[1], distances); do { *distances2++ = *btBuf++; *distances2++ = *btBuf++; } while ((len -= 2) != 0); len = (UInt32)(distances2 - (distances)); } INCREASE_LZ_POS return len; } #define SKIP_HEADER2_MT do { GET_NEXT_BLOCK_IF_REQUIRED #define SKIP_HEADER_MT(n) SKIP_HEADER2_MT if (p->btNumAvailBytes-- >= (n)) { const Byte *cur = p->pointerToCurPos; UInt32 *hash = p->hash; #define SKIP_FOOTER_MT } INCREASE_LZ_POS p->btBufPos += p->btBuf[p->btBufPos] + 1; } while (--num != 0); void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER2_MT { p->btNumAvailBytes--; SKIP_FOOTER_MT } void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(2) UInt32 hash2Value; MT_HASH2_CALC hash[hash2Value] = p->lzPos; SKIP_FOOTER_MT } void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(3) UInt32 hash2Value, hash3Value; MT_HASH3_CALC hash[kFix3HashSize + hash3Value] = hash[ hash2Value] = p->lzPos; SKIP_FOOTER_MT } /* void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(4) UInt32 hash2Value, hash3Value, hash4Value; MT_HASH4_CALC hash[kFix4HashSize + hash4Value] = hash[kFix3HashSize + hash3Value] = hash[ hash2Value] = p->lzPos; SKIP_FOOTER_MT } */ void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable) { vTable->Init = (Mf_Init_Func)MatchFinderMt_Init; vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinderMt_GetIndexByte; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinderMt_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinderMt_GetPointerToCurrentPos; vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt_GetMatches; switch(p->MatchFinder->numHashBytes) { case 2: p->GetHeadsFunc = GetHeads2; p->MixMatchesFunc = (Mf_Mix_Matches)0; vTable->Skip = (Mf_Skip_Func)MatchFinderMt0_Skip; vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt2_GetMatches; break; case 3: p->GetHeadsFunc = GetHeads3; p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches2; vTable->Skip = (Mf_Skip_Func)MatchFinderMt2_Skip; break; default: /* case 4: */ p->GetHeadsFunc = p->MatchFinder->bigHash ? GetHeads4b : GetHeads4; /* p->GetHeadsFunc = GetHeads4; */ p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches3; vTable->Skip = (Mf_Skip_Func)MatchFinderMt3_Skip; break; /* default: p->GetHeadsFunc = GetHeads5; p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches4; vTable->Skip = (Mf_Skip_Func)MatchFinderMt4_Skip; break; */ } } src/libs/7zip/unix/C/LzFindMt.h000066400000000000000000000047031325366651500165140ustar00rootroot00000000000000/* LzFindMt.h -- multithreaded Match finder for LZ algorithms 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_MT_H #define __LZ_FIND_MT_H #include "LzFind.h" #include "Threads.h" EXTERN_C_BEGIN #define kMtHashBlockSize (1 << 13) #define kMtHashNumBlocks (1 << 3) #define kMtHashNumBlocksMask (kMtHashNumBlocks - 1) #define kMtBtBlockSize (1 << 14) #define kMtBtNumBlocks (1 << 6) #define kMtBtNumBlocksMask (kMtBtNumBlocks - 1) typedef struct _CMtSync { Bool wasCreated; Bool needStart; Bool exit; Bool stopWriting; CThread thread; CAutoResetEvent canStart; CAutoResetEvent wasStarted; CAutoResetEvent wasStopped; CSemaphore freeSemaphore; CSemaphore filledSemaphore; Bool csWasInitialized; Bool csWasEntered; CCriticalSection cs; UInt32 numProcessedBlocks; } CMtSync; typedef UInt32 * (*Mf_Mix_Matches)(void *p, UInt32 matchMinPos, UInt32 *distances); /* kMtCacheLineDummy must be >= size_of_CPU_cache_line */ #define kMtCacheLineDummy 128 typedef void (*Mf_GetHeads)(const Byte *buffer, UInt32 pos, UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc); typedef struct _CMatchFinderMt { /* LZ */ const Byte *pointerToCurPos; UInt32 *btBuf; UInt32 btBufPos; UInt32 btBufPosLimit; UInt32 lzPos; UInt32 btNumAvailBytes; UInt32 *hash; UInt32 fixedHashSize; UInt32 historySize; const UInt32 *crc; Mf_Mix_Matches MixMatchesFunc; /* LZ + BT */ CMtSync btSync; Byte btDummy[kMtCacheLineDummy]; /* BT */ UInt32 *hashBuf; UInt32 hashBufPos; UInt32 hashBufPosLimit; UInt32 hashNumAvail; CLzRef *son; UInt32 matchMaxLen; UInt32 numHashBytes; UInt32 pos; Byte *buffer; UInt32 cyclicBufferPos; UInt32 cyclicBufferSize; /* it must be historySize + 1 */ UInt32 cutValue; /* BT + Hash */ CMtSync hashSync; /* Byte hashDummy[kMtCacheLineDummy]; */ /* Hash */ Mf_GetHeads GetHeadsFunc; CMatchFinder *MatchFinder; } CMatchFinderMt; void MatchFinderMt_Construct(CMatchFinderMt *p); void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc); SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable); void MatchFinderMt_ReleaseStream(CMatchFinderMt *p); EXTERN_C_END #endif src/libs/7zip/unix/C/LzHash.h000066400000000000000000000037421325366651500162200ustar00rootroot00000000000000/* LzHash.h -- HASH functions for LZ algorithms 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __LZ_HASH_H #define __LZ_HASH_H #define kHash2Size (1 << 10) #define kHash3Size (1 << 16) #define kHash4Size (1 << 20) #define kFix3HashSize (kHash2Size) #define kFix4HashSize (kHash2Size + kHash3Size) #define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) #define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); #define HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } #define HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } #define HASH5_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ hash4Value &= (kHash4Size - 1); } /* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ #define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; #define MT_HASH2_CALC \ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); #define MT_HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } #define MT_HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } #endif src/libs/7zip/unix/C/Lzma2Dec.c000066400000000000000000000236271325366651500164270ustar00rootroot00000000000000/* Lzma2Dec.c -- LZMA2 Decoder 2010-12-15 : Igor Pavlov : Public domain */ /* #define SHOW_DEBUG_INFO */ #include "Precomp.h" #ifdef SHOW_DEBUG_INFO #include #endif #include #include "Lzma2Dec.h" /* 00000000 - EOS 00000001 U U - Uncompressed Reset Dic 00000010 U U - Uncompressed No Reset 100uuuuu U U P P - LZMA no reset 101uuuuu U U P P - LZMA reset state 110uuuuu U U P P S - LZMA reset state + new prop 111uuuuu U U P P S - LZMA reset state + new prop + reset dic u, U - Unpack Size P - Pack Size S - Props */ #define LZMA2_CONTROL_LZMA (1 << 7) #define LZMA2_CONTROL_COPY_NO_RESET 2 #define LZMA2_CONTROL_COPY_RESET_DIC 1 #define LZMA2_CONTROL_EOF 0 #define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0) #define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3) #define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2) #define LZMA2_LCLP_MAX 4 #define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) #ifdef SHOW_DEBUG_INFO #define PRF(x) x #else #define PRF(x) #endif typedef enum { LZMA2_STATE_CONTROL, LZMA2_STATE_UNPACK0, LZMA2_STATE_UNPACK1, LZMA2_STATE_PACK0, LZMA2_STATE_PACK1, LZMA2_STATE_PROP, LZMA2_STATE_DATA, LZMA2_STATE_DATA_CONT, LZMA2_STATE_FINISHED, LZMA2_STATE_ERROR } ELzma2State; static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props) { UInt32 dicSize; if (prop > 40) return SZ_ERROR_UNSUPPORTED; dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop); props[0] = (Byte)LZMA2_LCLP_MAX; props[1] = (Byte)(dicSize); props[2] = (Byte)(dicSize >> 8); props[3] = (Byte)(dicSize >> 16); props[4] = (Byte)(dicSize >> 24); return SZ_OK; } SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) { Byte props[LZMA_PROPS_SIZE]; RINOK(Lzma2Dec_GetOldProps(prop, props)); return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc); } SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) { Byte props[LZMA_PROPS_SIZE]; RINOK(Lzma2Dec_GetOldProps(prop, props)); return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc); } void Lzma2Dec_Init(CLzma2Dec *p) { p->state = LZMA2_STATE_CONTROL; p->needInitDic = True; p->needInitState = True; p->needInitProp = True; LzmaDec_Init(&p->decoder); } static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) { switch(p->state) { case LZMA2_STATE_CONTROL: p->control = b; PRF(printf("\n %4X ", p->decoder.dicPos)); PRF(printf(" %2X", b)); if (p->control == 0) return LZMA2_STATE_FINISHED; if (LZMA2_IS_UNCOMPRESSED_STATE(p)) { if ((p->control & 0x7F) > 2) return LZMA2_STATE_ERROR; p->unpackSize = 0; } else p->unpackSize = (UInt32)(p->control & 0x1F) << 16; return LZMA2_STATE_UNPACK0; case LZMA2_STATE_UNPACK0: p->unpackSize |= (UInt32)b << 8; return LZMA2_STATE_UNPACK1; case LZMA2_STATE_UNPACK1: p->unpackSize |= (UInt32)b; p->unpackSize++; PRF(printf(" %8d", p->unpackSize)); return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0; case LZMA2_STATE_PACK0: p->packSize = (UInt32)b << 8; return LZMA2_STATE_PACK1; case LZMA2_STATE_PACK1: p->packSize |= (UInt32)b; p->packSize++; PRF(printf(" %8d", p->packSize)); return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP: (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA); case LZMA2_STATE_PROP: { int lc, lp; if (b >= (9 * 5 * 5)) return LZMA2_STATE_ERROR; lc = b % 9; b /= 9; p->decoder.prop.pb = b / 5; lp = b % 5; if (lc + lp > LZMA2_LCLP_MAX) return LZMA2_STATE_ERROR; p->decoder.prop.lc = lc; p->decoder.prop.lp = lp; p->needInitProp = False; return LZMA2_STATE_DATA; } } return LZMA2_STATE_ERROR; } static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size) { memcpy(p->dic + p->dicPos, src, size); p->dicPos += size; if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size) p->checkDicSize = p->prop.dicSize; p->processedPos += (UInt32)size; } void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState); SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; *srcLen = 0; *status = LZMA_STATUS_NOT_SPECIFIED; while (p->state != LZMA2_STATE_FINISHED) { SizeT dicPos = p->decoder.dicPos; if (p->state == LZMA2_STATE_ERROR) return SZ_ERROR_DATA; if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_OK; } if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT) { if (*srcLen == inSize) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } (*srcLen)++; p->state = Lzma2Dec_UpdateState(p, *src++); continue; } { SizeT destSizeCur = dicLimit - dicPos; SizeT srcSizeCur = inSize - *srcLen; ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY; if (p->unpackSize <= destSizeCur) { destSizeCur = (SizeT)p->unpackSize; curFinishMode = LZMA_FINISH_END; } if (LZMA2_IS_UNCOMPRESSED_STATE(p)) { if (*srcLen == inSize) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (p->state == LZMA2_STATE_DATA) { Bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC); if (initDic) p->needInitProp = p->needInitState = True; else if (p->needInitDic) return SZ_ERROR_DATA; p->needInitDic = False; LzmaDec_InitDicAndState(&p->decoder, initDic, False); } if (srcSizeCur > destSizeCur) srcSizeCur = destSizeCur; if (srcSizeCur == 0) return SZ_ERROR_DATA; LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur); src += srcSizeCur; *srcLen += srcSizeCur; p->unpackSize -= (UInt32)srcSizeCur; p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT; } else { SizeT outSizeProcessed; SRes res; if (p->state == LZMA2_STATE_DATA) { int mode = LZMA2_GET_LZMA_MODE(p); Bool initDic = (mode == 3); Bool initState = (mode > 0); if ((!initDic && p->needInitDic) || (!initState && p->needInitState)) return SZ_ERROR_DATA; LzmaDec_InitDicAndState(&p->decoder, initDic, initState); p->needInitDic = False; p->needInitState = False; p->state = LZMA2_STATE_DATA_CONT; } if (srcSizeCur > p->packSize) srcSizeCur = (SizeT)p->packSize; res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status); src += srcSizeCur; *srcLen += srcSizeCur; p->packSize -= (UInt32)srcSizeCur; outSizeProcessed = p->decoder.dicPos - dicPos; p->unpackSize -= (UInt32)outSizeProcessed; RINOK(res); if (*status == LZMA_STATUS_NEEDS_MORE_INPUT) return res; if (srcSizeCur == 0 && outSizeProcessed == 0) { if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK || p->unpackSize != 0 || p->packSize != 0) return SZ_ERROR_DATA; p->state = LZMA2_STATE_CONTROL; } if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) *status = LZMA_STATUS_NOT_FINISHED; } } } *status = LZMA_STATUS_FINISHED_WITH_MARK; return SZ_OK; } SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen, inSize = *srcLen; *srcLen = *destLen = 0; for (;;) { SizeT srcSizeCur = inSize, outSizeCur, dicPos; ELzmaFinishMode curFinishMode; SRes res; if (p->decoder.dicPos == p->decoder.dicBufSize) p->decoder.dicPos = 0; dicPos = p->decoder.dicPos; if (outSize > p->decoder.dicBufSize - dicPos) { outSizeCur = p->decoder.dicBufSize; curFinishMode = LZMA_FINISH_ANY; } else { outSizeCur = dicPos + outSize; curFinishMode = finishMode; } res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status); src += srcSizeCur; inSize -= srcSizeCur; *srcLen += srcSizeCur; outSizeCur = p->decoder.dicPos - dicPos; memcpy(dest, p->decoder.dic + dicPos, outSizeCur); dest += outSizeCur; outSize -= outSizeCur; *destLen += outSizeCur; if (res != 0) return res; if (outSizeCur == 0 || outSize == 0) return SZ_OK; } } SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) { CLzma2Dec p; SRes res; SizeT outSize = *destLen, inSize = *srcLen; *destLen = *srcLen = 0; *status = LZMA_STATUS_NOT_SPECIFIED; Lzma2Dec_Construct(&p); RINOK(Lzma2Dec_AllocateProbs(&p, prop, alloc)); p.decoder.dic = dest; p.decoder.dicBufSize = outSize; Lzma2Dec_Init(&p); *srcLen = inSize; res = Lzma2Dec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); *destLen = p.decoder.dicPos; if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) res = SZ_ERROR_INPUT_EOF; Lzma2Dec_FreeProbs(&p, alloc); return res; } src/libs/7zip/unix/C/Lzma2Dec.h000066400000000000000000000043031325366651500164220ustar00rootroot00000000000000/* Lzma2Dec.h -- LZMA2 Decoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA2_DEC_H #define __LZMA2_DEC_H #include "LzmaDec.h" EXTERN_C_BEGIN /* ---------- State Interface ---------- */ typedef struct { CLzmaDec decoder; UInt32 packSize; UInt32 unpackSize; int state; Byte control; Bool needInitDic; Bool needInitState; Bool needInitProp; } CLzma2Dec; #define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder) #define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc); #define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc); SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); void Lzma2Dec_Init(CLzma2Dec *p); /* finishMode: It has meaning only if the decoding reaches output limit (*destLen or dicLimit). LZMA_FINISH_ANY - use smallest number of input bytes LZMA_FINISH_END - read EndOfStream marker after decoding Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_NEEDS_MORE_INPUT SZ_ERROR_DATA - Data error */ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- One Call Interface ---------- */ /* finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - use smallest number of input bytes LZMA_FINISH_END - read EndOfStream marker after decoding Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED SZ_ERROR_DATA - Data error SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). */ SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); EXTERN_C_END #endif src/libs/7zip/unix/C/Lzma2Enc.c000066400000000000000000000304521325366651500164330ustar00rootroot00000000000000/* Lzma2Enc.c -- LZMA2 Encoder 2012-06-19 : Igor Pavlov : Public domain */ #include "Precomp.h" /* #include */ #include /* #define _7ZIP_ST */ #include "Lzma2Enc.h" #ifndef _7ZIP_ST #include "MtCoder.h" #else #define NUM_MT_CODER_THREADS_MAX 1 #endif #define LZMA2_CONTROL_LZMA (1 << 7) #define LZMA2_CONTROL_COPY_NO_RESET 2 #define LZMA2_CONTROL_COPY_RESET_DIC 1 #define LZMA2_CONTROL_EOF 0 #define LZMA2_LCLP_MAX 4 #define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) #define LZMA2_PACK_SIZE_MAX (1 << 16) #define LZMA2_COPY_CHUNK_SIZE LZMA2_PACK_SIZE_MAX #define LZMA2_UNPACK_SIZE_MAX (1 << 21) #define LZMA2_KEEP_WINDOW_SIZE LZMA2_UNPACK_SIZE_MAX #define LZMA2_CHUNK_SIZE_COMPRESSED_MAX ((1 << 16) + 16) #define PRF(x) /* x */ /* ---------- CLzma2EncInt ---------- */ typedef struct { CLzmaEncHandle enc; UInt64 srcPos; Byte props; Bool needInitState; Bool needInitProp; } CLzma2EncInt; static SRes Lzma2EncInt_Init(CLzma2EncInt *p, const CLzma2EncProps *props) { Byte propsEncoded[LZMA_PROPS_SIZE]; SizeT propsSize = LZMA_PROPS_SIZE; RINOK(LzmaEnc_SetProps(p->enc, &props->lzmaProps)); RINOK(LzmaEnc_WriteProperties(p->enc, propsEncoded, &propsSize)); p->srcPos = 0; p->props = propsEncoded[0]; p->needInitState = True; p->needInitProp = True; return SZ_OK; } SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize); const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp); void LzmaEnc_Finish(CLzmaEncHandle pp); void LzmaEnc_SaveState(CLzmaEncHandle pp); void LzmaEnc_RestoreState(CLzmaEncHandle pp); static SRes Lzma2EncInt_EncodeSubblock(CLzma2EncInt *p, Byte *outBuf, size_t *packSizeRes, ISeqOutStream *outStream) { size_t packSizeLimit = *packSizeRes; size_t packSize = packSizeLimit; UInt32 unpackSize = LZMA2_UNPACK_SIZE_MAX; unsigned lzHeaderSize = 5 + (p->needInitProp ? 1 : 0); Bool useCopyBlock; SRes res; *packSizeRes = 0; if (packSize < lzHeaderSize) return SZ_ERROR_OUTPUT_EOF; packSize -= lzHeaderSize; LzmaEnc_SaveState(p->enc); res = LzmaEnc_CodeOneMemBlock(p->enc, p->needInitState, outBuf + lzHeaderSize, &packSize, LZMA2_PACK_SIZE_MAX, &unpackSize); PRF(printf("\npackSize = %7d unpackSize = %7d ", packSize, unpackSize)); if (unpackSize == 0) return res; if (res == SZ_OK) useCopyBlock = (packSize + 2 >= unpackSize || packSize > (1 << 16)); else { if (res != SZ_ERROR_OUTPUT_EOF) return res; res = SZ_OK; useCopyBlock = True; } if (useCopyBlock) { size_t destPos = 0; PRF(printf("################# COPY ")); while (unpackSize > 0) { UInt32 u = (unpackSize < LZMA2_COPY_CHUNK_SIZE) ? unpackSize : LZMA2_COPY_CHUNK_SIZE; if (packSizeLimit - destPos < u + 3) return SZ_ERROR_OUTPUT_EOF; outBuf[destPos++] = (Byte)(p->srcPos == 0 ? LZMA2_CONTROL_COPY_RESET_DIC : LZMA2_CONTROL_COPY_NO_RESET); outBuf[destPos++] = (Byte)((u - 1) >> 8); outBuf[destPos++] = (Byte)(u - 1); memcpy(outBuf + destPos, LzmaEnc_GetCurBuf(p->enc) - unpackSize, u); unpackSize -= u; destPos += u; p->srcPos += u; if (outStream) { *packSizeRes += destPos; if (outStream->Write(outStream, outBuf, destPos) != destPos) return SZ_ERROR_WRITE; destPos = 0; } else *packSizeRes = destPos; /* needInitState = True; */ } LzmaEnc_RestoreState(p->enc); return SZ_OK; } { size_t destPos = 0; UInt32 u = unpackSize - 1; UInt32 pm = (UInt32)(packSize - 1); unsigned mode = (p->srcPos == 0) ? 3 : (p->needInitState ? (p->needInitProp ? 2 : 1) : 0); PRF(printf(" ")); outBuf[destPos++] = (Byte)(LZMA2_CONTROL_LZMA | (mode << 5) | ((u >> 16) & 0x1F)); outBuf[destPos++] = (Byte)(u >> 8); outBuf[destPos++] = (Byte)u; outBuf[destPos++] = (Byte)(pm >> 8); outBuf[destPos++] = (Byte)pm; if (p->needInitProp) outBuf[destPos++] = p->props; p->needInitProp = False; p->needInitState = False; destPos += packSize; p->srcPos += unpackSize; if (outStream) if (outStream->Write(outStream, outBuf, destPos) != destPos) return SZ_ERROR_WRITE; *packSizeRes = destPos; return SZ_OK; } } /* ---------- Lzma2 Props ---------- */ void Lzma2EncProps_Init(CLzma2EncProps *p) { LzmaEncProps_Init(&p->lzmaProps); p->numTotalThreads = -1; p->numBlockThreads = -1; p->blockSize = 0; } void Lzma2EncProps_Normalize(CLzma2EncProps *p) { int t1, t1n, t2, t3; { CLzmaEncProps lzmaProps = p->lzmaProps; LzmaEncProps_Normalize(&lzmaProps); t1n = lzmaProps.numThreads; } t1 = p->lzmaProps.numThreads; t2 = p->numBlockThreads; t3 = p->numTotalThreads; if (t2 > NUM_MT_CODER_THREADS_MAX) t2 = NUM_MT_CODER_THREADS_MAX; if (t3 <= 0) { if (t2 <= 0) t2 = 1; t3 = t1n * t2; } else if (t2 <= 0) { t2 = t3 / t1n; if (t2 == 0) { t1 = 1; t2 = t3; } if (t2 > NUM_MT_CODER_THREADS_MAX) t2 = NUM_MT_CODER_THREADS_MAX; } else if (t1 <= 0) { t1 = t3 / t2; if (t1 == 0) t1 = 1; } else t3 = t1n * t2; p->lzmaProps.numThreads = t1; LzmaEncProps_Normalize(&p->lzmaProps); if (p->blockSize == 0) { UInt32 dictSize = p->lzmaProps.dictSize; UInt64 blockSize = (UInt64)dictSize << 2; const UInt32 kMinSize = (UInt32)1 << 20; const UInt32 kMaxSize = (UInt32)1 << 28; if (blockSize < kMinSize) blockSize = kMinSize; if (blockSize > kMaxSize) blockSize = kMaxSize; if (blockSize < dictSize) blockSize = dictSize; p->blockSize = (size_t)blockSize; } if (t2 > 1) { UInt64 temp = p->lzmaProps.reduceSize + p->blockSize - 1; if (temp > p->lzmaProps.reduceSize) { UInt64 numBlocks = temp / p->blockSize; if (numBlocks < t2) { t2 = (UInt32)numBlocks; t3 = t1 * t2; } } } p->numBlockThreads = t2; p->numTotalThreads = t3; } static SRes Progress(ICompressProgress *p, UInt64 inSize, UInt64 outSize) { return (p && p->Progress(p, inSize, outSize) != SZ_OK) ? SZ_ERROR_PROGRESS : SZ_OK; } /* ---------- Lzma2 ---------- */ typedef struct { Byte propEncoded; CLzma2EncProps props; Byte *outBuf; ISzAlloc *alloc; ISzAlloc *allocBig; CLzma2EncInt coders[NUM_MT_CODER_THREADS_MAX]; #ifndef _7ZIP_ST CMtCoder mtCoder; #endif } CLzma2Enc; /* ---------- Lzma2EncThread ---------- */ static SRes Lzma2Enc_EncodeMt1(CLzma2EncInt *p, CLzma2Enc *mainEncoder, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress) { UInt64 packTotal = 0; SRes res = SZ_OK; if (mainEncoder->outBuf == 0) { mainEncoder->outBuf = (Byte *)IAlloc_Alloc(mainEncoder->alloc, LZMA2_CHUNK_SIZE_COMPRESSED_MAX); if (mainEncoder->outBuf == 0) return SZ_ERROR_MEM; } RINOK(Lzma2EncInt_Init(p, &mainEncoder->props)); RINOK(LzmaEnc_PrepareForLzma2(p->enc, inStream, LZMA2_KEEP_WINDOW_SIZE, mainEncoder->alloc, mainEncoder->allocBig)); for (;;) { size_t packSize = LZMA2_CHUNK_SIZE_COMPRESSED_MAX; res = Lzma2EncInt_EncodeSubblock(p, mainEncoder->outBuf, &packSize, outStream); if (res != SZ_OK) break; packTotal += packSize; res = Progress(progress, p->srcPos, packTotal); if (res != SZ_OK) break; if (packSize == 0) break; } LzmaEnc_Finish(p->enc); if (res == SZ_OK) { Byte b = 0; if (outStream->Write(outStream, &b, 1) != 1) return SZ_ERROR_WRITE; } return res; } #ifndef _7ZIP_ST typedef struct { IMtCoderCallback funcTable; CLzma2Enc *lzma2Enc; } CMtCallbackImp; static SRes MtCallbackImp_Code(void *pp, unsigned index, Byte *dest, size_t *destSize, const Byte *src, size_t srcSize, int finished) { CMtCallbackImp *imp = (CMtCallbackImp *)pp; CLzma2Enc *mainEncoder = imp->lzma2Enc; CLzma2EncInt *p = &mainEncoder->coders[index]; SRes res = SZ_OK; { size_t destLim = *destSize; *destSize = 0; if (srcSize != 0) { RINOK(Lzma2EncInt_Init(p, &mainEncoder->props)); RINOK(LzmaEnc_MemPrepare(p->enc, src, srcSize, LZMA2_KEEP_WINDOW_SIZE, mainEncoder->alloc, mainEncoder->allocBig)); while (p->srcPos < srcSize) { size_t packSize = destLim - *destSize; res = Lzma2EncInt_EncodeSubblock(p, dest + *destSize, &packSize, NULL); if (res != SZ_OK) break; *destSize += packSize; if (packSize == 0) { res = SZ_ERROR_FAIL; break; } if (MtProgress_Set(&mainEncoder->mtCoder.mtProgress, index, p->srcPos, *destSize) != SZ_OK) { res = SZ_ERROR_PROGRESS; break; } } LzmaEnc_Finish(p->enc); if (res != SZ_OK) return res; } if (finished) { if (*destSize == destLim) return SZ_ERROR_OUTPUT_EOF; dest[(*destSize)++] = 0; } } return res; } #endif /* ---------- Lzma2Enc ---------- */ CLzma2EncHandle Lzma2Enc_Create(ISzAlloc *alloc, ISzAlloc *allocBig) { CLzma2Enc *p = (CLzma2Enc *)alloc->Alloc(alloc, sizeof(CLzma2Enc)); if (p == 0) return NULL; Lzma2EncProps_Init(&p->props); Lzma2EncProps_Normalize(&p->props); p->outBuf = 0; p->alloc = alloc; p->allocBig = allocBig; { unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) p->coders[i].enc = 0; } #ifndef _7ZIP_ST MtCoder_Construct(&p->mtCoder); #endif return p; } void Lzma2Enc_Destroy(CLzma2EncHandle pp) { CLzma2Enc *p = (CLzma2Enc *)pp; unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) { CLzma2EncInt *t = &p->coders[i]; if (t->enc) { LzmaEnc_Destroy(t->enc, p->alloc, p->allocBig); t->enc = 0; } } #ifndef _7ZIP_ST MtCoder_Destruct(&p->mtCoder); #endif IAlloc_Free(p->alloc, p->outBuf); IAlloc_Free(p->alloc, pp); } SRes Lzma2Enc_SetProps(CLzma2EncHandle pp, const CLzma2EncProps *props) { CLzma2Enc *p = (CLzma2Enc *)pp; CLzmaEncProps lzmaProps = props->lzmaProps; LzmaEncProps_Normalize(&lzmaProps); if (lzmaProps.lc + lzmaProps.lp > LZMA2_LCLP_MAX) return SZ_ERROR_PARAM; p->props = *props; Lzma2EncProps_Normalize(&p->props); return SZ_OK; } Byte Lzma2Enc_WriteProperties(CLzma2EncHandle pp) { CLzma2Enc *p = (CLzma2Enc *)pp; unsigned i; UInt32 dicSize = LzmaEncProps_GetDictSize(&p->props.lzmaProps); for (i = 0; i < 40; i++) if (dicSize <= LZMA2_DIC_SIZE_FROM_PROP(i)) break; return (Byte)i; } SRes Lzma2Enc_Encode(CLzma2EncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress) { CLzma2Enc *p = (CLzma2Enc *)pp; int i; for (i = 0; i < p->props.numBlockThreads; i++) { CLzma2EncInt *t = &p->coders[i]; if (t->enc == NULL) { t->enc = LzmaEnc_Create(p->alloc); if (t->enc == NULL) return SZ_ERROR_MEM; } } #ifndef _7ZIP_ST if (p->props.numBlockThreads <= 1) #endif return Lzma2Enc_EncodeMt1(&p->coders[0], p, outStream, inStream, progress); #ifndef _7ZIP_ST { CMtCallbackImp mtCallback; mtCallback.funcTable.Code = MtCallbackImp_Code; mtCallback.lzma2Enc = p; p->mtCoder.progress = progress; p->mtCoder.inStream = inStream; p->mtCoder.outStream = outStream; p->mtCoder.alloc = p->alloc; p->mtCoder.mtCallback = &mtCallback.funcTable; p->mtCoder.blockSize = p->props.blockSize; p->mtCoder.destBlockSize = p->props.blockSize + (p->props.blockSize >> 10) + 16; p->mtCoder.numThreads = p->props.numBlockThreads; return MtCoder_Code(&p->mtCoder); } #endif } src/libs/7zip/unix/C/Lzma2Enc.h000066400000000000000000000034431325366651500164400ustar00rootroot00000000000000/* Lzma2Enc.h -- LZMA2 Encoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA2_ENC_H #define __LZMA2_ENC_H #include "LzmaEnc.h" EXTERN_C_BEGIN typedef struct { CLzmaEncProps lzmaProps; size_t blockSize; int numBlockThreads; int numTotalThreads; } CLzma2EncProps; void Lzma2EncProps_Init(CLzma2EncProps *p); void Lzma2EncProps_Normalize(CLzma2EncProps *p); /* ---------- CLzmaEnc2Handle Interface ---------- */ /* Lzma2Enc_* functions can return the following exit codes: Returns: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater in props SZ_ERROR_WRITE - Write callback error SZ_ERROR_PROGRESS - some break from progress callback SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ typedef void * CLzma2EncHandle; CLzma2EncHandle Lzma2Enc_Create(ISzAlloc *alloc, ISzAlloc *allocBig); void Lzma2Enc_Destroy(CLzma2EncHandle p); SRes Lzma2Enc_SetProps(CLzma2EncHandle p, const CLzma2EncProps *props); Byte Lzma2Enc_WriteProperties(CLzma2EncHandle p); SRes Lzma2Enc_Encode(CLzma2EncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress); /* ---------- One Call Interface ---------- */ /* Lzma2Encode Return code: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater SZ_ERROR_OUTPUT_EOF - output buffer overflow SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ /* SRes Lzma2Encode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); */ EXTERN_C_END #endif src/libs/7zip/unix/C/LzmaDec.c000066400000000000000000000703151325366651500163410ustar00rootroot00000000000000/* LzmaDec.c -- LZMA Decoder 2015-01-01 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "LzmaDec.h" #include #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_INIT_SIZE 5 #define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); #define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } #define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) #define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } #define TREE_DECODE(probs, limit, i) \ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } /* #define _LZMA_SIZE_OPT */ #ifdef _LZMA_SIZE_OPT #define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) #else #define TREE_6_DECODE(probs, i) \ { i = 1; \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ i -= 0x40; } #endif #define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) #define MATCHED_LITER_DEC \ matchByte <<= 1; \ bit = (matchByte & offs); \ probLit = prob + offs + bit + symbol; \ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0_CHECK range = bound; #define UPDATE_1_CHECK range -= bound; code -= bound; #define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ { UPDATE_0_CHECK; i = (i + i); A0; } else \ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } #define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) #define TREE_DECODE_CHECK(probs, limit, i) \ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } #define kNumPosBitsMax 4 #define kNumPosStatesMax (1 << kNumPosBitsMax) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define LenChoice 0 #define LenChoice2 (LenChoice + 1) #define LenLow (LenChoice2 + 1) #define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) #define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) #define kNumLenProbs (LenHigh + kLenNumHighSymbols) #define kNumStates 12 #define kNumLitStates 7 #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #define kNumPosSlotBits 6 #define kNumLenToPosStates 4 #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kMatchMinLen 2 #define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define IsMatch 0 #define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) #define IsRepG0 (IsRep + kNumStates) #define IsRepG1 (IsRepG0 + kNumStates) #define IsRepG2 (IsRepG1 + kNumStates) #define IsRep0Long (IsRepG2 + kNumStates) #define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) #define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) #define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) #define LenCoder (Align + kAlignTableSize) #define RepLenCoder (LenCoder + kNumLenProbs) #define Literal (RepLenCoder + kNumLenProbs) #define LZMA_BASE_SIZE 1846 #define LZMA_LIT_SIZE 768 #define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) #if Literal != LZMA_BASE_SIZE StopCompilingDueBUG #endif #define LZMA_DIC_MIN (1 << 12) /* First LZMA-symbol is always decoded. And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization Out: Result: SZ_OK - OK SZ_ERROR_DATA - Error p->remainLen: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished = kMatchSpecLenStart + 1 : Flush marker = kMatchSpecLenStart + 2 : State Init Marker */ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { CLzmaProb *probs = p->probs; unsigned state = p->state; UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; unsigned lc = p->prop.lc; Byte *dic = p->dic; SizeT dicBufSize = p->dicBufSize; SizeT dicPos = p->dicPos; UInt32 processedPos = p->processedPos; UInt32 checkDicSize = p->checkDicSize; unsigned len = 0; const Byte *buf = p->buf; UInt32 range = p->range; UInt32 code = p->code; do { CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = processedPos & pbMask; prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { unsigned symbol; UPDATE_0(prob); prob = probs + Literal; if (checkDicSize != 0 || processedPos != 0) prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); if (state < kNumLitStates) { state -= (state < 4) ? state : 3; symbol = 1; #ifdef _LZMA_SIZE_OPT do { NORMAL_LITER_DEC } while (symbol < 0x100); #else NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC #endif } else { unsigned matchByte = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; unsigned offs = 0x100; state -= (state < 10) ? 3 : 6; symbol = 1; #ifdef _LZMA_SIZE_OPT do { unsigned bit; CLzmaProb *probLit; MATCHED_LITER_DEC } while (symbol < 0x100); #else { unsigned bit; CLzmaProb *probLit; MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC } #endif } dic[dicPos++] = (Byte)symbol; processedPos++; continue; } else { UPDATE_1(prob); prob = probs + IsRep + state; IF_BIT_0(prob) { UPDATE_0(prob); state += kNumStates; prob = probs + LenCoder; } else { UPDATE_1(prob); if (checkDicSize == 0 && processedPos == 0) return SZ_ERROR_DATA; prob = probs + IsRepG0 + state; IF_BIT_0(prob) { UPDATE_0(prob); prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { UPDATE_0(prob); dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; processedPos++; state = state < kNumLitStates ? 9 : 11; continue; } UPDATE_1(prob); } else { UInt32 distance; UPDATE_1(prob); prob = probs + IsRepG1 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep1; } else { UPDATE_1(prob); prob = probs + IsRepG2 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep2; } else { UPDATE_1(prob); distance = rep3; rep3 = rep2; } rep2 = rep1; } rep1 = rep0; rep0 = distance; } state = state < kNumLitStates ? 8 : 11; prob = probs + RepLenCoder; } { unsigned limit, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = (1 << kLenNumMidBits); } else { UPDATE_1(probLen); probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = (1 << kLenNumHighBits); } } TREE_DECODE(probLen, limit, len); len += offset; } if (state >= kNumStates) { UInt32 distance; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_6_DECODE(prob, distance); if (distance >= kStartPosModelIndex) { unsigned posSlot = (unsigned)distance; int numDirectBits = (int)(((distance >> 1) - 1)); distance = (2 | (distance & 1)); if (posSlot < kEndPosModelIndex) { distance <<= numDirectBits; prob = probs + SpecPos + distance - posSlot - 1; { UInt32 mask = 1; unsigned i = 1; do { GET_BIT2(prob + i, i, ; , distance |= mask); mask <<= 1; } while (--numDirectBits != 0); } } else { numDirectBits -= kNumAlignBits; do { NORMALIZE range >>= 1; { UInt32 t; code -= range; t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ distance = (distance << 1) + (t + 1); code += range & t; } /* distance <<= 1; if (code >= range) { code -= range; distance |= 1; } */ } while (--numDirectBits != 0); prob = probs + Align; distance <<= kNumAlignBits; { unsigned i = 1; GET_BIT2(prob + i, i, ; , distance |= 1); GET_BIT2(prob + i, i, ; , distance |= 2); GET_BIT2(prob + i, i, ; , distance |= 4); GET_BIT2(prob + i, i, ; , distance |= 8); } if (distance == (UInt32)0xFFFFFFFF) { len += kMatchSpecLenStart; state -= kNumStates; break; } } } rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance + 1; if (checkDicSize == 0) { if (distance >= processedPos) return SZ_ERROR_DATA; } else if (distance >= checkDicSize) return SZ_ERROR_DATA; state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; } len += kMatchMinLen; if (limit == dicPos) return SZ_ERROR_DATA; { SizeT rem = limit - dicPos; unsigned curLen = ((rem < len) ? (unsigned)rem : len); SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); processedPos += curLen; len -= curLen; if (pos + curLen <= dicBufSize) { Byte *dest = dic + dicPos; ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; const Byte *lim = dest + curLen; dicPos += curLen; do *(dest) = (Byte)*(dest + src); while (++dest != lim); } else { do { dic[dicPos++] = dic[pos]; if (++pos == dicBufSize) pos = 0; } while (--curLen != 0); } } } } while (dicPos < limit && buf < bufLimit); NORMALIZE; p->buf = buf; p->range = range; p->code = code; p->remainLen = len; p->dicPos = dicPos; p->processedPos = processedPos; p->reps[0] = rep0; p->reps[1] = rep1; p->reps[2] = rep2; p->reps[3] = rep3; p->state = state; return SZ_OK; } static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) { if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) { Byte *dic = p->dic; SizeT dicPos = p->dicPos; SizeT dicBufSize = p->dicBufSize; unsigned len = p->remainLen; UInt32 rep0 = p->reps[0]; if (limit - dicPos < len) len = (unsigned)(limit - dicPos); if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) p->checkDicSize = p->prop.dicSize; p->processedPos += len; p->remainLen -= len; while (len != 0) { len--; dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; } p->dicPos = dicPos; } } static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { do { SizeT limit2 = limit; if (p->checkDicSize == 0) { UInt32 rem = p->prop.dicSize - p->processedPos; if (limit - p->dicPos > rem) limit2 = p->dicPos + rem; } RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); if (p->processedPos >= p->prop.dicSize) p->checkDicSize = p->prop.dicSize; LzmaDec_WriteRem(p, limit); } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); if (p->remainLen > kMatchSpecLenStart) { p->remainLen = kMatchSpecLenStart; } return 0; } typedef enum { DUMMY_ERROR, /* unexpected end of input stream */ DUMMY_LIT, DUMMY_MATCH, DUMMY_REP } ELzmaDummy; static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) { UInt32 range = p->range; UInt32 code = p->code; const Byte *bufLimit = buf + inSize; CLzmaProb *probs = p->probs; unsigned state = p->state; ELzmaDummy res; { CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ prob = probs + Literal; if (p->checkDicSize != 0 || p->processedPos != 0) prob += (LZMA_LIT_SIZE * ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); if (state < kNumLitStates) { unsigned symbol = 1; do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); } else { unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; unsigned offs = 0x100; unsigned symbol = 1; do { unsigned bit; CLzmaProb *probLit; matchByte <<= 1; bit = (matchByte & offs); probLit = prob + offs + bit + symbol; GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) } while (symbol < 0x100); } res = DUMMY_LIT; } else { unsigned len; UPDATE_1_CHECK; prob = probs + IsRep + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; state = 0; prob = probs + LenCoder; res = DUMMY_MATCH; } else { UPDATE_1_CHECK; res = DUMMY_REP; prob = probs + IsRepG0 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; NORMALIZE_CHECK; return DUMMY_REP; } else { UPDATE_1_CHECK; } } else { UPDATE_1_CHECK; prob = probs + IsRepG1 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; prob = probs + IsRepG2 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; } } } state = kNumStates; prob = probs + RepLenCoder; } { unsigned limit, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = 1 << kLenNumLowBits; } else { UPDATE_1_CHECK; probLen = prob + LenChoice2; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = 1 << kLenNumMidBits; } else { UPDATE_1_CHECK; probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = 1 << kLenNumHighBits; } } TREE_DECODE_CHECK(probLen, limit, len); len += offset; } if (state < 4) { unsigned posSlot; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { int numDirectBits = ((posSlot >> 1) - 1); /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ if (posSlot < kEndPosModelIndex) { prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; } else { numDirectBits -= kNumAlignBits; do { NORMALIZE_CHECK range >>= 1; code -= range & (((code - range) >> 31) - 1); /* if (code >= range) code -= range; */ } while (--numDirectBits != 0); prob = probs + Align; numDirectBits = kNumAlignBits; } { unsigned i = 1; do { GET_BIT_CHECK(prob + i, i); } while (--numDirectBits != 0); } } } } } NORMALIZE_CHECK; return res; } static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) { p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); p->range = 0xFFFFFFFF; p->needFlush = 0; } void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) { p->needFlush = 1; p->remainLen = 0; p->tempBufSize = 0; if (initDic) { p->processedPos = 0; p->checkDicSize = 0; p->needInitState = 1; } if (initState) p->needInitState = 1; } void LzmaDec_Init(CLzmaDec *p) { p->dicPos = 0; LzmaDec_InitDicAndState(p, True, True); } static void LzmaDec_InitStateReal(CLzmaDec *p) { UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); UInt32 i; CLzmaProb *probs = p->probs; for (i = 0; i < numProbs; i++) probs[i] = kBitModelTotal >> 1; p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; p->state = 0; p->needInitState = 0; } SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; (*srcLen) = 0; LzmaDec_WriteRem(p, dicLimit); *status = LZMA_STATUS_NOT_SPECIFIED; while (p->remainLen != kMatchSpecLenStart) { int checkEndMarkNow; if (p->needFlush != 0) { for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) p->tempBuf[p->tempBufSize++] = *src++; if (p->tempBufSize < RC_INIT_SIZE) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (p->tempBuf[0] != 0) return SZ_ERROR_DATA; LzmaDec_InitRc(p, p->tempBuf); p->tempBufSize = 0; } checkEndMarkNow = 0; if (p->dicPos >= dicLimit) { if (p->remainLen == 0 && p->code == 0) { *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; return SZ_OK; } if (finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_OK; } if (p->remainLen != 0) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } checkEndMarkNow = 1; } if (p->needInitState) LzmaDec_InitStateReal(p); if (p->tempBufSize == 0) { SizeT processed; const Byte *bufLimit; if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, src, inSize); if (dummyRes == DUMMY_ERROR) { memcpy(p->tempBuf, src, inSize); p->tempBufSize = (unsigned)inSize; (*srcLen) += inSize; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } bufLimit = src; } else bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; p->buf = src; if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) return SZ_ERROR_DATA; processed = (SizeT)(p->buf - src); (*srcLen) += processed; src += processed; inSize -= processed; } else { unsigned rem = p->tempBufSize, lookAhead = 0; while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) p->tempBuf[rem++] = src[lookAhead++]; p->tempBufSize = rem; if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); if (dummyRes == DUMMY_ERROR) { (*srcLen) += lookAhead; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } } p->buf = p->tempBuf; if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) return SZ_ERROR_DATA; lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); (*srcLen) += lookAhead; src += lookAhead; inSize -= lookAhead; p->tempBufSize = 0; } } if (p->code == 0) *status = LZMA_STATUS_FINISHED_WITH_MARK; return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; } SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen; SizeT inSize = *srcLen; *srcLen = *destLen = 0; for (;;) { SizeT inSizeCur = inSize, outSizeCur, dicPos; ELzmaFinishMode curFinishMode; SRes res; if (p->dicPos == p->dicBufSize) p->dicPos = 0; dicPos = p->dicPos; if (outSize > p->dicBufSize - dicPos) { outSizeCur = p->dicBufSize; curFinishMode = LZMA_FINISH_ANY; } else { outSizeCur = dicPos + outSize; curFinishMode = finishMode; } res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); src += inSizeCur; inSize -= inSizeCur; *srcLen += inSizeCur; outSizeCur = p->dicPos - dicPos; memcpy(dest, p->dic + dicPos, outSizeCur); dest += outSizeCur; outSize -= outSizeCur; *destLen += outSizeCur; if (res != 0) return res; if (outSizeCur == 0 || outSize == 0) return SZ_OK; } } void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->probs); p->probs = 0; } static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->dic); p->dic = 0; } void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) { LzmaDec_FreeProbs(p, alloc); LzmaDec_FreeDict(p, alloc); } SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) { UInt32 dicSize; Byte d; if (size < LZMA_PROPS_SIZE) return SZ_ERROR_UNSUPPORTED; else dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); if (dicSize < LZMA_DIC_MIN) dicSize = LZMA_DIC_MIN; p->dicSize = dicSize; d = data[0]; if (d >= (9 * 5 * 5)) return SZ_ERROR_UNSUPPORTED; p->lc = d % 9; d /= 9; p->pb = d / 5; p->lp = d % 5; return SZ_OK; } static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) { UInt32 numProbs = LzmaProps_GetNumProbs(propNew); if (p->probs == 0 || numProbs != p->numProbs) { LzmaDec_FreeProbs(p, alloc); p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); p->numProbs = numProbs; if (p->probs == 0) return SZ_ERROR_MEM; } return SZ_OK; } SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) { CLzmaProps propNew; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); p->prop = propNew; return SZ_OK; } SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) { CLzmaProps propNew; SizeT dicBufSize; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); dicBufSize = propNew.dicSize; if (p->dic == 0 || dicBufSize != p->dicBufSize) { LzmaDec_FreeDict(p, alloc); p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); if (p->dic == 0) { LzmaDec_FreeProbs(p, alloc); return SZ_ERROR_MEM; } } p->dicBufSize = dicBufSize; p->prop = propNew; return SZ_OK; } SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) { CLzmaDec p; SRes res; SizeT outSize = *destLen, inSize = *srcLen; *destLen = *srcLen = 0; *status = LZMA_STATUS_NOT_SPECIFIED; if (inSize < RC_INIT_SIZE) return SZ_ERROR_INPUT_EOF; LzmaDec_Construct(&p); RINOK(LzmaDec_AllocateProbs(&p, propData, propSize, alloc)); p.dic = dest; p.dicBufSize = outSize; LzmaDec_Init(&p); *srcLen = inSize; res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); *destLen = p.dicPos; if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) res = SZ_ERROR_INPUT_EOF; LzmaDec_FreeProbs(&p, alloc); return res; } src/libs/7zip/unix/C/LzmaDec.h000066400000000000000000000156111325366651500163440ustar00rootroot00000000000000/* LzmaDec.h -- LZMA Decoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA_DEC_H #define __LZMA_DEC_H #include "7zTypes.h" EXTERN_C_BEGIN /* #define _LZMA_PROB32 */ /* _LZMA_PROB32 can increase the speed on some CPUs, but memory usage for CLzmaDec::probs will be doubled in that case */ #ifdef _LZMA_PROB32 #define CLzmaProb UInt32 #else #define CLzmaProb UInt16 #endif /* ---------- LZMA Properties ---------- */ #define LZMA_PROPS_SIZE 5 typedef struct _CLzmaProps { unsigned lc, lp, pb; UInt32 dicSize; } CLzmaProps; /* LzmaProps_Decode - decodes properties Returns: SZ_OK SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); /* ---------- LZMA Decoder state ---------- */ /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ #define LZMA_REQUIRED_INPUT_MAX 20 typedef struct { CLzmaProps prop; CLzmaProb *probs; Byte *dic; const Byte *buf; UInt32 range, code; SizeT dicPos; SizeT dicBufSize; UInt32 processedPos; UInt32 checkDicSize; unsigned state; UInt32 reps[4]; unsigned remainLen; int needFlush; int needInitState; UInt32 numProbs; unsigned tempBufSize; Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; } CLzmaDec; #define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } void LzmaDec_Init(CLzmaDec *p); /* There are two types of LZMA streams: 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ typedef enum { LZMA_FINISH_ANY, /* finish at any point */ LZMA_FINISH_END /* block must be finished at the end */ } ELzmaFinishMode; /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! You must use LZMA_FINISH_END, when you know that current output buffer covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, and output value of destLen will be less than output buffer size limit. You can check status result also. You can use multiple checks to test data integrity after full decompression: 1) Check Result and "status" variable. 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. You must use correct finish mode in that case. */ typedef enum { LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ } ELzmaStatus; /* ELzmaStatus is used only as output value for function call */ /* ---------- Interfaces ---------- */ /* There are 3 levels of interfaces: 1) Dictionary Interface 2) Buffer Interface 3) One Call Interface You can select any of these interfaces, but don't mix functions from different groups for same object. */ /* There are two variants to allocate state for Dictionary Interface: 1) LzmaDec_Allocate / LzmaDec_Free 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs You can use variant 2, if you set dictionary buffer manually. For Buffer Interface you must always use variant 1. LzmaDec_Allocate* can return: SZ_OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); /* ---------- Dictionary Interface ---------- */ /* You can use it, if you want to eliminate the overhead for data copying from dictionary to some other external buffer. You must work with CLzmaDec variables directly in this interface. STEPS: LzmaDec_Constr() LzmaDec_Allocate() for (each new stream) { LzmaDec_Init() while (it needs more decompression) { LzmaDec_DecodeToDic() use data from CLzmaDec::dic and update CLzmaDec::dicPos } } LzmaDec_Free() */ /* LzmaDec_DecodeToDic The decoding to internal dictionary buffer (CLzmaDec::dic). You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! finishMode: It has meaning only if the decoding reaches output limit (dicLimit). LZMA_FINISH_ANY - Decode just dicLimit bytes. LZMA_FINISH_END - Stream must be finished after dicLimit. Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_NEEDS_MORE_INPUT LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error */ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- Buffer Interface ---------- */ /* It's zlib-like interface. See LzmaDec_DecodeToDic description for information about STEPS and return results, but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need to work with CLzmaDec variables manually. finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). */ SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- One Call Interface ---------- */ /* LzmaDecode finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). */ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); EXTERN_C_END #endif src/libs/7zip/unix/C/LzmaEnc.c000066400000000000000000001767161325366651500163670ustar00rootroot00000000000000/* LzmaEnc.c -- LZMA Encoder 2014-12-29 : Igor Pavlov : Public domain */ #include "Precomp.h" #include /* #define SHOW_STAT */ /* #define SHOW_STAT2 */ #if defined(SHOW_STAT) || defined(SHOW_STAT2) #include #endif #include "LzmaEnc.h" #include "LzFind.h" #ifndef _7ZIP_ST #include "LzFindMt.h" #endif #ifdef SHOW_STAT static unsigned g_STAT_OFFSET = 0; #endif #define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) #define kBlockSize (9 << 10) #define kUnpackBlockSize (1 << 18) #define kMatchArraySize (1 << 21) #define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) #define kNumMaxDirectBits (31) #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define kProbInitValue (kBitModelTotal >> 1) #define kNumMoveReducingBits 4 #define kNumBitPriceShiftBits 4 #define kBitPrice (1 << kNumBitPriceShiftBits) void LzmaEncProps_Init(CLzmaEncProps *p) { p->level = 5; p->dictSize = p->mc = 0; p->reduceSize = (UInt64)(Int64)-1; p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; p->writeEndMark = 0; } void LzmaEncProps_Normalize(CLzmaEncProps *p) { int level = p->level; if (level < 0) level = 5; p->level = level; if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); if (p->dictSize > p->reduceSize) { unsigned i; for (i = 11; i <= 30; i++) { if ((UInt32)p->reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } if ((UInt32)p->reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } } } if (p->lc < 0) p->lc = 3; if (p->lp < 0) p->lp = 0; if (p->pb < 0) p->pb = 2; if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); if (p->numHashBytes < 0) p->numHashBytes = 4; if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); if (p->numThreads < 0) p->numThreads = #ifndef _7ZIP_ST ((p->btMode && p->algo) ? 2 : 1); #else 1; #endif } UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) { CLzmaEncProps props = *props2; LzmaEncProps_Normalize(&props); return props.dictSize; } /* #define LZMA_LOG_BSR */ /* Define it for Intel's CPU */ #ifdef LZMA_LOG_BSR #define kDicLogSizeMaxCompress 30 #define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } UInt32 GetPosSlot1(UInt32 pos) { UInt32 res; BSR2_RET(pos, res); return res; } #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } #else #define kNumLogBits (9 + (int)sizeof(size_t) / 2) #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) void LzmaEnc_FastPosInit(Byte *g_FastPos) { int c = 2, slotFast; g_FastPos[0] = 0; g_FastPos[1] = 1; for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) { UInt32 k = (1 << ((slotFast >> 1) - 1)); UInt32 j; for (j = 0; j < k; j++, c++) g_FastPos[c] = (Byte)slotFast; } } #define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ res = p->g_FastPos[pos >> i] + (i * 2); } /* #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ p->g_FastPos[pos >> 6] + 12 : \ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } */ #define GetPosSlot1(pos) p->g_FastPos[pos] #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } #endif #define LZMA_NUM_REPS 4 typedef unsigned CState; typedef struct { UInt32 price; CState state; int prev1IsChar; int prev2; UInt32 posPrev2; UInt32 backPrev2; UInt32 posPrev; UInt32 backPrev; UInt32 backs[LZMA_NUM_REPS]; } COptimal; #define kNumOpts (1 << 12) #define kNumLenToPosStates 4 #define kNumPosSlotBits 6 #define kDicLogSizeMin 0 #define kDicLogSizeMax 32 #define kDistTableSizeMax (kDicLogSizeMax * 2) #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kAlignMask (kAlignTableSize - 1) #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #ifdef _LZMA_PROB32 #define CLzmaProb UInt32 #else #define CLzmaProb UInt16 #endif #define LZMA_PB_MAX 4 #define LZMA_LC_MAX 8 #define LZMA_LP_MAX 4 #define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define LZMA_MATCH_LEN_MIN 2 #define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) #define kNumStates 12 typedef struct { CLzmaProb choice; CLzmaProb choice2; CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; CLzmaProb high[kLenNumHighSymbols]; } CLenEnc; typedef struct { CLenEnc p; UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; UInt32 tableSize; UInt32 counters[LZMA_NUM_PB_STATES_MAX]; } CLenPriceEnc; typedef struct { UInt32 range; Byte cache; UInt64 low; UInt64 cacheSize; Byte *buf; Byte *bufLim; Byte *bufBase; ISeqOutStream *outStream; UInt64 processed; SRes res; } CRangeEnc; typedef struct { CLzmaProb *litProbs; CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; UInt32 reps[LZMA_NUM_REPS]; UInt32 state; } CSaveState; typedef struct { IMatchFinder matchFinder; void *matchFinderObj; #ifndef _7ZIP_ST Bool mtMode; CMatchFinderMt matchFinderMt; #endif CMatchFinder matchFinderBase; #ifndef _7ZIP_ST Byte pad[128]; #endif UInt32 optimumEndIndex; UInt32 optimumCurrentIndex; UInt32 longestMatchLength; UInt32 numPairs; UInt32 numAvail; COptimal opt[kNumOpts]; #ifndef LZMA_LOG_BSR Byte g_FastPos[1 << kNumLogBits]; #endif UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; UInt32 numFastBytes; UInt32 additionalOffset; UInt32 reps[LZMA_NUM_REPS]; UInt32 state; UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; UInt32 alignPrices[kAlignTableSize]; UInt32 alignPriceCount; UInt32 distTableSize; unsigned lc, lp, pb; unsigned lpMask, pbMask; CLzmaProb *litProbs; CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; unsigned lclp; Bool fastMode; CRangeEnc rc; Bool writeEndMark; UInt64 nowPos64; UInt32 matchPriceCount; Bool finished; Bool multiThread; SRes result; UInt32 dictSize; int needInit; CSaveState saveState; } CLzmaEnc; void LzmaEnc_SaveState(CLzmaEncHandle pp) { CLzmaEnc *p = (CLzmaEnc *)pp; CSaveState *dest = &p->saveState; int i; dest->lenEnc = p->lenEnc; dest->repLenEnc = p->repLenEnc; dest->state = p->state; for (i = 0; i < kNumStates; i++) { memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); } for (i = 0; i < kNumLenToPosStates; i++) memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); } void LzmaEnc_RestoreState(CLzmaEncHandle pp) { CLzmaEnc *dest = (CLzmaEnc *)pp; const CSaveState *p = &dest->saveState; int i; dest->lenEnc = p->lenEnc; dest->repLenEnc = p->repLenEnc; dest->state = p->state; for (i = 0; i < kNumStates; i++) { memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); } for (i = 0; i < kNumLenToPosStates; i++) memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); } SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) { CLzmaEnc *p = (CLzmaEnc *)pp; CLzmaEncProps props = *props2; LzmaEncProps_Normalize(&props); if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || props.dictSize > ((UInt32)1 << kDicLogSizeMaxCompress) || props.dictSize > ((UInt32)1 << 30)) return SZ_ERROR_PARAM; p->dictSize = props.dictSize; { unsigned fb = props.fb; if (fb < 5) fb = 5; if (fb > LZMA_MATCH_LEN_MAX) fb = LZMA_MATCH_LEN_MAX; p->numFastBytes = fb; } p->lc = props.lc; p->lp = props.lp; p->pb = props.pb; p->fastMode = (props.algo == 0); p->matchFinderBase.btMode = props.btMode; { UInt32 numHashBytes = 4; if (props.btMode) { if (props.numHashBytes < 2) numHashBytes = 2; else if (props.numHashBytes < 4) numHashBytes = props.numHashBytes; } p->matchFinderBase.numHashBytes = numHashBytes; } p->matchFinderBase.cutValue = props.mc; p->writeEndMark = props.writeEndMark; #ifndef _7ZIP_ST /* if (newMultiThread != _multiThread) { ReleaseMatchFinder(); _multiThread = newMultiThread; } */ p->multiThread = (props.numThreads > 1); #endif return SZ_OK; } static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; #define IsCharState(s) ((s) < 7) #define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) #define kInfinityPrice (1 << 30) static void RangeEnc_Construct(CRangeEnc *p) { p->outStream = 0; p->bufBase = 0; } #define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) #define RC_BUF_SIZE (1 << 16) static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) { if (p->bufBase == 0) { p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); if (p->bufBase == 0) return 0; p->bufLim = p->bufBase + RC_BUF_SIZE; } return 1; } static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->bufBase); p->bufBase = 0; } static void RangeEnc_Init(CRangeEnc *p) { /* Stream.Init(); */ p->low = 0; p->range = 0xFFFFFFFF; p->cacheSize = 1; p->cache = 0; p->buf = p->bufBase; p->processed = 0; p->res = SZ_OK; } static void RangeEnc_FlushStream(CRangeEnc *p) { size_t num; if (p->res != SZ_OK) return; num = p->buf - p->bufBase; if (num != p->outStream->Write(p->outStream, p->bufBase, num)) p->res = SZ_ERROR_WRITE; p->processed += num; p->buf = p->bufBase; } static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) { if ((UInt32)p->low < (UInt32)0xFF000000 || (unsigned)(p->low >> 32) != 0) { Byte temp = p->cache; do { Byte *buf = p->buf; *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); p->buf = buf; if (buf == p->bufLim) RangeEnc_FlushStream(p); temp = 0xFF; } while (--p->cacheSize != 0); p->cache = (Byte)((UInt32)p->low >> 24); } p->cacheSize++; p->low = (UInt32)p->low << 8; } static void RangeEnc_FlushData(CRangeEnc *p) { int i; for (i = 0; i < 5; i++) RangeEnc_ShiftLow(p); } static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, unsigned numBits) { do { p->range >>= 1; p->low += p->range & (0 - ((value >> --numBits) & 1)); if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } while (numBits != 0); } static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) { UInt32 ttt = *prob; UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; if (symbol == 0) { p->range = newBound; ttt += (kBitModelTotal - ttt) >> kNumMoveBits; } else { p->low += newBound; p->range -= newBound; ttt -= ttt >> kNumMoveBits; } *prob = (CLzmaProb)ttt; if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) { symbol |= 0x100; do { RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); } static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) { UInt32 offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); } void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) { UInt32 i; for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) { const int kCyclesBits = kNumBitPriceShiftBits; UInt32 w = i; UInt32 bitCount = 0; int j; for (j = 0; j < kCyclesBits; j++) { w = w * w; bitCount <<= 1; while (w >= ((UInt32)1 << 16)) { w >>= 1; bitCount++; } } ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); } } #define GET_PRICE(prob, symbol) \ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICEa(prob, symbol) \ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] #define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; symbol |= 0x100; do { price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); return price; } static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) { UInt32 price = 0; UInt32 offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); return price; } static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) { UInt32 m = 1; int i; for (i = numBitLevels; i != 0;) { UInt32 bit; i--; bit = (symbol >> i) & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; } } static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) { UInt32 m = 1; int i; for (i = 0; i < numBitLevels; i++) { UInt32 bit = symbol & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; symbol >>= 1; } } static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; symbol |= (1 << numBitLevels); while (symbol != 1) { price += GET_PRICEa(probs[symbol >> 1], symbol & 1); symbol >>= 1; } return price; } static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; UInt32 m = 1; int i; for (i = numBitLevels; i != 0; i--) { UInt32 bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[m], bit); m = (m << 1) | bit; } return price; } static void LenEnc_Init(CLenEnc *p) { unsigned i; p->choice = p->choice2 = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) p->low[i] = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) p->mid[i] = kProbInitValue; for (i = 0; i < kLenNumHighSymbols; i++) p->high[i] = kProbInitValue; } static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) { if (symbol < kLenNumLowSymbols) { RangeEnc_EncodeBit(rc, &p->choice, 0); RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); } else { RangeEnc_EncodeBit(rc, &p->choice, 1); if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) { RangeEnc_EncodeBit(rc, &p->choice2, 0); RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); } else { RangeEnc_EncodeBit(rc, &p->choice2, 1); RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); } } } static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) { UInt32 a0 = GET_PRICE_0a(p->choice); UInt32 a1 = GET_PRICE_1a(p->choice); UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); UInt32 i = 0; for (i = 0; i < kLenNumLowSymbols; i++) { if (i >= numSymbols) return; prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); } for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) { if (i >= numSymbols) return; prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); } for (; i < numSymbols; i++) prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); } static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) { LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); p->counters[posState] = p->tableSize; } static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) { UInt32 posState; for (posState = 0; posState < numPosStates; posState++) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) { LenEnc_Encode(&p->p, rc, symbol, posState); if (updatePrice) if (--p->counters[posState] == 0) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void MovePos(CLzmaEnc *p, UInt32 num) { #ifdef SHOW_STAT g_STAT_OFFSET += num; printf("\n MovePos %d", num); #endif if (num != 0) { p->additionalOffset += num; p->matchFinder.Skip(p->matchFinderObj, num); } } static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) { UInt32 lenRes = 0, numPairs; p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); #ifdef SHOW_STAT printf("\n i = %d numPairs = %d ", g_STAT_OFFSET, numPairs / 2); g_STAT_OFFSET++; { UInt32 i; for (i = 0; i < numPairs; i += 2) printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); } #endif if (numPairs > 0) { lenRes = p->matches[numPairs - 2]; if (lenRes == p->numFastBytes) { const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; UInt32 distance = p->matches[numPairs - 1] + 1; UInt32 numAvail = p->numAvail; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; { const Byte *pby2 = pby - distance; for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); } } } p->additionalOffset++; *numDistancePairsRes = numPairs; return lenRes; } #define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; #define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; #define IsShortRep(p) ((p)->backPrev == 0) static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) { return GET_PRICE_0(p->isRepG0[state]) + GET_PRICE_0(p->isRep0Long[state][posState]); } static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) { UInt32 price; if (repIndex == 0) { price = GET_PRICE_0(p->isRepG0[state]); price += GET_PRICE_1(p->isRep0Long[state][posState]); } else { price = GET_PRICE_1(p->isRepG0[state]); if (repIndex == 1) price += GET_PRICE_0(p->isRepG1[state]); else { price += GET_PRICE_1(p->isRepG1[state]); price += GET_PRICE(p->isRepG2[state], repIndex - 2); } } return price; } static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) { return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + GetPureRepPrice(p, repIndex, state, posState); } static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) { UInt32 posMem = p->opt[cur].posPrev; UInt32 backMem = p->opt[cur].backPrev; p->optimumEndIndex = cur; do { if (p->opt[cur].prev1IsChar) { MakeAsChar(&p->opt[posMem]) p->opt[posMem].posPrev = posMem - 1; if (p->opt[cur].prev2) { p->opt[posMem - 1].prev1IsChar = False; p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; } } { UInt32 posPrev = posMem; UInt32 backCur = backMem; backMem = p->opt[posPrev].backPrev; posMem = p->opt[posPrev].posPrev; p->opt[posPrev].backPrev = backCur; p->opt[posPrev].posPrev = cur; cur = posPrev; } } while (cur != 0); *backRes = p->opt[0].backPrev; p->optimumCurrentIndex = p->opt[0].posPrev; return p->optimumCurrentIndex; } #define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) { UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; UInt32 matchPrice, repMatchPrice, normalMatchPrice; UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; UInt32 *matches; const Byte *data; Byte curByte, matchByte; if (p->optimumEndIndex != p->optimumCurrentIndex) { const COptimal *opt = &p->opt[p->optimumCurrentIndex]; UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; *backRes = opt->backPrev; p->optimumCurrentIndex = opt->posPrev; return lenRes; } p->optimumCurrentIndex = p->optimumEndIndex = 0; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; if (numAvail < 2) { *backRes = (UInt32)(-1); return 1; } if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; repMaxIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 lenTest; const Byte *data2; reps[i] = p->reps[i]; data2 = data - (reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) { repLens[i] = 0; continue; } for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); repLens[i] = lenTest; if (lenTest > repLens[repMaxIndex]) repMaxIndex = i; } if (repLens[repMaxIndex] >= p->numFastBytes) { UInt32 lenRes; *backRes = repMaxIndex; lenRes = repLens[repMaxIndex]; MovePos(p, lenRes - 1); return lenRes; } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } curByte = *data; matchByte = *(data - (reps[0] + 1)); if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) { *backRes = (UInt32)-1; return 1; } p->opt[0].state = (CState)p->state; posState = (position & p->pbMask); { const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + (!IsCharState(p->state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } MakeAsChar(&p->opt[1]); matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); if (matchByte == curByte) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); if (shortRepPrice < p->opt[1].price) { p->opt[1].price = shortRepPrice; MakeAsShortRep(&p->opt[1]); } } lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); if (lenEnd < 2) { *backRes = p->opt[1].backPrev; return 1; } p->opt[1].posPrev = 0; for (i = 0; i < LZMA_NUM_REPS; i++) p->opt[0].backs[i] = reps[i]; len = lenEnd; do p->opt[len--].price = kInfinityPrice; while (len >= 2); for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 repLen = repLens[i]; UInt32 price; if (repLen < 2) continue; price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); do { UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; COptimal *opt = &p->opt[repLen]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = i; opt->prev1IsChar = False; } } while (--repLen >= 2); } normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); if (len <= mainLen) { UInt32 offs = 0; while (len > matches[offs]) offs += 2; for (; ; len++) { COptimal *opt; UInt32 distance = matches[offs + 1]; UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; UInt32 lenToPosState = GetLenToPosState(len); if (distance < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][distance]; else { UInt32 slot; GetPosSlot2(distance, slot); curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; } opt = &p->opt[len]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = distance + LZMA_NUM_REPS; opt->prev1IsChar = False; } if (len == matches[offs]) { offs += 2; if (offs == numPairs) break; } } } cur = 0; #ifdef SHOW_STAT2 if (position >= 0) { unsigned i; printf("\n pos = %4X", position); for (i = cur; i <= lenEnd; i++) printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); } #endif for (;;) { UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; Bool nextIsChar; Byte curByte, matchByte; const Byte *data; COptimal *curOpt; COptimal *nextOpt; cur++; if (cur == lenEnd) return Backward(p, backRes, cur); newLen = ReadMatchDistances(p, &numPairs); if (newLen >= p->numFastBytes) { p->numPairs = numPairs; p->longestMatchLength = newLen; return Backward(p, backRes, cur); } position++; curOpt = &p->opt[cur]; posPrev = curOpt->posPrev; if (curOpt->prev1IsChar) { posPrev--; if (curOpt->prev2) { state = p->opt[curOpt->posPrev2].state; if (curOpt->backPrev2 < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } else state = p->opt[posPrev].state; state = kLiteralNextStates[state]; } else state = p->opt[posPrev].state; if (posPrev == cur - 1) { if (IsShortRep(curOpt)) state = kShortRepNextStates[state]; else state = kLiteralNextStates[state]; } else { UInt32 pos; const COptimal *prevOpt; if (curOpt->prev1IsChar && curOpt->prev2) { posPrev = curOpt->posPrev2; pos = curOpt->backPrev2; state = kRepNextStates[state]; } else { pos = curOpt->backPrev; if (pos < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } prevOpt = &p->opt[posPrev]; if (pos < LZMA_NUM_REPS) { UInt32 i; reps[0] = prevOpt->backs[pos]; for (i = 1; i <= pos; i++) reps[i] = prevOpt->backs[i - 1]; for (; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i]; } else { UInt32 i; reps[0] = (pos - LZMA_NUM_REPS); for (i = 1; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i - 1]; } } curOpt->state = (CState)state; curOpt->backs[0] = reps[0]; curOpt->backs[1] = reps[1]; curOpt->backs[2] = reps[2]; curOpt->backs[3] = reps[3]; curPrice = curOpt->price; nextIsChar = False; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; curByte = *data; matchByte = *(data - (reps[0] + 1)); posState = (position & p->pbMask); curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); { const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); curAnd1Price += (!IsCharState(state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } nextOpt = &p->opt[cur + 1]; if (curAnd1Price < nextOpt->price) { nextOpt->price = curAnd1Price; nextOpt->posPrev = cur; MakeAsChar(nextOpt); nextIsChar = True; } matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); if (shortRepPrice <= nextOpt->price) { nextOpt->price = shortRepPrice; nextOpt->posPrev = cur; MakeAsShortRep(nextOpt); nextIsChar = True; } } numAvailFull = p->numAvail; { UInt32 temp = kNumOpts - 1 - cur; if (temp < numAvailFull) numAvailFull = temp; } if (numAvailFull < 2) continue; numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); if (!nextIsChar && matchByte != curByte) /* speed optimization */ { /* try Literal + rep0 */ UInt32 temp; UInt32 lenTest2; const Byte *data2 = data - (reps[0] + 1); UInt32 limit = p->numFastBytes + 1; if (limit > numAvailFull) limit = numAvailFull; for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); lenTest2 = temp - 1; if (lenTest2 >= 2) { UInt32 state2 = kLiteralNextStates[state]; UInt32 posStateNext = (position + 1) & p->pbMask; UInt32 nextRepMatchPrice = curAnd1Price + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 curAndLenPrice; COptimal *opt; UInt32 offset = cur + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = False; } } } } startLen = 2; /* speed optimization */ { UInt32 repIndex; for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) { UInt32 lenTest; UInt32 lenTestTemp; UInt32 price; const Byte *data2 = data - (reps[repIndex] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); while (lenEnd < cur + lenTest) p->opt[++lenEnd].price = kInfinityPrice; lenTestTemp = lenTest; price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); do { UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; COptimal *opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = repIndex; opt->prev1IsChar = False; } } while (--lenTest >= 2); lenTest = lenTestTemp; if (repIndex == 0) startLen = lenTest + 1; /* if (_maxMode) */ { UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { UInt32 state2 = kRepNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = price + p->repLenEnc.prices[posState][lenTest - 2] + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (position + lenTest + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 curAndLenPrice; COptimal *opt; UInt32 offset = cur + lenTest + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = True; opt->posPrev2 = cur; opt->backPrev2 = repIndex; } } } } } } /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ if (newLen > numAvail) { newLen = numAvail; for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); matches[numPairs] = newLen; numPairs += 2; } if (newLen >= startLen) { UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); UInt32 offs, curBack, posSlot; UInt32 lenTest; while (lenEnd < cur + newLen) p->opt[++lenEnd].price = kInfinityPrice; offs = 0; while (startLen > matches[offs]) offs += 2; curBack = matches[offs + 1]; GetPosSlot2(curBack, posSlot); for (lenTest = /*2*/ startLen; ; lenTest++) { UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; UInt32 lenToPosState = GetLenToPosState(lenTest); COptimal *opt; if (curBack < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; else curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = curBack + LZMA_NUM_REPS; opt->prev1IsChar = False; } if (/*_maxMode && */lenTest == matches[offs]) { /* Try Match + Literal + Rep0 */ const Byte *data2 = data - (curBack + 1); UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { UInt32 state2 = kMatchNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = curAndLenPrice + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (posStateNext + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 offset = cur + lenTest + 1 + lenTest2; UInt32 curAndLenPrice; COptimal *opt; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = True; opt->posPrev2 = cur; opt->backPrev2 = curBack + LZMA_NUM_REPS; } } } offs += 2; if (offs == numPairs) break; curBack = matches[offs + 1]; if (curBack >= kNumFullDistances) GetPosSlot2(curBack, posSlot); } } } } } #define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) { UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; const Byte *data; const UInt32 *matches; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; *backRes = (UInt32)-1; if (numAvail < 2) return 1; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; repLen = repIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len; const Byte *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (len = 2; len < numAvail && data[len] == data2[len]; len++); if (len >= p->numFastBytes) { *backRes = i; MovePos(p, len - 1); return len; } if (len > repLen) { repIndex = i; repLen = len; } } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } mainDist = 0; /* for GCC */ if (mainLen >= 2) { mainDist = matches[numPairs - 1]; while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) { if (!ChangePair(matches[numPairs - 3], mainDist)) break; numPairs -= 2; mainLen = matches[numPairs - 2]; mainDist = matches[numPairs - 1]; } if (mainLen == 2 && mainDist >= 0x80) mainLen = 1; } if (repLen >= 2 && ( (repLen + 1 >= mainLen) || (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) { *backRes = repIndex; MovePos(p, repLen - 1); return repLen; } if (mainLen < 2 || numAvail <= 2) return 1; p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); if (p->longestMatchLength >= 2) { UInt32 newDistance = matches[p->numPairs - 1]; if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || (p->longestMatchLength > mainLen + 1) || (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) return 1; } data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len, limit; const Byte *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; limit = mainLen - 1; for (len = 2; len < limit && data[len] == data2[len]; len++); if (len >= limit) return 1; } *backRes = mainDist + LZMA_NUM_REPS; MovePos(p, mainLen - 2); return mainLen; } static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) { UInt32 len; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; len = LZMA_MATCH_LEN_MIN; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); } static SRes CheckErrors(CLzmaEnc *p) { if (p->result != SZ_OK) return p->result; if (p->rc.res != SZ_OK) p->result = SZ_ERROR_WRITE; if (p->matchFinderBase.result != SZ_OK) p->result = SZ_ERROR_READ; if (p->result != SZ_OK) p->finished = True; return p->result; } static SRes Flush(CLzmaEnc *p, UInt32 nowPos) { /* ReleaseMFStream(); */ p->finished = True; if (p->writeEndMark) WriteEndMarker(p, nowPos & p->pbMask); RangeEnc_FlushData(&p->rc); RangeEnc_FlushStream(&p->rc); return CheckErrors(p); } static void FillAlignPrices(CLzmaEnc *p) { UInt32 i; for (i = 0; i < kAlignTableSize; i++) p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); p->alignPriceCount = 0; } static void FillDistancesPrices(CLzmaEnc *p) { UInt32 tempPrices[kNumFullDistances]; UInt32 i, lenToPosState; for (i = kStartPosModelIndex; i < kNumFullDistances; i++) { UInt32 posSlot = GetPosSlot1(i); UInt32 footerBits = ((posSlot >> 1) - 1); UInt32 base = ((2 | (posSlot & 1)) << footerBits); tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); } for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) { UInt32 posSlot; const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; for (posSlot = 0; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); { UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; UInt32 i; for (i = 0; i < kStartPosModelIndex; i++) distancesPrices[i] = posSlotPrices[i]; for (; i < kNumFullDistances; i++) distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; } } p->matchPriceCount = 0; } void LzmaEnc_Construct(CLzmaEnc *p) { RangeEnc_Construct(&p->rc); MatchFinder_Construct(&p->matchFinderBase); #ifndef _7ZIP_ST MatchFinderMt_Construct(&p->matchFinderMt); p->matchFinderMt.MatchFinder = &p->matchFinderBase; #endif { CLzmaEncProps props; LzmaEncProps_Init(&props); LzmaEnc_SetProps(p, &props); } #ifndef LZMA_LOG_BSR LzmaEnc_FastPosInit(p->g_FastPos); #endif LzmaEnc_InitPriceTables(p->ProbPrices); p->litProbs = 0; p->saveState.litProbs = 0; } CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) { void *p; p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); if (p != 0) LzmaEnc_Construct((CLzmaEnc *)p); return p; } void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->litProbs); alloc->Free(alloc, p->saveState.litProbs); p->litProbs = 0; p->saveState.litProbs = 0; } void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) { #ifndef _7ZIP_ST MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); #endif MatchFinder_Free(&p->matchFinderBase, allocBig); LzmaEnc_FreeLits(p, alloc); RangeEnc_Free(&p->rc, alloc); } void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) { LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); alloc->Free(alloc, p); } static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) { UInt32 nowPos32, startPos32; if (p->needInit) { p->matchFinder.Init(p->matchFinderObj); p->needInit = 0; } if (p->finished) return p->result; RINOK(CheckErrors(p)); nowPos32 = (UInt32)p->nowPos64; startPos32 = nowPos32; if (p->nowPos64 == 0) { UInt32 numPairs; Byte curByte; if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) return Flush(p, nowPos32); ReadMatchDistances(p, &numPairs); RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); p->state = kLiteralNextStates[p->state]; curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); LitEnc_Encode(&p->rc, p->litProbs, curByte); p->additionalOffset--; nowPos32++; } if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) for (;;) { UInt32 pos, len, posState; if (p->fastMode) len = GetOptimumFast(p, &pos); else len = GetOptimum(p, nowPos32, &pos); #ifdef SHOW_STAT2 printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); #endif posState = nowPos32 & p->pbMask; if (len == 1 && pos == (UInt32)-1) { Byte curByte; CLzmaProb *probs; const Byte *data; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; curByte = *data; probs = LIT_PROBS(nowPos32, *(data - 1)); if (IsCharState(p->state)) LitEnc_Encode(&p->rc, probs, curByte); else LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); p->state = kLiteralNextStates[p->state]; } else { RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); if (pos < LZMA_NUM_REPS) { RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); if (pos == 0) { RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); } else { UInt32 distance = p->reps[pos]; RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); if (pos == 1) RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); else { RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); if (pos == 3) p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; } p->reps[1] = p->reps[0]; p->reps[0] = distance; } if (len == 1) p->state = kShortRepNextStates[p->state]; else { LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); p->state = kRepNextStates[p->state]; } } else { UInt32 posSlot; RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); pos -= LZMA_NUM_REPS; GetPosSlot(pos, posSlot); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { UInt32 footerBits = ((posSlot >> 1) - 1); UInt32 base = ((2 | (posSlot & 1)) << footerBits); UInt32 posReduced = pos - base; if (posSlot < kEndPosModelIndex) RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); else { RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); p->alignPriceCount++; } } p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; p->reps[1] = p->reps[0]; p->reps[0] = pos; p->matchPriceCount++; } } p->additionalOffset -= len; nowPos32 += len; if (p->additionalOffset == 0) { UInt32 processed; if (!p->fastMode) { if (p->matchPriceCount >= (1 << 7)) FillDistancesPrices(p); if (p->alignPriceCount >= kAlignTableSize) FillAlignPrices(p); } if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) break; processed = nowPos32 - startPos32; if (useLimits) { if (processed + kNumOpts + 300 >= maxUnpackSize || RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) break; } else if (processed >= (1 << 15)) { p->nowPos64 += nowPos32 - startPos32; return CheckErrors(p); } } } p->nowPos64 += nowPos32 - startPos32; return Flush(p, nowPos32); } #define kBigHashDicLimit ((UInt32)1 << 24) static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 beforeSize = kNumOpts; if (!RangeEnc_Alloc(&p->rc, alloc)) return SZ_ERROR_MEM; #ifndef _7ZIP_ST p->mtMode = (p->multiThread && !p->fastMode && (p->matchFinderBase.btMode != 0)); #endif { unsigned lclp = p->lc + p->lp; if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) { LzmaEnc_FreeLits(p, alloc); p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); if (p->litProbs == 0 || p->saveState.litProbs == 0) { LzmaEnc_FreeLits(p, alloc); return SZ_ERROR_MEM; } p->lclp = lclp; } } p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); if (beforeSize + p->dictSize < keepWindowSize) beforeSize = keepWindowSize - p->dictSize; #ifndef _7ZIP_ST if (p->mtMode) { RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); p->matchFinderObj = &p->matchFinderMt; MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); } else #endif { if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) return SZ_ERROR_MEM; p->matchFinderObj = &p->matchFinderBase; MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); } return SZ_OK; } void LzmaEnc_Init(CLzmaEnc *p) { UInt32 i; p->state = 0; for (i = 0 ; i < LZMA_NUM_REPS; i++) p->reps[i] = 0; RangeEnc_Init(&p->rc); for (i = 0; i < kNumStates; i++) { UInt32 j; for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) { p->isMatch[i][j] = kProbInitValue; p->isRep0Long[i][j] = kProbInitValue; } p->isRep[i] = kProbInitValue; p->isRepG0[i] = kProbInitValue; p->isRepG1[i] = kProbInitValue; p->isRepG2[i] = kProbInitValue; } { UInt32 num = 0x300 << (p->lp + p->lc); for (i = 0; i < num; i++) p->litProbs[i] = kProbInitValue; } { for (i = 0; i < kNumLenToPosStates; i++) { CLzmaProb *probs = p->posSlotEncoder[i]; UInt32 j; for (j = 0; j < (1 << kNumPosSlotBits); j++) probs[j] = kProbInitValue; } } { for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) p->posEncoders[i] = kProbInitValue; } LenEnc_Init(&p->lenEnc.p); LenEnc_Init(&p->repLenEnc.p); for (i = 0; i < (1 << kNumAlignBits); i++) p->posAlignEncoder[i] = kProbInitValue; p->optimumEndIndex = 0; p->optimumCurrentIndex = 0; p->additionalOffset = 0; p->pbMask = (1 << p->pb) - 1; p->lpMask = (1 << p->lp) - 1; } void LzmaEnc_InitPrices(CLzmaEnc *p) { if (!p->fastMode) { FillDistancesPrices(p); FillAlignPrices(p); } p->lenEnc.tableSize = p->repLenEnc.tableSize = p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); } static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 i; for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) if (p->dictSize <= ((UInt32)1 << i)) break; p->distTableSize = i * 2; p->finished = False; p->result = SZ_OK; RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); LzmaEnc_Init(p); LzmaEnc_InitPrices(p); p->nowPos64 = 0; return SZ_OK; } static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; p->needInit = 1; p->rc.outStream = outStream; return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); } SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; p->needInit = 1; return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) { p->matchFinderBase.directInput = 1; p->matchFinderBase.bufferBase = (Byte *)src; p->matchFinderBase.directInputRem = srcLen; } SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; LzmaEnc_SetInputBuf(p, src, srcLen); p->needInit = 1; return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } void LzmaEnc_Finish(CLzmaEncHandle pp) { #ifndef _7ZIP_ST CLzmaEnc *p = (CLzmaEnc *)pp; if (p->mtMode) MatchFinderMt_ReleaseStream(&p->matchFinderMt); #else pp = pp; #endif } typedef struct { ISeqOutStream funcTable; Byte *data; SizeT rem; Bool overflow; } CSeqOutStreamBuf; static size_t MyWrite(void *pp, const void *data, size_t size) { CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; if (p->rem < size) { size = p->rem; p->overflow = True; } memcpy(p->data, data, size); p->rem -= size; p->data += size; return size; } UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); } const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; } SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) { CLzmaEnc *p = (CLzmaEnc *)pp; UInt64 nowPos64; SRes res; CSeqOutStreamBuf outStream; outStream.funcTable.Write = MyWrite; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = False; p->finished = False; p->result = SZ_OK; if (reInit) LzmaEnc_Init(p); LzmaEnc_InitPrices(p); nowPos64 = p->nowPos64; RangeEnc_Init(&p->rc); p->rc.outStream = &outStream.funcTable; res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); *unpackSize = (UInt32)(p->nowPos64 - nowPos64); *destLen -= outStream.rem; if (outStream.overflow) return SZ_ERROR_OUTPUT_EOF; return res; } static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) { SRes res = SZ_OK; #ifndef _7ZIP_ST Byte allocaDummy[0x300]; allocaDummy[0] = 0; allocaDummy[1] = allocaDummy[0]; #endif for (;;) { res = LzmaEnc_CodeOneBlock(p, False, 0, 0); if (res != SZ_OK || p->finished != 0) break; if (progress != 0) { res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); if (res != SZ_OK) { res = SZ_ERROR_PROGRESS; break; } } } LzmaEnc_Finish(p); return res; } SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); } SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) { CLzmaEnc *p = (CLzmaEnc *)pp; int i; UInt32 dictSize = p->dictSize; if (*size < LZMA_PROPS_SIZE) return SZ_ERROR_PARAM; *size = LZMA_PROPS_SIZE; props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); for (i = 11; i <= 30; i++) { if (dictSize <= ((UInt32)2 << i)) { dictSize = (2 << i); break; } if (dictSize <= ((UInt32)3 << i)) { dictSize = (3 << i); break; } } for (i = 0; i < 4; i++) props[1 + i] = (Byte)(dictSize >> (8 * i)); return SZ_OK; } SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { SRes res; CLzmaEnc *p = (CLzmaEnc *)pp; CSeqOutStreamBuf outStream; LzmaEnc_SetInputBuf(p, src, srcLen); outStream.funcTable.Write = MyWrite; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = writeEndMark; p->rc.outStream = &outStream.funcTable; res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); if (res == SZ_OK) res = LzmaEnc_Encode2(p, progress); *destLen -= outStream.rem; if (outStream.overflow) return SZ_ERROR_OUTPUT_EOF; return res; } SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); SRes res; if (p == 0) return SZ_ERROR_MEM; res = LzmaEnc_SetProps(p, props); if (res == SZ_OK) { res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); if (res == SZ_OK) res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, writeEndMark, progress, alloc, allocBig); } LzmaEnc_Destroy(p, alloc, allocBig); return res; } src/libs/7zip/unix/C/LzmaEnc.h000066400000000000000000000060551325366651500163600ustar00rootroot00000000000000/* LzmaEnc.h -- LZMA Encoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA_ENC_H #define __LZMA_ENC_H #include "7zTypes.h" EXTERN_C_BEGIN #define LZMA_PROPS_SIZE 5 typedef struct _CLzmaEncProps { int level; /* 0 <= level <= 9 */ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version (1 << 12) <= dictSize <= (1 << 30) for 64-bit version default = (1 << 24) */ UInt64 reduceSize; /* estimated size of data that will be compressed. default = 0xFFFFFFFF. Encoder uses this value to reduce dictionary size */ int lc; /* 0 <= lc <= 8, default = 3 */ int lp; /* 0 <= lp <= 4, default = 0 */ int pb; /* 0 <= pb <= 4, default = 2 */ int algo; /* 0 - fast, 1 - normal, default = 1 */ int fb; /* 5 <= fb <= 273, default = 32 */ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ int numHashBytes; /* 2, 3 or 4, default = 4 */ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ int numThreads; /* 1 or 2, default = 2 */ } CLzmaEncProps; void LzmaEncProps_Init(CLzmaEncProps *p); void LzmaEncProps_Normalize(CLzmaEncProps *p); UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); /* ---------- CLzmaEncHandle Interface ---------- */ /* LzmaEnc_* functions can return the following exit codes: Returns: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater in props SZ_ERROR_WRITE - Write callback error. SZ_ERROR_PROGRESS - some break from progress callback SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ typedef void * CLzmaEncHandle; CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); /* ---------- One Call Interface ---------- */ /* LzmaEncode Return code: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater SZ_ERROR_OUTPUT_EOF - output buffer overflow SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); EXTERN_C_END #endif src/libs/7zip/unix/C/MtCoder.c000066400000000000000000000202161325366651500163520ustar00rootroot00000000000000/* MtCoder.c -- Multi-thread Coder 2010-09-24 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "MtCoder.h" void LoopThread_Construct(CLoopThread *p) { Thread_Construct(&p->thread); Event_Construct(&p->startEvent); Event_Construct(&p->finishedEvent); } void LoopThread_Close(CLoopThread *p) { Thread_Close(&p->thread); Event_Close(&p->startEvent); Event_Close(&p->finishedEvent); } static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE LoopThreadFunc(void *pp) { CLoopThread *p = (CLoopThread *)pp; for (;;) { if (Event_Wait(&p->startEvent) != 0) return SZ_ERROR_THREAD; if (p->stop) return 0; p->res = p->func(p->param); if (Event_Set(&p->finishedEvent) != 0) return SZ_ERROR_THREAD; } } WRes LoopThread_Create(CLoopThread *p) { p->stop = 0; RINOK(AutoResetEvent_CreateNotSignaled(&p->startEvent)); RINOK(AutoResetEvent_CreateNotSignaled(&p->finishedEvent)); return Thread_Create(&p->thread, LoopThreadFunc, p); } WRes LoopThread_StopAndWait(CLoopThread *p) { p->stop = 1; if (Event_Set(&p->startEvent) != 0) return SZ_ERROR_THREAD; return Thread_Wait(&p->thread); } WRes LoopThread_StartSubThread(CLoopThread *p) { return Event_Set(&p->startEvent); } WRes LoopThread_WaitSubThread(CLoopThread *p) { return Event_Wait(&p->finishedEvent); } static SRes Progress(ICompressProgress *p, UInt64 inSize, UInt64 outSize) { return (p && p->Progress(p, inSize, outSize) != SZ_OK) ? SZ_ERROR_PROGRESS : SZ_OK; } static void MtProgress_Init(CMtProgress *p, ICompressProgress *progress) { unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) p->inSizes[i] = p->outSizes[i] = 0; p->totalInSize = p->totalOutSize = 0; p->progress = progress; p->res = SZ_OK; } static void MtProgress_Reinit(CMtProgress *p, unsigned index) { p->inSizes[index] = 0; p->outSizes[index] = 0; } #define UPDATE_PROGRESS(size, prev, total) \ if (size != (UInt64)(Int64)-1) { total += size - prev; prev = size; } SRes MtProgress_Set(CMtProgress *p, unsigned index, UInt64 inSize, UInt64 outSize) { SRes res; CriticalSection_Enter(&p->cs); UPDATE_PROGRESS(inSize, p->inSizes[index], p->totalInSize) UPDATE_PROGRESS(outSize, p->outSizes[index], p->totalOutSize) if (p->res == SZ_OK) p->res = Progress(p->progress, p->totalInSize, p->totalOutSize); res = p->res; CriticalSection_Leave(&p->cs); return res; } static void MtProgress_SetError(CMtProgress *p, SRes res) { CriticalSection_Enter(&p->cs); if (p->res == SZ_OK) p->res = res; CriticalSection_Leave(&p->cs); } static void MtCoder_SetError(CMtCoder* p, SRes res) { CriticalSection_Enter(&p->cs); if (p->res == SZ_OK) p->res = res; CriticalSection_Leave(&p->cs); } /* ---------- MtThread ---------- */ void CMtThread_Construct(CMtThread *p, CMtCoder *mtCoder) { p->mtCoder = mtCoder; p->outBuf = 0; p->inBuf = 0; Event_Construct(&p->canRead); Event_Construct(&p->canWrite); LoopThread_Construct(&p->thread); } #define RINOK_THREAD(x) { if((x) != 0) return SZ_ERROR_THREAD; } static void CMtThread_CloseEvents(CMtThread *p) { Event_Close(&p->canRead); Event_Close(&p->canWrite); } static void CMtThread_Destruct(CMtThread *p) { CMtThread_CloseEvents(p); if (Thread_WasCreated(&p->thread.thread)) { LoopThread_StopAndWait(&p->thread); LoopThread_Close(&p->thread); } if (p->mtCoder->alloc) IAlloc_Free(p->mtCoder->alloc, p->outBuf); p->outBuf = 0; if (p->mtCoder->alloc) IAlloc_Free(p->mtCoder->alloc, p->inBuf); p->inBuf = 0; } #define MY_BUF_ALLOC(buf, size, newSize) \ if (buf == 0 || size != newSize) \ { IAlloc_Free(p->mtCoder->alloc, buf); \ size = newSize; buf = (Byte *)IAlloc_Alloc(p->mtCoder->alloc, size); \ if (buf == 0) return SZ_ERROR_MEM; } static SRes CMtThread_Prepare(CMtThread *p) { MY_BUF_ALLOC(p->inBuf, p->inBufSize, p->mtCoder->blockSize) MY_BUF_ALLOC(p->outBuf, p->outBufSize, p->mtCoder->destBlockSize) p->stopReading = False; p->stopWriting = False; RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canRead)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canWrite)); return SZ_OK; } static SRes FullRead(ISeqInStream *stream, Byte *data, size_t *processedSize) { size_t size = *processedSize; *processedSize = 0; while (size != 0) { size_t curSize = size; SRes res = stream->Read(stream, data, &curSize); *processedSize += curSize; data += curSize; size -= curSize; RINOK(res); if (curSize == 0) return SZ_OK; } return SZ_OK; } #define GET_NEXT_THREAD(p) &p->mtCoder->threads[p->index == p->mtCoder->numThreads - 1 ? 0 : p->index + 1] static SRes MtThread_Process(CMtThread *p, Bool *stop) { CMtThread *next; *stop = True; if (Event_Wait(&p->canRead) != 0) return SZ_ERROR_THREAD; next = GET_NEXT_THREAD(p); if (p->stopReading) { next->stopReading = True; return Event_Set(&next->canRead) == 0 ? SZ_OK : SZ_ERROR_THREAD; } { size_t size = p->mtCoder->blockSize; size_t destSize = p->outBufSize; RINOK(FullRead(p->mtCoder->inStream, p->inBuf, &size)); next->stopReading = *stop = (size != p->mtCoder->blockSize); if (Event_Set(&next->canRead) != 0) return SZ_ERROR_THREAD; RINOK(p->mtCoder->mtCallback->Code(p->mtCoder->mtCallback, p->index, p->outBuf, &destSize, p->inBuf, size, *stop)); MtProgress_Reinit(&p->mtCoder->mtProgress, p->index); if (Event_Wait(&p->canWrite) != 0) return SZ_ERROR_THREAD; if (p->stopWriting) return SZ_ERROR_FAIL; if (p->mtCoder->outStream->Write(p->mtCoder->outStream, p->outBuf, destSize) != destSize) return SZ_ERROR_WRITE; return Event_Set(&next->canWrite) == 0 ? SZ_OK : SZ_ERROR_THREAD; } } static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE ThreadFunc(void *pp) { CMtThread *p = (CMtThread *)pp; for (;;) { Bool stop; CMtThread *next = GET_NEXT_THREAD(p); SRes res = MtThread_Process(p, &stop); if (res != SZ_OK) { MtCoder_SetError(p->mtCoder, res); MtProgress_SetError(&p->mtCoder->mtProgress, res); next->stopReading = True; next->stopWriting = True; Event_Set(&next->canRead); Event_Set(&next->canWrite); return res; } if (stop) return 0; } } void MtCoder_Construct(CMtCoder* p) { unsigned i; p->alloc = 0; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) { CMtThread *t = &p->threads[i]; t->index = i; CMtThread_Construct(t, p); } CriticalSection_Init(&p->cs); CriticalSection_Init(&p->mtProgress.cs); } void MtCoder_Destruct(CMtCoder* p) { unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) CMtThread_Destruct(&p->threads[i]); CriticalSection_Delete(&p->cs); CriticalSection_Delete(&p->mtProgress.cs); } SRes MtCoder_Code(CMtCoder *p) { unsigned i, numThreads = p->numThreads; SRes res = SZ_OK; p->res = SZ_OK; MtProgress_Init(&p->mtProgress, p->progress); for (i = 0; i < numThreads; i++) { RINOK(CMtThread_Prepare(&p->threads[i])); } for (i = 0; i < numThreads; i++) { CMtThread *t = &p->threads[i]; CLoopThread *lt = &t->thread; if (!Thread_WasCreated(<->thread)) { lt->func = ThreadFunc; lt->param = t; if (LoopThread_Create(lt) != SZ_OK) { res = SZ_ERROR_THREAD; break; } } } if (res == SZ_OK) { unsigned j; for (i = 0; i < numThreads; i++) { CMtThread *t = &p->threads[i]; if (LoopThread_StartSubThread(&t->thread) != SZ_OK) { res = SZ_ERROR_THREAD; p->threads[0].stopReading = True; break; } } Event_Set(&p->threads[0].canWrite); Event_Set(&p->threads[0].canRead); for (j = 0; j < i; j++) LoopThread_WaitSubThread(&p->threads[j].thread); } for (i = 0; i < numThreads; i++) CMtThread_CloseEvents(&p->threads[i]); return (res == SZ_OK) ? p->res : res; } src/libs/7zip/unix/C/MtCoder.h000066400000000000000000000040241325366651500163560ustar00rootroot00000000000000/* MtCoder.h -- Multi-thread Coder 2009-11-19 : Igor Pavlov : Public domain */ #ifndef __MT_CODER_H #define __MT_CODER_H #include "Threads.h" EXTERN_C_BEGIN typedef struct { CThread thread; CAutoResetEvent startEvent; CAutoResetEvent finishedEvent; int stop; THREAD_FUNC_TYPE func; LPVOID param; THREAD_FUNC_RET_TYPE res; } CLoopThread; void LoopThread_Construct(CLoopThread *p); void LoopThread_Close(CLoopThread *p); WRes LoopThread_Create(CLoopThread *p); WRes LoopThread_StopAndWait(CLoopThread *p); WRes LoopThread_StartSubThread(CLoopThread *p); WRes LoopThread_WaitSubThread(CLoopThread *p); #ifndef _7ZIP_ST #define NUM_MT_CODER_THREADS_MAX 32 #else #define NUM_MT_CODER_THREADS_MAX 1 #endif typedef struct { UInt64 totalInSize; UInt64 totalOutSize; ICompressProgress *progress; SRes res; CCriticalSection cs; UInt64 inSizes[NUM_MT_CODER_THREADS_MAX]; UInt64 outSizes[NUM_MT_CODER_THREADS_MAX]; } CMtProgress; SRes MtProgress_Set(CMtProgress *p, unsigned index, UInt64 inSize, UInt64 outSize); struct _CMtCoder; typedef struct { struct _CMtCoder *mtCoder; Byte *outBuf; size_t outBufSize; Byte *inBuf; size_t inBufSize; unsigned index; CLoopThread thread; Bool stopReading; Bool stopWriting; CAutoResetEvent canRead; CAutoResetEvent canWrite; } CMtThread; typedef struct { SRes (*Code)(void *p, unsigned index, Byte *dest, size_t *destSize, const Byte *src, size_t srcSize, int finished); } IMtCoderCallback; typedef struct _CMtCoder { size_t blockSize; size_t destBlockSize; unsigned numThreads; ISeqInStream *inStream; ISeqOutStream *outStream; ICompressProgress *progress; ISzAlloc *alloc; IMtCoderCallback *mtCallback; CCriticalSection cs; SRes res; CMtProgress mtProgress; CMtThread threads[NUM_MT_CODER_THREADS_MAX]; } CMtCoder; void MtCoder_Construct(CMtCoder* p); void MtCoder_Destruct(CMtCoder* p); SRes MtCoder_Code(CMtCoder *p); EXTERN_C_END #endif src/libs/7zip/unix/C/Precomp.h000066400000000000000000000002661325366651500164320ustar00rootroot00000000000000/* Precomp.h -- StdAfx 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_PRECOMP_H #define __7Z_PRECOMP_H #include "Compiler.h" /* #include "7zTypes.h" */ #endif src/libs/7zip/unix/C/RotateDefs.h000066400000000000000000000007731325366651500170700ustar00rootroot00000000000000/* RotateDefs.h -- Rotate functions 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __ROTATE_DEFS_H #define __ROTATE_DEFS_H #ifdef _MSC_VER #include // #if (_MSC_VER >= 1200) #pragma intrinsic(_rotl) #pragma intrinsic(_rotr) // #endif #define rotlFixed(x, n) _rotl((x), (n)) #define rotrFixed(x, n) _rotr((x), (n)) #else #define rotlFixed(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) #define rotrFixed(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) #endif #endif src/libs/7zip/unix/C/Sha256.c000066400000000000000000000121501325366651500157630ustar00rootroot00000000000000/* Crypto/Sha256.c -- SHA-256 Hash 2010-06-11 : Igor Pavlov : Public domain This code is based on public domain code from Wei Dai's Crypto++ library. */ #include "Precomp.h" #include "RotateDefs.h" #include "Sha256.h" /* define it for speed optimization */ /* #define _SHA256_UNROLL */ /* #define _SHA256_UNROLL2 */ void Sha256_Init(CSha256 *p) { p->state[0] = 0x6a09e667; p->state[1] = 0xbb67ae85; p->state[2] = 0x3c6ef372; p->state[3] = 0xa54ff53a; p->state[4] = 0x510e527f; p->state[5] = 0x9b05688c; p->state[6] = 0x1f83d9ab; p->state[7] = 0x5be0cd19; p->count = 0; } #define S0(x) (rotrFixed(x, 2) ^ rotrFixed(x,13) ^ rotrFixed(x, 22)) #define S1(x) (rotrFixed(x, 6) ^ rotrFixed(x,11) ^ rotrFixed(x, 25)) #define s0(x) (rotrFixed(x, 7) ^ rotrFixed(x,18) ^ (x >> 3)) #define s1(x) (rotrFixed(x,17) ^ rotrFixed(x,19) ^ (x >> 10)) #define blk0(i) (W[i] = data[i]) #define blk2(i) (W[i&15] += s1(W[(i-2)&15]) + W[(i-7)&15] + s0(W[(i-15)&15])) #define Ch(x,y,z) (z^(x&(y^z))) #define Maj(x,y,z) ((x&y)|(z&(x|y))) #define a(i) T[(0-(i))&7] #define b(i) T[(1-(i))&7] #define c(i) T[(2-(i))&7] #define d(i) T[(3-(i))&7] #define e(i) T[(4-(i))&7] #define f(i) T[(5-(i))&7] #define g(i) T[(6-(i))&7] #define h(i) T[(7-(i))&7] #ifdef _SHA256_UNROLL2 #define R(a,b,c,d,e,f,g,h, i) h += S1(e) + Ch(e,f,g) + K[i+j] + (j?blk2(i):blk0(i));\ d += h; h += S0(a) + Maj(a, b, c) #define RX_8(i) \ R(a,b,c,d,e,f,g,h, i); \ R(h,a,b,c,d,e,f,g, i+1); \ R(g,h,a,b,c,d,e,f, i+2); \ R(f,g,h,a,b,c,d,e, i+3); \ R(e,f,g,h,a,b,c,d, i+4); \ R(d,e,f,g,h,a,b,c, i+5); \ R(c,d,e,f,g,h,a,b, i+6); \ R(b,c,d,e,f,g,h,a, i+7) #else #define R(i) h(i) += S1(e(i)) + Ch(e(i),f(i),g(i)) + K[i+j] + (j?blk2(i):blk0(i));\ d(i) += h(i); h(i) += S0(a(i)) + Maj(a(i), b(i), c(i)) #ifdef _SHA256_UNROLL #define RX_8(i) R(i+0); R(i+1); R(i+2); R(i+3); R(i+4); R(i+5); R(i+6); R(i+7); #endif #endif static const UInt32 K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; static void Sha256_Transform(UInt32 *state, const UInt32 *data) { UInt32 W[16]; unsigned j; #ifdef _SHA256_UNROLL2 UInt32 a,b,c,d,e,f,g,h; a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; f = state[5]; g = state[6]; h = state[7]; #else UInt32 T[8]; for (j = 0; j < 8; j++) T[j] = state[j]; #endif for (j = 0; j < 64; j += 16) { #if defined(_SHA256_UNROLL) || defined(_SHA256_UNROLL2) RX_8(0); RX_8(8); #else unsigned i; for (i = 0; i < 16; i++) { R(i); } #endif } #ifdef _SHA256_UNROLL2 state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; #else for (j = 0; j < 8; j++) state[j] += T[j]; #endif /* Wipe variables */ /* memset(W, 0, sizeof(W)); */ /* memset(T, 0, sizeof(T)); */ } #undef S0 #undef S1 #undef s0 #undef s1 static void Sha256_WriteByteBlock(CSha256 *p) { UInt32 data32[16]; unsigned i; for (i = 0; i < 16; i++) data32[i] = ((UInt32)(p->buffer[i * 4 ]) << 24) + ((UInt32)(p->buffer[i * 4 + 1]) << 16) + ((UInt32)(p->buffer[i * 4 + 2]) << 8) + ((UInt32)(p->buffer[i * 4 + 3])); Sha256_Transform(p->state, data32); } void Sha256_Update(CSha256 *p, const Byte *data, size_t size) { UInt32 curBufferPos = (UInt32)p->count & 0x3F; while (size > 0) { p->buffer[curBufferPos++] = *data++; p->count++; size--; if (curBufferPos == 64) { curBufferPos = 0; Sha256_WriteByteBlock(p); } } } void Sha256_Final(CSha256 *p, Byte *digest) { UInt64 lenInBits = (p->count << 3); UInt32 curBufferPos = (UInt32)p->count & 0x3F; unsigned i; p->buffer[curBufferPos++] = 0x80; while (curBufferPos != (64 - 8)) { curBufferPos &= 0x3F; if (curBufferPos == 0) Sha256_WriteByteBlock(p); p->buffer[curBufferPos++] = 0; } for (i = 0; i < 8; i++) { p->buffer[curBufferPos++] = (Byte)(lenInBits >> 56); lenInBits <<= 8; } Sha256_WriteByteBlock(p); for (i = 0; i < 8; i++) { *digest++ = (Byte)(p->state[i] >> 24); *digest++ = (Byte)(p->state[i] >> 16); *digest++ = (Byte)(p->state[i] >> 8); *digest++ = (Byte)(p->state[i]); } Sha256_Init(p); } src/libs/7zip/unix/C/Sha256.h000066400000000000000000000007201325366651500157700ustar00rootroot00000000000000/* Sha256.h -- SHA-256 Hash 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __CRYPTO_SHA256_H #define __CRYPTO_SHA256_H #include "7zTypes.h" EXTERN_C_BEGIN #define SHA256_DIGEST_SIZE 32 typedef struct { UInt32 state[8]; UInt64 count; Byte buffer[64]; } CSha256; void Sha256_Init(CSha256 *p); void Sha256_Update(CSha256 *p, const Byte *data, size_t size); void Sha256_Final(CSha256 *p, Byte *digest); EXTERN_C_END #endif src/libs/7zip/unix/C/Threads.c000066400000000000000000000351731325366651500164170ustar00rootroot00000000000000/* Threads.c */ #include "Threads.h" #ifdef ENV_BEOS #include #else #include #include #endif #include #if defined(__linux__) #define PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_ERRORCHECK_NP #endif #ifdef ENV_BEOS /* TODO : optimize the code and verify the returned values */ WRes Thread_Create(CThread *thread, THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter) { thread->_tid = spawn_thread((int32 (*)(void *))startAddress, "CThread", B_LOW_PRIORITY, parameter); if (thread->_tid >= B_OK) { resume_thread(thread->_tid); } else { thread->_tid = B_BAD_THREAD_ID; } thread->_created = 1; return 0; // SZ_OK; } WRes Thread_Wait(CThread *thread) { int ret; if (thread->_created == 0) return EINVAL; if (thread->_tid >= B_OK) { status_t exit_value; wait_for_thread(thread->_tid, &exit_value); thread->_tid = B_BAD_THREAD_ID; } else { return EINVAL; } thread->_created = 0; return 0; } WRes Thread_Close(CThread *thread) { if (!thread->_created) return SZ_OK; thread->_tid = B_BAD_THREAD_ID; thread->_created = 0; return SZ_OK; } WRes Event_Create(CEvent *p, BOOL manualReset, int initialSignaled) { p->_index_waiting = 0; p->_manual_reset = manualReset; p->_state = (initialSignaled ? TRUE : FALSE); p->_created = 1; p->_sem = create_sem(1,"event"); return 0; } WRes Event_Set(CEvent *p) { int index; acquire_sem(p->_sem); p->_state = TRUE; for(index = 0 ; index < p->_index_waiting ; index++) { send_data(p->_waiting[index], '7zCN', NULL, 0); } p->_index_waiting = 0; release_sem(p->_sem); return 0; } WRes Event_Reset(CEvent *p) { acquire_sem(p->_sem); p->_state = FALSE; release_sem(p->_sem); return 0; } WRes Event_Wait(CEvent *p) { acquire_sem(p->_sem); while (p->_state == FALSE) { thread_id sender; p->_waiting[p->_index_waiting++] = find_thread(NULL); release_sem(p->_sem); /* int msg = */ receive_data(&sender, NULL, 0); acquire_sem(p->_sem); } if (p->_manual_reset == FALSE) { p->_state = FALSE; } release_sem(p->_sem); return 0; } WRes Event_Close(CEvent *p) { if (p->_created) { p->_created = 0; delete_sem(p->_sem); } return 0; } WRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount) { p->_index_waiting = 0; p->_count = initiallyCount; p->_maxCount = maxCount; p->_created = 1; p->_sem = create_sem(1,"sem"); return 0; } WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 releaseCount) { UInt32 newCount; int index; if (releaseCount < 1) return EINVAL; acquire_sem(p->_sem); newCount = p->_count + releaseCount; if (newCount > p->_maxCount) { release_sem(p->_sem); return EINVAL; } p->_count = newCount; for(index = 0 ; index < p->_index_waiting ; index++) { send_data(p->_waiting[index], '7zCN', NULL, 0); } p->_index_waiting = 0; release_sem(p->_sem); return 0; } WRes Semaphore_Wait(CSemaphore *p) { acquire_sem(p->_sem); while (p->_count < 1) { thread_id sender; p->_waiting[p->_index_waiting++] = find_thread(NULL); release_sem(p->_sem); /* int msg = */ receive_data(&sender, NULL, 0); acquire_sem(p->_sem); } p->_count--; release_sem(p->_sem); return 0; } WRes Semaphore_Close(CSemaphore *p) { if (p->_created) { p->_created = 0; delete_sem(p->_sem); } return 0; } WRes CriticalSection_Init(CCriticalSection * lpCriticalSection) { lpCriticalSection->_sem = create_sem(1,"cc"); return 0; } #else /* !ENV_BEOS */ WRes Thread_Create(CThread *thread, THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter) { pthread_attr_t attr; int ret; thread->_created = 0; ret = pthread_attr_init(&attr); if (ret) return ret; ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE); if (ret) return ret; ret = pthread_create(&thread->_tid, &attr, (void * (*)(void *))startAddress, parameter); /* ret2 = */ pthread_attr_destroy(&attr); if (ret) return ret; thread->_created = 1; return 0; // SZ_OK; } WRes Thread_Wait(CThread *thread) { void *thread_return; int ret; if (thread->_created == 0) return EINVAL; ret = pthread_join(thread->_tid,&thread_return); thread->_created = 0; return ret; } WRes Thread_Close(CThread *thread) { if (!thread->_created) return SZ_OK; pthread_detach(thread->_tid); thread->_tid = 0; thread->_created = 0; return SZ_OK; } #ifdef DEBUG_SYNCHRO #include static void dump_error(int ligne,int ret,const char *text,void *param) { printf("\n##T%d#ERROR2 (l=%d) %s : param=%p ret = %d (%s)##\n",(int)pthread_self(),ligne,text,param,ret,strerror(ret)); // abort(); } WRes Event_Create(CEvent *p, BOOL manualReset, int initialSignaled) { int ret; pthread_mutexattr_t mutexattr; memset(&mutexattr,0,sizeof(mutexattr)); ret = pthread_mutexattr_init(&mutexattr); if (ret != 0) dump_error(__LINE__,ret,"Event_Create::pthread_mutexattr_init",&mutexattr); ret = pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK); if (ret != 0) dump_error(__LINE__,ret,"Event_Create::pthread_mutexattr_settype",&mutexattr); ret = pthread_mutex_init(&p->_mutex,&mutexattr); if (ret != 0) dump_error(__LINE__,ret,"Event_Create::pthread_mutexattr_init",&p->_mutex); if (ret == 0) { ret = pthread_cond_init(&p->_cond,0); if (ret != 0) dump_error(__LINE__,ret,"Event_Create::pthread_cond_init",&p->_cond); p->_manual_reset = manualReset; p->_state = (initialSignaled ? TRUE : FALSE); p->_created = 1; } return ret; } WRes Event_Set(CEvent *p) { int ret = pthread_mutex_lock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"ES::pthread_mutex_lock",&p->_mutex); if (ret == 0) { p->_state = TRUE; ret = pthread_cond_broadcast(&p->_cond); if (ret != 0) dump_error(__LINE__,ret,"ES::pthread_cond_broadcast",&p->_cond); if (ret == 0) { ret = pthread_mutex_unlock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"ES::pthread_mutex_unlock",&p->_mutex); } } return ret; } WRes Event_Reset(CEvent *p) { int ret = pthread_mutex_lock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"ER::pthread_mutex_lock",&p->_mutex); if (ret == 0) { p->_state = FALSE; ret = pthread_mutex_unlock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"ER::pthread_mutex_unlock",&p->_mutex); } return ret; } WRes Event_Wait(CEvent *p) { int ret = pthread_mutex_lock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"EW::pthread_mutex_lock",&p->_mutex); if (ret == 0) { while ((p->_state == FALSE) && (ret == 0)) { ret = pthread_cond_wait(&p->_cond, &p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"EW::pthread_cond_wait",&p->_mutex); } if (ret == 0) { if (p->_manual_reset == FALSE) { p->_state = FALSE; } ret = pthread_mutex_unlock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"EW::pthread_mutex_unlock",&p->_mutex); } } return ret; } WRes Event_Close(CEvent *p) { if (p->_created) { int ret; p->_created = 0; ret = pthread_mutex_destroy(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"EC::pthread_mutex_destroy",&p->_mutex); ret = pthread_cond_destroy(&p->_cond); if (ret != 0) dump_error(__LINE__,ret,"EC::pthread_cond_destroy",&p->_cond); } return 0; } WRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount) { int ret; pthread_mutexattr_t mutexattr; memset(&mutexattr,0,sizeof(mutexattr)); ret = pthread_mutexattr_init(&mutexattr); if (ret != 0) dump_error(__LINE__,ret,"SemC::pthread_mutexattr_init",&mutexattr); ret = pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK); if (ret != 0) dump_error(__LINE__,ret,"SemC::pthread_mutexattr_settype",&mutexattr); ret = pthread_mutex_init(&p->_mutex,&mutexattr); if (ret != 0) dump_error(__LINE__,ret,"SemC::pthread_mutexattr_init",&p->_mutex); if (ret == 0) { ret = pthread_cond_init(&p->_cond,0); if (ret != 0) dump_error(__LINE__,ret,"SemC::pthread_cond_init",&p->_mutex); p->_count = initiallyCount; p->_maxCount = maxCount; p->_created = 1; } return ret; } WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 releaseCount) { int ret; if (releaseCount < 1) return EINVAL; ret = pthread_mutex_lock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"SemR::pthread_mutex_lock",&p->_mutex); if (ret == 0) { UInt32 newCount = p->_count + releaseCount; if (newCount > p->_maxCount) { ret = pthread_mutex_unlock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"SemR::pthread_mutex_unlock",&p->_mutex); return EINVAL; } p->_count = newCount; ret = pthread_cond_broadcast(&p->_cond); if (ret != 0) dump_error(__LINE__,ret,"SemR::pthread_cond_broadcast",&p->_cond); if (ret == 0) { ret = pthread_mutex_unlock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"SemR::pthread_mutex_unlock",&p->_mutex); } } return ret; } WRes Semaphore_Wait(CSemaphore *p) { int ret = pthread_mutex_lock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"SemW::pthread_mutex_lock",&p->_mutex); if (ret == 0) { while ((p->_count < 1) && (ret == 0)) { ret = pthread_cond_wait(&p->_cond, &p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"SemW::pthread_cond_wait",&p->_mutex); } if (ret == 0) { p->_count--; ret = pthread_mutex_unlock(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"SemW::pthread_mutex_unlock",&p->_mutex); } } return ret; } WRes Semaphore_Close(CSemaphore *p) { if (p->_created) { int ret; p->_created = 0; ret = pthread_mutex_destroy(&p->_mutex); if (ret != 0) dump_error(__LINE__,ret,"Semc::pthread_mutex_destroy",&p->_mutex); ret = pthread_cond_destroy(&p->_cond); if (ret != 0) dump_error(__LINE__,ret,"Semc::pthread_cond_destroy",&p->_cond); } return 0; } WRes CriticalSection_Init(CCriticalSection * lpCriticalSection) { if (lpCriticalSection) { int ret; pthread_mutexattr_t mutexattr; memset(&mutexattr,0,sizeof(mutexattr)); ret = pthread_mutexattr_init(&mutexattr); if (ret != 0) dump_error(__LINE__,ret,"CS I::pthread_mutexattr_init",&mutexattr); ret = pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK); if (ret != 0) dump_error(__LINE__,ret,"CS I::pthread_mutexattr_settype",&mutexattr); ret = pthread_mutex_init(&lpCriticalSection->_mutex,&mutexattr); if (ret != 0) dump_error(__LINE__,ret,"CS I::pthread_mutexattr_init",&lpCriticalSection->_mutex); return ret; } return EINTR; } void CriticalSection_Enter(CCriticalSection * lpCriticalSection) { if (lpCriticalSection) { int ret = pthread_mutex_lock(&(lpCriticalSection->_mutex)); if (ret != 0) dump_error(__LINE__,ret,"CS::pthread_mutex_lock",&(lpCriticalSection->_mutex)); } } void CriticalSection_Leave(CCriticalSection * lpCriticalSection) { if (lpCriticalSection) { int ret = pthread_mutex_unlock(&(lpCriticalSection->_mutex)); if (ret != 0) dump_error(__LINE__,ret,"CS::pthread_mutex_unlock",&(lpCriticalSection->_mutex)); } } void CriticalSection_Delete(CCriticalSection * lpCriticalSection) { if (lpCriticalSection) { int ret = pthread_mutex_destroy(&(lpCriticalSection->_mutex)); if (ret != 0) dump_error(__LINE__,ret,"CS::pthread_mutex_destroy",&(lpCriticalSection->_mutex)); } } #else WRes Event_Create(CEvent *p, BOOL manualReset, int initialSignaled) { pthread_mutex_init(&p->_mutex,0); pthread_cond_init(&p->_cond,0); p->_manual_reset = manualReset; p->_state = (initialSignaled ? TRUE : FALSE); p->_created = 1; return 0; } WRes Event_Set(CEvent *p) { pthread_mutex_lock(&p->_mutex); p->_state = TRUE; pthread_cond_broadcast(&p->_cond); pthread_mutex_unlock(&p->_mutex); return 0; } WRes Event_Reset(CEvent *p) { pthread_mutex_lock(&p->_mutex); p->_state = FALSE; pthread_mutex_unlock(&p->_mutex); return 0; } WRes Event_Wait(CEvent *p) { pthread_mutex_lock(&p->_mutex); while (p->_state == FALSE) { pthread_cond_wait(&p->_cond, &p->_mutex); } if (p->_manual_reset == FALSE) { p->_state = FALSE; } pthread_mutex_unlock(&p->_mutex); return 0; } WRes Event_Close(CEvent *p) { if (p->_created) { p->_created = 0; pthread_mutex_destroy(&p->_mutex); pthread_cond_destroy(&p->_cond); } return 0; } WRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount) { pthread_mutex_init(&p->_mutex,0); pthread_cond_init(&p->_cond,0); p->_count = initiallyCount; p->_maxCount = maxCount; p->_created = 1; return 0; } WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 releaseCount) { UInt32 newCount; if (releaseCount < 1) return EINVAL; pthread_mutex_lock(&p->_mutex); newCount = p->_count + releaseCount; if (newCount > p->_maxCount) { pthread_mutex_unlock(&p->_mutex); return EINVAL; } p->_count = newCount; pthread_cond_broadcast(&p->_cond); pthread_mutex_unlock(&p->_mutex); return 0; } WRes Semaphore_Wait(CSemaphore *p) { pthread_mutex_lock(&p->_mutex); while (p->_count < 1) { pthread_cond_wait(&p->_cond, &p->_mutex); } p->_count--; pthread_mutex_unlock(&p->_mutex); return 0; } WRes Semaphore_Close(CSemaphore *p) { if (p->_created) { p->_created = 0; pthread_mutex_destroy(&p->_mutex); pthread_cond_destroy(&p->_cond); } return 0; } WRes CriticalSection_Init(CCriticalSection * lpCriticalSection) { return pthread_mutex_init(&(lpCriticalSection->_mutex),0); } #endif /* DEBUG_SYNCHRO */ #endif /* ENV_BEOS */ WRes ManualResetEvent_Create(CManualResetEvent *p, int initialSignaled) { return Event_Create(p, TRUE, initialSignaled); } WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) { return ManualResetEvent_Create(p, 0); } WRes AutoResetEvent_Create(CAutoResetEvent *p, int initialSignaled) { return Event_Create(p, FALSE, initialSignaled); } WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) { return AutoResetEvent_Create(p, 0); } src/libs/7zip/unix/C/Threads.h000066400000000000000000000062051325366651500164160ustar00rootroot00000000000000/* Threads.h -- multithreading library 2008-11-22 : Igor Pavlov : Public domain */ #ifndef __7Z_THRESDS_H #define __7Z_THRESDS_H #include "7zTypes.h" #include "windows.h" #ifdef ENV_BEOS #include #define MAX_THREAD 256 #else #include #endif /* #define DEBUG_SYNCHRO 1 */ typedef struct _CThread { #ifdef ENV_BEOS thread_id _tid; #else pthread_t _tid; #endif int _created; } CThread; #define Thread_Construct(thread) (thread)->_created = 0 #define Thread_WasCreated(thread) ((thread)->_created != 0) typedef unsigned THREAD_FUNC_RET_TYPE; #define THREAD_FUNC_CALL_TYPE MY_STD_CALL #define THREAD_FUNC_DECL THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE typedef THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE * THREAD_FUNC_TYPE)(void *); WRes Thread_Create(CThread *thread, THREAD_FUNC_TYPE startAddress, LPVOID parameter); WRes Thread_Wait(CThread *thread); WRes Thread_Close(CThread *thread); typedef struct _CEvent { int _created; int _manual_reset; int _state; #ifdef ENV_BEOS thread_id _waiting[MAX_THREAD]; int _index_waiting; sem_id _sem; #else pthread_mutex_t _mutex; pthread_cond_t _cond; #endif } CEvent; typedef CEvent CAutoResetEvent; typedef CEvent CManualResetEvent; #define Event_Construct(event) (event)->_created = 0 #define Event_IsCreated(event) ((event)->_created) WRes ManualResetEvent_Create(CManualResetEvent *event, int initialSignaled); WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *event); WRes AutoResetEvent_Create(CAutoResetEvent *event, int initialSignaled); WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *event); WRes Event_Set(CEvent *event); WRes Event_Reset(CEvent *event); WRes Event_Wait(CEvent *event); WRes Event_Close(CEvent *event); typedef struct _CSemaphore { int _created; UInt32 _count; UInt32 _maxCount; #ifdef ENV_BEOS thread_id _waiting[MAX_THREAD]; int _index_waiting; sem_id _sem; #else pthread_mutex_t _mutex; pthread_cond_t _cond; #endif } CSemaphore; #define Semaphore_Construct(p) (p)->_created = 0 WRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount); WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num); #define Semaphore_Release1(p) Semaphore_ReleaseN(p, 1) WRes Semaphore_Wait(CSemaphore *p); WRes Semaphore_Close(CSemaphore *p); typedef struct { #ifdef ENV_BEOS sem_id _sem; #else pthread_mutex_t _mutex; #endif } CCriticalSection; WRes CriticalSection_Init(CCriticalSection *p); #ifdef ENV_BEOS #define CriticalSection_Delete(p) delete_sem((p)->_sem) #define CriticalSection_Enter(p) acquire_sem((p)->_sem) #define CriticalSection_Leave(p) release_sem((p)->_sem) #else #ifdef DEBUG_SYNCHRO void CriticalSection_Delete(CCriticalSection *); void CriticalSection_Enter(CCriticalSection *); void CriticalSection_Leave(CCriticalSection *); #else #define CriticalSection_Delete(p) pthread_mutex_destroy(&((p)->_mutex)) #define CriticalSection_Enter(p) pthread_mutex_lock(&((p)->_mutex)) #define CriticalSection_Leave(p) pthread_mutex_unlock(&((p)->_mutex)) #endif #endif #endif src/libs/7zip/unix/C/Xz.c000066400000000000000000000037101325366651500154160ustar00rootroot00000000000000/* Xz.c - Xz 2009-04-15 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "7zCrc.h" #include "CpuArch.h" #include "Xz.h" #include "XzCrc64.h" Byte XZ_SIG[XZ_SIG_SIZE] = { 0xFD, '7', 'z', 'X', 'Z', 0 }; Byte XZ_FOOTER_SIG[XZ_FOOTER_SIG_SIZE] = { 'Y', 'Z' }; unsigned Xz_WriteVarInt(Byte *buf, UInt64 v) { unsigned i = 0; do { buf[i++] = (Byte)((v & 0x7F) | 0x80); v >>= 7; } while (v != 0); buf[i - 1] &= 0x7F; return i; } void Xz_Construct(CXzStream *p) { p->numBlocks = p->numBlocksAllocated = 0; p->blocks = 0; p->flags = 0; } void Xz_Free(CXzStream *p, ISzAlloc *alloc) { alloc->Free(alloc, p->blocks); p->numBlocks = p->numBlocksAllocated = 0; p->blocks = 0; } unsigned XzFlags_GetCheckSize(CXzStreamFlags f) { int t = XzFlags_GetCheckType(f); return (t == 0) ? 0 : (4 << ((t - 1) / 3)); } void XzCheck_Init(CXzCheck *p, int mode) { p->mode = mode; switch (mode) { case XZ_CHECK_CRC32: p->crc = CRC_INIT_VAL; break; case XZ_CHECK_CRC64: p->crc64 = CRC64_INIT_VAL; break; case XZ_CHECK_SHA256: Sha256_Init(&p->sha); break; } } void XzCheck_Update(CXzCheck *p, const void *data, size_t size) { switch (p->mode) { case XZ_CHECK_CRC32: p->crc = CrcUpdate(p->crc, data, size); break; case XZ_CHECK_CRC64: p->crc64 = Crc64Update(p->crc64, data, size); break; case XZ_CHECK_SHA256: Sha256_Update(&p->sha, (const Byte *)data, size); break; } } int XzCheck_Final(CXzCheck *p, Byte *digest) { switch (p->mode) { case XZ_CHECK_CRC32: SetUi32(digest, CRC_GET_DIGEST(p->crc)); break; case XZ_CHECK_CRC64: { int i; UInt64 v = CRC64_GET_DIGEST(p->crc64); for (i = 0; i < 8; i++, v >>= 8) digest[i] = (Byte)(v & 0xFF); break; } case XZ_CHECK_SHA256: Sha256_Final(&p->sha, digest); break; default: return 0; } return 1; } src/libs/7zip/unix/C/Xz.h000066400000000000000000000167561325366651500154410ustar00rootroot00000000000000/* Xz.h - Xz interface 2014-12-30 : Igor Pavlov : Public domain */ #ifndef __XZ_H #define __XZ_H #include "Sha256.h" EXTERN_C_BEGIN #define XZ_ID_Subblock 1 #define XZ_ID_Delta 3 #define XZ_ID_X86 4 #define XZ_ID_PPC 5 #define XZ_ID_IA64 6 #define XZ_ID_ARM 7 #define XZ_ID_ARMT 8 #define XZ_ID_SPARC 9 #define XZ_ID_LZMA2 0x21 unsigned Xz_ReadVarInt(const Byte *p, size_t maxSize, UInt64 *value); unsigned Xz_WriteVarInt(Byte *buf, UInt64 v); /* ---------- xz block ---------- */ #define XZ_BLOCK_HEADER_SIZE_MAX 1024 #define XZ_NUM_FILTERS_MAX 4 #define XZ_BF_NUM_FILTERS_MASK 3 #define XZ_BF_PACK_SIZE (1 << 6) #define XZ_BF_UNPACK_SIZE (1 << 7) #define XZ_FILTER_PROPS_SIZE_MAX 20 typedef struct { UInt64 id; UInt32 propsSize; Byte props[XZ_FILTER_PROPS_SIZE_MAX]; } CXzFilter; typedef struct { UInt64 packSize; UInt64 unpackSize; Byte flags; CXzFilter filters[XZ_NUM_FILTERS_MAX]; } CXzBlock; #define XzBlock_GetNumFilters(p) (((p)->flags & XZ_BF_NUM_FILTERS_MASK) + 1) #define XzBlock_HasPackSize(p) (((p)->flags & XZ_BF_PACK_SIZE) != 0) #define XzBlock_HasUnpackSize(p) (((p)->flags & XZ_BF_UNPACK_SIZE) != 0) SRes XzBlock_Parse(CXzBlock *p, const Byte *header); SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStream *inStream, Bool *isIndex, UInt32 *headerSizeRes); /* ---------- xz stream ---------- */ #define XZ_SIG_SIZE 6 #define XZ_FOOTER_SIG_SIZE 2 extern Byte XZ_SIG[XZ_SIG_SIZE]; extern Byte XZ_FOOTER_SIG[XZ_FOOTER_SIG_SIZE]; #define XZ_STREAM_FLAGS_SIZE 2 #define XZ_STREAM_CRC_SIZE 4 #define XZ_STREAM_HEADER_SIZE (XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE + XZ_STREAM_CRC_SIZE) #define XZ_STREAM_FOOTER_SIZE (XZ_FOOTER_SIG_SIZE + XZ_STREAM_FLAGS_SIZE + XZ_STREAM_CRC_SIZE + 4) #define XZ_CHECK_MASK 0xF #define XZ_CHECK_NO 0 #define XZ_CHECK_CRC32 1 #define XZ_CHECK_CRC64 4 #define XZ_CHECK_SHA256 10 typedef struct { int mode; UInt32 crc; UInt64 crc64; CSha256 sha; } CXzCheck; void XzCheck_Init(CXzCheck *p, int mode); void XzCheck_Update(CXzCheck *p, const void *data, size_t size); int XzCheck_Final(CXzCheck *p, Byte *digest); typedef UInt16 CXzStreamFlags; #define XzFlags_IsSupported(f) ((f) <= XZ_CHECK_MASK) #define XzFlags_GetCheckType(f) ((f) & XZ_CHECK_MASK) #define XzFlags_HasDataCrc32(f) (Xz_GetCheckType(f) == XZ_CHECK_CRC32) unsigned XzFlags_GetCheckSize(CXzStreamFlags f); SRes Xz_ParseHeader(CXzStreamFlags *p, const Byte *buf); SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStream *inStream); typedef struct { UInt64 unpackSize; UInt64 totalSize; } CXzBlockSizes; typedef struct { CXzStreamFlags flags; size_t numBlocks; size_t numBlocksAllocated; CXzBlockSizes *blocks; UInt64 startOffset; } CXzStream; void Xz_Construct(CXzStream *p); void Xz_Free(CXzStream *p, ISzAlloc *alloc); #define XZ_SIZE_OVERFLOW ((UInt64)(Int64)-1) UInt64 Xz_GetUnpackSize(const CXzStream *p); UInt64 Xz_GetPackSize(const CXzStream *p); typedef struct { size_t num; size_t numAllocated; CXzStream *streams; } CXzs; void Xzs_Construct(CXzs *p); void Xzs_Free(CXzs *p, ISzAlloc *alloc); SRes Xzs_ReadBackward(CXzs *p, ILookInStream *inStream, Int64 *startOffset, ICompressProgress *progress, ISzAlloc *alloc); UInt64 Xzs_GetNumBlocks(const CXzs *p); UInt64 Xzs_GetUnpackSize(const CXzs *p); typedef enum { CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */ CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ CODER_STATUS_NOT_FINISHED, /* stream was not finished */ CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */ } ECoderStatus; typedef enum { CODER_FINISH_ANY, /* finish at any point */ CODER_FINISH_END /* block must be finished at the end */ } ECoderFinishMode; typedef struct _IStateCoder { void *p; void (*Free)(void *p, ISzAlloc *alloc); SRes (*SetProps)(void *p, const Byte *props, size_t propSize, ISzAlloc *alloc); void (*Init)(void *p); SRes (*Code)(void *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished); } IStateCoder; #define MIXCODER_NUM_FILTERS_MAX 4 typedef struct { ISzAlloc *alloc; Byte *buf; int numCoders; int finished[MIXCODER_NUM_FILTERS_MAX - 1]; size_t pos[MIXCODER_NUM_FILTERS_MAX - 1]; size_t size[MIXCODER_NUM_FILTERS_MAX - 1]; UInt64 ids[MIXCODER_NUM_FILTERS_MAX]; IStateCoder coders[MIXCODER_NUM_FILTERS_MAX]; } CMixCoder; void MixCoder_Construct(CMixCoder *p, ISzAlloc *alloc); void MixCoder_Free(CMixCoder *p); void MixCoder_Init(CMixCoder *p); SRes MixCoder_SetFromMethod(CMixCoder *p, int coderIndex, UInt64 methodId); SRes MixCoder_Code(CMixCoder *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, ECoderStatus *status); typedef enum { XZ_STATE_STREAM_HEADER, XZ_STATE_STREAM_INDEX, XZ_STATE_STREAM_INDEX_CRC, XZ_STATE_STREAM_FOOTER, XZ_STATE_STREAM_PADDING, XZ_STATE_BLOCK_HEADER, XZ_STATE_BLOCK, XZ_STATE_BLOCK_FOOTER } EXzState; typedef struct { EXzState state; UInt32 pos; unsigned alignPos; unsigned indexPreSize; CXzStreamFlags streamFlags; UInt32 blockHeaderSize; UInt64 packSize; UInt64 unpackSize; UInt64 numBlocks; UInt64 indexSize; UInt64 indexPos; UInt64 padSize; UInt64 numStartedStreams; UInt64 numFinishedStreams; UInt64 numTotalBlocks; UInt32 crc; CMixCoder decoder; CXzBlock block; CXzCheck check; CSha256 sha; Byte shaDigest[SHA256_DIGEST_SIZE]; Byte buf[XZ_BLOCK_HEADER_SIZE_MAX]; } CXzUnpacker; void XzUnpacker_Construct(CXzUnpacker *p, ISzAlloc *alloc); void XzUnpacker_Init(CXzUnpacker *p); void XzUnpacker_Free(CXzUnpacker *p); /* finishMode: It has meaning only if the decoding reaches output limit (*destLen). CODER_FINISH_ANY - use smallest number of input bytes CODER_FINISH_END - read EndOfStream marker after decoding Returns: SZ_OK status: CODER_STATUS_NOT_FINISHED, CODER_STATUS_NEEDS_MORE_INPUT - maybe there are more xz streams, call XzUnpacker_IsStreamWasFinished to check that current stream was finished SZ_ERROR_MEM - Memory allocation error SZ_ERROR_DATA - Data error SZ_ERROR_UNSUPPORTED - Unsupported method or method properties SZ_ERROR_CRC - CRC error // SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). SZ_ERROR_NO_ARCHIVE - the error with xz Stream Header with one of the following reasons: - xz Stream Signature failure - CRC32 of xz Stream Header is failed - The size of Stream padding is not multiple of four bytes. It's possible to get that error, if xz stream was finished and the stream contains some another data. In that case you can call XzUnpacker_GetExtraSize() function to get real size of xz stream. */ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ECoderFinishMode finishMode, ECoderStatus *status); Bool XzUnpacker_IsStreamWasFinished(CXzUnpacker *p); /* Call XzUnpacker_GetExtraSize after XzUnpacker_Code function to detect real size of xz stream in two cases: XzUnpacker_Code() returns: res == SZ_OK && status == CODER_STATUS_NEEDS_MORE_INPUT res == SZ_ERROR_NO_ARCHIVE */ UInt64 XzUnpacker_GetExtraSize(CXzUnpacker *p); EXTERN_C_END #endif src/libs/7zip/unix/C/XzCrc64.c000066400000000000000000000042031325366651500162160ustar00rootroot00000000000000/* XzCrc64.c -- CRC64 calculation 2011-06-28 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "XzCrc64.h" #include "CpuArch.h" #define kCrc64Poly UINT64_CONST(0xC96C5795D7870F42) #ifdef MY_CPU_LE #define CRC_NUM_TABLES 4 #else #define CRC_NUM_TABLES 5 #define CRC_UINT64_SWAP(v) \ ((v >> 56) | \ ((v >> 40) & ((UInt64)0xFF << 8)) | \ ((v >> 24) & ((UInt64)0xFF << 16)) | \ ((v >> 8) & ((UInt64)0xFF << 24)) | \ ((v << 8) & ((UInt64)0xFF << 32)) | \ ((v << 24) & ((UInt64)0xFF << 40)) | \ ((v << 40) & ((UInt64)0xFF << 48)) | \ (v << 56)) UInt64 MY_FAST_CALL XzCrc64UpdateT1_BeT4(UInt64 v, const void *data, size_t size, const UInt64 *table); #endif #ifndef MY_CPU_BE UInt64 MY_FAST_CALL XzCrc64UpdateT4(UInt64 v, const void *data, size_t size, const UInt64 *table); #endif typedef UInt64 (MY_FAST_CALL *CRC_FUNC)(UInt64 v, const void *data, size_t size, const UInt64 *table); static CRC_FUNC g_Crc64Update; UInt64 g_Crc64Table[256 * CRC_NUM_TABLES]; UInt64 MY_FAST_CALL Crc64Update(UInt64 v, const void *data, size_t size) { return g_Crc64Update(v, data, size, g_Crc64Table); } UInt64 MY_FAST_CALL Crc64Calc(const void *data, size_t size) { return g_Crc64Update(CRC64_INIT_VAL, data, size, g_Crc64Table) ^ CRC64_INIT_VAL; } void MY_FAST_CALL Crc64GenerateTable() { UInt32 i; for (i = 0; i < 256; i++) { UInt64 r = i; unsigned j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrc64Poly & ~((r & 1) - 1)); g_Crc64Table[i] = r; } for (; i < 256 * CRC_NUM_TABLES; i++) { UInt64 r = g_Crc64Table[i - 256]; g_Crc64Table[i] = g_Crc64Table[r & 0xFF] ^ (r >> 8); } #ifdef MY_CPU_LE g_Crc64Update = XzCrc64UpdateT4; #else { #ifndef MY_CPU_BE UInt32 k = 1; if (*(const Byte *)&k == 1) g_Crc64Update = XzCrc64UpdateT4; else #endif { for (i = 256 * CRC_NUM_TABLES - 1; i >= 256; i--) { UInt64 x = g_Crc64Table[i - 256]; g_Crc64Table[i] = CRC_UINT64_SWAP(x); } g_Crc64Update = XzCrc64UpdateT1_BeT4; } } #endif } src/libs/7zip/unix/C/XzCrc64.h000066400000000000000000000012011325366651500162160ustar00rootroot00000000000000/* XzCrc64.h -- CRC64 calculation 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __XZ_CRC64_H #define __XZ_CRC64_H #include #include "7zTypes.h" EXTERN_C_BEGIN extern UInt64 g_Crc64Table[]; void MY_FAST_CALL Crc64GenerateTable(void); #define CRC64_INIT_VAL UINT64_CONST(0xFFFFFFFFFFFFFFFF) #define CRC64_GET_DIGEST(crc) ((crc) ^ CRC64_INIT_VAL) #define CRC64_UPDATE_BYTE(crc, b) (g_Crc64Table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) UInt64 MY_FAST_CALL Crc64Update(UInt64 crc, const void *data, size_t size); UInt64 MY_FAST_CALL Crc64Calc(const void *data, size_t size); EXTERN_C_END #endif src/libs/7zip/unix/C/XzCrc64Opt.c000066400000000000000000000036111325366651500167030ustar00rootroot00000000000000/* XzCrc64Opt.c -- CRC64 calculation 2011-06-28 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "CpuArch.h" #define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) #ifndef MY_CPU_BE UInt64 MY_FAST_CALL XzCrc64UpdateT4(UInt64 v, const void *data, size_t size, const UInt64 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); for (; size >= 4; size -= 4, p += 4) { UInt32 d = (UInt32)v ^ *(const UInt32 *)p; v = (v >> 32) ^ table[0x300 + ((d ) & 0xFF)] ^ table[0x200 + ((d >> 8) & 0xFF)] ^ table[0x100 + ((d >> 16) & 0xFF)] ^ table[0x000 + ((d >> 24))]; } for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } #endif #ifndef MY_CPU_LE #define CRC_UINT64_SWAP(v) \ ((v >> 56) | \ ((v >> 40) & ((UInt64)0xFF << 8)) | \ ((v >> 24) & ((UInt64)0xFF << 16)) | \ ((v >> 8) & ((UInt64)0xFF << 24)) | \ ((v << 8) & ((UInt64)0xFF << 32)) | \ ((v << 24) & ((UInt64)0xFF << 40)) | \ ((v << 40) & ((UInt64)0xFF << 48)) | \ (v << 56)) UInt64 MY_FAST_CALL XzCrc64UpdateT1_BeT4(UInt64 v, const void *data, size_t size, const UInt64 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); v = CRC_UINT64_SWAP(v); table += 0x100; for (; size >= 4; size -= 4, p += 4) { UInt32 d = (UInt32)(v >> 32) ^ *(const UInt32 *)p; v = (v << 32) ^ table[0x000 + ((d ) & 0xFF)] ^ table[0x100 + ((d >> 8) & 0xFF)] ^ table[0x200 + ((d >> 16) & 0xFF)] ^ table[0x300 + ((d >> 24))]; } table -= 0x100; v = CRC_UINT64_SWAP(v); for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } #endif src/libs/7zip/unix/C/XzDec.c000066400000000000000000000554771325366651500160530ustar00rootroot00000000000000/* XzDec.c -- Xz Decode 2014-12-30 : Igor Pavlov : Public domain */ #include "Precomp.h" /* #define XZ_DUMP */ #ifdef XZ_DUMP #include #endif #include #include #include "7zCrc.h" #include "Alloc.h" #include "Bra.h" #include "CpuArch.h" #include "Delta.h" #include "Lzma2Dec.h" #ifdef USE_SUBBLOCK #include "Bcj3Dec.c" #include "SbDec.c" #endif #include "Xz.h" #define XZ_CHECK_SIZE_MAX 64 #define CODER_BUF_SIZE (1 << 17) unsigned Xz_ReadVarInt(const Byte *p, size_t maxSize, UInt64 *value) { int i, limit; *value = 0; limit = (maxSize > 9) ? 9 : (int)maxSize; for (i = 0; i < limit;) { Byte b = p[i]; *value |= (UInt64)(b & 0x7F) << (7 * i++); if ((b & 0x80) == 0) return (b == 0 && i != 1) ? 0 : i; } return 0; } /* ---------- BraState ---------- */ #define BRA_BUF_SIZE (1 << 14) typedef struct { size_t bufPos; size_t bufConv; size_t bufTotal; UInt32 methodId; int encodeMode; UInt32 delta; UInt32 ip; UInt32 x86State; Byte deltaState[DELTA_STATE_SIZE]; Byte buf[BRA_BUF_SIZE]; } CBraState; void BraState_Free(void *pp, ISzAlloc *alloc) { alloc->Free(alloc, pp); } SRes BraState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { CBraState *p = ((CBraState *)pp); alloc = alloc; p->ip = 0; if (p->methodId == XZ_ID_Delta) { if (propSize != 1) return SZ_ERROR_UNSUPPORTED; p->delta = (unsigned)props[0] + 1; } else { if (propSize == 4) { UInt32 v = GetUi32(props); switch(p->methodId) { case XZ_ID_PPC: case XZ_ID_ARM: case XZ_ID_SPARC: if ((v & 3) != 0) return SZ_ERROR_UNSUPPORTED; break; case XZ_ID_ARMT: if ((v & 1) != 0) return SZ_ERROR_UNSUPPORTED; break; case XZ_ID_IA64: if ((v & 0xF) != 0) return SZ_ERROR_UNSUPPORTED; break; } p->ip = v; } else if (propSize != 0) return SZ_ERROR_UNSUPPORTED; } return SZ_OK; } void BraState_Init(void *pp) { CBraState *p = ((CBraState *)pp); p->bufPos = p->bufConv = p->bufTotal = 0; x86_Convert_Init(p->x86State); if (p->methodId == XZ_ID_Delta) Delta_Init(p->deltaState); } #define CASE_BRA_CONV(isa) case XZ_ID_ ## isa: p->bufConv = isa ## _Convert(p->buf, p->bufTotal, p->ip, p->encodeMode); break; static SRes BraState_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { CBraState *p = ((CBraState *)pp); SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; *destLen = 0; *srcLen = 0; finishMode = finishMode; *wasFinished = 0; while (destLenOrig > 0) { if (p->bufPos != p->bufConv) { size_t curSize = p->bufConv - p->bufPos; if (curSize > destLenOrig) curSize = destLenOrig; memcpy(dest, p->buf + p->bufPos, curSize); p->bufPos += curSize; *destLen += curSize; dest += curSize; destLenOrig -= curSize; continue; } p->bufTotal -= p->bufPos; memmove(p->buf, p->buf + p->bufPos, p->bufTotal); p->bufPos = 0; p->bufConv = 0; { size_t curSize = BRA_BUF_SIZE - p->bufTotal; if (curSize > srcLenOrig) curSize = srcLenOrig; memcpy(p->buf + p->bufTotal, src, curSize); *srcLen += curSize; src += curSize; srcLenOrig -= curSize; p->bufTotal += curSize; } if (p->bufTotal == 0) break; switch(p->methodId) { case XZ_ID_Delta: if (p->encodeMode) Delta_Encode(p->deltaState, p->delta, p->buf, p->bufTotal); else Delta_Decode(p->deltaState, p->delta, p->buf, p->bufTotal); p->bufConv = p->bufTotal; break; case XZ_ID_X86: p->bufConv = x86_Convert(p->buf, p->bufTotal, p->ip, &p->x86State, p->encodeMode); break; CASE_BRA_CONV(PPC) CASE_BRA_CONV(IA64) CASE_BRA_CONV(ARM) CASE_BRA_CONV(ARMT) CASE_BRA_CONV(SPARC) default: return SZ_ERROR_UNSUPPORTED; } p->ip += (UInt32)p->bufConv; if (p->bufConv == 0) { if (!srcWasFinished) break; p->bufConv = p->bufTotal; } } if (p->bufTotal == p->bufPos && srcLenOrig == 0 && srcWasFinished) *wasFinished = 1; return SZ_OK; } SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAlloc *alloc) { CBraState *decoder; if (id != XZ_ID_Delta && id != XZ_ID_X86 && id != XZ_ID_PPC && id != XZ_ID_IA64 && id != XZ_ID_ARM && id != XZ_ID_ARMT && id != XZ_ID_SPARC) return SZ_ERROR_UNSUPPORTED; p->p = 0; decoder = (CBraState *)alloc->Alloc(alloc, sizeof(CBraState)); if (decoder == 0) return SZ_ERROR_MEM; decoder->methodId = (UInt32)id; decoder->encodeMode = encodeMode; p->p = decoder; p->Free = BraState_Free; p->SetProps = BraState_SetProps; p->Init = BraState_Init; p->Code = BraState_Code; return SZ_OK; } /* ---------- SbState ---------- */ #ifdef USE_SUBBLOCK static void SbState_Free(void *pp, ISzAlloc *alloc) { CSbDec *p = (CSbDec *)pp; SbDec_Free(p); alloc->Free(alloc, pp); } static SRes SbState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { pp = pp; props = props; alloc = alloc; return (propSize == 0) ? SZ_OK : SZ_ERROR_UNSUPPORTED; } static void SbState_Init(void *pp) { SbDec_Init((CSbDec *)pp); } static SRes SbState_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { CSbDec *p = (CSbDec *)pp; SRes res; srcWasFinished = srcWasFinished; p->dest = dest; p->destLen = *destLen; p->src = src; p->srcLen = *srcLen; p->finish = finishMode; /* change it */ res = SbDec_Decode((CSbDec *)pp); *destLen -= p->destLen; *srcLen -= p->srcLen; *wasFinished = (*destLen == 0 && *srcLen == 0); /* change it */ return res; } SRes SbState_SetFromMethod(IStateCoder *p, ISzAlloc *alloc) { CSbDec *decoder; p->p = 0; decoder = alloc->Alloc(alloc, sizeof(CSbDec)); if (decoder == 0) return SZ_ERROR_MEM; p->p = decoder; p->Free = SbState_Free; p->SetProps = SbState_SetProps; p->Init = SbState_Init; p->Code = SbState_Code; SbDec_Construct(decoder); SbDec_SetAlloc(decoder, alloc); return SZ_OK; } #endif /* ---------- Lzma2State ---------- */ static void Lzma2State_Free(void *pp, ISzAlloc *alloc) { Lzma2Dec_Free((CLzma2Dec *)pp, alloc); alloc->Free(alloc, pp); } static SRes Lzma2State_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { if (propSize != 1) return SZ_ERROR_UNSUPPORTED; return Lzma2Dec_Allocate((CLzma2Dec *)pp, props[0], alloc); } static void Lzma2State_Init(void *pp) { Lzma2Dec_Init((CLzma2Dec *)pp); } static SRes Lzma2State_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { ELzmaStatus status; /* ELzmaFinishMode fm = (finishMode == LZMA_FINISH_ANY) ? LZMA_FINISH_ANY : LZMA_FINISH_END; */ SRes res = Lzma2Dec_DecodeToBuf((CLzma2Dec *)pp, dest, destLen, src, srcLen, (ELzmaFinishMode)finishMode, &status); srcWasFinished = srcWasFinished; *wasFinished = (status == LZMA_STATUS_FINISHED_WITH_MARK); return res; } static SRes Lzma2State_SetFromMethod(IStateCoder *p, ISzAlloc *alloc) { CLzma2Dec *decoder = (CLzma2Dec *)alloc->Alloc(alloc, sizeof(CLzma2Dec)); p->p = decoder; if (decoder == 0) return SZ_ERROR_MEM; p->Free = Lzma2State_Free; p->SetProps = Lzma2State_SetProps; p->Init = Lzma2State_Init; p->Code = Lzma2State_Code; Lzma2Dec_Construct(decoder); return SZ_OK; } void MixCoder_Construct(CMixCoder *p, ISzAlloc *alloc) { int i; p->alloc = alloc; p->buf = 0; p->numCoders = 0; for (i = 0; i < MIXCODER_NUM_FILTERS_MAX; i++) p->coders[i].p = NULL; } void MixCoder_Free(CMixCoder *p) { int i; for (i = 0; i < p->numCoders; i++) { IStateCoder *sc = &p->coders[i]; if (p->alloc && sc->p) sc->Free(sc->p, p->alloc); } p->numCoders = 0; if (p->buf) { p->alloc->Free(p->alloc, p->buf); p->buf = 0; /* 9.31: the BUG was fixed */ } } void MixCoder_Init(CMixCoder *p) { int i; for (i = 0; i < p->numCoders - 1; i++) { p->size[i] = 0; p->pos[i] = 0; p->finished[i] = 0; } for (i = 0; i < p->numCoders; i++) { IStateCoder *coder = &p->coders[i]; coder->Init(coder->p); } } SRes MixCoder_SetFromMethod(CMixCoder *p, int coderIndex, UInt64 methodId) { IStateCoder *sc = &p->coders[coderIndex]; p->ids[coderIndex] = methodId; switch(methodId) { case XZ_ID_LZMA2: return Lzma2State_SetFromMethod(sc, p->alloc); #ifdef USE_SUBBLOCK case XZ_ID_Subblock: return SbState_SetFromMethod(sc, p->alloc); #endif } if (coderIndex == 0) return SZ_ERROR_UNSUPPORTED; return BraState_SetFromMethod(sc, methodId, 0, p->alloc); } SRes MixCoder_Code(CMixCoder *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, ECoderStatus *status) { SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; Bool allFinished = True; *destLen = 0; *srcLen = 0; *status = CODER_STATUS_NOT_FINISHED; if (p->buf == 0) { p->buf = (Byte *)p->alloc->Alloc(p->alloc, CODER_BUF_SIZE * (MIXCODER_NUM_FILTERS_MAX - 1)); if (p->buf == 0) return SZ_ERROR_MEM; } if (p->numCoders != 1) finishMode = CODER_FINISH_ANY; for (;;) { Bool processed = False; int i; /* if (p->numCoders == 1 && *destLen == destLenOrig && finishMode == LZMA_FINISH_ANY) break; */ for (i = 0; i < p->numCoders; i++) { SRes res; IStateCoder *coder = &p->coders[i]; Byte *destCur; SizeT destLenCur, srcLenCur; const Byte *srcCur; int srcFinishedCur; int encodingWasFinished; if (i == 0) { srcCur = src; srcLenCur = srcLenOrig - *srcLen; srcFinishedCur = srcWasFinished; } else { srcCur = p->buf + (CODER_BUF_SIZE * (i - 1)) + p->pos[i - 1]; srcLenCur = p->size[i - 1] - p->pos[i - 1]; srcFinishedCur = p->finished[i - 1]; } if (i == p->numCoders - 1) { destCur = dest; destLenCur = destLenOrig - *destLen; } else { if (p->pos[i] != p->size[i]) continue; destCur = p->buf + (CODER_BUF_SIZE * i); destLenCur = CODER_BUF_SIZE; } res = coder->Code(coder->p, destCur, &destLenCur, srcCur, &srcLenCur, srcFinishedCur, finishMode, &encodingWasFinished); if (!encodingWasFinished) allFinished = False; if (i == 0) { *srcLen += srcLenCur; src += srcLenCur; } else { p->pos[i - 1] += srcLenCur; } if (i == p->numCoders - 1) { *destLen += destLenCur; dest += destLenCur; } else { p->size[i] = destLenCur; p->pos[i] = 0; p->finished[i] = encodingWasFinished; } if (res != SZ_OK) return res; if (destLenCur != 0 || srcLenCur != 0) processed = True; } if (!processed) break; } if (allFinished) *status = CODER_STATUS_FINISHED_WITH_MARK; return SZ_OK; } SRes Xz_ParseHeader(CXzStreamFlags *p, const Byte *buf) { *p = (CXzStreamFlags)GetBe16(buf + XZ_SIG_SIZE); if (CrcCalc(buf + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE) != GetUi32(buf + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE)) return SZ_ERROR_NO_ARCHIVE; return XzFlags_IsSupported(*p) ? SZ_OK : SZ_ERROR_UNSUPPORTED; } static Bool Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *buf) { return indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2) && (GetUi32(buf) == CrcCalc(buf + 4, 6) && flags == GetBe16(buf + 8) && memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0); } #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } SRes XzBlock_Parse(CXzBlock *p, const Byte *header) { unsigned pos; int numFilters, i; UInt32 headerSize = (UInt32)header[0] << 2; if (CrcCalc(header, headerSize) != GetUi32(header + headerSize)) return SZ_ERROR_ARCHIVE; pos = 1; if (pos == headerSize) return SZ_ERROR_ARCHIVE; p->flags = header[pos++]; if (XzBlock_HasPackSize(p)) { READ_VARINT_AND_CHECK(header, pos, headerSize, &p->packSize); if (p->packSize == 0 || p->packSize + headerSize >= (UInt64)1 << 63) return SZ_ERROR_ARCHIVE; } if (XzBlock_HasUnpackSize(p)) READ_VARINT_AND_CHECK(header, pos, headerSize, &p->unpackSize); numFilters = XzBlock_GetNumFilters(p); for (i = 0; i < numFilters; i++) { CXzFilter *filter = p->filters + i; UInt64 size; READ_VARINT_AND_CHECK(header, pos, headerSize, &filter->id); READ_VARINT_AND_CHECK(header, pos, headerSize, &size); if (size > headerSize - pos || size > XZ_FILTER_PROPS_SIZE_MAX) return SZ_ERROR_ARCHIVE; filter->propsSize = (UInt32)size; memcpy(filter->props, header + pos, (size_t)size); pos += (unsigned)size; #ifdef XZ_DUMP printf("\nf[%d] = %2X: ", i, filter->id); { int i; for (i = 0; i < size; i++) printf(" %2X", filter->props[i]); } #endif } while (pos < headerSize) if (header[pos++] != 0) return SZ_ERROR_ARCHIVE; return SZ_OK; } SRes XzDec_Init(CMixCoder *p, const CXzBlock *block) { int i; Bool needReInit = True; int numFilters = XzBlock_GetNumFilters(block); if (numFilters == p->numCoders) { for (i = 0; i < numFilters; i++) if (p->ids[i] != block->filters[numFilters - 1 - i].id) break; needReInit = (i != numFilters); } if (needReInit) { MixCoder_Free(p); p->numCoders = numFilters; for (i = 0; i < numFilters; i++) { const CXzFilter *f = &block->filters[numFilters - 1 - i]; RINOK(MixCoder_SetFromMethod(p, i, f->id)); } } for (i = 0; i < numFilters; i++) { const CXzFilter *f = &block->filters[numFilters - 1 - i]; IStateCoder *sc = &p->coders[i]; RINOK(sc->SetProps(sc->p, f->props, f->propsSize, p->alloc)); } MixCoder_Init(p); return SZ_OK; } void XzUnpacker_Init(CXzUnpacker *p) { p->state = XZ_STATE_STREAM_HEADER; p->pos = 0; p->numStartedStreams = 0; p->numFinishedStreams = 0; p->numTotalBlocks = 0; p->padSize = 0; } void XzUnpacker_Construct(CXzUnpacker *p, ISzAlloc *alloc) { MixCoder_Construct(&p->decoder, alloc); XzUnpacker_Init(p); } void XzUnpacker_Free(CXzUnpacker *p) { MixCoder_Free(&p->decoder); } SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ECoderFinishMode finishMode, ECoderStatus *status) { SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; *destLen = 0; *srcLen = 0; *status = CODER_STATUS_NOT_SPECIFIED; for (;;) { SizeT srcRem = srcLenOrig - *srcLen; if (p->state == XZ_STATE_BLOCK) { SizeT destLen2 = destLenOrig - *destLen; SizeT srcLen2 = srcLenOrig - *srcLen; SRes res; if (srcLen2 == 0 && destLen2 == 0) { *status = CODER_STATUS_NOT_FINISHED; return SZ_OK; } res = MixCoder_Code(&p->decoder, dest, &destLen2, src, &srcLen2, False, finishMode, status); XzCheck_Update(&p->check, dest, destLen2); (*srcLen) += srcLen2; src += srcLen2; p->packSize += srcLen2; (*destLen) += destLen2; dest += destLen2; p->unpackSize += destLen2; RINOK(res); if (*status == CODER_STATUS_FINISHED_WITH_MARK) { Byte temp[32]; unsigned num = Xz_WriteVarInt(temp, p->packSize + p->blockHeaderSize + XzFlags_GetCheckSize(p->streamFlags)); num += Xz_WriteVarInt(temp + num, p->unpackSize); Sha256_Update(&p->sha, temp, num); p->indexSize += num; p->numBlocks++; p->state = XZ_STATE_BLOCK_FOOTER; p->pos = 0; p->alignPos = 0; } else if (srcLen2 == 0 && destLen2 == 0) return SZ_OK; continue; } if (srcRem == 0) { *status = CODER_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } switch (p->state) { case XZ_STATE_STREAM_HEADER: { if (p->pos < XZ_STREAM_HEADER_SIZE) { if (p->pos < XZ_SIG_SIZE && *src != XZ_SIG[p->pos]) return SZ_ERROR_NO_ARCHIVE; p->buf[p->pos++] = *src++; (*srcLen)++; } else { RINOK(Xz_ParseHeader(&p->streamFlags, p->buf)); p->numStartedStreams++; p->state = XZ_STATE_BLOCK_HEADER; Sha256_Init(&p->sha); p->indexSize = 0; p->numBlocks = 0; p->pos = 0; } break; } case XZ_STATE_BLOCK_HEADER: { if (p->pos == 0) { p->buf[p->pos++] = *src++; (*srcLen)++; if (p->buf[0] == 0) { p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks); p->indexPos = p->indexPreSize; p->indexSize += p->indexPreSize; Sha256_Final(&p->sha, p->shaDigest); Sha256_Init(&p->sha); p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize); p->state = XZ_STATE_STREAM_INDEX; } p->blockHeaderSize = ((UInt32)p->buf[0] << 2) + 4; } else if (p->pos != p->blockHeaderSize) { UInt32 cur = p->blockHeaderSize - p->pos; if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; } else { RINOK(XzBlock_Parse(&p->block, p->buf)); p->numTotalBlocks++; p->state = XZ_STATE_BLOCK; p->packSize = 0; p->unpackSize = 0; XzCheck_Init(&p->check, XzFlags_GetCheckType(p->streamFlags)); RINOK(XzDec_Init(&p->decoder, &p->block)); } break; } case XZ_STATE_BLOCK_FOOTER: { if (((p->packSize + p->alignPos) & 3) != 0) { (*srcLen)++; p->alignPos++; if (*src++ != 0) return SZ_ERROR_CRC; } else { UInt32 checkSize = XzFlags_GetCheckSize(p->streamFlags); UInt32 cur = checkSize - p->pos; if (cur != 0) { if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; } else { Byte digest[XZ_CHECK_SIZE_MAX]; p->state = XZ_STATE_BLOCK_HEADER; p->pos = 0; if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0) return SZ_ERROR_CRC; } } break; } case XZ_STATE_STREAM_INDEX: { if (p->pos < p->indexPreSize) { (*srcLen)++; if (*src++ != p->buf[p->pos++]) return SZ_ERROR_CRC; } else { if (p->indexPos < p->indexSize) { UInt64 cur = p->indexSize - p->indexPos; if (srcRem > cur) srcRem = (SizeT)cur; p->crc = CrcUpdate(p->crc, src, srcRem); Sha256_Update(&p->sha, src, srcRem); (*srcLen) += srcRem; src += srcRem; p->indexPos += srcRem; } else if ((p->indexPos & 3) != 0) { Byte b = *src++; p->crc = CRC_UPDATE_BYTE(p->crc, b); (*srcLen)++; p->indexPos++; p->indexSize++; if (b != 0) return SZ_ERROR_CRC; } else { Byte digest[SHA256_DIGEST_SIZE]; p->state = XZ_STATE_STREAM_INDEX_CRC; p->indexSize += 4; p->pos = 0; Sha256_Final(&p->sha, digest); if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0) return SZ_ERROR_CRC; } } break; } case XZ_STATE_STREAM_INDEX_CRC: { if (p->pos < 4) { (*srcLen)++; p->buf[p->pos++] = *src++; } else { p->state = XZ_STATE_STREAM_FOOTER; p->pos = 0; if (CRC_GET_DIGEST(p->crc) != GetUi32(p->buf)) return SZ_ERROR_CRC; } break; } case XZ_STATE_STREAM_FOOTER: { UInt32 cur = XZ_STREAM_FOOTER_SIZE - p->pos; if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; if (p->pos == XZ_STREAM_FOOTER_SIZE) { p->state = XZ_STATE_STREAM_PADDING; p->numFinishedStreams++; p->padSize = 0; if (!Xz_CheckFooter(p->streamFlags, p->indexSize, p->buf)) return SZ_ERROR_CRC; } break; } case XZ_STATE_STREAM_PADDING: { if (*src != 0) { if (((UInt32)p->padSize & 3) != 0) return SZ_ERROR_NO_ARCHIVE; p->pos = 0; p->state = XZ_STATE_STREAM_HEADER; } else { (*srcLen)++; src++; p->padSize++; } break; } case XZ_STATE_BLOCK: break; /* to disable GCC warning */ } } /* if (p->state == XZ_STATE_FINISHED) *status = CODER_STATUS_FINISHED_WITH_MARK; return SZ_OK; */ } Bool XzUnpacker_IsStreamWasFinished(CXzUnpacker *p) { return (p->state == XZ_STATE_STREAM_PADDING) && (((UInt32)p->padSize & 3) == 0); } UInt64 XzUnpacker_GetExtraSize(CXzUnpacker *p) { UInt64 num = 0; if (p->state == XZ_STATE_STREAM_PADDING) num += p->padSize; else if (p->state == XZ_STATE_STREAM_HEADER) num += p->padSize + p->pos; return num; } src/libs/7zip/unix/C/XzEnc.c000066400000000000000000000325571325366651500160570ustar00rootroot00000000000000/* XzEnc.c -- Xz Encode 2014-12-30 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include #include "7zCrc.h" #include "Alloc.h" #include "Bra.h" #include "CpuArch.h" #ifdef USE_SUBBLOCK #include "Bcj3Enc.c" #include "SbFind.c" #include "SbEnc.c" #endif #include "XzEnc.h" static void *SzBigAlloc(void *p, size_t size) { p = p; return BigAlloc(size); } static void SzBigFree(void *p, void *address) { p = p; BigFree(address); } static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } static void SzFree(void *p, void *address) { p = p; MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; #define XzBlock_ClearFlags(p) (p)->flags = 0; #define XzBlock_SetNumFilters(p, n) (p)->flags |= ((n) - 1); #define XzBlock_SetHasPackSize(p) (p)->flags |= XZ_BF_PACK_SIZE; #define XzBlock_SetHasUnpackSize(p) (p)->flags |= XZ_BF_UNPACK_SIZE; static SRes WriteBytes(ISeqOutStream *s, const void *buf, UInt32 size) { return (s->Write(s, buf, size) == size) ? SZ_OK : SZ_ERROR_WRITE; } static SRes WriteBytesAndCrc(ISeqOutStream *s, const void *buf, UInt32 size, UInt32 *crc) { *crc = CrcUpdate(*crc, buf, size); return WriteBytes(s, buf, size); } SRes Xz_WriteHeader(CXzStreamFlags f, ISeqOutStream *s) { UInt32 crc; Byte header[XZ_STREAM_HEADER_SIZE]; memcpy(header, XZ_SIG, XZ_SIG_SIZE); header[XZ_SIG_SIZE] = (Byte)(f >> 8); header[XZ_SIG_SIZE + 1] = (Byte)(f & 0xFF); crc = CrcCalc(header + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE); SetUi32(header + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE, crc); return WriteBytes(s, header, XZ_STREAM_HEADER_SIZE); } SRes XzBlock_WriteHeader(const CXzBlock *p, ISeqOutStream *s) { Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; unsigned pos = 1; int numFilters, i; header[pos++] = p->flags; if (XzBlock_HasPackSize(p)) pos += Xz_WriteVarInt(header + pos, p->packSize); if (XzBlock_HasUnpackSize(p)) pos += Xz_WriteVarInt(header + pos, p->unpackSize); numFilters = XzBlock_GetNumFilters(p); for (i = 0; i < numFilters; i++) { const CXzFilter *f = &p->filters[i]; pos += Xz_WriteVarInt(header + pos, f->id); pos += Xz_WriteVarInt(header + pos, f->propsSize); memcpy(header + pos, f->props, f->propsSize); pos += f->propsSize; } while((pos & 3) != 0) header[pos++] = 0; header[0] = (Byte)(pos >> 2); SetUi32(header + pos, CrcCalc(header, pos)); return WriteBytes(s, header, pos + 4); } SRes Xz_WriteFooter(CXzStream *p, ISeqOutStream *s) { Byte buf[32]; UInt64 globalPos; { UInt32 crc = CRC_INIT_VAL; unsigned pos = 1 + Xz_WriteVarInt(buf + 1, p->numBlocks); size_t i; globalPos = pos; buf[0] = 0; RINOK(WriteBytesAndCrc(s, buf, pos, &crc)); for (i = 0; i < p->numBlocks; i++) { const CXzBlockSizes *block = &p->blocks[i]; pos = Xz_WriteVarInt(buf, block->totalSize); pos += Xz_WriteVarInt(buf + pos, block->unpackSize); globalPos += pos; RINOK(WriteBytesAndCrc(s, buf, pos, &crc)); } pos = ((unsigned)globalPos & 3); if (pos != 0) { buf[0] = buf[1] = buf[2] = 0; RINOK(WriteBytesAndCrc(s, buf, 4 - pos, &crc)); globalPos += 4 - pos; } { SetUi32(buf, CRC_GET_DIGEST(crc)); RINOK(WriteBytes(s, buf, 4)); globalPos += 4; } } { UInt32 indexSize = (UInt32)((globalPos >> 2) - 1); SetUi32(buf + 4, indexSize); buf[8] = (Byte)(p->flags >> 8); buf[9] = (Byte)(p->flags & 0xFF); SetUi32(buf, CrcCalc(buf + 4, 6)); memcpy(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE); return WriteBytes(s, buf, 12); } } SRes Xz_AddIndexRecord(CXzStream *p, UInt64 unpackSize, UInt64 totalSize, ISzAlloc *alloc) { if (p->blocks == 0 || p->numBlocksAllocated == p->numBlocks) { size_t num = (p->numBlocks + 1) * 2; size_t newSize = sizeof(CXzBlockSizes) * num; CXzBlockSizes *blocks; if (newSize / sizeof(CXzBlockSizes) != num) return SZ_ERROR_MEM; blocks = (CXzBlockSizes *)alloc->Alloc(alloc, newSize); if (blocks == 0) return SZ_ERROR_MEM; if (p->numBlocks != 0) { memcpy(blocks, p->blocks, p->numBlocks * sizeof(CXzBlockSizes)); Xz_Free(p, alloc); } p->blocks = blocks; p->numBlocksAllocated = num; } { CXzBlockSizes *block = &p->blocks[p->numBlocks++]; block->totalSize = totalSize; block->unpackSize = unpackSize; } return SZ_OK; } /* ---------- CSeqCheckInStream ---------- */ typedef struct { ISeqInStream p; ISeqInStream *realStream; UInt64 processed; CXzCheck check; } CSeqCheckInStream; void SeqCheckInStream_Init(CSeqCheckInStream *p, int mode) { p->processed = 0; XzCheck_Init(&p->check, mode); } void SeqCheckInStream_GetDigest(CSeqCheckInStream *p, Byte *digest) { XzCheck_Final(&p->check, digest); } static SRes SeqCheckInStream_Read(void *pp, void *data, size_t *size) { CSeqCheckInStream *p = (CSeqCheckInStream *)pp; SRes res = p->realStream->Read(p->realStream, data, size); XzCheck_Update(&p->check, data, *size); p->processed += *size; return res; } /* ---------- CSeqSizeOutStream ---------- */ typedef struct { ISeqOutStream p; ISeqOutStream *realStream; UInt64 processed; } CSeqSizeOutStream; static size_t MyWrite(void *pp, const void *data, size_t size) { CSeqSizeOutStream *p = (CSeqSizeOutStream *)pp; size = p->realStream->Write(p->realStream, data, size); p->processed += size; return size; } /* ---------- CSeqInFilter ---------- */ #define FILTER_BUF_SIZE (1 << 20) typedef struct { ISeqInStream p; ISeqInStream *realStream; IStateCoder StateCoder; Byte *buf; size_t curPos; size_t endPos; int srcWasFinished; } CSeqInFilter; static SRes SeqInFilter_Read(void *pp, void *data, size_t *size) { CSeqInFilter *p = (CSeqInFilter *)pp; size_t sizeOriginal = *size; if (sizeOriginal == 0) return SZ_OK; *size = 0; for (;;) { if (!p->srcWasFinished && p->curPos == p->endPos) { p->curPos = 0; p->endPos = FILTER_BUF_SIZE; RINOK(p->realStream->Read(p->realStream, p->buf, &p->endPos)); if (p->endPos == 0) p->srcWasFinished = 1; } { SizeT srcLen = p->endPos - p->curPos; int wasFinished; SRes res; *size = sizeOriginal; res = p->StateCoder.Code(p->StateCoder.p, data, size, p->buf + p->curPos, &srcLen, p->srcWasFinished, CODER_FINISH_ANY, &wasFinished); p->curPos += srcLen; if (*size != 0 || srcLen == 0 || res != 0) return res; } } } static void SeqInFilter_Construct(CSeqInFilter *p) { p->buf = NULL; p->p.Read = SeqInFilter_Read; } static void SeqInFilter_Free(CSeqInFilter *p) { if (p->buf) { g_Alloc.Free(&g_Alloc, p->buf); p->buf = NULL; } } SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAlloc *alloc); static SRes SeqInFilter_Init(CSeqInFilter *p, const CXzFilter *props) { if (!p->buf) { p->buf = g_Alloc.Alloc(&g_Alloc, FILTER_BUF_SIZE); if (!p->buf) return SZ_ERROR_MEM; } p->curPos = p->endPos = 0; p->srcWasFinished = 0; RINOK(BraState_SetFromMethod(&p->StateCoder, props->id, 1, &g_Alloc)); RINOK(p->StateCoder.SetProps(p->StateCoder.p, props->props, props->propsSize, &g_Alloc)); p->StateCoder.Init(p->StateCoder.p); return SZ_OK; } /* ---------- CSbEncInStream ---------- */ #ifdef USE_SUBBLOCK typedef struct { ISeqInStream p; ISeqInStream *inStream; CSbEnc enc; } CSbEncInStream; static SRes SbEncInStream_Read(void *pp, void *data, size_t *size) { CSbEncInStream *p = (CSbEncInStream *)pp; size_t sizeOriginal = *size; if (sizeOriginal == 0) return S_OK; for (;;) { if (p->enc.needRead && !p->enc.readWasFinished) { size_t processed = p->enc.needReadSizeMax; RINOK(p->inStream->Read(p->inStream, p->enc.buf + p->enc.readPos, &processed)); p->enc.readPos += processed; if (processed == 0) { p->enc.readWasFinished = True; p->enc.isFinalFinished = True; } p->enc.needRead = False; } *size = sizeOriginal; RINOK(SbEnc_Read(&p->enc, data, size)); if (*size != 0 || !p->enc.needRead) return S_OK; } } void SbEncInStream_Construct(CSbEncInStream *p, ISzAlloc *alloc) { SbEnc_Construct(&p->enc, alloc); p->p.Read = SbEncInStream_Read; } SRes SbEncInStream_Init(CSbEncInStream *p) { return SbEnc_Init(&p->enc); } void SbEncInStream_Free(CSbEncInStream *p) { SbEnc_Free(&p->enc); } #endif typedef struct { CLzma2EncHandle lzma2; #ifdef USE_SUBBLOCK CSbEncInStream sb; #endif CSeqInFilter filter; ISzAlloc *alloc; ISzAlloc *bigAlloc; } CLzma2WithFilters; static void Lzma2WithFilters_Construct(CLzma2WithFilters *p, ISzAlloc *alloc, ISzAlloc *bigAlloc) { p->alloc = alloc; p->bigAlloc = bigAlloc; p->lzma2 = NULL; #ifdef USE_SUBBLOCK SbEncInStream_Construct(&p->sb, alloc); #endif SeqInFilter_Construct(&p->filter); } static SRes Lzma2WithFilters_Create(CLzma2WithFilters *p) { p->lzma2 = Lzma2Enc_Create(p->alloc, p->bigAlloc); if (p->lzma2 == 0) return SZ_ERROR_MEM; return SZ_OK; } static void Lzma2WithFilters_Free(CLzma2WithFilters *p) { SeqInFilter_Free(&p->filter); #ifdef USE_SUBBLOCK SbEncInStream_Free(&p->sb); #endif if (p->lzma2) { Lzma2Enc_Destroy(p->lzma2); p->lzma2 = NULL; } } void XzProps_Init(CXzProps *p) { p->lzma2Props = 0; p->filterProps = 0; p->checkId = XZ_CHECK_CRC32; } void XzFilterProps_Init(CXzFilterProps *p) { p->id = 0; p->delta = 0; p->ip= 0; p->ipDefined = False; } static SRes Xz_Compress(CXzStream *xz, CLzma2WithFilters *lzmaf, ISeqOutStream *outStream, ISeqInStream *inStream, const CXzProps *props, ICompressProgress *progress) { xz->flags = (Byte)props->checkId; RINOK(Lzma2Enc_SetProps(lzmaf->lzma2, props->lzma2Props)); RINOK(Xz_WriteHeader(xz->flags, outStream)); { CSeqCheckInStream checkInStream; CSeqSizeOutStream seqSizeOutStream; CXzBlock block; int filterIndex = 0; CXzFilter *filter = NULL; const CXzFilterProps *fp = props->filterProps; XzBlock_ClearFlags(&block); XzBlock_SetNumFilters(&block, 1 + (fp ? 1 : 0)); if (fp) { filter = &block.filters[filterIndex++]; filter->id = fp->id; filter->propsSize = 0; if (fp->id == XZ_ID_Delta) { filter->props[0] = (Byte)(fp->delta - 1); filter->propsSize = 1; } else if (fp->ipDefined) { SetUi32(filter->props, fp->ip); filter->propsSize = 4; } } { CXzFilter *f = &block.filters[filterIndex++]; f->id = XZ_ID_LZMA2; f->propsSize = 1; f->props[0] = Lzma2Enc_WriteProperties(lzmaf->lzma2); } seqSizeOutStream.p.Write = MyWrite; seqSizeOutStream.realStream = outStream; seqSizeOutStream.processed = 0; RINOK(XzBlock_WriteHeader(&block, &seqSizeOutStream.p)); checkInStream.p.Read = SeqCheckInStream_Read; checkInStream.realStream = inStream; SeqCheckInStream_Init(&checkInStream, XzFlags_GetCheckType(xz->flags)); if (fp) { #ifdef USE_SUBBLOCK if (fp->id == XZ_ID_Subblock) { lzmaf->sb.inStream = &checkInStream.p; RINOK(SbEncInStream_Init(&lzmaf->sb)); } else #endif { lzmaf->filter.realStream = &checkInStream.p; RINOK(SeqInFilter_Init(&lzmaf->filter, filter)); } } { UInt64 packPos = seqSizeOutStream.processed; SRes res = Lzma2Enc_Encode(lzmaf->lzma2, &seqSizeOutStream.p, fp ? #ifdef USE_SUBBLOCK (fp->id == XZ_ID_Subblock) ? &lzmaf->sb.p: #endif &lzmaf->filter.p: &checkInStream.p, progress); RINOK(res); block.unpackSize = checkInStream.processed; block.packSize = seqSizeOutStream.processed - packPos; } { unsigned padSize = 0; Byte buf[128]; while((((unsigned)block.packSize + padSize) & 3) != 0) buf[padSize++] = 0; SeqCheckInStream_GetDigest(&checkInStream, buf + padSize); RINOK(WriteBytes(&seqSizeOutStream.p, buf, padSize + XzFlags_GetCheckSize(xz->flags))); RINOK(Xz_AddIndexRecord(xz, block.unpackSize, seqSizeOutStream.processed - padSize, &g_Alloc)); } } return Xz_WriteFooter(xz, outStream); } SRes Xz_Encode(ISeqOutStream *outStream, ISeqInStream *inStream, const CXzProps *props, ICompressProgress *progress) { SRes res; CXzStream xz; CLzma2WithFilters lzmaf; Xz_Construct(&xz); Lzma2WithFilters_Construct(&lzmaf, &g_Alloc, &g_BigAlloc); res = Lzma2WithFilters_Create(&lzmaf); if (res == SZ_OK) res = Xz_Compress(&xz, &lzmaf, outStream, inStream, props, progress); Lzma2WithFilters_Free(&lzmaf); Xz_Free(&xz, &g_Alloc); return res; } SRes Xz_EncodeEmpty(ISeqOutStream *outStream) { SRes res; CXzStream xz; Xz_Construct(&xz); res = Xz_WriteHeader(xz.flags, outStream); if (res == SZ_OK) res = Xz_WriteFooter(&xz, outStream); Xz_Free(&xz, &g_Alloc); return res; } src/libs/7zip/unix/C/XzEnc.h000066400000000000000000000012631325366651500160520ustar00rootroot00000000000000/* XzEnc.h -- Xz Encode 2011-02-07 : Igor Pavlov : Public domain */ #ifndef __XZ_ENC_H #define __XZ_ENC_H #include "Lzma2Enc.h" #include "Xz.h" EXTERN_C_BEGIN typedef struct { UInt32 id; UInt32 delta; UInt32 ip; int ipDefined; } CXzFilterProps; void XzFilterProps_Init(CXzFilterProps *p); typedef struct { const CLzma2EncProps *lzma2Props; const CXzFilterProps *filterProps; unsigned checkId; } CXzProps; void XzProps_Init(CXzProps *p); SRes Xz_Encode(ISeqOutStream *outStream, ISeqInStream *inStream, const CXzProps *props, ICompressProgress *progress); SRes Xz_EncodeEmpty(ISeqOutStream *outStream); EXTERN_C_END #endif src/libs/7zip/unix/C/XzIn.c000066400000000000000000000206441325366651500157120ustar00rootroot00000000000000/* XzIn.c - Xz input 2014-12-30 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "7zCrc.h" #include "CpuArch.h" #include "Xz.h" SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStream *inStream) { Byte sig[XZ_STREAM_HEADER_SIZE]; RINOK(SeqInStream_Read2(inStream, sig, XZ_STREAM_HEADER_SIZE, SZ_ERROR_NO_ARCHIVE)); if (memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0) return SZ_ERROR_NO_ARCHIVE; return Xz_ParseHeader(p, sig); } #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStream *inStream, Bool *isIndex, UInt32 *headerSizeRes) { Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; unsigned headerSize; *headerSizeRes = 0; RINOK(SeqInStream_ReadByte(inStream, &header[0])); headerSize = ((unsigned)header[0] << 2) + 4; if (headerSize == 0) { *headerSizeRes = 1; *isIndex = True; return SZ_OK; } *isIndex = False; *headerSizeRes = headerSize; RINOK(SeqInStream_Read(inStream, header + 1, headerSize - 1)); return XzBlock_Parse(p, header); } #define ADD_SIZE_CHECH(size, val) \ { UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; } UInt64 Xz_GetUnpackSize(const CXzStream *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->numBlocks; i++) ADD_SIZE_CHECH(size, p->blocks[i].unpackSize); return size; } UInt64 Xz_GetPackSize(const CXzStream *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->numBlocks; i++) ADD_SIZE_CHECH(size, (p->blocks[i].totalSize + 3) & ~(UInt64)3); return size; } /* SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStream *inStream) { return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f)); } */ static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAlloc *alloc) { size_t i, numBlocks, pos = 1; UInt32 crc; if (size < 5 || buf[0] != 0) return SZ_ERROR_ARCHIVE; size -= 4; crc = CrcCalc(buf, size); if (crc != GetUi32(buf + size)) return SZ_ERROR_ARCHIVE; { UInt64 numBlocks64; READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64); numBlocks = (size_t)numBlocks64; if (numBlocks != numBlocks64 || numBlocks * 2 > size) return SZ_ERROR_ARCHIVE; } Xz_Free(p, alloc); if (numBlocks != 0) { p->numBlocks = numBlocks; p->numBlocksAllocated = numBlocks; p->blocks = alloc->Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); if (p->blocks == 0) return SZ_ERROR_MEM; for (i = 0; i < numBlocks; i++) { CXzBlockSizes *block = &p->blocks[i]; READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize); READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize); if (block->totalSize == 0) return SZ_ERROR_ARCHIVE; } } while ((pos & 3) != 0) if (buf[pos++] != 0) return SZ_ERROR_ARCHIVE; return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; } static SRes Xz_ReadIndex(CXzStream *p, ILookInStream *stream, UInt64 indexSize, ISzAlloc *alloc) { SRes res; size_t size; Byte *buf; if (indexSize > ((UInt32)1 << 31)) return SZ_ERROR_UNSUPPORTED; size = (size_t)indexSize; if (size != indexSize) return SZ_ERROR_UNSUPPORTED; buf = alloc->Alloc(alloc, size); if (buf == 0) return SZ_ERROR_MEM; res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); if (res == SZ_OK) res = Xz_ReadIndex2(p, buf, size, alloc); alloc->Free(alloc, buf); return res; } static SRes SeekFromCur(ILookInStream *inStream, Int64 *res) { return inStream->Seek(inStream, res, SZ_SEEK_CUR); } static SRes Xz_ReadBackward(CXzStream *p, ILookInStream *stream, Int64 *startOffset, ISzAlloc *alloc) { UInt64 indexSize; Byte buf[XZ_STREAM_FOOTER_SIZE]; if ((*startOffset & 3) != 0 || *startOffset < XZ_STREAM_FOOTER_SIZE) return SZ_ERROR_NO_ARCHIVE; *startOffset = -XZ_STREAM_FOOTER_SIZE; RINOK(SeekFromCur(stream, startOffset)); RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO_ARCHIVE)); if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0) { UInt32 total = 0; *startOffset += XZ_STREAM_FOOTER_SIZE; for (;;) { size_t i; #define TEMP_BUF_SIZE (1 << 10) Byte tempBuf[TEMP_BUF_SIZE]; if (*startOffset < XZ_STREAM_FOOTER_SIZE || total > (1 << 16)) return SZ_ERROR_NO_ARCHIVE; i = (*startOffset > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)*startOffset; total += (UInt32)i; *startOffset = -(Int64)i; RINOK(SeekFromCur(stream, startOffset)); RINOK(LookInStream_Read2(stream, tempBuf, i, SZ_ERROR_NO_ARCHIVE)); for (; i != 0; i--) if (tempBuf[i - 1] != 0) break; if (i != 0) { if ((i & 3) != 0) return SZ_ERROR_NO_ARCHIVE; *startOffset += i; break; } } if (*startOffset < XZ_STREAM_FOOTER_SIZE) return SZ_ERROR_NO_ARCHIVE; *startOffset -= XZ_STREAM_FOOTER_SIZE; RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET)); RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO_ARCHIVE)); if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0) return SZ_ERROR_NO_ARCHIVE; } p->flags = (CXzStreamFlags)GetBe16(buf + 8); if (!XzFlags_IsSupported(p->flags)) return SZ_ERROR_UNSUPPORTED; if (GetUi32(buf) != CrcCalc(buf + 4, 6)) return SZ_ERROR_ARCHIVE; indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2; *startOffset = -(Int64)(indexSize + XZ_STREAM_FOOTER_SIZE); RINOK(SeekFromCur(stream, startOffset)); RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)); { UInt64 totalSize = Xz_GetPackSize(p); UInt64 sum = XZ_STREAM_HEADER_SIZE + totalSize + indexSize; if (totalSize == XZ_SIZE_OVERFLOW || sum >= ((UInt64)1 << 63) || totalSize >= ((UInt64)1 << 63)) return SZ_ERROR_ARCHIVE; *startOffset = -(Int64)sum; RINOK(SeekFromCur(stream, startOffset)); } { CXzStreamFlags headerFlags; CSecToRead secToRead; SecToRead_CreateVTable(&secToRead); secToRead.realStream = stream; RINOK(Xz_ReadHeader(&headerFlags, &secToRead.s)); return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; } } /* ---------- Xz Streams ---------- */ void Xzs_Construct(CXzs *p) { p->num = p->numAllocated = 0; p->streams = 0; } void Xzs_Free(CXzs *p, ISzAlloc *alloc) { size_t i; for (i = 0; i < p->num; i++) Xz_Free(&p->streams[i], alloc); alloc->Free(alloc, p->streams); p->num = p->numAllocated = 0; p->streams = 0; } UInt64 Xzs_GetNumBlocks(const CXzs *p) { UInt64 num = 0; size_t i; for (i = 0; i < p->num; i++) num += p->streams[i].numBlocks; return num; } UInt64 Xzs_GetUnpackSize(const CXzs *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->num; i++) ADD_SIZE_CHECH(size, Xz_GetUnpackSize(&p->streams[i])); return size; } /* UInt64 Xzs_GetPackSize(const CXzs *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->num; i++) ADD_SIZE_CHECH(size, Xz_GetTotalSize(&p->streams[i])); return size; } */ SRes Xzs_ReadBackward(CXzs *p, ILookInStream *stream, Int64 *startOffset, ICompressProgress *progress, ISzAlloc *alloc) { Int64 endOffset = 0; RINOK(stream->Seek(stream, &endOffset, SZ_SEEK_END)); *startOffset = endOffset; for (;;) { CXzStream st; SRes res; Xz_Construct(&st); res = Xz_ReadBackward(&st, stream, startOffset, alloc); st.startOffset = *startOffset; RINOK(res); if (p->num == p->numAllocated) { size_t newNum = p->num + p->num / 4 + 1; Byte *data = (Byte *)alloc->Alloc(alloc, newNum * sizeof(CXzStream)); if (data == 0) return SZ_ERROR_MEM; p->numAllocated = newNum; if (p->num != 0) memcpy(data, p->streams, p->num * sizeof(CXzStream)); alloc->Free(alloc, p->streams); p->streams = (CXzStream *)data; } p->streams[p->num++] = st; if (*startOffset == 0) break; RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET)); if (progress && progress->Progress(progress, endOffset - *startOffset, (UInt64)(Int64)-1) != SZ_OK) return SZ_ERROR_PROGRESS; } return SZ_OK; } src/libs/7zip/unix/CPP/000077500000000000000000000000001325366651500151105ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/000077500000000000000000000000001325366651500160015ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/7zip.pri000066400000000000000000000003511325366651500174050ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/ICoder.h \ $$7ZIP_BASE/CPP/7zip/IDecl.h \ $$7ZIP_BASE/CPP/7zip/IPassword.h \ $$7ZIP_BASE/CPP/7zip/IProgress.h \ $$7ZIP_BASE/CPP/7zip/IStream.h \ $$7ZIP_BASE/CPP/7zip/PropID.h src/libs/7zip/unix/CPP/7zip/Archive/000077500000000000000000000000001325366651500173625ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/Archive/7z/000077500000000000000000000000001325366651500177225ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/Archive/7z/7z.pri000066400000000000000000000026441325366651500210040ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Archive/7z/7zCompressionMode.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zDecode.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zEncode.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderInStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderOutStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHandler.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHeader.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zIn.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zItem.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zOut.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zProperties.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zSpecStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zUpdate.h SOURCES += $$7ZIP_BASE/CPP/7zip/Archive/7z/7zDecode.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zEncode.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zExtract.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderInStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderOutStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHandler.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHandlerOut.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHeader.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zIn.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zOut.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zProperties.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zSpecStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zUpdate.cpp src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h000066400000000000000000000016521325366651500234660ustar00rootroot00000000000000// 7zCompressionMode.h #ifndef __7Z_COMPRESSION_MODE_H #define __7Z_COMPRESSION_MODE_H #include "../../Common/MethodId.h" #include "../../Common/MethodProps.h" namespace NArchive { namespace N7z { struct CMethodFull: public CProps { CMethodId Id; UInt32 NumInStreams; UInt32 NumOutStreams; bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } }; struct CBind { UInt32 InCoder; UInt32 InStream; UInt32 OutCoder; UInt32 OutStream; }; struct CCompressionMethodMode { CObjectVector Methods; CRecordVector Binds; #ifndef _7ZIP_ST UInt32 NumThreads; #endif bool PasswordIsDefined; UString Password; bool IsEmpty() const { return (Methods.IsEmpty() && !PasswordIsDefined); } CCompressionMethodMode(): PasswordIsDefined(false) #ifndef _7ZIP_ST , NumThreads(1) #endif {} }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp000066400000000000000000000252651325366651500221040ustar00rootroot00000000000000// 7zDecode.cpp #include "StdAfx.h" #include "../../Common/LimitedStreams.h" #include "../../Common/LockedStream.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "7zDecode.h" namespace NArchive { namespace N7z { static void ConvertFolderItemInfoToBindInfo(const CFolder &folder, CBindInfoEx &bindInfo) { bindInfo.Clear(); bindInfo.BindPairs.ClearAndSetSize(folder.BindPairs.Size()); unsigned i; for (i = 0; i < folder.BindPairs.Size(); i++) { NCoderMixer::CBindPair &bindPair = bindInfo.BindPairs[i]; bindPair.InIndex = (UInt32)folder.BindPairs[i].InIndex; bindPair.OutIndex = (UInt32)folder.BindPairs[i].OutIndex; } bindInfo.Coders.ClearAndSetSize(folder.Coders.Size()); bindInfo.CoderMethodIDs.ClearAndSetSize(folder.Coders.Size()); UInt32 outStreamIndex = 0; for (i = 0; i < folder.Coders.Size(); i++) { NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i]; const CCoderInfo &coderInfo = folder.Coders[i]; coderStreamsInfo.NumInStreams = (UInt32)coderInfo.NumInStreams; coderStreamsInfo.NumOutStreams = (UInt32)coderInfo.NumOutStreams; bindInfo.CoderMethodIDs[i] = coderInfo.MethodID; for (UInt32 j = 0; j < coderStreamsInfo.NumOutStreams; j++, outStreamIndex++) if (folder.FindBindPairForOutStream(outStreamIndex) < 0) bindInfo.OutStreams.Add(outStreamIndex); } bindInfo.InStreams.ClearAndSetSize(folder.PackStreams.Size()); for (i = 0; i < folder.PackStreams.Size(); i++) bindInfo.InStreams[i] = (UInt32)folder.PackStreams[i]; } static bool AreCodersEqual(const NCoderMixer::CCoderStreamsInfo &a1, const NCoderMixer::CCoderStreamsInfo &a2) { return (a1.NumInStreams == a2.NumInStreams) && (a1.NumOutStreams == a2.NumOutStreams); } static bool AreBindPairsEqual(const NCoderMixer::CBindPair &a1, const NCoderMixer::CBindPair &a2) { return (a1.InIndex == a2.InIndex) && (a1.OutIndex == a2.OutIndex); } static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2) { if (a1.Coders.Size() != a2.Coders.Size()) return false; unsigned i; for (i = 0; i < a1.Coders.Size(); i++) if (!AreCodersEqual(a1.Coders[i], a2.Coders[i])) return false; if (a1.BindPairs.Size() != a2.BindPairs.Size()) return false; for (i = 0; i < a1.BindPairs.Size(); i++) if (!AreBindPairsEqual(a1.BindPairs[i], a2.BindPairs[i])) return false; for (i = 0; i < a1.CoderMethodIDs.Size(); i++) if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i]) return false; if (a1.InStreams.Size() != a2.InStreams.Size()) return false; if (a1.OutStreams.Size() != a2.OutStreams.Size()) return false; return true; } CDecoder::CDecoder(bool multiThread) { #ifndef _ST_MODE multiThread = true; #endif _multiThread = multiThread; _bindInfoExPrevIsDefined = false; } HRESULT CDecoder::Decode( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, UInt64 startPos, const CFolders &folders, int folderIndex, ISequentialOutStream *outStream, ICompressProgressInfo *compressProgress _7Z_DECODER_CRYPRO_VARS_DECL #if !defined(_7ZIP_ST) && !defined(_SFX) , bool mtMode, UInt32 numThreads #endif ) { const UInt64 *packPositions = &folders.PackPositions[folders.FoStartPackStreamIndex[folderIndex]]; CFolder folderInfo; folders.ParseFolderInfo(folderIndex, folderInfo); if (!folderInfo.CheckStructure(folders.GetNumFolderUnpackSizes(folderIndex))) return E_NOTIMPL; /* We don't need to init isEncrypted and passwordIsDefined We must upgrade them only #ifndef _NO_CRYPTO isEncrypted = false; passwordIsDefined = false; #endif */ CObjectVector< CMyComPtr > inStreams; CLockedInStream lockedInStream; lockedInStream.Init(inStream); for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++) { CLockedSequentialInStreamImp *lockedStreamImpSpec = new CLockedSequentialInStreamImp; CMyComPtr lockedStreamImp = lockedStreamImpSpec; lockedStreamImpSpec->Init(&lockedInStream, startPos + packPositions[j]); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr inStream = streamSpec; streamSpec->SetStream(lockedStreamImp); streamSpec->Init(packPositions[j + 1] - packPositions[j]); inStreams.Add(inStream); } unsigned numCoders = folderInfo.Coders.Size(); CBindInfoEx bindInfo; ConvertFolderItemInfoToBindInfo(folderInfo, bindInfo); bool createNewCoders; if (!_bindInfoExPrevIsDefined) createNewCoders = true; else createNewCoders = !AreBindInfoExEqual(bindInfo, _bindInfoExPrev); if (createNewCoders) { unsigned i; _decoders.Clear(); // _decoders2.Clear(); _mixerCoder.Release(); if (_multiThread) { _mixerCoderMTSpec = new NCoderMixer::CCoderMixer2MT; _mixerCoder = _mixerCoderMTSpec; _mixerCoderCommon = _mixerCoderMTSpec; } else { #ifdef _ST_MODE _mixerCoderSTSpec = new NCoderMixer::CCoderMixer2ST; _mixerCoder = _mixerCoderSTSpec; _mixerCoderCommon = _mixerCoderSTSpec; #endif } RINOK(_mixerCoderCommon->SetBindInfo(bindInfo)); for (i = 0; i < numCoders; i++) { const CCoderInfo &coderInfo = folderInfo.Coders[i]; CMyComPtr decoder; CMyComPtr decoder2; RINOK(CreateCoder( EXTERNAL_CODECS_LOC_VARS coderInfo.MethodID, decoder, decoder2, false)); CMyComPtr decoderUnknown; if (coderInfo.IsSimpleCoder()) { if (decoder == 0) return E_NOTIMPL; decoderUnknown = (IUnknown *)decoder; if (_multiThread) _mixerCoderMTSpec->AddCoder(decoder); #ifdef _ST_MODE else _mixerCoderSTSpec->AddCoder(decoder, false); #endif } else { if (decoder2 == 0) return E_NOTIMPL; decoderUnknown = (IUnknown *)decoder2; if (_multiThread) _mixerCoderMTSpec->AddCoder2(decoder2); #ifdef _ST_MODE else _mixerCoderSTSpec->AddCoder2(decoder2, false); #endif } _decoders.Add(decoderUnknown); #ifdef EXTERNAL_CODECS CMyComPtr setCompressCodecsInfo; decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs)); } #endif } _bindInfoExPrev = bindInfo; _bindInfoExPrevIsDefined = true; } unsigned i; _mixerCoderCommon->ReInit(); UInt32 packStreamIndex = 0; UInt32 unpackStreamIndexStart = folders.FoToCoderUnpackSizes[folderIndex]; UInt32 unpackStreamIndex = unpackStreamIndexStart; UInt32 coderIndex = 0; // UInt32 coder2Index = 0; for (i = 0; i < numCoders; i++) { const CCoderInfo &coderInfo = folderInfo.Coders[i]; CMyComPtr &decoder = _decoders[coderIndex]; { CMyComPtr setDecoderProperties; decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); if (setDecoderProperties) { const CByteBuffer &props = coderInfo.Props; size_t size = props.Size(); if (size > 0xFFFFFFFF) return E_NOTIMPL; // if (size > 0) { RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size)); } } } #if !defined(_7ZIP_ST) && !defined(_SFX) if (mtMode) { CMyComPtr setCoderMt; decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); if (setCoderMt) { RINOK(setCoderMt->SetNumberOfThreads(numThreads)); } } #endif #ifndef _NO_CRYPTO { CMyComPtr cryptoSetPassword; decoder.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); if (cryptoSetPassword) { isEncrypted = true; if (!getTextPassword) return E_NOTIMPL; CMyComBSTR passwordBSTR; RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR)); passwordIsDefined = true; size_t len = 0; if (passwordBSTR) len = MyStringLen((BSTR)passwordBSTR); CByteBuffer buffer(len * 2); for (size_t i = 0; i < len; i++) { wchar_t c = passwordBSTR[i]; ((Byte *)buffer)[i * 2] = (Byte)c; ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); } RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size())); } } #endif coderIndex++; UInt32 numInStreams = (UInt32)coderInfo.NumInStreams; UInt32 numOutStreams = (UInt32)coderInfo.NumOutStreams; CObjArray packSizes(numInStreams); CObjArray packSizesPointers(numInStreams); CObjArray unpackSizesPointers(numOutStreams); UInt32 j; for (j = 0; j < numOutStreams; j++, unpackStreamIndex++) unpackSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndex]; for (j = 0; j < numInStreams; j++, packStreamIndex++) { int bindPairIndex = folderInfo.FindBindPairForInStream(packStreamIndex); if (bindPairIndex >= 0) packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + (UInt32)folderInfo.BindPairs[bindPairIndex].OutIndex]; else { int index = folderInfo.FindPackStreamArrayIndex(packStreamIndex); if (index < 0) return S_FALSE; // check it packSizes[j] = packPositions[index + 1] - packPositions[index]; packSizesPointers[j] = &packSizes[j]; } } _mixerCoderCommon->SetCoderInfo(i, packSizesPointers, unpackSizesPointers); } UInt32 mainCoder, temp; bindInfo.FindOutStream(bindInfo.OutStreams[0], mainCoder, temp); if (_multiThread) _mixerCoderMTSpec->SetProgressCoderIndex(mainCoder); /* else _mixerCoderSTSpec->SetProgressCoderIndex(mainCoder);; */ if (numCoders == 0) return 0; unsigned num = inStreams.Size(); CObjArray inStreamPointers(num); for (i = 0; i < num; i++) inStreamPointers[i] = inStreams[i]; ISequentialOutStream *outStreamPointer = outStream; return _mixerCoder->Code( inStreamPointers, NULL, num, &outStreamPointer, NULL, 1, compressProgress); } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h000066400000000000000000000026501325366651500215420ustar00rootroot00000000000000// 7zDecode.h #ifndef __7Z_DECODE_H #define __7Z_DECODE_H #include "../../IStream.h" #include "../../IPassword.h" #include "../Common/CoderMixer2.h" #include "../Common/CoderMixer2MT.h" #ifdef _ST_MODE #include "../Common/CoderMixer2ST.h" #endif #include "../../Common/CreateCoder.h" #include "7zIn.h" namespace NArchive { namespace N7z { struct CBindInfoEx: public NCoderMixer::CBindInfo { CRecordVector CoderMethodIDs; void Clear() { CBindInfo::Clear(); CoderMethodIDs.Clear(); } }; class CDecoder { bool _bindInfoExPrevIsDefined; CBindInfoEx _bindInfoExPrev; bool _multiThread; #ifdef _ST_MODE NCoderMixer::CCoderMixer2ST *_mixerCoderSTSpec; #endif NCoderMixer::CCoderMixer2MT *_mixerCoderMTSpec; NCoderMixer::CCoderMixer2 *_mixerCoderCommon; CMyComPtr _mixerCoder; CObjectVector > _decoders; // CObjectVector > _decoders2; public: CDecoder(bool multiThread); HRESULT Decode( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, UInt64 startPos, const CFolders &folders, int folderIndex, ISequentialOutStream *outStream, ICompressProgressInfo *compressProgress _7Z_DECODER_CRYPRO_VARS_DECL #if !defined(_7ZIP_ST) && !defined(_SFX) , bool mtMode, UInt32 numThreads #endif ); }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp000066400000000000000000000345141325366651500221130ustar00rootroot00000000000000// 7zEncode.cpp #include "StdAfx.h" #include "../../Common/CreateCoder.h" #include "../../Common/FilterCoder.h" #include "../../Common/LimitedStreams.h" #include "../../Common/InOutTempBuffer.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "7zEncode.h" #include "7zSpecStream.h" static const UInt64 k_Delta = 0x03; static const UInt64 k_BCJ = 0x03030103; static const UInt64 k_BCJ2 = 0x0303011B; namespace NArchive { namespace N7z { static void ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo &bindInfo, const CRecordVector decompressionMethods, CFolder &folder) { // bindInfo.CoderMethodIDs.Clear(); // folder.OutStreams.Clear(); folder.BindPairs.SetSize(bindInfo.BindPairs.Size()); unsigned i; for (i = 0; i < bindInfo.BindPairs.Size(); i++) { CBindPair &bp = folder.BindPairs[i]; const NCoderMixer::CBindPair &mixerBp = bindInfo.BindPairs[i]; bp.InIndex = mixerBp.InIndex; bp.OutIndex = mixerBp.OutIndex; } folder.Coders.SetSize(bindInfo.Coders.Size()); for (i = 0; i < bindInfo.Coders.Size(); i++) { CCoderInfo &coderInfo = folder.Coders[i]; const NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i]; coderInfo.NumInStreams = coderStreamsInfo.NumInStreams; coderInfo.NumOutStreams = coderStreamsInfo.NumOutStreams; coderInfo.MethodID = decompressionMethods[i]; // coderInfo.Props can be nonFree; } folder.PackStreams.SetSize(bindInfo.InStreams.Size()); for (i = 0; i < bindInfo.InStreams.Size(); i++) folder.PackStreams[i] = bindInfo.InStreams[i]; } static HRESULT SetCoderProps2(const CProps &props, const UInt64 *dataSizeReduce, IUnknown *coder) { CMyComPtr setCoderProperties; coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties); if (setCoderProperties) return props.SetCoderProps(setCoderProperties, dataSizeReduce); return props.AreThereNonOptionalProps() ? E_INVALIDARG : S_OK; } HRESULT CEncoder::CreateMixerCoder( DECL_EXTERNAL_CODECS_LOC_VARS const UInt64 *inSizeForReduce) { _mixerCoderSpec = new NCoderMixer::CCoderMixer2MT; _mixerCoder = _mixerCoderSpec; RINOK(_mixerCoderSpec->SetBindInfo(_bindInfo)); FOR_VECTOR (i, _options.Methods) { const CMethodFull &methodFull = _options.Methods[i]; CCoderInfo &encodingInfo = _codersInfo.AddNew(); encodingInfo.MethodID = methodFull.Id; CMyComPtr encoder; CMyComPtr encoder2; RINOK(CreateCoder( EXTERNAL_CODECS_LOC_VARS methodFull.Id, encoder, encoder2, true)); if (!encoder && !encoder2) return E_FAIL; CMyComPtr encoderCommon = encoder ? (IUnknown *)encoder : (IUnknown *)encoder2; #ifndef _7ZIP_ST { CMyComPtr setCoderMt; encoderCommon.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); if (setCoderMt) { RINOK(setCoderMt->SetNumberOfThreads(_options.NumThreads)); } } #endif RINOK(SetCoderProps2(methodFull, inSizeForReduce, encoderCommon)); /* CMyComPtr resetSalt; encoderCommon.QueryInterface(IID_ICryptoResetSalt, (void **)&resetSalt); if (resetSalt) { resetSalt->ResetSalt(); } */ #ifdef EXTERNAL_CODECS CMyComPtr setCompressCodecsInfo; encoderCommon.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs)); } #endif CMyComPtr cryptoSetPassword; encoderCommon.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); if (cryptoSetPassword) { const UInt32 sizeInBytes = _options.Password.Len() * 2; CByteBuffer buffer(sizeInBytes); for (unsigned i = 0; i < _options.Password.Len(); i++) { wchar_t c = _options.Password[i]; ((Byte *)buffer)[i * 2] = (Byte)c; ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); } RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); } if (encoder) _mixerCoderSpec->AddCoder(encoder); else _mixerCoderSpec->AddCoder2(encoder2); } return S_OK; } HRESULT CEncoder::Encode( DECL_EXTERNAL_CODECS_LOC_VARS ISequentialInStream *inStream, const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, CFolder &folderItem, CRecordVector &coderUnpackSizes, UInt64 &unpackSize, ISequentialOutStream *outStream, CRecordVector &packSizes, ICompressProgressInfo *compressProgress) { RINOK(EncoderConstr()); if (!_mixerCoderSpec) { RINOK(CreateMixerCoder(EXTERNAL_CODECS_LOC_VARS inSizeForReduce)); } _mixerCoderSpec->ReInit(); // _mixerCoderSpec->SetCoderInfo(0, NULL, NULL, progress); CObjectVector inOutTempBuffers; CObjectVector tempBufferSpecs; CObjectVector > tempBuffers; unsigned numMethods = _bindInfo.Coders.Size(); unsigned i; for (i = 1; i < _bindInfo.OutStreams.Size(); i++) { CInOutTempBuffer &iotb = inOutTempBuffers.AddNew(); iotb.Create(); iotb.InitWriting(); } for (i = 1; i < _bindInfo.OutStreams.Size(); i++) { CSequentialOutTempBufferImp *tempBufferSpec = new CSequentialOutTempBufferImp; CMyComPtr tempBuffer = tempBufferSpec; tempBufferSpec->Init(&inOutTempBuffers[i - 1]); tempBuffers.Add(tempBuffer); tempBufferSpecs.Add(tempBufferSpec); } for (i = 0; i < numMethods; i++) _mixerCoderSpec->SetCoderInfo(i, NULL, NULL); if (_bindInfo.InStreams.IsEmpty()) return E_FAIL; UInt32 mainCoderIndex, mainStreamIndex; _bindInfo.FindInStream(_bindInfo.InStreams[0], mainCoderIndex, mainStreamIndex); if (inStreamSize) { CRecordVector sizePointers; for (UInt32 i = 0; i < _bindInfo.Coders[mainCoderIndex].NumInStreams; i++) if (i == mainStreamIndex) sizePointers.Add(inStreamSize); else sizePointers.Add(NULL); _mixerCoderSpec->SetCoderInfo(mainCoderIndex, &sizePointers.Front(), NULL); } // UInt64 outStreamStartPos; // RINOK(stream->Seek(0, STREAM_SEEK_CUR, &outStreamStartPos)); CSequentialInStreamSizeCount2 *inStreamSizeCountSpec = new CSequentialInStreamSizeCount2; CMyComPtr inStreamSizeCount = inStreamSizeCountSpec; CSequentialOutStreamSizeCount *outStreamSizeCountSpec = NULL; CMyComPtr outStreamSizeCount; inStreamSizeCountSpec->Init(inStream); CRecordVector inStreamPointers; CRecordVector outStreamPointers; inStreamPointers.Add(inStreamSizeCount); if (_bindInfo.OutStreams.Size() != 0) { outStreamSizeCountSpec = new CSequentialOutStreamSizeCount; outStreamSizeCount = outStreamSizeCountSpec; outStreamSizeCountSpec->SetStream(outStream); outStreamSizeCountSpec->Init(); outStreamPointers.Add(outStreamSizeCount); } for (i = 1; i < _bindInfo.OutStreams.Size(); i++) outStreamPointers.Add(tempBuffers[i - 1]); for (i = 0; i < _codersInfo.Size(); i++) { CCoderInfo &encodingInfo = _codersInfo[i]; CMyComPtr resetInitVector; _mixerCoderSpec->_coders[i].QueryInterface(IID_ICryptoResetInitVector, (void **)&resetInitVector); if (resetInitVector) { resetInitVector->ResetInitVector(); } CMyComPtr writeCoderProperties; _mixerCoderSpec->_coders[i].QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties); if (writeCoderProperties) { CDynBufSeqOutStream *outStreamSpec = new CDynBufSeqOutStream; CMyComPtr outStream(outStreamSpec); outStreamSpec->Init(); writeCoderProperties->WriteCoderProperties(outStream); outStreamSpec->CopyToBuffer(encodingInfo.Props); } } UInt32 progressIndex = mainCoderIndex; for (i = 0; i + 1 < _codersInfo.Size(); i++) { UInt64 m = _codersInfo[i].MethodID; if (m == k_Delta || m == k_BCJ || m == k_BCJ2) progressIndex = i + 1; } _mixerCoderSpec->SetProgressCoderIndex(progressIndex); RINOK(_mixerCoder->Code(&inStreamPointers.Front(), NULL, 1, &outStreamPointers.Front(), NULL, outStreamPointers.Size(), compressProgress)); ConvertBindInfoToFolderItemInfo(_decompressBindInfo, _decompressionMethods, folderItem); if (_bindInfo.OutStreams.Size() != 0) packSizes.Add(outStreamSizeCountSpec->GetSize()); for (i = 1; i < _bindInfo.OutStreams.Size(); i++) { CInOutTempBuffer &inOutTempBuffer = inOutTempBuffers[i - 1]; RINOK(inOutTempBuffer.WriteToStream(outStream)); packSizes.Add(inOutTempBuffer.GetDataSize()); } unpackSize = 0; for (i = 0; i < (int)_bindReverseConverter->NumSrcInStreams; i++) { int binder = _bindInfo.FindBinderForInStream( _bindReverseConverter->DestOutToSrcInMap[i]); UInt64 streamSize; if (binder < 0) { streamSize = inStreamSizeCountSpec->GetSize(); unpackSize = streamSize; } else streamSize = _mixerCoderSpec->GetWriteProcessedSize(binder); coderUnpackSizes.Add(streamSize); } for (i = 0; i < numMethods; i++) folderItem.Coders[numMethods - 1 - i].Props = _codersInfo[i].Props; return S_OK; } CEncoder::CEncoder(const CCompressionMethodMode &options): _bindReverseConverter(0), _constructed(false) { if (options.IsEmpty()) throw 1; _options = options; _mixerCoderSpec = NULL; } HRESULT CEncoder::EncoderConstr() { if (_constructed) return S_OK; if (_options.Methods.IsEmpty()) { // it has only password method; if (!_options.PasswordIsDefined) throw 1; if (!_options.Binds.IsEmpty()) throw 1; NCoderMixer::CCoderStreamsInfo coderStreamsInfo; CMethodFull method; method.NumInStreams = 1; method.NumOutStreams = 1; coderStreamsInfo.NumInStreams = 1; coderStreamsInfo.NumOutStreams = 1; method.Id = k_AES; _options.Methods.Add(method); _bindInfo.Coders.Add(coderStreamsInfo); _bindInfo.InStreams.Add(0); _bindInfo.OutStreams.Add(0); } else { UInt32 numInStreams = 0, numOutStreams = 0; unsigned i; for (i = 0; i < _options.Methods.Size(); i++) { const CMethodFull &methodFull = _options.Methods[i]; NCoderMixer::CCoderStreamsInfo coderStreamsInfo; coderStreamsInfo.NumInStreams = methodFull.NumOutStreams; coderStreamsInfo.NumOutStreams = methodFull.NumInStreams; if (_options.Binds.IsEmpty()) { if (i < _options.Methods.Size() - 1) { NCoderMixer::CBindPair bindPair; bindPair.InIndex = numInStreams + coderStreamsInfo.NumInStreams; bindPair.OutIndex = numOutStreams; _bindInfo.BindPairs.Add(bindPair); } else if (coderStreamsInfo.NumOutStreams != 0) _bindInfo.OutStreams.Insert(0, numOutStreams); for (UInt32 j = 1; j < coderStreamsInfo.NumOutStreams; j++) _bindInfo.OutStreams.Add(numOutStreams + j); } numInStreams += coderStreamsInfo.NumInStreams; numOutStreams += coderStreamsInfo.NumOutStreams; _bindInfo.Coders.Add(coderStreamsInfo); } if (!_options.Binds.IsEmpty()) { for (i = 0; i < _options.Binds.Size(); i++) { NCoderMixer::CBindPair bindPair; const CBind &bind = _options.Binds[i]; bindPair.InIndex = _bindInfo.GetCoderInStreamIndex(bind.InCoder) + bind.InStream; bindPair.OutIndex = _bindInfo.GetCoderOutStreamIndex(bind.OutCoder) + bind.OutStream; _bindInfo.BindPairs.Add(bindPair); } for (i = 0; i < (int)numOutStreams; i++) if (_bindInfo.FindBinderForOutStream(i) == -1) _bindInfo.OutStreams.Add(i); } for (i = 0; i < (int)numInStreams; i++) if (_bindInfo.FindBinderForInStream(i) == -1) _bindInfo.InStreams.Add(i); if (_bindInfo.InStreams.IsEmpty()) throw 1; // this is error // Make main stream first in list int inIndex = _bindInfo.InStreams[0]; for (;;) { UInt32 coderIndex, coderStreamIndex; _bindInfo.FindInStream(inIndex, coderIndex, coderStreamIndex); UInt32 outIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex); int binder = _bindInfo.FindBinderForOutStream(outIndex); if (binder >= 0) { inIndex = _bindInfo.BindPairs[binder].InIndex; continue; } for (i = 0; i < _bindInfo.OutStreams.Size(); i++) if (_bindInfo.OutStreams[i] == outIndex) { _bindInfo.OutStreams.Delete(i); _bindInfo.OutStreams.Insert(0, outIndex); break; } break; } if (_options.PasswordIsDefined) { unsigned numCryptoStreams = _bindInfo.OutStreams.Size(); for (i = 0; i < numCryptoStreams; i++) { NCoderMixer::CBindPair bindPair; bindPair.InIndex = numInStreams + i; bindPair.OutIndex = _bindInfo.OutStreams[i]; _bindInfo.BindPairs.Add(bindPair); } _bindInfo.OutStreams.Clear(); /* if (numCryptoStreams == 0) numCryptoStreams = 1; */ for (i = 0; i < numCryptoStreams; i++) { NCoderMixer::CCoderStreamsInfo coderStreamsInfo; CMethodFull method; method.NumInStreams = 1; method.NumOutStreams = 1; coderStreamsInfo.NumInStreams = method.NumOutStreams; coderStreamsInfo.NumOutStreams = method.NumInStreams; method.Id = k_AES; _options.Methods.Add(method); _bindInfo.Coders.Add(coderStreamsInfo); _bindInfo.OutStreams.Add(numOutStreams + i); } } } for (int i = _options.Methods.Size() - 1; i >= 0; i--) { const CMethodFull &methodFull = _options.Methods[i]; _decompressionMethods.Add(methodFull.Id); } _bindReverseConverter = new NCoderMixer::CBindReverseConverter(_bindInfo); _bindReverseConverter->CreateReverseBindInfo(_decompressBindInfo); _constructed = true; return S_OK; } CEncoder::~CEncoder() { delete _bindReverseConverter; } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h000066400000000000000000000026121325366651500215520ustar00rootroot00000000000000// 7zEncode.h #ifndef __7Z_ENCODE_H #define __7Z_ENCODE_H // #include "../../Common/StreamObjects.h" #include "7zCompressionMode.h" #include "../Common/CoderMixer2.h" #include "../Common/CoderMixer2MT.h" #ifdef _ST_MODE #include "../Common/CoderMixer2ST.h" #endif #include "7zItem.h" #include "../../Common/CreateCoder.h" namespace NArchive { namespace N7z { class CEncoder { NCoderMixer::CCoderMixer2MT *_mixerCoderSpec; CMyComPtr _mixerCoder; CObjectVector _codersInfo; CCompressionMethodMode _options; NCoderMixer::CBindInfo _bindInfo; NCoderMixer::CBindInfo _decompressBindInfo; NCoderMixer::CBindReverseConverter *_bindReverseConverter; CRecordVector _decompressionMethods; HRESULT CreateMixerCoder(DECL_EXTERNAL_CODECS_LOC_VARS const UInt64 *inSizeForReduce); bool _constructed; public: CEncoder(const CCompressionMethodMode &options); ~CEncoder(); HRESULT EncoderConstr(); HRESULT Encode( DECL_EXTERNAL_CODECS_LOC_VARS ISequentialInStream *inStream, const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, CFolder &folderItem, CRecordVector &coderUnpackSizes, UInt64 &unpackSize, ISequentialOutStream *outStream, CRecordVector &packSizes, ICompressProgressInfo *compressProgress); }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp000066400000000000000000000156171325366651500223330ustar00rootroot00000000000000// 7zExtract.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../Common/ProgressUtils.h" #include "7zDecode.h" // #include "7z1Decode.h" #include "7zFolderOutStream.h" #include "7zHandler.h" namespace NArchive { namespace N7z { struct CExtractFolderInfo { #ifdef _7Z_VOL int VolumeIndex; #endif CNum FileIndex; CNum FolderIndex; CBoolVector ExtractStatuses; UInt64 UnpackSize; CExtractFolderInfo( #ifdef _7Z_VOL int volumeIndex, #endif CNum fileIndex, CNum folderIndex): #ifdef _7Z_VOL VolumeIndex(volumeIndex), #endif FileIndex(fileIndex), FolderIndex(folderIndex), UnpackSize(0) { if (fileIndex != kNumNoIndex) { ExtractStatuses.ClearAndSetSize(1); ExtractStatuses[0] = true; } }; }; STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec) { COM_TRY_BEGIN bool testMode = (testModeSpec != 0); CMyComPtr extractCallback = extractCallbackSpec; UInt64 importantTotalUnpacked = 0; bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = #ifdef _7Z_VOL _refs.Size(); #else _db.Files.Size(); #endif if (numItems == 0) return S_OK; /* if (_volumes.Size() != 1) return E_FAIL; const CVolume &volume = _volumes.Front(); const CDbEx &_db = volume.Database; IInStream *_inStream = volume.Stream; */ CObjectVector extractFolderInfoVector; for (UInt32 ii = 0; ii < numItems; ii++) { // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex]; UInt32 ref2Index = allFilesMode ? ii : indices[ii]; // const CRef2 &ref2 = _refs[ref2Index]; // for (UInt32 ri = 0; ri < ref2.Refs.Size(); ri++) { #ifdef _7Z_VOL // const CRef &ref = ref2.Refs[ri]; const CRef &ref = _refs[ref2Index]; int volumeIndex = ref.VolumeIndex; const CVolume &volume = _volumes[volumeIndex]; const CDbEx &db = volume.Database; UInt32 fileIndex = ref.ItemIndex; #else const CDbEx &db = _db; UInt32 fileIndex = ref2Index; #endif CNum folderIndex = db.FileIndexToFolderIndexMap[fileIndex]; if (folderIndex == kNumNoIndex) { extractFolderInfoVector.Add(CExtractFolderInfo( #ifdef _7Z_VOL volumeIndex, #endif fileIndex, kNumNoIndex)); continue; } if (extractFolderInfoVector.IsEmpty() || folderIndex != extractFolderInfoVector.Back().FolderIndex #ifdef _7Z_VOL || volumeIndex != extractFolderInfoVector.Back().VolumeIndex #endif ) { extractFolderInfoVector.Add(CExtractFolderInfo( #ifdef _7Z_VOL volumeIndex, #endif kNumNoIndex, folderIndex)); UInt64 unpackSize = db.GetFolderUnpackSize(folderIndex); importantTotalUnpacked += unpackSize; extractFolderInfoVector.Back().UnpackSize = unpackSize; } CExtractFolderInfo &efi = extractFolderInfoVector.Back(); // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex]; CNum startIndex = db.FolderStartFileIndex[folderIndex]; for (CNum index = efi.ExtractStatuses.Size(); index <= fileIndex - startIndex; index++) { // UInt64 unpackSize = _db.Files[startIndex + index].UnpackSize; // Count partial_folder_size // efi.UnpackSize += unpackSize; // importantTotalUnpacked += unpackSize; efi.ExtractStatuses.Add(index == fileIndex - startIndex); } } } RINOK(extractCallback->SetTotal(importantTotalUnpacked)); CDecoder decoder( #ifdef _ST_MODE false #else true #endif ); // CDecoder1 decoder; UInt64 totalPacked = 0; UInt64 totalUnpacked = 0; UInt64 curPacked, curUnpacked; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); for (unsigned i = 0;; i++, totalUnpacked += curUnpacked, totalPacked += curPacked) { lps->OutSize = totalUnpacked; lps->InSize = totalPacked; RINOK(lps->SetCur()); if (i >= extractFolderInfoVector.Size()) break; const CExtractFolderInfo &efi = extractFolderInfoVector[i]; curUnpacked = efi.UnpackSize; curPacked = 0; CFolderOutStream *folderOutStream = new CFolderOutStream; CMyComPtr outStream(folderOutStream); #ifdef _7Z_VOL const CVolume &volume = _volumes[efi.VolumeIndex]; const CDbEx &db = volume.Database; #else const CDbEx &db = _db; #endif CNum startIndex; if (efi.FileIndex != kNumNoIndex) startIndex = efi.FileIndex; else startIndex = db.FolderStartFileIndex[efi.FolderIndex]; HRESULT result = folderOutStream->Init(&db, #ifdef _7Z_VOL volume.StartRef2Index, #else 0, #endif startIndex, &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0); RINOK(result); if (efi.FileIndex != kNumNoIndex) continue; CNum folderIndex = efi.FolderIndex; curPacked = _db.GetFolderFullPackSize(folderIndex); #ifndef _NO_CRYPTO CMyComPtr getTextPassword; if (extractCallback) extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); #endif try { #ifndef _NO_CRYPTO bool isEncrypted = false; bool passwordIsDefined = false; #endif HRESULT result = decoder.Decode( EXTERNAL_CODECS_VARS #ifdef _7Z_VOL volume.Stream, #else _inStream, #endif db.ArcInfo.DataStartPosition, db, folderIndex, outStream, progress _7Z_DECODER_CRYPRO_VARS #if !defined(_7ZIP_ST) && !defined(_SFX) , true, _numThreads #endif ); if (result == S_FALSE) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } if (result == E_NOTIMPL) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kUnsupportedMethod)); continue; } if (result != S_OK) return result; if (folderOutStream->WasWritingFinished() != S_OK) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } } catch(...) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } } return S_OK; COM_TRY_END } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp000066400000000000000000000055651325366651500236000ustar00rootroot00000000000000// 7zFolderInStream.cpp #include "StdAfx.h" #include "7zFolderInStream.h" namespace NArchive { namespace N7z { CFolderInStream::CFolderInStream() { _inStreamWithHashSpec = new CSequentialInStreamWithCRC; _inStreamWithHash = _inStreamWithHashSpec; } void CFolderInStream::Init(IArchiveUpdateCallback *updateCallback, const UInt32 *fileIndices, UInt32 numFiles) { _updateCallback = updateCallback; _numFiles = numFiles; _fileIndex = 0; _fileIndices = fileIndices; Processed.Clear(); CRCs.Clear(); Sizes.Clear(); _fileIsOpen = false; _currentSizeIsDefined = false; } HRESULT CFolderInStream::OpenStream() { _filePos = 0; while (_fileIndex < _numFiles) { CMyComPtr stream; HRESULT result = _updateCallback->GetStream(_fileIndices[_fileIndex], &stream); if (result != S_OK && result != S_FALSE) return result; _fileIndex++; _inStreamWithHashSpec->SetStream(stream); _inStreamWithHashSpec->Init(); if (stream) { _fileIsOpen = true; CMyComPtr streamGetSize; stream.QueryInterface(IID_IStreamGetSize, &streamGetSize); if (streamGetSize) { RINOK(streamGetSize->GetSize(&_currentSize)); _currentSizeIsDefined = true; } return S_OK; } RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); Sizes.Add(0); Processed.Add(result == S_OK); AddDigest(); } return S_OK; } void CFolderInStream::AddDigest() { CRCs.Add(_inStreamWithHashSpec->GetCRC()); } HRESULT CFolderInStream::CloseStream() { RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); _inStreamWithHashSpec->ReleaseStream(); _fileIsOpen = false; _currentSizeIsDefined = false; Processed.Add(true); Sizes.Add(_filePos); AddDigest(); return S_OK; } STDMETHODIMP CFolderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != 0) *processedSize = 0; while (size > 0) { if (_fileIsOpen) { UInt32 processed2; RINOK(_inStreamWithHash->Read(data, size, &processed2)); if (processed2 == 0) { RINOK(CloseStream()); continue; } if (processedSize != 0) *processedSize = processed2; _filePos += processed2; break; } if (_fileIndex >= _numFiles) break; RINOK(OpenStream()); } return S_OK; } STDMETHODIMP CFolderInStream::GetSubStreamSize(UInt64 subStream, UInt64 *value) { *value = 0; unsigned index2 = (unsigned)subStream; if (subStream > Sizes.Size()) return E_FAIL; if (index2 < Sizes.Size()) { *value = Sizes[index2]; return S_OK; } if (!_currentSizeIsDefined) return S_FALSE; *value = _currentSize; return S_OK; } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h000066400000000000000000000024751325366651500232420ustar00rootroot00000000000000// 7zFolderInStream.h #ifndef __7Z_FOLDER_IN_STREAM_H #define __7Z_FOLDER_IN_STREAM_H #include "../../ICoder.h" #include "../IArchive.h" #include "../Common/InStreamWithCRC.h" #include "7zItem.h" namespace NArchive { namespace N7z { class CFolderInStream: public ISequentialInStream, public ICompressGetSubStreamSize, public CMyUnknownImp { CSequentialInStreamWithCRC *_inStreamWithHashSpec; CMyComPtr _inStreamWithHash; CMyComPtr _updateCallback; bool _currentSizeIsDefined; bool _fileIsOpen; UInt64 _currentSize; UInt64 _filePos; const UInt32 *_fileIndices; UInt32 _numFiles; UInt32 _fileIndex; HRESULT OpenStream(); HRESULT CloseStream(); void AddDigest(); public: CRecordVector Processed; CRecordVector CRCs; CRecordVector Sizes; MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); CFolderInStream(); void Init(IArchiveUpdateCallback *updateCallback, const UInt32 *fileIndices, UInt32 numFiles); UInt64 GetFullSize() const { UInt64 size = 0; FOR_VECTOR (i, Sizes) size += Sizes[i]; return size; } }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp000066400000000000000000000073721325366651500237770ustar00rootroot00000000000000// 7zFolderOutStream.cpp #include "StdAfx.h" #include "7zFolderOutStream.h" namespace NArchive { namespace N7z { CFolderOutStream::CFolderOutStream() { _crcStreamSpec = new COutStreamWithCRC; _crcStream = _crcStreamSpec; } HRESULT CFolderOutStream::Init( const CDbEx *db, UInt32 ref2Offset, UInt32 startIndex, const CBoolVector *extractStatuses, IArchiveExtractCallback *extractCallback, bool testMode, bool checkCrc) { _db = db; _ref2Offset = ref2Offset; _startIndex = startIndex; _extractStatuses = extractStatuses; _extractCallback = extractCallback; _testMode = testMode; _checkCrc = checkCrc; _currentIndex = 0; _fileIsOpen = false; return ProcessEmptyFiles(); } HRESULT CFolderOutStream::OpenFile() { Int32 askMode = ((*_extractStatuses)[_currentIndex]) ? (_testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract) : NExtract::NAskMode::kSkip; CMyComPtr realOutStream; UInt32 index = _startIndex + _currentIndex; RINOK(_extractCallback->GetStream(_ref2Offset + index, &realOutStream, askMode)); _crcStreamSpec->SetStream(realOutStream); _crcStreamSpec->Init(_checkCrc); _fileIsOpen = true; const CFileItem &fi = _db->Files[index]; _rem = fi.Size; if (askMode == NExtract::NAskMode::kExtract && !realOutStream && !_db->IsItemAnti(index) && !fi.IsDir) askMode = NExtract::NAskMode::kSkip; return _extractCallback->PrepareOperation(askMode); } HRESULT CFolderOutStream::CloseFileAndSetResult(Int32 res) { _crcStreamSpec->ReleaseStream(); _fileIsOpen = false; _currentIndex++; return _extractCallback->SetOperationResult(res); } HRESULT CFolderOutStream::CloseFileAndSetResult() { const CFileItem &fi = _db->Files[_startIndex + _currentIndex]; return CloseFileAndSetResult( (fi.IsDir || !fi.CrcDefined || !_checkCrc || fi.Crc == _crcStreamSpec->GetCRC()) ? NExtract::NOperationResult::kOK : NExtract::NOperationResult::kCRCError); } HRESULT CFolderOutStream::ProcessEmptyFiles() { while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0) { RINOK(OpenFile()); RINOK(CloseFileAndSetResult()); } return S_OK; } STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size != 0) { if (_fileIsOpen) { UInt32 cur = size < _rem ? size : (UInt32)_rem; RINOK(_crcStream->Write(data, cur, &cur)); if (cur == 0) break; data = (const Byte *)data + cur; size -= cur; _rem -= cur; if (processedSize != NULL) *processedSize += cur; if (_rem == 0) { RINOK(CloseFileAndSetResult()); RINOK(ProcessEmptyFiles()); continue; } } else { RINOK(ProcessEmptyFiles()); if (_currentIndex == _extractStatuses->Size()) { // we support partial extracting if (processedSize != NULL) *processedSize += size; break; } RINOK(OpenFile()); } } return S_OK; } STDMETHODIMP CFolderOutStream::GetSubStreamSize(UInt64 subStream, UInt64 *value) { *value = 0; if ((int)subStream >= _extractStatuses->Size()) return S_FALSE; *value = _db->Files[_startIndex + (int)subStream].Size; return S_OK; } HRESULT CFolderOutStream::FlushCorrupted(Int32 resultEOperationResult) { while (_currentIndex < _extractStatuses->Size()) { if (_fileIsOpen) { RINOK(CloseFileAndSetResult(resultEOperationResult)); } else { RINOK(OpenFile()); } } return S_OK; } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h000066400000000000000000000027311325366651500234360ustar00rootroot00000000000000// 7zFolderOutStream.h #ifndef __7Z_FOLDER_OUT_STREAM_H #define __7Z_FOLDER_OUT_STREAM_H #include "../../IStream.h" #include "../IArchive.h" #include "../Common/OutStreamWithCRC.h" #include "7zIn.h" namespace NArchive { namespace N7z { class CFolderOutStream: public ISequentialOutStream, public ICompressGetSubStreamSize, public CMyUnknownImp { COutStreamWithCRC *_crcStreamSpec; CMyComPtr _crcStream; const CDbEx *_db; const CBoolVector *_extractStatuses; CMyComPtr _extractCallback; UInt32 _ref2Offset; UInt32 _startIndex; unsigned _currentIndex; bool _testMode; bool _checkCrc; bool _fileIsOpen; UInt64 _rem; HRESULT OpenFile(); HRESULT CloseFileAndSetResult(Int32 res); HRESULT CloseFileAndSetResult(); HRESULT ProcessEmptyFiles(); public: MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) CFolderOutStream(); STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); HRESULT Init( const CDbEx *db, UInt32 ref2Offset, UInt32 startIndex, const CBoolVector *extractStatuses, IArchiveExtractCallback *extractCallback, bool testMode, bool checkCrc); HRESULT FlushCorrupted(Int32 resultEOperationResult); HRESULT WasWritingFinished() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; } }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp000066400000000000000000000457341325366651500223010ustar00rootroot00000000000000// 7zHandler.cpp #include "StdAfx.h" #include "../../../../C/CpuArch.h" #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #ifndef __7Z_SET_PROPERTIES #include "../../../Windows/System.h" #endif #include "../Common/ItemNameUtils.h" #include "7zHandler.h" #include "7zProperties.h" #ifdef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY #include "../Common/ParseProperties.h" #endif #endif using namespace NWindows; using namespace NCOM; namespace NArchive { namespace N7z { CHandler::CHandler() { #ifndef _NO_CRYPTO _isEncrypted = false; _passwordIsDefined = false; #endif #ifdef EXTRACT_ONLY _crcSize = 4; #ifdef __7Z_SET_PROPERTIES _numThreads = NSystem::GetNumberOfProcessors(); #endif #endif } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _db.Files.Size(); return S_OK; } #ifdef _SFX IMP_IInArchive_ArcProps_NO_Table STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) { *numProps = 0; return S_OK; } STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */, BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */) { return E_NOTIMPL; } #else static const Byte kArcProps[] = { kpidHeadersSize, kpidMethod, kpidSolid, kpidNumBlocks // , kpidIsTree }; IMP_IInArchive_ArcProps static inline char GetHex(unsigned value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } static unsigned ConvertMethodIdToString_Back(char *s, UInt64 id) { int len = 0; do { s[--len] = GetHex((unsigned)id & 0xF); id >>= 4; s[--len] = GetHex((unsigned)id & 0xF); id >>= 4; } while (id != 0); return (unsigned)-len; } static void ConvertMethodIdToString(AString &res, UInt64 id) { const unsigned kLen = 32; char s[kLen]; unsigned len = kLen - 1; s[len] = 0; res += s + len - ConvertMethodIdToString_Back(s + len, id); } static unsigned GetStringForSizeValue(char *s, UInt32 val) { unsigned i; for (i = 0; i <= 31; i++) if (((UInt32)1 << i) == val) { if (i < 10) { s[0] = (char)('0' + i); s[1] = 0; return 1; } if (i < 20) { s[0] = '1'; s[1] = (char)('0' + i - 10); } else if (i < 30) { s[0] = '2'; s[1] = (char)('0' + i - 20); } else { s[0] = '3'; s[1] = (char)('0' + i - 30); } s[2] = 0; return 2; } char c = 'b'; if ((val & ((1 << 20) - 1)) == 0) { val >>= 20; c = 'm'; } else if ((val & ((1 << 10) - 1)) == 0) { val >>= 10; c = 'k'; } ::ConvertUInt32ToString(val, s); unsigned pos = MyStringLen(s); s[pos++] = c; s[pos] = 0; return pos; } /* static inline void AddHexToString(UString &res, Byte value) { res += GetHex((Byte)(value >> 4)); res += GetHex((Byte)(value & 0xF)); } */ static char *AddProp32(char *s, const char *name, UInt32 v) { *s++ = ':'; s = MyStpCpy(s, name); ::ConvertUInt32ToString(v, s); return s + MyStringLen(s); } void CHandler::AddMethodName(AString &s, UInt64 id) { UString methodName; FindMethod(EXTERNAL_CODECS_VARS id, methodName); if (methodName.IsEmpty()) { for (unsigned i = 0; i < methodName.Len(); i++) if (methodName[i] >= 0x80) { methodName.Empty(); break; } } if (methodName.IsEmpty()) ConvertMethodIdToString(s, id); else for (unsigned i = 0; i < methodName.Len(); i++) s += (char)methodName[i]; } #endif STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { #ifndef _SFX COM_TRY_BEGIN #endif NCOM::CPropVariant prop; switch (propID) { #ifndef _SFX case kpidMethod: { AString s; const CParsedMethods &pm = _db.ParsedMethods; FOR_VECTOR (i, pm.IDs) { UInt64 id = pm.IDs[i]; if (!s.IsEmpty()) s += ' '; char temp[16]; if (id == k_LZMA2) { s += "LZMA2:"; if ((pm.Lzma2Prop & 1) == 0) ConvertUInt32ToString((pm.Lzma2Prop >> 1) + 12, temp); else GetStringForSizeValue(temp, 3 << ((pm.Lzma2Prop >> 1) + 11)); s += temp; } else if (id == k_LZMA) { s += "LZMA:"; GetStringForSizeValue(temp, pm.LzmaDic); s += temp; } else AddMethodName(s, id); } prop = s; break; } case kpidSolid: prop = _db.IsSolid(); break; case kpidNumBlocks: prop = (UInt32)_db.NumFolders; break; case kpidHeadersSize: prop = _db.HeadersSize; break; case kpidPhySize: prop = _db.PhySize; break; case kpidOffset: if (_db.ArcInfo.StartPosition != 0) prop = _db.ArcInfo.StartPosition; break; /* case kpidIsTree: if (_db.IsTree) prop = true; break; case kpidIsAltStream: if (_db.ThereAreAltStreams) prop = true; break; case kpidIsAux: if (_db.IsTree) prop = true; break; */ // case kpidError: if (_db.ThereIsHeaderError) prop = "Header error"; break; #endif case kpidWarningFlags: { UInt32 v = 0; if (_db.StartHeaderWasRecovered) v |= kpv_ErrorFlags_HeadersError; if (_db.UnsupportedFeatureWarning) v |= kpv_ErrorFlags_UnsupportedFeature; if (v != 0) prop = v; break; } case kpidErrorFlags: { UInt32 v = 0; if (!_db.IsArc) v |= kpv_ErrorFlags_IsNotArc; if (_db.ThereIsHeaderError) v |= kpv_ErrorFlags_HeadersError; if (_db.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd; // if (_db.UnsupportedVersion) v |= kpv_ErrorFlags_Unsupported; if (_db.UnsupportedFeatureError) v |= kpv_ErrorFlags_UnsupportedFeature; prop = v; break; } } prop.Detach(value); return S_OK; #ifndef _SFX COM_TRY_END #endif } static void SetFileTimeProp_From_UInt64Def(PROPVARIANT *prop, const CUInt64DefVector &v, int index) { UInt64 value; if (v.GetItem(index, value)) PropVarEm_Set_FileTime64(prop, value); } bool CHandler::IsFolderEncrypted(CNum folderIndex) const { if (folderIndex == kNumNoIndex) return false; size_t startPos = _db.FoCodersDataOffset[folderIndex]; const Byte *p = _db.CodersData + startPos; size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos; CInByte2 inByte; inByte.Init(p, size); CNum numCoders = inByte.ReadNum(); for (; numCoders != 0; numCoders--) { Byte mainByte = inByte.ReadByte(); unsigned idSize = (mainByte & 0xF); const Byte *longID = inByte.GetPtr(); UInt64 id64 = 0; for (unsigned j = 0; j < idSize; j++) id64 = ((id64 << 8) | longID[j]); inByte.SkipDataNoCheck(idSize); if (id64 == k_AES) return true; if ((mainByte & 0x20) != 0) inByte.SkipDataNoCheck(inByte.ReadNum()); } return false; } STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps) { *numProps = 0; return S_OK; } STDMETHODIMP CHandler::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) { *name = NULL; *propID = kpidNtSecure; return S_OK; } STDMETHODIMP CHandler::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) { /* const CFileItem &file = _db.Files[index]; *parentType = (file.IsAltStream ? NParentType::kAltStream : NParentType::kDir); *parent = (UInt32)(Int32)file.Parent; */ *parentType = NParentType::kDir; *parent = (UInt32)(Int32)-1; return S_OK; } STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) { *data = NULL; *dataSize = 0; *propType = 0; if (/* _db.IsTree && propID == kpidName || !_db.IsTree && */ propID == kpidPath) { if (_db.NameOffsets && _db.NamesBuf) { size_t offset = _db.NameOffsets[index]; size_t size = (_db.NameOffsets[index + 1] - offset) * 2; if (size < ((UInt32)1 << 31)) { *data = (const void *)(_db.NamesBuf + offset * 2); *dataSize = (UInt32)size; *propType = NPropDataType::kUtf16z; } } return S_OK; } /* if (propID == kpidNtSecure) { if (index < (UInt32)_db.SecureIDs.Size()) { int id = _db.SecureIDs[index]; size_t offs = _db.SecureOffsets[id]; size_t size = _db.SecureOffsets[id + 1] - offs; if (size >= 0) { *data = _db.SecureBuf + offs; *dataSize = (UInt32)size; *propType = NPropDataType::kRaw; } } } */ return S_OK; } #ifndef _SFX HRESULT CHandler::SetMethodToProp(CNum folderIndex, PROPVARIANT *prop) const { PropVariant_Clear(prop); if (folderIndex == kNumNoIndex) return S_OK; // for (int ttt = 0; ttt < 1; ttt++) { const unsigned kTempSize = 256; char temp[kTempSize]; unsigned pos = kTempSize; temp[--pos] = 0; size_t startPos = _db.FoCodersDataOffset[folderIndex]; const Byte *p = _db.CodersData + startPos; size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos; CInByte2 inByte; inByte.Init(p, size); // numCoders == 0 ??? CNum numCoders = inByte.ReadNum(); bool needSpace = false; for (; numCoders != 0; numCoders--, needSpace = true) { if (pos < 32) // max size of property break; Byte mainByte = inByte.ReadByte(); unsigned idSize = (mainByte & 0xF); const Byte *longID = inByte.GetPtr(); UInt64 id64 = 0; for (unsigned j = 0; j < idSize; j++) id64 = ((id64 << 8) | longID[j]); inByte.SkipDataNoCheck(idSize); if ((mainByte & 0x10) != 0) { inByte.ReadNum(); // NumInStreams inByte.ReadNum(); // NumOutStreams } CNum propsSize = 0; const Byte *props = NULL; if ((mainByte & 0x20) != 0) { propsSize = inByte.ReadNum(); props = inByte.GetPtr(); inByte.SkipDataNoCheck(propsSize); } const char *name = NULL; char s[32]; s[0] = 0; if (id64 <= (UInt32)0xFFFFFFFF) { UInt32 id = (UInt32)id64; if (id == k_LZMA) { name = "LZMA"; if (propsSize == 5) { UInt32 dicSize = GetUi32((const Byte *)props + 1); char *dest = s + GetStringForSizeValue(s, dicSize); UInt32 d = props[0]; if (d != 0x5D) { UInt32 lc = d % 9; d /= 9; UInt32 pb = d / 5; UInt32 lp = d % 5; if (lc != 3) dest = AddProp32(dest, "lc", lc); if (lp != 0) dest = AddProp32(dest, "lp", lp); if (pb != 2) dest = AddProp32(dest, "pb", pb); } } } else if (id == k_LZMA2) { name = "LZMA2"; if (propsSize == 1) { Byte p = props[0]; if ((p & 1) == 0) ConvertUInt32ToString((UInt32)((p >> 1) + 12), s); else GetStringForSizeValue(s, 3 << ((p >> 1) + 11)); } } else if (id == k_PPMD) { name = "PPMD"; if (propsSize == 5) { Byte order = *props; char *dest = s; *dest++ = 'o'; ConvertUInt32ToString(order, dest); dest += MyStringLen(dest); dest = MyStpCpy(dest, ":mem"); GetStringForSizeValue(dest, GetUi32(props + 1)); } } else if (id == k_Delta) { name = "Delta"; if (propsSize == 1) ConvertUInt32ToString((UInt32)props[0] + 1, s); } else if (id == k_BCJ2) name = "BCJ2"; else if (id == k_BCJ) name = "BCJ"; else if (id == k_AES) { name = "7zAES"; if (propsSize >= 1) { Byte firstByte = props[0]; UInt32 numCyclesPower = firstByte & 0x3F; ConvertUInt32ToString(numCyclesPower, s); } } } if (name) { unsigned nameLen = MyStringLen(name); unsigned propsLen = MyStringLen(s); unsigned totalLen = nameLen + propsLen; if (propsLen != 0) totalLen++; if (needSpace) totalLen++; if (totalLen + 5 >= pos) break; pos -= totalLen; MyStringCopy(temp + pos, name); if (propsLen != 0) { char *dest = temp + pos + nameLen; *dest++ = ':'; MyStringCopy(dest, s); } if (needSpace) temp[pos + totalLen - 1] = ' '; } else { UString methodName; FindMethod(EXTERNAL_CODECS_VARS id64, methodName); if (methodName.IsEmpty()) { for (unsigned j = 0; j < methodName.Len(); j++) if (methodName[j] >= 0x80) { methodName.Empty(); break; } } if (needSpace) temp[--pos] = ' '; if (methodName.IsEmpty()) pos -= ConvertMethodIdToString_Back(temp + pos, id64); else { unsigned len = methodName.Len(); if (len + 5 > pos) break; pos -= len; for (unsigned i = 0; i < len; i++) temp[pos + i] = (char)methodName[i]; } } } if (numCoders != 0 && pos >= 4) { temp[--pos] = ' '; temp[--pos] = '.'; temp[--pos] = '.'; temp[--pos] = '.'; } return PropVarEm_Set_Str(prop, temp + pos); // } } #endif STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { PropVariant_Clear(value); // COM_TRY_BEGIN // NCOM::CPropVariant prop; /* const CRef2 &ref2 = _refs[index]; if (ref2.Refs.IsEmpty()) return E_FAIL; const CRef &ref = ref2.Refs.Front(); */ const CFileItem &item = _db.Files[index]; UInt32 index2 = index; switch(propID) { case kpidIsDir: PropVarEm_Set_Bool(value, item.IsDir); break; case kpidSize: { PropVarEm_Set_UInt64(value, item.Size); // prop = ref2.Size; break; } case kpidPackSize: { // prop = ref2.PackSize; { CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) { if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2) PropVarEm_Set_UInt64(value, _db.GetFolderFullPackSize(folderIndex)); /* else PropVarEm_Set_UInt64(value, 0); */ } else PropVarEm_Set_UInt64(value, 0); } break; } // case kpidIsAux: prop = _db.IsItemAux(index2); break; case kpidPosition: { UInt64 v; if (_db.StartPos.GetItem(index2, v)) PropVarEm_Set_UInt64(value, v); break; } case kpidCTime: SetFileTimeProp_From_UInt64Def(value, _db.CTime, index2); break; case kpidATime: SetFileTimeProp_From_UInt64Def(value, _db.ATime, index2); break; case kpidMTime: SetFileTimeProp_From_UInt64Def(value, _db.MTime, index2); break; case kpidAttrib: if (item.AttribDefined) PropVarEm_Set_UInt32(value, item.Attrib); break; case kpidCRC: if (item.CrcDefined) PropVarEm_Set_UInt32(value, item.Crc); break; case kpidEncrypted: PropVarEm_Set_Bool(value, IsFolderEncrypted(_db.FileIndexToFolderIndexMap[index2])); break; case kpidIsAnti: PropVarEm_Set_Bool(value, _db.IsItemAnti(index2)); break; /* case kpidIsAltStream: prop = item.IsAltStream; break; case kpidNtSecure: { int id = _db.SecureIDs[index]; size_t offs = _db.SecureOffsets[id]; size_t size = _db.SecureOffsets[id + 1] - offs; if (size >= 0) { prop.SetBlob(_db.SecureBuf + offs, (ULONG)size); } break; } */ case kpidPath: return _db.GetPath_Prop(index, value); #ifndef _SFX case kpidMethod: return SetMethodToProp(_db.FileIndexToFolderIndexMap[index2], value); case kpidBlock: { CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) PropVarEm_Set_UInt32(value, (UInt32)folderIndex); } break; case kpidPackedSize0: case kpidPackedSize1: case kpidPackedSize2: case kpidPackedSize3: case kpidPackedSize4: { /* CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) { const CFolder &folderInfo = _db.Folders[folderIndex]; if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2 && folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0)) { prop = _db.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0); } else prop = (UInt64)0; } else prop = (UInt64)0; */ } break; #endif } // prop.Detach(value); return S_OK; // COM_TRY_END } STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openArchiveCallback) { COM_TRY_BEGIN Close(); #ifndef _SFX _fileInfoPopIDs.Clear(); #endif try { CMyComPtr openArchiveCallbackTemp = openArchiveCallback; #ifndef _NO_CRYPTO CMyComPtr getTextPassword; if (openArchiveCallback) openArchiveCallbackTemp.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); #endif CInArchive archive; _db.IsArc = false; RINOK(archive.Open(stream, maxCheckStartPosition)); _db.IsArc = true; HRESULT result = archive.ReadDatabase( EXTERNAL_CODECS_VARS _db #ifndef _NO_CRYPTO , getTextPassword, _isEncrypted, _passwordIsDefined #endif ); RINOK(result); _inStream = stream; } catch(...) { Close(); // return E_INVALIDARG; // we must return out_of_memory here return S_FALSE; } // _inStream = stream; #ifndef _SFX FillPopIDs(); #endif return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { COM_TRY_BEGIN _inStream.Release(); _db.Clear(); #ifndef _NO_CRYPTO _isEncrypted = false; _passwordIsDefined = false; #endif return S_OK; COM_TRY_END } #ifdef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { COM_TRY_BEGIN const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); _numThreads = numProcessors; for (UInt32 i = 0; i < numProps; i++) { UString name = names[i]; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; const PROPVARIANT &value = values[i]; UInt32 number; int index = ParseStringToUInt32(name, number); if (index == 0) { if (name.IsPrefixedBy(L"mt")) { RINOK(ParseMtProp(name.Ptr(2), value, numProcessors, _numThreads)); continue; } else return E_INVALIDARG; } } return S_OK; COM_TRY_END } #endif #endif IMPL_ISetCompressCodecsInfo }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h000066400000000000000000000072511325366651500217360ustar00rootroot00000000000000// 7z/Handler.h #ifndef __7Z_HANDLER_H #define __7Z_HANDLER_H #include "../../ICoder.h" #include "../IArchive.h" #include "../../Common/CreateCoder.h" #ifndef EXTRACT_ONLY #include "../Common/HandlerOut.h" #endif #include "7zCompressionMode.h" #include "7zIn.h" namespace NArchive { namespace N7z { const UInt32 k_Copy = 0x0; const UInt32 k_Delta = 3; const UInt32 k_LZMA2 = 0x21; const UInt32 k_LZMA = 0x030101; const UInt32 k_PPMD = 0x030401; const UInt32 k_BCJ = 0x03030103; const UInt32 k_BCJ2 = 0x0303011B; const UInt32 k_Deflate = 0x040108; const UInt32 k_BZip2 = 0x040202; #ifndef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY #if !defined(_7ZIP_ST) && !defined(_SFX) #define __7Z_SET_PROPERTIES #endif #else #define __7Z_SET_PROPERTIES #endif #endif #ifndef EXTRACT_ONLY class COutHandler: public CMultiMethodProps { HRESULT SetSolidFromString(const UString &s); HRESULT SetSolidFromPROPVARIANT(const PROPVARIANT &value); public: bool _removeSfxBlock; UInt64 _numSolidFiles; UInt64 _numSolidBytes; bool _numSolidBytesDefined; bool _solidExtension; bool _compressHeaders; bool _encryptHeadersSpecified; bool _encryptHeaders; // bool _useParents; 9.26 CBoolPair Write_CTime; CBoolPair Write_ATime; CBoolPair Write_MTime; bool _volumeMode; void InitSolidFiles() { _numSolidFiles = (UInt64)(Int64)(-1); } void InitSolidSize() { _numSolidBytes = (UInt64)(Int64)(-1); } void InitSolid() { InitSolidFiles(); InitSolidSize(); _solidExtension = false; _numSolidBytesDefined = false; } void InitProps(); COutHandler() { InitProps(); } HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); }; #endif class CHandler: public IInArchive, public IArchiveGetRawProps, #ifdef __7Z_SET_PROPERTIES public ISetProperties, #endif #ifndef EXTRACT_ONLY public IOutArchive, #endif PUBLIC_ISetCompressCodecsInfo public CMyUnknownImp #ifndef EXTRACT_ONLY , public COutHandler #endif { public: MY_QUERYINTERFACE_BEGIN2(IInArchive) MY_QUERYINTERFACE_ENTRY(IArchiveGetRawProps) #ifdef __7Z_SET_PROPERTIES MY_QUERYINTERFACE_ENTRY(ISetProperties) #endif #ifndef EXTRACT_ONLY MY_QUERYINTERFACE_ENTRY(IOutArchive) #endif QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_END MY_ADDREF_RELEASE INTERFACE_IInArchive(;) INTERFACE_IArchiveGetRawProps(;) #ifdef __7Z_SET_PROPERTIES STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps); #endif #ifndef EXTRACT_ONLY INTERFACE_IOutArchive(;) #endif DECL_ISetCompressCodecsInfo CHandler(); private: CMyComPtr _inStream; NArchive::N7z::CDbEx _db; #ifndef _NO_CRYPTO bool _isEncrypted; bool _passwordIsDefined; #endif #ifdef EXTRACT_ONLY #ifdef __7Z_SET_PROPERTIES UInt32 _numThreads; #endif UInt32 _crcSize; #else CRecordVector _binds; HRESULT PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m); HRESULT SetHeaderMethod(CCompressionMethodMode &headerMethod); void AddDefaultMethod(); HRESULT SetMainMethod(CCompressionMethodMode &method, CObjectVector &methodsInfo #ifndef _7ZIP_ST , UInt32 numThreads #endif ); #endif bool IsFolderEncrypted(CNum folderIndex) const; #ifndef _SFX CRecordVector _fileInfoPopIDs; void FillPopIDs(); void AddMethodName(AString &s, UInt64 id); HRESULT SetMethodToProp(CNum folderIndex, PROPVARIANT *prop) const; #endif DECL_EXTERNAL_CODECS_VARS }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp000066400000000000000000000557231325366651500227700ustar00rootroot00000000000000// 7zHandlerOut.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../../Common/StringToInt.h" #include "../../../Common/Wildcard.h" #include "../Common/ItemNameUtils.h" #include "../Common/ParseProperties.h" #include "7zHandler.h" #include "7zOut.h" #include "7zUpdate.h" using namespace NWindows; namespace NArchive { namespace N7z { static const wchar_t *k_LZMA_Name = L"LZMA"; static const wchar_t *kDefaultMethodName = L"LZMA2"; static const wchar_t *k_Copy_Name = L"Copy"; static const wchar_t *k_MatchFinder_ForHeaders = L"BT2"; static const UInt32 k_NumFastBytes_ForHeaders = 273; static const UInt32 k_Level_ForHeaders = 5; static const UInt32 k_Dictionary_ForHeaders = #ifdef UNDER_CE 1 << 18; #else 1 << 20; #endif STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) { *type = NFileTimeType::kWindows; return S_OK; } HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m) { if (!FindMethod( EXTERNAL_CODECS_VARS m.MethodName, dest.Id, dest.NumInStreams, dest.NumOutStreams)) return E_INVALIDARG; (CProps &)dest = (CProps &)m; return S_OK; } HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod) { if (!_compressHeaders) return S_OK; COneMethodInfo m; m.MethodName = k_LZMA_Name; m.AddPropString(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders); m.AddProp32(NCoderPropID::kLevel, k_Level_ForHeaders); m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders); m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders); m.AddNumThreadsProp(1); CMethodFull methodFull; RINOK(PropsMethod_To_FullMethod(methodFull, m)); headerMethod.Methods.Add(methodFull); return S_OK; } void CHandler::AddDefaultMethod() { FOR_VECTOR (i, _methods) { UString &methodName = _methods[i].MethodName; if (methodName.IsEmpty()) methodName = kDefaultMethodName; } if (_methods.IsEmpty()) { COneMethodInfo m; m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName); _methods.Add(m); } } HRESULT CHandler::SetMainMethod( CCompressionMethodMode &methodMode, CObjectVector &methods #ifndef _7ZIP_ST , UInt32 numThreads #endif ) { AddDefaultMethod(); const UInt64 kSolidBytes_Min = (1 << 24); const UInt64 kSolidBytes_Max = ((UInt64)1 << 32) - 1; bool needSolid = false; FOR_VECTOR (i, methods) { COneMethodInfo &oneMethodInfo = methods[i]; SetGlobalLevelAndThreads(oneMethodInfo #ifndef _7ZIP_ST , numThreads #endif ); CMethodFull methodFull; RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo)); methodMode.Methods.Add(methodFull); if (methodFull.Id != k_Copy) needSolid = true; if (_numSolidBytesDefined) continue; UInt32 dicSize; switch (methodFull.Id) { case k_LZMA: case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break; case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break; case k_Deflate: dicSize = (UInt32)1 << 15; break; case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break; default: continue; } _numSolidBytes = (UInt64)dicSize << 7; if (_numSolidBytes < kSolidBytes_Min) _numSolidBytes = kSolidBytes_Min; if (_numSolidBytes > kSolidBytes_Max) _numSolidBytes = kSolidBytes_Max; _numSolidBytesDefined = true; } if (!_numSolidBytesDefined) if (needSolid) _numSolidBytes = kSolidBytes_Max; else _numSolidBytes = 0; _numSolidBytesDefined = true; return S_OK; } static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, UInt64 &ft, bool &ftDefined) { // ft = 0; // ftDefined = false; NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(index, propID, &prop)); if (prop.vt == VT_FILETIME) { ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32); ftDefined = true; } else if (prop.vt != VT_EMPTY) return E_INVALIDARG; else { ft = 0; ftDefined = false; } return S_OK; } /* #ifdef _WIN32 static const wchar_t kDirDelimiter1 = L'\\'; #endif static const wchar_t kDirDelimiter2 = L'/'; static inline bool IsCharDirLimiter(wchar_t c) { return ( #ifdef _WIN32 c == kDirDelimiter1 || #endif c == kDirDelimiter2); } static int FillSortIndex(CObjectVector &treeFolders, int cur, int curSortIndex) { CTreeFolder &tf = treeFolders[cur]; tf.SortIndex = curSortIndex++; for (int i = 0; i < tf.SubFolders.Size(); i++) curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex); tf.SortIndexEnd = curSortIndex; return curSortIndex; } static int FindSubFolder(const CObjectVector &treeFolders, int cur, const UString &name, int &insertPos) { const CIntVector &subFolders = treeFolders[cur].SubFolders; int left = 0, right = subFolders.Size(); insertPos = -1; for (;;) { if (left == right) { insertPos = left; return -1; } int mid = (left + right) / 2; int midFolder = subFolders[mid]; int compare = CompareFileNames(name, treeFolders[midFolder].Name); if (compare == 0) return midFolder; if (compare < 0) right = mid; else left = mid + 1; } } static int AddFolder(CObjectVector &treeFolders, int cur, const UString &name) { int insertPos; int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos); if (folderIndex < 0) { folderIndex = treeFolders.Size(); CTreeFolder &newFolder = treeFolders.AddNew(); newFolder.Parent = cur; newFolder.Name = name; treeFolders[cur].SubFolders.Insert(insertPos, folderIndex); } // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234; return folderIndex; } */ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) { COM_TRY_BEGIN const CDbEx *db = 0; #ifdef _7Z_VOL if (_volumes.Size() > 1) return E_FAIL; const CVolume *volume = 0; if (_volumes.Size() == 1) { volume = &_volumes.Front(); db = &volume->Database; } #else if (_inStream != 0) db = &_db; #endif /* CMyComPtr getRawProps; updateCallback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps); CUniqBlocks secureBlocks; secureBlocks.AddUniq(NULL, 0); CObjectVector treeFolders; { CTreeFolder folder; folder.Parent = -1; treeFolders.Add(folder); } */ CObjectVector updateItems; bool need_CTime = (Write_CTime.Def && Write_CTime.Val); bool need_ATime = (Write_ATime.Def && Write_ATime.Val); bool need_MTime = (Write_MTime.Def && Write_MTime.Val || !Write_MTime.Def); if (db) { if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty(); if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty(); if (!Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty(); } UString s; for (UInt32 i = 0; i < numItems; i++) { Int32 newData, newProps; UInt32 indexInArchive; if (!updateCallback) return E_FAIL; RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive)); CUpdateItem ui; ui.NewProps = IntToBool(newProps); ui.NewData = IntToBool(newData); ui.IndexInArchive = indexInArchive; ui.IndexInClient = i; ui.IsAnti = false; ui.Size = 0; UString name; // bool isAltStream = false; if (ui.IndexInArchive != -1) { if (db == 0 || (unsigned)ui.IndexInArchive >= db->Files.Size()) return E_INVALIDARG; const CFileItem &fi = db->Files[ui.IndexInArchive]; if (!ui.NewProps) { _db.GetPath(ui.IndexInArchive, name); } ui.IsDir = fi.IsDir; ui.Size = fi.Size; // isAltStream = fi.IsAltStream; ui.IsAnti = db->IsItemAnti(ui.IndexInArchive); if (!ui.NewProps) { ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime); ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime); ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime); } } if (ui.NewProps) { bool folderStatusIsDefined; { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop)); if (prop.vt == VT_EMPTY) ui.AttribDefined = false; else if (prop.vt != VT_UI4) return E_INVALIDARG; else { ui.Attrib = prop.ulVal; ui.AttribDefined = true; } } // we need MTime to sort files. if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined)); if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined)); if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined)); /* if (getRawProps) { const void *data; UInt32 dataSize; UInt32 propType; getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType); if (dataSize != 0 && propType != NPropDataType::kRaw) return E_FAIL; ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize); } */ { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidPath, &prop)); if (prop.vt == VT_EMPTY) { } else if (prop.vt != VT_BSTR) return E_INVALIDARG; else { name = NItemName::MakeLegalName(prop.bstrVal); } } { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop)); if (prop.vt == VT_EMPTY) folderStatusIsDefined = false; else if (prop.vt != VT_BOOL) return E_INVALIDARG; else { ui.IsDir = (prop.boolVal != VARIANT_FALSE); folderStatusIsDefined = true; } } { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop)); if (prop.vt == VT_EMPTY) ui.IsAnti = false; else if (prop.vt != VT_BOOL) return E_INVALIDARG; else ui.IsAnti = (prop.boolVal != VARIANT_FALSE); } /* { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop)); if (prop.vt == VT_EMPTY) isAltStream = false; else if (prop.vt != VT_BOOL) return E_INVALIDARG; else isAltStream = (prop.boolVal != VARIANT_FALSE); } */ if (ui.IsAnti) { ui.AttribDefined = false; ui.CTimeDefined = false; ui.ATimeDefined = false; ui.MTimeDefined = false; ui.Size = 0; } if (!folderStatusIsDefined && ui.AttribDefined) ui.SetDirStatusFromAttrib(); } else { /* if (_db.SecureIDs.IsEmpty()) ui.SecureIndex = secureBlocks.AddUniq(NULL, 0); else { int id = _db.SecureIDs[ui.IndexInArchive]; size_t offs = _db.SecureOffsets[id]; size_t size = _db.SecureOffsets[id + 1] - offs; ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size); } */ } /* { int folderIndex = 0; if (_useParents) { int j; s.Empty(); for (j = 0; j < name.Len(); j++) { wchar_t c = name[j]; if (IsCharDirLimiter(c)) { folderIndex = AddFolder(treeFolders, folderIndex, s); s.Empty(); continue; } s += c; } if (isAltStream) { int colonPos = s.Find(':'); if (colonPos < 0) { // isAltStream = false; return E_INVALIDARG; } UString mainName = s.Left(colonPos); int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName); if (treeFolders[newFolderIndex].UpdateItemIndex < 0) { for (int j = updateItems.Size() - 1; j >= 0; j--) { CUpdateItem &ui2 = updateItems[j]; if (ui2.ParentFolderIndex == folderIndex && ui2.Name == mainName) { ui2.TreeFolderIndex = newFolderIndex; treeFolders[newFolderIndex].UpdateItemIndex = j; } } } folderIndex = newFolderIndex; s.Delete(0, colonPos + 1); } ui.Name = s; } else ui.Name = name; ui.IsAltStream = isAltStream; ui.ParentFolderIndex = folderIndex; ui.TreeFolderIndex = -1; if (ui.IsDir && !s.IsEmpty()) { ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s); treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size(); } } */ ui.Name = name; if (ui.NewData) { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; ui.Size = (UInt64)prop.uhVal.QuadPart; if (ui.Size != 0 && ui.IsAnti) return E_INVALIDARG; } updateItems.Add(ui); } /* FillSortIndex(treeFolders, 0, 0); for (i = 0; i < (UInt32)updateItems.Size(); i++) { CUpdateItem &ui = updateItems[i]; ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex; ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd; } */ CCompressionMethodMode methodMode, headerMethod; HRESULT res = SetMainMethod(methodMode, _methods #ifndef _7ZIP_ST , _numThreads #endif ); RINOK(res); methodMode.Binds = _binds; RINOK(SetHeaderMethod(headerMethod)); #ifndef _7ZIP_ST methodMode.NumThreads = _numThreads; headerMethod.NumThreads = 1; #endif CMyComPtr getPassword2; updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2); methodMode.PasswordIsDefined = false; methodMode.Password.Empty(); if (getPassword2) { CMyComBSTR password; Int32 passwordIsDefined; RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password)); methodMode.PasswordIsDefined = IntToBool(passwordIsDefined); if (methodMode.PasswordIsDefined && (BSTR)password) methodMode.Password = password; } bool compressMainHeader = _compressHeaders; // check it bool encryptHeaders = false; if (methodMode.PasswordIsDefined) { if (_encryptHeadersSpecified) encryptHeaders = _encryptHeaders; #ifndef _NO_CRYPTO else encryptHeaders = _passwordIsDefined; #endif compressMainHeader = true; if (encryptHeaders) { headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined; headerMethod.Password = methodMode.Password; } } if (numItems < 2) compressMainHeader = false; CUpdateOptions options; options.Method = &methodMode; options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0; int level = GetLevel(); options.UseFilters = level != 0 && _autoFilter; options.MaxFilter = level >= 8; options.HeaderOptions.CompressMainHeader = compressMainHeader; /* options.HeaderOptions.WriteCTime = Write_CTime; options.HeaderOptions.WriteATime = Write_ATime; options.HeaderOptions.WriteMTime = Write_MTime; */ options.NumSolidFiles = _numSolidFiles; options.NumSolidBytes = _numSolidBytes; options.SolidExtension = _solidExtension; options.RemoveSfxBlock = _removeSfxBlock; options.VolumeMode = _volumeMode; COutArchive archive; CArchiveDatabaseOut newDatabase; CMyComPtr getPassword; updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword); /* if (secureBlocks.Sorted.Size() > 1) { secureBlocks.GetReverseMap(); for (int i = 0; i < updateItems.Size(); i++) { int &secureIndex = updateItems[i].SecureIndex; secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex]; } } */ res = Update( EXTERNAL_CODECS_VARS #ifdef _7Z_VOL volume ? volume->Stream: 0, volume ? db : 0, #else _inStream, db, #endif updateItems, // treeFolders, // secureBlocks, archive, newDatabase, outStream, updateCallback, options #ifndef _NO_CRYPTO , getPassword #endif ); RINOK(res); updateItems.ClearAndFree(); return archive.WriteDatabase(EXTERNAL_CODECS_VARS newDatabase, options.HeaderMethod, options.HeaderOptions); COM_TRY_END } static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream) { stream = 0; int index = ParseStringToUInt32(srcString, coder); if (index == 0) return E_INVALIDARG; srcString.Delete(0, index); if (srcString[0] == 's') { srcString.Delete(0); int index = ParseStringToUInt32(srcString, stream); if (index == 0) return E_INVALIDARG; srcString.Delete(0, index); } return S_OK; } void COutHandler::InitProps() { CMultiMethodProps::Init(); _removeSfxBlock = false; _compressHeaders = true; _encryptHeadersSpecified = false; _encryptHeaders = false; // _useParents = false; Write_CTime.Init(); Write_ATime.Init(); Write_MTime.Init(); _volumeMode = false; InitSolid(); } HRESULT COutHandler::SetSolidFromString(const UString &s) { UString s2 = s; s2.MakeLower_Ascii(); for (unsigned i = 0; i < s2.Len();) { const wchar_t *start = ((const wchar_t *)s2) + i; const wchar_t *end; UInt64 v = ConvertStringToUInt64(start, &end); if (start == end) { if (s2[i++] != 'e') return E_INVALIDARG; _solidExtension = true; continue; } i += (int)(end - start); if (i == s2.Len()) return E_INVALIDARG; wchar_t c = s2[i++]; if (c == 'f') { if (v < 1) v = 1; _numSolidFiles = v; } else { unsigned numBits; switch (c) { case 'b': numBits = 0; break; case 'k': numBits = 10; break; case 'm': numBits = 20; break; case 'g': numBits = 30; break; case 't': numBits = 40; break; default: return E_INVALIDARG; } _numSolidBytes = (v << numBits); _numSolidBytesDefined = true; } } return S_OK; } HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value) { bool isSolid; switch (value.vt) { case VT_EMPTY: isSolid = true; break; case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break; case VT_BSTR: if (StringToBool(value.bstrVal, isSolid)) break; return SetSolidFromString(value.bstrVal); default: return E_INVALIDARG; } if (isSolid) InitSolid(); else _numSolidFiles = 1; return S_OK; } static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest) { RINOK(PROPVARIANT_to_bool(prop, dest.Val)); dest.Def = true; return S_OK; } HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) { UString name = nameSpec; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; if (name[0] == L's') { name.Delete(0); if (name.IsEmpty()) return SetSolidFromPROPVARIANT(value); if (value.vt != VT_EMPTY) return E_INVALIDARG; return SetSolidFromString(name); } UInt32 number; int index = ParseStringToUInt32(name, number); UString realName = name.Ptr(index); if (index == 0) { if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock); if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders); // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents); if (name.IsEqualTo("hcf")) { bool compressHeadersFull = true; RINOK(PROPVARIANT_to_bool(value, compressHeadersFull)); return compressHeadersFull ? S_OK: E_INVALIDARG; } if (name.IsEqualTo("he")) { RINOK(PROPVARIANT_to_bool(value, _encryptHeaders)); _encryptHeadersSpecified = true; return S_OK; } if (name.IsEqualTo("tc")) return PROPVARIANT_to_BoolPair(value, Write_CTime); if (name.IsEqualTo("ta")) return PROPVARIANT_to_BoolPair(value, Write_ATime); if (name.IsEqualTo("tm")) return PROPVARIANT_to_BoolPair(value, Write_MTime); if (name.IsEqualTo("v")) return PROPVARIANT_to_bool(value, _volumeMode); } return CMultiMethodProps::SetProperty(name, value); } STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { COM_TRY_BEGIN _binds.Clear(); InitProps(); for (UInt32 i = 0; i < numProps; i++) { UString name = names[i]; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; const PROPVARIANT &value = values[i]; if (name[0] == 'b') { if (value.vt != VT_EMPTY) return E_INVALIDARG; name.Delete(0); CBind bind; RINOK(GetBindInfoPart(name, bind.OutCoder, bind.OutStream)); if (name[0] != ':') return E_INVALIDARG; name.Delete(0); RINOK(GetBindInfoPart(name, bind.InCoder, bind.InStream)); if (!name.IsEmpty()) return E_INVALIDARG; _binds.Add(bind); continue; } RINOK(SetProperty(name, value)); } unsigned numEmptyMethods = GetNumEmptyMethods(); if (numEmptyMethods > 0) { unsigned k; for (k = 0; k < _binds.Size(); k++) { const CBind &bind = _binds[k]; if (bind.InCoder < (UInt32)numEmptyMethods || bind.OutCoder < (UInt32)numEmptyMethods) return E_INVALIDARG; } for (k = 0; k < _binds.Size(); k++) { CBind &bind = _binds[k]; bind.InCoder -= (UInt32)numEmptyMethods; bind.OutCoder -= (UInt32)numEmptyMethods; } _methods.DeleteFrontal(numEmptyMethods); } AddDefaultMethod(); if (!_filterMethod.MethodName.IsEmpty()) { FOR_VECTOR (k, _binds) { CBind &bind = _binds[k]; bind.InCoder++; bind.OutCoder++; } _methods.Insert(0, _filterMethod); } FOR_VECTOR (k, _binds) { const CBind &bind = _binds[k]; if (bind.InCoder >= (UInt32)_methods.Size() || bind.OutCoder >= (UInt32)_methods.Size()) return E_INVALIDARG; } return S_OK; COM_TRY_END } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp000066400000000000000000000007651325366651500221070ustar00rootroot00000000000000// 7zHeader.cpp #include "StdAfx.h" #include "7zHeader.h" namespace NArchive { namespace N7z { Byte kSignature[kSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; #ifdef _7Z_VOL Byte kFinishSignature[kSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C + 1}; #endif // We can change signature. So file doesn't contain correct signature. // struct SignatureInitializer { SignatureInitializer() { kSignature[0]--; } }; // static SignatureInitializer g_SignatureInitializer; }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h000066400000000000000000000030721325366651500215460ustar00rootroot00000000000000// 7z/7zHeader.h #ifndef __7Z_HEADER_H #define __7Z_HEADER_H #include "../../../Common/MyTypes.h" namespace NArchive { namespace N7z { const unsigned kSignatureSize = 6; extern Byte kSignature[kSignatureSize]; // #define _7Z_VOL // 7z-MultiVolume is not finished yet. // It can work already, but I still do not like some // things of that new multivolume format. // So please keep it commented. #ifdef _7Z_VOL extern Byte kFinishSignature[kSignatureSize]; #endif struct CArchiveVersion { Byte Major; Byte Minor; }; const Byte kMajorVersion = 0; struct CStartHeader { UInt64 NextHeaderOffset; UInt64 NextHeaderSize; UInt32 NextHeaderCRC; }; const UInt32 kStartHeaderSize = 20; #ifdef _7Z_VOL struct CFinishHeader: public CStartHeader { UInt64 ArchiveStartOffset; // data offset from end if that struct UInt64 AdditionalStartBlockSize; // start signature & start header size }; const UInt32 kFinishHeaderSize = kStartHeaderSize + 16; #endif namespace NID { enum EEnum { kEnd, kHeader, kArchiveProperties, kAdditionalStreamsInfo, kMainStreamsInfo, kFilesInfo, kPackInfo, kUnpackInfo, kSubStreamsInfo, kSize, kCRC, kFolder, kCodersUnpackSize, kNumUnpackStream, kEmptyStream, kEmptyFile, kAnti, kName, kCTime, kATime, kMTime, kWinAttrib, kComment, kEncodedHeader, kStartPos, kDummy // kNtSecure, // kParent, // kIsAux }; } }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp000066400000000000000000001251761325366651500212710ustar00rootroot00000000000000// 7zIn.cpp #include "StdAfx.h" #ifdef _WIN32 #include #else #include #endif #include "../../../../C/7zCrc.h" #include "../../../../C/CpuArch.h" #include "../../Common/StreamObjects.h" #include "../../Common/StreamUtils.h" #include "7zDecode.h" #include "7zIn.h" #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) #define Get64(p) GetUi64(p) // define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader #ifndef _SFX #define FORMAT_7Z_RECOVERY #endif using namespace NWindows; using namespace NCOM; namespace NArchive { namespace N7z { static const UInt32 k_LZMA2 = 0x21; static const UInt32 k_LZMA = 0x030101; static void BoolVector_Fill_False(CBoolVector &v, unsigned size) { v.ClearAndSetSize(size); bool *p = &v[0]; for (unsigned i = 0; i < size; i++) p[i] = false; } static bool BoolVector_GetAndSet(CBoolVector &v, UInt32 index) { if (index >= (UInt32)v.Size()) return true; bool res = v[index]; v[index] = true; return res; } bool CFolder::CheckStructure(unsigned numUnpackSizes) const { const unsigned kNumCodersMax = sizeof(UInt32) * 8; // don't change it const unsigned kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax const unsigned kNumBindsMax = 32; if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax) return false; { CBoolVector v; BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size()); unsigned i; for (i = 0; i < BindPairs.Size(); i++) if (BoolVector_GetAndSet(v, BindPairs[i].InIndex)) return false; for (i = 0; i < PackStreams.Size(); i++) if (BoolVector_GetAndSet(v, PackStreams[i])) return false; BoolVector_Fill_False(v, numUnpackSizes); for (i = 0; i < BindPairs.Size(); i++) if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex)) return false; } UInt32 mask[kMaskSize]; unsigned i; for (i = 0; i < kMaskSize; i++) mask[i] = 0; { CUIntVector inStreamToCoder, outStreamToCoder; for (i = 0; i < Coders.Size(); i++) { CNum j; const CCoderInfo &coder = Coders[i]; for (j = 0; j < coder.NumInStreams; j++) inStreamToCoder.Add(i); for (j = 0; j < coder.NumOutStreams; j++) outStreamToCoder.Add(i); } for (i = 0; i < BindPairs.Size(); i++) { const CBindPair &bp = BindPairs[i]; mask[inStreamToCoder[bp.InIndex]] |= (1 << outStreamToCoder[bp.OutIndex]); } } for (i = 0; i < kMaskSize; i++) for (unsigned j = 0; j < kMaskSize; j++) if (((1 << j) & mask[i]) != 0) mask[i] |= mask[j]; for (i = 0; i < kMaskSize; i++) if (((1 << i) & mask[i]) != 0) return false; return true; } class CInArchiveException {}; class CUnsupportedFeatureException: public CInArchiveException {}; static void ThrowException() { throw CInArchiveException(); } static inline void ThrowEndOfData() { ThrowException(); } static inline void ThrowUnsupported() { throw CUnsupportedFeatureException(); } static inline void ThrowIncorrect() { ThrowException(); } class CStreamSwitch { CInArchive *_archive; bool _needRemove; bool _needUpdatePos; public: CStreamSwitch(): _needRemove(false), _needUpdatePos(false) {} ~CStreamSwitch() { Remove(); } void Remove(); void Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos); void Set(CInArchive *archive, const CByteBuffer &byteBuffer); void Set(CInArchive *archive, const CObjectVector *dataVector); }; void CStreamSwitch::Remove() { if (_needRemove) { if (_archive->_inByteBack->GetRem() != 0) _archive->ThereIsHeaderError = true; _archive->DeleteByteStream(_needUpdatePos); _needRemove = false; } } void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos) { Remove(); _archive = archive; _archive->AddByteStream(data, size); _needRemove = true; _needUpdatePos = needUpdatePos; } void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer) { Set(archive, byteBuffer, byteBuffer.Size(), false); } void CStreamSwitch::Set(CInArchive *archive, const CObjectVector *dataVector) { Remove(); Byte external = archive->ReadByte(); if (external != 0) { CNum dataIndex = archive->ReadNum(); if (dataIndex >= dataVector->Size()) ThrowIncorrect(); Set(archive, (*dataVector)[dataIndex]); } } void CInArchive::AddByteStream(const Byte *buf, size_t size) { if (_numInByteBufs == kNumBufLevelsMax) ThrowIncorrect(); _inByteBack = &_inByteVector[_numInByteBufs++]; _inByteBack->Init(buf, size); } Byte CInByte2::ReadByte() { if (_pos >= _size) ThrowEndOfData(); return _buffer[_pos++]; } void CInByte2::ReadBytes(Byte *data, size_t size) { if (size > _size - _pos) ThrowEndOfData(); memcpy(data, _buffer + _pos, size); _pos += size; } void CInByte2::SkipData(UInt64 size) { if (size > _size - _pos) ThrowEndOfData(); _pos += (size_t)size; } void CInByte2::SkipData() { SkipData(ReadNumber()); } static UInt64 ReadNumberSpec(const Byte *p, size_t size, size_t &processed) { if (size == 0) { processed = 0; return 0; } Byte firstByte = *p++; size--; if ((firstByte & 0x80) == 0) { processed = 1; return firstByte; } Byte mask = 0x40; if (size == 0) { processed = 0; return 0; } UInt64 value = (UInt64)*p; p++; size--; for (unsigned i = 1; i < 8; i++) { if ((firstByte & mask) == 0) { UInt64 highPart = firstByte & (mask - 1); value += (highPart << (i * 8)); processed = i + 1; return value; } if (size == 0) { processed = 0; return 0; } value |= ((UInt64)*p << (i * 8)); p++; size--; mask >>= 1; } processed = 9; return value; } UInt64 CInByte2::ReadNumber() { size_t processed; UInt64 res = ReadNumberSpec(_buffer + _pos, _size - _pos, processed); if (processed == 0) ThrowEndOfData(); _pos += processed; return res; } CNum CInByte2::ReadNum() { /* if (_pos < _size) { Byte val = _buffer[_pos]; if ((unsigned)val < 0x80) { _pos++; return (unsigned)val; } } */ UInt64 value = ReadNumber(); if (value > kNumMax) ThrowUnsupported(); return (CNum)value; } UInt32 CInByte2::ReadUInt32() { if (_pos + 4 > _size) ThrowEndOfData(); UInt32 res = Get32(_buffer + _pos); _pos += 4; return res; } UInt64 CInByte2::ReadUInt64() { if (_pos + 8 > _size) ThrowEndOfData(); UInt64 res = Get64(_buffer + _pos); _pos += 8; return res; } #define CHECK_SIGNATURE if (p[0] != '7' || p[1] != 'z' || p[2] != 0xBC || p[3] != 0xAF || p[4] != 0x27 || p[5] != 0x1C) return false; static inline bool TestSignature(const Byte *p) { CHECK_SIGNATURE return CrcCalc(p + 12, 20) == Get32(p + 8); } #ifdef FORMAT_7Z_RECOVERY static inline bool TestSignature2(const Byte *p) { CHECK_SIGNATURE; if (CrcCalc(p + 12, 20) == Get32(p + 8)) return true; for (unsigned i = 8; i < kHeaderSize; i++) if (p[i] != 0) return false; return (p[6] != 0 || p[7] != 0); } #else #define TestSignature2(p) TestSignature(p) #endif HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { RINOK(ReadStream_FALSE(stream, _header, kHeaderSize)); if (TestSignature2(_header)) return S_OK; if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0) return S_FALSE; const UInt32 kBufSize = 1 << 15; CByteArr buf(kBufSize); memcpy(buf, _header, kHeaderSize); UInt64 offset = 0; for (;;) { UInt32 readSize = kBufSize - kHeaderSize; { UInt64 rem = *searchHeaderSizeLimit - offset; if (readSize > rem) readSize = (UInt32)rem; if (readSize == 0) return S_FALSE; } UInt32 processed = 0; RINOK(stream->Read(buf + kHeaderSize, readSize, &processed)); if (processed == 0) return S_FALSE; for (UInt32 pos = 0;;) { const Byte *p = buf + pos + 1; const Byte *lim = buf + processed; for (; p <= lim; p += 4) { if (p[0] == '7') break; if (p[1] == '7') { p += 1; break; } if (p[2] == '7') { p += 2; break; } if (p[3] == '7') { p += 3; break; } }; if (p > lim) break; pos = (UInt32)(p - buf); if (TestSignature(p)) { memcpy(_header, p, kHeaderSize); _arhiveBeginStreamPosition += offset + pos; return stream->Seek(_arhiveBeginStreamPosition + kHeaderSize, STREAM_SEEK_SET, NULL); } } offset += processed; memmove(buf, buf + processed, kHeaderSize); } } // S_FALSE means that file is not archive HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { HeadersSize = 0; Close(); RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition)) RINOK(stream->Seek(0, STREAM_SEEK_END, &_fileEndPosition)) RINOK(stream->Seek(_arhiveBeginStreamPosition, STREAM_SEEK_SET, NULL)) RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); _stream = stream; return S_OK; } void CInArchive::Close() { _numInByteBufs = 0; _stream.Release(); ThereIsHeaderError = false; } void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) { for (;;) { if (ReadID() == NID::kEnd) break; SkipData(); } } // CFolder &folder can be non empty. So we must set all fields void CInByte2::ParseFolder(CFolder &folder) { CNum numCoders = ReadNum(); folder.Coders.SetSize(numCoders); CNum numInStreams = 0; CNum numOutStreams = 0; CNum i; for (i = 0; i < numCoders; i++) { CCoderInfo &coder = folder.Coders[i]; { Byte mainByte = ReadByte(); if ((mainByte & 0xC0) != 0) ThrowUnsupported(); unsigned idSize = (mainByte & 0xF); if (idSize > 8 || idSize > GetRem()) ThrowUnsupported(); const Byte *longID = GetPtr(); UInt64 id = 0; for (unsigned j = 0; j < idSize; j++) id = ((id << 8) | longID[j]); SkipDataNoCheck(idSize); coder.MethodID = id; if ((mainByte & 0x10) != 0) { coder.NumInStreams = ReadNum(); coder.NumOutStreams = ReadNum(); } else { coder.NumInStreams = 1; coder.NumOutStreams = 1; } if ((mainByte & 0x20) != 0) { CNum propsSize = ReadNum(); coder.Props.Alloc((size_t)propsSize); ReadBytes((Byte *)coder.Props, (size_t)propsSize); } else coder.Props.Free(); } numInStreams += coder.NumInStreams; numOutStreams += coder.NumOutStreams; } CNum numBindPairs = numOutStreams - 1; folder.BindPairs.SetSize(numBindPairs); for (i = 0; i < numBindPairs; i++) { CBindPair &bp = folder.BindPairs[i]; bp.InIndex = ReadNum(); bp.OutIndex = ReadNum(); } if (numInStreams < numBindPairs) ThrowUnsupported(); CNum numPackStreams = numInStreams - numBindPairs; folder.PackStreams.SetSize(numPackStreams); if (numPackStreams == 1) { for (i = 0; i < numInStreams; i++) if (folder.FindBindPairForInStream(i) < 0) { folder.PackStreams[0] = i; break; } if (i == numInStreams) ThrowUnsupported(); } else for (i = 0; i < numPackStreams; i++) folder.PackStreams[i] = ReadNum(); } void CFolders::ParseFolderInfo(unsigned folderIndex, CFolder &folder) const { size_t startPos = FoCodersDataOffset[folderIndex]; CInByte2 inByte; inByte.Init(CodersData + startPos, FoCodersDataOffset[folderIndex + 1] - startPos); inByte.ParseFolder(folder); if (inByte.GetRem() != 0) throw 20120424; } void CDatabase::GetPath(unsigned index, UString &path) const { path.Empty(); if (!NameOffsets || !NamesBuf) return; size_t offset = NameOffsets[index]; size_t size = NameOffsets[index + 1] - offset - 1; if (size >= (1 << 20)) return; wchar_t *s = path.GetBuffer((unsigned)size); const Byte *p = ((const Byte *)NamesBuf + offset * 2); #if defined(_WIN32) && defined(MY_CPU_LE) wmemcpy(s, (const wchar_t *)p, size); #else for (size_t i = 0; i < size; i++) { *s = Get16(p); p += 2; s++; } #endif path.ReleaseBuffer((unsigned)size); } HRESULT CDatabase::GetPath_Prop(unsigned index, PROPVARIANT *path) const throw() { PropVariant_Clear(path); if (!NameOffsets || !NamesBuf) return S_OK; size_t offset = NameOffsets[index]; size_t size = NameOffsets[index + 1] - offset; if (size >= (1 << 14)) return S_OK; RINOK(PropVarEm_Alloc_Bstr(path, (unsigned)size - 1)); wchar_t *s = path->bstrVal; const Byte *p = ((const Byte *)NamesBuf + offset * 2); for (size_t i = 0; i < size; i++) { wchar_t c = Get16(p); p += 2; #if WCHAR_PATH_SEPARATOR != L'/' if (c == L'/') c = WCHAR_PATH_SEPARATOR; #endif *s++ = c; } return S_OK; /* unsigned cur = index; unsigned size = 0; for (int i = 0;; i++) { size_t len = NameOffsets[cur + 1] - NameOffsets[cur]; size += (unsigned)len; if (i > 256 || len > (1 << 14) || size > (1 << 14)) return PropVarEm_Set_Str(path, "[TOO-LONG]"); cur = Files[cur].Parent; if (cur < 0) break; } size--; RINOK(PropVarEm_Alloc_Bstr(path, size)); wchar_t *s = path->bstrVal; s += size; *s = 0; cur = index; for (;;) { unsigned len = (unsigned)(NameOffsets[cur + 1] - NameOffsets[cur] - 1); const Byte *p = (const Byte *)NamesBuf + (NameOffsets[cur + 1] * 2) - 2; do { p -= 2; --s; wchar_t c = Get16(p); if (c == '/') c = WCHAR_PATH_SEPARATOR; *s = c; } while (--len); const CFileItem &file = Files[cur]; cur = file.Parent; if (cur < 0) return S_OK; *(--s) = (file.IsAltStream ? ':' : WCHAR_PATH_SEPARATOR); } */ } void CInArchive::WaitId(UInt64 id) { for (;;) { UInt64 type = ReadID(); if (type == id) return; if (type == NID::kEnd) ThrowIncorrect(); SkipData(); } } void CInArchive::ReadHashDigests(unsigned numItems, CUInt32DefVector &crcs) { ReadBoolVector2(numItems, crcs.Defs); crcs.Vals.ClearAndSetSize(numItems); UInt32 *p = &crcs.Vals[0]; const bool *defs = &crcs.Defs[0]; for (unsigned i = 0; i < numItems; i++) { UInt32 crc = 0; if (defs[i]) crc = ReadUInt32(); p[i] = crc; } } void CInArchive::ReadPackInfo(CFolders &f) { CNum numPackStreams = ReadNum(); WaitId(NID::kSize); f.PackPositions.Alloc(numPackStreams + 1); f.NumPackStreams = numPackStreams; UInt64 sum = 0; for (CNum i = 0; i < numPackStreams; i++) { f.PackPositions[i] = sum; UInt64 packSize = ReadNumber(); sum += packSize; if (sum < packSize) ThrowIncorrect(); } f.PackPositions[numPackStreams] = sum; UInt64 type; for (;;) { type = ReadID(); if (type == NID::kEnd) return; if (type == NID::kCRC) { CUInt32DefVector PackCRCs; ReadHashDigests(numPackStreams, PackCRCs); continue; } SkipData(); } } void CInArchive::ReadUnpackInfo( const CObjectVector *dataVector, CFolders &folders) { WaitId(NID::kFolder); CNum numFolders = ReadNum(); CNum numCodersOutStreams = 0; { CStreamSwitch streamSwitch; streamSwitch.Set(this, dataVector); const Byte *startBufPtr = _inByteBack->GetPtr(); folders.NumFolders = numFolders; folders.FoStartPackStreamIndex.Alloc(numFolders + 1); folders.FoToMainUnpackSizeIndex.Alloc(numFolders); folders.FoCodersDataOffset.Alloc(numFolders + 1); folders.FoToCoderUnpackSizes.Alloc(numFolders + 1); CRecordVector InStreamUsed; CRecordVector OutStreamUsed; CNum packStreamIndex = 0; CNum fo; CInByte2 *inByte = _inByteBack; for (fo = 0; fo < numFolders; fo++) { UInt32 numOutStreams = 0; UInt32 indexOfMainStream = 0; UInt32 numPackStreams = 0; folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr; numOutStreams = 0; CNum numInStreams = 0; CNum numCoders = inByte->ReadNum(); for (CNum ci = 0; ci < numCoders; ci++) { Byte mainByte = inByte->ReadByte(); if ((mainByte & 0xC0) != 0) ThrowUnsupported(); unsigned idSize = (mainByte & 0xF); if (idSize > 8) ThrowUnsupported(); if (idSize > inByte->GetRem()) ThrowEndOfData(); const Byte *longID = inByte->GetPtr(); UInt64 id = 0; for (unsigned j = 0; j < idSize; j++) id = ((id << 8) | longID[j]); inByte->SkipDataNoCheck(idSize); if (folders.ParsedMethods.IDs.Size() < 128) folders.ParsedMethods.IDs.AddToUniqueSorted(id); CNum coderInStreams = 1; CNum coderOutStreams = 1; if ((mainByte & 0x10) != 0) { coderInStreams = inByte->ReadNum(); coderOutStreams = inByte->ReadNum(); } numInStreams += coderInStreams; if (numInStreams < coderInStreams) ThrowUnsupported(); numOutStreams += coderOutStreams; if (numOutStreams < coderOutStreams) ThrowUnsupported(); if ((mainByte & 0x20) != 0) { CNum propsSize = inByte->ReadNum(); if (propsSize > inByte->GetRem()) ThrowEndOfData(); if (id == k_LZMA2 && propsSize == 1) { Byte v = *_inByteBack->GetPtr(); if (folders.ParsedMethods.Lzma2Prop < v) folders.ParsedMethods.Lzma2Prop = v; } else if (id == k_LZMA && propsSize == 5) { UInt32 dicSize = GetUi32(_inByteBack->GetPtr() + 1); if (folders.ParsedMethods.LzmaDic < dicSize) folders.ParsedMethods.LzmaDic = dicSize; } inByte->SkipDataNoCheck((size_t)propsSize); } } if (numOutStreams == 1 && numInStreams == 1) { indexOfMainStream = 0; numPackStreams = 1; } else { UInt32 i; if (numOutStreams == 0) ThrowUnsupported(); CNum numBindPairs = numOutStreams - 1; if (numInStreams < numBindPairs) ThrowUnsupported(); if (numInStreams >= 256 || numOutStreams >= 256) ThrowUnsupported(); InStreamUsed.ClearAndSetSize(numInStreams); for (i = 0; i < numInStreams; i++) InStreamUsed[i] = false; OutStreamUsed.ClearAndSetSize(numOutStreams); for (i = 0; i < numOutStreams; i++) OutStreamUsed[i] = false; for (i = 0; i < numBindPairs; i++) { CNum index = ReadNum(); if (index >= numInStreams || InStreamUsed[index]) ThrowUnsupported(); InStreamUsed[index] = true; index = ReadNum(); if (index >= numOutStreams || OutStreamUsed[index]) ThrowUnsupported(); OutStreamUsed[index] = true; } numPackStreams = numInStreams - numBindPairs; if (numPackStreams != 1) for (i = 0; i < numPackStreams; i++) inByte->ReadNum(); // PackStreams for (i = 0; i < numOutStreams; i++) if (!OutStreamUsed[i]) { indexOfMainStream = i; break; } if (i == numOutStreams) ThrowUnsupported(); } folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams; numCodersOutStreams += numOutStreams; folders.FoStartPackStreamIndex[fo] = packStreamIndex; packStreamIndex += numPackStreams; folders.FoToMainUnpackSizeIndex[fo] = (Byte)indexOfMainStream; } size_t dataSize = _inByteBack->GetPtr() - startBufPtr; folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams; folders.FoStartPackStreamIndex[fo] = packStreamIndex; folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr; folders.CodersData.CopyFrom(startBufPtr, dataSize); } WaitId(NID::kCodersUnpackSize); folders.CoderUnpackSizes.Alloc(numCodersOutStreams); for (CNum i = 0; i < numCodersOutStreams; i++) folders.CoderUnpackSizes[i] = ReadNumber(); for (;;) { UInt64 type = ReadID(); if (type == NID::kEnd) return; if (type == NID::kCRC) { ReadHashDigests(numFolders, folders.FolderCRCs); continue; } SkipData(); } } void CInArchive::ReadSubStreamsInfo( CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests) { folders.NumUnpackStreamsVector.Alloc(folders.NumFolders); CNum i; for (i = 0; i < folders.NumFolders; i++) folders.NumUnpackStreamsVector[i] = 1; UInt64 type; for (;;) { type = ReadID(); if (type == NID::kNumUnpackStream) { for (i = 0; i < folders.NumFolders; i++) folders.NumUnpackStreamsVector[i] = ReadNum(); continue; } if (type == NID::kCRC || type == NID::kSize || type == NID::kEnd) break; SkipData(); } if (type == NID::kSize) { for (i = 0; i < folders.NumFolders; i++) { // v3.13 incorrectly worked with empty folders // v4.07: we check that folder is empty CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams == 0) continue; UInt64 sum = 0; for (CNum j = 1; j < numSubstreams; j++) { UInt64 size = ReadNumber(); unpackSizes.Add(size); sum += size; if (sum < size) ThrowIncorrect(); } UInt64 folderUnpackSize = folders.GetFolderUnpackSize(i); if (folderUnpackSize < sum) ThrowIncorrect(); unpackSizes.Add(folderUnpackSize - sum); } type = ReadID(); } else { for (i = 0; i < folders.NumFolders; i++) { /* v9.26 - v9.29 incorrectly worked: if (folders.NumUnpackStreamsVector[i] == 0), it threw error */ CNum val = folders.NumUnpackStreamsVector[i]; if (val > 1) ThrowIncorrect(); if (val == 1) unpackSizes.Add(folders.GetFolderUnpackSize(i)); } } unsigned numDigests = 0; for (i = 0; i < folders.NumFolders; i++) { CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams != 1 || !folders.FolderCRCs.ValidAndDefined(i)) numDigests += numSubstreams; } for (;;) { if (type == NID::kEnd) break; if (type == NID::kCRC) { // CUInt32DefVector digests2; // ReadHashDigests(numDigests, digests2); CBoolVector digests2; ReadBoolVector2(numDigests, digests2); digests.ClearAndSetSize(unpackSizes.Size()); unsigned k = 0; unsigned k2 = 0; for (i = 0; i < folders.NumFolders; i++) { CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i)) { digests.Defs[k] = true; digests.Vals[k] = folders.FolderCRCs.Vals[i]; k++; } else for (CNum j = 0; j < numSubstreams; j++) { bool defined = digests2[k2++]; digests.Defs[k] = defined; UInt32 crc = 0; if (defined) crc = ReadUInt32(); digests.Vals[k] = crc; k++; } } // if (k != unpackSizes.Size()) throw 1234567; } else SkipData(); type = ReadID(); } if (digests.Defs.Size() != unpackSizes.Size()) { digests.ClearAndSetSize(unpackSizes.Size()); unsigned k = 0; for (i = 0; i < folders.NumFolders; i++) { CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i)) { digests.Defs[k] = true; digests.Vals[k] = folders.FolderCRCs.Vals[i]; k++; } else for (CNum j = 0; j < numSubstreams; j++) { digests.Defs[k] = false; digests.Vals[k] = 0; k++; } } } } void CInArchive::ReadStreamsInfo( const CObjectVector *dataVector, UInt64 &dataOffset, CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests) { UInt64 type = ReadID(); if (type == NID::kPackInfo) { dataOffset = ReadNumber(); ReadPackInfo(folders); type = ReadID(); } if (type == NID::kUnpackInfo) { ReadUnpackInfo(dataVector, folders); type = ReadID(); } if (folders.NumFolders != 0 && !folders.PackPositions) { // if there are folders, we need PackPositions also folders.PackPositions.Alloc(1); folders.PackPositions[0] = 0; } if (type == NID::kSubStreamsInfo) { ReadSubStreamsInfo(folders, unpackSizes, digests); type = ReadID(); } else { folders.NumUnpackStreamsVector.Alloc(folders.NumFolders); /* If digests.Defs.Size() == 0, it means that there are no crcs. So we don't need to fill digests with values. */ // digests.Vals.ClearAndSetSize(folders.NumFolders); // BoolVector_Fill_False(digests.Defs, folders.NumFolders); for (CNum i = 0; i < folders.NumFolders; i++) { folders.NumUnpackStreamsVector[i] = 1; unpackSizes.Add(folders.GetFolderUnpackSize(i)); // digests.Vals[i] = 0; } } if (type != NID::kEnd) ThrowIncorrect(); } void CInArchive::ReadBoolVector(unsigned numItems, CBoolVector &v) { v.ClearAndSetSize(numItems); Byte b = 0; Byte mask = 0; bool *p = &v[0]; for (unsigned i = 0; i < numItems; i++) { if (mask == 0) { b = ReadByte(); mask = 0x80; } p[i] = ((b & mask) != 0); mask >>= 1; } } void CInArchive::ReadBoolVector2(unsigned numItems, CBoolVector &v) { Byte allAreDefined = ReadByte(); if (allAreDefined == 0) { ReadBoolVector(numItems, v); return; } v.ClearAndSetSize(numItems); bool *p = &v[0]; for (unsigned i = 0; i < numItems; i++) p[i] = true; } void CInArchive::ReadUInt64DefVector(const CObjectVector &dataVector, CUInt64DefVector &v, unsigned numItems) { ReadBoolVector2(numItems, v.Defs); CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); v.Vals.ClearAndSetSize(numItems); UInt64 *p = &v.Vals[0]; const bool *defs = &v.Defs[0]; for (unsigned i = 0; i < numItems; i++) { UInt64 t = 0; if (defs[i]) t = ReadUInt64(); p[i] = t; } } HRESULT CInArchive::ReadAndDecodePackedStreams( DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset, UInt64 &dataOffset, CObjectVector &dataVector _7Z_DECODER_CRYPRO_VARS_DECL ) { CFolders folders; CRecordVector unpackSizes; CUInt32DefVector digests; ReadStreamsInfo(NULL, dataOffset, folders, unpackSizes, digests); CDecoder decoder( #ifdef _ST_MODE false #else true #endif ); for (CNum i = 0; i < folders.NumFolders; i++) { CByteBuffer &data = dataVector.AddNew(); UInt64 unpackSize64 = folders.GetFolderUnpackSize(i); size_t unpackSize = (size_t)unpackSize64; if (unpackSize != unpackSize64) ThrowUnsupported(); data.Alloc(unpackSize); CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; CMyComPtr outStream = outStreamSpec; outStreamSpec->Init(data, unpackSize); HRESULT result = decoder.Decode( EXTERNAL_CODECS_LOC_VARS _stream, baseOffset + dataOffset, folders, i, outStream, NULL _7Z_DECODER_CRYPRO_VARS #if !defined(_7ZIP_ST) && !defined(_SFX) , false, 1 #endif ); RINOK(result); if (folders.FolderCRCs.ValidAndDefined(i)) if (CrcCalc(data, unpackSize) != folders.FolderCRCs.Vals[i]) ThrowIncorrect(); } HeadersSize += folders.PackPositions[folders.NumPackStreams]; return S_OK; } HRESULT CInArchive::ReadHeader( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ) { UInt64 type = ReadID(); if (type == NID::kArchiveProperties) { ReadArchiveProperties(db.ArcInfo); type = ReadID(); } CObjectVector dataVector; if (type == NID::kAdditionalStreamsInfo) { HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS db.ArcInfo.StartPositionAfterHeader, db.ArcInfo.DataStartPosition2, dataVector _7Z_DECODER_CRYPRO_VARS ); RINOK(result); db.ArcInfo.DataStartPosition2 += db.ArcInfo.StartPositionAfterHeader; type = ReadID(); } CRecordVector unpackSizes; CUInt32DefVector digests; if (type == NID::kMainStreamsInfo) { ReadStreamsInfo(&dataVector, db.ArcInfo.DataStartPosition, (CFolders &)db, unpackSizes, digests); db.ArcInfo.DataStartPosition += db.ArcInfo.StartPositionAfterHeader; type = ReadID(); } db.Files.Clear(); if (type == NID::kFilesInfo) { CNum numFiles = ReadNum(); db.Files.ClearAndSetSize(numFiles); CNum i; /* db.Files.Reserve(numFiles); CNum i; for (i = 0; i < numFiles; i++) db.Files.Add(CFileItem()); */ db.ArcInfo.FileInfoPopIDs.Add(NID::kSize); // if (!db.PackSizes.IsEmpty()) db.ArcInfo.FileInfoPopIDs.Add(NID::kPackInfo); if (numFiles > 0 && !digests.Defs.IsEmpty()) db.ArcInfo.FileInfoPopIDs.Add(NID::kCRC); CBoolVector emptyStreamVector; BoolVector_Fill_False(emptyStreamVector, (unsigned)numFiles); CBoolVector emptyFileVector; CBoolVector antiFileVector; CNum numEmptyStreams = 0; for (;;) { UInt64 type = ReadID(); if (type == NID::kEnd) break; UInt64 size = ReadNumber(); if (size > _inByteBack->GetRem()) ThrowIncorrect(); CStreamSwitch switchProp; switchProp.Set(this, _inByteBack->GetPtr(), (size_t)size, true); bool addPropIdToList = true; bool isKnownType = true; if (type > ((UInt32)1 << 30)) isKnownType = false; else switch((UInt32)type) { case NID::kName: { CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); size_t rem = _inByteBack->GetRem(); db.NamesBuf.Alloc(rem); ReadBytes(db.NamesBuf, rem); db.NameOffsets.Alloc(db.Files.Size() + 1); size_t pos = 0; unsigned i; for (i = 0; i < db.Files.Size(); i++) { size_t curRem = (rem - pos) / 2; const UInt16 *buf = (const UInt16 *)(db.NamesBuf + pos); size_t j; for (j = 0; j < curRem && buf[j] != 0; j++); if (j == curRem) ThrowEndOfData(); db.NameOffsets[i] = pos / 2; pos += j * 2 + 2; } db.NameOffsets[i] = pos / 2; if (pos != rem) ThereIsHeaderError = true; break; } case NID::kWinAttrib: { CBoolVector boolVector; ReadBoolVector2(db.Files.Size(), boolVector); CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); for (i = 0; i < numFiles; i++) { CFileItem &file = db.Files[i]; file.AttribDefined = boolVector[i]; if (file.AttribDefined) file.Attrib = ReadUInt32(); } break; } /* case NID::kIsAux: { ReadBoolVector(db.Files.Size(), db.IsAux); break; } case NID::kParent: { db.IsTree = true; // CBoolVector boolVector; // ReadBoolVector2(db.Files.Size(), boolVector); // CStreamSwitch streamSwitch; // streamSwitch.Set(this, &dataVector); CBoolVector boolVector; ReadBoolVector2(db.Files.Size(), boolVector); db.ThereAreAltStreams = false; for (i = 0; i < numFiles; i++) { CFileItem &file = db.Files[i]; // file.Parent = -1; // if (boolVector[i]) file.Parent = (int)ReadUInt32(); file.IsAltStream = !boolVector[i]; if (file.IsAltStream) db.ThereAreAltStreams = true; } break; } */ case NID::kEmptyStream: { ReadBoolVector(numFiles, emptyStreamVector); numEmptyStreams = 0; for (i = 0; i < (CNum)emptyStreamVector.Size(); i++) if (emptyStreamVector[i]) numEmptyStreams++; BoolVector_Fill_False(emptyFileVector, numEmptyStreams); BoolVector_Fill_False(antiFileVector, numEmptyStreams); break; } case NID::kEmptyFile: ReadBoolVector(numEmptyStreams, emptyFileVector); break; case NID::kAnti: ReadBoolVector(numEmptyStreams, antiFileVector); break; case NID::kStartPos: ReadUInt64DefVector(dataVector, db.StartPos, (unsigned)numFiles); break; case NID::kCTime: ReadUInt64DefVector(dataVector, db.CTime, (unsigned)numFiles); break; case NID::kATime: ReadUInt64DefVector(dataVector, db.ATime, (unsigned)numFiles); break; case NID::kMTime: ReadUInt64DefVector(dataVector, db.MTime, (unsigned)numFiles); break; case NID::kDummy: { for (UInt64 j = 0; j < size; j++) if (ReadByte() != 0) ThereIsHeaderError = true; addPropIdToList = false; break; } /* case NID::kNtSecure: { try { { CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); UInt32 numDescriptors = ReadUInt32(); size_t offset = 0; db.SecureOffsets.Clear(); for (i = 0; i < numDescriptors; i++) { UInt32 size = ReadUInt32(); db.SecureOffsets.Add(offset); offset += size; } // ThrowIncorrect();; db.SecureOffsets.Add(offset); db.SecureBuf.SetCapacity(offset); for (i = 0; i < numDescriptors; i++) { offset = db.SecureOffsets[i]; ReadBytes(db.SecureBuf + offset, db.SecureOffsets[i + 1] - offset); } db.SecureIDs.Clear(); for (unsigned i = 0; i < db.Files.Size(); i++) { db.SecureIDs.Add(ReadNum()); // db.SecureIDs.Add(ReadUInt32()); } // ReadUInt32(); if (_inByteBack->GetRem() != 0) ThrowIncorrect();; } } catch(CInArchiveException &) { ThereIsHeaderError = true; addPropIdToList = isKnownType = false; db.ClearSecure(); } break; } */ default: addPropIdToList = isKnownType = false; } if (isKnownType) { if (addPropIdToList) db.ArcInfo.FileInfoPopIDs.Add(type); } else { db.UnsupportedFeatureWarning = true; _inByteBack->SkipRem(); } // SkipData worked incorrectly in some versions before v4.59 (7zVer <= 00.02) if (_inByteBack->GetRem() != 0) ThrowIncorrect(); } type = ReadID(); // Read (NID::kEnd) end of headers CNum emptyFileIndex = 0; CNum sizeIndex = 0; CNum numAntiItems = 0; for (i = 0; i < numEmptyStreams; i++) if (antiFileVector[i]) numAntiItems++; for (i = 0; i < numFiles; i++) { CFileItem &file = db.Files[i]; bool isAnti; file.HasStream = !emptyStreamVector[i]; file.Crc = 0; if (file.HasStream) { file.IsDir = false; isAnti = false; file.Size = unpackSizes[sizeIndex]; file.CrcDefined = digests.ValidAndDefined(sizeIndex); if (file.CrcDefined) file.Crc = digests.Vals[sizeIndex]; sizeIndex++; } else { file.IsDir = !emptyFileVector[emptyFileIndex]; isAnti = antiFileVector[emptyFileIndex]; emptyFileIndex++; file.Size = 0; file.CrcDefined = false; } if (numAntiItems != 0) db.IsAnti.Add(isAnti); } } db.FillLinks(); /* if (type != NID::kEnd) ThrowIncorrect(); if (_inByteBack->GetRem() != 0) ThrowIncorrect(); */ return S_OK; } void CDbEx::FillLinks() { FolderStartFileIndex.ClearAndSetSize(NumFolders); FileIndexToFolderIndexMap.ClearAndSetSize(Files.Size()); CNum folderIndex = 0; CNum indexInFolder = 0; unsigned i; for (i = 0; i < Files.Size(); i++) { bool emptyStream = !Files[i].HasStream; if (indexInFolder == 0) { if (emptyStream) { FileIndexToFolderIndexMap[i] = kNumNoIndex; continue; } // v3.13 incorrectly worked with empty folders // v4.07: we skip empty folders for (;;) { if (folderIndex >= NumFolders) ThrowIncorrect(); FolderStartFileIndex[folderIndex] = i; if (NumUnpackStreamsVector[folderIndex] != 0) break; folderIndex++; } } FileIndexToFolderIndexMap[i] = folderIndex; if (emptyStream) continue; if (++indexInFolder >= NumUnpackStreamsVector[folderIndex]) { folderIndex++; indexInFolder = 0; } } if (indexInFolder != 0) folderIndex++; /* if (indexInFolder != 0) ThrowIncorrect(); */ for (;;) { if (folderIndex >= NumFolders) return; FolderStartFileIndex[folderIndex] = i; /* if (NumUnpackStreamsVector[folderIndex] != 0) ThrowIncorrect();; */ folderIndex++; } } HRESULT CInArchive::ReadDatabase2( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ) { db.Clear(); db.ArcInfo.StartPosition = _arhiveBeginStreamPosition; db.ArcInfo.Version.Major = _header[6]; db.ArcInfo.Version.Minor = _header[7]; if (db.ArcInfo.Version.Major != kMajorVersion) { // db.UnsupportedVersion = true; return S_FALSE; } UInt64 nextHeaderOffset = Get64(_header + 12); UInt64 nextHeaderSize = Get64(_header + 20); UInt32 nextHeaderCRC = Get32(_header + 28); #ifdef FORMAT_7Z_RECOVERY UInt32 crcFromArc = Get32(_header + 8); if (crcFromArc == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0) { UInt64 cur, fileSize; RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur)); const unsigned kCheckSize = 512; Byte buf[kCheckSize]; RINOK(_stream->Seek(0, STREAM_SEEK_END, &fileSize)); UInt64 rem = fileSize - cur; unsigned checkSize = kCheckSize; if (rem < kCheckSize) checkSize = (unsigned)(rem); if (checkSize < 3) return S_FALSE; RINOK(_stream->Seek(fileSize - checkSize, STREAM_SEEK_SET, NULL)); RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize)); if (buf[checkSize - 1] != 0) return S_FALSE; unsigned i; for (i = checkSize - 2;; i--) { if (buf[i] == NID::kEncodedHeader && buf[i + 1] == NID::kPackInfo || buf[i] == NID::kHeader && buf[i + 1] == NID::kMainStreamsInfo) break; if (i == 0) return S_FALSE; } nextHeaderSize = checkSize - i; nextHeaderOffset = rem - nextHeaderSize; nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL)); db.StartHeaderWasRecovered = true; } else #endif { // Crc was tested already at signature check // if (CrcCalc(_header + 12, 20) != crcFromArchive) ThrowIncorrect(); } db.ArcInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize; db.PhySize = kHeaderSize; db.IsArc = false; if ((Int64)nextHeaderOffset < 0 || nextHeaderSize > ((UInt64)1 << 62)) return S_FALSE; if (nextHeaderSize == 0) { if (nextHeaderOffset != 0) return S_FALSE; db.IsArc = true; return S_OK; } if (!db.StartHeaderWasRecovered) db.IsArc = true; HeadersSize += kHeaderSize + nextHeaderSize; db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize; if (_fileEndPosition - db.ArcInfo.StartPositionAfterHeader < nextHeaderOffset + nextHeaderSize) { db.UnexpectedEnd = true; return S_FALSE; } RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL)); size_t nextHeaderSize_t = (size_t)nextHeaderSize; if (nextHeaderSize_t != nextHeaderSize) return E_OUTOFMEMORY; CByteBuffer buffer2(nextHeaderSize_t); RINOK(ReadStream_FALSE(_stream, buffer2, nextHeaderSize_t)); if (CrcCalc(buffer2, nextHeaderSize_t) != nextHeaderCRC) ThrowIncorrect(); if (!db.StartHeaderWasRecovered) db.PhySizeWasConfirmed = true; CStreamSwitch streamSwitch; streamSwitch.Set(this, buffer2); CObjectVector dataVector; UInt64 type = ReadID(); if (type != NID::kHeader) { if (type != NID::kEncodedHeader) ThrowIncorrect(); HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS db.ArcInfo.StartPositionAfterHeader, db.ArcInfo.DataStartPosition2, dataVector _7Z_DECODER_CRYPRO_VARS ); RINOK(result); if (dataVector.Size() == 0) return S_OK; if (dataVector.Size() > 1) ThrowIncorrect(); streamSwitch.Remove(); streamSwitch.Set(this, dataVector.Front()); if (ReadID() != NID::kHeader) ThrowIncorrect(); } db.IsArc = true; db.HeadersSize = HeadersSize; return ReadHeader( EXTERNAL_CODECS_LOC_VARS db _7Z_DECODER_CRYPRO_VARS ); } HRESULT CInArchive::ReadDatabase( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ) { try { HRESULT res = ReadDatabase2( EXTERNAL_CODECS_LOC_VARS db _7Z_DECODER_CRYPRO_VARS ); if (ThereIsHeaderError) db.ThereIsHeaderError = true; if (res == E_NOTIMPL) ThrowUnsupported(); return res; } catch(CUnsupportedFeatureException &) { db.UnsupportedFeatureError = true; return S_FALSE; } catch(CInArchiveException &) { db.ThereIsHeaderError = true; return S_FALSE; } } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.h000066400000000000000000000240431325366651500207250ustar00rootroot00000000000000// 7zIn.h #ifndef __7Z_IN_H #define __7Z_IN_H #include "../../../Common/MyCom.h" #include "../../../Windows/PropVariant.h" #include "../../IPassword.h" #include "../../IStream.h" #include "../../Common/CreateCoder.h" #include "../../Common/InBuffer.h" #include "7zItem.h" namespace NArchive { namespace N7z { /* We don't need to init isEncrypted and passwordIsDefined We must upgrade them only */ #ifdef _NO_CRYPTO #define _7Z_DECODER_CRYPRO_VARS_DECL #define _7Z_DECODER_CRYPRO_VARS #else #define _7Z_DECODER_CRYPRO_VARS_DECL , ICryptoGetTextPassword *getTextPassword, bool &isEncrypted, bool &passwordIsDefined #define _7Z_DECODER_CRYPRO_VARS , getTextPassword, isEncrypted, passwordIsDefined #endif struct CParsedMethods { Byte Lzma2Prop; UInt32 LzmaDic; CRecordVector IDs; CParsedMethods(): Lzma2Prop(0), LzmaDic(0) {} }; struct CFolders { CNum NumPackStreams; CNum NumFolders; CObjArray PackPositions; // NumPackStreams + 1 // CUInt32DefVector PackCRCs; // we don't use PackCRCs now CUInt32DefVector FolderCRCs; // NumFolders CObjArray NumUnpackStreamsVector; // NumFolders CObjArray CoderUnpackSizes; // including unpack sizes of bind coders CObjArray FoToCoderUnpackSizes; // NumFolders + 1 CObjArray FoStartPackStreamIndex; // NumFolders + 1 CObjArray FoToMainUnpackSizeIndex; // NumFolders CObjArray FoCodersDataOffset; // NumFolders + 1 CByteBuffer CodersData; CParsedMethods ParsedMethods; void ParseFolderInfo(unsigned folderIndex, CFolder &folder) const; unsigned GetNumFolderUnpackSizes(unsigned folderIndex) const { return FoToCoderUnpackSizes[folderIndex + 1] - FoToCoderUnpackSizes[folderIndex]; } UInt64 GetFolderUnpackSize(unsigned folderIndex) const { return CoderUnpackSizes[FoToCoderUnpackSizes[folderIndex] + FoToMainUnpackSizeIndex[folderIndex]]; } UInt64 GetStreamPackSize(unsigned index) const { return PackPositions[index + 1] - PackPositions[index]; } void Clear() { NumPackStreams = 0; PackPositions.Free(); // PackCRCs.Clear(); NumFolders = 0; FolderCRCs.Clear(); NumUnpackStreamsVector.Free(); CoderUnpackSizes.Free(); FoToCoderUnpackSizes.Free(); FoStartPackStreamIndex.Free(); FoToMainUnpackSizeIndex.Free(); FoCodersDataOffset.Free(); CodersData.Free(); } }; struct CDatabase: public CFolders { CRecordVector Files; CUInt64DefVector CTime; CUInt64DefVector ATime; CUInt64DefVector MTime; CUInt64DefVector StartPos; CRecordVector IsAnti; /* CRecordVector IsAux; CByteBuffer SecureBuf; CRecordVector SecureIDs; */ CByteBuffer NamesBuf; CObjArray NameOffsets; // numFiles + 1, offsets of utf-16 symbols /* void ClearSecure() { SecureBuf.Free(); SecureIDs.Clear(); } */ void Clear() { CFolders::Clear(); // ClearSecure(); NamesBuf.Free(); NameOffsets.Free(); Files.Clear(); CTime.Clear(); ATime.Clear(); MTime.Clear(); StartPos.Clear(); IsAnti.Clear(); // IsAux.Clear(); } bool IsSolid() const { for (CNum i = 0; i < NumFolders; i++) if (NumUnpackStreamsVector[i] > 1) return true; return false; } bool IsItemAnti(unsigned index) const { return (index < IsAnti.Size() && IsAnti[index]); } // bool IsItemAux(unsigned index) const { return (index < IsAux.Size() && IsAux[index]); } const void * GetName(unsigned index) const { if (!NameOffsets || !NamesBuf) return NULL; return (const void *)((const Byte *)NamesBuf + NameOffsets[index] * 2); }; void GetPath(unsigned index, UString &path) const; HRESULT GetPath_Prop(unsigned index, PROPVARIANT *path) const throw(); }; struct CInArchiveInfo { CArchiveVersion Version; UInt64 StartPosition; UInt64 StartPositionAfterHeader; UInt64 DataStartPosition; UInt64 DataStartPosition2; CRecordVector FileInfoPopIDs; void Clear() { StartPosition = 0; StartPositionAfterHeader = 0; DataStartPosition = 0; DataStartPosition2 = 0; FileInfoPopIDs.Clear(); } }; struct CDbEx: public CDatabase { CInArchiveInfo ArcInfo; CRecordVector FolderStartFileIndex; CRecordVector FileIndexToFolderIndexMap; UInt64 HeadersSize; UInt64 PhySize; /* CRecordVector SecureOffsets; bool IsTree; bool ThereAreAltStreams; */ bool IsArc; bool PhySizeWasConfirmed; bool ThereIsHeaderError; bool UnexpectedEnd; // bool UnsupportedVersion; bool StartHeaderWasRecovered; bool UnsupportedFeatureWarning; bool UnsupportedFeatureError; /* void ClearSecureEx() { ClearSecure(); SecureOffsets.Clear(); } */ void Clear() { IsArc = false; PhySizeWasConfirmed = false; ThereIsHeaderError = false; UnexpectedEnd = false; // UnsupportedVersion = false; StartHeaderWasRecovered = false; UnsupportedFeatureError = false; UnsupportedFeatureWarning = false; /* IsTree = false; ThereAreAltStreams = false; */ CDatabase::Clear(); // SecureOffsets.Clear(); ArcInfo.Clear(); FolderStartFileIndex.Clear(); FileIndexToFolderIndexMap.Clear(); HeadersSize = 0; PhySize = 0; } void FillLinks(); UInt64 GetFolderStreamPos(unsigned folderIndex, unsigned indexInFolder) const { return ArcInfo.DataStartPosition + PackPositions[FoStartPackStreamIndex[folderIndex] + indexInFolder]; } UInt64 GetFolderFullPackSize(unsigned folderIndex) const { return PackPositions[FoStartPackStreamIndex[folderIndex + 1]] - PackPositions[FoStartPackStreamIndex[folderIndex]]; } UInt64 GetFolderPackStreamSize(unsigned folderIndex, unsigned streamIndex) const { unsigned i = FoStartPackStreamIndex[folderIndex] + streamIndex; return PackPositions[i + 1] - PackPositions[i]; } UInt64 GetFilePackSize(CNum fileIndex) const { CNum folderIndex = FileIndexToFolderIndexMap[fileIndex]; if (folderIndex != kNumNoIndex) if (FolderStartFileIndex[folderIndex] == fileIndex) return GetFolderFullPackSize(folderIndex); return 0; } }; const unsigned kNumBufLevelsMax = 4; struct CInByte2 { const Byte *_buffer; public: size_t _size; size_t _pos; size_t GetRem() const { return _size - _pos; } const Byte *GetPtr() const { return _buffer + _pos; } void Init(const Byte *buffer, size_t size) { _buffer = buffer; _size = size; _pos = 0; } Byte ReadByte(); void ReadBytes(Byte *data, size_t size); void SkipDataNoCheck(UInt64 size) { _pos += (size_t)size; } void SkipData(UInt64 size); void SkipData(); void SkipRem() { _pos = _size; } UInt64 ReadNumber(); CNum ReadNum(); UInt32 ReadUInt32(); UInt64 ReadUInt64(); void ParseFolder(CFolder &folder); }; class CStreamSwitch; const UInt32 kHeaderSize = 32; class CInArchive { friend class CStreamSwitch; CMyComPtr _stream; unsigned _numInByteBufs; CInByte2 _inByteVector[kNumBufLevelsMax]; CInByte2 *_inByteBack; bool ThereIsHeaderError; UInt64 _arhiveBeginStreamPosition; UInt64 _fileEndPosition; Byte _header[kHeaderSize]; UInt64 HeadersSize; void AddByteStream(const Byte *buffer, size_t size); void DeleteByteStream(bool needUpdatePos) { _numInByteBufs--; if (_numInByteBufs > 0) { _inByteBack = &_inByteVector[_numInByteBufs - 1]; if (needUpdatePos) _inByteBack->_pos += _inByteVector[_numInByteBufs]._pos; } } private: HRESULT FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit); void ReadBytes(Byte *data, size_t size) { _inByteBack->ReadBytes(data, size); } Byte ReadByte() { return _inByteBack->ReadByte(); } UInt64 ReadNumber() { return _inByteBack->ReadNumber(); } CNum ReadNum() { return _inByteBack->ReadNum(); } UInt64 ReadID() { return _inByteBack->ReadNumber(); } UInt32 ReadUInt32() { return _inByteBack->ReadUInt32(); } UInt64 ReadUInt64() { return _inByteBack->ReadUInt64(); } void SkipData(UInt64 size) { _inByteBack->SkipData(size); } void SkipData() { _inByteBack->SkipData(); } void WaitId(UInt64 id); void ReadArchiveProperties(CInArchiveInfo &archiveInfo); void ReadHashDigests(unsigned numItems, CUInt32DefVector &crcs); void ReadPackInfo(CFolders &f); void ReadUnpackInfo( const CObjectVector *dataVector, CFolders &folders); void ReadSubStreamsInfo( CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests); void ReadStreamsInfo( const CObjectVector *dataVector, UInt64 &dataOffset, CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests); void ReadBoolVector(unsigned numItems, CBoolVector &v); void ReadBoolVector2(unsigned numItems, CBoolVector &v); void ReadUInt64DefVector(const CObjectVector &dataVector, CUInt64DefVector &v, unsigned numItems); HRESULT ReadAndDecodePackedStreams( DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset, UInt64 &dataOffset, CObjectVector &dataVector _7Z_DECODER_CRYPRO_VARS_DECL ); HRESULT ReadHeader( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ); HRESULT ReadDatabase2( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ); public: CInArchive(): _numInByteBufs(0) { } HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); // S_FALSE means is not archive void Close(); HRESULT ReadDatabase( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ); }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zItem.h000066400000000000000000000067121325366651500212600ustar00rootroot00000000000000// 7zItem.h #ifndef __7Z_ITEM_H #define __7Z_ITEM_H #include "../../../Common/MyBuffer.h" #include "../../../Common/MyString.h" #include "../../Common/MethodId.h" #include "7zHeader.h" namespace NArchive { namespace N7z { const UInt64 k_AES = 0x06F10701; typedef UInt32 CNum; const CNum kNumMax = 0x7FFFFFFF; const CNum kNumNoIndex = 0xFFFFFFFF; struct CCoderInfo { CMethodId MethodID; CByteBuffer Props; CNum NumInStreams; CNum NumOutStreams; bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } }; struct CBindPair { CNum InIndex; CNum OutIndex; }; struct CFolder { CObjArray2 Coders; CObjArray2 BindPairs; CObjArray2 PackStreams; CNum GetNumOutStreams() const { CNum result = 0; FOR_VECTOR(i, Coders) result += Coders[i].NumOutStreams; return result; } int FindBindPairForInStream(CNum inStreamIndex) const { FOR_VECTOR(i, BindPairs) if (BindPairs[i].InIndex == inStreamIndex) return i; return -1; } int FindBindPairForOutStream(CNum outStreamIndex) const { FOR_VECTOR(i, BindPairs) if (BindPairs[i].OutIndex == outStreamIndex) return i; return -1; } int FindPackStreamArrayIndex(CNum inStreamIndex) const { FOR_VECTOR(i, PackStreams) if (PackStreams[i] == inStreamIndex) return i; return -1; } int GetIndexOfMainOutStream() const { for (int i = (int)GetNumOutStreams() - 1; i >= 0; i--) if (FindBindPairForOutStream(i) < 0) return i; throw 1; } bool IsEncrypted() const { for (int i = Coders.Size() - 1; i >= 0; i--) if (Coders[i].MethodID == k_AES) return true; return false; } bool CheckStructure(unsigned numUnpackSizes) const; }; struct CUInt32DefVector { CBoolVector Defs; CRecordVector Vals; void ClearAndSetSize(unsigned newSize) { Defs.ClearAndSetSize(newSize); Vals.ClearAndSetSize(newSize); } void Clear() { Defs.Clear(); Vals.Clear(); } void ReserveDown() { Defs.ReserveDown(); Vals.ReserveDown(); } bool ValidAndDefined(unsigned i) const { return i < Defs.Size() && Defs[i]; } }; struct CUInt64DefVector { CBoolVector Defs; CRecordVector Vals; void Clear() { Defs.Clear(); Vals.Clear(); } void ReserveDown() { Defs.ReserveDown(); Vals.ReserveDown(); } bool GetItem(unsigned index, UInt64 &value) const { if (index < Defs.Size() && Defs[index]) { value = Vals[index]; return true; } value = 0; return false; } void SetItem(unsigned index, bool defined, UInt64 value); bool CheckSize(unsigned size) const { return Defs.Size() == size || Defs.Size() == 0; } }; struct CFileItem { UInt64 Size; UInt32 Attrib; UInt32 Crc; /* int Parent; bool IsAltStream; */ bool HasStream; // Test it !!! it means that there is // stream in some folder. It can be empty stream bool IsDir; bool CrcDefined; bool AttribDefined; CFileItem(): /* Parent(-1), IsAltStream(false), */ HasStream(true), IsDir(false), CrcDefined(false), AttribDefined(false) {} void SetAttrib(UInt32 attrib) { AttribDefined = true; Attrib = attrib; } }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp000066400000000000000000000550621325366651500214660ustar00rootroot00000000000000// 7zOut.cpp #include "StdAfx.h" #include "../../../../C/7zCrc.h" #include "../../Common/StreamObjects.h" #include "7zOut.h" namespace NArchive { namespace N7z { HRESULT COutArchive::WriteSignature() { Byte buf[8]; memcpy(buf, kSignature, kSignatureSize); buf[kSignatureSize] = kMajorVersion; buf[kSignatureSize + 1] = 4; return WriteDirect(buf, 8); } #ifdef _7Z_VOL HRESULT COutArchive::WriteFinishSignature() { RINOK(WriteDirect(kFinishSignature, kSignatureSize)); CArchiveVersion av; av.Major = kMajorVersion; av.Minor = 2; RINOK(WriteDirectByte(av.Major)); return WriteDirectByte(av.Minor); } #endif static void SetUInt32(Byte *p, UInt32 d) { for (int i = 0; i < 4; i++, d >>= 8) p[i] = (Byte)d; } static void SetUInt64(Byte *p, UInt64 d) { for (int i = 0; i < 8; i++, d >>= 8) p[i] = (Byte)d; } HRESULT COutArchive::WriteStartHeader(const CStartHeader &h) { Byte buf[24]; SetUInt64(buf + 4, h.NextHeaderOffset); SetUInt64(buf + 12, h.NextHeaderSize); SetUInt32(buf + 20, h.NextHeaderCRC); SetUInt32(buf, CrcCalc(buf + 4, 20)); return WriteDirect(buf, 24); } #ifdef _7Z_VOL HRESULT COutArchive::WriteFinishHeader(const CFinishHeader &h) { CCRC crc; crc.UpdateUInt64(h.NextHeaderOffset); crc.UpdateUInt64(h.NextHeaderSize); crc.UpdateUInt32(h.NextHeaderCRC); crc.UpdateUInt64(h.ArchiveStartOffset); crc.UpdateUInt64(h.AdditionalStartBlockSize); RINOK(WriteDirectUInt32(crc.GetDigest())); RINOK(WriteDirectUInt64(h.NextHeaderOffset)); RINOK(WriteDirectUInt64(h.NextHeaderSize)); RINOK(WriteDirectUInt32(h.NextHeaderCRC)); RINOK(WriteDirectUInt64(h.ArchiveStartOffset)); return WriteDirectUInt64(h.AdditionalStartBlockSize); } #endif HRESULT COutArchive::Create(ISequentialOutStream *stream, bool endMarker) { Close(); #ifdef _7Z_VOL // endMarker = false; _endMarker = endMarker; #endif SeqStream = stream; if (!endMarker) { SeqStream.QueryInterface(IID_IOutStream, &Stream); if (!Stream) { return E_NOTIMPL; // endMarker = true; } } #ifdef _7Z_VOL if (endMarker) { /* CStartHeader sh; sh.NextHeaderOffset = (UInt32)(Int32)-1; sh.NextHeaderSize = (UInt32)(Int32)-1; sh.NextHeaderCRC = 0; WriteStartHeader(sh); */ } else #endif { if (!Stream) return E_FAIL; RINOK(WriteSignature()); RINOK(Stream->Seek(0, STREAM_SEEK_CUR, &_prefixHeaderPos)); } return S_OK; } void COutArchive::Close() { SeqStream.Release(); Stream.Release(); } HRESULT COutArchive::SkipPrefixArchiveHeader() { #ifdef _7Z_VOL if (_endMarker) return S_OK; #endif Byte buf[24]; memset(buf, 0, 24); return WriteDirect(buf, 24); } UInt64 COutArchive::GetPos() const { if (_countMode) return _countSize; if (_writeToStream) return _outByte.GetProcessedSize(); return _outByte2.GetPos(); } void COutArchive::WriteBytes(const void *data, size_t size) { if (_countMode) _countSize += size; else if (_writeToStream) { _outByte.WriteBytes(data, size); _crc = CrcUpdate(_crc, data, size); } else _outByte2.WriteBytes(data, size); } void COutArchive::WriteByte(Byte b) { if (_countMode) _countSize++; else if (_writeToStream) { _outByte.WriteByte(b); _crc = CRC_UPDATE_BYTE(_crc, b); } else _outByte2.WriteByte(b); } void COutArchive::WriteUInt32(UInt32 value) { for (int i = 0; i < 4; i++) { WriteByte((Byte)value); value >>= 8; } } void COutArchive::WriteUInt64(UInt64 value) { for (int i = 0; i < 8; i++) { WriteByte((Byte)value); value >>= 8; } } void COutArchive::WriteNumber(UInt64 value) { Byte firstByte = 0; Byte mask = 0x80; int i; for (i = 0; i < 8; i++) { if (value < ((UInt64(1) << ( 7 * (i + 1))))) { firstByte |= Byte(value >> (8 * i)); break; } firstByte |= mask; mask >>= 1; } WriteByte(firstByte); for (;i > 0; i--) { WriteByte((Byte)value); value >>= 8; } } static UInt32 GetBigNumberSize(UInt64 value) { int i; for (i = 1; i < 9; i++) if (value < (((UInt64)1 << (i * 7)))) break; return i; } #ifdef _7Z_VOL UInt32 COutArchive::GetVolHeadersSize(UInt64 dataSize, int nameLength, bool props) { UInt32 result = GetBigNumberSize(dataSize) * 2 + 41; if (nameLength != 0) { nameLength = (nameLength + 1) * 2; result += nameLength + GetBigNumberSize(nameLength) + 2; } if (props) { result += 20; } if (result >= 128) result++; result += kSignatureSize + 2 + kFinishHeaderSize; return result; } UInt64 COutArchive::GetVolPureSize(UInt64 volSize, int nameLength, bool props) { UInt32 headersSizeBase = COutArchive::GetVolHeadersSize(1, nameLength, props); int testSize; if (volSize > headersSizeBase) testSize = volSize - headersSizeBase; else testSize = 1; UInt32 headersSize = COutArchive::GetVolHeadersSize(testSize, nameLength, props); UInt64 pureSize = 1; if (volSize > headersSize) pureSize = volSize - headersSize; return pureSize; } #endif void COutArchive::WriteFolder(const CFolder &folder) { WriteNumber(folder.Coders.Size()); unsigned i; for (i = 0; i < folder.Coders.Size(); i++) { const CCoderInfo &coder = folder.Coders[i]; { size_t propsSize = coder.Props.Size(); UInt64 id = coder.MethodID; int idSize; for (idSize = 1; idSize < sizeof(id); idSize++) if ((id >> (8 * idSize)) == 0) break; Byte longID[15]; for (int t = idSize - 1; t >= 0 ; t--, id >>= 8) longID[t] = (Byte)(id & 0xFF); Byte b; b = (Byte)(idSize & 0xF); bool isComplex = !coder.IsSimpleCoder(); b |= (isComplex ? 0x10 : 0); b |= ((propsSize != 0) ? 0x20 : 0 ); WriteByte(b); WriteBytes(longID, idSize); if (isComplex) { WriteNumber(coder.NumInStreams); WriteNumber(coder.NumOutStreams); } if (propsSize == 0) continue; WriteNumber(propsSize); WriteBytes(coder.Props, propsSize); } } for (i = 0; i < folder.BindPairs.Size(); i++) { const CBindPair &bindPair = folder.BindPairs[i]; WriteNumber(bindPair.InIndex); WriteNumber(bindPair.OutIndex); } if (folder.PackStreams.Size() > 1) for (i = 0; i < folder.PackStreams.Size(); i++) { WriteNumber(folder.PackStreams[i]); } } void COutArchive::WriteBoolVector(const CBoolVector &boolVector) { Byte b = 0; Byte mask = 0x80; FOR_VECTOR (i, boolVector) { if (boolVector[i]) b |= mask; mask >>= 1; if (mask == 0) { WriteByte(b); mask = 0x80; b = 0; } } if (mask != 0x80) WriteByte(b); } static inline unsigned Bv_GetSizeInBytes(const CBoolVector &v) { return ((unsigned)v.Size() + 7) / 8; } void COutArchive::WritePropBoolVector(Byte id, const CBoolVector &boolVector) { WriteByte(id); WriteNumber(Bv_GetSizeInBytes(boolVector)); WriteBoolVector(boolVector); } void COutArchive::WriteHashDigests(const CUInt32DefVector &digests) { unsigned numDefined = 0; unsigned i; for (i = 0; i < digests.Defs.Size(); i++) if (digests.Defs[i]) numDefined++; if (numDefined == 0) return; WriteByte(NID::kCRC); if (numDefined == digests.Defs.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(digests.Defs); } for (i = 0; i < digests.Defs.Size(); i++) if (digests.Defs[i]) WriteUInt32(digests.Vals[i]); } void COutArchive::WritePackInfo( UInt64 dataOffset, const CRecordVector &packSizes, const CUInt32DefVector &packCRCs) { if (packSizes.IsEmpty()) return; WriteByte(NID::kPackInfo); WriteNumber(dataOffset); WriteNumber(packSizes.Size()); WriteByte(NID::kSize); FOR_VECTOR (i, packSizes) WriteNumber(packSizes[i]); WriteHashDigests(packCRCs); WriteByte(NID::kEnd); } void COutArchive::WriteUnpackInfo(const CObjectVector &folders, const COutFolders &outFolders) { if (folders.IsEmpty()) return; WriteByte(NID::kUnpackInfo); WriteByte(NID::kFolder); WriteNumber(folders.Size()); { WriteByte(0); FOR_VECTOR (i, folders) WriteFolder(folders[i]); } WriteByte(NID::kCodersUnpackSize); FOR_VECTOR (i, outFolders.CoderUnpackSizes) WriteNumber(outFolders.CoderUnpackSizes[i]); WriteHashDigests(outFolders.FolderUnpackCRCs); WriteByte(NID::kEnd); } void COutArchive::WriteSubStreamsInfo(const CObjectVector &folders, const COutFolders &outFolders, const CRecordVector &unpackSizes, const CUInt32DefVector &digests) { const CRecordVector &numUnpackStreamsInFolders = outFolders.NumUnpackStreamsVector; WriteByte(NID::kSubStreamsInfo); unsigned i; for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) if (numUnpackStreamsInFolders[i] != 1) { WriteByte(NID::kNumUnpackStream); for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) WriteNumber(numUnpackStreamsInFolders[i]); break; } for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) if (numUnpackStreamsInFolders[i] > 1) { WriteByte(NID::kSize); CNum index = 0; for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) { CNum num = numUnpackStreamsInFolders[i]; for (CNum j = 0; j < num; j++) { if (j + 1 != num) WriteNumber(unpackSizes[index]); index++; } } break; } CUInt32DefVector digests2; unsigned digestIndex = 0; for (i = 0; i < folders.Size(); i++) { unsigned numSubStreams = (unsigned)numUnpackStreamsInFolders[i]; if (numSubStreams == 1 && outFolders.FolderUnpackCRCs.ValidAndDefined(i)) digestIndex++; else for (unsigned j = 0; j < numSubStreams; j++, digestIndex++) { digests2.Defs.Add(digests.Defs[digestIndex]); digests2.Vals.Add(digests.Vals[digestIndex]); } } WriteHashDigests(digests2); WriteByte(NID::kEnd); } // 7-Zip 4.50 - 4.58 contain BUG, so they do not support .7z archives with Unknown field. void COutArchive::SkipAlign(unsigned pos, unsigned alignSize) { if (!_useAlign) return; pos += (unsigned)GetPos(); pos &= (alignSize - 1); if (pos == 0) return; unsigned skip = alignSize - pos; if (skip < 2) skip += alignSize; skip -= 2; WriteByte(NID::kDummy); WriteByte((Byte)skip); for (unsigned i = 0; i < skip; i++) WriteByte(0); } void COutArchive::WriteAlignedBoolHeader(const CBoolVector &v, unsigned numDefined, Byte type, unsigned itemSize) { const unsigned bvSize = (numDefined == v.Size()) ? 0 : Bv_GetSizeInBytes(v); const UInt64 dataSize = (UInt64)numDefined * itemSize + bvSize + 2; SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize); WriteByte(type); WriteNumber(dataSize); if (numDefined == v.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(v); } WriteByte(0); } void COutArchive::WriteUInt64DefVector(const CUInt64DefVector &v, Byte type) { unsigned numDefined = 0; unsigned i; for (i = 0; i < v.Defs.Size(); i++) if (v.Defs[i]) numDefined++; if (numDefined == 0) return; WriteAlignedBoolHeader(v.Defs, numDefined, type, 8); for (i = 0; i < v.Defs.Size(); i++) if (v.Defs[i]) WriteUInt64(v.Vals[i]); } HRESULT COutArchive::EncodeStream( DECL_EXTERNAL_CODECS_LOC_VARS CEncoder &encoder, const CByteBuffer &data, CRecordVector &packSizes, CObjectVector &folders, COutFolders &outFolders) { CBufInStream *streamSpec = new CBufInStream; CMyComPtr stream = streamSpec; streamSpec->Init(data, data.Size()); outFolders.FolderUnpackCRCs.Defs.Add(true); outFolders.FolderUnpackCRCs.Vals.Add(CrcCalc(data, data.Size())); // outFolders.NumUnpackStreamsVector.Add(1); UInt64 dataSize64 = data.Size(); UInt64 unpackSize; RINOK(encoder.Encode( EXTERNAL_CODECS_LOC_VARS stream, NULL, &dataSize64, folders.AddNew(), outFolders.CoderUnpackSizes, unpackSize, SeqStream, packSizes, NULL)) return S_OK; } void COutArchive::WriteHeader( const CArchiveDatabaseOut &db, // const CHeaderOptions &headerOptions, UInt64 &headerOffset) { /* bool thereIsSecure = (db.SecureBuf.Size() != 0); */ _useAlign = true; unsigned i; UInt64 packedSize = 0; for (i = 0; i < db.PackSizes.Size(); i++) packedSize += db.PackSizes[i]; headerOffset = packedSize; WriteByte(NID::kHeader); // Archive Properties if (db.Folders.Size() > 0) { WriteByte(NID::kMainStreamsInfo); WritePackInfo(0, db.PackSizes, db.PackCRCs); WriteUnpackInfo(db.Folders, (const COutFolders &)db); CRecordVector unpackSizes; CUInt32DefVector digests; for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; if (!file.HasStream) continue; unpackSizes.Add(file.Size); digests.Defs.Add(file.CrcDefined); digests.Vals.Add(file.Crc); } WriteSubStreamsInfo(db.Folders, (const COutFolders &)db, unpackSizes, digests); WriteByte(NID::kEnd); } if (db.Files.IsEmpty()) { WriteByte(NID::kEnd); return; } WriteByte(NID::kFilesInfo); WriteNumber(db.Files.Size()); { /* ---------- Empty Streams ---------- */ CBoolVector emptyStreamVector; emptyStreamVector.ClearAndSetSize(db.Files.Size()); unsigned numEmptyStreams = 0; for (i = 0; i < db.Files.Size(); i++) if (db.Files[i].HasStream) emptyStreamVector[i] = false; else { emptyStreamVector[i] = true; numEmptyStreams++; } if (numEmptyStreams != 0) { WritePropBoolVector(NID::kEmptyStream, emptyStreamVector); CBoolVector emptyFileVector, antiVector; emptyFileVector.ClearAndSetSize(numEmptyStreams); antiVector.ClearAndSetSize(numEmptyStreams); bool thereAreEmptyFiles = false, thereAreAntiItems = false; unsigned cur = 0; for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; if (file.HasStream) continue; emptyFileVector[cur] = !file.IsDir; if (!file.IsDir) thereAreEmptyFiles = true; bool isAnti = db.IsItemAnti(i); antiVector[cur] = isAnti; if (isAnti) thereAreAntiItems = true; cur++; } if (thereAreEmptyFiles) WritePropBoolVector(NID::kEmptyFile, emptyFileVector); if (thereAreAntiItems) WritePropBoolVector(NID::kAnti, antiVector); } } { /* ---------- Names ---------- */ unsigned numDefined = 0; size_t namesDataSize = 0; FOR_VECTOR (i, db.Files) { const UString &name = db.Names[i]; if (!name.IsEmpty()) numDefined++; namesDataSize += (name.Len() + 1) * 2; } if (numDefined > 0) { namesDataSize++; SkipAlign(2 + GetBigNumberSize(namesDataSize), 16); WriteByte(NID::kName); WriteNumber(namesDataSize); WriteByte(0); FOR_VECTOR (i, db.Files) { const UString &name = db.Names[i]; for (unsigned t = 0; t <= name.Len(); t++) { wchar_t c = name[t]; WriteByte((Byte)c); WriteByte((Byte)(c >> 8)); } } } } /* if (headerOptions.WriteCTime) */ WriteUInt64DefVector(db.CTime, NID::kCTime); /* if (headerOptions.WriteATime) */ WriteUInt64DefVector(db.ATime, NID::kATime); /* if (headerOptions.WriteMTime) */ WriteUInt64DefVector(db.MTime, NID::kMTime); WriteUInt64DefVector(db.StartPos, NID::kStartPos); { /* ---------- Write Attrib ---------- */ CBoolVector boolVector; boolVector.ClearAndSetSize(db.Files.Size()); unsigned numDefined = 0; for (i = 0; i < db.Files.Size(); i++) { bool defined = db.Files[i].AttribDefined; boolVector[i] = defined; if (defined) numDefined++; } if (numDefined != 0) { WriteAlignedBoolHeader(boolVector, numDefined, NID::kWinAttrib, 4); for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; if (file.AttribDefined) WriteUInt32(file.Attrib); } } } /* { // ---------- Write IsAux ---------- unsigned numAux = 0; const CBoolVector &isAux = db.IsAux; for (i = 0; i < isAux.Size(); i++) if (isAux[i]) numAux++; if (numAux > 0) { const unsigned bvSize = Bv_GetSizeInBytes(isAux); WriteByte(NID::kIsAux); WriteNumber(bvSize); WriteBoolVector(isAux); } } { // ---------- Write Parent ---------- CBoolVector boolVector; boolVector.Reserve(db.Files.Size()); unsigned numIsDir = 0; unsigned numParentLinks = 0; for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; bool defined = !file.IsAltStream; boolVector.Add(defined); if (defined) numIsDir++; if (file.Parent >= 0) numParentLinks++; } if (numParentLinks > 0) { // WriteAlignedBoolHeader(boolVector, numDefined, NID::kParent, 4); const unsigned bvSize = (numIsDir == boolVector.Size()) ? 0 : Bv_GetSizeInBytes(boolVector); const UInt64 dataSize = (UInt64)db.Files.Size() * 4 + bvSize + 1; SkipAlign(2 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), 4); WriteByte(NID::kParent); WriteNumber(dataSize); if (numIsDir == boolVector.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(boolVector); } for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; // if (file.Parent >= 0) WriteUInt32(file.Parent); } } } if (thereIsSecure) { UInt64 secureDataSize = 1 + 4 + db.SecureBuf.Size() + db.SecureSizes.Size() * 4; // secureDataSize += db.SecureIDs.Size() * 4; for (i = 0; i < db.SecureIDs.Size(); i++) secureDataSize += GetBigNumberSize(db.SecureIDs[i]); SkipAlign(2 + GetBigNumberSize(secureDataSize), 4); WriteByte(NID::kNtSecure); WriteNumber(secureDataSize); WriteByte(0); WriteUInt32(db.SecureSizes.Size()); for (i = 0; i < db.SecureSizes.Size(); i++) WriteUInt32(db.SecureSizes[i]); WriteBytes(db.SecureBuf, db.SecureBuf.Size()); for (i = 0; i < db.SecureIDs.Size(); i++) { WriteNumber(db.SecureIDs[i]); // WriteUInt32(db.SecureIDs[i]); } } */ WriteByte(NID::kEnd); // for files WriteByte(NID::kEnd); // for headers } HRESULT COutArchive::WriteDatabase( DECL_EXTERNAL_CODECS_LOC_VARS const CArchiveDatabaseOut &db, const CCompressionMethodMode *options, const CHeaderOptions &headerOptions) { if (!db.CheckNumFiles()) return E_FAIL; UInt64 headerOffset; UInt32 headerCRC; UInt64 headerSize; if (db.IsEmpty()) { headerSize = 0; headerOffset = 0; headerCRC = CrcCalc(0, 0); } else { bool encodeHeaders = false; if (options != 0) if (options->IsEmpty()) options = 0; if (options != 0) if (options->PasswordIsDefined || headerOptions.CompressMainHeader) encodeHeaders = true; _outByte.SetStream(SeqStream); _outByte.Init(); _crc = CRC_INIT_VAL; _countMode = encodeHeaders; _writeToStream = true; _countSize = 0; WriteHeader(db, /* headerOptions, */ headerOffset); if (encodeHeaders) { CByteBuffer buf(_countSize); _outByte2.Init((Byte *)buf, _countSize); _countMode = false; _writeToStream = false; WriteHeader(db, /* headerOptions, */ headerOffset); if (_countSize != _outByte2.GetPos()) return E_FAIL; CCompressionMethodMode encryptOptions; encryptOptions.PasswordIsDefined = options->PasswordIsDefined; encryptOptions.Password = options->Password; CEncoder encoder(headerOptions.CompressMainHeader ? *options : encryptOptions); CRecordVector packSizes; CObjectVector folders; COutFolders outFolders; RINOK(EncodeStream( EXTERNAL_CODECS_LOC_VARS encoder, buf, packSizes, folders, outFolders)); _writeToStream = true; if (folders.Size() == 0) throw 1; WriteID(NID::kEncodedHeader); WritePackInfo(headerOffset, packSizes, CUInt32DefVector()); WriteUnpackInfo(folders, outFolders); WriteByte(NID::kEnd); FOR_VECTOR (i, packSizes) headerOffset += packSizes[i]; } RINOK(_outByte.Flush()); headerCRC = CRC_GET_DIGEST(_crc); headerSize = _outByte.GetProcessedSize(); } #ifdef _7Z_VOL if (_endMarker) { CFinishHeader h; h.NextHeaderSize = headerSize; h.NextHeaderCRC = headerCRC; h.NextHeaderOffset = UInt64(0) - (headerSize + 4 + kFinishHeaderSize); h.ArchiveStartOffset = h.NextHeaderOffset - headerOffset; h.AdditionalStartBlockSize = 0; RINOK(WriteFinishHeader(h)); return WriteFinishSignature(); } else #endif { CStartHeader h; h.NextHeaderSize = headerSize; h.NextHeaderCRC = headerCRC; h.NextHeaderOffset = headerOffset; RINOK(Stream->Seek(_prefixHeaderPos, STREAM_SEEK_SET, NULL)); return WriteStartHeader(h); } } void CUInt64DefVector::SetItem(unsigned index, bool defined, UInt64 value) { while (index >= Defs.Size()) Defs.Add(false); Defs[index] = defined; if (!defined) return; while (index >= Vals.Size()) Vals.Add(0); Vals[index] = value; } void CArchiveDatabaseOut::AddFile(const CFileItem &file, const CFileItem2 &file2, const UString &name) { unsigned index = Files.Size(); CTime.SetItem(index, file2.CTimeDefined, file2.CTime); ATime.SetItem(index, file2.ATimeDefined, file2.ATime); MTime.SetItem(index, file2.MTimeDefined, file2.MTime); StartPos.SetItem(index, file2.StartPosDefined, file2.StartPos); SetItem_Anti(index, file2.IsAnti); // SetItem_Aux(index, file2.IsAux); Names.Add(name); Files.Add(file); } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.h000066400000000000000000000163011325366651500211240ustar00rootroot00000000000000// 7zOut.h #ifndef __7Z_OUT_H #define __7Z_OUT_H #include "7zCompressionMode.h" #include "7zEncode.h" #include "7zHeader.h" #include "7zItem.h" #include "../../Common/OutBuffer.h" #include "../../Common/StreamUtils.h" namespace NArchive { namespace N7z { class CWriteBufferLoc { Byte *_data; size_t _size; size_t _pos; public: CWriteBufferLoc(): _size(0), _pos(0) {} void Init(Byte *data, size_t size) { _data = data; _size = size; _pos = 0; } void WriteBytes(const void *data, size_t size) { if (size > _size - _pos) throw 1; memcpy(_data + _pos, data, size); _pos += size; } void WriteByte(Byte b) { if (_size == _pos) throw 1; _data[_pos++] = b; } size_t GetPos() const { return _pos; } }; struct CHeaderOptions { bool CompressMainHeader; /* bool WriteCTime; bool WriteATime; bool WriteMTime; */ CHeaderOptions(): CompressMainHeader(true) /* , WriteCTime(false) , WriteATime(false) , WriteMTime(true) */ {} }; struct CFileItem2 { UInt64 CTime; UInt64 ATime; UInt64 MTime; UInt64 StartPos; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; bool StartPosDefined; bool IsAnti; // bool IsAux; void Init() { CTimeDefined = false; ATimeDefined = false; MTimeDefined = false; StartPosDefined = false; IsAnti = false; // IsAux = false; } }; struct COutFolders { CUInt32DefVector FolderUnpackCRCs; // Now we use it for headers only. CRecordVector NumUnpackStreamsVector; CRecordVector CoderUnpackSizes; // including unpack sizes of bind coders void OutFoldersClear() { FolderUnpackCRCs.Clear(); NumUnpackStreamsVector.Clear(); CoderUnpackSizes.Clear(); } void OutFoldersReserveDown() { FolderUnpackCRCs.ReserveDown(); NumUnpackStreamsVector.ReserveDown(); CoderUnpackSizes.ReserveDown(); } }; struct CArchiveDatabaseOut: public COutFolders { CRecordVector PackSizes; CUInt32DefVector PackCRCs; CObjectVector Folders; CRecordVector Files; UStringVector Names; CUInt64DefVector CTime; CUInt64DefVector ATime; CUInt64DefVector MTime; CUInt64DefVector StartPos; CRecordVector IsAnti; /* CRecordVector IsAux; CByteBuffer SecureBuf; CRecordVector SecureSizes; CRecordVector SecureIDs; void ClearSecure() { SecureBuf.Free(); SecureSizes.Clear(); SecureIDs.Clear(); } */ void Clear() { OutFoldersClear(); PackSizes.Clear(); PackCRCs.Clear(); Folders.Clear(); Files.Clear(); Names.Clear(); CTime.Clear(); ATime.Clear(); MTime.Clear(); StartPos.Clear(); IsAnti.Clear(); /* IsAux.Clear(); ClearSecure(); */ } void ReserveDown() { OutFoldersReserveDown(); PackSizes.ReserveDown(); PackCRCs.ReserveDown(); Folders.ReserveDown(); Files.ReserveDown(); Names.ReserveDown(); CTime.ReserveDown(); ATime.ReserveDown(); MTime.ReserveDown(); StartPos.ReserveDown(); IsAnti.ReserveDown(); /* IsAux.ReserveDown(); */ } bool IsEmpty() const { return ( PackSizes.IsEmpty() && NumUnpackStreamsVector.IsEmpty() && Folders.IsEmpty() && Files.IsEmpty()); } bool CheckNumFiles() const { unsigned size = Files.Size(); return ( CTime.CheckSize(size) && ATime.CheckSize(size) && MTime.CheckSize(size) && StartPos.CheckSize(size) && (size == IsAnti.Size() || IsAnti.Size() == 0)); } bool IsItemAnti(unsigned index) const { return (index < IsAnti.Size() && IsAnti[index]); } // bool IsItemAux(unsigned index) const { return (index < IsAux.Size() && IsAux[index]); } void SetItem_Anti(unsigned index, bool isAnti) { while (index >= IsAnti.Size()) IsAnti.Add(false); IsAnti[index] = isAnti; } /* void SetItem_Aux(unsigned index, bool isAux) { while (index >= IsAux.Size()) IsAux.Add(false); IsAux[index] = isAux; } */ void AddFile(const CFileItem &file, const CFileItem2 &file2, const UString &name); }; class COutArchive { UInt64 _prefixHeaderPos; HRESULT WriteDirect(const void *data, UInt32 size) { return WriteStream(SeqStream, data, size); } UInt64 GetPos() const; void WriteBytes(const void *data, size_t size); void WriteBytes(const CByteBuffer &data) { WriteBytes(data, data.Size()); } void WriteByte(Byte b); void WriteUInt32(UInt32 value); void WriteUInt64(UInt64 value); void WriteNumber(UInt64 value); void WriteID(UInt64 value) { WriteNumber(value); } void WriteFolder(const CFolder &folder); HRESULT WriteFileHeader(const CFileItem &itemInfo); void WriteBoolVector(const CBoolVector &boolVector); void WritePropBoolVector(Byte id, const CBoolVector &boolVector); void WriteHashDigests(const CUInt32DefVector &digests); void WritePackInfo( UInt64 dataOffset, const CRecordVector &packSizes, const CUInt32DefVector &packCRCs); void WriteUnpackInfo( const CObjectVector &folders, const COutFolders &outFolders); void WriteSubStreamsInfo( const CObjectVector &folders, const COutFolders &outFolders, const CRecordVector &unpackSizes, const CUInt32DefVector &digests); void SkipAlign(unsigned pos, unsigned alignSize); void WriteAlignedBoolHeader(const CBoolVector &v, unsigned numDefined, Byte type, unsigned itemSize); void WriteUInt64DefVector(const CUInt64DefVector &v, Byte type); HRESULT EncodeStream( DECL_EXTERNAL_CODECS_LOC_VARS CEncoder &encoder, const CByteBuffer &data, CRecordVector &packSizes, CObjectVector &folders, COutFolders &outFolders); void WriteHeader( const CArchiveDatabaseOut &db, // const CHeaderOptions &headerOptions, UInt64 &headerOffset); bool _countMode; bool _writeToStream; size_t _countSize; UInt32 _crc; COutBuffer _outByte; CWriteBufferLoc _outByte2; #ifdef _7Z_VOL bool _endMarker; #endif bool _useAlign; HRESULT WriteSignature(); #ifdef _7Z_VOL HRESULT WriteFinishSignature(); #endif HRESULT WriteStartHeader(const CStartHeader &h); #ifdef _7Z_VOL HRESULT WriteFinishHeader(const CFinishHeader &h); #endif CMyComPtr Stream; public: COutArchive() { _outByte.Create(1 << 16); } CMyComPtr SeqStream; HRESULT Create(ISequentialOutStream *stream, bool endMarker); void Close(); HRESULT SkipPrefixArchiveHeader(); HRESULT WriteDatabase( DECL_EXTERNAL_CODECS_LOC_VARS const CArchiveDatabaseOut &db, const CCompressionMethodMode *options, const CHeaderOptions &headerOptions); #ifdef _7Z_VOL static UInt32 GetVolHeadersSize(UInt64 dataSize, int nameLength = 0, bool props = false); static UInt64 GetVolPureSize(UInt64 volSize, int nameLength = 0, bool props = false); #endif }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp000066400000000000000000000100101325366651500230330ustar00rootroot00000000000000// 7zProperties.cpp #include "StdAfx.h" #include "7zProperties.h" #include "7zHeader.h" #include "7zHandler.h" // #define _MULTI_PACK namespace NArchive { namespace N7z { struct CPropMap { UInt64 FilePropID; STATPROPSTG StatPROPSTG; }; static const CPropMap kPropMap[] = { { NID::kName, { NULL, kpidPath, VT_BSTR } }, { NID::kSize, { NULL, kpidSize, VT_UI8 } }, { NID::kPackInfo, { NULL, kpidPackSize, VT_UI8 } }, #ifdef _MULTI_PACK { 100, { L"Pack0", kpidPackedSize0, VT_UI8 } }, { 101, { L"Pack1", kpidPackedSize1, VT_UI8 } }, { 102, { L"Pack2", kpidPackedSize2, VT_UI8 } }, { 103, { L"Pack3", kpidPackedSize3, VT_UI8 } }, { 104, { L"Pack4", kpidPackedSize4, VT_UI8 } }, #endif { NID::kCTime, { NULL, kpidCTime, VT_FILETIME } }, { NID::kMTime, { NULL, kpidMTime, VT_FILETIME } }, { NID::kATime, { NULL, kpidATime, VT_FILETIME } }, { NID::kWinAttrib, { NULL, kpidAttrib, VT_UI4 } }, { NID::kStartPos, { NULL, kpidPosition, VT_UI4 } }, { NID::kCRC, { NULL, kpidCRC, VT_UI4 } }, // { NID::kIsAux, { NULL, kpidIsAux, VT_BOOL } }, { NID::kAnti, { NULL, kpidIsAnti, VT_BOOL } } #ifndef _SFX , { 97, { NULL,kpidEncrypted, VT_BOOL } }, { 98, { NULL,kpidMethod, VT_BSTR } }, { 99, { NULL,kpidBlock, VT_UI4 } } #endif }; static int FindPropInMap(UInt64 filePropID) { for (int i = 0; i < ARRAY_SIZE(kPropMap); i++) if (kPropMap[i].FilePropID == filePropID) return i; return -1; } static void CopyOneItem(CRecordVector &src, CRecordVector &dest, UInt32 item) { FOR_VECTOR (i, src) if (src[i] == item) { dest.Add(item); src.Delete(i); return; } } static void RemoveOneItem(CRecordVector &src, UInt32 item) { FOR_VECTOR (i, src) if (src[i] == item) { src.Delete(i); return; } } static void InsertToHead(CRecordVector &dest, UInt32 item) { FOR_VECTOR (i, dest) if (dest[i] == item) { dest.Delete(i); break; } dest.Insert(0, item); } #define COPY_ONE_ITEM(id) CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::id); void CHandler::FillPopIDs() { _fileInfoPopIDs.Clear(); #ifdef _7Z_VOL if (_volumes.Size() < 1) return; const CVolume &volume = _volumes.Front(); const CArchiveDatabaseEx &_db = volume.Database; #endif CRecordVector fileInfoPopIDs = _db.ArcInfo.FileInfoPopIDs; RemoveOneItem(fileInfoPopIDs, NID::kEmptyStream); RemoveOneItem(fileInfoPopIDs, NID::kEmptyFile); /* RemoveOneItem(fileInfoPopIDs, NID::kParent); RemoveOneItem(fileInfoPopIDs, NID::kNtSecure); */ COPY_ONE_ITEM(kName); COPY_ONE_ITEM(kAnti); COPY_ONE_ITEM(kSize); COPY_ONE_ITEM(kPackInfo); COPY_ONE_ITEM(kCTime); COPY_ONE_ITEM(kMTime); COPY_ONE_ITEM(kATime); COPY_ONE_ITEM(kWinAttrib); COPY_ONE_ITEM(kCRC); COPY_ONE_ITEM(kComment); _fileInfoPopIDs += fileInfoPopIDs; #ifndef _SFX _fileInfoPopIDs.Add(97); _fileInfoPopIDs.Add(98); _fileInfoPopIDs.Add(99); #endif #ifdef _MULTI_PACK _fileInfoPopIDs.Add(100); _fileInfoPopIDs.Add(101); _fileInfoPopIDs.Add(102); _fileInfoPopIDs.Add(103); _fileInfoPopIDs.Add(104); #endif #ifndef _SFX InsertToHead(_fileInfoPopIDs, NID::kMTime); InsertToHead(_fileInfoPopIDs, NID::kPackInfo); InsertToHead(_fileInfoPopIDs, NID::kSize); InsertToHead(_fileInfoPopIDs, NID::kName); #endif } STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) { *numProps = _fileInfoPopIDs.Size(); return S_OK; } STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) { if ((int)index >= _fileInfoPopIDs.Size()) return E_INVALIDARG; int indexInMap = FindPropInMap(_fileInfoPopIDs[index]); if (indexInMap == -1) return E_INVALIDARG; const STATPROPSTG &srcItem = kPropMap[indexInMap].StatPROPSTG; *propID = srcItem.propid; *varType = srcItem.vt; *name = 0; return S_OK; } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h000066400000000000000000000004451325366651500225130ustar00rootroot00000000000000// 7zProperties.h #ifndef __7Z_PROPERTIES_H #define __7Z_PROPERTIES_H #include "../../PropID.h" namespace NArchive { namespace N7z { enum { kpidPackedSize0 = kpidUserDefined, kpidPackedSize1, kpidPackedSize2, kpidPackedSize3, kpidPackedSize4 }; }} #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp000066400000000000000000000006271325366651500225000ustar00rootroot00000000000000// 7zRegister.cpp #include "StdAfx.h" #include "../../Common/RegisterArc.h" #include "7zHandler.h" namespace NArchive { namespace N7z { IMP_CreateArcIn IMP_CreateArcOut static CArcInfo g_ArcInfo = { "7z", "7z", 0, 7, 6, {'7' + 1, 'z', 0xBC, 0xAF, 0x27, 0x1C}, 0, NArcInfoFlags::kFindSignature, REF_CreateArc_Pair }; REGISTER_ARC_DEC_SIG(7z) // REGISTER_ARC(7z) }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp000066400000000000000000000011351325366651500227550ustar00rootroot00000000000000// 7zSpecStream.cpp #include "StdAfx.h" #include "7zSpecStream.h" STDMETHODIMP CSequentialInStreamSizeCount2::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize; HRESULT result = _stream->Read(data, size, &realProcessedSize); _size += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return result; } STDMETHODIMP CSequentialInStreamSizeCount2::GetSubStreamSize(UInt64 subStream, UInt64 *value) { if (!_getSubStreamSize) return E_NOTIMPL; return _getSubStreamSize->GetSubStreamSize(subStream, value); } src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h000066400000000000000000000015531325366651500224260ustar00rootroot00000000000000// 7zSpecStream.h #ifndef __7Z_SPEC_STREAM_H #define __7Z_SPEC_STREAM_H #include "../../IStream.h" #include "../../ICoder.h" #include "../../../Common/MyCom.h" class CSequentialInStreamSizeCount2: public ISequentialInStream, public ICompressGetSubStreamSize, public CMyUnknownImp { CMyComPtr _stream; CMyComPtr _getSubStreamSize; UInt64 _size; public: void Init(ISequentialInStream *stream) { _stream = stream; _getSubStreamSize = 0; _stream.QueryInterface(IID_ICompressGetSubStreamSize, &_getSubStreamSize); _size = 0; } UInt64 GetSize() const { return _size; } MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); }; #endif src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp000066400000000000000000001141651325366651500221410ustar00rootroot00000000000000// 7zUpdate.cpp #include "StdAfx.h" #include "../../../../C/CpuArch.h" #include "../../../Common/Wildcard.h" #include "../../Common/CreateCoder.h" #include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Compress/CopyCoder.h" #include "../Common/ItemNameUtils.h" #include "../Common/OutStreamWithCRC.h" #include "7zDecode.h" #include "7zEncode.h" #include "7zFolderInStream.h" #include "7zHandler.h" #include "7zOut.h" #include "7zUpdate.h" #ifndef WIN32 #include "Windows/FileIO.h" #endif namespace NArchive { namespace N7z { #ifdef MY_CPU_X86_OR_AMD64 #define USE_86_FILTER #endif static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream, UInt64 position, UInt64 size, ICompressProgressInfo *progress) { RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0)); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr inStreamLimited(streamSpec); streamSpec->SetStream(inStream); streamSpec->Init(size); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr copyCoder = copyCoderSpec; RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress)); return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL); } static int GetReverseSlashPos(const UString &name) { int slashPos = name.ReverseFind(L'/'); #ifdef _WIN32 int slash1Pos = name.ReverseFind(L'\\'); slashPos = MyMax(slashPos, slash1Pos); #endif return slashPos; } int CUpdateItem::GetExtensionPos() const { int slashPos = GetReverseSlashPos(Name); int dotPos = Name.ReverseFind(L'.'); if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0)) return Name.Len(); return dotPos + 1; } UString CUpdateItem::GetExtension() const { return Name.Ptr(GetExtensionPos()); } #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } #define RINOZ_COMP(a, b) RINOZ(MyCompare(a, b)) /* static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2) { size_t c1 = a1.GetCapacity(); size_t c2 = a2.GetCapacity(); RINOZ_COMP(c1, c2); for (size_t i = 0; i < c1; i++) RINOZ_COMP(a1[i], a2[i]); return 0; } static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2) { RINOZ_COMP(c1.NumInStreams, c2.NumInStreams); RINOZ_COMP(c1.NumOutStreams, c2.NumOutStreams); RINOZ_COMP(c1.MethodID, c2.MethodID); return CompareBuffers(c1.Props, c2.Props); } static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2) { RINOZ_COMP(b1.InIndex, b2.InIndex); return MyCompare(b1.OutIndex, b2.OutIndex); } static int CompareFolders(const CFolder &f1, const CFolder &f2) { int s1 = f1.Coders.Size(); int s2 = f2.Coders.Size(); RINOZ_COMP(s1, s2); int i; for (i = 0; i < s1; i++) RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i])); s1 = f1.BindPairs.Size(); s2 = f2.BindPairs.Size(); RINOZ_COMP(s1, s2); for (i = 0; i < s1; i++) RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i])); return 0; } */ /* static int CompareFiles(const CFileItem &f1, const CFileItem &f2) { return CompareFileNames(f1.Name, f2.Name); } */ struct CFolderRepack { int FolderIndex; int Group; CNum NumCopyFiles; }; static int CompareFolderRepacks(const CFolderRepack *p1, const CFolderRepack *p2, void * /* param */) { RINOZ_COMP(p1->Group, p2->Group); int i1 = p1->FolderIndex; int i2 = p2->FolderIndex; /* // In that version we don't want to parse folders here, so we don't compare folders // probably it must be improved in future const CDbEx &db = *(const CDbEx *)param; RINOZ(CompareFolders( db.Folders[i1], db.Folders[i2])); */ return MyCompare(i1, i2); /* RINOZ_COMP( db.NumUnpackStreamsVector[i1], db.NumUnpackStreamsVector[i2]); if (db.NumUnpackStreamsVector[i1] == 0) return 0; return CompareFiles( db.Files[db.FolderStartFileIndex[i1]], db.Files[db.FolderStartFileIndex[i2]]); */ } /* we sort empty files and dirs in such order: - Dir.NonAnti (name sorted) - File.NonAnti (name sorted) - File.Anti (name sorted) - Dir.Anti (reverse name sorted) */ static int CompareEmptyItems(const int *p1, const int *p2, void *param) { const CObjectVector &updateItems = *(const CObjectVector *)param; const CUpdateItem &u1 = updateItems[*p1]; const CUpdateItem &u2 = updateItems[*p2]; // NonAnti < Anti if (u1.IsAnti != u2.IsAnti) return (u1.IsAnti ? 1 : -1); if (u1.IsDir != u2.IsDir) { // Dir.NonAnti < File < Dir.Anti if (u1.IsDir) return (u1.IsAnti ? 1 : -1); return (u2.IsAnti ? -1 : 1); } int n = CompareFileNames(u1.Name, u2.Name); return (u1.IsDir && u1.IsAnti) ? -n : n; } static const char *g_Exts = " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo" " zip jar ear war msi" " 3gp avi mov mpeg mpg mpe wmv" " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav" " swf " " chm hxi hxs" " gif jpeg jpg jp2 png tiff bmp ico psd psp" " awg ps eps cgm dxf svg vrml wmf emf ai md" " cad dwg pps key sxi" " max 3ds" " iso bin nrg mdf img pdi tar cpio xpi" " vfd vhd vud vmc vsv" " vmdk dsk nvram vmem vmsd vmsn vmss vmtm" " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def" " f77 f f90 f95" " asm sql manifest dep " " mak clw csproj vcproj sln dsp dsw " " class " " bat cmd" " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml" " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs" " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf" " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf" " abw afp cwk lwp wpd wps wpt wrf wri" " abf afm bdf fon mgf otf pcf pfa snf ttf" " dbf mdb nsf ntf wdb db fdb gdb" " exe dll ocx vbx sfx sys tlb awx com obj lib out o so " " pdb pch idb ncb opt"; static int GetExtIndex(const char *ext) { int extIndex = 1; const char *p = g_Exts; for (;;) { char c = *p++; if (c == 0) return extIndex; if (c == ' ') continue; int pos = 0; for (;;) { char c2 = ext[pos++]; if (c2 == 0 && (c == 0 || c == ' ')) return extIndex; if (c != c2) break; c = *p++; } extIndex++; for (;;) { if (c == 0) return extIndex; if (c == ' ') break; c = *p++; } } } struct CRefItem { const CUpdateItem *UpdateItem; UInt32 Index; UInt32 ExtensionPos; UInt32 NamePos; unsigned ExtensionIndex; CRefItem() {}; CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType): UpdateItem(&ui), Index(index), ExtensionPos(0), NamePos(0), ExtensionIndex(0) { if (sortByType) { int slashPos = GetReverseSlashPos(ui.Name); NamePos = slashPos + 1; int dotPos = ui.Name.ReverseFind(L'.'); if (dotPos < 0 || dotPos < slashPos) ExtensionPos = ui.Name.Len(); else { ExtensionPos = dotPos + 1; if (ExtensionPos != ui.Name.Len()) { AString s; for (unsigned pos = ExtensionPos;; pos++) { wchar_t c = ui.Name[pos]; if (c >= 0x80) break; if (c == 0) { ExtensionIndex = GetExtIndex(s); break; } s += (char)MyCharLower_Ascii((char)c); } } } } } }; struct CSortParam { // const CObjectVector *TreeFolders; bool SortByType; }; /* we sort files in such order: - Dir.NonAnti (name sorted) - alt streams - Dirs - Dir.Anti (reverse name sorted) */ static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param) { const CRefItem &a1 = *p1; const CRefItem &a2 = *p2; const CUpdateItem &u1 = *a1.UpdateItem; const CUpdateItem &u2 = *a2.UpdateItem; /* if (u1.IsAltStream != u2.IsAltStream) return u1.IsAltStream ? 1 : -1; */ // Actually there are no dirs that time. They were stored in other steps // So that code is unused? if (u1.IsDir != u2.IsDir) return u1.IsDir ? 1 : -1; if (u1.IsDir) { if (u1.IsAnti != u2.IsAnti) return (u1.IsAnti ? 1 : -1); int n = CompareFileNames(u1.Name, u2.Name); return -n; } // bool sortByType = *(bool *)param; const CSortParam *sortParam = (const CSortParam *)param; bool sortByType = sortParam->SortByType; if (sortByType) { RINOZ_COMP(a1.ExtensionIndex, a2.ExtensionIndex); RINOZ(CompareFileNames(u1.Name.Ptr(a1.ExtensionPos), u2.Name.Ptr(a2.ExtensionPos))); RINOZ(CompareFileNames(u1.Name.Ptr(a1.NamePos), u2.Name.Ptr(a2.NamePos))); if (!u1.MTimeDefined && u2.MTimeDefined) return 1; if (u1.MTimeDefined && !u2.MTimeDefined) return -1; if (u1.MTimeDefined && u2.MTimeDefined) RINOZ_COMP(u1.MTime, u2.MTime); RINOZ_COMP(u1.Size, u2.Size); } /* int par1 = a1.UpdateItem->ParentFolderIndex; int par2 = a2.UpdateItem->ParentFolderIndex; const CTreeFolder &tf1 = (*sortParam->TreeFolders)[par1]; const CTreeFolder &tf2 = (*sortParam->TreeFolders)[par2]; int b1 = tf1.SortIndex, e1 = tf1.SortIndexEnd; int b2 = tf2.SortIndex, e2 = tf2.SortIndexEnd; if (b1 < b2) { if (e1 <= b2) return -1; // p2 in p1 int par = par2; for (;;) { const CTreeFolder &tf = (*sortParam->TreeFolders)[par]; par = tf.Parent; if (par == par1) { RINOZ(CompareFileNames(u1.Name, tf.Name)); break; } } } else if (b2 < b1) { if (e2 <= b1) return 1; // p1 in p2 int par = par1; for (;;) { const CTreeFolder &tf = (*sortParam->TreeFolders)[par]; par = tf.Parent; if (par == par2) { RINOZ(CompareFileNames(tf.Name, u2.Name)); break; } } } */ // RINOZ_COMP(a1.UpdateItem->ParentSortIndex, a2.UpdateItem->ParentSortIndex); RINOK(CompareFileNames(u1.Name, u2.Name)); RINOZ_COMP(a1.UpdateItem->IndexInClient, a2.UpdateItem->IndexInClient); RINOZ_COMP(a1.UpdateItem->IndexInArchive, a2.UpdateItem->IndexInArchive); return 0; } struct CSolidGroup { CRecordVector Indices; }; #ifdef _WIN32 static const wchar_t *g_ExeExts[] = { L"dll" , L"exe" , L"ocx" , L"sfx" , L"sys" }; static bool IsExeExt(const wchar_t *ext) { for (int i = 0; i < ARRAY_SIZE(g_ExeExts); i++) if (MyStringCompareNoCase(ext, g_ExeExts[i]) == 0) return true; return false; } #else static bool IsExeFile(const CUpdateItem &ui) { if (ui.Attrib & FILE_ATTRIBUTE_UNIX_EXTENSION) { unsigned short st_mode = ui.Attrib >> 16; if ((st_mode & 00111) && (ui.Size >= 2048)) { // file has the execution flag and it's big enought // try to find if the file is a script NWindows::NFile::NIO::CInFile file; if (file.Open(ui.Name)) { char buffer[512]; UINT32 processedSize; if (file.Read(buffer,sizeof(buffer),processedSize)) { for(UInt32 i = 0; i < processedSize ; i++) { if (buffer[i] == 0) { return true; // this file is not a text (ascii, utf8, ...) ! } } } } } } return false; } #endif static inline void GetMethodFull(UInt64 methodID, UInt32 numInStreams, CMethodFull &m) { m.Id = methodID; m.NumInStreams = numInStreams; m.NumOutStreams = 1; } static void AddBcj2Methods(CCompressionMethodMode &mode) { CMethodFull m; GetMethodFull(k_LZMA, 1, m); m.AddProp32(NCoderPropID::kDictionarySize, 1 << 20); m.AddProp32(NCoderPropID::kNumFastBytes, 128); m.AddProp32(NCoderPropID::kNumThreads, 1); m.AddProp32(NCoderPropID::kLitPosBits, 2); m.AddProp32(NCoderPropID::kLitContextBits, 0); // m.AddPropString(NCoderPropID::kMatchFinder, L"BT2"); mode.Methods.Add(m); mode.Methods.Add(m); CBind bind; bind.OutCoder = 0; bind.InStream = 0; bind.InCoder = 1; bind.OutStream = 0; mode.Binds.Add(bind); bind.InCoder = 2; bind.OutStream = 1; mode.Binds.Add(bind); bind.InCoder = 3; bind.OutStream = 2; mode.Binds.Add(bind); } static void MakeExeMethod(CCompressionMethodMode &mode, bool useFilters, bool addFilter, bool bcj2Filter) { if (!mode.Binds.IsEmpty() || !useFilters || mode.Methods.Size() > 2) return; if (mode.Methods.Size() == 2) { if (mode.Methods[0].Id == k_BCJ2) AddBcj2Methods(mode); return; } if (!addFilter) return; bcj2Filter = bcj2Filter; #ifdef USE_86_FILTER if (bcj2Filter) { CMethodFull m; GetMethodFull(k_BCJ2, 4, m); mode.Methods.Insert(0, m); AddBcj2Methods(mode); } else { CMethodFull m; GetMethodFull(k_BCJ, 1, m); mode.Methods.Insert(0, m); CBind bind; bind.OutCoder = 0; bind.InStream = 0; bind.InCoder = 1; bind.OutStream = 0; mode.Binds.Add(bind); } #endif } static void FromUpdateItemToFileItem(const CUpdateItem &ui, CFileItem &file, CFileItem2 &file2) { if (ui.AttribDefined) file.SetAttrib(ui.Attrib); file2.CTime = ui.CTime; file2.CTimeDefined = ui.CTimeDefined; file2.ATime = ui.ATime; file2.ATimeDefined = ui.ATimeDefined; file2.MTime = ui.MTime; file2.MTimeDefined = ui.MTimeDefined; file2.IsAnti = ui.IsAnti; // file2.IsAux = false; file2.StartPosDefined = false; file.Size = ui.Size; file.IsDir = ui.IsDir; file.HasStream = ui.HasStream(); // file.IsAltStream = ui.IsAltStream; } class CFolderOutStream2: public ISequentialOutStream, public CMyUnknownImp { COutStreamWithCRC *_crcStreamSpec; CMyComPtr _crcStream; const CDbEx *_db; const CBoolVector *_extractStatuses; CMyComPtr _outStream; UInt32 _startIndex; unsigned _currentIndex; bool _fileIsOpen; UInt64 _rem; void OpenFile(); void CloseFile(); HRESULT CloseFileAndSetResult(); HRESULT ProcessEmptyFiles(); public: MY_UNKNOWN_IMP CFolderOutStream2() { _crcStreamSpec = new COutStreamWithCRC; _crcStream = _crcStreamSpec; } HRESULT Init(const CDbEx *db, UInt32 startIndex, const CBoolVector *extractStatuses, ISequentialOutStream *outStream); void ReleaseOutStream(); HRESULT CheckFinishedState() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; } STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; HRESULT CFolderOutStream2::Init(const CDbEx *db, UInt32 startIndex, const CBoolVector *extractStatuses, ISequentialOutStream *outStream) { _db = db; _startIndex = startIndex; _extractStatuses = extractStatuses; _outStream = outStream; _currentIndex = 0; _fileIsOpen = false; return ProcessEmptyFiles(); } void CFolderOutStream2::ReleaseOutStream() { _outStream.Release(); _crcStreamSpec->ReleaseStream(); } void CFolderOutStream2::OpenFile() { _crcStreamSpec->SetStream((*_extractStatuses)[_currentIndex] ? (ISequentialOutStream *)_outStream : NULL); // FIXED for gcc 2.95 _crcStreamSpec->Init(true); _fileIsOpen = true; _rem = _db->Files[_startIndex + _currentIndex].Size; } void CFolderOutStream2::CloseFile() { _crcStreamSpec->ReleaseStream(); _fileIsOpen = false; _currentIndex++; } HRESULT CFolderOutStream2::CloseFileAndSetResult() { const CFileItem &file = _db->Files[_startIndex + _currentIndex]; CloseFile(); return (file.IsDir || !file.CrcDefined || file.Crc == _crcStreamSpec->GetCRC()) ? S_OK: S_FALSE; } HRESULT CFolderOutStream2::ProcessEmptyFiles() { while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0) { OpenFile(); RINOK(CloseFileAndSetResult()); } return S_OK; } STDMETHODIMP CFolderOutStream2::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size != 0) { if (_fileIsOpen) { UInt32 cur = size < _rem ? size : (UInt32)_rem; RINOK(_crcStream->Write(data, cur, &cur)); if (cur == 0) break; data = (const Byte *)data + cur; size -= cur; _rem -= cur; if (processedSize != NULL) *processedSize += cur; if (_rem == 0) { RINOK(CloseFileAndSetResult()); RINOK(ProcessEmptyFiles()); continue; } } else { RINOK(ProcessEmptyFiles()); if (_currentIndex == _extractStatuses->Size()) { // we don't support partial extracting return E_FAIL; } OpenFile(); } } return S_OK; } class CThreadDecoder: public CVirtThread { public: HRESULT Result; CMyComPtr InStream; CFolderOutStream2 *FosSpec; CMyComPtr Fos; UInt64 StartPos; const CFolders *Folders; int FolderIndex; #ifndef _NO_CRYPTO CMyComPtr getTextPassword; #endif DECL_EXTERNAL_CODECS_LOC_VARS2; CDecoder Decoder; #ifndef _7ZIP_ST bool MtMode; UInt32 NumThreads; #endif CThreadDecoder(): Decoder(true) { #ifndef _7ZIP_ST MtMode = false; NumThreads = 1; #endif FosSpec = new CFolderOutStream2; Fos = FosSpec; Result = E_FAIL; } ~CThreadDecoder() { CVirtThread::WaitThreadFinish(); } virtual void Execute(); }; void CThreadDecoder::Execute() { try { #ifndef _NO_CRYPTO bool isEncrypted = false; bool passwordIsDefined = false; #endif Result = Decoder.Decode( EXTERNAL_CODECS_LOC_VARS InStream, StartPos, *Folders, FolderIndex, Fos, NULL _7Z_DECODER_CRYPRO_VARS #ifndef _7ZIP_ST , MtMode, NumThreads #endif ); } catch(...) { Result = E_FAIL; } if (Result == S_OK) Result = FosSpec->CheckFinishedState(); FosSpec->ReleaseOutStream(); } bool static Is86FilteredFolder(const CFolder &f) { FOR_VECTOR(i, f.Coders) { CMethodId m = f.Coders[i].MethodID; if (m == k_BCJ || m == k_BCJ2) return true; } return false; } #ifndef _NO_CRYPTO class CCryptoGetTextPassword: public ICryptoGetTextPassword, public CMyUnknownImp { public: UString Password; MY_UNKNOWN_IMP STDMETHOD(CryptoGetTextPassword)(BSTR *password); }; STDMETHODIMP CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password) { return StringToBstr(Password, password); } #endif static const int kNumGroupsMax = 4; static bool Is86Group(int group) { return (group & 1) != 0; } static bool IsEncryptedGroup(int group) { return (group & 2) != 0; } static int GetGroupIndex(bool encrypted, int bcjFiltered) { return (encrypted ? 2 : 0) + (bcjFiltered ? 1 : 0); } static void GetFile(const CDatabase &inDb, int index, CFileItem &file, CFileItem2 &file2) { file = inDb.Files[index]; file2.CTimeDefined = inDb.CTime.GetItem(index, file2.CTime); file2.ATimeDefined = inDb.ATime.GetItem(index, file2.ATime); file2.MTimeDefined = inDb.MTime.GetItem(index, file2.MTime); file2.StartPosDefined = inDb.StartPos.GetItem(index, file2.StartPos); file2.IsAnti = inDb.IsItemAnti(index); // file2.IsAux = inDb.IsItemAux(index); } HRESULT Update( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, const CDbEx *db, const CObjectVector &updateItems, // const CObjectVector &treeFolders, // const CUniqBlocks &secureBlocks, COutArchive &archive, CArchiveDatabaseOut &newDatabase, ISequentialOutStream *seqOutStream, IArchiveUpdateCallback *updateCallback, const CUpdateOptions &options #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getDecoderPassword #endif ) { UInt64 numSolidFiles = options.NumSolidFiles; if (numSolidFiles == 0) numSolidFiles = 1; // size_t totalSecureDataSize = (size_t)secureBlocks.GetTotalSizeInBytes(); /* CMyComPtr outStream; RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream)); if (!outStream) return E_NOTIMPL; */ UInt64 startBlockSize = db != 0 ? db->ArcInfo.StartPosition: 0; if (startBlockSize > 0 && !options.RemoveSfxBlock) { RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL)); } CIntArr fileIndexToUpdateIndexMap; CRecordVector folderRefs; UInt64 complexity = 0; UInt64 inSizeForReduce2 = 0; bool needEncryptedRepack = false; if (db != 0) { fileIndexToUpdateIndexMap.Alloc(db->Files.Size()); unsigned i; for (i = 0; i < db->Files.Size(); i++) fileIndexToUpdateIndexMap[i] = -1; for (i = 0; i < updateItems.Size(); i++) { int index = updateItems[i].IndexInArchive; if (index != -1) fileIndexToUpdateIndexMap[index] = i; } for (i = 0; i < (int)db->NumFolders; i++) { CNum indexInFolder = 0; CNum numCopyItems = 0; CNum numUnpackStreams = db->NumUnpackStreamsVector[i]; UInt64 repackSize = 0; for (CNum fi = db->FolderStartFileIndex[i]; indexInFolder < numUnpackStreams; fi++) { const CFileItem &file = db->Files[fi]; if (file.HasStream) { indexInFolder++; int updateIndex = fileIndexToUpdateIndexMap[fi]; if (updateIndex >= 0 && !updateItems[updateIndex].NewData) { numCopyItems++; repackSize += file.Size; } } } if (numCopyItems == 0) continue; CFolderRepack rep; rep.FolderIndex = i; rep.NumCopyFiles = numCopyItems; CFolder f; db->ParseFolderInfo(i, f); bool isEncrypted = f.IsEncrypted(); rep.Group = GetGroupIndex(isEncrypted, Is86FilteredFolder(f)); folderRefs.Add(rep); if (numCopyItems == numUnpackStreams) complexity += db->GetFolderFullPackSize(i); else { complexity += repackSize; if (repackSize > inSizeForReduce2) inSizeForReduce2 = repackSize; if (isEncrypted) needEncryptedRepack = true; } } folderRefs.Sort(CompareFolderRepacks, (void *)db); } UInt64 inSizeForReduce = 0; unsigned i; for (i = 0; i < updateItems.Size(); i++) { const CUpdateItem &ui = updateItems[i]; if (ui.NewData) { complexity += ui.Size; if (numSolidFiles != 1) inSizeForReduce += ui.Size; else if (ui.Size > inSizeForReduce) inSizeForReduce = ui.Size; } } if (inSizeForReduce2 > inSizeForReduce) inSizeForReduce = inSizeForReduce2; RINOK(updateCallback->SetTotal(complexity)); CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(updateCallback, true); CStreamBinder sb; RINOK(sb.CreateEvents()); CThreadDecoder threadDecoder; if (!folderRefs.IsEmpty()) { #ifdef EXTERNAL_CODECS threadDecoder.__externalCodecs = __externalCodecs; #endif RINOK(threadDecoder.Create()); } CObjectVector groups; for (i = 0; i < kNumGroupsMax; i++) groups.AddNew(); { // ---------- Split files to groups ---------- bool useFilters = options.UseFilters; const CCompressionMethodMode &method = *options.Method; if (method.Methods.Size() != 1 || method.Binds.Size() != 0) useFilters = false; for (i = 0; i < updateItems.Size(); i++) { const CUpdateItem &ui = updateItems[i]; if (!ui.NewData || !ui.HasStream()) continue; bool filteredGroup = false; if (useFilters) { #ifdef _WIN32 int dotPos = ui.Name.ReverseFind(L'.'); if (dotPos >= 0) filteredGroup = IsExeExt(ui.Name.Ptr(dotPos + 1)); #else filteredGroup = IsExeFile(ui); #endif } groups[GetGroupIndex(method.PasswordIsDefined, filteredGroup)].Indices.Add(i); } } #ifndef _NO_CRYPTO CCryptoGetTextPassword *getPasswordSpec = NULL; if (needEncryptedRepack) { getPasswordSpec = new CCryptoGetTextPassword; threadDecoder.getTextPassword = getPasswordSpec; if (options.Method->PasswordIsDefined) getPasswordSpec->Password = options.Method->Password; else { if (!getDecoderPassword) return E_NOTIMPL; CMyComBSTR password; RINOK(getDecoderPassword->CryptoGetTextPassword(&password)); if ((BSTR)password) getPasswordSpec->Password = password; } } #endif // ---------- Compress ---------- RINOK(archive.Create(seqOutStream, false)); RINOK(archive.SkipPrefixArchiveHeader()); /* CIntVector treeFolderToArcIndex; treeFolderToArcIndex.Reserve(treeFolders.Size()); for (i = 0; i < treeFolders.Size(); i++) treeFolderToArcIndex.Add(-1); // ---------- Write Tree (only AUX dirs) ---------- for (i = 1; i < treeFolders.Size(); i++) { const CTreeFolder &treeFolder = treeFolders[i]; CFileItem file; CFileItem2 file2; file2.Init(); int secureID = 0; if (treeFolder.UpdateItemIndex < 0) { // we can store virtual dir item wuthout attrib, but we want all items have attrib. file.SetAttrib(FILE_ATTRIBUTE_DIRECTORY); file2.IsAux = true; } else { const CUpdateItem &ui = updateItems[treeFolder.UpdateItemIndex]; // if item is not dir, then it's parent for alt streams. // we will write such items later if (!ui.IsDir) continue; secureID = ui.SecureIndex; if (ui.NewProps) FromUpdateItemToFileItem(ui, file, file2); else GetFile(*db, ui.IndexInArchive, file, file2); } file.Size = 0; file.HasStream = false; file.IsDir = true; file.Parent = treeFolder.Parent; treeFolderToArcIndex[i] = newDatabase.Files.Size(); newDatabase.AddFile(file, file2, treeFolder.Name); if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(secureID); } */ { /* ---------- Write non-AUX dirs and Empty files ---------- */ CRecordVector emptyRefs; for (i = 0; i < updateItems.Size(); i++) { const CUpdateItem &ui = updateItems[i]; if (ui.NewData) { if (ui.HasStream()) continue; } else if (ui.IndexInArchive != -1 && db->Files[ui.IndexInArchive].HasStream) continue; /* if (ui.TreeFolderIndex >= 0) continue; */ emptyRefs.Add(i); } emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems); for (i = 0; i < emptyRefs.Size(); i++) { const CUpdateItem &ui = updateItems[emptyRefs[i]]; CFileItem file; CFileItem2 file2; UString name; if (ui.NewProps) { FromUpdateItemToFileItem(ui, file, file2); name = ui.Name; } else { GetFile(*db, ui.IndexInArchive, file, file2); db->GetPath(ui.IndexInArchive, name); } /* if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(ui.SecureIndex); file.Parent = ui.ParentFolderIndex; */ newDatabase.AddFile(file, file2, name); } } unsigned folderRefIndex = 0; lps->ProgressOffset = 0; for (int groupIndex = 0; groupIndex < kNumGroupsMax; groupIndex++) { const CSolidGroup &group = groups[groupIndex]; CCompressionMethodMode method = *options.Method; MakeExeMethod(method, options.UseFilters, Is86Group(groupIndex), options.MaxFilter); if (IsEncryptedGroup(groupIndex)) { if (!method.PasswordIsDefined) { #ifndef _NO_CRYPTO if (getPasswordSpec) method.Password = getPasswordSpec->Password; #endif method.PasswordIsDefined = true; } } else { method.PasswordIsDefined = false; method.Password.Empty(); } CEncoder encoder(method); for (; folderRefIndex < folderRefs.Size(); folderRefIndex++) { const CFolderRepack &rep = folderRefs[folderRefIndex]; if (rep.Group != groupIndex) break; int folderIndex = rep.FolderIndex; if (rep.NumCopyFiles == db->NumUnpackStreamsVector[folderIndex]) { UInt64 packSize = db->GetFolderFullPackSize(folderIndex); RINOK(WriteRange(inStream, archive.SeqStream, db->GetFolderStreamPos(folderIndex, 0), packSize, progress)); lps->ProgressOffset += packSize; CFolder &folder = newDatabase.Folders.AddNew(); db->ParseFolderInfo(folderIndex, folder); CNum startIndex = db->FoStartPackStreamIndex[folderIndex]; for (unsigned j = 0; j < folder.PackStreams.Size(); j++) { newDatabase.PackSizes.Add(db->GetStreamPackSize(startIndex + j)); // newDatabase.PackCRCsDefined.Add(db.PackCRCsDefined[startIndex + j]); // newDatabase.PackCRCs.Add(db.PackCRCs[startIndex + j]); } UInt32 indexStart = db->FoToCoderUnpackSizes[folderIndex]; UInt32 indexEnd = db->FoToCoderUnpackSizes[folderIndex + 1]; for (; indexStart < indexEnd; indexStart++) newDatabase.CoderUnpackSizes.Add(db->CoderUnpackSizes[indexStart]); } else { CBoolVector extractStatuses; CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex]; CNum indexInFolder = 0; for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++) { bool needExtract = false; if (db->Files[fi].HasStream) { indexInFolder++; int updateIndex = fileIndexToUpdateIndexMap[fi]; if (updateIndex >= 0 && !updateItems[updateIndex].NewData) needExtract = true; } extractStatuses.Add(needExtract); } unsigned startPackIndex = newDatabase.PackSizes.Size(); UInt64 curUnpackSize; { CMyComPtr sbInStream; { CMyComPtr sbOutStream; sb.CreateStreams(&sbInStream, &sbOutStream); sb.ReInit(); RINOK(threadDecoder.FosSpec->Init(db, db->FolderStartFileIndex[folderIndex], &extractStatuses, sbOutStream)); } threadDecoder.InStream = inStream; threadDecoder.Folders = (const CFolders *)db; threadDecoder.FolderIndex = folderIndex; threadDecoder.StartPos = db->ArcInfo.DataStartPosition; // db->GetFolderStreamPos(folderIndex, 0); threadDecoder.Start(); RINOK(encoder.Encode( EXTERNAL_CODECS_LOC_VARS sbInStream, NULL, &inSizeForReduce, newDatabase.Folders.AddNew(), newDatabase.CoderUnpackSizes, curUnpackSize, archive.SeqStream, newDatabase.PackSizes, progress)); threadDecoder.WaitExecuteFinish(); } RINOK(threadDecoder.Result); for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++) lps->OutSize += newDatabase.PackSizes[startPackIndex]; lps->InSize += curUnpackSize; } newDatabase.NumUnpackStreamsVector.Add(rep.NumCopyFiles); CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex]; CNum indexInFolder = 0; for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++) { CFileItem file; CFileItem2 file2; GetFile(*db, fi, file, file2); UString name; db->GetPath(fi, name); if (file.HasStream) { indexInFolder++; int updateIndex = fileIndexToUpdateIndexMap[fi]; if (updateIndex >= 0) { const CUpdateItem &ui = updateItems[updateIndex]; if (ui.NewData) continue; if (ui.NewProps) { CFileItem uf; FromUpdateItemToFileItem(ui, uf, file2); uf.Size = file.Size; uf.Crc = file.Crc; uf.CrcDefined = file.CrcDefined; uf.HasStream = file.HasStream; file = uf; name = ui.Name; } /* file.Parent = ui.ParentFolderIndex; if (ui.TreeFolderIndex >= 0) treeFolderToArcIndex[ui.TreeFolderIndex] = newDatabase.Files.Size(); if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(ui.SecureIndex); */ newDatabase.AddFile(file, file2, name); } } } } unsigned numFiles = group.Indices.Size(); if (numFiles == 0) continue; CRecordVector refItems; refItems.ClearAndSetSize(numFiles); bool sortByType = (numSolidFiles > 1); for (i = 0; i < numFiles; i++) refItems[i] = CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType); CSortParam sortParam; // sortParam.TreeFolders = &treeFolders; sortParam.SortByType = sortByType; refItems.Sort(CompareUpdateItems, (void *)&sortParam); CObjArray indices(numFiles); for (i = 0; i < numFiles; i++) { UInt32 index = refItems[i].Index; indices[i] = index; /* const CUpdateItem &ui = updateItems[index]; CFileItem file; if (ui.NewProps) FromUpdateItemToFileItem(ui, file); else file = db.Files[ui.IndexInArchive]; if (file.IsAnti || file.IsDir) return E_FAIL; newDatabase.Files.Add(file); */ } for (i = 0; i < numFiles;) { UInt64 totalSize = 0; int numSubFiles; UString prevExtension; for (numSubFiles = 0; i + numSubFiles < numFiles && numSubFiles < numSolidFiles; numSubFiles++) { const CUpdateItem &ui = updateItems[indices[i + numSubFiles]]; totalSize += ui.Size; if (totalSize > options.NumSolidBytes) break; if (options.SolidExtension) { UString ext = ui.GetExtension(); if (numSubFiles == 0) prevExtension = ext; else if (!ext.IsEqualToNoCase(prevExtension)) break; } } if (numSubFiles < 1) numSubFiles = 1; CFolderInStream *inStreamSpec = new CFolderInStream; CMyComPtr solidInStream(inStreamSpec); inStreamSpec->Init(updateCallback, &indices[i], numSubFiles); unsigned startPackIndex = newDatabase.PackSizes.Size(); UInt64 curFolderUnpackSize; RINOK(encoder.Encode( EXTERNAL_CODECS_LOC_VARS solidInStream, NULL, &inSizeForReduce, newDatabase.Folders.AddNew(), newDatabase.CoderUnpackSizes, curFolderUnpackSize, archive.SeqStream, newDatabase.PackSizes, progress)); for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++) lps->OutSize += newDatabase.PackSizes[startPackIndex]; lps->InSize += curFolderUnpackSize; // for () // newDatabase.PackCRCsDefined.Add(false); // newDatabase.PackCRCs.Add(0); CNum numUnpackStreams = 0; for (int subIndex = 0; subIndex < numSubFiles; subIndex++) { const CUpdateItem &ui = updateItems[indices[i + subIndex]]; CFileItem file; CFileItem2 file2; UString name; if (ui.NewProps) { FromUpdateItemToFileItem(ui, file, file2); name = ui.Name; } else { GetFile(*db, ui.IndexInArchive, file, file2); db->GetPath(ui.IndexInArchive, name); } if (file2.IsAnti || file.IsDir) return E_FAIL; /* CFileItem &file = newDatabase.Files[ startFileIndexInDatabase + i + subIndex]; */ if (!inStreamSpec->Processed[subIndex]) { continue; // file.Name += L".locked"; } file.Crc = inStreamSpec->CRCs[subIndex]; file.Size = inStreamSpec->Sizes[subIndex]; if (file.Size != 0) { file.CrcDefined = true; file.HasStream = true; numUnpackStreams++; } else { file.CrcDefined = false; file.HasStream = false; } /* file.Parent = ui.ParentFolderIndex; if (ui.TreeFolderIndex >= 0) treeFolderToArcIndex[ui.TreeFolderIndex] = newDatabase.Files.Size(); if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(ui.SecureIndex); */ newDatabase.AddFile(file, file2, name); } // numUnpackStreams = 0 is very bad case for locked files // v3.13 doesn't understand it. newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams); i += numSubFiles; } } if (folderRefIndex != folderRefs.Size()) return E_FAIL; RINOK(lps->SetCur()); /* folderRefs.ClearAndFree(); fileIndexToUpdateIndexMap.ClearAndFree(); groups.ClearAndFree(); */ /* for (i = 0; i < newDatabase.Files.Size(); i++) { CFileItem &file = newDatabase.Files[i]; file.Parent = treeFolderToArcIndex[file.Parent]; } if (totalSecureDataSize != 0) { newDatabase.SecureBuf.SetCapacity(totalSecureDataSize); size_t pos = 0; newDatabase.SecureSizes.Reserve(secureBlocks.Sorted.Size()); for (i = 0; i < secureBlocks.Sorted.Size(); i++) { const CByteBuffer &buf = secureBlocks.Bufs[secureBlocks.Sorted[i]]; size_t size = buf.GetCapacity(); memcpy(newDatabase.SecureBuf + pos, buf, size); newDatabase.SecureSizes.Add((UInt32)size); pos += size; } } */ newDatabase.ReserveDown(); return S_OK; } }} src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h000066400000000000000000000045441325366651500216050ustar00rootroot00000000000000// 7zUpdate.h #ifndef __7Z_UPDATE_H #define __7Z_UPDATE_H #include "../IArchive.h" // #include "../../Common/UniqBlocks.h" #include "7zCompressionMode.h" #include "7zIn.h" #include "7zOut.h" namespace NArchive { namespace N7z { /* struct CTreeFolder { UString Name; int Parent; CIntVector SubFolders; int UpdateItemIndex; int SortIndex; int SortIndexEnd; CTreeFolder(): UpdateItemIndex(-1) {} }; */ struct CUpdateItem { int IndexInArchive; int IndexInClient; UInt64 CTime; UInt64 ATime; UInt64 MTime; UInt64 Size; UString Name; /* bool IsAltStream; int ParentFolderIndex; int TreeFolderIndex; */ // that code is not used in 9.26 // int ParentSortIndex; // int ParentSortIndexEnd; UInt32 Attrib; bool NewData; bool NewProps; bool IsAnti; bool IsDir; bool AttribDefined; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; // int SecureIndex; // 0 means (no_security) bool HasStream() const { return !IsDir && !IsAnti && Size != 0; } CUpdateItem(): // ParentSortIndex(-1), // IsAltStream(false), IsAnti(false), IsDir(false), AttribDefined(false), CTimeDefined(false), ATimeDefined(false), MTimeDefined(false) // SecureIndex(0) {} void SetDirStatusFromAttrib() { IsDir = ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0); }; int GetExtensionPos() const; UString GetExtension() const; }; struct CUpdateOptions { const CCompressionMethodMode *Method; const CCompressionMethodMode *HeaderMethod; bool UseFilters; bool MaxFilter; CHeaderOptions HeaderOptions; UInt64 NumSolidFiles; UInt64 NumSolidBytes; bool SolidExtension; bool RemoveSfxBlock; bool VolumeMode; }; HRESULT Update( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, const CDbEx *db, const CObjectVector &updateItems, // const CObjectVector &treeFolders, // treeFolders[0] is root // const CUniqBlocks &secureBlocks, COutArchive &archive, CArchiveDatabaseOut &newDatabase, ISequentialOutStream *seqOutStream, IArchiveUpdateCallback *updateCallback, const CUpdateOptions &options #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getDecoderPassword #endif ); }} #endif src/libs/7zip/unix/CPP/7zip/Archive/Archive.pri000066400000000000000000000003261325366651500214600ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Archive/IArchive.h SOURCES += $$7ZIP_BASE/CPP/7zip/Archive/LzmaHandler.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/SplitHandler.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/XzHandler.cpp src/libs/7zip/unix/CPP/7zip/Archive/Common/000077500000000000000000000000001325366651500206125ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp000066400000000000000000000071551325366651500234510ustar00rootroot00000000000000// CoderMixer2.cpp #include "StdAfx.h" #include "CoderMixer2.h" namespace NCoderMixer { CBindReverseConverter::CBindReverseConverter(const CBindInfo &srcBindInfo): _srcBindInfo(srcBindInfo) { srcBindInfo.GetNumStreams(NumSrcInStreams, _numSrcOutStreams); UInt32 j; _srcInToDestOutMap.ClearAndSetSize(NumSrcInStreams); DestOutToSrcInMap.ClearAndSetSize(NumSrcInStreams); for (j = 0; j < NumSrcInStreams; j++) { _srcInToDestOutMap[j] = 0; DestOutToSrcInMap[j] = 0; } _srcOutToDestInMap.ClearAndSetSize(_numSrcOutStreams); _destInToSrcOutMap.ClearAndSetSize(_numSrcOutStreams); for (j = 0; j < _numSrcOutStreams; j++) { _srcOutToDestInMap[j] = 0; _destInToSrcOutMap[j] = 0; } UInt32 destInOffset = 0; UInt32 destOutOffset = 0; UInt32 srcInOffset = NumSrcInStreams; UInt32 srcOutOffset = _numSrcOutStreams; for (int i = srcBindInfo.Coders.Size() - 1; i >= 0; i--) { const CCoderStreamsInfo &srcCoderInfo = srcBindInfo.Coders[i]; srcInOffset -= srcCoderInfo.NumInStreams; srcOutOffset -= srcCoderInfo.NumOutStreams; UInt32 j; for (j = 0; j < srcCoderInfo.NumInStreams; j++, destOutOffset++) { UInt32 index = srcInOffset + j; _srcInToDestOutMap[index] = destOutOffset; DestOutToSrcInMap[destOutOffset] = index; } for (j = 0; j < srcCoderInfo.NumOutStreams; j++, destInOffset++) { UInt32 index = srcOutOffset + j; _srcOutToDestInMap[index] = destInOffset; _destInToSrcOutMap[destInOffset] = index; } } } void CBindReverseConverter::CreateReverseBindInfo(CBindInfo &destBindInfo) { destBindInfo.Coders.ClearAndReserve(_srcBindInfo.Coders.Size()); destBindInfo.BindPairs.ClearAndReserve(_srcBindInfo.BindPairs.Size()); destBindInfo.InStreams.ClearAndReserve(_srcBindInfo.OutStreams.Size()); destBindInfo.OutStreams.ClearAndReserve(_srcBindInfo.InStreams.Size()); unsigned i; for (i = _srcBindInfo.Coders.Size(); i != 0;) { i--; const CCoderStreamsInfo &srcCoderInfo = _srcBindInfo.Coders[i]; CCoderStreamsInfo destCoderInfo; destCoderInfo.NumInStreams = srcCoderInfo.NumOutStreams; destCoderInfo.NumOutStreams = srcCoderInfo.NumInStreams; destBindInfo.Coders.AddInReserved(destCoderInfo); } for (i = _srcBindInfo.BindPairs.Size(); i != 0;) { i--; const CBindPair &srcBindPair = _srcBindInfo.BindPairs[i]; CBindPair destBindPair; destBindPair.InIndex = _srcOutToDestInMap[srcBindPair.OutIndex]; destBindPair.OutIndex = _srcInToDestOutMap[srcBindPair.InIndex]; destBindInfo.BindPairs.AddInReserved(destBindPair); } for (i = 0; i < _srcBindInfo.InStreams.Size(); i++) destBindInfo.OutStreams.AddInReserved(_srcInToDestOutMap[_srcBindInfo.InStreams[i]]); for (i = 0; i < _srcBindInfo.OutStreams.Size(); i++) destBindInfo.InStreams.AddInReserved(_srcOutToDestInMap[_srcBindInfo.OutStreams[i]]); } void SetSizes(const UInt64 **srcSizes, CRecordVector &sizes, CRecordVector &sizePointers, UInt32 numItems) { sizes.ClearAndSetSize(numItems); sizePointers.ClearAndSetSize(numItems); for (UInt32 i = 0; i < numItems; i++) { if (!srcSizes || !srcSizes[i]) { sizes[i] = 0; sizePointers[i] = NULL; } else { sizes[i] = *(srcSizes[i]); sizePointers[i] = &sizes[i]; } } } void CCoderInfo2::SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes) { SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); } } src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h000066400000000000000000000104671325366651500231160ustar00rootroot00000000000000// CoderMixer2.h #ifndef __CODER_MIXER2_H #define __CODER_MIXER2_H #include "../../../Common/MyCom.h" #include "../../../Common/MyVector.h" #include "../../ICoder.h" namespace NCoderMixer { struct CBindPair { UInt32 InIndex; UInt32 OutIndex; }; struct CCoderStreamsInfo { UInt32 NumInStreams; UInt32 NumOutStreams; }; struct CBindInfo { CRecordVector Coders; CRecordVector BindPairs; CRecordVector InStreams; CRecordVector OutStreams; void Clear() { Coders.Clear(); BindPairs.Clear(); InStreams.Clear(); OutStreams.Clear(); } /* UInt32 GetCoderStartOutStream(UInt32 coderIndex) const { UInt32 numOutStreams = 0; for (UInt32 i = 0; i < coderIndex; i++) numOutStreams += Coders[i].NumOutStreams; return numOutStreams; } */ void GetNumStreams(UInt32 &numInStreams, UInt32 &numOutStreams) const { numInStreams = 0; numOutStreams = 0; FOR_VECTOR (i, Coders) { const CCoderStreamsInfo &coderStreamsInfo = Coders[i]; numInStreams += coderStreamsInfo.NumInStreams; numOutStreams += coderStreamsInfo.NumOutStreams; } } int FindBinderForInStream(UInt32 inStream) const { FOR_VECTOR (i, BindPairs) if (BindPairs[i].InIndex == inStream) return i; return -1; } int FindBinderForOutStream(UInt32 outStream) const { FOR_VECTOR (i, BindPairs) if (BindPairs[i].OutIndex == outStream) return i; return -1; } UInt32 GetCoderInStreamIndex(UInt32 coderIndex) const { UInt32 streamIndex = 0; for (UInt32 i = 0; i < coderIndex; i++) streamIndex += Coders[i].NumInStreams; return streamIndex; } UInt32 GetCoderOutStreamIndex(UInt32 coderIndex) const { UInt32 streamIndex = 0; for (UInt32 i = 0; i < coderIndex; i++) streamIndex += Coders[i].NumOutStreams; return streamIndex; } void FindInStream(UInt32 streamIndex, UInt32 &coderIndex, UInt32 &coderStreamIndex) const { for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) { UInt32 curSize = Coders[coderIndex].NumInStreams; if (streamIndex < curSize) { coderStreamIndex = streamIndex; return; } streamIndex -= curSize; } throw 1; } void FindOutStream(UInt32 streamIndex, UInt32 &coderIndex, UInt32 &coderStreamIndex) const { for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) { UInt32 curSize = Coders[coderIndex].NumOutStreams; if (streamIndex < curSize) { coderStreamIndex = streamIndex; return; } streamIndex -= curSize; } throw 1; } }; class CBindReverseConverter { UInt32 _numSrcOutStreams; NCoderMixer::CBindInfo _srcBindInfo; CRecordVector _srcInToDestOutMap; CRecordVector _srcOutToDestInMap; CRecordVector _destInToSrcOutMap; public: UInt32 NumSrcInStreams; CRecordVector DestOutToSrcInMap; CBindReverseConverter(const NCoderMixer::CBindInfo &srcBindInfo); void CreateReverseBindInfo(NCoderMixer::CBindInfo &destBindInfo); }; void SetSizes(const UInt64 **srcSizes, CRecordVector &sizes, CRecordVector &sizePointers, UInt32 numItems); struct CCoderInfo2 { CMyComPtr Coder; CMyComPtr Coder2; UInt32 NumInStreams; UInt32 NumOutStreams; CRecordVector InSizes; CRecordVector OutSizes; CRecordVector InSizePointers; CRecordVector OutSizePointers; CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams): NumInStreams(numInStreams), NumOutStreams(numOutStreams) {} void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); HRESULT QueryInterface(REFGUID iid, void** pp) const { IUnknown *p = Coder ? (IUnknown *)Coder : (IUnknown *)Coder2; return p->QueryInterface(iid, pp); } }; class CCoderMixer2 { public: virtual HRESULT SetBindInfo(const CBindInfo &bindInfo) = 0; virtual void ReInit() = 0; virtual void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) = 0; }; } #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp000066400000000000000000000140721325366651500237060ustar00rootroot00000000000000// CoderMixer2MT.cpp #include "StdAfx.h" #include "CoderMixer2MT.h" namespace NCoderMixer { CCoder2::CCoder2(UInt32 numInStreams, UInt32 numOutStreams): CCoderInfo2(numInStreams, numOutStreams) { InStreams.ClearAndReserve(NumInStreams); OutStreams.ClearAndReserve(NumOutStreams); } void CCoder2::Execute() { Code(NULL); } void CCoder2::Code(ICompressProgressInfo *progress) { InStreamPointers.ClearAndReserve(NumInStreams); OutStreamPointers.ClearAndReserve(NumOutStreams); UInt32 i; for (i = 0; i < NumInStreams; i++) { if (InSizePointers[i]) InSizePointers[i] = &InSizes[i]; InStreamPointers.AddInReserved((ISequentialInStream *)InStreams[i]); } for (i = 0; i < NumOutStreams; i++) { if (OutSizePointers[i]) OutSizePointers[i] = &OutSizes[i]; OutStreamPointers.AddInReserved((ISequentialOutStream *)OutStreams[i]); } if (Coder) Result = Coder->Code(InStreamPointers[0], OutStreamPointers[0], InSizePointers[0], OutSizePointers[0], progress); else Result = Coder2->Code(&InStreamPointers.Front(), &InSizePointers.Front(), NumInStreams, &OutStreamPointers.Front(), &OutSizePointers.Front(), NumOutStreams, progress); { unsigned i; for (i = 0; i < InStreams.Size(); i++) InStreams[i].Release(); for (i = 0; i < OutStreams.Size(); i++) OutStreams[i].Release(); } } /* void CCoder2::SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes) { SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); } */ ////////////////////////////////////// // CCoderMixer2MT HRESULT CCoderMixer2MT::SetBindInfo(const CBindInfo &bindInfo) { _bindInfo = bindInfo; _streamBinders.Clear(); FOR_VECTOR (i, _bindInfo.BindPairs) { RINOK(_streamBinders.AddNew().CreateEvents()); } return S_OK; } void CCoderMixer2MT::AddCoderCommon() { const CCoderStreamsInfo &c = _bindInfo.Coders[_coders.Size()]; CCoder2 threadCoderInfo(c.NumInStreams, c.NumOutStreams); _coders.Add(threadCoderInfo); } void CCoderMixer2MT::AddCoder(ICompressCoder *coder) { AddCoderCommon(); _coders.Back().Coder = coder; } void CCoderMixer2MT::AddCoder2(ICompressCoder2 *coder) { AddCoderCommon(); _coders.Back().Coder2 = coder; } void CCoderMixer2MT::ReInit() { FOR_VECTOR (i, _streamBinders) _streamBinders[i].ReInit(); } HRESULT CCoderMixer2MT::Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams) { /* if (_coders.Size() != _bindInfo.Coders.Size()) throw 0; */ unsigned i; for (i = 0; i < _coders.Size(); i++) { CCoder2 &coderInfo = _coders[i]; const CCoderStreamsInfo &coderStreamsInfo = _bindInfo.Coders[i]; coderInfo.InStreams.Clear(); UInt32 j; for (j = 0; j < coderStreamsInfo.NumInStreams; j++) coderInfo.InStreams.Add(NULL); coderInfo.OutStreams.Clear(); for (j = 0; j < coderStreamsInfo.NumOutStreams; j++) coderInfo.OutStreams.Add(NULL); } for (i = 0; i < _bindInfo.BindPairs.Size(); i++) { const CBindPair &bindPair = _bindInfo.BindPairs[i]; UInt32 inCoderIndex, inCoderStreamIndex; UInt32 outCoderIndex, outCoderStreamIndex; _bindInfo.FindInStream(bindPair.InIndex, inCoderIndex, inCoderStreamIndex); _bindInfo.FindOutStream(bindPair.OutIndex, outCoderIndex, outCoderStreamIndex); _streamBinders[i].CreateStreams( &_coders[inCoderIndex].InStreams[inCoderStreamIndex], &_coders[outCoderIndex].OutStreams[outCoderStreamIndex]); CMyComPtr inSetSize, outSetSize; _coders[inCoderIndex].QueryInterface(IID_ICompressSetBufSize, (void **)&inSetSize); _coders[outCoderIndex].QueryInterface(IID_ICompressSetBufSize, (void **)&outSetSize); if (inSetSize && outSetSize) { const UInt32 kBufSize = 1 << 19; inSetSize->SetInBufSize(inCoderStreamIndex, kBufSize); outSetSize->SetOutBufSize(outCoderStreamIndex, kBufSize); } } for (i = 0; i < _bindInfo.InStreams.Size(); i++) { UInt32 inCoderIndex, inCoderStreamIndex; _bindInfo.FindInStream(_bindInfo.InStreams[i], inCoderIndex, inCoderStreamIndex); _coders[inCoderIndex].InStreams[inCoderStreamIndex] = inStreams[i]; } for (i = 0; i < _bindInfo.OutStreams.Size(); i++) { UInt32 outCoderIndex, outCoderStreamIndex; _bindInfo.FindOutStream(_bindInfo.OutStreams[i], outCoderIndex, outCoderStreamIndex); _coders[outCoderIndex].OutStreams[outCoderStreamIndex] = outStreams[i]; } return S_OK; } HRESULT CCoderMixer2MT::ReturnIfError(HRESULT code) { FOR_VECTOR (i, _coders) if (_coders[i].Result == code) return code; return S_OK; } STDMETHODIMP CCoderMixer2MT::Code(ISequentialInStream **inStreams, const UInt64 ** /* inSizes */, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams, ICompressProgressInfo *progress) { if (numInStreams != (UInt32)_bindInfo.InStreams.Size() || numOutStreams != (UInt32)_bindInfo.OutStreams.Size()) return E_INVALIDARG; Init(inStreams, outStreams); unsigned i; for (i = 0; i < _coders.Size(); i++) if (i != _progressCoderIndex) { RINOK(_coders[i].Create()); } for (i = 0; i < _coders.Size(); i++) if (i != _progressCoderIndex) _coders[i].Start(); _coders[_progressCoderIndex].Code(progress); for (i = 0; i < _coders.Size(); i++) if (i != _progressCoderIndex) _coders[i].WaitExecuteFinish(); RINOK(ReturnIfError(E_ABORT)); RINOK(ReturnIfError(E_OUTOFMEMORY)); for (i = 0; i < _coders.Size(); i++) { HRESULT result = _coders[i].Result; if (result != S_OK && result != E_FAIL && result != S_FALSE) return result; } RINOK(ReturnIfError(S_FALSE)); for (i = 0; i < _coders.Size(); i++) { HRESULT result = _coders[i].Result; if (result != S_OK) return result; } return S_OK; } } src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h000066400000000000000000000043171325366651500233540ustar00rootroot00000000000000// CoderMixer2MT.h #ifndef __CODER_MIXER2_MT_H #define __CODER_MIXER2_MT_H #include "CoderMixer2.h" #include "../../../Common/MyCom.h" #include "../../Common/StreamBinder.h" #include "../../Common/VirtThread.h" namespace NCoderMixer { struct CCoder2: public CCoderInfo2, public CVirtThread { CRecordVector InStreamPointers; CRecordVector OutStreamPointers; public: HRESULT Result; CObjectVector< CMyComPtr > InStreams; CObjectVector< CMyComPtr > OutStreams; CCoder2(UInt32 numInStreams, UInt32 numOutStreams); ~CCoder2() { CVirtThread::WaitThreadFinish(); } // void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); virtual void Execute(); void Code(ICompressProgressInfo *progress); }; /* SetBindInfo() for each coder AddCoder[2]() SetProgressIndex(UInt32 coderIndex); for each file { ReInit() for each coder SetCoderInfo Code } */ class CCoderMixer2MT: public ICompressCoder2, public CCoderMixer2, public CMyUnknownImp { CBindInfo _bindInfo; CObjectVector _streamBinders; unsigned _progressCoderIndex; void AddCoderCommon(); HRESULT Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams); HRESULT ReturnIfError(HRESULT code); public: CObjectVector _coders; MY_UNKNOWN_IMP STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); HRESULT SetBindInfo(const CBindInfo &bindInfo); void AddCoder(ICompressCoder *coder); void AddCoder2(ICompressCoder2 *coder); void SetProgressCoderIndex(unsigned coderIndex) { _progressCoderIndex = coderIndex; } void ReInit(); void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) { _coders[coderIndex].SetCoderInfo(inSizes, outSizes); } UInt64 GetWriteProcessedSize(UInt32 binderIndex) const { return _streamBinders[binderIndex].ProcessedSize; } }; } #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/Common.pri000066400000000000000000000020071325366651500225550ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2MT.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/DummyOutStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/HandlerOut.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/InStreamWithCRC.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/ItemNameUtils.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/MultiStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/OutStreamWithCRC.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/ParseProperties.h SOURCES += $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2MT.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/DummyOutStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/HandlerOut.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/InStreamWithCRC.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/ItemNameUtils.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/MultiStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp000066400000000000000000000006431325366651500242600ustar00rootroot00000000000000// DummyOutStream.cpp #include "StdAfx.h" #include "DummyOutStream.h" STDMETHODIMP CDummyOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize = size; HRESULT res = S_OK; if (_stream) res = _stream->Write(data, size, &realProcessedSize); _size += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return res; } src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h000066400000000000000000000011421325366651500237200ustar00rootroot00000000000000// DummyOutStream.h #ifndef __DUMMY_OUT_STREAM_H #define __DUMMY_OUT_STREAM_H #include "../../../Common/MyCom.h" #include "../../IStream.h" class CDummyOutStream: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; public: void SetStream(ISequentialOutStream *outStream) { _stream = outStream; } void ReleaseStream() { _stream.Release(); } void Init() { _size = 0; } MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); UInt64 GetSize() const { return _size; } }; #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp000066400000000000000000000064051325366651500233700ustar00rootroot00000000000000// HandlerOut.cpp #include "StdAfx.h" #ifndef _7ZIP_ST #include "../../../Windows/System.h" #endif #include "../Common/ParseProperties.h" #include "HandlerOut.h" using namespace NWindows; namespace NArchive { static void SetMethodProp32(COneMethodInfo &m, PROPID propID, UInt32 value) { if (m.FindProp(propID) < 0) m.AddProp32(propID, value); } void CMultiMethodProps::SetGlobalLevelAndThreads(COneMethodInfo &oneMethodInfo #ifndef _7ZIP_ST , UInt32 numThreads #endif ) { UInt32 level = _level; if (level != (UInt32)(Int32)-1) SetMethodProp32(oneMethodInfo, NCoderPropID::kLevel, (UInt32)level); #ifndef _7ZIP_ST SetMethodProp32(oneMethodInfo, NCoderPropID::kNumThreads, numThreads); #endif } void CMultiMethodProps::Init() { #ifndef _7ZIP_ST _numProcessors = _numThreads = NSystem::GetNumberOfProcessors(); #endif _level = (UInt32)(Int32)-1; _autoFilter = true; _crcSize = 4; _filterMethod.Clear(); _methods.Clear(); } HRESULT CMultiMethodProps::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) { UString name = nameSpec; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; if (name[0] == 'x') { name.Delete(0); _level = 9; return ParsePropToUInt32(name, value, _level); } if (name == L"crc") { name.Delete(0, 3); _crcSize = 4; return ParsePropToUInt32(name, value, _crcSize); } UInt32 number; int index = ParseStringToUInt32(name, number); UString realName = name.Ptr(index); if (index == 0) { if (name.IsPrefixedBy(L"mt")) { #ifndef _7ZIP_ST RINOK(ParseMtProp(name.Ptr(2), value, _numProcessors, _numThreads)); #endif return S_OK; } if (name.IsEqualTo("f")) { HRESULT res = PROPVARIANT_to_bool(value, _autoFilter); if (res == S_OK) return res; if (value.vt != VT_BSTR) return E_INVALIDARG; return _filterMethod.ParseMethodFromPROPVARIANT(L"", value); } number = 0; } if (number > 64) return E_FAIL; for (int j = _methods.Size(); j <= (int)number; j++) _methods.Add(COneMethodInfo()); return _methods[number].ParseMethodFromPROPVARIANT(realName, value); } void CSingleMethodProps::Init() { Clear(); #ifndef _7ZIP_ST _numProcessors = _numThreads = NWindows::NSystem::GetNumberOfProcessors(); AddNumThreadsProp(_numThreads); #endif _level = (UInt32)(Int32)-1; } HRESULT CSingleMethodProps::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { Init(); for (UInt32 i = 0; i < numProps; i++) { UString name = names[i]; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; const PROPVARIANT &value = values[i]; if (name[0] == L'x') { UInt32 a = 9; RINOK(ParsePropToUInt32(name.Ptr(1), value, a)); _level = a; AddLevelProp(a); } else if (name.IsPrefixedBy(L"mt")) { #ifndef _7ZIP_ST RINOK(ParseMtProp(name.Ptr(2), value, _numProcessors, _numThreads)); AddNumThreadsProp(_numThreads); #endif } else return ParseMethodFromPROPVARIANT(names[i], value); } return S_OK; } } src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h000066400000000000000000000024621325366651500230340ustar00rootroot00000000000000// HandlerOut.h #ifndef __HANDLER_OUT_H #define __HANDLER_OUT_H #include "../../Common/MethodProps.h" namespace NArchive { class CMultiMethodProps { UInt32 _level; public: #ifndef _7ZIP_ST UInt32 _numThreads; UInt32 _numProcessors; #endif UInt32 _crcSize; CObjectVector _methods; COneMethodInfo _filterMethod; bool _autoFilter; void SetGlobalLevelAndThreads(COneMethodInfo &oneMethodInfo #ifndef _7ZIP_ST , UInt32 numThreads #endif ); unsigned GetNumEmptyMethods() const { unsigned i; for (i = 0; i < _methods.Size(); i++) if (!_methods[i].IsEmpty()) break; return i; } int GetLevel() const { return _level == (UInt32)(Int32)-1 ? 5 : (int)_level; } void Init(); CMultiMethodProps() { Init(); } HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); }; class CSingleMethodProps: public COneMethodInfo { UInt32 _level; public: #ifndef _7ZIP_ST UInt32 _numThreads; UInt32 _numProcessors; #endif void Init(); CSingleMethodProps() { Init(); } int GetLevel() const { return _level == (UInt32)(Int32)-1 ? 5 : (int)_level; } HRESULT SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps); }; } #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp000066400000000000000000000023371325366651500242310ustar00rootroot00000000000000// InStreamWithCRC.cpp #include "StdAfx.h" #include "InStreamWithCRC.h" STDMETHODIMP CSequentialInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessed = 0; HRESULT result = S_OK; if (_stream) result = _stream->Read(data, size, &realProcessed); _size += realProcessed; if (size != 0 && realProcessed == 0) _wasFinished = true; _crc = CrcUpdate(_crc, data, realProcessed); if (processedSize) *processedSize = realProcessed; return result; } STDMETHODIMP CInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessed = 0; HRESULT result = S_OK; if (_stream) result = _stream->Read(data, size, &realProcessed); _size += realProcessed; /* if (size != 0 && realProcessed == 0) _wasFinished = true; */ _crc = CrcUpdate(_crc, data, realProcessed); if (processedSize) *processedSize = realProcessed; return result; } STDMETHODIMP CInStreamWithCRC::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin != STREAM_SEEK_SET || offset != 0) return E_FAIL; _size = 0; _crc = CRC_INIT_VAL; return _stream->Seek(offset, seekOrigin, newPosition); } src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h000066400000000000000000000031171325366651500236730ustar00rootroot00000000000000// InStreamWithCRC.h #ifndef __IN_STREAM_WITH_CRC_H #define __IN_STREAM_WITH_CRC_H #include "../../../../C/7zCrc.h" #include "../../../Common/MyCom.h" #include "../../IStream.h" class CSequentialInStreamWithCRC: public ISequentialInStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); private: CMyComPtr _stream; UInt64 _size; UInt32 _crc; bool _wasFinished; public: void SetStream(ISequentialInStream *stream) { _stream = stream; } void Init() { _size = 0; _wasFinished = false; _crc = CRC_INIT_VAL; } void ReleaseStream() { _stream.Release(); } UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } UInt64 GetSize() const { return _size; } bool WasFinished() const { return _wasFinished; } }; class CInStreamWithCRC: public IInStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP1(IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); private: CMyComPtr _stream; UInt64 _size; UInt32 _crc; // bool _wasFinished; public: void SetStream(IInStream *stream) { _stream = stream; } void Init() { _size = 0; // _wasFinished = false; _crc = CRC_INIT_VAL; } void ReleaseStream() { _stream.Release(); } UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } UInt64 GetSize() const { return _size; } // bool WasFinished() const { return _wasFinished; } }; #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp000066400000000000000000000033041325366651500240360ustar00rootroot00000000000000// Archive/Common/ItemNameUtils.cpp #include "StdAfx.h" #include "ItemNameUtils.h" namespace NArchive { namespace NItemName { static const wchar_t kOSDirDelimiter = WCHAR_PATH_SEPARATOR; static const wchar_t kDirDelimiter = L'/'; void ReplaceToOsPathSeparator(wchar_t *s) { #ifdef _WIN32 for (;;) { wchar_t c = *s; if (c == 0) break; if (c == kDirDelimiter) *s = kOSDirDelimiter; s++; } #endif } UString MakeLegalName(const UString &name) { UString zipName = name; zipName.Replace(kOSDirDelimiter, kDirDelimiter); return zipName; } UString GetOSName(const UString &name) { UString newName = name; newName.Replace(kDirDelimiter, kOSDirDelimiter); return newName; } UString GetOSName2(const UString &name) { if (name.IsEmpty()) return UString(); UString newName = GetOSName(name); if (newName.Back() == kOSDirDelimiter) newName.DeleteBack(); return newName; } void ConvertToOSName2(UString &name) { if (!name.IsEmpty()) { name.Replace(kDirDelimiter, kOSDirDelimiter); if (name.Back() == kOSDirDelimiter) name.DeleteBack(); } } bool HasTailSlash(const AString &name, UINT #if defined(_WIN32) && !defined(UNDER_CE) codePage #endif ) { if (name.IsEmpty()) return false; LPCSTR prev = #if defined(_WIN32) && !defined(UNDER_CE) CharPrevExA((WORD)codePage, name, &name[name.Len()], 0); #else (LPCSTR)(name) + (name.Len() - 1); #endif return (*prev == '/'); } #ifndef _WIN32 UString WinNameToOSName(const UString &name) { UString newName = name; newName.Replace(L'\\', kOSDirDelimiter); return newName; } #endif }} src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h000066400000000000000000000012221325366651500235000ustar00rootroot00000000000000// Archive/Common/ItemNameUtils.h #ifndef __ARCHIVE_ITEM_NAME_UTILS_H #define __ARCHIVE_ITEM_NAME_UTILS_H #include "../../../Common/MyString.h" namespace NArchive { namespace NItemName { void ReplaceToOsPathSeparator(wchar_t *s); UString MakeLegalName(const UString &name); UString GetOSName(const UString &name); UString GetOSName2(const UString &name); void ConvertToOSName2(UString &name); bool HasTailSlash(const AString &name, UINT codePage); #ifdef _WIN32 inline UString WinNameToOSName(const UString &name) { return name; } #else UString WinNameToOSName(const UString &name); #endif }} #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp000066400000000000000000000113311325366651500235630ustar00rootroot00000000000000// MultiStream.cpp #include "StdAfx.h" #include "MultiStream.h" STDMETHODIMP CMultiStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (_pos >= _totalLength) return S_OK; { unsigned left = 0, mid = _streamIndex, right = Streams.Size(); for (;;) { CSubStreamInfo &m = Streams[mid]; if (_pos < m.GlobalOffset) right = mid; else if (_pos >= m.GlobalOffset + m.Size) left = mid + 1; else { _streamIndex = mid; break; } mid = (left + right) / 2; } _streamIndex = mid; } CSubStreamInfo &s = Streams[_streamIndex]; UInt64 localPos = _pos - s.GlobalOffset; if (localPos != s.LocalPos) { RINOK(s.Stream->Seek(localPos, STREAM_SEEK_SET, &s.LocalPos)); } UInt64 rem = s.Size - localPos; if (size > rem) size = (UInt32)rem; HRESULT result = s.Stream->Read(data, size, &size); _pos += size; s.LocalPos += size; if (processedSize) *processedSize = size; return result; } STDMETHODIMP CMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _pos; break; case STREAM_SEEK_END: offset += _totalLength; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _pos = offset; if (newPosition) *newPosition = offset; return S_OK; } /* class COutVolumeStream: public ISequentialOutStream, public CMyUnknownImp { unsigned _volIndex; UInt64 _volSize; UInt64 _curPos; CMyComPtr _volumeStream; COutArchive _archive; CCRC _crc; public: MY_UNKNOWN_IMP CFileItem _file; CUpdateOptions _options; CMyComPtr VolumeCallback; void Init(IArchiveUpdateCallback2 *volumeCallback, const UString &name) { _file.Name = name; _file.IsStartPosDefined = true; _file.StartPos = 0; VolumeCallback = volumeCallback; _volIndex = 0; _volSize = 0; } HRESULT Flush(); STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; HRESULT COutVolumeStream::Flush() { if (_volumeStream) { _file.UnPackSize = _curPos; _file.FileCRC = _crc.GetDigest(); RINOK(WriteVolumeHeader(_archive, _file, _options)); _archive.Close(); _volumeStream.Release(); _file.StartPos += _file.UnPackSize; } return S_OK; } */ /* STDMETHODIMP COutMultiStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { if (_streamIndex >= Streams.Size()) { CSubStreamInfo subStream; RINOK(VolumeCallback->GetVolumeSize(Streams.Size(), &subStream.Size)); RINOK(VolumeCallback->GetVolumeStream(Streams.Size(), &subStream.Stream)); subStream.Pos = 0; Streams.Add(subStream); continue; } CSubStreamInfo &subStream = Streams[_streamIndex]; if (_offsetPos >= subStream.Size) { _offsetPos -= subStream.Size; _streamIndex++; continue; } if (_offsetPos != subStream.Pos) { CMyComPtr outStream; RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream)); RINOK(outStream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); subStream.Pos = _offsetPos; } UInt32 curSize = (UInt32)MyMin((UInt64)size, subStream.Size - subStream.Pos); UInt32 realProcessed; RINOK(subStream.Stream->Write(data, curSize, &realProcessed)); data = (void *)((Byte *)data + realProcessed); size -= realProcessed; subStream.Pos += realProcessed; _offsetPos += realProcessed; _absPos += realProcessed; if (_absPos > _length) _length = _absPos; if (processedSize != NULL) *processedSize += realProcessed; if (subStream.Pos == subStream.Size) { _streamIndex++; _offsetPos = 0; } if (realProcessed != curSize && realProcessed == 0) return E_FAIL; } return S_OK; } STDMETHODIMP COutMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _absPos; break; case STREAM_SEEK_END: offset += _length; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _absPos = offset; _offsetPos = _absPos; _streamIndex = 0; if (newPosition) *newPosition = offset; return S_OK; } */ src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h000066400000000000000000000035001325366651500232270ustar00rootroot00000000000000// MultiStream.h #ifndef __MULTI_STREAM_H #define __MULTI_STREAM_H #include "../../../Common/MyCom.h" #include "../../../Common/MyVector.h" #include "../../IStream.h" class CMultiStream: public IInStream, public CMyUnknownImp { UInt64 _pos; UInt64 _totalLength; unsigned _streamIndex; public: struct CSubStreamInfo { CMyComPtr Stream; UInt64 Size; UInt64 GlobalOffset; UInt64 LocalPos; CSubStreamInfo(): Size(0), GlobalOffset(0), LocalPos(0) {} }; CObjectVector Streams; HRESULT Init() { UInt64 total = 0; FOR_VECTOR (i, Streams) { CSubStreamInfo &s = Streams[i]; s.GlobalOffset = total; total += Streams[i].Size; RINOK(s.Stream->Seek(0, STREAM_SEEK_CUR, &s.LocalPos)); } _totalLength = total; _pos = 0; _streamIndex = 0; return S_OK; } MY_UNKNOWN_IMP1(IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; /* class COutMultiStream: public IOutStream, public CMyUnknownImp { unsigned _streamIndex; // required stream UInt64 _offsetPos; // offset from start of _streamIndex index UInt64 _absPos; UInt64 _length; struct CSubStreamInfo { CMyComPtr Stream; UInt64 Size; UInt64 Pos; }; CObjectVector Streams; public: CMyComPtr VolumeCallback; void Init() { _streamIndex = 0; _offsetPos = 0; _absPos = 0; _length = 0; } MY_UNKNOWN_IMP1(IOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; */ #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp000066400000000000000000000006531325366651500244310ustar00rootroot00000000000000// OutStreamWithCRC.cpp #include "StdAfx.h" #include "OutStreamWithCRC.h" STDMETHODIMP COutStreamWithCRC::Write(const void *data, UInt32 size, UInt32 *processedSize) { HRESULT result = S_OK; if (_stream) result = _stream->Write(data, size, &size); if (_calculate) _crc = CrcUpdate(_crc, data, size); _size += size; if (processedSize != NULL) *processedSize = size; return result; } src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h000066400000000000000000000016511325366651500240750ustar00rootroot00000000000000// OutStreamWithCRC.h #ifndef __OUT_STREAM_WITH_CRC_H #define __OUT_STREAM_WITH_CRC_H #include "../../../../C/7zCrc.h" #include "../../../Common/MyCom.h" #include "../../IStream.h" class COutStreamWithCRC: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; UInt32 _crc; bool _calculate; public: MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); void SetStream(ISequentialOutStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(bool calculate = true) { _size = 0; _calculate = calculate; _crc = CRC_INIT_VAL; } void EnableCalc(bool calculate) { _calculate = calculate; } void InitCRC() { _crc = CRC_INIT_VAL; } UInt64 GetSize() const { return _size; } UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } }; #endif src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h000066400000000000000000000001361325366651500241120ustar00rootroot00000000000000// ParseProperties.h #ifndef __PARSE_PROPERTIES_H #define __PARSE_PROPERTIES_H #endif src/libs/7zip/unix/CPP/7zip/Archive/IArchive.h000066400000000000000000000360171325366651500212340ustar00rootroot00000000000000// IArchive.h #ifndef __IARCHIVE_H #define __IARCHIVE_H #include "../IProgress.h" #include "../IStream.h" #include "../PropID.h" #define ARCHIVE_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 6, x) #define ARCHIVE_INTERFACE(i, x) ARCHIVE_INTERFACE_SUB(i, IUnknown, x) namespace NFileTimeType { enum EEnum { kWindows, kUnix, kDOS }; } namespace NArcInfoFlags { const UInt32 kKeepName = 1 << 0; // keep name of file in archive name const UInt32 kAltStreams = 1 << 1; // the handler supports alt streams const UInt32 kNtSecure = 1 << 2; // the handler supports NT security const UInt32 kFindSignature = 1 << 3; // the handler can find start of archive const UInt32 kMultiSignature = 1 << 4; // there are several signatures const UInt32 kUseGlobalOffset = 1 << 5; // the seek position of stream must be set as global offset const UInt32 kStartOpen = 1 << 6; // call handler for each start position const UInt32 kPureStartOpen = 1 << 7; // call handler only for start of file const UInt32 kBackwardOpen = 1 << 8; // archive can be open backward const UInt32 kPreArc = 1 << 9; // such archive can be stored before real archive (like SFX stub) const UInt32 kSymLinks = 1 << 10; // the handler supports symbolic links const UInt32 kHardLinks = 1 << 11; // the handler supports hard links } namespace NArchive { namespace NHandlerPropID { enum { kName = 0, // VT_BSTR kClassID, // binary GUID in VT_BSTR kExtension, // VT_BSTR kAddExtension, // VT_BSTR kUpdate, // VT_BOOL kKeepName, // VT_BOOL kSignature, // binary in VT_BSTR kMultiSignature, // binary in VT_BSTR kSignatureOffset, // VT_UI4 kAltStreams, // VT_BOOL kNtSecure, // VT_BOOL kFlags // VT_UI4 // kVersion // VT_UI4 ((VER_MAJOR << 8) | VER_MINOR) }; } namespace NExtract { namespace NAskMode { enum { kExtract = 0, kTest, kSkip }; } namespace NOperationResult { enum { kOK = 0, kUnsupportedMethod, kDataError, kCRCError, kUnavailable, kUnexpectedEnd, kDataAfterEnd, kIsNotArc, kHeadersError }; } } namespace NUpdate { namespace NOperationResult { enum { kOK = 0 , // kError }; } } } #define INTERFACE_IArchiveOpenCallback(x) \ STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes) x; \ STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes) x; \ ARCHIVE_INTERFACE(IArchiveOpenCallback, 0x10) { INTERFACE_IArchiveOpenCallback(PURE); }; /* IArchiveExtractCallback::GetStream Result: (*inStream == NULL) - for directories (*inStream == NULL) - if link (hard link or symbolic link) was created */ #define INTERFACE_IArchiveExtractCallback(x) \ INTERFACE_IProgress(x) \ STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) x; \ STDMETHOD(PrepareOperation)(Int32 askExtractMode) x; \ STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) x; \ ARCHIVE_INTERFACE_SUB(IArchiveExtractCallback, IProgress, 0x20) { INTERFACE_IArchiveExtractCallback(PURE) }; #define INTERFACE_IArchiveOpenVolumeCallback(x) \ STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) x; \ STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) x; \ ARCHIVE_INTERFACE(IArchiveOpenVolumeCallback, 0x30) { INTERFACE_IArchiveOpenVolumeCallback(PURE); }; ARCHIVE_INTERFACE(IInArchiveGetStream, 0x40) { STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream) PURE; }; ARCHIVE_INTERFACE(IArchiveOpenSetSubArchiveName, 0x50) { STDMETHOD(SetSubArchiveName)(const wchar_t *name) PURE; }; /* IInArchive::Open stream if (kUseGlobalOffset), stream current position can be non 0. if (!kUseGlobalOffset), stream current position is 0. if (maxCheckStartPosition == NULL), the handler can try to search archive start in stream if (*maxCheckStartPosition == 0), the handler must check only current position as archive start IInArchive::Extract: indices must be sorted numItems = (UInt32)(Int32)-1 = 0xFFFFFFFF means "all files" testMode != 0 means "test files without writing to outStream" IInArchive::GetArchiveProperty: kpidOffset - start offset of archive. VT_EMPTY : means offset = 0. VT_UI4, VT_UI8, VT_I8 : result offset; negative values is allowed kpidPhySize - size of archive. VT_EMPTY means unknown size. kpidPhySize is allowed to be larger than file size. In that case it must show supposed size. kpidIsDeleted: kpidIsAltStream: kpidIsAux: kpidINode: must return VARIANT_TRUE (VT_BOOL), if archive can support that property in GetProperty. Notes: Don't call IInArchive functions for same IInArchive object from different threads simultaneously. Some IInArchive handlers will work incorrectly in that case. */ /* MSVC allows the code where there is throw() in declaration of function, but there is no throw() in definition of function. */ #ifdef _MSC_VER #define MY_NO_THROW_DECL_ONLY throw() #else #define MY_NO_THROW_DECL_ONLY #endif #define INTERFACE_IInArchive(x) \ STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(Close)() MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetNumberOfItems)(UInt32 *numItems) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetNumberOfProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetPropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetArchivePropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; ARCHIVE_INTERFACE(IInArchive, 0x60) { INTERFACE_IInArchive(PURE) }; namespace NParentType { enum { kDir = 0, kAltStream }; }; namespace NPropDataType { const UInt32 kMask_ZeroEnd = 1 << 4; // const UInt32 kMask_BigEndian = 1 << 5; const UInt32 kMask_Utf = 1 << 6; // const UInt32 kMask_Utf8 = kMask_Utf | 0; const UInt32 kMask_Utf16 = kMask_Utf | 1; // const UInt32 kMask_Utf32 = kMask_Utf | 2; const UInt32 kNotDefined = 0; const UInt32 kRaw = 1; const UInt32 kUtf16z = kMask_Utf16 | kMask_ZeroEnd; }; // UTF string (pointer to wchar_t) with zero end and little-endian. #define PROP_DATA_TYPE_wchar_t_PTR_Z_LE ((NPropDataType::kMask_Utf | NPropDataType::kMask_ZeroEnd) + (sizeof(wchar_t) >> 1)) /* GetRawProp: Result: S_OK - even if property is not set */ #define INTERFACE_IArchiveGetRawProps(x) \ STDMETHOD(GetParent)(UInt32 index, UInt32 *parent, UInt32 *parentType) x; \ STDMETHOD(GetRawProp)(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) x; \ STDMETHOD(GetNumRawProps)(UInt32 *numProps) x; \ STDMETHOD(GetRawPropInfo)(UInt32 index, BSTR *name, PROPID *propID) x; ARCHIVE_INTERFACE(IArchiveGetRawProps, 0x70) { INTERFACE_IArchiveGetRawProps(PURE) }; #define INTERFACE_IArchiveGetRootProps(x) \ STDMETHOD(GetRootProp)(PROPID propID, PROPVARIANT *value) x; \ STDMETHOD(GetRootRawProp)(PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) x; \ ARCHIVE_INTERFACE(IArchiveGetRootProps, 0x71) { INTERFACE_IArchiveGetRootProps(PURE) }; ARCHIVE_INTERFACE(IArchiveOpenSeq, 0x61) { STDMETHOD(OpenSeq)(ISequentialInStream *stream) PURE; }; /* OpenForSize Result: S_FALSE - is not archive ? - DATA error */ /* const UInt32 kOpenFlags_RealPhySize = 1 << 0; const UInt32 kOpenFlags_NoSeek = 1 << 1; // const UInt32 kOpenFlags_BeforeExtract = 1 << 2; */ /* Flags: 0 - opens archive with IInStream, if IInStream interface is supported - if phySize is not available, it doesn't try to make full parse to get phySize kOpenFlags_NoSeek - ArcOpen2 function doesn't use IInStream interface, even if it's available kOpenFlags_RealPhySize - the handler will try to get PhySize, even if it requires full decompression for file if handler is not allowed to use IInStream and the flag kOpenFlags_RealPhySize is not specified, the handler can return S_OK, but it doesn't check even Signature. So next Extract can be called for that sequential stream. */ /* ARCHIVE_INTERFACE(IArchiveOpen2, 0x62) { STDMETHOD(ArcOpen2)(ISequentialInStream *stream, UInt32 flags, IArchiveOpenCallback *openCallback) PURE; }; */ // ---------- UPDATE ---------- /* GetUpdateItemInfo outs: *newData *newProps 0 0 - Copy data and properties from archive 0 1 - Copy data from archive, request new properties 1 0 - that combination is unused now 1 1 - Request new data and new properties. It can be used even for folders indexInArchive = -1 if there is no item in archive, or if it doesn't matter. GetStream out: Result: S_OK: (*inStream == NULL) - only for directories - the bug was fixed in 9.33: (*Stream == NULL) was in case of anti-file (*inStream != NULL) - for any file, even for empty file or anti-file S_FALSE - skip that file (don't add item to archive) - (client code can't open stream of that file by some reason) (*inStream == NULL) The order of calling for hard links: - GetStream() - GetProperty(kpidHardLink) */ #define INTERFACE_IArchiveUpdateCallback(x) \ INTERFACE_IProgress(x); \ STDMETHOD(GetUpdateItemInfo)(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive) x; \ STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) x; \ STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream) x; \ STDMETHOD(SetOperationResult)(Int32 operationResult) x; \ ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback, IProgress, 0x80) { INTERFACE_IArchiveUpdateCallback(PURE); }; #define INTERFACE_IArchiveUpdateCallback2(x) \ INTERFACE_IArchiveUpdateCallback(x) \ STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size) x; \ STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream) x; \ ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback2, IArchiveUpdateCallback, 0x82) { INTERFACE_IArchiveUpdateCallback2(PURE); }; /* UpdateItems() ------------- outStream: output stream. (the handler) MUST support the case when Seek position in outStream is not ZERO. but the caller calls with empty outStream and seek position is ZERO?? archives with stub: If archive is open and the handler and (Offset > 0), then the handler knows about stub size. UpdateItems(): 1) the handler MUST copy that stub to outStream 2) the caller MUST NOT copy the stub to outStream, if "rsfx" property is set with SetProperties the handler must support the case where ISequentialOutStream *outStream */ #define INTERFACE_IOutArchive(x) \ STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) x; \ STDMETHOD(GetFileTimeType)(UInt32 *type) x; ARCHIVE_INTERFACE(IOutArchive, 0xA0) { INTERFACE_IOutArchive(PURE) }; ARCHIVE_INTERFACE(ISetProperties, 0x03) { STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) PURE; }; ARCHIVE_INTERFACE(IArchiveKeepModeForNextOpen, 0x04) { STDMETHOD(KeepModeForNextOpen)() PURE; }; /* Exe handler: the handler for executable format (PE, ELF, Mach-O). SFX archive: executable stub + some tail data. before 9.31: exe handler didn't parse SFX archives as executable format. for 9.31+: exe handler parses SFX archives as executable format, only if AllowTail(1) was called */ ARCHIVE_INTERFACE(IArchiveAllowTail, 0x05) { STDMETHOD(AllowTail)(Int32 allowTail) PURE; }; #define IMP_IInArchive_GetProp(k) \ (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ { if (index >= ARRAY_SIZE(k)) return E_INVALIDARG; \ *propID = k[index]; *varType = k7z_PROPID_To_VARTYPE[(unsigned)*propID]; *name = 0; return S_OK; } \ #define IMP_IInArchive_GetProp_WITH_NAME(k) \ (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ { if (index >= ARRAY_SIZE(k)) return E_INVALIDARG; \ const STATPROPSTG &srcItem = k[index]; \ *propID = srcItem.propid; *varType = srcItem.vt; \ if (srcItem.lpwstrName == 0) *name = 0; else *name = ::SysAllocString(srcItem.lpwstrName); return S_OK; } \ #define IMP_IInArchive_Props \ STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kProps); return S_OK; } \ STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp(kProps) #define IMP_IInArchive_Props_WITH_NAME \ STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kProps); return S_OK; } \ STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kProps) #define IMP_IInArchive_ArcProps \ STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kArcProps); return S_OK; } \ STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp(kArcProps) #define IMP_IInArchive_ArcProps_WITH_NAME \ STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kArcProps); return S_OK; } \ STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kArcProps) #define IMP_IInArchive_ArcProps_NO_Table \ STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProps) \ { *numProps = 0; return S_OK; } \ STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32, BSTR *, PROPID *, VARTYPE *) \ { return E_NOTIMPL; } \ #define IMP_IInArchive_ArcProps_NO \ IMP_IInArchive_ArcProps_NO_Table \ STDMETHODIMP CHandler::GetArchiveProperty(PROPID, PROPVARIANT *value) \ { value->vt = VT_EMPTY; return S_OK; } #define k_IsArc_Res_NO 0 #define k_IsArc_Res_YES 1 #define k_IsArc_Res_NEED_MORE 2 // #define k_IsArc_Res_YES_LOW_PROB 3 #define API_FUNC_IsArc EXTERN_C UInt32 WINAPI #define API_FUNC_static_IsArc extern "C" { static UInt32 WINAPI extern "C" { typedef UInt32 (*Func_IsArc)(const Byte *p, size_t size); typedef IOutArchive * (*Func_CreateOutArchive)(); typedef IInArchive * (*Func_CreateInArchive)(); } #endif src/libs/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp000066400000000000000000000344631325366651500223010ustar00rootroot00000000000000// LzmaHandler.cpp #include "StdAfx.h" #include "../../../C/CpuArch.h" #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" #include "../../Windows/PropVariant.h" #include "../Common/CreateCoder.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "../Compress/LzmaDecoder.h" #include "Common/DummyOutStream.h" using namespace NWindows; namespace NArchive { namespace NLzma { static bool CheckDicSize(const Byte *p) { UInt32 dicSize = GetUi32(p); if (dicSize == 1) return true; for (unsigned i = 0; i <= 30; i++) if (dicSize == ((UInt32)2 << i) || dicSize == ((UInt32)3 << i)) return true; return (dicSize == 0xFFFFFFFF); } static const Byte kProps[] = { kpidSize, kpidPackSize, kpidMethod }; static const Byte kArcProps[] = { kpidNumStreams }; struct CHeader { UInt64 Size; Byte FilterID; Byte LzmaProps[5]; UInt32 GetDicSize() const { return GetUi32(LzmaProps + 1); } bool HasSize() const { return (Size != (UInt64)(Int64)-1); } bool Parse(const Byte *buf, bool isThereFilter); }; bool CHeader::Parse(const Byte *buf, bool isThereFilter) { FilterID = 0; if (isThereFilter) FilterID = buf[0]; const Byte *sig = buf + (isThereFilter ? 1 : 0); for (int i = 0; i < 5; i++) LzmaProps[i] = sig[i]; Size = GetUi64(sig + 5); return LzmaProps[0] < 5 * 5 * 9 && FilterID < 2 && (!HasSize() || Size < ((UInt64)1 << 56)) && CheckDicSize(LzmaProps + 1); } class CDecoder { CMyComPtr _lzmaDecoder; CMyComPtr _bcjStream; public: NCompress::NLzma::CDecoder *_lzmaDecoderSpec; ~CDecoder(); HRESULT Create(DECL_EXTERNAL_CODECS_LOC_VARS bool filtered, ISequentialInStream *inStream); HRESULT Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress); UInt64 GetInputProcessedSize() const { return _lzmaDecoderSpec->GetInputProcessedSize(); } void ReleaseInStream() { if (_lzmaDecoder) _lzmaDecoderSpec->ReleaseInStream(); } HRESULT ReadInput(Byte *data, UInt32 size, UInt32 *processedSize) { return _lzmaDecoderSpec->ReadFromInputStream(data, size, processedSize); } }; static const UInt32 k_BCJ = 0x03030103; HRESULT CDecoder::Create( DECL_EXTERNAL_CODECS_LOC_VARS bool filteredMode, ISequentialInStream *inStream) { if (!_lzmaDecoder) { _lzmaDecoderSpec = new NCompress::NLzma::CDecoder; _lzmaDecoderSpec->FinishStream = true; _lzmaDecoder = _lzmaDecoderSpec; } if (filteredMode) { if (!_bcjStream) { CMyComPtr coder; RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS k_BCJ, coder, false)); if (!coder) return E_NOTIMPL; coder.QueryInterface(IID_ISequentialOutStream, &_bcjStream); if (!_bcjStream) return E_NOTIMPL; } } return _lzmaDecoderSpec->SetInStream(inStream); } CDecoder::~CDecoder() { ReleaseInStream(); } HRESULT CDecoder::Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { if (header.FilterID > 1) return E_NOTIMPL; { CMyComPtr setDecoderProperties; _lzmaDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); if (!setDecoderProperties) return E_NOTIMPL; RINOK(setDecoderProperties->SetDecoderProperties2(header.LzmaProps, 5)); } CMyComPtr setOutStream; bool filteredMode = (header.FilterID == 1); if (filteredMode) { _bcjStream.QueryInterface(IID_ICompressSetOutStream, &setOutStream); if (!setOutStream) return E_NOTIMPL; RINOK(setOutStream->SetOutStream(outStream)); outStream = _bcjStream; } const UInt64 *Size = header.HasSize() ? &header.Size : NULL; HRESULT res = _lzmaDecoderSpec->CodeResume(outStream, Size, progress); if (filteredMode) { CMyComPtr flush; _bcjStream.QueryInterface(IID_IOutStreamFlush, &flush); if (flush) { HRESULT res2 = flush->Flush(); if (res == S_OK) res = res2; } HRESULT res2 = setOutStream->ReleaseOutStream(); if (res == S_OK) res = res2; } RINOK(res); if (header.HasSize()) if (_lzmaDecoderSpec->GetOutputProcessedSize() != header.Size) return S_FALSE; return S_OK; } class CHandler: public IInArchive, public IArchiveOpenSeq, PUBLIC_ISetCompressCodecsInfo public CMyUnknownImp { CHeader _header; bool _lzma86; CMyComPtr _stream; CMyComPtr _seqStream; bool _isArc; bool _needSeekToStart; bool _dataAfterEnd; bool _needMoreInput; bool _packSize_Defined; bool _unpackSize_Defined; bool _numStreams_Defined; bool _unsupported; bool _dataError; UInt64 _packSize; UInt64 _unpackSize; UInt64 _numStreams; DECL_EXTERNAL_CODECS_VARS DECL_ISetCompressCodecsInfo public: MY_QUERYINTERFACE_BEGIN2(IInArchive) MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_END MY_ADDREF_RELEASE INTERFACE_IInArchive(;) STDMETHOD(OpenSeq)(ISequentialInStream *stream); CHandler(bool lzma86) { _lzma86 = lzma86; } unsigned GetHeaderSize() const { return 5 + 8 + (_lzma86 ? 1 : 0); } }; IMP_IInArchive_Props IMP_IInArchive_ArcProps STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidPhySize: if (_packSize_Defined) prop = _packSize; break; case kpidNumStreams: if (_numStreams_Defined) prop = _numStreams; break; case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break; case kpidErrorFlags: { UInt32 v = 0; if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd; if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; if (_dataError) v |= kpv_ErrorFlags_DataError; prop = v; } } prop.Detach(value); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = 1; return S_OK; } static void DictSizeToString(UInt32 value, char *s) { for (int i = 0; i <= 31; i++) if (((UInt32)1 << i) == value) { ::ConvertUInt32ToString(i, s); return; } char c = 'b'; if ((value & ((1 << 20) - 1)) == 0) { value >>= 20; c = 'm'; } else if ((value & ((1 << 10) - 1)) == 0) { value >>= 10; c = 'k'; } ::ConvertUInt32ToString(value, s); s += MyStringLen(s); *s++ = c; *s = 0; } STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidSize: if (_stream && _header.HasSize()) prop = _header.Size; break; case kpidPackSize: if (_packSize_Defined) prop = _packSize; break; case kpidMethod: if (_stream) { char sz[64]; char *s = sz; if (_header.FilterID != 0) s = MyStpCpy(s, "BCJ "); s = MyStpCpy(s, "LZMA:"); DictSizeToString(_header.GetDicSize(), s); prop = sz; } break; } prop.Detach(value); return S_OK; } API_FUNC_static_IsArc IsArc_Lzma(const Byte *p, size_t size) { const UInt32 kHeaderSize = 1 + 4 + 8; if (size < kHeaderSize) return k_IsArc_Res_NEED_MORE; if (p[0] >= 5 * 5 * 9) return k_IsArc_Res_NO; UInt64 unpackSize = GetUi64(p + 1 + 4); if (unpackSize != (UInt64)(Int64)-1) { if (size >= ((UInt64)1 << 56)) return k_IsArc_Res_NO; } if (unpackSize != 0) { if (size < kHeaderSize + 2) return k_IsArc_Res_NEED_MORE; if (p[kHeaderSize] != 0) return k_IsArc_Res_NO; if (unpackSize != (UInt64)(Int64)-1) { if ((p[kHeaderSize + 1] & 0x80) != 0) return k_IsArc_Res_NO; } } if (!CheckDicSize(p + 1)) // return k_IsArc_Res_YES_LOW_PROB; return k_IsArc_Res_NO; return k_IsArc_Res_YES; } } API_FUNC_static_IsArc IsArc_Lzma86(const Byte *p, size_t size) { if (size < 1) return k_IsArc_Res_NEED_MORE; Byte filterID = p[0]; if (filterID != 0 && filterID != 1) return k_IsArc_Res_NO; return IsArc_Lzma(p + 1, size - 1); } } STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *) { Close(); const UInt32 kBufSize = 1 + 5 + 8 + 2; Byte buf[kBufSize]; RINOK(ReadStream_FALSE(inStream, buf, kBufSize)); if (!_header.Parse(buf, _lzma86)) return S_FALSE; const Byte *start = buf + GetHeaderSize(); if (start[0] != 0 /* || (start[1] & 0x80) != 0 */ ) // empty stream with EOS is not 0x80 return S_FALSE; RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize)); if (_packSize >= 24 && _header.Size == 0 && _header.FilterID == 0 && _header.LzmaProps[0] == 0) return S_FALSE; _isArc = true; _stream = inStream; _seqStream = inStream; _needSeekToStart = true; return S_OK; } STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) { Close(); _isArc = true; _seqStream = stream; return S_OK; } STDMETHODIMP CHandler::Close() { _isArc = false; _packSize_Defined = false; _unpackSize_Defined = false; _numStreams_Defined = false; _dataAfterEnd = false; _needMoreInput = false; _unsupported = false; _dataError = false; _packSize = 0; _needSeekToStart = false; _stream.Release(); _seqStream.Release(); return S_OK; } class CCompressProgressInfoImp: public ICompressProgressInfo, public CMyUnknownImp { CMyComPtr Callback; public: UInt64 Offset; MY_UNKNOWN_IMP1(ICompressProgressInfo) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); void Init(IArchiveOpenCallback *callback) { Callback = callback; } }; STDMETHODIMP CCompressProgressInfoImp::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */) { if (Callback) { UInt64 files = 0; UInt64 value = Offset + *inSize; return Callback->SetCompleted(&files, &value); } return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN if (numItems == 0) return S_OK; if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; if (_packSize_Defined) extractCallback->SetTotal(_packSize); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); if (!testMode && !realOutStream) return S_OK; extractCallback->PrepareOperation(askMode); CDummyOutStream *outStreamSpec = new CDummyOutStream; CMyComPtr outStream(outStreamSpec); outStreamSpec->SetStream(realOutStream); outStreamSpec->Init(); realOutStream.Release(); CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, true); if (_needSeekToStart) { if (!_stream) return E_FAIL; RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); } else _needSeekToStart = true; CDecoder decoder; HRESULT result = decoder.Create( EXTERNAL_CODECS_VARS _lzma86, _seqStream); RINOK(result); bool firstItem = true; UInt64 packSize = 0; UInt64 unpackSize = 0; UInt64 numStreams = 0; bool dataAfterEnd = false; for (;;) { lps->InSize = packSize; lps->OutSize = unpackSize; RINOK(lps->SetCur()); const UInt32 kBufSize = 1 + 5 + 8; Byte buf[kBufSize]; const UInt32 headerSize = GetHeaderSize(); UInt32 processed; RINOK(decoder.ReadInput(buf, headerSize, &processed)); if (processed != headerSize) { if (processed != 0) dataAfterEnd = true; break; } CHeader st; if (!st.Parse(buf, _lzma86)) { dataAfterEnd = true; break; } numStreams++; firstItem = false; result = decoder.Code(st, outStream, progress); packSize = decoder.GetInputProcessedSize(); unpackSize = outStreamSpec->GetSize(); if (result == E_NOTIMPL) { _unsupported = true; result = S_FALSE; break; } if (result == S_FALSE) break; RINOK(result); } if (firstItem) { _isArc = false; result = S_FALSE; } else if (result == S_OK || result == S_FALSE) { if (dataAfterEnd) _dataAfterEnd = true; else if (decoder._lzmaDecoderSpec->NeedMoreInput) _needMoreInput = true; _packSize = packSize; _unpackSize = unpackSize; _numStreams = numStreams; _packSize_Defined = true; _unpackSize_Defined = true; _numStreams_Defined = true; } Int32 opResult = NExtract::NOperationResult::kOK; if (!_isArc) opResult = NExtract::NOperationResult::kIsNotArc; else if (_needMoreInput) opResult = NExtract::NOperationResult::kUnexpectedEnd; else if (_unsupported) opResult = NExtract::NOperationResult::kUnsupportedMethod; else if (_dataAfterEnd) opResult = NExtract::NOperationResult::kDataAfterEnd; else if (result == S_FALSE) opResult = NExtract::NOperationResult::kDataError; else if (result == S_OK) opResult = NExtract::NOperationResult::kOK; else return result; outStream.Release(); return extractCallback->SetOperationResult(opResult); COM_TRY_END } IMPL_ISetCompressCodecsInfo namespace NLzmaAr { IMP_CreateArcIn_2(CHandler(false)) static CArcInfo g_ArcInfo = { "lzma", "lzma", 0, 0xA, 0, { 0 }, // 2, { 0x5D, 0x00 }, 0, NArcInfoFlags::kStartOpen | NArcInfoFlags::kKeepName, CreateArc, NULL, IsArc_Lzma }; REGISTER_ARC(Lzma) } namespace NLzma86Ar { IMP_CreateArcIn_2(CHandler(true)) static CArcInfo g_ArcInfo = { "lzma86", "lzma86", 0, 0xB, 0, { 0 }, 0, NArcInfoFlags::kKeepName, CreateArc, NULL, IsArc_Lzma86 }; REGISTER_ARC(Lzma86) } }} src/libs/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp000066400000000000000000000211721325366651500224620ustar00rootroot00000000000000// SplitHandler.cpp #include "StdAfx.h" #include "../../Common/ComTry.h" #include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Compress/CopyCoder.h" #include "Common/MultiStream.h" using namespace NWindows; namespace NArchive { namespace NSplit { static const Byte kProps[] = { kpidPath, kpidSize }; static const Byte kArcProps[] = { kpidNumVolumes, kpidTotalPhySize }; class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { CObjectVector > _streams; CRecordVector _sizes; UString _subName; UInt64 _totalSize; HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); public: MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); }; IMP_IInArchive_Props IMP_IInArchive_ArcProps STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidMainSubfile: prop = (UInt32)0; break; case kpidPhySize: if (!_sizes.IsEmpty()) prop = _sizes[0]; break; case kpidTotalPhySize: prop = _totalSize; break; case kpidNumVolumes: prop = (UInt32)_streams.Size(); break; } prop.Detach(value); return S_OK; } struct CSeqName { UString _unchangedPart; UString _changedPart; bool _splitStyle; UString GetNextName() { UString newName; if (_splitStyle) { int i; int numLetters = _changedPart.Len(); for (i = numLetters - 1; i >= 0; i--) { wchar_t c = _changedPart[i]; if (c == 'z') { newName.InsertAtFront('a'); continue; } else if (c == 'Z') { newName.InsertAtFront('A'); continue; } c++; if ((c == 'z' || c == 'Z') && i == 0) { _unchangedPart += c; wchar_t newChar = (c == 'z') ? L'a' : L'A'; newName.Empty(); numLetters++; for (int k = 0; k < numLetters; k++) newName += newChar; break; } newName.InsertAtFront(c); i--; for (; i >= 0; i--) newName.InsertAtFront(_changedPart[i]); break; } } else { int i; int numLetters = _changedPart.Len(); for (i = numLetters - 1; i >= 0; i--) { wchar_t c = _changedPart[i]; if (c == '9') { newName.InsertAtFront('0'); if (i == 0) newName.InsertAtFront('1'); continue; } c++; newName.InsertAtFront(c); i--; for (; i >= 0; i--) newName.InsertAtFront(_changedPart[i]); break; } } _changedPart = newName; return _unchangedPart + _changedPart; } }; HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) { Close(); if (!callback) return S_FALSE; CMyComPtr volumeCallback; callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback); if (!volumeCallback) return S_FALSE; UString name; { NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidName, &prop)); if (prop.vt != VT_BSTR) return S_FALSE; name = prop.bstrVal; } int dotPos = name.ReverseFind('.'); const UString prefix = name.Left(dotPos + 1); const UString ext = name.Ptr(dotPos + 1); UString ext2 = ext; ext2.MakeLower_Ascii(); CSeqName seqName; unsigned numLetters = 2; bool splitStyle = false; if (ext2.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "aa")) { splitStyle = true; while (numLetters < ext2.Len()) { if (ext2[ext2.Len() - numLetters - 1] != 'a') break; numLetters++; } } else if (ext.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "01")) { while (numLetters < ext2.Len()) { if (ext2[ext2.Len() - numLetters - 1] != '0') break; numLetters++; } if (numLetters != ext.Len()) return S_FALSE; } else return S_FALSE; seqName._unchangedPart = prefix + ext.Left(ext2.Len() - numLetters); seqName._changedPart = ext.RightPtr(numLetters); seqName._splitStyle = splitStyle; if (prefix.Len() < 1) _subName = L"file"; else _subName.SetFrom(prefix, prefix.Len() - 1); UInt64 size; { NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; } _totalSize += size; _sizes.Add(size); _streams.Add(stream); { UInt64 numFiles = _streams.Size(); RINOK(callback->SetCompleted(&numFiles, NULL)); } for (;;) { const UString fullName = seqName.GetNextName(); CMyComPtr nextStream; HRESULT result = volumeCallback->GetStream(fullName, &nextStream); if (result == S_FALSE) break; if (result != S_OK) return result; if (!stream) break; { NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; } _totalSize += size; _sizes.Add(size); _streams.Add(nextStream); { UInt64 numFiles = _streams.Size(); RINOK(callback->SetCompleted(&numFiles, NULL)); } } if (_streams.Size() == 1) { if (splitStyle) return S_FALSE; } return S_OK; } STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) { COM_TRY_BEGIN HRESULT res = Open2(stream, callback); if (res != S_OK) Close(); return res; COM_TRY_END } STDMETHODIMP CHandler::Close() { _totalSize = 0; _subName.Empty(); _streams.Clear(); _sizes.Clear(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _streams.IsEmpty() ? 0 : 1; return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidPath: prop = _subName; break; case kpidSize: case kpidPackSize: prop = _totalSize; break; } prop.Detach(value); return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN if (numItems == 0) return S_OK; if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; UInt64 currentTotalSize = 0; RINOK(extractCallback->SetTotal(_totalSize)); CMyComPtr outStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(0, &outStream, askMode)); if (!testMode && !outStream) return S_OK; RINOK(extractCallback->PrepareOperation(askMode)); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr copyCoder = copyCoderSpec; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); FOR_VECTOR (i, _streams) { lps->InSize = lps->OutSize = currentTotalSize; RINOK(lps->SetCur()); IInStream *inStream = _streams[i]; RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); currentTotalSize += copyCoderSpec->TotalSize; } outStream.Release(); return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK); COM_TRY_END } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN if (index != 0) return E_INVALIDARG; *stream = 0; CMultiStream *streamSpec = new CMultiStream; CMyComPtr streamTemp = streamSpec; FOR_VECTOR (i, _streams) { CMultiStream::CSubStreamInfo subStreamInfo; subStreamInfo.Stream = _streams[i]; subStreamInfo.Size = _sizes[i]; streamSpec->Streams.Add(subStreamInfo); } streamSpec->Init(); *stream = streamTemp.Detach(); return S_OK; COM_TRY_END } IMP_CreateArcIn static CArcInfo g_ArcInfo = { "Split", "001", 0, 0xEA, 0, { 0 }, 0, 0, CreateArc }; REGISTER_ARC(Split) }} src/libs/7zip/unix/CPP/7zip/Archive/XzHandler.cpp000066400000000000000000000554231325366651500217760ustar00rootroot00000000000000// XzHandler.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../../../C/XzCrc64.h" #include "../../../C/XzEnc.h" #include "../../Common/ComTry.h" #include "../../Common/Defs.h" #include "../../Common/IntToString.h" #include "../ICoder.h" #include "../Common/CWrappers.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "../Compress/CopyCoder.h" #include "IArchive.h" #include "Common/HandlerOut.h" using namespace NWindows; namespace NCompress { namespace NLzma2 { HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props); }} static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } static void SzFree(void *, void *address) { MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; namespace NArchive { namespace NXz { struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit; static const wchar_t *k_LZMA2_Name = L"LZMA2"; struct CStatInfo { UInt64 InSize; UInt64 OutSize; UInt64 PhySize; UInt64 NumStreams; UInt64 NumBlocks; bool UnpackSize_Defined; bool NumStreams_Defined; bool NumBlocks_Defined; bool IsArc; bool UnexpectedEnd; bool DataAfterEnd; bool Unsupported; bool HeadersError; bool DataError; bool CrcError; CStatInfo() { Clear(); } void Clear() { InSize = 0; OutSize = 0; PhySize = 0; NumStreams = 0; NumBlocks = 0; UnpackSize_Defined = false; NumStreams_Defined = false; NumBlocks_Defined = false; UnexpectedEnd = false; DataAfterEnd = false; Unsupported = false; HeadersError = false; DataError = false; CrcError = false; IsArc = false; } }; struct IDecodeState: public CStatInfo { SRes DecodeRes; IDecodeState(): DecodeRes(SZ_OK) {} virtual HRESULT Progress() = 0; HRESULT Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream); }; struct CVirtProgress_To_LocalProgress: public IDecodeState { CLocalProgress *lps; CMyComPtr progress; HRESULT Progress(); }; HRESULT CVirtProgress_To_LocalProgress::Progress() { lps->InSize = InSize; lps->OutSize = OutSize; return lps->SetCur(); } class CHandler: public IInArchive, public IArchiveOpenSeq, #ifndef EXTRACT_ONLY public IOutArchive, public ISetProperties, public CMultiMethodProps, #endif public CMyUnknownImp { CStatInfo _stat; bool _isArc; bool _needSeekToStart; bool _phySize_Defined; CMyComPtr _stream; CMyComPtr _seqStream; UInt32 _filterId; AString _methodsString; void Init() { _filterId = 0; CMultiMethodProps::Init(); } HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback); HRESULT Decode2(ISequentialInStream *seqInStream, ISequentialOutStream *outStream, IDecodeState &progress) { RINOK(progress.Decode(seqInStream, outStream)); _stat = progress; _phySize_Defined = true; return S_OK; } public: MY_QUERYINTERFACE_BEGIN2(IInArchive) MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) #ifndef EXTRACT_ONLY MY_QUERYINTERFACE_ENTRY(IOutArchive) MY_QUERYINTERFACE_ENTRY(ISetProperties) #endif MY_QUERYINTERFACE_END MY_ADDREF_RELEASE INTERFACE_IInArchive(;) STDMETHOD(OpenSeq)(ISequentialInStream *stream); #ifndef EXTRACT_ONLY INTERFACE_IOutArchive(;) STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps); #endif CHandler(); }; CHandler::CHandler() { Init(); } static const Byte kProps[] = { kpidSize, kpidPackSize, kpidMethod }; static const Byte kArcProps[] = { kpidMethod, kpidNumStreams, kpidNumBlocks }; IMP_IInArchive_Props IMP_IInArchive_ArcProps static inline char GetHex(unsigned value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } static inline void AddHexToString(AString &s, Byte value) { s += GetHex(value >> 4); s += GetHex(value & 0xF); } static void AddUInt32ToString(AString &s, UInt32 value) { char temp[16]; ConvertUInt32ToString(value, temp); s += temp; } static void Lzma2PropToString(AString &s, unsigned prop) { char c = 0; UInt32 size; if ((prop & 1) == 0) size = prop / 2 + 12; else { c = 'k'; size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1); if (prop > 17) { size >>= 10; c = 'm'; } } AddUInt32ToString(s, size); if (c != 0) s += c; } struct CMethodNamePair { UInt32 Id; const char *Name; }; static const CMethodNamePair g_NamePairs[] = { { XZ_ID_Subblock, "SB" }, { XZ_ID_Delta, "Delta" }, { XZ_ID_X86, "BCJ" }, { XZ_ID_PPC, "PPC" }, { XZ_ID_IA64, "IA64" }, { XZ_ID_ARM, "ARM" }, { XZ_ID_ARMT, "ARMT" }, { XZ_ID_SPARC, "SPARC" }, { XZ_ID_LZMA2, "LZMA2" } }; static AString GetMethodString(const CXzFilter &f) { const char *p = NULL; for (unsigned i = 0; i < ARRAY_SIZE(g_NamePairs); i++) if (g_NamePairs[i].Id == f.id) { p = g_NamePairs[i].Name; break; } char temp[32]; if (!p) { ::ConvertUInt64ToString(f.id, temp); p = temp; } AString s = p; if (f.propsSize > 0) { s += ':'; if (f.id == XZ_ID_LZMA2 && f.propsSize == 1) Lzma2PropToString(s, f.props[0]); else if (f.id == XZ_ID_Delta && f.propsSize == 1) AddUInt32ToString(s, (UInt32)f.props[0] + 1); else { s += '['; for (UInt32 bi = 0; bi < f.propsSize; bi++) AddHexToString(s, f.props[bi]); s += ']'; } } return s; } static void AddString(AString &dest, const AString &src) { if (!dest.IsEmpty()) dest += ' '; dest += src; } static const char *kChecks[] = { "NoCheck" , "CRC32" , NULL , NULL , "CRC64" , NULL , NULL , NULL , NULL , NULL , "SHA256" , NULL , NULL , NULL , NULL , NULL }; static AString GetCheckString(const CXzs &xzs) { size_t i; UInt32 mask = 0; for (i = 0; i < xzs.num; i++) mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags)); AString s; for (i = 0; i <= XZ_CHECK_MASK; i++) if (((mask >> i) & 1) != 0) { AString s2; if (kChecks[i]) s2 = kChecks[i]; else { s2 = "Check-"; AddUInt32ToString(s2, (UInt32)i); } AddString(s, s2); } return s; } STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; switch (propID) { case kpidPhySize: if (_phySize_Defined) prop = _stat.PhySize; break; case kpidNumStreams: if (_stat.NumStreams_Defined) prop = _stat.NumStreams; break; case kpidNumBlocks: if (_stat.NumBlocks_Defined) prop = _stat.NumBlocks; break; case kpidUnpackSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; case kpidErrorFlags: { UInt32 v = 0; if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; if (_stat.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd; if (_stat.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; if (_stat.HeadersError) v |= kpv_ErrorFlags_HeadersError; if (_stat.Unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; if (_stat.DataError) v |= kpv_ErrorFlags_DataError; if (_stat.CrcError) v |= kpv_ErrorFlags_CrcError; prop = v; } } prop.Detach(value); return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = 1; return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; switch (propID) { case kpidSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; case kpidPackSize: if (_phySize_Defined) prop = _stat.PhySize; break; case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; } prop.Detach(value); return S_OK; COM_TRY_END } struct COpenCallbackWrap { ICompressProgress p; IArchiveOpenCallback *OpenCallback; HRESULT Res; COpenCallbackWrap(IArchiveOpenCallback *progress); }; static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */) { COpenCallbackWrap *p = (COpenCallbackWrap *)pp; p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); return (SRes)p->Res; } COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback) { p.Progress = OpenCallbackProgress; OpenCallback = callback; Res = SZ_OK; } struct CXzsCPP { CXzs p; CXzsCPP() { Xzs_Construct(&p); } ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } }; struct CVirtProgress_To_OpenProgress: public IDecodeState { IArchiveOpenCallback *Callback; UInt64 Offset; HRESULT Progress(); }; HRESULT CVirtProgress_To_OpenProgress::Progress() { if (Callback) { UInt64 files = 0; UInt64 value = Offset + InSize; return Callback->SetCompleted(&files, &value); } return S_OK; } static HRESULT SRes_to_Open_HRESULT(SRes res) { switch (res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PROGRESS: return E_ABORT; /* case SZ_ERROR_UNSUPPORTED: case SZ_ERROR_CRC: case SZ_ERROR_DATA: case SZ_ERROR_ARCHIVE: case SZ_ERROR_NO_ARCHIVE: return S_FALSE; */ } return S_FALSE; } HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback) { _needSeekToStart = true; { CXzStreamFlags st; CSeqInStreamWrap inStreamWrap(inStream); SRes res = Xz_ReadHeader(&st, &inStreamWrap.p); if (res != SZ_OK) return SRes_to_Open_HRESULT(res); { CXzBlock block; Bool isIndex; UInt32 headerSizeRes; SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes); if (res2 == SZ_OK && !isIndex) { unsigned numFilters = XzBlock_GetNumFilters(&block); for (unsigned i = 0; i < numFilters; i++) AddString(_methodsString, GetMethodString(block.filters[i])); } } } RINOK(inStream->Seek(0, STREAM_SEEK_END, &_stat.PhySize)); RINOK(callback->SetTotal(NULL, &_stat.PhySize)); CSeekInStreamWrap inStreamImp(inStream); CLookToRead lookStream; LookToRead_CreateVTable(&lookStream, True); lookStream.realStream = &inStreamImp.p; LookToRead_Init(&lookStream); COpenCallbackWrap openWrap(callback); CXzsCPP xzs; Int64 startPosition; SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &startPosition, &openWrap.p, &g_Alloc); if (res == SZ_ERROR_PROGRESS) return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res; /* if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0) res = SZ_OK; */ if (res == SZ_OK && startPosition == 0) { _phySize_Defined = true; _stat.OutSize = Xzs_GetUnpackSize(&xzs.p); _stat.UnpackSize_Defined = true; _stat.NumStreams = xzs.p.num; _stat.NumStreams_Defined = true; _stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p); _stat.NumBlocks_Defined = true; AddString(_methodsString, GetCheckString(xzs.p)); } else { res = SZ_OK; } RINOK(SRes_to_Open_HRESULT(res)); _stream = inStream; _seqStream = inStream; _isArc = true; return S_OK; } STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) { COM_TRY_BEGIN { Close(); return Open2(inStream, /* 0, */ callback); } COM_TRY_END } STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) { Close(); _seqStream = stream; _isArc = true; _needSeekToStart = false; return S_OK; } STDMETHODIMP CHandler::Close() { _stat.Clear(); _isArc = false; _needSeekToStart = false; _phySize_Defined = false; _methodsString.Empty(); _stream.Release(); _seqStream.Release(); return S_OK; } class CSeekToSeqStream: public IInStream, public CMyUnknownImp { public: CMyComPtr Stream; MY_UNKNOWN_IMP1(IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize) { return Stream->Read(data, size, processedSize); } STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; } struct CXzUnpackerCPP { Byte *InBuf; Byte *OutBuf; CXzUnpacker p; CXzUnpackerCPP(): InBuf(0), OutBuf(0) { XzUnpacker_Construct(&p, &g_Alloc); } ~CXzUnpackerCPP() { XzUnpacker_Free(&p); MyFree(InBuf); MyFree(OutBuf); } }; HRESULT IDecodeState::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream) { const size_t kInBufSize = 1 << 15; const size_t kOutBufSize = 1 << 21; DecodeRes = SZ_OK; CXzUnpackerCPP xzu; XzUnpacker_Init(&xzu.p); xzu.InBuf = (Byte *)MyAlloc(kInBufSize); xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize); if (!xzu.InBuf || !xzu.OutBuf) return E_OUTOFMEMORY; UInt32 inSize = 0; SizeT inPos = 0; SizeT outPos = 0; for (;;) { if (inPos == inSize) { inPos = inSize = 0; RINOK(seqInStream->Read(xzu.InBuf, kInBufSize, &inSize)); } SizeT inLen = inSize - inPos; SizeT outLen = kOutBufSize - outPos; ECoderStatus status; SRes res = XzUnpacker_Code(&xzu.p, xzu.OutBuf + outPos, &outLen, xzu.InBuf + inPos, &inLen, (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status); inPos += inLen; outPos += outLen; InSize += inLen; OutSize += outLen; DecodeRes = res; bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK); if (outStream) { if (outPos == kOutBufSize || finished) { if (outPos != 0) { RINOK(WriteStream(outStream, xzu.OutBuf, outPos)); outPos = 0; } } } else outPos = 0; RINOK(Progress()); if (finished) { PhySize = InSize; NumStreams = xzu.p.numStartedStreams; if (NumStreams > 0) IsArc = true; NumBlocks = xzu.p.numTotalBlocks; UnpackSize_Defined = true; NumStreams_Defined = true; NumBlocks_Defined = true; UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p); if (res == SZ_OK) { if (status == CODER_STATUS_NEEDS_MORE_INPUT) { extraSize = 0; if (!XzUnpacker_IsStreamWasFinished(&xzu.p)) { // finished at padding bytes, but padding is not aligned for 4 UnexpectedEnd = true; res = SZ_ERROR_DATA; } } else // status == CODER_STATUS_NOT_FINISHED res = SZ_ERROR_DATA; } else if (res == SZ_ERROR_NO_ARCHIVE) { if (InSize == extraSize) IsArc = false; else { if (extraSize != 0 || inPos != inSize) { DataAfterEnd = true; res = SZ_OK; } } } DecodeRes = res; PhySize -= extraSize; switch (res) { case SZ_OK: break; case SZ_ERROR_NO_ARCHIVE: IsArc = false; break; case SZ_ERROR_ARCHIVE: HeadersError = true; break; case SZ_ERROR_UNSUPPORTED: Unsupported = true; break; case SZ_ERROR_CRC: CrcError = true; break; case SZ_ERROR_DATA: DataError = true; break; default: DataError = true; break; } break; } } return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN if (numItems == 0) return S_OK; if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; extractCallback->SetTotal(_stat.PhySize); UInt64 currentTotalPacked = 0; RINOK(extractCallback->SetCompleted(¤tTotalPacked)); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); if (!testMode && !realOutStream) return S_OK; extractCallback->PrepareOperation(askMode); CVirtProgress_To_LocalProgress vp; vp.lps = new CLocalProgress; vp.progress = vp.lps; vp.lps->Init(extractCallback, true); if (_needSeekToStart) { if (!_stream) return E_FAIL; RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); } else _needSeekToStart = true; RINOK(Decode2(_seqStream, realOutStream, vp)); Int32 opRes; if (!vp.IsArc) opRes = NExtract::NOperationResult::kIsNotArc; else if (vp.UnexpectedEnd) opRes = NExtract::NOperationResult::kUnexpectedEnd; else if (vp.DataAfterEnd) opRes = NExtract::NOperationResult::kDataAfterEnd; else if (vp.CrcError) opRes = NExtract::NOperationResult::kCRCError; else if (vp.Unsupported) opRes = NExtract::NOperationResult::kUnsupportedMethod; else if (vp.HeadersError) opRes = NExtract::NOperationResult::kDataError; else if (vp.DataError) opRes = NExtract::NOperationResult::kDataError; else if (vp.DecodeRes != SZ_OK) opRes = NExtract::NOperationResult::kDataError; else opRes = NExtract::NOperationResult::kOK; realOutStream.Release(); return extractCallback->SetOperationResult(opRes); COM_TRY_END } #ifndef EXTRACT_ONLY STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) { *timeType = NFileTimeType::kUnix; return S_OK; } STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) { CSeqOutStreamWrap seqOutStream(outStream); if (numItems == 0) { SRes res = Xz_EncodeEmpty(&seqOutStream.p); return SResToHRESULT(res); } if (numItems != 1) return E_INVALIDARG; Int32 newData, newProps; UInt32 indexInArchive; if (!updateCallback) return E_FAIL; RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); if (IntToBool(newProps)) { { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); if (prop.vt != VT_EMPTY) if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) return E_INVALIDARG; } } if (IntToBool(newData)) { UInt64 size; { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; RINOK(updateCallback->SetTotal(size)); } CLzma2EncProps lzma2Props; Lzma2EncProps_Init(&lzma2Props); lzma2Props.lzmaProps.level = GetLevel(); CMyComPtr fileInStream; RINOK(updateCallback->GetStream(0, &fileInStream)); CSeqInStreamWrap seqInStream(fileInStream); { NCOM::CPropVariant prop = (UInt64)size; RINOK(NCompress::NLzma2::SetLzma2Prop(NCoderPropID::kReduceSize, prop, lzma2Props)); } FOR_VECTOR (i, _methods) { COneMethodInfo &m = _methods[i]; SetGlobalLevelAndThreads(m #ifndef _7ZIP_ST , _numThreads #endif ); { FOR_VECTOR (j, m.Props) { const CProp &prop = m.Props[j]; RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props)); } } } #ifndef _7ZIP_ST lzma2Props.numTotalThreads = _numThreads; #endif CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(updateCallback, true); CCompressProgressWrap progressWrap(progress); CXzProps xzProps; CXzFilterProps filter; XzProps_Init(&xzProps); XzFilterProps_Init(&filter); xzProps.lzma2Props = &lzma2Props; xzProps.filterProps = (_filterId != 0 ? &filter : NULL); switch (_crcSize) { case 0: xzProps.checkId = XZ_CHECK_NO; break; case 4: xzProps.checkId = XZ_CHECK_CRC32; break; case 8: xzProps.checkId = XZ_CHECK_CRC64; break; case 32: xzProps.checkId = XZ_CHECK_SHA256; break; default: return E_INVALIDARG; } filter.id = _filterId; if (_filterId == XZ_ID_Delta) { bool deltaDefined = false; FOR_VECTOR (j, _filterMethod.Props) { const CProp &prop = _filterMethod.Props[j]; if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4) { UInt32 delta = (UInt32)prop.Value.ulVal; if (delta < 1 || delta > 256) return E_INVALIDARG; filter.delta = delta; deltaDefined = true; } } if (!deltaDefined) return E_INVALIDARG; } SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &xzProps, &progressWrap.p); if (res == SZ_OK) return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); return SResToHRESULT(res); } if (indexInArchive != 0) return E_INVALIDARG; if (_stream) RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); return NCompress::CopyStream(_stream, outStream, NULL); } STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { COM_TRY_BEGIN Init(); for (UInt32 i = 0; i < numProps; i++) { RINOK(SetProperty(names[i], values[i])); } if (!_filterMethod.MethodName.IsEmpty()) { unsigned k; for (k = 0; k < ARRAY_SIZE(g_NamePairs); k++) { const CMethodNamePair &pair = g_NamePairs[k]; if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name)) { _filterId = pair.Id; break; } } if (k == ARRAY_SIZE(g_NamePairs)) return E_INVALIDARG; } _methods.DeleteFrontal(GetNumEmptyMethods()); if (_methods.Size() > 1) return E_INVALIDARG; if (_methods.Size() == 1) { UString &methodName = _methods[0].MethodName; if (methodName.IsEmpty()) methodName = k_LZMA2_Name; else if (!methodName.IsEqualToNoCase(k_LZMA2_Name)) return E_INVALIDARG; } return S_OK; COM_TRY_END } #endif IMP_CreateArcIn IMP_CreateArcOut static CArcInfo g_ArcInfo = { "xz", "xz txz", "* .tar", 0xC, 6, { 0xFD, '7' , 'z', 'X', 'Z', 0 }, 0, NArcInfoFlags::kKeepName, REF_CreateArc_Pair }; REGISTER_ARC(xz) }} src/libs/7zip/unix/CPP/7zip/Common/000077500000000000000000000000001325366651500172315ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/Common/CWrappers.cpp000066400000000000000000000123041325366651500216430ustar00rootroot00000000000000// CWrappers.h #include "StdAfx.h" #include "../../../C/Alloc.h" #include "CWrappers.h" #include "StreamUtils.h" #define PROGRESS_UNKNOWN_VALUE ((UInt64)(Int64)-1) #define CONVERT_PR_VAL(x) (x == PROGRESS_UNKNOWN_VALUE ? NULL : &x) static SRes CompressProgress(void *pp, UInt64 inSize, UInt64 outSize) throw() { CCompressProgressWrap *p = (CCompressProgressWrap *)pp; p->Res = p->Progress->SetRatioInfo(CONVERT_PR_VAL(inSize), CONVERT_PR_VAL(outSize)); return (SRes)p->Res; } CCompressProgressWrap::CCompressProgressWrap(ICompressProgressInfo *progress) throw() { p.Progress = CompressProgress; Progress = progress; Res = SZ_OK; } static const UInt32 kStreamStepSize = (UInt32)1 << 31; SRes HRESULT_To_SRes(HRESULT res, SRes defaultRes) { switch (res) { case S_OK: return SZ_OK; case E_OUTOFMEMORY: return SZ_ERROR_MEM; case E_INVALIDARG: return SZ_ERROR_PARAM; case E_ABORT: return SZ_ERROR_PROGRESS; case S_FALSE: return SZ_ERROR_DATA; case E_NOTIMPL: return SZ_ERROR_UNSUPPORTED; } return defaultRes; } static SRes MyRead(void *object, void *data, size_t *size) throw() { CSeqInStreamWrap *p = (CSeqInStreamWrap *)object; UInt32 curSize = ((*size < kStreamStepSize) ? (UInt32)*size : kStreamStepSize); p->Res = (p->Stream->Read(data, curSize, &curSize)); *size = curSize; p->Processed += curSize; if (p->Res == S_OK) return SZ_OK; return HRESULT_To_SRes(p->Res, SZ_ERROR_READ); } static size_t MyWrite(void *object, const void *data, size_t size) throw() { CSeqOutStreamWrap *p = (CSeqOutStreamWrap *)object; if (p->Stream) { p->Res = WriteStream(p->Stream, data, size); if (p->Res != 0) return 0; } else p->Res = S_OK; p->Processed += size; return size; } CSeqInStreamWrap::CSeqInStreamWrap(ISequentialInStream *stream) throw() { p.Read = MyRead; Stream = stream; Processed = 0; } CSeqOutStreamWrap::CSeqOutStreamWrap(ISequentialOutStream *stream) throw() { p.Write = MyWrite; Stream = stream; Res = SZ_OK; Processed = 0; } HRESULT SResToHRESULT(SRes res) throw() { switch(res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PARAM: return E_INVALIDARG; case SZ_ERROR_PROGRESS: return E_ABORT; case SZ_ERROR_DATA: return S_FALSE; case SZ_ERROR_UNSUPPORTED: return E_NOTIMPL; } return E_FAIL; } static SRes InStreamWrap_Read(void *pp, void *data, size_t *size) throw() { CSeekInStreamWrap *p = (CSeekInStreamWrap *)pp; UInt32 curSize = ((*size < kStreamStepSize) ? (UInt32)*size : kStreamStepSize); p->Res = p->Stream->Read(data, curSize, &curSize); *size = curSize; return (p->Res == S_OK) ? SZ_OK : SZ_ERROR_READ; } static SRes InStreamWrap_Seek(void *pp, Int64 *offset, ESzSeek origin) throw() { CSeekInStreamWrap *p = (CSeekInStreamWrap *)pp; UInt32 moveMethod; switch(origin) { case SZ_SEEK_SET: moveMethod = STREAM_SEEK_SET; break; case SZ_SEEK_CUR: moveMethod = STREAM_SEEK_CUR; break; case SZ_SEEK_END: moveMethod = STREAM_SEEK_END; break; default: return SZ_ERROR_PARAM; } UInt64 newPosition; p->Res = p->Stream->Seek(*offset, moveMethod, &newPosition); *offset = (Int64)newPosition; return (p->Res == S_OK) ? SZ_OK : SZ_ERROR_READ; } CSeekInStreamWrap::CSeekInStreamWrap(IInStream *stream) throw() { Stream = stream; p.Read = InStreamWrap_Read; p.Seek = InStreamWrap_Seek; Res = S_OK; } /* ---------- CByteInBufWrap ---------- */ void CByteInBufWrap::Free() throw() { ::MidFree(Buf); Buf = 0; } bool CByteInBufWrap::Alloc(UInt32 size) throw() { if (Buf == 0 || size != Size) { Free(); Lim = Cur = Buf = (Byte *)::MidAlloc((size_t)size); Size = size; } return (Buf != 0); } Byte CByteInBufWrap::ReadByteFromNewBlock() throw() { if (Res == S_OK) { UInt32 avail; Processed += (Cur - Buf); Res = Stream->Read(Buf, Size, &avail); Cur = Buf; Lim = Buf + avail; if (avail != 0) return *Cur++; } Extra = true; return 0; } static Byte Wrap_ReadByte(void *pp) throw() { CByteInBufWrap *p = (CByteInBufWrap *)pp; if (p->Cur != p->Lim) return *p->Cur++; return p->ReadByteFromNewBlock(); } CByteInBufWrap::CByteInBufWrap(): Buf(0) { p.Read = Wrap_ReadByte; } /* ---------- CByteOutBufWrap ---------- */ void CByteOutBufWrap::Free() throw() { ::MidFree(Buf); Buf = 0; } bool CByteOutBufWrap::Alloc(size_t size) throw() { if (Buf == 0 || size != Size) { Free(); Buf = (Byte *)::MidAlloc(size); Size = size; } return (Buf != 0); } HRESULT CByteOutBufWrap::Flush() throw() { if (Res == S_OK) { size_t size = (Cur - Buf); Res = WriteStream(Stream, Buf, size); if (Res == S_OK) Processed += size; Cur = Buf; } return Res; } static void Wrap_WriteByte(void *pp, Byte b) throw() { CByteOutBufWrap *p = (CByteOutBufWrap *)pp; Byte *dest = p->Cur; *dest = b; p->Cur = ++dest; if (dest == p->Lim) p->Flush(); } CByteOutBufWrap::CByteOutBufWrap() throw(): Buf(0) { p.Write = Wrap_WriteByte; } src/libs/7zip/unix/CPP/7zip/Common/CWrappers.h000066400000000000000000000040711325366651500213120ustar00rootroot00000000000000// CWrappers.h #ifndef __C_WRAPPERS_H #define __C_WRAPPERS_H #include "../ICoder.h" #include "../../Common/MyCom.h" struct CCompressProgressWrap { ICompressProgress p; ICompressProgressInfo *Progress; HRESULT Res; CCompressProgressWrap(ICompressProgressInfo *progress) throw(); }; struct CSeqInStreamWrap { ISeqInStream p; ISequentialInStream *Stream; HRESULT Res; UInt64 Processed; CSeqInStreamWrap(ISequentialInStream *stream) throw(); }; struct CSeekInStreamWrap { ISeekInStream p; IInStream *Stream; HRESULT Res; CSeekInStreamWrap(IInStream *stream) throw(); }; struct CSeqOutStreamWrap { ISeqOutStream p; ISequentialOutStream *Stream; HRESULT Res; UInt64 Processed; CSeqOutStreamWrap(ISequentialOutStream *stream) throw(); }; HRESULT SResToHRESULT(SRes res) throw(); struct CByteInBufWrap { IByteIn p; const Byte *Cur; const Byte *Lim; Byte *Buf; UInt32 Size; ISequentialInStream *Stream; UInt64 Processed; bool Extra; HRESULT Res; CByteInBufWrap(); ~CByteInBufWrap() { Free(); } void Free() throw(); bool Alloc(UInt32 size) throw(); void Init() { Lim = Cur = Buf; Processed = 0; Extra = false; Res = S_OK; } UInt64 GetProcessed() const { return Processed + (Cur - Buf); } Byte ReadByteFromNewBlock() throw(); Byte ReadByte() { if (Cur != Lim) return *Cur++; return ReadByteFromNewBlock(); } }; struct CByteOutBufWrap { IByteOut p; Byte *Cur; const Byte *Lim; Byte *Buf; size_t Size; ISequentialOutStream *Stream; UInt64 Processed; HRESULT Res; CByteOutBufWrap() throw(); ~CByteOutBufWrap() { Free(); } void Free() throw(); bool Alloc(size_t size) throw(); void Init() { Cur = Buf; Lim = Buf + Size; Processed = 0; Res = S_OK; } UInt64 GetProcessed() const { return Processed + (Cur - Buf); } HRESULT Flush() throw(); void WriteByte(Byte b) { *Cur++ = b; if (Cur == Lim) Flush(); } }; #endif src/libs/7zip/unix/CPP/7zip/Common/Common.pri000066400000000000000000000036061325366651500212020ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Common/CWrappers.h \ $$7ZIP_BASE/CPP/7zip/Common/CreateCoder.h \ $$7ZIP_BASE/CPP/7zip/Common/FilePathAutoRename.h \ $$7ZIP_BASE/CPP/7zip/Common/FileStreams.h \ $$7ZIP_BASE/CPP/7zip/Common/FilterCoder.h \ $$7ZIP_BASE/CPP/7zip/Common/InBuffer.h \ $$7ZIP_BASE/CPP/7zip/Common/InOutTempBuffer.h \ $$7ZIP_BASE/CPP/7zip/Common/LimitedStreams.h \ $$7ZIP_BASE/CPP/7zip/Common/LockedStream.h \ $$7ZIP_BASE/CPP/7zip/Common/MethodId.h \ $$7ZIP_BASE/CPP/7zip/Common/MethodProps.h \ $$7ZIP_BASE/CPP/7zip/Common/OutBuffer.h \ $$7ZIP_BASE/CPP/7zip/Common/ProgressUtils.h \ $$7ZIP_BASE/CPP/7zip/Common/RegisterArc.h \ $$7ZIP_BASE/CPP/7zip/Common/RegisterCodec.h \ $$7ZIP_BASE/CPP/7zip/Common/StreamBinder.h \ $$7ZIP_BASE/CPP/7zip/Common/StreamObjects.h \ $$7ZIP_BASE/CPP/7zip/Common/StreamUtils.h \ $$7ZIP_BASE/CPP/7zip/Common/UniqBlocks.h \ $$7ZIP_BASE/CPP/7zip/Common/VirtThread.h SOURCES += $$7ZIP_BASE/CPP/7zip/Common/CWrappers.cpp \ $$7ZIP_BASE/CPP/7zip/Common/CreateCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Common/FilePathAutoRename.cpp \ $$7ZIP_BASE/CPP/7zip/Common/FileStreams.cpp \ $$7ZIP_BASE/CPP/7zip/Common/FilterCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Common/InBuffer.cpp \ $$7ZIP_BASE/CPP/7zip/Common/InOutTempBuffer.cpp \ $$7ZIP_BASE/CPP/7zip/Common/LimitedStreams.cpp \ $$7ZIP_BASE/CPP/7zip/Common/LockedStream.cpp \ $$7ZIP_BASE/CPP/7zip/Common/MethodProps.cpp \ $$7ZIP_BASE/CPP/7zip/Common/OutBuffer.cpp \ $$7ZIP_BASE/CPP/7zip/Common/ProgressUtils.cpp \ $$7ZIP_BASE/CPP/7zip/Common/PropId.cpp \ $$7ZIP_BASE/CPP/7zip/Common/StreamBinder.cpp \ $$7ZIP_BASE/CPP/7zip/Common/StreamObjects.cpp \ $$7ZIP_BASE/CPP/7zip/Common/StreamUtils.cpp \ $$7ZIP_BASE/CPP/7zip/Common/UniqBlocks.cpp \ $$7ZIP_BASE/CPP/7zip/Common/VirtThread.cpp src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.cpp000066400000000000000000000246761325366651500221340ustar00rootroot00000000000000// CreateCoder.cpp #include "StdAfx.h" #include "../../Windows/Defs.h" #include "../../Windows/PropVariant.h" #include "CreateCoder.h" #include "FilterCoder.h" #include "RegisterCodec.h" static const unsigned int kNumCodecsMax = 64; unsigned int g_NumCodecs = 0; const CCodecInfo *g_Codecs[kNumCodecsMax]; void RegisterCodec(const CCodecInfo *codecInfo) throw() { if (g_NumCodecs < kNumCodecsMax) g_Codecs[g_NumCodecs++] = codecInfo; } static const unsigned int kNumHashersMax = 16; unsigned int g_NumHashers = 0; const CHasherInfo *g_Hashers[kNumHashersMax]; void RegisterHasher(const CHasherInfo *hashInfo) throw() { if (g_NumHashers < kNumHashersMax) g_Hashers[g_NumHashers++] = hashInfo; } #ifdef EXTERNAL_CODECS static HRESULT ReadNumberOfStreams(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, UInt32 &res) { NWindows::NCOM::CPropVariant prop; RINOK(codecsInfo->GetProperty(index, propID, &prop)); if (prop.vt == VT_EMPTY) res = 1; else if (prop.vt == VT_UI4) res = prop.ulVal; else return E_INVALIDARG; return S_OK; } static HRESULT ReadIsAssignedProp(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, bool &res) { NWindows::NCOM::CPropVariant prop; RINOK(codecsInfo->GetProperty(index, propID, &prop)); if (prop.vt == VT_EMPTY) res = true; else if (prop.vt == VT_BOOL) res = VARIANT_BOOLToBool(prop.boolVal); else return E_INVALIDARG; return S_OK; } HRESULT CExternalCodecs::LoadCodecs() { if (GetCodecs) { UInt32 num; RINOK(GetCodecs->GetNumberOfMethods(&num)); for (UInt32 i = 0; i < num; i++) { CCodecInfoEx info; NWindows::NCOM::CPropVariant prop; RINOK(GetCodecs->GetProperty(i, NMethodPropID::kID, &prop)); // if (prop.vt != VT_BSTR) // info.Id.IDSize = (Byte)SysStringByteLen(prop.bstrVal); // memcpy(info.Id.ID, prop.bstrVal, info.Id.IDSize); if (prop.vt != VT_UI8) continue; // old Interface info.Id = prop.uhVal.QuadPart; prop.Clear(); RINOK(GetCodecs->GetProperty(i, NMethodPropID::kName, &prop)); if (prop.vt == VT_BSTR) info.Name = prop.bstrVal; else if (prop.vt != VT_EMPTY) return E_INVALIDARG; RINOK(ReadNumberOfStreams(GetCodecs, i, NMethodPropID::kInStreams, info.NumInStreams)); RINOK(ReadNumberOfStreams(GetCodecs, i, NMethodPropID::kOutStreams, info.NumOutStreams)); RINOK(ReadIsAssignedProp(GetCodecs, i, NMethodPropID::kEncoderIsAssigned, info.EncoderIsAssigned)); RINOK(ReadIsAssignedProp(GetCodecs, i, NMethodPropID::kDecoderIsAssigned, info.DecoderIsAssigned)); Codecs.Add(info); } } if (GetHashers) { UInt32 num = GetHashers->GetNumHashers(); for (UInt32 i = 0; i < num; i++) { CHasherInfoEx info; NWindows::NCOM::CPropVariant prop; RINOK(GetHashers->GetHasherProp(i, NMethodPropID::kID, &prop)); if (prop.vt != VT_UI8) continue; info.Id = prop.uhVal.QuadPart; prop.Clear(); RINOK(GetHashers->GetHasherProp(i, NMethodPropID::kName, &prop)); if (prop.vt == VT_BSTR) info.Name = prop.bstrVal; else if (prop.vt != VT_EMPTY) return E_INVALIDARG; Hashers.Add(info); } } return S_OK; } #endif bool FindMethod(DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams) { UInt32 i; for (i = 0; i < g_NumCodecs; i++) { const CCodecInfo &codec = *g_Codecs[i]; if (name.IsEqualToNoCase(codec.Name)) { methodId = codec.Id; numInStreams = codec.NumInStreams; numOutStreams = 1; return true; } } #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Codecs.Size(); i++) { const CCodecInfoEx &codec = __externalCodecs->Codecs[i]; if (codec.Name.IsEqualToNoCase(name)) { methodId = codec.Id; numInStreams = codec.NumInStreams; numOutStreams = codec.NumOutStreams; return true; } } #endif return false; } bool FindMethod(DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name) { UInt32 i; for (i = 0; i < g_NumCodecs; i++) { const CCodecInfo &codec = *g_Codecs[i]; if (methodId == codec.Id) { name = codec.Name; return true; } } #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Codecs.Size(); i++) { const CCodecInfoEx &codec = __externalCodecs->Codecs[i]; if (methodId == codec.Id) { name = codec.Name; return true; } } #endif return false; } bool FindHashMethod(DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId) { UInt32 i; for (i = 0; i < g_NumHashers; i++) { const CHasherInfo &codec = *g_Hashers[i]; if (name.IsEqualToNoCase(codec.Name)) { methodId = codec.Id; return true; } } #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Hashers.Size(); i++) { const CHasherInfoEx &codec = __externalCodecs->Hashers[i]; if (codec.Name.IsEqualToNoCase(name)) { methodId = codec.Id; return true; } } #endif return false; } void GetHashMethods(DECL_EXTERNAL_CODECS_LOC_VARS CRecordVector &methods) { methods.ClearAndSetSize(g_NumHashers); UInt32 i; for (i = 0; i < g_NumHashers; i++) methods[i] = (*g_Hashers[i]).Id; #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Hashers.Size(); i++) methods.Add(__externalCodecs->Hashers[i].Id); #endif } HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, CMyComPtr &coder, CMyComPtr &coder2, bool encode, bool onlyCoder) { UInt32 i; for (i = 0; i < g_NumCodecs; i++) { const CCodecInfo &codec = *g_Codecs[i]; if (codec.Id == methodId) { if (encode) { if (codec.CreateEncoder) { void *p = codec.CreateEncoder(); if (codec.IsFilter) filter = (ICompressFilter *)p; else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p; else coder2 = (ICompressCoder2 *)p; break; } } else if (codec.CreateDecoder) { void *p = codec.CreateDecoder(); if (codec.IsFilter) filter = (ICompressFilter *)p; else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p; else coder2 = (ICompressCoder2 *)p; break; } } } #ifdef EXTERNAL_CODECS if (!filter && !coder && !coder2 && __externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Codecs.Size(); i++) { const CCodecInfoEx &codec = __externalCodecs->Codecs[i]; if (codec.Id == methodId) { if (encode) { if (codec.EncoderIsAssigned) { if (codec.IsSimpleCodec()) { HRESULT result = __externalCodecs->GetCodecs->CreateEncoder(i, &IID_ICompressCoder, (void **)&coder); if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE) return result; if (!coder) { RINOK(__externalCodecs->GetCodecs->CreateEncoder(i, &IID_ICompressFilter, (void **)&filter)); } } else { RINOK(__externalCodecs->GetCodecs->CreateEncoder(i, &IID_ICompressCoder2, (void **)&coder2)); } break; } } else if (codec.DecoderIsAssigned) { if (codec.IsSimpleCodec()) { HRESULT result = __externalCodecs->GetCodecs->CreateDecoder(i, &IID_ICompressCoder, (void **)&coder); if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE) return result; if (!coder) { RINOK(__externalCodecs->GetCodecs->CreateDecoder(i, &IID_ICompressFilter, (void **)&filter)); } } else { RINOK(__externalCodecs->GetCodecs->CreateDecoder(i, &IID_ICompressCoder2, (void **)&coder2)); } break; } } } #endif if (onlyCoder && filter) { CFilterCoder *coderSpec = new CFilterCoder; coder = coderSpec; coderSpec->Filter = filter; } return S_OK; } HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, CMyComPtr &coder2, bool encode) { CMyComPtr filter; return CreateCoder( EXTERNAL_CODECS_LOC_VARS methodId, filter, coder, coder2, encode, true); } HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, bool encode) { CMyComPtr filter; CMyComPtr coder2; return CreateCoder( EXTERNAL_CODECS_LOC_VARS methodId, coder, coder2, encode); } HRESULT CreateFilter( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, bool encode) { CMyComPtr coder; CMyComPtr coder2; return CreateCoder( EXTERNAL_CODECS_LOC_VARS methodId, filter, coder, coder2, encode, false); } HRESULT CreateHasher( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name, CMyComPtr &hasher) { UInt32 i; for (i = 0; i < g_NumHashers; i++) { const CHasherInfo &codec = *g_Hashers[i]; if (codec.Id == methodId) { hasher = (IHasher *)codec.CreateHasher(); name = codec.Name; break; } } #ifdef EXTERNAL_CODECS if (!hasher && __externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Hashers.Size(); i++) { const CHasherInfoEx &codec = __externalCodecs->Hashers[i]; if (codec.Id == methodId) { name = codec.Name; return __externalCodecs->GetHashers->CreateHasher(i, &hasher); } } #endif return S_OK; } src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.h000066400000000000000000000067471325366651500216000ustar00rootroot00000000000000// CreateCoder.h #ifndef __CREATE_CODER_H #define __CREATE_CODER_H #include "../../Common/MyCom.h" #include "../../Common/MyString.h" #include "../ICoder.h" #include "MethodId.h" #ifdef EXTERNAL_CODECS struct CCodecInfoEx { UString Name; CMethodId Id; UInt32 NumInStreams; UInt32 NumOutStreams; bool EncoderIsAssigned; bool DecoderIsAssigned; bool IsSimpleCodec() const { return NumOutStreams == 1 && NumInStreams == 1; } CCodecInfoEx(): EncoderIsAssigned(false), DecoderIsAssigned(false) {} }; struct CHasherInfoEx { UString Name; CMethodId Id; }; #define PUBLIC_ISetCompressCodecsInfo public ISetCompressCodecsInfo, #define QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_ENTRY(ISetCompressCodecsInfo) #define DECL_ISetCompressCodecsInfo STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo); #define IMPL_ISetCompressCodecsInfo2(x) \ STDMETHODIMP x::SetCompressCodecsInfo(ICompressCodecsInfo *compressCodecsInfo) { \ COM_TRY_BEGIN __externalCodecs.GetCodecs = compressCodecsInfo; return __externalCodecs.LoadCodecs(); COM_TRY_END } #define IMPL_ISetCompressCodecsInfo IMPL_ISetCompressCodecsInfo2(CHandler) struct CExternalCodecs { CMyComPtr GetCodecs; CMyComPtr GetHashers; CObjectVector Codecs; CObjectVector Hashers; HRESULT LoadCodecs(); }; #define EXTERNAL_CODECS_VARS2 &__externalCodecs #define DECL_EXTERNAL_CODECS_VARS CExternalCodecs __externalCodecs; #define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2, #define DECL_EXTERNAL_CODECS_LOC_VARS2 const CExternalCodecs *__externalCodecs #define EXTERNAL_CODECS_LOC_VARS2 __externalCodecs #define DECL_EXTERNAL_CODECS_LOC_VARS DECL_EXTERNAL_CODECS_LOC_VARS2, #define EXTERNAL_CODECS_LOC_VARS EXTERNAL_CODECS_LOC_VARS2, #else #define PUBLIC_ISetCompressCodecsInfo #define QUERY_ENTRY_ISetCompressCodecsInfo #define DECL_ISetCompressCodecsInfo #define IMPL_ISetCompressCodecsInfo #define EXTERNAL_CODECS_VARS2 #define DECL_EXTERNAL_CODECS_VARS #define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2 #define DECL_EXTERNAL_CODECS_LOC_VARS2 #define EXTERNAL_CODECS_LOC_VARS2 #define DECL_EXTERNAL_CODECS_LOC_VARS #define EXTERNAL_CODECS_LOC_VARS #endif bool FindMethod( DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams); bool FindMethod( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name); bool FindHashMethod( DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId); void GetHashMethods( DECL_EXTERNAL_CODECS_LOC_VARS CRecordVector &methods); HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, CMyComPtr &coder, CMyComPtr &coder2, bool encode, bool onlyCoder); HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, CMyComPtr &coder2, bool encode); HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, bool encode); HRESULT CreateFilter( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, bool encode); HRESULT CreateHasher( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name, CMyComPtr &hacher); #endif src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.cpp000066400000000000000000000025161325366651500234160ustar00rootroot00000000000000// FilePathAutoRename.cpp #include "StdAfx.h" #include "../../Common/Defs.h" #include "../../Common/IntToString.h" #include "../../Windows/FileFind.h" #include "FilePathAutoRename.h" using namespace NWindows; static bool MakeAutoName(const FString &name, const FString &extension, unsigned value, FString &path) { FChar number[16]; ConvertUInt32ToString(value, number); path = name; path += number; path += extension; return NFile::NFind::DoesFileOrDirExist(path); } bool AutoRenamePath(FString &fullProcessedPath) { FString path; int dotPos = fullProcessedPath.ReverseFind(FTEXT('.')); int slashPos = fullProcessedPath.ReverseFind(FTEXT('/')); #ifdef _WIN32 int slash1Pos = fullProcessedPath.ReverseFind(FTEXT('\\')); slashPos = MyMax(slashPos, slash1Pos); #endif FString name, extension; if (dotPos > slashPos && dotPos > 0) { name.SetFrom(fullProcessedPath, dotPos); extension = fullProcessedPath.Ptr(dotPos); } else name = fullProcessedPath; name += L'_'; unsigned left = 1, right = (1 << 30); while (left != right) { unsigned mid = (left + right) / 2; if (MakeAutoName(name, extension, mid, path)) left = mid + 1; else right = mid; } return !MakeAutoName(name, extension, right, fullProcessedPath); } src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.h000066400000000000000000000003051325366651500230550ustar00rootroot00000000000000// FilePathAutoRename.h #ifndef __FILE_PATH_AUTO_RENAME_H #define __FILE_PATH_AUTO_RENAME_H #include "../../Common/MyString.h" bool AutoRenamePath(FString &fullProcessedPath); #endif src/libs/7zip/unix/CPP/7zip/Common/FileStreams.cpp000066400000000000000000000243741325366651500221650ustar00rootroot00000000000000// FileStreams.cpp #include "StdAfx.h" #ifndef _WIN32 #include #include #include #endif #ifdef SUPPORT_DEVICE_FILE #include "../../../C/Alloc.h" #include "../../Common/Defs.h" #endif #include "FileStreams.h" static inline HRESULT ConvertBoolToHRESULT(bool result) { #ifdef _WIN32 if (result) return S_OK; DWORD lastError = ::GetLastError(); if (lastError == 0) return E_FAIL; return HRESULT_FROM_WIN32(lastError); #else return result ? S_OK: E_FAIL; #endif } #ifdef SUPPORT_DEVICE_FILE static const UInt32 kClusterSize = 1 << 18; CInFileStream::CInFileStream(): VirtPos(0), PhyPos(0), Buf(0), BufSize(0), SupportHardLinks(false) { } #endif CInFileStream::~CInFileStream() { #ifdef SUPPORT_DEVICE_FILE MidFree(Buf); #endif } STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { #ifdef USE_WIN_FILE #ifdef SUPPORT_DEVICE_FILE if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (File.IsDeviceFile) { if (File.SizeDefined) { if (VirtPos >= File.Size) return VirtPos == File.Size ? S_OK : E_FAIL; UInt64 rem = File.Size - VirtPos; if (size > rem) size = (UInt32)rem; } for (;;) { const UInt32 mask = kClusterSize - 1; const UInt64 mask2 = ~(UInt64)mask; UInt64 alignedPos = VirtPos & mask2; if (BufSize > 0 && BufStartPos == alignedPos) { UInt32 pos = (UInt32)VirtPos & mask; if (pos >= BufSize) return S_OK; UInt32 rem = MyMin(BufSize - pos, size); memcpy(data, Buf + pos, rem); VirtPos += rem; if (processedSize) *processedSize += rem; return S_OK; } bool useBuf = false; if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 ) useBuf = true; else { UInt64 end = VirtPos + size; if ((end & mask) != 0) { end &= mask2; if (end <= VirtPos) useBuf = true; else size = (UInt32)(end - VirtPos); } } if (!useBuf) break; if (alignedPos != PhyPos) { UInt64 realNewPosition; bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition); if (!result) return ConvertBoolToHRESULT(result); PhyPos = realNewPosition; } BufStartPos = alignedPos; UInt32 readSize = kClusterSize; if (File.SizeDefined) readSize = (UInt32)MyMin(File.Size - PhyPos, (UInt64)kClusterSize); if (!Buf) { Buf = (Byte *)MidAlloc(kClusterSize); if (!Buf) return E_OUTOFMEMORY; } bool result = File.Read1(Buf, readSize, BufSize); if (!result) return ConvertBoolToHRESULT(result); if (BufSize == 0) return S_OK; PhyPos += BufSize; } if (VirtPos != PhyPos) { UInt64 realNewPosition; bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition); if (!result) return ConvertBoolToHRESULT(result); PhyPos = VirtPos = realNewPosition; } } #endif UInt32 realProcessedSize; bool result = File.ReadPart(data, size, realProcessedSize); if (processedSize) *processedSize = realProcessedSize; #ifdef SUPPORT_DEVICE_FILE VirtPos += realProcessedSize; PhyPos += realProcessedSize; #endif return ConvertBoolToHRESULT(result); #else if (processedSize) *processedSize = 0; ssize_t res = File.Read(data, (size_t)size); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; #endif } #ifdef UNDER_CE STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { size_t s2 = fread(data, 1, size, stdin); int error = ferror(stdin); if (processedSize) *processedSize = s2; if (s2 <= size && error == 0) return S_OK; return E_FAIL; } #else STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { #ifdef _WIN32 DWORD realProcessedSize; UInt32 sizeTemp = (1 << 20); if (sizeTemp > size) sizeTemp = size; BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL); if (processedSize) *processedSize = realProcessedSize; if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE) return S_OK; return ConvertBoolToHRESULT(res != FALSE); #else if (processedSize) *processedSize = 0; ssize_t res; do { res = read(0, data, (size_t)size); } while (res < 0 && (errno == EINTR)); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; #endif } #endif STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; #ifdef USE_WIN_FILE #ifdef SUPPORT_DEVICE_FILE if (File.IsDeviceFile && (File.SizeDefined || seekOrigin != STREAM_SEEK_END)) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += VirtPos; break; case STREAM_SEEK_END: offset += File.Size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; VirtPos = offset; if (newPosition) *newPosition = offset; return S_OK; } #endif UInt64 realNewPosition; bool result = File.Seek(offset, seekOrigin, realNewPosition); #ifdef SUPPORT_DEVICE_FILE PhyPos = VirtPos = realNewPosition; #endif if (newPosition) *newPosition = realNewPosition; return ConvertBoolToHRESULT(result); #else off_t res = File.Seek((off_t)offset, seekOrigin); if (res == -1) return E_FAIL; if (newPosition) *newPosition = (UInt64)res; return S_OK; #endif } STDMETHODIMP CInFileStream::GetSize(UInt64 *size) { return ConvertBoolToHRESULT(File.GetLength(*size)); } #if 0 // FIXME #ifdef USE_WIN_FILE STDMETHODIMP CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) { BY_HANDLE_FILE_INFORMATION info; if (File.GetFileInformation(&info)) { if (size) *size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; if (cTime) *cTime = info.ftCreationTime; if (aTime) *aTime = info.ftLastAccessTime; if (mTime) *mTime = info.ftLastWriteTime; if (attrib) *attrib = info.dwFileAttributes; return S_OK; } return GetLastError(); } STDMETHODIMP CInFileStream::GetProps2(CStreamFileProps *props) { BY_HANDLE_FILE_INFORMATION info; if (File.GetFileInformation(&info)) { props->Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; props->VolID = info.dwVolumeSerialNumber; props->FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow; props->FileID_High = 0; props->NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1; props->Attrib = info.dwFileAttributes; props->CTime = info.ftCreationTime; props->ATime = info.ftLastAccessTime; props->MTime = info.ftLastWriteTime; return S_OK; } return GetLastError(); } #endif ////////////////////////// // COutFileStream HRESULT COutFileStream::Close() { return ConvertBoolToHRESULT(File.Close()); } STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { #ifdef USE_WIN_FILE UInt32 realProcessedSize; bool result = File.WritePart(data, size, realProcessedSize); ProcessedSize += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return ConvertBoolToHRESULT(result); #else if (processedSize) *processedSize = 0; ssize_t res = File.Write(data, (size_t)size); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; ProcessedSize += res; return S_OK; #endif } STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; #ifdef USE_WIN_FILE UInt64 realNewPosition; bool result = File.Seek(offset, seekOrigin, realNewPosition); if (newPosition) *newPosition = realNewPosition; return ConvertBoolToHRESULT(result); #else off_t res = File.Seek((off_t)offset, seekOrigin); if (res == -1) return E_FAIL; if (newPosition) *newPosition = (UInt64)res; return S_OK; #endif } STDMETHODIMP COutFileStream::SetSize(UInt64 newSize) { #ifdef USE_WIN_FILE UInt64 currentPos; if (!File.Seek(0, FILE_CURRENT, currentPos)) return E_FAIL; bool result = File.SetLength(newSize); UInt64 currentPos2; result = result && File.Seek(currentPos, currentPos2); return result ? S_OK : E_FAIL; #else return E_FAIL; #endif } #ifdef UNDER_CE STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { size_t s2 = fwrite(data, 1, size, stdout); if (processedSize) *processedSize = s2; return (s2 == size) ? S_OK : E_FAIL; } #else STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; #ifdef _WIN32 UInt32 realProcessedSize; BOOL res = TRUE; if (size > 0) { // Seems that Windows doesn't like big amounts writing to stdout. // So we limit portions by 32KB. UInt32 sizeTemp = (1 << 15); if (sizeTemp > size) sizeTemp = size; res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), data, sizeTemp, (DWORD *)&realProcessedSize, NULL); size -= realProcessedSize; data = (const void *)((const Byte *)data + realProcessedSize); if (processedSize) *processedSize += realProcessedSize; } return ConvertBoolToHRESULT(res != FALSE); #else ssize_t res; do { res = write(1, data, (size_t)size); } while (res < 0 && (errno == EINTR)); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; return S_OK; #endif } #endif src/libs/7zip/unix/CPP/7zip/Common/FileStreams.h000066400000000000000000000063721325366651500216300ustar00rootroot00000000000000// FileStreams.h #ifndef __FILE_STREAMS_H #define __FILE_STREAMS_H #if defined(_WIN32) || defined(ENV_UNIX) #define USE_WIN_FILE #endif #include "../../Common/MyString.h" #ifdef USE_WIN_FILE #include "../../Windows/FileIO.h" #else #include "../../Common/C_FileIO.h" #endif #include "../../Common/MyCom.h" #include "../IStream.h" class CInFileStream: public IInStream, public IStreamGetSize, #if 0 // #ifdef USE_WIN_FILE public IStreamGetProps, public IStreamGetProps2, #endif public CMyUnknownImp { bool _ignoreSymbolicLink; public: #ifdef USE_WIN_FILE NWindows::NFile::NIO::CInFile File; #ifdef SUPPORT_DEVICE_FILE UInt64 VirtPos; UInt64 PhyPos; UInt64 BufStartPos; Byte *Buf; UInt32 BufSize; #endif #else NC::NFile::NIO::CInFile File; #endif bool SupportHardLinks; CInFileStream(bool b=false) { _ignoreSymbolicLink = b; } virtual ~CInFileStream(); bool Open(CFSTR fileName) { return File.Open(fileName,_ignoreSymbolicLink); } bool OpenShared(CFSTR fileName , bool /* shareForWrite */ ) { return File.Open(fileName,_ignoreSymbolicLink); } MY_QUERYINTERFACE_BEGIN2(IInStream) MY_QUERYINTERFACE_ENTRY(IStreamGetSize) #if 0 // #ifdef USE_WIN_FILE MY_QUERYINTERFACE_ENTRY(IStreamGetProps) MY_QUERYINTERFACE_ENTRY(IStreamGetProps2) #endif MY_QUERYINTERFACE_END MY_ADDREF_RELEASE STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(GetSize)(UInt64 *size); #if 0 // #ifdef USE_WIN_FILE STDMETHOD(GetProps)(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib); STDMETHOD(GetProps2)(CStreamFileProps *props); #endif }; class CStdInFileStream: public ISequentialInStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP virtual ~CStdInFileStream() {} STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; class COutFileStream: public IOutStream, public CMyUnknownImp { public: #ifdef USE_WIN_FILE NWindows::NFile::NIO::COutFile File; #else NC::NFile::NIO::COutFile File; #endif virtual ~COutFileStream() {} bool Create(CFSTR fileName, bool createAlways) { ProcessedSize = 0; return File.Create(fileName, createAlways); } bool Open(CFSTR fileName, DWORD creationDisposition) { ProcessedSize = 0; return File.Open(fileName, creationDisposition); } HRESULT Close(); UInt64 ProcessedSize; #ifdef USE_WIN_FILE bool SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) { return File.SetTime(cTime, aTime, mTime); } bool SetMTime(const FILETIME *mTime) { return File.SetMTime(mTime); } #endif MY_UNKNOWN_IMP1(IOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(SetSize)(UInt64 newSize); }; class CStdOutFileStream: public ISequentialOutStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP virtual ~CStdOutFileStream() {} STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; #endif src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.cpp000066400000000000000000000151661325366651500221500ustar00rootroot00000000000000// FilterCoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../../Common/Defs.h" #include "FilterCoder.h" #include "StreamUtils.h" static const UInt32 kBufferSize = 1 << 17; CFilterCoder::CFilterCoder() { _buffer = (Byte *)::MidAlloc(kBufferSize); if (_buffer == 0) throw 1; } CFilterCoder::~CFilterCoder() { ::MidFree(_buffer); } HRESULT CFilterCoder::WriteWithLimit(ISequentialOutStream *outStream, UInt32 size) { if (_outSizeIsDefined) { UInt64 remSize = _outSize - _nowPos64; if (size > remSize) size = (UInt32)remSize; } RINOK(WriteStream(outStream, _buffer, size)); _nowPos64 += size; return S_OK; } STDMETHODIMP CFilterCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { RINOK(Init()); UInt32 bufferPos = 0; _outSizeIsDefined = (outSize != 0); if (_outSizeIsDefined) _outSize = *outSize; while (!_outSizeIsDefined || _nowPos64 < _outSize) { size_t processedSize = kBufferSize - bufferPos; // Change it: It can be optimized using ReadPart RINOK(ReadStream(inStream, _buffer + bufferPos, &processedSize)); UInt32 endPos = bufferPos + (UInt32)processedSize; bufferPos = Filter->Filter(_buffer, endPos); if (bufferPos > endPos) { for (; endPos < bufferPos; endPos++) _buffer[endPos] = 0; bufferPos = Filter->Filter(_buffer, endPos); } if (bufferPos == 0) { if (endPos == 0) return S_OK; return WriteWithLimit(outStream, endPos); } RINOK(WriteWithLimit(outStream, bufferPos)); if (progress != NULL) { RINOK(progress->SetRatioInfo(&_nowPos64, &_nowPos64)); } UInt32 i = 0; while (bufferPos < endPos) _buffer[i++] = _buffer[bufferPos++]; bufferPos = i; } return S_OK; } STDMETHODIMP CFilterCoder::SetOutStream(ISequentialOutStream *outStream) { _bufferPos = 0; _outStream = outStream; return Init(); } STDMETHODIMP CFilterCoder::ReleaseOutStream() { _outStream.Release(); return S_OK; } STDMETHODIMP CFilterCoder::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { UInt32 sizeTemp = MyMin(size, kBufferSize - _bufferPos); memcpy(_buffer + _bufferPos, data, sizeTemp); size -= sizeTemp; if (processedSize != NULL) *processedSize += sizeTemp; data = (const Byte *)data + sizeTemp; UInt32 endPos = _bufferPos + sizeTemp; _bufferPos = Filter->Filter(_buffer, endPos); if (_bufferPos == 0) { _bufferPos = endPos; break; } if (_bufferPos > endPos) { if (size != 0) return E_FAIL; break; } RINOK(WriteWithLimit(_outStream, _bufferPos)); UInt32 i = 0; while (_bufferPos < endPos) _buffer[i++] = _buffer[_bufferPos++]; _bufferPos = i; } return S_OK; } STDMETHODIMP CFilterCoder::Flush() { if (_bufferPos != 0) { // _buffer contains only data refused by previous Filter->Filter call. UInt32 endPos = Filter->Filter(_buffer, _bufferPos); if (endPos > _bufferPos) { for (; _bufferPos < endPos; _bufferPos++) _buffer[_bufferPos] = 0; if (Filter->Filter(_buffer, endPos) != endPos) return E_FAIL; } RINOK(WriteWithLimit(_outStream, _bufferPos)); _bufferPos = 0; } CMyComPtr flush; _outStream.QueryInterface(IID_IOutStreamFlush, &flush); if (flush) return flush->Flush(); return S_OK; } void CFilterCoder::SetInStream_NoSubFilterInit(ISequentialInStream *inStream) { _convertedPosBegin = _convertedPosEnd = _bufferPos = 0; _inStream = inStream; Init2(); } STDMETHODIMP CFilterCoder::SetInStream(ISequentialInStream *inStream) { SetInStream_NoSubFilterInit(inStream); return Init(); } STDMETHODIMP CFilterCoder::ReleaseInStream() { _inStream.Release(); return S_OK; } STDMETHODIMP CFilterCoder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { if (_convertedPosBegin != _convertedPosEnd) { UInt32 sizeTemp = MyMin(size, _convertedPosEnd - _convertedPosBegin); memcpy(data, _buffer + _convertedPosBegin, sizeTemp); _convertedPosBegin += sizeTemp; data = (void *)((Byte *)data + sizeTemp); size -= sizeTemp; if (processedSize != NULL) *processedSize += sizeTemp; break; } UInt32 i; for (i = 0; _convertedPosEnd + i < _bufferPos; i++) _buffer[i] = _buffer[_convertedPosEnd + i]; _bufferPos = i; _convertedPosBegin = _convertedPosEnd = 0; size_t processedSizeTemp = kBufferSize - _bufferPos; RINOK(ReadStream(_inStream, _buffer + _bufferPos, &processedSizeTemp)); _bufferPos += (UInt32)processedSizeTemp; _convertedPosEnd = Filter->Filter(_buffer, _bufferPos); if (_convertedPosEnd == 0) { if (_bufferPos == 0) break; _convertedPosEnd = _bufferPos; // check it continue; } if (_convertedPosEnd > _bufferPos) { for (; _bufferPos < _convertedPosEnd; _bufferPos++) _buffer[_bufferPos] = 0; _convertedPosEnd = Filter->Filter(_buffer, _bufferPos); } } return S_OK; } #ifndef _NO_CRYPTO STDMETHODIMP CFilterCoder::CryptoSetPassword(const Byte *data, UInt32 size) { return _setPassword->CryptoSetPassword(data, size); } STDMETHODIMP CFilterCoder::SetKey(const Byte *data, UInt32 size) { return _cryptoProperties->SetKey(data, size); } STDMETHODIMP CFilterCoder::SetInitVector(const Byte *data, UInt32 size) { return _cryptoProperties->SetInitVector(data, size); } #endif #ifndef EXTRACT_ONLY STDMETHODIMP CFilterCoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *properties, UInt32 numProperties) { return _SetCoderProperties->SetCoderProperties(propIDs, properties, numProperties); } STDMETHODIMP CFilterCoder::WriteCoderProperties(ISequentialOutStream *outStream) { return _writeCoderProperties->WriteCoderProperties(outStream); } /* STDMETHODIMP CFilterCoder::ResetSalt() { return _CryptoResetSalt->ResetSalt(); } */ STDMETHODIMP CFilterCoder::ResetInitVector() { return _CryptoResetInitVector->ResetInitVector(); } #endif STDMETHODIMP CFilterCoder::SetDecoderProperties2(const Byte *data, UInt32 size) { return _setDecoderProperties->SetDecoderProperties2(data, size); } src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.h000066400000000000000000000106401325366651500216050ustar00rootroot00000000000000// FilterCoder.h #ifndef __FILTER_CODER_H #define __FILTER_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" #include "../IPassword.h" #define MY_QUERYINTERFACE_ENTRY_AG(i, sub0, sub) else if (iid == IID_ ## i) \ { if (!sub) RINOK(sub0->QueryInterface(IID_ ## i, (void **)&sub)) \ *outObject = (void *)(i *)this; } class CFilterCoder: public ICompressCoder, public ICompressSetInStream, public ISequentialInStream, public ICompressSetOutStream, public ISequentialOutStream, public IOutStreamFlush, #ifndef _NO_CRYPTO public ICryptoSetPassword, public ICryptoProperties, #endif #ifndef EXTRACT_ONLY public ICompressSetCoderProperties, public ICompressWriteCoderProperties, // public ICryptoResetSalt, public ICryptoResetInitVector, #endif public ICompressSetDecoderProperties2, public CMyUnknownImp { protected: Byte *_buffer; CMyComPtr _inStream; CMyComPtr _outStream; UInt32 _bufferPos; UInt32 _convertedPosBegin; UInt32 _convertedPosEnd; bool _outSizeIsDefined; UInt64 _outSize; UInt64 _nowPos64; void Init2() { _nowPos64 = 0; _outSizeIsDefined = false; } HRESULT Init() { Init2(); return Filter->Init(); } CMyComPtr _setPassword; CMyComPtr _cryptoProperties; #ifndef EXTRACT_ONLY CMyComPtr _SetCoderProperties; CMyComPtr _writeCoderProperties; // CMyComPtr _CryptoResetSalt; CMyComPtr _CryptoResetInitVector; #endif CMyComPtr _setDecoderProperties; public: CMyComPtr Filter; CFilterCoder(); ~CFilterCoder(); HRESULT WriteWithLimit(ISequentialOutStream *outStream, UInt32 size); public: MY_QUERYINTERFACE_BEGIN2(ICompressCoder) MY_QUERYINTERFACE_ENTRY(ICompressSetInStream) MY_QUERYINTERFACE_ENTRY(ISequentialInStream) MY_QUERYINTERFACE_ENTRY(ICompressSetOutStream) MY_QUERYINTERFACE_ENTRY(ISequentialOutStream) MY_QUERYINTERFACE_ENTRY(IOutStreamFlush) #ifndef _NO_CRYPTO MY_QUERYINTERFACE_ENTRY_AG(ICryptoSetPassword, Filter, _setPassword) MY_QUERYINTERFACE_ENTRY_AG(ICryptoProperties, Filter, _cryptoProperties) #endif #ifndef EXTRACT_ONLY MY_QUERYINTERFACE_ENTRY_AG(ICompressSetCoderProperties, Filter, _SetCoderProperties) MY_QUERYINTERFACE_ENTRY_AG(ICompressWriteCoderProperties, Filter, _writeCoderProperties) // MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetSalt, Filter, _CryptoResetSalt) MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetInitVector, Filter, _CryptoResetInitVector) #endif MY_QUERYINTERFACE_ENTRY_AG(ICompressSetDecoderProperties2, Filter, _setDecoderProperties) MY_QUERYINTERFACE_END MY_ADDREF_RELEASE STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(ReleaseInStream)(); STDMETHOD(SetInStream)(ISequentialInStream *inStream); STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); \ STDMETHOD(SetOutStream)(ISequentialOutStream *outStream); STDMETHOD(ReleaseOutStream)(); STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Flush)(); #ifndef _NO_CRYPTO STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size); STDMETHOD(SetKey)(const Byte *data, UInt32 size); STDMETHOD(SetInitVector)(const Byte *data, UInt32 size); #endif #ifndef EXTRACT_ONLY STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *properties, UInt32 numProperties); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); // STDMETHOD(ResetSalt)(); STDMETHOD(ResetInitVector)(); #endif STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); void SetInStream_NoSubFilterInit(ISequentialInStream *inStream); }; class CInStreamReleaser { public: CFilterCoder *FilterCoder; CInStreamReleaser(): FilterCoder(0) {} ~CInStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseInStream(); } }; class COutStreamReleaser { public: CFilterCoder *FilterCoder; COutStreamReleaser(): FilterCoder(0) {} ~COutStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseOutStream(); } }; #endif src/libs/7zip/unix/CPP/7zip/Common/InBuffer.cpp000066400000000000000000000050201325366651500214320ustar00rootroot00000000000000// InBuffer.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "InBuffer.h" CInBufferBase::CInBufferBase() throw(): _buf(0), _bufLim(0), _bufBase(0), _stream(0), _processedSize(0), _bufSize(0), _wasFinished(false), NumExtraBytes(0) {} bool CInBuffer::Create(size_t bufSize) throw() { const unsigned kMinBlockSize = 1; if (bufSize < kMinBlockSize) bufSize = kMinBlockSize; if (_bufBase != 0 && _bufSize == bufSize) return true; Free(); _bufSize = bufSize; _bufBase = (Byte *)::MidAlloc(bufSize); return (_bufBase != 0); } void CInBuffer::Free() throw() { ::MidFree(_bufBase); _bufBase = 0; } void CInBufferBase::Init() throw() { _processedSize = 0; _buf = _bufBase; _bufLim = _buf; _wasFinished = false; #ifdef _NO_EXCEPTIONS ErrorCode = S_OK; #endif NumExtraBytes = 0; } bool CInBufferBase::ReadBlock() { #ifdef _NO_EXCEPTIONS if (ErrorCode != S_OK) return false; #endif if (_wasFinished) return false; _processedSize += (_buf - _bufBase); _buf = _bufBase; _bufLim = _bufBase; UInt32 processed; // FIX_ME: we can improve it to support (_bufSize >= (1 << 32)) HRESULT result = _stream->Read(_bufBase, (UInt32)_bufSize, &processed); #ifdef _NO_EXCEPTIONS ErrorCode = result; #else if (result != S_OK) throw CInBufferException(result); #endif _bufLim = _buf + processed; _wasFinished = (processed == 0); return !_wasFinished; } bool CInBufferBase::ReadByte_FromNewBlock(Byte &b) { if (!ReadBlock()) { NumExtraBytes++; b = 0xFF; return false; } b = *_buf++; return true; } Byte CInBufferBase::ReadByte_FromNewBlock() { if (!ReadBlock()) { NumExtraBytes++; return 0xFF; } return *_buf++; } size_t CInBufferBase::ReadBytes(Byte *buf, size_t size) { if ((size_t)(_bufLim - _buf) >= size) { const Byte *src = _buf; for (size_t i = 0; i < size; i++) buf[i] = src[i]; _buf += size; return size; } for (size_t i = 0; i < size; i++) { if (_buf >= _bufLim) if (!ReadBlock()) return i; buf[i] = *_buf++; } return size; } size_t CInBufferBase::Skip(size_t size) { size_t processed = 0; for (;;) { size_t rem = (_bufLim - _buf); if (rem >= size) { _buf += size; return processed + size; } _buf += rem; processed += rem; size -= rem; if (!ReadBlock()) return processed; } } src/libs/7zip/unix/CPP/7zip/Common/InBuffer.h000066400000000000000000000037551325366651500211140ustar00rootroot00000000000000// InBuffer.h #ifndef __IN_BUFFER_H #define __IN_BUFFER_H #include "../../Common/MyException.h" #include "../IStream.h" #ifndef _NO_EXCEPTIONS struct CInBufferException: public CSystemException { CInBufferException(HRESULT errorCode): CSystemException(errorCode) {} }; #endif class CInBufferBase { protected: Byte *_buf; Byte *_bufLim; Byte *_bufBase; ISequentialInStream *_stream; UInt64 _processedSize; size_t _bufSize; // actually it's number of Bytes for next read. The buf can be larger // only up to 32-bits values now are supported! bool _wasFinished; bool ReadBlock(); bool ReadByte_FromNewBlock(Byte &b); Byte ReadByte_FromNewBlock(); public: #ifdef _NO_EXCEPTIONS HRESULT ErrorCode; #endif UInt32 NumExtraBytes; CInBufferBase() throw(); UInt64 GetStreamSize() const { return _processedSize + (_buf - _bufBase); } UInt64 GetProcessedSize() const { return _processedSize + NumExtraBytes + (_buf - _bufBase); } bool WasFinished() const { return _wasFinished; } void SetStream(ISequentialInStream *stream) { _stream = stream; } void SetBuf(Byte *buf, size_t bufSize, size_t end, size_t pos) { _bufBase = buf; _bufSize = bufSize; _processedSize = 0; _buf = buf + pos; _bufLim = buf + end; _wasFinished = false; #ifdef _NO_EXCEPTIONS ErrorCode = S_OK; #endif NumExtraBytes = 0; } void Init() throw(); bool ReadByte(Byte &b) { if (_buf >= _bufLim) return ReadByte_FromNewBlock(b); b = *_buf++; return true; } Byte ReadByte() { if (_buf >= _bufLim) return ReadByte_FromNewBlock(); return *_buf++; } size_t ReadBytes(Byte *buf, size_t size); size_t Skip(size_t size); }; class CInBuffer: public CInBufferBase { public: ~CInBuffer() { Free(); } bool Create(size_t bufSize) throw(); // only up to 32-bits values now are supported! void Free() throw(); }; #endif src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.cpp000066400000000000000000000051221325366651500227530ustar00rootroot00000000000000// InOutTempBuffer.cpp #include "StdAfx.h" #include "../../../C/7zCrc.h" #include "../../Common/Defs.h" #include "InOutTempBuffer.h" #include "StreamUtils.h" using namespace NWindows; using namespace NFile; using namespace NDir; static const UInt32 kTempBufSize = (1 << 20); static CFSTR kTempFilePrefixString = FTEXT("7zt"); CInOutTempBuffer::CInOutTempBuffer(): _buf(NULL) { } void CInOutTempBuffer::Create() { if (!_buf) _buf = new Byte[kTempBufSize]; } CInOutTempBuffer::~CInOutTempBuffer() { delete []_buf; } void CInOutTempBuffer::InitWriting() { _bufPos = 0; _tempFileCreated = false; _size = 0; _crc = CRC_INIT_VAL; } bool CInOutTempBuffer::WriteToFile(const void *data, UInt32 size) { if (size == 0) return true; if (!_tempFileCreated) { if (!_tempFile.CreateRandomInTempFolder(kTempFilePrefixString, &_outFile)) return false; _tempFileCreated = true; } UInt32 processed; if (!_outFile.Write(data, size, processed)) return false; _crc = CrcUpdate(_crc, data, processed); _size += processed; return (processed == size); } bool CInOutTempBuffer::Write(const void *data, UInt32 size) { if (_bufPos < kTempBufSize) { UInt32 cur = MyMin(kTempBufSize - _bufPos, size); memcpy(_buf + _bufPos, data, cur); _crc = CrcUpdate(_crc, data, cur); _bufPos += cur; size -= cur; data = ((const Byte *)data) + cur; _size += cur; } return WriteToFile(data, size); } HRESULT CInOutTempBuffer::WriteToStream(ISequentialOutStream *stream) { if (!_outFile.Close()) return E_FAIL; UInt64 size = 0; UInt32 crc = CRC_INIT_VAL; if (_bufPos > 0) { RINOK(WriteStream(stream, _buf, _bufPos)); crc = CrcUpdate(crc, _buf, _bufPos); size += _bufPos; } if (_tempFileCreated) { NIO::CInFile inFile; if (!inFile.Open(_tempFile.GetPath())) return E_FAIL; while (size < _size) { UInt32 processed; if (!inFile.ReadPart(_buf, kTempBufSize, processed)) return E_FAIL; if (processed == 0) break; RINOK(WriteStream(stream, _buf, processed)); crc = CrcUpdate(crc, _buf, processed); size += processed; } } return (_crc == crc && size == _size) ? S_OK : E_FAIL; } STDMETHODIMP CSequentialOutTempBufferImp::Write(const void *data, UInt32 size, UInt32 *processed) { if (!_buf->Write(data, size)) { if (processed != NULL) *processed = 0; return E_FAIL; } if (processed != NULL) *processed = size; return S_OK; } src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.h000066400000000000000000000017611325366651500224250ustar00rootroot00000000000000// InOutTempBuffer.h #ifndef __IN_OUT_TEMP_BUFFER_H #define __IN_OUT_TEMP_BUFFER_H #include "../../Common/MyCom.h" #include "../../Windows/FileDir.h" #include "../IStream.h" class CInOutTempBuffer { NWindows::NFile::NDir::CTempFile _tempFile; NWindows::NFile::NIO::COutFile _outFile; Byte *_buf; UInt32 _bufPos; bool _tempFileCreated; UInt64 _size; UInt32 _crc; bool WriteToFile(const void *data, UInt32 size); public: CInOutTempBuffer(); ~CInOutTempBuffer(); void Create(); void InitWriting(); bool Write(const void *data, UInt32 size); HRESULT WriteToStream(ISequentialOutStream *stream); UInt64 GetDataSize() const { return _size; } }; class CSequentialOutTempBufferImp: public ISequentialOutStream, public CMyUnknownImp { CInOutTempBuffer *_buf; public: void Init(CInOutTempBuffer *buffer) { _buf = buffer; } MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; #endif src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.cpp000066400000000000000000000222241325366651500226650ustar00rootroot00000000000000// LimitedStreams.cpp #include "StdAfx.h" #include "LimitedStreams.h" #include "../../Common/Defs.h" STDMETHODIMP CLimitedSequentialInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize = 0; UInt32 sizeToRead = (UInt32)MyMin((_size - _pos), (UInt64)size); HRESULT result = S_OK; if (sizeToRead > 0) { result = _stream->Read(data, sizeToRead, &realProcessedSize); _pos += realProcessedSize; if (realProcessedSize == 0) _wasFinished = true; } if (processedSize) *processedSize = realProcessedSize; return result; } STDMETHODIMP CLimitedInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= _size) { // 9.31: Fixed. Windows doesn't return error in ReadFile and IStream->Read in that case. return S_OK; // return (_virtPos == _size) ? S_OK: E_FAIL; // ERROR_HANDLE_EOF } UInt64 rem = _size - _virtPos; if (rem < size) size = (UInt32)rem; UInt64 newPos = _startOffset + _virtPos; if (newPos != _physPos) { _physPos = newPos; RINOK(SeekToPhys()); } HRESULT res = _stream->Read(data, size, &size); if (processedSize) *processedSize = size; _physPos += size; _virtPos += size; return res; } STDMETHODIMP CLimitedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return S_OK; } HRESULT CreateLimitedInStream(IInStream *inStream, UInt64 pos, UInt64 size, ISequentialInStream **resStream) { *resStream = 0; CLimitedInStream *streamSpec = new CLimitedInStream; CMyComPtr streamTemp = streamSpec; streamSpec->SetStream(inStream); RINOK(streamSpec->InitAndSeek(pos, size)); streamSpec->SeekToStart(); *resStream = streamTemp.Detach(); return S_OK; } STDMETHODIMP CClusterInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= Size) return S_OK; if (_curRem == 0) { UInt32 blockSize = (UInt32)1 << BlockSizeLog; UInt32 virtBlock = (UInt32)(_virtPos >> BlockSizeLog); UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1); UInt32 phyBlock = Vector[virtBlock]; UInt64 newPos = StartOffset + ((UInt64)phyBlock << BlockSizeLog) + offsetInBlock; if (newPos != _physPos) { _physPos = newPos; RINOK(SeekToPhys()); } _curRem = blockSize - offsetInBlock; for (int i = 1; i < 64 && (virtBlock + i) < (UInt32)Vector.Size() && phyBlock + i == Vector[virtBlock + i]; i++) _curRem += (UInt32)1 << BlockSizeLog; UInt64 rem = Size - _virtPos; if (_curRem > rem) _curRem = (UInt32)rem; } if (size > _curRem) size = _curRem; HRESULT res = Stream->Read(data, size, &size); if (processedSize) *processedSize = size; _physPos += size; _virtPos += size; _curRem -= size; return res; } STDMETHODIMP CClusterInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += Size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; if (_virtPos != (UInt64)offset) _curRem = 0; _virtPos = offset; if (newPosition) *newPosition = offset; return S_OK; } STDMETHODIMP CExtentsStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= Extents.Back().Virt) return S_OK; if (size == 0) return S_OK; unsigned left = 0, right = Extents.Size() - 1; for (;;) { unsigned mid = (left + right) / 2; if (mid == left) break; if (_virtPos < Extents[mid].Virt) right = mid; else left = mid; } const CSeekExtent &extent = Extents[left]; UInt64 phyPos = extent.Phy + (_virtPos - extent.Virt); if (_needStartSeek || _phyPos != phyPos) { _needStartSeek = false; _phyPos = phyPos; RINOK(SeekToPhys()); } UInt64 rem = Extents[left + 1].Virt - _virtPos; if (size > rem) size = (UInt32)rem; HRESULT res = Stream->Read(data, size, &size); _phyPos += size; _virtPos += size; if (processedSize) *processedSize = size; return res; } STDMETHODIMP CExtentsStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += Extents.Back().Virt; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return S_OK; } STDMETHODIMP CLimitedSequentialOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { HRESULT result = S_OK; if (processedSize) *processedSize = 0; if (size > _size) { if (_size == 0) { _overflow = true; if (!_overflowIsAllowed) return E_FAIL; if (processedSize) *processedSize = size; return S_OK; } size = (UInt32)_size; } if (_stream) result = _stream->Write(data, size, &size); _size -= size; if (processedSize) *processedSize = size; return result; } STDMETHODIMP CTailInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 cur; HRESULT res = Stream->Read(data, size, &cur); if (processedSize) *processedSize = cur; _virtPos += cur; return res; } STDMETHODIMP CTailInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: { UInt64 pos = 0; RINOK(Stream->Seek(offset, STREAM_SEEK_END, &pos)); if (pos < Offset) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = pos - Offset; if (newPosition) *newPosition = _virtPos; return S_OK; } default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return Stream->Seek(Offset + _virtPos, STREAM_SEEK_SET, NULL); } STDMETHODIMP CLimitedCachedInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= _size) { // 9.31: Fixed. Windows doesn't return error in ReadFile and IStream->Read in that case. return S_OK; // return (_virtPos == _size) ? S_OK: E_FAIL; // ERROR_HANDLE_EOF } UInt64 rem = _size - _virtPos; if (rem < size) size = (UInt32)rem; UInt64 newPos = _startOffset + _virtPos; UInt64 offsetInCache = newPos - _cachePhyPos; HRESULT res = S_OK; if (newPos >= _cachePhyPos && offsetInCache <= _cacheSize && size <= _cacheSize - (size_t)offsetInCache) memcpy(data, _cache + (size_t)offsetInCache, size); else { if (newPos != _physPos) { _physPos = newPos; RINOK(SeekToPhys()); } res = _stream->Read(data, size, &size); _physPos += size; } if (processedSize) *processedSize = size; _virtPos += size; return res; } STDMETHODIMP CLimitedCachedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return S_OK; } STDMETHODIMP CTailOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { UInt32 cur; HRESULT res = Stream->Write(data, size, &cur); if (processedSize) *processedSize = cur; _virtPos += cur; if (_virtSize < _virtPos) _virtSize = _virtPos; return res; } STDMETHODIMP CTailOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += _virtSize; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return Stream->Seek(Offset + _virtPos, STREAM_SEEK_SET, NULL); } STDMETHODIMP CTailOutStream::SetSize(UInt64 newSize) { _virtSize = newSize; return Stream->SetSize(Offset + newSize); } src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.h000066400000000000000000000136631325366651500223410ustar00rootroot00000000000000// LimitedStreams.h #ifndef __LIMITED_STREAMS_H #define __LIMITED_STREAMS_H #include "../../Common/MyBuffer.h" #include "../../Common/MyCom.h" #include "../../Common/MyVector.h" #include "../IStream.h" class CLimitedSequentialInStream: public ISequentialInStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; UInt64 _pos; bool _wasFinished; public: void SetStream(ISequentialInStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(UInt64 streamSize) { _size = streamSize; _pos = 0; _wasFinished = false; } MY_UNKNOWN_IMP1(ISequentialInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); UInt64 GetSize() const { return _pos; } bool WasFinished() const { return _wasFinished; } }; class CLimitedInStream: public IInStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _virtPos; UInt64 _physPos; UInt64 _size; UInt64 _startOffset; HRESULT SeekToPhys() { return _stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } public: void SetStream(IInStream *stream) { _stream = stream; } HRESULT InitAndSeek(UInt64 startOffset, UInt64 size) { _startOffset = startOffset; _physPos = startOffset; _virtPos = 0; _size = size; return SeekToPhys(); } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); HRESULT SeekToStart() { return Seek(0, STREAM_SEEK_SET, NULL); } }; HRESULT CreateLimitedInStream(IInStream *inStream, UInt64 pos, UInt64 size, ISequentialInStream **resStream); class CClusterInStream: public IInStream, public CMyUnknownImp { UInt64 _virtPos; UInt64 _physPos; UInt32 _curRem; public: CMyComPtr Stream; UInt64 StartOffset; UInt64 Size; unsigned BlockSizeLog; CRecordVector Vector; HRESULT SeekToPhys() { return Stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } HRESULT InitAndSeek() { _curRem = 0; _virtPos = 0; _physPos = StartOffset; if (Vector.Size() > 0) { _physPos = StartOffset + (Vector[0] << BlockSizeLog); return SeekToPhys(); } return S_OK; } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; struct CSeekExtent { UInt64 Phy; UInt64 Virt; }; class CExtentsStream: public IInStream, public CMyUnknownImp { UInt64 _phyPos; UInt64 _virtPos; bool _needStartSeek; HRESULT SeekToPhys() { return Stream->Seek(_phyPos, STREAM_SEEK_SET, NULL); } public: CMyComPtr Stream; CRecordVector Extents; MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); void ReleaseStream() { Stream.Release(); } void Init() { _virtPos = 0; _phyPos = 0; _needStartSeek = true; } }; class CLimitedSequentialOutStream: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; bool _overflow; bool _overflowIsAllowed; public: MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); void SetStream(ISequentialOutStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(UInt64 size, bool overflowIsAllowed = false) { _size = size; _overflow = false; _overflowIsAllowed = overflowIsAllowed; } bool IsFinishedOK() const { return (_size == 0 && !_overflow); } UInt64 GetRem() const { return _size; } }; class CTailInStream: public IInStream, public CMyUnknownImp { UInt64 _virtPos; public: CMyComPtr Stream; UInt64 Offset; void Init() { _virtPos = 0; } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); HRESULT SeekToStart() { return Stream->Seek(Offset, STREAM_SEEK_SET, NULL); } }; class CLimitedCachedInStream: public IInStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _virtPos; UInt64 _physPos; UInt64 _size; UInt64 _startOffset; const Byte *_cache; size_t _cacheSize; size_t _cachePhyPos; HRESULT SeekToPhys() { return _stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } public: CByteBuffer Buffer; void SetStream(IInStream *stream) { _stream = stream; } void SetCache(size_t cacheSize, size_t cachePos) { _cache = Buffer; _cacheSize = cacheSize; _cachePhyPos = cachePos; } HRESULT InitAndSeek(UInt64 startOffset, UInt64 size) { _startOffset = startOffset; _physPos = startOffset; _virtPos = 0; _size = size; return SeekToPhys(); } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); HRESULT SeekToStart() { return Seek(0, STREAM_SEEK_SET, NULL); } }; class CTailOutStream: public IOutStream, public CMyUnknownImp { UInt64 _virtPos; UInt64 _virtSize; public: CMyComPtr Stream; UInt64 Offset; virtual ~CTailOutStream() {} MY_UNKNOWN_IMP2(ISequentialOutStream, IOutStream) void Init() { _virtPos = 0; _virtSize = 0; } STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(SetSize)(UInt64 newSize); }; #endif src/libs/7zip/unix/CPP/7zip/Common/LockedStream.cpp000066400000000000000000000013001325366651500223040ustar00rootroot00000000000000// LockedStream.cpp #include "StdAfx.h" #include "LockedStream.h" HRESULT CLockedInStream::Read(UInt64 startPos, void *data, UInt32 size, UInt32 *processedSize) { NWindows::NSynchronization::CCriticalSectionLock lock(_criticalSection); RINOK(_stream->Seek(startPos, STREAM_SEEK_SET, NULL)); return _stream->Read(data, size, processedSize); } STDMETHODIMP CLockedSequentialInStreamImp::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize = 0; HRESULT result = _lockedInStream->Read(_pos, data, size, &realProcessedSize); _pos += realProcessedSize; if (processedSize != NULL) *processedSize = realProcessedSize; return result; } src/libs/7zip/unix/CPP/7zip/Common/LockedStream.h000066400000000000000000000015271325366651500217640ustar00rootroot00000000000000// LockedStream.h #ifndef __LOCKEDSTREAM_H #define __LOCKEDSTREAM_H #include "../../Windows/Synchronization.h" #include "../../Common/MyCom.h" #include "../IStream.h" class CLockedInStream { CMyComPtr _stream; NWindows::NSynchronization::CCriticalSection _criticalSection; public: void Init(IInStream *stream) { _stream = stream; } HRESULT Read(UInt64 startPos, void *data, UInt32 size, UInt32 *processedSize); }; class CLockedSequentialInStreamImp: public ISequentialInStream, public CMyUnknownImp { CLockedInStream *_lockedInStream; UInt64 _pos; public: void Init(CLockedInStream *lockedInStream, UInt64 startPos) { _lockedInStream = lockedInStream; _pos = startPos; } MY_UNKNOWN_IMP STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; #endif src/libs/7zip/unix/CPP/7zip/Common/MethodId.h000066400000000000000000000002211325366651500210720ustar00rootroot00000000000000// MethodId.h #ifndef __7Z_METHOD_ID_H #define __7Z_METHOD_ID_H #include "../../Common/MyTypes.h" typedef UInt64 CMethodId; #endif src/libs/7zip/unix/CPP/7zip/Common/MethodProps.cpp000066400000000000000000000236671325366651500222170ustar00rootroot00000000000000// MethodProps.cpp #include "StdAfx.h" #include "../../Common/StringToInt.h" #include "MethodProps.h" using namespace NWindows; bool StringToBool(const UString &s, bool &res) { if (s.IsEmpty() || s == L"+" || StringsAreEqualNoCase_Ascii(s, "ON")) { res = true; return true; } if (s == L"-" || StringsAreEqualNoCase_Ascii(s, "OFF")) { res = false; return true; } return false; } HRESULT PROPVARIANT_to_bool(const PROPVARIANT &prop, bool &dest) { switch (prop.vt) { case VT_EMPTY: dest = true; return S_OK; case VT_BOOL: dest = (prop.boolVal != VARIANT_FALSE); return S_OK; case VT_BSTR: return StringToBool(prop.bstrVal, dest) ? S_OK : E_INVALIDARG; } return E_INVALIDARG; } unsigned ParseStringToUInt32(const UString &srcString, UInt32 &number) { const wchar_t *start = srcString; const wchar_t *end; number = ConvertStringToUInt32(start, &end); return (unsigned)(end - start); } HRESULT ParsePropToUInt32(const UString &name, const PROPVARIANT &prop, UInt32 &resValue) { // =VT_UI4 // =VT_EMPTY // {stringUInt32}=VT_EMPTY if (prop.vt == VT_UI4) { if (!name.IsEmpty()) return E_INVALIDARG; resValue = prop.ulVal; return S_OK; } if (prop.vt != VT_EMPTY) return E_INVALIDARG; if (name.IsEmpty()) return S_OK; UInt32 v; if (ParseStringToUInt32(name, v) != name.Len()) return E_INVALIDARG; resValue = v; return S_OK; } HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads) { if (name.IsEmpty()) { switch (prop.vt) { case VT_UI4: numThreads = prop.ulVal; break; default: { bool val; RINOK(PROPVARIANT_to_bool(prop, val)); numThreads = (val ? defaultNumThreads : 1); break; } } return S_OK; } if (prop.vt != VT_EMPTY) return E_INVALIDARG; return ParsePropToUInt32(name, prop, numThreads); } static HRESULT StringToDictSize(const UString &s, UInt32 &dicSize) { const wchar_t *end; UInt32 number = ConvertStringToUInt32(s, &end); unsigned numDigits = (unsigned)(end - s); if (numDigits == 0 || s.Len() > numDigits + 1) return E_INVALIDARG; const unsigned kLogDictSizeLimit = 32; if (s.Len() == numDigits) { if (number >= kLogDictSizeLimit) return E_INVALIDARG; dicSize = (UInt32)1 << (unsigned)number; return S_OK; } unsigned numBits; switch (MyCharLower_Ascii(s[numDigits])) { case 'b': dicSize = number; return S_OK; case 'k': numBits = 10; break; case 'm': numBits = 20; break; case 'g': numBits = 30; break; default: return E_INVALIDARG; } if (number >= ((UInt32)1 << (kLogDictSizeLimit - numBits))) return E_INVALIDARG; dicSize = number << numBits; return S_OK; } static HRESULT PROPVARIANT_to_DictSize(const PROPVARIANT &prop, UInt32 &resValue) { if (prop.vt == VT_UI4) { UInt32 v = prop.ulVal; if (v >= 32) return E_INVALIDARG; resValue = (UInt32)1 << v; return S_OK; } if (prop.vt == VT_BSTR) return StringToDictSize(prop.bstrVal, resValue); return E_INVALIDARG; } void CProps::AddProp32(PROPID propid, UInt32 level) { CProp prop; prop.IsOptional = true; prop.Id = propid; prop.Value = (UInt32)level; Props.Add(prop); } class CCoderProps { PROPID *_propIDs; NCOM::CPropVariant *_props; unsigned _numProps; unsigned _numPropsMax; public: CCoderProps(unsigned numPropsMax) { _numPropsMax = numPropsMax; _numProps = 0; _propIDs = new PROPID[numPropsMax]; _props = new NCOM::CPropVariant[numPropsMax]; } ~CCoderProps() { delete []_propIDs; delete []_props; } void AddProp(const CProp &prop); HRESULT SetProps(ICompressSetCoderProperties *setCoderProperties) { return setCoderProperties->SetCoderProperties(_propIDs, _props, _numProps); } }; void CCoderProps::AddProp(const CProp &prop) { if (_numProps >= _numPropsMax) throw 1; _propIDs[_numProps] = prop.Id; _props[_numProps] = prop.Value; _numProps++; } HRESULT CProps::SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const { CCoderProps coderProps(Props.Size() + (dataSizeReduce ? 1 : 0)); FOR_VECTOR (i, Props) coderProps.AddProp(Props[i]); if (dataSizeReduce) { CProp prop; prop.Id = NCoderPropID::kReduceSize; prop.Value = *dataSizeReduce; coderProps.AddProp(prop); } return coderProps.SetProps(scp); } int CMethodProps::FindProp(PROPID id) const { for (int i = Props.Size() - 1; i >= 0; i--) if (Props[i].Id == id) return i; return -1; } int CMethodProps::GetLevel() const { int i = FindProp(NCoderPropID::kLevel); if (i < 0) return 5; if (Props[i].Value.vt != VT_UI4) return 9; UInt32 level = Props[i].Value.ulVal; return level > 9 ? 9 : (int)level; } struct CNameToPropID { VARTYPE VarType; const char *Name; }; static const CNameToPropID g_NameToPropID[] = { { VT_UI4, "" }, { VT_UI4, "d" }, { VT_UI4, "mem" }, { VT_UI4, "o" }, { VT_UI4, "c" }, { VT_UI4, "pb" }, { VT_UI4, "lc" }, { VT_UI4, "lp" }, { VT_UI4, "fb" }, { VT_BSTR, "mf" }, { VT_UI4, "mc" }, { VT_UI4, "pass" }, { VT_UI4, "a" }, { VT_UI4, "mt" }, { VT_BOOL, "eos" }, { VT_UI4, "x" }, { VT_UI4, "reduceSize" } }; static int FindPropIdExact(const UString &name) { for (unsigned i = 0; i < ARRAY_SIZE(g_NameToPropID); i++) if (StringsAreEqualNoCase_Ascii(name, g_NameToPropID[i].Name)) return i; return -1; } static bool ConvertProperty(const PROPVARIANT &srcProp, VARTYPE varType, NCOM::CPropVariant &destProp) { if (varType == srcProp.vt) { destProp = srcProp; return true; } if (varType == VT_BOOL) { bool res; if (PROPVARIANT_to_bool(srcProp, res) != S_OK) return false; destProp = res; return true; } if (srcProp.vt == VT_EMPTY) { destProp = srcProp; return true; } return false; } static void SplitParams(const UString &srcString, UStringVector &subStrings) { subStrings.Clear(); UString s; int len = srcString.Len(); if (len == 0) return; for (int i = 0; i < len; i++) { wchar_t c = srcString[i]; if (c == L':') { subStrings.Add(s); s.Empty(); } else s += c; } subStrings.Add(s); } static void SplitParam(const UString ¶m, UString &name, UString &value) { int eqPos = param.Find(L'='); if (eqPos >= 0) { name.SetFrom(param, eqPos); value = param.Ptr(eqPos + 1); return; } unsigned i; for (i = 0; i < param.Len(); i++) { wchar_t c = param[i]; if (c >= L'0' && c <= L'9') break; } name.SetFrom(param, i); value = param.Ptr(i); } static bool IsLogSizeProp(PROPID propid) { switch (propid) { case NCoderPropID::kDictionarySize: case NCoderPropID::kUsedMemorySize: case NCoderPropID::kBlockSize: case NCoderPropID::kReduceSize: return true; } return false; } HRESULT CMethodProps::SetParam(const UString &name, const UString &value) { int index = FindPropIdExact(name); if (index < 0) return E_INVALIDARG; const CNameToPropID &nameToPropID = g_NameToPropID[index]; CProp prop; prop.Id = index; if (IsLogSizeProp(prop.Id)) { UInt32 dicSize; RINOK(StringToDictSize(value, dicSize)); prop.Value = dicSize; } else { NCOM::CPropVariant propValue; if (nameToPropID.VarType == VT_BSTR) propValue = value; else if (nameToPropID.VarType == VT_BOOL) { bool res; if (!StringToBool(value, res)) return E_INVALIDARG; propValue = res; } else if (!value.IsEmpty()) { UInt32 number; if (ParseStringToUInt32(value, number) == value.Len()) propValue = number; else propValue = value; } if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value)) return E_INVALIDARG; } Props.Add(prop); return S_OK; } HRESULT CMethodProps::ParseParamsFromString(const UString &srcString) { UStringVector params; SplitParams(srcString, params); FOR_VECTOR (i, params) { const UString ¶m = params[i]; UString name, value; SplitParam(param, name, value); RINOK(SetParam(name, value)); } return S_OK; } HRESULT CMethodProps::ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value) { if (realName.Len() == 0) { // [empty]=method return E_INVALIDARG; } if (value.vt == VT_EMPTY) { // {realName}=[empty] UString name, value; SplitParam(realName, name, value); return SetParam(name, value); } // {realName}=value int index = FindPropIdExact(realName); if (index < 0) return E_INVALIDARG; const CNameToPropID &nameToPropID = g_NameToPropID[index]; CProp prop; prop.Id = index; if (IsLogSizeProp(prop.Id)) { UInt32 dicSize; RINOK(PROPVARIANT_to_DictSize(value, dicSize)); prop.Value = dicSize; } else { if (!ConvertProperty(value, nameToPropID.VarType, prop.Value)) return E_INVALIDARG; } Props.Add(prop); return S_OK; } HRESULT COneMethodInfo::ParseMethodFromString(const UString &s) { int splitPos = s.Find(':'); MethodName = s; if (splitPos < 0) return S_OK; MethodName.DeleteFrom(splitPos); return ParseParamsFromString(s.Ptr(splitPos + 1)); } HRESULT COneMethodInfo::ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value) { if (!realName.IsEmpty() && !StringsAreEqualNoCase_Ascii(realName, "m")) return ParseParamsFromPROPVARIANT(realName, value); // -m{N}=method if (value.vt != VT_BSTR) return E_INVALIDARG; return ParseMethodFromString(value.bstrVal); } src/libs/7zip/unix/CPP/7zip/Common/MethodProps.h000066400000000000000000000111241325366651500216450ustar00rootroot00000000000000// MethodProps.h #ifndef __7Z_METHOD_PROPS_H #define __7Z_METHOD_PROPS_H #include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" #include "../ICoder.h" bool StringToBool(const UString &s, bool &res); HRESULT PROPVARIANT_to_bool(const PROPVARIANT &prop, bool &dest); unsigned ParseStringToUInt32(const UString &srcString, UInt32 &number); HRESULT ParsePropToUInt32(const UString &name, const PROPVARIANT &prop, UInt32 &resValue); HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads); struct CProp { PROPID Id; bool IsOptional; NWindows::NCOM::CPropVariant Value; CProp(): IsOptional(false) {} }; struct CProps { CObjectVector Props; void Clear() { Props.Clear(); } bool AreThereNonOptionalProps() const { FOR_VECTOR (i, Props) if (!Props[i].IsOptional) return true; return false; } void AddProp32(PROPID propid, UInt32 level); void AddPropString(PROPID propid, const wchar_t *s) { CProp prop; prop.IsOptional = true; prop.Id = propid; prop.Value = s; Props.Add(prop); } HRESULT SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const; }; class CMethodProps: public CProps { HRESULT SetParam(const UString &name, const UString &value); public: int GetLevel() const; int Get_NumThreads() const { int i = FindProp(NCoderPropID::kNumThreads); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return (int)Props[i].Value.ulVal; return -1; } bool Get_DicSize(UInt32 &res) const { res = 0; int i = FindProp(NCoderPropID::kDictionarySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) { res = Props[i].Value.ulVal; return true; } return false; } int FindProp(PROPID id) const; UInt32 Get_Lzma_Algo() const { int i = FindProp(NCoderPropID::kAlgorithm); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return Props[i].Value.ulVal; return GetLevel() >= 5 ? 1 : 0; } UInt32 Get_Lzma_DicSize() const { int i = FindProp(NCoderPropID::kDictionarySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return Props[i].Value.ulVal; int level = GetLevel(); return level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26)); } UInt32 Get_Lzma_NumThreads(bool &fixedNumber) const { fixedNumber = false; int numThreads = Get_NumThreads(); if (numThreads >= 0) { fixedNumber = true; return numThreads < 2 ? 1 : 2; } return Get_Lzma_Algo() == 0 ? 1 : 2; } UInt32 Get_BZip2_NumThreads(bool &fixedNumber) const { fixedNumber = false; int numThreads = Get_NumThreads(); if (numThreads >= 0) { fixedNumber = true; if (numThreads < 1) return 1; if (numThreads > 64) return 64; return numThreads; } return 1; } UInt32 Get_BZip2_BlockSize() const { int i = FindProp(NCoderPropID::kDictionarySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) { UInt32 blockSize = Props[i].Value.ulVal; const UInt32 kDicSizeMin = 100000; const UInt32 kDicSizeMax = 900000; if (blockSize < kDicSizeMin) blockSize = kDicSizeMin; if (blockSize > kDicSizeMax) blockSize = kDicSizeMax; return blockSize; } int level = GetLevel(); return 100000 * (level >= 5 ? 9 : (level >= 1 ? level * 2 - 1: 1)); } UInt32 Get_Ppmd_MemSize() const { int i = FindProp(NCoderPropID::kUsedMemorySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return Props[i].Value.ulVal; int level = GetLevel(); return level >= 9 ? (192 << 20) : ((UInt32)1 << (level + 19)); } void AddLevelProp(UInt32 level) { AddProp32(NCoderPropID::kLevel, level); } void AddNumThreadsProp(UInt32 numThreads) { AddProp32(NCoderPropID::kNumThreads, numThreads); } HRESULT ParseParamsFromString(const UString &srcString); HRESULT ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value); }; class COneMethodInfo: public CMethodProps { public: UString MethodName; void Clear() { CMethodProps::Clear(); MethodName.Empty(); } bool IsEmpty() const { return MethodName.IsEmpty() && Props.IsEmpty(); } HRESULT ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value); HRESULT ParseMethodFromString(const UString &s); }; #endif src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.cpp000066400000000000000000000041701325366651500216400ustar00rootroot00000000000000// OutBuffer.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "OutBuffer.h" bool COutBuffer::Create(UInt32 bufSize) throw() { const UInt32 kMinBlockSize = 1; if (bufSize < kMinBlockSize) bufSize = kMinBlockSize; if (_buf != 0 && _bufSize == bufSize) return true; Free(); _bufSize = bufSize; _buf = (Byte *)::MidAlloc(bufSize); return (_buf != 0); } void COutBuffer::Free() throw() { ::MidFree(_buf); _buf = 0; } void COutBuffer::Init() throw() { _streamPos = 0; _limitPos = _bufSize; _pos = 0; _processedSize = 0; _overDict = false; #ifdef _NO_EXCEPTIONS ErrorCode = S_OK; #endif } UInt64 COutBuffer::GetProcessedSize() const throw() { UInt64 res = _processedSize + _pos - _streamPos; if (_streamPos > _pos) res += _bufSize; return res; } HRESULT COutBuffer::FlushPart() throw() { // _streamPos < _bufSize UInt32 size = (_streamPos >= _pos) ? (_bufSize - _streamPos) : (_pos - _streamPos); HRESULT result = S_OK; #ifdef _NO_EXCEPTIONS result = ErrorCode; #endif if (_buf2 != 0) { memcpy(_buf2, _buf + _streamPos, size); _buf2 += size; } if (_stream != 0 #ifdef _NO_EXCEPTIONS && (ErrorCode == S_OK) #endif ) { UInt32 processedSize = 0; result = _stream->Write(_buf + _streamPos, size, &processedSize); size = processedSize; } _streamPos += size; if (_streamPos == _bufSize) _streamPos = 0; if (_pos == _bufSize) { _overDict = true; _pos = 0; } _limitPos = (_streamPos > _pos) ? _streamPos : _bufSize; _processedSize += size; return result; } HRESULT COutBuffer::Flush() throw() { #ifdef _NO_EXCEPTIONS if (ErrorCode != S_OK) return ErrorCode; #endif while (_streamPos != _pos) { HRESULT result = FlushPart(); if (result != S_OK) return result; } return S_OK; } void COutBuffer::FlushWithCheck() { HRESULT result = Flush(); #ifdef _NO_EXCEPTIONS ErrorCode = result; #else if (result != S_OK) throw COutBufferException(result); #endif } src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.h000066400000000000000000000024611325366651500213060ustar00rootroot00000000000000// OutBuffer.h #ifndef __OUT_BUFFER_H #define __OUT_BUFFER_H #include "../IStream.h" #include "../../Common/MyCom.h" #include "../../Common/MyException.h" #ifndef _NO_EXCEPTIONS struct COutBufferException: public CSystemException { COutBufferException(HRESULT errorCode): CSystemException(errorCode) {} }; #endif class COutBuffer { protected: Byte *_buf; UInt32 _pos; UInt32 _limitPos; UInt32 _streamPos; UInt32 _bufSize; ISequentialOutStream *_stream; UInt64 _processedSize; Byte *_buf2; bool _overDict; HRESULT FlushPart() throw(); public: #ifdef _NO_EXCEPTIONS HRESULT ErrorCode; #endif COutBuffer(): _buf(0), _pos(0), _stream(0), _buf2(0) {} ~COutBuffer() { Free(); } bool Create(UInt32 bufSize) throw(); void Free() throw(); void SetMemStream(Byte *buf) { _buf2 = buf; } void SetStream(ISequentialOutStream *stream) { _stream = stream; } void Init() throw(); HRESULT Flush() throw(); void FlushWithCheck(); void WriteByte(Byte b) { _buf[_pos++] = b; if (_pos == _limitPos) FlushWithCheck(); } void WriteBytes(const void *data, size_t size) { for (size_t i = 0; i < size; i++) WriteByte(((const Byte *)data)[i]); } UInt64 GetProcessedSize() const throw(); }; #endif src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.cpp000066400000000000000000000020211325366651500225550ustar00rootroot00000000000000// ProgressUtils.cpp #include "StdAfx.h" #include "ProgressUtils.h" CLocalProgress::CLocalProgress() { ProgressOffset = InSize = OutSize = 0; SendRatio = SendProgress = true; } void CLocalProgress::Init(IProgress *progress, bool inSizeIsMain) { _ratioProgress.Release(); _progress = progress; _progress.QueryInterface(IID_ICompressProgressInfo, &_ratioProgress); _inSizeIsMain = inSizeIsMain; } STDMETHODIMP CLocalProgress::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { UInt64 inSizeNew = InSize, outSizeNew = OutSize; if (inSize) inSizeNew += (*inSize); if (outSize) outSizeNew += (*outSize); if (SendRatio && _ratioProgress) { RINOK(_ratioProgress->SetRatioInfo(&inSizeNew, &outSizeNew)); } inSizeNew += ProgressOffset; outSizeNew += ProgressOffset; if (SendProgress) return _progress->SetCompleted(_inSizeIsMain ? &inSizeNew : &outSizeNew); return S_OK; } HRESULT CLocalProgress::SetCur() { return SetRatioInfo(NULL, NULL); } src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.h000066400000000000000000000012471325366651500222330ustar00rootroot00000000000000// ProgressUtils.h #ifndef __PROGRESSUTILS_H #define __PROGRESSUTILS_H #include "../../Common/MyCom.h" #include "../ICoder.h" #include "../IProgress.h" class CLocalProgress: public ICompressProgressInfo, public CMyUnknownImp { CMyComPtr _progress; CMyComPtr _ratioProgress; bool _inSizeIsMain; public: UInt64 ProgressOffset; UInt64 InSize; UInt64 OutSize; bool SendRatio; bool SendProgress; CLocalProgress(); void Init(IProgress *progress, bool inSizeIsMain); HRESULT SetCur(); MY_UNKNOWN_IMP STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); }; #endif src/libs/7zip/unix/CPP/7zip/Common/PropId.cpp000066400000000000000000000024221325366651500211320ustar00rootroot00000000000000// PropId.cpp #include "StdAfx.h" #include "../PropID.h" // VARTYPE Byte k7z_PROPID_To_VARTYPE[kpid_NUM_DEFINED] = { VT_EMPTY, VT_UI4, VT_UI4, VT_BSTR, VT_BSTR, VT_BSTR, VT_BOOL, VT_UI8, VT_UI8, VT_UI4, VT_FILETIME, VT_FILETIME, VT_FILETIME, VT_BOOL, VT_BOOL, VT_BOOL, VT_BOOL, VT_BOOL, VT_UI4, VT_UI4, VT_BSTR, VT_BOOL, VT_BSTR, VT_BSTR, VT_BSTR, VT_BSTR, VT_BSTR, VT_UI8, VT_BSTR, VT_UI8, VT_BSTR, VT_UI8, VT_UI8, VT_BSTR, // or VT_UI8 kpidUnpackVer VT_UI4, // or VT_UI8 kpidVolume VT_BOOL, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI4, VT_BOOL, VT_BOOL, VT_BSTR, VT_UI8, VT_UI8, VT_UI4, // kpidChecksum VT_BSTR, VT_UI8, VT_BSTR, // or VT_UI8 kpidId VT_BSTR, VT_BSTR, VT_UI4, VT_UI4, VT_BSTR, VT_BSTR, VT_UI8, VT_UI8, VT_UI4, VT_BSTR, VT_BSTR, VT_BSTR, VT_BSTR, // kpidNtSecure VT_BOOL, VT_BOOL, VT_BOOL, VT_BOOL, VT_BSTR, // SHA-1 VT_BSTR, // SHA-256 VT_BSTR, VT_UI8, VT_UI4, VT_UI4, VT_BSTR, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_BSTR, VT_BSTR, VT_BSTR, VT_BOOL, VT_BOOL, VT_BOOL, VT_UI8, VT_UI8 }; src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h000066400000000000000000000034401325366651500216150ustar00rootroot00000000000000// RegisterArc.h #ifndef __REGISTER_ARC_H #define __REGISTER_ARC_H #include "../Archive/IArchive.h" #include struct CArcInfo { const char *Name; const char *Ext; const char *AddExt; Byte ClassId; Byte SignatureSize; Byte Signature[20]; UInt16 SignatureOffset; UInt16 Flags; Func_CreateInArchive CreateInArchive; Func_CreateOutArchive CreateOutArchive; Func_IsArc IsArc; bool IsMultiSignature() const { return (Flags & NArcInfoFlags::kMultiSignature) != 0; } std::once_flag once; }; void RegisterArc(const CArcInfo *arcInfo) throw(); #define REGISTER_ARC_NAME(x) CRegister ## x #define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) \ { \ REGISTER_ARC_NAME(x)() \ { \ std::call_once(g_ArcInfo.once, [] { RegisterArc(&g_ArcInfo); }); \ } \ }; \ static REGISTER_ARC_NAME(x) g_RegisterArc; \ void registerArc##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } #define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) \ { \ REGISTER_ARC_NAME(x)() { \ std::call_once(g_ArcInfo.once, [] { \ g_ArcInfo.Signature[0]--; \ RegisterArc(&g_ArcInfo); \ }); \ } \ }; \ static REGISTER_ARC_NAME(x) g_RegisterArc; \ void registerArcDec##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } #define IMP_CreateArcIn_2(c) \ static IInArchive *CreateArc() { return new c; } #define IMP_CreateArcIn IMP_CreateArcIn_2(CHandler) #ifdef EXTRACT_ONLY #define IMP_CreateArcOut #define REF_CreateArc_Pair CreateArc, NULL #else #define IMP_CreateArcOut static IOutArchive *CreateArcOut() { return new CHandler; } #define REF_CreateArc_Pair CreateArc, CreateArcOut #endif #endif src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h000066400000000000000000000034261325366651500221310ustar00rootroot00000000000000// RegisterCodec.h #ifndef __REGISTER_CODEC_H #define __REGISTER_CODEC_H #include "../Common/MethodId.h" #include "../ICoder.h" #include typedef void * (*CreateCodecP)(); struct CCodecInfo { CreateCodecP CreateDecoder; CreateCodecP CreateEncoder; CMethodId Id; const wchar_t *Name; UInt32 NumInStreams; bool IsFilter; std::once_flag once; }; void RegisterCodec(const CCodecInfo *codecInfo) throw(); #define REGISTER_CODEC_NAME(x) CRegisterCodec ## x #define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) \ { \ REGISTER_CODEC_NAME(x)() \ { \ std::call_once(g_CodecInfo.once, [] { RegisterCodec(&g_CodecInfo); }); \ } \ }; \ static REGISTER_CODEC_NAME(x) g_RegisterCodec; \ void registerCodec##x() { static REGISTER_CODEC_NAME(x) g_RegisterCodecs; } #define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x #define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) \ { \ REGISTER_CODECS_NAME(x)() \ { \ for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \ std::call_once(g_CodecsInfo[i].once, [&i] { RegisterCodec(&g_CodecsInfo[i]); }); \ } \ }; \ static REGISTER_CODECS_NAME(x) g_RegisterCodecs; \ void registerCodec##x() { static REGISTER_CODECS_NAME(x) g_RegisterCodecs; } struct CHasherInfo { IHasher * (*CreateHasher)(); CMethodId Id; const wchar_t *Name; UInt32 DigestSize; }; void RegisterHasher(const CHasherInfo *hasher) throw(); #define REGISTER_HASHER_NAME(x) CRegisterHasher ## x #define REGISTER_HASHER(x) struct REGISTER_HASHER_NAME(x) { \ REGISTER_HASHER_NAME(x)() { RegisterHasher(&g_HasherInfo); }}; \ static REGISTER_HASHER_NAME(x) g_RegisterHasher; #endif src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.cpp000066400000000000000000000066671325366651500223330ustar00rootroot00000000000000// StreamBinder.cpp #include "StdAfx.h" #include "../../Common/MyCom.h" #include "StreamBinder.h" class CBinderInStream: public ISequentialInStream, public CMyUnknownImp { CStreamBinder *_binder; public: MY_UNKNOWN_IMP STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); ~CBinderInStream() { _binder->CloseRead(); } CBinderInStream(CStreamBinder *binder): _binder(binder) {} }; STDMETHODIMP CBinderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { return _binder->Read(data, size, processedSize); } class CBinderOutStream: public ISequentialOutStream, public CMyUnknownImp { CStreamBinder *_binder; public: MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); ~CBinderOutStream() { _binder->CloseWrite(); } CBinderOutStream(CStreamBinder *binder): _binder(binder) {} }; STDMETHODIMP CBinderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { return _binder->Write(data, size, processedSize); } WRes CStreamBinder::CreateEvents() { _synchroFor_canWrite_Event_and_readingWasClosed_Event = new NWindows::NSynchronization::CSynchro(); _synchroFor_canWrite_Event_and_readingWasClosed_Event->Create(); RINOK(_canWrite_Event.Create(_synchroFor_canWrite_Event_and_readingWasClosed_Event,true)); // RINOK(_canWrite_Event.Create(true)); RINOK(_canRead_Event.Create()); return _readingWasClosed_Event.Create(_synchroFor_canWrite_Event_and_readingWasClosed_Event); } void CStreamBinder::ReInit() { _waitWrite = true; _canRead_Event.Reset(); _readingWasClosed_Event.Reset(); ProcessedSize = 0; } void CStreamBinder::CreateStreams(ISequentialInStream **inStream, ISequentialOutStream **outStream) { _waitWrite = true; _bufSize = 0; _buf = NULL; ProcessedSize = 0; CBinderInStream *inStreamSpec = new CBinderInStream(this); CMyComPtr inStreamLoc(inStreamSpec); *inStream = inStreamLoc.Detach(); CBinderOutStream *outStreamSpec = new CBinderOutStream(this); CMyComPtr outStreamLoc(outStreamSpec); *outStream = outStreamLoc.Detach(); } // (_canRead_Event && _bufSize == 0) means that stream is finished. HRESULT CStreamBinder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size != 0) { if (_waitWrite) { RINOK(_canRead_Event.Lock()); _waitWrite = false; } if (size > _bufSize) size = _bufSize; if (size != 0) { memcpy(data, _buf, size); _buf = ((const Byte *)_buf) + size; ProcessedSize += size; if (processedSize) *processedSize = size; _bufSize -= size; if (_bufSize == 0) { _waitWrite = true; _canRead_Event.Reset(); _canWrite_Event.Set(); } } } return S_OK; } HRESULT CStreamBinder::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size != 0) { _buf = data; _bufSize = size; _canWrite_Event.Reset(); _canRead_Event.Set(); HANDLE events[2] = { _canWrite_Event, _readingWasClosed_Event }; DWORD waitResult = ::WaitForMultipleObjects(2, events, FALSE, INFINITE); if (waitResult != WAIT_OBJECT_0 + 0) return S_FALSE; if (processedSize) *processedSize = size; } return S_OK; } src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.h000066400000000000000000000017551325366651500217710ustar00rootroot00000000000000// StreamBinder.h #ifndef __STREAM_BINDER_H #define __STREAM_BINDER_H #include "../../Windows/Synchronization.h" #include "../IStream.h" class CStreamBinder { NWindows::NSynchronization::CManualResetEventWFMO _canWrite_Event; NWindows::NSynchronization::CManualResetEvent _canRead_Event; NWindows::NSynchronization::CManualResetEventWFMO _readingWasClosed_Event; NWindows::NSynchronization::CSynchro * _synchroFor_canWrite_Event_and_readingWasClosed_Event; bool _waitWrite; UInt32 _bufSize; const void *_buf; public: UInt64 ProcessedSize; WRes CreateEvents(); void CreateStreams(ISequentialInStream **inStream, ISequentialOutStream **outStream); void ReInit(); HRESULT Read(void *data, UInt32 size, UInt32 *processedSize); HRESULT Write(const void *data, UInt32 size, UInt32 *processedSize); void CloseRead() { _readingWasClosed_Event.Set(); } void CloseWrite() { // _bufSize must be = 0 _canRead_Event.Set(); } }; #endif src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.cpp000066400000000000000000000143671325366651500225150ustar00rootroot00000000000000// StreamObjects.cpp #include "StdAfx.h" #include #include "../../../C/Alloc.h" #include "StreamObjects.h" STDMETHODIMP CBufInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (_pos >= _size) return S_OK; size_t rem = _size - (size_t)_pos; if (rem > size) rem = (size_t)size; memcpy(data, _data + (size_t)_pos, rem); _pos += rem; if (processedSize) *processedSize = (UInt32)rem; return S_OK; } STDMETHODIMP CBufInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _pos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _pos = offset; if (newPosition) *newPosition = offset; return S_OK; } /* void Create_BufInStream_WithReference(const void *data, size_t size, ISequentialInStream **stream) { CBufInStream *inStreamSpec = new CBufInStream; CMyComPtr streamTemp = inStreamSpec; inStreamSpec->Init((const Byte *)data, size); *stream = streamTemp.Detach(); } */ void Create_BufInStream_WithNewBuf(const void *data, size_t size, ISequentialInStream **stream) { CReferenceBuf *referenceBuf = new CReferenceBuf; CMyComPtr ref = referenceBuf; referenceBuf->Buf.CopyFrom((const Byte *)data, size); CBufInStream *inStreamSpec = new CBufInStream; CMyComPtr streamTemp = inStreamSpec; inStreamSpec->Init(referenceBuf); *stream = streamTemp.Detach(); } void CByteDynBuffer::Free() throw() { free(_buf); _buf = 0; _capacity = 0; } bool CByteDynBuffer::EnsureCapacity(size_t cap) throw() { if (cap <= _capacity) return true; size_t delta; if (_capacity > 64) delta = _capacity / 4; else if (_capacity > 8) delta = 16; else delta = 4; cap = MyMax(_capacity + delta, cap); Byte *buf = (Byte *)realloc(_buf, cap); if (!buf) return false; _buf = buf; _capacity = cap; return true; } Byte *CDynBufSeqOutStream::GetBufPtrForWriting(size_t addSize) { addSize += _size; if (addSize < _size) return NULL; if (!_buffer.EnsureCapacity(addSize)) return NULL; return (Byte *)_buffer + _size; } void CDynBufSeqOutStream::CopyToBuffer(CByteBuffer &dest) const { dest.CopyFrom((const Byte *)_buffer, _size); } STDMETHODIMP CDynBufSeqOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; Byte *buf = GetBufPtrForWriting(size); if (!buf) return E_OUTOFMEMORY; memcpy(buf, data, size); UpdateSize(size); if (processedSize) *processedSize = size; return S_OK; } STDMETHODIMP CBufPtrSeqOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { size_t rem = _size - _pos; if (rem > size) rem = (size_t)size; memcpy(_buffer + _pos, data, rem); _pos += rem; if (processedSize) *processedSize = (UInt32)rem; return (rem != 0 || size == 0) ? S_OK : E_FAIL; } STDMETHODIMP CSequentialOutStreamSizeCount::Write(const void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize; HRESULT result = _stream->Write(data, size, &realProcessedSize); _size += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return result; } static const UInt64 kEmptyTag = (UInt64)(Int64)-1; void CCachedInStream::Free() throw() { MyFree(_tags); _tags = 0; MidFree(_data); _data = 0; } bool CCachedInStream::Alloc(unsigned blockSizeLog, unsigned numBlocksLog) throw() { unsigned sizeLog = blockSizeLog + numBlocksLog; if (sizeLog >= sizeof(size_t) * 8) return false; size_t dataSize = (size_t)1 << sizeLog; if (_data == 0 || dataSize != _dataSize) { MidFree(_data); _data = (Byte *)MidAlloc(dataSize); if (_data == 0) return false; _dataSize = dataSize; } if (_tags == 0 || numBlocksLog != _numBlocksLog) { MyFree(_tags); _tags = (UInt64 *)MyAlloc(sizeof(UInt64) << numBlocksLog); if (_tags == 0) return false; _numBlocksLog = numBlocksLog; } _blockSizeLog = blockSizeLog; return true; } void CCachedInStream::Init(UInt64 size) throw() { _size = size; _pos = 0; size_t numBlocks = (size_t)1 << _numBlocksLog; for (size_t i = 0; i < numBlocks; i++) _tags[i] = kEmptyTag; } STDMETHODIMP CCachedInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (_pos >= _size) return S_OK; { UInt64 rem = _size - _pos; if (size > rem) size = (UInt32)rem; } while (size != 0) { UInt64 cacheTag = _pos >> _blockSizeLog; size_t cacheIndex = (size_t)cacheTag & (((size_t)1 << _numBlocksLog) - 1); Byte *p = _data + (cacheIndex << _blockSizeLog); if (_tags[cacheIndex] != cacheTag) { UInt64 remInBlock = _size - (cacheTag << _blockSizeLog); size_t blockSize = (size_t)1 << _blockSizeLog; if (blockSize > remInBlock) blockSize = (size_t)remInBlock; RINOK(ReadBlock(cacheTag, p, blockSize)); _tags[cacheIndex] = cacheTag; } size_t offset = (size_t)_pos & (((size_t)1 << _blockSizeLog) - 1); UInt32 cur = (UInt32)MyMin(((size_t)1 << _blockSizeLog) - offset, (size_t)size); memcpy(data, p + offset, cur); if (processedSize) *processedSize += cur; data = (void *)((const Byte *)data + cur); _pos += cur; size -= cur; } return S_OK; } STDMETHODIMP CCachedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _pos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _pos = offset; if (newPosition) *newPosition = offset; return S_OK; } src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.h000066400000000000000000000073101325366651500221500ustar00rootroot00000000000000// StreamObjects.h #ifndef __STREAM_OBJECTS_H #define __STREAM_OBJECTS_H #include "../../Common/MyBuffer.h" #include "../../Common/MyCom.h" #include "../../Common/MyVector.h" #include "../IStream.h" struct CReferenceBuf: public IUnknown, public CMyUnknownImp { CByteBuffer Buf; MY_UNKNOWN_IMP }; class CBufInStream: public IInStream, public CMyUnknownImp { const Byte *_data; UInt64 _pos; size_t _size; CMyComPtr _ref; public: void Init(const Byte *data, size_t size, IUnknown *ref = 0) { _data = data; _size = size; _pos = 0; _ref = ref; } void Init(CReferenceBuf *ref) { Init(ref->Buf, ref->Buf.Size(), ref); } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; // void Create_BufInStream_WithReference(const void *data, size_t size, ISequentialInStream **stream); void Create_BufInStream_WithNewBuf(const void *data, size_t size, ISequentialInStream **stream); class CByteDynBuffer { size_t _capacity; Byte *_buf; public: CByteDynBuffer(): _capacity(0), _buf(0) {}; // there is no copy constructor. So don't copy this object. ~CByteDynBuffer() { Free(); } void Free() throw(); size_t GetCapacity() const { return _capacity; } operator Byte*() const { return _buf; }; operator const Byte*() const { return _buf; }; bool EnsureCapacity(size_t capacity) throw(); }; class CDynBufSeqOutStream: public ISequentialOutStream, public CMyUnknownImp { CByteDynBuffer _buffer; size_t _size; public: CDynBufSeqOutStream(): _size(0) {} void Init() { _size = 0; } size_t GetSize() const { return _size; } const Byte *GetBuffer() const { return _buffer; } void CopyToBuffer(CByteBuffer &dest) const; Byte *GetBufPtrForWriting(size_t addSize); void UpdateSize(size_t addSize) { _size += addSize; } MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; class CBufPtrSeqOutStream: public ISequentialOutStream, public CMyUnknownImp { Byte *_buffer; size_t _size; size_t _pos; public: void Init(Byte *buffer, size_t size) { _buffer = buffer; _pos = 0; _size = size; } size_t GetPos() const { return _pos; } MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; class CSequentialOutStreamSizeCount: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; public: void SetStream(ISequentialOutStream *stream) { _stream = stream; } void Init() { _size = 0; } UInt64 GetSize() const { return _size; } MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; class CCachedInStream: public IInStream, public CMyUnknownImp { UInt64 *_tags; Byte *_data; size_t _dataSize; unsigned _blockSizeLog; unsigned _numBlocksLog; UInt64 _size; UInt64 _pos; protected: virtual HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize) = 0; public: CCachedInStream(): _tags(0), _data(0) {} virtual ~CCachedInStream() { Free(); } // the destructor must be virtual (release calls it) !!! void Free() throw(); bool Alloc(unsigned blockSizeLog, unsigned numBlocksLog) throw(); void Init(UInt64 size) throw(); MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; #endif src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.cpp000066400000000000000000000031461325366651500222150ustar00rootroot00000000000000// StreamUtils.cpp #include "StdAfx.h" #include "StreamUtils.h" static const UInt32 kBlockSize = ((UInt32)1 << 31); HRESULT ReadStream(ISequentialInStream *stream, void *data, size_t *processedSize) throw() { size_t size = *processedSize; *processedSize = 0; while (size != 0) { UInt32 curSize = (size < kBlockSize) ? (UInt32)size : kBlockSize; UInt32 processedSizeLoc; HRESULT res = stream->Read(data, curSize, &processedSizeLoc); *processedSize += processedSizeLoc; data = (void *)((Byte *)data + processedSizeLoc); size -= processedSizeLoc; RINOK(res); if (processedSizeLoc == 0) return S_OK; } return S_OK; } HRESULT ReadStream_FALSE(ISequentialInStream *stream, void *data, size_t size) throw() { size_t processedSize = size; RINOK(ReadStream(stream, data, &processedSize)); return (size == processedSize) ? S_OK : S_FALSE; } HRESULT ReadStream_FAIL(ISequentialInStream *stream, void *data, size_t size) throw() { size_t processedSize = size; RINOK(ReadStream(stream, data, &processedSize)); return (size == processedSize) ? S_OK : E_FAIL; } HRESULT WriteStream(ISequentialOutStream *stream, const void *data, size_t size) throw() { while (size != 0) { UInt32 curSize = (size < kBlockSize) ? (UInt32)size : kBlockSize; UInt32 processedSizeLoc; HRESULT res = stream->Write(data, curSize, &processedSizeLoc); data = (const void *)((const Byte *)data + processedSizeLoc); size -= processedSizeLoc; RINOK(res); if (processedSizeLoc == 0) return E_FAIL; } return S_OK; } src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.h000066400000000000000000000007171325366651500216630ustar00rootroot00000000000000// StreamUtils.h #ifndef __STREAM_UTILS_H #define __STREAM_UTILS_H #include "../IStream.h" HRESULT ReadStream(ISequentialInStream *stream, void *data, size_t *size) throw(); HRESULT ReadStream_FALSE(ISequentialInStream *stream, void *data, size_t size) throw(); HRESULT ReadStream_FAIL(ISequentialInStream *stream, void *data, size_t size) throw(); HRESULT WriteStream(ISequentialOutStream *stream, const void *data, size_t size) throw(); #endif src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.cpp000066400000000000000000000022711325366651500220110ustar00rootroot00000000000000// UniqBlocks.cpp #include "StdAfx.h" #include "UniqBlocks.h" int CUniqBlocks::AddUniq(const Byte *data, size_t size) { unsigned left = 0, right = Sorted.Size(); while (left != right) { unsigned mid = (left + right) / 2; int index = Sorted[mid]; const CByteBuffer &buf = Bufs[index]; size_t sizeMid = buf.Size(); if (size < sizeMid) right = mid; else if (size > sizeMid) left = mid + 1; else { int cmp = memcmp(data, buf, size); if (cmp == 0) return index; if (cmp < 0) right = mid; else left = mid + 1; } } int index = Bufs.Size(); Sorted.Insert(left, index); CByteBuffer &buf = Bufs.AddNew(); buf.CopyFrom(data, size); return index; } UInt64 CUniqBlocks::GetTotalSizeInBytes() const { UInt64 size = 0; FOR_VECTOR (i, Bufs) size += Bufs[i].Size(); return size; } void CUniqBlocks::GetReverseMap() { unsigned num = Sorted.Size(); BufIndexToSortedIndex.ClearAndSetSize(num); int *p = &BufIndexToSortedIndex[0]; unsigned i; for (i = 0; i < num; i++) p[i] = 0; for (i = 0; i < num; i++) p[Sorted[i]] = i; } src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.h000066400000000000000000000011201325366651500214460ustar00rootroot00000000000000// UniqBlocks.h #ifndef __UNIQ_BLOCKS_H #define __UNIQ_BLOCKS_H #include "../../Common/MyTypes.h" #include "../../Common/MyBuffer.h" #include "../../Common/MyVector.h" struct CUniqBlocks { CObjectVector Bufs; CIntVector Sorted; CIntVector BufIndexToSortedIndex; int AddUniq(const Byte *data, size_t size); UInt64 GetTotalSizeInBytes() const; void GetReverseMap(); bool IsOnlyEmpty() const { if (Bufs.Size() == 0) return true; if (Bufs.Size() > 1) return false; return Bufs[0].Size() == 0; } }; #endif src/libs/7zip/unix/CPP/7zip/Common/VirtThread.cpp000066400000000000000000000015121325366651500220100ustar00rootroot00000000000000// VirtThread.cpp #include "StdAfx.h" #include "VirtThread.h" static THREAD_FUNC_DECL CoderThread(void *p) { for (;;) { CVirtThread *t = (CVirtThread *)p; t->StartEvent.Lock(); if (t->Exit) return 0; t->Execute(); t->FinishedEvent.Set(); } } WRes CVirtThread::Create() { RINOK(StartEvent.CreateIfNotCreated()); RINOK(FinishedEvent.CreateIfNotCreated()); StartEvent.Reset(); FinishedEvent.Reset(); Exit = false; if (Thread.IsCreated()) return S_OK; return Thread.Create(CoderThread, this); } void CVirtThread::Start() { Exit = false; StartEvent.Set(); } void CVirtThread::WaitThreadFinish() { Exit = true; if (StartEvent.IsCreated()) StartEvent.Set(); if (Thread.IsCreated()) { Thread.Wait(); Thread.Close(); } } src/libs/7zip/unix/CPP/7zip/Common/VirtThread.h000066400000000000000000000011131325366651500214520ustar00rootroot00000000000000// VirtThread.h #ifndef __VIRT_THREAD_H #define __VIRT_THREAD_H #include "../../Windows/Synchronization.h" #include "../../Windows/Thread.h" struct CVirtThread { NWindows::NSynchronization::CAutoResetEvent StartEvent; NWindows::NSynchronization::CAutoResetEvent FinishedEvent; NWindows::CThread Thread; bool Exit; ~CVirtThread() { WaitThreadFinish(); } void WaitThreadFinish(); // call it in destructor of child class ! WRes Create(); void Start(); virtual void Execute() = 0; void WaitExecuteFinish() { FinishedEvent.Lock(); } }; #endif src/libs/7zip/unix/CPP/7zip/Compress/000077500000000000000000000000001325366651500175745ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.cpp000066400000000000000000000242621325366651500220430ustar00rootroot00000000000000// Bcj2Coder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "Bcj2Coder.h" namespace NCompress { namespace NBcj2 { inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); } inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); } inline unsigned GetIndex(Byte b0, Byte b1) { return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); } #ifndef EXTRACT_ONLY static const unsigned kBufSize = 1 << 17; #define NUM_BITS 2 #define SIGN_BIT (1 << NUM_BITS) #define MASK_HIGH (0x100 - (1 << (NUM_BITS + 1))) static const UInt32 kDefaultLimit = (1 << (24 + NUM_BITS)); static bool inline Test86MSByte(Byte b) { return (((b) + SIGN_BIT) & MASK_HIGH) == 0; } CEncoder::~CEncoder() { ::MidFree(_buf); } HRESULT CEncoder::Flush() { RINOK(_mainStream.Flush()); RINOK(_callStream.Flush()); RINOK(_jumpStream.Flush()); _rc.FlushData(); return _rc.FlushStream(); } HRESULT CEncoder::CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams, ICompressProgressInfo *progress) { if (numInStreams != 1 || numOutStreams != 4) return E_INVALIDARG; if (!_mainStream.Create(1 << 18)) return E_OUTOFMEMORY; if (!_callStream.Create(1 << 18)) return E_OUTOFMEMORY; if (!_jumpStream.Create(1 << 18)) return E_OUTOFMEMORY; if (!_rc.Create(1 << 20)) return E_OUTOFMEMORY; if (_buf == 0) { _buf = (Byte *)MidAlloc(kBufSize); if (_buf == 0) return E_OUTOFMEMORY; } bool sizeIsDefined = false; UInt64 inSize = 0; if (inSizes) if (inSizes[0]) { inSize = *inSizes[0]; if (inSize <= kDefaultLimit) sizeIsDefined = true; } ISequentialInStream *inStream = inStreams[0]; _mainStream.SetStream(outStreams[0]); _mainStream.Init(); _callStream.SetStream(outStreams[1]); _callStream.Init(); _jumpStream.SetStream(outStreams[2]); _jumpStream.Init(); _rc.SetStream(outStreams[3]); _rc.Init(); for (unsigned i = 0; i < 256 + 2; i++) _statusEncoder[i].Init(); CMyComPtr getSubStreamSize; { inStream->QueryInterface(IID_ICompressGetSubStreamSize, (void **)&getSubStreamSize); } UInt32 nowPos = 0; UInt64 nowPos64 = 0; UInt32 bufPos = 0; Byte prevByte = 0; UInt64 subStreamIndex = 0; UInt64 subStreamStartPos = 0; UInt64 subStreamEndPos = 0; for (;;) { UInt32 processedSize = 0; for (;;) { UInt32 size = kBufSize - (bufPos + processedSize); UInt32 processedSizeLoc; if (size == 0) break; RINOK(inStream->Read(_buf + bufPos + processedSize, size, &processedSizeLoc)); if (processedSizeLoc == 0) break; processedSize += processedSizeLoc; } UInt32 endPos = bufPos + processedSize; if (endPos < 5) { // change it for (bufPos = 0; bufPos < endPos; bufPos++) { Byte b = _buf[bufPos]; _mainStream.WriteByte(b); UInt32 index; if (b == 0xE8) index = prevByte; else if (b == 0xE9) index = 256; else if (IsJcc(prevByte, b)) index = 257; else { prevByte = b; continue; } _statusEncoder[index].Encode(&_rc, 0); prevByte = b; } return Flush(); } bufPos = 0; UInt32 limit = endPos - 5; while (bufPos <= limit) { Byte b = _buf[bufPos]; _mainStream.WriteByte(b); if (!IsJ(prevByte, b)) { bufPos++; prevByte = b; continue; } Byte nextByte = _buf[bufPos + 4]; UInt32 src = (UInt32(nextByte) << 24) | (UInt32(_buf[bufPos + 3]) << 16) | (UInt32(_buf[bufPos + 2]) << 8) | (_buf[bufPos + 1]); UInt32 dest = (nowPos + bufPos + 5) + src; // if (Test86MSByte(nextByte)) bool convert; if (getSubStreamSize) { UInt64 currentPos = (nowPos64 + bufPos); while (subStreamEndPos < currentPos) { UInt64 subStreamSize; HRESULT result = getSubStreamSize->GetSubStreamSize(subStreamIndex, &subStreamSize); if (result == S_OK) { subStreamStartPos = subStreamEndPos; subStreamEndPos += subStreamSize; subStreamIndex++; } else if (result == S_FALSE || result == E_NOTIMPL) { getSubStreamSize.Release(); subStreamStartPos = 0; subStreamEndPos = subStreamStartPos - 1; } else return result; } if (getSubStreamSize == NULL) { if (sizeIsDefined) convert = (dest < inSize); else convert = Test86MSByte(nextByte); } else if (subStreamEndPos - subStreamStartPos > kDefaultLimit) convert = Test86MSByte(nextByte); else { UInt64 dest64 = (currentPos + 5) + Int64(Int32(src)); convert = (dest64 >= subStreamStartPos && dest64 < subStreamEndPos); } } else if (sizeIsDefined) convert = (dest < inSize); else convert = Test86MSByte(nextByte); unsigned index = GetIndex(prevByte, b); if (convert) { _statusEncoder[index].Encode(&_rc, 1); bufPos += 5; COutBuffer &s = (b == 0xE8) ? _callStream : _jumpStream; for (int i = 24; i >= 0; i -= 8) s.WriteByte((Byte)(dest >> i)); prevByte = nextByte; } else { _statusEncoder[index].Encode(&_rc, 0); bufPos++; prevByte = b; } } nowPos += bufPos; nowPos64 += bufPos; if (progress) { /* const UInt64 compressedSize = _mainStream.GetProcessedSize() + _callStream.GetProcessedSize() + _jumpStream.GetProcessedSize() + _rc.GetProcessedSize(); */ RINOK(progress->SetRatioInfo(&nowPos64, NULL)); } UInt32 i = 0; while (bufPos < endPos) _buf[i++] = _buf[bufPos++]; bufPos = i; } } STDMETHODIMP CEncoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress) { try { return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress); } catch(const COutBufferException &e) { return e.ErrorCode; } catch(...) { return S_FALSE; } } #endif STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; } STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; } CDecoder::CDecoder(): _outBufSize(1 << 16) { _inBufSizes[0] = 1 << 20; _inBufSizes[1] = 1 << 20; _inBufSizes[2] = 1 << 20; _inBufSizes[3] = 1 << 20; } HRESULT CDecoder::CodeReal(ISequentialInStream **inStreams, const UInt64 ** /* inSizes */, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams, ICompressProgressInfo *progress) { if (numInStreams != 4 || numOutStreams != 1) return E_INVALIDARG; if (!_mainStream.Create(_inBufSizes[0])) return E_OUTOFMEMORY; if (!_callStream.Create(_inBufSizes[1])) return E_OUTOFMEMORY; if (!_jumpStream.Create(_inBufSizes[2])) return E_OUTOFMEMORY; if (!_rc.Create(_inBufSizes[3])) return E_OUTOFMEMORY; if (!_outStream.Create(_outBufSize)) return E_OUTOFMEMORY; _mainStream.SetStream(inStreams[0]); _callStream.SetStream(inStreams[1]); _jumpStream.SetStream(inStreams[2]); _rc.SetStream(inStreams[3]); _outStream.SetStream(outStreams[0]); _mainStream.Init(); _callStream.Init(); _jumpStream.Init(); _rc.Init(); _outStream.Init(); for (unsigned i = 0; i < 256 + 2; i++) _statusDecoder[i].Init(); Byte prevByte = 0; UInt32 processedBytes = 0; for (;;) { if (processedBytes >= (1 << 20) && progress) { /* const UInt64 compressedSize = _mainStream.GetProcessedSize() + _callStream.GetProcessedSize() + _jumpStream.GetProcessedSize() + _rc.GetProcessedSize(); */ const UInt64 nowPos64 = _outStream.GetProcessedSize(); RINOK(progress->SetRatioInfo(NULL, &nowPos64)); processedBytes = 0; } UInt32 i; Byte b = 0; const UInt32 kBurstSize = (1 << 18); for (i = 0; i < kBurstSize; i++) { if (!_mainStream.ReadByte(b)) return _outStream.Flush(); _outStream.WriteByte(b); if (IsJ(prevByte, b)) break; prevByte = b; } processedBytes += i; if (i == kBurstSize) continue; unsigned index = GetIndex(prevByte, b); if (_statusDecoder[index].Decode(&_rc) == 1) { UInt32 src = 0; CInBuffer &s = (b == 0xE8) ? _callStream : _jumpStream; for (unsigned i = 0; i < 4; i++) { Byte b0; if (!s.ReadByte(b0)) return S_FALSE; src <<= 8; src |= ((UInt32)b0); } UInt32 dest = src - (UInt32(_outStream.GetProcessedSize()) + 4) ; _outStream.WriteByte((Byte)(dest)); _outStream.WriteByte((Byte)(dest >> 8)); _outStream.WriteByte((Byte)(dest >> 16)); _outStream.WriteByte((Byte)(dest >> 24)); prevByte = (Byte)(dest >> 24); processedBytes += 4; } else prevByte = b; } } STDMETHODIMP CDecoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress) { try { return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress); } catch(const CInBufferException &e) { return e.ErrorCode; } catch(const COutBufferException &e) { return e.ErrorCode; } catch(...) { return S_FALSE; } } }} src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.h000066400000000000000000000041351325366651500215050ustar00rootroot00000000000000// Bcj2Coder.h #ifndef __COMPRESS_BCJ2_CODER_H #define __COMPRESS_BCJ2_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" #include "RangeCoderBit.h" namespace NCompress { namespace NBcj2 { const unsigned kNumMoveBits = 5; #ifndef EXTRACT_ONLY class CEncoder: public ICompressCoder2, public CMyUnknownImp { Byte *_buf; COutBuffer _mainStream; COutBuffer _callStream; COutBuffer _jumpStream; NRangeCoder::CEncoder _rc; NRangeCoder::CBitEncoder _statusEncoder[256 + 2]; HRESULT Flush(); public: MY_UNKNOWN_IMP HRESULT CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); CEncoder(): _buf(0) {}; ~CEncoder(); }; #endif class CDecoder: public ICompressCoder2, public ICompressSetBufSize, public CMyUnknownImp { CInBuffer _mainStream; CInBuffer _callStream; CInBuffer _jumpStream; NRangeCoder::CDecoder _rc; NRangeCoder::CBitDecoder _statusDecoder[256 + 2]; COutBuffer _outStream; UInt32 _inBufSizes[4]; UInt32 _outBufSize; public: MY_UNKNOWN_IMP1(ICompressSetBufSize); HRESULT CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); STDMETHOD(SetInBufSize)(UInt32 streamIndex, UInt32 size); STDMETHOD(SetOutBufSize)(UInt32 streamIndex, UInt32 size); CDecoder(); }; }} #endif src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Register.cpp000066400000000000000000000007751325366651500225760ustar00rootroot00000000000000// Bcj2Register.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "Bcj2Coder.h" static void *CreateCodec() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CDecoder()); } #ifndef EXTRACT_ONLY static void *CreateCodecOut() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CEncoder()); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x0303011B, L"BCJ2", 4, false }; REGISTER_CODEC(BCJ2) src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.cpp000066400000000000000000000005331325366651500217540ustar00rootroot00000000000000// BcjCoder.cpp #include "StdAfx.h" #include "BcjCoder.h" UInt32 CBCJ_x86_Encoder::SubFilter(Byte *data, UInt32 size) { return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 1); } UInt32 CBCJ_x86_Decoder::SubFilter(Byte *data, UInt32 size) { return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 0); } src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.h000066400000000000000000000005171325366651500214230ustar00rootroot00000000000000// BcjCoder.h #ifndef __COMPRESS_BCJ_CODER_H #define __COMPRESS_BCJ_CODER_H #include "../../../C/Bra.h" #include "BranchCoder.h" struct CBranch86 { UInt32 _prevMask; void x86Init() { x86_Convert_Init(_prevMask); } }; MyClassB(BCJ_x86, 0x01, 3, CBranch86 , virtual void SubInit() { x86Init(); }) #endif src/libs/7zip/unix/CPP/7zip/Compress/BcjRegister.cpp000066400000000000000000000007441325366651500225100ustar00rootroot00000000000000// BcjRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "BcjCoder.h" static void *CreateCodec() { return (void *)(ICompressFilter *)(new CBCJ_x86_Decoder()); } #ifndef EXTRACT_ONLY static void *CreateCodecOut() { return (void *)(ICompressFilter *)(new CBCJ_x86_Encoder()); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x03030103, L"BCJ", 1, true }; REGISTER_CODEC(BCJ) src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.cpp000066400000000000000000000005421325366651500224530ustar00rootroot00000000000000// BranchCoder.cpp #include "StdAfx.h" #include "BranchCoder.h" STDMETHODIMP CBranchConverter::Init() { _bufferPos = 0; SubInit(); return S_OK; } STDMETHODIMP_(UInt32) CBranchConverter::Filter(Byte *data, UInt32 size) { UInt32 processedSize = SubFilter(data, size); _bufferPos += processedSize; return processedSize; } src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.h000066400000000000000000000025621325366651500221240ustar00rootroot00000000000000// BranchCoder.h #ifndef __COMPRESS_BRANCH_CODER_H #define __COMPRESS_BRANCH_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" class CBranchConverter: public ICompressFilter, public CMyUnknownImp { protected: UInt32 _bufferPos; virtual void SubInit() {} virtual UInt32 SubFilter(Byte *data, UInt32 size) = 0; public: MY_UNKNOWN_IMP; STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); }; #define MyClassEncoderA(Name) class C ## Name: public CBranchConverter \ { public: UInt32 SubFilter(Byte *data, UInt32 size); }; #define MyClassDecoderA(Name) class C ## Name: public CBranchConverter \ { public: UInt32 SubFilter(Byte *data, UInt32 size); }; #define MyClassEncoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \ { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT}; #define MyClassDecoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \ { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT}; #define MyClassA(Name, id, subId) \ MyClassEncoderA(Name ## _Encoder) \ MyClassDecoderA(Name ## _Decoder) #define MyClassB(Name, id, subId, ADD_ITEMS, ADD_INIT) \ MyClassEncoderB(Name ## _Encoder, ADD_ITEMS, ADD_INIT) \ MyClassDecoderB(Name ## _Decoder, ADD_ITEMS, ADD_INIT) #endif src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.cpp000066400000000000000000000010231325366651500223050ustar00rootroot00000000000000// BranchMisc.cpp #include "StdAfx.h" #include "../../../C/Bra.h" #include "BranchMisc.h" #define SUB_FILTER_IMP2(name, coderStr, coderNum) \ UInt32 CBC_ ## name ## coderStr::SubFilter(Byte *data, UInt32 size) \ { return (UInt32)::name ## Convert(data, size, _bufferPos, coderNum); } #define SUB_FILTER_IMP(name) \ SUB_FILTER_IMP2(name, Encoder, 1) \ SUB_FILTER_IMP2(name, Decoder, 0) \ SUB_FILTER_IMP(ARM_) SUB_FILTER_IMP(ARMT_) SUB_FILTER_IMP(PPC_) SUB_FILTER_IMP(SPARC_) SUB_FILTER_IMP(IA64_) src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.h000066400000000000000000000004201325366651500217520ustar00rootroot00000000000000// BranchMisc.h #ifndef __COMPRESS_BRANCH_MISC_H #define __COMPRESS_BRANCH_MISC_H #include "BranchCoder.h" MyClassA(BC_ARM, 0x05, 1) MyClassA(BC_ARMT, 0x07, 1) MyClassA(BC_PPC, 0x02, 5) MyClassA(BC_SPARC, 0x08, 5) MyClassA(BC_IA64, 0x04, 1) #endif src/libs/7zip/unix/CPP/7zip/Compress/BranchRegister.cpp000066400000000000000000000016011325366651500232000ustar00rootroot00000000000000// BranchRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "BranchMisc.h" #define CREATE_CODEC(x) \ static void *CreateCodec ## x() { return (void *)(ICompressFilter *)(new C ## x ## _Decoder); } \ static void *CreateCodec ## x ## Out() { return (void *)(ICompressFilter *)(new C ## x ## _Encoder); } CREATE_CODEC(BC_PPC) CREATE_CODEC(BC_IA64) CREATE_CODEC(BC_ARM) CREATE_CODEC(BC_ARMT) CREATE_CODEC(BC_SPARC) #define METHOD_ITEM(x, id1, id2, name) { CreateCodec ## x, CreateCodec ## x ## Out, 0x03030000 + (id1 * 256) + id2, name, 1, true } static CCodecInfo g_CodecsInfo[] = { METHOD_ITEM(BC_PPC, 0x02, 0x05, L"PPC"), METHOD_ITEM(BC_IA64, 0x04, 1, L"IA64"), METHOD_ITEM(BC_ARM, 0x05, 1, L"ARM"), METHOD_ITEM(BC_ARMT, 0x07, 1, L"ARMT"), METHOD_ITEM(BC_SPARC, 0x08, 0x05, L"SPARC") }; REGISTER_CODECS(Branch) src/libs/7zip/unix/CPP/7zip/Compress/ByteSwap.cpp000066400000000000000000000030611325366651500220360ustar00rootroot00000000000000// ByteSwap.cpp #include "StdAfx.h" #include "../../Common/MyCom.h" #include "../ICoder.h" #include "../Common/RegisterCodec.h" class CByteSwap2: public ICompressFilter, public CMyUnknownImp { public: MY_UNKNOWN_IMP STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); }; class CByteSwap4: public ICompressFilter, public CMyUnknownImp { public: MY_UNKNOWN_IMP STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); }; STDMETHODIMP CByteSwap2::Init() { return S_OK; } STDMETHODIMP_(UInt32) CByteSwap2::Filter(Byte *data, UInt32 size) { const UInt32 kStep = 2; UInt32 i; for (i = 0; i + kStep <= size; i += kStep) { Byte b = data[i]; data[i] = data[i + 1]; data[i + 1] = b; } return i; } STDMETHODIMP CByteSwap4::Init() { return S_OK; } STDMETHODIMP_(UInt32) CByteSwap4::Filter(Byte *data, UInt32 size) { const UInt32 kStep = 4; UInt32 i; for (i = 0; i + kStep <= size; i += kStep) { Byte b0 = data[i]; Byte b1 = data[i + 1]; data[i] = data[i + 3]; data[i + 1] = data[i + 2]; data[i + 2] = b1; data[i + 3] = b0; } return i; } static void *CreateCodec2() { return (void *)(ICompressFilter *)(new CByteSwap2); } static void *CreateCodec4() { return (void *)(ICompressFilter *)(new CByteSwap4); } static CCodecInfo g_CodecsInfo[] = { { CreateCodec2, CreateCodec2, 0x020302, L"Swap2", 1, true }, { CreateCodec4, CreateCodec4, 0x020304, L"Swap4", 1, true } }; REGISTER_CODECS(ByteSwap) src/libs/7zip/unix/CPP/7zip/Compress/Compress.pri000066400000000000000000000026741325366651500221140ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Compress/Bcj2Coder.h \ $$7ZIP_BASE/CPP/7zip/Compress/BcjCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/BranchCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/BranchMisc.h \ $$7ZIP_BASE/CPP/7zip/Compress/CopyCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Decoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Encoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaDecoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaEncoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/RangeCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/RangeCoderBit.h SOURCES += $$7ZIP_BASE/CPP/7zip/Compress/Bcj2Coder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Bcj2Register.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BcjCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BcjRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BranchCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BranchMisc.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BranchRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/ByteSwap.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/CopyCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/CopyRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/DeltaFilter.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Decoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Encoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Register.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaDecoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaEncoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaRegister.cpp src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.cpp000066400000000000000000000034731325366651500221760ustar00rootroot00000000000000// Compress/CopyCoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/StreamUtils.h" #include "CopyCoder.h" namespace NCompress { static const UInt32 kBufferSize = 1 << 17; CCopyCoder::~CCopyCoder() { ::MidFree(_buffer); } STDMETHODIMP CCopyCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (!_buffer) { _buffer = (Byte *)::MidAlloc(kBufferSize); if (!_buffer) return E_OUTOFMEMORY; } TotalSize = 0; for (;;) { UInt32 size = kBufferSize; if (outSize && size > *outSize - TotalSize) size = (UInt32)(*outSize - TotalSize); RINOK(inStream->Read(_buffer, size, &size)); if (size == 0) break; if (outStream) { RINOK(WriteStream(outStream, _buffer, size)); } TotalSize += size; if (progress) { RINOK(progress->SetRatioInfo(&TotalSize, &TotalSize)); } } return S_OK; } STDMETHODIMP CCopyCoder::GetInStreamProcessedSize(UInt64 *value) { *value = TotalSize; return S_OK; } HRESULT CopyStream(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { CMyComPtr copyCoder = new CCopyCoder; return copyCoder->Code(inStream, outStream, NULL, NULL, progress); } HRESULT CopyStream_ExactSize(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt64 size, ICompressProgressInfo *progress) { NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr copyCoder = copyCoderSpec; RINOK(copyCoder->Code(inStream, outStream, NULL, &size, progress)); return copyCoderSpec->TotalSize == size ? S_OK : E_FAIL; } } src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.h000066400000000000000000000017141325366651500216370ustar00rootroot00000000000000// Compress/CopyCoder.h #ifndef __COMPRESS_COPY_CODER_H #define __COMPRESS_COPY_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { class CCopyCoder: public ICompressCoder, public ICompressGetInStreamProcessedSize, public CMyUnknownImp { Byte *_buffer; public: UInt64 TotalSize; CCopyCoder(): TotalSize(0), _buffer(0) {}; ~CCopyCoder(); MY_UNKNOWN_IMP1(ICompressGetInStreamProcessedSize) STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(GetInStreamProcessedSize)(UInt64 *value); }; HRESULT CopyStream(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress); HRESULT CopyStream_ExactSize(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt64 size, ICompressProgressInfo *progress); } #endif src/libs/7zip/unix/CPP/7zip/Compress/CopyRegister.cpp000066400000000000000000000005011325366651500227130ustar00rootroot00000000000000// CopyRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "CopyCoder.h" static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::CCopyCoder); } static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodec, 0x00, L"Copy", 1, false }; REGISTER_CODEC(Copy) src/libs/7zip/unix/CPP/7zip/Compress/DeltaFilter.cpp000066400000000000000000000061071325366651500225030ustar00rootroot00000000000000// DeltaFilter.cpp #include "StdAfx.h" #include "../../../C/Delta.h" #include "../Common/RegisterCodec.h" #include "BranchCoder.h" struct CDelta { unsigned _delta; Byte _state[DELTA_STATE_SIZE]; CDelta(): _delta(1) {} void DeltaInit() { Delta_Init(_state); } }; class CDeltaEncoder: public ICompressFilter, public ICompressSetCoderProperties, public ICompressWriteCoderProperties, CDelta, public CMyUnknownImp { public: MY_UNKNOWN_IMP2(ICompressSetCoderProperties, ICompressWriteCoderProperties) STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); }; class CDeltaDecoder: public ICompressFilter, public ICompressSetDecoderProperties2, CDelta, public CMyUnknownImp { public: MY_UNKNOWN_IMP1(ICompressSetDecoderProperties2) STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); }; STDMETHODIMP CDeltaEncoder::Init() { DeltaInit(); return S_OK; } STDMETHODIMP_(UInt32) CDeltaEncoder::Filter(Byte *data, UInt32 size) { Delta_Encode(_state, _delta, data, size); return size; } STDMETHODIMP CDeltaEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps) { UInt32 delta = _delta; for (UInt32 i = 0; i < numProps; i++) { const PROPVARIANT &prop = props[i]; PROPID propID = propIDs[i]; if (propID >= NCoderPropID::kReduceSize) continue; if (prop.vt != VT_UI4) return E_INVALIDARG; switch (propID) { case NCoderPropID::kDefaultProp: delta = (UInt32)prop.ulVal; if (delta < 1 || delta > 256) return E_INVALIDARG; break; case NCoderPropID::kNumThreads: break; case NCoderPropID::kLevel: break; default: return E_INVALIDARG; } } _delta = delta; return S_OK; } STDMETHODIMP CDeltaEncoder::WriteCoderProperties(ISequentialOutStream *outStream) { Byte prop = (Byte)(_delta - 1); return outStream->Write(&prop, 1, NULL); } STDMETHODIMP CDeltaDecoder::Init() { DeltaInit(); return S_OK; } STDMETHODIMP_(UInt32) CDeltaDecoder::Filter(Byte *data, UInt32 size) { Delta_Decode(_state, _delta, data, size); return size; } STDMETHODIMP CDeltaDecoder::SetDecoderProperties2(const Byte *props, UInt32 size) { if (size != 1) return E_INVALIDARG; _delta = (unsigned)props[0] + 1; return S_OK; } #define CREATE_CODEC(x) \ static void *CreateCodec ## x() { return (void *)(ICompressFilter *)(new C ## x ## Decoder); } \ static void *CreateCodec ## x ## Out() { return (void *)(ICompressFilter *)(new C ## x ## Encoder); } CREATE_CODEC(Delta) #define METHOD_ITEM(x, id, name) { CreateCodec ## x, CreateCodec ## x ## Out, id, name, 1, true } static CCodecInfo g_CodecsInfo[] = { METHOD_ITEM(Delta, 3, L"Delta") }; REGISTER_CODECS(Delta) src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.cpp000066400000000000000000000120731325366651500225560ustar00rootroot00000000000000// Lzma2Decoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/StreamUtils.h" #include "Lzma2Decoder.h" static HRESULT SResToHRESULT(SRes res) { switch(res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PARAM: return E_INVALIDARG; // case SZ_ERROR_PROGRESS: return E_ABORT; case SZ_ERROR_DATA: return S_FALSE; } return E_FAIL; } namespace NCompress { namespace NLzma2 { static const UInt32 kInBufSize = 1 << 20; CDecoder::CDecoder(): _inBuf(0), _outSizeDefined(false) { Lzma2Dec_Construct(&_state); } static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } static void SzFree(void *p, void *address) { p = p; MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CDecoder::~CDecoder() { Lzma2Dec_Free(&_state, &g_Alloc); MyFree(_inBuf); } STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size) { if (size != 1) return SZ_ERROR_UNSUPPORTED; RINOK(SResToHRESULT(Lzma2Dec_Allocate(&_state, prop[0], &g_Alloc))); if (_inBuf == 0) { _inBuf = (Byte *)MyAlloc(kInBufSize); if (_inBuf == 0) return E_OUTOFMEMORY; } return S_OK; } STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; } STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; } STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; } STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) { _outSizeDefined = (outSize != NULL); if (_outSizeDefined) _outSize = *outSize; Lzma2Dec_Init(&_state); _inPos = _inSize = 0; _inSizeProcessed = _outSizeProcessed = 0; return S_OK; } STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (_inBuf == 0) return S_FALSE; SetOutStreamSize(outSize); for (;;) { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(inStream->Read(_inBuf, kInBufSize, &_inSize)); } SizeT dicPos = _state.decoder.dicPos; SizeT curSize = _state.decoder.dicBufSize - dicPos; const UInt32 kStepSize = ((UInt32)1 << 22); if (curSize > kStepSize) curSize = (SizeT)kStepSize; ELzmaFinishMode finishMode = LZMA_FINISH_ANY; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem < curSize) { curSize = (SizeT)rem; /* // finishMode = LZMA_FINISH_END; we can't use LZMA_FINISH_END here to allow partial decoding */ } } SizeT inSizeProcessed = _inSize - _inPos; ELzmaStatus status; SRes res = Lzma2Dec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status); _inPos += (UInt32)inSizeProcessed; _inSizeProcessed += inSizeProcessed; SizeT outSizeProcessed = _state.decoder.dicPos - dicPos; _outSizeProcessed += outSizeProcessed; bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0); bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize); if (res != 0 || _state.decoder.dicPos == _state.decoder.dicBufSize || finished || stopDecoding) { HRESULT res2 = WriteStream(outStream, _state.decoder.dic, _state.decoder.dicPos); if (res != 0) return S_FALSE; RINOK(res2); if (stopDecoding) return S_OK; if (finished) return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE); } if (_state.decoder.dicPos == _state.decoder.dicBufSize) _state.decoder.dicPos = 0; if (progress != NULL) { RINOK(progress->SetRatioInfo(&_inSizeProcessed, &_outSizeProcessed)); } } } #ifndef NO_READ_FROM_CODER STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; do { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(_inStream->Read(_inBuf, kInBufSize, &_inSize)); } { SizeT inProcessed = _inSize - _inPos; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem < size) size = (UInt32)rem; } SizeT outProcessed = size; ELzmaStatus status; SRes res = Lzma2Dec_DecodeToBuf(&_state, (Byte *)data, &outProcessed, _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status); _inPos += (UInt32)inProcessed; _inSizeProcessed += inProcessed; _outSizeProcessed += outProcessed; size -= (UInt32)outProcessed; data = (Byte *)data + outProcessed; if (processedSize) *processedSize += (UInt32)outProcessed; RINOK(SResToHRESULT(res)); if (inProcessed == 0 && outProcessed == 0) return S_OK; } } while (size != 0); return S_OK; } #endif }} src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.h000066400000000000000000000032661325366651500222270ustar00rootroot00000000000000// Lzma2Decoder.h #ifndef __LZMA2_DECODER_H #define __LZMA2_DECODER_H #include "../../../C/Lzma2Dec.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma2 { class CDecoder: public ICompressCoder, public ICompressSetDecoderProperties2, public ICompressGetInStreamProcessedSize, #ifndef NO_READ_FROM_CODER public ICompressSetInStream, public ICompressSetOutStreamSize, public ISequentialInStream, #endif public CMyUnknownImp { CMyComPtr _inStream; Byte *_inBuf; UInt32 _inPos; UInt32 _inSize; CLzma2Dec _state; bool _outSizeDefined; UInt64 _outSize; UInt64 _inSizeProcessed; UInt64 _outSizeProcessed; public: #ifndef NO_READ_FROM_CODER MY_UNKNOWN_IMP5( ICompressSetDecoderProperties2, ICompressGetInStreamProcessedSize, ICompressSetInStream, ICompressSetOutStreamSize, ISequentialInStream) #else MY_UNKNOWN_IMP2( ICompressSetDecoderProperties2, ICompressGetInStreamProcessedSize) #endif STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *_inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); STDMETHOD(GetInStreamProcessedSize)(UInt64 *value); STDMETHOD(SetInStream)(ISequentialInStream *inStream); STDMETHOD(ReleaseInStream)(); STDMETHOD(SetOutStreamSize)(const UInt64 *outSize); #ifndef NO_READ_FROM_CODER STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); #endif CDecoder(); virtual ~CDecoder(); }; }} #endif src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.cpp000066400000000000000000000052211325366651500225650ustar00rootroot00000000000000// Lzma2Encoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/CWrappers.h" #include "../Common/StreamUtils.h" #include "Lzma2Encoder.h" namespace NCompress { namespace NLzma { HRESULT SetLzmaProp(PROPID propID, const PROPVARIANT &prop, CLzmaEncProps &ep); } namespace NLzma2 { static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); } static void SzBigFree(void *, void *address) { BigFree(address); } static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } static void SzFree(void *, void *address) { MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CEncoder::CEncoder() { _encoder = 0; _encoder = Lzma2Enc_Create(&g_Alloc, &g_BigAlloc); if (_encoder == 0) throw 1; } CEncoder::~CEncoder() { if (_encoder != 0) Lzma2Enc_Destroy(_encoder); } HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props) { switch (propID) { case NCoderPropID::kBlockSize: if (prop.vt != VT_UI4) return E_INVALIDARG; lzma2Props.blockSize = prop.ulVal; break; case NCoderPropID::kNumThreads: if (prop.vt != VT_UI4) return E_INVALIDARG; lzma2Props.numTotalThreads = (int)(prop.ulVal); break; default: RINOK(NLzma::SetLzmaProp(propID, prop, lzma2Props.lzmaProps)); } return S_OK; } STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps) { CLzma2EncProps lzma2Props; Lzma2EncProps_Init(&lzma2Props); for (UInt32 i = 0; i < numProps; i++) { RINOK(SetLzma2Prop(propIDs[i], coderProps[i], lzma2Props)); } return SResToHRESULT(Lzma2Enc_SetProps(_encoder, &lzma2Props)); } STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream) { Byte prop = Lzma2Enc_WriteProperties(_encoder); return WriteStream(outStream, &prop, 1); } STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) { CSeqInStreamWrap inWrap(inStream); CSeqOutStreamWrap outWrap(outStream); CCompressProgressWrap progressWrap(progress); SRes res = Lzma2Enc_Encode(_encoder, &outWrap.p, &inWrap.p, progress ? &progressWrap.p : NULL); if (res == SZ_ERROR_READ && inWrap.Res != S_OK) return inWrap.Res; if (res == SZ_ERROR_WRITE && outWrap.Res != S_OK) return outWrap.Res; if (res == SZ_ERROR_PROGRESS && progressWrap.Res != S_OK) return progressWrap.Res; return SResToHRESULT(res); } }} src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.h000066400000000000000000000015671325366651500222430ustar00rootroot00000000000000// Lzma2Encoder.h #ifndef __LZMA2_ENCODER_H #define __LZMA2_ENCODER_H #include "../../../C/Lzma2Enc.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma2 { class CEncoder: public ICompressCoder, public ICompressSetCoderProperties, public ICompressWriteCoderProperties, public CMyUnknownImp { CLzma2EncHandle _encoder; public: MY_UNKNOWN_IMP2(ICompressSetCoderProperties, ICompressWriteCoderProperties) STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); CEncoder(); virtual ~CEncoder(); }; }} #endif src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Register.cpp000066400000000000000000000010241325366651500227670ustar00rootroot00000000000000// Lzma2Register.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "Lzma2Decoder.h" static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::NLzma2::CDecoder); } #ifndef EXTRACT_ONLY #include "Lzma2Encoder.h" static void *CreateCodecOut() { return (void *)(ICompressCoder *)(new NCompress::NLzma2::CEncoder); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x21, L"LZMA2", 1, false }; REGISTER_CODEC(LZMA2) src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.cpp000066400000000000000000000164021325366651500224740ustar00rootroot00000000000000// LzmaDecoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/StreamUtils.h" #include "LzmaDecoder.h" static HRESULT SResToHRESULT(SRes res) { switch(res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PARAM: return E_INVALIDARG; case SZ_ERROR_UNSUPPORTED: return E_NOTIMPL; case SZ_ERROR_DATA: return S_FALSE; } return E_FAIL; } namespace NCompress { namespace NLzma { CDecoder::CDecoder(): _inBuf(0), _propsWereSet(false), _outSizeDefined(false), _inBufSize(1 << 20), _outBufSize(1 << 22), FinishStream(false), NeedMoreInput(false) { _inSizeProcessed = 0; _inPos = _inSize = 0; LzmaDec_Construct(&_state); } static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } static void SzFree(void *p, void *address) { p = p; MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CDecoder::~CDecoder() { LzmaDec_Free(&_state, &g_Alloc); MyFree(_inBuf); } STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; } STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; } HRESULT CDecoder::CreateInputBuffer() { if (_inBuf == 0 || _inBufSize != _inBufSizeAllocated) { MyFree(_inBuf); _inBuf = (Byte *)MyAlloc(_inBufSize); if (_inBuf == 0) return E_OUTOFMEMORY; _inBufSizeAllocated = _inBufSize; } return S_OK; } STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size) { RINOK(SResToHRESULT(LzmaDec_Allocate(&_state, prop, size, &g_Alloc))); _propsWereSet = true; return CreateInputBuffer(); } void CDecoder::SetOutStreamSizeResume(const UInt64 *outSize) { _outSizeDefined = (outSize != NULL); if (_outSizeDefined) _outSize = *outSize; _outSizeProcessed = 0; _wrPos = 0; LzmaDec_Init(&_state); } STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) { _inSizeProcessed = 0; _inPos = _inSize = 0; NeedMoreInput = false; SetOutStreamSizeResume(outSize); return S_OK; } HRESULT CDecoder::CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { if (_inBuf == 0 || !_propsWereSet) return S_FALSE; UInt64 startInProgress = _inSizeProcessed; SizeT next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize); for (;;) { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize)); } SizeT dicPos = _state.dicPos; SizeT curSize = next - dicPos; ELzmaFinishMode finishMode = LZMA_FINISH_ANY; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem <= curSize) { curSize = (SizeT)rem; if (FinishStream) finishMode = LZMA_FINISH_END; } } SizeT inSizeProcessed = _inSize - _inPos; ELzmaStatus status; SRes res = LzmaDec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status); _inPos += (UInt32)inSizeProcessed; _inSizeProcessed += inSizeProcessed; SizeT outSizeProcessed = _state.dicPos - dicPos; _outSizeProcessed += outSizeProcessed; bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0); bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize); if (res != 0 || _state.dicPos == next || finished || stopDecoding) { HRESULT res2 = WriteStream(outStream, _state.dic + _wrPos, _state.dicPos - _wrPos); _wrPos = _state.dicPos; if (_state.dicPos == _state.dicBufSize) { _state.dicPos = 0; _wrPos = 0; } next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize); if (res != 0) return S_FALSE; RINOK(res2); if (stopDecoding) { if (status == LZMA_STATUS_NEEDS_MORE_INPUT) NeedMoreInput = true; if (FinishStream && status != LZMA_STATUS_FINISHED_WITH_MARK && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) return S_FALSE; return S_OK; } if (finished) { if (status == LZMA_STATUS_NEEDS_MORE_INPUT) NeedMoreInput = true; return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE); } } if (progress) { UInt64 inSize = _inSizeProcessed - startInProgress; RINOK(progress->SetRatioInfo(&inSize, &_outSizeProcessed)); } } } STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (_inBuf == 0) return E_INVALIDARG; SetOutStreamSize(outSize); return CodeSpec(inStream, outStream, progress); } #ifndef NO_READ_FROM_CODER STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; } STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; } STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; do { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize)); } { SizeT inProcessed = _inSize - _inPos; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem < size) size = (UInt32)rem; } SizeT outProcessed = size; ELzmaStatus status; SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed, _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status); _inPos += (UInt32)inProcessed; _inSizeProcessed += inProcessed; _outSizeProcessed += outProcessed; size -= (UInt32)outProcessed; data = (Byte *)data + outProcessed; if (processedSize) *processedSize += (UInt32)outProcessed; RINOK(SResToHRESULT(res)); if (inProcessed == 0 && outProcessed == 0) return S_OK; } } while (size != 0); return S_OK; } HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress) { SetOutStreamSizeResume(outSize); return CodeSpec(_inStream, outStream, progress); } HRESULT CDecoder::ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize) { RINOK(CreateInputBuffer()); if (processedSize) *processedSize = 0; while (size > 0) { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize)); if (_inSize == 0) break; } { UInt32 curSize = _inSize - _inPos; if (curSize > size) curSize = size; memcpy(data, _inBuf + _inPos, curSize); _inPos += curSize; _inSizeProcessed += curSize; size -= curSize; data = (Byte *)data + curSize; if (processedSize) *processedSize += curSize; } } return S_OK; } #endif }} src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.h000066400000000000000000000051231325366651500221370ustar00rootroot00000000000000// LzmaDecoder.h #ifndef __LZMA_DECODER_H #define __LZMA_DECODER_H #include "../../../C/LzmaDec.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma { class CDecoder: public ICompressCoder, public ICompressSetDecoderProperties2, public ICompressSetBufSize, #ifndef NO_READ_FROM_CODER public ICompressSetInStream, public ICompressSetOutStreamSize, public ISequentialInStream, #endif public CMyUnknownImp { CMyComPtr _inStream; Byte *_inBuf; UInt32 _inPos; UInt32 _inSize; CLzmaDec _state; bool _propsWereSet; bool _outSizeDefined; UInt64 _outSize; UInt64 _inSizeProcessed; UInt64 _outSizeProcessed; UInt32 _inBufSizeAllocated; UInt32 _inBufSize; UInt32 _outBufSize; SizeT _wrPos; HRESULT CreateInputBuffer(); HRESULT CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress); void SetOutStreamSizeResume(const UInt64 *outSize); public: MY_QUERYINTERFACE_BEGIN2(ICompressCoder) MY_QUERYINTERFACE_ENTRY(ICompressSetDecoderProperties2) MY_QUERYINTERFACE_ENTRY(ICompressSetBufSize) #ifndef NO_READ_FROM_CODER MY_QUERYINTERFACE_ENTRY(ICompressSetInStream) MY_QUERYINTERFACE_ENTRY(ICompressSetOutStreamSize) MY_QUERYINTERFACE_ENTRY(ISequentialInStream) #endif MY_QUERYINTERFACE_END MY_ADDREF_RELEASE STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); STDMETHOD(SetOutStreamSize)(const UInt64 *outSize); STDMETHOD(SetInBufSize)(UInt32 streamIndex, UInt32 size); STDMETHOD(SetOutBufSize)(UInt32 streamIndex, UInt32 size); #ifndef NO_READ_FROM_CODER STDMETHOD(SetInStream)(ISequentialInStream *inStream); STDMETHOD(ReleaseInStream)(); STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); HRESULT CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress); HRESULT ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize); UInt64 GetInputProcessedSize() const { return _inSizeProcessed; } #endif bool FinishStream; // set it before decoding, if you need to decode full LZMA stream bool NeedMoreInput; // it's set by decoder, if it needs more input data to decode stream CDecoder(); virtual ~CDecoder(); UInt64 GetOutputProcessedSize() const { return _outSizeProcessed; } }; }} #endif src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.cpp000066400000000000000000000107001325366651500225010ustar00rootroot00000000000000// LzmaEncoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/CWrappers.h" #include "../Common/StreamUtils.h" #include "LzmaEncoder.h" namespace NCompress { namespace NLzma { static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); } static void SzBigFree(void *, void *address) { BigFree(address); } static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } static void SzFree(void *, void *address) { MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CEncoder::CEncoder() { _encoder = 0; _encoder = LzmaEnc_Create(&g_Alloc); if (_encoder == 0) throw 1; } CEncoder::~CEncoder() { if (_encoder != 0) LzmaEnc_Destroy(_encoder, &g_Alloc, &g_BigAlloc); } inline wchar_t GetUpperChar(wchar_t c) { if (c >= 'a' && c <= 'z') c -= 0x20; return c; } static int ParseMatchFinder(const wchar_t *s, int *btMode, int *numHashBytes) { wchar_t c = GetUpperChar(*s++); if (c == L'H') { if (GetUpperChar(*s++) != L'C') return 0; int numHashBytesLoc = (int)(*s++ - L'0'); if (numHashBytesLoc < 4 || numHashBytesLoc > 4) return 0; if (*s++ != 0) return 0; *btMode = 0; *numHashBytes = numHashBytesLoc; return 1; } if (c != L'B') return 0; if (GetUpperChar(*s++) != L'T') return 0; int numHashBytesLoc = (int)(*s++ - L'0'); if (numHashBytesLoc < 2 || numHashBytesLoc > 4) return 0; c = GetUpperChar(*s++); if (c != L'\0') return 0; *btMode = 1; *numHashBytes = numHashBytesLoc; return 1; } #define SET_PROP_32(_id_, _dest_) case NCoderPropID::_id_: ep._dest_ = v; break; HRESULT SetLzmaProp(PROPID propID, const PROPVARIANT &prop, CLzmaEncProps &ep) { if (propID == NCoderPropID::kMatchFinder) { if (prop.vt != VT_BSTR) return E_INVALIDARG; return ParseMatchFinder(prop.bstrVal, &ep.btMode, &ep.numHashBytes) ? S_OK : E_INVALIDARG; } if (propID > NCoderPropID::kReduceSize) return S_OK; if (propID == NCoderPropID::kReduceSize) { if (prop.vt == VT_UI8) ep.reduceSize = prop.uhVal.QuadPart; return S_OK; } if (prop.vt != VT_UI4) return E_INVALIDARG; UInt32 v = prop.ulVal; switch (propID) { case NCoderPropID::kDefaultProp: if (v > 31) return E_INVALIDARG; ep.dictSize = (UInt32)1 << (unsigned)v; break; SET_PROP_32(kLevel, level) SET_PROP_32(kNumFastBytes, fb) SET_PROP_32(kMatchFinderCycles, mc) SET_PROP_32(kAlgorithm, algo) SET_PROP_32(kDictionarySize, dictSize) SET_PROP_32(kPosStateBits, pb) SET_PROP_32(kLitPosBits, lp) SET_PROP_32(kLitContextBits, lc) SET_PROP_32(kNumThreads, numThreads) default: return E_INVALIDARG; } return S_OK; } STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps) { CLzmaEncProps props; LzmaEncProps_Init(&props); for (UInt32 i = 0; i < numProps; i++) { const PROPVARIANT &prop = coderProps[i]; PROPID propID = propIDs[i]; switch (propID) { case NCoderPropID::kEndMarker: if (prop.vt != VT_BOOL) return E_INVALIDARG; props.writeEndMark = (prop.boolVal != VARIANT_FALSE); break; default: RINOK(SetLzmaProp(propID, prop, props)); } } return SResToHRESULT(LzmaEnc_SetProps(_encoder, &props)); } STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream) { Byte props[LZMA_PROPS_SIZE]; size_t size = LZMA_PROPS_SIZE; RINOK(LzmaEnc_WriteProperties(_encoder, props, &size)); return WriteStream(outStream, props, size); } STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) { CSeqInStreamWrap inWrap(inStream); CSeqOutStreamWrap outWrap(outStream); CCompressProgressWrap progressWrap(progress); SRes res = LzmaEnc_Encode(_encoder, &outWrap.p, &inWrap.p, progress ? &progressWrap.p : NULL, &g_Alloc, &g_BigAlloc); _inputProcessed = inWrap.Processed; if (res == SZ_ERROR_READ && inWrap.Res != S_OK) return inWrap.Res; if (res == SZ_ERROR_WRITE && outWrap.Res != S_OK) return outWrap.Res; if (res == SZ_ERROR_PROGRESS && progressWrap.Res != S_OK) return progressWrap.Res; return SResToHRESULT(res); } }} src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.h000066400000000000000000000017201325366651500221500ustar00rootroot00000000000000// LzmaEncoder.h #ifndef __LZMA_ENCODER_H #define __LZMA_ENCODER_H #include "../../../C/LzmaEnc.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma { class CEncoder: public ICompressCoder, public ICompressSetCoderProperties, public ICompressWriteCoderProperties, public CMyUnknownImp { CLzmaEncHandle _encoder; UInt64 _inputProcessed; public: MY_UNKNOWN_IMP2(ICompressSetCoderProperties, ICompressWriteCoderProperties) STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); CEncoder(); virtual ~CEncoder(); UInt64 GetInputProcessedSize() const { return _inputProcessed; } }; }} #endif src/libs/7zip/unix/CPP/7zip/Compress/LzmaRegister.cpp000066400000000000000000000010211325366651500227020ustar00rootroot00000000000000// LzmaRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "LzmaDecoder.h" static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::NLzma::CDecoder); } #ifndef EXTRACT_ONLY #include "LzmaEncoder.h" static void *CreateCodecOut() { return (void *)(ICompressCoder *)(new NCompress::NLzma::CEncoder); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x030101, L"LZMA", 1, false }; REGISTER_CODEC(LZMA) src/libs/7zip/unix/CPP/7zip/Compress/RangeCoder.h000066400000000000000000000076751325366651500217750ustar00rootroot00000000000000// Compress/RangeCoder.h // 2013-01-10 : Igor Pavlov : Public domain #ifndef __COMPRESS_RANGE_CODER_H #define __COMPRESS_RANGE_CODER_H #include "../Common/InBuffer.h" #include "../Common/OutBuffer.h" namespace NCompress { namespace NRangeCoder { const unsigned kNumTopBits = 24; const UInt32 kTopValue = (1 << kNumTopBits); class CEncoder { UInt32 _cacheSize; Byte _cache; public: UInt64 Low; UInt32 Range; COutBuffer Stream; bool Create(UInt32 bufSize) { return Stream.Create(bufSize); } void SetStream(ISequentialOutStream *stream) { Stream.SetStream(stream); } void Init() { Stream.Init(); Low = 0; Range = 0xFFFFFFFF; _cacheSize = 1; _cache = 0; } void FlushData() { // Low += 1; for (int i = 0; i < 5; i++) ShiftLow(); } HRESULT FlushStream() { return Stream.Flush(); } void Encode(UInt32 start, UInt32 size, UInt32 total) { Low += start * (Range /= total); Range *= size; while (Range < kTopValue) { Range <<= 8; ShiftLow(); } } void ShiftLow() { if ((UInt32)Low < (UInt32)0xFF000000 || (unsigned)(Low >> 32) != 0) { Byte temp = _cache; do { Stream.WriteByte((Byte)(temp + (Byte)(Low >> 32))); temp = 0xFF; } while (--_cacheSize != 0); _cache = (Byte)((UInt32)Low >> 24); } _cacheSize++; Low = (UInt32)Low << 8; } void EncodeDirectBits(UInt32 value, int numBits) { for (numBits--; numBits >= 0; numBits--) { Range >>= 1; Low += Range & (0 - ((value >> numBits) & 1)); if (Range < kTopValue) { Range <<= 8; ShiftLow(); } } } void EncodeBit(UInt32 size0, UInt32 numTotalBits, UInt32 symbol) { UInt32 newBound = (Range >> numTotalBits) * size0; if (symbol == 0) Range = newBound; else { Low += newBound; Range -= newBound; } while (Range < kTopValue) { Range <<= 8; ShiftLow(); } } UInt64 GetProcessedSize() { return Stream.GetProcessedSize() + _cacheSize + 4; } }; class CDecoder { public: CInBuffer Stream; UInt32 Range; UInt32 Code; bool Create(UInt32 bufSize) { return Stream.Create(bufSize); } void Normalize() { while (Range < kTopValue) { Code = (Code << 8) | Stream.ReadByte(); Range <<= 8; } } void SetStream(ISequentialInStream *stream) { Stream.SetStream(stream); } void Init() { Stream.Init(); Code = 0; Range = 0xFFFFFFFF; for (int i = 0; i < 5; i++) Code = (Code << 8) | Stream.ReadByte(); } UInt32 GetThreshold(UInt32 total) { return (Code) / (Range /= total); } void Decode(UInt32 start, UInt32 size) { Code -= start * Range; Range *= size; Normalize(); } UInt32 DecodeDirectBits(int numTotalBits) { UInt32 range = Range; UInt32 code = Code; UInt32 result = 0; for (int i = numTotalBits; i != 0; i--) { range >>= 1; /* result <<= 1; if (code >= range) { code -= range; result |= 1; } */ UInt32 t = (code - range) >> 31; code -= range & (t - 1); result = (result << 1) | (1 - t); if (range < kTopValue) { code = (code << 8) | Stream.ReadByte(); range <<= 8; } } Range = range; Code = code; return result; } UInt32 DecodeBit(UInt32 size0, UInt32 numTotalBits) { UInt32 newBound = (Range >> numTotalBits) * size0; UInt32 symbol; if (Code < newBound) { symbol = 0; Range = newBound; } else { symbol = 1; Code -= newBound; Range -= newBound; } Normalize(); return symbol; } UInt64 GetProcessedSize() { return Stream.GetProcessedSize(); } }; }} #endif src/libs/7zip/unix/CPP/7zip/Compress/RangeCoderBit.h000066400000000000000000000057161325366651500224260ustar00rootroot00000000000000// Compress/RangeCoderBit.h // 2013-01-10 : Igor Pavlov : Public domain #ifndef __COMPRESS_RANGE_CODER_BIT_H #define __COMPRESS_RANGE_CODER_BIT_H #include "RangeCoder.h" namespace NCompress { namespace NRangeCoder { const unsigned kNumBitModelTotalBits = 11; const UInt32 kBitModelTotal = (1 << kNumBitModelTotalBits); const unsigned kNumMoveReducingBits = 4; const unsigned kNumBitPriceShiftBits = 4; const UInt32 kBitPrice = 1 << kNumBitPriceShiftBits; extern UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; template class CBitModel { public: UInt32 Prob; void UpdateModel(UInt32 symbol) { /* Prob -= (Prob + ((symbol - 1) & ((1 << numMoveBits) - 1))) >> numMoveBits; Prob += (1 - symbol) << (kNumBitModelTotalBits - numMoveBits); */ if (symbol == 0) Prob += (kBitModelTotal - Prob) >> numMoveBits; else Prob -= (Prob) >> numMoveBits; } public: void Init() { Prob = kBitModelTotal / 2; } }; template class CBitEncoder: public CBitModel { public: void Encode(CEncoder *encoder, UInt32 symbol) { /* encoder->EncodeBit(this->Prob, kNumBitModelTotalBits, symbol); this->UpdateModel(symbol); */ UInt32 newBound = (encoder->Range >> kNumBitModelTotalBits) * this->Prob; if (symbol == 0) { encoder->Range = newBound; this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits; } else { encoder->Low += newBound; encoder->Range -= newBound; this->Prob -= (this->Prob) >> numMoveBits; } if (encoder->Range < kTopValue) { encoder->Range <<= 8; encoder->ShiftLow(); } } UInt32 GetPrice(UInt32 symbol) const { return ProbPrices[(this->Prob ^ ((-(int)(Int32)symbol)) & (kBitModelTotal - 1)) >> kNumMoveReducingBits]; } UInt32 GetPrice0() const { return ProbPrices[this->Prob >> kNumMoveReducingBits]; } UInt32 GetPrice1() const { return ProbPrices[(this->Prob ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]; } }; template class CBitDecoder: public CBitModel { public: UInt32 Decode(CDecoder *decoder) { UInt32 newBound = (decoder->Range >> kNumBitModelTotalBits) * this->Prob; if (decoder->Code < newBound) { decoder->Range = newBound; this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits; if (decoder->Range < kTopValue) { decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte(); decoder->Range <<= 8; } return 0; } else { decoder->Range -= newBound; decoder->Code -= newBound; this->Prob -= (this->Prob) >> numMoveBits; if (decoder->Range < kTopValue) { decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte(); decoder->Range <<= 8; } return 1; } } }; }} #endif src/libs/7zip/unix/CPP/7zip/Guid.txt000066400000000000000000000071121325366651500174330ustar00rootroot00000000000000{23170F69-40C1-278A-0000-00yy00xx0000} 00 IProgress.h 05 IProgress 01 IFolderArchive.h // 05 IArchiveFolder // old // 06 IInFolderArchive // old 07 IFileExtractCallback.h::IFolderArchiveExtractCallback // 0A IOutFolderArchive 0B IFolderArchiveUpdateCallback 0C Agent.h::IArchiveFolderInternal 0D IArchiveFolder 0E IInFolderArchive 0F IOutFolderArchive 20 IFileExtractCallback.h::IGetProp 30 IFileExtractCallback.h::IFolderExtractToStreamCallback 03 IStream.h 01 ISequentialInStream 02 ISequentialOutStream 03 IInStream 04 IOutStream 06 IStreamGetSize 07 IOutStreamFlush 08 IStreamGetProps 09 IStreamGetProps2 04 ICoder.h 04 ICompressProgressInfo 05 ICompressCoder 18 ICompressCoder2 20 ICompressSetCoderProperties 21 ICompressSetDecoderProperties // 22 ICompressSetDecoderProperties2 23 ICompressWriteCoderProperties 24 ICompressGetInStreamProcessedSize 25 ICompressSetCoderMt 30 ICompressGetSubStreamSize 31 ICompressSetInStream 32 ICompressSetOutStream 33 ICompressSetInStreamSize 34 ICompressSetOutStreamSize 35 ICompressSetBufSize 40 ICompressFilter 60 ICompressCodecsInfo 61 ISetCompressCodecsInfo 80 ICryptoProperties 88 ICryptoResetSalt 8C ICryptoResetInitVector 90 ICryptoSetPassword A0 ICryptoSetCRC C0 IHasher C1 IHashers 05 IPassword.h 10 ICryptoGetTextPassword 11 ICryptoGetTextPassword2 06 IArchive.h 03 ISetProperties 04 IArchiveKeepModeForNextOpen 05 IArchiveAllowTail 10 IArchiveOpenCallback 20 IArchiveExtractCallback 30 IArchiveOpenVolumeCallback 40 IInArchiveGetStream 50 IArchiveOpenSetSubArchiveName 60 IInArchive 61 IArchiveOpenSeq 70 IArchiveGetRawProps 71 IArchiveGetRootProps 80 IArchiveUpdateCallback 82 IArchiveUpdateCallback2 A0 IOutArchive 08 IFolder.h 00 IFolderFolder 01 IEnumProperties 02 IFolderGetTypeID 03 IFolderGetPath 04 IFolderWasChanged 05 // IFolderReload 06 // IFolderOperations old 07 IFolderGetSystemIconIndex 08 IFolderGetItemFullSize 09 IFolderClone 0A IFolderSetFlatMode 0B IFolderOperationsExtractCallback 0C // 0D // 0E IFolderProperties 0F 10 IFolderArcProps 11 IGetFolderArcProps 12 // IFolderOperations 13 IFolderOperations 14 IFolderCalcItemFullSize 15 IFolderCompare 16 IFolderGetItemName 09 IFolder.h :: FOLDER_MANAGER_INTERFACE 00 - 04 // old IFolderManager 05 IFolderManager // 0A PluginInterface.h 00 IInitContextMenu 01 IPluginOptionsCallback 02 IPluginOptions Handler GUIDs: {23170F69-40C1-278A-1000-000110xx0000} 01 Zip 02 BZip2 03 Rar 04 Arj 05 Z 06 Lzh 07 7z 08 Cab 09 Nsis 0A lzma 0B lzma86 0C xz 0D ppmd CD IHex CE Hxs CF TE D0 UEFIc D1 UEFIs D2 SquashFS D3 CramFS D4 APM D5 Mslz D6 Flv D7 Swf D8 Swfc D9 Ntfs DA Fat DB Mbr DC Vhd DD Pe DE Elf DF Mach-O E0 Udf E1 Xar E2 Mub E3 Hfs E4 Dmg E5 Compound E6 Wim E7 Iso E8 E9 Chm EA Split EB Rpm EC Deb ED Cpio EE Tar EF GZip {23170F69-40C1-278A-1000-000100030000} CAgentArchiveHandle {23170F69-40C1-278A-1000-000100020000} ContextMenu.h::CZipContextMenu {23170F69-40C1-278B- old codecs clsids {23170F69-40C1-278D-1000-000100020000} OptionsDialog.h::CLSID_CSevenZipOptions {23170F69-40C1-2790-id} Codec Decoders {23170F69-40C1-2791-id} Codec Encoders {23170F69-40C1-2792-id} Hashers src/libs/7zip/unix/CPP/7zip/ICoder.h000066400000000000000000000125541325366651500173260ustar00rootroot00000000000000// ICoder.h #ifndef __ICODER_H #define __ICODER_H #include "IStream.h" #define CODER_INTERFACE(i, x) DECL_INTERFACE(i, 4, x) CODER_INTERFACE(ICompressProgressInfo, 0x04) { STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize) PURE; }; CODER_INTERFACE(ICompressCoder, 0x05) { STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress) PURE; }; CODER_INTERFACE(ICompressCoder2, 0x18) { STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress) PURE; }; namespace NCoderPropID { enum EEnum { kDefaultProp = 0, kDictionarySize, kUsedMemorySize, kOrder, kBlockSize, kPosStateBits, kLitContextBits, kLitPosBits, kNumFastBytes, kMatchFinder, kMatchFinderCycles, kNumPasses, kAlgorithm, kNumThreads, kEndMarker, kLevel, kReduceSize // estimated size of data that will be compressed. Encoder can use this value to reduce dictionary size. }; } CODER_INTERFACE(ICompressSetCoderProperties, 0x20) { STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps) PURE; }; /* CODER_INTERFACE(ICompressSetCoderProperties, 0x21) { STDMETHOD(SetDecoderProperties)(ISequentialInStream *inStream) PURE; }; */ CODER_INTERFACE(ICompressSetDecoderProperties2, 0x22) { STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size) PURE; }; CODER_INTERFACE(ICompressWriteCoderProperties, 0x23) { STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream) PURE; }; CODER_INTERFACE(ICompressGetInStreamProcessedSize, 0x24) { STDMETHOD(GetInStreamProcessedSize)(UInt64 *value) PURE; }; CODER_INTERFACE(ICompressSetCoderMt, 0x25) { STDMETHOD(SetNumberOfThreads)(UInt32 numThreads) PURE; }; CODER_INTERFACE(ICompressGetSubStreamSize, 0x30) { STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value) PURE; }; CODER_INTERFACE(ICompressSetInStream, 0x31) { STDMETHOD(SetInStream)(ISequentialInStream *inStream) PURE; STDMETHOD(ReleaseInStream)() PURE; }; CODER_INTERFACE(ICompressSetOutStream, 0x32) { STDMETHOD(SetOutStream)(ISequentialOutStream *outStream) PURE; STDMETHOD(ReleaseOutStream)() PURE; }; CODER_INTERFACE(ICompressSetInStreamSize, 0x33) { STDMETHOD(SetInStreamSize)(const UInt64 *inSize) PURE; }; CODER_INTERFACE(ICompressSetOutStreamSize, 0x34) { STDMETHOD(SetOutStreamSize)(const UInt64 *outSize) PURE; }; CODER_INTERFACE(ICompressSetBufSize, 0x35) { STDMETHOD(SetInBufSize)(UInt32 streamIndex, UInt32 size) PURE; STDMETHOD(SetOutBufSize)(UInt32 streamIndex, UInt32 size) PURE; }; CODER_INTERFACE(ICompressFilter, 0x40) { STDMETHOD(Init)() PURE; STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size) PURE; // Filter converts as most as possible bytes // Filter return outSize (UInt32) // if (outSize <= size): Filter have converted outSize bytes // if (outSize > size): Filter have not converted anything. // and it needs at least outSize bytes to convert one block // (it's for crypto block algorithms). }; CODER_INTERFACE(ICompressCodecsInfo, 0x60) { STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods) PURE; STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) PURE; STDMETHOD(CreateDecoder)(UInt32 index, const GUID *iid, void **coder) PURE; STDMETHOD(CreateEncoder)(UInt32 index, const GUID *iid, void **coder) PURE; }; CODER_INTERFACE(ISetCompressCodecsInfo, 0x61) { STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo) PURE; }; CODER_INTERFACE(ICryptoProperties, 0x80) { STDMETHOD(SetKey)(const Byte *data, UInt32 size) PURE; STDMETHOD(SetInitVector)(const Byte *data, UInt32 size) PURE; }; /* CODER_INTERFACE(ICryptoResetSalt, 0x88) { STDMETHOD(ResetSalt)() PURE; }; */ CODER_INTERFACE(ICryptoResetInitVector, 0x8C) { STDMETHOD(ResetInitVector)() PURE; }; CODER_INTERFACE(ICryptoSetPassword, 0x90) { STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size) PURE; }; CODER_INTERFACE(ICryptoSetCRC, 0xA0) { STDMETHOD(CryptoSetCRC)(UInt32 crc) PURE; }; ////////////////////// // It's for DLL file namespace NMethodPropID { enum EEnum { kID, kName, kDecoder, kEncoder, kInStreams, kOutStreams, kDescription, kDecoderIsAssigned, kEncoderIsAssigned, kDigestSize }; } CODER_INTERFACE(IHasher, 0xC0) { STDMETHOD_(void, Init)() PURE; STDMETHOD_(void, Update)(const void *data, UInt32 size) PURE; STDMETHOD_(void, Final)(Byte *digest) PURE; STDMETHOD_(UInt32, GetDigestSize)() PURE; }; CODER_INTERFACE(IHashers, 0xC1) { STDMETHOD_(UInt32, GetNumHashers)() PURE; STDMETHOD(GetHasherProp)(UInt32 index, PROPID propID, PROPVARIANT *value) PURE; STDMETHOD(CreateHasher)(UInt32 index, IHasher **hasher) PURE; }; extern "C" { typedef HRESULT (WINAPI *Func_GetNumberOfMethods)(UInt32 *numMethods); typedef HRESULT (WINAPI *Func_GetMethodProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); typedef HRESULT (WINAPI *Func_GetHashers)(IHashers **hashers); } #endif src/libs/7zip/unix/CPP/7zip/IDecl.h000066400000000000000000000005571325366651500171410ustar00rootroot00000000000000// IDecl.h #ifndef __IDECL_H #define __IDECL_H #include "../Common/MyUnknown.h" #define DECL_INTERFACE_SUB(i, base, groupId, subId) \ DEFINE_GUID(IID_ ## i, \ 0x23170F69, 0x40C1, 0x278A, 0, 0, 0, (groupId), 0, (subId), 0, 0); \ struct i: public base #define DECL_INTERFACE(i, groupId, subId) DECL_INTERFACE_SUB(i, IUnknown, groupId, subId) #endif src/libs/7zip/unix/CPP/7zip/IPassword.h000066400000000000000000000007461325366651500200740ustar00rootroot00000000000000// IPassword.h #ifndef __IPASSWORD_H #define __IPASSWORD_H #include "../Common/MyTypes.h" #include "../Common/MyUnknown.h" #include "IDecl.h" #define PASSWORD_INTERFACE(i, x) DECL_INTERFACE(i, 5, x) PASSWORD_INTERFACE(ICryptoGetTextPassword, 0x10) { STDMETHOD(CryptoGetTextPassword)(BSTR *password) PURE; }; PASSWORD_INTERFACE(ICryptoGetTextPassword2, 0x11) { STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password) PURE; }; #endif src/libs/7zip/unix/CPP/7zip/IProgress.h000066400000000000000000000013651325366651500200740ustar00rootroot00000000000000// Interface/IProgress.h #ifndef __IPROGRESS_H #define __IPROGRESS_H #include "../Common/MyTypes.h" #include "../Common/MyUnknown.h" #include "IDecl.h" #define INTERFACE_IProgress(x) \ STDMETHOD(SetTotal)(UInt64 total) x; \ STDMETHOD(SetCompleted)(const UInt64 *completeValue) x; \ DECL_INTERFACE(IProgress, 0, 5) { INTERFACE_IProgress(PURE) }; /* // {23170F69-40C1-278A-0000-000000050002} DEFINE_GUID(IID_IProgress2, 0x23170F69, 0x40C1, 0x278A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02); MIDL_INTERFACE("23170F69-40C1-278A-0000-000000050002") IProgress2: public IUnknown { public: STDMETHOD(SetTotal)(const UInt64 *total) PURE; STDMETHOD(SetCompleted)(const UInt64 *completeValue) PURE; }; */ #endif src/libs/7zip/unix/CPP/7zip/IStream.h000066400000000000000000000073051325366651500175230ustar00rootroot00000000000000// IStream.h #ifndef __ISTREAM_H #define __ISTREAM_H #include "../Common/MyTypes.h" #include "../Common/MyUnknown.h" #include "IDecl.h" #define STREAM_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 3, x) #define STREAM_INTERFACE(i, x) STREAM_INTERFACE_SUB(i, IUnknown, x) STREAM_INTERFACE(ISequentialInStream, 0x01) { STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize) PURE; /* The requirement for caller: (processedSize != NULL). The callee can allow (processedSize == NULL) for compatibility reasons. if (size == 0), this function returns S_OK and (*processedSize) is set to 0. if (size != 0) { Partial read is allowed: (*processedSize <= avail_size && *processedSize <= size), where (avail_size) is the size of remaining bytes in stream. If (avail_size != 0), this function must read at least 1 byte: (*processedSize > 0). You must call Read() in loop, if you need to read exact amount of data. } If seek pointer before Read() call was changed to position past the end of stream: if (seek_pointer >= stream_size), this function returns S_OK and (*processedSize) is set to 0. ERROR CASES: If the function returns error code, then (*processedSize) is size of data written to (data) buffer (it can be data before error or data with errors). The recommended way for callee to work with reading errors: 1) write part of data before error to (data) buffer and return S_OK. 2) return error code for further calls of Read(). */ }; STREAM_INTERFACE(ISequentialOutStream, 0x02) { STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize) PURE; /* The requirement for caller: (processedSize != NULL). The callee can allow (processedSize == NULL) for compatibility reasons. if (size != 0) { Partial write is allowed: (*processedSize <= size), but this function must write at least 1 byte: (*processedSize > 0). You must call Write() in loop, if you need to write exact amount of data. } ERROR CASES: If the function returns error code, then (*processedSize) is size of data written from (data) buffer. */ }; #ifdef __HRESULT_FROM_WIN32 #define HRESULT_WIN32_ERROR_NEGATIVE_SEEK __HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK) #else #define HRESULT_WIN32_ERROR_NEGATIVE_SEEK HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK) #endif /* Seek() Function If you seek before the beginning of the stream, Seek() function returns error code: Recommended error code is __HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK). or STG_E_INVALIDFUNCTION It is allowed to seek past the end of the stream. if Seek() returns error, then the value of *newPosition is undefined. */ STREAM_INTERFACE_SUB(IInStream, ISequentialInStream, 0x03) { STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE; }; STREAM_INTERFACE_SUB(IOutStream, ISequentialOutStream, 0x04) { STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE; STDMETHOD(SetSize)(UInt64 newSize) PURE; }; STREAM_INTERFACE(IStreamGetSize, 0x06) { STDMETHOD(GetSize)(UInt64 *size) PURE; }; STREAM_INTERFACE(IOutStreamFlush, 0x07) { STDMETHOD(Flush)() PURE; }; STREAM_INTERFACE(IStreamGetProps, 0x08) { STDMETHOD(GetProps)(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) PURE; }; struct CStreamFileProps { UInt64 Size; UInt64 VolID; UInt64 FileID_Low; UInt64 FileID_High; UInt32 NumLinks; UInt32 Attrib; FILETIME CTime; FILETIME ATime; FILETIME MTime; }; STREAM_INTERFACE(IStreamGetProps2, 0x09) { STDMETHOD(GetProps2)(CStreamFileProps *props) PURE; }; #endif src/libs/7zip/unix/CPP/7zip/PropID.h000066400000000000000000000050511325366651500173100ustar00rootroot00000000000000// PropID.h #ifndef __7ZIP_PROP_ID_H #define __7ZIP_PROP_ID_H #include "../Common/MyTypes.h" enum { kpidNoProperty = 0, kpidMainSubfile, kpidHandlerItemIndex, kpidPath, kpidName, kpidExtension, kpidIsDir, kpidSize, kpidPackSize, kpidAttrib, kpidCTime, kpidATime, kpidMTime, kpidSolid, kpidCommented, kpidEncrypted, kpidSplitBefore, kpidSplitAfter, kpidDictionarySize, kpidCRC, kpidType, kpidIsAnti, kpidMethod, kpidHostOS, kpidFileSystem, kpidUser, kpidGroup, kpidBlock, kpidComment, kpidPosition, kpidPrefix, kpidNumSubDirs, kpidNumSubFiles, kpidUnpackVer, kpidVolume, kpidIsVolume, kpidOffset, kpidLinks, kpidNumBlocks, kpidNumVolumes, kpidTimeType, kpidBit64, kpidBigEndian, kpidCpu, kpidPhySize, kpidHeadersSize, kpidChecksum, kpidCharacts, kpidVa, kpidId, kpidShortName, kpidCreatorApp, kpidSectorSize, kpidPosixAttrib, kpidSymLink, kpidError, kpidTotalSize, kpidFreeSpace, kpidClusterSize, kpidVolumeName, kpidLocalName, kpidProvider, kpidNtSecure, kpidIsAltStream, kpidIsAux, kpidIsDeleted, kpidIsTree, kpidSha1, kpidSha256, kpidErrorType, kpidNumErrors, kpidErrorFlags, kpidWarningFlags, kpidWarning, kpidNumStreams, kpidNumAltStreams, kpidAltStreamsSize, kpidVirtualSize, kpidUnpackSize, kpidTotalPhySize, kpidVolumeIndex, kpidSubType, kpidShortComment, kpidCodePage, kpidIsNotArcType, kpidPhySizeCantBeDetected, kpidZerosTailIsAllowed, kpidTailSize, kpidEmbeddedStubSize, kpidNtReparse, kpidHardLink, kpidINode, kpidStreamId, kpid_NUM_DEFINED, kpidUserDefined = 0x10000 }; extern Byte k7z_PROPID_To_VARTYPE[kpid_NUM_DEFINED]; // VARTYPE const UInt32 kpv_ErrorFlags_IsNotArc = 1 << 0; const UInt32 kpv_ErrorFlags_HeadersError = 1 << 1; const UInt32 kpv_ErrorFlags_EncryptedHeadersError = 1 << 2; const UInt32 kpv_ErrorFlags_UnavailableStart = 1 << 3; const UInt32 kpv_ErrorFlags_UnconfirmedStart = 1 << 4; const UInt32 kpv_ErrorFlags_UnexpectedEnd = 1 << 5; const UInt32 kpv_ErrorFlags_DataAfterEnd = 1 << 6; const UInt32 kpv_ErrorFlags_UnsupportedMethod = 1 << 7; const UInt32 kpv_ErrorFlags_UnsupportedFeature = 1 << 8; const UInt32 kpv_ErrorFlags_DataError = 1 << 9; const UInt32 kpv_ErrorFlags_CrcError = 1 << 10; // const UInt32 kpv_ErrorFlags_Unsupported = 1 << 11; #endif src/libs/7zip/unix/CPP/7zip/UI/000077500000000000000000000000001325366651500163165ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/UI/Common/000077500000000000000000000000001325366651500175465ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.cpp000066400000000000000000001112301325366651500237400ustar00rootroot00000000000000// ArchiveCommandLine.cpp #include "StdAfx.h" #undef printf #undef sprintf #ifdef _WIN32 #ifndef UNDER_CE #include #endif #endif #include #include "../../../Common/ListFileUtils.h" #include "../../../Common/StringConvert.h" #include "../../../Common/StringToInt.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileName.h" #ifdef _WIN32 #include "../../../Windows/FileMapping.h" #include "../../../Windows/Synchronization.h" #else #include "myPrivate.h" #endif #include "ArchiveCommandLine.h" #include "EnumDirItems.h" #include "SortUtils.h" #include "Update.h" #include "UpdateAction.h" extern bool g_CaseSensitive; #ifdef UNDER_CE #define MY_IS_TERMINAL(x) false; #else #if _MSC_VER >= 1400 #define MY_isatty_fileno(x) _isatty(_fileno(x)) #else #define MY_isatty_fileno(x) isatty(fileno(x)) #endif #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0); #endif using namespace NCommandLineParser; using namespace NWindows; using namespace NFile; static bool StringToUInt32(const wchar_t *s, UInt32 &v) { if (*s == 0) return false; const wchar_t *end; v = ConvertStringToUInt32(s, &end); return *end == 0; } static void AddNewLine(UString &s) { s += L'\n'; } CArcCmdLineException::CArcCmdLineException(const char *a, const wchar_t *u) { (*this) += MultiByteToUnicodeString(a); if (u) { AddNewLine(*this); (*this) += u; } } int g_CodePage = -1; namespace NKey { enum Enum { kHelp1 = 0, kHelp2, kHelp3, kDisableHeaders, kDisablePercents, kArchiveType, kYes, #ifndef _NO_CRYPTO kPassword, #endif kProperty, kOutputDir, kWorkingDir, kInclude, kExclude, kArInclude, kArExclude, kNoArName, kUpdate, kVolume, kRecursed, kSfx, kStdIn, kStdOut, kOverwrite, kEmail, kShowDialog, kLargePages, // kListfileCharSet, // kConsoleCharSet, kTechMode, #ifdef _WIN32 kShareForWrite, #endif kUseLStat, kCaseSensitive, kHash, kArcNameMode, kDisableWildcardParsing, kElimDup, kFullPathMode, kHardLinks, kSymLinks, kNtSecurity, kAltStreams, kReplaceColonForAltStream, kWriteToAltStreamIfColon, kDeleteAfterCompressing, kSetArcMTime, kExcludedArcType }; } static const wchar_t kRecursedIDChar = 'r'; static const char *kRecursedPostCharSet = "0-"; static const char *k_ArcNameMode_PostCharSet = "sea"; static inline const EArcNameMode ParseArcNameMode(int postCharIndex) { switch (postCharIndex) { case 1: return k_ArcNameMode_Exact; case 2: return k_ArcNameMode_Add; default: return k_ArcNameMode_Smart; } } namespace NRecursedPostCharIndex { enum EEnum { kWildcardRecursionOnly = 0, kNoRecursion = 1 }; } static const char kImmediateNameID = '!'; static const char kMapNameID = '#'; static const char kFileListID = '@'; static const char kSomeCludePostStringMinSize = 2; // at least <@|!>ame must be static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!>ame must be static const char *kOverwritePostCharSet = "asut"; NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = { NExtract::NOverwriteMode::kOverwrite, NExtract::NOverwriteMode::kSkip, NExtract::NOverwriteMode::kRename, NExtract::NOverwriteMode::kRenameExisting }; static const CSwitchForm kSwitchForms[] = { { "?" }, { "h" }, { "-help" }, { "ba" }, { "bd" }, { "t", NSwitchType::kString, false, 1 }, { "y" }, #ifndef _NO_CRYPTO { "p", NSwitchType::kString }, #endif { "m", NSwitchType::kString, true, 1 }, { "o", NSwitchType::kString, false, 1 }, { "w", NSwitchType::kString }, { "i", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "x", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "ai", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "ax", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "an" }, { "u", NSwitchType::kString, true, 1}, { "v", NSwitchType::kString, true, 1}, { "r", NSwitchType::kChar, false, 0, kRecursedPostCharSet }, { "sfx", NSwitchType::kString }, { "si", NSwitchType::kString }, { "so" }, { "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet}, { "seml", NSwitchType::kString, false, 0}, { "ad" }, { "slp", NSwitchType::kMinus }, // { "scs", NSwitchType::kString }, // { "scc", NSwitchType::kString }, { "slt" }, // { "ssw" }, { "l" }, { "ssc", NSwitchType::kMinus }, { "scrc", NSwitchType::kString, true, 0 }, { "sa", NSwitchType::kChar, false, 1, k_ArcNameMode_PostCharSet }, { "spd" }, { "spe", NSwitchType::kMinus }, { "spf", NSwitchType::kString, false, 0 }, { "snh", NSwitchType::kMinus }, { "snl", NSwitchType::kMinus }, { "sni" }, { "sns", NSwitchType::kMinus }, { "snr" }, { "snc" }, { "sdel" }, { "stl" }, { "stx", NSwitchType::kString, true, 1 } }; static const wchar_t *kUniversalWildcard = L"*"; static const int kMinNonSwitchWords = 1; static const int kCommandIndex = 0; // static const char *kUserErrorMessage = "Incorrect command line"; static const char *kCannotFindListFile = "Cannot find listfile"; static const char *kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch."; static const char *kIncorrectWildcardInListFile = "Incorrect wildcard in listfile"; static const char *kIncorrectWildcardInCommandLine = "Incorrect wildcard in command line"; static const char *kTerminalOutError = "I won't write compressed data to a terminal"; static const char *kSameTerminalError = "I won't write data and program's messages to same terminal"; static const char *kEmptyFilePath = "Empty file path"; static const char *kCannotFindArchive = "Cannot find archive"; bool CArcCommand::IsFromExtractGroup() const { switch (CommandType) { case NCommandType::kTest: case NCommandType::kExtract: case NCommandType::kExtractFull: return true; } return false; } NExtract::NPathMode::EEnum CArcCommand::GetPathMode() const { switch (CommandType) { case NCommandType::kTest: case NCommandType::kExtractFull: return NExtract::NPathMode::kFullPaths; } return NExtract::NPathMode::kNoPaths; } bool CArcCommand::IsFromUpdateGroup() const { switch (CommandType) { case NCommandType::kAdd: case NCommandType::kUpdate: case NCommandType::kDelete: case NCommandType::kRename: return true; } return false; } static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) { switch (index) { case NRecursedPostCharIndex::kWildcardRecursionOnly: return NRecursedType::kWildcardOnlyRecursed; case NRecursedPostCharIndex::kNoRecursion: return NRecursedType::kNonRecursed; default: return NRecursedType::kRecursed; } } static const char *g_Commands = "audtexlbih"; static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command) { UString s = commandString; s.MakeLower_Ascii(); if (s.Len() == 1) { if (s[0] > 0x7F) return false; int index = FindCharPosInString(g_Commands, (char)s[0]); if (index < 0) return false; command.CommandType = (NCommandType::EEnum)index; return true; } if (s.Len() == 2 && s[0] == 'r' && s[1] == 'n') { command.CommandType = (NCommandType::kRename); return true; } return false; } // ------------------------------------------------------------------ // filenames functions static void AddNameToCensor(NWildcard::CCensor &censor, const UString &name, bool include, NRecursedType::EEnum type, bool wildcardMatching) { bool recursed = false; switch (type) { case NRecursedType::kWildcardOnlyRecursed: recursed = DoesNameContainWildcard(name); break; case NRecursedType::kRecursed: recursed = true; break; } censor.AddPreItem(include, name, recursed, wildcardMatching); } static void AddRenamePair(CObjectVector *renamePairs, const UString &oldName, const UString &newName, NRecursedType::EEnum type, bool wildcardMatching) { CRenamePair &pair = renamePairs->AddNew(); pair.OldName = oldName; pair.NewName = newName; pair.RecursedType = type; pair.WildcardParsing = wildcardMatching; if (!pair.Prepare()) { UString val; val += pair.OldName; AddNewLine(val); val += pair.NewName; AddNewLine(val); if (type == NRecursedType::kRecursed) val += L"-r"; else if (type == NRecursedType::kRecursed) val += L"-r0"; throw CArcCmdLineException("Unsupported rename command:", val); } } static void AddToCensorFromListFile( CObjectVector *renamePairs, NWildcard::CCensor &censor, LPCWSTR fileName, bool include, NRecursedType::EEnum type, bool wildcardMatching, Int32 codePage) { UStringVector names; if (!NFind::DoesFileExist(us2fs(fileName))) throw CArcCmdLineException(kCannotFindListFile, fileName); if (!ReadNamesFromListFile(us2fs(fileName), names, codePage)) throw CArcCmdLineException(kIncorrectListFile, fileName); if (renamePairs) { if ((names.Size() & 1) != 0) throw CArcCmdLineException(kIncorrectListFile, fileName); for (unsigned i = 0; i < names.Size(); i += 2) { // change type !!!! AddRenamePair(renamePairs, names[i], names[i + 1], type, wildcardMatching); } } else FOR_VECTOR (i, names) AddNameToCensor(censor, names[i], include, type, wildcardMatching); } static void AddToCensorFromNonSwitchesStrings( CObjectVector *renamePairs, unsigned startIndex, NWildcard::CCensor &censor, const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, bool wildcardMatching, bool thereAreSwitchIncludes, Int32 codePage) { if ((renamePairs || nonSwitchStrings.Size() == startIndex) && !thereAreSwitchIncludes) AddNameToCensor(censor, kUniversalWildcard, true, type, true // wildcardMatching ); int oldIndex = -1; for (unsigned i = startIndex; i < nonSwitchStrings.Size(); i++) { const UString &s = nonSwitchStrings[i]; if (s.IsEmpty()) throw CArcCmdLineException(kEmptyFilePath); if (s[0] == kFileListID) AddToCensorFromListFile(renamePairs, censor, s.Ptr(1), true, type, wildcardMatching, codePage); else if (renamePairs) { if (oldIndex == -1) oldIndex = startIndex; else { // NRecursedType::EEnum type is used for global wildcard (-i! switches) AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, NRecursedType::kNonRecursed, wildcardMatching); // AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, type); oldIndex = -1; } } else AddNameToCensor(censor, s, true, type, wildcardMatching); } if (oldIndex != -1) { throw CArcCmdLineException("There is no second file name for rename pair:", nonSwitchStrings[oldIndex]); } } #ifdef _WIN32 struct CEventSetEnd { UString Name; CEventSetEnd(const wchar_t *name): Name(name) {} ~CEventSetEnd() { NSynchronization::CManualResetEvent event; if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(Name)) == 0) event.Set(); } }; const char *k_IncorrectMapCommand = "Incorrect Map command"; static const char *ParseMapWithPaths( NWildcard::CCensor &censor, const UString &s2, bool include, NRecursedType::EEnum commonRecursedType, bool wildcardMatching) { UString s = s2; int pos = s.Find(L':'); if (pos < 0) return k_IncorrectMapCommand; int pos2 = s.Find(L':', pos + 1); if (pos2 < 0) return k_IncorrectMapCommand; CEventSetEnd eventSetEnd((const wchar_t *)s + (pos2 + 1)); s.DeleteFrom(pos2); UInt32 size; if (!StringToUInt32(s.Ptr(pos + 1), size) || size < sizeof(wchar_t) || size > ((UInt32)1 << 31) || size % sizeof(wchar_t) != 0) return "Unsupported Map data size"; s.DeleteFrom(pos); CFileMapping map; if (map.Open(FILE_MAP_READ, GetSystemString(s)) != 0) return "Can not open mapping"; LPVOID data = map.Map(FILE_MAP_READ, 0, size); if (!data) return "MapViewOfFile error"; CFileUnmapper unmapper(data); UString name; const wchar_t *p = (const wchar_t *)data; if (*p != 0) // data format marker return "Unsupported Map data"; UInt32 numChars = size / sizeof(wchar_t); for (UInt32 i = 1; i < numChars; i++) { wchar_t c = p[i]; if (c == 0) { // MessageBoxW(0, name, L"7-Zip", 0); AddNameToCensor(censor, name, include, commonRecursedType, wildcardMatching); name.Empty(); } else name += c; } if (!name.IsEmpty()) return "Map data error"; return NULL; } #endif static void AddSwitchWildcardsToCensor( NWildcard::CCensor &censor, const UStringVector &strings, bool include, NRecursedType::EEnum commonRecursedType, bool wildcardMatching, Int32 codePage) { const char *errorMessage = NULL; unsigned i; for (i = 0; i < strings.Size(); i++) { const UString &name = strings[i]; NRecursedType::EEnum recursedType; unsigned pos = 0; if (name.Len() < kSomeCludePostStringMinSize) { errorMessage = "Too short switch"; break; } if (::MyCharLower_Ascii(name[pos]) == kRecursedIDChar) { pos++; wchar_t c = name[pos]; int index = -1; if (c <= 0x7F) index = FindCharPosInString(kRecursedPostCharSet, (char)c); recursedType = GetRecursedTypeFromIndex(index); if (index >= 0) pos++; } else recursedType = commonRecursedType; if (name.Len() < pos + kSomeCludeAfterRecursedPostStringMinSize) { errorMessage = "Too short switch"; break; } UString tail = name.Ptr(pos + 1); if (name[pos] == kImmediateNameID) AddNameToCensor(censor, tail, include, recursedType, wildcardMatching); else if (name[pos] == kFileListID) AddToCensorFromListFile(NULL, censor, tail, include, recursedType, wildcardMatching, codePage); #ifdef _WIN32 else if (name[pos] == kMapNameID) { errorMessage = ParseMapWithPaths(censor, tail, include, recursedType, wildcardMatching); if (errorMessage) break; } #endif else { errorMessage = "Incorrect wildcarc type marker"; break; } } if (i != strings.Size()) throw CArcCmdLineException(errorMessage, strings[i]); } #ifdef _WIN32 // This code converts all short file names to long file names. static void ConvertToLongName(const UString &prefix, UString &name) { if (name.IsEmpty() || DoesNameContainWildcard(name)) return; NFind::CFileInfo fi; const FString path = us2fs(prefix + name); if (NFile::NName::IsDevicePath(path)) return; if (fi.Find(path)) name = fs2us(fi.Name); } static void ConvertToLongNames(const UString &prefix, CObjectVector &items) { FOR_VECTOR (i, items) { NWildcard::CItem &item = items[i]; if (item.Recursive || item.PathParts.Size() != 1) continue; if (prefix.IsEmpty() && item.IsDriveItem()) continue; ConvertToLongName(prefix, item.PathParts.Front()); } } static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) { ConvertToLongNames(prefix, node.IncludeItems); ConvertToLongNames(prefix, node.ExcludeItems); unsigned i; for (i = 0; i < node.SubNodes.Size(); i++) { UString &name = node.SubNodes[i].Name; if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name)) continue; ConvertToLongName(prefix, name); } // mix folders with same name for (i = 0; i < node.SubNodes.Size(); i++) { NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; for (unsigned j = i + 1; j < node.SubNodes.Size();) { const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; if (nextNode1.Name.IsEqualToNoCase(nextNode2.Name)) { nextNode1.IncludeItems += nextNode2.IncludeItems; nextNode1.ExcludeItems += nextNode2.ExcludeItems; node.SubNodes.Delete(j); } else j++; } } for (i = 0; i < node.SubNodes.Size(); i++) { NWildcard::CCensorNode &nextNode = node.SubNodes[i]; ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode); } } void ConvertToLongNames(NWildcard::CCensor &censor) { FOR_VECTOR (i, censor.Pairs) { NWildcard::CPair &pair = censor.Pairs[i]; ConvertToLongNames(pair.Prefix, pair.Head); } } #endif /* static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i) { switch (i) { case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore; case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy; case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress; case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti; } throw 98111603; } */ static const wchar_t *kUpdatePairStateIDSet = L"pqrxyzw"; static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; static const unsigned kNumUpdatePairActions = 4; static const char *kUpdateIgnoreItselfPostStringID = "-"; static const wchar_t kUpdateNewArchivePostCharID = '!'; static bool ParseUpdateCommandString2(const UString &command, NUpdateArchive::CActionSet &actionSet, UString &postString) { for (unsigned i = 0; i < command.Len();) { wchar_t c = MyCharLower_Ascii(command[i]); int statePos = FindCharPosInString(kUpdatePairStateIDSet, c); if (statePos < 0) { postString = command.Ptr(i); return true; } i++; if (i >= command.Len()) return false; c = command[i]; if (c < '0' || c >= '0' + kNumUpdatePairActions) return false; int actionPos = c - '0'; actionSet.StateActions[statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos); if (kUpdatePairStateNotSupportedActions[statePos] == actionPos) return false; i++; } postString.Empty(); return true; } static void ParseUpdateCommandString(CUpdateOptions &options, const UStringVector &updatePostStrings, const NUpdateArchive::CActionSet &defaultActionSet) { const char *errorMessage = "incorrect update switch command"; unsigned i; for (i = 0; i < updatePostStrings.Size(); i++) { const UString &updateString = updatePostStrings[i]; if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID)) { if (options.UpdateArchiveItself) { options.UpdateArchiveItself = false; options.Commands.Delete(0); } } else { NUpdateArchive::CActionSet actionSet = defaultActionSet; UString postString; if (!ParseUpdateCommandString2(updateString, actionSet, postString)) break; if (postString.IsEmpty()) { if (options.UpdateArchiveItself) options.Commands[0].ActionSet = actionSet; } else { if (postString[0] != kUpdateNewArchivePostCharID) break; CUpdateArchiveCommand uc; UString archivePath = postString.Ptr(1); if (archivePath.IsEmpty()) break; uc.UserArchivePath = archivePath; uc.ActionSet = actionSet; options.Commands.Add(uc); } } } if (i != updatePostStrings.Size()) throw CArcCmdLineException(errorMessage, updatePostStrings[i]); } bool ParseComplexSize(const wchar_t *s, UInt64 &result); static void SetAddCommandOptions( NCommandType::EEnum commandType, const CParser &parser, CUpdateOptions &options) { NUpdateArchive::CActionSet defaultActionSet; switch (commandType) { case NCommandType::kAdd: defaultActionSet = NUpdateArchive::k_ActionSet_Add; break; case NCommandType::kDelete: defaultActionSet = NUpdateArchive::k_ActionSet_Delete; break; default: defaultActionSet = NUpdateArchive::k_ActionSet_Update; } options.UpdateArchiveItself = true; options.Commands.Clear(); CUpdateArchiveCommand updateMainCommand; updateMainCommand.ActionSet = defaultActionSet; options.Commands.Add(updateMainCommand); if (parser[NKey::kUpdate].ThereIs) ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings, defaultActionSet); if (parser[NKey::kWorkingDir].ThereIs) { const UString &postString = parser[NKey::kWorkingDir].PostStrings[0]; if (postString.IsEmpty()) NDir::MyGetTempPath(options.WorkingDir); else options.WorkingDir = us2fs(postString); } options.SfxMode = parser[NKey::kSfx].ThereIs; if (options.SfxMode) options.SfxModule = us2fs(parser[NKey::kSfx].PostStrings[0]); if (parser[NKey::kVolume].ThereIs) { const UStringVector &sv = parser[NKey::kVolume].PostStrings; FOR_VECTOR (i, sv) { UInt64 size; if (!ParseComplexSize(sv[i], size) || size == 0) throw CArcCmdLineException("Incorrect volume size:", sv[i]); options.VolumesSizes.Add(size); } } } static void SetMethodOptions(const CParser &parser, CObjectVector &properties) { if (parser[NKey::kProperty].ThereIs) { FOR_VECTOR (i, parser[NKey::kProperty].PostStrings) { CProperty prop; prop.Name = parser[NKey::kProperty].PostStrings[i]; int index = prop.Name.Find(L'='); if (index >= 0) { prop.Value = prop.Name.Ptr(index + 1); prop.Name.DeleteFrom(index); } properties.Add(prop); } } } CArcCmdLineParser::CArcCmdLineParser(): parser(ARRAY_SIZE(kSwitchForms)) {} void CArcCmdLineParser::Parse1(const UStringVector &commandStrings, CArcCmdLineOptions &options) { if (!parser.ParseStrings(kSwitchForms, commandStrings)) throw CArcCmdLineException(parser.ErrorMessage, parser.ErrorLine); options.IsInTerminal = MY_IS_TERMINAL(stdin); options.IsStdOutTerminal = MY_IS_TERMINAL(stdout); options.IsStdErrTerminal = MY_IS_TERMINAL(stderr); options.StdInMode = parser[NKey::kStdIn].ThereIs; options.StdOutMode = parser[NKey::kStdOut].ThereIs; options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs; options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs || parser[NKey::kHelp3].ThereIs; if (parser[NKey::kCaseSensitive].ThereIs) { g_CaseSensitive = !parser[NKey::kCaseSensitive].WithMinus; options.CaseSensitiveChange = true; options.CaseSensitive = g_CaseSensitive; } #ifdef _7ZIP_LARGE_PAGES options.LargePages = false; if (parser[NKey::kLargePages].ThereIs) { options.LargePages = !parser[NKey::kLargePages].WithMinus; } #endif } struct CCodePagePair { const char *Name; Int32 CodePage; }; static const unsigned kNumByteOnlyCodePages = 3; static CCodePagePair g_CodePagePairs[] = { { "utf-8", CP_UTF8 }, { "win", CP_ACP }, { "dos", CP_OEMCP }, { "utf-16le", MY__CP_UTF16 }, { "utf-16be", MY__CP_UTF16BE } }; static Int32 FindCharset(const NCommandLineParser::CParser &parser, int keyIndex, bool byteOnlyCodePages, Int32 defaultVal) { if (!parser[keyIndex].ThereIs) return defaultVal; UString name = parser[keyIndex].PostStrings.Back(); UInt32 v; if (StringToUInt32(name, v)) if (v < ((UInt32)1 << 16)) return (Int32)v; name.MakeLower_Ascii(); unsigned num = byteOnlyCodePages ? kNumByteOnlyCodePages : ARRAY_SIZE(g_CodePagePairs); for (unsigned i = 0;; i++) { if (i == num) // to disable warnings from different compilers throw CArcCmdLineException("Unsupported charset:", name); const CCodePagePair &pair = g_CodePagePairs[i]; if (name.IsEqualTo(pair.Name)) return pair.CodePage; } } void EnumerateDirItemsAndSort( bool storeAltStreams, NWildcard::CCensor &censor, NWildcard::ECensorPathMode censorPathMode, const UString &addPathPrefix, UStringVector &sortedPaths, UStringVector &sortedFullPaths) { UStringVector paths; { CDirItems dirItems; { dirItems.ScanAltStreams = storeAltStreams; HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems, NULL); if (res != S_OK || dirItems.ErrorPaths.Size() > 0) { UString errorPath; if (dirItems.ErrorPaths.Size() > 0) errorPath = fs2us(dirItems.ErrorPaths[0]); throw CArcCmdLineException(kCannotFindArchive, dirItems.ErrorPaths.Size() > 0 ? (const wchar_t *)errorPath : NULL); } } FOR_VECTOR (i, dirItems.Items) { const CDirItem &dirItem = dirItems.Items[i]; if (!dirItem.IsDir()) paths.Add(dirItems.GetPhyPath(i)); } } if (paths.Size() == 0) throw CArcCmdLineException(kCannotFindArchive); UStringVector fullPaths; unsigned i; for (i = 0; i < paths.Size(); i++) { FString fullPath; NFile::NDir::MyGetFullPathName(us2fs(paths[i]), fullPath); fullPaths.Add(fs2us(fullPath)); } CUIntVector indices; SortFileNames(fullPaths, indices); sortedPaths.ClearAndReserve(indices.Size()); sortedFullPaths.ClearAndReserve(indices.Size()); for (i = 0; i < indices.Size(); i++) { unsigned index = indices[i]; sortedPaths.AddInReserved(paths[index]); sortedFullPaths.AddInReserved(fullPaths[index]); if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0) throw CArcCmdLineException("Duplicate archive path:", sortedFullPaths[i]); } } static void SetBoolPair(NCommandLineParser::CParser &parser, unsigned switchID, CBoolPair &bp) { bp.Def = parser[switchID].ThereIs; if (bp.Def) bp.Val = !parser[switchID].WithMinus; } void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options) { const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; int numNonSwitchStrings = nonSwitchStrings.Size(); if (numNonSwitchStrings < kMinNonSwitchWords) throw CArcCmdLineException("The command must be spcified"); if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command)) throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]); options.TechMode = parser[NKey::kTechMode].ThereIs; if (parser[NKey::kHash].ThereIs) options.HashMethods = parser[NKey::kHash].PostStrings; if (parser[NKey::kElimDup].ThereIs) { options.ExtractOptions.ElimDup.Def = true; options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus; } NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath; bool fullPathMode = parser[NKey::kFullPathMode].ThereIs; if (fullPathMode) { censorPathMode = NWildcard::k_AbsPath; const UString &s = parser[NKey::kFullPathMode].PostStrings[0]; if (!s.IsEmpty()) { if (s == L"2") censorPathMode = NWildcard::k_FullPath; else throw CArcCmdLineException("Unsupported -spf:", s); } } NRecursedType::EEnum recursedType; if (parser[NKey::kRecursed].ThereIs) recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex); else recursedType = NRecursedType::kNonRecursed; bool wildcardMatching = true; if (parser[NKey::kDisableWildcardParsing].ThereIs) wildcardMatching = false; Int32 codePage = CP_ACP; bool thereAreSwitchIncludes = false; if (parser[NKey::kInclude].ThereIs) { thereAreSwitchIncludes = true; AddSwitchWildcardsToCensor(options.Censor, parser[NKey::kInclude].PostStrings, true, recursedType, wildcardMatching, codePage); } if (parser[NKey::kExclude].ThereIs) AddSwitchWildcardsToCensor(options.Censor, parser[NKey::kExclude].PostStrings, false, recursedType, wildcardMatching, codePage); int curCommandIndex = kCommandIndex + 1; bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && options.Command.CommandType != NCommandType::kBenchmark && options.Command.CommandType != NCommandType::kInfo && options.Command.CommandType != NCommandType::kHash; bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList; bool isRename = options.Command.CommandType == NCommandType::kRename; if ((isExtractOrList || isRename) && options.StdInMode) thereIsArchiveName = false; if (parser[NKey::kArcNameMode].ThereIs) options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex); if (thereIsArchiveName) { if (curCommandIndex >= numNonSwitchStrings) throw CArcCmdLineException("Cannot find archive name"); options.ArchiveName = nonSwitchStrings[curCommandIndex++]; if (options.ArchiveName.IsEmpty()) throw CArcCmdLineException("Archive name cannot by empty"); } AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL, curCommandIndex, options.Censor, nonSwitchStrings, recursedType, wildcardMatching, thereAreSwitchIncludes, codePage); options.YesToAll = parser[NKey::kYes].ThereIs; #ifndef _NO_CRYPTO options.PasswordEnabled = parser[NKey::kPassword].ThereIs; if (options.PasswordEnabled) options.Password = parser[NKey::kPassword].PostStrings[0]; #endif options.ShowDialog = parser[NKey::kShowDialog].ThereIs; if (parser[NKey::kArchiveType].ThereIs) options.ArcType = parser[NKey::kArchiveType].PostStrings[0]; options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings; SetMethodOptions(parser, options.Properties); options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs; if (options.EnablePercents) { if ((options.StdOutMode && !options.IsStdErrTerminal) || (!options.StdOutMode && !options.IsStdOutTerminal)) options.EnablePercents = false; } if (parser[NKey::kNtSecurity].ThereIs) options.NtSecurity.SetTrueTrue(); SetBoolPair(parser, NKey::kAltStreams, options.AltStreams); SetBoolPair(parser, NKey::kHardLinks, options.HardLinks); SetBoolPair(parser, NKey::kSymLinks, options.SymLinks); if (isExtractOrList) { CExtractOptionsBase &eo = options.ExtractOptions; { CExtractNtOptions &nt = eo.NtOptions; nt.NtSecurity = options.NtSecurity; nt.AltStreams = options.AltStreams; if (!options.AltStreams.Def) nt.AltStreams.Val = true; nt.HardLinks = options.HardLinks; if (!options.HardLinks.Def) nt.HardLinks.Val = true; nt.SymLinks = options.SymLinks; if (!options.SymLinks.Def) nt.SymLinks.Val = true; nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs; nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs; } options.Censor.AddPathsToCensor(NWildcard::k_AbsPath); options.Censor.ExtendExclude(); // are there paths that look as non-relative (!Prefix.IsEmpty()) if (!options.Censor.AllAreRelative()) throw CArcCmdLineException("Cannot use absolute pathnames for this command"); NWildcard::CCensor arcCensor; if (parser[NKey::kArInclude].ThereIs) AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, wildcardMatching, codePage); if (parser[NKey::kArExclude].ThereIs) AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, wildcardMatching, codePage); if (thereIsArchiveName) AddNameToCensor(arcCensor, options.ArchiveName, true, NRecursedType::kNonRecursed, wildcardMatching); arcCensor.AddPathsToCensor(NWildcard::k_RelatPath); #ifdef _WIN32 ConvertToLongNames(arcCensor); #endif arcCensor.ExtendExclude(); if (options.StdInMode) { UString arcName = parser[NKey::kStdIn].PostStrings.Front(); options.ArchivePathsSorted.Add(arcName); options.ArchivePathsFullSorted.Add(arcName); } else { EnumerateDirItemsAndSort( false, // scanAltStreams arcCensor, NWildcard::k_RelatPath, UString(), // addPathPrefix options.ArchivePathsSorted, options.ArchivePathsFullSorted); } if (isExtractGroupCommand) { if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal) throw CArcCmdLineException(kSameTerminalError); if (parser[NKey::kOutputDir].ThereIs) { eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]); NFile::NName::NormalizeDirPathPrefix(eo.OutputDir); } eo.OverwriteMode = NExtract::NOverwriteMode::kAsk; if (parser[NKey::kOverwrite].ThereIs) { eo.OverwriteMode = k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex]; eo.OverwriteMode_Force = true; } else if (options.YesToAll) { eo.OverwriteMode = NExtract::NOverwriteMode::kOverwrite; eo.OverwriteMode_Force = true; } } eo.PathMode = options.Command.GetPathMode(); if (censorPathMode == NWildcard::k_AbsPath) { eo.PathMode = NExtract::NPathMode::kAbsPaths; eo.PathMode_Force = true; } else if (censorPathMode == NWildcard::k_FullPath) { eo.PathMode = NExtract::NPathMode::kFullPaths; eo.PathMode_Force = true; } } else if (options.Command.IsFromUpdateGroup()) { if (parser[NKey::kArInclude].ThereIs) throw CArcCmdLineException("-ai switch is not supported for this command"); CUpdateOptions &updateOptions = options.UpdateOptions; SetAddCommandOptions(options.Command.CommandType, parser, updateOptions); updateOptions.MethodMode.Properties = options.Properties; #ifdef _WIN32 if (parser[NKey::kShareForWrite].ThereIs) updateOptions.OpenShareForWrite = true; #endif updateOptions.PathMode = censorPathMode; updateOptions.AltStreams = options.AltStreams; updateOptions.NtSecurity = options.NtSecurity; updateOptions.HardLinks = options.HardLinks; updateOptions.SymLinks = options.SymLinks; updateOptions.EMailMode = parser[NKey::kEmail].ThereIs; if (updateOptions.EMailMode) { updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front(); if (updateOptions.EMailAddress.Len() > 0) if (updateOptions.EMailAddress[0] == L'.') { updateOptions.EMailRemoveAfter = true; updateOptions.EMailAddress.Delete(0); } } updateOptions.StdOutMode = options.StdOutMode; updateOptions.StdInMode = options.StdInMode; updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs; updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs; if (updateOptions.StdOutMode && updateOptions.EMailMode) throw CArcCmdLineException("stdout mode and email mode cannot be combined"); if (updateOptions.StdOutMode && options.IsStdOutTerminal) throw CArcCmdLineException(kTerminalOutError); if (updateOptions.StdInMode) updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front(); if (options.Command.CommandType == NCommandType::kRename) if (updateOptions.Commands.Size() != 1) throw CArcCmdLineException("Only one archive can be created with rename command"); } else if (options.Command.CommandType == NCommandType::kBenchmark) { options.NumIterations = 1; if (curCommandIndex < numNonSwitchStrings) { if (!StringToUInt32(nonSwitchStrings[curCommandIndex], options.NumIterations)) throw CArcCmdLineException("Incorrect Number of benmchmark iterations", nonSwitchStrings[curCommandIndex]); curCommandIndex++; } } else if (options.Command.CommandType == NCommandType::kHash) { options.Censor.AddPathsToCensor(censorPathMode); options.Censor.ExtendExclude(); CHashOptions &hashOptions = options.HashOptions; hashOptions.PathMode = censorPathMode; hashOptions.Methods = options.HashMethods; #ifdef _WIN32 if (parser[NKey::kShareForWrite].ThereIs) hashOptions.OpenShareForWrite = true; #endif hashOptions.StdInMode = options.StdInMode; hashOptions.AltStreamsMode = options.AltStreams.Val; } else if (options.Command.CommandType == NCommandType::kInfo) { } else throw 815676711; // FIXME 9815676711; } src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.h000066400000000000000000000046541325366651500234200ustar00rootroot00000000000000// ArchiveCommandLine.h #ifndef __ARCHIVE_COMMAND_LINE_H #define __ARCHIVE_COMMAND_LINE_H #include "../../../Common/CommandLineParser.h" #include "../../../Common/Wildcard.h" #include "Extract.h" #include "HashCalc.h" #include "Update.h" struct CArcCmdLineException: public UString { CArcCmdLineException(const char *a, const wchar_t *u = NULL); }; namespace NCommandType { enum EEnum { kAdd = 0, kUpdate, kDelete, kTest, kExtract, kExtractFull, kList, kBenchmark, kInfo, kHash, kRename };} struct CArcCommand { NCommandType::EEnum CommandType; bool IsFromExtractGroup() const; bool IsFromUpdateGroup() const; bool IsTestCommand() const { return CommandType == NCommandType::kTest; } NExtract::NPathMode::EEnum GetPathMode() const; }; struct CArcCmdLineOptions { bool HelpMode; #ifdef _7ZIP_LARGE_PAGES bool LargePages; #endif bool CaseSensitiveChange; bool CaseSensitive; bool IsInTerminal; bool IsStdOutTerminal; bool IsStdErrTerminal; bool StdInMode; bool StdOutMode; bool EnableHeaders; bool YesToAll; bool ShowDialog; NWildcard::CCensor Censor; CArcCommand Command; UString ArchiveName; #ifndef _NO_CRYPTO bool PasswordEnabled; UString Password; #endif bool TechMode; UStringVector HashMethods; bool AppendName; UStringVector ArchivePathsSorted; UStringVector ArchivePathsFullSorted; CObjectVector Properties; CExtractOptionsBase ExtractOptions; CBoolPair NtSecurity; CBoolPair AltStreams; CBoolPair HardLinks; CBoolPair SymLinks; CUpdateOptions UpdateOptions; CHashOptions HashOptions; UString ArcType; UStringVector ExcludedArcTypes; bool EnablePercents; // Benchmark UInt32 NumIterations; CArcCmdLineOptions(): StdInMode(false), StdOutMode(false), CaseSensitiveChange(false), CaseSensitive(false) {}; }; class CArcCmdLineParser { NCommandLineParser::CParser parser; public: CArcCmdLineParser(); void Parse1(const UStringVector &commandStrings, CArcCmdLineOptions &options); void Parse2(CArcCmdLineOptions &options); }; void EnumerateDirItemsAndSort( bool storeAltStreams, NWildcard::CCensor &censor, NWildcard::ECensorPathMode pathMode, const UString &addPathPrefix, UStringVector &sortedPaths, UStringVector &sortedFullPaths); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp000066400000000000000000000751461325366651500246200ustar00rootroot00000000000000// ArchiveExtractCallback.cpp #include "StdAfx.h" #undef sprintf #undef printf #include "../../../Common/ComTry.h" #include "../../../Common/StringConvert.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif #include "../../Common/FilePathAutoRename.h" #include "../Common/ExtractingFilePath.h" #include "../Common/PropIDUtils.h" #include "ArchiveExtractCallback.h" using namespace NWindows; using namespace NFile; using namespace NDir; static const char *kCantAutoRename = "Can not create file with auto name"; static const char *kCantRenameFile = "Can not rename existing file"; static const char *kCantDeleteOutputFile = "Can not delete output file"; static const char *kCantDeleteOutputDir = "Can not delete output folder"; #ifndef _SFX STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize) { HRESULT result = S_OK; if (_stream) result = _stream->Write(data, size, &size); if (_calculate) _hash->Update(data, size); _size += size; if (processedSize) *processedSize = size; return result; } #endif #ifdef _USE_SECURITY_CODE bool InitLocalPrivileges() { NSecurity::CAccessToken token; if (!token.OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)) return false; TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid)) return false; if (!token.AdjustPrivileges(&tp)) return false; return (GetLastError() == ERROR_SUCCESS); } #endif #ifdef SUPPORT_LINKS int CHardLinkNode::Compare(const CHardLinkNode &a) const { if (StreamId < a.StreamId) return -1; if (StreamId > a.StreamId) return 1; return MyCompare(INode, a.INode); } HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined) { h.INode = 0; h.StreamId = (UInt64)(Int64)-1; defined = false; { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidINode, &prop)); if (!ConvertPropVariantToUInt64(prop, h.INode)) return S_OK; } { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidStreamId, &prop)); ConvertPropVariantToUInt64(prop, h.StreamId); } defined = true; return S_OK; } HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector *realIndices) { _hardLinks.Clear(); if (!_arc->Ask_INode) return S_OK; IInArchive *archive = _arc->Archive; CRecordVector &hardIDs = _hardLinks.IDs; { UInt32 numItems; if (realIndices) numItems = realIndices->Size(); else { RINOK(archive->GetNumberOfItems(&numItems)); } for (UInt32 i = 0; i < numItems; i++) { CHardLinkNode h; bool defined; RINOK(Archive_Get_HardLinkNode(archive, realIndices ? (*realIndices)[i] : i, h, defined)); if (defined) hardIDs.Add(h); } } hardIDs.Sort2(); { // wee keep only items that have 2 or more items unsigned k = 0; unsigned numSame = 1; for (unsigned i = 1; i < hardIDs.Size(); i++) { if (hardIDs[i].Compare(hardIDs[i - 1]) != 0) numSame = 1; else if (++numSame == 2) { if (i - 1 != k) hardIDs[k] = hardIDs[i - 1]; k++; } } hardIDs.DeleteFrom(k); } _hardLinks.PrepareLinks(); return S_OK; } #endif CArchiveExtractCallback::CArchiveExtractCallback(): WriteCTime(true), WriteATime(true), WriteMTime(true), _multiArchives(false) { LocalProgressSpec = new CLocalProgress(); _localProgress = LocalProgressSpec; #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } void CArchiveExtractCallback::Init( const CExtractNtOptions &ntOptions, const NWildcard::CCensorNode *wildcardCensor, const CArc *arc, IFolderArchiveExtractCallback *extractCallback2, bool stdOutMode, bool testMode, const FString &directoryPath, const UStringVector &removePathParts, UInt64 packSize) { _extractedFolderPaths.Clear(); _extractedFolderIndices.Clear(); #ifdef SUPPORT_LINKS _hardLinks.Clear(); #endif _ntOptions = ntOptions; _wildcardCensor = wildcardCensor; _stdOutMode = stdOutMode; _testMode = testMode; _unpTotal = 1; _packTotal = packSize; _extractCallback2 = extractCallback2; _compressProgress.Release(); _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); #ifndef _SFX _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback); if (ExtractToStreamCallback) { Int32 useStreams = 0; if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK) useStreams = 0; if (useStreams == 0) ExtractToStreamCallback.Release(); } #endif LocalProgressSpec->Init(extractCallback2, true); LocalProgressSpec->SendProgress = false; _removePathParts = removePathParts; _baseParentFolder = (UInt32)(Int32)-1; _use_baseParentFolder_mode = false; _arc = arc; _directoryPath = directoryPath; NName::NormalizeDirPathPrefix(_directoryPath); NDir::MyGetFullPathName(directoryPath, _directoryPathFull); } STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) { COM_TRY_BEGIN _unpTotal = size; if (!_multiArchives && _extractCallback2) return _extractCallback2->SetTotal(size); return S_OK; COM_TRY_END } static void NormalizeVals(UInt64 &v1, UInt64 &v2) { const UInt64 kMax = (UInt64)1 << 31; while (v1 > kMax) { v1 >>= 1; v2 >>= 1; } } static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) { NormalizeVals(packTotal, unpTotal); NormalizeVals(unpCur, unpTotal); if (unpTotal == 0) unpTotal = 1; return unpCur * packTotal / unpTotal; } STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) { COM_TRY_BEGIN if (!_extractCallback2) return S_OK; if (_multiArchives) { if (completeValue != NULL) { UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); return _extractCallback2->SetCompleted(&packCur); } } return _extractCallback2->SetCompleted(completeValue); COM_TRY_END } STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { COM_TRY_BEGIN return _localProgress->SetRatioInfo(inSize, outSize); COM_TRY_END } #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z') static inline bool IsDriveName(const UString &s) { return s.Len() == 2 && s[1] == ':' && IS_LETTER_CHAR(s[0]); } void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) { bool isAbsPath = false; if (!dirPathParts.IsEmpty()) { const UString &s = dirPathParts[0]; if (s.IsEmpty()) isAbsPath = true; #ifdef _WIN32 else { if (dirPathParts.Size() > 1 && IsDriveName(s)) isAbsPath = true; } #endif } if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath) fullPath.Empty(); else fullPath = _directoryPath; FOR_VECTOR (i, dirPathParts) { if (i > 0) fullPath += FCHAR_PATH_SEPARATOR; const UString &s = dirPathParts[i]; fullPath += us2fs(s); #ifdef _WIN32 if (_pathMode == NExtract::NPathMode::kAbsPaths) if (i == 0 && IsDriveName(s)) continue; #endif CreateDir(fullPath); } } HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined) { filetimeIsDefined = false; NCOM::CPropVariant prop; RINOK(_arc->Archive->GetProperty(index, propID, &prop)); if (prop.vt == VT_FILETIME) { filetime = prop.filetime; filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0); } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } HRESULT CArchiveExtractCallback::GetUnpackSize() { return _arc->GetItemSize(_index, _curSize, _curSizeDefined); } HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) { return _extractCallback2->MessageError( UString(L"ERROR: ") + GetUnicodeString(message) + L": " + fs2us(path)); } HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2) { return _extractCallback2->MessageError( UString(L"ERROR: ") + GetUnicodeString(message) + UString(L": ") + fs2us(path1) + UString(L" : ") + fs2us(path2)); } #ifndef _SFX STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value) { if (propID == kpidName) { COM_TRY_BEGIN NCOM::CPropVariant prop = Name.Ptr(); prop.Detach(value); return S_OK; COM_TRY_END } return Arc->Archive->GetProperty(IndexInArc, propID, value); } #endif #ifdef SUPPORT_LINKS static UString GetDirPrefixOf(const UString &src) { UString s = src; if (!s.IsEmpty()) { if (s.Back() == WCHAR_PATH_SEPARATOR) s.DeleteBack(); int pos = s.ReverseFind(WCHAR_PATH_SEPARATOR); s.DeleteFrom(pos + 1); } return s; } static bool IsSafePath(const UString &path) { UStringVector parts; SplitPathToParts(path, parts); int level = 0; FOR_VECTOR(i, parts) { const UString &s = parts[i]; if (s.IsEmpty()) continue; if (s == L".") continue; if (s == L"..") { if (level <= 0) return false; level--; } else level++; } return level > 0; } #endif STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) { COM_TRY_BEGIN *outStream = 0; #ifndef _SFX if (_hashStream) _hashStreamSpec->ReleaseStream(); _hashStreamWasUsed = false; #endif _outFileStream.Release(); _encrypted = false; _isSplit = false; _isAltStream = false; _curSize = 0; _curSizeDefined = false; _index = index; UString fullPath; IInArchive *archive = _arc->Archive; RINOK(_arc->GetItemPath(index, fullPath)); RINOK(Archive_IsItem_Folder(archive, index, _fi.IsDir)); _filePath = fullPath; { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidPosition, &prop)); if (prop.vt != VT_EMPTY) { if (prop.vt != VT_UI8) return E_FAIL; _position = prop.uhVal.QuadPart; _isSplit = true; } } #ifdef SUPPORT_LINKS bool isHardLink = false; bool isJunction = false; bool isRelative = false; UString linkPath; // RINOK(Archive_GetItemBoolProp(archive, index, kpidIsHardLink, isHardLink)); // if (isHardLink) { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidHardLink, &prop)); if (prop.vt == VT_BSTR) { isHardLink = true; linkPath = prop.bstrVal; isRelative = false; // TAR: hard links are from root folder of archive } else if (prop.vt == VT_EMPTY) { // linkPath.Empty(); } else return E_FAIL; } { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidSymLink, &prop)); if (prop.vt == VT_BSTR) { isHardLink = false; linkPath = prop.bstrVal; isRelative = true; // TAR: symbolic links are relative } else if (prop.vt == VT_EMPTY) { // linkPath.Empty(); } else return E_FAIL; } bool isOkReparse = false; if (linkPath.IsEmpty() && _arc->GetRawProps) { const void *data; UInt32 dataSize; UInt32 propType; _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType); if (dataSize != 0) { if (propType != NPropDataType::kRaw) return E_FAIL; UString s; CReparseAttr reparse; isOkReparse = reparse.Parse((const Byte *)data, dataSize); if (isOkReparse) { isHardLink = false; linkPath = reparse.GetPath(); isJunction = reparse.IsMountPoint(); isRelative = reparse.IsRelative(); #ifndef _WIN32 linkPath.Replace(WCHAR_PATH_SEPARATOR, '/', ); #endif } } } if (!linkPath.IsEmpty()) { #ifdef _WIN32 linkPath.Replace('/', WCHAR_PATH_SEPARATOR); #endif for (;;) // while (NName::IsAbsolutePath(linkPath)) { unsigned n = NName::GetRootPrefixSize(linkPath); if (n == 0) break; isRelative = false; linkPath.DeleteFrontal(n); } } if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0) { UStringVector pathParts; SplitPathToParts(linkPath, pathParts); bool badPrefix = false; FOR_VECTOR (i, _removePathParts) { if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) { badPrefix = true; break; } } if (!badPrefix) pathParts.DeleteFrontal(_removePathParts.Size()); linkPath = MakePathNameFromParts(pathParts); } #endif RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted)); RINOK(GetUnpackSize()); RINOK(Archive_IsItem_AltStream(archive, index, _isAltStream)); if (!_ntOptions.AltStreams.Val && _isAltStream) return S_OK; if (_wildcardCensor) { if (!_wildcardCensor->CheckPath(_isAltStream, fullPath, !_fi.IsDir)) return S_OK; } UStringVector pathParts; if (_use_baseParentFolder_mode) { int baseParent = _baseParentFolder; if (_pathMode == NExtract::NPathMode::kFullPaths || _pathMode == NExtract::NPathMode::kAbsPaths) baseParent = -1; RINOK(_arc->GetItemPathToParent(index, baseParent, pathParts)); if (_pathMode == NExtract::NPathMode::kNoPaths && !pathParts.IsEmpty()) pathParts.DeleteFrontal(pathParts.Size() - 1); } else { SplitPathToParts(fullPath, pathParts); if (pathParts.IsEmpty()) return E_FAIL; unsigned numRemovePathParts = 0; switch (_pathMode) { case NExtract::NPathMode::kCurPaths: { bool badPrefix = false; if (pathParts.Size() <= _removePathParts.Size()) badPrefix = true; else { FOR_VECTOR (i, _removePathParts) { if (!_removePathParts[i].IsEqualToNoCase(pathParts[i])) { badPrefix = true; break; } } } if (badPrefix) { if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) return E_FAIL; } else numRemovePathParts = _removePathParts.Size(); break; } case NExtract::NPathMode::kNoPaths: { numRemovePathParts = pathParts.Size() - 1; break; } /* case NExtract::NPathMode::kFullPaths: case NExtract::NPathMode::kAbsPaths: break; */ } pathParts.DeleteFrontal(numRemovePathParts); } #ifndef _SFX if (ExtractToStreamCallback) { if (!GetProp) { GetProp_Spec = new CGetProp; GetProp = GetProp_Spec; } GetProp_Spec->Arc = _arc; GetProp_Spec->IndexInArc = index; GetProp_Spec->Name = MakePathNameFromParts(pathParts); return ExtractToStreamCallback->GetStream7(GetProp_Spec->Name, _fi.IsDir, outStream, askExtractMode, GetProp); } #endif CMyComPtr outStreamLoc; if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) { if (_stdOutMode) { outStreamLoc = new CStdOutFileStream; } else { { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidAttrib, &prop)); if (prop.vt == VT_UI4) { _fi.Attrib = prop.ulVal; _fi.AttribDefined = true; } else if (prop.vt == VT_EMPTY) _fi.AttribDefined = false; else return E_FAIL; } RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined)); RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined)); RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined)); bool isAnti = false; RINOK(_arc->IsItemAnti(index, isAnti)); bool replace = _isAltStream ? _ntOptions.ReplaceColonForAltStream : !_ntOptions.WriteToAltStreamIfColon; if (_pathMode != NExtract::NPathMode::kAbsPaths) MakeCorrectPath(_directoryPath.IsEmpty(), pathParts, replace); Correct_IfEmptyLastPart(pathParts); UString processedPath = MakePathNameFromParts(pathParts); if (!isAnti) { if (!_fi.IsDir) { if (!pathParts.IsEmpty()) pathParts.DeleteBack(); } if (!pathParts.IsEmpty()) { FString fullPathNew; CreateComplexDirectory(pathParts, fullPathNew); if (_fi.IsDir) { _extractedFolderPaths.Add(fullPathNew); _extractedFolderIndices.Add(index); SetDirTime(fullPathNew, (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); } } } FString fullProcessedPath = us2fs(processedPath); if (_pathMode != NExtract::NPathMode::kAbsPaths || !NName::IsAbsolutePath(processedPath)) fullProcessedPath = _directoryPath + fullProcessedPath; if (_fi.IsDir) { _diskFilePath = fullProcessedPath; if (isAnti) RemoveDir(_diskFilePath); #ifdef SUPPORT_LINKS if (linkPath.IsEmpty()) #endif return S_OK; } else if (!_isSplit) { NFind::CFileInfo fileInfo; if (fileInfo.Find(fullProcessedPath)) { switch (_overwriteMode) { case NExtract::NOverwriteMode::kSkip: return S_OK; case NExtract::NOverwriteMode::kAsk: { int slashPos = fullProcessedPath.ReverseFind(FTEXT('/')); #ifdef _WIN32 int slash1Pos = fullProcessedPath.ReverseFind(FTEXT('\\')); slashPos = MyMax(slashPos, slash1Pos); #endif FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name; Int32 overwiteResult; RINOK(_extractCallback2->AskOverwrite( fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath, _fi.MTimeDefined ? &_fi.MTime : NULL, _curSizeDefined ? &_curSize : NULL, &overwiteResult)) switch (overwiteResult) { case NOverwriteAnswer::kCancel: return E_ABORT; case NOverwriteAnswer::kNo: return S_OK; case NOverwriteAnswer::kNoToAll: _overwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK; case NOverwriteAnswer::kYes: break; case NOverwriteAnswer::kYesToAll: _overwriteMode = NExtract::NOverwriteMode::kOverwrite; break; case NOverwriteAnswer::kAutoRename: _overwriteMode = NExtract::NOverwriteMode::kRename; break; default: return E_FAIL; } } } if (_overwriteMode == NExtract::NOverwriteMode::kRename) { if (!AutoRenamePath(fullProcessedPath)) { RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); return E_FAIL; } } else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting) { FString existPath = fullProcessedPath; if (!AutoRenamePath(existPath)) { RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); return E_FAIL; } // MyMoveFile can raname folders. So it's OK to use it folders too if (!MyMoveFile(fullProcessedPath, existPath)) { RINOK(SendMessageError(kCantRenameFile, fullProcessedPath)); return E_FAIL; } } else { if (fileInfo.IsDir()) { // do we need to delete all files in folder? if (!RemoveDir(fullProcessedPath)) { RINOK(SendMessageError(kCantDeleteOutputDir, fullProcessedPath)); return S_OK; } } else if (!DeleteFileAlways(fullProcessedPath)) { RINOK(SendMessageError(kCantDeleteOutputFile, fullProcessedPath)); return S_OK; // return E_FAIL; } } } } _diskFilePath = fullProcessedPath; if (!isAnti) { #ifdef SUPPORT_LINKS if (!linkPath.IsEmpty()) { #ifndef UNDER_CE UString relatPath; if (isRelative) relatPath = GetDirPrefixOf(_filePath); relatPath += linkPath; if (!IsSafePath(relatPath)) { RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath))); } else { FString existPath; if (isHardLink || !isRelative) { if (!NName::GetFullPath(_directoryPathFull, us2fs(relatPath), existPath)) { RINOK(SendMessageError("Incorrect path", us2fs(relatPath))); } } else { existPath = us2fs(linkPath); } if (!existPath.IsEmpty()) { if (isHardLink) { if (!MyCreateHardLink(fullProcessedPath, existPath)) { RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath)); // return S_OK; } } else if (_ntOptions.SymLinks.Val) { // bool isSymLink = true; // = false for junction if (_fi.IsDir && !isRelative) { // if it's before Vista we use Junction Point // isJunction = true; // convertToAbs = true; } CByteBuffer data; if (FillLinkData(data, fs2us(existPath), !isJunction)) { CReparseAttr attr; if (!attr.Parse(data, data.Size())) { return E_FAIL; // "Internal conversion error"; } if (!NFile::NIO::SetReparseData(fullProcessedPath, _fi.IsDir, data, (DWORD)data.Size())) { RINOK(SendMessageError("Can not set reparse data", fullProcessedPath)); } } } } } #endif } else #endif // SUPPORT_LINKS { bool needWriteFile = true; #ifdef SUPPORT_LINKS if (!_hardLinks.IDs.IsEmpty()) { CHardLinkNode h; bool defined; RINOK(Archive_Get_HardLinkNode(archive, index, h, defined)); if (defined) { { int linkIndex = _hardLinks.IDs.FindInSorted2(h); if (linkIndex >= 0) { FString &hl = _hardLinks.Links[linkIndex]; if (hl.IsEmpty()) hl = fullProcessedPath; else { if (!MyCreateHardLink(fullProcessedPath, hl)) { RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl)); return S_OK; } needWriteFile = false; } } } } } #endif if (needWriteFile) { _outFileStreamSpec = new COutFileStream; CMyComPtr outStreamLoc(_outFileStreamSpec); if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) { // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) { RINOK(SendMessageError("Can not open output file ", fullProcessedPath)); return S_OK; } } if (_isSplit) { RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); } _outFileStream = outStreamLoc; } } } outStreamLoc = _outFileStream; } } #ifndef _SFX if (_hashStream) { if (askExtractMode == NArchive::NExtract::NAskMode::kExtract || askExtractMode == NArchive::NExtract::NAskMode::kTest) { _hashStreamSpec->SetStream(outStreamLoc); outStreamLoc = _hashStream; _hashStreamSpec->Init(true); _hashStreamWasUsed = true; } } #endif if (outStreamLoc) *outStream = outStreamLoc.Detach(); return S_OK; COM_TRY_END } STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) { COM_TRY_BEGIN #ifndef _SFX if (ExtractToStreamCallback) return ExtractToStreamCallback->PrepareOperation7(askExtractMode); #endif _extractMode = false; switch (askExtractMode) { case NArchive::NExtract::NAskMode::kExtract: if (_testMode) askExtractMode = NArchive::NExtract::NAskMode::kTest; else _extractMode = true; break; }; return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir, askExtractMode, _isSplit ? &_position: 0); COM_TRY_END } STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) { COM_TRY_BEGIN #ifndef _SFX if (ExtractToStreamCallback) return ExtractToStreamCallback->SetOperationResult7(operationResult, _encrypted); #endif #ifndef _SFX if (_hashStreamWasUsed) { _hashStreamSpec->_hash->Final(_fi.IsDir, _isAltStream, _filePath); _curSize = _hashStreamSpec->GetSize(); _curSizeDefined = true; _hashStreamSpec->ReleaseStream(); _hashStreamWasUsed = false; } #endif if (_outFileStream) { _outFileStreamSpec->SetTime( (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); _curSize = _outFileStreamSpec->ProcessedSize; _curSizeDefined = true; RINOK(_outFileStreamSpec->Close()); _outFileStream.Release(); } #ifdef _USE_SECURITY_CODE if (_ntOptions.NtSecurity.Val && _arc->GetRawProps) { const void *data; UInt32 dataSize; UInt32 propType; _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType); if (dataSize != 0) { if (propType != NPropDataType::kRaw) return E_FAIL; if (CheckNtSecure((const Byte *)data, dataSize)) { SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; if (_saclEnabled) securInfo |= SACL_SECURITY_INFORMATION; ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)data); } } } #endif if (!_curSizeDefined) GetUnpackSize(); if (_curSizeDefined) { if (_isAltStream) AltStreams_UnpackSize += _curSize; else UnpackSize += _curSize; } if (_fi.IsDir) NumFolders++; else if (_isAltStream) NumAltStreams++; else NumFiles++; if (_extractMode && _fi.AttribDefined) SetFileAttrib(_diskFilePath, _fi.Attrib); RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); return S_OK; COM_TRY_END } /* STDMETHODIMP CArchiveExtractCallback::GetInStream( const wchar_t *name, ISequentialInStream **inStream) { COM_TRY_BEGIN CInFileStream *inFile = new CInFileStream; CMyComPtr inStreamTemp = inFile; if (!inFile->Open(_srcDirectoryPrefix + name)) return ::GetLastError(); *inStream = inStreamTemp.Detach(); return S_OK; COM_TRY_END } */ STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN if (!_cryptoGetTextPassword) { RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, &_cryptoGetTextPassword)); } return _cryptoGetTextPassword->CryptoGetTextPassword(password); COM_TRY_END } struct CExtrRefSortPair { int Len; int Index; int Compare(const CExtrRefSortPair &a) const; }; #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const { RINOZ(-MyCompare(Len, a.Len)); return MyCompare(Index, a.Index); } static int GetNumSlashes(const FChar *s) { for (int numSlashes = 0;;) { FChar c = *s++; if (c == 0) return numSlashes; if ( #ifdef _WIN32 c == FTEXT('\\') || #endif c == FTEXT('/')) numSlashes++; } } HRESULT CArchiveExtractCallback::SetDirsTimes() { CRecordVector pairs; pairs.ClearAndSetSize(_extractedFolderPaths.Size()); unsigned i; for (i = 0; i < _extractedFolderPaths.Size(); i++) { CExtrRefSortPair &pair = pairs[i]; pair.Index = i; pair.Len = GetNumSlashes(_extractedFolderPaths[i]); } pairs.Sort2(); for (i = 0; i < pairs.Size(); i++) { int pairIndex = pairs[i].Index; int index = _extractedFolderIndices[pairIndex]; FILETIME CTime; FILETIME ATime; FILETIME MTime; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined)); RINOK(GetTime(index, kpidATime, ATime, ATimeDefined)); RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined)); // printf("\n%S", _extractedFolderPaths[pairIndex]); SetDirTime(_extractedFolderPaths[pairIndex], (WriteCTime && CTimeDefined) ? &CTime : NULL, (WriteATime && ATimeDefined) ? &ATime : NULL, (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); } return S_OK; } src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.h000066400000000000000000000150011325366651500242450ustar00rootroot00000000000000// ArchiveExtractCallback.h #ifndef __ARCHIVE_EXTRACT_CALLBACK_H #define __ARCHIVE_EXTRACT_CALLBACK_H #include "../../../Common/MyCom.h" #include "../../../Common/Wildcard.h" #include "../../IPassword.h" #include "../../Common/FileStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Archive/IArchive.h" #include "ExtractMode.h" #include "IFileExtractCallback.h" #include "OpenArchive.h" #include "HashCalc.h" #ifndef _SFX class COutStreamWithHash: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; bool _calculate; public: IHashCalc *_hash; MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); void SetStream(ISequentialOutStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(bool calculate = true) { InitCRC(); _size = 0; _calculate = calculate; } void EnableCalc(bool calculate) { _calculate = calculate; } void InitCRC() { _hash->InitForNewFile(); } UInt64 GetSize() const { return _size; } }; #endif struct CExtractNtOptions { CBoolPair NtSecurity; CBoolPair SymLinks; CBoolPair HardLinks; CBoolPair AltStreams; bool ReplaceColonForAltStream; bool WriteToAltStreamIfColon; CExtractNtOptions(): ReplaceColonForAltStream(false), WriteToAltStreamIfColon(false) { SymLinks.Val = true; HardLinks.Val = true; AltStreams.Val = true; } }; #ifndef _SFX class CGetProp: public IGetProp, public CMyUnknownImp { public: const CArc *Arc; UInt32 IndexInArc; UString Name; // relative path MY_UNKNOWN_IMP1(IGetProp) INTERFACE_IGetProp(;) }; #endif #ifndef _SFX #ifndef UNDER_CE // FIXME #define SUPPORT_LINKS #endif #endif #ifdef SUPPORT_LINKS struct CHardLinkNode { UInt64 StreamId; UInt64 INode; int Compare(const CHardLinkNode &a) const; }; class CHardLinks { public: CRecordVector IDs; CObjectVector Links; void Clear() { IDs.Clear(); Links.Clear(); } void PrepareLinks() { while (Links.Size() < IDs.Size()) Links.AddNew(); } }; #endif class CArchiveExtractCallback: public IArchiveExtractCallback, // public IArchiveVolumeExtractCallback, public ICryptoGetTextPassword, public ICompressProgressInfo, public CMyUnknownImp { const CArc *_arc; CExtractNtOptions _ntOptions; const NWildcard::CCensorNode *_wildcardCensor; CMyComPtr _extractCallback2; CMyComPtr _compressProgress; CMyComPtr _cryptoGetTextPassword; FString _directoryPath; FString _directoryPathFull; NExtract::NPathMode::EEnum _pathMode; NExtract::NOverwriteMode::EEnum _overwriteMode; #ifndef _SFX CMyComPtr ExtractToStreamCallback; CGetProp *GetProp_Spec; CMyComPtr GetProp; #endif FString _diskFilePath; UString _filePath; UInt64 _position; bool _isSplit; bool _isAltStream; bool _extractMode; bool WriteCTime; bool WriteATime; bool WriteMTime; bool _encrypted; struct CProcessedFileInfo { FILETIME CTime; FILETIME ATime; FILETIME MTime; UInt32 Attrib; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; bool AttribDefined; bool IsDir; } _fi; UInt32 _index; UInt64 _curSize; bool _curSizeDefined; COutFileStream *_outFileStreamSpec; CMyComPtr _outFileStream; #ifndef _SFX COutStreamWithHash *_hashStreamSpec; CMyComPtr _hashStream; bool _hashStreamWasUsed; #endif UStringVector _removePathParts; bool _use_baseParentFolder_mode; UInt32 _baseParentFolder; bool _stdOutMode; bool _testMode; bool _multiArchives; CMyComPtr _localProgress; UInt64 _packTotal; UInt64 _unpTotal; FStringVector _extractedFolderPaths; CRecordVector _extractedFolderIndices; #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX) bool _saclEnabled; #endif void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath); HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined); HRESULT GetUnpackSize(); HRESULT SendMessageError(const char *message, const FString &path); HRESULT SendMessageError2(const char *message, const FString &path1, const FString &path2); public: CLocalProgress *LocalProgressSpec; UInt64 NumFolders; UInt64 NumFiles; UInt64 NumAltStreams; UInt64 UnpackSize; UInt64 AltStreams_UnpackSize; MY_UNKNOWN_IMP2(ICryptoGetTextPassword, ICompressProgressInfo) // COM_INTERFACE_ENTRY(IArchiveVolumeExtractCallback) INTERFACE_IArchiveExtractCallback(;) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); // IArchiveVolumeExtractCallback // STDMETHOD(GetInStream)(const wchar_t *name, ISequentialInStream **inStream); STDMETHOD(CryptoGetTextPassword)(BSTR *password); CArchiveExtractCallback(); void InitForMulti(bool multiArchives, NExtract::NPathMode::EEnum pathMode, NExtract::NOverwriteMode::EEnum overwriteMode) { _multiArchives = multiArchives; _pathMode = pathMode; _overwriteMode = overwriteMode; NumFolders = NumFiles = NumAltStreams = UnpackSize = AltStreams_UnpackSize = 0; } #ifndef _SFX void SetHashMethods(IHashCalc *hash) { if (!hash) return; _hashStreamSpec = new COutStreamWithHash; _hashStream = _hashStreamSpec; _hashStreamSpec->_hash = hash; } #endif void Init( const CExtractNtOptions &ntOptions, const NWildcard::CCensorNode *wildcardCensor, const CArc *arc, IFolderArchiveExtractCallback *extractCallback2, bool stdOutMode, bool testMode, const FString &directoryPath, const UStringVector &removePathParts, UInt64 packSize); #ifdef SUPPORT_LINKS private: CHardLinks _hardLinks; public: // call PrepareHardLinks() after Init() HRESULT PrepareHardLinks(const CRecordVector *realIndices); // NULL means all items #endif // call it after Init() void SetBaseParentFolderIndex(UInt32 indexInArc) { _use_baseParentFolder_mode = true; _baseParentFolder = indexInArc; } HRESULT SetDirsTimes(); }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp000066400000000000000000000064661325366651500241060ustar00rootroot00000000000000// ArchiveOpenCallback.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../Common/FileStreams.h" #include "ArchiveOpenCallback.h" using namespace NWindows; STDMETHODIMP COpenCallbackImp::SetTotal(const UInt64 *files, const UInt64 *bytes) { COM_TRY_BEGIN if (ReOpenCallback) return ReOpenCallback->SetTotal(files, bytes); if (!Callback) return S_OK; return Callback->Open_SetTotal(files, bytes); COM_TRY_END } STDMETHODIMP COpenCallbackImp::SetCompleted(const UInt64 *files, const UInt64 *bytes) { COM_TRY_BEGIN if (ReOpenCallback) return ReOpenCallback->SetCompleted(files, bytes); if (!Callback) return S_OK; return Callback->Open_SetCompleted(files, bytes); COM_TRY_END } STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; if (_subArchiveMode) switch(propID) { case kpidName: prop = _subArchiveName; break; } else switch(propID) { case kpidName: prop = _fileInfo.Name; break; case kpidIsDir: prop = _fileInfo.IsDir(); break; case kpidSize: prop = _fileInfo.Size; break; case kpidAttrib: prop = (UInt32)_fileInfo.Attrib; break; case kpidCTime: prop = _fileInfo.CTime; break; case kpidATime: prop = _fileInfo.ATime; break; case kpidMTime: prop = _fileInfo.MTime; break; } prop.Detach(value); return S_OK; COM_TRY_END } struct CInFileStreamVol: public CInFileStream { int FileNameIndex; COpenCallbackImp *OpenCallbackImp; CMyComPtr OpenCallbackRef; CInFileStreamVol(bool ignoreLink = false) : CInFileStream(ignoreLink) { } ~CInFileStreamVol() { if (OpenCallbackRef) OpenCallbackImp->FileNames_WasUsed[FileNameIndex] = false; } }; STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream) { COM_TRY_BEGIN *inStream = NULL; if (_subArchiveMode) return S_FALSE; if (Callback) { RINOK(Callback->Open_CheckBreak()); } FString fullPath; if (!NFile::NName::GetFullPath(_folderPrefix, us2fs(name), fullPath)) return S_FALSE; if (!_fileInfo.Find(fullPath,true)) return S_FALSE; if (_fileInfo.IsDir()) return S_FALSE; CInFileStreamVol *inFile = new CInFileStreamVol(true); CMyComPtr inStreamTemp = inFile; if (!inFile->Open(fullPath)) return ::GetLastError(); FileSizes.Add(_fileInfo.Size); FileNames.Add(name); inFile->FileNameIndex = FileNames_WasUsed.Add(true); inFile->OpenCallbackImp = this; inFile->OpenCallbackRef = this; // TotalSize += _fileInfo.Size; *inStream = inStreamTemp.Detach(); return S_OK; COM_TRY_END } #ifndef _NO_CRYPTO STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN if (ReOpenCallback) { CMyComPtr getTextPassword; ReOpenCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); if (getTextPassword) return getTextPassword->CryptoGetTextPassword(password); } if (!Callback) return E_NOTIMPL; return Callback->Open_CryptoGetTextPassword(password); COM_TRY_END } #endif src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.h000066400000000000000000000052321325366651500235410ustar00rootroot00000000000000// ArchiveOpenCallback.h #ifndef __ARCHIVE_OPEN_CALLBACK_H #define __ARCHIVE_OPEN_CALLBACK_H #include "../../../Common/MyCom.h" #include "../../../Windows/FileFind.h" #ifndef _NO_CRYPTO #include "../../IPassword.h" #endif #include "../../Archive/IArchive.h" #ifdef _NO_CRYPTO #define INTERFACE_IOpenCallbackUI_Crypto(x) #else #define INTERFACE_IOpenCallbackUI_Crypto(x) \ virtual HRESULT Open_CryptoGetTextPassword(BSTR *password) x; \ virtual HRESULT Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password) x; \ virtual bool Open_WasPasswordAsked() x; \ virtual void Open_ClearPasswordWasAskedFlag() x; \ #endif #define INTERFACE_IOpenCallbackUI(x) \ virtual HRESULT Open_CheckBreak() x; \ virtual HRESULT Open_SetTotal(const UInt64 *files, const UInt64 *bytes) x; \ virtual HRESULT Open_SetCompleted(const UInt64 *files, const UInt64 *bytes) x; \ INTERFACE_IOpenCallbackUI_Crypto(x) struct IOpenCallbackUI { INTERFACE_IOpenCallbackUI(=0) }; class COpenCallbackImp: public IArchiveOpenCallback, public IArchiveOpenVolumeCallback, public IArchiveOpenSetSubArchiveName, #ifndef _NO_CRYPTO public ICryptoGetTextPassword, #endif public CMyUnknownImp { public: #ifndef _NO_CRYPTO MY_UNKNOWN_IMP3( IArchiveOpenVolumeCallback, ICryptoGetTextPassword, IArchiveOpenSetSubArchiveName ) #else MY_UNKNOWN_IMP2( IArchiveOpenVolumeCallback, IArchiveOpenSetSubArchiveName ) #endif INTERFACE_IArchiveOpenCallback(;) INTERFACE_IArchiveOpenVolumeCallback(;) #ifndef _NO_CRYPTO STDMETHOD(CryptoGetTextPassword)(BSTR *password); #endif STDMETHOD(SetSubArchiveName(const wchar_t *name)) { _subArchiveMode = true; _subArchiveName = name; // TotalSize = 0; return S_OK; } private: FString _folderPrefix; NWindows::NFile::NFind::CFileInfo _fileInfo; bool _subArchiveMode; UString _subArchiveName; public: UStringVector FileNames; CBoolVector FileNames_WasUsed; CRecordVector FileSizes; IOpenCallbackUI *Callback; CMyComPtr ReOpenCallback; // UInt64 TotalSize; COpenCallbackImp(): Callback(NULL) {} void Init(const FString &folderPrefix, const FString &fileName) { _folderPrefix = folderPrefix; if (!_fileInfo.Find(_folderPrefix + fileName,true)) throw 20121118; FileNames.Clear(); FileNames_WasUsed.Clear(); FileSizes.Clear(); _subArchiveMode = false; // TotalSize = 0; } bool SetSecondFileInfo(CFSTR newName) { return _fileInfo.Find(newName) && !_fileInfo.IsDir(); } }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/Common.pri000066400000000000000000000043541325366651500215200ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveOpenCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveExtractCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveCommandLine.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/DefaultName.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/DirItem.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/EnumDirItems.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/Extract.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ExtractMode.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ExtractingFilePath.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/HashCalc.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/IFileExtractCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/LoadCodecs.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/OpenArchive.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/Property.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/PropIDUtils.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/SetProperties.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/SortUtils.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/TempFiles.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/Update.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateAction.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdatePair.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateProduce.h SOURCES += $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveCommandLine.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/DefaultName.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/EnumDirItems.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/Extract.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/ExtractingFilePath.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/HashCalc.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/LoadCodecs.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/OpenArchive.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/PropIDUtils.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/SetProperties.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/SortUtils.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/TempFiles.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/Update.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateAction.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateCallback.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdatePair.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateProduce.cpp src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.cpp000066400000000000000000000017371325366651500224470ustar00rootroot00000000000000// DefaultName.cpp #include "StdAfx.h" #include "DefaultName.h" static UString GetDefaultName3(const UString &fileName, const UString &extension, const UString &addSubExtension) { int extLength = extension.Len(); int fileNameLength = fileName.Len(); if (fileNameLength > extLength + 1) { int dotPos = fileNameLength - (extLength + 1); if (fileName[dotPos] == '.') if (extension.IsEqualToNoCase(fileName.Ptr(dotPos + 1))) return fileName.Left(dotPos) + addSubExtension; } int dotPos = fileName.ReverseFind(L'.'); if (dotPos > 0) return fileName.Left(dotPos) + addSubExtension; if (addSubExtension.IsEmpty()) return fileName + L"~"; else return fileName + addSubExtension; } UString GetDefaultName2(const UString &fileName, const UString &extension, const UString &addSubExtension) { UString name = GetDefaultName3(fileName, extension, addSubExtension); name.TrimRight(); return name; } src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.h000066400000000000000000000003571325366651500221110ustar00rootroot00000000000000// DefaultName.h #ifndef __DEFAULT_NAME_H #define __DEFAULT_NAME_H #include "../../../Common/MyString.h" UString GetDefaultName2(const UString &fileName, const UString &extension, const UString &addSubExtension); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/DirItem.h000066400000000000000000000054011325366651500212540ustar00rootroot00000000000000// DirItem.h #ifndef __DIR_ITEM_H #define __DIR_ITEM_H #include "../../../Common/MyString.h" #include "../../../Windows/FileFind.h" #include "../../Common/UniqBlocks.h" #include "../../Archive/IArchive.h" struct CDirItem { UInt64 Size; FILETIME CTime; FILETIME ATime; FILETIME MTime; UString Name; #if defined(_WIN32) && !defined(UNDER_CE) // UString ShortName; CByteBuffer ReparseData; CByteBuffer ReparseData2; // fixed (reduced) absolute links bool AreReparseData() const { return ReparseData.Size() != 0 || ReparseData2.Size() != 0; } #endif UInt32 Attrib; int PhyParent; int LogParent; int SecureIndex; bool IsAltStream; CDirItem(): PhyParent(-1), LogParent(-1), SecureIndex(-1), IsAltStream(false) {} bool IsDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; } }; class CDirItems { UStringVector Prefixes; CIntVector PhyParents; CIntVector LogParents; UString GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const; void EnumerateDir(int phyParent, int logParent, const FString &phyPrefix); public: CObjectVector Items; bool SymLinks; bool ScanAltStreams; FStringVector ErrorPaths; CRecordVector ErrorCodes; UInt64 TotalSize; #ifndef UNDER_CE void SetLinkInfo(CDirItem &dirItem, const NWindows::NFile::NFind::CFileInfo &fi, const FString &phyPrefix); #endif void AddError(const FString &path, DWORD errorCode) { ErrorCodes.Add(errorCode); ErrorPaths.Add(path); } void AddError(const FString &path) { AddError(path, ::GetLastError()); } #if defined(_WIN32) && !defined(UNDER_CE) CUniqBlocks SecureBlocks; CByteBuffer TempSecureBuf; bool _saclEnabled; bool ReadSecure; void AddSecurityItem(const FString &path, int &secureIndex); #endif CDirItems(); int GetNumFolders() const { return Prefixes.Size(); } UString GetPhyPath(unsigned index) const; UString GetLogPath(unsigned index) const; unsigned AddPrefix(int phyParent, int logParent, const UString &prefix); void DeleteLastPrefix(); void EnumerateItems2( const FString &phyPrefix, const UString &logPrefix, const FStringVector &filePaths, FStringVector *requestedPaths); #if defined(_WIN32) && !defined(UNDER_CE) void FillFixedReparse(); #endif void ReserveDown(); }; struct CArcItem { UInt64 Size; FILETIME MTime; UString Name; bool IsDir; bool IsAltStream; bool SizeDefined; bool MTimeDefined; bool Censored; UInt32 IndexInServer; int TimeType; CArcItem(): IsDir(false), IsAltStream(false), SizeDefined(false), MTimeDefined(false), Censored(false), TimeType(-1) {} }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.cpp000066400000000000000000000511121325366651500226170ustar00rootroot00000000000000// EnumDirItems.cpp #include "StdAfx.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/FileName.h" #if defined(_WIN32) && !defined(UNDER_CE) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif #include "EnumDirItems.h" using namespace NWindows; using namespace NFile; using namespace NName; void AddDirFileInfo(int phyParent, int logParent, int secureIndex, const NFind::CFileInfo &fi, CObjectVector &dirItems) { CDirItem di; di.Size = fi.Size; di.CTime = fi.CTime; di.ATime = fi.ATime; di.MTime = fi.MTime; di.Attrib = fi.Attrib; // FIXME di.IsAltStream = fi.IsAltStream; di.PhyParent = phyParent; di.LogParent = logParent; di.SecureIndex = secureIndex; di.Name = fs2us(fi.Name); #if defined(_WIN32) && !defined(UNDER_CE) // di.ShortName = fs2us(fi.ShortName); #endif dirItems.Add(di); } UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const { UString path; unsigned len = name.Len(); int i; for (i = index; i >= 0; i = parents[i]) len += Prefixes[i].Len(); unsigned totalLen = len; wchar_t *p = path.GetBuffer(len); p[len] = 0; len -= name.Len(); memcpy(p + len, (const wchar_t *)name, name.Len() * sizeof(wchar_t)); for (i = index; i >= 0; i = parents[i]) { const UString &s = Prefixes[i]; len -= s.Len(); memcpy(p + len, (const wchar_t *)s, s.Len() * sizeof(wchar_t)); } path.ReleaseBuffer(totalLen); return path; } UString CDirItems::GetPhyPath(unsigned index) const { const CDirItem &di = Items[index]; return GetPrefixesPath(PhyParents, di.PhyParent, di.Name); } UString CDirItems::GetLogPath(unsigned index) const { const CDirItem &di = Items[index]; return GetPrefixesPath(LogParents, di.LogParent, di.Name); } void CDirItems::ReserveDown() { Prefixes.ReserveDown(); PhyParents.ReserveDown(); LogParents.ReserveDown(); Items.ReserveDown(); } unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix) { PhyParents.Add(phyParent); LogParents.Add(logParent); return Prefixes.Add(prefix); } void CDirItems::DeleteLastPrefix() { PhyParents.DeleteBack(); LogParents.DeleteBack(); Prefixes.DeleteBack(); } bool InitLocalPrivileges(); CDirItems::CDirItems(): SymLinks(false), TotalSize(0) #ifdef _USE_SECURITY_CODE , ReadSecure(false) #endif { #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } #ifdef _USE_SECURITY_CODE void CDirItems::AddSecurityItem(const FString &path, int &secureIndex) { secureIndex = -1; SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; if (_saclEnabled) securInfo |= SACL_SECURITY_INFORMATION; DWORD errorCode = 0; DWORD secureSize; BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); if (res) { if (secureSize == 0) return; if (secureSize > TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION; } else { errorCode = GetLastError(); if (errorCode == ERROR_INSUFFICIENT_BUFFER) { if (secureSize <= TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION; else { TempSecureBuf.Alloc(secureSize); res = ::GetFileSecurityW(fs2us(path), securInfo, TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); if (res) { if (secureSize != TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION;; } else errorCode = GetLastError(); } } } if (res) { secureIndex = SecureBlocks.AddUniq(TempSecureBuf, secureSize); return; } if (errorCode == 0) errorCode = ERROR_INVALID_FUNCTION; AddError(path, errorCode); } #endif void CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix) { NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK); for (;;) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { AddError(phyPrefix); return; } if (!found) break; int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (ReadSecure) AddSecurityItem(phyPrefix + fi.Name, secureIndex); #endif AddDirFileInfo(phyParent, logParent, secureIndex, fi, Items); if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2)); EnumerateDir(parent, parent, phyPrefix + name2); } } } void CDirItems::EnumerateItems2( const FString &phyPrefix, const UString &logPrefix, const FStringVector &filePaths, FStringVector *requestedPaths) { int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, fs2us(phyPrefix)); int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix); FOR_VECTOR (i, filePaths) { const FString &filePath = filePaths[i]; NFind::CFileInfo fi; const FString phyPath = phyPrefix + filePath; if (!fi.Find(phyPath)) { AddError(phyPath); continue; } if (requestedPaths) requestedPaths->Add(phyPath); int delimiter = filePath.ReverseFind(FCHAR_PATH_SEPARATOR); FString phyPrefixCur; int phyParentCur = phyParent; if (delimiter >= 0) { phyPrefixCur.SetFrom(filePath, delimiter + 1); phyParentCur = AddPrefix(phyParent, logParent, fs2us(phyPrefixCur)); } int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (ReadSecure) AddSecurityItem(phyPath, secureIndex); #endif AddDirFileInfo(phyParentCur, logParent, secureIndex, fi, Items); if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2)); EnumerateDir(parent, parent, phyPrefix + phyPrefixCur + name2); } } ReserveDown(); } static HRESULT EnumerateDirItems( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback); static HRESULT EnumerateDirItems_Spec( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &curFolderName, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback) { const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR; unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2)); unsigned numItems = dirItems.Items.Size(); HRESULT res = EnumerateDirItems( curNode, parent, parent, phyPrefix + name2, addArchivePrefix, dirItems, enterToSubFolders, callback); if (numItems == dirItems.Items.Size()) dirItems.DeleteLastPrefix(); return res; } #if 0 // #ifndef UNDER_CE static void EnumerateAltStreams( const NFind::CFileInfo &fi, const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems) { const FString fullPath = phyPrefix + fi.Name; NFind::CStreamEnumerator enumerator(fullPath); for (;;) { NFind::CStreamInfo si; bool found; if (!enumerator.Next(si, found)) { dirItems.AddError(fullPath + FTEXT(":*"), (DWORD)E_FAIL); break; } if (!found) break; if (si.IsMainStream()) continue; UStringVector addArchivePrefixNew = addArchivePrefix; UString reducedName = si.GetReducedName(); addArchivePrefixNew.Back() += reducedName; if (curNode.CheckPathToRoot(false, addArchivePrefixNew, true)) continue; NFind::CFileInfo fi2 = fi; fi2.Name += us2fs(reducedName); fi2.Size = si.Size; fi2.Attrib &= ~FILE_ATTRIBUTE_DIRECTORY; fi2.IsAltStream = true; AddDirFileInfo(phyParent, logParent, -1, fi2, dirItems.Items); dirItems.TotalSize += fi2.Size; } } void CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi, const FString &phyPrefix) { if (!SymLinks || !fi.HasReparsePoint()) return; const FString path = phyPrefix + fi.Name; CByteBuffer &buf = dirItem.ReparseData; if (NIO::GetReparseData(path, buf)) { CReparseAttr attr; if (attr.Parse(buf, buf.Size())) return; } AddError(path); buf.Free(); } #endif static HRESULT EnumerateForItem( NFind::CFileInfo &fi, const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback) { const UString name = fs2us(fi.Name); bool enterToSubFolders2 = enterToSubFolders; UStringVector addArchivePrefixNew = addArchivePrefix; addArchivePrefixNew.Add(name); { UStringVector addArchivePrefixNewTemp(addArchivePrefixNew); if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir())) return S_OK; } int dirItemIndex = -1; if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir())) { int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (dirItems.ReadSecure) dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex); #endif dirItemIndex = dirItems.Items.Size(); AddDirFileInfo(phyParent, logParent, secureIndex, fi, dirItems.Items); dirItems.TotalSize += fi.Size; if (fi.IsDir()) enterToSubFolders2 = true; } #if 0 // #ifndef UNDER_CE if (dirItems.ScanAltStreams) { EnumerateAltStreams(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefixNew, dirItems); } if (dirItemIndex >= 0) { CDirItem &dirItem = dirItems.Items[dirItemIndex]; dirItems.SetLinkInfo(dirItem, fi, phyPrefix); if (dirItem.ReparseData.Size() != 0) return S_OK; } #endif if (!fi.IsDir()) return S_OK; const NWildcard::CCensorNode *nextNode = 0; if (addArchivePrefix.IsEmpty()) { int index = curNode.FindSubNode(name); if (index >= 0) nextNode = &curNode.SubNodes[index]; } if (!enterToSubFolders2 && nextNode == 0) return S_OK; addArchivePrefixNew = addArchivePrefix; if (nextNode == 0) { nextNode = &curNode; addArchivePrefixNew.Add(name); } return EnumerateDirItems_Spec( *nextNode, phyParent, logParent, fi.Name, phyPrefix, addArchivePrefixNew, dirItems, enterToSubFolders2, callback); } static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode) { FOR_VECTOR (i, curNode.IncludeItems) { const NWildcard::CItem &item = curNode.IncludeItems[i]; if (item.Recursive || item.PathParts.Size() != 1) return false; const UString &name = item.PathParts.Front(); if (name.IsEmpty()) return false; /* Windows doesn't support file name with wildcard. but if another system supports file name with wildcard, and wildcard mode is disabled, we can ignore wildcard in name */ /* if (!item.WildcardParsing) continue; */ if (DoesNameContainWildcard(name)) return false; } return true; } static HRESULT EnumerateDirItems( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback) { if (!enterToSubFolders) if (curNode.NeedCheckSubDirs()) enterToSubFolders = true; if (callback) RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), dirItems.TotalSize, fs2us(phyPrefix), true)); // try direct_names case at first if (addArchivePrefix.IsEmpty() && !enterToSubFolders) { if (CanUseFsDirect(curNode)) { // all names are direct (no wildcards) // so we don't need file_system's dir enumerator CRecordVector needEnterVector; unsigned i; for (i = 0; i < curNode.IncludeItems.Size(); i++) { const NWildcard::CItem &item = curNode.IncludeItems[i]; const UString &name = item.PathParts.Front(); const FString fullPath = phyPrefix + us2fs(name); NFind::CFileInfo fi; #ifdef _WIN32 if (phyPrefix.IsEmpty() && item.IsDriveItem()) { fi.SetAsDir(); fi.Name = fullPath; } else #endif if (!fi.Find(fullPath)) { dirItems.AddError(fullPath); continue; } bool isDir = fi.IsDir(); if (isDir && !item.ForDir || !isDir && !item.ForFile) { dirItems.AddError(fullPath, (DWORD)E_FAIL); continue; } { UStringVector pathParts; pathParts.Add(fs2us(fi.Name)); if (curNode.CheckPathToRoot(false, pathParts, !isDir)) continue; } int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (dirItems.ReadSecure) dirItems.AddSecurityItem(fullPath, secureIndex); #endif AddDirFileInfo(phyParent, logParent, secureIndex, fi, dirItems.Items); #if 0 // #ifndef UNDER_CE { CDirItem &dirItem = dirItems.Items.Back(); dirItems.SetLinkInfo(dirItem, fi, phyPrefix); if (dirItem.ReparseData.Size() != 0) continue; } #endif dirItems.TotalSize += fi.Size; #if 0 // #ifndef UNDER_CE if (dirItems.ScanAltStreams) { UStringVector pathParts; pathParts.Add(fs2us(fi.Name)); EnumerateAltStreams(fi, curNode, phyParent, logParent, phyPrefix, pathParts, dirItems); } #endif if (!isDir) continue; UStringVector addArchivePrefixNew; const NWildcard::CCensorNode *nextNode = 0; int index = curNode.FindSubNode(name); if (index >= 0) { for (int t = needEnterVector.Size(); t <= index; t++) needEnterVector.Add(true); needEnterVector[index] = false; nextNode = &curNode.SubNodes[index]; } else { nextNode = &curNode; addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support } RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix, addArchivePrefixNew, dirItems, true, callback)); } for (i = 0; i < curNode.SubNodes.Size(); i++) { if (i < needEnterVector.Size()) if (!needEnterVector[i]) continue; const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i]; const FString fullPath = phyPrefix + us2fs(nextNode.Name); NFind::CFileInfo fi; #ifdef _WIN32 if (phyPrefix.IsEmpty() && NWildcard::IsDriveColonName(nextNode.Name)) { fi.SetAsDir(); fi.Name = fullPath; } else #endif if (!fi.Find(fullPath,true)) { if (!nextNode.AreThereIncludeItems()) continue; dirItems.AddError(fullPath); continue; } if (!fi.IsDir()) { dirItems.AddError(fullPath, (DWORD)E_FAIL); continue; } RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix, UStringVector(), dirItems, false, callback)); } return S_OK; } } #ifdef _WIN32 #ifndef UNDER_CE // scan drives, if wildcard is "*:\" if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0) { unsigned i; for (i = 0; i < curNode.IncludeItems.Size(); i++) { const NWildcard::CItem &item = curNode.IncludeItems[i]; if (item.PathParts.Size() < 1) break; const UString &name = item.PathParts.Front(); if (name.Len() != 2 || name[1] != ':') break; if (item.PathParts.Size() == 1) if (item.ForFile || !item.ForDir) break; if (NWildcard::IsDriveColonName(name)) continue; if (name[0] != '*' && name[0] != '?') break; } if (i == curNode.IncludeItems.Size()) { FStringVector driveStrings; NFind::MyGetLogicalDriveStrings(driveStrings); for (i = 0; i < driveStrings.Size(); i++) { FString driveName = driveStrings[i]; if (driveName.Len() < 3 || driveName.Back() != '\\') return E_FAIL; driveName.DeleteBack(); NFind::CFileInfo fi; fi.SetAsDir(); fi.Name = driveName; RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefix, dirItems, enterToSubFolders, callback)); } return S_OK; } } #endif #endif NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK); for (unsigned ttt = 0; ; ttt++) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { dirItems.AddError(phyPrefix); break; } if (!found) break; if (callback && (ttt & 0xFF) == 0xFF) RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), dirItems.TotalSize, fs2us(phyPrefix), true)); RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefix, dirItems, enterToSubFolders, callback)); } return S_OK; } HRESULT EnumerateItems( const NWildcard::CCensor &censor, const NWildcard::ECensorPathMode pathMode, const UString &addPathPrefix, CDirItems &dirItems, IEnumDirItemCallback *callback) { FOR_VECTOR (i, censor.Pairs) { const NWildcard::CPair &pair = censor.Pairs[i]; int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix); int logParent = -1; if (pathMode == NWildcard::k_AbsPath) logParent = phyParent; else { if (!addPathPrefix.IsEmpty()) logParent = dirItems.AddPrefix(-1, -1, addPathPrefix); } RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(), dirItems, false, // enterToSubFolders callback)); } dirItems.ReserveDown(); #if defined(_WIN32) && !defined(UNDER_CE) dirItems.FillFixedReparse(); #endif return S_OK; } #if defined(_WIN32) && !defined(UNDER_CE) void CDirItems::FillFixedReparse() { /* imagex/WIM reduces absolute pathes in links (raparse data), if we archive non root folder. We do same thing here */ if (!SymLinks) return; FOR_VECTOR(i, Items) { CDirItem &item = Items[i]; if (item.ReparseData.Size() == 0) continue; CReparseAttr attr; if (!attr.Parse(item.ReparseData, item.ReparseData.Size())) continue; if (attr.IsRelative()) continue; const UString &link = attr.GetPath(); if (!IsDrivePath(link)) continue; // maybe we need to support networks paths also ? FString fullPathF; if (!NDir::MyGetFullPathName(us2fs(GetPhyPath(i)), fullPathF)) continue; UString fullPath = fs2us(fullPathF); const UString logPath = GetLogPath(i); if (logPath.Len() >= fullPath.Len()) continue; if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0) continue; const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len()); if (prefix.Back() != WCHAR_PATH_SEPARATOR) continue; unsigned rootPrefixSize = GetRootPrefixSize(prefix); if (rootPrefixSize == 0) continue; if (rootPrefixSize == prefix.Len()) continue; // simple case: paths are from root if (link.Len() <= prefix.Len()) continue; if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0) continue; UString newLink = prefix.Left(rootPrefixSize); newLink += link.Ptr(prefix.Len()); CByteBuffer data; if (!FillLinkData(data, newLink, attr.IsSymLink())) continue; item.ReparseData2 = data; } } #endif src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.h000066400000000000000000000013171325366651500222660ustar00rootroot00000000000000// EnumDirItems.h #ifndef __ENUM_DIR_ITEMS_H #define __ENUM_DIR_ITEMS_H #include "../../../Common/Wildcard.h" #include "../../../Windows/FileFind.h" #include "DirItem.h" void AddDirFileInfo(int phyParent, int logParent, int secureIndex, const NWindows::NFile::NFind::CFileInfo &fi, CObjectVector &dirItems); struct IEnumDirItemCallback { virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) = 0; }; HRESULT EnumerateItems( const NWildcard::CCensor &censor, NWildcard::ECensorPathMode pathMode, const UString &addPathPrefix, CDirItems &dirItems, IEnumDirItemCallback *callback); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp000066400000000000000000000310161325366651500216650ustar00rootroot00000000000000// Extract.cpp #include "StdAfx.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #include "../Common/ExtractingFilePath.h" #include "Extract.h" #include "SetProperties.h" using namespace NWindows; using namespace NFile; using namespace NDir; static HRESULT DecompressArchive( CCodecs *codecs, const CArchiveLink &arcLink, UInt64 packSize, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, bool calcCrc, IExtractCallbackUI *callback, CArchiveExtractCallback *ecs, UString &errorMessage, UInt64 &stdInProcessed) { const CArc &arc = arcLink.Arcs.Back(); stdInProcessed = 0; IInArchive *archive = arc.Archive; CRecordVector realIndices; UStringVector removePathParts; FString outDir = options.OutputDir; UString replaceName = arc.DefaultName; if (arcLink.Arcs.Size() > 1) { // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1". // So it extracts different archives to one folder. // We will use top level archive name const CArc &arc0 = arcLink.Arcs[0]; if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe")) replaceName = arc0.DefaultName; } outDir.Replace(FSTRING_ANY_MASK, us2fs(GetCorrectFsPath(replaceName))); bool elimIsPossible = false; UString elimPrefix; // only pure name without dir delimiter FString outDirReduced = outDir; if (options.ElimDup.Val) { UString dirPrefix; SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix); if (!elimPrefix.IsEmpty()) { if (IsCharDirLimiter(elimPrefix.Back())) elimPrefix.DeleteBack(); if (!elimPrefix.IsEmpty()) { outDirReduced = us2fs(dirPrefix); elimIsPossible = true; } } } if (!options.StdInMode) { UInt32 numItems; RINOK(archive->GetNumberOfItems(&numItems)); UString filePath; for (UInt32 i = 0; i < numItems; i++) { RINOK(arc.GetItemPath(i, filePath)); if (elimIsPossible && options.ElimDup.Val) { if (!IsPath1PrefixedByPath2(filePath, elimPrefix)) elimIsPossible = false; else { wchar_t c = filePath[elimPrefix.Len()]; if (c != 0 && !IsCharDirLimiter(c)) elimIsPossible = false; } } bool isFolder; RINOK(Archive_IsItem_Folder(archive, i, isFolder)); bool isAltStream; RINOK(Archive_IsItem_AltStream(archive, i, isAltStream)); if (!options.NtOptions.AltStreams.Val && isAltStream) continue; if (!wildcardCensor.CheckPath(isAltStream, filePath, !isFolder)) continue; realIndices.Add(i); } if (realIndices.Size() == 0) { callback->ThereAreNoFiles(); return callback->ExtractResult(S_OK); } } if (elimIsPossible) outDir = outDirReduced; #ifdef _WIN32 // GetCorrectFullFsPath doesn't like "..". // outDir.TrimRight(); // outDir = GetCorrectFullFsPath(outDir); #endif if (outDir.IsEmpty()) outDir = FString(FTEXT(".")) + FString(FSTRING_PATH_SEPARATOR); else if (!CreateComplexDir(outDir)) { HRESULT res = ::GetLastError(); if (res == S_OK) res = E_FAIL; errorMessage = ((UString)L"Can not create output directory ") + fs2us(outDir); return res; } ecs->Init( options.NtOptions, options.StdInMode ? &wildcardCensor : NULL, &arc, callback, options.StdOutMode, options.TestMode, outDir, removePathParts, packSize); #ifdef SUPPORT_LINKS if (!options.StdInMode && !options.TestMode && options.NtOptions.HardLinks.Val) { RINOK(ecs->PrepareHardLinks(&realIndices)); } #endif HRESULT result; Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0; if (options.StdInMode) { result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs); NCOM::CPropVariant prop; if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK) ConvertPropVariantToUInt64(prop, stdInProcessed); } else result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs); if (result == S_OK && !options.StdInMode) result = ecs->SetDirsTimes(); return callback->ExtractResult(result); } /* v9.31: BUG was fixed: Sorted list for file paths was sorted with case insensitive compare function. But FindInSorted function did binary search via case sensitive compare function */ int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name) { unsigned left = 0, right = fileName.Size(); while (left != right) { unsigned mid = (left + right) / 2; const UString &midValue = fileName[mid]; int compare = CompareFileNames(name, midValue); if (compare == 0) return mid; if (compare < 0) right = mid; else left = mid + 1; } return -1; } HRESULT Extract( CCodecs *codecs, const CObjectVector &types, const CIntVector &excludedFormats, UStringVector &arcPaths, UStringVector &arcPathsFull, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, IOpenCallbackUI *openCallback, IExtractCallbackUI *extractCallback, #ifndef _SFX IHashCalc *hash, #endif UString &errorMessage, CDecompressStat &stat) { stat.Clear(); UInt64 totalPackSize = 0; CRecordVector arcSizes; unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size(); unsigned i; for (i = 0; i < numArcs; i++) { NFind::CFileInfo fi; fi.Size = 0; if (!options.StdInMode) { const FString &arcPath = us2fs(arcPaths[i]); if (!fi.Find(arcPath,true)) throw "there is no such archive"; if (fi.IsDir()) throw "can't decompress folder"; } arcSizes.Add(fi.Size); totalPackSize += fi.Size; } CBoolArr skipArcs(numArcs); for (i = 0; i < numArcs; i++) skipArcs[i] = false; CArchiveExtractCallback *ecs = new CArchiveExtractCallback; CMyComPtr ec(ecs); bool multi = (numArcs > 1); ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode); #ifndef _SFX ecs->SetHashMethods(hash); #endif if (multi) { RINOK(extractCallback->SetTotal(totalPackSize)); } UInt64 totalPackProcessed = 0; bool thereAreNotOpenArcs = false; for (i = 0; i < numArcs; i++) { if (skipArcs[i]) continue; const UString &arcPath = arcPaths[i]; NFind::CFileInfo fi; if (options.StdInMode) { fi.Size = 0; fi.Attrib = 0; } else { if (!fi.Find(us2fs(arcPath),true) || fi.IsDir()) throw "there is no such archive"; } #ifndef _NO_CRYPTO openCallback->Open_ClearPasswordWasAskedFlag(); #endif RINOK(extractCallback->BeforeOpen(arcPath)); CArchiveLink arcLink; CObjectVector types2 = types; /* #ifndef _SFX if (types.IsEmpty()) { int pos = arcPath.ReverseFind(L'.'); if (pos >= 0) { UString s = arcPath.Ptr(pos + 1); int index = codecs->FindFormatForExtension(s); if (index >= 0 && s == L"001") { s = arcPath.Left(pos); pos = s.ReverseFind(L'.'); if (pos >= 0) { int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1)); if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0 { types2.Add(index2); types2.Add(index); } } } } } #endif */ COpenOptions op; #ifndef _SFX op.props = &options.Properties; #endif op.codecs = codecs; op.types = &types2; op.excludedFormats = &excludedFormats; op.stdInMode = options.StdInMode; op.stream = NULL; op.filePath = arcPath; HRESULT result = arcLink.Open2(op, openCallback); if (result == E_ABORT) return result; bool crypted = false; #ifndef _NO_CRYPTO crypted = openCallback->Open_WasPasswordAsked(); #endif if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) result = S_FALSE; // arcLink.Set_ErrorsText(); RINOK(extractCallback->OpenResult(arcPath, result, crypted)); { FOR_VECTOR (r, arcLink.Arcs) { const CArc &arc = arcLink.Arcs[r]; const CArcErrorInfo &er = arc.ErrorInfo; if (er.IsThereErrorOrWarning()) { RINOK(extractCallback->SetError(r, arc.Path, er.GetErrorFlags(), er.ErrorMessage, er.GetWarningFlags(), er.WarningMessage)); } } } if (result != S_OK) { thereAreNotOpenArcs = true; if (!options.StdInMode) { NFind::CFileInfo fi; if (fi.Find(us2fs(arcPath))) if (!fi.IsDir()) totalPackProcessed += fi.Size; } continue; } if (!options.StdInMode) { // numVolumes += arcLink.VolumePaths.Size(); // arcLink.VolumesSize; // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes); // numArcs = arcPaths.Size(); if (arcLink.VolumePaths.Size() != 0) { Int64 correctionSize = arcLink.VolumesSize; FOR_VECTOR (v, arcLink.VolumePaths) { int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]); if (index >= 0) { if ((unsigned)index > i) { skipArcs[index] = true; correctionSize -= arcSizes[index]; } } } if (correctionSize != 0) { Int64 newPackSize = (Int64)totalPackSize + correctionSize; if (newPackSize < 0) newPackSize = 0; totalPackSize = newPackSize; RINOK(extractCallback->SetTotal(totalPackSize)); } } } #ifndef _NO_CRYPTO bool passwordIsDefined; UString password; RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password)); if (passwordIsDefined) { RINOK(extractCallback->SetPassword(password)); } #endif FOR_VECTOR (k, arcLink.Arcs) { const CArc &arc = arcLink.Arcs[k]; const CArcErrorInfo &er = arc.ErrorInfo; if (er.ErrorFormatIndex >= 0) { RINOK(extractCallback->OpenTypeWarning(arc.Path, codecs->GetFormatNamePtr(arc.FormatIndex), codecs->GetFormatNamePtr(er.ErrorFormatIndex))) /* UString s = L"Can not open the file as [" + codecs->Formats[arc.ErrorFormatIndex].Name + L"] archive\n"; s += L"The file is open as [" + codecs->Formats[arc.FormatIndex].Name + L"] archive"; RINOK(extractCallback->MessageError(s)); */ } { const UString &s = er.ErrorMessage; if (!s.IsEmpty()) { RINOK(extractCallback->MessageError(s)); } } } CArc &arc = arcLink.Arcs.Back(); arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice); arc.MTime = fi.MTime; UInt64 packProcessed; bool calcCrc = #ifndef _SFX (hash != NULL); #else false; #endif RINOK(DecompressArchive( codecs, arcLink, fi.Size + arcLink.VolumesSize, wildcardCensor, options, calcCrc, extractCallback, ecs, errorMessage, packProcessed)); if (!options.StdInMode) packProcessed = fi.Size + arcLink.VolumesSize; totalPackProcessed += packProcessed; ecs->LocalProgressSpec->InSize += packProcessed; ecs->LocalProgressSpec->OutSize = ecs->UnpackSize; if (!errorMessage.IsEmpty()) return E_FAIL; } if (multi || thereAreNotOpenArcs) { RINOK(extractCallback->SetTotal(totalPackSize)); RINOK(extractCallback->SetCompleted(&totalPackProcessed)); } stat.NumFolders = ecs->NumFolders; stat.NumFiles = ecs->NumFiles; stat.NumAltStreams = ecs->NumAltStreams; stat.UnpackSize = ecs->UnpackSize; stat.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize; stat.NumArchives = arcPaths.Size(); stat.PackSize = ecs->LocalProgressSpec->InSize; return S_OK; } src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.h000066400000000000000000000037771325366651500213470ustar00rootroot00000000000000// Extract.h #ifndef __EXTRACT_H #define __EXTRACT_H #include "../../../Windows/FileFind.h" #include "../../Archive/IArchive.h" #include "ArchiveExtractCallback.h" #include "ArchiveOpenCallback.h" #include "ExtractMode.h" #include "Property.h" #include "../Common/LoadCodecs.h" struct CExtractOptionsBase { CBoolPair ElimDup; bool PathMode_Force; bool OverwriteMode_Force; NExtract::NPathMode::EEnum PathMode; NExtract::NOverwriteMode::EEnum OverwriteMode; FString OutputDir; CExtractNtOptions NtOptions; CExtractOptionsBase(): PathMode_Force(false), OverwriteMode_Force(false), PathMode(NExtract::NPathMode::kFullPaths), OverwriteMode(NExtract::NOverwriteMode::kAsk) {} }; struct CExtractOptions: public CExtractOptionsBase { bool StdInMode; bool StdOutMode; bool YesToAll; bool TestMode; // bool ShowDialog; // bool PasswordEnabled; // UString Password; #ifndef _SFX CObjectVector Properties; #endif #ifdef EXTERNAL_CODECS CCodecs *Codecs; #endif CExtractOptions(): TestMode(false), StdInMode(false), StdOutMode(false), YesToAll(false) {} }; struct CDecompressStat { UInt64 NumArchives; UInt64 UnpackSize; UInt64 AltStreams_UnpackSize; UInt64 PackSize; UInt64 NumFolders; UInt64 NumFiles; UInt64 NumAltStreams; void Clear() { NumArchives = UnpackSize = AltStreams_UnpackSize = PackSize = NumFolders = NumFiles = NumAltStreams = 0; } }; HRESULT Extract( CCodecs *codecs, const CObjectVector &types, const CIntVector &excludedFormats, UStringVector &archivePaths, UStringVector &archivePathsFull, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, IOpenCallbackUI *openCallback, IExtractCallbackUI *extractCallback, #ifndef _SFX IHashCalc *hash, #endif UString &errorMessage, CDecompressStat &stat); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractMode.h000066400000000000000000000005521325366651500221400ustar00rootroot00000000000000// ExtractMode.h #ifndef __EXTRACT_MODE_H #define __EXTRACT_MODE_H namespace NExtract { namespace NPathMode { enum EEnum { kFullPaths, kCurPaths, kNoPaths, kAbsPaths }; } namespace NOverwriteMode { enum EEnum { kAsk, kOverwrite, kSkip, kRename, kRenameExisting }; } } #endif src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.cpp000066400000000000000000000102511325366651500237760ustar00rootroot00000000000000// ExtractingFilePath.cpp #include "StdAfx.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileName.h" #include "ExtractingFilePath.h" static UString ReplaceIncorrectChars(const UString &s, bool repaceColon) { #ifdef _WIN32 UString res; bool beforeColon = true; { for (unsigned i = 0; i < s.Len(); i++) { wchar_t c = s[i]; if (beforeColon) if (c == '*' || c == '?' || c < 0x20 || c == '<' || c == '>' || c == '|' || c == '"') c = '_'; if (c == ':') { if (repaceColon) c = '_'; else beforeColon = false; } res += c; } } if (beforeColon) { for (int i = res.Len() - 1; i >= 0; i--) { wchar_t c = res[i]; if (c != '.' && c != ' ') break; res.ReplaceOneCharAtPos(i, '_'); } } return res; #else return s; #endif } #ifdef _WIN32 static const wchar_t *g_ReservedNames[] = { L"CON", L"PRN", L"AUX", L"NUL" }; static bool CheckTail(const UString &name, unsigned len) { int dotPos = name.Find(L'.'); if (dotPos < 0) dotPos = name.Len(); UString s = name.Left(dotPos); s.TrimRight(); return s.Len() != len; } static bool CheckNameNum(const UString &name, const wchar_t *reservedName) { unsigned len = MyStringLen(reservedName); if (name.Len() <= len) return true; if (MyStringCompareNoCase_N(name, reservedName, len) != 0) return true; wchar_t c = name[len]; if (c < L'0' || c > L'9') return true; return CheckTail(name, len + 1); } static bool IsSupportedName(const UString &name) { for (unsigned i = 0; i < ARRAY_SIZE(g_ReservedNames); i++) { const wchar_t *reservedName = g_ReservedNames[i]; unsigned len = MyStringLen(reservedName); if (name.Len() < len) continue; if (MyStringCompareNoCase_N(name, reservedName, len) != 0) continue; if (!CheckTail(name, len)) return false; } if (!CheckNameNum(name, L"COM")) return false; return CheckNameNum(name, L"LPT"); } #endif static UString GetCorrectFileName(const UString &path, bool repaceColon) { if (path == L".." || path == L".") return UString(); return ReplaceIncorrectChars(path, repaceColon); } void MakeCorrectPath(bool isPathFromRoot, UStringVector &pathParts, bool replaceAltStreamColon) { for (unsigned i = 0; i < pathParts.Size();) { UString &s = pathParts[i]; #ifdef _WIN32 bool needReplaceColon = (replaceAltStreamColon || i != pathParts.Size() - 1); if (i == 0 && isPathFromRoot && NWindows::NFile::NName::IsDrivePath(s)) { UString s2 = s[0]; s2 += L'_'; s2 += GetCorrectFileName(s.Ptr(2), needReplaceColon); s = s2; } else s = GetCorrectFileName(s, needReplaceColon); #endif if (s.IsEmpty()) pathParts.Delete(i); else { #ifdef _WIN32 if (!IsSupportedName(s)) s = (UString)L"_" + s; #endif i++; } } } UString MakePathNameFromParts(const UStringVector &parts) { UString result; FOR_VECTOR (i, parts) { if (i != 0) result += WCHAR_PATH_SEPARATOR; result += parts[i]; } return result; } static const wchar_t *k_EmptyReplaceName = L"[]"; void Correct_IfEmptyLastPart(UStringVector &parts) { if (parts.IsEmpty()) parts.Add(k_EmptyReplaceName); else { UString &s = parts.Back(); if (s.IsEmpty()) s = k_EmptyReplaceName; } } UString GetCorrectFsPath(const UString &path) { UString res = GetCorrectFileName(path, true); #ifdef _WIN32 if (!IsSupportedName(res)) res = (UString)L"_" + res; #endif if (res.IsEmpty()) res = k_EmptyReplaceName; return res; } UString GetCorrectFullFsPath(const UString &path) { UStringVector parts; SplitPathToParts(path, parts); FOR_VECTOR (i, parts) { UString &s = parts[i]; #ifdef _WIN32 while (!s.IsEmpty() && (s.Back() == '.' || s.Back() == ' ')) s.DeleteBack(); if (!IsSupportedName(s)) s.InsertAtFront(L'_'); #endif } return MakePathNameFromParts(parts); } src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.h000066400000000000000000000012311325366651500234410ustar00rootroot00000000000000// ExtractingFilePath.h #ifndef __EXTRACTING_FILE_PATH_H #define __EXTRACTING_FILE_PATH_H #include "../../../Common/MyString.h" UString MakePathNameFromParts(const UStringVector &parts); /* for WIN32: if (isRoot == true), and pathParts[0] contains path like "c:name", it thinks that "c:" is drive prefix (it's not ":name alt stream) and the function changes part to c_name */ void MakeCorrectPath(bool isPathFromRoot, UStringVector &pathParts, bool replaceAltStreamColon); UString GetCorrectFsPath(const UString &path); UString GetCorrectFullFsPath(const UString &path); void Correct_IfEmptyLastPart(UStringVector &parts); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.cpp000066400000000000000000000211101325366651500217130ustar00rootroot00000000000000// HashCalc.cpp #include "StdAfx.h" #include "../../../../C/Alloc.h" #include "../../../Common/StringToInt.h" #include "../../Common/FileStreams.h" #include "../../Common/StreamUtils.h" #include "EnumDirItems.h" #include "HashCalc.h" using namespace NWindows; class CHashMidBuf { void *_data; public: CHashMidBuf(): _data(0) {} operator void *() { return _data; } bool Alloc(size_t size) { if (_data != 0) return false; _data = ::MidAlloc(size); return _data != 0; } ~CHashMidBuf() { ::MidFree(_data); } }; struct CEnumDirItemCallback_Hash: public IEnumDirItemCallback { IHashCallbackUI *Callback; HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) { return Callback->ScanProgress(numFolders, numFiles, totalSize, path, isDir); } }; static const wchar_t *k_DefaultHashMethod = L"CRC32"; HRESULT CHashBundle::SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &hashMethods) { UStringVector names = hashMethods; if (names.IsEmpty()) names.Add(k_DefaultHashMethod); CRecordVector ids; CObjectVector methods; unsigned i; for (i = 0; i < names.Size(); i++) { COneMethodInfo m; RINOK(m.ParseMethodFromString(names[i])); if (m.MethodName.IsEmpty()) m.MethodName = k_DefaultHashMethod; if (m.MethodName == L"*") { CRecordVector tempMethods; GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods); methods.Clear(); ids.Clear(); FOR_VECTOR (t, tempMethods) { int index = ids.AddToUniqueSorted(tempMethods[t]); if (ids.Size() != methods.Size()) methods.Insert(index, m); } break; } else { // m.MethodName.RemoveChar(L'-'); CMethodId id; if (!FindHashMethod(EXTERNAL_CODECS_LOC_VARS m.MethodName, id)) return E_NOTIMPL; int index = ids.AddToUniqueSorted(id); if (ids.Size() != methods.Size()) methods.Insert(index, m); } } for (i = 0; i < ids.Size(); i++) { CMyComPtr hasher; UString name; RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS ids[i], name, hasher)); if (!hasher) throw "Can't create hasher"; const COneMethodInfo &m = methods[i]; { CMyComPtr scp; hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp); if (scp) { RINOK(m.SetCoderProps(scp, NULL)); } } UInt32 digestSize = hasher->GetDigestSize(); if (digestSize > k_HashCalc_DigestSize_Max) return E_NOTIMPL; CHasherState &h = Hashers.AddNew(); h.Hasher = hasher; h.Name = name; h.DigestSize = digestSize; for (int i = 0; i < k_HashCalc_NumGroups; i++) memset(h.Digests[i], 0, digestSize); } return S_OK; } void CHashBundle::InitForNewFile() { CurSize = 0; FOR_VECTOR (i, Hashers) { CHasherState &h = Hashers[i]; h.Hasher->Init(); memset(h.Digests[k_HashCalc_Index_Current], 0, h.DigestSize); } } void CHashBundle::Update(const void *data, UInt32 size) { CurSize += size; FOR_VECTOR (i, Hashers) Hashers[i].Hasher->Update(data, size); } void CHashBundle::SetSize(UInt64 size) { CurSize = size; } static void AddDigests(Byte *dest, const Byte *src, UInt32 size) { unsigned next = 0; for (UInt32 i = 0; i < size; i++) { next += (unsigned)dest[i] + (unsigned)src[i]; dest[i] = (Byte)next; next >>= 8; } } void CHashBundle::Final(bool isDir, bool isAltStream, const UString &path) { if (isDir) NumDirs++; else if (isAltStream) { NumAltStreams++; AltStreamsSize += CurSize; } else { NumFiles++; FilesSize += CurSize; } Byte pre[16]; memset(pre, 0, sizeof(pre)); if (isDir) pre[0] = 1; FOR_VECTOR (i, Hashers) { CHasherState &h = Hashers[i]; if (!isDir) { h.Hasher->Final(h.Digests[0]); if (!isAltStream) AddDigests(h.Digests[k_HashCalc_Index_DataSum], h.Digests[0], h.DigestSize); } h.Hasher->Init(); h.Hasher->Update(pre, sizeof(pre)); h.Hasher->Update(h.Digests[0], h.DigestSize); for (unsigned k = 0; k < path.Len(); k++) { wchar_t c = path[k]; Byte temp[2] = { (Byte)(c & 0xFF), (Byte)((c >> 8) & 0xFF) }; h.Hasher->Update(temp, 2); } Byte tempDigest[k_HashCalc_DigestSize_Max]; h.Hasher->Final(tempDigest); if (!isAltStream) AddDigests(h.Digests[k_HashCalc_Index_NamesSum], tempDigest, h.DigestSize); AddDigests(h.Digests[k_HashCalc_Index_StreamsSum], tempDigest, h.DigestSize); } } HRESULT HashCalc( DECL_EXTERNAL_CODECS_LOC_VARS const NWildcard::CCensor &censor, const CHashOptions &options, UString &errorInfo, IHashCallbackUI *callback) { CDirItems dirItems; UInt64 numErrors = 0; UInt64 totalBytes = 0; if (options.StdInMode) { CDirItem di; di.Size = (UInt64)(Int64)-1; di.Attrib = 0; di.MTime.dwLowDateTime = 0; di.MTime.dwHighDateTime = 0; di.CTime = di.ATime = di.MTime; dirItems.Items.Add(di); } else { CEnumDirItemCallback_Hash enumCallback; enumCallback.Callback = callback; RINOK(callback->StartScanning()); dirItems.ScanAltStreams = options.AltStreamsMode; HRESULT res = EnumerateItems(censor, options.PathMode, UString(), dirItems, &enumCallback); totalBytes = dirItems.TotalSize; FOR_VECTOR (i, dirItems.ErrorPaths) { RINOK(callback->CanNotFindError(fs2us(dirItems.ErrorPaths[i]), dirItems.ErrorCodes[i])); } numErrors = dirItems.ErrorPaths.Size(); if (res != S_OK) { if (res != E_ABORT) errorInfo = L"Scanning error"; return res; } RINOK(callback->FinishScanning()); } unsigned i; CHashBundle hb; RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS options.Methods)); hb.Init(); hb.NumErrors = numErrors; if (options.StdInMode) { RINOK(callback->SetNumFiles(1)); } else { RINOK(callback->SetTotal(totalBytes)); } const UInt32 kBufSize = 1 << 15; CHashMidBuf buf; if (!buf.Alloc(kBufSize)) return E_OUTOFMEMORY; UInt64 completeValue = 0; RINOK(callback->BeforeFirstFile(hb)); for (i = 0; i < dirItems.Items.Size(); i++) { CMyComPtr inStream; UString path; bool isDir = false; bool isAltStream = false; if (options.StdInMode) { inStream = new CStdInFileStream; } else { CInFileStream *inStreamSpec = new CInFileStream; inStream = inStreamSpec; const CDirItem &dirItem = dirItems.Items[i]; isDir = dirItem.IsDir(); isAltStream = dirItem.IsAltStream; path = dirItems.GetLogPath(i); if (!isDir) { UString phyPath = dirItems.GetPhyPath(i); if (!inStreamSpec->OpenShared(us2fs(phyPath), options.OpenShareForWrite)) { HRESULT res = callback->OpenFileError(phyPath, ::GetLastError()); hb.NumErrors++; if (res != S_FALSE) return res; continue; } } } RINOK(callback->GetStream(path, isDir)); UInt64 fileSize = 0; hb.InitForNewFile(); if (!isDir) { for (UInt32 step = 0;; step++) { if ((step & 0xFF) == 0) RINOK(callback->SetCompleted(&completeValue)); UInt32 size; RINOK(inStream->Read(buf, kBufSize, &size)); if (size == 0) break; hb.Update(buf, size); fileSize += size; completeValue += size; } } hb.Final(isDir, isAltStream, path); RINOK(callback->SetOperationResult(fileSize, hb, !isDir)); RINOK(callback->SetCompleted(&completeValue)); } return callback->AfterLastFile(hb); } static inline char GetHex(Byte value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } void AddHashHexToString(char *dest, const Byte *data, UInt32 size) { dest[size * 2] = 0; if (!data) { for (UInt32 i = 0; i < size; i++) { dest[0] = ' '; dest[1] = ' '; dest += 2; } return; } int step = 2; if (size <= 8) { step = -2; dest += size * 2 - 2; } for (UInt32 i = 0; i < size; i++) { Byte b = data[i]; dest[0] = GetHex((Byte)((b >> 4) & 0xF)); dest[1] = GetHex((Byte)(b & 0xF)); dest += step; } } src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.h000066400000000000000000000056451325366651500213770ustar00rootroot00000000000000// HashCalc.h #ifndef __HASH_CALC_H #define __HASH_CALC_H #include "../../../Common/Wildcard.h" #include "../../Common/CreateCoder.h" #include "../../Common/MethodProps.h" #include "Property.h" const unsigned k_HashCalc_DigestSize_Max = 64; const unsigned k_HashCalc_NumGroups = 4; enum { k_HashCalc_Index_Current, k_HashCalc_Index_DataSum, k_HashCalc_Index_NamesSum, k_HashCalc_Index_StreamsSum }; struct CHasherState { CMyComPtr Hasher; UString Name; UInt32 DigestSize; Byte Digests[k_HashCalc_NumGroups][k_HashCalc_DigestSize_Max]; }; struct IHashCalc { virtual void InitForNewFile() = 0; virtual void Update(const void *data, UInt32 size) = 0; virtual void SetSize(UInt64 size) = 0; virtual void Final(bool isDir, bool isAltStream, const UString &path) = 0; }; struct CHashBundle: public IHashCalc { CObjectVector Hashers; UInt64 NumFiles; UInt64 NumDirs; UInt64 NumAltStreams; UInt64 FilesSize; UInt64 AltStreamsSize; UInt64 NumErrors; UInt64 CurSize; HRESULT SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &methods); void Init() { NumFiles = NumDirs = NumAltStreams = FilesSize = AltStreamsSize = NumErrors = 0; } void InitForNewFile(); void Update(const void *data, UInt32 size); void SetSize(UInt64 size); void Final(bool isDir, bool isAltStream, const UString &path); }; #define INTERFACE_IHashCallbackUI(x) \ virtual HRESULT StartScanning() x; \ virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) x; \ virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT FinishScanning() x; \ virtual HRESULT SetNumFiles(UInt64 numFiles) x; \ virtual HRESULT SetTotal(UInt64 size) x; \ virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \ virtual HRESULT CheckBreak() x; \ virtual HRESULT BeforeFirstFile(const CHashBundle &hb) x; \ virtual HRESULT GetStream(const wchar_t *name, bool isFolder) x; \ virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT SetOperationResult(UInt64 fileSize, const CHashBundle &hb, bool showHash) x; \ virtual HRESULT AfterLastFile(const CHashBundle &hb) x; \ struct IHashCallbackUI { INTERFACE_IHashCallbackUI(=0) }; struct CHashOptions { UStringVector Methods; bool OpenShareForWrite; bool StdInMode; bool AltStreamsMode; NWildcard::ECensorPathMode PathMode; CHashOptions(): StdInMode(false), OpenShareForWrite(false), AltStreamsMode(false), PathMode(NWildcard::k_RelatPath) {}; }; HRESULT HashCalc( DECL_EXTERNAL_CODECS_LOC_VARS const NWildcard::CCensor &censor, const CHashOptions &options, UString &errorInfo, IHashCallbackUI *callback); void AddHashHexToString(char *dest, const Byte *data, UInt32 size); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/IFileExtractCallback.h000066400000000000000000000043541325366651500236650ustar00rootroot00000000000000// IFileExtractCallback.h #ifndef __I_FILE_EXTRACT_CALLBACK_H #define __I_FILE_EXTRACT_CALLBACK_H #include "../../../Common/MyString.h" #include "../../IDecl.h" namespace NOverwriteAnswer { enum EEnum { kYes, kYesToAll, kNo, kNoToAll, kAutoRename, kCancel }; } DECL_INTERFACE_SUB(IFolderArchiveExtractCallback, IProgress, 0x01, 0x07) { public: STDMETHOD(AskOverwrite)( const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, Int32 *answer) PURE; STDMETHOD(PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position) PURE; STDMETHOD(MessageError)(const wchar_t *message) PURE; STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted) PURE; }; struct IExtractCallbackUI: IFolderArchiveExtractCallback { virtual HRESULT BeforeOpen(const wchar_t *name) = 0; virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted) = 0; virtual HRESULT SetError(int level, const wchar_t *name, UInt32 errorFlags, const wchar_t *errors, UInt32 warningFlags, const wchar_t *warnings) = 0; virtual HRESULT ThereAreNoFiles() = 0; virtual HRESULT ExtractResult(HRESULT result) = 0; virtual HRESULT OpenTypeWarning(const wchar_t *name, const wchar_t *okType, const wchar_t *errorType) = 0; #ifndef _NO_CRYPTO virtual HRESULT SetPassword(const UString &password) = 0; #endif }; #define INTERFACE_IGetProp(x) \ STDMETHOD(GetProp)(PROPID propID, PROPVARIANT *value) x; \ DECL_INTERFACE_SUB(IGetProp, IUnknown, 0x01, 0x20) { INTERFACE_IGetProp(PURE) }; #define INTERFACE_IFolderExtractToStreamCallback(x) \ STDMETHOD(UseExtractToStream)(Int32 *res) x; \ STDMETHOD(GetStream7)(const wchar_t *name, Int32 isDir, ISequentialOutStream **outStream, Int32 askExtractMode, IGetProp *getProp) x; \ STDMETHOD(PrepareOperation7)(Int32 askExtractMode) x; \ STDMETHOD(SetOperationResult7)(Int32 resultEOperationResult, bool encrypted) x; \ DECL_INTERFACE_SUB(IFolderExtractToStreamCallback, IUnknown, 0x01, 0x30) { INTERFACE_IFolderExtractToStreamCallback(PURE) }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.cpp000066400000000000000000000550321325366651500222570ustar00rootroot00000000000000// LoadCodecs.cpp #include "StdAfx.h" #include "../../../../C/7zVersion.h" #include "../../../Common/MyCom.h" #include "../../../Common/StringToInt.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/PropVariant.h" #include "LoadCodecs.h" using namespace NWindows; #ifdef NEW_FOLDER_INTERFACE #include "../../../Common/StringToInt.h" #endif #include "../../ICoder.h" #include "../../Common/RegisterArc.h" #ifdef EXTERNAL_CODECS #include "../../../Windows/FileFind.h" #include "../../../Windows/DLL.h" #ifdef NEW_FOLDER_INTERFACE #include "../../../Windows/ResourceString.h" static const UINT kIconTypesResId = 100; #endif #ifdef _WIN32 #include "../../../Windows/FileName.h" #include "../../../Windows/Registry.h" #else #include "../../../Common/StringConvert.h" #endif using namespace NFile; #ifdef _WIN32 extern HINSTANCE g_hInstance; #endif #define kCodecsFolderName FTEXT("Codecs") #define kFormatsFolderName FTEXT("Formats") static CFSTR kMainDll = FTEXT("7z.dll"); #ifdef _WIN32 static LPCTSTR kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip"); static LPCWSTR kProgramPathValue = L"Path"; static LPCWSTR kProgramPath2Value = L"Path" #ifdef _WIN64 L"64"; #else L"32"; #endif static bool ReadPathFromRegistry(HKEY baseKey, LPCWSTR value, FString &path) { NRegistry::CKey key; if (key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS) { UString pathU; if (key.QueryValue(value, pathU) == ERROR_SUCCESS) { path = us2fs(pathU); NName::NormalizeDirPathPrefix(path); return NFind::DoesFileExist(path + kMainDll); } } return false; } #endif // _WIN32 #endif // EXTERNAL_CODECS static const unsigned kNumArcsMax = 48; static unsigned g_NumArcs = 0; static const CArcInfo *g_Arcs[kNumArcsMax]; void RegisterArc(const CArcInfo *arcInfo) throw() { if (g_NumArcs < kNumArcsMax) { g_Arcs[g_NumArcs] = arcInfo; g_NumArcs++; } } static void SplitString(const UString &srcString, UStringVector &destStrings) { destStrings.Clear(); UString s; unsigned len = srcString.Len(); if (len == 0) return; for (unsigned i = 0; i < len; i++) { wchar_t c = srcString[i]; if (c == L' ') { if (!s.IsEmpty()) { destStrings.Add(s); s.Empty(); } } else s += c; } if (!s.IsEmpty()) destStrings.Add(s); } int CArcInfoEx::FindExtension(const UString &ext) const { FOR_VECTOR (i, Exts) if (ext.IsEqualToNoCase(Exts[i].Ext)) return i; return -1; } void CArcInfoEx::AddExts(const UString &ext, const UString &addExt) { UStringVector exts, addExts; SplitString(ext, exts); SplitString(addExt, addExts); FOR_VECTOR (i, exts) { CArcExtInfo extInfo; extInfo.Ext = exts[i]; if (i < addExts.Size()) { extInfo.AddExt = addExts[i]; if (extInfo.AddExt == L"*") extInfo.AddExt.Empty(); } Exts.Add(extInfo); } } #ifndef _SFX static bool ParseSignatures(const Byte *data, unsigned size, CObjectVector &signatures) { signatures.Clear(); while (size > 0) { unsigned len = *data++; size--; if (len > size) return false; signatures.AddNew().CopyFrom(data, len); data += len; size -= len; } return true; } #endif // _SFX #ifdef EXTERNAL_CODECS static FString GetBaseFolderPrefixFromRegistry() { FString moduleFolderPrefix = NDLL::GetModuleDirPrefix(); #ifdef _WIN32 if (!NFind::DoesFileExist(moduleFolderPrefix + kMainDll) && !NFind::DoesDirExist(moduleFolderPrefix + kCodecsFolderName) && !NFind::DoesDirExist(moduleFolderPrefix + kFormatsFolderName)) { FString path; if (ReadPathFromRegistry(HKEY_CURRENT_USER, kProgramPath2Value, path)) return path; if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPath2Value, path)) return path; if (ReadPathFromRegistry(HKEY_CURRENT_USER, kProgramPathValue, path)) return path; if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPathValue, path)) return path; } #endif return moduleFolderPrefix; } static HRESULT GetCoderClass(Func_GetMethodProperty getMethodProperty, UInt32 index, PROPID propId, CLSID &clsId, bool &isAssigned) { NCOM::CPropVariant prop; isAssigned = false; RINOK(getMethodProperty(index, propId, &prop)); if (prop.vt == VT_BSTR) { if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID)) return E_FAIL; isAssigned = true; clsId = *(const GUID *)prop.bstrVal; } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } HRESULT CCodecs::LoadCodecs() { CCodecLib &lib = Libs.Back(); lib.GetMethodProperty = (Func_GetMethodProperty)lib.Lib.GetProc("GetMethodProperty"); if (lib.GetMethodProperty) { UInt32 numMethods = 1; Func_GetNumberOfMethods getNumberOfMethodsFunc = (Func_GetNumberOfMethods)lib.Lib.GetProc("GetNumberOfMethods"); if (getNumberOfMethodsFunc) { RINOK(getNumberOfMethodsFunc(&numMethods)); } for (UInt32 i = 0; i < numMethods; i++) { CDllCodecInfo info; info.LibIndex = Libs.Size() - 1; info.CodecIndex = i; RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned)); RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned)); Codecs.Add(info); } } Func_GetHashers getHashers = (Func_GetHashers)lib.Lib.GetProc("GetHashers"); if (getHashers) { RINOK(getHashers(&lib.Hashers)); if (lib.Hashers) { UInt32 numMethods = lib.Hashers->GetNumHashers(); for (UInt32 i = 0; i < numMethods; i++) { CDllHasherInfo info; info.LibIndex = Libs.Size() - 1; info.HasherIndex = i; Hashers.Add(info); } } } return S_OK; } static HRESULT GetProp( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, NCOM::CPropVariant &prop) { if (getProp2) return getProp2(index, propID, &prop);; return getProp(propID, &prop); } static HRESULT GetProp_Bool( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, bool &res) { res = false; NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_BOOL) res = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT GetProp_UInt32( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, UInt32 &res, bool &defined) { res = 0; defined = false; NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_UI4) { res = prop.ulVal; defined = true; } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT GetProp_String( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, UString &res) { res.Empty(); NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_BSTR) res = prop.bstrVal; else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT GetProp_RawData( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, CByteBuffer &bb) { bb.Free(); NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_BSTR) { UINT len = ::SysStringByteLen(prop.bstrVal); bb.CopyFrom((const Byte *)prop.bstrVal, len); } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static const UInt32 kArcFlagsPars[] = { NArchive::NHandlerPropID::kKeepName, NArcInfoFlags::kKeepName, NArchive::NHandlerPropID::kAltStreams, NArcInfoFlags::kAltStreams, NArchive::NHandlerPropID::kNtSecure, NArcInfoFlags::kNtSecure }; HRESULT CCodecs::LoadFormats() { const NDLL::CLibrary &lib = Libs.Back().Lib; Func_GetHandlerProperty getProp = NULL; Func_GetHandlerProperty2 getProp2 = (Func_GetHandlerProperty2)lib.GetProc("GetHandlerProperty2"); Func_GetIsArc getIsArc = (Func_GetIsArc)lib.GetProc("GetIsArc"); UInt32 numFormats = 1; if (getProp2) { Func_GetNumberOfFormats getNumberOfFormats = (Func_GetNumberOfFormats)lib.GetProc("GetNumberOfFormats"); if (getNumberOfFormats) { RINOK(getNumberOfFormats(&numFormats)); } } else { getProp = (Func_GetHandlerProperty)lib.GetProc("GetHandlerProperty"); if (!getProp) return S_OK; } for (UInt32 i = 0; i < numFormats; i++) { CArcInfoEx item; item.LibIndex = Libs.Size() - 1; item.FormatIndex = i; RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kName, item.Name)); { NCOM::CPropVariant prop; if (GetProp(getProp, getProp2, i, NArchive::NHandlerPropID::kClassID, prop) != S_OK) continue; if (prop.vt != VT_BSTR) continue; if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID)) return E_FAIL; item.ClassID = *(const GUID *)prop.bstrVal; prop.Clear(); } UString ext, addExt; RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kExtension, ext)); RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kAddExtension, addExt)); item.AddExts(ext, addExt); GetProp_Bool(getProp, getProp2, i, NArchive::NHandlerPropID::kUpdate, item.UpdateEnabled); bool flags_Defined = false; RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kFlags, item.Flags, flags_Defined)); item.NewInterface = flags_Defined; if (!flags_Defined) // && item.UpdateEnabled { // support for DLL version before 9.31: for (unsigned j = 0; j < ARRAY_SIZE(kArcFlagsPars); j += 2) { bool val = false; GetProp_Bool(getProp, getProp2, i, kArcFlagsPars[j], val); if (val) item.Flags |= kArcFlagsPars[j + 1]; } } CByteBuffer sig; RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kSignature, sig)); if (sig.Size() != 0) item.Signatures.Add(sig); else { RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kMultiSignature, sig)); ParseSignatures(sig, (unsigned)sig.Size(), item.Signatures); } bool signatureOffset_Defined; RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kSignatureOffset, item.SignatureOffset, signatureOffset_Defined)); // bool version_Defined; // RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kVersion, item.Version, version_Defined)); if (getIsArc) getIsArc(i, &item.IsArcFunc); Formats.Add(item); } return S_OK; } #ifdef NEW_FOLDER_INTERFACE void CCodecIcons::LoadIcons(HMODULE m) { #ifdef _WIN32 UString iconTypes; MyLoadString(m, kIconTypesResId, iconTypes); UStringVector pairs; SplitString(iconTypes, pairs); FOR_VECTOR (i, pairs) { const UString &s = pairs[i]; int pos = s.Find(L':'); CIconPair iconPair; iconPair.IconIndex = -1; if (pos < 0) pos = s.Len(); else { UString num = s.Ptr(pos + 1); if (!num.IsEmpty()) { const wchar_t *end; iconPair.IconIndex = ConvertStringToUInt32(num, &end); if (*end != 0) continue; } } iconPair.Ext = s.Left(pos); IconPairs.Add(iconPair); } #endif // #ifdef _WIN32 } bool CCodecIcons::FindIconIndex(const UString &ext, int &iconIndex) const { #ifdef _WIN32 iconIndex = -1; FOR_VECTOR (i, IconPairs) { const CIconPair &pair = IconPairs[i]; if (ext.IsEqualToNoCase(pair.Ext)) { iconIndex = pair.IconIndex; return true; } } #endif // #ifdef _WIN32 return false; } #endif // EXTERNAL_CODECS #ifdef _7ZIP_LARGE_PAGES extern "C" { extern size_t g_LargePageSize; } #endif HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll) { #ifdef _WIN32 if (needCheckDll) { NDLL::CLibrary library; if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE)) return S_OK; } #endif Libs.Add(CCodecLib()); CCodecLib &lib = Libs.Back(); lib.Path = dllPath; bool used = false; HRESULT res = S_OK; if (lib.Lib.Load(dllPath)) { #ifdef NEW_FOLDER_INTERFACE lib.LoadIcons(); #endif #ifdef _7ZIP_LARGE_PAGES if (g_LargePageSize != 0) { Func_SetLargePageMode setLargePageMode = (Func_SetLargePageMode)lib.Lib.GetProc("SetLargePageMode"); if (setLargePageMode) setLargePageMode(); } #endif if (CaseSensitiveChange) { Func_SetCaseSensitive setCaseSensitive = (Func_SetCaseSensitive)lib.Lib.GetProc("SetCaseSensitive"); if (setCaseSensitive) setCaseSensitive(CaseSensitive ? 1 : 0); } lib.CreateObject = (Func_CreateObject)lib.Lib.GetProc("CreateObject"); if (lib.CreateObject) { unsigned startSize = Codecs.Size() + Hashers.Size(); res = LoadCodecs(); used = (startSize != Codecs.Size() + Hashers.Size()); if (res == S_OK) { startSize = Formats.Size(); res = LoadFormats(); if (startSize != Formats.Size()) used = true; } } } if (!used) Libs.DeleteBack(); return res; } HRESULT CCodecs::LoadDllsFromFolder(const FString &folderPrefix) { NFile::NFind::CEnumerator enumerator(folderPrefix + FCHAR_ANY_MASK); NFile::NFind::CFileInfo fi; while (enumerator.Next(fi)) { if (fi.IsDir()) continue; RINOK(LoadDll(folderPrefix + fi.Name, true)); } return S_OK; } #endif HRESULT CCodecs::Load() { #ifdef NEW_FOLDER_INTERFACE #ifdef _WIN32 InternalIcons.LoadIcons(g_hInstance); #endif #endif Formats.Clear(); #ifdef EXTERNAL_CODECS Codecs.Clear(); Hashers.Clear(); #endif for (UInt32 i = 0; i < g_NumArcs; i++) { const CArcInfo &arc = *g_Arcs[i]; CArcInfoEx item; item.Name.SetFromAscii(arc.Name); item.CreateInArchive = arc.CreateInArchive; item.IsArcFunc = arc.IsArc; item.Flags = arc.Flags; { UString e, ae; if (arc.Ext) e.SetFromAscii(arc.Ext); if (arc.AddExt) ae.SetFromAscii(arc.AddExt); item.AddExts(e, ae); } #ifndef _SFX item.CreateOutArchive = arc.CreateOutArchive; item.UpdateEnabled = (arc.CreateOutArchive != NULL); item.SignatureOffset = arc.SignatureOffset; // item.Version = MY_VER_MIX; item.NewInterface = true; if (arc.IsMultiSignature()) ParseSignatures(arc.Signature, arc.SignatureSize, item.Signatures); else item.Signatures.AddNew().CopyFrom(arc.Signature, arc.SignatureSize); #endif Formats.Add(item); } #ifdef EXTERNAL_CODECS const FString baseFolder = GetBaseFolderPrefixFromRegistry(); RINOK(LoadDll(baseFolder + kMainDll, false)); RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName FSTRING_PATH_SEPARATOR)); RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName FSTRING_PATH_SEPARATOR)); #endif return S_OK; } #ifndef _SFX int CCodecs::FindFormatForArchiveName(const UString &arcPath) const { int slashPos = arcPath.ReverseFind(WCHAR_PATH_SEPARATOR); int dotPos = arcPath.ReverseFind(L'.'); if (dotPos < 0 || dotPos < slashPos) return -1; const UString ext = arcPath.Ptr(dotPos + 1); if (ext.IsEmpty()) return -1; if (ext.IsEqualToNoCase(L"exe")) return -1; FOR_VECTOR (i, Formats) { const CArcInfoEx &arc = Formats[i]; /* if (!arc.UpdateEnabled) continue; */ if (arc.FindExtension(ext) >= 0) return i; } return -1; } int CCodecs::FindFormatForExtension(const UString &ext) const { if (ext.IsEmpty()) return -1; FOR_VECTOR (i, Formats) if (Formats[i].FindExtension(ext) >= 0) return i; return -1; } int CCodecs::FindFormatForArchiveType(const UString &arcType) const { FOR_VECTOR (i, Formats) if (Formats[i].Name.IsEqualToNoCase(arcType)) return i; return -1; } bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const { formatIndices.Clear(); for (unsigned pos = 0; pos < arcType.Len();) { int pos2 = arcType.Find('.', pos); if (pos2 < 0) pos2 = arcType.Len(); const UString name = arcType.Mid(pos, pos2 - pos); if (name.IsEmpty()) return false; int index = FindFormatForArchiveType(name); if (index < 0 && name != L"*") { formatIndices.Clear(); return false; } formatIndices.Add(index); pos = pos2 + 1; } return true; } #endif // _SFX #ifdef EXTERNAL_CODECS // #define EXPORT_CODECS #ifdef EXPORT_CODECS extern unsigned g_NumCodecs; STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject); STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); #define NUM_EXPORT_CODECS g_NumCodecs extern unsigned g_NumHashers; STDAPI CreateHasher(UInt32 index, IHasher **hasher); STDAPI GetHasherProp(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); #define NUM_EXPORT_HASHERS g_NumHashers #else // EXPORT_CODECS #define NUM_EXPORT_CODECS 0 #define NUM_EXPORT_HASHERS 0 #endif // EXPORT_CODECS STDMETHODIMP CCodecs::GetNumberOfMethods(UInt32 *numMethods) { *numMethods = NUM_EXPORT_CODECS #ifdef EXTERNAL_CODECS + Codecs.Size() #endif ; return S_OK; } STDMETHODIMP CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return GetMethodProperty(index, propID, value); #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (propID == NMethodPropID::kDecoderIsAssigned || propID == NMethodPropID::kEncoderIsAssigned) { NCOM::CPropVariant prop; prop = (propID == NMethodPropID::kDecoderIsAssigned) ? ci.DecoderIsAssigned : ci.EncoderIsAssigned; prop.Detach(value); return S_OK; } return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value); #else return E_FAIL; #endif } STDMETHODIMP CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return CreateCoder2(false, index, iid, coder); #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (ci.DecoderIsAssigned) return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder); return S_OK; #else return E_FAIL; #endif } STDMETHODIMP CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return CreateCoder2(true, index, iid, coder); #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (ci.EncoderIsAssigned) return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder); return S_OK; #else return E_FAIL; #endif } STDMETHODIMP_(UInt32) CCodecs::GetNumHashers() { return NUM_EXPORT_HASHERS #ifdef EXTERNAL_CODECS + Hashers.Size() #endif ; } STDMETHODIMP CCodecs::GetHasherProp(UInt32 index, PROPID propID, PROPVARIANT *value) { #ifdef EXPORT_CODECS if (index < g_NumHashers) return ::GetHasherProp(index, propID, value); #endif #ifdef EXTERNAL_CODECS const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; return Libs[ci.LibIndex].Hashers->GetHasherProp(ci.HasherIndex, propID, value); #else return E_FAIL; #endif } STDMETHODIMP CCodecs::CreateHasher(UInt32 index, IHasher **hasher) { #ifdef EXPORT_CODECS if (index < g_NumHashers) return CreateHasher(index, hasher); #endif #ifdef EXTERNAL_CODECS const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; return Libs[ci.LibIndex].Hashers->CreateHasher(ci.HasherIndex, hasher); #else return E_FAIL; #endif } int CCodecs::GetCodecLibIndex(UInt32 index) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return -1; #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; return ci.LibIndex; #else return -1; #endif } int CCodecs::GetHasherLibIndex(UInt32 index) { #ifdef EXPORT_CODECS if (index < g_NumHashers) return -1; #endif #ifdef EXTERNAL_CODECS const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; return ci.LibIndex; #else return -1; #endif } bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) { NCOM::CPropVariant prop; if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK) if (prop.vt != VT_EMPTY) return true; return false; } #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; return ci.EncoderIsAssigned; #else return false; #endif } HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id) { NCOM::CPropVariant prop; RINOK(GetProperty(index, NMethodPropID::kID, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; id = prop.uhVal.QuadPart; return S_OK; } UString CCodecs::GetCodecName(UInt32 index) { UString s; NCOM::CPropVariant prop; if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK) if (prop.vt == VT_BSTR) s = prop.bstrVal; return s; } UInt64 CCodecs::GetHasherId(UInt32 index) { NCOM::CPropVariant prop; RINOK(GetHasherProp(index, NMethodPropID::kID, &prop)); if (prop.vt != VT_UI8) return 0; return prop.uhVal.QuadPart; } UString CCodecs::GetHasherName(UInt32 index) { UString s; NCOM::CPropVariant prop; if (GetHasherProp(index, NMethodPropID::kName, &prop) == S_OK) if (prop.vt == VT_BSTR) s = prop.bstrVal; return s; } UInt32 CCodecs::GetHasherDigestSize(UInt32 index) { NCOM::CPropVariant prop; RINOK(GetHasherProp(index, NMethodPropID::kDigestSize, &prop)); if (prop.vt != VT_UI4) return 0; return prop.ulVal; } #endif // EXTERNAL_CODECS src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.h000066400000000000000000000167111325366651500217250ustar00rootroot00000000000000// LoadCodecs.h #ifndef __LOAD_CODECS_H #define __LOAD_CODECS_H #include "../../../Common/MyBuffer.h" #include "../../../Common/MyCom.h" #include "../../../Common/MyString.h" #include "../../../Common/ComTry.h" #include "../../ICoder.h" #ifdef EXTERNAL_CODECS #include "../../../Windows/DLL.h" #endif struct CDllCodecInfo { CLSID Encoder; CLSID Decoder; bool EncoderIsAssigned; bool DecoderIsAssigned; int LibIndex; UInt32 CodecIndex; }; struct CDllHasherInfo { int LibIndex; UInt32 HasherIndex; }; #include "../../Archive/IArchive.h" struct CArcExtInfo { UString Ext; UString AddExt; CArcExtInfo() {} CArcExtInfo(const UString &ext): Ext(ext) {} CArcExtInfo(const UString &ext, const UString &addExt): Ext(ext), AddExt(addExt) {} }; struct CArcInfoEx { UInt32 Flags; Func_CreateInArchive CreateInArchive; Func_IsArc IsArcFunc; UString Name; CObjectVector Exts; #ifndef _SFX Func_CreateOutArchive CreateOutArchive; bool UpdateEnabled; bool NewInterface; // UInt32 Version; UInt32 SignatureOffset; CObjectVector Signatures; #ifdef NEW_FOLDER_INTERFACE UStringVector AssociateExts; #endif #endif #ifdef EXTERNAL_CODECS int LibIndex; UInt32 FormatIndex; CLSID ClassID; #endif bool Flags_KeepName() const { return (Flags & NArcInfoFlags::kKeepName) != 0; } bool Flags_FindSignature() const { return (Flags & NArcInfoFlags::kFindSignature) != 0; } bool Flags_AltStreams() const { return (Flags & NArcInfoFlags::kAltStreams) != 0; } bool Flags_NtSecure() const { return (Flags & NArcInfoFlags::kNtSecure) != 0; } bool Flags_SymLinks() const { return (Flags & NArcInfoFlags::kSymLinks) != 0; } bool Flags_HardLinks() const { return (Flags & NArcInfoFlags::kHardLinks) != 0; } bool Flags_UseGlobalOffset() const { return (Flags & NArcInfoFlags::kUseGlobalOffset) != 0; } bool Flags_StartOpen() const { return (Flags & NArcInfoFlags::kStartOpen) != 0; } bool Flags_BackwardOpen() const { return (Flags & NArcInfoFlags::kBackwardOpen) != 0; } bool Flags_PreArc() const { return (Flags & NArcInfoFlags::kPreArc) != 0; } bool Flags_PureStartOpen() const { return (Flags & NArcInfoFlags::kPureStartOpen) != 0; } UString GetMainExt() const { if (Exts.IsEmpty()) return UString(); return Exts[0].Ext; } int FindExtension(const UString &ext) const; /* UString GetAllExtensions() const { UString s; for (int i = 0; i < Exts.Size(); i++) { if (i > 0) s += ' '; s += Exts[i].Ext; } return s; } */ void AddExts(const UString &ext, const UString &addExt); bool IsSplit() const { return StringsAreEqualNoCase_Ascii(Name, "Split"); } // bool IsRar() const { return StringsAreEqualNoCase_Ascii(Name, "Rar"); } CArcInfoEx(): Flags(0), CreateInArchive(NULL), IsArcFunc(NULL) #ifndef _SFX , CreateOutArchive(NULL) , UpdateEnabled(false) , NewInterface(false) // , Version(0) , SignatureOffset(0) #endif #ifdef EXTERNAL_CODECS , LibIndex(-1) #endif {} }; #ifdef EXTERNAL_CODECS #ifdef NEW_FOLDER_INTERFACE struct CCodecIcons { struct CIconPair { UString Ext; int IconIndex; }; CObjectVector IconPairs; void LoadIcons(HMODULE m); bool FindIconIndex(const UString &ext, int &iconIndex) const; }; #endif struct CCodecLib #ifdef NEW_FOLDER_INTERFACE : public CCodecIcons #endif { NWindows::NDLL::CLibrary Lib; FString Path; Func_GetMethodProperty GetMethodProperty; Func_CreateObject CreateObject; CMyComPtr Hashers; #ifdef NEW_FOLDER_INTERFACE void LoadIcons() { CCodecIcons::LoadIcons((HMODULE)Lib); } #endif CCodecLib(): GetMethodProperty(NULL) {} }; #endif class CCodecs: #ifdef EXTERNAL_CODECS public ICompressCodecsInfo, public IHashers, #else public IUnknown, #endif public CMyUnknownImp { public: #ifdef EXTERNAL_CODECS CObjectVector Libs; CRecordVector Codecs; CRecordVector Hashers; #ifdef NEW_FOLDER_INTERFACE CCodecIcons InternalIcons; #endif HRESULT LoadCodecs(); HRESULT LoadFormats(); HRESULT LoadDll(const FString &path, bool needCheckDll); HRESULT LoadDllsFromFolder(const FString &folderPrefix); HRESULT CreateArchiveHandler(const CArcInfoEx &ai, void **archive, bool outHandler) const { return Libs[ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &IID_IOutArchive : &IID_IInArchive, (void **)archive); } #endif public: CObjectVector Formats; bool CaseSensitiveChange; bool CaseSensitive; CCodecs(): CaseSensitiveChange(false), CaseSensitive(false) {} const wchar_t *GetFormatNamePtr(int formatIndex) { return formatIndex < 0 ? L"#" : (const wchar_t *)Formats[formatIndex].Name; } HRESULT Load(); #ifndef _SFX int FindFormatForArchiveName(const UString &arcPath) const; int FindFormatForExtension(const UString &ext) const; int FindFormatForArchiveType(const UString &arcType) const; bool FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const; #endif #ifdef EXTERNAL_CODECS MY_UNKNOWN_IMP2(ICompressCodecsInfo, IHashers) STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods); STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); STDMETHOD(CreateDecoder)(UInt32 index, const GUID *interfaceID, void **coder); STDMETHOD(CreateEncoder)(UInt32 index, const GUID *interfaceID, void **coder); STDMETHOD_(UInt32, GetNumHashers)(); STDMETHOD(GetHasherProp)(UInt32 index, PROPID propID, PROPVARIANT *value); STDMETHOD(CreateHasher)(UInt32 index, IHasher **hasher); #else MY_UNKNOWN_IMP #endif // EXTERNAL_CODECS #ifdef EXTERNAL_CODECS int GetCodecLibIndex(UInt32 index); bool GetCodecEncoderIsAssigned(UInt32 index); HRESULT GetCodecId(UInt32 index, UInt64 &id); UString GetCodecName(UInt32 index); int GetHasherLibIndex(UInt32 index); UInt64 GetHasherId(UInt32 index); UString GetHasherName(UInt32 index); UInt32 GetHasherDigestSize(UInt32 index); #endif HRESULT CreateInArchive(unsigned formatIndex, CMyComPtr &archive) const { const CArcInfoEx &ai = Formats[formatIndex]; #ifdef EXTERNAL_CODECS if (ai.LibIndex < 0) #endif { COM_TRY_BEGIN archive = ai.CreateInArchive(); return S_OK; COM_TRY_END } #ifdef EXTERNAL_CODECS return CreateArchiveHandler(ai, (void **)&archive, false); #endif } #ifndef _SFX HRESULT CreateOutArchive(unsigned formatIndex, CMyComPtr &archive) const { const CArcInfoEx &ai = Formats[formatIndex]; #ifdef EXTERNAL_CODECS if (ai.LibIndex < 0) #endif { COM_TRY_BEGIN archive = ai.CreateOutArchive(); return S_OK; COM_TRY_END } #ifdef EXTERNAL_CODECS return CreateArchiveHandler(ai, (void **)&archive, true); #endif } int FindOutFormatFromName(const UString &name) const { FOR_VECTOR (i, Formats) { const CArcInfoEx &arc = Formats[i]; if (!arc.UpdateEnabled) continue; if (arc.Name.IsEqualToNoCase(name)) return i; } return -1; } #endif // _SFX }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.cpp000066400000000000000000002461471325366651500224730ustar00rootroot00000000000000// OpenArchive.cpp #include "StdAfx.h" // #define SHOW_DEBUG_INFO #ifdef SHOW_DEBUG_INFO #include #endif #include "../../../../C/CpuArch.h" #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Common/StringToInt.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../Common/FileStreams.h" #include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamUtils.h" #include "../../Compress/CopyCoder.h" #include "DefaultName.h" #include "OpenArchive.h" #ifndef _SFX #include "SetProperties.h" #endif #ifdef SHOW_DEBUG_INFO #define PRF(x) x #else #define PRF(x) #endif // increase it, if you need to support larger SFX stubs static const UInt64 kMaxCheckStartPosition = 1 << 22; /* Open: - formatIndex >= 0 (exact Format) 1) Open with main type. Archive handler is allowed to use archive start finder. Warning, if there is tail. - formatIndex = -1 (Parser:0) (default) - same as #1 but doesn't return Parser - formatIndex = -2 (#1) - file has supported extension (like a.7z) Open with that main type (only starting from start of file). - open OK: - if there is no tail - return OK - if there is tail: - archive is not "Self Exe" - return OK with Warning, that there is tail - archive is "Self Exe" ignore "Self Exe" stub, and tries to open tail - tail can be open as archive - shows that archive and stub size property. - tail can't be open as archive - shows Parser ??? - open FAIL: Try to open with all other types from offset 0 only. If some open type is OK and physical archive size is uequal or larger than file size, then return that archive with warning that can not be open as [extension type]. If extension was EXE, it will try to open as unknown_extension case - file has unknown extension (like a.hhh) It tries to open via parser code. - if there is full archive or tail archive and unknown block or "Self Exe" at front, it shows tail archive and stub size property. - in another cases, if there is some archive inside file, it returns parser/ - in another cases, it retuens S_FALSE - formatIndex = -3 (#2) - same as #1, but - stub (EXE) + archive is open in Parser - formatIndex = -4 (#3) - returns only Parser. skip full file archive. And show other sub-archives - formatIndex = -5 (#4) - returns only Parser. skip full file archive. And show other sub-archives for each byte pos */ using namespace NWindows; /* #ifdef _SFX #define OPEN_PROPS_PARAM #else #define OPEN_PROPS_PARAM , props #endif */ /* CArc::~CArc() { GetRawProps.Release(); Archive.Release(); printf("\nCArc::~CArc()\n"); } */ #ifndef _SFX namespace NArchive { namespace NParser { struct CParseItem { UInt64 Offset; UInt64 Size; // UInt64 OkSize; UString Name; UString Extension; FILETIME FileTime; UString Comment; UString ArcType; bool FileTime_Defined; bool UnpackSize_Defined; bool NumSubDirs_Defined; bool NumSubFiles_Defined; bool IsSelfExe; bool IsNotArcType; UInt64 UnpackSize; UInt64 NumSubDirs; UInt64 NumSubFiles; int FormatIndex; bool LenIsUnknown; CParseItem(): LenIsUnknown(false), FileTime_Defined(false), UnpackSize_Defined(false), NumSubFiles_Defined(false), NumSubDirs_Defined(false), IsSelfExe(false), IsNotArcType(false) // OkSize(0) {} /* bool IsEqualTo(const CParseItem &item) const { return Offset == item.Offset && Size == item.Size; } */ void NormalizeOffset() { if ((Int64)Offset < 0) { Size += Offset; // OkSize += Offset; Offset = 0; } } }; class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { public: CObjectVector _items; UInt64 _maxEndOffset; CMyComPtr _stream; MY_UNKNOWN_IMP2( IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); UInt64 GetLastEnd() const { if (_items.IsEmpty()) return 0; const CParseItem &back = _items.Back(); return back.Offset + back.Size; } void AddUnknownItem(UInt64 next); int FindInsertPos(const CParseItem &item); void AddItem(const CParseItem &item); // void Init(); CHandler() { _maxEndOffset = 0; } }; int CHandler::FindInsertPos(const CParseItem &item) { unsigned left = 0, right = _items.Size(); while (left != right) { unsigned mid = (left + right) / 2; const CParseItem & midItem = _items[mid]; if (item.Offset < midItem.Offset) right = mid; else if (item.Offset > midItem.Offset) left = mid + 1; else if (item.Size < midItem.Size) right = mid; else if (item.Size > midItem.Size) left = mid + 1; else { left = mid + 1; // return -1; } } return left; } void CHandler::AddUnknownItem(UInt64 next) { /* UInt64 prevEnd = 0; if (!_items.IsEmpty()) { const CParseItem &back = _items.Back(); prevEnd = back.Offset + back.Size; } */ if (_maxEndOffset < next) { CParseItem item2; item2.Offset = _maxEndOffset; item2.Size = next - _maxEndOffset; _maxEndOffset = next; _items.Add(item2); } else if (_maxEndOffset > next && !_items.IsEmpty()) { CParseItem &back = _items.Back(); if (back.LenIsUnknown) { back.Size = next - back.Offset; _maxEndOffset = next; } } } void CHandler::AddItem(const CParseItem &item) { AddUnknownItem(item.Offset); int pos = FindInsertPos(item); if (pos >= 0) { _items.Insert(pos, item); UInt64 next = item.Offset + item.Size; if (_maxEndOffset < next) _maxEndOffset = next; } } /* static const STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidSize, VT_UI8}, { NULL, kpidMTime, VT_FILETIME}, { NULL, kpidType, VT_BSTR}, { NULL, kpidComment, VT_BSTR}, { NULL, kpidOffset, VT_UI8}, { NULL, kpidUnpackSize, VT_UI8}, // { NULL, kpidNumSubDirs, VT_UI8}, }; */ static const Byte kProps[] = { kpidPath, kpidSize, kpidMTime, kpidType, kpidComment, kpidOffset, kpidUnpackSize }; IMP_IInArchive_Props IMP_IInArchive_ArcProps_NO STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */) { COM_TRY_BEGIN { Close(); _stream = stream; } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { _items.Clear(); _stream.Release(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _items.Size(); return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; const CParseItem &item = _items[index]; switch (propID) { case kpidPath: { wchar_t sz[32]; ConvertUInt32ToString(index + 1, sz); UString s = sz; if (!item.Name.IsEmpty()) { s += L'.'; s += item.Name; } if (!item.Extension.IsEmpty()) { s += L'.'; s += item.Extension; } prop = s; break; } case kpidSize: case kpidPackSize: prop = item.Size; break; case kpidOffset: prop = item.Offset; break; case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break; case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break; case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break; case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break; case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break; case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break; } prop.Detach(value); return S_OK; COM_TRY_END } HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _items.Size(); if (_stream && numItems == 0) return S_OK; UInt64 totalSize = 0; UInt32 i; for (i = 0; i < numItems; i++) totalSize += _items[allFilesMode ? i : indices[i]].Size; extractCallback->SetTotal(totalSize); totalSize = 0; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr inStream(streamSpec); streamSpec->SetStream(_stream); CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; CMyComPtr outStream(outStreamSpec); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); CMyComPtr copyCoder = copyCoderSpec; for (i = 0; i < numItems; i++) { lps->InSize = totalSize; lps->OutSize = totalSize; RINOK(lps->SetCur()); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; Int32 index = allFilesMode ? i : indices[i]; const CParseItem &item = _items[index]; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); UInt64 unpackSize = item.Size; totalSize += unpackSize; bool skipMode = false; if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); outStreamSpec->SetStream(realOutStream); realOutStream.Release(); outStreamSpec->Init(skipMode ? 0 : unpackSize, true); Int32 opRes = NExtract::NOperationResult::kOK; RINOK(_stream->Seek(item.Offset, STREAM_SEEK_SET, NULL)); streamSpec->Init(unpackSize); RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); if (outStreamSpec->GetRem() != 0) opRes = NExtract::NOperationResult::kDataError; outStreamSpec->ReleaseStream(); RINOK(extractCallback->SetOperationResult(opRes)); } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN const CParseItem &item = _items[index]; return CreateLimitedInStream(_stream, item.Offset, item.Size, stream); COM_TRY_END } }} #endif HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw() { NCOM::CPropVariant prop; result = false; RINOK(arc->GetProperty(index, propID, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } HRESULT Archive_IsItem_Folder(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsDir, result); } HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsAux, result); } HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result); } HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result); } static HRESULT Archive_GetArcBoolProp(IInArchive *arc, PROPID propid, bool &result) { NCOM::CPropVariant prop; result = false; RINOK(arc->GetArchiveProperty(propid, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined) { defined = false; NCOM::CPropVariant prop; RINOK(arc->GetArchiveProperty(propid, &prop)); switch (prop.vt) { case VT_UI4: result = prop.ulVal; defined = true; break; case VT_I4: result = prop.lVal; defined = true; break; case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; defined = true; break; case VT_I8: result = (UInt64)prop.hVal.QuadPart; defined = true; break; case VT_EMPTY: break; default: return E_FAIL; } return S_OK; } static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined) { defined = false; NCOM::CPropVariant prop; RINOK(arc->GetArchiveProperty(propid, &prop)); switch (prop.vt) { case VT_UI4: result = prop.ulVal; defined = true; break; case VT_I4: result = prop.lVal; defined = true; break; case VT_UI8: result = (Int64)prop.uhVal.QuadPart; defined = true; break; case VT_I8: result = (Int64)prop.hVal.QuadPart; defined = true; break; case VT_EMPTY: break; default: return E_FAIL; } return S_OK; } HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const { if (!GetRawProps) return E_FAIL; UInt32 curIndex = index; bool prevWasAltStream = false; for (;;) { UString s; #ifdef MY_CPU_LE const void *p; UInt32 size; UInt32 propType; RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType)); if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE) s = (const wchar_t *)p; else #endif { NCOM::CPropVariant prop; RINOK(Archive->GetProperty(curIndex, kpidName, &prop)); if (prop.vt == VT_BSTR) s = prop.bstrVal; else if (prop.vt == VT_EMPTY) s = L"[Content]"; else return E_FAIL; } if (prevWasAltStream) parts[0] = s + L":" + parts[0]; else parts.Insert(0, s); UInt32 curParent = (UInt32)(Int32)-1; UInt32 parentType = 0; RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType)); if (parent == curParent) return S_OK; if (curParent == (UInt32)(Int32)-1) return E_FAIL; prevWasAltStream = (parentType == NParentType::kAltStream); curIndex = curParent; } } HRESULT CArc::GetItemPath(UInt32 index, UString &result) const { #ifdef MY_CPU_LE if (GetRawProps) { const void *p; UInt32 size; UInt32 propType; if (!IsTree) { if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK && propType == NPropDataType::kUtf16z) { unsigned len = size / 2 - 1; wchar_t *s = result.GetBuffer(len); for (unsigned i = 0; i < len; i++) { wchar_t c = GetUi16(p); p = (const void *)((const Byte *)p + 2); #if WCHAR_PATH_SEPARATOR != L'/' if (c == L'/') c = WCHAR_PATH_SEPARATOR; #endif *s++ = c; } result.ReleaseBuffer(len); if (len != 0) return S_OK; } } /* else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK && p && propType == NPropDataType::kUtf16z) { UInt32 totalSize = size; bool isOK = false; { UInt32 index2 = index; for (;;) { UInt32 parent = (UInt32)(Int32)-1; UInt32 parentType = 0; if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) break; if (parent == (UInt32)(Int32)-1) { isOK = true; break; } index2 = parent; UInt32 size2; const void *p2; if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) break; totalSize += size2; } } if (isOK) { wchar_t *sz = result.GetBuffer(totalSize / 2); UInt32 pos = totalSize - size; memcpy((Byte *)sz + pos, p, size - 2); UInt32 index2 = index; for (;;) { UInt32 parent = (UInt32)(Int32)-1; UInt32 parentType = 0; if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) break; if (parent == (UInt32)(Int32)-1) break; index2 = parent; UInt32 size2; const void *p2; if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) break; pos -= size2; memcpy((Byte *)sz + pos, p2, size2); sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':'; } result.ReleaseBuffer((totalSize - 2) / 2); #ifdef _WIN32 // result.Replace(L'/', WCHAR_PATH_SEPARATOR); #endif return S_OK; } } */ } #endif { NCOM::CPropVariant prop; RINOK(Archive->GetProperty(index, kpidPath, &prop)); if (prop.vt == VT_BSTR) result = prop.bstrVal; else if (prop.vt == VT_EMPTY) result.Empty(); else return E_FAIL; } if (result.IsEmpty()) { result = DefaultName; NCOM::CPropVariant prop; RINOK(Archive->GetProperty(index, kpidExtension, &prop)); if (prop.vt == VT_BSTR) { result += L'.'; result += prop.bstrVal; } else if (prop.vt != VT_EMPTY) return E_FAIL; } return S_OK; } HRESULT CArc::GetItemPath2(UInt32 index, UString &result) const { RINOK(GetItemPath(index, result)); if (Ask_Deleted) { bool isDeleted = false; RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted)); if (isDeleted) result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR); } return S_OK; } #ifndef _SFX static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined) { NCOM::CPropVariant prop; defined = false; size = 0; RINOK(archive->GetProperty(index, kpidSize, &prop)); switch (prop.vt) { case VT_UI1: size = prop.bVal; break; case VT_UI2: size = prop.uiVal; break; case VT_UI4: size = prop.ulVal; break; case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; case VT_EMPTY: return S_OK; default: return E_FAIL; } defined = true; return S_OK; } #endif HRESULT CArc::GetItemSize(UInt32 index, UInt64 &size, bool &defined) const { NCOM::CPropVariant prop; defined = false; size = 0; RINOK(Archive->GetProperty(index, kpidSize, &prop)); switch (prop.vt) { case VT_UI1: size = prop.bVal; break; case VT_UI2: size = prop.uiVal; break; case VT_UI4: size = prop.ulVal; break; case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; case VT_EMPTY: return S_OK; default: return E_FAIL; } defined = true; return S_OK; } HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const { NCOM::CPropVariant prop; defined = false; ft.dwHighDateTime = ft.dwLowDateTime = 0; RINOK(Archive->GetProperty(index, kpidMTime, &prop)); if (prop.vt == VT_FILETIME) { ft = prop.filetime; defined = true; } else if (prop.vt != VT_EMPTY) return E_FAIL; else if (MTimeDefined) { ft = MTime; defined = true; } return S_OK; } #ifndef _SFX static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) { for (size_t i = 0; i < size; i++) if (p1[i] != p2[i]) return false; return true; } static void MakeCheckOrder(CCodecs *codecs, CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2, const Byte *data, size_t dataSize) { for (unsigned i = 0; i < numTypes; i++) { int index = orderIndices[i]; if (index < 0) continue; const CArcInfoEx &ai = codecs->Formats[index]; if (ai.SignatureOffset != 0) { orderIndices2.Add(index); orderIndices[i] = -1; continue; } const CObjectVector &sigs = ai.Signatures; FOR_VECTOR (k, sigs) { const CByteBuffer &sig = sigs[k]; if (sig.Size() == 0 && dataSize == 0 || sig.Size() != 0 && sig.Size() <= dataSize && TestSignature(data, sig, sig.Size())) { orderIndices2.Add(index); orderIndices[i] = -1; break; } } } } #endif #ifdef UNDER_CE static const unsigned kNumHashBytes = 1; #define HASH_VAL(buf, pos) ((buf)[pos]) #else static const unsigned kNumHashBytes = 2; #define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8)) #endif #ifndef _SFX static bool IsExeExt(const UString &ext) { return ext.IsEqualToNoCase(L"exe"); } static const char *k_PreArcFormats[] = { "pe" , "elf" , "macho" , "mub" , "te" }; static bool IsNameFromList(const UString &s, const char *names[], size_t num) { for (unsigned i = 0; i < num; i++) if (StringsAreEqualNoCase_Ascii(s, names[i])) return true; return false; } static bool IsPreArcFormat(const CArcInfoEx &ai) { if (ai.Flags_PreArc()) return true; return IsNameFromList(ai.Name, k_PreArcFormats, ARRAY_SIZE(k_PreArcFormats)); } static const char *k_Formats_with_simple_signuature[] = { "7z" , "xz" , "rar" , "bzip2" , "gzip" , "cab" , "wim" , "rpm" , "vhd" , "xar" }; static bool IsNewStyleSignature(const CArcInfoEx &ai) { // if (ai.Version >= 0x91F) if (ai.NewInterface) return true; return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, ARRAY_SIZE(k_Formats_with_simple_signuature)); } class CArchiveOpenCallback_Offset: public IArchiveOpenCallback, #ifndef _NO_CRYPTO public ICryptoGetTextPassword, #endif public CMyUnknownImp { public: CMyComPtr Callback; UInt64 Files; UInt64 Offset; #ifndef _NO_CRYPTO CMyComPtr GetTextPassword; MY_UNKNOWN_IMP2( IArchiveOpenCallback, ICryptoGetTextPassword) #else MY_UNKNOWN_IMP1(IArchiveOpenCallback) #endif STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes); STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes); #ifndef _NO_CRYPTO STDMETHOD(CryptoGetTextPassword)(BSTR *password); #endif }; #ifndef _NO_CRYPTO STDMETHODIMP CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN if (GetTextPassword) return GetTextPassword->CryptoGetTextPassword(password); return E_NOTIMPL; COM_TRY_END } #endif STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */) { return S_OK; } STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 * /* files */, const UInt64 *bytes) { if (!Callback) return S_OK; UInt64 value = Offset; if (bytes) value += *bytes; return Callback->SetCompleted(&Files, &value); } #endif UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp) { if (isDefinedProp != NULL) *isDefinedProp = false; switch (prop.vt) { case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart; case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal; case VT_EMPTY: return 0; default: throw 151199; } } void CArcErrorInfo::ClearErrors() { // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!! ThereIsTail = false; UnexpecedEnd = false; IgnoreTail = false; // NonZerosTail = false; ErrorFlags_Defined = false; ErrorFlags = 0; WarningFlags = 0; TailSize = 0; ErrorMessage.Empty(); WarningMessage.Empty(); } HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes) { // OkPhySize_Defined = false; PhySizeDefined = false; PhySize = 0; Offset = 0; AvailPhySize = FileSize - startPos; ErrorInfo.ClearErrors(); { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop)); ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined); } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop)); ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop); } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidError, &prop)); if (prop.vt != VT_EMPTY) ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown error"; } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidWarning, &prop)); if (prop.vt != VT_EMPTY) ErrorInfo.WarningMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown warning"; } if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen()) { RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySizeDefined)); /* RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined)); if (!OkPhySize_Defined) { OkPhySize_Defined = PhySizeDefined; OkPhySize = PhySize; } */ bool offsetDefined; RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined)); Int64 globalOffset = startPos + Offset; AvailPhySize = FileSize - globalOffset; if (PhySizeDefined) { UInt64 endPos = globalOffset + PhySize; if (endPos < FileSize) { AvailPhySize = PhySize; ErrorInfo.ThereIsTail = true; ErrorInfo.TailSize = FileSize - endPos; } else if (endPos > FileSize) ErrorInfo.UnexpecedEnd = true; } } return S_OK; } /* static PrintNumber(const char *s, int n) { char temp[100]; sprintf(temp, "%s %d", s, n); OutputDebugStringA(temp); } */ HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr &archive) { // OutputDebugStringW(L"a1"); // PrintNumber("formatIndex", formatIndex); RINOK(op.codecs->CreateInArchive(formatIndex, archive)); // OutputDebugStringW(L"a2"); if (!archive) return S_OK; #ifdef EXTERNAL_CODECS { CMyComPtr setCompressCodecsInfo; archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs)); } } #endif // OutputDebugStringW(ai.Name); // OutputDebugStringW(L"a3"); #ifndef _SFX const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.Flags_PreArc()) { /* we notify parsers that extract executables, that they don't need to open archive, if there is tail after executable (for SFX cases) */ CMyComPtr allowTail; archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail); if (allowTail) allowTail->AllowTail(BoolToInt(true)); } if (op.props) { /* FOR_VECTOR (y, op.props) { const COptionalOpenProperties &optProps = (*op.props)[y]; if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0) { RINOK(SetProperties(archive, optProps.Props)); break; } } */ RINOK(SetProperties(archive, *op.props)); } #endif return S_OK; } #ifndef _SFX static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi) { pi.Extension = ai.GetMainExt(); pi.FileTime_Defined = false; pi.ArcType = ai.Name; RINOK(Archive_GetArcBoolProp(archive, kpidIsNotArcType, pi.IsNotArcType)); // RINOK(Archive_GetArcBoolProp(archive, kpidIsSelfExe, pi.IsSelfExe)); pi.IsSelfExe = ai.Flags_PreArc(); { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidMTime, &prop)); if (prop.vt == VT_FILETIME) { pi.FileTime_Defined = true; pi.FileTime = prop.filetime; } } if (!pi.FileTime_Defined) { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidCTime, &prop)); if (prop.vt == VT_FILETIME) { pi.FileTime_Defined = true; pi.FileTime = prop.filetime; } } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidName, &prop)); if (prop.vt == VT_BSTR) { pi.Name = prop.bstrVal; pi.Extension.Empty(); } else { RINOK(archive->GetArchiveProperty(kpidExtension, &prop)); if (prop.vt == VT_BSTR) pi.Extension = prop.bstrVal; } } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidShortComment, &prop)); if (prop.vt == VT_BSTR) pi.Comment = prop.bstrVal; } UInt32 numItems; RINOK(archive->GetNumberOfItems(&numItems)); // pi.NumSubFiles = numItems; // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined)); // if (!pi.UnpackSize_Defined) { pi.NumSubFiles = 0; pi.NumSubDirs = 0; pi.UnpackSize = 0; for (UInt32 i = 0; i < numItems; i++) { UInt64 size = 0; bool defined = false; Archive_GetItem_Size(archive, i, size, defined); if (defined) { pi.UnpackSize_Defined = true; pi.UnpackSize += size; } bool isDir = false; Archive_IsItem_Folder(archive, i, isDir); if (isDir) pi.NumSubDirs++; else pi.NumSubFiles++; } if (pi.NumSubDirs != 0) pi.NumSubDirs_Defined = true; pi.NumSubFiles_Defined = true; } return S_OK; } #endif HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset) { if (!op.stream) return S_OK; RINOK(op.stream->Seek(offset, STREAM_SEEK_SET, NULL)); const UInt32 kBufSize = 1 << 11; Byte buf[kBufSize]; for (;;) { UInt32 processed = 0; RINOK(op.stream->Read(buf, kBufSize, &processed)); if (processed == 0) { // ErrorInfo.NonZerosTail = false; ErrorInfo.IgnoreTail = true; return S_OK; } for (size_t i = 0; i < processed; i++) { if (buf[i] != 0) { // ErrorInfo.IgnoreTail = false; // ErrorInfo.NonZerosTail = true; return S_OK; } } } } #ifndef _SFX class CExtractCallback_To_OpenCallback: public IArchiveExtractCallback, public ICompressProgressInfo, public CMyUnknownImp { public: CMyComPtr Callback; UInt64 Files; UInt64 Offset; MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICompressProgressInfo) INTERFACE_IArchiveExtractCallback(;) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); void Init(IArchiveOpenCallback *callback) { Callback = callback; Files = 0; Offset = 0; } }; STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */) { if (Callback) { UInt64 value = Offset; if (inSize) value += *inSize; return Callback->SetCompleted(&Files, &value); } return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */) { *outStream = 0; return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */) { return S_OK; } static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize, IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback, IArchiveExtractCallback *extractCallback) { /* if (needPhySize) { CMyComPtr open2; archive->QueryInterface(IID_IArchiveOpen2, (void **)&open2); if (open2) return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback); } */ RINOK(archive->Open(stream, maxCheckStartPosition, openCallback)); if (needPhySize) { bool phySize_Defined = false; UInt64 phySize = 0; RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined)); if (phySize_Defined) return S_OK; bool phySizeCantBeDetected = false;; RINOK(Archive_GetArcBoolProp(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected)); if (!phySizeCantBeDetected) { RINOK(archive->Extract(0, (UInt32)(Int32)-1, BoolToInt(true), extractCallback)); } } return S_OK; } static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name) { FOR_VECTOR (i, orderIndices) if (StringsAreEqualNoCase_Ascii(codecs->Formats[orderIndices[i]].Name, name)) return i; return -1; } #endif HRESULT CArc::OpenStream2(const COpenOptions &op) { // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout); Archive.Release(); GetRawProps.Release(); GetRootProps.Release(); ErrorInfo.ClearErrors(); ErrorInfo.ErrorFormatIndex = -1; IsParseArc = false; ArcStreamOffset = 0; // OutputDebugStringW(L"1"); // OutputDebugStringW(Path); const UString fileName = ExtractFileNameFromPath(Path); UString extension; { int dotPos = fileName.ReverseFind(L'.'); if (dotPos >= 0) extension = fileName.Ptr(dotPos + 1); } CIntVector orderIndices; bool searchMarkerInHandler = false; #ifdef _SFX searchMarkerInHandler = true; #endif CBoolArr isMainFormatArr(op.codecs->Formats.Size()); { FOR_VECTOR(i, op.codecs->Formats) isMainFormatArr[i] = false; } UInt64 maxStartOffset = op.openType.MaxStartOffset_Defined ? op.openType.MaxStartOffset : kMaxCheckStartPosition; #ifndef _SFX bool isUnknownExt = false; #endif bool isForced = false; unsigned numMainTypes = 0; int formatIndex = op.openType.FormatIndex; if (formatIndex >= 0) { isForced = true; orderIndices.Add(formatIndex); numMainTypes = 1; isMainFormatArr[formatIndex] = true; searchMarkerInHandler = true; } else { unsigned numFinded = 0; #ifndef _SFX bool isPrearcExt = false; #endif { FOR_VECTOR (i, op.codecs->Formats) { const CArcInfoEx &ai = op.codecs->Formats[i]; if (IgnoreSplit || !op.openType.CanReturnArc) if (ai.IsSplit()) continue; if (op.excludedFormats->FindInSorted(i) >= 0) continue; #ifndef _SFX if (IsPreArcFormat(ai)) isPrearcExt = true; #endif if (ai.FindExtension(extension) >= 0) { // PrintNumber("orderIndices.Insert", i); orderIndices.Insert(numFinded++, i); isMainFormatArr[i] = true; } else orderIndices.Add(i); } } if (!op.stream) { if (numFinded != 1) return E_NOTIMPL; orderIndices.DeleteFrom(1); } // PrintNumber("numFinded", numFinded ); /* if (op.openOnlySpecifiedByExtension) { if (numFinded != 0 && !IsExeExt(extension)) orderIndices.DeleteFrom(numFinded); } */ #ifndef _SFX if (op.stream && orderIndices.Size() >= 2) { RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CByteBuffer byteBuffer; CIntVector orderIndices2; if (numFinded == 0 || IsExeExt(extension)) { // signature search was here } else if (extension == L"000" || extension == L"001") { int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar"); if (i >= 0) { const size_t kBufSize = (1 << 10); byteBuffer.Alloc(kBufSize); size_t processedSize = kBufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize >= 16) { const Byte *buf = byteBuffer; const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }; if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0) { orderIndices2.Add(orderIndices[i]); orderIndices[i] = -1; if (i >= (int)numFinded) numFinded++; } } } } else { const size_t kBufSize = (1 << 10); byteBuffer.Alloc(kBufSize); size_t processedSize = kBufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize == 0) return S_FALSE; /* check type order: 1) matched extension, no signuature 2) matched extension, matched signuature // 3) no signuature // 4) matched signuature */ MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0); MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize); // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0); // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize); } FOR_VECTOR (i, orderIndices) { int val = orderIndices[i]; if (val != -1) orderIndices2.Add(val); } orderIndices = orderIndices2; } if (orderIndices.Size() >= 2) { int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso"); int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf"); if (iUdf > iIso && iIso >= 0) { int isoIndex = orderIndices[iIso]; int udfIndex = orderIndices[iUdf]; orderIndices[iUdf] = isoIndex; orderIndices[iIso] = udfIndex; } } numMainTypes = numFinded; isUnknownExt = (numMainTypes == 0) || isPrearcExt; #else // _SFX numMainTypes = orderIndices.Size(); #endif } UInt64 fileSize = 0; if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } FileSize = fileSize; #ifndef _SFX CBoolArr skipFrontalFormat(op.codecs->Formats.Size()); { FOR_VECTOR(i, op.codecs->Formats) skipFrontalFormat[i] = false; } #endif const COpenType &mode = op.openType; if (mode.CanReturnArc) { // ---------- OPEN main type by extenssion ---------- unsigned numCheckTypes = orderIndices.Size(); if (formatIndex >= 0) numCheckTypes = numMainTypes; for (unsigned i = 0; i < numCheckTypes; i++) { FormatIndex = orderIndices[i]; const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; // OutputDebugStringW(ai.Name); bool exactOnly = false; if (i >= numMainTypes) { if (!ai.Flags_BackwardOpen() // && !ai.Flags_PureStartOpen() ) continue; exactOnly = true; } // Some handlers do not set total bytes. So we set it here RINOK(op.callback->SetTotal(NULL, &fileSize)); if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } CMyComPtr archive; RINOK(PrepareToOpen(op, FormatIndex, archive)); if (!archive) continue; HRESULT result; if (op.stream) { UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0; result = archive->Open(op.stream, &searchLimit, op.callback); } else { CMyComPtr openSeq; archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq); if (!openSeq) return E_NOTIMPL; result = openSeq->OpenSeq(op.seqStream); } RINOK(ReadBasicProps(archive, 0, result)); if (result == S_FALSE) { bool isArc = ErrorInfo.IsArc_After_NonOpen(); #ifndef _SFX // if it's archive, we allow another open attempt for parser if (!mode.CanReturnParser || !isArc) skipFrontalFormat[FormatIndex] = true; #endif if (exactOnly) continue; if (i == 0 && numMainTypes == 1) { // we set NonOpenErrorInfo, only if there is only one main format (defined by extension). ErrorInfo.ErrorFormatIndex = FormatIndex; NonOpen_ErrorInfo = ErrorInfo; if (!mode.CanReturnParser && isArc) { // if (formatIndex < 0 && !searchMarkerInHandler) { // if bad archive was detected, we don't need additional open attempts #ifndef _SFX if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */) #endif return S_FALSE; } } } /* #ifndef _SFX if (IsExeExt(extension) || ai.Flags_PreArc()) { // openOnlyFullArc = false; // canReturnTailArc = true; // limitSignatureSearch = true; } #endif */ continue; } RINOK(result); #ifndef _SFX bool isMainFormat = isMainFormatArr[FormatIndex]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); bool thereIsTail = ErrorInfo.ThereIsTail; if (thereIsTail && mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, Offset + PhySize)); if (ErrorInfo.IgnoreTail) thereIsTail = false; } if (Offset > 0) { if (exactOnly || !searchMarkerInHandler || !specFlags.CanReturn_NonStart() || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset)) continue; } if (thereIsTail) { if (Offset > 0) { if (!specFlags.CanReturnMid) continue; } else if (!specFlags.CanReturnFrontal) continue; } if (Offset > 0 || thereIsTail) { if (formatIndex < 0) { if (IsPreArcFormat(ai)) { // openOnlyFullArc = false; // canReturnTailArc = true; /* if (mode.SkipSfxStub) limitSignatureSearch = true; */ // if (mode.SkipSfxStub) { // skipFrontalFormat[FormatIndex] = true; continue; } } } } #endif Archive = archive; return S_OK; } } #ifndef _SFX if (!op.stream) return S_FALSE; if (formatIndex >= 0 && !mode.CanReturnParser) { if (mode.MaxStartOffset_Defined) { if (mode.MaxStartOffset == 0) return S_FALSE; } else { const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.FindExtension(extension) >= 0) { const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.Flags_FindSignature() && searchMarkerInHandler) return S_FALSE; } } } NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler; CMyComPtr handler = handlerSpec; CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback; CMyComPtr extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec; extractCallback_To_OpenCallback_Spec->Init(op.callback); { // ---------- Check all possible START archives ---------- // this code is better for full file archives than Parser's code. CByteBuffer byteBuffer; bool endOfFile = false; size_t processedSize; { size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF) if (bufSize > fileSize) { bufSize = (size_t)fileSize; endOfFile = true; } byteBuffer.Alloc(bufSize); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); processedSize = bufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize == 0) return S_FALSE; if (processedSize < bufSize) endOfFile = true; } CUIntVector sortedFormats; unsigned i; int splitIndex = -1; for (i = 0; i < orderIndices.Size(); i++) { unsigned form = orderIndices[i]; if (skipFrontalFormat[form]) continue; const CArcInfoEx &ai = op.codecs->Formats[form]; if (ai.IsSplit()) { splitIndex = form; continue; } if (ai.IsArcFunc) { UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize); if (isArcRes == k_IsArc_Res_NO) continue; if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) continue; // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue; sortedFormats.Insert(0, form); continue; } bool isNewStyleSignature = IsNewStyleSignature(ai); bool needCheck = !isNewStyleSignature || ai.Signatures.IsEmpty() || ai.Flags_PureStartOpen() || ai.Flags_StartOpen() || ai.Flags_BackwardOpen(); if (isNewStyleSignature && !ai.Signatures.IsEmpty()) { unsigned k; for (k = 0; k < ai.Signatures.Size(); k++) { const CByteBuffer &sig = ai.Signatures[k]; UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); if (processedSize < signatureEnd) { if (!endOfFile) needCheck = true; } else if (memcmp(sig, byteBuffer + ai.SignatureOffset, sig.Size()) == 0) break; } if (k != ai.Signatures.Size()) { sortedFormats.Insert(0, form); continue; } } if (needCheck) sortedFormats.Add(form); } if (splitIndex >= 0) sortedFormats.Insert(0, splitIndex); for (i = 0; i < sortedFormats.Size(); i++) { FormatIndex = sortedFormats[i]; const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; RINOK(op.callback->SetTotal(NULL, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CMyComPtr archive; RINOK(PrepareToOpen(op, FormatIndex, archive)); if (!archive) continue; PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name)); HRESULT result; { UInt64 searchLimit = 0; /* if (mode.CanReturnArc) result = archive->Open(op.stream, &searchLimit, op.callback); else */ result = OpenArchiveSpec(archive, !mode.CanReturnArc, op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback); } if (result == S_FALSE) { skipFrontalFormat[FormatIndex] = true; // FIXME: maybe we must use LenIsUnknown. // printf(" OpenForSize Error"); continue; } RINOK(result); RINOK(ReadBasicProps(archive, 0, result)); if (Offset > 0) { continue; // good handler doesn't return such Offset > 0 // but there are some cases like false prefixed PK00 archive, when // we can support it? } NArchive::NParser::CParseItem pi; pi.Offset = Offset; pi.Size = AvailPhySize; // bool needScan = false; if (!PhySizeDefined) { // it's for Z format pi.LenIsUnknown = true; // needScan = true; // phySize = arcRem; // nextNeedCheckStartOpen = false; } /* if (OkPhySize_Defined) pi.OkSize = pi.OkPhySize; else pi.OkSize = pi.Size; */ pi.NormalizeOffset(); // printf(" phySize = %8d", (unsigned)phySize); if (mode.CanReturnArc) { bool isMainFormat = isMainFormatArr[FormatIndex]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); bool openCur = false; if (!ErrorInfo.ThereIsTail) openCur = true; else { if (mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, Offset + PhySize)); if (ErrorInfo.IgnoreTail) openCur = true; } if (!openCur) { openCur = specFlags.CanReturnFrontal; if (formatIndex < 0) // format is not forced { if (IsPreArcFormat(ai)) { // if (mode.SkipSfxStub) { openCur = false; } } } } } if (openCur) { InStream = op.stream; Archive = archive; return S_OK; } } skipFrontalFormat[FormatIndex] = true; // if (!mode.CanReturnArc) /* if (!ErrorInfo.ThereIsTail) continue; */ if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) continue; // printf("\nAdd offset = %d", (int)pi.Offset); RINOK(ReadParseItemProps(archive, ai, pi)); handlerSpec->AddItem(pi); } } // ---------- PARSER ---------- CUIntVector arc2sig; // formatIndex to signatureIndex CUIntVector sig2arc; // signatureIndex to formatIndex; { unsigned sum = 0; FOR_VECTOR (i, op.codecs->Formats) { arc2sig.Add(sum); const CObjectVector &sigs = op.codecs->Formats[i].Signatures; sum += sigs.Size(); FOR_VECTOR (k, sigs) sig2arc.Add(i); } } { CArchiveOpenCallback_Offset *openCallback_Offset_Spec = new CArchiveOpenCallback_Offset; CMyComPtr openCallback_Offset = openCallback_Offset_Spec; const size_t kBeforeSize = 1 << 16; const size_t kAfterSize = 1 << 20; const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8); CByteArr hashBuffer(kNumVals); Byte *hash = hashBuffer; memset(hash, 0xFF, kNumVals); Byte prevs[256]; memset(prevs, 0xFF, sizeof(prevs)); if (sig2arc.Size() >= 0xFF) return S_FALSE; CUIntVector difficultFormats; CBoolArr difficultBools(256); { for (unsigned i = 0; i < 256; i++) difficultBools[i] = false; } bool thereAreHandlersForSearch = false; // UInt32 maxSignatureEnd = 0; FOR_VECTOR (i, orderIndices) { int index = orderIndices[i]; if (index < 0) continue; const CArcInfoEx &ai = op.codecs->Formats[index]; bool isDifficult = false; // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31) if (!ai.NewInterface) isDifficult = true; else { if (ai.Flags_StartOpen()) isDifficult = true; FOR_VECTOR (k, ai.Signatures) { const CByteBuffer &sig = ai.Signatures[k]; /* UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); if (maxSignatureEnd < signatureEnd) maxSignatureEnd = signatureEnd; */ if (sig.Size() < kNumHashBytes) { isDifficult = true; continue; } thereAreHandlersForSearch = true; UInt32 v = HASH_VAL(sig, 0); unsigned sigIndex = arc2sig[index] + k; prevs[sigIndex] = hash[v]; hash[v] = (Byte)sigIndex; } } if (isDifficult) { difficultFormats.Add(index); difficultBools[index] = true; } } if (!thereAreHandlersForSearch) { // openOnlyFullArc = true; // canReturnTailArc = true; } RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream; CMyComPtr limitedStream = limitedStreamSpec; limitedStreamSpec->SetStream(op.stream); openCallback_Offset_Spec->Callback = op.callback; #ifndef _NO_CRYPTO if (op.callback) { openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword); } #endif RINOK(op.callback->SetTotal(NULL, &fileSize)); CByteBuffer &byteBuffer = limitedStreamSpec->Buffer; byteBuffer.Alloc(kBufSize); UInt64 callbackPrev = 0; bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos. bool endOfFile = false; UInt64 bufPhyPos = 0; size_t bytesInBuf = 0; // UInt64 prevPos = 0; // ---------- Main Scan Loop ---------- UInt64 pos = 0; if (!mode.EachPos && handlerSpec->_items.Size() == 1) { NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; if (!pi.LenIsUnknown && pi.Offset == 0) pos = pi.Size; } for (;;) { // printf("\nPos = %d", (int)pos); UInt64 posInBuf = pos - bufPhyPos; // if (pos > ((UInt64)1 << 35)) break; if (!endOfFile) { if (bytesInBuf < kBufSize) { size_t processedSize = kBufSize - bytesInBuf; // printf("\nRead ask = %d", (unsigned)processedSize); UInt64 seekPos = bufPhyPos + bytesInBuf; RINOK(op.stream->Seek(bufPhyPos + bytesInBuf, STREAM_SEEK_SET, NULL)); RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize)); // printf(" processed = %d", (unsigned)processedSize); if (processedSize == 0) { fileSize = seekPos; endOfFile = true; } else { bytesInBuf += processedSize; limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos); } continue; } if (bytesInBuf < posInBuf) { UInt64 skipSize = posInBuf - bytesInBuf; if (skipSize <= kBeforeSize) { size_t keepSize = (size_t)(kBeforeSize - skipSize); // printf("\nmemmove skip = %d", (int)keepSize); memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize); bytesInBuf = keepSize; bufPhyPos = pos - keepSize; continue; } // printf("\nSkip %d", (int)(skipSize - kBeforeSize)); // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL)); bytesInBuf = 0; bufPhyPos = pos - kBeforeSize; continue; } if (bytesInBuf - posInBuf < kAfterSize) { size_t beg = (size_t)posInBuf - kBeforeSize; // printf("\nmemmove for after beg = %d", (int)beg); memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg); bufPhyPos += beg; bytesInBuf -= beg; continue; } } if (pos >= callbackPrev + (1 << 23)) { openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); openCallback_Offset_Spec->Offset = pos; RINOK(openCallback_Offset->SetCompleted(NULL, NULL)); callbackPrev = pos; } { UInt64 endPos = bufPhyPos + bytesInBuf; if (fileSize < endPos) { FileSize = fileSize; // why ???? fileSize = endPos; } } size_t availSize = bytesInBuf - (size_t)posInBuf; if (availSize < kNumHashBytes) break; size_t scanSize = availSize - ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes); { /* UInt64 scanLimit = openOnlyFullArc ? maxSignatureEnd : op.openType.ScanSize + maxSignatureEnd; */ if (!mode.CanReturnParser) { if (pos > maxStartOffset) break; UInt64 remScan = maxStartOffset - pos; if (scanSize > remScan) scanSize = (size_t)remScan; } } scanSize++; const Byte *buf = byteBuffer + (size_t)posInBuf; size_t ppp = 0; if (!needCheckStartOpen) { for (; ppp < scanSize && hash[HASH_VAL(buf, ppp)] == 0xFF; ppp++); pos += ppp; if (ppp == scanSize) continue; } UInt32 v = HASH_VAL(buf, ppp); bool nextNeedCheckStartOpen = true; unsigned i = hash[v]; unsigned indexOfDifficult = 0; // ---------- Open Loop for Current Pos ---------- bool wasOpen = false; for (;;) { unsigned index; bool isDifficult; if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size()) { index = difficultFormats[indexOfDifficult++]; isDifficult = true; } else { if (i == 0xFF) break; index = sig2arc[i]; unsigned sigIndex = i - arc2sig[index]; i = prevs[i]; if (needCheckStartOpen && difficultBools[index]) continue; const CArcInfoEx &ai = op.codecs->Formats[index]; if (pos < ai.SignatureOffset) continue; /* if (openOnlyFullArc) if (pos != ai.SignatureOffset) continue; */ const CByteBuffer &sig = ai.Signatures[sigIndex]; if (ppp + sig.Size() > availSize || !TestSignature(buf + ppp, sig, sig.Size())) continue; // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos)); // prevPos = pos; isDifficult = false; } const CArcInfoEx &ai = op.codecs->Formats[index]; if ((isDifficult && pos == 0) || ai.SignatureOffset == pos) { // we don't check same archive second time */ if (skipFrontalFormat[index]) continue; } UInt64 startArcPos = pos; if (!isDifficult) { if (pos < ai.SignatureOffset) continue; startArcPos = pos - ai.SignatureOffset; /* // we don't need the check for Z files if (startArcPos < handlerSpec->GetLastEnd()) continue; */ } if (ai.IsArcFunc && startArcPos >= bufPhyPos) { size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos); if (offsetInBuf < bytesInBuf) { UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf); if (isArcRes == k_IsArc_Res_NO) continue; if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) continue; /* if (isArcRes == k_IsArc_Res_YES_LOW_PROB) { // if (pos != ai.SignatureOffset) continue; } */ } // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name); } /* if (pos == 67109888) pos = pos; */ PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name)); bool isMainFormat = isMainFormatArr[index]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); CMyComPtr archive; RINOK(PrepareToOpen(op, index, archive)); if (!archive) return E_FAIL; // OutputDebugStringW(ai.Name); UInt64 rem = fileSize - startArcPos; UInt64 arcStreamOffset = 0; if (ai.Flags_UseGlobalOffset()) { limitedStreamSpec->InitAndSeek(0, fileSize); limitedStream->Seek(startArcPos, STREAM_SEEK_SET, NULL); } else { limitedStreamSpec->InitAndSeek(startArcPos, rem); arcStreamOffset = startArcPos; } UInt64 maxCheckStartPosition = 0; openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); openCallback_Offset_Spec->Offset = startArcPos; // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset); extractCallback_To_OpenCallback_Spec->Files = 0; extractCallback_To_OpenCallback_Spec->Offset = startArcPos; HRESULT result = OpenArchiveSpec(archive, true, limitedStream, &maxCheckStartPosition, openCallback_Offset, extractCallback_To_OpenCallback); RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result)); bool isOpen = false; if (result == S_FALSE) { if (!mode.CanReturnParser) { if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen()) { ErrorInfo.ErrorFormatIndex = index; NonOpen_ErrorInfo = ErrorInfo; // if archive was detected, we don't need additional open attempts return S_FALSE; } continue; } if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0) continue; } else { isOpen = true; RINOK(result); PRF(printf(" OK ")); } // fprintf(stderr, "\n %8X %S", startArcPos, Path); // printf("\nOpen OK: %S", ai.Name); NArchive::NParser::CParseItem pi; pi.Offset = startArcPos; if (ai.Flags_UseGlobalOffset()) pi.Offset = Offset; else if (Offset != 0) return E_FAIL; UInt64 arcRem = FileSize - pi.Offset; UInt64 phySize = arcRem; bool phySizeDefined = PhySizeDefined; if (phySizeDefined) { if (pi.Offset + PhySize > FileSize) { // ErrorInfo.ThereIsTail = true; PhySize = FileSize - pi.Offset; } phySize = PhySize; } if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63)) return E_FAIL; /* if (!ai.UseGlobalOffset) { if (phySize > arcRem) { ThereIsTail = true; phySize = arcRem; } } */ bool needScan = false; if (isOpen && !phySizeDefined) { // it's for Z format pi.LenIsUnknown = true; needScan = true; phySize = arcRem; nextNeedCheckStartOpen = false; } pi.Size = phySize; /* if (OkPhySize_Defined) pi.OkSize = OkPhySize; */ pi.NormalizeOffset(); // printf(" phySize = %8d", (unsigned)phySize); /* if (needSkipFullArc) if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize) continue; */ if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) { // it's possible for dmg archives if (!mode.CanReturnArc) continue; } if (mode.EachPos) pos++; else if (needScan) { pos++; /* if (!OkPhySize_Defined) pos++; else pos = pi.Offset + pi.OkSize; */ } else pos = pi.Offset + pi.Size; RINOK(ReadParseItemProps(archive, ai, pi)); if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */) { /* It's for DMG format. This code deletes all previous items that are included to current item */ while (!handlerSpec->_items.IsEmpty()) { { const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back(); if (back.Offset < pi.Offset) break; if (back.Offset + back.Size > pi.Offset + pi.Size) break; } handlerSpec->_items.DeleteBack(); } } if (isOpen && mode.CanReturnArc && phySizeDefined) { // if (pi.Offset + pi.Size >= fileSize) bool openCur = false; bool thereIsTail = ErrorInfo.ThereIsTail; if (thereIsTail && mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, arcStreamOffset + Offset + PhySize)); if (ErrorInfo.IgnoreTail) thereIsTail = false; } if (pi.Offset != 0) { if (!pi.IsNotArcType) if (thereIsTail) openCur = specFlags.CanReturnMid; else openCur = specFlags.CanReturnTail; } else { if (!thereIsTail) openCur = true; else openCur = specFlags.CanReturnFrontal; if (formatIndex >= -2) openCur = true; } if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */) openCur = false; // We open file as SFX, if there is front archive or first archive is "Self Executable" if (!openCur && !pi.IsSelfExe && !thereIsTail && (!pi.IsNotArcType || pi.Offset == 0)) { if (handlerSpec->_items.IsEmpty()) { if (specFlags.CanReturnTail) openCur = true; } else if (handlerSpec->_items.Size() == 1) { if (handlerSpec->_items[0].IsSelfExe) { if (mode.SpecUnknownExt.CanReturnTail) openCur = true; } } } if (openCur) { InStream = op.stream; Archive = archive; FormatIndex = index; ArcStreamOffset = arcStreamOffset; return S_OK; } } /* if (openOnlyFullArc) { ErrorInfo.ClearErrors(); return S_FALSE; } */ pi.FormatIndex = index; // printf("\nAdd offset = %d", (int)pi.Offset); handlerSpec->AddItem(pi); wasOpen = true; break; } // ---------- End of Open Loop for Current Pos ---------- if (!wasOpen) pos++; needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen); } // ---------- End of Main Scan Loop ---------- /* if (handlerSpec->_items.Size() == 1) { const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; if (pi.Size == fileSize && pi.Offset == 0) { Archive = archive; FormatIndex2 = pi.FormatIndex; return S_OK; } } */ if (mode.CanReturnParser) { bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing handlerSpec->AddUnknownItem(fileSize); if (handlerSpec->_items.Size() == 0) return S_FALSE; if (returnParser || handlerSpec->_items.Size() != 1) { // return S_FALSE; handlerSpec->_stream = op.stream; Archive = handler; ErrorInfo.ClearErrors(); IsParseArc = true; FormatIndex = -1; // It's parser Offset = 0; return S_OK; } } } #endif if (!Archive) return S_FALSE; return S_OK; } HRESULT CArc::OpenStream(const COpenOptions &op) { RINOK(OpenStream2(op)); // PrintNumber("op.formatIndex 3", op.formatIndex); if (Archive) { GetRawProps.Release(); GetRootProps.Release(); Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps); Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps); RINOK(Archive_GetArcBoolProp(Archive, kpidIsTree, IsTree)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsDeleted, Ask_Deleted)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsAltStream, Ask_AltStream)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsAux, Ask_Aux)); RINOK(Archive_GetArcBoolProp(Archive, kpidINode, Ask_INode)); const UString fileName = ExtractFileNameFromPath(Path); UString extension; { int dotPos = fileName.ReverseFind(L'.'); if (dotPos >= 0) extension = fileName.Ptr(dotPos + 1); } DefaultName.Empty(); if (FormatIndex >= 0) { const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; if (ai.Exts.Size() == 0) DefaultName = GetDefaultName2(fileName, L"", L""); else { int subExtIndex = ai.FindExtension(extension); if (subExtIndex < 0) subExtIndex = 0; const CArcExtInfo &extInfo = ai.Exts[subExtIndex]; DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt); } } } return S_OK; } #ifdef _SFX #ifdef _WIN32 static const wchar_t *k_ExeExt = L".exe"; static const unsigned k_ExeExt_Len = 4; #else static const wchar_t *k_ExeExt = L""; static const unsigned k_ExeExt_Len = 0; #endif #endif HRESULT CArc::OpenStreamOrFile(COpenOptions &op) { CMyComPtr fileStream; CMyComPtr seqStream; CInFileStream *fileStreamSpec = NULL; if (op.stdInMode) { seqStream = new CStdInFileStream; op.seqStream = seqStream; } else if (!op.stream) { fileStreamSpec = new CInFileStream(true); fileStream = fileStreamSpec; Path = filePath; if (!fileStreamSpec->Open(us2fs(Path))) { return GetLastError(); } op.stream = fileStream; #ifdef _SFX IgnoreSplit = true; #endif } /* if (callback) { UInt64 fileSize; RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.callback->SetTotal(NULL, &fileSize)) } */ HRESULT res = OpenStream(op); IgnoreSplit = false; #ifdef _SFX if (res != S_FALSE || !fileStreamSpec || !op.callbackSpec || NonOpen_ErrorInfo.IsArc_After_NonOpen()) return res; { if (filePath.Len() > k_ExeExt_Len && MyStringCompareNoCase(filePath.RightPtr(k_ExeExt_Len), k_ExeExt) == 0) { const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len); FOR_VECTOR (i, op.codecs->Formats) { const CArcInfoEx &ai = op.codecs->Formats[i]; if (ai.IsSplit()) continue; UString path3 = path2; path3 += L"."; path3 += ai.GetMainExt(); // "7z" for SFX. Path = path3 + L".001"; bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); if (!isOk) { Path = path3; isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); } if (isOk) { if (fileStreamSpec->Open(us2fs(Path))) { op.stream = fileStream; NonOpen_ErrorInfo.ClearErrors_Full(); if (OpenStream(op) == S_OK) return S_OK; } } } } } #endif return res; } void CArchiveLink::KeepModeForNextOpen() { for (int i = Arcs.Size() - 1; i >= 0; i--) { CMyComPtr keep; Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep); if (keep) keep->KeepModeForNextOpen(); } } HRESULT CArchiveLink::Close() { for (int i = Arcs.Size() - 1; i >= 0; i--) { RINOK(Arcs[i].Close()); } IsOpen = false; // ErrorsText.Empty(); return S_OK; } void CArchiveLink::Release() { // NonOpenErrorFormatIndex = -1; NonOpen_ErrorInfo.ClearErrors(); NonOpen_ArcPath.Empty(); while (!Arcs.IsEmpty()) Arcs.DeleteBack(); } /* void CArchiveLink::Set_ErrorsText() { FOR_VECTOR(i, Arcs) { const CArc &arc = Arcs[i]; if (!arc.ErrorFlagsText.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += GetUnicodeString(arc.ErrorFlagsText); } if (!arc.ErrorMessage.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += arc.ErrorMessage; } if (!arc.WarningMessage.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += arc.WarningMessage; } } } */ HRESULT CArchiveLink::Open(COpenOptions &op) { Release(); if (op.types->Size() >= 32) return E_NOTIMPL; HRESULT resSpec; for (;;) { resSpec = S_OK; op.openType = COpenType(); if (op.types->Size() >= 1) { COpenType latest; if (Arcs.Size() < op.types->Size()) latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; else { latest = (*op.types)[0]; if (!latest.Recursive) break; } op.openType = latest; } else if (Arcs.Size() >= 32) break; /* op.formatIndex = -1; if (op.types->Size() >= 1) { int latest; if (Arcs.Size() < op.types->Size()) latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; else { latest = (*op.types)[0]; if (latest != -2 && latest != -3) break; } if (latest >= 0) op.formatIndex = latest; else if (latest == -1 || latest == -2) { // default } else if (latest == -3) op.formatIndex = -2; else op.formatIndex = latest + 2; } else if (Arcs.Size() >= 32) break; */ if (Arcs.IsEmpty()) { CArc arc; arc.filePath = op.filePath; arc.Path = op.filePath; arc.SubfileIndex = (UInt32)(Int32)-1; HRESULT result = arc.OpenStreamOrFile(op); if (result != S_OK) { if (result == S_FALSE) { NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo; // NonOpenErrorFormatIndex = arc.ErrorFormatIndex; NonOpen_ArcPath = arc.Path; } return result; } Arcs.Add(arc); continue; } // PrintNumber("op.formatIndex 11", op.formatIndex); const CArc &arc = Arcs.Back(); if (op.types->Size() > Arcs.Size()) resSpec = E_NOTIMPL; UInt32 mainSubfile; { NCOM::CPropVariant prop; RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop)); if (prop.vt == VT_UI4) mainSubfile = prop.ulVal; else break; UInt32 numItems; RINOK(arc.Archive->GetNumberOfItems(&numItems)); if (mainSubfile >= numItems) break; } CMyComPtr getStream; if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream) break; CMyComPtr subSeqStream; if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream) break; CMyComPtr subStream; if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream) break; CArc arc2; RINOK(arc.GetItemPath(mainSubfile, arc2.Path)); bool zerosTailIsAllowed; RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed)); CMyComPtr setSubArchiveName; op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); if (setSubArchiveName) setSubArchiveName->SetSubArchiveName(arc2.Path); arc2.SubfileIndex = mainSubfile; // CIntVector incl; CIntVector excl; COpenOptions op2; #ifndef _SFX op2.props = op.props; #endif op2.codecs = op.codecs; // op2.types = &incl; op2.openType = op.openType; op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed; op2.excludedFormats = ! op2.stdInMode = false; op2.stream = subStream; op2.filePath = arc2.Path; op2.callback = op.callback; op2.callbackSpec = op.callbackSpec; HRESULT result = arc2.OpenStream(op2); resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE); if (result == S_FALSE) { NonOpen_ErrorInfo = arc2.ErrorInfo; NonOpen_ArcPath = arc2.Path; break; } RINOK(result); RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined)); Arcs.Add(arc2); } IsOpen = !Arcs.IsEmpty(); return resSpec; } static void SetCallback(const FString &filePath, IOpenCallbackUI *callbackUI, IArchiveOpenCallback *reOpenCallback, CMyComPtr &callback) { COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; callback = openCallbackSpec; openCallbackSpec->Callback = callbackUI; openCallbackSpec->ReOpenCallback = reOpenCallback; FString dirPrefix, fileName; NFile::NDir::GetFullPathAndSplit(filePath, dirPrefix, fileName); openCallbackSpec->Init(dirPrefix, fileName); } HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI) { VolumesSize = 0; COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; CMyComPtr callback = openCallbackSpec; openCallbackSpec->Callback = callbackUI; FString prefix, name; if (!op.stream && !op.stdInMode) { NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name); openCallbackSpec->Init(prefix, name); } else { openCallbackSpec->SetSubArchiveName(op.filePath); } op.callback = callback; op.callbackSpec = openCallbackSpec; RINOK(Open(op)); // VolumePaths.Add(fs2us(prefix + name)); FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed) { if (openCallbackSpec->FileNames_WasUsed[i]) { VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]); VolumesSize += openCallbackSpec->FileSizes[i]; } } // VolumesSize = openCallbackSpec->TotalSize; return S_OK; } HRESULT CArc::ReOpen(const COpenOptions &op) { ErrorInfo.ClearErrors(); ErrorInfo.ErrorFormatIndex = -1; UInt64 fileSize = 0; if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } FileSize = fileSize; CMyComPtr stream2; Int64 globalOffset = GetGlobalOffset(); if (globalOffset <= 0) stream2 = op.stream; else { CTailInStream *tailStreamSpec = new CTailInStream; stream2 = tailStreamSpec; tailStreamSpec->Stream = op.stream; tailStreamSpec->Offset = globalOffset; tailStreamSpec->Init(); RINOK(tailStreamSpec->SeekToStart()); } // There are archives with embedded STUBs (like ZIP), so we must support signature scanning // But for another archives we can use 0 here. So the code can be fixed !!! UInt64 maxStartPosition = kMaxCheckStartPosition; HRESULT res = Archive->Open(stream2, &maxStartPosition, op.callback); if (res == S_OK) { RINOK(ReadBasicProps(Archive, globalOffset, res)); ArcStreamOffset = globalOffset; if (ArcStreamOffset != 0) InStream = op.stream; } return res; } HRESULT CArchiveLink::ReOpen(COpenOptions &op) { if (Arcs.Size() > 1) return E_NOTIMPL; CObjectVector inc; CIntVector excl; op.types = &inc; op.excludedFormats = ! op.stdInMode = false; op.stream = NULL; if (Arcs.Size() == 0) // ??? return Open2(op, NULL); CMyComPtr openCallbackNew; SetCallback(us2fs(op.filePath), NULL, op.callback, openCallbackNew); CInFileStream *fileStreamSpec = new CInFileStream(true); CMyComPtr stream(fileStreamSpec); if (!fileStreamSpec->Open(us2fs(op.filePath))) return GetLastError(); op.stream = stream; CArc &arc = Arcs[0]; HRESULT res = arc.ReOpen(op); IsOpen = (res == S_OK); return res; } #ifndef _SFX bool ParseComplexSize(const wchar_t *s, UInt64 &result) { result = 0; const wchar_t *end; UInt64 number = ConvertStringToUInt64(s, &end); if (end == s) return false; if (*end == 0) { result = number; return true; } if (end[1] != 0) return false; unsigned numBits; switch (MyCharLower_Ascii(*end)) { case 'b': result = number; return true; case 'k': numBits = 10; break; case 'm': numBits = 20; break; case 'g': numBits = 30; break; case 't': numBits = 40; break; default: return false; } if (number >= ((UInt64)1 << (64 - numBits))) return false; result = number << numBits; return true; } static bool ParseTypeParams(const UString &s, COpenType &type) { if (s[0] == 0) return true; if (s[1] == 0) { switch ((unsigned)(Byte)s[0]) { case 'e': type.EachPos = true; return true; case 'a': type.CanReturnArc = true; return true; case 'r': type.Recursive = true; return true; } return false; } if (s[0] == 's') { UInt64 result; if (!ParseComplexSize(s.Ptr(1), result)) return false; type.MaxStartOffset = result; type.MaxStartOffset_Defined = true; return true; } return false; } bool ParseType(CCodecs &codecs, const UString &s, COpenType &type) { int pos2 = s.Find(':'); UString name; if (pos2 < 0) { name = s; pos2 = s.Len(); } else { name = s.Left(pos2); pos2++; } int index = codecs.FindFormatForArchiveType(name); type.Recursive = false; if (index < 0) { if (name[0] == '*') { if (name[1] != 0) return false; } else if (name[0] == '#') { if (name[1] != 0) return false; type.CanReturnArc = false; type.CanReturnParser = true; } else return false; } type.FormatIndex = index; for (unsigned i = pos2; i < s.Len();) { int next = s.Find(':', i); if (next < 0) next = s.Len(); UString name = s.Mid(i, next - i); if (name.IsEmpty()) return false; if (!ParseTypeParams(name, type)) return false; i = next + 1; } return true; } bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector &types) { types.Clear(); for (unsigned pos = 0; pos < s.Len();) { int pos2 = s.Find('.', pos); if (pos2 < 0) pos2 = s.Len(); UString name = s.Mid(pos, pos2 - pos); if (name.IsEmpty()) return false; COpenType type; if (!ParseType(codecs, name, type)) return false; types.Add(type); pos = pos2 + 1; } return true; } #endif src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.h000066400000000000000000000221011325366651500221160ustar00rootroot00000000000000// OpenArchive.h #ifndef __OPEN_ARCHIVE_H #define __OPEN_ARCHIVE_H #include "../../../Windows/PropVariant.h" #include "ArchiveOpenCallback.h" #include "LoadCodecs.h" #include "Property.h" HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw(); HRESULT Archive_IsItem_Folder(IInArchive *arc, UInt32 index, bool &result) throw(); HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw(); HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw(); HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &deleted) throw(); /* struct COptionalOpenProperties { UString FormatName; CObjectVector Props; }; */ #ifdef _SFX #define OPEN_PROPS_DECL #else #define OPEN_PROPS_DECL const CObjectVector *props; // #define OPEN_PROPS_DECL , const CObjectVector *props #endif struct COpenSpecFlags { // bool CanReturnFull; bool CanReturnFrontal; bool CanReturnTail; bool CanReturnMid; bool CanReturn_NonStart() const { return CanReturnTail || CanReturnMid; } COpenSpecFlags(): // CanReturnFull(true), CanReturnFrontal(false), CanReturnTail(false), CanReturnMid(false) {} }; struct COpenType { int FormatIndex; COpenSpecFlags SpecForcedType; COpenSpecFlags SpecMainType; COpenSpecFlags SpecWrongExt; COpenSpecFlags SpecUnknownExt; bool Recursive; bool CanReturnArc; bool CanReturnParser; bool EachPos; // bool SkipSfxStub; // bool ExeAsUnknown; bool ZerosTailIsAllowed; bool MaxStartOffset_Defined; UInt64 MaxStartOffset; const COpenSpecFlags &GetSpec(bool isForced, bool isMain, bool isUnknown) const { return isForced ? SpecForcedType : (isMain ? SpecMainType : (isUnknown ? SpecUnknownExt : SpecWrongExt)); } COpenType(): FormatIndex(-1), Recursive(true), CanReturnArc(true), CanReturnParser(false), EachPos(false), // SkipSfxStub(true), // ExeAsUnknown(true), ZerosTailIsAllowed(false), MaxStartOffset_Defined(false), MaxStartOffset(0) { SpecForcedType.CanReturnFrontal = true; SpecForcedType.CanReturnTail = true; SpecForcedType.CanReturnMid = true; SpecMainType.CanReturnFrontal = true; SpecUnknownExt.CanReturnTail = true; // for sfx SpecUnknownExt.CanReturnMid = true; SpecUnknownExt.CanReturnFrontal = true; // for alt streams of sfx with pad // ZerosTailIsAllowed = true; } }; struct COpenOptions { CCodecs *codecs; COpenType openType; const CObjectVector *types; const CIntVector *excludedFormats; IInStream *stream; ISequentialInStream *seqStream; IArchiveOpenCallback *callback; COpenCallbackImp *callbackSpec; OPEN_PROPS_DECL // bool openOnlySpecifiedByExtension, bool stdInMode; UString filePath; COpenOptions(): codecs(NULL), types(NULL), excludedFormats(NULL), stream(NULL), seqStream(NULL), callback(NULL), callbackSpec(NULL), stdInMode(false) {} }; UInt32 GetOpenArcErrorFlags(const NWindows::NCOM::CPropVariant &prop, bool *isDefinedProp = NULL); struct CArcErrorInfo { bool ThereIsTail; bool UnexpecedEnd; bool IgnoreTail; // all are zeros // bool NonZerosTail; bool ErrorFlags_Defined; UInt32 ErrorFlags; UInt32 WarningFlags; int ErrorFormatIndex; // - 1 means no Error. // if FormatIndex == ErrorFormatIndex, the archive is open with offset UInt64 TailSize; /* if CArc is Open OK with some format: - ErrorFormatIndex shows error format index, if extension is incorrect - other variables show message and warnings of archive that is open */ UString ErrorMessage; UString WarningMessage; // call IsArc_After_NonOpen only if Open returns S_FALSE bool IsArc_After_NonOpen() const { return (ErrorFlags_Defined && (ErrorFlags & kpv_ErrorFlags_IsNotArc) == 0); } CArcErrorInfo(): ThereIsTail(false), UnexpecedEnd(false), IgnoreTail(false), // NonZerosTail(false), ErrorFlags_Defined(false), ErrorFlags(0), WarningFlags(0), ErrorFormatIndex(-1), TailSize(0) {} void ClearErrors(); void ClearErrors_Full() { ErrorFormatIndex = -1; ClearErrors(); } bool IsThereErrorOrWarning() const { return ErrorFlags != 0 || WarningFlags != 0 || NeedTailWarning() || UnexpecedEnd || !ErrorMessage.IsEmpty() || !WarningMessage.IsEmpty(); } bool AreThereErrors() const { return ErrorFlags != 0 || UnexpecedEnd; } bool AreThereWarnings() const { return WarningFlags != 0 || NeedTailWarning(); } bool NeedTailWarning() const { return !IgnoreTail && ThereIsTail; } UInt32 GetWarningFlags() const { UInt32 a = WarningFlags; if (NeedTailWarning() && (ErrorFlags & kpv_ErrorFlags_DataAfterEnd) == 0) a |= kpv_ErrorFlags_DataAfterEnd; return a; } UInt32 GetErrorFlags() const { UInt32 a = ErrorFlags; if (UnexpecedEnd) a |= kpv_ErrorFlags_UnexpectedEnd; return a; } }; class CArc { HRESULT PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr &archive); HRESULT CheckZerosTail(const COpenOptions &op, UInt64 offset); HRESULT OpenStream2(const COpenOptions &options); public: CMyComPtr Archive; CMyComPtr InStream; // we use InStream in 2 cases (ArcStreamOffset != 0): // 1) if we use additional cache stream // 2) we reopen sfx archive with CTailInStream CMyComPtr GetRawProps; CMyComPtr GetRootProps; CArcErrorInfo ErrorInfo; // for OK archives CArcErrorInfo NonOpen_ErrorInfo; // ErrorInfo for mainArchive (false OPEN) UString Path; UString filePath; UString DefaultName; int FormatIndex; // - 1 means Parser. int SubfileIndex; FILETIME MTime; bool MTimeDefined; Int64 Offset; // it's offset of start of archive inside stream that is open by Archive Handler UInt64 PhySize; // UInt64 OkPhySize; bool PhySizeDefined; // bool OkPhySize_Defined; UInt64 FileSize; UInt64 AvailPhySize; // PhySize, but it's reduced if exceed end of file // bool offsetDefined; UInt64 ArcStreamOffset; // offset of stream that is open by Archive Handler Int64 GetGlobalOffset() const { return ArcStreamOffset + Offset; } // it's global offset of archive // AString ErrorFlagsText; bool IsParseArc; bool IsTree; bool Ask_Deleted; bool Ask_AltStream; bool Ask_Aux; bool Ask_INode; bool IgnoreSplit; // don't try split handler // void Set_ErrorFlagsText(); CArc(): MTimeDefined(false), IsTree(false), Ask_Deleted(false), Ask_AltStream(false), Ask_Aux(false), Ask_INode(false), IgnoreSplit(false) {} HRESULT ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes); // ~CArc(); HRESULT Close() { InStream.Release(); return Archive->Close(); } // AltStream's name is concatenated with base file name in one string in parts.Back() HRESULT GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const; HRESULT GetItemPath(UInt32 index, UString &result) const; // GetItemPath2 adds [DELETED] dir prefix for deleted items. HRESULT GetItemPath2(UInt32 index, UString &result) const; HRESULT GetItemSize(UInt32 index, UInt64 &size, bool &defined) const; HRESULT GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const; HRESULT IsItemAnti(UInt32 index, bool &result) const { return Archive_GetItemBoolProp(Archive, index, kpidIsAnti, result); } HRESULT OpenStream(const COpenOptions &options); HRESULT OpenStreamOrFile(COpenOptions &options); HRESULT ReOpen(const COpenOptions &options); HRESULT CreateNewTailStream(CMyComPtr &stream); }; struct CArchiveLink { CObjectVector Arcs; UStringVector VolumePaths; UInt64 VolumesSize; bool IsOpen; // int NonOpenErrorFormatIndex; // - 1 means no Error. UString NonOpen_ArcPath; CArcErrorInfo NonOpen_ErrorInfo; // UString ErrorsText; // void Set_ErrorsText(); CArchiveLink(): VolumesSize(0), IsOpen(false) {} void KeepModeForNextOpen(); HRESULT Close(); void Release(); ~CArchiveLink() { Release(); } const CArc *GetArc() const { return &Arcs.Back(); } IInArchive *GetArchive() const { return Arcs.Back().Archive; } IArchiveGetRawProps *GetArchiveGetRawProps() const { return Arcs.Back().GetRawProps; } IArchiveGetRootProps *GetArchiveGetRootProps() const { return Arcs.Back().GetRootProps; } HRESULT Open(COpenOptions &options); HRESULT Open2(COpenOptions &options, IOpenCallbackUI *callbackUI); HRESULT ReOpen(COpenOptions &options); }; bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector &types); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.cpp000066400000000000000000000307301325366651500224330ustar00rootroot00000000000000// PropIDUtils.cpp #include "StdAfx.h" #include "../../../../C/CpuArch.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/PropVariantConv.h" #include "../../PropID.h" #include "PropIDUtils.h" #define Get16(x) GetUi16(x) #define Get32(x) GetUi32(x) using namespace NWindows; static const char g_WinAttribChars[16 + 1] = "RHS8DAdNTsLCOnE_"; /* 0 READONLY 1 HIDDEN 2 SYSTEM 4 DIRECTORY 5 ARCHIVE 6 DEVICE 7 NORMAL 8 TEMPORARY 9 SPARSE_FILE 10 REPARSE_POINT 11 COMPRESSED 12 OFFLINE 13 NOT_CONTENT_INDEXED 14 ENCRYPTED 16 VIRTUAL */ void ConvertWinAttribToString(char *s, UInt32 wa) { for (int i = 0; i < 16; i++) if ((wa & (1 << i)) && i != 7) *s++ = g_WinAttribChars[i]; *s = 0; } static const char kPosixTypes[16] = { '0', 'p', 'c', '3', 'd', '5', 'b', '7', '-', '9', 'l', 'B', 's', 'D', 'E', 'F' }; #define MY_ATTR_CHAR(a, n, c) ((a) & (1 << (n))) ? c : '-'; void ConvertPropertyToShortString(char *dest, const PROPVARIANT &prop, PROPID propID, bool full) throw() { *dest = 0; if (prop.vt == VT_FILETIME) { FILETIME localFileTime; if ((prop.filetime.dwHighDateTime == 0 && prop.filetime.dwLowDateTime == 0) || !::FileTimeToLocalFileTime(&prop.filetime, &localFileTime)) return; ConvertFileTimeToString(localFileTime, dest, true, full); return; } switch (propID) { case kpidCRC: { if (prop.vt != VT_UI4) break; ConvertUInt32ToHex8Digits(prop.ulVal, dest); return; } case kpidAttrib: { if (prop.vt != VT_UI4) break; ConvertWinAttribToString(dest, prop.ulVal); return; } case kpidPosixAttrib: { if (prop.vt != VT_UI4) break; UString res; UInt32 a = prop.ulVal; dest[0] = kPosixTypes[(a >> 12) & 0xF]; for (int i = 6; i >= 0; i -= 3) { dest[7 - i] = MY_ATTR_CHAR(a, i + 2, 'r'); dest[8 - i] = MY_ATTR_CHAR(a, i + 1, 'w'); dest[9 - i] = MY_ATTR_CHAR(a, i + 0, 'x'); } if ((a & 0x800) != 0) dest[3] = ((a & (1 << 6)) ? 's' : 'S'); if ((a & 0x400) != 0) dest[6] = ((a & (1 << 3)) ? 's' : 'S'); if ((a & 0x200) != 0) dest[9] = ((a & (1 << 0)) ? 't' : 'T'); dest[10] = 0; a &= ~(UInt32)0xFFFF; if (a != 0) { dest[10] = ' '; ConvertUInt32ToHex8Digits(a, dest + 11); } return; } case kpidINode: { if (prop.vt != VT_UI8) break; ConvertUInt32ToString((UInt32)(prop.uhVal.QuadPart >> 48), dest); dest += strlen(dest); *dest++ = '-'; UInt64 low = prop.uhVal.QuadPart & (((UInt64)1 << 48) - 1); ConvertUInt64ToString(low, dest); return; } case kpidVa: { UInt64 v = 0; if (ConvertPropVariantToUInt64(prop, v)) { dest[0] = '0'; dest[1] = 'x'; ConvertUInt64ToHex(prop.ulVal, dest + 2); return; } break; } } ConvertPropVariantToShortString(prop, dest); } void ConvertPropertyToString(UString &dest, const PROPVARIANT &prop, PROPID propID, bool full) { if (prop.vt == VT_BSTR) { dest = prop.bstrVal; return; } char temp[64]; ConvertPropertyToShortString(temp, prop, propID, full); int len = MyStringLen(temp); wchar_t *str = dest.GetBuffer(len); for (int i = 0; i < len; i++) str[i] = temp[i]; dest.ReleaseBuffer(len); } static inline char GetHex(Byte value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } #ifndef _SFX static inline void AddHexToString(AString &res, Byte value) { res += GetHex((Byte)(value >> 4)); res += GetHex((Byte)(value & 0xF)); res += ' '; } /* static AString Data_To_Hex(const Byte *data, size_t size) { AString s; for (size_t i = 0; i < size; i++) AddHexToString(s, data[i]); return s; } */ static const char *sidNames[] = { "0", "Dialup", "Network", "Batch", "Interactive", "Logon", // S-1-5-5-X-Y "Service", "Anonymous", "Proxy", "EnterpriseDC", "Self", "AuthenticatedUsers", "RestrictedCode", "TerminalServer", "RemoteInteractiveLogon", "ThisOrganization", "16", "IUserIIS", "LocalSystem", "LocalService", "NetworkService", "Domains" }; struct CSecID2Name { UInt32 n; const char *sz; }; const CSecID2Name sid_32_Names[] = { { 544, "Administrators" }, { 545, "Users" }, { 546, "Guests" }, { 547, "PowerUsers" }, { 548, "AccountOperators" }, { 549, "ServerOperators" }, { 550, "PrintOperators" }, { 551, "BackupOperators" }, { 552, "Replicators" }, { 553, "Backup Operators" }, { 554, "PreWindows2000CompatibleAccess" }, { 555, "RemoteDesktopUsers" }, { 556, "NetworkConfigurationOperators" }, { 557, "IncomingForestTrustBuilders" }, { 558, "PerformanceMonitorUsers" }, { 559, "PerformanceLogUsers" }, { 560, "WindowsAuthorizationAccessGroup" }, { 561, "TerminalServerLicenseServers" }, { 562, "DistributedCOMUsers" }, { 569, "CryptographicOperators" }, { 573, "EventLogReaders" }, { 574, "CertificateServiceDCOMAccess" } }; static const CSecID2Name sid_21_Names[] = { { 500, "Administrator" }, { 501, "Guest" }, { 502, "KRBTGT" }, { 512, "DomainAdmins" }, { 513, "DomainUsers" }, { 515, "DomainComputers" }, { 516, "DomainControllers" }, { 517, "CertPublishers" }, { 518, "SchemaAdmins" }, { 519, "EnterpriseAdmins" }, { 520, "GroupPolicyCreatorOwners" }, { 553, "RASandIASServers" }, { 553, "RASandIASServers" }, { 571, "AllowedRODCPasswordReplicationGroup" }, { 572, "DeniedRODCPasswordReplicationGroup" } }; struct CServicesToName { UInt32 n[5]; const char *sz; }; static const CServicesToName services_to_name[] = { { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" } }; static void ParseSid(AString &s, const Byte *p, UInt32 lim, UInt32 &sidSize) { sidSize = 0; if (lim < 8) { s += "ERROR"; return; } UInt32 rev = p[0]; if (rev != 1) { s += "UNSUPPORTED"; return; } UInt32 num = p[1]; if (8 + num * 4 > lim) { s += "ERROR"; return; } sidSize = 8 + num * 4; UInt32 authority = GetBe32(p + 4); if (p[2] == 0 && p[3] == 0 && authority == 5 && num >= 1) { UInt32 v0 = Get32(p + 8); if (v0 < ARRAY_SIZE(sidNames)) { s += sidNames[v0]; return; } if (v0 == 32 && num == 2) { UInt32 v1 = Get32(p + 12); for (int i = 0; i < ARRAY_SIZE(sid_32_Names); i++) if (sid_32_Names[i].n == v1) { s += sid_32_Names[i].sz; return; } } if (v0 == 21 && num == 5) { UInt32 v4 = Get32(p + 8 + 4 * 4); for (int i = 0; i < ARRAY_SIZE(sid_21_Names); i++) if (sid_21_Names[i].n == v4) { s += sid_21_Names[i].sz; return; } } if (v0 == 80 && num == 6) { for (int i = 0; i < ARRAY_SIZE(services_to_name); i++) { const CServicesToName &sn = services_to_name[i]; int j; for (j = 0; j < 5 && sn.n[j] == Get32(p + 8 + 4 + j * 4); j++); if (j == 5) { s += sn.sz; return; } } } } char sz[16]; s += "S-1-"; if (p[2] == 0 && p[3] == 0) { ConvertUInt32ToString(authority, sz); s += sz; } else { s += "0x"; for (int i = 2; i < 8; i++) AddHexToString(s, p[i]); } for (UInt32 i = 0; i < num; i++) { s += '-'; ConvertUInt32ToString(Get32(p + 8 + i * 4), sz); s += sz; } } static void ParseOwner(AString &s, const Byte *p, UInt32 size, UInt32 pos) { if (pos > size) { s += "ERROR"; return; } UInt32 sidSize = 0; ParseSid(s, p + pos, size - pos, sidSize); } static void AddUInt32ToString(AString &s, UInt32 val) { char sz[16]; ConvertUInt32ToString(val, sz); s += sz; } static void ParseAcl(AString &s, const Byte *p, UInt32 size, const char *strName, UInt32 flags, UInt32 offset) { UInt32 control = Get16(p + 2); if ((flags & control) == 0) return; UInt32 pos = Get32(p + offset); s += ' '; s += strName; if (pos >= size) return; p += pos; size -= pos; if (size < 8) return; if (Get16(p) != 2) // revision return; // UInt32 aclSize = Get16(p + 2); UInt32 num = Get32(p + 4); AddUInt32ToString(s, num); /* if (num >= (1 << 16)) return; if (aclSize > size) return; size = aclSize; size -= 8; p += 8; for (UInt32 i = 0 ; i < num; i++) { if (size <= 8) return; // Byte type = p[0]; // Byte flags = p[1]; // UInt32 aceSize = Get16(p + 2); // UInt32 mask = Get32(p + 4); p += 8; size -= 8; UInt32 sidSize = 0; s += ' '; s += ParseSid(p, size, sidSize); if (sidSize == 0) return; p += sidSize; size -= sidSize; } if (size != 0) s += " ERROR"; */ } #define MY_SE_OWNER_DEFAULTED (0x0001) #define MY_SE_GROUP_DEFAULTED (0x0002) #define MY_SE_DACL_PRESENT (0x0004) #define MY_SE_DACL_DEFAULTED (0x0008) #define MY_SE_SACL_PRESENT (0x0010) #define MY_SE_SACL_DEFAULTED (0x0020) #define MY_SE_DACL_AUTO_INHERIT_REQ (0x0100) #define MY_SE_SACL_AUTO_INHERIT_REQ (0x0200) #define MY_SE_DACL_AUTO_INHERITED (0x0400) #define MY_SE_SACL_AUTO_INHERITED (0x0800) #define MY_SE_DACL_PROTECTED (0x1000) #define MY_SE_SACL_PROTECTED (0x2000) #define MY_SE_RM_CONTROL_VALID (0x4000) #define MY_SE_SELF_RELATIVE (0x8000) void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s) { s.Empty(); if (size < 20 || size > (1 << 18)) { s += "ERROR"; return; } if (Get16(data) != 1) // revision { s += "UNSUPPORTED"; return; } ParseOwner(s, data, size, Get32(data + 4)); s += ' '; ParseOwner(s, data, size, Get32(data + 8)); ParseAcl(s, data, size, "s:", MY_SE_SACL_PRESENT, 12); ParseAcl(s, data, size, "d:", MY_SE_DACL_PRESENT, 16); s += ' '; AddUInt32ToString(s, size); // s += '\n'; // s += Data_To_Hex(data, size); } #ifdef _WIN32 static bool CheckSid(const Byte *data, UInt32 size, UInt32 pos) { if (pos >= size) return false; size -= pos; if (size < 8) return false; UInt32 rev = data[pos]; if (rev != 1) return false; UInt32 num = data[pos + 1]; return (8 + num * 4 <= size); } static bool CheckAcl(const Byte *p, UInt32 size, UInt32 flags, UInt32 offset) { UInt32 control = Get16(p + 2); if ((flags & control) == 0) return true; UInt32 pos = Get32(p + offset); if (pos >= size) return false; p += pos; size -= pos; if (size < 8) return false; UInt32 aclSize = Get16(p + 2); return (aclSize <= size); } bool CheckNtSecure(const Byte *data, UInt32 size) { if (size < 20) return false; if (Get16(data) != 1) // revision return true; // windows function can handle such error, so we allow it if (size > (1 << 18)) return false; if (!CheckSid(data, size, Get32(data + 4))) return false; if (!CheckSid(data, size, Get32(data + 8))) return false; if (!CheckAcl(data, size, MY_SE_SACL_PRESENT, 12)) return false; if (!CheckAcl(data, size, MY_SE_DACL_PRESENT, 16)) return false; return true; } #endif #ifdef _WIN32 bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s) { s.Empty(); NFile::CReparseAttr attr; if (attr.Parse(data, size)) { if (!attr.IsSymLink()) s += L"Junction: "; s += attr.GetPath(); if (!attr.IsOkNamePair()) { s += L" : "; s += attr.PrintName; } return true; } if (size < 8) return false; UInt32 tag = Get32(data); UInt32 len = Get16(data + 4); if (len + 8 > size) return false; if (Get16(data + 6) != 0) // padding return false; char hex[16]; ConvertUInt32ToHex8Digits(tag, hex); s.AddAsciiStr(hex); s += L' '; data += 8; for (UInt32 i = 0; i < len; i++) { Byte b = ((const Byte *)data)[i]; s += (wchar_t)GetHex((Byte)((b >> 4) & 0xF)); s += (wchar_t)GetHex((Byte)(b & 0xF)); } return true; } #endif #endif src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.h000066400000000000000000000012401325366651500220720ustar00rootroot00000000000000// PropIDUtils.h #ifndef __PROPID_UTILS_H #define __PROPID_UTILS_H #include "../../../Common/MyString.h" // provide at least 64 bytes for buffer including zero-end void ConvertPropertyToShortString(char *dest, const PROPVARIANT &propVariant, PROPID propID, bool full = true) throw(); void ConvertPropertyToString(UString &dest, const PROPVARIANT &propVariant, PROPID propID, bool full = true); bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s); void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s); bool CheckNtSecure(const Byte *data, UInt32 size); void ConvertWinAttribToString(char *s, UInt32 wa); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/Property.h000066400000000000000000000002641325366651500215450ustar00rootroot00000000000000// Property.h #ifndef __7Z_PROPERTY_H #define __7Z_PROPERTY_H #include "../../../Common/MyString.h" struct CProperty { UString Name; UString Value; }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.cpp000066400000000000000000000037531325366651500230720ustar00rootroot00000000000000// SetProperties.cpp #include "StdAfx.h" #include "../../../Common/MyCom.h" #include "../../../Common/MyString.h" #include "../../../Common/StringToInt.h" #include "../../../Windows/PropVariant.h" #include "../../Archive/IArchive.h" #include "SetProperties.h" using namespace NWindows; using namespace NCOM; static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop) { const wchar_t *end; UInt64 result = ConvertStringToUInt64(s, &end); if (*end != 0 || s.IsEmpty()) prop = s; else if (result <= (UInt32)0xFFFFFFFF) prop = (UInt32)result; else prop = result; } HRESULT SetProperties(IUnknown *unknown, const CObjectVector &properties) { if (properties.IsEmpty()) return S_OK; CMyComPtr setProperties; unknown->QueryInterface(IID_ISetProperties, (void **)&setProperties); if (!setProperties) return S_OK; UStringVector realNames; CPropVariant *values = new CPropVariant[properties.Size()]; try { unsigned i; for (i = 0; i < properties.Size(); i++) { const CProperty &property = properties[i]; NCOM::CPropVariant propVariant; UString name = property.Name; if (property.Value.IsEmpty()) { if (!name.IsEmpty()) { wchar_t c = name.Back(); if (c == L'-') propVariant = false; else if (c == L'+') propVariant = true; if (propVariant.vt != VT_EMPTY) name.DeleteBack(); } } else ParseNumberString(property.Value, propVariant); realNames.Add(name); values[i] = propVariant; } CRecordVector names; for (i = 0; i < realNames.Size(); i++) names.Add((const wchar_t *)realNames[i]); RINOK(setProperties->SetProperties(&names.Front(), values, names.Size())); } catch(...) { delete []values; throw; } delete []values; return S_OK; } src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.h000066400000000000000000000003101325366651500225210ustar00rootroot00000000000000// SetProperties.h #ifndef __SETPROPERTIES_H #define __SETPROPERTIES_H #include "Property.h" HRESULT SetProperties(IUnknown *unknown, const CObjectVector &properties); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.cpp000066400000000000000000000011531325366651500222220ustar00rootroot00000000000000// SortUtils.cpp #include "StdAfx.h" #include "../../../Common/Wildcard.h" #include "SortUtils.h" static int CompareStrings(const unsigned *p1, const unsigned *p2, void *param) { const UStringVector &strings = *(const UStringVector *)param; return CompareFileNames(strings[*p1], strings[*p2]); } void SortFileNames(const UStringVector &strings, CUIntVector &indices) { unsigned numItems = strings.Size(); indices.ClearAndSetSize(numItems); unsigned *vals = &indices[0]; for (unsigned i = 0; i < numItems; i++) vals[i] = i; indices.Sort(CompareStrings, (void *)&strings); } src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.h000066400000000000000000000002761325366651500216740ustar00rootroot00000000000000// SortUtils.h #ifndef __SORT_UTLS_H #define __SORT_UTLS_H #include "../../../Common/MyString.h" void SortFileNames(const UStringVector &strings, CUIntVector &indices); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.cpp000066400000000000000000000004611325366651500221430ustar00rootroot00000000000000// TempFiles.cpp #include "StdAfx.h" #include "../../../Windows/FileDir.h" #include "TempFiles.h" using namespace NWindows; using namespace NFile; void CTempFiles::Clear() { while (!Paths.IsEmpty()) { NDir::DeleteFileAlways(Paths.Back()); Paths.DeleteBack(); } } src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.h000066400000000000000000000003401325366651500216040ustar00rootroot00000000000000// TempFiles.h #ifndef __TEMP_FILES_H #define __TEMP_FILES_H #include "../../../Common/MyString.h" class CTempFiles { void Clear(); public: FStringVector Paths; ~CTempFiles() { Clear(); } }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/Update.cpp000066400000000000000000001163501325366651500215020ustar00rootroot00000000000000// Update.cpp #include "StdAfx.h" #include "Update.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/DLL.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #include "../../../Windows/TimeUtils.h" #include "../../Common/FileStreams.h" #include "../../Common/LimitedStreams.h" #include "../../Compress/CopyCoder.h" #include "../Common/DirItem.h" #include "../Common/EnumDirItems.h" #include "../Common/OpenArchive.h" #include "../Common/UpdateProduce.h" #include "EnumDirItems.h" #include "SetProperties.h" #include "TempFiles.h" #include "UpdateCallback.h" static const char *kUpdateIsNotSupoorted = "update operations are not supported for this archive"; using namespace NWindows; using namespace NCOM; using namespace NFile; using namespace NDir; using namespace NName; static CFSTR kTempFolderPrefix = FTEXT("7zE"); #ifdef ENV_UNIX FString GetModuleDirPrefix() { FString s; const char *p7zip_home_dir = getenv("P7ZIP_HOME_DIR"); if (p7zip_home_dir) { return MultiByteToUnicodeString(p7zip_home_dir,CP_ACP); } return FTEXT(".") FSTRING_PATH_SEPARATOR; } #endif static bool DeleteEmptyFolderAndEmptySubFolders(const FString &path) { NFind::CFileInfo fileInfo; FString pathPrefix = path + FCHAR_PATH_SEPARATOR; { NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK); while (enumerator.Next(fileInfo)) { if (fileInfo.IsDir()) if (!DeleteEmptyFolderAndEmptySubFolders(pathPrefix + fileInfo.Name)) return false; } } /* // we don't need clear read-only for folders if (!MySetFileAttributes(path, 0)) return false; */ return RemoveDir(path); } using namespace NUpdateArchive; class COutMultiVolStream: public IOutStream, public CMyUnknownImp { unsigned _streamIndex; // required stream UInt64 _offsetPos; // offset from start of _streamIndex index UInt64 _absPos; UInt64 _length; struct CAltStreamInfo { COutFileStream *StreamSpec; CMyComPtr Stream; FString Name; UInt64 Pos; UInt64 RealSize; }; CObjectVector Streams; public: // CMyComPtr VolumeCallback; CRecordVector Sizes; FString Prefix; CTempFiles *TempFiles; void Init() { _streamIndex = 0; _offsetPos = 0; _absPos = 0; _length = 0; } bool SetMTime(const FILETIME *mTime); HRESULT Close(); MY_UNKNOWN_IMP1(IOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(SetSize)(UInt64 newSize); }; // static NSynchronization::CCriticalSection g_TempPathsCS; HRESULT COutMultiVolStream::Close() { HRESULT res = S_OK; FOR_VECTOR (i, Streams) { COutFileStream *s = Streams[i].StreamSpec; if (s) { HRESULT res2 = s->Close(); if (res2 != S_OK) res = res2; } } return res; } bool COutMultiVolStream::SetMTime(const FILETIME *mTime) { bool res = true; FOR_VECTOR (i, Streams) { COutFileStream *s = Streams[i].StreamSpec; if (s) if (!s->SetMTime(mTime)) res = false; } return res; } STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { if (_streamIndex >= Streams.Size()) { CAltStreamInfo altStream; FChar temp[16]; ConvertUInt32ToString(_streamIndex + 1, temp); FString res = temp; while (res.Len() < 3) res = FString(FTEXT('0')) + res; FString name = Prefix + res; altStream.StreamSpec = new COutFileStream; altStream.Stream = altStream.StreamSpec; if (!altStream.StreamSpec->Create(name, false)) return ::GetLastError(); { // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS); TempFiles->Paths.Add(name); } altStream.Pos = 0; altStream.RealSize = 0; altStream.Name = name; Streams.Add(altStream); continue; } CAltStreamInfo &altStream = Streams[_streamIndex]; unsigned index = _streamIndex; if (index >= Sizes.Size()) index = Sizes.Size() - 1; UInt64 volSize = Sizes[index]; if (_offsetPos >= volSize) { _offsetPos -= volSize; _streamIndex++; continue; } if (_offsetPos != altStream.Pos) { // CMyComPtr outStream; // RINOK(altStream.Stream.QueryInterface(IID_IOutStream, &outStream)); RINOK(altStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); altStream.Pos = _offsetPos; } UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - altStream.Pos); UInt32 realProcessed; RINOK(altStream.Stream->Write(data, curSize, &realProcessed)); data = (void *)((Byte *)data + realProcessed); size -= realProcessed; altStream.Pos += realProcessed; _offsetPos += realProcessed; _absPos += realProcessed; if (_absPos > _length) _length = _absPos; if (_offsetPos > altStream.RealSize) altStream.RealSize = _offsetPos; if (processedSize != NULL) *processedSize += realProcessed; if (altStream.Pos == volSize) { _streamIndex++; _offsetPos = 0; } if (realProcessed == 0 && curSize != 0) return E_FAIL; break; } return S_OK; } STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; switch (seekOrigin) { case STREAM_SEEK_SET: _absPos = offset; break; case STREAM_SEEK_CUR: _absPos += offset; break; case STREAM_SEEK_END: _absPos = _length + offset; break; } _offsetPos = _absPos; if (newPosition != NULL) *newPosition = _absPos; _streamIndex = 0; return S_OK; } STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize) { if (newSize < 0) return E_INVALIDARG; unsigned i = 0; while (i < Streams.Size()) { CAltStreamInfo &altStream = Streams[i++]; if ((UInt64)newSize < altStream.RealSize) { RINOK(altStream.Stream->SetSize(newSize)); altStream.RealSize = newSize; break; } newSize -= altStream.RealSize; } while (i < Streams.Size()) { { CAltStreamInfo &altStream = Streams.Back(); altStream.Stream.Release(); DeleteFileAlways(altStream.Name); } Streams.DeleteBack(); } _offsetPos = _absPos; _streamIndex = 0; _length = newSize; return S_OK; } void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode) { OriginalPath = path; SplitPathToParts_2(path, Prefix, Name); if (mode == k_ArcNameMode_Add) return; if (mode == k_ArcNameMode_Exact) { BaseExtension.Empty(); return; } int dotPos = Name.ReverseFind(L'.'); if (dotPos < 0) return; if ((unsigned)dotPos == Name.Len() - 1) { Name.DeleteBack(); BaseExtension.Empty(); return; } const UString ext = Name.Ptr(dotPos + 1); if (BaseExtension.IsEqualToNoCase(ext)) { BaseExtension = ext; Name.DeleteFrom(dotPos); } else BaseExtension.Empty(); } UString CArchivePath::GetFinalPath() const { UString path = GetPathWithoutExt(); if (!BaseExtension.IsEmpty()) path += UString(L'.') + BaseExtension; return path; } UString CArchivePath::GetFinalVolPath() const { UString path = GetPathWithoutExt(); if (!BaseExtension.IsEmpty()) path += UString(L'.') + VolExtension; return path; } FString CArchivePath::GetTempPath() const { FString path = TempPrefix + us2fs(Name); if (!BaseExtension.IsEmpty()) path += FString(FTEXT('.')) + us2fs(BaseExtension); path += FTEXT(".tmp"); path += TempPostfix; return path; } static const wchar_t *kDefaultArcType = L"7z"; static const wchar_t *kDefaultArcExt = L"7z"; static const wchar_t *kSFXExtension = #ifdef _WIN32 L"exe"; #else L""; #endif bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs, const CObjectVector &types, const UString &arcPath) { if (types.Size() > 1) return false; // int arcTypeIndex = -1; if (types.Size() != 0) { MethodMode.Type = types[0]; MethodMode.Type_Defined = true; } if (MethodMode.Type.FormatIndex < 0) { // MethodMode.Type = -1; MethodMode.Type = COpenType(); if (ArcNameMode != k_ArcNameMode_Add) { MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath); if (MethodMode.Type.FormatIndex >= 0) MethodMode.Type_Defined = true; } } return true; } bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath) { UString typeExt; int formatIndex = MethodMode.Type.FormatIndex; if (formatIndex < 0) { typeExt = kDefaultArcExt; } else { const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; if (!arcInfo.UpdateEnabled) return false; typeExt = arcInfo.GetMainExt(); } UString ext = typeExt; if (SfxMode) ext = kSFXExtension; ArchivePath.BaseExtension = ext; ArchivePath.VolExtension = typeExt; ArchivePath.ParseFromPath(arcPath, ArcNameMode); FOR_VECTOR (i, Commands) { CUpdateArchiveCommand &uc = Commands[i]; uc.ArchivePath.BaseExtension = ext; uc.ArchivePath.VolExtension = typeExt; uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode); } return true; } /* struct CUpdateProduceCallbackImp: public IUpdateProduceCallback { const CObjectVector *_arcItems; IUpdateCallbackUI *_callback; CUpdateProduceCallbackImp(const CObjectVector *a, IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {} virtual HRESULT ShowDeleteFile(int arcIndex); }; HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex) { return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name); } */ bool CRenamePair::Prepare() { if (RecursedType != NRecursedType::kNonRecursed) return false; if (!WildcardParsing) return true; return !DoesNameContainWildcard(OldName); } extern bool g_CaseSensitive; static int CompareTwoNames(const wchar_t *s1, const wchar_t *s2) { for (int i = 0;; i++) { wchar_t c1 = s1[i]; wchar_t c2 = s2[i]; if (c1 == 0 || c2 == 0) return i; if (c1 == c2) continue; if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2))) continue; if (IsCharDirLimiter(c1) && IsCharDirLimiter(c2)) continue; return i; } } bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const { int num = CompareTwoNames(OldName, src); if (OldName[num] == 0) { if (src[num] != 0 && !IsCharDirLimiter(src[num]) && num != 0 && !IsCharDirLimiter(src[num - 1])) return false; } else { // OldName[num] != 0 // OldName = "1\1a.txt" // src = "1" if (!isFolder || src[num] != 0 || !IsCharDirLimiter(OldName[num]) || OldName[num + 1] != 0) return false; } dest = NewName + src.Ptr(num); return true; } static int GetReverseSlashPos(const UString &name) { int slashPos = name.ReverseFind(L'/'); #ifdef _WIN32 int slash1Pos = name.ReverseFind(L'\\'); slashPos = MyMax(slashPos, slash1Pos); #endif return slashPos; } static HRESULT Compress( const CUpdateOptions &options, CCodecs *codecs, const CActionSet &actionSet, const CArc *arc, CArchivePath &archivePath, const CObjectVector &arcItems, Byte *processedItemsStatuses, const CDirItems &dirItems, const CDirItem *parentDirItem, CTempFiles &tempFiles, CUpdateErrorInfo &errorInfo, IUpdateCallbackUI *callback) { CMyComPtr outArchive; int formatIndex = options.MethodMode.Type.FormatIndex; if (arc) { formatIndex = arc->FormatIndex; if (formatIndex < 0) return E_NOTIMPL; CMyComPtr archive2 = arc->Archive; HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive); if (result != S_OK) throw kUpdateIsNotSupoorted; } else { RINOK(codecs->CreateOutArchive(formatIndex, outArchive)); #ifdef EXTERNAL_CODECS { CMyComPtr setCompressCodecsInfo; outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); } } #endif } if (outArchive == 0) throw kUpdateIsNotSupoorted; NFileTimeType::EEnum fileTimeType; UInt32 value; RINOK(outArchive->GetFileTimeType(&value)); switch (value) { case NFileTimeType::kWindows: case NFileTimeType::kUnix: case NFileTimeType::kDOS: fileTimeType = (NFileTimeType::EEnum)value; break; default: return E_FAIL; } { const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; if (options.AltStreams.Val && !arcInfo.Flags_AltStreams()) return E_NOTIMPL; if (options.NtSecurity.Val && !arcInfo.Flags_NtSecure()) return E_NOTIMPL; } CRecordVector updatePairs2; UStringVector newNames; if (options.RenamePairs.Size() != 0) { FOR_VECTOR (i, arcItems) { const CArcItem &ai = arcItems[i]; bool needRename = false; UString dest; if (ai.Censored) { FOR_VECTOR (j, options.RenamePairs) { const CRenamePair &rp = options.RenamePairs[j]; if (rp.GetNewPath(ai.IsDir, ai.Name, dest)) { needRename = true; break; } if (ai.IsAltStream) { int colonPos = ai.Name.ReverseFind(':'); int slashPosPos = GetReverseSlashPos(ai.Name); if (colonPos > slashPosPos) { UString mainName = ai.Name.Left(colonPos); /* actually we must improve that code to support cases with folder renaming like: rn arc dir1\ dir2\ */ if (rp.GetNewPath(false, mainName, dest)) { needRename = true; dest += ':'; dest += ai.Name.Ptr(colonPos + 1); break; } } } } } CUpdatePair2 up2; up2.SetAs_NoChangeArcItem(ai.IndexInServer); if (needRename) { up2.NewProps = true; RINOK(arc->IsItemAnti(i, up2.IsAnti)); up2.NewNameIndex = newNames.Add(dest); } updatePairs2.Add(up2); } } else { CRecordVector updatePairs; GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!! // CUpdateProduceCallbackImp upCallback(&arcItems, callback); UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */); } UInt32 numFiles = 0; FOR_VECTOR (i, updatePairs2) if (updatePairs2[i].NewData) numFiles++; RINOK(callback->SetNumFiles(numFiles)); CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr updateCallback(updateCallbackSpec); updateCallbackSpec->ShareForWrite = options.OpenShareForWrite; updateCallbackSpec->StdInMode = options.StdInMode; updateCallbackSpec->Callback = callback; if (arc) { // we set Archive to allow to transfer GetProperty requests back to DLL. updateCallbackSpec->Archive = arc->Archive; updateCallbackSpec->GetRawProps = arc->GetRawProps; updateCallbackSpec->GetRootProps = arc->GetRootProps; } updateCallbackSpec->DirItems = &dirItems; updateCallbackSpec->ParentDirItem = parentDirItem; updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val; updateCallbackSpec->StoreHardLinks = options.HardLinks.Val; updateCallbackSpec->StoreSymLinks = options.SymLinks.Val; updateCallbackSpec->ArcItems = &arcItems; updateCallbackSpec->UpdatePairs = &updatePairs2; updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses; if (options.RenamePairs.Size() != 0) updateCallbackSpec->NewNames = &newNames; CMyComPtr outSeekStream; CMyComPtr outStream; if (!options.StdOutMode) { FString dirPrefix; if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix)) throw 1417161; CreateComplexDir(dirPrefix); } COutFileStream *outStreamSpec = NULL; COutMultiVolStream *volStreamSpec = NULL; if (options.VolumesSizes.Size() == 0) { if (options.StdOutMode) outStream = new CStdOutFileStream; else { outStreamSpec = new COutFileStream; outSeekStream = outStreamSpec; outStream = outSeekStream; bool isOK = false; FString realPath; for (int i = 0; i < (1 << 16); i++) { if (archivePath.Temp) { if (i > 0) { FChar s[16]; ConvertUInt32ToString(i, s); archivePath.TempPostfix = s; } realPath = archivePath.GetTempPath(); } else realPath = us2fs(archivePath.GetFinalPath()); if (outStreamSpec->Create(realPath, false)) { tempFiles.Paths.Add(realPath); isOK = true; break; } if (::GetLastError() != ERROR_FILE_EXISTS) break; if (!archivePath.Temp) break; } if (!isOK) { errorInfo.SystemError = ::GetLastError(); errorInfo.FileName = realPath; errorInfo.Message = L"7-Zip cannot open file"; return E_FAIL; } } } else { if (options.StdOutMode) return E_FAIL; if (arc && arc->GetGlobalOffset() > 0) return E_NOTIMPL; volStreamSpec = new COutMultiVolStream; outSeekStream = volStreamSpec; outStream = outSeekStream; volStreamSpec->Sizes = options.VolumesSizes; volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath() + L"."); volStreamSpec->TempFiles = &tempFiles; volStreamSpec->Init(); /* updateCallbackSpec->VolumesSizes = volumesSizes; updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name; if (!archivePath.VolExtension.IsEmpty()) updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension; */ } RINOK(SetProperties(outArchive, options.MethodMode.Properties)); if (options.SfxMode) { CInFileStream *sfxStreamSpec = new CInFileStream; CMyComPtr sfxStream(sfxStreamSpec); if (!sfxStreamSpec->Open(options.SfxModule)) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot open SFX module"; errorInfo.FileName = options.SfxModule; return E_FAIL; } CMyComPtr sfxOutStream; COutFileStream *outStreamSpec = NULL; if (options.VolumesSizes.Size() == 0) sfxOutStream = outStream; else { outStreamSpec = new COutFileStream; sfxOutStream = outStreamSpec; FString realPath = us2fs(archivePath.GetFinalPath()); if (!outStreamSpec->Create(realPath, false)) { errorInfo.SystemError = ::GetLastError(); errorInfo.FileName = realPath; errorInfo.Message = L"7-Zip cannot open file"; return E_FAIL; } } RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL)); if (outStreamSpec) { RINOK(outStreamSpec->Close()); } } CMyComPtr tailStream; if (options.SfxMode || !arc || arc->ArcStreamOffset == 0) tailStream = outStream; else { // Int64 globalOffset = arc->GetGlobalOffset(); RINOK(arc->InStream->Seek(0, STREAM_SEEK_SET, NULL)); RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL)); if (options.StdOutMode) tailStream = outStream; else { CTailOutStream *tailStreamSpec = new CTailOutStream; tailStream = tailStreamSpec; tailStreamSpec->Stream = outSeekStream; tailStreamSpec->Offset = arc->ArcStreamOffset; tailStreamSpec->Init(); } } HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback); callback->Finilize(); RINOK(result); if (options.SetArcMTime) { FILETIME ft; ft.dwLowDateTime = 0; ft.dwHighDateTime = 0; FOR_VECTOR (i, updatePairs2) { CUpdatePair2 &pair2 = updatePairs2[i]; const FILETIME *ft2 = NULL; if (pair2.NewProps && pair2.DirIndex >= 0) ft2 = &dirItems.Items[pair2.DirIndex].MTime; else if (pair2.UseArcProps && pair2.ArcIndex >= 0) ft2 = &arcItems[pair2.ArcIndex].MTime; if (ft2) { if (::CompareFileTime(&ft, ft2) < 0) ft = *ft2; } } if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0) { if (outStreamSpec) outStreamSpec->SetMTime(&ft); else if (volStreamSpec) volStreamSpec->SetMTime(&ft);; } } if (outStreamSpec) result = outStreamSpec->Close(); else if (volStreamSpec) result = volStreamSpec->Close(); return result; } static HRESULT EnumerateInArchiveItems( // bool storeStreamsMode, const NWildcard::CCensor &censor, const CArc &arc, CObjectVector &arcItems) { arcItems.Clear(); UInt32 numItems; IInArchive *archive = arc.Archive; RINOK(archive->GetNumberOfItems(&numItems)); arcItems.ClearAndReserve(numItems); for (UInt32 i = 0; i < numItems; i++) { CArcItem ai; RINOK(arc.GetItemPath(i, ai.Name)); RINOK(Archive_IsItem_Folder(archive, i, ai.IsDir)); RINOK(Archive_IsItem_AltStream(archive, i, ai.IsAltStream)); /* if (!storeStreamsMode && ai.IsAltStream) continue; */ ai.Censored = censor.CheckPath(ai.IsAltStream, ai.Name, !ai.IsDir); RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined)); RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined)); { CPropVariant prop; RINOK(archive->GetProperty(i, kpidTimeType, &prop)); if (prop.vt == VT_UI4) { ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal; switch (ai.TimeType) { case NFileTimeType::kWindows: case NFileTimeType::kUnix: case NFileTimeType::kDOS: break; default: return E_FAIL; } } } ai.IndexInServer = i; arcItems.AddInReserved(ai); } return S_OK; } struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback { IUpdateCallbackUI2 *Callback; HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) { return Callback->ScanProgress(numFolders, numFiles, totalSize, path, isDir); } }; #if defined(_WIN32) && !defined(UNDER_CE) #include #endif struct CRefSortPair { int Len; int Index; }; #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } static int CompareRefSortPair(const CRefSortPair *a1, const CRefSortPair *a2, void *) { RINOZ(-MyCompare(a1->Len, a2->Len)); return MyCompare(a1->Index, a2->Index); } static int GetNumSlashes(const FChar *s) { for (int numSlashes = 0;;) { FChar c = *s++; if (c == 0) return numSlashes; if ( #ifdef _WIN32 c == FTEXT('\\') || #endif c == FTEXT('/')) numSlashes++; } } #ifdef _WIN32 void ConvertToLongNames(NWildcard::CCensor &censor); #endif HRESULT UpdateArchive( CCodecs *codecs, const CObjectVector &types, const UString &cmdArcPath2, NWildcard::CCensor &censor, CUpdateOptions &options, CUpdateErrorInfo &errorInfo, IOpenCallbackUI *openCallback, IUpdateCallbackUI2 *callback, bool needSetPath) { if (options.StdOutMode && options.EMailMode) return E_FAIL; if (types.Size() > 1) return E_NOTIMPL; bool renameMode = !options.RenamePairs.IsEmpty(); if (renameMode) { if (options.Commands.Size() != 1) return E_FAIL; } if (options.DeleteAfterCompressing) { if (options.Commands.Size() != 1) return E_NOTIMPL; const CActionSet &as = options.Commands[0].ActionSet; for (int i = 2; i < NPairState::kNumValues; i++) if (as.StateActions[i] != NPairAction::kCompress) return E_NOTIMPL; } censor.AddPathsToCensor(options.PathMode); #ifdef _WIN32 ConvertToLongNames(censor); #endif censor.ExtendExclude(); if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */)) return E_NOTIMPL; if (options.SfxMode) { CProperty property; property.Name = L"rsfx"; property.Value = L"on"; options.MethodMode.Properties.Add(property); if (options.SfxModule.IsEmpty()) { errorInfo.Message = L"SFX file is not specified"; return E_FAIL; } bool found = false; if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0) { const FString fullName = ::GetModuleDirPrefix() + options.SfxModule; if (NFind::DoesFileExist(fullName)) { options.SfxModule = fullName; found = true; } } if (!found) { if (!NFind::DoesFileExist(options.SfxModule)) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot find specified SFX module"; errorInfo.FileName = options.SfxModule; return E_FAIL; } } } CArchiveLink arcLink; if (needSetPath) { if (!options.InitFormatIndex(codecs, types, cmdArcPath2) || !options.SetArcPath(codecs, cmdArcPath2)) return E_NOTIMPL; } UString arcPath = options.ArchivePath.GetFinalPath(); if (cmdArcPath2.IsEmpty()) { if (options.MethodMode.Type.FormatIndex < 0) throw "type of archive is not specified"; } else { NFind::CFileInfo fi; if (!fi.Find(us2fs(arcPath))) { if (renameMode) throw "can't find archive";; if (options.MethodMode.Type.FormatIndex < 0) { if (!options.SetArcPath(codecs, cmdArcPath2)) return E_NOTIMPL; } } else { if (fi.IsDir()) throw "there is no such archive"; if (fi.IsDevice) return E_NOTIMPL; if (options.VolumesSizes.Size() > 0) return E_NOTIMPL; CObjectVector types; // change it. if (options.MethodMode.Type_Defined) types.Add(options.MethodMode.Type); // We need to set Properties to open archive only in some cases (WIM archives). CIntVector excl; COpenOptions op; #ifndef _SFX op.props = &options.MethodMode.Properties; #endif op.codecs = codecs; op.types = &types; op.excludedFormats = ! op.stdInMode = false; op.stream = NULL; op.filePath = arcPath; HRESULT result = arcLink.Open2(op, openCallback); if (result == E_ABORT) return result; const wchar_t *errorArcType = NULL; if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex > 0) errorArcType = codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name; RINOK(callback->OpenResult(arcPath, result, errorArcType)); /* if (result == S_FALSE) return E_FAIL; */ RINOK(result); if (arcLink.VolumePaths.Size() > 1) { errorInfo.SystemError = (DWORD)E_NOTIMPL; errorInfo.Message = L"Updating for multivolume archives is not implemented"; return E_NOTIMPL; } CArc &arc = arcLink.Arcs.Back(); arc.MTimeDefined = !fi.IsDevice; arc.MTime = fi.MTime; if (arc.ErrorInfo.ThereIsTail) { errorInfo.SystemError = (DWORD)E_NOTIMPL; errorInfo.Message = L"There is some data block after the end of the archive"; return E_NOTIMPL; } if (options.MethodMode.Type.FormatIndex < 0) { options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex; if (!options.SetArcPath(codecs, cmdArcPath2)) return E_NOTIMPL; } } } if (options.MethodMode.Type.FormatIndex < 0) { options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArcType); if (options.MethodMode.Type.FormatIndex < 0) return E_NOTIMPL; } bool thereIsInArchive = arcLink.IsOpen; if (!thereIsInArchive && renameMode) return E_FAIL; CDirItems dirItems; CDirItem parentDirItem; CDirItem *parentDirItem_Ptr = NULL; /* FStringVector requestedPaths; FStringVector *requestedPaths_Ptr = NULL; if (options.DeleteAfterCompressing) requestedPaths_Ptr = &requestedPaths; */ if (options.StdInMode) { CDirItem di; di.Name = options.StdInFileName; di.Size = (UInt64)(Int64)-1; di.Attrib = 0; NTime::GetCurUtcFileTime(di.MTime); di.CTime = di.ATime = di.MTime; dirItems.Items.Add(di); } else { bool needScanning = false; if (!renameMode) FOR_VECTOR (i, options.Commands) if (options.Commands[i].ActionSet.NeedScanning()) needScanning = true; if (needScanning) { CEnumDirItemUpdateCallback enumCallback; enumCallback.Callback = callback; RINOK(callback->StartScanning()); dirItems.SymLinks = options.SymLinks.Val; #if defined(_WIN32) && !defined(UNDER_CE) dirItems.ReadSecure = options.NtSecurity.Val; #endif dirItems.ScanAltStreams = options.AltStreams.Val; HRESULT res = EnumerateItems(censor, options.PathMode, options.AddPathPrefix, dirItems, &enumCallback); FOR_VECTOR (i, dirItems.ErrorPaths) { RINOK(callback->CanNotFindError(fs2us(dirItems.ErrorPaths[i]), dirItems.ErrorCodes[i])); } if (res != S_OK) { if (res != E_ABORT) errorInfo.Message = L"Scanning error"; return res; } RINOK(callback->FinishScanning()); if (censor.Pairs.Size() == 1) { NFind::CFileInfo fi; FString prefix = us2fs(censor.Pairs[0].Prefix) + FTEXT("."); // UString prefix = censor.Pairs[0].Prefix; /* if (prefix.Back() == WCHAR_PATH_SEPARATOR) { prefix.DeleteBack(); } */ if (fi.Find(prefix)) if (fi.IsDir()) { parentDirItem.Size = fi.Size; parentDirItem.CTime = fi.CTime; parentDirItem.ATime = fi.ATime; parentDirItem.MTime = fi.MTime; parentDirItem.Attrib = fi.Attrib; parentDirItem_Ptr = &parentDirItem; int secureIndex = -1; #if defined(_WIN32) && !defined(UNDER_CE) if (options.NtSecurity.Val) dirItems.AddSecurityItem(prefix, secureIndex); #endif parentDirItem.SecureIndex = secureIndex; parentDirItem_Ptr = &parentDirItem; } } } } FString tempDirPrefix; bool usesTempDir = false; #ifdef _WIN32 CTempDir tempDirectory; if (options.EMailMode && options.EMailRemoveAfter) { tempDirectory.Create(kTempFolderPrefix); tempDirPrefix = tempDirectory.GetPath(); NormalizeDirPathPrefix(tempDirPrefix); usesTempDir = true; } #endif CTempFiles tempFiles; bool createTempFile = false; if (!options.StdOutMode && options.UpdateArchiveItself) { CArchivePath &ap = options.Commands[0].ArchivePath; ap = options.ArchivePath; // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty()) if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0) { createTempFile = true; ap.Temp = true; if (!options.WorkingDir.IsEmpty()) ap.TempPrefix = options.WorkingDir; else ap.TempPrefix = us2fs(ap.Prefix); NormalizeDirPathPrefix(ap.TempPrefix); } } unsigned i; for (i = 0; i < options.Commands.Size(); i++) { CArchivePath &ap = options.Commands[i].ArchivePath; if (usesTempDir) { // Check it ap.Prefix = fs2us(tempDirPrefix); // ap.Temp = true; // ap.TempPrefix = tempDirPrefix; } if (!options.StdOutMode && (i > 0 || !createTempFile)) { const FString path = us2fs(ap.GetFinalPath()); if (NFind::DoesFileOrDirExist(path)) { errorInfo.SystemError = 0; errorInfo.Message = L"The file already exists"; errorInfo.FileName = path; return E_FAIL; } } } CObjectVector arcItems; if (thereIsInArchive) { RINOK(EnumerateInArchiveItems( // options.StoreAltStreams, censor, arcLink.Arcs.Back(), arcItems)); } /* FStringVector processedFilePaths; FStringVector *processedFilePaths_Ptr = NULL; if (options.DeleteAfterCompressing) processedFilePaths_Ptr = &processedFilePaths; */ CByteBuffer processedItems; if (options.DeleteAfterCompressing) { unsigned num = dirItems.Items.Size(); processedItems.Alloc(num); for (i = 0; i < num; i++) processedItems[i] = 0; } for (i = 0; i < options.Commands.Size(); i++) { const CArc *arc = thereIsInArchive ? arcLink.GetArc() : 0; // IInArchiveExtra *archiveExtra = thereIsInArchive ? arcLink.GetArchiveExtra() : 0; // IArchiveGetRootProps *archiveGetRootProps = thereIsInArchive ? arcLink.GetArchiveGetRootProps() : 0; CUpdateArchiveCommand &command = options.Commands[i]; UString name; bool isUpdating; if (options.StdOutMode) { name = L"stdout"; isUpdating = arc != 0; } else { name = command.ArchivePath.GetFinalPath(); isUpdating = (i == 0 && options.UpdateArchiveItself && arc != 0); } RINOK(callback->StartArchive(name, isUpdating)) RINOK(Compress(options, codecs, command.ActionSet, arc, command.ArchivePath, arcItems, options.DeleteAfterCompressing ? (Byte *)processedItems : NULL, dirItems, parentDirItem_Ptr, tempFiles, errorInfo, callback)); RINOK(callback->FinishArchive()); } if (thereIsInArchive) { RINOK(arcLink.Close()); arcLink.Release(); } tempFiles.Paths.Clear(); if (createTempFile) { try { CArchivePath &ap = options.Commands[0].ArchivePath; const FString &tempPath = ap.GetTempPath(); if (thereIsInArchive) if (!DeleteFileAlways(us2fs(arcPath))) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot delete the file"; errorInfo.FileName = us2fs(arcPath); return E_FAIL; } if (!MyMoveFile(tempPath, us2fs(arcPath))) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot move the file"; errorInfo.FileName = tempPath; errorInfo.FileName2 = us2fs(arcPath); return E_FAIL; } } catch(...) { throw; } } #if defined(_WIN32) && !defined(UNDER_CE) if (options.EMailMode) { NDLL::CLibrary mapiLib; if (!mapiLib.Load(FTEXT("Mapi32.dll"))) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot load Mapi32.dll"; return E_FAIL; } /* LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments"); if (fnSend == 0) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function"; return E_FAIL; } */ LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)mapiLib.GetProc("MAPISendMail"); if (sendMail == 0) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot find MAPISendMail function"; return E_FAIL; } FStringVector fullPaths; unsigned i; for (i = 0; i < options.Commands.Size(); i++) { CArchivePath &ap = options.Commands[i].ArchivePath; FString arcPath; if (!MyGetFullPathName(us2fs(ap.GetFinalPath()), arcPath)) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"GetFullPathName error"; return E_FAIL; } fullPaths.Add(arcPath); } CCurrentDirRestorer curDirRestorer; for (i = 0; i < fullPaths.Size(); i++) { UString arcPath = fs2us(fullPaths[i]); UString fileName = ExtractFileNameFromPath(arcPath); AString path = GetAnsiString(arcPath); AString name = GetAnsiString(fileName); // Warning!!! MAPISendDocuments function changes Current directory // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); MapiFileDesc f; memset(&f, 0, sizeof(f)); f.nPosition = 0xFFFFFFFF; f.lpszPathName = (char *)(const char *)path; f.lpszFileName = (char *)(const char *)name; MapiMessage m; memset(&m, 0, sizeof(m)); m.nFileCount = 1; m.lpFiles = &f; const AString addr = GetAnsiString(options.EMailAddress); MapiRecipDesc rec; if (!addr.IsEmpty()) { memset(&rec, 0, sizeof(rec)); rec.ulRecipClass = MAPI_TO; rec.lpszAddress = (char *)(const char *)addr; m.nRecipCount = 1; m.lpRecips = &rec; } sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0); } } #endif if (options.DeleteAfterCompressing) { CRecordVector pairs; FStringVector foldersNames; for (i = 0; i < dirItems.Items.Size(); i++) { const CDirItem &dirItem = dirItems.Items[i]; FString phyPath = us2fs(dirItems.GetPhyPath(i)); if (dirItem.IsDir()) { CRefSortPair pair; pair.Index = i; pair.Len = GetNumSlashes(phyPath); pairs.Add(pair); } else { if (processedItems[i] != 0 || dirItem.Size == 0) { DeleteFileAlways(phyPath); } else { // file was skipped /* errorInfo.SystemError = 0; errorInfo.Message = L"file was not processed"; errorInfo.FileName = phyPath; return E_FAIL; */ } } } pairs.Sort(CompareRefSortPair, NULL); for (i = 0; i < pairs.Size(); i++) { FString phyPath = us2fs(dirItems.GetPhyPath(pairs[i].Index)); if (NFind::DoesDirExist(phyPath)) { // printf("delete %S\n", phyPath); RemoveDir(phyPath); } } } return S_OK; } src/libs/7zip/unix/CPP/7zip/UI/Common/Update.h000066400000000000000000000106141325366651500211430ustar00rootroot00000000000000// Update.h #ifndef __COMMON_UPDATE_H #define __COMMON_UPDATE_H #include "../../../Common/Wildcard.h" #include "ArchiveOpenCallback.h" #include "LoadCodecs.h" #include "OpenArchive.h" #include "Property.h" #include "UpdateAction.h" #include "UpdateCallback.h" enum EArcNameMode { k_ArcNameMode_Smart, k_ArcNameMode_Exact, k_ArcNameMode_Add, }; struct CArchivePath { UString OriginalPath; UString Prefix; // path(folder) prefix including slash UString Name; // base name UString BaseExtension; // archive type extension or "exe" extension UString VolExtension; // archive type extension for volumes bool Temp; FString TempPrefix; // path(folder) for temp location FString TempPostfix; CArchivePath(): Temp(false) {}; void ParseFromPath(const UString &path, EArcNameMode mode); UString GetPathWithoutExt() const { return Prefix + Name; } UString GetFinalPath() const; UString GetFinalVolPath() const; FString GetTempPath() const; }; struct CUpdateArchiveCommand { UString UserArchivePath; CArchivePath ArchivePath; NUpdateArchive::CActionSet ActionSet; }; struct CCompressionMethodMode { bool Type_Defined; COpenType Type; CObjectVector Properties; CCompressionMethodMode(): Type_Defined(false) {} }; namespace NRecursedType { enum EEnum { kRecursed, kWildcardOnlyRecursed, kNonRecursed };} struct CRenamePair { UString OldName; UString NewName; bool WildcardParsing; NRecursedType::EEnum RecursedType; CRenamePair(): WildcardParsing(true), RecursedType(NRecursedType::kNonRecursed) {} bool Prepare(); bool GetNewPath(bool isFolder, const UString &src, UString &dest) const; }; struct CUpdateOptions { CCompressionMethodMode MethodMode; CObjectVector Commands; bool UpdateArchiveItself; CArchivePath ArchivePath; EArcNameMode ArcNameMode; bool SfxMode; FString SfxModule; bool OpenShareForWrite; bool StdInMode; UString StdInFileName; bool StdOutMode; bool EMailMode; bool EMailRemoveAfter; UString EMailAddress; FString WorkingDir; NWildcard::ECensorPathMode PathMode; UString AddPathPrefix; CBoolPair NtSecurity; CBoolPair AltStreams; CBoolPair HardLinks; CBoolPair SymLinks; bool DeleteAfterCompressing; bool SetArcMTime; CObjectVector RenamePairs; bool InitFormatIndex(const CCodecs *codecs, const CObjectVector &types, const UString &arcPath); bool SetArcPath(const CCodecs *codecs, const UString &arcPath); CUpdateOptions(): UpdateArchiveItself(true), ArcNameMode(k_ArcNameMode_Smart), SfxMode(false), OpenShareForWrite(false), StdInMode(false), StdOutMode(false), EMailMode(false), EMailRemoveAfter(false), PathMode(NWildcard::k_RelatPath), DeleteAfterCompressing(false), SetArcMTime(false) {}; void SetActionCommand_Add() { Commands.Clear(); CUpdateArchiveCommand c; c.ActionSet = NUpdateArchive::k_ActionSet_Add; Commands.Add(c); } CRecordVector VolumesSizes; }; struct CErrorInfo { DWORD SystemError; FString FileName; FString FileName2; UString Message; // UStringVector ErrorPaths; // CRecordVector ErrorCodes; CErrorInfo(): SystemError(0) {}; }; struct CUpdateErrorInfo: public CErrorInfo { }; #define INTERFACE_IUpdateCallbackUI2(x) \ INTERFACE_IUpdateCallbackUI(x) \ virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, const wchar_t *errorArcType) x; \ virtual HRESULT StartScanning() x; \ virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) x; \ virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT FinishScanning() x; \ virtual HRESULT StartArchive(const wchar_t *name, bool updating) x; \ virtual HRESULT FinishArchive() x; \ struct IUpdateCallbackUI2: public IUpdateCallbackUI { INTERFACE_IUpdateCallbackUI2(=0) }; HRESULT UpdateArchive( CCodecs *codecs, const CObjectVector &types, const UString &cmdArcPath2, NWildcard::CCensor &censor, CUpdateOptions &options, CUpdateErrorInfo &errorInfo, IOpenCallbackUI *openCallback, IUpdateCallbackUI2 *callback, bool needSetPath); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.cpp000066400000000000000000000023051325366651500226320ustar00rootroot00000000000000// UpdateAction.cpp #include "StdAfx.h" #include "UpdateAction.h" namespace NUpdateArchive { const CActionSet k_ActionSet_Add = {{ NPairAction::kCopy, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCompress, NPairAction::kCompress, NPairAction::kCompress, NPairAction::kCompress }}; const CActionSet k_ActionSet_Update = {{ NPairAction::kCopy, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress }}; const CActionSet k_ActionSet_Fresh = {{ NPairAction::kCopy, NPairAction::kCopy, NPairAction::kIgnore, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress }}; const CActionSet k_ActionSet_Sync = {{ NPairAction::kCopy, NPairAction::kIgnore, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress, }}; const CActionSet k_ActionSet_Delete = {{ NPairAction::kCopy, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore }}; } src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.h000066400000000000000000000026061325366651500223030ustar00rootroot00000000000000// UpdateAction.h #ifndef __UPDATE_ACTION_H #define __UPDATE_ACTION_H namespace NUpdateArchive { namespace NPairState { const unsigned kNumValues = 7; enum EEnum { kNotMasked = 0, kOnlyInArchive, kOnlyOnDisk, kNewInArchive, kOldInArchive, kSameFiles, kUnknowNewerFiles }; } namespace NPairAction { enum EEnum { kIgnore = 0, kCopy, kCompress, kCompressAsAnti }; } struct CActionSet { NPairAction::EEnum StateActions[NPairState::kNumValues]; bool IsEqualTo(const CActionSet &a) const { for (unsigned i = 0; i < NPairState::kNumValues; i++) if (StateActions[i] != a.StateActions[i]) return false; return true; } bool NeedScanning() const { unsigned i; for (i = 0; i < NPairState::kNumValues; i++) if (StateActions[i] == NPairAction::kCompress) return true; for (i = 1; i < NPairState::kNumValues; i++) if (StateActions[i] != NPairAction::kIgnore) return true; return false; } }; extern const CActionSet k_ActionSet_Add; extern const CActionSet k_ActionSet_Update; extern const CActionSet k_ActionSet_Fresh; extern const CActionSet k_ActionSet_Sync; extern const CActionSet k_ActionSet_Delete; } #endif src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.cpp000066400000000000000000000353131325366651500231160ustar00rootroot00000000000000// UpdateCallback.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/Synchronization.h" #include "../../Common/FileStreams.h" #include "../../Common/StreamObjects.h" #include "UpdateCallback.h" #if defined(_WIN32) && !defined(UNDER_CE) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif using namespace NWindows; using namespace NFile; #ifdef _USE_SECURITY_CODE bool InitLocalPrivileges(); #endif CArchiveUpdateCallback::CArchiveUpdateCallback(): Callback(0), ShareForWrite(false), StdInMode(false), DirItems(0), ArcItems(0), UpdatePairs(0), NewNames(0), KeepOriginalItemNames(false), ProcessedItemsStatuses(NULL), ParentDirItem(NULL), StoreNtSecurity(false), StoreHardLinks(false), StoreSymLinks(false), _hardIndex_From((UInt32)(Int32)-1) { #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size) { COM_TRY_BEGIN return Callback->SetTotal(size); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue) { COM_TRY_BEGIN return Callback->SetCompleted(completeValue); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { COM_TRY_BEGIN return Callback->SetRatioInfo(inSize, outSize); COM_TRY_END } /* static const STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidIsDir, VT_BOOL}, { NULL, kpidSize, VT_UI8}, { NULL, kpidCTime, VT_FILETIME}, { NULL, kpidATime, VT_FILETIME}, { NULL, kpidMTime, VT_FILETIME}, { NULL, kpidAttrib, VT_UI4}, { NULL, kpidIsAnti, VT_BOOL} }; STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **) { return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator); } */ STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive) { COM_TRY_BEGIN RINOK(Callback->CheckBreak()); const CUpdatePair2 &up = (*UpdatePairs)[index]; if (newData) *newData = BoolToInt(up.NewData); if (newProps) *newProps = BoolToInt(up.NewProps); if (indexInArchive) { *indexInArchive = (UInt32)(Int32)-1; if (up.ExistInArchive()) *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer; } return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidIsDir: prop = true; break; case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break; case kpidCTime: if (ParentDirItem) prop = ParentDirItem->CTime; break; case kpidATime: if (ParentDirItem) prop = ParentDirItem->ATime; break; case kpidMTime: if (ParentDirItem) prop = ParentDirItem->MTime; break; } prop.Detach(value); return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) { *parentType = NParentType::kDir; *parent = (UInt32)(Int32)-1; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps) { *numProps = 0; if (StoreNtSecurity) *numProps = 1; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) { *name = NULL; *propID = kpidNtSecure; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID #ifdef _USE_SECURITY_CODE propID #endif , const void **data, UInt32 *dataSize, UInt32 *propType) { *data = 0; *dataSize = 0; *propType = 0; if (!StoreNtSecurity) return S_OK; #ifdef _USE_SECURITY_CODE if (propID == kpidNtSecure) { if (StdInMode) return S_OK; if (ParentDirItem) { if (ParentDirItem->SecureIndex < 0) return S_OK; const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex]; *data = buf; *dataSize = (UInt32)buf.Size(); *propType = NPropDataType::kRaw; return S_OK; } if (GetRootProps) return GetRootProps->GetRootRawProp(propID, data, dataSize, propType); } #endif return S_OK; } // #ifdef _USE_SECURITY_CODE // #endif STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) { *data = 0; *dataSize = 0; *propType = 0; if (propID == kpidNtSecure || propID == kpidNtReparse) { if (StdInMode) return S_OK; const CUpdatePair2 &up = (*UpdatePairs)[index]; if (up.UseArcProps && up.ExistInArchive() && GetRawProps) return GetRawProps->GetRawProp( ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, data, dataSize, propType); { const CUpdatePair2 &up = (*UpdatePairs)[index]; /* if (!up.NewData) return E_FAIL; */ if (up.IsAnti) return S_OK; #ifndef UNDER_CE const CDirItem &di = DirItems->Items[up.DirIndex]; #endif #ifdef _USE_SECURITY_CODE if (propID == kpidNtSecure) { if (!StoreNtSecurity) return S_OK; if (di.SecureIndex < 0) return S_OK; const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex]; *data = buf; *dataSize = (UInt32)buf.Size(); *propType = NPropDataType::kRaw; } else #endif { // propID == kpidNtReparse if (!StoreSymLinks) return S_OK; #if 0 // #ifndef UNDER_CE const CByteBuffer *buf = &di.ReparseData2; if (buf->Size() == 0) buf = &di.ReparseData; if (buf->Size() != 0) { *data = *buf; *dataSize = (UInt32)buf->Size(); *propType = NPropDataType::kRaw; } #endif } return S_OK; } } return S_OK; } #ifndef UNDER_CE static UString GetRelativePath(const UString &to, const UString &from) { UStringVector partsTo, partsFrom; SplitPathToParts(to, partsTo); SplitPathToParts(from, partsFrom); unsigned i; for (i = 0;; i++) { if (i + 1 >= partsFrom.Size() || i + 1 >= partsTo.Size()) break; if (CompareFileNames(partsFrom[i], partsTo[i]) != 0) break; } if (i == 0) { #ifdef _WIN32 if (NName::IsDrivePath(to) || NName::IsDrivePath(from)) return to; #endif } UString s; unsigned k; for (k = i + 1; k < partsFrom.Size(); k++) s += L".." WSTRING_PATH_SEPARATOR; for (k = i; k < partsTo.Size(); k++) { if (k != i) s += WCHAR_PATH_SEPARATOR; s += partsTo[k]; } return s; } #endif STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN const CUpdatePair2 &up = (*UpdatePairs)[index]; NCOM::CPropVariant prop; if (up.NewData) { /* if (propID == kpidIsHardLink) { prop = _isHardLink; prop.Detach(value); return S_OK; } */ if (propID == kpidSymLink) { if (index == _hardIndex_From) { prop.Detach(value); return S_OK; } if (up.DirIndex >= 0) { #if 0 // #ifndef UNDER_CE const CDirItem &di = DirItems->Items[up.DirIndex]; // if (di.IsDir()) { CReparseAttr attr; if (attr.Parse(di.ReparseData, di.ReparseData.Size())) { UString simpleName = attr.GetPath(); if (attr.IsRelative()) prop = simpleName; else { const UString phyPath = DirItems->GetPhyPath(up.DirIndex); FString fullPath; if (NDir::MyGetFullPathName(us2fs(phyPath), fullPath)) { prop = GetRelativePath(simpleName, fs2us(fullPath)); } } prop.Detach(value); return S_OK; } } #endif } } else if (propID == kpidHardLink) { if (index == _hardIndex_From) { const CKeyKeyValPair &pair = _map[_hardIndex_To]; const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value]; prop = DirItems->GetLogPath(up2.DirIndex); prop.Detach(value); return S_OK; } if (up.DirIndex >= 0) { prop.Detach(value); return S_OK; } } } if (up.IsAnti && propID != kpidIsDir && propID != kpidPath && propID != kpidIsAltStream) { switch (propID) { case kpidSize: prop = (UInt64)0; break; case kpidIsAnti: prop = true; break; } } else if (propID == kpidPath && up.NewNameIndex >= 0) prop = (*NewNames)[up.NewNameIndex]; else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem) { // we can generate new ShortName here; } else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream))) && up.ExistInArchive() && Archive) return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value); else if (up.ExistOnDisk()) { const CDirItem &di = DirItems->Items[up.DirIndex]; switch (propID) { case kpidPath: prop = DirItems->GetLogPath(up.DirIndex); break; case kpidIsDir: prop = di.IsDir(); break; case kpidSize: prop = di.Size; break; case kpidAttrib: prop = di.Attrib; break; case kpidCTime: prop = di.CTime; break; case kpidATime: prop = di.ATime; break; case kpidMTime: prop = di.MTime; break; case kpidIsAltStream: prop = di.IsAltStream; break; #if defined(_WIN32) && !defined(UNDER_CE) // case kpidShortName: prop = di.ShortName; break; #endif } } prop.Detach(value); return S_OK; COM_TRY_END } static NSynchronization::CCriticalSection CS; STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) { COM_TRY_BEGIN *inStream = NULL; const CUpdatePair2 &up = (*UpdatePairs)[index]; if (!up.NewData) return E_FAIL; RINOK(Callback->CheckBreak()); RINOK(Callback->Finilize()); bool isDir = IsDir(up); if (up.IsAnti) { UString name; if (up.ArcIndex >= 0) name = (*ArcItems)[up.ArcIndex].Name; else if (up.DirIndex >= 0) name = DirItems->GetLogPath(up.DirIndex); RINOK(Callback->GetStream(name, true)); /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file. so we return empty stream */ if (!isDir) { CBufInStream *inStreamSpec = new CBufInStream(); CMyComPtr inStreamLoc = inStreamSpec; inStreamSpec->Init(NULL, 0); *inStream = inStreamLoc.Detach(); } return S_OK; } RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false)); if (isDir) return S_OK; if (StdInMode) { CStdInFileStream *inStreamSpec = new CStdInFileStream; CMyComPtr inStreamLoc(inStreamSpec); *inStream = inStreamLoc.Detach(); } else { CInFileStream *inStreamSpec = new CInFileStream; CMyComPtr inStreamLoc(inStreamSpec); inStreamSpec->SupportHardLinks = StoreHardLinks; const UString path = DirItems->GetPhyPath(up.DirIndex); #if defined(_WIN32) && !defined(UNDER_CE) if (DirItems->Items[up.DirIndex].AreReparseData()) { if (!inStreamSpec->File.OpenReparse(us2fs(path))) { return Callback->OpenFileError(path, ::GetLastError()); } } else #endif if (!inStreamSpec->OpenShared(us2fs(path), ShareForWrite)) { return Callback->OpenFileError(path, ::GetLastError()); } #if 0 // FIXME if (StoreHardLinks) { CStreamFileProps props; if (inStreamSpec->GetProps2(&props) == S_OK) { if (props.NumLinks > 1) { CKeyKeyValPair pair; pair.Key1 = props.VolID; pair.Key2 = props.FileID_Low; pair.Value = index; unsigned numItems = _map.Size(); unsigned pairIndex = _map.AddToUniqueSorted2(pair); if (numItems == _map.Size()) { // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex]; _hardIndex_From = index; _hardIndex_To = pairIndex; // we could return NULL as stream, but it's better to return real stream // return S_OK; } } } } #endif if (ProcessedItemsStatuses) { NSynchronization::CCriticalSectionLock lock(CS); ProcessedItemsStatuses[up.DirIndex] = 1; } *inStream = inStreamLoc.Detach(); } return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult) { COM_TRY_BEGIN return Callback->SetOperationResult(operationResult); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) { if (VolumesSizes.Size() == 0) return S_FALSE; if (index >= (UInt32)VolumesSizes.Size()) index = VolumesSizes.Size() - 1; *size = VolumesSizes[index]; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) { COM_TRY_BEGIN FChar temp[16]; ConvertUInt32ToString(index + 1, temp); FString res = temp; while (res.Len() < 2) res.InsertAtFront(FTEXT('0')); FString fileName = VolName; fileName += L'.'; fileName += res; fileName += VolExt; COutFileStream *streamSpec = new COutFileStream; CMyComPtr streamLoc(streamSpec); if (!streamSpec->Create(fileName, false)) return ::GetLastError(); *volumeStream = streamLoc.Detach(); return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) { COM_TRY_BEGIN return Callback->CryptoGetTextPassword2(passwordIsDefined, password); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN return Callback->CryptoGetTextPassword(password); COM_TRY_END } src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.h000066400000000000000000000063351325366651500225650ustar00rootroot00000000000000// UpdateCallback.h #ifndef __UPDATE_CALLBACK_H #define __UPDATE_CALLBACK_H #include "../../../Common/MyCom.h" #include "../../IPassword.h" #include "../../ICoder.h" #include "../Common/UpdatePair.h" #include "../Common/UpdateProduce.h" #define INTERFACE_IUpdateCallbackUI(x) \ virtual HRESULT SetTotal(UInt64 size) x; \ virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \ virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) x; \ virtual HRESULT CheckBreak() x; \ virtual HRESULT Finilize() x; \ virtual HRESULT SetNumFiles(UInt64 numFiles) x; \ virtual HRESULT GetStream(const wchar_t *name, bool isAnti) x; \ virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT SetOperationResult(Int32 operationResult) x; \ virtual HRESULT CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) x; \ virtual HRESULT CryptoGetTextPassword(BSTR *password) x; \ /* virtual HRESULT ShowDeleteFile(const wchar_t *name) x; */ \ /* virtual HRESULT CloseProgress() { return S_OK; }; */ struct IUpdateCallbackUI { INTERFACE_IUpdateCallbackUI(=0) }; struct CKeyKeyValPair { UInt64 Key1; UInt64 Key2; unsigned Value; int Compare(const CKeyKeyValPair &a) const { if (Key1 < a.Key1) return -1; if (Key1 > a.Key1) return 1; return MyCompare(Key2, a.Key2); } }; class CArchiveUpdateCallback: public IArchiveUpdateCallback2, public IArchiveGetRawProps, public IArchiveGetRootProps, public ICryptoGetTextPassword2, public ICryptoGetTextPassword, public ICompressProgressInfo, public CMyUnknownImp { #if defined(_WIN32) && !defined(UNDER_CE) bool _saclEnabled; #endif CRecordVector _map; UInt32 _hardIndex_From; UInt32 _hardIndex_To; public: MY_UNKNOWN_IMP6( IArchiveUpdateCallback2, IArchiveGetRawProps, IArchiveGetRootProps, ICryptoGetTextPassword2, ICryptoGetTextPassword, ICompressProgressInfo) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); INTERFACE_IArchiveUpdateCallback2(;) INTERFACE_IArchiveGetRawProps(;) INTERFACE_IArchiveGetRootProps(;) STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password); STDMETHOD(CryptoGetTextPassword)(BSTR *password); CRecordVector VolumesSizes; FString VolName; FString VolExt; IUpdateCallbackUI *Callback; bool ShareForWrite; bool StdInMode; const CDirItems *DirItems; const CDirItem *ParentDirItem; const CObjectVector *ArcItems; const CRecordVector *UpdatePairs; const UStringVector *NewNames; CMyComPtr Archive; CMyComPtr GetRawProps; CMyComPtr GetRootProps; bool KeepOriginalItemNames; bool StoreNtSecurity; bool StoreHardLinks; bool StoreSymLinks; Byte *ProcessedItemsStatuses; CArchiveUpdateCallback(); bool IsDir(const CUpdatePair2 &up) const { if (up.DirIndex >= 0) return DirItems->Items[up.DirIndex].IsDir(); else if (up.ArcIndex >= 0) return (*ArcItems)[up.ArcIndex].IsDir; return false; } }; #endif src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.cpp000066400000000000000000000144421325366651500223150ustar00rootroot00000000000000// UpdatePair.cpp #include "StdAfx.h" #include #include "../../../Common/Wildcard.h" #include "../../../Windows/TimeUtils.h" #include "SortUtils.h" #include "UpdatePair.h" using namespace NWindows; using namespace NTime; static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time1, const FILETIME &time2) { switch (fileTimeType) { case NFileTimeType::kWindows: return ::CompareFileTime(&time1, &time2); case NFileTimeType::kUnix: { UInt32 unixTime1, unixTime2; FileTimeToUnixTime(time1, unixTime1); FileTimeToUnixTime(time2, unixTime2); return MyCompare(unixTime1, unixTime2); } case NFileTimeType::kDOS: { UInt32 dosTime1, dosTime2; FileTimeToDosTime(time1, dosTime1); FileTimeToDosTime(time2, dosTime2); return MyCompare(dosTime1, dosTime2); } } throw 4191618; } static const char *k_Duplicate_inArc_Message = "Duplicate filename in archive:"; static const char *k_Duplicate_inDir_Message = "Duplicate filename on disk:"; static const char *k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):"; static void ThrowError(const char *message, const UString &s1, const UString &s2) { UString m; m.SetFromAscii(message); m += L'\n'; m += s1; m += L'\n'; m += s2; throw m; } static int CompareArcItemsBase(const CArcItem &ai1, const CArcItem &ai2) { int res = CompareFileNames(ai1.Name, ai2.Name); if (res != 0) return res; if (ai1.IsDir != ai2.IsDir) return ai1.IsDir ? -1 : 1; return 0; } static int CompareArcItems(const unsigned *p1, const unsigned *p2, void *param) { unsigned i1 = *p1; unsigned i2 = *p2; const CObjectVector &arcItems = *(const CObjectVector *)param; int res = CompareArcItemsBase(arcItems[i1], arcItems[i2]); if (res != 0) return res; return MyCompare(i1, i2); } void GetUpdatePairInfoList( const CDirItems &dirItems, const CObjectVector &arcItems, NFileTimeType::EEnum fileTimeType, CRecordVector &updatePairs) { CUIntVector dirIndices, arcIndices; unsigned numDirItems = dirItems.Items.Size(); unsigned numArcItems = arcItems.Size(); CIntArr duplicatedArcItem(numArcItems); { int *vals = &duplicatedArcItem[0]; for (unsigned i = 0; i < numArcItems; i++) vals[i] = 0; } { arcIndices.ClearAndSetSize(numArcItems); { unsigned *vals = &arcIndices[0]; for (unsigned i = 0; i < numArcItems; i++) vals[i] = i; } arcIndices.Sort(CompareArcItems, (void *)&arcItems); for (unsigned i = 0; i + 1 < numArcItems; i++) if (CompareArcItemsBase( arcItems[arcIndices[i]], arcItems[arcIndices[i + 1]]) == 0) { duplicatedArcItem[i] = 1; duplicatedArcItem[i + 1] = -1; } } UStringVector dirNames; { dirNames.ClearAndReserve(numDirItems); unsigned i; for (i = 0; i < numDirItems; i++) dirNames.AddInReserved(dirItems.GetLogPath(i)); SortFileNames(dirNames, dirIndices); for (i = 0; i + 1 < numDirItems; i++) { const UString &s1 = dirNames[dirIndices[i]]; const UString &s2 = dirNames[dirIndices[i + 1]]; if (CompareFileNames(s1, s2) == 0) ThrowError(k_Duplicate_inDir_Message, s1, s2); } } unsigned dirIndex = 0; unsigned arcIndex = 0; int prevHostFile = -1; const UString *prevHostName = NULL; while (dirIndex < numDirItems || arcIndex < numArcItems) { CUpdatePair pair; int dirIndex2 = -1; int arcIndex2 = -1; const CDirItem *di = NULL; const CArcItem *ai = NULL; int compareResult = -1; const UString *name = NULL; if (dirIndex < numDirItems) { dirIndex2 = dirIndices[dirIndex]; di = &dirItems.Items[dirIndex2]; } if (arcIndex < numArcItems) { arcIndex2 = arcIndices[arcIndex]; ai = &arcItems[arcIndex2]; compareResult = 1; if (dirIndex < numDirItems) { compareResult = CompareFileNames(dirNames[dirIndex2], ai->Name); if (compareResult == 0) { if (di->IsDir() != ai->IsDir) compareResult = (ai->IsDir ? 1 : -1); } } } if (compareResult < 0) { name = &dirNames[dirIndex2]; pair.State = NUpdateArchive::NPairState::kOnlyOnDisk; pair.DirIndex = dirIndex2; dirIndex++; } else if (compareResult > 0) { name = &ai->Name; pair.State = ai->Censored ? NUpdateArchive::NPairState::kOnlyInArchive: NUpdateArchive::NPairState::kNotMasked; pair.ArcIndex = arcIndex2; arcIndex++; } else { int dupl = duplicatedArcItem[arcIndex]; if (dupl != 0) ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[arcIndex + dupl]].Name); name = &dirNames[dirIndex2]; if (!ai->Censored) ThrowError(k_NotCensoredCollision_Message, *name, ai->Name); pair.DirIndex = dirIndex2; pair.ArcIndex = arcIndex2; switch (ai->MTimeDefined ? MyCompareTime( ai->TimeType != - 1 ? (NFileTimeType::EEnum)ai->TimeType : fileTimeType, di->MTime, ai->MTime): 0) { case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break; case 1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break; default: pair.State = (ai->SizeDefined && di->Size == ai->Size) ? NUpdateArchive::NPairState::kSameFiles : NUpdateArchive::NPairState::kUnknowNewerFiles; } dirIndex++; arcIndex++; } if ((di && di->IsAltStream) || (ai && ai->IsAltStream)) { if (prevHostName) { unsigned hostLen = prevHostName->Len(); if (name->Len() > hostLen) if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0) pair.HostIndex = prevHostFile; } } else { prevHostFile = updatePairs.Size(); prevHostName = name; } updatePairs.Add(pair); } updatePairs.ReserveDown(); } src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.h000066400000000000000000000011451325366651500217560ustar00rootroot00000000000000// UpdatePair.h #ifndef __UPDATE_PAIR_H #define __UPDATE_PAIR_H #include "DirItem.h" #include "UpdateAction.h" #include "../../Archive/IArchive.h" struct CUpdatePair { NUpdateArchive::NPairState::EEnum State; int ArcIndex; int DirIndex; int HostIndex; // >= 0 for alt streams only, contains index of host pair CUpdatePair(): ArcIndex(-1), DirIndex(-1), HostIndex(-1) {} }; void GetUpdatePairInfoList( const CDirItems &dirItems, const CObjectVector &arcItems, NFileTimeType::EEnum fileTimeType, CRecordVector &updatePairs); #endif src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.cpp000066400000000000000000000037661325366651500230320ustar00rootroot00000000000000// UpdateProduce.cpp #include "StdAfx.h" #include "UpdateProduce.h" using namespace NUpdateArchive; static const char *kUpdateActionSetCollision = "Internal collision in update action set"; void UpdateProduce( const CRecordVector &updatePairs, const CActionSet &actionSet, CRecordVector &operationChain, IUpdateProduceCallback *callback) { FOR_VECTOR (i, updatePairs) { const CUpdatePair &pair = updatePairs[i]; CUpdatePair2 up2; up2.DirIndex = pair.DirIndex; up2.ArcIndex = pair.ArcIndex; up2.NewData = up2.NewProps = true; up2.UseArcProps = false; switch (actionSet.StateActions[pair.State]) { case NPairAction::kIgnore: /* if (pair.State != NPairState::kOnlyOnDisk) IgnoreArchiveItem(m_ArchiveItems[pair.ArcIndex]); // cout << "deleting"; */ if (callback) callback->ShowDeleteFile(pair.ArcIndex); continue; case NPairAction::kCopy: if (pair.State == NPairState::kOnlyOnDisk) throw kUpdateActionSetCollision; if (pair.State == NPairState::kOnlyInArchive) { if (pair.HostIndex >= 0) { /* ignore alt stream if 1) no such alt stream in Disk 2) there is Host file in disk */ if (updatePairs[pair.HostIndex].DirIndex >= 0) continue; } } up2.NewData = up2.NewProps = false; up2.UseArcProps = true; break; case NPairAction::kCompress: if (pair.State == NPairState::kOnlyInArchive || pair.State == NPairState::kNotMasked) throw kUpdateActionSetCollision; break; case NPairAction::kCompressAsAnti: up2.IsAnti = true; up2.UseArcProps = (pair.ArcIndex >= 0); break; } operationChain.Add(up2); } operationChain.ReserveDown(); } src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.h000066400000000000000000000023301325366651500224610ustar00rootroot00000000000000// UpdateProduce.h #ifndef __UPDATE_PRODUCE_H #define __UPDATE_PRODUCE_H #include "UpdatePair.h" struct CUpdatePair2 { bool NewData; bool NewProps; bool UseArcProps; // if (UseArcProps && NewProps), we want to change only some properties. bool IsAnti; // if (!IsAnti) we use other ways to detect Anti status int DirIndex; int ArcIndex; int NewNameIndex; bool IsMainRenameItem; void SetAs_NoChangeArcItem(int arcIndex) { NewData = NewProps = false; UseArcProps = true; IsAnti = false; ArcIndex = arcIndex; } bool ExistOnDisk() const { return DirIndex != -1; } bool ExistInArchive() const { return ArcIndex != -1; } CUpdatePair2(): NewData(false), NewProps(false), UseArcProps(false), IsAnti(false), DirIndex(-1), ArcIndex(-1), NewNameIndex(-1), IsMainRenameItem(false) {} }; struct IUpdateProduceCallback { virtual HRESULT ShowDeleteFile(int arcIndex) = 0; }; void UpdateProduce( const CRecordVector &updatePairs, const NUpdateArchive::CActionSet &actionSet, CRecordVector &operationChain, IUpdateProduceCallback *callback); #endif src/libs/7zip/unix/CPP/7zip/UI/Console/000077500000000000000000000000001325366651500177205ustar00rootroot00000000000000src/libs/7zip/unix/CPP/7zip/UI/Console/Console.pri000066400000000000000000000001741325366651500220400ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/UI/Console/PercentPrinter.h SOURCES += $$7ZIP_BASE/CPP/7zip/UI/Console/PercentPrinter.cpp src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.cpp000066400000000000000000000042611325366651500233730ustar00rootroot00000000000000// PercentPrinter.cpp #include "StdAfx.h" #include "../../../Common/Defs.h" #include "../../../Common/IntToString.h" #include "PercentPrinter.h" static const unsigned kPaddingSize = 2; static const unsigned kPercentsSize = 4; static const unsigned kMaxExtraSize = kPaddingSize + 32 + kPercentsSize; static void ClearPrev(char *p, unsigned num) { unsigned i; for (i = 0; i < num; i++) *p++ = '\b'; for (i = 0; i < num; i++) *p++ = ' '; for (i = 0; i < num; i++) *p++ = '\b'; *p = '\0'; } void CPercentPrinter::ClosePrint() { if (m_NumExtraChars == 0) return; char s[kMaxExtraSize * 3 + 1]; ClearPrev(s, m_NumExtraChars); (*OutStream) << s; m_NumExtraChars = 0; } void CPercentPrinter::PrintString(const char *s) { ClosePrint(); (*OutStream) << s; } void CPercentPrinter::PrintString(const wchar_t *s) { ClosePrint(); (*OutStream) << s; } void CPercentPrinter::PrintNewLine() { ClosePrint(); (*OutStream) << "\n"; } void CPercentPrinter::RePrintRatio() { char s[32]; unsigned size; { char c = '%'; UInt64 value = 0; if (m_Total == (UInt64)(Int64)-1) { value = m_CurValue >> 20; c = 'M'; } else if (m_Total != 0) value = m_CurValue * 100 / m_Total; ConvertUInt64ToString(value, s); size = (unsigned)strlen(s); s[size++] = c; s[size] = '\0'; } unsigned extraSize = kPaddingSize + MyMax(size, kPercentsSize); if (extraSize < m_NumExtraChars) extraSize = m_NumExtraChars; char fullString[kMaxExtraSize * 3]; char *p = fullString; unsigned i; if (m_NumExtraChars == 0) { for (i = 0; i < extraSize; i++) *p++ = ' '; m_NumExtraChars = extraSize; } for (i = 0; i < m_NumExtraChars; i++) *p++ = '\b'; m_NumExtraChars = extraSize; for (; size < extraSize; size++) *p++ = ' '; MyStringCopy(p, s); (*OutStream) << fullString; OutStream->Flush(); m_PrevValue = m_CurValue; } void CPercentPrinter::PrintRatio() { if (m_CurValue < m_PrevValue + m_MinStepSize && m_CurValue + m_MinStepSize > m_PrevValue && m_NumExtraChars != 0) return; RePrintRatio(); } src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.h000066400000000000000000000014211325366651500230330ustar00rootroot00000000000000// PercentPrinter.h #ifndef __PERCENT_PRINTER_H #define __PERCENT_PRINTER_H #include "../../../Common/StdOutStream.h" class CPercentPrinter { UInt64 m_MinStepSize; UInt64 m_PrevValue; UInt64 m_CurValue; UInt64 m_Total; unsigned m_NumExtraChars; public: CStdOutStream *OutStream; CPercentPrinter(UInt64 minStepSize = 1): m_MinStepSize(minStepSize), m_PrevValue(0), m_CurValue(0), m_Total((UInt64)(Int64)-1), m_NumExtraChars(0) {} void SetTotal(UInt64 total) { m_Total = total; m_PrevValue = 0; } void SetRatio(UInt64 doneValue) { m_CurValue = doneValue; } void PrintString(const char *s); void PrintString(const wchar_t *s); void PrintNewLine(); void ClosePrint(); void RePrintRatio(); void PrintRatio(); }; #endif src/libs/7zip/unix/CPP/Common/000077500000000000000000000000001325366651500163405ustar00rootroot00000000000000src/libs/7zip/unix/CPP/Common/ComTry.h000066400000000000000000000006721325366651500177330ustar00rootroot00000000000000// ComTry.h #ifndef __COM_TRY_H #define __COM_TRY_H #include "MyWindows.h" // #include "Exception.h" // #include "NewHandler.h" #define COM_TRY_BEGIN try { #define COM_TRY_END } catch(const char * s) { throw s ; } \ catch(...) { return E_OUTOFMEMORY; } // catch(const CNewException &) { return E_OUTOFMEMORY; } // catch(const CSystemException &e) { return e.ErrorCode; } // catch(...) { return E_FAIL; } #endif src/libs/7zip/unix/CPP/Common/CommandLineParser.cpp000066400000000000000000000076551325366651500224240ustar00rootroot00000000000000// CommandLineParser.cpp #include "StdAfx.h" #include "CommandLineParser.h" static bool IsString1PrefixedByString2_NoCase(const wchar_t *u, const char *a) { for (;;) { char c = *a; if (c == 0) return true; if (MyCharLower_Ascii(c) != MyCharLower_Ascii(*u)) return false; a++; u++; } } namespace NCommandLineParser { #ifdef _WIN32 bool SplitCommandLine(const UString &src, UString &dest1, UString &dest2) { dest1.Empty(); dest2.Empty(); bool quoteMode = false; unsigned i; for (i = 0; i < src.Len(); i++) { wchar_t c = src[i]; if ((c == L' ' || c == L'\t') && !quoteMode) { dest2 = src.Ptr(i + 1); return i != 0; } if (c == L'\"') quoteMode = !quoteMode; else dest1 += c; } return i != 0; } void SplitCommandLine(const UString &s, UStringVector &parts) { UString sTemp = s; sTemp.Trim(); parts.Clear(); for (;;) { UString s1, s2; if (SplitCommandLine(sTemp, s1, s2)) parts.Add(s1); if (s2.IsEmpty()) break; sTemp = s2; } } #endif static const char *kStopSwitchParsing = "--"; static bool inline IsItSwitchChar(wchar_t c) { return (c == '-'); } CParser::CParser(unsigned numSwitches): _numSwitches(numSwitches), _switches(0) { _switches = new CSwitchResult[numSwitches]; } CParser::~CParser() { delete []_switches; } // if (s) contains switch then function updates switch structures // out: true, if (s) is a switch bool CParser::ParseString(const UString &s, const CSwitchForm *switchForms) { if (s.IsEmpty() || !IsItSwitchChar(s[0])) return false; unsigned pos = 1; unsigned switchIndex = 0; int maxLen = -1; for (unsigned i = 0; i < _numSwitches; i++) { const char *key = switchForms[i].Key; unsigned switchLen = MyStringLen(key); if ((int)switchLen <= maxLen || pos + switchLen > s.Len()) continue; if (IsString1PrefixedByString2_NoCase((const wchar_t *)s + pos, key)) { switchIndex = i; maxLen = switchLen; } } if (maxLen < 0) { ErrorMessage = "Unknown switch:"; return false; } pos += maxLen; CSwitchResult &sw = _switches[switchIndex]; const CSwitchForm &form = switchForms[switchIndex]; if (!form.Multi && sw.ThereIs) { ErrorMessage = "Multiple instances for switch:"; return false; } sw.ThereIs = true; int rem = s.Len() - pos; if (rem < form.MinLen) { ErrorMessage = "Too short switch:"; return false; } sw.WithMinus = false; sw.PostCharIndex = -1; switch (form.Type) { case NSwitchType::kMinus: if (rem != 0) { sw.WithMinus = (s[pos] == '-'); if (sw.WithMinus) pos++; } break; case NSwitchType::kChar: if (rem != 0) { wchar_t c = s[pos]; if (c <= 0x7F) { sw.PostCharIndex = FindCharPosInString(form.PostCharSet, (char)c); if (sw.PostCharIndex >= 0) pos++; } } break; case NSwitchType::kString: sw.PostStrings.Add((const wchar_t *)s + pos); return true; } if (pos != s.Len()) { ErrorMessage = "Too long switch:"; return false; } return true; } bool CParser::ParseStrings(const CSwitchForm *switchForms, const UStringVector &commandStrings) { ErrorLine.Empty(); bool stopSwitch = false; FOR_VECTOR (i, commandStrings) { const UString &s = commandStrings[i]; if (!stopSwitch) { if (s.IsEqualTo(kStopSwitchParsing)) { stopSwitch = true; continue; } if (!s.IsEmpty() && IsItSwitchChar(s[0])) { if (ParseString(s, switchForms)) continue; ErrorLine = s; return false; } } NonSwitchStrings.Add(s); } return true; } } src/libs/7zip/unix/CPP/Common/CommandLineParser.h000066400000000000000000000022661325366651500220620ustar00rootroot00000000000000// Common/CommandLineParser.h #ifndef __COMMON_COMMAND_LINE_PARSER_H #define __COMMON_COMMAND_LINE_PARSER_H #include "MyString.h" namespace NCommandLineParser { bool SplitCommandLine(const UString &src, UString &dest1, UString &dest2); void SplitCommandLine(const UString &s, UStringVector &parts); namespace NSwitchType { enum EEnum { kSimple, kMinus, kString, kChar }; } struct CSwitchForm { const char *Key; Byte Type; bool Multi; Byte MinLen; // int MaxLen; const char *PostCharSet; }; struct CSwitchResult { bool ThereIs; bool WithMinus; int PostCharIndex; UStringVector PostStrings; CSwitchResult(): ThereIs(false) {}; }; class CParser { unsigned _numSwitches; CSwitchResult *_switches; bool ParseString(const UString &s, const CSwitchForm *switchForms); public: UStringVector NonSwitchStrings; AString ErrorMessage; UString ErrorLine; CParser(unsigned numSwitches); ~CParser(); bool ParseStrings(const CSwitchForm *switchForms, const UStringVector &commandStrings); const CSwitchResult& operator[](size_t index) const { return _switches[index]; } }; } #endif src/libs/7zip/unix/CPP/Common/Common.h000066400000000000000000000003311325366651500177360ustar00rootroot00000000000000// Common.h #ifndef __COMMON_COMMON_H #define __COMMON_COMMON_H #include "../../C/Compiler.h" #include "MyWindows.h" #include "NewHandler.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[1])) #endif src/libs/7zip/unix/CPP/Common/Common.pri000066400000000000000000000026521325366651500203110ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/Common/CommandLineParser.h \ $$7ZIP_BASE/CPP/Common/ComTry.h \ $$7ZIP_BASE/CPP/Common/Common.h \ $$7ZIP_BASE/CPP/Common/Defs.h \ $$7ZIP_BASE/CPP/Common/IntToString.h \ $$7ZIP_BASE/CPP/Common/ListFileUtils.h \ $$7ZIP_BASE/CPP/Common/MyBuffer.h \ $$7ZIP_BASE/CPP/Common/MyCom.h \ $$7ZIP_BASE/CPP/Common/MyException.h \ $$7ZIP_BASE/CPP/Common/MyGuidDef.h \ $$7ZIP_BASE/CPP/Common/MyInitGuid.h \ $$7ZIP_BASE/CPP/Common/MyString.h \ $$7ZIP_BASE/CPP/Common/MyTypes.h \ $$7ZIP_BASE/CPP/Common/MyUnknown.h \ $$7ZIP_BASE/CPP/Common/MyVector.h \ $$7ZIP_BASE/CPP/Common/MyWindows.h \ $$7ZIP_BASE/CPP/Common/NewHandler.h \ $$7ZIP_BASE/CPP/Common/StdOutStream.h \ $$7ZIP_BASE/CPP/Common/StringConvert.h \ $$7ZIP_BASE/CPP/Common/StringToInt.h \ $$7ZIP_BASE/CPP/Common/UTFConvert.h \ $$7ZIP_BASE/CPP/Common/Wildcard.h SOURCES += $$7ZIP_BASE/CPP/Common/CommandLineParser.cpp \ $$7ZIP_BASE/CPP/Common/IntToString.cpp \ $$7ZIP_BASE/CPP/Common/ListFileUtils.cpp \ $$7ZIP_BASE/CPP/Common/MyString.cpp \ $$7ZIP_BASE/CPP/Common/MyWindows.cpp \ $$7ZIP_BASE/CPP/Common/NewHandler.cpp \ $$7ZIP_BASE/CPP/Common/StdOutStream.cpp \ $$7ZIP_BASE/CPP/Common/StringConvert.cpp \ $$7ZIP_BASE/CPP/Common/StringToInt.cpp \ $$7ZIP_BASE/CPP/Common/UTFConvert.cpp \ $$7ZIP_BASE/CPP/Common/Wildcard.cpp src/libs/7zip/unix/CPP/Common/Defs.h000066400000000000000000000007151325366651500173750ustar00rootroot00000000000000// Common/Defs.h #ifndef __COMMON_DEFS_H #define __COMMON_DEFS_H template inline T MyMin(T a, T b) { return a < b ? a : b; } template inline T MyMax(T a, T b) { return a > b ? a : b; } template inline int MyCompare(T a, T b) { return a < b ? -1 : (a == b ? 0 : 1); } inline int BoolToInt(bool value) { return (value ? 1: 0); } inline bool IntToBool(int value) { return (value != 0); } #endif src/libs/7zip/unix/CPP/Common/IntToString.cpp000066400000000000000000000052741325366651500213000ustar00rootroot00000000000000// Common/IntToString.cpp #include "StdAfx.h" #include "IntToString.h" #define CONVERT_INT_TO_STR(charType, tempSize) \ unsigned char temp[tempSize]; unsigned i = 0; \ while (val >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(val % 10)); val /= 10; } \ *s++ = (charType)('0' + (unsigned)val); \ while (i != 0) { i--; *s++ = temp[i]; } \ *s = 0; void ConvertUInt32ToString(UInt32 val, char *s) throw() { CONVERT_INT_TO_STR(char, 16); } void ConvertUInt64ToString(UInt64 val, char *s) throw() { if (val <= (UInt32)0xFFFFFFFF) { ConvertUInt32ToString((UInt32)val, s); return; } CONVERT_INT_TO_STR(char, 24); } void ConvertUInt64ToOct(UInt64 val, char *s) throw() { UInt64 v = val; unsigned i; for (i = 1;; i++) { v >>= 3; if (v == 0) break; } s[i] = 0; do { unsigned t = (unsigned)(val & 0x7); val >>= 3; s[--i] = (char)('0' + t); } while (i); } void ConvertUInt32ToHex(UInt32 val, char *s) throw() { UInt32 v = val; unsigned i; for (i = 1;; i++) { v >>= 4; if (v == 0) break; } s[i] = 0; do { unsigned t = (unsigned)((val & 0xF)); val >>= 4; s[--i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } while (i); } void ConvertUInt64ToHex(UInt64 val, char *s) throw() { UInt64 v = val; unsigned i; for (i = 1;; i++) { v >>= 4; if (v == 0) break; } s[i] = 0; do { unsigned t = (unsigned)((val & 0xF)); val >>= 4; s[--i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } while (i); } void ConvertUInt32ToHex8Digits(UInt32 val, char *s) throw() { s[8] = 0; for (int i = 7; i >= 0; i--) { unsigned t = val & 0xF; val >>= 4; s[i] = (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); } } /* void ConvertUInt32ToHex8Digits(UInt32 val, wchar_t *s) { s[8] = 0; for (int i = 7; i >= 0; i--) { unsigned t = val & 0xF; val >>= 4; s[i] = (wchar_t)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); } } */ void ConvertUInt32ToString(UInt32 val, wchar_t *s) throw() { CONVERT_INT_TO_STR(wchar_t, 16); } void ConvertUInt64ToString(UInt64 val, wchar_t *s) throw() { if (val <= (UInt32)0xFFFFFFFF) { ConvertUInt32ToString((UInt32)val, s); return; } CONVERT_INT_TO_STR(wchar_t, 24); } void ConvertInt64ToString(Int64 val, char *s) throw() { if (val < 0) { *s++ = '-'; val = -val; } ConvertUInt64ToString(val, s); } void ConvertInt64ToString(Int64 val, wchar_t *s) throw() { if (val < 0) { *s++ = L'-'; val = -val; } ConvertUInt64ToString(val, s); } src/libs/7zip/unix/CPP/Common/IntToString.h000066400000000000000000000014511325366651500207360ustar00rootroot00000000000000// Common/IntToString.h #ifndef __COMMON_INT_TO_STRING_H #define __COMMON_INT_TO_STRING_H #include "MyTypes.h" void ConvertUInt32ToString(UInt32 value, char *s) throw(); void ConvertUInt64ToString(UInt64 value, char *s) throw(); void ConvertUInt32ToString(UInt32 value, wchar_t *s) throw(); void ConvertUInt64ToString(UInt64 value, wchar_t *s) throw(); void ConvertUInt64ToOct(UInt64 value, char *s) throw(); void ConvertUInt32ToHex(UInt32 value, char *s) throw(); void ConvertUInt64ToHex(UInt64 value, char *s) throw(); void ConvertUInt32ToHex8Digits(UInt32 value, char *s) throw(); // void ConvertUInt32ToHex8Digits(UInt32 value, wchar_t *s) throw(); void ConvertInt64ToString(Int64 value, char *s) throw(); void ConvertInt64ToString(Int64 value, wchar_t *s) throw(); #endif src/libs/7zip/unix/CPP/Common/ListFileUtils.cpp000066400000000000000000000052021325366651500215770ustar00rootroot00000000000000// Common/ListFileUtils.cpp #include "StdAfx.h" #include "../../C/CpuArch.h" #include "../Windows/FileIO.h" #include "ListFileUtils.h" #include "MyBuffer.h" #include "StringConvert.h" #include "UTFConvert.h" static const char kQuoteChar = '\"'; static void AddName(UStringVector &strings, UString &s) { s.Trim(); if (s.Len() >= 2 && s[0] == kQuoteChar && s.Back() == kQuoteChar) { s.DeleteBack(); s.Delete(0); } if (!s.IsEmpty()) strings.Add(s); } bool ReadNamesFromListFile(CFSTR fileName, UStringVector &strings, UINT codePage) { NWindows::NFile::NIO::CInFile file; if (!file.Open(fileName,true)) /* follow the symbolic link */ return false; UInt64 fileSize; if (!file.GetLength(fileSize)) return false; if (fileSize >= ((UInt32)1 << 31) - 32) return false; UString u; if (codePage == MY__CP_UTF16 || codePage == MY__CP_UTF16BE) { if ((fileSize & 1) != 0) return false; CByteArr buf((size_t)fileSize); UInt32 processed; if (!file.Read(buf, (UInt32)fileSize, processed)) return false; if (processed != fileSize) return false; file.Close(); unsigned num = (unsigned)fileSize / 2; wchar_t *p = u.GetBuffer(num); if (codePage == MY__CP_UTF16) for (unsigned i = 0; i < num; i++) { wchar_t c = GetUi16(buf + i * 2); if (c == 0) return false; p[i] = c; } else for (unsigned i = 0; i < num; i++) { wchar_t c = (wchar_t)GetBe16(buf + i * 2); if (c == 0) return false; p[i] = c; } u.ReleaseBuffer(num); } else { AString s; char *p = s.GetBuffer((unsigned)fileSize); UInt32 processed; if (!file.Read(p, (UInt32)fileSize, processed)) return false; if (processed != fileSize) return false; file.Close(); p[processed] = 0; s.ReleaseBuffer(); if (s.Len() != processed) return false; // #ifdef CP_UTF8 if (codePage == CP_UTF8) { if (!ConvertUTF8ToUnicode(s, u)) return false; } else // #endif MultiByteToUnicodeString2(u, s, codePage); } const wchar_t kGoodBOM = 0xFEFF; const wchar_t kBadBOM = 0xFFFE; UString s; unsigned i = 0; for (; i < u.Len() && u[i] == kGoodBOM; i++); for (; i < u.Len(); i++) { wchar_t c = u[i]; if (c == kGoodBOM || c == kBadBOM) return false; if (c == L'\n' || c == 0xD) { AddName(strings, s); s.Empty(); } else s += c; } AddName(strings, s); return true; } src/libs/7zip/unix/CPP/Common/ListFileUtils.h000066400000000000000000000004731325366651500212510ustar00rootroot00000000000000// Common/ListFileUtils.h #ifndef __COMMON_LIST_FILE_UTILS_H #define __COMMON_LIST_FILE_UTILS_H #include "MyString.h" #include "MyTypes.h" #define MY__CP_UTF16 1200 #define MY__CP_UTF16BE 1201 bool ReadNamesFromListFile(CFSTR fileName, UStringVector &strings, UINT codePage = CP_OEMCP); #endif src/libs/7zip/unix/CPP/Common/MyBuffer.h000066400000000000000000000115331325366651500202330ustar00rootroot00000000000000// Common/MyBuffer.h #ifndef __COMMON_MY_BUFFER_H #define __COMMON_MY_BUFFER_H #include "Defs.h" template class CBuffer { T *_items; size_t _size; void CopyToEmpty(const CBuffer &buffer) { if (buffer._size > 0) { _items = new T[buffer._size]; memcpy(_items, buffer._items, buffer._size * sizeof(T)); _size = buffer._size; } } public: void Free() { if (_items) { delete []_items; _items = 0; } _size = 0; } CBuffer(): _items(0), _size(0) {}; CBuffer(size_t size): _items(0), _size(0) { _items = new T[size]; _size = size; } CBuffer(const CBuffer &buffer): _items(0), _size(0) { CopyToEmpty(buffer); } ~CBuffer() { delete []_items; } operator T *() { return _items; }; operator const T *() const { return _items; }; size_t Size() const { return _size; } void Alloc(size_t size) { if (size != _size) { Free(); if (size != 0) { _items = new T[size]; _size = size; } } } void AllocAtLeast(size_t size) { if (size > _size) { Free(); _items = new T[size]; _size = size; } } void CopyFrom(const T *data, size_t size) { Alloc(size); memcpy(_items, data, size * sizeof(T)); } void ChangeSize_KeepData(size_t newSize, size_t keepSize) { if (newSize == _size) return; T *newBuffer = NULL; if (newSize > 0) { newBuffer = new T[newSize]; if (_size > 0) memcpy(newBuffer, _items, MyMin(MyMin(_size, keepSize), newSize) * sizeof(T)); } delete []_items; _items = newBuffer; _size = newSize; } CBuffer& operator=(const CBuffer &buffer) { Free(); CopyToEmpty(buffer); return *this; } }; template bool operator==(const CBuffer& b1, const CBuffer& b2) { size_t size1 = b1.Size(); if (size1 != b2.Size()) return false; return memcmp(b1, b2, size1 * sizeof(T)) == 0; } template bool operator!=(const CBuffer& b1, const CBuffer& b2) { size_t size1 = b1.Size(); if (size1 == b2.Size()) return false; return memcmp(b1, b2, size1 * sizeof(T)) != 0; } typedef CBuffer CCharBuffer; typedef CBuffer CWCharBuffer; typedef CBuffer CByteBuffer; template class CObjArray { protected: T *_items; private: // we disable constructors CObjArray(const CObjArray &buffer); void operator=(const CObjArray &buffer); public: void Free() { delete []_items; _items = 0; } CObjArray(size_t size): _items(0) { if (size != 0) _items = new T[size]; } CObjArray(): _items(0) {}; ~CObjArray() { delete []_items; } operator T *() { return _items; }; operator const T *() const { return _items; }; void Alloc(size_t newSize) { delete []_items; _items = 0; _items = new T[newSize]; } }; typedef CObjArray CByteArr; typedef CObjArray CBoolArr; typedef CObjArray CIntArr; // #define CRecArray CObjArray template class CObjArray2 { // protected: T *_items; unsigned _size; CObjArray2(const CObjArray2 &buffer); void operator=(const CObjArray2 &buffer); public: void Free() { delete []_items; _items = 0; _size = 0; } CObjArray2(): _items(0), _size(0) {}; /* CObjArray2(const CObjArray2 &buffer): _items(0), _size(0) { size_t newSize = buffer._size; if (newSize > 0) { T *newBuffer = new T[newSize];; _items = newBuffer; _size = newSize; const T *src = buffer; for (size_t i = 0; i < newSize; i++) newBuffer[i] = src[i]; } } */ /* CObjArray2(size_t size): _items(0), _size(0) { if (size != 0) { _items = new T[size]; _size = size; } } */ ~CObjArray2() { delete []_items; } operator T *() { return _items; }; operator const T *() const { return _items; }; unsigned Size() const { return (unsigned)_size; } bool IsEmpty() const { return _size == 0; } // SetSize doesn't keep old items. It allocates new array if size is not equal void SetSize(unsigned size) { if (size == _size) return; T *newBuffer = NULL; if (size > 0) newBuffer = new T[size]; delete []_items; _items = newBuffer; _size = size; } /* CObjArray2& operator=(const CObjArray2 &buffer) { Free(); size_t newSize = buffer._size; if (newSize > 0) { T *newBuffer = new T[newSize];; _items = newBuffer; _size = newSize; const T *src = buffer; for (size_t i = 0; i < newSize; i++) newBuffer[i] = src[i]; } return *this; } */ }; #endif src/libs/7zip/unix/CPP/Common/MyCom.h000066400000000000000000000146611325366651500175450ustar00rootroot00000000000000// MyCom.h #ifndef __MY_COM_H #define __MY_COM_H #include "MyWindows.h" #include "NewHandler.h" #ifndef RINOK #define RINOK(x) { HRESULT __result_ = (x); if (__result_ != S_OK) return __result_; } #endif template class CMyComPtr { T* _p; public: CMyComPtr(): _p(NULL) {} CMyComPtr(T* p) throw() { if ((_p = p) != NULL) p->AddRef(); } CMyComPtr(const CMyComPtr& lp) throw() { if ((_p = lp._p) != NULL) _p->AddRef(); } ~CMyComPtr() { if (_p) _p->Release(); } void Release() { if (_p) { _p->Release(); _p = NULL; } } operator T*() const { return (T*)_p; } // T& operator*() const { return *_p; } T** operator&() { return &_p; } T* operator->() const { return _p; } T* operator=(T* p) { if (p) p->AddRef(); if (_p) _p->Release(); _p = p; return p; } T* operator=(const CMyComPtr& lp) { return (*this = lp._p); } bool operator!() const { return (_p == NULL); } // bool operator==(T* pT) const { return _p == pT; } void Attach(T* p2) { Release(); _p = p2; } T* Detach() { T* pt = _p; _p = NULL; return pt; } #ifdef _WIN32 HRESULT CoCreateInstance(REFCLSID rclsid, REFIID iid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) { return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, iid, (void**)&_p); } #endif /* HRESULT CoCreateInstance(LPCOLESTR szProgID, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) { CLSID clsid; HRESULT hr = CLSIDFromProgID(szProgID, &clsid); ATLASSERT(_p == NULL); if (SUCCEEDED(hr)) hr = ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&_p); return hr; } */ template HRESULT QueryInterface(REFGUID iid, Q** pp) const throw() { return _p->QueryInterface(iid, (void**)pp); } }; ////////////////////////////////////////////////////////// inline HRESULT StringToBstr(LPCOLESTR src, BSTR *bstr) { *bstr = ::SysAllocString(src); return (*bstr != NULL) ? S_OK : E_OUTOFMEMORY; } class CMyComBSTR { BSTR m_str; public: CMyComBSTR(): m_str(NULL) {} CMyComBSTR(LPCOLESTR src) { m_str = ::SysAllocString(src); } // CMyComBSTR(int nSize) { m_str = ::SysAllocStringLen(NULL, nSize); } // CMyComBSTR(int nSize, LPCOLESTR sz) { m_str = ::SysAllocStringLen(sz, nSize); } CMyComBSTR(const CMyComBSTR& src) { m_str = src.MyCopy(); } /* CMyComBSTR(REFGUID src) { LPOLESTR szGuid; StringFromCLSID(src, &szGuid); m_str = ::SysAllocString(szGuid); CoTaskMemFree(szGuid); } */ ~CMyComBSTR() { ::SysFreeString(m_str); } CMyComBSTR& operator=(const CMyComBSTR& src) { if (m_str != src.m_str) { if (m_str) ::SysFreeString(m_str); m_str = src.MyCopy(); } return *this; } CMyComBSTR& operator=(LPCOLESTR src) { ::SysFreeString(m_str); m_str = ::SysAllocString(src); return *this; } // unsigned Len() const { return ::SysStringLen(m_str); } operator BSTR() const { return m_str; } BSTR* operator&() { return &m_str; } BSTR MyCopy() const { int byteLen = ::SysStringByteLen(m_str); BSTR res = ::SysAllocStringByteLen(NULL, byteLen); memcpy(res, m_str, byteLen); return res; } /* void Attach(BSTR src) { m_str = src; } BSTR Detach() { BSTR s = m_str; m_str = NULL; return s; } */ void Empty() { ::SysFreeString(m_str); m_str = NULL; } bool operator!() const { return (m_str == NULL); } }; ////////////////////////////////////////////////////////// class CMyUnknownImp { public: ULONG __m_RefCount; CMyUnknownImp(): __m_RefCount(0) {} }; #define MY_QUERYINTERFACE_BEGIN STDMETHOD(QueryInterface) \ (REFGUID iid, void **outObject) throw() { *outObject = NULL; #define MY_QUERYINTERFACE_ENTRY(i) else if (iid == IID_ ## i) \ { *outObject = (void *)(i *)this; } #define MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) if (iid == IID_IUnknown) \ { *outObject = (void *)(IUnknown *)(i *)this; } #define MY_QUERYINTERFACE_BEGIN2(i) MY_QUERYINTERFACE_BEGIN \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \ MY_QUERYINTERFACE_ENTRY(i) #define MY_QUERYINTERFACE_END else return E_NOINTERFACE; AddRef(); return S_OK; } #define MY_ADDREF_RELEASE \ STDMETHOD_(ULONG, AddRef)() throw() { return ++__m_RefCount; } \ STDMETHOD_(ULONG, Release)() { if (--__m_RefCount != 0) \ return __m_RefCount; delete this; return 0; } #define MY_UNKNOWN_IMP_SPEC(i) \ MY_QUERYINTERFACE_BEGIN \ i \ MY_QUERYINTERFACE_END \ MY_ADDREF_RELEASE #define MY_UNKNOWN_IMP MY_QUERYINTERFACE_BEGIN \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(IUnknown) \ MY_QUERYINTERFACE_END \ MY_ADDREF_RELEASE #define MY_UNKNOWN_IMP1(i) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \ MY_QUERYINTERFACE_ENTRY(i) \ ) #define MY_UNKNOWN_IMP2(i1, i2) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ ) #define MY_UNKNOWN_IMP3(i1, i2, i3) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ ) #define MY_UNKNOWN_IMP4(i1, i2, i3, i4) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ ) #define MY_UNKNOWN_IMP5(i1, i2, i3, i4, i5) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ MY_QUERYINTERFACE_ENTRY(i5) \ ) #define MY_UNKNOWN_IMP6(i1, i2, i3, i4, i5, i6) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ MY_QUERYINTERFACE_ENTRY(i5) \ MY_QUERYINTERFACE_ENTRY(i6) \ ) #define MY_UNKNOWN_IMP7(i1, i2, i3, i4, i5, i6, i7) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ MY_QUERYINTERFACE_ENTRY(i5) \ MY_QUERYINTERFACE_ENTRY(i6) \ MY_QUERYINTERFACE_ENTRY(i7) \ ) #endif src/libs/7zip/unix/CPP/Common/MyException.h000066400000000000000000000003611325366651500207550ustar00rootroot00000000000000// Common/Exception.h #ifndef __COMMON_EXCEPTION_H #define __COMMON_EXCEPTION_H #include "MyWindows.h" struct CSystemException { HRESULT ErrorCode; CSystemException(HRESULT errorCode): ErrorCode(errorCode) {} }; #endif src/libs/7zip/unix/CPP/Common/MyGuidDef.h000066400000000000000000000021221325366651500203230ustar00rootroot00000000000000// Common/MyGuidDef.h #ifndef GUID_DEFINED #define GUID_DEFINED #include "MyTypes.h" typedef struct { UInt32 Data1; UInt16 Data2; UInt16 Data3; unsigned char Data4[8]; } GUID; #ifdef __cplusplus #define REFGUID const GUID & #else #define REFGUID const GUID * #endif #define REFCLSID REFGUID #define REFIID REFGUID #ifdef __cplusplus inline int operator==(REFGUID g1, REFGUID g2) { for (int i = 0; i < (int)sizeof(g1); i++) if (((unsigned char *)&g1)[i] != ((unsigned char *)&g2)[i]) return 0; return 1; } inline int operator!=(REFGUID g1, REFGUID g2) { return !(g1 == g2); } #endif #ifdef __cplusplus #define MY_EXTERN_C extern "C" #else #define MY_EXTERN_C extern #endif #endif #ifdef DEFINE_GUID #undef DEFINE_GUID #endif #ifdef INITGUID #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ MY_EXTERN_C const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } #else #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ MY_EXTERN_C const GUID name #endif src/libs/7zip/unix/CPP/Common/MyInitGuid.h000066400000000000000000000023451325366651500205370ustar00rootroot00000000000000// Common/MyInitGuid.h #ifndef __COMMON_MY_INITGUID_H #define __COMMON_MY_INITGUID_H /* This file must be included only to one C++ file in project before declarations of COM interfaces with DEFINE_GUID macro. Each GUID must be initialized exactly once in project. There are two different versions of the DEFINE_GUID macro in guiddef.h (MyGuidDef.h): - if INITGUID is not defined: DEFINE_GUID declares an external reference to the symbol name. - if INITGUID is defined: DEFINE_GUID initializes the symbol name to the value of the GUID. Also we need IID_IUnknown that is initialized in some file for linking: MSVC: by default the linker uses some lib file that contains IID_IUnknown MinGW: add -luuid switch for linker WinCE: we define IID_IUnknown in this file Other: we define IID_IUnknown in this file */ #ifdef _WIN32 #ifdef UNDER_CE #include #endif #include #ifdef UNDER_CE DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); #endif #else #define INITGUID #include "MyGuidDef.h" DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); #endif #endif src/libs/7zip/unix/CPP/Common/MyString.cpp000066400000000000000000000575241325366651500206350ustar00rootroot00000000000000// Common/MyString.cpp #include "StdAfx.h" #ifdef _WIN32 #include #include #else #include #endif #if !defined(_UNICODE) || !defined(USE_UNICODE_FSTRING) #include "StringConvert.h" #endif #include "MyString.h" #define MY_STRING_NEW(_T_, _size_) new _T_[_size_] // #define MY_STRING_NEW(_T_, _size_) ((_T_ *)my_new((size_t)(_size_) * sizeof(_T_))) /* inline const char* MyStringGetNextCharPointer(const char *p) throw() { #if defined(_WIN32) && !defined(UNDER_CE) return CharNextA(p); #else return p + 1; #endif } */ int FindCharPosInString(const char *s, char c) throw() { for (const char *p = s;; p++) { if (*p == c) return (int)(p - s); if (*p == 0) return -1; // MyStringGetNextCharPointer(p); } } int FindCharPosInString(const wchar_t *s, wchar_t c) throw() { for (const wchar_t *p = s;; p++) { if (*p == c) return (int)(p - s); if (*p == 0) return -1; } } /* void MyStringUpper_Ascii(wchar_t *s) { for (;;) { wchar_t c = *s; if (c == 0) return; *s++ = MyCharUpper_Ascii(c); } } */ void MyStringLower_Ascii(wchar_t *s) throw() { for (;;) { wchar_t c = *s; if (c == 0) return; *s++ = MyCharLower_Ascii(c); } } #ifdef _WIN32 #ifdef _UNICODE // wchar_t * MyStringUpper(wchar_t *s) { return CharUpperW(s); } // wchar_t * MyStringLower(wchar_t *s) { return CharLowerW(s); } // for WinCE - FString - char // const char *MyStringGetPrevCharPointer(const char * /* base */, const char *p) { return p - 1; } #else // const char * MyStringGetPrevCharPointer(const char *base, const char *p) throw() { return CharPrevA(base, p); } // char * MyStringUpper(char *s) { return CharUpperA(s); } // char * MyStringLower(char *s) { return CharLowerA(s); } wchar_t MyCharUpper_WIN(wchar_t c) throw() { wchar_t *res = CharUpperW((LPWSTR)(UINT_PTR)(unsigned)c); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return (wchar_t)(unsigned)(UINT_PTR)res; const int kBufSize = 4; char s[kBufSize + 1]; int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufSize, 0, 0); if (numChars == 0 || numChars > kBufSize) return c; s[numChars] = 0; ::CharUpperA(s); ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1); return c; } /* wchar_t MyCharLower_WIN(wchar_t c) { wchar_t *res = CharLowerW((LPWSTR)(UINT_PTR)(unsigned)c); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return (wchar_t)(unsigned)(UINT_PTR)res; const int kBufSize = 4; char s[kBufSize + 1]; int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufSize, 0, 0); if (numChars == 0 || numChars > kBufSize) return c; s[numChars] = 0; ::CharLowerA(s); ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1); return c; } */ /* wchar_t * MyStringUpper(wchar_t *s) { if (s == 0) return 0; wchar_t *res = CharUpperW(s); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return res; AString a = UnicodeStringToMultiByte(s); a.MakeUpper(); MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a)); return s; } */ /* wchar_t * MyStringLower(wchar_t *s) { if (s == 0) return 0; wchar_t *res = CharLowerW(s); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return res; AString a = UnicodeStringToMultiByte(s); a.MakeLower(); MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a)); return s; } */ #endif #endif bool IsString1PrefixedByString2(const char *s1, const char *s2) throw() { for (;;) { unsigned char c2 = (unsigned char)*s2++; if (c2 == 0) return true; unsigned char c1 = (unsigned char)*s1++; if (c1 != c2) return false; } } bool StringsAreEqualNoCase(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2 && MyCharUpper(c1) != MyCharUpper(c2)) return false; if (c1 == 0) return true; } } // ---------- ASCII ---------- bool AString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw() { const char *s1 = _chars; for (;;) { char c2 = *s++; if (c2 == 0) return true; char c1 = *s1++; if (MyCharLower_Ascii(c1) != MyCharLower_Ascii(c2)) return false; } } bool UString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw() { const wchar_t *s1 = _chars; for (;;) { char c2 = *s++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (MyCharLower_Ascii(c1) != (unsigned char)MyCharLower_Ascii(c2)) return false; } } bool StringsAreEqual_Ascii(const wchar_t *u, const char *a) throw() { for (;;) { unsigned char c = *a; if (c != *u) return false; if (c == 0) return true; a++; u++; } } bool StringsAreEqualNoCase_Ascii(const char *s1, const char *s2) throw() { for (;;) { char c1 = *s1++; char c2 = *s2++; if (c1 != c2 && MyCharLower_Ascii(c1) != MyCharLower_Ascii(c2)) return false; if (c1 == 0) return true; } } bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2 && MyCharLower_Ascii(c1) != MyCharLower_Ascii(c2)) return false; if (c1 == 0) return true; } } bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const char *s2) throw() { for (;;) { wchar_t c1 = *s1++; char c2 = *s2++; if (c1 != (unsigned char)c2 && (c1 > 0x7F || MyCharLower_Ascii(c1) != (unsigned char)MyCharLower_Ascii(c2))) return false; if (c1 == 0) return true; } } bool IsString1PrefixedByString2(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c2 = *s2++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (c1 != c2) return false; } } // NTFS order: uses upper case int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2) { wchar_t u1 = MyCharUpper(c1); wchar_t u2 = MyCharUpper(c2); if (u1 < u2) return -1; if (u1 > u2) return 1; } if (c1 == 0) return 0; } } int MyStringCompareNoCase_N(const wchar_t *s1, const wchar_t *s2, unsigned num) throw() { for (; num != 0; num--) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2) { wchar_t u1 = MyCharUpper(c1); wchar_t u2 = MyCharUpper(c2); if (u1 < u2) return -1; if (u1 > u2) return 1; } if (c1 == 0) return 0; } return 0; } // ---------- AString ---------- void AString::InsertSpace(unsigned &index, unsigned size) { Grow(size); MoveItems(index + size, index); } void AString::ReAlloc(unsigned newLimit) { if (newLimit < _len || newLimit >= 0x20000000) throw 20130220; // MY_STRING_REALLOC(_chars, char, newLimit + 1, _len + 1); char *newBuf = MY_STRING_NEW(char, newLimit + 1); memcpy(newBuf, _chars, (size_t)(_len + 1)); \ MY_STRING_DELETE(_chars); _chars = newBuf; _limit = newLimit; } void AString::SetStartLen(unsigned len) { _chars = 0; _chars = MY_STRING_NEW(char, len + 1); _len = len; _limit = len; } void AString::Grow_1() { unsigned next = _len; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } void AString::Grow(unsigned n) { unsigned freeSize = _limit - _len; if (n <= freeSize) return; unsigned next = _len + n; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } /* AString::AString(unsigned num, const char *s) { unsigned len = MyStringLen(s); if (num > len) num = len; SetStartLen(num); memcpy(_chars, s, num); _chars[num] = 0; } */ AString::AString(unsigned num, const AString &s) { if (num > s._len) num = s._len; SetStartLen(num); memcpy(_chars, s._chars, num); _chars[num] = 0; } AString::AString(const AString &s, char c) { SetStartLen(s.Len() + 1); char *chars = _chars; unsigned len = s.Len(); memcpy(chars, s, len); chars[len] = c; chars[len + 1] = 0; } AString::AString(const char *s1, unsigned num1, const char *s2, unsigned num2) { SetStartLen(num1 + num2); char *chars = _chars; memcpy(chars, s1, num1); memcpy(chars + num1, s2, num2 + 1); } AString operator+(const AString &s1, const AString &s2) { return AString(s1, s1.Len(), s2, s2.Len()); } AString operator+(const AString &s1, const char *s2) { return AString(s1, s1.Len(), s2, MyStringLen(s2)); } AString operator+(const char *s1, const AString &s2) { return AString(s1, MyStringLen(s1), s2, s2.Len()); } AString::AString() { _chars = 0; _chars = MY_STRING_NEW(char, 4); _len = 0; _limit = 4 - 1; _chars[0] = 0; } AString::AString(char c) { SetStartLen(1); _chars[0] = c; _chars[1] = 0; } AString::AString(const char *s) { SetStartLen(MyStringLen(s)); MyStringCopy(_chars, s); } AString::AString(const AString &s) { SetStartLen(s._len); MyStringCopy(_chars, s._chars); } AString &AString::operator=(char c) { if (1 > _limit) { char *newBuf = MY_STRING_NEW(char, 1 + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = 1; } _len = 1; _chars[0] = c; _chars[1] = 0; return *this; } AString &AString::operator=(const char *s) { unsigned len = MyStringLen(s); if (len > _limit) { char *newBuf = MY_STRING_NEW(char, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s); return *this; } AString &AString::operator=(const AString &s) { if (&s == this) return *this; unsigned len = s._len; if (len > _limit) { char *newBuf = MY_STRING_NEW(char, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s._chars); return *this; } AString &AString::operator+=(const char *s) { unsigned len = MyStringLen(s); Grow(len); MyStringCopy(_chars + _len, s); _len += len; return *this; } AString &AString::operator+=(const AString &s) { Grow(s._len); MyStringCopy(_chars + _len, s._chars); _len += s._len; return *this; } void AString::SetFrom(const char *s, unsigned len) // no check { if (len > _limit) { char *newBuf = MY_STRING_NEW(char, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } memcpy(_chars, s, len); _chars[len] = 0; _len = len; } int AString::Find(const AString &s, unsigned startIndex) const throw() { if (s.IsEmpty()) return startIndex; for (; startIndex < _len; startIndex++) { unsigned j; for (j = 0; j < s._len && startIndex + j < _len; j++) if (_chars[startIndex + j] != s._chars[j]) break; if (j == s._len) return (int)startIndex; } return -1; } int AString::ReverseFind(char c) const throw() { if (_len == 0) return -1; const char *p = _chars + _len - 1; for (;;) { if (*p == c) return (int)(p - _chars); if (p == _chars) return -1; p--; // p = GetPrevCharPointer(_chars, p); } } void AString::TrimLeft() throw() { const char *p = _chars; for (;; p++) { char c = *p; if (c != ' ' && c != '\n' && c != '\t') break; } unsigned pos = (unsigned)(p - _chars); if (pos != 0) { MoveItems(0, pos); _len -= pos; } } void AString::TrimRight() throw() { const char *p = _chars; int i; for (i = _len - 1; i >= 0; i--) { char c = p[i]; if (c != ' ' && c != '\n' && c != '\t') break; } i++; if ((unsigned)i != _len) { _chars[i] = 0; _len = i; } } void AString::InsertAtFront(char c) { if (_limit == _len) Grow_1(); MoveItems(1, 0); _chars[0] = c; _len++; } /* void AString::Insert(unsigned index, char c) { InsertSpace(index, 1); _chars[index] = c; _len++; } */ void AString::Insert(unsigned index, const char *s) { unsigned num = MyStringLen(s); if (num != 0) { InsertSpace(index, num); memcpy(_chars + index, s, num); _len += num; } } void AString::Insert(unsigned index, const AString &s) { unsigned num = s.Len(); if (num != 0) { InsertSpace(index, num); memcpy(_chars + index, s, num); _len += num; } } void AString::RemoveChar(char ch) throw() { int pos = Find(ch); if (pos < 0) return; const char *src = _chars; char *dest = _chars + pos; pos++; unsigned len = _len; for (; (unsigned)pos < len; pos++) { char c = src[(unsigned)pos]; if (c != ch) *dest++ = c; } *dest = 0; _len = (unsigned)(dest - _chars); } // !!!!!!!!!!!!!!! test it if newChar = '\0' void AString::Replace(char oldChar, char newChar) throw() { if (oldChar == newChar) return; // 0; // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldChar, pos); if (pos < 0) break; _chars[pos] = newChar; pos++; // number++; } return; // number; } void AString::Replace(const AString &oldString, const AString &newString) { if (oldString.IsEmpty()) return; // 0; if (oldString == newString) return; // 0; unsigned oldLen = oldString.Len(); unsigned newLen = newString.Len(); // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldString, pos); if (pos < 0) break; Delete(pos, oldLen); Insert(pos, newString); pos += newLen; // number++; } // return number; } void AString::Delete(unsigned index) throw() { MoveItems(index, index + 1); _len--; } void AString::Delete(unsigned index, unsigned count) throw() { if (index + count > _len) count = _len - index; if (count > 0) { MoveItems(index, index + count); _len -= count; } } void AString::DeleteFrontal(unsigned num) throw() { if (num != 0) { MoveItems(0, num); _len -= num; } } /* AString operator+(const AString &s1, const AString &s2) { AString result(s1); result += s2; return result; } AString operator+(const AString &s, const char *chars) { AString result(s); result += chars; return result; } AString operator+(const char *chars, const AString &s) { AString result(chars); result += s; return result; } AString operator+(const AString &s, char c) { AString result(s); result += c; return result; } */ /* AString operator+(char c, const AString &s) { AString result(c); result += s; return result; } */ // ---------- UString ---------- void UString::InsertSpace(unsigned index, unsigned size) { Grow(size); MoveItems(index + size, index); } void UString::ReAlloc(unsigned newLimit) { if (newLimit < _len || newLimit >= 0x20000000) throw 20130221; // MY_STRING_REALLOC(_chars, wchar_t, newLimit + 1, _len + 1); wchar_t *newBuf = MY_STRING_NEW(wchar_t, newLimit + 1); wmemcpy(newBuf, _chars, _len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = newLimit; } void UString::SetStartLen(unsigned len) { _chars = 0; _chars = MY_STRING_NEW(wchar_t, len + 1); _len = len; _limit = len; } void UString::Grow_1() { unsigned next = _len; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } void UString::Grow(unsigned n) { unsigned freeSize = _limit - _len; if (n <= freeSize) return; unsigned next = _len + n; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } UString::UString(unsigned num, const wchar_t *s) { unsigned len = MyStringLen(s); if (num > len) num = len; SetStartLen(num); wmemcpy(_chars, s, num); _chars[num] = 0; } UString::UString(unsigned num, const UString &s) { if (num > s._len) num = s._len; SetStartLen(num); wmemcpy(_chars, s._chars, num); _chars[num] = 0; } UString::UString(const UString &s, wchar_t c) { SetStartLen(s.Len() + 1); wchar_t *chars = _chars; unsigned len = s.Len(); wmemcpy(chars, s, len); chars[len] = c; chars[len + 1] = 0; } UString::UString(const wchar_t *s1, unsigned num1, const wchar_t *s2, unsigned num2) { SetStartLen(num1 + num2); wchar_t *chars = _chars; wmemcpy(chars, s1, num1); wmemcpy(chars + num1, s2, num2 + 1); } UString operator+(const UString &s1, const UString &s2) { return UString(s1, s1.Len(), s2, s2.Len()); } UString operator+(const UString &s1, const wchar_t *s2) { return UString(s1, s1.Len(), s2, MyStringLen(s2)); } UString operator+(const wchar_t *s1, const UString &s2) { return UString(s1, MyStringLen(s1), s2, s2.Len()); } UString::UString() { _chars = 0; _chars = MY_STRING_NEW(wchar_t, 4); _len = 0; _limit = 4 - 1; _chars[0] = 0; } UString::UString(wchar_t c) { SetStartLen(1); _chars[0] = c; _chars[1] = 0; } UString::UString(const wchar_t *s) { SetStartLen(MyStringLen(s)); MyStringCopy(_chars, s); } UString::UString(const UString &s) { SetStartLen(s._len); MyStringCopy(_chars, s._chars); } UString &UString::operator=(wchar_t c) { if (1 > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, 1 + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = 1; } _len = 1; _chars[0] = c; _chars[1] = 0; return *this; } UString &UString::operator=(const wchar_t *s) { unsigned len = MyStringLen(s); if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s); return *this; } UString &UString::operator=(const UString &s) { if (&s == this) return *this; unsigned len = s._len; if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s._chars); return *this; } UString &UString::operator+=(const wchar_t *s) { unsigned len = MyStringLen(s); Grow(len); MyStringCopy(_chars + _len, s); _len += len; return *this; } UString &UString::operator+=(const UString &s) { Grow(s._len); MyStringCopy(_chars + _len, s._chars); _len += s._len; return *this; } void UString::SetFrom(const wchar_t *s, unsigned len) // no check { if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } wmemcpy(_chars, s, len); _chars[len] = 0; _len = len; } void UString::SetFromAscii(const char *s) { unsigned len = MyStringLen(s); if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } wchar_t *chars = _chars; for (unsigned i = 0; i < len; i++) chars[i] = s[i]; chars[len] = 0; _len = len; } void UString::AddAsciiStr(const char *s) { unsigned len = MyStringLen(s); Grow(len); wchar_t *chars = _chars + _len; for (unsigned i = 0; i < len; i++) chars[i] = s[i]; chars[len] = 0; _len += len; } int UString::Find(const UString &s, unsigned startIndex) const throw() { if (s.IsEmpty()) return startIndex; for (; startIndex < _len; startIndex++) { unsigned j; for (j = 0; j < s._len && startIndex + j < _len; j++) if (_chars[startIndex + j] != s._chars[j]) break; if (j == s._len) return (int)startIndex; } return -1; } int UString::ReverseFind(wchar_t c) const throw() { if (_len == 0) return -1; const wchar_t *p = _chars + _len - 1; for (;;) { if (*p == c) return (int)(p - _chars); if (p == _chars) return -1; p--; } } void UString::TrimLeft() throw() { const wchar_t *p = _chars; for (;; p++) { wchar_t c = *p; if (c != ' ' && c != '\n' && c != '\t') break; } unsigned pos = (unsigned)(p - _chars); if (pos != 0) { MoveItems(0, pos); _len -= pos; } } void UString::TrimRight() throw() { const wchar_t *p = _chars; int i; for (i = _len - 1; i >= 0; i--) { wchar_t c = p[i]; if (c != ' ' && c != '\n' && c != '\t') break; } i++; if ((unsigned)i != _len) { _chars[i] = 0; _len = i; } } void UString::InsertAtFront(wchar_t c) { if (_limit == _len) Grow_1(); MoveItems(1, 0); _chars[0] = c; _len++; } /* void UString::Insert(unsigned index, wchar_t c) { InsertSpace(index, 1); _chars[index] = c; _len++; } */ void UString::Insert(unsigned index, const wchar_t *s) { unsigned num = MyStringLen(s); if (num != 0) { InsertSpace(index, num); wmemcpy(_chars + index, s, num); _len += num; } } void UString::Insert(unsigned index, const UString &s) { unsigned num = s.Len(); if (num != 0) { InsertSpace(index, num); wmemcpy(_chars + index, s, num); _len += num; } } void UString::RemoveChar(wchar_t ch) throw() { int pos = Find(ch); if (pos < 0) return; const wchar_t *src = _chars; wchar_t *dest = _chars + pos; pos++; unsigned len = _len; for (; (unsigned)pos < len; pos++) { wchar_t c = src[(unsigned)pos]; if (c != ch) *dest++ = c; } *dest = 0; _len = (unsigned)(dest - _chars); } // !!!!!!!!!!!!!!! test it if newChar = '\0' void UString::Replace(wchar_t oldChar, wchar_t newChar) throw() { if (oldChar == newChar) return; // 0; // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldChar, pos); if (pos < 0) break; _chars[pos] = newChar; pos++; // number++; } return; // number; } void UString::Replace(const UString &oldString, const UString &newString) { if (oldString.IsEmpty()) return; // 0; if (oldString == newString) return; // 0; unsigned oldLen = oldString.Len(); unsigned newLen = newString.Len(); // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldString, pos); if (pos < 0) break; Delete(pos, oldLen); Insert(pos, newString); pos += newLen; // number++; } // return number; } void UString::Delete(unsigned index) throw() { MoveItems(index, index + 1); _len--; } void UString::Delete(unsigned index, unsigned count) throw() { if (index + count > _len) count = _len - index; if (count > 0) { MoveItems(index, index + count); _len -= count; } } void UString::DeleteFrontal(unsigned num) throw() { if (num != 0) { MoveItems(0, num); _len -= num; } } // ---------------------------------------- /* int MyStringCompareNoCase(const char *s1, const char *s2) { return MyStringCompareNoCase(MultiByteToUnicodeString(s1), MultiByteToUnicodeString(s2)); } */ static inline UINT GetCurrentCodePage() { #if defined(UNDER_CE) || !defined(_WIN32) return CP_ACP; #else return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; #endif } #ifdef USE_UNICODE_FSTRING #ifndef _UNICODE AString fs2fas(CFSTR s) { return UnicodeStringToMultiByte(s, GetCurrentCodePage()); } FString fas2fs(const AString &s) { return MultiByteToUnicodeString(s, GetCurrentCodePage()); } #endif #else UString fs2us(const FString &s) { return MultiByteToUnicodeString((AString)s, GetCurrentCodePage()); } FString us2fs(const wchar_t *s) { return UnicodeStringToMultiByte(s, GetCurrentCodePage()); } #endif src/libs/7zip/unix/CPP/Common/MyString.h000066400000000000000000000373501325366651500202750ustar00rootroot00000000000000// Common/String.h #ifndef __COMMON_STRING_H #define __COMMON_STRING_H #include #ifndef _WIN32 #include #include #endif #include "MyTypes.h" #include "MyVector.h" inline unsigned MyStringLen(const char *s) { unsigned i; for (i = 0; s[i] != 0; i++); return i; } inline void MyStringCopy(char *dest, const char *src) { while ((*dest++ = *src++) != 0); } inline char *MyStpCpy(char *dest, const char *src) { for (;;) { char c = *src; *dest = c; if (c == 0) return dest; src++; dest++; } } inline unsigned MyStringLen(const wchar_t *s) { unsigned i; for (i = 0; s[i] != 0; i++); return i; } inline void MyStringCopy(wchar_t *dest, const wchar_t *src) { while ((*dest++ = *src++) != 0); } int FindCharPosInString(const char *s, char c) throw(); int FindCharPosInString(const wchar_t *s, wchar_t c) throw(); #ifdef _WIN32 #ifndef _UNICODE #define STRING_UNICODE_THROW #endif #endif #ifndef STRING_UNICODE_THROW #define STRING_UNICODE_THROW throw() #endif /* inline char MyCharUpper_Ascii(char c) { if (c >= 'a' && c <= 'z') return (char)(c - 0x20); return c; } inline wchar_t MyCharUpper_Ascii(wchar_t c) { if (c >= 'a' && c <= 'z') return (wchar_t)(c - 0x20); return c; } */ inline char MyCharLower_Ascii(char c) { if (c >= 'A' && c <= 'Z') return (char)(c + 0x20); return c; } inline wchar_t MyCharLower_Ascii(wchar_t c) { if (c >= 'A' && c <= 'Z') return (wchar_t)(c + 0x20); return c; } wchar_t MyCharUpper_WIN(wchar_t c) throw(); inline wchar_t MyCharUpper(wchar_t c) throw() { if (c < 'a') return c; if (c <= 'z') return (wchar_t)(c - 0x20); if (c <= 0x7F) return c; #ifdef _WIN32 #ifdef _UNICODE return (wchar_t)(unsigned)(UINT_PTR)CharUpperW((LPWSTR)(UINT_PTR)(unsigned)c); #else return (wchar_t)MyCharUpper_WIN(c); #endif #else return (wchar_t)towupper(c); #endif } /* wchar_t MyCharLower_WIN(wchar_t c) throw(); inline wchar_t MyCharLower(wchar_t c) throw() { if (c < 'A') return c; if (c <= 'Z') return (wchar_t)(c + 0x20); if (c <= 0x7F) return c; #ifdef _WIN32 #ifdef _UNICODE return (wchar_t)(unsigned)(UINT_PTR)CharLowerW((LPWSTR)(UINT_PTR)(unsigned)c); #else return (wchar_t)MyCharLower_WIN(c); #endif #else return (wchar_t)tolower(c); #endif } */ // char *MyStringUpper(char *s) throw(); // char *MyStringLower(char *s) throw(); // void MyStringUpper_Ascii(wchar_t *s) throw(); void MyStringLower_Ascii(wchar_t *s) throw(); // wchar_t *MyStringUpper(wchar_t *s) STRING_UNICODE_THROW; // wchar_t *MyStringLower(wchar_t *s) STRING_UNICODE_THROW; bool StringsAreEqualNoCase(const wchar_t *s1, const wchar_t *s2) throw(); bool IsString1PrefixedByString2(const char *s1, const char *s2) throw(); bool IsString1PrefixedByString2(const wchar_t *s1, const wchar_t *s2) throw(); int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2) throw(); int MyStringCompareNoCase_N(const wchar_t *s1, const wchar_t *s2, unsigned num) throw(); // ---------- ASCII ---------- // char values in ASCII strings must be less then 128 bool StringsAreEqual_Ascii(const wchar_t *u, const char *a) throw(); bool StringsAreEqualNoCase_Ascii(const char *s1, const char *s2) throw(); bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const char *s2) throw(); bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const wchar_t *s2) throw(); #define MY_STRING_DELETE(_p_) delete []_p_; // #define MY_STRING_DELETE(_p_) my_delete(_p_); class AString { char *_chars; unsigned _len; unsigned _limit; void MoveItems(unsigned dest, unsigned src) { memmove(_chars + dest, _chars + src, (size_t)(_len - src + 1) * sizeof(char)); } void InsertSpace(unsigned &index, unsigned size); void ReAlloc(unsigned newLimit); void SetStartLen(unsigned len); void Grow_1(); void Grow(unsigned n); // AString(unsigned num, const char *s); AString(unsigned num, const AString &s); AString(const AString &s, char c); // it's for String + char AString(const char *s1, unsigned num1, const char *s2, unsigned num2); friend AString operator+(const AString &s, char c) { return AString(s, c); } ; // friend AString operator+(char c, const AString &s); // is not supported friend AString operator+(const AString &s1, const AString &s2); friend AString operator+(const AString &s1, const char *s2); friend AString operator+(const char *s1, const AString &s2); public: AString(); AString(char c); AString(const char *s); AString(const AString &s); ~AString() { MY_STRING_DELETE(_chars); } unsigned Len() const { return _len; } bool IsEmpty() const { return _len == 0; } void Empty() { _len = 0; _chars[0] = 0; } operator const char *() const { return _chars; } const char *Ptr() const { return _chars; } const char *Ptr(unsigned pos) const { return _chars + pos; } const char *RightPtr(unsigned num) const { return _chars + _len - num; } char Back() const { return _chars[_len - 1]; } void ReplaceOneCharAtPos(unsigned pos, char c) { _chars[pos] = c; } // The minimum size of the character buffer in characters. // This value does not include space for a null terminator. char *GetBuffer(unsigned minBufLen) { if (minBufLen > _limit) ReAlloc(minBufLen); return _chars; } void ReleaseBuffer() { ReleaseBuffer(MyStringLen(_chars)); } void ReleaseBuffer(unsigned newLen) { _len = newLen; _chars[newLen] = 0; } AString &operator=(char c); AString &operator=(const char *s); AString &operator=(const AString &s); AString &operator+=(char c) { if (_limit == _len) Grow_1(); unsigned len = _len; char *chars = _chars; chars[len++] = c; chars[len] = 0; _len = len; return *this; } AString &operator+=(const char *s); AString &operator+=(const AString &s); void SetFrom(const char *s, unsigned len); // no check // AString Mid(unsigned startIndex, unsigned count) const { return AString(count, _chars + startIndex); } AString Left(unsigned count) const { return AString(count, *this); } // void MakeUpper() { MyStringUpper(_chars); } // void MakeLower() { MyStringLower(_chars); } // int Compare(const char *s) const { return MyStringCompare(_chars, s); } // int Compare(const AString &s) const { return MyStringCompare(_chars, s._chars); } // int CompareNoCase(const char *s) const { return MyStringCompareNoCase(_chars, s); } // int CompareNoCase(const AString &s) const { return MyStringCompareNoCase(_chars, s._chars); } bool IsPrefixedBy(const char *s) const { return IsString1PrefixedByString2(_chars, s); } bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw(); int Find(char c) const { return FindCharPosInString(_chars, c); } int Find(char c, unsigned startIndex) const { int pos = FindCharPosInString(_chars + startIndex, c); return pos < 0 ? -1 : (int)startIndex + pos; } int ReverseFind(char c) const throw(); int Find(const AString &s) const { return Find(s, 0); } int Find(const AString &s, unsigned startIndex) const throw(); void TrimLeft() throw(); void TrimRight() throw(); void Trim() { TrimRight(); TrimLeft(); } void InsertAtFront(char c); // void Insert(unsigned index, char c); void Insert(unsigned index, const char *s); void Insert(unsigned index, const AString &s); void RemoveChar(char ch) throw(); void Replace(char oldChar, char newChar) throw(); void Replace(const AString &oldString, const AString &newString); void Delete(unsigned index) throw(); void Delete(unsigned index, unsigned count) throw(); void DeleteFrontal(unsigned num) throw(); void DeleteBack() { _chars[--_len] = 0; } void DeleteFrom(unsigned index) { if (index < _len) { _len = index; _chars[index] = 0; } } }; bool operator<(const AString &s1, const AString &s2); bool operator>(const AString &s1, const AString &s2); /* bool operator==(const AString &s1, const AString &s2); bool operator==(const AString &s1, const char *s2); bool operator==(const char *s1, const AString &s2); bool operator!=(const AString &s1, const AString &s2); bool operator!=(const AString &s1, const char *s2); bool operator!=(const char *s1, const AString &s2); */ inline bool operator==(const AString &s1, const AString &s2) { return s1.Len() == s2.Len() && strcmp(s1, s2) == 0; } inline bool operator==(const AString &s1, const char *s2) { return strcmp(s1, s2) == 0; } inline bool operator==(const char *s1, const AString &s2) { return strcmp(s1, s2) == 0; } inline bool operator!=(const AString &s1, const AString &s2) { return s1.Len() != s2.Len() || strcmp(s1, s2) != 0; } inline bool operator!=(const AString &s1, const char *s2) { return strcmp(s1, s2) != 0; } inline bool operator!=(const char *s1, const AString &s2) { return strcmp(s1, s2) != 0; } class UString { wchar_t *_chars; unsigned _len; unsigned _limit; void MoveItems(unsigned dest, unsigned src) { memmove(_chars + dest, _chars + src, (size_t)(_len - src + 1) * sizeof(wchar_t)); } void InsertSpace(unsigned index, unsigned size); void ReAlloc(unsigned newLimit); void SetStartLen(unsigned len); void Grow_1(); void Grow(unsigned n); UString(unsigned num, const wchar_t *s); // for Mid UString(unsigned num, const UString &s); // for Left UString(const UString &s, wchar_t c); // it's for String + char UString(const wchar_t *s1, unsigned num1, const wchar_t *s2, unsigned num2); friend UString operator+(const UString &s, wchar_t c) { return UString(s, c); } ; // friend UString operator+(wchar_t c, const UString &s); // is not supported friend UString operator+(const UString &s1, const UString &s2); friend UString operator+(const UString &s1, const wchar_t *s2); friend UString operator+(const wchar_t *s1, const UString &s2); public: UString(); UString(wchar_t c); UString(const wchar_t *s); UString(const UString &s); ~UString() { MY_STRING_DELETE(_chars); } unsigned Len() const { return _len; } bool IsEmpty() const { return _len == 0; } void Empty() { _len = 0; _chars[0] = 0; } operator const wchar_t *() const { return _chars; } const wchar_t *Ptr() const { return _chars; } const wchar_t *Ptr(unsigned pos) const { return _chars + pos; } const wchar_t *RightPtr(unsigned num) const { return _chars + _len - num; } wchar_t Back() const { return _chars[_len - 1]; } void ReplaceOneCharAtPos(unsigned pos, wchar_t c) { _chars[pos] = c; } // The minimum size of the character buffer in characters. // This value does not include space for a null terminator. wchar_t *GetBuffer(unsigned minBufLen) { if (minBufLen > _limit) ReAlloc(minBufLen); return _chars; } void ReleaseBuffer() { ReleaseBuffer(MyStringLen(_chars)); } void ReleaseBuffer(unsigned newLen) { _len = newLen; _chars[newLen] = 0; } UString &operator=(wchar_t c); UString &operator=(const wchar_t *s); UString &operator=(const UString &s); UString &operator+=(wchar_t c) { if (_limit == _len) Grow_1(); unsigned len = _len; wchar_t *chars = _chars; chars[len++] = c; chars[len] = 0; _len = len; return *this; } UString &operator+=(const wchar_t *s); UString &operator+=(const UString &s); void SetFrom(const wchar_t *s, unsigned len); // no check void SetFromAscii(const char *s); void AddAsciiStr(const char *s); UString Mid(unsigned startIndex, unsigned count) const { return UString(count, _chars + startIndex); } UString Left(unsigned count) const { return UString(count, *this); } // void MakeUpper() { MyStringUpper(_chars); } // void MakeUpper() { MyStringUpper_Ascii(_chars); } // void MakeUpper_Ascii() { MyStringUpper_Ascii(_chars); } void MakeLower_Ascii() { MyStringLower_Ascii(_chars); } bool IsEqualTo(const char *s) const { return StringsAreEqual_Ascii(_chars, s); } bool IsEqualToNoCase(const wchar_t *s) const { return StringsAreEqualNoCase(_chars, s); } int Compare(const wchar_t *s) const { return wcscmp(_chars, s); } // int Compare(const UString &s) const { return MyStringCompare(_chars, s._chars); } // int CompareNoCase(const wchar_t *s) const { return MyStringCompareNoCase(_chars, s); } // int CompareNoCase(const UString &s) const { return MyStringCompareNoCase(_chars, s._chars); } bool IsPrefixedBy(const wchar_t *s) const { return IsString1PrefixedByString2(_chars, s); }; bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw(); int Find(wchar_t c) const { return FindCharPosInString(_chars, c); } int Find(wchar_t c, unsigned startIndex) const { int pos = FindCharPosInString(_chars + startIndex, c); return pos < 0 ? -1 : (int)startIndex + pos; } int Find(const UString &s) const { return Find(s, 0); } int Find(const UString &s, unsigned startIndex) const throw(); int ReverseFind(wchar_t c) const throw(); void TrimLeft() throw(); void TrimRight() throw(); void Trim() { TrimRight(); TrimLeft(); } void InsertAtFront(wchar_t c); // void Insert(unsigned index, wchar_t c); void Insert(unsigned index, const wchar_t *s); void Insert(unsigned index, const UString &s); void RemoveChar(wchar_t ch) throw(); void Replace(wchar_t oldChar, wchar_t newChar) throw(); void Replace(const UString &oldString, const UString &newString); void Delete(unsigned index) throw(); void Delete(unsigned index, unsigned count) throw(); void DeleteFrontal(unsigned num) throw(); void DeleteBack() { _chars[--_len] = 0; } void DeleteFrom(unsigned index) { if (index < _len) { _len = index; _chars[index] = 0; } } }; bool operator<(const UString &s1, const UString &s2); bool operator>(const UString &s1, const UString &s2); inline bool operator==(const UString &s1, const UString &s2) { return s1.Len() == s2.Len() && wcscmp(s1, s2) == 0; } inline bool operator==(const UString &s1, const wchar_t *s2) { return wcscmp(s1, s2) == 0; } inline bool operator==(const wchar_t *s1, const UString &s2) { return wcscmp(s1, s2) == 0; } inline bool operator!=(const UString &s1, const UString &s2) { return s1.Len() != s2.Len() || wcscmp(s1, s2) != 0; } inline bool operator!=(const UString &s1, const wchar_t *s2) { return wcscmp(s1, s2) != 0; } inline bool operator!=(const wchar_t *s1, const UString &s2) { return wcscmp(s1, s2) != 0; } typedef CObjectVector AStringVector; typedef CObjectVector UStringVector; #ifdef _UNICODE typedef UString CSysString; #else typedef AString CSysString; #endif typedef CObjectVector CSysStringVector; // ---------- FString ---------- // #ifdef _WIN32 #define USE_UNICODE_FSTRING // #endif #ifdef USE_UNICODE_FSTRING #define __FTEXT(quote) L##quote typedef wchar_t FChar; typedef UString FString; #define fs2us(_x_) (_x_) #define us2fs(_x_) (_x_) FString fas2fs(const AString &s); AString fs2fas(const FChar *s); #else #define __FTEXT(quote) quote typedef char FChar; typedef AString FString; UString fs2us(const FString &s); FString us2fs(const wchar_t *s); #define fas2fs(_x_) (_x_) #define fs2fas(_x_) (_x_) #endif #define FTEXT(quote) __FTEXT(quote) #define FCHAR_PATH_SEPARATOR FTEXT(CHAR_PATH_SEPARATOR) #define FSTRING_PATH_SEPARATOR FTEXT(STRING_PATH_SEPARATOR) #define FCHAR_ANY_MASK FTEXT('*') #define FSTRING_ANY_MASK FTEXT("*") typedef const FChar *CFSTR; typedef CObjectVector FStringVector; #endif src/libs/7zip/unix/CPP/Common/MyTypes.h000066400000000000000000000006201325366651500201210ustar00rootroot00000000000000// Common/MyTypes.h #ifndef __COMMON_MY_TYPES_H #define __COMMON_MY_TYPES_H #include "../../C/7zTypes.h" typedef int HRes; #ifdef __cplusplus struct CBoolPair { bool Val; bool Def; CBoolPair(): Val(false), Def(false) {} void Init() { Val = false; Def = false; } void SetTrueTrue() { Val = true; Def = true; } }; #endif #endif src/libs/7zip/unix/CPP/Common/MyUnknown.h000066400000000000000000000002601325366651500204540ustar00rootroot00000000000000// MyUnknown.h #ifndef __MY_UNKNOWN_H #define __MY_UNKNOWN_H #ifdef _WIN32 #include #include #else #include "MyWindows.h" #endif #endif src/libs/7zip/unix/CPP/Common/MyVector.h000066400000000000000000000320401325366651500202600ustar00rootroot00000000000000// Common/MyVector.h #ifndef __COMMON_MY_VECTOR_H #define __COMMON_MY_VECTOR_H template class CRecordVector { T *_items; unsigned _size; unsigned _capacity; void MoveItems(unsigned destIndex, unsigned srcIndex) { memmove(_items + destIndex, _items + srcIndex, (size_t)(_size - srcIndex) * (size_t)sizeof(T)); } void ReserveOnePosition() { if (_size == _capacity) { unsigned newCapacity = _capacity + (_capacity >> 2) + 1; T *p = new T[newCapacity]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); delete []_items; _items = p; _capacity = newCapacity; } } public: CRecordVector(): _items(0), _size(0), _capacity(0) {} CRecordVector(const CRecordVector &v): _items(0), _size(0), _capacity(0) { unsigned size = v.Size(); if (size != 0) { _items = new T[size]; _size = size; _capacity = size; memcpy(_items, v._items, (size_t)size * (size_t)sizeof(T)); } } unsigned Size() const { return _size; } bool IsEmpty() const { return _size == 0; } void ConstructReserve(unsigned size) { if (size != 0) { _items = new T[size]; _capacity = size; } } void Reserve(unsigned newCapacity) { if (newCapacity > _capacity) { T *p = new T[newCapacity]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); delete []_items; _items = p; _capacity = newCapacity; } } void ClearAndReserve(unsigned newCapacity) { Clear(); if (newCapacity > _capacity) { delete []_items; _items = NULL; _capacity = 0; _items = new T[newCapacity]; _capacity = newCapacity; } } void ClearAndSetSize(unsigned newSize) { ClearAndReserve(newSize); _size = newSize; } void ChangeSize_KeepData(unsigned newSize) { if (newSize > _capacity) { T *p = new T[newSize]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); delete []_items; _items = p; _capacity = newSize; } _size = newSize; } void ReserveDown() { if (_size == _capacity) return; T *p = NULL; if (_size != 0) { p = new T[_size]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); } delete []_items; _items = p; _capacity = _size; } ~CRecordVector() { delete []_items; } void ClearAndFree() { delete []_items; _items = NULL; _size = 0; _capacity = 0; } void Clear() { _size = 0; } void DeleteBack() { _size--; } void DeleteFrom(unsigned index) { // if (index <= _size) _size = index; } void DeleteFrontal(unsigned num) { if (num != 0) { MoveItems(0, num); _size -= num; } } void Delete(unsigned index) { MoveItems(index, index + 1); _size -= 1; } /* void Delete(unsigned index, unsigned num) { if (num > 0) { MoveItems(index, index + num); _size -= num; } } */ CRecordVector& operator=(const CRecordVector &v) { unsigned size = v.Size(); if (size > _capacity) { delete []_items; _capacity = 0; _size = 0; _items = NULL; _items = new T[size]; _capacity = size; } _size = size; memcpy(_items, v._items, (size_t)size * (size_t)sizeof(T)); return *this; } CRecordVector& operator+=(const CRecordVector &v) { unsigned size = v.Size(); Reserve(_size + size); memcpy(_items + _size, v._items, (size_t)size * (size_t)sizeof(T)); _size += size; return *this; } unsigned Add(const T item) { ReserveOnePosition(); _items[_size] = item; return _size++; } void AddInReserved(const T item) { _items[_size++] = item; } void Insert(unsigned index, const T item) { ReserveOnePosition(); MoveItems(index + 1, index); _items[index] = item; _size++; } void MoveToFront(unsigned index) { if (index != 0) { T temp = _items[index]; memmove(_items + 1, _items, (size_t)index * (size_t)sizeof(T)); _items[0] = temp; } } const T& operator[](unsigned index) const { return _items[index]; } T& operator[](unsigned index) { return _items[index]; } const T& Front() const { return _items[0]; } T& Front() { return _items[0]; } const T& Back() const { return _items[_size - 1]; } T& Back() { return _items[_size - 1]; } /* void Swap(unsigned i, unsigned j) { T temp = _items[i]; _items[i] = _items[j]; _items[j] = temp; } */ int FindInSorted(const T item, unsigned left, unsigned right) const { while (left != right) { unsigned mid = (left + right) / 2; const T midVal = (*this)[mid]; if (item == midVal) return mid; if (item < midVal) right = mid; else left = mid + 1; } return -1; } int FindInSorted2(const T &item, unsigned left, unsigned right) const { while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } return -1; } int FindInSorted(const T item) const { return FindInSorted(item, 0, _size); } int FindInSorted2(const T &item) const { return FindInSorted2(item, 0, _size); } unsigned AddToUniqueSorted(const T item) { unsigned left = 0, right = _size; while (left != right) { unsigned mid = (left + right) / 2; const T midVal = (*this)[mid]; if (item == midVal) return mid; if (item < midVal) right = mid; else left = mid + 1; } Insert(right, item); return right; } unsigned AddToUniqueSorted2(const T &item) { unsigned left = 0, right = _size; while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } Insert(right, item); return right; } static void SortRefDown(T* p, unsigned k, unsigned size, int (*compare)(const T*, const T*, void *), void *param) { T temp = p[k]; for (;;) { unsigned s = (k << 1); if (s > size) break; if (s < size && compare(p + s + 1, p + s, param) > 0) s++; if (compare(&temp, p + s, param) >= 0) break; p[k] = p[s]; k = s; } p[k] = temp; } void Sort(int (*compare)(const T*, const T*, void *), void *param) { unsigned size = _size; if (size <= 1) return; T* p = (&Front()) - 1; { unsigned i = size >> 1; do SortRefDown(p, i, size, compare, param); while (--i != 0); } do { T temp = p[size]; p[size--] = p[1]; p[1] = temp; SortRefDown(p, 1, size, compare, param); } while (size > 1); } static void SortRefDown2(T* p, unsigned k, unsigned size) { T temp = p[k]; for (;;) { unsigned s = (k << 1); if (s > size) break; if (s < size && p[s + 1].Compare(p[s]) > 0) s++; if (temp.Compare(p[s]) >= 0) break; p[k] = p[s]; k = s; } p[k] = temp; } void Sort2() { unsigned size = _size; if (size <= 1) return; T* p = (&Front()) - 1; { unsigned i = size >> 1; do SortRefDown2(p, i, size); while (--i != 0); } do { T temp = p[size]; p[size--] = p[1]; p[1] = temp; SortRefDown2(p, 1, size); } while (size > 1); } }; typedef CRecordVector CIntVector; typedef CRecordVector CUIntVector; typedef CRecordVector CBoolVector; typedef CRecordVector CByteVector; typedef CRecordVector CPointerVector; template class CObjectVector { CPointerVector _v; public: unsigned Size() const { return _v.Size(); } bool IsEmpty() const { return _v.IsEmpty(); } void ReserveDown() { _v.ReserveDown(); } // void Reserve(unsigned newCapacity) { _v.Reserve(newCapacity); } void ClearAndReserve(unsigned newCapacity) { Clear(); _v.ClearAndReserve(newCapacity); } CObjectVector() {}; CObjectVector(const CObjectVector &v) { unsigned size = v.Size(); _v.ConstructReserve(size); for (unsigned i = 0; i < size; i++) _v.AddInReserved(new T(v[i])); } CObjectVector& operator=(const CObjectVector &v) { Clear(); unsigned size = v.Size(); _v.Reserve(size); for (unsigned i = 0; i < size; i++) _v.AddInReserved(new T(v[i])); return *this; } CObjectVector& operator+=(const CObjectVector &v) { unsigned size = v.Size(); _v.Reserve(Size() + size); for (unsigned i = 0; i < size; i++) _v.AddInReserved(new T(v[i])); return *this; } const T& operator[](unsigned index) const { return *((T *)_v[index]); } T& operator[](unsigned index) { return *((T *)_v[index]); } const T& Front() const { return operator[](0); } T& Front() { return operator[](0); } const T& Back() const { return operator[](_v.Size() - 1); } T& Back() { return operator[](_v.Size() - 1); } void MoveToFront(unsigned index) { _v.MoveToFront(index); } unsigned Add(const T& item) { return _v.Add(new T(item)); } void AddInReserved(const T& item) { _v.AddInReserved(new T(item)); } T& AddNew() { T *p = new T; _v.Add(p); return *p; } T& AddNewInReserved() { T *p = new T; _v.AddInReserved(p); return *p; } void Insert(unsigned index, const T& item) { _v.Insert(index, new T(item)); } T& InsertNew(unsigned index) { T *p = new T; _v.Insert(index, p); return *p; } ~CObjectVector() { for (unsigned i = _v.Size(); i != 0;) delete (T *)_v[--i]; } void ClearAndFree() { Clear(); _v.ClearAndFree(); } void Clear() { for (unsigned i = _v.Size(); i != 0;) delete (T *)_v[--i]; _v.Clear(); } void DeleteFrom(unsigned index) { unsigned size = _v.Size(); for (unsigned i = index; i < size; i++) delete (T *)_v[i]; _v.DeleteFrom(index); } void DeleteFrontal(unsigned num) { for (unsigned i = 0; i < num; i++) delete (T *)_v[i]; _v.DeleteFrontal(num); } void DeleteBack() { delete (T *)_v[_v.Size() - 1]; _v.DeleteBack(); } void Delete(unsigned index) { delete (T *)_v[index]; _v.Delete(index); } /* void Delete(unsigned index, unsigned num) { for (unsigned i = 0; i < num; i++) delete (T *)_v[index + i]; _v.Delete(index, num); } */ /* int Find(const T& item) const { unsigned size = Size(); for (unsigned i = 0; i < size; i++) if (item == (*this)[i]) return i; return -1; } */ int FindInSorted(const T& item) const { unsigned left = 0, right = Size(); while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } return -1; } unsigned AddToUniqueSorted(const T& item) { unsigned left = 0, right = Size(); while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } Insert(right, item); return right; } /* unsigned AddToSorted(const T& item) { unsigned left = 0, right = Size(); while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) { right = mid + 1; break; } if (comp < 0) right = mid; else left = mid + 1; } Insert(right, item); return right; } */ void Sort(int (*compare)(void *const *, void *const *, void *), void *param) { _v.Sort(compare, param); } static int CompareObjectItems(void *const *a1, void *const *a2, void * /* param */) { return (*(*((const T **)a1))).Compare(*(*((const T **)a2))); } void Sort() { _v.Sort(CompareObjectItems, 0); } }; #define FOR_VECTOR(_i_, _v_) for (unsigned _i_ = 0; _i_ < (_v_).Size(); _i_++) #endif src/libs/7zip/unix/CPP/Common/MyWindows.cpp000066400000000000000000000061351325366651500210110ustar00rootroot00000000000000// MyWindows.cpp #include "StdAfx.h" #ifndef _WIN32 #include "MyWindows.h" #include "MyTypes.h" #include /* FIXED */ static inline void *AllocateForBSTR(size_t cb) { return ::malloc(cb); } static inline void FreeForBSTR(void *pv) { ::free(pv);} static UINT MyStringLen(const wchar_t *s) { UINT i; for (i = 0; s[i] != '\0'; i++); return i; } BSTR SysAllocStringByteLen(LPCSTR psz, UINT len) { // FIXED int realLen = len + sizeof(UINT) + 3; const int LEN_ADDON = sizeof(wchar_t) - 1; int realLen = len + sizeof(UINT) + sizeof(wchar_t) + LEN_ADDON; void *p = AllocateForBSTR(realLen); if (p == 0) return 0; *(UINT *)p = len; // "void *" instead of "BSTR" to avoid unaligned copy of "wchar_t" because of optimizer on Solaris void * bstr = (void *)((UINT *)p + 1); if (psz) memmove(bstr, psz, len); // psz does not always have "wchar_t" alignment. void *pb = (void *)(((Byte *)bstr) + len); memset(pb,0,sizeof(wchar_t) + LEN_ADDON); return (BSTR)bstr; } BSTR WINAPI SysAllocStringLen(const OLECHAR *sz, unsigned int numChars) // FIXME - code { UINT len = (numChars + 1) * sizeof(OLECHAR); void *p = AllocateForBSTR(len + sizeof(UINT)); if (p == 0) return 0; memset(p,0,len + sizeof(UINT)); *(UINT *)p = numChars * sizeof(OLECHAR); // FIXED void * bstr = (void *)((UINT *)p + 1); if (sz) memmove(bstr, sz, numChars * sizeof(OLECHAR)); // sz does not always have "wchar_t" alignment. return (BSTR)bstr; } BSTR SysAllocString(const OLECHAR *sz) { if (sz == 0) return 0; UINT strLen = MyStringLen(sz); UINT len = (strLen + 1) * sizeof(OLECHAR); void *p = AllocateForBSTR(len + sizeof(UINT)); if (p == 0) return 0; *(UINT *)p = strLen * sizeof(OLECHAR); // FIXED void * bstr = (void *)((UINT *)p + 1); memmove(bstr, sz, len); // sz does not always have "wchar_t" alignment. return (BSTR)bstr; } void SysFreeString(BSTR bstr) { if (bstr != 0) FreeForBSTR((UINT *)bstr - 1); } UINT SysStringByteLen(BSTR bstr) { if (bstr == 0) return 0; return *((UINT *)bstr - 1); } UINT SysStringLen(BSTR bstr) { return SysStringByteLen(bstr) / sizeof(OLECHAR); } HRESULT VariantClear(VARIANTARG *prop) { if (prop->vt == VT_BSTR) SysFreeString(prop->bstrVal); prop->vt = VT_EMPTY; return S_OK; } HRESULT VariantCopy(VARIANTARG *dest, VARIANTARG *src) { HRESULT res = ::VariantClear(dest); if (res != S_OK) return res; if (src->vt == VT_BSTR) { dest->bstrVal = SysAllocStringByteLen((LPCSTR)src->bstrVal, SysStringByteLen(src->bstrVal)); if (dest->bstrVal == 0) return E_OUTOFMEMORY; dest->vt = VT_BSTR; } else *dest = *src; return S_OK; } LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2) { if(ft1->dwHighDateTime < ft2->dwHighDateTime) return -1; if(ft1->dwHighDateTime > ft2->dwHighDateTime) return 1; if(ft1->dwLowDateTime < ft2->dwLowDateTime) return -1; if(ft1->dwLowDateTime > ft2->dwLowDateTime) return 1; return 0; } #endif src/libs/7zip/unix/CPP/Common/MyWindows.h000066400000000000000000000114351325366651500204550ustar00rootroot00000000000000// MyWindows.h #ifndef __MYWINDOWS_H #define __MYWINDOWS_H #ifdef _WIN32 #include #else #include // for wchar_t #include #include "MyGuidDef.h" typedef char CHAR; typedef unsigned char UCHAR; #undef BYTE typedef unsigned char BYTE; typedef short SHORT; typedef unsigned short USHORT; #undef WORD typedef unsigned short WORD; typedef short VARIANT_BOOL; typedef int INT; typedef Int32 INT32; typedef unsigned int UINT; typedef UInt32 UINT32; typedef INT32 LONG; // LONG, ULONG and DWORD must be 32-bit typedef UINT32 ULONG; #undef DWORD typedef UINT32 DWORD; typedef Int64 LONGLONG; typedef UInt64 ULONGLONG; typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; }; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER; typedef struct _ULARGE_INTEGER { ULONGLONG QuadPart;} ULARGE_INTEGER; typedef const CHAR *LPCSTR; typedef wchar_t WCHAR; #ifdef _UNICODE typedef WCHAR TCHAR; #define lstrcpy wcscpy #define lstrcat wcscat #define lstrlen wcslen #else typedef CHAR TCHAR; #define lstrcpy strcpy #define lstrcat strcat #define lstrlen strlen #endif #define _wcsicmp(str1,str2) MyStringCompareNoCase(str1,str2) typedef const TCHAR *LPCTSTR; typedef WCHAR OLECHAR; typedef const WCHAR *LPCWSTR; typedef OLECHAR *BSTR; typedef const OLECHAR *LPCOLESTR; typedef OLECHAR *LPOLESTR; typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; }FILETIME; #define HRESULT LONG #define FAILED(Status) ((HRESULT)(Status)<0) typedef ULONG PROPID; typedef LONG SCODE; #define S_OK ((HRESULT)0x00000000L) #define S_FALSE ((HRESULT)0x00000001L) #define E_NOTIMPL ((HRESULT)0x80004001L) #define E_NOINTERFACE ((HRESULT)0x80004002L) #define E_ABORT ((HRESULT)0x80004004L) #define E_FAIL ((HRESULT)0x80004005L) #define STG_E_INVALIDFUNCTION ((HRESULT)0x80030001L) #define E_OUTOFMEMORY ((HRESULT)0x8007000EL) #define E_INVALIDARG ((HRESULT)0x80070057L) #ifdef _MSC_VER #define STDMETHODCALLTYPE __stdcall #else #define STDMETHODCALLTYPE #endif #define STDMETHOD_(t, f) virtual t STDMETHODCALLTYPE f #define STDMETHOD(f) STDMETHOD_(HRESULT, f) #define STDMETHODIMP_(type) type STDMETHODCALLTYPE #define STDMETHODIMP STDMETHODIMP_(HRESULT) #define PURE = 0 #define MIDL_INTERFACE(x) struct #ifdef __cplusplus DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); struct IUnknown { STDMETHOD(QueryInterface) (REFIID iid, void **outObject) PURE; STDMETHOD_(ULONG, AddRef)() PURE; STDMETHOD_(ULONG, Release)() PURE; #ifndef _WIN32 virtual ~IUnknown() {} #endif }; typedef IUnknown *LPUNKNOWN; #endif #define VARIANT_TRUE ((VARIANT_BOOL)-1) #define VARIANT_FALSE ((VARIANT_BOOL)0) enum VARENUM { VT_EMPTY = 0, VT_NULL = 1, VT_I2 = 2, VT_I4 = 3, VT_R4 = 4, VT_R8 = 5, VT_CY = 6, VT_DATE = 7, VT_BSTR = 8, VT_DISPATCH = 9, VT_ERROR = 10, VT_BOOL = 11, VT_VARIANT = 12, VT_UNKNOWN = 13, VT_DECIMAL = 14, VT_I1 = 16, VT_UI1 = 17, VT_UI2 = 18, VT_UI4 = 19, VT_I8 = 20, VT_UI8 = 21, VT_INT = 22, VT_UINT = 23, VT_VOID = 24, VT_HRESULT = 25, VT_FILETIME = 64 }; typedef unsigned short VARTYPE; typedef WORD PROPVAR_PAD1; typedef WORD PROPVAR_PAD2; typedef WORD PROPVAR_PAD3; #ifdef __cplusplus typedef struct tagPROPVARIANT { VARTYPE vt; PROPVAR_PAD1 wReserved1; PROPVAR_PAD2 wReserved2; PROPVAR_PAD3 wReserved3; union { CHAR cVal; UCHAR bVal; SHORT iVal; USHORT uiVal; LONG lVal; ULONG ulVal; INT intVal; UINT uintVal; LARGE_INTEGER hVal; ULARGE_INTEGER uhVal; VARIANT_BOOL boolVal; SCODE scode; FILETIME filetime; BSTR bstrVal; }; } PROPVARIANT; typedef PROPVARIANT tagVARIANT; typedef tagVARIANT VARIANT; typedef VARIANT VARIANTARG; #define MY_EXTERN_C extern "C" MY_EXTERN_C HRESULT VariantClear(VARIANTARG *prop); MY_EXTERN_C HRESULT VariantCopy(VARIANTARG *dest, VARIANTARG *src); #else #define MY_EXTERN_C extern #endif MY_EXTERN_C BSTR SysAllocStringByteLen(LPCSTR psz, UINT len); MY_EXTERN_C BSTR SysAllocStringLen(const OLECHAR*,UINT); MY_EXTERN_C BSTR SysAllocString(const OLECHAR *sz); MY_EXTERN_C void SysFreeString(BSTR bstr); MY_EXTERN_C UINT SysStringByteLen(BSTR bstr); MY_EXTERN_C UINT SysStringLen(BSTR bstr); /* MY_EXTERN_C DWORD GetLastError(); */ MY_EXTERN_C LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2); #define CP_ACP 0 #define CP_OEMCP 1 typedef enum tagSTREAM_SEEK { STREAM_SEEK_SET = 0, STREAM_SEEK_CUR = 1, STREAM_SEEK_END = 2 } STREAM_SEEK; #endif #endif src/libs/7zip/unix/CPP/Common/NewHandler.cpp000066400000000000000000000010121325366651500210650ustar00rootroot00000000000000// NewHandler.cpp #include "StdAfx.h" #include "../../C/Alloc.h" /* An overload function for the C++ new */ void * operator new(size_t size) { return MyAlloc(size); } /* An overload function for the C++ new[] */ void * operator new[](size_t size) { return MyAlloc(size); } /* An overload function for the C++ delete */ void operator delete(void *pnt) { MyFree(pnt); } /* An overload function for the C++ delete[] */ void operator delete[](void *pnt) { MyFree(pnt); } src/libs/7zip/unix/CPP/Common/NewHandler.h000066400000000000000000000031441325366651500205420ustar00rootroot00000000000000// Common/NewHandler.h #ifndef __COMMON_NEW_HANDLER_H #define __COMMON_NEW_HANDLER_H /* This file must be included before any code that uses operators "delete" or "new". Also you must compile and link "NewHandler.cpp", if you use MSVC 6.0. The operator "new" in MSVC 6.0 doesn't throw exception "bad_alloc". So we define another version of operator "new" that throws "CNewException" on failure. If you use compiler that throws exception in "new" operator (GCC or new version of MSVC), you can compile without "NewHandler.cpp". So standard exception "bad_alloc" will be used. It's still allowed to use redefined version of operator "new" from "NewHandler.cpp" with any compiler. 7-Zip's code can work with "bad_alloc" and "CNewException" exceptions. But if you use some additional code (outside of 7-Zip's code), you must check that redefined version of operator "new" (that throws CNewException) is not problem for your code. Also we declare delete(void *p) throw() that creates smaller code. */ class CNewException {}; #ifdef WIN32 // We can compile my_new and my_delete with _fastcall /* void * my_new(size_t size); void my_delete(void *p) throw(); // void * my_Realloc(void *p, size_t newSize, size_t oldSize); */ #endif #ifdef _WIN32 void * #ifdef _MSC_VER __cdecl #endif operator new(size_t size); void #ifdef _MSC_VER __cdecl #endif operator delete(void *p) throw(); #endif /* #ifdef _WIN32 void * #ifdef _MSC_VER __cdecl #endif operator new[](size_t size); void #ifdef _MSC_VER __cdecl #endif operator delete[](void *p) throw(); #endif */ #endif src/libs/7zip/unix/CPP/Common/StdOutStream.cpp000066400000000000000000000043671325366651500214540ustar00rootroot00000000000000// Common/StdOutStream.cpp #include "StdAfx.h" #include #include "IntToString.h" #include "StdOutStream.h" #include "StringConvert.h" #include "UTFConvert.h" static const char kNewLineChar = '\n'; static const char *kFileOpenMode = "wt"; extern int g_CodePage; CStdOutStream g_StdOut(stdout); CStdOutStream g_StdErr(stderr); bool CStdOutStream::Open(const char *fileName) throw() { Close(); _stream = fopen(fileName, kFileOpenMode); _streamIsOpen = (_stream != 0); return _streamIsOpen; } bool CStdOutStream::Close() throw() { if (!_streamIsOpen) return true; if (fclose(_stream) != 0) return false; _stream = 0; _streamIsOpen = false; return true; } bool CStdOutStream::Flush() throw() { return (fflush(_stream) == 0); } CStdOutStream & endl(CStdOutStream & outStream) throw() { return outStream << kNewLineChar; } CStdOutStream & CStdOutStream::operator<<(const wchar_t *s) { int codePage = g_CodePage; if (codePage == -1) codePage = CP_OEMCP; AString dest; if (codePage == CP_UTF8) ConvertUnicodeToUTF8(s, dest); else UnicodeStringToMultiByte2(dest, s, (UINT)codePage); return operator<<((const char *)dest); } void StdOut_Convert_UString_to_AString(const UString &s, AString &temp) { int codePage = g_CodePage; if (codePage == -1) codePage = CP_OEMCP; if (codePage == CP_UTF8) ConvertUnicodeToUTF8(s, temp); else UnicodeStringToMultiByte2(temp, s, (UINT)codePage); } void CStdOutStream::PrintUString(const UString &s, AString &temp) { StdOut_Convert_UString_to_AString(s, temp); *this << (const char *)temp; } CStdOutStream & CStdOutStream::operator<<(Int32 number) throw() { char s[32]; ConvertInt64ToString(number, s); return operator<<(s); } CStdOutStream & CStdOutStream::operator<<(Int64 number) throw() { char s[32]; ConvertInt64ToString(number, s); return operator<<(s); } CStdOutStream & CStdOutStream::operator<<(UInt32 number) throw() { char s[16]; ConvertUInt32ToString(number, s); return operator<<(s); } CStdOutStream & CStdOutStream::operator<<(UInt64 number) throw() { char s[32]; ConvertUInt64ToString(number, s); return operator<<(s); } src/libs/7zip/unix/CPP/Common/StdOutStream.h000066400000000000000000000030341325366651500211070ustar00rootroot00000000000000// Common/StdOutStream.h #ifndef __COMMON_STD_OUT_STREAM_H #define __COMMON_STD_OUT_STREAM_H #include #include "MyString.h" #include "MyTypes.h" class CStdOutStream { FILE *_stream; bool _streamIsOpen; public: CStdOutStream(): _stream(0), _streamIsOpen(false) {}; CStdOutStream(FILE *stream): _stream(stream), _streamIsOpen(false) {}; ~CStdOutStream() { Close(); } // void AttachStdStream(FILE *stream) { _stream = stream; _streamIsOpen = false; } // bool IsDefined() const { return _stream != NULL; } operator FILE *() { return _stream; } bool Open(const char *fileName) throw(); bool Close() throw(); bool Flush() throw(); CStdOutStream & operator<<(CStdOutStream & (* func)(CStdOutStream &)) { (*func)(*this); return *this; } CStdOutStream & operator<<(const char *s) throw() { fputs(s, _stream); return *this; } CStdOutStream & operator<<(char c) throw() { fputc(c, _stream); return *this; } CStdOutStream & operator<<(Int32 number) throw(); CStdOutStream & operator<<(Int64 number) throw(); CStdOutStream & operator<<(UInt32 number) throw(); CStdOutStream & operator<<(UInt64 number) throw(); CStdOutStream & operator<<(const wchar_t *s); void PrintUString(const UString &s, AString &temp); }; CStdOutStream & endl(CStdOutStream & outStream) throw(); extern CStdOutStream g_StdOut; extern CStdOutStream g_StdErr; void StdOut_Convert_UString_to_AString(const UString &s, AString &temp); #endif src/libs/7zip/unix/CPP/Common/StringConvert.cpp000066400000000000000000000127461325366651500216650ustar00rootroot00000000000000// Common/StringConvert.cpp #include "StdAfx.h" #include "StringConvert.h" extern "C" { int global_use_utf16_conversion = 0; } #ifdef LOCALE_IS_UTF8 #ifdef __APPLE_CC__ #define UInt32 macUIn32 #include #undef UInt32 UString MultiByteToUnicodeString(const AString &srcString, UINT codePage) { if (!srcString.IsEmpty()) { UString resultString; const char * path = &srcString[0]; // FIXME size_t n = strlen(path); CFStringRef cfpath = CFStringCreateWithCString(NULL,path,kCFStringEncodingUTF8); if (cfpath) { CFMutableStringRef cfpath2 = CFStringCreateMutableCopy(NULL,0,cfpath); CFRelease(cfpath); CFStringNormalize(cfpath2,kCFStringNormalizationFormC); size_t n = CFStringGetLength(cfpath2); for(size_t i = 0 ; i< n ;i++) { resultString += CFStringGetCharacterAtIndex(cfpath2,i); } CFRelease(cfpath2); return resultString; } } UString resultString; for (int i = 0; i < srcString.Len(); i++) resultString += wchar_t(srcString[i] & 255); return resultString; } AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage) { if (!srcString.IsEmpty()) { const wchar_t * wcs = &srcString[0]; char utf8[4096]; UniChar unipath[4096]; size_t n = wcslen(wcs); for(size_t i = 0 ; i<= n ;i++) { unipath[i] = wcs[i]; } CFStringRef cfpath = CFStringCreateWithCharacters(NULL,unipath,n); CFMutableStringRef cfpath2 = CFStringCreateMutableCopy(NULL,0,cfpath); CFRelease(cfpath); CFStringNormalize(cfpath2,kCFStringNormalizationFormD); CFStringGetCString(cfpath2,(char *)utf8,4096,kCFStringEncodingUTF8); CFRelease(cfpath2); return AString(utf8); } AString resultString; for (int i = 0; i < srcString.Len(); i++) { if (srcString[i] >= 256) resultString += '?'; else resultString += char(srcString[i]); } return resultString; } #else /* __APPLE_CC__ */ #include "UTFConvert.h" UString MultiByteToUnicodeString(const AString &srcString, UINT codePage) { if ((global_use_utf16_conversion) && (!srcString.IsEmpty())) { UString resultString; bool bret = ConvertUTF8ToUnicode(srcString,resultString); if (bret) return resultString; } UString resultString; for (int i = 0; i < srcString.Len(); i++) resultString += wchar_t(srcString[i] & 255); return resultString; } AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage) { if ((global_use_utf16_conversion) && (!srcString.IsEmpty())) { AString resultString; bool bret = ConvertUnicodeToUTF8(srcString,resultString); if (bret) return resultString; } AString resultString; for (int i = 0; i < srcString.Len(); i++) { if (srcString[i] >= 256) resultString += '?'; else resultString += char(srcString[i]); } return resultString; } #endif /* __APPLE_CC__ */ #else /* LOCALE_IS_UTF8 */ UString MultiByteToUnicodeString(const AString &srcString, UINT /* codePage */ ) { #ifdef ENV_HAVE_MBSTOWCS if ((global_use_utf16_conversion) && (!srcString.IsEmpty())) { UString resultString; int numChars = mbstowcs(resultString.GetBuffer(srcString.Len()),srcString,srcString.Len()+1); if (numChars >= 0) { resultString.ReleaseBuffer(numChars); #if WCHAR_MAX > 0xffff for (int i = numChars; i >= 0; i--) { if (resultString[i] > 0xffff) { wchar_t c = resultString[i] - 0x10000; resultString.Delete(i); resultString.Insert(i, ((c >> 10) & 0x3ff) + 0xd800); resultString.Insert(i + 1, (c & 0x3ff) + 0xdc00); numChars++; } } #endif return resultString; } } #endif UString resultString; for (int i = 0; i < srcString.Len(); i++) resultString += wchar_t(srcString[i] & 255); return resultString; } AString UnicodeStringToMultiByte(const UString &src, UINT /* codePage */ ) { #ifdef ENV_HAVE_WCSTOMBS #if WCHAR_MAX > 0xffff UString srcString(src); for (int i = 0; i < srcString.Len(); i++) { if ((0xd800 <= srcString[i] && srcString[i] <= 0xdbff) && ((i + 1) < srcString.Len()) && (0xdc00 <= srcString[i + 1] && srcString[i + 1] <= 0xdf00)) { wchar_t c = (((srcString[i] - 0xd800) << 10) | (srcString[i + 1] - 0xdc00)) + 0x10000; srcString.Delete(i, 2); srcString.Insert(i, c); } } #else UString &srcString = src; #endif if ((global_use_utf16_conversion) && (!srcString.IsEmpty())) { AString resultString; int numRequiredBytes = srcString.Len() * 6+1; int numChars = wcstombs(resultString.GetBuffer(numRequiredBytes),srcString,numRequiredBytes); if (numChars >= 0) { resultString.ReleaseBuffer(numChars); return resultString; } } #endif AString resultString; for (int i = 0; i < srcString.Len(); i++) { if (srcString[i] >= 256) resultString += '?'; else resultString += char(srcString[i]); } return resultString; } #endif /* LOCALE_IS_UTF8 */ void MultiByteToUnicodeString2(UString &dest, const AString &srcString, UINT codePage) { dest = MultiByteToUnicodeString(srcString,codePage); } void UnicodeStringToMultiByte2(AString &dest, const UString &srcString, UINT codePage) { dest = UnicodeStringToMultiByte(srcString,codePage); } src/libs/7zip/unix/CPP/Common/StringConvert.h000066400000000000000000000066541325366651500213330ustar00rootroot00000000000000// Common/StringConvert.h #ifndef __COMMON_STRING_CONVERT_H #define __COMMON_STRING_CONVERT_H #include "MyString.h" #include "MyWindows.h" UString MultiByteToUnicodeString(const AString &srcString, UINT codePage = CP_ACP); // optimized versions that work faster for ASCII strings void MultiByteToUnicodeString2(UString &dest, const AString &srcString, UINT codePage = CP_ACP); void UnicodeStringToMultiByte2(AString &dest, const UString &s, UINT codePage, char defaultChar, bool &defaultCharWasUsed); void UnicodeStringToMultiByte2(AString &dest, const UString &srcString, UINT codePage); AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage, char defaultChar, bool &defaultCharWasUsed); AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage = CP_ACP); inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString) { return unicodeString; } inline const UString& GetUnicodeString(const UString &unicodeString) { return unicodeString; } inline UString GetUnicodeString(const AString &ansiString) { return MultiByteToUnicodeString(ansiString); } inline UString GetUnicodeString(const AString &multiByteString, UINT codePage) { return MultiByteToUnicodeString(multiByteString, codePage); } inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString, UINT) { return unicodeString; } inline const UString& GetUnicodeString(const UString &unicodeString, UINT) { return unicodeString; } inline const char* GetAnsiString(const char* ansiString) { return ansiString; } inline const AString& GetAnsiString(const AString &ansiString) { return ansiString; } inline AString GetAnsiString(const UString &unicodeString) { return UnicodeStringToMultiByte(unicodeString); } inline const char* GetOemString(const char* oemString) { return oemString; } inline const AString& GetOemString(const AString &oemString) { return oemString; } inline AString GetOemString(const UString &unicodeString) { return UnicodeStringToMultiByte(unicodeString, CP_OEMCP); } #ifdef _UNICODE inline const wchar_t* GetSystemString(const wchar_t* unicodeString) { return unicodeString;} inline const UString& GetSystemString(const UString &unicodeString) { return unicodeString;} inline const wchar_t* GetSystemString(const wchar_t* unicodeString, UINT /* codePage */) { return unicodeString;} inline const UString& GetSystemString(const UString &unicodeString, UINT /* codePage */) { return unicodeString;} inline UString GetSystemString(const AString &multiByteString, UINT codePage) { return MultiByteToUnicodeString(multiByteString, codePage);} inline UString GetSystemString(const AString &multiByteString) { return MultiByteToUnicodeString(multiByteString);} #else inline const char* GetSystemString(const char *ansiString) { return ansiString; } inline const AString& GetSystemString(const AString &multiByteString, UINT) { return multiByteString; } inline const char * GetSystemString(const char *multiByteString, UINT) { return multiByteString; } inline AString GetSystemString(const UString &unicodeString) { return UnicodeStringToMultiByte(unicodeString); } inline AString GetSystemString(const UString &unicodeString, UINT codePage) { return UnicodeStringToMultiByte(unicodeString, codePage); } #endif #ifndef UNDER_CE AString SystemStringToOemString(const CSysString &srcString); #endif #endif src/libs/7zip/unix/CPP/Common/StringToInt.cpp000066400000000000000000000063011325366651500212700ustar00rootroot00000000000000// Common/StringToInt.cpp #include "StdAfx.h" #include "StringToInt.h" static const UInt32 k_UInt32_max = 0xFFFFFFFF; static const UInt64 k_UInt64_max = UINT64_CONST(0xFFFFFFFFFFFFFFFF); // static const UInt64 k_UInt64_max = (UInt64)(Int64)-1; #define CONVERT_STRING_TO_UINT_FUNC(uintType, charType) \ uintType ConvertStringTo ## uintType(const charType *s, const charType **end) throw() { \ if (end) *end = s; \ uintType res = 0; \ for (;; s++) { \ charType c = *s; \ if (c < '0' || c > '9') { if (end) *end = s; return res; } \ if (res > (k_ ## uintType ## _max) / 10) return 0; \ res *= 10; \ unsigned v = (c - '0'); \ if (res > (k_ ## uintType ## _max) - v) return 0; \ res += v; }} CONVERT_STRING_TO_UINT_FUNC(UInt32, char) CONVERT_STRING_TO_UINT_FUNC(UInt32, wchar_t) CONVERT_STRING_TO_UINT_FUNC(UInt64, char) CONVERT_STRING_TO_UINT_FUNC(UInt64, wchar_t) Int32 ConvertStringToInt32(const wchar_t *s, const wchar_t **end) throw() { if (end) *end = s; const wchar_t *s2 = s; if (*s == '-') s2++; if (*s2 == 0) return 0; const wchar_t *end2; UInt32 res = ConvertStringToUInt32(s2, &end2); if (*s == '-') { if (res > ((UInt32)1 << (32 - 1))) return 0; } else if ((res & ((UInt32)1 << (32 - 1))) != 0) return 0; if (end) *end = end2; if (*s == '-') return -(Int32)res; return (Int32)res; } UInt32 ConvertOctStringToUInt32(const char *s, const char **end) throw() { if (end) *end = s; UInt32 res = 0; for (;; s++) { char c = *s; if (c < '0' || c > '7') { if (end) *end = s; return res; } if ((res & (UInt32)7 << (32 - 3)) != 0) return 0; res <<= 3; res |= (unsigned)(c - '0'); } } UInt64 ConvertOctStringToUInt64(const char *s, const char **end) throw() { if (end) *end = s; UInt64 res = 0; for (;; s++) { char c = *s; if (c < '0' || c > '7') { if (end) *end = s; return res; } if ((res & (UInt64)7 << (64 - 3)) != 0) return 0; res <<= 3; res |= (unsigned)(c - '0'); } } UInt32 ConvertHexStringToUInt32(const char *s, const char **end) throw() { if (end) *end = s; UInt32 res = 0; for (;; s++) { char c = *s; unsigned v; if (c >= '0' && c <= '9') v = (c - '0'); else if (c >= 'A' && c <= 'F') v = 10 + (c - 'A'); else if (c >= 'a' && c <= 'f') v = 10 + (c - 'a'); else { if (end) *end = s; return res; } if ((res & (UInt32)0xF << (32 - 4)) != 0) return 0; res <<= 4; res |= v; } } UInt64 ConvertHexStringToUInt64(const char *s, const char **end) throw() { if (end) *end = s; UInt64 res = 0; for (;; s++) { char c = *s; unsigned v; if (c >= '0' && c <= '9') v = (c - '0'); else if (c >= 'A' && c <= 'F') v = 10 + (c - 'A'); else if (c >= 'a' && c <= 'f') v = 10 + (c - 'a'); else { if (end) *end = s; return res; } if ((res & (UInt64)0xF << (64 - 4)) != 0) return 0; res <<= 4; res |= v; } } src/libs/7zip/unix/CPP/Common/StringToInt.h000066400000000000000000000014551325366651500207420ustar00rootroot00000000000000// Common/StringToInt.h #ifndef __COMMON_STRING_TO_INT_H #define __COMMON_STRING_TO_INT_H #include "MyTypes.h" UInt32 ConvertStringToUInt32(const char *s, const char **end) throw(); UInt64 ConvertStringToUInt64(const char *s, const char **end) throw(); UInt32 ConvertStringToUInt32(const wchar_t *s, const wchar_t **end) throw(); UInt64 ConvertStringToUInt64(const wchar_t *s, const wchar_t **end) throw(); Int32 ConvertStringToInt32(const wchar_t *s, const wchar_t **end) throw(); UInt32 ConvertOctStringToUInt32(const char *s, const char **end) throw(); UInt64 ConvertOctStringToUInt64(const char *s, const char **end) throw(); UInt32 ConvertHexStringToUInt32(const char *s, const char **end) throw(); UInt64 ConvertHexStringToUInt64(const char *s, const char **end) throw(); #endif src/libs/7zip/unix/CPP/Common/UTFConvert.cpp000066400000000000000000000075241325366651500210530ustar00rootroot00000000000000// UTFConvert.cpp #include "StdAfx.h" #include "MyTypes.h" #include "UTFConvert.h" static const Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; bool CheckUTF8(const char *src) throw() { for (;;) { Byte c; unsigned numAdds; c = *src++; if (c == 0) return true; if (c < 0x80) continue; if (c < 0xC0) return false; for (numAdds = 1; numAdds < 5; numAdds++) if (c < kUtf8Limits[numAdds]) break; UInt32 value = (c - kUtf8Limits[numAdds - 1]); do { Byte c2 = *src++; if (c2 < 0x80 || c2 >= 0xC0) return false; value <<= 6; value |= (c2 - 0x80); } while (--numAdds); if (value >= 0x110000) return false; } } static Bool Utf8_To_Utf16(wchar_t *dest, size_t *destLen, const char *src, size_t srcLen) throw() { size_t destPos = 0, srcPos = 0; for (;;) { Byte c; unsigned numAdds; if (srcPos == srcLen) { *destLen = destPos; return True; } c = (Byte)src[srcPos++]; if (c < 0x80) { if (dest) dest[destPos] = (wchar_t)c; destPos++; continue; } if (c < 0xC0) break; for (numAdds = 1; numAdds < 5; numAdds++) if (c < kUtf8Limits[numAdds]) break; UInt32 value = (c - kUtf8Limits[numAdds - 1]); do { Byte c2; if (srcPos == srcLen) break; c2 = (Byte)src[srcPos++]; if (c2 < 0x80 || c2 >= 0xC0) break; value <<= 6; value |= (c2 - 0x80); } while (--numAdds); if (value < 0x10000) { if (dest) dest[destPos] = (wchar_t)value; destPos++; } else { value -= 0x10000; if (value >= 0x100000) break; if (dest) { dest[destPos + 0] = (wchar_t)(0xD800 + (value >> 10)); dest[destPos + 1] = (wchar_t)(0xDC00 + (value & 0x3FF)); } destPos += 2; } } *destLen = destPos; return False; } static Bool Utf16_To_Utf8(char *dest, size_t *destLen, const wchar_t *src, size_t srcLen) { size_t destPos = 0, srcPos = 0; for (;;) { unsigned numAdds; UInt32 value; if (srcPos == srcLen) { *destLen = destPos; return True; } value = src[srcPos++]; if (value < 0x80) { if (dest) dest[destPos] = (char)value; destPos++; continue; } if (value >= 0xD800 && value < 0xE000) { UInt32 c2; if (value >= 0xDC00 || srcPos == srcLen) break; c2 = src[srcPos++]; if (c2 < 0xDC00 || c2 >= 0xE000) break; value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; } for (numAdds = 1; numAdds < 5; numAdds++) if (value < (((UInt32)1) << (numAdds * 5 + 6))) break; if (dest) dest[destPos] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); destPos++; do { numAdds--; if (dest) dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); destPos++; } while (numAdds != 0); } *destLen = destPos; return False; } bool ConvertUTF8ToUnicode(const AString &src, UString &dest) { dest.Empty(); size_t destLen = 0; Utf8_To_Utf16(NULL, &destLen, src, src.Len()); Bool res = Utf8_To_Utf16(dest.GetBuffer((unsigned)destLen), &destLen, src, src.Len()); dest.ReleaseBuffer((unsigned)destLen); return res ? true : false; } bool ConvertUnicodeToUTF8(const UString &src, AString &dest) { dest.Empty(); size_t destLen = 0; Utf16_To_Utf8(NULL, &destLen, src, src.Len()); Bool res = Utf16_To_Utf8(dest.GetBuffer((unsigned)destLen), &destLen, src, src.Len()); dest.ReleaseBuffer((unsigned)destLen); return res ? true : false; } src/libs/7zip/unix/CPP/Common/UTFConvert.h000066400000000000000000000005071325366651500205120ustar00rootroot00000000000000// Common/UTFConvert.h #ifndef __COMMON_UTF_CONVERT_H #define __COMMON_UTF_CONVERT_H #include "MyString.h" bool CheckUTF8(const char *src) throw(); bool ConvertUTF8ToUnicode(const AString &utfString, UString &resultString); bool ConvertUnicodeToUTF8(const UString &unicodeString, AString &resultString); #endif src/libs/7zip/unix/CPP/Common/Wildcard.cpp000066400000000000000000000342721325366651500206050ustar00rootroot00000000000000// Common/Wildcard.cpp #include "StdAfx.h" #include "Wildcard.h" bool g_CaseSensitive = #ifdef _WIN32 false; #else true; #endif bool IsPath1PrefixedByPath2(const wchar_t *s1, const wchar_t *s2) { if (g_CaseSensitive) { for (;;) { wchar_t c2 = *s2++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (MyCharUpper(c1) != MyCharUpper(c2)) return false; } } for (;;) { wchar_t c2 = *s2++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (c1 != c2) return false; } } int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW { if (g_CaseSensitive) return wcscmp(s1, s2); return MyStringCompareNoCase(s1, s2); } #ifndef USE_UNICODE_FSTRING int CompareFileNames(const char *s1, const char *s2) { if (g_CaseSensitive) return wcscmp(fs2us(s1), fs2us(s2)); return MyStringCompareNoCase(fs2us(s1), fs2us(s2)); } #endif // ----------------------------------------- // this function compares name with mask // ? - any char // * - any char or empty static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name) { for (;;) { wchar_t m = *mask; wchar_t c = *name; if (m == 0) return (c == 0); if (m == '*') { if (EnhancedMaskTest(mask + 1, name)) return true; if (c == 0) return false; } else { if (m == '?') { if (c == 0) return false; } else if (m != c) if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c)) return false; mask++; } name++; } } // -------------------------------------------------- // Splits path to strings void SplitPathToParts(const UString &path, UStringVector &pathParts) { pathParts.Clear(); unsigned len = path.Len(); if (len == 0) return; UString name; unsigned prev = 0; for (unsigned i = 0; i < len; i++) if (IsCharDirLimiter(path[i])) { name.SetFrom(path.Ptr(prev), i - prev); pathParts.Add(name); prev = i + 1; } name.SetFrom(path.Ptr(prev), len - prev); pathParts.Add(name); } void SplitPathToParts_2(const UString &path, UString &dirPrefix, UString &name) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; dirPrefix.SetFrom(path, (unsigned)(p - start)); name = p; } void SplitPathToParts_Smart(const UString &path, UString &dirPrefix, UString &name) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); if (p != start) { if (IsCharDirLimiter(*(p - 1))) p--; for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; } dirPrefix.SetFrom(path, (unsigned)(p - start)); name = p; } UString ExtractDirPrefixFromPath(const UString &path) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; return path.Left((unsigned)(p - start)); } UString ExtractFileNameFromPath(const UString &path) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; return p; } bool DoesWildcardMatchName(const UString &mask, const UString &name) { return EnhancedMaskTest(mask, name); } bool DoesNameContainWildcard(const UString &path) { for (unsigned i = 0; i < path.Len(); i++) { wchar_t c = path[i]; if (c == '*' || c == '?') return true; } return false; } // ----------------------------------------------------------' // NWildcard namespace NWildcard { #ifdef _WIN32 bool IsDriveColonName(const wchar_t *s) { wchar_t c = s[0]; return c != 0 && s[1] == ':' && s[2] == 0 && (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'); } #endif /* M = MaskParts.Size(); N = TestNameParts.Size(); File Dir ForFile rec M<=N [N-M, N) - !ForDir nonrec M=N [0, M) - ForDir rec M 1) return true; } return false; } bool CCensorNode::AreThereIncludeItems() const { if (IncludeItems.Size() > 0) return true; FOR_VECTOR (i, SubNodes) if (SubNodes[i].AreThereIncludeItems()) return true; return false; } bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const { const CObjectVector &items = include ? IncludeItems : ExcludeItems; FOR_VECTOR (i, items) if (items[i].CheckPath(pathParts, isFile)) return true; return false; } bool CCensorNode::CheckPathVect(const UStringVector &pathParts, bool isFile, bool &include) const { if (CheckPathCurrent(false, pathParts, isFile)) { include = false; return true; } include = true; bool finded = CheckPathCurrent(true, pathParts, isFile); if (pathParts.Size() <= 1) return finded; int index = FindSubNode(pathParts.Front()); if (index >= 0) { UStringVector pathParts2 = pathParts; pathParts2.Delete(0); if (SubNodes[index].CheckPathVect(pathParts2, isFile, include)) return true; } return finded; } bool CCensorNode::CheckPath2(bool isAltStream, const UString &path, bool isFile, bool &include) const { UStringVector pathParts; SplitPathToParts(path, pathParts); if (CheckPathVect(pathParts, isFile, include)) { if (!include || !isAltStream) return true; } if (isAltStream && !pathParts.IsEmpty()) { UString &back = pathParts.Back(); int pos = back.Find(L':'); if (pos > 0) { back.DeleteFrom(pos); return CheckPathVect(pathParts, isFile, include); } } return false; } bool CCensorNode::CheckPath(bool isAltStream, const UString &path, bool isFile) const { bool include; if (CheckPath2(isAltStream, path, isFile, include)) return include; return false; } bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const { if (CheckPathCurrent(include, pathParts, isFile)) return true; if (Parent == 0) return false; pathParts.Insert(0, Name); return Parent->CheckPathToRoot(include, pathParts, isFile); } /* bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const { UStringVector pathParts; SplitPathToParts(path, pathParts); return CheckPathToRoot(include, pathParts, isFile); } */ void CCensorNode::AddItem2(bool include, const UString &path, bool recursive, bool wildcardMatching) { if (path.IsEmpty()) return; bool forFile = true; bool forFolder = true; UString path2 = path; if (IsCharDirLimiter(path.Back())) { path2.DeleteBack(); forFile = false; } AddItem(include, path2, recursive, forFile, forFolder, wildcardMatching); } void CCensorNode::ExtendExclude(const CCensorNode &fromNodes) { ExcludeItems += fromNodes.ExcludeItems; FOR_VECTOR (i, fromNodes.SubNodes) { const CCensorNode &node = fromNodes.SubNodes[i]; int subNodeIndex = FindSubNode(node.Name); if (subNodeIndex < 0) subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this)); SubNodes[subNodeIndex].ExtendExclude(node); } } int CCensor::FindPrefix(const UString &prefix) const { FOR_VECTOR (i, Pairs) if (CompareFileNames(Pairs[i].Prefix, prefix) == 0) return i; return -1; } void CCensor::AddItem(ECensorPathMode pathMode, bool include, const UString &path, bool recursive, bool wildcardMatching) { UStringVector pathParts; if (path.IsEmpty()) throw "Empty file path"; SplitPathToParts(path, pathParts); bool forFile = true; if (pathParts.Back().IsEmpty()) { forFile = false; pathParts.DeleteBack(); } UString prefix; if (pathMode != k_AbsPath) { const UString &front = pathParts.Front(); bool isAbs = false; if (front.IsEmpty()) isAbs = true; else { #ifdef _WIN32 if (IsDriveColonName(front)) isAbs = true; else #endif FOR_VECTOR (i, pathParts) { const UString &part = pathParts[i]; if (part == L".." || part == L".") { isAbs = true; break; } } } unsigned numAbsParts = 0; if (isAbs) if (pathParts.Size() > 1) numAbsParts = pathParts.Size() - 1; else numAbsParts = 1; #ifdef _WIN32 // \\?\ case if (numAbsParts >= 3) { if (pathParts[0].IsEmpty() && pathParts[1].IsEmpty() && pathParts[2] == L"?") { prefix = WSTRING_PATH_SEPARATOR WSTRING_PATH_SEPARATOR L"?" WSTRING_PATH_SEPARATOR; numAbsParts -= 3; pathParts.DeleteFrontal(3); } } #endif if (numAbsParts > 1 && pathMode == k_FullPath) numAbsParts = 1; // We can't ignore wildcard, since we don't allow wildcard in SubNodes[].Name // if (wildcardMatching) for (unsigned i = 0; i < numAbsParts; i++) { { const UString &front = pathParts.Front(); if (DoesNameContainWildcard(front)) break; prefix += front; prefix += WCHAR_PATH_SEPARATOR; } pathParts.Delete(0); } } int index = FindPrefix(prefix); if (index < 0) index = Pairs.Add(CPair(prefix)); CItem item; item.PathParts = pathParts; item.ForDir = true; item.ForFile = forFile; item.Recursive = recursive; item.WildcardMatching = wildcardMatching; Pairs[index].Head.AddItem(include, item); } bool CCensor::CheckPath(bool isAltStream, const UString &path, bool isFile) const { bool finded = false; FOR_VECTOR (i, Pairs) { bool include; if (Pairs[i].Head.CheckPath2(isAltStream, path, isFile, include)) { if (!include) return false; finded = true; } } return finded; } void CCensor::ExtendExclude() { unsigned i; for (i = 0; i < Pairs.Size(); i++) if (Pairs[i].Prefix.IsEmpty()) break; if (i == Pairs.Size()) return; unsigned index = i; for (i = 0; i < Pairs.Size(); i++) if (index != i) Pairs[i].Head.ExtendExclude(Pairs[index].Head); } void CCensor::AddPathsToCensor(ECensorPathMode censorPathMode) { FOR_VECTOR(i, CensorPaths) { const CCensorPath &cp = CensorPaths[i]; AddItem(censorPathMode, cp.Include, cp.Path, cp.Recursive, cp.WildcardMatching); } CensorPaths.Clear(); } void CCensor::AddPreItem(bool include, const UString &path, bool recursive, bool wildcardMatching) { CCensorPath &cp = CensorPaths.AddNew(); cp.Path = path; cp.Include = include; cp.Recursive = recursive; cp.WildcardMatching = wildcardMatching; } } src/libs/7zip/unix/CPP/Common/Wildcard.h000066400000000000000000000104131325366651500202410ustar00rootroot00000000000000// Common/Wildcard.h #ifndef __COMMON_WILDCARD_H #define __COMMON_WILDCARD_H #include "MyString.h" int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW; #ifndef USE_UNICODE_FSTRING int CompareFileNames(const char *s1, const char *s2); #endif bool IsPath1PrefixedByPath2(const wchar_t *s1, const wchar_t *s2); inline bool IsCharDirLimiter(wchar_t c) { return c == WCHAR_PATH_SEPARATOR #ifdef _WIN32 || c == L'/' #endif ; } void SplitPathToParts(const UString &path, UStringVector &pathParts); void SplitPathToParts_2(const UString &path, UString &dirPrefix, UString &name); void SplitPathToParts_Smart(const UString &path, UString &dirPrefix, UString &name); // ignores dir delimiter at the end of (path) UString ExtractDirPrefixFromPath(const UString &path); UString ExtractFileNameFromPath(const UString &path); bool DoesNameContainWildcard(const UString &path); bool DoesWildcardMatchName(const UString &mask, const UString &name); namespace NWildcard { #ifdef _WIN32 // returns true, if name is like "a:", "c:", ... bool IsDriveColonName(const wchar_t *s); #endif struct CItem { UStringVector PathParts; bool Recursive; bool ForFile; bool ForDir; bool WildcardMatching; #ifdef _WIN32 bool IsDriveItem() const { return PathParts.Size() == 1 && !ForFile && ForDir && IsDriveColonName(PathParts[0]); } #endif // CItem(): WildcardMatching(true) {} bool AreAllAllowed() const; bool CheckPath(const UStringVector &pathParts, bool isFile) const; }; class CCensorNode { CCensorNode *Parent; bool CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const; void AddItemSimple(bool include, CItem &item); bool CheckPathVect(const UStringVector &pathParts, bool isFile, bool &include) const; public: CCensorNode(): Parent(0) { }; CCensorNode(const UString &name, CCensorNode *parent): Parent(parent), Name(name) { }; UString Name; // wildcard is not allowed here CObjectVector SubNodes; CObjectVector IncludeItems; CObjectVector ExcludeItems; bool AreAllAllowed() const; int FindSubNode(const UString &path) const; void AddItem(bool include, CItem &item); void AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir, bool wildcardMatching); void AddItem2(bool include, const UString &path, bool recursive, bool wildcardMatching); bool NeedCheckSubDirs() const; bool AreThereIncludeItems() const; bool CheckPath2(bool isAltStream, const UString &path, bool isFile, bool &include) const; bool CheckPath(bool isAltStream, const UString &path, bool isFile) const; bool CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const; // bool CheckPathToRoot(const UString &path, bool isFile, bool include) const; void ExtendExclude(const CCensorNode &fromNodes); }; struct CPair { UString Prefix; CCensorNode Head; CPair(const UString &prefix): Prefix(prefix) { }; }; enum ECensorPathMode { k_RelatPath, // absolute prefix as Prefix, remain path in Tree k_FullPath, // drive prefix as Prefix, remain path in Tree k_AbsPath // full path in Tree }; struct CCensorPath { UString Path; bool Include; bool Recursive; bool WildcardMatching; CCensorPath(): Include(true), Recursive(false), WildcardMatching(true) {} }; class CCensor { int FindPrefix(const UString &prefix) const; public: CObjectVector Pairs; CObjectVector CensorPaths; bool AllAreRelative() const { return (Pairs.Size() == 1 && Pairs.Front().Prefix.IsEmpty()); } void AddItem(ECensorPathMode pathMode, bool include, const UString &path, bool recursive, bool wildcardMatching); bool CheckPath(bool isAltStream, const UString &path, bool isFile) const; void ExtendExclude(); void AddPathsToCensor(NWildcard::ECensorPathMode censorPathMode); void AddPreItem(bool include, const UString &path, bool recursive, bool wildcardMatching); void AddPreItem(const UString &path) { AddPreItem(true, path, false, false); } void AddPreItem_Wildcard() { AddPreItem(true, L"*", false, true); } }; } #endif src/libs/7zip/unix/CPP/Windows/000077500000000000000000000000001325366651500165425ustar00rootroot00000000000000src/libs/7zip/unix/CPP/Windows/DLL.cpp000066400000000000000000000125421325366651500176650ustar00rootroot00000000000000// Windows/DLL.cpp #include "StdAfx.h" #ifdef __APPLE_CC__ #include #elif ENV_BEOS #include #include #else #define UINT64 DLL_UINT64 // HP-UX , dlfcn.h defines UINT64 but p7zip also defines UINT64 #include // dlopen ... #undef UINT64 #endif #include "DLL.h" #include "Defs.h" #ifdef _UNICODE #include "../Common/StringConvert.h" #endif #define NEED_NAME_WINDOWS_TO_UNIX #include "myPrivate.h" // #define TRACEN(u) u; #define TRACEN(u) /* */ namespace NWindows { namespace NDLL { bool CLibrary::Free() { TRACEN((printf("CLibrary::Free(this=%p,%p)\n",(void *)this,(void *)_module))) if (_module == 0) return true; #ifdef __APPLE_CC__ int ret = NSUnLinkModule ((NSModule)_module, 0); #elif ENV_BEOS int ret = unload_add_on((image_id)_module); #else int ret = dlclose(_module); #endif TRACEN((printf("CLibrary::Free dlclose(%p)=%d\n",(void *)_module,ret))) if (ret != 0) return false; _module = 0; return true; } static FARPROC local_GetProcAddress(HMODULE module,LPCSTR lpProcName) { void *ptr = 0; TRACEN((printf("local_GetProcAddress(%p,%s)\n",(void *)module,lpProcName))) if (module) { #ifdef __APPLE_CC__ char name[MAX_PATHNAME_LEN]; snprintf(name,sizeof(name),"_%s",lpProcName); name[sizeof(name)-1] = 0; TRACEN((printf("NSLookupSymbolInModule(%p,%s)\n",(void *)module,name))) NSSymbol sym; sym = NSLookupSymbolInModule((NSModule)module, name); if (sym) { ptr = NSAddressOfSymbol(sym); } else { ptr = 0; } #elif ENV_BEOS if (get_image_symbol((image_id)module, lpProcName, B_SYMBOL_TYPE_TEXT, &ptr) != B_OK) ptr = 0; #else ptr = dlsym (module, lpProcName); #endif TRACEN((printf("CLibrary::GetProc : dlsym(%p,%s)=%p\n",(void *)module,lpProcName,ptr))) } return (FARPROC)ptr; } FARPROC CLibrary::GetProc(LPCSTR lpProcName) const { TRACEN((printf("CLibrary::GetProc(%p,%s)\n",(void *)_module,lpProcName))) return local_GetProcAddress(_module,lpProcName); } bool CLibrary::Load(LPCTSTR lpLibFileName) { if(!Free()) return false; void *handler = 0; char name[MAX_PATHNAME_LEN+1]; #ifdef _UNICODE AString name2 = UnicodeStringToMultiByte(lpLibFileName); strcpy(name,nameWindowToUnix((const char *)name2)); #else strcpy(name,nameWindowToUnix(lpLibFileName)); #endif // replace ".dll" with ".so" size_t len = strlen(name); if ((len >=4) && (strcmp(name+len-4,".dll") == 0)) { strcpy(name+len-4,".so"); } TRACEN((printf("CLibrary::Load(this=%p,%ls) => %s\n",(void *)this,lpLibFileName,name))) #ifdef __APPLE_CC__ NSObjectFileImage image; NSObjectFileImageReturnCode nsret; nsret = NSCreateObjectFileImageFromFile (name, &image); if (nsret == NSObjectFileImageSuccess) { TRACEN((printf("NSCreateObjectFileImageFromFile(%s) : OK\n",name))) handler = (HMODULE)NSLinkModule(image,name,NSLINKMODULE_OPTION_RETURN_ON_ERROR | NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW); } else { TRACEN((printf("NSCreateObjectFileImageFromFile(%s) : ERROR\n",name))) } #elif ENV_BEOS // normalize path (remove things like "./", "..", etc..), otherwise it won't work BPath p(name, NULL, true); status_t err = B_OK; image_id image = load_add_on(p.Path()); TRACEN((printf("load_add_on(%s)=%d\n",p.Path(),(int)image))) if (image < 0) { err = (image_id)handler; handler = 0; } else { err = 0; handler = (HMODULE)image; } #else int options_dlopen = 0; #ifdef RTLD_LOCAL options_dlopen |= RTLD_LOCAL; #endif #ifdef RTLD_NOW options_dlopen |= RTLD_NOW; #endif #ifdef RTLD_GROUP #if ! (defined(hpux) || defined(__hpux)) options_dlopen |= RTLD_GROUP; // mainly for solaris but not for HPUX #endif #endif TRACEN((printf("CLibrary::Load - dlopen(%s,0x%d)\n",name,options_dlopen))) handler = dlopen(name,options_dlopen); #endif // __APPLE_CC__ TRACEN((printf("CLibrary::Load(%s) => %p\n",name,handler))) if (handler) { // Call DllMain() like in Windows : useless now // Propagate the value of global_use_utf16_conversion into the plugins int *tmp = (int *)local_GetProcAddress(handler,"global_use_utf16_conversion"); if (tmp) *tmp = global_use_utf16_conversion; tmp = (int *)local_GetProcAddress(handler,"global_use_lstat"); if (tmp) *tmp = global_use_lstat; // test construtors calls void (*fctTest)(void) = (void (*)(void))local_GetProcAddress(handler,"sync_TestConstructor"); if (fctTest) fctTest(); } else { #ifdef __APPLE_CC__ NSLinkEditErrors c; int num_err; const char *file,*err; NSLinkEditError(&c,&num_err,&file,&err); printf("Can't load '%ls' (%s)\n", lpLibFileName,err); #elif ENV_BEOS printf("Can't load '%ls' (%s)\n", lpLibFileName,strerror(err)); #else printf("Can't load '%ls' (%s)\n", lpLibFileName,dlerror()); #endif } _module = handler; TRACEN((printf("CLibrary::Load(this=%p,%ls) => _module=%p\n",(void *)this,lpLibFileName,_module))) return true; } #ifndef _SFX FString GetModuleDirPrefix() { FString s; const char *p7zip_home_dir = getenv("P7ZIP_HOME_DIR"); if (p7zip_home_dir) { return MultiByteToUnicodeString(p7zip_home_dir,CP_ACP); } return FTEXT(".") FSTRING_PATH_SEPARATOR; } #endif }} src/libs/7zip/unix/CPP/Windows/DLL.h000066400000000000000000000017121325366651500173270ustar00rootroot00000000000000// Windows/DLL.h #ifndef __WINDOWS_DLL_H #define __WINDOWS_DLL_H #include "../Common/MyString.h" typedef void * HMODULE; // #define LOAD_LIBRARY_AS_DATAFILE 0 typedef int (*FARPROC)(); namespace NWindows { namespace NDLL { class CLibrary { HMODULE _module; public: CLibrary(): _module(NULL) {}; ~CLibrary() { Free(); } operator HMODULE() const { return _module; } HMODULE* operator&() { return &_module; } bool IsLoaded() const { return (_module != NULL); }; void Attach(HMODULE m) { Free(); _module = m; } HMODULE Detach() { HMODULE m = _module; _module = NULL; return m; } bool Free(); // bool LoadEx(CFSTR path, DWORD flags = LOAD_LIBRARY_AS_DATAFILE); bool Load(CFSTR path); FARPROC GetProc(LPCSTR procName) const; // { return My_GetProcAddress(_module, procName); } }; bool MyGetModuleFileName(FString &path); FString GetModuleDirPrefix(); }} #endif src/libs/7zip/unix/CPP/Windows/Defs.h000066400000000000000000000010371325366651500175750ustar00rootroot00000000000000// Windows/Defs.h #ifndef __WINDOWS_DEFS_H #define __WINDOWS_DEFS_H #include "../myWindows/StdAfx.h" #include "../Common/MyWindows.h" // #ifdef _WIN32 inline bool LRESULTToBool(LRESULT v) { return (v != FALSE); } inline bool BOOLToBool(BOOL v) { return (v != FALSE); } inline BOOL BoolToBOOL(bool v) { return (v ? TRUE: FALSE); } // #endif inline VARIANT_BOOL BoolToVARIANT_BOOL(bool v) { return (v ? VARIANT_TRUE: VARIANT_FALSE); } inline bool VARIANT_BOOLToBool(VARIANT_BOOL v) { return (v != VARIANT_FALSE); } #endif src/libs/7zip/unix/CPP/Windows/FileDir.cpp000066400000000000000000000530211325366651500205650ustar00rootroot00000000000000// Windows/FileDir.cpp #include "StdAfx.h" #ifndef _UNICODE #include "../Common/StringConvert.h" #endif #include "FileDir.h" #include "FileFind.h" #include "FileName.h" #include "../Common/StringConvert.h" #include "../Common/IntToString.h" #define NEED_NAME_WINDOWS_TO_UNIX #include "myPrivate.h" #include "Windows/Synchronization.h" #include // rmdir #include #include // mkdir #include #include #include // #define TRACEN(u) u; #define TRACEN(u) /* */ int g_filedir = 1; static NWindows::NSynchronization::CCriticalSection g_CountCriticalSection; class Umask { public: mode_t current_umask; mode_t mask; Umask() { current_umask = umask (0); /* get and set the umask */ umask(current_umask); /* restore the umask */ mask = 0777 & (~current_umask); } }; static Umask gbl_umask; extern BOOLEAN WINAPI RtlTimeToSecondsSince1970( const LARGE_INTEGER *Time, DWORD *Seconds ); #ifdef _UNICODE AString nameWindowToUnix2(LPCWSTR name) // FIXME : optimization ? { AString astr = UnicodeStringToMultiByte(name); return AString(nameWindowToUnix((const char *)astr)); } #endif DWORD WINAPI GetFullPathNameW( LPCTSTR name, DWORD len, LPTSTR buffer, LPTSTR *lastpart ) { // FIXME if (name == 0) return 0; DWORD name_len = lstrlen(name); if (name[0] == '/') { DWORD ret = name_len+2; if (ret >= len) { TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 0)\n",name, (int)len))) return 0; } lstrcpy(buffer,L"c:"); lstrcat(buffer,name); *lastpart=buffer; TCHAR *ptr=buffer; while (*ptr) { if (*ptr == '/') *lastpart=ptr+1; ptr++; } TRACEN((printf("GetFullPathNameA(%ls,%d,%ls,%ls)=%d\n",name, (int)len,buffer, *lastpart,(int)ret))) return ret; } if (isascii(name[0]) && (name[1] == ':')) { // FIXME isascii DWORD ret = name_len; if (ret >= len) { TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 1)\n",name, (int)len))) return 0; } lstrcpy(buffer,name); *lastpart=buffer; TCHAR *ptr=buffer; while (*ptr) { if (*ptr == '/') *lastpart=ptr+1; ptr++; } TRACEN((printf("GetFullPathNameA(%ls,%d,%ls,%ls)=%d\n",name, (int)len,buffer, *lastpart,(int)ret))) return ret; } // name is a relative pathname. // if (len < 2) { TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 2)\n",name, (int)len))) return 0; } DWORD ret = 0; char begin[MAX_PATHNAME_LEN]; /* DWORD begin_len = GetCurrentDirectoryA(MAX_PATHNAME_LEN,begin); */ DWORD begin_len = 0; begin[0]='c'; begin[1]=':'; char * cret = getcwd(begin+2, MAX_PATHNAME_LEN - 3); if (cret) { begin_len = strlen(begin); } if (begin_len >= 1) { // strlen(begin) + strlen("/") + strlen(name) ret = begin_len + 1 + name_len; if (ret >= len) { TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 4)\n",name, (int)len))) return 0; } UString wbegin = GetUnicodeString(begin); lstrcpy(buffer,wbegin); lstrcat(buffer,L"/"); lstrcat(buffer,name); *lastpart=buffer + begin_len + 1; TCHAR *ptr=buffer; while (*ptr) { if (*ptr == '/') *lastpart=ptr+1; ptr++; } TRACEN((printf("GetFullPathNameA(%ls,%d,%ls,%ls)=%d\n",name, (int)len,buffer, *lastpart,(int)ret))) } else { ret = 0; TRACEN((printf("GetFullPathNameA(%ls,%d,)=0000 (case 5)\n",name, (int)len))) } return ret; } static int copy_fd(int fin,int fout) { char buffer[16384]; ssize_t ret_in; ssize_t ret_out; do { ret_out = -1; do { ret_in = read(fin, buffer,sizeof(buffer)); } while (ret_in < 0 && (errno == EINTR)); if (ret_in >= 1) { do { ret_out = write (fout, buffer, ret_in); } while (ret_out < 0 && (errno == EINTR)); } else if (ret_in == 0) { ret_out = 0; } } while (ret_out >= 1); return ret_out; } static BOOL CopyFile(const char *src,const char *dst) { int ret = -1; #ifdef O_BINARY int flags = O_BINARY; #else int flags = 0; #endif #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif // printf("##DBG CopyFile(%s,%s)\n",src,dst); int fout = open(dst,O_CREAT | O_WRONLY | O_EXCL | flags, 0600); if (fout != -1) { int fin = open(src,O_RDONLY | flags , 0600); if (fin != -1) { ret = copy_fd(fin,fout); if (ret == 0) ret = close(fin); else close(fin); } if (ret == 0) ret = close(fout); else close(fout); } if (ret == 0) return TRUE; return FALSE; } #ifndef _UNICODE extern bool g_IsNT; #endif namespace NWindows { namespace NFile { // SetCurrentDirectory doesn't support \\?\ prefix #ifdef WIN_LONG_PATH bool GetLongPathBase(CFSTR fileName, UString &res); bool GetLongPath(CFSTR fileName, UString &res); #endif namespace NDir { #ifdef _WIN32 #ifndef UNDER_CE bool MyGetWindowsDirectory(FString &path) { UINT needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetWindowsDirectory(s, MAX_PATH + 1); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } bool MyGetSystemDirectory(FString &path) { UINT needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetSystemDirectory(s, MAX_PATH + 1); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } #endif #endif // _WIN32 bool SetDirTime(CFSTR fileName, const FILETIME * /* cTime */ , const FILETIME *aTime, const FILETIME *mTime) { AString cfilename = UnicodeStringToMultiByte(fileName); const char * unix_filename = nameWindowToUnix((const char *)cfilename); struct utimbuf buf; struct stat oldbuf; int ret = stat(unix_filename,&oldbuf); if (ret == 0) { buf.actime = oldbuf.st_atime; buf.modtime = oldbuf.st_mtime; } else { time_t current_time = time(0); buf.actime = current_time; buf.modtime = current_time; } if (aTime) { LARGE_INTEGER ltime; DWORD dw; ltime.QuadPart = aTime->dwHighDateTime; ltime.QuadPart = (ltime.QuadPart << 32) | aTime->dwLowDateTime; RtlTimeToSecondsSince1970( <ime, &dw ); buf.actime = dw; } if (mTime) { LARGE_INTEGER ltime; DWORD dw; ltime.QuadPart = mTime->dwHighDateTime; ltime.QuadPart = (ltime.QuadPart << 32) | mTime->dwLowDateTime; RtlTimeToSecondsSince1970( <ime, &dw ); buf.modtime = dw; } /* ret = */ utime(unix_filename, &buf); return true; } #ifdef WIN_LONG_PATH bool GetLongPaths(CFSTR s1, CFSTR s2, UString &d1, UString &d2) { if (!GetLongPathBase(s1, d1) || !GetLongPathBase(s2, d2)) return false; if (d1.IsEmpty() && d2.IsEmpty()) return false; if (d1.IsEmpty()) d1 = fs2us(s1); if (d2.IsEmpty()) d2 = fs2us(s2); return true; } #endif static int convert_to_symlink(const char * name) { FILE *file = fopen(name,"rb"); if (file) { char buf[MAX_PATHNAME_LEN+1]; char * ret = fgets(buf,sizeof(buf)-1,file); fclose(file); if (ret) { int ir = unlink(name); if (ir == 0) { ir = symlink(buf,name); } return ir; } } return -1; } bool SetFileAttrib(CFSTR fileName, DWORD fileAttributes) { if (!fileName) { SetLastError(ERROR_PATH_NOT_FOUND); TRACEN((printf("SetFileAttrib(NULL,%d) : false-1\n",fileAttributes))) return false; } #ifdef _UNICODE AString name = nameWindowToUnix2(fileName); #else const char * name = nameWindowToUnix(fileName); #endif struct stat stat_info; #ifdef ENV_HAVE_LSTAT if (global_use_lstat) { if(lstat(name,&stat_info)!=0) { TRACEN((printf("SetFileAttrib(%s,%d) : false-2-1\n",(const char *)name,fileAttributes))) return false; } } else #endif { if(stat(name,&stat_info)!=0) { TRACEN((printf("SetFileAttrib(%s,%d) : false-2-2\n",(const char *)name,fileAttributes))) return false; } } if (fileAttributes & FILE_ATTRIBUTE_UNIX_EXTENSION) { stat_info.st_mode = fileAttributes >> 16; #ifdef ENV_HAVE_LSTAT if (S_ISLNK(stat_info.st_mode)) { if ( convert_to_symlink(name) != 0) { TRACEN((printf("SetFileAttrib(%s,%d) : false-3\n",(const char *)name,fileAttributes))) return false; } } else #endif if (S_ISREG(stat_info.st_mode)) { TRACEN((printf("##DBG chmod-2(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask))) chmod(name,stat_info.st_mode & gbl_umask.mask); } else if (S_ISDIR(stat_info.st_mode)) { // user/7za must be able to create files in this directory stat_info.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR); TRACEN((printf("##DBG chmod-3(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask))) chmod(name,stat_info.st_mode & gbl_umask.mask); } #ifdef ENV_HAVE_LSTAT } else if (!S_ISLNK(stat_info.st_mode)) { // do not use chmod on a link #else } else { #endif /* Only Windows Attributes */ if( S_ISDIR(stat_info.st_mode)) { /* Remark : FILE_ATTRIBUTE_READONLY ignored for directory. */ TRACEN((printf("##DBG chmod-4(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask))) chmod(name,stat_info.st_mode & gbl_umask.mask); } else { if (fileAttributes & FILE_ATTRIBUTE_READONLY) stat_info.st_mode &= ~0222; /* octal!, clear write permission bits */ TRACEN((printf("##DBG chmod-5(%s,%o)\n",(const char *)name,(unsigned)stat_info.st_mode & gbl_umask.mask))) chmod(name,stat_info.st_mode & gbl_umask.mask); } } TRACEN((printf("SetFileAttrib(%s,%d) : true\n",(const char *)name,fileAttributes))) return true; } bool RemoveDir(CFSTR path) { if (!path || !*path) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } AString name = nameWindowToUnix2(path); TRACEN((printf("RemoveDirectoryA(%s)\n",(const char *)name))) if (rmdir( (const char *)name ) != 0) { return FALSE; } return TRUE; } bool MyMoveFile(CFSTR existFileName, CFSTR newFileName) { #ifdef _UNICODE AString src = nameWindowToUnix2(existFileName); AString dst = nameWindowToUnix2(newFileName); #else const char * src = nameWindowToUnix(existFileName); const char * dst = nameWindowToUnix(newFileName); #endif TRACEN((printf("MyMoveFile(%s,%s)\n",(const char *)src,(const char *)dst))) int ret = rename(src,dst); if (ret != 0) { if (errno == EXDEV) // FIXED : bug #1112167 (Temporary directory must be on same partition as target) { BOOL bret = CopyFile(src,dst); if (bret == FALSE) return false; struct stat info_file; ret = stat(src,&info_file); if (ret == 0) { TRACEN((printf("##DBG chmod-1(%s,%o)\n",(const char *)dst,(unsigned)info_file.st_mode & gbl_umask.mask))) ret = chmod(dst,info_file.st_mode & gbl_umask.mask); } if (ret == 0) { ret = unlink(src); } if (ret == 0) return true; } return false; } return true; } bool CreateDir(CFSTR path) { if (!path || !*path) { SetLastError(ERROR_PATH_NOT_FOUND); return false; } #ifdef _UNICODE AString name = nameWindowToUnix2(path); #else const char * name = nameWindowToUnix(path); #endif bool bret = false; if (mkdir( name, 0700 ) == 0) bret = true; TRACEN((printf("CreateDir(%s)=%d\n",(const char *)name,(int)bret))) return bret; } bool CreateComplexDir(CFSTR _aPathName) { AString name = nameWindowToUnix2(_aPathName); TRACEN((printf("CreateComplexDir(%s)\n",(const char *)name))) FString pathName = _aPathName; int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos > 0 && pos == pathName.Len() - 1) { if (pathName.Len() == 3 && pathName[1] == L':') return true; // Disk folder; pathName.Delete(pos); } FString pathName2 = pathName; pos = pathName.Len(); TRACEN((printf("CreateComplexDir(%s) pathName2=%ls\n",(const char *)name,(CFSTR)pathName2))) for (;;) { if (CreateDir(pathName)) break; TRACEN((printf("CreateComplexDir(%s) GetLastError=%d (ERROR_ALREADY_EXISTS=%d)\n",(const char *)name,::GetLastError(), ERROR_ALREADY_EXISTS))) if (::GetLastError() == ERROR_ALREADY_EXISTS) { #ifdef _WIN32 // FIXED for supporting symbolic link instead of a directory NFind::CFileInfo fileInfo; if (!fileInfo.Find(pathName)) // For network folders return true; if (!fileInfo.IsDir()) return false; #endif break; } pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos < 0 || pos == 0) return false; if (pathName[pos - 1] == L':') return false; pathName = pathName.Left(pos); } pathName = pathName2; while (pos < pathName.Len()) { pos = pathName.Find(FCHAR_PATH_SEPARATOR, pos + 1); if (pos < 0) pos = pathName.Len(); if (!CreateDir(pathName.Left(pos))) return false; } return true; } bool DeleteFileAlways(CFSTR name) { if (!name || !*name) { SetLastError(ERROR_PATH_NOT_FOUND); return false; } #ifdef _UNICODE AString unixname = nameWindowToUnix2(name); #else const char * unixname = nameWindowToUnix(name); #endif bool bret = false; if (remove(unixname) == 0) bret = true; TRACEN((printf("DeleteFileAlways(%s)=%d\n",(const char *)unixname,(int)bret))) return bret; } bool RemoveDirWithSubItems(const FString &path) { bool needRemoveSubItems = true; { NFind::CFileInfo fi; if (!fi.Find(path)) return false; if (!fi.IsDir()) { ::SetLastError(ERROR_DIRECTORY); return false; } if (fi.HasReparsePoint()) needRemoveSubItems = false; } if (needRemoveSubItems) { FString s = path; s += FCHAR_PATH_SEPARATOR; unsigned prefixSize = s.Len(); s += FCHAR_ANY_MASK; NFind::CEnumerator enumerator(s); NFind::CFileInfo fi; while (enumerator.Next(fi)) { s.DeleteFrom(prefixSize); s += fi.Name; if (fi.IsDir()) { if (!RemoveDirWithSubItems(s)) return false; } else if (!DeleteFileAlways(s)) return false; } } if (!SetFileAttrib(path, 0)) return false; return RemoveDir(path); } bool RemoveDirectoryWithSubItems(const FString &path); // FIXME static bool RemoveDirectorySubItems2(const FString pathPrefix, const NFind::CFileInfo &fileInfo) { if (fileInfo.IsDir()) return RemoveDirectoryWithSubItems(pathPrefix + fileInfo.Name); return DeleteFileAlways(pathPrefix + fileInfo.Name); } bool RemoveDirectoryWithSubItems(const FString &path) { NFind::CFileInfo fileInfo; FString pathPrefix = path + FCHAR_PATH_SEPARATOR; { NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK); while (enumerator.Next(fileInfo)) if (!RemoveDirectorySubItems2(pathPrefix, fileInfo)) return false; } if (!SetFileAttrib(path, 0)) return false; return RemoveDir(path); } #ifdef UNDER_CE bool MyGetFullPathName(CFSTR fileName, FString &resFullPath) { resFullPath = fileName; return true; } #else #ifdef WIN_LONG_PATH static FString GetLastPart(CFSTR path) { int i = MyStringLen(path); for (; i > 0; i--) { FChar c = path[i - 1]; if (c == FCHAR_PATH_SEPARATOR || c == '/') break; } return path + i; } static void AddTrailingDots(CFSTR oldPath, FString &newPath) { int len = MyStringLen(oldPath); int i; for (i = len; i > 0 && oldPath[i - 1] == '.'; i--); if (i == 0 || i == len) return; FString oldName = GetLastPart(oldPath); FString newName = GetLastPart(newPath); int nonDotsLen = oldName.Len() - (len - i); if (nonDotsLen == 0 || newName.CompareNoCase(oldName.Left(nonDotsLen)) != 0) return; for (; i != len; i++) newPath += '.'; } #endif bool MyGetFullPathName(CFSTR fileName, FString &resFullPath) { resFullPath.Empty(); #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; LPTSTR fileNamePointer = 0; DWORD needLength = ::GetFullPathName(fs2fas(fileName), MAX_PATH + 1, s, &fileNamePointer); if (needLength == 0 || needLength > MAX_PATH) return false; resFullPath = fas2fs(s); return true; } else #endif { LPWSTR fileNamePointer = 0; WCHAR s[MAX_PATH + 2]; s[0] = 0; DWORD needLength = ::GetFullPathNameW(fs2us(fileName), MAX_PATH + 1, s, &fileNamePointer); if (needLength == 0) return false; if (needLength <= MAX_PATH) { resFullPath = us2fs(s); return true; } #ifdef WIN_LONG_PATH needLength++; UString temp; LPWSTR buffer = temp.GetBuffer(needLength + 1); buffer[0] = 0; DWORD needLength2 = ::GetFullPathNameW(fs2us(fileName), needLength, buffer, &fileNamePointer); temp.ReleaseBuffer(); if (needLength2 > 0 && needLength2 <= needLength) { resFullPath = us2fs(temp); AddTrailingDots(fileName, resFullPath); return true; } #endif return false; } } bool SetCurrentDir(CFSTR path) { AString apath = UnicodeStringToMultiByte(path); return chdir((const char*)apath) == 0; } bool GetCurrentDir(FString &path) { char begin[MAX_PATHNAME_LEN]; begin[0]='c'; begin[1]=':'; char * cret = getcwd(begin+2, MAX_PATHNAME_LEN - 3); if (cret) { #ifdef _UNICODE path = GetUnicodeString(begin); #else path = begin; #endif return true; } return false; } #endif bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName) { bool res = MyGetFullPathName(path, resDirPrefix); if (!res) resDirPrefix = path; int pos = resDirPrefix.ReverseFind(FCHAR_PATH_SEPARATOR); resFileName = resDirPrefix.Ptr(pos + 1); resDirPrefix.DeleteFrom(pos + 1); return res; } bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix) { FString resFileName; return GetFullPathAndSplit(path, resDirPrefix, resFileName); } bool MyGetTempPath(FString &path) { path = L"c:/tmp/"; // final '/' is needed return true; } static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile) { #ifdef _WIN32 UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId(); #else static UInt32 memo_count = 0; UInt32 count; g_CountCriticalSection.Enter(); count = memo_count++; g_CountCriticalSection.Leave(); UINT number = (UINT)getpid(); UInt32 d = (GetTickCount() << 12) ^ (count << 14) ^ number; #endif for (unsigned i = 0; i < 100; i++) { path = prefix; if (addRandom) { FChar s[16]; UInt32 value = d; unsigned k; for (k = 0; k < 8; k++) { unsigned t = value & 0xF; value >>= 4; s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } s[k] = '\0'; if (outFile) path += FChar('.'); path += s; UInt32 step = GetTickCount() + 2; if (step == 0) step = 1; d += step; } addRandom = true; if (outFile) path += FTEXT(".tmp"); if (NFind::DoesFileOrDirExist(path)) { SetLastError(ERROR_ALREADY_EXISTS); continue; } if (outFile) { if (outFile->Create(path, false)) return true; } else { if (CreateDir(path)) return true; } DWORD error = GetLastError(); if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) break; } path.Empty(); return false; } bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile) { if (!Remove()) return false; if (!CreateTempFile(prefix, false, _path, outFile)) return false; _mustBeDeleted = true; return true; } bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile) { if (!Remove()) return false; FString tempPath; if (!MyGetTempPath(tempPath)) return false; if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile)) return false; _mustBeDeleted = true; return true; } bool CTempFile::Remove() { if (!_mustBeDeleted) return true; _mustBeDeleted = !DeleteFileAlways(_path); return !_mustBeDeleted; } bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore) { if (deleteDestBefore) if (NFind::DoesFileExist(name)) if (!DeleteFileAlways(name)) return false; DisableDeleting(); return MyMoveFile(_path, name); } bool CTempDir::Create(CFSTR prefix) { if (!Remove()) return false; FString tempPath; if (!MyGetTempPath(tempPath)) return false; if (!CreateTempFile(tempPath + prefix, true, _path, NULL)) return false; _mustBeDeleted = true; return true; } bool CTempDir::Remove() { if (!_mustBeDeleted) return true; _mustBeDeleted = !RemoveDirectoryWithSubItems(_path); return !_mustBeDeleted; } }}} src/libs/7zip/unix/CPP/Windows/FileDir.h000066400000000000000000000043671325366651500202430ustar00rootroot00000000000000// Windows/FileDir.h #ifndef __WINDOWS_FILE_DIR_H #define __WINDOWS_FILE_DIR_H #include "../Common/MyString.h" #include "FileIO.h" namespace NWindows { namespace NFile { namespace NDir { bool GetWindowsDir(FString &path); bool GetSystemDir(FString &path); bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime); bool SetFileAttrib(CFSTR path, DWORD attrib); bool MyMoveFile(CFSTR existFileName, CFSTR newFileName); #ifndef UNDER_CE bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName); #endif bool RemoveDir(CFSTR path); bool CreateDir(CFSTR path); bool CreateComplexDir(CFSTR path); bool DeleteFileAlways(CFSTR name); bool RemoveDirWithSubItems(const FString &path); bool MyGetFullPathName(CFSTR path, FString &resFullPath); bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName); bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix); #ifndef UNDER_CE bool SetCurrentDir(CFSTR path); bool GetCurrentDir(FString &resultPath); #endif bool MyGetTempPath(FString &resultPath); class CTempFile { bool _mustBeDeleted; FString _path; void DisableDeleting() { _mustBeDeleted = false; } public: CTempFile(): _mustBeDeleted(false) {} ~CTempFile() { Remove(); } const FString &GetPath() const { return _path; } bool Create(CFSTR pathPrefix, NIO::COutFile *outFile); // pathPrefix is not folder prefix bool CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile); bool Remove(); bool MoveTo(CFSTR name, bool deleteDestBefore); }; class CTempDir { bool _mustBeDeleted; FString _path; public: CTempDir(): _mustBeDeleted(false) {} ~CTempDir() { Remove(); } const FString &GetPath() const { return _path; } void DisableDeleting() { _mustBeDeleted = false; } bool Create(CFSTR namePrefix) ; bool Remove(); }; #if !defined(UNDER_CE) class CCurrentDirRestorer { FString _path; public: bool NeedRestore; CCurrentDirRestorer(): NeedRestore(true) { GetCurrentDir(_path); } ~CCurrentDirRestorer() { if (!NeedRestore) return; FString s; if (GetCurrentDir(s)) if (s != _path) SetCurrentDir(_path); } }; #endif }}} #endif src/libs/7zip/unix/CPP/Windows/FileFind.cpp000066400000000000000000000342751325366651500207410ustar00rootroot00000000000000// Windows/FileFind.cpp #include "StdAfx.h" #include "FileFind.h" #include "FileIO.h" #include "../Common/StringConvert.h" #ifndef _UNICODE extern bool g_IsNT; #endif #include #include #include #ifdef ENV_HAVE_LSTAT extern "C" { int global_use_lstat=1; // default behaviour : p7zip stores symlinks instead of dumping the files they point to } #endif #define NEED_NAME_WINDOWS_TO_UNIX #include "myPrivate.h" // #define TRACEN(u) u; #define TRACEN(u) /* */ void my_windows_split_path(const AString &p_path, AString &dir , AString &base) { int pos = p_path.ReverseFind('/'); if (pos == -1) { // no separator dir = "."; if (p_path.IsEmpty()) base = "."; else base = p_path; } else if ((pos+1) < p_path.Len()) { // true separator base = p_path.Ptr(pos+1); while ((pos >= 1) && (p_path[pos-1] == '/')) pos--; if (pos == 0) dir = "/"; else dir = p_path.Left(pos); } else { // separator at the end of the path // pos = p_path.find_last_not_of("/"); pos = -1; int ind = 0; while (p_path[ind]) { if (p_path[ind] != '/') pos = ind; ind++; } if (pos == -1) { base = "/"; dir = "/"; } else { my_windows_split_path(p_path.Left(pos+1),dir,base); } } } static void my_windows_split_path(const UString &p_path, UString &dir , UString &base) { int pos = p_path.ReverseFind(L'/'); if (pos == -1) { // no separator dir = L"."; if (p_path.IsEmpty()) base = L"."; else base = p_path; } else if ((pos+1) < p_path.Len()) { // true separator base = p_path.Ptr(pos+1); while ((pos >= 1) && (p_path[pos-1] == L'/')) pos--; if (pos == 0) dir = L"/"; else dir = p_path.Left(pos); } else { // separator at the end of the path // pos = p_path.find_last_not_of("/"); pos = -1; int ind = 0; while (p_path[ind]) { if (p_path[ind] != L'/') pos = ind; ind++; } if (pos == -1) { base = L"/"; dir = L"/"; } else { my_windows_split_path(p_path.Left(pos+1),dir,base); } } } static int filter_pattern(const char *string , const char *pattern , int flags_nocase) { if ((string == 0) || (*string==0)) { if (pattern == 0) return 1; while (*pattern=='*') ++pattern; return (!*pattern); } switch (*pattern) { case '*': if (!filter_pattern(string+1,pattern,flags_nocase)) return filter_pattern(string,pattern+1,flags_nocase); return 1; case 0: if (*string==0) return 1; break; case '?': return filter_pattern(string+1,pattern+1,flags_nocase); default: if ( ((flags_nocase) && (tolower(*pattern)==tolower(*string))) || (*pattern == *string) ) { return filter_pattern(string+1,pattern+1,flags_nocase); } break; } return 0; } namespace NWindows { namespace NFile { #ifdef SUPPORT_DEVICE_FILE bool IsDeviceName(CFSTR n); #endif #if defined(WIN_LONG_PATH) bool GetLongPath(CFSTR fileName, UString &res); #endif namespace NFind { bool CFileInfo::IsDots() const { if (!IsDir() || Name.IsEmpty()) return false; if (Name[0] != FTEXT('.')) return false; return Name.Len() == 1 || (Name.Len() == 2 && Name[1] == FTEXT('.')); } #define WIN_FD_TO_MY_FI(fi, fd) \ fi.Attrib = fd.dwFileAttributes; \ fi.CTime = fd.ftCreationTime; \ fi.ATime = fd.ftLastAccessTime; \ fi.MTime = fd.ftLastWriteTime; \ fi.Size = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \ fi.IsDevice = false; /* #ifdef UNDER_CE fi.ObjectID = fd.dwOID; #else fi.ReparseTag = fd.dwReserved0; #endif */ #ifndef _UNICODE static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; } static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi) { WIN_FD_TO_MY_FI(fi, fd); fi.Name = fas2fs(fd.cFileName); } #endif //////////////////////////////// // CFindFile bool CFindFile::Close() { if(_dirp == 0) return true; int ret = closedir(_dirp); if (ret == 0) { _dirp = 0; return true; } return false; } static bool originalFilename(const UString & src, AString & res) { // Try to recover the original filename res = ""; int i=0; while (src[i]) { if (src[i] >= 256) { return false; } else { res += char(src[i]); } i++; } return true; } // Warning this function cannot update "fileInfo.Name" static int fillin_CFileInfo(CFileInfo &fileInfo,const char *filename,bool ignoreLink) { struct stat stat_info; int ret; #ifdef ENV_HAVE_LSTAT if ( (global_use_lstat) && (ignoreLink == false)) { ret = lstat(filename,&stat_info); } else #endif { ret = stat(filename,&stat_info); } // printf("fillin_CFileInfo(%s,%d)=%d mode=%o\n",filename,(int)ignoreLink,ret,(unsigned)stat_info.st_mode); if (ret != 0) return ret; /* FIXME : FILE_ATTRIBUTE_HIDDEN ? */ if (S_ISDIR(stat_info.st_mode)) { fileInfo.Attrib = FILE_ATTRIBUTE_DIRECTORY; } else { fileInfo.Attrib = FILE_ATTRIBUTE_ARCHIVE; } if (!(stat_info.st_mode & S_IWUSR)) fileInfo.Attrib |= FILE_ATTRIBUTE_READONLY; fileInfo.Attrib |= FILE_ATTRIBUTE_UNIX_EXTENSION + ((stat_info.st_mode & 0xFFFF) << 16); RtlSecondsSince1970ToFileTime( stat_info.st_ctime, &fileInfo.CTime ); RtlSecondsSince1970ToFileTime( stat_info.st_mtime, &fileInfo.MTime ); RtlSecondsSince1970ToFileTime( stat_info.st_atime, &fileInfo.ATime ); fileInfo.IsDevice = false; if (S_ISDIR(stat_info.st_mode)) { fileInfo.Size = 0; } else { // file or symbolic link fileInfo.Size = stat_info.st_size; // for a symbolic link, size = size of filename } return 0; } static int fillin_CFileInfo(CFileInfo &fi,const char *dir,const char *name,bool ignoreLink) { char filename[MAX_PATHNAME_LEN]; size_t dir_len = strlen(dir); size_t name_len = strlen(name); size_t total = dir_len + 1 + name_len + 1; // 1 = strlen("/"); + le zero character if (total >= MAX_PATHNAME_LEN) throw "fillin_CFileInfo - internal error - MAX_PATHNAME_LEN"; memcpy(filename,dir,dir_len); if (dir_len >= 1) { if (filename[dir_len-1] == CHAR_PATH_SEPARATOR) { // delete the '/' dir_len--; } } filename[dir_len] = CHAR_PATH_SEPARATOR; memcpy(filename+(dir_len+1),name,name_len+1); // copy also final '\0' #ifdef _UNICODE fi.Name = GetUnicodeString(name, CP_ACP); #else fi.Name = name; #endif int ret = fillin_CFileInfo(fi,filename,ignoreLink); if (ret != 0) { AString err_msg = "stat error for "; err_msg += filename; err_msg += " ("; err_msg += strerror(errno); err_msg += ")"; throw err_msg; } return ret; } bool CFindFile::FindFirst(CFSTR cfWildcard, CFileInfo &fi, bool ignoreLink) { if (!Close()) return false; AString Awildcard = UnicodeStringToMultiByte(cfWildcard, CP_ACP); const char * wildcard = (const char *)Awildcard; if ((!wildcard) || (wildcard[0]==0)) { SetLastError(ERROR_PATH_NOT_FOUND); return false; } my_windows_split_path(nameWindowToUnix(wildcard),_directory,_pattern); TRACEN((printf("CFindFile::FindFirst : %s (dirname=%s,pattern=%s)\n",wildcard,(const char *)_directory,(const char *)_pattern))) _dirp = ::opendir((const char *)_directory); TRACEN((printf("CFindFile::FindFirst : opendir=%p\n",_dirp))) if ((_dirp == 0) && (global_use_utf16_conversion)) { // Try to recover the original filename UString ustr = MultiByteToUnicodeString(_directory, 0); AString resultString; bool is_good = originalFilename(ustr, resultString); if (is_good) { _dirp = ::opendir((const char *)resultString); _directory = resultString; } } if (_dirp == 0) return false; struct dirent *dp; while ((dp = readdir(_dirp)) != NULL) { if (filter_pattern(dp->d_name,(const char *)_pattern,0) == 1) { int retf = fillin_CFileInfo(fi,(const char *)_directory,dp->d_name,ignoreLink); if (retf) { TRACEN((printf("CFindFile::FindFirst : closedir-1(dirp=%p)\n",_dirp))) closedir(_dirp); _dirp = 0; SetLastError( ERROR_NO_MORE_FILES ); return false; } TRACEN((printf("CFindFile::FindFirst -%s- true\n",dp->d_name))) return true; } } TRACEN((printf("CFindFile::FindFirst : closedir-2(dirp=%p)\n",_dirp))) closedir(_dirp); _dirp = 0; SetLastError( ERROR_NO_MORE_FILES ); return false; } bool CFindFile::FindNext(CFileInfo &fi) { if (_dirp == 0) { SetLastError( ERROR_INVALID_HANDLE ); return false; } struct dirent *dp; while ((dp = readdir(_dirp)) != NULL) { if (filter_pattern(dp->d_name,(const char *)_pattern,0) == 1) { int retf = fillin_CFileInfo(fi,(const char *)_directory,dp->d_name,false); if (retf) { TRACEN((printf("FindNextFileA -%s- ret_handle=FALSE (errno=%d)\n",dp->d_name,errno))) return false; } TRACEN((printf("FindNextFileA -%s- true\n",dp->d_name))) return true; } } TRACEN((printf("FindNextFileA ret_handle=FALSE (ERROR_NO_MORE_FILES)\n"))) SetLastError( ERROR_NO_MORE_FILES ); return false; } #define MY_CLEAR_FILETIME(ft) ft.dwLowDateTime = ft.dwHighDateTime = 0; void CFileInfoBase::Clear() { Size = 0; MY_CLEAR_FILETIME(CTime); MY_CLEAR_FILETIME(ATime); MY_CLEAR_FILETIME(MTime); Attrib = 0; } bool CFileInfo::Find(CFSTR wildcard, bool ignoreLink) { #ifdef SUPPORT_DEVICE_FILE if (IsDeviceName(wildcard)) { Clear(); IsDevice = true; NIO::CInFile inFile; if (!inFile.Open(wildcard)) return false; Name = wildcard + 4; if (inFile.LengthDefined) Size = inFile.Length; return true; } #endif CFindFile finder; if (finder.FindFirst(wildcard, *this,ignoreLink)) return true; #ifdef _WIN32 { DWORD lastError = GetLastError(); if (lastError == ERROR_BAD_NETPATH || lastError == ERROR_FILE_NOT_FOUND) { int len = MyStringLen(wildcard); if (len > 2 && wildcard[0] == '\\' && wildcard[1] == '\\') { int pos = FindCharPosInString(wildcard + 2, FTEXT('\\')); if (pos >= 0) { pos += 2 + 1; len -= pos; CFSTR remString = wildcard + pos; int pos2 = FindCharPosInString(remString, FTEXT('\\')); FString s = wildcard; if (pos2 < 0 || pos2 == len - 1) { FString s = wildcard; if (pos2 < 0) { pos2 = len; s += FTEXT('\\'); } s += FCHAR_ANY_MASK; if (finder.FindFirst(s, *this)) if (Name == FTEXT(".")) { Name.SetFrom(s.Ptr(pos), pos2); return true; } ::SetLastError(lastError); } } } } } #endif return false; } bool DoesFileExist(CFSTR name) { CFileInfo fi; return fi.Find(name) && !fi.IsDir(); } bool DoesDirExist(CFSTR name) { CFileInfo fi; return fi.Find(name) && fi.IsDir(); } bool DoesFileOrDirExist(CFSTR name) { CFileInfo fi; return fi.Find(name); } bool CEnumerator::NextAny(CFileInfo &fi) { if (_findFile.IsHandleAllocated()) return _findFile.FindNext(fi); else return _findFile.FindFirst(_wildcard, fi); } bool CEnumerator::Next(CFileInfo &fi) { for (;;) { if (!NextAny(fi)) return false; if (!fi.IsDots()) return true; } } bool CEnumerator::Next(CFileInfo &fi, bool &found) { if (Next(fi)) { found = true; return true; } found = false; return (::GetLastError() == ERROR_NO_MORE_FILES); } //////////////////////////////// // CFindChangeNotification // FindFirstChangeNotification can return 0. MSDN doesn't tell about it. #ifdef _WIN32 bool CFindChangeNotification::Close() { if (!IsHandleAllocated()) return true; if (!::FindCloseChangeNotification(_handle)) return false; _handle = INVALID_HANDLE_VALUE; return true; } HANDLE CFindChangeNotification::FindFirst(CFSTR pathName, bool watchSubtree, DWORD notifyFilter) { #ifndef _UNICODE if (!g_IsNT) _handle = ::FindFirstChangeNotification(fs2fas(pathName), BoolToBOOL(watchSubtree), notifyFilter); else #endif { _handle = ::FindFirstChangeNotificationW(fs2us(pathName), BoolToBOOL(watchSubtree), notifyFilter); #ifdef WIN_LONG_PATH if (!IsHandleAllocated()) { UString longPath; if (GetLongPath(pathName, longPath)) _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter); } #endif } return _handle; } #ifndef UNDER_CE bool MyGetLogicalDriveStrings(CObjectVector &driveStrings) { driveStrings.Clear(); #ifndef _UNICODE if (!g_IsNT) { driveStrings.Clear(); UINT32 size = GetLogicalDriveStrings(0, NULL); if (size == 0) return false; AString buf; UINT32 newSize = GetLogicalDriveStrings(size, buf.GetBuffer(size)); if (newSize == 0 || newSize > size) return false; AString s; for (UINT32 i = 0; i < newSize; i++) { char c = buf[i]; if (c == '\0') { driveStrings.Add(fas2fs(s)); s.Empty(); } else s += c; } return s.IsEmpty(); } else #endif { UINT32 size = GetLogicalDriveStringsW(0, NULL); if (size == 0) return false; UString buf; UINT32 newSize = GetLogicalDriveStringsW(size, buf.GetBuffer(size)); if (newSize == 0 || newSize > size) return false; UString s; for (UINT32 i = 0; i < newSize; i++) { WCHAR c = buf[i]; if (c == L'\0') { driveStrings.Add(us2fs(s)); s.Empty(); } else s += c; } return s.IsEmpty(); } } #endif #endif // _WIN32 }}} src/libs/7zip/unix/CPP/Windows/FileFind.h000066400000000000000000000072361325366651500204030ustar00rootroot00000000000000// Windows/FileFind.h #ifndef __WINDOWS_FILE_FIND_H #define __WINDOWS_FILE_FIND_H #include "../Common/MyString.h" #include "../Common/MyTypes.h" #include "Defs.h" #include /* for DIR */ #include namespace NWindows { namespace NFile { namespace NFind { namespace NAttributes { inline bool IsReadOnly(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_READONLY) != 0; } inline bool IsHidden(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_HIDDEN) != 0; } inline bool IsSystem(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_SYSTEM) != 0; } inline bool IsDir(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; } inline bool IsArchived(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_ARCHIVE) != 0; } inline bool IsCompressed(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_COMPRESSED) != 0; } inline bool IsEncrypted(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_ENCRYPTED) != 0; } } class CFileInfoBase { bool MatchesMask(UINT32 mask) const { return ((Attrib & mask) != 0); } protected: void Clear(); public: UInt64 Size; FILETIME CTime; FILETIME ATime; FILETIME MTime; DWORD Attrib; bool IsDevice; /* #ifdef UNDER_CE DWORD ObjectID; #else UINT32 ReparseTag; #endif */ bool IsArchived() const { return MatchesMask(FILE_ATTRIBUTE_ARCHIVE); } bool IsCompressed() const { return MatchesMask(FILE_ATTRIBUTE_COMPRESSED); } bool IsDir() const { return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); } bool IsEncrypted() const { return MatchesMask(FILE_ATTRIBUTE_ENCRYPTED); } bool IsHidden() const { return MatchesMask(FILE_ATTRIBUTE_HIDDEN); } bool IsNormal() const { return MatchesMask(FILE_ATTRIBUTE_NORMAL); } bool IsOffline() const { return MatchesMask(FILE_ATTRIBUTE_OFFLINE); } bool IsReadOnly() const { return MatchesMask(FILE_ATTRIBUTE_READONLY); } bool HasReparsePoint() const { return MatchesMask(FILE_ATTRIBUTE_REPARSE_POINT); } bool IsSparse() const { return MatchesMask(FILE_ATTRIBUTE_SPARSE_FILE); } bool IsSystem() const { return MatchesMask(FILE_ATTRIBUTE_SYSTEM); } bool IsTemporary() const { return MatchesMask(FILE_ATTRIBUTE_TEMPORARY); } }; struct CFileInfo: public CFileInfoBase { FString Name; bool IsDots() const; bool Find(CFSTR wildcard, bool ignoreLink = false); }; class CFindFile { friend class CEnumerator; DIR *_dirp; AString _pattern; AString _directory; public: bool IsHandleAllocated() const { return (_dirp != 0); } CFindFile(): _dirp(0) {} ~CFindFile() { Close(); } bool FindFirst(CFSTR wildcard, CFileInfo &fileInfo, bool ignoreLink = false); bool FindNext(CFileInfo &fileInfo); bool Close(); }; bool DoesFileExist(CFSTR name); bool DoesDirExist(CFSTR name); bool DoesFileOrDirExist(CFSTR name); class CEnumerator { CFindFile _findFile; FString _wildcard; bool NextAny(CFileInfo &fileInfo); public: CEnumerator(const FString &wildcard): _wildcard(wildcard) {} bool Next(CFileInfo &fileInfo); bool Next(CFileInfo &fileInfo, bool &found); }; #ifdef _WIN32 class CFindChangeNotification { HANDLE _handle; public: operator HANDLE () { return _handle; } bool IsHandleAllocated() const { return _handle != INVALID_HANDLE_VALUE && _handle != 0; } CFindChangeNotification(): _handle(INVALID_HANDLE_VALUE) {} ~CFindChangeNotification() { Close(); } bool Close(); HANDLE FindFirst(CFSTR pathName, bool watchSubtree, DWORD notifyFilter); bool FindNext() { return BOOLToBool(::FindNextChangeNotification(_handle)); } }; #endif #ifndef UNDER_CE bool MyGetLogicalDriveStrings(CObjectVector &driveStrings); #endif }}} #endif src/libs/7zip/unix/CPP/Windows/FileIO.cpp000066400000000000000000000265621325366651500203700ustar00rootroot00000000000000// Windows/FileIO.cpp #include "StdAfx.h" #include "FileIO.h" #include "Defs.h" #include "../Common/StringConvert.h" #include #include #include #include #include #define NEED_NAME_WINDOWS_TO_UNIX #include "myPrivate.h" #include #include #ifdef ENV_HAVE_LSTAT #define FD_LINK (-2) #endif #define GENERIC_READ 0x80000000 #define GENERIC_WRITE 0x40000000 extern BOOLEAN WINAPI RtlTimeToSecondsSince1970( const LARGE_INTEGER *Time, DWORD *Seconds ); namespace NWindows { namespace NFile { namespace NIO { CFileBase::~CFileBase() { Close(); } bool CFileBase::Create(LPCSTR filename, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,bool ignoreSymbolicLink) { Close(); int flags = 0; const char * name = nameWindowToUnix(filename); #ifdef O_BINARY flags |= O_BINARY; #endif #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif /* now use the umask value */ int mask = umask(0); (void)umask(mask); int mode = 0666 & ~(mask & 066); /* keep the R/W for the user */ if (dwDesiredAccess & GENERIC_WRITE) flags |= O_WRONLY; if (dwDesiredAccess & GENERIC_READ) flags |= O_RDONLY; switch (dwCreationDisposition) { case CREATE_NEW : flags |= O_CREAT | O_EXCL; break; case CREATE_ALWAYS : flags |= O_CREAT; break; case OPEN_EXISTING : break; case OPEN_ALWAYS : flags |= O_CREAT; break; // case TRUNCATE_EXISTING : flags |= O_TRUNC; break; } // printf("##DBG open(%s,0x%x,%o)##\n",name,flags,(unsigned)mode); _fd = -1; #ifdef ENV_HAVE_LSTAT if ((global_use_lstat) && (ignoreSymbolicLink == false)) { _size = readlink(name, _buffer, sizeof(_buffer)-1); if (_size > 0) { if (dwDesiredAccess & GENERIC_READ) { _fd = FD_LINK; _offset = 0; _buffer[_size]=0; } else if (dwDesiredAccess & GENERIC_WRITE) { // does not overwrite the file pointed by symbolic link if (!unlink(name)) return false; } } } #endif if (_fd == -1) { _fd = open(name,flags, mode); } if ((_fd == -1) && (global_use_utf16_conversion)) { // bug #1204993 - Try to recover the original filename UString ustr = MultiByteToUnicodeString(AString(name), 0); AString resultString; int is_good = 1; for (int i = 0; i < ustr.Len(); i++) { if (ustr[i] >= 256) { is_good = 0; break; } else { resultString += char(ustr[i]); } } if (is_good) { _fd = open((const char *)resultString,flags, mode); } } if (_fd == -1) { /* !ENV_HAVE_LSTAT : an invalid symbolic link => errno == ENOENT */ return false; } else { _unix_filename = name; } return true; } bool CFileBase::Create(LPCWSTR fileName, DWORD desiredAccess, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes,bool ignoreSymbolicLink) { Close(); return Create(UnicodeStringToMultiByte(fileName, CP_ACP), desiredAccess, shareMode, creationDisposition, flagsAndAttributes,ignoreSymbolicLink); } bool CFileBase::Close() { struct utimbuf buf; buf.actime = _lastAccessTime; buf.modtime = _lastWriteTime; _lastAccessTime = _lastWriteTime = (time_t)-1; if(_fd == -1) return true; #ifdef ENV_HAVE_LSTAT if(_fd == FD_LINK) { _fd = -1; return true; } #endif int ret = ::close(_fd); if (ret == 0) { _fd = -1; /* On some OS (mingwin, MacOSX ...), you must close the file before updating times */ if ((buf.actime != (time_t)-1) || (buf.modtime != (time_t)-1)) { struct stat oldbuf; int ret = stat((const char*)(_unix_filename),&oldbuf); if (ret == 0) { if (buf.actime == (time_t)-1) buf.actime = oldbuf.st_atime; if (buf.modtime == (time_t)-1) buf.modtime = oldbuf.st_mtime; } else { time_t current_time = time(0); if (buf.actime == (time_t)-1) buf.actime = current_time; if (buf.modtime == (time_t)-1) buf.modtime = current_time; } /* ret = */ utime((const char *)(_unix_filename), &buf); } return true; } return false; } bool CFileBase::GetLength(UINT64 &length) const { if (_fd == -1) { SetLastError( ERROR_INVALID_HANDLE ); return false; } #ifdef ENV_HAVE_LSTAT if (_fd == FD_LINK) { length = _size; return true; } #endif off_t pos_cur = ::lseek(_fd, 0, SEEK_CUR); if (pos_cur == (off_t)-1) return false; off_t pos_end = ::lseek(_fd, 0, SEEK_END); if (pos_end == (off_t)-1) return false; off_t pos_cur2 = ::lseek(_fd, pos_cur, SEEK_SET); if (pos_cur2 == (off_t)-1) return false; length = (UINT64)pos_end; return true; } bool CFileBase::Seek(INT64 distanceToMove, DWORD moveMethod, UINT64 &newPosition) { if (_fd == -1) { SetLastError( ERROR_INVALID_HANDLE ); return false; } #ifdef ENV_HAVE_LSTAT if (_fd == FD_LINK) { INT64 offset; switch (moveMethod) { case STREAM_SEEK_SET : offset = distanceToMove; break; case STREAM_SEEK_CUR : offset = _offset + distanceToMove; break; case STREAM_SEEK_END : offset = _size + distanceToMove; break; default : offset = -1; } if (offset < 0) { SetLastError( EINVAL ); return false; } if (offset > _size) offset = _size; newPosition = _offset = offset; return true; } #endif bool ret = true; off_t pos = (off_t)distanceToMove; off_t newpos = ::lseek(_fd,pos,moveMethod); if (newpos == ((off_t)-1)) { ret = false; } else { newPosition = (UINT64)newpos; } return ret; } bool CFileBase::Seek(UINT64 position, UINT64 &newPosition) { return Seek(position, FILE_BEGIN, newPosition); } ///////////////////////// // CInFile bool CInFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); } bool CInFile::Open(LPCTSTR fileName,bool ignoreSymbolicLink) { return Create(fileName, GENERIC_READ , FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,ignoreSymbolicLink); } #ifndef _UNICODE bool CInFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); } bool CInFile::Open(LPCWSTR fileName,bool ignoreSymbolicLink) { return Create(fileName, GENERIC_READ , FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,ignoreSymbolicLink); } #endif // ReadFile and WriteFile functions in Windows have BUG: // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES // (Insufficient system resources exist to complete the requested service). // static UINT32 kChunkSizeMax = (1 << 24); bool CInFile::ReadPart(void *data, UINT32 size, UINT32 &processedSize) { // if (size > kChunkSizeMax) // size = kChunkSizeMax; return Read(data,size,processedSize); } bool CInFile::Read(void *buffer, UINT32 bytesToRead, UINT32 &bytesRead) { if (_fd == -1) { SetLastError( ERROR_INVALID_HANDLE ); return false; } if (bytesToRead == 0) { bytesRead =0; return TRUE; } #ifdef ENV_HAVE_LSTAT if (_fd == FD_LINK) { if (_offset >= _size) { bytesRead = 0; return TRUE; } int len = (_size - _offset); if (len > bytesToRead) len = bytesToRead; memcpy(buffer,_buffer+_offset,len); bytesRead = len; _offset += len; return TRUE; } #endif ssize_t ret; do { ret = read(_fd,buffer,bytesToRead); } while (ret < 0 && (errno == EINTR)); if (ret != -1) { bytesRead = ret; return TRUE; } bytesRead =0; return FALSE; } ///////////////////////// // COutFile bool COutFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } static inline DWORD GetCreationDisposition(bool createAlways) { return createAlways? CREATE_ALWAYS: CREATE_NEW; } bool COutFile::Open(LPCTSTR fileName, DWORD creationDisposition) { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } bool COutFile::Create(LPCTSTR fileName, bool createAlways) { return Open(fileName, GetCreationDisposition(createAlways)); } bool COutFile::CreateAlways(LPCTSTR fileName, DWORD /* flagsAndAttributes */ ) { return Open(fileName, true); // FIXME } #ifndef _UNICODE bool COutFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } bool COutFile::Open(LPCWSTR fileName, DWORD creationDisposition) { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } bool COutFile::Create(LPCWSTR fileName, bool createAlways) { return Open(fileName, GetCreationDisposition(createAlways)); } #endif bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) { LARGE_INTEGER ltime; DWORD dw; if (_fd == -1) { SetLastError( ERROR_INVALID_HANDLE ); return false; } /* On some OS (cygwin, MacOSX ...), you must close the file before updating times */ if (aTime) { ltime.QuadPart = aTime->dwHighDateTime; ltime.QuadPart = (ltime.QuadPart << 32) | aTime->dwLowDateTime; RtlTimeToSecondsSince1970( <ime, &dw ); _lastAccessTime = dw; } if (mTime) { ltime.QuadPart = mTime->dwHighDateTime; ltime.QuadPart = (ltime.QuadPart << 32) | mTime->dwLowDateTime; RtlTimeToSecondsSince1970( <ime, &dw ); _lastWriteTime = dw; } return true; } bool COutFile::SetMTime(const FILETIME *mTime) { return SetTime(NULL, NULL, mTime); } bool COutFile::WritePart(const void *data, UINT32 size, UINT32 &processedSize) { // if (size > kChunkSizeMax) // size = kChunkSizeMax; return Write(data,size,processedSize); } bool COutFile::Write(const void *buffer, UINT32 bytesToWrite, UINT32 &bytesWritten) { if (_fd == -1) { SetLastError( ERROR_INVALID_HANDLE ); return false; } ssize_t ret; do { ret = write(_fd,buffer, bytesToWrite); } while (ret < 0 && (errno == EINTR)); if (ret != -1) { bytesWritten = ret; return TRUE; } bytesWritten =0; return FALSE; } bool COutFile::SetEndOfFile() { if (_fd == -1) { SetLastError( ERROR_INVALID_HANDLE ); return false; } bool bret = false; off_t pos_cur = lseek(_fd, 0, SEEK_CUR); if (pos_cur != (off_t)-1) { int iret = ftruncate(_fd, pos_cur); if (iret == 0) bret = true; } return bret; } bool COutFile::SetLength(UINT64 length) { UINT64 newPosition; if(!Seek(length, newPosition)) return false; if(newPosition != length) return false; return SetEndOfFile(); } }}} src/libs/7zip/unix/CPP/Windows/FileIO.h000066400000000000000000000062201325366651500200220ustar00rootroot00000000000000// Windows/FileIO.h #ifndef __WINDOWS_FILEIO_H #define __WINDOWS_FILEIO_H #include #ifndef _WIN32 #define FILE_SHARE_READ 1 #define FILE_SHARE_WRITE 2 #define FILE_BEGIN SEEK_SET #define FILE_CURRENT SEEK_CUR #define FILE_END SEEK_END #define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif namespace NWindows { namespace NFile { namespace NIO { class CFileBase { protected: int _fd; AString _unix_filename; time_t _lastAccessTime; time_t _lastWriteTime; #ifdef ENV_HAVE_LSTAT int _size; char _buffer[MAX_PATHNAME_LEN+1]; int _offset; #endif bool Create(LPCSTR fileName, DWORD desiredAccess, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes,bool ignoreSymbolicLink=false); bool Create(LPCWSTR fileName, DWORD desiredAccess, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes,bool ignoreSymbolicLink=false); public: CFileBase(): _fd(-1) {}; virtual ~CFileBase(); virtual bool Close(); bool GetLength(UINT64 &length) const; bool Seek(INT64 distanceToMove, DWORD moveMethod, UINT64 &newPosition); bool Seek(UINT64 position, UINT64 &newPosition); }; class CInFile: public CFileBase { public: bool Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); bool OpenShared(LPCTSTR fileName, bool /* shareForWrite */ ,bool ignoreSymbolicLink=false) { return Open(fileName,ignoreSymbolicLink); } bool Open(LPCTSTR fileName,bool ignoreSymbolicLink=false); #ifndef _UNICODE bool Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); bool OpenShared(LPCWSTR fileName, bool /* shareForWrite */ ,bool ignoreSymbolicLink=false) { return Open(fileName,ignoreSymbolicLink); } bool Open(LPCWSTR fileName,bool ignoreSymbolicLink=false); #endif bool ReadPart(void *data, UINT32 size, UINT32 &processedSize); bool Read(void *data, UINT32 size, UINT32 &processedSize); }; class COutFile: public CFileBase { public: bool Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); bool Open(LPCTSTR fileName, DWORD creationDisposition); bool Create(LPCTSTR fileName, bool createAlways); bool CreateAlways(LPCTSTR fileName, DWORD flagsAndAttributes); #ifndef _UNICODE bool Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); bool Open(LPCWSTR fileName, DWORD creationDisposition); bool Create(LPCWSTR fileName, bool createAlways); #endif /* void SetOpenCreationDisposition(DWORD creationDisposition) { m_CreationDisposition = creationDisposition; } void SetOpenCreationDispositionCreateAlways() { m_CreationDisposition = CREATE_ALWAYS; } */ bool SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime); bool SetMTime(const FILETIME *mTime); bool WritePart(const void *data, UINT32 size, UINT32 &processedSize); bool Write(const void *data, UINT32 size, UINT32 &processedSize); bool SetEndOfFile(); bool SetLength(UINT64 length); }; }}} #endif src/libs/7zip/unix/CPP/Windows/FileName.cpp000066400000000000000000000016761325366651500207400ustar00rootroot00000000000000// Windows/FileName.cpp #include "StdAfx.h" #include "Windows/FileName.h" #include "Common/Wildcard.h" // FIXME namespace NWindows { namespace NFile { namespace NDir { bool MyGetFullPathName(CFSTR path, FString &resFullPath); }}} namespace NWindows { namespace NFile { namespace NName { bool IsAbsolutePath(const wchar_t *s) { return s[0] == WCHAR_PATH_SEPARATOR; } // FIXME void NormalizeDirPathPrefix(CSysString &dirPath) { if (dirPath.IsEmpty()) return; if (dirPath.ReverseFind(kDirDelimiter) != dirPath.Len() - 1) dirPath += kDirDelimiter; } #ifndef _UNICODE void NormalizeDirPathPrefix(UString &dirPath) { if (dirPath.IsEmpty()) return; if (dirPath.ReverseFind(wchar_t(kDirDelimiter)) != dirPath.Len() - 1) dirPath += wchar_t(kDirDelimiter); } #endif bool GetFullPath(CFSTR dirPrefix, CFSTR s, FString &res) { res = FString(dirPrefix) + FString(s); return true; } }}} src/libs/7zip/unix/CPP/Windows/FileName.h000066400000000000000000000012611325366651500203730ustar00rootroot00000000000000// Windows/FileName.h #ifndef __WINDOWS_FILENAME_H #define __WINDOWS_FILENAME_H #include "../../C/7zTypes.h" #include "../Common/MyString.h" namespace NWindows { namespace NFile { namespace NName { const TCHAR kDirDelimiter = CHAR_PATH_SEPARATOR; const TCHAR kAnyStringWildcard = '*'; void NormalizeDirPathPrefix(CSysString &dirPath); // ensures that it ended with '\\' #ifndef _UNICODE void NormalizeDirPathPrefix(UString &dirPath); // ensures that it ended with '\\' #endif bool IsAbsolutePath(const wchar_t *s); bool GetFullPath(CFSTR dirPrefix, CFSTR path, FString &fullPath); // FIXME bool GetFullPath(CFSTR path, FString &fullPath); }}} #endif src/libs/7zip/unix/CPP/Windows/PropVariant.cpp000066400000000000000000000145551325366651500215250ustar00rootroot00000000000000// Windows/PropVariant.cpp #include "StdAfx.h" #include "../Common/Defs.h" #include "PropVariant.h" namespace NWindows { namespace NCOM { HRESULT PropVarEm_Alloc_Bstr(PROPVARIANT *p, unsigned numChars) throw() { p->bstrVal = ::SysAllocStringLen(0, numChars); if (!p->bstrVal) { p->vt = VT_ERROR; p->scode = E_OUTOFMEMORY; return E_OUTOFMEMORY; } p->vt = VT_BSTR; return S_OK; } HRESULT PropVarEm_Set_Str(PROPVARIANT *p, const char *s) throw() { UINT len = (UINT)strlen(s); p->bstrVal = ::SysAllocStringLen(0, len); if (!p->bstrVal) { p->vt = VT_ERROR; p->scode = E_OUTOFMEMORY; return E_OUTOFMEMORY; } p->vt = VT_BSTR; BSTR dest = p->bstrVal; for (UINT i = 0; i <= len; i++) dest[i] = s[i]; return S_OK; } CPropVariant::CPropVariant(const PROPVARIANT &varSrc) { vt = VT_EMPTY; InternalCopy(&varSrc); } CPropVariant::CPropVariant(const CPropVariant &varSrc) { vt = VT_EMPTY; InternalCopy(&varSrc); } CPropVariant::CPropVariant(BSTR bstrSrc) { vt = VT_EMPTY; *this = bstrSrc; } CPropVariant::CPropVariant(LPCOLESTR lpszSrc) { vt = VT_EMPTY; *this = lpszSrc; } CPropVariant& CPropVariant::operator=(const CPropVariant &varSrc) { InternalCopy(&varSrc); return *this; } CPropVariant& CPropVariant::operator=(const PROPVARIANT &varSrc) { InternalCopy(&varSrc); return *this; } CPropVariant& CPropVariant::operator=(BSTR bstrSrc) { *this = (LPCOLESTR)bstrSrc; return *this; } static const char *kMemException = "out of memory"; CPropVariant& CPropVariant::operator=(LPCOLESTR lpszSrc) { InternalClear(); vt = VT_BSTR; wReserved1 = 0; bstrVal = ::SysAllocString(lpszSrc); if (!bstrVal && lpszSrc) { throw kMemException; // vt = VT_ERROR; // scode = E_OUTOFMEMORY; } return *this; } CPropVariant& CPropVariant::operator=(const char *s) { InternalClear(); vt = VT_BSTR; wReserved1 = 0; UINT len = (UINT)strlen(s); bstrVal = ::SysAllocStringLen(0, len); if (!bstrVal) { throw kMemException; // vt = VT_ERROR; // scode = E_OUTOFMEMORY; } else { for (UINT i = 0; i <= len; i++) bstrVal[i] = s[i]; } return *this; } CPropVariant& CPropVariant::operator=(bool bSrc) throw() { if (vt != VT_BOOL) { InternalClear(); vt = VT_BOOL; } boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE; return *this; } BSTR CPropVariant::AllocBstr(unsigned numChars) { if (vt != VT_EMPTY) InternalClear(); vt = VT_BSTR; wReserved1 = 0; bstrVal = ::SysAllocStringLen(0, numChars); if (!bstrVal) { throw kMemException; // vt = VT_ERROR; // scode = E_OUTOFMEMORY; } return bstrVal; } #define SET_PROP_FUNC(type, id, dest) \ CPropVariant& CPropVariant::operator=(type value) throw() \ { if (vt != id) { InternalClear(); vt = id; } \ dest = value; return *this; } SET_PROP_FUNC(Byte, VT_UI1, bVal) // SET_PROP_FUNC(Int16, VT_I2, iVal) SET_PROP_FUNC(Int32, VT_I4, lVal) SET_PROP_FUNC(UInt32, VT_UI4, ulVal) SET_PROP_FUNC(UInt64, VT_UI8, uhVal.QuadPart) SET_PROP_FUNC(Int64, VT_I8, hVal.QuadPart) SET_PROP_FUNC(const FILETIME &, VT_FILETIME, filetime) HRESULT PropVariant_Clear(PROPVARIANT *prop) throw() { switch (prop->vt) { case VT_EMPTY: case VT_UI1: case VT_I1: case VT_I2: case VT_UI2: case VT_BOOL: case VT_I4: case VT_UI4: case VT_R4: case VT_INT: case VT_UINT: case VT_ERROR: case VT_FILETIME: case VT_UI8: case VT_R8: case VT_CY: case VT_DATE: prop->vt = VT_EMPTY; prop->wReserved1 = 0; prop->wReserved2 = 0; prop->wReserved3 = 0; prop->uhVal.QuadPart = 0; return S_OK; } return ::VariantClear((VARIANTARG *)prop); // return ::PropVariantClear(prop); // PropVariantClear can clear VT_BLOB. } HRESULT CPropVariant::Clear() throw() { if (vt == VT_EMPTY) return S_OK; return PropVariant_Clear(this); } HRESULT CPropVariant::Copy(const PROPVARIANT* pSrc) throw() { ::VariantClear((tagVARIANT *)this); switch(pSrc->vt) { case VT_UI1: case VT_I1: case VT_I2: case VT_UI2: case VT_BOOL: case VT_I4: case VT_UI4: case VT_R4: case VT_INT: case VT_UINT: case VT_ERROR: case VT_FILETIME: case VT_UI8: case VT_R8: case VT_CY: case VT_DATE: memmove((PROPVARIANT*)this, pSrc, sizeof(PROPVARIANT)); return S_OK; } return ::VariantCopy((tagVARIANT *)this, (tagVARIANT *)const_cast(pSrc)); } HRESULT CPropVariant::Attach(PROPVARIANT *pSrc) throw() { HRESULT hr = Clear(); if (FAILED(hr)) return hr; memcpy(this, pSrc, sizeof(PROPVARIANT)); pSrc->vt = VT_EMPTY; return S_OK; } HRESULT CPropVariant::Detach(PROPVARIANT *pDest) throw() { if (pDest->vt != VT_EMPTY) { HRESULT hr = PropVariant_Clear(pDest); if (FAILED(hr)) return hr; } memcpy(pDest, this, sizeof(PROPVARIANT)); vt = VT_EMPTY; return S_OK; } HRESULT CPropVariant::InternalClear() throw() { if (vt == VT_EMPTY) return S_OK; HRESULT hr = Clear(); if (FAILED(hr)) { vt = VT_ERROR; scode = hr; } return hr; } void CPropVariant::InternalCopy(const PROPVARIANT *pSrc) { HRESULT hr = Copy(pSrc); if (FAILED(hr)) { if (hr == E_OUTOFMEMORY) throw kMemException; vt = VT_ERROR; scode = hr; } } int CPropVariant::Compare(const CPropVariant &a) throw() { if (vt != a.vt) return MyCompare(vt, a.vt); switch (vt) { case VT_EMPTY: return 0; // case VT_I1: return MyCompare(cVal, a.cVal); case VT_UI1: return MyCompare(bVal, a.bVal); case VT_I2: return MyCompare(iVal, a.iVal); case VT_UI2: return MyCompare(uiVal, a.uiVal); case VT_I4: return MyCompare(lVal, a.lVal); case VT_UI4: return MyCompare(ulVal, a.ulVal); // case VT_UINT: return MyCompare(uintVal, a.uintVal); case VT_I8: return MyCompare(hVal.QuadPart, a.hVal.QuadPart); case VT_UI8: return MyCompare(uhVal.QuadPart, a.uhVal.QuadPart); case VT_BOOL: return -MyCompare(boolVal, a.boolVal); case VT_FILETIME: return ::CompareFileTime(&filetime, &a.filetime); case VT_BSTR: return 0; // Not implemented default: return 0; } } }} src/libs/7zip/unix/CPP/Windows/PropVariant.h000066400000000000000000000061071325366651500211640ustar00rootroot00000000000000// Windows/PropVariant.h #ifndef __WINDOWS_PROP_VARIANT_H #define __WINDOWS_PROP_VARIANT_H #include "../Common/MyTypes.h" #include "../Common/MyWindows.h" namespace NWindows { namespace NCOM { HRESULT PropVariant_Clear(PROPVARIANT *p) throw(); HRESULT PropVarEm_Alloc_Bstr(PROPVARIANT *p, unsigned numChars) throw(); HRESULT PropVarEm_Set_Str(PROPVARIANT *p, const char *s) throw(); inline void PropVarEm_Set_UInt32(PROPVARIANT *p, UInt32 v) throw() { p->vt = VT_UI4; p->ulVal = v; } inline void PropVarEm_Set_UInt64(PROPVARIANT *p, UInt64 v) throw() { p->vt = VT_UI8; p->uhVal.QuadPart = v; } inline void PropVarEm_Set_FileTime64(PROPVARIANT *p, UInt64 v) throw() { p->vt = VT_FILETIME; p->filetime.dwLowDateTime = (DWORD)v; p->filetime.dwHighDateTime = (DWORD)(v >> 32); } inline void PropVarEm_Set_Bool(PROPVARIANT *p, bool b) throw() { p->vt = VT_BOOL; p->boolVal = (b ? VARIANT_TRUE : VARIANT_FALSE); } class CPropVariant : public tagPROPVARIANT { public: CPropVariant() { vt = VT_EMPTY; wReserved1 = 0; // wReserved2 = 0; // wReserved3 = 0; // uhVal.QuadPart = 0; bstrVal = 0; } ~CPropVariant() throw() { Clear(); } CPropVariant(const PROPVARIANT &varSrc); CPropVariant(const CPropVariant &varSrc); CPropVariant(BSTR bstrSrc); CPropVariant(LPCOLESTR lpszSrc); CPropVariant(bool bSrc) { vt = VT_BOOL; wReserved1 = 0; boolVal = (bSrc ? VARIANT_TRUE : VARIANT_FALSE); }; CPropVariant(Byte value) { vt = VT_UI1; wReserved1 = 0; bVal = value; } private: CPropVariant(Int16 value); // { vt = VT_I2; wReserved1 = 0; iVal = value; } CPropVariant(Int32 value); // { vt = VT_I4; wReserved1 = 0; lVal = value; } public: CPropVariant(UInt32 value) { vt = VT_UI4; wReserved1 = 0; ulVal = value; } CPropVariant(UInt64 value) { vt = VT_UI8; wReserved1 = 0; uhVal.QuadPart = value; } CPropVariant(Int64 value) { vt = VT_I8; wReserved1 = 0; hVal.QuadPart = value; } CPropVariant(const FILETIME &value) { vt = VT_FILETIME; wReserved1 = 0; filetime = value; } CPropVariant& operator=(const CPropVariant &varSrc); CPropVariant& operator=(const PROPVARIANT &varSrc); CPropVariant& operator=(BSTR bstrSrc); CPropVariant& operator=(LPCOLESTR lpszSrc); CPropVariant& operator=(const char *s); CPropVariant& operator=(bool bSrc) throw(); CPropVariant& operator=(Byte value) throw(); private: CPropVariant& operator=(Int16 value) throw(); public: CPropVariant& operator=(Int32 value) throw(); CPropVariant& operator=(UInt32 value) throw(); CPropVariant& operator=(UInt64 value) throw(); CPropVariant& operator=(Int64 value) throw(); CPropVariant& operator=(const FILETIME &value) throw(); BSTR AllocBstr(unsigned numChars); HRESULT Clear() throw(); HRESULT Copy(const PROPVARIANT *pSrc) throw(); HRESULT Attach(PROPVARIANT *pSrc) throw(); HRESULT Detach(PROPVARIANT *pDest) throw(); HRESULT InternalClear() throw(); void InternalCopy(const PROPVARIANT *pSrc); int Compare(const CPropVariant &a) throw(); }; }} #endif src/libs/7zip/unix/CPP/Windows/PropVariantConv.cpp000066400000000000000000000065761325366651500223570ustar00rootroot00000000000000// PropVariantConvert.cpp #include "StdAfx.h" #include "../Common/IntToString.h" #include "Defs.h" #include "PropVariantConv.h" #define UINT_TO_STR_2(c, val) { s[0] = (c); s[1] = (char)('0' + (val) / 10); s[2] = (char)('0' + (val) % 10); s += 3; } bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime, bool includeSeconds) throw() { SYSTEMTIME st; if (!BOOLToBool(FileTimeToSystemTime(&ft, &st))) { *s = 0; return false; } unsigned val = st.wYear; if (val >= 10000) { *s++ = (char)('0' + val / 10000); val %= 10000; } { s[3] = (char)('0' + val % 10); val /= 10; s[2] = (char)('0' + val % 10); val /= 10; s[1] = (char)('0' + val % 10); s[0] = (char)('0' + val / 10); s += 4; } UINT_TO_STR_2('-', st.wMonth); UINT_TO_STR_2('-', st.wDay); if (includeTime) { UINT_TO_STR_2(' ', st.wHour); UINT_TO_STR_2(':', st.wMinute); if (includeSeconds) UINT_TO_STR_2(':', st.wSecond); } *s = 0; return true; } void ConvertFileTimeToString(const FILETIME &ft, wchar_t *dest, bool includeTime, bool includeSeconds) throw() { char s[32]; ConvertFileTimeToString(ft, s, includeTime, includeSeconds); for (unsigned i = 0;; i++) { unsigned char c = s[i]; dest[i] = c; if (c == 0) return; } } void ConvertPropVariantToShortString(const PROPVARIANT &prop, char *dest) throw() { *dest = 0; switch (prop.vt) { case VT_EMPTY: return; case VT_BSTR: dest[0] = '?'; dest[1] = 0; return; case VT_UI1: ConvertUInt32ToString(prop.bVal, dest); return; case VT_UI2: ConvertUInt32ToString(prop.uiVal, dest); return; case VT_UI4: ConvertUInt32ToString(prop.ulVal, dest); return; case VT_UI8: ConvertUInt64ToString(prop.uhVal.QuadPart, dest); return; case VT_FILETIME: ConvertFileTimeToString(prop.filetime, dest, true, true); return; // case VT_I1: return ConvertInt64ToString(prop.cVal, dest); return; case VT_I2: ConvertInt64ToString(prop.iVal, dest); return; case VT_I4: ConvertInt64ToString(prop.lVal, dest); return; case VT_I8: ConvertInt64ToString(prop.hVal.QuadPart, dest); return; case VT_BOOL: dest[0] = VARIANT_BOOLToBool(prop.boolVal) ? '+' : '-'; dest[1] = 0; return; default: dest[0] = '?'; dest[1] = ':'; ConvertUInt64ToString(prop.vt, dest + 2); } } void ConvertPropVariantToShortString(const PROPVARIANT &prop, wchar_t *dest) throw() { *dest = 0; switch (prop.vt) { case VT_EMPTY: return; case VT_BSTR: dest[0] = '?'; dest[1] = 0; return; case VT_UI1: ConvertUInt32ToString(prop.bVal, dest); return; case VT_UI2: ConvertUInt32ToString(prop.uiVal, dest); return; case VT_UI4: ConvertUInt32ToString(prop.ulVal, dest); return; case VT_UI8: ConvertUInt64ToString(prop.uhVal.QuadPart, dest); return; case VT_FILETIME: ConvertFileTimeToString(prop.filetime, dest, true, true); return; // case VT_I1: return ConvertInt64ToString(prop.cVal, dest); return; case VT_I2: ConvertInt64ToString(prop.iVal, dest); return; case VT_I4: ConvertInt64ToString(prop.lVal, dest); return; case VT_I8: ConvertInt64ToString(prop.hVal.QuadPart, dest); return; case VT_BOOL: dest[0] = VARIANT_BOOLToBool(prop.boolVal) ? '+' : '-'; dest[1] = 0; return; default: dest[0] = '?'; dest[1] = ':'; ConvertUInt32ToString(prop.vt, dest + 2); } } src/libs/7zip/unix/CPP/Windows/PropVariantConv.h000066400000000000000000000021401325366651500220030ustar00rootroot00000000000000// Windows/PropVariantConv.h #ifndef __PROP_VARIANT_CONV_H #define __PROP_VARIANT_CONV_H #include "../Common/MyTypes.h" // provide at least 32 bytes for buffer including zero-end bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime = true, bool includeSeconds = true) throw(); void ConvertFileTimeToString(const FILETIME &ft, wchar_t *s, bool includeTime = true, bool includeSeconds = true) throw(); // provide at least 32 bytes for buffer including zero-end // don't send VT_BSTR to these functions void ConvertPropVariantToShortString(const PROPVARIANT &prop, char *dest) throw(); void ConvertPropVariantToShortString(const PROPVARIANT &prop, wchar_t *dest) throw(); inline bool ConvertPropVariantToUInt64(const PROPVARIANT &prop, UInt64 &value) { switch (prop.vt) { case VT_UI8: value = (UInt64)prop.uhVal.QuadPart; return true; case VT_UI4: value = prop.ulVal; return true; case VT_UI2: value = prop.uiVal; return true; case VT_UI1: value = prop.bVal; return true; case VT_EMPTY: return false; default: throw 151199; } } #endif src/libs/7zip/unix/CPP/Windows/Synchronization.cpp000066400000000000000000000136201325366651500224510ustar00rootroot00000000000000// Windows/Synchronization.cpp #include "StdAfx.h" #include "Synchronization.h" // #define TRACEN(u) u; #define TRACEN(u) /* */ #define MAGIC 0x1234CAFE class CSynchroTest { int _magic; public: CSynchroTest() { _magic = MAGIC; } void testConstructor() { if (_magic != MAGIC) { printf("ERROR : no constructors called during loading of plugins (please look at LINK_SHARED in makefile.machine)\n"); exit(EXIT_FAILURE); } } }; static CSynchroTest gbl_synchroTest; extern "C" void sync_TestConstructor(void) { gbl_synchroTest.testConstructor(); } namespace NWindows { namespace NSynchronization { #ifndef ENV_BEOS #ifdef DEBUG_SYNCHRO void CSynchro::dump_error(int ligne,int ret,const char *text,void *param) { printf("\n##T%d#ERROR2 (l=%d) %s : param=%p ret = %d (%s)##\n",(int)pthread_self(),ligne,text,param,ret,strerror(ret)); // abort(); } CSynchro::CSynchro() { TRACEN((printf("\nT%d : E1-CSynchro(this=%p,m=%p,cond=%p)\n",(int)pthread_self(),(void *)this,(void *)&_object,(void *)&_cond))) _isValid = false; } void CSynchro::Create() { TRACEN((printf("\nT%d : E1-CSynchro::Create(this=%p,m=%p,cond=%p)\n",(int)pthread_self(),(void *)this,(void *)&_object,(void *)&_cond))) pthread_mutexattr_t mutexattr; memset(&mutexattr,0,sizeof(mutexattr)); int ret = pthread_mutexattr_init(&mutexattr); if (ret != 0) { dump_error(__LINE__,ret,"pthread_mutexattr_init",&mutexattr); } ret = pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK); if (ret != 0) dump_error(__LINE__,ret,"pthread_mutexattr_settype",&mutexattr); ret = ::pthread_mutex_init(&_object,&mutexattr); if (ret != 0) dump_error(__LINE__,ret,"pthread_mutex_init",&_object); ret = ::pthread_cond_init(&_cond,0); if (ret != 0) dump_error(__LINE__,ret,"pthread_cond_init",&_cond); TRACEN((printf("\nT%d : E2-CSynchro::Create(m=%p,cond=%p)\n",(int)pthread_self(),(void *)&_object,(void *)&_cond))) } CSynchro::~CSynchro() { TRACEN((printf("\nT%d : E1-~CSynchro(this=%p,m=%p,cond=%p)\n",(int)pthread_self(),(void *)this,(void *)&_object,(void *)&_cond))) if (_isValid) { int ret = ::pthread_mutex_destroy(&_object); if (ret != 0) dump_error(__LINE__,ret,"pthread_mutex_destroy",&_object); ret = ::pthread_cond_destroy(&_cond); if (ret != 0) dump_error(__LINE__,ret,"pthread_cond_destroy",&_cond); TRACEN((printf("\nT%d : E2-~CSynchro(m=%p,cond=%p)\n",(int)pthread_self(),(void *)&_object,(void *)&_cond))) } _isValid = false; } void CSynchro::Enter() { TRACEN((printf("\nT%d : E1-CSynchro::Enter(%p)\n",(int)pthread_self(),(void *)&_object))) int ret = ::pthread_mutex_lock(&_object); if (ret != 0) { dump_error(__LINE__,ret,"CSynchro::Enter-pthread_mutex_lock",&_object); } TRACEN((printf("\nT%d : E2-CSynchro::Enter(%p)\n",(int)pthread_self(),(void *)&_object))) } void CSynchro::Leave() { TRACEN((printf("\nT%d : E1-CSynchro::Leave(%p)\n",(int)pthread_self(),(void *)&_object))) int ret = ::pthread_mutex_unlock(&_object); if (ret != 0) dump_error(__LINE__,ret,"Leave::pthread_mutex_unlock",&_object); TRACEN((printf("\nT%d : E2-CSynchro::Leave(%p)\n",(int)pthread_self(),(void *)&_object))) } void CSynchro::WaitCond() { TRACEN((printf("\nT%d : E1-CSynchro::WaitCond(%p,%p)\n",(int)pthread_self(),(void *)&_cond,(void *)&_object))) int ret = ::pthread_cond_wait(&_cond, &_object); if (ret != 0) dump_error(__LINE__,ret,"pthread_cond_wait",&_cond); TRACEN((printf("\nT%d : E2-CSynchro::WaitCond(%p,%p)\n",(int)pthread_self(),(void *)&_cond,(void *)&_object))) } void CSynchro::LeaveAndSignal() { TRACEN((printf("\nT%d : E1-CSynchro::LeaveAndSignal(%p)\n",(int)pthread_self(),(void *)&_cond))) int ret = ::pthread_cond_broadcast(&_cond); if (ret != 0) dump_error(__LINE__,ret,"pthread_cond_broadcast",&_cond); TRACEN((printf("\nT%d : E2-CSynchro::LeaveAndSignal(%p)\n",(int)pthread_self(),(void *)&_object))) ret = ::pthread_mutex_unlock(&_object); if (ret != 0) dump_error(__LINE__,ret,"LeaveAndSignal::pthread_mutex_unlock",&_object); TRACEN((printf("\nT%d : E3-CSynchro::LeaveAndSignal(%p)\n",(int)pthread_self(),(void *)&_cond))) } #endif #endif }} DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE *handles, BOOL wait_all, DWORD timeout ) { TRACEN((printf("\nT%d : E1-WaitForMultipleObjects(%d)\n",(int)pthread_self(),(int)count))) if (wait_all != FALSE) { printf("\n\n INTERNAL ERROR - WaitForMultipleObjects(...) wait_all(%d) != FALSE\n\n",(unsigned)wait_all); abort(); } if (timeout != INFINITE) { printf("\n\n INTERNAL ERROR - WaitForMultipleObjects(...) timeout(%u) != INFINITE\n\n",(unsigned)timeout); abort(); } if (count < 1) { printf("\n\n INTERNAL ERROR - WaitForMultipleObjects(...) count(%u) < 1\n\n",(unsigned)count); abort(); } NWindows::NSynchronization::CSynchro *synchro = handles[0]->_sync; TRACEN((printf("\nT%d : E2-WaitForMultipleObjects(%d)\n",(int)pthread_self(),(int)count))) synchro->Enter(); TRACEN((printf("\nT%d : E3-WaitForMultipleObjects(%d)\n",(int)pthread_self(),(int)count))) #ifdef DEBUG_SYNCHRO for(DWORD i=1;i_sync) { printf("\n\n INTERNAL ERROR - WaitForMultipleObjects(...) synchro(%p) != handles[%d]->_sync(%p)\n\n", synchro,(unsigned)i,handles[i]->_sync); abort(); } } #endif while(1) { for(DWORD i=0;iIsSignaledAndUpdate()) { synchro->Leave(); TRACEN((printf("\nT%d : E4-WaitForMultipleObjects(%d)\n",(int)pthread_self(),(int)count))) return WAIT_OBJECT_0+i; } } synchro->WaitCond(); } synchro->Leave(); return ETIMEDOUT; // WAIT_TIMEOUT; } src/libs/7zip/unix/CPP/Windows/Synchronization.h000066400000000000000000000112441325366651500221160ustar00rootroot00000000000000// Windows/Synchronization.h #ifndef __WINDOWS_SYNCHRONIZATION_H #define __WINDOWS_SYNCHRONIZATION_H #include "Defs.h" extern "C" { #include "../../C/Threads.h" } #ifdef _WIN32 #include "Handle.h" #endif namespace NWindows { namespace NSynchronization { class Uncopyable { protected: Uncopyable() {} // allow construction ~Uncopyable() {} // and destruction of derived objects... private: Uncopyable(const Uncopyable&); // ...but prevent copying Uncopyable& operator=(const Uncopyable&); }; class CBaseEvent // FIXME : private Uncopyable { protected: ::CEvent _object; public: bool IsCreated() { return Event_IsCreated(&_object) != 0; } #ifdef _WIN32 operator HANDLE() { return _object.handle; } #endif CBaseEvent() { Event_Construct(&_object); } ~CBaseEvent() { Close(); } WRes Close() { return Event_Close(&_object); } #ifdef _WIN32 WRes Create(bool manualReset, bool initiallyOwn, LPCTSTR name = NULL, LPSECURITY_ATTRIBUTES securityAttributes = NULL) { _object.handle = ::CreateEvent(securityAttributes, BoolToBOOL(manualReset), BoolToBOOL(initiallyOwn), name); if (_object.handle != 0) return 0; return ::GetLastError(); } WRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name) { _object.handle = ::OpenEvent(desiredAccess, BoolToBOOL(inheritHandle), name); if (_object.handle != 0) return 0; return ::GetLastError(); } #endif WRes Set() { return Event_Set(&_object); } // bool Pulse() { return BOOLToBool(::PulseEvent(_handle)); } WRes Reset() { return Event_Reset(&_object); } WRes Lock() { return Event_Wait(&_object); } }; class CManualResetEvent: public CBaseEvent { public: WRes Create(bool initiallyOwn = false) { return ManualResetEvent_Create(&_object, initiallyOwn ? 1: 0); } WRes CreateIfNotCreated() { if (IsCreated()) return 0; return ManualResetEvent_CreateNotSignaled(&_object); } #ifdef _WIN32 WRes CreateWithName(bool initiallyOwn, LPCTSTR name) { return CBaseEvent::Create(true, initiallyOwn, name); } #endif }; class CAutoResetEvent: public CBaseEvent { public: WRes Create() { return AutoResetEvent_CreateNotSignaled(&_object); } WRes CreateIfNotCreated() { if (IsCreated()) return 0; return AutoResetEvent_CreateNotSignaled(&_object); } }; #ifdef _WIN32 class CObject: public CHandle { public: WRes Lock(DWORD timeoutInterval = INFINITE) { return (::WaitForSingleObject(_handle, timeoutInterval) == WAIT_OBJECT_0 ? 0 : ::GetLastError()); } }; class CMutex: public CObject { public: WRes Create(bool initiallyOwn, LPCTSTR name = NULL, LPSECURITY_ATTRIBUTES securityAttributes = NULL) { _handle = ::CreateMutex(securityAttributes, BoolToBOOL(initiallyOwn), name); if (_handle != 0) return 0; return ::GetLastError(); } WRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name) { _handle = ::OpenMutex(desiredAccess, BoolToBOOL(inheritHandle), name); if (_handle != 0) return 0; return ::GetLastError(); } WRes Release() { return ::ReleaseMutex(_handle) ? 0 : ::GetLastError(); } }; class CMutexLock { CMutex *_object; public: CMutexLock(CMutex &object): _object(&object) { _object->Lock(); } ~CMutexLock() { _object->Release(); } }; #endif class CSemaphore : private Uncopyable { ::CSemaphore _object; public: CSemaphore() { Semaphore_Construct(&_object); } ~CSemaphore() { Close(); } WRes Close() { return Semaphore_Close(&_object); } #ifdef _WIN32 operator HANDLE() { return _object.handle; } #endif WRes Create(UInt32 initiallyCount, UInt32 maxCount) { return Semaphore_Create(&_object, initiallyCount, maxCount); } WRes Release() { return Semaphore_Release1(&_object); } WRes Release(UInt32 releaseCount) { return Semaphore_ReleaseN(&_object, releaseCount); } WRes Lock() { return Semaphore_Wait(&_object); } }; class CCriticalSection : private Uncopyable { ::CCriticalSection _object; public: CCriticalSection() { CriticalSection_Init(&_object); } ~CCriticalSection() { CriticalSection_Delete(&_object); } void Enter() { CriticalSection_Enter(&_object); } void Leave() { CriticalSection_Leave(&_object); } }; class CCriticalSectionLock : private Uncopyable { CCriticalSection *_object; void Unlock() { _object->Leave(); } public: CCriticalSectionLock(CCriticalSection &object): _object(&object) {_object->Enter(); } ~CCriticalSectionLock() { Unlock(); } }; }} #ifndef _WIN32 #include "Synchronization2.h" #endif #endif src/libs/7zip/unix/CPP/Windows/Synchronization2.h000066400000000000000000000121271325366651500222010ustar00rootroot00000000000000// Windows/Synchronization.h #ifdef ENV_BEOS #include #include #include #endif /* Remark : WFMO = WaitForMultipleObjects */ namespace NWindows { namespace NSynchronization { struct CBaseHandleWFMO; } } typedef NWindows::NSynchronization::CBaseHandleWFMO *HANDLE; DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE *handles, BOOL wait_all, DWORD timeout ); namespace NWindows { namespace NSynchronization { #ifdef ENV_BEOS class CSynchro : BLocker, private Uncopyable { #define MAX_THREAD 256 thread_id _waiting[MAX_THREAD]; // std::list _waiting; int index_waiting; public: CSynchro() { index_waiting = 0; } void Create() { index_waiting = 0; } ~CSynchro() {} void Enter() { Lock(); } void Leave() { Unlock(); } void WaitCond() { _waiting[index_waiting++] = find_thread(NULL); // _waiting.push_back(find_thread(NULL)); thread_id sender; Unlock(); int msg = receive_data(&sender, NULL, 0); Lock(); } void LeaveAndSignal() { // Unlock(); // Lock(); // for (std::list::iterator index = _waiting.begin(); index != _waiting.end(); index++) for(int index = 0 ; index < index_waiting ; index++) { send_data(_waiting[index], '7zCN', NULL, 0); } index_waiting = 0; // _waiting.clear(); Unlock(); } }; #else // #ifdef ENV_BEOS #ifdef DEBUG_SYNCHRO class CSynchro: private Uncopyable { pthread_mutex_t _object; pthread_cond_t _cond; bool _isValid; void dump_error(int ligne,int ret,const char *text,void *param); public: CSynchro(); ~CSynchro(); void Create(); void Enter(); void Leave(); void WaitCond(); void LeaveAndSignal(); }; #else // #ifdef DEBUG_SYNCHRO class CSynchro : private Uncopyable { pthread_mutex_t _object; pthread_cond_t _cond; bool _isValid; public: CSynchro() { _isValid = false; } ~CSynchro() { if (_isValid) { ::pthread_mutex_destroy(&_object); ::pthread_cond_destroy(&_cond); } _isValid = false; } void Create() { ::pthread_mutex_init(&_object,0); ::pthread_cond_init(&_cond,0); } void Enter() { ::pthread_mutex_lock(&_object); } void Leave() { ::pthread_mutex_unlock(&_object); } void WaitCond() { ::pthread_cond_wait(&_cond, &_object); } void LeaveAndSignal() { ::pthread_cond_broadcast(&_cond); ::pthread_mutex_unlock(&_object); } }; #endif // #ifdef DEBUG_SYNCHRO #endif // #ifdef ENV_BEOS struct CBaseHandleWFMO // FIXME : private Uncopyable { CSynchro *_sync; CBaseHandleWFMO() { } operator HANDLE() { return this; } virtual bool IsSignaledAndUpdate() = 0; }; class CBaseEventWFMO : public CBaseHandleWFMO { bool _manual_reset; bool _state; public: bool IsCreated() { return (this->_sync != 0); } CBaseEventWFMO() { this->_sync = 0; } ~CBaseEventWFMO() { Close(); } WRes Close() { this->_sync = 0; return S_OK; } WRes Create(CSynchro *sync,bool manualReset, bool initiallyOwn) { this->_sync = sync; this->_manual_reset = manualReset; this->_state = initiallyOwn; return S_OK; } WRes Set() { this->_sync->Enter(); this->_state = true; this->_sync->LeaveAndSignal(); return S_OK; } WRes Reset() { this->_sync->Enter(); this->_state = false; this->_sync->Leave(); return S_OK; } virtual bool IsSignaledAndUpdate() { if (this->_state == true) { if (this->_manual_reset == false) this->_state = false; return true; } return false; } }; class CManualResetEventWFMO: public CBaseEventWFMO { public: WRes Create(CSynchro *sync,bool initiallyOwn = false) { return CBaseEventWFMO::Create(sync,true, initiallyOwn); } }; class CAutoResetEventWFMO: public CBaseEventWFMO { public: WRes Create(CSynchro *sync) { return CBaseEventWFMO::Create(sync,false, false); } WRes CreateIfNotCreated(CSynchro *sync) { if (IsCreated()) return 0; return CBaseEventWFMO::Create(sync,false, false); } }; class CSemaphoreWFMO : public CBaseHandleWFMO { LONG _count; LONG _maxCount; public: CSemaphoreWFMO() : _count(0), _maxCount(0) { this->_sync=0;} WRes Create(CSynchro *sync,LONG initiallyCount, LONG maxCount) { if ((initiallyCount < 0) || (initiallyCount > maxCount) || (maxCount < 1)) return S_FALSE; this->_sync = sync; this->_count = initiallyCount; this->_maxCount = maxCount; return S_OK; } WRes Release(LONG releaseCount = 1) { if (releaseCount < 1) return S_FALSE; this->_sync->Enter(); LONG newCount = this->_count + releaseCount; if (newCount > this->_maxCount) { this->_sync->Leave(); return S_FALSE; } this->_count = newCount; this->_sync->LeaveAndSignal(); return S_OK; } WRes Close() { this->_sync=0; return S_OK; } virtual bool IsSignaledAndUpdate() { if (this->_count > 0) { this->_count--; return true; } return false; } }; }} src/libs/7zip/unix/CPP/Windows/System.cpp000066400000000000000000000107661325366651500205440ustar00rootroot00000000000000#include #include #if defined (__NetBSD__) || defined(__OpenBSD__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) #include #include #elif defined(__linux__) || defined(__CYGWIN__) || defined(sun) || defined(__NETWARE__) #include #elif defined(hpux) || defined(__hpux) #include #include #endif #if defined(__NETWARE__) #include #endif #if defined(ENV_BEOS) #include #endif #include "Common/MyTypes.h" namespace NWindows { namespace NSystem { /************************ GetNumberOfProcessors ************************/ #if defined (__NetBSD__) || defined(__OpenBSD__) UInt32 GetNumberOfProcessors() { int mib[2], value; int nbcpu = 1; mib[0] = CTL_HW; mib[1] = HW_NCPU; size_t len = sizeof(size_t); if (sysctl(mib, 2, &value, &len, NULL, 0) >= 0) if (value > nbcpu) nbcpu = value; return nbcpu; } #elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) UInt32 GetNumberOfProcessors() { int nbcpu = 1; size_t value; size_t len = sizeof(value); if (sysctlbyname("hw.ncpu", &value, &len, NULL, 0) == 0) nbcpu = value; return nbcpu; } #elif defined (__APPLE__) UInt32 GetNumberOfProcessors() { int nbcpu = 1,value; size_t valSize = sizeof(value); if (sysctlbyname ("hw.ncpu", &value, &valSize, NULL, 0) == 0) nbcpu = value; return nbcpu; } #elif defined(__linux__) || defined(__CYGWIN__) || defined(sun) UInt32 GetNumberOfProcessors() { int nbcpu = sysconf (_SC_NPROCESSORS_CONF); if (nbcpu < 1) nbcpu = 1; return nbcpu; } #elif defined(hpux) || defined(__hpux) UInt32 GetNumberOfProcessors() { struct pst_dynamic psd; if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1) return (UInt32)psd.psd_proc_cnt; return 1; } #elif defined(__NETWARE__) UInt32 GetNumberOfProcessors() { // int nbcpu = get_nprocs_conf(); int nbcpu = get_nprocs(); if (nbcpu < 1) nbcpu = 1; return nbcpu; } #elif defined(ENV_BEOS) UInt32 GetNumberOfProcessors() { system_info info; get_system_info(&info); int nbcpu = info.cpu_count; if (nbcpu < 1) nbcpu = 1; return nbcpu; } #else #warning Generic GetNumberOfProcessors UInt32 GetNumberOfProcessors() { return 1; } #endif /************************ GetRamSize ************************/ UInt64 GetRamSize() { UInt64 ullTotalPhys = 128 * 1024 * 1024; // default : 128MB #ifdef linux FILE * f = fopen( "/proc/meminfo", "r" ); if (f) { char buffer[256]; unsigned long total; ullTotalPhys = 0; while (fgets( buffer, sizeof(buffer), f )) { /* old style /proc/meminfo ... */ if (sscanf( buffer, "Mem: %lu", &total)) { ullTotalPhys += total; } /* new style /proc/meminfo ... */ if (sscanf(buffer, "MemTotal: %lu", &total)) ullTotalPhys = ((UInt64)total)*1024; } fclose( f ); } #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__APPLE__) #ifdef HW_MEMSIZE uint64_t val = 0; // support 2Gb+ RAM int mib[2] = { CTL_HW, HW_MEMSIZE }; #else // HW_MEMSIZE unsigned int val = 0; // For old system int mib[2] = { CTL_HW, HW_PHYSMEM }; #endif // HW_MEMSIZE size_t size_sys = sizeof(val); sysctl(mib, 2, &val, &size_sys, NULL, 0); if (val) ullTotalPhys = val; #elif defined(__CYGWIN__) unsigned long pagesize=4096; // FIXME - sysconf(_SC_PAGESIZE) returns 65536 !? // see http://readlist.com/lists/cygwin.com/cygwin/0/3313.html unsigned long maxpages=sysconf(_SC_PHYS_PAGES); ullTotalPhys = ((UInt64)pagesize)*maxpages; #elif defined ( sun ) || defined(__NETWARE__) unsigned long pagesize=sysconf(_SC_PAGESIZE); unsigned long maxpages=sysconf(_SC_PHYS_PAGES); ullTotalPhys = ((UInt64)pagesize)*maxpages; #elif defined(hpux) || defined(__hpux) struct pst_static pst; union pstun pu; pu.pst_static = &pst; if ( pstat( PSTAT_STATIC, pu, (size_t)sizeof(pst), (size_t)0, 0 ) != -1 ) { ullTotalPhys = ((UInt64)pst.physical_memory)*pst.page_size; } #elif defined(ENV_BEOS) system_info info; get_system_info(&info); ullTotalPhys = info.max_pages; ullTotalPhys *= 4096; #else #warning Generic GetRamSize #endif return ullTotalPhys; } } } src/libs/7zip/unix/CPP/Windows/System.h000066400000000000000000000003471325366651500202030ustar00rootroot00000000000000// Windows/System.h #ifndef __WINDOWS_SYSTEM_H #define __WINDOWS_SYSTEM_H #include "../Common/MyTypes.h" namespace NWindows { namespace NSystem { UInt32 GetNumberOfProcessors(); UInt64 GetRamSize(); }} #endif src/libs/7zip/unix/CPP/Windows/Thread.h000066400000000000000000000022451325366651500201250ustar00rootroot00000000000000// Windows/Thread.h #ifndef __WINDOWS_THREAD_H #define __WINDOWS_THREAD_H #include "Defs.h" extern "C" { #include "../../C/Threads.h" } namespace NWindows { class CThread { ::CThread thread; public: CThread() { Thread_Construct(&thread); } ~CThread() { Close(); } bool IsCreated() { return Thread_WasCreated(&thread) != 0; } WRes Close() { return Thread_Close(&thread); } WRes Create(THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter) { return Thread_Create(&thread, startAddress, parameter); } WRes Wait() { return Thread_Wait(&thread); } #ifdef _WIN32 operator HANDLE() { return thread; } void Attach(HANDLE handle) { thread = handle; } HANDLE Detach() { HANDLE h = thread; thread = NULL; return h; } DWORD Resume() { return ::ResumeThread(thread); } DWORD Suspend() { return ::SuspendThread(thread); } bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread, exitCode)); } int GetPriority() { return ::GetThreadPriority(thread); } bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread, priority)); } #endif }; } #endif src/libs/7zip/unix/CPP/Windows/TimeUtils.cpp000066400000000000000000000131001325366651500211600ustar00rootroot00000000000000// Windows/TimeUtils.cpp #include "StdAfx.h" #include "Defs.h" #include "TimeUtils.h" namespace NWindows { namespace NTime { static const UInt32 kNumTimeQuantumsInSecond = 10000000; static const UInt32 kFileTimeStartYear = 1601; static const UInt32 kDosTimeStartYear = 1980; static const UInt32 kUnixTimeStartYear = 1970; static const UInt64 kUnixTimeOffset = (UInt64)60 * 60 * 24 * (89 + 365 * (kUnixTimeStartYear - kFileTimeStartYear)); static const UInt64 kNumSecondsInFileTime = (UInt64)(Int64)-1 / kNumTimeQuantumsInSecond; bool DosTimeToFileTime(UInt32 dosTime, FILETIME &ft) throw() { #if defined(_WIN32) && !defined(UNDER_CE) return BOOLToBool(::DosDateTimeToFileTime((UInt16)(dosTime >> 16), (UInt16)(dosTime & 0xFFFF), &ft)); #else ft.dwLowDateTime = 0; ft.dwHighDateTime = 0; UInt64 res; if (!GetSecondsSince1601(kDosTimeStartYear + (dosTime >> 25), (dosTime >> 21) & 0xF, (dosTime >> 16) & 0x1F, (dosTime >> 11) & 0x1F, (dosTime >> 5) & 0x3F, (dosTime & 0x1F) * 2, res)) return false; res *= kNumTimeQuantumsInSecond; ft.dwLowDateTime = (UInt32)res; ft.dwHighDateTime = (UInt32)(res >> 32); return true; #endif } static const UInt32 kHighDosTime = 0xFF9FBF7D; static const UInt32 kLowDosTime = 0x210000; #define PERIOD_4 (4 * 365 + 1) #define PERIOD_100 (PERIOD_4 * 25 - 1) #define PERIOD_400 (PERIOD_100 * 4 + 1) bool FileTimeToDosTime(const FILETIME &ft, UInt32 &dosTime) throw() { #if defined(_WIN32) && !defined(UNDER_CE) WORD datePart, timePart; if (!::FileTimeToDosDateTime(&ft, &datePart, &timePart)) { dosTime = (ft.dwHighDateTime >= 0x01C00000) ? kHighDosTime : kLowDosTime; return false; } dosTime = (((UInt32)datePart) << 16) + timePart; #else unsigned year, mon, day, hour, min, sec; UInt64 v64 = ft.dwLowDateTime | ((UInt64)ft.dwHighDateTime << 32); Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; unsigned temp; UInt32 v; v64 += (kNumTimeQuantumsInSecond * 2 - 1); v64 /= kNumTimeQuantumsInSecond; sec = (unsigned)(v64 % 60); v64 /= 60; min = (unsigned)(v64 % 60); v64 /= 60; hour = (unsigned)(v64 % 24); v64 /= 24; v = (UInt32)v64; year = (unsigned)(kFileTimeStartYear + v / PERIOD_400 * 400); v %= PERIOD_400; temp = (unsigned)(v / PERIOD_100); if (temp == 4) temp = 3; year += temp * 100; v -= temp * PERIOD_100; temp = v / PERIOD_4; if (temp == 25) temp = 24; year += temp * 4; v -= temp * PERIOD_4; temp = v / 365; if (temp == 4) temp = 3; year += temp; v -= temp * 365; if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ms[1] = 29; for (mon = 1; mon <= 12; mon++) { unsigned s = ms[mon - 1]; if (v < s) break; v -= s; } day = (unsigned)v + 1; dosTime = kLowDosTime; if (year < kDosTimeStartYear) return false; year -= kDosTimeStartYear; dosTime = kHighDosTime; if (year >= 128) return false; dosTime = (year << 25) | (mon << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec >> 1); #endif return true; } void UnixTimeToFileTime(UInt32 unixTime, FILETIME &ft) throw() { UInt64 v = (kUnixTimeOffset + (UInt64)unixTime) * kNumTimeQuantumsInSecond; ft.dwLowDateTime = (DWORD)v; ft.dwHighDateTime = (DWORD)(v >> 32); } bool UnixTime64ToFileTime(Int64 unixTime, FILETIME &ft) throw() { if (unixTime > kNumSecondsInFileTime - kUnixTimeOffset) { ft.dwLowDateTime = ft.dwHighDateTime = (UInt32)(Int32)-1; return false; } Int64 v = (Int64)kUnixTimeOffset + unixTime; if (v < 0) { ft.dwLowDateTime = ft.dwHighDateTime = 0; return false; } UInt64 v2 = (UInt64)v * kNumTimeQuantumsInSecond; ft.dwLowDateTime = (DWORD)v2; ft.dwHighDateTime = (DWORD)(v2 >> 32); return true; } Int64 FileTimeToUnixTime64(const FILETIME &ft) throw() { UInt64 winTime = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime; return (Int64)(winTime / kNumTimeQuantumsInSecond) - kUnixTimeOffset; } bool FileTimeToUnixTime(const FILETIME &ft, UInt32 &unixTime) throw() { UInt64 winTime = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime; winTime /= kNumTimeQuantumsInSecond; if (winTime < kUnixTimeOffset) { unixTime = 0; return false; } winTime -= kUnixTimeOffset; if (winTime > 0xFFFFFFFF) { unixTime = 0xFFFFFFFF; return false; } unixTime = (UInt32)winTime; return true; } bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned min, unsigned sec, UInt64 &resSeconds) throw() { resSeconds = 0; if (year < kFileTimeStartYear || year >= 10000 || month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || min > 59 || sec > 59) return false; UInt32 numYears = year - kFileTimeStartYear; UInt32 numDays = numYears * 365 + numYears / 4 - numYears / 100 + numYears / 400; Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ms[1] = 29; month--; for (unsigned i = 0; i < month; i++) numDays += ms[i]; numDays += day - 1; resSeconds = ((UInt64)(numDays * 24 + hour) * 60 + min) * 60 + sec; return true; } void GetCurUtcFileTime(FILETIME &ft) throw() { // Both variants provide same low resolution on WinXP: about 15 ms. // But GetSystemTimeAsFileTime is much faster. #ifdef UNDER_CE SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); #else GetSystemTimeAsFileTime(&ft); #endif } }} src/libs/7zip/unix/CPP/Windows/TimeUtils.h000066400000000000000000000014411325366651500206320ustar00rootroot00000000000000// Windows/TimeUtils.h #ifndef __WINDOWS_TIME_UTILS_H #define __WINDOWS_TIME_UTILS_H #include "../Common/MyTypes.h" namespace NWindows { namespace NTime { bool DosTimeToFileTime(UInt32 dosTime, FILETIME &fileTime) throw(); bool FileTimeToDosTime(const FILETIME &fileTime, UInt32 &dosTime) throw(); void UnixTimeToFileTime(UInt32 unixTime, FILETIME &fileTime) throw(); bool UnixTime64ToFileTime(Int64 unixTime, FILETIME &fileTime) throw(); bool FileTimeToUnixTime(const FILETIME &fileTime, UInt32 &unixTime) throw(); Int64 FileTimeToUnixTime64(const FILETIME &ft) throw(); bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned min, unsigned sec, UInt64 &resSeconds) throw(); void GetCurUtcFileTime(FILETIME &ft) throw(); }} #endif src/libs/7zip/unix/CPP/Windows/Windows.pri000066400000000000000000000017701325366651500207150ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/Windows/DLL.h \ $$7ZIP_BASE/CPP/Windows/Defs.h \ $$7ZIP_BASE/CPP/Windows/FileDir.h \ $$7ZIP_BASE/CPP/Windows/FileFind.h \ $$7ZIP_BASE/CPP/Windows/FileIO.h \ $$7ZIP_BASE/CPP/Windows/FileName.h \ $$7ZIP_BASE/CPP/Windows/PropVariant.h \ $$7ZIP_BASE/CPP/Windows/PropVariantConv.h \ $$7ZIP_BASE/CPP/Windows/Synchronization.h \ $$7ZIP_BASE/CPP/Windows/Synchronization2.h \ $$7ZIP_BASE/CPP/Windows/System.h \ $$7ZIP_BASE/CPP/Windows/Thread.h \ $$7ZIP_BASE/CPP/Windows/TimeUtils.h SOURCES += $$7ZIP_BASE/CPP/Windows/DLL.cpp \ $$7ZIP_BASE/CPP/Windows/FileDir.cpp \ $$7ZIP_BASE/CPP/Windows/FileFind.cpp \ $$7ZIP_BASE/CPP/Windows/FileIO.cpp \ $$7ZIP_BASE/CPP/Windows/FileName.cpp \ $$7ZIP_BASE/CPP/Windows/PropVariant.cpp \ $$7ZIP_BASE/CPP/Windows/PropVariantConv.cpp \ $$7ZIP_BASE/CPP/Windows/Synchronization.cpp \ $$7ZIP_BASE/CPP/Windows/System.cpp \ $$7ZIP_BASE/CPP/Windows/TimeUtils.cpp src/libs/7zip/unix/CPP/include_windows/000077500000000000000000000000001325366651500203055ustar00rootroot00000000000000src/libs/7zip/unix/CPP/include_windows/basetyps.h000066400000000000000000000005631325366651500223140ustar00rootroot00000000000000#ifndef _BASETYPS_H #define _BASETYPS_H #ifdef ENV_HAVE_GCCVISIBILITYPATCH #define DLLEXPORT __attribute__ ((visibility("default"))) #else #define DLLEXPORT #endif #ifdef __cplusplus #define STDAPI extern "C" DLLEXPORT HRESULT #else #define STDAPI extern DLLEXPORT HRESULT #endif /* __cplusplus */ typedef GUID IID; typedef GUID CLSID; #endif src/libs/7zip/unix/CPP/include_windows/include_windows.pri000066400000000000000000000002271325366651500242170ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/include_windows/basetyps.h \ $$7ZIP_BASE/CPP/include_windows/tchar.h \ $$7ZIP_BASE/CPP/include_windows/windows.h src/libs/7zip/unix/CPP/include_windows/tchar.h000066400000000000000000000044231325366651500215620ustar00rootroot00000000000000/* * tchar.h * * Unicode mapping layer for the standard C library. By including this * file and using the 't' names for string functions * (eg. _tprintf) you can make code which can be easily adapted to both * Unicode and non-unicode environments. In a unicode enabled compile define * _UNICODE before including tchar.h, otherwise the standard non-unicode * library functions will be used. * * Note that you still need to include string.h or stdlib.h etc. to define * the appropriate functions. Also note that there are several defines * included for non-ANSI functions which are commonly available (but using * the convention of prepending an underscore to non-ANSI library function * names). * * This file is part of the Mingw32 package. * * Contributors: * Created by Colin Peters * * THIS SOFTWARE IS NOT COPYRIGHTED * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. * * This code is distributed in the hope that it will be useful but * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY * DISCLAIMED. This includes but is not limited to warranties of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * $Revision: 1.11 $ * $Author: earnie $ * $Date: 2003/05/03 13:48:46 $ * */ #ifndef _TCHAR_H_ #define _TCHAR_H_ /* All the headers include this file. */ #ifndef __int64 #define __int64 long long #endif #ifndef __cdecl #define __cdecl /* */ #endif /* * NOTE: This tests _UNICODE, which is different from the UNICODE define * used to differentiate Win32 API calls. */ #ifdef _UNICODE /* * Use TCHAR instead of char or wchar_t. It will be appropriately translated * if _UNICODE is correctly defined (or not). */ #ifndef _TCHAR_DEFINED typedef wchar_t TCHAR; #define _TCHAR_DEFINED #endif /* * Unicode functions */ /* #define _tfopen _wfopen FILE *_wfopen( const wchar_t *filename, const wchar_t *mode ); */ #else /* Not _UNICODE */ #define _tfopen fopen /* * TCHAR, the type you should use instead of char. */ #ifndef _TCHAR_DEFINED typedef char TCHAR; #define _TCHAR_DEFINED #endif #endif /* Not _UNICODE */ #endif /* Not _TCHAR_H_ */ src/libs/7zip/unix/CPP/include_windows/windows.h000066400000000000000000000124401325366651500221510ustar00rootroot00000000000000/* windows.h - main header file for the Win32 API Written by Anders Norlander This file is part of a free library for the Win32 API. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef _WINDOWS_H #define _WINDOWS_H #include /* BEGIN #include */ #include "Common/MyWindows.h" // FIXED #ifndef CONST #define CONST const #endif #undef MAX_PATH #define MAX_PATH 4096 /* Linux : 4096 - Windows : 260 */ #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define WINAPI #undef BOOL typedef int BOOL; #define CREATE_NEW 1 #define CREATE_ALWAYS 2 #define OPEN_EXISTING 3 #define OPEN_ALWAYS 4 /* #define TRUNCATE_EXISTING 5 */ /* BEGIN #include */ /* BEGIN */ #define NO_ERROR 0L #define ERROR_ALREADY_EXISTS EEXIST #define ERROR_FILE_EXISTS EEXIST #define ERROR_INVALID_HANDLE EBADF #define ERROR_PATH_NOT_FOUND ENOENT #define ERROR_DISK_FULL ENOSPC #define ERROR_NO_MORE_FILES 0x100018 // FIXME #define ERROR_DIRECTORY 267 // FIXME // #define ERROR_NEGATIVE_SEEK 0x100131 // FIXME /* see Common/WyWindows.h #define S_OK ((HRESULT)0x00000000L) #define S_FALSE ((HRESULT)0x00000001L) #define E_INVALIDARG ((HRESULT)0x80070057L) #define E_NOTIMPL ((HRESULT)0x80004001L) #define E_NOINTERFACE ((HRESULT)0x80004002L) #define E_ABORT ((HRESULT)0x80004004L) #define E_FAIL ((HRESULT)0x80004005L) #define E_OUTOFMEMORY ((HRESULT)0x8007000EL) #define STG_E_INVALIDFUNCTION ((HRESULT)0x80030001L) #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define FAILED(Status) ((HRESULT)(Status)<0) */ #ifndef VOID #define VOID void #endif typedef void *PVOID,*LPVOID; typedef WCHAR *LPWSTR; typedef CHAR *LPSTR; typedef TCHAR *LPTSTR; #ifdef UNICODE /* * P7ZIP_TEXT is a private macro whose specific use is to force the expansion of a * macro passed as an argument to the macro TEXT. DO NOT use this * macro within your programs. It's name and function could change without * notice. */ #define P7ZIP_TEXT(q) L##q #else #define P7ZIP_TEXT(q) q #endif /* * UNICODE a constant string when UNICODE is defined, else returns the string * unmodified. * The corresponding macros _TEXT() and _T() for mapping _UNICODE strings * passed to C runtime functions are defined in mingw/tchar.h */ #define TEXT(q) P7ZIP_TEXT(q) typedef BYTE BOOLEAN; /* BEGIN #include */ #ifndef __int64 #define __int64 long long #endif typedef unsigned __int64 UINT64; typedef __int64 INT64; /* END #include */ #define FILE_ATTRIBUTE_READONLY 1 #define FILE_ATTRIBUTE_HIDDEN 2 #define FILE_ATTRIBUTE_SYSTEM 4 #define FILE_ATTRIBUTE_DIRECTORY 16 #define FILE_ATTRIBUTE_ARCHIVE 32 #define FILE_ATTRIBUTE_DEVICE 64 #define FILE_ATTRIBUTE_NORMAL 128 #define FILE_ATTRIBUTE_TEMPORARY 256 #define FILE_ATTRIBUTE_SPARSE_FILE 512 #define FILE_ATTRIBUTE_REPARSE_POINT 1024 #define FILE_ATTRIBUTE_COMPRESSED 2048 #define FILE_ATTRIBUTE_OFFLINE 0x1000 #define FILE_ATTRIBUTE_ENCRYPTED 0x4000 #define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ /* END */ #include #include /* END #include */ /* END #include */ /* BEGIN #include */ #define WAIT_OBJECT_0 0 #define INFINITE 0xFFFFFFFF typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME; #ifdef __cplusplus extern "C" { #endif BOOL WINAPI DosDateTimeToFileTime(WORD,WORD,FILETIME *); BOOL WINAPI FileTimeToDosDateTime(CONST FILETIME *,WORD *, WORD *); BOOL WINAPI FileTimeToLocalFileTime(CONST FILETIME *,FILETIME *); BOOL WINAPI FileTimeToSystemTime(CONST FILETIME *,SYSTEMTIME *); BOOL WINAPI LocalFileTimeToFileTime(CONST FILETIME *,FILETIME *); VOID WINAPI GetSystemTime(SYSTEMTIME *); BOOL WINAPI SystemTimeToFileTime(const SYSTEMTIME*,FILETIME *); VOID WINAPI GetSystemTimeAsFileTime(FILETIME * time); DWORD WINAPI GetTickCount(VOID); #ifdef __cplusplus } #endif /* END #include */ /* BEGIN #include */ #define CP_ACP 0 #define CP_OEMCP 1 #define CP_UTF8 65001 /* #include */ #include "basetyps.h" struct IEnumSTATPROPSTG; typedef struct tagSTATPROPSTG { const OLECHAR * lpwstrName; /* to avoid some warnings : LPOLESTR lpwstrName; */ PROPID propid; VARTYPE vt; } STATPROPSTG; #ifdef __cplusplus extern "C" const IID IID_ISequentialStream; struct ISequentialStream : public IUnknown { STDMETHOD(QueryInterface)(REFIID,PVOID*) PURE; STDMETHOD_(ULONG,AddRef)(void) PURE; STDMETHOD_(ULONG,Release)(void) PURE; STDMETHOD(Read)(void*,ULONG,ULONG*) PURE; STDMETHOD(Write)(void const*,ULONG,ULONG*) PURE; }; #else extern const IID IID_ISequentialStream; #endif /* __cplusplus */ /* END #include */ #endif src/libs/7zip/unix/CPP/myWindows/000077500000000000000000000000001325366651500171105ustar00rootroot00000000000000src/libs/7zip/unix/CPP/myWindows/StdAfx.h000066400000000000000000000110231325366651500204470ustar00rootroot00000000000000// stdafx.h #ifndef __STDAFX_H #define __STDAFX_H #include "config.h" #define MAXIMUM_WAIT_OBJECTS 64 #define NO_INLINE /* FIXME */ #ifdef ENV_HAVE_PTHREAD #include #endif #include "Common/Common.h" #include "Common/MyWindows.h" #include "Common/MyTypes.h" #include "../include_windows/tchar.h" #include "../include_windows/windows.h" #include #include #include #include #include #include #include #include #include #ifdef __NETWARE__ #include #endif #undef CS /* fix for Solaris 10 x86 */ #ifdef __cplusplus # define EXTERN_C extern "C" #else # define EXTERN_C extern #endif /***************************/ #ifndef ENV_HAVE_WCHAR__H EXTERN_C_BEGIN size_t wcslen(const wchar_t *); wchar_t *wcscpy(wchar_t * , const wchar_t * ); wchar_t *wcscat(wchar_t * , const wchar_t * ); EXTERN_C_END #endif /***************************/ #define CLASS_E_CLASSNOTAVAILABLE ((HRESULT)0x80040111L) /************************* LastError *************************/ inline DWORD WINAPI GetLastError(void) { return errno; } inline void WINAPI SetLastError( DWORD err ) { errno = err; } #define AreFileApisANSI() (1) void Sleep(unsigned millisleep); typedef pid_t t_processID; t_processID GetCurrentProcess(void); #define NORMAL_PRIORITY_CLASS (0) #define IDLE_PRIORITY_CLASS (10) void SetPriorityClass(t_processID , int priority); #ifdef __cplusplus class wxWindow; typedef wxWindow *HWND; #define MB_ICONERROR (0x00000200) // wxICON_ERROR #define MB_YESNOCANCEL (0x00000002 | 0x00000008 | 0x00000010) // wxYES | wxNO | wxCANCEL #define MB_ICONQUESTION (0x00000400) // wxICON_QUESTION #define MB_TASKMODAL (0) // FIXME #define MB_SYSTEMMODAL (0) // FIXME #define MB_OK (0x00000004) // wxOK #define MB_ICONSTOP (0x00000200) // wxICON_STOP #define MB_OKCANCEL (0x00000004 | 0x00000010) // wxOK | wxCANCEL #define MessageBox MessageBoxW int MessageBoxW(wxWindow * parent, const TCHAR * mes, const TCHAR * title,int flag); // FIXME #define IDCLOSE (5001) // wxID_CLOSE #define IDEXIT (5006) // wxID_EXIT #define IDOK (5100) // wxID_OK #define IDCANCEL (5101) // wxID_CANCEL #define IDABORT (5115) // wxID_ABORT #define IDYES (5103) // wxID_YES #define IDNO (5104) // wxID_NO #define IDHELP (5009) // wxID_HELP // Show #define SW_HIDE 0 #define SW_SHOW 5 typedef void *HINSTANCE; typedef int INT_PTR; // FIXME 64 bits ? typedef unsigned int UINT_PTR; // FIXME 64 bits ? typedef long LONG_PTR; // FIXME 64 bits ? typedef long DWORD_PTR; // FIXME 64 bits ? typedef UINT_PTR WPARAM; /* WARNING LPARAM shall be 'long' because of CListView::SortItems and wxListCtrl::SortItems : */ typedef LONG_PTR LPARAM; typedef LONG_PTR LRESULT; #define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xFFFF)) #define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16)) #define CALLBACK /* */ #define ERROR_NEGATIVE_SEEK 0x100131 // FIXME #define FACILITY_WIN32 7 // FIXME #define __HRESULT_FROM_WIN32(x) ((HRESULT)(x) > 0 ? ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)) : (HRESULT)(x) ) // FIXME /************ Windows2.h ***********/ typedef void * WNDPROC; typedef void * CREATESTRUCT; typedef struct { HWND hwndFrom; UINT code; #define NM_DBLCLK 1 #define LVN_ITEMCHANGED 2 #define LVN_COLUMNCLICK 3 #define CBEN_BEGINEDIT 10 #define CBEN_ENDEDITW 11 } NMHDR; typedef NMHDR * LPNMHDR; typedef struct tagNMLISTVIEW { NMHDR hdr; INT iItem; INT iSubItem; UINT uNewState; UINT uOldState; // UINT uChanged; // POINT ptAction; LPARAM lParam; } NMLISTVIEW, *LPNMLISTVIEW; typedef void * LPNMITEMACTIVATE; #define NM_RCLICK 1234 /* FIXME */ // FIXME #define WM_CREATE 1 #define WM_COMMAND 2 #define WM_NOTIFY 3 #define WM_DESTROY 4 #define WM_CLOSE 5 #define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16)) #define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xFFFF)) /************ LANG ***********/ typedef WORD LANGID; LANGID GetUserDefaultLangID(void); LANGID GetSystemDefaultLangID(void); #define PRIMARYLANGID(l) ((WORD)(l) & 0x3ff) #define SUBLANGID(l) ((WORD)(l) >> 10) #if defined( __x86_64__ ) #define _WIN64 1 #endif #endif #endif src/libs/7zip/unix/CPP/myWindows/config.h000066400000000000000000000024251325366651500205310ustar00rootroot00000000000000 #if !defined(__DJGPP__) #ifndef __CYGWIN__ #define FILESYSTEM_IS_CASE_SENSITIVE 1 #endif #if !defined(ENV_MACOSX) && !defined(ENV_BEOS) /* */ /* ENV_HAVE_WCHAR__H and not ENV_HAVE_WCHAR_H to avoid warning with wxWidgets */ #define ENV_HAVE_WCHAR__H /* */ #define ENV_HAVE_WCTYPE_H /* mbrtowc */ /* #ifndef __hpux */ /* #define ENV_HAVE_MBRTOWC */ /* #endif */ /* towupper */ #define ENV_HAVE_TOWUPPER #endif /* !ENV_MACOSX && !ENV_BEOS */ #if !defined(ENV_BEOS) && !defined(ANDROID_NDK) #define ENV_HAVE_GETPASS #if !defined(sun) #define ENV_HAVE_TIMEGM #endif #endif /* lstat, readlink and S_ISLNK */ #define ENV_HAVE_LSTAT /* */ #define ENV_HAVE_LOCALE /* mbstowcs */ #define ENV_HAVE_MBSTOWCS /* wcstombs */ #define ENV_HAVE_WCSTOMBS #endif /* !__DJGPP__ */ #ifndef ENV_BEOS #define ENV_HAVE_PTHREAD #endif /* ANDROID don't have wcstombs or mbstowcs ? */ #if defined(ENV_MACOSX) || defined(ANDROID_NDK) #define LOCALE_IS_UTF8 #endif #ifdef LOCALE_IS_UTF8 #undef ENV_HAVE_LOCALE #undef ENV_HAVE_MBSTOWCS #undef ENV_HAVE_WCSTOMBS /* #undef ENV_HAVE_MBRTOWC */ #endif #define MAX_PATHNAME_LEN 1024 src/libs/7zip/unix/CPP/myWindows/initguid.h000066400000000000000000000000631325366651500210740ustar00rootroot00000000000000// initguid.h #include "Common/MyInitGuid.h" src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp000066400000000000000000000040651325366651500235320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "../CPP/Common/MyString.h" #include namespace NCommandLineParser { void SplitCommandLine(const UString &s, UStringVector &parts) { parts.Clear(); const QString cmdLine = QString::fromStdWString(static_cast(s)); const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts); foreach (const QString &arg, args) parts.Add(arg.toStdWString().c_str()); } } // namespace NCommandLineParser src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp000066400000000000000000000147731325366651500223150ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "windows.h" #include #include /* MSDN description about FILETIME structure: The FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). The DeltaToEpochInMsec can be calculated like this: \code quint64 delta = quint64(QDateTime(QDate(1601, 1, 1)).msecsTo(QDateTime(QDate(1970, 1, 1)))) * 10000ULL; \endcode But since the value is static, we use a precalculated number here. */ static const ULONGLONG DeltaToEpochInMsec = 116444736000000000ULL; inline void LocalDateTimeToFileTime(const QDateTime &dt, ULONGLONG utcOffsetInMsec, FILETIME *ft) { const ULONGLONG msec = dt.toMSecsSinceEpoch() + utcOffsetInMsec; const ULONGLONG nano100 = (msec * 10000ULL) + DeltaToEpochInMsec; ft->dwLowDateTime = nano100; ft->dwHighDateTime = nano100 >> 32; } inline void UTCDateTimeToSystemTime(const QDateTime &dt, SYSTEMTIME *st) { const QDate date = dt.date(); const QTime time = dt.time(); st->wYear = date.year(); st->wMonth = date.month(); st->wDayOfWeek = date.dayOfWeek(); st->wDay = date.day(); st->wHour = time.hour(); st->wMinute = time.minute(); st->wSecond = time.second(); st->wMilliseconds = time.msec(); } // -- WINAPI BOOL WINAPI FileTimeToSystemTime(CONST FILETIME *source,SYSTEMTIME *target) { const QDateTime dt = QDateTime(QDate(1601, 1, 1), QTime(0, 0, 0, 1), Qt::UTC).addMSecs ((ULONGLONG(source->dwLowDateTime) + (ULONGLONG(source->dwHighDateTime) << 32)) / 10000ULL); UTCDateTimeToSystemTime(dt.toUTC(), target); return TRUE; } BOOL WINAPI FileTimeToLocalFileTime(CONST FILETIME *source,FILETIME *target) { const QDateTime dt = QDateTime(QDate(1601, 1, 1), QTime(0, 0, 0, 1), Qt::UTC).addMSecs ((ULONGLONG(source->dwLowDateTime) + (ULONGLONG(source->dwHighDateTime) << 32)) / 10000ULL); LocalDateTimeToFileTime(dt.toLocalTime(), ULONGLONG(QDateTime::currentDateTime() .offsetFromUtc()) * 1000ULL, target); return TRUE; } BOOLEAN WINAPI RtlTimeToSecondsSince1970(const LARGE_INTEGER *Time, DWORD *Seconds) { /* MSDN suggests to implement the function like this: 1. Call SystemTimeToFileTime to copy the system time to a FILETIME structure. Call GetSystemTime to get the current system time to pass to SystemTimeToFileTime. 2. Copy the contents of the FILETIME structure to a ULARGE_INTEGER structure. 3. Initialize a SYSTEMTIME structure with the date and time of the first second of January 1, 1970. 4. Call SystemTimeToFileTime, passing the SYSTEMTIME structure initialized in Step 3 to the call. 5. Copy the contents of the FILETIME structure returned by SystemTimeToFileTime in Step 4 to a second ULARGE_INTEGER. The copied value should be less than or equal to the value copied in Step 2. 6. Subtract the 64-bit value in the ULARGE_INTEGER structure initialized in Step 5 (January 1, 1970) from the 64-bit value of the ULARGE_INTEGER structure initialized in Step 2 (the current system time). This produces a value in 100-nanosecond intervals since January 1, 1970. To convert this value to seconds, divide by 10,000,000. We can omit step 1 and 2, cause we get the LARGE_INTEGER passed as function argument. */ SYSTEMTIME stFirstSecondOf1979; stFirstSecondOf1979.wSecond = 1; stFirstSecondOf1979.wMinute = 0; stFirstSecondOf1979.wHour = 0; stFirstSecondOf1979.wDay = 1; stFirstSecondOf1979.wMonth = 1; stFirstSecondOf1979.wYear = 1970; stFirstSecondOf1979.wDayOfWeek = 4; FILETIME ftFirstSecondOf1979; SystemTimeToFileTime(&stFirstSecondOf1979, &ftFirstSecondOf1979); LARGE_INTEGER liFirstSecondOf1979; liFirstSecondOf1979.LowPart = ftFirstSecondOf1979.dwLowDateTime; liFirstSecondOf1979.HighPart = ftFirstSecondOf1979.dwHighDateTime; const ULONGLONG diffNano100 = Time->QuadPart - liFirstSecondOf1979.QuadPart; *Seconds = diffNano100 / 10000000ULL; return TRUE; } void WINAPI RtlSecondsSince1970ToFileTime(DWORD Seconds, FILETIME *ft) { const ULONGLONG nano100 = (ULONGLONG(Seconds) * 10000000ULL) + DeltaToEpochInMsec; ft->dwLowDateTime = nano100; ft->dwHighDateTime = nano100 >> 32; } VOID WINAPI GetSystemTime(SYSTEMTIME *st) { UTCDateTimeToSystemTime(QDateTime::currentDateTimeUtc(), st); } VOID WINAPI GetSystemTimeAsFileTime(FILETIME *time) { SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, time); } DWORD WINAPI GetTickCount() { using namespace std::chrono; return duration_cast(steady_clock::now().time_since_epoch()).count(); } BOOL WINAPI SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, FILETIME *lpFileTime) { const QDateTime dt(QDate(lpSystemTime->wYear, lpSystemTime->wMonth, lpSystemTime->wDay), QTime(lpSystemTime->wHour, lpSystemTime->wMinute, lpSystemTime->wSecond, lpSystemTime->wMilliseconds), Qt::UTC); LocalDateTimeToFileTime(dt.toLocalTime(), 0ULL, lpFileTime); return TRUE; } src/libs/7zip/unix/CPP/myWindows/myPrivate.h000066400000000000000000000006761325366651500212520ustar00rootroot00000000000000 void WINAPI RtlSecondsSince1970ToFileTime( DWORD Seconds, FILETIME * ft ); extern "C" int global_use_utf16_conversion; #ifdef ENV_HAVE_LSTAT extern "C" int global_use_lstat; #endif const char *my_getlocale(void); #ifdef NEED_NAME_WINDOWS_TO_UNIX static inline const char * nameWindowToUnix(const char * lpFileName) { if ((lpFileName[0] == 'c') && (lpFileName[1] == ':')) return lpFileName+2; return lpFileName; } #endif src/libs/7zip/unix/CPP/myWindows/myWindows.pri000066400000000000000000000004451325366651500216270ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/myWindows/StdAfx.h \ $$7ZIP_BASE/CPP/myWindows/config.h \ $$7ZIP_BASE/CPP/myWindows/initguid.h \ $$7ZIP_BASE/CPP/myWindows/myPrivate.h SOURCES += $$7ZIP_BASE/CPP/myWindows/myDateAndTime.cpp \ $$7ZIP_BASE/CPP/myWindows/myCommandLineParser.cpp src/libs/7zip/unix/unix.pri000066400000000000000000000007411325366651500161670ustar00rootroot00000000000000include(C/C.pri) include(CPP/7zip/7zip.pri) include(CPP/7zip/Archive/7z/7z.pri) include(CPP/7zip/Archive/Archive.pri) include(CPP/7zip/Archive/Common/Common.pri) include(CPP/7zip/Common/Common.pri) include(CPP/7zip/Compress/Compress.pri) include(CPP/7zip/UI/Common/Common.pri) include(CPP/7zip/UI/Console/Console.pri) include(CPP/Common/Common.pri) include(CPP/include_windows/include_windows.pri) include(CPP/myWindows/myWindows.pri) include(CPP/Windows/Windows.pri) src/libs/7zip/win/000077500000000000000000000000001325366651500143005ustar00rootroot00000000000000src/libs/7zip/win/C/000077500000000000000000000000001325366651500144625ustar00rootroot00000000000000src/libs/7zip/win/C/7zCrc.c000066400000000000000000000040731325366651500156220ustar00rootroot00000000000000/* 7zCrc.c -- CRC32 init 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "7zCrc.h" #include "CpuArch.h" #define kCrcPoly 0xEDB88320 #ifdef MY_CPU_X86_OR_AMD64 #define CRC_NUM_TABLES 8 UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); #elif defined(MY_CPU_LE) #define CRC_NUM_TABLES 4 #else #define CRC_NUM_TABLES 5 #define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table); #endif #ifndef MY_CPU_BE UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); #endif typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); CRC_FUNC g_CrcUpdate; UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) { return g_CrcUpdate(v, data, size, g_CrcTable); } UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) { return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL; } void MY_FAST_CALL CrcGenerateTable() { UInt32 i; for (i = 0; i < 256; i++) { UInt32 r = i; unsigned j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); g_CrcTable[i] = r; } for (; i < 256 * CRC_NUM_TABLES; i++) { UInt32 r = g_CrcTable[i - 256]; g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); } #ifdef MY_CPU_LE g_CrcUpdate = CrcUpdateT4; #if CRC_NUM_TABLES == 8 if (!CPU_Is_InOrder()) g_CrcUpdate = CrcUpdateT8; #endif #else { #ifndef MY_CPU_BE UInt32 k = 1; if (*(const Byte *)&k == 1) g_CrcUpdate = CrcUpdateT4; else #endif { for (i = 256 * CRC_NUM_TABLES - 1; i >= 256; i--) { UInt32 x = g_CrcTable[i - 256]; g_CrcTable[i] = CRC_UINT32_SWAP(x); } g_CrcUpdate = CrcUpdateT1_BeT4; } } #endif } src/libs/7zip/win/C/7zCrc.h000066400000000000000000000011751325366651500156270ustar00rootroot00000000000000/* 7zCrc.h -- CRC32 calculation 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __7Z_CRC_H #define __7Z_CRC_H #include "7zTypes.h" EXTERN_C_BEGIN extern UInt32 g_CrcTable[]; /* Call CrcGenerateTable one time before other CRC functions */ void MY_FAST_CALL CrcGenerateTable(void); #define CRC_INIT_VAL 0xFFFFFFFF #define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL) #define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size); UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); EXTERN_C_END #endif src/libs/7zip/win/C/7zCrcOpt.c000066400000000000000000000033441325366651500163050ustar00rootroot00000000000000/* 7zCrcOpt.c -- CRC32 calculation 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "CpuArch.h" #define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) #ifndef MY_CPU_BE UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); for (; size >= 4; size -= 4, p += 4) { v ^= *(const UInt32 *)p; v = table[0x300 + (v & 0xFF)] ^ table[0x200 + ((v >> 8) & 0xFF)] ^ table[0x100 + ((v >> 16) & 0xFF)] ^ table[0x000 + ((v >> 24))]; } for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) { return CrcUpdateT4(v, data, size, table); } #endif #ifndef MY_CPU_LE #define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); v = CRC_UINT32_SWAP(v); table += 0x100; for (; size >= 4; size -= 4, p += 4) { v ^= *(const UInt32 *)p; v = table[0x000 + (v & 0xFF)] ^ table[0x100 + ((v >> 8) & 0xFF)] ^ table[0x200 + ((v >> 16) & 0xFF)] ^ table[0x300 + ((v >> 24))]; } table -= 0x100; v = CRC_UINT32_SWAP(v); for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } #endif src/libs/7zip/win/C/7zStream.c000066400000000000000000000101341325366651500163410ustar00rootroot00000000000000/* 7zStream.c -- 7z Stream functions 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "7zTypes.h" SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) { while (size != 0) { size_t processed = size; RINOK(stream->Read(stream, buf, &processed)); if (processed == 0) return errorType; buf = (void *)((Byte *)buf + processed); size -= processed; } return SZ_OK; } SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size) { return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); } SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf) { size_t processed = 1; RINOK(stream->Read(stream, buf, &processed)); return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF; } SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) { Int64 t = offset; return stream->Seek(stream, &t, SZ_SEEK_SET); } SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size) { const void *lookBuf; if (*size == 0) return SZ_OK; RINOK(stream->Look(stream, &lookBuf, size)); memcpy(buf, lookBuf, *size); return stream->Skip(stream, *size); } SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) { while (size != 0) { size_t processed = size; RINOK(stream->Read(stream, buf, &processed)); if (processed == 0) return errorType; buf = (void *)((Byte *)buf + processed); size -= processed; } return SZ_OK; } SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) { return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); } static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size) { SRes res = SZ_OK; CLookToRead *p = (CLookToRead *)pp; size_t size2 = p->size - p->pos; if (size2 == 0 && *size > 0) { p->pos = 0; size2 = LookToRead_BUF_SIZE; res = p->realStream->Read(p->realStream, p->buf, &size2); p->size = size2; } if (size2 < *size) *size = size2; *buf = p->buf + p->pos; return res; } static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size) { SRes res = SZ_OK; CLookToRead *p = (CLookToRead *)pp; size_t size2 = p->size - p->pos; if (size2 == 0 && *size > 0) { p->pos = 0; if (*size > LookToRead_BUF_SIZE) *size = LookToRead_BUF_SIZE; res = p->realStream->Read(p->realStream, p->buf, size); size2 = p->size = *size; } if (size2 < *size) *size = size2; *buf = p->buf + p->pos; return res; } static SRes LookToRead_Skip(void *pp, size_t offset) { CLookToRead *p = (CLookToRead *)pp; p->pos += offset; return SZ_OK; } static SRes LookToRead_Read(void *pp, void *buf, size_t *size) { CLookToRead *p = (CLookToRead *)pp; size_t rem = p->size - p->pos; if (rem == 0) return p->realStream->Read(p->realStream, buf, size); if (rem > *size) rem = *size; memcpy(buf, p->buf + p->pos, rem); p->pos += rem; *size = rem; return SZ_OK; } static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) { CLookToRead *p = (CLookToRead *)pp; p->pos = p->size = 0; return p->realStream->Seek(p->realStream, pos, origin); } void LookToRead_CreateVTable(CLookToRead *p, int lookahead) { p->s.Look = lookahead ? LookToRead_Look_Lookahead : LookToRead_Look_Exact; p->s.Skip = LookToRead_Skip; p->s.Read = LookToRead_Read; p->s.Seek = LookToRead_Seek; } void LookToRead_Init(CLookToRead *p) { p->pos = p->size = 0; } static SRes SecToLook_Read(void *pp, void *buf, size_t *size) { CSecToLook *p = (CSecToLook *)pp; return LookInStream_LookRead(p->realStream, buf, size); } void SecToLook_CreateVTable(CSecToLook *p) { p->s.Read = SecToLook_Read; } static SRes SecToRead_Read(void *pp, void *buf, size_t *size) { CSecToRead *p = (CSecToRead *)pp; return p->realStream->Read(p->realStream, buf, size); } void SecToRead_CreateVTable(CSecToRead *p) { p->s.Read = SecToRead_Read; } src/libs/7zip/win/C/7zTypes.h000066400000000000000000000131511325366651500162210ustar00rootroot00000000000000/* 7zTypes.h -- Basic types 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H #ifdef _WIN32 /* #include */ #endif #include #ifndef EXTERN_C_BEGIN #ifdef __cplusplus #define EXTERN_C_BEGIN extern "C" { #define EXTERN_C_END } #else #define EXTERN_C_BEGIN #define EXTERN_C_END #endif #endif EXTERN_C_BEGIN #define SZ_OK 0 #define SZ_ERROR_DATA 1 #define SZ_ERROR_MEM 2 #define SZ_ERROR_CRC 3 #define SZ_ERROR_UNSUPPORTED 4 #define SZ_ERROR_PARAM 5 #define SZ_ERROR_INPUT_EOF 6 #define SZ_ERROR_OUTPUT_EOF 7 #define SZ_ERROR_READ 8 #define SZ_ERROR_WRITE 9 #define SZ_ERROR_PROGRESS 10 #define SZ_ERROR_FAIL 11 #define SZ_ERROR_THREAD 12 #define SZ_ERROR_ARCHIVE 16 #define SZ_ERROR_NO_ARCHIVE 17 typedef int SRes; #ifdef _WIN32 /* typedef DWORD WRes; */ typedef unsigned WRes; #else typedef int WRes; #endif #ifndef RINOK #define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } #endif typedef unsigned char Byte; typedef short Int16; typedef unsigned short UInt16; #ifdef _LZMA_UINT32_IS_ULONG typedef long Int32; typedef unsigned long UInt32; #else typedef int Int32; typedef unsigned int UInt32; #endif #ifdef _SZ_NO_INT_64 /* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. NOTES: Some code will work incorrectly in that case! */ typedef long Int64; typedef unsigned long UInt64; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 Int64; typedef unsigned __int64 UInt64; #define UINT64_CONST(n) n #else typedef long long int Int64; typedef unsigned long long int UInt64; #define UINT64_CONST(n) n ## ULL #endif #endif #ifdef _LZMA_NO_SYSTEM_SIZE_T typedef UInt32 SizeT; #else typedef size_t SizeT; #endif typedef int Bool; #define True 1 #define False 0 #ifdef _WIN32 #define MY_STD_CALL __stdcall #else #define MY_STD_CALL #endif #ifdef _MSC_VER #if _MSC_VER >= 1300 #define MY_NO_INLINE __declspec(noinline) #else #define MY_NO_INLINE #endif #define MY_CDECL __cdecl #define MY_FAST_CALL __fastcall #else #define MY_NO_INLINE #define MY_CDECL #define MY_FAST_CALL #endif /* The following interfaces use first parameter as pointer to structure */ typedef struct { Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ } IByteIn; typedef struct { void (*Write)(void *p, Byte b); } IByteOut; typedef struct { SRes (*Read)(void *p, void *buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) < input(*size)) is allowed */ } ISeqInStream; /* it can return SZ_ERROR_INPUT_EOF */ SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); typedef struct { size_t (*Write)(void *p, const void *buf, size_t size); /* Returns: result - the number of actually written bytes. (result < size) means error */ } ISeqOutStream; typedef enum { SZ_SEEK_SET = 0, SZ_SEEK_CUR = 1, SZ_SEEK_END = 2 } ESzSeek; typedef struct { SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); } ISeekInStream; typedef struct { SRes (*Look)(void *p, const void **buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) > input(*size)) is not allowed (output(*size) < input(*size)) is allowed */ SRes (*Skip)(void *p, size_t offset); /* offset must be <= output(*size) of Look */ SRes (*Read)(void *p, void *buf, size_t *size); /* reads directly (without buffer). It's same as ISeqInStream::Read */ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); } ILookInStream; SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); /* reads via ILookInStream::Read */ SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); #define LookToRead_BUF_SIZE (1 << 14) typedef struct { ILookInStream s; ISeekInStream *realStream; size_t pos; size_t size; Byte buf[LookToRead_BUF_SIZE]; } CLookToRead; void LookToRead_CreateVTable(CLookToRead *p, int lookahead); void LookToRead_Init(CLookToRead *p); typedef struct { ISeqInStream s; ILookInStream *realStream; } CSecToLook; void SecToLook_CreateVTable(CSecToLook *p); typedef struct { ISeqInStream s; ILookInStream *realStream; } CSecToRead; void SecToRead_CreateVTable(CSecToRead *p); typedef struct { SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); /* Returns: result. (result != SZ_OK) means break. Value (UInt64)(Int64)-1 for size means unknown value. */ } ICompressProgress; typedef struct { void *(*Alloc)(void *p, size_t size); void (*Free)(void *p, void *address); /* address can be 0 */ } ISzAlloc; #define IAlloc_Alloc(p, size) (p)->Alloc((p), size) #define IAlloc_Free(p, a) (p)->Free((p), a) #ifdef _WIN32 #define CHAR_PATH_SEPARATOR '\\' #define WCHAR_PATH_SEPARATOR L'\\' #define STRING_PATH_SEPARATOR "\\" #define WSTRING_PATH_SEPARATOR L"\\" #else #define CHAR_PATH_SEPARATOR '/' #define WCHAR_PATH_SEPARATOR L'/' #define STRING_PATH_SEPARATOR "/" #define WSTRING_PATH_SEPARATOR L"/" #endif EXTERN_C_END #endif src/libs/7zip/win/C/7zVersion.h000066400000000000000000000005451325366651500165450ustar00rootroot00000000000000#define MY_VER_MAJOR 9 #define MY_VER_MINOR 38 #define MY_VER_BUILD 00 #define MY_VERSION "9.38 beta" // #define MY_7ZIP_VERSION "9.38" #define MY_DATE "2015-01-03" #undef MY_COPYRIGHT #undef MY_VERSION_COPYRIGHT_DATE #define MY_COPYRIGHT ": Igor Pavlov : Public domain" #define MY_VERSION_COPYRIGHT_DATE MY_VERSION " " MY_COPYRIGHT " : " MY_DATE src/libs/7zip/win/C/Alloc.c000066400000000000000000000054211325366651500156620ustar00rootroot00000000000000/* Alloc.c -- Memory allocation functions 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #ifdef _WIN32 #include #endif #include #include "Alloc.h" /* #define _SZ_ALLOC_DEBUG */ /* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ #ifdef _SZ_ALLOC_DEBUG #include int g_allocCount = 0; int g_allocCountMid = 0; int g_allocCountBig = 0; #endif void *MyAlloc(size_t size) { if (size == 0) return 0; #ifdef _SZ_ALLOC_DEBUG { void *p = malloc(size); fprintf(stderr, "\nAlloc %10d bytes, count = %10d, addr = %8X", size, g_allocCount++, (unsigned)p); return p; } #else return malloc(size); #endif } void MyFree(void *address) { #ifdef _SZ_ALLOC_DEBUG if (address != 0) fprintf(stderr, "\nFree; count = %10d, addr = %8X", --g_allocCount, (unsigned)address); #endif free(address); } #ifdef _WIN32 void *MidAlloc(size_t size) { if (size == 0) return 0; #ifdef _SZ_ALLOC_DEBUG fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); #endif return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); } void MidFree(void *address) { #ifdef _SZ_ALLOC_DEBUG if (address != 0) fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); #endif if (address == 0) return; VirtualFree(address, 0, MEM_RELEASE); } #ifndef MEM_LARGE_PAGES #undef _7ZIP_LARGE_PAGES #endif #ifdef _7ZIP_LARGE_PAGES SIZE_T g_LargePageSize = 0; typedef SIZE_T (WINAPI *GetLargePageMinimumP)(); #endif void SetLargePageSize() { #ifdef _7ZIP_LARGE_PAGES SIZE_T size = 0; GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); if (largePageMinimum == 0) return; size = largePageMinimum(); if (size == 0 || (size & (size - 1)) != 0) return; g_LargePageSize = size; #endif } void *BigAlloc(size_t size) { if (size == 0) return 0; #ifdef _SZ_ALLOC_DEBUG fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); #endif #ifdef _7ZIP_LARGE_PAGES if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) { void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); if (res != 0) return res; } #endif return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); } void BigFree(void *address) { #ifdef _SZ_ALLOC_DEBUG if (address != 0) fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); #endif if (address == 0) return; VirtualFree(address, 0, MEM_RELEASE); } #endif src/libs/7zip/win/C/Alloc.h000066400000000000000000000012241325366651500156640ustar00rootroot00000000000000/* Alloc.h -- Memory allocation functions 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __COMMON_ALLOC_H #define __COMMON_ALLOC_H #include #ifdef __cplusplus extern "C" { #endif void *MyAlloc(size_t size); void MyFree(void *address); #ifdef _WIN32 void SetLargePageSize(); void *MidAlloc(size_t size); void MidFree(void *address); void *BigAlloc(size_t size); void BigFree(void *address); #else #define MidAlloc(size) MyAlloc(size) #define MidFree(address) MyFree(address) #define BigAlloc(size) MyAlloc(size) #define BigFree(address) MyFree(address) #endif #ifdef __cplusplus } #endif #endif src/libs/7zip/win/C/Bra.c000066400000000000000000000063071325366651500153400ustar00rootroot00000000000000/* Bra.c -- Converters for RISC code 2010-04-16 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Bra.h" SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 4) return 0; size -= 4; ip += 8; for (i = 0; i <= size; i += 4) { if (data[i + 3] == 0xEB) { UInt32 dest; UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]); src <<= 2; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); dest >>= 2; data[i + 2] = (Byte)(dest >> 16); data[i + 1] = (Byte)(dest >> 8); data[i + 0] = (Byte)dest; } } return i; } SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 4) return 0; size -= 4; ip += 4; for (i = 0; i <= size; i += 2) { if ((data[i + 1] & 0xF8) == 0xF0 && (data[i + 3] & 0xF8) == 0xF8) { UInt32 dest; UInt32 src = (((UInt32)data[i + 1] & 0x7) << 19) | ((UInt32)data[i + 0] << 11) | (((UInt32)data[i + 3] & 0x7) << 8) | (data[i + 2]); src <<= 1; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); dest >>= 1; data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); data[i + 0] = (Byte)(dest >> 11); data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); data[i + 2] = (Byte)dest; i += 2; } } return i; } SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 4) return 0; size -= 4; for (i = 0; i <= size; i += 4) { if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1) { UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) | ((UInt32)data[i + 1] << 16) | ((UInt32)data[i + 2] << 8) | ((UInt32)data[i + 3] & (~3)); UInt32 dest; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); data[i + 1] = (Byte)(dest >> 16); data[i + 2] = (Byte)(dest >> 8); data[i + 3] &= 0x3; data[i + 3] |= dest; } } return i; } SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { UInt32 i; if (size < 4) return 0; size -= 4; for (i = 0; i <= size; i += 4) { if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) || (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)) { UInt32 src = ((UInt32)data[i + 0] << 24) | ((UInt32)data[i + 1] << 16) | ((UInt32)data[i + 2] << 8) | ((UInt32)data[i + 3]); UInt32 dest; src <<= 2; if (encoding) dest = ip + i + src; else dest = src - (ip + i); dest >>= 2; dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; data[i + 0] = (Byte)(dest >> 24); data[i + 1] = (Byte)(dest >> 16); data[i + 2] = (Byte)(dest >> 8); data[i + 3] = (Byte)dest; } } return i; } src/libs/7zip/win/C/Bra.h000066400000000000000000000035761325366651500153520ustar00rootroot00000000000000/* Bra.h -- Branch converters for executables 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __BRA_H #define __BRA_H #include "7zTypes.h" EXTERN_C_BEGIN /* These functions convert relative addresses to absolute addresses in CALL instructions to increase the compression ratio. In: data - data buffer size - size of data ip - current virtual Instruction Pinter (IP) value state - state variable for x86 converter encoding - 0 (for decoding), 1 (for encoding) Out: state - state variable for x86 converter Returns: The number of processed bytes. If you call these functions with multiple calls, you must start next call with first byte after block of processed bytes. Type Endian Alignment LookAhead x86 little 1 4 ARMT little 2 2 ARM little 4 0 PPC big 4 0 SPARC big 4 0 IA64 little 16 0 size must be >= Alignment + LookAhead, if it's not last block. If (size < Alignment + LookAhead), converter returns 0. Example: UInt32 ip = 0; for () { ; size must be >= Alignment + LookAhead, if it's not last block SizeT processed = Convert(data, size, ip, 1); data += processed; size -= processed; ip += processed; } */ #define x86_Convert_Init(state) { state = 0; } SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); EXTERN_C_END #endif src/libs/7zip/win/C/Bra86.c000066400000000000000000000034071325366651500155140ustar00rootroot00000000000000/* Bra86.c -- Converter for x86 code (BCJ) 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Bra.h" #define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0) SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) { SizeT pos = 0; UInt32 mask = *state & 7; if (size < 5) return 0; size -= 4; ip += 5; for (;;) { Byte *p = data + pos; const Byte *limit = data + size; for (; p < limit; p++) if ((*p & 0xFE) == 0xE8) break; { SizeT d = (SizeT)(p - data - pos); pos = (SizeT)(p - data); if (p >= limit) { *state = (d > 2 ? 0 : mask >> (unsigned)d); return pos; } if (d > 2) mask = 0; else { mask >>= (unsigned)d; if (mask != 0 && (mask > 4 || mask == 3 || Test86MSByte(p[(mask >> 1) + 1]))) { mask = (mask >> 1) | 4; pos++; continue; } } } if (Test86MSByte(p[4])) { UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); UInt32 cur = ip + (UInt32)pos; pos += 5; if (encoding) v += cur; else v -= cur; if (mask != 0) { unsigned sh = (mask & 6) << 2; if (Test86MSByte((Byte)(v >> sh))) { v ^= (((UInt32)0x100 << sh) - 1); if (encoding) v += cur; else v -= cur; } mask = 0; } p[1] = (Byte)v; p[2] = (Byte)(v >> 8); p[3] = (Byte)(v >> 16); p[4] = (Byte)(0 - ((v >> 24) & 1)); } else { mask = (mask >> 1) | 4; pos++; } } } src/libs/7zip/win/C/BraIA64.c000066400000000000000000000034071325366651500157220ustar00rootroot00000000000000/* BraIA64.c -- Converter for IA-64 code 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Bra.h" static const Byte kBranchTable[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0 }; SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) { SizeT i; if (size < 16) return 0; size -= 16; for (i = 0; i <= size; i += 16) { UInt32 instrTemplate = data[i] & 0x1F; UInt32 mask = kBranchTable[instrTemplate]; UInt32 bitPos = 5; int slot; for (slot = 0; slot < 3; slot++, bitPos += 41) { UInt32 bytePos, bitRes; UInt64 instruction, instNorm; int j; if (((mask >> slot) & 1) == 0) continue; bytePos = (bitPos >> 3); bitRes = bitPos & 0x7; instruction = 0; for (j = 0; j < 6; j++) instruction += (UInt64)data[i + j + bytePos] << (8 * j); instNorm = instruction >> bitRes; if (((instNorm >> 37) & 0xF) == 0x5 && ((instNorm >> 9) & 0x7) == 0) { UInt32 src = (UInt32)((instNorm >> 13) & 0xFFFFF); UInt32 dest; src |= ((UInt32)(instNorm >> 36) & 1) << 20; src <<= 4; if (encoding) dest = ip + (UInt32)i + src; else dest = src - (ip + (UInt32)i); dest >>= 4; instNorm &= ~((UInt64)(0x8FFFFF) << 13); instNorm |= ((UInt64)(dest & 0xFFFFF) << 13); instNorm |= ((UInt64)(dest & 0x100000) << (36 - 20)); instruction &= (1 << bitRes) - 1; instruction |= (instNorm << bitRes); for (j = 0; j < 6; j++) data[i + j + bytePos] = (Byte)(instruction >> (8 * j)); } } } return i; } src/libs/7zip/win/C/C.pri000066400000000000000000000026401325366651500153620ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/C/7zCrc.h \ $$7ZIP_BASE/C/7zTypes.h \ $$7ZIP_BASE/C/7zVersion.h \ $$7ZIP_BASE/C/Alloc.h \ $$7ZIP_BASE/C/Bra.h \ $$7ZIP_BASE/C/Compiler.h \ $$7ZIP_BASE/C/CpuArch.h \ $$7ZIP_BASE/C/Delta.h \ $$7ZIP_BASE/C/LzFind.h \ $$7ZIP_BASE/C/LzFindMt.h \ $$7ZIP_BASE/C/LzHash.h \ $$7ZIP_BASE/C/Lzma2Dec.h \ $$7ZIP_BASE/C/Lzma2Enc.h \ $$7ZIP_BASE/C/LzmaDec.h \ $$7ZIP_BASE/C/LzmaEnc.h \ $$7ZIP_BASE/C/MtCoder.h \ $$7ZIP_BASE/C/Precomp.h \ $$7ZIP_BASE/C/RotateDefs.h \ $$7ZIP_BASE/C/Sha256.h \ $$7ZIP_BASE/C/Threads.h \ $$7ZIP_BASE/C/Xz.h \ $$7ZIP_BASE/C/XzCrc64.h \ $$7ZIP_BASE/C/XzEnc.h SOURCES += $$7ZIP_BASE/C/7zCrc.c \ $$7ZIP_BASE/C/7zCrcOpt.c \ $$7ZIP_BASE/C/7zStream.c \ $$7ZIP_BASE/C/Alloc.c \ $$7ZIP_BASE/C/Bra.c \ $$7ZIP_BASE/C/Bra86.c \ $$7ZIP_BASE/C/BraIA64.c \ $$7ZIP_BASE/C/CpuArch.c \ $$7ZIP_BASE/C/Delta.c \ $$7ZIP_BASE/C/LzFind.c \ $$7ZIP_BASE/C/LzFindMt.c \ $$7ZIP_BASE/C/Lzma2Dec.c \ $$7ZIP_BASE/C/Lzma2Enc.c \ $$7ZIP_BASE/C/LzmaDec.c \ $$7ZIP_BASE/C/LzmaEnc.c \ $$7ZIP_BASE/C/MtCoder.c \ $$7ZIP_BASE/C/Sha256.c \ $$7ZIP_BASE/C/Threads.c \ $$7ZIP_BASE/C/Xz.c \ $$7ZIP_BASE/C/XzCrc64.c \ $$7ZIP_BASE/C/XzCrc64Opt.c \ $$7ZIP_BASE/C/XzDec.c \ $$7ZIP_BASE/C/XzEnc.c \ $$7ZIP_BASE/C/XzIn.c src/libs/7zip/win/C/Compiler.h000066400000000000000000000020071325366651500164040ustar00rootroot00000000000000/* Compiler.h -- Compiler ypes 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_COMPILER_H #define __7Z_COMPILER_H #ifdef _MSC_VER #ifdef UNDER_CE #define RPC_NO_WINDOWS_H /* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */ #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int #endif #if _MSC_VER >= 1300 #pragma warning(disable : 4996) // This function or variable may be unsafe #else #pragma warning(disable : 4511) // copy constructor could not be generated #pragma warning(disable : 4512) // assignment operator could not be generated #pragma warning(disable : 4702) // unreachable code #pragma warning(disable : 4710) // not inlined #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information #endif #endif #endif src/libs/7zip/win/C/CpuArch.c000066400000000000000000000100651325366651500161550ustar00rootroot00000000000000/* CpuArch.c -- CPU specific code 2012-05-29: Igor Pavlov : Public domain */ #include "Precomp.h" #include "CpuArch.h" #ifdef MY_CPU_X86_OR_AMD64 #if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) #define USE_ASM #endif #if !defined(USE_ASM) && _MSC_VER >= 1500 #include #endif #if defined(USE_ASM) && !defined(MY_CPU_AMD64) static UInt32 CheckFlag(UInt32 flag) { #ifdef _MSC_VER __asm pushfd; __asm pop EAX; __asm mov EDX, EAX; __asm xor EAX, flag; __asm push EAX; __asm popfd; __asm pushfd; __asm pop EAX; __asm xor EAX, EDX; __asm push EDX; __asm popfd; __asm and flag, EAX; #else __asm__ __volatile__ ( "pushf\n\t" "pop %%EAX\n\t" "movl %%EAX,%%EDX\n\t" "xorl %0,%%EAX\n\t" "push %%EAX\n\t" "popf\n\t" "pushf\n\t" "pop %%EAX\n\t" "xorl %%EDX,%%EAX\n\t" "push %%EDX\n\t" "popf\n\t" "andl %%EAX, %0\n\t": "=c" (flag) : "c" (flag)); #endif return flag; } #define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False; #else #define CHECK_CPUID_IS_SUPPORTED #endif static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) { #ifdef USE_ASM #ifdef _MSC_VER UInt32 a2, b2, c2, d2; __asm xor EBX, EBX; __asm xor ECX, ECX; __asm xor EDX, EDX; __asm mov EAX, function; __asm cpuid; __asm mov a2, EAX; __asm mov b2, EBX; __asm mov c2, ECX; __asm mov d2, EDX; *a = a2; *b = b2; *c = c2; *d = d2; #else __asm__ __volatile__ ( #if defined(MY_CPU_X86) && defined(__PIC__) "mov %%ebx, %%edi;" "cpuid;" "xchgl %%ebx, %%edi;" : "=a" (*a) , "=D" (*b) , #else "cpuid" : "=a" (*a) , "=b" (*b) , #endif "=c" (*c) , "=d" (*d) : "0" (function)) ; #endif #else int CPUInfo[4]; __cpuid(CPUInfo, function); *a = CPUInfo[0]; *b = CPUInfo[1]; *c = CPUInfo[2]; *d = CPUInfo[3]; #endif } Bool x86cpuid_CheckAndRead(Cx86cpuid *p) { CHECK_CPUID_IS_SUPPORTED MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]); MyCPUID(1, &p->ver, &p->b, &p->c, &p->d); return True; } static UInt32 kVendors[][3] = { { 0x756E6547, 0x49656E69, 0x6C65746E}, { 0x68747541, 0x69746E65, 0x444D4163}, { 0x746E6543, 0x48727561, 0x736C7561} }; int x86cpuid_GetFirm(const Cx86cpuid *p) { unsigned i; for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++) { const UInt32 *v = kVendors[i]; if (v[0] == p->vendor[0] && v[1] == p->vendor[1] && v[2] == p->vendor[2]) return (int)i; } return -1; } Bool CPU_Is_InOrder() { Cx86cpuid p; int firm; UInt32 family, model; if (!x86cpuid_CheckAndRead(&p)) return True; family = x86cpuid_GetFamily(&p); model = x86cpuid_GetModel(&p); firm = x86cpuid_GetFirm(&p); switch (firm) { case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && ( /* Atom CPU */ model == 0x100C /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */ || model == 0x2006 /* 45 nm, Z6xx */ || model == 0x2007 /* 32 nm, Z2460 */ || model == 0x3005 /* 32 nm, Z2760 */ || model == 0x3006 /* 32 nm, N2xxx, D2xxx */ ))); case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); } return True; } #if !defined(MY_CPU_AMD64) && defined(_WIN32) #include static Bool CPU_Sys_Is_SSE_Supported() { OSVERSIONINFO vi; vi.dwOSVersionInfoSize = sizeof(vi); if (!GetVersionEx(&vi)) return False; return (vi.dwMajorVersion >= 5); } #define CHECK_SYS_SSE_SUPPORT if (!CPU_Sys_Is_SSE_Supported()) return False; #else #define CHECK_SYS_SSE_SUPPORT #endif Bool CPU_Is_Aes_Supported() { Cx86cpuid p; CHECK_SYS_SSE_SUPPORT if (!x86cpuid_CheckAndRead(&p)) return False; return (p.c >> 25) & 1; } #endif src/libs/7zip/win/C/CpuArch.h000066400000000000000000000077551325366651500161760ustar00rootroot00000000000000/* CpuArch.h -- CPU specific code 2013-11-12: Igor Pavlov : Public domain */ #ifndef __CPU_ARCH_H #define __CPU_ARCH_H #include "7zTypes.h" EXTERN_C_BEGIN /* MY_CPU_LE means that CPU is LITTLE ENDIAN. If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. */ #if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) #define MY_CPU_AMD64 #endif #if defined(MY_CPU_AMD64) || defined(_M_IA64) #define MY_CPU_64BIT #endif #if defined(_M_IX86) || defined(__i386__) #define MY_CPU_X86 #endif #if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) #define MY_CPU_X86_OR_AMD64 #endif #if defined(MY_CPU_X86) || defined(_M_ARM) #define MY_CPU_32BIT #endif #if defined(_WIN32) && defined(_M_ARM) #define MY_CPU_ARM_LE #endif #if defined(_WIN32) && defined(_M_IA64) #define MY_CPU_IA64_LE #endif #if defined(MY_CPU_X86_OR_AMD64) #define MY_CPU_LE_UNALIGN #endif #if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) #define MY_CPU_LE #endif #if defined(__BIG_ENDIAN__) || defined(__m68k__) || defined(__ARMEB__) || defined(__MIPSEB__) #define MY_CPU_BE #endif #if defined(MY_CPU_LE) && defined(MY_CPU_BE) Stop_Compiling_Bad_Endian #endif #ifdef MY_CPU_LE_UNALIGN #define GetUi16(p) (*(const UInt16 *)(const void *)(p)) #define GetUi32(p) (*(const UInt32 *)(const void *)(p)) #define GetUi64(p) (*(const UInt64 *)(const void *)(p)) #define SetUi16(p, d) *(UInt16 *)(p) = (d); #define SetUi32(p, d) *(UInt32 *)(p) = (d); #define SetUi64(p, d) *(UInt64 *)(p) = (d); #else #define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) #define GetUi32(p) ( \ ((const Byte *)(p))[0] | \ ((UInt32)((const Byte *)(p))[1] << 8) | \ ((UInt32)((const Byte *)(p))[2] << 16) | \ ((UInt32)((const Byte *)(p))[3] << 24)) #define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) #define SetUi16(p, d) { UInt32 _x_ = (d); \ ((Byte *)(p))[0] = (Byte)_x_; \ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } #define SetUi32(p, d) { UInt32 _x_ = (d); \ ((Byte *)(p))[0] = (Byte)_x_; \ ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } #define SetUi64(p, d) { UInt64 _x64_ = (d); \ SetUi32(p, (UInt32)_x64_); \ SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } #endif #if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) #include #pragma intrinsic(_byteswap_ulong) #pragma intrinsic(_byteswap_uint64) #define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) #define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) #else #define GetBe32(p) ( \ ((UInt32)((const Byte *)(p))[0] << 24) | \ ((UInt32)((const Byte *)(p))[1] << 16) | \ ((UInt32)((const Byte *)(p))[2] << 8) | \ ((const Byte *)(p))[3] ) #define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) #endif #define GetBe16(p) ((UInt16)(((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1])) #ifdef MY_CPU_X86_OR_AMD64 typedef struct { UInt32 maxFunc; UInt32 vendor[3]; UInt32 ver; UInt32 b; UInt32 c; UInt32 d; } Cx86cpuid; enum { CPU_FIRM_INTEL, CPU_FIRM_AMD, CPU_FIRM_VIA }; Bool x86cpuid_CheckAndRead(Cx86cpuid *p); int x86cpuid_GetFirm(const Cx86cpuid *p); #define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) #define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) #define x86cpuid_GetStepping(p) ((p)->ver & 0xF) Bool CPU_Is_InOrder(); Bool CPU_Is_Aes_Supported(); #endif EXTERN_C_END #endif src/libs/7zip/win/C/Delta.c000066400000000000000000000024711325366651500156630ustar00rootroot00000000000000/* Delta.c -- Delta converter 2009-05-26 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "Delta.h" void Delta_Init(Byte *state) { unsigned i; for (i = 0; i < DELTA_STATE_SIZE; i++) state[i] = 0; } static void MyMemCpy(Byte *dest, const Byte *src, unsigned size) { unsigned i; for (i = 0; i < size; i++) dest[i] = src[i]; } void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size) { Byte buf[DELTA_STATE_SIZE]; unsigned j = 0; MyMemCpy(buf, state, delta); { SizeT i; for (i = 0; i < size;) { for (j = 0; j < delta && i < size; i++, j++) { Byte b = data[i]; data[i] = (Byte)(b - buf[j]); buf[j] = b; } } } if (j == delta) j = 0; MyMemCpy(state, buf + j, delta - j); MyMemCpy(state + delta - j, buf, j); } void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size) { Byte buf[DELTA_STATE_SIZE]; unsigned j = 0; MyMemCpy(buf, state, delta); { SizeT i; for (i = 0; i < size;) { for (j = 0; j < delta && i < size; i++, j++) { buf[j] = data[i] = (Byte)(buf[j] + data[i]); } } } if (j == delta) j = 0; MyMemCpy(state, buf + j, delta - j); MyMemCpy(state + delta - j, buf, j); } src/libs/7zip/win/C/Delta.h000066400000000000000000000006131325366651500156640ustar00rootroot00000000000000/* Delta.h -- Delta converter 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __DELTA_H #define __DELTA_H #include "7zTypes.h" EXTERN_C_BEGIN #define DELTA_STATE_SIZE 256 void Delta_Init(Byte *state); void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size); void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size); EXTERN_C_END #endif src/libs/7zip/win/C/LzFind.c000066400000000000000000000500151325366651500160150ustar00rootroot00000000000000/* LzFind.c -- Match finder for LZ algorithms 2009-04-22 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "LzFind.h" #include "LzHash.h" #define kEmptyHashValue 0 #define kMaxValForNormalize ((UInt32)0xFFFFFFFF) #define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ #define kNormalizeMask (~(kNormalizeStepMin - 1)) #define kMaxHistorySize ((UInt32)3 << 30) #define kStartMaxLen 3 static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) { if (!p->directInput) { alloc->Free(alloc, p->bufferBase); p->bufferBase = 0; } } /* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) { UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; if (p->directInput) { p->blockSize = blockSize; return 1; } if (p->bufferBase == 0 || p->blockSize != blockSize) { LzInWindow_Free(p, alloc); p->blockSize = blockSize; p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); } return (p->bufferBase != 0); } Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) { p->posLimit -= subValue; p->pos -= subValue; p->streamPos -= subValue; } static void MatchFinder_ReadBlock(CMatchFinder *p) { if (p->streamEndWasReached || p->result != SZ_OK) return; if (p->directInput) { UInt32 curSize = 0xFFFFFFFF - p->streamPos; if (curSize > p->directInputRem) curSize = (UInt32)p->directInputRem; p->directInputRem -= curSize; p->streamPos += curSize; if (p->directInputRem == 0) p->streamEndWasReached = 1; return; } for (;;) { Byte *dest = p->buffer + (p->streamPos - p->pos); size_t size = (p->bufferBase + p->blockSize - dest); if (size == 0) return; p->result = p->stream->Read(p->stream, dest, &size); if (p->result != SZ_OK) return; if (size == 0) { p->streamEndWasReached = 1; return; } p->streamPos += (UInt32)size; if (p->streamPos - p->pos > p->keepSizeAfter) return; } } void MatchFinder_MoveBlock(CMatchFinder *p) { memmove(p->bufferBase, p->buffer - p->keepSizeBefore, (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); p->buffer = p->bufferBase + p->keepSizeBefore; } int MatchFinder_NeedMove(CMatchFinder *p) { if (p->directInput) return 0; /* if (p->streamEndWasReached) return 0; */ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); } void MatchFinder_ReadIfRequired(CMatchFinder *p) { if (p->streamEndWasReached) return; if (p->keepSizeAfter >= p->streamPos - p->pos) MatchFinder_ReadBlock(p); } static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) { if (MatchFinder_NeedMove(p)) MatchFinder_MoveBlock(p); MatchFinder_ReadBlock(p); } static void MatchFinder_SetDefaultSettings(CMatchFinder *p) { p->cutValue = 32; p->btMode = 1; p->numHashBytes = 4; p->bigHash = 0; } #define kCrcPoly 0xEDB88320 void MatchFinder_Construct(CMatchFinder *p) { UInt32 i; p->bufferBase = 0; p->directInput = 0; p->hash = 0; MatchFinder_SetDefaultSettings(p); for (i = 0; i < 256; i++) { UInt32 r = i; int j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); p->crc[i] = r; } } static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) { alloc->Free(alloc, p->hash); p->hash = 0; } void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) { MatchFinder_FreeThisClassMemory(p, alloc); LzInWindow_Free(p, alloc); } static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) { size_t sizeInBytes = (size_t)num * sizeof(CLzRef); if (sizeInBytes / sizeof(CLzRef) != num) return 0; return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); } int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) { UInt32 sizeReserv; if (historySize > kMaxHistorySize) { MatchFinder_Free(p, alloc); return 0; } sizeReserv = historySize >> 1; if (historySize > ((UInt32)2 << 30)) sizeReserv = historySize >> 2; sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); p->keepSizeBefore = historySize + keepAddBufferBefore + 1; p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ if (LzInWindow_Create(p, sizeReserv, alloc)) { UInt32 newCyclicBufferSize = historySize + 1; UInt32 hs; p->matchMaxLen = matchMaxLen; { p->fixedHashSize = 0; if (p->numHashBytes == 2) hs = (1 << 16) - 1; else { hs = historySize - 1; hs |= (hs >> 1); hs |= (hs >> 2); hs |= (hs >> 4); hs |= (hs >> 8); hs >>= 1; hs |= 0xFFFF; /* don't change it! It's required for Deflate */ if (hs > (1 << 24)) { if (p->numHashBytes == 3) hs = (1 << 24) - 1; else hs >>= 1; } } p->hashMask = hs; hs++; if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; hs += p->fixedHashSize; } { UInt32 prevSize = p->hashSizeSum + p->numSons; UInt32 newSize; p->historySize = historySize; p->hashSizeSum = hs; p->cyclicBufferSize = newCyclicBufferSize; p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); newSize = p->hashSizeSum + p->numSons; if (p->hash != 0 && prevSize == newSize) return 1; MatchFinder_FreeThisClassMemory(p, alloc); p->hash = AllocRefs(newSize, alloc); if (p->hash != 0) { p->son = p->hash + p->hashSizeSum; return 1; } } } MatchFinder_Free(p, alloc); return 0; } static void MatchFinder_SetLimits(CMatchFinder *p) { UInt32 limit = kMaxValForNormalize - p->pos; UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; if (limit2 < limit) limit = limit2; limit2 = p->streamPos - p->pos; if (limit2 <= p->keepSizeAfter) { if (limit2 > 0) limit2 = 1; } else limit2 -= p->keepSizeAfter; if (limit2 < limit) limit = limit2; { UInt32 lenLimit = p->streamPos - p->pos; if (lenLimit > p->matchMaxLen) lenLimit = p->matchMaxLen; p->lenLimit = lenLimit; } p->posLimit = p->pos + limit; } void MatchFinder_Init(CMatchFinder *p) { UInt32 i; for (i = 0; i < p->hashSizeSum; i++) p->hash[i] = kEmptyHashValue; p->cyclicBufferPos = 0; p->buffer = p->bufferBase; p->pos = p->streamPos = p->cyclicBufferSize; p->result = SZ_OK; p->streamEndWasReached = 0; MatchFinder_ReadBlock(p); MatchFinder_SetLimits(p); } static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) { return (p->pos - p->historySize - 1) & kNormalizeMask; } void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) { UInt32 i; for (i = 0; i < numItems; i++) { UInt32 value = items[i]; if (value <= subValue) value = kEmptyHashValue; else value -= subValue; items[i] = value; } } static void MatchFinder_Normalize(CMatchFinder *p) { UInt32 subValue = MatchFinder_GetSubValue(p); MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); MatchFinder_ReduceOffsets(p, subValue); } static void MatchFinder_CheckLimits(CMatchFinder *p) { if (p->pos == kMaxValForNormalize) MatchFinder_Normalize(p); if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) MatchFinder_CheckAndMoveAndRead(p); if (p->cyclicBufferPos == p->cyclicBufferSize) p->cyclicBufferPos = 0; MatchFinder_SetLimits(p); } static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, UInt32 *distances, UInt32 maxLen) { son[_cyclicBufferPos] = curMatch; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) return distances; { const Byte *pb = cur - delta; curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; if (pb[maxLen] == cur[maxLen] && *pb == *cur) { UInt32 len = 0; while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) return distances; } } } } } UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, UInt32 *distances, UInt32 maxLen) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return distances; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { if (++len != lenLimit && pb[len] == cur[len]) while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return distances; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { while (++len != lenLimit) if (pb[len] != cur[len]) break; { if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } #define MOVE_POS \ ++p->cyclicBufferPos; \ p->buffer++; \ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); #define MOVE_POS_RET MOVE_POS return offset; static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } #define GET_MATCHES_HEADER2(minLen, ret_op) \ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ cur = p->buffer; #define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) #define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) #define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue #define GET_MATCHES_FOOTER(offset, maxLen) \ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ distances + offset, maxLen) - distances); MOVE_POS_RET; #define SKIP_FOOTER \ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 1) } UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 2) } static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, delta2, maxLen, offset; GET_MATCHES_HEADER(3) HASH3_CALC; delta2 = p->pos - p->hash[hash2Value]; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; maxLen = 2; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[0] = maxLen; distances[1] = delta2 - 1; offset = 2; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } GET_MATCHES_FOOTER(offset, maxLen) } static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[ hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; GET_MATCHES_FOOTER(offset, maxLen) } static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[ hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances + offset, maxLen) - (distances)); MOVE_POS_RET } UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances, 2) - (distances)); MOVE_POS_RET } static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value; SKIP_HEADER(3) HASH3_CALC; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->pos; p->hash[kFix4HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { UInt32 hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(3) HASH_ZIP_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) { vTable->Init = (Mf_Init_Func)MatchFinder_Init; vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; if (!p->btMode) { vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; } else if (p->numHashBytes == 2) { vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; } else if (p->numHashBytes == 3) { vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; } else { vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; } } src/libs/7zip/win/C/LzFind.h000066400000000000000000000064441325366651500160310ustar00rootroot00000000000000/* LzFind.h -- Match finder for LZ algorithms 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_H #define __LZ_FIND_H #include "7zTypes.h" EXTERN_C_BEGIN typedef UInt32 CLzRef; typedef struct _CMatchFinder { Byte *buffer; UInt32 pos; UInt32 posLimit; UInt32 streamPos; UInt32 lenLimit; UInt32 cyclicBufferPos; UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ UInt32 matchMaxLen; CLzRef *hash; CLzRef *son; UInt32 hashMask; UInt32 cutValue; Byte *bufferBase; ISeqInStream *stream; int streamEndWasReached; UInt32 blockSize; UInt32 keepSizeBefore; UInt32 keepSizeAfter; UInt32 numHashBytes; int directInput; size_t directInputRem; int btMode; int bigHash; UInt32 historySize; UInt32 fixedHashSize; UInt32 hashSizeSum; UInt32 numSons; SRes result; UInt32 crc[256]; } CMatchFinder; #define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) #define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) #define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) int MatchFinder_NeedMove(CMatchFinder *p); Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); void MatchFinder_MoveBlock(CMatchFinder *p); void MatchFinder_ReadIfRequired(CMatchFinder *p); void MatchFinder_Construct(CMatchFinder *p); /* Conditions: historySize <= 3 GB keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB */ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, UInt32 *distances, UInt32 maxLen); /* Conditions: Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. Mf_GetPointerToCurrentPos_Func's result must be used only before any other function */ typedef void (*Mf_Init_Func)(void *object); typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); typedef void (*Mf_Skip_Func)(void *object, UInt32); typedef struct _IMatchFinder { Mf_Init_Func Init; Mf_GetIndexByte_Func GetIndexByte; Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; Mf_GetMatches_Func GetMatches; Mf_Skip_Func Skip; } IMatchFinder; void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); void MatchFinder_Init(CMatchFinder *p); UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); EXTERN_C_END #endif src/libs/7zip/win/C/LzFindMt.c000066400000000000000000000546651325366651500163350ustar00rootroot00000000000000/* LzFindMt.c -- multithreaded Match finder for LZ algorithms 2014-12-29 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "LzHash.h" #include "LzFindMt.h" void MtSync_Construct(CMtSync *p) { p->wasCreated = False; p->csWasInitialized = False; p->csWasEntered = False; Thread_Construct(&p->thread); Event_Construct(&p->canStart); Event_Construct(&p->wasStarted); Event_Construct(&p->wasStopped); Semaphore_Construct(&p->freeSemaphore); Semaphore_Construct(&p->filledSemaphore); } void MtSync_GetNextBlock(CMtSync *p) { if (p->needStart) { p->numProcessedBlocks = 1; p->needStart = False; p->stopWriting = False; p->exit = False; Event_Reset(&p->wasStarted); Event_Reset(&p->wasStopped); Event_Set(&p->canStart); Event_Wait(&p->wasStarted); } else { CriticalSection_Leave(&p->cs); p->csWasEntered = False; p->numProcessedBlocks++; Semaphore_Release1(&p->freeSemaphore); } Semaphore_Wait(&p->filledSemaphore); CriticalSection_Enter(&p->cs); p->csWasEntered = True; } /* MtSync_StopWriting must be called if Writing was started */ void MtSync_StopWriting(CMtSync *p) { UInt32 myNumBlocks = p->numProcessedBlocks; if (!Thread_WasCreated(&p->thread) || p->needStart) return; p->stopWriting = True; if (p->csWasEntered) { CriticalSection_Leave(&p->cs); p->csWasEntered = False; } Semaphore_Release1(&p->freeSemaphore); Event_Wait(&p->wasStopped); while (myNumBlocks++ != p->numProcessedBlocks) { Semaphore_Wait(&p->filledSemaphore); Semaphore_Release1(&p->freeSemaphore); } p->needStart = True; } void MtSync_Destruct(CMtSync *p) { if (Thread_WasCreated(&p->thread)) { MtSync_StopWriting(p); p->exit = True; if (p->needStart) Event_Set(&p->canStart); Thread_Wait(&p->thread); Thread_Close(&p->thread); } if (p->csWasInitialized) { CriticalSection_Delete(&p->cs); p->csWasInitialized = False; } Event_Close(&p->canStart); Event_Close(&p->wasStarted); Event_Close(&p->wasStopped); Semaphore_Close(&p->freeSemaphore); Semaphore_Close(&p->filledSemaphore); p->wasCreated = False; } #define RINOK_THREAD(x) { if ((x) != 0) return SZ_ERROR_THREAD; } static SRes MtSync_Create2(CMtSync *p, THREAD_FUNC_TYPE startAddress, void *obj, UInt32 numBlocks) { if (p->wasCreated) return SZ_OK; RINOK_THREAD(CriticalSection_Init(&p->cs)); p->csWasInitialized = True; RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canStart)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStarted)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStopped)); RINOK_THREAD(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks)); RINOK_THREAD(Semaphore_Create(&p->filledSemaphore, 0, numBlocks)); p->needStart = True; RINOK_THREAD(Thread_Create(&p->thread, startAddress, obj)); p->wasCreated = True; return SZ_OK; } static SRes MtSync_Create(CMtSync *p, THREAD_FUNC_TYPE startAddress, void *obj, UInt32 numBlocks) { SRes res = MtSync_Create2(p, startAddress, obj, numBlocks); if (res != SZ_OK) MtSync_Destruct(p); return res; } void MtSync_Init(CMtSync *p) { p->needStart = True; } #define kMtMaxValForNormalize 0xFFFFFFFF #define DEF_GetHeads2(name, v, action) \ static void GetHeads ## name(const Byte *p, UInt32 pos, \ UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc) \ { action; for (; numHeads != 0; numHeads--) { \ const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++; } } #define DEF_GetHeads(name, v) DEF_GetHeads2(name, v, ;) DEF_GetHeads2(2, (p[0] | ((UInt32)p[1] << 8)), hashMask = hashMask; crc = crc; ) DEF_GetHeads(3, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8)) & hashMask) DEF_GetHeads(4, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5)) & hashMask) DEF_GetHeads(4b, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ ((UInt32)p[3] << 16)) & hashMask) /* DEF_GetHeads(5, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5) ^ (crc[p[4]] << 3)) & hashMask) */ void HashThreadFunc(CMatchFinderMt *mt) { CMtSync *p = &mt->hashSync; for (;;) { UInt32 numProcessedBlocks = 0; Event_Wait(&p->canStart); Event_Set(&p->wasStarted); for (;;) { if (p->exit) return; if (p->stopWriting) { p->numProcessedBlocks = numProcessedBlocks; Event_Set(&p->wasStopped); break; } { CMatchFinder *mf = mt->MatchFinder; if (MatchFinder_NeedMove(mf)) { CriticalSection_Enter(&mt->btSync.cs); CriticalSection_Enter(&mt->hashSync.cs); { const Byte *beforePtr = MatchFinder_GetPointerToCurrentPos(mf); const Byte *afterPtr; MatchFinder_MoveBlock(mf); afterPtr = MatchFinder_GetPointerToCurrentPos(mf); mt->pointerToCurPos -= beforePtr - afterPtr; mt->buffer -= beforePtr - afterPtr; } CriticalSection_Leave(&mt->btSync.cs); CriticalSection_Leave(&mt->hashSync.cs); continue; } Semaphore_Wait(&p->freeSemaphore); MatchFinder_ReadIfRequired(mf); if (mf->pos > (kMtMaxValForNormalize - kMtHashBlockSize)) { UInt32 subValue = (mf->pos - mf->historySize - 1); MatchFinder_ReduceOffsets(mf, subValue); MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, mf->hashMask + 1); } { UInt32 *heads = mt->hashBuf + ((numProcessedBlocks++) & kMtHashNumBlocksMask) * kMtHashBlockSize; UInt32 num = mf->streamPos - mf->pos; heads[0] = 2; heads[1] = num; if (num >= mf->numHashBytes) { num = num - mf->numHashBytes + 1; if (num > kMtHashBlockSize - 2) num = kMtHashBlockSize - 2; mt->GetHeadsFunc(mf->buffer, mf->pos, mf->hash + mf->fixedHashSize, mf->hashMask, heads + 2, num, mf->crc); heads[0] += num; } mf->pos += num; mf->buffer += num; } } Semaphore_Release1(&p->filledSemaphore); } } } void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p) { MtSync_GetNextBlock(&p->hashSync); p->hashBufPosLimit = p->hashBufPos = ((p->hashSync.numProcessedBlocks - 1) & kMtHashNumBlocksMask) * kMtHashBlockSize; p->hashBufPosLimit += p->hashBuf[p->hashBufPos++]; p->hashNumAvail = p->hashBuf[p->hashBufPos++]; } #define kEmptyHashValue 0 /* #define MFMT_GM_INLINE */ #ifdef MFMT_GM_INLINE #define NO_INLINE MY_FAST_CALL Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, UInt32 *_distances, UInt32 _maxLen, const UInt32 *hash, Int32 limit, UInt32 size, UInt32 *posRes) { do { UInt32 *distances = _distances + 1; UInt32 curMatch = pos - *hash++; CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); UInt32 len0 = 0, len1 = 0; UInt32 cutValue = _cutValue; UInt32 maxLen = _maxLen; for (;;) { UInt32 delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; break; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const Byte *pb = cur - delta; UInt32 len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { if (++len != lenLimit && pb[len] == cur[len]) while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; break; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } pos++; _cyclicBufferPos++; cur++; { UInt32 num = (UInt32)(distances - _distances); *_distances = num - 1; _distances += num; limit -= num; } } while (limit > 0 && --size != 0); *posRes = pos; return limit; } #endif void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) { UInt32 numProcessed = 0; UInt32 curPos = 2; UInt32 limit = kMtBtBlockSize - (p->matchMaxLen * 2); distances[1] = p->hashNumAvail; while (curPos < limit) { if (p->hashBufPos == p->hashBufPosLimit) { MatchFinderMt_GetNextBlock_Hash(p); distances[1] = numProcessed + p->hashNumAvail; if (p->hashNumAvail >= p->numHashBytes) continue; for (; p->hashNumAvail != 0; p->hashNumAvail--) distances[curPos++] = 0; break; } { UInt32 size = p->hashBufPosLimit - p->hashBufPos; UInt32 lenLimit = p->matchMaxLen; UInt32 pos = p->pos; UInt32 cyclicBufferPos = p->cyclicBufferPos; if (lenLimit >= p->hashNumAvail) lenLimit = p->hashNumAvail; { UInt32 size2 = p->hashNumAvail - lenLimit + 1; if (size2 < size) size = size2; size2 = p->cyclicBufferSize - cyclicBufferPos; if (size2 < size) size = size2; } #ifndef MFMT_GM_INLINE while (curPos < limit && size-- != 0) { UInt32 *startDistances = distances + curPos; UInt32 num = (UInt32)(GetMatchesSpec1(lenLimit, pos - p->hashBuf[p->hashBufPos++], pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, startDistances + 1, p->numHashBytes - 1) - startDistances); *startDistances = num - 1; curPos += num; cyclicBufferPos++; pos++; p->buffer++; } #else { UInt32 posRes; curPos = limit - GetMatchesSpecN(lenLimit, pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos) , size, &posRes); p->hashBufPos += posRes - pos; cyclicBufferPos += posRes - pos; p->buffer += posRes - pos; pos = posRes; } #endif numProcessed += pos - p->pos; p->hashNumAvail -= pos - p->pos; p->pos = pos; if (cyclicBufferPos == p->cyclicBufferSize) cyclicBufferPos = 0; p->cyclicBufferPos = cyclicBufferPos; } } distances[0] = curPos; } void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex) { CMtSync *sync = &p->hashSync; if (!sync->needStart) { CriticalSection_Enter(&sync->cs); sync->csWasEntered = True; } BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize); if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize) { UInt32 subValue = p->pos - p->cyclicBufferSize; MatchFinder_Normalize3(subValue, p->son, p->cyclicBufferSize * 2); p->pos -= subValue; } if (!sync->needStart) { CriticalSection_Leave(&sync->cs); sync->csWasEntered = False; } } void BtThreadFunc(CMatchFinderMt *mt) { CMtSync *p = &mt->btSync; for (;;) { UInt32 blockIndex = 0; Event_Wait(&p->canStart); Event_Set(&p->wasStarted); for (;;) { if (p->exit) return; if (p->stopWriting) { p->numProcessedBlocks = blockIndex; MtSync_StopWriting(&mt->hashSync); Event_Set(&p->wasStopped); break; } Semaphore_Wait(&p->freeSemaphore); BtFillBlock(mt, blockIndex++); Semaphore_Release1(&p->filledSemaphore); } } } void MatchFinderMt_Construct(CMatchFinderMt *p) { p->hashBuf = 0; MtSync_Construct(&p->hashSync); MtSync_Construct(&p->btSync); } void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc) { alloc->Free(alloc, p->hashBuf); p->hashBuf = 0; } void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc) { MtSync_Destruct(&p->hashSync); MtSync_Destruct(&p->btSync); MatchFinderMt_FreeMem(p, alloc); } #define kHashBufferSize (kMtHashBlockSize * kMtHashNumBlocks) #define kBtBufferSize (kMtBtBlockSize * kMtBtNumBlocks) static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p); return 0; } static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE BtThreadFunc2(void *p) { Byte allocaDummy[0x180]; allocaDummy[0] = 0; allocaDummy[1] = allocaDummy[0]; BtThreadFunc((CMatchFinderMt *)p); return 0; } SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) { CMatchFinder *mf = p->MatchFinder; p->historySize = historySize; if (kMtBtBlockSize <= matchMaxLen * 4) return SZ_ERROR_PARAM; if (p->hashBuf == 0) { p->hashBuf = (UInt32 *)alloc->Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32)); if (p->hashBuf == 0) return SZ_ERROR_MEM; p->btBuf = p->hashBuf + kHashBufferSize; } keepAddBufferBefore += (kHashBufferSize + kBtBufferSize); keepAddBufferAfter += kMtHashBlockSize; if (!MatchFinder_Create(mf, historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter, alloc)) return SZ_ERROR_MEM; RINOK(MtSync_Create(&p->hashSync, HashThreadFunc2, p, kMtHashNumBlocks)); RINOK(MtSync_Create(&p->btSync, BtThreadFunc2, p, kMtBtNumBlocks)); return SZ_OK; } /* Call it after ReleaseStream / SetStream */ void MatchFinderMt_Init(CMatchFinderMt *p) { CMatchFinder *mf = p->MatchFinder; p->btBufPos = p->btBufPosLimit = 0; p->hashBufPos = p->hashBufPosLimit = 0; MatchFinder_Init(mf); p->pointerToCurPos = MatchFinder_GetPointerToCurrentPos(mf); p->btNumAvailBytes = 0; p->lzPos = p->historySize + 1; p->hash = mf->hash; p->fixedHashSize = mf->fixedHashSize; p->crc = mf->crc; p->son = mf->son; p->matchMaxLen = mf->matchMaxLen; p->numHashBytes = mf->numHashBytes; p->pos = mf->pos; p->buffer = mf->buffer; p->cyclicBufferPos = mf->cyclicBufferPos; p->cyclicBufferSize = mf->cyclicBufferSize; p->cutValue = mf->cutValue; } /* ReleaseStream is required to finish multithreading */ void MatchFinderMt_ReleaseStream(CMatchFinderMt *p) { MtSync_StopWriting(&p->btSync); /* p->MatchFinder->ReleaseStream(); */ } void MatchFinderMt_Normalize(CMatchFinderMt *p) { MatchFinder_Normalize3(p->lzPos - p->historySize - 1, p->hash, p->fixedHashSize); p->lzPos = p->historySize + 1; } void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p) { UInt32 blockIndex; MtSync_GetNextBlock(&p->btSync); blockIndex = ((p->btSync.numProcessedBlocks - 1) & kMtBtNumBlocksMask); p->btBufPosLimit = p->btBufPos = blockIndex * kMtBtBlockSize; p->btBufPosLimit += p->btBuf[p->btBufPos++]; p->btNumAvailBytes = p->btBuf[p->btBufPos++]; if (p->lzPos >= kMtMaxValForNormalize - kMtBtBlockSize) MatchFinderMt_Normalize(p); } const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p) { return p->pointerToCurPos; } #define GET_NEXT_BLOCK_IF_REQUIRED if (p->btBufPos == p->btBufPosLimit) MatchFinderMt_GetNextBlock_Bt(p); UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p) { GET_NEXT_BLOCK_IF_REQUIRED; return p->btNumAvailBytes; } Byte MatchFinderMt_GetIndexByte(CMatchFinderMt *p, Int32 index) { return p->pointerToCurPos[index]; } UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { UInt32 hash2Value, curMatch2; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH2_CALC curMatch2 = hash[hash2Value]; hash[hash2Value] = lzPos; if (curMatch2 >= matchMinPos) if (cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { *distances++ = 2; *distances++ = lzPos - curMatch2 - 1; } return distances; } UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { UInt32 hash2Value, hash3Value, curMatch2, curMatch3; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH3_CALC curMatch2 = hash[ hash2Value]; curMatch3 = hash[kFix3HashSize + hash3Value]; hash[ hash2Value] = hash[kFix3HashSize + hash3Value] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch2 - 1; if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) { distances[0] = 3; return distances + 2; } distances[0] = 2; distances += 2; } if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) { *distances++ = 3; *distances++ = lzPos - curMatch3 - 1; } return distances; } /* UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { UInt32 hash2Value, hash3Value, hash4Value, curMatch2, curMatch3, curMatch4; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH4_CALC curMatch2 = hash[ hash2Value]; curMatch3 = hash[kFix3HashSize + hash3Value]; curMatch4 = hash[kFix4HashSize + hash4Value]; hash[ hash2Value] = hash[kFix3HashSize + hash3Value] = hash[kFix4HashSize + hash4Value] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch2 - 1; if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) { distances[0] = (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3; return distances + 2; } distances[0] = 2; distances += 2; } if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch3 - 1; if (cur[(ptrdiff_t)curMatch3 - lzPos + 3] == cur[3]) { distances[0] = 4; return distances + 2; } distances[0] = 3; distances += 2; } if (curMatch4 >= matchMinPos) if ( cur[(ptrdiff_t)curMatch4 - lzPos] == cur[0] && cur[(ptrdiff_t)curMatch4 - lzPos + 3] == cur[3] ) { *distances++ = 4; *distances++ = lzPos - curMatch4 - 1; } return distances; } */ #define INCREASE_LZ_POS p->lzPos++; p->pointerToCurPos++; UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances) { const UInt32 *btBuf = p->btBuf + p->btBufPos; UInt32 len = *btBuf++; p->btBufPos += 1 + len; p->btNumAvailBytes--; { UInt32 i; for (i = 0; i < len; i += 2) { *distances++ = *btBuf++; *distances++ = *btBuf++; } } INCREASE_LZ_POS return len; } UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) { const UInt32 *btBuf = p->btBuf + p->btBufPos; UInt32 len = *btBuf++; p->btBufPos += 1 + len; if (len == 0) { if (p->btNumAvailBytes-- >= 4) len = (UInt32)(p->MixMatchesFunc(p, p->lzPos - p->historySize, distances) - (distances)); } else { /* Condition: there are matches in btBuf with length < p->numHashBytes */ UInt32 *distances2; p->btNumAvailBytes--; distances2 = p->MixMatchesFunc(p, p->lzPos - btBuf[1], distances); do { *distances2++ = *btBuf++; *distances2++ = *btBuf++; } while ((len -= 2) != 0); len = (UInt32)(distances2 - (distances)); } INCREASE_LZ_POS return len; } #define SKIP_HEADER2_MT do { GET_NEXT_BLOCK_IF_REQUIRED #define SKIP_HEADER_MT(n) SKIP_HEADER2_MT if (p->btNumAvailBytes-- >= (n)) { const Byte *cur = p->pointerToCurPos; UInt32 *hash = p->hash; #define SKIP_FOOTER_MT } INCREASE_LZ_POS p->btBufPos += p->btBuf[p->btBufPos] + 1; } while (--num != 0); void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER2_MT { p->btNumAvailBytes--; SKIP_FOOTER_MT } void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(2) UInt32 hash2Value; MT_HASH2_CALC hash[hash2Value] = p->lzPos; SKIP_FOOTER_MT } void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(3) UInt32 hash2Value, hash3Value; MT_HASH3_CALC hash[kFix3HashSize + hash3Value] = hash[ hash2Value] = p->lzPos; SKIP_FOOTER_MT } /* void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(4) UInt32 hash2Value, hash3Value, hash4Value; MT_HASH4_CALC hash[kFix4HashSize + hash4Value] = hash[kFix3HashSize + hash3Value] = hash[ hash2Value] = p->lzPos; SKIP_FOOTER_MT } */ void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable) { vTable->Init = (Mf_Init_Func)MatchFinderMt_Init; vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinderMt_GetIndexByte; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinderMt_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinderMt_GetPointerToCurrentPos; vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt_GetMatches; switch(p->MatchFinder->numHashBytes) { case 2: p->GetHeadsFunc = GetHeads2; p->MixMatchesFunc = (Mf_Mix_Matches)0; vTable->Skip = (Mf_Skip_Func)MatchFinderMt0_Skip; vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt2_GetMatches; break; case 3: p->GetHeadsFunc = GetHeads3; p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches2; vTable->Skip = (Mf_Skip_Func)MatchFinderMt2_Skip; break; default: /* case 4: */ p->GetHeadsFunc = p->MatchFinder->bigHash ? GetHeads4b : GetHeads4; /* p->GetHeadsFunc = GetHeads4; */ p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches3; vTable->Skip = (Mf_Skip_Func)MatchFinderMt3_Skip; break; /* default: p->GetHeadsFunc = GetHeads5; p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches4; vTable->Skip = (Mf_Skip_Func)MatchFinderMt4_Skip; break; */ } } src/libs/7zip/win/C/LzFindMt.h000066400000000000000000000047031325366651500163260ustar00rootroot00000000000000/* LzFindMt.h -- multithreaded Match finder for LZ algorithms 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_MT_H #define __LZ_FIND_MT_H #include "LzFind.h" #include "Threads.h" EXTERN_C_BEGIN #define kMtHashBlockSize (1 << 13) #define kMtHashNumBlocks (1 << 3) #define kMtHashNumBlocksMask (kMtHashNumBlocks - 1) #define kMtBtBlockSize (1 << 14) #define kMtBtNumBlocks (1 << 6) #define kMtBtNumBlocksMask (kMtBtNumBlocks - 1) typedef struct _CMtSync { Bool wasCreated; Bool needStart; Bool exit; Bool stopWriting; CThread thread; CAutoResetEvent canStart; CAutoResetEvent wasStarted; CAutoResetEvent wasStopped; CSemaphore freeSemaphore; CSemaphore filledSemaphore; Bool csWasInitialized; Bool csWasEntered; CCriticalSection cs; UInt32 numProcessedBlocks; } CMtSync; typedef UInt32 * (*Mf_Mix_Matches)(void *p, UInt32 matchMinPos, UInt32 *distances); /* kMtCacheLineDummy must be >= size_of_CPU_cache_line */ #define kMtCacheLineDummy 128 typedef void (*Mf_GetHeads)(const Byte *buffer, UInt32 pos, UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc); typedef struct _CMatchFinderMt { /* LZ */ const Byte *pointerToCurPos; UInt32 *btBuf; UInt32 btBufPos; UInt32 btBufPosLimit; UInt32 lzPos; UInt32 btNumAvailBytes; UInt32 *hash; UInt32 fixedHashSize; UInt32 historySize; const UInt32 *crc; Mf_Mix_Matches MixMatchesFunc; /* LZ + BT */ CMtSync btSync; Byte btDummy[kMtCacheLineDummy]; /* BT */ UInt32 *hashBuf; UInt32 hashBufPos; UInt32 hashBufPosLimit; UInt32 hashNumAvail; CLzRef *son; UInt32 matchMaxLen; UInt32 numHashBytes; UInt32 pos; Byte *buffer; UInt32 cyclicBufferPos; UInt32 cyclicBufferSize; /* it must be historySize + 1 */ UInt32 cutValue; /* BT + Hash */ CMtSync hashSync; /* Byte hashDummy[kMtCacheLineDummy]; */ /* Hash */ Mf_GetHeads GetHeadsFunc; CMatchFinder *MatchFinder; } CMatchFinderMt; void MatchFinderMt_Construct(CMatchFinderMt *p); void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc); SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable); void MatchFinderMt_ReleaseStream(CMatchFinderMt *p); EXTERN_C_END #endif src/libs/7zip/win/C/LzHash.h000066400000000000000000000037421325366651500160320ustar00rootroot00000000000000/* LzHash.h -- HASH functions for LZ algorithms 2009-02-07 : Igor Pavlov : Public domain */ #ifndef __LZ_HASH_H #define __LZ_HASH_H #define kHash2Size (1 << 10) #define kHash3Size (1 << 16) #define kHash4Size (1 << 20) #define kFix3HashSize (kHash2Size) #define kFix4HashSize (kHash2Size + kHash3Size) #define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) #define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); #define HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } #define HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } #define HASH5_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ hash4Value &= (kHash4Size - 1); } /* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ #define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; #define MT_HASH2_CALC \ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); #define MT_HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } #define MT_HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } #endif src/libs/7zip/win/C/Lzma2Dec.c000066400000000000000000000236271325366651500162410ustar00rootroot00000000000000/* Lzma2Dec.c -- LZMA2 Decoder 2010-12-15 : Igor Pavlov : Public domain */ /* #define SHOW_DEBUG_INFO */ #include "Precomp.h" #ifdef SHOW_DEBUG_INFO #include #endif #include #include "Lzma2Dec.h" /* 00000000 - EOS 00000001 U U - Uncompressed Reset Dic 00000010 U U - Uncompressed No Reset 100uuuuu U U P P - LZMA no reset 101uuuuu U U P P - LZMA reset state 110uuuuu U U P P S - LZMA reset state + new prop 111uuuuu U U P P S - LZMA reset state + new prop + reset dic u, U - Unpack Size P - Pack Size S - Props */ #define LZMA2_CONTROL_LZMA (1 << 7) #define LZMA2_CONTROL_COPY_NO_RESET 2 #define LZMA2_CONTROL_COPY_RESET_DIC 1 #define LZMA2_CONTROL_EOF 0 #define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0) #define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3) #define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2) #define LZMA2_LCLP_MAX 4 #define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) #ifdef SHOW_DEBUG_INFO #define PRF(x) x #else #define PRF(x) #endif typedef enum { LZMA2_STATE_CONTROL, LZMA2_STATE_UNPACK0, LZMA2_STATE_UNPACK1, LZMA2_STATE_PACK0, LZMA2_STATE_PACK1, LZMA2_STATE_PROP, LZMA2_STATE_DATA, LZMA2_STATE_DATA_CONT, LZMA2_STATE_FINISHED, LZMA2_STATE_ERROR } ELzma2State; static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props) { UInt32 dicSize; if (prop > 40) return SZ_ERROR_UNSUPPORTED; dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop); props[0] = (Byte)LZMA2_LCLP_MAX; props[1] = (Byte)(dicSize); props[2] = (Byte)(dicSize >> 8); props[3] = (Byte)(dicSize >> 16); props[4] = (Byte)(dicSize >> 24); return SZ_OK; } SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) { Byte props[LZMA_PROPS_SIZE]; RINOK(Lzma2Dec_GetOldProps(prop, props)); return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc); } SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) { Byte props[LZMA_PROPS_SIZE]; RINOK(Lzma2Dec_GetOldProps(prop, props)); return LzmaDec_Allocate(&p->decoder, props, LZMA_PROPS_SIZE, alloc); } void Lzma2Dec_Init(CLzma2Dec *p) { p->state = LZMA2_STATE_CONTROL; p->needInitDic = True; p->needInitState = True; p->needInitProp = True; LzmaDec_Init(&p->decoder); } static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) { switch(p->state) { case LZMA2_STATE_CONTROL: p->control = b; PRF(printf("\n %4X ", p->decoder.dicPos)); PRF(printf(" %2X", b)); if (p->control == 0) return LZMA2_STATE_FINISHED; if (LZMA2_IS_UNCOMPRESSED_STATE(p)) { if ((p->control & 0x7F) > 2) return LZMA2_STATE_ERROR; p->unpackSize = 0; } else p->unpackSize = (UInt32)(p->control & 0x1F) << 16; return LZMA2_STATE_UNPACK0; case LZMA2_STATE_UNPACK0: p->unpackSize |= (UInt32)b << 8; return LZMA2_STATE_UNPACK1; case LZMA2_STATE_UNPACK1: p->unpackSize |= (UInt32)b; p->unpackSize++; PRF(printf(" %8d", p->unpackSize)); return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0; case LZMA2_STATE_PACK0: p->packSize = (UInt32)b << 8; return LZMA2_STATE_PACK1; case LZMA2_STATE_PACK1: p->packSize |= (UInt32)b; p->packSize++; PRF(printf(" %8d", p->packSize)); return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP: (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA); case LZMA2_STATE_PROP: { int lc, lp; if (b >= (9 * 5 * 5)) return LZMA2_STATE_ERROR; lc = b % 9; b /= 9; p->decoder.prop.pb = b / 5; lp = b % 5; if (lc + lp > LZMA2_LCLP_MAX) return LZMA2_STATE_ERROR; p->decoder.prop.lc = lc; p->decoder.prop.lp = lp; p->needInitProp = False; return LZMA2_STATE_DATA; } } return LZMA2_STATE_ERROR; } static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size) { memcpy(p->dic + p->dicPos, src, size); p->dicPos += size; if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size) p->checkDicSize = p->prop.dicSize; p->processedPos += (UInt32)size; } void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState); SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; *srcLen = 0; *status = LZMA_STATUS_NOT_SPECIFIED; while (p->state != LZMA2_STATE_FINISHED) { SizeT dicPos = p->decoder.dicPos; if (p->state == LZMA2_STATE_ERROR) return SZ_ERROR_DATA; if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_OK; } if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT) { if (*srcLen == inSize) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } (*srcLen)++; p->state = Lzma2Dec_UpdateState(p, *src++); continue; } { SizeT destSizeCur = dicLimit - dicPos; SizeT srcSizeCur = inSize - *srcLen; ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY; if (p->unpackSize <= destSizeCur) { destSizeCur = (SizeT)p->unpackSize; curFinishMode = LZMA_FINISH_END; } if (LZMA2_IS_UNCOMPRESSED_STATE(p)) { if (*srcLen == inSize) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (p->state == LZMA2_STATE_DATA) { Bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC); if (initDic) p->needInitProp = p->needInitState = True; else if (p->needInitDic) return SZ_ERROR_DATA; p->needInitDic = False; LzmaDec_InitDicAndState(&p->decoder, initDic, False); } if (srcSizeCur > destSizeCur) srcSizeCur = destSizeCur; if (srcSizeCur == 0) return SZ_ERROR_DATA; LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur); src += srcSizeCur; *srcLen += srcSizeCur; p->unpackSize -= (UInt32)srcSizeCur; p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT; } else { SizeT outSizeProcessed; SRes res; if (p->state == LZMA2_STATE_DATA) { int mode = LZMA2_GET_LZMA_MODE(p); Bool initDic = (mode == 3); Bool initState = (mode > 0); if ((!initDic && p->needInitDic) || (!initState && p->needInitState)) return SZ_ERROR_DATA; LzmaDec_InitDicAndState(&p->decoder, initDic, initState); p->needInitDic = False; p->needInitState = False; p->state = LZMA2_STATE_DATA_CONT; } if (srcSizeCur > p->packSize) srcSizeCur = (SizeT)p->packSize; res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status); src += srcSizeCur; *srcLen += srcSizeCur; p->packSize -= (UInt32)srcSizeCur; outSizeProcessed = p->decoder.dicPos - dicPos; p->unpackSize -= (UInt32)outSizeProcessed; RINOK(res); if (*status == LZMA_STATUS_NEEDS_MORE_INPUT) return res; if (srcSizeCur == 0 && outSizeProcessed == 0) { if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK || p->unpackSize != 0 || p->packSize != 0) return SZ_ERROR_DATA; p->state = LZMA2_STATE_CONTROL; } if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) *status = LZMA_STATUS_NOT_FINISHED; } } } *status = LZMA_STATUS_FINISHED_WITH_MARK; return SZ_OK; } SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen, inSize = *srcLen; *srcLen = *destLen = 0; for (;;) { SizeT srcSizeCur = inSize, outSizeCur, dicPos; ELzmaFinishMode curFinishMode; SRes res; if (p->decoder.dicPos == p->decoder.dicBufSize) p->decoder.dicPos = 0; dicPos = p->decoder.dicPos; if (outSize > p->decoder.dicBufSize - dicPos) { outSizeCur = p->decoder.dicBufSize; curFinishMode = LZMA_FINISH_ANY; } else { outSizeCur = dicPos + outSize; curFinishMode = finishMode; } res = Lzma2Dec_DecodeToDic(p, outSizeCur, src, &srcSizeCur, curFinishMode, status); src += srcSizeCur; inSize -= srcSizeCur; *srcLen += srcSizeCur; outSizeCur = p->decoder.dicPos - dicPos; memcpy(dest, p->decoder.dic + dicPos, outSizeCur); dest += outSizeCur; outSize -= outSizeCur; *destLen += outSizeCur; if (res != 0) return res; if (outSizeCur == 0 || outSize == 0) return SZ_OK; } } SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) { CLzma2Dec p; SRes res; SizeT outSize = *destLen, inSize = *srcLen; *destLen = *srcLen = 0; *status = LZMA_STATUS_NOT_SPECIFIED; Lzma2Dec_Construct(&p); RINOK(Lzma2Dec_AllocateProbs(&p, prop, alloc)); p.decoder.dic = dest; p.decoder.dicBufSize = outSize; Lzma2Dec_Init(&p); *srcLen = inSize; res = Lzma2Dec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); *destLen = p.decoder.dicPos; if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) res = SZ_ERROR_INPUT_EOF; Lzma2Dec_FreeProbs(&p, alloc); return res; } src/libs/7zip/win/C/Lzma2Dec.h000066400000000000000000000043031325366651500162340ustar00rootroot00000000000000/* Lzma2Dec.h -- LZMA2 Decoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA2_DEC_H #define __LZMA2_DEC_H #include "LzmaDec.h" EXTERN_C_BEGIN /* ---------- State Interface ---------- */ typedef struct { CLzmaDec decoder; UInt32 packSize; UInt32 unpackSize; int state; Byte control; Bool needInitDic; Bool needInitState; Bool needInitProp; } CLzma2Dec; #define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder) #define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc); #define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc); SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); SRes Lzma2Dec_Allocate(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); void Lzma2Dec_Init(CLzma2Dec *p); /* finishMode: It has meaning only if the decoding reaches output limit (*destLen or dicLimit). LZMA_FINISH_ANY - use smallest number of input bytes LZMA_FINISH_END - read EndOfStream marker after decoding Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_NEEDS_MORE_INPUT SZ_ERROR_DATA - Data error */ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); SRes Lzma2Dec_DecodeToBuf(CLzma2Dec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- One Call Interface ---------- */ /* finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - use smallest number of input bytes LZMA_FINISH_END - read EndOfStream marker after decoding Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED SZ_ERROR_DATA - Data error SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). */ SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); EXTERN_C_END #endif src/libs/7zip/win/C/Lzma2Enc.c000066400000000000000000000304521325366651500162450ustar00rootroot00000000000000/* Lzma2Enc.c -- LZMA2 Encoder 2012-06-19 : Igor Pavlov : Public domain */ #include "Precomp.h" /* #include */ #include /* #define _7ZIP_ST */ #include "Lzma2Enc.h" #ifndef _7ZIP_ST #include "MtCoder.h" #else #define NUM_MT_CODER_THREADS_MAX 1 #endif #define LZMA2_CONTROL_LZMA (1 << 7) #define LZMA2_CONTROL_COPY_NO_RESET 2 #define LZMA2_CONTROL_COPY_RESET_DIC 1 #define LZMA2_CONTROL_EOF 0 #define LZMA2_LCLP_MAX 4 #define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) #define LZMA2_PACK_SIZE_MAX (1 << 16) #define LZMA2_COPY_CHUNK_SIZE LZMA2_PACK_SIZE_MAX #define LZMA2_UNPACK_SIZE_MAX (1 << 21) #define LZMA2_KEEP_WINDOW_SIZE LZMA2_UNPACK_SIZE_MAX #define LZMA2_CHUNK_SIZE_COMPRESSED_MAX ((1 << 16) + 16) #define PRF(x) /* x */ /* ---------- CLzma2EncInt ---------- */ typedef struct { CLzmaEncHandle enc; UInt64 srcPos; Byte props; Bool needInitState; Bool needInitProp; } CLzma2EncInt; static SRes Lzma2EncInt_Init(CLzma2EncInt *p, const CLzma2EncProps *props) { Byte propsEncoded[LZMA_PROPS_SIZE]; SizeT propsSize = LZMA_PROPS_SIZE; RINOK(LzmaEnc_SetProps(p->enc, &props->lzmaProps)); RINOK(LzmaEnc_WriteProperties(p->enc, propsEncoded, &propsSize)); p->srcPos = 0; p->props = propsEncoded[0]; p->needInitState = True; p->needInitProp = True; return SZ_OK; } SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize); const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp); void LzmaEnc_Finish(CLzmaEncHandle pp); void LzmaEnc_SaveState(CLzmaEncHandle pp); void LzmaEnc_RestoreState(CLzmaEncHandle pp); static SRes Lzma2EncInt_EncodeSubblock(CLzma2EncInt *p, Byte *outBuf, size_t *packSizeRes, ISeqOutStream *outStream) { size_t packSizeLimit = *packSizeRes; size_t packSize = packSizeLimit; UInt32 unpackSize = LZMA2_UNPACK_SIZE_MAX; unsigned lzHeaderSize = 5 + (p->needInitProp ? 1 : 0); Bool useCopyBlock; SRes res; *packSizeRes = 0; if (packSize < lzHeaderSize) return SZ_ERROR_OUTPUT_EOF; packSize -= lzHeaderSize; LzmaEnc_SaveState(p->enc); res = LzmaEnc_CodeOneMemBlock(p->enc, p->needInitState, outBuf + lzHeaderSize, &packSize, LZMA2_PACK_SIZE_MAX, &unpackSize); PRF(printf("\npackSize = %7d unpackSize = %7d ", packSize, unpackSize)); if (unpackSize == 0) return res; if (res == SZ_OK) useCopyBlock = (packSize + 2 >= unpackSize || packSize > (1 << 16)); else { if (res != SZ_ERROR_OUTPUT_EOF) return res; res = SZ_OK; useCopyBlock = True; } if (useCopyBlock) { size_t destPos = 0; PRF(printf("################# COPY ")); while (unpackSize > 0) { UInt32 u = (unpackSize < LZMA2_COPY_CHUNK_SIZE) ? unpackSize : LZMA2_COPY_CHUNK_SIZE; if (packSizeLimit - destPos < u + 3) return SZ_ERROR_OUTPUT_EOF; outBuf[destPos++] = (Byte)(p->srcPos == 0 ? LZMA2_CONTROL_COPY_RESET_DIC : LZMA2_CONTROL_COPY_NO_RESET); outBuf[destPos++] = (Byte)((u - 1) >> 8); outBuf[destPos++] = (Byte)(u - 1); memcpy(outBuf + destPos, LzmaEnc_GetCurBuf(p->enc) - unpackSize, u); unpackSize -= u; destPos += u; p->srcPos += u; if (outStream) { *packSizeRes += destPos; if (outStream->Write(outStream, outBuf, destPos) != destPos) return SZ_ERROR_WRITE; destPos = 0; } else *packSizeRes = destPos; /* needInitState = True; */ } LzmaEnc_RestoreState(p->enc); return SZ_OK; } { size_t destPos = 0; UInt32 u = unpackSize - 1; UInt32 pm = (UInt32)(packSize - 1); unsigned mode = (p->srcPos == 0) ? 3 : (p->needInitState ? (p->needInitProp ? 2 : 1) : 0); PRF(printf(" ")); outBuf[destPos++] = (Byte)(LZMA2_CONTROL_LZMA | (mode << 5) | ((u >> 16) & 0x1F)); outBuf[destPos++] = (Byte)(u >> 8); outBuf[destPos++] = (Byte)u; outBuf[destPos++] = (Byte)(pm >> 8); outBuf[destPos++] = (Byte)pm; if (p->needInitProp) outBuf[destPos++] = p->props; p->needInitProp = False; p->needInitState = False; destPos += packSize; p->srcPos += unpackSize; if (outStream) if (outStream->Write(outStream, outBuf, destPos) != destPos) return SZ_ERROR_WRITE; *packSizeRes = destPos; return SZ_OK; } } /* ---------- Lzma2 Props ---------- */ void Lzma2EncProps_Init(CLzma2EncProps *p) { LzmaEncProps_Init(&p->lzmaProps); p->numTotalThreads = -1; p->numBlockThreads = -1; p->blockSize = 0; } void Lzma2EncProps_Normalize(CLzma2EncProps *p) { int t1, t1n, t2, t3; { CLzmaEncProps lzmaProps = p->lzmaProps; LzmaEncProps_Normalize(&lzmaProps); t1n = lzmaProps.numThreads; } t1 = p->lzmaProps.numThreads; t2 = p->numBlockThreads; t3 = p->numTotalThreads; if (t2 > NUM_MT_CODER_THREADS_MAX) t2 = NUM_MT_CODER_THREADS_MAX; if (t3 <= 0) { if (t2 <= 0) t2 = 1; t3 = t1n * t2; } else if (t2 <= 0) { t2 = t3 / t1n; if (t2 == 0) { t1 = 1; t2 = t3; } if (t2 > NUM_MT_CODER_THREADS_MAX) t2 = NUM_MT_CODER_THREADS_MAX; } else if (t1 <= 0) { t1 = t3 / t2; if (t1 == 0) t1 = 1; } else t3 = t1n * t2; p->lzmaProps.numThreads = t1; LzmaEncProps_Normalize(&p->lzmaProps); if (p->blockSize == 0) { UInt32 dictSize = p->lzmaProps.dictSize; UInt64 blockSize = (UInt64)dictSize << 2; const UInt32 kMinSize = (UInt32)1 << 20; const UInt32 kMaxSize = (UInt32)1 << 28; if (blockSize < kMinSize) blockSize = kMinSize; if (blockSize > kMaxSize) blockSize = kMaxSize; if (blockSize < dictSize) blockSize = dictSize; p->blockSize = (size_t)blockSize; } if (t2 > 1) { UInt64 temp = p->lzmaProps.reduceSize + p->blockSize - 1; if (temp > p->lzmaProps.reduceSize) { UInt64 numBlocks = temp / p->blockSize; if (numBlocks < t2) { t2 = (UInt32)numBlocks; t3 = t1 * t2; } } } p->numBlockThreads = t2; p->numTotalThreads = t3; } static SRes Progress(ICompressProgress *p, UInt64 inSize, UInt64 outSize) { return (p && p->Progress(p, inSize, outSize) != SZ_OK) ? SZ_ERROR_PROGRESS : SZ_OK; } /* ---------- Lzma2 ---------- */ typedef struct { Byte propEncoded; CLzma2EncProps props; Byte *outBuf; ISzAlloc *alloc; ISzAlloc *allocBig; CLzma2EncInt coders[NUM_MT_CODER_THREADS_MAX]; #ifndef _7ZIP_ST CMtCoder mtCoder; #endif } CLzma2Enc; /* ---------- Lzma2EncThread ---------- */ static SRes Lzma2Enc_EncodeMt1(CLzma2EncInt *p, CLzma2Enc *mainEncoder, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress) { UInt64 packTotal = 0; SRes res = SZ_OK; if (mainEncoder->outBuf == 0) { mainEncoder->outBuf = (Byte *)IAlloc_Alloc(mainEncoder->alloc, LZMA2_CHUNK_SIZE_COMPRESSED_MAX); if (mainEncoder->outBuf == 0) return SZ_ERROR_MEM; } RINOK(Lzma2EncInt_Init(p, &mainEncoder->props)); RINOK(LzmaEnc_PrepareForLzma2(p->enc, inStream, LZMA2_KEEP_WINDOW_SIZE, mainEncoder->alloc, mainEncoder->allocBig)); for (;;) { size_t packSize = LZMA2_CHUNK_SIZE_COMPRESSED_MAX; res = Lzma2EncInt_EncodeSubblock(p, mainEncoder->outBuf, &packSize, outStream); if (res != SZ_OK) break; packTotal += packSize; res = Progress(progress, p->srcPos, packTotal); if (res != SZ_OK) break; if (packSize == 0) break; } LzmaEnc_Finish(p->enc); if (res == SZ_OK) { Byte b = 0; if (outStream->Write(outStream, &b, 1) != 1) return SZ_ERROR_WRITE; } return res; } #ifndef _7ZIP_ST typedef struct { IMtCoderCallback funcTable; CLzma2Enc *lzma2Enc; } CMtCallbackImp; static SRes MtCallbackImp_Code(void *pp, unsigned index, Byte *dest, size_t *destSize, const Byte *src, size_t srcSize, int finished) { CMtCallbackImp *imp = (CMtCallbackImp *)pp; CLzma2Enc *mainEncoder = imp->lzma2Enc; CLzma2EncInt *p = &mainEncoder->coders[index]; SRes res = SZ_OK; { size_t destLim = *destSize; *destSize = 0; if (srcSize != 0) { RINOK(Lzma2EncInt_Init(p, &mainEncoder->props)); RINOK(LzmaEnc_MemPrepare(p->enc, src, srcSize, LZMA2_KEEP_WINDOW_SIZE, mainEncoder->alloc, mainEncoder->allocBig)); while (p->srcPos < srcSize) { size_t packSize = destLim - *destSize; res = Lzma2EncInt_EncodeSubblock(p, dest + *destSize, &packSize, NULL); if (res != SZ_OK) break; *destSize += packSize; if (packSize == 0) { res = SZ_ERROR_FAIL; break; } if (MtProgress_Set(&mainEncoder->mtCoder.mtProgress, index, p->srcPos, *destSize) != SZ_OK) { res = SZ_ERROR_PROGRESS; break; } } LzmaEnc_Finish(p->enc); if (res != SZ_OK) return res; } if (finished) { if (*destSize == destLim) return SZ_ERROR_OUTPUT_EOF; dest[(*destSize)++] = 0; } } return res; } #endif /* ---------- Lzma2Enc ---------- */ CLzma2EncHandle Lzma2Enc_Create(ISzAlloc *alloc, ISzAlloc *allocBig) { CLzma2Enc *p = (CLzma2Enc *)alloc->Alloc(alloc, sizeof(CLzma2Enc)); if (p == 0) return NULL; Lzma2EncProps_Init(&p->props); Lzma2EncProps_Normalize(&p->props); p->outBuf = 0; p->alloc = alloc; p->allocBig = allocBig; { unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) p->coders[i].enc = 0; } #ifndef _7ZIP_ST MtCoder_Construct(&p->mtCoder); #endif return p; } void Lzma2Enc_Destroy(CLzma2EncHandle pp) { CLzma2Enc *p = (CLzma2Enc *)pp; unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) { CLzma2EncInt *t = &p->coders[i]; if (t->enc) { LzmaEnc_Destroy(t->enc, p->alloc, p->allocBig); t->enc = 0; } } #ifndef _7ZIP_ST MtCoder_Destruct(&p->mtCoder); #endif IAlloc_Free(p->alloc, p->outBuf); IAlloc_Free(p->alloc, pp); } SRes Lzma2Enc_SetProps(CLzma2EncHandle pp, const CLzma2EncProps *props) { CLzma2Enc *p = (CLzma2Enc *)pp; CLzmaEncProps lzmaProps = props->lzmaProps; LzmaEncProps_Normalize(&lzmaProps); if (lzmaProps.lc + lzmaProps.lp > LZMA2_LCLP_MAX) return SZ_ERROR_PARAM; p->props = *props; Lzma2EncProps_Normalize(&p->props); return SZ_OK; } Byte Lzma2Enc_WriteProperties(CLzma2EncHandle pp) { CLzma2Enc *p = (CLzma2Enc *)pp; unsigned i; UInt32 dicSize = LzmaEncProps_GetDictSize(&p->props.lzmaProps); for (i = 0; i < 40; i++) if (dicSize <= LZMA2_DIC_SIZE_FROM_PROP(i)) break; return (Byte)i; } SRes Lzma2Enc_Encode(CLzma2EncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress) { CLzma2Enc *p = (CLzma2Enc *)pp; int i; for (i = 0; i < p->props.numBlockThreads; i++) { CLzma2EncInt *t = &p->coders[i]; if (t->enc == NULL) { t->enc = LzmaEnc_Create(p->alloc); if (t->enc == NULL) return SZ_ERROR_MEM; } } #ifndef _7ZIP_ST if (p->props.numBlockThreads <= 1) #endif return Lzma2Enc_EncodeMt1(&p->coders[0], p, outStream, inStream, progress); #ifndef _7ZIP_ST { CMtCallbackImp mtCallback; mtCallback.funcTable.Code = MtCallbackImp_Code; mtCallback.lzma2Enc = p; p->mtCoder.progress = progress; p->mtCoder.inStream = inStream; p->mtCoder.outStream = outStream; p->mtCoder.alloc = p->alloc; p->mtCoder.mtCallback = &mtCallback.funcTable; p->mtCoder.blockSize = p->props.blockSize; p->mtCoder.destBlockSize = p->props.blockSize + (p->props.blockSize >> 10) + 16; p->mtCoder.numThreads = p->props.numBlockThreads; return MtCoder_Code(&p->mtCoder); } #endif } src/libs/7zip/win/C/Lzma2Enc.h000066400000000000000000000034431325366651500162520ustar00rootroot00000000000000/* Lzma2Enc.h -- LZMA2 Encoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA2_ENC_H #define __LZMA2_ENC_H #include "LzmaEnc.h" EXTERN_C_BEGIN typedef struct { CLzmaEncProps lzmaProps; size_t blockSize; int numBlockThreads; int numTotalThreads; } CLzma2EncProps; void Lzma2EncProps_Init(CLzma2EncProps *p); void Lzma2EncProps_Normalize(CLzma2EncProps *p); /* ---------- CLzmaEnc2Handle Interface ---------- */ /* Lzma2Enc_* functions can return the following exit codes: Returns: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater in props SZ_ERROR_WRITE - Write callback error SZ_ERROR_PROGRESS - some break from progress callback SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ typedef void * CLzma2EncHandle; CLzma2EncHandle Lzma2Enc_Create(ISzAlloc *alloc, ISzAlloc *allocBig); void Lzma2Enc_Destroy(CLzma2EncHandle p); SRes Lzma2Enc_SetProps(CLzma2EncHandle p, const CLzma2EncProps *props); Byte Lzma2Enc_WriteProperties(CLzma2EncHandle p); SRes Lzma2Enc_Encode(CLzma2EncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress); /* ---------- One Call Interface ---------- */ /* Lzma2Encode Return code: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater SZ_ERROR_OUTPUT_EOF - output buffer overflow SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ /* SRes Lzma2Encode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); */ EXTERN_C_END #endif src/libs/7zip/win/C/LzmaDec.c000066400000000000000000000703151325366651500161530ustar00rootroot00000000000000/* LzmaDec.c -- LZMA Decoder 2015-01-01 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "LzmaDec.h" #include #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_INIT_SIZE 5 #define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); #define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } #define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) #define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } #define TREE_DECODE(probs, limit, i) \ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } /* #define _LZMA_SIZE_OPT */ #ifdef _LZMA_SIZE_OPT #define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) #else #define TREE_6_DECODE(probs, i) \ { i = 1; \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ i -= 0x40; } #endif #define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) #define MATCHED_LITER_DEC \ matchByte <<= 1; \ bit = (matchByte & offs); \ probLit = prob + offs + bit + symbol; \ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0_CHECK range = bound; #define UPDATE_1_CHECK range -= bound; code -= bound; #define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ { UPDATE_0_CHECK; i = (i + i); A0; } else \ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } #define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) #define TREE_DECODE_CHECK(probs, limit, i) \ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } #define kNumPosBitsMax 4 #define kNumPosStatesMax (1 << kNumPosBitsMax) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define LenChoice 0 #define LenChoice2 (LenChoice + 1) #define LenLow (LenChoice2 + 1) #define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) #define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) #define kNumLenProbs (LenHigh + kLenNumHighSymbols) #define kNumStates 12 #define kNumLitStates 7 #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #define kNumPosSlotBits 6 #define kNumLenToPosStates 4 #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kMatchMinLen 2 #define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define IsMatch 0 #define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) #define IsRepG0 (IsRep + kNumStates) #define IsRepG1 (IsRepG0 + kNumStates) #define IsRepG2 (IsRepG1 + kNumStates) #define IsRep0Long (IsRepG2 + kNumStates) #define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) #define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) #define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) #define LenCoder (Align + kAlignTableSize) #define RepLenCoder (LenCoder + kNumLenProbs) #define Literal (RepLenCoder + kNumLenProbs) #define LZMA_BASE_SIZE 1846 #define LZMA_LIT_SIZE 768 #define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) #if Literal != LZMA_BASE_SIZE StopCompilingDueBUG #endif #define LZMA_DIC_MIN (1 << 12) /* First LZMA-symbol is always decoded. And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization Out: Result: SZ_OK - OK SZ_ERROR_DATA - Error p->remainLen: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished = kMatchSpecLenStart + 1 : Flush marker = kMatchSpecLenStart + 2 : State Init Marker */ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { CLzmaProb *probs = p->probs; unsigned state = p->state; UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; unsigned lc = p->prop.lc; Byte *dic = p->dic; SizeT dicBufSize = p->dicBufSize; SizeT dicPos = p->dicPos; UInt32 processedPos = p->processedPos; UInt32 checkDicSize = p->checkDicSize; unsigned len = 0; const Byte *buf = p->buf; UInt32 range = p->range; UInt32 code = p->code; do { CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = processedPos & pbMask; prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { unsigned symbol; UPDATE_0(prob); prob = probs + Literal; if (checkDicSize != 0 || processedPos != 0) prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); if (state < kNumLitStates) { state -= (state < 4) ? state : 3; symbol = 1; #ifdef _LZMA_SIZE_OPT do { NORMAL_LITER_DEC } while (symbol < 0x100); #else NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC NORMAL_LITER_DEC #endif } else { unsigned matchByte = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; unsigned offs = 0x100; state -= (state < 10) ? 3 : 6; symbol = 1; #ifdef _LZMA_SIZE_OPT do { unsigned bit; CLzmaProb *probLit; MATCHED_LITER_DEC } while (symbol < 0x100); #else { unsigned bit; CLzmaProb *probLit; MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC MATCHED_LITER_DEC } #endif } dic[dicPos++] = (Byte)symbol; processedPos++; continue; } else { UPDATE_1(prob); prob = probs + IsRep + state; IF_BIT_0(prob) { UPDATE_0(prob); state += kNumStates; prob = probs + LenCoder; } else { UPDATE_1(prob); if (checkDicSize == 0 && processedPos == 0) return SZ_ERROR_DATA; prob = probs + IsRepG0 + state; IF_BIT_0(prob) { UPDATE_0(prob); prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { UPDATE_0(prob); dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; processedPos++; state = state < kNumLitStates ? 9 : 11; continue; } UPDATE_1(prob); } else { UInt32 distance; UPDATE_1(prob); prob = probs + IsRepG1 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep1; } else { UPDATE_1(prob); prob = probs + IsRepG2 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep2; } else { UPDATE_1(prob); distance = rep3; rep3 = rep2; } rep2 = rep1; } rep1 = rep0; rep0 = distance; } state = state < kNumLitStates ? 8 : 11; prob = probs + RepLenCoder; } { unsigned limit, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = (1 << kLenNumMidBits); } else { UPDATE_1(probLen); probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = (1 << kLenNumHighBits); } } TREE_DECODE(probLen, limit, len); len += offset; } if (state >= kNumStates) { UInt32 distance; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_6_DECODE(prob, distance); if (distance >= kStartPosModelIndex) { unsigned posSlot = (unsigned)distance; int numDirectBits = (int)(((distance >> 1) - 1)); distance = (2 | (distance & 1)); if (posSlot < kEndPosModelIndex) { distance <<= numDirectBits; prob = probs + SpecPos + distance - posSlot - 1; { UInt32 mask = 1; unsigned i = 1; do { GET_BIT2(prob + i, i, ; , distance |= mask); mask <<= 1; } while (--numDirectBits != 0); } } else { numDirectBits -= kNumAlignBits; do { NORMALIZE range >>= 1; { UInt32 t; code -= range; t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ distance = (distance << 1) + (t + 1); code += range & t; } /* distance <<= 1; if (code >= range) { code -= range; distance |= 1; } */ } while (--numDirectBits != 0); prob = probs + Align; distance <<= kNumAlignBits; { unsigned i = 1; GET_BIT2(prob + i, i, ; , distance |= 1); GET_BIT2(prob + i, i, ; , distance |= 2); GET_BIT2(prob + i, i, ; , distance |= 4); GET_BIT2(prob + i, i, ; , distance |= 8); } if (distance == (UInt32)0xFFFFFFFF) { len += kMatchSpecLenStart; state -= kNumStates; break; } } } rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance + 1; if (checkDicSize == 0) { if (distance >= processedPos) return SZ_ERROR_DATA; } else if (distance >= checkDicSize) return SZ_ERROR_DATA; state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; } len += kMatchMinLen; if (limit == dicPos) return SZ_ERROR_DATA; { SizeT rem = limit - dicPos; unsigned curLen = ((rem < len) ? (unsigned)rem : len); SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); processedPos += curLen; len -= curLen; if (pos + curLen <= dicBufSize) { Byte *dest = dic + dicPos; ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; const Byte *lim = dest + curLen; dicPos += curLen; do *(dest) = (Byte)*(dest + src); while (++dest != lim); } else { do { dic[dicPos++] = dic[pos]; if (++pos == dicBufSize) pos = 0; } while (--curLen != 0); } } } } while (dicPos < limit && buf < bufLimit); NORMALIZE; p->buf = buf; p->range = range; p->code = code; p->remainLen = len; p->dicPos = dicPos; p->processedPos = processedPos; p->reps[0] = rep0; p->reps[1] = rep1; p->reps[2] = rep2; p->reps[3] = rep3; p->state = state; return SZ_OK; } static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) { if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) { Byte *dic = p->dic; SizeT dicPos = p->dicPos; SizeT dicBufSize = p->dicBufSize; unsigned len = p->remainLen; UInt32 rep0 = p->reps[0]; if (limit - dicPos < len) len = (unsigned)(limit - dicPos); if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) p->checkDicSize = p->prop.dicSize; p->processedPos += len; p->remainLen -= len; while (len != 0) { len--; dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; } p->dicPos = dicPos; } } static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { do { SizeT limit2 = limit; if (p->checkDicSize == 0) { UInt32 rem = p->prop.dicSize - p->processedPos; if (limit - p->dicPos > rem) limit2 = p->dicPos + rem; } RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); if (p->processedPos >= p->prop.dicSize) p->checkDicSize = p->prop.dicSize; LzmaDec_WriteRem(p, limit); } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); if (p->remainLen > kMatchSpecLenStart) { p->remainLen = kMatchSpecLenStart; } return 0; } typedef enum { DUMMY_ERROR, /* unexpected end of input stream */ DUMMY_LIT, DUMMY_MATCH, DUMMY_REP } ELzmaDummy; static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) { UInt32 range = p->range; UInt32 code = p->code; const Byte *bufLimit = buf + inSize; CLzmaProb *probs = p->probs; unsigned state = p->state; ELzmaDummy res; { CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ prob = probs + Literal; if (p->checkDicSize != 0 || p->processedPos != 0) prob += (LZMA_LIT_SIZE * ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); if (state < kNumLitStates) { unsigned symbol = 1; do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); } else { unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; unsigned offs = 0x100; unsigned symbol = 1; do { unsigned bit; CLzmaProb *probLit; matchByte <<= 1; bit = (matchByte & offs); probLit = prob + offs + bit + symbol; GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) } while (symbol < 0x100); } res = DUMMY_LIT; } else { unsigned len; UPDATE_1_CHECK; prob = probs + IsRep + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; state = 0; prob = probs + LenCoder; res = DUMMY_MATCH; } else { UPDATE_1_CHECK; res = DUMMY_REP; prob = probs + IsRepG0 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; NORMALIZE_CHECK; return DUMMY_REP; } else { UPDATE_1_CHECK; } } else { UPDATE_1_CHECK; prob = probs + IsRepG1 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; prob = probs + IsRepG2 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; } } } state = kNumStates; prob = probs + RepLenCoder; } { unsigned limit, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = 1 << kLenNumLowBits; } else { UPDATE_1_CHECK; probLen = prob + LenChoice2; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = 1 << kLenNumMidBits; } else { UPDATE_1_CHECK; probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = 1 << kLenNumHighBits; } } TREE_DECODE_CHECK(probLen, limit, len); len += offset; } if (state < 4) { unsigned posSlot; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { int numDirectBits = ((posSlot >> 1) - 1); /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ if (posSlot < kEndPosModelIndex) { prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; } else { numDirectBits -= kNumAlignBits; do { NORMALIZE_CHECK range >>= 1; code -= range & (((code - range) >> 31) - 1); /* if (code >= range) code -= range; */ } while (--numDirectBits != 0); prob = probs + Align; numDirectBits = kNumAlignBits; } { unsigned i = 1; do { GET_BIT_CHECK(prob + i, i); } while (--numDirectBits != 0); } } } } } NORMALIZE_CHECK; return res; } static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) { p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); p->range = 0xFFFFFFFF; p->needFlush = 0; } void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) { p->needFlush = 1; p->remainLen = 0; p->tempBufSize = 0; if (initDic) { p->processedPos = 0; p->checkDicSize = 0; p->needInitState = 1; } if (initState) p->needInitState = 1; } void LzmaDec_Init(CLzmaDec *p) { p->dicPos = 0; LzmaDec_InitDicAndState(p, True, True); } static void LzmaDec_InitStateReal(CLzmaDec *p) { UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); UInt32 i; CLzmaProb *probs = p->probs; for (i = 0; i < numProbs; i++) probs[i] = kBitModelTotal >> 1; p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; p->state = 0; p->needInitState = 0; } SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; (*srcLen) = 0; LzmaDec_WriteRem(p, dicLimit); *status = LZMA_STATUS_NOT_SPECIFIED; while (p->remainLen != kMatchSpecLenStart) { int checkEndMarkNow; if (p->needFlush != 0) { for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) p->tempBuf[p->tempBufSize++] = *src++; if (p->tempBufSize < RC_INIT_SIZE) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (p->tempBuf[0] != 0) return SZ_ERROR_DATA; LzmaDec_InitRc(p, p->tempBuf); p->tempBufSize = 0; } checkEndMarkNow = 0; if (p->dicPos >= dicLimit) { if (p->remainLen == 0 && p->code == 0) { *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; return SZ_OK; } if (finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_OK; } if (p->remainLen != 0) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } checkEndMarkNow = 1; } if (p->needInitState) LzmaDec_InitStateReal(p); if (p->tempBufSize == 0) { SizeT processed; const Byte *bufLimit; if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, src, inSize); if (dummyRes == DUMMY_ERROR) { memcpy(p->tempBuf, src, inSize); p->tempBufSize = (unsigned)inSize; (*srcLen) += inSize; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } bufLimit = src; } else bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; p->buf = src; if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) return SZ_ERROR_DATA; processed = (SizeT)(p->buf - src); (*srcLen) += processed; src += processed; inSize -= processed; } else { unsigned rem = p->tempBufSize, lookAhead = 0; while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) p->tempBuf[rem++] = src[lookAhead++]; p->tempBufSize = rem; if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); if (dummyRes == DUMMY_ERROR) { (*srcLen) += lookAhead; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_ERROR_DATA; } } p->buf = p->tempBuf; if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) return SZ_ERROR_DATA; lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); (*srcLen) += lookAhead; src += lookAhead; inSize -= lookAhead; p->tempBufSize = 0; } } if (p->code == 0) *status = LZMA_STATUS_FINISHED_WITH_MARK; return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; } SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen; SizeT inSize = *srcLen; *srcLen = *destLen = 0; for (;;) { SizeT inSizeCur = inSize, outSizeCur, dicPos; ELzmaFinishMode curFinishMode; SRes res; if (p->dicPos == p->dicBufSize) p->dicPos = 0; dicPos = p->dicPos; if (outSize > p->dicBufSize - dicPos) { outSizeCur = p->dicBufSize; curFinishMode = LZMA_FINISH_ANY; } else { outSizeCur = dicPos + outSize; curFinishMode = finishMode; } res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); src += inSizeCur; inSize -= inSizeCur; *srcLen += inSizeCur; outSizeCur = p->dicPos - dicPos; memcpy(dest, p->dic + dicPos, outSizeCur); dest += outSizeCur; outSize -= outSizeCur; *destLen += outSizeCur; if (res != 0) return res; if (outSizeCur == 0 || outSize == 0) return SZ_OK; } } void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->probs); p->probs = 0; } static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->dic); p->dic = 0; } void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) { LzmaDec_FreeProbs(p, alloc); LzmaDec_FreeDict(p, alloc); } SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) { UInt32 dicSize; Byte d; if (size < LZMA_PROPS_SIZE) return SZ_ERROR_UNSUPPORTED; else dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); if (dicSize < LZMA_DIC_MIN) dicSize = LZMA_DIC_MIN; p->dicSize = dicSize; d = data[0]; if (d >= (9 * 5 * 5)) return SZ_ERROR_UNSUPPORTED; p->lc = d % 9; d /= 9; p->pb = d / 5; p->lp = d % 5; return SZ_OK; } static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) { UInt32 numProbs = LzmaProps_GetNumProbs(propNew); if (p->probs == 0 || numProbs != p->numProbs) { LzmaDec_FreeProbs(p, alloc); p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); p->numProbs = numProbs; if (p->probs == 0) return SZ_ERROR_MEM; } return SZ_OK; } SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) { CLzmaProps propNew; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); p->prop = propNew; return SZ_OK; } SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) { CLzmaProps propNew; SizeT dicBufSize; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); dicBufSize = propNew.dicSize; if (p->dic == 0 || dicBufSize != p->dicBufSize) { LzmaDec_FreeDict(p, alloc); p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); if (p->dic == 0) { LzmaDec_FreeProbs(p, alloc); return SZ_ERROR_MEM; } } p->dicBufSize = dicBufSize; p->prop = propNew; return SZ_OK; } SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc) { CLzmaDec p; SRes res; SizeT outSize = *destLen, inSize = *srcLen; *destLen = *srcLen = 0; *status = LZMA_STATUS_NOT_SPECIFIED; if (inSize < RC_INIT_SIZE) return SZ_ERROR_INPUT_EOF; LzmaDec_Construct(&p); RINOK(LzmaDec_AllocateProbs(&p, propData, propSize, alloc)); p.dic = dest; p.dicBufSize = outSize; LzmaDec_Init(&p); *srcLen = inSize; res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); *destLen = p.dicPos; if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) res = SZ_ERROR_INPUT_EOF; LzmaDec_FreeProbs(&p, alloc); return res; } src/libs/7zip/win/C/LzmaDec.h000066400000000000000000000156111325366651500161560ustar00rootroot00000000000000/* LzmaDec.h -- LZMA Decoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA_DEC_H #define __LZMA_DEC_H #include "7zTypes.h" EXTERN_C_BEGIN /* #define _LZMA_PROB32 */ /* _LZMA_PROB32 can increase the speed on some CPUs, but memory usage for CLzmaDec::probs will be doubled in that case */ #ifdef _LZMA_PROB32 #define CLzmaProb UInt32 #else #define CLzmaProb UInt16 #endif /* ---------- LZMA Properties ---------- */ #define LZMA_PROPS_SIZE 5 typedef struct _CLzmaProps { unsigned lc, lp, pb; UInt32 dicSize; } CLzmaProps; /* LzmaProps_Decode - decodes properties Returns: SZ_OK SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); /* ---------- LZMA Decoder state ---------- */ /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ #define LZMA_REQUIRED_INPUT_MAX 20 typedef struct { CLzmaProps prop; CLzmaProb *probs; Byte *dic; const Byte *buf; UInt32 range, code; SizeT dicPos; SizeT dicBufSize; UInt32 processedPos; UInt32 checkDicSize; unsigned state; UInt32 reps[4]; unsigned remainLen; int needFlush; int needInitState; UInt32 numProbs; unsigned tempBufSize; Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; } CLzmaDec; #define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } void LzmaDec_Init(CLzmaDec *p); /* There are two types of LZMA streams: 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ typedef enum { LZMA_FINISH_ANY, /* finish at any point */ LZMA_FINISH_END /* block must be finished at the end */ } ELzmaFinishMode; /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! You must use LZMA_FINISH_END, when you know that current output buffer covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, and output value of destLen will be less than output buffer size limit. You can check status result also. You can use multiple checks to test data integrity after full decompression: 1) Check Result and "status" variable. 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. You must use correct finish mode in that case. */ typedef enum { LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ } ELzmaStatus; /* ELzmaStatus is used only as output value for function call */ /* ---------- Interfaces ---------- */ /* There are 3 levels of interfaces: 1) Dictionary Interface 2) Buffer Interface 3) One Call Interface You can select any of these interfaces, but don't mix functions from different groups for same object. */ /* There are two variants to allocate state for Dictionary Interface: 1) LzmaDec_Allocate / LzmaDec_Free 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs You can use variant 2, if you set dictionary buffer manually. For Buffer Interface you must always use variant 1. LzmaDec_Allocate* can return: SZ_OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties */ SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); /* ---------- Dictionary Interface ---------- */ /* You can use it, if you want to eliminate the overhead for data copying from dictionary to some other external buffer. You must work with CLzmaDec variables directly in this interface. STEPS: LzmaDec_Constr() LzmaDec_Allocate() for (each new stream) { LzmaDec_Init() while (it needs more decompression) { LzmaDec_DecodeToDic() use data from CLzmaDec::dic and update CLzmaDec::dicPos } } LzmaDec_Free() */ /* LzmaDec_DecodeToDic The decoding to internal dictionary buffer (CLzmaDec::dic). You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! finishMode: It has meaning only if the decoding reaches output limit (dicLimit). LZMA_FINISH_ANY - Decode just dicLimit bytes. LZMA_FINISH_END - Stream must be finished after dicLimit. Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_NEEDS_MORE_INPUT LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error */ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- Buffer Interface ---------- */ /* It's zlib-like interface. See LzmaDec_DecodeToDic description for information about STEPS and return results, but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need to work with CLzmaDec variables manually. finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). */ SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); /* ---------- One Call Interface ---------- */ /* LzmaDecode finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). Returns: SZ_OK status: LZMA_STATUS_FINISHED_WITH_MARK LZMA_STATUS_NOT_FINISHED LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK SZ_ERROR_DATA - Data error SZ_ERROR_MEM - Memory allocation error SZ_ERROR_UNSUPPORTED - Unsupported properties SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). */ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); EXTERN_C_END #endif src/libs/7zip/win/C/LzmaEnc.c000066400000000000000000001767161325366651500162010ustar00rootroot00000000000000/* LzmaEnc.c -- LZMA Encoder 2014-12-29 : Igor Pavlov : Public domain */ #include "Precomp.h" #include /* #define SHOW_STAT */ /* #define SHOW_STAT2 */ #if defined(SHOW_STAT) || defined(SHOW_STAT2) #include #endif #include "LzmaEnc.h" #include "LzFind.h" #ifndef _7ZIP_ST #include "LzFindMt.h" #endif #ifdef SHOW_STAT static unsigned g_STAT_OFFSET = 0; #endif #define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) #define kBlockSize (9 << 10) #define kUnpackBlockSize (1 << 18) #define kMatchArraySize (1 << 21) #define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) #define kNumMaxDirectBits (31) #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define kProbInitValue (kBitModelTotal >> 1) #define kNumMoveReducingBits 4 #define kNumBitPriceShiftBits 4 #define kBitPrice (1 << kNumBitPriceShiftBits) void LzmaEncProps_Init(CLzmaEncProps *p) { p->level = 5; p->dictSize = p->mc = 0; p->reduceSize = (UInt64)(Int64)-1; p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; p->writeEndMark = 0; } void LzmaEncProps_Normalize(CLzmaEncProps *p) { int level = p->level; if (level < 0) level = 5; p->level = level; if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); if (p->dictSize > p->reduceSize) { unsigned i; for (i = 11; i <= 30; i++) { if ((UInt32)p->reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } if ((UInt32)p->reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } } } if (p->lc < 0) p->lc = 3; if (p->lp < 0) p->lp = 0; if (p->pb < 0) p->pb = 2; if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); if (p->numHashBytes < 0) p->numHashBytes = 4; if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); if (p->numThreads < 0) p->numThreads = #ifndef _7ZIP_ST ((p->btMode && p->algo) ? 2 : 1); #else 1; #endif } UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) { CLzmaEncProps props = *props2; LzmaEncProps_Normalize(&props); return props.dictSize; } /* #define LZMA_LOG_BSR */ /* Define it for Intel's CPU */ #ifdef LZMA_LOG_BSR #define kDicLogSizeMaxCompress 30 #define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } UInt32 GetPosSlot1(UInt32 pos) { UInt32 res; BSR2_RET(pos, res); return res; } #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } #else #define kNumLogBits (9 + (int)sizeof(size_t) / 2) #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) void LzmaEnc_FastPosInit(Byte *g_FastPos) { int c = 2, slotFast; g_FastPos[0] = 0; g_FastPos[1] = 1; for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) { UInt32 k = (1 << ((slotFast >> 1) - 1)); UInt32 j; for (j = 0; j < k; j++, c++) g_FastPos[c] = (Byte)slotFast; } } #define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ res = p->g_FastPos[pos >> i] + (i * 2); } /* #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ p->g_FastPos[pos >> 6] + 12 : \ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } */ #define GetPosSlot1(pos) p->g_FastPos[pos] #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } #endif #define LZMA_NUM_REPS 4 typedef unsigned CState; typedef struct { UInt32 price; CState state; int prev1IsChar; int prev2; UInt32 posPrev2; UInt32 backPrev2; UInt32 posPrev; UInt32 backPrev; UInt32 backs[LZMA_NUM_REPS]; } COptimal; #define kNumOpts (1 << 12) #define kNumLenToPosStates 4 #define kNumPosSlotBits 6 #define kDicLogSizeMin 0 #define kDicLogSizeMax 32 #define kDistTableSizeMax (kDicLogSizeMax * 2) #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kAlignMask (kAlignTableSize - 1) #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #ifdef _LZMA_PROB32 #define CLzmaProb UInt32 #else #define CLzmaProb UInt16 #endif #define LZMA_PB_MAX 4 #define LZMA_LC_MAX 8 #define LZMA_LP_MAX 4 #define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define LZMA_MATCH_LEN_MIN 2 #define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) #define kNumStates 12 typedef struct { CLzmaProb choice; CLzmaProb choice2; CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; CLzmaProb high[kLenNumHighSymbols]; } CLenEnc; typedef struct { CLenEnc p; UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; UInt32 tableSize; UInt32 counters[LZMA_NUM_PB_STATES_MAX]; } CLenPriceEnc; typedef struct { UInt32 range; Byte cache; UInt64 low; UInt64 cacheSize; Byte *buf; Byte *bufLim; Byte *bufBase; ISeqOutStream *outStream; UInt64 processed; SRes res; } CRangeEnc; typedef struct { CLzmaProb *litProbs; CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; UInt32 reps[LZMA_NUM_REPS]; UInt32 state; } CSaveState; typedef struct { IMatchFinder matchFinder; void *matchFinderObj; #ifndef _7ZIP_ST Bool mtMode; CMatchFinderMt matchFinderMt; #endif CMatchFinder matchFinderBase; #ifndef _7ZIP_ST Byte pad[128]; #endif UInt32 optimumEndIndex; UInt32 optimumCurrentIndex; UInt32 longestMatchLength; UInt32 numPairs; UInt32 numAvail; COptimal opt[kNumOpts]; #ifndef LZMA_LOG_BSR Byte g_FastPos[1 << kNumLogBits]; #endif UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; UInt32 numFastBytes; UInt32 additionalOffset; UInt32 reps[LZMA_NUM_REPS]; UInt32 state; UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; UInt32 alignPrices[kAlignTableSize]; UInt32 alignPriceCount; UInt32 distTableSize; unsigned lc, lp, pb; unsigned lpMask, pbMask; CLzmaProb *litProbs; CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; unsigned lclp; Bool fastMode; CRangeEnc rc; Bool writeEndMark; UInt64 nowPos64; UInt32 matchPriceCount; Bool finished; Bool multiThread; SRes result; UInt32 dictSize; int needInit; CSaveState saveState; } CLzmaEnc; void LzmaEnc_SaveState(CLzmaEncHandle pp) { CLzmaEnc *p = (CLzmaEnc *)pp; CSaveState *dest = &p->saveState; int i; dest->lenEnc = p->lenEnc; dest->repLenEnc = p->repLenEnc; dest->state = p->state; for (i = 0; i < kNumStates; i++) { memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); } for (i = 0; i < kNumLenToPosStates; i++) memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); } void LzmaEnc_RestoreState(CLzmaEncHandle pp) { CLzmaEnc *dest = (CLzmaEnc *)pp; const CSaveState *p = &dest->saveState; int i; dest->lenEnc = p->lenEnc; dest->repLenEnc = p->repLenEnc; dest->state = p->state; for (i = 0; i < kNumStates; i++) { memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); } for (i = 0; i < kNumLenToPosStates; i++) memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); } SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) { CLzmaEnc *p = (CLzmaEnc *)pp; CLzmaEncProps props = *props2; LzmaEncProps_Normalize(&props); if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || props.dictSize > ((UInt32)1 << kDicLogSizeMaxCompress) || props.dictSize > ((UInt32)1 << 30)) return SZ_ERROR_PARAM; p->dictSize = props.dictSize; { unsigned fb = props.fb; if (fb < 5) fb = 5; if (fb > LZMA_MATCH_LEN_MAX) fb = LZMA_MATCH_LEN_MAX; p->numFastBytes = fb; } p->lc = props.lc; p->lp = props.lp; p->pb = props.pb; p->fastMode = (props.algo == 0); p->matchFinderBase.btMode = props.btMode; { UInt32 numHashBytes = 4; if (props.btMode) { if (props.numHashBytes < 2) numHashBytes = 2; else if (props.numHashBytes < 4) numHashBytes = props.numHashBytes; } p->matchFinderBase.numHashBytes = numHashBytes; } p->matchFinderBase.cutValue = props.mc; p->writeEndMark = props.writeEndMark; #ifndef _7ZIP_ST /* if (newMultiThread != _multiThread) { ReleaseMatchFinder(); _multiThread = newMultiThread; } */ p->multiThread = (props.numThreads > 1); #endif return SZ_OK; } static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; #define IsCharState(s) ((s) < 7) #define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) #define kInfinityPrice (1 << 30) static void RangeEnc_Construct(CRangeEnc *p) { p->outStream = 0; p->bufBase = 0; } #define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) #define RC_BUF_SIZE (1 << 16) static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) { if (p->bufBase == 0) { p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); if (p->bufBase == 0) return 0; p->bufLim = p->bufBase + RC_BUF_SIZE; } return 1; } static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->bufBase); p->bufBase = 0; } static void RangeEnc_Init(CRangeEnc *p) { /* Stream.Init(); */ p->low = 0; p->range = 0xFFFFFFFF; p->cacheSize = 1; p->cache = 0; p->buf = p->bufBase; p->processed = 0; p->res = SZ_OK; } static void RangeEnc_FlushStream(CRangeEnc *p) { size_t num; if (p->res != SZ_OK) return; num = p->buf - p->bufBase; if (num != p->outStream->Write(p->outStream, p->bufBase, num)) p->res = SZ_ERROR_WRITE; p->processed += num; p->buf = p->bufBase; } static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) { if ((UInt32)p->low < (UInt32)0xFF000000 || (unsigned)(p->low >> 32) != 0) { Byte temp = p->cache; do { Byte *buf = p->buf; *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); p->buf = buf; if (buf == p->bufLim) RangeEnc_FlushStream(p); temp = 0xFF; } while (--p->cacheSize != 0); p->cache = (Byte)((UInt32)p->low >> 24); } p->cacheSize++; p->low = (UInt32)p->low << 8; } static void RangeEnc_FlushData(CRangeEnc *p) { int i; for (i = 0; i < 5; i++) RangeEnc_ShiftLow(p); } static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, unsigned numBits) { do { p->range >>= 1; p->low += p->range & (0 - ((value >> --numBits) & 1)); if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } while (numBits != 0); } static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) { UInt32 ttt = *prob; UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; if (symbol == 0) { p->range = newBound; ttt += (kBitModelTotal - ttt) >> kNumMoveBits; } else { p->low += newBound; p->range -= newBound; ttt -= ttt >> kNumMoveBits; } *prob = (CLzmaProb)ttt; if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) { symbol |= 0x100; do { RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); } static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) { UInt32 offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); } void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) { UInt32 i; for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) { const int kCyclesBits = kNumBitPriceShiftBits; UInt32 w = i; UInt32 bitCount = 0; int j; for (j = 0; j < kCyclesBits; j++) { w = w * w; bitCount <<= 1; while (w >= ((UInt32)1 << 16)) { w >>= 1; bitCount++; } } ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); } } #define GET_PRICE(prob, symbol) \ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICEa(prob, symbol) \ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] #define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; symbol |= 0x100; do { price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); return price; } static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) { UInt32 price = 0; UInt32 offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); return price; } static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) { UInt32 m = 1; int i; for (i = numBitLevels; i != 0;) { UInt32 bit; i--; bit = (symbol >> i) & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; } } static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) { UInt32 m = 1; int i; for (i = 0; i < numBitLevels; i++) { UInt32 bit = symbol & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; symbol >>= 1; } } static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; symbol |= (1 << numBitLevels); while (symbol != 1) { price += GET_PRICEa(probs[symbol >> 1], symbol & 1); symbol >>= 1; } return price; } static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) { UInt32 price = 0; UInt32 m = 1; int i; for (i = numBitLevels; i != 0; i--) { UInt32 bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[m], bit); m = (m << 1) | bit; } return price; } static void LenEnc_Init(CLenEnc *p) { unsigned i; p->choice = p->choice2 = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) p->low[i] = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) p->mid[i] = kProbInitValue; for (i = 0; i < kLenNumHighSymbols; i++) p->high[i] = kProbInitValue; } static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) { if (symbol < kLenNumLowSymbols) { RangeEnc_EncodeBit(rc, &p->choice, 0); RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); } else { RangeEnc_EncodeBit(rc, &p->choice, 1); if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) { RangeEnc_EncodeBit(rc, &p->choice2, 0); RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); } else { RangeEnc_EncodeBit(rc, &p->choice2, 1); RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); } } } static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) { UInt32 a0 = GET_PRICE_0a(p->choice); UInt32 a1 = GET_PRICE_1a(p->choice); UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); UInt32 i = 0; for (i = 0; i < kLenNumLowSymbols; i++) { if (i >= numSymbols) return; prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); } for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) { if (i >= numSymbols) return; prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); } for (; i < numSymbols; i++) prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); } static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) { LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); p->counters[posState] = p->tableSize; } static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) { UInt32 posState; for (posState = 0; posState < numPosStates; posState++) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) { LenEnc_Encode(&p->p, rc, symbol, posState); if (updatePrice) if (--p->counters[posState] == 0) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void MovePos(CLzmaEnc *p, UInt32 num) { #ifdef SHOW_STAT g_STAT_OFFSET += num; printf("\n MovePos %d", num); #endif if (num != 0) { p->additionalOffset += num; p->matchFinder.Skip(p->matchFinderObj, num); } } static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) { UInt32 lenRes = 0, numPairs; p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); #ifdef SHOW_STAT printf("\n i = %d numPairs = %d ", g_STAT_OFFSET, numPairs / 2); g_STAT_OFFSET++; { UInt32 i; for (i = 0; i < numPairs; i += 2) printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); } #endif if (numPairs > 0) { lenRes = p->matches[numPairs - 2]; if (lenRes == p->numFastBytes) { const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; UInt32 distance = p->matches[numPairs - 1] + 1; UInt32 numAvail = p->numAvail; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; { const Byte *pby2 = pby - distance; for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); } } } p->additionalOffset++; *numDistancePairsRes = numPairs; return lenRes; } #define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; #define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; #define IsShortRep(p) ((p)->backPrev == 0) static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) { return GET_PRICE_0(p->isRepG0[state]) + GET_PRICE_0(p->isRep0Long[state][posState]); } static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) { UInt32 price; if (repIndex == 0) { price = GET_PRICE_0(p->isRepG0[state]); price += GET_PRICE_1(p->isRep0Long[state][posState]); } else { price = GET_PRICE_1(p->isRepG0[state]); if (repIndex == 1) price += GET_PRICE_0(p->isRepG1[state]); else { price += GET_PRICE_1(p->isRepG1[state]); price += GET_PRICE(p->isRepG2[state], repIndex - 2); } } return price; } static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) { return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + GetPureRepPrice(p, repIndex, state, posState); } static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) { UInt32 posMem = p->opt[cur].posPrev; UInt32 backMem = p->opt[cur].backPrev; p->optimumEndIndex = cur; do { if (p->opt[cur].prev1IsChar) { MakeAsChar(&p->opt[posMem]) p->opt[posMem].posPrev = posMem - 1; if (p->opt[cur].prev2) { p->opt[posMem - 1].prev1IsChar = False; p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; } } { UInt32 posPrev = posMem; UInt32 backCur = backMem; backMem = p->opt[posPrev].backPrev; posMem = p->opt[posPrev].posPrev; p->opt[posPrev].backPrev = backCur; p->opt[posPrev].posPrev = cur; cur = posPrev; } } while (cur != 0); *backRes = p->opt[0].backPrev; p->optimumCurrentIndex = p->opt[0].posPrev; return p->optimumCurrentIndex; } #define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) { UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; UInt32 matchPrice, repMatchPrice, normalMatchPrice; UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; UInt32 *matches; const Byte *data; Byte curByte, matchByte; if (p->optimumEndIndex != p->optimumCurrentIndex) { const COptimal *opt = &p->opt[p->optimumCurrentIndex]; UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; *backRes = opt->backPrev; p->optimumCurrentIndex = opt->posPrev; return lenRes; } p->optimumCurrentIndex = p->optimumEndIndex = 0; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; if (numAvail < 2) { *backRes = (UInt32)(-1); return 1; } if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; repMaxIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 lenTest; const Byte *data2; reps[i] = p->reps[i]; data2 = data - (reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) { repLens[i] = 0; continue; } for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); repLens[i] = lenTest; if (lenTest > repLens[repMaxIndex]) repMaxIndex = i; } if (repLens[repMaxIndex] >= p->numFastBytes) { UInt32 lenRes; *backRes = repMaxIndex; lenRes = repLens[repMaxIndex]; MovePos(p, lenRes - 1); return lenRes; } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } curByte = *data; matchByte = *(data - (reps[0] + 1)); if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) { *backRes = (UInt32)-1; return 1; } p->opt[0].state = (CState)p->state; posState = (position & p->pbMask); { const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + (!IsCharState(p->state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } MakeAsChar(&p->opt[1]); matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); if (matchByte == curByte) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); if (shortRepPrice < p->opt[1].price) { p->opt[1].price = shortRepPrice; MakeAsShortRep(&p->opt[1]); } } lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); if (lenEnd < 2) { *backRes = p->opt[1].backPrev; return 1; } p->opt[1].posPrev = 0; for (i = 0; i < LZMA_NUM_REPS; i++) p->opt[0].backs[i] = reps[i]; len = lenEnd; do p->opt[len--].price = kInfinityPrice; while (len >= 2); for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 repLen = repLens[i]; UInt32 price; if (repLen < 2) continue; price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); do { UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; COptimal *opt = &p->opt[repLen]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = i; opt->prev1IsChar = False; } } while (--repLen >= 2); } normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); if (len <= mainLen) { UInt32 offs = 0; while (len > matches[offs]) offs += 2; for (; ; len++) { COptimal *opt; UInt32 distance = matches[offs + 1]; UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; UInt32 lenToPosState = GetLenToPosState(len); if (distance < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][distance]; else { UInt32 slot; GetPosSlot2(distance, slot); curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; } opt = &p->opt[len]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = distance + LZMA_NUM_REPS; opt->prev1IsChar = False; } if (len == matches[offs]) { offs += 2; if (offs == numPairs) break; } } } cur = 0; #ifdef SHOW_STAT2 if (position >= 0) { unsigned i; printf("\n pos = %4X", position); for (i = cur; i <= lenEnd; i++) printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); } #endif for (;;) { UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; Bool nextIsChar; Byte curByte, matchByte; const Byte *data; COptimal *curOpt; COptimal *nextOpt; cur++; if (cur == lenEnd) return Backward(p, backRes, cur); newLen = ReadMatchDistances(p, &numPairs); if (newLen >= p->numFastBytes) { p->numPairs = numPairs; p->longestMatchLength = newLen; return Backward(p, backRes, cur); } position++; curOpt = &p->opt[cur]; posPrev = curOpt->posPrev; if (curOpt->prev1IsChar) { posPrev--; if (curOpt->prev2) { state = p->opt[curOpt->posPrev2].state; if (curOpt->backPrev2 < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } else state = p->opt[posPrev].state; state = kLiteralNextStates[state]; } else state = p->opt[posPrev].state; if (posPrev == cur - 1) { if (IsShortRep(curOpt)) state = kShortRepNextStates[state]; else state = kLiteralNextStates[state]; } else { UInt32 pos; const COptimal *prevOpt; if (curOpt->prev1IsChar && curOpt->prev2) { posPrev = curOpt->posPrev2; pos = curOpt->backPrev2; state = kRepNextStates[state]; } else { pos = curOpt->backPrev; if (pos < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } prevOpt = &p->opt[posPrev]; if (pos < LZMA_NUM_REPS) { UInt32 i; reps[0] = prevOpt->backs[pos]; for (i = 1; i <= pos; i++) reps[i] = prevOpt->backs[i - 1]; for (; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i]; } else { UInt32 i; reps[0] = (pos - LZMA_NUM_REPS); for (i = 1; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i - 1]; } } curOpt->state = (CState)state; curOpt->backs[0] = reps[0]; curOpt->backs[1] = reps[1]; curOpt->backs[2] = reps[2]; curOpt->backs[3] = reps[3]; curPrice = curOpt->price; nextIsChar = False; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; curByte = *data; matchByte = *(data - (reps[0] + 1)); posState = (position & p->pbMask); curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); { const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); curAnd1Price += (!IsCharState(state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } nextOpt = &p->opt[cur + 1]; if (curAnd1Price < nextOpt->price) { nextOpt->price = curAnd1Price; nextOpt->posPrev = cur; MakeAsChar(nextOpt); nextIsChar = True; } matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); if (shortRepPrice <= nextOpt->price) { nextOpt->price = shortRepPrice; nextOpt->posPrev = cur; MakeAsShortRep(nextOpt); nextIsChar = True; } } numAvailFull = p->numAvail; { UInt32 temp = kNumOpts - 1 - cur; if (temp < numAvailFull) numAvailFull = temp; } if (numAvailFull < 2) continue; numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); if (!nextIsChar && matchByte != curByte) /* speed optimization */ { /* try Literal + rep0 */ UInt32 temp; UInt32 lenTest2; const Byte *data2 = data - (reps[0] + 1); UInt32 limit = p->numFastBytes + 1; if (limit > numAvailFull) limit = numAvailFull; for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); lenTest2 = temp - 1; if (lenTest2 >= 2) { UInt32 state2 = kLiteralNextStates[state]; UInt32 posStateNext = (position + 1) & p->pbMask; UInt32 nextRepMatchPrice = curAnd1Price + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 curAndLenPrice; COptimal *opt; UInt32 offset = cur + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = False; } } } } startLen = 2; /* speed optimization */ { UInt32 repIndex; for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) { UInt32 lenTest; UInt32 lenTestTemp; UInt32 price; const Byte *data2 = data - (reps[repIndex] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); while (lenEnd < cur + lenTest) p->opt[++lenEnd].price = kInfinityPrice; lenTestTemp = lenTest; price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); do { UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; COptimal *opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = repIndex; opt->prev1IsChar = False; } } while (--lenTest >= 2); lenTest = lenTestTemp; if (repIndex == 0) startLen = lenTest + 1; /* if (_maxMode) */ { UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { UInt32 state2 = kRepNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = price + p->repLenEnc.prices[posState][lenTest - 2] + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (position + lenTest + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 curAndLenPrice; COptimal *opt; UInt32 offset = cur + lenTest + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = True; opt->posPrev2 = cur; opt->backPrev2 = repIndex; } } } } } } /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ if (newLen > numAvail) { newLen = numAvail; for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); matches[numPairs] = newLen; numPairs += 2; } if (newLen >= startLen) { UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); UInt32 offs, curBack, posSlot; UInt32 lenTest; while (lenEnd < cur + newLen) p->opt[++lenEnd].price = kInfinityPrice; offs = 0; while (startLen > matches[offs]) offs += 2; curBack = matches[offs + 1]; GetPosSlot2(curBack, posSlot); for (lenTest = /*2*/ startLen; ; lenTest++) { UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; UInt32 lenToPosState = GetLenToPosState(lenTest); COptimal *opt; if (curBack < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; else curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = curBack + LZMA_NUM_REPS; opt->prev1IsChar = False; } if (/*_maxMode && */lenTest == matches[offs]) { /* Try Match + Literal + Rep0 */ const Byte *data2 = data - (curBack + 1); UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { UInt32 state2 = kMatchNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = curAndLenPrice + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (posStateNext + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { UInt32 offset = cur + lenTest + 1 + lenTest2; UInt32 curAndLenPrice; COptimal *opt; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = True; opt->prev2 = True; opt->posPrev2 = cur; opt->backPrev2 = curBack + LZMA_NUM_REPS; } } } offs += 2; if (offs == numPairs) break; curBack = matches[offs + 1]; if (curBack >= kNumFullDistances) GetPosSlot2(curBack, posSlot); } } } } } #define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) { UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; const Byte *data; const UInt32 *matches; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; *backRes = (UInt32)-1; if (numAvail < 2) return 1; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; repLen = repIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len; const Byte *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (len = 2; len < numAvail && data[len] == data2[len]; len++); if (len >= p->numFastBytes) { *backRes = i; MovePos(p, len - 1); return len; } if (len > repLen) { repIndex = i; repLen = len; } } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } mainDist = 0; /* for GCC */ if (mainLen >= 2) { mainDist = matches[numPairs - 1]; while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) { if (!ChangePair(matches[numPairs - 3], mainDist)) break; numPairs -= 2; mainLen = matches[numPairs - 2]; mainDist = matches[numPairs - 1]; } if (mainLen == 2 && mainDist >= 0x80) mainLen = 1; } if (repLen >= 2 && ( (repLen + 1 >= mainLen) || (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) { *backRes = repIndex; MovePos(p, repLen - 1); return repLen; } if (mainLen < 2 || numAvail <= 2) return 1; p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); if (p->longestMatchLength >= 2) { UInt32 newDistance = matches[p->numPairs - 1]; if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || (p->longestMatchLength > mainLen + 1) || (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) return 1; } data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len, limit; const Byte *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; limit = mainLen - 1; for (len = 2; len < limit && data[len] == data2[len]; len++); if (len >= limit) return 1; } *backRes = mainDist + LZMA_NUM_REPS; MovePos(p, mainLen - 2); return mainLen; } static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) { UInt32 len; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; len = LZMA_MATCH_LEN_MIN; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); } static SRes CheckErrors(CLzmaEnc *p) { if (p->result != SZ_OK) return p->result; if (p->rc.res != SZ_OK) p->result = SZ_ERROR_WRITE; if (p->matchFinderBase.result != SZ_OK) p->result = SZ_ERROR_READ; if (p->result != SZ_OK) p->finished = True; return p->result; } static SRes Flush(CLzmaEnc *p, UInt32 nowPos) { /* ReleaseMFStream(); */ p->finished = True; if (p->writeEndMark) WriteEndMarker(p, nowPos & p->pbMask); RangeEnc_FlushData(&p->rc); RangeEnc_FlushStream(&p->rc); return CheckErrors(p); } static void FillAlignPrices(CLzmaEnc *p) { UInt32 i; for (i = 0; i < kAlignTableSize; i++) p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); p->alignPriceCount = 0; } static void FillDistancesPrices(CLzmaEnc *p) { UInt32 tempPrices[kNumFullDistances]; UInt32 i, lenToPosState; for (i = kStartPosModelIndex; i < kNumFullDistances; i++) { UInt32 posSlot = GetPosSlot1(i); UInt32 footerBits = ((posSlot >> 1) - 1); UInt32 base = ((2 | (posSlot & 1)) << footerBits); tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); } for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) { UInt32 posSlot; const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; for (posSlot = 0; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); { UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; UInt32 i; for (i = 0; i < kStartPosModelIndex; i++) distancesPrices[i] = posSlotPrices[i]; for (; i < kNumFullDistances; i++) distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; } } p->matchPriceCount = 0; } void LzmaEnc_Construct(CLzmaEnc *p) { RangeEnc_Construct(&p->rc); MatchFinder_Construct(&p->matchFinderBase); #ifndef _7ZIP_ST MatchFinderMt_Construct(&p->matchFinderMt); p->matchFinderMt.MatchFinder = &p->matchFinderBase; #endif { CLzmaEncProps props; LzmaEncProps_Init(&props); LzmaEnc_SetProps(p, &props); } #ifndef LZMA_LOG_BSR LzmaEnc_FastPosInit(p->g_FastPos); #endif LzmaEnc_InitPriceTables(p->ProbPrices); p->litProbs = 0; p->saveState.litProbs = 0; } CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) { void *p; p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); if (p != 0) LzmaEnc_Construct((CLzmaEnc *)p); return p; } void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->litProbs); alloc->Free(alloc, p->saveState.litProbs); p->litProbs = 0; p->saveState.litProbs = 0; } void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) { #ifndef _7ZIP_ST MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); #endif MatchFinder_Free(&p->matchFinderBase, allocBig); LzmaEnc_FreeLits(p, alloc); RangeEnc_Free(&p->rc, alloc); } void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) { LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); alloc->Free(alloc, p); } static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) { UInt32 nowPos32, startPos32; if (p->needInit) { p->matchFinder.Init(p->matchFinderObj); p->needInit = 0; } if (p->finished) return p->result; RINOK(CheckErrors(p)); nowPos32 = (UInt32)p->nowPos64; startPos32 = nowPos32; if (p->nowPos64 == 0) { UInt32 numPairs; Byte curByte; if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) return Flush(p, nowPos32); ReadMatchDistances(p, &numPairs); RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); p->state = kLiteralNextStates[p->state]; curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); LitEnc_Encode(&p->rc, p->litProbs, curByte); p->additionalOffset--; nowPos32++; } if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) for (;;) { UInt32 pos, len, posState; if (p->fastMode) len = GetOptimumFast(p, &pos); else len = GetOptimum(p, nowPos32, &pos); #ifdef SHOW_STAT2 printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); #endif posState = nowPos32 & p->pbMask; if (len == 1 && pos == (UInt32)-1) { Byte curByte; CLzmaProb *probs; const Byte *data; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; curByte = *data; probs = LIT_PROBS(nowPos32, *(data - 1)); if (IsCharState(p->state)) LitEnc_Encode(&p->rc, probs, curByte); else LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); p->state = kLiteralNextStates[p->state]; } else { RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); if (pos < LZMA_NUM_REPS) { RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); if (pos == 0) { RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); } else { UInt32 distance = p->reps[pos]; RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); if (pos == 1) RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); else { RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); if (pos == 3) p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; } p->reps[1] = p->reps[0]; p->reps[0] = distance; } if (len == 1) p->state = kShortRepNextStates[p->state]; else { LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); p->state = kRepNextStates[p->state]; } } else { UInt32 posSlot; RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); pos -= LZMA_NUM_REPS; GetPosSlot(pos, posSlot); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { UInt32 footerBits = ((posSlot >> 1) - 1); UInt32 base = ((2 | (posSlot & 1)) << footerBits); UInt32 posReduced = pos - base; if (posSlot < kEndPosModelIndex) RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); else { RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); p->alignPriceCount++; } } p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; p->reps[1] = p->reps[0]; p->reps[0] = pos; p->matchPriceCount++; } } p->additionalOffset -= len; nowPos32 += len; if (p->additionalOffset == 0) { UInt32 processed; if (!p->fastMode) { if (p->matchPriceCount >= (1 << 7)) FillDistancesPrices(p); if (p->alignPriceCount >= kAlignTableSize) FillAlignPrices(p); } if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) break; processed = nowPos32 - startPos32; if (useLimits) { if (processed + kNumOpts + 300 >= maxUnpackSize || RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) break; } else if (processed >= (1 << 15)) { p->nowPos64 += nowPos32 - startPos32; return CheckErrors(p); } } } p->nowPos64 += nowPos32 - startPos32; return Flush(p, nowPos32); } #define kBigHashDicLimit ((UInt32)1 << 24) static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 beforeSize = kNumOpts; if (!RangeEnc_Alloc(&p->rc, alloc)) return SZ_ERROR_MEM; #ifndef _7ZIP_ST p->mtMode = (p->multiThread && !p->fastMode && (p->matchFinderBase.btMode != 0)); #endif { unsigned lclp = p->lc + p->lp; if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) { LzmaEnc_FreeLits(p, alloc); p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); if (p->litProbs == 0 || p->saveState.litProbs == 0) { LzmaEnc_FreeLits(p, alloc); return SZ_ERROR_MEM; } p->lclp = lclp; } } p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); if (beforeSize + p->dictSize < keepWindowSize) beforeSize = keepWindowSize - p->dictSize; #ifndef _7ZIP_ST if (p->mtMode) { RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); p->matchFinderObj = &p->matchFinderMt; MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); } else #endif { if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) return SZ_ERROR_MEM; p->matchFinderObj = &p->matchFinderBase; MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); } return SZ_OK; } void LzmaEnc_Init(CLzmaEnc *p) { UInt32 i; p->state = 0; for (i = 0 ; i < LZMA_NUM_REPS; i++) p->reps[i] = 0; RangeEnc_Init(&p->rc); for (i = 0; i < kNumStates; i++) { UInt32 j; for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) { p->isMatch[i][j] = kProbInitValue; p->isRep0Long[i][j] = kProbInitValue; } p->isRep[i] = kProbInitValue; p->isRepG0[i] = kProbInitValue; p->isRepG1[i] = kProbInitValue; p->isRepG2[i] = kProbInitValue; } { UInt32 num = 0x300 << (p->lp + p->lc); for (i = 0; i < num; i++) p->litProbs[i] = kProbInitValue; } { for (i = 0; i < kNumLenToPosStates; i++) { CLzmaProb *probs = p->posSlotEncoder[i]; UInt32 j; for (j = 0; j < (1 << kNumPosSlotBits); j++) probs[j] = kProbInitValue; } } { for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) p->posEncoders[i] = kProbInitValue; } LenEnc_Init(&p->lenEnc.p); LenEnc_Init(&p->repLenEnc.p); for (i = 0; i < (1 << kNumAlignBits); i++) p->posAlignEncoder[i] = kProbInitValue; p->optimumEndIndex = 0; p->optimumCurrentIndex = 0; p->additionalOffset = 0; p->pbMask = (1 << p->pb) - 1; p->lpMask = (1 << p->lp) - 1; } void LzmaEnc_InitPrices(CLzmaEnc *p) { if (!p->fastMode) { FillDistancesPrices(p); FillAlignPrices(p); } p->lenEnc.tableSize = p->repLenEnc.tableSize = p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); } static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 i; for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) if (p->dictSize <= ((UInt32)1 << i)) break; p->distTableSize = i * 2; p->finished = False; p->result = SZ_OK; RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); LzmaEnc_Init(p); LzmaEnc_InitPrices(p); p->nowPos64 = 0; return SZ_OK; } static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; p->needInit = 1; p->rc.outStream = outStream; return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); } SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; p->needInit = 1; return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) { p->matchFinderBase.directInput = 1; p->matchFinderBase.bufferBase = (Byte *)src; p->matchFinderBase.directInputRem = srcLen; } SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; LzmaEnc_SetInputBuf(p, src, srcLen); p->needInit = 1; return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } void LzmaEnc_Finish(CLzmaEncHandle pp) { #ifndef _7ZIP_ST CLzmaEnc *p = (CLzmaEnc *)pp; if (p->mtMode) MatchFinderMt_ReleaseStream(&p->matchFinderMt); #else pp = pp; #endif } typedef struct { ISeqOutStream funcTable; Byte *data; SizeT rem; Bool overflow; } CSeqOutStreamBuf; static size_t MyWrite(void *pp, const void *data, size_t size) { CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; if (p->rem < size) { size = p->rem; p->overflow = True; } memcpy(p->data, data, size); p->rem -= size; p->data += size; return size; } UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); } const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; } SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) { CLzmaEnc *p = (CLzmaEnc *)pp; UInt64 nowPos64; SRes res; CSeqOutStreamBuf outStream; outStream.funcTable.Write = MyWrite; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = False; p->finished = False; p->result = SZ_OK; if (reInit) LzmaEnc_Init(p); LzmaEnc_InitPrices(p); nowPos64 = p->nowPos64; RangeEnc_Init(&p->rc); p->rc.outStream = &outStream.funcTable; res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); *unpackSize = (UInt32)(p->nowPos64 - nowPos64); *destLen -= outStream.rem; if (outStream.overflow) return SZ_ERROR_OUTPUT_EOF; return res; } static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) { SRes res = SZ_OK; #ifndef _7ZIP_ST Byte allocaDummy[0x300]; allocaDummy[0] = 0; allocaDummy[1] = allocaDummy[0]; #endif for (;;) { res = LzmaEnc_CodeOneBlock(p, False, 0, 0); if (res != SZ_OK || p->finished != 0) break; if (progress != 0) { res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); if (res != SZ_OK) { res = SZ_ERROR_PROGRESS; break; } } } LzmaEnc_Finish(p); return res; } SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); } SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) { CLzmaEnc *p = (CLzmaEnc *)pp; int i; UInt32 dictSize = p->dictSize; if (*size < LZMA_PROPS_SIZE) return SZ_ERROR_PARAM; *size = LZMA_PROPS_SIZE; props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); for (i = 11; i <= 30; i++) { if (dictSize <= ((UInt32)2 << i)) { dictSize = (2 << i); break; } if (dictSize <= ((UInt32)3 << i)) { dictSize = (3 << i); break; } } for (i = 0; i < 4; i++) props[1 + i] = (Byte)(dictSize >> (8 * i)); return SZ_OK; } SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { SRes res; CLzmaEnc *p = (CLzmaEnc *)pp; CSeqOutStreamBuf outStream; LzmaEnc_SetInputBuf(p, src, srcLen); outStream.funcTable.Write = MyWrite; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = writeEndMark; p->rc.outStream = &outStream.funcTable; res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); if (res == SZ_OK) res = LzmaEnc_Encode2(p, progress); *destLen -= outStream.rem; if (outStream.overflow) return SZ_ERROR_OUTPUT_EOF; return res; } SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); SRes res; if (p == 0) return SZ_ERROR_MEM; res = LzmaEnc_SetProps(p, props); if (res == SZ_OK) { res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); if (res == SZ_OK) res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, writeEndMark, progress, alloc, allocBig); } LzmaEnc_Destroy(p, alloc, allocBig); return res; } src/libs/7zip/win/C/LzmaEnc.h000066400000000000000000000060551325366651500161720ustar00rootroot00000000000000/* LzmaEnc.h -- LZMA Encoder 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA_ENC_H #define __LZMA_ENC_H #include "7zTypes.h" EXTERN_C_BEGIN #define LZMA_PROPS_SIZE 5 typedef struct _CLzmaEncProps { int level; /* 0 <= level <= 9 */ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version (1 << 12) <= dictSize <= (1 << 30) for 64-bit version default = (1 << 24) */ UInt64 reduceSize; /* estimated size of data that will be compressed. default = 0xFFFFFFFF. Encoder uses this value to reduce dictionary size */ int lc; /* 0 <= lc <= 8, default = 3 */ int lp; /* 0 <= lp <= 4, default = 0 */ int pb; /* 0 <= pb <= 4, default = 2 */ int algo; /* 0 - fast, 1 - normal, default = 1 */ int fb; /* 5 <= fb <= 273, default = 32 */ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ int numHashBytes; /* 2, 3 or 4, default = 4 */ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ int numThreads; /* 1 or 2, default = 2 */ } CLzmaEncProps; void LzmaEncProps_Init(CLzmaEncProps *p); void LzmaEncProps_Normalize(CLzmaEncProps *p); UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); /* ---------- CLzmaEncHandle Interface ---------- */ /* LzmaEnc_* functions can return the following exit codes: Returns: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater in props SZ_ERROR_WRITE - Write callback error. SZ_ERROR_PROGRESS - some break from progress callback SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ typedef void * CLzmaEncHandle; CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); /* ---------- One Call Interface ---------- */ /* LzmaEncode Return code: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater SZ_ERROR_OUTPUT_EOF - output buffer overflow SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) */ SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); EXTERN_C_END #endif src/libs/7zip/win/C/MtCoder.c000066400000000000000000000202161325366651500161640ustar00rootroot00000000000000/* MtCoder.c -- Multi-thread Coder 2010-09-24 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "MtCoder.h" void LoopThread_Construct(CLoopThread *p) { Thread_Construct(&p->thread); Event_Construct(&p->startEvent); Event_Construct(&p->finishedEvent); } void LoopThread_Close(CLoopThread *p) { Thread_Close(&p->thread); Event_Close(&p->startEvent); Event_Close(&p->finishedEvent); } static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE LoopThreadFunc(void *pp) { CLoopThread *p = (CLoopThread *)pp; for (;;) { if (Event_Wait(&p->startEvent) != 0) return SZ_ERROR_THREAD; if (p->stop) return 0; p->res = p->func(p->param); if (Event_Set(&p->finishedEvent) != 0) return SZ_ERROR_THREAD; } } WRes LoopThread_Create(CLoopThread *p) { p->stop = 0; RINOK(AutoResetEvent_CreateNotSignaled(&p->startEvent)); RINOK(AutoResetEvent_CreateNotSignaled(&p->finishedEvent)); return Thread_Create(&p->thread, LoopThreadFunc, p); } WRes LoopThread_StopAndWait(CLoopThread *p) { p->stop = 1; if (Event_Set(&p->startEvent) != 0) return SZ_ERROR_THREAD; return Thread_Wait(&p->thread); } WRes LoopThread_StartSubThread(CLoopThread *p) { return Event_Set(&p->startEvent); } WRes LoopThread_WaitSubThread(CLoopThread *p) { return Event_Wait(&p->finishedEvent); } static SRes Progress(ICompressProgress *p, UInt64 inSize, UInt64 outSize) { return (p && p->Progress(p, inSize, outSize) != SZ_OK) ? SZ_ERROR_PROGRESS : SZ_OK; } static void MtProgress_Init(CMtProgress *p, ICompressProgress *progress) { unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) p->inSizes[i] = p->outSizes[i] = 0; p->totalInSize = p->totalOutSize = 0; p->progress = progress; p->res = SZ_OK; } static void MtProgress_Reinit(CMtProgress *p, unsigned index) { p->inSizes[index] = 0; p->outSizes[index] = 0; } #define UPDATE_PROGRESS(size, prev, total) \ if (size != (UInt64)(Int64)-1) { total += size - prev; prev = size; } SRes MtProgress_Set(CMtProgress *p, unsigned index, UInt64 inSize, UInt64 outSize) { SRes res; CriticalSection_Enter(&p->cs); UPDATE_PROGRESS(inSize, p->inSizes[index], p->totalInSize) UPDATE_PROGRESS(outSize, p->outSizes[index], p->totalOutSize) if (p->res == SZ_OK) p->res = Progress(p->progress, p->totalInSize, p->totalOutSize); res = p->res; CriticalSection_Leave(&p->cs); return res; } static void MtProgress_SetError(CMtProgress *p, SRes res) { CriticalSection_Enter(&p->cs); if (p->res == SZ_OK) p->res = res; CriticalSection_Leave(&p->cs); } static void MtCoder_SetError(CMtCoder* p, SRes res) { CriticalSection_Enter(&p->cs); if (p->res == SZ_OK) p->res = res; CriticalSection_Leave(&p->cs); } /* ---------- MtThread ---------- */ void CMtThread_Construct(CMtThread *p, CMtCoder *mtCoder) { p->mtCoder = mtCoder; p->outBuf = 0; p->inBuf = 0; Event_Construct(&p->canRead); Event_Construct(&p->canWrite); LoopThread_Construct(&p->thread); } #define RINOK_THREAD(x) { if((x) != 0) return SZ_ERROR_THREAD; } static void CMtThread_CloseEvents(CMtThread *p) { Event_Close(&p->canRead); Event_Close(&p->canWrite); } static void CMtThread_Destruct(CMtThread *p) { CMtThread_CloseEvents(p); if (Thread_WasCreated(&p->thread.thread)) { LoopThread_StopAndWait(&p->thread); LoopThread_Close(&p->thread); } if (p->mtCoder->alloc) IAlloc_Free(p->mtCoder->alloc, p->outBuf); p->outBuf = 0; if (p->mtCoder->alloc) IAlloc_Free(p->mtCoder->alloc, p->inBuf); p->inBuf = 0; } #define MY_BUF_ALLOC(buf, size, newSize) \ if (buf == 0 || size != newSize) \ { IAlloc_Free(p->mtCoder->alloc, buf); \ size = newSize; buf = (Byte *)IAlloc_Alloc(p->mtCoder->alloc, size); \ if (buf == 0) return SZ_ERROR_MEM; } static SRes CMtThread_Prepare(CMtThread *p) { MY_BUF_ALLOC(p->inBuf, p->inBufSize, p->mtCoder->blockSize) MY_BUF_ALLOC(p->outBuf, p->outBufSize, p->mtCoder->destBlockSize) p->stopReading = False; p->stopWriting = False; RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canRead)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canWrite)); return SZ_OK; } static SRes FullRead(ISeqInStream *stream, Byte *data, size_t *processedSize) { size_t size = *processedSize; *processedSize = 0; while (size != 0) { size_t curSize = size; SRes res = stream->Read(stream, data, &curSize); *processedSize += curSize; data += curSize; size -= curSize; RINOK(res); if (curSize == 0) return SZ_OK; } return SZ_OK; } #define GET_NEXT_THREAD(p) &p->mtCoder->threads[p->index == p->mtCoder->numThreads - 1 ? 0 : p->index + 1] static SRes MtThread_Process(CMtThread *p, Bool *stop) { CMtThread *next; *stop = True; if (Event_Wait(&p->canRead) != 0) return SZ_ERROR_THREAD; next = GET_NEXT_THREAD(p); if (p->stopReading) { next->stopReading = True; return Event_Set(&next->canRead) == 0 ? SZ_OK : SZ_ERROR_THREAD; } { size_t size = p->mtCoder->blockSize; size_t destSize = p->outBufSize; RINOK(FullRead(p->mtCoder->inStream, p->inBuf, &size)); next->stopReading = *stop = (size != p->mtCoder->blockSize); if (Event_Set(&next->canRead) != 0) return SZ_ERROR_THREAD; RINOK(p->mtCoder->mtCallback->Code(p->mtCoder->mtCallback, p->index, p->outBuf, &destSize, p->inBuf, size, *stop)); MtProgress_Reinit(&p->mtCoder->mtProgress, p->index); if (Event_Wait(&p->canWrite) != 0) return SZ_ERROR_THREAD; if (p->stopWriting) return SZ_ERROR_FAIL; if (p->mtCoder->outStream->Write(p->mtCoder->outStream, p->outBuf, destSize) != destSize) return SZ_ERROR_WRITE; return Event_Set(&next->canWrite) == 0 ? SZ_OK : SZ_ERROR_THREAD; } } static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE ThreadFunc(void *pp) { CMtThread *p = (CMtThread *)pp; for (;;) { Bool stop; CMtThread *next = GET_NEXT_THREAD(p); SRes res = MtThread_Process(p, &stop); if (res != SZ_OK) { MtCoder_SetError(p->mtCoder, res); MtProgress_SetError(&p->mtCoder->mtProgress, res); next->stopReading = True; next->stopWriting = True; Event_Set(&next->canRead); Event_Set(&next->canWrite); return res; } if (stop) return 0; } } void MtCoder_Construct(CMtCoder* p) { unsigned i; p->alloc = 0; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) { CMtThread *t = &p->threads[i]; t->index = i; CMtThread_Construct(t, p); } CriticalSection_Init(&p->cs); CriticalSection_Init(&p->mtProgress.cs); } void MtCoder_Destruct(CMtCoder* p) { unsigned i; for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++) CMtThread_Destruct(&p->threads[i]); CriticalSection_Delete(&p->cs); CriticalSection_Delete(&p->mtProgress.cs); } SRes MtCoder_Code(CMtCoder *p) { unsigned i, numThreads = p->numThreads; SRes res = SZ_OK; p->res = SZ_OK; MtProgress_Init(&p->mtProgress, p->progress); for (i = 0; i < numThreads; i++) { RINOK(CMtThread_Prepare(&p->threads[i])); } for (i = 0; i < numThreads; i++) { CMtThread *t = &p->threads[i]; CLoopThread *lt = &t->thread; if (!Thread_WasCreated(<->thread)) { lt->func = ThreadFunc; lt->param = t; if (LoopThread_Create(lt) != SZ_OK) { res = SZ_ERROR_THREAD; break; } } } if (res == SZ_OK) { unsigned j; for (i = 0; i < numThreads; i++) { CMtThread *t = &p->threads[i]; if (LoopThread_StartSubThread(&t->thread) != SZ_OK) { res = SZ_ERROR_THREAD; p->threads[0].stopReading = True; break; } } Event_Set(&p->threads[0].canWrite); Event_Set(&p->threads[0].canRead); for (j = 0; j < i; j++) LoopThread_WaitSubThread(&p->threads[j].thread); } for (i = 0; i < numThreads; i++) CMtThread_CloseEvents(&p->threads[i]); return (res == SZ_OK) ? p->res : res; } src/libs/7zip/win/C/MtCoder.h000066400000000000000000000040241325366651500161700ustar00rootroot00000000000000/* MtCoder.h -- Multi-thread Coder 2009-11-19 : Igor Pavlov : Public domain */ #ifndef __MT_CODER_H #define __MT_CODER_H #include "Threads.h" EXTERN_C_BEGIN typedef struct { CThread thread; CAutoResetEvent startEvent; CAutoResetEvent finishedEvent; int stop; THREAD_FUNC_TYPE func; LPVOID param; THREAD_FUNC_RET_TYPE res; } CLoopThread; void LoopThread_Construct(CLoopThread *p); void LoopThread_Close(CLoopThread *p); WRes LoopThread_Create(CLoopThread *p); WRes LoopThread_StopAndWait(CLoopThread *p); WRes LoopThread_StartSubThread(CLoopThread *p); WRes LoopThread_WaitSubThread(CLoopThread *p); #ifndef _7ZIP_ST #define NUM_MT_CODER_THREADS_MAX 32 #else #define NUM_MT_CODER_THREADS_MAX 1 #endif typedef struct { UInt64 totalInSize; UInt64 totalOutSize; ICompressProgress *progress; SRes res; CCriticalSection cs; UInt64 inSizes[NUM_MT_CODER_THREADS_MAX]; UInt64 outSizes[NUM_MT_CODER_THREADS_MAX]; } CMtProgress; SRes MtProgress_Set(CMtProgress *p, unsigned index, UInt64 inSize, UInt64 outSize); struct _CMtCoder; typedef struct { struct _CMtCoder *mtCoder; Byte *outBuf; size_t outBufSize; Byte *inBuf; size_t inBufSize; unsigned index; CLoopThread thread; Bool stopReading; Bool stopWriting; CAutoResetEvent canRead; CAutoResetEvent canWrite; } CMtThread; typedef struct { SRes (*Code)(void *p, unsigned index, Byte *dest, size_t *destSize, const Byte *src, size_t srcSize, int finished); } IMtCoderCallback; typedef struct _CMtCoder { size_t blockSize; size_t destBlockSize; unsigned numThreads; ISeqInStream *inStream; ISeqOutStream *outStream; ICompressProgress *progress; ISzAlloc *alloc; IMtCoderCallback *mtCallback; CCriticalSection cs; SRes res; CMtProgress mtProgress; CMtThread threads[NUM_MT_CODER_THREADS_MAX]; } CMtCoder; void MtCoder_Construct(CMtCoder* p); void MtCoder_Destruct(CMtCoder* p); SRes MtCoder_Code(CMtCoder *p); EXTERN_C_END #endif src/libs/7zip/win/C/Precomp.h000066400000000000000000000002661325366651500162440ustar00rootroot00000000000000/* Precomp.h -- StdAfx 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_PRECOMP_H #define __7Z_PRECOMP_H #include "Compiler.h" /* #include "7zTypes.h" */ #endif src/libs/7zip/win/C/RotateDefs.h000066400000000000000000000007731325366651500167020ustar00rootroot00000000000000/* RotateDefs.h -- Rotate functions 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __ROTATE_DEFS_H #define __ROTATE_DEFS_H #ifdef _MSC_VER #include // #if (_MSC_VER >= 1200) #pragma intrinsic(_rotl) #pragma intrinsic(_rotr) // #endif #define rotlFixed(x, n) _rotl((x), (n)) #define rotrFixed(x, n) _rotr((x), (n)) #else #define rotlFixed(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) #define rotrFixed(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) #endif #endif src/libs/7zip/win/C/Sha256.c000066400000000000000000000121501325366651500155750ustar00rootroot00000000000000/* Crypto/Sha256.c -- SHA-256 Hash 2010-06-11 : Igor Pavlov : Public domain This code is based on public domain code from Wei Dai's Crypto++ library. */ #include "Precomp.h" #include "RotateDefs.h" #include "Sha256.h" /* define it for speed optimization */ /* #define _SHA256_UNROLL */ /* #define _SHA256_UNROLL2 */ void Sha256_Init(CSha256 *p) { p->state[0] = 0x6a09e667; p->state[1] = 0xbb67ae85; p->state[2] = 0x3c6ef372; p->state[3] = 0xa54ff53a; p->state[4] = 0x510e527f; p->state[5] = 0x9b05688c; p->state[6] = 0x1f83d9ab; p->state[7] = 0x5be0cd19; p->count = 0; } #define S0(x) (rotrFixed(x, 2) ^ rotrFixed(x,13) ^ rotrFixed(x, 22)) #define S1(x) (rotrFixed(x, 6) ^ rotrFixed(x,11) ^ rotrFixed(x, 25)) #define s0(x) (rotrFixed(x, 7) ^ rotrFixed(x,18) ^ (x >> 3)) #define s1(x) (rotrFixed(x,17) ^ rotrFixed(x,19) ^ (x >> 10)) #define blk0(i) (W[i] = data[i]) #define blk2(i) (W[i&15] += s1(W[(i-2)&15]) + W[(i-7)&15] + s0(W[(i-15)&15])) #define Ch(x,y,z) (z^(x&(y^z))) #define Maj(x,y,z) ((x&y)|(z&(x|y))) #define a(i) T[(0-(i))&7] #define b(i) T[(1-(i))&7] #define c(i) T[(2-(i))&7] #define d(i) T[(3-(i))&7] #define e(i) T[(4-(i))&7] #define f(i) T[(5-(i))&7] #define g(i) T[(6-(i))&7] #define h(i) T[(7-(i))&7] #ifdef _SHA256_UNROLL2 #define R(a,b,c,d,e,f,g,h, i) h += S1(e) + Ch(e,f,g) + K[i+j] + (j?blk2(i):blk0(i));\ d += h; h += S0(a) + Maj(a, b, c) #define RX_8(i) \ R(a,b,c,d,e,f,g,h, i); \ R(h,a,b,c,d,e,f,g, i+1); \ R(g,h,a,b,c,d,e,f, i+2); \ R(f,g,h,a,b,c,d,e, i+3); \ R(e,f,g,h,a,b,c,d, i+4); \ R(d,e,f,g,h,a,b,c, i+5); \ R(c,d,e,f,g,h,a,b, i+6); \ R(b,c,d,e,f,g,h,a, i+7) #else #define R(i) h(i) += S1(e(i)) + Ch(e(i),f(i),g(i)) + K[i+j] + (j?blk2(i):blk0(i));\ d(i) += h(i); h(i) += S0(a(i)) + Maj(a(i), b(i), c(i)) #ifdef _SHA256_UNROLL #define RX_8(i) R(i+0); R(i+1); R(i+2); R(i+3); R(i+4); R(i+5); R(i+6); R(i+7); #endif #endif static const UInt32 K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; static void Sha256_Transform(UInt32 *state, const UInt32 *data) { UInt32 W[16]; unsigned j; #ifdef _SHA256_UNROLL2 UInt32 a,b,c,d,e,f,g,h; a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; f = state[5]; g = state[6]; h = state[7]; #else UInt32 T[8]; for (j = 0; j < 8; j++) T[j] = state[j]; #endif for (j = 0; j < 64; j += 16) { #if defined(_SHA256_UNROLL) || defined(_SHA256_UNROLL2) RX_8(0); RX_8(8); #else unsigned i; for (i = 0; i < 16; i++) { R(i); } #endif } #ifdef _SHA256_UNROLL2 state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; state[5] += f; state[6] += g; state[7] += h; #else for (j = 0; j < 8; j++) state[j] += T[j]; #endif /* Wipe variables */ /* memset(W, 0, sizeof(W)); */ /* memset(T, 0, sizeof(T)); */ } #undef S0 #undef S1 #undef s0 #undef s1 static void Sha256_WriteByteBlock(CSha256 *p) { UInt32 data32[16]; unsigned i; for (i = 0; i < 16; i++) data32[i] = ((UInt32)(p->buffer[i * 4 ]) << 24) + ((UInt32)(p->buffer[i * 4 + 1]) << 16) + ((UInt32)(p->buffer[i * 4 + 2]) << 8) + ((UInt32)(p->buffer[i * 4 + 3])); Sha256_Transform(p->state, data32); } void Sha256_Update(CSha256 *p, const Byte *data, size_t size) { UInt32 curBufferPos = (UInt32)p->count & 0x3F; while (size > 0) { p->buffer[curBufferPos++] = *data++; p->count++; size--; if (curBufferPos == 64) { curBufferPos = 0; Sha256_WriteByteBlock(p); } } } void Sha256_Final(CSha256 *p, Byte *digest) { UInt64 lenInBits = (p->count << 3); UInt32 curBufferPos = (UInt32)p->count & 0x3F; unsigned i; p->buffer[curBufferPos++] = 0x80; while (curBufferPos != (64 - 8)) { curBufferPos &= 0x3F; if (curBufferPos == 0) Sha256_WriteByteBlock(p); p->buffer[curBufferPos++] = 0; } for (i = 0; i < 8; i++) { p->buffer[curBufferPos++] = (Byte)(lenInBits >> 56); lenInBits <<= 8; } Sha256_WriteByteBlock(p); for (i = 0; i < 8; i++) { *digest++ = (Byte)(p->state[i] >> 24); *digest++ = (Byte)(p->state[i] >> 16); *digest++ = (Byte)(p->state[i] >> 8); *digest++ = (Byte)(p->state[i]); } Sha256_Init(p); } src/libs/7zip/win/C/Sha256.h000066400000000000000000000007201325366651500156020ustar00rootroot00000000000000/* Sha256.h -- SHA-256 Hash 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __CRYPTO_SHA256_H #define __CRYPTO_SHA256_H #include "7zTypes.h" EXTERN_C_BEGIN #define SHA256_DIGEST_SIZE 32 typedef struct { UInt32 state[8]; UInt64 count; Byte buffer[64]; } CSha256; void Sha256_Init(CSha256 *p); void Sha256_Update(CSha256 *p, const Byte *data, size_t size); void Sha256_Final(CSha256 *p, Byte *digest); EXTERN_C_END #endif src/libs/7zip/win/C/Threads.c000066400000000000000000000053101325366651500162170ustar00rootroot00000000000000/* Threads.c -- multithreading library 2013-11-12 : Igor Pavlov : Public domain */ #include "Precomp.h" #ifndef _WIN32_WCE #include #endif #include "Threads.h" static WRes GetError() { DWORD res = GetLastError(); return (res) ? (WRes)(res) : 1; } WRes HandleToWRes(HANDLE h) { return (h != 0) ? 0 : GetError(); } WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); } WRes HandlePtr_Close(HANDLE *p) { if (*p != NULL) if (!CloseHandle(*p)) return GetError(); *p = NULL; return 0; } WRes Handle_WaitObject(HANDLE h) { return (WRes)WaitForSingleObject(h, INFINITE); } WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param) { /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */ #ifdef UNDER_CE DWORD threadId; *p = CreateThread(0, 0, func, param, 0, &threadId); #else unsigned threadId; *p = (HANDLE)_beginthreadex(NULL, 0, func, param, 0, &threadId); #endif /* maybe we must use errno here, but probably GetLastError() is also OK. */ return HandleToWRes(*p); } WRes Event_Create(CEvent *p, BOOL manualReset, int signaled) { *p = CreateEvent(NULL, manualReset, (signaled ? TRUE : FALSE), NULL); return HandleToWRes(*p); } WRes Event_Set(CEvent *p) { return BOOLToWRes(SetEvent(*p)); } WRes Event_Reset(CEvent *p) { return BOOLToWRes(ResetEvent(*p)); } WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled) { return Event_Create(p, TRUE, signaled); } WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled) { return Event_Create(p, FALSE, signaled); } WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) { return ManualResetEvent_Create(p, 0); } WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) { return AutoResetEvent_Create(p, 0); } WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount) { *p = CreateSemaphore(NULL, (LONG)initCount, (LONG)maxCount, NULL); return HandleToWRes(*p); } static WRes Semaphore_Release(CSemaphore *p, LONG releaseCount, LONG *previousCount) { return BOOLToWRes(ReleaseSemaphore(*p, releaseCount, previousCount)); } WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num) { return Semaphore_Release(p, (LONG)num, NULL); } WRes Semaphore_Release1(CSemaphore *p) { return Semaphore_ReleaseN(p, 1); } WRes CriticalSection_Init(CCriticalSection *p) { /* InitializeCriticalSection can raise only STATUS_NO_MEMORY exception */ #ifdef _MSC_VER __try #endif { InitializeCriticalSection(p); /* InitializeCriticalSectionAndSpinCount(p, 0); */ } #ifdef _MSC_VER __except (EXCEPTION_EXECUTE_HANDLER) { return 1; } #endif return 0; } src/libs/7zip/win/C/Threads.h000066400000000000000000000040271325366651500162300ustar00rootroot00000000000000/* Threads.h -- multithreading library 2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_THREADS_H #define __7Z_THREADS_H #ifdef _WIN32 #include #endif #include "7zTypes.h" EXTERN_C_BEGIN WRes HandlePtr_Close(HANDLE *h); WRes Handle_WaitObject(HANDLE h); typedef HANDLE CThread; #define Thread_Construct(p) *(p) = NULL #define Thread_WasCreated(p) (*(p) != NULL) #define Thread_Close(p) HandlePtr_Close(p) #define Thread_Wait(p) Handle_WaitObject(*(p)) typedef #ifdef UNDER_CE DWORD #else unsigned #endif THREAD_FUNC_RET_TYPE; #define THREAD_FUNC_CALL_TYPE MY_STD_CALL #define THREAD_FUNC_DECL THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE typedef THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE * THREAD_FUNC_TYPE)(void *); WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param); typedef HANDLE CEvent; typedef CEvent CAutoResetEvent; typedef CEvent CManualResetEvent; #define Event_Construct(p) *(p) = NULL #define Event_IsCreated(p) (*(p) != NULL) #define Event_Close(p) HandlePtr_Close(p) #define Event_Wait(p) Handle_WaitObject(*(p)) WRes Event_Set(CEvent *p); WRes Event_Reset(CEvent *p); WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled); WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p); WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled); WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p); typedef HANDLE CSemaphore; #define Semaphore_Construct(p) (*p) = NULL #define Semaphore_Close(p) HandlePtr_Close(p) #define Semaphore_Wait(p) Handle_WaitObject(*(p)) WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount); WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num); WRes Semaphore_Release1(CSemaphore *p); typedef CRITICAL_SECTION CCriticalSection; WRes CriticalSection_Init(CCriticalSection *p); #define CriticalSection_Delete(p) DeleteCriticalSection(p) #define CriticalSection_Enter(p) EnterCriticalSection(p) #define CriticalSection_Leave(p) LeaveCriticalSection(p) EXTERN_C_END #endif src/libs/7zip/win/C/Xz.c000066400000000000000000000037101325366651500152300ustar00rootroot00000000000000/* Xz.c - Xz 2009-04-15 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "7zCrc.h" #include "CpuArch.h" #include "Xz.h" #include "XzCrc64.h" Byte XZ_SIG[XZ_SIG_SIZE] = { 0xFD, '7', 'z', 'X', 'Z', 0 }; Byte XZ_FOOTER_SIG[XZ_FOOTER_SIG_SIZE] = { 'Y', 'Z' }; unsigned Xz_WriteVarInt(Byte *buf, UInt64 v) { unsigned i = 0; do { buf[i++] = (Byte)((v & 0x7F) | 0x80); v >>= 7; } while (v != 0); buf[i - 1] &= 0x7F; return i; } void Xz_Construct(CXzStream *p) { p->numBlocks = p->numBlocksAllocated = 0; p->blocks = 0; p->flags = 0; } void Xz_Free(CXzStream *p, ISzAlloc *alloc) { alloc->Free(alloc, p->blocks); p->numBlocks = p->numBlocksAllocated = 0; p->blocks = 0; } unsigned XzFlags_GetCheckSize(CXzStreamFlags f) { int t = XzFlags_GetCheckType(f); return (t == 0) ? 0 : (4 << ((t - 1) / 3)); } void XzCheck_Init(CXzCheck *p, int mode) { p->mode = mode; switch (mode) { case XZ_CHECK_CRC32: p->crc = CRC_INIT_VAL; break; case XZ_CHECK_CRC64: p->crc64 = CRC64_INIT_VAL; break; case XZ_CHECK_SHA256: Sha256_Init(&p->sha); break; } } void XzCheck_Update(CXzCheck *p, const void *data, size_t size) { switch (p->mode) { case XZ_CHECK_CRC32: p->crc = CrcUpdate(p->crc, data, size); break; case XZ_CHECK_CRC64: p->crc64 = Crc64Update(p->crc64, data, size); break; case XZ_CHECK_SHA256: Sha256_Update(&p->sha, (const Byte *)data, size); break; } } int XzCheck_Final(CXzCheck *p, Byte *digest) { switch (p->mode) { case XZ_CHECK_CRC32: SetUi32(digest, CRC_GET_DIGEST(p->crc)); break; case XZ_CHECK_CRC64: { int i; UInt64 v = CRC64_GET_DIGEST(p->crc64); for (i = 0; i < 8; i++, v >>= 8) digest[i] = (Byte)(v & 0xFF); break; } case XZ_CHECK_SHA256: Sha256_Final(&p->sha, digest); break; default: return 0; } return 1; } src/libs/7zip/win/C/Xz.h000066400000000000000000000167561325366651500152530ustar00rootroot00000000000000/* Xz.h - Xz interface 2014-12-30 : Igor Pavlov : Public domain */ #ifndef __XZ_H #define __XZ_H #include "Sha256.h" EXTERN_C_BEGIN #define XZ_ID_Subblock 1 #define XZ_ID_Delta 3 #define XZ_ID_X86 4 #define XZ_ID_PPC 5 #define XZ_ID_IA64 6 #define XZ_ID_ARM 7 #define XZ_ID_ARMT 8 #define XZ_ID_SPARC 9 #define XZ_ID_LZMA2 0x21 unsigned Xz_ReadVarInt(const Byte *p, size_t maxSize, UInt64 *value); unsigned Xz_WriteVarInt(Byte *buf, UInt64 v); /* ---------- xz block ---------- */ #define XZ_BLOCK_HEADER_SIZE_MAX 1024 #define XZ_NUM_FILTERS_MAX 4 #define XZ_BF_NUM_FILTERS_MASK 3 #define XZ_BF_PACK_SIZE (1 << 6) #define XZ_BF_UNPACK_SIZE (1 << 7) #define XZ_FILTER_PROPS_SIZE_MAX 20 typedef struct { UInt64 id; UInt32 propsSize; Byte props[XZ_FILTER_PROPS_SIZE_MAX]; } CXzFilter; typedef struct { UInt64 packSize; UInt64 unpackSize; Byte flags; CXzFilter filters[XZ_NUM_FILTERS_MAX]; } CXzBlock; #define XzBlock_GetNumFilters(p) (((p)->flags & XZ_BF_NUM_FILTERS_MASK) + 1) #define XzBlock_HasPackSize(p) (((p)->flags & XZ_BF_PACK_SIZE) != 0) #define XzBlock_HasUnpackSize(p) (((p)->flags & XZ_BF_UNPACK_SIZE) != 0) SRes XzBlock_Parse(CXzBlock *p, const Byte *header); SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStream *inStream, Bool *isIndex, UInt32 *headerSizeRes); /* ---------- xz stream ---------- */ #define XZ_SIG_SIZE 6 #define XZ_FOOTER_SIG_SIZE 2 extern Byte XZ_SIG[XZ_SIG_SIZE]; extern Byte XZ_FOOTER_SIG[XZ_FOOTER_SIG_SIZE]; #define XZ_STREAM_FLAGS_SIZE 2 #define XZ_STREAM_CRC_SIZE 4 #define XZ_STREAM_HEADER_SIZE (XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE + XZ_STREAM_CRC_SIZE) #define XZ_STREAM_FOOTER_SIZE (XZ_FOOTER_SIG_SIZE + XZ_STREAM_FLAGS_SIZE + XZ_STREAM_CRC_SIZE + 4) #define XZ_CHECK_MASK 0xF #define XZ_CHECK_NO 0 #define XZ_CHECK_CRC32 1 #define XZ_CHECK_CRC64 4 #define XZ_CHECK_SHA256 10 typedef struct { int mode; UInt32 crc; UInt64 crc64; CSha256 sha; } CXzCheck; void XzCheck_Init(CXzCheck *p, int mode); void XzCheck_Update(CXzCheck *p, const void *data, size_t size); int XzCheck_Final(CXzCheck *p, Byte *digest); typedef UInt16 CXzStreamFlags; #define XzFlags_IsSupported(f) ((f) <= XZ_CHECK_MASK) #define XzFlags_GetCheckType(f) ((f) & XZ_CHECK_MASK) #define XzFlags_HasDataCrc32(f) (Xz_GetCheckType(f) == XZ_CHECK_CRC32) unsigned XzFlags_GetCheckSize(CXzStreamFlags f); SRes Xz_ParseHeader(CXzStreamFlags *p, const Byte *buf); SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStream *inStream); typedef struct { UInt64 unpackSize; UInt64 totalSize; } CXzBlockSizes; typedef struct { CXzStreamFlags flags; size_t numBlocks; size_t numBlocksAllocated; CXzBlockSizes *blocks; UInt64 startOffset; } CXzStream; void Xz_Construct(CXzStream *p); void Xz_Free(CXzStream *p, ISzAlloc *alloc); #define XZ_SIZE_OVERFLOW ((UInt64)(Int64)-1) UInt64 Xz_GetUnpackSize(const CXzStream *p); UInt64 Xz_GetPackSize(const CXzStream *p); typedef struct { size_t num; size_t numAllocated; CXzStream *streams; } CXzs; void Xzs_Construct(CXzs *p); void Xzs_Free(CXzs *p, ISzAlloc *alloc); SRes Xzs_ReadBackward(CXzs *p, ILookInStream *inStream, Int64 *startOffset, ICompressProgress *progress, ISzAlloc *alloc); UInt64 Xzs_GetNumBlocks(const CXzs *p); UInt64 Xzs_GetUnpackSize(const CXzs *p); typedef enum { CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */ CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ CODER_STATUS_NOT_FINISHED, /* stream was not finished */ CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */ } ECoderStatus; typedef enum { CODER_FINISH_ANY, /* finish at any point */ CODER_FINISH_END /* block must be finished at the end */ } ECoderFinishMode; typedef struct _IStateCoder { void *p; void (*Free)(void *p, ISzAlloc *alloc); SRes (*SetProps)(void *p, const Byte *props, size_t propSize, ISzAlloc *alloc); void (*Init)(void *p); SRes (*Code)(void *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished); } IStateCoder; #define MIXCODER_NUM_FILTERS_MAX 4 typedef struct { ISzAlloc *alloc; Byte *buf; int numCoders; int finished[MIXCODER_NUM_FILTERS_MAX - 1]; size_t pos[MIXCODER_NUM_FILTERS_MAX - 1]; size_t size[MIXCODER_NUM_FILTERS_MAX - 1]; UInt64 ids[MIXCODER_NUM_FILTERS_MAX]; IStateCoder coders[MIXCODER_NUM_FILTERS_MAX]; } CMixCoder; void MixCoder_Construct(CMixCoder *p, ISzAlloc *alloc); void MixCoder_Free(CMixCoder *p); void MixCoder_Init(CMixCoder *p); SRes MixCoder_SetFromMethod(CMixCoder *p, int coderIndex, UInt64 methodId); SRes MixCoder_Code(CMixCoder *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, ECoderStatus *status); typedef enum { XZ_STATE_STREAM_HEADER, XZ_STATE_STREAM_INDEX, XZ_STATE_STREAM_INDEX_CRC, XZ_STATE_STREAM_FOOTER, XZ_STATE_STREAM_PADDING, XZ_STATE_BLOCK_HEADER, XZ_STATE_BLOCK, XZ_STATE_BLOCK_FOOTER } EXzState; typedef struct { EXzState state; UInt32 pos; unsigned alignPos; unsigned indexPreSize; CXzStreamFlags streamFlags; UInt32 blockHeaderSize; UInt64 packSize; UInt64 unpackSize; UInt64 numBlocks; UInt64 indexSize; UInt64 indexPos; UInt64 padSize; UInt64 numStartedStreams; UInt64 numFinishedStreams; UInt64 numTotalBlocks; UInt32 crc; CMixCoder decoder; CXzBlock block; CXzCheck check; CSha256 sha; Byte shaDigest[SHA256_DIGEST_SIZE]; Byte buf[XZ_BLOCK_HEADER_SIZE_MAX]; } CXzUnpacker; void XzUnpacker_Construct(CXzUnpacker *p, ISzAlloc *alloc); void XzUnpacker_Init(CXzUnpacker *p); void XzUnpacker_Free(CXzUnpacker *p); /* finishMode: It has meaning only if the decoding reaches output limit (*destLen). CODER_FINISH_ANY - use smallest number of input bytes CODER_FINISH_END - read EndOfStream marker after decoding Returns: SZ_OK status: CODER_STATUS_NOT_FINISHED, CODER_STATUS_NEEDS_MORE_INPUT - maybe there are more xz streams, call XzUnpacker_IsStreamWasFinished to check that current stream was finished SZ_ERROR_MEM - Memory allocation error SZ_ERROR_DATA - Data error SZ_ERROR_UNSUPPORTED - Unsupported method or method properties SZ_ERROR_CRC - CRC error // SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). SZ_ERROR_NO_ARCHIVE - the error with xz Stream Header with one of the following reasons: - xz Stream Signature failure - CRC32 of xz Stream Header is failed - The size of Stream padding is not multiple of four bytes. It's possible to get that error, if xz stream was finished and the stream contains some another data. In that case you can call XzUnpacker_GetExtraSize() function to get real size of xz stream. */ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ECoderFinishMode finishMode, ECoderStatus *status); Bool XzUnpacker_IsStreamWasFinished(CXzUnpacker *p); /* Call XzUnpacker_GetExtraSize after XzUnpacker_Code function to detect real size of xz stream in two cases: XzUnpacker_Code() returns: res == SZ_OK && status == CODER_STATUS_NEEDS_MORE_INPUT res == SZ_ERROR_NO_ARCHIVE */ UInt64 XzUnpacker_GetExtraSize(CXzUnpacker *p); EXTERN_C_END #endif src/libs/7zip/win/C/XzCrc64.c000066400000000000000000000042031325366651500160300ustar00rootroot00000000000000/* XzCrc64.c -- CRC64 calculation 2011-06-28 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "XzCrc64.h" #include "CpuArch.h" #define kCrc64Poly UINT64_CONST(0xC96C5795D7870F42) #ifdef MY_CPU_LE #define CRC_NUM_TABLES 4 #else #define CRC_NUM_TABLES 5 #define CRC_UINT64_SWAP(v) \ ((v >> 56) | \ ((v >> 40) & ((UInt64)0xFF << 8)) | \ ((v >> 24) & ((UInt64)0xFF << 16)) | \ ((v >> 8) & ((UInt64)0xFF << 24)) | \ ((v << 8) & ((UInt64)0xFF << 32)) | \ ((v << 24) & ((UInt64)0xFF << 40)) | \ ((v << 40) & ((UInt64)0xFF << 48)) | \ (v << 56)) UInt64 MY_FAST_CALL XzCrc64UpdateT1_BeT4(UInt64 v, const void *data, size_t size, const UInt64 *table); #endif #ifndef MY_CPU_BE UInt64 MY_FAST_CALL XzCrc64UpdateT4(UInt64 v, const void *data, size_t size, const UInt64 *table); #endif typedef UInt64 (MY_FAST_CALL *CRC_FUNC)(UInt64 v, const void *data, size_t size, const UInt64 *table); static CRC_FUNC g_Crc64Update; UInt64 g_Crc64Table[256 * CRC_NUM_TABLES]; UInt64 MY_FAST_CALL Crc64Update(UInt64 v, const void *data, size_t size) { return g_Crc64Update(v, data, size, g_Crc64Table); } UInt64 MY_FAST_CALL Crc64Calc(const void *data, size_t size) { return g_Crc64Update(CRC64_INIT_VAL, data, size, g_Crc64Table) ^ CRC64_INIT_VAL; } void MY_FAST_CALL Crc64GenerateTable() { UInt32 i; for (i = 0; i < 256; i++) { UInt64 r = i; unsigned j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrc64Poly & ~((r & 1) - 1)); g_Crc64Table[i] = r; } for (; i < 256 * CRC_NUM_TABLES; i++) { UInt64 r = g_Crc64Table[i - 256]; g_Crc64Table[i] = g_Crc64Table[r & 0xFF] ^ (r >> 8); } #ifdef MY_CPU_LE g_Crc64Update = XzCrc64UpdateT4; #else { #ifndef MY_CPU_BE UInt32 k = 1; if (*(const Byte *)&k == 1) g_Crc64Update = XzCrc64UpdateT4; else #endif { for (i = 256 * CRC_NUM_TABLES - 1; i >= 256; i--) { UInt64 x = g_Crc64Table[i - 256]; g_Crc64Table[i] = CRC_UINT64_SWAP(x); } g_Crc64Update = XzCrc64UpdateT1_BeT4; } } #endif } src/libs/7zip/win/C/XzCrc64.h000066400000000000000000000012011325366651500160300ustar00rootroot00000000000000/* XzCrc64.h -- CRC64 calculation 2013-01-18 : Igor Pavlov : Public domain */ #ifndef __XZ_CRC64_H #define __XZ_CRC64_H #include #include "7zTypes.h" EXTERN_C_BEGIN extern UInt64 g_Crc64Table[]; void MY_FAST_CALL Crc64GenerateTable(void); #define CRC64_INIT_VAL UINT64_CONST(0xFFFFFFFFFFFFFFFF) #define CRC64_GET_DIGEST(crc) ((crc) ^ CRC64_INIT_VAL) #define CRC64_UPDATE_BYTE(crc, b) (g_Crc64Table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) UInt64 MY_FAST_CALL Crc64Update(UInt64 crc, const void *data, size_t size); UInt64 MY_FAST_CALL Crc64Calc(const void *data, size_t size); EXTERN_C_END #endif src/libs/7zip/win/C/XzCrc64Opt.c000066400000000000000000000036111325366651500165150ustar00rootroot00000000000000/* XzCrc64Opt.c -- CRC64 calculation 2011-06-28 : Igor Pavlov : Public domain */ #include "Precomp.h" #include "CpuArch.h" #define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) #ifndef MY_CPU_BE UInt64 MY_FAST_CALL XzCrc64UpdateT4(UInt64 v, const void *data, size_t size, const UInt64 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); for (; size >= 4; size -= 4, p += 4) { UInt32 d = (UInt32)v ^ *(const UInt32 *)p; v = (v >> 32) ^ table[0x300 + ((d ) & 0xFF)] ^ table[0x200 + ((d >> 8) & 0xFF)] ^ table[0x100 + ((d >> 16) & 0xFF)] ^ table[0x000 + ((d >> 24))]; } for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } #endif #ifndef MY_CPU_LE #define CRC_UINT64_SWAP(v) \ ((v >> 56) | \ ((v >> 40) & ((UInt64)0xFF << 8)) | \ ((v >> 24) & ((UInt64)0xFF << 16)) | \ ((v >> 8) & ((UInt64)0xFF << 24)) | \ ((v << 8) & ((UInt64)0xFF << 32)) | \ ((v << 24) & ((UInt64)0xFF << 40)) | \ ((v << 40) & ((UInt64)0xFF << 48)) | \ (v << 56)) UInt64 MY_FAST_CALL XzCrc64UpdateT1_BeT4(UInt64 v, const void *data, size_t size, const UInt64 *table) { const Byte *p = (const Byte *)data; for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); v = CRC_UINT64_SWAP(v); table += 0x100; for (; size >= 4; size -= 4, p += 4) { UInt32 d = (UInt32)(v >> 32) ^ *(const UInt32 *)p; v = (v << 32) ^ table[0x000 + ((d ) & 0xFF)] ^ table[0x100 + ((d >> 8) & 0xFF)] ^ table[0x200 + ((d >> 16) & 0xFF)] ^ table[0x300 + ((d >> 24))]; } table -= 0x100; v = CRC_UINT64_SWAP(v); for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } #endif src/libs/7zip/win/C/XzDec.c000066400000000000000000000554771325366651500156650ustar00rootroot00000000000000/* XzDec.c -- Xz Decode 2014-12-30 : Igor Pavlov : Public domain */ #include "Precomp.h" /* #define XZ_DUMP */ #ifdef XZ_DUMP #include #endif #include #include #include "7zCrc.h" #include "Alloc.h" #include "Bra.h" #include "CpuArch.h" #include "Delta.h" #include "Lzma2Dec.h" #ifdef USE_SUBBLOCK #include "Bcj3Dec.c" #include "SbDec.c" #endif #include "Xz.h" #define XZ_CHECK_SIZE_MAX 64 #define CODER_BUF_SIZE (1 << 17) unsigned Xz_ReadVarInt(const Byte *p, size_t maxSize, UInt64 *value) { int i, limit; *value = 0; limit = (maxSize > 9) ? 9 : (int)maxSize; for (i = 0; i < limit;) { Byte b = p[i]; *value |= (UInt64)(b & 0x7F) << (7 * i++); if ((b & 0x80) == 0) return (b == 0 && i != 1) ? 0 : i; } return 0; } /* ---------- BraState ---------- */ #define BRA_BUF_SIZE (1 << 14) typedef struct { size_t bufPos; size_t bufConv; size_t bufTotal; UInt32 methodId; int encodeMode; UInt32 delta; UInt32 ip; UInt32 x86State; Byte deltaState[DELTA_STATE_SIZE]; Byte buf[BRA_BUF_SIZE]; } CBraState; void BraState_Free(void *pp, ISzAlloc *alloc) { alloc->Free(alloc, pp); } SRes BraState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { CBraState *p = ((CBraState *)pp); alloc = alloc; p->ip = 0; if (p->methodId == XZ_ID_Delta) { if (propSize != 1) return SZ_ERROR_UNSUPPORTED; p->delta = (unsigned)props[0] + 1; } else { if (propSize == 4) { UInt32 v = GetUi32(props); switch(p->methodId) { case XZ_ID_PPC: case XZ_ID_ARM: case XZ_ID_SPARC: if ((v & 3) != 0) return SZ_ERROR_UNSUPPORTED; break; case XZ_ID_ARMT: if ((v & 1) != 0) return SZ_ERROR_UNSUPPORTED; break; case XZ_ID_IA64: if ((v & 0xF) != 0) return SZ_ERROR_UNSUPPORTED; break; } p->ip = v; } else if (propSize != 0) return SZ_ERROR_UNSUPPORTED; } return SZ_OK; } void BraState_Init(void *pp) { CBraState *p = ((CBraState *)pp); p->bufPos = p->bufConv = p->bufTotal = 0; x86_Convert_Init(p->x86State); if (p->methodId == XZ_ID_Delta) Delta_Init(p->deltaState); } #define CASE_BRA_CONV(isa) case XZ_ID_ ## isa: p->bufConv = isa ## _Convert(p->buf, p->bufTotal, p->ip, p->encodeMode); break; static SRes BraState_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { CBraState *p = ((CBraState *)pp); SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; *destLen = 0; *srcLen = 0; finishMode = finishMode; *wasFinished = 0; while (destLenOrig > 0) { if (p->bufPos != p->bufConv) { size_t curSize = p->bufConv - p->bufPos; if (curSize > destLenOrig) curSize = destLenOrig; memcpy(dest, p->buf + p->bufPos, curSize); p->bufPos += curSize; *destLen += curSize; dest += curSize; destLenOrig -= curSize; continue; } p->bufTotal -= p->bufPos; memmove(p->buf, p->buf + p->bufPos, p->bufTotal); p->bufPos = 0; p->bufConv = 0; { size_t curSize = BRA_BUF_SIZE - p->bufTotal; if (curSize > srcLenOrig) curSize = srcLenOrig; memcpy(p->buf + p->bufTotal, src, curSize); *srcLen += curSize; src += curSize; srcLenOrig -= curSize; p->bufTotal += curSize; } if (p->bufTotal == 0) break; switch(p->methodId) { case XZ_ID_Delta: if (p->encodeMode) Delta_Encode(p->deltaState, p->delta, p->buf, p->bufTotal); else Delta_Decode(p->deltaState, p->delta, p->buf, p->bufTotal); p->bufConv = p->bufTotal; break; case XZ_ID_X86: p->bufConv = x86_Convert(p->buf, p->bufTotal, p->ip, &p->x86State, p->encodeMode); break; CASE_BRA_CONV(PPC) CASE_BRA_CONV(IA64) CASE_BRA_CONV(ARM) CASE_BRA_CONV(ARMT) CASE_BRA_CONV(SPARC) default: return SZ_ERROR_UNSUPPORTED; } p->ip += (UInt32)p->bufConv; if (p->bufConv == 0) { if (!srcWasFinished) break; p->bufConv = p->bufTotal; } } if (p->bufTotal == p->bufPos && srcLenOrig == 0 && srcWasFinished) *wasFinished = 1; return SZ_OK; } SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAlloc *alloc) { CBraState *decoder; if (id != XZ_ID_Delta && id != XZ_ID_X86 && id != XZ_ID_PPC && id != XZ_ID_IA64 && id != XZ_ID_ARM && id != XZ_ID_ARMT && id != XZ_ID_SPARC) return SZ_ERROR_UNSUPPORTED; p->p = 0; decoder = (CBraState *)alloc->Alloc(alloc, sizeof(CBraState)); if (decoder == 0) return SZ_ERROR_MEM; decoder->methodId = (UInt32)id; decoder->encodeMode = encodeMode; p->p = decoder; p->Free = BraState_Free; p->SetProps = BraState_SetProps; p->Init = BraState_Init; p->Code = BraState_Code; return SZ_OK; } /* ---------- SbState ---------- */ #ifdef USE_SUBBLOCK static void SbState_Free(void *pp, ISzAlloc *alloc) { CSbDec *p = (CSbDec *)pp; SbDec_Free(p); alloc->Free(alloc, pp); } static SRes SbState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { pp = pp; props = props; alloc = alloc; return (propSize == 0) ? SZ_OK : SZ_ERROR_UNSUPPORTED; } static void SbState_Init(void *pp) { SbDec_Init((CSbDec *)pp); } static SRes SbState_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { CSbDec *p = (CSbDec *)pp; SRes res; srcWasFinished = srcWasFinished; p->dest = dest; p->destLen = *destLen; p->src = src; p->srcLen = *srcLen; p->finish = finishMode; /* change it */ res = SbDec_Decode((CSbDec *)pp); *destLen -= p->destLen; *srcLen -= p->srcLen; *wasFinished = (*destLen == 0 && *srcLen == 0); /* change it */ return res; } SRes SbState_SetFromMethod(IStateCoder *p, ISzAlloc *alloc) { CSbDec *decoder; p->p = 0; decoder = alloc->Alloc(alloc, sizeof(CSbDec)); if (decoder == 0) return SZ_ERROR_MEM; p->p = decoder; p->Free = SbState_Free; p->SetProps = SbState_SetProps; p->Init = SbState_Init; p->Code = SbState_Code; SbDec_Construct(decoder); SbDec_SetAlloc(decoder, alloc); return SZ_OK; } #endif /* ---------- Lzma2State ---------- */ static void Lzma2State_Free(void *pp, ISzAlloc *alloc) { Lzma2Dec_Free((CLzma2Dec *)pp, alloc); alloc->Free(alloc, pp); } static SRes Lzma2State_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { if (propSize != 1) return SZ_ERROR_UNSUPPORTED; return Lzma2Dec_Allocate((CLzma2Dec *)pp, props[0], alloc); } static void Lzma2State_Init(void *pp) { Lzma2Dec_Init((CLzma2Dec *)pp); } static SRes Lzma2State_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { ELzmaStatus status; /* ELzmaFinishMode fm = (finishMode == LZMA_FINISH_ANY) ? LZMA_FINISH_ANY : LZMA_FINISH_END; */ SRes res = Lzma2Dec_DecodeToBuf((CLzma2Dec *)pp, dest, destLen, src, srcLen, (ELzmaFinishMode)finishMode, &status); srcWasFinished = srcWasFinished; *wasFinished = (status == LZMA_STATUS_FINISHED_WITH_MARK); return res; } static SRes Lzma2State_SetFromMethod(IStateCoder *p, ISzAlloc *alloc) { CLzma2Dec *decoder = (CLzma2Dec *)alloc->Alloc(alloc, sizeof(CLzma2Dec)); p->p = decoder; if (decoder == 0) return SZ_ERROR_MEM; p->Free = Lzma2State_Free; p->SetProps = Lzma2State_SetProps; p->Init = Lzma2State_Init; p->Code = Lzma2State_Code; Lzma2Dec_Construct(decoder); return SZ_OK; } void MixCoder_Construct(CMixCoder *p, ISzAlloc *alloc) { int i; p->alloc = alloc; p->buf = 0; p->numCoders = 0; for (i = 0; i < MIXCODER_NUM_FILTERS_MAX; i++) p->coders[i].p = NULL; } void MixCoder_Free(CMixCoder *p) { int i; for (i = 0; i < p->numCoders; i++) { IStateCoder *sc = &p->coders[i]; if (p->alloc && sc->p) sc->Free(sc->p, p->alloc); } p->numCoders = 0; if (p->buf) { p->alloc->Free(p->alloc, p->buf); p->buf = 0; /* 9.31: the BUG was fixed */ } } void MixCoder_Init(CMixCoder *p) { int i; for (i = 0; i < p->numCoders - 1; i++) { p->size[i] = 0; p->pos[i] = 0; p->finished[i] = 0; } for (i = 0; i < p->numCoders; i++) { IStateCoder *coder = &p->coders[i]; coder->Init(coder->p); } } SRes MixCoder_SetFromMethod(CMixCoder *p, int coderIndex, UInt64 methodId) { IStateCoder *sc = &p->coders[coderIndex]; p->ids[coderIndex] = methodId; switch(methodId) { case XZ_ID_LZMA2: return Lzma2State_SetFromMethod(sc, p->alloc); #ifdef USE_SUBBLOCK case XZ_ID_Subblock: return SbState_SetFromMethod(sc, p->alloc); #endif } if (coderIndex == 0) return SZ_ERROR_UNSUPPORTED; return BraState_SetFromMethod(sc, methodId, 0, p->alloc); } SRes MixCoder_Code(CMixCoder *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, ECoderStatus *status) { SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; Bool allFinished = True; *destLen = 0; *srcLen = 0; *status = CODER_STATUS_NOT_FINISHED; if (p->buf == 0) { p->buf = (Byte *)p->alloc->Alloc(p->alloc, CODER_BUF_SIZE * (MIXCODER_NUM_FILTERS_MAX - 1)); if (p->buf == 0) return SZ_ERROR_MEM; } if (p->numCoders != 1) finishMode = CODER_FINISH_ANY; for (;;) { Bool processed = False; int i; /* if (p->numCoders == 1 && *destLen == destLenOrig && finishMode == LZMA_FINISH_ANY) break; */ for (i = 0; i < p->numCoders; i++) { SRes res; IStateCoder *coder = &p->coders[i]; Byte *destCur; SizeT destLenCur, srcLenCur; const Byte *srcCur; int srcFinishedCur; int encodingWasFinished; if (i == 0) { srcCur = src; srcLenCur = srcLenOrig - *srcLen; srcFinishedCur = srcWasFinished; } else { srcCur = p->buf + (CODER_BUF_SIZE * (i - 1)) + p->pos[i - 1]; srcLenCur = p->size[i - 1] - p->pos[i - 1]; srcFinishedCur = p->finished[i - 1]; } if (i == p->numCoders - 1) { destCur = dest; destLenCur = destLenOrig - *destLen; } else { if (p->pos[i] != p->size[i]) continue; destCur = p->buf + (CODER_BUF_SIZE * i); destLenCur = CODER_BUF_SIZE; } res = coder->Code(coder->p, destCur, &destLenCur, srcCur, &srcLenCur, srcFinishedCur, finishMode, &encodingWasFinished); if (!encodingWasFinished) allFinished = False; if (i == 0) { *srcLen += srcLenCur; src += srcLenCur; } else { p->pos[i - 1] += srcLenCur; } if (i == p->numCoders - 1) { *destLen += destLenCur; dest += destLenCur; } else { p->size[i] = destLenCur; p->pos[i] = 0; p->finished[i] = encodingWasFinished; } if (res != SZ_OK) return res; if (destLenCur != 0 || srcLenCur != 0) processed = True; } if (!processed) break; } if (allFinished) *status = CODER_STATUS_FINISHED_WITH_MARK; return SZ_OK; } SRes Xz_ParseHeader(CXzStreamFlags *p, const Byte *buf) { *p = (CXzStreamFlags)GetBe16(buf + XZ_SIG_SIZE); if (CrcCalc(buf + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE) != GetUi32(buf + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE)) return SZ_ERROR_NO_ARCHIVE; return XzFlags_IsSupported(*p) ? SZ_OK : SZ_ERROR_UNSUPPORTED; } static Bool Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *buf) { return indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2) && (GetUi32(buf) == CrcCalc(buf + 4, 6) && flags == GetBe16(buf + 8) && memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0); } #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } SRes XzBlock_Parse(CXzBlock *p, const Byte *header) { unsigned pos; int numFilters, i; UInt32 headerSize = (UInt32)header[0] << 2; if (CrcCalc(header, headerSize) != GetUi32(header + headerSize)) return SZ_ERROR_ARCHIVE; pos = 1; if (pos == headerSize) return SZ_ERROR_ARCHIVE; p->flags = header[pos++]; if (XzBlock_HasPackSize(p)) { READ_VARINT_AND_CHECK(header, pos, headerSize, &p->packSize); if (p->packSize == 0 || p->packSize + headerSize >= (UInt64)1 << 63) return SZ_ERROR_ARCHIVE; } if (XzBlock_HasUnpackSize(p)) READ_VARINT_AND_CHECK(header, pos, headerSize, &p->unpackSize); numFilters = XzBlock_GetNumFilters(p); for (i = 0; i < numFilters; i++) { CXzFilter *filter = p->filters + i; UInt64 size; READ_VARINT_AND_CHECK(header, pos, headerSize, &filter->id); READ_VARINT_AND_CHECK(header, pos, headerSize, &size); if (size > headerSize - pos || size > XZ_FILTER_PROPS_SIZE_MAX) return SZ_ERROR_ARCHIVE; filter->propsSize = (UInt32)size; memcpy(filter->props, header + pos, (size_t)size); pos += (unsigned)size; #ifdef XZ_DUMP printf("\nf[%d] = %2X: ", i, filter->id); { int i; for (i = 0; i < size; i++) printf(" %2X", filter->props[i]); } #endif } while (pos < headerSize) if (header[pos++] != 0) return SZ_ERROR_ARCHIVE; return SZ_OK; } SRes XzDec_Init(CMixCoder *p, const CXzBlock *block) { int i; Bool needReInit = True; int numFilters = XzBlock_GetNumFilters(block); if (numFilters == p->numCoders) { for (i = 0; i < numFilters; i++) if (p->ids[i] != block->filters[numFilters - 1 - i].id) break; needReInit = (i != numFilters); } if (needReInit) { MixCoder_Free(p); p->numCoders = numFilters; for (i = 0; i < numFilters; i++) { const CXzFilter *f = &block->filters[numFilters - 1 - i]; RINOK(MixCoder_SetFromMethod(p, i, f->id)); } } for (i = 0; i < numFilters; i++) { const CXzFilter *f = &block->filters[numFilters - 1 - i]; IStateCoder *sc = &p->coders[i]; RINOK(sc->SetProps(sc->p, f->props, f->propsSize, p->alloc)); } MixCoder_Init(p); return SZ_OK; } void XzUnpacker_Init(CXzUnpacker *p) { p->state = XZ_STATE_STREAM_HEADER; p->pos = 0; p->numStartedStreams = 0; p->numFinishedStreams = 0; p->numTotalBlocks = 0; p->padSize = 0; } void XzUnpacker_Construct(CXzUnpacker *p, ISzAlloc *alloc) { MixCoder_Construct(&p->decoder, alloc); XzUnpacker_Init(p); } void XzUnpacker_Free(CXzUnpacker *p) { MixCoder_Free(&p->decoder); } SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ECoderFinishMode finishMode, ECoderStatus *status) { SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; *destLen = 0; *srcLen = 0; *status = CODER_STATUS_NOT_SPECIFIED; for (;;) { SizeT srcRem = srcLenOrig - *srcLen; if (p->state == XZ_STATE_BLOCK) { SizeT destLen2 = destLenOrig - *destLen; SizeT srcLen2 = srcLenOrig - *srcLen; SRes res; if (srcLen2 == 0 && destLen2 == 0) { *status = CODER_STATUS_NOT_FINISHED; return SZ_OK; } res = MixCoder_Code(&p->decoder, dest, &destLen2, src, &srcLen2, False, finishMode, status); XzCheck_Update(&p->check, dest, destLen2); (*srcLen) += srcLen2; src += srcLen2; p->packSize += srcLen2; (*destLen) += destLen2; dest += destLen2; p->unpackSize += destLen2; RINOK(res); if (*status == CODER_STATUS_FINISHED_WITH_MARK) { Byte temp[32]; unsigned num = Xz_WriteVarInt(temp, p->packSize + p->blockHeaderSize + XzFlags_GetCheckSize(p->streamFlags)); num += Xz_WriteVarInt(temp + num, p->unpackSize); Sha256_Update(&p->sha, temp, num); p->indexSize += num; p->numBlocks++; p->state = XZ_STATE_BLOCK_FOOTER; p->pos = 0; p->alignPos = 0; } else if (srcLen2 == 0 && destLen2 == 0) return SZ_OK; continue; } if (srcRem == 0) { *status = CODER_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } switch (p->state) { case XZ_STATE_STREAM_HEADER: { if (p->pos < XZ_STREAM_HEADER_SIZE) { if (p->pos < XZ_SIG_SIZE && *src != XZ_SIG[p->pos]) return SZ_ERROR_NO_ARCHIVE; p->buf[p->pos++] = *src++; (*srcLen)++; } else { RINOK(Xz_ParseHeader(&p->streamFlags, p->buf)); p->numStartedStreams++; p->state = XZ_STATE_BLOCK_HEADER; Sha256_Init(&p->sha); p->indexSize = 0; p->numBlocks = 0; p->pos = 0; } break; } case XZ_STATE_BLOCK_HEADER: { if (p->pos == 0) { p->buf[p->pos++] = *src++; (*srcLen)++; if (p->buf[0] == 0) { p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks); p->indexPos = p->indexPreSize; p->indexSize += p->indexPreSize; Sha256_Final(&p->sha, p->shaDigest); Sha256_Init(&p->sha); p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize); p->state = XZ_STATE_STREAM_INDEX; } p->blockHeaderSize = ((UInt32)p->buf[0] << 2) + 4; } else if (p->pos != p->blockHeaderSize) { UInt32 cur = p->blockHeaderSize - p->pos; if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; } else { RINOK(XzBlock_Parse(&p->block, p->buf)); p->numTotalBlocks++; p->state = XZ_STATE_BLOCK; p->packSize = 0; p->unpackSize = 0; XzCheck_Init(&p->check, XzFlags_GetCheckType(p->streamFlags)); RINOK(XzDec_Init(&p->decoder, &p->block)); } break; } case XZ_STATE_BLOCK_FOOTER: { if (((p->packSize + p->alignPos) & 3) != 0) { (*srcLen)++; p->alignPos++; if (*src++ != 0) return SZ_ERROR_CRC; } else { UInt32 checkSize = XzFlags_GetCheckSize(p->streamFlags); UInt32 cur = checkSize - p->pos; if (cur != 0) { if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; } else { Byte digest[XZ_CHECK_SIZE_MAX]; p->state = XZ_STATE_BLOCK_HEADER; p->pos = 0; if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0) return SZ_ERROR_CRC; } } break; } case XZ_STATE_STREAM_INDEX: { if (p->pos < p->indexPreSize) { (*srcLen)++; if (*src++ != p->buf[p->pos++]) return SZ_ERROR_CRC; } else { if (p->indexPos < p->indexSize) { UInt64 cur = p->indexSize - p->indexPos; if (srcRem > cur) srcRem = (SizeT)cur; p->crc = CrcUpdate(p->crc, src, srcRem); Sha256_Update(&p->sha, src, srcRem); (*srcLen) += srcRem; src += srcRem; p->indexPos += srcRem; } else if ((p->indexPos & 3) != 0) { Byte b = *src++; p->crc = CRC_UPDATE_BYTE(p->crc, b); (*srcLen)++; p->indexPos++; p->indexSize++; if (b != 0) return SZ_ERROR_CRC; } else { Byte digest[SHA256_DIGEST_SIZE]; p->state = XZ_STATE_STREAM_INDEX_CRC; p->indexSize += 4; p->pos = 0; Sha256_Final(&p->sha, digest); if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0) return SZ_ERROR_CRC; } } break; } case XZ_STATE_STREAM_INDEX_CRC: { if (p->pos < 4) { (*srcLen)++; p->buf[p->pos++] = *src++; } else { p->state = XZ_STATE_STREAM_FOOTER; p->pos = 0; if (CRC_GET_DIGEST(p->crc) != GetUi32(p->buf)) return SZ_ERROR_CRC; } break; } case XZ_STATE_STREAM_FOOTER: { UInt32 cur = XZ_STREAM_FOOTER_SIZE - p->pos; if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; if (p->pos == XZ_STREAM_FOOTER_SIZE) { p->state = XZ_STATE_STREAM_PADDING; p->numFinishedStreams++; p->padSize = 0; if (!Xz_CheckFooter(p->streamFlags, p->indexSize, p->buf)) return SZ_ERROR_CRC; } break; } case XZ_STATE_STREAM_PADDING: { if (*src != 0) { if (((UInt32)p->padSize & 3) != 0) return SZ_ERROR_NO_ARCHIVE; p->pos = 0; p->state = XZ_STATE_STREAM_HEADER; } else { (*srcLen)++; src++; p->padSize++; } break; } case XZ_STATE_BLOCK: break; /* to disable GCC warning */ } } /* if (p->state == XZ_STATE_FINISHED) *status = CODER_STATUS_FINISHED_WITH_MARK; return SZ_OK; */ } Bool XzUnpacker_IsStreamWasFinished(CXzUnpacker *p) { return (p->state == XZ_STATE_STREAM_PADDING) && (((UInt32)p->padSize & 3) == 0); } UInt64 XzUnpacker_GetExtraSize(CXzUnpacker *p) { UInt64 num = 0; if (p->state == XZ_STATE_STREAM_PADDING) num += p->padSize; else if (p->state == XZ_STATE_STREAM_HEADER) num += p->padSize + p->pos; return num; } src/libs/7zip/win/C/XzEnc.c000066400000000000000000000325571325366651500156710ustar00rootroot00000000000000/* XzEnc.c -- Xz Encode 2014-12-30 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include #include "7zCrc.h" #include "Alloc.h" #include "Bra.h" #include "CpuArch.h" #ifdef USE_SUBBLOCK #include "Bcj3Enc.c" #include "SbFind.c" #include "SbEnc.c" #endif #include "XzEnc.h" static void *SzBigAlloc(void *p, size_t size) { p = p; return BigAlloc(size); } static void SzBigFree(void *p, void *address) { p = p; BigFree(address); } static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } static void SzFree(void *p, void *address) { p = p; MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; #define XzBlock_ClearFlags(p) (p)->flags = 0; #define XzBlock_SetNumFilters(p, n) (p)->flags |= ((n) - 1); #define XzBlock_SetHasPackSize(p) (p)->flags |= XZ_BF_PACK_SIZE; #define XzBlock_SetHasUnpackSize(p) (p)->flags |= XZ_BF_UNPACK_SIZE; static SRes WriteBytes(ISeqOutStream *s, const void *buf, UInt32 size) { return (s->Write(s, buf, size) == size) ? SZ_OK : SZ_ERROR_WRITE; } static SRes WriteBytesAndCrc(ISeqOutStream *s, const void *buf, UInt32 size, UInt32 *crc) { *crc = CrcUpdate(*crc, buf, size); return WriteBytes(s, buf, size); } SRes Xz_WriteHeader(CXzStreamFlags f, ISeqOutStream *s) { UInt32 crc; Byte header[XZ_STREAM_HEADER_SIZE]; memcpy(header, XZ_SIG, XZ_SIG_SIZE); header[XZ_SIG_SIZE] = (Byte)(f >> 8); header[XZ_SIG_SIZE + 1] = (Byte)(f & 0xFF); crc = CrcCalc(header + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE); SetUi32(header + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE, crc); return WriteBytes(s, header, XZ_STREAM_HEADER_SIZE); } SRes XzBlock_WriteHeader(const CXzBlock *p, ISeqOutStream *s) { Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; unsigned pos = 1; int numFilters, i; header[pos++] = p->flags; if (XzBlock_HasPackSize(p)) pos += Xz_WriteVarInt(header + pos, p->packSize); if (XzBlock_HasUnpackSize(p)) pos += Xz_WriteVarInt(header + pos, p->unpackSize); numFilters = XzBlock_GetNumFilters(p); for (i = 0; i < numFilters; i++) { const CXzFilter *f = &p->filters[i]; pos += Xz_WriteVarInt(header + pos, f->id); pos += Xz_WriteVarInt(header + pos, f->propsSize); memcpy(header + pos, f->props, f->propsSize); pos += f->propsSize; } while((pos & 3) != 0) header[pos++] = 0; header[0] = (Byte)(pos >> 2); SetUi32(header + pos, CrcCalc(header, pos)); return WriteBytes(s, header, pos + 4); } SRes Xz_WriteFooter(CXzStream *p, ISeqOutStream *s) { Byte buf[32]; UInt64 globalPos; { UInt32 crc = CRC_INIT_VAL; unsigned pos = 1 + Xz_WriteVarInt(buf + 1, p->numBlocks); size_t i; globalPos = pos; buf[0] = 0; RINOK(WriteBytesAndCrc(s, buf, pos, &crc)); for (i = 0; i < p->numBlocks; i++) { const CXzBlockSizes *block = &p->blocks[i]; pos = Xz_WriteVarInt(buf, block->totalSize); pos += Xz_WriteVarInt(buf + pos, block->unpackSize); globalPos += pos; RINOK(WriteBytesAndCrc(s, buf, pos, &crc)); } pos = ((unsigned)globalPos & 3); if (pos != 0) { buf[0] = buf[1] = buf[2] = 0; RINOK(WriteBytesAndCrc(s, buf, 4 - pos, &crc)); globalPos += 4 - pos; } { SetUi32(buf, CRC_GET_DIGEST(crc)); RINOK(WriteBytes(s, buf, 4)); globalPos += 4; } } { UInt32 indexSize = (UInt32)((globalPos >> 2) - 1); SetUi32(buf + 4, indexSize); buf[8] = (Byte)(p->flags >> 8); buf[9] = (Byte)(p->flags & 0xFF); SetUi32(buf, CrcCalc(buf + 4, 6)); memcpy(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE); return WriteBytes(s, buf, 12); } } SRes Xz_AddIndexRecord(CXzStream *p, UInt64 unpackSize, UInt64 totalSize, ISzAlloc *alloc) { if (p->blocks == 0 || p->numBlocksAllocated == p->numBlocks) { size_t num = (p->numBlocks + 1) * 2; size_t newSize = sizeof(CXzBlockSizes) * num; CXzBlockSizes *blocks; if (newSize / sizeof(CXzBlockSizes) != num) return SZ_ERROR_MEM; blocks = (CXzBlockSizes *)alloc->Alloc(alloc, newSize); if (blocks == 0) return SZ_ERROR_MEM; if (p->numBlocks != 0) { memcpy(blocks, p->blocks, p->numBlocks * sizeof(CXzBlockSizes)); Xz_Free(p, alloc); } p->blocks = blocks; p->numBlocksAllocated = num; } { CXzBlockSizes *block = &p->blocks[p->numBlocks++]; block->totalSize = totalSize; block->unpackSize = unpackSize; } return SZ_OK; } /* ---------- CSeqCheckInStream ---------- */ typedef struct { ISeqInStream p; ISeqInStream *realStream; UInt64 processed; CXzCheck check; } CSeqCheckInStream; void SeqCheckInStream_Init(CSeqCheckInStream *p, int mode) { p->processed = 0; XzCheck_Init(&p->check, mode); } void SeqCheckInStream_GetDigest(CSeqCheckInStream *p, Byte *digest) { XzCheck_Final(&p->check, digest); } static SRes SeqCheckInStream_Read(void *pp, void *data, size_t *size) { CSeqCheckInStream *p = (CSeqCheckInStream *)pp; SRes res = p->realStream->Read(p->realStream, data, size); XzCheck_Update(&p->check, data, *size); p->processed += *size; return res; } /* ---------- CSeqSizeOutStream ---------- */ typedef struct { ISeqOutStream p; ISeqOutStream *realStream; UInt64 processed; } CSeqSizeOutStream; static size_t MyWrite(void *pp, const void *data, size_t size) { CSeqSizeOutStream *p = (CSeqSizeOutStream *)pp; size = p->realStream->Write(p->realStream, data, size); p->processed += size; return size; } /* ---------- CSeqInFilter ---------- */ #define FILTER_BUF_SIZE (1 << 20) typedef struct { ISeqInStream p; ISeqInStream *realStream; IStateCoder StateCoder; Byte *buf; size_t curPos; size_t endPos; int srcWasFinished; } CSeqInFilter; static SRes SeqInFilter_Read(void *pp, void *data, size_t *size) { CSeqInFilter *p = (CSeqInFilter *)pp; size_t sizeOriginal = *size; if (sizeOriginal == 0) return SZ_OK; *size = 0; for (;;) { if (!p->srcWasFinished && p->curPos == p->endPos) { p->curPos = 0; p->endPos = FILTER_BUF_SIZE; RINOK(p->realStream->Read(p->realStream, p->buf, &p->endPos)); if (p->endPos == 0) p->srcWasFinished = 1; } { SizeT srcLen = p->endPos - p->curPos; int wasFinished; SRes res; *size = sizeOriginal; res = p->StateCoder.Code(p->StateCoder.p, data, size, p->buf + p->curPos, &srcLen, p->srcWasFinished, CODER_FINISH_ANY, &wasFinished); p->curPos += srcLen; if (*size != 0 || srcLen == 0 || res != 0) return res; } } } static void SeqInFilter_Construct(CSeqInFilter *p) { p->buf = NULL; p->p.Read = SeqInFilter_Read; } static void SeqInFilter_Free(CSeqInFilter *p) { if (p->buf) { g_Alloc.Free(&g_Alloc, p->buf); p->buf = NULL; } } SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, int encodeMode, ISzAlloc *alloc); static SRes SeqInFilter_Init(CSeqInFilter *p, const CXzFilter *props) { if (!p->buf) { p->buf = g_Alloc.Alloc(&g_Alloc, FILTER_BUF_SIZE); if (!p->buf) return SZ_ERROR_MEM; } p->curPos = p->endPos = 0; p->srcWasFinished = 0; RINOK(BraState_SetFromMethod(&p->StateCoder, props->id, 1, &g_Alloc)); RINOK(p->StateCoder.SetProps(p->StateCoder.p, props->props, props->propsSize, &g_Alloc)); p->StateCoder.Init(p->StateCoder.p); return SZ_OK; } /* ---------- CSbEncInStream ---------- */ #ifdef USE_SUBBLOCK typedef struct { ISeqInStream p; ISeqInStream *inStream; CSbEnc enc; } CSbEncInStream; static SRes SbEncInStream_Read(void *pp, void *data, size_t *size) { CSbEncInStream *p = (CSbEncInStream *)pp; size_t sizeOriginal = *size; if (sizeOriginal == 0) return S_OK; for (;;) { if (p->enc.needRead && !p->enc.readWasFinished) { size_t processed = p->enc.needReadSizeMax; RINOK(p->inStream->Read(p->inStream, p->enc.buf + p->enc.readPos, &processed)); p->enc.readPos += processed; if (processed == 0) { p->enc.readWasFinished = True; p->enc.isFinalFinished = True; } p->enc.needRead = False; } *size = sizeOriginal; RINOK(SbEnc_Read(&p->enc, data, size)); if (*size != 0 || !p->enc.needRead) return S_OK; } } void SbEncInStream_Construct(CSbEncInStream *p, ISzAlloc *alloc) { SbEnc_Construct(&p->enc, alloc); p->p.Read = SbEncInStream_Read; } SRes SbEncInStream_Init(CSbEncInStream *p) { return SbEnc_Init(&p->enc); } void SbEncInStream_Free(CSbEncInStream *p) { SbEnc_Free(&p->enc); } #endif typedef struct { CLzma2EncHandle lzma2; #ifdef USE_SUBBLOCK CSbEncInStream sb; #endif CSeqInFilter filter; ISzAlloc *alloc; ISzAlloc *bigAlloc; } CLzma2WithFilters; static void Lzma2WithFilters_Construct(CLzma2WithFilters *p, ISzAlloc *alloc, ISzAlloc *bigAlloc) { p->alloc = alloc; p->bigAlloc = bigAlloc; p->lzma2 = NULL; #ifdef USE_SUBBLOCK SbEncInStream_Construct(&p->sb, alloc); #endif SeqInFilter_Construct(&p->filter); } static SRes Lzma2WithFilters_Create(CLzma2WithFilters *p) { p->lzma2 = Lzma2Enc_Create(p->alloc, p->bigAlloc); if (p->lzma2 == 0) return SZ_ERROR_MEM; return SZ_OK; } static void Lzma2WithFilters_Free(CLzma2WithFilters *p) { SeqInFilter_Free(&p->filter); #ifdef USE_SUBBLOCK SbEncInStream_Free(&p->sb); #endif if (p->lzma2) { Lzma2Enc_Destroy(p->lzma2); p->lzma2 = NULL; } } void XzProps_Init(CXzProps *p) { p->lzma2Props = 0; p->filterProps = 0; p->checkId = XZ_CHECK_CRC32; } void XzFilterProps_Init(CXzFilterProps *p) { p->id = 0; p->delta = 0; p->ip= 0; p->ipDefined = False; } static SRes Xz_Compress(CXzStream *xz, CLzma2WithFilters *lzmaf, ISeqOutStream *outStream, ISeqInStream *inStream, const CXzProps *props, ICompressProgress *progress) { xz->flags = (Byte)props->checkId; RINOK(Lzma2Enc_SetProps(lzmaf->lzma2, props->lzma2Props)); RINOK(Xz_WriteHeader(xz->flags, outStream)); { CSeqCheckInStream checkInStream; CSeqSizeOutStream seqSizeOutStream; CXzBlock block; int filterIndex = 0; CXzFilter *filter = NULL; const CXzFilterProps *fp = props->filterProps; XzBlock_ClearFlags(&block); XzBlock_SetNumFilters(&block, 1 + (fp ? 1 : 0)); if (fp) { filter = &block.filters[filterIndex++]; filter->id = fp->id; filter->propsSize = 0; if (fp->id == XZ_ID_Delta) { filter->props[0] = (Byte)(fp->delta - 1); filter->propsSize = 1; } else if (fp->ipDefined) { SetUi32(filter->props, fp->ip); filter->propsSize = 4; } } { CXzFilter *f = &block.filters[filterIndex++]; f->id = XZ_ID_LZMA2; f->propsSize = 1; f->props[0] = Lzma2Enc_WriteProperties(lzmaf->lzma2); } seqSizeOutStream.p.Write = MyWrite; seqSizeOutStream.realStream = outStream; seqSizeOutStream.processed = 0; RINOK(XzBlock_WriteHeader(&block, &seqSizeOutStream.p)); checkInStream.p.Read = SeqCheckInStream_Read; checkInStream.realStream = inStream; SeqCheckInStream_Init(&checkInStream, XzFlags_GetCheckType(xz->flags)); if (fp) { #ifdef USE_SUBBLOCK if (fp->id == XZ_ID_Subblock) { lzmaf->sb.inStream = &checkInStream.p; RINOK(SbEncInStream_Init(&lzmaf->sb)); } else #endif { lzmaf->filter.realStream = &checkInStream.p; RINOK(SeqInFilter_Init(&lzmaf->filter, filter)); } } { UInt64 packPos = seqSizeOutStream.processed; SRes res = Lzma2Enc_Encode(lzmaf->lzma2, &seqSizeOutStream.p, fp ? #ifdef USE_SUBBLOCK (fp->id == XZ_ID_Subblock) ? &lzmaf->sb.p: #endif &lzmaf->filter.p: &checkInStream.p, progress); RINOK(res); block.unpackSize = checkInStream.processed; block.packSize = seqSizeOutStream.processed - packPos; } { unsigned padSize = 0; Byte buf[128]; while((((unsigned)block.packSize + padSize) & 3) != 0) buf[padSize++] = 0; SeqCheckInStream_GetDigest(&checkInStream, buf + padSize); RINOK(WriteBytes(&seqSizeOutStream.p, buf, padSize + XzFlags_GetCheckSize(xz->flags))); RINOK(Xz_AddIndexRecord(xz, block.unpackSize, seqSizeOutStream.processed - padSize, &g_Alloc)); } } return Xz_WriteFooter(xz, outStream); } SRes Xz_Encode(ISeqOutStream *outStream, ISeqInStream *inStream, const CXzProps *props, ICompressProgress *progress) { SRes res; CXzStream xz; CLzma2WithFilters lzmaf; Xz_Construct(&xz); Lzma2WithFilters_Construct(&lzmaf, &g_Alloc, &g_BigAlloc); res = Lzma2WithFilters_Create(&lzmaf); if (res == SZ_OK) res = Xz_Compress(&xz, &lzmaf, outStream, inStream, props, progress); Lzma2WithFilters_Free(&lzmaf); Xz_Free(&xz, &g_Alloc); return res; } SRes Xz_EncodeEmpty(ISeqOutStream *outStream) { SRes res; CXzStream xz; Xz_Construct(&xz); res = Xz_WriteHeader(xz.flags, outStream); if (res == SZ_OK) res = Xz_WriteFooter(&xz, outStream); Xz_Free(&xz, &g_Alloc); return res; } src/libs/7zip/win/C/XzEnc.h000066400000000000000000000012631325366651500156640ustar00rootroot00000000000000/* XzEnc.h -- Xz Encode 2011-02-07 : Igor Pavlov : Public domain */ #ifndef __XZ_ENC_H #define __XZ_ENC_H #include "Lzma2Enc.h" #include "Xz.h" EXTERN_C_BEGIN typedef struct { UInt32 id; UInt32 delta; UInt32 ip; int ipDefined; } CXzFilterProps; void XzFilterProps_Init(CXzFilterProps *p); typedef struct { const CLzma2EncProps *lzma2Props; const CXzFilterProps *filterProps; unsigned checkId; } CXzProps; void XzProps_Init(CXzProps *p); SRes Xz_Encode(ISeqOutStream *outStream, ISeqInStream *inStream, const CXzProps *props, ICompressProgress *progress); SRes Xz_EncodeEmpty(ISeqOutStream *outStream); EXTERN_C_END #endif src/libs/7zip/win/C/XzIn.c000066400000000000000000000206441325366651500155240ustar00rootroot00000000000000/* XzIn.c - Xz input 2014-12-30 : Igor Pavlov : Public domain */ #include "Precomp.h" #include #include "7zCrc.h" #include "CpuArch.h" #include "Xz.h" SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStream *inStream) { Byte sig[XZ_STREAM_HEADER_SIZE]; RINOK(SeqInStream_Read2(inStream, sig, XZ_STREAM_HEADER_SIZE, SZ_ERROR_NO_ARCHIVE)); if (memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0) return SZ_ERROR_NO_ARCHIVE; return Xz_ParseHeader(p, sig); } #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStream *inStream, Bool *isIndex, UInt32 *headerSizeRes) { Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; unsigned headerSize; *headerSizeRes = 0; RINOK(SeqInStream_ReadByte(inStream, &header[0])); headerSize = ((unsigned)header[0] << 2) + 4; if (headerSize == 0) { *headerSizeRes = 1; *isIndex = True; return SZ_OK; } *isIndex = False; *headerSizeRes = headerSize; RINOK(SeqInStream_Read(inStream, header + 1, headerSize - 1)); return XzBlock_Parse(p, header); } #define ADD_SIZE_CHECH(size, val) \ { UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; } UInt64 Xz_GetUnpackSize(const CXzStream *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->numBlocks; i++) ADD_SIZE_CHECH(size, p->blocks[i].unpackSize); return size; } UInt64 Xz_GetPackSize(const CXzStream *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->numBlocks; i++) ADD_SIZE_CHECH(size, (p->blocks[i].totalSize + 3) & ~(UInt64)3); return size; } /* SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStream *inStream) { return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f)); } */ static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAlloc *alloc) { size_t i, numBlocks, pos = 1; UInt32 crc; if (size < 5 || buf[0] != 0) return SZ_ERROR_ARCHIVE; size -= 4; crc = CrcCalc(buf, size); if (crc != GetUi32(buf + size)) return SZ_ERROR_ARCHIVE; { UInt64 numBlocks64; READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64); numBlocks = (size_t)numBlocks64; if (numBlocks != numBlocks64 || numBlocks * 2 > size) return SZ_ERROR_ARCHIVE; } Xz_Free(p, alloc); if (numBlocks != 0) { p->numBlocks = numBlocks; p->numBlocksAllocated = numBlocks; p->blocks = alloc->Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); if (p->blocks == 0) return SZ_ERROR_MEM; for (i = 0; i < numBlocks; i++) { CXzBlockSizes *block = &p->blocks[i]; READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize); READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize); if (block->totalSize == 0) return SZ_ERROR_ARCHIVE; } } while ((pos & 3) != 0) if (buf[pos++] != 0) return SZ_ERROR_ARCHIVE; return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; } static SRes Xz_ReadIndex(CXzStream *p, ILookInStream *stream, UInt64 indexSize, ISzAlloc *alloc) { SRes res; size_t size; Byte *buf; if (indexSize > ((UInt32)1 << 31)) return SZ_ERROR_UNSUPPORTED; size = (size_t)indexSize; if (size != indexSize) return SZ_ERROR_UNSUPPORTED; buf = alloc->Alloc(alloc, size); if (buf == 0) return SZ_ERROR_MEM; res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); if (res == SZ_OK) res = Xz_ReadIndex2(p, buf, size, alloc); alloc->Free(alloc, buf); return res; } static SRes SeekFromCur(ILookInStream *inStream, Int64 *res) { return inStream->Seek(inStream, res, SZ_SEEK_CUR); } static SRes Xz_ReadBackward(CXzStream *p, ILookInStream *stream, Int64 *startOffset, ISzAlloc *alloc) { UInt64 indexSize; Byte buf[XZ_STREAM_FOOTER_SIZE]; if ((*startOffset & 3) != 0 || *startOffset < XZ_STREAM_FOOTER_SIZE) return SZ_ERROR_NO_ARCHIVE; *startOffset = -XZ_STREAM_FOOTER_SIZE; RINOK(SeekFromCur(stream, startOffset)); RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO_ARCHIVE)); if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0) { UInt32 total = 0; *startOffset += XZ_STREAM_FOOTER_SIZE; for (;;) { size_t i; #define TEMP_BUF_SIZE (1 << 10) Byte tempBuf[TEMP_BUF_SIZE]; if (*startOffset < XZ_STREAM_FOOTER_SIZE || total > (1 << 16)) return SZ_ERROR_NO_ARCHIVE; i = (*startOffset > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)*startOffset; total += (UInt32)i; *startOffset = -(Int64)i; RINOK(SeekFromCur(stream, startOffset)); RINOK(LookInStream_Read2(stream, tempBuf, i, SZ_ERROR_NO_ARCHIVE)); for (; i != 0; i--) if (tempBuf[i - 1] != 0) break; if (i != 0) { if ((i & 3) != 0) return SZ_ERROR_NO_ARCHIVE; *startOffset += i; break; } } if (*startOffset < XZ_STREAM_FOOTER_SIZE) return SZ_ERROR_NO_ARCHIVE; *startOffset -= XZ_STREAM_FOOTER_SIZE; RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET)); RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO_ARCHIVE)); if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0) return SZ_ERROR_NO_ARCHIVE; } p->flags = (CXzStreamFlags)GetBe16(buf + 8); if (!XzFlags_IsSupported(p->flags)) return SZ_ERROR_UNSUPPORTED; if (GetUi32(buf) != CrcCalc(buf + 4, 6)) return SZ_ERROR_ARCHIVE; indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2; *startOffset = -(Int64)(indexSize + XZ_STREAM_FOOTER_SIZE); RINOK(SeekFromCur(stream, startOffset)); RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)); { UInt64 totalSize = Xz_GetPackSize(p); UInt64 sum = XZ_STREAM_HEADER_SIZE + totalSize + indexSize; if (totalSize == XZ_SIZE_OVERFLOW || sum >= ((UInt64)1 << 63) || totalSize >= ((UInt64)1 << 63)) return SZ_ERROR_ARCHIVE; *startOffset = -(Int64)sum; RINOK(SeekFromCur(stream, startOffset)); } { CXzStreamFlags headerFlags; CSecToRead secToRead; SecToRead_CreateVTable(&secToRead); secToRead.realStream = stream; RINOK(Xz_ReadHeader(&headerFlags, &secToRead.s)); return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; } } /* ---------- Xz Streams ---------- */ void Xzs_Construct(CXzs *p) { p->num = p->numAllocated = 0; p->streams = 0; } void Xzs_Free(CXzs *p, ISzAlloc *alloc) { size_t i; for (i = 0; i < p->num; i++) Xz_Free(&p->streams[i], alloc); alloc->Free(alloc, p->streams); p->num = p->numAllocated = 0; p->streams = 0; } UInt64 Xzs_GetNumBlocks(const CXzs *p) { UInt64 num = 0; size_t i; for (i = 0; i < p->num; i++) num += p->streams[i].numBlocks; return num; } UInt64 Xzs_GetUnpackSize(const CXzs *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->num; i++) ADD_SIZE_CHECH(size, Xz_GetUnpackSize(&p->streams[i])); return size; } /* UInt64 Xzs_GetPackSize(const CXzs *p) { UInt64 size = 0; size_t i; for (i = 0; i < p->num; i++) ADD_SIZE_CHECH(size, Xz_GetTotalSize(&p->streams[i])); return size; } */ SRes Xzs_ReadBackward(CXzs *p, ILookInStream *stream, Int64 *startOffset, ICompressProgress *progress, ISzAlloc *alloc) { Int64 endOffset = 0; RINOK(stream->Seek(stream, &endOffset, SZ_SEEK_END)); *startOffset = endOffset; for (;;) { CXzStream st; SRes res; Xz_Construct(&st); res = Xz_ReadBackward(&st, stream, startOffset, alloc); st.startOffset = *startOffset; RINOK(res); if (p->num == p->numAllocated) { size_t newNum = p->num + p->num / 4 + 1; Byte *data = (Byte *)alloc->Alloc(alloc, newNum * sizeof(CXzStream)); if (data == 0) return SZ_ERROR_MEM; p->numAllocated = newNum; if (p->num != 0) memcpy(data, p->streams, p->num * sizeof(CXzStream)); alloc->Free(alloc, p->streams); p->streams = (CXzStream *)data; } p->streams[p->num++] = st; if (*startOffset == 0) break; RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET)); if (progress && progress->Progress(progress, endOffset - *startOffset, (UInt64)(Int64)-1) != SZ_OK) return SZ_ERROR_PROGRESS; } return SZ_OK; } src/libs/7zip/win/CPP/000077500000000000000000000000001325366651500147225ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/000077500000000000000000000000001325366651500156135ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/7zip.pri000066400000000000000000000003511325366651500172170ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/ICoder.h \ $$7ZIP_BASE/CPP/7zip/IDecl.h \ $$7ZIP_BASE/CPP/7zip/IPassword.h \ $$7ZIP_BASE/CPP/7zip/IProgress.h \ $$7ZIP_BASE/CPP/7zip/IStream.h \ $$7ZIP_BASE/CPP/7zip/PropID.h src/libs/7zip/win/CPP/7zip/Archive/000077500000000000000000000000001325366651500171745ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/Archive/7z/000077500000000000000000000000001325366651500175345ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/Archive/7z/7z.pri000066400000000000000000000027241325366651500206150ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Archive/7z/7zCompressionMode.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zDecode.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zEncode.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderInStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderOutStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHandler.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHeader.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zIn.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zItem.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zOut.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zProperties.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zSpecStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zUpdate.h \ $$7ZIP_BASE/CPP/7zip/Archive/7z/StdAfx.h SOURCES += $$7ZIP_BASE/CPP/7zip/Archive/7z/7zDecode.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zEncode.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zExtract.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderInStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zFolderOutStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHandler.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHandlerOut.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zHeader.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zIn.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zOut.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zProperties.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zSpecStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/7z/7zUpdate.cpp src/libs/7zip/win/CPP/7zip/Archive/7z/7zCompressionMode.h000066400000000000000000000016521325366651500233000ustar00rootroot00000000000000// 7zCompressionMode.h #ifndef __7Z_COMPRESSION_MODE_H #define __7Z_COMPRESSION_MODE_H #include "../../Common/MethodId.h" #include "../../Common/MethodProps.h" namespace NArchive { namespace N7z { struct CMethodFull: public CProps { CMethodId Id; UInt32 NumInStreams; UInt32 NumOutStreams; bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } }; struct CBind { UInt32 InCoder; UInt32 InStream; UInt32 OutCoder; UInt32 OutStream; }; struct CCompressionMethodMode { CObjectVector Methods; CRecordVector Binds; #ifndef _7ZIP_ST UInt32 NumThreads; #endif bool PasswordIsDefined; UString Password; bool IsEmpty() const { return (Methods.IsEmpty() && !PasswordIsDefined); } CCompressionMethodMode(): PasswordIsDefined(false) #ifndef _7ZIP_ST , NumThreads(1) #endif {} }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.cpp000066400000000000000000000252651325366651500217160ustar00rootroot00000000000000// 7zDecode.cpp #include "StdAfx.h" #include "../../Common/LimitedStreams.h" #include "../../Common/LockedStream.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "7zDecode.h" namespace NArchive { namespace N7z { static void ConvertFolderItemInfoToBindInfo(const CFolder &folder, CBindInfoEx &bindInfo) { bindInfo.Clear(); bindInfo.BindPairs.ClearAndSetSize(folder.BindPairs.Size()); unsigned i; for (i = 0; i < folder.BindPairs.Size(); i++) { NCoderMixer::CBindPair &bindPair = bindInfo.BindPairs[i]; bindPair.InIndex = (UInt32)folder.BindPairs[i].InIndex; bindPair.OutIndex = (UInt32)folder.BindPairs[i].OutIndex; } bindInfo.Coders.ClearAndSetSize(folder.Coders.Size()); bindInfo.CoderMethodIDs.ClearAndSetSize(folder.Coders.Size()); UInt32 outStreamIndex = 0; for (i = 0; i < folder.Coders.Size(); i++) { NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i]; const CCoderInfo &coderInfo = folder.Coders[i]; coderStreamsInfo.NumInStreams = (UInt32)coderInfo.NumInStreams; coderStreamsInfo.NumOutStreams = (UInt32)coderInfo.NumOutStreams; bindInfo.CoderMethodIDs[i] = coderInfo.MethodID; for (UInt32 j = 0; j < coderStreamsInfo.NumOutStreams; j++, outStreamIndex++) if (folder.FindBindPairForOutStream(outStreamIndex) < 0) bindInfo.OutStreams.Add(outStreamIndex); } bindInfo.InStreams.ClearAndSetSize(folder.PackStreams.Size()); for (i = 0; i < folder.PackStreams.Size(); i++) bindInfo.InStreams[i] = (UInt32)folder.PackStreams[i]; } static bool AreCodersEqual(const NCoderMixer::CCoderStreamsInfo &a1, const NCoderMixer::CCoderStreamsInfo &a2) { return (a1.NumInStreams == a2.NumInStreams) && (a1.NumOutStreams == a2.NumOutStreams); } static bool AreBindPairsEqual(const NCoderMixer::CBindPair &a1, const NCoderMixer::CBindPair &a2) { return (a1.InIndex == a2.InIndex) && (a1.OutIndex == a2.OutIndex); } static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2) { if (a1.Coders.Size() != a2.Coders.Size()) return false; unsigned i; for (i = 0; i < a1.Coders.Size(); i++) if (!AreCodersEqual(a1.Coders[i], a2.Coders[i])) return false; if (a1.BindPairs.Size() != a2.BindPairs.Size()) return false; for (i = 0; i < a1.BindPairs.Size(); i++) if (!AreBindPairsEqual(a1.BindPairs[i], a2.BindPairs[i])) return false; for (i = 0; i < a1.CoderMethodIDs.Size(); i++) if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i]) return false; if (a1.InStreams.Size() != a2.InStreams.Size()) return false; if (a1.OutStreams.Size() != a2.OutStreams.Size()) return false; return true; } CDecoder::CDecoder(bool multiThread) { #ifndef _ST_MODE multiThread = true; #endif _multiThread = multiThread; _bindInfoExPrevIsDefined = false; } HRESULT CDecoder::Decode( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, UInt64 startPos, const CFolders &folders, int folderIndex, ISequentialOutStream *outStream, ICompressProgressInfo *compressProgress _7Z_DECODER_CRYPRO_VARS_DECL #if !defined(_7ZIP_ST) && !defined(_SFX) , bool mtMode, UInt32 numThreads #endif ) { const UInt64 *packPositions = &folders.PackPositions[folders.FoStartPackStreamIndex[folderIndex]]; CFolder folderInfo; folders.ParseFolderInfo(folderIndex, folderInfo); if (!folderInfo.CheckStructure(folders.GetNumFolderUnpackSizes(folderIndex))) return E_NOTIMPL; /* We don't need to init isEncrypted and passwordIsDefined We must upgrade them only #ifndef _NO_CRYPTO isEncrypted = false; passwordIsDefined = false; #endif */ CObjectVector< CMyComPtr > inStreams; CLockedInStream lockedInStream; lockedInStream.Init(inStream); for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++) { CLockedSequentialInStreamImp *lockedStreamImpSpec = new CLockedSequentialInStreamImp; CMyComPtr lockedStreamImp = lockedStreamImpSpec; lockedStreamImpSpec->Init(&lockedInStream, startPos + packPositions[j]); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr inStream = streamSpec; streamSpec->SetStream(lockedStreamImp); streamSpec->Init(packPositions[j + 1] - packPositions[j]); inStreams.Add(inStream); } unsigned numCoders = folderInfo.Coders.Size(); CBindInfoEx bindInfo; ConvertFolderItemInfoToBindInfo(folderInfo, bindInfo); bool createNewCoders; if (!_bindInfoExPrevIsDefined) createNewCoders = true; else createNewCoders = !AreBindInfoExEqual(bindInfo, _bindInfoExPrev); if (createNewCoders) { unsigned i; _decoders.Clear(); // _decoders2.Clear(); _mixerCoder.Release(); if (_multiThread) { _mixerCoderMTSpec = new NCoderMixer::CCoderMixer2MT; _mixerCoder = _mixerCoderMTSpec; _mixerCoderCommon = _mixerCoderMTSpec; } else { #ifdef _ST_MODE _mixerCoderSTSpec = new NCoderMixer::CCoderMixer2ST; _mixerCoder = _mixerCoderSTSpec; _mixerCoderCommon = _mixerCoderSTSpec; #endif } RINOK(_mixerCoderCommon->SetBindInfo(bindInfo)); for (i = 0; i < numCoders; i++) { const CCoderInfo &coderInfo = folderInfo.Coders[i]; CMyComPtr decoder; CMyComPtr decoder2; RINOK(CreateCoder( EXTERNAL_CODECS_LOC_VARS coderInfo.MethodID, decoder, decoder2, false)); CMyComPtr decoderUnknown; if (coderInfo.IsSimpleCoder()) { if (decoder == 0) return E_NOTIMPL; decoderUnknown = (IUnknown *)decoder; if (_multiThread) _mixerCoderMTSpec->AddCoder(decoder); #ifdef _ST_MODE else _mixerCoderSTSpec->AddCoder(decoder, false); #endif } else { if (decoder2 == 0) return E_NOTIMPL; decoderUnknown = (IUnknown *)decoder2; if (_multiThread) _mixerCoderMTSpec->AddCoder2(decoder2); #ifdef _ST_MODE else _mixerCoderSTSpec->AddCoder2(decoder2, false); #endif } _decoders.Add(decoderUnknown); #ifdef EXTERNAL_CODECS CMyComPtr setCompressCodecsInfo; decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs)); } #endif } _bindInfoExPrev = bindInfo; _bindInfoExPrevIsDefined = true; } unsigned i; _mixerCoderCommon->ReInit(); UInt32 packStreamIndex = 0; UInt32 unpackStreamIndexStart = folders.FoToCoderUnpackSizes[folderIndex]; UInt32 unpackStreamIndex = unpackStreamIndexStart; UInt32 coderIndex = 0; // UInt32 coder2Index = 0; for (i = 0; i < numCoders; i++) { const CCoderInfo &coderInfo = folderInfo.Coders[i]; CMyComPtr &decoder = _decoders[coderIndex]; { CMyComPtr setDecoderProperties; decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); if (setDecoderProperties) { const CByteBuffer &props = coderInfo.Props; size_t size = props.Size(); if (size > 0xFFFFFFFF) return E_NOTIMPL; // if (size > 0) { RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size)); } } } #if !defined(_7ZIP_ST) && !defined(_SFX) if (mtMode) { CMyComPtr setCoderMt; decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); if (setCoderMt) { RINOK(setCoderMt->SetNumberOfThreads(numThreads)); } } #endif #ifndef _NO_CRYPTO { CMyComPtr cryptoSetPassword; decoder.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); if (cryptoSetPassword) { isEncrypted = true; if (!getTextPassword) return E_NOTIMPL; CMyComBSTR passwordBSTR; RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR)); passwordIsDefined = true; size_t len = 0; if (passwordBSTR) len = MyStringLen((BSTR)passwordBSTR); CByteBuffer buffer(len * 2); for (size_t i = 0; i < len; i++) { wchar_t c = passwordBSTR[i]; ((Byte *)buffer)[i * 2] = (Byte)c; ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); } RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size())); } } #endif coderIndex++; UInt32 numInStreams = (UInt32)coderInfo.NumInStreams; UInt32 numOutStreams = (UInt32)coderInfo.NumOutStreams; CObjArray packSizes(numInStreams); CObjArray packSizesPointers(numInStreams); CObjArray unpackSizesPointers(numOutStreams); UInt32 j; for (j = 0; j < numOutStreams; j++, unpackStreamIndex++) unpackSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndex]; for (j = 0; j < numInStreams; j++, packStreamIndex++) { int bindPairIndex = folderInfo.FindBindPairForInStream(packStreamIndex); if (bindPairIndex >= 0) packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + (UInt32)folderInfo.BindPairs[bindPairIndex].OutIndex]; else { int index = folderInfo.FindPackStreamArrayIndex(packStreamIndex); if (index < 0) return S_FALSE; // check it packSizes[j] = packPositions[index + 1] - packPositions[index]; packSizesPointers[j] = &packSizes[j]; } } _mixerCoderCommon->SetCoderInfo(i, packSizesPointers, unpackSizesPointers); } UInt32 mainCoder, temp; bindInfo.FindOutStream(bindInfo.OutStreams[0], mainCoder, temp); if (_multiThread) _mixerCoderMTSpec->SetProgressCoderIndex(mainCoder); /* else _mixerCoderSTSpec->SetProgressCoderIndex(mainCoder);; */ if (numCoders == 0) return 0; unsigned num = inStreams.Size(); CObjArray inStreamPointers(num); for (i = 0; i < num; i++) inStreamPointers[i] = inStreams[i]; ISequentialOutStream *outStreamPointer = outStream; return _mixerCoder->Code( inStreamPointers, NULL, num, &outStreamPointer, NULL, 1, compressProgress); } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.h000066400000000000000000000026501325366651500213540ustar00rootroot00000000000000// 7zDecode.h #ifndef __7Z_DECODE_H #define __7Z_DECODE_H #include "../../IStream.h" #include "../../IPassword.h" #include "../Common/CoderMixer2.h" #include "../Common/CoderMixer2MT.h" #ifdef _ST_MODE #include "../Common/CoderMixer2ST.h" #endif #include "../../Common/CreateCoder.h" #include "7zIn.h" namespace NArchive { namespace N7z { struct CBindInfoEx: public NCoderMixer::CBindInfo { CRecordVector CoderMethodIDs; void Clear() { CBindInfo::Clear(); CoderMethodIDs.Clear(); } }; class CDecoder { bool _bindInfoExPrevIsDefined; CBindInfoEx _bindInfoExPrev; bool _multiThread; #ifdef _ST_MODE NCoderMixer::CCoderMixer2ST *_mixerCoderSTSpec; #endif NCoderMixer::CCoderMixer2MT *_mixerCoderMTSpec; NCoderMixer::CCoderMixer2 *_mixerCoderCommon; CMyComPtr _mixerCoder; CObjectVector > _decoders; // CObjectVector > _decoders2; public: CDecoder(bool multiThread); HRESULT Decode( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, UInt64 startPos, const CFolders &folders, int folderIndex, ISequentialOutStream *outStream, ICompressProgressInfo *compressProgress _7Z_DECODER_CRYPRO_VARS_DECL #if !defined(_7ZIP_ST) && !defined(_SFX) , bool mtMode, UInt32 numThreads #endif ); }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.cpp000066400000000000000000000345141325366651500217250ustar00rootroot00000000000000// 7zEncode.cpp #include "StdAfx.h" #include "../../Common/CreateCoder.h" #include "../../Common/FilterCoder.h" #include "../../Common/LimitedStreams.h" #include "../../Common/InOutTempBuffer.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "7zEncode.h" #include "7zSpecStream.h" static const UInt64 k_Delta = 0x03; static const UInt64 k_BCJ = 0x03030103; static const UInt64 k_BCJ2 = 0x0303011B; namespace NArchive { namespace N7z { static void ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo &bindInfo, const CRecordVector decompressionMethods, CFolder &folder) { // bindInfo.CoderMethodIDs.Clear(); // folder.OutStreams.Clear(); folder.BindPairs.SetSize(bindInfo.BindPairs.Size()); unsigned i; for (i = 0; i < bindInfo.BindPairs.Size(); i++) { CBindPair &bp = folder.BindPairs[i]; const NCoderMixer::CBindPair &mixerBp = bindInfo.BindPairs[i]; bp.InIndex = mixerBp.InIndex; bp.OutIndex = mixerBp.OutIndex; } folder.Coders.SetSize(bindInfo.Coders.Size()); for (i = 0; i < bindInfo.Coders.Size(); i++) { CCoderInfo &coderInfo = folder.Coders[i]; const NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i]; coderInfo.NumInStreams = coderStreamsInfo.NumInStreams; coderInfo.NumOutStreams = coderStreamsInfo.NumOutStreams; coderInfo.MethodID = decompressionMethods[i]; // coderInfo.Props can be nonFree; } folder.PackStreams.SetSize(bindInfo.InStreams.Size()); for (i = 0; i < bindInfo.InStreams.Size(); i++) folder.PackStreams[i] = bindInfo.InStreams[i]; } static HRESULT SetCoderProps2(const CProps &props, const UInt64 *dataSizeReduce, IUnknown *coder) { CMyComPtr setCoderProperties; coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties); if (setCoderProperties) return props.SetCoderProps(setCoderProperties, dataSizeReduce); return props.AreThereNonOptionalProps() ? E_INVALIDARG : S_OK; } HRESULT CEncoder::CreateMixerCoder( DECL_EXTERNAL_CODECS_LOC_VARS const UInt64 *inSizeForReduce) { _mixerCoderSpec = new NCoderMixer::CCoderMixer2MT; _mixerCoder = _mixerCoderSpec; RINOK(_mixerCoderSpec->SetBindInfo(_bindInfo)); FOR_VECTOR (i, _options.Methods) { const CMethodFull &methodFull = _options.Methods[i]; CCoderInfo &encodingInfo = _codersInfo.AddNew(); encodingInfo.MethodID = methodFull.Id; CMyComPtr encoder; CMyComPtr encoder2; RINOK(CreateCoder( EXTERNAL_CODECS_LOC_VARS methodFull.Id, encoder, encoder2, true)); if (!encoder && !encoder2) return E_FAIL; CMyComPtr encoderCommon = encoder ? (IUnknown *)encoder : (IUnknown *)encoder2; #ifndef _7ZIP_ST { CMyComPtr setCoderMt; encoderCommon.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); if (setCoderMt) { RINOK(setCoderMt->SetNumberOfThreads(_options.NumThreads)); } } #endif RINOK(SetCoderProps2(methodFull, inSizeForReduce, encoderCommon)); /* CMyComPtr resetSalt; encoderCommon.QueryInterface(IID_ICryptoResetSalt, (void **)&resetSalt); if (resetSalt) { resetSalt->ResetSalt(); } */ #ifdef EXTERNAL_CODECS CMyComPtr setCompressCodecsInfo; encoderCommon.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs)); } #endif CMyComPtr cryptoSetPassword; encoderCommon.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); if (cryptoSetPassword) { const UInt32 sizeInBytes = _options.Password.Len() * 2; CByteBuffer buffer(sizeInBytes); for (unsigned i = 0; i < _options.Password.Len(); i++) { wchar_t c = _options.Password[i]; ((Byte *)buffer)[i * 2] = (Byte)c; ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); } RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); } if (encoder) _mixerCoderSpec->AddCoder(encoder); else _mixerCoderSpec->AddCoder2(encoder2); } return S_OK; } HRESULT CEncoder::Encode( DECL_EXTERNAL_CODECS_LOC_VARS ISequentialInStream *inStream, const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, CFolder &folderItem, CRecordVector &coderUnpackSizes, UInt64 &unpackSize, ISequentialOutStream *outStream, CRecordVector &packSizes, ICompressProgressInfo *compressProgress) { RINOK(EncoderConstr()); if (!_mixerCoderSpec) { RINOK(CreateMixerCoder(EXTERNAL_CODECS_LOC_VARS inSizeForReduce)); } _mixerCoderSpec->ReInit(); // _mixerCoderSpec->SetCoderInfo(0, NULL, NULL, progress); CObjectVector inOutTempBuffers; CObjectVector tempBufferSpecs; CObjectVector > tempBuffers; unsigned numMethods = _bindInfo.Coders.Size(); unsigned i; for (i = 1; i < _bindInfo.OutStreams.Size(); i++) { CInOutTempBuffer &iotb = inOutTempBuffers.AddNew(); iotb.Create(); iotb.InitWriting(); } for (i = 1; i < _bindInfo.OutStreams.Size(); i++) { CSequentialOutTempBufferImp *tempBufferSpec = new CSequentialOutTempBufferImp; CMyComPtr tempBuffer = tempBufferSpec; tempBufferSpec->Init(&inOutTempBuffers[i - 1]); tempBuffers.Add(tempBuffer); tempBufferSpecs.Add(tempBufferSpec); } for (i = 0; i < numMethods; i++) _mixerCoderSpec->SetCoderInfo(i, NULL, NULL); if (_bindInfo.InStreams.IsEmpty()) return E_FAIL; UInt32 mainCoderIndex, mainStreamIndex; _bindInfo.FindInStream(_bindInfo.InStreams[0], mainCoderIndex, mainStreamIndex); if (inStreamSize) { CRecordVector sizePointers; for (UInt32 i = 0; i < _bindInfo.Coders[mainCoderIndex].NumInStreams; i++) if (i == mainStreamIndex) sizePointers.Add(inStreamSize); else sizePointers.Add(NULL); _mixerCoderSpec->SetCoderInfo(mainCoderIndex, &sizePointers.Front(), NULL); } // UInt64 outStreamStartPos; // RINOK(stream->Seek(0, STREAM_SEEK_CUR, &outStreamStartPos)); CSequentialInStreamSizeCount2 *inStreamSizeCountSpec = new CSequentialInStreamSizeCount2; CMyComPtr inStreamSizeCount = inStreamSizeCountSpec; CSequentialOutStreamSizeCount *outStreamSizeCountSpec = NULL; CMyComPtr outStreamSizeCount; inStreamSizeCountSpec->Init(inStream); CRecordVector inStreamPointers; CRecordVector outStreamPointers; inStreamPointers.Add(inStreamSizeCount); if (_bindInfo.OutStreams.Size() != 0) { outStreamSizeCountSpec = new CSequentialOutStreamSizeCount; outStreamSizeCount = outStreamSizeCountSpec; outStreamSizeCountSpec->SetStream(outStream); outStreamSizeCountSpec->Init(); outStreamPointers.Add(outStreamSizeCount); } for (i = 1; i < _bindInfo.OutStreams.Size(); i++) outStreamPointers.Add(tempBuffers[i - 1]); for (i = 0; i < _codersInfo.Size(); i++) { CCoderInfo &encodingInfo = _codersInfo[i]; CMyComPtr resetInitVector; _mixerCoderSpec->_coders[i].QueryInterface(IID_ICryptoResetInitVector, (void **)&resetInitVector); if (resetInitVector) { resetInitVector->ResetInitVector(); } CMyComPtr writeCoderProperties; _mixerCoderSpec->_coders[i].QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties); if (writeCoderProperties) { CDynBufSeqOutStream *outStreamSpec = new CDynBufSeqOutStream; CMyComPtr outStream(outStreamSpec); outStreamSpec->Init(); writeCoderProperties->WriteCoderProperties(outStream); outStreamSpec->CopyToBuffer(encodingInfo.Props); } } UInt32 progressIndex = mainCoderIndex; for (i = 0; i + 1 < _codersInfo.Size(); i++) { UInt64 m = _codersInfo[i].MethodID; if (m == k_Delta || m == k_BCJ || m == k_BCJ2) progressIndex = i + 1; } _mixerCoderSpec->SetProgressCoderIndex(progressIndex); RINOK(_mixerCoder->Code(&inStreamPointers.Front(), NULL, 1, &outStreamPointers.Front(), NULL, outStreamPointers.Size(), compressProgress)); ConvertBindInfoToFolderItemInfo(_decompressBindInfo, _decompressionMethods, folderItem); if (_bindInfo.OutStreams.Size() != 0) packSizes.Add(outStreamSizeCountSpec->GetSize()); for (i = 1; i < _bindInfo.OutStreams.Size(); i++) { CInOutTempBuffer &inOutTempBuffer = inOutTempBuffers[i - 1]; RINOK(inOutTempBuffer.WriteToStream(outStream)); packSizes.Add(inOutTempBuffer.GetDataSize()); } unpackSize = 0; for (i = 0; i < (int)_bindReverseConverter->NumSrcInStreams; i++) { int binder = _bindInfo.FindBinderForInStream( _bindReverseConverter->DestOutToSrcInMap[i]); UInt64 streamSize; if (binder < 0) { streamSize = inStreamSizeCountSpec->GetSize(); unpackSize = streamSize; } else streamSize = _mixerCoderSpec->GetWriteProcessedSize(binder); coderUnpackSizes.Add(streamSize); } for (i = 0; i < numMethods; i++) folderItem.Coders[numMethods - 1 - i].Props = _codersInfo[i].Props; return S_OK; } CEncoder::CEncoder(const CCompressionMethodMode &options): _bindReverseConverter(0), _constructed(false) { if (options.IsEmpty()) throw 1; _options = options; _mixerCoderSpec = NULL; } HRESULT CEncoder::EncoderConstr() { if (_constructed) return S_OK; if (_options.Methods.IsEmpty()) { // it has only password method; if (!_options.PasswordIsDefined) throw 1; if (!_options.Binds.IsEmpty()) throw 1; NCoderMixer::CCoderStreamsInfo coderStreamsInfo; CMethodFull method; method.NumInStreams = 1; method.NumOutStreams = 1; coderStreamsInfo.NumInStreams = 1; coderStreamsInfo.NumOutStreams = 1; method.Id = k_AES; _options.Methods.Add(method); _bindInfo.Coders.Add(coderStreamsInfo); _bindInfo.InStreams.Add(0); _bindInfo.OutStreams.Add(0); } else { UInt32 numInStreams = 0, numOutStreams = 0; unsigned i; for (i = 0; i < _options.Methods.Size(); i++) { const CMethodFull &methodFull = _options.Methods[i]; NCoderMixer::CCoderStreamsInfo coderStreamsInfo; coderStreamsInfo.NumInStreams = methodFull.NumOutStreams; coderStreamsInfo.NumOutStreams = methodFull.NumInStreams; if (_options.Binds.IsEmpty()) { if (i < _options.Methods.Size() - 1) { NCoderMixer::CBindPair bindPair; bindPair.InIndex = numInStreams + coderStreamsInfo.NumInStreams; bindPair.OutIndex = numOutStreams; _bindInfo.BindPairs.Add(bindPair); } else if (coderStreamsInfo.NumOutStreams != 0) _bindInfo.OutStreams.Insert(0, numOutStreams); for (UInt32 j = 1; j < coderStreamsInfo.NumOutStreams; j++) _bindInfo.OutStreams.Add(numOutStreams + j); } numInStreams += coderStreamsInfo.NumInStreams; numOutStreams += coderStreamsInfo.NumOutStreams; _bindInfo.Coders.Add(coderStreamsInfo); } if (!_options.Binds.IsEmpty()) { for (i = 0; i < _options.Binds.Size(); i++) { NCoderMixer::CBindPair bindPair; const CBind &bind = _options.Binds[i]; bindPair.InIndex = _bindInfo.GetCoderInStreamIndex(bind.InCoder) + bind.InStream; bindPair.OutIndex = _bindInfo.GetCoderOutStreamIndex(bind.OutCoder) + bind.OutStream; _bindInfo.BindPairs.Add(bindPair); } for (i = 0; i < (int)numOutStreams; i++) if (_bindInfo.FindBinderForOutStream(i) == -1) _bindInfo.OutStreams.Add(i); } for (i = 0; i < (int)numInStreams; i++) if (_bindInfo.FindBinderForInStream(i) == -1) _bindInfo.InStreams.Add(i); if (_bindInfo.InStreams.IsEmpty()) throw 1; // this is error // Make main stream first in list int inIndex = _bindInfo.InStreams[0]; for (;;) { UInt32 coderIndex, coderStreamIndex; _bindInfo.FindInStream(inIndex, coderIndex, coderStreamIndex); UInt32 outIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex); int binder = _bindInfo.FindBinderForOutStream(outIndex); if (binder >= 0) { inIndex = _bindInfo.BindPairs[binder].InIndex; continue; } for (i = 0; i < _bindInfo.OutStreams.Size(); i++) if (_bindInfo.OutStreams[i] == outIndex) { _bindInfo.OutStreams.Delete(i); _bindInfo.OutStreams.Insert(0, outIndex); break; } break; } if (_options.PasswordIsDefined) { unsigned numCryptoStreams = _bindInfo.OutStreams.Size(); for (i = 0; i < numCryptoStreams; i++) { NCoderMixer::CBindPair bindPair; bindPair.InIndex = numInStreams + i; bindPair.OutIndex = _bindInfo.OutStreams[i]; _bindInfo.BindPairs.Add(bindPair); } _bindInfo.OutStreams.Clear(); /* if (numCryptoStreams == 0) numCryptoStreams = 1; */ for (i = 0; i < numCryptoStreams; i++) { NCoderMixer::CCoderStreamsInfo coderStreamsInfo; CMethodFull method; method.NumInStreams = 1; method.NumOutStreams = 1; coderStreamsInfo.NumInStreams = method.NumOutStreams; coderStreamsInfo.NumOutStreams = method.NumInStreams; method.Id = k_AES; _options.Methods.Add(method); _bindInfo.Coders.Add(coderStreamsInfo); _bindInfo.OutStreams.Add(numOutStreams + i); } } } for (int i = _options.Methods.Size() - 1; i >= 0; i--) { const CMethodFull &methodFull = _options.Methods[i]; _decompressionMethods.Add(methodFull.Id); } _bindReverseConverter = new NCoderMixer::CBindReverseConverter(_bindInfo); _bindReverseConverter->CreateReverseBindInfo(_decompressBindInfo); _constructed = true; return S_OK; } CEncoder::~CEncoder() { delete _bindReverseConverter; } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.h000066400000000000000000000026121325366651500213640ustar00rootroot00000000000000// 7zEncode.h #ifndef __7Z_ENCODE_H #define __7Z_ENCODE_H // #include "../../Common/StreamObjects.h" #include "7zCompressionMode.h" #include "../Common/CoderMixer2.h" #include "../Common/CoderMixer2MT.h" #ifdef _ST_MODE #include "../Common/CoderMixer2ST.h" #endif #include "7zItem.h" #include "../../Common/CreateCoder.h" namespace NArchive { namespace N7z { class CEncoder { NCoderMixer::CCoderMixer2MT *_mixerCoderSpec; CMyComPtr _mixerCoder; CObjectVector _codersInfo; CCompressionMethodMode _options; NCoderMixer::CBindInfo _bindInfo; NCoderMixer::CBindInfo _decompressBindInfo; NCoderMixer::CBindReverseConverter *_bindReverseConverter; CRecordVector _decompressionMethods; HRESULT CreateMixerCoder(DECL_EXTERNAL_CODECS_LOC_VARS const UInt64 *inSizeForReduce); bool _constructed; public: CEncoder(const CCompressionMethodMode &options); ~CEncoder(); HRESULT EncoderConstr(); HRESULT Encode( DECL_EXTERNAL_CODECS_LOC_VARS ISequentialInStream *inStream, const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, CFolder &folderItem, CRecordVector &coderUnpackSizes, UInt64 &unpackSize, ISequentialOutStream *outStream, CRecordVector &packSizes, ICompressProgressInfo *compressProgress); }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zExtract.cpp000066400000000000000000000156171325366651500221450ustar00rootroot00000000000000// 7zExtract.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../Common/ProgressUtils.h" #include "7zDecode.h" // #include "7z1Decode.h" #include "7zFolderOutStream.h" #include "7zHandler.h" namespace NArchive { namespace N7z { struct CExtractFolderInfo { #ifdef _7Z_VOL int VolumeIndex; #endif CNum FileIndex; CNum FolderIndex; CBoolVector ExtractStatuses; UInt64 UnpackSize; CExtractFolderInfo( #ifdef _7Z_VOL int volumeIndex, #endif CNum fileIndex, CNum folderIndex): #ifdef _7Z_VOL VolumeIndex(volumeIndex), #endif FileIndex(fileIndex), FolderIndex(folderIndex), UnpackSize(0) { if (fileIndex != kNumNoIndex) { ExtractStatuses.ClearAndSetSize(1); ExtractStatuses[0] = true; } }; }; STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec) { COM_TRY_BEGIN bool testMode = (testModeSpec != 0); CMyComPtr extractCallback = extractCallbackSpec; UInt64 importantTotalUnpacked = 0; bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = #ifdef _7Z_VOL _refs.Size(); #else _db.Files.Size(); #endif if (numItems == 0) return S_OK; /* if (_volumes.Size() != 1) return E_FAIL; const CVolume &volume = _volumes.Front(); const CDbEx &_db = volume.Database; IInStream *_inStream = volume.Stream; */ CObjectVector extractFolderInfoVector; for (UInt32 ii = 0; ii < numItems; ii++) { // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex]; UInt32 ref2Index = allFilesMode ? ii : indices[ii]; // const CRef2 &ref2 = _refs[ref2Index]; // for (UInt32 ri = 0; ri < ref2.Refs.Size(); ri++) { #ifdef _7Z_VOL // const CRef &ref = ref2.Refs[ri]; const CRef &ref = _refs[ref2Index]; int volumeIndex = ref.VolumeIndex; const CVolume &volume = _volumes[volumeIndex]; const CDbEx &db = volume.Database; UInt32 fileIndex = ref.ItemIndex; #else const CDbEx &db = _db; UInt32 fileIndex = ref2Index; #endif CNum folderIndex = db.FileIndexToFolderIndexMap[fileIndex]; if (folderIndex == kNumNoIndex) { extractFolderInfoVector.Add(CExtractFolderInfo( #ifdef _7Z_VOL volumeIndex, #endif fileIndex, kNumNoIndex)); continue; } if (extractFolderInfoVector.IsEmpty() || folderIndex != extractFolderInfoVector.Back().FolderIndex #ifdef _7Z_VOL || volumeIndex != extractFolderInfoVector.Back().VolumeIndex #endif ) { extractFolderInfoVector.Add(CExtractFolderInfo( #ifdef _7Z_VOL volumeIndex, #endif kNumNoIndex, folderIndex)); UInt64 unpackSize = db.GetFolderUnpackSize(folderIndex); importantTotalUnpacked += unpackSize; extractFolderInfoVector.Back().UnpackSize = unpackSize; } CExtractFolderInfo &efi = extractFolderInfoVector.Back(); // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex]; CNum startIndex = db.FolderStartFileIndex[folderIndex]; for (CNum index = efi.ExtractStatuses.Size(); index <= fileIndex - startIndex; index++) { // UInt64 unpackSize = _db.Files[startIndex + index].UnpackSize; // Count partial_folder_size // efi.UnpackSize += unpackSize; // importantTotalUnpacked += unpackSize; efi.ExtractStatuses.Add(index == fileIndex - startIndex); } } } RINOK(extractCallback->SetTotal(importantTotalUnpacked)); CDecoder decoder( #ifdef _ST_MODE false #else true #endif ); // CDecoder1 decoder; UInt64 totalPacked = 0; UInt64 totalUnpacked = 0; UInt64 curPacked, curUnpacked; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); for (unsigned i = 0;; i++, totalUnpacked += curUnpacked, totalPacked += curPacked) { lps->OutSize = totalUnpacked; lps->InSize = totalPacked; RINOK(lps->SetCur()); if (i >= extractFolderInfoVector.Size()) break; const CExtractFolderInfo &efi = extractFolderInfoVector[i]; curUnpacked = efi.UnpackSize; curPacked = 0; CFolderOutStream *folderOutStream = new CFolderOutStream; CMyComPtr outStream(folderOutStream); #ifdef _7Z_VOL const CVolume &volume = _volumes[efi.VolumeIndex]; const CDbEx &db = volume.Database; #else const CDbEx &db = _db; #endif CNum startIndex; if (efi.FileIndex != kNumNoIndex) startIndex = efi.FileIndex; else startIndex = db.FolderStartFileIndex[efi.FolderIndex]; HRESULT result = folderOutStream->Init(&db, #ifdef _7Z_VOL volume.StartRef2Index, #else 0, #endif startIndex, &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0); RINOK(result); if (efi.FileIndex != kNumNoIndex) continue; CNum folderIndex = efi.FolderIndex; curPacked = _db.GetFolderFullPackSize(folderIndex); #ifndef _NO_CRYPTO CMyComPtr getTextPassword; if (extractCallback) extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); #endif try { #ifndef _NO_CRYPTO bool isEncrypted = false; bool passwordIsDefined = false; #endif HRESULT result = decoder.Decode( EXTERNAL_CODECS_VARS #ifdef _7Z_VOL volume.Stream, #else _inStream, #endif db.ArcInfo.DataStartPosition, db, folderIndex, outStream, progress _7Z_DECODER_CRYPRO_VARS #if !defined(_7ZIP_ST) && !defined(_SFX) , true, _numThreads #endif ); if (result == S_FALSE) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } if (result == E_NOTIMPL) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kUnsupportedMethod)); continue; } if (result != S_OK) return result; if (folderOutStream->WasWritingFinished() != S_OK) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } } catch(...) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } } return S_OK; COM_TRY_END } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.cpp000066400000000000000000000055651325366651500234120ustar00rootroot00000000000000// 7zFolderInStream.cpp #include "StdAfx.h" #include "7zFolderInStream.h" namespace NArchive { namespace N7z { CFolderInStream::CFolderInStream() { _inStreamWithHashSpec = new CSequentialInStreamWithCRC; _inStreamWithHash = _inStreamWithHashSpec; } void CFolderInStream::Init(IArchiveUpdateCallback *updateCallback, const UInt32 *fileIndices, UInt32 numFiles) { _updateCallback = updateCallback; _numFiles = numFiles; _fileIndex = 0; _fileIndices = fileIndices; Processed.Clear(); CRCs.Clear(); Sizes.Clear(); _fileIsOpen = false; _currentSizeIsDefined = false; } HRESULT CFolderInStream::OpenStream() { _filePos = 0; while (_fileIndex < _numFiles) { CMyComPtr stream; HRESULT result = _updateCallback->GetStream(_fileIndices[_fileIndex], &stream); if (result != S_OK && result != S_FALSE) return result; _fileIndex++; _inStreamWithHashSpec->SetStream(stream); _inStreamWithHashSpec->Init(); if (stream) { _fileIsOpen = true; CMyComPtr streamGetSize; stream.QueryInterface(IID_IStreamGetSize, &streamGetSize); if (streamGetSize) { RINOK(streamGetSize->GetSize(&_currentSize)); _currentSizeIsDefined = true; } return S_OK; } RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); Sizes.Add(0); Processed.Add(result == S_OK); AddDigest(); } return S_OK; } void CFolderInStream::AddDigest() { CRCs.Add(_inStreamWithHashSpec->GetCRC()); } HRESULT CFolderInStream::CloseStream() { RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); _inStreamWithHashSpec->ReleaseStream(); _fileIsOpen = false; _currentSizeIsDefined = false; Processed.Add(true); Sizes.Add(_filePos); AddDigest(); return S_OK; } STDMETHODIMP CFolderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != 0) *processedSize = 0; while (size > 0) { if (_fileIsOpen) { UInt32 processed2; RINOK(_inStreamWithHash->Read(data, size, &processed2)); if (processed2 == 0) { RINOK(CloseStream()); continue; } if (processedSize != 0) *processedSize = processed2; _filePos += processed2; break; } if (_fileIndex >= _numFiles) break; RINOK(OpenStream()); } return S_OK; } STDMETHODIMP CFolderInStream::GetSubStreamSize(UInt64 subStream, UInt64 *value) { *value = 0; unsigned index2 = (unsigned)subStream; if (subStream > Sizes.Size()) return E_FAIL; if (index2 < Sizes.Size()) { *value = Sizes[index2]; return S_OK; } if (!_currentSizeIsDefined) return S_FALSE; *value = _currentSize; return S_OK; } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.h000066400000000000000000000024751325366651500230540ustar00rootroot00000000000000// 7zFolderInStream.h #ifndef __7Z_FOLDER_IN_STREAM_H #define __7Z_FOLDER_IN_STREAM_H #include "../../ICoder.h" #include "../IArchive.h" #include "../Common/InStreamWithCRC.h" #include "7zItem.h" namespace NArchive { namespace N7z { class CFolderInStream: public ISequentialInStream, public ICompressGetSubStreamSize, public CMyUnknownImp { CSequentialInStreamWithCRC *_inStreamWithHashSpec; CMyComPtr _inStreamWithHash; CMyComPtr _updateCallback; bool _currentSizeIsDefined; bool _fileIsOpen; UInt64 _currentSize; UInt64 _filePos; const UInt32 *_fileIndices; UInt32 _numFiles; UInt32 _fileIndex; HRESULT OpenStream(); HRESULT CloseStream(); void AddDigest(); public: CRecordVector Processed; CRecordVector CRCs; CRecordVector Sizes; MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); CFolderInStream(); void Init(IArchiveUpdateCallback *updateCallback, const UInt32 *fileIndices, UInt32 numFiles); UInt64 GetFullSize() const { UInt64 size = 0; FOR_VECTOR (i, Sizes) size += Sizes[i]; return size; } }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.cpp000066400000000000000000000073721325366651500236110ustar00rootroot00000000000000// 7zFolderOutStream.cpp #include "StdAfx.h" #include "7zFolderOutStream.h" namespace NArchive { namespace N7z { CFolderOutStream::CFolderOutStream() { _crcStreamSpec = new COutStreamWithCRC; _crcStream = _crcStreamSpec; } HRESULT CFolderOutStream::Init( const CDbEx *db, UInt32 ref2Offset, UInt32 startIndex, const CBoolVector *extractStatuses, IArchiveExtractCallback *extractCallback, bool testMode, bool checkCrc) { _db = db; _ref2Offset = ref2Offset; _startIndex = startIndex; _extractStatuses = extractStatuses; _extractCallback = extractCallback; _testMode = testMode; _checkCrc = checkCrc; _currentIndex = 0; _fileIsOpen = false; return ProcessEmptyFiles(); } HRESULT CFolderOutStream::OpenFile() { Int32 askMode = ((*_extractStatuses)[_currentIndex]) ? (_testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract) : NExtract::NAskMode::kSkip; CMyComPtr realOutStream; UInt32 index = _startIndex + _currentIndex; RINOK(_extractCallback->GetStream(_ref2Offset + index, &realOutStream, askMode)); _crcStreamSpec->SetStream(realOutStream); _crcStreamSpec->Init(_checkCrc); _fileIsOpen = true; const CFileItem &fi = _db->Files[index]; _rem = fi.Size; if (askMode == NExtract::NAskMode::kExtract && !realOutStream && !_db->IsItemAnti(index) && !fi.IsDir) askMode = NExtract::NAskMode::kSkip; return _extractCallback->PrepareOperation(askMode); } HRESULT CFolderOutStream::CloseFileAndSetResult(Int32 res) { _crcStreamSpec->ReleaseStream(); _fileIsOpen = false; _currentIndex++; return _extractCallback->SetOperationResult(res); } HRESULT CFolderOutStream::CloseFileAndSetResult() { const CFileItem &fi = _db->Files[_startIndex + _currentIndex]; return CloseFileAndSetResult( (fi.IsDir || !fi.CrcDefined || !_checkCrc || fi.Crc == _crcStreamSpec->GetCRC()) ? NExtract::NOperationResult::kOK : NExtract::NOperationResult::kCRCError); } HRESULT CFolderOutStream::ProcessEmptyFiles() { while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0) { RINOK(OpenFile()); RINOK(CloseFileAndSetResult()); } return S_OK; } STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size != 0) { if (_fileIsOpen) { UInt32 cur = size < _rem ? size : (UInt32)_rem; RINOK(_crcStream->Write(data, cur, &cur)); if (cur == 0) break; data = (const Byte *)data + cur; size -= cur; _rem -= cur; if (processedSize != NULL) *processedSize += cur; if (_rem == 0) { RINOK(CloseFileAndSetResult()); RINOK(ProcessEmptyFiles()); continue; } } else { RINOK(ProcessEmptyFiles()); if (_currentIndex == _extractStatuses->Size()) { // we support partial extracting if (processedSize != NULL) *processedSize += size; break; } RINOK(OpenFile()); } } return S_OK; } STDMETHODIMP CFolderOutStream::GetSubStreamSize(UInt64 subStream, UInt64 *value) { *value = 0; if ((int)subStream >= _extractStatuses->Size()) return S_FALSE; *value = _db->Files[_startIndex + (int)subStream].Size; return S_OK; } HRESULT CFolderOutStream::FlushCorrupted(Int32 resultEOperationResult) { while (_currentIndex < _extractStatuses->Size()) { if (_fileIsOpen) { RINOK(CloseFileAndSetResult(resultEOperationResult)); } else { RINOK(OpenFile()); } } return S_OK; } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.h000066400000000000000000000027311325366651500232500ustar00rootroot00000000000000// 7zFolderOutStream.h #ifndef __7Z_FOLDER_OUT_STREAM_H #define __7Z_FOLDER_OUT_STREAM_H #include "../../IStream.h" #include "../IArchive.h" #include "../Common/OutStreamWithCRC.h" #include "7zIn.h" namespace NArchive { namespace N7z { class CFolderOutStream: public ISequentialOutStream, public ICompressGetSubStreamSize, public CMyUnknownImp { COutStreamWithCRC *_crcStreamSpec; CMyComPtr _crcStream; const CDbEx *_db; const CBoolVector *_extractStatuses; CMyComPtr _extractCallback; UInt32 _ref2Offset; UInt32 _startIndex; unsigned _currentIndex; bool _testMode; bool _checkCrc; bool _fileIsOpen; UInt64 _rem; HRESULT OpenFile(); HRESULT CloseFileAndSetResult(Int32 res); HRESULT CloseFileAndSetResult(); HRESULT ProcessEmptyFiles(); public: MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) CFolderOutStream(); STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); HRESULT Init( const CDbEx *db, UInt32 ref2Offset, UInt32 startIndex, const CBoolVector *extractStatuses, IArchiveExtractCallback *extractCallback, bool testMode, bool checkCrc); HRESULT FlushCorrupted(Int32 resultEOperationResult); HRESULT WasWritingFinished() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; } }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.cpp000066400000000000000000000457341325366651500221130ustar00rootroot00000000000000// 7zHandler.cpp #include "StdAfx.h" #include "../../../../C/CpuArch.h" #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #ifndef __7Z_SET_PROPERTIES #include "../../../Windows/System.h" #endif #include "../Common/ItemNameUtils.h" #include "7zHandler.h" #include "7zProperties.h" #ifdef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY #include "../Common/ParseProperties.h" #endif #endif using namespace NWindows; using namespace NCOM; namespace NArchive { namespace N7z { CHandler::CHandler() { #ifndef _NO_CRYPTO _isEncrypted = false; _passwordIsDefined = false; #endif #ifdef EXTRACT_ONLY _crcSize = 4; #ifdef __7Z_SET_PROPERTIES _numThreads = NSystem::GetNumberOfProcessors(); #endif #endif } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _db.Files.Size(); return S_OK; } #ifdef _SFX IMP_IInArchive_ArcProps_NO_Table STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) { *numProps = 0; return S_OK; } STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */, BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */) { return E_NOTIMPL; } #else static const Byte kArcProps[] = { kpidHeadersSize, kpidMethod, kpidSolid, kpidNumBlocks // , kpidIsTree }; IMP_IInArchive_ArcProps static inline char GetHex(unsigned value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } static unsigned ConvertMethodIdToString_Back(char *s, UInt64 id) { int len = 0; do { s[--len] = GetHex((unsigned)id & 0xF); id >>= 4; s[--len] = GetHex((unsigned)id & 0xF); id >>= 4; } while (id != 0); return (unsigned)-len; } static void ConvertMethodIdToString(AString &res, UInt64 id) { const unsigned kLen = 32; char s[kLen]; unsigned len = kLen - 1; s[len] = 0; res += s + len - ConvertMethodIdToString_Back(s + len, id); } static unsigned GetStringForSizeValue(char *s, UInt32 val) { unsigned i; for (i = 0; i <= 31; i++) if (((UInt32)1 << i) == val) { if (i < 10) { s[0] = (char)('0' + i); s[1] = 0; return 1; } if (i < 20) { s[0] = '1'; s[1] = (char)('0' + i - 10); } else if (i < 30) { s[0] = '2'; s[1] = (char)('0' + i - 20); } else { s[0] = '3'; s[1] = (char)('0' + i - 30); } s[2] = 0; return 2; } char c = 'b'; if ((val & ((1 << 20) - 1)) == 0) { val >>= 20; c = 'm'; } else if ((val & ((1 << 10) - 1)) == 0) { val >>= 10; c = 'k'; } ::ConvertUInt32ToString(val, s); unsigned pos = MyStringLen(s); s[pos++] = c; s[pos] = 0; return pos; } /* static inline void AddHexToString(UString &res, Byte value) { res += GetHex((Byte)(value >> 4)); res += GetHex((Byte)(value & 0xF)); } */ static char *AddProp32(char *s, const char *name, UInt32 v) { *s++ = ':'; s = MyStpCpy(s, name); ::ConvertUInt32ToString(v, s); return s + MyStringLen(s); } void CHandler::AddMethodName(AString &s, UInt64 id) { UString methodName; FindMethod(EXTERNAL_CODECS_VARS id, methodName); if (methodName.IsEmpty()) { for (unsigned i = 0; i < methodName.Len(); i++) if (methodName[i] >= 0x80) { methodName.Empty(); break; } } if (methodName.IsEmpty()) ConvertMethodIdToString(s, id); else for (unsigned i = 0; i < methodName.Len(); i++) s += (char)methodName[i]; } #endif STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { #ifndef _SFX COM_TRY_BEGIN #endif NCOM::CPropVariant prop; switch (propID) { #ifndef _SFX case kpidMethod: { AString s; const CParsedMethods &pm = _db.ParsedMethods; FOR_VECTOR (i, pm.IDs) { UInt64 id = pm.IDs[i]; if (!s.IsEmpty()) s += ' '; char temp[16]; if (id == k_LZMA2) { s += "LZMA2:"; if ((pm.Lzma2Prop & 1) == 0) ConvertUInt32ToString((pm.Lzma2Prop >> 1) + 12, temp); else GetStringForSizeValue(temp, 3 << ((pm.Lzma2Prop >> 1) + 11)); s += temp; } else if (id == k_LZMA) { s += "LZMA:"; GetStringForSizeValue(temp, pm.LzmaDic); s += temp; } else AddMethodName(s, id); } prop = s; break; } case kpidSolid: prop = _db.IsSolid(); break; case kpidNumBlocks: prop = (UInt32)_db.NumFolders; break; case kpidHeadersSize: prop = _db.HeadersSize; break; case kpidPhySize: prop = _db.PhySize; break; case kpidOffset: if (_db.ArcInfo.StartPosition != 0) prop = _db.ArcInfo.StartPosition; break; /* case kpidIsTree: if (_db.IsTree) prop = true; break; case kpidIsAltStream: if (_db.ThereAreAltStreams) prop = true; break; case kpidIsAux: if (_db.IsTree) prop = true; break; */ // case kpidError: if (_db.ThereIsHeaderError) prop = "Header error"; break; #endif case kpidWarningFlags: { UInt32 v = 0; if (_db.StartHeaderWasRecovered) v |= kpv_ErrorFlags_HeadersError; if (_db.UnsupportedFeatureWarning) v |= kpv_ErrorFlags_UnsupportedFeature; if (v != 0) prop = v; break; } case kpidErrorFlags: { UInt32 v = 0; if (!_db.IsArc) v |= kpv_ErrorFlags_IsNotArc; if (_db.ThereIsHeaderError) v |= kpv_ErrorFlags_HeadersError; if (_db.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd; // if (_db.UnsupportedVersion) v |= kpv_ErrorFlags_Unsupported; if (_db.UnsupportedFeatureError) v |= kpv_ErrorFlags_UnsupportedFeature; prop = v; break; } } prop.Detach(value); return S_OK; #ifndef _SFX COM_TRY_END #endif } static void SetFileTimeProp_From_UInt64Def(PROPVARIANT *prop, const CUInt64DefVector &v, int index) { UInt64 value; if (v.GetItem(index, value)) PropVarEm_Set_FileTime64(prop, value); } bool CHandler::IsFolderEncrypted(CNum folderIndex) const { if (folderIndex == kNumNoIndex) return false; size_t startPos = _db.FoCodersDataOffset[folderIndex]; const Byte *p = _db.CodersData + startPos; size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos; CInByte2 inByte; inByte.Init(p, size); CNum numCoders = inByte.ReadNum(); for (; numCoders != 0; numCoders--) { Byte mainByte = inByte.ReadByte(); unsigned idSize = (mainByte & 0xF); const Byte *longID = inByte.GetPtr(); UInt64 id64 = 0; for (unsigned j = 0; j < idSize; j++) id64 = ((id64 << 8) | longID[j]); inByte.SkipDataNoCheck(idSize); if (id64 == k_AES) return true; if ((mainByte & 0x20) != 0) inByte.SkipDataNoCheck(inByte.ReadNum()); } return false; } STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps) { *numProps = 0; return S_OK; } STDMETHODIMP CHandler::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) { *name = NULL; *propID = kpidNtSecure; return S_OK; } STDMETHODIMP CHandler::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) { /* const CFileItem &file = _db.Files[index]; *parentType = (file.IsAltStream ? NParentType::kAltStream : NParentType::kDir); *parent = (UInt32)(Int32)file.Parent; */ *parentType = NParentType::kDir; *parent = (UInt32)(Int32)-1; return S_OK; } STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) { *data = NULL; *dataSize = 0; *propType = 0; if (/* _db.IsTree && propID == kpidName || !_db.IsTree && */ propID == kpidPath) { if (_db.NameOffsets && _db.NamesBuf) { size_t offset = _db.NameOffsets[index]; size_t size = (_db.NameOffsets[index + 1] - offset) * 2; if (size < ((UInt32)1 << 31)) { *data = (const void *)(_db.NamesBuf + offset * 2); *dataSize = (UInt32)size; *propType = NPropDataType::kUtf16z; } } return S_OK; } /* if (propID == kpidNtSecure) { if (index < (UInt32)_db.SecureIDs.Size()) { int id = _db.SecureIDs[index]; size_t offs = _db.SecureOffsets[id]; size_t size = _db.SecureOffsets[id + 1] - offs; if (size >= 0) { *data = _db.SecureBuf + offs; *dataSize = (UInt32)size; *propType = NPropDataType::kRaw; } } } */ return S_OK; } #ifndef _SFX HRESULT CHandler::SetMethodToProp(CNum folderIndex, PROPVARIANT *prop) const { PropVariant_Clear(prop); if (folderIndex == kNumNoIndex) return S_OK; // for (int ttt = 0; ttt < 1; ttt++) { const unsigned kTempSize = 256; char temp[kTempSize]; unsigned pos = kTempSize; temp[--pos] = 0; size_t startPos = _db.FoCodersDataOffset[folderIndex]; const Byte *p = _db.CodersData + startPos; size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos; CInByte2 inByte; inByte.Init(p, size); // numCoders == 0 ??? CNum numCoders = inByte.ReadNum(); bool needSpace = false; for (; numCoders != 0; numCoders--, needSpace = true) { if (pos < 32) // max size of property break; Byte mainByte = inByte.ReadByte(); unsigned idSize = (mainByte & 0xF); const Byte *longID = inByte.GetPtr(); UInt64 id64 = 0; for (unsigned j = 0; j < idSize; j++) id64 = ((id64 << 8) | longID[j]); inByte.SkipDataNoCheck(idSize); if ((mainByte & 0x10) != 0) { inByte.ReadNum(); // NumInStreams inByte.ReadNum(); // NumOutStreams } CNum propsSize = 0; const Byte *props = NULL; if ((mainByte & 0x20) != 0) { propsSize = inByte.ReadNum(); props = inByte.GetPtr(); inByte.SkipDataNoCheck(propsSize); } const char *name = NULL; char s[32]; s[0] = 0; if (id64 <= (UInt32)0xFFFFFFFF) { UInt32 id = (UInt32)id64; if (id == k_LZMA) { name = "LZMA"; if (propsSize == 5) { UInt32 dicSize = GetUi32((const Byte *)props + 1); char *dest = s + GetStringForSizeValue(s, dicSize); UInt32 d = props[0]; if (d != 0x5D) { UInt32 lc = d % 9; d /= 9; UInt32 pb = d / 5; UInt32 lp = d % 5; if (lc != 3) dest = AddProp32(dest, "lc", lc); if (lp != 0) dest = AddProp32(dest, "lp", lp); if (pb != 2) dest = AddProp32(dest, "pb", pb); } } } else if (id == k_LZMA2) { name = "LZMA2"; if (propsSize == 1) { Byte p = props[0]; if ((p & 1) == 0) ConvertUInt32ToString((UInt32)((p >> 1) + 12), s); else GetStringForSizeValue(s, 3 << ((p >> 1) + 11)); } } else if (id == k_PPMD) { name = "PPMD"; if (propsSize == 5) { Byte order = *props; char *dest = s; *dest++ = 'o'; ConvertUInt32ToString(order, dest); dest += MyStringLen(dest); dest = MyStpCpy(dest, ":mem"); GetStringForSizeValue(dest, GetUi32(props + 1)); } } else if (id == k_Delta) { name = "Delta"; if (propsSize == 1) ConvertUInt32ToString((UInt32)props[0] + 1, s); } else if (id == k_BCJ2) name = "BCJ2"; else if (id == k_BCJ) name = "BCJ"; else if (id == k_AES) { name = "7zAES"; if (propsSize >= 1) { Byte firstByte = props[0]; UInt32 numCyclesPower = firstByte & 0x3F; ConvertUInt32ToString(numCyclesPower, s); } } } if (name) { unsigned nameLen = MyStringLen(name); unsigned propsLen = MyStringLen(s); unsigned totalLen = nameLen + propsLen; if (propsLen != 0) totalLen++; if (needSpace) totalLen++; if (totalLen + 5 >= pos) break; pos -= totalLen; MyStringCopy(temp + pos, name); if (propsLen != 0) { char *dest = temp + pos + nameLen; *dest++ = ':'; MyStringCopy(dest, s); } if (needSpace) temp[pos + totalLen - 1] = ' '; } else { UString methodName; FindMethod(EXTERNAL_CODECS_VARS id64, methodName); if (methodName.IsEmpty()) { for (unsigned j = 0; j < methodName.Len(); j++) if (methodName[j] >= 0x80) { methodName.Empty(); break; } } if (needSpace) temp[--pos] = ' '; if (methodName.IsEmpty()) pos -= ConvertMethodIdToString_Back(temp + pos, id64); else { unsigned len = methodName.Len(); if (len + 5 > pos) break; pos -= len; for (unsigned i = 0; i < len; i++) temp[pos + i] = (char)methodName[i]; } } } if (numCoders != 0 && pos >= 4) { temp[--pos] = ' '; temp[--pos] = '.'; temp[--pos] = '.'; temp[--pos] = '.'; } return PropVarEm_Set_Str(prop, temp + pos); // } } #endif STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { PropVariant_Clear(value); // COM_TRY_BEGIN // NCOM::CPropVariant prop; /* const CRef2 &ref2 = _refs[index]; if (ref2.Refs.IsEmpty()) return E_FAIL; const CRef &ref = ref2.Refs.Front(); */ const CFileItem &item = _db.Files[index]; UInt32 index2 = index; switch(propID) { case kpidIsDir: PropVarEm_Set_Bool(value, item.IsDir); break; case kpidSize: { PropVarEm_Set_UInt64(value, item.Size); // prop = ref2.Size; break; } case kpidPackSize: { // prop = ref2.PackSize; { CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) { if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2) PropVarEm_Set_UInt64(value, _db.GetFolderFullPackSize(folderIndex)); /* else PropVarEm_Set_UInt64(value, 0); */ } else PropVarEm_Set_UInt64(value, 0); } break; } // case kpidIsAux: prop = _db.IsItemAux(index2); break; case kpidPosition: { UInt64 v; if (_db.StartPos.GetItem(index2, v)) PropVarEm_Set_UInt64(value, v); break; } case kpidCTime: SetFileTimeProp_From_UInt64Def(value, _db.CTime, index2); break; case kpidATime: SetFileTimeProp_From_UInt64Def(value, _db.ATime, index2); break; case kpidMTime: SetFileTimeProp_From_UInt64Def(value, _db.MTime, index2); break; case kpidAttrib: if (item.AttribDefined) PropVarEm_Set_UInt32(value, item.Attrib); break; case kpidCRC: if (item.CrcDefined) PropVarEm_Set_UInt32(value, item.Crc); break; case kpidEncrypted: PropVarEm_Set_Bool(value, IsFolderEncrypted(_db.FileIndexToFolderIndexMap[index2])); break; case kpidIsAnti: PropVarEm_Set_Bool(value, _db.IsItemAnti(index2)); break; /* case kpidIsAltStream: prop = item.IsAltStream; break; case kpidNtSecure: { int id = _db.SecureIDs[index]; size_t offs = _db.SecureOffsets[id]; size_t size = _db.SecureOffsets[id + 1] - offs; if (size >= 0) { prop.SetBlob(_db.SecureBuf + offs, (ULONG)size); } break; } */ case kpidPath: return _db.GetPath_Prop(index, value); #ifndef _SFX case kpidMethod: return SetMethodToProp(_db.FileIndexToFolderIndexMap[index2], value); case kpidBlock: { CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) PropVarEm_Set_UInt32(value, (UInt32)folderIndex); } break; case kpidPackedSize0: case kpidPackedSize1: case kpidPackedSize2: case kpidPackedSize3: case kpidPackedSize4: { /* CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; if (folderIndex != kNumNoIndex) { const CFolder &folderInfo = _db.Folders[folderIndex]; if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2 && folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0)) { prop = _db.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0); } else prop = (UInt64)0; } else prop = (UInt64)0; */ } break; #endif } // prop.Detach(value); return S_OK; // COM_TRY_END } STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openArchiveCallback) { COM_TRY_BEGIN Close(); #ifndef _SFX _fileInfoPopIDs.Clear(); #endif try { CMyComPtr openArchiveCallbackTemp = openArchiveCallback; #ifndef _NO_CRYPTO CMyComPtr getTextPassword; if (openArchiveCallback) openArchiveCallbackTemp.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); #endif CInArchive archive; _db.IsArc = false; RINOK(archive.Open(stream, maxCheckStartPosition)); _db.IsArc = true; HRESULT result = archive.ReadDatabase( EXTERNAL_CODECS_VARS _db #ifndef _NO_CRYPTO , getTextPassword, _isEncrypted, _passwordIsDefined #endif ); RINOK(result); _inStream = stream; } catch(...) { Close(); // return E_INVALIDARG; // we must return out_of_memory here return S_FALSE; } // _inStream = stream; #ifndef _SFX FillPopIDs(); #endif return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { COM_TRY_BEGIN _inStream.Release(); _db.Clear(); #ifndef _NO_CRYPTO _isEncrypted = false; _passwordIsDefined = false; #endif return S_OK; COM_TRY_END } #ifdef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { COM_TRY_BEGIN const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); _numThreads = numProcessors; for (UInt32 i = 0; i < numProps; i++) { UString name = names[i]; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; const PROPVARIANT &value = values[i]; UInt32 number; int index = ParseStringToUInt32(name, number); if (index == 0) { if (name.IsPrefixedBy(L"mt")) { RINOK(ParseMtProp(name.Ptr(2), value, numProcessors, _numThreads)); continue; } else return E_INVALIDARG; } } return S_OK; COM_TRY_END } #endif #endif IMPL_ISetCompressCodecsInfo }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.h000066400000000000000000000072511325366651500215500ustar00rootroot00000000000000// 7z/Handler.h #ifndef __7Z_HANDLER_H #define __7Z_HANDLER_H #include "../../ICoder.h" #include "../IArchive.h" #include "../../Common/CreateCoder.h" #ifndef EXTRACT_ONLY #include "../Common/HandlerOut.h" #endif #include "7zCompressionMode.h" #include "7zIn.h" namespace NArchive { namespace N7z { const UInt32 k_Copy = 0x0; const UInt32 k_Delta = 3; const UInt32 k_LZMA2 = 0x21; const UInt32 k_LZMA = 0x030101; const UInt32 k_PPMD = 0x030401; const UInt32 k_BCJ = 0x03030103; const UInt32 k_BCJ2 = 0x0303011B; const UInt32 k_Deflate = 0x040108; const UInt32 k_BZip2 = 0x040202; #ifndef __7Z_SET_PROPERTIES #ifdef EXTRACT_ONLY #if !defined(_7ZIP_ST) && !defined(_SFX) #define __7Z_SET_PROPERTIES #endif #else #define __7Z_SET_PROPERTIES #endif #endif #ifndef EXTRACT_ONLY class COutHandler: public CMultiMethodProps { HRESULT SetSolidFromString(const UString &s); HRESULT SetSolidFromPROPVARIANT(const PROPVARIANT &value); public: bool _removeSfxBlock; UInt64 _numSolidFiles; UInt64 _numSolidBytes; bool _numSolidBytesDefined; bool _solidExtension; bool _compressHeaders; bool _encryptHeadersSpecified; bool _encryptHeaders; // bool _useParents; 9.26 CBoolPair Write_CTime; CBoolPair Write_ATime; CBoolPair Write_MTime; bool _volumeMode; void InitSolidFiles() { _numSolidFiles = (UInt64)(Int64)(-1); } void InitSolidSize() { _numSolidBytes = (UInt64)(Int64)(-1); } void InitSolid() { InitSolidFiles(); InitSolidSize(); _solidExtension = false; _numSolidBytesDefined = false; } void InitProps(); COutHandler() { InitProps(); } HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); }; #endif class CHandler: public IInArchive, public IArchiveGetRawProps, #ifdef __7Z_SET_PROPERTIES public ISetProperties, #endif #ifndef EXTRACT_ONLY public IOutArchive, #endif PUBLIC_ISetCompressCodecsInfo public CMyUnknownImp #ifndef EXTRACT_ONLY , public COutHandler #endif { public: MY_QUERYINTERFACE_BEGIN2(IInArchive) MY_QUERYINTERFACE_ENTRY(IArchiveGetRawProps) #ifdef __7Z_SET_PROPERTIES MY_QUERYINTERFACE_ENTRY(ISetProperties) #endif #ifndef EXTRACT_ONLY MY_QUERYINTERFACE_ENTRY(IOutArchive) #endif QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_END MY_ADDREF_RELEASE INTERFACE_IInArchive(;) INTERFACE_IArchiveGetRawProps(;) #ifdef __7Z_SET_PROPERTIES STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps); #endif #ifndef EXTRACT_ONLY INTERFACE_IOutArchive(;) #endif DECL_ISetCompressCodecsInfo CHandler(); private: CMyComPtr _inStream; NArchive::N7z::CDbEx _db; #ifndef _NO_CRYPTO bool _isEncrypted; bool _passwordIsDefined; #endif #ifdef EXTRACT_ONLY #ifdef __7Z_SET_PROPERTIES UInt32 _numThreads; #endif UInt32 _crcSize; #else CRecordVector _binds; HRESULT PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m); HRESULT SetHeaderMethod(CCompressionMethodMode &headerMethod); void AddDefaultMethod(); HRESULT SetMainMethod(CCompressionMethodMode &method, CObjectVector &methodsInfo #ifndef _7ZIP_ST , UInt32 numThreads #endif ); #endif bool IsFolderEncrypted(CNum folderIndex) const; #ifndef _SFX CRecordVector _fileInfoPopIDs; void FillPopIDs(); void AddMethodName(AString &s, UInt64 id); HRESULT SetMethodToProp(CNum folderIndex, PROPVARIANT *prop) const; #endif DECL_EXTERNAL_CODECS_VARS }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandlerOut.cpp000066400000000000000000000557231325366651500226020ustar00rootroot00000000000000// 7zHandlerOut.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../../Common/StringToInt.h" #include "../../../Common/Wildcard.h" #include "../Common/ItemNameUtils.h" #include "../Common/ParseProperties.h" #include "7zHandler.h" #include "7zOut.h" #include "7zUpdate.h" using namespace NWindows; namespace NArchive { namespace N7z { static const wchar_t *k_LZMA_Name = L"LZMA"; static const wchar_t *kDefaultMethodName = L"LZMA2"; static const wchar_t *k_Copy_Name = L"Copy"; static const wchar_t *k_MatchFinder_ForHeaders = L"BT2"; static const UInt32 k_NumFastBytes_ForHeaders = 273; static const UInt32 k_Level_ForHeaders = 5; static const UInt32 k_Dictionary_ForHeaders = #ifdef UNDER_CE 1 << 18; #else 1 << 20; #endif STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) { *type = NFileTimeType::kWindows; return S_OK; } HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m) { if (!FindMethod( EXTERNAL_CODECS_VARS m.MethodName, dest.Id, dest.NumInStreams, dest.NumOutStreams)) return E_INVALIDARG; (CProps &)dest = (CProps &)m; return S_OK; } HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod) { if (!_compressHeaders) return S_OK; COneMethodInfo m; m.MethodName = k_LZMA_Name; m.AddPropString(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders); m.AddProp32(NCoderPropID::kLevel, k_Level_ForHeaders); m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders); m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders); m.AddNumThreadsProp(1); CMethodFull methodFull; RINOK(PropsMethod_To_FullMethod(methodFull, m)); headerMethod.Methods.Add(methodFull); return S_OK; } void CHandler::AddDefaultMethod() { FOR_VECTOR (i, _methods) { UString &methodName = _methods[i].MethodName; if (methodName.IsEmpty()) methodName = kDefaultMethodName; } if (_methods.IsEmpty()) { COneMethodInfo m; m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName); _methods.Add(m); } } HRESULT CHandler::SetMainMethod( CCompressionMethodMode &methodMode, CObjectVector &methods #ifndef _7ZIP_ST , UInt32 numThreads #endif ) { AddDefaultMethod(); const UInt64 kSolidBytes_Min = (1 << 24); const UInt64 kSolidBytes_Max = ((UInt64)1 << 32) - 1; bool needSolid = false; FOR_VECTOR (i, methods) { COneMethodInfo &oneMethodInfo = methods[i]; SetGlobalLevelAndThreads(oneMethodInfo #ifndef _7ZIP_ST , numThreads #endif ); CMethodFull methodFull; RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo)); methodMode.Methods.Add(methodFull); if (methodFull.Id != k_Copy) needSolid = true; if (_numSolidBytesDefined) continue; UInt32 dicSize; switch (methodFull.Id) { case k_LZMA: case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break; case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break; case k_Deflate: dicSize = (UInt32)1 << 15; break; case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break; default: continue; } _numSolidBytes = (UInt64)dicSize << 7; if (_numSolidBytes < kSolidBytes_Min) _numSolidBytes = kSolidBytes_Min; if (_numSolidBytes > kSolidBytes_Max) _numSolidBytes = kSolidBytes_Max; _numSolidBytesDefined = true; } if (!_numSolidBytesDefined) if (needSolid) _numSolidBytes = kSolidBytes_Max; else _numSolidBytes = 0; _numSolidBytesDefined = true; return S_OK; } static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, UInt64 &ft, bool &ftDefined) { // ft = 0; // ftDefined = false; NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(index, propID, &prop)); if (prop.vt == VT_FILETIME) { ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32); ftDefined = true; } else if (prop.vt != VT_EMPTY) return E_INVALIDARG; else { ft = 0; ftDefined = false; } return S_OK; } /* #ifdef _WIN32 static const wchar_t kDirDelimiter1 = L'\\'; #endif static const wchar_t kDirDelimiter2 = L'/'; static inline bool IsCharDirLimiter(wchar_t c) { return ( #ifdef _WIN32 c == kDirDelimiter1 || #endif c == kDirDelimiter2); } static int FillSortIndex(CObjectVector &treeFolders, int cur, int curSortIndex) { CTreeFolder &tf = treeFolders[cur]; tf.SortIndex = curSortIndex++; for (int i = 0; i < tf.SubFolders.Size(); i++) curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex); tf.SortIndexEnd = curSortIndex; return curSortIndex; } static int FindSubFolder(const CObjectVector &treeFolders, int cur, const UString &name, int &insertPos) { const CIntVector &subFolders = treeFolders[cur].SubFolders; int left = 0, right = subFolders.Size(); insertPos = -1; for (;;) { if (left == right) { insertPos = left; return -1; } int mid = (left + right) / 2; int midFolder = subFolders[mid]; int compare = CompareFileNames(name, treeFolders[midFolder].Name); if (compare == 0) return midFolder; if (compare < 0) right = mid; else left = mid + 1; } } static int AddFolder(CObjectVector &treeFolders, int cur, const UString &name) { int insertPos; int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos); if (folderIndex < 0) { folderIndex = treeFolders.Size(); CTreeFolder &newFolder = treeFolders.AddNew(); newFolder.Parent = cur; newFolder.Name = name; treeFolders[cur].SubFolders.Insert(insertPos, folderIndex); } // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234; return folderIndex; } */ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) { COM_TRY_BEGIN const CDbEx *db = 0; #ifdef _7Z_VOL if (_volumes.Size() > 1) return E_FAIL; const CVolume *volume = 0; if (_volumes.Size() == 1) { volume = &_volumes.Front(); db = &volume->Database; } #else if (_inStream != 0) db = &_db; #endif /* CMyComPtr getRawProps; updateCallback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps); CUniqBlocks secureBlocks; secureBlocks.AddUniq(NULL, 0); CObjectVector treeFolders; { CTreeFolder folder; folder.Parent = -1; treeFolders.Add(folder); } */ CObjectVector updateItems; bool need_CTime = (Write_CTime.Def && Write_CTime.Val); bool need_ATime = (Write_ATime.Def && Write_ATime.Val); bool need_MTime = (Write_MTime.Def && Write_MTime.Val || !Write_MTime.Def); if (db) { if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty(); if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty(); if (!Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty(); } UString s; for (UInt32 i = 0; i < numItems; i++) { Int32 newData, newProps; UInt32 indexInArchive; if (!updateCallback) return E_FAIL; RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive)); CUpdateItem ui; ui.NewProps = IntToBool(newProps); ui.NewData = IntToBool(newData); ui.IndexInArchive = indexInArchive; ui.IndexInClient = i; ui.IsAnti = false; ui.Size = 0; UString name; // bool isAltStream = false; if (ui.IndexInArchive != -1) { if (db == 0 || (unsigned)ui.IndexInArchive >= db->Files.Size()) return E_INVALIDARG; const CFileItem &fi = db->Files[ui.IndexInArchive]; if (!ui.NewProps) { _db.GetPath(ui.IndexInArchive, name); } ui.IsDir = fi.IsDir; ui.Size = fi.Size; // isAltStream = fi.IsAltStream; ui.IsAnti = db->IsItemAnti(ui.IndexInArchive); if (!ui.NewProps) { ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime); ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime); ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime); } } if (ui.NewProps) { bool folderStatusIsDefined; { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop)); if (prop.vt == VT_EMPTY) ui.AttribDefined = false; else if (prop.vt != VT_UI4) return E_INVALIDARG; else { ui.Attrib = prop.ulVal; ui.AttribDefined = true; } } // we need MTime to sort files. if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined)); if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined)); if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined)); /* if (getRawProps) { const void *data; UInt32 dataSize; UInt32 propType; getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType); if (dataSize != 0 && propType != NPropDataType::kRaw) return E_FAIL; ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize); } */ { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidPath, &prop)); if (prop.vt == VT_EMPTY) { } else if (prop.vt != VT_BSTR) return E_INVALIDARG; else { name = NItemName::MakeLegalName(prop.bstrVal); } } { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop)); if (prop.vt == VT_EMPTY) folderStatusIsDefined = false; else if (prop.vt != VT_BOOL) return E_INVALIDARG; else { ui.IsDir = (prop.boolVal != VARIANT_FALSE); folderStatusIsDefined = true; } } { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop)); if (prop.vt == VT_EMPTY) ui.IsAnti = false; else if (prop.vt != VT_BOOL) return E_INVALIDARG; else ui.IsAnti = (prop.boolVal != VARIANT_FALSE); } /* { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop)); if (prop.vt == VT_EMPTY) isAltStream = false; else if (prop.vt != VT_BOOL) return E_INVALIDARG; else isAltStream = (prop.boolVal != VARIANT_FALSE); } */ if (ui.IsAnti) { ui.AttribDefined = false; ui.CTimeDefined = false; ui.ATimeDefined = false; ui.MTimeDefined = false; ui.Size = 0; } if (!folderStatusIsDefined && ui.AttribDefined) ui.SetDirStatusFromAttrib(); } else { /* if (_db.SecureIDs.IsEmpty()) ui.SecureIndex = secureBlocks.AddUniq(NULL, 0); else { int id = _db.SecureIDs[ui.IndexInArchive]; size_t offs = _db.SecureOffsets[id]; size_t size = _db.SecureOffsets[id + 1] - offs; ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size); } */ } /* { int folderIndex = 0; if (_useParents) { int j; s.Empty(); for (j = 0; j < name.Len(); j++) { wchar_t c = name[j]; if (IsCharDirLimiter(c)) { folderIndex = AddFolder(treeFolders, folderIndex, s); s.Empty(); continue; } s += c; } if (isAltStream) { int colonPos = s.Find(':'); if (colonPos < 0) { // isAltStream = false; return E_INVALIDARG; } UString mainName = s.Left(colonPos); int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName); if (treeFolders[newFolderIndex].UpdateItemIndex < 0) { for (int j = updateItems.Size() - 1; j >= 0; j--) { CUpdateItem &ui2 = updateItems[j]; if (ui2.ParentFolderIndex == folderIndex && ui2.Name == mainName) { ui2.TreeFolderIndex = newFolderIndex; treeFolders[newFolderIndex].UpdateItemIndex = j; } } } folderIndex = newFolderIndex; s.Delete(0, colonPos + 1); } ui.Name = s; } else ui.Name = name; ui.IsAltStream = isAltStream; ui.ParentFolderIndex = folderIndex; ui.TreeFolderIndex = -1; if (ui.IsDir && !s.IsEmpty()) { ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s); treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size(); } } */ ui.Name = name; if (ui.NewData) { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(i, kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; ui.Size = (UInt64)prop.uhVal.QuadPart; if (ui.Size != 0 && ui.IsAnti) return E_INVALIDARG; } updateItems.Add(ui); } /* FillSortIndex(treeFolders, 0, 0); for (i = 0; i < (UInt32)updateItems.Size(); i++) { CUpdateItem &ui = updateItems[i]; ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex; ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd; } */ CCompressionMethodMode methodMode, headerMethod; HRESULT res = SetMainMethod(methodMode, _methods #ifndef _7ZIP_ST , _numThreads #endif ); RINOK(res); methodMode.Binds = _binds; RINOK(SetHeaderMethod(headerMethod)); #ifndef _7ZIP_ST methodMode.NumThreads = _numThreads; headerMethod.NumThreads = 1; #endif CMyComPtr getPassword2; updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2); methodMode.PasswordIsDefined = false; methodMode.Password.Empty(); if (getPassword2) { CMyComBSTR password; Int32 passwordIsDefined; RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password)); methodMode.PasswordIsDefined = IntToBool(passwordIsDefined); if (methodMode.PasswordIsDefined && (BSTR)password) methodMode.Password = password; } bool compressMainHeader = _compressHeaders; // check it bool encryptHeaders = false; if (methodMode.PasswordIsDefined) { if (_encryptHeadersSpecified) encryptHeaders = _encryptHeaders; #ifndef _NO_CRYPTO else encryptHeaders = _passwordIsDefined; #endif compressMainHeader = true; if (encryptHeaders) { headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined; headerMethod.Password = methodMode.Password; } } if (numItems < 2) compressMainHeader = false; CUpdateOptions options; options.Method = &methodMode; options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0; int level = GetLevel(); options.UseFilters = level != 0 && _autoFilter; options.MaxFilter = level >= 8; options.HeaderOptions.CompressMainHeader = compressMainHeader; /* options.HeaderOptions.WriteCTime = Write_CTime; options.HeaderOptions.WriteATime = Write_ATime; options.HeaderOptions.WriteMTime = Write_MTime; */ options.NumSolidFiles = _numSolidFiles; options.NumSolidBytes = _numSolidBytes; options.SolidExtension = _solidExtension; options.RemoveSfxBlock = _removeSfxBlock; options.VolumeMode = _volumeMode; COutArchive archive; CArchiveDatabaseOut newDatabase; CMyComPtr getPassword; updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword); /* if (secureBlocks.Sorted.Size() > 1) { secureBlocks.GetReverseMap(); for (int i = 0; i < updateItems.Size(); i++) { int &secureIndex = updateItems[i].SecureIndex; secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex]; } } */ res = Update( EXTERNAL_CODECS_VARS #ifdef _7Z_VOL volume ? volume->Stream: 0, volume ? db : 0, #else _inStream, db, #endif updateItems, // treeFolders, // secureBlocks, archive, newDatabase, outStream, updateCallback, options #ifndef _NO_CRYPTO , getPassword #endif ); RINOK(res); updateItems.ClearAndFree(); return archive.WriteDatabase(EXTERNAL_CODECS_VARS newDatabase, options.HeaderMethod, options.HeaderOptions); COM_TRY_END } static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream) { stream = 0; int index = ParseStringToUInt32(srcString, coder); if (index == 0) return E_INVALIDARG; srcString.Delete(0, index); if (srcString[0] == 's') { srcString.Delete(0); int index = ParseStringToUInt32(srcString, stream); if (index == 0) return E_INVALIDARG; srcString.Delete(0, index); } return S_OK; } void COutHandler::InitProps() { CMultiMethodProps::Init(); _removeSfxBlock = false; _compressHeaders = true; _encryptHeadersSpecified = false; _encryptHeaders = false; // _useParents = false; Write_CTime.Init(); Write_ATime.Init(); Write_MTime.Init(); _volumeMode = false; InitSolid(); } HRESULT COutHandler::SetSolidFromString(const UString &s) { UString s2 = s; s2.MakeLower_Ascii(); for (unsigned i = 0; i < s2.Len();) { const wchar_t *start = ((const wchar_t *)s2) + i; const wchar_t *end; UInt64 v = ConvertStringToUInt64(start, &end); if (start == end) { if (s2[i++] != 'e') return E_INVALIDARG; _solidExtension = true; continue; } i += (int)(end - start); if (i == s2.Len()) return E_INVALIDARG; wchar_t c = s2[i++]; if (c == 'f') { if (v < 1) v = 1; _numSolidFiles = v; } else { unsigned numBits; switch (c) { case 'b': numBits = 0; break; case 'k': numBits = 10; break; case 'm': numBits = 20; break; case 'g': numBits = 30; break; case 't': numBits = 40; break; default: return E_INVALIDARG; } _numSolidBytes = (v << numBits); _numSolidBytesDefined = true; } } return S_OK; } HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value) { bool isSolid; switch (value.vt) { case VT_EMPTY: isSolid = true; break; case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break; case VT_BSTR: if (StringToBool(value.bstrVal, isSolid)) break; return SetSolidFromString(value.bstrVal); default: return E_INVALIDARG; } if (isSolid) InitSolid(); else _numSolidFiles = 1; return S_OK; } static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest) { RINOK(PROPVARIANT_to_bool(prop, dest.Val)); dest.Def = true; return S_OK; } HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) { UString name = nameSpec; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; if (name[0] == L's') { name.Delete(0); if (name.IsEmpty()) return SetSolidFromPROPVARIANT(value); if (value.vt != VT_EMPTY) return E_INVALIDARG; return SetSolidFromString(name); } UInt32 number; int index = ParseStringToUInt32(name, number); UString realName = name.Ptr(index); if (index == 0) { if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock); if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders); // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents); if (name.IsEqualTo("hcf")) { bool compressHeadersFull = true; RINOK(PROPVARIANT_to_bool(value, compressHeadersFull)); return compressHeadersFull ? S_OK: E_INVALIDARG; } if (name.IsEqualTo("he")) { RINOK(PROPVARIANT_to_bool(value, _encryptHeaders)); _encryptHeadersSpecified = true; return S_OK; } if (name.IsEqualTo("tc")) return PROPVARIANT_to_BoolPair(value, Write_CTime); if (name.IsEqualTo("ta")) return PROPVARIANT_to_BoolPair(value, Write_ATime); if (name.IsEqualTo("tm")) return PROPVARIANT_to_BoolPair(value, Write_MTime); if (name.IsEqualTo("v")) return PROPVARIANT_to_bool(value, _volumeMode); } return CMultiMethodProps::SetProperty(name, value); } STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { COM_TRY_BEGIN _binds.Clear(); InitProps(); for (UInt32 i = 0; i < numProps; i++) { UString name = names[i]; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; const PROPVARIANT &value = values[i]; if (name[0] == 'b') { if (value.vt != VT_EMPTY) return E_INVALIDARG; name.Delete(0); CBind bind; RINOK(GetBindInfoPart(name, bind.OutCoder, bind.OutStream)); if (name[0] != ':') return E_INVALIDARG; name.Delete(0); RINOK(GetBindInfoPart(name, bind.InCoder, bind.InStream)); if (!name.IsEmpty()) return E_INVALIDARG; _binds.Add(bind); continue; } RINOK(SetProperty(name, value)); } unsigned numEmptyMethods = GetNumEmptyMethods(); if (numEmptyMethods > 0) { unsigned k; for (k = 0; k < _binds.Size(); k++) { const CBind &bind = _binds[k]; if (bind.InCoder < (UInt32)numEmptyMethods || bind.OutCoder < (UInt32)numEmptyMethods) return E_INVALIDARG; } for (k = 0; k < _binds.Size(); k++) { CBind &bind = _binds[k]; bind.InCoder -= (UInt32)numEmptyMethods; bind.OutCoder -= (UInt32)numEmptyMethods; } _methods.DeleteFrontal(numEmptyMethods); } AddDefaultMethod(); if (!_filterMethod.MethodName.IsEmpty()) { FOR_VECTOR (k, _binds) { CBind &bind = _binds[k]; bind.InCoder++; bind.OutCoder++; } _methods.Insert(0, _filterMethod); } FOR_VECTOR (k, _binds) { const CBind &bind = _binds[k]; if (bind.InCoder >= (UInt32)_methods.Size() || bind.OutCoder >= (UInt32)_methods.Size()) return E_INVALIDARG; } return S_OK; COM_TRY_END } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.cpp000066400000000000000000000007651325366651500217210ustar00rootroot00000000000000// 7zHeader.cpp #include "StdAfx.h" #include "7zHeader.h" namespace NArchive { namespace N7z { Byte kSignature[kSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; #ifdef _7Z_VOL Byte kFinishSignature[kSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C + 1}; #endif // We can change signature. So file doesn't contain correct signature. // struct SignatureInitializer { SignatureInitializer() { kSignature[0]--; } }; // static SignatureInitializer g_SignatureInitializer; }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.h000066400000000000000000000030721325366651500213600ustar00rootroot00000000000000// 7z/7zHeader.h #ifndef __7Z_HEADER_H #define __7Z_HEADER_H #include "../../../Common/MyTypes.h" namespace NArchive { namespace N7z { const unsigned kSignatureSize = 6; extern Byte kSignature[kSignatureSize]; // #define _7Z_VOL // 7z-MultiVolume is not finished yet. // It can work already, but I still do not like some // things of that new multivolume format. // So please keep it commented. #ifdef _7Z_VOL extern Byte kFinishSignature[kSignatureSize]; #endif struct CArchiveVersion { Byte Major; Byte Minor; }; const Byte kMajorVersion = 0; struct CStartHeader { UInt64 NextHeaderOffset; UInt64 NextHeaderSize; UInt32 NextHeaderCRC; }; const UInt32 kStartHeaderSize = 20; #ifdef _7Z_VOL struct CFinishHeader: public CStartHeader { UInt64 ArchiveStartOffset; // data offset from end if that struct UInt64 AdditionalStartBlockSize; // start signature & start header size }; const UInt32 kFinishHeaderSize = kStartHeaderSize + 16; #endif namespace NID { enum EEnum { kEnd, kHeader, kArchiveProperties, kAdditionalStreamsInfo, kMainStreamsInfo, kFilesInfo, kPackInfo, kUnpackInfo, kSubStreamsInfo, kSize, kCRC, kFolder, kCodersUnpackSize, kNumUnpackStream, kEmptyStream, kEmptyFile, kAnti, kName, kCTime, kATime, kMTime, kWinAttrib, kComment, kEncodedHeader, kStartPos, kDummy // kNtSecure, // kParent, // kIsAux }; } }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.cpp000066400000000000000000001251761325366651500211030ustar00rootroot00000000000000// 7zIn.cpp #include "StdAfx.h" #ifdef _WIN32 #include #else #include #endif #include "../../../../C/7zCrc.h" #include "../../../../C/CpuArch.h" #include "../../Common/StreamObjects.h" #include "../../Common/StreamUtils.h" #include "7zDecode.h" #include "7zIn.h" #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) #define Get64(p) GetUi64(p) // define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader #ifndef _SFX #define FORMAT_7Z_RECOVERY #endif using namespace NWindows; using namespace NCOM; namespace NArchive { namespace N7z { static const UInt32 k_LZMA2 = 0x21; static const UInt32 k_LZMA = 0x030101; static void BoolVector_Fill_False(CBoolVector &v, unsigned size) { v.ClearAndSetSize(size); bool *p = &v[0]; for (unsigned i = 0; i < size; i++) p[i] = false; } static bool BoolVector_GetAndSet(CBoolVector &v, UInt32 index) { if (index >= (UInt32)v.Size()) return true; bool res = v[index]; v[index] = true; return res; } bool CFolder::CheckStructure(unsigned numUnpackSizes) const { const unsigned kNumCodersMax = sizeof(UInt32) * 8; // don't change it const unsigned kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax const unsigned kNumBindsMax = 32; if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax) return false; { CBoolVector v; BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size()); unsigned i; for (i = 0; i < BindPairs.Size(); i++) if (BoolVector_GetAndSet(v, BindPairs[i].InIndex)) return false; for (i = 0; i < PackStreams.Size(); i++) if (BoolVector_GetAndSet(v, PackStreams[i])) return false; BoolVector_Fill_False(v, numUnpackSizes); for (i = 0; i < BindPairs.Size(); i++) if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex)) return false; } UInt32 mask[kMaskSize]; unsigned i; for (i = 0; i < kMaskSize; i++) mask[i] = 0; { CUIntVector inStreamToCoder, outStreamToCoder; for (i = 0; i < Coders.Size(); i++) { CNum j; const CCoderInfo &coder = Coders[i]; for (j = 0; j < coder.NumInStreams; j++) inStreamToCoder.Add(i); for (j = 0; j < coder.NumOutStreams; j++) outStreamToCoder.Add(i); } for (i = 0; i < BindPairs.Size(); i++) { const CBindPair &bp = BindPairs[i]; mask[inStreamToCoder[bp.InIndex]] |= (1 << outStreamToCoder[bp.OutIndex]); } } for (i = 0; i < kMaskSize; i++) for (unsigned j = 0; j < kMaskSize; j++) if (((1 << j) & mask[i]) != 0) mask[i] |= mask[j]; for (i = 0; i < kMaskSize; i++) if (((1 << i) & mask[i]) != 0) return false; return true; } class CInArchiveException {}; class CUnsupportedFeatureException: public CInArchiveException {}; static void ThrowException() { throw CInArchiveException(); } static inline void ThrowEndOfData() { ThrowException(); } static inline void ThrowUnsupported() { throw CUnsupportedFeatureException(); } static inline void ThrowIncorrect() { ThrowException(); } class CStreamSwitch { CInArchive *_archive; bool _needRemove; bool _needUpdatePos; public: CStreamSwitch(): _needRemove(false), _needUpdatePos(false) {} ~CStreamSwitch() { Remove(); } void Remove(); void Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos); void Set(CInArchive *archive, const CByteBuffer &byteBuffer); void Set(CInArchive *archive, const CObjectVector *dataVector); }; void CStreamSwitch::Remove() { if (_needRemove) { if (_archive->_inByteBack->GetRem() != 0) _archive->ThereIsHeaderError = true; _archive->DeleteByteStream(_needUpdatePos); _needRemove = false; } } void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos) { Remove(); _archive = archive; _archive->AddByteStream(data, size); _needRemove = true; _needUpdatePos = needUpdatePos; } void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer) { Set(archive, byteBuffer, byteBuffer.Size(), false); } void CStreamSwitch::Set(CInArchive *archive, const CObjectVector *dataVector) { Remove(); Byte external = archive->ReadByte(); if (external != 0) { CNum dataIndex = archive->ReadNum(); if (dataIndex >= dataVector->Size()) ThrowIncorrect(); Set(archive, (*dataVector)[dataIndex]); } } void CInArchive::AddByteStream(const Byte *buf, size_t size) { if (_numInByteBufs == kNumBufLevelsMax) ThrowIncorrect(); _inByteBack = &_inByteVector[_numInByteBufs++]; _inByteBack->Init(buf, size); } Byte CInByte2::ReadByte() { if (_pos >= _size) ThrowEndOfData(); return _buffer[_pos++]; } void CInByte2::ReadBytes(Byte *data, size_t size) { if (size > _size - _pos) ThrowEndOfData(); memcpy(data, _buffer + _pos, size); _pos += size; } void CInByte2::SkipData(UInt64 size) { if (size > _size - _pos) ThrowEndOfData(); _pos += (size_t)size; } void CInByte2::SkipData() { SkipData(ReadNumber()); } static UInt64 ReadNumberSpec(const Byte *p, size_t size, size_t &processed) { if (size == 0) { processed = 0; return 0; } Byte firstByte = *p++; size--; if ((firstByte & 0x80) == 0) { processed = 1; return firstByte; } Byte mask = 0x40; if (size == 0) { processed = 0; return 0; } UInt64 value = (UInt64)*p; p++; size--; for (unsigned i = 1; i < 8; i++) { if ((firstByte & mask) == 0) { UInt64 highPart = firstByte & (mask - 1); value += (highPart << (i * 8)); processed = i + 1; return value; } if (size == 0) { processed = 0; return 0; } value |= ((UInt64)*p << (i * 8)); p++; size--; mask >>= 1; } processed = 9; return value; } UInt64 CInByte2::ReadNumber() { size_t processed; UInt64 res = ReadNumberSpec(_buffer + _pos, _size - _pos, processed); if (processed == 0) ThrowEndOfData(); _pos += processed; return res; } CNum CInByte2::ReadNum() { /* if (_pos < _size) { Byte val = _buffer[_pos]; if ((unsigned)val < 0x80) { _pos++; return (unsigned)val; } } */ UInt64 value = ReadNumber(); if (value > kNumMax) ThrowUnsupported(); return (CNum)value; } UInt32 CInByte2::ReadUInt32() { if (_pos + 4 > _size) ThrowEndOfData(); UInt32 res = Get32(_buffer + _pos); _pos += 4; return res; } UInt64 CInByte2::ReadUInt64() { if (_pos + 8 > _size) ThrowEndOfData(); UInt64 res = Get64(_buffer + _pos); _pos += 8; return res; } #define CHECK_SIGNATURE if (p[0] != '7' || p[1] != 'z' || p[2] != 0xBC || p[3] != 0xAF || p[4] != 0x27 || p[5] != 0x1C) return false; static inline bool TestSignature(const Byte *p) { CHECK_SIGNATURE return CrcCalc(p + 12, 20) == Get32(p + 8); } #ifdef FORMAT_7Z_RECOVERY static inline bool TestSignature2(const Byte *p) { CHECK_SIGNATURE; if (CrcCalc(p + 12, 20) == Get32(p + 8)) return true; for (unsigned i = 8; i < kHeaderSize; i++) if (p[i] != 0) return false; return (p[6] != 0 || p[7] != 0); } #else #define TestSignature2(p) TestSignature(p) #endif HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { RINOK(ReadStream_FALSE(stream, _header, kHeaderSize)); if (TestSignature2(_header)) return S_OK; if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0) return S_FALSE; const UInt32 kBufSize = 1 << 15; CByteArr buf(kBufSize); memcpy(buf, _header, kHeaderSize); UInt64 offset = 0; for (;;) { UInt32 readSize = kBufSize - kHeaderSize; { UInt64 rem = *searchHeaderSizeLimit - offset; if (readSize > rem) readSize = (UInt32)rem; if (readSize == 0) return S_FALSE; } UInt32 processed = 0; RINOK(stream->Read(buf + kHeaderSize, readSize, &processed)); if (processed == 0) return S_FALSE; for (UInt32 pos = 0;;) { const Byte *p = buf + pos + 1; const Byte *lim = buf + processed; for (; p <= lim; p += 4) { if (p[0] == '7') break; if (p[1] == '7') { p += 1; break; } if (p[2] == '7') { p += 2; break; } if (p[3] == '7') { p += 3; break; } }; if (p > lim) break; pos = (UInt32)(p - buf); if (TestSignature(p)) { memcpy(_header, p, kHeaderSize); _arhiveBeginStreamPosition += offset + pos; return stream->Seek(_arhiveBeginStreamPosition + kHeaderSize, STREAM_SEEK_SET, NULL); } } offset += processed; memmove(buf, buf + processed, kHeaderSize); } } // S_FALSE means that file is not archive HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { HeadersSize = 0; Close(); RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition)) RINOK(stream->Seek(0, STREAM_SEEK_END, &_fileEndPosition)) RINOK(stream->Seek(_arhiveBeginStreamPosition, STREAM_SEEK_SET, NULL)) RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); _stream = stream; return S_OK; } void CInArchive::Close() { _numInByteBufs = 0; _stream.Release(); ThereIsHeaderError = false; } void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) { for (;;) { if (ReadID() == NID::kEnd) break; SkipData(); } } // CFolder &folder can be non empty. So we must set all fields void CInByte2::ParseFolder(CFolder &folder) { CNum numCoders = ReadNum(); folder.Coders.SetSize(numCoders); CNum numInStreams = 0; CNum numOutStreams = 0; CNum i; for (i = 0; i < numCoders; i++) { CCoderInfo &coder = folder.Coders[i]; { Byte mainByte = ReadByte(); if ((mainByte & 0xC0) != 0) ThrowUnsupported(); unsigned idSize = (mainByte & 0xF); if (idSize > 8 || idSize > GetRem()) ThrowUnsupported(); const Byte *longID = GetPtr(); UInt64 id = 0; for (unsigned j = 0; j < idSize; j++) id = ((id << 8) | longID[j]); SkipDataNoCheck(idSize); coder.MethodID = id; if ((mainByte & 0x10) != 0) { coder.NumInStreams = ReadNum(); coder.NumOutStreams = ReadNum(); } else { coder.NumInStreams = 1; coder.NumOutStreams = 1; } if ((mainByte & 0x20) != 0) { CNum propsSize = ReadNum(); coder.Props.Alloc((size_t)propsSize); ReadBytes((Byte *)coder.Props, (size_t)propsSize); } else coder.Props.Free(); } numInStreams += coder.NumInStreams; numOutStreams += coder.NumOutStreams; } CNum numBindPairs = numOutStreams - 1; folder.BindPairs.SetSize(numBindPairs); for (i = 0; i < numBindPairs; i++) { CBindPair &bp = folder.BindPairs[i]; bp.InIndex = ReadNum(); bp.OutIndex = ReadNum(); } if (numInStreams < numBindPairs) ThrowUnsupported(); CNum numPackStreams = numInStreams - numBindPairs; folder.PackStreams.SetSize(numPackStreams); if (numPackStreams == 1) { for (i = 0; i < numInStreams; i++) if (folder.FindBindPairForInStream(i) < 0) { folder.PackStreams[0] = i; break; } if (i == numInStreams) ThrowUnsupported(); } else for (i = 0; i < numPackStreams; i++) folder.PackStreams[i] = ReadNum(); } void CFolders::ParseFolderInfo(unsigned folderIndex, CFolder &folder) const { size_t startPos = FoCodersDataOffset[folderIndex]; CInByte2 inByte; inByte.Init(CodersData + startPos, FoCodersDataOffset[folderIndex + 1] - startPos); inByte.ParseFolder(folder); if (inByte.GetRem() != 0) throw 20120424; } void CDatabase::GetPath(unsigned index, UString &path) const { path.Empty(); if (!NameOffsets || !NamesBuf) return; size_t offset = NameOffsets[index]; size_t size = NameOffsets[index + 1] - offset - 1; if (size >= (1 << 20)) return; wchar_t *s = path.GetBuffer((unsigned)size); const Byte *p = ((const Byte *)NamesBuf + offset * 2); #if defined(_WIN32) && defined(MY_CPU_LE) wmemcpy(s, (const wchar_t *)p, size); #else for (size_t i = 0; i < size; i++) { *s = Get16(p); p += 2; s++; } #endif path.ReleaseBuffer((unsigned)size); } HRESULT CDatabase::GetPath_Prop(unsigned index, PROPVARIANT *path) const throw() { PropVariant_Clear(path); if (!NameOffsets || !NamesBuf) return S_OK; size_t offset = NameOffsets[index]; size_t size = NameOffsets[index + 1] - offset; if (size >= (1 << 14)) return S_OK; RINOK(PropVarEm_Alloc_Bstr(path, (unsigned)size - 1)); wchar_t *s = path->bstrVal; const Byte *p = ((const Byte *)NamesBuf + offset * 2); for (size_t i = 0; i < size; i++) { wchar_t c = Get16(p); p += 2; #if WCHAR_PATH_SEPARATOR != L'/' if (c == L'/') c = WCHAR_PATH_SEPARATOR; #endif *s++ = c; } return S_OK; /* unsigned cur = index; unsigned size = 0; for (int i = 0;; i++) { size_t len = NameOffsets[cur + 1] - NameOffsets[cur]; size += (unsigned)len; if (i > 256 || len > (1 << 14) || size > (1 << 14)) return PropVarEm_Set_Str(path, "[TOO-LONG]"); cur = Files[cur].Parent; if (cur < 0) break; } size--; RINOK(PropVarEm_Alloc_Bstr(path, size)); wchar_t *s = path->bstrVal; s += size; *s = 0; cur = index; for (;;) { unsigned len = (unsigned)(NameOffsets[cur + 1] - NameOffsets[cur] - 1); const Byte *p = (const Byte *)NamesBuf + (NameOffsets[cur + 1] * 2) - 2; do { p -= 2; --s; wchar_t c = Get16(p); if (c == '/') c = WCHAR_PATH_SEPARATOR; *s = c; } while (--len); const CFileItem &file = Files[cur]; cur = file.Parent; if (cur < 0) return S_OK; *(--s) = (file.IsAltStream ? ':' : WCHAR_PATH_SEPARATOR); } */ } void CInArchive::WaitId(UInt64 id) { for (;;) { UInt64 type = ReadID(); if (type == id) return; if (type == NID::kEnd) ThrowIncorrect(); SkipData(); } } void CInArchive::ReadHashDigests(unsigned numItems, CUInt32DefVector &crcs) { ReadBoolVector2(numItems, crcs.Defs); crcs.Vals.ClearAndSetSize(numItems); UInt32 *p = &crcs.Vals[0]; const bool *defs = &crcs.Defs[0]; for (unsigned i = 0; i < numItems; i++) { UInt32 crc = 0; if (defs[i]) crc = ReadUInt32(); p[i] = crc; } } void CInArchive::ReadPackInfo(CFolders &f) { CNum numPackStreams = ReadNum(); WaitId(NID::kSize); f.PackPositions.Alloc(numPackStreams + 1); f.NumPackStreams = numPackStreams; UInt64 sum = 0; for (CNum i = 0; i < numPackStreams; i++) { f.PackPositions[i] = sum; UInt64 packSize = ReadNumber(); sum += packSize; if (sum < packSize) ThrowIncorrect(); } f.PackPositions[numPackStreams] = sum; UInt64 type; for (;;) { type = ReadID(); if (type == NID::kEnd) return; if (type == NID::kCRC) { CUInt32DefVector PackCRCs; ReadHashDigests(numPackStreams, PackCRCs); continue; } SkipData(); } } void CInArchive::ReadUnpackInfo( const CObjectVector *dataVector, CFolders &folders) { WaitId(NID::kFolder); CNum numFolders = ReadNum(); CNum numCodersOutStreams = 0; { CStreamSwitch streamSwitch; streamSwitch.Set(this, dataVector); const Byte *startBufPtr = _inByteBack->GetPtr(); folders.NumFolders = numFolders; folders.FoStartPackStreamIndex.Alloc(numFolders + 1); folders.FoToMainUnpackSizeIndex.Alloc(numFolders); folders.FoCodersDataOffset.Alloc(numFolders + 1); folders.FoToCoderUnpackSizes.Alloc(numFolders + 1); CRecordVector InStreamUsed; CRecordVector OutStreamUsed; CNum packStreamIndex = 0; CNum fo; CInByte2 *inByte = _inByteBack; for (fo = 0; fo < numFolders; fo++) { UInt32 numOutStreams = 0; UInt32 indexOfMainStream = 0; UInt32 numPackStreams = 0; folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr; numOutStreams = 0; CNum numInStreams = 0; CNum numCoders = inByte->ReadNum(); for (CNum ci = 0; ci < numCoders; ci++) { Byte mainByte = inByte->ReadByte(); if ((mainByte & 0xC0) != 0) ThrowUnsupported(); unsigned idSize = (mainByte & 0xF); if (idSize > 8) ThrowUnsupported(); if (idSize > inByte->GetRem()) ThrowEndOfData(); const Byte *longID = inByte->GetPtr(); UInt64 id = 0; for (unsigned j = 0; j < idSize; j++) id = ((id << 8) | longID[j]); inByte->SkipDataNoCheck(idSize); if (folders.ParsedMethods.IDs.Size() < 128) folders.ParsedMethods.IDs.AddToUniqueSorted(id); CNum coderInStreams = 1; CNum coderOutStreams = 1; if ((mainByte & 0x10) != 0) { coderInStreams = inByte->ReadNum(); coderOutStreams = inByte->ReadNum(); } numInStreams += coderInStreams; if (numInStreams < coderInStreams) ThrowUnsupported(); numOutStreams += coderOutStreams; if (numOutStreams < coderOutStreams) ThrowUnsupported(); if ((mainByte & 0x20) != 0) { CNum propsSize = inByte->ReadNum(); if (propsSize > inByte->GetRem()) ThrowEndOfData(); if (id == k_LZMA2 && propsSize == 1) { Byte v = *_inByteBack->GetPtr(); if (folders.ParsedMethods.Lzma2Prop < v) folders.ParsedMethods.Lzma2Prop = v; } else if (id == k_LZMA && propsSize == 5) { UInt32 dicSize = GetUi32(_inByteBack->GetPtr() + 1); if (folders.ParsedMethods.LzmaDic < dicSize) folders.ParsedMethods.LzmaDic = dicSize; } inByte->SkipDataNoCheck((size_t)propsSize); } } if (numOutStreams == 1 && numInStreams == 1) { indexOfMainStream = 0; numPackStreams = 1; } else { UInt32 i; if (numOutStreams == 0) ThrowUnsupported(); CNum numBindPairs = numOutStreams - 1; if (numInStreams < numBindPairs) ThrowUnsupported(); if (numInStreams >= 256 || numOutStreams >= 256) ThrowUnsupported(); InStreamUsed.ClearAndSetSize(numInStreams); for (i = 0; i < numInStreams; i++) InStreamUsed[i] = false; OutStreamUsed.ClearAndSetSize(numOutStreams); for (i = 0; i < numOutStreams; i++) OutStreamUsed[i] = false; for (i = 0; i < numBindPairs; i++) { CNum index = ReadNum(); if (index >= numInStreams || InStreamUsed[index]) ThrowUnsupported(); InStreamUsed[index] = true; index = ReadNum(); if (index >= numOutStreams || OutStreamUsed[index]) ThrowUnsupported(); OutStreamUsed[index] = true; } numPackStreams = numInStreams - numBindPairs; if (numPackStreams != 1) for (i = 0; i < numPackStreams; i++) inByte->ReadNum(); // PackStreams for (i = 0; i < numOutStreams; i++) if (!OutStreamUsed[i]) { indexOfMainStream = i; break; } if (i == numOutStreams) ThrowUnsupported(); } folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams; numCodersOutStreams += numOutStreams; folders.FoStartPackStreamIndex[fo] = packStreamIndex; packStreamIndex += numPackStreams; folders.FoToMainUnpackSizeIndex[fo] = (Byte)indexOfMainStream; } size_t dataSize = _inByteBack->GetPtr() - startBufPtr; folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams; folders.FoStartPackStreamIndex[fo] = packStreamIndex; folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr; folders.CodersData.CopyFrom(startBufPtr, dataSize); } WaitId(NID::kCodersUnpackSize); folders.CoderUnpackSizes.Alloc(numCodersOutStreams); for (CNum i = 0; i < numCodersOutStreams; i++) folders.CoderUnpackSizes[i] = ReadNumber(); for (;;) { UInt64 type = ReadID(); if (type == NID::kEnd) return; if (type == NID::kCRC) { ReadHashDigests(numFolders, folders.FolderCRCs); continue; } SkipData(); } } void CInArchive::ReadSubStreamsInfo( CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests) { folders.NumUnpackStreamsVector.Alloc(folders.NumFolders); CNum i; for (i = 0; i < folders.NumFolders; i++) folders.NumUnpackStreamsVector[i] = 1; UInt64 type; for (;;) { type = ReadID(); if (type == NID::kNumUnpackStream) { for (i = 0; i < folders.NumFolders; i++) folders.NumUnpackStreamsVector[i] = ReadNum(); continue; } if (type == NID::kCRC || type == NID::kSize || type == NID::kEnd) break; SkipData(); } if (type == NID::kSize) { for (i = 0; i < folders.NumFolders; i++) { // v3.13 incorrectly worked with empty folders // v4.07: we check that folder is empty CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams == 0) continue; UInt64 sum = 0; for (CNum j = 1; j < numSubstreams; j++) { UInt64 size = ReadNumber(); unpackSizes.Add(size); sum += size; if (sum < size) ThrowIncorrect(); } UInt64 folderUnpackSize = folders.GetFolderUnpackSize(i); if (folderUnpackSize < sum) ThrowIncorrect(); unpackSizes.Add(folderUnpackSize - sum); } type = ReadID(); } else { for (i = 0; i < folders.NumFolders; i++) { /* v9.26 - v9.29 incorrectly worked: if (folders.NumUnpackStreamsVector[i] == 0), it threw error */ CNum val = folders.NumUnpackStreamsVector[i]; if (val > 1) ThrowIncorrect(); if (val == 1) unpackSizes.Add(folders.GetFolderUnpackSize(i)); } } unsigned numDigests = 0; for (i = 0; i < folders.NumFolders; i++) { CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams != 1 || !folders.FolderCRCs.ValidAndDefined(i)) numDigests += numSubstreams; } for (;;) { if (type == NID::kEnd) break; if (type == NID::kCRC) { // CUInt32DefVector digests2; // ReadHashDigests(numDigests, digests2); CBoolVector digests2; ReadBoolVector2(numDigests, digests2); digests.ClearAndSetSize(unpackSizes.Size()); unsigned k = 0; unsigned k2 = 0; for (i = 0; i < folders.NumFolders; i++) { CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i)) { digests.Defs[k] = true; digests.Vals[k] = folders.FolderCRCs.Vals[i]; k++; } else for (CNum j = 0; j < numSubstreams; j++) { bool defined = digests2[k2++]; digests.Defs[k] = defined; UInt32 crc = 0; if (defined) crc = ReadUInt32(); digests.Vals[k] = crc; k++; } } // if (k != unpackSizes.Size()) throw 1234567; } else SkipData(); type = ReadID(); } if (digests.Defs.Size() != unpackSizes.Size()) { digests.ClearAndSetSize(unpackSizes.Size()); unsigned k = 0; for (i = 0; i < folders.NumFolders; i++) { CNum numSubstreams = folders.NumUnpackStreamsVector[i]; if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i)) { digests.Defs[k] = true; digests.Vals[k] = folders.FolderCRCs.Vals[i]; k++; } else for (CNum j = 0; j < numSubstreams; j++) { digests.Defs[k] = false; digests.Vals[k] = 0; k++; } } } } void CInArchive::ReadStreamsInfo( const CObjectVector *dataVector, UInt64 &dataOffset, CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests) { UInt64 type = ReadID(); if (type == NID::kPackInfo) { dataOffset = ReadNumber(); ReadPackInfo(folders); type = ReadID(); } if (type == NID::kUnpackInfo) { ReadUnpackInfo(dataVector, folders); type = ReadID(); } if (folders.NumFolders != 0 && !folders.PackPositions) { // if there are folders, we need PackPositions also folders.PackPositions.Alloc(1); folders.PackPositions[0] = 0; } if (type == NID::kSubStreamsInfo) { ReadSubStreamsInfo(folders, unpackSizes, digests); type = ReadID(); } else { folders.NumUnpackStreamsVector.Alloc(folders.NumFolders); /* If digests.Defs.Size() == 0, it means that there are no crcs. So we don't need to fill digests with values. */ // digests.Vals.ClearAndSetSize(folders.NumFolders); // BoolVector_Fill_False(digests.Defs, folders.NumFolders); for (CNum i = 0; i < folders.NumFolders; i++) { folders.NumUnpackStreamsVector[i] = 1; unpackSizes.Add(folders.GetFolderUnpackSize(i)); // digests.Vals[i] = 0; } } if (type != NID::kEnd) ThrowIncorrect(); } void CInArchive::ReadBoolVector(unsigned numItems, CBoolVector &v) { v.ClearAndSetSize(numItems); Byte b = 0; Byte mask = 0; bool *p = &v[0]; for (unsigned i = 0; i < numItems; i++) { if (mask == 0) { b = ReadByte(); mask = 0x80; } p[i] = ((b & mask) != 0); mask >>= 1; } } void CInArchive::ReadBoolVector2(unsigned numItems, CBoolVector &v) { Byte allAreDefined = ReadByte(); if (allAreDefined == 0) { ReadBoolVector(numItems, v); return; } v.ClearAndSetSize(numItems); bool *p = &v[0]; for (unsigned i = 0; i < numItems; i++) p[i] = true; } void CInArchive::ReadUInt64DefVector(const CObjectVector &dataVector, CUInt64DefVector &v, unsigned numItems) { ReadBoolVector2(numItems, v.Defs); CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); v.Vals.ClearAndSetSize(numItems); UInt64 *p = &v.Vals[0]; const bool *defs = &v.Defs[0]; for (unsigned i = 0; i < numItems; i++) { UInt64 t = 0; if (defs[i]) t = ReadUInt64(); p[i] = t; } } HRESULT CInArchive::ReadAndDecodePackedStreams( DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset, UInt64 &dataOffset, CObjectVector &dataVector _7Z_DECODER_CRYPRO_VARS_DECL ) { CFolders folders; CRecordVector unpackSizes; CUInt32DefVector digests; ReadStreamsInfo(NULL, dataOffset, folders, unpackSizes, digests); CDecoder decoder( #ifdef _ST_MODE false #else true #endif ); for (CNum i = 0; i < folders.NumFolders; i++) { CByteBuffer &data = dataVector.AddNew(); UInt64 unpackSize64 = folders.GetFolderUnpackSize(i); size_t unpackSize = (size_t)unpackSize64; if (unpackSize != unpackSize64) ThrowUnsupported(); data.Alloc(unpackSize); CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; CMyComPtr outStream = outStreamSpec; outStreamSpec->Init(data, unpackSize); HRESULT result = decoder.Decode( EXTERNAL_CODECS_LOC_VARS _stream, baseOffset + dataOffset, folders, i, outStream, NULL _7Z_DECODER_CRYPRO_VARS #if !defined(_7ZIP_ST) && !defined(_SFX) , false, 1 #endif ); RINOK(result); if (folders.FolderCRCs.ValidAndDefined(i)) if (CrcCalc(data, unpackSize) != folders.FolderCRCs.Vals[i]) ThrowIncorrect(); } HeadersSize += folders.PackPositions[folders.NumPackStreams]; return S_OK; } HRESULT CInArchive::ReadHeader( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ) { UInt64 type = ReadID(); if (type == NID::kArchiveProperties) { ReadArchiveProperties(db.ArcInfo); type = ReadID(); } CObjectVector dataVector; if (type == NID::kAdditionalStreamsInfo) { HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS db.ArcInfo.StartPositionAfterHeader, db.ArcInfo.DataStartPosition2, dataVector _7Z_DECODER_CRYPRO_VARS ); RINOK(result); db.ArcInfo.DataStartPosition2 += db.ArcInfo.StartPositionAfterHeader; type = ReadID(); } CRecordVector unpackSizes; CUInt32DefVector digests; if (type == NID::kMainStreamsInfo) { ReadStreamsInfo(&dataVector, db.ArcInfo.DataStartPosition, (CFolders &)db, unpackSizes, digests); db.ArcInfo.DataStartPosition += db.ArcInfo.StartPositionAfterHeader; type = ReadID(); } db.Files.Clear(); if (type == NID::kFilesInfo) { CNum numFiles = ReadNum(); db.Files.ClearAndSetSize(numFiles); CNum i; /* db.Files.Reserve(numFiles); CNum i; for (i = 0; i < numFiles; i++) db.Files.Add(CFileItem()); */ db.ArcInfo.FileInfoPopIDs.Add(NID::kSize); // if (!db.PackSizes.IsEmpty()) db.ArcInfo.FileInfoPopIDs.Add(NID::kPackInfo); if (numFiles > 0 && !digests.Defs.IsEmpty()) db.ArcInfo.FileInfoPopIDs.Add(NID::kCRC); CBoolVector emptyStreamVector; BoolVector_Fill_False(emptyStreamVector, (unsigned)numFiles); CBoolVector emptyFileVector; CBoolVector antiFileVector; CNum numEmptyStreams = 0; for (;;) { UInt64 type = ReadID(); if (type == NID::kEnd) break; UInt64 size = ReadNumber(); if (size > _inByteBack->GetRem()) ThrowIncorrect(); CStreamSwitch switchProp; switchProp.Set(this, _inByteBack->GetPtr(), (size_t)size, true); bool addPropIdToList = true; bool isKnownType = true; if (type > ((UInt32)1 << 30)) isKnownType = false; else switch((UInt32)type) { case NID::kName: { CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); size_t rem = _inByteBack->GetRem(); db.NamesBuf.Alloc(rem); ReadBytes(db.NamesBuf, rem); db.NameOffsets.Alloc(db.Files.Size() + 1); size_t pos = 0; unsigned i; for (i = 0; i < db.Files.Size(); i++) { size_t curRem = (rem - pos) / 2; const UInt16 *buf = (const UInt16 *)(db.NamesBuf + pos); size_t j; for (j = 0; j < curRem && buf[j] != 0; j++); if (j == curRem) ThrowEndOfData(); db.NameOffsets[i] = pos / 2; pos += j * 2 + 2; } db.NameOffsets[i] = pos / 2; if (pos != rem) ThereIsHeaderError = true; break; } case NID::kWinAttrib: { CBoolVector boolVector; ReadBoolVector2(db.Files.Size(), boolVector); CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); for (i = 0; i < numFiles; i++) { CFileItem &file = db.Files[i]; file.AttribDefined = boolVector[i]; if (file.AttribDefined) file.Attrib = ReadUInt32(); } break; } /* case NID::kIsAux: { ReadBoolVector(db.Files.Size(), db.IsAux); break; } case NID::kParent: { db.IsTree = true; // CBoolVector boolVector; // ReadBoolVector2(db.Files.Size(), boolVector); // CStreamSwitch streamSwitch; // streamSwitch.Set(this, &dataVector); CBoolVector boolVector; ReadBoolVector2(db.Files.Size(), boolVector); db.ThereAreAltStreams = false; for (i = 0; i < numFiles; i++) { CFileItem &file = db.Files[i]; // file.Parent = -1; // if (boolVector[i]) file.Parent = (int)ReadUInt32(); file.IsAltStream = !boolVector[i]; if (file.IsAltStream) db.ThereAreAltStreams = true; } break; } */ case NID::kEmptyStream: { ReadBoolVector(numFiles, emptyStreamVector); numEmptyStreams = 0; for (i = 0; i < (CNum)emptyStreamVector.Size(); i++) if (emptyStreamVector[i]) numEmptyStreams++; BoolVector_Fill_False(emptyFileVector, numEmptyStreams); BoolVector_Fill_False(antiFileVector, numEmptyStreams); break; } case NID::kEmptyFile: ReadBoolVector(numEmptyStreams, emptyFileVector); break; case NID::kAnti: ReadBoolVector(numEmptyStreams, antiFileVector); break; case NID::kStartPos: ReadUInt64DefVector(dataVector, db.StartPos, (unsigned)numFiles); break; case NID::kCTime: ReadUInt64DefVector(dataVector, db.CTime, (unsigned)numFiles); break; case NID::kATime: ReadUInt64DefVector(dataVector, db.ATime, (unsigned)numFiles); break; case NID::kMTime: ReadUInt64DefVector(dataVector, db.MTime, (unsigned)numFiles); break; case NID::kDummy: { for (UInt64 j = 0; j < size; j++) if (ReadByte() != 0) ThereIsHeaderError = true; addPropIdToList = false; break; } /* case NID::kNtSecure: { try { { CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); UInt32 numDescriptors = ReadUInt32(); size_t offset = 0; db.SecureOffsets.Clear(); for (i = 0; i < numDescriptors; i++) { UInt32 size = ReadUInt32(); db.SecureOffsets.Add(offset); offset += size; } // ThrowIncorrect();; db.SecureOffsets.Add(offset); db.SecureBuf.SetCapacity(offset); for (i = 0; i < numDescriptors; i++) { offset = db.SecureOffsets[i]; ReadBytes(db.SecureBuf + offset, db.SecureOffsets[i + 1] - offset); } db.SecureIDs.Clear(); for (unsigned i = 0; i < db.Files.Size(); i++) { db.SecureIDs.Add(ReadNum()); // db.SecureIDs.Add(ReadUInt32()); } // ReadUInt32(); if (_inByteBack->GetRem() != 0) ThrowIncorrect();; } } catch(CInArchiveException &) { ThereIsHeaderError = true; addPropIdToList = isKnownType = false; db.ClearSecure(); } break; } */ default: addPropIdToList = isKnownType = false; } if (isKnownType) { if (addPropIdToList) db.ArcInfo.FileInfoPopIDs.Add(type); } else { db.UnsupportedFeatureWarning = true; _inByteBack->SkipRem(); } // SkipData worked incorrectly in some versions before v4.59 (7zVer <= 00.02) if (_inByteBack->GetRem() != 0) ThrowIncorrect(); } type = ReadID(); // Read (NID::kEnd) end of headers CNum emptyFileIndex = 0; CNum sizeIndex = 0; CNum numAntiItems = 0; for (i = 0; i < numEmptyStreams; i++) if (antiFileVector[i]) numAntiItems++; for (i = 0; i < numFiles; i++) { CFileItem &file = db.Files[i]; bool isAnti; file.HasStream = !emptyStreamVector[i]; file.Crc = 0; if (file.HasStream) { file.IsDir = false; isAnti = false; file.Size = unpackSizes[sizeIndex]; file.CrcDefined = digests.ValidAndDefined(sizeIndex); if (file.CrcDefined) file.Crc = digests.Vals[sizeIndex]; sizeIndex++; } else { file.IsDir = !emptyFileVector[emptyFileIndex]; isAnti = antiFileVector[emptyFileIndex]; emptyFileIndex++; file.Size = 0; file.CrcDefined = false; } if (numAntiItems != 0) db.IsAnti.Add(isAnti); } } db.FillLinks(); /* if (type != NID::kEnd) ThrowIncorrect(); if (_inByteBack->GetRem() != 0) ThrowIncorrect(); */ return S_OK; } void CDbEx::FillLinks() { FolderStartFileIndex.ClearAndSetSize(NumFolders); FileIndexToFolderIndexMap.ClearAndSetSize(Files.Size()); CNum folderIndex = 0; CNum indexInFolder = 0; unsigned i; for (i = 0; i < Files.Size(); i++) { bool emptyStream = !Files[i].HasStream; if (indexInFolder == 0) { if (emptyStream) { FileIndexToFolderIndexMap[i] = kNumNoIndex; continue; } // v3.13 incorrectly worked with empty folders // v4.07: we skip empty folders for (;;) { if (folderIndex >= NumFolders) ThrowIncorrect(); FolderStartFileIndex[folderIndex] = i; if (NumUnpackStreamsVector[folderIndex] != 0) break; folderIndex++; } } FileIndexToFolderIndexMap[i] = folderIndex; if (emptyStream) continue; if (++indexInFolder >= NumUnpackStreamsVector[folderIndex]) { folderIndex++; indexInFolder = 0; } } if (indexInFolder != 0) folderIndex++; /* if (indexInFolder != 0) ThrowIncorrect(); */ for (;;) { if (folderIndex >= NumFolders) return; FolderStartFileIndex[folderIndex] = i; /* if (NumUnpackStreamsVector[folderIndex] != 0) ThrowIncorrect();; */ folderIndex++; } } HRESULT CInArchive::ReadDatabase2( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ) { db.Clear(); db.ArcInfo.StartPosition = _arhiveBeginStreamPosition; db.ArcInfo.Version.Major = _header[6]; db.ArcInfo.Version.Minor = _header[7]; if (db.ArcInfo.Version.Major != kMajorVersion) { // db.UnsupportedVersion = true; return S_FALSE; } UInt64 nextHeaderOffset = Get64(_header + 12); UInt64 nextHeaderSize = Get64(_header + 20); UInt32 nextHeaderCRC = Get32(_header + 28); #ifdef FORMAT_7Z_RECOVERY UInt32 crcFromArc = Get32(_header + 8); if (crcFromArc == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0) { UInt64 cur, fileSize; RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur)); const unsigned kCheckSize = 512; Byte buf[kCheckSize]; RINOK(_stream->Seek(0, STREAM_SEEK_END, &fileSize)); UInt64 rem = fileSize - cur; unsigned checkSize = kCheckSize; if (rem < kCheckSize) checkSize = (unsigned)(rem); if (checkSize < 3) return S_FALSE; RINOK(_stream->Seek(fileSize - checkSize, STREAM_SEEK_SET, NULL)); RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize)); if (buf[checkSize - 1] != 0) return S_FALSE; unsigned i; for (i = checkSize - 2;; i--) { if (buf[i] == NID::kEncodedHeader && buf[i + 1] == NID::kPackInfo || buf[i] == NID::kHeader && buf[i + 1] == NID::kMainStreamsInfo) break; if (i == 0) return S_FALSE; } nextHeaderSize = checkSize - i; nextHeaderOffset = rem - nextHeaderSize; nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL)); db.StartHeaderWasRecovered = true; } else #endif { // Crc was tested already at signature check // if (CrcCalc(_header + 12, 20) != crcFromArchive) ThrowIncorrect(); } db.ArcInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize; db.PhySize = kHeaderSize; db.IsArc = false; if ((Int64)nextHeaderOffset < 0 || nextHeaderSize > ((UInt64)1 << 62)) return S_FALSE; if (nextHeaderSize == 0) { if (nextHeaderOffset != 0) return S_FALSE; db.IsArc = true; return S_OK; } if (!db.StartHeaderWasRecovered) db.IsArc = true; HeadersSize += kHeaderSize + nextHeaderSize; db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize; if (_fileEndPosition - db.ArcInfo.StartPositionAfterHeader < nextHeaderOffset + nextHeaderSize) { db.UnexpectedEnd = true; return S_FALSE; } RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL)); size_t nextHeaderSize_t = (size_t)nextHeaderSize; if (nextHeaderSize_t != nextHeaderSize) return E_OUTOFMEMORY; CByteBuffer buffer2(nextHeaderSize_t); RINOK(ReadStream_FALSE(_stream, buffer2, nextHeaderSize_t)); if (CrcCalc(buffer2, nextHeaderSize_t) != nextHeaderCRC) ThrowIncorrect(); if (!db.StartHeaderWasRecovered) db.PhySizeWasConfirmed = true; CStreamSwitch streamSwitch; streamSwitch.Set(this, buffer2); CObjectVector dataVector; UInt64 type = ReadID(); if (type != NID::kHeader) { if (type != NID::kEncodedHeader) ThrowIncorrect(); HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS db.ArcInfo.StartPositionAfterHeader, db.ArcInfo.DataStartPosition2, dataVector _7Z_DECODER_CRYPRO_VARS ); RINOK(result); if (dataVector.Size() == 0) return S_OK; if (dataVector.Size() > 1) ThrowIncorrect(); streamSwitch.Remove(); streamSwitch.Set(this, dataVector.Front()); if (ReadID() != NID::kHeader) ThrowIncorrect(); } db.IsArc = true; db.HeadersSize = HeadersSize; return ReadHeader( EXTERNAL_CODECS_LOC_VARS db _7Z_DECODER_CRYPRO_VARS ); } HRESULT CInArchive::ReadDatabase( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ) { try { HRESULT res = ReadDatabase2( EXTERNAL_CODECS_LOC_VARS db _7Z_DECODER_CRYPRO_VARS ); if (ThereIsHeaderError) db.ThereIsHeaderError = true; if (res == E_NOTIMPL) ThrowUnsupported(); return res; } catch(CUnsupportedFeatureException &) { db.UnsupportedFeatureError = true; return S_FALSE; } catch(CInArchiveException &) { db.ThereIsHeaderError = true; return S_FALSE; } } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.h000066400000000000000000000240431325366651500205370ustar00rootroot00000000000000// 7zIn.h #ifndef __7Z_IN_H #define __7Z_IN_H #include "../../../Common/MyCom.h" #include "../../../Windows/PropVariant.h" #include "../../IPassword.h" #include "../../IStream.h" #include "../../Common/CreateCoder.h" #include "../../Common/InBuffer.h" #include "7zItem.h" namespace NArchive { namespace N7z { /* We don't need to init isEncrypted and passwordIsDefined We must upgrade them only */ #ifdef _NO_CRYPTO #define _7Z_DECODER_CRYPRO_VARS_DECL #define _7Z_DECODER_CRYPRO_VARS #else #define _7Z_DECODER_CRYPRO_VARS_DECL , ICryptoGetTextPassword *getTextPassword, bool &isEncrypted, bool &passwordIsDefined #define _7Z_DECODER_CRYPRO_VARS , getTextPassword, isEncrypted, passwordIsDefined #endif struct CParsedMethods { Byte Lzma2Prop; UInt32 LzmaDic; CRecordVector IDs; CParsedMethods(): Lzma2Prop(0), LzmaDic(0) {} }; struct CFolders { CNum NumPackStreams; CNum NumFolders; CObjArray PackPositions; // NumPackStreams + 1 // CUInt32DefVector PackCRCs; // we don't use PackCRCs now CUInt32DefVector FolderCRCs; // NumFolders CObjArray NumUnpackStreamsVector; // NumFolders CObjArray CoderUnpackSizes; // including unpack sizes of bind coders CObjArray FoToCoderUnpackSizes; // NumFolders + 1 CObjArray FoStartPackStreamIndex; // NumFolders + 1 CObjArray FoToMainUnpackSizeIndex; // NumFolders CObjArray FoCodersDataOffset; // NumFolders + 1 CByteBuffer CodersData; CParsedMethods ParsedMethods; void ParseFolderInfo(unsigned folderIndex, CFolder &folder) const; unsigned GetNumFolderUnpackSizes(unsigned folderIndex) const { return FoToCoderUnpackSizes[folderIndex + 1] - FoToCoderUnpackSizes[folderIndex]; } UInt64 GetFolderUnpackSize(unsigned folderIndex) const { return CoderUnpackSizes[FoToCoderUnpackSizes[folderIndex] + FoToMainUnpackSizeIndex[folderIndex]]; } UInt64 GetStreamPackSize(unsigned index) const { return PackPositions[index + 1] - PackPositions[index]; } void Clear() { NumPackStreams = 0; PackPositions.Free(); // PackCRCs.Clear(); NumFolders = 0; FolderCRCs.Clear(); NumUnpackStreamsVector.Free(); CoderUnpackSizes.Free(); FoToCoderUnpackSizes.Free(); FoStartPackStreamIndex.Free(); FoToMainUnpackSizeIndex.Free(); FoCodersDataOffset.Free(); CodersData.Free(); } }; struct CDatabase: public CFolders { CRecordVector Files; CUInt64DefVector CTime; CUInt64DefVector ATime; CUInt64DefVector MTime; CUInt64DefVector StartPos; CRecordVector IsAnti; /* CRecordVector IsAux; CByteBuffer SecureBuf; CRecordVector SecureIDs; */ CByteBuffer NamesBuf; CObjArray NameOffsets; // numFiles + 1, offsets of utf-16 symbols /* void ClearSecure() { SecureBuf.Free(); SecureIDs.Clear(); } */ void Clear() { CFolders::Clear(); // ClearSecure(); NamesBuf.Free(); NameOffsets.Free(); Files.Clear(); CTime.Clear(); ATime.Clear(); MTime.Clear(); StartPos.Clear(); IsAnti.Clear(); // IsAux.Clear(); } bool IsSolid() const { for (CNum i = 0; i < NumFolders; i++) if (NumUnpackStreamsVector[i] > 1) return true; return false; } bool IsItemAnti(unsigned index) const { return (index < IsAnti.Size() && IsAnti[index]); } // bool IsItemAux(unsigned index) const { return (index < IsAux.Size() && IsAux[index]); } const void * GetName(unsigned index) const { if (!NameOffsets || !NamesBuf) return NULL; return (const void *)((const Byte *)NamesBuf + NameOffsets[index] * 2); }; void GetPath(unsigned index, UString &path) const; HRESULT GetPath_Prop(unsigned index, PROPVARIANT *path) const throw(); }; struct CInArchiveInfo { CArchiveVersion Version; UInt64 StartPosition; UInt64 StartPositionAfterHeader; UInt64 DataStartPosition; UInt64 DataStartPosition2; CRecordVector FileInfoPopIDs; void Clear() { StartPosition = 0; StartPositionAfterHeader = 0; DataStartPosition = 0; DataStartPosition2 = 0; FileInfoPopIDs.Clear(); } }; struct CDbEx: public CDatabase { CInArchiveInfo ArcInfo; CRecordVector FolderStartFileIndex; CRecordVector FileIndexToFolderIndexMap; UInt64 HeadersSize; UInt64 PhySize; /* CRecordVector SecureOffsets; bool IsTree; bool ThereAreAltStreams; */ bool IsArc; bool PhySizeWasConfirmed; bool ThereIsHeaderError; bool UnexpectedEnd; // bool UnsupportedVersion; bool StartHeaderWasRecovered; bool UnsupportedFeatureWarning; bool UnsupportedFeatureError; /* void ClearSecureEx() { ClearSecure(); SecureOffsets.Clear(); } */ void Clear() { IsArc = false; PhySizeWasConfirmed = false; ThereIsHeaderError = false; UnexpectedEnd = false; // UnsupportedVersion = false; StartHeaderWasRecovered = false; UnsupportedFeatureError = false; UnsupportedFeatureWarning = false; /* IsTree = false; ThereAreAltStreams = false; */ CDatabase::Clear(); // SecureOffsets.Clear(); ArcInfo.Clear(); FolderStartFileIndex.Clear(); FileIndexToFolderIndexMap.Clear(); HeadersSize = 0; PhySize = 0; } void FillLinks(); UInt64 GetFolderStreamPos(unsigned folderIndex, unsigned indexInFolder) const { return ArcInfo.DataStartPosition + PackPositions[FoStartPackStreamIndex[folderIndex] + indexInFolder]; } UInt64 GetFolderFullPackSize(unsigned folderIndex) const { return PackPositions[FoStartPackStreamIndex[folderIndex + 1]] - PackPositions[FoStartPackStreamIndex[folderIndex]]; } UInt64 GetFolderPackStreamSize(unsigned folderIndex, unsigned streamIndex) const { unsigned i = FoStartPackStreamIndex[folderIndex] + streamIndex; return PackPositions[i + 1] - PackPositions[i]; } UInt64 GetFilePackSize(CNum fileIndex) const { CNum folderIndex = FileIndexToFolderIndexMap[fileIndex]; if (folderIndex != kNumNoIndex) if (FolderStartFileIndex[folderIndex] == fileIndex) return GetFolderFullPackSize(folderIndex); return 0; } }; const unsigned kNumBufLevelsMax = 4; struct CInByte2 { const Byte *_buffer; public: size_t _size; size_t _pos; size_t GetRem() const { return _size - _pos; } const Byte *GetPtr() const { return _buffer + _pos; } void Init(const Byte *buffer, size_t size) { _buffer = buffer; _size = size; _pos = 0; } Byte ReadByte(); void ReadBytes(Byte *data, size_t size); void SkipDataNoCheck(UInt64 size) { _pos += (size_t)size; } void SkipData(UInt64 size); void SkipData(); void SkipRem() { _pos = _size; } UInt64 ReadNumber(); CNum ReadNum(); UInt32 ReadUInt32(); UInt64 ReadUInt64(); void ParseFolder(CFolder &folder); }; class CStreamSwitch; const UInt32 kHeaderSize = 32; class CInArchive { friend class CStreamSwitch; CMyComPtr _stream; unsigned _numInByteBufs; CInByte2 _inByteVector[kNumBufLevelsMax]; CInByte2 *_inByteBack; bool ThereIsHeaderError; UInt64 _arhiveBeginStreamPosition; UInt64 _fileEndPosition; Byte _header[kHeaderSize]; UInt64 HeadersSize; void AddByteStream(const Byte *buffer, size_t size); void DeleteByteStream(bool needUpdatePos) { _numInByteBufs--; if (_numInByteBufs > 0) { _inByteBack = &_inByteVector[_numInByteBufs - 1]; if (needUpdatePos) _inByteBack->_pos += _inByteVector[_numInByteBufs]._pos; } } private: HRESULT FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit); void ReadBytes(Byte *data, size_t size) { _inByteBack->ReadBytes(data, size); } Byte ReadByte() { return _inByteBack->ReadByte(); } UInt64 ReadNumber() { return _inByteBack->ReadNumber(); } CNum ReadNum() { return _inByteBack->ReadNum(); } UInt64 ReadID() { return _inByteBack->ReadNumber(); } UInt32 ReadUInt32() { return _inByteBack->ReadUInt32(); } UInt64 ReadUInt64() { return _inByteBack->ReadUInt64(); } void SkipData(UInt64 size) { _inByteBack->SkipData(size); } void SkipData() { _inByteBack->SkipData(); } void WaitId(UInt64 id); void ReadArchiveProperties(CInArchiveInfo &archiveInfo); void ReadHashDigests(unsigned numItems, CUInt32DefVector &crcs); void ReadPackInfo(CFolders &f); void ReadUnpackInfo( const CObjectVector *dataVector, CFolders &folders); void ReadSubStreamsInfo( CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests); void ReadStreamsInfo( const CObjectVector *dataVector, UInt64 &dataOffset, CFolders &folders, CRecordVector &unpackSizes, CUInt32DefVector &digests); void ReadBoolVector(unsigned numItems, CBoolVector &v); void ReadBoolVector2(unsigned numItems, CBoolVector &v); void ReadUInt64DefVector(const CObjectVector &dataVector, CUInt64DefVector &v, unsigned numItems); HRESULT ReadAndDecodePackedStreams( DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset, UInt64 &dataOffset, CObjectVector &dataVector _7Z_DECODER_CRYPRO_VARS_DECL ); HRESULT ReadHeader( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ); HRESULT ReadDatabase2( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ); public: CInArchive(): _numInByteBufs(0) { } HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); // S_FALSE means is not archive void Close(); HRESULT ReadDatabase( DECL_EXTERNAL_CODECS_LOC_VARS CDbEx &db _7Z_DECODER_CRYPRO_VARS_DECL ); }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zItem.h000066400000000000000000000067121325366651500210720ustar00rootroot00000000000000// 7zItem.h #ifndef __7Z_ITEM_H #define __7Z_ITEM_H #include "../../../Common/MyBuffer.h" #include "../../../Common/MyString.h" #include "../../Common/MethodId.h" #include "7zHeader.h" namespace NArchive { namespace N7z { const UInt64 k_AES = 0x06F10701; typedef UInt32 CNum; const CNum kNumMax = 0x7FFFFFFF; const CNum kNumNoIndex = 0xFFFFFFFF; struct CCoderInfo { CMethodId MethodID; CByteBuffer Props; CNum NumInStreams; CNum NumOutStreams; bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } }; struct CBindPair { CNum InIndex; CNum OutIndex; }; struct CFolder { CObjArray2 Coders; CObjArray2 BindPairs; CObjArray2 PackStreams; CNum GetNumOutStreams() const { CNum result = 0; FOR_VECTOR(i, Coders) result += Coders[i].NumOutStreams; return result; } int FindBindPairForInStream(CNum inStreamIndex) const { FOR_VECTOR(i, BindPairs) if (BindPairs[i].InIndex == inStreamIndex) return i; return -1; } int FindBindPairForOutStream(CNum outStreamIndex) const { FOR_VECTOR(i, BindPairs) if (BindPairs[i].OutIndex == outStreamIndex) return i; return -1; } int FindPackStreamArrayIndex(CNum inStreamIndex) const { FOR_VECTOR(i, PackStreams) if (PackStreams[i] == inStreamIndex) return i; return -1; } int GetIndexOfMainOutStream() const { for (int i = (int)GetNumOutStreams() - 1; i >= 0; i--) if (FindBindPairForOutStream(i) < 0) return i; throw 1; } bool IsEncrypted() const { for (int i = Coders.Size() - 1; i >= 0; i--) if (Coders[i].MethodID == k_AES) return true; return false; } bool CheckStructure(unsigned numUnpackSizes) const; }; struct CUInt32DefVector { CBoolVector Defs; CRecordVector Vals; void ClearAndSetSize(unsigned newSize) { Defs.ClearAndSetSize(newSize); Vals.ClearAndSetSize(newSize); } void Clear() { Defs.Clear(); Vals.Clear(); } void ReserveDown() { Defs.ReserveDown(); Vals.ReserveDown(); } bool ValidAndDefined(unsigned i) const { return i < Defs.Size() && Defs[i]; } }; struct CUInt64DefVector { CBoolVector Defs; CRecordVector Vals; void Clear() { Defs.Clear(); Vals.Clear(); } void ReserveDown() { Defs.ReserveDown(); Vals.ReserveDown(); } bool GetItem(unsigned index, UInt64 &value) const { if (index < Defs.Size() && Defs[index]) { value = Vals[index]; return true; } value = 0; return false; } void SetItem(unsigned index, bool defined, UInt64 value); bool CheckSize(unsigned size) const { return Defs.Size() == size || Defs.Size() == 0; } }; struct CFileItem { UInt64 Size; UInt32 Attrib; UInt32 Crc; /* int Parent; bool IsAltStream; */ bool HasStream; // Test it !!! it means that there is // stream in some folder. It can be empty stream bool IsDir; bool CrcDefined; bool AttribDefined; CFileItem(): /* Parent(-1), IsAltStream(false), */ HasStream(true), IsDir(false), CrcDefined(false), AttribDefined(false) {} void SetAttrib(UInt32 attrib) { AttribDefined = true; Attrib = attrib; } }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp000066400000000000000000000550621325366651500213000ustar00rootroot00000000000000// 7zOut.cpp #include "StdAfx.h" #include "../../../../C/7zCrc.h" #include "../../Common/StreamObjects.h" #include "7zOut.h" namespace NArchive { namespace N7z { HRESULT COutArchive::WriteSignature() { Byte buf[8]; memcpy(buf, kSignature, kSignatureSize); buf[kSignatureSize] = kMajorVersion; buf[kSignatureSize + 1] = 4; return WriteDirect(buf, 8); } #ifdef _7Z_VOL HRESULT COutArchive::WriteFinishSignature() { RINOK(WriteDirect(kFinishSignature, kSignatureSize)); CArchiveVersion av; av.Major = kMajorVersion; av.Minor = 2; RINOK(WriteDirectByte(av.Major)); return WriteDirectByte(av.Minor); } #endif static void SetUInt32(Byte *p, UInt32 d) { for (int i = 0; i < 4; i++, d >>= 8) p[i] = (Byte)d; } static void SetUInt64(Byte *p, UInt64 d) { for (int i = 0; i < 8; i++, d >>= 8) p[i] = (Byte)d; } HRESULT COutArchive::WriteStartHeader(const CStartHeader &h) { Byte buf[24]; SetUInt64(buf + 4, h.NextHeaderOffset); SetUInt64(buf + 12, h.NextHeaderSize); SetUInt32(buf + 20, h.NextHeaderCRC); SetUInt32(buf, CrcCalc(buf + 4, 20)); return WriteDirect(buf, 24); } #ifdef _7Z_VOL HRESULT COutArchive::WriteFinishHeader(const CFinishHeader &h) { CCRC crc; crc.UpdateUInt64(h.NextHeaderOffset); crc.UpdateUInt64(h.NextHeaderSize); crc.UpdateUInt32(h.NextHeaderCRC); crc.UpdateUInt64(h.ArchiveStartOffset); crc.UpdateUInt64(h.AdditionalStartBlockSize); RINOK(WriteDirectUInt32(crc.GetDigest())); RINOK(WriteDirectUInt64(h.NextHeaderOffset)); RINOK(WriteDirectUInt64(h.NextHeaderSize)); RINOK(WriteDirectUInt32(h.NextHeaderCRC)); RINOK(WriteDirectUInt64(h.ArchiveStartOffset)); return WriteDirectUInt64(h.AdditionalStartBlockSize); } #endif HRESULT COutArchive::Create(ISequentialOutStream *stream, bool endMarker) { Close(); #ifdef _7Z_VOL // endMarker = false; _endMarker = endMarker; #endif SeqStream = stream; if (!endMarker) { SeqStream.QueryInterface(IID_IOutStream, &Stream); if (!Stream) { return E_NOTIMPL; // endMarker = true; } } #ifdef _7Z_VOL if (endMarker) { /* CStartHeader sh; sh.NextHeaderOffset = (UInt32)(Int32)-1; sh.NextHeaderSize = (UInt32)(Int32)-1; sh.NextHeaderCRC = 0; WriteStartHeader(sh); */ } else #endif { if (!Stream) return E_FAIL; RINOK(WriteSignature()); RINOK(Stream->Seek(0, STREAM_SEEK_CUR, &_prefixHeaderPos)); } return S_OK; } void COutArchive::Close() { SeqStream.Release(); Stream.Release(); } HRESULT COutArchive::SkipPrefixArchiveHeader() { #ifdef _7Z_VOL if (_endMarker) return S_OK; #endif Byte buf[24]; memset(buf, 0, 24); return WriteDirect(buf, 24); } UInt64 COutArchive::GetPos() const { if (_countMode) return _countSize; if (_writeToStream) return _outByte.GetProcessedSize(); return _outByte2.GetPos(); } void COutArchive::WriteBytes(const void *data, size_t size) { if (_countMode) _countSize += size; else if (_writeToStream) { _outByte.WriteBytes(data, size); _crc = CrcUpdate(_crc, data, size); } else _outByte2.WriteBytes(data, size); } void COutArchive::WriteByte(Byte b) { if (_countMode) _countSize++; else if (_writeToStream) { _outByte.WriteByte(b); _crc = CRC_UPDATE_BYTE(_crc, b); } else _outByte2.WriteByte(b); } void COutArchive::WriteUInt32(UInt32 value) { for (int i = 0; i < 4; i++) { WriteByte((Byte)value); value >>= 8; } } void COutArchive::WriteUInt64(UInt64 value) { for (int i = 0; i < 8; i++) { WriteByte((Byte)value); value >>= 8; } } void COutArchive::WriteNumber(UInt64 value) { Byte firstByte = 0; Byte mask = 0x80; int i; for (i = 0; i < 8; i++) { if (value < ((UInt64(1) << ( 7 * (i + 1))))) { firstByte |= Byte(value >> (8 * i)); break; } firstByte |= mask; mask >>= 1; } WriteByte(firstByte); for (;i > 0; i--) { WriteByte((Byte)value); value >>= 8; } } static UInt32 GetBigNumberSize(UInt64 value) { int i; for (i = 1; i < 9; i++) if (value < (((UInt64)1 << (i * 7)))) break; return i; } #ifdef _7Z_VOL UInt32 COutArchive::GetVolHeadersSize(UInt64 dataSize, int nameLength, bool props) { UInt32 result = GetBigNumberSize(dataSize) * 2 + 41; if (nameLength != 0) { nameLength = (nameLength + 1) * 2; result += nameLength + GetBigNumberSize(nameLength) + 2; } if (props) { result += 20; } if (result >= 128) result++; result += kSignatureSize + 2 + kFinishHeaderSize; return result; } UInt64 COutArchive::GetVolPureSize(UInt64 volSize, int nameLength, bool props) { UInt32 headersSizeBase = COutArchive::GetVolHeadersSize(1, nameLength, props); int testSize; if (volSize > headersSizeBase) testSize = volSize - headersSizeBase; else testSize = 1; UInt32 headersSize = COutArchive::GetVolHeadersSize(testSize, nameLength, props); UInt64 pureSize = 1; if (volSize > headersSize) pureSize = volSize - headersSize; return pureSize; } #endif void COutArchive::WriteFolder(const CFolder &folder) { WriteNumber(folder.Coders.Size()); unsigned i; for (i = 0; i < folder.Coders.Size(); i++) { const CCoderInfo &coder = folder.Coders[i]; { size_t propsSize = coder.Props.Size(); UInt64 id = coder.MethodID; int idSize; for (idSize = 1; idSize < sizeof(id); idSize++) if ((id >> (8 * idSize)) == 0) break; Byte longID[15]; for (int t = idSize - 1; t >= 0 ; t--, id >>= 8) longID[t] = (Byte)(id & 0xFF); Byte b; b = (Byte)(idSize & 0xF); bool isComplex = !coder.IsSimpleCoder(); b |= (isComplex ? 0x10 : 0); b |= ((propsSize != 0) ? 0x20 : 0 ); WriteByte(b); WriteBytes(longID, idSize); if (isComplex) { WriteNumber(coder.NumInStreams); WriteNumber(coder.NumOutStreams); } if (propsSize == 0) continue; WriteNumber(propsSize); WriteBytes(coder.Props, propsSize); } } for (i = 0; i < folder.BindPairs.Size(); i++) { const CBindPair &bindPair = folder.BindPairs[i]; WriteNumber(bindPair.InIndex); WriteNumber(bindPair.OutIndex); } if (folder.PackStreams.Size() > 1) for (i = 0; i < folder.PackStreams.Size(); i++) { WriteNumber(folder.PackStreams[i]); } } void COutArchive::WriteBoolVector(const CBoolVector &boolVector) { Byte b = 0; Byte mask = 0x80; FOR_VECTOR (i, boolVector) { if (boolVector[i]) b |= mask; mask >>= 1; if (mask == 0) { WriteByte(b); mask = 0x80; b = 0; } } if (mask != 0x80) WriteByte(b); } static inline unsigned Bv_GetSizeInBytes(const CBoolVector &v) { return ((unsigned)v.Size() + 7) / 8; } void COutArchive::WritePropBoolVector(Byte id, const CBoolVector &boolVector) { WriteByte(id); WriteNumber(Bv_GetSizeInBytes(boolVector)); WriteBoolVector(boolVector); } void COutArchive::WriteHashDigests(const CUInt32DefVector &digests) { unsigned numDefined = 0; unsigned i; for (i = 0; i < digests.Defs.Size(); i++) if (digests.Defs[i]) numDefined++; if (numDefined == 0) return; WriteByte(NID::kCRC); if (numDefined == digests.Defs.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(digests.Defs); } for (i = 0; i < digests.Defs.Size(); i++) if (digests.Defs[i]) WriteUInt32(digests.Vals[i]); } void COutArchive::WritePackInfo( UInt64 dataOffset, const CRecordVector &packSizes, const CUInt32DefVector &packCRCs) { if (packSizes.IsEmpty()) return; WriteByte(NID::kPackInfo); WriteNumber(dataOffset); WriteNumber(packSizes.Size()); WriteByte(NID::kSize); FOR_VECTOR (i, packSizes) WriteNumber(packSizes[i]); WriteHashDigests(packCRCs); WriteByte(NID::kEnd); } void COutArchive::WriteUnpackInfo(const CObjectVector &folders, const COutFolders &outFolders) { if (folders.IsEmpty()) return; WriteByte(NID::kUnpackInfo); WriteByte(NID::kFolder); WriteNumber(folders.Size()); { WriteByte(0); FOR_VECTOR (i, folders) WriteFolder(folders[i]); } WriteByte(NID::kCodersUnpackSize); FOR_VECTOR (i, outFolders.CoderUnpackSizes) WriteNumber(outFolders.CoderUnpackSizes[i]); WriteHashDigests(outFolders.FolderUnpackCRCs); WriteByte(NID::kEnd); } void COutArchive::WriteSubStreamsInfo(const CObjectVector &folders, const COutFolders &outFolders, const CRecordVector &unpackSizes, const CUInt32DefVector &digests) { const CRecordVector &numUnpackStreamsInFolders = outFolders.NumUnpackStreamsVector; WriteByte(NID::kSubStreamsInfo); unsigned i; for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) if (numUnpackStreamsInFolders[i] != 1) { WriteByte(NID::kNumUnpackStream); for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) WriteNumber(numUnpackStreamsInFolders[i]); break; } for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) if (numUnpackStreamsInFolders[i] > 1) { WriteByte(NID::kSize); CNum index = 0; for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) { CNum num = numUnpackStreamsInFolders[i]; for (CNum j = 0; j < num; j++) { if (j + 1 != num) WriteNumber(unpackSizes[index]); index++; } } break; } CUInt32DefVector digests2; unsigned digestIndex = 0; for (i = 0; i < folders.Size(); i++) { unsigned numSubStreams = (unsigned)numUnpackStreamsInFolders[i]; if (numSubStreams == 1 && outFolders.FolderUnpackCRCs.ValidAndDefined(i)) digestIndex++; else for (unsigned j = 0; j < numSubStreams; j++, digestIndex++) { digests2.Defs.Add(digests.Defs[digestIndex]); digests2.Vals.Add(digests.Vals[digestIndex]); } } WriteHashDigests(digests2); WriteByte(NID::kEnd); } // 7-Zip 4.50 - 4.58 contain BUG, so they do not support .7z archives with Unknown field. void COutArchive::SkipAlign(unsigned pos, unsigned alignSize) { if (!_useAlign) return; pos += (unsigned)GetPos(); pos &= (alignSize - 1); if (pos == 0) return; unsigned skip = alignSize - pos; if (skip < 2) skip += alignSize; skip -= 2; WriteByte(NID::kDummy); WriteByte((Byte)skip); for (unsigned i = 0; i < skip; i++) WriteByte(0); } void COutArchive::WriteAlignedBoolHeader(const CBoolVector &v, unsigned numDefined, Byte type, unsigned itemSize) { const unsigned bvSize = (numDefined == v.Size()) ? 0 : Bv_GetSizeInBytes(v); const UInt64 dataSize = (UInt64)numDefined * itemSize + bvSize + 2; SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize); WriteByte(type); WriteNumber(dataSize); if (numDefined == v.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(v); } WriteByte(0); } void COutArchive::WriteUInt64DefVector(const CUInt64DefVector &v, Byte type) { unsigned numDefined = 0; unsigned i; for (i = 0; i < v.Defs.Size(); i++) if (v.Defs[i]) numDefined++; if (numDefined == 0) return; WriteAlignedBoolHeader(v.Defs, numDefined, type, 8); for (i = 0; i < v.Defs.Size(); i++) if (v.Defs[i]) WriteUInt64(v.Vals[i]); } HRESULT COutArchive::EncodeStream( DECL_EXTERNAL_CODECS_LOC_VARS CEncoder &encoder, const CByteBuffer &data, CRecordVector &packSizes, CObjectVector &folders, COutFolders &outFolders) { CBufInStream *streamSpec = new CBufInStream; CMyComPtr stream = streamSpec; streamSpec->Init(data, data.Size()); outFolders.FolderUnpackCRCs.Defs.Add(true); outFolders.FolderUnpackCRCs.Vals.Add(CrcCalc(data, data.Size())); // outFolders.NumUnpackStreamsVector.Add(1); UInt64 dataSize64 = data.Size(); UInt64 unpackSize; RINOK(encoder.Encode( EXTERNAL_CODECS_LOC_VARS stream, NULL, &dataSize64, folders.AddNew(), outFolders.CoderUnpackSizes, unpackSize, SeqStream, packSizes, NULL)) return S_OK; } void COutArchive::WriteHeader( const CArchiveDatabaseOut &db, // const CHeaderOptions &headerOptions, UInt64 &headerOffset) { /* bool thereIsSecure = (db.SecureBuf.Size() != 0); */ _useAlign = true; unsigned i; UInt64 packedSize = 0; for (i = 0; i < db.PackSizes.Size(); i++) packedSize += db.PackSizes[i]; headerOffset = packedSize; WriteByte(NID::kHeader); // Archive Properties if (db.Folders.Size() > 0) { WriteByte(NID::kMainStreamsInfo); WritePackInfo(0, db.PackSizes, db.PackCRCs); WriteUnpackInfo(db.Folders, (const COutFolders &)db); CRecordVector unpackSizes; CUInt32DefVector digests; for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; if (!file.HasStream) continue; unpackSizes.Add(file.Size); digests.Defs.Add(file.CrcDefined); digests.Vals.Add(file.Crc); } WriteSubStreamsInfo(db.Folders, (const COutFolders &)db, unpackSizes, digests); WriteByte(NID::kEnd); } if (db.Files.IsEmpty()) { WriteByte(NID::kEnd); return; } WriteByte(NID::kFilesInfo); WriteNumber(db.Files.Size()); { /* ---------- Empty Streams ---------- */ CBoolVector emptyStreamVector; emptyStreamVector.ClearAndSetSize(db.Files.Size()); unsigned numEmptyStreams = 0; for (i = 0; i < db.Files.Size(); i++) if (db.Files[i].HasStream) emptyStreamVector[i] = false; else { emptyStreamVector[i] = true; numEmptyStreams++; } if (numEmptyStreams != 0) { WritePropBoolVector(NID::kEmptyStream, emptyStreamVector); CBoolVector emptyFileVector, antiVector; emptyFileVector.ClearAndSetSize(numEmptyStreams); antiVector.ClearAndSetSize(numEmptyStreams); bool thereAreEmptyFiles = false, thereAreAntiItems = false; unsigned cur = 0; for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; if (file.HasStream) continue; emptyFileVector[cur] = !file.IsDir; if (!file.IsDir) thereAreEmptyFiles = true; bool isAnti = db.IsItemAnti(i); antiVector[cur] = isAnti; if (isAnti) thereAreAntiItems = true; cur++; } if (thereAreEmptyFiles) WritePropBoolVector(NID::kEmptyFile, emptyFileVector); if (thereAreAntiItems) WritePropBoolVector(NID::kAnti, antiVector); } } { /* ---------- Names ---------- */ unsigned numDefined = 0; size_t namesDataSize = 0; FOR_VECTOR (i, db.Files) { const UString &name = db.Names[i]; if (!name.IsEmpty()) numDefined++; namesDataSize += (name.Len() + 1) * 2; } if (numDefined > 0) { namesDataSize++; SkipAlign(2 + GetBigNumberSize(namesDataSize), 16); WriteByte(NID::kName); WriteNumber(namesDataSize); WriteByte(0); FOR_VECTOR (i, db.Files) { const UString &name = db.Names[i]; for (unsigned t = 0; t <= name.Len(); t++) { wchar_t c = name[t]; WriteByte((Byte)c); WriteByte((Byte)(c >> 8)); } } } } /* if (headerOptions.WriteCTime) */ WriteUInt64DefVector(db.CTime, NID::kCTime); /* if (headerOptions.WriteATime) */ WriteUInt64DefVector(db.ATime, NID::kATime); /* if (headerOptions.WriteMTime) */ WriteUInt64DefVector(db.MTime, NID::kMTime); WriteUInt64DefVector(db.StartPos, NID::kStartPos); { /* ---------- Write Attrib ---------- */ CBoolVector boolVector; boolVector.ClearAndSetSize(db.Files.Size()); unsigned numDefined = 0; for (i = 0; i < db.Files.Size(); i++) { bool defined = db.Files[i].AttribDefined; boolVector[i] = defined; if (defined) numDefined++; } if (numDefined != 0) { WriteAlignedBoolHeader(boolVector, numDefined, NID::kWinAttrib, 4); for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; if (file.AttribDefined) WriteUInt32(file.Attrib); } } } /* { // ---------- Write IsAux ---------- unsigned numAux = 0; const CBoolVector &isAux = db.IsAux; for (i = 0; i < isAux.Size(); i++) if (isAux[i]) numAux++; if (numAux > 0) { const unsigned bvSize = Bv_GetSizeInBytes(isAux); WriteByte(NID::kIsAux); WriteNumber(bvSize); WriteBoolVector(isAux); } } { // ---------- Write Parent ---------- CBoolVector boolVector; boolVector.Reserve(db.Files.Size()); unsigned numIsDir = 0; unsigned numParentLinks = 0; for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; bool defined = !file.IsAltStream; boolVector.Add(defined); if (defined) numIsDir++; if (file.Parent >= 0) numParentLinks++; } if (numParentLinks > 0) { // WriteAlignedBoolHeader(boolVector, numDefined, NID::kParent, 4); const unsigned bvSize = (numIsDir == boolVector.Size()) ? 0 : Bv_GetSizeInBytes(boolVector); const UInt64 dataSize = (UInt64)db.Files.Size() * 4 + bvSize + 1; SkipAlign(2 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), 4); WriteByte(NID::kParent); WriteNumber(dataSize); if (numIsDir == boolVector.Size()) WriteByte(1); else { WriteByte(0); WriteBoolVector(boolVector); } for (i = 0; i < db.Files.Size(); i++) { const CFileItem &file = db.Files[i]; // if (file.Parent >= 0) WriteUInt32(file.Parent); } } } if (thereIsSecure) { UInt64 secureDataSize = 1 + 4 + db.SecureBuf.Size() + db.SecureSizes.Size() * 4; // secureDataSize += db.SecureIDs.Size() * 4; for (i = 0; i < db.SecureIDs.Size(); i++) secureDataSize += GetBigNumberSize(db.SecureIDs[i]); SkipAlign(2 + GetBigNumberSize(secureDataSize), 4); WriteByte(NID::kNtSecure); WriteNumber(secureDataSize); WriteByte(0); WriteUInt32(db.SecureSizes.Size()); for (i = 0; i < db.SecureSizes.Size(); i++) WriteUInt32(db.SecureSizes[i]); WriteBytes(db.SecureBuf, db.SecureBuf.Size()); for (i = 0; i < db.SecureIDs.Size(); i++) { WriteNumber(db.SecureIDs[i]); // WriteUInt32(db.SecureIDs[i]); } } */ WriteByte(NID::kEnd); // for files WriteByte(NID::kEnd); // for headers } HRESULT COutArchive::WriteDatabase( DECL_EXTERNAL_CODECS_LOC_VARS const CArchiveDatabaseOut &db, const CCompressionMethodMode *options, const CHeaderOptions &headerOptions) { if (!db.CheckNumFiles()) return E_FAIL; UInt64 headerOffset; UInt32 headerCRC; UInt64 headerSize; if (db.IsEmpty()) { headerSize = 0; headerOffset = 0; headerCRC = CrcCalc(0, 0); } else { bool encodeHeaders = false; if (options != 0) if (options->IsEmpty()) options = 0; if (options != 0) if (options->PasswordIsDefined || headerOptions.CompressMainHeader) encodeHeaders = true; _outByte.SetStream(SeqStream); _outByte.Init(); _crc = CRC_INIT_VAL; _countMode = encodeHeaders; _writeToStream = true; _countSize = 0; WriteHeader(db, /* headerOptions, */ headerOffset); if (encodeHeaders) { CByteBuffer buf(_countSize); _outByte2.Init((Byte *)buf, _countSize); _countMode = false; _writeToStream = false; WriteHeader(db, /* headerOptions, */ headerOffset); if (_countSize != _outByte2.GetPos()) return E_FAIL; CCompressionMethodMode encryptOptions; encryptOptions.PasswordIsDefined = options->PasswordIsDefined; encryptOptions.Password = options->Password; CEncoder encoder(headerOptions.CompressMainHeader ? *options : encryptOptions); CRecordVector packSizes; CObjectVector folders; COutFolders outFolders; RINOK(EncodeStream( EXTERNAL_CODECS_LOC_VARS encoder, buf, packSizes, folders, outFolders)); _writeToStream = true; if (folders.Size() == 0) throw 1; WriteID(NID::kEncodedHeader); WritePackInfo(headerOffset, packSizes, CUInt32DefVector()); WriteUnpackInfo(folders, outFolders); WriteByte(NID::kEnd); FOR_VECTOR (i, packSizes) headerOffset += packSizes[i]; } RINOK(_outByte.Flush()); headerCRC = CRC_GET_DIGEST(_crc); headerSize = _outByte.GetProcessedSize(); } #ifdef _7Z_VOL if (_endMarker) { CFinishHeader h; h.NextHeaderSize = headerSize; h.NextHeaderCRC = headerCRC; h.NextHeaderOffset = UInt64(0) - (headerSize + 4 + kFinishHeaderSize); h.ArchiveStartOffset = h.NextHeaderOffset - headerOffset; h.AdditionalStartBlockSize = 0; RINOK(WriteFinishHeader(h)); return WriteFinishSignature(); } else #endif { CStartHeader h; h.NextHeaderSize = headerSize; h.NextHeaderCRC = headerCRC; h.NextHeaderOffset = headerOffset; RINOK(Stream->Seek(_prefixHeaderPos, STREAM_SEEK_SET, NULL)); return WriteStartHeader(h); } } void CUInt64DefVector::SetItem(unsigned index, bool defined, UInt64 value) { while (index >= Defs.Size()) Defs.Add(false); Defs[index] = defined; if (!defined) return; while (index >= Vals.Size()) Vals.Add(0); Vals[index] = value; } void CArchiveDatabaseOut::AddFile(const CFileItem &file, const CFileItem2 &file2, const UString &name) { unsigned index = Files.Size(); CTime.SetItem(index, file2.CTimeDefined, file2.CTime); ATime.SetItem(index, file2.ATimeDefined, file2.ATime); MTime.SetItem(index, file2.MTimeDefined, file2.MTime); StartPos.SetItem(index, file2.StartPosDefined, file2.StartPos); SetItem_Anti(index, file2.IsAnti); // SetItem_Aux(index, file2.IsAux); Names.Add(name); Files.Add(file); } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.h000066400000000000000000000163011325366651500207360ustar00rootroot00000000000000// 7zOut.h #ifndef __7Z_OUT_H #define __7Z_OUT_H #include "7zCompressionMode.h" #include "7zEncode.h" #include "7zHeader.h" #include "7zItem.h" #include "../../Common/OutBuffer.h" #include "../../Common/StreamUtils.h" namespace NArchive { namespace N7z { class CWriteBufferLoc { Byte *_data; size_t _size; size_t _pos; public: CWriteBufferLoc(): _size(0), _pos(0) {} void Init(Byte *data, size_t size) { _data = data; _size = size; _pos = 0; } void WriteBytes(const void *data, size_t size) { if (size > _size - _pos) throw 1; memcpy(_data + _pos, data, size); _pos += size; } void WriteByte(Byte b) { if (_size == _pos) throw 1; _data[_pos++] = b; } size_t GetPos() const { return _pos; } }; struct CHeaderOptions { bool CompressMainHeader; /* bool WriteCTime; bool WriteATime; bool WriteMTime; */ CHeaderOptions(): CompressMainHeader(true) /* , WriteCTime(false) , WriteATime(false) , WriteMTime(true) */ {} }; struct CFileItem2 { UInt64 CTime; UInt64 ATime; UInt64 MTime; UInt64 StartPos; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; bool StartPosDefined; bool IsAnti; // bool IsAux; void Init() { CTimeDefined = false; ATimeDefined = false; MTimeDefined = false; StartPosDefined = false; IsAnti = false; // IsAux = false; } }; struct COutFolders { CUInt32DefVector FolderUnpackCRCs; // Now we use it for headers only. CRecordVector NumUnpackStreamsVector; CRecordVector CoderUnpackSizes; // including unpack sizes of bind coders void OutFoldersClear() { FolderUnpackCRCs.Clear(); NumUnpackStreamsVector.Clear(); CoderUnpackSizes.Clear(); } void OutFoldersReserveDown() { FolderUnpackCRCs.ReserveDown(); NumUnpackStreamsVector.ReserveDown(); CoderUnpackSizes.ReserveDown(); } }; struct CArchiveDatabaseOut: public COutFolders { CRecordVector PackSizes; CUInt32DefVector PackCRCs; CObjectVector Folders; CRecordVector Files; UStringVector Names; CUInt64DefVector CTime; CUInt64DefVector ATime; CUInt64DefVector MTime; CUInt64DefVector StartPos; CRecordVector IsAnti; /* CRecordVector IsAux; CByteBuffer SecureBuf; CRecordVector SecureSizes; CRecordVector SecureIDs; void ClearSecure() { SecureBuf.Free(); SecureSizes.Clear(); SecureIDs.Clear(); } */ void Clear() { OutFoldersClear(); PackSizes.Clear(); PackCRCs.Clear(); Folders.Clear(); Files.Clear(); Names.Clear(); CTime.Clear(); ATime.Clear(); MTime.Clear(); StartPos.Clear(); IsAnti.Clear(); /* IsAux.Clear(); ClearSecure(); */ } void ReserveDown() { OutFoldersReserveDown(); PackSizes.ReserveDown(); PackCRCs.ReserveDown(); Folders.ReserveDown(); Files.ReserveDown(); Names.ReserveDown(); CTime.ReserveDown(); ATime.ReserveDown(); MTime.ReserveDown(); StartPos.ReserveDown(); IsAnti.ReserveDown(); /* IsAux.ReserveDown(); */ } bool IsEmpty() const { return ( PackSizes.IsEmpty() && NumUnpackStreamsVector.IsEmpty() && Folders.IsEmpty() && Files.IsEmpty()); } bool CheckNumFiles() const { unsigned size = Files.Size(); return ( CTime.CheckSize(size) && ATime.CheckSize(size) && MTime.CheckSize(size) && StartPos.CheckSize(size) && (size == IsAnti.Size() || IsAnti.Size() == 0)); } bool IsItemAnti(unsigned index) const { return (index < IsAnti.Size() && IsAnti[index]); } // bool IsItemAux(unsigned index) const { return (index < IsAux.Size() && IsAux[index]); } void SetItem_Anti(unsigned index, bool isAnti) { while (index >= IsAnti.Size()) IsAnti.Add(false); IsAnti[index] = isAnti; } /* void SetItem_Aux(unsigned index, bool isAux) { while (index >= IsAux.Size()) IsAux.Add(false); IsAux[index] = isAux; } */ void AddFile(const CFileItem &file, const CFileItem2 &file2, const UString &name); }; class COutArchive { UInt64 _prefixHeaderPos; HRESULT WriteDirect(const void *data, UInt32 size) { return WriteStream(SeqStream, data, size); } UInt64 GetPos() const; void WriteBytes(const void *data, size_t size); void WriteBytes(const CByteBuffer &data) { WriteBytes(data, data.Size()); } void WriteByte(Byte b); void WriteUInt32(UInt32 value); void WriteUInt64(UInt64 value); void WriteNumber(UInt64 value); void WriteID(UInt64 value) { WriteNumber(value); } void WriteFolder(const CFolder &folder); HRESULT WriteFileHeader(const CFileItem &itemInfo); void WriteBoolVector(const CBoolVector &boolVector); void WritePropBoolVector(Byte id, const CBoolVector &boolVector); void WriteHashDigests(const CUInt32DefVector &digests); void WritePackInfo( UInt64 dataOffset, const CRecordVector &packSizes, const CUInt32DefVector &packCRCs); void WriteUnpackInfo( const CObjectVector &folders, const COutFolders &outFolders); void WriteSubStreamsInfo( const CObjectVector &folders, const COutFolders &outFolders, const CRecordVector &unpackSizes, const CUInt32DefVector &digests); void SkipAlign(unsigned pos, unsigned alignSize); void WriteAlignedBoolHeader(const CBoolVector &v, unsigned numDefined, Byte type, unsigned itemSize); void WriteUInt64DefVector(const CUInt64DefVector &v, Byte type); HRESULT EncodeStream( DECL_EXTERNAL_CODECS_LOC_VARS CEncoder &encoder, const CByteBuffer &data, CRecordVector &packSizes, CObjectVector &folders, COutFolders &outFolders); void WriteHeader( const CArchiveDatabaseOut &db, // const CHeaderOptions &headerOptions, UInt64 &headerOffset); bool _countMode; bool _writeToStream; size_t _countSize; UInt32 _crc; COutBuffer _outByte; CWriteBufferLoc _outByte2; #ifdef _7Z_VOL bool _endMarker; #endif bool _useAlign; HRESULT WriteSignature(); #ifdef _7Z_VOL HRESULT WriteFinishSignature(); #endif HRESULT WriteStartHeader(const CStartHeader &h); #ifdef _7Z_VOL HRESULT WriteFinishHeader(const CFinishHeader &h); #endif CMyComPtr Stream; public: COutArchive() { _outByte.Create(1 << 16); } CMyComPtr SeqStream; HRESULT Create(ISequentialOutStream *stream, bool endMarker); void Close(); HRESULT SkipPrefixArchiveHeader(); HRESULT WriteDatabase( DECL_EXTERNAL_CODECS_LOC_VARS const CArchiveDatabaseOut &db, const CCompressionMethodMode *options, const CHeaderOptions &headerOptions); #ifdef _7Z_VOL static UInt32 GetVolHeadersSize(UInt64 dataSize, int nameLength = 0, bool props = false); static UInt64 GetVolPureSize(UInt64 volSize, int nameLength = 0, bool props = false); #endif }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.cpp000066400000000000000000000100101325366651500226450ustar00rootroot00000000000000// 7zProperties.cpp #include "StdAfx.h" #include "7zProperties.h" #include "7zHeader.h" #include "7zHandler.h" // #define _MULTI_PACK namespace NArchive { namespace N7z { struct CPropMap { UInt64 FilePropID; STATPROPSTG StatPROPSTG; }; static const CPropMap kPropMap[] = { { NID::kName, { NULL, kpidPath, VT_BSTR } }, { NID::kSize, { NULL, kpidSize, VT_UI8 } }, { NID::kPackInfo, { NULL, kpidPackSize, VT_UI8 } }, #ifdef _MULTI_PACK { 100, { L"Pack0", kpidPackedSize0, VT_UI8 } }, { 101, { L"Pack1", kpidPackedSize1, VT_UI8 } }, { 102, { L"Pack2", kpidPackedSize2, VT_UI8 } }, { 103, { L"Pack3", kpidPackedSize3, VT_UI8 } }, { 104, { L"Pack4", kpidPackedSize4, VT_UI8 } }, #endif { NID::kCTime, { NULL, kpidCTime, VT_FILETIME } }, { NID::kMTime, { NULL, kpidMTime, VT_FILETIME } }, { NID::kATime, { NULL, kpidATime, VT_FILETIME } }, { NID::kWinAttrib, { NULL, kpidAttrib, VT_UI4 } }, { NID::kStartPos, { NULL, kpidPosition, VT_UI4 } }, { NID::kCRC, { NULL, kpidCRC, VT_UI4 } }, // { NID::kIsAux, { NULL, kpidIsAux, VT_BOOL } }, { NID::kAnti, { NULL, kpidIsAnti, VT_BOOL } } #ifndef _SFX , { 97, { NULL,kpidEncrypted, VT_BOOL } }, { 98, { NULL,kpidMethod, VT_BSTR } }, { 99, { NULL,kpidBlock, VT_UI4 } } #endif }; static int FindPropInMap(UInt64 filePropID) { for (int i = 0; i < ARRAY_SIZE(kPropMap); i++) if (kPropMap[i].FilePropID == filePropID) return i; return -1; } static void CopyOneItem(CRecordVector &src, CRecordVector &dest, UInt32 item) { FOR_VECTOR (i, src) if (src[i] == item) { dest.Add(item); src.Delete(i); return; } } static void RemoveOneItem(CRecordVector &src, UInt32 item) { FOR_VECTOR (i, src) if (src[i] == item) { src.Delete(i); return; } } static void InsertToHead(CRecordVector &dest, UInt32 item) { FOR_VECTOR (i, dest) if (dest[i] == item) { dest.Delete(i); break; } dest.Insert(0, item); } #define COPY_ONE_ITEM(id) CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::id); void CHandler::FillPopIDs() { _fileInfoPopIDs.Clear(); #ifdef _7Z_VOL if (_volumes.Size() < 1) return; const CVolume &volume = _volumes.Front(); const CArchiveDatabaseEx &_db = volume.Database; #endif CRecordVector fileInfoPopIDs = _db.ArcInfo.FileInfoPopIDs; RemoveOneItem(fileInfoPopIDs, NID::kEmptyStream); RemoveOneItem(fileInfoPopIDs, NID::kEmptyFile); /* RemoveOneItem(fileInfoPopIDs, NID::kParent); RemoveOneItem(fileInfoPopIDs, NID::kNtSecure); */ COPY_ONE_ITEM(kName); COPY_ONE_ITEM(kAnti); COPY_ONE_ITEM(kSize); COPY_ONE_ITEM(kPackInfo); COPY_ONE_ITEM(kCTime); COPY_ONE_ITEM(kMTime); COPY_ONE_ITEM(kATime); COPY_ONE_ITEM(kWinAttrib); COPY_ONE_ITEM(kCRC); COPY_ONE_ITEM(kComment); _fileInfoPopIDs += fileInfoPopIDs; #ifndef _SFX _fileInfoPopIDs.Add(97); _fileInfoPopIDs.Add(98); _fileInfoPopIDs.Add(99); #endif #ifdef _MULTI_PACK _fileInfoPopIDs.Add(100); _fileInfoPopIDs.Add(101); _fileInfoPopIDs.Add(102); _fileInfoPopIDs.Add(103); _fileInfoPopIDs.Add(104); #endif #ifndef _SFX InsertToHead(_fileInfoPopIDs, NID::kMTime); InsertToHead(_fileInfoPopIDs, NID::kPackInfo); InsertToHead(_fileInfoPopIDs, NID::kSize); InsertToHead(_fileInfoPopIDs, NID::kName); #endif } STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) { *numProps = _fileInfoPopIDs.Size(); return S_OK; } STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) { if ((int)index >= _fileInfoPopIDs.Size()) return E_INVALIDARG; int indexInMap = FindPropInMap(_fileInfoPopIDs[index]); if (indexInMap == -1) return E_INVALIDARG; const STATPROPSTG &srcItem = kPropMap[indexInMap].StatPROPSTG; *propID = srcItem.propid; *varType = srcItem.vt; *name = 0; return S_OK; } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.h000066400000000000000000000004451325366651500223250ustar00rootroot00000000000000// 7zProperties.h #ifndef __7Z_PROPERTIES_H #define __7Z_PROPERTIES_H #include "../../PropID.h" namespace NArchive { namespace N7z { enum { kpidPackedSize0 = kpidUserDefined, kpidPackedSize1, kpidPackedSize2, kpidPackedSize3, kpidPackedSize4 }; }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zRegister.cpp000066400000000000000000000006271325366651500223120ustar00rootroot00000000000000// 7zRegister.cpp #include "StdAfx.h" #include "../../Common/RegisterArc.h" #include "7zHandler.h" namespace NArchive { namespace N7z { IMP_CreateArcIn IMP_CreateArcOut static CArcInfo g_ArcInfo = { "7z", "7z", 0, 7, 6, {'7' + 1, 'z', 0xBC, 0xAF, 0x27, 0x1C}, 0, NArcInfoFlags::kFindSignature, REF_CreateArc_Pair }; REGISTER_ARC_DEC_SIG(7z) // REGISTER_ARC(7z) }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.cpp000066400000000000000000000011351325366651500225670ustar00rootroot00000000000000// 7zSpecStream.cpp #include "StdAfx.h" #include "7zSpecStream.h" STDMETHODIMP CSequentialInStreamSizeCount2::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize; HRESULT result = _stream->Read(data, size, &realProcessedSize); _size += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return result; } STDMETHODIMP CSequentialInStreamSizeCount2::GetSubStreamSize(UInt64 subStream, UInt64 *value) { if (!_getSubStreamSize) return E_NOTIMPL; return _getSubStreamSize->GetSubStreamSize(subStream, value); } src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.h000066400000000000000000000015531325366651500222400ustar00rootroot00000000000000// 7zSpecStream.h #ifndef __7Z_SPEC_STREAM_H #define __7Z_SPEC_STREAM_H #include "../../IStream.h" #include "../../ICoder.h" #include "../../../Common/MyCom.h" class CSequentialInStreamSizeCount2: public ISequentialInStream, public ICompressGetSubStreamSize, public CMyUnknownImp { CMyComPtr _stream; CMyComPtr _getSubStreamSize; UInt64 _size; public: void Init(ISequentialInStream *stream) { _stream = stream; _getSubStreamSize = 0; _stream.QueryInterface(IID_ICompressGetSubStreamSize, &_getSubStreamSize); _size = 0; } UInt64 GetSize() const { return _size; } MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); }; #endif src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.cpp000066400000000000000000001122531325366651500217470ustar00rootroot00000000000000// 7zUpdate.cpp #include "StdAfx.h" #include "../../../../C/CpuArch.h" #include "../../../Common/Wildcard.h" #include "../../Common/CreateCoder.h" #include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Compress/CopyCoder.h" #include "../Common/ItemNameUtils.h" #include "../Common/OutStreamWithCRC.h" #include "7zDecode.h" #include "7zEncode.h" #include "7zFolderInStream.h" #include "7zHandler.h" #include "7zOut.h" #include "7zUpdate.h" namespace NArchive { namespace N7z { #ifdef MY_CPU_X86_OR_AMD64 #define USE_86_FILTER #endif static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream, UInt64 position, UInt64 size, ICompressProgressInfo *progress) { RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0)); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr inStreamLimited(streamSpec); streamSpec->SetStream(inStream); streamSpec->Init(size); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr copyCoder = copyCoderSpec; RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress)); return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL); } static int GetReverseSlashPos(const UString &name) { int slashPos = name.ReverseFind(L'/'); #ifdef _WIN32 int slash1Pos = name.ReverseFind(L'\\'); slashPos = MyMax(slashPos, slash1Pos); #endif return slashPos; } int CUpdateItem::GetExtensionPos() const { int slashPos = GetReverseSlashPos(Name); int dotPos = Name.ReverseFind(L'.'); if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0)) return Name.Len(); return dotPos + 1; } UString CUpdateItem::GetExtension() const { return Name.Ptr(GetExtensionPos()); } #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } #define RINOZ_COMP(a, b) RINOZ(MyCompare(a, b)) /* static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2) { size_t c1 = a1.GetCapacity(); size_t c2 = a2.GetCapacity(); RINOZ_COMP(c1, c2); for (size_t i = 0; i < c1; i++) RINOZ_COMP(a1[i], a2[i]); return 0; } static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2) { RINOZ_COMP(c1.NumInStreams, c2.NumInStreams); RINOZ_COMP(c1.NumOutStreams, c2.NumOutStreams); RINOZ_COMP(c1.MethodID, c2.MethodID); return CompareBuffers(c1.Props, c2.Props); } static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2) { RINOZ_COMP(b1.InIndex, b2.InIndex); return MyCompare(b1.OutIndex, b2.OutIndex); } static int CompareFolders(const CFolder &f1, const CFolder &f2) { int s1 = f1.Coders.Size(); int s2 = f2.Coders.Size(); RINOZ_COMP(s1, s2); int i; for (i = 0; i < s1; i++) RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i])); s1 = f1.BindPairs.Size(); s2 = f2.BindPairs.Size(); RINOZ_COMP(s1, s2); for (i = 0; i < s1; i++) RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i])); return 0; } */ /* static int CompareFiles(const CFileItem &f1, const CFileItem &f2) { return CompareFileNames(f1.Name, f2.Name); } */ struct CFolderRepack { int FolderIndex; int Group; CNum NumCopyFiles; }; static int CompareFolderRepacks(const CFolderRepack *p1, const CFolderRepack *p2, void * /* param */) { RINOZ_COMP(p1->Group, p2->Group); int i1 = p1->FolderIndex; int i2 = p2->FolderIndex; /* // In that version we don't want to parse folders here, so we don't compare folders // probably it must be improved in future const CDbEx &db = *(const CDbEx *)param; RINOZ(CompareFolders( db.Folders[i1], db.Folders[i2])); */ return MyCompare(i1, i2); /* RINOZ_COMP( db.NumUnpackStreamsVector[i1], db.NumUnpackStreamsVector[i2]); if (db.NumUnpackStreamsVector[i1] == 0) return 0; return CompareFiles( db.Files[db.FolderStartFileIndex[i1]], db.Files[db.FolderStartFileIndex[i2]]); */ } /* we sort empty files and dirs in such order: - Dir.NonAnti (name sorted) - File.NonAnti (name sorted) - File.Anti (name sorted) - Dir.Anti (reverse name sorted) */ static int CompareEmptyItems(const int *p1, const int *p2, void *param) { const CObjectVector &updateItems = *(const CObjectVector *)param; const CUpdateItem &u1 = updateItems[*p1]; const CUpdateItem &u2 = updateItems[*p2]; // NonAnti < Anti if (u1.IsAnti != u2.IsAnti) return (u1.IsAnti ? 1 : -1); if (u1.IsDir != u2.IsDir) { // Dir.NonAnti < File < Dir.Anti if (u1.IsDir) return (u1.IsAnti ? 1 : -1); return (u2.IsAnti ? -1 : 1); } int n = CompareFileNames(u1.Name, u2.Name); return (u1.IsDir && u1.IsAnti) ? -n : n; } static const char *g_Exts = " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo" " zip jar ear war msi" " 3gp avi mov mpeg mpg mpe wmv" " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav" " swf " " chm hxi hxs" " gif jpeg jpg jp2 png tiff bmp ico psd psp" " awg ps eps cgm dxf svg vrml wmf emf ai md" " cad dwg pps key sxi" " max 3ds" " iso bin nrg mdf img pdi tar cpio xpi" " vfd vhd vud vmc vsv" " vmdk dsk nvram vmem vmsd vmsn vmss vmtm" " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def" " f77 f f90 f95" " asm sql manifest dep " " mak clw csproj vcproj sln dsp dsw " " class " " bat cmd" " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml" " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs" " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf" " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf" " abw afp cwk lwp wpd wps wpt wrf wri" " abf afm bdf fon mgf otf pcf pfa snf ttf" " dbf mdb nsf ntf wdb db fdb gdb" " exe dll ocx vbx sfx sys tlb awx com obj lib out o so " " pdb pch idb ncb opt"; static int GetExtIndex(const char *ext) { int extIndex = 1; const char *p = g_Exts; for (;;) { char c = *p++; if (c == 0) return extIndex; if (c == ' ') continue; int pos = 0; for (;;) { char c2 = ext[pos++]; if (c2 == 0 && (c == 0 || c == ' ')) return extIndex; if (c != c2) break; c = *p++; } extIndex++; for (;;) { if (c == 0) return extIndex; if (c == ' ') break; c = *p++; } } } struct CRefItem { const CUpdateItem *UpdateItem; UInt32 Index; UInt32 ExtensionPos; UInt32 NamePos; unsigned ExtensionIndex; CRefItem() {}; CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType): UpdateItem(&ui), Index(index), ExtensionPos(0), NamePos(0), ExtensionIndex(0) { if (sortByType) { int slashPos = GetReverseSlashPos(ui.Name); NamePos = slashPos + 1; int dotPos = ui.Name.ReverseFind(L'.'); if (dotPos < 0 || dotPos < slashPos) ExtensionPos = ui.Name.Len(); else { ExtensionPos = dotPos + 1; if (ExtensionPos != ui.Name.Len()) { AString s; for (unsigned pos = ExtensionPos;; pos++) { wchar_t c = ui.Name[pos]; if (c >= 0x80) break; if (c == 0) { ExtensionIndex = GetExtIndex(s); break; } s += (char)MyCharLower_Ascii((char)c); } } } } } }; struct CSortParam { // const CObjectVector *TreeFolders; bool SortByType; }; /* we sort files in such order: - Dir.NonAnti (name sorted) - alt streams - Dirs - Dir.Anti (reverse name sorted) */ static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param) { const CRefItem &a1 = *p1; const CRefItem &a2 = *p2; const CUpdateItem &u1 = *a1.UpdateItem; const CUpdateItem &u2 = *a2.UpdateItem; /* if (u1.IsAltStream != u2.IsAltStream) return u1.IsAltStream ? 1 : -1; */ // Actually there are no dirs that time. They were stored in other steps // So that code is unused? if (u1.IsDir != u2.IsDir) return u1.IsDir ? 1 : -1; if (u1.IsDir) { if (u1.IsAnti != u2.IsAnti) return (u1.IsAnti ? 1 : -1); int n = CompareFileNames(u1.Name, u2.Name); return -n; } // bool sortByType = *(bool *)param; const CSortParam *sortParam = (const CSortParam *)param; bool sortByType = sortParam->SortByType; if (sortByType) { RINOZ_COMP(a1.ExtensionIndex, a2.ExtensionIndex); RINOZ(CompareFileNames(u1.Name.Ptr(a1.ExtensionPos), u2.Name.Ptr(a2.ExtensionPos))); RINOZ(CompareFileNames(u1.Name.Ptr(a1.NamePos), u2.Name.Ptr(a2.NamePos))); if (!u1.MTimeDefined && u2.MTimeDefined) return 1; if (u1.MTimeDefined && !u2.MTimeDefined) return -1; if (u1.MTimeDefined && u2.MTimeDefined) RINOZ_COMP(u1.MTime, u2.MTime); RINOZ_COMP(u1.Size, u2.Size); } /* int par1 = a1.UpdateItem->ParentFolderIndex; int par2 = a2.UpdateItem->ParentFolderIndex; const CTreeFolder &tf1 = (*sortParam->TreeFolders)[par1]; const CTreeFolder &tf2 = (*sortParam->TreeFolders)[par2]; int b1 = tf1.SortIndex, e1 = tf1.SortIndexEnd; int b2 = tf2.SortIndex, e2 = tf2.SortIndexEnd; if (b1 < b2) { if (e1 <= b2) return -1; // p2 in p1 int par = par2; for (;;) { const CTreeFolder &tf = (*sortParam->TreeFolders)[par]; par = tf.Parent; if (par == par1) { RINOZ(CompareFileNames(u1.Name, tf.Name)); break; } } } else if (b2 < b1) { if (e2 <= b1) return 1; // p1 in p2 int par = par1; for (;;) { const CTreeFolder &tf = (*sortParam->TreeFolders)[par]; par = tf.Parent; if (par == par2) { RINOZ(CompareFileNames(tf.Name, u2.Name)); break; } } } */ // RINOZ_COMP(a1.UpdateItem->ParentSortIndex, a2.UpdateItem->ParentSortIndex); RINOK(CompareFileNames(u1.Name, u2.Name)); RINOZ_COMP(a1.UpdateItem->IndexInClient, a2.UpdateItem->IndexInClient); RINOZ_COMP(a1.UpdateItem->IndexInArchive, a2.UpdateItem->IndexInArchive); return 0; } struct CSolidGroup { CRecordVector Indices; }; static const wchar_t *g_ExeExts[] = { L"dll" , L"exe" , L"ocx" , L"sfx" , L"sys" }; static bool IsExeExt(const wchar_t *ext) { for (int i = 0; i < ARRAY_SIZE(g_ExeExts); i++) if (MyStringCompareNoCase(ext, g_ExeExts[i]) == 0) return true; return false; } static inline void GetMethodFull(UInt64 methodID, UInt32 numInStreams, CMethodFull &m) { m.Id = methodID; m.NumInStreams = numInStreams; m.NumOutStreams = 1; } static void AddBcj2Methods(CCompressionMethodMode &mode) { CMethodFull m; GetMethodFull(k_LZMA, 1, m); m.AddProp32(NCoderPropID::kDictionarySize, 1 << 20); m.AddProp32(NCoderPropID::kNumFastBytes, 128); m.AddProp32(NCoderPropID::kNumThreads, 1); m.AddProp32(NCoderPropID::kLitPosBits, 2); m.AddProp32(NCoderPropID::kLitContextBits, 0); // m.AddPropString(NCoderPropID::kMatchFinder, L"BT2"); mode.Methods.Add(m); mode.Methods.Add(m); CBind bind; bind.OutCoder = 0; bind.InStream = 0; bind.InCoder = 1; bind.OutStream = 0; mode.Binds.Add(bind); bind.InCoder = 2; bind.OutStream = 1; mode.Binds.Add(bind); bind.InCoder = 3; bind.OutStream = 2; mode.Binds.Add(bind); } static void MakeExeMethod(CCompressionMethodMode &mode, bool useFilters, bool addFilter, bool bcj2Filter) { if (!mode.Binds.IsEmpty() || !useFilters || mode.Methods.Size() > 2) return; if (mode.Methods.Size() == 2) { if (mode.Methods[0].Id == k_BCJ2) AddBcj2Methods(mode); return; } if (!addFilter) return; bcj2Filter = bcj2Filter; #ifdef USE_86_FILTER if (bcj2Filter) { CMethodFull m; GetMethodFull(k_BCJ2, 4, m); mode.Methods.Insert(0, m); AddBcj2Methods(mode); } else { CMethodFull m; GetMethodFull(k_BCJ, 1, m); mode.Methods.Insert(0, m); CBind bind; bind.OutCoder = 0; bind.InStream = 0; bind.InCoder = 1; bind.OutStream = 0; mode.Binds.Add(bind); } #endif } static void FromUpdateItemToFileItem(const CUpdateItem &ui, CFileItem &file, CFileItem2 &file2) { if (ui.AttribDefined) file.SetAttrib(ui.Attrib); file2.CTime = ui.CTime; file2.CTimeDefined = ui.CTimeDefined; file2.ATime = ui.ATime; file2.ATimeDefined = ui.ATimeDefined; file2.MTime = ui.MTime; file2.MTimeDefined = ui.MTimeDefined; file2.IsAnti = ui.IsAnti; // file2.IsAux = false; file2.StartPosDefined = false; file.Size = ui.Size; file.IsDir = ui.IsDir; file.HasStream = ui.HasStream(); // file.IsAltStream = ui.IsAltStream; } class CFolderOutStream2: public ISequentialOutStream, public CMyUnknownImp { COutStreamWithCRC *_crcStreamSpec; CMyComPtr _crcStream; const CDbEx *_db; const CBoolVector *_extractStatuses; CMyComPtr _outStream; UInt32 _startIndex; unsigned _currentIndex; bool _fileIsOpen; UInt64 _rem; void OpenFile(); void CloseFile(); HRESULT CloseFileAndSetResult(); HRESULT ProcessEmptyFiles(); public: MY_UNKNOWN_IMP CFolderOutStream2() { _crcStreamSpec = new COutStreamWithCRC; _crcStream = _crcStreamSpec; } HRESULT Init(const CDbEx *db, UInt32 startIndex, const CBoolVector *extractStatuses, ISequentialOutStream *outStream); void ReleaseOutStream(); HRESULT CheckFinishedState() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; } STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; HRESULT CFolderOutStream2::Init(const CDbEx *db, UInt32 startIndex, const CBoolVector *extractStatuses, ISequentialOutStream *outStream) { _db = db; _startIndex = startIndex; _extractStatuses = extractStatuses; _outStream = outStream; _currentIndex = 0; _fileIsOpen = false; return ProcessEmptyFiles(); } void CFolderOutStream2::ReleaseOutStream() { _outStream.Release(); _crcStreamSpec->ReleaseStream(); } void CFolderOutStream2::OpenFile() { _crcStreamSpec->SetStream((*_extractStatuses)[_currentIndex] ? _outStream : NULL); _crcStreamSpec->Init(true); _fileIsOpen = true; _rem = _db->Files[_startIndex + _currentIndex].Size; } void CFolderOutStream2::CloseFile() { _crcStreamSpec->ReleaseStream(); _fileIsOpen = false; _currentIndex++; } HRESULT CFolderOutStream2::CloseFileAndSetResult() { const CFileItem &file = _db->Files[_startIndex + _currentIndex]; CloseFile(); return (file.IsDir || !file.CrcDefined || file.Crc == _crcStreamSpec->GetCRC()) ? S_OK: S_FALSE; } HRESULT CFolderOutStream2::ProcessEmptyFiles() { while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0) { OpenFile(); RINOK(CloseFileAndSetResult()); } return S_OK; } STDMETHODIMP CFolderOutStream2::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size != 0) { if (_fileIsOpen) { UInt32 cur = size < _rem ? size : (UInt32)_rem; RINOK(_crcStream->Write(data, cur, &cur)); if (cur == 0) break; data = (const Byte *)data + cur; size -= cur; _rem -= cur; if (processedSize != NULL) *processedSize += cur; if (_rem == 0) { RINOK(CloseFileAndSetResult()); RINOK(ProcessEmptyFiles()); continue; } } else { RINOK(ProcessEmptyFiles()); if (_currentIndex == _extractStatuses->Size()) { // we don't support partial extracting return E_FAIL; } OpenFile(); } } return S_OK; } class CThreadDecoder: public CVirtThread { public: HRESULT Result; CMyComPtr InStream; CFolderOutStream2 *FosSpec; CMyComPtr Fos; UInt64 StartPos; const CFolders *Folders; int FolderIndex; #ifndef _NO_CRYPTO CMyComPtr getTextPassword; #endif DECL_EXTERNAL_CODECS_LOC_VARS2; CDecoder Decoder; #ifndef _7ZIP_ST bool MtMode; UInt32 NumThreads; #endif CThreadDecoder(): Decoder(true) { #ifndef _7ZIP_ST MtMode = false; NumThreads = 1; #endif FosSpec = new CFolderOutStream2; Fos = FosSpec; Result = E_FAIL; } ~CThreadDecoder() { CVirtThread::WaitThreadFinish(); } virtual void Execute(); }; void CThreadDecoder::Execute() { try { #ifndef _NO_CRYPTO bool isEncrypted = false; bool passwordIsDefined = false; #endif Result = Decoder.Decode( EXTERNAL_CODECS_LOC_VARS InStream, StartPos, *Folders, FolderIndex, Fos, NULL _7Z_DECODER_CRYPRO_VARS #ifndef _7ZIP_ST , MtMode, NumThreads #endif ); } catch(...) { Result = E_FAIL; } if (Result == S_OK) Result = FosSpec->CheckFinishedState(); FosSpec->ReleaseOutStream(); } bool static Is86FilteredFolder(const CFolder &f) { FOR_VECTOR(i, f.Coders) { CMethodId m = f.Coders[i].MethodID; if (m == k_BCJ || m == k_BCJ2) return true; } return false; } #ifndef _NO_CRYPTO class CCryptoGetTextPassword: public ICryptoGetTextPassword, public CMyUnknownImp { public: UString Password; MY_UNKNOWN_IMP STDMETHOD(CryptoGetTextPassword)(BSTR *password); }; STDMETHODIMP CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password) { return StringToBstr(Password, password); } #endif static const int kNumGroupsMax = 4; static bool Is86Group(int group) { return (group & 1) != 0; } static bool IsEncryptedGroup(int group) { return (group & 2) != 0; } static int GetGroupIndex(bool encrypted, int bcjFiltered) { return (encrypted ? 2 : 0) + (bcjFiltered ? 1 : 0); } static void GetFile(const CDatabase &inDb, int index, CFileItem &file, CFileItem2 &file2) { file = inDb.Files[index]; file2.CTimeDefined = inDb.CTime.GetItem(index, file2.CTime); file2.ATimeDefined = inDb.ATime.GetItem(index, file2.ATime); file2.MTimeDefined = inDb.MTime.GetItem(index, file2.MTime); file2.StartPosDefined = inDb.StartPos.GetItem(index, file2.StartPos); file2.IsAnti = inDb.IsItemAnti(index); // file2.IsAux = inDb.IsItemAux(index); } HRESULT Update( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, const CDbEx *db, const CObjectVector &updateItems, // const CObjectVector &treeFolders, // const CUniqBlocks &secureBlocks, COutArchive &archive, CArchiveDatabaseOut &newDatabase, ISequentialOutStream *seqOutStream, IArchiveUpdateCallback *updateCallback, const CUpdateOptions &options #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getDecoderPassword #endif ) { UInt64 numSolidFiles = options.NumSolidFiles; if (numSolidFiles == 0) numSolidFiles = 1; // size_t totalSecureDataSize = (size_t)secureBlocks.GetTotalSizeInBytes(); /* CMyComPtr outStream; RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream)); if (!outStream) return E_NOTIMPL; */ UInt64 startBlockSize = db != 0 ? db->ArcInfo.StartPosition: 0; if (startBlockSize > 0 && !options.RemoveSfxBlock) { RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL)); } CIntArr fileIndexToUpdateIndexMap; CRecordVector folderRefs; UInt64 complexity = 0; UInt64 inSizeForReduce2 = 0; bool needEncryptedRepack = false; if (db != 0) { fileIndexToUpdateIndexMap.Alloc(db->Files.Size()); unsigned i; for (i = 0; i < db->Files.Size(); i++) fileIndexToUpdateIndexMap[i] = -1; for (i = 0; i < updateItems.Size(); i++) { int index = updateItems[i].IndexInArchive; if (index != -1) fileIndexToUpdateIndexMap[index] = i; } for (i = 0; i < (int)db->NumFolders; i++) { CNum indexInFolder = 0; CNum numCopyItems = 0; CNum numUnpackStreams = db->NumUnpackStreamsVector[i]; UInt64 repackSize = 0; for (CNum fi = db->FolderStartFileIndex[i]; indexInFolder < numUnpackStreams; fi++) { const CFileItem &file = db->Files[fi]; if (file.HasStream) { indexInFolder++; int updateIndex = fileIndexToUpdateIndexMap[fi]; if (updateIndex >= 0 && !updateItems[updateIndex].NewData) { numCopyItems++; repackSize += file.Size; } } } if (numCopyItems == 0) continue; CFolderRepack rep; rep.FolderIndex = i; rep.NumCopyFiles = numCopyItems; CFolder f; db->ParseFolderInfo(i, f); bool isEncrypted = f.IsEncrypted(); rep.Group = GetGroupIndex(isEncrypted, Is86FilteredFolder(f)); folderRefs.Add(rep); if (numCopyItems == numUnpackStreams) complexity += db->GetFolderFullPackSize(i); else { complexity += repackSize; if (repackSize > inSizeForReduce2) inSizeForReduce2 = repackSize; if (isEncrypted) needEncryptedRepack = true; } } folderRefs.Sort(CompareFolderRepacks, (void *)db); } UInt64 inSizeForReduce = 0; unsigned i; for (i = 0; i < updateItems.Size(); i++) { const CUpdateItem &ui = updateItems[i]; if (ui.NewData) { complexity += ui.Size; if (numSolidFiles != 1) inSizeForReduce += ui.Size; else if (ui.Size > inSizeForReduce) inSizeForReduce = ui.Size; } } if (inSizeForReduce2 > inSizeForReduce) inSizeForReduce = inSizeForReduce2; RINOK(updateCallback->SetTotal(complexity)); CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(updateCallback, true); CStreamBinder sb; RINOK(sb.CreateEvents()); CThreadDecoder threadDecoder; if (!folderRefs.IsEmpty()) { #ifdef EXTERNAL_CODECS threadDecoder.__externalCodecs = __externalCodecs; #endif RINOK(threadDecoder.Create()); } CObjectVector groups; for (i = 0; i < kNumGroupsMax; i++) groups.AddNew(); { // ---------- Split files to groups ---------- bool useFilters = options.UseFilters; const CCompressionMethodMode &method = *options.Method; if (method.Methods.Size() != 1 || method.Binds.Size() != 0) useFilters = false; for (i = 0; i < updateItems.Size(); i++) { const CUpdateItem &ui = updateItems[i]; if (!ui.NewData || !ui.HasStream()) continue; bool filteredGroup = false; if (useFilters) { int dotPos = ui.Name.ReverseFind(L'.'); if (dotPos >= 0) filteredGroup = IsExeExt(ui.Name.Ptr(dotPos + 1)); } groups[GetGroupIndex(method.PasswordIsDefined, filteredGroup)].Indices.Add(i); } } #ifndef _NO_CRYPTO CCryptoGetTextPassword *getPasswordSpec = NULL; if (needEncryptedRepack) { getPasswordSpec = new CCryptoGetTextPassword; threadDecoder.getTextPassword = getPasswordSpec; if (options.Method->PasswordIsDefined) getPasswordSpec->Password = options.Method->Password; else { if (!getDecoderPassword) return E_NOTIMPL; CMyComBSTR password; RINOK(getDecoderPassword->CryptoGetTextPassword(&password)); if ((BSTR)password) getPasswordSpec->Password = password; } } #endif // ---------- Compress ---------- RINOK(archive.Create(seqOutStream, false)); RINOK(archive.SkipPrefixArchiveHeader()); /* CIntVector treeFolderToArcIndex; treeFolderToArcIndex.Reserve(treeFolders.Size()); for (i = 0; i < treeFolders.Size(); i++) treeFolderToArcIndex.Add(-1); // ---------- Write Tree (only AUX dirs) ---------- for (i = 1; i < treeFolders.Size(); i++) { const CTreeFolder &treeFolder = treeFolders[i]; CFileItem file; CFileItem2 file2; file2.Init(); int secureID = 0; if (treeFolder.UpdateItemIndex < 0) { // we can store virtual dir item wuthout attrib, but we want all items have attrib. file.SetAttrib(FILE_ATTRIBUTE_DIRECTORY); file2.IsAux = true; } else { const CUpdateItem &ui = updateItems[treeFolder.UpdateItemIndex]; // if item is not dir, then it's parent for alt streams. // we will write such items later if (!ui.IsDir) continue; secureID = ui.SecureIndex; if (ui.NewProps) FromUpdateItemToFileItem(ui, file, file2); else GetFile(*db, ui.IndexInArchive, file, file2); } file.Size = 0; file.HasStream = false; file.IsDir = true; file.Parent = treeFolder.Parent; treeFolderToArcIndex[i] = newDatabase.Files.Size(); newDatabase.AddFile(file, file2, treeFolder.Name); if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(secureID); } */ { /* ---------- Write non-AUX dirs and Empty files ---------- */ CRecordVector emptyRefs; for (i = 0; i < updateItems.Size(); i++) { const CUpdateItem &ui = updateItems[i]; if (ui.NewData) { if (ui.HasStream()) continue; } else if (ui.IndexInArchive != -1 && db->Files[ui.IndexInArchive].HasStream) continue; /* if (ui.TreeFolderIndex >= 0) continue; */ emptyRefs.Add(i); } emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems); for (i = 0; i < emptyRefs.Size(); i++) { const CUpdateItem &ui = updateItems[emptyRefs[i]]; CFileItem file; CFileItem2 file2; UString name; if (ui.NewProps) { FromUpdateItemToFileItem(ui, file, file2); name = ui.Name; } else { GetFile(*db, ui.IndexInArchive, file, file2); db->GetPath(ui.IndexInArchive, name); } /* if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(ui.SecureIndex); file.Parent = ui.ParentFolderIndex; */ newDatabase.AddFile(file, file2, name); } } unsigned folderRefIndex = 0; lps->ProgressOffset = 0; for (int groupIndex = 0; groupIndex < kNumGroupsMax; groupIndex++) { const CSolidGroup &group = groups[groupIndex]; CCompressionMethodMode method = *options.Method; MakeExeMethod(method, options.UseFilters, Is86Group(groupIndex), options.MaxFilter); if (IsEncryptedGroup(groupIndex)) { if (!method.PasswordIsDefined) { #ifndef _NO_CRYPTO if (getPasswordSpec) method.Password = getPasswordSpec->Password; #endif method.PasswordIsDefined = true; } } else { method.PasswordIsDefined = false; method.Password.Empty(); } CEncoder encoder(method); for (; folderRefIndex < folderRefs.Size(); folderRefIndex++) { const CFolderRepack &rep = folderRefs[folderRefIndex]; if (rep.Group != groupIndex) break; int folderIndex = rep.FolderIndex; if (rep.NumCopyFiles == db->NumUnpackStreamsVector[folderIndex]) { UInt64 packSize = db->GetFolderFullPackSize(folderIndex); RINOK(WriteRange(inStream, archive.SeqStream, db->GetFolderStreamPos(folderIndex, 0), packSize, progress)); lps->ProgressOffset += packSize; CFolder &folder = newDatabase.Folders.AddNew(); db->ParseFolderInfo(folderIndex, folder); CNum startIndex = db->FoStartPackStreamIndex[folderIndex]; for (unsigned j = 0; j < folder.PackStreams.Size(); j++) { newDatabase.PackSizes.Add(db->GetStreamPackSize(startIndex + j)); // newDatabase.PackCRCsDefined.Add(db.PackCRCsDefined[startIndex + j]); // newDatabase.PackCRCs.Add(db.PackCRCs[startIndex + j]); } UInt32 indexStart = db->FoToCoderUnpackSizes[folderIndex]; UInt32 indexEnd = db->FoToCoderUnpackSizes[folderIndex + 1]; for (; indexStart < indexEnd; indexStart++) newDatabase.CoderUnpackSizes.Add(db->CoderUnpackSizes[indexStart]); } else { CBoolVector extractStatuses; CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex]; CNum indexInFolder = 0; for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++) { bool needExtract = false; if (db->Files[fi].HasStream) { indexInFolder++; int updateIndex = fileIndexToUpdateIndexMap[fi]; if (updateIndex >= 0 && !updateItems[updateIndex].NewData) needExtract = true; } extractStatuses.Add(needExtract); } unsigned startPackIndex = newDatabase.PackSizes.Size(); UInt64 curUnpackSize; { CMyComPtr sbInStream; { CMyComPtr sbOutStream; sb.CreateStreams(&sbInStream, &sbOutStream); sb.ReInit(); RINOK(threadDecoder.FosSpec->Init(db, db->FolderStartFileIndex[folderIndex], &extractStatuses, sbOutStream)); } threadDecoder.InStream = inStream; threadDecoder.Folders = (const CFolders *)db; threadDecoder.FolderIndex = folderIndex; threadDecoder.StartPos = db->ArcInfo.DataStartPosition; // db->GetFolderStreamPos(folderIndex, 0); threadDecoder.Start(); RINOK(encoder.Encode( EXTERNAL_CODECS_LOC_VARS sbInStream, NULL, &inSizeForReduce, newDatabase.Folders.AddNew(), newDatabase.CoderUnpackSizes, curUnpackSize, archive.SeqStream, newDatabase.PackSizes, progress)); threadDecoder.WaitExecuteFinish(); } RINOK(threadDecoder.Result); for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++) lps->OutSize += newDatabase.PackSizes[startPackIndex]; lps->InSize += curUnpackSize; } newDatabase.NumUnpackStreamsVector.Add(rep.NumCopyFiles); CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex]; CNum indexInFolder = 0; for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++) { CFileItem file; CFileItem2 file2; GetFile(*db, fi, file, file2); UString name; db->GetPath(fi, name); if (file.HasStream) { indexInFolder++; int updateIndex = fileIndexToUpdateIndexMap[fi]; if (updateIndex >= 0) { const CUpdateItem &ui = updateItems[updateIndex]; if (ui.NewData) continue; if (ui.NewProps) { CFileItem uf; FromUpdateItemToFileItem(ui, uf, file2); uf.Size = file.Size; uf.Crc = file.Crc; uf.CrcDefined = file.CrcDefined; uf.HasStream = file.HasStream; file = uf; name = ui.Name; } /* file.Parent = ui.ParentFolderIndex; if (ui.TreeFolderIndex >= 0) treeFolderToArcIndex[ui.TreeFolderIndex] = newDatabase.Files.Size(); if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(ui.SecureIndex); */ newDatabase.AddFile(file, file2, name); } } } } unsigned numFiles = group.Indices.Size(); if (numFiles == 0) continue; CRecordVector refItems; refItems.ClearAndSetSize(numFiles); bool sortByType = (numSolidFiles > 1); for (i = 0; i < numFiles; i++) refItems[i] = CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType); CSortParam sortParam; // sortParam.TreeFolders = &treeFolders; sortParam.SortByType = sortByType; refItems.Sort(CompareUpdateItems, (void *)&sortParam); CObjArray indices(numFiles); for (i = 0; i < numFiles; i++) { UInt32 index = refItems[i].Index; indices[i] = index; /* const CUpdateItem &ui = updateItems[index]; CFileItem file; if (ui.NewProps) FromUpdateItemToFileItem(ui, file); else file = db.Files[ui.IndexInArchive]; if (file.IsAnti || file.IsDir) return E_FAIL; newDatabase.Files.Add(file); */ } for (i = 0; i < numFiles;) { UInt64 totalSize = 0; int numSubFiles; UString prevExtension; for (numSubFiles = 0; i + numSubFiles < numFiles && numSubFiles < numSolidFiles; numSubFiles++) { const CUpdateItem &ui = updateItems[indices[i + numSubFiles]]; totalSize += ui.Size; if (totalSize > options.NumSolidBytes) break; if (options.SolidExtension) { UString ext = ui.GetExtension(); if (numSubFiles == 0) prevExtension = ext; else if (!ext.IsEqualToNoCase(prevExtension)) break; } } if (numSubFiles < 1) numSubFiles = 1; CFolderInStream *inStreamSpec = new CFolderInStream; CMyComPtr solidInStream(inStreamSpec); inStreamSpec->Init(updateCallback, &indices[i], numSubFiles); unsigned startPackIndex = newDatabase.PackSizes.Size(); UInt64 curFolderUnpackSize; RINOK(encoder.Encode( EXTERNAL_CODECS_LOC_VARS solidInStream, NULL, &inSizeForReduce, newDatabase.Folders.AddNew(), newDatabase.CoderUnpackSizes, curFolderUnpackSize, archive.SeqStream, newDatabase.PackSizes, progress)); for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++) lps->OutSize += newDatabase.PackSizes[startPackIndex]; lps->InSize += curFolderUnpackSize; // for () // newDatabase.PackCRCsDefined.Add(false); // newDatabase.PackCRCs.Add(0); CNum numUnpackStreams = 0; for (int subIndex = 0; subIndex < numSubFiles; subIndex++) { const CUpdateItem &ui = updateItems[indices[i + subIndex]]; CFileItem file; CFileItem2 file2; UString name; if (ui.NewProps) { FromUpdateItemToFileItem(ui, file, file2); name = ui.Name; } else { GetFile(*db, ui.IndexInArchive, file, file2); db->GetPath(ui.IndexInArchive, name); } if (file2.IsAnti || file.IsDir) return E_FAIL; /* CFileItem &file = newDatabase.Files[ startFileIndexInDatabase + i + subIndex]; */ if (!inStreamSpec->Processed[subIndex]) { continue; // file.Name += L".locked"; } file.Crc = inStreamSpec->CRCs[subIndex]; file.Size = inStreamSpec->Sizes[subIndex]; if (file.Size != 0) { file.CrcDefined = true; file.HasStream = true; numUnpackStreams++; } else { file.CrcDefined = false; file.HasStream = false; } /* file.Parent = ui.ParentFolderIndex; if (ui.TreeFolderIndex >= 0) treeFolderToArcIndex[ui.TreeFolderIndex] = newDatabase.Files.Size(); if (totalSecureDataSize != 0) newDatabase.SecureIDs.Add(ui.SecureIndex); */ newDatabase.AddFile(file, file2, name); } // numUnpackStreams = 0 is very bad case for locked files // v3.13 doesn't understand it. newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams); i += numSubFiles; } } if (folderRefIndex != folderRefs.Size()) return E_FAIL; RINOK(lps->SetCur()); /* folderRefs.ClearAndFree(); fileIndexToUpdateIndexMap.ClearAndFree(); groups.ClearAndFree(); */ /* for (i = 0; i < newDatabase.Files.Size(); i++) { CFileItem &file = newDatabase.Files[i]; file.Parent = treeFolderToArcIndex[file.Parent]; } if (totalSecureDataSize != 0) { newDatabase.SecureBuf.SetCapacity(totalSecureDataSize); size_t pos = 0; newDatabase.SecureSizes.Reserve(secureBlocks.Sorted.Size()); for (i = 0; i < secureBlocks.Sorted.Size(); i++) { const CByteBuffer &buf = secureBlocks.Bufs[secureBlocks.Sorted[i]]; size_t size = buf.GetCapacity(); memcpy(newDatabase.SecureBuf + pos, buf, size); newDatabase.SecureSizes.Add((UInt32)size); pos += size; } } */ newDatabase.ReserveDown(); return S_OK; } }} src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.h000066400000000000000000000045441325366651500214170ustar00rootroot00000000000000// 7zUpdate.h #ifndef __7Z_UPDATE_H #define __7Z_UPDATE_H #include "../IArchive.h" // #include "../../Common/UniqBlocks.h" #include "7zCompressionMode.h" #include "7zIn.h" #include "7zOut.h" namespace NArchive { namespace N7z { /* struct CTreeFolder { UString Name; int Parent; CIntVector SubFolders; int UpdateItemIndex; int SortIndex; int SortIndexEnd; CTreeFolder(): UpdateItemIndex(-1) {} }; */ struct CUpdateItem { int IndexInArchive; int IndexInClient; UInt64 CTime; UInt64 ATime; UInt64 MTime; UInt64 Size; UString Name; /* bool IsAltStream; int ParentFolderIndex; int TreeFolderIndex; */ // that code is not used in 9.26 // int ParentSortIndex; // int ParentSortIndexEnd; UInt32 Attrib; bool NewData; bool NewProps; bool IsAnti; bool IsDir; bool AttribDefined; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; // int SecureIndex; // 0 means (no_security) bool HasStream() const { return !IsDir && !IsAnti && Size != 0; } CUpdateItem(): // ParentSortIndex(-1), // IsAltStream(false), IsAnti(false), IsDir(false), AttribDefined(false), CTimeDefined(false), ATimeDefined(false), MTimeDefined(false) // SecureIndex(0) {} void SetDirStatusFromAttrib() { IsDir = ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0); }; int GetExtensionPos() const; UString GetExtension() const; }; struct CUpdateOptions { const CCompressionMethodMode *Method; const CCompressionMethodMode *HeaderMethod; bool UseFilters; bool MaxFilter; CHeaderOptions HeaderOptions; UInt64 NumSolidFiles; UInt64 NumSolidBytes; bool SolidExtension; bool RemoveSfxBlock; bool VolumeMode; }; HRESULT Update( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, const CDbEx *db, const CObjectVector &updateItems, // const CObjectVector &treeFolders, // treeFolders[0] is root // const CUniqBlocks &secureBlocks, COutArchive &archive, CArchiveDatabaseOut &newDatabase, ISequentialOutStream *seqOutStream, IArchiveUpdateCallback *updateCallback, const CUpdateOptions &options #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getDecoderPassword #endif ); }} #endif src/libs/7zip/win/CPP/7zip/Archive/7z/StdAfx.h000066400000000000000000000001501325366651500210720ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../../../Common/Common.h" #endif src/libs/7zip/win/CPP/7zip/Archive/Archive.pri000066400000000000000000000004041325366651500212670ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Archive/IArchive.h \ $$7ZIP_BASE/CPP/7zip/Archive/StdAfx.h SOURCES += $$7ZIP_BASE/CPP/7zip/Archive/LzmaHandler.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/SplitHandler.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/XzHandler.cpp src/libs/7zip/win/CPP/7zip/Archive/Common/000077500000000000000000000000001325366651500204245ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.cpp000066400000000000000000000071551325366651500232630ustar00rootroot00000000000000// CoderMixer2.cpp #include "StdAfx.h" #include "CoderMixer2.h" namespace NCoderMixer { CBindReverseConverter::CBindReverseConverter(const CBindInfo &srcBindInfo): _srcBindInfo(srcBindInfo) { srcBindInfo.GetNumStreams(NumSrcInStreams, _numSrcOutStreams); UInt32 j; _srcInToDestOutMap.ClearAndSetSize(NumSrcInStreams); DestOutToSrcInMap.ClearAndSetSize(NumSrcInStreams); for (j = 0; j < NumSrcInStreams; j++) { _srcInToDestOutMap[j] = 0; DestOutToSrcInMap[j] = 0; } _srcOutToDestInMap.ClearAndSetSize(_numSrcOutStreams); _destInToSrcOutMap.ClearAndSetSize(_numSrcOutStreams); for (j = 0; j < _numSrcOutStreams; j++) { _srcOutToDestInMap[j] = 0; _destInToSrcOutMap[j] = 0; } UInt32 destInOffset = 0; UInt32 destOutOffset = 0; UInt32 srcInOffset = NumSrcInStreams; UInt32 srcOutOffset = _numSrcOutStreams; for (int i = srcBindInfo.Coders.Size() - 1; i >= 0; i--) { const CCoderStreamsInfo &srcCoderInfo = srcBindInfo.Coders[i]; srcInOffset -= srcCoderInfo.NumInStreams; srcOutOffset -= srcCoderInfo.NumOutStreams; UInt32 j; for (j = 0; j < srcCoderInfo.NumInStreams; j++, destOutOffset++) { UInt32 index = srcInOffset + j; _srcInToDestOutMap[index] = destOutOffset; DestOutToSrcInMap[destOutOffset] = index; } for (j = 0; j < srcCoderInfo.NumOutStreams; j++, destInOffset++) { UInt32 index = srcOutOffset + j; _srcOutToDestInMap[index] = destInOffset; _destInToSrcOutMap[destInOffset] = index; } } } void CBindReverseConverter::CreateReverseBindInfo(CBindInfo &destBindInfo) { destBindInfo.Coders.ClearAndReserve(_srcBindInfo.Coders.Size()); destBindInfo.BindPairs.ClearAndReserve(_srcBindInfo.BindPairs.Size()); destBindInfo.InStreams.ClearAndReserve(_srcBindInfo.OutStreams.Size()); destBindInfo.OutStreams.ClearAndReserve(_srcBindInfo.InStreams.Size()); unsigned i; for (i = _srcBindInfo.Coders.Size(); i != 0;) { i--; const CCoderStreamsInfo &srcCoderInfo = _srcBindInfo.Coders[i]; CCoderStreamsInfo destCoderInfo; destCoderInfo.NumInStreams = srcCoderInfo.NumOutStreams; destCoderInfo.NumOutStreams = srcCoderInfo.NumInStreams; destBindInfo.Coders.AddInReserved(destCoderInfo); } for (i = _srcBindInfo.BindPairs.Size(); i != 0;) { i--; const CBindPair &srcBindPair = _srcBindInfo.BindPairs[i]; CBindPair destBindPair; destBindPair.InIndex = _srcOutToDestInMap[srcBindPair.OutIndex]; destBindPair.OutIndex = _srcInToDestOutMap[srcBindPair.InIndex]; destBindInfo.BindPairs.AddInReserved(destBindPair); } for (i = 0; i < _srcBindInfo.InStreams.Size(); i++) destBindInfo.OutStreams.AddInReserved(_srcInToDestOutMap[_srcBindInfo.InStreams[i]]); for (i = 0; i < _srcBindInfo.OutStreams.Size(); i++) destBindInfo.InStreams.AddInReserved(_srcOutToDestInMap[_srcBindInfo.OutStreams[i]]); } void SetSizes(const UInt64 **srcSizes, CRecordVector &sizes, CRecordVector &sizePointers, UInt32 numItems) { sizes.ClearAndSetSize(numItems); sizePointers.ClearAndSetSize(numItems); for (UInt32 i = 0; i < numItems; i++) { if (!srcSizes || !srcSizes[i]) { sizes[i] = 0; sizePointers[i] = NULL; } else { sizes[i] = *(srcSizes[i]); sizePointers[i] = &sizes[i]; } } } void CCoderInfo2::SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes) { SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); } } src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.h000066400000000000000000000104671325366651500227300ustar00rootroot00000000000000// CoderMixer2.h #ifndef __CODER_MIXER2_H #define __CODER_MIXER2_H #include "../../../Common/MyCom.h" #include "../../../Common/MyVector.h" #include "../../ICoder.h" namespace NCoderMixer { struct CBindPair { UInt32 InIndex; UInt32 OutIndex; }; struct CCoderStreamsInfo { UInt32 NumInStreams; UInt32 NumOutStreams; }; struct CBindInfo { CRecordVector Coders; CRecordVector BindPairs; CRecordVector InStreams; CRecordVector OutStreams; void Clear() { Coders.Clear(); BindPairs.Clear(); InStreams.Clear(); OutStreams.Clear(); } /* UInt32 GetCoderStartOutStream(UInt32 coderIndex) const { UInt32 numOutStreams = 0; for (UInt32 i = 0; i < coderIndex; i++) numOutStreams += Coders[i].NumOutStreams; return numOutStreams; } */ void GetNumStreams(UInt32 &numInStreams, UInt32 &numOutStreams) const { numInStreams = 0; numOutStreams = 0; FOR_VECTOR (i, Coders) { const CCoderStreamsInfo &coderStreamsInfo = Coders[i]; numInStreams += coderStreamsInfo.NumInStreams; numOutStreams += coderStreamsInfo.NumOutStreams; } } int FindBinderForInStream(UInt32 inStream) const { FOR_VECTOR (i, BindPairs) if (BindPairs[i].InIndex == inStream) return i; return -1; } int FindBinderForOutStream(UInt32 outStream) const { FOR_VECTOR (i, BindPairs) if (BindPairs[i].OutIndex == outStream) return i; return -1; } UInt32 GetCoderInStreamIndex(UInt32 coderIndex) const { UInt32 streamIndex = 0; for (UInt32 i = 0; i < coderIndex; i++) streamIndex += Coders[i].NumInStreams; return streamIndex; } UInt32 GetCoderOutStreamIndex(UInt32 coderIndex) const { UInt32 streamIndex = 0; for (UInt32 i = 0; i < coderIndex; i++) streamIndex += Coders[i].NumOutStreams; return streamIndex; } void FindInStream(UInt32 streamIndex, UInt32 &coderIndex, UInt32 &coderStreamIndex) const { for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) { UInt32 curSize = Coders[coderIndex].NumInStreams; if (streamIndex < curSize) { coderStreamIndex = streamIndex; return; } streamIndex -= curSize; } throw 1; } void FindOutStream(UInt32 streamIndex, UInt32 &coderIndex, UInt32 &coderStreamIndex) const { for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) { UInt32 curSize = Coders[coderIndex].NumOutStreams; if (streamIndex < curSize) { coderStreamIndex = streamIndex; return; } streamIndex -= curSize; } throw 1; } }; class CBindReverseConverter { UInt32 _numSrcOutStreams; NCoderMixer::CBindInfo _srcBindInfo; CRecordVector _srcInToDestOutMap; CRecordVector _srcOutToDestInMap; CRecordVector _destInToSrcOutMap; public: UInt32 NumSrcInStreams; CRecordVector DestOutToSrcInMap; CBindReverseConverter(const NCoderMixer::CBindInfo &srcBindInfo); void CreateReverseBindInfo(NCoderMixer::CBindInfo &destBindInfo); }; void SetSizes(const UInt64 **srcSizes, CRecordVector &sizes, CRecordVector &sizePointers, UInt32 numItems); struct CCoderInfo2 { CMyComPtr Coder; CMyComPtr Coder2; UInt32 NumInStreams; UInt32 NumOutStreams; CRecordVector InSizes; CRecordVector OutSizes; CRecordVector InSizePointers; CRecordVector OutSizePointers; CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams): NumInStreams(numInStreams), NumOutStreams(numOutStreams) {} void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); HRESULT QueryInterface(REFGUID iid, void** pp) const { IUnknown *p = Coder ? (IUnknown *)Coder : (IUnknown *)Coder2; return p->QueryInterface(iid, pp); } }; class CCoderMixer2 { public: virtual HRESULT SetBindInfo(const CBindInfo &bindInfo) = 0; virtual void ReInit() = 0; virtual void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) = 0; }; } #endif src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.cpp000066400000000000000000000140721325366651500235200ustar00rootroot00000000000000// CoderMixer2MT.cpp #include "StdAfx.h" #include "CoderMixer2MT.h" namespace NCoderMixer { CCoder2::CCoder2(UInt32 numInStreams, UInt32 numOutStreams): CCoderInfo2(numInStreams, numOutStreams) { InStreams.ClearAndReserve(NumInStreams); OutStreams.ClearAndReserve(NumOutStreams); } void CCoder2::Execute() { Code(NULL); } void CCoder2::Code(ICompressProgressInfo *progress) { InStreamPointers.ClearAndReserve(NumInStreams); OutStreamPointers.ClearAndReserve(NumOutStreams); UInt32 i; for (i = 0; i < NumInStreams; i++) { if (InSizePointers[i]) InSizePointers[i] = &InSizes[i]; InStreamPointers.AddInReserved((ISequentialInStream *)InStreams[i]); } for (i = 0; i < NumOutStreams; i++) { if (OutSizePointers[i]) OutSizePointers[i] = &OutSizes[i]; OutStreamPointers.AddInReserved((ISequentialOutStream *)OutStreams[i]); } if (Coder) Result = Coder->Code(InStreamPointers[0], OutStreamPointers[0], InSizePointers[0], OutSizePointers[0], progress); else Result = Coder2->Code(&InStreamPointers.Front(), &InSizePointers.Front(), NumInStreams, &OutStreamPointers.Front(), &OutSizePointers.Front(), NumOutStreams, progress); { unsigned i; for (i = 0; i < InStreams.Size(); i++) InStreams[i].Release(); for (i = 0; i < OutStreams.Size(); i++) OutStreams[i].Release(); } } /* void CCoder2::SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes) { SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); } */ ////////////////////////////////////// // CCoderMixer2MT HRESULT CCoderMixer2MT::SetBindInfo(const CBindInfo &bindInfo) { _bindInfo = bindInfo; _streamBinders.Clear(); FOR_VECTOR (i, _bindInfo.BindPairs) { RINOK(_streamBinders.AddNew().CreateEvents()); } return S_OK; } void CCoderMixer2MT::AddCoderCommon() { const CCoderStreamsInfo &c = _bindInfo.Coders[_coders.Size()]; CCoder2 threadCoderInfo(c.NumInStreams, c.NumOutStreams); _coders.Add(threadCoderInfo); } void CCoderMixer2MT::AddCoder(ICompressCoder *coder) { AddCoderCommon(); _coders.Back().Coder = coder; } void CCoderMixer2MT::AddCoder2(ICompressCoder2 *coder) { AddCoderCommon(); _coders.Back().Coder2 = coder; } void CCoderMixer2MT::ReInit() { FOR_VECTOR (i, _streamBinders) _streamBinders[i].ReInit(); } HRESULT CCoderMixer2MT::Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams) { /* if (_coders.Size() != _bindInfo.Coders.Size()) throw 0; */ unsigned i; for (i = 0; i < _coders.Size(); i++) { CCoder2 &coderInfo = _coders[i]; const CCoderStreamsInfo &coderStreamsInfo = _bindInfo.Coders[i]; coderInfo.InStreams.Clear(); UInt32 j; for (j = 0; j < coderStreamsInfo.NumInStreams; j++) coderInfo.InStreams.Add(NULL); coderInfo.OutStreams.Clear(); for (j = 0; j < coderStreamsInfo.NumOutStreams; j++) coderInfo.OutStreams.Add(NULL); } for (i = 0; i < _bindInfo.BindPairs.Size(); i++) { const CBindPair &bindPair = _bindInfo.BindPairs[i]; UInt32 inCoderIndex, inCoderStreamIndex; UInt32 outCoderIndex, outCoderStreamIndex; _bindInfo.FindInStream(bindPair.InIndex, inCoderIndex, inCoderStreamIndex); _bindInfo.FindOutStream(bindPair.OutIndex, outCoderIndex, outCoderStreamIndex); _streamBinders[i].CreateStreams( &_coders[inCoderIndex].InStreams[inCoderStreamIndex], &_coders[outCoderIndex].OutStreams[outCoderStreamIndex]); CMyComPtr inSetSize, outSetSize; _coders[inCoderIndex].QueryInterface(IID_ICompressSetBufSize, (void **)&inSetSize); _coders[outCoderIndex].QueryInterface(IID_ICompressSetBufSize, (void **)&outSetSize); if (inSetSize && outSetSize) { const UInt32 kBufSize = 1 << 19; inSetSize->SetInBufSize(inCoderStreamIndex, kBufSize); outSetSize->SetOutBufSize(outCoderStreamIndex, kBufSize); } } for (i = 0; i < _bindInfo.InStreams.Size(); i++) { UInt32 inCoderIndex, inCoderStreamIndex; _bindInfo.FindInStream(_bindInfo.InStreams[i], inCoderIndex, inCoderStreamIndex); _coders[inCoderIndex].InStreams[inCoderStreamIndex] = inStreams[i]; } for (i = 0; i < _bindInfo.OutStreams.Size(); i++) { UInt32 outCoderIndex, outCoderStreamIndex; _bindInfo.FindOutStream(_bindInfo.OutStreams[i], outCoderIndex, outCoderStreamIndex); _coders[outCoderIndex].OutStreams[outCoderStreamIndex] = outStreams[i]; } return S_OK; } HRESULT CCoderMixer2MT::ReturnIfError(HRESULT code) { FOR_VECTOR (i, _coders) if (_coders[i].Result == code) return code; return S_OK; } STDMETHODIMP CCoderMixer2MT::Code(ISequentialInStream **inStreams, const UInt64 ** /* inSizes */, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams, ICompressProgressInfo *progress) { if (numInStreams != (UInt32)_bindInfo.InStreams.Size() || numOutStreams != (UInt32)_bindInfo.OutStreams.Size()) return E_INVALIDARG; Init(inStreams, outStreams); unsigned i; for (i = 0; i < _coders.Size(); i++) if (i != _progressCoderIndex) { RINOK(_coders[i].Create()); } for (i = 0; i < _coders.Size(); i++) if (i != _progressCoderIndex) _coders[i].Start(); _coders[_progressCoderIndex].Code(progress); for (i = 0; i < _coders.Size(); i++) if (i != _progressCoderIndex) _coders[i].WaitExecuteFinish(); RINOK(ReturnIfError(E_ABORT)); RINOK(ReturnIfError(E_OUTOFMEMORY)); for (i = 0; i < _coders.Size(); i++) { HRESULT result = _coders[i].Result; if (result != S_OK && result != E_FAIL && result != S_FALSE) return result; } RINOK(ReturnIfError(S_FALSE)); for (i = 0; i < _coders.Size(); i++) { HRESULT result = _coders[i].Result; if (result != S_OK) return result; } return S_OK; } } src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.h000066400000000000000000000043171325366651500231660ustar00rootroot00000000000000// CoderMixer2MT.h #ifndef __CODER_MIXER2_MT_H #define __CODER_MIXER2_MT_H #include "CoderMixer2.h" #include "../../../Common/MyCom.h" #include "../../Common/StreamBinder.h" #include "../../Common/VirtThread.h" namespace NCoderMixer { struct CCoder2: public CCoderInfo2, public CVirtThread { CRecordVector InStreamPointers; CRecordVector OutStreamPointers; public: HRESULT Result; CObjectVector< CMyComPtr > InStreams; CObjectVector< CMyComPtr > OutStreams; CCoder2(UInt32 numInStreams, UInt32 numOutStreams); ~CCoder2() { CVirtThread::WaitThreadFinish(); } // void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); virtual void Execute(); void Code(ICompressProgressInfo *progress); }; /* SetBindInfo() for each coder AddCoder[2]() SetProgressIndex(UInt32 coderIndex); for each file { ReInit() for each coder SetCoderInfo Code } */ class CCoderMixer2MT: public ICompressCoder2, public CCoderMixer2, public CMyUnknownImp { CBindInfo _bindInfo; CObjectVector _streamBinders; unsigned _progressCoderIndex; void AddCoderCommon(); HRESULT Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams); HRESULT ReturnIfError(HRESULT code); public: CObjectVector _coders; MY_UNKNOWN_IMP STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); HRESULT SetBindInfo(const CBindInfo &bindInfo); void AddCoder(ICompressCoder *coder); void AddCoder2(ICompressCoder2 *coder); void SetProgressCoderIndex(unsigned coderIndex) { _progressCoderIndex = coderIndex; } void ReInit(); void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) { _coders[coderIndex].SetCoderInfo(inSizes, outSizes); } UInt64 GetWriteProcessedSize(UInt32 binderIndex) const { return _streamBinders[binderIndex].ProcessedSize; } }; } #endif src/libs/7zip/win/CPP/7zip/Archive/Common/Common.pri000066400000000000000000000020731325366651500223720ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2MT.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/DummyOutStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/HandlerOut.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/InStreamWithCRC.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/ItemNameUtils.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/MultiStream.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/OutStreamWithCRC.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/ParseProperties.h \ $$7ZIP_BASE/CPP/7zip/Archive/Common/StdAfx.h SOURCES += $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/CoderMixer2MT.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/DummyOutStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/HandlerOut.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/InStreamWithCRC.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/ItemNameUtils.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/MultiStream.cpp \ $$7ZIP_BASE/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.cpp000066400000000000000000000006431325366651500240720ustar00rootroot00000000000000// DummyOutStream.cpp #include "StdAfx.h" #include "DummyOutStream.h" STDMETHODIMP CDummyOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize = size; HRESULT res = S_OK; if (_stream) res = _stream->Write(data, size, &realProcessedSize); _size += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return res; } src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.h000066400000000000000000000011421325366651500235320ustar00rootroot00000000000000// DummyOutStream.h #ifndef __DUMMY_OUT_STREAM_H #define __DUMMY_OUT_STREAM_H #include "../../../Common/MyCom.h" #include "../../IStream.h" class CDummyOutStream: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; public: void SetStream(ISequentialOutStream *outStream) { _stream = outStream; } void ReleaseStream() { _stream.Release(); } void Init() { _size = 0; } MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); UInt64 GetSize() const { return _size; } }; #endif src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.cpp000066400000000000000000000064051325366651500232020ustar00rootroot00000000000000// HandlerOut.cpp #include "StdAfx.h" #ifndef _7ZIP_ST #include "../../../Windows/System.h" #endif #include "../Common/ParseProperties.h" #include "HandlerOut.h" using namespace NWindows; namespace NArchive { static void SetMethodProp32(COneMethodInfo &m, PROPID propID, UInt32 value) { if (m.FindProp(propID) < 0) m.AddProp32(propID, value); } void CMultiMethodProps::SetGlobalLevelAndThreads(COneMethodInfo &oneMethodInfo #ifndef _7ZIP_ST , UInt32 numThreads #endif ) { UInt32 level = _level; if (level != (UInt32)(Int32)-1) SetMethodProp32(oneMethodInfo, NCoderPropID::kLevel, (UInt32)level); #ifndef _7ZIP_ST SetMethodProp32(oneMethodInfo, NCoderPropID::kNumThreads, numThreads); #endif } void CMultiMethodProps::Init() { #ifndef _7ZIP_ST _numProcessors = _numThreads = NSystem::GetNumberOfProcessors(); #endif _level = (UInt32)(Int32)-1; _autoFilter = true; _crcSize = 4; _filterMethod.Clear(); _methods.Clear(); } HRESULT CMultiMethodProps::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) { UString name = nameSpec; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; if (name[0] == 'x') { name.Delete(0); _level = 9; return ParsePropToUInt32(name, value, _level); } if (name == L"crc") { name.Delete(0, 3); _crcSize = 4; return ParsePropToUInt32(name, value, _crcSize); } UInt32 number; int index = ParseStringToUInt32(name, number); UString realName = name.Ptr(index); if (index == 0) { if (name.IsPrefixedBy(L"mt")) { #ifndef _7ZIP_ST RINOK(ParseMtProp(name.Ptr(2), value, _numProcessors, _numThreads)); #endif return S_OK; } if (name.IsEqualTo("f")) { HRESULT res = PROPVARIANT_to_bool(value, _autoFilter); if (res == S_OK) return res; if (value.vt != VT_BSTR) return E_INVALIDARG; return _filterMethod.ParseMethodFromPROPVARIANT(L"", value); } number = 0; } if (number > 64) return E_FAIL; for (int j = _methods.Size(); j <= (int)number; j++) _methods.Add(COneMethodInfo()); return _methods[number].ParseMethodFromPROPVARIANT(realName, value); } void CSingleMethodProps::Init() { Clear(); #ifndef _7ZIP_ST _numProcessors = _numThreads = NWindows::NSystem::GetNumberOfProcessors(); AddNumThreadsProp(_numThreads); #endif _level = (UInt32)(Int32)-1; } HRESULT CSingleMethodProps::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { Init(); for (UInt32 i = 0; i < numProps; i++) { UString name = names[i]; name.MakeLower_Ascii(); if (name.IsEmpty()) return E_INVALIDARG; const PROPVARIANT &value = values[i]; if (name[0] == L'x') { UInt32 a = 9; RINOK(ParsePropToUInt32(name.Ptr(1), value, a)); _level = a; AddLevelProp(a); } else if (name.IsPrefixedBy(L"mt")) { #ifndef _7ZIP_ST RINOK(ParseMtProp(name.Ptr(2), value, _numProcessors, _numThreads)); AddNumThreadsProp(_numThreads); #endif } else return ParseMethodFromPROPVARIANT(names[i], value); } return S_OK; } } src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.h000066400000000000000000000024621325366651500226460ustar00rootroot00000000000000// HandlerOut.h #ifndef __HANDLER_OUT_H #define __HANDLER_OUT_H #include "../../Common/MethodProps.h" namespace NArchive { class CMultiMethodProps { UInt32 _level; public: #ifndef _7ZIP_ST UInt32 _numThreads; UInt32 _numProcessors; #endif UInt32 _crcSize; CObjectVector _methods; COneMethodInfo _filterMethod; bool _autoFilter; void SetGlobalLevelAndThreads(COneMethodInfo &oneMethodInfo #ifndef _7ZIP_ST , UInt32 numThreads #endif ); unsigned GetNumEmptyMethods() const { unsigned i; for (i = 0; i < _methods.Size(); i++) if (!_methods[i].IsEmpty()) break; return i; } int GetLevel() const { return _level == (UInt32)(Int32)-1 ? 5 : (int)_level; } void Init(); CMultiMethodProps() { Init(); } HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); }; class CSingleMethodProps: public COneMethodInfo { UInt32 _level; public: #ifndef _7ZIP_ST UInt32 _numThreads; UInt32 _numProcessors; #endif void Init(); CSingleMethodProps() { Init(); } int GetLevel() const { return _level == (UInt32)(Int32)-1 ? 5 : (int)_level; } HRESULT SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps); }; } #endif src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.cpp000066400000000000000000000023371325366651500240430ustar00rootroot00000000000000// InStreamWithCRC.cpp #include "StdAfx.h" #include "InStreamWithCRC.h" STDMETHODIMP CSequentialInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessed = 0; HRESULT result = S_OK; if (_stream) result = _stream->Read(data, size, &realProcessed); _size += realProcessed; if (size != 0 && realProcessed == 0) _wasFinished = true; _crc = CrcUpdate(_crc, data, realProcessed); if (processedSize) *processedSize = realProcessed; return result; } STDMETHODIMP CInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessed = 0; HRESULT result = S_OK; if (_stream) result = _stream->Read(data, size, &realProcessed); _size += realProcessed; /* if (size != 0 && realProcessed == 0) _wasFinished = true; */ _crc = CrcUpdate(_crc, data, realProcessed); if (processedSize) *processedSize = realProcessed; return result; } STDMETHODIMP CInStreamWithCRC::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin != STREAM_SEEK_SET || offset != 0) return E_FAIL; _size = 0; _crc = CRC_INIT_VAL; return _stream->Seek(offset, seekOrigin, newPosition); } src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.h000066400000000000000000000031171325366651500235050ustar00rootroot00000000000000// InStreamWithCRC.h #ifndef __IN_STREAM_WITH_CRC_H #define __IN_STREAM_WITH_CRC_H #include "../../../../C/7zCrc.h" #include "../../../Common/MyCom.h" #include "../../IStream.h" class CSequentialInStreamWithCRC: public ISequentialInStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); private: CMyComPtr _stream; UInt64 _size; UInt32 _crc; bool _wasFinished; public: void SetStream(ISequentialInStream *stream) { _stream = stream; } void Init() { _size = 0; _wasFinished = false; _crc = CRC_INIT_VAL; } void ReleaseStream() { _stream.Release(); } UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } UInt64 GetSize() const { return _size; } bool WasFinished() const { return _wasFinished; } }; class CInStreamWithCRC: public IInStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP1(IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); private: CMyComPtr _stream; UInt64 _size; UInt32 _crc; // bool _wasFinished; public: void SetStream(IInStream *stream) { _stream = stream; } void Init() { _size = 0; // _wasFinished = false; _crc = CRC_INIT_VAL; } void ReleaseStream() { _stream.Release(); } UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } UInt64 GetSize() const { return _size; } // bool WasFinished() const { return _wasFinished; } }; #endif src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.cpp000066400000000000000000000033041325366651500236500ustar00rootroot00000000000000// Archive/Common/ItemNameUtils.cpp #include "StdAfx.h" #include "ItemNameUtils.h" namespace NArchive { namespace NItemName { static const wchar_t kOSDirDelimiter = WCHAR_PATH_SEPARATOR; static const wchar_t kDirDelimiter = L'/'; void ReplaceToOsPathSeparator(wchar_t *s) { #ifdef _WIN32 for (;;) { wchar_t c = *s; if (c == 0) break; if (c == kDirDelimiter) *s = kOSDirDelimiter; s++; } #endif } UString MakeLegalName(const UString &name) { UString zipName = name; zipName.Replace(kOSDirDelimiter, kDirDelimiter); return zipName; } UString GetOSName(const UString &name) { UString newName = name; newName.Replace(kDirDelimiter, kOSDirDelimiter); return newName; } UString GetOSName2(const UString &name) { if (name.IsEmpty()) return UString(); UString newName = GetOSName(name); if (newName.Back() == kOSDirDelimiter) newName.DeleteBack(); return newName; } void ConvertToOSName2(UString &name) { if (!name.IsEmpty()) { name.Replace(kDirDelimiter, kOSDirDelimiter); if (name.Back() == kOSDirDelimiter) name.DeleteBack(); } } bool HasTailSlash(const AString &name, UINT #if defined(_WIN32) && !defined(UNDER_CE) codePage #endif ) { if (name.IsEmpty()) return false; LPCSTR prev = #if defined(_WIN32) && !defined(UNDER_CE) CharPrevExA((WORD)codePage, name, &name[name.Len()], 0); #else (LPCSTR)(name) + (name.Len() - 1); #endif return (*prev == '/'); } #ifndef _WIN32 UString WinNameToOSName(const UString &name) { UString newName = name; newName.Replace(L'\\', kOSDirDelimiter); return newName; } #endif }} src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.h000066400000000000000000000012221325366651500233120ustar00rootroot00000000000000// Archive/Common/ItemNameUtils.h #ifndef __ARCHIVE_ITEM_NAME_UTILS_H #define __ARCHIVE_ITEM_NAME_UTILS_H #include "../../../Common/MyString.h" namespace NArchive { namespace NItemName { void ReplaceToOsPathSeparator(wchar_t *s); UString MakeLegalName(const UString &name); UString GetOSName(const UString &name); UString GetOSName2(const UString &name); void ConvertToOSName2(UString &name); bool HasTailSlash(const AString &name, UINT codePage); #ifdef _WIN32 inline UString WinNameToOSName(const UString &name) { return name; } #else UString WinNameToOSName(const UString &name); #endif }} #endif src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.cpp000066400000000000000000000113311325366651500233750ustar00rootroot00000000000000// MultiStream.cpp #include "StdAfx.h" #include "MultiStream.h" STDMETHODIMP CMultiStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (_pos >= _totalLength) return S_OK; { unsigned left = 0, mid = _streamIndex, right = Streams.Size(); for (;;) { CSubStreamInfo &m = Streams[mid]; if (_pos < m.GlobalOffset) right = mid; else if (_pos >= m.GlobalOffset + m.Size) left = mid + 1; else { _streamIndex = mid; break; } mid = (left + right) / 2; } _streamIndex = mid; } CSubStreamInfo &s = Streams[_streamIndex]; UInt64 localPos = _pos - s.GlobalOffset; if (localPos != s.LocalPos) { RINOK(s.Stream->Seek(localPos, STREAM_SEEK_SET, &s.LocalPos)); } UInt64 rem = s.Size - localPos; if (size > rem) size = (UInt32)rem; HRESULT result = s.Stream->Read(data, size, &size); _pos += size; s.LocalPos += size; if (processedSize) *processedSize = size; return result; } STDMETHODIMP CMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _pos; break; case STREAM_SEEK_END: offset += _totalLength; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _pos = offset; if (newPosition) *newPosition = offset; return S_OK; } /* class COutVolumeStream: public ISequentialOutStream, public CMyUnknownImp { unsigned _volIndex; UInt64 _volSize; UInt64 _curPos; CMyComPtr _volumeStream; COutArchive _archive; CCRC _crc; public: MY_UNKNOWN_IMP CFileItem _file; CUpdateOptions _options; CMyComPtr VolumeCallback; void Init(IArchiveUpdateCallback2 *volumeCallback, const UString &name) { _file.Name = name; _file.IsStartPosDefined = true; _file.StartPos = 0; VolumeCallback = volumeCallback; _volIndex = 0; _volSize = 0; } HRESULT Flush(); STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; HRESULT COutVolumeStream::Flush() { if (_volumeStream) { _file.UnPackSize = _curPos; _file.FileCRC = _crc.GetDigest(); RINOK(WriteVolumeHeader(_archive, _file, _options)); _archive.Close(); _volumeStream.Release(); _file.StartPos += _file.UnPackSize; } return S_OK; } */ /* STDMETHODIMP COutMultiStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { if (_streamIndex >= Streams.Size()) { CSubStreamInfo subStream; RINOK(VolumeCallback->GetVolumeSize(Streams.Size(), &subStream.Size)); RINOK(VolumeCallback->GetVolumeStream(Streams.Size(), &subStream.Stream)); subStream.Pos = 0; Streams.Add(subStream); continue; } CSubStreamInfo &subStream = Streams[_streamIndex]; if (_offsetPos >= subStream.Size) { _offsetPos -= subStream.Size; _streamIndex++; continue; } if (_offsetPos != subStream.Pos) { CMyComPtr outStream; RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream)); RINOK(outStream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); subStream.Pos = _offsetPos; } UInt32 curSize = (UInt32)MyMin((UInt64)size, subStream.Size - subStream.Pos); UInt32 realProcessed; RINOK(subStream.Stream->Write(data, curSize, &realProcessed)); data = (void *)((Byte *)data + realProcessed); size -= realProcessed; subStream.Pos += realProcessed; _offsetPos += realProcessed; _absPos += realProcessed; if (_absPos > _length) _length = _absPos; if (processedSize != NULL) *processedSize += realProcessed; if (subStream.Pos == subStream.Size) { _streamIndex++; _offsetPos = 0; } if (realProcessed != curSize && realProcessed == 0) return E_FAIL; } return S_OK; } STDMETHODIMP COutMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _absPos; break; case STREAM_SEEK_END: offset += _length; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _absPos = offset; _offsetPos = _absPos; _streamIndex = 0; if (newPosition) *newPosition = offset; return S_OK; } */ src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.h000066400000000000000000000035001325366651500230410ustar00rootroot00000000000000// MultiStream.h #ifndef __MULTI_STREAM_H #define __MULTI_STREAM_H #include "../../../Common/MyCom.h" #include "../../../Common/MyVector.h" #include "../../IStream.h" class CMultiStream: public IInStream, public CMyUnknownImp { UInt64 _pos; UInt64 _totalLength; unsigned _streamIndex; public: struct CSubStreamInfo { CMyComPtr Stream; UInt64 Size; UInt64 GlobalOffset; UInt64 LocalPos; CSubStreamInfo(): Size(0), GlobalOffset(0), LocalPos(0) {} }; CObjectVector Streams; HRESULT Init() { UInt64 total = 0; FOR_VECTOR (i, Streams) { CSubStreamInfo &s = Streams[i]; s.GlobalOffset = total; total += Streams[i].Size; RINOK(s.Stream->Seek(0, STREAM_SEEK_CUR, &s.LocalPos)); } _totalLength = total; _pos = 0; _streamIndex = 0; return S_OK; } MY_UNKNOWN_IMP1(IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; /* class COutMultiStream: public IOutStream, public CMyUnknownImp { unsigned _streamIndex; // required stream UInt64 _offsetPos; // offset from start of _streamIndex index UInt64 _absPos; UInt64 _length; struct CSubStreamInfo { CMyComPtr Stream; UInt64 Size; UInt64 Pos; }; CObjectVector Streams; public: CMyComPtr VolumeCallback; void Init() { _streamIndex = 0; _offsetPos = 0; _absPos = 0; _length = 0; } MY_UNKNOWN_IMP1(IOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; */ #endif src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp000066400000000000000000000006531325366651500242430ustar00rootroot00000000000000// OutStreamWithCRC.cpp #include "StdAfx.h" #include "OutStreamWithCRC.h" STDMETHODIMP COutStreamWithCRC::Write(const void *data, UInt32 size, UInt32 *processedSize) { HRESULT result = S_OK; if (_stream) result = _stream->Write(data, size, &size); if (_calculate) _crc = CrcUpdate(_crc, data, size); _size += size; if (processedSize != NULL) *processedSize = size; return result; } src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.h000066400000000000000000000016511325366651500237070ustar00rootroot00000000000000// OutStreamWithCRC.h #ifndef __OUT_STREAM_WITH_CRC_H #define __OUT_STREAM_WITH_CRC_H #include "../../../../C/7zCrc.h" #include "../../../Common/MyCom.h" #include "../../IStream.h" class COutStreamWithCRC: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; UInt32 _crc; bool _calculate; public: MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); void SetStream(ISequentialOutStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(bool calculate = true) { _size = 0; _calculate = calculate; _crc = CRC_INIT_VAL; } void EnableCalc(bool calculate) { _calculate = calculate; } void InitCRC() { _crc = CRC_INIT_VAL; } UInt64 GetSize() const { return _size; } UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } }; #endif src/libs/7zip/win/CPP/7zip/Archive/Common/ParseProperties.h000066400000000000000000000001361325366651500237240ustar00rootroot00000000000000// ParseProperties.h #ifndef __PARSE_PROPERTIES_H #define __PARSE_PROPERTIES_H #endif src/libs/7zip/win/CPP/7zip/Archive/Common/StdAfx.h000066400000000000000000000001501325366651500217620ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../../../Common/Common.h" #endif src/libs/7zip/win/CPP/7zip/Archive/IArchive.h000066400000000000000000000371411325366651500210450ustar00rootroot00000000000000// IArchive.h #ifndef __IARCHIVE_H #define __IARCHIVE_H #include "../IProgress.h" #include "../IStream.h" #include "../PropID.h" #define ARCHIVE_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 6, x) #define ARCHIVE_INTERFACE(i, x) ARCHIVE_INTERFACE_SUB(i, IUnknown, x) namespace NFileTimeType { enum EEnum { kWindows, kUnix, kDOS }; } namespace NArcInfoFlags { const UInt32 kKeepName = 1 << 0; // keep name of file in archive name const UInt32 kAltStreams = 1 << 1; // the handler supports alt streams const UInt32 kNtSecure = 1 << 2; // the handler supports NT security const UInt32 kFindSignature = 1 << 3; // the handler can find start of archive const UInt32 kMultiSignature = 1 << 4; // there are several signatures const UInt32 kUseGlobalOffset = 1 << 5; // the seek position of stream must be set as global offset const UInt32 kStartOpen = 1 << 6; // call handler for each start position const UInt32 kPureStartOpen = 1 << 7; // call handler only for start of file const UInt32 kBackwardOpen = 1 << 8; // archive can be open backward const UInt32 kPreArc = 1 << 9; // such archive can be stored before real archive (like SFX stub) const UInt32 kSymLinks = 1 << 10; // the handler supports symbolic links const UInt32 kHardLinks = 1 << 11; // the handler supports hard links } namespace NArchive { namespace NHandlerPropID { enum { kName = 0, // VT_BSTR kClassID, // binary GUID in VT_BSTR kExtension, // VT_BSTR kAddExtension, // VT_BSTR kUpdate, // VT_BOOL kKeepName, // VT_BOOL kSignature, // binary in VT_BSTR kMultiSignature, // binary in VT_BSTR kSignatureOffset, // VT_UI4 kAltStreams, // VT_BOOL kNtSecure, // VT_BOOL kFlags // VT_UI4 // kVersion // VT_UI4 ((VER_MAJOR << 8) | VER_MINOR) }; } namespace NExtract { namespace NAskMode { enum { kExtract = 0, kTest, kSkip }; } namespace NOperationResult { enum { kOK = 0, kUnsupportedMethod, kDataError, kCRCError, kUnavailable, kUnexpectedEnd, kDataAfterEnd, kIsNotArc, kHeadersError }; } } namespace NUpdate { namespace NOperationResult { enum { kOK = 0 , // kError }; } } } #define INTERFACE_IArchiveOpenCallback(x) \ STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes) x; \ STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes) x; \ ARCHIVE_INTERFACE(IArchiveOpenCallback, 0x10) { INTERFACE_IArchiveOpenCallback(PURE); }; /* IArchiveExtractCallback::GetStream Result: (*inStream == NULL) - for directories (*inStream == NULL) - if link (hard link or symbolic link) was created */ #define INTERFACE_IArchiveExtractCallback(x) \ INTERFACE_IProgress(x) \ STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) x; \ STDMETHOD(PrepareOperation)(Int32 askExtractMode) x; \ STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) x; \ ARCHIVE_INTERFACE_SUB(IArchiveExtractCallback, IProgress, 0x20) { INTERFACE_IArchiveExtractCallback(PURE) }; #define INTERFACE_IArchiveOpenVolumeCallback(x) \ STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) x; \ STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) x; \ ARCHIVE_INTERFACE(IArchiveOpenVolumeCallback, 0x30) { INTERFACE_IArchiveOpenVolumeCallback(PURE); }; ARCHIVE_INTERFACE(IInArchiveGetStream, 0x40) { STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream) PURE; }; ARCHIVE_INTERFACE(IArchiveOpenSetSubArchiveName, 0x50) { STDMETHOD(SetSubArchiveName)(const wchar_t *name) PURE; }; /* IInArchive::Open stream if (kUseGlobalOffset), stream current position can be non 0. if (!kUseGlobalOffset), stream current position is 0. if (maxCheckStartPosition == NULL), the handler can try to search archive start in stream if (*maxCheckStartPosition == 0), the handler must check only current position as archive start IInArchive::Extract: indices must be sorted numItems = (UInt32)(Int32)-1 = 0xFFFFFFFF means "all files" testMode != 0 means "test files without writing to outStream" IInArchive::GetArchiveProperty: kpidOffset - start offset of archive. VT_EMPTY : means offset = 0. VT_UI4, VT_UI8, VT_I8 : result offset; negative values is allowed kpidPhySize - size of archive. VT_EMPTY means unknown size. kpidPhySize is allowed to be larger than file size. In that case it must show supposed size. kpidIsDeleted: kpidIsAltStream: kpidIsAux: kpidINode: must return VARIANT_TRUE (VT_BOOL), if archive can support that property in GetProperty. Notes: Don't call IInArchive functions for same IInArchive object from different threads simultaneously. Some IInArchive handlers will work incorrectly in that case. */ /* MSVC allows the code where there is throw() in declaration of function, but there is no throw() in definition of function. */ #ifdef _MSC_VER #define MY_NO_THROW_DECL_ONLY throw() #else #define MY_NO_THROW_DECL_ONLY #endif #define INTERFACE_IInArchive(x) \ STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(Close)() MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetNumberOfItems)(UInt32 *numItems) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetNumberOfProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetPropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ STDMETHOD(GetArchivePropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; ARCHIVE_INTERFACE(IInArchive, 0x60) { INTERFACE_IInArchive(PURE) }; namespace NParentType { enum { kDir = 0, kAltStream }; }; namespace NPropDataType { const UInt32 kMask_ZeroEnd = 1 << 4; // const UInt32 kMask_BigEndian = 1 << 5; const UInt32 kMask_Utf = 1 << 6; // const UInt32 kMask_Utf8 = kMask_Utf | 0; const UInt32 kMask_Utf16 = kMask_Utf | 1; // const UInt32 kMask_Utf32 = kMask_Utf | 2; const UInt32 kNotDefined = 0; const UInt32 kRaw = 1; const UInt32 kUtf16z = kMask_Utf16 | kMask_ZeroEnd; }; // UTF string (pointer to wchar_t) with zero end and little-endian. #define PROP_DATA_TYPE_wchar_t_PTR_Z_LE ((NPropDataType::kMask_Utf | NPropDataType::kMask_ZeroEnd) + (sizeof(wchar_t) >> 1)) /* GetRawProp: Result: S_OK - even if property is not set */ #define INTERFACE_IArchiveGetRawProps(x) \ STDMETHOD(GetParent)(UInt32 index, UInt32 *parent, UInt32 *parentType) x; \ STDMETHOD(GetRawProp)(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) x; \ STDMETHOD(GetNumRawProps)(UInt32 *numProps) x; \ STDMETHOD(GetRawPropInfo)(UInt32 index, BSTR *name, PROPID *propID) x; ARCHIVE_INTERFACE(IArchiveGetRawProps, 0x70) { INTERFACE_IArchiveGetRawProps(PURE) }; #define INTERFACE_IArchiveGetRootProps(x) \ STDMETHOD(GetRootProp)(PROPID propID, PROPVARIANT *value) x; \ STDMETHOD(GetRootRawProp)(PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) x; \ ARCHIVE_INTERFACE(IArchiveGetRootProps, 0x71) { INTERFACE_IArchiveGetRootProps(PURE) }; ARCHIVE_INTERFACE(IArchiveOpenSeq, 0x61) { STDMETHOD(OpenSeq)(ISequentialInStream *stream) PURE; }; /* OpenForSize Result: S_FALSE - is not archive ? - DATA error */ /* const UInt32 kOpenFlags_RealPhySize = 1 << 0; const UInt32 kOpenFlags_NoSeek = 1 << 1; // const UInt32 kOpenFlags_BeforeExtract = 1 << 2; */ /* Flags: 0 - opens archive with IInStream, if IInStream interface is supported - if phySize is not available, it doesn't try to make full parse to get phySize kOpenFlags_NoSeek - ArcOpen2 function doesn't use IInStream interface, even if it's available kOpenFlags_RealPhySize - the handler will try to get PhySize, even if it requires full decompression for file if handler is not allowed to use IInStream and the flag kOpenFlags_RealPhySize is not specified, the handler can return S_OK, but it doesn't check even Signature. So next Extract can be called for that sequential stream. */ /* ARCHIVE_INTERFACE(IArchiveOpen2, 0x62) { STDMETHOD(ArcOpen2)(ISequentialInStream *stream, UInt32 flags, IArchiveOpenCallback *openCallback) PURE; }; */ // ---------- UPDATE ---------- /* GetUpdateItemInfo outs: *newData *newProps 0 0 - Copy data and properties from archive 0 1 - Copy data from archive, request new properties 1 0 - that combination is unused now 1 1 - Request new data and new properties. It can be used even for folders indexInArchive = -1 if there is no item in archive, or if it doesn't matter. GetStream out: Result: S_OK: (*inStream == NULL) - only for directories - the bug was fixed in 9.33: (*Stream == NULL) was in case of anti-file (*inStream != NULL) - for any file, even for empty file or anti-file S_FALSE - skip that file (don't add item to archive) - (client code can't open stream of that file by some reason) (*inStream == NULL) The order of calling for hard links: - GetStream() - GetProperty(kpidHardLink) */ #define INTERFACE_IArchiveUpdateCallback(x) \ INTERFACE_IProgress(x); \ STDMETHOD(GetUpdateItemInfo)(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive) x; \ STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) x; \ STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream) x; \ STDMETHOD(SetOperationResult)(Int32 operationResult) x; \ ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback, IProgress, 0x80) { INTERFACE_IArchiveUpdateCallback(PURE); }; #define INTERFACE_IArchiveUpdateCallback2(x) \ INTERFACE_IArchiveUpdateCallback(x) \ STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size) x; \ STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream) x; \ ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback2, IArchiveUpdateCallback, 0x82) { INTERFACE_IArchiveUpdateCallback2(PURE); }; /* UpdateItems() ------------- outStream: output stream. (the handler) MUST support the case when Seek position in outStream is not ZERO. but the caller calls with empty outStream and seek position is ZERO?? archives with stub: If archive is open and the handler and (Offset > 0), then the handler knows about stub size. UpdateItems(): 1) the handler MUST copy that stub to outStream 2) the caller MUST NOT copy the stub to outStream, if "rsfx" property is set with SetProperties the handler must support the case where ISequentialOutStream *outStream */ #define INTERFACE_IOutArchive(x) \ STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) x; \ STDMETHOD(GetFileTimeType)(UInt32 *type) x; ARCHIVE_INTERFACE(IOutArchive, 0xA0) { INTERFACE_IOutArchive(PURE) }; ARCHIVE_INTERFACE(ISetProperties, 0x03) { STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) PURE; }; ARCHIVE_INTERFACE(IArchiveKeepModeForNextOpen, 0x04) { STDMETHOD(KeepModeForNextOpen)() PURE; }; /* Exe handler: the handler for executable format (PE, ELF, Mach-O). SFX archive: executable stub + some tail data. before 9.31: exe handler didn't parse SFX archives as executable format. for 9.31+: exe handler parses SFX archives as executable format, only if AllowTail(1) was called */ ARCHIVE_INTERFACE(IArchiveAllowTail, 0x05) { STDMETHOD(AllowTail)(Int32 allowTail) PURE; }; #define IMP_IInArchive_GetProp(k) \ (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ { if (index >= ARRAY_SIZE(k)) return E_INVALIDARG; \ *propID = k[index]; *varType = k7z_PROPID_To_VARTYPE[(unsigned)*propID]; *name = 0; return S_OK; } \ #define IMP_IInArchive_GetProp_WITH_NAME(k) \ (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ { if (index >= ARRAY_SIZE(k)) return E_INVALIDARG; \ const STATPROPSTG &srcItem = k[index]; \ *propID = srcItem.propid; *varType = srcItem.vt; \ if (srcItem.lpwstrName == 0) *name = 0; else *name = ::SysAllocString(srcItem.lpwstrName); return S_OK; } \ #define IMP_IInArchive_Props \ STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kProps); return S_OK; } \ STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp(kProps) #define IMP_IInArchive_Props_WITH_NAME \ STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kProps); return S_OK; } \ STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kProps) #define IMP_IInArchive_ArcProps \ STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kArcProps); return S_OK; } \ STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp(kArcProps) #define IMP_IInArchive_ArcProps_WITH_NAME \ STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProps) \ { *numProps = ARRAY_SIZE(kArcProps); return S_OK; } \ STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kArcProps) #define IMP_IInArchive_ArcProps_NO_Table \ STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProps) \ { *numProps = 0; return S_OK; } \ STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32, BSTR *, PROPID *, VARTYPE *) \ { return E_NOTIMPL; } \ #define IMP_IInArchive_ArcProps_NO \ IMP_IInArchive_ArcProps_NO_Table \ STDMETHODIMP CHandler::GetArchiveProperty(PROPID, PROPVARIANT *value) \ { value->vt = VT_EMPTY; return S_OK; } #define k_IsArc_Res_NO 0 #define k_IsArc_Res_YES 1 #define k_IsArc_Res_NEED_MORE 2 // #define k_IsArc_Res_YES_LOW_PROB 3 #define API_FUNC_IsArc EXTERN_C UInt32 WINAPI #define API_FUNC_static_IsArc extern "C" { static UInt32 WINAPI extern "C" { typedef HRESULT (WINAPI *Func_CreateObject)(const GUID *clsID, const GUID *iid, void **outObject); typedef UInt32 (WINAPI *Func_IsArc)(const Byte *p, size_t size); typedef HRESULT (WINAPI *Func_GetIsArc)(UInt32 formatIndex, Func_IsArc *isArc); typedef HRESULT (WINAPI *Func_GetNumberOfFormats)(UInt32 *numFormats); typedef HRESULT (WINAPI *Func_GetHandlerProperty)(PROPID propID, PROPVARIANT *value); typedef HRESULT (WINAPI *Func_GetHandlerProperty2)(UInt32 index, PROPID propID, PROPVARIANT *value); typedef HRESULT (WINAPI *Func_SetCaseSensitive)(Int32 caseSensitive); typedef HRESULT (WINAPI *Func_SetLargePageMode)(); typedef IOutArchive * (*Func_CreateOutArchive)(); typedef IInArchive * (*Func_CreateInArchive)(); } #endif src/libs/7zip/win/CPP/7zip/Archive/LzmaHandler.cpp000066400000000000000000000344631325366651500221130ustar00rootroot00000000000000// LzmaHandler.cpp #include "StdAfx.h" #include "../../../C/CpuArch.h" #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" #include "../../Windows/PropVariant.h" #include "../Common/CreateCoder.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "../Compress/LzmaDecoder.h" #include "Common/DummyOutStream.h" using namespace NWindows; namespace NArchive { namespace NLzma { static bool CheckDicSize(const Byte *p) { UInt32 dicSize = GetUi32(p); if (dicSize == 1) return true; for (unsigned i = 0; i <= 30; i++) if (dicSize == ((UInt32)2 << i) || dicSize == ((UInt32)3 << i)) return true; return (dicSize == 0xFFFFFFFF); } static const Byte kProps[] = { kpidSize, kpidPackSize, kpidMethod }; static const Byte kArcProps[] = { kpidNumStreams }; struct CHeader { UInt64 Size; Byte FilterID; Byte LzmaProps[5]; UInt32 GetDicSize() const { return GetUi32(LzmaProps + 1); } bool HasSize() const { return (Size != (UInt64)(Int64)-1); } bool Parse(const Byte *buf, bool isThereFilter); }; bool CHeader::Parse(const Byte *buf, bool isThereFilter) { FilterID = 0; if (isThereFilter) FilterID = buf[0]; const Byte *sig = buf + (isThereFilter ? 1 : 0); for (int i = 0; i < 5; i++) LzmaProps[i] = sig[i]; Size = GetUi64(sig + 5); return LzmaProps[0] < 5 * 5 * 9 && FilterID < 2 && (!HasSize() || Size < ((UInt64)1 << 56)) && CheckDicSize(LzmaProps + 1); } class CDecoder { CMyComPtr _lzmaDecoder; CMyComPtr _bcjStream; public: NCompress::NLzma::CDecoder *_lzmaDecoderSpec; ~CDecoder(); HRESULT Create(DECL_EXTERNAL_CODECS_LOC_VARS bool filtered, ISequentialInStream *inStream); HRESULT Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress); UInt64 GetInputProcessedSize() const { return _lzmaDecoderSpec->GetInputProcessedSize(); } void ReleaseInStream() { if (_lzmaDecoder) _lzmaDecoderSpec->ReleaseInStream(); } HRESULT ReadInput(Byte *data, UInt32 size, UInt32 *processedSize) { return _lzmaDecoderSpec->ReadFromInputStream(data, size, processedSize); } }; static const UInt32 k_BCJ = 0x03030103; HRESULT CDecoder::Create( DECL_EXTERNAL_CODECS_LOC_VARS bool filteredMode, ISequentialInStream *inStream) { if (!_lzmaDecoder) { _lzmaDecoderSpec = new NCompress::NLzma::CDecoder; _lzmaDecoderSpec->FinishStream = true; _lzmaDecoder = _lzmaDecoderSpec; } if (filteredMode) { if (!_bcjStream) { CMyComPtr coder; RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS k_BCJ, coder, false)); if (!coder) return E_NOTIMPL; coder.QueryInterface(IID_ISequentialOutStream, &_bcjStream); if (!_bcjStream) return E_NOTIMPL; } } return _lzmaDecoderSpec->SetInStream(inStream); } CDecoder::~CDecoder() { ReleaseInStream(); } HRESULT CDecoder::Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { if (header.FilterID > 1) return E_NOTIMPL; { CMyComPtr setDecoderProperties; _lzmaDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); if (!setDecoderProperties) return E_NOTIMPL; RINOK(setDecoderProperties->SetDecoderProperties2(header.LzmaProps, 5)); } CMyComPtr setOutStream; bool filteredMode = (header.FilterID == 1); if (filteredMode) { _bcjStream.QueryInterface(IID_ICompressSetOutStream, &setOutStream); if (!setOutStream) return E_NOTIMPL; RINOK(setOutStream->SetOutStream(outStream)); outStream = _bcjStream; } const UInt64 *Size = header.HasSize() ? &header.Size : NULL; HRESULT res = _lzmaDecoderSpec->CodeResume(outStream, Size, progress); if (filteredMode) { CMyComPtr flush; _bcjStream.QueryInterface(IID_IOutStreamFlush, &flush); if (flush) { HRESULT res2 = flush->Flush(); if (res == S_OK) res = res2; } HRESULT res2 = setOutStream->ReleaseOutStream(); if (res == S_OK) res = res2; } RINOK(res); if (header.HasSize()) if (_lzmaDecoderSpec->GetOutputProcessedSize() != header.Size) return S_FALSE; return S_OK; } class CHandler: public IInArchive, public IArchiveOpenSeq, PUBLIC_ISetCompressCodecsInfo public CMyUnknownImp { CHeader _header; bool _lzma86; CMyComPtr _stream; CMyComPtr _seqStream; bool _isArc; bool _needSeekToStart; bool _dataAfterEnd; bool _needMoreInput; bool _packSize_Defined; bool _unpackSize_Defined; bool _numStreams_Defined; bool _unsupported; bool _dataError; UInt64 _packSize; UInt64 _unpackSize; UInt64 _numStreams; DECL_EXTERNAL_CODECS_VARS DECL_ISetCompressCodecsInfo public: MY_QUERYINTERFACE_BEGIN2(IInArchive) MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_END MY_ADDREF_RELEASE INTERFACE_IInArchive(;) STDMETHOD(OpenSeq)(ISequentialInStream *stream); CHandler(bool lzma86) { _lzma86 = lzma86; } unsigned GetHeaderSize() const { return 5 + 8 + (_lzma86 ? 1 : 0); } }; IMP_IInArchive_Props IMP_IInArchive_ArcProps STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidPhySize: if (_packSize_Defined) prop = _packSize; break; case kpidNumStreams: if (_numStreams_Defined) prop = _numStreams; break; case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break; case kpidErrorFlags: { UInt32 v = 0; if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd; if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; if (_dataError) v |= kpv_ErrorFlags_DataError; prop = v; } } prop.Detach(value); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = 1; return S_OK; } static void DictSizeToString(UInt32 value, char *s) { for (int i = 0; i <= 31; i++) if (((UInt32)1 << i) == value) { ::ConvertUInt32ToString(i, s); return; } char c = 'b'; if ((value & ((1 << 20) - 1)) == 0) { value >>= 20; c = 'm'; } else if ((value & ((1 << 10) - 1)) == 0) { value >>= 10; c = 'k'; } ::ConvertUInt32ToString(value, s); s += MyStringLen(s); *s++ = c; *s = 0; } STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidSize: if (_stream && _header.HasSize()) prop = _header.Size; break; case kpidPackSize: if (_packSize_Defined) prop = _packSize; break; case kpidMethod: if (_stream) { char sz[64]; char *s = sz; if (_header.FilterID != 0) s = MyStpCpy(s, "BCJ "); s = MyStpCpy(s, "LZMA:"); DictSizeToString(_header.GetDicSize(), s); prop = sz; } break; } prop.Detach(value); return S_OK; } API_FUNC_static_IsArc IsArc_Lzma(const Byte *p, size_t size) { const UInt32 kHeaderSize = 1 + 4 + 8; if (size < kHeaderSize) return k_IsArc_Res_NEED_MORE; if (p[0] >= 5 * 5 * 9) return k_IsArc_Res_NO; UInt64 unpackSize = GetUi64(p + 1 + 4); if (unpackSize != (UInt64)(Int64)-1) { if (size >= ((UInt64)1 << 56)) return k_IsArc_Res_NO; } if (unpackSize != 0) { if (size < kHeaderSize + 2) return k_IsArc_Res_NEED_MORE; if (p[kHeaderSize] != 0) return k_IsArc_Res_NO; if (unpackSize != (UInt64)(Int64)-1) { if ((p[kHeaderSize + 1] & 0x80) != 0) return k_IsArc_Res_NO; } } if (!CheckDicSize(p + 1)) // return k_IsArc_Res_YES_LOW_PROB; return k_IsArc_Res_NO; return k_IsArc_Res_YES; } } API_FUNC_static_IsArc IsArc_Lzma86(const Byte *p, size_t size) { if (size < 1) return k_IsArc_Res_NEED_MORE; Byte filterID = p[0]; if (filterID != 0 && filterID != 1) return k_IsArc_Res_NO; return IsArc_Lzma(p + 1, size - 1); } } STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *) { Close(); const UInt32 kBufSize = 1 + 5 + 8 + 2; Byte buf[kBufSize]; RINOK(ReadStream_FALSE(inStream, buf, kBufSize)); if (!_header.Parse(buf, _lzma86)) return S_FALSE; const Byte *start = buf + GetHeaderSize(); if (start[0] != 0 /* || (start[1] & 0x80) != 0 */ ) // empty stream with EOS is not 0x80 return S_FALSE; RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize)); if (_packSize >= 24 && _header.Size == 0 && _header.FilterID == 0 && _header.LzmaProps[0] == 0) return S_FALSE; _isArc = true; _stream = inStream; _seqStream = inStream; _needSeekToStart = true; return S_OK; } STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) { Close(); _isArc = true; _seqStream = stream; return S_OK; } STDMETHODIMP CHandler::Close() { _isArc = false; _packSize_Defined = false; _unpackSize_Defined = false; _numStreams_Defined = false; _dataAfterEnd = false; _needMoreInput = false; _unsupported = false; _dataError = false; _packSize = 0; _needSeekToStart = false; _stream.Release(); _seqStream.Release(); return S_OK; } class CCompressProgressInfoImp: public ICompressProgressInfo, public CMyUnknownImp { CMyComPtr Callback; public: UInt64 Offset; MY_UNKNOWN_IMP1(ICompressProgressInfo) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); void Init(IArchiveOpenCallback *callback) { Callback = callback; } }; STDMETHODIMP CCompressProgressInfoImp::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */) { if (Callback) { UInt64 files = 0; UInt64 value = Offset + *inSize; return Callback->SetCompleted(&files, &value); } return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN if (numItems == 0) return S_OK; if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; if (_packSize_Defined) extractCallback->SetTotal(_packSize); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); if (!testMode && !realOutStream) return S_OK; extractCallback->PrepareOperation(askMode); CDummyOutStream *outStreamSpec = new CDummyOutStream; CMyComPtr outStream(outStreamSpec); outStreamSpec->SetStream(realOutStream); outStreamSpec->Init(); realOutStream.Release(); CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, true); if (_needSeekToStart) { if (!_stream) return E_FAIL; RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); } else _needSeekToStart = true; CDecoder decoder; HRESULT result = decoder.Create( EXTERNAL_CODECS_VARS _lzma86, _seqStream); RINOK(result); bool firstItem = true; UInt64 packSize = 0; UInt64 unpackSize = 0; UInt64 numStreams = 0; bool dataAfterEnd = false; for (;;) { lps->InSize = packSize; lps->OutSize = unpackSize; RINOK(lps->SetCur()); const UInt32 kBufSize = 1 + 5 + 8; Byte buf[kBufSize]; const UInt32 headerSize = GetHeaderSize(); UInt32 processed; RINOK(decoder.ReadInput(buf, headerSize, &processed)); if (processed != headerSize) { if (processed != 0) dataAfterEnd = true; break; } CHeader st; if (!st.Parse(buf, _lzma86)) { dataAfterEnd = true; break; } numStreams++; firstItem = false; result = decoder.Code(st, outStream, progress); packSize = decoder.GetInputProcessedSize(); unpackSize = outStreamSpec->GetSize(); if (result == E_NOTIMPL) { _unsupported = true; result = S_FALSE; break; } if (result == S_FALSE) break; RINOK(result); } if (firstItem) { _isArc = false; result = S_FALSE; } else if (result == S_OK || result == S_FALSE) { if (dataAfterEnd) _dataAfterEnd = true; else if (decoder._lzmaDecoderSpec->NeedMoreInput) _needMoreInput = true; _packSize = packSize; _unpackSize = unpackSize; _numStreams = numStreams; _packSize_Defined = true; _unpackSize_Defined = true; _numStreams_Defined = true; } Int32 opResult = NExtract::NOperationResult::kOK; if (!_isArc) opResult = NExtract::NOperationResult::kIsNotArc; else if (_needMoreInput) opResult = NExtract::NOperationResult::kUnexpectedEnd; else if (_unsupported) opResult = NExtract::NOperationResult::kUnsupportedMethod; else if (_dataAfterEnd) opResult = NExtract::NOperationResult::kDataAfterEnd; else if (result == S_FALSE) opResult = NExtract::NOperationResult::kDataError; else if (result == S_OK) opResult = NExtract::NOperationResult::kOK; else return result; outStream.Release(); return extractCallback->SetOperationResult(opResult); COM_TRY_END } IMPL_ISetCompressCodecsInfo namespace NLzmaAr { IMP_CreateArcIn_2(CHandler(false)) static CArcInfo g_ArcInfo = { "lzma", "lzma", 0, 0xA, 0, { 0 }, // 2, { 0x5D, 0x00 }, 0, NArcInfoFlags::kStartOpen | NArcInfoFlags::kKeepName, CreateArc, NULL, IsArc_Lzma }; REGISTER_ARC(Lzma) } namespace NLzma86Ar { IMP_CreateArcIn_2(CHandler(true)) static CArcInfo g_ArcInfo = { "lzma86", "lzma86", 0, 0xB, 0, { 0 }, 0, NArcInfoFlags::kKeepName, CreateArc, NULL, IsArc_Lzma86 }; REGISTER_ARC(Lzma86) } }} src/libs/7zip/win/CPP/7zip/Archive/SplitHandler.cpp000066400000000000000000000211721325366651500222740ustar00rootroot00000000000000// SplitHandler.cpp #include "StdAfx.h" #include "../../Common/ComTry.h" #include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Compress/CopyCoder.h" #include "Common/MultiStream.h" using namespace NWindows; namespace NArchive { namespace NSplit { static const Byte kProps[] = { kpidPath, kpidSize }; static const Byte kArcProps[] = { kpidNumVolumes, kpidTotalPhySize }; class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { CObjectVector > _streams; CRecordVector _sizes; UString _subName; UInt64 _totalSize; HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); public: MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); }; IMP_IInArchive_Props IMP_IInArchive_ArcProps STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidMainSubfile: prop = (UInt32)0; break; case kpidPhySize: if (!_sizes.IsEmpty()) prop = _sizes[0]; break; case kpidTotalPhySize: prop = _totalSize; break; case kpidNumVolumes: prop = (UInt32)_streams.Size(); break; } prop.Detach(value); return S_OK; } struct CSeqName { UString _unchangedPart; UString _changedPart; bool _splitStyle; UString GetNextName() { UString newName; if (_splitStyle) { int i; int numLetters = _changedPart.Len(); for (i = numLetters - 1; i >= 0; i--) { wchar_t c = _changedPart[i]; if (c == 'z') { newName.InsertAtFront('a'); continue; } else if (c == 'Z') { newName.InsertAtFront('A'); continue; } c++; if ((c == 'z' || c == 'Z') && i == 0) { _unchangedPart += c; wchar_t newChar = (c == 'z') ? L'a' : L'A'; newName.Empty(); numLetters++; for (int k = 0; k < numLetters; k++) newName += newChar; break; } newName.InsertAtFront(c); i--; for (; i >= 0; i--) newName.InsertAtFront(_changedPart[i]); break; } } else { int i; int numLetters = _changedPart.Len(); for (i = numLetters - 1; i >= 0; i--) { wchar_t c = _changedPart[i]; if (c == '9') { newName.InsertAtFront('0'); if (i == 0) newName.InsertAtFront('1'); continue; } c++; newName.InsertAtFront(c); i--; for (; i >= 0; i--) newName.InsertAtFront(_changedPart[i]); break; } } _changedPart = newName; return _unchangedPart + _changedPart; } }; HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) { Close(); if (!callback) return S_FALSE; CMyComPtr volumeCallback; callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback); if (!volumeCallback) return S_FALSE; UString name; { NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidName, &prop)); if (prop.vt != VT_BSTR) return S_FALSE; name = prop.bstrVal; } int dotPos = name.ReverseFind('.'); const UString prefix = name.Left(dotPos + 1); const UString ext = name.Ptr(dotPos + 1); UString ext2 = ext; ext2.MakeLower_Ascii(); CSeqName seqName; unsigned numLetters = 2; bool splitStyle = false; if (ext2.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "aa")) { splitStyle = true; while (numLetters < ext2.Len()) { if (ext2[ext2.Len() - numLetters - 1] != 'a') break; numLetters++; } } else if (ext.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "01")) { while (numLetters < ext2.Len()) { if (ext2[ext2.Len() - numLetters - 1] != '0') break; numLetters++; } if (numLetters != ext.Len()) return S_FALSE; } else return S_FALSE; seqName._unchangedPart = prefix + ext.Left(ext2.Len() - numLetters); seqName._changedPart = ext.RightPtr(numLetters); seqName._splitStyle = splitStyle; if (prefix.Len() < 1) _subName = L"file"; else _subName.SetFrom(prefix, prefix.Len() - 1); UInt64 size; { NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; } _totalSize += size; _sizes.Add(size); _streams.Add(stream); { UInt64 numFiles = _streams.Size(); RINOK(callback->SetCompleted(&numFiles, NULL)); } for (;;) { const UString fullName = seqName.GetNextName(); CMyComPtr nextStream; HRESULT result = volumeCallback->GetStream(fullName, &nextStream); if (result == S_FALSE) break; if (result != S_OK) return result; if (!stream) break; { NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; } _totalSize += size; _sizes.Add(size); _streams.Add(nextStream); { UInt64 numFiles = _streams.Size(); RINOK(callback->SetCompleted(&numFiles, NULL)); } } if (_streams.Size() == 1) { if (splitStyle) return S_FALSE; } return S_OK; } STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) { COM_TRY_BEGIN HRESULT res = Open2(stream, callback); if (res != S_OK) Close(); return res; COM_TRY_END } STDMETHODIMP CHandler::Close() { _totalSize = 0; _subName.Empty(); _streams.Clear(); _sizes.Clear(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _streams.IsEmpty() ? 0 : 1; return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidPath: prop = _subName; break; case kpidSize: case kpidPackSize: prop = _totalSize; break; } prop.Detach(value); return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN if (numItems == 0) return S_OK; if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; UInt64 currentTotalSize = 0; RINOK(extractCallback->SetTotal(_totalSize)); CMyComPtr outStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(0, &outStream, askMode)); if (!testMode && !outStream) return S_OK; RINOK(extractCallback->PrepareOperation(askMode)); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr copyCoder = copyCoderSpec; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); FOR_VECTOR (i, _streams) { lps->InSize = lps->OutSize = currentTotalSize; RINOK(lps->SetCur()); IInStream *inStream = _streams[i]; RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); currentTotalSize += copyCoderSpec->TotalSize; } outStream.Release(); return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK); COM_TRY_END } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN if (index != 0) return E_INVALIDARG; *stream = 0; CMultiStream *streamSpec = new CMultiStream; CMyComPtr streamTemp = streamSpec; FOR_VECTOR (i, _streams) { CMultiStream::CSubStreamInfo subStreamInfo; subStreamInfo.Stream = _streams[i]; subStreamInfo.Size = _sizes[i]; streamSpec->Streams.Add(subStreamInfo); } streamSpec->Init(); *stream = streamTemp.Detach(); return S_OK; COM_TRY_END } IMP_CreateArcIn static CArcInfo g_ArcInfo = { "Split", "001", 0, 0xEA, 0, { 0 }, 0, 0, CreateArc }; REGISTER_ARC(Split) }} src/libs/7zip/win/CPP/7zip/Archive/StdAfx.h000066400000000000000000000001451325366651500205360ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../../Common/Common.h" #endif src/libs/7zip/win/CPP/7zip/Archive/XzHandler.cpp000066400000000000000000000554231325366651500216100ustar00rootroot00000000000000// XzHandler.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../../../C/XzCrc64.h" #include "../../../C/XzEnc.h" #include "../../Common/ComTry.h" #include "../../Common/Defs.h" #include "../../Common/IntToString.h" #include "../ICoder.h" #include "../Common/CWrappers.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "../Compress/CopyCoder.h" #include "IArchive.h" #include "Common/HandlerOut.h" using namespace NWindows; namespace NCompress { namespace NLzma2 { HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props); }} static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } static void SzFree(void *, void *address) { MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; namespace NArchive { namespace NXz { struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit; static const wchar_t *k_LZMA2_Name = L"LZMA2"; struct CStatInfo { UInt64 InSize; UInt64 OutSize; UInt64 PhySize; UInt64 NumStreams; UInt64 NumBlocks; bool UnpackSize_Defined; bool NumStreams_Defined; bool NumBlocks_Defined; bool IsArc; bool UnexpectedEnd; bool DataAfterEnd; bool Unsupported; bool HeadersError; bool DataError; bool CrcError; CStatInfo() { Clear(); } void Clear() { InSize = 0; OutSize = 0; PhySize = 0; NumStreams = 0; NumBlocks = 0; UnpackSize_Defined = false; NumStreams_Defined = false; NumBlocks_Defined = false; UnexpectedEnd = false; DataAfterEnd = false; Unsupported = false; HeadersError = false; DataError = false; CrcError = false; IsArc = false; } }; struct IDecodeState: public CStatInfo { SRes DecodeRes; IDecodeState(): DecodeRes(SZ_OK) {} virtual HRESULT Progress() = 0; HRESULT Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream); }; struct CVirtProgress_To_LocalProgress: public IDecodeState { CLocalProgress *lps; CMyComPtr progress; HRESULT Progress(); }; HRESULT CVirtProgress_To_LocalProgress::Progress() { lps->InSize = InSize; lps->OutSize = OutSize; return lps->SetCur(); } class CHandler: public IInArchive, public IArchiveOpenSeq, #ifndef EXTRACT_ONLY public IOutArchive, public ISetProperties, public CMultiMethodProps, #endif public CMyUnknownImp { CStatInfo _stat; bool _isArc; bool _needSeekToStart; bool _phySize_Defined; CMyComPtr _stream; CMyComPtr _seqStream; UInt32 _filterId; AString _methodsString; void Init() { _filterId = 0; CMultiMethodProps::Init(); } HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback); HRESULT Decode2(ISequentialInStream *seqInStream, ISequentialOutStream *outStream, IDecodeState &progress) { RINOK(progress.Decode(seqInStream, outStream)); _stat = progress; _phySize_Defined = true; return S_OK; } public: MY_QUERYINTERFACE_BEGIN2(IInArchive) MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) #ifndef EXTRACT_ONLY MY_QUERYINTERFACE_ENTRY(IOutArchive) MY_QUERYINTERFACE_ENTRY(ISetProperties) #endif MY_QUERYINTERFACE_END MY_ADDREF_RELEASE INTERFACE_IInArchive(;) STDMETHOD(OpenSeq)(ISequentialInStream *stream); #ifndef EXTRACT_ONLY INTERFACE_IOutArchive(;) STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps); #endif CHandler(); }; CHandler::CHandler() { Init(); } static const Byte kProps[] = { kpidSize, kpidPackSize, kpidMethod }; static const Byte kArcProps[] = { kpidMethod, kpidNumStreams, kpidNumBlocks }; IMP_IInArchive_Props IMP_IInArchive_ArcProps static inline char GetHex(unsigned value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } static inline void AddHexToString(AString &s, Byte value) { s += GetHex(value >> 4); s += GetHex(value & 0xF); } static void AddUInt32ToString(AString &s, UInt32 value) { char temp[16]; ConvertUInt32ToString(value, temp); s += temp; } static void Lzma2PropToString(AString &s, unsigned prop) { char c = 0; UInt32 size; if ((prop & 1) == 0) size = prop / 2 + 12; else { c = 'k'; size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1); if (prop > 17) { size >>= 10; c = 'm'; } } AddUInt32ToString(s, size); if (c != 0) s += c; } struct CMethodNamePair { UInt32 Id; const char *Name; }; static const CMethodNamePair g_NamePairs[] = { { XZ_ID_Subblock, "SB" }, { XZ_ID_Delta, "Delta" }, { XZ_ID_X86, "BCJ" }, { XZ_ID_PPC, "PPC" }, { XZ_ID_IA64, "IA64" }, { XZ_ID_ARM, "ARM" }, { XZ_ID_ARMT, "ARMT" }, { XZ_ID_SPARC, "SPARC" }, { XZ_ID_LZMA2, "LZMA2" } }; static AString GetMethodString(const CXzFilter &f) { const char *p = NULL; for (unsigned i = 0; i < ARRAY_SIZE(g_NamePairs); i++) if (g_NamePairs[i].Id == f.id) { p = g_NamePairs[i].Name; break; } char temp[32]; if (!p) { ::ConvertUInt64ToString(f.id, temp); p = temp; } AString s = p; if (f.propsSize > 0) { s += ':'; if (f.id == XZ_ID_LZMA2 && f.propsSize == 1) Lzma2PropToString(s, f.props[0]); else if (f.id == XZ_ID_Delta && f.propsSize == 1) AddUInt32ToString(s, (UInt32)f.props[0] + 1); else { s += '['; for (UInt32 bi = 0; bi < f.propsSize; bi++) AddHexToString(s, f.props[bi]); s += ']'; } } return s; } static void AddString(AString &dest, const AString &src) { if (!dest.IsEmpty()) dest += ' '; dest += src; } static const char *kChecks[] = { "NoCheck" , "CRC32" , NULL , NULL , "CRC64" , NULL , NULL , NULL , NULL , NULL , "SHA256" , NULL , NULL , NULL , NULL , NULL }; static AString GetCheckString(const CXzs &xzs) { size_t i; UInt32 mask = 0; for (i = 0; i < xzs.num; i++) mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags)); AString s; for (i = 0; i <= XZ_CHECK_MASK; i++) if (((mask >> i) & 1) != 0) { AString s2; if (kChecks[i]) s2 = kChecks[i]; else { s2 = "Check-"; AddUInt32ToString(s2, (UInt32)i); } AddString(s, s2); } return s; } STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; switch (propID) { case kpidPhySize: if (_phySize_Defined) prop = _stat.PhySize; break; case kpidNumStreams: if (_stat.NumStreams_Defined) prop = _stat.NumStreams; break; case kpidNumBlocks: if (_stat.NumBlocks_Defined) prop = _stat.NumBlocks; break; case kpidUnpackSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; case kpidErrorFlags: { UInt32 v = 0; if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; if (_stat.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd; if (_stat.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; if (_stat.HeadersError) v |= kpv_ErrorFlags_HeadersError; if (_stat.Unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; if (_stat.DataError) v |= kpv_ErrorFlags_DataError; if (_stat.CrcError) v |= kpv_ErrorFlags_CrcError; prop = v; } } prop.Detach(value); return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = 1; return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; switch (propID) { case kpidSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; case kpidPackSize: if (_phySize_Defined) prop = _stat.PhySize; break; case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; } prop.Detach(value); return S_OK; COM_TRY_END } struct COpenCallbackWrap { ICompressProgress p; IArchiveOpenCallback *OpenCallback; HRESULT Res; COpenCallbackWrap(IArchiveOpenCallback *progress); }; static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */) { COpenCallbackWrap *p = (COpenCallbackWrap *)pp; p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); return (SRes)p->Res; } COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback) { p.Progress = OpenCallbackProgress; OpenCallback = callback; Res = SZ_OK; } struct CXzsCPP { CXzs p; CXzsCPP() { Xzs_Construct(&p); } ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } }; struct CVirtProgress_To_OpenProgress: public IDecodeState { IArchiveOpenCallback *Callback; UInt64 Offset; HRESULT Progress(); }; HRESULT CVirtProgress_To_OpenProgress::Progress() { if (Callback) { UInt64 files = 0; UInt64 value = Offset + InSize; return Callback->SetCompleted(&files, &value); } return S_OK; } static HRESULT SRes_to_Open_HRESULT(SRes res) { switch (res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PROGRESS: return E_ABORT; /* case SZ_ERROR_UNSUPPORTED: case SZ_ERROR_CRC: case SZ_ERROR_DATA: case SZ_ERROR_ARCHIVE: case SZ_ERROR_NO_ARCHIVE: return S_FALSE; */ } return S_FALSE; } HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback) { _needSeekToStart = true; { CXzStreamFlags st; CSeqInStreamWrap inStreamWrap(inStream); SRes res = Xz_ReadHeader(&st, &inStreamWrap.p); if (res != SZ_OK) return SRes_to_Open_HRESULT(res); { CXzBlock block; Bool isIndex; UInt32 headerSizeRes; SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes); if (res2 == SZ_OK && !isIndex) { unsigned numFilters = XzBlock_GetNumFilters(&block); for (unsigned i = 0; i < numFilters; i++) AddString(_methodsString, GetMethodString(block.filters[i])); } } } RINOK(inStream->Seek(0, STREAM_SEEK_END, &_stat.PhySize)); RINOK(callback->SetTotal(NULL, &_stat.PhySize)); CSeekInStreamWrap inStreamImp(inStream); CLookToRead lookStream; LookToRead_CreateVTable(&lookStream, True); lookStream.realStream = &inStreamImp.p; LookToRead_Init(&lookStream); COpenCallbackWrap openWrap(callback); CXzsCPP xzs; Int64 startPosition; SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &startPosition, &openWrap.p, &g_Alloc); if (res == SZ_ERROR_PROGRESS) return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res; /* if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0) res = SZ_OK; */ if (res == SZ_OK && startPosition == 0) { _phySize_Defined = true; _stat.OutSize = Xzs_GetUnpackSize(&xzs.p); _stat.UnpackSize_Defined = true; _stat.NumStreams = xzs.p.num; _stat.NumStreams_Defined = true; _stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p); _stat.NumBlocks_Defined = true; AddString(_methodsString, GetCheckString(xzs.p)); } else { res = SZ_OK; } RINOK(SRes_to_Open_HRESULT(res)); _stream = inStream; _seqStream = inStream; _isArc = true; return S_OK; } STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) { COM_TRY_BEGIN { Close(); return Open2(inStream, /* 0, */ callback); } COM_TRY_END } STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) { Close(); _seqStream = stream; _isArc = true; _needSeekToStart = false; return S_OK; } STDMETHODIMP CHandler::Close() { _stat.Clear(); _isArc = false; _needSeekToStart = false; _phySize_Defined = false; _methodsString.Empty(); _stream.Release(); _seqStream.Release(); return S_OK; } class CSeekToSeqStream: public IInStream, public CMyUnknownImp { public: CMyComPtr Stream; MY_UNKNOWN_IMP1(IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize) { return Stream->Read(data, size, processedSize); } STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; } struct CXzUnpackerCPP { Byte *InBuf; Byte *OutBuf; CXzUnpacker p; CXzUnpackerCPP(): InBuf(0), OutBuf(0) { XzUnpacker_Construct(&p, &g_Alloc); } ~CXzUnpackerCPP() { XzUnpacker_Free(&p); MyFree(InBuf); MyFree(OutBuf); } }; HRESULT IDecodeState::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream) { const size_t kInBufSize = 1 << 15; const size_t kOutBufSize = 1 << 21; DecodeRes = SZ_OK; CXzUnpackerCPP xzu; XzUnpacker_Init(&xzu.p); xzu.InBuf = (Byte *)MyAlloc(kInBufSize); xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize); if (!xzu.InBuf || !xzu.OutBuf) return E_OUTOFMEMORY; UInt32 inSize = 0; SizeT inPos = 0; SizeT outPos = 0; for (;;) { if (inPos == inSize) { inPos = inSize = 0; RINOK(seqInStream->Read(xzu.InBuf, kInBufSize, &inSize)); } SizeT inLen = inSize - inPos; SizeT outLen = kOutBufSize - outPos; ECoderStatus status; SRes res = XzUnpacker_Code(&xzu.p, xzu.OutBuf + outPos, &outLen, xzu.InBuf + inPos, &inLen, (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status); inPos += inLen; outPos += outLen; InSize += inLen; OutSize += outLen; DecodeRes = res; bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK); if (outStream) { if (outPos == kOutBufSize || finished) { if (outPos != 0) { RINOK(WriteStream(outStream, xzu.OutBuf, outPos)); outPos = 0; } } } else outPos = 0; RINOK(Progress()); if (finished) { PhySize = InSize; NumStreams = xzu.p.numStartedStreams; if (NumStreams > 0) IsArc = true; NumBlocks = xzu.p.numTotalBlocks; UnpackSize_Defined = true; NumStreams_Defined = true; NumBlocks_Defined = true; UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p); if (res == SZ_OK) { if (status == CODER_STATUS_NEEDS_MORE_INPUT) { extraSize = 0; if (!XzUnpacker_IsStreamWasFinished(&xzu.p)) { // finished at padding bytes, but padding is not aligned for 4 UnexpectedEnd = true; res = SZ_ERROR_DATA; } } else // status == CODER_STATUS_NOT_FINISHED res = SZ_ERROR_DATA; } else if (res == SZ_ERROR_NO_ARCHIVE) { if (InSize == extraSize) IsArc = false; else { if (extraSize != 0 || inPos != inSize) { DataAfterEnd = true; res = SZ_OK; } } } DecodeRes = res; PhySize -= extraSize; switch (res) { case SZ_OK: break; case SZ_ERROR_NO_ARCHIVE: IsArc = false; break; case SZ_ERROR_ARCHIVE: HeadersError = true; break; case SZ_ERROR_UNSUPPORTED: Unsupported = true; break; case SZ_ERROR_CRC: CrcError = true; break; case SZ_ERROR_DATA: DataError = true; break; default: DataError = true; break; } break; } } return S_OK; } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN if (numItems == 0) return S_OK; if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; extractCallback->SetTotal(_stat.PhySize); UInt64 currentTotalPacked = 0; RINOK(extractCallback->SetCompleted(¤tTotalPacked)); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); if (!testMode && !realOutStream) return S_OK; extractCallback->PrepareOperation(askMode); CVirtProgress_To_LocalProgress vp; vp.lps = new CLocalProgress; vp.progress = vp.lps; vp.lps->Init(extractCallback, true); if (_needSeekToStart) { if (!_stream) return E_FAIL; RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); } else _needSeekToStart = true; RINOK(Decode2(_seqStream, realOutStream, vp)); Int32 opRes; if (!vp.IsArc) opRes = NExtract::NOperationResult::kIsNotArc; else if (vp.UnexpectedEnd) opRes = NExtract::NOperationResult::kUnexpectedEnd; else if (vp.DataAfterEnd) opRes = NExtract::NOperationResult::kDataAfterEnd; else if (vp.CrcError) opRes = NExtract::NOperationResult::kCRCError; else if (vp.Unsupported) opRes = NExtract::NOperationResult::kUnsupportedMethod; else if (vp.HeadersError) opRes = NExtract::NOperationResult::kDataError; else if (vp.DataError) opRes = NExtract::NOperationResult::kDataError; else if (vp.DecodeRes != SZ_OK) opRes = NExtract::NOperationResult::kDataError; else opRes = NExtract::NOperationResult::kOK; realOutStream.Release(); return extractCallback->SetOperationResult(opRes); COM_TRY_END } #ifndef EXTRACT_ONLY STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) { *timeType = NFileTimeType::kUnix; return S_OK; } STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) { CSeqOutStreamWrap seqOutStream(outStream); if (numItems == 0) { SRes res = Xz_EncodeEmpty(&seqOutStream.p); return SResToHRESULT(res); } if (numItems != 1) return E_INVALIDARG; Int32 newData, newProps; UInt32 indexInArchive; if (!updateCallback) return E_FAIL; RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); if (IntToBool(newProps)) { { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); if (prop.vt != VT_EMPTY) if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) return E_INVALIDARG; } } if (IntToBool(newData)) { UInt64 size; { NCOM::CPropVariant prop; RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; RINOK(updateCallback->SetTotal(size)); } CLzma2EncProps lzma2Props; Lzma2EncProps_Init(&lzma2Props); lzma2Props.lzmaProps.level = GetLevel(); CMyComPtr fileInStream; RINOK(updateCallback->GetStream(0, &fileInStream)); CSeqInStreamWrap seqInStream(fileInStream); { NCOM::CPropVariant prop = (UInt64)size; RINOK(NCompress::NLzma2::SetLzma2Prop(NCoderPropID::kReduceSize, prop, lzma2Props)); } FOR_VECTOR (i, _methods) { COneMethodInfo &m = _methods[i]; SetGlobalLevelAndThreads(m #ifndef _7ZIP_ST , _numThreads #endif ); { FOR_VECTOR (j, m.Props) { const CProp &prop = m.Props[j]; RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props)); } } } #ifndef _7ZIP_ST lzma2Props.numTotalThreads = _numThreads; #endif CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(updateCallback, true); CCompressProgressWrap progressWrap(progress); CXzProps xzProps; CXzFilterProps filter; XzProps_Init(&xzProps); XzFilterProps_Init(&filter); xzProps.lzma2Props = &lzma2Props; xzProps.filterProps = (_filterId != 0 ? &filter : NULL); switch (_crcSize) { case 0: xzProps.checkId = XZ_CHECK_NO; break; case 4: xzProps.checkId = XZ_CHECK_CRC32; break; case 8: xzProps.checkId = XZ_CHECK_CRC64; break; case 32: xzProps.checkId = XZ_CHECK_SHA256; break; default: return E_INVALIDARG; } filter.id = _filterId; if (_filterId == XZ_ID_Delta) { bool deltaDefined = false; FOR_VECTOR (j, _filterMethod.Props) { const CProp &prop = _filterMethod.Props[j]; if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4) { UInt32 delta = (UInt32)prop.Value.ulVal; if (delta < 1 || delta > 256) return E_INVALIDARG; filter.delta = delta; deltaDefined = true; } } if (!deltaDefined) return E_INVALIDARG; } SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &xzProps, &progressWrap.p); if (res == SZ_OK) return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); return SResToHRESULT(res); } if (indexInArchive != 0) return E_INVALIDARG; if (_stream) RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); return NCompress::CopyStream(_stream, outStream, NULL); } STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) { COM_TRY_BEGIN Init(); for (UInt32 i = 0; i < numProps; i++) { RINOK(SetProperty(names[i], values[i])); } if (!_filterMethod.MethodName.IsEmpty()) { unsigned k; for (k = 0; k < ARRAY_SIZE(g_NamePairs); k++) { const CMethodNamePair &pair = g_NamePairs[k]; if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name)) { _filterId = pair.Id; break; } } if (k == ARRAY_SIZE(g_NamePairs)) return E_INVALIDARG; } _methods.DeleteFrontal(GetNumEmptyMethods()); if (_methods.Size() > 1) return E_INVALIDARG; if (_methods.Size() == 1) { UString &methodName = _methods[0].MethodName; if (methodName.IsEmpty()) methodName = k_LZMA2_Name; else if (!methodName.IsEqualToNoCase(k_LZMA2_Name)) return E_INVALIDARG; } return S_OK; COM_TRY_END } #endif IMP_CreateArcIn IMP_CreateArcOut static CArcInfo g_ArcInfo = { "xz", "xz txz", "* .tar", 0xC, 6, { 0xFD, '7' , 'z', 'X', 'Z', 0 }, 0, NArcInfoFlags::kKeepName, REF_CreateArc_Pair }; REGISTER_ARC(xz) }} src/libs/7zip/win/CPP/7zip/Common/000077500000000000000000000000001325366651500170435ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/Common/CWrappers.cpp000066400000000000000000000123041325366651500214550ustar00rootroot00000000000000// CWrappers.h #include "StdAfx.h" #include "../../../C/Alloc.h" #include "CWrappers.h" #include "StreamUtils.h" #define PROGRESS_UNKNOWN_VALUE ((UInt64)(Int64)-1) #define CONVERT_PR_VAL(x) (x == PROGRESS_UNKNOWN_VALUE ? NULL : &x) static SRes CompressProgress(void *pp, UInt64 inSize, UInt64 outSize) throw() { CCompressProgressWrap *p = (CCompressProgressWrap *)pp; p->Res = p->Progress->SetRatioInfo(CONVERT_PR_VAL(inSize), CONVERT_PR_VAL(outSize)); return (SRes)p->Res; } CCompressProgressWrap::CCompressProgressWrap(ICompressProgressInfo *progress) throw() { p.Progress = CompressProgress; Progress = progress; Res = SZ_OK; } static const UInt32 kStreamStepSize = (UInt32)1 << 31; SRes HRESULT_To_SRes(HRESULT res, SRes defaultRes) { switch (res) { case S_OK: return SZ_OK; case E_OUTOFMEMORY: return SZ_ERROR_MEM; case E_INVALIDARG: return SZ_ERROR_PARAM; case E_ABORT: return SZ_ERROR_PROGRESS; case S_FALSE: return SZ_ERROR_DATA; case E_NOTIMPL: return SZ_ERROR_UNSUPPORTED; } return defaultRes; } static SRes MyRead(void *object, void *data, size_t *size) throw() { CSeqInStreamWrap *p = (CSeqInStreamWrap *)object; UInt32 curSize = ((*size < kStreamStepSize) ? (UInt32)*size : kStreamStepSize); p->Res = (p->Stream->Read(data, curSize, &curSize)); *size = curSize; p->Processed += curSize; if (p->Res == S_OK) return SZ_OK; return HRESULT_To_SRes(p->Res, SZ_ERROR_READ); } static size_t MyWrite(void *object, const void *data, size_t size) throw() { CSeqOutStreamWrap *p = (CSeqOutStreamWrap *)object; if (p->Stream) { p->Res = WriteStream(p->Stream, data, size); if (p->Res != 0) return 0; } else p->Res = S_OK; p->Processed += size; return size; } CSeqInStreamWrap::CSeqInStreamWrap(ISequentialInStream *stream) throw() { p.Read = MyRead; Stream = stream; Processed = 0; } CSeqOutStreamWrap::CSeqOutStreamWrap(ISequentialOutStream *stream) throw() { p.Write = MyWrite; Stream = stream; Res = SZ_OK; Processed = 0; } HRESULT SResToHRESULT(SRes res) throw() { switch(res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PARAM: return E_INVALIDARG; case SZ_ERROR_PROGRESS: return E_ABORT; case SZ_ERROR_DATA: return S_FALSE; case SZ_ERROR_UNSUPPORTED: return E_NOTIMPL; } return E_FAIL; } static SRes InStreamWrap_Read(void *pp, void *data, size_t *size) throw() { CSeekInStreamWrap *p = (CSeekInStreamWrap *)pp; UInt32 curSize = ((*size < kStreamStepSize) ? (UInt32)*size : kStreamStepSize); p->Res = p->Stream->Read(data, curSize, &curSize); *size = curSize; return (p->Res == S_OK) ? SZ_OK : SZ_ERROR_READ; } static SRes InStreamWrap_Seek(void *pp, Int64 *offset, ESzSeek origin) throw() { CSeekInStreamWrap *p = (CSeekInStreamWrap *)pp; UInt32 moveMethod; switch(origin) { case SZ_SEEK_SET: moveMethod = STREAM_SEEK_SET; break; case SZ_SEEK_CUR: moveMethod = STREAM_SEEK_CUR; break; case SZ_SEEK_END: moveMethod = STREAM_SEEK_END; break; default: return SZ_ERROR_PARAM; } UInt64 newPosition; p->Res = p->Stream->Seek(*offset, moveMethod, &newPosition); *offset = (Int64)newPosition; return (p->Res == S_OK) ? SZ_OK : SZ_ERROR_READ; } CSeekInStreamWrap::CSeekInStreamWrap(IInStream *stream) throw() { Stream = stream; p.Read = InStreamWrap_Read; p.Seek = InStreamWrap_Seek; Res = S_OK; } /* ---------- CByteInBufWrap ---------- */ void CByteInBufWrap::Free() throw() { ::MidFree(Buf); Buf = 0; } bool CByteInBufWrap::Alloc(UInt32 size) throw() { if (Buf == 0 || size != Size) { Free(); Lim = Cur = Buf = (Byte *)::MidAlloc((size_t)size); Size = size; } return (Buf != 0); } Byte CByteInBufWrap::ReadByteFromNewBlock() throw() { if (Res == S_OK) { UInt32 avail; Processed += (Cur - Buf); Res = Stream->Read(Buf, Size, &avail); Cur = Buf; Lim = Buf + avail; if (avail != 0) return *Cur++; } Extra = true; return 0; } static Byte Wrap_ReadByte(void *pp) throw() { CByteInBufWrap *p = (CByteInBufWrap *)pp; if (p->Cur != p->Lim) return *p->Cur++; return p->ReadByteFromNewBlock(); } CByteInBufWrap::CByteInBufWrap(): Buf(0) { p.Read = Wrap_ReadByte; } /* ---------- CByteOutBufWrap ---------- */ void CByteOutBufWrap::Free() throw() { ::MidFree(Buf); Buf = 0; } bool CByteOutBufWrap::Alloc(size_t size) throw() { if (Buf == 0 || size != Size) { Free(); Buf = (Byte *)::MidAlloc(size); Size = size; } return (Buf != 0); } HRESULT CByteOutBufWrap::Flush() throw() { if (Res == S_OK) { size_t size = (Cur - Buf); Res = WriteStream(Stream, Buf, size); if (Res == S_OK) Processed += size; Cur = Buf; } return Res; } static void Wrap_WriteByte(void *pp, Byte b) throw() { CByteOutBufWrap *p = (CByteOutBufWrap *)pp; Byte *dest = p->Cur; *dest = b; p->Cur = ++dest; if (dest == p->Lim) p->Flush(); } CByteOutBufWrap::CByteOutBufWrap() throw(): Buf(0) { p.Write = Wrap_WriteByte; } src/libs/7zip/win/CPP/7zip/Common/CWrappers.h000066400000000000000000000040711325366651500211240ustar00rootroot00000000000000// CWrappers.h #ifndef __C_WRAPPERS_H #define __C_WRAPPERS_H #include "../ICoder.h" #include "../../Common/MyCom.h" struct CCompressProgressWrap { ICompressProgress p; ICompressProgressInfo *Progress; HRESULT Res; CCompressProgressWrap(ICompressProgressInfo *progress) throw(); }; struct CSeqInStreamWrap { ISeqInStream p; ISequentialInStream *Stream; HRESULT Res; UInt64 Processed; CSeqInStreamWrap(ISequentialInStream *stream) throw(); }; struct CSeekInStreamWrap { ISeekInStream p; IInStream *Stream; HRESULT Res; CSeekInStreamWrap(IInStream *stream) throw(); }; struct CSeqOutStreamWrap { ISeqOutStream p; ISequentialOutStream *Stream; HRESULT Res; UInt64 Processed; CSeqOutStreamWrap(ISequentialOutStream *stream) throw(); }; HRESULT SResToHRESULT(SRes res) throw(); struct CByteInBufWrap { IByteIn p; const Byte *Cur; const Byte *Lim; Byte *Buf; UInt32 Size; ISequentialInStream *Stream; UInt64 Processed; bool Extra; HRESULT Res; CByteInBufWrap(); ~CByteInBufWrap() { Free(); } void Free() throw(); bool Alloc(UInt32 size) throw(); void Init() { Lim = Cur = Buf; Processed = 0; Extra = false; Res = S_OK; } UInt64 GetProcessed() const { return Processed + (Cur - Buf); } Byte ReadByteFromNewBlock() throw(); Byte ReadByte() { if (Cur != Lim) return *Cur++; return ReadByteFromNewBlock(); } }; struct CByteOutBufWrap { IByteOut p; Byte *Cur; const Byte *Lim; Byte *Buf; size_t Size; ISequentialOutStream *Stream; UInt64 Processed; HRESULT Res; CByteOutBufWrap() throw(); ~CByteOutBufWrap() { Free(); } void Free() throw(); bool Alloc(size_t size) throw(); void Init() { Cur = Buf; Lim = Buf + Size; Processed = 0; Res = S_OK; } UInt64 GetProcessed() const { return Processed + (Cur - Buf); } HRESULT Flush() throw(); void WriteByte(Byte b) { *Cur++ = b; if (Cur == Lim) Flush(); } }; #endif src/libs/7zip/win/CPP/7zip/Common/Common.pri000066400000000000000000000036621325366651500210160ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Common/CWrappers.h \ $$7ZIP_BASE/CPP/7zip/Common/CreateCoder.h \ $$7ZIP_BASE/CPP/7zip/Common/FilePathAutoRename.h \ $$7ZIP_BASE/CPP/7zip/Common/FileStreams.h \ $$7ZIP_BASE/CPP/7zip/Common/FilterCoder.h \ $$7ZIP_BASE/CPP/7zip/Common/InBuffer.h \ $$7ZIP_BASE/CPP/7zip/Common/InOutTempBuffer.h \ $$7ZIP_BASE/CPP/7zip/Common/LimitedStreams.h \ $$7ZIP_BASE/CPP/7zip/Common/LockedStream.h \ $$7ZIP_BASE/CPP/7zip/Common/MethodId.h \ $$7ZIP_BASE/CPP/7zip/Common/MethodProps.h \ $$7ZIP_BASE/CPP/7zip/Common/OutBuffer.h \ $$7ZIP_BASE/CPP/7zip/Common/ProgressUtils.h \ $$7ZIP_BASE/CPP/7zip/Common/RegisterArc.h \ $$7ZIP_BASE/CPP/7zip/Common/RegisterCodec.h \ $$7ZIP_BASE/CPP/7zip/Common/StdAfx.h \ $$7ZIP_BASE/CPP/7zip/Common/StreamBinder.h \ $$7ZIP_BASE/CPP/7zip/Common/StreamObjects.h \ $$7ZIP_BASE/CPP/7zip/Common/StreamUtils.h \ $$7ZIP_BASE/CPP/7zip/Common/UniqBlocks.h \ $$7ZIP_BASE/CPP/7zip/Common/VirtThread.h SOURCES += $$7ZIP_BASE/CPP/7zip/Common/CWrappers.cpp \ $$7ZIP_BASE/CPP/7zip/Common/CreateCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Common/FilePathAutoRename.cpp \ $$7ZIP_BASE/CPP/7zip/Common/FileStreams.cpp \ $$7ZIP_BASE/CPP/7zip/Common/FilterCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Common/InBuffer.cpp \ $$7ZIP_BASE/CPP/7zip/Common/InOutTempBuffer.cpp \ $$7ZIP_BASE/CPP/7zip/Common/LimitedStreams.cpp \ $$7ZIP_BASE/CPP/7zip/Common/LockedStream.cpp \ $$7ZIP_BASE/CPP/7zip/Common/MethodProps.cpp \ $$7ZIP_BASE/CPP/7zip/Common/OutBuffer.cpp \ $$7ZIP_BASE/CPP/7zip/Common/ProgressUtils.cpp \ $$7ZIP_BASE/CPP/7zip/Common/PropId.cpp \ $$7ZIP_BASE/CPP/7zip/Common/StreamBinder.cpp \ $$7ZIP_BASE/CPP/7zip/Common/StreamObjects.cpp \ $$7ZIP_BASE/CPP/7zip/Common/StreamUtils.cpp \ $$7ZIP_BASE/CPP/7zip/Common/UniqBlocks.cpp \ $$7ZIP_BASE/CPP/7zip/Common/VirtThread.cpp src/libs/7zip/win/CPP/7zip/Common/CreateCoder.cpp000066400000000000000000000246761325366651500217460ustar00rootroot00000000000000// CreateCoder.cpp #include "StdAfx.h" #include "../../Windows/Defs.h" #include "../../Windows/PropVariant.h" #include "CreateCoder.h" #include "FilterCoder.h" #include "RegisterCodec.h" static const unsigned int kNumCodecsMax = 64; unsigned int g_NumCodecs = 0; const CCodecInfo *g_Codecs[kNumCodecsMax]; void RegisterCodec(const CCodecInfo *codecInfo) throw() { if (g_NumCodecs < kNumCodecsMax) g_Codecs[g_NumCodecs++] = codecInfo; } static const unsigned int kNumHashersMax = 16; unsigned int g_NumHashers = 0; const CHasherInfo *g_Hashers[kNumHashersMax]; void RegisterHasher(const CHasherInfo *hashInfo) throw() { if (g_NumHashers < kNumHashersMax) g_Hashers[g_NumHashers++] = hashInfo; } #ifdef EXTERNAL_CODECS static HRESULT ReadNumberOfStreams(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, UInt32 &res) { NWindows::NCOM::CPropVariant prop; RINOK(codecsInfo->GetProperty(index, propID, &prop)); if (prop.vt == VT_EMPTY) res = 1; else if (prop.vt == VT_UI4) res = prop.ulVal; else return E_INVALIDARG; return S_OK; } static HRESULT ReadIsAssignedProp(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, bool &res) { NWindows::NCOM::CPropVariant prop; RINOK(codecsInfo->GetProperty(index, propID, &prop)); if (prop.vt == VT_EMPTY) res = true; else if (prop.vt == VT_BOOL) res = VARIANT_BOOLToBool(prop.boolVal); else return E_INVALIDARG; return S_OK; } HRESULT CExternalCodecs::LoadCodecs() { if (GetCodecs) { UInt32 num; RINOK(GetCodecs->GetNumberOfMethods(&num)); for (UInt32 i = 0; i < num; i++) { CCodecInfoEx info; NWindows::NCOM::CPropVariant prop; RINOK(GetCodecs->GetProperty(i, NMethodPropID::kID, &prop)); // if (prop.vt != VT_BSTR) // info.Id.IDSize = (Byte)SysStringByteLen(prop.bstrVal); // memcpy(info.Id.ID, prop.bstrVal, info.Id.IDSize); if (prop.vt != VT_UI8) continue; // old Interface info.Id = prop.uhVal.QuadPart; prop.Clear(); RINOK(GetCodecs->GetProperty(i, NMethodPropID::kName, &prop)); if (prop.vt == VT_BSTR) info.Name = prop.bstrVal; else if (prop.vt != VT_EMPTY) return E_INVALIDARG; RINOK(ReadNumberOfStreams(GetCodecs, i, NMethodPropID::kInStreams, info.NumInStreams)); RINOK(ReadNumberOfStreams(GetCodecs, i, NMethodPropID::kOutStreams, info.NumOutStreams)); RINOK(ReadIsAssignedProp(GetCodecs, i, NMethodPropID::kEncoderIsAssigned, info.EncoderIsAssigned)); RINOK(ReadIsAssignedProp(GetCodecs, i, NMethodPropID::kDecoderIsAssigned, info.DecoderIsAssigned)); Codecs.Add(info); } } if (GetHashers) { UInt32 num = GetHashers->GetNumHashers(); for (UInt32 i = 0; i < num; i++) { CHasherInfoEx info; NWindows::NCOM::CPropVariant prop; RINOK(GetHashers->GetHasherProp(i, NMethodPropID::kID, &prop)); if (prop.vt != VT_UI8) continue; info.Id = prop.uhVal.QuadPart; prop.Clear(); RINOK(GetHashers->GetHasherProp(i, NMethodPropID::kName, &prop)); if (prop.vt == VT_BSTR) info.Name = prop.bstrVal; else if (prop.vt != VT_EMPTY) return E_INVALIDARG; Hashers.Add(info); } } return S_OK; } #endif bool FindMethod(DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams) { UInt32 i; for (i = 0; i < g_NumCodecs; i++) { const CCodecInfo &codec = *g_Codecs[i]; if (name.IsEqualToNoCase(codec.Name)) { methodId = codec.Id; numInStreams = codec.NumInStreams; numOutStreams = 1; return true; } } #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Codecs.Size(); i++) { const CCodecInfoEx &codec = __externalCodecs->Codecs[i]; if (codec.Name.IsEqualToNoCase(name)) { methodId = codec.Id; numInStreams = codec.NumInStreams; numOutStreams = codec.NumOutStreams; return true; } } #endif return false; } bool FindMethod(DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name) { UInt32 i; for (i = 0; i < g_NumCodecs; i++) { const CCodecInfo &codec = *g_Codecs[i]; if (methodId == codec.Id) { name = codec.Name; return true; } } #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Codecs.Size(); i++) { const CCodecInfoEx &codec = __externalCodecs->Codecs[i]; if (methodId == codec.Id) { name = codec.Name; return true; } } #endif return false; } bool FindHashMethod(DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId) { UInt32 i; for (i = 0; i < g_NumHashers; i++) { const CHasherInfo &codec = *g_Hashers[i]; if (name.IsEqualToNoCase(codec.Name)) { methodId = codec.Id; return true; } } #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Hashers.Size(); i++) { const CHasherInfoEx &codec = __externalCodecs->Hashers[i]; if (codec.Name.IsEqualToNoCase(name)) { methodId = codec.Id; return true; } } #endif return false; } void GetHashMethods(DECL_EXTERNAL_CODECS_LOC_VARS CRecordVector &methods) { methods.ClearAndSetSize(g_NumHashers); UInt32 i; for (i = 0; i < g_NumHashers; i++) methods[i] = (*g_Hashers[i]).Id; #ifdef EXTERNAL_CODECS if (__externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Hashers.Size(); i++) methods.Add(__externalCodecs->Hashers[i].Id); #endif } HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, CMyComPtr &coder, CMyComPtr &coder2, bool encode, bool onlyCoder) { UInt32 i; for (i = 0; i < g_NumCodecs; i++) { const CCodecInfo &codec = *g_Codecs[i]; if (codec.Id == methodId) { if (encode) { if (codec.CreateEncoder) { void *p = codec.CreateEncoder(); if (codec.IsFilter) filter = (ICompressFilter *)p; else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p; else coder2 = (ICompressCoder2 *)p; break; } } else if (codec.CreateDecoder) { void *p = codec.CreateDecoder(); if (codec.IsFilter) filter = (ICompressFilter *)p; else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p; else coder2 = (ICompressCoder2 *)p; break; } } } #ifdef EXTERNAL_CODECS if (!filter && !coder && !coder2 && __externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Codecs.Size(); i++) { const CCodecInfoEx &codec = __externalCodecs->Codecs[i]; if (codec.Id == methodId) { if (encode) { if (codec.EncoderIsAssigned) { if (codec.IsSimpleCodec()) { HRESULT result = __externalCodecs->GetCodecs->CreateEncoder(i, &IID_ICompressCoder, (void **)&coder); if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE) return result; if (!coder) { RINOK(__externalCodecs->GetCodecs->CreateEncoder(i, &IID_ICompressFilter, (void **)&filter)); } } else { RINOK(__externalCodecs->GetCodecs->CreateEncoder(i, &IID_ICompressCoder2, (void **)&coder2)); } break; } } else if (codec.DecoderIsAssigned) { if (codec.IsSimpleCodec()) { HRESULT result = __externalCodecs->GetCodecs->CreateDecoder(i, &IID_ICompressCoder, (void **)&coder); if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE) return result; if (!coder) { RINOK(__externalCodecs->GetCodecs->CreateDecoder(i, &IID_ICompressFilter, (void **)&filter)); } } else { RINOK(__externalCodecs->GetCodecs->CreateDecoder(i, &IID_ICompressCoder2, (void **)&coder2)); } break; } } } #endif if (onlyCoder && filter) { CFilterCoder *coderSpec = new CFilterCoder; coder = coderSpec; coderSpec->Filter = filter; } return S_OK; } HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, CMyComPtr &coder2, bool encode) { CMyComPtr filter; return CreateCoder( EXTERNAL_CODECS_LOC_VARS methodId, filter, coder, coder2, encode, true); } HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, bool encode) { CMyComPtr filter; CMyComPtr coder2; return CreateCoder( EXTERNAL_CODECS_LOC_VARS methodId, coder, coder2, encode); } HRESULT CreateFilter( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, bool encode) { CMyComPtr coder; CMyComPtr coder2; return CreateCoder( EXTERNAL_CODECS_LOC_VARS methodId, filter, coder, coder2, encode, false); } HRESULT CreateHasher( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name, CMyComPtr &hasher) { UInt32 i; for (i = 0; i < g_NumHashers; i++) { const CHasherInfo &codec = *g_Hashers[i]; if (codec.Id == methodId) { hasher = (IHasher *)codec.CreateHasher(); name = codec.Name; break; } } #ifdef EXTERNAL_CODECS if (!hasher && __externalCodecs) for (i = 0; i < (UInt32)__externalCodecs->Hashers.Size(); i++) { const CHasherInfoEx &codec = __externalCodecs->Hashers[i]; if (codec.Id == methodId) { name = codec.Name; return __externalCodecs->GetHashers->CreateHasher(i, &hasher); } } #endif return S_OK; } src/libs/7zip/win/CPP/7zip/Common/CreateCoder.h000066400000000000000000000067471325366651500214120ustar00rootroot00000000000000// CreateCoder.h #ifndef __CREATE_CODER_H #define __CREATE_CODER_H #include "../../Common/MyCom.h" #include "../../Common/MyString.h" #include "../ICoder.h" #include "MethodId.h" #ifdef EXTERNAL_CODECS struct CCodecInfoEx { UString Name; CMethodId Id; UInt32 NumInStreams; UInt32 NumOutStreams; bool EncoderIsAssigned; bool DecoderIsAssigned; bool IsSimpleCodec() const { return NumOutStreams == 1 && NumInStreams == 1; } CCodecInfoEx(): EncoderIsAssigned(false), DecoderIsAssigned(false) {} }; struct CHasherInfoEx { UString Name; CMethodId Id; }; #define PUBLIC_ISetCompressCodecsInfo public ISetCompressCodecsInfo, #define QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_ENTRY(ISetCompressCodecsInfo) #define DECL_ISetCompressCodecsInfo STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo); #define IMPL_ISetCompressCodecsInfo2(x) \ STDMETHODIMP x::SetCompressCodecsInfo(ICompressCodecsInfo *compressCodecsInfo) { \ COM_TRY_BEGIN __externalCodecs.GetCodecs = compressCodecsInfo; return __externalCodecs.LoadCodecs(); COM_TRY_END } #define IMPL_ISetCompressCodecsInfo IMPL_ISetCompressCodecsInfo2(CHandler) struct CExternalCodecs { CMyComPtr GetCodecs; CMyComPtr GetHashers; CObjectVector Codecs; CObjectVector Hashers; HRESULT LoadCodecs(); }; #define EXTERNAL_CODECS_VARS2 &__externalCodecs #define DECL_EXTERNAL_CODECS_VARS CExternalCodecs __externalCodecs; #define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2, #define DECL_EXTERNAL_CODECS_LOC_VARS2 const CExternalCodecs *__externalCodecs #define EXTERNAL_CODECS_LOC_VARS2 __externalCodecs #define DECL_EXTERNAL_CODECS_LOC_VARS DECL_EXTERNAL_CODECS_LOC_VARS2, #define EXTERNAL_CODECS_LOC_VARS EXTERNAL_CODECS_LOC_VARS2, #else #define PUBLIC_ISetCompressCodecsInfo #define QUERY_ENTRY_ISetCompressCodecsInfo #define DECL_ISetCompressCodecsInfo #define IMPL_ISetCompressCodecsInfo #define EXTERNAL_CODECS_VARS2 #define DECL_EXTERNAL_CODECS_VARS #define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2 #define DECL_EXTERNAL_CODECS_LOC_VARS2 #define EXTERNAL_CODECS_LOC_VARS2 #define DECL_EXTERNAL_CODECS_LOC_VARS #define EXTERNAL_CODECS_LOC_VARS #endif bool FindMethod( DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams); bool FindMethod( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name); bool FindHashMethod( DECL_EXTERNAL_CODECS_LOC_VARS const UString &name, CMethodId &methodId); void GetHashMethods( DECL_EXTERNAL_CODECS_LOC_VARS CRecordVector &methods); HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, CMyComPtr &coder, CMyComPtr &coder2, bool encode, bool onlyCoder); HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, CMyComPtr &coder2, bool encode); HRESULT CreateCoder( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &coder, bool encode); HRESULT CreateFilter( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, CMyComPtr &filter, bool encode); HRESULT CreateHasher( DECL_EXTERNAL_CODECS_LOC_VARS CMethodId methodId, UString &name, CMyComPtr &hacher); #endif src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.cpp000066400000000000000000000025161325366651500232300ustar00rootroot00000000000000// FilePathAutoRename.cpp #include "StdAfx.h" #include "../../Common/Defs.h" #include "../../Common/IntToString.h" #include "../../Windows/FileFind.h" #include "FilePathAutoRename.h" using namespace NWindows; static bool MakeAutoName(const FString &name, const FString &extension, unsigned value, FString &path) { FChar number[16]; ConvertUInt32ToString(value, number); path = name; path += number; path += extension; return NFile::NFind::DoesFileOrDirExist(path); } bool AutoRenamePath(FString &fullProcessedPath) { FString path; int dotPos = fullProcessedPath.ReverseFind(FTEXT('.')); int slashPos = fullProcessedPath.ReverseFind(FTEXT('/')); #ifdef _WIN32 int slash1Pos = fullProcessedPath.ReverseFind(FTEXT('\\')); slashPos = MyMax(slashPos, slash1Pos); #endif FString name, extension; if (dotPos > slashPos && dotPos > 0) { name.SetFrom(fullProcessedPath, dotPos); extension = fullProcessedPath.Ptr(dotPos); } else name = fullProcessedPath; name += L'_'; unsigned left = 1, right = (1 << 30); while (left != right) { unsigned mid = (left + right) / 2; if (MakeAutoName(name, extension, mid, path)) left = mid + 1; else right = mid; } return !MakeAutoName(name, extension, right, fullProcessedPath); } src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.h000066400000000000000000000003051325366651500226670ustar00rootroot00000000000000// FilePathAutoRename.h #ifndef __FILE_PATH_AUTO_RENAME_H #define __FILE_PATH_AUTO_RENAME_H #include "../../Common/MyString.h" bool AutoRenamePath(FString &fullProcessedPath); #endif src/libs/7zip/win/CPP/7zip/Common/FileStreams.cpp000066400000000000000000000243551325366651500217760ustar00rootroot00000000000000// FileStreams.cpp #include "StdAfx.h" #ifndef _WIN32 #include #include #include #endif #ifdef SUPPORT_DEVICE_FILE #include "../../../C/Alloc.h" #include "../../Common/Defs.h" #endif #include "FileStreams.h" static inline HRESULT ConvertBoolToHRESULT(bool result) { #ifdef _WIN32 if (result) return S_OK; DWORD lastError = ::GetLastError(); if (lastError == 0) return E_FAIL; return HRESULT_FROM_WIN32(lastError); #else return result ? S_OK: E_FAIL; #endif } #ifdef SUPPORT_DEVICE_FILE static const UInt32 kClusterSize = 1 << 18; CInFileStream::CInFileStream(): VirtPos(0), PhyPos(0), Buf(0), BufSize(0), SupportHardLinks(false) { } #endif CInFileStream::~CInFileStream() { #ifdef SUPPORT_DEVICE_FILE MidFree(Buf); #endif } STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { #ifdef USE_WIN_FILE #ifdef SUPPORT_DEVICE_FILE if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (File.IsDeviceFile) { if (File.SizeDefined) { if (VirtPos >= File.Size) return VirtPos == File.Size ? S_OK : E_FAIL; UInt64 rem = File.Size - VirtPos; if (size > rem) size = (UInt32)rem; } for (;;) { const UInt32 mask = kClusterSize - 1; const UInt64 mask2 = ~(UInt64)mask; UInt64 alignedPos = VirtPos & mask2; if (BufSize > 0 && BufStartPos == alignedPos) { UInt32 pos = (UInt32)VirtPos & mask; if (pos >= BufSize) return S_OK; UInt32 rem = MyMin(BufSize - pos, size); memcpy(data, Buf + pos, rem); VirtPos += rem; if (processedSize) *processedSize += rem; return S_OK; } bool useBuf = false; if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 ) useBuf = true; else { UInt64 end = VirtPos + size; if ((end & mask) != 0) { end &= mask2; if (end <= VirtPos) useBuf = true; else size = (UInt32)(end - VirtPos); } } if (!useBuf) break; if (alignedPos != PhyPos) { UInt64 realNewPosition; bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition); if (!result) return ConvertBoolToHRESULT(result); PhyPos = realNewPosition; } BufStartPos = alignedPos; UInt32 readSize = kClusterSize; if (File.SizeDefined) readSize = (UInt32)MyMin(File.Size - PhyPos, (UInt64)kClusterSize); if (!Buf) { Buf = (Byte *)MidAlloc(kClusterSize); if (!Buf) return E_OUTOFMEMORY; } bool result = File.Read1(Buf, readSize, BufSize); if (!result) return ConvertBoolToHRESULT(result); if (BufSize == 0) return S_OK; PhyPos += BufSize; } if (VirtPos != PhyPos) { UInt64 realNewPosition; bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition); if (!result) return ConvertBoolToHRESULT(result); PhyPos = VirtPos = realNewPosition; } } #endif UInt32 realProcessedSize; bool result = File.ReadPart(data, size, realProcessedSize); if (processedSize) *processedSize = realProcessedSize; #ifdef SUPPORT_DEVICE_FILE VirtPos += realProcessedSize; PhyPos += realProcessedSize; #endif return ConvertBoolToHRESULT(result); #else if (processedSize) *processedSize = 0; ssize_t res = File.Read(data, (size_t)size); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; #endif } #ifdef UNDER_CE STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { size_t s2 = fread(data, 1, size, stdin); int error = ferror(stdin); if (processedSize) *processedSize = s2; if (s2 <= size && error == 0) return S_OK; return E_FAIL; } #else STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize) { #ifdef _WIN32 DWORD realProcessedSize; UInt32 sizeTemp = (1 << 20); if (sizeTemp > size) sizeTemp = size; BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL); if (processedSize) *processedSize = realProcessedSize; if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE) return S_OK; return ConvertBoolToHRESULT(res != FALSE); #else if (processedSize) *processedSize = 0; ssize_t res; do { res = read(0, data, (size_t)size); } while (res < 0 && (errno == EINTR)); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; #endif } #endif STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; #ifdef USE_WIN_FILE #ifdef SUPPORT_DEVICE_FILE if (File.IsDeviceFile && (File.SizeDefined || seekOrigin != STREAM_SEEK_END)) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += VirtPos; break; case STREAM_SEEK_END: offset += File.Size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; VirtPos = offset; if (newPosition) *newPosition = offset; return S_OK; } #endif UInt64 realNewPosition; bool result = File.Seek(offset, seekOrigin, realNewPosition); #ifdef SUPPORT_DEVICE_FILE PhyPos = VirtPos = realNewPosition; #endif if (newPosition) *newPosition = realNewPosition; return ConvertBoolToHRESULT(result); #else off_t res = File.Seek((off_t)offset, seekOrigin); if (res == -1) return E_FAIL; if (newPosition) *newPosition = (UInt64)res; return S_OK; #endif } STDMETHODIMP CInFileStream::GetSize(UInt64 *size) { return ConvertBoolToHRESULT(File.GetLength(*size)); } #ifdef USE_WIN_FILE STDMETHODIMP CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) { BY_HANDLE_FILE_INFORMATION info; if (File.GetFileInformation(&info)) { if (size) *size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; if (cTime) *cTime = info.ftCreationTime; if (aTime) *aTime = info.ftLastAccessTime; if (mTime) *mTime = info.ftLastWriteTime; if (attrib) *attrib = info.dwFileAttributes; return S_OK; } return GetLastError(); } STDMETHODIMP CInFileStream::GetProps2(CStreamFileProps *props) { BY_HANDLE_FILE_INFORMATION info; if (File.GetFileInformation(&info)) { props->Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; props->VolID = info.dwVolumeSerialNumber; props->FileID_Low = (((UInt64)info.nFileIndexHigh) << 32) + info.nFileIndexLow; props->FileID_High = 0; props->NumLinks = SupportHardLinks ? info.nNumberOfLinks : 1; props->Attrib = info.dwFileAttributes; props->CTime = info.ftCreationTime; props->ATime = info.ftLastAccessTime; props->MTime = info.ftLastWriteTime; return S_OK; } return GetLastError(); } #endif ////////////////////////// // COutFileStream HRESULT COutFileStream::Close() { return ConvertBoolToHRESULT(File.Close()); } STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { #ifdef USE_WIN_FILE UInt32 realProcessedSize; bool result = File.WritePart(data, size, realProcessedSize); ProcessedSize += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return ConvertBoolToHRESULT(result); #else if (processedSize) *processedSize = 0; ssize_t res = File.Write(data, (size_t)size); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; ProcessedSize += res; return S_OK; #endif } STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; #ifdef USE_WIN_FILE UInt64 realNewPosition; bool result = File.Seek(offset, seekOrigin, realNewPosition); if (newPosition) *newPosition = realNewPosition; return ConvertBoolToHRESULT(result); #else off_t res = File.Seek((off_t)offset, seekOrigin); if (res == -1) return E_FAIL; if (newPosition) *newPosition = (UInt64)res; return S_OK; #endif } STDMETHODIMP COutFileStream::SetSize(UInt64 newSize) { #ifdef USE_WIN_FILE UInt64 currentPos; if (!File.Seek(0, FILE_CURRENT, currentPos)) return E_FAIL; bool result = File.SetLength(newSize); UInt64 currentPos2; result = result && File.Seek(currentPos, currentPos2); return result ? S_OK : E_FAIL; #else return E_FAIL; #endif } #ifdef UNDER_CE STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { size_t s2 = fwrite(data, 1, size, stdout); if (processedSize) *processedSize = s2; return (s2 == size) ? S_OK : E_FAIL; } #else STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; #ifdef _WIN32 UInt32 realProcessedSize; BOOL res = TRUE; if (size > 0) { // Seems that Windows doesn't like big amounts writing to stdout. // So we limit portions by 32KB. UInt32 sizeTemp = (1 << 15); if (sizeTemp > size) sizeTemp = size; res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), data, sizeTemp, (DWORD *)&realProcessedSize, NULL); size -= realProcessedSize; data = (const void *)((const Byte *)data + realProcessedSize); if (processedSize) *processedSize += realProcessedSize; } return ConvertBoolToHRESULT(res != FALSE); #else ssize_t res; do { res = write(1, data, (size_t)size); } while (res < 0 && (errno == EINTR)); if (res == -1) return E_FAIL; if (processedSize) *processedSize = (UInt32)res; return S_OK; return S_OK; #endif } #endif src/libs/7zip/win/CPP/7zip/Common/FileStreams.h000066400000000000000000000062161325366651500214370ustar00rootroot00000000000000// FileStreams.h #ifndef __FILE_STREAMS_H #define __FILE_STREAMS_H #ifdef _WIN32 #define USE_WIN_FILE #endif #include "../../Common/MyString.h" #ifdef USE_WIN_FILE #include "../../Windows/FileIO.h" #else #include "../../Common/C_FileIO.h" #endif #include "../../Common/MyCom.h" #include "../IStream.h" class CInFileStream: public IInStream, public IStreamGetSize, #ifdef USE_WIN_FILE public IStreamGetProps, public IStreamGetProps2, #endif public CMyUnknownImp { public: #ifdef USE_WIN_FILE NWindows::NFile::NIO::CInFile File; #ifdef SUPPORT_DEVICE_FILE UInt64 VirtPos; UInt64 PhyPos; UInt64 BufStartPos; Byte *Buf; UInt32 BufSize; #endif #else NC::NFile::NIO::CInFile File; #endif bool SupportHardLinks; virtual ~CInFileStream(); #ifdef SUPPORT_DEVICE_FILE CInFileStream(); #endif bool Open(CFSTR fileName) { return File.Open(fileName); } bool OpenShared(CFSTR fileName, bool shareForWrite) { return File.OpenShared(fileName, shareForWrite); } MY_QUERYINTERFACE_BEGIN2(IInStream) MY_QUERYINTERFACE_ENTRY(IStreamGetSize) #ifdef USE_WIN_FILE MY_QUERYINTERFACE_ENTRY(IStreamGetProps) MY_QUERYINTERFACE_ENTRY(IStreamGetProps2) #endif MY_QUERYINTERFACE_END MY_ADDREF_RELEASE STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(GetSize)(UInt64 *size); #ifdef USE_WIN_FILE STDMETHOD(GetProps)(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib); STDMETHOD(GetProps2)(CStreamFileProps *props); #endif }; class CStdInFileStream: public ISequentialInStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP virtual ~CStdInFileStream() {} STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; class COutFileStream: public IOutStream, public CMyUnknownImp { public: #ifdef USE_WIN_FILE NWindows::NFile::NIO::COutFile File; #else NC::NFile::NIO::COutFile File; #endif virtual ~COutFileStream() {} bool Create(CFSTR fileName, bool createAlways) { ProcessedSize = 0; return File.Create(fileName, createAlways); } bool Open(CFSTR fileName, DWORD creationDisposition) { ProcessedSize = 0; return File.Open(fileName, creationDisposition); } HRESULT Close(); UInt64 ProcessedSize; #ifdef USE_WIN_FILE bool SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) { return File.SetTime(cTime, aTime, mTime); } bool SetMTime(const FILETIME *mTime) { return File.SetMTime(mTime); } #endif MY_UNKNOWN_IMP1(IOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(SetSize)(UInt64 newSize); }; class CStdOutFileStream: public ISequentialOutStream, public CMyUnknownImp { public: MY_UNKNOWN_IMP virtual ~CStdOutFileStream() {} STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; #endif src/libs/7zip/win/CPP/7zip/Common/FilterCoder.cpp000066400000000000000000000151661325366651500217620ustar00rootroot00000000000000// FilterCoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../../Common/Defs.h" #include "FilterCoder.h" #include "StreamUtils.h" static const UInt32 kBufferSize = 1 << 17; CFilterCoder::CFilterCoder() { _buffer = (Byte *)::MidAlloc(kBufferSize); if (_buffer == 0) throw 1; } CFilterCoder::~CFilterCoder() { ::MidFree(_buffer); } HRESULT CFilterCoder::WriteWithLimit(ISequentialOutStream *outStream, UInt32 size) { if (_outSizeIsDefined) { UInt64 remSize = _outSize - _nowPos64; if (size > remSize) size = (UInt32)remSize; } RINOK(WriteStream(outStream, _buffer, size)); _nowPos64 += size; return S_OK; } STDMETHODIMP CFilterCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { RINOK(Init()); UInt32 bufferPos = 0; _outSizeIsDefined = (outSize != 0); if (_outSizeIsDefined) _outSize = *outSize; while (!_outSizeIsDefined || _nowPos64 < _outSize) { size_t processedSize = kBufferSize - bufferPos; // Change it: It can be optimized using ReadPart RINOK(ReadStream(inStream, _buffer + bufferPos, &processedSize)); UInt32 endPos = bufferPos + (UInt32)processedSize; bufferPos = Filter->Filter(_buffer, endPos); if (bufferPos > endPos) { for (; endPos < bufferPos; endPos++) _buffer[endPos] = 0; bufferPos = Filter->Filter(_buffer, endPos); } if (bufferPos == 0) { if (endPos == 0) return S_OK; return WriteWithLimit(outStream, endPos); } RINOK(WriteWithLimit(outStream, bufferPos)); if (progress != NULL) { RINOK(progress->SetRatioInfo(&_nowPos64, &_nowPos64)); } UInt32 i = 0; while (bufferPos < endPos) _buffer[i++] = _buffer[bufferPos++]; bufferPos = i; } return S_OK; } STDMETHODIMP CFilterCoder::SetOutStream(ISequentialOutStream *outStream) { _bufferPos = 0; _outStream = outStream; return Init(); } STDMETHODIMP CFilterCoder::ReleaseOutStream() { _outStream.Release(); return S_OK; } STDMETHODIMP CFilterCoder::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { UInt32 sizeTemp = MyMin(size, kBufferSize - _bufferPos); memcpy(_buffer + _bufferPos, data, sizeTemp); size -= sizeTemp; if (processedSize != NULL) *processedSize += sizeTemp; data = (const Byte *)data + sizeTemp; UInt32 endPos = _bufferPos + sizeTemp; _bufferPos = Filter->Filter(_buffer, endPos); if (_bufferPos == 0) { _bufferPos = endPos; break; } if (_bufferPos > endPos) { if (size != 0) return E_FAIL; break; } RINOK(WriteWithLimit(_outStream, _bufferPos)); UInt32 i = 0; while (_bufferPos < endPos) _buffer[i++] = _buffer[_bufferPos++]; _bufferPos = i; } return S_OK; } STDMETHODIMP CFilterCoder::Flush() { if (_bufferPos != 0) { // _buffer contains only data refused by previous Filter->Filter call. UInt32 endPos = Filter->Filter(_buffer, _bufferPos); if (endPos > _bufferPos) { for (; _bufferPos < endPos; _bufferPos++) _buffer[_bufferPos] = 0; if (Filter->Filter(_buffer, endPos) != endPos) return E_FAIL; } RINOK(WriteWithLimit(_outStream, _bufferPos)); _bufferPos = 0; } CMyComPtr flush; _outStream.QueryInterface(IID_IOutStreamFlush, &flush); if (flush) return flush->Flush(); return S_OK; } void CFilterCoder::SetInStream_NoSubFilterInit(ISequentialInStream *inStream) { _convertedPosBegin = _convertedPosEnd = _bufferPos = 0; _inStream = inStream; Init2(); } STDMETHODIMP CFilterCoder::SetInStream(ISequentialInStream *inStream) { SetInStream_NoSubFilterInit(inStream); return Init(); } STDMETHODIMP CFilterCoder::ReleaseInStream() { _inStream.Release(); return S_OK; } STDMETHODIMP CFilterCoder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { if (_convertedPosBegin != _convertedPosEnd) { UInt32 sizeTemp = MyMin(size, _convertedPosEnd - _convertedPosBegin); memcpy(data, _buffer + _convertedPosBegin, sizeTemp); _convertedPosBegin += sizeTemp; data = (void *)((Byte *)data + sizeTemp); size -= sizeTemp; if (processedSize != NULL) *processedSize += sizeTemp; break; } UInt32 i; for (i = 0; _convertedPosEnd + i < _bufferPos; i++) _buffer[i] = _buffer[_convertedPosEnd + i]; _bufferPos = i; _convertedPosBegin = _convertedPosEnd = 0; size_t processedSizeTemp = kBufferSize - _bufferPos; RINOK(ReadStream(_inStream, _buffer + _bufferPos, &processedSizeTemp)); _bufferPos += (UInt32)processedSizeTemp; _convertedPosEnd = Filter->Filter(_buffer, _bufferPos); if (_convertedPosEnd == 0) { if (_bufferPos == 0) break; _convertedPosEnd = _bufferPos; // check it continue; } if (_convertedPosEnd > _bufferPos) { for (; _bufferPos < _convertedPosEnd; _bufferPos++) _buffer[_bufferPos] = 0; _convertedPosEnd = Filter->Filter(_buffer, _bufferPos); } } return S_OK; } #ifndef _NO_CRYPTO STDMETHODIMP CFilterCoder::CryptoSetPassword(const Byte *data, UInt32 size) { return _setPassword->CryptoSetPassword(data, size); } STDMETHODIMP CFilterCoder::SetKey(const Byte *data, UInt32 size) { return _cryptoProperties->SetKey(data, size); } STDMETHODIMP CFilterCoder::SetInitVector(const Byte *data, UInt32 size) { return _cryptoProperties->SetInitVector(data, size); } #endif #ifndef EXTRACT_ONLY STDMETHODIMP CFilterCoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *properties, UInt32 numProperties) { return _SetCoderProperties->SetCoderProperties(propIDs, properties, numProperties); } STDMETHODIMP CFilterCoder::WriteCoderProperties(ISequentialOutStream *outStream) { return _writeCoderProperties->WriteCoderProperties(outStream); } /* STDMETHODIMP CFilterCoder::ResetSalt() { return _CryptoResetSalt->ResetSalt(); } */ STDMETHODIMP CFilterCoder::ResetInitVector() { return _CryptoResetInitVector->ResetInitVector(); } #endif STDMETHODIMP CFilterCoder::SetDecoderProperties2(const Byte *data, UInt32 size) { return _setDecoderProperties->SetDecoderProperties2(data, size); } src/libs/7zip/win/CPP/7zip/Common/FilterCoder.h000066400000000000000000000106401325366651500214170ustar00rootroot00000000000000// FilterCoder.h #ifndef __FILTER_CODER_H #define __FILTER_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" #include "../IPassword.h" #define MY_QUERYINTERFACE_ENTRY_AG(i, sub0, sub) else if (iid == IID_ ## i) \ { if (!sub) RINOK(sub0->QueryInterface(IID_ ## i, (void **)&sub)) \ *outObject = (void *)(i *)this; } class CFilterCoder: public ICompressCoder, public ICompressSetInStream, public ISequentialInStream, public ICompressSetOutStream, public ISequentialOutStream, public IOutStreamFlush, #ifndef _NO_CRYPTO public ICryptoSetPassword, public ICryptoProperties, #endif #ifndef EXTRACT_ONLY public ICompressSetCoderProperties, public ICompressWriteCoderProperties, // public ICryptoResetSalt, public ICryptoResetInitVector, #endif public ICompressSetDecoderProperties2, public CMyUnknownImp { protected: Byte *_buffer; CMyComPtr _inStream; CMyComPtr _outStream; UInt32 _bufferPos; UInt32 _convertedPosBegin; UInt32 _convertedPosEnd; bool _outSizeIsDefined; UInt64 _outSize; UInt64 _nowPos64; void Init2() { _nowPos64 = 0; _outSizeIsDefined = false; } HRESULT Init() { Init2(); return Filter->Init(); } CMyComPtr _setPassword; CMyComPtr _cryptoProperties; #ifndef EXTRACT_ONLY CMyComPtr _SetCoderProperties; CMyComPtr _writeCoderProperties; // CMyComPtr _CryptoResetSalt; CMyComPtr _CryptoResetInitVector; #endif CMyComPtr _setDecoderProperties; public: CMyComPtr Filter; CFilterCoder(); ~CFilterCoder(); HRESULT WriteWithLimit(ISequentialOutStream *outStream, UInt32 size); public: MY_QUERYINTERFACE_BEGIN2(ICompressCoder) MY_QUERYINTERFACE_ENTRY(ICompressSetInStream) MY_QUERYINTERFACE_ENTRY(ISequentialInStream) MY_QUERYINTERFACE_ENTRY(ICompressSetOutStream) MY_QUERYINTERFACE_ENTRY(ISequentialOutStream) MY_QUERYINTERFACE_ENTRY(IOutStreamFlush) #ifndef _NO_CRYPTO MY_QUERYINTERFACE_ENTRY_AG(ICryptoSetPassword, Filter, _setPassword) MY_QUERYINTERFACE_ENTRY_AG(ICryptoProperties, Filter, _cryptoProperties) #endif #ifndef EXTRACT_ONLY MY_QUERYINTERFACE_ENTRY_AG(ICompressSetCoderProperties, Filter, _SetCoderProperties) MY_QUERYINTERFACE_ENTRY_AG(ICompressWriteCoderProperties, Filter, _writeCoderProperties) // MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetSalt, Filter, _CryptoResetSalt) MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetInitVector, Filter, _CryptoResetInitVector) #endif MY_QUERYINTERFACE_ENTRY_AG(ICompressSetDecoderProperties2, Filter, _setDecoderProperties) MY_QUERYINTERFACE_END MY_ADDREF_RELEASE STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(ReleaseInStream)(); STDMETHOD(SetInStream)(ISequentialInStream *inStream); STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); \ STDMETHOD(SetOutStream)(ISequentialOutStream *outStream); STDMETHOD(ReleaseOutStream)(); STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Flush)(); #ifndef _NO_CRYPTO STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size); STDMETHOD(SetKey)(const Byte *data, UInt32 size); STDMETHOD(SetInitVector)(const Byte *data, UInt32 size); #endif #ifndef EXTRACT_ONLY STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *properties, UInt32 numProperties); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); // STDMETHOD(ResetSalt)(); STDMETHOD(ResetInitVector)(); #endif STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); void SetInStream_NoSubFilterInit(ISequentialInStream *inStream); }; class CInStreamReleaser { public: CFilterCoder *FilterCoder; CInStreamReleaser(): FilterCoder(0) {} ~CInStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseInStream(); } }; class COutStreamReleaser { public: CFilterCoder *FilterCoder; COutStreamReleaser(): FilterCoder(0) {} ~COutStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseOutStream(); } }; #endif src/libs/7zip/win/CPP/7zip/Common/InBuffer.cpp000066400000000000000000000050201325366651500212440ustar00rootroot00000000000000// InBuffer.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "InBuffer.h" CInBufferBase::CInBufferBase() throw(): _buf(0), _bufLim(0), _bufBase(0), _stream(0), _processedSize(0), _bufSize(0), _wasFinished(false), NumExtraBytes(0) {} bool CInBuffer::Create(size_t bufSize) throw() { const unsigned kMinBlockSize = 1; if (bufSize < kMinBlockSize) bufSize = kMinBlockSize; if (_bufBase != 0 && _bufSize == bufSize) return true; Free(); _bufSize = bufSize; _bufBase = (Byte *)::MidAlloc(bufSize); return (_bufBase != 0); } void CInBuffer::Free() throw() { ::MidFree(_bufBase); _bufBase = 0; } void CInBufferBase::Init() throw() { _processedSize = 0; _buf = _bufBase; _bufLim = _buf; _wasFinished = false; #ifdef _NO_EXCEPTIONS ErrorCode = S_OK; #endif NumExtraBytes = 0; } bool CInBufferBase::ReadBlock() { #ifdef _NO_EXCEPTIONS if (ErrorCode != S_OK) return false; #endif if (_wasFinished) return false; _processedSize += (_buf - _bufBase); _buf = _bufBase; _bufLim = _bufBase; UInt32 processed; // FIX_ME: we can improve it to support (_bufSize >= (1 << 32)) HRESULT result = _stream->Read(_bufBase, (UInt32)_bufSize, &processed); #ifdef _NO_EXCEPTIONS ErrorCode = result; #else if (result != S_OK) throw CInBufferException(result); #endif _bufLim = _buf + processed; _wasFinished = (processed == 0); return !_wasFinished; } bool CInBufferBase::ReadByte_FromNewBlock(Byte &b) { if (!ReadBlock()) { NumExtraBytes++; b = 0xFF; return false; } b = *_buf++; return true; } Byte CInBufferBase::ReadByte_FromNewBlock() { if (!ReadBlock()) { NumExtraBytes++; return 0xFF; } return *_buf++; } size_t CInBufferBase::ReadBytes(Byte *buf, size_t size) { if ((size_t)(_bufLim - _buf) >= size) { const Byte *src = _buf; for (size_t i = 0; i < size; i++) buf[i] = src[i]; _buf += size; return size; } for (size_t i = 0; i < size; i++) { if (_buf >= _bufLim) if (!ReadBlock()) return i; buf[i] = *_buf++; } return size; } size_t CInBufferBase::Skip(size_t size) { size_t processed = 0; for (;;) { size_t rem = (_bufLim - _buf); if (rem >= size) { _buf += size; return processed + size; } _buf += rem; processed += rem; size -= rem; if (!ReadBlock()) return processed; } } src/libs/7zip/win/CPP/7zip/Common/InBuffer.h000066400000000000000000000037551325366651500207260ustar00rootroot00000000000000// InBuffer.h #ifndef __IN_BUFFER_H #define __IN_BUFFER_H #include "../../Common/MyException.h" #include "../IStream.h" #ifndef _NO_EXCEPTIONS struct CInBufferException: public CSystemException { CInBufferException(HRESULT errorCode): CSystemException(errorCode) {} }; #endif class CInBufferBase { protected: Byte *_buf; Byte *_bufLim; Byte *_bufBase; ISequentialInStream *_stream; UInt64 _processedSize; size_t _bufSize; // actually it's number of Bytes for next read. The buf can be larger // only up to 32-bits values now are supported! bool _wasFinished; bool ReadBlock(); bool ReadByte_FromNewBlock(Byte &b); Byte ReadByte_FromNewBlock(); public: #ifdef _NO_EXCEPTIONS HRESULT ErrorCode; #endif UInt32 NumExtraBytes; CInBufferBase() throw(); UInt64 GetStreamSize() const { return _processedSize + (_buf - _bufBase); } UInt64 GetProcessedSize() const { return _processedSize + NumExtraBytes + (_buf - _bufBase); } bool WasFinished() const { return _wasFinished; } void SetStream(ISequentialInStream *stream) { _stream = stream; } void SetBuf(Byte *buf, size_t bufSize, size_t end, size_t pos) { _bufBase = buf; _bufSize = bufSize; _processedSize = 0; _buf = buf + pos; _bufLim = buf + end; _wasFinished = false; #ifdef _NO_EXCEPTIONS ErrorCode = S_OK; #endif NumExtraBytes = 0; } void Init() throw(); bool ReadByte(Byte &b) { if (_buf >= _bufLim) return ReadByte_FromNewBlock(b); b = *_buf++; return true; } Byte ReadByte() { if (_buf >= _bufLim) return ReadByte_FromNewBlock(); return *_buf++; } size_t ReadBytes(Byte *buf, size_t size); size_t Skip(size_t size); }; class CInBuffer: public CInBufferBase { public: ~CInBuffer() { Free(); } bool Create(size_t bufSize) throw(); // only up to 32-bits values now are supported! void Free() throw(); }; #endif src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.cpp000066400000000000000000000051221325366651500225650ustar00rootroot00000000000000// InOutTempBuffer.cpp #include "StdAfx.h" #include "../../../C/7zCrc.h" #include "../../Common/Defs.h" #include "InOutTempBuffer.h" #include "StreamUtils.h" using namespace NWindows; using namespace NFile; using namespace NDir; static const UInt32 kTempBufSize = (1 << 20); static CFSTR kTempFilePrefixString = FTEXT("7zt"); CInOutTempBuffer::CInOutTempBuffer(): _buf(NULL) { } void CInOutTempBuffer::Create() { if (!_buf) _buf = new Byte[kTempBufSize]; } CInOutTempBuffer::~CInOutTempBuffer() { delete []_buf; } void CInOutTempBuffer::InitWriting() { _bufPos = 0; _tempFileCreated = false; _size = 0; _crc = CRC_INIT_VAL; } bool CInOutTempBuffer::WriteToFile(const void *data, UInt32 size) { if (size == 0) return true; if (!_tempFileCreated) { if (!_tempFile.CreateRandomInTempFolder(kTempFilePrefixString, &_outFile)) return false; _tempFileCreated = true; } UInt32 processed; if (!_outFile.Write(data, size, processed)) return false; _crc = CrcUpdate(_crc, data, processed); _size += processed; return (processed == size); } bool CInOutTempBuffer::Write(const void *data, UInt32 size) { if (_bufPos < kTempBufSize) { UInt32 cur = MyMin(kTempBufSize - _bufPos, size); memcpy(_buf + _bufPos, data, cur); _crc = CrcUpdate(_crc, data, cur); _bufPos += cur; size -= cur; data = ((const Byte *)data) + cur; _size += cur; } return WriteToFile(data, size); } HRESULT CInOutTempBuffer::WriteToStream(ISequentialOutStream *stream) { if (!_outFile.Close()) return E_FAIL; UInt64 size = 0; UInt32 crc = CRC_INIT_VAL; if (_bufPos > 0) { RINOK(WriteStream(stream, _buf, _bufPos)); crc = CrcUpdate(crc, _buf, _bufPos); size += _bufPos; } if (_tempFileCreated) { NIO::CInFile inFile; if (!inFile.Open(_tempFile.GetPath())) return E_FAIL; while (size < _size) { UInt32 processed; if (!inFile.ReadPart(_buf, kTempBufSize, processed)) return E_FAIL; if (processed == 0) break; RINOK(WriteStream(stream, _buf, processed)); crc = CrcUpdate(crc, _buf, processed); size += processed; } } return (_crc == crc && size == _size) ? S_OK : E_FAIL; } STDMETHODIMP CSequentialOutTempBufferImp::Write(const void *data, UInt32 size, UInt32 *processed) { if (!_buf->Write(data, size)) { if (processed != NULL) *processed = 0; return E_FAIL; } if (processed != NULL) *processed = size; return S_OK; } src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.h000066400000000000000000000017611325366651500222370ustar00rootroot00000000000000// InOutTempBuffer.h #ifndef __IN_OUT_TEMP_BUFFER_H #define __IN_OUT_TEMP_BUFFER_H #include "../../Common/MyCom.h" #include "../../Windows/FileDir.h" #include "../IStream.h" class CInOutTempBuffer { NWindows::NFile::NDir::CTempFile _tempFile; NWindows::NFile::NIO::COutFile _outFile; Byte *_buf; UInt32 _bufPos; bool _tempFileCreated; UInt64 _size; UInt32 _crc; bool WriteToFile(const void *data, UInt32 size); public: CInOutTempBuffer(); ~CInOutTempBuffer(); void Create(); void InitWriting(); bool Write(const void *data, UInt32 size); HRESULT WriteToStream(ISequentialOutStream *stream); UInt64 GetDataSize() const { return _size; } }; class CSequentialOutTempBufferImp: public ISequentialOutStream, public CMyUnknownImp { CInOutTempBuffer *_buf; public: void Init(CInOutTempBuffer *buffer) { _buf = buffer; } MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; #endif src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.cpp000066400000000000000000000222241325366651500224770ustar00rootroot00000000000000// LimitedStreams.cpp #include "StdAfx.h" #include "LimitedStreams.h" #include "../../Common/Defs.h" STDMETHODIMP CLimitedSequentialInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize = 0; UInt32 sizeToRead = (UInt32)MyMin((_size - _pos), (UInt64)size); HRESULT result = S_OK; if (sizeToRead > 0) { result = _stream->Read(data, sizeToRead, &realProcessedSize); _pos += realProcessedSize; if (realProcessedSize == 0) _wasFinished = true; } if (processedSize) *processedSize = realProcessedSize; return result; } STDMETHODIMP CLimitedInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= _size) { // 9.31: Fixed. Windows doesn't return error in ReadFile and IStream->Read in that case. return S_OK; // return (_virtPos == _size) ? S_OK: E_FAIL; // ERROR_HANDLE_EOF } UInt64 rem = _size - _virtPos; if (rem < size) size = (UInt32)rem; UInt64 newPos = _startOffset + _virtPos; if (newPos != _physPos) { _physPos = newPos; RINOK(SeekToPhys()); } HRESULT res = _stream->Read(data, size, &size); if (processedSize) *processedSize = size; _physPos += size; _virtPos += size; return res; } STDMETHODIMP CLimitedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return S_OK; } HRESULT CreateLimitedInStream(IInStream *inStream, UInt64 pos, UInt64 size, ISequentialInStream **resStream) { *resStream = 0; CLimitedInStream *streamSpec = new CLimitedInStream; CMyComPtr streamTemp = streamSpec; streamSpec->SetStream(inStream); RINOK(streamSpec->InitAndSeek(pos, size)); streamSpec->SeekToStart(); *resStream = streamTemp.Detach(); return S_OK; } STDMETHODIMP CClusterInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= Size) return S_OK; if (_curRem == 0) { UInt32 blockSize = (UInt32)1 << BlockSizeLog; UInt32 virtBlock = (UInt32)(_virtPos >> BlockSizeLog); UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1); UInt32 phyBlock = Vector[virtBlock]; UInt64 newPos = StartOffset + ((UInt64)phyBlock << BlockSizeLog) + offsetInBlock; if (newPos != _physPos) { _physPos = newPos; RINOK(SeekToPhys()); } _curRem = blockSize - offsetInBlock; for (int i = 1; i < 64 && (virtBlock + i) < (UInt32)Vector.Size() && phyBlock + i == Vector[virtBlock + i]; i++) _curRem += (UInt32)1 << BlockSizeLog; UInt64 rem = Size - _virtPos; if (_curRem > rem) _curRem = (UInt32)rem; } if (size > _curRem) size = _curRem; HRESULT res = Stream->Read(data, size, &size); if (processedSize) *processedSize = size; _physPos += size; _virtPos += size; _curRem -= size; return res; } STDMETHODIMP CClusterInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += Size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; if (_virtPos != (UInt64)offset) _curRem = 0; _virtPos = offset; if (newPosition) *newPosition = offset; return S_OK; } STDMETHODIMP CExtentsStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= Extents.Back().Virt) return S_OK; if (size == 0) return S_OK; unsigned left = 0, right = Extents.Size() - 1; for (;;) { unsigned mid = (left + right) / 2; if (mid == left) break; if (_virtPos < Extents[mid].Virt) right = mid; else left = mid; } const CSeekExtent &extent = Extents[left]; UInt64 phyPos = extent.Phy + (_virtPos - extent.Virt); if (_needStartSeek || _phyPos != phyPos) { _needStartSeek = false; _phyPos = phyPos; RINOK(SeekToPhys()); } UInt64 rem = Extents[left + 1].Virt - _virtPos; if (size > rem) size = (UInt32)rem; HRESULT res = Stream->Read(data, size, &size); _phyPos += size; _virtPos += size; if (processedSize) *processedSize = size; return res; } STDMETHODIMP CExtentsStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += Extents.Back().Virt; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return S_OK; } STDMETHODIMP CLimitedSequentialOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { HRESULT result = S_OK; if (processedSize) *processedSize = 0; if (size > _size) { if (_size == 0) { _overflow = true; if (!_overflowIsAllowed) return E_FAIL; if (processedSize) *processedSize = size; return S_OK; } size = (UInt32)_size; } if (_stream) result = _stream->Write(data, size, &size); _size -= size; if (processedSize) *processedSize = size; return result; } STDMETHODIMP CTailInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 cur; HRESULT res = Stream->Read(data, size, &cur); if (processedSize) *processedSize = cur; _virtPos += cur; return res; } STDMETHODIMP CTailInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: { UInt64 pos = 0; RINOK(Stream->Seek(offset, STREAM_SEEK_END, &pos)); if (pos < Offset) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = pos - Offset; if (newPosition) *newPosition = _virtPos; return S_OK; } default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return Stream->Seek(Offset + _virtPos, STREAM_SEEK_SET, NULL); } STDMETHODIMP CLimitedCachedInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (_virtPos >= _size) { // 9.31: Fixed. Windows doesn't return error in ReadFile and IStream->Read in that case. return S_OK; // return (_virtPos == _size) ? S_OK: E_FAIL; // ERROR_HANDLE_EOF } UInt64 rem = _size - _virtPos; if (rem < size) size = (UInt32)rem; UInt64 newPos = _startOffset + _virtPos; UInt64 offsetInCache = newPos - _cachePhyPos; HRESULT res = S_OK; if (newPos >= _cachePhyPos && offsetInCache <= _cacheSize && size <= _cacheSize - (size_t)offsetInCache) memcpy(data, _cache + (size_t)offsetInCache, size); else { if (newPos != _physPos) { _physPos = newPos; RINOK(SeekToPhys()); } res = _stream->Read(data, size, &size); _physPos += size; } if (processedSize) *processedSize = size; _virtPos += size; return res; } STDMETHODIMP CLimitedCachedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return S_OK; } STDMETHODIMP CTailOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { UInt32 cur; HRESULT res = Stream->Write(data, size, &cur); if (processedSize) *processedSize = cur; _virtPos += cur; if (_virtSize < _virtPos) _virtSize = _virtPos; return res; } STDMETHODIMP CTailOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _virtPos; break; case STREAM_SEEK_END: offset += _virtSize; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _virtPos = offset; if (newPosition) *newPosition = _virtPos; return Stream->Seek(Offset + _virtPos, STREAM_SEEK_SET, NULL); } STDMETHODIMP CTailOutStream::SetSize(UInt64 newSize) { _virtSize = newSize; return Stream->SetSize(Offset + newSize); } src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.h000066400000000000000000000136631325366651500221530ustar00rootroot00000000000000// LimitedStreams.h #ifndef __LIMITED_STREAMS_H #define __LIMITED_STREAMS_H #include "../../Common/MyBuffer.h" #include "../../Common/MyCom.h" #include "../../Common/MyVector.h" #include "../IStream.h" class CLimitedSequentialInStream: public ISequentialInStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; UInt64 _pos; bool _wasFinished; public: void SetStream(ISequentialInStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(UInt64 streamSize) { _size = streamSize; _pos = 0; _wasFinished = false; } MY_UNKNOWN_IMP1(ISequentialInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); UInt64 GetSize() const { return _pos; } bool WasFinished() const { return _wasFinished; } }; class CLimitedInStream: public IInStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _virtPos; UInt64 _physPos; UInt64 _size; UInt64 _startOffset; HRESULT SeekToPhys() { return _stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } public: void SetStream(IInStream *stream) { _stream = stream; } HRESULT InitAndSeek(UInt64 startOffset, UInt64 size) { _startOffset = startOffset; _physPos = startOffset; _virtPos = 0; _size = size; return SeekToPhys(); } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); HRESULT SeekToStart() { return Seek(0, STREAM_SEEK_SET, NULL); } }; HRESULT CreateLimitedInStream(IInStream *inStream, UInt64 pos, UInt64 size, ISequentialInStream **resStream); class CClusterInStream: public IInStream, public CMyUnknownImp { UInt64 _virtPos; UInt64 _physPos; UInt32 _curRem; public: CMyComPtr Stream; UInt64 StartOffset; UInt64 Size; unsigned BlockSizeLog; CRecordVector Vector; HRESULT SeekToPhys() { return Stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } HRESULT InitAndSeek() { _curRem = 0; _virtPos = 0; _physPos = StartOffset; if (Vector.Size() > 0) { _physPos = StartOffset + (Vector[0] << BlockSizeLog); return SeekToPhys(); } return S_OK; } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; struct CSeekExtent { UInt64 Phy; UInt64 Virt; }; class CExtentsStream: public IInStream, public CMyUnknownImp { UInt64 _phyPos; UInt64 _virtPos; bool _needStartSeek; HRESULT SeekToPhys() { return Stream->Seek(_phyPos, STREAM_SEEK_SET, NULL); } public: CMyComPtr Stream; CRecordVector Extents; MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); void ReleaseStream() { Stream.Release(); } void Init() { _virtPos = 0; _phyPos = 0; _needStartSeek = true; } }; class CLimitedSequentialOutStream: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; bool _overflow; bool _overflowIsAllowed; public: MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); void SetStream(ISequentialOutStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(UInt64 size, bool overflowIsAllowed = false) { _size = size; _overflow = false; _overflowIsAllowed = overflowIsAllowed; } bool IsFinishedOK() const { return (_size == 0 && !_overflow); } UInt64 GetRem() const { return _size; } }; class CTailInStream: public IInStream, public CMyUnknownImp { UInt64 _virtPos; public: CMyComPtr Stream; UInt64 Offset; void Init() { _virtPos = 0; } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); HRESULT SeekToStart() { return Stream->Seek(Offset, STREAM_SEEK_SET, NULL); } }; class CLimitedCachedInStream: public IInStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _virtPos; UInt64 _physPos; UInt64 _size; UInt64 _startOffset; const Byte *_cache; size_t _cacheSize; size_t _cachePhyPos; HRESULT SeekToPhys() { return _stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } public: CByteBuffer Buffer; void SetStream(IInStream *stream) { _stream = stream; } void SetCache(size_t cacheSize, size_t cachePos) { _cache = Buffer; _cacheSize = cacheSize; _cachePhyPos = cachePos; } HRESULT InitAndSeek(UInt64 startOffset, UInt64 size) { _startOffset = startOffset; _physPos = startOffset; _virtPos = 0; _size = size; return SeekToPhys(); } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); HRESULT SeekToStart() { return Seek(0, STREAM_SEEK_SET, NULL); } }; class CTailOutStream: public IOutStream, public CMyUnknownImp { UInt64 _virtPos; UInt64 _virtSize; public: CMyComPtr Stream; UInt64 Offset; virtual ~CTailOutStream() {} MY_UNKNOWN_IMP2(ISequentialOutStream, IOutStream) void Init() { _virtPos = 0; _virtSize = 0; } STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(SetSize)(UInt64 newSize); }; #endif src/libs/7zip/win/CPP/7zip/Common/LockedStream.cpp000066400000000000000000000013001325366651500221160ustar00rootroot00000000000000// LockedStream.cpp #include "StdAfx.h" #include "LockedStream.h" HRESULT CLockedInStream::Read(UInt64 startPos, void *data, UInt32 size, UInt32 *processedSize) { NWindows::NSynchronization::CCriticalSectionLock lock(_criticalSection); RINOK(_stream->Seek(startPos, STREAM_SEEK_SET, NULL)); return _stream->Read(data, size, processedSize); } STDMETHODIMP CLockedSequentialInStreamImp::Read(void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize = 0; HRESULT result = _lockedInStream->Read(_pos, data, size, &realProcessedSize); _pos += realProcessedSize; if (processedSize != NULL) *processedSize = realProcessedSize; return result; } src/libs/7zip/win/CPP/7zip/Common/LockedStream.h000066400000000000000000000015271325366651500215760ustar00rootroot00000000000000// LockedStream.h #ifndef __LOCKEDSTREAM_H #define __LOCKEDSTREAM_H #include "../../Windows/Synchronization.h" #include "../../Common/MyCom.h" #include "../IStream.h" class CLockedInStream { CMyComPtr _stream; NWindows::NSynchronization::CCriticalSection _criticalSection; public: void Init(IInStream *stream) { _stream = stream; } HRESULT Read(UInt64 startPos, void *data, UInt32 size, UInt32 *processedSize); }; class CLockedSequentialInStreamImp: public ISequentialInStream, public CMyUnknownImp { CLockedInStream *_lockedInStream; UInt64 _pos; public: void Init(CLockedInStream *lockedInStream, UInt64 startPos) { _lockedInStream = lockedInStream; _pos = startPos; } MY_UNKNOWN_IMP STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; #endif src/libs/7zip/win/CPP/7zip/Common/MethodId.h000066400000000000000000000002211325366651500207040ustar00rootroot00000000000000// MethodId.h #ifndef __7Z_METHOD_ID_H #define __7Z_METHOD_ID_H #include "../../Common/MyTypes.h" typedef UInt64 CMethodId; #endif src/libs/7zip/win/CPP/7zip/Common/MethodProps.cpp000066400000000000000000000236671325366651500220310ustar00rootroot00000000000000// MethodProps.cpp #include "StdAfx.h" #include "../../Common/StringToInt.h" #include "MethodProps.h" using namespace NWindows; bool StringToBool(const UString &s, bool &res) { if (s.IsEmpty() || s == L"+" || StringsAreEqualNoCase_Ascii(s, "ON")) { res = true; return true; } if (s == L"-" || StringsAreEqualNoCase_Ascii(s, "OFF")) { res = false; return true; } return false; } HRESULT PROPVARIANT_to_bool(const PROPVARIANT &prop, bool &dest) { switch (prop.vt) { case VT_EMPTY: dest = true; return S_OK; case VT_BOOL: dest = (prop.boolVal != VARIANT_FALSE); return S_OK; case VT_BSTR: return StringToBool(prop.bstrVal, dest) ? S_OK : E_INVALIDARG; } return E_INVALIDARG; } unsigned ParseStringToUInt32(const UString &srcString, UInt32 &number) { const wchar_t *start = srcString; const wchar_t *end; number = ConvertStringToUInt32(start, &end); return (unsigned)(end - start); } HRESULT ParsePropToUInt32(const UString &name, const PROPVARIANT &prop, UInt32 &resValue) { // =VT_UI4 // =VT_EMPTY // {stringUInt32}=VT_EMPTY if (prop.vt == VT_UI4) { if (!name.IsEmpty()) return E_INVALIDARG; resValue = prop.ulVal; return S_OK; } if (prop.vt != VT_EMPTY) return E_INVALIDARG; if (name.IsEmpty()) return S_OK; UInt32 v; if (ParseStringToUInt32(name, v) != name.Len()) return E_INVALIDARG; resValue = v; return S_OK; } HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads) { if (name.IsEmpty()) { switch (prop.vt) { case VT_UI4: numThreads = prop.ulVal; break; default: { bool val; RINOK(PROPVARIANT_to_bool(prop, val)); numThreads = (val ? defaultNumThreads : 1); break; } } return S_OK; } if (prop.vt != VT_EMPTY) return E_INVALIDARG; return ParsePropToUInt32(name, prop, numThreads); } static HRESULT StringToDictSize(const UString &s, UInt32 &dicSize) { const wchar_t *end; UInt32 number = ConvertStringToUInt32(s, &end); unsigned numDigits = (unsigned)(end - s); if (numDigits == 0 || s.Len() > numDigits + 1) return E_INVALIDARG; const unsigned kLogDictSizeLimit = 32; if (s.Len() == numDigits) { if (number >= kLogDictSizeLimit) return E_INVALIDARG; dicSize = (UInt32)1 << (unsigned)number; return S_OK; } unsigned numBits; switch (MyCharLower_Ascii(s[numDigits])) { case 'b': dicSize = number; return S_OK; case 'k': numBits = 10; break; case 'm': numBits = 20; break; case 'g': numBits = 30; break; default: return E_INVALIDARG; } if (number >= ((UInt32)1 << (kLogDictSizeLimit - numBits))) return E_INVALIDARG; dicSize = number << numBits; return S_OK; } static HRESULT PROPVARIANT_to_DictSize(const PROPVARIANT &prop, UInt32 &resValue) { if (prop.vt == VT_UI4) { UInt32 v = prop.ulVal; if (v >= 32) return E_INVALIDARG; resValue = (UInt32)1 << v; return S_OK; } if (prop.vt == VT_BSTR) return StringToDictSize(prop.bstrVal, resValue); return E_INVALIDARG; } void CProps::AddProp32(PROPID propid, UInt32 level) { CProp prop; prop.IsOptional = true; prop.Id = propid; prop.Value = (UInt32)level; Props.Add(prop); } class CCoderProps { PROPID *_propIDs; NCOM::CPropVariant *_props; unsigned _numProps; unsigned _numPropsMax; public: CCoderProps(unsigned numPropsMax) { _numPropsMax = numPropsMax; _numProps = 0; _propIDs = new PROPID[numPropsMax]; _props = new NCOM::CPropVariant[numPropsMax]; } ~CCoderProps() { delete []_propIDs; delete []_props; } void AddProp(const CProp &prop); HRESULT SetProps(ICompressSetCoderProperties *setCoderProperties) { return setCoderProperties->SetCoderProperties(_propIDs, _props, _numProps); } }; void CCoderProps::AddProp(const CProp &prop) { if (_numProps >= _numPropsMax) throw 1; _propIDs[_numProps] = prop.Id; _props[_numProps] = prop.Value; _numProps++; } HRESULT CProps::SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const { CCoderProps coderProps(Props.Size() + (dataSizeReduce ? 1 : 0)); FOR_VECTOR (i, Props) coderProps.AddProp(Props[i]); if (dataSizeReduce) { CProp prop; prop.Id = NCoderPropID::kReduceSize; prop.Value = *dataSizeReduce; coderProps.AddProp(prop); } return coderProps.SetProps(scp); } int CMethodProps::FindProp(PROPID id) const { for (int i = Props.Size() - 1; i >= 0; i--) if (Props[i].Id == id) return i; return -1; } int CMethodProps::GetLevel() const { int i = FindProp(NCoderPropID::kLevel); if (i < 0) return 5; if (Props[i].Value.vt != VT_UI4) return 9; UInt32 level = Props[i].Value.ulVal; return level > 9 ? 9 : (int)level; } struct CNameToPropID { VARTYPE VarType; const char *Name; }; static const CNameToPropID g_NameToPropID[] = { { VT_UI4, "" }, { VT_UI4, "d" }, { VT_UI4, "mem" }, { VT_UI4, "o" }, { VT_UI4, "c" }, { VT_UI4, "pb" }, { VT_UI4, "lc" }, { VT_UI4, "lp" }, { VT_UI4, "fb" }, { VT_BSTR, "mf" }, { VT_UI4, "mc" }, { VT_UI4, "pass" }, { VT_UI4, "a" }, { VT_UI4, "mt" }, { VT_BOOL, "eos" }, { VT_UI4, "x" }, { VT_UI4, "reduceSize" } }; static int FindPropIdExact(const UString &name) { for (unsigned i = 0; i < ARRAY_SIZE(g_NameToPropID); i++) if (StringsAreEqualNoCase_Ascii(name, g_NameToPropID[i].Name)) return i; return -1; } static bool ConvertProperty(const PROPVARIANT &srcProp, VARTYPE varType, NCOM::CPropVariant &destProp) { if (varType == srcProp.vt) { destProp = srcProp; return true; } if (varType == VT_BOOL) { bool res; if (PROPVARIANT_to_bool(srcProp, res) != S_OK) return false; destProp = res; return true; } if (srcProp.vt == VT_EMPTY) { destProp = srcProp; return true; } return false; } static void SplitParams(const UString &srcString, UStringVector &subStrings) { subStrings.Clear(); UString s; int len = srcString.Len(); if (len == 0) return; for (int i = 0; i < len; i++) { wchar_t c = srcString[i]; if (c == L':') { subStrings.Add(s); s.Empty(); } else s += c; } subStrings.Add(s); } static void SplitParam(const UString ¶m, UString &name, UString &value) { int eqPos = param.Find(L'='); if (eqPos >= 0) { name.SetFrom(param, eqPos); value = param.Ptr(eqPos + 1); return; } unsigned i; for (i = 0; i < param.Len(); i++) { wchar_t c = param[i]; if (c >= L'0' && c <= L'9') break; } name.SetFrom(param, i); value = param.Ptr(i); } static bool IsLogSizeProp(PROPID propid) { switch (propid) { case NCoderPropID::kDictionarySize: case NCoderPropID::kUsedMemorySize: case NCoderPropID::kBlockSize: case NCoderPropID::kReduceSize: return true; } return false; } HRESULT CMethodProps::SetParam(const UString &name, const UString &value) { int index = FindPropIdExact(name); if (index < 0) return E_INVALIDARG; const CNameToPropID &nameToPropID = g_NameToPropID[index]; CProp prop; prop.Id = index; if (IsLogSizeProp(prop.Id)) { UInt32 dicSize; RINOK(StringToDictSize(value, dicSize)); prop.Value = dicSize; } else { NCOM::CPropVariant propValue; if (nameToPropID.VarType == VT_BSTR) propValue = value; else if (nameToPropID.VarType == VT_BOOL) { bool res; if (!StringToBool(value, res)) return E_INVALIDARG; propValue = res; } else if (!value.IsEmpty()) { UInt32 number; if (ParseStringToUInt32(value, number) == value.Len()) propValue = number; else propValue = value; } if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value)) return E_INVALIDARG; } Props.Add(prop); return S_OK; } HRESULT CMethodProps::ParseParamsFromString(const UString &srcString) { UStringVector params; SplitParams(srcString, params); FOR_VECTOR (i, params) { const UString ¶m = params[i]; UString name, value; SplitParam(param, name, value); RINOK(SetParam(name, value)); } return S_OK; } HRESULT CMethodProps::ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value) { if (realName.Len() == 0) { // [empty]=method return E_INVALIDARG; } if (value.vt == VT_EMPTY) { // {realName}=[empty] UString name, value; SplitParam(realName, name, value); return SetParam(name, value); } // {realName}=value int index = FindPropIdExact(realName); if (index < 0) return E_INVALIDARG; const CNameToPropID &nameToPropID = g_NameToPropID[index]; CProp prop; prop.Id = index; if (IsLogSizeProp(prop.Id)) { UInt32 dicSize; RINOK(PROPVARIANT_to_DictSize(value, dicSize)); prop.Value = dicSize; } else { if (!ConvertProperty(value, nameToPropID.VarType, prop.Value)) return E_INVALIDARG; } Props.Add(prop); return S_OK; } HRESULT COneMethodInfo::ParseMethodFromString(const UString &s) { int splitPos = s.Find(':'); MethodName = s; if (splitPos < 0) return S_OK; MethodName.DeleteFrom(splitPos); return ParseParamsFromString(s.Ptr(splitPos + 1)); } HRESULT COneMethodInfo::ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value) { if (!realName.IsEmpty() && !StringsAreEqualNoCase_Ascii(realName, "m")) return ParseParamsFromPROPVARIANT(realName, value); // -m{N}=method if (value.vt != VT_BSTR) return E_INVALIDARG; return ParseMethodFromString(value.bstrVal); } src/libs/7zip/win/CPP/7zip/Common/MethodProps.h000066400000000000000000000111241325366651500214570ustar00rootroot00000000000000// MethodProps.h #ifndef __7Z_METHOD_PROPS_H #define __7Z_METHOD_PROPS_H #include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" #include "../ICoder.h" bool StringToBool(const UString &s, bool &res); HRESULT PROPVARIANT_to_bool(const PROPVARIANT &prop, bool &dest); unsigned ParseStringToUInt32(const UString &srcString, UInt32 &number); HRESULT ParsePropToUInt32(const UString &name, const PROPVARIANT &prop, UInt32 &resValue); HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads); struct CProp { PROPID Id; bool IsOptional; NWindows::NCOM::CPropVariant Value; CProp(): IsOptional(false) {} }; struct CProps { CObjectVector Props; void Clear() { Props.Clear(); } bool AreThereNonOptionalProps() const { FOR_VECTOR (i, Props) if (!Props[i].IsOptional) return true; return false; } void AddProp32(PROPID propid, UInt32 level); void AddPropString(PROPID propid, const wchar_t *s) { CProp prop; prop.IsOptional = true; prop.Id = propid; prop.Value = s; Props.Add(prop); } HRESULT SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const; }; class CMethodProps: public CProps { HRESULT SetParam(const UString &name, const UString &value); public: int GetLevel() const; int Get_NumThreads() const { int i = FindProp(NCoderPropID::kNumThreads); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return (int)Props[i].Value.ulVal; return -1; } bool Get_DicSize(UInt32 &res) const { res = 0; int i = FindProp(NCoderPropID::kDictionarySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) { res = Props[i].Value.ulVal; return true; } return false; } int FindProp(PROPID id) const; UInt32 Get_Lzma_Algo() const { int i = FindProp(NCoderPropID::kAlgorithm); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return Props[i].Value.ulVal; return GetLevel() >= 5 ? 1 : 0; } UInt32 Get_Lzma_DicSize() const { int i = FindProp(NCoderPropID::kDictionarySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return Props[i].Value.ulVal; int level = GetLevel(); return level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26)); } UInt32 Get_Lzma_NumThreads(bool &fixedNumber) const { fixedNumber = false; int numThreads = Get_NumThreads(); if (numThreads >= 0) { fixedNumber = true; return numThreads < 2 ? 1 : 2; } return Get_Lzma_Algo() == 0 ? 1 : 2; } UInt32 Get_BZip2_NumThreads(bool &fixedNumber) const { fixedNumber = false; int numThreads = Get_NumThreads(); if (numThreads >= 0) { fixedNumber = true; if (numThreads < 1) return 1; if (numThreads > 64) return 64; return numThreads; } return 1; } UInt32 Get_BZip2_BlockSize() const { int i = FindProp(NCoderPropID::kDictionarySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) { UInt32 blockSize = Props[i].Value.ulVal; const UInt32 kDicSizeMin = 100000; const UInt32 kDicSizeMax = 900000; if (blockSize < kDicSizeMin) blockSize = kDicSizeMin; if (blockSize > kDicSizeMax) blockSize = kDicSizeMax; return blockSize; } int level = GetLevel(); return 100000 * (level >= 5 ? 9 : (level >= 1 ? level * 2 - 1: 1)); } UInt32 Get_Ppmd_MemSize() const { int i = FindProp(NCoderPropID::kUsedMemorySize); if (i >= 0) if (Props[i].Value.vt == VT_UI4) return Props[i].Value.ulVal; int level = GetLevel(); return level >= 9 ? (192 << 20) : ((UInt32)1 << (level + 19)); } void AddLevelProp(UInt32 level) { AddProp32(NCoderPropID::kLevel, level); } void AddNumThreadsProp(UInt32 numThreads) { AddProp32(NCoderPropID::kNumThreads, numThreads); } HRESULT ParseParamsFromString(const UString &srcString); HRESULT ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value); }; class COneMethodInfo: public CMethodProps { public: UString MethodName; void Clear() { CMethodProps::Clear(); MethodName.Empty(); } bool IsEmpty() const { return MethodName.IsEmpty() && Props.IsEmpty(); } HRESULT ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value); HRESULT ParseMethodFromString(const UString &s); }; #endif src/libs/7zip/win/CPP/7zip/Common/OutBuffer.cpp000066400000000000000000000041701325366651500214520ustar00rootroot00000000000000// OutBuffer.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "OutBuffer.h" bool COutBuffer::Create(UInt32 bufSize) throw() { const UInt32 kMinBlockSize = 1; if (bufSize < kMinBlockSize) bufSize = kMinBlockSize; if (_buf != 0 && _bufSize == bufSize) return true; Free(); _bufSize = bufSize; _buf = (Byte *)::MidAlloc(bufSize); return (_buf != 0); } void COutBuffer::Free() throw() { ::MidFree(_buf); _buf = 0; } void COutBuffer::Init() throw() { _streamPos = 0; _limitPos = _bufSize; _pos = 0; _processedSize = 0; _overDict = false; #ifdef _NO_EXCEPTIONS ErrorCode = S_OK; #endif } UInt64 COutBuffer::GetProcessedSize() const throw() { UInt64 res = _processedSize + _pos - _streamPos; if (_streamPos > _pos) res += _bufSize; return res; } HRESULT COutBuffer::FlushPart() throw() { // _streamPos < _bufSize UInt32 size = (_streamPos >= _pos) ? (_bufSize - _streamPos) : (_pos - _streamPos); HRESULT result = S_OK; #ifdef _NO_EXCEPTIONS result = ErrorCode; #endif if (_buf2 != 0) { memcpy(_buf2, _buf + _streamPos, size); _buf2 += size; } if (_stream != 0 #ifdef _NO_EXCEPTIONS && (ErrorCode == S_OK) #endif ) { UInt32 processedSize = 0; result = _stream->Write(_buf + _streamPos, size, &processedSize); size = processedSize; } _streamPos += size; if (_streamPos == _bufSize) _streamPos = 0; if (_pos == _bufSize) { _overDict = true; _pos = 0; } _limitPos = (_streamPos > _pos) ? _streamPos : _bufSize; _processedSize += size; return result; } HRESULT COutBuffer::Flush() throw() { #ifdef _NO_EXCEPTIONS if (ErrorCode != S_OK) return ErrorCode; #endif while (_streamPos != _pos) { HRESULT result = FlushPart(); if (result != S_OK) return result; } return S_OK; } void COutBuffer::FlushWithCheck() { HRESULT result = Flush(); #ifdef _NO_EXCEPTIONS ErrorCode = result; #else if (result != S_OK) throw COutBufferException(result); #endif } src/libs/7zip/win/CPP/7zip/Common/OutBuffer.h000066400000000000000000000024611325366651500211200ustar00rootroot00000000000000// OutBuffer.h #ifndef __OUT_BUFFER_H #define __OUT_BUFFER_H #include "../IStream.h" #include "../../Common/MyCom.h" #include "../../Common/MyException.h" #ifndef _NO_EXCEPTIONS struct COutBufferException: public CSystemException { COutBufferException(HRESULT errorCode): CSystemException(errorCode) {} }; #endif class COutBuffer { protected: Byte *_buf; UInt32 _pos; UInt32 _limitPos; UInt32 _streamPos; UInt32 _bufSize; ISequentialOutStream *_stream; UInt64 _processedSize; Byte *_buf2; bool _overDict; HRESULT FlushPart() throw(); public: #ifdef _NO_EXCEPTIONS HRESULT ErrorCode; #endif COutBuffer(): _buf(0), _pos(0), _stream(0), _buf2(0) {} ~COutBuffer() { Free(); } bool Create(UInt32 bufSize) throw(); void Free() throw(); void SetMemStream(Byte *buf) { _buf2 = buf; } void SetStream(ISequentialOutStream *stream) { _stream = stream; } void Init() throw(); HRESULT Flush() throw(); void FlushWithCheck(); void WriteByte(Byte b) { _buf[_pos++] = b; if (_pos == _limitPos) FlushWithCheck(); } void WriteBytes(const void *data, size_t size) { for (size_t i = 0; i < size; i++) WriteByte(((const Byte *)data)[i]); } UInt64 GetProcessedSize() const throw(); }; #endif src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.cpp000066400000000000000000000020211325366651500223670ustar00rootroot00000000000000// ProgressUtils.cpp #include "StdAfx.h" #include "ProgressUtils.h" CLocalProgress::CLocalProgress() { ProgressOffset = InSize = OutSize = 0; SendRatio = SendProgress = true; } void CLocalProgress::Init(IProgress *progress, bool inSizeIsMain) { _ratioProgress.Release(); _progress = progress; _progress.QueryInterface(IID_ICompressProgressInfo, &_ratioProgress); _inSizeIsMain = inSizeIsMain; } STDMETHODIMP CLocalProgress::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { UInt64 inSizeNew = InSize, outSizeNew = OutSize; if (inSize) inSizeNew += (*inSize); if (outSize) outSizeNew += (*outSize); if (SendRatio && _ratioProgress) { RINOK(_ratioProgress->SetRatioInfo(&inSizeNew, &outSizeNew)); } inSizeNew += ProgressOffset; outSizeNew += ProgressOffset; if (SendProgress) return _progress->SetCompleted(_inSizeIsMain ? &inSizeNew : &outSizeNew); return S_OK; } HRESULT CLocalProgress::SetCur() { return SetRatioInfo(NULL, NULL); } src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.h000066400000000000000000000012471325366651500220450ustar00rootroot00000000000000// ProgressUtils.h #ifndef __PROGRESSUTILS_H #define __PROGRESSUTILS_H #include "../../Common/MyCom.h" #include "../ICoder.h" #include "../IProgress.h" class CLocalProgress: public ICompressProgressInfo, public CMyUnknownImp { CMyComPtr _progress; CMyComPtr _ratioProgress; bool _inSizeIsMain; public: UInt64 ProgressOffset; UInt64 InSize; UInt64 OutSize; bool SendRatio; bool SendProgress; CLocalProgress(); void Init(IProgress *progress, bool inSizeIsMain); HRESULT SetCur(); MY_UNKNOWN_IMP STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); }; #endif src/libs/7zip/win/CPP/7zip/Common/PropId.cpp000066400000000000000000000024221325366651500207440ustar00rootroot00000000000000// PropId.cpp #include "StdAfx.h" #include "../PropID.h" // VARTYPE Byte k7z_PROPID_To_VARTYPE[kpid_NUM_DEFINED] = { VT_EMPTY, VT_UI4, VT_UI4, VT_BSTR, VT_BSTR, VT_BSTR, VT_BOOL, VT_UI8, VT_UI8, VT_UI4, VT_FILETIME, VT_FILETIME, VT_FILETIME, VT_BOOL, VT_BOOL, VT_BOOL, VT_BOOL, VT_BOOL, VT_UI4, VT_UI4, VT_BSTR, VT_BOOL, VT_BSTR, VT_BSTR, VT_BSTR, VT_BSTR, VT_BSTR, VT_UI8, VT_BSTR, VT_UI8, VT_BSTR, VT_UI8, VT_UI8, VT_BSTR, // or VT_UI8 kpidUnpackVer VT_UI4, // or VT_UI8 kpidVolume VT_BOOL, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI4, VT_BOOL, VT_BOOL, VT_BSTR, VT_UI8, VT_UI8, VT_UI4, // kpidChecksum VT_BSTR, VT_UI8, VT_BSTR, // or VT_UI8 kpidId VT_BSTR, VT_BSTR, VT_UI4, VT_UI4, VT_BSTR, VT_BSTR, VT_UI8, VT_UI8, VT_UI4, VT_BSTR, VT_BSTR, VT_BSTR, VT_BSTR, // kpidNtSecure VT_BOOL, VT_BOOL, VT_BOOL, VT_BOOL, VT_BSTR, // SHA-1 VT_BSTR, // SHA-256 VT_BSTR, VT_UI8, VT_UI4, VT_UI4, VT_BSTR, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_UI8, VT_BSTR, VT_BSTR, VT_BSTR, VT_BOOL, VT_BOOL, VT_BOOL, VT_UI8, VT_UI8 }; src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h000066400000000000000000000034401325366651500214270ustar00rootroot00000000000000// RegisterArc.h #ifndef __REGISTER_ARC_H #define __REGISTER_ARC_H #include "../Archive/IArchive.h" #include struct CArcInfo { const char *Name; const char *Ext; const char *AddExt; Byte ClassId; Byte SignatureSize; Byte Signature[20]; UInt16 SignatureOffset; UInt16 Flags; Func_CreateInArchive CreateInArchive; Func_CreateOutArchive CreateOutArchive; Func_IsArc IsArc; bool IsMultiSignature() const { return (Flags & NArcInfoFlags::kMultiSignature) != 0; } std::once_flag once; }; void RegisterArc(const CArcInfo *arcInfo) throw(); #define REGISTER_ARC_NAME(x) CRegister ## x #define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) \ { \ REGISTER_ARC_NAME(x)() \ { \ std::call_once(g_ArcInfo.once, [] { RegisterArc(&g_ArcInfo); }); \ } \ }; \ static REGISTER_ARC_NAME(x) g_RegisterArc; \ void registerArc##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } #define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) \ { \ REGISTER_ARC_NAME(x)() { \ std::call_once(g_ArcInfo.once, [] { \ g_ArcInfo.Signature[0]--; \ RegisterArc(&g_ArcInfo); \ }); \ } \ }; \ static REGISTER_ARC_NAME(x) g_RegisterArc; \ void registerArcDec##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; } #define IMP_CreateArcIn_2(c) \ static IInArchive *CreateArc() { return new c; } #define IMP_CreateArcIn IMP_CreateArcIn_2(CHandler) #ifdef EXTRACT_ONLY #define IMP_CreateArcOut #define REF_CreateArc_Pair CreateArc, NULL #else #define IMP_CreateArcOut static IOutArchive *CreateArcOut() { return new CHandler; } #define REF_CreateArc_Pair CreateArc, CreateArcOut #endif #endif src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h000066400000000000000000000034261325366651500217430ustar00rootroot00000000000000// RegisterCodec.h #ifndef __REGISTER_CODEC_H #define __REGISTER_CODEC_H #include "../Common/MethodId.h" #include "../ICoder.h" #include typedef void * (*CreateCodecP)(); struct CCodecInfo { CreateCodecP CreateDecoder; CreateCodecP CreateEncoder; CMethodId Id; const wchar_t *Name; UInt32 NumInStreams; bool IsFilter; std::once_flag once; }; void RegisterCodec(const CCodecInfo *codecInfo) throw(); #define REGISTER_CODEC_NAME(x) CRegisterCodec ## x #define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) \ { \ REGISTER_CODEC_NAME(x)() \ { \ std::call_once(g_CodecInfo.once, [] { RegisterCodec(&g_CodecInfo); }); \ } \ }; \ static REGISTER_CODEC_NAME(x) g_RegisterCodec; \ void registerCodec##x() { static REGISTER_CODEC_NAME(x) g_RegisterCodecs; } #define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x #define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) \ { \ REGISTER_CODECS_NAME(x)() \ { \ for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \ std::call_once(g_CodecsInfo[i].once, [&i] { RegisterCodec(&g_CodecsInfo[i]); }); \ } \ }; \ static REGISTER_CODECS_NAME(x) g_RegisterCodecs; \ void registerCodec##x() { static REGISTER_CODECS_NAME(x) g_RegisterCodecs; } struct CHasherInfo { IHasher * (*CreateHasher)(); CMethodId Id; const wchar_t *Name; UInt32 DigestSize; }; void RegisterHasher(const CHasherInfo *hasher) throw(); #define REGISTER_HASHER_NAME(x) CRegisterHasher ## x #define REGISTER_HASHER(x) struct REGISTER_HASHER_NAME(x) { \ REGISTER_HASHER_NAME(x)() { RegisterHasher(&g_HasherInfo); }}; \ static REGISTER_HASHER_NAME(x) g_RegisterHasher; #endif src/libs/7zip/win/CPP/7zip/Common/StdAfx.h000066400000000000000000000001451325366651500204050ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../../Common/Common.h" #endif src/libs/7zip/win/CPP/7zip/Common/StreamBinder.cpp000066400000000000000000000061661325366651500221370ustar00rootroot00000000000000// StreamBinder.cpp #include "StdAfx.h" #include "../../Common/MyCom.h" #include "StreamBinder.h" class CBinderInStream: public ISequentialInStream, public CMyUnknownImp { CStreamBinder *_binder; public: MY_UNKNOWN_IMP STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); ~CBinderInStream() { _binder->CloseRead(); } CBinderInStream(CStreamBinder *binder): _binder(binder) {} }; STDMETHODIMP CBinderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { return _binder->Read(data, size, processedSize); } class CBinderOutStream: public ISequentialOutStream, public CMyUnknownImp { CStreamBinder *_binder; public: MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); ~CBinderOutStream() { _binder->CloseWrite(); } CBinderOutStream(CStreamBinder *binder): _binder(binder) {} }; STDMETHODIMP CBinderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { return _binder->Write(data, size, processedSize); } WRes CStreamBinder::CreateEvents() { RINOK(_canWrite_Event.Create(true)); RINOK(_canRead_Event.Create()); return _readingWasClosed_Event.Create(); } void CStreamBinder::ReInit() { _waitWrite = true; _canRead_Event.Reset(); _readingWasClosed_Event.Reset(); ProcessedSize = 0; } void CStreamBinder::CreateStreams(ISequentialInStream **inStream, ISequentialOutStream **outStream) { _waitWrite = true; _bufSize = 0; _buf = NULL; ProcessedSize = 0; CBinderInStream *inStreamSpec = new CBinderInStream(this); CMyComPtr inStreamLoc(inStreamSpec); *inStream = inStreamLoc.Detach(); CBinderOutStream *outStreamSpec = new CBinderOutStream(this); CMyComPtr outStreamLoc(outStreamSpec); *outStream = outStreamLoc.Detach(); } // (_canRead_Event && _bufSize == 0) means that stream is finished. HRESULT CStreamBinder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size != 0) { if (_waitWrite) { RINOK(_canRead_Event.Lock()); _waitWrite = false; } if (size > _bufSize) size = _bufSize; if (size != 0) { memcpy(data, _buf, size); _buf = ((const Byte *)_buf) + size; ProcessedSize += size; if (processedSize) *processedSize = size; _bufSize -= size; if (_bufSize == 0) { _waitWrite = true; _canRead_Event.Reset(); _canWrite_Event.Set(); } } } return S_OK; } HRESULT CStreamBinder::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size != 0) { _buf = data; _bufSize = size; _canWrite_Event.Reset(); _canRead_Event.Set(); HANDLE events[2] = { _canWrite_Event, _readingWasClosed_Event }; DWORD waitResult = ::WaitForMultipleObjects(2, events, FALSE, INFINITE); if (waitResult != WAIT_OBJECT_0 + 0) return S_FALSE; if (processedSize) *processedSize = size; } return S_OK; } src/libs/7zip/win/CPP/7zip/Common/StreamBinder.h000066400000000000000000000016041325366651500215740ustar00rootroot00000000000000// StreamBinder.h #ifndef __STREAM_BINDER_H #define __STREAM_BINDER_H #include "../../Windows/Synchronization.h" #include "../IStream.h" class CStreamBinder { NWindows::NSynchronization::CManualResetEvent _canWrite_Event; NWindows::NSynchronization::CManualResetEvent _canRead_Event; NWindows::NSynchronization::CManualResetEvent _readingWasClosed_Event; bool _waitWrite; UInt32 _bufSize; const void *_buf; public: UInt64 ProcessedSize; WRes CreateEvents(); void CreateStreams(ISequentialInStream **inStream, ISequentialOutStream **outStream); void ReInit(); HRESULT Read(void *data, UInt32 size, UInt32 *processedSize); HRESULT Write(const void *data, UInt32 size, UInt32 *processedSize); void CloseRead() { _readingWasClosed_Event.Set(); } void CloseWrite() { // _bufSize must be = 0 _canRead_Event.Set(); } }; #endif src/libs/7zip/win/CPP/7zip/Common/StreamObjects.cpp000066400000000000000000000143671325366651500223270ustar00rootroot00000000000000// StreamObjects.cpp #include "StdAfx.h" #include #include "../../../C/Alloc.h" #include "StreamObjects.h" STDMETHODIMP CBufInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (_pos >= _size) return S_OK; size_t rem = _size - (size_t)_pos; if (rem > size) rem = (size_t)size; memcpy(data, _data + (size_t)_pos, rem); _pos += rem; if (processedSize) *processedSize = (UInt32)rem; return S_OK; } STDMETHODIMP CBufInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _pos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _pos = offset; if (newPosition) *newPosition = offset; return S_OK; } /* void Create_BufInStream_WithReference(const void *data, size_t size, ISequentialInStream **stream) { CBufInStream *inStreamSpec = new CBufInStream; CMyComPtr streamTemp = inStreamSpec; inStreamSpec->Init((const Byte *)data, size); *stream = streamTemp.Detach(); } */ void Create_BufInStream_WithNewBuf(const void *data, size_t size, ISequentialInStream **stream) { CReferenceBuf *referenceBuf = new CReferenceBuf; CMyComPtr ref = referenceBuf; referenceBuf->Buf.CopyFrom((const Byte *)data, size); CBufInStream *inStreamSpec = new CBufInStream; CMyComPtr streamTemp = inStreamSpec; inStreamSpec->Init(referenceBuf); *stream = streamTemp.Detach(); } void CByteDynBuffer::Free() throw() { free(_buf); _buf = 0; _capacity = 0; } bool CByteDynBuffer::EnsureCapacity(size_t cap) throw() { if (cap <= _capacity) return true; size_t delta; if (_capacity > 64) delta = _capacity / 4; else if (_capacity > 8) delta = 16; else delta = 4; cap = MyMax(_capacity + delta, cap); Byte *buf = (Byte *)realloc(_buf, cap); if (!buf) return false; _buf = buf; _capacity = cap; return true; } Byte *CDynBufSeqOutStream::GetBufPtrForWriting(size_t addSize) { addSize += _size; if (addSize < _size) return NULL; if (!_buffer.EnsureCapacity(addSize)) return NULL; return (Byte *)_buffer + _size; } void CDynBufSeqOutStream::CopyToBuffer(CByteBuffer &dest) const { dest.CopyFrom((const Byte *)_buffer, _size); } STDMETHODIMP CDynBufSeqOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; Byte *buf = GetBufPtrForWriting(size); if (!buf) return E_OUTOFMEMORY; memcpy(buf, data, size); UpdateSize(size); if (processedSize) *processedSize = size; return S_OK; } STDMETHODIMP CBufPtrSeqOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { size_t rem = _size - _pos; if (rem > size) rem = (size_t)size; memcpy(_buffer + _pos, data, rem); _pos += rem; if (processedSize) *processedSize = (UInt32)rem; return (rem != 0 || size == 0) ? S_OK : E_FAIL; } STDMETHODIMP CSequentialOutStreamSizeCount::Write(const void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize; HRESULT result = _stream->Write(data, size, &realProcessedSize); _size += realProcessedSize; if (processedSize) *processedSize = realProcessedSize; return result; } static const UInt64 kEmptyTag = (UInt64)(Int64)-1; void CCachedInStream::Free() throw() { MyFree(_tags); _tags = 0; MidFree(_data); _data = 0; } bool CCachedInStream::Alloc(unsigned blockSizeLog, unsigned numBlocksLog) throw() { unsigned sizeLog = blockSizeLog + numBlocksLog; if (sizeLog >= sizeof(size_t) * 8) return false; size_t dataSize = (size_t)1 << sizeLog; if (_data == 0 || dataSize != _dataSize) { MidFree(_data); _data = (Byte *)MidAlloc(dataSize); if (_data == 0) return false; _dataSize = dataSize; } if (_tags == 0 || numBlocksLog != _numBlocksLog) { MyFree(_tags); _tags = (UInt64 *)MyAlloc(sizeof(UInt64) << numBlocksLog); if (_tags == 0) return false; _numBlocksLog = numBlocksLog; } _blockSizeLog = blockSizeLog; return true; } void CCachedInStream::Init(UInt64 size) throw() { _size = size; _pos = 0; size_t numBlocks = (size_t)1 << _numBlocksLog; for (size_t i = 0; i < numBlocks; i++) _tags[i] = kEmptyTag; } STDMETHODIMP CCachedInStream::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; if (size == 0) return S_OK; if (_pos >= _size) return S_OK; { UInt64 rem = _size - _pos; if (size > rem) size = (UInt32)rem; } while (size != 0) { UInt64 cacheTag = _pos >> _blockSizeLog; size_t cacheIndex = (size_t)cacheTag & (((size_t)1 << _numBlocksLog) - 1); Byte *p = _data + (cacheIndex << _blockSizeLog); if (_tags[cacheIndex] != cacheTag) { UInt64 remInBlock = _size - (cacheTag << _blockSizeLog); size_t blockSize = (size_t)1 << _blockSizeLog; if (blockSize > remInBlock) blockSize = (size_t)remInBlock; RINOK(ReadBlock(cacheTag, p, blockSize)); _tags[cacheIndex] = cacheTag; } size_t offset = (size_t)_pos & (((size_t)1 << _blockSizeLog) - 1); UInt32 cur = (UInt32)MyMin(((size_t)1 << _blockSizeLog) - offset, (size_t)size); memcpy(data, p + offset, cur); if (processedSize) *processedSize += cur; data = (void *)((const Byte *)data + cur); _pos += cur; size -= cur; } return S_OK; } STDMETHODIMP CCachedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { switch (seekOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: offset += _pos; break; case STREAM_SEEK_END: offset += _size; break; default: return STG_E_INVALIDFUNCTION; } if (offset < 0) return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; _pos = offset; if (newPosition) *newPosition = offset; return S_OK; } src/libs/7zip/win/CPP/7zip/Common/StreamObjects.h000066400000000000000000000073101325366651500217620ustar00rootroot00000000000000// StreamObjects.h #ifndef __STREAM_OBJECTS_H #define __STREAM_OBJECTS_H #include "../../Common/MyBuffer.h" #include "../../Common/MyCom.h" #include "../../Common/MyVector.h" #include "../IStream.h" struct CReferenceBuf: public IUnknown, public CMyUnknownImp { CByteBuffer Buf; MY_UNKNOWN_IMP }; class CBufInStream: public IInStream, public CMyUnknownImp { const Byte *_data; UInt64 _pos; size_t _size; CMyComPtr _ref; public: void Init(const Byte *data, size_t size, IUnknown *ref = 0) { _data = data; _size = size; _pos = 0; _ref = ref; } void Init(CReferenceBuf *ref) { Init(ref->Buf, ref->Buf.Size(), ref); } MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; // void Create_BufInStream_WithReference(const void *data, size_t size, ISequentialInStream **stream); void Create_BufInStream_WithNewBuf(const void *data, size_t size, ISequentialInStream **stream); class CByteDynBuffer { size_t _capacity; Byte *_buf; public: CByteDynBuffer(): _capacity(0), _buf(0) {}; // there is no copy constructor. So don't copy this object. ~CByteDynBuffer() { Free(); } void Free() throw(); size_t GetCapacity() const { return _capacity; } operator Byte*() const { return _buf; }; operator const Byte*() const { return _buf; }; bool EnsureCapacity(size_t capacity) throw(); }; class CDynBufSeqOutStream: public ISequentialOutStream, public CMyUnknownImp { CByteDynBuffer _buffer; size_t _size; public: CDynBufSeqOutStream(): _size(0) {} void Init() { _size = 0; } size_t GetSize() const { return _size; } const Byte *GetBuffer() const { return _buffer; } void CopyToBuffer(CByteBuffer &dest) const; Byte *GetBufPtrForWriting(size_t addSize); void UpdateSize(size_t addSize) { _size += addSize; } MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; class CBufPtrSeqOutStream: public ISequentialOutStream, public CMyUnknownImp { Byte *_buffer; size_t _size; size_t _pos; public: void Init(Byte *buffer, size_t size) { _buffer = buffer; _pos = 0; _size = size; } size_t GetPos() const { return _pos; } MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; class CSequentialOutStreamSizeCount: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; public: void SetStream(ISequentialOutStream *stream) { _stream = stream; } void Init() { _size = 0; } UInt64 GetSize() const { return _size; } MY_UNKNOWN_IMP1(ISequentialOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; class CCachedInStream: public IInStream, public CMyUnknownImp { UInt64 *_tags; Byte *_data; size_t _dataSize; unsigned _blockSizeLog; unsigned _numBlocksLog; UInt64 _size; UInt64 _pos; protected: virtual HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize) = 0; public: CCachedInStream(): _tags(0), _data(0) {} virtual ~CCachedInStream() { Free(); } // the destructor must be virtual (release calls it) !!! void Free() throw(); bool Alloc(unsigned blockSizeLog, unsigned numBlocksLog) throw(); void Init(UInt64 size) throw(); MY_UNKNOWN_IMP2(ISequentialInStream, IInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; #endif src/libs/7zip/win/CPP/7zip/Common/StreamUtils.cpp000066400000000000000000000031461325366651500220270ustar00rootroot00000000000000// StreamUtils.cpp #include "StdAfx.h" #include "StreamUtils.h" static const UInt32 kBlockSize = ((UInt32)1 << 31); HRESULT ReadStream(ISequentialInStream *stream, void *data, size_t *processedSize) throw() { size_t size = *processedSize; *processedSize = 0; while (size != 0) { UInt32 curSize = (size < kBlockSize) ? (UInt32)size : kBlockSize; UInt32 processedSizeLoc; HRESULT res = stream->Read(data, curSize, &processedSizeLoc); *processedSize += processedSizeLoc; data = (void *)((Byte *)data + processedSizeLoc); size -= processedSizeLoc; RINOK(res); if (processedSizeLoc == 0) return S_OK; } return S_OK; } HRESULT ReadStream_FALSE(ISequentialInStream *stream, void *data, size_t size) throw() { size_t processedSize = size; RINOK(ReadStream(stream, data, &processedSize)); return (size == processedSize) ? S_OK : S_FALSE; } HRESULT ReadStream_FAIL(ISequentialInStream *stream, void *data, size_t size) throw() { size_t processedSize = size; RINOK(ReadStream(stream, data, &processedSize)); return (size == processedSize) ? S_OK : E_FAIL; } HRESULT WriteStream(ISequentialOutStream *stream, const void *data, size_t size) throw() { while (size != 0) { UInt32 curSize = (size < kBlockSize) ? (UInt32)size : kBlockSize; UInt32 processedSizeLoc; HRESULT res = stream->Write(data, curSize, &processedSizeLoc); data = (const void *)((const Byte *)data + processedSizeLoc); size -= processedSizeLoc; RINOK(res); if (processedSizeLoc == 0) return E_FAIL; } return S_OK; } src/libs/7zip/win/CPP/7zip/Common/StreamUtils.h000066400000000000000000000007171325366651500214750ustar00rootroot00000000000000// StreamUtils.h #ifndef __STREAM_UTILS_H #define __STREAM_UTILS_H #include "../IStream.h" HRESULT ReadStream(ISequentialInStream *stream, void *data, size_t *size) throw(); HRESULT ReadStream_FALSE(ISequentialInStream *stream, void *data, size_t size) throw(); HRESULT ReadStream_FAIL(ISequentialInStream *stream, void *data, size_t size) throw(); HRESULT WriteStream(ISequentialOutStream *stream, const void *data, size_t size) throw(); #endif src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.cpp000066400000000000000000000022711325366651500216230ustar00rootroot00000000000000// UniqBlocks.cpp #include "StdAfx.h" #include "UniqBlocks.h" int CUniqBlocks::AddUniq(const Byte *data, size_t size) { unsigned left = 0, right = Sorted.Size(); while (left != right) { unsigned mid = (left + right) / 2; int index = Sorted[mid]; const CByteBuffer &buf = Bufs[index]; size_t sizeMid = buf.Size(); if (size < sizeMid) right = mid; else if (size > sizeMid) left = mid + 1; else { int cmp = memcmp(data, buf, size); if (cmp == 0) return index; if (cmp < 0) right = mid; else left = mid + 1; } } int index = Bufs.Size(); Sorted.Insert(left, index); CByteBuffer &buf = Bufs.AddNew(); buf.CopyFrom(data, size); return index; } UInt64 CUniqBlocks::GetTotalSizeInBytes() const { UInt64 size = 0; FOR_VECTOR (i, Bufs) size += Bufs[i].Size(); return size; } void CUniqBlocks::GetReverseMap() { unsigned num = Sorted.Size(); BufIndexToSortedIndex.ClearAndSetSize(num); int *p = &BufIndexToSortedIndex[0]; unsigned i; for (i = 0; i < num; i++) p[i] = 0; for (i = 0; i < num; i++) p[Sorted[i]] = i; } src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.h000066400000000000000000000011201325366651500212600ustar00rootroot00000000000000// UniqBlocks.h #ifndef __UNIQ_BLOCKS_H #define __UNIQ_BLOCKS_H #include "../../Common/MyTypes.h" #include "../../Common/MyBuffer.h" #include "../../Common/MyVector.h" struct CUniqBlocks { CObjectVector Bufs; CIntVector Sorted; CIntVector BufIndexToSortedIndex; int AddUniq(const Byte *data, size_t size); UInt64 GetTotalSizeInBytes() const; void GetReverseMap(); bool IsOnlyEmpty() const { if (Bufs.Size() == 0) return true; if (Bufs.Size() > 1) return false; return Bufs[0].Size() == 0; } }; #endif src/libs/7zip/win/CPP/7zip/Common/VirtThread.cpp000066400000000000000000000015121325366651500216220ustar00rootroot00000000000000// VirtThread.cpp #include "StdAfx.h" #include "VirtThread.h" static THREAD_FUNC_DECL CoderThread(void *p) { for (;;) { CVirtThread *t = (CVirtThread *)p; t->StartEvent.Lock(); if (t->Exit) return 0; t->Execute(); t->FinishedEvent.Set(); } } WRes CVirtThread::Create() { RINOK(StartEvent.CreateIfNotCreated()); RINOK(FinishedEvent.CreateIfNotCreated()); StartEvent.Reset(); FinishedEvent.Reset(); Exit = false; if (Thread.IsCreated()) return S_OK; return Thread.Create(CoderThread, this); } void CVirtThread::Start() { Exit = false; StartEvent.Set(); } void CVirtThread::WaitThreadFinish() { Exit = true; if (StartEvent.IsCreated()) StartEvent.Set(); if (Thread.IsCreated()) { Thread.Wait(); Thread.Close(); } } src/libs/7zip/win/CPP/7zip/Common/VirtThread.h000066400000000000000000000011131325366651500212640ustar00rootroot00000000000000// VirtThread.h #ifndef __VIRT_THREAD_H #define __VIRT_THREAD_H #include "../../Windows/Synchronization.h" #include "../../Windows/Thread.h" struct CVirtThread { NWindows::NSynchronization::CAutoResetEvent StartEvent; NWindows::NSynchronization::CAutoResetEvent FinishedEvent; NWindows::CThread Thread; bool Exit; ~CVirtThread() { WaitThreadFinish(); } void WaitThreadFinish(); // call it in destructor of child class ! WRes Create(); void Start(); virtual void Execute() = 0; void WaitExecuteFinish() { FinishedEvent.Lock(); } }; #endif src/libs/7zip/win/CPP/7zip/Compress/000077500000000000000000000000001325366651500174065ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.cpp000066400000000000000000000242621325366651500216550ustar00rootroot00000000000000// Bcj2Coder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "Bcj2Coder.h" namespace NCompress { namespace NBcj2 { inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); } inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); } inline unsigned GetIndex(Byte b0, Byte b1) { return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); } #ifndef EXTRACT_ONLY static const unsigned kBufSize = 1 << 17; #define NUM_BITS 2 #define SIGN_BIT (1 << NUM_BITS) #define MASK_HIGH (0x100 - (1 << (NUM_BITS + 1))) static const UInt32 kDefaultLimit = (1 << (24 + NUM_BITS)); static bool inline Test86MSByte(Byte b) { return (((b) + SIGN_BIT) & MASK_HIGH) == 0; } CEncoder::~CEncoder() { ::MidFree(_buf); } HRESULT CEncoder::Flush() { RINOK(_mainStream.Flush()); RINOK(_callStream.Flush()); RINOK(_jumpStream.Flush()); _rc.FlushData(); return _rc.FlushStream(); } HRESULT CEncoder::CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams, ICompressProgressInfo *progress) { if (numInStreams != 1 || numOutStreams != 4) return E_INVALIDARG; if (!_mainStream.Create(1 << 18)) return E_OUTOFMEMORY; if (!_callStream.Create(1 << 18)) return E_OUTOFMEMORY; if (!_jumpStream.Create(1 << 18)) return E_OUTOFMEMORY; if (!_rc.Create(1 << 20)) return E_OUTOFMEMORY; if (_buf == 0) { _buf = (Byte *)MidAlloc(kBufSize); if (_buf == 0) return E_OUTOFMEMORY; } bool sizeIsDefined = false; UInt64 inSize = 0; if (inSizes) if (inSizes[0]) { inSize = *inSizes[0]; if (inSize <= kDefaultLimit) sizeIsDefined = true; } ISequentialInStream *inStream = inStreams[0]; _mainStream.SetStream(outStreams[0]); _mainStream.Init(); _callStream.SetStream(outStreams[1]); _callStream.Init(); _jumpStream.SetStream(outStreams[2]); _jumpStream.Init(); _rc.SetStream(outStreams[3]); _rc.Init(); for (unsigned i = 0; i < 256 + 2; i++) _statusEncoder[i].Init(); CMyComPtr getSubStreamSize; { inStream->QueryInterface(IID_ICompressGetSubStreamSize, (void **)&getSubStreamSize); } UInt32 nowPos = 0; UInt64 nowPos64 = 0; UInt32 bufPos = 0; Byte prevByte = 0; UInt64 subStreamIndex = 0; UInt64 subStreamStartPos = 0; UInt64 subStreamEndPos = 0; for (;;) { UInt32 processedSize = 0; for (;;) { UInt32 size = kBufSize - (bufPos + processedSize); UInt32 processedSizeLoc; if (size == 0) break; RINOK(inStream->Read(_buf + bufPos + processedSize, size, &processedSizeLoc)); if (processedSizeLoc == 0) break; processedSize += processedSizeLoc; } UInt32 endPos = bufPos + processedSize; if (endPos < 5) { // change it for (bufPos = 0; bufPos < endPos; bufPos++) { Byte b = _buf[bufPos]; _mainStream.WriteByte(b); UInt32 index; if (b == 0xE8) index = prevByte; else if (b == 0xE9) index = 256; else if (IsJcc(prevByte, b)) index = 257; else { prevByte = b; continue; } _statusEncoder[index].Encode(&_rc, 0); prevByte = b; } return Flush(); } bufPos = 0; UInt32 limit = endPos - 5; while (bufPos <= limit) { Byte b = _buf[bufPos]; _mainStream.WriteByte(b); if (!IsJ(prevByte, b)) { bufPos++; prevByte = b; continue; } Byte nextByte = _buf[bufPos + 4]; UInt32 src = (UInt32(nextByte) << 24) | (UInt32(_buf[bufPos + 3]) << 16) | (UInt32(_buf[bufPos + 2]) << 8) | (_buf[bufPos + 1]); UInt32 dest = (nowPos + bufPos + 5) + src; // if (Test86MSByte(nextByte)) bool convert; if (getSubStreamSize) { UInt64 currentPos = (nowPos64 + bufPos); while (subStreamEndPos < currentPos) { UInt64 subStreamSize; HRESULT result = getSubStreamSize->GetSubStreamSize(subStreamIndex, &subStreamSize); if (result == S_OK) { subStreamStartPos = subStreamEndPos; subStreamEndPos += subStreamSize; subStreamIndex++; } else if (result == S_FALSE || result == E_NOTIMPL) { getSubStreamSize.Release(); subStreamStartPos = 0; subStreamEndPos = subStreamStartPos - 1; } else return result; } if (getSubStreamSize == NULL) { if (sizeIsDefined) convert = (dest < inSize); else convert = Test86MSByte(nextByte); } else if (subStreamEndPos - subStreamStartPos > kDefaultLimit) convert = Test86MSByte(nextByte); else { UInt64 dest64 = (currentPos + 5) + Int64(Int32(src)); convert = (dest64 >= subStreamStartPos && dest64 < subStreamEndPos); } } else if (sizeIsDefined) convert = (dest < inSize); else convert = Test86MSByte(nextByte); unsigned index = GetIndex(prevByte, b); if (convert) { _statusEncoder[index].Encode(&_rc, 1); bufPos += 5; COutBuffer &s = (b == 0xE8) ? _callStream : _jumpStream; for (int i = 24; i >= 0; i -= 8) s.WriteByte((Byte)(dest >> i)); prevByte = nextByte; } else { _statusEncoder[index].Encode(&_rc, 0); bufPos++; prevByte = b; } } nowPos += bufPos; nowPos64 += bufPos; if (progress) { /* const UInt64 compressedSize = _mainStream.GetProcessedSize() + _callStream.GetProcessedSize() + _jumpStream.GetProcessedSize() + _rc.GetProcessedSize(); */ RINOK(progress->SetRatioInfo(&nowPos64, NULL)); } UInt32 i = 0; while (bufPos < endPos) _buf[i++] = _buf[bufPos++]; bufPos = i; } } STDMETHODIMP CEncoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress) { try { return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress); } catch(const COutBufferException &e) { return e.ErrorCode; } catch(...) { return S_FALSE; } } #endif STDMETHODIMP CDecoder::SetInBufSize(UInt32 streamIndex, UInt32 size) { _inBufSizes[streamIndex] = size; return S_OK; } STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; } CDecoder::CDecoder(): _outBufSize(1 << 16) { _inBufSizes[0] = 1 << 20; _inBufSizes[1] = 1 << 20; _inBufSizes[2] = 1 << 20; _inBufSizes[3] = 1 << 20; } HRESULT CDecoder::CodeReal(ISequentialInStream **inStreams, const UInt64 ** /* inSizes */, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 ** /* outSizes */, UInt32 numOutStreams, ICompressProgressInfo *progress) { if (numInStreams != 4 || numOutStreams != 1) return E_INVALIDARG; if (!_mainStream.Create(_inBufSizes[0])) return E_OUTOFMEMORY; if (!_callStream.Create(_inBufSizes[1])) return E_OUTOFMEMORY; if (!_jumpStream.Create(_inBufSizes[2])) return E_OUTOFMEMORY; if (!_rc.Create(_inBufSizes[3])) return E_OUTOFMEMORY; if (!_outStream.Create(_outBufSize)) return E_OUTOFMEMORY; _mainStream.SetStream(inStreams[0]); _callStream.SetStream(inStreams[1]); _jumpStream.SetStream(inStreams[2]); _rc.SetStream(inStreams[3]); _outStream.SetStream(outStreams[0]); _mainStream.Init(); _callStream.Init(); _jumpStream.Init(); _rc.Init(); _outStream.Init(); for (unsigned i = 0; i < 256 + 2; i++) _statusDecoder[i].Init(); Byte prevByte = 0; UInt32 processedBytes = 0; for (;;) { if (processedBytes >= (1 << 20) && progress) { /* const UInt64 compressedSize = _mainStream.GetProcessedSize() + _callStream.GetProcessedSize() + _jumpStream.GetProcessedSize() + _rc.GetProcessedSize(); */ const UInt64 nowPos64 = _outStream.GetProcessedSize(); RINOK(progress->SetRatioInfo(NULL, &nowPos64)); processedBytes = 0; } UInt32 i; Byte b = 0; const UInt32 kBurstSize = (1 << 18); for (i = 0; i < kBurstSize; i++) { if (!_mainStream.ReadByte(b)) return _outStream.Flush(); _outStream.WriteByte(b); if (IsJ(prevByte, b)) break; prevByte = b; } processedBytes += i; if (i == kBurstSize) continue; unsigned index = GetIndex(prevByte, b); if (_statusDecoder[index].Decode(&_rc) == 1) { UInt32 src = 0; CInBuffer &s = (b == 0xE8) ? _callStream : _jumpStream; for (unsigned i = 0; i < 4; i++) { Byte b0; if (!s.ReadByte(b0)) return S_FALSE; src <<= 8; src |= ((UInt32)b0); } UInt32 dest = src - (UInt32(_outStream.GetProcessedSize()) + 4) ; _outStream.WriteByte((Byte)(dest)); _outStream.WriteByte((Byte)(dest >> 8)); _outStream.WriteByte((Byte)(dest >> 16)); _outStream.WriteByte((Byte)(dest >> 24)); prevByte = (Byte)(dest >> 24); processedBytes += 4; } else prevByte = b; } } STDMETHODIMP CDecoder::Code(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress) { try { return CodeReal(inStreams, inSizes, numInStreams, outStreams, outSizes,numOutStreams, progress); } catch(const CInBufferException &e) { return e.ErrorCode; } catch(const COutBufferException &e) { return e.ErrorCode; } catch(...) { return S_FALSE; } } }} src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.h000066400000000000000000000041351325366651500213170ustar00rootroot00000000000000// Bcj2Coder.h #ifndef __COMPRESS_BCJ2_CODER_H #define __COMPRESS_BCJ2_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" #include "RangeCoderBit.h" namespace NCompress { namespace NBcj2 { const unsigned kNumMoveBits = 5; #ifndef EXTRACT_ONLY class CEncoder: public ICompressCoder2, public CMyUnknownImp { Byte *_buf; COutBuffer _mainStream; COutBuffer _callStream; COutBuffer _jumpStream; NRangeCoder::CEncoder _rc; NRangeCoder::CBitEncoder _statusEncoder[256 + 2]; HRESULT Flush(); public: MY_UNKNOWN_IMP HRESULT CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); CEncoder(): _buf(0) {}; ~CEncoder(); }; #endif class CDecoder: public ICompressCoder2, public ICompressSetBufSize, public CMyUnknownImp { CInBuffer _mainStream; CInBuffer _callStream; CInBuffer _jumpStream; NRangeCoder::CDecoder _rc; NRangeCoder::CBitDecoder _statusDecoder[256 + 2]; COutBuffer _outStream; UInt32 _inBufSizes[4]; UInt32 _outBufSize; public: MY_UNKNOWN_IMP1(ICompressSetBufSize); HRESULT CodeReal(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress); STDMETHOD(SetInBufSize)(UInt32 streamIndex, UInt32 size); STDMETHOD(SetOutBufSize)(UInt32 streamIndex, UInt32 size); CDecoder(); }; }} #endif src/libs/7zip/win/CPP/7zip/Compress/Bcj2Register.cpp000066400000000000000000000007751325366651500224100ustar00rootroot00000000000000// Bcj2Register.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "Bcj2Coder.h" static void *CreateCodec() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CDecoder()); } #ifndef EXTRACT_ONLY static void *CreateCodecOut() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CEncoder()); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x0303011B, L"BCJ2", 4, false }; REGISTER_CODEC(BCJ2) src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.cpp000066400000000000000000000005331325366651500215660ustar00rootroot00000000000000// BcjCoder.cpp #include "StdAfx.h" #include "BcjCoder.h" UInt32 CBCJ_x86_Encoder::SubFilter(Byte *data, UInt32 size) { return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 1); } UInt32 CBCJ_x86_Decoder::SubFilter(Byte *data, UInt32 size) { return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 0); } src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.h000066400000000000000000000005171325366651500212350ustar00rootroot00000000000000// BcjCoder.h #ifndef __COMPRESS_BCJ_CODER_H #define __COMPRESS_BCJ_CODER_H #include "../../../C/Bra.h" #include "BranchCoder.h" struct CBranch86 { UInt32 _prevMask; void x86Init() { x86_Convert_Init(_prevMask); } }; MyClassB(BCJ_x86, 0x01, 3, CBranch86 , virtual void SubInit() { x86Init(); }) #endif src/libs/7zip/win/CPP/7zip/Compress/BcjRegister.cpp000066400000000000000000000007441325366651500223220ustar00rootroot00000000000000// BcjRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "BcjCoder.h" static void *CreateCodec() { return (void *)(ICompressFilter *)(new CBCJ_x86_Decoder()); } #ifndef EXTRACT_ONLY static void *CreateCodecOut() { return (void *)(ICompressFilter *)(new CBCJ_x86_Encoder()); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x03030103, L"BCJ", 1, true }; REGISTER_CODEC(BCJ) src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.cpp000066400000000000000000000005421325366651500222650ustar00rootroot00000000000000// BranchCoder.cpp #include "StdAfx.h" #include "BranchCoder.h" STDMETHODIMP CBranchConverter::Init() { _bufferPos = 0; SubInit(); return S_OK; } STDMETHODIMP_(UInt32) CBranchConverter::Filter(Byte *data, UInt32 size) { UInt32 processedSize = SubFilter(data, size); _bufferPos += processedSize; return processedSize; } src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.h000066400000000000000000000025621325366651500217360ustar00rootroot00000000000000// BranchCoder.h #ifndef __COMPRESS_BRANCH_CODER_H #define __COMPRESS_BRANCH_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" class CBranchConverter: public ICompressFilter, public CMyUnknownImp { protected: UInt32 _bufferPos; virtual void SubInit() {} virtual UInt32 SubFilter(Byte *data, UInt32 size) = 0; public: MY_UNKNOWN_IMP; STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); }; #define MyClassEncoderA(Name) class C ## Name: public CBranchConverter \ { public: UInt32 SubFilter(Byte *data, UInt32 size); }; #define MyClassDecoderA(Name) class C ## Name: public CBranchConverter \ { public: UInt32 SubFilter(Byte *data, UInt32 size); }; #define MyClassEncoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \ { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT}; #define MyClassDecoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \ { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT}; #define MyClassA(Name, id, subId) \ MyClassEncoderA(Name ## _Encoder) \ MyClassDecoderA(Name ## _Decoder) #define MyClassB(Name, id, subId, ADD_ITEMS, ADD_INIT) \ MyClassEncoderB(Name ## _Encoder, ADD_ITEMS, ADD_INIT) \ MyClassDecoderB(Name ## _Decoder, ADD_ITEMS, ADD_INIT) #endif src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.cpp000066400000000000000000000010231325366651500221170ustar00rootroot00000000000000// BranchMisc.cpp #include "StdAfx.h" #include "../../../C/Bra.h" #include "BranchMisc.h" #define SUB_FILTER_IMP2(name, coderStr, coderNum) \ UInt32 CBC_ ## name ## coderStr::SubFilter(Byte *data, UInt32 size) \ { return (UInt32)::name ## Convert(data, size, _bufferPos, coderNum); } #define SUB_FILTER_IMP(name) \ SUB_FILTER_IMP2(name, Encoder, 1) \ SUB_FILTER_IMP2(name, Decoder, 0) \ SUB_FILTER_IMP(ARM_) SUB_FILTER_IMP(ARMT_) SUB_FILTER_IMP(PPC_) SUB_FILTER_IMP(SPARC_) SUB_FILTER_IMP(IA64_) src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.h000066400000000000000000000004201325366651500215640ustar00rootroot00000000000000// BranchMisc.h #ifndef __COMPRESS_BRANCH_MISC_H #define __COMPRESS_BRANCH_MISC_H #include "BranchCoder.h" MyClassA(BC_ARM, 0x05, 1) MyClassA(BC_ARMT, 0x07, 1) MyClassA(BC_PPC, 0x02, 5) MyClassA(BC_SPARC, 0x08, 5) MyClassA(BC_IA64, 0x04, 1) #endif src/libs/7zip/win/CPP/7zip/Compress/BranchRegister.cpp000066400000000000000000000016011325366651500230120ustar00rootroot00000000000000// BranchRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "BranchMisc.h" #define CREATE_CODEC(x) \ static void *CreateCodec ## x() { return (void *)(ICompressFilter *)(new C ## x ## _Decoder); } \ static void *CreateCodec ## x ## Out() { return (void *)(ICompressFilter *)(new C ## x ## _Encoder); } CREATE_CODEC(BC_PPC) CREATE_CODEC(BC_IA64) CREATE_CODEC(BC_ARM) CREATE_CODEC(BC_ARMT) CREATE_CODEC(BC_SPARC) #define METHOD_ITEM(x, id1, id2, name) { CreateCodec ## x, CreateCodec ## x ## Out, 0x03030000 + (id1 * 256) + id2, name, 1, true } static CCodecInfo g_CodecsInfo[] = { METHOD_ITEM(BC_PPC, 0x02, 0x05, L"PPC"), METHOD_ITEM(BC_IA64, 0x04, 1, L"IA64"), METHOD_ITEM(BC_ARM, 0x05, 1, L"ARM"), METHOD_ITEM(BC_ARMT, 0x07, 1, L"ARMT"), METHOD_ITEM(BC_SPARC, 0x08, 0x05, L"SPARC") }; REGISTER_CODECS(Branch) src/libs/7zip/win/CPP/7zip/Compress/ByteSwap.cpp000066400000000000000000000030611325366651500216500ustar00rootroot00000000000000// ByteSwap.cpp #include "StdAfx.h" #include "../../Common/MyCom.h" #include "../ICoder.h" #include "../Common/RegisterCodec.h" class CByteSwap2: public ICompressFilter, public CMyUnknownImp { public: MY_UNKNOWN_IMP STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); }; class CByteSwap4: public ICompressFilter, public CMyUnknownImp { public: MY_UNKNOWN_IMP STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); }; STDMETHODIMP CByteSwap2::Init() { return S_OK; } STDMETHODIMP_(UInt32) CByteSwap2::Filter(Byte *data, UInt32 size) { const UInt32 kStep = 2; UInt32 i; for (i = 0; i + kStep <= size; i += kStep) { Byte b = data[i]; data[i] = data[i + 1]; data[i + 1] = b; } return i; } STDMETHODIMP CByteSwap4::Init() { return S_OK; } STDMETHODIMP_(UInt32) CByteSwap4::Filter(Byte *data, UInt32 size) { const UInt32 kStep = 4; UInt32 i; for (i = 0; i + kStep <= size; i += kStep) { Byte b0 = data[i]; Byte b1 = data[i + 1]; data[i] = data[i + 3]; data[i + 1] = data[i + 2]; data[i + 2] = b1; data[i + 3] = b0; } return i; } static void *CreateCodec2() { return (void *)(ICompressFilter *)(new CByteSwap2); } static void *CreateCodec4() { return (void *)(ICompressFilter *)(new CByteSwap4); } static CCodecInfo g_CodecsInfo[] = { { CreateCodec2, CreateCodec2, 0x020302, L"Swap2", 1, true }, { CreateCodec4, CreateCodec4, 0x020304, L"Swap4", 1, true } }; REGISTER_CODECS(ByteSwap) src/libs/7zip/win/CPP/7zip/Compress/Compress.pri000066400000000000000000000027521325366651500217230ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/Compress/Bcj2Coder.h \ $$7ZIP_BASE/CPP/7zip/Compress/BcjCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/BranchCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/BranchMisc.h \ $$7ZIP_BASE/CPP/7zip/Compress/CopyCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Decoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Encoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaDecoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaEncoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/RangeCoder.h \ $$7ZIP_BASE/CPP/7zip/Compress/RangeCoderBit.h \ $$7ZIP_BASE/CPP/7zip/Compress/StdAfx.h SOURCES += $$7ZIP_BASE/CPP/7zip/Compress/Bcj2Coder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Bcj2Register.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BcjCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BcjRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BranchCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BranchMisc.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/BranchRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/ByteSwap.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/CopyCoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/CopyRegister.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/DeltaFilter.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Decoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Encoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/Lzma2Register.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaDecoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaEncoder.cpp \ $$7ZIP_BASE/CPP/7zip/Compress/LzmaRegister.cpp src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.cpp000066400000000000000000000034731325366651500220100ustar00rootroot00000000000000// Compress/CopyCoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/StreamUtils.h" #include "CopyCoder.h" namespace NCompress { static const UInt32 kBufferSize = 1 << 17; CCopyCoder::~CCopyCoder() { ::MidFree(_buffer); } STDMETHODIMP CCopyCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (!_buffer) { _buffer = (Byte *)::MidAlloc(kBufferSize); if (!_buffer) return E_OUTOFMEMORY; } TotalSize = 0; for (;;) { UInt32 size = kBufferSize; if (outSize && size > *outSize - TotalSize) size = (UInt32)(*outSize - TotalSize); RINOK(inStream->Read(_buffer, size, &size)); if (size == 0) break; if (outStream) { RINOK(WriteStream(outStream, _buffer, size)); } TotalSize += size; if (progress) { RINOK(progress->SetRatioInfo(&TotalSize, &TotalSize)); } } return S_OK; } STDMETHODIMP CCopyCoder::GetInStreamProcessedSize(UInt64 *value) { *value = TotalSize; return S_OK; } HRESULT CopyStream(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { CMyComPtr copyCoder = new CCopyCoder; return copyCoder->Code(inStream, outStream, NULL, NULL, progress); } HRESULT CopyStream_ExactSize(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt64 size, ICompressProgressInfo *progress) { NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr copyCoder = copyCoderSpec; RINOK(copyCoder->Code(inStream, outStream, NULL, &size, progress)); return copyCoderSpec->TotalSize == size ? S_OK : E_FAIL; } } src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.h000066400000000000000000000017141325366651500214510ustar00rootroot00000000000000// Compress/CopyCoder.h #ifndef __COMPRESS_COPY_CODER_H #define __COMPRESS_COPY_CODER_H #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { class CCopyCoder: public ICompressCoder, public ICompressGetInStreamProcessedSize, public CMyUnknownImp { Byte *_buffer; public: UInt64 TotalSize; CCopyCoder(): TotalSize(0), _buffer(0) {}; ~CCopyCoder(); MY_UNKNOWN_IMP1(ICompressGetInStreamProcessedSize) STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(GetInStreamProcessedSize)(UInt64 *value); }; HRESULT CopyStream(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress); HRESULT CopyStream_ExactSize(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt64 size, ICompressProgressInfo *progress); } #endif src/libs/7zip/win/CPP/7zip/Compress/CopyRegister.cpp000066400000000000000000000005011325366651500225250ustar00rootroot00000000000000// CopyRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "CopyCoder.h" static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::CCopyCoder); } static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodec, 0x00, L"Copy", 1, false }; REGISTER_CODEC(Copy) src/libs/7zip/win/CPP/7zip/Compress/DeltaFilter.cpp000066400000000000000000000061071325366651500223150ustar00rootroot00000000000000// DeltaFilter.cpp #include "StdAfx.h" #include "../../../C/Delta.h" #include "../Common/RegisterCodec.h" #include "BranchCoder.h" struct CDelta { unsigned _delta; Byte _state[DELTA_STATE_SIZE]; CDelta(): _delta(1) {} void DeltaInit() { Delta_Init(_state); } }; class CDeltaEncoder: public ICompressFilter, public ICompressSetCoderProperties, public ICompressWriteCoderProperties, CDelta, public CMyUnknownImp { public: MY_UNKNOWN_IMP2(ICompressSetCoderProperties, ICompressWriteCoderProperties) STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); }; class CDeltaDecoder: public ICompressFilter, public ICompressSetDecoderProperties2, CDelta, public CMyUnknownImp { public: MY_UNKNOWN_IMP1(ICompressSetDecoderProperties2) STDMETHOD(Init)(); STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size); STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); }; STDMETHODIMP CDeltaEncoder::Init() { DeltaInit(); return S_OK; } STDMETHODIMP_(UInt32) CDeltaEncoder::Filter(Byte *data, UInt32 size) { Delta_Encode(_state, _delta, data, size); return size; } STDMETHODIMP CDeltaEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps) { UInt32 delta = _delta; for (UInt32 i = 0; i < numProps; i++) { const PROPVARIANT &prop = props[i]; PROPID propID = propIDs[i]; if (propID >= NCoderPropID::kReduceSize) continue; if (prop.vt != VT_UI4) return E_INVALIDARG; switch (propID) { case NCoderPropID::kDefaultProp: delta = (UInt32)prop.ulVal; if (delta < 1 || delta > 256) return E_INVALIDARG; break; case NCoderPropID::kNumThreads: break; case NCoderPropID::kLevel: break; default: return E_INVALIDARG; } } _delta = delta; return S_OK; } STDMETHODIMP CDeltaEncoder::WriteCoderProperties(ISequentialOutStream *outStream) { Byte prop = (Byte)(_delta - 1); return outStream->Write(&prop, 1, NULL); } STDMETHODIMP CDeltaDecoder::Init() { DeltaInit(); return S_OK; } STDMETHODIMP_(UInt32) CDeltaDecoder::Filter(Byte *data, UInt32 size) { Delta_Decode(_state, _delta, data, size); return size; } STDMETHODIMP CDeltaDecoder::SetDecoderProperties2(const Byte *props, UInt32 size) { if (size != 1) return E_INVALIDARG; _delta = (unsigned)props[0] + 1; return S_OK; } #define CREATE_CODEC(x) \ static void *CreateCodec ## x() { return (void *)(ICompressFilter *)(new C ## x ## Decoder); } \ static void *CreateCodec ## x ## Out() { return (void *)(ICompressFilter *)(new C ## x ## Encoder); } CREATE_CODEC(Delta) #define METHOD_ITEM(x, id, name) { CreateCodec ## x, CreateCodec ## x ## Out, id, name, 1, true } static CCodecInfo g_CodecsInfo[] = { METHOD_ITEM(Delta, 3, L"Delta") }; REGISTER_CODECS(Delta) src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.cpp000066400000000000000000000120731325366651500223700ustar00rootroot00000000000000// Lzma2Decoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/StreamUtils.h" #include "Lzma2Decoder.h" static HRESULT SResToHRESULT(SRes res) { switch(res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PARAM: return E_INVALIDARG; // case SZ_ERROR_PROGRESS: return E_ABORT; case SZ_ERROR_DATA: return S_FALSE; } return E_FAIL; } namespace NCompress { namespace NLzma2 { static const UInt32 kInBufSize = 1 << 20; CDecoder::CDecoder(): _inBuf(0), _outSizeDefined(false) { Lzma2Dec_Construct(&_state); } static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } static void SzFree(void *p, void *address) { p = p; MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CDecoder::~CDecoder() { Lzma2Dec_Free(&_state, &g_Alloc); MyFree(_inBuf); } STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size) { if (size != 1) return SZ_ERROR_UNSUPPORTED; RINOK(SResToHRESULT(Lzma2Dec_Allocate(&_state, prop[0], &g_Alloc))); if (_inBuf == 0) { _inBuf = (Byte *)MyAlloc(kInBufSize); if (_inBuf == 0) return E_OUTOFMEMORY; } return S_OK; } STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; } STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; } STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; } STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) { _outSizeDefined = (outSize != NULL); if (_outSizeDefined) _outSize = *outSize; Lzma2Dec_Init(&_state); _inPos = _inSize = 0; _inSizeProcessed = _outSizeProcessed = 0; return S_OK; } STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (_inBuf == 0) return S_FALSE; SetOutStreamSize(outSize); for (;;) { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(inStream->Read(_inBuf, kInBufSize, &_inSize)); } SizeT dicPos = _state.decoder.dicPos; SizeT curSize = _state.decoder.dicBufSize - dicPos; const UInt32 kStepSize = ((UInt32)1 << 22); if (curSize > kStepSize) curSize = (SizeT)kStepSize; ELzmaFinishMode finishMode = LZMA_FINISH_ANY; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem < curSize) { curSize = (SizeT)rem; /* // finishMode = LZMA_FINISH_END; we can't use LZMA_FINISH_END here to allow partial decoding */ } } SizeT inSizeProcessed = _inSize - _inPos; ELzmaStatus status; SRes res = Lzma2Dec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status); _inPos += (UInt32)inSizeProcessed; _inSizeProcessed += inSizeProcessed; SizeT outSizeProcessed = _state.decoder.dicPos - dicPos; _outSizeProcessed += outSizeProcessed; bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0); bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize); if (res != 0 || _state.decoder.dicPos == _state.decoder.dicBufSize || finished || stopDecoding) { HRESULT res2 = WriteStream(outStream, _state.decoder.dic, _state.decoder.dicPos); if (res != 0) return S_FALSE; RINOK(res2); if (stopDecoding) return S_OK; if (finished) return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE); } if (_state.decoder.dicPos == _state.decoder.dicBufSize) _state.decoder.dicPos = 0; if (progress != NULL) { RINOK(progress->SetRatioInfo(&_inSizeProcessed, &_outSizeProcessed)); } } } #ifndef NO_READ_FROM_CODER STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; do { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(_inStream->Read(_inBuf, kInBufSize, &_inSize)); } { SizeT inProcessed = _inSize - _inPos; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem < size) size = (UInt32)rem; } SizeT outProcessed = size; ELzmaStatus status; SRes res = Lzma2Dec_DecodeToBuf(&_state, (Byte *)data, &outProcessed, _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status); _inPos += (UInt32)inProcessed; _inSizeProcessed += inProcessed; _outSizeProcessed += outProcessed; size -= (UInt32)outProcessed; data = (Byte *)data + outProcessed; if (processedSize) *processedSize += (UInt32)outProcessed; RINOK(SResToHRESULT(res)); if (inProcessed == 0 && outProcessed == 0) return S_OK; } } while (size != 0); return S_OK; } #endif }} src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.h000066400000000000000000000032661325366651500220410ustar00rootroot00000000000000// Lzma2Decoder.h #ifndef __LZMA2_DECODER_H #define __LZMA2_DECODER_H #include "../../../C/Lzma2Dec.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma2 { class CDecoder: public ICompressCoder, public ICompressSetDecoderProperties2, public ICompressGetInStreamProcessedSize, #ifndef NO_READ_FROM_CODER public ICompressSetInStream, public ICompressSetOutStreamSize, public ISequentialInStream, #endif public CMyUnknownImp { CMyComPtr _inStream; Byte *_inBuf; UInt32 _inPos; UInt32 _inSize; CLzma2Dec _state; bool _outSizeDefined; UInt64 _outSize; UInt64 _inSizeProcessed; UInt64 _outSizeProcessed; public: #ifndef NO_READ_FROM_CODER MY_UNKNOWN_IMP5( ICompressSetDecoderProperties2, ICompressGetInStreamProcessedSize, ICompressSetInStream, ICompressSetOutStreamSize, ISequentialInStream) #else MY_UNKNOWN_IMP2( ICompressSetDecoderProperties2, ICompressGetInStreamProcessedSize) #endif STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *_inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); STDMETHOD(GetInStreamProcessedSize)(UInt64 *value); STDMETHOD(SetInStream)(ISequentialInStream *inStream); STDMETHOD(ReleaseInStream)(); STDMETHOD(SetOutStreamSize)(const UInt64 *outSize); #ifndef NO_READ_FROM_CODER STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); #endif CDecoder(); virtual ~CDecoder(); }; }} #endif src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.cpp000066400000000000000000000052211325366651500223770ustar00rootroot00000000000000// Lzma2Encoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/CWrappers.h" #include "../Common/StreamUtils.h" #include "Lzma2Encoder.h" namespace NCompress { namespace NLzma { HRESULT SetLzmaProp(PROPID propID, const PROPVARIANT &prop, CLzmaEncProps &ep); } namespace NLzma2 { static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); } static void SzBigFree(void *, void *address) { BigFree(address); } static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } static void SzFree(void *, void *address) { MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CEncoder::CEncoder() { _encoder = 0; _encoder = Lzma2Enc_Create(&g_Alloc, &g_BigAlloc); if (_encoder == 0) throw 1; } CEncoder::~CEncoder() { if (_encoder != 0) Lzma2Enc_Destroy(_encoder); } HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props) { switch (propID) { case NCoderPropID::kBlockSize: if (prop.vt != VT_UI4) return E_INVALIDARG; lzma2Props.blockSize = prop.ulVal; break; case NCoderPropID::kNumThreads: if (prop.vt != VT_UI4) return E_INVALIDARG; lzma2Props.numTotalThreads = (int)(prop.ulVal); break; default: RINOK(NLzma::SetLzmaProp(propID, prop, lzma2Props.lzmaProps)); } return S_OK; } STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps) { CLzma2EncProps lzma2Props; Lzma2EncProps_Init(&lzma2Props); for (UInt32 i = 0; i < numProps; i++) { RINOK(SetLzma2Prop(propIDs[i], coderProps[i], lzma2Props)); } return SResToHRESULT(Lzma2Enc_SetProps(_encoder, &lzma2Props)); } STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream) { Byte prop = Lzma2Enc_WriteProperties(_encoder); return WriteStream(outStream, &prop, 1); } STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) { CSeqInStreamWrap inWrap(inStream); CSeqOutStreamWrap outWrap(outStream); CCompressProgressWrap progressWrap(progress); SRes res = Lzma2Enc_Encode(_encoder, &outWrap.p, &inWrap.p, progress ? &progressWrap.p : NULL); if (res == SZ_ERROR_READ && inWrap.Res != S_OK) return inWrap.Res; if (res == SZ_ERROR_WRITE && outWrap.Res != S_OK) return outWrap.Res; if (res == SZ_ERROR_PROGRESS && progressWrap.Res != S_OK) return progressWrap.Res; return SResToHRESULT(res); } }} src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.h000066400000000000000000000015671325366651500220550ustar00rootroot00000000000000// Lzma2Encoder.h #ifndef __LZMA2_ENCODER_H #define __LZMA2_ENCODER_H #include "../../../C/Lzma2Enc.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma2 { class CEncoder: public ICompressCoder, public ICompressSetCoderProperties, public ICompressWriteCoderProperties, public CMyUnknownImp { CLzma2EncHandle _encoder; public: MY_UNKNOWN_IMP2(ICompressSetCoderProperties, ICompressWriteCoderProperties) STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); CEncoder(); virtual ~CEncoder(); }; }} #endif src/libs/7zip/win/CPP/7zip/Compress/Lzma2Register.cpp000066400000000000000000000010241325366651500226010ustar00rootroot00000000000000// Lzma2Register.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "Lzma2Decoder.h" static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::NLzma2::CDecoder); } #ifndef EXTRACT_ONLY #include "Lzma2Encoder.h" static void *CreateCodecOut() { return (void *)(ICompressCoder *)(new NCompress::NLzma2::CEncoder); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x21, L"LZMA2", 1, false }; REGISTER_CODEC(LZMA2) src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.cpp000066400000000000000000000164021325366651500223060ustar00rootroot00000000000000// LzmaDecoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/StreamUtils.h" #include "LzmaDecoder.h" static HRESULT SResToHRESULT(SRes res) { switch(res) { case SZ_OK: return S_OK; case SZ_ERROR_MEM: return E_OUTOFMEMORY; case SZ_ERROR_PARAM: return E_INVALIDARG; case SZ_ERROR_UNSUPPORTED: return E_NOTIMPL; case SZ_ERROR_DATA: return S_FALSE; } return E_FAIL; } namespace NCompress { namespace NLzma { CDecoder::CDecoder(): _inBuf(0), _propsWereSet(false), _outSizeDefined(false), _inBufSize(1 << 20), _outBufSize(1 << 22), FinishStream(false), NeedMoreInput(false) { _inSizeProcessed = 0; _inPos = _inSize = 0; LzmaDec_Construct(&_state); } static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } static void SzFree(void *p, void *address) { p = p; MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CDecoder::~CDecoder() { LzmaDec_Free(&_state, &g_Alloc); MyFree(_inBuf); } STDMETHODIMP CDecoder::SetInBufSize(UInt32 , UInt32 size) { _inBufSize = size; return S_OK; } STDMETHODIMP CDecoder::SetOutBufSize(UInt32 , UInt32 size) { _outBufSize = size; return S_OK; } HRESULT CDecoder::CreateInputBuffer() { if (_inBuf == 0 || _inBufSize != _inBufSizeAllocated) { MyFree(_inBuf); _inBuf = (Byte *)MyAlloc(_inBufSize); if (_inBuf == 0) return E_OUTOFMEMORY; _inBufSizeAllocated = _inBufSize; } return S_OK; } STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size) { RINOK(SResToHRESULT(LzmaDec_Allocate(&_state, prop, size, &g_Alloc))); _propsWereSet = true; return CreateInputBuffer(); } void CDecoder::SetOutStreamSizeResume(const UInt64 *outSize) { _outSizeDefined = (outSize != NULL); if (_outSizeDefined) _outSize = *outSize; _outSizeProcessed = 0; _wrPos = 0; LzmaDec_Init(&_state); } STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize) { _inSizeProcessed = 0; _inPos = _inSize = 0; NeedMoreInput = false; SetOutStreamSizeResume(outSize); return S_OK; } HRESULT CDecoder::CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress) { if (_inBuf == 0 || !_propsWereSet) return S_FALSE; UInt64 startInProgress = _inSizeProcessed; SizeT next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize); for (;;) { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize)); } SizeT dicPos = _state.dicPos; SizeT curSize = next - dicPos; ELzmaFinishMode finishMode = LZMA_FINISH_ANY; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem <= curSize) { curSize = (SizeT)rem; if (FinishStream) finishMode = LZMA_FINISH_END; } } SizeT inSizeProcessed = _inSize - _inPos; ELzmaStatus status; SRes res = LzmaDec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status); _inPos += (UInt32)inSizeProcessed; _inSizeProcessed += inSizeProcessed; SizeT outSizeProcessed = _state.dicPos - dicPos; _outSizeProcessed += outSizeProcessed; bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0); bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize); if (res != 0 || _state.dicPos == next || finished || stopDecoding) { HRESULT res2 = WriteStream(outStream, _state.dic + _wrPos, _state.dicPos - _wrPos); _wrPos = _state.dicPos; if (_state.dicPos == _state.dicBufSize) { _state.dicPos = 0; _wrPos = 0; } next = (_state.dicBufSize - _state.dicPos < _outBufSize) ? _state.dicBufSize : (_state.dicPos + _outBufSize); if (res != 0) return S_FALSE; RINOK(res2); if (stopDecoding) { if (status == LZMA_STATUS_NEEDS_MORE_INPUT) NeedMoreInput = true; if (FinishStream && status != LZMA_STATUS_FINISHED_WITH_MARK && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) return S_FALSE; return S_OK; } if (finished) { if (status == LZMA_STATUS_NEEDS_MORE_INPUT) NeedMoreInput = true; return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE); } } if (progress) { UInt64 inSize = _inSizeProcessed - startInProgress; RINOK(progress->SetRatioInfo(&inSize, &_outSizeProcessed)); } } } STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) { if (_inBuf == 0) return E_INVALIDARG; SetOutStreamSize(outSize); return CodeSpec(inStream, outStream, progress); } #ifndef NO_READ_FROM_CODER STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; } STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; } STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; do { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize)); } { SizeT inProcessed = _inSize - _inPos; if (_outSizeDefined) { const UInt64 rem = _outSize - _outSizeProcessed; if (rem < size) size = (UInt32)rem; } SizeT outProcessed = size; ELzmaStatus status; SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed, _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status); _inPos += (UInt32)inProcessed; _inSizeProcessed += inProcessed; _outSizeProcessed += outProcessed; size -= (UInt32)outProcessed; data = (Byte *)data + outProcessed; if (processedSize) *processedSize += (UInt32)outProcessed; RINOK(SResToHRESULT(res)); if (inProcessed == 0 && outProcessed == 0) return S_OK; } } while (size != 0); return S_OK; } HRESULT CDecoder::CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress) { SetOutStreamSizeResume(outSize); return CodeSpec(_inStream, outStream, progress); } HRESULT CDecoder::ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize) { RINOK(CreateInputBuffer()); if (processedSize) *processedSize = 0; while (size > 0) { if (_inPos == _inSize) { _inPos = _inSize = 0; RINOK(_inStream->Read(_inBuf, _inBufSizeAllocated, &_inSize)); if (_inSize == 0) break; } { UInt32 curSize = _inSize - _inPos; if (curSize > size) curSize = size; memcpy(data, _inBuf + _inPos, curSize); _inPos += curSize; _inSizeProcessed += curSize; size -= curSize; data = (Byte *)data + curSize; if (processedSize) *processedSize += curSize; } } return S_OK; } #endif }} src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.h000066400000000000000000000051231325366651500217510ustar00rootroot00000000000000// LzmaDecoder.h #ifndef __LZMA_DECODER_H #define __LZMA_DECODER_H #include "../../../C/LzmaDec.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma { class CDecoder: public ICompressCoder, public ICompressSetDecoderProperties2, public ICompressSetBufSize, #ifndef NO_READ_FROM_CODER public ICompressSetInStream, public ICompressSetOutStreamSize, public ISequentialInStream, #endif public CMyUnknownImp { CMyComPtr _inStream; Byte *_inBuf; UInt32 _inPos; UInt32 _inSize; CLzmaDec _state; bool _propsWereSet; bool _outSizeDefined; UInt64 _outSize; UInt64 _inSizeProcessed; UInt64 _outSizeProcessed; UInt32 _inBufSizeAllocated; UInt32 _inBufSize; UInt32 _outBufSize; SizeT _wrPos; HRESULT CreateInputBuffer(); HRESULT CodeSpec(ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress); void SetOutStreamSizeResume(const UInt64 *outSize); public: MY_QUERYINTERFACE_BEGIN2(ICompressCoder) MY_QUERYINTERFACE_ENTRY(ICompressSetDecoderProperties2) MY_QUERYINTERFACE_ENTRY(ICompressSetBufSize) #ifndef NO_READ_FROM_CODER MY_QUERYINTERFACE_ENTRY(ICompressSetInStream) MY_QUERYINTERFACE_ENTRY(ICompressSetOutStreamSize) MY_QUERYINTERFACE_ENTRY(ISequentialInStream) #endif MY_QUERYINTERFACE_END MY_ADDREF_RELEASE STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size); STDMETHOD(SetOutStreamSize)(const UInt64 *outSize); STDMETHOD(SetInBufSize)(UInt32 streamIndex, UInt32 size); STDMETHOD(SetOutBufSize)(UInt32 streamIndex, UInt32 size); #ifndef NO_READ_FROM_CODER STDMETHOD(SetInStream)(ISequentialInStream *inStream); STDMETHOD(ReleaseInStream)(); STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); HRESULT CodeResume(ISequentialOutStream *outStream, const UInt64 *outSize, ICompressProgressInfo *progress); HRESULT ReadFromInputStream(void *data, UInt32 size, UInt32 *processedSize); UInt64 GetInputProcessedSize() const { return _inSizeProcessed; } #endif bool FinishStream; // set it before decoding, if you need to decode full LZMA stream bool NeedMoreInput; // it's set by decoder, if it needs more input data to decode stream CDecoder(); virtual ~CDecoder(); UInt64 GetOutputProcessedSize() const { return _outSizeProcessed; } }; }} #endif src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.cpp000066400000000000000000000107001325366651500223130ustar00rootroot00000000000000// LzmaEncoder.cpp #include "StdAfx.h" #include "../../../C/Alloc.h" #include "../Common/CWrappers.h" #include "../Common/StreamUtils.h" #include "LzmaEncoder.h" namespace NCompress { namespace NLzma { static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); } static void SzBigFree(void *, void *address) { BigFree(address); } static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } static void SzFree(void *, void *address) { MyFree(address); } static ISzAlloc g_Alloc = { SzAlloc, SzFree }; CEncoder::CEncoder() { _encoder = 0; _encoder = LzmaEnc_Create(&g_Alloc); if (_encoder == 0) throw 1; } CEncoder::~CEncoder() { if (_encoder != 0) LzmaEnc_Destroy(_encoder, &g_Alloc, &g_BigAlloc); } inline wchar_t GetUpperChar(wchar_t c) { if (c >= 'a' && c <= 'z') c -= 0x20; return c; } static int ParseMatchFinder(const wchar_t *s, int *btMode, int *numHashBytes) { wchar_t c = GetUpperChar(*s++); if (c == L'H') { if (GetUpperChar(*s++) != L'C') return 0; int numHashBytesLoc = (int)(*s++ - L'0'); if (numHashBytesLoc < 4 || numHashBytesLoc > 4) return 0; if (*s++ != 0) return 0; *btMode = 0; *numHashBytes = numHashBytesLoc; return 1; } if (c != L'B') return 0; if (GetUpperChar(*s++) != L'T') return 0; int numHashBytesLoc = (int)(*s++ - L'0'); if (numHashBytesLoc < 2 || numHashBytesLoc > 4) return 0; c = GetUpperChar(*s++); if (c != L'\0') return 0; *btMode = 1; *numHashBytes = numHashBytesLoc; return 1; } #define SET_PROP_32(_id_, _dest_) case NCoderPropID::_id_: ep._dest_ = v; break; HRESULT SetLzmaProp(PROPID propID, const PROPVARIANT &prop, CLzmaEncProps &ep) { if (propID == NCoderPropID::kMatchFinder) { if (prop.vt != VT_BSTR) return E_INVALIDARG; return ParseMatchFinder(prop.bstrVal, &ep.btMode, &ep.numHashBytes) ? S_OK : E_INVALIDARG; } if (propID > NCoderPropID::kReduceSize) return S_OK; if (propID == NCoderPropID::kReduceSize) { if (prop.vt == VT_UI8) ep.reduceSize = prop.uhVal.QuadPart; return S_OK; } if (prop.vt != VT_UI4) return E_INVALIDARG; UInt32 v = prop.ulVal; switch (propID) { case NCoderPropID::kDefaultProp: if (v > 31) return E_INVALIDARG; ep.dictSize = (UInt32)1 << (unsigned)v; break; SET_PROP_32(kLevel, level) SET_PROP_32(kNumFastBytes, fb) SET_PROP_32(kMatchFinderCycles, mc) SET_PROP_32(kAlgorithm, algo) SET_PROP_32(kDictionarySize, dictSize) SET_PROP_32(kPosStateBits, pb) SET_PROP_32(kLitPosBits, lp) SET_PROP_32(kLitContextBits, lc) SET_PROP_32(kNumThreads, numThreads) default: return E_INVALIDARG; } return S_OK; } STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps) { CLzmaEncProps props; LzmaEncProps_Init(&props); for (UInt32 i = 0; i < numProps; i++) { const PROPVARIANT &prop = coderProps[i]; PROPID propID = propIDs[i]; switch (propID) { case NCoderPropID::kEndMarker: if (prop.vt != VT_BOOL) return E_INVALIDARG; props.writeEndMark = (prop.boolVal != VARIANT_FALSE); break; default: RINOK(SetLzmaProp(propID, prop, props)); } } return SResToHRESULT(LzmaEnc_SetProps(_encoder, &props)); } STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream) { Byte props[LZMA_PROPS_SIZE]; size_t size = LZMA_PROPS_SIZE; RINOK(LzmaEnc_WriteProperties(_encoder, props, &size)); return WriteStream(outStream, props, size); } STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress) { CSeqInStreamWrap inWrap(inStream); CSeqOutStreamWrap outWrap(outStream); CCompressProgressWrap progressWrap(progress); SRes res = LzmaEnc_Encode(_encoder, &outWrap.p, &inWrap.p, progress ? &progressWrap.p : NULL, &g_Alloc, &g_BigAlloc); _inputProcessed = inWrap.Processed; if (res == SZ_ERROR_READ && inWrap.Res != S_OK) return inWrap.Res; if (res == SZ_ERROR_WRITE && outWrap.Res != S_OK) return outWrap.Res; if (res == SZ_ERROR_PROGRESS && progressWrap.Res != S_OK) return progressWrap.Res; return SResToHRESULT(res); } }} src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.h000066400000000000000000000017201325366651500217620ustar00rootroot00000000000000// LzmaEncoder.h #ifndef __LZMA_ENCODER_H #define __LZMA_ENCODER_H #include "../../../C/LzmaEnc.h" #include "../../Common/MyCom.h" #include "../ICoder.h" namespace NCompress { namespace NLzma { class CEncoder: public ICompressCoder, public ICompressSetCoderProperties, public ICompressWriteCoderProperties, public CMyUnknownImp { CLzmaEncHandle _encoder; UInt64 _inputProcessed; public: MY_UNKNOWN_IMP2(ICompressSetCoderProperties, ICompressWriteCoderProperties) STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream); CEncoder(); virtual ~CEncoder(); UInt64 GetInputProcessedSize() const { return _inputProcessed; } }; }} #endif src/libs/7zip/win/CPP/7zip/Compress/LzmaRegister.cpp000066400000000000000000000010211325366651500225140ustar00rootroot00000000000000// LzmaRegister.cpp #include "StdAfx.h" #include "../Common/RegisterCodec.h" #include "LzmaDecoder.h" static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::NLzma::CDecoder); } #ifndef EXTRACT_ONLY #include "LzmaEncoder.h" static void *CreateCodecOut() { return (void *)(ICompressCoder *)(new NCompress::NLzma::CEncoder); } #else #define CreateCodecOut 0 #endif static CCodecInfo g_CodecInfo = { CreateCodec, CreateCodecOut, 0x030101, L"LZMA", 1, false }; REGISTER_CODEC(LZMA) src/libs/7zip/win/CPP/7zip/Compress/RangeCoder.h000066400000000000000000000076751325366651500216070ustar00rootroot00000000000000// Compress/RangeCoder.h // 2013-01-10 : Igor Pavlov : Public domain #ifndef __COMPRESS_RANGE_CODER_H #define __COMPRESS_RANGE_CODER_H #include "../Common/InBuffer.h" #include "../Common/OutBuffer.h" namespace NCompress { namespace NRangeCoder { const unsigned kNumTopBits = 24; const UInt32 kTopValue = (1 << kNumTopBits); class CEncoder { UInt32 _cacheSize; Byte _cache; public: UInt64 Low; UInt32 Range; COutBuffer Stream; bool Create(UInt32 bufSize) { return Stream.Create(bufSize); } void SetStream(ISequentialOutStream *stream) { Stream.SetStream(stream); } void Init() { Stream.Init(); Low = 0; Range = 0xFFFFFFFF; _cacheSize = 1; _cache = 0; } void FlushData() { // Low += 1; for (int i = 0; i < 5; i++) ShiftLow(); } HRESULT FlushStream() { return Stream.Flush(); } void Encode(UInt32 start, UInt32 size, UInt32 total) { Low += start * (Range /= total); Range *= size; while (Range < kTopValue) { Range <<= 8; ShiftLow(); } } void ShiftLow() { if ((UInt32)Low < (UInt32)0xFF000000 || (unsigned)(Low >> 32) != 0) { Byte temp = _cache; do { Stream.WriteByte((Byte)(temp + (Byte)(Low >> 32))); temp = 0xFF; } while (--_cacheSize != 0); _cache = (Byte)((UInt32)Low >> 24); } _cacheSize++; Low = (UInt32)Low << 8; } void EncodeDirectBits(UInt32 value, int numBits) { for (numBits--; numBits >= 0; numBits--) { Range >>= 1; Low += Range & (0 - ((value >> numBits) & 1)); if (Range < kTopValue) { Range <<= 8; ShiftLow(); } } } void EncodeBit(UInt32 size0, UInt32 numTotalBits, UInt32 symbol) { UInt32 newBound = (Range >> numTotalBits) * size0; if (symbol == 0) Range = newBound; else { Low += newBound; Range -= newBound; } while (Range < kTopValue) { Range <<= 8; ShiftLow(); } } UInt64 GetProcessedSize() { return Stream.GetProcessedSize() + _cacheSize + 4; } }; class CDecoder { public: CInBuffer Stream; UInt32 Range; UInt32 Code; bool Create(UInt32 bufSize) { return Stream.Create(bufSize); } void Normalize() { while (Range < kTopValue) { Code = (Code << 8) | Stream.ReadByte(); Range <<= 8; } } void SetStream(ISequentialInStream *stream) { Stream.SetStream(stream); } void Init() { Stream.Init(); Code = 0; Range = 0xFFFFFFFF; for (int i = 0; i < 5; i++) Code = (Code << 8) | Stream.ReadByte(); } UInt32 GetThreshold(UInt32 total) { return (Code) / (Range /= total); } void Decode(UInt32 start, UInt32 size) { Code -= start * Range; Range *= size; Normalize(); } UInt32 DecodeDirectBits(int numTotalBits) { UInt32 range = Range; UInt32 code = Code; UInt32 result = 0; for (int i = numTotalBits; i != 0; i--) { range >>= 1; /* result <<= 1; if (code >= range) { code -= range; result |= 1; } */ UInt32 t = (code - range) >> 31; code -= range & (t - 1); result = (result << 1) | (1 - t); if (range < kTopValue) { code = (code << 8) | Stream.ReadByte(); range <<= 8; } } Range = range; Code = code; return result; } UInt32 DecodeBit(UInt32 size0, UInt32 numTotalBits) { UInt32 newBound = (Range >> numTotalBits) * size0; UInt32 symbol; if (Code < newBound) { symbol = 0; Range = newBound; } else { symbol = 1; Code -= newBound; Range -= newBound; } Normalize(); return symbol; } UInt64 GetProcessedSize() { return Stream.GetProcessedSize(); } }; }} #endif src/libs/7zip/win/CPP/7zip/Compress/RangeCoderBit.h000066400000000000000000000057161325366651500222400ustar00rootroot00000000000000// Compress/RangeCoderBit.h // 2013-01-10 : Igor Pavlov : Public domain #ifndef __COMPRESS_RANGE_CODER_BIT_H #define __COMPRESS_RANGE_CODER_BIT_H #include "RangeCoder.h" namespace NCompress { namespace NRangeCoder { const unsigned kNumBitModelTotalBits = 11; const UInt32 kBitModelTotal = (1 << kNumBitModelTotalBits); const unsigned kNumMoveReducingBits = 4; const unsigned kNumBitPriceShiftBits = 4; const UInt32 kBitPrice = 1 << kNumBitPriceShiftBits; extern UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; template class CBitModel { public: UInt32 Prob; void UpdateModel(UInt32 symbol) { /* Prob -= (Prob + ((symbol - 1) & ((1 << numMoveBits) - 1))) >> numMoveBits; Prob += (1 - symbol) << (kNumBitModelTotalBits - numMoveBits); */ if (symbol == 0) Prob += (kBitModelTotal - Prob) >> numMoveBits; else Prob -= (Prob) >> numMoveBits; } public: void Init() { Prob = kBitModelTotal / 2; } }; template class CBitEncoder: public CBitModel { public: void Encode(CEncoder *encoder, UInt32 symbol) { /* encoder->EncodeBit(this->Prob, kNumBitModelTotalBits, symbol); this->UpdateModel(symbol); */ UInt32 newBound = (encoder->Range >> kNumBitModelTotalBits) * this->Prob; if (symbol == 0) { encoder->Range = newBound; this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits; } else { encoder->Low += newBound; encoder->Range -= newBound; this->Prob -= (this->Prob) >> numMoveBits; } if (encoder->Range < kTopValue) { encoder->Range <<= 8; encoder->ShiftLow(); } } UInt32 GetPrice(UInt32 symbol) const { return ProbPrices[(this->Prob ^ ((-(int)(Int32)symbol)) & (kBitModelTotal - 1)) >> kNumMoveReducingBits]; } UInt32 GetPrice0() const { return ProbPrices[this->Prob >> kNumMoveReducingBits]; } UInt32 GetPrice1() const { return ProbPrices[(this->Prob ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]; } }; template class CBitDecoder: public CBitModel { public: UInt32 Decode(CDecoder *decoder) { UInt32 newBound = (decoder->Range >> kNumBitModelTotalBits) * this->Prob; if (decoder->Code < newBound) { decoder->Range = newBound; this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits; if (decoder->Range < kTopValue) { decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte(); decoder->Range <<= 8; } return 0; } else { decoder->Range -= newBound; decoder->Code -= newBound; this->Prob -= (this->Prob) >> numMoveBits; if (decoder->Range < kTopValue) { decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte(); decoder->Range <<= 8; } return 1; } } }; }} #endif src/libs/7zip/win/CPP/7zip/Compress/StdAfx.h000066400000000000000000000001451325366651500207500ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../../Common/Common.h" #endif src/libs/7zip/win/CPP/7zip/Guid.txt000066400000000000000000000071121325366651500172450ustar00rootroot00000000000000{23170F69-40C1-278A-0000-00yy00xx0000} 00 IProgress.h 05 IProgress 01 IFolderArchive.h // 05 IArchiveFolder // old // 06 IInFolderArchive // old 07 IFileExtractCallback.h::IFolderArchiveExtractCallback // 0A IOutFolderArchive 0B IFolderArchiveUpdateCallback 0C Agent.h::IArchiveFolderInternal 0D IArchiveFolder 0E IInFolderArchive 0F IOutFolderArchive 20 IFileExtractCallback.h::IGetProp 30 IFileExtractCallback.h::IFolderExtractToStreamCallback 03 IStream.h 01 ISequentialInStream 02 ISequentialOutStream 03 IInStream 04 IOutStream 06 IStreamGetSize 07 IOutStreamFlush 08 IStreamGetProps 09 IStreamGetProps2 04 ICoder.h 04 ICompressProgressInfo 05 ICompressCoder 18 ICompressCoder2 20 ICompressSetCoderProperties 21 ICompressSetDecoderProperties // 22 ICompressSetDecoderProperties2 23 ICompressWriteCoderProperties 24 ICompressGetInStreamProcessedSize 25 ICompressSetCoderMt 30 ICompressGetSubStreamSize 31 ICompressSetInStream 32 ICompressSetOutStream 33 ICompressSetInStreamSize 34 ICompressSetOutStreamSize 35 ICompressSetBufSize 40 ICompressFilter 60 ICompressCodecsInfo 61 ISetCompressCodecsInfo 80 ICryptoProperties 88 ICryptoResetSalt 8C ICryptoResetInitVector 90 ICryptoSetPassword A0 ICryptoSetCRC C0 IHasher C1 IHashers 05 IPassword.h 10 ICryptoGetTextPassword 11 ICryptoGetTextPassword2 06 IArchive.h 03 ISetProperties 04 IArchiveKeepModeForNextOpen 05 IArchiveAllowTail 10 IArchiveOpenCallback 20 IArchiveExtractCallback 30 IArchiveOpenVolumeCallback 40 IInArchiveGetStream 50 IArchiveOpenSetSubArchiveName 60 IInArchive 61 IArchiveOpenSeq 70 IArchiveGetRawProps 71 IArchiveGetRootProps 80 IArchiveUpdateCallback 82 IArchiveUpdateCallback2 A0 IOutArchive 08 IFolder.h 00 IFolderFolder 01 IEnumProperties 02 IFolderGetTypeID 03 IFolderGetPath 04 IFolderWasChanged 05 // IFolderReload 06 // IFolderOperations old 07 IFolderGetSystemIconIndex 08 IFolderGetItemFullSize 09 IFolderClone 0A IFolderSetFlatMode 0B IFolderOperationsExtractCallback 0C // 0D // 0E IFolderProperties 0F 10 IFolderArcProps 11 IGetFolderArcProps 12 // IFolderOperations 13 IFolderOperations 14 IFolderCalcItemFullSize 15 IFolderCompare 16 IFolderGetItemName 09 IFolder.h :: FOLDER_MANAGER_INTERFACE 00 - 04 // old IFolderManager 05 IFolderManager // 0A PluginInterface.h 00 IInitContextMenu 01 IPluginOptionsCallback 02 IPluginOptions Handler GUIDs: {23170F69-40C1-278A-1000-000110xx0000} 01 Zip 02 BZip2 03 Rar 04 Arj 05 Z 06 Lzh 07 7z 08 Cab 09 Nsis 0A lzma 0B lzma86 0C xz 0D ppmd CD IHex CE Hxs CF TE D0 UEFIc D1 UEFIs D2 SquashFS D3 CramFS D4 APM D5 Mslz D6 Flv D7 Swf D8 Swfc D9 Ntfs DA Fat DB Mbr DC Vhd DD Pe DE Elf DF Mach-O E0 Udf E1 Xar E2 Mub E3 Hfs E4 Dmg E5 Compound E6 Wim E7 Iso E8 E9 Chm EA Split EB Rpm EC Deb ED Cpio EE Tar EF GZip {23170F69-40C1-278A-1000-000100030000} CAgentArchiveHandle {23170F69-40C1-278A-1000-000100020000} ContextMenu.h::CZipContextMenu {23170F69-40C1-278B- old codecs clsids {23170F69-40C1-278D-1000-000100020000} OptionsDialog.h::CLSID_CSevenZipOptions {23170F69-40C1-2790-id} Codec Decoders {23170F69-40C1-2791-id} Codec Encoders {23170F69-40C1-2792-id} Hashers src/libs/7zip/win/CPP/7zip/ICoder.h000066400000000000000000000125541325366651500171400ustar00rootroot00000000000000// ICoder.h #ifndef __ICODER_H #define __ICODER_H #include "IStream.h" #define CODER_INTERFACE(i, x) DECL_INTERFACE(i, 4, x) CODER_INTERFACE(ICompressProgressInfo, 0x04) { STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize) PURE; }; CODER_INTERFACE(ICompressCoder, 0x05) { STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress) PURE; }; CODER_INTERFACE(ICompressCoder2, 0x18) { STDMETHOD(Code)(ISequentialInStream **inStreams, const UInt64 **inSizes, UInt32 numInStreams, ISequentialOutStream **outStreams, const UInt64 **outSizes, UInt32 numOutStreams, ICompressProgressInfo *progress) PURE; }; namespace NCoderPropID { enum EEnum { kDefaultProp = 0, kDictionarySize, kUsedMemorySize, kOrder, kBlockSize, kPosStateBits, kLitContextBits, kLitPosBits, kNumFastBytes, kMatchFinder, kMatchFinderCycles, kNumPasses, kAlgorithm, kNumThreads, kEndMarker, kLevel, kReduceSize // estimated size of data that will be compressed. Encoder can use this value to reduce dictionary size. }; } CODER_INTERFACE(ICompressSetCoderProperties, 0x20) { STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps) PURE; }; /* CODER_INTERFACE(ICompressSetCoderProperties, 0x21) { STDMETHOD(SetDecoderProperties)(ISequentialInStream *inStream) PURE; }; */ CODER_INTERFACE(ICompressSetDecoderProperties2, 0x22) { STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size) PURE; }; CODER_INTERFACE(ICompressWriteCoderProperties, 0x23) { STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream) PURE; }; CODER_INTERFACE(ICompressGetInStreamProcessedSize, 0x24) { STDMETHOD(GetInStreamProcessedSize)(UInt64 *value) PURE; }; CODER_INTERFACE(ICompressSetCoderMt, 0x25) { STDMETHOD(SetNumberOfThreads)(UInt32 numThreads) PURE; }; CODER_INTERFACE(ICompressGetSubStreamSize, 0x30) { STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value) PURE; }; CODER_INTERFACE(ICompressSetInStream, 0x31) { STDMETHOD(SetInStream)(ISequentialInStream *inStream) PURE; STDMETHOD(ReleaseInStream)() PURE; }; CODER_INTERFACE(ICompressSetOutStream, 0x32) { STDMETHOD(SetOutStream)(ISequentialOutStream *outStream) PURE; STDMETHOD(ReleaseOutStream)() PURE; }; CODER_INTERFACE(ICompressSetInStreamSize, 0x33) { STDMETHOD(SetInStreamSize)(const UInt64 *inSize) PURE; }; CODER_INTERFACE(ICompressSetOutStreamSize, 0x34) { STDMETHOD(SetOutStreamSize)(const UInt64 *outSize) PURE; }; CODER_INTERFACE(ICompressSetBufSize, 0x35) { STDMETHOD(SetInBufSize)(UInt32 streamIndex, UInt32 size) PURE; STDMETHOD(SetOutBufSize)(UInt32 streamIndex, UInt32 size) PURE; }; CODER_INTERFACE(ICompressFilter, 0x40) { STDMETHOD(Init)() PURE; STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size) PURE; // Filter converts as most as possible bytes // Filter return outSize (UInt32) // if (outSize <= size): Filter have converted outSize bytes // if (outSize > size): Filter have not converted anything. // and it needs at least outSize bytes to convert one block // (it's for crypto block algorithms). }; CODER_INTERFACE(ICompressCodecsInfo, 0x60) { STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods) PURE; STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) PURE; STDMETHOD(CreateDecoder)(UInt32 index, const GUID *iid, void **coder) PURE; STDMETHOD(CreateEncoder)(UInt32 index, const GUID *iid, void **coder) PURE; }; CODER_INTERFACE(ISetCompressCodecsInfo, 0x61) { STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo) PURE; }; CODER_INTERFACE(ICryptoProperties, 0x80) { STDMETHOD(SetKey)(const Byte *data, UInt32 size) PURE; STDMETHOD(SetInitVector)(const Byte *data, UInt32 size) PURE; }; /* CODER_INTERFACE(ICryptoResetSalt, 0x88) { STDMETHOD(ResetSalt)() PURE; }; */ CODER_INTERFACE(ICryptoResetInitVector, 0x8C) { STDMETHOD(ResetInitVector)() PURE; }; CODER_INTERFACE(ICryptoSetPassword, 0x90) { STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size) PURE; }; CODER_INTERFACE(ICryptoSetCRC, 0xA0) { STDMETHOD(CryptoSetCRC)(UInt32 crc) PURE; }; ////////////////////// // It's for DLL file namespace NMethodPropID { enum EEnum { kID, kName, kDecoder, kEncoder, kInStreams, kOutStreams, kDescription, kDecoderIsAssigned, kEncoderIsAssigned, kDigestSize }; } CODER_INTERFACE(IHasher, 0xC0) { STDMETHOD_(void, Init)() PURE; STDMETHOD_(void, Update)(const void *data, UInt32 size) PURE; STDMETHOD_(void, Final)(Byte *digest) PURE; STDMETHOD_(UInt32, GetDigestSize)() PURE; }; CODER_INTERFACE(IHashers, 0xC1) { STDMETHOD_(UInt32, GetNumHashers)() PURE; STDMETHOD(GetHasherProp)(UInt32 index, PROPID propID, PROPVARIANT *value) PURE; STDMETHOD(CreateHasher)(UInt32 index, IHasher **hasher) PURE; }; extern "C" { typedef HRESULT (WINAPI *Func_GetNumberOfMethods)(UInt32 *numMethods); typedef HRESULT (WINAPI *Func_GetMethodProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); typedef HRESULT (WINAPI *Func_GetHashers)(IHashers **hashers); } #endif src/libs/7zip/win/CPP/7zip/IDecl.h000066400000000000000000000005571325366651500167530ustar00rootroot00000000000000// IDecl.h #ifndef __IDECL_H #define __IDECL_H #include "../Common/MyUnknown.h" #define DECL_INTERFACE_SUB(i, base, groupId, subId) \ DEFINE_GUID(IID_ ## i, \ 0x23170F69, 0x40C1, 0x278A, 0, 0, 0, (groupId), 0, (subId), 0, 0); \ struct i: public base #define DECL_INTERFACE(i, groupId, subId) DECL_INTERFACE_SUB(i, IUnknown, groupId, subId) #endif src/libs/7zip/win/CPP/7zip/IPassword.h000066400000000000000000000007461325366651500177060ustar00rootroot00000000000000// IPassword.h #ifndef __IPASSWORD_H #define __IPASSWORD_H #include "../Common/MyTypes.h" #include "../Common/MyUnknown.h" #include "IDecl.h" #define PASSWORD_INTERFACE(i, x) DECL_INTERFACE(i, 5, x) PASSWORD_INTERFACE(ICryptoGetTextPassword, 0x10) { STDMETHOD(CryptoGetTextPassword)(BSTR *password) PURE; }; PASSWORD_INTERFACE(ICryptoGetTextPassword2, 0x11) { STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password) PURE; }; #endif src/libs/7zip/win/CPP/7zip/IProgress.h000066400000000000000000000013651325366651500177060ustar00rootroot00000000000000// Interface/IProgress.h #ifndef __IPROGRESS_H #define __IPROGRESS_H #include "../Common/MyTypes.h" #include "../Common/MyUnknown.h" #include "IDecl.h" #define INTERFACE_IProgress(x) \ STDMETHOD(SetTotal)(UInt64 total) x; \ STDMETHOD(SetCompleted)(const UInt64 *completeValue) x; \ DECL_INTERFACE(IProgress, 0, 5) { INTERFACE_IProgress(PURE) }; /* // {23170F69-40C1-278A-0000-000000050002} DEFINE_GUID(IID_IProgress2, 0x23170F69, 0x40C1, 0x278A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02); MIDL_INTERFACE("23170F69-40C1-278A-0000-000000050002") IProgress2: public IUnknown { public: STDMETHOD(SetTotal)(const UInt64 *total) PURE; STDMETHOD(SetCompleted)(const UInt64 *completeValue) PURE; }; */ #endif src/libs/7zip/win/CPP/7zip/IStream.h000066400000000000000000000073051325366651500173350ustar00rootroot00000000000000// IStream.h #ifndef __ISTREAM_H #define __ISTREAM_H #include "../Common/MyTypes.h" #include "../Common/MyUnknown.h" #include "IDecl.h" #define STREAM_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 3, x) #define STREAM_INTERFACE(i, x) STREAM_INTERFACE_SUB(i, IUnknown, x) STREAM_INTERFACE(ISequentialInStream, 0x01) { STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize) PURE; /* The requirement for caller: (processedSize != NULL). The callee can allow (processedSize == NULL) for compatibility reasons. if (size == 0), this function returns S_OK and (*processedSize) is set to 0. if (size != 0) { Partial read is allowed: (*processedSize <= avail_size && *processedSize <= size), where (avail_size) is the size of remaining bytes in stream. If (avail_size != 0), this function must read at least 1 byte: (*processedSize > 0). You must call Read() in loop, if you need to read exact amount of data. } If seek pointer before Read() call was changed to position past the end of stream: if (seek_pointer >= stream_size), this function returns S_OK and (*processedSize) is set to 0. ERROR CASES: If the function returns error code, then (*processedSize) is size of data written to (data) buffer (it can be data before error or data with errors). The recommended way for callee to work with reading errors: 1) write part of data before error to (data) buffer and return S_OK. 2) return error code for further calls of Read(). */ }; STREAM_INTERFACE(ISequentialOutStream, 0x02) { STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize) PURE; /* The requirement for caller: (processedSize != NULL). The callee can allow (processedSize == NULL) for compatibility reasons. if (size != 0) { Partial write is allowed: (*processedSize <= size), but this function must write at least 1 byte: (*processedSize > 0). You must call Write() in loop, if you need to write exact amount of data. } ERROR CASES: If the function returns error code, then (*processedSize) is size of data written from (data) buffer. */ }; #ifdef __HRESULT_FROM_WIN32 #define HRESULT_WIN32_ERROR_NEGATIVE_SEEK __HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK) #else #define HRESULT_WIN32_ERROR_NEGATIVE_SEEK HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK) #endif /* Seek() Function If you seek before the beginning of the stream, Seek() function returns error code: Recommended error code is __HRESULT_FROM_WIN32(ERROR_NEGATIVE_SEEK). or STG_E_INVALIDFUNCTION It is allowed to seek past the end of the stream. if Seek() returns error, then the value of *newPosition is undefined. */ STREAM_INTERFACE_SUB(IInStream, ISequentialInStream, 0x03) { STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE; }; STREAM_INTERFACE_SUB(IOutStream, ISequentialOutStream, 0x04) { STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE; STDMETHOD(SetSize)(UInt64 newSize) PURE; }; STREAM_INTERFACE(IStreamGetSize, 0x06) { STDMETHOD(GetSize)(UInt64 *size) PURE; }; STREAM_INTERFACE(IOutStreamFlush, 0x07) { STDMETHOD(Flush)() PURE; }; STREAM_INTERFACE(IStreamGetProps, 0x08) { STDMETHOD(GetProps)(UInt64 *size, FILETIME *cTime, FILETIME *aTime, FILETIME *mTime, UInt32 *attrib) PURE; }; struct CStreamFileProps { UInt64 Size; UInt64 VolID; UInt64 FileID_Low; UInt64 FileID_High; UInt32 NumLinks; UInt32 Attrib; FILETIME CTime; FILETIME ATime; FILETIME MTime; }; STREAM_INTERFACE(IStreamGetProps2, 0x09) { STDMETHOD(GetProps2)(CStreamFileProps *props) PURE; }; #endif src/libs/7zip/win/CPP/7zip/PropID.h000066400000000000000000000050511325366651500171220ustar00rootroot00000000000000// PropID.h #ifndef __7ZIP_PROP_ID_H #define __7ZIP_PROP_ID_H #include "../Common/MyTypes.h" enum { kpidNoProperty = 0, kpidMainSubfile, kpidHandlerItemIndex, kpidPath, kpidName, kpidExtension, kpidIsDir, kpidSize, kpidPackSize, kpidAttrib, kpidCTime, kpidATime, kpidMTime, kpidSolid, kpidCommented, kpidEncrypted, kpidSplitBefore, kpidSplitAfter, kpidDictionarySize, kpidCRC, kpidType, kpidIsAnti, kpidMethod, kpidHostOS, kpidFileSystem, kpidUser, kpidGroup, kpidBlock, kpidComment, kpidPosition, kpidPrefix, kpidNumSubDirs, kpidNumSubFiles, kpidUnpackVer, kpidVolume, kpidIsVolume, kpidOffset, kpidLinks, kpidNumBlocks, kpidNumVolumes, kpidTimeType, kpidBit64, kpidBigEndian, kpidCpu, kpidPhySize, kpidHeadersSize, kpidChecksum, kpidCharacts, kpidVa, kpidId, kpidShortName, kpidCreatorApp, kpidSectorSize, kpidPosixAttrib, kpidSymLink, kpidError, kpidTotalSize, kpidFreeSpace, kpidClusterSize, kpidVolumeName, kpidLocalName, kpidProvider, kpidNtSecure, kpidIsAltStream, kpidIsAux, kpidIsDeleted, kpidIsTree, kpidSha1, kpidSha256, kpidErrorType, kpidNumErrors, kpidErrorFlags, kpidWarningFlags, kpidWarning, kpidNumStreams, kpidNumAltStreams, kpidAltStreamsSize, kpidVirtualSize, kpidUnpackSize, kpidTotalPhySize, kpidVolumeIndex, kpidSubType, kpidShortComment, kpidCodePage, kpidIsNotArcType, kpidPhySizeCantBeDetected, kpidZerosTailIsAllowed, kpidTailSize, kpidEmbeddedStubSize, kpidNtReparse, kpidHardLink, kpidINode, kpidStreamId, kpid_NUM_DEFINED, kpidUserDefined = 0x10000 }; extern Byte k7z_PROPID_To_VARTYPE[kpid_NUM_DEFINED]; // VARTYPE const UInt32 kpv_ErrorFlags_IsNotArc = 1 << 0; const UInt32 kpv_ErrorFlags_HeadersError = 1 << 1; const UInt32 kpv_ErrorFlags_EncryptedHeadersError = 1 << 2; const UInt32 kpv_ErrorFlags_UnavailableStart = 1 << 3; const UInt32 kpv_ErrorFlags_UnconfirmedStart = 1 << 4; const UInt32 kpv_ErrorFlags_UnexpectedEnd = 1 << 5; const UInt32 kpv_ErrorFlags_DataAfterEnd = 1 << 6; const UInt32 kpv_ErrorFlags_UnsupportedMethod = 1 << 7; const UInt32 kpv_ErrorFlags_UnsupportedFeature = 1 << 8; const UInt32 kpv_ErrorFlags_DataError = 1 << 9; const UInt32 kpv_ErrorFlags_CrcError = 1 << 10; // const UInt32 kpv_ErrorFlags_Unsupported = 1 << 11; #endif src/libs/7zip/win/CPP/7zip/UI/000077500000000000000000000000001325366651500161305ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/UI/Common/000077500000000000000000000000001325366651500173605ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.cpp000066400000000000000000001111551325366651500235600ustar00rootroot00000000000000// ArchiveCommandLine.cpp #include "StdAfx.h" #undef printf #undef sprintf #ifdef _WIN32 #ifndef UNDER_CE #include #endif #endif #include #include "../../../Common/ListFileUtils.h" #include "../../../Common/StringConvert.h" #include "../../../Common/StringToInt.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileName.h" #ifdef _WIN32 #include "../../../Windows/FileMapping.h" #include "../../../Windows/Synchronization.h" #endif #include "ArchiveCommandLine.h" #include "EnumDirItems.h" #include "SortUtils.h" #include "Update.h" #include "UpdateAction.h" extern bool g_CaseSensitive; #ifdef UNDER_CE #define MY_IS_TERMINAL(x) false; #else #if _MSC_VER >= 1400 #define MY_isatty_fileno(x) _isatty(_fileno(x)) #else #define MY_isatty_fileno(x) isatty(fileno(x)) #endif #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0); #endif using namespace NCommandLineParser; using namespace NWindows; using namespace NFile; static bool StringToUInt32(const wchar_t *s, UInt32 &v) { if (*s == 0) return false; const wchar_t *end; v = ConvertStringToUInt32(s, &end); return *end == 0; } static void AddNewLine(UString &s) { s += L'\n'; } CArcCmdLineException::CArcCmdLineException(const char *a, const wchar_t *u) { (*this) += MultiByteToUnicodeString(a); if (u) { AddNewLine(*this); (*this) += u; } } int g_CodePage = -1; namespace NKey { enum Enum { kHelp1 = 0, kHelp2, kHelp3, kDisableHeaders, kDisablePercents, kArchiveType, kYes, #ifndef _NO_CRYPTO kPassword, #endif kProperty, kOutputDir, kWorkingDir, kInclude, kExclude, kArInclude, kArExclude, kNoArName, kUpdate, kVolume, kRecursed, kSfx, kStdIn, kStdOut, kOverwrite, kEmail, kShowDialog, kLargePages, kListfileCharSet, kConsoleCharSet, kTechMode, kShareForWrite, kCaseSensitive, kHash, kArcNameMode, kDisableWildcardParsing, kElimDup, kFullPathMode, kHardLinks, kSymLinks, kNtSecurity, kAltStreams, kReplaceColonForAltStream, kWriteToAltStreamIfColon, kDeleteAfterCompressing, kSetArcMTime, kExcludedArcType }; } static const wchar_t kRecursedIDChar = 'r'; static const char *kRecursedPostCharSet = "0-"; static const char *k_ArcNameMode_PostCharSet = "sea"; static inline const EArcNameMode ParseArcNameMode(int postCharIndex) { switch (postCharIndex) { case 1: return k_ArcNameMode_Exact; case 2: return k_ArcNameMode_Add; default: return k_ArcNameMode_Smart; } } namespace NRecursedPostCharIndex { enum EEnum { kWildcardRecursionOnly = 0, kNoRecursion = 1 }; } static const char kImmediateNameID = '!'; static const char kMapNameID = '#'; static const char kFileListID = '@'; static const char kSomeCludePostStringMinSize = 2; // at least <@|!>ame must be static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!>ame must be static const char *kOverwritePostCharSet = "asut"; NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = { NExtract::NOverwriteMode::kOverwrite, NExtract::NOverwriteMode::kSkip, NExtract::NOverwriteMode::kRename, NExtract::NOverwriteMode::kRenameExisting }; static const CSwitchForm kSwitchForms[] = { { "?" }, { "h" }, { "-help" }, { "ba" }, { "bd" }, { "t", NSwitchType::kString, false, 1 }, { "y" }, #ifndef _NO_CRYPTO { "p", NSwitchType::kString }, #endif { "m", NSwitchType::kString, true, 1 }, { "o", NSwitchType::kString, false, 1 }, { "w", NSwitchType::kString }, { "i", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "x", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "ai", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "ax", NSwitchType::kString, true, kSomeCludePostStringMinSize}, { "an" }, { "u", NSwitchType::kString, true, 1}, { "v", NSwitchType::kString, true, 1}, { "r", NSwitchType::kChar, false, 0, kRecursedPostCharSet }, { "sfx", NSwitchType::kString }, { "si", NSwitchType::kString }, { "so" }, { "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet}, { "seml", NSwitchType::kString, false, 0}, { "ad" }, { "slp", NSwitchType::kMinus }, { "scs", NSwitchType::kString }, { "scc", NSwitchType::kString }, { "slt" }, { "ssw" }, { "ssc", NSwitchType::kMinus }, { "scrc", NSwitchType::kString, true, 0 }, { "sa", NSwitchType::kChar, false, 1, k_ArcNameMode_PostCharSet }, { "spd" }, { "spe", NSwitchType::kMinus }, { "spf", NSwitchType::kString, false, 0 }, { "snh", NSwitchType::kMinus }, { "snl", NSwitchType::kMinus }, { "sni" }, { "sns", NSwitchType::kMinus }, { "snr" }, { "snc" }, { "sdel" }, { "stl" }, { "stx", NSwitchType::kString, true, 1 } }; static const wchar_t *kUniversalWildcard = L"*"; static const int kMinNonSwitchWords = 1; static const int kCommandIndex = 0; // static const char *kUserErrorMessage = "Incorrect command line"; static const char *kCannotFindListFile = "Cannot find listfile"; static const char *kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch."; // static const char *kIncorrectWildcardInListFile = "Incorrect wildcard in listfile"; // static const char *kIncorrectWildcardInCommandLine = "Incorrect wildcard in command line"; static const char *kTerminalOutError = "I won't write compressed data to a terminal"; static const char *kSameTerminalError = "I won't write data and program's messages to same terminal"; static const char *kEmptyFilePath = "Empty file path"; static const char *kCannotFindArchive = "Cannot find archive"; bool CArcCommand::IsFromExtractGroup() const { switch (CommandType) { case NCommandType::kTest: case NCommandType::kExtract: case NCommandType::kExtractFull: return true; } return false; } NExtract::NPathMode::EEnum CArcCommand::GetPathMode() const { switch (CommandType) { case NCommandType::kTest: case NCommandType::kExtractFull: return NExtract::NPathMode::kFullPaths; } return NExtract::NPathMode::kNoPaths; } bool CArcCommand::IsFromUpdateGroup() const { switch (CommandType) { case NCommandType::kAdd: case NCommandType::kUpdate: case NCommandType::kDelete: case NCommandType::kRename: return true; } return false; } static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) { switch (index) { case NRecursedPostCharIndex::kWildcardRecursionOnly: return NRecursedType::kWildcardOnlyRecursed; case NRecursedPostCharIndex::kNoRecursion: return NRecursedType::kNonRecursed; default: return NRecursedType::kRecursed; } } static const char *g_Commands = "audtexlbih"; static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command) { UString s = commandString; s.MakeLower_Ascii(); if (s.Len() == 1) { if (s[0] > 0x7F) return false; int index = FindCharPosInString(g_Commands, (char)s[0]); if (index < 0) return false; command.CommandType = (NCommandType::EEnum)index; return true; } if (s.Len() == 2 && s[0] == 'r' && s[1] == 'n') { command.CommandType = (NCommandType::kRename); return true; } return false; } // ------------------------------------------------------------------ // filenames functions static void AddNameToCensor(NWildcard::CCensor &censor, const UString &name, bool include, NRecursedType::EEnum type, bool wildcardMatching) { bool recursed = false; switch (type) { case NRecursedType::kWildcardOnlyRecursed: recursed = DoesNameContainWildcard(name); break; case NRecursedType::kRecursed: recursed = true; break; } censor.AddPreItem(include, name, recursed, wildcardMatching); } static void AddRenamePair(CObjectVector *renamePairs, const UString &oldName, const UString &newName, NRecursedType::EEnum type, bool wildcardMatching) { CRenamePair &pair = renamePairs->AddNew(); pair.OldName = oldName; pair.NewName = newName; pair.RecursedType = type; pair.WildcardParsing = wildcardMatching; if (!pair.Prepare()) { UString val; val += pair.OldName; AddNewLine(val); val += pair.NewName; AddNewLine(val); if (type == NRecursedType::kRecursed) val += L"-r"; else if (type == NRecursedType::kRecursed) val += L"-r0"; throw CArcCmdLineException("Unsupported rename command:", val); } } static void AddToCensorFromListFile( CObjectVector *renamePairs, NWildcard::CCensor &censor, LPCWSTR fileName, bool include, NRecursedType::EEnum type, bool wildcardMatching, Int32 codePage) { UStringVector names; if (!NFind::DoesFileExist(us2fs(fileName))) throw CArcCmdLineException(kCannotFindListFile, fileName); if (!ReadNamesFromListFile(us2fs(fileName), names, codePage)) throw CArcCmdLineException(kIncorrectListFile, fileName); if (renamePairs) { if ((names.Size() & 1) != 0) throw CArcCmdLineException(kIncorrectListFile, fileName); for (unsigned i = 0; i < names.Size(); i += 2) { // change type !!!! AddRenamePair(renamePairs, names[i], names[i + 1], type, wildcardMatching); } } else FOR_VECTOR (i, names) AddNameToCensor(censor, names[i], include, type, wildcardMatching); } static void AddToCensorFromNonSwitchesStrings( CObjectVector *renamePairs, unsigned startIndex, NWildcard::CCensor &censor, const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, bool wildcardMatching, bool thereAreSwitchIncludes, Int32 codePage) { if ((renamePairs || nonSwitchStrings.Size() == startIndex) && !thereAreSwitchIncludes) AddNameToCensor(censor, kUniversalWildcard, true, type, true // wildcardMatching ); int oldIndex = -1; for (unsigned i = startIndex; i < nonSwitchStrings.Size(); i++) { const UString &s = nonSwitchStrings[i]; if (s.IsEmpty()) throw CArcCmdLineException(kEmptyFilePath); if (s[0] == kFileListID) AddToCensorFromListFile(renamePairs, censor, s.Ptr(1), true, type, wildcardMatching, codePage); else if (renamePairs) { if (oldIndex == -1) oldIndex = startIndex; else { // NRecursedType::EEnum type is used for global wildcard (-i! switches) AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, NRecursedType::kNonRecursed, wildcardMatching); // AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, type); oldIndex = -1; } } else AddNameToCensor(censor, s, true, type, wildcardMatching); } if (oldIndex != -1) { throw CArcCmdLineException("There is no second file name for rename pair:", nonSwitchStrings[oldIndex]); } } #ifdef _WIN32 struct CEventSetEnd { UString Name; CEventSetEnd(const wchar_t *name): Name(name) {} ~CEventSetEnd() { NSynchronization::CManualResetEvent event; if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(Name)) == 0) event.Set(); } }; const char *k_IncorrectMapCommand = "Incorrect Map command"; static const char *ParseMapWithPaths( NWildcard::CCensor &censor, const UString &s2, bool include, NRecursedType::EEnum commonRecursedType, bool wildcardMatching) { UString s = s2; int pos = s.Find(L':'); if (pos < 0) return k_IncorrectMapCommand; int pos2 = s.Find(L':', pos + 1); if (pos2 < 0) return k_IncorrectMapCommand; CEventSetEnd eventSetEnd((const wchar_t *)s + (pos2 + 1)); s.DeleteFrom(pos2); UInt32 size; if (!StringToUInt32(s.Ptr(pos + 1), size) || size < sizeof(wchar_t) || size > ((UInt32)1 << 31) || size % sizeof(wchar_t) != 0) return "Unsupported Map data size"; s.DeleteFrom(pos); CFileMapping map; if (map.Open(FILE_MAP_READ, GetSystemString(s)) != 0) return "Can not open mapping"; LPVOID data = map.Map(FILE_MAP_READ, 0, size); if (!data) return "MapViewOfFile error"; CFileUnmapper unmapper(data); UString name; const wchar_t *p = (const wchar_t *)data; if (*p != 0) // data format marker return "Unsupported Map data"; UInt32 numChars = size / sizeof(wchar_t); for (UInt32 i = 1; i < numChars; i++) { wchar_t c = p[i]; if (c == 0) { // MessageBoxW(0, name, L"7-Zip", 0); AddNameToCensor(censor, name, include, commonRecursedType, wildcardMatching); name.Empty(); } else name += c; } if (!name.IsEmpty()) return "Map data error"; return NULL; } #endif static void AddSwitchWildcardsToCensor( NWildcard::CCensor &censor, const UStringVector &strings, bool include, NRecursedType::EEnum commonRecursedType, bool wildcardMatching, Int32 codePage) { const char *errorMessage = NULL; unsigned i; for (i = 0; i < strings.Size(); i++) { const UString &name = strings[i]; NRecursedType::EEnum recursedType; unsigned pos = 0; if (name.Len() < kSomeCludePostStringMinSize) { errorMessage = "Too short switch"; break; } if (::MyCharLower_Ascii(name[pos]) == kRecursedIDChar) { pos++; wchar_t c = name[pos]; int index = -1; if (c <= 0x7F) index = FindCharPosInString(kRecursedPostCharSet, (char)c); recursedType = GetRecursedTypeFromIndex(index); if (index >= 0) pos++; } else recursedType = commonRecursedType; if (name.Len() < pos + kSomeCludeAfterRecursedPostStringMinSize) { errorMessage = "Too short switch"; break; } UString tail = name.Ptr(pos + 1); if (name[pos] == kImmediateNameID) AddNameToCensor(censor, tail, include, recursedType, wildcardMatching); else if (name[pos] == kFileListID) AddToCensorFromListFile(NULL, censor, tail, include, recursedType, wildcardMatching, codePage); #ifdef _WIN32 else if (name[pos] == kMapNameID) { errorMessage = ParseMapWithPaths(censor, tail, include, recursedType, wildcardMatching); if (errorMessage) break; } #endif else { errorMessage = "Incorrect wildcarc type marker"; break; } } if (i != strings.Size()) throw CArcCmdLineException(errorMessage, strings[i]); } #ifdef _WIN32 // This code converts all short file names to long file names. static void ConvertToLongName(const UString &prefix, UString &name) { if (name.IsEmpty() || DoesNameContainWildcard(name)) return; NFind::CFileInfo fi; const FString path = us2fs(prefix + name); if (NFile::NName::IsDevicePath(path)) return; if (fi.Find(path)) name = fs2us(fi.Name); } static void ConvertToLongNames(const UString &prefix, CObjectVector &items) { FOR_VECTOR (i, items) { NWildcard::CItem &item = items[i]; if (item.Recursive || item.PathParts.Size() != 1) continue; if (prefix.IsEmpty() && item.IsDriveItem()) continue; ConvertToLongName(prefix, item.PathParts.Front()); } } static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) { ConvertToLongNames(prefix, node.IncludeItems); ConvertToLongNames(prefix, node.ExcludeItems); unsigned i; for (i = 0; i < node.SubNodes.Size(); i++) { UString &name = node.SubNodes[i].Name; if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name)) continue; ConvertToLongName(prefix, name); } // mix folders with same name for (i = 0; i < node.SubNodes.Size(); i++) { NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; for (unsigned j = i + 1; j < node.SubNodes.Size();) { const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; if (nextNode1.Name.IsEqualToNoCase(nextNode2.Name)) { nextNode1.IncludeItems += nextNode2.IncludeItems; nextNode1.ExcludeItems += nextNode2.ExcludeItems; node.SubNodes.Delete(j); } else j++; } } for (i = 0; i < node.SubNodes.Size(); i++) { NWildcard::CCensorNode &nextNode = node.SubNodes[i]; ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode); } } void ConvertToLongNames(NWildcard::CCensor &censor) { FOR_VECTOR (i, censor.Pairs) { NWildcard::CPair &pair = censor.Pairs[i]; ConvertToLongNames(pair.Prefix, pair.Head); } } #endif /* static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i) { switch (i) { case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore; case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy; case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress; case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti; } throw 98111603; } */ static const wchar_t *kUpdatePairStateIDSet = L"pqrxyzw"; static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; static const unsigned kNumUpdatePairActions = 4; static const char *kUpdateIgnoreItselfPostStringID = "-"; static const wchar_t kUpdateNewArchivePostCharID = '!'; static bool ParseUpdateCommandString2(const UString &command, NUpdateArchive::CActionSet &actionSet, UString &postString) { for (unsigned i = 0; i < command.Len();) { wchar_t c = MyCharLower_Ascii(command[i]); int statePos = FindCharPosInString(kUpdatePairStateIDSet, c); if (statePos < 0) { postString = command.Ptr(i); return true; } i++; if (i >= command.Len()) return false; c = command[i]; if (c < '0' || c >= '0' + kNumUpdatePairActions) return false; int actionPos = c - '0'; actionSet.StateActions[statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos); if (kUpdatePairStateNotSupportedActions[statePos] == actionPos) return false; i++; } postString.Empty(); return true; } static void ParseUpdateCommandString(CUpdateOptions &options, const UStringVector &updatePostStrings, const NUpdateArchive::CActionSet &defaultActionSet) { const char *errorMessage = "incorrect update switch command"; unsigned i; for (i = 0; i < updatePostStrings.Size(); i++) { const UString &updateString = updatePostStrings[i]; if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID)) { if (options.UpdateArchiveItself) { options.UpdateArchiveItself = false; options.Commands.Delete(0); } } else { NUpdateArchive::CActionSet actionSet = defaultActionSet; UString postString; if (!ParseUpdateCommandString2(updateString, actionSet, postString)) break; if (postString.IsEmpty()) { if (options.UpdateArchiveItself) options.Commands[0].ActionSet = actionSet; } else { if (postString[0] != kUpdateNewArchivePostCharID) break; CUpdateArchiveCommand uc; UString archivePath = postString.Ptr(1); if (archivePath.IsEmpty()) break; uc.UserArchivePath = archivePath; uc.ActionSet = actionSet; options.Commands.Add(uc); } } } if (i != updatePostStrings.Size()) throw CArcCmdLineException(errorMessage, updatePostStrings[i]); } bool ParseComplexSize(const wchar_t *s, UInt64 &result); static void SetAddCommandOptions( NCommandType::EEnum commandType, const CParser &parser, CUpdateOptions &options) { NUpdateArchive::CActionSet defaultActionSet; switch (commandType) { case NCommandType::kAdd: defaultActionSet = NUpdateArchive::k_ActionSet_Add; break; case NCommandType::kDelete: defaultActionSet = NUpdateArchive::k_ActionSet_Delete; break; default: defaultActionSet = NUpdateArchive::k_ActionSet_Update; } options.UpdateArchiveItself = true; options.Commands.Clear(); CUpdateArchiveCommand updateMainCommand; updateMainCommand.ActionSet = defaultActionSet; options.Commands.Add(updateMainCommand); if (parser[NKey::kUpdate].ThereIs) ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings, defaultActionSet); if (parser[NKey::kWorkingDir].ThereIs) { const UString &postString = parser[NKey::kWorkingDir].PostStrings[0]; if (postString.IsEmpty()) NDir::MyGetTempPath(options.WorkingDir); else options.WorkingDir = us2fs(postString); } options.SfxMode = parser[NKey::kSfx].ThereIs; if (options.SfxMode) options.SfxModule = us2fs(parser[NKey::kSfx].PostStrings[0]); if (parser[NKey::kVolume].ThereIs) { const UStringVector &sv = parser[NKey::kVolume].PostStrings; FOR_VECTOR (i, sv) { UInt64 size; if (!ParseComplexSize(sv[i], size) || size == 0) throw CArcCmdLineException("Incorrect volume size:", sv[i]); options.VolumesSizes.Add(size); } } } static void SetMethodOptions(const CParser &parser, CObjectVector &properties) { if (parser[NKey::kProperty].ThereIs) { FOR_VECTOR (i, parser[NKey::kProperty].PostStrings) { CProperty prop; prop.Name = parser[NKey::kProperty].PostStrings[i]; int index = prop.Name.Find(L'='); if (index >= 0) { prop.Value = prop.Name.Ptr(index + 1); prop.Name.DeleteFrom(index); } properties.Add(prop); } } } CArcCmdLineParser::CArcCmdLineParser(): parser(ARRAY_SIZE(kSwitchForms)) {} void CArcCmdLineParser::Parse1(const UStringVector &commandStrings, CArcCmdLineOptions &options) { if (!parser.ParseStrings(kSwitchForms, commandStrings)) throw CArcCmdLineException(parser.ErrorMessage, parser.ErrorLine); options.IsInTerminal = MY_IS_TERMINAL(stdin); options.IsStdOutTerminal = MY_IS_TERMINAL(stdout); options.IsStdErrTerminal = MY_IS_TERMINAL(stderr); options.StdInMode = parser[NKey::kStdIn].ThereIs; options.StdOutMode = parser[NKey::kStdOut].ThereIs; options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs; options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs || parser[NKey::kHelp3].ThereIs; if (parser[NKey::kCaseSensitive].ThereIs) { g_CaseSensitive = !parser[NKey::kCaseSensitive].WithMinus; options.CaseSensitiveChange = true; options.CaseSensitive = g_CaseSensitive; } #ifdef _WIN32 options.LargePages = false; if (parser[NKey::kLargePages].ThereIs) { options.LargePages = !parser[NKey::kLargePages].WithMinus; } #endif } struct CCodePagePair { const char *Name; Int32 CodePage; }; static const unsigned kNumByteOnlyCodePages = 3; static CCodePagePair g_CodePagePairs[] = { { "utf-8", CP_UTF8 }, { "win", CP_ACP }, { "dos", CP_OEMCP }, { "utf-16le", MY__CP_UTF16 }, { "utf-16be", MY__CP_UTF16BE } }; static Int32 FindCharset(const NCommandLineParser::CParser &parser, int keyIndex, bool byteOnlyCodePages, Int32 defaultVal) { if (!parser[keyIndex].ThereIs) return defaultVal; UString name = parser[keyIndex].PostStrings.Back(); UInt32 v; if (StringToUInt32(name, v)) if (v < ((UInt32)1 << 16)) return (Int32)v; name.MakeLower_Ascii(); unsigned num = byteOnlyCodePages ? kNumByteOnlyCodePages : ARRAY_SIZE(g_CodePagePairs); for (unsigned i = 0;; i++) { if (i == num) // to disable warnings from different compilers throw CArcCmdLineException("Unsupported charset:", name); const CCodePagePair &pair = g_CodePagePairs[i]; if (name.IsEqualTo(pair.Name)) return pair.CodePage; } } void EnumerateDirItemsAndSort( bool storeAltStreams, NWildcard::CCensor &censor, NWildcard::ECensorPathMode censorPathMode, const UString &addPathPrefix, UStringVector &sortedPaths, UStringVector &sortedFullPaths) { UStringVector paths; { CDirItems dirItems; { dirItems.ScanAltStreams = storeAltStreams; HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems, NULL); if (res != S_OK || dirItems.ErrorPaths.Size() > 0) { UString errorPath; if (dirItems.ErrorPaths.Size() > 0) errorPath = fs2us(dirItems.ErrorPaths[0]); throw CArcCmdLineException(kCannotFindArchive, dirItems.ErrorPaths.Size() > 0 ? (const wchar_t *)errorPath : NULL); } } FOR_VECTOR (i, dirItems.Items) { const CDirItem &dirItem = dirItems.Items[i]; if (!dirItem.IsDir()) paths.Add(dirItems.GetPhyPath(i)); } } if (paths.Size() == 0) throw CArcCmdLineException(kCannotFindArchive); UStringVector fullPaths; unsigned i; for (i = 0; i < paths.Size(); i++) { FString fullPath; NFile::NDir::MyGetFullPathName(us2fs(paths[i]), fullPath); fullPaths.Add(fs2us(fullPath)); } CUIntVector indices; SortFileNames(fullPaths, indices); sortedPaths.ClearAndReserve(indices.Size()); sortedFullPaths.ClearAndReserve(indices.Size()); for (i = 0; i < indices.Size(); i++) { unsigned index = indices[i]; sortedPaths.AddInReserved(paths[index]); sortedFullPaths.AddInReserved(fullPaths[index]); if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0) throw CArcCmdLineException("Duplicate archive path:", sortedFullPaths[i]); } } static void SetBoolPair(NCommandLineParser::CParser &parser, unsigned switchID, CBoolPair &bp) { bp.Def = parser[switchID].ThereIs; if (bp.Def) bp.Val = !parser[switchID].WithMinus; } void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options) { const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; int numNonSwitchStrings = nonSwitchStrings.Size(); if (numNonSwitchStrings < kMinNonSwitchWords) throw CArcCmdLineException("The command must be spcified"); if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command)) throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]); options.TechMode = parser[NKey::kTechMode].ThereIs; if (parser[NKey::kHash].ThereIs) options.HashMethods = parser[NKey::kHash].PostStrings; if (parser[NKey::kElimDup].ThereIs) { options.ExtractOptions.ElimDup.Def = true; options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus; } NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath; bool fullPathMode = parser[NKey::kFullPathMode].ThereIs; if (fullPathMode) { censorPathMode = NWildcard::k_AbsPath; const UString &s = parser[NKey::kFullPathMode].PostStrings[0]; if (!s.IsEmpty()) { if (s == L"2") censorPathMode = NWildcard::k_FullPath; else throw CArcCmdLineException("Unsupported -spf:", s); } } NRecursedType::EEnum recursedType; if (parser[NKey::kRecursed].ThereIs) recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex); else recursedType = NRecursedType::kNonRecursed; bool wildcardMatching = true; if (parser[NKey::kDisableWildcardParsing].ThereIs) wildcardMatching = false; g_CodePage = FindCharset(parser, NKey::kConsoleCharSet, true, -1); Int32 codePage = FindCharset(parser, NKey::kListfileCharSet, false, CP_UTF8); bool thereAreSwitchIncludes = false; if (parser[NKey::kInclude].ThereIs) { thereAreSwitchIncludes = true; AddSwitchWildcardsToCensor(options.Censor, parser[NKey::kInclude].PostStrings, true, recursedType, wildcardMatching, codePage); } if (parser[NKey::kExclude].ThereIs) AddSwitchWildcardsToCensor(options.Censor, parser[NKey::kExclude].PostStrings, false, recursedType, wildcardMatching, codePage); int curCommandIndex = kCommandIndex + 1; bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && options.Command.CommandType != NCommandType::kBenchmark && options.Command.CommandType != NCommandType::kInfo && options.Command.CommandType != NCommandType::kHash; bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList; bool isRename = options.Command.CommandType == NCommandType::kRename; if ((isExtractOrList || isRename) && options.StdInMode) thereIsArchiveName = false; if (parser[NKey::kArcNameMode].ThereIs) options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex); if (thereIsArchiveName) { if (curCommandIndex >= numNonSwitchStrings) throw CArcCmdLineException("Cannot find archive name"); options.ArchiveName = nonSwitchStrings[curCommandIndex++]; if (options.ArchiveName.IsEmpty()) throw CArcCmdLineException("Archive name cannot by empty"); } AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL, curCommandIndex, options.Censor, nonSwitchStrings, recursedType, wildcardMatching, thereAreSwitchIncludes, codePage); options.YesToAll = parser[NKey::kYes].ThereIs; #ifndef _NO_CRYPTO options.PasswordEnabled = parser[NKey::kPassword].ThereIs; if (options.PasswordEnabled) options.Password = parser[NKey::kPassword].PostStrings[0]; #endif options.ShowDialog = parser[NKey::kShowDialog].ThereIs; if (parser[NKey::kArchiveType].ThereIs) options.ArcType = parser[NKey::kArchiveType].PostStrings[0]; options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings; SetMethodOptions(parser, options.Properties); options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs; if (options.EnablePercents) { if ((options.StdOutMode && !options.IsStdErrTerminal) || (!options.StdOutMode && !options.IsStdOutTerminal)) options.EnablePercents = false; } if (parser[NKey::kNtSecurity].ThereIs) options.NtSecurity.SetTrueTrue(); SetBoolPair(parser, NKey::kAltStreams, options.AltStreams); SetBoolPair(parser, NKey::kHardLinks, options.HardLinks); SetBoolPair(parser, NKey::kSymLinks, options.SymLinks); if (isExtractOrList) { CExtractOptionsBase &eo = options.ExtractOptions; { CExtractNtOptions &nt = eo.NtOptions; nt.NtSecurity = options.NtSecurity; nt.AltStreams = options.AltStreams; if (!options.AltStreams.Def) nt.AltStreams.Val = true; nt.HardLinks = options.HardLinks; if (!options.HardLinks.Def) nt.HardLinks.Val = true; nt.SymLinks = options.SymLinks; if (!options.SymLinks.Def) nt.SymLinks.Val = true; nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs; nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs; } options.Censor.AddPathsToCensor(NWildcard::k_AbsPath); options.Censor.ExtendExclude(); // are there paths that look as non-relative (!Prefix.IsEmpty()) if (!options.Censor.AllAreRelative()) throw CArcCmdLineException("Cannot use absolute pathnames for this command"); NWildcard::CCensor arcCensor; if (parser[NKey::kArInclude].ThereIs) AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, wildcardMatching, codePage); if (parser[NKey::kArExclude].ThereIs) AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, wildcardMatching, codePage); if (thereIsArchiveName) AddNameToCensor(arcCensor, options.ArchiveName, true, NRecursedType::kNonRecursed, wildcardMatching); arcCensor.AddPathsToCensor(NWildcard::k_RelatPath); #ifdef _WIN32 ConvertToLongNames(arcCensor); #endif arcCensor.ExtendExclude(); if (options.StdInMode) { UString arcName = parser[NKey::kStdIn].PostStrings.Front(); options.ArchivePathsSorted.Add(arcName); options.ArchivePathsFullSorted.Add(arcName); } else { EnumerateDirItemsAndSort( false, // scanAltStreams arcCensor, NWildcard::k_RelatPath, UString(), // addPathPrefix options.ArchivePathsSorted, options.ArchivePathsFullSorted); } if (isExtractGroupCommand) { if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal) throw CArcCmdLineException(kSameTerminalError); if (parser[NKey::kOutputDir].ThereIs) { eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]); NFile::NName::NormalizeDirPathPrefix(eo.OutputDir); } eo.OverwriteMode = NExtract::NOverwriteMode::kAsk; if (parser[NKey::kOverwrite].ThereIs) { eo.OverwriteMode = k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex]; eo.OverwriteMode_Force = true; } else if (options.YesToAll) { eo.OverwriteMode = NExtract::NOverwriteMode::kOverwrite; eo.OverwriteMode_Force = true; } } eo.PathMode = options.Command.GetPathMode(); if (censorPathMode == NWildcard::k_AbsPath) { eo.PathMode = NExtract::NPathMode::kAbsPaths; eo.PathMode_Force = true; } else if (censorPathMode == NWildcard::k_FullPath) { eo.PathMode = NExtract::NPathMode::kFullPaths; eo.PathMode_Force = true; } } else if (options.Command.IsFromUpdateGroup()) { if (parser[NKey::kArInclude].ThereIs) throw CArcCmdLineException("-ai switch is not supported for this command"); CUpdateOptions &updateOptions = options.UpdateOptions; SetAddCommandOptions(options.Command.CommandType, parser, updateOptions); updateOptions.MethodMode.Properties = options.Properties; if (parser[NKey::kShareForWrite].ThereIs) updateOptions.OpenShareForWrite = true; updateOptions.PathMode = censorPathMode; updateOptions.AltStreams = options.AltStreams; updateOptions.NtSecurity = options.NtSecurity; updateOptions.HardLinks = options.HardLinks; updateOptions.SymLinks = options.SymLinks; updateOptions.EMailMode = parser[NKey::kEmail].ThereIs; if (updateOptions.EMailMode) { updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front(); if (updateOptions.EMailAddress.Len() > 0) if (updateOptions.EMailAddress[0] == L'.') { updateOptions.EMailRemoveAfter = true; updateOptions.EMailAddress.Delete(0); } } updateOptions.StdOutMode = options.StdOutMode; updateOptions.StdInMode = options.StdInMode; updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs; updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs; if (updateOptions.StdOutMode && updateOptions.EMailMode) throw CArcCmdLineException("stdout mode and email mode cannot be combined"); if (updateOptions.StdOutMode && options.IsStdOutTerminal) throw CArcCmdLineException(kTerminalOutError); if (updateOptions.StdInMode) updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front(); if (options.Command.CommandType == NCommandType::kRename) if (updateOptions.Commands.Size() != 1) throw CArcCmdLineException("Only one archive can be created with rename command"); } else if (options.Command.CommandType == NCommandType::kBenchmark) { options.NumIterations = 1; if (curCommandIndex < numNonSwitchStrings) { if (!StringToUInt32(nonSwitchStrings[curCommandIndex], options.NumIterations)) throw CArcCmdLineException("Incorrect Number of benmchmark iterations", nonSwitchStrings[curCommandIndex]); curCommandIndex++; } } else if (options.Command.CommandType == NCommandType::kHash) { options.Censor.AddPathsToCensor(censorPathMode); options.Censor.ExtendExclude(); CHashOptions &hashOptions = options.HashOptions; hashOptions.PathMode = censorPathMode; hashOptions.Methods = options.HashMethods; if (parser[NKey::kShareForWrite].ThereIs) hashOptions.OpenShareForWrite = true; hashOptions.StdInMode = options.StdInMode; hashOptions.AltStreamsMode = options.AltStreams.Val; } else if (options.Command.CommandType == NCommandType::kInfo) { } else throw 9815676711; } src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.h000066400000000000000000000046411325366651500232260ustar00rootroot00000000000000// ArchiveCommandLine.h #ifndef __ARCHIVE_COMMAND_LINE_H #define __ARCHIVE_COMMAND_LINE_H #include "../../../Common/CommandLineParser.h" #include "../../../Common/Wildcard.h" #include "Extract.h" #include "HashCalc.h" #include "Update.h" struct CArcCmdLineException: public UString { CArcCmdLineException(const char *a, const wchar_t *u = NULL); }; namespace NCommandType { enum EEnum { kAdd = 0, kUpdate, kDelete, kTest, kExtract, kExtractFull, kList, kBenchmark, kInfo, kHash, kRename };} struct CArcCommand { NCommandType::EEnum CommandType; bool IsFromExtractGroup() const; bool IsFromUpdateGroup() const; bool IsTestCommand() const { return CommandType == NCommandType::kTest; } NExtract::NPathMode::EEnum GetPathMode() const; }; struct CArcCmdLineOptions { bool HelpMode; #ifdef _WIN32 bool LargePages; #endif bool CaseSensitiveChange; bool CaseSensitive; bool IsInTerminal; bool IsStdOutTerminal; bool IsStdErrTerminal; bool StdInMode; bool StdOutMode; bool EnableHeaders; bool YesToAll; bool ShowDialog; NWildcard::CCensor Censor; CArcCommand Command; UString ArchiveName; #ifndef _NO_CRYPTO bool PasswordEnabled; UString Password; #endif bool TechMode; UStringVector HashMethods; bool AppendName; UStringVector ArchivePathsSorted; UStringVector ArchivePathsFullSorted; CObjectVector Properties; CExtractOptionsBase ExtractOptions; CBoolPair NtSecurity; CBoolPair AltStreams; CBoolPair HardLinks; CBoolPair SymLinks; CUpdateOptions UpdateOptions; CHashOptions HashOptions; UString ArcType; UStringVector ExcludedArcTypes; bool EnablePercents; // Benchmark UInt32 NumIterations; CArcCmdLineOptions(): StdInMode(false), StdOutMode(false), CaseSensitiveChange(false), CaseSensitive(false) {}; }; class CArcCmdLineParser { NCommandLineParser::CParser parser; public: CArcCmdLineParser(); void Parse1(const UStringVector &commandStrings, CArcCmdLineOptions &options); void Parse2(CArcCmdLineOptions &options); }; void EnumerateDirItemsAndSort( bool storeAltStreams, NWildcard::CCensor &censor, NWildcard::ECensorPathMode pathMode, const UString &addPathPrefix, UStringVector &sortedPaths, UStringVector &sortedFullPaths); #endif src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp000066400000000000000000000751461325366651500244320ustar00rootroot00000000000000// ArchiveExtractCallback.cpp #include "StdAfx.h" #undef sprintf #undef printf #include "../../../Common/ComTry.h" #include "../../../Common/StringConvert.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif #include "../../Common/FilePathAutoRename.h" #include "../Common/ExtractingFilePath.h" #include "../Common/PropIDUtils.h" #include "ArchiveExtractCallback.h" using namespace NWindows; using namespace NFile; using namespace NDir; static const char *kCantAutoRename = "Can not create file with auto name"; static const char *kCantRenameFile = "Can not rename existing file"; static const char *kCantDeleteOutputFile = "Can not delete output file"; static const char *kCantDeleteOutputDir = "Can not delete output folder"; #ifndef _SFX STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize) { HRESULT result = S_OK; if (_stream) result = _stream->Write(data, size, &size); if (_calculate) _hash->Update(data, size); _size += size; if (processedSize) *processedSize = size; return result; } #endif #ifdef _USE_SECURITY_CODE bool InitLocalPrivileges() { NSecurity::CAccessToken token; if (!token.OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)) return false; TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid)) return false; if (!token.AdjustPrivileges(&tp)) return false; return (GetLastError() == ERROR_SUCCESS); } #endif #ifdef SUPPORT_LINKS int CHardLinkNode::Compare(const CHardLinkNode &a) const { if (StreamId < a.StreamId) return -1; if (StreamId > a.StreamId) return 1; return MyCompare(INode, a.INode); } HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined) { h.INode = 0; h.StreamId = (UInt64)(Int64)-1; defined = false; { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidINode, &prop)); if (!ConvertPropVariantToUInt64(prop, h.INode)) return S_OK; } { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidStreamId, &prop)); ConvertPropVariantToUInt64(prop, h.StreamId); } defined = true; return S_OK; } HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector *realIndices) { _hardLinks.Clear(); if (!_arc->Ask_INode) return S_OK; IInArchive *archive = _arc->Archive; CRecordVector &hardIDs = _hardLinks.IDs; { UInt32 numItems; if (realIndices) numItems = realIndices->Size(); else { RINOK(archive->GetNumberOfItems(&numItems)); } for (UInt32 i = 0; i < numItems; i++) { CHardLinkNode h; bool defined; RINOK(Archive_Get_HardLinkNode(archive, realIndices ? (*realIndices)[i] : i, h, defined)); if (defined) hardIDs.Add(h); } } hardIDs.Sort2(); { // wee keep only items that have 2 or more items unsigned k = 0; unsigned numSame = 1; for (unsigned i = 1; i < hardIDs.Size(); i++) { if (hardIDs[i].Compare(hardIDs[i - 1]) != 0) numSame = 1; else if (++numSame == 2) { if (i - 1 != k) hardIDs[k] = hardIDs[i - 1]; k++; } } hardIDs.DeleteFrom(k); } _hardLinks.PrepareLinks(); return S_OK; } #endif CArchiveExtractCallback::CArchiveExtractCallback(): WriteCTime(true), WriteATime(true), WriteMTime(true), _multiArchives(false) { LocalProgressSpec = new CLocalProgress(); _localProgress = LocalProgressSpec; #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } void CArchiveExtractCallback::Init( const CExtractNtOptions &ntOptions, const NWildcard::CCensorNode *wildcardCensor, const CArc *arc, IFolderArchiveExtractCallback *extractCallback2, bool stdOutMode, bool testMode, const FString &directoryPath, const UStringVector &removePathParts, UInt64 packSize) { _extractedFolderPaths.Clear(); _extractedFolderIndices.Clear(); #ifdef SUPPORT_LINKS _hardLinks.Clear(); #endif _ntOptions = ntOptions; _wildcardCensor = wildcardCensor; _stdOutMode = stdOutMode; _testMode = testMode; _unpTotal = 1; _packTotal = packSize; _extractCallback2 = extractCallback2; _compressProgress.Release(); _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); #ifndef _SFX _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback); if (ExtractToStreamCallback) { Int32 useStreams = 0; if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK) useStreams = 0; if (useStreams == 0) ExtractToStreamCallback.Release(); } #endif LocalProgressSpec->Init(extractCallback2, true); LocalProgressSpec->SendProgress = false; _removePathParts = removePathParts; _baseParentFolder = (UInt32)(Int32)-1; _use_baseParentFolder_mode = false; _arc = arc; _directoryPath = directoryPath; NName::NormalizeDirPathPrefix(_directoryPath); NDir::MyGetFullPathName(directoryPath, _directoryPathFull); } STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) { COM_TRY_BEGIN _unpTotal = size; if (!_multiArchives && _extractCallback2) return _extractCallback2->SetTotal(size); return S_OK; COM_TRY_END } static void NormalizeVals(UInt64 &v1, UInt64 &v2) { const UInt64 kMax = (UInt64)1 << 31; while (v1 > kMax) { v1 >>= 1; v2 >>= 1; } } static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) { NormalizeVals(packTotal, unpTotal); NormalizeVals(unpCur, unpTotal); if (unpTotal == 0) unpTotal = 1; return unpCur * packTotal / unpTotal; } STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) { COM_TRY_BEGIN if (!_extractCallback2) return S_OK; if (_multiArchives) { if (completeValue != NULL) { UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); return _extractCallback2->SetCompleted(&packCur); } } return _extractCallback2->SetCompleted(completeValue); COM_TRY_END } STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { COM_TRY_BEGIN return _localProgress->SetRatioInfo(inSize, outSize); COM_TRY_END } #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z') static inline bool IsDriveName(const UString &s) { return s.Len() == 2 && s[1] == ':' && IS_LETTER_CHAR(s[0]); } void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) { bool isAbsPath = false; if (!dirPathParts.IsEmpty()) { const UString &s = dirPathParts[0]; if (s.IsEmpty()) isAbsPath = true; #ifdef _WIN32 else { if (dirPathParts.Size() > 1 && IsDriveName(s)) isAbsPath = true; } #endif } if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath) fullPath.Empty(); else fullPath = _directoryPath; FOR_VECTOR (i, dirPathParts) { if (i > 0) fullPath += FCHAR_PATH_SEPARATOR; const UString &s = dirPathParts[i]; fullPath += us2fs(s); #ifdef _WIN32 if (_pathMode == NExtract::NPathMode::kAbsPaths) if (i == 0 && IsDriveName(s)) continue; #endif CreateDir(fullPath); } } HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined) { filetimeIsDefined = false; NCOM::CPropVariant prop; RINOK(_arc->Archive->GetProperty(index, propID, &prop)); if (prop.vt == VT_FILETIME) { filetime = prop.filetime; filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0); } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } HRESULT CArchiveExtractCallback::GetUnpackSize() { return _arc->GetItemSize(_index, _curSize, _curSizeDefined); } HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) { return _extractCallback2->MessageError( UString(L"ERROR: ") + GetUnicodeString(message) + L": " + fs2us(path)); } HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2) { return _extractCallback2->MessageError( UString(L"ERROR: ") + GetUnicodeString(message) + UString(L": ") + fs2us(path1) + UString(L" : ") + fs2us(path2)); } #ifndef _SFX STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value) { if (propID == kpidName) { COM_TRY_BEGIN NCOM::CPropVariant prop = Name.Ptr(); prop.Detach(value); return S_OK; COM_TRY_END } return Arc->Archive->GetProperty(IndexInArc, propID, value); } #endif #ifdef SUPPORT_LINKS static UString GetDirPrefixOf(const UString &src) { UString s = src; if (!s.IsEmpty()) { if (s.Back() == WCHAR_PATH_SEPARATOR) s.DeleteBack(); int pos = s.ReverseFind(WCHAR_PATH_SEPARATOR); s.DeleteFrom(pos + 1); } return s; } static bool IsSafePath(const UString &path) { UStringVector parts; SplitPathToParts(path, parts); int level = 0; FOR_VECTOR(i, parts) { const UString &s = parts[i]; if (s.IsEmpty()) continue; if (s == L".") continue; if (s == L"..") { if (level <= 0) return false; level--; } else level++; } return level > 0; } #endif STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) { COM_TRY_BEGIN *outStream = 0; #ifndef _SFX if (_hashStream) _hashStreamSpec->ReleaseStream(); _hashStreamWasUsed = false; #endif _outFileStream.Release(); _encrypted = false; _isSplit = false; _isAltStream = false; _curSize = 0; _curSizeDefined = false; _index = index; UString fullPath; IInArchive *archive = _arc->Archive; RINOK(_arc->GetItemPath(index, fullPath)); RINOK(Archive_IsItem_Folder(archive, index, _fi.IsDir)); _filePath = fullPath; { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidPosition, &prop)); if (prop.vt != VT_EMPTY) { if (prop.vt != VT_UI8) return E_FAIL; _position = prop.uhVal.QuadPart; _isSplit = true; } } #ifdef SUPPORT_LINKS bool isHardLink = false; bool isJunction = false; bool isRelative = false; UString linkPath; // RINOK(Archive_GetItemBoolProp(archive, index, kpidIsHardLink, isHardLink)); // if (isHardLink) { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidHardLink, &prop)); if (prop.vt == VT_BSTR) { isHardLink = true; linkPath = prop.bstrVal; isRelative = false; // TAR: hard links are from root folder of archive } else if (prop.vt == VT_EMPTY) { // linkPath.Empty(); } else return E_FAIL; } { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidSymLink, &prop)); if (prop.vt == VT_BSTR) { isHardLink = false; linkPath = prop.bstrVal; isRelative = true; // TAR: symbolic links are relative } else if (prop.vt == VT_EMPTY) { // linkPath.Empty(); } else return E_FAIL; } bool isOkReparse = false; if (linkPath.IsEmpty() && _arc->GetRawProps) { const void *data; UInt32 dataSize; UInt32 propType; _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType); if (dataSize != 0) { if (propType != NPropDataType::kRaw) return E_FAIL; UString s; CReparseAttr reparse; isOkReparse = reparse.Parse((const Byte *)data, dataSize); if (isOkReparse) { isHardLink = false; linkPath = reparse.GetPath(); isJunction = reparse.IsMountPoint(); isRelative = reparse.IsRelative(); #ifndef _WIN32 linkPath.Replace(WCHAR_PATH_SEPARATOR, '/', ); #endif } } } if (!linkPath.IsEmpty()) { #ifdef _WIN32 linkPath.Replace('/', WCHAR_PATH_SEPARATOR); #endif for (;;) // while (NName::IsAbsolutePath(linkPath)) { unsigned n = NName::GetRootPrefixSize(linkPath); if (n == 0) break; isRelative = false; linkPath.DeleteFrontal(n); } } if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0) { UStringVector pathParts; SplitPathToParts(linkPath, pathParts); bool badPrefix = false; FOR_VECTOR (i, _removePathParts) { if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) { badPrefix = true; break; } } if (!badPrefix) pathParts.DeleteFrontal(_removePathParts.Size()); linkPath = MakePathNameFromParts(pathParts); } #endif RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted)); RINOK(GetUnpackSize()); RINOK(Archive_IsItem_AltStream(archive, index, _isAltStream)); if (!_ntOptions.AltStreams.Val && _isAltStream) return S_OK; if (_wildcardCensor) { if (!_wildcardCensor->CheckPath(_isAltStream, fullPath, !_fi.IsDir)) return S_OK; } UStringVector pathParts; if (_use_baseParentFolder_mode) { int baseParent = _baseParentFolder; if (_pathMode == NExtract::NPathMode::kFullPaths || _pathMode == NExtract::NPathMode::kAbsPaths) baseParent = -1; RINOK(_arc->GetItemPathToParent(index, baseParent, pathParts)); if (_pathMode == NExtract::NPathMode::kNoPaths && !pathParts.IsEmpty()) pathParts.DeleteFrontal(pathParts.Size() - 1); } else { SplitPathToParts(fullPath, pathParts); if (pathParts.IsEmpty()) return E_FAIL; unsigned numRemovePathParts = 0; switch (_pathMode) { case NExtract::NPathMode::kCurPaths: { bool badPrefix = false; if (pathParts.Size() <= _removePathParts.Size()) badPrefix = true; else { FOR_VECTOR (i, _removePathParts) { if (!_removePathParts[i].IsEqualToNoCase(pathParts[i])) { badPrefix = true; break; } } } if (badPrefix) { if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) return E_FAIL; } else numRemovePathParts = _removePathParts.Size(); break; } case NExtract::NPathMode::kNoPaths: { numRemovePathParts = pathParts.Size() - 1; break; } /* case NExtract::NPathMode::kFullPaths: case NExtract::NPathMode::kAbsPaths: break; */ } pathParts.DeleteFrontal(numRemovePathParts); } #ifndef _SFX if (ExtractToStreamCallback) { if (!GetProp) { GetProp_Spec = new CGetProp; GetProp = GetProp_Spec; } GetProp_Spec->Arc = _arc; GetProp_Spec->IndexInArc = index; GetProp_Spec->Name = MakePathNameFromParts(pathParts); return ExtractToStreamCallback->GetStream7(GetProp_Spec->Name, _fi.IsDir, outStream, askExtractMode, GetProp); } #endif CMyComPtr outStreamLoc; if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) { if (_stdOutMode) { outStreamLoc = new CStdOutFileStream; } else { { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidAttrib, &prop)); if (prop.vt == VT_UI4) { _fi.Attrib = prop.ulVal; _fi.AttribDefined = true; } else if (prop.vt == VT_EMPTY) _fi.AttribDefined = false; else return E_FAIL; } RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined)); RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined)); RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined)); bool isAnti = false; RINOK(_arc->IsItemAnti(index, isAnti)); bool replace = _isAltStream ? _ntOptions.ReplaceColonForAltStream : !_ntOptions.WriteToAltStreamIfColon; if (_pathMode != NExtract::NPathMode::kAbsPaths) MakeCorrectPath(_directoryPath.IsEmpty(), pathParts, replace); Correct_IfEmptyLastPart(pathParts); UString processedPath = MakePathNameFromParts(pathParts); if (!isAnti) { if (!_fi.IsDir) { if (!pathParts.IsEmpty()) pathParts.DeleteBack(); } if (!pathParts.IsEmpty()) { FString fullPathNew; CreateComplexDirectory(pathParts, fullPathNew); if (_fi.IsDir) { _extractedFolderPaths.Add(fullPathNew); _extractedFolderIndices.Add(index); SetDirTime(fullPathNew, (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); } } } FString fullProcessedPath = us2fs(processedPath); if (_pathMode != NExtract::NPathMode::kAbsPaths || !NName::IsAbsolutePath(processedPath)) fullProcessedPath = _directoryPath + fullProcessedPath; if (_fi.IsDir) { _diskFilePath = fullProcessedPath; if (isAnti) RemoveDir(_diskFilePath); #ifdef SUPPORT_LINKS if (linkPath.IsEmpty()) #endif return S_OK; } else if (!_isSplit) { NFind::CFileInfo fileInfo; if (fileInfo.Find(fullProcessedPath)) { switch (_overwriteMode) { case NExtract::NOverwriteMode::kSkip: return S_OK; case NExtract::NOverwriteMode::kAsk: { int slashPos = fullProcessedPath.ReverseFind(FTEXT('/')); #ifdef _WIN32 int slash1Pos = fullProcessedPath.ReverseFind(FTEXT('\\')); slashPos = MyMax(slashPos, slash1Pos); #endif FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name; Int32 overwiteResult; RINOK(_extractCallback2->AskOverwrite( fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath, _fi.MTimeDefined ? &_fi.MTime : NULL, _curSizeDefined ? &_curSize : NULL, &overwiteResult)) switch (overwiteResult) { case NOverwriteAnswer::kCancel: return E_ABORT; case NOverwriteAnswer::kNo: return S_OK; case NOverwriteAnswer::kNoToAll: _overwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK; case NOverwriteAnswer::kYes: break; case NOverwriteAnswer::kYesToAll: _overwriteMode = NExtract::NOverwriteMode::kOverwrite; break; case NOverwriteAnswer::kAutoRename: _overwriteMode = NExtract::NOverwriteMode::kRename; break; default: return E_FAIL; } } } if (_overwriteMode == NExtract::NOverwriteMode::kRename) { if (!AutoRenamePath(fullProcessedPath)) { RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); return E_FAIL; } } else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting) { FString existPath = fullProcessedPath; if (!AutoRenamePath(existPath)) { RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); return E_FAIL; } // MyMoveFile can raname folders. So it's OK to use it folders too if (!MyMoveFile(fullProcessedPath, existPath)) { RINOK(SendMessageError(kCantRenameFile, fullProcessedPath)); return E_FAIL; } } else { if (fileInfo.IsDir()) { // do we need to delete all files in folder? if (!RemoveDir(fullProcessedPath)) { RINOK(SendMessageError(kCantDeleteOutputDir, fullProcessedPath)); return S_OK; } } else if (!DeleteFileAlways(fullProcessedPath)) { RINOK(SendMessageError(kCantDeleteOutputFile, fullProcessedPath)); return S_OK; // return E_FAIL; } } } } _diskFilePath = fullProcessedPath; if (!isAnti) { #ifdef SUPPORT_LINKS if (!linkPath.IsEmpty()) { #ifndef UNDER_CE UString relatPath; if (isRelative) relatPath = GetDirPrefixOf(_filePath); relatPath += linkPath; if (!IsSafePath(relatPath)) { RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath))); } else { FString existPath; if (isHardLink || !isRelative) { if (!NName::GetFullPath(_directoryPathFull, us2fs(relatPath), existPath)) { RINOK(SendMessageError("Incorrect path", us2fs(relatPath))); } } else { existPath = us2fs(linkPath); } if (!existPath.IsEmpty()) { if (isHardLink) { if (!MyCreateHardLink(fullProcessedPath, existPath)) { RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath)); // return S_OK; } } else if (_ntOptions.SymLinks.Val) { // bool isSymLink = true; // = false for junction if (_fi.IsDir && !isRelative) { // if it's before Vista we use Junction Point // isJunction = true; // convertToAbs = true; } CByteBuffer data; if (FillLinkData(data, fs2us(existPath), !isJunction)) { CReparseAttr attr; if (!attr.Parse(data, data.Size())) { return E_FAIL; // "Internal conversion error"; } if (!NFile::NIO::SetReparseData(fullProcessedPath, _fi.IsDir, data, (DWORD)data.Size())) { RINOK(SendMessageError("Can not set reparse data", fullProcessedPath)); } } } } } #endif } else #endif // SUPPORT_LINKS { bool needWriteFile = true; #ifdef SUPPORT_LINKS if (!_hardLinks.IDs.IsEmpty()) { CHardLinkNode h; bool defined; RINOK(Archive_Get_HardLinkNode(archive, index, h, defined)); if (defined) { { int linkIndex = _hardLinks.IDs.FindInSorted2(h); if (linkIndex >= 0) { FString &hl = _hardLinks.Links[linkIndex]; if (hl.IsEmpty()) hl = fullProcessedPath; else { if (!MyCreateHardLink(fullProcessedPath, hl)) { RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl)); return S_OK; } needWriteFile = false; } } } } } #endif if (needWriteFile) { _outFileStreamSpec = new COutFileStream; CMyComPtr outStreamLoc(_outFileStreamSpec); if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) { // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) { RINOK(SendMessageError("Can not open output file ", fullProcessedPath)); return S_OK; } } if (_isSplit) { RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); } _outFileStream = outStreamLoc; } } } outStreamLoc = _outFileStream; } } #ifndef _SFX if (_hashStream) { if (askExtractMode == NArchive::NExtract::NAskMode::kExtract || askExtractMode == NArchive::NExtract::NAskMode::kTest) { _hashStreamSpec->SetStream(outStreamLoc); outStreamLoc = _hashStream; _hashStreamSpec->Init(true); _hashStreamWasUsed = true; } } #endif if (outStreamLoc) *outStream = outStreamLoc.Detach(); return S_OK; COM_TRY_END } STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) { COM_TRY_BEGIN #ifndef _SFX if (ExtractToStreamCallback) return ExtractToStreamCallback->PrepareOperation7(askExtractMode); #endif _extractMode = false; switch (askExtractMode) { case NArchive::NExtract::NAskMode::kExtract: if (_testMode) askExtractMode = NArchive::NExtract::NAskMode::kTest; else _extractMode = true; break; }; return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir, askExtractMode, _isSplit ? &_position: 0); COM_TRY_END } STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) { COM_TRY_BEGIN #ifndef _SFX if (ExtractToStreamCallback) return ExtractToStreamCallback->SetOperationResult7(operationResult, _encrypted); #endif #ifndef _SFX if (_hashStreamWasUsed) { _hashStreamSpec->_hash->Final(_fi.IsDir, _isAltStream, _filePath); _curSize = _hashStreamSpec->GetSize(); _curSizeDefined = true; _hashStreamSpec->ReleaseStream(); _hashStreamWasUsed = false; } #endif if (_outFileStream) { _outFileStreamSpec->SetTime( (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); _curSize = _outFileStreamSpec->ProcessedSize; _curSizeDefined = true; RINOK(_outFileStreamSpec->Close()); _outFileStream.Release(); } #ifdef _USE_SECURITY_CODE if (_ntOptions.NtSecurity.Val && _arc->GetRawProps) { const void *data; UInt32 dataSize; UInt32 propType; _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType); if (dataSize != 0) { if (propType != NPropDataType::kRaw) return E_FAIL; if (CheckNtSecure((const Byte *)data, dataSize)) { SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; if (_saclEnabled) securInfo |= SACL_SECURITY_INFORMATION; ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)data); } } } #endif if (!_curSizeDefined) GetUnpackSize(); if (_curSizeDefined) { if (_isAltStream) AltStreams_UnpackSize += _curSize; else UnpackSize += _curSize; } if (_fi.IsDir) NumFolders++; else if (_isAltStream) NumAltStreams++; else NumFiles++; if (_extractMode && _fi.AttribDefined) SetFileAttrib(_diskFilePath, _fi.Attrib); RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); return S_OK; COM_TRY_END } /* STDMETHODIMP CArchiveExtractCallback::GetInStream( const wchar_t *name, ISequentialInStream **inStream) { COM_TRY_BEGIN CInFileStream *inFile = new CInFileStream; CMyComPtr inStreamTemp = inFile; if (!inFile->Open(_srcDirectoryPrefix + name)) return ::GetLastError(); *inStream = inStreamTemp.Detach(); return S_OK; COM_TRY_END } */ STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN if (!_cryptoGetTextPassword) { RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword, &_cryptoGetTextPassword)); } return _cryptoGetTextPassword->CryptoGetTextPassword(password); COM_TRY_END } struct CExtrRefSortPair { int Len; int Index; int Compare(const CExtrRefSortPair &a) const; }; #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const { RINOZ(-MyCompare(Len, a.Len)); return MyCompare(Index, a.Index); } static int GetNumSlashes(const FChar *s) { for (int numSlashes = 0;;) { FChar c = *s++; if (c == 0) return numSlashes; if ( #ifdef _WIN32 c == FTEXT('\\') || #endif c == FTEXT('/')) numSlashes++; } } HRESULT CArchiveExtractCallback::SetDirsTimes() { CRecordVector pairs; pairs.ClearAndSetSize(_extractedFolderPaths.Size()); unsigned i; for (i = 0; i < _extractedFolderPaths.Size(); i++) { CExtrRefSortPair &pair = pairs[i]; pair.Index = i; pair.Len = GetNumSlashes(_extractedFolderPaths[i]); } pairs.Sort2(); for (i = 0; i < pairs.Size(); i++) { int pairIndex = pairs[i].Index; int index = _extractedFolderIndices[pairIndex]; FILETIME CTime; FILETIME ATime; FILETIME MTime; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined)); RINOK(GetTime(index, kpidATime, ATime, ATimeDefined)); RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined)); // printf("\n%S", _extractedFolderPaths[pairIndex]); SetDirTime(_extractedFolderPaths[pairIndex], (WriteCTime && CTimeDefined) ? &CTime : NULL, (WriteATime && ATimeDefined) ? &ATime : NULL, (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); } return S_OK; } src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.h000066400000000000000000000147701325366651500240730ustar00rootroot00000000000000// ArchiveExtractCallback.h #ifndef __ARCHIVE_EXTRACT_CALLBACK_H #define __ARCHIVE_EXTRACT_CALLBACK_H #include "../../../Common/MyCom.h" #include "../../../Common/Wildcard.h" #include "../../IPassword.h" #include "../../Common/FileStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Archive/IArchive.h" #include "ExtractMode.h" #include "IFileExtractCallback.h" #include "OpenArchive.h" #include "HashCalc.h" #ifndef _SFX class COutStreamWithHash: public ISequentialOutStream, public CMyUnknownImp { CMyComPtr _stream; UInt64 _size; bool _calculate; public: IHashCalc *_hash; MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); void SetStream(ISequentialOutStream *stream) { _stream = stream; } void ReleaseStream() { _stream.Release(); } void Init(bool calculate = true) { InitCRC(); _size = 0; _calculate = calculate; } void EnableCalc(bool calculate) { _calculate = calculate; } void InitCRC() { _hash->InitForNewFile(); } UInt64 GetSize() const { return _size; } }; #endif struct CExtractNtOptions { CBoolPair NtSecurity; CBoolPair SymLinks; CBoolPair HardLinks; CBoolPair AltStreams; bool ReplaceColonForAltStream; bool WriteToAltStreamIfColon; CExtractNtOptions(): ReplaceColonForAltStream(false), WriteToAltStreamIfColon(false) { SymLinks.Val = true; HardLinks.Val = true; AltStreams.Val = true; } }; #ifndef _SFX class CGetProp: public IGetProp, public CMyUnknownImp { public: const CArc *Arc; UInt32 IndexInArc; UString Name; // relative path MY_UNKNOWN_IMP1(IGetProp) INTERFACE_IGetProp(;) }; #endif #ifndef _SFX #ifndef UNDER_CE #define SUPPORT_LINKS #endif #endif #ifdef SUPPORT_LINKS struct CHardLinkNode { UInt64 StreamId; UInt64 INode; int Compare(const CHardLinkNode &a) const; }; class CHardLinks { public: CRecordVector IDs; CObjectVector Links; void Clear() { IDs.Clear(); Links.Clear(); } void PrepareLinks() { while (Links.Size() < IDs.Size()) Links.AddNew(); } }; #endif class CArchiveExtractCallback: public IArchiveExtractCallback, // public IArchiveVolumeExtractCallback, public ICryptoGetTextPassword, public ICompressProgressInfo, public CMyUnknownImp { const CArc *_arc; CExtractNtOptions _ntOptions; const NWildcard::CCensorNode *_wildcardCensor; CMyComPtr _extractCallback2; CMyComPtr _compressProgress; CMyComPtr _cryptoGetTextPassword; FString _directoryPath; FString _directoryPathFull; NExtract::NPathMode::EEnum _pathMode; NExtract::NOverwriteMode::EEnum _overwriteMode; #ifndef _SFX CMyComPtr ExtractToStreamCallback; CGetProp *GetProp_Spec; CMyComPtr GetProp; #endif FString _diskFilePath; UString _filePath; UInt64 _position; bool _isSplit; bool _isAltStream; bool _extractMode; bool WriteCTime; bool WriteATime; bool WriteMTime; bool _encrypted; struct CProcessedFileInfo { FILETIME CTime; FILETIME ATime; FILETIME MTime; UInt32 Attrib; bool CTimeDefined; bool ATimeDefined; bool MTimeDefined; bool AttribDefined; bool IsDir; } _fi; UInt32 _index; UInt64 _curSize; bool _curSizeDefined; COutFileStream *_outFileStreamSpec; CMyComPtr _outFileStream; #ifndef _SFX COutStreamWithHash *_hashStreamSpec; CMyComPtr _hashStream; bool _hashStreamWasUsed; #endif UStringVector _removePathParts; bool _use_baseParentFolder_mode; UInt32 _baseParentFolder; bool _stdOutMode; bool _testMode; bool _multiArchives; CMyComPtr _localProgress; UInt64 _packTotal; UInt64 _unpTotal; FStringVector _extractedFolderPaths; CRecordVector _extractedFolderIndices; #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX) bool _saclEnabled; #endif void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath); HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined); HRESULT GetUnpackSize(); HRESULT SendMessageError(const char *message, const FString &path); HRESULT SendMessageError2(const char *message, const FString &path1, const FString &path2); public: CLocalProgress *LocalProgressSpec; UInt64 NumFolders; UInt64 NumFiles; UInt64 NumAltStreams; UInt64 UnpackSize; UInt64 AltStreams_UnpackSize; MY_UNKNOWN_IMP2(ICryptoGetTextPassword, ICompressProgressInfo) // COM_INTERFACE_ENTRY(IArchiveVolumeExtractCallback) INTERFACE_IArchiveExtractCallback(;) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); // IArchiveVolumeExtractCallback // STDMETHOD(GetInStream)(const wchar_t *name, ISequentialInStream **inStream); STDMETHOD(CryptoGetTextPassword)(BSTR *password); CArchiveExtractCallback(); void InitForMulti(bool multiArchives, NExtract::NPathMode::EEnum pathMode, NExtract::NOverwriteMode::EEnum overwriteMode) { _multiArchives = multiArchives; _pathMode = pathMode; _overwriteMode = overwriteMode; NumFolders = NumFiles = NumAltStreams = UnpackSize = AltStreams_UnpackSize = 0; } #ifndef _SFX void SetHashMethods(IHashCalc *hash) { if (!hash) return; _hashStreamSpec = new COutStreamWithHash; _hashStream = _hashStreamSpec; _hashStreamSpec->_hash = hash; } #endif void Init( const CExtractNtOptions &ntOptions, const NWildcard::CCensorNode *wildcardCensor, const CArc *arc, IFolderArchiveExtractCallback *extractCallback2, bool stdOutMode, bool testMode, const FString &directoryPath, const UStringVector &removePathParts, UInt64 packSize); #ifdef SUPPORT_LINKS private: CHardLinks _hardLinks; public: // call PrepareHardLinks() after Init() HRESULT PrepareHardLinks(const CRecordVector *realIndices); // NULL means all items #endif // call it after Init() void SetBaseParentFolderIndex(UInt32 indexInArc) { _use_baseParentFolder_mode = true; _baseParentFolder = indexInArc; } HRESULT SetDirsTimes(); }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp000066400000000000000000000063401325366651500237070ustar00rootroot00000000000000// ArchiveOpenCallback.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../Common/FileStreams.h" #include "ArchiveOpenCallback.h" using namespace NWindows; STDMETHODIMP COpenCallbackImp::SetTotal(const UInt64 *files, const UInt64 *bytes) { COM_TRY_BEGIN if (ReOpenCallback) return ReOpenCallback->SetTotal(files, bytes); if (!Callback) return S_OK; return Callback->Open_SetTotal(files, bytes); COM_TRY_END } STDMETHODIMP COpenCallbackImp::SetCompleted(const UInt64 *files, const UInt64 *bytes) { COM_TRY_BEGIN if (ReOpenCallback) return ReOpenCallback->SetCompleted(files, bytes); if (!Callback) return S_OK; return Callback->Open_SetCompleted(files, bytes); COM_TRY_END } STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; if (_subArchiveMode) switch(propID) { case kpidName: prop = _subArchiveName; break; } else switch(propID) { case kpidName: prop = _fileInfo.Name; break; case kpidIsDir: prop = _fileInfo.IsDir(); break; case kpidSize: prop = _fileInfo.Size; break; case kpidAttrib: prop = (UInt32)_fileInfo.Attrib; break; case kpidCTime: prop = _fileInfo.CTime; break; case kpidATime: prop = _fileInfo.ATime; break; case kpidMTime: prop = _fileInfo.MTime; break; } prop.Detach(value); return S_OK; COM_TRY_END } struct CInFileStreamVol: public CInFileStream { int FileNameIndex; COpenCallbackImp *OpenCallbackImp; CMyComPtr OpenCallbackRef; ~CInFileStreamVol() { if (OpenCallbackRef) OpenCallbackImp->FileNames_WasUsed[FileNameIndex] = false; } }; STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream) { COM_TRY_BEGIN *inStream = NULL; if (_subArchiveMode) return S_FALSE; if (Callback) { RINOK(Callback->Open_CheckBreak()); } FString fullPath; if (!NFile::NName::GetFullPath(_folderPrefix, us2fs(name), fullPath)) return S_FALSE; if (!_fileInfo.Find(fullPath)) return S_FALSE; if (_fileInfo.IsDir()) return S_FALSE; CInFileStreamVol *inFile = new CInFileStreamVol; CMyComPtr inStreamTemp = inFile; if (!inFile->Open(fullPath)) return ::GetLastError(); FileSizes.Add(_fileInfo.Size); FileNames.Add(name); inFile->FileNameIndex = FileNames_WasUsed.Add(true); inFile->OpenCallbackImp = this; inFile->OpenCallbackRef = this; // TotalSize += _fileInfo.Size; *inStream = inStreamTemp.Detach(); return S_OK; COM_TRY_END } #ifndef _NO_CRYPTO STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN if (ReOpenCallback) { CMyComPtr getTextPassword; ReOpenCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); if (getTextPassword) return getTextPassword->CryptoGetTextPassword(password); } if (!Callback) return E_NOTIMPL; return Callback->Open_CryptoGetTextPassword(password); COM_TRY_END } #endif src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.h000066400000000000000000000052251325366651500233550ustar00rootroot00000000000000// ArchiveOpenCallback.h #ifndef __ARCHIVE_OPEN_CALLBACK_H #define __ARCHIVE_OPEN_CALLBACK_H #include "../../../Common/MyCom.h" #include "../../../Windows/FileFind.h" #ifndef _NO_CRYPTO #include "../../IPassword.h" #endif #include "../../Archive/IArchive.h" #ifdef _NO_CRYPTO #define INTERFACE_IOpenCallbackUI_Crypto(x) #else #define INTERFACE_IOpenCallbackUI_Crypto(x) \ virtual HRESULT Open_CryptoGetTextPassword(BSTR *password) x; \ virtual HRESULT Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password) x; \ virtual bool Open_WasPasswordAsked() x; \ virtual void Open_ClearPasswordWasAskedFlag() x; \ #endif #define INTERFACE_IOpenCallbackUI(x) \ virtual HRESULT Open_CheckBreak() x; \ virtual HRESULT Open_SetTotal(const UInt64 *files, const UInt64 *bytes) x; \ virtual HRESULT Open_SetCompleted(const UInt64 *files, const UInt64 *bytes) x; \ INTERFACE_IOpenCallbackUI_Crypto(x) struct IOpenCallbackUI { INTERFACE_IOpenCallbackUI(=0) }; class COpenCallbackImp: public IArchiveOpenCallback, public IArchiveOpenVolumeCallback, public IArchiveOpenSetSubArchiveName, #ifndef _NO_CRYPTO public ICryptoGetTextPassword, #endif public CMyUnknownImp { public: #ifndef _NO_CRYPTO MY_UNKNOWN_IMP3( IArchiveOpenVolumeCallback, ICryptoGetTextPassword, IArchiveOpenSetSubArchiveName ) #else MY_UNKNOWN_IMP2( IArchiveOpenVolumeCallback, IArchiveOpenSetSubArchiveName ) #endif INTERFACE_IArchiveOpenCallback(;) INTERFACE_IArchiveOpenVolumeCallback(;) #ifndef _NO_CRYPTO STDMETHOD(CryptoGetTextPassword)(BSTR *password); #endif STDMETHOD(SetSubArchiveName(const wchar_t *name)) { _subArchiveMode = true; _subArchiveName = name; // TotalSize = 0; return S_OK; } private: FString _folderPrefix; NWindows::NFile::NFind::CFileInfo _fileInfo; bool _subArchiveMode; UString _subArchiveName; public: UStringVector FileNames; CBoolVector FileNames_WasUsed; CRecordVector FileSizes; IOpenCallbackUI *Callback; CMyComPtr ReOpenCallback; // UInt64 TotalSize; COpenCallbackImp(): Callback(NULL) {} void Init(const FString &folderPrefix, const FString &fileName) { _folderPrefix = folderPrefix; if (!_fileInfo.Find(_folderPrefix + fileName)) throw 20121118; FileNames.Clear(); FileNames_WasUsed.Clear(); FileSizes.Clear(); _subArchiveMode = false; // TotalSize = 0; } bool SetSecondFileInfo(CFSTR newName) { return _fileInfo.Find(newName) && !_fileInfo.IsDir(); } }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/Common.pri000066400000000000000000000044331325366651500213300ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveOpenCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveExtractCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveCommandLine.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/DefaultName.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/DirItem.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/EnumDirItems.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/Extract.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ExtractMode.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/ExtractingFilePath.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/HashCalc.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/IFileExtractCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/LoadCodecs.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/OpenArchive.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/Property.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/PropIDUtils.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/SetProperties.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/SortUtils.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/StdAfx.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/TempFiles.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/Update.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateAction.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateCallback.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdatePair.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateProduce.h SOURCES += $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/ArchiveCommandLine.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/DefaultName.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/EnumDirItems.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/Extract.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/ExtractingFilePath.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/HashCalc.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/LoadCodecs.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/OpenArchive.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/PropIDUtils.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/SetProperties.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/SortUtils.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/TempFiles.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/Update.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateAction.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateCallback.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdatePair.cpp \ $$7ZIP_BASE/CPP/7zip/UI/Common/UpdateProduce.cpp src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.cpp000066400000000000000000000017371325366651500222610ustar00rootroot00000000000000// DefaultName.cpp #include "StdAfx.h" #include "DefaultName.h" static UString GetDefaultName3(const UString &fileName, const UString &extension, const UString &addSubExtension) { int extLength = extension.Len(); int fileNameLength = fileName.Len(); if (fileNameLength > extLength + 1) { int dotPos = fileNameLength - (extLength + 1); if (fileName[dotPos] == '.') if (extension.IsEqualToNoCase(fileName.Ptr(dotPos + 1))) return fileName.Left(dotPos) + addSubExtension; } int dotPos = fileName.ReverseFind(L'.'); if (dotPos > 0) return fileName.Left(dotPos) + addSubExtension; if (addSubExtension.IsEmpty()) return fileName + L"~"; else return fileName + addSubExtension; } UString GetDefaultName2(const UString &fileName, const UString &extension, const UString &addSubExtension) { UString name = GetDefaultName3(fileName, extension, addSubExtension); name.TrimRight(); return name; } src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.h000066400000000000000000000003571325366651500217230ustar00rootroot00000000000000// DefaultName.h #ifndef __DEFAULT_NAME_H #define __DEFAULT_NAME_H #include "../../../Common/MyString.h" UString GetDefaultName2(const UString &fileName, const UString &extension, const UString &addSubExtension); #endif src/libs/7zip/win/CPP/7zip/UI/Common/DirItem.h000066400000000000000000000054011325366651500210660ustar00rootroot00000000000000// DirItem.h #ifndef __DIR_ITEM_H #define __DIR_ITEM_H #include "../../../Common/MyString.h" #include "../../../Windows/FileFind.h" #include "../../Common/UniqBlocks.h" #include "../../Archive/IArchive.h" struct CDirItem { UInt64 Size; FILETIME CTime; FILETIME ATime; FILETIME MTime; UString Name; #if defined(_WIN32) && !defined(UNDER_CE) // UString ShortName; CByteBuffer ReparseData; CByteBuffer ReparseData2; // fixed (reduced) absolute links bool AreReparseData() const { return ReparseData.Size() != 0 || ReparseData2.Size() != 0; } #endif UInt32 Attrib; int PhyParent; int LogParent; int SecureIndex; bool IsAltStream; CDirItem(): PhyParent(-1), LogParent(-1), SecureIndex(-1), IsAltStream(false) {} bool IsDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; } }; class CDirItems { UStringVector Prefixes; CIntVector PhyParents; CIntVector LogParents; UString GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const; void EnumerateDir(int phyParent, int logParent, const FString &phyPrefix); public: CObjectVector Items; bool SymLinks; bool ScanAltStreams; FStringVector ErrorPaths; CRecordVector ErrorCodes; UInt64 TotalSize; #ifndef UNDER_CE void SetLinkInfo(CDirItem &dirItem, const NWindows::NFile::NFind::CFileInfo &fi, const FString &phyPrefix); #endif void AddError(const FString &path, DWORD errorCode) { ErrorCodes.Add(errorCode); ErrorPaths.Add(path); } void AddError(const FString &path) { AddError(path, ::GetLastError()); } #if defined(_WIN32) && !defined(UNDER_CE) CUniqBlocks SecureBlocks; CByteBuffer TempSecureBuf; bool _saclEnabled; bool ReadSecure; void AddSecurityItem(const FString &path, int &secureIndex); #endif CDirItems(); int GetNumFolders() const { return Prefixes.Size(); } UString GetPhyPath(unsigned index) const; UString GetLogPath(unsigned index) const; unsigned AddPrefix(int phyParent, int logParent, const UString &prefix); void DeleteLastPrefix(); void EnumerateItems2( const FString &phyPrefix, const UString &logPrefix, const FStringVector &filePaths, FStringVector *requestedPaths); #if defined(_WIN32) && !defined(UNDER_CE) void FillFixedReparse(); #endif void ReserveDown(); }; struct CArcItem { UInt64 Size; FILETIME MTime; UString Name; bool IsDir; bool IsAltStream; bool SizeDefined; bool MTimeDefined; bool Censored; UInt32 IndexInServer; int TimeType; CArcItem(): IsDir(false), IsAltStream(false), SizeDefined(false), MTimeDefined(false), Censored(false), TimeType(-1) {} }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.cpp000066400000000000000000000511241325366651500224340ustar00rootroot00000000000000// EnumDirItems.cpp #include "StdAfx.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/FileName.h" #if defined(_WIN32) && !defined(UNDER_CE) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif #include "EnumDirItems.h" using namespace NWindows; using namespace NFile; using namespace NName; void AddDirFileInfo(int phyParent, int logParent, int secureIndex, const NFind::CFileInfo &fi, CObjectVector &dirItems) { CDirItem di; di.Size = fi.Size; di.CTime = fi.CTime; di.ATime = fi.ATime; di.MTime = fi.MTime; di.Attrib = fi.Attrib; di.IsAltStream = fi.IsAltStream; di.PhyParent = phyParent; di.LogParent = logParent; di.SecureIndex = secureIndex; di.Name = fs2us(fi.Name); #if defined(_WIN32) && !defined(UNDER_CE) // di.ShortName = fs2us(fi.ShortName); #endif dirItems.Add(di); } UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const { UString path; unsigned len = name.Len(); int i; for (i = index; i >= 0; i = parents[i]) len += Prefixes[i].Len(); unsigned totalLen = len; wchar_t *p = path.GetBuffer(len); p[len] = 0; len -= name.Len(); memcpy(p + len, (const wchar_t *)name, name.Len() * sizeof(wchar_t)); for (i = index; i >= 0; i = parents[i]) { const UString &s = Prefixes[i]; len -= s.Len(); memcpy(p + len, (const wchar_t *)s, s.Len() * sizeof(wchar_t)); } path.ReleaseBuffer(totalLen); return path; } UString CDirItems::GetPhyPath(unsigned index) const { const CDirItem &di = Items[index]; return GetPrefixesPath(PhyParents, di.PhyParent, di.Name); } UString CDirItems::GetLogPath(unsigned index) const { const CDirItem &di = Items[index]; return GetPrefixesPath(LogParents, di.LogParent, di.Name); } void CDirItems::ReserveDown() { Prefixes.ReserveDown(); PhyParents.ReserveDown(); LogParents.ReserveDown(); Items.ReserveDown(); } unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix) { PhyParents.Add(phyParent); LogParents.Add(logParent); return Prefixes.Add(prefix); } void CDirItems::DeleteLastPrefix() { PhyParents.DeleteBack(); LogParents.DeleteBack(); Prefixes.DeleteBack(); } bool InitLocalPrivileges(); CDirItems::CDirItems(): SymLinks(false), TotalSize(0) #ifdef _USE_SECURITY_CODE , ReadSecure(false) #endif { #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } #ifdef _USE_SECURITY_CODE void CDirItems::AddSecurityItem(const FString &path, int &secureIndex) { secureIndex = -1; SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; if (_saclEnabled) securInfo |= SACL_SECURITY_INFORMATION; DWORD errorCode = 0; DWORD secureSize; BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); if (res) { if (secureSize == 0) return; if (secureSize > TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION; } else { errorCode = GetLastError(); if (errorCode == ERROR_INSUFFICIENT_BUFFER) { if (secureSize <= TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION; else { TempSecureBuf.Alloc(secureSize); res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); if (res) { if (secureSize != TempSecureBuf.Size()) errorCode = ERROR_INVALID_FUNCTION;; } else errorCode = GetLastError(); } } } if (res) { secureIndex = SecureBlocks.AddUniq(TempSecureBuf, secureSize); return; } if (errorCode == 0) errorCode = ERROR_INVALID_FUNCTION; AddError(path, errorCode); } #endif void CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix) { NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK); for (;;) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { AddError(phyPrefix); return; } if (!found) break; int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (ReadSecure) AddSecurityItem(phyPrefix + fi.Name, secureIndex); #endif AddDirFileInfo(phyParent, logParent, secureIndex, fi, Items); if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2)); EnumerateDir(parent, parent, phyPrefix + name2); } } } void CDirItems::EnumerateItems2( const FString &phyPrefix, const UString &logPrefix, const FStringVector &filePaths, FStringVector *requestedPaths) { int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, fs2us(phyPrefix)); int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix); FOR_VECTOR (i, filePaths) { const FString &filePath = filePaths[i]; NFind::CFileInfo fi; const FString phyPath = phyPrefix + filePath; if (!fi.Find(phyPath)) { AddError(phyPath); continue; } if (requestedPaths) requestedPaths->Add(phyPath); int delimiter = filePath.ReverseFind(FCHAR_PATH_SEPARATOR); FString phyPrefixCur; int phyParentCur = phyParent; if (delimiter >= 0) { phyPrefixCur.SetFrom(filePath, delimiter + 1); phyParentCur = AddPrefix(phyParent, logParent, fs2us(phyPrefixCur)); } int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (ReadSecure) AddSecurityItem(phyPath, secureIndex); #endif AddDirFileInfo(phyParentCur, logParent, secureIndex, fi, Items); if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2)); EnumerateDir(parent, parent, phyPrefix + phyPrefixCur + name2); } } ReserveDown(); } static HRESULT EnumerateDirItems( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback); static HRESULT EnumerateDirItems_Spec( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &curFolderName, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback) { const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR; unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2)); unsigned numItems = dirItems.Items.Size(); HRESULT res = EnumerateDirItems( curNode, parent, parent, phyPrefix + name2, addArchivePrefix, dirItems, enterToSubFolders, callback); if (numItems == dirItems.Items.Size()) dirItems.DeleteLastPrefix(); return res; } #ifndef UNDER_CE static void EnumerateAltStreams( const NFind::CFileInfo &fi, const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems) { const FString fullPath = phyPrefix + fi.Name; NFind::CStreamEnumerator enumerator(fullPath); for (;;) { NFind::CStreamInfo si; bool found; if (!enumerator.Next(si, found)) { dirItems.AddError(fullPath + FTEXT(":*"), (DWORD)E_FAIL); break; } if (!found) break; if (si.IsMainStream()) continue; UStringVector addArchivePrefixNew = addArchivePrefix; UString reducedName = si.GetReducedName(); addArchivePrefixNew.Back() += reducedName; if (curNode.CheckPathToRoot(false, addArchivePrefixNew, true)) continue; NFind::CFileInfo fi2 = fi; fi2.Name += us2fs(reducedName); fi2.Size = si.Size; fi2.Attrib &= ~FILE_ATTRIBUTE_DIRECTORY; fi2.IsAltStream = true; AddDirFileInfo(phyParent, logParent, -1, fi2, dirItems.Items); dirItems.TotalSize += fi2.Size; } } void CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi, const FString &phyPrefix) { if (!SymLinks || !fi.HasReparsePoint()) return; const FString path = phyPrefix + fi.Name; CByteBuffer &buf = dirItem.ReparseData; if (NIO::GetReparseData(path, buf)) { CReparseAttr attr; if (attr.Parse(buf, buf.Size())) return; } AddError(path); buf.Free(); } #endif static HRESULT EnumerateForItem( NFind::CFileInfo &fi, const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback) { const UString name = fs2us(fi.Name); bool enterToSubFolders2 = enterToSubFolders; UStringVector addArchivePrefixNew = addArchivePrefix; addArchivePrefixNew.Add(name); { UStringVector addArchivePrefixNewTemp(addArchivePrefixNew); if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir())) return S_OK; } int dirItemIndex = -1; if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir())) { int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (dirItems.ReadSecure) dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex); #endif dirItemIndex = dirItems.Items.Size(); AddDirFileInfo(phyParent, logParent, secureIndex, fi, dirItems.Items); dirItems.TotalSize += fi.Size; if (fi.IsDir()) enterToSubFolders2 = true; } #ifndef UNDER_CE if (dirItems.ScanAltStreams) { EnumerateAltStreams(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefixNew, dirItems); } if (dirItemIndex >= 0) { CDirItem &dirItem = dirItems.Items[dirItemIndex]; dirItems.SetLinkInfo(dirItem, fi, phyPrefix); if (dirItem.ReparseData.Size() != 0) return S_OK; } #endif if (!fi.IsDir()) return S_OK; const NWildcard::CCensorNode *nextNode = 0; if (addArchivePrefix.IsEmpty()) { int index = curNode.FindSubNode(name); if (index >= 0) nextNode = &curNode.SubNodes[index]; } if (!enterToSubFolders2 && nextNode == 0) return S_OK; addArchivePrefixNew = addArchivePrefix; if (nextNode == 0) { nextNode = &curNode; addArchivePrefixNew.Add(name); } return EnumerateDirItems_Spec( *nextNode, phyParent, logParent, fi.Name, phyPrefix, addArchivePrefixNew, dirItems, enterToSubFolders2, callback); } static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode) { FOR_VECTOR (i, curNode.IncludeItems) { const NWildcard::CItem &item = curNode.IncludeItems[i]; if (item.Recursive || item.PathParts.Size() != 1) return false; const UString &name = item.PathParts.Front(); if (name.IsEmpty()) return false; /* Windows doesn't support file name with wildcard. but if another system supports file name with wildcard, and wildcard mode is disabled, we can ignore wildcard in name */ /* if (!item.WildcardParsing) continue; */ if (DoesNameContainWildcard(name)) return false; } return true; } static HRESULT EnumerateDirItems( const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems, bool enterToSubFolders, IEnumDirItemCallback *callback) { if (!enterToSubFolders) if (curNode.NeedCheckSubDirs()) enterToSubFolders = true; if (callback) RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), dirItems.TotalSize, fs2us(phyPrefix), true)); // try direct_names case at first if (addArchivePrefix.IsEmpty() && !enterToSubFolders) { if (CanUseFsDirect(curNode)) { // all names are direct (no wildcards) // so we don't need file_system's dir enumerator CRecordVector needEnterVector; unsigned i; for (i = 0; i < curNode.IncludeItems.Size(); i++) { const NWildcard::CItem &item = curNode.IncludeItems[i]; const UString &name = item.PathParts.Front(); const FString fullPath = phyPrefix + us2fs(name); NFind::CFileInfo fi; #ifdef _WIN32 if (phyPrefix.IsEmpty() && item.IsDriveItem()) { fi.SetAsDir(); fi.Name = fullPath; } else #endif if (!fi.Find(fullPath)) { dirItems.AddError(fullPath); continue; } bool isDir = fi.IsDir(); if (isDir && !item.ForDir || !isDir && !item.ForFile) { dirItems.AddError(fullPath, (DWORD)E_FAIL); continue; } { UStringVector pathParts; pathParts.Add(fs2us(fi.Name)); if (curNode.CheckPathToRoot(false, pathParts, !isDir)) continue; } int secureIndex = -1; #ifdef _USE_SECURITY_CODE if (dirItems.ReadSecure) dirItems.AddSecurityItem(fullPath, secureIndex); #endif AddDirFileInfo(phyParent, logParent, secureIndex, fi, dirItems.Items); #ifndef UNDER_CE { CDirItem &dirItem = dirItems.Items.Back(); dirItems.SetLinkInfo(dirItem, fi, phyPrefix); if (dirItem.ReparseData.Size() != 0) continue; } #endif dirItems.TotalSize += fi.Size; #ifndef UNDER_CE if (dirItems.ScanAltStreams) { UStringVector pathParts; pathParts.Add(fs2us(fi.Name)); EnumerateAltStreams(fi, curNode, phyParent, logParent, phyPrefix, pathParts, dirItems); } #endif if (!isDir) continue; UStringVector addArchivePrefixNew; const NWildcard::CCensorNode *nextNode = 0; int index = curNode.FindSubNode(name); if (index >= 0) { for (int t = needEnterVector.Size(); t <= index; t++) needEnterVector.Add(true); needEnterVector[index] = false; nextNode = &curNode.SubNodes[index]; } else { nextNode = &curNode; addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support } RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix, addArchivePrefixNew, dirItems, true, callback)); } for (i = 0; i < curNode.SubNodes.Size(); i++) { if (i < needEnterVector.Size()) if (!needEnterVector[i]) continue; const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i]; const FString fullPath = phyPrefix + us2fs(nextNode.Name); NFind::CFileInfo fi; #ifdef _WIN32 if (phyPrefix.IsEmpty() && NWildcard::IsDriveColonName(nextNode.Name)) { fi.SetAsDir(); fi.Name = fullPath; } else #endif if (!fi.Find(fullPath)) { if (!nextNode.AreThereIncludeItems()) continue; dirItems.AddError(fullPath); continue; } if (!fi.IsDir()) { dirItems.AddError(fullPath, (DWORD)E_FAIL); continue; } RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix, UStringVector(), dirItems, false, callback)); } return S_OK; } } #ifdef _WIN32 #ifndef UNDER_CE // scan drives, if wildcard is "*:\" if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0) { unsigned i; for (i = 0; i < curNode.IncludeItems.Size(); i++) { const NWildcard::CItem &item = curNode.IncludeItems[i]; if (item.PathParts.Size() < 1) break; const UString &name = item.PathParts.Front(); if (name.Len() != 2 || name[1] != ':') break; if (item.PathParts.Size() == 1) if (item.ForFile || !item.ForDir) break; if (NWildcard::IsDriveColonName(name)) continue; if (name[0] != '*' && name[0] != '?') break; } if (i == curNode.IncludeItems.Size()) { FStringVector driveStrings; NFind::MyGetLogicalDriveStrings(driveStrings); for (i = 0; i < driveStrings.Size(); i++) { FString driveName = driveStrings[i]; if (driveName.Len() < 3 || driveName.Back() != '\\') return E_FAIL; driveName.DeleteBack(); NFind::CFileInfo fi; fi.SetAsDir(); fi.Name = driveName; RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefix, dirItems, enterToSubFolders, callback)); } return S_OK; } } #endif #endif NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK); for (unsigned ttt = 0; ; ttt++) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { dirItems.AddError(phyPrefix); break; } if (!found) break; if (callback && (ttt & 0xFF) == 0xFF) RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), dirItems.TotalSize, fs2us(phyPrefix), true)); RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, addArchivePrefix, dirItems, enterToSubFolders, callback)); } return S_OK; } HRESULT EnumerateItems( const NWildcard::CCensor &censor, const NWildcard::ECensorPathMode pathMode, const UString &addPathPrefix, CDirItems &dirItems, IEnumDirItemCallback *callback) { FOR_VECTOR (i, censor.Pairs) { const NWildcard::CPair &pair = censor.Pairs[i]; int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix); int logParent = -1; if (pathMode == NWildcard::k_AbsPath) logParent = phyParent; else { if (!addPathPrefix.IsEmpty()) logParent = dirItems.AddPrefix(-1, -1, addPathPrefix); } RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(), dirItems, false, // enterToSubFolders callback)); } dirItems.ReserveDown(); #if defined(_WIN32) && !defined(UNDER_CE) dirItems.FillFixedReparse(); #endif return S_OK; } #if defined(_WIN32) && !defined(UNDER_CE) void CDirItems::FillFixedReparse() { /* imagex/WIM reduces absolute pathes in links (raparse data), if we archive non root folder. We do same thing here */ if (!SymLinks) return; FOR_VECTOR(i, Items) { CDirItem &item = Items[i]; if (item.ReparseData.Size() == 0) continue; CReparseAttr attr; if (!attr.Parse(item.ReparseData, item.ReparseData.Size())) continue; if (attr.IsRelative()) continue; const UString &link = attr.GetPath(); if (!IsDrivePath(link)) continue; // maybe we need to support networks paths also ? FString fullPathF; if (!NDir::MyGetFullPathName(us2fs(GetPhyPath(i)), fullPathF)) continue; UString fullPath = fs2us(fullPathF); const UString logPath = GetLogPath(i); if (logPath.Len() >= fullPath.Len()) continue; if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0) continue; const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len()); if (prefix.Back() != WCHAR_PATH_SEPARATOR) continue; unsigned rootPrefixSize = GetRootPrefixSize(prefix); if (rootPrefixSize == 0) continue; if (rootPrefixSize == prefix.Len()) continue; // simple case: paths are from root if (link.Len() <= prefix.Len()) continue; if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0) continue; UString newLink = prefix.Left(rootPrefixSize); newLink += link.Ptr(prefix.Len()); CByteBuffer data; if (!FillLinkData(data, newLink, attr.IsSymLink())) continue; item.ReparseData2 = data; } } #endif src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.h000066400000000000000000000013171325366651500221000ustar00rootroot00000000000000// EnumDirItems.h #ifndef __ENUM_DIR_ITEMS_H #define __ENUM_DIR_ITEMS_H #include "../../../Common/Wildcard.h" #include "../../../Windows/FileFind.h" #include "DirItem.h" void AddDirFileInfo(int phyParent, int logParent, int secureIndex, const NWindows::NFile::NFind::CFileInfo &fi, CObjectVector &dirItems); struct IEnumDirItemCallback { virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) = 0; }; HRESULT EnumerateItems( const NWildcard::CCensor &censor, NWildcard::ECensorPathMode pathMode, const UString &addPathPrefix, CDirItems &dirItems, IEnumDirItemCallback *callback); #endif src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp000066400000000000000000000310041325366651500214740ustar00rootroot00000000000000// Extract.cpp #include "StdAfx.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #include "../Common/ExtractingFilePath.h" #include "Extract.h" #include "SetProperties.h" using namespace NWindows; using namespace NFile; using namespace NDir; static HRESULT DecompressArchive( CCodecs *codecs, const CArchiveLink &arcLink, UInt64 packSize, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, bool calcCrc, IExtractCallbackUI *callback, CArchiveExtractCallback *ecs, UString &errorMessage, UInt64 &stdInProcessed) { const CArc &arc = arcLink.Arcs.Back(); stdInProcessed = 0; IInArchive *archive = arc.Archive; CRecordVector realIndices; UStringVector removePathParts; FString outDir = options.OutputDir; UString replaceName = arc.DefaultName; if (arcLink.Arcs.Size() > 1) { // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1". // So it extracts different archives to one folder. // We will use top level archive name const CArc &arc0 = arcLink.Arcs[0]; if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe")) replaceName = arc0.DefaultName; } outDir.Replace(FSTRING_ANY_MASK, us2fs(GetCorrectFsPath(replaceName))); bool elimIsPossible = false; UString elimPrefix; // only pure name without dir delimiter FString outDirReduced = outDir; if (options.ElimDup.Val) { UString dirPrefix; SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix); if (!elimPrefix.IsEmpty()) { if (IsCharDirLimiter(elimPrefix.Back())) elimPrefix.DeleteBack(); if (!elimPrefix.IsEmpty()) { outDirReduced = us2fs(dirPrefix); elimIsPossible = true; } } } if (!options.StdInMode) { UInt32 numItems; RINOK(archive->GetNumberOfItems(&numItems)); UString filePath; for (UInt32 i = 0; i < numItems; i++) { RINOK(arc.GetItemPath(i, filePath)); if (elimIsPossible && options.ElimDup.Val) { if (!IsPath1PrefixedByPath2(filePath, elimPrefix)) elimIsPossible = false; else { wchar_t c = filePath[elimPrefix.Len()]; if (c != 0 && !IsCharDirLimiter(c)) elimIsPossible = false; } } bool isFolder; RINOK(Archive_IsItem_Folder(archive, i, isFolder)); bool isAltStream; RINOK(Archive_IsItem_AltStream(archive, i, isAltStream)); if (!options.NtOptions.AltStreams.Val && isAltStream) continue; if (!wildcardCensor.CheckPath(isAltStream, filePath, !isFolder)) continue; realIndices.Add(i); } if (realIndices.Size() == 0) { callback->ThereAreNoFiles(); return callback->ExtractResult(S_OK); } } if (elimIsPossible) outDir = outDirReduced; #ifdef _WIN32 // GetCorrectFullFsPath doesn't like "..". // outDir.TrimRight(); // outDir = GetCorrectFullFsPath(outDir); #endif if (outDir.IsEmpty()) outDir = FString(FTEXT(".")) + FString(FSTRING_PATH_SEPARATOR); else if (!CreateComplexDir(outDir)) { HRESULT res = ::GetLastError(); if (res == S_OK) res = E_FAIL; errorMessage = ((UString)L"Can not create output directory ") + fs2us(outDir); return res; } ecs->Init( options.NtOptions, options.StdInMode ? &wildcardCensor : NULL, &arc, callback, options.StdOutMode, options.TestMode, outDir, removePathParts, packSize); #ifdef SUPPORT_LINKS if (!options.StdInMode && !options.TestMode && options.NtOptions.HardLinks.Val) { RINOK(ecs->PrepareHardLinks(&realIndices)); } #endif HRESULT result; Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0; if (options.StdInMode) { result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs); NCOM::CPropVariant prop; if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK) ConvertPropVariantToUInt64(prop, stdInProcessed); } else result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs); if (result == S_OK && !options.StdInMode) result = ecs->SetDirsTimes(); return callback->ExtractResult(result); } /* v9.31: BUG was fixed: Sorted list for file paths was sorted with case insensitive compare function. But FindInSorted function did binary search via case sensitive compare function */ int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name) { unsigned left = 0, right = fileName.Size(); while (left != right) { unsigned mid = (left + right) / 2; const UString &midValue = fileName[mid]; int compare = CompareFileNames(name, midValue); if (compare == 0) return mid; if (compare < 0) right = mid; else left = mid + 1; } return -1; } HRESULT Extract( CCodecs *codecs, const CObjectVector &types, const CIntVector &excludedFormats, UStringVector &arcPaths, UStringVector &arcPathsFull, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, IOpenCallbackUI *openCallback, IExtractCallbackUI *extractCallback, #ifndef _SFX IHashCalc *hash, #endif UString &errorMessage, CDecompressStat &stat) { stat.Clear(); UInt64 totalPackSize = 0; CRecordVector arcSizes; unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size(); unsigned i; for (i = 0; i < numArcs; i++) { NFind::CFileInfo fi; fi.Size = 0; if (!options.StdInMode) { const FString &arcPath = us2fs(arcPaths[i]); if (!fi.Find(arcPath)) throw "there is no such archive"; if (fi.IsDir()) throw "can't decompress folder"; } arcSizes.Add(fi.Size); totalPackSize += fi.Size; } CBoolArr skipArcs(numArcs); for (i = 0; i < numArcs; i++) skipArcs[i] = false; CArchiveExtractCallback *ecs = new CArchiveExtractCallback; CMyComPtr ec(ecs); bool multi = (numArcs > 1); ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode); #ifndef _SFX ecs->SetHashMethods(hash); #endif if (multi) { RINOK(extractCallback->SetTotal(totalPackSize)); } UInt64 totalPackProcessed = 0; bool thereAreNotOpenArcs = false; for (i = 0; i < numArcs; i++) { if (skipArcs[i]) continue; const UString &arcPath = arcPaths[i]; NFind::CFileInfo fi; if (options.StdInMode) { fi.Size = 0; fi.Attrib = 0; } else { if (!fi.Find(us2fs(arcPath)) || fi.IsDir()) throw "there is no such archive"; } #ifndef _NO_CRYPTO openCallback->Open_ClearPasswordWasAskedFlag(); #endif RINOK(extractCallback->BeforeOpen(arcPath)); CArchiveLink arcLink; CObjectVector types2 = types; /* #ifndef _SFX if (types.IsEmpty()) { int pos = arcPath.ReverseFind(L'.'); if (pos >= 0) { UString s = arcPath.Ptr(pos + 1); int index = codecs->FindFormatForExtension(s); if (index >= 0 && s == L"001") { s = arcPath.Left(pos); pos = s.ReverseFind(L'.'); if (pos >= 0) { int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1)); if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0 { types2.Add(index2); types2.Add(index); } } } } } #endif */ COpenOptions op; #ifndef _SFX op.props = &options.Properties; #endif op.codecs = codecs; op.types = &types2; op.excludedFormats = &excludedFormats; op.stdInMode = options.StdInMode; op.stream = NULL; op.filePath = arcPath; HRESULT result = arcLink.Open2(op, openCallback); if (result == E_ABORT) return result; bool crypted = false; #ifndef _NO_CRYPTO crypted = openCallback->Open_WasPasswordAsked(); #endif if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) result = S_FALSE; // arcLink.Set_ErrorsText(); RINOK(extractCallback->OpenResult(arcPath, result, crypted)); { FOR_VECTOR (r, arcLink.Arcs) { const CArc &arc = arcLink.Arcs[r]; const CArcErrorInfo &er = arc.ErrorInfo; if (er.IsThereErrorOrWarning()) { RINOK(extractCallback->SetError(r, arc.Path, er.GetErrorFlags(), er.ErrorMessage, er.GetWarningFlags(), er.WarningMessage)); } } } if (result != S_OK) { thereAreNotOpenArcs = true; if (!options.StdInMode) { NFind::CFileInfo fi; if (fi.Find(us2fs(arcPath))) if (!fi.IsDir()) totalPackProcessed += fi.Size; } continue; } if (!options.StdInMode) { // numVolumes += arcLink.VolumePaths.Size(); // arcLink.VolumesSize; // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes); // numArcs = arcPaths.Size(); if (arcLink.VolumePaths.Size() != 0) { Int64 correctionSize = arcLink.VolumesSize; FOR_VECTOR (v, arcLink.VolumePaths) { int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]); if (index >= 0) { if ((unsigned)index > i) { skipArcs[index] = true; correctionSize -= arcSizes[index]; } } } if (correctionSize != 0) { Int64 newPackSize = (Int64)totalPackSize + correctionSize; if (newPackSize < 0) newPackSize = 0; totalPackSize = newPackSize; RINOK(extractCallback->SetTotal(totalPackSize)); } } } #ifndef _NO_CRYPTO bool passwordIsDefined; UString password; RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password)); if (passwordIsDefined) { RINOK(extractCallback->SetPassword(password)); } #endif FOR_VECTOR (k, arcLink.Arcs) { const CArc &arc = arcLink.Arcs[k]; const CArcErrorInfo &er = arc.ErrorInfo; if (er.ErrorFormatIndex >= 0) { RINOK(extractCallback->OpenTypeWarning(arc.Path, codecs->GetFormatNamePtr(arc.FormatIndex), codecs->GetFormatNamePtr(er.ErrorFormatIndex))) /* UString s = L"Can not open the file as [" + codecs->Formats[arc.ErrorFormatIndex].Name + L"] archive\n"; s += L"The file is open as [" + codecs->Formats[arc.FormatIndex].Name + L"] archive"; RINOK(extractCallback->MessageError(s)); */ } { const UString &s = er.ErrorMessage; if (!s.IsEmpty()) { RINOK(extractCallback->MessageError(s)); } } } CArc &arc = arcLink.Arcs.Back(); arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice); arc.MTime = fi.MTime; UInt64 packProcessed; bool calcCrc = #ifndef _SFX (hash != NULL); #else false; #endif RINOK(DecompressArchive( codecs, arcLink, fi.Size + arcLink.VolumesSize, wildcardCensor, options, calcCrc, extractCallback, ecs, errorMessage, packProcessed)); if (!options.StdInMode) packProcessed = fi.Size + arcLink.VolumesSize; totalPackProcessed += packProcessed; ecs->LocalProgressSpec->InSize += packProcessed; ecs->LocalProgressSpec->OutSize = ecs->UnpackSize; if (!errorMessage.IsEmpty()) return E_FAIL; } if (multi || thereAreNotOpenArcs) { RINOK(extractCallback->SetTotal(totalPackSize)); RINOK(extractCallback->SetCompleted(&totalPackProcessed)); } stat.NumFolders = ecs->NumFolders; stat.NumFiles = ecs->NumFiles; stat.NumAltStreams = ecs->NumAltStreams; stat.UnpackSize = ecs->UnpackSize; stat.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize; stat.NumArchives = arcPaths.Size(); stat.PackSize = ecs->LocalProgressSpec->InSize; return S_OK; } src/libs/7zip/win/CPP/7zip/UI/Common/Extract.h000066400000000000000000000037771325366651500211610ustar00rootroot00000000000000// Extract.h #ifndef __EXTRACT_H #define __EXTRACT_H #include "../../../Windows/FileFind.h" #include "../../Archive/IArchive.h" #include "ArchiveExtractCallback.h" #include "ArchiveOpenCallback.h" #include "ExtractMode.h" #include "Property.h" #include "../Common/LoadCodecs.h" struct CExtractOptionsBase { CBoolPair ElimDup; bool PathMode_Force; bool OverwriteMode_Force; NExtract::NPathMode::EEnum PathMode; NExtract::NOverwriteMode::EEnum OverwriteMode; FString OutputDir; CExtractNtOptions NtOptions; CExtractOptionsBase(): PathMode_Force(false), OverwriteMode_Force(false), PathMode(NExtract::NPathMode::kFullPaths), OverwriteMode(NExtract::NOverwriteMode::kAsk) {} }; struct CExtractOptions: public CExtractOptionsBase { bool StdInMode; bool StdOutMode; bool YesToAll; bool TestMode; // bool ShowDialog; // bool PasswordEnabled; // UString Password; #ifndef _SFX CObjectVector Properties; #endif #ifdef EXTERNAL_CODECS CCodecs *Codecs; #endif CExtractOptions(): TestMode(false), StdInMode(false), StdOutMode(false), YesToAll(false) {} }; struct CDecompressStat { UInt64 NumArchives; UInt64 UnpackSize; UInt64 AltStreams_UnpackSize; UInt64 PackSize; UInt64 NumFolders; UInt64 NumFiles; UInt64 NumAltStreams; void Clear() { NumArchives = UnpackSize = AltStreams_UnpackSize = PackSize = NumFolders = NumFiles = NumAltStreams = 0; } }; HRESULT Extract( CCodecs *codecs, const CObjectVector &types, const CIntVector &excludedFormats, UStringVector &archivePaths, UStringVector &archivePathsFull, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, IOpenCallbackUI *openCallback, IExtractCallbackUI *extractCallback, #ifndef _SFX IHashCalc *hash, #endif UString &errorMessage, CDecompressStat &stat); #endif src/libs/7zip/win/CPP/7zip/UI/Common/ExtractMode.h000066400000000000000000000005521325366651500217520ustar00rootroot00000000000000// ExtractMode.h #ifndef __EXTRACT_MODE_H #define __EXTRACT_MODE_H namespace NExtract { namespace NPathMode { enum EEnum { kFullPaths, kCurPaths, kNoPaths, kAbsPaths }; } namespace NOverwriteMode { enum EEnum { kAsk, kOverwrite, kSkip, kRename, kRenameExisting }; } } #endif src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.cpp000066400000000000000000000102511325366651500236100ustar00rootroot00000000000000// ExtractingFilePath.cpp #include "StdAfx.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileName.h" #include "ExtractingFilePath.h" static UString ReplaceIncorrectChars(const UString &s, bool repaceColon) { #ifdef _WIN32 UString res; bool beforeColon = true; { for (unsigned i = 0; i < s.Len(); i++) { wchar_t c = s[i]; if (beforeColon) if (c == '*' || c == '?' || c < 0x20 || c == '<' || c == '>' || c == '|' || c == '"') c = '_'; if (c == ':') { if (repaceColon) c = '_'; else beforeColon = false; } res += c; } } if (beforeColon) { for (int i = res.Len() - 1; i >= 0; i--) { wchar_t c = res[i]; if (c != '.' && c != ' ') break; res.ReplaceOneCharAtPos(i, '_'); } } return res; #else return s; #endif } #ifdef _WIN32 static const wchar_t *g_ReservedNames[] = { L"CON", L"PRN", L"AUX", L"NUL" }; static bool CheckTail(const UString &name, unsigned len) { int dotPos = name.Find(L'.'); if (dotPos < 0) dotPos = name.Len(); UString s = name.Left(dotPos); s.TrimRight(); return s.Len() != len; } static bool CheckNameNum(const UString &name, const wchar_t *reservedName) { unsigned len = MyStringLen(reservedName); if (name.Len() <= len) return true; if (MyStringCompareNoCase_N(name, reservedName, len) != 0) return true; wchar_t c = name[len]; if (c < L'0' || c > L'9') return true; return CheckTail(name, len + 1); } static bool IsSupportedName(const UString &name) { for (unsigned i = 0; i < ARRAY_SIZE(g_ReservedNames); i++) { const wchar_t *reservedName = g_ReservedNames[i]; unsigned len = MyStringLen(reservedName); if (name.Len() < len) continue; if (MyStringCompareNoCase_N(name, reservedName, len) != 0) continue; if (!CheckTail(name, len)) return false; } if (!CheckNameNum(name, L"COM")) return false; return CheckNameNum(name, L"LPT"); } #endif static UString GetCorrectFileName(const UString &path, bool repaceColon) { if (path == L".." || path == L".") return UString(); return ReplaceIncorrectChars(path, repaceColon); } void MakeCorrectPath(bool isPathFromRoot, UStringVector &pathParts, bool replaceAltStreamColon) { for (unsigned i = 0; i < pathParts.Size();) { UString &s = pathParts[i]; #ifdef _WIN32 bool needReplaceColon = (replaceAltStreamColon || i != pathParts.Size() - 1); if (i == 0 && isPathFromRoot && NWindows::NFile::NName::IsDrivePath(s)) { UString s2 = s[0]; s2 += L'_'; s2 += GetCorrectFileName(s.Ptr(2), needReplaceColon); s = s2; } else s = GetCorrectFileName(s, needReplaceColon); #endif if (s.IsEmpty()) pathParts.Delete(i); else { #ifdef _WIN32 if (!IsSupportedName(s)) s = (UString)L"_" + s; #endif i++; } } } UString MakePathNameFromParts(const UStringVector &parts) { UString result; FOR_VECTOR (i, parts) { if (i != 0) result += WCHAR_PATH_SEPARATOR; result += parts[i]; } return result; } static const wchar_t *k_EmptyReplaceName = L"[]"; void Correct_IfEmptyLastPart(UStringVector &parts) { if (parts.IsEmpty()) parts.Add(k_EmptyReplaceName); else { UString &s = parts.Back(); if (s.IsEmpty()) s = k_EmptyReplaceName; } } UString GetCorrectFsPath(const UString &path) { UString res = GetCorrectFileName(path, true); #ifdef _WIN32 if (!IsSupportedName(res)) res = (UString)L"_" + res; #endif if (res.IsEmpty()) res = k_EmptyReplaceName; return res; } UString GetCorrectFullFsPath(const UString &path) { UStringVector parts; SplitPathToParts(path, parts); FOR_VECTOR (i, parts) { UString &s = parts[i]; #ifdef _WIN32 while (!s.IsEmpty() && (s.Back() == '.' || s.Back() == ' ')) s.DeleteBack(); if (!IsSupportedName(s)) s.InsertAtFront(L'_'); #endif } return MakePathNameFromParts(parts); } src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.h000066400000000000000000000012311325366651500232530ustar00rootroot00000000000000// ExtractingFilePath.h #ifndef __EXTRACTING_FILE_PATH_H #define __EXTRACTING_FILE_PATH_H #include "../../../Common/MyString.h" UString MakePathNameFromParts(const UStringVector &parts); /* for WIN32: if (isRoot == true), and pathParts[0] contains path like "c:name", it thinks that "c:" is drive prefix (it's not ":name alt stream) and the function changes part to c_name */ void MakeCorrectPath(bool isPathFromRoot, UStringVector &pathParts, bool replaceAltStreamColon); UString GetCorrectFsPath(const UString &path); UString GetCorrectFullFsPath(const UString &path); void Correct_IfEmptyLastPart(UStringVector &parts); #endif src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.cpp000066400000000000000000000211101325366651500215250ustar00rootroot00000000000000// HashCalc.cpp #include "StdAfx.h" #include "../../../../C/Alloc.h" #include "../../../Common/StringToInt.h" #include "../../Common/FileStreams.h" #include "../../Common/StreamUtils.h" #include "EnumDirItems.h" #include "HashCalc.h" using namespace NWindows; class CHashMidBuf { void *_data; public: CHashMidBuf(): _data(0) {} operator void *() { return _data; } bool Alloc(size_t size) { if (_data != 0) return false; _data = ::MidAlloc(size); return _data != 0; } ~CHashMidBuf() { ::MidFree(_data); } }; struct CEnumDirItemCallback_Hash: public IEnumDirItemCallback { IHashCallbackUI *Callback; HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) { return Callback->ScanProgress(numFolders, numFiles, totalSize, path, isDir); } }; static const wchar_t *k_DefaultHashMethod = L"CRC32"; HRESULT CHashBundle::SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &hashMethods) { UStringVector names = hashMethods; if (names.IsEmpty()) names.Add(k_DefaultHashMethod); CRecordVector ids; CObjectVector methods; unsigned i; for (i = 0; i < names.Size(); i++) { COneMethodInfo m; RINOK(m.ParseMethodFromString(names[i])); if (m.MethodName.IsEmpty()) m.MethodName = k_DefaultHashMethod; if (m.MethodName == L"*") { CRecordVector tempMethods; GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods); methods.Clear(); ids.Clear(); FOR_VECTOR (t, tempMethods) { int index = ids.AddToUniqueSorted(tempMethods[t]); if (ids.Size() != methods.Size()) methods.Insert(index, m); } break; } else { // m.MethodName.RemoveChar(L'-'); CMethodId id; if (!FindHashMethod(EXTERNAL_CODECS_LOC_VARS m.MethodName, id)) return E_NOTIMPL; int index = ids.AddToUniqueSorted(id); if (ids.Size() != methods.Size()) methods.Insert(index, m); } } for (i = 0; i < ids.Size(); i++) { CMyComPtr hasher; UString name; RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS ids[i], name, hasher)); if (!hasher) throw "Can't create hasher"; const COneMethodInfo &m = methods[i]; { CMyComPtr scp; hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp); if (scp) { RINOK(m.SetCoderProps(scp, NULL)); } } UInt32 digestSize = hasher->GetDigestSize(); if (digestSize > k_HashCalc_DigestSize_Max) return E_NOTIMPL; CHasherState &h = Hashers.AddNew(); h.Hasher = hasher; h.Name = name; h.DigestSize = digestSize; for (int i = 0; i < k_HashCalc_NumGroups; i++) memset(h.Digests[i], 0, digestSize); } return S_OK; } void CHashBundle::InitForNewFile() { CurSize = 0; FOR_VECTOR (i, Hashers) { CHasherState &h = Hashers[i]; h.Hasher->Init(); memset(h.Digests[k_HashCalc_Index_Current], 0, h.DigestSize); } } void CHashBundle::Update(const void *data, UInt32 size) { CurSize += size; FOR_VECTOR (i, Hashers) Hashers[i].Hasher->Update(data, size); } void CHashBundle::SetSize(UInt64 size) { CurSize = size; } static void AddDigests(Byte *dest, const Byte *src, UInt32 size) { unsigned next = 0; for (UInt32 i = 0; i < size; i++) { next += (unsigned)dest[i] + (unsigned)src[i]; dest[i] = (Byte)next; next >>= 8; } } void CHashBundle::Final(bool isDir, bool isAltStream, const UString &path) { if (isDir) NumDirs++; else if (isAltStream) { NumAltStreams++; AltStreamsSize += CurSize; } else { NumFiles++; FilesSize += CurSize; } Byte pre[16]; memset(pre, 0, sizeof(pre)); if (isDir) pre[0] = 1; FOR_VECTOR (i, Hashers) { CHasherState &h = Hashers[i]; if (!isDir) { h.Hasher->Final(h.Digests[0]); if (!isAltStream) AddDigests(h.Digests[k_HashCalc_Index_DataSum], h.Digests[0], h.DigestSize); } h.Hasher->Init(); h.Hasher->Update(pre, sizeof(pre)); h.Hasher->Update(h.Digests[0], h.DigestSize); for (unsigned k = 0; k < path.Len(); k++) { wchar_t c = path[k]; Byte temp[2] = { (Byte)(c & 0xFF), (Byte)((c >> 8) & 0xFF) }; h.Hasher->Update(temp, 2); } Byte tempDigest[k_HashCalc_DigestSize_Max]; h.Hasher->Final(tempDigest); if (!isAltStream) AddDigests(h.Digests[k_HashCalc_Index_NamesSum], tempDigest, h.DigestSize); AddDigests(h.Digests[k_HashCalc_Index_StreamsSum], tempDigest, h.DigestSize); } } HRESULT HashCalc( DECL_EXTERNAL_CODECS_LOC_VARS const NWildcard::CCensor &censor, const CHashOptions &options, UString &errorInfo, IHashCallbackUI *callback) { CDirItems dirItems; UInt64 numErrors = 0; UInt64 totalBytes = 0; if (options.StdInMode) { CDirItem di; di.Size = (UInt64)(Int64)-1; di.Attrib = 0; di.MTime.dwLowDateTime = 0; di.MTime.dwHighDateTime = 0; di.CTime = di.ATime = di.MTime; dirItems.Items.Add(di); } else { CEnumDirItemCallback_Hash enumCallback; enumCallback.Callback = callback; RINOK(callback->StartScanning()); dirItems.ScanAltStreams = options.AltStreamsMode; HRESULT res = EnumerateItems(censor, options.PathMode, UString(), dirItems, &enumCallback); totalBytes = dirItems.TotalSize; FOR_VECTOR (i, dirItems.ErrorPaths) { RINOK(callback->CanNotFindError(fs2us(dirItems.ErrorPaths[i]), dirItems.ErrorCodes[i])); } numErrors = dirItems.ErrorPaths.Size(); if (res != S_OK) { if (res != E_ABORT) errorInfo = L"Scanning error"; return res; } RINOK(callback->FinishScanning()); } unsigned i; CHashBundle hb; RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS options.Methods)); hb.Init(); hb.NumErrors = numErrors; if (options.StdInMode) { RINOK(callback->SetNumFiles(1)); } else { RINOK(callback->SetTotal(totalBytes)); } const UInt32 kBufSize = 1 << 15; CHashMidBuf buf; if (!buf.Alloc(kBufSize)) return E_OUTOFMEMORY; UInt64 completeValue = 0; RINOK(callback->BeforeFirstFile(hb)); for (i = 0; i < dirItems.Items.Size(); i++) { CMyComPtr inStream; UString path; bool isDir = false; bool isAltStream = false; if (options.StdInMode) { inStream = new CStdInFileStream; } else { CInFileStream *inStreamSpec = new CInFileStream; inStream = inStreamSpec; const CDirItem &dirItem = dirItems.Items[i]; isDir = dirItem.IsDir(); isAltStream = dirItem.IsAltStream; path = dirItems.GetLogPath(i); if (!isDir) { UString phyPath = dirItems.GetPhyPath(i); if (!inStreamSpec->OpenShared(us2fs(phyPath), options.OpenShareForWrite)) { HRESULT res = callback->OpenFileError(phyPath, ::GetLastError()); hb.NumErrors++; if (res != S_FALSE) return res; continue; } } } RINOK(callback->GetStream(path, isDir)); UInt64 fileSize = 0; hb.InitForNewFile(); if (!isDir) { for (UInt32 step = 0;; step++) { if ((step & 0xFF) == 0) RINOK(callback->SetCompleted(&completeValue)); UInt32 size; RINOK(inStream->Read(buf, kBufSize, &size)); if (size == 0) break; hb.Update(buf, size); fileSize += size; completeValue += size; } } hb.Final(isDir, isAltStream, path); RINOK(callback->SetOperationResult(fileSize, hb, !isDir)); RINOK(callback->SetCompleted(&completeValue)); } return callback->AfterLastFile(hb); } static inline char GetHex(Byte value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } void AddHashHexToString(char *dest, const Byte *data, UInt32 size) { dest[size * 2] = 0; if (!data) { for (UInt32 i = 0; i < size; i++) { dest[0] = ' '; dest[1] = ' '; dest += 2; } return; } int step = 2; if (size <= 8) { step = -2; dest += size * 2 - 2; } for (UInt32 i = 0; i < size; i++) { Byte b = data[i]; dest[0] = GetHex((Byte)((b >> 4) & 0xF)); dest[1] = GetHex((Byte)(b & 0xF)); dest += step; } } src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.h000066400000000000000000000056451325366651500212110ustar00rootroot00000000000000// HashCalc.h #ifndef __HASH_CALC_H #define __HASH_CALC_H #include "../../../Common/Wildcard.h" #include "../../Common/CreateCoder.h" #include "../../Common/MethodProps.h" #include "Property.h" const unsigned k_HashCalc_DigestSize_Max = 64; const unsigned k_HashCalc_NumGroups = 4; enum { k_HashCalc_Index_Current, k_HashCalc_Index_DataSum, k_HashCalc_Index_NamesSum, k_HashCalc_Index_StreamsSum }; struct CHasherState { CMyComPtr Hasher; UString Name; UInt32 DigestSize; Byte Digests[k_HashCalc_NumGroups][k_HashCalc_DigestSize_Max]; }; struct IHashCalc { virtual void InitForNewFile() = 0; virtual void Update(const void *data, UInt32 size) = 0; virtual void SetSize(UInt64 size) = 0; virtual void Final(bool isDir, bool isAltStream, const UString &path) = 0; }; struct CHashBundle: public IHashCalc { CObjectVector Hashers; UInt64 NumFiles; UInt64 NumDirs; UInt64 NumAltStreams; UInt64 FilesSize; UInt64 AltStreamsSize; UInt64 NumErrors; UInt64 CurSize; HRESULT SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &methods); void Init() { NumFiles = NumDirs = NumAltStreams = FilesSize = AltStreamsSize = NumErrors = 0; } void InitForNewFile(); void Update(const void *data, UInt32 size); void SetSize(UInt64 size); void Final(bool isDir, bool isAltStream, const UString &path); }; #define INTERFACE_IHashCallbackUI(x) \ virtual HRESULT StartScanning() x; \ virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) x; \ virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT FinishScanning() x; \ virtual HRESULT SetNumFiles(UInt64 numFiles) x; \ virtual HRESULT SetTotal(UInt64 size) x; \ virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \ virtual HRESULT CheckBreak() x; \ virtual HRESULT BeforeFirstFile(const CHashBundle &hb) x; \ virtual HRESULT GetStream(const wchar_t *name, bool isFolder) x; \ virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT SetOperationResult(UInt64 fileSize, const CHashBundle &hb, bool showHash) x; \ virtual HRESULT AfterLastFile(const CHashBundle &hb) x; \ struct IHashCallbackUI { INTERFACE_IHashCallbackUI(=0) }; struct CHashOptions { UStringVector Methods; bool OpenShareForWrite; bool StdInMode; bool AltStreamsMode; NWildcard::ECensorPathMode PathMode; CHashOptions(): StdInMode(false), OpenShareForWrite(false), AltStreamsMode(false), PathMode(NWildcard::k_RelatPath) {}; }; HRESULT HashCalc( DECL_EXTERNAL_CODECS_LOC_VARS const NWildcard::CCensor &censor, const CHashOptions &options, UString &errorInfo, IHashCallbackUI *callback); void AddHashHexToString(char *dest, const Byte *data, UInt32 size); #endif src/libs/7zip/win/CPP/7zip/UI/Common/IFileExtractCallback.h000066400000000000000000000043541325366651500234770ustar00rootroot00000000000000// IFileExtractCallback.h #ifndef __I_FILE_EXTRACT_CALLBACK_H #define __I_FILE_EXTRACT_CALLBACK_H #include "../../../Common/MyString.h" #include "../../IDecl.h" namespace NOverwriteAnswer { enum EEnum { kYes, kYesToAll, kNo, kNoToAll, kAutoRename, kCancel }; } DECL_INTERFACE_SUB(IFolderArchiveExtractCallback, IProgress, 0x01, 0x07) { public: STDMETHOD(AskOverwrite)( const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, Int32 *answer) PURE; STDMETHOD(PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position) PURE; STDMETHOD(MessageError)(const wchar_t *message) PURE; STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted) PURE; }; struct IExtractCallbackUI: IFolderArchiveExtractCallback { virtual HRESULT BeforeOpen(const wchar_t *name) = 0; virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted) = 0; virtual HRESULT SetError(int level, const wchar_t *name, UInt32 errorFlags, const wchar_t *errors, UInt32 warningFlags, const wchar_t *warnings) = 0; virtual HRESULT ThereAreNoFiles() = 0; virtual HRESULT ExtractResult(HRESULT result) = 0; virtual HRESULT OpenTypeWarning(const wchar_t *name, const wchar_t *okType, const wchar_t *errorType) = 0; #ifndef _NO_CRYPTO virtual HRESULT SetPassword(const UString &password) = 0; #endif }; #define INTERFACE_IGetProp(x) \ STDMETHOD(GetProp)(PROPID propID, PROPVARIANT *value) x; \ DECL_INTERFACE_SUB(IGetProp, IUnknown, 0x01, 0x20) { INTERFACE_IGetProp(PURE) }; #define INTERFACE_IFolderExtractToStreamCallback(x) \ STDMETHOD(UseExtractToStream)(Int32 *res) x; \ STDMETHOD(GetStream7)(const wchar_t *name, Int32 isDir, ISequentialOutStream **outStream, Int32 askExtractMode, IGetProp *getProp) x; \ STDMETHOD(PrepareOperation7)(Int32 askExtractMode) x; \ STDMETHOD(SetOperationResult7)(Int32 resultEOperationResult, bool encrypted) x; \ DECL_INTERFACE_SUB(IFolderExtractToStreamCallback, IUnknown, 0x01, 0x30) { INTERFACE_IFolderExtractToStreamCallback(PURE) }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.cpp000066400000000000000000000545451325366651500221010ustar00rootroot00000000000000// LoadCodecs.cpp #include "StdAfx.h" #include "../../../../C/7zVersion.h" #include "../../../Common/MyCom.h" #include "../../../Common/StringToInt.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/PropVariant.h" #include "LoadCodecs.h" using namespace NWindows; #ifdef NEW_FOLDER_INTERFACE #include "../../../Common/StringToInt.h" #endif #include "../../ICoder.h" #include "../../Common/RegisterArc.h" #ifdef EXTERNAL_CODECS #include "../../../Windows/FileFind.h" #include "../../../Windows/DLL.h" #ifdef NEW_FOLDER_INTERFACE #include "../../../Windows/ResourceString.h" static const UINT kIconTypesResId = 100; #endif #ifdef _WIN32 #include "../../../Windows/FileName.h" #include "../../../Windows/Registry.h" #endif using namespace NFile; #ifdef _WIN32 extern HINSTANCE g_hInstance; #endif #define kCodecsFolderName FTEXT("Codecs") #define kFormatsFolderName FTEXT("Formats") static CFSTR kMainDll = FTEXT("7z.dll"); #ifdef _WIN32 static LPCTSTR kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip"); static LPCWSTR kProgramPathValue = L"Path"; static LPCWSTR kProgramPath2Value = L"Path" #ifdef _WIN64 L"64"; #else L"32"; #endif static bool ReadPathFromRegistry(HKEY baseKey, LPCWSTR value, FString &path) { NRegistry::CKey key; if (key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS) { UString pathU; if (key.QueryValue(value, pathU) == ERROR_SUCCESS) { path = us2fs(pathU); NName::NormalizeDirPathPrefix(path); return NFind::DoesFileExist(path + kMainDll); } } return false; } #endif // _WIN32 #endif // EXTERNAL_CODECS static const unsigned kNumArcsMax = 48; static unsigned g_NumArcs = 0; static const CArcInfo *g_Arcs[kNumArcsMax]; void RegisterArc(const CArcInfo *arcInfo) throw() { if (g_NumArcs < kNumArcsMax) { g_Arcs[g_NumArcs] = arcInfo; g_NumArcs++; } } static void SplitString(const UString &srcString, UStringVector &destStrings) { destStrings.Clear(); UString s; unsigned len = srcString.Len(); if (len == 0) return; for (unsigned i = 0; i < len; i++) { wchar_t c = srcString[i]; if (c == L' ') { if (!s.IsEmpty()) { destStrings.Add(s); s.Empty(); } } else s += c; } if (!s.IsEmpty()) destStrings.Add(s); } int CArcInfoEx::FindExtension(const UString &ext) const { FOR_VECTOR (i, Exts) if (ext.IsEqualToNoCase(Exts[i].Ext)) return i; return -1; } void CArcInfoEx::AddExts(const UString &ext, const UString &addExt) { UStringVector exts, addExts; SplitString(ext, exts); SplitString(addExt, addExts); FOR_VECTOR (i, exts) { CArcExtInfo extInfo; extInfo.Ext = exts[i]; if (i < addExts.Size()) { extInfo.AddExt = addExts[i]; if (extInfo.AddExt == L"*") extInfo.AddExt.Empty(); } Exts.Add(extInfo); } } #ifndef _SFX static bool ParseSignatures(const Byte *data, unsigned size, CObjectVector &signatures) { signatures.Clear(); while (size > 0) { unsigned len = *data++; size--; if (len > size) return false; signatures.AddNew().CopyFrom(data, len); data += len; size -= len; } return true; } #endif // _SFX #ifdef EXTERNAL_CODECS static FString GetBaseFolderPrefixFromRegistry() { FString moduleFolderPrefix = NDLL::GetModuleDirPrefix(); #ifdef _WIN32 if (!NFind::DoesFileExist(moduleFolderPrefix + kMainDll) && !NFind::DoesDirExist(moduleFolderPrefix + kCodecsFolderName) && !NFind::DoesDirExist(moduleFolderPrefix + kFormatsFolderName)) { FString path; if (ReadPathFromRegistry(HKEY_CURRENT_USER, kProgramPath2Value, path)) return path; if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPath2Value, path)) return path; if (ReadPathFromRegistry(HKEY_CURRENT_USER, kProgramPathValue, path)) return path; if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPathValue, path)) return path; } #endif return moduleFolderPrefix; } static HRESULT GetCoderClass(Func_GetMethodProperty getMethodProperty, UInt32 index, PROPID propId, CLSID &clsId, bool &isAssigned) { NCOM::CPropVariant prop; isAssigned = false; RINOK(getMethodProperty(index, propId, &prop)); if (prop.vt == VT_BSTR) { if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID)) return E_FAIL; isAssigned = true; clsId = *(const GUID *)prop.bstrVal; } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } HRESULT CCodecs::LoadCodecs() { CCodecLib &lib = Libs.Back(); lib.GetMethodProperty = (Func_GetMethodProperty)lib.Lib.GetProc("GetMethodProperty"); if (lib.GetMethodProperty) { UInt32 numMethods = 1; Func_GetNumberOfMethods getNumberOfMethodsFunc = (Func_GetNumberOfMethods)lib.Lib.GetProc("GetNumberOfMethods"); if (getNumberOfMethodsFunc) { RINOK(getNumberOfMethodsFunc(&numMethods)); } for (UInt32 i = 0; i < numMethods; i++) { CDllCodecInfo info; info.LibIndex = Libs.Size() - 1; info.CodecIndex = i; RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned)); RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned)); Codecs.Add(info); } } Func_GetHashers getHashers = (Func_GetHashers)lib.Lib.GetProc("GetHashers"); if (getHashers) { RINOK(getHashers(&lib.Hashers)); if (lib.Hashers) { UInt32 numMethods = lib.Hashers->GetNumHashers(); for (UInt32 i = 0; i < numMethods; i++) { CDllHasherInfo info; info.LibIndex = Libs.Size() - 1; info.HasherIndex = i; Hashers.Add(info); } } } return S_OK; } static HRESULT GetProp( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, NCOM::CPropVariant &prop) { if (getProp2) return getProp2(index, propID, &prop);; return getProp(propID, &prop); } static HRESULT GetProp_Bool( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, bool &res) { res = false; NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_BOOL) res = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT GetProp_UInt32( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, UInt32 &res, bool &defined) { res = 0; defined = false; NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_UI4) { res = prop.ulVal; defined = true; } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT GetProp_String( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, UString &res) { res.Empty(); NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_BSTR) res = prop.bstrVal; else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT GetProp_RawData( Func_GetHandlerProperty getProp, Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, CByteBuffer &bb) { bb.Free(); NCOM::CPropVariant prop; RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_BSTR) { UINT len = ::SysStringByteLen(prop.bstrVal); bb.CopyFrom((const Byte *)prop.bstrVal, len); } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static const UInt32 kArcFlagsPars[] = { NArchive::NHandlerPropID::kKeepName, NArcInfoFlags::kKeepName, NArchive::NHandlerPropID::kAltStreams, NArcInfoFlags::kAltStreams, NArchive::NHandlerPropID::kNtSecure, NArcInfoFlags::kNtSecure }; HRESULT CCodecs::LoadFormats() { const NDLL::CLibrary &lib = Libs.Back().Lib; Func_GetHandlerProperty getProp = NULL; Func_GetHandlerProperty2 getProp2 = (Func_GetHandlerProperty2)lib.GetProc("GetHandlerProperty2"); Func_GetIsArc getIsArc = (Func_GetIsArc)lib.GetProc("GetIsArc"); UInt32 numFormats = 1; if (getProp2) { Func_GetNumberOfFormats getNumberOfFormats = (Func_GetNumberOfFormats)lib.GetProc("GetNumberOfFormats"); if (getNumberOfFormats) { RINOK(getNumberOfFormats(&numFormats)); } } else { getProp = (Func_GetHandlerProperty)lib.GetProc("GetHandlerProperty"); if (!getProp) return S_OK; } for (UInt32 i = 0; i < numFormats; i++) { CArcInfoEx item; item.LibIndex = Libs.Size() - 1; item.FormatIndex = i; RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kName, item.Name)); { NCOM::CPropVariant prop; if (GetProp(getProp, getProp2, i, NArchive::NHandlerPropID::kClassID, prop) != S_OK) continue; if (prop.vt != VT_BSTR) continue; if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID)) return E_FAIL; item.ClassID = *(const GUID *)prop.bstrVal; prop.Clear(); } UString ext, addExt; RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kExtension, ext)); RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kAddExtension, addExt)); item.AddExts(ext, addExt); GetProp_Bool(getProp, getProp2, i, NArchive::NHandlerPropID::kUpdate, item.UpdateEnabled); bool flags_Defined = false; RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kFlags, item.Flags, flags_Defined)); item.NewInterface = flags_Defined; if (!flags_Defined) // && item.UpdateEnabled { // support for DLL version before 9.31: for (unsigned j = 0; j < ARRAY_SIZE(kArcFlagsPars); j += 2) { bool val = false; GetProp_Bool(getProp, getProp2, i, kArcFlagsPars[j], val); if (val) item.Flags |= kArcFlagsPars[j + 1]; } } CByteBuffer sig; RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kSignature, sig)); if (sig.Size() != 0) item.Signatures.Add(sig); else { RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kMultiSignature, sig)); ParseSignatures(sig, (unsigned)sig.Size(), item.Signatures); } bool signatureOffset_Defined; RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kSignatureOffset, item.SignatureOffset, signatureOffset_Defined)); // bool version_Defined; // RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kVersion, item.Version, version_Defined)); if (getIsArc) getIsArc(i, &item.IsArcFunc); Formats.Add(item); } return S_OK; } #ifdef NEW_FOLDER_INTERFACE void CCodecIcons::LoadIcons(HMODULE m) { UString iconTypes; MyLoadString(m, kIconTypesResId, iconTypes); UStringVector pairs; SplitString(iconTypes, pairs); FOR_VECTOR (i, pairs) { const UString &s = pairs[i]; int pos = s.Find(L':'); CIconPair iconPair; iconPair.IconIndex = -1; if (pos < 0) pos = s.Len(); else { UString num = s.Ptr(pos + 1); if (!num.IsEmpty()) { const wchar_t *end; iconPair.IconIndex = ConvertStringToUInt32(num, &end); if (*end != 0) continue; } } iconPair.Ext = s.Left(pos); IconPairs.Add(iconPair); } } bool CCodecIcons::FindIconIndex(const UString &ext, int &iconIndex) const { iconIndex = -1; FOR_VECTOR (i, IconPairs) { const CIconPair &pair = IconPairs[i]; if (ext.IsEqualToNoCase(pair.Ext)) { iconIndex = pair.IconIndex; return true; } } return false; } #endif // EXTERNAL_CODECS #ifdef _7ZIP_LARGE_PAGES extern "C" { extern SIZE_T g_LargePageSize; } #endif HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll) { if (needCheckDll) { NDLL::CLibrary library; if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE)) return S_OK; } Libs.Add(CCodecLib()); CCodecLib &lib = Libs.Back(); lib.Path = dllPath; bool used = false; HRESULT res = S_OK; if (lib.Lib.Load(dllPath)) { #ifdef NEW_FOLDER_INTERFACE lib.LoadIcons(); #endif #ifdef _7ZIP_LARGE_PAGES if (g_LargePageSize != 0) { Func_SetLargePageMode setLargePageMode = (Func_SetLargePageMode)lib.Lib.GetProc("SetLargePageMode"); if (setLargePageMode) setLargePageMode(); } #endif if (CaseSensitiveChange) { Func_SetCaseSensitive setCaseSensitive = (Func_SetCaseSensitive)lib.Lib.GetProc("SetCaseSensitive"); if (setCaseSensitive) setCaseSensitive(CaseSensitive ? 1 : 0); } lib.CreateObject = (Func_CreateObject)lib.Lib.GetProc("CreateObject"); if (lib.CreateObject) { unsigned startSize = Codecs.Size() + Hashers.Size(); res = LoadCodecs(); used = (startSize != Codecs.Size() + Hashers.Size()); if (res == S_OK) { startSize = Formats.Size(); res = LoadFormats(); if (startSize != Formats.Size()) used = true; } } } if (!used) Libs.DeleteBack(); return res; } HRESULT CCodecs::LoadDllsFromFolder(const FString &folderPrefix) { NFile::NFind::CEnumerator enumerator(folderPrefix + FCHAR_ANY_MASK); NFile::NFind::CFileInfo fi; while (enumerator.Next(fi)) { if (fi.IsDir()) continue; RINOK(LoadDll(folderPrefix + fi.Name, true)); } return S_OK; } #endif HRESULT CCodecs::Load() { #ifdef NEW_FOLDER_INTERFACE InternalIcons.LoadIcons(g_hInstance); #endif Formats.Clear(); #ifdef EXTERNAL_CODECS Codecs.Clear(); Hashers.Clear(); #endif for (UInt32 i = 0; i < g_NumArcs; i++) { const CArcInfo &arc = *g_Arcs[i]; CArcInfoEx item; item.Name.SetFromAscii(arc.Name); item.CreateInArchive = arc.CreateInArchive; item.IsArcFunc = arc.IsArc; item.Flags = arc.Flags; { UString e, ae; if (arc.Ext) e.SetFromAscii(arc.Ext); if (arc.AddExt) ae.SetFromAscii(arc.AddExt); item.AddExts(e, ae); } #ifndef _SFX item.CreateOutArchive = arc.CreateOutArchive; item.UpdateEnabled = (arc.CreateOutArchive != NULL); item.SignatureOffset = arc.SignatureOffset; // item.Version = MY_VER_MIX; item.NewInterface = true; if (arc.IsMultiSignature()) ParseSignatures(arc.Signature, arc.SignatureSize, item.Signatures); else item.Signatures.AddNew().CopyFrom(arc.Signature, arc.SignatureSize); #endif Formats.Add(item); } #ifdef EXTERNAL_CODECS const FString baseFolder = GetBaseFolderPrefixFromRegistry(); RINOK(LoadDll(baseFolder + kMainDll, false)); RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName FSTRING_PATH_SEPARATOR)); RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName FSTRING_PATH_SEPARATOR)); #endif return S_OK; } #ifndef _SFX int CCodecs::FindFormatForArchiveName(const UString &arcPath) const { int slashPos = arcPath.ReverseFind(WCHAR_PATH_SEPARATOR); int dotPos = arcPath.ReverseFind(L'.'); if (dotPos < 0 || dotPos < slashPos) return -1; const UString ext = arcPath.Ptr(dotPos + 1); if (ext.IsEmpty()) return -1; if (ext.IsEqualToNoCase(L"exe")) return -1; FOR_VECTOR (i, Formats) { const CArcInfoEx &arc = Formats[i]; /* if (!arc.UpdateEnabled) continue; */ if (arc.FindExtension(ext) >= 0) return i; } return -1; } int CCodecs::FindFormatForExtension(const UString &ext) const { if (ext.IsEmpty()) return -1; FOR_VECTOR (i, Formats) if (Formats[i].FindExtension(ext) >= 0) return i; return -1; } int CCodecs::FindFormatForArchiveType(const UString &arcType) const { FOR_VECTOR (i, Formats) if (Formats[i].Name.IsEqualToNoCase(arcType)) return i; return -1; } bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const { formatIndices.Clear(); for (unsigned pos = 0; pos < arcType.Len();) { int pos2 = arcType.Find('.', pos); if (pos2 < 0) pos2 = arcType.Len(); const UString name = arcType.Mid(pos, pos2 - pos); if (name.IsEmpty()) return false; int index = FindFormatForArchiveType(name); if (index < 0 && name != L"*") { formatIndices.Clear(); return false; } formatIndices.Add(index); pos = pos2 + 1; } return true; } #endif // _SFX #ifdef EXTERNAL_CODECS // #define EXPORT_CODECS #ifdef EXPORT_CODECS extern unsigned g_NumCodecs; STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject); STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); #define NUM_EXPORT_CODECS g_NumCodecs extern unsigned g_NumHashers; STDAPI CreateHasher(UInt32 index, IHasher **hasher); STDAPI GetHasherProp(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); #define NUM_EXPORT_HASHERS g_NumHashers #else // EXPORT_CODECS #define NUM_EXPORT_CODECS 0 #define NUM_EXPORT_HASHERS 0 #endif // EXPORT_CODECS STDMETHODIMP CCodecs::GetNumberOfMethods(UInt32 *numMethods) { *numMethods = NUM_EXPORT_CODECS #ifdef EXTERNAL_CODECS + Codecs.Size() #endif ; return S_OK; } STDMETHODIMP CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return GetMethodProperty(index, propID, value); #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (propID == NMethodPropID::kDecoderIsAssigned || propID == NMethodPropID::kEncoderIsAssigned) { NCOM::CPropVariant prop; prop = (propID == NMethodPropID::kDecoderIsAssigned) ? ci.DecoderIsAssigned : ci.EncoderIsAssigned; prop.Detach(value); return S_OK; } return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value); #else return E_FAIL; #endif } STDMETHODIMP CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return CreateCoder2(false, index, iid, coder); #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (ci.DecoderIsAssigned) return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder); return S_OK; #else return E_FAIL; #endif } STDMETHODIMP CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return CreateCoder2(true, index, iid, coder); #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (ci.EncoderIsAssigned) return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder); return S_OK; #else return E_FAIL; #endif } STDMETHODIMP_(UInt32) CCodecs::GetNumHashers() { return NUM_EXPORT_HASHERS #ifdef EXTERNAL_CODECS + Hashers.Size() #endif ; } STDMETHODIMP CCodecs::GetHasherProp(UInt32 index, PROPID propID, PROPVARIANT *value) { #ifdef EXPORT_CODECS if (index < g_NumHashers) return ::GetHasherProp(index, propID, value); #endif #ifdef EXTERNAL_CODECS const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; return Libs[ci.LibIndex].Hashers->GetHasherProp(ci.HasherIndex, propID, value); #else return E_FAIL; #endif } STDMETHODIMP CCodecs::CreateHasher(UInt32 index, IHasher **hasher) { #ifdef EXPORT_CODECS if (index < g_NumHashers) return CreateHasher(index, hasher); #endif #ifdef EXTERNAL_CODECS const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; return Libs[ci.LibIndex].Hashers->CreateHasher(ci.HasherIndex, hasher); #else return E_FAIL; #endif } int CCodecs::GetCodecLibIndex(UInt32 index) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return -1; #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; return ci.LibIndex; #else return -1; #endif } int CCodecs::GetHasherLibIndex(UInt32 index) { #ifdef EXPORT_CODECS if (index < g_NumHashers) return -1; #endif #ifdef EXTERNAL_CODECS const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; return ci.LibIndex; #else return -1; #endif } bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) { NCOM::CPropVariant prop; if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK) if (prop.vt != VT_EMPTY) return true; return false; } #endif #ifdef EXTERNAL_CODECS const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; return ci.EncoderIsAssigned; #else return false; #endif } HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id) { NCOM::CPropVariant prop; RINOK(GetProperty(index, NMethodPropID::kID, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; id = prop.uhVal.QuadPart; return S_OK; } UString CCodecs::GetCodecName(UInt32 index) { UString s; NCOM::CPropVariant prop; if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK) if (prop.vt == VT_BSTR) s = prop.bstrVal; return s; } UInt64 CCodecs::GetHasherId(UInt32 index) { NCOM::CPropVariant prop; RINOK(GetHasherProp(index, NMethodPropID::kID, &prop)); if (prop.vt != VT_UI8) return 0; return prop.uhVal.QuadPart; } UString CCodecs::GetHasherName(UInt32 index) { UString s; NCOM::CPropVariant prop; if (GetHasherProp(index, NMethodPropID::kName, &prop) == S_OK) if (prop.vt == VT_BSTR) s = prop.bstrVal; return s; } UInt32 CCodecs::GetHasherDigestSize(UInt32 index) { NCOM::CPropVariant prop; RINOK(GetHasherProp(index, NMethodPropID::kDigestSize, &prop)); if (prop.vt != VT_UI4) return 0; return prop.ulVal; } #endif // EXTERNAL_CODECS src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.h000066400000000000000000000167111325366651500215370ustar00rootroot00000000000000// LoadCodecs.h #ifndef __LOAD_CODECS_H #define __LOAD_CODECS_H #include "../../../Common/MyBuffer.h" #include "../../../Common/MyCom.h" #include "../../../Common/MyString.h" #include "../../../Common/ComTry.h" #include "../../ICoder.h" #ifdef EXTERNAL_CODECS #include "../../../Windows/DLL.h" #endif struct CDllCodecInfo { CLSID Encoder; CLSID Decoder; bool EncoderIsAssigned; bool DecoderIsAssigned; int LibIndex; UInt32 CodecIndex; }; struct CDllHasherInfo { int LibIndex; UInt32 HasherIndex; }; #include "../../Archive/IArchive.h" struct CArcExtInfo { UString Ext; UString AddExt; CArcExtInfo() {} CArcExtInfo(const UString &ext): Ext(ext) {} CArcExtInfo(const UString &ext, const UString &addExt): Ext(ext), AddExt(addExt) {} }; struct CArcInfoEx { UInt32 Flags; Func_CreateInArchive CreateInArchive; Func_IsArc IsArcFunc; UString Name; CObjectVector Exts; #ifndef _SFX Func_CreateOutArchive CreateOutArchive; bool UpdateEnabled; bool NewInterface; // UInt32 Version; UInt32 SignatureOffset; CObjectVector Signatures; #ifdef NEW_FOLDER_INTERFACE UStringVector AssociateExts; #endif #endif #ifdef EXTERNAL_CODECS int LibIndex; UInt32 FormatIndex; CLSID ClassID; #endif bool Flags_KeepName() const { return (Flags & NArcInfoFlags::kKeepName) != 0; } bool Flags_FindSignature() const { return (Flags & NArcInfoFlags::kFindSignature) != 0; } bool Flags_AltStreams() const { return (Flags & NArcInfoFlags::kAltStreams) != 0; } bool Flags_NtSecure() const { return (Flags & NArcInfoFlags::kNtSecure) != 0; } bool Flags_SymLinks() const { return (Flags & NArcInfoFlags::kSymLinks) != 0; } bool Flags_HardLinks() const { return (Flags & NArcInfoFlags::kHardLinks) != 0; } bool Flags_UseGlobalOffset() const { return (Flags & NArcInfoFlags::kUseGlobalOffset) != 0; } bool Flags_StartOpen() const { return (Flags & NArcInfoFlags::kStartOpen) != 0; } bool Flags_BackwardOpen() const { return (Flags & NArcInfoFlags::kBackwardOpen) != 0; } bool Flags_PreArc() const { return (Flags & NArcInfoFlags::kPreArc) != 0; } bool Flags_PureStartOpen() const { return (Flags & NArcInfoFlags::kPureStartOpen) != 0; } UString GetMainExt() const { if (Exts.IsEmpty()) return UString(); return Exts[0].Ext; } int FindExtension(const UString &ext) const; /* UString GetAllExtensions() const { UString s; for (int i = 0; i < Exts.Size(); i++) { if (i > 0) s += ' '; s += Exts[i].Ext; } return s; } */ void AddExts(const UString &ext, const UString &addExt); bool IsSplit() const { return StringsAreEqualNoCase_Ascii(Name, "Split"); } // bool IsRar() const { return StringsAreEqualNoCase_Ascii(Name, "Rar"); } CArcInfoEx(): Flags(0), CreateInArchive(NULL), IsArcFunc(NULL) #ifndef _SFX , CreateOutArchive(NULL) , UpdateEnabled(false) , NewInterface(false) // , Version(0) , SignatureOffset(0) #endif #ifdef EXTERNAL_CODECS , LibIndex(-1) #endif {} }; #ifdef EXTERNAL_CODECS #ifdef NEW_FOLDER_INTERFACE struct CCodecIcons { struct CIconPair { UString Ext; int IconIndex; }; CObjectVector IconPairs; void LoadIcons(HMODULE m); bool FindIconIndex(const UString &ext, int &iconIndex) const; }; #endif struct CCodecLib #ifdef NEW_FOLDER_INTERFACE : public CCodecIcons #endif { NWindows::NDLL::CLibrary Lib; FString Path; Func_GetMethodProperty GetMethodProperty; Func_CreateObject CreateObject; CMyComPtr Hashers; #ifdef NEW_FOLDER_INTERFACE void LoadIcons() { CCodecIcons::LoadIcons((HMODULE)Lib); } #endif CCodecLib(): GetMethodProperty(NULL) {} }; #endif class CCodecs: #ifdef EXTERNAL_CODECS public ICompressCodecsInfo, public IHashers, #else public IUnknown, #endif public CMyUnknownImp { public: #ifdef EXTERNAL_CODECS CObjectVector Libs; CRecordVector Codecs; CRecordVector Hashers; #ifdef NEW_FOLDER_INTERFACE CCodecIcons InternalIcons; #endif HRESULT LoadCodecs(); HRESULT LoadFormats(); HRESULT LoadDll(const FString &path, bool needCheckDll); HRESULT LoadDllsFromFolder(const FString &folderPrefix); HRESULT CreateArchiveHandler(const CArcInfoEx &ai, void **archive, bool outHandler) const { return Libs[ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &IID_IOutArchive : &IID_IInArchive, (void **)archive); } #endif public: CObjectVector Formats; bool CaseSensitiveChange; bool CaseSensitive; CCodecs(): CaseSensitiveChange(false), CaseSensitive(false) {} const wchar_t *GetFormatNamePtr(int formatIndex) { return formatIndex < 0 ? L"#" : (const wchar_t *)Formats[formatIndex].Name; } HRESULT Load(); #ifndef _SFX int FindFormatForArchiveName(const UString &arcPath) const; int FindFormatForExtension(const UString &ext) const; int FindFormatForArchiveType(const UString &arcType) const; bool FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const; #endif #ifdef EXTERNAL_CODECS MY_UNKNOWN_IMP2(ICompressCodecsInfo, IHashers) STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods); STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); STDMETHOD(CreateDecoder)(UInt32 index, const GUID *interfaceID, void **coder); STDMETHOD(CreateEncoder)(UInt32 index, const GUID *interfaceID, void **coder); STDMETHOD_(UInt32, GetNumHashers)(); STDMETHOD(GetHasherProp)(UInt32 index, PROPID propID, PROPVARIANT *value); STDMETHOD(CreateHasher)(UInt32 index, IHasher **hasher); #else MY_UNKNOWN_IMP #endif // EXTERNAL_CODECS #ifdef EXTERNAL_CODECS int GetCodecLibIndex(UInt32 index); bool GetCodecEncoderIsAssigned(UInt32 index); HRESULT GetCodecId(UInt32 index, UInt64 &id); UString GetCodecName(UInt32 index); int GetHasherLibIndex(UInt32 index); UInt64 GetHasherId(UInt32 index); UString GetHasherName(UInt32 index); UInt32 GetHasherDigestSize(UInt32 index); #endif HRESULT CreateInArchive(unsigned formatIndex, CMyComPtr &archive) const { const CArcInfoEx &ai = Formats[formatIndex]; #ifdef EXTERNAL_CODECS if (ai.LibIndex < 0) #endif { COM_TRY_BEGIN archive = ai.CreateInArchive(); return S_OK; COM_TRY_END } #ifdef EXTERNAL_CODECS return CreateArchiveHandler(ai, (void **)&archive, false); #endif } #ifndef _SFX HRESULT CreateOutArchive(unsigned formatIndex, CMyComPtr &archive) const { const CArcInfoEx &ai = Formats[formatIndex]; #ifdef EXTERNAL_CODECS if (ai.LibIndex < 0) #endif { COM_TRY_BEGIN archive = ai.CreateOutArchive(); return S_OK; COM_TRY_END } #ifdef EXTERNAL_CODECS return CreateArchiveHandler(ai, (void **)&archive, true); #endif } int FindOutFormatFromName(const UString &name) const { FOR_VECTOR (i, Formats) { const CArcInfoEx &arc = Formats[i]; if (!arc.UpdateEnabled) continue; if (arc.Name.IsEqualToNoCase(name)) return i; } return -1; } #endif // _SFX }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.cpp000066400000000000000000002461351325366651500223020ustar00rootroot00000000000000// OpenArchive.cpp #include "StdAfx.h" // #define SHOW_DEBUG_INFO #ifdef SHOW_DEBUG_INFO #include #endif #include "../../../../C/CpuArch.h" #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Common/StringToInt.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../Common/FileStreams.h" #include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamUtils.h" #include "../../Compress/CopyCoder.h" #include "DefaultName.h" #include "OpenArchive.h" #ifndef _SFX #include "SetProperties.h" #endif #ifdef SHOW_DEBUG_INFO #define PRF(x) x #else #define PRF(x) #endif // increase it, if you need to support larger SFX stubs static const UInt64 kMaxCheckStartPosition = 1 << 22; /* Open: - formatIndex >= 0 (exact Format) 1) Open with main type. Archive handler is allowed to use archive start finder. Warning, if there is tail. - formatIndex = -1 (Parser:0) (default) - same as #1 but doesn't return Parser - formatIndex = -2 (#1) - file has supported extension (like a.7z) Open with that main type (only starting from start of file). - open OK: - if there is no tail - return OK - if there is tail: - archive is not "Self Exe" - return OK with Warning, that there is tail - archive is "Self Exe" ignore "Self Exe" stub, and tries to open tail - tail can be open as archive - shows that archive and stub size property. - tail can't be open as archive - shows Parser ??? - open FAIL: Try to open with all other types from offset 0 only. If some open type is OK and physical archive size is uequal or larger than file size, then return that archive with warning that can not be open as [extension type]. If extension was EXE, it will try to open as unknown_extension case - file has unknown extension (like a.hhh) It tries to open via parser code. - if there is full archive or tail archive and unknown block or "Self Exe" at front, it shows tail archive and stub size property. - in another cases, if there is some archive inside file, it returns parser/ - in another cases, it retuens S_FALSE - formatIndex = -3 (#2) - same as #1, but - stub (EXE) + archive is open in Parser - formatIndex = -4 (#3) - returns only Parser. skip full file archive. And show other sub-archives - formatIndex = -5 (#4) - returns only Parser. skip full file archive. And show other sub-archives for each byte pos */ using namespace NWindows; /* #ifdef _SFX #define OPEN_PROPS_PARAM #else #define OPEN_PROPS_PARAM , props #endif */ /* CArc::~CArc() { GetRawProps.Release(); Archive.Release(); printf("\nCArc::~CArc()\n"); } */ #ifndef _SFX namespace NArchive { namespace NParser { struct CParseItem { UInt64 Offset; UInt64 Size; // UInt64 OkSize; UString Name; UString Extension; FILETIME FileTime; UString Comment; UString ArcType; bool FileTime_Defined; bool UnpackSize_Defined; bool NumSubDirs_Defined; bool NumSubFiles_Defined; bool IsSelfExe; bool IsNotArcType; UInt64 UnpackSize; UInt64 NumSubDirs; UInt64 NumSubFiles; int FormatIndex; bool LenIsUnknown; CParseItem(): LenIsUnknown(false), FileTime_Defined(false), UnpackSize_Defined(false), NumSubFiles_Defined(false), NumSubDirs_Defined(false), IsSelfExe(false), IsNotArcType(false) // OkSize(0) {} /* bool IsEqualTo(const CParseItem &item) const { return Offset == item.Offset && Size == item.Size; } */ void NormalizeOffset() { if ((Int64)Offset < 0) { Size += Offset; // OkSize += Offset; Offset = 0; } } }; class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { public: CObjectVector _items; UInt64 _maxEndOffset; CMyComPtr _stream; MY_UNKNOWN_IMP2( IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); UInt64 GetLastEnd() const { if (_items.IsEmpty()) return 0; const CParseItem &back = _items.Back(); return back.Offset + back.Size; } void AddUnknownItem(UInt64 next); int FindInsertPos(const CParseItem &item); void AddItem(const CParseItem &item); // void Init(); CHandler() { _maxEndOffset = 0; } }; int CHandler::FindInsertPos(const CParseItem &item) { unsigned left = 0, right = _items.Size(); while (left != right) { unsigned mid = (left + right) / 2; const CParseItem & midItem = _items[mid]; if (item.Offset < midItem.Offset) right = mid; else if (item.Offset > midItem.Offset) left = mid + 1; else if (item.Size < midItem.Size) right = mid; else if (item.Size > midItem.Size) left = mid + 1; else { left = mid + 1; // return -1; } } return left; } void CHandler::AddUnknownItem(UInt64 next) { /* UInt64 prevEnd = 0; if (!_items.IsEmpty()) { const CParseItem &back = _items.Back(); prevEnd = back.Offset + back.Size; } */ if (_maxEndOffset < next) { CParseItem item2; item2.Offset = _maxEndOffset; item2.Size = next - _maxEndOffset; _maxEndOffset = next; _items.Add(item2); } else if (_maxEndOffset > next && !_items.IsEmpty()) { CParseItem &back = _items.Back(); if (back.LenIsUnknown) { back.Size = next - back.Offset; _maxEndOffset = next; } } } void CHandler::AddItem(const CParseItem &item) { AddUnknownItem(item.Offset); int pos = FindInsertPos(item); if (pos >= 0) { _items.Insert(pos, item); UInt64 next = item.Offset + item.Size; if (_maxEndOffset < next) _maxEndOffset = next; } } /* static const STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidSize, VT_UI8}, { NULL, kpidMTime, VT_FILETIME}, { NULL, kpidType, VT_BSTR}, { NULL, kpidComment, VT_BSTR}, { NULL, kpidOffset, VT_UI8}, { NULL, kpidUnpackSize, VT_UI8}, // { NULL, kpidNumSubDirs, VT_UI8}, }; */ static const Byte kProps[] = { kpidPath, kpidSize, kpidMTime, kpidType, kpidComment, kpidOffset, kpidUnpackSize }; IMP_IInArchive_Props IMP_IInArchive_ArcProps_NO STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */) { COM_TRY_BEGIN { Close(); _stream = stream; } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { _items.Clear(); _stream.Release(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _items.Size(); return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; const CParseItem &item = _items[index]; switch (propID) { case kpidPath: { wchar_t sz[32]; ConvertUInt32ToString(index + 1, sz); UString s = sz; if (!item.Name.IsEmpty()) { s += L'.'; s += item.Name; } if (!item.Extension.IsEmpty()) { s += L'.'; s += item.Extension; } prop = s; break; } case kpidSize: case kpidPackSize: prop = item.Size; break; case kpidOffset: prop = item.Offset; break; case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break; case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break; case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break; case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break; case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break; case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break; } prop.Detach(value); return S_OK; COM_TRY_END } HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _items.Size(); if (_stream && numItems == 0) return S_OK; UInt64 totalSize = 0; UInt32 i; for (i = 0; i < numItems; i++) totalSize += _items[allFilesMode ? i : indices[i]].Size; extractCallback->SetTotal(totalSize); totalSize = 0; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr inStream(streamSpec); streamSpec->SetStream(_stream); CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; CMyComPtr outStream(outStreamSpec); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); CMyComPtr copyCoder = copyCoderSpec; for (i = 0; i < numItems; i++) { lps->InSize = totalSize; lps->OutSize = totalSize; RINOK(lps->SetCur()); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; Int32 index = allFilesMode ? i : indices[i]; const CParseItem &item = _items[index]; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); UInt64 unpackSize = item.Size; totalSize += unpackSize; bool skipMode = false; if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); outStreamSpec->SetStream(realOutStream); realOutStream.Release(); outStreamSpec->Init(skipMode ? 0 : unpackSize, true); Int32 opRes = NExtract::NOperationResult::kOK; RINOK(_stream->Seek(item.Offset, STREAM_SEEK_SET, NULL)); streamSpec->Init(unpackSize); RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); if (outStreamSpec->GetRem() != 0) opRes = NExtract::NOperationResult::kDataError; outStreamSpec->ReleaseStream(); RINOK(extractCallback->SetOperationResult(opRes)); } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN const CParseItem &item = _items[index]; return CreateLimitedInStream(_stream, item.Offset, item.Size, stream); COM_TRY_END } }} #endif HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw() { NCOM::CPropVariant prop; result = false; RINOK(arc->GetProperty(index, propID, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } HRESULT Archive_IsItem_Folder(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsDir, result); } HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsAux, result); } HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result); } HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result); } static HRESULT Archive_GetArcBoolProp(IInArchive *arc, PROPID propid, bool &result) { NCOM::CPropVariant prop; result = false; RINOK(arc->GetArchiveProperty(propid, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined) { defined = false; NCOM::CPropVariant prop; RINOK(arc->GetArchiveProperty(propid, &prop)); switch (prop.vt) { case VT_UI4: result = prop.ulVal; defined = true; break; case VT_I4: result = prop.lVal; defined = true; break; case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; defined = true; break; case VT_I8: result = (UInt64)prop.hVal.QuadPart; defined = true; break; case VT_EMPTY: break; default: return E_FAIL; } return S_OK; } static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined) { defined = false; NCOM::CPropVariant prop; RINOK(arc->GetArchiveProperty(propid, &prop)); switch (prop.vt) { case VT_UI4: result = prop.ulVal; defined = true; break; case VT_I4: result = prop.lVal; defined = true; break; case VT_UI8: result = (Int64)prop.uhVal.QuadPart; defined = true; break; case VT_I8: result = (Int64)prop.hVal.QuadPart; defined = true; break; case VT_EMPTY: break; default: return E_FAIL; } return S_OK; } HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const { if (!GetRawProps) return E_FAIL; UInt32 curIndex = index; bool prevWasAltStream = false; for (;;) { UString s; #ifdef MY_CPU_LE const void *p; UInt32 size; UInt32 propType; RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType)); if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE) s = (const wchar_t *)p; else #endif { NCOM::CPropVariant prop; RINOK(Archive->GetProperty(curIndex, kpidName, &prop)); if (prop.vt == VT_BSTR) s = prop.bstrVal; else if (prop.vt == VT_EMPTY) s = L"[Content]"; else return E_FAIL; } if (prevWasAltStream) parts[0] = s + L":" + parts[0]; else parts.Insert(0, s); UInt32 curParent = (UInt32)(Int32)-1; UInt32 parentType = 0; RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType)); if (parent == curParent) return S_OK; if (curParent == (UInt32)(Int32)-1) return E_FAIL; prevWasAltStream = (parentType == NParentType::kAltStream); curIndex = curParent; } } HRESULT CArc::GetItemPath(UInt32 index, UString &result) const { #ifdef MY_CPU_LE if (GetRawProps) { const void *p; UInt32 size; UInt32 propType; if (!IsTree) { if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK && propType == NPropDataType::kUtf16z) { unsigned len = size / 2 - 1; wchar_t *s = result.GetBuffer(len); for (unsigned i = 0; i < len; i++) { wchar_t c = GetUi16(p); p = (const void *)((const Byte *)p + 2); #if WCHAR_PATH_SEPARATOR != L'/' if (c == L'/') c = WCHAR_PATH_SEPARATOR; #endif *s++ = c; } result.ReleaseBuffer(len); if (len != 0) return S_OK; } } /* else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK && p && propType == NPropDataType::kUtf16z) { UInt32 totalSize = size; bool isOK = false; { UInt32 index2 = index; for (;;) { UInt32 parent = (UInt32)(Int32)-1; UInt32 parentType = 0; if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) break; if (parent == (UInt32)(Int32)-1) { isOK = true; break; } index2 = parent; UInt32 size2; const void *p2; if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) break; totalSize += size2; } } if (isOK) { wchar_t *sz = result.GetBuffer(totalSize / 2); UInt32 pos = totalSize - size; memcpy((Byte *)sz + pos, p, size - 2); UInt32 index2 = index; for (;;) { UInt32 parent = (UInt32)(Int32)-1; UInt32 parentType = 0; if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) break; if (parent == (UInt32)(Int32)-1) break; index2 = parent; UInt32 size2; const void *p2; if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) break; pos -= size2; memcpy((Byte *)sz + pos, p2, size2); sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':'; } result.ReleaseBuffer((totalSize - 2) / 2); #ifdef _WIN32 // result.Replace(L'/', WCHAR_PATH_SEPARATOR); #endif return S_OK; } } */ } #endif { NCOM::CPropVariant prop; RINOK(Archive->GetProperty(index, kpidPath, &prop)); if (prop.vt == VT_BSTR) result = prop.bstrVal; else if (prop.vt == VT_EMPTY) result.Empty(); else return E_FAIL; } if (result.IsEmpty()) { result = DefaultName; NCOM::CPropVariant prop; RINOK(Archive->GetProperty(index, kpidExtension, &prop)); if (prop.vt == VT_BSTR) { result += L'.'; result += prop.bstrVal; } else if (prop.vt != VT_EMPTY) return E_FAIL; } return S_OK; } HRESULT CArc::GetItemPath2(UInt32 index, UString &result) const { RINOK(GetItemPath(index, result)); if (Ask_Deleted) { bool isDeleted = false; RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted)); if (isDeleted) result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR); } return S_OK; } #ifndef _SFX static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined) { NCOM::CPropVariant prop; defined = false; size = 0; RINOK(archive->GetProperty(index, kpidSize, &prop)); switch (prop.vt) { case VT_UI1: size = prop.bVal; break; case VT_UI2: size = prop.uiVal; break; case VT_UI4: size = prop.ulVal; break; case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; case VT_EMPTY: return S_OK; default: return E_FAIL; } defined = true; return S_OK; } #endif HRESULT CArc::GetItemSize(UInt32 index, UInt64 &size, bool &defined) const { NCOM::CPropVariant prop; defined = false; size = 0; RINOK(Archive->GetProperty(index, kpidSize, &prop)); switch (prop.vt) { case VT_UI1: size = prop.bVal; break; case VT_UI2: size = prop.uiVal; break; case VT_UI4: size = prop.ulVal; break; case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; case VT_EMPTY: return S_OK; default: return E_FAIL; } defined = true; return S_OK; } HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const { NCOM::CPropVariant prop; defined = false; ft.dwHighDateTime = ft.dwLowDateTime = 0; RINOK(Archive->GetProperty(index, kpidMTime, &prop)); if (prop.vt == VT_FILETIME) { ft = prop.filetime; defined = true; } else if (prop.vt != VT_EMPTY) return E_FAIL; else if (MTimeDefined) { ft = MTime; defined = true; } return S_OK; } #ifndef _SFX static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) { for (size_t i = 0; i < size; i++) if (p1[i] != p2[i]) return false; return true; } static void MakeCheckOrder(CCodecs *codecs, CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2, const Byte *data, size_t dataSize) { for (unsigned i = 0; i < numTypes; i++) { int index = orderIndices[i]; if (index < 0) continue; const CArcInfoEx &ai = codecs->Formats[index]; if (ai.SignatureOffset != 0) { orderIndices2.Add(index); orderIndices[i] = -1; continue; } const CObjectVector &sigs = ai.Signatures; FOR_VECTOR (k, sigs) { const CByteBuffer &sig = sigs[k]; if (sig.Size() == 0 && dataSize == 0 || sig.Size() != 0 && sig.Size() <= dataSize && TestSignature(data, sig, sig.Size())) { orderIndices2.Add(index); orderIndices[i] = -1; break; } } } } #endif #ifdef UNDER_CE static const unsigned kNumHashBytes = 1; #define HASH_VAL(buf, pos) ((buf)[pos]) #else static const unsigned kNumHashBytes = 2; #define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8)) #endif #ifndef _SFX static bool IsExeExt(const UString &ext) { return ext.IsEqualToNoCase(L"exe"); } static const char *k_PreArcFormats[] = { "pe" , "elf" , "macho" , "mub" , "te" }; static bool IsNameFromList(const UString &s, const char *names[], size_t num) { for (unsigned i = 0; i < num; i++) if (StringsAreEqualNoCase_Ascii(s, names[i])) return true; return false; } static bool IsPreArcFormat(const CArcInfoEx &ai) { if (ai.Flags_PreArc()) return true; return IsNameFromList(ai.Name, k_PreArcFormats, ARRAY_SIZE(k_PreArcFormats)); } static const char *k_Formats_with_simple_signuature[] = { "7z" , "xz" , "rar" , "bzip2" , "gzip" , "cab" , "wim" , "rpm" , "vhd" , "xar" }; static bool IsNewStyleSignature(const CArcInfoEx &ai) { // if (ai.Version >= 0x91F) if (ai.NewInterface) return true; return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, ARRAY_SIZE(k_Formats_with_simple_signuature)); } class CArchiveOpenCallback_Offset: public IArchiveOpenCallback, #ifndef _NO_CRYPTO public ICryptoGetTextPassword, #endif public CMyUnknownImp { public: CMyComPtr Callback; UInt64 Files; UInt64 Offset; #ifndef _NO_CRYPTO CMyComPtr GetTextPassword; MY_UNKNOWN_IMP2( IArchiveOpenCallback, ICryptoGetTextPassword) #else MY_UNKNOWN_IMP1(IArchiveOpenCallback) #endif STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes); STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes); #ifndef _NO_CRYPTO STDMETHOD(CryptoGetTextPassword)(BSTR *password); #endif }; #ifndef _NO_CRYPTO STDMETHODIMP CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN if (GetTextPassword) return GetTextPassword->CryptoGetTextPassword(password); return E_NOTIMPL; COM_TRY_END } #endif STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */) { return S_OK; } STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 * /* files */, const UInt64 *bytes) { if (!Callback) return S_OK; UInt64 value = Offset; if (bytes) value += *bytes; return Callback->SetCompleted(&Files, &value); } #endif UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp) { if (isDefinedProp != NULL) *isDefinedProp = false; switch (prop.vt) { case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart; case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal; case VT_EMPTY: return 0; default: throw 151199; } } void CArcErrorInfo::ClearErrors() { // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!! ThereIsTail = false; UnexpecedEnd = false; IgnoreTail = false; // NonZerosTail = false; ErrorFlags_Defined = false; ErrorFlags = 0; WarningFlags = 0; TailSize = 0; ErrorMessage.Empty(); WarningMessage.Empty(); } HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes) { // OkPhySize_Defined = false; PhySizeDefined = false; PhySize = 0; Offset = 0; AvailPhySize = FileSize - startPos; ErrorInfo.ClearErrors(); { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop)); ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined); } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop)); ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop); } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidError, &prop)); if (prop.vt != VT_EMPTY) ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown error"; } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidWarning, &prop)); if (prop.vt != VT_EMPTY) ErrorInfo.WarningMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown warning"; } if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen()) { RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySizeDefined)); /* RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined)); if (!OkPhySize_Defined) { OkPhySize_Defined = PhySizeDefined; OkPhySize = PhySize; } */ bool offsetDefined; RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined)); Int64 globalOffset = startPos + Offset; AvailPhySize = FileSize - globalOffset; if (PhySizeDefined) { UInt64 endPos = globalOffset + PhySize; if (endPos < FileSize) { AvailPhySize = PhySize; ErrorInfo.ThereIsTail = true; ErrorInfo.TailSize = FileSize - endPos; } else if (endPos > FileSize) ErrorInfo.UnexpecedEnd = true; } } return S_OK; } /* static PrintNumber(const char *s, int n) { char temp[100]; sprintf(temp, "%s %d", s, n); OutputDebugStringA(temp); } */ HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr &archive) { // OutputDebugStringW(L"a1"); // PrintNumber("formatIndex", formatIndex); RINOK(op.codecs->CreateInArchive(formatIndex, archive)); // OutputDebugStringW(L"a2"); if (!archive) return S_OK; #ifdef EXTERNAL_CODECS { CMyComPtr setCompressCodecsInfo; archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs)); } } #endif // OutputDebugStringW(ai.Name); // OutputDebugStringW(L"a3"); #ifndef _SFX const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.Flags_PreArc()) { /* we notify parsers that extract executables, that they don't need to open archive, if there is tail after executable (for SFX cases) */ CMyComPtr allowTail; archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail); if (allowTail) allowTail->AllowTail(BoolToInt(true)); } if (op.props) { /* FOR_VECTOR (y, op.props) { const COptionalOpenProperties &optProps = (*op.props)[y]; if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0) { RINOK(SetProperties(archive, optProps.Props)); break; } } */ RINOK(SetProperties(archive, *op.props)); } #endif return S_OK; } #ifndef _SFX static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi) { pi.Extension = ai.GetMainExt(); pi.FileTime_Defined = false; pi.ArcType = ai.Name; RINOK(Archive_GetArcBoolProp(archive, kpidIsNotArcType, pi.IsNotArcType)); // RINOK(Archive_GetArcBoolProp(archive, kpidIsSelfExe, pi.IsSelfExe)); pi.IsSelfExe = ai.Flags_PreArc(); { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidMTime, &prop)); if (prop.vt == VT_FILETIME) { pi.FileTime_Defined = true; pi.FileTime = prop.filetime; } } if (!pi.FileTime_Defined) { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidCTime, &prop)); if (prop.vt == VT_FILETIME) { pi.FileTime_Defined = true; pi.FileTime = prop.filetime; } } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidName, &prop)); if (prop.vt == VT_BSTR) { pi.Name = prop.bstrVal; pi.Extension.Empty(); } else { RINOK(archive->GetArchiveProperty(kpidExtension, &prop)); if (prop.vt == VT_BSTR) pi.Extension = prop.bstrVal; } } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidShortComment, &prop)); if (prop.vt == VT_BSTR) pi.Comment = prop.bstrVal; } UInt32 numItems; RINOK(archive->GetNumberOfItems(&numItems)); // pi.NumSubFiles = numItems; // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined)); // if (!pi.UnpackSize_Defined) { pi.NumSubFiles = 0; pi.NumSubDirs = 0; pi.UnpackSize = 0; for (UInt32 i = 0; i < numItems; i++) { UInt64 size = 0; bool defined = false; Archive_GetItem_Size(archive, i, size, defined); if (defined) { pi.UnpackSize_Defined = true; pi.UnpackSize += size; } bool isDir = false; Archive_IsItem_Folder(archive, i, isDir); if (isDir) pi.NumSubDirs++; else pi.NumSubFiles++; } if (pi.NumSubDirs != 0) pi.NumSubDirs_Defined = true; pi.NumSubFiles_Defined = true; } return S_OK; } #endif HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset) { if (!op.stream) return S_OK; RINOK(op.stream->Seek(offset, STREAM_SEEK_SET, NULL)); const UInt32 kBufSize = 1 << 11; Byte buf[kBufSize]; for (;;) { UInt32 processed = 0; RINOK(op.stream->Read(buf, kBufSize, &processed)); if (processed == 0) { // ErrorInfo.NonZerosTail = false; ErrorInfo.IgnoreTail = true; return S_OK; } for (size_t i = 0; i < processed; i++) { if (buf[i] != 0) { // ErrorInfo.IgnoreTail = false; // ErrorInfo.NonZerosTail = true; return S_OK; } } } } #ifndef _SFX class CExtractCallback_To_OpenCallback: public IArchiveExtractCallback, public ICompressProgressInfo, public CMyUnknownImp { public: CMyComPtr Callback; UInt64 Files; UInt64 Offset; MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICompressProgressInfo) INTERFACE_IArchiveExtractCallback(;) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); void Init(IArchiveOpenCallback *callback) { Callback = callback; Files = 0; Offset = 0; } }; STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */) { if (Callback) { UInt64 value = Offset; if (inSize) value += *inSize; return Callback->SetCompleted(&Files, &value); } return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */) { *outStream = 0; return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */) { return S_OK; } static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize, IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback, IArchiveExtractCallback *extractCallback) { /* if (needPhySize) { CMyComPtr open2; archive->QueryInterface(IID_IArchiveOpen2, (void **)&open2); if (open2) return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback); } */ RINOK(archive->Open(stream, maxCheckStartPosition, openCallback)); if (needPhySize) { bool phySize_Defined = false; UInt64 phySize = 0; RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined)); if (phySize_Defined) return S_OK; bool phySizeCantBeDetected = false;; RINOK(Archive_GetArcBoolProp(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected)); if (!phySizeCantBeDetected) { RINOK(archive->Extract(0, (UInt32)(Int32)-1, BoolToInt(true), extractCallback)); } } return S_OK; } static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name) { FOR_VECTOR (i, orderIndices) if (StringsAreEqualNoCase_Ascii(codecs->Formats[orderIndices[i]].Name, name)) return i; return -1; } #endif HRESULT CArc::OpenStream2(const COpenOptions &op) { // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout); Archive.Release(); GetRawProps.Release(); GetRootProps.Release(); ErrorInfo.ClearErrors(); ErrorInfo.ErrorFormatIndex = -1; IsParseArc = false; ArcStreamOffset = 0; // OutputDebugStringW(L"1"); // OutputDebugStringW(Path); const UString fileName = ExtractFileNameFromPath(Path); UString extension; { int dotPos = fileName.ReverseFind(L'.'); if (dotPos >= 0) extension = fileName.Ptr(dotPos + 1); } CIntVector orderIndices; bool searchMarkerInHandler = false; #ifdef _SFX searchMarkerInHandler = true; #endif CBoolArr isMainFormatArr(op.codecs->Formats.Size()); { FOR_VECTOR(i, op.codecs->Formats) isMainFormatArr[i] = false; } UInt64 maxStartOffset = op.openType.MaxStartOffset_Defined ? op.openType.MaxStartOffset : kMaxCheckStartPosition; #ifndef _SFX bool isUnknownExt = false; #endif bool isForced = false; unsigned numMainTypes = 0; int formatIndex = op.openType.FormatIndex; if (formatIndex >= 0) { isForced = true; orderIndices.Add(formatIndex); numMainTypes = 1; isMainFormatArr[formatIndex] = true; searchMarkerInHandler = true; } else { unsigned numFinded = 0; #ifndef _SFX bool isPrearcExt = false; #endif { FOR_VECTOR (i, op.codecs->Formats) { const CArcInfoEx &ai = op.codecs->Formats[i]; if (IgnoreSplit || !op.openType.CanReturnArc) if (ai.IsSplit()) continue; if (op.excludedFormats->FindInSorted(i) >= 0) continue; #ifndef _SFX if (IsPreArcFormat(ai)) isPrearcExt = true; #endif if (ai.FindExtension(extension) >= 0) { // PrintNumber("orderIndices.Insert", i); orderIndices.Insert(numFinded++, i); isMainFormatArr[i] = true; } else orderIndices.Add(i); } } if (!op.stream) { if (numFinded != 1) return E_NOTIMPL; orderIndices.DeleteFrom(1); } // PrintNumber("numFinded", numFinded ); /* if (op.openOnlySpecifiedByExtension) { if (numFinded != 0 && !IsExeExt(extension)) orderIndices.DeleteFrom(numFinded); } */ #ifndef _SFX if (op.stream && orderIndices.Size() >= 2) { RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CByteBuffer byteBuffer; CIntVector orderIndices2; if (numFinded == 0 || IsExeExt(extension)) { // signature search was here } else if (extension == L"000" || extension == L"001") { int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar"); if (i >= 0) { const size_t kBufSize = (1 << 10); byteBuffer.Alloc(kBufSize); size_t processedSize = kBufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize >= 16) { const Byte *buf = byteBuffer; const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }; if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0) { orderIndices2.Add(orderIndices[i]); orderIndices[i] = -1; if (i >= (int)numFinded) numFinded++; } } } } else { const size_t kBufSize = (1 << 10); byteBuffer.Alloc(kBufSize); size_t processedSize = kBufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize == 0) return S_FALSE; /* check type order: 1) matched extension, no signuature 2) matched extension, matched signuature // 3) no signuature // 4) matched signuature */ MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0); MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize); // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0); // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize); } FOR_VECTOR (i, orderIndices) { int val = orderIndices[i]; if (val != -1) orderIndices2.Add(val); } orderIndices = orderIndices2; } if (orderIndices.Size() >= 2) { int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso"); int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf"); if (iUdf > iIso && iIso >= 0) { int isoIndex = orderIndices[iIso]; int udfIndex = orderIndices[iUdf]; orderIndices[iUdf] = isoIndex; orderIndices[iIso] = udfIndex; } } numMainTypes = numFinded; isUnknownExt = (numMainTypes == 0) || isPrearcExt; #else // _SFX numMainTypes = orderIndices.Size(); #endif } UInt64 fileSize = 0; if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } FileSize = fileSize; #ifndef _SFX CBoolArr skipFrontalFormat(op.codecs->Formats.Size()); { FOR_VECTOR(i, op.codecs->Formats) skipFrontalFormat[i] = false; } #endif const COpenType &mode = op.openType; if (mode.CanReturnArc) { // ---------- OPEN main type by extenssion ---------- unsigned numCheckTypes = orderIndices.Size(); if (formatIndex >= 0) numCheckTypes = numMainTypes; for (unsigned i = 0; i < numCheckTypes; i++) { FormatIndex = orderIndices[i]; const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; // OutputDebugStringW(ai.Name); bool exactOnly = false; if (i >= numMainTypes) { if (!ai.Flags_BackwardOpen() // && !ai.Flags_PureStartOpen() ) continue; exactOnly = true; } // Some handlers do not set total bytes. So we set it here RINOK(op.callback->SetTotal(NULL, &fileSize)); if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } CMyComPtr archive; RINOK(PrepareToOpen(op, FormatIndex, archive)); if (!archive) continue; HRESULT result; if (op.stream) { UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0; result = archive->Open(op.stream, &searchLimit, op.callback); } else { CMyComPtr openSeq; archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq); if (!openSeq) return E_NOTIMPL; result = openSeq->OpenSeq(op.seqStream); } RINOK(ReadBasicProps(archive, 0, result)); if (result == S_FALSE) { bool isArc = ErrorInfo.IsArc_After_NonOpen(); #ifndef _SFX // if it's archive, we allow another open attempt for parser if (!mode.CanReturnParser || !isArc) skipFrontalFormat[FormatIndex] = true; #endif if (exactOnly) continue; if (i == 0 && numMainTypes == 1) { // we set NonOpenErrorInfo, only if there is only one main format (defined by extension). ErrorInfo.ErrorFormatIndex = FormatIndex; NonOpen_ErrorInfo = ErrorInfo; if (!mode.CanReturnParser && isArc) { // if (formatIndex < 0 && !searchMarkerInHandler) { // if bad archive was detected, we don't need additional open attempts #ifndef _SFX if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */) #endif return S_FALSE; } } } /* #ifndef _SFX if (IsExeExt(extension) || ai.Flags_PreArc()) { // openOnlyFullArc = false; // canReturnTailArc = true; // limitSignatureSearch = true; } #endif */ continue; } RINOK(result); #ifndef _SFX bool isMainFormat = isMainFormatArr[FormatIndex]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); bool thereIsTail = ErrorInfo.ThereIsTail; if (thereIsTail && mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, Offset + PhySize)); if (ErrorInfo.IgnoreTail) thereIsTail = false; } if (Offset > 0) { if (exactOnly || !searchMarkerInHandler || !specFlags.CanReturn_NonStart() || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset)) continue; } if (thereIsTail) { if (Offset > 0) { if (!specFlags.CanReturnMid) continue; } else if (!specFlags.CanReturnFrontal) continue; } if (Offset > 0 || thereIsTail) { if (formatIndex < 0) { if (IsPreArcFormat(ai)) { // openOnlyFullArc = false; // canReturnTailArc = true; /* if (mode.SkipSfxStub) limitSignatureSearch = true; */ // if (mode.SkipSfxStub) { // skipFrontalFormat[FormatIndex] = true; continue; } } } } #endif Archive = archive; return S_OK; } } #ifndef _SFX if (!op.stream) return S_FALSE; if (formatIndex >= 0 && !mode.CanReturnParser) { if (mode.MaxStartOffset_Defined) { if (mode.MaxStartOffset == 0) return S_FALSE; } else { const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.FindExtension(extension) >= 0) { const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.Flags_FindSignature() && searchMarkerInHandler) return S_FALSE; } } } NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler; CMyComPtr handler = handlerSpec; CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback; CMyComPtr extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec; extractCallback_To_OpenCallback_Spec->Init(op.callback); { // ---------- Check all possible START archives ---------- // this code is better for full file archives than Parser's code. CByteBuffer byteBuffer; bool endOfFile = false; size_t processedSize; { size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF) if (bufSize > fileSize) { bufSize = (size_t)fileSize; endOfFile = true; } byteBuffer.Alloc(bufSize); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); processedSize = bufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize == 0) return S_FALSE; if (processedSize < bufSize) endOfFile = true; } CUIntVector sortedFormats; unsigned i; int splitIndex = -1; for (i = 0; i < orderIndices.Size(); i++) { unsigned form = orderIndices[i]; if (skipFrontalFormat[form]) continue; const CArcInfoEx &ai = op.codecs->Formats[form]; if (ai.IsSplit()) { splitIndex = form; continue; } if (ai.IsArcFunc) { UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize); if (isArcRes == k_IsArc_Res_NO) continue; if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) continue; // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue; sortedFormats.Insert(0, form); continue; } bool isNewStyleSignature = IsNewStyleSignature(ai); bool needCheck = !isNewStyleSignature || ai.Signatures.IsEmpty() || ai.Flags_PureStartOpen() || ai.Flags_StartOpen() || ai.Flags_BackwardOpen(); if (isNewStyleSignature && !ai.Signatures.IsEmpty()) { unsigned k; for (k = 0; k < ai.Signatures.Size(); k++) { const CByteBuffer &sig = ai.Signatures[k]; UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); if (processedSize < signatureEnd) { if (!endOfFile) needCheck = true; } else if (memcmp(sig, byteBuffer + ai.SignatureOffset, sig.Size()) == 0) break; } if (k != ai.Signatures.Size()) { sortedFormats.Insert(0, form); continue; } } if (needCheck) sortedFormats.Add(form); } if (splitIndex >= 0) sortedFormats.Insert(0, splitIndex); for (i = 0; i < sortedFormats.Size(); i++) { FormatIndex = sortedFormats[i]; const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; RINOK(op.callback->SetTotal(NULL, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CMyComPtr archive; RINOK(PrepareToOpen(op, FormatIndex, archive)); if (!archive) continue; PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name)); HRESULT result; { UInt64 searchLimit = 0; /* if (mode.CanReturnArc) result = archive->Open(op.stream, &searchLimit, op.callback); else */ result = OpenArchiveSpec(archive, !mode.CanReturnArc, op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback); } if (result == S_FALSE) { skipFrontalFormat[FormatIndex] = true; // FIXME: maybe we must use LenIsUnknown. // printf(" OpenForSize Error"); continue; } RINOK(result); RINOK(ReadBasicProps(archive, 0, result)); if (Offset > 0) { continue; // good handler doesn't return such Offset > 0 // but there are some cases like false prefixed PK00 archive, when // we can support it? } NArchive::NParser::CParseItem pi; pi.Offset = Offset; pi.Size = AvailPhySize; // bool needScan = false; if (!PhySizeDefined) { // it's for Z format pi.LenIsUnknown = true; // needScan = true; // phySize = arcRem; // nextNeedCheckStartOpen = false; } /* if (OkPhySize_Defined) pi.OkSize = pi.OkPhySize; else pi.OkSize = pi.Size; */ pi.NormalizeOffset(); // printf(" phySize = %8d", (unsigned)phySize); if (mode.CanReturnArc) { bool isMainFormat = isMainFormatArr[FormatIndex]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); bool openCur = false; if (!ErrorInfo.ThereIsTail) openCur = true; else { if (mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, Offset + PhySize)); if (ErrorInfo.IgnoreTail) openCur = true; } if (!openCur) { openCur = specFlags.CanReturnFrontal; if (formatIndex < 0) // format is not forced { if (IsPreArcFormat(ai)) { // if (mode.SkipSfxStub) { openCur = false; } } } } } if (openCur) { InStream = op.stream; Archive = archive; return S_OK; } } skipFrontalFormat[FormatIndex] = true; // if (!mode.CanReturnArc) /* if (!ErrorInfo.ThereIsTail) continue; */ if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) continue; // printf("\nAdd offset = %d", (int)pi.Offset); RINOK(ReadParseItemProps(archive, ai, pi)); handlerSpec->AddItem(pi); } } // ---------- PARSER ---------- CUIntVector arc2sig; // formatIndex to signatureIndex CUIntVector sig2arc; // signatureIndex to formatIndex; { unsigned sum = 0; FOR_VECTOR (i, op.codecs->Formats) { arc2sig.Add(sum); const CObjectVector &sigs = op.codecs->Formats[i].Signatures; sum += sigs.Size(); FOR_VECTOR (k, sigs) sig2arc.Add(i); } } { CArchiveOpenCallback_Offset *openCallback_Offset_Spec = new CArchiveOpenCallback_Offset; CMyComPtr openCallback_Offset = openCallback_Offset_Spec; const size_t kBeforeSize = 1 << 16; const size_t kAfterSize = 1 << 20; const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8); CByteArr hashBuffer(kNumVals); Byte *hash = hashBuffer; memset(hash, 0xFF, kNumVals); Byte prevs[256]; memset(prevs, 0xFF, sizeof(prevs)); if (sig2arc.Size() >= 0xFF) return S_FALSE; CUIntVector difficultFormats; CBoolArr difficultBools(256); { for (unsigned i = 0; i < 256; i++) difficultBools[i] = false; } bool thereAreHandlersForSearch = false; // UInt32 maxSignatureEnd = 0; FOR_VECTOR (i, orderIndices) { int index = orderIndices[i]; if (index < 0) continue; const CArcInfoEx &ai = op.codecs->Formats[index]; bool isDifficult = false; // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31) if (!ai.NewInterface) isDifficult = true; else { if (ai.Flags_StartOpen()) isDifficult = true; FOR_VECTOR (k, ai.Signatures) { const CByteBuffer &sig = ai.Signatures[k]; /* UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); if (maxSignatureEnd < signatureEnd) maxSignatureEnd = signatureEnd; */ if (sig.Size() < kNumHashBytes) { isDifficult = true; continue; } thereAreHandlersForSearch = true; UInt32 v = HASH_VAL(sig, 0); unsigned sigIndex = arc2sig[index] + k; prevs[sigIndex] = hash[v]; hash[v] = (Byte)sigIndex; } } if (isDifficult) { difficultFormats.Add(index); difficultBools[index] = true; } } if (!thereAreHandlersForSearch) { // openOnlyFullArc = true; // canReturnTailArc = true; } RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream; CMyComPtr limitedStream = limitedStreamSpec; limitedStreamSpec->SetStream(op.stream); openCallback_Offset_Spec->Callback = op.callback; #ifndef _NO_CRYPTO if (op.callback) { openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword); } #endif RINOK(op.callback->SetTotal(NULL, &fileSize)); CByteBuffer &byteBuffer = limitedStreamSpec->Buffer; byteBuffer.Alloc(kBufSize); UInt64 callbackPrev = 0; bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos. bool endOfFile = false; UInt64 bufPhyPos = 0; size_t bytesInBuf = 0; // UInt64 prevPos = 0; // ---------- Main Scan Loop ---------- UInt64 pos = 0; if (!mode.EachPos && handlerSpec->_items.Size() == 1) { NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; if (!pi.LenIsUnknown && pi.Offset == 0) pos = pi.Size; } for (;;) { // printf("\nPos = %d", (int)pos); UInt64 posInBuf = pos - bufPhyPos; // if (pos > ((UInt64)1 << 35)) break; if (!endOfFile) { if (bytesInBuf < kBufSize) { size_t processedSize = kBufSize - bytesInBuf; // printf("\nRead ask = %d", (unsigned)processedSize); UInt64 seekPos = bufPhyPos + bytesInBuf; RINOK(op.stream->Seek(bufPhyPos + bytesInBuf, STREAM_SEEK_SET, NULL)); RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize)); // printf(" processed = %d", (unsigned)processedSize); if (processedSize == 0) { fileSize = seekPos; endOfFile = true; } else { bytesInBuf += processedSize; limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos); } continue; } if (bytesInBuf < posInBuf) { UInt64 skipSize = posInBuf - bytesInBuf; if (skipSize <= kBeforeSize) { size_t keepSize = (size_t)(kBeforeSize - skipSize); // printf("\nmemmove skip = %d", (int)keepSize); memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize); bytesInBuf = keepSize; bufPhyPos = pos - keepSize; continue; } // printf("\nSkip %d", (int)(skipSize - kBeforeSize)); // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL)); bytesInBuf = 0; bufPhyPos = pos - kBeforeSize; continue; } if (bytesInBuf - posInBuf < kAfterSize) { size_t beg = (size_t)posInBuf - kBeforeSize; // printf("\nmemmove for after beg = %d", (int)beg); memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg); bufPhyPos += beg; bytesInBuf -= beg; continue; } } if (pos >= callbackPrev + (1 << 23)) { openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); openCallback_Offset_Spec->Offset = pos; RINOK(openCallback_Offset->SetCompleted(NULL, NULL)); callbackPrev = pos; } { UInt64 endPos = bufPhyPos + bytesInBuf; if (fileSize < endPos) { FileSize = fileSize; // why ???? fileSize = endPos; } } size_t availSize = bytesInBuf - (size_t)posInBuf; if (availSize < kNumHashBytes) break; size_t scanSize = availSize - ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes); { /* UInt64 scanLimit = openOnlyFullArc ? maxSignatureEnd : op.openType.ScanSize + maxSignatureEnd; */ if (!mode.CanReturnParser) { if (pos > maxStartOffset) break; UInt64 remScan = maxStartOffset - pos; if (scanSize > remScan) scanSize = (size_t)remScan; } } scanSize++; const Byte *buf = byteBuffer + (size_t)posInBuf; size_t ppp = 0; if (!needCheckStartOpen) { for (; ppp < scanSize && hash[HASH_VAL(buf, ppp)] == 0xFF; ppp++); pos += ppp; if (ppp == scanSize) continue; } UInt32 v = HASH_VAL(buf, ppp); bool nextNeedCheckStartOpen = true; unsigned i = hash[v]; unsigned indexOfDifficult = 0; // ---------- Open Loop for Current Pos ---------- bool wasOpen = false; for (;;) { unsigned index; bool isDifficult; if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size()) { index = difficultFormats[indexOfDifficult++]; isDifficult = true; } else { if (i == 0xFF) break; index = sig2arc[i]; unsigned sigIndex = i - arc2sig[index]; i = prevs[i]; if (needCheckStartOpen && difficultBools[index]) continue; const CArcInfoEx &ai = op.codecs->Formats[index]; if (pos < ai.SignatureOffset) continue; /* if (openOnlyFullArc) if (pos != ai.SignatureOffset) continue; */ const CByteBuffer &sig = ai.Signatures[sigIndex]; if (ppp + sig.Size() > availSize || !TestSignature(buf + ppp, sig, sig.Size())) continue; // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos)); // prevPos = pos; isDifficult = false; } const CArcInfoEx &ai = op.codecs->Formats[index]; if ((isDifficult && pos == 0) || ai.SignatureOffset == pos) { // we don't check same archive second time */ if (skipFrontalFormat[index]) continue; } UInt64 startArcPos = pos; if (!isDifficult) { if (pos < ai.SignatureOffset) continue; startArcPos = pos - ai.SignatureOffset; /* // we don't need the check for Z files if (startArcPos < handlerSpec->GetLastEnd()) continue; */ } if (ai.IsArcFunc && startArcPos >= bufPhyPos) { size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos); if (offsetInBuf < bytesInBuf) { UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf); if (isArcRes == k_IsArc_Res_NO) continue; if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) continue; /* if (isArcRes == k_IsArc_Res_YES_LOW_PROB) { // if (pos != ai.SignatureOffset) continue; } */ } // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name); } /* if (pos == 67109888) pos = pos; */ PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name)); bool isMainFormat = isMainFormatArr[index]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); CMyComPtr archive; RINOK(PrepareToOpen(op, index, archive)); if (!archive) return E_FAIL; // OutputDebugStringW(ai.Name); UInt64 rem = fileSize - startArcPos; UInt64 arcStreamOffset = 0; if (ai.Flags_UseGlobalOffset()) { limitedStreamSpec->InitAndSeek(0, fileSize); limitedStream->Seek(startArcPos, STREAM_SEEK_SET, NULL); } else { limitedStreamSpec->InitAndSeek(startArcPos, rem); arcStreamOffset = startArcPos; } UInt64 maxCheckStartPosition = 0; openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); openCallback_Offset_Spec->Offset = startArcPos; // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset); extractCallback_To_OpenCallback_Spec->Files = 0; extractCallback_To_OpenCallback_Spec->Offset = startArcPos; HRESULT result = OpenArchiveSpec(archive, true, limitedStream, &maxCheckStartPosition, openCallback_Offset, extractCallback_To_OpenCallback); RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result)); bool isOpen = false; if (result == S_FALSE) { if (!mode.CanReturnParser) { if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen()) { ErrorInfo.ErrorFormatIndex = index; NonOpen_ErrorInfo = ErrorInfo; // if archive was detected, we don't need additional open attempts return S_FALSE; } continue; } if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0) continue; } else { isOpen = true; RINOK(result); PRF(printf(" OK ")); } // fprintf(stderr, "\n %8X %S", startArcPos, Path); // printf("\nOpen OK: %S", ai.Name); NArchive::NParser::CParseItem pi; pi.Offset = startArcPos; if (ai.Flags_UseGlobalOffset()) pi.Offset = Offset; else if (Offset != 0) return E_FAIL; UInt64 arcRem = FileSize - pi.Offset; UInt64 phySize = arcRem; bool phySizeDefined = PhySizeDefined; if (phySizeDefined) { if (pi.Offset + PhySize > FileSize) { // ErrorInfo.ThereIsTail = true; PhySize = FileSize - pi.Offset; } phySize = PhySize; } if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63)) return E_FAIL; /* if (!ai.UseGlobalOffset) { if (phySize > arcRem) { ThereIsTail = true; phySize = arcRem; } } */ bool needScan = false; if (isOpen && !phySizeDefined) { // it's for Z format pi.LenIsUnknown = true; needScan = true; phySize = arcRem; nextNeedCheckStartOpen = false; } pi.Size = phySize; /* if (OkPhySize_Defined) pi.OkSize = OkPhySize; */ pi.NormalizeOffset(); // printf(" phySize = %8d", (unsigned)phySize); /* if (needSkipFullArc) if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize) continue; */ if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) { // it's possible for dmg archives if (!mode.CanReturnArc) continue; } if (mode.EachPos) pos++; else if (needScan) { pos++; /* if (!OkPhySize_Defined) pos++; else pos = pi.Offset + pi.OkSize; */ } else pos = pi.Offset + pi.Size; RINOK(ReadParseItemProps(archive, ai, pi)); if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */) { /* It's for DMG format. This code deletes all previous items that are included to current item */ while (!handlerSpec->_items.IsEmpty()) { { const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back(); if (back.Offset < pi.Offset) break; if (back.Offset + back.Size > pi.Offset + pi.Size) break; } handlerSpec->_items.DeleteBack(); } } if (isOpen && mode.CanReturnArc && phySizeDefined) { // if (pi.Offset + pi.Size >= fileSize) bool openCur = false; bool thereIsTail = ErrorInfo.ThereIsTail; if (thereIsTail && mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, arcStreamOffset + Offset + PhySize)); if (ErrorInfo.IgnoreTail) thereIsTail = false; } if (pi.Offset != 0) { if (!pi.IsNotArcType) if (thereIsTail) openCur = specFlags.CanReturnMid; else openCur = specFlags.CanReturnTail; } else { if (!thereIsTail) openCur = true; else openCur = specFlags.CanReturnFrontal; if (formatIndex >= -2) openCur = true; } if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */) openCur = false; // We open file as SFX, if there is front archive or first archive is "Self Executable" if (!openCur && !pi.IsSelfExe && !thereIsTail && (!pi.IsNotArcType || pi.Offset == 0)) { if (handlerSpec->_items.IsEmpty()) { if (specFlags.CanReturnTail) openCur = true; } else if (handlerSpec->_items.Size() == 1) { if (handlerSpec->_items[0].IsSelfExe) { if (mode.SpecUnknownExt.CanReturnTail) openCur = true; } } } if (openCur) { InStream = op.stream; Archive = archive; FormatIndex = index; ArcStreamOffset = arcStreamOffset; return S_OK; } } /* if (openOnlyFullArc) { ErrorInfo.ClearErrors(); return S_FALSE; } */ pi.FormatIndex = index; // printf("\nAdd offset = %d", (int)pi.Offset); handlerSpec->AddItem(pi); wasOpen = true; break; } // ---------- End of Open Loop for Current Pos ---------- if (!wasOpen) pos++; needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen); } // ---------- End of Main Scan Loop ---------- /* if (handlerSpec->_items.Size() == 1) { const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; if (pi.Size == fileSize && pi.Offset == 0) { Archive = archive; FormatIndex2 = pi.FormatIndex; return S_OK; } } */ if (mode.CanReturnParser) { bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing handlerSpec->AddUnknownItem(fileSize); if (handlerSpec->_items.Size() == 0) return S_FALSE; if (returnParser || handlerSpec->_items.Size() != 1) { // return S_FALSE; handlerSpec->_stream = op.stream; Archive = handler; ErrorInfo.ClearErrors(); IsParseArc = true; FormatIndex = -1; // It's parser Offset = 0; return S_OK; } } } #endif if (!Archive) return S_FALSE; return S_OK; } HRESULT CArc::OpenStream(const COpenOptions &op) { RINOK(OpenStream2(op)); // PrintNumber("op.formatIndex 3", op.formatIndex); if (Archive) { GetRawProps.Release(); GetRootProps.Release(); Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps); Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps); RINOK(Archive_GetArcBoolProp(Archive, kpidIsTree, IsTree)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsDeleted, Ask_Deleted)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsAltStream, Ask_AltStream)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsAux, Ask_Aux)); RINOK(Archive_GetArcBoolProp(Archive, kpidINode, Ask_INode)); const UString fileName = ExtractFileNameFromPath(Path); UString extension; { int dotPos = fileName.ReverseFind(L'.'); if (dotPos >= 0) extension = fileName.Ptr(dotPos + 1); } DefaultName.Empty(); if (FormatIndex >= 0) { const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; if (ai.Exts.Size() == 0) DefaultName = GetDefaultName2(fileName, L"", L""); else { int subExtIndex = ai.FindExtension(extension); if (subExtIndex < 0) subExtIndex = 0; const CArcExtInfo &extInfo = ai.Exts[subExtIndex]; DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt); } } } return S_OK; } #ifdef _SFX #ifdef _WIN32 static const wchar_t *k_ExeExt = L".exe"; static const unsigned k_ExeExt_Len = 4; #else static const wchar_t *k_ExeExt = L""; static const unsigned k_ExeExt_Len = 0; #endif #endif HRESULT CArc::OpenStreamOrFile(COpenOptions &op) { CMyComPtr fileStream; CMyComPtr seqStream; CInFileStream *fileStreamSpec = NULL; if (op.stdInMode) { seqStream = new CStdInFileStream; op.seqStream = seqStream; } else if (!op.stream) { fileStreamSpec = new CInFileStream; fileStream = fileStreamSpec; Path = filePath; if (!fileStreamSpec->Open(us2fs(Path))) { return GetLastError(); } op.stream = fileStream; #ifdef _SFX IgnoreSplit = true; #endif } /* if (callback) { UInt64 fileSize; RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.callback->SetTotal(NULL, &fileSize)) } */ HRESULT res = OpenStream(op); IgnoreSplit = false; #ifdef _SFX if (res != S_FALSE || !fileStreamSpec || !op.callbackSpec || NonOpen_ErrorInfo.IsArc_After_NonOpen()) return res; { if (filePath.Len() > k_ExeExt_Len && MyStringCompareNoCase(filePath.RightPtr(k_ExeExt_Len), k_ExeExt) == 0) { const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len); FOR_VECTOR (i, op.codecs->Formats) { const CArcInfoEx &ai = op.codecs->Formats[i]; if (ai.IsSplit()) continue; UString path3 = path2; path3 += L"."; path3 += ai.GetMainExt(); // "7z" for SFX. Path = path3 + L".001"; bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); if (!isOk) { Path = path3; isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); } if (isOk) { if (fileStreamSpec->Open(us2fs(Path))) { op.stream = fileStream; NonOpen_ErrorInfo.ClearErrors_Full(); if (OpenStream(op) == S_OK) return S_OK; } } } } } #endif return res; } void CArchiveLink::KeepModeForNextOpen() { for (int i = Arcs.Size() - 1; i >= 0; i--) { CMyComPtr keep; Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep); if (keep) keep->KeepModeForNextOpen(); } } HRESULT CArchiveLink::Close() { for (int i = Arcs.Size() - 1; i >= 0; i--) { RINOK(Arcs[i].Close()); } IsOpen = false; // ErrorsText.Empty(); return S_OK; } void CArchiveLink::Release() { // NonOpenErrorFormatIndex = -1; NonOpen_ErrorInfo.ClearErrors(); NonOpen_ArcPath.Empty(); while (!Arcs.IsEmpty()) Arcs.DeleteBack(); } /* void CArchiveLink::Set_ErrorsText() { FOR_VECTOR(i, Arcs) { const CArc &arc = Arcs[i]; if (!arc.ErrorFlagsText.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += GetUnicodeString(arc.ErrorFlagsText); } if (!arc.ErrorMessage.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += arc.ErrorMessage; } if (!arc.WarningMessage.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += arc.WarningMessage; } } } */ HRESULT CArchiveLink::Open(COpenOptions &op) { Release(); if (op.types->Size() >= 32) return E_NOTIMPL; HRESULT resSpec; for (;;) { resSpec = S_OK; op.openType = COpenType(); if (op.types->Size() >= 1) { COpenType latest; if (Arcs.Size() < op.types->Size()) latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; else { latest = (*op.types)[0]; if (!latest.Recursive) break; } op.openType = latest; } else if (Arcs.Size() >= 32) break; /* op.formatIndex = -1; if (op.types->Size() >= 1) { int latest; if (Arcs.Size() < op.types->Size()) latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; else { latest = (*op.types)[0]; if (latest != -2 && latest != -3) break; } if (latest >= 0) op.formatIndex = latest; else if (latest == -1 || latest == -2) { // default } else if (latest == -3) op.formatIndex = -2; else op.formatIndex = latest + 2; } else if (Arcs.Size() >= 32) break; */ if (Arcs.IsEmpty()) { CArc arc; arc.filePath = op.filePath; arc.Path = op.filePath; arc.SubfileIndex = (UInt32)(Int32)-1; HRESULT result = arc.OpenStreamOrFile(op); if (result != S_OK) { if (result == S_FALSE) { NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo; // NonOpenErrorFormatIndex = arc.ErrorFormatIndex; NonOpen_ArcPath = arc.Path; } return result; } Arcs.Add(arc); continue; } // PrintNumber("op.formatIndex 11", op.formatIndex); const CArc &arc = Arcs.Back(); if (op.types->Size() > Arcs.Size()) resSpec = E_NOTIMPL; UInt32 mainSubfile; { NCOM::CPropVariant prop; RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop)); if (prop.vt == VT_UI4) mainSubfile = prop.ulVal; else break; UInt32 numItems; RINOK(arc.Archive->GetNumberOfItems(&numItems)); if (mainSubfile >= numItems) break; } CMyComPtr getStream; if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream) break; CMyComPtr subSeqStream; if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream) break; CMyComPtr subStream; if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream) break; CArc arc2; RINOK(arc.GetItemPath(mainSubfile, arc2.Path)); bool zerosTailIsAllowed; RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed)); CMyComPtr setSubArchiveName; op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); if (setSubArchiveName) setSubArchiveName->SetSubArchiveName(arc2.Path); arc2.SubfileIndex = mainSubfile; // CIntVector incl; CIntVector excl; COpenOptions op2; #ifndef _SFX op2.props = op.props; #endif op2.codecs = op.codecs; // op2.types = &incl; op2.openType = op.openType; op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed; op2.excludedFormats = ! op2.stdInMode = false; op2.stream = subStream; op2.filePath = arc2.Path; op2.callback = op.callback; op2.callbackSpec = op.callbackSpec; HRESULT result = arc2.OpenStream(op2); resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE); if (result == S_FALSE) { NonOpen_ErrorInfo = arc2.ErrorInfo; NonOpen_ArcPath = arc2.Path; break; } RINOK(result); RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined)); Arcs.Add(arc2); } IsOpen = !Arcs.IsEmpty(); return resSpec; } static void SetCallback(const FString &filePath, IOpenCallbackUI *callbackUI, IArchiveOpenCallback *reOpenCallback, CMyComPtr &callback) { COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; callback = openCallbackSpec; openCallbackSpec->Callback = callbackUI; openCallbackSpec->ReOpenCallback = reOpenCallback; FString dirPrefix, fileName; NFile::NDir::GetFullPathAndSplit(filePath, dirPrefix, fileName); openCallbackSpec->Init(dirPrefix, fileName); } HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI) { VolumesSize = 0; COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; CMyComPtr callback = openCallbackSpec; openCallbackSpec->Callback = callbackUI; FString prefix, name; if (!op.stream && !op.stdInMode) { NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name); openCallbackSpec->Init(prefix, name); } else { openCallbackSpec->SetSubArchiveName(op.filePath); } op.callback = callback; op.callbackSpec = openCallbackSpec; RINOK(Open(op)); // VolumePaths.Add(fs2us(prefix + name)); FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed) { if (openCallbackSpec->FileNames_WasUsed[i]) { VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]); VolumesSize += openCallbackSpec->FileSizes[i]; } } // VolumesSize = openCallbackSpec->TotalSize; return S_OK; } HRESULT CArc::ReOpen(const COpenOptions &op) { ErrorInfo.ClearErrors(); ErrorInfo.ErrorFormatIndex = -1; UInt64 fileSize = 0; if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } FileSize = fileSize; CMyComPtr stream2; Int64 globalOffset = GetGlobalOffset(); if (globalOffset <= 0) stream2 = op.stream; else { CTailInStream *tailStreamSpec = new CTailInStream; stream2 = tailStreamSpec; tailStreamSpec->Stream = op.stream; tailStreamSpec->Offset = globalOffset; tailStreamSpec->Init(); RINOK(tailStreamSpec->SeekToStart()); } // There are archives with embedded STUBs (like ZIP), so we must support signature scanning // But for another archives we can use 0 here. So the code can be fixed !!! UInt64 maxStartPosition = kMaxCheckStartPosition; HRESULT res = Archive->Open(stream2, &maxStartPosition, op.callback); if (res == S_OK) { RINOK(ReadBasicProps(Archive, globalOffset, res)); ArcStreamOffset = globalOffset; if (ArcStreamOffset != 0) InStream = op.stream; } return res; } HRESULT CArchiveLink::ReOpen(COpenOptions &op) { if (Arcs.Size() > 1) return E_NOTIMPL; CObjectVector inc; CIntVector excl; op.types = &inc; op.excludedFormats = ! op.stdInMode = false; op.stream = NULL; if (Arcs.Size() == 0) // ??? return Open2(op, NULL); CMyComPtr openCallbackNew; SetCallback(us2fs(op.filePath), NULL, op.callback, openCallbackNew); CInFileStream *fileStreamSpec = new CInFileStream; CMyComPtr stream(fileStreamSpec); if (!fileStreamSpec->Open(us2fs(op.filePath))) return GetLastError(); op.stream = stream; CArc &arc = Arcs[0]; HRESULT res = arc.ReOpen(op); IsOpen = (res == S_OK); return res; } #ifndef _SFX bool ParseComplexSize(const wchar_t *s, UInt64 &result) { result = 0; const wchar_t *end; UInt64 number = ConvertStringToUInt64(s, &end); if (end == s) return false; if (*end == 0) { result = number; return true; } if (end[1] != 0) return false; unsigned numBits; switch (MyCharLower_Ascii(*end)) { case 'b': result = number; return true; case 'k': numBits = 10; break; case 'm': numBits = 20; break; case 'g': numBits = 30; break; case 't': numBits = 40; break; default: return false; } if (number >= ((UInt64)1 << (64 - numBits))) return false; result = number << numBits; return true; } static bool ParseTypeParams(const UString &s, COpenType &type) { if (s[0] == 0) return true; if (s[1] == 0) { switch ((unsigned)(Byte)s[0]) { case 'e': type.EachPos = true; return true; case 'a': type.CanReturnArc = true; return true; case 'r': type.Recursive = true; return true; } return false; } if (s[0] == 's') { UInt64 result; if (!ParseComplexSize(s.Ptr(1), result)) return false; type.MaxStartOffset = result; type.MaxStartOffset_Defined = true; return true; } return false; } bool ParseType(CCodecs &codecs, const UString &s, COpenType &type) { int pos2 = s.Find(':'); UString name; if (pos2 < 0) { name = s; pos2 = s.Len(); } else { name = s.Left(pos2); pos2++; } int index = codecs.FindFormatForArchiveType(name); type.Recursive = false; if (index < 0) { if (name[0] == '*') { if (name[1] != 0) return false; } else if (name[0] == '#') { if (name[1] != 0) return false; type.CanReturnArc = false; type.CanReturnParser = true; } else return false; } type.FormatIndex = index; for (unsigned i = pos2; i < s.Len();) { int next = s.Find(':', i); if (next < 0) next = s.Len(); UString name = s.Mid(i, next - i); if (name.IsEmpty()) return false; if (!ParseTypeParams(name, type)) return false; i = next + 1; } return true; } bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector &types) { types.Clear(); for (unsigned pos = 0; pos < s.Len();) { int pos2 = s.Find('.', pos); if (pos2 < 0) pos2 = s.Len(); UString name = s.Mid(pos, pos2 - pos); if (name.IsEmpty()) return false; COpenType type; if (!ParseType(codecs, name, type)) return false; types.Add(type); pos = pos2 + 1; } return true; } #endif src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.h000066400000000000000000000221011325366651500217300ustar00rootroot00000000000000// OpenArchive.h #ifndef __OPEN_ARCHIVE_H #define __OPEN_ARCHIVE_H #include "../../../Windows/PropVariant.h" #include "ArchiveOpenCallback.h" #include "LoadCodecs.h" #include "Property.h" HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw(); HRESULT Archive_IsItem_Folder(IInArchive *arc, UInt32 index, bool &result) throw(); HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw(); HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw(); HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &deleted) throw(); /* struct COptionalOpenProperties { UString FormatName; CObjectVector Props; }; */ #ifdef _SFX #define OPEN_PROPS_DECL #else #define OPEN_PROPS_DECL const CObjectVector *props; // #define OPEN_PROPS_DECL , const CObjectVector *props #endif struct COpenSpecFlags { // bool CanReturnFull; bool CanReturnFrontal; bool CanReturnTail; bool CanReturnMid; bool CanReturn_NonStart() const { return CanReturnTail || CanReturnMid; } COpenSpecFlags(): // CanReturnFull(true), CanReturnFrontal(false), CanReturnTail(false), CanReturnMid(false) {} }; struct COpenType { int FormatIndex; COpenSpecFlags SpecForcedType; COpenSpecFlags SpecMainType; COpenSpecFlags SpecWrongExt; COpenSpecFlags SpecUnknownExt; bool Recursive; bool CanReturnArc; bool CanReturnParser; bool EachPos; // bool SkipSfxStub; // bool ExeAsUnknown; bool ZerosTailIsAllowed; bool MaxStartOffset_Defined; UInt64 MaxStartOffset; const COpenSpecFlags &GetSpec(bool isForced, bool isMain, bool isUnknown) const { return isForced ? SpecForcedType : (isMain ? SpecMainType : (isUnknown ? SpecUnknownExt : SpecWrongExt)); } COpenType(): FormatIndex(-1), Recursive(true), EachPos(false), CanReturnArc(true), CanReturnParser(false), // SkipSfxStub(true), // ExeAsUnknown(true), ZerosTailIsAllowed(false), MaxStartOffset_Defined(false), MaxStartOffset(0) { SpecForcedType.CanReturnFrontal = true; SpecForcedType.CanReturnTail = true; SpecForcedType.CanReturnMid = true; SpecMainType.CanReturnFrontal = true; SpecUnknownExt.CanReturnTail = true; // for sfx SpecUnknownExt.CanReturnMid = true; SpecUnknownExt.CanReturnFrontal = true; // for alt streams of sfx with pad // ZerosTailIsAllowed = true; } }; struct COpenOptions { CCodecs *codecs; COpenType openType; const CObjectVector *types; const CIntVector *excludedFormats; IInStream *stream; ISequentialInStream *seqStream; IArchiveOpenCallback *callback; COpenCallbackImp *callbackSpec; OPEN_PROPS_DECL // bool openOnlySpecifiedByExtension, bool stdInMode; UString filePath; COpenOptions(): codecs(NULL), types(NULL), excludedFormats(NULL), stream(NULL), seqStream(NULL), callback(NULL), callbackSpec(NULL), stdInMode(false) {} }; UInt32 GetOpenArcErrorFlags(const NWindows::NCOM::CPropVariant &prop, bool *isDefinedProp = NULL); struct CArcErrorInfo { bool ThereIsTail; bool UnexpecedEnd; bool IgnoreTail; // all are zeros // bool NonZerosTail; bool ErrorFlags_Defined; UInt32 ErrorFlags; UInt32 WarningFlags; int ErrorFormatIndex; // - 1 means no Error. // if FormatIndex == ErrorFormatIndex, the archive is open with offset UInt64 TailSize; /* if CArc is Open OK with some format: - ErrorFormatIndex shows error format index, if extension is incorrect - other variables show message and warnings of archive that is open */ UString ErrorMessage; UString WarningMessage; // call IsArc_After_NonOpen only if Open returns S_FALSE bool IsArc_After_NonOpen() const { return (ErrorFlags_Defined && (ErrorFlags & kpv_ErrorFlags_IsNotArc) == 0); } CArcErrorInfo(): ThereIsTail(false), UnexpecedEnd(false), IgnoreTail(false), // NonZerosTail(false), ErrorFlags_Defined(false), ErrorFlags(0), WarningFlags(0), ErrorFormatIndex(-1), TailSize(0) {} void ClearErrors(); void ClearErrors_Full() { ErrorFormatIndex = -1; ClearErrors(); } bool IsThereErrorOrWarning() const { return ErrorFlags != 0 || WarningFlags != 0 || NeedTailWarning() || UnexpecedEnd || !ErrorMessage.IsEmpty() || !WarningMessage.IsEmpty(); } bool AreThereErrors() const { return ErrorFlags != 0 || UnexpecedEnd; } bool AreThereWarnings() const { return WarningFlags != 0 || NeedTailWarning(); } bool NeedTailWarning() const { return !IgnoreTail && ThereIsTail; } UInt32 GetWarningFlags() const { UInt32 a = WarningFlags; if (NeedTailWarning() && (ErrorFlags & kpv_ErrorFlags_DataAfterEnd) == 0) a |= kpv_ErrorFlags_DataAfterEnd; return a; } UInt32 GetErrorFlags() const { UInt32 a = ErrorFlags; if (UnexpecedEnd) a |= kpv_ErrorFlags_UnexpectedEnd; return a; } }; class CArc { HRESULT PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr &archive); HRESULT CheckZerosTail(const COpenOptions &op, UInt64 offset); HRESULT OpenStream2(const COpenOptions &options); public: CMyComPtr Archive; CMyComPtr InStream; // we use InStream in 2 cases (ArcStreamOffset != 0): // 1) if we use additional cache stream // 2) we reopen sfx archive with CTailInStream CMyComPtr GetRawProps; CMyComPtr GetRootProps; CArcErrorInfo ErrorInfo; // for OK archives CArcErrorInfo NonOpen_ErrorInfo; // ErrorInfo for mainArchive (false OPEN) UString Path; UString filePath; UString DefaultName; int FormatIndex; // - 1 means Parser. int SubfileIndex; FILETIME MTime; bool MTimeDefined; Int64 Offset; // it's offset of start of archive inside stream that is open by Archive Handler UInt64 PhySize; // UInt64 OkPhySize; bool PhySizeDefined; // bool OkPhySize_Defined; UInt64 FileSize; UInt64 AvailPhySize; // PhySize, but it's reduced if exceed end of file // bool offsetDefined; UInt64 ArcStreamOffset; // offset of stream that is open by Archive Handler Int64 GetGlobalOffset() const { return ArcStreamOffset + Offset; } // it's global offset of archive // AString ErrorFlagsText; bool IsParseArc; bool IsTree; bool Ask_Deleted; bool Ask_AltStream; bool Ask_Aux; bool Ask_INode; bool IgnoreSplit; // don't try split handler // void Set_ErrorFlagsText(); CArc(): MTimeDefined(false), IsTree(false), Ask_Deleted(false), Ask_AltStream(false), Ask_Aux(false), Ask_INode(false), IgnoreSplit(false) {} HRESULT ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes); // ~CArc(); HRESULT Close() { InStream.Release(); return Archive->Close(); } // AltStream's name is concatenated with base file name in one string in parts.Back() HRESULT GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const; HRESULT GetItemPath(UInt32 index, UString &result) const; // GetItemPath2 adds [DELETED] dir prefix for deleted items. HRESULT GetItemPath2(UInt32 index, UString &result) const; HRESULT GetItemSize(UInt32 index, UInt64 &size, bool &defined) const; HRESULT GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const; HRESULT IsItemAnti(UInt32 index, bool &result) const { return Archive_GetItemBoolProp(Archive, index, kpidIsAnti, result); } HRESULT OpenStream(const COpenOptions &options); HRESULT OpenStreamOrFile(COpenOptions &options); HRESULT ReOpen(const COpenOptions &options); HRESULT CreateNewTailStream(CMyComPtr &stream); }; struct CArchiveLink { CObjectVector Arcs; UStringVector VolumePaths; UInt64 VolumesSize; bool IsOpen; // int NonOpenErrorFormatIndex; // - 1 means no Error. UString NonOpen_ArcPath; CArcErrorInfo NonOpen_ErrorInfo; // UString ErrorsText; // void Set_ErrorsText(); CArchiveLink(): VolumesSize(0), IsOpen(false) {} void KeepModeForNextOpen(); HRESULT Close(); void Release(); ~CArchiveLink() { Release(); } const CArc *GetArc() const { return &Arcs.Back(); } IInArchive *GetArchive() const { return Arcs.Back().Archive; } IArchiveGetRawProps *GetArchiveGetRawProps() const { return Arcs.Back().GetRawProps; } IArchiveGetRootProps *GetArchiveGetRootProps() const { return Arcs.Back().GetRootProps; } HRESULT Open(COpenOptions &options); HRESULT Open2(COpenOptions &options, IOpenCallbackUI *callbackUI); HRESULT ReOpen(COpenOptions &options); }; bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector &types); #endif src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.cpp000066400000000000000000000307011325366651500222430ustar00rootroot00000000000000// PropIDUtils.cpp #include "StdAfx.h" #include "../../../../C/CpuArch.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileIO.h" #include "../../../Windows/PropVariantConv.h" #include "../../PropID.h" #include "PropIDUtils.h" #define Get16(x) GetUi16(x) #define Get32(x) GetUi32(x) using namespace NWindows; static const char g_WinAttribChars[16 + 1] = "RHS8DAdNTsLCOnE_"; /* 0 READONLY 1 HIDDEN 2 SYSTEM 4 DIRECTORY 5 ARCHIVE 6 DEVICE 7 NORMAL 8 TEMPORARY 9 SPARSE_FILE 10 REPARSE_POINT 11 COMPRESSED 12 OFFLINE 13 NOT_CONTENT_INDEXED 14 ENCRYPTED 16 VIRTUAL */ void ConvertWinAttribToString(char *s, UInt32 wa) { for (int i = 0; i < 16; i++) if ((wa & (1 << i)) && i != 7) *s++ = g_WinAttribChars[i]; *s = 0; } static const char kPosixTypes[16] = { '0', 'p', 'c', '3', 'd', '5', 'b', '7', '-', '9', 'l', 'B', 's', 'D', 'E', 'F' }; #define MY_ATTR_CHAR(a, n, c) ((a) & (1 << (n))) ? c : '-'; void ConvertPropertyToShortString(char *dest, const PROPVARIANT &prop, PROPID propID, bool full) throw() { *dest = 0; if (prop.vt == VT_FILETIME) { FILETIME localFileTime; if ((prop.filetime.dwHighDateTime == 0 && prop.filetime.dwLowDateTime == 0) || !::FileTimeToLocalFileTime(&prop.filetime, &localFileTime)) return; ConvertFileTimeToString(localFileTime, dest, true, full); return; } switch (propID) { case kpidCRC: { if (prop.vt != VT_UI4) break; ConvertUInt32ToHex8Digits(prop.ulVal, dest); return; } case kpidAttrib: { if (prop.vt != VT_UI4) break; ConvertWinAttribToString(dest, prop.ulVal); return; } case kpidPosixAttrib: { if (prop.vt != VT_UI4) break; UString res; UInt32 a = prop.ulVal; dest[0] = kPosixTypes[(a >> 12) & 0xF]; for (int i = 6; i >= 0; i -= 3) { dest[7 - i] = MY_ATTR_CHAR(a, i + 2, 'r'); dest[8 - i] = MY_ATTR_CHAR(a, i + 1, 'w'); dest[9 - i] = MY_ATTR_CHAR(a, i + 0, 'x'); } if ((a & 0x800) != 0) dest[3] = ((a & (1 << 6)) ? 's' : 'S'); if ((a & 0x400) != 0) dest[6] = ((a & (1 << 3)) ? 's' : 'S'); if ((a & 0x200) != 0) dest[9] = ((a & (1 << 0)) ? 't' : 'T'); dest[10] = 0; a &= ~(UInt32)0xFFFF; if (a != 0) { dest[10] = ' '; ConvertUInt32ToHex8Digits(a, dest + 11); } return; } case kpidINode: { if (prop.vt != VT_UI8) break; ConvertUInt32ToString((UInt32)(prop.uhVal.QuadPart >> 48), dest); dest += strlen(dest); *dest++ = '-'; UInt64 low = prop.uhVal.QuadPart & (((UInt64)1 << 48) - 1); ConvertUInt64ToString(low, dest); return; } case kpidVa: { UInt64 v = 0; if (ConvertPropVariantToUInt64(prop, v)) { dest[0] = '0'; dest[1] = 'x'; ConvertUInt64ToHex(prop.ulVal, dest + 2); return; } break; } } ConvertPropVariantToShortString(prop, dest); } void ConvertPropertyToString(UString &dest, const PROPVARIANT &prop, PROPID propID, bool full) { if (prop.vt == VT_BSTR) { dest = prop.bstrVal; return; } char temp[64]; ConvertPropertyToShortString(temp, prop, propID, full); int len = MyStringLen(temp); wchar_t *str = dest.GetBuffer(len); for (int i = 0; i < len; i++) str[i] = temp[i]; dest.ReleaseBuffer(len); } static inline char GetHex(Byte value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } #ifndef _SFX static inline void AddHexToString(AString &res, Byte value) { res += GetHex((Byte)(value >> 4)); res += GetHex((Byte)(value & 0xF)); res += ' '; } /* static AString Data_To_Hex(const Byte *data, size_t size) { AString s; for (size_t i = 0; i < size; i++) AddHexToString(s, data[i]); return s; } */ static const char *sidNames[] = { "0", "Dialup", "Network", "Batch", "Interactive", "Logon", // S-1-5-5-X-Y "Service", "Anonymous", "Proxy", "EnterpriseDC", "Self", "AuthenticatedUsers", "RestrictedCode", "TerminalServer", "RemoteInteractiveLogon", "ThisOrganization", "16", "IUserIIS", "LocalSystem", "LocalService", "NetworkService", "Domains" }; struct CSecID2Name { UInt32 n; const char *sz; }; const CSecID2Name sid_32_Names[] = { { 544, "Administrators" }, { 545, "Users" }, { 546, "Guests" }, { 547, "PowerUsers" }, { 548, "AccountOperators" }, { 549, "ServerOperators" }, { 550, "PrintOperators" }, { 551, "BackupOperators" }, { 552, "Replicators" }, { 553, "Backup Operators" }, { 554, "PreWindows2000CompatibleAccess" }, { 555, "RemoteDesktopUsers" }, { 556, "NetworkConfigurationOperators" }, { 557, "IncomingForestTrustBuilders" }, { 558, "PerformanceMonitorUsers" }, { 559, "PerformanceLogUsers" }, { 560, "WindowsAuthorizationAccessGroup" }, { 561, "TerminalServerLicenseServers" }, { 562, "DistributedCOMUsers" }, { 569, "CryptographicOperators" }, { 573, "EventLogReaders" }, { 574, "CertificateServiceDCOMAccess" } }; static const CSecID2Name sid_21_Names[] = { { 500, "Administrator" }, { 501, "Guest" }, { 502, "KRBTGT" }, { 512, "DomainAdmins" }, { 513, "DomainUsers" }, { 515, "DomainComputers" }, { 516, "DomainControllers" }, { 517, "CertPublishers" }, { 518, "SchemaAdmins" }, { 519, "EnterpriseAdmins" }, { 520, "GroupPolicyCreatorOwners" }, { 553, "RASandIASServers" }, { 553, "RASandIASServers" }, { 571, "AllowedRODCPasswordReplicationGroup" }, { 572, "DeniedRODCPasswordReplicationGroup" } }; struct CServicesToName { UInt32 n[5]; const char *sz; }; static const CServicesToName services_to_name[] = { { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" } }; static void ParseSid(AString &s, const Byte *p, UInt32 lim, UInt32 &sidSize) { sidSize = 0; if (lim < 8) { s += "ERROR"; return; } UInt32 rev = p[0]; if (rev != 1) { s += "UNSUPPORTED"; return; } UInt32 num = p[1]; if (8 + num * 4 > lim) { s += "ERROR"; return; } sidSize = 8 + num * 4; UInt32 authority = GetBe32(p + 4); if (p[2] == 0 && p[3] == 0 && authority == 5 && num >= 1) { UInt32 v0 = Get32(p + 8); if (v0 < ARRAY_SIZE(sidNames)) { s += sidNames[v0]; return; } if (v0 == 32 && num == 2) { UInt32 v1 = Get32(p + 12); for (int i = 0; i < ARRAY_SIZE(sid_32_Names); i++) if (sid_32_Names[i].n == v1) { s += sid_32_Names[i].sz; return; } } if (v0 == 21 && num == 5) { UInt32 v4 = Get32(p + 8 + 4 * 4); for (int i = 0; i < ARRAY_SIZE(sid_21_Names); i++) if (sid_21_Names[i].n == v4) { s += sid_21_Names[i].sz; return; } } if (v0 == 80 && num == 6) { for (int i = 0; i < ARRAY_SIZE(services_to_name); i++) { const CServicesToName &sn = services_to_name[i]; int j; for (j = 0; j < 5 && sn.n[j] == Get32(p + 8 + 4 + j * 4); j++); if (j == 5) { s += sn.sz; return; } } } } char sz[16]; s += "S-1-"; if (p[2] == 0 && p[3] == 0) { ConvertUInt32ToString(authority, sz); s += sz; } else { s += "0x"; for (int i = 2; i < 8; i++) AddHexToString(s, p[i]); } for (UInt32 i = 0; i < num; i++) { s += '-'; ConvertUInt32ToString(Get32(p + 8 + i * 4), sz); s += sz; } } static void ParseOwner(AString &s, const Byte *p, UInt32 size, UInt32 pos) { if (pos > size) { s += "ERROR"; return; } UInt32 sidSize = 0; ParseSid(s, p + pos, size - pos, sidSize); } static void AddUInt32ToString(AString &s, UInt32 val) { char sz[16]; ConvertUInt32ToString(val, sz); s += sz; } static void ParseAcl(AString &s, const Byte *p, UInt32 size, const char *strName, UInt32 flags, UInt32 offset) { UInt32 control = Get16(p + 2); if ((flags & control) == 0) return; UInt32 pos = Get32(p + offset); s += ' '; s += strName; if (pos >= size) return; p += pos; size -= pos; if (size < 8) return; if (Get16(p) != 2) // revision return; // UInt32 aclSize = Get16(p + 2); UInt32 num = Get32(p + 4); AddUInt32ToString(s, num); /* if (num >= (1 << 16)) return; if (aclSize > size) return; size = aclSize; size -= 8; p += 8; for (UInt32 i = 0 ; i < num; i++) { if (size <= 8) return; // Byte type = p[0]; // Byte flags = p[1]; // UInt32 aceSize = Get16(p + 2); // UInt32 mask = Get32(p + 4); p += 8; size -= 8; UInt32 sidSize = 0; s += ' '; s += ParseSid(p, size, sidSize); if (sidSize == 0) return; p += sidSize; size -= sidSize; } if (size != 0) s += " ERROR"; */ } #define MY_SE_OWNER_DEFAULTED (0x0001) #define MY_SE_GROUP_DEFAULTED (0x0002) #define MY_SE_DACL_PRESENT (0x0004) #define MY_SE_DACL_DEFAULTED (0x0008) #define MY_SE_SACL_PRESENT (0x0010) #define MY_SE_SACL_DEFAULTED (0x0020) #define MY_SE_DACL_AUTO_INHERIT_REQ (0x0100) #define MY_SE_SACL_AUTO_INHERIT_REQ (0x0200) #define MY_SE_DACL_AUTO_INHERITED (0x0400) #define MY_SE_SACL_AUTO_INHERITED (0x0800) #define MY_SE_DACL_PROTECTED (0x1000) #define MY_SE_SACL_PROTECTED (0x2000) #define MY_SE_RM_CONTROL_VALID (0x4000) #define MY_SE_SELF_RELATIVE (0x8000) void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s) { s.Empty(); if (size < 20 || size > (1 << 18)) { s += "ERROR"; return; } if (Get16(data) != 1) // revision { s += "UNSUPPORTED"; return; } ParseOwner(s, data, size, Get32(data + 4)); s += ' '; ParseOwner(s, data, size, Get32(data + 8)); ParseAcl(s, data, size, "s:", MY_SE_SACL_PRESENT, 12); ParseAcl(s, data, size, "d:", MY_SE_DACL_PRESENT, 16); s += ' '; AddUInt32ToString(s, size); // s += '\n'; // s += Data_To_Hex(data, size); } #ifdef _WIN32 static bool CheckSid(const Byte *data, UInt32 size, UInt32 pos) { if (pos >= size) return false; size -= pos; if (size < 8) return false; UInt32 rev = data[pos]; if (rev != 1) return false; UInt32 num = data[pos + 1]; return (8 + num * 4 <= size); } static bool CheckAcl(const Byte *p, UInt32 size, UInt32 flags, UInt32 offset) { UInt32 control = Get16(p + 2); if ((flags & control) == 0) return true; UInt32 pos = Get32(p + offset); if (pos >= size) return false; p += pos; size -= pos; if (size < 8) return false; UInt32 aclSize = Get16(p + 2); return (aclSize <= size); } bool CheckNtSecure(const Byte *data, UInt32 size) { if (size < 20) return false; if (Get16(data) != 1) // revision return true; // windows function can handle such error, so we allow it if (size > (1 << 18)) return false; if (!CheckSid(data, size, Get32(data + 4))) return false; if (!CheckSid(data, size, Get32(data + 8))) return false; if (!CheckAcl(data, size, MY_SE_SACL_PRESENT, 12)) return false; if (!CheckAcl(data, size, MY_SE_DACL_PRESENT, 16)) return false; return true; } #endif bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s) { s.Empty(); NFile::CReparseAttr attr; if (attr.Parse(data, size)) { if (!attr.IsSymLink()) s += L"Junction: "; s += attr.GetPath(); if (!attr.IsOkNamePair()) { s += L" : "; s += attr.PrintName; } return true; } if (size < 8) return false; UInt32 tag = Get32(data); UInt32 len = Get16(data + 4); if (len + 8 > size) return false; if (Get16(data + 6) != 0) // padding return false; char hex[16]; ConvertUInt32ToHex8Digits(tag, hex); s.AddAsciiStr(hex); s += L' '; data += 8; for (UInt32 i = 0; i < len; i++) { Byte b = ((const Byte *)data)[i]; s += (wchar_t)GetHex((Byte)((b >> 4) & 0xF)); s += (wchar_t)GetHex((Byte)(b & 0xF)); } return true; } #endif src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.h000066400000000000000000000012401325366651500217040ustar00rootroot00000000000000// PropIDUtils.h #ifndef __PROPID_UTILS_H #define __PROPID_UTILS_H #include "../../../Common/MyString.h" // provide at least 64 bytes for buffer including zero-end void ConvertPropertyToShortString(char *dest, const PROPVARIANT &propVariant, PROPID propID, bool full = true) throw(); void ConvertPropertyToString(UString &dest, const PROPVARIANT &propVariant, PROPID propID, bool full = true); bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s); void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s); bool CheckNtSecure(const Byte *data, UInt32 size); void ConvertWinAttribToString(char *s, UInt32 wa); #endif src/libs/7zip/win/CPP/7zip/UI/Common/Property.h000066400000000000000000000002641325366651500213570ustar00rootroot00000000000000// Property.h #ifndef __7Z_PROPERTY_H #define __7Z_PROPERTY_H #include "../../../Common/MyString.h" struct CProperty { UString Name; UString Value; }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.cpp000066400000000000000000000037531325366651500227040ustar00rootroot00000000000000// SetProperties.cpp #include "StdAfx.h" #include "../../../Common/MyCom.h" #include "../../../Common/MyString.h" #include "../../../Common/StringToInt.h" #include "../../../Windows/PropVariant.h" #include "../../Archive/IArchive.h" #include "SetProperties.h" using namespace NWindows; using namespace NCOM; static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop) { const wchar_t *end; UInt64 result = ConvertStringToUInt64(s, &end); if (*end != 0 || s.IsEmpty()) prop = s; else if (result <= (UInt32)0xFFFFFFFF) prop = (UInt32)result; else prop = result; } HRESULT SetProperties(IUnknown *unknown, const CObjectVector &properties) { if (properties.IsEmpty()) return S_OK; CMyComPtr setProperties; unknown->QueryInterface(IID_ISetProperties, (void **)&setProperties); if (!setProperties) return S_OK; UStringVector realNames; CPropVariant *values = new CPropVariant[properties.Size()]; try { unsigned i; for (i = 0; i < properties.Size(); i++) { const CProperty &property = properties[i]; NCOM::CPropVariant propVariant; UString name = property.Name; if (property.Value.IsEmpty()) { if (!name.IsEmpty()) { wchar_t c = name.Back(); if (c == L'-') propVariant = false; else if (c == L'+') propVariant = true; if (propVariant.vt != VT_EMPTY) name.DeleteBack(); } } else ParseNumberString(property.Value, propVariant); realNames.Add(name); values[i] = propVariant; } CRecordVector names; for (i = 0; i < realNames.Size(); i++) names.Add((const wchar_t *)realNames[i]); RINOK(setProperties->SetProperties(&names.Front(), values, names.Size())); } catch(...) { delete []values; throw; } delete []values; return S_OK; } src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.h000066400000000000000000000003101325366651500223330ustar00rootroot00000000000000// SetProperties.h #ifndef __SETPROPERTIES_H #define __SETPROPERTIES_H #include "Property.h" HRESULT SetProperties(IUnknown *unknown, const CObjectVector &properties); #endif src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.cpp000066400000000000000000000011531325366651500220340ustar00rootroot00000000000000// SortUtils.cpp #include "StdAfx.h" #include "../../../Common/Wildcard.h" #include "SortUtils.h" static int CompareStrings(const unsigned *p1, const unsigned *p2, void *param) { const UStringVector &strings = *(const UStringVector *)param; return CompareFileNames(strings[*p1], strings[*p2]); } void SortFileNames(const UStringVector &strings, CUIntVector &indices) { unsigned numItems = strings.Size(); indices.ClearAndSetSize(numItems); unsigned *vals = &indices[0]; for (unsigned i = 0; i < numItems; i++) vals[i] = i; indices.Sort(CompareStrings, (void *)&strings); } src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.h000066400000000000000000000002761325366651500215060ustar00rootroot00000000000000// SortUtils.h #ifndef __SORT_UTLS_H #define __SORT_UTLS_H #include "../../../Common/MyString.h" void SortFileNames(const UStringVector &strings, CUIntVector &indices); #endif src/libs/7zip/win/CPP/7zip/UI/Common/StdAfx.h000066400000000000000000000001501325366651500207160ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../../../Common/Common.h" #endif src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.cpp000066400000000000000000000004611325366651500217550ustar00rootroot00000000000000// TempFiles.cpp #include "StdAfx.h" #include "../../../Windows/FileDir.h" #include "TempFiles.h" using namespace NWindows; using namespace NFile; void CTempFiles::Clear() { while (!Paths.IsEmpty()) { NDir::DeleteFileAlways(Paths.Back()); Paths.DeleteBack(); } } src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.h000066400000000000000000000003401325366651500214160ustar00rootroot00000000000000// TempFiles.h #ifndef __TEMP_FILES_H #define __TEMP_FILES_H #include "../../../Common/MyString.h" class CTempFiles { void Clear(); public: FStringVector Paths; ~CTempFiles() { Clear(); } }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/Update.cpp000066400000000000000000001157331325366651500213200ustar00rootroot00000000000000// Update.cpp #include "StdAfx.h" #include "Update.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Windows/DLL.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileFind.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/PropVariantConv.h" #include "../../../Windows/TimeUtils.h" #include "../../Common/FileStreams.h" #include "../../Common/LimitedStreams.h" #include "../../Compress/CopyCoder.h" #include "../Common/DirItem.h" #include "../Common/EnumDirItems.h" #include "../Common/OpenArchive.h" #include "../Common/UpdateProduce.h" #include "EnumDirItems.h" #include "SetProperties.h" #include "TempFiles.h" #include "UpdateCallback.h" static const char *kUpdateIsNotSupoorted = "update operations are not supported for this archive"; using namespace NWindows; using namespace NCOM; using namespace NFile; using namespace NDir; using namespace NName; static CFSTR kTempFolderPrefix = FTEXT("7zE"); static bool DeleteEmptyFolderAndEmptySubFolders(const FString &path) { NFind::CFileInfo fileInfo; FString pathPrefix = path + FCHAR_PATH_SEPARATOR; { NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK); while (enumerator.Next(fileInfo)) { if (fileInfo.IsDir()) if (!DeleteEmptyFolderAndEmptySubFolders(pathPrefix + fileInfo.Name)) return false; } } /* // we don't need clear read-only for folders if (!MySetFileAttributes(path, 0)) return false; */ return RemoveDir(path); } using namespace NUpdateArchive; class COutMultiVolStream: public IOutStream, public CMyUnknownImp { unsigned _streamIndex; // required stream UInt64 _offsetPos; // offset from start of _streamIndex index UInt64 _absPos; UInt64 _length; struct CAltStreamInfo { COutFileStream *StreamSpec; CMyComPtr Stream; FString Name; UInt64 Pos; UInt64 RealSize; }; CObjectVector Streams; public: // CMyComPtr VolumeCallback; CRecordVector Sizes; FString Prefix; CTempFiles *TempFiles; void Init() { _streamIndex = 0; _offsetPos = 0; _absPos = 0; _length = 0; } bool SetMTime(const FILETIME *mTime); HRESULT Close(); MY_UNKNOWN_IMP1(IOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); STDMETHOD(SetSize)(UInt64 newSize); }; // static NSynchronization::CCriticalSection g_TempPathsCS; HRESULT COutMultiVolStream::Close() { HRESULT res = S_OK; FOR_VECTOR (i, Streams) { COutFileStream *s = Streams[i].StreamSpec; if (s) { HRESULT res2 = s->Close(); if (res2 != S_OK) res = res2; } } return res; } bool COutMultiVolStream::SetMTime(const FILETIME *mTime) { bool res = true; FOR_VECTOR (i, Streams) { COutFileStream *s = Streams[i].StreamSpec; if (s) if (!s->SetMTime(mTime)) res = false; } return res; } STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) *processedSize = 0; while (size > 0) { if (_streamIndex >= Streams.Size()) { CAltStreamInfo altStream; FChar temp[16]; ConvertUInt32ToString(_streamIndex + 1, temp); FString res = temp; while (res.Len() < 3) res = FString(FTEXT('0')) + res; FString name = Prefix + res; altStream.StreamSpec = new COutFileStream; altStream.Stream = altStream.StreamSpec; if (!altStream.StreamSpec->Create(name, false)) return ::GetLastError(); { // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS); TempFiles->Paths.Add(name); } altStream.Pos = 0; altStream.RealSize = 0; altStream.Name = name; Streams.Add(altStream); continue; } CAltStreamInfo &altStream = Streams[_streamIndex]; unsigned index = _streamIndex; if (index >= Sizes.Size()) index = Sizes.Size() - 1; UInt64 volSize = Sizes[index]; if (_offsetPos >= volSize) { _offsetPos -= volSize; _streamIndex++; continue; } if (_offsetPos != altStream.Pos) { // CMyComPtr outStream; // RINOK(altStream.Stream.QueryInterface(IID_IOutStream, &outStream)); RINOK(altStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); altStream.Pos = _offsetPos; } UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - altStream.Pos); UInt32 realProcessed; RINOK(altStream.Stream->Write(data, curSize, &realProcessed)); data = (void *)((Byte *)data + realProcessed); size -= realProcessed; altStream.Pos += realProcessed; _offsetPos += realProcessed; _absPos += realProcessed; if (_absPos > _length) _length = _absPos; if (_offsetPos > altStream.RealSize) altStream.RealSize = _offsetPos; if (processedSize != NULL) *processedSize += realProcessed; if (altStream.Pos == volSize) { _streamIndex++; _offsetPos = 0; } if (realProcessed == 0 && curSize != 0) return E_FAIL; break; } return S_OK; } STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; switch (seekOrigin) { case STREAM_SEEK_SET: _absPos = offset; break; case STREAM_SEEK_CUR: _absPos += offset; break; case STREAM_SEEK_END: _absPos = _length + offset; break; } _offsetPos = _absPos; if (newPosition != NULL) *newPosition = _absPos; _streamIndex = 0; return S_OK; } STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize) { if (newSize < 0) return E_INVALIDARG; unsigned i = 0; while (i < Streams.Size()) { CAltStreamInfo &altStream = Streams[i++]; if ((UInt64)newSize < altStream.RealSize) { RINOK(altStream.Stream->SetSize(newSize)); altStream.RealSize = newSize; break; } newSize -= altStream.RealSize; } while (i < Streams.Size()) { { CAltStreamInfo &altStream = Streams.Back(); altStream.Stream.Release(); DeleteFileAlways(altStream.Name); } Streams.DeleteBack(); } _offsetPos = _absPos; _streamIndex = 0; _length = newSize; return S_OK; } void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode) { OriginalPath = path; SplitPathToParts_2(path, Prefix, Name); if (mode == k_ArcNameMode_Add) return; if (mode == k_ArcNameMode_Exact) { BaseExtension.Empty(); return; } int dotPos = Name.ReverseFind(L'.'); if (dotPos < 0) return; if ((unsigned)dotPos == Name.Len() - 1) { Name.DeleteBack(); BaseExtension.Empty(); return; } const UString ext = Name.Ptr(dotPos + 1); if (BaseExtension.IsEqualToNoCase(ext)) { BaseExtension = ext; Name.DeleteFrom(dotPos); } else BaseExtension.Empty(); } UString CArchivePath::GetFinalPath() const { UString path = GetPathWithoutExt(); if (!BaseExtension.IsEmpty()) path += UString(L'.') + BaseExtension; return path; } UString CArchivePath::GetFinalVolPath() const { UString path = GetPathWithoutExt(); if (!BaseExtension.IsEmpty()) path += UString(L'.') + VolExtension; return path; } FString CArchivePath::GetTempPath() const { FString path = TempPrefix + us2fs(Name); if (!BaseExtension.IsEmpty()) path += FString(FTEXT('.')) + us2fs(BaseExtension); path += FTEXT(".tmp"); path += TempPostfix; return path; } static const wchar_t *kDefaultArcType = L"7z"; static const wchar_t *kDefaultArcExt = L"7z"; static const wchar_t *kSFXExtension = #ifdef _WIN32 L"exe"; #else L""; #endif bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs, const CObjectVector &types, const UString &arcPath) { if (types.Size() > 1) return false; // int arcTypeIndex = -1; if (types.Size() != 0) { MethodMode.Type = types[0]; MethodMode.Type_Defined = true; } if (MethodMode.Type.FormatIndex < 0) { // MethodMode.Type = -1; MethodMode.Type = COpenType(); if (ArcNameMode != k_ArcNameMode_Add) { MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath); if (MethodMode.Type.FormatIndex >= 0) MethodMode.Type_Defined = true; } } return true; } bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath) { UString typeExt; int formatIndex = MethodMode.Type.FormatIndex; if (formatIndex < 0) { typeExt = kDefaultArcExt; } else { const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; if (!arcInfo.UpdateEnabled) return false; typeExt = arcInfo.GetMainExt(); } UString ext = typeExt; if (SfxMode) ext = kSFXExtension; ArchivePath.BaseExtension = ext; ArchivePath.VolExtension = typeExt; ArchivePath.ParseFromPath(arcPath, ArcNameMode); FOR_VECTOR (i, Commands) { CUpdateArchiveCommand &uc = Commands[i]; uc.ArchivePath.BaseExtension = ext; uc.ArchivePath.VolExtension = typeExt; uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode); } return true; } /* struct CUpdateProduceCallbackImp: public IUpdateProduceCallback { const CObjectVector *_arcItems; IUpdateCallbackUI *_callback; CUpdateProduceCallbackImp(const CObjectVector *a, IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {} virtual HRESULT ShowDeleteFile(int arcIndex); }; HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex) { return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name); } */ bool CRenamePair::Prepare() { if (RecursedType != NRecursedType::kNonRecursed) return false; if (!WildcardParsing) return true; return !DoesNameContainWildcard(OldName); } extern bool g_CaseSensitive; static int CompareTwoNames(const wchar_t *s1, const wchar_t *s2) { for (int i = 0;; i++) { wchar_t c1 = s1[i]; wchar_t c2 = s2[i]; if (c1 == 0 || c2 == 0) return i; if (c1 == c2) continue; if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2))) continue; if (IsCharDirLimiter(c1) && IsCharDirLimiter(c2)) continue; return i; } } bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const { int num = CompareTwoNames(OldName, src); if (OldName[num] == 0) { if (src[num] != 0 && !IsCharDirLimiter(src[num]) && num != 0 && !IsCharDirLimiter(src[num - 1])) return false; } else { // OldName[num] != 0 // OldName = "1\1a.txt" // src = "1" if (!isFolder || src[num] != 0 || !IsCharDirLimiter(OldName[num]) || OldName[num + 1] != 0) return false; } dest = NewName + src.Ptr(num); return true; } static int GetReverseSlashPos(const UString &name) { int slashPos = name.ReverseFind(L'/'); #ifdef _WIN32 int slash1Pos = name.ReverseFind(L'\\'); slashPos = MyMax(slashPos, slash1Pos); #endif return slashPos; } static HRESULT Compress( const CUpdateOptions &options, CCodecs *codecs, const CActionSet &actionSet, const CArc *arc, CArchivePath &archivePath, const CObjectVector &arcItems, Byte *processedItemsStatuses, const CDirItems &dirItems, const CDirItem *parentDirItem, CTempFiles &tempFiles, CUpdateErrorInfo &errorInfo, IUpdateCallbackUI *callback) { CMyComPtr outArchive; int formatIndex = options.MethodMode.Type.FormatIndex; if (arc) { formatIndex = arc->FormatIndex; if (formatIndex < 0) return E_NOTIMPL; CMyComPtr archive2 = arc->Archive; HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive); if (result != S_OK) throw kUpdateIsNotSupoorted; } else { RINOK(codecs->CreateOutArchive(formatIndex, outArchive)); #ifdef EXTERNAL_CODECS { CMyComPtr setCompressCodecsInfo; outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); } } #endif } if (outArchive == 0) throw kUpdateIsNotSupoorted; NFileTimeType::EEnum fileTimeType; UInt32 value; RINOK(outArchive->GetFileTimeType(&value)); switch (value) { case NFileTimeType::kWindows: case NFileTimeType::kUnix: case NFileTimeType::kDOS: fileTimeType = (NFileTimeType::EEnum)value; break; default: return E_FAIL; } { const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; if (options.AltStreams.Val && !arcInfo.Flags_AltStreams()) return E_NOTIMPL; if (options.NtSecurity.Val && !arcInfo.Flags_NtSecure()) return E_NOTIMPL; } CRecordVector updatePairs2; UStringVector newNames; if (options.RenamePairs.Size() != 0) { FOR_VECTOR (i, arcItems) { const CArcItem &ai = arcItems[i]; bool needRename = false; UString dest; if (ai.Censored) { FOR_VECTOR (j, options.RenamePairs) { const CRenamePair &rp = options.RenamePairs[j]; if (rp.GetNewPath(ai.IsDir, ai.Name, dest)) { needRename = true; break; } if (ai.IsAltStream) { int colonPos = ai.Name.ReverseFind(':'); int slashPosPos = GetReverseSlashPos(ai.Name); if (colonPos > slashPosPos) { UString mainName = ai.Name.Left(colonPos); /* actually we must improve that code to support cases with folder renaming like: rn arc dir1\ dir2\ */ if (rp.GetNewPath(false, mainName, dest)) { needRename = true; dest += ':'; dest += ai.Name.Ptr(colonPos + 1); break; } } } } } CUpdatePair2 up2; up2.SetAs_NoChangeArcItem(ai.IndexInServer); if (needRename) { up2.NewProps = true; RINOK(arc->IsItemAnti(i, up2.IsAnti)); up2.NewNameIndex = newNames.Add(dest); } updatePairs2.Add(up2); } } else { CRecordVector updatePairs; GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!! // CUpdateProduceCallbackImp upCallback(&arcItems, callback); UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */); } UInt32 numFiles = 0; FOR_VECTOR (i, updatePairs2) if (updatePairs2[i].NewData) numFiles++; RINOK(callback->SetNumFiles(numFiles)); CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr updateCallback(updateCallbackSpec); updateCallbackSpec->ShareForWrite = options.OpenShareForWrite; updateCallbackSpec->StdInMode = options.StdInMode; updateCallbackSpec->Callback = callback; if (arc) { // we set Archive to allow to transfer GetProperty requests back to DLL. updateCallbackSpec->Archive = arc->Archive; updateCallbackSpec->GetRawProps = arc->GetRawProps; updateCallbackSpec->GetRootProps = arc->GetRootProps; } updateCallbackSpec->DirItems = &dirItems; updateCallbackSpec->ParentDirItem = parentDirItem; updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val; updateCallbackSpec->StoreHardLinks = options.HardLinks.Val; updateCallbackSpec->StoreSymLinks = options.SymLinks.Val; updateCallbackSpec->ArcItems = &arcItems; updateCallbackSpec->UpdatePairs = &updatePairs2; updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses; if (options.RenamePairs.Size() != 0) updateCallbackSpec->NewNames = &newNames; CMyComPtr outSeekStream; CMyComPtr outStream; if (!options.StdOutMode) { FString dirPrefix; if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix)) throw 1417161; CreateComplexDir(dirPrefix); } COutFileStream *outStreamSpec = NULL; COutMultiVolStream *volStreamSpec = NULL; if (options.VolumesSizes.Size() == 0) { if (options.StdOutMode) outStream = new CStdOutFileStream; else { outStreamSpec = new COutFileStream; outSeekStream = outStreamSpec; outStream = outSeekStream; bool isOK = false; FString realPath; for (int i = 0; i < (1 << 16); i++) { if (archivePath.Temp) { if (i > 0) { FChar s[16]; ConvertUInt32ToString(i, s); archivePath.TempPostfix = s; } realPath = archivePath.GetTempPath(); } else realPath = us2fs(archivePath.GetFinalPath()); if (outStreamSpec->Create(realPath, false)) { tempFiles.Paths.Add(realPath); isOK = true; break; } if (::GetLastError() != ERROR_FILE_EXISTS) break; if (!archivePath.Temp) break; } if (!isOK) { errorInfo.SystemError = ::GetLastError(); errorInfo.FileName = realPath; errorInfo.Message = L"7-Zip cannot open file"; return E_FAIL; } } } else { if (options.StdOutMode) return E_FAIL; if (arc && arc->GetGlobalOffset() > 0) return E_NOTIMPL; volStreamSpec = new COutMultiVolStream; outSeekStream = volStreamSpec; outStream = outSeekStream; volStreamSpec->Sizes = options.VolumesSizes; volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath() + L"."); volStreamSpec->TempFiles = &tempFiles; volStreamSpec->Init(); /* updateCallbackSpec->VolumesSizes = volumesSizes; updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name; if (!archivePath.VolExtension.IsEmpty()) updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension; */ } RINOK(SetProperties(outArchive, options.MethodMode.Properties)); if (options.SfxMode) { CInFileStream *sfxStreamSpec = new CInFileStream; CMyComPtr sfxStream(sfxStreamSpec); if (!sfxStreamSpec->Open(options.SfxModule)) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot open SFX module"; errorInfo.FileName = options.SfxModule; return E_FAIL; } CMyComPtr sfxOutStream; COutFileStream *outStreamSpec = NULL; if (options.VolumesSizes.Size() == 0) sfxOutStream = outStream; else { outStreamSpec = new COutFileStream; sfxOutStream = outStreamSpec; FString realPath = us2fs(archivePath.GetFinalPath()); if (!outStreamSpec->Create(realPath, false)) { errorInfo.SystemError = ::GetLastError(); errorInfo.FileName = realPath; errorInfo.Message = L"7-Zip cannot open file"; return E_FAIL; } } RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL)); if (outStreamSpec) { RINOK(outStreamSpec->Close()); } } CMyComPtr tailStream; if (options.SfxMode || !arc || arc->ArcStreamOffset == 0) tailStream = outStream; else { // Int64 globalOffset = arc->GetGlobalOffset(); RINOK(arc->InStream->Seek(0, STREAM_SEEK_SET, NULL)); RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL)); if (options.StdOutMode) tailStream = outStream; else { CTailOutStream *tailStreamSpec = new CTailOutStream; tailStream = tailStreamSpec; tailStreamSpec->Stream = outSeekStream; tailStreamSpec->Offset = arc->ArcStreamOffset; tailStreamSpec->Init(); } } HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback); callback->Finilize(); RINOK(result); if (options.SetArcMTime) { FILETIME ft; ft.dwLowDateTime = 0; ft.dwHighDateTime = 0; FOR_VECTOR (i, updatePairs2) { CUpdatePair2 &pair2 = updatePairs2[i]; const FILETIME *ft2 = NULL; if (pair2.NewProps && pair2.DirIndex >= 0) ft2 = &dirItems.Items[pair2.DirIndex].MTime; else if (pair2.UseArcProps && pair2.ArcIndex >= 0) ft2 = &arcItems[pair2.ArcIndex].MTime; if (ft2) { if (::CompareFileTime(&ft, ft2) < 0) ft = *ft2; } } if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0) { if (outStreamSpec) outStreamSpec->SetMTime(&ft); else if (volStreamSpec) volStreamSpec->SetMTime(&ft);; } } if (outStreamSpec) result = outStreamSpec->Close(); else if (volStreamSpec) result = volStreamSpec->Close(); return result; } static HRESULT EnumerateInArchiveItems( // bool storeStreamsMode, const NWildcard::CCensor &censor, const CArc &arc, CObjectVector &arcItems) { arcItems.Clear(); UInt32 numItems; IInArchive *archive = arc.Archive; RINOK(archive->GetNumberOfItems(&numItems)); arcItems.ClearAndReserve(numItems); for (UInt32 i = 0; i < numItems; i++) { CArcItem ai; RINOK(arc.GetItemPath(i, ai.Name)); RINOK(Archive_IsItem_Folder(archive, i, ai.IsDir)); RINOK(Archive_IsItem_AltStream(archive, i, ai.IsAltStream)); /* if (!storeStreamsMode && ai.IsAltStream) continue; */ ai.Censored = censor.CheckPath(ai.IsAltStream, ai.Name, !ai.IsDir); RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined)); RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined)); { CPropVariant prop; RINOK(archive->GetProperty(i, kpidTimeType, &prop)); if (prop.vt == VT_UI4) { ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal; switch (ai.TimeType) { case NFileTimeType::kWindows: case NFileTimeType::kUnix: case NFileTimeType::kDOS: break; default: return E_FAIL; } } } ai.IndexInServer = i; arcItems.AddInReserved(ai); } return S_OK; } struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback { IUpdateCallbackUI2 *Callback; HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) { return Callback->ScanProgress(numFolders, numFiles, totalSize, path, isDir); } }; #if defined(_WIN32) && !defined(UNDER_CE) #include #endif struct CRefSortPair { int Len; int Index; }; #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } static int CompareRefSortPair(const CRefSortPair *a1, const CRefSortPair *a2, void *) { RINOZ(-MyCompare(a1->Len, a2->Len)); return MyCompare(a1->Index, a2->Index); } static int GetNumSlashes(const FChar *s) { for (int numSlashes = 0;;) { FChar c = *s++; if (c == 0) return numSlashes; if ( #ifdef _WIN32 c == FTEXT('\\') || #endif c == FTEXT('/')) numSlashes++; } } #ifdef _WIN32 void ConvertToLongNames(NWildcard::CCensor &censor); #endif HRESULT UpdateArchive( CCodecs *codecs, const CObjectVector &types, const UString &cmdArcPath2, NWildcard::CCensor &censor, CUpdateOptions &options, CUpdateErrorInfo &errorInfo, IOpenCallbackUI *openCallback, IUpdateCallbackUI2 *callback, bool needSetPath) { if (options.StdOutMode && options.EMailMode) return E_FAIL; if (types.Size() > 1) return E_NOTIMPL; bool renameMode = !options.RenamePairs.IsEmpty(); if (renameMode) { if (options.Commands.Size() != 1) return E_FAIL; } if (options.DeleteAfterCompressing) { if (options.Commands.Size() != 1) return E_NOTIMPL; const CActionSet &as = options.Commands[0].ActionSet; for (int i = 2; i < NPairState::kNumValues; i++) if (as.StateActions[i] != NPairAction::kCompress) return E_NOTIMPL; } censor.AddPathsToCensor(options.PathMode); #ifdef _WIN32 ConvertToLongNames(censor); #endif censor.ExtendExclude(); if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */)) return E_NOTIMPL; if (options.SfxMode) { CProperty property; property.Name = L"rsfx"; property.Value = L"on"; options.MethodMode.Properties.Add(property); if (options.SfxModule.IsEmpty()) { errorInfo.Message = L"SFX file is not specified"; return E_FAIL; } bool found = false; if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0) { const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule; if (NFind::DoesFileExist(fullName)) { options.SfxModule = fullName; found = true; } } if (!found) { if (!NFind::DoesFileExist(options.SfxModule)) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot find specified SFX module"; errorInfo.FileName = options.SfxModule; return E_FAIL; } } } CArchiveLink arcLink; if (needSetPath) { if (!options.InitFormatIndex(codecs, types, cmdArcPath2) || !options.SetArcPath(codecs, cmdArcPath2)) return E_NOTIMPL; } UString arcPath = options.ArchivePath.GetFinalPath(); if (cmdArcPath2.IsEmpty()) { if (options.MethodMode.Type.FormatIndex < 0) throw "type of archive is not specified"; } else { NFind::CFileInfo fi; if (!fi.Find(us2fs(arcPath))) { if (renameMode) throw "can't find archive";; if (options.MethodMode.Type.FormatIndex < 0) { if (!options.SetArcPath(codecs, cmdArcPath2)) return E_NOTIMPL; } } else { if (fi.IsDir()) throw "there is no such archive"; if (fi.IsDevice) return E_NOTIMPL; if (options.VolumesSizes.Size() > 0) return E_NOTIMPL; CObjectVector types; // change it. if (options.MethodMode.Type_Defined) types.Add(options.MethodMode.Type); // We need to set Properties to open archive only in some cases (WIM archives). CIntVector excl; COpenOptions op; #ifndef _SFX op.props = &options.MethodMode.Properties; #endif op.codecs = codecs; op.types = &types; op.excludedFormats = ! op.stdInMode = false; op.stream = NULL; op.filePath = arcPath; HRESULT result = arcLink.Open2(op, openCallback); if (result == E_ABORT) return result; const wchar_t *errorArcType = NULL; if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex > 0) errorArcType = codecs->Formats[arcLink.NonOpen_ErrorInfo.ErrorFormatIndex].Name; RINOK(callback->OpenResult(arcPath, result, errorArcType)); /* if (result == S_FALSE) return E_FAIL; */ RINOK(result); if (arcLink.VolumePaths.Size() > 1) { errorInfo.SystemError = (DWORD)E_NOTIMPL; errorInfo.Message = L"Updating for multivolume archives is not implemented"; return E_NOTIMPL; } CArc &arc = arcLink.Arcs.Back(); arc.MTimeDefined = !fi.IsDevice; arc.MTime = fi.MTime; if (arc.ErrorInfo.ThereIsTail) { errorInfo.SystemError = (DWORD)E_NOTIMPL; errorInfo.Message = L"There is some data block after the end of the archive"; return E_NOTIMPL; } if (options.MethodMode.Type.FormatIndex < 0) { options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex; if (!options.SetArcPath(codecs, cmdArcPath2)) return E_NOTIMPL; } } } if (options.MethodMode.Type.FormatIndex < 0) { options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArcType); if (options.MethodMode.Type.FormatIndex < 0) return E_NOTIMPL; } bool thereIsInArchive = arcLink.IsOpen; if (!thereIsInArchive && renameMode) return E_FAIL; CDirItems dirItems; CDirItem parentDirItem; CDirItem *parentDirItem_Ptr = NULL; /* FStringVector requestedPaths; FStringVector *requestedPaths_Ptr = NULL; if (options.DeleteAfterCompressing) requestedPaths_Ptr = &requestedPaths; */ if (options.StdInMode) { CDirItem di; di.Name = options.StdInFileName; di.Size = (UInt64)(Int64)-1; di.Attrib = 0; NTime::GetCurUtcFileTime(di.MTime); di.CTime = di.ATime = di.MTime; dirItems.Items.Add(di); } else { bool needScanning = false; if (!renameMode) FOR_VECTOR (i, options.Commands) if (options.Commands[i].ActionSet.NeedScanning()) needScanning = true; if (needScanning) { CEnumDirItemUpdateCallback enumCallback; enumCallback.Callback = callback; RINOK(callback->StartScanning()); dirItems.SymLinks = options.SymLinks.Val; #if defined(_WIN32) && !defined(UNDER_CE) dirItems.ReadSecure = options.NtSecurity.Val; #endif dirItems.ScanAltStreams = options.AltStreams.Val; HRESULT res = EnumerateItems(censor, options.PathMode, options.AddPathPrefix, dirItems, &enumCallback); FOR_VECTOR (i, dirItems.ErrorPaths) { RINOK(callback->CanNotFindError(fs2us(dirItems.ErrorPaths[i]), dirItems.ErrorCodes[i])); } if (res != S_OK) { if (res != E_ABORT) errorInfo.Message = L"Scanning error"; return res; } RINOK(callback->FinishScanning()); if (censor.Pairs.Size() == 1) { NFind::CFileInfo fi; FString prefix = us2fs(censor.Pairs[0].Prefix) + FTEXT("."); // UString prefix = censor.Pairs[0].Prefix; /* if (prefix.Back() == WCHAR_PATH_SEPARATOR) { prefix.DeleteBack(); } */ if (fi.Find(prefix)) if (fi.IsDir()) { parentDirItem.Size = fi.Size; parentDirItem.CTime = fi.CTime; parentDirItem.ATime = fi.ATime; parentDirItem.MTime = fi.MTime; parentDirItem.Attrib = fi.Attrib; parentDirItem_Ptr = &parentDirItem; int secureIndex = -1; #if defined(_WIN32) && !defined(UNDER_CE) if (options.NtSecurity.Val) dirItems.AddSecurityItem(prefix, secureIndex); #endif parentDirItem.SecureIndex = secureIndex; parentDirItem_Ptr = &parentDirItem; } } } } FString tempDirPrefix; bool usesTempDir = false; #ifdef _WIN32 CTempDir tempDirectory; if (options.EMailMode && options.EMailRemoveAfter) { tempDirectory.Create(kTempFolderPrefix); tempDirPrefix = tempDirectory.GetPath(); NormalizeDirPathPrefix(tempDirPrefix); usesTempDir = true; } #endif CTempFiles tempFiles; bool createTempFile = false; if (!options.StdOutMode && options.UpdateArchiveItself) { CArchivePath &ap = options.Commands[0].ArchivePath; ap = options.ArchivePath; // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty()) if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0) { createTempFile = true; ap.Temp = true; if (!options.WorkingDir.IsEmpty()) ap.TempPrefix = options.WorkingDir; else ap.TempPrefix = us2fs(ap.Prefix); NormalizeDirPathPrefix(ap.TempPrefix); } } unsigned i; for (i = 0; i < options.Commands.Size(); i++) { CArchivePath &ap = options.Commands[i].ArchivePath; if (usesTempDir) { // Check it ap.Prefix = fs2us(tempDirPrefix); // ap.Temp = true; // ap.TempPrefix = tempDirPrefix; } if (!options.StdOutMode && (i > 0 || !createTempFile)) { const FString path = us2fs(ap.GetFinalPath()); if (NFind::DoesFileOrDirExist(path)) { errorInfo.SystemError = 0; errorInfo.Message = L"The file already exists"; errorInfo.FileName = path; return E_FAIL; } } } CObjectVector arcItems; if (thereIsInArchive) { RINOK(EnumerateInArchiveItems( // options.StoreAltStreams, censor, arcLink.Arcs.Back(), arcItems)); } /* FStringVector processedFilePaths; FStringVector *processedFilePaths_Ptr = NULL; if (options.DeleteAfterCompressing) processedFilePaths_Ptr = &processedFilePaths; */ CByteBuffer processedItems; if (options.DeleteAfterCompressing) { unsigned num = dirItems.Items.Size(); processedItems.Alloc(num); for (i = 0; i < num; i++) processedItems[i] = 0; } for (i = 0; i < options.Commands.Size(); i++) { const CArc *arc = thereIsInArchive ? arcLink.GetArc() : 0; // IInArchiveExtra *archiveExtra = thereIsInArchive ? arcLink.GetArchiveExtra() : 0; // IArchiveGetRootProps *archiveGetRootProps = thereIsInArchive ? arcLink.GetArchiveGetRootProps() : 0; CUpdateArchiveCommand &command = options.Commands[i]; UString name; bool isUpdating; if (options.StdOutMode) { name = L"stdout"; isUpdating = arc != 0; } else { name = command.ArchivePath.GetFinalPath(); isUpdating = (i == 0 && options.UpdateArchiveItself && arc != 0); } RINOK(callback->StartArchive(name, isUpdating)) RINOK(Compress(options, codecs, command.ActionSet, arc, command.ArchivePath, arcItems, options.DeleteAfterCompressing ? (Byte *)processedItems : NULL, dirItems, parentDirItem_Ptr, tempFiles, errorInfo, callback)); RINOK(callback->FinishArchive()); } if (thereIsInArchive) { RINOK(arcLink.Close()); arcLink.Release(); } tempFiles.Paths.Clear(); if (createTempFile) { try { CArchivePath &ap = options.Commands[0].ArchivePath; const FString &tempPath = ap.GetTempPath(); if (thereIsInArchive) if (!DeleteFileAlways(us2fs(arcPath))) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot delete the file"; errorInfo.FileName = us2fs(arcPath); return E_FAIL; } if (!MyMoveFile(tempPath, us2fs(arcPath))) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot move the file"; errorInfo.FileName = tempPath; errorInfo.FileName2 = us2fs(arcPath); return E_FAIL; } } catch(...) { throw; } } #if defined(_WIN32) && !defined(UNDER_CE) if (options.EMailMode) { NDLL::CLibrary mapiLib; if (!mapiLib.Load(FTEXT("Mapi32.dll"))) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot load Mapi32.dll"; return E_FAIL; } /* LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments"); if (fnSend == 0) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function"; return E_FAIL; } */ LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)mapiLib.GetProc("MAPISendMail"); if (sendMail == 0) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"7-Zip cannot find MAPISendMail function"; return E_FAIL; } FStringVector fullPaths; unsigned i; for (i = 0; i < options.Commands.Size(); i++) { CArchivePath &ap = options.Commands[i].ArchivePath; FString arcPath; if (!MyGetFullPathName(us2fs(ap.GetFinalPath()), arcPath)) { errorInfo.SystemError = ::GetLastError(); errorInfo.Message = L"GetFullPathName error"; return E_FAIL; } fullPaths.Add(arcPath); } CCurrentDirRestorer curDirRestorer; for (i = 0; i < fullPaths.Size(); i++) { UString arcPath = fs2us(fullPaths[i]); UString fileName = ExtractFileNameFromPath(arcPath); AString path = GetAnsiString(arcPath); AString name = GetAnsiString(fileName); // Warning!!! MAPISendDocuments function changes Current directory // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); MapiFileDesc f; memset(&f, 0, sizeof(f)); f.nPosition = 0xFFFFFFFF; f.lpszPathName = (char *)(const char *)path; f.lpszFileName = (char *)(const char *)name; MapiMessage m; memset(&m, 0, sizeof(m)); m.nFileCount = 1; m.lpFiles = &f; const AString addr = GetAnsiString(options.EMailAddress); MapiRecipDesc rec; if (!addr.IsEmpty()) { memset(&rec, 0, sizeof(rec)); rec.ulRecipClass = MAPI_TO; rec.lpszAddress = (char *)(const char *)addr; m.nRecipCount = 1; m.lpRecips = &rec; } sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0); } } #endif if (options.DeleteAfterCompressing) { CRecordVector pairs; FStringVector foldersNames; for (i = 0; i < dirItems.Items.Size(); i++) { const CDirItem &dirItem = dirItems.Items[i]; FString phyPath = us2fs(dirItems.GetPhyPath(i)); if (dirItem.IsDir()) { CRefSortPair pair; pair.Index = i; pair.Len = GetNumSlashes(phyPath); pairs.Add(pair); } else { if (processedItems[i] != 0 || dirItem.Size == 0) { DeleteFileAlways(phyPath); } else { // file was skipped /* errorInfo.SystemError = 0; errorInfo.Message = L"file was not processed"; errorInfo.FileName = phyPath; return E_FAIL; */ } } } pairs.Sort(CompareRefSortPair, NULL); for (i = 0; i < pairs.Size(); i++) { FString phyPath = us2fs(dirItems.GetPhyPath(pairs[i].Index)); if (NFind::DoesDirExist(phyPath)) { // printf("delete %S\n", phyPath); RemoveDir(phyPath); } } } return S_OK; } src/libs/7zip/win/CPP/7zip/UI/Common/Update.h000066400000000000000000000106141325366651500207550ustar00rootroot00000000000000// Update.h #ifndef __COMMON_UPDATE_H #define __COMMON_UPDATE_H #include "../../../Common/Wildcard.h" #include "ArchiveOpenCallback.h" #include "LoadCodecs.h" #include "OpenArchive.h" #include "Property.h" #include "UpdateAction.h" #include "UpdateCallback.h" enum EArcNameMode { k_ArcNameMode_Smart, k_ArcNameMode_Exact, k_ArcNameMode_Add, }; struct CArchivePath { UString OriginalPath; UString Prefix; // path(folder) prefix including slash UString Name; // base name UString BaseExtension; // archive type extension or "exe" extension UString VolExtension; // archive type extension for volumes bool Temp; FString TempPrefix; // path(folder) for temp location FString TempPostfix; CArchivePath(): Temp(false) {}; void ParseFromPath(const UString &path, EArcNameMode mode); UString GetPathWithoutExt() const { return Prefix + Name; } UString GetFinalPath() const; UString GetFinalVolPath() const; FString GetTempPath() const; }; struct CUpdateArchiveCommand { UString UserArchivePath; CArchivePath ArchivePath; NUpdateArchive::CActionSet ActionSet; }; struct CCompressionMethodMode { bool Type_Defined; COpenType Type; CObjectVector Properties; CCompressionMethodMode(): Type_Defined(false) {} }; namespace NRecursedType { enum EEnum { kRecursed, kWildcardOnlyRecursed, kNonRecursed };} struct CRenamePair { UString OldName; UString NewName; bool WildcardParsing; NRecursedType::EEnum RecursedType; CRenamePair(): WildcardParsing(true), RecursedType(NRecursedType::kNonRecursed) {} bool Prepare(); bool GetNewPath(bool isFolder, const UString &src, UString &dest) const; }; struct CUpdateOptions { CCompressionMethodMode MethodMode; CObjectVector Commands; bool UpdateArchiveItself; CArchivePath ArchivePath; EArcNameMode ArcNameMode; bool SfxMode; FString SfxModule; bool OpenShareForWrite; bool StdInMode; UString StdInFileName; bool StdOutMode; bool EMailMode; bool EMailRemoveAfter; UString EMailAddress; FString WorkingDir; NWildcard::ECensorPathMode PathMode; UString AddPathPrefix; CBoolPair NtSecurity; CBoolPair AltStreams; CBoolPair HardLinks; CBoolPair SymLinks; bool DeleteAfterCompressing; bool SetArcMTime; CObjectVector RenamePairs; bool InitFormatIndex(const CCodecs *codecs, const CObjectVector &types, const UString &arcPath); bool SetArcPath(const CCodecs *codecs, const UString &arcPath); CUpdateOptions(): UpdateArchiveItself(true), SfxMode(false), StdInMode(false), StdOutMode(false), EMailMode(false), EMailRemoveAfter(false), OpenShareForWrite(false), ArcNameMode(k_ArcNameMode_Smart), PathMode(NWildcard::k_RelatPath), DeleteAfterCompressing(false), SetArcMTime(false) {}; void SetActionCommand_Add() { Commands.Clear(); CUpdateArchiveCommand c; c.ActionSet = NUpdateArchive::k_ActionSet_Add; Commands.Add(c); } CRecordVector VolumesSizes; }; struct CErrorInfo { DWORD SystemError; FString FileName; FString FileName2; UString Message; // UStringVector ErrorPaths; // CRecordVector ErrorCodes; CErrorInfo(): SystemError(0) {}; }; struct CUpdateErrorInfo: public CErrorInfo { }; #define INTERFACE_IUpdateCallbackUI2(x) \ INTERFACE_IUpdateCallbackUI(x) \ virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, const wchar_t *errorArcType) x; \ virtual HRESULT StartScanning() x; \ virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, UInt64 totalSize, const wchar_t *path, bool isDir) x; \ virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT FinishScanning() x; \ virtual HRESULT StartArchive(const wchar_t *name, bool updating) x; \ virtual HRESULT FinishArchive() x; \ struct IUpdateCallbackUI2: public IUpdateCallbackUI { INTERFACE_IUpdateCallbackUI2(=0) }; HRESULT UpdateArchive( CCodecs *codecs, const CObjectVector &types, const UString &cmdArcPath2, NWildcard::CCensor &censor, CUpdateOptions &options, CUpdateErrorInfo &errorInfo, IOpenCallbackUI *openCallback, IUpdateCallbackUI2 *callback, bool needSetPath); #endif src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.cpp000066400000000000000000000023051325366651500224440ustar00rootroot00000000000000// UpdateAction.cpp #include "StdAfx.h" #include "UpdateAction.h" namespace NUpdateArchive { const CActionSet k_ActionSet_Add = {{ NPairAction::kCopy, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCompress, NPairAction::kCompress, NPairAction::kCompress, NPairAction::kCompress }}; const CActionSet k_ActionSet_Update = {{ NPairAction::kCopy, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress }}; const CActionSet k_ActionSet_Fresh = {{ NPairAction::kCopy, NPairAction::kCopy, NPairAction::kIgnore, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress }}; const CActionSet k_ActionSet_Sync = {{ NPairAction::kCopy, NPairAction::kIgnore, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress, NPairAction::kCopy, NPairAction::kCompress, }}; const CActionSet k_ActionSet_Delete = {{ NPairAction::kCopy, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore, NPairAction::kIgnore }}; } src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.h000066400000000000000000000026141325366651500221140ustar00rootroot00000000000000// UpdateAction.h #ifndef __UPDATE_ACTION_H #define __UPDATE_ACTION_H namespace NUpdateArchive { namespace NPairState { const unsigned kNumValues = 7; enum EEnum { kNotMasked = 0, kOnlyInArchive, kOnlyOnDisk, kNewInArchive, kOldInArchive, kSameFiles, kUnknowNewerFiles }; } namespace NPairAction { enum EEnum { kIgnore = 0, kCopy, kCompress, kCompressAsAnti }; } struct CActionSet { NPairAction::EEnum StateActions[NPairState::kNumValues]; const bool IsEqualTo(const CActionSet &a) const { for (unsigned i = 0; i < NPairState::kNumValues; i++) if (StateActions[i] != a.StateActions[i]) return false; return true; } bool NeedScanning() const { unsigned i; for (i = 0; i < NPairState::kNumValues; i++) if (StateActions[i] == NPairAction::kCompress) return true; for (i = 1; i < NPairState::kNumValues; i++) if (StateActions[i] != NPairAction::kIgnore) return true; return false; } }; extern const CActionSet k_ActionSet_Add; extern const CActionSet k_ActionSet_Update; extern const CActionSet k_ActionSet_Fresh; extern const CActionSet k_ActionSet_Sync; extern const CActionSet k_ActionSet_Delete; } #endif src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.cpp000066400000000000000000000352411325366651500227300ustar00rootroot00000000000000// UpdateCallback.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../../Windows/FileName.h" #include "../../../Windows/PropVariant.h" #include "../../../Windows/Synchronization.h" #include "../../Common/FileStreams.h" #include "../../Common/StreamObjects.h" #include "UpdateCallback.h" #if defined(_WIN32) && !defined(UNDER_CE) #define _USE_SECURITY_CODE #include "../../../Windows/SecurityUtils.h" #endif using namespace NWindows; using namespace NFile; #ifdef _USE_SECURITY_CODE bool InitLocalPrivileges(); #endif CArchiveUpdateCallback::CArchiveUpdateCallback(): Callback(0), ShareForWrite(false), StdInMode(false), DirItems(0), ArcItems(0), UpdatePairs(0), NewNames(0), KeepOriginalItemNames(false), ProcessedItemsStatuses(NULL), ParentDirItem(NULL), StoreNtSecurity(false), StoreHardLinks(false), StoreSymLinks(false), _hardIndex_From((UInt32)(Int32)-1) { #ifdef _USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); #endif } STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size) { COM_TRY_BEGIN return Callback->SetTotal(size); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue) { COM_TRY_BEGIN return Callback->SetCompleted(completeValue); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { COM_TRY_BEGIN return Callback->SetRatioInfo(inSize, outSize); COM_TRY_END } /* static const STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidIsDir, VT_BOOL}, { NULL, kpidSize, VT_UI8}, { NULL, kpidCTime, VT_FILETIME}, { NULL, kpidATime, VT_FILETIME}, { NULL, kpidMTime, VT_FILETIME}, { NULL, kpidAttrib, VT_UI4}, { NULL, kpidIsAnti, VT_BOOL} }; STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **) { return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator); } */ STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, Int32 *newData, Int32 *newProps, UInt32 *indexInArchive) { COM_TRY_BEGIN RINOK(Callback->CheckBreak()); const CUpdatePair2 &up = (*UpdatePairs)[index]; if (newData) *newData = BoolToInt(up.NewData); if (newProps) *newProps = BoolToInt(up.NewProps); if (indexInArchive) { *indexInArchive = (UInt32)(Int32)-1; if (up.ExistInArchive()) *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer; } return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch (propID) { case kpidIsDir: prop = true; break; case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break; case kpidCTime: if (ParentDirItem) prop = ParentDirItem->CTime; break; case kpidATime: if (ParentDirItem) prop = ParentDirItem->ATime; break; case kpidMTime: if (ParentDirItem) prop = ParentDirItem->MTime; break; } prop.Detach(value); return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) { *parentType = NParentType::kDir; *parent = (UInt32)(Int32)-1; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps) { *numProps = 0; if (StoreNtSecurity) *numProps = 1; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) { *name = NULL; *propID = kpidNtSecure; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID #ifdef _USE_SECURITY_CODE propID #endif , const void **data, UInt32 *dataSize, UInt32 *propType) { *data = 0; *dataSize = 0; *propType = 0; if (!StoreNtSecurity) return S_OK; #ifdef _USE_SECURITY_CODE if (propID == kpidNtSecure) { if (StdInMode) return S_OK; if (ParentDirItem) { if (ParentDirItem->SecureIndex < 0) return S_OK; const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex]; *data = buf; *dataSize = (UInt32)buf.Size(); *propType = NPropDataType::kRaw; return S_OK; } if (GetRootProps) return GetRootProps->GetRootRawProp(propID, data, dataSize, propType); } #endif return S_OK; } // #ifdef _USE_SECURITY_CODE // #endif STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) { *data = 0; *dataSize = 0; *propType = 0; if (propID == kpidNtSecure || propID == kpidNtReparse) { if (StdInMode) return S_OK; const CUpdatePair2 &up = (*UpdatePairs)[index]; if (up.UseArcProps && up.ExistInArchive() && GetRawProps) return GetRawProps->GetRawProp( ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, data, dataSize, propType); { const CUpdatePair2 &up = (*UpdatePairs)[index]; /* if (!up.NewData) return E_FAIL; */ if (up.IsAnti) return S_OK; #ifndef UNDER_CE const CDirItem &di = DirItems->Items[up.DirIndex]; #endif #ifdef _USE_SECURITY_CODE if (propID == kpidNtSecure) { if (!StoreNtSecurity) return S_OK; if (di.SecureIndex < 0) return S_OK; const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex]; *data = buf; *dataSize = (UInt32)buf.Size(); *propType = NPropDataType::kRaw; } else #endif { // propID == kpidNtReparse if (!StoreSymLinks) return S_OK; #ifndef UNDER_CE const CByteBuffer *buf = &di.ReparseData2; if (buf->Size() == 0) buf = &di.ReparseData; if (buf->Size() != 0) { *data = *buf; *dataSize = (UInt32)buf->Size(); *propType = NPropDataType::kRaw; } #endif } return S_OK; } } return S_OK; } #ifndef UNDER_CE static UString GetRelativePath(const UString &to, const UString &from) { UStringVector partsTo, partsFrom; SplitPathToParts(to, partsTo); SplitPathToParts(from, partsFrom); unsigned i; for (i = 0;; i++) { if (i + 1 >= partsFrom.Size() || i + 1 >= partsTo.Size()) break; if (CompareFileNames(partsFrom[i], partsTo[i]) != 0) break; } if (i == 0) { #ifdef _WIN32 if (NName::IsDrivePath(to) || NName::IsDrivePath(from)) return to; #endif } UString s; unsigned k; for (k = i + 1; k < partsFrom.Size(); k++) s += L".." WSTRING_PATH_SEPARATOR; for (k = i; k < partsTo.Size(); k++) { if (k != i) s += WCHAR_PATH_SEPARATOR; s += partsTo[k]; } return s; } #endif STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN const CUpdatePair2 &up = (*UpdatePairs)[index]; NCOM::CPropVariant prop; if (up.NewData) { /* if (propID == kpidIsHardLink) { prop = _isHardLink; prop.Detach(value); return S_OK; } */ if (propID == kpidSymLink) { if (index == _hardIndex_From) { prop.Detach(value); return S_OK; } if (up.DirIndex >= 0) { #ifndef UNDER_CE const CDirItem &di = DirItems->Items[up.DirIndex]; // if (di.IsDir()) { CReparseAttr attr; if (attr.Parse(di.ReparseData, di.ReparseData.Size())) { UString simpleName = attr.GetPath(); if (attr.IsRelative()) prop = simpleName; else { const UString phyPath = DirItems->GetPhyPath(up.DirIndex); FString fullPath; if (NDir::MyGetFullPathName(us2fs(phyPath), fullPath)) { prop = GetRelativePath(simpleName, fs2us(fullPath)); } } prop.Detach(value); return S_OK; } } #endif } } else if (propID == kpidHardLink) { if (index == _hardIndex_From) { const CKeyKeyValPair &pair = _map[_hardIndex_To]; const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value]; prop = DirItems->GetLogPath(up2.DirIndex); prop.Detach(value); return S_OK; } if (up.DirIndex >= 0) { prop.Detach(value); return S_OK; } } } if (up.IsAnti && propID != kpidIsDir && propID != kpidPath && propID != kpidIsAltStream) { switch (propID) { case kpidSize: prop = (UInt64)0; break; case kpidIsAnti: prop = true; break; } } else if (propID == kpidPath && up.NewNameIndex >= 0) prop = (*NewNames)[up.NewNameIndex]; else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem) { // we can generate new ShortName here; } else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream))) && up.ExistInArchive() && Archive) return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value); else if (up.ExistOnDisk()) { const CDirItem &di = DirItems->Items[up.DirIndex]; switch (propID) { case kpidPath: prop = DirItems->GetLogPath(up.DirIndex); break; case kpidIsDir: prop = di.IsDir(); break; case kpidSize: prop = di.Size; break; case kpidAttrib: prop = di.Attrib; break; case kpidCTime: prop = di.CTime; break; case kpidATime: prop = di.ATime; break; case kpidMTime: prop = di.MTime; break; case kpidIsAltStream: prop = di.IsAltStream; break; #if defined(_WIN32) && !defined(UNDER_CE) // case kpidShortName: prop = di.ShortName; break; #endif } } prop.Detach(value); return S_OK; COM_TRY_END } static NSynchronization::CCriticalSection CS; STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) { COM_TRY_BEGIN *inStream = NULL; const CUpdatePair2 &up = (*UpdatePairs)[index]; if (!up.NewData) return E_FAIL; RINOK(Callback->CheckBreak()); RINOK(Callback->Finilize()); bool isDir = IsDir(up); if (up.IsAnti) { UString name; if (up.ArcIndex >= 0) name = (*ArcItems)[up.ArcIndex].Name; else if (up.DirIndex >= 0) name = DirItems->GetLogPath(up.DirIndex); RINOK(Callback->GetStream(name, true)); /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file. so we return empty stream */ if (!isDir) { CBufInStream *inStreamSpec = new CBufInStream(); CMyComPtr inStreamLoc = inStreamSpec; inStreamSpec->Init(NULL, 0); *inStream = inStreamLoc.Detach(); } return S_OK; } RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false)); if (isDir) return S_OK; if (StdInMode) { CStdInFileStream *inStreamSpec = new CStdInFileStream; CMyComPtr inStreamLoc(inStreamSpec); *inStream = inStreamLoc.Detach(); } else { CInFileStream *inStreamSpec = new CInFileStream; CMyComPtr inStreamLoc(inStreamSpec); inStreamSpec->SupportHardLinks = StoreHardLinks; const UString path = DirItems->GetPhyPath(up.DirIndex); #if defined(_WIN32) && !defined(UNDER_CE) if (DirItems->Items[up.DirIndex].AreReparseData()) { if (!inStreamSpec->File.OpenReparse(us2fs(path))) { return Callback->OpenFileError(path, ::GetLastError()); } } else #endif if (!inStreamSpec->OpenShared(us2fs(path), ShareForWrite)) { return Callback->OpenFileError(path, ::GetLastError()); } if (StoreHardLinks) { CStreamFileProps props; if (inStreamSpec->GetProps2(&props) == S_OK) { if (props.NumLinks > 1) { CKeyKeyValPair pair; pair.Key1 = props.VolID; pair.Key2 = props.FileID_Low; pair.Value = index; unsigned numItems = _map.Size(); unsigned pairIndex = _map.AddToUniqueSorted2(pair); if (numItems == _map.Size()) { // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex]; _hardIndex_From = index; _hardIndex_To = pairIndex; // we could return NULL as stream, but it's better to return real stream // return S_OK; } } } } if (ProcessedItemsStatuses) { NSynchronization::CCriticalSectionLock lock(CS); ProcessedItemsStatuses[up.DirIndex] = 1; } *inStream = inStreamLoc.Detach(); } return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult) { COM_TRY_BEGIN return Callback->SetOperationResult(operationResult); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) { if (VolumesSizes.Size() == 0) return S_FALSE; if (index >= (UInt32)VolumesSizes.Size()) index = VolumesSizes.Size() - 1; *size = VolumesSizes[index]; return S_OK; } STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) { COM_TRY_BEGIN FChar temp[16]; ConvertUInt32ToString(index + 1, temp); FString res = temp; while (res.Len() < 2) res.InsertAtFront(FTEXT('0')); FString fileName = VolName; fileName += L'.'; fileName += res; fileName += VolExt; COutFileStream *streamSpec = new COutFileStream; CMyComPtr streamLoc(streamSpec); if (!streamSpec->Create(fileName, false)) return ::GetLastError(); *volumeStream = streamLoc.Detach(); return S_OK; COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) { COM_TRY_BEGIN return Callback->CryptoGetTextPassword2(passwordIsDefined, password); COM_TRY_END } STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN return Callback->CryptoGetTextPassword(password); COM_TRY_END } src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.h000066400000000000000000000063351325366651500223770ustar00rootroot00000000000000// UpdateCallback.h #ifndef __UPDATE_CALLBACK_H #define __UPDATE_CALLBACK_H #include "../../../Common/MyCom.h" #include "../../IPassword.h" #include "../../ICoder.h" #include "../Common/UpdatePair.h" #include "../Common/UpdateProduce.h" #define INTERFACE_IUpdateCallbackUI(x) \ virtual HRESULT SetTotal(UInt64 size) x; \ virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \ virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) x; \ virtual HRESULT CheckBreak() x; \ virtual HRESULT Finilize() x; \ virtual HRESULT SetNumFiles(UInt64 numFiles) x; \ virtual HRESULT GetStream(const wchar_t *name, bool isAnti) x; \ virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \ virtual HRESULT SetOperationResult(Int32 operationResult) x; \ virtual HRESULT CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) x; \ virtual HRESULT CryptoGetTextPassword(BSTR *password) x; \ /* virtual HRESULT ShowDeleteFile(const wchar_t *name) x; */ \ /* virtual HRESULT CloseProgress() { return S_OK; }; */ struct IUpdateCallbackUI { INTERFACE_IUpdateCallbackUI(=0) }; struct CKeyKeyValPair { UInt64 Key1; UInt64 Key2; unsigned Value; int Compare(const CKeyKeyValPair &a) const { if (Key1 < a.Key1) return -1; if (Key1 > a.Key1) return 1; return MyCompare(Key2, a.Key2); } }; class CArchiveUpdateCallback: public IArchiveUpdateCallback2, public IArchiveGetRawProps, public IArchiveGetRootProps, public ICryptoGetTextPassword2, public ICryptoGetTextPassword, public ICompressProgressInfo, public CMyUnknownImp { #if defined(_WIN32) && !defined(UNDER_CE) bool _saclEnabled; #endif CRecordVector _map; UInt32 _hardIndex_From; UInt32 _hardIndex_To; public: MY_UNKNOWN_IMP6( IArchiveUpdateCallback2, IArchiveGetRawProps, IArchiveGetRootProps, ICryptoGetTextPassword2, ICryptoGetTextPassword, ICompressProgressInfo) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); INTERFACE_IArchiveUpdateCallback2(;) INTERFACE_IArchiveGetRawProps(;) INTERFACE_IArchiveGetRootProps(;) STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password); STDMETHOD(CryptoGetTextPassword)(BSTR *password); CRecordVector VolumesSizes; FString VolName; FString VolExt; IUpdateCallbackUI *Callback; bool ShareForWrite; bool StdInMode; const CDirItems *DirItems; const CDirItem *ParentDirItem; const CObjectVector *ArcItems; const CRecordVector *UpdatePairs; const UStringVector *NewNames; CMyComPtr Archive; CMyComPtr GetRawProps; CMyComPtr GetRootProps; bool KeepOriginalItemNames; bool StoreNtSecurity; bool StoreHardLinks; bool StoreSymLinks; Byte *ProcessedItemsStatuses; CArchiveUpdateCallback(); bool IsDir(const CUpdatePair2 &up) const { if (up.DirIndex >= 0) return DirItems->Items[up.DirIndex].IsDir(); else if (up.ArcIndex >= 0) return (*ArcItems)[up.ArcIndex].IsDir; return false; } }; #endif src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.cpp000066400000000000000000000144421325366651500221270ustar00rootroot00000000000000// UpdatePair.cpp #include "StdAfx.h" #include #include "../../../Common/Wildcard.h" #include "../../../Windows/TimeUtils.h" #include "SortUtils.h" #include "UpdatePair.h" using namespace NWindows; using namespace NTime; static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time1, const FILETIME &time2) { switch (fileTimeType) { case NFileTimeType::kWindows: return ::CompareFileTime(&time1, &time2); case NFileTimeType::kUnix: { UInt32 unixTime1, unixTime2; FileTimeToUnixTime(time1, unixTime1); FileTimeToUnixTime(time2, unixTime2); return MyCompare(unixTime1, unixTime2); } case NFileTimeType::kDOS: { UInt32 dosTime1, dosTime2; FileTimeToDosTime(time1, dosTime1); FileTimeToDosTime(time2, dosTime2); return MyCompare(dosTime1, dosTime2); } } throw 4191618; } static const char *k_Duplicate_inArc_Message = "Duplicate filename in archive:"; static const char *k_Duplicate_inDir_Message = "Duplicate filename on disk:"; static const char *k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):"; static void ThrowError(const char *message, const UString &s1, const UString &s2) { UString m; m.SetFromAscii(message); m += L'\n'; m += s1; m += L'\n'; m += s2; throw m; } static int CompareArcItemsBase(const CArcItem &ai1, const CArcItem &ai2) { int res = CompareFileNames(ai1.Name, ai2.Name); if (res != 0) return res; if (ai1.IsDir != ai2.IsDir) return ai1.IsDir ? -1 : 1; return 0; } static int CompareArcItems(const unsigned *p1, const unsigned *p2, void *param) { unsigned i1 = *p1; unsigned i2 = *p2; const CObjectVector &arcItems = *(const CObjectVector *)param; int res = CompareArcItemsBase(arcItems[i1], arcItems[i2]); if (res != 0) return res; return MyCompare(i1, i2); } void GetUpdatePairInfoList( const CDirItems &dirItems, const CObjectVector &arcItems, NFileTimeType::EEnum fileTimeType, CRecordVector &updatePairs) { CUIntVector dirIndices, arcIndices; unsigned numDirItems = dirItems.Items.Size(); unsigned numArcItems = arcItems.Size(); CIntArr duplicatedArcItem(numArcItems); { int *vals = &duplicatedArcItem[0]; for (unsigned i = 0; i < numArcItems; i++) vals[i] = 0; } { arcIndices.ClearAndSetSize(numArcItems); { unsigned *vals = &arcIndices[0]; for (unsigned i = 0; i < numArcItems; i++) vals[i] = i; } arcIndices.Sort(CompareArcItems, (void *)&arcItems); for (unsigned i = 0; i + 1 < numArcItems; i++) if (CompareArcItemsBase( arcItems[arcIndices[i]], arcItems[arcIndices[i + 1]]) == 0) { duplicatedArcItem[i] = 1; duplicatedArcItem[i + 1] = -1; } } UStringVector dirNames; { dirNames.ClearAndReserve(numDirItems); unsigned i; for (i = 0; i < numDirItems; i++) dirNames.AddInReserved(dirItems.GetLogPath(i)); SortFileNames(dirNames, dirIndices); for (i = 0; i + 1 < numDirItems; i++) { const UString &s1 = dirNames[dirIndices[i]]; const UString &s2 = dirNames[dirIndices[i + 1]]; if (CompareFileNames(s1, s2) == 0) ThrowError(k_Duplicate_inDir_Message, s1, s2); } } unsigned dirIndex = 0; unsigned arcIndex = 0; int prevHostFile = -1; const UString *prevHostName = NULL; while (dirIndex < numDirItems || arcIndex < numArcItems) { CUpdatePair pair; int dirIndex2 = -1; int arcIndex2 = -1; const CDirItem *di = NULL; const CArcItem *ai = NULL; int compareResult = -1; const UString *name = NULL; if (dirIndex < numDirItems) { dirIndex2 = dirIndices[dirIndex]; di = &dirItems.Items[dirIndex2]; } if (arcIndex < numArcItems) { arcIndex2 = arcIndices[arcIndex]; ai = &arcItems[arcIndex2]; compareResult = 1; if (dirIndex < numDirItems) { compareResult = CompareFileNames(dirNames[dirIndex2], ai->Name); if (compareResult == 0) { if (di->IsDir() != ai->IsDir) compareResult = (ai->IsDir ? 1 : -1); } } } if (compareResult < 0) { name = &dirNames[dirIndex2]; pair.State = NUpdateArchive::NPairState::kOnlyOnDisk; pair.DirIndex = dirIndex2; dirIndex++; } else if (compareResult > 0) { name = &ai->Name; pair.State = ai->Censored ? NUpdateArchive::NPairState::kOnlyInArchive: NUpdateArchive::NPairState::kNotMasked; pair.ArcIndex = arcIndex2; arcIndex++; } else { int dupl = duplicatedArcItem[arcIndex]; if (dupl != 0) ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[arcIndex + dupl]].Name); name = &dirNames[dirIndex2]; if (!ai->Censored) ThrowError(k_NotCensoredCollision_Message, *name, ai->Name); pair.DirIndex = dirIndex2; pair.ArcIndex = arcIndex2; switch (ai->MTimeDefined ? MyCompareTime( ai->TimeType != - 1 ? (NFileTimeType::EEnum)ai->TimeType : fileTimeType, di->MTime, ai->MTime): 0) { case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break; case 1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break; default: pair.State = (ai->SizeDefined && di->Size == ai->Size) ? NUpdateArchive::NPairState::kSameFiles : NUpdateArchive::NPairState::kUnknowNewerFiles; } dirIndex++; arcIndex++; } if ((di && di->IsAltStream) || (ai && ai->IsAltStream)) { if (prevHostName) { unsigned hostLen = prevHostName->Len(); if (name->Len() > hostLen) if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0) pair.HostIndex = prevHostFile; } } else { prevHostFile = updatePairs.Size(); prevHostName = name; } updatePairs.Add(pair); } updatePairs.ReserveDown(); } src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.h000066400000000000000000000011451325366651500215700ustar00rootroot00000000000000// UpdatePair.h #ifndef __UPDATE_PAIR_H #define __UPDATE_PAIR_H #include "DirItem.h" #include "UpdateAction.h" #include "../../Archive/IArchive.h" struct CUpdatePair { NUpdateArchive::NPairState::EEnum State; int ArcIndex; int DirIndex; int HostIndex; // >= 0 for alt streams only, contains index of host pair CUpdatePair(): ArcIndex(-1), DirIndex(-1), HostIndex(-1) {} }; void GetUpdatePairInfoList( const CDirItems &dirItems, const CObjectVector &arcItems, NFileTimeType::EEnum fileTimeType, CRecordVector &updatePairs); #endif src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.cpp000066400000000000000000000037661325366651500226440ustar00rootroot00000000000000// UpdateProduce.cpp #include "StdAfx.h" #include "UpdateProduce.h" using namespace NUpdateArchive; static const char *kUpdateActionSetCollision = "Internal collision in update action set"; void UpdateProduce( const CRecordVector &updatePairs, const CActionSet &actionSet, CRecordVector &operationChain, IUpdateProduceCallback *callback) { FOR_VECTOR (i, updatePairs) { const CUpdatePair &pair = updatePairs[i]; CUpdatePair2 up2; up2.DirIndex = pair.DirIndex; up2.ArcIndex = pair.ArcIndex; up2.NewData = up2.NewProps = true; up2.UseArcProps = false; switch (actionSet.StateActions[pair.State]) { case NPairAction::kIgnore: /* if (pair.State != NPairState::kOnlyOnDisk) IgnoreArchiveItem(m_ArchiveItems[pair.ArcIndex]); // cout << "deleting"; */ if (callback) callback->ShowDeleteFile(pair.ArcIndex); continue; case NPairAction::kCopy: if (pair.State == NPairState::kOnlyOnDisk) throw kUpdateActionSetCollision; if (pair.State == NPairState::kOnlyInArchive) { if (pair.HostIndex >= 0) { /* ignore alt stream if 1) no such alt stream in Disk 2) there is Host file in disk */ if (updatePairs[pair.HostIndex].DirIndex >= 0) continue; } } up2.NewData = up2.NewProps = false; up2.UseArcProps = true; break; case NPairAction::kCompress: if (pair.State == NPairState::kOnlyInArchive || pair.State == NPairState::kNotMasked) throw kUpdateActionSetCollision; break; case NPairAction::kCompressAsAnti: up2.IsAnti = true; up2.UseArcProps = (pair.ArcIndex >= 0); break; } operationChain.Add(up2); } operationChain.ReserveDown(); } src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.h000066400000000000000000000023301325366651500222730ustar00rootroot00000000000000// UpdateProduce.h #ifndef __UPDATE_PRODUCE_H #define __UPDATE_PRODUCE_H #include "UpdatePair.h" struct CUpdatePair2 { bool NewData; bool NewProps; bool UseArcProps; // if (UseArcProps && NewProps), we want to change only some properties. bool IsAnti; // if (!IsAnti) we use other ways to detect Anti status int DirIndex; int ArcIndex; int NewNameIndex; bool IsMainRenameItem; void SetAs_NoChangeArcItem(int arcIndex) { NewData = NewProps = false; UseArcProps = true; IsAnti = false; ArcIndex = arcIndex; } bool ExistOnDisk() const { return DirIndex != -1; } bool ExistInArchive() const { return ArcIndex != -1; } CUpdatePair2(): NewData(false), NewProps(false), UseArcProps(false), IsAnti(false), DirIndex(-1), ArcIndex(-1), NewNameIndex(-1), IsMainRenameItem(false) {} }; struct IUpdateProduceCallback { virtual HRESULT ShowDeleteFile(int arcIndex) = 0; }; void UpdateProduce( const CRecordVector &updatePairs, const NUpdateArchive::CActionSet &actionSet, CRecordVector &operationChain, IUpdateProduceCallback *callback); #endif src/libs/7zip/win/CPP/7zip/UI/Console/000077500000000000000000000000001325366651500175325ustar00rootroot00000000000000src/libs/7zip/win/CPP/7zip/UI/Console/Console.pri000066400000000000000000000002551325366651500216520ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/7zip/UI/Console/PercentPrinter.h \ $$7ZIP_BASE/CPP/7zip/UI/Common/StdAfx.h SOURCES += $$7ZIP_BASE/CPP/7zip/UI/Console/PercentPrinter.cpp src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.cpp000066400000000000000000000042611325366651500232050ustar00rootroot00000000000000// PercentPrinter.cpp #include "StdAfx.h" #include "../../../Common/Defs.h" #include "../../../Common/IntToString.h" #include "PercentPrinter.h" static const unsigned kPaddingSize = 2; static const unsigned kPercentsSize = 4; static const unsigned kMaxExtraSize = kPaddingSize + 32 + kPercentsSize; static void ClearPrev(char *p, unsigned num) { unsigned i; for (i = 0; i < num; i++) *p++ = '\b'; for (i = 0; i < num; i++) *p++ = ' '; for (i = 0; i < num; i++) *p++ = '\b'; *p = '\0'; } void CPercentPrinter::ClosePrint() { if (m_NumExtraChars == 0) return; char s[kMaxExtraSize * 3 + 1]; ClearPrev(s, m_NumExtraChars); (*OutStream) << s; m_NumExtraChars = 0; } void CPercentPrinter::PrintString(const char *s) { ClosePrint(); (*OutStream) << s; } void CPercentPrinter::PrintString(const wchar_t *s) { ClosePrint(); (*OutStream) << s; } void CPercentPrinter::PrintNewLine() { ClosePrint(); (*OutStream) << "\n"; } void CPercentPrinter::RePrintRatio() { char s[32]; unsigned size; { char c = '%'; UInt64 value = 0; if (m_Total == (UInt64)(Int64)-1) { value = m_CurValue >> 20; c = 'M'; } else if (m_Total != 0) value = m_CurValue * 100 / m_Total; ConvertUInt64ToString(value, s); size = (unsigned)strlen(s); s[size++] = c; s[size] = '\0'; } unsigned extraSize = kPaddingSize + MyMax(size, kPercentsSize); if (extraSize < m_NumExtraChars) extraSize = m_NumExtraChars; char fullString[kMaxExtraSize * 3]; char *p = fullString; unsigned i; if (m_NumExtraChars == 0) { for (i = 0; i < extraSize; i++) *p++ = ' '; m_NumExtraChars = extraSize; } for (i = 0; i < m_NumExtraChars; i++) *p++ = '\b'; m_NumExtraChars = extraSize; for (; size < extraSize; size++) *p++ = ' '; MyStringCopy(p, s); (*OutStream) << fullString; OutStream->Flush(); m_PrevValue = m_CurValue; } void CPercentPrinter::PrintRatio() { if (m_CurValue < m_PrevValue + m_MinStepSize && m_CurValue + m_MinStepSize > m_PrevValue && m_NumExtraChars != 0) return; RePrintRatio(); } src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.h000066400000000000000000000014211325366651500226450ustar00rootroot00000000000000// PercentPrinter.h #ifndef __PERCENT_PRINTER_H #define __PERCENT_PRINTER_H #include "../../../Common/StdOutStream.h" class CPercentPrinter { UInt64 m_MinStepSize; UInt64 m_PrevValue; UInt64 m_CurValue; UInt64 m_Total; unsigned m_NumExtraChars; public: CStdOutStream *OutStream; CPercentPrinter(UInt64 minStepSize = 1): m_MinStepSize(minStepSize), m_PrevValue(0), m_CurValue(0), m_Total((UInt64)(Int64)-1), m_NumExtraChars(0) {} void SetTotal(UInt64 total) { m_Total = total; m_PrevValue = 0; } void SetRatio(UInt64 doneValue) { m_CurValue = doneValue; } void PrintString(const char *s); void PrintString(const wchar_t *s); void PrintNewLine(); void ClosePrint(); void RePrintRatio(); void PrintRatio(); }; #endif src/libs/7zip/win/CPP/7zip/UI/Console/StdAfx.h000066400000000000000000000001501325366651500210700ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../../../Common/Common.h" #endif src/libs/7zip/win/CPP/Common/000077500000000000000000000000001325366651500161525ustar00rootroot00000000000000src/libs/7zip/win/CPP/Common/ComTry.h000066400000000000000000000006211325366651500175370ustar00rootroot00000000000000// ComTry.h #ifndef __COM_TRY_H #define __COM_TRY_H #include "MyWindows.h" // #include "Exception.h" // #include "NewHandler.h" #define COM_TRY_BEGIN try { #define COM_TRY_END } catch(...) { return E_OUTOFMEMORY; } // catch(const CNewException &) { return E_OUTOFMEMORY; } // catch(const CSystemException &e) { return e.ErrorCode; } // catch(...) { return E_FAIL; } #endif src/libs/7zip/win/CPP/Common/CommandLineParser.cpp000066400000000000000000000076301325366651500222270ustar00rootroot00000000000000// CommandLineParser.cpp #include "StdAfx.h" #include "CommandLineParser.h" static bool IsString1PrefixedByString2_NoCase(const wchar_t *u, const char *a) { for (;;) { char c = *a; if (c == 0) return true; if (MyCharLower_Ascii(c) != MyCharLower_Ascii(*u)) return false; a++; u++; } } namespace NCommandLineParser { bool SplitCommandLine(const UString &src, UString &dest1, UString &dest2) { dest1.Empty(); dest2.Empty(); bool quoteMode = false; unsigned i; for (i = 0; i < src.Len(); i++) { wchar_t c = src[i]; if ((c == L' ' || c == L'\t') && !quoteMode) { dest2 = src.Ptr(i + 1); return i != 0; } if (c == L'\"') quoteMode = !quoteMode; else dest1 += c; } return i != 0; } void SplitCommandLine(const UString &s, UStringVector &parts) { UString sTemp = s; sTemp.Trim(); parts.Clear(); for (;;) { UString s1, s2; if (SplitCommandLine(sTemp, s1, s2)) parts.Add(s1); if (s2.IsEmpty()) break; sTemp = s2; } } static const char *kStopSwitchParsing = "--"; static bool inline IsItSwitchChar(wchar_t c) { return (c == '-'); } CParser::CParser(unsigned numSwitches): _numSwitches(numSwitches), _switches(0) { _switches = new CSwitchResult[numSwitches]; } CParser::~CParser() { delete []_switches; } // if (s) contains switch then function updates switch structures // out: true, if (s) is a switch bool CParser::ParseString(const UString &s, const CSwitchForm *switchForms) { if (s.IsEmpty() || !IsItSwitchChar(s[0])) return false; unsigned pos = 1; unsigned switchIndex = 0; int maxLen = -1; for (unsigned i = 0; i < _numSwitches; i++) { const char *key = switchForms[i].Key; unsigned switchLen = MyStringLen(key); if ((int)switchLen <= maxLen || pos + switchLen > s.Len()) continue; if (IsString1PrefixedByString2_NoCase((const wchar_t *)s + pos, key)) { switchIndex = i; maxLen = switchLen; } } if (maxLen < 0) { ErrorMessage = "Unknown switch:"; return false; } pos += maxLen; CSwitchResult &sw = _switches[switchIndex]; const CSwitchForm &form = switchForms[switchIndex]; if (!form.Multi && sw.ThereIs) { ErrorMessage = "Multiple instances for switch:"; return false; } sw.ThereIs = true; int rem = s.Len() - pos; if (rem < form.MinLen) { ErrorMessage = "Too short switch:"; return false; } sw.WithMinus = false; sw.PostCharIndex = -1; switch (form.Type) { case NSwitchType::kMinus: if (rem != 0) { sw.WithMinus = (s[pos] == '-'); if (sw.WithMinus) pos++; } break; case NSwitchType::kChar: if (rem != 0) { wchar_t c = s[pos]; if (c <= 0x7F) { sw.PostCharIndex = FindCharPosInString(form.PostCharSet, (char)c); if (sw.PostCharIndex >= 0) pos++; } } break; case NSwitchType::kString: sw.PostStrings.Add((const wchar_t *)s + pos); return true; } if (pos != s.Len()) { ErrorMessage = "Too long switch:"; return false; } return true; } bool CParser::ParseStrings(const CSwitchForm *switchForms, const UStringVector &commandStrings) { ErrorLine.Empty(); bool stopSwitch = false; FOR_VECTOR (i, commandStrings) { const UString &s = commandStrings[i]; if (!stopSwitch) { if (s.IsEqualTo(kStopSwitchParsing)) { stopSwitch = true; continue; } if (!s.IsEmpty() && IsItSwitchChar(s[0])) { if (ParseString(s, switchForms)) continue; ErrorLine = s; return false; } } NonSwitchStrings.Add(s); } return true; } } src/libs/7zip/win/CPP/Common/CommandLineParser.h000066400000000000000000000022661325366651500216740ustar00rootroot00000000000000// Common/CommandLineParser.h #ifndef __COMMON_COMMAND_LINE_PARSER_H #define __COMMON_COMMAND_LINE_PARSER_H #include "MyString.h" namespace NCommandLineParser { bool SplitCommandLine(const UString &src, UString &dest1, UString &dest2); void SplitCommandLine(const UString &s, UStringVector &parts); namespace NSwitchType { enum EEnum { kSimple, kMinus, kString, kChar }; } struct CSwitchForm { const char *Key; Byte Type; bool Multi; Byte MinLen; // int MaxLen; const char *PostCharSet; }; struct CSwitchResult { bool ThereIs; bool WithMinus; int PostCharIndex; UStringVector PostStrings; CSwitchResult(): ThereIs(false) {}; }; class CParser { unsigned _numSwitches; CSwitchResult *_switches; bool ParseString(const UString &s, const CSwitchForm *switchForms); public: UStringVector NonSwitchStrings; AString ErrorMessage; UString ErrorLine; CParser(unsigned numSwitches); ~CParser(); bool ParseStrings(const CSwitchForm *switchForms, const UStringVector &commandStrings); const CSwitchResult& operator[](size_t index) const { return _switches[index]; } }; } #endif src/libs/7zip/win/CPP/Common/Common.h000066400000000000000000000003311325366651500175500ustar00rootroot00000000000000// Common.h #ifndef __COMMON_COMMON_H #define __COMMON_COMMON_H #include "../../C/Compiler.h" #include "MyWindows.h" #include "NewHandler.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[1])) #endif src/libs/7zip/win/CPP/Common/Common.pri000066400000000000000000000027211325366651500201200ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/Common/CommandLineParser.h \ $$7ZIP_BASE/CPP/Common/ComTry.h \ $$7ZIP_BASE/CPP/Common/Common.h \ $$7ZIP_BASE/CPP/Common/Defs.h \ $$7ZIP_BASE/CPP/Common/IntToString.h \ $$7ZIP_BASE/CPP/Common/ListFileUtils.h \ $$7ZIP_BASE/CPP/Common/MyBuffer.h \ $$7ZIP_BASE/CPP/Common/MyCom.h \ $$7ZIP_BASE/CPP/Common/MyException.h \ $$7ZIP_BASE/CPP/Common/MyGuidDef.h \ $$7ZIP_BASE/CPP/Common/MyInitGuid.h \ $$7ZIP_BASE/CPP/Common/MyString.h \ $$7ZIP_BASE/CPP/Common/MyTypes.h \ $$7ZIP_BASE/CPP/Common/MyUnknown.h \ $$7ZIP_BASE/CPP/Common/MyVector.h \ $$7ZIP_BASE/CPP/Common/MyWindows.h \ $$7ZIP_BASE/CPP/Common/NewHandler.h \ $$7ZIP_BASE/CPP/Common/StdAfx.h \ $$7ZIP_BASE/CPP/Common/StdOutStream.h \ $$7ZIP_BASE/CPP/Common/StringConvert.h \ $$7ZIP_BASE/CPP/Common/StringToInt.h \ $$7ZIP_BASE/CPP/Common/UTFConvert.h \ $$7ZIP_BASE/CPP/Common/Wildcard.h SOURCES += $$7ZIP_BASE/CPP/Common/CommandLineParser.cpp \ $$7ZIP_BASE/CPP/Common/IntToString.cpp \ $$7ZIP_BASE/CPP/Common/ListFileUtils.cpp \ $$7ZIP_BASE/CPP/Common/MyString.cpp \ $$7ZIP_BASE/CPP/Common/MyWindows.cpp \ $$7ZIP_BASE/CPP/Common/NewHandler.cpp \ $$7ZIP_BASE/CPP/Common/StdOutStream.cpp \ $$7ZIP_BASE/CPP/Common/StringConvert.cpp \ $$7ZIP_BASE/CPP/Common/StringToInt.cpp \ $$7ZIP_BASE/CPP/Common/UTFConvert.cpp \ $$7ZIP_BASE/CPP/Common/Wildcard.cpp src/libs/7zip/win/CPP/Common/Defs.h000066400000000000000000000007151325366651500172070ustar00rootroot00000000000000// Common/Defs.h #ifndef __COMMON_DEFS_H #define __COMMON_DEFS_H template inline T MyMin(T a, T b) { return a < b ? a : b; } template inline T MyMax(T a, T b) { return a > b ? a : b; } template inline int MyCompare(T a, T b) { return a < b ? -1 : (a == b ? 0 : 1); } inline int BoolToInt(bool value) { return (value ? 1: 0); } inline bool IntToBool(int value) { return (value != 0); } #endif src/libs/7zip/win/CPP/Common/IntToString.cpp000066400000000000000000000052741325366651500211120ustar00rootroot00000000000000// Common/IntToString.cpp #include "StdAfx.h" #include "IntToString.h" #define CONVERT_INT_TO_STR(charType, tempSize) \ unsigned char temp[tempSize]; unsigned i = 0; \ while (val >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(val % 10)); val /= 10; } \ *s++ = (charType)('0' + (unsigned)val); \ while (i != 0) { i--; *s++ = temp[i]; } \ *s = 0; void ConvertUInt32ToString(UInt32 val, char *s) throw() { CONVERT_INT_TO_STR(char, 16); } void ConvertUInt64ToString(UInt64 val, char *s) throw() { if (val <= (UInt32)0xFFFFFFFF) { ConvertUInt32ToString((UInt32)val, s); return; } CONVERT_INT_TO_STR(char, 24); } void ConvertUInt64ToOct(UInt64 val, char *s) throw() { UInt64 v = val; unsigned i; for (i = 1;; i++) { v >>= 3; if (v == 0) break; } s[i] = 0; do { unsigned t = (unsigned)(val & 0x7); val >>= 3; s[--i] = (char)('0' + t); } while (i); } void ConvertUInt32ToHex(UInt32 val, char *s) throw() { UInt32 v = val; unsigned i; for (i = 1;; i++) { v >>= 4; if (v == 0) break; } s[i] = 0; do { unsigned t = (unsigned)((val & 0xF)); val >>= 4; s[--i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } while (i); } void ConvertUInt64ToHex(UInt64 val, char *s) throw() { UInt64 v = val; unsigned i; for (i = 1;; i++) { v >>= 4; if (v == 0) break; } s[i] = 0; do { unsigned t = (unsigned)((val & 0xF)); val >>= 4; s[--i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } while (i); } void ConvertUInt32ToHex8Digits(UInt32 val, char *s) throw() { s[8] = 0; for (int i = 7; i >= 0; i--) { unsigned t = val & 0xF; val >>= 4; s[i] = (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); } } /* void ConvertUInt32ToHex8Digits(UInt32 val, wchar_t *s) { s[8] = 0; for (int i = 7; i >= 0; i--) { unsigned t = val & 0xF; val >>= 4; s[i] = (wchar_t)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); } } */ void ConvertUInt32ToString(UInt32 val, wchar_t *s) throw() { CONVERT_INT_TO_STR(wchar_t, 16); } void ConvertUInt64ToString(UInt64 val, wchar_t *s) throw() { if (val <= (UInt32)0xFFFFFFFF) { ConvertUInt32ToString((UInt32)val, s); return; } CONVERT_INT_TO_STR(wchar_t, 24); } void ConvertInt64ToString(Int64 val, char *s) throw() { if (val < 0) { *s++ = '-'; val = -val; } ConvertUInt64ToString(val, s); } void ConvertInt64ToString(Int64 val, wchar_t *s) throw() { if (val < 0) { *s++ = L'-'; val = -val; } ConvertUInt64ToString(val, s); } src/libs/7zip/win/CPP/Common/IntToString.h000066400000000000000000000014511325366651500205500ustar00rootroot00000000000000// Common/IntToString.h #ifndef __COMMON_INT_TO_STRING_H #define __COMMON_INT_TO_STRING_H #include "MyTypes.h" void ConvertUInt32ToString(UInt32 value, char *s) throw(); void ConvertUInt64ToString(UInt64 value, char *s) throw(); void ConvertUInt32ToString(UInt32 value, wchar_t *s) throw(); void ConvertUInt64ToString(UInt64 value, wchar_t *s) throw(); void ConvertUInt64ToOct(UInt64 value, char *s) throw(); void ConvertUInt32ToHex(UInt32 value, char *s) throw(); void ConvertUInt64ToHex(UInt64 value, char *s) throw(); void ConvertUInt32ToHex8Digits(UInt32 value, char *s) throw(); // void ConvertUInt32ToHex8Digits(UInt32 value, wchar_t *s) throw(); void ConvertInt64ToString(Int64 value, char *s) throw(); void ConvertInt64ToString(Int64 value, wchar_t *s) throw(); #endif src/libs/7zip/win/CPP/Common/ListFileUtils.cpp000066400000000000000000000051351325366651500214160ustar00rootroot00000000000000// Common/ListFileUtils.cpp #include "StdAfx.h" #include "../../C/CpuArch.h" #include "../Windows/FileIO.h" #include "ListFileUtils.h" #include "MyBuffer.h" #include "StringConvert.h" #include "UTFConvert.h" static const char kQuoteChar = '\"'; static void AddName(UStringVector &strings, UString &s) { s.Trim(); if (s.Len() >= 2 && s[0] == kQuoteChar && s.Back() == kQuoteChar) { s.DeleteBack(); s.Delete(0); } if (!s.IsEmpty()) strings.Add(s); } bool ReadNamesFromListFile(CFSTR fileName, UStringVector &strings, UINT codePage) { NWindows::NFile::NIO::CInFile file; if (!file.Open(fileName)) return false; UInt64 fileSize; if (!file.GetLength(fileSize)) return false; if (fileSize >= ((UInt32)1 << 31) - 32) return false; UString u; if (codePage == MY__CP_UTF16 || codePage == MY__CP_UTF16BE) { if ((fileSize & 1) != 0) return false; CByteArr buf((size_t)fileSize); UInt32 processed; if (!file.Read(buf, (UInt32)fileSize, processed)) return false; if (processed != fileSize) return false; file.Close(); unsigned num = (unsigned)fileSize / 2; wchar_t *p = u.GetBuffer(num); if (codePage == MY__CP_UTF16) for (unsigned i = 0; i < num; i++) { wchar_t c = GetUi16(buf + i * 2); if (c == 0) return false; p[i] = c; } else for (unsigned i = 0; i < num; i++) { wchar_t c = (wchar_t)GetBe16(buf + i * 2); if (c == 0) return false; p[i] = c; } u.ReleaseBuffer(num); } else { AString s; char *p = s.GetBuffer((unsigned)fileSize); UInt32 processed; if (!file.Read(p, (UInt32)fileSize, processed)) return false; if (processed != fileSize) return false; file.Close(); p[processed] = 0; s.ReleaseBuffer(); if (s.Len() != processed) return false; // #ifdef CP_UTF8 if (codePage == CP_UTF8) { if (!ConvertUTF8ToUnicode(s, u)) return false; } else // #endif MultiByteToUnicodeString2(u, s, codePage); } const wchar_t kGoodBOM = 0xFEFF; const wchar_t kBadBOM = 0xFFFE; UString s; unsigned i = 0; for (; i < u.Len() && u[i] == kGoodBOM; i++); for (; i < u.Len(); i++) { wchar_t c = u[i]; if (c == kGoodBOM || c == kBadBOM) return false; if (c == L'\n' || c == 0xD) { AddName(strings, s); s.Empty(); } else s += c; } AddName(strings, s); return true; } src/libs/7zip/win/CPP/Common/ListFileUtils.h000066400000000000000000000004731325366651500210630ustar00rootroot00000000000000// Common/ListFileUtils.h #ifndef __COMMON_LIST_FILE_UTILS_H #define __COMMON_LIST_FILE_UTILS_H #include "MyString.h" #include "MyTypes.h" #define MY__CP_UTF16 1200 #define MY__CP_UTF16BE 1201 bool ReadNamesFromListFile(CFSTR fileName, UStringVector &strings, UINT codePage = CP_OEMCP); #endif src/libs/7zip/win/CPP/Common/MyBuffer.h000066400000000000000000000115331325366651500200450ustar00rootroot00000000000000// Common/MyBuffer.h #ifndef __COMMON_MY_BUFFER_H #define __COMMON_MY_BUFFER_H #include "Defs.h" template class CBuffer { T *_items; size_t _size; void CopyToEmpty(const CBuffer &buffer) { if (buffer._size > 0) { _items = new T[buffer._size]; memcpy(_items, buffer._items, buffer._size * sizeof(T)); _size = buffer._size; } } public: void Free() { if (_items) { delete []_items; _items = 0; } _size = 0; } CBuffer(): _items(0), _size(0) {}; CBuffer(size_t size): _items(0), _size(0) { _items = new T[size]; _size = size; } CBuffer(const CBuffer &buffer): _items(0), _size(0) { CopyToEmpty(buffer); } ~CBuffer() { delete []_items; } operator T *() { return _items; }; operator const T *() const { return _items; }; size_t Size() const { return _size; } void Alloc(size_t size) { if (size != _size) { Free(); if (size != 0) { _items = new T[size]; _size = size; } } } void AllocAtLeast(size_t size) { if (size > _size) { Free(); _items = new T[size]; _size = size; } } void CopyFrom(const T *data, size_t size) { Alloc(size); memcpy(_items, data, size * sizeof(T)); } void ChangeSize_KeepData(size_t newSize, size_t keepSize) { if (newSize == _size) return; T *newBuffer = NULL; if (newSize > 0) { newBuffer = new T[newSize]; if (_size > 0) memcpy(newBuffer, _items, MyMin(MyMin(_size, keepSize), newSize) * sizeof(T)); } delete []_items; _items = newBuffer; _size = newSize; } CBuffer& operator=(const CBuffer &buffer) { Free(); CopyToEmpty(buffer); return *this; } }; template bool operator==(const CBuffer& b1, const CBuffer& b2) { size_t size1 = b1.Size(); if (size1 != b2.Size()) return false; return memcmp(b1, b2, size1 * sizeof(T)) == 0; } template bool operator!=(const CBuffer& b1, const CBuffer& b2) { size_t size1 = b1.Size(); if (size1 == b2.Size()) return false; return memcmp(b1, b2, size1 * sizeof(T)) != 0; } typedef CBuffer CCharBuffer; typedef CBuffer CWCharBuffer; typedef CBuffer CByteBuffer; template class CObjArray { protected: T *_items; private: // we disable constructors CObjArray(const CObjArray &buffer); void operator=(const CObjArray &buffer); public: void Free() { delete []_items; _items = 0; } CObjArray(size_t size): _items(0) { if (size != 0) _items = new T[size]; } CObjArray(): _items(0) {}; ~CObjArray() { delete []_items; } operator T *() { return _items; }; operator const T *() const { return _items; }; void Alloc(size_t newSize) { delete []_items; _items = 0; _items = new T[newSize]; } }; typedef CObjArray CByteArr; typedef CObjArray CBoolArr; typedef CObjArray CIntArr; // #define CRecArray CObjArray template class CObjArray2 { // protected: T *_items; unsigned _size; CObjArray2(const CObjArray2 &buffer); void operator=(const CObjArray2 &buffer); public: void Free() { delete []_items; _items = 0; _size = 0; } CObjArray2(): _items(0), _size(0) {}; /* CObjArray2(const CObjArray2 &buffer): _items(0), _size(0) { size_t newSize = buffer._size; if (newSize > 0) { T *newBuffer = new T[newSize];; _items = newBuffer; _size = newSize; const T *src = buffer; for (size_t i = 0; i < newSize; i++) newBuffer[i] = src[i]; } } */ /* CObjArray2(size_t size): _items(0), _size(0) { if (size != 0) { _items = new T[size]; _size = size; } } */ ~CObjArray2() { delete []_items; } operator T *() { return _items; }; operator const T *() const { return _items; }; unsigned Size() const { return (unsigned)_size; } bool IsEmpty() const { return _size == 0; } // SetSize doesn't keep old items. It allocates new array if size is not equal void SetSize(unsigned size) { if (size == _size) return; T *newBuffer = NULL; if (size > 0) newBuffer = new T[size]; delete []_items; _items = newBuffer; _size = size; } /* CObjArray2& operator=(const CObjArray2 &buffer) { Free(); size_t newSize = buffer._size; if (newSize > 0) { T *newBuffer = new T[newSize];; _items = newBuffer; _size = newSize; const T *src = buffer; for (size_t i = 0; i < newSize; i++) newBuffer[i] = src[i]; } return *this; } */ }; #endif src/libs/7zip/win/CPP/Common/MyCom.h000066400000000000000000000146611325366651500173570ustar00rootroot00000000000000// MyCom.h #ifndef __MY_COM_H #define __MY_COM_H #include "MyWindows.h" #include "NewHandler.h" #ifndef RINOK #define RINOK(x) { HRESULT __result_ = (x); if (__result_ != S_OK) return __result_; } #endif template class CMyComPtr { T* _p; public: CMyComPtr(): _p(NULL) {} CMyComPtr(T* p) throw() { if ((_p = p) != NULL) p->AddRef(); } CMyComPtr(const CMyComPtr& lp) throw() { if ((_p = lp._p) != NULL) _p->AddRef(); } ~CMyComPtr() { if (_p) _p->Release(); } void Release() { if (_p) { _p->Release(); _p = NULL; } } operator T*() const { return (T*)_p; } // T& operator*() const { return *_p; } T** operator&() { return &_p; } T* operator->() const { return _p; } T* operator=(T* p) { if (p) p->AddRef(); if (_p) _p->Release(); _p = p; return p; } T* operator=(const CMyComPtr& lp) { return (*this = lp._p); } bool operator!() const { return (_p == NULL); } // bool operator==(T* pT) const { return _p == pT; } void Attach(T* p2) { Release(); _p = p2; } T* Detach() { T* pt = _p; _p = NULL; return pt; } #ifdef _WIN32 HRESULT CoCreateInstance(REFCLSID rclsid, REFIID iid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) { return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, iid, (void**)&_p); } #endif /* HRESULT CoCreateInstance(LPCOLESTR szProgID, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) { CLSID clsid; HRESULT hr = CLSIDFromProgID(szProgID, &clsid); ATLASSERT(_p == NULL); if (SUCCEEDED(hr)) hr = ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&_p); return hr; } */ template HRESULT QueryInterface(REFGUID iid, Q** pp) const throw() { return _p->QueryInterface(iid, (void**)pp); } }; ////////////////////////////////////////////////////////// inline HRESULT StringToBstr(LPCOLESTR src, BSTR *bstr) { *bstr = ::SysAllocString(src); return (*bstr != NULL) ? S_OK : E_OUTOFMEMORY; } class CMyComBSTR { BSTR m_str; public: CMyComBSTR(): m_str(NULL) {} CMyComBSTR(LPCOLESTR src) { m_str = ::SysAllocString(src); } // CMyComBSTR(int nSize) { m_str = ::SysAllocStringLen(NULL, nSize); } // CMyComBSTR(int nSize, LPCOLESTR sz) { m_str = ::SysAllocStringLen(sz, nSize); } CMyComBSTR(const CMyComBSTR& src) { m_str = src.MyCopy(); } /* CMyComBSTR(REFGUID src) { LPOLESTR szGuid; StringFromCLSID(src, &szGuid); m_str = ::SysAllocString(szGuid); CoTaskMemFree(szGuid); } */ ~CMyComBSTR() { ::SysFreeString(m_str); } CMyComBSTR& operator=(const CMyComBSTR& src) { if (m_str != src.m_str) { if (m_str) ::SysFreeString(m_str); m_str = src.MyCopy(); } return *this; } CMyComBSTR& operator=(LPCOLESTR src) { ::SysFreeString(m_str); m_str = ::SysAllocString(src); return *this; } // unsigned Len() const { return ::SysStringLen(m_str); } operator BSTR() const { return m_str; } BSTR* operator&() { return &m_str; } BSTR MyCopy() const { int byteLen = ::SysStringByteLen(m_str); BSTR res = ::SysAllocStringByteLen(NULL, byteLen); memcpy(res, m_str, byteLen); return res; } /* void Attach(BSTR src) { m_str = src; } BSTR Detach() { BSTR s = m_str; m_str = NULL; return s; } */ void Empty() { ::SysFreeString(m_str); m_str = NULL; } bool operator!() const { return (m_str == NULL); } }; ////////////////////////////////////////////////////////// class CMyUnknownImp { public: ULONG __m_RefCount; CMyUnknownImp(): __m_RefCount(0) {} }; #define MY_QUERYINTERFACE_BEGIN STDMETHOD(QueryInterface) \ (REFGUID iid, void **outObject) throw() { *outObject = NULL; #define MY_QUERYINTERFACE_ENTRY(i) else if (iid == IID_ ## i) \ { *outObject = (void *)(i *)this; } #define MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) if (iid == IID_IUnknown) \ { *outObject = (void *)(IUnknown *)(i *)this; } #define MY_QUERYINTERFACE_BEGIN2(i) MY_QUERYINTERFACE_BEGIN \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \ MY_QUERYINTERFACE_ENTRY(i) #define MY_QUERYINTERFACE_END else return E_NOINTERFACE; AddRef(); return S_OK; } #define MY_ADDREF_RELEASE \ STDMETHOD_(ULONG, AddRef)() throw() { return ++__m_RefCount; } \ STDMETHOD_(ULONG, Release)() { if (--__m_RefCount != 0) \ return __m_RefCount; delete this; return 0; } #define MY_UNKNOWN_IMP_SPEC(i) \ MY_QUERYINTERFACE_BEGIN \ i \ MY_QUERYINTERFACE_END \ MY_ADDREF_RELEASE #define MY_UNKNOWN_IMP MY_QUERYINTERFACE_BEGIN \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(IUnknown) \ MY_QUERYINTERFACE_END \ MY_ADDREF_RELEASE #define MY_UNKNOWN_IMP1(i) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \ MY_QUERYINTERFACE_ENTRY(i) \ ) #define MY_UNKNOWN_IMP2(i1, i2) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ ) #define MY_UNKNOWN_IMP3(i1, i2, i3) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ ) #define MY_UNKNOWN_IMP4(i1, i2, i3, i4) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ ) #define MY_UNKNOWN_IMP5(i1, i2, i3, i4, i5) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ MY_QUERYINTERFACE_ENTRY(i5) \ ) #define MY_UNKNOWN_IMP6(i1, i2, i3, i4, i5, i6) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ MY_QUERYINTERFACE_ENTRY(i5) \ MY_QUERYINTERFACE_ENTRY(i6) \ ) #define MY_UNKNOWN_IMP7(i1, i2, i3, i4, i5, i6, i7) MY_UNKNOWN_IMP_SPEC( \ MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \ MY_QUERYINTERFACE_ENTRY(i1) \ MY_QUERYINTERFACE_ENTRY(i2) \ MY_QUERYINTERFACE_ENTRY(i3) \ MY_QUERYINTERFACE_ENTRY(i4) \ MY_QUERYINTERFACE_ENTRY(i5) \ MY_QUERYINTERFACE_ENTRY(i6) \ MY_QUERYINTERFACE_ENTRY(i7) \ ) #endif src/libs/7zip/win/CPP/Common/MyException.h000066400000000000000000000003611325366651500205670ustar00rootroot00000000000000// Common/Exception.h #ifndef __COMMON_EXCEPTION_H #define __COMMON_EXCEPTION_H #include "MyWindows.h" struct CSystemException { HRESULT ErrorCode; CSystemException(HRESULT errorCode): ErrorCode(errorCode) {} }; #endif src/libs/7zip/win/CPP/Common/MyGuidDef.h000066400000000000000000000021221325366651500201350ustar00rootroot00000000000000// Common/MyGuidDef.h #ifndef GUID_DEFINED #define GUID_DEFINED #include "MyTypes.h" typedef struct { UInt32 Data1; UInt16 Data2; UInt16 Data3; unsigned char Data4[8]; } GUID; #ifdef __cplusplus #define REFGUID const GUID & #else #define REFGUID const GUID * #endif #define REFCLSID REFGUID #define REFIID REFGUID #ifdef __cplusplus inline int operator==(REFGUID g1, REFGUID g2) { for (int i = 0; i < (int)sizeof(g1); i++) if (((unsigned char *)&g1)[i] != ((unsigned char *)&g2)[i]) return 0; return 1; } inline int operator!=(REFGUID g1, REFGUID g2) { return !(g1 == g2); } #endif #ifdef __cplusplus #define MY_EXTERN_C extern "C" #else #define MY_EXTERN_C extern #endif #endif #ifdef DEFINE_GUID #undef DEFINE_GUID #endif #ifdef INITGUID #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ MY_EXTERN_C const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } #else #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ MY_EXTERN_C const GUID name #endif src/libs/7zip/win/CPP/Common/MyInitGuid.h000066400000000000000000000023451325366651500203510ustar00rootroot00000000000000// Common/MyInitGuid.h #ifndef __COMMON_MY_INITGUID_H #define __COMMON_MY_INITGUID_H /* This file must be included only to one C++ file in project before declarations of COM interfaces with DEFINE_GUID macro. Each GUID must be initialized exactly once in project. There are two different versions of the DEFINE_GUID macro in guiddef.h (MyGuidDef.h): - if INITGUID is not defined: DEFINE_GUID declares an external reference to the symbol name. - if INITGUID is defined: DEFINE_GUID initializes the symbol name to the value of the GUID. Also we need IID_IUnknown that is initialized in some file for linking: MSVC: by default the linker uses some lib file that contains IID_IUnknown MinGW: add -luuid switch for linker WinCE: we define IID_IUnknown in this file Other: we define IID_IUnknown in this file */ #ifdef _WIN32 #ifdef UNDER_CE #include #endif #include #ifdef UNDER_CE DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); #endif #else #define INITGUID #include "MyGuidDef.h" DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); #endif #endif src/libs/7zip/win/CPP/Common/MyString.cpp000066400000000000000000000575241325366651500204470ustar00rootroot00000000000000// Common/MyString.cpp #include "StdAfx.h" #ifdef _WIN32 #include #include #else #include #endif #if !defined(_UNICODE) || !defined(USE_UNICODE_FSTRING) #include "StringConvert.h" #endif #include "MyString.h" #define MY_STRING_NEW(_T_, _size_) new _T_[_size_] // #define MY_STRING_NEW(_T_, _size_) ((_T_ *)my_new((size_t)(_size_) * sizeof(_T_))) /* inline const char* MyStringGetNextCharPointer(const char *p) throw() { #if defined(_WIN32) && !defined(UNDER_CE) return CharNextA(p); #else return p + 1; #endif } */ int FindCharPosInString(const char *s, char c) throw() { for (const char *p = s;; p++) { if (*p == c) return (int)(p - s); if (*p == 0) return -1; // MyStringGetNextCharPointer(p); } } int FindCharPosInString(const wchar_t *s, wchar_t c) throw() { for (const wchar_t *p = s;; p++) { if (*p == c) return (int)(p - s); if (*p == 0) return -1; } } /* void MyStringUpper_Ascii(wchar_t *s) { for (;;) { wchar_t c = *s; if (c == 0) return; *s++ = MyCharUpper_Ascii(c); } } */ void MyStringLower_Ascii(wchar_t *s) throw() { for (;;) { wchar_t c = *s; if (c == 0) return; *s++ = MyCharLower_Ascii(c); } } #ifdef _WIN32 #ifdef _UNICODE // wchar_t * MyStringUpper(wchar_t *s) { return CharUpperW(s); } // wchar_t * MyStringLower(wchar_t *s) { return CharLowerW(s); } // for WinCE - FString - char // const char *MyStringGetPrevCharPointer(const char * /* base */, const char *p) { return p - 1; } #else // const char * MyStringGetPrevCharPointer(const char *base, const char *p) throw() { return CharPrevA(base, p); } // char * MyStringUpper(char *s) { return CharUpperA(s); } // char * MyStringLower(char *s) { return CharLowerA(s); } wchar_t MyCharUpper_WIN(wchar_t c) throw() { wchar_t *res = CharUpperW((LPWSTR)(UINT_PTR)(unsigned)c); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return (wchar_t)(unsigned)(UINT_PTR)res; const int kBufSize = 4; char s[kBufSize + 1]; int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufSize, 0, 0); if (numChars == 0 || numChars > kBufSize) return c; s[numChars] = 0; ::CharUpperA(s); ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1); return c; } /* wchar_t MyCharLower_WIN(wchar_t c) { wchar_t *res = CharLowerW((LPWSTR)(UINT_PTR)(unsigned)c); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return (wchar_t)(unsigned)(UINT_PTR)res; const int kBufSize = 4; char s[kBufSize + 1]; int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufSize, 0, 0); if (numChars == 0 || numChars > kBufSize) return c; s[numChars] = 0; ::CharLowerA(s); ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1); return c; } */ /* wchar_t * MyStringUpper(wchar_t *s) { if (s == 0) return 0; wchar_t *res = CharUpperW(s); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return res; AString a = UnicodeStringToMultiByte(s); a.MakeUpper(); MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a)); return s; } */ /* wchar_t * MyStringLower(wchar_t *s) { if (s == 0) return 0; wchar_t *res = CharLowerW(s); if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) return res; AString a = UnicodeStringToMultiByte(s); a.MakeLower(); MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a)); return s; } */ #endif #endif bool IsString1PrefixedByString2(const char *s1, const char *s2) throw() { for (;;) { unsigned char c2 = (unsigned char)*s2++; if (c2 == 0) return true; unsigned char c1 = (unsigned char)*s1++; if (c1 != c2) return false; } } bool StringsAreEqualNoCase(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2 && MyCharUpper(c1) != MyCharUpper(c2)) return false; if (c1 == 0) return true; } } // ---------- ASCII ---------- bool AString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw() { const char *s1 = _chars; for (;;) { char c2 = *s++; if (c2 == 0) return true; char c1 = *s1++; if (MyCharLower_Ascii(c1) != MyCharLower_Ascii(c2)) return false; } } bool UString::IsPrefixedBy_Ascii_NoCase(const char *s) const throw() { const wchar_t *s1 = _chars; for (;;) { char c2 = *s++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (MyCharLower_Ascii(c1) != (unsigned char)MyCharLower_Ascii(c2)) return false; } } bool StringsAreEqual_Ascii(const wchar_t *u, const char *a) throw() { for (;;) { unsigned char c = *a; if (c != *u) return false; if (c == 0) return true; a++; u++; } } bool StringsAreEqualNoCase_Ascii(const char *s1, const char *s2) throw() { for (;;) { char c1 = *s1++; char c2 = *s2++; if (c1 != c2 && MyCharLower_Ascii(c1) != MyCharLower_Ascii(c2)) return false; if (c1 == 0) return true; } } bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2 && MyCharLower_Ascii(c1) != MyCharLower_Ascii(c2)) return false; if (c1 == 0) return true; } } bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const char *s2) throw() { for (;;) { wchar_t c1 = *s1++; char c2 = *s2++; if (c1 != (unsigned char)c2 && (c1 > 0x7F || MyCharLower_Ascii(c1) != (unsigned char)MyCharLower_Ascii(c2))) return false; if (c1 == 0) return true; } } bool IsString1PrefixedByString2(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c2 = *s2++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (c1 != c2) return false; } } // NTFS order: uses upper case int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2) throw() { for (;;) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2) { wchar_t u1 = MyCharUpper(c1); wchar_t u2 = MyCharUpper(c2); if (u1 < u2) return -1; if (u1 > u2) return 1; } if (c1 == 0) return 0; } } int MyStringCompareNoCase_N(const wchar_t *s1, const wchar_t *s2, unsigned num) throw() { for (; num != 0; num--) { wchar_t c1 = *s1++; wchar_t c2 = *s2++; if (c1 != c2) { wchar_t u1 = MyCharUpper(c1); wchar_t u2 = MyCharUpper(c2); if (u1 < u2) return -1; if (u1 > u2) return 1; } if (c1 == 0) return 0; } return 0; } // ---------- AString ---------- void AString::InsertSpace(unsigned &index, unsigned size) { Grow(size); MoveItems(index + size, index); } void AString::ReAlloc(unsigned newLimit) { if (newLimit < _len || newLimit >= 0x20000000) throw 20130220; // MY_STRING_REALLOC(_chars, char, newLimit + 1, _len + 1); char *newBuf = MY_STRING_NEW(char, newLimit + 1); memcpy(newBuf, _chars, (size_t)(_len + 1)); \ MY_STRING_DELETE(_chars); _chars = newBuf; _limit = newLimit; } void AString::SetStartLen(unsigned len) { _chars = 0; _chars = MY_STRING_NEW(char, len + 1); _len = len; _limit = len; } void AString::Grow_1() { unsigned next = _len; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } void AString::Grow(unsigned n) { unsigned freeSize = _limit - _len; if (n <= freeSize) return; unsigned next = _len + n; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } /* AString::AString(unsigned num, const char *s) { unsigned len = MyStringLen(s); if (num > len) num = len; SetStartLen(num); memcpy(_chars, s, num); _chars[num] = 0; } */ AString::AString(unsigned num, const AString &s) { if (num > s._len) num = s._len; SetStartLen(num); memcpy(_chars, s._chars, num); _chars[num] = 0; } AString::AString(const AString &s, char c) { SetStartLen(s.Len() + 1); char *chars = _chars; unsigned len = s.Len(); memcpy(chars, s, len); chars[len] = c; chars[len + 1] = 0; } AString::AString(const char *s1, unsigned num1, const char *s2, unsigned num2) { SetStartLen(num1 + num2); char *chars = _chars; memcpy(chars, s1, num1); memcpy(chars + num1, s2, num2 + 1); } AString operator+(const AString &s1, const AString &s2) { return AString(s1, s1.Len(), s2, s2.Len()); } AString operator+(const AString &s1, const char *s2) { return AString(s1, s1.Len(), s2, MyStringLen(s2)); } AString operator+(const char *s1, const AString &s2) { return AString(s1, MyStringLen(s1), s2, s2.Len()); } AString::AString() { _chars = 0; _chars = MY_STRING_NEW(char, 4); _len = 0; _limit = 4 - 1; _chars[0] = 0; } AString::AString(char c) { SetStartLen(1); _chars[0] = c; _chars[1] = 0; } AString::AString(const char *s) { SetStartLen(MyStringLen(s)); MyStringCopy(_chars, s); } AString::AString(const AString &s) { SetStartLen(s._len); MyStringCopy(_chars, s._chars); } AString &AString::operator=(char c) { if (1 > _limit) { char *newBuf = MY_STRING_NEW(char, 1 + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = 1; } _len = 1; _chars[0] = c; _chars[1] = 0; return *this; } AString &AString::operator=(const char *s) { unsigned len = MyStringLen(s); if (len > _limit) { char *newBuf = MY_STRING_NEW(char, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s); return *this; } AString &AString::operator=(const AString &s) { if (&s == this) return *this; unsigned len = s._len; if (len > _limit) { char *newBuf = MY_STRING_NEW(char, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s._chars); return *this; } AString &AString::operator+=(const char *s) { unsigned len = MyStringLen(s); Grow(len); MyStringCopy(_chars + _len, s); _len += len; return *this; } AString &AString::operator+=(const AString &s) { Grow(s._len); MyStringCopy(_chars + _len, s._chars); _len += s._len; return *this; } void AString::SetFrom(const char *s, unsigned len) // no check { if (len > _limit) { char *newBuf = MY_STRING_NEW(char, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } memcpy(_chars, s, len); _chars[len] = 0; _len = len; } int AString::Find(const AString &s, unsigned startIndex) const throw() { if (s.IsEmpty()) return startIndex; for (; startIndex < _len; startIndex++) { unsigned j; for (j = 0; j < s._len && startIndex + j < _len; j++) if (_chars[startIndex + j] != s._chars[j]) break; if (j == s._len) return (int)startIndex; } return -1; } int AString::ReverseFind(char c) const throw() { if (_len == 0) return -1; const char *p = _chars + _len - 1; for (;;) { if (*p == c) return (int)(p - _chars); if (p == _chars) return -1; p--; // p = GetPrevCharPointer(_chars, p); } } void AString::TrimLeft() throw() { const char *p = _chars; for (;; p++) { char c = *p; if (c != ' ' && c != '\n' && c != '\t') break; } unsigned pos = (unsigned)(p - _chars); if (pos != 0) { MoveItems(0, pos); _len -= pos; } } void AString::TrimRight() throw() { const char *p = _chars; int i; for (i = _len - 1; i >= 0; i--) { char c = p[i]; if (c != ' ' && c != '\n' && c != '\t') break; } i++; if ((unsigned)i != _len) { _chars[i] = 0; _len = i; } } void AString::InsertAtFront(char c) { if (_limit == _len) Grow_1(); MoveItems(1, 0); _chars[0] = c; _len++; } /* void AString::Insert(unsigned index, char c) { InsertSpace(index, 1); _chars[index] = c; _len++; } */ void AString::Insert(unsigned index, const char *s) { unsigned num = MyStringLen(s); if (num != 0) { InsertSpace(index, num); memcpy(_chars + index, s, num); _len += num; } } void AString::Insert(unsigned index, const AString &s) { unsigned num = s.Len(); if (num != 0) { InsertSpace(index, num); memcpy(_chars + index, s, num); _len += num; } } void AString::RemoveChar(char ch) throw() { int pos = Find(ch); if (pos < 0) return; const char *src = _chars; char *dest = _chars + pos; pos++; unsigned len = _len; for (; (unsigned)pos < len; pos++) { char c = src[(unsigned)pos]; if (c != ch) *dest++ = c; } *dest = 0; _len = (unsigned)(dest - _chars); } // !!!!!!!!!!!!!!! test it if newChar = '\0' void AString::Replace(char oldChar, char newChar) throw() { if (oldChar == newChar) return; // 0; // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldChar, pos); if (pos < 0) break; _chars[pos] = newChar; pos++; // number++; } return; // number; } void AString::Replace(const AString &oldString, const AString &newString) { if (oldString.IsEmpty()) return; // 0; if (oldString == newString) return; // 0; unsigned oldLen = oldString.Len(); unsigned newLen = newString.Len(); // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldString, pos); if (pos < 0) break; Delete(pos, oldLen); Insert(pos, newString); pos += newLen; // number++; } // return number; } void AString::Delete(unsigned index) throw() { MoveItems(index, index + 1); _len--; } void AString::Delete(unsigned index, unsigned count) throw() { if (index + count > _len) count = _len - index; if (count > 0) { MoveItems(index, index + count); _len -= count; } } void AString::DeleteFrontal(unsigned num) throw() { if (num != 0) { MoveItems(0, num); _len -= num; } } /* AString operator+(const AString &s1, const AString &s2) { AString result(s1); result += s2; return result; } AString operator+(const AString &s, const char *chars) { AString result(s); result += chars; return result; } AString operator+(const char *chars, const AString &s) { AString result(chars); result += s; return result; } AString operator+(const AString &s, char c) { AString result(s); result += c; return result; } */ /* AString operator+(char c, const AString &s) { AString result(c); result += s; return result; } */ // ---------- UString ---------- void UString::InsertSpace(unsigned index, unsigned size) { Grow(size); MoveItems(index + size, index); } void UString::ReAlloc(unsigned newLimit) { if (newLimit < _len || newLimit >= 0x20000000) throw 20130221; // MY_STRING_REALLOC(_chars, wchar_t, newLimit + 1, _len + 1); wchar_t *newBuf = MY_STRING_NEW(wchar_t, newLimit + 1); wmemcpy(newBuf, _chars, _len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = newLimit; } void UString::SetStartLen(unsigned len) { _chars = 0; _chars = MY_STRING_NEW(wchar_t, len + 1); _len = len; _limit = len; } void UString::Grow_1() { unsigned next = _len; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } void UString::Grow(unsigned n) { unsigned freeSize = _limit - _len; if (n <= freeSize) return; unsigned next = _len + n; next += next / 2; next += 16; next &= ~(unsigned)15; ReAlloc(next - 1); } UString::UString(unsigned num, const wchar_t *s) { unsigned len = MyStringLen(s); if (num > len) num = len; SetStartLen(num); wmemcpy(_chars, s, num); _chars[num] = 0; } UString::UString(unsigned num, const UString &s) { if (num > s._len) num = s._len; SetStartLen(num); wmemcpy(_chars, s._chars, num); _chars[num] = 0; } UString::UString(const UString &s, wchar_t c) { SetStartLen(s.Len() + 1); wchar_t *chars = _chars; unsigned len = s.Len(); wmemcpy(chars, s, len); chars[len] = c; chars[len + 1] = 0; } UString::UString(const wchar_t *s1, unsigned num1, const wchar_t *s2, unsigned num2) { SetStartLen(num1 + num2); wchar_t *chars = _chars; wmemcpy(chars, s1, num1); wmemcpy(chars + num1, s2, num2 + 1); } UString operator+(const UString &s1, const UString &s2) { return UString(s1, s1.Len(), s2, s2.Len()); } UString operator+(const UString &s1, const wchar_t *s2) { return UString(s1, s1.Len(), s2, MyStringLen(s2)); } UString operator+(const wchar_t *s1, const UString &s2) { return UString(s1, MyStringLen(s1), s2, s2.Len()); } UString::UString() { _chars = 0; _chars = MY_STRING_NEW(wchar_t, 4); _len = 0; _limit = 4 - 1; _chars[0] = 0; } UString::UString(wchar_t c) { SetStartLen(1); _chars[0] = c; _chars[1] = 0; } UString::UString(const wchar_t *s) { SetStartLen(MyStringLen(s)); MyStringCopy(_chars, s); } UString::UString(const UString &s) { SetStartLen(s._len); MyStringCopy(_chars, s._chars); } UString &UString::operator=(wchar_t c) { if (1 > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, 1 + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = 1; } _len = 1; _chars[0] = c; _chars[1] = 0; return *this; } UString &UString::operator=(const wchar_t *s) { unsigned len = MyStringLen(s); if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s); return *this; } UString &UString::operator=(const UString &s) { if (&s == this) return *this; unsigned len = s._len; if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } _len = len; MyStringCopy(_chars, s._chars); return *this; } UString &UString::operator+=(const wchar_t *s) { unsigned len = MyStringLen(s); Grow(len); MyStringCopy(_chars + _len, s); _len += len; return *this; } UString &UString::operator+=(const UString &s) { Grow(s._len); MyStringCopy(_chars + _len, s._chars); _len += s._len; return *this; } void UString::SetFrom(const wchar_t *s, unsigned len) // no check { if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } wmemcpy(_chars, s, len); _chars[len] = 0; _len = len; } void UString::SetFromAscii(const char *s) { unsigned len = MyStringLen(s); if (len > _limit) { wchar_t *newBuf = MY_STRING_NEW(wchar_t, len + 1); MY_STRING_DELETE(_chars); _chars = newBuf; _limit = len; } wchar_t *chars = _chars; for (unsigned i = 0; i < len; i++) chars[i] = s[i]; chars[len] = 0; _len = len; } void UString::AddAsciiStr(const char *s) { unsigned len = MyStringLen(s); Grow(len); wchar_t *chars = _chars + _len; for (unsigned i = 0; i < len; i++) chars[i] = s[i]; chars[len] = 0; _len += len; } int UString::Find(const UString &s, unsigned startIndex) const throw() { if (s.IsEmpty()) return startIndex; for (; startIndex < _len; startIndex++) { unsigned j; for (j = 0; j < s._len && startIndex + j < _len; j++) if (_chars[startIndex + j] != s._chars[j]) break; if (j == s._len) return (int)startIndex; } return -1; } int UString::ReverseFind(wchar_t c) const throw() { if (_len == 0) return -1; const wchar_t *p = _chars + _len - 1; for (;;) { if (*p == c) return (int)(p - _chars); if (p == _chars) return -1; p--; } } void UString::TrimLeft() throw() { const wchar_t *p = _chars; for (;; p++) { wchar_t c = *p; if (c != ' ' && c != '\n' && c != '\t') break; } unsigned pos = (unsigned)(p - _chars); if (pos != 0) { MoveItems(0, pos); _len -= pos; } } void UString::TrimRight() throw() { const wchar_t *p = _chars; int i; for (i = _len - 1; i >= 0; i--) { wchar_t c = p[i]; if (c != ' ' && c != '\n' && c != '\t') break; } i++; if ((unsigned)i != _len) { _chars[i] = 0; _len = i; } } void UString::InsertAtFront(wchar_t c) { if (_limit == _len) Grow_1(); MoveItems(1, 0); _chars[0] = c; _len++; } /* void UString::Insert(unsigned index, wchar_t c) { InsertSpace(index, 1); _chars[index] = c; _len++; } */ void UString::Insert(unsigned index, const wchar_t *s) { unsigned num = MyStringLen(s); if (num != 0) { InsertSpace(index, num); wmemcpy(_chars + index, s, num); _len += num; } } void UString::Insert(unsigned index, const UString &s) { unsigned num = s.Len(); if (num != 0) { InsertSpace(index, num); wmemcpy(_chars + index, s, num); _len += num; } } void UString::RemoveChar(wchar_t ch) throw() { int pos = Find(ch); if (pos < 0) return; const wchar_t *src = _chars; wchar_t *dest = _chars + pos; pos++; unsigned len = _len; for (; (unsigned)pos < len; pos++) { wchar_t c = src[(unsigned)pos]; if (c != ch) *dest++ = c; } *dest = 0; _len = (unsigned)(dest - _chars); } // !!!!!!!!!!!!!!! test it if newChar = '\0' void UString::Replace(wchar_t oldChar, wchar_t newChar) throw() { if (oldChar == newChar) return; // 0; // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldChar, pos); if (pos < 0) break; _chars[pos] = newChar; pos++; // number++; } return; // number; } void UString::Replace(const UString &oldString, const UString &newString) { if (oldString.IsEmpty()) return; // 0; if (oldString == newString) return; // 0; unsigned oldLen = oldString.Len(); unsigned newLen = newString.Len(); // unsigned number = 0; int pos = 0; while ((unsigned)pos < _len) { pos = Find(oldString, pos); if (pos < 0) break; Delete(pos, oldLen); Insert(pos, newString); pos += newLen; // number++; } // return number; } void UString::Delete(unsigned index) throw() { MoveItems(index, index + 1); _len--; } void UString::Delete(unsigned index, unsigned count) throw() { if (index + count > _len) count = _len - index; if (count > 0) { MoveItems(index, index + count); _len -= count; } } void UString::DeleteFrontal(unsigned num) throw() { if (num != 0) { MoveItems(0, num); _len -= num; } } // ---------------------------------------- /* int MyStringCompareNoCase(const char *s1, const char *s2) { return MyStringCompareNoCase(MultiByteToUnicodeString(s1), MultiByteToUnicodeString(s2)); } */ static inline UINT GetCurrentCodePage() { #if defined(UNDER_CE) || !defined(_WIN32) return CP_ACP; #else return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; #endif } #ifdef USE_UNICODE_FSTRING #ifndef _UNICODE AString fs2fas(CFSTR s) { return UnicodeStringToMultiByte(s, GetCurrentCodePage()); } FString fas2fs(const AString &s) { return MultiByteToUnicodeString(s, GetCurrentCodePage()); } #endif #else UString fs2us(const FString &s) { return MultiByteToUnicodeString((AString)s, GetCurrentCodePage()); } FString us2fs(const wchar_t *s) { return UnicodeStringToMultiByte(s, GetCurrentCodePage()); } #endif src/libs/7zip/win/CPP/Common/MyString.h000066400000000000000000000373421325366651500201100ustar00rootroot00000000000000// Common/String.h #ifndef __COMMON_STRING_H #define __COMMON_STRING_H #include #ifndef _WIN32 #include #include #endif #include "MyTypes.h" #include "MyVector.h" inline unsigned MyStringLen(const char *s) { unsigned i; for (i = 0; s[i] != 0; i++); return i; } inline void MyStringCopy(char *dest, const char *src) { while ((*dest++ = *src++) != 0); } inline char *MyStpCpy(char *dest, const char *src) { for (;;) { char c = *src; *dest = c; if (c == 0) return dest; src++; dest++; } } inline unsigned MyStringLen(const wchar_t *s) { unsigned i; for (i = 0; s[i] != 0; i++); return i; } inline void MyStringCopy(wchar_t *dest, const wchar_t *src) { while ((*dest++ = *src++) != 0); } int FindCharPosInString(const char *s, char c) throw(); int FindCharPosInString(const wchar_t *s, wchar_t c) throw(); #ifdef _WIN32 #ifndef _UNICODE #define STRING_UNICODE_THROW #endif #endif #ifndef STRING_UNICODE_THROW #define STRING_UNICODE_THROW throw() #endif /* inline char MyCharUpper_Ascii(char c) { if (c >= 'a' && c <= 'z') return (char)(c - 0x20); return c; } inline wchar_t MyCharUpper_Ascii(wchar_t c) { if (c >= 'a' && c <= 'z') return (wchar_t)(c - 0x20); return c; } */ inline char MyCharLower_Ascii(char c) { if (c >= 'A' && c <= 'Z') return (char)(c + 0x20); return c; } inline wchar_t MyCharLower_Ascii(wchar_t c) { if (c >= 'A' && c <= 'Z') return (wchar_t)(c + 0x20); return c; } wchar_t MyCharUpper_WIN(wchar_t c) throw(); inline wchar_t MyCharUpper(wchar_t c) throw() { if (c < 'a') return c; if (c <= 'z') return (wchar_t)(c - 0x20); if (c <= 0x7F) return c; #ifdef _WIN32 #ifdef _UNICODE return (wchar_t)(unsigned)(UINT_PTR)CharUpperW((LPWSTR)(UINT_PTR)(unsigned)c); #else return (wchar_t)MyCharUpper_WIN(c); #endif #else return (wchar_t)towupper(c); #endif } /* wchar_t MyCharLower_WIN(wchar_t c) throw(); inline wchar_t MyCharLower(wchar_t c) throw() { if (c < 'A') return c; if (c <= 'Z') return (wchar_t)(c + 0x20); if (c <= 0x7F) return c; #ifdef _WIN32 #ifdef _UNICODE return (wchar_t)(unsigned)(UINT_PTR)CharLowerW((LPWSTR)(UINT_PTR)(unsigned)c); #else return (wchar_t)MyCharLower_WIN(c); #endif #else return (wchar_t)tolower(c); #endif } */ // char *MyStringUpper(char *s) throw(); // char *MyStringLower(char *s) throw(); // void MyStringUpper_Ascii(wchar_t *s) throw(); void MyStringLower_Ascii(wchar_t *s) throw(); // wchar_t *MyStringUpper(wchar_t *s) STRING_UNICODE_THROW; // wchar_t *MyStringLower(wchar_t *s) STRING_UNICODE_THROW; bool StringsAreEqualNoCase(const wchar_t *s1, const wchar_t *s2) throw(); bool IsString1PrefixedByString2(const char *s1, const char *s2) throw(); bool IsString1PrefixedByString2(const wchar_t *s1, const wchar_t *s2) throw(); int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2) throw(); int MyStringCompareNoCase_N(const wchar_t *s1, const wchar_t *s2, unsigned num) throw(); // ---------- ASCII ---------- // char values in ASCII strings must be less then 128 bool StringsAreEqual_Ascii(const wchar_t *u, const char *a) throw(); bool StringsAreEqualNoCase_Ascii(const char *s1, const char *s2) throw(); bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const char *s2) throw(); bool StringsAreEqualNoCase_Ascii(const wchar_t *s1, const wchar_t *s2) throw(); #define MY_STRING_DELETE(_p_) delete []_p_; // #define MY_STRING_DELETE(_p_) my_delete(_p_); class AString { char *_chars; unsigned _len; unsigned _limit; void MoveItems(unsigned dest, unsigned src) { memmove(_chars + dest, _chars + src, (size_t)(_len - src + 1) * sizeof(char)); } void InsertSpace(unsigned &index, unsigned size); void ReAlloc(unsigned newLimit); void SetStartLen(unsigned len); void Grow_1(); void Grow(unsigned n); // AString(unsigned num, const char *s); AString(unsigned num, const AString &s); AString(const AString &s, char c); // it's for String + char AString(const char *s1, unsigned num1, const char *s2, unsigned num2); friend AString operator+(const AString &s, char c) { return AString(s, c); } ; // friend AString operator+(char c, const AString &s); // is not supported friend AString operator+(const AString &s1, const AString &s2); friend AString operator+(const AString &s1, const char *s2); friend AString operator+(const char *s1, const AString &s2); public: AString(); AString(char c); AString(const char *s); AString(const AString &s); ~AString() { MY_STRING_DELETE(_chars); } unsigned Len() const { return _len; } bool IsEmpty() const { return _len == 0; } void Empty() { _len = 0; _chars[0] = 0; } operator const char *() const { return _chars; } const char *Ptr() const { return _chars; } const char *Ptr(unsigned pos) const { return _chars + pos; } const char *RightPtr(unsigned num) const { return _chars + _len - num; } char Back() const { return _chars[_len - 1]; } void ReplaceOneCharAtPos(unsigned pos, char c) { _chars[pos] = c; } // The minimum size of the character buffer in characters. // This value does not include space for a null terminator. char *GetBuffer(unsigned minBufLen) { if (minBufLen > _limit) ReAlloc(minBufLen); return _chars; } void ReleaseBuffer() { ReleaseBuffer(MyStringLen(_chars)); } void ReleaseBuffer(unsigned newLen) { _len = newLen; _chars[newLen] = 0; } AString &operator=(char c); AString &operator=(const char *s); AString &operator=(const AString &s); AString &operator+=(char c) { if (_limit == _len) Grow_1(); unsigned len = _len; char *chars = _chars; chars[len++] = c; chars[len] = 0; _len = len; return *this; } AString &operator+=(const char *s); AString &operator+=(const AString &s); void SetFrom(const char *s, unsigned len); // no check // AString Mid(unsigned startIndex, unsigned count) const { return AString(count, _chars + startIndex); } AString Left(unsigned count) const { return AString(count, *this); } // void MakeUpper() { MyStringUpper(_chars); } // void MakeLower() { MyStringLower(_chars); } // int Compare(const char *s) const { return MyStringCompare(_chars, s); } // int Compare(const AString &s) const { return MyStringCompare(_chars, s._chars); } // int CompareNoCase(const char *s) const { return MyStringCompareNoCase(_chars, s); } // int CompareNoCase(const AString &s) const { return MyStringCompareNoCase(_chars, s._chars); } bool IsPrefixedBy(const char *s) const { return IsString1PrefixedByString2(_chars, s); } bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw(); int Find(char c) const { return FindCharPosInString(_chars, c); } int Find(char c, unsigned startIndex) const { int pos = FindCharPosInString(_chars + startIndex, c); return pos < 0 ? -1 : (int)startIndex + pos; } int ReverseFind(char c) const throw(); int Find(const AString &s) const { return Find(s, 0); } int Find(const AString &s, unsigned startIndex) const throw(); void TrimLeft() throw(); void TrimRight() throw(); void Trim() { TrimRight(); TrimLeft(); } void InsertAtFront(char c); // void Insert(unsigned index, char c); void Insert(unsigned index, const char *s); void Insert(unsigned index, const AString &s); void RemoveChar(char ch) throw(); void Replace(char oldChar, char newChar) throw(); void Replace(const AString &oldString, const AString &newString); void Delete(unsigned index) throw(); void Delete(unsigned index, unsigned count) throw(); void DeleteFrontal(unsigned num) throw(); void DeleteBack() { _chars[--_len] = 0; } void DeleteFrom(unsigned index) { if (index < _len) { _len = index; _chars[index] = 0; } } }; bool operator<(const AString &s1, const AString &s2); bool operator>(const AString &s1, const AString &s2); /* bool operator==(const AString &s1, const AString &s2); bool operator==(const AString &s1, const char *s2); bool operator==(const char *s1, const AString &s2); bool operator!=(const AString &s1, const AString &s2); bool operator!=(const AString &s1, const char *s2); bool operator!=(const char *s1, const AString &s2); */ inline bool operator==(const AString &s1, const AString &s2) { return s1.Len() == s2.Len() && strcmp(s1, s2) == 0; } inline bool operator==(const AString &s1, const char *s2) { return strcmp(s1, s2) == 0; } inline bool operator==(const char *s1, const AString &s2) { return strcmp(s1, s2) == 0; } inline bool operator!=(const AString &s1, const AString &s2) { return s1.Len() != s2.Len() || strcmp(s1, s2) != 0; } inline bool operator!=(const AString &s1, const char *s2) { return strcmp(s1, s2) != 0; } inline bool operator!=(const char *s1, const AString &s2) { return strcmp(s1, s2) != 0; } class UString { wchar_t *_chars; unsigned _len; unsigned _limit; void MoveItems(unsigned dest, unsigned src) { memmove(_chars + dest, _chars + src, (size_t)(_len - src + 1) * sizeof(wchar_t)); } void InsertSpace(unsigned index, unsigned size); void ReAlloc(unsigned newLimit); void SetStartLen(unsigned len); void Grow_1(); void Grow(unsigned n); UString(unsigned num, const wchar_t *s); // for Mid UString(unsigned num, const UString &s); // for Left UString(const UString &s, wchar_t c); // it's for String + char UString(const wchar_t *s1, unsigned num1, const wchar_t *s2, unsigned num2); friend UString operator+(const UString &s, wchar_t c) { return UString(s, c); } ; // friend UString operator+(wchar_t c, const UString &s); // is not supported friend UString operator+(const UString &s1, const UString &s2); friend UString operator+(const UString &s1, const wchar_t *s2); friend UString operator+(const wchar_t *s1, const UString &s2); public: UString(); UString(wchar_t c); UString(const wchar_t *s); UString(const UString &s); ~UString() { MY_STRING_DELETE(_chars); } unsigned Len() const { return _len; } bool IsEmpty() const { return _len == 0; } void Empty() { _len = 0; _chars[0] = 0; } operator const wchar_t *() const { return _chars; } const wchar_t *Ptr() const { return _chars; } const wchar_t *Ptr(unsigned pos) const { return _chars + pos; } const wchar_t *RightPtr(unsigned num) const { return _chars + _len - num; } wchar_t Back() const { return _chars[_len - 1]; } void ReplaceOneCharAtPos(unsigned pos, wchar_t c) { _chars[pos] = c; } // The minimum size of the character buffer in characters. // This value does not include space for a null terminator. wchar_t *GetBuffer(unsigned minBufLen) { if (minBufLen > _limit) ReAlloc(minBufLen); return _chars; } void ReleaseBuffer() { ReleaseBuffer(MyStringLen(_chars)); } void ReleaseBuffer(unsigned newLen) { _len = newLen; _chars[newLen] = 0; } UString &operator=(wchar_t c); UString &operator=(const wchar_t *s); UString &operator=(const UString &s); UString &operator+=(wchar_t c) { if (_limit == _len) Grow_1(); unsigned len = _len; wchar_t *chars = _chars; chars[len++] = c; chars[len] = 0; _len = len; return *this; } UString &operator+=(const wchar_t *s); UString &operator+=(const UString &s); void SetFrom(const wchar_t *s, unsigned len); // no check void SetFromAscii(const char *s); void AddAsciiStr(const char *s); UString Mid(unsigned startIndex, unsigned count) const { return UString(count, _chars + startIndex); } UString Left(unsigned count) const { return UString(count, *this); } // void MakeUpper() { MyStringUpper(_chars); } // void MakeUpper() { MyStringUpper_Ascii(_chars); } // void MakeUpper_Ascii() { MyStringUpper_Ascii(_chars); } void MakeLower_Ascii() { MyStringLower_Ascii(_chars); } bool IsEqualTo(const char *s) const { return StringsAreEqual_Ascii(_chars, s); } bool IsEqualToNoCase(const wchar_t *s) const { return StringsAreEqualNoCase(_chars, s); } int Compare(const wchar_t *s) const { return wcscmp(_chars, s); } // int Compare(const UString &s) const { return MyStringCompare(_chars, s._chars); } // int CompareNoCase(const wchar_t *s) const { return MyStringCompareNoCase(_chars, s); } // int CompareNoCase(const UString &s) const { return MyStringCompareNoCase(_chars, s._chars); } bool IsPrefixedBy(const wchar_t *s) const { return IsString1PrefixedByString2(_chars, s); }; bool IsPrefixedBy_Ascii_NoCase(const char *s) const throw(); int Find(wchar_t c) const { return FindCharPosInString(_chars, c); } int Find(wchar_t c, unsigned startIndex) const { int pos = FindCharPosInString(_chars + startIndex, c); return pos < 0 ? -1 : (int)startIndex + pos; } int Find(const UString &s) const { return Find(s, 0); } int Find(const UString &s, unsigned startIndex) const throw(); int ReverseFind(wchar_t c) const throw(); void TrimLeft() throw(); void TrimRight() throw(); void Trim() { TrimRight(); TrimLeft(); } void InsertAtFront(wchar_t c); // void Insert(unsigned index, wchar_t c); void Insert(unsigned index, const wchar_t *s); void Insert(unsigned index, const UString &s); void RemoveChar(wchar_t ch) throw(); void Replace(wchar_t oldChar, wchar_t newChar) throw(); void Replace(const UString &oldString, const UString &newString); void Delete(unsigned index) throw(); void Delete(unsigned index, unsigned count) throw(); void DeleteFrontal(unsigned num) throw(); void DeleteBack() { _chars[--_len] = 0; } void DeleteFrom(unsigned index) { if (index < _len) { _len = index; _chars[index] = 0; } } }; bool operator<(const UString &s1, const UString &s2); bool operator>(const UString &s1, const UString &s2); inline bool operator==(const UString &s1, const UString &s2) { return s1.Len() == s2.Len() && wcscmp(s1, s2) == 0; } inline bool operator==(const UString &s1, const wchar_t *s2) { return wcscmp(s1, s2) == 0; } inline bool operator==(const wchar_t *s1, const UString &s2) { return wcscmp(s1, s2) == 0; } inline bool operator!=(const UString &s1, const UString &s2) { return s1.Len() != s2.Len() || wcscmp(s1, s2) != 0; } inline bool operator!=(const UString &s1, const wchar_t *s2) { return wcscmp(s1, s2) != 0; } inline bool operator!=(const wchar_t *s1, const UString &s2) { return wcscmp(s1, s2) != 0; } typedef CObjectVector AStringVector; typedef CObjectVector UStringVector; #ifdef _UNICODE typedef UString CSysString; #else typedef AString CSysString; #endif typedef CObjectVector CSysStringVector; // ---------- FString ---------- #ifdef _WIN32 #define USE_UNICODE_FSTRING #endif #ifdef USE_UNICODE_FSTRING #define __FTEXT(quote) L##quote typedef wchar_t FChar; typedef UString FString; #define fs2us(_x_) (_x_) #define us2fs(_x_) (_x_) FString fas2fs(const AString &s); AString fs2fas(const FChar *s); #else #define __FTEXT(quote) quote typedef char FChar; typedef AString FString; UString fs2us(const FString &s); FString us2fs(const wchar_t *s); #define fas2fs(_x_) (_x_) #define fs2fas(_x_) (_x_) #endif #define FTEXT(quote) __FTEXT(quote) #define FCHAR_PATH_SEPARATOR FTEXT(CHAR_PATH_SEPARATOR) #define FSTRING_PATH_SEPARATOR FTEXT(STRING_PATH_SEPARATOR) #define FCHAR_ANY_MASK FTEXT('*') #define FSTRING_ANY_MASK FTEXT("*") typedef const FChar *CFSTR; typedef CObjectVector FStringVector; #endif src/libs/7zip/win/CPP/Common/MyTypes.h000066400000000000000000000005641325366651500177420ustar00rootroot00000000000000// Common/MyTypes.h #ifndef __COMMON_MY_TYPES_H #define __COMMON_MY_TYPES_H #include "../../C/7zTypes.h" typedef int HRes; struct CBoolPair { bool Val; bool Def; CBoolPair(): Val(false), Def(false) {} void Init() { Val = false; Def = false; } void SetTrueTrue() { Val = true; Def = true; } }; #endif src/libs/7zip/win/CPP/Common/MyUnknown.h000066400000000000000000000002601325366651500202660ustar00rootroot00000000000000// MyUnknown.h #ifndef __MY_UNKNOWN_H #define __MY_UNKNOWN_H #ifdef _WIN32 #include #include #else #include "MyWindows.h" #endif #endif src/libs/7zip/win/CPP/Common/MyVector.h000066400000000000000000000320401325366651500200720ustar00rootroot00000000000000// Common/MyVector.h #ifndef __COMMON_MY_VECTOR_H #define __COMMON_MY_VECTOR_H template class CRecordVector { T *_items; unsigned _size; unsigned _capacity; void MoveItems(unsigned destIndex, unsigned srcIndex) { memmove(_items + destIndex, _items + srcIndex, (size_t)(_size - srcIndex) * (size_t)sizeof(T)); } void ReserveOnePosition() { if (_size == _capacity) { unsigned newCapacity = _capacity + (_capacity >> 2) + 1; T *p = new T[newCapacity]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); delete []_items; _items = p; _capacity = newCapacity; } } public: CRecordVector(): _items(0), _size(0), _capacity(0) {} CRecordVector(const CRecordVector &v): _items(0), _size(0), _capacity(0) { unsigned size = v.Size(); if (size != 0) { _items = new T[size]; _size = size; _capacity = size; memcpy(_items, v._items, (size_t)size * (size_t)sizeof(T)); } } unsigned Size() const { return _size; } bool IsEmpty() const { return _size == 0; } void ConstructReserve(unsigned size) { if (size != 0) { _items = new T[size]; _capacity = size; } } void Reserve(unsigned newCapacity) { if (newCapacity > _capacity) { T *p = new T[newCapacity]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); delete []_items; _items = p; _capacity = newCapacity; } } void ClearAndReserve(unsigned newCapacity) { Clear(); if (newCapacity > _capacity) { delete []_items; _items = NULL; _capacity = 0; _items = new T[newCapacity]; _capacity = newCapacity; } } void ClearAndSetSize(unsigned newSize) { ClearAndReserve(newSize); _size = newSize; } void ChangeSize_KeepData(unsigned newSize) { if (newSize > _capacity) { T *p = new T[newSize]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); delete []_items; _items = p; _capacity = newSize; } _size = newSize; } void ReserveDown() { if (_size == _capacity) return; T *p = NULL; if (_size != 0) { p = new T[_size]; memcpy(p, _items, (size_t)_size * (size_t)sizeof(T)); } delete []_items; _items = p; _capacity = _size; } ~CRecordVector() { delete []_items; } void ClearAndFree() { delete []_items; _items = NULL; _size = 0; _capacity = 0; } void Clear() { _size = 0; } void DeleteBack() { _size--; } void DeleteFrom(unsigned index) { // if (index <= _size) _size = index; } void DeleteFrontal(unsigned num) { if (num != 0) { MoveItems(0, num); _size -= num; } } void Delete(unsigned index) { MoveItems(index, index + 1); _size -= 1; } /* void Delete(unsigned index, unsigned num) { if (num > 0) { MoveItems(index, index + num); _size -= num; } } */ CRecordVector& operator=(const CRecordVector &v) { unsigned size = v.Size(); if (size > _capacity) { delete []_items; _capacity = 0; _size = 0; _items = NULL; _items = new T[size]; _capacity = size; } _size = size; memcpy(_items, v._items, (size_t)size * (size_t)sizeof(T)); return *this; } CRecordVector& operator+=(const CRecordVector &v) { unsigned size = v.Size(); Reserve(_size + size); memcpy(_items + _size, v._items, (size_t)size * (size_t)sizeof(T)); _size += size; return *this; } unsigned Add(const T item) { ReserveOnePosition(); _items[_size] = item; return _size++; } void AddInReserved(const T item) { _items[_size++] = item; } void Insert(unsigned index, const T item) { ReserveOnePosition(); MoveItems(index + 1, index); _items[index] = item; _size++; } void MoveToFront(unsigned index) { if (index != 0) { T temp = _items[index]; memmove(_items + 1, _items, (size_t)index * (size_t)sizeof(T)); _items[0] = temp; } } const T& operator[](unsigned index) const { return _items[index]; } T& operator[](unsigned index) { return _items[index]; } const T& Front() const { return _items[0]; } T& Front() { return _items[0]; } const T& Back() const { return _items[_size - 1]; } T& Back() { return _items[_size - 1]; } /* void Swap(unsigned i, unsigned j) { T temp = _items[i]; _items[i] = _items[j]; _items[j] = temp; } */ int FindInSorted(const T item, unsigned left, unsigned right) const { while (left != right) { unsigned mid = (left + right) / 2; const T midVal = (*this)[mid]; if (item == midVal) return mid; if (item < midVal) right = mid; else left = mid + 1; } return -1; } int FindInSorted2(const T &item, unsigned left, unsigned right) const { while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } return -1; } int FindInSorted(const T item) const { return FindInSorted(item, 0, _size); } int FindInSorted2(const T &item) const { return FindInSorted2(item, 0, _size); } unsigned AddToUniqueSorted(const T item) { unsigned left = 0, right = _size; while (left != right) { unsigned mid = (left + right) / 2; const T midVal = (*this)[mid]; if (item == midVal) return mid; if (item < midVal) right = mid; else left = mid + 1; } Insert(right, item); return right; } unsigned AddToUniqueSorted2(const T &item) { unsigned left = 0, right = _size; while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } Insert(right, item); return right; } static void SortRefDown(T* p, unsigned k, unsigned size, int (*compare)(const T*, const T*, void *), void *param) { T temp = p[k]; for (;;) { unsigned s = (k << 1); if (s > size) break; if (s < size && compare(p + s + 1, p + s, param) > 0) s++; if (compare(&temp, p + s, param) >= 0) break; p[k] = p[s]; k = s; } p[k] = temp; } void Sort(int (*compare)(const T*, const T*, void *), void *param) { unsigned size = _size; if (size <= 1) return; T* p = (&Front()) - 1; { unsigned i = size >> 1; do SortRefDown(p, i, size, compare, param); while (--i != 0); } do { T temp = p[size]; p[size--] = p[1]; p[1] = temp; SortRefDown(p, 1, size, compare, param); } while (size > 1); } static void SortRefDown2(T* p, unsigned k, unsigned size) { T temp = p[k]; for (;;) { unsigned s = (k << 1); if (s > size) break; if (s < size && p[s + 1].Compare(p[s]) > 0) s++; if (temp.Compare(p[s]) >= 0) break; p[k] = p[s]; k = s; } p[k] = temp; } void Sort2() { unsigned size = _size; if (size <= 1) return; T* p = (&Front()) - 1; { unsigned i = size >> 1; do SortRefDown2(p, i, size); while (--i != 0); } do { T temp = p[size]; p[size--] = p[1]; p[1] = temp; SortRefDown2(p, 1, size); } while (size > 1); } }; typedef CRecordVector CIntVector; typedef CRecordVector CUIntVector; typedef CRecordVector CBoolVector; typedef CRecordVector CByteVector; typedef CRecordVector CPointerVector; template class CObjectVector { CPointerVector _v; public: unsigned Size() const { return _v.Size(); } bool IsEmpty() const { return _v.IsEmpty(); } void ReserveDown() { _v.ReserveDown(); } // void Reserve(unsigned newCapacity) { _v.Reserve(newCapacity); } void ClearAndReserve(unsigned newCapacity) { Clear(); _v.ClearAndReserve(newCapacity); } CObjectVector() {}; CObjectVector(const CObjectVector &v) { unsigned size = v.Size(); _v.ConstructReserve(size); for (unsigned i = 0; i < size; i++) _v.AddInReserved(new T(v[i])); } CObjectVector& operator=(const CObjectVector &v) { Clear(); unsigned size = v.Size(); _v.Reserve(size); for (unsigned i = 0; i < size; i++) _v.AddInReserved(new T(v[i])); return *this; } CObjectVector& operator+=(const CObjectVector &v) { unsigned size = v.Size(); _v.Reserve(Size() + size); for (unsigned i = 0; i < size; i++) _v.AddInReserved(new T(v[i])); return *this; } const T& operator[](unsigned index) const { return *((T *)_v[index]); } T& operator[](unsigned index) { return *((T *)_v[index]); } const T& Front() const { return operator[](0); } T& Front() { return operator[](0); } const T& Back() const { return operator[](_v.Size() - 1); } T& Back() { return operator[](_v.Size() - 1); } void MoveToFront(unsigned index) { _v.MoveToFront(index); } unsigned Add(const T& item) { return _v.Add(new T(item)); } void AddInReserved(const T& item) { _v.AddInReserved(new T(item)); } T& AddNew() { T *p = new T; _v.Add(p); return *p; } T& AddNewInReserved() { T *p = new T; _v.AddInReserved(p); return *p; } void Insert(unsigned index, const T& item) { _v.Insert(index, new T(item)); } T& InsertNew(unsigned index) { T *p = new T; _v.Insert(index, p); return *p; } ~CObjectVector() { for (unsigned i = _v.Size(); i != 0;) delete (T *)_v[--i]; } void ClearAndFree() { Clear(); _v.ClearAndFree(); } void Clear() { for (unsigned i = _v.Size(); i != 0;) delete (T *)_v[--i]; _v.Clear(); } void DeleteFrom(unsigned index) { unsigned size = _v.Size(); for (unsigned i = index; i < size; i++) delete (T *)_v[i]; _v.DeleteFrom(index); } void DeleteFrontal(unsigned num) { for (unsigned i = 0; i < num; i++) delete (T *)_v[i]; _v.DeleteFrontal(num); } void DeleteBack() { delete (T *)_v[_v.Size() - 1]; _v.DeleteBack(); } void Delete(unsigned index) { delete (T *)_v[index]; _v.Delete(index); } /* void Delete(unsigned index, unsigned num) { for (unsigned i = 0; i < num; i++) delete (T *)_v[index + i]; _v.Delete(index, num); } */ /* int Find(const T& item) const { unsigned size = Size(); for (unsigned i = 0; i < size; i++) if (item == (*this)[i]) return i; return -1; } */ int FindInSorted(const T& item) const { unsigned left = 0, right = Size(); while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } return -1; } unsigned AddToUniqueSorted(const T& item) { unsigned left = 0, right = Size(); while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) return mid; if (comp < 0) right = mid; else left = mid + 1; } Insert(right, item); return right; } /* unsigned AddToSorted(const T& item) { unsigned left = 0, right = Size(); while (left != right) { unsigned mid = (left + right) / 2; const T& midVal = (*this)[mid]; int comp = item.Compare(midVal); if (comp == 0) { right = mid + 1; break; } if (comp < 0) right = mid; else left = mid + 1; } Insert(right, item); return right; } */ void Sort(int (*compare)(void *const *, void *const *, void *), void *param) { _v.Sort(compare, param); } static int CompareObjectItems(void *const *a1, void *const *a2, void * /* param */) { return (*(*((const T **)a1))).Compare(*(*((const T **)a2))); } void Sort() { _v.Sort(CompareObjectItems, 0); } }; #define FOR_VECTOR(_i_, _v_) for (unsigned _i_ = 0; _i_ < (_v_).Size(); _i_++) #endif src/libs/7zip/win/CPP/Common/MyWindows.cpp000066400000000000000000000071461325366651500206260ustar00rootroot00000000000000// MyWindows.cpp #include "StdAfx.h" #ifndef _WIN32 #include #include "MyWindows.h" static inline void *AllocateForBSTR(size_t cb) { return ::malloc(cb); } static inline void FreeForBSTR(void *pv) { ::free(pv);} /* Win32 uses DWORD (32-bit) type to store size of string before (OLECHAR *) string. We must select CBstrSizeType for another systems (not Win32): if (CBstrSizeType is UINT32), then we support only strings smaller than 4 GB. Win32 version always has that limitation. if (CBstrSizeType is UINT), (UINT can be 16/32/64-bit) We can support strings larger than 4 GB (if UINT is 64-bit), but sizeof(UINT) can be different in parts compiled by different compilers/settings, and we can't send such BSTR strings between such parts. */ typedef UINT32 CBstrSizeType; // typedef UINT CBstrSizeType; #define k_BstrSize_Max 0xFFFFFFFF // #define k_BstrSize_Max UINT_MAX // #define k_BstrSize_Max ((UINT)(INT)-1) BSTR SysAllocStringByteLen(LPCSTR s, UINT len) { /* Original SysAllocStringByteLen in Win32 maybe fills only unaligned null OLECHAR at the end. We provide also aligned null OLECHAR at the end. */ if (len >= (k_BstrSize_Max - sizeof(OLECHAR) - sizeof(OLECHAR) - sizeof(CBstrSizeType))) return NULL; UINT size = (len + sizeof(OLECHAR) + sizeof(OLECHAR) - 1) & ~(sizeof(OLECHAR) - 1); void *p = AllocateForBSTR(size + sizeof(CBstrSizeType)); if (!p) return NULL; *(CBstrSizeType *)p = (CBstrSizeType)len; BSTR bstr = (BSTR)((CBstrSizeType *)p + 1); if (s) memcpy(bstr, s, len); for (; len < size; len++) ((Byte *)bstr)[len] = 0; return bstr; } BSTR SysAllocStringLen(const OLECHAR *s, UINT len) { if (len >= (k_BstrSize_Max - sizeof(OLECHAR) - sizeof(CBstrSizeType)) / sizeof(OLECHAR)) return NULL; UINT size = len * sizeof(OLECHAR); void *p = AllocateForBSTR(size + sizeof(CBstrSizeType) + sizeof(OLECHAR)); if (!p) return NULL; *(CBstrSizeType *)p = (CBstrSizeType)size; BSTR bstr = (BSTR)((CBstrSizeType *)p + 1); if (s) memcpy(bstr, s, size); bstr[len] = 0; return bstr; } BSTR SysAllocString(const OLECHAR *s) { if (!s) return 0; const OLECHAR *s2 = s; while (*s2 != 0) s2++; return SysAllocStringLen(s, (UINT)(s2 - s)); } void SysFreeString(BSTR bstr) { if (bstr) FreeForBSTR((CBstrSizeType *)bstr - 1); } UINT SysStringByteLen(BSTR bstr) { if (!bstr) return 0; return *((CBstrSizeType *)bstr - 1); } UINT SysStringLen(BSTR bstr) { if (!bstr) return 0; return *((CBstrSizeType *)bstr - 1) / sizeof(OLECHAR); } HRESULT VariantClear(VARIANTARG *prop) { if (prop->vt == VT_BSTR) SysFreeString(prop->bstrVal); prop->vt = VT_EMPTY; return S_OK; } HRESULT VariantCopy(VARIANTARG *dest, const VARIANTARG *src) { HRESULT res = ::VariantClear(dest); if (res != S_OK) return res; if (src->vt == VT_BSTR) { dest->bstrVal = SysAllocStringByteLen((LPCSTR)src->bstrVal, SysStringByteLen(src->bstrVal)); if (!dest->bstrVal) return E_OUTOFMEMORY; dest->vt = VT_BSTR; } else *dest = *src; return S_OK; } LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2) { if (ft1->dwHighDateTime < ft2->dwHighDateTime) return -1; if (ft1->dwHighDateTime > ft2->dwHighDateTime) return 1; if (ft1->dwLowDateTime < ft2->dwLowDateTime) return -1; if (ft1->dwLowDateTime > ft2->dwLowDateTime) return 1; return 0; } DWORD GetLastError() { return 0; } #endif src/libs/7zip/win/CPP/Common/MyWindows.h000066400000000000000000000110261325366651500202630ustar00rootroot00000000000000// MyWindows.h #ifndef __MY_WINDOWS_H #define __MY_WINDOWS_H #ifdef _WIN32 #include #ifdef UNDER_CE #undef VARIANT_TRUE #define VARIANT_TRUE ((VARIANT_BOOL)-1) #endif #else #include // for wchar_t #include #include "MyGuidDef.h" #define WINAPI typedef char CHAR; typedef unsigned char UCHAR; #undef BYTE typedef unsigned char BYTE; typedef short SHORT; typedef unsigned short USHORT; #undef WORD typedef unsigned short WORD; typedef short VARIANT_BOOL; typedef int INT; typedef Int32 INT32; typedef unsigned int UINT; typedef UInt32 UINT32; typedef INT32 LONG; // LONG, ULONG and DWORD must be 32-bit typedef UINT32 ULONG; #undef DWORD typedef UINT32 DWORD; typedef Int64 LONGLONG; typedef UInt64 ULONGLONG; typedef struct _LARGE_INTEGER { LONGLONG QuadPart; } LARGE_INTEGER; typedef struct _ULARGE_INTEGER { ULONGLONG QuadPart; } ULARGE_INTEGER; typedef const CHAR *LPCSTR; typedef CHAR TCHAR; typedef const TCHAR *LPCTSTR; typedef wchar_t WCHAR; typedef WCHAR OLECHAR; typedef const WCHAR *LPCWSTR; typedef OLECHAR *BSTR; typedef const OLECHAR *LPCOLESTR; typedef OLECHAR *LPOLESTR; typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME; #define HRESULT LONG #define FAILED(Status) ((HRESULT)(Status)<0) typedef ULONG PROPID; typedef LONG SCODE; #define S_OK ((HRESULT)0x00000000L) #define S_FALSE ((HRESULT)0x00000001L) #define E_NOTIMPL ((HRESULT)0x80004001L) #define E_NOINTERFACE ((HRESULT)0x80004002L) #define E_ABORT ((HRESULT)0x80004004L) #define E_FAIL ((HRESULT)0x80004005L) #define STG_E_INVALIDFUNCTION ((HRESULT)0x80030001L) #define E_OUTOFMEMORY ((HRESULT)0x8007000EL) #define E_INVALIDARG ((HRESULT)0x80070057L) #ifdef _MSC_VER #define STDMETHODCALLTYPE __stdcall #else #define STDMETHODCALLTYPE #endif #define STDMETHOD_(t, f) virtual t STDMETHODCALLTYPE f #define STDMETHOD(f) STDMETHOD_(HRESULT, f) #define STDMETHODIMP_(type) type STDMETHODCALLTYPE #define STDMETHODIMP STDMETHODIMP_(HRESULT) #define PURE = 0 #define MIDL_INTERFACE(x) struct #ifdef __cplusplus DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); struct IUnknown { STDMETHOD(QueryInterface) (REFIID iid, void **outObject) PURE; STDMETHOD_(ULONG, AddRef)() PURE; STDMETHOD_(ULONG, Release)() PURE; #ifndef _WIN32 virtual ~IUnknown() {} #endif }; typedef IUnknown *LPUNKNOWN; #endif #define VARIANT_TRUE ((VARIANT_BOOL)-1) #define VARIANT_FALSE ((VARIANT_BOOL)0) enum VARENUM { VT_EMPTY = 0, VT_NULL = 1, VT_I2 = 2, VT_I4 = 3, VT_R4 = 4, VT_R8 = 5, VT_CY = 6, VT_DATE = 7, VT_BSTR = 8, VT_DISPATCH = 9, VT_ERROR = 10, VT_BOOL = 11, VT_VARIANT = 12, VT_UNKNOWN = 13, VT_DECIMAL = 14, VT_I1 = 16, VT_UI1 = 17, VT_UI2 = 18, VT_UI4 = 19, VT_I8 = 20, VT_UI8 = 21, VT_INT = 22, VT_UINT = 23, VT_VOID = 24, VT_HRESULT = 25, VT_FILETIME = 64 }; typedef unsigned short VARTYPE; typedef WORD PROPVAR_PAD1; typedef WORD PROPVAR_PAD2; typedef WORD PROPVAR_PAD3; typedef struct tagPROPVARIANT { VARTYPE vt; PROPVAR_PAD1 wReserved1; PROPVAR_PAD2 wReserved2; PROPVAR_PAD3 wReserved3; union { CHAR cVal; UCHAR bVal; SHORT iVal; USHORT uiVal; LONG lVal; ULONG ulVal; INT intVal; UINT uintVal; LARGE_INTEGER hVal; ULARGE_INTEGER uhVal; VARIANT_BOOL boolVal; SCODE scode; FILETIME filetime; BSTR bstrVal; }; } PROPVARIANT; typedef PROPVARIANT tagVARIANT; typedef tagVARIANT VARIANT; typedef VARIANT VARIANTARG; MY_EXTERN_C HRESULT VariantClear(VARIANTARG *prop); MY_EXTERN_C HRESULT VariantCopy(VARIANTARG *dest, const VARIANTARG *src); typedef struct tagSTATPROPSTG { LPOLESTR lpwstrName; PROPID propid; VARTYPE vt; } STATPROPSTG; MY_EXTERN_C BSTR SysAllocStringByteLen(LPCSTR psz, UINT len); MY_EXTERN_C BSTR SysAllocStringLen(const OLECHAR *sz, UINT len); MY_EXTERN_C BSTR SysAllocString(const OLECHAR *sz); MY_EXTERN_C void SysFreeString(BSTR bstr); MY_EXTERN_C UINT SysStringByteLen(BSTR bstr); MY_EXTERN_C UINT SysStringLen(BSTR bstr); MY_EXTERN_C DWORD GetLastError(); MY_EXTERN_C LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2); #define CP_ACP 0 #define CP_OEMCP 1 #define CP_UTF8 65001 typedef enum tagSTREAM_SEEK { STREAM_SEEK_SET = 0, STREAM_SEEK_CUR = 1, STREAM_SEEK_END = 2 } STREAM_SEEK; #endif #endif src/libs/7zip/win/CPP/Common/NewHandler.cpp000066400000000000000000000051271325366651500207120ustar00rootroot00000000000000// NewHandler.cpp #include "StdAfx.h" #include #include "NewHandler.h" // #define DEBUG_MEMORY_LEAK #ifndef DEBUG_MEMORY_LEAK #ifdef _WIN32 /* void * my_new(size_t size) { // void *p = ::HeapAlloc(::GetProcessHeap(), 0, size); void *p = ::malloc(size); if (p == 0) throw CNewException(); return p; } void my_delete(void *p) throw() { // if (p == 0) return; ::HeapFree(::GetProcessHeap(), 0, p); ::free(p); } void * my_Realloc(void *p, size_t newSize, size_t oldSize) { void *newBuf = my_new(newSize); memcpy(newBuf, p, oldSize); my_delete(p); return newBuf; } */ void * #ifdef _MSC_VER __cdecl #endif operator new(size_t size) { // void *p = ::HeapAlloc(::GetProcessHeap(), 0, size); void *p = ::malloc(size); if (p == 0) throw CNewException(); return p; } void #ifdef _MSC_VER __cdecl #endif operator delete(void *p) throw() { // if (p == 0) return; ::HeapFree(::GetProcessHeap(), 0, p); ::free(p); } /* void * #ifdef _MSC_VER __cdecl #endif operator new[](size_t size) { // void *p = ::HeapAlloc(::GetProcessHeap(), 0, size); void *p = ::malloc(size); if (p == 0) throw CNewException(); return p; } void #ifdef _MSC_VER __cdecl #endif operator delete[](void *p) throw() { // if (p == 0) return; ::HeapFree(::GetProcessHeap(), 0, p); ::free(p); } */ #endif #else #include // #pragma init_seg(lib) const int kDebugSize = 1000000; static void *a[kDebugSize]; static int index = 0; static int numAllocs = 0; void * __cdecl operator new(size_t size) { numAllocs++; void *p = HeapAlloc(GetProcessHeap(), 0, size); if (index < kDebugSize) { a[index] = p; index++; } if (p == 0) throw CNewException(); printf("Alloc %6d, size = %8d\n", numAllocs, size); return p; } class CC { public: CC() { for (int i = 0; i < kDebugSize; i++) a[i] = 0; } ~CC() { for (int i = 0; i < kDebugSize; i++) if (a[i] != 0) return; } } g_CC; void __cdecl operator delete(void *p) { if (p == 0) return; /* for (int i = 0; i < index; i++) if (a[i] == p) a[i] = 0; */ HeapFree(GetProcessHeap(), 0, p); numAllocs--; printf("Free %d\n", numAllocs); } #endif /* int MemErrorVC(size_t) { throw CNewException(); // return 1; } CNewHandlerSetter::CNewHandlerSetter() { // MemErrorOldVCFunction = _set_new_handler(MemErrorVC); } CNewHandlerSetter::~CNewHandlerSetter() { // _set_new_handler(MemErrorOldVCFunction); } */ src/libs/7zip/win/CPP/Common/NewHandler.h000066400000000000000000000031441325366651500203540ustar00rootroot00000000000000// Common/NewHandler.h #ifndef __COMMON_NEW_HANDLER_H #define __COMMON_NEW_HANDLER_H /* This file must be included before any code that uses operators "delete" or "new". Also you must compile and link "NewHandler.cpp", if you use MSVC 6.0. The operator "new" in MSVC 6.0 doesn't throw exception "bad_alloc". So we define another version of operator "new" that throws "CNewException" on failure. If you use compiler that throws exception in "new" operator (GCC or new version of MSVC), you can compile without "NewHandler.cpp". So standard exception "bad_alloc" will be used. It's still allowed to use redefined version of operator "new" from "NewHandler.cpp" with any compiler. 7-Zip's code can work with "bad_alloc" and "CNewException" exceptions. But if you use some additional code (outside of 7-Zip's code), you must check that redefined version of operator "new" (that throws CNewException) is not problem for your code. Also we declare delete(void *p) throw() that creates smaller code. */ class CNewException {}; #ifdef WIN32 // We can compile my_new and my_delete with _fastcall /* void * my_new(size_t size); void my_delete(void *p) throw(); // void * my_Realloc(void *p, size_t newSize, size_t oldSize); */ #endif #ifdef _WIN32 void * #ifdef _MSC_VER __cdecl #endif operator new(size_t size); void #ifdef _MSC_VER __cdecl #endif operator delete(void *p) throw(); #endif /* #ifdef _WIN32 void * #ifdef _MSC_VER __cdecl #endif operator new[](size_t size); void #ifdef _MSC_VER __cdecl #endif operator delete[](void *p) throw(); #endif */ #endif src/libs/7zip/win/CPP/Common/StdAfx.h000066400000000000000000000001301325366651500175060ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "Common.h" #endif src/libs/7zip/win/CPP/Common/StdOutStream.cpp000066400000000000000000000043671325366651500212660ustar00rootroot00000000000000// Common/StdOutStream.cpp #include "StdAfx.h" #include #include "IntToString.h" #include "StdOutStream.h" #include "StringConvert.h" #include "UTFConvert.h" static const char kNewLineChar = '\n'; static const char *kFileOpenMode = "wt"; extern int g_CodePage; CStdOutStream g_StdOut(stdout); CStdOutStream g_StdErr(stderr); bool CStdOutStream::Open(const char *fileName) throw() { Close(); _stream = fopen(fileName, kFileOpenMode); _streamIsOpen = (_stream != 0); return _streamIsOpen; } bool CStdOutStream::Close() throw() { if (!_streamIsOpen) return true; if (fclose(_stream) != 0) return false; _stream = 0; _streamIsOpen = false; return true; } bool CStdOutStream::Flush() throw() { return (fflush(_stream) == 0); } CStdOutStream & endl(CStdOutStream & outStream) throw() { return outStream << kNewLineChar; } CStdOutStream & CStdOutStream::operator<<(const wchar_t *s) { int codePage = g_CodePage; if (codePage == -1) codePage = CP_OEMCP; AString dest; if (codePage == CP_UTF8) ConvertUnicodeToUTF8(s, dest); else UnicodeStringToMultiByte2(dest, s, (UINT)codePage); return operator<<((const char *)dest); } void StdOut_Convert_UString_to_AString(const UString &s, AString &temp) { int codePage = g_CodePage; if (codePage == -1) codePage = CP_OEMCP; if (codePage == CP_UTF8) ConvertUnicodeToUTF8(s, temp); else UnicodeStringToMultiByte2(temp, s, (UINT)codePage); } void CStdOutStream::PrintUString(const UString &s, AString &temp) { StdOut_Convert_UString_to_AString(s, temp); *this << (const char *)temp; } CStdOutStream & CStdOutStream::operator<<(Int32 number) throw() { char s[32]; ConvertInt64ToString(number, s); return operator<<(s); } CStdOutStream & CStdOutStream::operator<<(Int64 number) throw() { char s[32]; ConvertInt64ToString(number, s); return operator<<(s); } CStdOutStream & CStdOutStream::operator<<(UInt32 number) throw() { char s[16]; ConvertUInt32ToString(number, s); return operator<<(s); } CStdOutStream & CStdOutStream::operator<<(UInt64 number) throw() { char s[32]; ConvertUInt64ToString(number, s); return operator<<(s); } src/libs/7zip/win/CPP/Common/StdOutStream.h000066400000000000000000000030341325366651500207210ustar00rootroot00000000000000// Common/StdOutStream.h #ifndef __COMMON_STD_OUT_STREAM_H #define __COMMON_STD_OUT_STREAM_H #include #include "MyString.h" #include "MyTypes.h" class CStdOutStream { FILE *_stream; bool _streamIsOpen; public: CStdOutStream(): _stream(0), _streamIsOpen(false) {}; CStdOutStream(FILE *stream): _stream(stream), _streamIsOpen(false) {}; ~CStdOutStream() { Close(); } // void AttachStdStream(FILE *stream) { _stream = stream; _streamIsOpen = false; } // bool IsDefined() const { return _stream != NULL; } operator FILE *() { return _stream; } bool Open(const char *fileName) throw(); bool Close() throw(); bool Flush() throw(); CStdOutStream & operator<<(CStdOutStream & (* func)(CStdOutStream &)) { (*func)(*this); return *this; } CStdOutStream & operator<<(const char *s) throw() { fputs(s, _stream); return *this; } CStdOutStream & operator<<(char c) throw() { fputc(c, _stream); return *this; } CStdOutStream & operator<<(Int32 number) throw(); CStdOutStream & operator<<(Int64 number) throw(); CStdOutStream & operator<<(UInt32 number) throw(); CStdOutStream & operator<<(UInt64 number) throw(); CStdOutStream & operator<<(const wchar_t *s); void PrintUString(const UString &s, AString &temp); }; CStdOutStream & endl(CStdOutStream & outStream) throw(); extern CStdOutStream g_StdOut; extern CStdOutStream g_StdErr; void StdOut_Convert_UString_to_AString(const UString &s, AString &temp); #endif src/libs/7zip/win/CPP/Common/StringConvert.cpp000066400000000000000000000105401325366651500214650ustar00rootroot00000000000000// Common/StringConvert.cpp #include "StdAfx.h" #include "StringConvert.h" #ifndef _WIN32 #include #endif #ifdef _WIN32 UString MultiByteToUnicodeString(const AString &srcString, UINT codePage) { UString resultString; if (!srcString.IsEmpty()) { int numChars = MultiByteToWideChar(codePage, 0, srcString, srcString.Len(), resultString.GetBuffer(srcString.Len()), srcString.Len() + 1); if (numChars == 0) throw 282228; resultString.ReleaseBuffer(numChars); } return resultString; } void MultiByteToUnicodeString2(UString &dest, const AString &srcString, UINT codePage) { dest.Empty(); if (!srcString.IsEmpty()) { wchar_t *destBuf = dest.GetBuffer(srcString.Len()); const char *sp = (const char *)srcString; unsigned i; for (i = 0;;) { char c = sp[i]; if ((Byte)c >= 0x80 || c == 0) break; destBuf[i++] = (wchar_t)c; } if (i != srcString.Len()) { unsigned numChars = MultiByteToWideChar(codePage, 0, sp + i, srcString.Len() - i, destBuf + i, srcString.Len() + 1 - i); if (numChars == 0) throw 282228; i += numChars; } dest.ReleaseBuffer(i); } } void UnicodeStringToMultiByte2(AString &dest, const UString &s, UINT codePage, char defaultChar, bool &defaultCharWasUsed) { dest.Empty(); defaultCharWasUsed = false; if (!s.IsEmpty()) { unsigned numRequiredBytes = s.Len() * 2; char *destBuf = dest.GetBuffer(numRequiredBytes); unsigned i; const wchar_t *sp = (const wchar_t *)s; for (i = 0;;) { wchar_t c = sp[i]; if (c >= 0x80 || c == 0) break; destBuf[i++] = (char)c; } defaultCharWasUsed = false; if (i != s.Len()) { BOOL defUsed; unsigned numChars = WideCharToMultiByte(codePage, 0, sp + i, s.Len() - i, destBuf + i, numRequiredBytes + 1 - i, &defaultChar, &defUsed); defaultCharWasUsed = (defUsed != FALSE); if (numChars == 0) throw 282229; i += numChars; } dest.ReleaseBuffer(i); } } void UnicodeStringToMultiByte2(AString &dest, const UString &srcString, UINT codePage) { bool defaultCharWasUsed; UnicodeStringToMultiByte2(dest, srcString, codePage, '_', defaultCharWasUsed); } AString UnicodeStringToMultiByte(const UString &s, UINT codePage, char defaultChar, bool &defaultCharWasUsed) { AString dest; defaultCharWasUsed = false; if (!s.IsEmpty()) { unsigned numRequiredBytes = s.Len() * 2; BOOL defUsed; int numChars = WideCharToMultiByte(codePage, 0, s, s.Len(), dest.GetBuffer(numRequiredBytes), numRequiredBytes + 1, &defaultChar, &defUsed); defaultCharWasUsed = (defUsed != FALSE); if (numChars == 0) throw 282229; dest.ReleaseBuffer(numChars); } return dest; } AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage) { bool defaultCharWasUsed; return UnicodeStringToMultiByte(srcString, codePage, '_', defaultCharWasUsed); } #ifndef UNDER_CE AString SystemStringToOemString(const CSysString &srcString) { AString result; CharToOem(srcString, result.GetBuffer(srcString.Len() * 2)); result.ReleaseBuffer(); return result; } #endif #else UString MultiByteToUnicodeString(const AString &srcString, UINT codePage) { UString resultString; for (unsigned i = 0; i < srcString.Len(); i++) resultString += (wchar_t)srcString[i]; /* if (!srcString.IsEmpty()) { int numChars = mbstowcs(resultString.GetBuffer(srcString.Len()), srcString, srcString.Len() + 1); if (numChars < 0) throw "Your environment does not support UNICODE"; resultString.ReleaseBuffer(numChars); } */ return resultString; } AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage) { AString resultString; for (unsigned i = 0; i < srcString.Len(); i++) resultString += (char)srcString[i]; /* if (!srcString.IsEmpty()) { int numRequiredBytes = srcString.Len() * 6 + 1; int numChars = wcstombs(resultString.GetBuffer(numRequiredBytes), srcString, numRequiredBytes); if (numChars < 0) throw "Your environment does not support UNICODE"; resultString.ReleaseBuffer(numChars); } */ return resultString; } #endif src/libs/7zip/win/CPP/Common/StringConvert.h000066400000000000000000000066541325366651500211450ustar00rootroot00000000000000// Common/StringConvert.h #ifndef __COMMON_STRING_CONVERT_H #define __COMMON_STRING_CONVERT_H #include "MyString.h" #include "MyWindows.h" UString MultiByteToUnicodeString(const AString &srcString, UINT codePage = CP_ACP); // optimized versions that work faster for ASCII strings void MultiByteToUnicodeString2(UString &dest, const AString &srcString, UINT codePage = CP_ACP); void UnicodeStringToMultiByte2(AString &dest, const UString &s, UINT codePage, char defaultChar, bool &defaultCharWasUsed); void UnicodeStringToMultiByte2(AString &dest, const UString &srcString, UINT codePage); AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage, char defaultChar, bool &defaultCharWasUsed); AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage = CP_ACP); inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString) { return unicodeString; } inline const UString& GetUnicodeString(const UString &unicodeString) { return unicodeString; } inline UString GetUnicodeString(const AString &ansiString) { return MultiByteToUnicodeString(ansiString); } inline UString GetUnicodeString(const AString &multiByteString, UINT codePage) { return MultiByteToUnicodeString(multiByteString, codePage); } inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString, UINT) { return unicodeString; } inline const UString& GetUnicodeString(const UString &unicodeString, UINT) { return unicodeString; } inline const char* GetAnsiString(const char* ansiString) { return ansiString; } inline const AString& GetAnsiString(const AString &ansiString) { return ansiString; } inline AString GetAnsiString(const UString &unicodeString) { return UnicodeStringToMultiByte(unicodeString); } inline const char* GetOemString(const char* oemString) { return oemString; } inline const AString& GetOemString(const AString &oemString) { return oemString; } inline AString GetOemString(const UString &unicodeString) { return UnicodeStringToMultiByte(unicodeString, CP_OEMCP); } #ifdef _UNICODE inline const wchar_t* GetSystemString(const wchar_t* unicodeString) { return unicodeString;} inline const UString& GetSystemString(const UString &unicodeString) { return unicodeString;} inline const wchar_t* GetSystemString(const wchar_t* unicodeString, UINT /* codePage */) { return unicodeString;} inline const UString& GetSystemString(const UString &unicodeString, UINT /* codePage */) { return unicodeString;} inline UString GetSystemString(const AString &multiByteString, UINT codePage) { return MultiByteToUnicodeString(multiByteString, codePage);} inline UString GetSystemString(const AString &multiByteString) { return MultiByteToUnicodeString(multiByteString);} #else inline const char* GetSystemString(const char *ansiString) { return ansiString; } inline const AString& GetSystemString(const AString &multiByteString, UINT) { return multiByteString; } inline const char * GetSystemString(const char *multiByteString, UINT) { return multiByteString; } inline AString GetSystemString(const UString &unicodeString) { return UnicodeStringToMultiByte(unicodeString); } inline AString GetSystemString(const UString &unicodeString, UINT codePage) { return UnicodeStringToMultiByte(unicodeString, codePage); } #endif #ifndef UNDER_CE AString SystemStringToOemString(const CSysString &srcString); #endif #endif src/libs/7zip/win/CPP/Common/StringToInt.cpp000066400000000000000000000063011325366651500211020ustar00rootroot00000000000000// Common/StringToInt.cpp #include "StdAfx.h" #include "StringToInt.h" static const UInt32 k_UInt32_max = 0xFFFFFFFF; static const UInt64 k_UInt64_max = UINT64_CONST(0xFFFFFFFFFFFFFFFF); // static const UInt64 k_UInt64_max = (UInt64)(Int64)-1; #define CONVERT_STRING_TO_UINT_FUNC(uintType, charType) \ uintType ConvertStringTo ## uintType(const charType *s, const charType **end) throw() { \ if (end) *end = s; \ uintType res = 0; \ for (;; s++) { \ charType c = *s; \ if (c < '0' || c > '9') { if (end) *end = s; return res; } \ if (res > (k_ ## uintType ## _max) / 10) return 0; \ res *= 10; \ unsigned v = (c - '0'); \ if (res > (k_ ## uintType ## _max) - v) return 0; \ res += v; }} CONVERT_STRING_TO_UINT_FUNC(UInt32, char) CONVERT_STRING_TO_UINT_FUNC(UInt32, wchar_t) CONVERT_STRING_TO_UINT_FUNC(UInt64, char) CONVERT_STRING_TO_UINT_FUNC(UInt64, wchar_t) Int32 ConvertStringToInt32(const wchar_t *s, const wchar_t **end) throw() { if (end) *end = s; const wchar_t *s2 = s; if (*s == '-') s2++; if (*s2 == 0) return 0; const wchar_t *end2; UInt32 res = ConvertStringToUInt32(s2, &end2); if (*s == '-') { if (res > ((UInt32)1 << (32 - 1))) return 0; } else if ((res & ((UInt32)1 << (32 - 1))) != 0) return 0; if (end) *end = end2; if (*s == '-') return -(Int32)res; return (Int32)res; } UInt32 ConvertOctStringToUInt32(const char *s, const char **end) throw() { if (end) *end = s; UInt32 res = 0; for (;; s++) { char c = *s; if (c < '0' || c > '7') { if (end) *end = s; return res; } if ((res & (UInt32)7 << (32 - 3)) != 0) return 0; res <<= 3; res |= (unsigned)(c - '0'); } } UInt64 ConvertOctStringToUInt64(const char *s, const char **end) throw() { if (end) *end = s; UInt64 res = 0; for (;; s++) { char c = *s; if (c < '0' || c > '7') { if (end) *end = s; return res; } if ((res & (UInt64)7 << (64 - 3)) != 0) return 0; res <<= 3; res |= (unsigned)(c - '0'); } } UInt32 ConvertHexStringToUInt32(const char *s, const char **end) throw() { if (end) *end = s; UInt32 res = 0; for (;; s++) { char c = *s; unsigned v; if (c >= '0' && c <= '9') v = (c - '0'); else if (c >= 'A' && c <= 'F') v = 10 + (c - 'A'); else if (c >= 'a' && c <= 'f') v = 10 + (c - 'a'); else { if (end) *end = s; return res; } if ((res & (UInt32)0xF << (32 - 4)) != 0) return 0; res <<= 4; res |= v; } } UInt64 ConvertHexStringToUInt64(const char *s, const char **end) throw() { if (end) *end = s; UInt64 res = 0; for (;; s++) { char c = *s; unsigned v; if (c >= '0' && c <= '9') v = (c - '0'); else if (c >= 'A' && c <= 'F') v = 10 + (c - 'A'); else if (c >= 'a' && c <= 'f') v = 10 + (c - 'a'); else { if (end) *end = s; return res; } if ((res & (UInt64)0xF << (64 - 4)) != 0) return 0; res <<= 4; res |= v; } } src/libs/7zip/win/CPP/Common/StringToInt.h000066400000000000000000000014551325366651500205540ustar00rootroot00000000000000// Common/StringToInt.h #ifndef __COMMON_STRING_TO_INT_H #define __COMMON_STRING_TO_INT_H #include "MyTypes.h" UInt32 ConvertStringToUInt32(const char *s, const char **end) throw(); UInt64 ConvertStringToUInt64(const char *s, const char **end) throw(); UInt32 ConvertStringToUInt32(const wchar_t *s, const wchar_t **end) throw(); UInt64 ConvertStringToUInt64(const wchar_t *s, const wchar_t **end) throw(); Int32 ConvertStringToInt32(const wchar_t *s, const wchar_t **end) throw(); UInt32 ConvertOctStringToUInt32(const char *s, const char **end) throw(); UInt64 ConvertOctStringToUInt64(const char *s, const char **end) throw(); UInt32 ConvertHexStringToUInt32(const char *s, const char **end) throw(); UInt64 ConvertHexStringToUInt64(const char *s, const char **end) throw(); #endif src/libs/7zip/win/CPP/Common/UTFConvert.cpp000066400000000000000000000075241325366651500206650ustar00rootroot00000000000000// UTFConvert.cpp #include "StdAfx.h" #include "MyTypes.h" #include "UTFConvert.h" static const Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; bool CheckUTF8(const char *src) throw() { for (;;) { Byte c; unsigned numAdds; c = *src++; if (c == 0) return true; if (c < 0x80) continue; if (c < 0xC0) return false; for (numAdds = 1; numAdds < 5; numAdds++) if (c < kUtf8Limits[numAdds]) break; UInt32 value = (c - kUtf8Limits[numAdds - 1]); do { Byte c2 = *src++; if (c2 < 0x80 || c2 >= 0xC0) return false; value <<= 6; value |= (c2 - 0x80); } while (--numAdds); if (value >= 0x110000) return false; } } static Bool Utf8_To_Utf16(wchar_t *dest, size_t *destLen, const char *src, size_t srcLen) throw() { size_t destPos = 0, srcPos = 0; for (;;) { Byte c; unsigned numAdds; if (srcPos == srcLen) { *destLen = destPos; return True; } c = (Byte)src[srcPos++]; if (c < 0x80) { if (dest) dest[destPos] = (wchar_t)c; destPos++; continue; } if (c < 0xC0) break; for (numAdds = 1; numAdds < 5; numAdds++) if (c < kUtf8Limits[numAdds]) break; UInt32 value = (c - kUtf8Limits[numAdds - 1]); do { Byte c2; if (srcPos == srcLen) break; c2 = (Byte)src[srcPos++]; if (c2 < 0x80 || c2 >= 0xC0) break; value <<= 6; value |= (c2 - 0x80); } while (--numAdds); if (value < 0x10000) { if (dest) dest[destPos] = (wchar_t)value; destPos++; } else { value -= 0x10000; if (value >= 0x100000) break; if (dest) { dest[destPos + 0] = (wchar_t)(0xD800 + (value >> 10)); dest[destPos + 1] = (wchar_t)(0xDC00 + (value & 0x3FF)); } destPos += 2; } } *destLen = destPos; return False; } static Bool Utf16_To_Utf8(char *dest, size_t *destLen, const wchar_t *src, size_t srcLen) { size_t destPos = 0, srcPos = 0; for (;;) { unsigned numAdds; UInt32 value; if (srcPos == srcLen) { *destLen = destPos; return True; } value = src[srcPos++]; if (value < 0x80) { if (dest) dest[destPos] = (char)value; destPos++; continue; } if (value >= 0xD800 && value < 0xE000) { UInt32 c2; if (value >= 0xDC00 || srcPos == srcLen) break; c2 = src[srcPos++]; if (c2 < 0xDC00 || c2 >= 0xE000) break; value = (((value - 0xD800) << 10) | (c2 - 0xDC00)) + 0x10000; } for (numAdds = 1; numAdds < 5; numAdds++) if (value < (((UInt32)1) << (numAdds * 5 + 6))) break; if (dest) dest[destPos] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds))); destPos++; do { numAdds--; if (dest) dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F)); destPos++; } while (numAdds != 0); } *destLen = destPos; return False; } bool ConvertUTF8ToUnicode(const AString &src, UString &dest) { dest.Empty(); size_t destLen = 0; Utf8_To_Utf16(NULL, &destLen, src, src.Len()); Bool res = Utf8_To_Utf16(dest.GetBuffer((unsigned)destLen), &destLen, src, src.Len()); dest.ReleaseBuffer((unsigned)destLen); return res ? true : false; } bool ConvertUnicodeToUTF8(const UString &src, AString &dest) { dest.Empty(); size_t destLen = 0; Utf16_To_Utf8(NULL, &destLen, src, src.Len()); Bool res = Utf16_To_Utf8(dest.GetBuffer((unsigned)destLen), &destLen, src, src.Len()); dest.ReleaseBuffer((unsigned)destLen); return res ? true : false; } src/libs/7zip/win/CPP/Common/UTFConvert.h000066400000000000000000000005071325366651500203240ustar00rootroot00000000000000// Common/UTFConvert.h #ifndef __COMMON_UTF_CONVERT_H #define __COMMON_UTF_CONVERT_H #include "MyString.h" bool CheckUTF8(const char *src) throw(); bool ConvertUTF8ToUnicode(const AString &utfString, UString &resultString); bool ConvertUnicodeToUTF8(const UString &unicodeString, AString &resultString); #endif src/libs/7zip/win/CPP/Common/Wildcard.cpp000066400000000000000000000342721325366651500204170ustar00rootroot00000000000000// Common/Wildcard.cpp #include "StdAfx.h" #include "Wildcard.h" bool g_CaseSensitive = #ifdef _WIN32 false; #else true; #endif bool IsPath1PrefixedByPath2(const wchar_t *s1, const wchar_t *s2) { if (g_CaseSensitive) { for (;;) { wchar_t c2 = *s2++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (MyCharUpper(c1) != MyCharUpper(c2)) return false; } } for (;;) { wchar_t c2 = *s2++; if (c2 == 0) return true; wchar_t c1 = *s1++; if (c1 != c2) return false; } } int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW { if (g_CaseSensitive) return wcscmp(s1, s2); return MyStringCompareNoCase(s1, s2); } #ifndef USE_UNICODE_FSTRING int CompareFileNames(const char *s1, const char *s2) { if (g_CaseSensitive) return wcscmp(fs2us(s1), fs2us(s2)); return MyStringCompareNoCase(fs2us(s1), fs2us(s2)); } #endif // ----------------------------------------- // this function compares name with mask // ? - any char // * - any char or empty static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name) { for (;;) { wchar_t m = *mask; wchar_t c = *name; if (m == 0) return (c == 0); if (m == '*') { if (EnhancedMaskTest(mask + 1, name)) return true; if (c == 0) return false; } else { if (m == '?') { if (c == 0) return false; } else if (m != c) if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c)) return false; mask++; } name++; } } // -------------------------------------------------- // Splits path to strings void SplitPathToParts(const UString &path, UStringVector &pathParts) { pathParts.Clear(); unsigned len = path.Len(); if (len == 0) return; UString name; unsigned prev = 0; for (unsigned i = 0; i < len; i++) if (IsCharDirLimiter(path[i])) { name.SetFrom(path.Ptr(prev), i - prev); pathParts.Add(name); prev = i + 1; } name.SetFrom(path.Ptr(prev), len - prev); pathParts.Add(name); } void SplitPathToParts_2(const UString &path, UString &dirPrefix, UString &name) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; dirPrefix.SetFrom(path, (unsigned)(p - start)); name = p; } void SplitPathToParts_Smart(const UString &path, UString &dirPrefix, UString &name) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); if (p != start) { if (IsCharDirLimiter(*(p - 1))) p--; for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; } dirPrefix.SetFrom(path, (unsigned)(p - start)); name = p; } UString ExtractDirPrefixFromPath(const UString &path) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; return path.Left((unsigned)(p - start)); } UString ExtractFileNameFromPath(const UString &path) { const wchar_t *start = path; const wchar_t *p = start + path.Len(); for (; p != start; p--) if (IsCharDirLimiter(*(p - 1))) break; return p; } bool DoesWildcardMatchName(const UString &mask, const UString &name) { return EnhancedMaskTest(mask, name); } bool DoesNameContainWildcard(const UString &path) { for (unsigned i = 0; i < path.Len(); i++) { wchar_t c = path[i]; if (c == '*' || c == '?') return true; } return false; } // ----------------------------------------------------------' // NWildcard namespace NWildcard { #ifdef _WIN32 bool IsDriveColonName(const wchar_t *s) { wchar_t c = s[0]; return c != 0 && s[1] == ':' && s[2] == 0 && (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'); } #endif /* M = MaskParts.Size(); N = TestNameParts.Size(); File Dir ForFile rec M<=N [N-M, N) - !ForDir nonrec M=N [0, M) - ForDir rec M 1) return true; } return false; } bool CCensorNode::AreThereIncludeItems() const { if (IncludeItems.Size() > 0) return true; FOR_VECTOR (i, SubNodes) if (SubNodes[i].AreThereIncludeItems()) return true; return false; } bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const { const CObjectVector &items = include ? IncludeItems : ExcludeItems; FOR_VECTOR (i, items) if (items[i].CheckPath(pathParts, isFile)) return true; return false; } bool CCensorNode::CheckPathVect(const UStringVector &pathParts, bool isFile, bool &include) const { if (CheckPathCurrent(false, pathParts, isFile)) { include = false; return true; } include = true; bool finded = CheckPathCurrent(true, pathParts, isFile); if (pathParts.Size() <= 1) return finded; int index = FindSubNode(pathParts.Front()); if (index >= 0) { UStringVector pathParts2 = pathParts; pathParts2.Delete(0); if (SubNodes[index].CheckPathVect(pathParts2, isFile, include)) return true; } return finded; } bool CCensorNode::CheckPath2(bool isAltStream, const UString &path, bool isFile, bool &include) const { UStringVector pathParts; SplitPathToParts(path, pathParts); if (CheckPathVect(pathParts, isFile, include)) { if (!include || !isAltStream) return true; } if (isAltStream && !pathParts.IsEmpty()) { UString &back = pathParts.Back(); int pos = back.Find(L':'); if (pos > 0) { back.DeleteFrom(pos); return CheckPathVect(pathParts, isFile, include); } } return false; } bool CCensorNode::CheckPath(bool isAltStream, const UString &path, bool isFile) const { bool include; if (CheckPath2(isAltStream, path, isFile, include)) return include; return false; } bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const { if (CheckPathCurrent(include, pathParts, isFile)) return true; if (Parent == 0) return false; pathParts.Insert(0, Name); return Parent->CheckPathToRoot(include, pathParts, isFile); } /* bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const { UStringVector pathParts; SplitPathToParts(path, pathParts); return CheckPathToRoot(include, pathParts, isFile); } */ void CCensorNode::AddItem2(bool include, const UString &path, bool recursive, bool wildcardMatching) { if (path.IsEmpty()) return; bool forFile = true; bool forFolder = true; UString path2 = path; if (IsCharDirLimiter(path.Back())) { path2.DeleteBack(); forFile = false; } AddItem(include, path2, recursive, forFile, forFolder, wildcardMatching); } void CCensorNode::ExtendExclude(const CCensorNode &fromNodes) { ExcludeItems += fromNodes.ExcludeItems; FOR_VECTOR (i, fromNodes.SubNodes) { const CCensorNode &node = fromNodes.SubNodes[i]; int subNodeIndex = FindSubNode(node.Name); if (subNodeIndex < 0) subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this)); SubNodes[subNodeIndex].ExtendExclude(node); } } int CCensor::FindPrefix(const UString &prefix) const { FOR_VECTOR (i, Pairs) if (CompareFileNames(Pairs[i].Prefix, prefix) == 0) return i; return -1; } void CCensor::AddItem(ECensorPathMode pathMode, bool include, const UString &path, bool recursive, bool wildcardMatching) { UStringVector pathParts; if (path.IsEmpty()) throw "Empty file path"; SplitPathToParts(path, pathParts); bool forFile = true; if (pathParts.Back().IsEmpty()) { forFile = false; pathParts.DeleteBack(); } UString prefix; if (pathMode != k_AbsPath) { const UString &front = pathParts.Front(); bool isAbs = false; if (front.IsEmpty()) isAbs = true; else { #ifdef _WIN32 if (IsDriveColonName(front)) isAbs = true; else #endif FOR_VECTOR (i, pathParts) { const UString &part = pathParts[i]; if (part == L".." || part == L".") { isAbs = true; break; } } } unsigned numAbsParts = 0; if (isAbs) if (pathParts.Size() > 1) numAbsParts = pathParts.Size() - 1; else numAbsParts = 1; #ifdef _WIN32 // \\?\ case if (numAbsParts >= 3) { if (pathParts[0].IsEmpty() && pathParts[1].IsEmpty() && pathParts[2] == L"?") { prefix = WSTRING_PATH_SEPARATOR WSTRING_PATH_SEPARATOR L"?" WSTRING_PATH_SEPARATOR; numAbsParts -= 3; pathParts.DeleteFrontal(3); } } #endif if (numAbsParts > 1 && pathMode == k_FullPath) numAbsParts = 1; // We can't ignore wildcard, since we don't allow wildcard in SubNodes[].Name // if (wildcardMatching) for (unsigned i = 0; i < numAbsParts; i++) { { const UString &front = pathParts.Front(); if (DoesNameContainWildcard(front)) break; prefix += front; prefix += WCHAR_PATH_SEPARATOR; } pathParts.Delete(0); } } int index = FindPrefix(prefix); if (index < 0) index = Pairs.Add(CPair(prefix)); CItem item; item.PathParts = pathParts; item.ForDir = true; item.ForFile = forFile; item.Recursive = recursive; item.WildcardMatching = wildcardMatching; Pairs[index].Head.AddItem(include, item); } bool CCensor::CheckPath(bool isAltStream, const UString &path, bool isFile) const { bool finded = false; FOR_VECTOR (i, Pairs) { bool include; if (Pairs[i].Head.CheckPath2(isAltStream, path, isFile, include)) { if (!include) return false; finded = true; } } return finded; } void CCensor::ExtendExclude() { unsigned i; for (i = 0; i < Pairs.Size(); i++) if (Pairs[i].Prefix.IsEmpty()) break; if (i == Pairs.Size()) return; unsigned index = i; for (i = 0; i < Pairs.Size(); i++) if (index != i) Pairs[i].Head.ExtendExclude(Pairs[index].Head); } void CCensor::AddPathsToCensor(ECensorPathMode censorPathMode) { FOR_VECTOR(i, CensorPaths) { const CCensorPath &cp = CensorPaths[i]; AddItem(censorPathMode, cp.Include, cp.Path, cp.Recursive, cp.WildcardMatching); } CensorPaths.Clear(); } void CCensor::AddPreItem(bool include, const UString &path, bool recursive, bool wildcardMatching) { CCensorPath &cp = CensorPaths.AddNew(); cp.Path = path; cp.Include = include; cp.Recursive = recursive; cp.WildcardMatching = wildcardMatching; } } src/libs/7zip/win/CPP/Common/Wildcard.h000066400000000000000000000104131325366651500200530ustar00rootroot00000000000000// Common/Wildcard.h #ifndef __COMMON_WILDCARD_H #define __COMMON_WILDCARD_H #include "MyString.h" int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW; #ifndef USE_UNICODE_FSTRING int CompareFileNames(const char *s1, const char *s2); #endif bool IsPath1PrefixedByPath2(const wchar_t *s1, const wchar_t *s2); inline bool IsCharDirLimiter(wchar_t c) { return c == WCHAR_PATH_SEPARATOR #ifdef _WIN32 || c == L'/' #endif ; } void SplitPathToParts(const UString &path, UStringVector &pathParts); void SplitPathToParts_2(const UString &path, UString &dirPrefix, UString &name); void SplitPathToParts_Smart(const UString &path, UString &dirPrefix, UString &name); // ignores dir delimiter at the end of (path) UString ExtractDirPrefixFromPath(const UString &path); UString ExtractFileNameFromPath(const UString &path); bool DoesNameContainWildcard(const UString &path); bool DoesWildcardMatchName(const UString &mask, const UString &name); namespace NWildcard { #ifdef _WIN32 // returns true, if name is like "a:", "c:", ... bool IsDriveColonName(const wchar_t *s); #endif struct CItem { UStringVector PathParts; bool Recursive; bool ForFile; bool ForDir; bool WildcardMatching; #ifdef _WIN32 bool IsDriveItem() const { return PathParts.Size() == 1 && !ForFile && ForDir && IsDriveColonName(PathParts[0]); } #endif // CItem(): WildcardMatching(true) {} bool AreAllAllowed() const; bool CheckPath(const UStringVector &pathParts, bool isFile) const; }; class CCensorNode { CCensorNode *Parent; bool CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const; void AddItemSimple(bool include, CItem &item); bool CheckPathVect(const UStringVector &pathParts, bool isFile, bool &include) const; public: CCensorNode(): Parent(0) { }; CCensorNode(const UString &name, CCensorNode *parent): Name(name), Parent(parent) { }; UString Name; // wildcard is not allowed here CObjectVector SubNodes; CObjectVector IncludeItems; CObjectVector ExcludeItems; bool AreAllAllowed() const; int FindSubNode(const UString &path) const; void AddItem(bool include, CItem &item); void AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir, bool wildcardMatching); void AddItem2(bool include, const UString &path, bool recursive, bool wildcardMatching); bool NeedCheckSubDirs() const; bool AreThereIncludeItems() const; bool CheckPath2(bool isAltStream, const UString &path, bool isFile, bool &include) const; bool CheckPath(bool isAltStream, const UString &path, bool isFile) const; bool CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const; // bool CheckPathToRoot(const UString &path, bool isFile, bool include) const; void ExtendExclude(const CCensorNode &fromNodes); }; struct CPair { UString Prefix; CCensorNode Head; CPair(const UString &prefix): Prefix(prefix) { }; }; enum ECensorPathMode { k_RelatPath, // absolute prefix as Prefix, remain path in Tree k_FullPath, // drive prefix as Prefix, remain path in Tree k_AbsPath // full path in Tree }; struct CCensorPath { UString Path; bool Include; bool Recursive; bool WildcardMatching; CCensorPath(): Include(true), Recursive(false), WildcardMatching(true) {} }; class CCensor { int FindPrefix(const UString &prefix) const; public: CObjectVector Pairs; CObjectVector CensorPaths; bool AllAreRelative() const { return (Pairs.Size() == 1 && Pairs.Front().Prefix.IsEmpty()); } void AddItem(ECensorPathMode pathMode, bool include, const UString &path, bool recursive, bool wildcardMatching); bool CheckPath(bool isAltStream, const UString &path, bool isFile) const; void ExtendExclude(); void AddPathsToCensor(NWildcard::ECensorPathMode censorPathMode); void AddPreItem(bool include, const UString &path, bool recursive, bool wildcardMatching); void AddPreItem(const UString &path) { AddPreItem(true, path, false, false); } void AddPreItem_Wildcard() { AddPreItem(true, L"*", false, true); } }; } #endif src/libs/7zip/win/CPP/Windows/000077500000000000000000000000001325366651500163545ustar00rootroot00000000000000src/libs/7zip/win/CPP/Windows/DLL.cpp000066400000000000000000000034721325366651500175010ustar00rootroot00000000000000// Windows/DLL.cpp #include "StdAfx.h" #include "DLL.h" #ifndef _UNICODE extern bool g_IsNT; #endif extern HINSTANCE g_hInstance; namespace NWindows { namespace NDLL { bool CLibrary::Free() throw() { if (_module == 0) return true; if (!::FreeLibrary(_module)) return false; _module = 0; return true; } bool CLibrary::LoadEx(CFSTR path, DWORD flags) throw() { if (!Free()) return false; #ifndef _UNICODE if (!g_IsNT) { _module = ::LoadLibraryEx(fs2fas(path), NULL, flags); } else #endif { _module = ::LoadLibraryExW(fs2us(path), NULL, flags); } return (_module != NULL); } bool CLibrary::Load(CFSTR path) throw() { if (!Free()) return false; #ifndef _UNICODE if (!g_IsNT) { _module = ::LoadLibrary(fs2fas(path)); } else #endif { _module = ::LoadLibraryW(fs2us(path)); } return (_module != NULL); } bool MyGetModuleFileName(FString &path) { HMODULE hModule = g_hInstance; path.Empty(); #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; DWORD size = ::GetModuleFileName(hModule, s, MAX_PATH + 1); if (size <= MAX_PATH && size != 0) { path = fas2fs(s); return true; } } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; DWORD size = ::GetModuleFileNameW(hModule, s, MAX_PATH + 1); if (size <= MAX_PATH && size != 0) { path = us2fs(s); return true; } } return false; } #ifndef _SFX FString GetModuleDirPrefix() { FString s; if (MyGetModuleFileName(s)) { int pos = s.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos >= 0) { s.DeleteFrom(pos + 1); return s; } } return FTEXT(".") FSTRING_PATH_SEPARATOR; } #endif }} src/libs/7zip/win/CPP/Windows/DLL.h000066400000000000000000000021001325366651500171310ustar00rootroot00000000000000// Windows/DLL.h #ifndef __WINDOWS_DLL_H #define __WINDOWS_DLL_H #include "../Common/MyString.h" namespace NWindows { namespace NDLL { #ifdef UNDER_CE #define My_GetProcAddress(module, procName) ::GetProcAddressA(module, procName) #else #define My_GetProcAddress(module, procName) ::GetProcAddress(module, procName) #endif class CLibrary { HMODULE _module; public: CLibrary(): _module(NULL) {}; ~CLibrary() { Free(); } operator HMODULE() const { return _module; } HMODULE* operator&() { return &_module; } bool IsLoaded() const { return (_module != NULL); }; void Attach(HMODULE m) { Free(); _module = m; } HMODULE Detach() { HMODULE m = _module; _module = NULL; return m; } bool Free() throw(); bool LoadEx(CFSTR path, DWORD flags = LOAD_LIBRARY_AS_DATAFILE) throw(); bool Load(CFSTR path) throw(); FARPROC GetProc(LPCSTR procName) const { return My_GetProcAddress(_module, procName); } }; bool MyGetModuleFileName(FString &path); FString GetModuleDirPrefix(); }} #endif src/libs/7zip/win/CPP/Windows/Defs.h000066400000000000000000000007671325366651500174200ustar00rootroot00000000000000// Windows/Defs.h #ifndef __WINDOWS_DEFS_H #define __WINDOWS_DEFS_H #include "../Common/MyWindows.h" #ifdef _WIN32 inline bool LRESULTToBool(LRESULT v) { return (v != FALSE); } inline bool BOOLToBool(BOOL v) { return (v != FALSE); } inline BOOL BoolToBOOL(bool v) { return (v ? TRUE: FALSE); } #endif inline VARIANT_BOOL BoolToVARIANT_BOOL(bool v) { return (v ? VARIANT_TRUE: VARIANT_FALSE); } inline bool VARIANT_BOOLToBool(VARIANT_BOOL v) { return (v != VARIANT_FALSE); } #endif src/libs/7zip/win/CPP/Windows/FileDir.cpp000066400000000000000000000306631325366651500204060ustar00rootroot00000000000000// Windows/FileDir.cpp #include "StdAfx.h" #ifndef _UNICODE #include "../Common/StringConvert.h" #endif #include "FileDir.h" #include "FileFind.h" #include "FileName.h" #ifndef _UNICODE extern bool g_IsNT; #endif using namespace NWindows; using namespace NFile; using namespace NName; namespace NWindows { namespace NFile { namespace NDir { #ifndef UNDER_CE bool GetWindowsDir(FString &path) { UINT needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetWindowsDirectory(s, MAX_PATH + 1); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } bool GetSystemDir(FString &path) { UINT needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetSystemDirectory(s, MAX_PATH + 1); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } #endif bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) { #ifndef _UNICODE if (!g_IsNT) { ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return false; } #endif HANDLE hDir = INVALID_HANDLE_VALUE; IF_USE_MAIN_PATH hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); #ifdef WIN_LONG_PATH if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) hDir = ::CreateFileW(longPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); } #endif bool res = false; if (hDir != INVALID_HANDLE_VALUE) { res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime)); ::CloseHandle(hDir); } return res; } bool SetFileAttrib(CFSTR path, DWORD attrib) { #ifndef _UNICODE if (!g_IsNT) { if (::SetFileAttributes(fs2fas(path), attrib)) return true; } else #endif { IF_USE_MAIN_PATH if (::SetFileAttributesW(fs2us(path), attrib)) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::SetFileAttributesW(longPath, attrib)); } #endif } return false; } bool RemoveDir(CFSTR path) { #ifndef _UNICODE if (!g_IsNT) { if (::RemoveDirectory(fs2fas(path))) return true; } else #endif { IF_USE_MAIN_PATH if (::RemoveDirectoryW(fs2us(path))) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::RemoveDirectoryW(longPath)); } #endif } return false; } bool MyMoveFile(CFSTR oldFile, CFSTR newFile) { #ifndef _UNICODE if (!g_IsNT) { if (::MoveFile(fs2fas(oldFile), fs2fas(newFile))) return true; } else #endif { IF_USE_MAIN_PATH_2(oldFile, newFile) if (::MoveFileW(fs2us(oldFile), fs2us(newFile))) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH_2) { UString d1, d2; if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2)) return BOOLToBool(::MoveFileW(d1, d2)); } #endif } return false; } #ifndef UNDER_CE EXTERN_C_BEGIN typedef BOOL (WINAPI *Func_CreateHardLinkW)( LPCWSTR lpFileName, LPCWSTR lpExistingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes ); EXTERN_C_END bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName) { #ifndef _UNICODE if (!g_IsNT) { SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return false; /* if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL)) return true; */ } else #endif { Func_CreateHardLinkW my_CreateHardLinkW = (Func_CreateHardLinkW) ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"); if (!my_CreateHardLinkW) return false; IF_USE_MAIN_PATH_2(newFileName, existFileName) if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL)) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH_2) { UString d1, d2; if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2)) return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL)); } #endif } return false; } #endif bool CreateDir(CFSTR path) { #ifndef _UNICODE if (!g_IsNT) { if (::CreateDirectory(fs2fas(path), NULL)) return true; } else #endif { IF_USE_MAIN_PATH if (::CreateDirectoryW(fs2us(path), NULL)) return true; #ifdef WIN_LONG_PATH if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::CreateDirectoryW(longPath, NULL)); } #endif } return false; } bool CreateComplexDir(CFSTR _aPathName) { FString pathName = _aPathName; int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos > 0 && (unsigned)pos == pathName.Len() - 1) { if (pathName.Len() == 3 && pathName[1] == L':') return true; // Disk folder; pathName.Delete(pos); } const FString pathName2 = pathName; pos = pathName.Len(); for (;;) { if (CreateDir(pathName)) break; if (::GetLastError() == ERROR_ALREADY_EXISTS) { NFind::CFileInfo fileInfo; if (!fileInfo.Find(pathName)) // For network folders return true; if (!fileInfo.IsDir()) return false; break; } pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos < 0 || pos == 0) return false; if (pathName[pos - 1] == L':') return false; pathName.DeleteFrom(pos); } while (pos < (int)pathName2.Len()) { pos = pathName2.Find(FCHAR_PATH_SEPARATOR, pos + 1); if (pos < 0) pos = pathName2.Len(); pathName.SetFrom(pathName2, pos); if (!CreateDir(pathName)) return false; } return true; } bool DeleteFileAlways(CFSTR path) { if (!SetFileAttrib(path, 0)) return false; #ifndef _UNICODE if (!g_IsNT) { if (::DeleteFile(fs2fas(path))) return true; } else #endif { IF_USE_MAIN_PATH if (::DeleteFileW(fs2us(path))) return true; #ifdef WIN_LONG_PATH if (USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) return BOOLToBool(::DeleteFileW(longPath)); } #endif } return false; } bool RemoveDirWithSubItems(const FString &path) { bool needRemoveSubItems = true; { NFind::CFileInfo fi; if (!fi.Find(path)) return false; if (!fi.IsDir()) { ::SetLastError(ERROR_DIRECTORY); return false; } if (fi.HasReparsePoint()) needRemoveSubItems = false; } if (needRemoveSubItems) { FString s = path; s += FCHAR_PATH_SEPARATOR; unsigned prefixSize = s.Len(); s += FCHAR_ANY_MASK; NFind::CEnumerator enumerator(s); NFind::CFileInfo fi; while (enumerator.Next(fi)) { s.DeleteFrom(prefixSize); s += fi.Name; if (fi.IsDir()) { if (!RemoveDirWithSubItems(s)) return false; } else if (!DeleteFileAlways(s)) return false; } } if (!SetFileAttrib(path, 0)) return false; return RemoveDir(path); } #ifdef UNDER_CE bool MyGetFullPathName(CFSTR path, FString &resFullPath) { resFullPath = path; return true; } #else bool MyGetFullPathName(CFSTR path, FString &resFullPath) { return GetFullPath(path, resFullPath); } bool SetCurrentDir(CFSTR path) { // SetCurrentDirectory doesn't support \\?\ prefix #ifndef _UNICODE if (!g_IsNT) { return BOOLToBool(::SetCurrentDirectory(fs2fas(path))); } else #endif { return BOOLToBool(::SetCurrentDirectoryW(fs2us(path))); } } bool GetCurrentDir(FString &path) { path.Empty(); DWORD needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetCurrentDirectory(MAX_PATH + 1, s); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s); path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } #endif bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName) { bool res = MyGetFullPathName(path, resDirPrefix); if (!res) resDirPrefix = path; int pos = resDirPrefix.ReverseFind(FCHAR_PATH_SEPARATOR); resFileName = resDirPrefix.Ptr(pos + 1); resDirPrefix.DeleteFrom(pos + 1); return res; } bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix) { FString resFileName; return GetFullPathAndSplit(path, resDirPrefix, resFileName); } bool MyGetTempPath(FString &path) { path.Empty(); DWORD needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetTempPath(MAX_PATH + 1, s); path = fas2fs(s); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetTempPathW(MAX_PATH + 1, s);; path = us2fs(s); } return (needLength > 0 && needLength <= MAX_PATH); } static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile) { UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId(); for (unsigned i = 0; i < 100; i++) { path = prefix; if (addRandom) { FChar s[16]; UInt32 value = d; unsigned k; for (k = 0; k < 8; k++) { unsigned t = value & 0xF; value >>= 4; s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); } s[k] = '\0'; if (outFile) path += FChar('.'); path += s; UInt32 step = GetTickCount() + 2; if (step == 0) step = 1; d += step; } addRandom = true; if (outFile) path += FTEXT(".tmp"); if (NFind::DoesFileOrDirExist(path)) { SetLastError(ERROR_ALREADY_EXISTS); continue; } if (outFile) { if (outFile->Create(path, false)) return true; } else { if (CreateDir(path)) return true; } DWORD error = GetLastError(); if (error != ERROR_FILE_EXISTS && error != ERROR_ALREADY_EXISTS) break; } path.Empty(); return false; } bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile) { if (!Remove()) return false; if (!CreateTempFile(prefix, false, _path, outFile)) return false; _mustBeDeleted = true; return true; } bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile) { if (!Remove()) return false; FString tempPath; if (!MyGetTempPath(tempPath)) return false; if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile)) return false; _mustBeDeleted = true; return true; } bool CTempFile::Remove() { if (!_mustBeDeleted) return true; _mustBeDeleted = !DeleteFileAlways(_path); return !_mustBeDeleted; } bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore) { if (deleteDestBefore) if (NFind::DoesFileExist(name)) if (!DeleteFileAlways(name)) return false; DisableDeleting(); return MyMoveFile(_path, name); } bool CTempDir::Create(CFSTR prefix) { if (!Remove()) return false; FString tempPath; if (!MyGetTempPath(tempPath)) return false; if (!CreateTempFile(tempPath + prefix, true, _path, NULL)) return false; _mustBeDeleted = true; return true; } bool CTempDir::Remove() { if (!_mustBeDeleted) return true; _mustBeDeleted = !RemoveDirWithSubItems(_path); return !_mustBeDeleted; } }}} src/libs/7zip/win/CPP/Windows/FileDir.h000066400000000000000000000043671325366651500200550ustar00rootroot00000000000000// Windows/FileDir.h #ifndef __WINDOWS_FILE_DIR_H #define __WINDOWS_FILE_DIR_H #include "../Common/MyString.h" #include "FileIO.h" namespace NWindows { namespace NFile { namespace NDir { bool GetWindowsDir(FString &path); bool GetSystemDir(FString &path); bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime); bool SetFileAttrib(CFSTR path, DWORD attrib); bool MyMoveFile(CFSTR existFileName, CFSTR newFileName); #ifndef UNDER_CE bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName); #endif bool RemoveDir(CFSTR path); bool CreateDir(CFSTR path); bool CreateComplexDir(CFSTR path); bool DeleteFileAlways(CFSTR name); bool RemoveDirWithSubItems(const FString &path); bool MyGetFullPathName(CFSTR path, FString &resFullPath); bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName); bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix); #ifndef UNDER_CE bool SetCurrentDir(CFSTR path); bool GetCurrentDir(FString &resultPath); #endif bool MyGetTempPath(FString &resultPath); class CTempFile { bool _mustBeDeleted; FString _path; void DisableDeleting() { _mustBeDeleted = false; } public: CTempFile(): _mustBeDeleted(false) {} ~CTempFile() { Remove(); } const FString &GetPath() const { return _path; } bool Create(CFSTR pathPrefix, NIO::COutFile *outFile); // pathPrefix is not folder prefix bool CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile); bool Remove(); bool MoveTo(CFSTR name, bool deleteDestBefore); }; class CTempDir { bool _mustBeDeleted; FString _path; public: CTempDir(): _mustBeDeleted(false) {} ~CTempDir() { Remove(); } const FString &GetPath() const { return _path; } void DisableDeleting() { _mustBeDeleted = false; } bool Create(CFSTR namePrefix) ; bool Remove(); }; #if !defined(UNDER_CE) class CCurrentDirRestorer { FString _path; public: bool NeedRestore; CCurrentDirRestorer(): NeedRestore(true) { GetCurrentDir(_path); } ~CCurrentDirRestorer() { if (!NeedRestore) return; FString s; if (GetCurrentDir(s)) if (s != _path) SetCurrentDir(_path); } }; #endif }}} #endif src/libs/7zip/win/CPP/Windows/FileFind.cpp000066400000000000000000000322441325366651500205450ustar00rootroot00000000000000// Windows/FileFind.cpp #include "StdAfx.h" #include "FileFind.h" #include "FileIO.h" #include "FileName.h" #ifndef _UNICODE #include "../Common/StringConvert.h" #endif #ifndef _UNICODE extern bool g_IsNT; #endif using namespace NWindows; using namespace NFile; using namespace NName; #if defined(_WIN32) && !defined(UNDER_CE) EXTERN_C_BEGIN typedef enum { My_FindStreamInfoStandard, My_FindStreamInfoMaxInfoLevel } MY_STREAM_INFO_LEVELS; typedef struct { LARGE_INTEGER StreamSize; WCHAR cStreamName[MAX_PATH + 36]; } MY_WIN32_FIND_STREAM_DATA, *MY_PWIN32_FIND_STREAM_DATA; typedef WINBASEAPI HANDLE (WINAPI *FindFirstStreamW_Ptr)(LPCWSTR fileName, MY_STREAM_INFO_LEVELS infoLevel, LPVOID findStreamData, DWORD flags); typedef WINBASEAPI BOOL (APIENTRY *FindNextStreamW_Ptr)(HANDLE findStream, LPVOID findStreamData); EXTERN_C_END #endif namespace NWindows { namespace NFile { #ifdef SUPPORT_DEVICE_FILE namespace NSystem { bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); } #endif namespace NFind { bool CFileInfo::IsDots() const throw() { if (!IsDir() || Name.IsEmpty()) return false; if (Name[0] != FTEXT('.')) return false; return Name.Len() == 1 || (Name.Len() == 2 && Name[1] == FTEXT('.')); } #define WIN_FD_TO_MY_FI(fi, fd) \ fi.Attrib = fd.dwFileAttributes; \ fi.CTime = fd.ftCreationTime; \ fi.ATime = fd.ftLastAccessTime; \ fi.MTime = fd.ftLastWriteTime; \ fi.Size = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \ fi.IsAltStream = false; \ fi.IsDevice = false; /* #ifdef UNDER_CE fi.ObjectID = fd.dwOID; #else fi.ReparseTag = fd.dwReserved0; #endif */ static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATAW &fd, CFileInfo &fi) { WIN_FD_TO_MY_FI(fi, fd); fi.Name = us2fs(fd.cFileName); #if defined(_WIN32) && !defined(UNDER_CE) // fi.ShortName = us2fs(fd.cAlternateFileName); #endif } #ifndef _UNICODE static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi) { WIN_FD_TO_MY_FI(fi, fd); fi.Name = fas2fs(fd.cFileName); #if defined(_WIN32) && !defined(UNDER_CE) // fi.ShortName = fas2fs(fd.cAlternateFileName); #endif } #endif //////////////////////////////// // CFindFile bool CFindFileBase::Close() throw() { if (_handle == INVALID_HANDLE_VALUE) return true; if (!::FindClose(_handle)) return false; _handle = INVALID_HANDLE_VALUE; return true; } bool CFindFile::FindFirst(CFSTR path, CFileInfo &fi) { if (!Close()) return false; #ifndef _UNICODE if (!g_IsNT) { WIN32_FIND_DATAA fd; _handle = ::FindFirstFileA(fs2fas(path), &fd); if (_handle == INVALID_HANDLE_VALUE) return false; Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi); } else #endif { WIN32_FIND_DATAW fd; IF_USE_MAIN_PATH _handle = ::FindFirstFileW(fs2us(path), &fd); #ifdef WIN_LONG_PATH if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) _handle = ::FindFirstFileW(longPath, &fd); } #endif if (_handle == INVALID_HANDLE_VALUE) return false; Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi); } return true; } bool CFindFile::FindNext(CFileInfo &fi) { #ifndef _UNICODE if (!g_IsNT) { WIN32_FIND_DATAA fd; if (!::FindNextFileA(_handle, &fd)) return false; Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi); } else #endif { WIN32_FIND_DATAW fd; if (!::FindNextFileW(_handle, &fd)) return false; Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi); } return true; } #if defined(_WIN32) && !defined(UNDER_CE) //////////////////////////////// // AltStreams static FindFirstStreamW_Ptr g_FindFirstStreamW; static FindNextStreamW_Ptr g_FindNextStreamW; struct CFindStreamLoader { CFindStreamLoader() { g_FindFirstStreamW = (FindFirstStreamW_Ptr)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "FindFirstStreamW"); g_FindNextStreamW = (FindNextStreamW_Ptr)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "FindNextStreamW"); } } g_FindStreamLoader; bool CStreamInfo::IsMainStream() const throw() { return Name == L"::$DATA"; }; UString CStreamInfo::GetReducedName() const { UString s = Name; if (s.Len() >= 6) if (wcscmp(s.RightPtr(6), L":$DATA") == 0) s.DeleteFrom(s.Len() - 6); return s; } static void Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(const MY_WIN32_FIND_STREAM_DATA &sd, CStreamInfo &si) { si.Size = sd.StreamSize.QuadPart; si.Name = sd.cStreamName; } bool CFindStream::FindFirst(CFSTR path, CStreamInfo &si) { if (!Close()) return false; if (!g_FindFirstStreamW) { ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return false; } { MY_WIN32_FIND_STREAM_DATA sd; IF_USE_MAIN_PATH _handle = g_FindFirstStreamW(fs2us(path), My_FindStreamInfoStandard, &sd, 0); if (_handle == INVALID_HANDLE_VALUE) { if (::GetLastError() == ERROR_HANDLE_EOF) return false; // long name can be tricky for path like ".\dirName". #ifdef WIN_LONG_PATH if (USE_SUPER_PATH) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) _handle = g_FindFirstStreamW(longPath, My_FindStreamInfoStandard, &sd, 0); } #endif } if (_handle == INVALID_HANDLE_VALUE) return false; Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si); } return true; } bool CFindStream::FindNext(CStreamInfo &si) { if (!g_FindNextStreamW) { ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return false; } { MY_WIN32_FIND_STREAM_DATA sd; if (!g_FindNextStreamW(_handle, &sd)) return false; Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si); } return true; } bool CStreamEnumerator::Next(CStreamInfo &si, bool &found) { bool res; if (_find.IsHandleAllocated()) res = _find.FindNext(si); else res = _find.FindFirst(_filePath, si); if (res) { found = true; return true; } found = false; return (::GetLastError() == ERROR_HANDLE_EOF); } #endif #define MY_CLEAR_FILETIME(ft) ft.dwLowDateTime = ft.dwHighDateTime = 0; void CFileInfoBase::Clear() throw() { Size = 0; MY_CLEAR_FILETIME(CTime); MY_CLEAR_FILETIME(ATime); MY_CLEAR_FILETIME(MTime); Attrib = 0; IsAltStream = false; IsDevice = false; } #if defined(_WIN32) && !defined(UNDER_CE) static int FindAltStreamColon(CFSTR path) { for (int i = 0;; i++) { FChar c = path[i]; if (c == 0) return -1; if (c == ':') { if (path[i + 1] == '\\') if (i == 1 || (i > 1 && path[i - 2] == '\\')) { wchar_t c0 = path[i - 1]; if (c0 >= 'a' && c0 <= 'z' || c0 >= 'A' && c0 <= 'Z') continue; } return i; } } } #endif bool CFileInfo::Find(CFSTR path) { #ifdef SUPPORT_DEVICE_FILE if (IsDevicePath(path)) { Clear(); Name = path + 4; IsDevice = true; if (/* path[0] == '\\' && path[1] == '\\' && path[2] == '.' && path[3] == '\\' && */ path[5] == ':' && path[6] == 0) { FChar drive[4] = { path[4], ':', '\\', 0 }; UInt64 clusterSize, totalSize, freeSize; if (NSystem::MyGetDiskFreeSpace(drive, clusterSize, totalSize, freeSize)) { Size = totalSize; return true; } } NIO::CInFile inFile; // ::OutputDebugStringW(path); if (!inFile.Open(path)) return false; // ::OutputDebugStringW(L"---"); if (inFile.SizeDefined) Size = inFile.Size; return true; } #endif #if defined(_WIN32) && !defined(UNDER_CE) int colonPos = FindAltStreamColon(path); if (colonPos >= 0) { UString streamName = fs2us(path + (unsigned)colonPos); FString filePath = path; filePath.DeleteFrom(colonPos); streamName += L":$DATA"; // change it!!!! if (Find(filePath)) { // if (IsDir()) Attrib &= ~FILE_ATTRIBUTE_DIRECTORY; Size = 0; CStreamEnumerator enumerator(filePath); for (;;) { CStreamInfo si; bool found; if (!enumerator.Next(si, found)) return false; if (!found) { ::SetLastError(ERROR_FILE_NOT_FOUND); return false; } if (si.Name.IsEqualToNoCase(streamName)) { Name += us2fs(si.Name); Name.DeleteFrom(Name.Len() - 6); Size = si.Size; IsAltStream = true; return true; } } } } #endif CFindFile finder; if (finder.FindFirst(path, *this)) return true; #ifdef _WIN32 { DWORD lastError = GetLastError(); if (lastError == ERROR_BAD_NETPATH || lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_INVALID_NAME // for "\\SERVER\shared" paths that are translated to "\\?\UNC\SERVER\shared" ) { unsigned len = MyStringLen(path); if (len > 2 && path[0] == '\\' && path[1] == '\\') { int startPos = 2; if (len > kSuperUncPathPrefixSize && IsSuperUncPath(path)) startPos = kSuperUncPathPrefixSize; int pos = FindCharPosInString(path + startPos, FTEXT('\\')); if (pos >= 0) { pos += startPos + 1; len -= pos; int pos2 = FindCharPosInString(path + pos, FTEXT('\\')); if (pos2 < 0 || pos2 == (int)len - 1) { FString s = path; if (pos2 < 0) { pos2 = len; s += FTEXT('\\'); } s += FCHAR_ANY_MASK; if (finder.FindFirst(s, *this)) if (Name == FTEXT(".")) { Name.SetFrom(s.Ptr(pos), pos2); return true; } ::SetLastError(lastError); } } } } } #endif return false; } bool DoesFileExist(CFSTR name) { CFileInfo fi; return fi.Find(name) && !fi.IsDir(); } bool DoesDirExist(CFSTR name) { CFileInfo fi; return fi.Find(name) && fi.IsDir(); } bool DoesFileOrDirExist(CFSTR name) { CFileInfo fi; return fi.Find(name); } bool CEnumerator::NextAny(CFileInfo &fi) { if (_findFile.IsHandleAllocated()) return _findFile.FindNext(fi); else return _findFile.FindFirst(_wildcard, fi); } bool CEnumerator::Next(CFileInfo &fi) { for (;;) { if (!NextAny(fi)) return false; if (!fi.IsDots()) return true; } } bool CEnumerator::Next(CFileInfo &fi, bool &found) { if (Next(fi)) { found = true; return true; } found = false; return (::GetLastError() == ERROR_NO_MORE_FILES); } //////////////////////////////// // CFindChangeNotification // FindFirstChangeNotification can return 0. MSDN doesn't tell about it. bool CFindChangeNotification::Close() throw() { if (!IsHandleAllocated()) return true; if (!::FindCloseChangeNotification(_handle)) return false; _handle = INVALID_HANDLE_VALUE; return true; } HANDLE CFindChangeNotification::FindFirst(CFSTR path, bool watchSubtree, DWORD notifyFilter) { #ifndef _UNICODE if (!g_IsNT) _handle = ::FindFirstChangeNotification(fs2fas(path), BoolToBOOL(watchSubtree), notifyFilter); else #endif { IF_USE_MAIN_PATH _handle = ::FindFirstChangeNotificationW(fs2us(path), BoolToBOOL(watchSubtree), notifyFilter); #ifdef WIN_LONG_PATH if (!IsHandleAllocated()) { UString longPath; if (GetSuperPath(path, longPath, USE_MAIN_PATH)) _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter); } #endif } return _handle; } #ifndef UNDER_CE bool MyGetLogicalDriveStrings(CObjectVector &driveStrings) { driveStrings.Clear(); #ifndef _UNICODE if (!g_IsNT) { driveStrings.Clear(); UINT32 size = GetLogicalDriveStrings(0, NULL); if (size == 0) return false; AString buf; UINT32 newSize = GetLogicalDriveStrings(size, buf.GetBuffer(size)); if (newSize == 0 || newSize > size) return false; AString s; for (UINT32 i = 0; i < newSize; i++) { char c = buf[i]; if (c == '\0') { driveStrings.Add(fas2fs(s)); s.Empty(); } else s += c; } return s.IsEmpty(); } else #endif { UINT32 size = GetLogicalDriveStringsW(0, NULL); if (size == 0) return false; UString buf; UINT32 newSize = GetLogicalDriveStringsW(size, buf.GetBuffer(size)); if (newSize == 0 || newSize > size) return false; UString s; for (UINT32 i = 0; i < newSize; i++) { WCHAR c = buf[i]; if (c == L'\0') { driveStrings.Add(us2fs(s)); s.Empty(); } else s += c; } return s.IsEmpty(); } } #endif }}} src/libs/7zip/win/CPP/Windows/FileFind.h000066400000000000000000000105351325366651500202110ustar00rootroot00000000000000// Windows/FileFind.h #ifndef __WINDOWS_FILE_FIND_H #define __WINDOWS_FILE_FIND_H #include "../Common/MyString.h" #include "Defs.h" namespace NWindows { namespace NFile { namespace NFind { namespace NAttributes { inline bool IsReadOnly(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_READONLY) != 0; } inline bool IsHidden(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_HIDDEN) != 0; } inline bool IsSystem(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_SYSTEM) != 0; } inline bool IsDir(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; } inline bool IsArchived(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_ARCHIVE) != 0; } inline bool IsCompressed(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_COMPRESSED) != 0; } inline bool IsEncrypted(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_ENCRYPTED) != 0; } } class CFileInfoBase { bool MatchesMask(UINT32 mask) const { return ((Attrib & mask) != 0); } public: UInt64 Size; FILETIME CTime; FILETIME ATime; FILETIME MTime; DWORD Attrib; bool IsAltStream; bool IsDevice; /* #ifdef UNDER_CE DWORD ObjectID; #else UINT32 ReparseTag; #endif */ CFileInfoBase() { Clear(); } void Clear() throw(); void SetAsDir() { Attrib = FILE_ATTRIBUTE_DIRECTORY; } bool IsArchived() const { return MatchesMask(FILE_ATTRIBUTE_ARCHIVE); } bool IsCompressed() const { return MatchesMask(FILE_ATTRIBUTE_COMPRESSED); } bool IsDir() const { return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); } bool IsEncrypted() const { return MatchesMask(FILE_ATTRIBUTE_ENCRYPTED); } bool IsHidden() const { return MatchesMask(FILE_ATTRIBUTE_HIDDEN); } bool IsNormal() const { return MatchesMask(FILE_ATTRIBUTE_NORMAL); } bool IsOffline() const { return MatchesMask(FILE_ATTRIBUTE_OFFLINE); } bool IsReadOnly() const { return MatchesMask(FILE_ATTRIBUTE_READONLY); } bool HasReparsePoint() const { return MatchesMask(FILE_ATTRIBUTE_REPARSE_POINT); } bool IsSparse() const { return MatchesMask(FILE_ATTRIBUTE_SPARSE_FILE); } bool IsSystem() const { return MatchesMask(FILE_ATTRIBUTE_SYSTEM); } bool IsTemporary() const { return MatchesMask(FILE_ATTRIBUTE_TEMPORARY); } }; struct CFileInfo: public CFileInfoBase { FString Name; #if defined(_WIN32) && !defined(UNDER_CE) // FString ShortName; #endif bool IsDots() const throw(); bool Find(CFSTR wildcard); }; class CFindFileBase { protected: HANDLE _handle; public: bool IsHandleAllocated() const { return _handle != INVALID_HANDLE_VALUE; } CFindFileBase(): _handle(INVALID_HANDLE_VALUE) {} ~CFindFileBase() { Close(); } bool Close() throw(); }; class CFindFile: public CFindFileBase { public: bool FindFirst(CFSTR wildcard, CFileInfo &fileInfo); bool FindNext(CFileInfo &fileInfo); }; #if defined(_WIN32) && !defined(UNDER_CE) struct CStreamInfo { UString Name; UInt64 Size; UString GetReducedName() const; bool IsMainStream() const throw(); }; class CFindStream: public CFindFileBase { public: bool FindFirst(CFSTR filePath, CStreamInfo &streamInfo); bool FindNext(CStreamInfo &streamInfo); }; class CStreamEnumerator { CFindStream _find; FString _filePath; bool NextAny(CFileInfo &fileInfo); public: CStreamEnumerator(const FString &filePath): _filePath(filePath) {} bool Next(CStreamInfo &streamInfo, bool &found); }; #endif bool DoesFileExist(CFSTR name); bool DoesDirExist(CFSTR name); bool DoesFileOrDirExist(CFSTR name); class CEnumerator { CFindFile _findFile; FString _wildcard; bool NextAny(CFileInfo &fileInfo); public: CEnumerator(const FString &wildcard): _wildcard(wildcard) {} bool Next(CFileInfo &fileInfo); bool Next(CFileInfo &fileInfo, bool &found); }; class CFindChangeNotification { HANDLE _handle; public: operator HANDLE () { return _handle; } bool IsHandleAllocated() const { return _handle != INVALID_HANDLE_VALUE && _handle != 0; } CFindChangeNotification(): _handle(INVALID_HANDLE_VALUE) {} ~CFindChangeNotification() { Close(); } bool Close() throw(); HANDLE FindFirst(CFSTR pathName, bool watchSubtree, DWORD notifyFilter); bool FindNext() { return BOOLToBool(::FindNextChangeNotification(_handle)); } }; #ifndef UNDER_CE bool MyGetLogicalDriveStrings(CObjectVector &driveStrings); #endif }}} #endif src/libs/7zip/win/CPP/Windows/FileIO.cpp000066400000000000000000000274321325366651500201770ustar00rootroot00000000000000// Windows/FileIO.cpp #include "StdAfx.h" #ifdef SUPPORT_DEVICE_FILE #include "../../C/Alloc.h" #endif #include "FileIO.h" #include "FileName.h" #ifndef _UNICODE extern bool g_IsNT; #endif using namespace NWindows; using namespace NFile; using namespace NName; namespace NWindows { namespace NFile { #ifdef SUPPORT_DEVICE_FILE namespace NSystem { bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); } #endif namespace NIO { /* WinXP-64 CreateFile(): "" - ERROR_PATH_NOT_FOUND :stream - OK .:stream - ERROR_PATH_NOT_FOUND .\:stream - OK folder\:stream - ERROR_INVALID_NAME folder:stream - OK c:\:stream - OK c::stream - ERROR_INVALID_NAME, if current dir is NOT ROOT ( c:\dir1 ) c::stream - OK, if current dir is ROOT ( c:\ ) */ bool CFileBase::Create(CFSTR path, DWORD desiredAccess, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) { if (!Close()) return false; #ifdef SUPPORT_DEVICE_FILE IsDeviceFile = false; #endif #ifndef _UNICODE if (!g_IsNT) { _handle = ::CreateFile(fs2fas(path), desiredAccess, shareMode, (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); } else #endif { IF_USE_MAIN_PATH _handle = ::CreateFileW(fs2us(path), desiredAccess, shareMode, (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); #ifdef WIN_LONG_PATH if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH) { UString superPath; if (GetSuperPath(path, superPath, USE_MAIN_PATH)) _handle = ::CreateFileW(superPath, desiredAccess, shareMode, (LPSECURITY_ATTRIBUTES)NULL, creationDisposition, flagsAndAttributes, (HANDLE)NULL); } #endif } return (_handle != INVALID_HANDLE_VALUE); } bool CFileBase::Close() throw() { if (_handle == INVALID_HANDLE_VALUE) return true; if (!::CloseHandle(_handle)) return false; _handle = INVALID_HANDLE_VALUE; return true; } bool CFileBase::GetPosition(UInt64 &position) const throw() { return Seek(0, FILE_CURRENT, position); } bool CFileBase::GetLength(UInt64 &length) const throw() { #ifdef SUPPORT_DEVICE_FILE if (IsDeviceFile && SizeDefined) { length = Size; return true; } #endif DWORD sizeHigh; DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh); if (sizeLow == 0xFFFFFFFF) if (::GetLastError() != NO_ERROR) return false; length = (((UInt64)sizeHigh) << 32) + sizeLow; return true; } bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const throw() { #ifdef SUPPORT_DEVICE_FILE if (IsDeviceFile && SizeDefined && moveMethod == FILE_END) { distanceToMove += Size; moveMethod = FILE_BEGIN; } #endif LONG high = (LONG)(distanceToMove >> 32); DWORD low = ::SetFilePointer(_handle, (LONG)(distanceToMove & 0xFFFFFFFF), &high, moveMethod); if (low == 0xFFFFFFFF) if (::GetLastError() != NO_ERROR) return false; newPosition = (((UInt64)high) << 32) + low; return true; } bool CFileBase::Seek(UInt64 position, UInt64 &newPosition) const throw() { return Seek(position, FILE_BEGIN, newPosition); } bool CFileBase::SeekToBegin() const throw() { UInt64 newPosition; return Seek(0, newPosition); } bool CFileBase::SeekToEnd(UInt64 &newPosition) const throw() { return Seek(0, FILE_END, newPosition); } // ---------- CInFile --------- #ifdef SUPPORT_DEVICE_FILE void CInFile::CorrectDeviceSize() { // maybe we must decrease kClusterSize to 1 << 12, if we want correct size at tail static const UInt32 kClusterSize = 1 << 14; UInt64 pos = Size & ~(UInt64)(kClusterSize - 1); UInt64 realNewPosition; if (!Seek(pos, realNewPosition)) return; Byte *buf = (Byte *)MidAlloc(kClusterSize); bool needbackward = true; for (;;) { UInt32 processed = 0; // up test is slow for "PhysicalDrive". // processed size for latest block for "PhysicalDrive0" is 0. if (!Read1(buf, kClusterSize, processed)) break; if (processed == 0) break; needbackward = false; Size = pos + processed; if (processed != kClusterSize) break; pos += kClusterSize; } if (needbackward && pos != 0) { pos -= kClusterSize; for (;;) { // break; if (!Seek(pos, realNewPosition)) break; if (!buf) { buf = (Byte *)MidAlloc(kClusterSize); if (!buf) break; } UInt32 processed = 0; // that code doesn't work for "PhysicalDrive0" if (!Read1(buf, kClusterSize, processed)) break; if (processed != 0) { Size = pos + processed; break; } if (pos == 0) break; pos -= kClusterSize; } } MidFree(buf); } void CInFile::CalcDeviceSize(CFSTR s) { SizeDefined = false; Size = 0; if (_handle == INVALID_HANDLE_VALUE || !IsDeviceFile) return; #ifdef UNDER_CE SizeDefined = true; Size = 128 << 20; #else PARTITION_INFORMATION partInfo; bool needCorrectSize = true; /* WinXP 64-bit: HDD \\.\PhysicalDrive0 (MBR): GetPartitionInfo == GeometryEx : corrrect size? (includes tail) Geometry : smaller than GeometryEx (no tail, maybe correct too?) MyGetDiskFreeSpace : FAIL Size correction is slow and block size (kClusterSize) must be small? HDD partition \\.\N: (NTFS): MyGetDiskFreeSpace : Size of NTFS clusters. Same size can be calculated after correction GetPartitionInfo : size of partition data: NTFS clusters + TAIL; TAIL contains extra empty sectors and copy of first sector of NTFS Geometry / CdRomGeometry / GeometryEx : size of HDD (not that partition) CD-ROM drive (ISO): MyGetDiskFreeSpace : correct size. Same size can be calculated after correction Geometry == CdRomGeometry : smaller than corrrect size GetPartitionInfo == GeometryEx : larger than corrrect size Floppy \\.\a: (FAT): Geometry : correct size. CdRomGeometry / GeometryEx / GetPartitionInfo / MyGetDiskFreeSpace - FAIL correction works OK for FAT. correction works OK for non-FAT, if kClusterSize = 512. */ if (GetPartitionInfo(&partInfo)) { Size = partInfo.PartitionLength.QuadPart; SizeDefined = true; needCorrectSize = false; if ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\' && (s)[5] == ':' && (s)[6] == 0) { FChar path[4] = { s[4], ':', '\\', 0 }; UInt64 clusterSize, totalSize, freeSize; if (NSystem::MyGetDiskFreeSpace(path, clusterSize, totalSize, freeSize)) Size = totalSize; else needCorrectSize = true; } } if (!SizeDefined) { my_DISK_GEOMETRY_EX geomEx; SizeDefined = GetGeometryEx(&geomEx); if (SizeDefined) Size = geomEx.DiskSize.QuadPart; else { DISK_GEOMETRY geom; SizeDefined = GetGeometry(&geom); if (!SizeDefined) SizeDefined = GetCdRomGeometry(&geom); if (SizeDefined) Size = geom.Cylinders.QuadPart * geom.TracksPerCylinder * geom.SectorsPerTrack * geom.BytesPerSector; } } if (needCorrectSize && SizeDefined && Size != 0) { CorrectDeviceSize(); SeekToBegin(); } // SeekToBegin(); #endif } // ((desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE)) == 0 && #define MY_DEVICE_EXTRA_CODE \ IsDeviceFile = IsDevicePath(fileName); \ CalcDeviceSize(fileName); #else #define MY_DEVICE_EXTRA_CODE #endif bool CInFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) { bool res = Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); MY_DEVICE_EXTRA_CODE return res; } bool CInFile::OpenShared(CFSTR fileName, bool shareForWrite) { return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); } bool CInFile::Open(CFSTR fileName) { return OpenShared(fileName, false); } // ReadFile and WriteFile functions in Windows have BUG: // If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1) // from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES // (Insufficient system resources exist to complete the requested service). // Probably in some version of Windows there are problems with other sizes: // for 32 MB (maybe also for 16 MB). // And message can be "Network connection was lost" static UInt32 kChunkSizeMax = (1 << 22); bool CInFile::Read1(void *data, UInt32 size, UInt32 &processedSize) throw() { DWORD processedLoc = 0; bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL)); processedSize = (UInt32)processedLoc; return res; } bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize) throw() { if (size > kChunkSizeMax) size = kChunkSizeMax; return Read1(data, size, processedSize); } bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize) throw() { processedSize = 0; do { UInt32 processedLoc = 0; bool res = ReadPart(data, size, processedLoc); processedSize += processedLoc; if (!res) return false; if (processedLoc == 0) return true; data = (void *)((unsigned char *)data + processedLoc); size -= processedLoc; } while (size > 0); return true; } // ---------- COutFile --------- static inline DWORD GetCreationDisposition(bool createAlways) { return createAlways? CREATE_ALWAYS: CREATE_NEW; } bool COutFile::Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes) { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); } bool COutFile::Open(CFSTR fileName, DWORD creationDisposition) { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); } bool COutFile::Create(CFSTR fileName, bool createAlways) { return Open(fileName, GetCreationDisposition(createAlways)); } bool COutFile::CreateAlways(CFSTR fileName, DWORD flagsAndAttributes) { return Open(fileName, FILE_SHARE_READ, GetCreationDisposition(true), flagsAndAttributes); } bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw() { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); } bool COutFile::SetMTime(const FILETIME *mTime) throw() { return SetTime(NULL, NULL, mTime); } bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw() { if (size > kChunkSizeMax) size = kChunkSizeMax; DWORD processedLoc = 0; bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL)); processedSize = (UInt32)processedLoc; return res; } bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize) throw() { processedSize = 0; do { UInt32 processedLoc = 0; bool res = WritePart(data, size, processedLoc); processedSize += processedLoc; if (!res) return false; if (processedLoc == 0) return true; data = (const void *)((const unsigned char *)data + processedLoc); size -= processedLoc; } while (size > 0); return true; } bool COutFile::SetEndOfFile() throw() { return BOOLToBool(::SetEndOfFile(_handle)); } bool COutFile::SetLength(UInt64 length) throw() { UInt64 newPosition; if (!Seek(length, newPosition)) return false; if (newPosition != length) return false; return SetEndOfFile(); } }}} src/libs/7zip/win/CPP/Windows/FileIO.h000066400000000000000000000142551325366651500176430ustar00rootroot00000000000000// Windows/FileIO.h #ifndef __WINDOWS_FILE_IO_H #define __WINDOWS_FILE_IO_H #if defined(_WIN32) && !defined(UNDER_CE) #include #endif #include "../Common/MyString.h" #include "../Common/MyBuffer.h" #include "Defs.h" #define _my_IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L) #define _my_IO_REPARSE_TAG_SYMLINK (0xA000000CL) #define _my_SYMLINK_FLAG_RELATIVE 1 #define my_FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // REPARSE_DATA_BUFFER #define my_FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) // REPARSE_DATA_BUFFER namespace NWindows { namespace NFile { #if defined(_WIN32) && !defined(UNDER_CE) bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink); #endif struct CReparseShortInfo { unsigned Offset; unsigned Size; bool Parse(const Byte *p, size_t size); }; struct CReparseAttr { UInt32 Tag; UInt32 Flags; UString SubsName; UString PrintName; CReparseAttr(): Tag(0), Flags(0) {} bool Parse(const Byte *p, size_t size); bool IsMountPoint() const { return Tag == _my_IO_REPARSE_TAG_MOUNT_POINT; } // it's Junction bool IsSymLink() const { return Tag == _my_IO_REPARSE_TAG_SYMLINK; } bool IsRelative() const { return Flags == _my_SYMLINK_FLAG_RELATIVE; } // bool IsVolume() const; bool IsOkNamePair() const; UString GetPath() const; }; namespace NIO { bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMATION *fileInfo = NULL); bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size); class CFileBase { protected: HANDLE _handle; bool Create(CFSTR path, DWORD desiredAccess, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); public: bool DeviceIoControl(DWORD controlCode, LPVOID inBuffer, DWORD inSize, LPVOID outBuffer, DWORD outSize, LPDWORD bytesReturned, LPOVERLAPPED overlapped = NULL) const { return BOOLToBool(::DeviceIoControl(_handle, controlCode, inBuffer, inSize, outBuffer, outSize, bytesReturned, overlapped)); } bool DeviceIoControlOut(DWORD controlCode, LPVOID outBuffer, DWORD outSize, LPDWORD bytesReturned) const { return DeviceIoControl(controlCode, NULL, 0, outBuffer, outSize, bytesReturned); } bool DeviceIoControlOut(DWORD controlCode, LPVOID outBuffer, DWORD outSize) const { DWORD bytesReturned; return DeviceIoControlOut(controlCode, outBuffer, outSize, &bytesReturned); } public: #ifdef SUPPORT_DEVICE_FILE bool IsDeviceFile; bool SizeDefined; UInt64 Size; // it can be larger than real available size #endif CFileBase(): _handle(INVALID_HANDLE_VALUE) {}; ~CFileBase() { Close(); } bool Close() throw(); bool GetPosition(UInt64 &position) const throw(); bool GetLength(UInt64 &length) const throw(); bool Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const throw(); bool Seek(UInt64 position, UInt64 &newPosition) const throw(); bool SeekToBegin() const throw(); bool SeekToEnd(UInt64 &newPosition) const throw(); bool GetFileInformation(BY_HANDLE_FILE_INFORMATION *info) const { return BOOLToBool(GetFileInformationByHandle(_handle, info)); } static bool GetFileInformation(CFSTR path, BY_HANDLE_FILE_INFORMATION *info) { NIO::CFileBase file; if (!file.Create(path, 0, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS)) return false; return file.GetFileInformation(info); } }; #ifndef UNDER_CE #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM #define IOCTL_CDROM_GET_DRIVE_GEOMETRY CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS) // #define IOCTL_CDROM_MEDIA_REMOVAL CTL_CODE(IOCTL_CDROM_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) // IOCTL_DISK_GET_DRIVE_GEOMETRY_EX works since WinXP #define my_IOCTL_DISK_GET_DRIVE_GEOMETRY_EX CTL_CODE(IOCTL_DISK_BASE, 0x0028, METHOD_BUFFERED, FILE_ANY_ACCESS) struct my_DISK_GEOMETRY_EX { DISK_GEOMETRY Geometry; LARGE_INTEGER DiskSize; BYTE Data[1]; }; #endif class CInFile: public CFileBase { #ifdef SUPPORT_DEVICE_FILE #ifndef UNDER_CE bool GetGeometry(DISK_GEOMETRY *res) const { return DeviceIoControlOut(IOCTL_DISK_GET_DRIVE_GEOMETRY, res, sizeof(*res)); } bool GetGeometryEx(my_DISK_GEOMETRY_EX *res) const { return DeviceIoControlOut(my_IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, res, sizeof(*res)); } bool GetCdRomGeometry(DISK_GEOMETRY *res) const { return DeviceIoControlOut(IOCTL_CDROM_GET_DRIVE_GEOMETRY, res, sizeof(*res)); } bool GetPartitionInfo(PARTITION_INFORMATION *res) { return DeviceIoControlOut(IOCTL_DISK_GET_PARTITION_INFO, LPVOID(res), sizeof(*res)); } #endif void CorrectDeviceSize(); void CalcDeviceSize(CFSTR name); #endif public: bool Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); bool OpenShared(CFSTR fileName, bool shareForWrite); bool Open(CFSTR fileName); #ifndef UNDER_CE bool OpenReparse(CFSTR fileName) { return Open(fileName, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS); } #endif bool Read1(void *data, UInt32 size, UInt32 &processedSize) throw(); bool ReadPart(void *data, UInt32 size, UInt32 &processedSize) throw(); bool Read(void *data, UInt32 size, UInt32 &processedSize) throw(); }; class COutFile: public CFileBase { public: bool Open(CFSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes); bool Open(CFSTR fileName, DWORD creationDisposition); bool Create(CFSTR fileName, bool createAlways); bool CreateAlways(CFSTR fileName, DWORD flagsAndAttributes); bool SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw(); bool SetMTime(const FILETIME *mTime) throw(); bool WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw(); bool Write(const void *data, UInt32 size, UInt32 &processedSize) throw(); bool SetEndOfFile() throw(); bool SetLength(UInt64 length) throw(); }; }}} #endif src/libs/7zip/win/CPP/Windows/FileLink.cpp000066400000000000000000000232411325366651500205570ustar00rootroot00000000000000// Windows/FileLink.cpp #include "StdAfx.h" #include "../../C/CpuArch.h" #ifdef SUPPORT_DEVICE_FILE #include "../../C/Alloc.h" #endif #include "FileDir.h" #include "FileFind.h" #include "FileIO.h" #include "FileName.h" #ifndef _UNICODE extern bool g_IsNT; #endif namespace NWindows { namespace NFile { using namespace NName; /* Reparse Points (Junctions and Symbolic Links): struct { UInt32 Tag; UInt16 Size; // not including starting 8 bytes UInt16 Reserved; // = 0 UInt16 SubstituteOffset; // offset in bytes from start of namesChars UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL UInt16 PrintOffset; // offset in bytes from start of namesChars UInt16 PrintLen; // size in bytes, it doesn't include tailed NUL [UInt32] Flags; // for Symbolic Links only. UInt16 namesChars[] } MOUNT_POINT (Junction point): 1) there is NUL wchar after path 2) Default Order in table: Substitute Path Print Path 3) pathnames can not contain dot directory names SYMLINK: 1) there is no NUL wchar after path 2) Default Order in table: Print Path Substitute Path */ /* static const UInt32 kReparseFlags_Alias = (1 << 29); static const UInt32 kReparseFlags_HighLatency = (1 << 30); static const UInt32 kReparseFlags_Microsoft = ((UInt32)1 << 31); #define _my_IO_REPARSE_TAG_HSM (0xC0000004L) #define _my_IO_REPARSE_TAG_HSM2 (0x80000006L) #define _my_IO_REPARSE_TAG_SIS (0x80000007L) #define _my_IO_REPARSE_TAG_WIM (0x80000008L) #define _my_IO_REPARSE_TAG_CSV (0x80000009L) #define _my_IO_REPARSE_TAG_DFS (0x8000000AL) #define _my_IO_REPARSE_TAG_DFSR (0x80000012L) */ #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) #define Set16(p, v) SetUi16(p, v) #define Set32(p, v) SetUi32(p, v) static const wchar_t *k_LinkPrefix = L"\\??\\"; static const unsigned k_LinkPrefix_Size = 4; static const bool IsLinkPrefix(const wchar_t *s) { return IsString1PrefixedByString2(s, k_LinkPrefix); } /* static const wchar_t *k_VolumePrefix = L"Volume{"; static const bool IsVolumeName(const wchar_t *s) { return IsString1PrefixedByString2(s, k_VolumePrefix); } */ void WriteString(Byte *dest, const wchar_t *path) { for (;;) { wchar_t c = *path++; if (c == 0) return; Set16(dest, (UInt16)c); dest += 2; } } bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink) { bool isAbs = IsAbsolutePath(path); if (!isAbs && !isSymLink) return false; bool needPrintName = true; if (IsSuperPath(path)) { path += kSuperPathPrefixSize; if (!IsDrivePath(path)) needPrintName = false; } const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0; unsigned len2 = MyStringLen(path) * 2; const unsigned len1 = len2 + add_Prefix_Len * 2; if (!needPrintName) len2 = 0; unsigned totalNamesSize = (len1 + len2); /* some WIM imagex software uses old scheme for symbolic links. so we can old scheme for byte to byte compatibility */ bool newOrderScheme = isSymLink; // newOrderScheme = false; if (!newOrderScheme) totalNamesSize += 2 * 2; const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize; dest.Alloc(size); memset(dest, 0, size); const UInt32 tag = isSymLink ? _my_IO_REPARSE_TAG_SYMLINK : _my_IO_REPARSE_TAG_MOUNT_POINT; Byte *p = dest; Set32(p, tag); Set16(p + 4, (UInt16)(size - 8)); Set16(p + 6, 0); p += 8; unsigned subOffs = 0; unsigned printOffs = 0; if (newOrderScheme) subOffs = len2; else printOffs = len1 + 2; Set16(p + 0, (UInt16)subOffs); Set16(p + 2, (UInt16)len1); Set16(p + 4, (UInt16)printOffs); Set16(p + 6, (UInt16)len2); p += 8; if (isSymLink) { UInt32 flags = isAbs ? 0 : _my_SYMLINK_FLAG_RELATIVE; Set32(p, flags); p += 4; } if (add_Prefix_Len != 0) WriteString(p + subOffs, k_LinkPrefix); WriteString(p + subOffs + add_Prefix_Len * 2, path); if (needPrintName) WriteString(p + printOffs, path); return true; } static void GetString(const Byte *p, unsigned len, UString &res) { wchar_t *s = res.GetBuffer(len); for (unsigned i = 0; i < len; i++) s[i] = Get16(p + i * 2); s[len] = 0; res.ReleaseBuffer(); } bool CReparseAttr::Parse(const Byte *p, size_t size) { if (size < 8) return false; Tag = Get32(p); UInt32 len = Get16(p + 4); if (len + 8 > size) return false; /* if ((type & kReparseFlags_Alias) == 0 || (type & kReparseFlags_Microsoft) == 0 || (type & 0xFFFF) != 3) */ if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT && Tag != _my_IO_REPARSE_TAG_SYMLINK) // return true; return false; if (Get16(p + 6) != 0) // padding return false; p += 8; size -= 8; if (len != size) // do we need that check? return false; if (len < 8) return false; unsigned subOffs = Get16(p); unsigned subLen = Get16(p + 2); unsigned printOffs = Get16(p + 4); unsigned printLen = Get16(p + 6); len -= 8; p += 8; Flags = 0; if (Tag == _my_IO_REPARSE_TAG_SYMLINK) { if (len < 4) return false; Flags = Get32(p); len -= 4; p += 4; } if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) return false; if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) return false; GetString(p + subOffs, subLen >> 1, SubsName); GetString(p + printOffs, printLen >> 1, PrintName); return true; } bool CReparseShortInfo::Parse(const Byte *p, size_t size) { const Byte *start = p; Offset= 0; Size = 0; if (size < 8) return false; UInt32 Tag = Get32(p); UInt32 len = Get16(p + 4); if (len + 8 > size) return false; /* if ((type & kReparseFlags_Alias) == 0 || (type & kReparseFlags_Microsoft) == 0 || (type & 0xFFFF) != 3) */ if (Tag != _my_IO_REPARSE_TAG_MOUNT_POINT && Tag != _my_IO_REPARSE_TAG_SYMLINK) // return true; return false; if (Get16(p + 6) != 0) // padding return false; p += 8; size -= 8; if (len != size) // do we need that check? return false; if (len < 8) return false; unsigned subOffs = Get16(p); unsigned subLen = Get16(p + 2); unsigned printOffs = Get16(p + 4); unsigned printLen = Get16(p + 6); len -= 8; p += 8; // UInt32 Flags = 0; if (Tag == _my_IO_REPARSE_TAG_SYMLINK) { if (len < 4) return false; // Flags = Get32(p); len -= 4; p += 4; } if ((subOffs & 1) != 0 || subOffs > len || len - subOffs < subLen) return false; if ((printOffs & 1) != 0 || printOffs > len || len - printOffs < printLen) return false; Offset = (unsigned)(p - start) + subOffs; Size = subLen; return true; } bool CReparseAttr::IsOkNamePair() const { if (IsLinkPrefix(SubsName)) { if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size))) return PrintName.IsEmpty(); if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0) return true; } return wcscmp(SubsName, PrintName) == 0; } /* bool CReparseAttr::IsVolume() const { if (!IsLinkPrefix(SubsName)) return false; return IsVolumeName(SubsName.Ptr(k_LinkPrefix_Size)); } */ UString CReparseAttr::GetPath() const { UString s = SubsName; if (IsLinkPrefix(s)) { s.ReplaceOneCharAtPos(1, '\\'); if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) s.DeleteFrontal(k_LinkPrefix_Size); } return s; } #ifdef SUPPORT_DEVICE_FILE namespace NSystem { bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize); } #endif #ifndef UNDER_CE namespace NIO { bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMATION *fileInfo) { reparseData.Free(); CInFile file; if (!file.OpenReparse(path)) return false; if (fileInfo) file.GetFileInformation(fileInfo); const unsigned kBufSize = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; CByteArr buf(kBufSize); DWORD returnedSize; if (!file.DeviceIoControlOut(my_FSCTL_GET_REPARSE_POINT, buf, kBufSize, &returnedSize)) return false; reparseData.CopyFrom(buf, returnedSize); return true; } static bool CreatePrefixDirOfFile(CFSTR path) { FString path2 = path; int pos = path2.ReverseFind(FCHAR_PATH_SEPARATOR); if (pos < 0) return true; #ifdef _WIN32 if (pos == 2 && path2[1] == L':') return true; // we don't create Disk folder; #endif path2.DeleteFrom(pos); return NDir::CreateComplexDir(path2); } // If there is Reprase data already, it still writes new Reparse data bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) { NFile::NFind::CFileInfo fi; if (fi.Find(path)) { if (fi.IsDir() != isDir) { ::SetLastError(ERROR_DIRECTORY); return false; } } else { if (isDir) { if (!NDir::CreateComplexDir(path)) return false; } else { CreatePrefixDirOfFile(path); COutFile file; if (!file.Create(path, CREATE_NEW)) return false; } } COutFile file; if (!file.Open(path, FILE_SHARE_WRITE, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)) return false; DWORD returnedSize; if (!file.DeviceIoControl(my_FSCTL_SET_REPARSE_POINT, (void *)data, size, NULL, 0, &returnedSize)) return false; return true; } } #endif }} src/libs/7zip/win/CPP/Windows/FileMapping.h000066400000000000000000000030701325366651500207200ustar00rootroot00000000000000// Windows/FileMapping.h #ifndef __WINDOWS_FILEMAPPING_H #define __WINDOWS_FILEMAPPING_H #include "../Common/MyTypes.h" #include "Handle.h" namespace NWindows { class CFileMapping: public CHandle { public: WRes Create(DWORD protect, UInt64 maxSize, LPCTSTR name) { _handle = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, protect, (DWORD)(maxSize >> 32), (DWORD)maxSize, name); return ::GetLastError(); } WRes Open(DWORD #ifndef UNDER_CE desiredAccess #endif , LPCTSTR name) { #ifdef UNDER_CE WRes res = Create(PAGE_READONLY, 0, name); if (res == ERROR_ALREADY_EXISTS) return 0; Close(); if (res == 0) res = ERROR_FILE_NOT_FOUND; return res; #else _handle = ::OpenFileMapping(desiredAccess, FALSE, name); if (_handle != 0) return 0; return ::GetLastError(); #endif } LPVOID Map(DWORD desiredAccess, UInt64 fileOffset, SIZE_T numberOfBytesToMap) { return ::MapViewOfFile(_handle, desiredAccess, (DWORD)(fileOffset >> 32), (DWORD)fileOffset, numberOfBytesToMap); } #ifndef UNDER_CE LPVOID Map(DWORD desiredAccess, UInt64 fileOffset, SIZE_T numberOfBytesToMap, LPVOID baseAddress) { return ::MapViewOfFileEx(_handle, desiredAccess, (DWORD)(fileOffset >> 32), (DWORD)fileOffset, numberOfBytesToMap, baseAddress); } #endif }; class CFileUnmapper { const void *_data; public: CFileUnmapper(const void *data) : _data(data) {} ~CFileUnmapper() { ::UnmapViewOfFile(_data); } }; } #endif src/libs/7zip/win/CPP/Windows/FileName.cpp000066400000000000000000000425361325366651500205520ustar00rootroot00000000000000// Windows/FileName.cpp #include "StdAfx.h" #include "FileName.h" #ifndef _UNICODE extern bool g_IsNT; #endif namespace NWindows { namespace NFile { namespace NName { #ifndef USE_UNICODE_FSTRING void NormalizeDirPathPrefix(FString &dirPath) { if (dirPath.IsEmpty()) return; if (dirPath.Back() != FCHAR_PATH_SEPARATOR) dirPath += FCHAR_PATH_SEPARATOR; } #endif void NormalizeDirPathPrefix(UString &dirPath) { if (dirPath.IsEmpty()) return; if (dirPath.Back() != WCHAR_PATH_SEPARATOR) dirPath += WCHAR_PATH_SEPARATOR; } #ifdef _WIN32 const wchar_t *kSuperPathPrefix = L"\\\\?\\"; static const wchar_t *kSuperUncPrefix = L"\\\\?\\UNC\\"; #define IS_DEVICE_PATH(s) ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '.' && (s)[3] == '\\') #define IS_SUPER_PREFIX(s) ((s)[0] == '\\' && (s)[1] == '\\' && (s)[2] == '?' && (s)[3] == '\\') #define IS_SUPER_OR_DEVICE_PATH(s) ((s)[0] == '\\' && (s)[1] == '\\' && ((s)[2] == '?' || (s)[2] == '.') && (s)[3] == '\\') #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z') #define IS_UNC_WITH_SLASH(s) ( \ ((s)[0] == 'U' || (s)[0] == 'u') && \ ((s)[1] == 'N' || (s)[1] == 'n') && \ ((s)[2] == 'C' || (s)[2] == 'c') && \ (s)[3] == '\\') bool IsDevicePath(CFSTR s) throw() { #ifdef UNDER_CE s = s; return false; /* // actually we don't know the way to open device file in WinCE. unsigned len = MyStringLen(s); if (len < 5 || len > 5 || memcmp(s, FTEXT("DSK"), 3 * sizeof(FChar)) != 0) return false; if (s[4] != ':') return false; // for reading use SG_REQ sg; if (DeviceIoControl(dsk, IOCTL_DISK_READ)); */ #else if (!IS_DEVICE_PATH(s)) return false; unsigned len = MyStringLen(s); if (len == 6 && s[5] == ':') return true; if (len < 18 || len > 22 || memcmp(s + kDevicePathPrefixSize, FTEXT("PhysicalDrive"), 13 * sizeof(FChar)) != 0) return false; for (unsigned i = 17; i < len; i++) if (s[i] < '0' || s[i] > '9') return false; return true; #endif } bool IsSuperUncPath(CFSTR s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); } bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == '\\'; } bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); } bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); } // bool IsSuperUncPath(const wchar_t *s) { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); } #ifndef USE_UNICODE_FSTRING bool IsDrivePath(CFSTR s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == '\\'; } bool IsSuperPath(CFSTR s) throw() { return IS_SUPER_PREFIX(s); } bool IsSuperOrDevicePath(CFSTR s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); } #endif // USE_UNICODE_FSTRING bool IsAbsolutePath(const wchar_t *s) throw() { return s[0] == WCHAR_PATH_SEPARATOR || IsDrivePath(s); } static const unsigned kDrivePrefixSize = 3; /* c:\ */ #ifndef USE_UNICODE_FSTRING static unsigned GetRootPrefixSize_Of_NetworkPath(CFSTR s) throw() { // Network path: we look "server\path\" as root prefix int pos = FindCharPosInString(s, '\\'); if (pos < 0) return 0; int pos2 = FindCharPosInString(s + pos + 1, '\\'); if (pos2 < 0) return 0; return pos + pos2 + 2; } static unsigned GetRootPrefixSize_Of_SimplePath(CFSTR s) throw() { if (IsDrivePath(s)) return kDrivePrefixSize; if (s[0] != '\\' || s[1] != '\\') return 0; unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2); return (size == 0) ? 0 : 2 + size; } static unsigned GetRootPrefixSize_Of_SuperPath(CFSTR s) throw() { if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)) { unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize); return (size == 0) ? 0 : kSuperUncPathPrefixSize + size; } // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\" int pos = FindCharPosInString(s + kSuperPathPrefixSize, FCHAR_PATH_SEPARATOR); if (pos < 0) return 0; return kSuperPathPrefixSize + pos + 1; } unsigned GetRootPrefixSize(CFSTR s) throw() { if (IS_DEVICE_PATH(s)) return kDevicePathPrefixSize; if (IsSuperPath(s)) return GetRootPrefixSize_Of_SuperPath(s); return GetRootPrefixSize_Of_SimplePath(s); } #endif // USE_UNICODE_FSTRING static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw() { // Network path: we look "server\path\" as root prefix int pos = FindCharPosInString(s, L'\\'); if (pos < 0) return 0; int pos2 = FindCharPosInString(s + pos + 1, L'\\'); if (pos2 < 0) return 0; return pos + pos2 + 2; } static unsigned GetRootPrefixSize_Of_SimplePath(const wchar_t *s) throw() { if (IsDrivePath(s)) return kDrivePrefixSize; if (s[0] != '\\' || s[1] != '\\') return 0; unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2); return (size == 0) ? 0 : 2 + size; } static unsigned GetRootPrefixSize_Of_SuperPath(const wchar_t *s) throw() { if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)) { unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize); return (size == 0) ? 0 : kSuperUncPathPrefixSize + size; } // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\" int pos = FindCharPosInString(s + kSuperPathPrefixSize, L'\\'); if (pos < 0) return 0; return kSuperPathPrefixSize + pos + 1; } unsigned GetRootPrefixSize(const wchar_t *s) throw() { if (IS_DEVICE_PATH(s)) return kDevicePathPrefixSize; if (IsSuperPath(s)) return GetRootPrefixSize_Of_SuperPath(s); return GetRootPrefixSize_Of_SimplePath(s); } #else // _WIN32 bool IsAbsolutePath(const wchar_t *s) throw() { return s[0] == WCHAR_PATH_SEPARATOR } #ifndef USE_UNICODE_FSTRING unsigned GetRootPrefixSize(CFSTR s) throw() { return s[0] == CHAR_PATH_SEPRATOR ? 1 : 0; } #endif unsigned GetRootPrefixSize(const wchar_t *s) throw() { return s[0] == CHAR_PATH_SEPRATOR ? 1 : 0; } #endif // _WIN32 #ifndef UNDER_CE static bool GetCurDir(UString &path) { path.Empty(); DWORD needLength; #ifndef _UNICODE if (!g_IsNT) { TCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetCurrentDirectory(MAX_PATH + 1, s); path = fs2us(fas2fs(s)); } else #endif { WCHAR s[MAX_PATH + 2]; s[0] = 0; needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s); path = s; } return (needLength > 0 && needLength <= MAX_PATH); } static bool ResolveDotsFolders(UString &s) { #ifdef _WIN32 s.Replace(L'/', WCHAR_PATH_SEPARATOR); #endif for (int i = 0;;) { wchar_t c = s[i]; if (c == 0) return true; if (c == '.' && (i == 0 || s[i - 1] == WCHAR_PATH_SEPARATOR)) { wchar_t c1 = s[i + 1]; if (c1 == '.') { wchar_t c2 = s[i + 2]; if (c2 == WCHAR_PATH_SEPARATOR || c2 == 0) { if (i == 0) return false; int k = i - 2; for (; k >= 0; k--) if (s[k] == WCHAR_PATH_SEPARATOR) break; unsigned num; if (k >= 0) { num = i + 2 - k; i = k; } else { num = (c2 == 0 ? (i + 2) : (i + 3)); i = 0; } s.Delete(i, num); continue; } } else { if (c1 == WCHAR_PATH_SEPARATOR || c1 == 0) { unsigned num = 2; if (i != 0) i--; else if (c1 == 0) num = 1; s.Delete(i, num); continue; } } } i++; } } #endif // UNDER_CE #define LONG_PATH_DOTS_FOLDERS_PARSING /* Windows (at least 64-bit XP) can't resolve "." or ".." in paths that start with SuperPrefix \\?\ To solve that problem we check such path: - super path contains "." or ".." - we use kSuperPathType_UseOnlySuper - super path doesn't contain "." or ".." - we use kSuperPathType_UseOnlyMain */ #ifdef LONG_PATH_DOTS_FOLDERS_PARSING #ifndef UNDER_CE static bool AreThereDotsFolders(CFSTR s) { for (unsigned i = 0;; i++) { FChar c = s[i]; if (c == 0) return false; if (c == '.' && (i == 0 || s[i - 1] == CHAR_PATH_SEPARATOR)) { FChar c1 = s[i + 1]; if (c1 == 0 || c1 == CHAR_PATH_SEPARATOR || (c1 == '.' && (s[i + 2] == 0 || s[i + 2] == CHAR_PATH_SEPARATOR))) return true; } } } #endif #endif // LONG_PATH_DOTS_FOLDERS_PARSING #ifdef WIN_LONG_PATH /* Most of Windows versions have problems, if some file or dir name contains '.' or ' ' at the end of name (Bad Path). To solve that problem, we always use Super Path ("\\?\" prefix and full path) in such cases. Note that "." and ".." are not bad names. There are 3 cases: 1) If the path is already Super Path, we use that path 2) If the path is not Super Path : 2.1) Bad Path; we use only Super Path. 2.2) Good Path; we use Main Path. If it fails, we use Super Path. NeedToUseOriginalPath returns: kSuperPathType_UseOnlyMain : Super already kSuperPathType_UseOnlySuper : not Super, Bad Path kSuperPathType_UseMainAndSuper : not Super, Good Path */ int GetUseSuperPathType(CFSTR s) throw() { if (IsSuperOrDevicePath(s)) { #ifdef LONG_PATH_DOTS_FOLDERS_PARSING if ((s)[2] != '.') if (AreThereDotsFolders(s + kSuperPathPrefixSize)) return kSuperPathType_UseOnlySuper; #endif return kSuperPathType_UseOnlyMain; } for (unsigned i = 0;; i++) { FChar c = s[i]; if (c == 0) return kSuperPathType_UseMainAndSuper; if (c == '.' || c == ' ') { FChar c2 = s[i + 1]; if (c2 == 0 || c2 == CHAR_PATH_SEPARATOR) { // if it's "." or "..", it's not bad name. if (c == '.') { if (i == 0 || s[i - 1] == CHAR_PATH_SEPARATOR) continue; if (s[i - 1] == '.') { if (i - 1 == 0 || s[i - 2] == CHAR_PATH_SEPARATOR) continue; } } return kSuperPathType_UseOnlySuper; } } } } /* returns false in two cases: - if GetCurDir was used, and GetCurDir returned error. - if we can't resolve ".." name. if path is ".", "..", res is empty. if it's Super Path already, res is empty. for \**** , and if GetCurDir is not drive (c:\), res is empty for absolute paths, returns true, res is Super path. */ static bool GetSuperPathBase(CFSTR s, UString &res) { res.Empty(); FChar c = s[0]; if (c == 0) return true; if (c == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0))) return true; if (IsSuperOrDevicePath(s)) { #ifdef LONG_PATH_DOTS_FOLDERS_PARSING if ((s)[2] == '.') return true; // we will return true here, so we will try to use these problem paths. if (!AreThereDotsFolders(s + kSuperPathPrefixSize)) return true; UString temp = fs2us(s); unsigned fixedSize = GetRootPrefixSize_Of_SuperPath(temp); if (fixedSize == 0) return true; UString rem = &temp[fixedSize]; if (!ResolveDotsFolders(rem)) return true; temp.DeleteFrom(fixedSize); res += temp; res += rem; #endif return true; } if (c == CHAR_PATH_SEPARATOR) { if (s[1] == CHAR_PATH_SEPARATOR) { UString temp = fs2us(s + 2); unsigned fixedSize = GetRootPrefixSize_Of_NetworkPath(temp); if (fixedSize == 0) // maybe we must ignore that error to allow short network paths? return false; UString rem = &temp[fixedSize]; if (!ResolveDotsFolders(rem)) return false; res += kSuperUncPrefix; temp.DeleteFrom(fixedSize); res += temp; res += rem; return true; } } else { if (IsDrivePath(s)) { UString temp = fs2us(s); UString rem = &temp[kDrivePrefixSize]; if (!ResolveDotsFolders(rem)) return true; res += kSuperPathPrefix; temp.DeleteFrom(kDrivePrefixSize); res += temp; res += rem; return true; } } UString curDir; if (!GetCurDir(curDir)) return false; if (curDir.Back() != WCHAR_PATH_SEPARATOR) curDir += WCHAR_PATH_SEPARATOR; unsigned fixedSizeStart = 0; unsigned fixedSize = 0; const wchar_t *superMarker = NULL; if (IsSuperPath(curDir)) { fixedSize = GetRootPrefixSize_Of_SuperPath(curDir); if (fixedSize == 0) return false; } else { if (IsDrivePath(curDir)) { superMarker = kSuperPathPrefix; fixedSize = kDrivePrefixSize; } else { if (curDir[0] != CHAR_PATH_SEPARATOR || curDir[1] != CHAR_PATH_SEPARATOR) return false; fixedSizeStart = 2; fixedSize = GetRootPrefixSize_Of_NetworkPath(&curDir[2]); if (fixedSize == 0) return false; superMarker = kSuperUncPrefix; } } UString temp; if (c == CHAR_PATH_SEPARATOR) { temp = fs2us(s + 1); } else { temp += &curDir[fixedSizeStart + fixedSize]; temp += fs2us(s); } if (!ResolveDotsFolders(temp)) return false; if (superMarker) res += superMarker; res += curDir.Mid(fixedSizeStart, fixedSize); res += temp; return true; } /* In that case if GetSuperPathBase doesn't return new path, we don't need to use same path that was used as main path GetSuperPathBase superPath.IsEmpty() onlyIfNew false * * GetCurDir Error true false * use Super path true true true don't use any path, we already used mainPath true true false use main path as Super Path, we don't try mainMath That case is possible now if GetCurDir returns unknow type of path (not drive and not network) We can change that code if we want to try mainPath, if GetSuperPathBase returns error, and we didn't try mainPath still. If we want to work that way, we don't need to use GetSuperPathBase return code. */ bool GetSuperPath(CFSTR path, UString &superPath, bool onlyIfNew) { if (GetSuperPathBase(path, superPath)) { if (superPath.IsEmpty()) { // actually the only possible when onlyIfNew == true and superPath is empty // is case when if (onlyIfNew) return false; superPath = fs2us(path); } return true; } return false; } bool GetSuperPaths(CFSTR s1, CFSTR s2, UString &d1, UString &d2, bool onlyIfNew) { if (!GetSuperPathBase(s1, d1) || !GetSuperPathBase(s2, d2)) return false; if (d1.IsEmpty() && d2.IsEmpty() && onlyIfNew) return false; if (d1.IsEmpty()) d1 = fs2us(s1); if (d2.IsEmpty()) d2 = fs2us(s2); return true; } /* // returns true, if we need additional use with New Super path. bool GetSuperPath(CFSTR path, UString &superPath) { if (GetSuperPathBase(path, superPath)) return !superPath.IsEmpty(); return false; } */ #endif // WIN_LONG_PATH bool GetFullPath(CFSTR dirPrefix, CFSTR s, FString &res) { res = s; #ifdef UNDER_CE if (s[0] != CHAR_PATH_SEPARATOR) { if (!dirPrefix) return false; res = dirPrefix; res += s; } #else unsigned prefixSize = GetRootPrefixSize(s); if (prefixSize != 0) { if (!AreThereDotsFolders(s + prefixSize)) return true; UString rem = fs2us(s + prefixSize); if (!ResolveDotsFolders(rem)) return true; // maybe false; res.DeleteFrom(prefixSize); res += us2fs(rem); return true; } /* FChar c = s[0]; if (c == 0) return true; if (c == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0))) return true; if (c == CHAR_PATH_SEPARATOR && s[1] == CHAR_PATH_SEPARATOR) return true; if (IsDrivePath(s)) return true; */ UString curDir; if (dirPrefix) curDir = fs2us(dirPrefix); else { if (!GetCurDir(curDir)) return false; } if (!curDir.IsEmpty() && curDir.Back() != WCHAR_PATH_SEPARATOR) curDir += WCHAR_PATH_SEPARATOR; unsigned fixedSize = 0; #ifdef _WIN32 if (IsSuperPath(curDir)) { fixedSize = GetRootPrefixSize_Of_SuperPath(curDir); if (fixedSize == 0) return false; } else { if (IsDrivePath(curDir)) fixedSize = kDrivePrefixSize; else { if (curDir[0] != WCHAR_PATH_SEPARATOR || curDir[1] != WCHAR_PATH_SEPARATOR) return false; fixedSize = GetRootPrefixSize_Of_NetworkPath(&curDir[2]); if (fixedSize == 0) return false; fixedSize += 2; } } #endif // _WIN32 UString temp; if (s[0] == CHAR_PATH_SEPARATOR) { temp = fs2us(s + 1); } else { temp += curDir.Ptr(fixedSize); temp += fs2us(s); } if (!ResolveDotsFolders(temp)) return false; curDir.DeleteFrom(fixedSize); res = us2fs(curDir); res += us2fs(temp); #endif // UNDER_CE return true; } bool GetFullPath(CFSTR path, FString &fullPath) { return GetFullPath(NULL, path, fullPath); } }}} src/libs/7zip/win/CPP/Windows/FileName.h000066400000000000000000000044771325366651500202210ustar00rootroot00000000000000// Windows/FileName.h #ifndef __WINDOWS_FILE_NAME_H #define __WINDOWS_FILE_NAME_H #include "../Common/MyString.h" namespace NWindows { namespace NFile { namespace NName { void NormalizeDirPathPrefix(FString &dirPath); // ensures that it ended with '\\', if dirPath is not epmty void NormalizeDirPathPrefix(UString &dirPath); #ifdef _WIN32 extern const wchar_t *kSuperPathPrefix; /* \\?\ */ const unsigned kDevicePathPrefixSize = 4; const unsigned kSuperPathPrefixSize = 4; const unsigned kSuperUncPathPrefixSize = kSuperPathPrefixSize + 4; bool IsDevicePath(CFSTR s) throw(); /* \\.\ */ bool IsSuperUncPath(CFSTR s) throw(); bool IsDrivePath(const wchar_t *s) throw(); bool IsSuperPath(const wchar_t *s) throw(); bool IsSuperOrDevicePath(const wchar_t *s) throw(); #ifndef USE_UNICODE_FSTRING bool IsDrivePath(CFSTR s) throw(); bool IsSuperPath(CFSTR s) throw(); bool IsSuperOrDevicePath(CFSTR s) throw(); #endif #endif // _WIN32 bool IsAbsolutePath(const wchar_t *s) throw(); unsigned GetRootPrefixSize(const wchar_t *s) throw(); #ifdef WIN_LONG_PATH const int kSuperPathType_UseOnlyMain = 0; const int kSuperPathType_UseOnlySuper = 1; const int kSuperPathType_UseMainAndSuper = 2; int GetUseSuperPathType(CFSTR s) throw(); bool GetSuperPath(CFSTR path, UString &longPath, bool onlyIfNew); bool GetSuperPaths(CFSTR s1, CFSTR s2, UString &d1, UString &d2, bool onlyIfNew); #define USE_MAIN_PATH (__useSuperPathType != kSuperPathType_UseOnlySuper) #define USE_MAIN_PATH_2 (__useSuperPathType1 != kSuperPathType_UseOnlySuper && __useSuperPathType2 != kSuperPathType_UseOnlySuper) #define USE_SUPER_PATH (__useSuperPathType != kSuperPathType_UseOnlyMain) #define USE_SUPER_PATH_2 (__useSuperPathType1 != kSuperPathType_UseOnlyMain || __useSuperPathType2 != kSuperPathType_UseOnlyMain) #define IF_USE_MAIN_PATH int __useSuperPathType = GetUseSuperPathType(path); if (USE_MAIN_PATH) #define IF_USE_MAIN_PATH_2(x1, x2) \ int __useSuperPathType1 = GetUseSuperPathType(x1); \ int __useSuperPathType2 = GetUseSuperPathType(x2); \ if (USE_MAIN_PATH_2) #else #define IF_USE_MAIN_PATH #define IF_USE_MAIN_PATH_2(x1, x2) #endif // WIN_LONG_PATH bool GetFullPath(CFSTR dirPrefix, CFSTR path, FString &fullPath); bool GetFullPath(CFSTR path, FString &fullPath); }}} #endif src/libs/7zip/win/CPP/Windows/Handle.h000066400000000000000000000012171325366651500177210ustar00rootroot00000000000000// Windows/Handle.h #ifndef __WINDOWS_HANDLE_H #define __WINDOWS_HANDLE_H namespace NWindows { class CHandle { protected: HANDLE _handle; public: operator HANDLE() { return _handle; } CHandle(): _handle(NULL) {} ~CHandle() { Close(); } bool IsCreated() const { return (_handle != NULL); } bool Close() { if (_handle == NULL) return true; if (!::CloseHandle(_handle)) return false; _handle = NULL; return true; } void Attach(HANDLE handle) { _handle = handle; } HANDLE Detach() { HANDLE handle = _handle; _handle = NULL; return handle; } }; } #endif src/libs/7zip/win/CPP/Windows/PropVariant.cpp000066400000000000000000000145551325366651500213370ustar00rootroot00000000000000// Windows/PropVariant.cpp #include "StdAfx.h" #include "../Common/Defs.h" #include "PropVariant.h" namespace NWindows { namespace NCOM { HRESULT PropVarEm_Alloc_Bstr(PROPVARIANT *p, unsigned numChars) throw() { p->bstrVal = ::SysAllocStringLen(0, numChars); if (!p->bstrVal) { p->vt = VT_ERROR; p->scode = E_OUTOFMEMORY; return E_OUTOFMEMORY; } p->vt = VT_BSTR; return S_OK; } HRESULT PropVarEm_Set_Str(PROPVARIANT *p, const char *s) throw() { UINT len = (UINT)strlen(s); p->bstrVal = ::SysAllocStringLen(0, len); if (!p->bstrVal) { p->vt = VT_ERROR; p->scode = E_OUTOFMEMORY; return E_OUTOFMEMORY; } p->vt = VT_BSTR; BSTR dest = p->bstrVal; for (UINT i = 0; i <= len; i++) dest[i] = s[i]; return S_OK; } CPropVariant::CPropVariant(const PROPVARIANT &varSrc) { vt = VT_EMPTY; InternalCopy(&varSrc); } CPropVariant::CPropVariant(const CPropVariant &varSrc) { vt = VT_EMPTY; InternalCopy(&varSrc); } CPropVariant::CPropVariant(BSTR bstrSrc) { vt = VT_EMPTY; *this = bstrSrc; } CPropVariant::CPropVariant(LPCOLESTR lpszSrc) { vt = VT_EMPTY; *this = lpszSrc; } CPropVariant& CPropVariant::operator=(const CPropVariant &varSrc) { InternalCopy(&varSrc); return *this; } CPropVariant& CPropVariant::operator=(const PROPVARIANT &varSrc) { InternalCopy(&varSrc); return *this; } CPropVariant& CPropVariant::operator=(BSTR bstrSrc) { *this = (LPCOLESTR)bstrSrc; return *this; } static const char *kMemException = "out of memory"; CPropVariant& CPropVariant::operator=(LPCOLESTR lpszSrc) { InternalClear(); vt = VT_BSTR; wReserved1 = 0; bstrVal = ::SysAllocString(lpszSrc); if (!bstrVal && lpszSrc) { throw kMemException; // vt = VT_ERROR; // scode = E_OUTOFMEMORY; } return *this; } CPropVariant& CPropVariant::operator=(const char *s) { InternalClear(); vt = VT_BSTR; wReserved1 = 0; UINT len = (UINT)strlen(s); bstrVal = ::SysAllocStringLen(0, len); if (!bstrVal) { throw kMemException; // vt = VT_ERROR; // scode = E_OUTOFMEMORY; } else { for (UINT i = 0; i <= len; i++) bstrVal[i] = s[i]; } return *this; } CPropVariant& CPropVariant::operator=(bool bSrc) throw() { if (vt != VT_BOOL) { InternalClear(); vt = VT_BOOL; } boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE; return *this; } BSTR CPropVariant::AllocBstr(unsigned numChars) { if (vt != VT_EMPTY) InternalClear(); vt = VT_BSTR; wReserved1 = 0; bstrVal = ::SysAllocStringLen(0, numChars); if (!bstrVal) { throw kMemException; // vt = VT_ERROR; // scode = E_OUTOFMEMORY; } return bstrVal; } #define SET_PROP_FUNC(type, id, dest) \ CPropVariant& CPropVariant::operator=(type value) throw() \ { if (vt != id) { InternalClear(); vt = id; } \ dest = value; return *this; } SET_PROP_FUNC(Byte, VT_UI1, bVal) // SET_PROP_FUNC(Int16, VT_I2, iVal) SET_PROP_FUNC(Int32, VT_I4, lVal) SET_PROP_FUNC(UInt32, VT_UI4, ulVal) SET_PROP_FUNC(UInt64, VT_UI8, uhVal.QuadPart) SET_PROP_FUNC(Int64, VT_I8, hVal.QuadPart) SET_PROP_FUNC(const FILETIME &, VT_FILETIME, filetime) HRESULT PropVariant_Clear(PROPVARIANT *prop) throw() { switch (prop->vt) { case VT_EMPTY: case VT_UI1: case VT_I1: case VT_I2: case VT_UI2: case VT_BOOL: case VT_I4: case VT_UI4: case VT_R4: case VT_INT: case VT_UINT: case VT_ERROR: case VT_FILETIME: case VT_UI8: case VT_R8: case VT_CY: case VT_DATE: prop->vt = VT_EMPTY; prop->wReserved1 = 0; prop->wReserved2 = 0; prop->wReserved3 = 0; prop->uhVal.QuadPart = 0; return S_OK; } return ::VariantClear((VARIANTARG *)prop); // return ::PropVariantClear(prop); // PropVariantClear can clear VT_BLOB. } HRESULT CPropVariant::Clear() throw() { if (vt == VT_EMPTY) return S_OK; return PropVariant_Clear(this); } HRESULT CPropVariant::Copy(const PROPVARIANT* pSrc) throw() { ::VariantClear((tagVARIANT *)this); switch(pSrc->vt) { case VT_UI1: case VT_I1: case VT_I2: case VT_UI2: case VT_BOOL: case VT_I4: case VT_UI4: case VT_R4: case VT_INT: case VT_UINT: case VT_ERROR: case VT_FILETIME: case VT_UI8: case VT_R8: case VT_CY: case VT_DATE: memmove((PROPVARIANT*)this, pSrc, sizeof(PROPVARIANT)); return S_OK; } return ::VariantCopy((tagVARIANT *)this, (tagVARIANT *)const_cast(pSrc)); } HRESULT CPropVariant::Attach(PROPVARIANT *pSrc) throw() { HRESULT hr = Clear(); if (FAILED(hr)) return hr; memcpy(this, pSrc, sizeof(PROPVARIANT)); pSrc->vt = VT_EMPTY; return S_OK; } HRESULT CPropVariant::Detach(PROPVARIANT *pDest) throw() { if (pDest->vt != VT_EMPTY) { HRESULT hr = PropVariant_Clear(pDest); if (FAILED(hr)) return hr; } memcpy(pDest, this, sizeof(PROPVARIANT)); vt = VT_EMPTY; return S_OK; } HRESULT CPropVariant::InternalClear() throw() { if (vt == VT_EMPTY) return S_OK; HRESULT hr = Clear(); if (FAILED(hr)) { vt = VT_ERROR; scode = hr; } return hr; } void CPropVariant::InternalCopy(const PROPVARIANT *pSrc) { HRESULT hr = Copy(pSrc); if (FAILED(hr)) { if (hr == E_OUTOFMEMORY) throw kMemException; vt = VT_ERROR; scode = hr; } } int CPropVariant::Compare(const CPropVariant &a) throw() { if (vt != a.vt) return MyCompare(vt, a.vt); switch (vt) { case VT_EMPTY: return 0; // case VT_I1: return MyCompare(cVal, a.cVal); case VT_UI1: return MyCompare(bVal, a.bVal); case VT_I2: return MyCompare(iVal, a.iVal); case VT_UI2: return MyCompare(uiVal, a.uiVal); case VT_I4: return MyCompare(lVal, a.lVal); case VT_UI4: return MyCompare(ulVal, a.ulVal); // case VT_UINT: return MyCompare(uintVal, a.uintVal); case VT_I8: return MyCompare(hVal.QuadPart, a.hVal.QuadPart); case VT_UI8: return MyCompare(uhVal.QuadPart, a.uhVal.QuadPart); case VT_BOOL: return -MyCompare(boolVal, a.boolVal); case VT_FILETIME: return ::CompareFileTime(&filetime, &a.filetime); case VT_BSTR: return 0; // Not implemented default: return 0; } } }} src/libs/7zip/win/CPP/Windows/PropVariant.h000066400000000000000000000061071325366651500207760ustar00rootroot00000000000000// Windows/PropVariant.h #ifndef __WINDOWS_PROP_VARIANT_H #define __WINDOWS_PROP_VARIANT_H #include "../Common/MyTypes.h" #include "../Common/MyWindows.h" namespace NWindows { namespace NCOM { HRESULT PropVariant_Clear(PROPVARIANT *p) throw(); HRESULT PropVarEm_Alloc_Bstr(PROPVARIANT *p, unsigned numChars) throw(); HRESULT PropVarEm_Set_Str(PROPVARIANT *p, const char *s) throw(); inline void PropVarEm_Set_UInt32(PROPVARIANT *p, UInt32 v) throw() { p->vt = VT_UI4; p->ulVal = v; } inline void PropVarEm_Set_UInt64(PROPVARIANT *p, UInt64 v) throw() { p->vt = VT_UI8; p->uhVal.QuadPart = v; } inline void PropVarEm_Set_FileTime64(PROPVARIANT *p, UInt64 v) throw() { p->vt = VT_FILETIME; p->filetime.dwLowDateTime = (DWORD)v; p->filetime.dwHighDateTime = (DWORD)(v >> 32); } inline void PropVarEm_Set_Bool(PROPVARIANT *p, bool b) throw() { p->vt = VT_BOOL; p->boolVal = (b ? VARIANT_TRUE : VARIANT_FALSE); } class CPropVariant : public tagPROPVARIANT { public: CPropVariant() { vt = VT_EMPTY; wReserved1 = 0; // wReserved2 = 0; // wReserved3 = 0; // uhVal.QuadPart = 0; bstrVal = 0; } ~CPropVariant() throw() { Clear(); } CPropVariant(const PROPVARIANT &varSrc); CPropVariant(const CPropVariant &varSrc); CPropVariant(BSTR bstrSrc); CPropVariant(LPCOLESTR lpszSrc); CPropVariant(bool bSrc) { vt = VT_BOOL; wReserved1 = 0; boolVal = (bSrc ? VARIANT_TRUE : VARIANT_FALSE); }; CPropVariant(Byte value) { vt = VT_UI1; wReserved1 = 0; bVal = value; } private: CPropVariant(Int16 value); // { vt = VT_I2; wReserved1 = 0; iVal = value; } CPropVariant(Int32 value); // { vt = VT_I4; wReserved1 = 0; lVal = value; } public: CPropVariant(UInt32 value) { vt = VT_UI4; wReserved1 = 0; ulVal = value; } CPropVariant(UInt64 value) { vt = VT_UI8; wReserved1 = 0; uhVal.QuadPart = value; } CPropVariant(Int64 value) { vt = VT_I8; wReserved1 = 0; hVal.QuadPart = value; } CPropVariant(const FILETIME &value) { vt = VT_FILETIME; wReserved1 = 0; filetime = value; } CPropVariant& operator=(const CPropVariant &varSrc); CPropVariant& operator=(const PROPVARIANT &varSrc); CPropVariant& operator=(BSTR bstrSrc); CPropVariant& operator=(LPCOLESTR lpszSrc); CPropVariant& operator=(const char *s); CPropVariant& operator=(bool bSrc) throw(); CPropVariant& operator=(Byte value) throw(); private: CPropVariant& operator=(Int16 value) throw(); public: CPropVariant& operator=(Int32 value) throw(); CPropVariant& operator=(UInt32 value) throw(); CPropVariant& operator=(UInt64 value) throw(); CPropVariant& operator=(Int64 value) throw(); CPropVariant& operator=(const FILETIME &value) throw(); BSTR AllocBstr(unsigned numChars); HRESULT Clear() throw(); HRESULT Copy(const PROPVARIANT *pSrc) throw(); HRESULT Attach(PROPVARIANT *pSrc) throw(); HRESULT Detach(PROPVARIANT *pDest) throw(); HRESULT InternalClear() throw(); void InternalCopy(const PROPVARIANT *pSrc); int Compare(const CPropVariant &a) throw(); }; }} #endif src/libs/7zip/win/CPP/Windows/PropVariantConv.cpp000066400000000000000000000065761325366651500221710ustar00rootroot00000000000000// PropVariantConvert.cpp #include "StdAfx.h" #include "../Common/IntToString.h" #include "Defs.h" #include "PropVariantConv.h" #define UINT_TO_STR_2(c, val) { s[0] = (c); s[1] = (char)('0' + (val) / 10); s[2] = (char)('0' + (val) % 10); s += 3; } bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime, bool includeSeconds) throw() { SYSTEMTIME st; if (!BOOLToBool(FileTimeToSystemTime(&ft, &st))) { *s = 0; return false; } unsigned val = st.wYear; if (val >= 10000) { *s++ = (char)('0' + val / 10000); val %= 10000; } { s[3] = (char)('0' + val % 10); val /= 10; s[2] = (char)('0' + val % 10); val /= 10; s[1] = (char)('0' + val % 10); s[0] = (char)('0' + val / 10); s += 4; } UINT_TO_STR_2('-', st.wMonth); UINT_TO_STR_2('-', st.wDay); if (includeTime) { UINT_TO_STR_2(' ', st.wHour); UINT_TO_STR_2(':', st.wMinute); if (includeSeconds) UINT_TO_STR_2(':', st.wSecond); } *s = 0; return true; } void ConvertFileTimeToString(const FILETIME &ft, wchar_t *dest, bool includeTime, bool includeSeconds) throw() { char s[32]; ConvertFileTimeToString(ft, s, includeTime, includeSeconds); for (unsigned i = 0;; i++) { unsigned char c = s[i]; dest[i] = c; if (c == 0) return; } } void ConvertPropVariantToShortString(const PROPVARIANT &prop, char *dest) throw() { *dest = 0; switch (prop.vt) { case VT_EMPTY: return; case VT_BSTR: dest[0] = '?'; dest[1] = 0; return; case VT_UI1: ConvertUInt32ToString(prop.bVal, dest); return; case VT_UI2: ConvertUInt32ToString(prop.uiVal, dest); return; case VT_UI4: ConvertUInt32ToString(prop.ulVal, dest); return; case VT_UI8: ConvertUInt64ToString(prop.uhVal.QuadPart, dest); return; case VT_FILETIME: ConvertFileTimeToString(prop.filetime, dest, true, true); return; // case VT_I1: return ConvertInt64ToString(prop.cVal, dest); return; case VT_I2: ConvertInt64ToString(prop.iVal, dest); return; case VT_I4: ConvertInt64ToString(prop.lVal, dest); return; case VT_I8: ConvertInt64ToString(prop.hVal.QuadPart, dest); return; case VT_BOOL: dest[0] = VARIANT_BOOLToBool(prop.boolVal) ? '+' : '-'; dest[1] = 0; return; default: dest[0] = '?'; dest[1] = ':'; ConvertUInt64ToString(prop.vt, dest + 2); } } void ConvertPropVariantToShortString(const PROPVARIANT &prop, wchar_t *dest) throw() { *dest = 0; switch (prop.vt) { case VT_EMPTY: return; case VT_BSTR: dest[0] = '?'; dest[1] = 0; return; case VT_UI1: ConvertUInt32ToString(prop.bVal, dest); return; case VT_UI2: ConvertUInt32ToString(prop.uiVal, dest); return; case VT_UI4: ConvertUInt32ToString(prop.ulVal, dest); return; case VT_UI8: ConvertUInt64ToString(prop.uhVal.QuadPart, dest); return; case VT_FILETIME: ConvertFileTimeToString(prop.filetime, dest, true, true); return; // case VT_I1: return ConvertInt64ToString(prop.cVal, dest); return; case VT_I2: ConvertInt64ToString(prop.iVal, dest); return; case VT_I4: ConvertInt64ToString(prop.lVal, dest); return; case VT_I8: ConvertInt64ToString(prop.hVal.QuadPart, dest); return; case VT_BOOL: dest[0] = VARIANT_BOOLToBool(prop.boolVal) ? '+' : '-'; dest[1] = 0; return; default: dest[0] = '?'; dest[1] = ':'; ConvertUInt32ToString(prop.vt, dest + 2); } } src/libs/7zip/win/CPP/Windows/PropVariantConv.h000066400000000000000000000021401325366651500216150ustar00rootroot00000000000000// Windows/PropVariantConv.h #ifndef __PROP_VARIANT_CONV_H #define __PROP_VARIANT_CONV_H #include "../Common/MyTypes.h" // provide at least 32 bytes for buffer including zero-end bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime = true, bool includeSeconds = true) throw(); void ConvertFileTimeToString(const FILETIME &ft, wchar_t *s, bool includeTime = true, bool includeSeconds = true) throw(); // provide at least 32 bytes for buffer including zero-end // don't send VT_BSTR to these functions void ConvertPropVariantToShortString(const PROPVARIANT &prop, char *dest) throw(); void ConvertPropVariantToShortString(const PROPVARIANT &prop, wchar_t *dest) throw(); inline bool ConvertPropVariantToUInt64(const PROPVARIANT &prop, UInt64 &value) { switch (prop.vt) { case VT_UI8: value = (UInt64)prop.uhVal.QuadPart; return true; case VT_UI4: value = prop.ulVal; return true; case VT_UI2: value = prop.uiVal; return true; case VT_UI1: value = prop.bVal; return true; case VT_EMPTY: return false; default: throw 151199; } } #endif src/libs/7zip/win/CPP/Windows/SecurityUtils.cpp000066400000000000000000000115061325366651500217130ustar00rootroot00000000000000// Windows/SecurityUtils.cpp #include "StdAfx.h" #include "SecurityUtils.h" namespace NWindows { namespace NSecurity { /* bool MyLookupAccountSid(LPCTSTR systemName, PSID sid, CSysString &accountName, CSysString &domainName, PSID_NAME_USE sidNameUse) { DWORD accountNameSize = 0, domainNameSize = 0; if (!::LookupAccountSid(systemName, sid, accountName.GetBuffer(0), &accountNameSize, domainName.GetBuffer(0), &domainNameSize, sidNameUse)) { if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) return false; } bool result = BOOLToBool(::LookupAccountSid(systemName, sid, accountName.GetBuffer(accountNameSize), &accountNameSize, domainName.GetBuffer(domainNameSize), &domainNameSize, sidNameUse)); accountName.ReleaseBuffer(); domainName.ReleaseBuffer(); return result; } */ static void SetLsaString(LPWSTR src, PLSA_UNICODE_STRING dest) { int len = (int)wcslen(src); dest->Length = (USHORT)(len * sizeof(WCHAR)); dest->MaximumLength = (USHORT)((len + 1) * sizeof(WCHAR)); dest->Buffer = src; } /* static void MyLookupSids(CPolicy &policy, PSID ps) { LSA_REFERENCED_DOMAIN_LIST *referencedDomains = NULL; LSA_TRANSLATED_NAME *names = NULL; NTSTATUS nts = policy.LookupSids(1, &ps, &referencedDomains, &names); int res = LsaNtStatusToWinError(nts); LsaFreeMemory(referencedDomains); LsaFreeMemory(names); } */ #ifndef _UNICODE typedef BOOL (WINAPI * LookupAccountNameWP)( LPCWSTR lpSystemName, LPCWSTR lpAccountName, PSID Sid, LPDWORD cbSid, LPWSTR ReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse ); #endif static PSID GetSid(LPWSTR accountName) { #ifndef _UNICODE HMODULE hModule = GetModuleHandle(TEXT("Advapi32.dll")); if (hModule == NULL) return NULL; LookupAccountNameWP lookupAccountNameW = (LookupAccountNameWP)GetProcAddress(hModule, "LookupAccountNameW"); if (lookupAccountNameW == NULL) return NULL; #endif DWORD sidLen = 0, domainLen = 0; SID_NAME_USE sidNameUse; if (! #ifdef _UNICODE ::LookupAccountNameW #else lookupAccountNameW #endif (NULL, accountName, NULL, &sidLen, NULL, &domainLen, &sidNameUse)) { if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { PSID pSid = ::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sidLen); LPWSTR domainName = (LPWSTR)::HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (domainLen + 1) * sizeof(WCHAR)); BOOL res = #ifdef _UNICODE ::LookupAccountNameW #else lookupAccountNameW #endif (NULL, accountName, pSid, &sidLen, domainName, &domainLen, &sidNameUse); ::HeapFree(GetProcessHeap(), 0, domainName); if (res) return pSid; } } return NULL; } #define MY__SE_LOCK_MEMORY_NAME L"SeLockMemoryPrivilege" bool AddLockMemoryPrivilege() { CPolicy policy; LSA_OBJECT_ATTRIBUTES attr; attr.Length = sizeof(attr); attr.RootDirectory = NULL; attr.ObjectName = NULL; attr.Attributes = 0; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; if (policy.Open(NULL, &attr, // GENERIC_WRITE) POLICY_ALL_ACCESS) // STANDARD_RIGHTS_REQUIRED, // GENERIC_READ | GENERIC_EXECUTE | POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES) != 0) return false; LSA_UNICODE_STRING userRights; wchar_t s[128] = MY__SE_LOCK_MEMORY_NAME; SetLsaString(s, &userRights); WCHAR userName[256 + 2]; DWORD size = 256; if (!GetUserNameW(userName, &size)) return false; PSID psid = GetSid(userName); if (psid == NULL) return false; bool res = false; /* PLSA_UNICODE_STRING userRightsArray; ULONG countOfRights; NTSTATUS status = policy.EnumerateAccountRights(psid, &userRightsArray, &countOfRights); if (status != 0) return false; bool finded = false; for (ULONG i = 0; i < countOfRights; i++) { LSA_UNICODE_STRING &ur = userRightsArray[i]; if (ur.Length != s.Length() * sizeof(WCHAR)) continue; if (wcsncmp(ur.Buffer, s, s.Length()) != 0) continue; finded = true; res = true; break; } if (!finded) */ { /* LSA_ENUMERATION_INFORMATION *enums; ULONG countReturned; NTSTATUS status = policy.EnumerateAccountsWithUserRight(&userRights, &enums, &countReturned); if (status == 0) { for (ULONG i = 0; i < countReturned; i++) MyLookupSids(policy, enums[i].Sid); if (enums) ::LsaFreeMemory(enums); res = true; } */ NTSTATUS status = policy.AddAccountRights(psid, &userRights); if (status == 0) res = true; // ULONG res = LsaNtStatusToWinError(status); } HeapFree(GetProcessHeap(), 0, psid); return res; } }} src/libs/7zip/win/CPP/Windows/SecurityUtils.h000066400000000000000000000117121325366651500213570ustar00rootroot00000000000000// Windows/SecurityUtils.h #ifndef __WINDOWS_SECURITY_UTILS_H #define __WINDOWS_SECURITY_UTILS_H #include #include "Defs.h" namespace NWindows { namespace NSecurity { class CAccessToken { HANDLE _handle; public: CAccessToken(): _handle(NULL) {}; ~CAccessToken() { Close(); } bool Close() { if (_handle == NULL) return true; bool res = BOOLToBool(::CloseHandle(_handle)); if (res) _handle = NULL; return res; } bool OpenProcessToken(HANDLE processHandle, DWORD desiredAccess) { Close(); return BOOLToBool(::OpenProcessToken(processHandle, desiredAccess, &_handle)); } /* bool OpenThreadToken(HANDLE threadHandle, DWORD desiredAccess, bool openAsSelf) { Close(); return BOOLToBool(::OpenTreadToken(threadHandle, desiredAccess, BoolToBOOL(anOpenAsSelf), &_handle)); } */ bool AdjustPrivileges(bool disableAllPrivileges, PTOKEN_PRIVILEGES newState, DWORD bufferLength, PTOKEN_PRIVILEGES previousState, PDWORD returnLength) { return BOOLToBool(::AdjustTokenPrivileges(_handle, BoolToBOOL(disableAllPrivileges), newState, bufferLength, previousState, returnLength)); } bool AdjustPrivileges(bool disableAllPrivileges, PTOKEN_PRIVILEGES newState) { return AdjustPrivileges(disableAllPrivileges, newState, 0, NULL, NULL); } bool AdjustPrivileges(PTOKEN_PRIVILEGES newState) { return AdjustPrivileges(false, newState); } }; #ifndef _UNICODE typedef NTSTATUS (NTAPI *LsaOpenPolicyP)(PLSA_UNICODE_STRING SystemName, PLSA_OBJECT_ATTRIBUTES ObjectAttributes, ACCESS_MASK DesiredAccess, PLSA_HANDLE PolicyHandle); typedef NTSTATUS (NTAPI *LsaCloseP)(LSA_HANDLE ObjectHandle); typedef NTSTATUS (NTAPI *LsaAddAccountRightsP)(LSA_HANDLE PolicyHandle, PSID AccountSid, PLSA_UNICODE_STRING UserRights, ULONG CountOfRights ); #define MY_STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L) #endif struct CPolicy { protected: LSA_HANDLE _handle; #ifndef _UNICODE HMODULE hModule; #endif public: operator LSA_HANDLE() const { return _handle; } CPolicy(): _handle(NULL) { #ifndef _UNICODE hModule = GetModuleHandle(TEXT("Advapi32.dll")); #endif }; ~CPolicy() { Close(); } NTSTATUS Open(PLSA_UNICODE_STRING systemName, PLSA_OBJECT_ATTRIBUTES objectAttributes, ACCESS_MASK desiredAccess) { #ifndef _UNICODE if (hModule == NULL) return MY_STATUS_NOT_IMPLEMENTED; LsaOpenPolicyP lsaOpenPolicy = (LsaOpenPolicyP)GetProcAddress(hModule, "LsaOpenPolicy"); if (lsaOpenPolicy == NULL) return MY_STATUS_NOT_IMPLEMENTED; #endif Close(); return #ifdef _UNICODE ::LsaOpenPolicy #else lsaOpenPolicy #endif (systemName, objectAttributes, desiredAccess, &_handle); } NTSTATUS Close() { if (_handle == NULL) return 0; #ifndef _UNICODE if (hModule == NULL) return MY_STATUS_NOT_IMPLEMENTED; LsaCloseP lsaClose = (LsaCloseP)GetProcAddress(hModule, "LsaClose"); if (lsaClose == NULL) return MY_STATUS_NOT_IMPLEMENTED; #endif NTSTATUS res = #ifdef _UNICODE ::LsaClose #else lsaClose #endif (_handle); _handle = NULL; return res; } NTSTATUS EnumerateAccountsWithUserRight(PLSA_UNICODE_STRING userRights, PLSA_ENUMERATION_INFORMATION *enumerationBuffer, PULONG countReturned) { return LsaEnumerateAccountsWithUserRight(_handle, userRights, (void **)enumerationBuffer, countReturned); } NTSTATUS EnumerateAccountRights(PSID sid, PLSA_UNICODE_STRING* userRights, PULONG countOfRights) { return ::LsaEnumerateAccountRights(_handle, sid, userRights, countOfRights); } NTSTATUS LookupSids(ULONG count, PSID* sids, PLSA_REFERENCED_DOMAIN_LIST* referencedDomains, PLSA_TRANSLATED_NAME* names) { return LsaLookupSids(_handle, count, sids, referencedDomains, names); } NTSTATUS AddAccountRights(PSID accountSid, PLSA_UNICODE_STRING userRights, ULONG countOfRights) { #ifndef _UNICODE if (hModule == NULL) return MY_STATUS_NOT_IMPLEMENTED; LsaAddAccountRightsP lsaAddAccountRights = (LsaAddAccountRightsP)GetProcAddress(hModule, "LsaAddAccountRights"); if (lsaAddAccountRights == NULL) return MY_STATUS_NOT_IMPLEMENTED; #endif return #ifdef _UNICODE ::LsaAddAccountRights #else lsaAddAccountRights #endif (_handle, accountSid, userRights, countOfRights); } NTSTATUS AddAccountRights(PSID accountSid, PLSA_UNICODE_STRING userRights) { return AddAccountRights(accountSid, userRights, 1); } NTSTATUS RemoveAccountRights(PSID accountSid, bool allRights, PLSA_UNICODE_STRING userRights, ULONG countOfRights) { return LsaRemoveAccountRights(_handle, accountSid, (BOOLEAN)(allRights ? TRUE : FALSE), userRights, countOfRights); } }; bool AddLockMemoryPrivilege(); }} #endif src/libs/7zip/win/CPP/Windows/StdAfx.h000066400000000000000000000001421325366651500177130ustar00rootroot00000000000000// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H #include "../Common/Common.h" #endif src/libs/7zip/win/CPP/Windows/Synchronization.cpp000066400000000000000000000002231325366651500222560ustar00rootroot00000000000000// Windows/Synchronization.cpp #include "StdAfx.h" #include "Synchronization.h" namespace NWindows { namespace NSynchronization { }} src/libs/7zip/win/CPP/Windows/Synchronization.h000066400000000000000000000101741325366651500217310ustar00rootroot00000000000000// Windows/Synchronization.h #ifndef __WINDOWS_SYNCHRONIZATION_H #define __WINDOWS_SYNCHRONIZATION_H #include "../../C/Threads.h" #include "Defs.h" #ifdef _WIN32 #include "Handle.h" #endif namespace NWindows { namespace NSynchronization { class CBaseEvent { protected: ::CEvent _object; public: bool IsCreated() { return Event_IsCreated(&_object) != 0; } operator HANDLE() { return _object; } CBaseEvent() { Event_Construct(&_object); } ~CBaseEvent() { Close(); } WRes Close() { return Event_Close(&_object); } #ifdef _WIN32 WRes Create(bool manualReset, bool initiallyOwn, LPCTSTR name = NULL, LPSECURITY_ATTRIBUTES sa = NULL) { _object = ::CreateEvent(sa, BoolToBOOL(manualReset), BoolToBOOL(initiallyOwn), name); if (name == NULL && _object != 0) return 0; return ::GetLastError(); } WRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name) { _object = ::OpenEvent(desiredAccess, BoolToBOOL(inheritHandle), name); if (_object != 0) return 0; return ::GetLastError(); } #endif WRes Set() { return Event_Set(&_object); } // bool Pulse() { return BOOLToBool(::PulseEvent(_handle)); } WRes Reset() { return Event_Reset(&_object); } WRes Lock() { return Event_Wait(&_object); } }; class CManualResetEvent: public CBaseEvent { public: WRes Create(bool initiallyOwn = false) { return ManualResetEvent_Create(&_object, initiallyOwn ? 1: 0); } WRes CreateIfNotCreated() { if (IsCreated()) return 0; return ManualResetEvent_CreateNotSignaled(&_object); } #ifdef _WIN32 WRes CreateWithName(bool initiallyOwn, LPCTSTR name) { return CBaseEvent::Create(true, initiallyOwn, name); } #endif }; class CAutoResetEvent: public CBaseEvent { public: WRes Create() { return AutoResetEvent_CreateNotSignaled(&_object); } WRes CreateIfNotCreated() { if (IsCreated()) return 0; return AutoResetEvent_CreateNotSignaled(&_object); } }; #ifdef _WIN32 class CObject: public CHandle { public: WRes Lock(DWORD timeoutInterval = INFINITE) { return (::WaitForSingleObject(_handle, timeoutInterval) == WAIT_OBJECT_0 ? 0 : ::GetLastError()); } }; class CMutex: public CObject { public: WRes Create(bool initiallyOwn, LPCTSTR name = NULL, LPSECURITY_ATTRIBUTES sa = NULL) { _handle = ::CreateMutex(sa, BoolToBOOL(initiallyOwn), name); if (name == NULL && _handle != 0) return 0; return ::GetLastError(); } #ifndef UNDER_CE WRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name) { _handle = ::OpenMutex(desiredAccess, BoolToBOOL(inheritHandle), name); if (_handle != 0) return 0; return ::GetLastError(); } #endif WRes Release() { return ::ReleaseMutex(_handle) ? 0 : ::GetLastError(); } }; class CMutexLock { CMutex *_object; public: CMutexLock(CMutex &object): _object(&object) { _object->Lock(); } ~CMutexLock() { _object->Release(); } }; #endif class CSemaphore { ::CSemaphore _object; public: CSemaphore() { Semaphore_Construct(&_object); } ~CSemaphore() { Close(); } WRes Close() { return Semaphore_Close(&_object); } operator HANDLE() { return _object; } WRes Create(UInt32 initiallyCount, UInt32 maxCount) { return Semaphore_Create(&_object, initiallyCount, maxCount); } WRes Release() { return Semaphore_Release1(&_object); } WRes Release(UInt32 releaseCount) { return Semaphore_ReleaseN(&_object, releaseCount); } WRes Lock() { return Semaphore_Wait(&_object); } }; class CCriticalSection { ::CCriticalSection _object; public: CCriticalSection() { CriticalSection_Init(&_object); } ~CCriticalSection() { CriticalSection_Delete(&_object); } void Enter() { CriticalSection_Enter(&_object); } void Leave() { CriticalSection_Leave(&_object); } }; class CCriticalSectionLock { CCriticalSection *_object; void Unlock() { _object->Leave(); } public: CCriticalSectionLock(CCriticalSection &object): _object(&object) {_object->Enter(); } ~CCriticalSectionLock() { Unlock(); } }; }} #endif src/libs/7zip/win/CPP/Windows/System.cpp000066400000000000000000000031571325366651500203520ustar00rootroot00000000000000// Windows/System.cpp #include "StdAfx.h" #include "../Common/Defs.h" #include "System.h" namespace NWindows { namespace NSystem { UInt32 GetNumberOfProcessors() { SYSTEM_INFO systemInfo; GetSystemInfo(&systemInfo); return (UInt32)systemInfo.dwNumberOfProcessors; } #ifndef UNDER_CE #if !defined(_WIN64) && defined(__GNUC__) typedef struct _MY_MEMORYSTATUSEX { DWORD dwLength; DWORD dwMemoryLoad; DWORDLONG ullTotalPhys; DWORDLONG ullAvailPhys; DWORDLONG ullTotalPageFile; DWORDLONG ullAvailPageFile; DWORDLONG ullTotalVirtual; DWORDLONG ullAvailVirtual; DWORDLONG ullAvailExtendedVirtual; } MY_MEMORYSTATUSEX, *MY_LPMEMORYSTATUSEX; #else #define MY_MEMORYSTATUSEX MEMORYSTATUSEX #define MY_LPMEMORYSTATUSEX LPMEMORYSTATUSEX #endif typedef BOOL (WINAPI *GlobalMemoryStatusExP)(MY_LPMEMORYSTATUSEX lpBuffer); #endif UInt64 GetRamSize() { #ifndef UNDER_CE MY_MEMORYSTATUSEX stat; stat.dwLength = sizeof(stat); #endif #ifdef _WIN64 if (!::GlobalMemoryStatusEx(&stat)) return 0; return MyMin(stat.ullTotalVirtual, stat.ullTotalPhys); #else #ifndef UNDER_CE GlobalMemoryStatusExP globalMemoryStatusEx = (GlobalMemoryStatusExP) ::GetProcAddress(::GetModuleHandle(TEXT("kernel32.dll")), "GlobalMemoryStatusEx"); if (globalMemoryStatusEx != 0 && globalMemoryStatusEx(&stat)) return MyMin(stat.ullTotalVirtual, stat.ullTotalPhys); #endif { MEMORYSTATUS stat; stat.dwLength = sizeof(stat); ::GlobalMemoryStatus(&stat); return MyMin(stat.dwTotalVirtual, stat.dwTotalPhys); } #endif } }} src/libs/7zip/win/CPP/Windows/System.h000066400000000000000000000003471325366651500200150ustar00rootroot00000000000000// Windows/System.h #ifndef __WINDOWS_SYSTEM_H #define __WINDOWS_SYSTEM_H #include "../Common/MyTypes.h" namespace NWindows { namespace NSystem { UInt32 GetNumberOfProcessors(); UInt64 GetRamSize(); }} #endif src/libs/7zip/win/CPP/Windows/Thread.h000066400000000000000000000022231325366651500177330ustar00rootroot00000000000000// Windows/Thread.h #ifndef __WINDOWS_THREAD_H #define __WINDOWS_THREAD_H #include "../../C/Threads.h" #include "Defs.h" namespace NWindows { class CThread { ::CThread thread; public: CThread() { Thread_Construct(&thread); } ~CThread() { Close(); } bool IsCreated() { return Thread_WasCreated(&thread) != 0; } WRes Close() { return Thread_Close(&thread); } WRes Create(THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter) { return Thread_Create(&thread, startAddress, parameter); } WRes Wait() { return Thread_Wait(&thread); } #ifdef _WIN32 operator HANDLE() { return thread; } void Attach(HANDLE handle) { thread = handle; } HANDLE Detach() { HANDLE h = thread; thread = NULL; return h; } DWORD Resume() { return ::ResumeThread(thread); } DWORD Suspend() { return ::SuspendThread(thread); } bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread, exitCode)); } int GetPriority() { return ::GetThreadPriority(thread); } bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread, priority)); } #endif }; } #endif src/libs/7zip/win/CPP/Windows/TimeUtils.cpp000066400000000000000000000131001325366651500207720ustar00rootroot00000000000000// Windows/TimeUtils.cpp #include "StdAfx.h" #include "Defs.h" #include "TimeUtils.h" namespace NWindows { namespace NTime { static const UInt32 kNumTimeQuantumsInSecond = 10000000; static const UInt32 kFileTimeStartYear = 1601; static const UInt32 kDosTimeStartYear = 1980; static const UInt32 kUnixTimeStartYear = 1970; static const UInt64 kUnixTimeOffset = (UInt64)60 * 60 * 24 * (89 + 365 * (kUnixTimeStartYear - kFileTimeStartYear)); static const UInt64 kNumSecondsInFileTime = (UInt64)(Int64)-1 / kNumTimeQuantumsInSecond; bool DosTimeToFileTime(UInt32 dosTime, FILETIME &ft) throw() { #if defined(_WIN32) && !defined(UNDER_CE) return BOOLToBool(::DosDateTimeToFileTime((UInt16)(dosTime >> 16), (UInt16)(dosTime & 0xFFFF), &ft)); #else ft.dwLowDateTime = 0; ft.dwHighDateTime = 0; UInt64 res; if (!GetSecondsSince1601(kDosTimeStartYear + (dosTime >> 25), (dosTime >> 21) & 0xF, (dosTime >> 16) & 0x1F, (dosTime >> 11) & 0x1F, (dosTime >> 5) & 0x3F, (dosTime & 0x1F) * 2, res)) return false; res *= kNumTimeQuantumsInSecond; ft.dwLowDateTime = (UInt32)res; ft.dwHighDateTime = (UInt32)(res >> 32); return true; #endif } static const UInt32 kHighDosTime = 0xFF9FBF7D; static const UInt32 kLowDosTime = 0x210000; #define PERIOD_4 (4 * 365 + 1) #define PERIOD_100 (PERIOD_4 * 25 - 1) #define PERIOD_400 (PERIOD_100 * 4 + 1) bool FileTimeToDosTime(const FILETIME &ft, UInt32 &dosTime) throw() { #if defined(_WIN32) && !defined(UNDER_CE) WORD datePart, timePart; if (!::FileTimeToDosDateTime(&ft, &datePart, &timePart)) { dosTime = (ft.dwHighDateTime >= 0x01C00000) ? kHighDosTime : kLowDosTime; return false; } dosTime = (((UInt32)datePart) << 16) + timePart; #else unsigned year, mon, day, hour, min, sec; UInt64 v64 = ft.dwLowDateTime | ((UInt64)ft.dwHighDateTime << 32); Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; unsigned temp; UInt32 v; v64 += (kNumTimeQuantumsInSecond * 2 - 1); v64 /= kNumTimeQuantumsInSecond; sec = (unsigned)(v64 % 60); v64 /= 60; min = (unsigned)(v64 % 60); v64 /= 60; hour = (unsigned)(v64 % 24); v64 /= 24; v = (UInt32)v64; year = (unsigned)(kFileTimeStartYear + v / PERIOD_400 * 400); v %= PERIOD_400; temp = (unsigned)(v / PERIOD_100); if (temp == 4) temp = 3; year += temp * 100; v -= temp * PERIOD_100; temp = v / PERIOD_4; if (temp == 25) temp = 24; year += temp * 4; v -= temp * PERIOD_4; temp = v / 365; if (temp == 4) temp = 3; year += temp; v -= temp * 365; if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ms[1] = 29; for (mon = 1; mon <= 12; mon++) { unsigned s = ms[mon - 1]; if (v < s) break; v -= s; } day = (unsigned)v + 1; dosTime = kLowDosTime; if (year < kDosTimeStartYear) return false; year -= kDosTimeStartYear; dosTime = kHighDosTime; if (year >= 128) return false; dosTime = (year << 25) | (mon << 21) | (day << 16) | (hour << 11) | (min << 5) | (sec >> 1); #endif return true; } void UnixTimeToFileTime(UInt32 unixTime, FILETIME &ft) throw() { UInt64 v = (kUnixTimeOffset + (UInt64)unixTime) * kNumTimeQuantumsInSecond; ft.dwLowDateTime = (DWORD)v; ft.dwHighDateTime = (DWORD)(v >> 32); } bool UnixTime64ToFileTime(Int64 unixTime, FILETIME &ft) throw() { if (unixTime > kNumSecondsInFileTime - kUnixTimeOffset) { ft.dwLowDateTime = ft.dwHighDateTime = (UInt32)(Int32)-1; return false; } Int64 v = (Int64)kUnixTimeOffset + unixTime; if (v < 0) { ft.dwLowDateTime = ft.dwHighDateTime = 0; return false; } UInt64 v2 = (UInt64)v * kNumTimeQuantumsInSecond; ft.dwLowDateTime = (DWORD)v2; ft.dwHighDateTime = (DWORD)(v2 >> 32); return true; } Int64 FileTimeToUnixTime64(const FILETIME &ft) throw() { UInt64 winTime = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime; return (Int64)(winTime / kNumTimeQuantumsInSecond) - kUnixTimeOffset; } bool FileTimeToUnixTime(const FILETIME &ft, UInt32 &unixTime) throw() { UInt64 winTime = (((UInt64)ft.dwHighDateTime) << 32) + ft.dwLowDateTime; winTime /= kNumTimeQuantumsInSecond; if (winTime < kUnixTimeOffset) { unixTime = 0; return false; } winTime -= kUnixTimeOffset; if (winTime > 0xFFFFFFFF) { unixTime = 0xFFFFFFFF; return false; } unixTime = (UInt32)winTime; return true; } bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned min, unsigned sec, UInt64 &resSeconds) throw() { resSeconds = 0; if (year < kFileTimeStartYear || year >= 10000 || month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || min > 59 || sec > 59) return false; UInt32 numYears = year - kFileTimeStartYear; UInt32 numDays = numYears * 365 + numYears / 4 - numYears / 100 + numYears / 400; Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ms[1] = 29; month--; for (unsigned i = 0; i < month; i++) numDays += ms[i]; numDays += day - 1; resSeconds = ((UInt64)(numDays * 24 + hour) * 60 + min) * 60 + sec; return true; } void GetCurUtcFileTime(FILETIME &ft) throw() { // Both variants provide same low resolution on WinXP: about 15 ms. // But GetSystemTimeAsFileTime is much faster. #ifdef UNDER_CE SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); #else GetSystemTimeAsFileTime(&ft); #endif } }} src/libs/7zip/win/CPP/Windows/TimeUtils.h000066400000000000000000000014411325366651500204440ustar00rootroot00000000000000// Windows/TimeUtils.h #ifndef __WINDOWS_TIME_UTILS_H #define __WINDOWS_TIME_UTILS_H #include "../Common/MyTypes.h" namespace NWindows { namespace NTime { bool DosTimeToFileTime(UInt32 dosTime, FILETIME &fileTime) throw(); bool FileTimeToDosTime(const FILETIME &fileTime, UInt32 &dosTime) throw(); void UnixTimeToFileTime(UInt32 unixTime, FILETIME &fileTime) throw(); bool UnixTime64ToFileTime(Int64 unixTime, FILETIME &fileTime) throw(); bool FileTimeToUnixTime(const FILETIME &fileTime, UInt32 &unixTime) throw(); Int64 FileTimeToUnixTime64(const FILETIME &ft) throw(); bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned min, unsigned sec, UInt64 &resSeconds) throw(); void GetCurUtcFileTime(FILETIME &ft) throw(); }} #endif src/libs/7zip/win/CPP/Windows/Windows.pri000066400000000000000000000023171325366651500205250ustar00rootroot00000000000000HEADERS += $$7ZIP_BASE/CPP/Windows/DLL.h \ $$7ZIP_BASE/CPP/Windows/Defs.h \ $$7ZIP_BASE/CPP/Windows/FileDir.h \ $$7ZIP_BASE/CPP/Windows/FileFind.h \ $$7ZIP_BASE/CPP/Windows/FileIO.h \ $$7ZIP_BASE/CPP/Windows/FileMapping.h \ $$7ZIP_BASE/CPP/Windows/FileName.h \ $$7ZIP_BASE/CPP/Windows/Handle.h \ $$7ZIP_BASE/CPP/Windows/PropVariant.h \ $$7ZIP_BASE/CPP/Windows/PropVariantConv.h \ $$7ZIP_BASE/CPP/Windows/SecurityUtils.h \ $$7ZIP_BASE/CPP/Windows/StdAfx.h \ $$7ZIP_BASE/CPP/Windows/Synchronization.h \ $$7ZIP_BASE/CPP/Windows/System.h \ $$7ZIP_BASE/CPP/Windows/Thread.h \ $$7ZIP_BASE/CPP/Windows/TimeUtils.h SOURCES += $$7ZIP_BASE/CPP/Windows/DLL.cpp \ $$7ZIP_BASE/CPP/Windows/FileDir.cpp \ $$7ZIP_BASE/CPP/Windows/FileFind.cpp \ $$7ZIP_BASE/CPP/Windows/FileIO.cpp \ $$7ZIP_BASE/CPP/Windows/FileLink.cpp \ $$7ZIP_BASE/CPP/Windows/FileName.cpp \ $$7ZIP_BASE/CPP/Windows/PropVariant.cpp \ $$7ZIP_BASE/CPP/Windows/PropVariantConv.cpp \ $$7ZIP_BASE/CPP/Windows/SecurityUtils.cpp \ $$7ZIP_BASE/CPP/Windows/Synchronization.cpp \ $$7ZIP_BASE/CPP/Windows/System.cpp \ $$7ZIP_BASE/CPP/Windows/TimeUtils.cpp src/libs/7zip/win/win.pri000066400000000000000000000006111325366651500156070ustar00rootroot00000000000000include(C/C.pri) include(CPP/7zip/7zip.pri) include(CPP/7zip/Archive/7z/7z.pri) include(CPP/7zip/Archive/Archive.pri) include(CPP/7zip/Archive/Common/Common.pri) include(CPP/7zip/Common/Common.pri) include(CPP/7zip/Compress/Compress.pri) include(CPP/7zip/UI/Common/Common.pri) include(CPP/7zip/UI/Console/Console.pri) include(CPP/Common/Common.pri) include(CPP/Windows/Windows.pri) src/libs/installer/000077500000000000000000000000001325366651500146075ustar00rootroot00000000000000src/libs/installer/abstractfiletask.cpp000066400000000000000000000130351325366651500206430ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "abstractfiletask.h" namespace QInstaller { /*! \inmodule QtInstallerFramework \class QInstaller::AbstractFileTask \brief The AbstractFileTask class is the base class of file related tasks. The class is not usable as a standalone class but provides common functionality when subclassed. */ /*! \inmodule QtInstallerFramework \namespace TaskRole \brief Contains identifiers for tasks. */ /*! \enum TaskRole::TaskRole \value Checksum \value TaskItem \value SourceFile \value TargetFile \value UserRole The first role that can be used for user-specific purposes. */ /*! \inmodule QtInstallerFramework \class QInstaller::FileTaskItem \brief The FileTaskItem class represents an item in a file task object. */ /*! \fn FileTaskItem::FileTaskItem() Creates a file task item. */ /*! \fn FileTaskItem::FileTaskItem(const QString &s) Creates a file task item using the source specified by \a s. */ /*! \fn FileTaskItem::FileTaskItem(const QString &s, const QString &t) Creates a file task item using the source specified by \a s and target specified by \a t. */ /*! \fn FileTaskItem::source() const Returns the source file of the file task item. */ /*! \fn FileTaskItem::target() const Returns the target file of the file task item. */ /*! \inmodule QtInstallerFramework \class QInstaller::FileTaskResult \brief The FileTaskResult class represents the results of a file task. */ /*! \fn FileTaskResult::FileTaskResult() Creates file task results. */ /*! \fn FileTaskResult::FileTaskResult(const QString &t, const QByteArray &c, const FileTaskItem &i) Creates file task results using the target file specified by \a t, checksum specified by \a c, and file task item specified by \a i. */ /*! \fn FileTaskResult::target() const Returns the target file of the task result. */ /*! \fn FileTaskResult::checkSum() const Returns the checksum of the task result. */ /*! \fn FileTaskResult::taskItem() const Returns file task items. */ /*! Constructs an empty abstract file task object. */ AbstractFileTask::AbstractFileTask() { registerMetaTypes(); } /*! \fn AbstractFileTask::~AbstractFileTask() Destroys the abstract file task object. */ /*! Constructs a new abstract file task object with \a source. */ AbstractFileTask::AbstractFileTask(const QString &source) { registerMetaTypes(); setTaskItem(FileTaskItem(source)); } /*! Constructs a new abstract file task object with \a item. */ AbstractFileTask::AbstractFileTask(const FileTaskItem &item) { registerMetaTypes(); setTaskItem(item); } /*! Constructs a new abstract file task object with \a source and \a target. */ AbstractFileTask::AbstractFileTask(const QString &source, const QString &target) { registerMetaTypes(); setTaskItem(FileTaskItem(source, target)); } /*! Returns a list of file task items this task is working on. */ QList AbstractFileTask::taskItems() const { QReadLocker _(&m_lock); return m_items; } /*! Sets a file task \a item this task is working on. */ void AbstractFileTask::setTaskItem(const FileTaskItem &item) { clearTaskItems(); addTaskItem(item); } // -- protected /*! \internal */ void AbstractFileTask::clearTaskItems() { QWriteLocker _(&m_lock); m_items.clear(); } /*! \internal */ void AbstractFileTask::addTaskItem(const FileTaskItem &item) { QWriteLocker _(&m_lock); m_items.append(item); } /*! \internal */ void AbstractFileTask::setTaskItems(const QList &items) { clearTaskItems(); addTaskItems(items); } /*! \internal */ void AbstractFileTask::addTaskItems(const QList &items) { QWriteLocker _(&m_lock); m_items.append(items); } // -- private /*! \internal */ void AbstractFileTask::registerMetaTypes() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } } // namespace QInstaller src/libs/installer/abstractfiletask.h000066400000000000000000000072271325366651500203160ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef ABSTRACTFILETASK_H #define ABSTRACTFILETASK_H #include "abstracttask.h" #include "installer_global.h" #include #include namespace QInstaller { namespace TaskRole { enum TaskRole { Checksum, TaskItem, SourceFile, TargetFile, UserRole = 1000 }; } class FileTaskItem : public AbstractTaskData { public: FileTaskItem() {} explicit FileTaskItem(const QString &s) { insert(TaskRole::SourceFile, s); } FileTaskItem(const QString &s, const QString &t) { insert(TaskRole::SourceFile, s); insert(TaskRole::TargetFile, t); } QString source() const { return value(TaskRole::SourceFile).toString(); } QString target() const { return value(TaskRole::TargetFile).toString(); } }; class FileTaskResult : public AbstractTaskData { public: FileTaskResult() {} FileTaskResult(const QString &t, const QByteArray &c, const FileTaskItem &i) { insert(TaskRole::Checksum, c); insert(TaskRole::TargetFile, t); insert(TaskRole::TaskItem, QVariant::fromValue(i)); } QString target() const { return value(TaskRole::TargetFile).toString(); } QByteArray checkSum() const { return value(TaskRole::Checksum).toByteArray(); } FileTaskItem taskItem() const { return value(TaskRole::TaskItem).value(); } }; class INSTALLER_EXPORT AbstractFileTask : public AbstractTask { Q_OBJECT Q_DISABLE_COPY(AbstractFileTask) public: AbstractFileTask(); virtual ~AbstractFileTask() {} explicit AbstractFileTask(const QString &source); explicit AbstractFileTask(const FileTaskItem &item); AbstractFileTask(const QString &source, const QString &target); QList taskItems() const; void setTaskItem(const FileTaskItem &item); protected: void clearTaskItems(); void addTaskItem(const FileTaskItem &item); void setTaskItems(const QList &items); void addTaskItems(const QList &items); private: void registerMetaTypes(); private: QList m_items; mutable QReadWriteLock m_lock; }; } // namespace QInstaller Q_DECLARE_METATYPE(QInstaller::FileTaskItem) Q_DECLARE_METATYPE(QInstaller::FileTaskResult) Q_DECLARE_METATYPE(QInstaller::TaskException) #endif // ABSTRACTFILETASK_H src/libs/installer/abstracttask.h000066400000000000000000000046611325366651500174550ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef ABSTRACTTASK_H #define ABSTRACTTASK_H #include "runextensions.h" #include namespace QInstaller { class AbstractTaskData { public: AbstractTaskData() {} virtual ~AbstractTaskData() = 0; QVariant value(int role) const { return m_data.value(role); } void insert(int key, const QVariant &value) { m_data.insert(key, value); } private: QHash m_data; }; inline AbstractTaskData::~AbstractTaskData() {} class TaskException : public QException { public: TaskException() {} ~TaskException() throw() {} explicit TaskException(const QString &message) : m_message(message) {} void raise() const { throw *this; } QString message() const { return m_message; } TaskException *clone() const { return new TaskException(*this); } private: QString m_message; }; template class AbstractTask : public QObject { Q_DISABLE_COPY(AbstractTask) public: AbstractTask() {} virtual ~AbstractTask() {} virtual void doTask(QFutureInterface &futureInterface) = 0; }; } // namespace QInstaller #endif // ABSTRACTTASK_H src/libs/installer/adminauthorization.h000066400000000000000000000031461325366651500206750ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef ADMINAUTHORIZATION_H #define ADMINAUTHORIZATION_H #include namespace QInstaller { class AdminAuthorization { public: static bool hasAdminRights(); static bool execute(QWidget *parent, const QString &programs, const QStringList &arguments); }; } // namespace QInstaller #endif // ADMINAUTHORIZATION_H src/libs/installer/adminauthorization_mac.cpp000066400000000000000000000057021325366651500220500ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "adminauthorization.h" #include #include #include #include #include namespace QInstaller { bool AdminAuthorization::execute(QWidget *, const QString &program, const QStringList &arguments) { AuthorizationRef authorizationRef; OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); if (status != errAuthorizationSuccess) return false; AuthorizationItem item = { kAuthorizationRightExecute, 0, 0, 0 }; AuthorizationRights rights = { 1, &item }; const AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; status = AuthorizationCopyRights(authorizationRef, &rights, kAuthorizationEmptyEnvironment, flags, 0); if (status != errAuthorizationSuccess) return false; QVector args; QVector utf8Args; foreach (const QString &argument, arguments) { utf8Args.push_back(argument.toUtf8()); args.push_back(utf8Args.last().data()); } args.push_back(0); const QByteArray utf8Program = program.toUtf8(); status = AuthorizationExecuteWithPrivileges(authorizationRef, utf8Program.data(), kAuthorizationFlagDefaults, args.data(), 0); AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights); return status == errAuthorizationSuccess; } bool AdminAuthorization::hasAdminRights() { return geteuid() == 0; } } // namespace QInstaller src/libs/installer/adminauthorization_win.cpp000066400000000000000000000103731325366651500221050ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "adminauthorization.h" #include "utils.h" #include #include #include #include #ifdef Q_CC_MINGW # ifndef SEE_MASK_NOASYNC # define SEE_MASK_NOASYNC 0x00000100 # endif #endif namespace QInstaller { struct DeCoInitializer { DeCoInitializer() : neededCoInit(CoInitialize(NULL) == S_OK) { } ~DeCoInitializer() { if (neededCoInit) CoUninitialize(); } bool neededCoInit; }; bool AdminAuthorization::hasAdminRights() { SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; PSID adminGroup; // Initialize SID. if (!AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroup)) return false; BOOL isInAdminGroup = FALSE; if (!CheckTokenMembership(0, adminGroup, &isInAdminGroup)) isInAdminGroup = FALSE; FreeSid(adminGroup); return isInAdminGroup; } bool AdminAuthorization::execute(QWidget *, const QString &program, const QStringList &arguments) { DeCoInitializer _; // AdminAuthorization::execute uses UAC to ask for admin privileges. If the user is no // administrator yet and the computer's policies are set to not use UAC (which is the case // in some corporate networks), the call to execute() will simply succeed and not at all // launch the child process. To avoid this, we detect this situation here and return early. if (!hasAdminRights()) { QLatin1String key("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" "Policies\\System"); QSettings registry(key, QSettings::NativeFormat); const QVariant enableLUA = registry.value(QLatin1String("EnableLUA")); if ((enableLUA.type() == QVariant::Int) && (enableLUA.toInt() == 0)) return false; } const QString file = QDir::toNativeSeparators(program); const QString args = QInstaller::createCommandline(QString(), arguments); SHELLEXECUTEINFOW shellExecuteInfo = { 0 }; shellExecuteInfo.nShow = SW_HIDE; shellExecuteInfo.lpVerb = L"runas"; shellExecuteInfo.lpFile = (wchar_t *)file.utf16(); shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFOW); shellExecuteInfo.lpParameters = (wchar_t *)args.utf16(); shellExecuteInfo.fMask = SEE_MASK_NOASYNC; qDebug() << "Starting elevated process" << file << "with arguments" << args; if (ShellExecuteExW(&shellExecuteInfo)) { qDebug() << "Finished starting elevated process."; return true; } else { qWarning() << "Error while starting elevated process" << program << ":" << QInstaller::windowsErrorString(GetLastError()); } return false; } } // namespace QInstaller src/libs/installer/adminauthorization_x11.cpp000066400000000000000000000212221325366651500217140ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "adminauthorization.h" #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_LINUX #include #include #else #ifdef Q_OS_FREEBSD #include #include #else #include #endif #endif #include #include #include #include #include #define SU_COMMAND "/usr/bin/sudo" //#define SU_COMMAND "/bin/echo" namespace QInstaller { static QString getPassword(QWidget *parent) { if (qobject_cast (qApp) != 0) { bool ok = false; const QString result = QInputDialog::getText(parent, QObject::tr("Authorization required"), QObject::tr("Enter your password to authorize for sudo:"), QLineEdit::Password, QString(), &ok); return ok ? result : QString(); } else { std::cout << QObject::tr("Authorization required").toStdString() << std::endl; std::cout << QObject::tr("Enter your password to authorize for sudo:").toStdString() << std::endl; std::string password; std::cin >> password; return QString::fromStdString(password); } } static void printError(QWidget *parent, const QString &value) { if (qobject_cast (qApp) != 0) { QMessageBox::critical(parent, QObject::tr( "Error acquiring admin rights" ), value, QMessageBox::Ok, QMessageBox::Ok); } else { std::cout << value.toStdString() << std::endl; } } bool AdminAuthorization::execute(QWidget *parent, const QString &program, const QStringList &arguments) { const QString fallback = program + QLatin1String(" ") + arguments.join(QLatin1String(" ")); qDebug() << "Fallback:" << fallback; // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it int masterFD = -1; int slaveFD = -1; char ptsn[ PATH_MAX ]; if (::openpty(&masterFD, &slaveFD, ptsn, 0, 0)) return false; masterFD = ::posix_openpt(O_RDWR | O_NOCTTY); if (masterFD < 0) return false; const QByteArray ttyName = ::ptsname(masterFD); if (::grantpt(masterFD)) { ::close(masterFD); return false; } ::revoke(ttyName); ::unlockpt(masterFD); slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY); if (slaveFD < 0) { ::close(masterFD); return false; } ::fcntl(masterFD, F_SETFD, FD_CLOEXEC); ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC); int pipedData[2]; if (pipe(pipedData) != 0) return false; int flags = ::fcntl(pipedData[0], F_GETFL); if (flags != -1) ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK); flags = ::fcntl(masterFD, F_GETFL); if (flags != -1) ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK); pid_t child = fork(); if (child < -1) { ::close(masterFD); ::close(slaveFD); ::close(pipedData[0]); ::close(pipedData[1]); return false; } // parent process else if (child > 0) { ::close(slaveFD); //close writing end of pipe ::close(pipedData[1]); QRegExp re(QLatin1String("[Pp]assword.*:")); QByteArray data; QByteArray errData; int bytes = 0; int errBytes = 0; char buf[1024]; char errBuf[1024]; int status; bool statusValid = false; while (bytes >= 0) { const pid_t waitResult = ::waitpid(child, &status, WNOHANG); if (waitResult == -1) { break; } if (waitResult == child) { statusValid = true; break; } bytes = ::read(masterFD, buf, 1023); if (bytes == -1 && errno == EAGAIN) bytes = 0; else if (bytes > 0) data.append(buf, bytes); errBytes = ::read(pipedData[0], errBuf, 1023); if (errBytes > 0) { errData.append(errBuf, errBytes); errBytes=0; } if (bytes > 0) { const QString line = QString::fromLatin1(data); if (re.indexIn(line) != -1) { const QString password = getPassword(parent); if (password.isEmpty()) { QByteArray pwd = password.toLatin1(); for (int i = 0; i < 3; ++i) { ::write(masterFD, pwd.data(), pwd.length()); ::write(masterFD, "\n", 1); } return false; } QByteArray pwd = password.toLatin1(); ::write(masterFD, pwd.data(), pwd.length()); ::write(masterFD, "\n", 1); ::read(masterFD, buf, pwd.length() + 1); } } if (bytes == 0) ::usleep(100000); } while (true) { errBytes = ::read(pipedData[0], errBuf, 1023); if (errBytes == -1 && errno == EAGAIN) { ::usleep(100000); continue; } if (errBytes <= 0) break; errData.append(errBuf, errBytes); } const bool success = statusValid && WIFEXITED(status) && WEXITSTATUS(status) == 0; if (!success && !errData.isEmpty()) { printError(parent, QString::fromLocal8Bit(errData.constData())); } ::close(pipedData[0]); return success; } // child process else { ::close(pipedData[0]); // Reset signal handlers for (int sig = 1; sig < NSIG; ++sig) signal(sig, SIG_DFL); signal(SIGHUP, SIG_IGN); ::setsid(); ::ioctl(slaveFD, TIOCSCTTY, 1); int pgrp = ::getpid(); ::tcsetpgrp(slaveFD, pgrp); ::dup2(slaveFD, 0); ::dup2(slaveFD, 1); ::dup2(pipedData[1], 2); // close all file descriptors struct rlimit rlp; getrlimit(RLIMIT_NOFILE, &rlp); for (int i = 3; i < static_cast(rlp.rlim_cur); ++i) ::close(i); char **argp = (char **) ::malloc((arguments.count() + 4) * sizeof(char *)); QList args; args.push_back(SU_COMMAND); args.push_back("-b"); args.push_back(program.toLocal8Bit()); for (QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it) args.push_back(it->toLocal8Bit()); int i = 0; for (QList::iterator it = args.begin(); it != args.end(); ++it, ++i) argp[i] = it->data(); argp[i] = 0; ::unsetenv("LANG"); ::unsetenv("LC_ALL"); int exitStatus = 0; if (::execv(SU_COMMAND, argp) == -1) exitStatus = -errno; _exit(exitStatus); return false; } } // has no guarantee to work bool AdminAuthorization::hasAdminRights() { return getuid() == 0; } } // namespace QInstaller src/libs/installer/binarycontent.cpp000066400000000000000000000305361325366651500202010ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "binarycontent.h" #include "binarylayout.h" #include "errors.h" #include "fileio.h" #include "fileutils.h" namespace QInstaller { /*! \class QInstaller::BinaryContent \inmodule QtInstallerFramework \brief The BinaryContent class handles binary information embedded into executables. The following types of binary information can be embedded into executable files: Qt resources, performed operations, and resource collections. The magic marker is a \c quint64 that identifies the kind of the binary: \c installer or \c uninstaller (maintenance tool). The magic cookie is a \c quint64 describing whether the binary is the file holding just data or whether it includes the executable as well. */ /*! Searches for the given magic cookie \a magicCookie starting from the end of the file \a in. Returns the position of the magic cookie inside the binary. Throws Error on failure. \note Searches through up to 1MB of data, if smaller, through the whole file. */ qint64 BinaryContent::findMagicCookie(QFile *in, quint64 magicCookie) { Q_ASSERT(in); Q_ASSERT(in->isOpen()); Q_ASSERT(in->isReadable()); const qint64 fileSize = in->size(); const size_t markerSize = sizeof(qint64); const qint64 maxSearch = qMin((1024LL * 1024LL), fileSize); QByteArray data(maxSearch, Qt::Uninitialized); uchar *const mapped = in->map(fileSize - maxSearch, maxSearch); if (!mapped) { // Fallback to read the file content in case we can't map it. // Note: Failing to map the file can happen for example while having a remote connection // established to the privileged server process and we do not support map over the socket. const int pos = in->pos(); try { in->seek(fileSize - maxSearch); QInstaller::blockingRead(in, data.data(), maxSearch); in->seek(pos); } catch (const Error &error) { in->seek(pos); throw error; } } else { // map does not change QFile::pos() data = QByteArray((const char*) mapped, maxSearch); in->unmap(mapped); } qint64 searched = maxSearch - markerSize; while (searched >= 0) { if (memcmp(&magicCookie, (data.data() + searched), markerSize) == 0) return (fileSize - maxSearch) + searched; --searched; } throw Error(QCoreApplication::translate("QInstaller", "No marker found, stopped after %1.") .arg(humanReadableSize(maxSearch))); return -1; // never reached } /*! Tries to read the binary layout of the file \a file. It starts searching from the end of the file \a file for the given \a magicCookie using findMagicCookie(). If the cookie was found, it fills a BinaryLayout structure and returns it. Throws Error on failure. */ BinaryLayout BinaryContent::binaryLayout(QFile *file, quint64 magicCookie) { BinaryLayout layout; layout.endOfBinaryContent = BinaryContent::findMagicCookie(file, magicCookie) + sizeof(qint64); const qint64 posOfMetaDataCount = layout.endOfBinaryContent - (4 * sizeof(qint64)); if (!file->seek(posOfMetaDataCount)) { throw QInstaller::Error(QCoreApplication::translate("BinaryLayout", "Cannot seek to %1 to read the embedded meta data count.").arg(posOfMetaDataCount)); } // read the meta resources count const qint64 metaResourcesCount = QInstaller::retrieveInt64(file); const qint64 posOfResourceCollectionsSegment = layout.endOfBinaryContent - ((metaResourcesCount * (2 * sizeof(qint64))) // minus the size of the meta data segments + (8 * sizeof(qint64))); // meta count, offset/length collection index, marker, cookie... if (!file->seek(posOfResourceCollectionsSegment)) { throw Error(QCoreApplication::translate("BinaryLayout", "Cannot seek to %1 to read the resource collection segment.") .arg(posOfResourceCollectionsSegment)); } // read the resource collection index offset and length layout.resourceCollectionsSegment = QInstaller::retrieveInt64Range(file); // read the meta data resource segments for (int i = 0; i < metaResourcesCount; ++i) layout.metaResourceSegments.append(QInstaller::retrieveInt64Range(file)); if (metaResourcesCount != layout.metaResourceSegments.count()) { throw Error(QCoreApplication::translate("BinaryLayout", "Unexpected mismatch of meta resources. Read %1, expected: %2.") .arg(layout.metaResourceSegments.count()).arg(metaResourcesCount)); } // read the operations offset and length layout.operationsSegment = QInstaller::retrieveInt64Range(file); // resources count Q_UNUSED(QInstaller::retrieveInt64(file)) // read it, but deliberately not used // read the binary content size layout.binaryContentSize = QInstaller::retrieveInt64(file); layout.endOfExectuable = layout.endOfBinaryContent - layout.binaryContentSize; layout.magicMarker = QInstaller::retrieveInt64(file); layout.magicCookie = QInstaller::retrieveInt64(file); // adjust the offsets to match the actual binary for (int i = 0; i < layout.metaResourceSegments.count(); ++i) layout.metaResourceSegments[i].move(layout.endOfExectuable); layout.metaResourcesSegment = Range::fromStartAndEnd(layout.metaResourceSegments .first().start(), layout.metaResourceSegments.last().end()); layout.operationsSegment.move(layout.endOfExectuable); layout.resourceCollectionsSegment.move(layout.endOfExectuable); return layout; } /*! Reads the binary content of the given file \a file. It starts by reading the binary layout of the file using binaryLayout() using \a magicCookie. Throws Error on failure. If \a operations is not 0, it is set to the performed operations from a previous run of for example the maintenance tool. If \a manager is not 0, it is first cleared and then set to the resource collections embedded into the binary. If \a magicMarker is not 0, it is set to the magic marker found in the binary. */ void BinaryContent::readBinaryContent(QFile *file, QList *operations, ResourceCollectionManager *manager, qint64 *magicMarker, quint64 magicCookie) { const BinaryLayout layout = BinaryContent::binaryLayout(file, magicCookie); if (manager) manager->clear(); if (manager) { // append the meta resources ResourceCollection metaResources("QResources"); foreach (const Range &segment, layout.metaResourceSegments) { metaResources.appendResource(QSharedPointer(new Resource(file->fileName(), segment))); } manager->insertCollection(metaResources); } if (operations) { const qint64 posOfOperationsBlock = layout.operationsSegment.start(); if (!file->seek(posOfOperationsBlock)) { throw Error(QCoreApplication::translate("BinaryContent", "Cannot seek to %1 to read the operation data.").arg(posOfOperationsBlock)); } // read the operations count qint64 operationsCount = QInstaller::retrieveInt64(file); // read the operations for (int i = 0; i < operationsCount; ++i) { const QString name = QInstaller::retrieveString(file); const QString xml = QInstaller::retrieveString(file); operations->append(OperationBlob(name, xml)); } // operations count Q_UNUSED(QInstaller::retrieveInt64(file)) // read it, but deliberately not used } if (manager) { // read the collection index and data const qint64 posOfResourceCollectionBlock = layout.resourceCollectionsSegment.start(); if (!file->seek(posOfResourceCollectionBlock)) { throw Error(QCoreApplication::translate("BinaryContent", "Cannot seek to %1 to " "read the resource collection block.").arg(posOfResourceCollectionBlock)); } manager->read(file, layout.endOfExectuable); } if (magicMarker) *magicMarker = layout.magicMarker; } /*! Writes the binary content to the given file \a out. Throws Error on failure. The binary content is written in the following order: \list \li Meta resources \a manager \li Operations \a operations \li Resource collections \a manager \li Magic marker \a magicMarker \li Magic cookie \a magicCookie \endlist For more information see the BinaryLayout documentation. */ void BinaryContent::writeBinaryContent(QFile *out, const QList &operations, const ResourceCollectionManager &manager, qint64 magicMarker, quint64 magicCookie) { const qint64 endOfBinary = out->pos(); ResourceCollectionManager localManager = manager; // resources qint64 pos = out->pos(); QVector > metaResourceSegments; const ResourceCollection collection = localManager.collectionByName("QResources"); foreach (const QSharedPointer &resource, collection.resources()) { const bool isOpen = resource->isOpen(); if ((!isOpen) && (!resource->open())) { throw Error(QCoreApplication::translate("BinaryContent", "Cannot open meta resource %1.").arg(resource->errorString())); } resource->seek(0); resource->copyData(out); metaResourceSegments.append(Range::fromStartAndEnd(pos, out->pos())); pos = out->pos(); if (!isOpen) // If we reach that point, either the resource was opened already... resource->close(); // or we did open it and have to close it again. } localManager.removeCollection("QResources"); // operations QInstaller::appendInt64(out, operations.count()); foreach (const OperationBlob &operation, operations) { QInstaller::appendString(out, operation.name); QInstaller::appendString(out, operation.xml); } QInstaller::appendInt64(out, operations.count()); const Range operationsSegment = Range::fromStartAndEnd(pos, out->pos()); // resource collections data and index const Range resourceCollectionsSegment = localManager.write(out, -endOfBinary); QInstaller::appendInt64Range(out, resourceCollectionsSegment.moved(-endOfBinary)); // meta resource segments foreach (const Range &segment, metaResourceSegments) QInstaller::appendInt64Range(out, segment.moved(-endOfBinary)); // operations segment QInstaller::appendInt64Range(out, operationsSegment.moved(-endOfBinary)); // resources count QInstaller::appendInt64(out, metaResourceSegments.count()); const qint64 binaryContentSize = (out->pos() + (3 * sizeof(qint64))) - endOfBinary; QInstaller::appendInt64(out, binaryContentSize); QInstaller::appendInt64(out, magicMarker); QInstaller::appendInt64(out, magicCookie); } } // namespace QInstaller src/libs/installer/binarycontent.h000066400000000000000000000054271325366651500176470ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef BINARYCONTENT_H #define BINARYCONTENT_H #include "binaryformat.h" #include "binarylayout.h" QT_BEGIN_NAMESPACE class QFile; QT_END_NAMESPACE namespace QInstaller { class INSTALLER_EXPORT BinaryContent { public: // the marker to distinguish what kind of binary static const qint64 MagicInstallerMarker = 0x12023233UL; static const qint64 MagicUninstallerMarker = 0x12023234UL; static const qint64 MagicUpdaterMarker = 0x12023235UL; static const qint64 MagicPackageManagerMarker = 0x12023236UL; // the cookie put at the end of the file static const quint64 MagicCookie = 0xc2630a1c99d668f8LL; // binary static const quint64 MagicCookieDat = 0xc2630a1c99d668f9LL; // data static qint64 findMagicCookie(QFile *file, quint64 magicCookie); static BinaryLayout binaryLayout(QFile *file, quint64 magicCookie); static void readBinaryContent(QFile *file, QList *operations, ResourceCollectionManager *manager, qint64 *magicMarker, quint64 magicCookie); static void writeBinaryContent(QFile *out, const QList &operations, const ResourceCollectionManager &manager, qint64 magicMarker, quint64 magicCookie); }; } // namespace QInstaller #endif // BINARYCONTENT_H src/libs/installer/binaryformat.cpp000066400000000000000000000327451325366651500200230ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "binaryformat.h" #include "errors.h" #include "fileio.h" #include #include #include namespace QInstaller { /*! \class QInstaller::OperationBlob \inmodule QtInstallerFramework \brief The OperationBlob class is a textual representation of an operation that can be instantiated and executed by the Qt Installer Framework. */ /*! \fn OperationBlob::OperationBlob(const QString &n, const QString &x) Constructs the operation blob with the given arguments, while \a n stands for the name part and \a x for the XML representation of the operation. */ /*! \variable QInstaller::OperationBlob::name \brief The name of the operation. */ /*! \variable QInstaller::OperationBlob::xml \brief The XML representation of the operation. */ /*! \class QInstaller::Resource \inmodule QtInstallerFramework \brief The Resource class is an interface for wrapping a file as read only device. Resource is an interface for reading inside a file, but is not supposed to write to the file it wraps. The \c Resource class is created by passing a path to an existing binary (such as a zipped archive or a Qt resource file). The resource name can be set at any time using setName() or during construction. The segment supplied during construction represents the offset and size of the resource inside the file. */ /*! \fn Range Resource::segment() const Returns the range inside the file this resource represents. */ /*! \fn void Resource::setSegment(const Range &segment) Sets the range to the \a segment of the file that this resource represents. */ /*! Creates a resource providing the data in \a path. */ Resource::Resource(const QString &path) : m_file(path) , m_name(QFileInfo(path).fileName().toUtf8()) , m_segment(Range::fromStartAndLength(0, m_file.size())) { } /*! Creates a resource providing the data in \a path identified by \a name. */ Resource::Resource(const QString &path, const QByteArray &name) : m_file(path) , m_name(name) , m_segment(Range::fromStartAndLength(0, m_file.size())) { } /*! Creates a resource providing the data in \a path limited to \a segment. */ Resource::Resource(const QString &path, const Range &segment) : m_file(path) , m_name(QFileInfo(path).fileName().toUtf8()) , m_segment(segment) { } /*! Destroys the resource. Calls close() if necessary before destroying the resource. */ Resource::~Resource() { if (isOpen()) close(); } /*! \reimp */ bool Resource::seek(qint64 pos) { return QIODevice::seek(pos); } /*! Returns the name of the resource. */ QByteArray Resource::name() const { return m_name; } /*! Sets the name of the resource to \a name. */ void Resource::setName(const QByteArray &name) { m_name = name; } /*! Opens a resource in QIODevice::ReadOnly mode. The function returns \c true if successful. */ bool Resource::open() { if (isOpen()) return false; if (!m_file.open(QIODevice::ReadOnly)) { setErrorString(m_file.errorString()); return false; } if (!QIODevice::open(QIODevice::ReadOnly)) { setErrorString(tr("Cannot open resource %1 for reading.").arg(QString::fromUtf8(m_name))); return false; } return true; } /*! \reimp */ void Resource::close() { m_file.close(); QIODevice::close(); } /*! \reimp */ qint64 Resource::size() const { return m_segment.length(); } /*! \reimp */ qint64 Resource::readData(char* data, qint64 maxSize) { // check if there is anything left to read maxSize = qMin(maxSize, m_segment.length() - pos()); if (maxSize <= 0) return 0; const qint64 p = m_file.pos(); m_file.seek(m_segment.start() + pos()); const qint64 amountRead = m_file.read(data, maxSize); m_file.seek(p); return amountRead; } /*! \reimp */ qint64 Resource::writeData(const char* data, qint64 maxSize) { Q_UNUSED(data); Q_UNUSED(maxSize); // should never be called, as we're read only return -1; } /*! \fn void Resource::copyData(QFileDevice *out) Copies the resource data to a file called \a out. Throws Error on failure. */ /*! \overload Copies the resource data of \a resource to a file called \a out. Throws Error on failure. */ void Resource::copyData(Resource *resource, QFileDevice *out) { qint64 left = resource->size(); char data[4096]; while (left > 0) { const qint64 len = qMin(left, 4096); const qint64 bytesRead = resource->read(data, len); if (bytesRead != len) { throw QInstaller::Error(tr("Read failed after %1 bytes: %2") .arg(QString::number(resource->size() - left), resource->errorString())); } const qint64 bytesWritten = out->write(data, len); if (bytesWritten != len) { throw QInstaller::Error(tr("Write failed after %1 bytes: %2") .arg(QString::number(resource->size() - left), out->errorString())); } left -= len; } } /*! \class QInstaller::ResourceCollection \inmodule QtInstallerFramework \brief The ResourceCollection class is an abstraction that groups together a number of resources. The resources are supposed to be sequential, so the collection keeps them ordered once a new resource is added. The name can be set at any time using setName(). */ /*! The class constructor creates an empty resource collection. By default the collection gets a unique name assigned using QUuid. */ ResourceCollection::ResourceCollection() : m_name(QUuid::createUuid().toByteArray()) { } /*! The class constructor creates an empty resource collection with a name set to \a name. */ ResourceCollection::ResourceCollection(const QByteArray &name) : m_name(name) {} /*! Returns the name of the resource collection. */ QByteArray ResourceCollection::name() const { return m_name; } /*! Sets the name of the resource collection to \a name. */ void ResourceCollection::setName(const QByteArray &name) { m_name = name; } /*! Appends \a resource to this collection. The collection takes ownership of \a resource. */ void ResourceCollection::appendResource(const QSharedPointer& resource) { Q_ASSERT(resource); resource->setParent(0); m_resources.append(resource); } /*! Appends a list of \a resources to this collection. The collection takes ownership of \a resources. */ void ResourceCollection::appendResources(const QList > &resources) { foreach (const QSharedPointer &resource, resources) appendResource(resource); } /*! Returns the resources associated with this collection. */ QList > ResourceCollection::resources() const { return m_resources; } /*! Returns the resource associated with the name \a name. */ QSharedPointer ResourceCollection::resourceByName(const QByteArray &name) const { foreach (const QSharedPointer& i, m_resources) { if (i->name() == name) return i; } return QSharedPointer(); } /*! \class QInstaller::ResourceCollectionManager \inmodule QtInstallerFramework \brief The ResourceCollectionManager class is an abstraction that groups together a number of resource collections. The resource collections it groups can be written to and read from a QFileDevice. */ /*! Reads the resource collection from the file \a dev. The \a offset argument is used to set the collection's resources segment information. */ void ResourceCollectionManager::read(QFileDevice *dev, qint64 offset) { const qint64 size = QInstaller::retrieveInt64(dev); for (int i = 0; i < size; ++i) { ResourceCollection collection(QInstaller::retrieveByteArray(dev)); const Range segment = QInstaller::retrieveInt64Range(dev).moved(offset); const qint64 pos = dev->pos(); dev->seek(segment.start()); const qint64 count = QInstaller::retrieveInt64(dev); for (int i = 0; i < count; ++i) { QSharedPointer resource(new Resource(dev->fileName())); resource->setName(QInstaller::retrieveByteArray(dev)); resource->setSegment(QInstaller::retrieveInt64Range(dev).moved(offset)); collection.appendResource(resource); } dev->seek(pos); insertCollection(collection); } } /*! Writes the resource collection to the file \a out. The \a offset argument is used to set the collection's segment information. */ Range ResourceCollectionManager::write(QFileDevice *out, qint64 offset) const { QHash < QByteArray, Range > table; QInstaller::appendInt64(out, collectionCount()); foreach (const ResourceCollection &collection, m_collections) { const qint64 dataBegin = out->pos(); QInstaller::appendInt64(out, collection.resources().count()); qint64 start = out->pos() + offset; foreach (const QSharedPointer &resource, collection.resources()) { start += (sizeof(qint64)) // the number of bytes that get written and the + resource->name().size() // resource name (see QInstaller::appendByteArray) + (2 * sizeof(qint64)); // the resource range (see QInstaller::appendInt64Range) } foreach (const QSharedPointer &resource, collection.resources()) { QInstaller::appendByteArray(out, resource->name()); QInstaller::appendInt64Range(out, Range::fromStartAndLength(start, resource->size())); // the actual range once the table has been written start += resource->size(); // adjust for next resource data } foreach (const QSharedPointer &resource, collection.resources()) { if (!resource->open()) { throw QInstaller::Error(tr("Cannot open resource %1: %2") .arg(QString::fromUtf8(resource->name()), resource->errorString())); } resource->copyData(out); } table.insert(collection.name(), Range::fromStartAndEnd(dataBegin, out->pos()) .moved(offset)); } const qint64 start = out->pos(); // Q: why do we write the size twice? // A: for us to be able to read it beginning from the end of the file as well QInstaller::appendInt64(out, collectionCount()); foreach (const QByteArray &name, table.keys()) { QInstaller::appendByteArray(out, name); QInstaller::appendInt64Range(out, table.value(name)); } QInstaller::appendInt64(out, collectionCount()); return Range::fromStartAndEnd(start, out->pos()); } /*! Returns the collection associated with the name \a name. */ ResourceCollection ResourceCollectionManager::collectionByName(const QByteArray &name) const { return m_collections.value(name); } /*! Inserts the \a collection into the collection manager. */ void ResourceCollectionManager::insertCollection(const ResourceCollection& collection) { m_collections.insert(collection.name(), collection); } /*! Removes all occurrences of \a name from the collection manager. */ void ResourceCollectionManager::removeCollection(const QByteArray &name) { m_collections.remove(name); } /*! Returns the collections the collection manager contains. */ QList ResourceCollectionManager::collections() const { return m_collections.values(); } /*! Clears the contents of the collection manager. */ void ResourceCollectionManager::clear() { m_collections.clear(); } /*! Returns the number of collections in the collection manager. */ int ResourceCollectionManager::collectionCount() const { return m_collections.count(); } } // namespace QInstaller src/libs/installer/binaryformat.h000066400000000000000000000077711325366651500174710ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef BINARYFORMAT_H #define BINARYFORMAT_H #include "installer_global.h" #include "range.h" #include #include #include #include namespace QInstaller { struct OperationBlob { OperationBlob(const QString &n, const QString &x) : name(n), xml(x) {} QString name; QString xml; }; class INSTALLER_EXPORT Resource : public QIODevice { Q_OBJECT Q_DISABLE_COPY(Resource) public: explicit Resource(const QString &path); Resource(const QString &path, const QByteArray &name); Resource(const QString &path, const Range &segment); ~Resource(); bool open(); void close(); bool seek(qint64 pos); qint64 size() const; QByteArray name() const; void setName(const QByteArray &name); Range segment() const { return m_segment; } void setSegment(const Range &segment) { m_segment = segment; } void copyData(QFileDevice *out) { copyData(this, out); } static void copyData(Resource *archive, QFileDevice *out); private: qint64 readData(char *data, qint64 maxSize); qint64 writeData(const char *data, qint64 maxSize); bool open(OpenMode mode) { return QIODevice::open(mode); } void setOpenMode(OpenMode mode) { QIODevice::setOpenMode(mode); } private: QFSFileEngine m_file; QByteArray m_name; Range m_segment; }; class INSTALLER_EXPORT ResourceCollection { Q_DECLARE_TR_FUNCTIONS(ResourceCollection) public: ResourceCollection(); explicit ResourceCollection(const QByteArray &name); QByteArray name() const; void setName(const QByteArray &ba); QList > resources() const; QSharedPointer resourceByName(const QByteArray &name) const; void appendResource(const QSharedPointer &resource); void appendResources(const QList > &resources); private: QByteArray m_name; QList > m_resources; }; class INSTALLER_EXPORT ResourceCollectionManager { Q_DECLARE_TR_FUNCTIONS(ResourceCollectionManager) public: void read(QFileDevice *dev, qint64 offset); Range write(QFileDevice *dev, qint64 offset) const; void clear(); int collectionCount() const; QList collections() const; ResourceCollection collectionByName(const QByteArray &name) const; void removeCollection(const QByteArray &name); void insertCollection(const ResourceCollection &collection); private: QHash m_collections; }; } // namespace QInstaller #endif // BINARYFORMAT_H src/libs/installer/binaryformatengine.cpp000066400000000000000000000174451325366651500212110ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "binaryformatengine.h" #include namespace { class StringListIterator : public QAbstractFileEngineIterator { public: StringListIterator( const QStringList &list, QDir::Filters filters, const QStringList &nameFilters) : QAbstractFileEngineIterator(filters, nameFilters) , list(list) , index(-1) { } bool hasNext() const { return index < list.size() - 1; } QString next() { if(!hasNext()) return QString(); ++index; return currentFilePath(); } QString currentFileName() const { return index < 0 ? QString() : list[index]; } private: const QStringList list; int index; }; } // anon namespace namespace QInstaller { /*! \class QInstaller::BinaryFormatEngine \inmodule QtInstallerFramework \brief The BinaryFormatEngine class is the default file engine for accessing resource collections and resource files. */ /*! Constructs a new binary format engine with \a collections and \a fileName. */ BinaryFormatEngine::BinaryFormatEngine(const QHash &collections, const QString &fileName) : m_resource(0) , m_collections(collections) { setFileName(fileName); } /*! \internal Sets the file engine's file name to \a file. This is the file that the rest of the virtual functions will operate on. */ void BinaryFormatEngine::setFileName(const QString &file) { m_fileNamePath = file; static const QChar sep = QLatin1Char('/'); static const QString prefix = QLatin1String("installer://"); Q_ASSERT(m_fileNamePath.toLower().startsWith(prefix)); // cut the prefix QString path = m_fileNamePath.mid(prefix.length()); while (path.endsWith(sep)) path.chop(1); m_collection = m_collections.value(path.section(sep, 0, 0).toUtf8()); m_collection.setName(path.section(sep, 0, 0).toUtf8()); m_resource = m_collection.resourceByName(path.section(sep, 1, 1).toUtf8()); } /*! \internal */ bool BinaryFormatEngine::close() { if (m_resource.isNull()) return false; const bool result = m_resource->isOpen(); m_resource->close(); return result; } /*! \internal */ bool BinaryFormatEngine::open(QIODevice::OpenMode mode) { Q_UNUSED(mode) return m_resource.isNull() ? false : m_resource->open(); } /*! \internal */ qint64 BinaryFormatEngine::pos() const { return m_resource.isNull() ? 0 : m_resource->pos(); } /*! \internal */ qint64 BinaryFormatEngine::read(char *data, qint64 maxlen) { return m_resource.isNull() ? -1 : m_resource->read(data, maxlen); } /*! \internal */ bool BinaryFormatEngine::seek(qint64 offset) { return m_resource.isNull() ? false : m_resource->seek(offset); } /*! \internal */ QString BinaryFormatEngine::fileName(FileName file) const { static const QChar sep = QLatin1Char('/'); switch(file) { case BaseName: return m_fileNamePath.section(sep, -1, -1, QString::SectionSkipEmpty); case PathName: case AbsolutePathName: case CanonicalPathName: return m_fileNamePath.section(sep, 0, -2, QString::SectionSkipEmpty); case DefaultName: case AbsoluteName: case CanonicalName: return m_fileNamePath; default: return QString(); } } /*! \internal */ bool BinaryFormatEngine::copy(const QString &newName) { if (QFile::exists(newName)) return false; QFile target(newName); if (!target.open(QIODevice::WriteOnly)) return false; qint64 bytesLeft = size(); if (!open(QIODevice::ReadOnly)) return false; char data[4096]; while(bytesLeft > 0) { const qint64 len = qMin(bytesLeft, 4096); const qint64 bytesRead = read(data, len); if (bytesRead != len) { close(); return false; } const qint64 bytesWritten = target.write(data, len); if (bytesWritten != len) { close(); return false; } bytesLeft -= len; } close(); return true; } /*! \internal */ QAbstractFileEngine::FileFlags BinaryFormatEngine::fileFlags(FileFlags type) const { FileFlags result; if ((type & FileType) && (!m_resource.isNull())) result |= FileType; if ((type & DirectoryType) && m_resource.isNull()) result |= DirectoryType; if ((type & ExistsFlag) && (!m_resource.isNull())) result |= ExistsFlag; if ((type & ExistsFlag) && m_resource.isNull() && (!m_collection.name().isEmpty())) result |= ExistsFlag; return result; } /*! \internal */ QAbstractFileEngineIterator *BinaryFormatEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) { const QStringList entries = entryList(filters, filterNames); return new StringListIterator(entries, filters, filterNames); } /*! \internal */ QStringList BinaryFormatEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const { if (!m_resource.isNull()) return QStringList(); QStringList result; if ((!m_collection.name().isEmpty()) && (filters & QDir::Files)) { foreach (const QSharedPointer &resource, m_collection.resources()) result.append(QString::fromUtf8(resource->name())); } else if (m_collection.name().isEmpty() && (filters & QDir::Dirs)) { foreach (const ResourceCollection &collection, m_collections) result.append(QString::fromUtf8(collection.name())); } result.removeAll(QString()); // Remove empty names, will crash while using directory iterator. if (filterNames.isEmpty()) return result; QList regexps; foreach (const QString &i, filterNames) regexps.append(QRegExp(i, Qt::CaseInsensitive, QRegExp::Wildcard)); QStringList entries; foreach (const QString &i, result) { bool matched = false; foreach (const QRegExp ®, regexps) { matched = reg.exactMatch(i); if (matched) break; } if (matched) entries.append(i); } return entries; } /*! \internal */ qint64 BinaryFormatEngine::size() const { return m_resource.isNull() ? 0 : m_resource->size(); } } // namespace QInstaller src/libs/installer/binaryformatengine.h000066400000000000000000000047111325366651500206460ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef BINARYFORMATENGINE_H #define BINARYFORMATENGINE_H #include "binaryformat.h" #include namespace QInstaller { class BinaryFormatEngine : public QAbstractFileEngine { Q_DISABLE_COPY(BinaryFormatEngine) public: BinaryFormatEngine(const QHash &collections, const QString &fileName); void setFileName(const QString &file); bool copy(const QString &newName); bool close(); bool open(QIODevice::OpenMode mode); qint64 pos() const; qint64 read(char *data, qint64 maxlen); bool seek(qint64 offset); qint64 size() const; QString fileName(FileName file = DefaultName) const; FileFlags fileFlags(FileFlags type = FileInfoAll) const; Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames); QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const; private: QString m_fileNamePath; ResourceCollection m_collection; QSharedPointer m_resource; QHash m_collections; }; } // namespace QInstaller #endif // BINARYFORMATENGINE_H src/libs/installer/binaryformatenginehandler.cpp000066400000000000000000000100431325366651500225320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "binaryformatengine.h" #include "binaryformatenginehandler.h" #include "productkeycheck.h" namespace QInstaller { /*! \class QInstaller::BinaryFormatEngineHandler \inmodule QtInstallerFramework \brief The BinaryFormatEngineHandler class provides a way to register resource collections and resource files. */ /*! Creates a file engine for the file specified by \a fileName. To be able to create a file engine, the file name needs to be prefixed with \c {installer://}. Returns 0 if the engine cannot handle \a fileName. */ QAbstractFileEngine *BinaryFormatEngineHandler::create(const QString &fileName) const { return fileName.startsWith(QLatin1String("installer://"), Qt::CaseInsensitive ) ? new BinaryFormatEngine(m_resources, fileName) : 0; } /*! Clears the contents of the binary format engine. */ void BinaryFormatEngineHandler::clear() { m_resources.clear(); } /*! Returns the active instance of the engine. */ BinaryFormatEngineHandler *BinaryFormatEngineHandler::instance() { static BinaryFormatEngineHandler instance; return &instance; } /*! Registers the given resource collections \a collections in the engine. */ void BinaryFormatEngineHandler::registerResources(const QList &collections) { foreach (const ResourceCollection &collection, collections) { if (ProductKeyCheck::instance()->isValidPackage(QString::fromUtf8(collection.name()))) m_resources.insert(collection.name(), collection); } } /*! Registers the resource specified by \a resourcePath in a resource collection specified by \a fileName. The file name \a fileName must be in the form of \c {installer://}, followed by the collection name and resource name separated by a forward slash. A valid file name looks like this: installer://collectionName/resourceName */ void BinaryFormatEngineHandler::registerResource(const QString &fileName, const QString &resourcePath) { static const QChar sep = QChar::fromLatin1('/'); static const QString prefix = QString::fromLatin1("installer://"); Q_ASSERT(fileName.toLower().startsWith(prefix)); // cut the prefix QString path = fileName.mid(prefix.length()); while (path.endsWith(sep)) path.chop(1); const QByteArray resourceName = path.section(sep, 1, 1).toUtf8(); const QByteArray collectionName = path.section(sep, 0, 0).toUtf8(); if (!ProductKeyCheck::instance()->isValidPackage(QString::fromUtf8(collectionName))) return; m_resources[collectionName].setName(collectionName); m_resources[collectionName].appendResource(QSharedPointer(new Resource(resourcePath, resourceName))); } } // namespace QInstaller src/libs/installer/binaryformatenginehandler.h000066400000000000000000000041401325366651500222000ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef BINARYFORMATENGINEHANDLER_H #define BINARYFORMATENGINEHANDLER_H #include "binaryformat.h" #include namespace QInstaller { class INSTALLER_EXPORT BinaryFormatEngineHandler : public QAbstractFileEngineHandler { Q_DISABLE_COPY(BinaryFormatEngineHandler) public: QAbstractFileEngine *create(const QString &fileName) const; void clear(); static BinaryFormatEngineHandler *instance(); void registerResources(const QList &collections); void registerResource(const QString &fileName, const QString &resourcePath); private: BinaryFormatEngineHandler() {} ~BinaryFormatEngineHandler() {} private: QHash m_resources; }; } // namespace QInstaller #endif // BINARYFORMATENGINEHANDLER_H src/libs/installer/binarylayout.cpp000066400000000000000000000066341325366651500200460ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ /*! \class QInstaller::BinaryLayout \inmodule QtInstallerFramework \brief The BinaryLayout class describes the binary content appended to a file. Explanation of the binary content at the end of the installer or the separate data file: \code Meta data entry [1 ... n] [Format] Plain data (QResource) [Format] ---------------------------------------------------------- Operation count (qint64) Operation entry [1 ... n] [Format] Name (qint64, QString) XML (qint64, QString) [Format] Operation count (qint64) ---------------------------------------------------------- Collection count Collection data entry [1 ... n] [Format] Archive count (qint64), Name entry [1 ... n] [Format] Name (qint64, QByteArray), Offset (qint64), Length (qint64), [Format] Archive data entry [1 ... n] [Format] Plain data [Format] [Format] ---------------------------------------------------------- Collection count (qint64) Collection index entry [1 ... n] [Format] Name (qint64, QByteArray) Offset (qint64) Length (qint64) [Format] Collection count (qint64) ---------------------------------------------------------- Collection index block [Offset (qint64)] Collection index block [Length (qint64)] ---------------------------------------------------------- Resource segments [1 ... n] [Format] Offset (qint64) Length (qint64) [Format] ---------------------------------------------------------- Operations information block [Offset (qint64)] Operations information block [Length (qint64)] ---------------------------------------------------------- Meta data count (qint64) ---------------------------------------------------------- Binary content size [Including Marker and Cookie (qint64)] ---------------------------------------------------------- Magic marker (qint64) Magic cookie (qint64) \endcode */ src/libs/installer/binarylayout.h000066400000000000000000000034351325366651500175070ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef BINARYLAYOUT_H #define BINARYLAYOUT_H #include "range.h" #include namespace QInstaller { struct BinaryLayout { qint64 endOfExectuable; QVector > metaResourceSegments; Range metaResourcesSegment; Range operationsSegment; Range resourceCollectionsSegment; qint64 binaryContentSize; qint64 magicMarker; quint64 magicCookie; qint64 endOfBinaryContent; }; } // namespace QInstaller #endif // BINARYLAYOUT src/libs/installer/component.cpp000066400000000000000000001371651325366651500173320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "component.h" #include "scriptengine.h" #include "errors.h" #include "fileutils.h" #include "globals.h" #include "lib7z_facade.h" #include "messageboxhandler.h" #include "packagemanagercore.h" #include "remoteclient.h" #include "settings.h" #include "utils.h" #include "updateoperationfactory.h" #include #include #include #include #include #include #include #include #include #include using namespace QInstaller; static const QLatin1String scScriptTag("Script"); static const QLatin1String scVirtual("Virtual"); static const QLatin1String scInstalled("Installed"); static const QLatin1String scUpdateText("UpdateText"); static const QLatin1String scUninstalled("Uninstalled"); static const QLatin1String scCurrentState("CurrentState"); static const QLatin1String scForcedInstallation("ForcedInstallation"); static const QLatin1String scCheckable("Checkable"); /*! \inmodule QtInstallerFramework \class Component::SortingPriorityLessThan \brief The SortingPriorityLessThan class sets an increasing sorting order for child components. If the component contains several children and has this sorting priority set, the child list is sorted so that the child component with the lowest priority is placed on top. */ /*! \fn Component::SortingPriorityLessThan::operator() (const Component *lhs, const Component *rhs) const Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false. */ /*! \inmodule QtInstallerFramework \class Component::SortingPriorityGreaterThan \brief The SortingPriorityGreaterThan class sets a decreasing sorting priority for child components. If the component contains several children and has this sorting priority set, the child list is sorted so that the child component with the highest priority is placed on top. */ /*! \fn Component::SortingPriorityGreaterThan::operator() (const Component *lhs, const Component *rhs) const Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c false. */ /*! \inmodule QtInstallerFramework \class QInstaller::Component \brief The Component class represents the current component. */ /*! \property Component::name \brief The name of the component as set in the \c tag of the package information file. */ /*! \property Component::displayName \brief The name of the component as shown in the user interface. */ /*! \property Component::autoCreateOperations \brief Whether some standard operations for the component should be automatically created when the installation starts. The default is \c true. */ /*! \property Component::archives \brief The list of archive URLs registered for the component. The URLs are prefixed with \c {installer://}. \sa addDownloadableArchive(), removeDownloadableArchive() */ /*! \property Component::dependencies \brief The components this component depends on. This is a read-only property. */ /*! \property Component::autoDependencies \brief The value of the \c element in the package information file. */ /*! \property Component::fromOnlineRepository \brief Whether this component has been loaded from an online repository. */ /*! \property Component::repositoryUrl \brief The repository URL the component is downloaded from. If this component is not downloaded from an online repository, returns an empty QUrl. */ /*! \property Component::default \brief Whether the component is a default one. This is a read-only property. \note Always \c false for virtual components. */ /*! \property Component::installed \brief Whether the component is installed. This is a read-only property. */ /*! \property Component::enabled \brief Whether the component is currently enabled. The property is both readable and writable. */ /*! \fn Component::loaded() \sa {component::loaded}{component.loaded} */ /*! \fn Component::valueChanged(const QString &key, const QString &value) Emitted when the value of the variable with the name \a key changes to \a value. \sa {component::valueChanged}{component.valueChanged}, setValue() */ /*! \fn Component::virtualStateChanged() \sa {component::virtualStateChanged}{component.virtualStateChanged} */ /*! Creates a new component in the package manager specified by \a core. */ Component::Component(PackageManagerCore *core) : d(new ComponentPrivate(core, this)) { setPrivate(d); connect(this, &Component::valueChanged, this, &Component::updateModelData); qRegisterMetaType >("QList"); } /*! Destroys the component. */ Component::~Component() { if (parentComponent() != 0) d->m_parentComponent->d->m_allChildComponents.removeAll(this); //why can we delete all create operations if the component gets destroyed if (!d->m_newlyInstalled) qDeleteAll(d->m_operations); //we need to copy the list, because we are changing it with removeAll at top //(this made the iterators broken in the past) QList copiedChildrenList = d->m_allChildComponents; copiedChildrenList.detach(); //this makes it a real copy qDeleteAll(copiedChildrenList); delete d; d = 0; } /*! Sets variables according to the values set in the package.xml file of a local \a package. */ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package) { setValue(scName, package.name); setValue(scDisplayName, package.title); setValue(scDescription, package.description); setValue(scVersion, package.version); setValue(scInheritVersion, package.inheritVersionFrom); setValue(scInstalledVersion, package.version); setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString()); setValue(QLatin1String("InstallDate"), package.installDate.toString()); setValue(scUncompressedSize, QString::number(package.uncompressedSize)); setValue(scDependencies, package.dependencies.join(QLatin1String(","))); setValue(scAutoDependOn, package.autoDependencies.join(QLatin1String(","))); setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse); if (package.forcedInstallation & !PackageManagerCore::noForceInstallation()) { setCheckable(false); setCheckState(Qt::Checked); } setValue(scVirtual, package.virtualComp ? scTrue : scFalse); setValue(scCurrentState, scInstalled); setValue(scCheckable, package.checkable ? scTrue : scFalse); } /*! Sets variables according to the values set in the package.xml file of \a package. Also loads UI files, licenses and translations if they are referenced in the package.xml. */ void Component::loadDataFromPackage(const Package &package) { Q_ASSERT(&package); setValue(scName, package.data(scName).toString()); setValue(scDisplayName, package.data(scDisplayName).toString()); setValue(scDescription, package.data(scDescription).toString()); setValue(scDefault, package.data(scDefault).toString()); setValue(scAutoDependOn, package.data(scAutoDependOn).toString()); setValue(scCompressedSize, package.data(scCompressedSize).toString()); setValue(scUncompressedSize, package.data(scUncompressedSize).toString()); setValue(scVersion, package.data(scVersion).toString()); setValue(scInheritVersion, package.data(scInheritVersion).toString()); setValue(scDependencies, package.data(scDependencies).toString()); setValue(scDownloadableArchives, package.data(scDownloadableArchives).toString()); setValue(scVirtual, package.data(scVirtual).toString()); setValue(scSortingPriority, package.data(scSortingPriority).toString()); setValue(scEssential, package.data(scEssential).toString()); setValue(scUpdateText, package.data(scUpdateText).toString()); setValue(scNewComponent, package.data(scNewComponent).toString()); setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString()); setValue(scScriptTag, package.data(scScriptTag).toString()); setValue(scReplaces, package.data(scReplaces).toString()); setValue(scReleaseDate, package.data(scReleaseDate).toString()); setValue(scCheckable, package.data(scCheckable).toString()); QString forced = package.data(scForcedInstallation, scFalse).toString().toLower(); if (PackageManagerCore::noForceInstallation()) forced = scFalse; setValue(scForcedInstallation, forced); if (forced == scTrue) { setCheckable(false); setCheckState(Qt::Checked); } setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url)); const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString() .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); if (!uis.isEmpty()) loadUserInterfaces(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), uis); const QStringList qms = package.data(QLatin1String("Translations")).toString() .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); if (!qms.isEmpty()) loadTranslations(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), qms); QHash licenseHash = package.data(QLatin1String("Licenses")).toHash(); if (!licenseHash.isEmpty()) loadLicenses(QString::fromLatin1("%1/%2/").arg(localTempPath(), name()), licenseHash); } /*! Returns the size of a compressed archive. */ quint64 Component::updateUncompressedSize() { quint64 size = 0; if (installAction() == ComponentModelHelper::Install || installAction() == ComponentModelHelper::KeepInstalled) { size = d->m_vars.value(scUncompressedSize).toLongLong(); } foreach (Component* comp, d->m_allChildComponents) size += comp->updateUncompressedSize(); setValue(scUncompressedSizeSum, QString::number(size)); setData(humanReadableSize(size), UncompressedSize); return size; } /*! Marks the component as installed. */ void Component::markAsPerformedInstallation() { d->m_newlyInstalled = true; } /*! Returns a key and value based hash of all variables set for this component. */ QHash Component::variables() const { return d->m_vars; } /*! Returns the value of variable name \a key. If \a key is not known yet, \a defaultValue is returned. \note If a component is virtual and you ask for the component value with the key \c Default, it will always return \c false. */ QString Component::value(const QString &key, const QString &defaultValue) const { if (key == scDefault) return isDefault() ? scTrue : scFalse; return d->m_vars.value(key, defaultValue); } /*! Sets the value of the variable with \a key to \a value. \sa {component::setValue}{component.setValue} */ void Component::setValue(const QString &key, const QString &value) { QString normalizedValue = d->m_core->replaceVariables(value); if (d->m_vars.value(key) == normalizedValue) return; if (key == scName) d->m_componentName = normalizedValue; if (key == scCheckable) this->setCheckable(normalizedValue.toLower() == scTrue); d->m_vars[key] = normalizedValue; emit valueChanged(key, normalizedValue); } /*! Returns the installer this component belongs to. */ PackageManagerCore *Component::packageManagerCore() const { return d->m_core; } /*! Returns the parent of this component. For example, the parent of a component called \c org.qt-project.sdk.qt is \c org.qt-project.sdk if it exists. */ Component *Component::parentComponent() const { return d->m_parentComponent; } /*! Appends \a component as a child of this component. If \a component already has a parent, it is removed from the previous parent. If the \a component contains several children and has the SortingPriorityGreaterThan() sorting priority set, the child list is sorted so that the child component with the highest priority is placed on top. */ void Component::appendComponent(Component *component) { if (d->m_core->isUpdater()) throw Error(tr("Components cannot have children in updater mode.")); if (!component->isVirtual()) { const QList virtualChildComponents = d->m_allChildComponents.mid(d->m_childComponents.count()); d->m_childComponents.append(component); std::sort(d->m_childComponents.begin(), d->m_childComponents.end(), SortingPriorityGreaterThan()); d->m_allChildComponents = d->m_childComponents + virtualChildComponents; } else { d->m_allChildComponents.append(component); } if (Component *parent = component->parentComponent()) parent->removeComponent(component); component->d->m_parentComponent = this; setTristate(d->m_childComponents.count() > 0); } /*! Removes \a component if it is a child of this component. The component object still exists after the function returns. It is up to the caller to delete the passed \a component. */ void Component::removeComponent(Component *component) { if (component->parentComponent() == this) { component->d->m_parentComponent = 0; d->m_childComponents.removeAll(component); d->m_allChildComponents.removeAll(component); } } /*! Returns a list of child components, including all descendants of the component's children. \note The returned list does include all children; non-virtual components as well as virtual components. */ QList Component::descendantComponents() const { if (d->m_core->isUpdater()) return QList(); QList result = d->m_allChildComponents; foreach (Component *component, d->m_allChildComponents) result += component->descendantComponents(); return result; } /*! Contains the unique identifier of this component. */ QString Component::name() const { return d->m_componentName; } /*! Contains this component's display name as visible to the user. */ QString Component::displayName() const { return d->m_vars.value(scDisplayName); } /*! Loads the component script into the script engine. */ void Component::loadComponentScript() { const QString script = d->m_vars.value(scScriptTag); if (!localTempPath().isEmpty() && !script.isEmpty()) loadComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name(), script)); } /*! Loads the script at \a fileName into the script engine. The installer and all its components as well as other useful things are being exported into the script. For more information, see \l{Component Scripting}. Throws an error when either the script at \a fileName could not be opened, or QScriptEngine could not evaluate the script. */ void Component::loadComponentScript(const QString &fileName) { // introduce the component object as javascript value and call the name to check that it // was successful d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName, QString::fromLatin1("var component = installer.componentByName('%1'); component.name;") .arg(name())); emit loaded(); languageChanged(); } /*! \internal Calls the script method retranslateUi(), if any. This is done whenever a QTranslator file is being loaded. */ void Component::languageChanged() { d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("retranslateUi")); } /*! Loads the translations matching the name filters \a qms inside \a directory. Only translations with a base name matching the current locale's name are loaded. For more information, see \l{Translating Pages}. */ void Component::loadTranslations(const QDir &directory, const QStringList &qms) { QDirIterator it(directory.path(), qms, QDir::Files); const QStringList translations = d->m_core->settings().translations(); const QString uiLanguage = QLocale().uiLanguages().value(0, QLatin1String("en")); while (it.hasNext()) { const QString filename = it.next(); const QString basename = QFileInfo(filename).baseName(); if (!uiLanguage.startsWith(QFileInfo(filename).baseName(), Qt::CaseInsensitive)) continue; // do not load the file if it does not match the UI language if (!translations.isEmpty()) { bool found = false; foreach (const QString &translation, translations) found |= translation.startsWith(basename, Qt::CaseInsensitive); if (!found) // don't load the file if it does match the UI language but is not allowed to be used continue; } QScopedPointer translator(new QTranslator(this)); if (translator->load(filename)) { // Do not throw if translator returns false as it may just be an intentionally // empty file. See also QTBUG-31031 qApp->installTranslator(translator.take()); } } } /*! Loads the user interface files matching the name filters \a uis inside \a directory. The loaded interface can be accessed via userInterfaces() by using the class name set in the UI file. */ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis) { if (qobject_cast (qApp) == 0) return; QDirIterator it(directory.path(), uis, QDir::Files); while (it.hasNext()) { QFile file(it.next()); if (!file.open(QIODevice::ReadOnly)) { throw Error(tr("Cannot open the requested UI file \"%1\": %2").arg( it.fileName(), file.errorString())); } static QUiLoader loader; loader.setTranslationEnabled(true); loader.setLanguageChangeEnabled(true); QWidget *const widget = loader.load(&file, 0); if (!widget) { throw Error(tr("Cannot load the requested UI file \"%1\": %2").arg( it.fileName(), loader.errorString())); } d->scriptEngine()->newQObject(widget); d->m_userInterfaces.insert(widget->objectName(), widget); } } /*! Loads the text of the licenses contained in \a licenseHash from \a directory. This is saved into a new hash containing the filename and the text of that file. */ void Component::loadLicenses(const QString &directory, const QHash &licenseHash) { QHash::const_iterator it; for (it = licenseHash.begin(); it != licenseHash.end(); ++it) { const QString &fileName = it.value().toString(); if (!ProductKeyCheck::instance()->isValidLicenseTextFile(fileName)) continue; QFileInfo fileInfo(directory, fileName); foreach (const QString &lang, QLocale().uiLanguages()) { if (QLocale(lang).language() == QLocale::English) // we assume English is the default language break; QList fileCandidates; foreach (const QString &locale, QInstaller::localeCandidates(lang.toLower())) { fileCandidates << QFileInfo(QString::fromLatin1("%1%2_%3.%4").arg( directory, fileInfo.baseName(), locale, fileInfo.completeSuffix())); } auto fInfo = std::find_if(fileCandidates.constBegin(), fileCandidates.constEnd(), [](const QFileInfo &file) { return file.exists(); }); if (fInfo != fileCandidates.constEnd()) { fileInfo = *fInfo; break; } } QFile file(fileInfo.filePath()); if (!file.open(QIODevice::ReadOnly)) { throw Error(tr("Cannot open the requested license file \"%1\": %2").arg( file.fileName(), file.errorString())); } QTextStream stream(&file); stream.setCodec("UTF-8"); d->m_licenses.insert(it.key(), qMakePair(fileName, stream.readAll())); } } /*! \property Component::userInterfaces \brief A list of all user interface class names known to this component. */ QStringList Component::userInterfaces() const { return d->m_userInterfaces.keys(); } /*! Returns a hash that contains the file names and text of license files for the component. */ QHash > Component::licenses() const { return d->m_licenses; } /*! Returns the QWidget created for \a name or \c 0 if the widget has been deleted or cannot be found. \sa {component::userInterface}{component.userInterface} */ QWidget *Component::userInterface(const QString &name) const { return d->m_userInterfaces.value(name).data(); } /*! Creates all operations needed to install this component's \a path. \a path is a fully qualified filename including the component's name. This method gets called from createOperationsForArchive. You can override it by providing a method with the same name in the component script. \note RSA signature files are omitted by this method. \note If you call this method from a script, it will not call the script's method with the same name. The default implementation is recursively creating Copy and Mkdir operations for all files and folders within \a path. \sa {component::createOperationsForPath}{component.createOperationsForPath} */ void Component::createOperationsForPath(const QString &path) { const QFileInfo fi(path); // don't copy over a checksum file if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists()) return; // the script can override this method if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined()) { return; } QString target; static const QString prefix = QString::fromLatin1("installer://"); target = QString::fromLatin1("@TargetDir@%1").arg(path.mid(prefix.length() + name().length())); if (fi.isFile()) { static const QString copy = QString::fromLatin1("Copy"); addOperation(copy, QStringList() << fi.filePath() << target); } else if (fi.isDir()) { qApp->processEvents(); static const QString mkdir = QString::fromLatin1("Mkdir"); addOperation(mkdir, QStringList(target)); QDirIterator it(fi.filePath()); while (it.hasNext()) createOperationsForPath(it.next()); } } /*! Creates all operations needed to install this component's \a archive. This method gets called from createOperations. You can override this method by providing a method with the same name in the component script. \note If you call this method from a script, it will not call the script's method with the same name. The default implementation calls createOperationsForPath for everything contained in the archive. If \a archive is a compressed archive known to the installer system, an Extract operation is created, instead. \sa {component::createOperationsForArchive}{component.createOperationsForArchive} */ void Component::createOperationsForArchive(const QString &archive) { const QFileInfo fi(archive); // don't do anything with sha1 files if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists()) return; // the script can override this method if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined()) { return; } const bool isZip = Lib7z::isSupportedArchive(archive); if (isZip) { // archives get completely extracted per default (if the script isn't doing other stuff) addOperation(QLatin1String("Extract"), QStringList() << archive << QLatin1String("@TargetDir@")); } else { createOperationsForPath(archive); } } /*! \sa {component::beginInstallation}{component.beginInstallation} */ void Component::beginInstallation() { // the script can override this method d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("beginInstallation")); } /*! \sa {component::createOperations}{component.createOperations} \sa createOperationsForArchive() */ void Component::createOperations() { // the script can override this method if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperations")) .isUndefined()) { d->m_operationsCreated = true; return; } foreach (const QString &archive, archives()) createOperationsForArchive(archive); d->m_operationsCreated = true; } /*! Registers the file or directory at \a path for being removed when this component gets uninstalled. In case of a directory, this will be recursive. If \a wipe is set to \c true, the directory will also be deleted if it contains changes made by the user after installation. \sa {component::registerPathForUninstallation}{component.registerPathForUninstallation} */ void Component::registerPathForUninstallation(const QString &path, bool wipe) { d->m_pathsForUninstallation.append(qMakePair(path, wipe)); } /*! Returns the list of paths previously registered for uninstallation with registerPathForUninstallation(). */ QList > Component::pathsForUninstallation() const { return d->m_pathsForUninstallation; } /*! Contains the names of all archives known to this component. Even downloaded archives are mapped to the \c{installer://} URL through the used QFileEngineHandler during the download process. */ QStringList Component::archives() const { QString pathString = QString::fromLatin1("installer://%1/").arg(name()); QStringList archivesNameList = QDir(pathString).entryList(); //RegExp "^" means line beginning archivesNameList.replaceInStrings(QRegExp(QLatin1String("^")), pathString); return archivesNameList; } /*! Adds the archive \a path to this component. This can only be called if this component was downloaded from an online repository. When adding \a path, it will be downloaded from the repository when the installation starts. \sa {component::addDownloadableArchive}{component.addDownloadableArchive} \sa removeDownloadableArchive(), fromOnlineRepository, archives */ void Component::addDownloadableArchive(const QString &path) { Q_ASSERT(isFromOnlineRepository()); qDebug() << "addDownloadable" << path; d->m_downloadableArchives.append(d->m_vars.value(scVersion) + path); } /*! Removes the archive \a path previously added via addDownloadableArchive() from this component. This can only be called if this component was downloaded from an online repository. \sa {component::removeDownloadableArchive}{component.removeDownloadableArchive} \sa addDownloadableArchive(), fromOnlineRepository, archives */ void Component::removeDownloadableArchive(const QString &path) { Q_ASSERT(isFromOnlineRepository()); d->m_downloadableArchives.removeAll(path); } /*! Returns the archives to be downloaded from the online repository before installation. */ QStringList Component::downloadableArchives() const { return d->m_downloadableArchives; } /*! Adds a request for quitting the process \a process before installing, updating, or uninstalling the component. \sa {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} */ void Component::addStopProcessForUpdateRequest(const QString &process) { d->m_stopProcessForUpdateRequests.append(process); } /*! Removes the request for quitting the process \a process again. \sa {component::removeStopProcessForUpdateRequest}{component.removeStopProcessForUpdateRequest} */ void Component::removeStopProcessForUpdateRequest(const QString &process) { d->m_stopProcessForUpdateRequests.removeAll(process); } /*! A convenience function for adding or removing the request for stopping \a process depending on whether \a requested is \c true (add) or \c false (remove). \sa {component::setStopProcessForUpdateRequest}{component.addStopProcessForUpdateReques} */ void Component::setStopProcessForUpdateRequest(const QString &process, bool requested) { if (requested) addStopProcessForUpdateRequest(process); else removeStopProcessForUpdateRequest(process); } /*! The list of processes that need to be closed before installing, updating, or uninstalling this component. */ QStringList Component::stopProcessForUpdateRequests() const { return d->m_stopProcessForUpdateRequests; } /*! Returns the operations needed to install this component. If autoCreateOperations() is \c true, createOperations() is called if no operations have been automatically created yet. */ OperationList Component::operations() const { if (d->m_autoCreateOperations && !d->m_operationsCreated) { const_cast(this)->createOperations(); if (!d->m_minimumProgressOperation) { d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance() .create(QLatin1String("MinimumProgress"), d->m_core); d->m_minimumProgressOperation->setValue(QLatin1String("component"), name()); d->m_operations.append(d->m_minimumProgressOperation); } if (!d->m_licenses.isEmpty()) { d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance() .create(QLatin1String("License"), d->m_core); d->m_licenseOperation->setValue(QLatin1String("component"), name()); QVariantMap licenses; const QList > values = d->m_licenses.values(); for (int i = 0; i < values.count(); ++i) licenses.insert(values.at(i).first, values.at(i).second); d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses); d->m_operations.append(d->m_licenseOperation); } } return d->m_operations; } /*! Adds \a operation to the list of operations needed to install this component. */ void Component::addOperation(Operation *operation) { d->m_operations.append(operation); if (RemoteClient::instance().isActive()) operation->setValue(QLatin1String("admin"), true); } /*! Adds \a operation to the list of operations needed to install this component. \a operation is executed with elevated rights. */ void Component::addElevatedOperation(Operation *operation) { addOperation(operation); operation->setValue(QLatin1String("admin"), true); } /*! Returns whether the operations needed to install this component were created successfully. */ bool Component::operationsCreatedSuccessfully() const { return d->m_operationsCreatedSuccessfully; } Operation *Component::createOperation(const QString &operationName, const QString ¶meter1, const QString ¶meter2, const QString ¶meter3, const QString ¶meter4, const QString ¶meter5, const QString ¶meter6, const QString ¶meter7, const QString ¶meter8, const QString ¶meter9, const QString ¶meter10) { QStringList arguments; if (!parameter1.isNull()) arguments.append(parameter1); if (!parameter2.isNull()) arguments.append(parameter2); if (!parameter3.isNull()) arguments.append(parameter3); if (!parameter4.isNull()) arguments.append(parameter4); if (!parameter5.isNull()) arguments.append(parameter5); if (!parameter6.isNull()) arguments.append(parameter6); if (!parameter7.isNull()) arguments.append(parameter7); if (!parameter8.isNull()) arguments.append(parameter8); if (!parameter9.isNull()) arguments.append(parameter9); if (!parameter10.isNull()) arguments.append(parameter10); return createOperation(operationName, arguments); } Operation *Component::createOperation(const QString &operationName, const QStringList ¶meters) { Operation *operation = KDUpdater::UpdateOperationFactory::instance().create(operationName, d->m_core); if (operation == 0) { const QMessageBox::StandardButton button = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("OperationDoesNotExistError"), tr("Error"), tr("Error: Operation %1 does not exist.") .arg(operationName), QMessageBox::Abort | QMessageBox::Ignore); if (button == QMessageBox::Abort) d->m_operationsCreatedSuccessfully = false; return operation; } if (operation->name() == QLatin1String("Delete")) operation->setValue(QLatin1String("performUndo"), false); operation->setArguments(d->m_core->replaceVariables(parameters)); operation->setValue(QLatin1String("component"), name()); return operation; } namespace { inline bool convert(QQmlV4Function *func, QStringList *toArgs) { if (func->length() < 2) return false; QV4::Scope scope(func->v4engine()); QV4::ScopedValue val(scope); val = (*func)[0]; *toArgs << val->toQString(); for (int i = 1; i < func->length(); i++) { val = (*func)[i]; if (val->isObject() && val->as()->isArrayObject()) { QV4::ScopedValue valtmp(scope); QV4::Object *array = val->as(); uint length = array->getLength(); for (uint ii = 0; ii < length; ++ii) { valtmp = array->getIndexed(ii); *toArgs << valtmp->toQStringNoThrow(); } } else { *toArgs << val->toQString(); } } return true; } } /*! \internal */ bool Component::addOperation(QQmlV4Function *func) { QStringList args; if (convert(func, &args)) return addOperation(args[0], args.mid(1)); return false; } /*! Creates and adds an installation operation for \a operation. Add any number of \a parameters. The variables that the parameters contain, such as \c @TargetDir@, are replaced with their values. \sa {component::addOperation}{component.addOperation} */ bool Component::addOperation(const QString &operation, const QStringList ¶meters) { if (Operation *op = createOperation(operation, parameters)) { addOperation(op); return true; } return false; } /*! \internal */ bool Component::addElevatedOperation(QQmlV4Function *func) { QStringList args; if (convert(func, &args)) return addElevatedOperation(args[0], args.mid(1)); return false; } /*! Creates and adds the installation operation \a operation. Add any number of \a parameters. The variables that the parameters contain, such as \c @TargetDir@, are replaced with their values. The operation is executed with elevated rights. \sa {component::addElevatedOperation}{component.addElevatedOperation} */ bool Component::addElevatedOperation(const QString &operation, const QStringList ¶meters) { if (Operation *op = createOperation(operation, parameters)) { addElevatedOperation(op); return true; } return false; } /*! Specifies whether operations should be automatically created when the installation starts. This would be done by calling createOperations(). If you set this to \c false, it is completely up to the component's script to create all operations. \sa {component::autoCreateOperations}{component.autoCreateOperations} */ bool Component::autoCreateOperations() const { return d->m_autoCreateOperations; } void Component::setAutoCreateOperations(bool autoCreateOperations) { d->m_autoCreateOperations = autoCreateOperations; } /*! Returns whether this component is virtual. */ bool Component::isVirtual() const { return d->m_vars.value(scVirtual, scFalse).toLower() == scTrue; } /*! Returns whether the component is selected. */ bool Component::isSelected() const { return checkState() != Qt::Unchecked; } /*! Returns whether this component should always be installed. */ bool Component::forcedInstallation() const { return d->m_vars.value(scForcedInstallation, scFalse).toLower() == scTrue; } /*! Sets the validator callback name to \a name. */ void Component::setValidatorCallbackName(const QString &name) { validatorCallbackName = name; } /*! Calls the script method with the validator callback name. Returns \c true if the method returns \c true. Always returns \c true if the validator callback name is empty. */ bool Component::validatePage() { if (!validatorCallbackName.isEmpty()) return d->scriptEngine()->callScriptMethod(d->m_scriptContext, validatorCallbackName).toBool(); return true; } /*! Adds the component specified by \a newDependency to the list of dependencies. \sa {component::addDependency}{component.addDependency} \sa dependencies */ void Component::addDependency(const QString &newDependency) { QString oldDependencies = d->m_vars.value(scDependencies); if (oldDependencies.isEmpty()) setValue(scDependencies, newDependency); else setValue(scDependencies, oldDependencies + QLatin1String(", ") + newDependency); } QStringList Component::dependencies() const { return d->m_vars.value(scDependencies).split(QInstaller::commaRegExp(), QString::SkipEmptyParts); } QStringList Component::autoDependencies() const { return d->m_vars.value(scAutoDependOn).split(QInstaller::commaRegExp(), QString::SkipEmptyParts); } /*! \sa {component::setInstalled}{component.setInstalled} */ void Component::setInstalled() { setValue(scCurrentState, scInstalled); } /*! Determines whether the component comes as an auto dependency. Returns \c true if all components in \a componentsToInstall are already installed or selected for installation and this component thus needs to be installed as well. \sa {component::isAutoDependOn}{component.isAutoDependOn} */ bool Component::isAutoDependOn(const QSet &componentsToInstall) const { // If there is no auto depend on value or the value is empty, we have nothing todo. The component does // not need to be installed as an auto dependency. QStringList autoDependOnList = autoDependencies(); if (autoDependOnList.isEmpty()) return false; QSet components = componentsToInstall; const QStringList installedPackages = d->m_core->localInstalledPackages().keys(); foreach (const QString &name, installedPackages) components.insert(name); foreach (const QString &component, components) { autoDependOnList.removeAll(component); if (autoDependOnList.isEmpty()) { // If all components in the isAutoDependOn field are already installed or selected for // installation, this component needs to be installed as well. return true; } } return false; } bool Component::isDefault() const { if (isVirtual()) return false; // the script can override this method if (d->m_vars.value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0) { QJSValue valueFromScript; try { valueFromScript = d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("isDefault")); } catch (const Error &error) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("isDefaultError"), tr("Cannot resolve isDefault in %1").arg(name()), error.message()); return false; } if (!valueFromScript.isError()) return valueFromScript.toBool(); qDebug() << "Value from script is not valid." << (valueFromScript.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : valueFromScript.toString()); return false; } return d->m_vars.value(scDefault).compare(scTrue, Qt::CaseInsensitive) == 0; } bool Component::isInstalled(const QString version) const { if (version.isEmpty()) { return scInstalled == d->m_vars.value(scCurrentState); } else { return d->m_vars.value(scInstalledVersion) == version; } } /*! Returns whether the user wants to install the component. \sa {component::installationRequested}{component.installationRequested} */ bool Component::installationRequested() const { return installAction() == Install; } /*! Returns whether the component is selected for installation. */ bool Component::isSelectedForInstallation() const { return !isInstalled() && isSelected(); } /*! Sets the \a isUpdateAvailable flag to \c true to indicate that the core found an update. \sa {component::setUpdateAvailable}{component.setUpdateAvailable} */ void Component::setUpdateAvailable(bool isUpdateAvailable) { d->m_updateIsAvailable = isUpdateAvailable; } /*! Returns whether the user wants to install the update for this component. \sa {component::updateRequested}{component.updateRequested} */ bool Component::updateRequested() { return d->m_updateIsAvailable && isSelected(); } /*! Returns \c true if that component will be changed (update, installation, or uninstallation). \sa {component::componentChangeRequested}{component.componentChangeRequested} */ bool Component::componentChangeRequested() { return updateRequested() || isSelectedForInstallation() || uninstallationRequested(); } /*! \sa {component::setUninstalled}{component.setUninstalled} */ void Component::setUninstalled() { setValue(scCurrentState, scUninstalled); } /*! Returns whether the component is uninstalled. \sa {component::isUninstalled}{component.isUninstalled} */ bool Component::isUninstalled() const { return scUninstalled == d->m_vars.value(scCurrentState); } /*! Returns whether the user wants to uninstall the component. \sa {component::uninstallationRequested}{component.uninstallationRequested} */ bool Component::uninstallationRequested() const { if (packageManagerCore()->isUpdater()) return false; return isInstalled() && !isSelected(); } /*! Returns whether this component has been loaded from an online repository. \sa {component::isFromOnlineRepository}{component.isFromOnlineRepository} \sa addDownloadableArchive(), fromOnlineRepository */ bool Component::isFromOnlineRepository() const { return !repositoryUrl().isEmpty(); } /*! Contains the repository URL this component is downloaded from. If this component is not downloaded from an online repository, returns an empty QUrl. */ QUrl Component::repositoryUrl() const { return d->m_repositoryUrl; } /*! Sets this component's repository URL as \a url. */ void Component::setRepositoryUrl(const QUrl &url) { d->m_repositoryUrl = url; } /*! Returns the path to the local directory where the component is temporarily stored. */ QString Component::localTempPath() const { return d->m_localTempPath; } void Component::setLocalTempPath(const QString &tempLocalPath) { d->m_localTempPath = tempLocalPath; } void Component::updateModelData(const QString &key, const QString &data) { if (key == scVirtual) { setData(data.toLower() == scTrue ? d->m_core->virtualComponentsFont() : QFont(), Qt::FontRole); if (Component *const parent = parentComponent()) { parent->removeComponent(this); parent->appendComponent(this); } emit virtualStateChanged(); } if (key == scRemoteDisplayVersion) setData(data, RemoteDisplayVersion); if (key == scDisplayName) setData(data, Qt::DisplayRole); if (key == scDisplayVersion) setData(data, LocalDisplayVersion); if (key == scReleaseDate) setData(data, ReleaseDate); if (key == scUncompressedSize) { quint64 size = d->m_vars.value(scUncompressedSizeSum).toLongLong(); setData(humanReadableSize(size), UncompressedSize); } const QString &updateInfo = d->m_vars.value(scUpdateText); if (!d->m_core->isUpdater() || updateInfo.isEmpty()) { const QString tooltipText = QString::fromLatin1("%1").arg(d->m_vars.value(scDescription)); setData(tooltipText, Qt::ToolTipRole); } else { const QString tooltipText = d->m_vars.value(scDescription) + QLatin1String("

") + tr("Update Info: ") + updateInfo; setData(tooltipText, Qt::ToolTipRole); } } /*! Returns the debugging output stream, \a dbg, for the component \a component. */ QDebug QInstaller::operator<<(QDebug dbg, Component *component) { dbg << "component: " << component->name() << "\n"; dbg << "\tisSelected: \t" << component->isSelected() << "\n"; dbg << "\tisInstalled: \t" << component->isInstalled() << "\n"; dbg << "\tisUninstalled: \t" << component->isUninstalled() << "\n"; dbg << "\tupdateRequested: \t" << component->updateRequested() << "\n"; dbg << "\tinstallationRequested: \t" << component->installationRequested() << "\n"; dbg << "\tuninstallationRequested: \t" << component->uninstallationRequested() << "\n"; return dbg; } src/libs/installer/component.h000066400000000000000000000207201325366651500167630ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef COMPONENT_H #define COMPONENT_H #include "constants.h" #include "component_p.h" #include "qinstallerglobal.h" #include #include #include #include QT_FORWARD_DECLARE_CLASS(QDebug) QT_FORWARD_DECLARE_CLASS(QQmlV4Function) namespace QInstaller { class PackageManagerCore; class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper { Q_OBJECT Q_DISABLE_COPY(Component) Q_PROPERTY(QString name READ name) Q_PROPERTY(QString displayName READ displayName) Q_PROPERTY(bool autoCreateOperations READ autoCreateOperations WRITE setAutoCreateOperations) Q_PROPERTY(QStringList archives READ archives) Q_PROPERTY(QStringList userInterfaces READ userInterfaces) Q_PROPERTY(QStringList dependencies READ dependencies) Q_PROPERTY(QStringList autoDependencies READ autoDependencies) Q_PROPERTY(bool fromOnlineRepository READ isFromOnlineRepository) Q_PROPERTY(QUrl repositoryUrl READ repositoryUrl) Q_PROPERTY(bool default READ isDefault) Q_PROPERTY(bool installed READ isInstalled) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) public: explicit Component(PackageManagerCore *core); ~Component(); struct SortingPriorityLessThan { bool operator() (const Component *lhs, const Component *rhs) const { const int lhsPriority = lhs->value(scSortingPriority).toInt(); const int rhsPriority = rhs->value(scSortingPriority).toInt(); if (lhsPriority == rhsPriority) return lhs->displayName() > rhs->displayName(); return lhsPriority < rhsPriority; } }; struct SortingPriorityGreaterThan { bool operator() (const Component *lhs, const Component *rhs) const { const int lhsPriority = lhs->value(scSortingPriority).toInt(); const int rhsPriority = rhs->value(scSortingPriority).toInt(); if (lhsPriority == rhsPriority) return lhs->displayName() < rhs->displayName(); return lhsPriority > rhsPriority; } }; void loadDataFromPackage(const Package &package); void loadDataFromPackage(const KDUpdater::LocalPackage &package); QHash variables() const; Q_INVOKABLE void setValue(const QString &key, const QString &value); Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const; QStringList archives() const; PackageManagerCore *packageManagerCore() const; Component *parentComponent() const; void appendComponent(Component *component); void removeComponent(Component *component); QList descendantComponents() const; void loadComponentScript(); //move this to private void loadComponentScript(const QString &fileName); void loadTranslations(const QDir &directory, const QStringList &qms); void loadUserInterfaces(const QDir &directory, const QStringList &uis); void loadLicenses(const QString &directory, const QHash &hash); void markAsPerformedInstallation(); QStringList userInterfaces() const; QHash > licenses() const; Q_INVOKABLE QWidget *userInterface(const QString &name) const; Q_INVOKABLE virtual void beginInstallation(); Q_INVOKABLE virtual void createOperations(); Q_INVOKABLE virtual void createOperationsForArchive(const QString &archive); Q_INVOKABLE virtual void createOperationsForPath(const QString &path); QList > pathsForUninstallation() const; Q_INVOKABLE void registerPathForUninstallation(const QString &path, bool wipe = false); OperationList operations() const; void addOperation(Operation *operation); Q_INVOKABLE bool addOperation(QQmlV4Function *args); bool addOperation(const QString &operation, const QStringList ¶meters); void addElevatedOperation(Operation *operation); Q_INVOKABLE bool addElevatedOperation(QQmlV4Function *args); bool addElevatedOperation(const QString &operation, const QStringList ¶meters); QStringList downloadableArchives() const; Q_INVOKABLE void addDownloadableArchive(const QString &path); Q_INVOKABLE void removeDownloadableArchive(const QString &path); QStringList stopProcessForUpdateRequests() const; Q_INVOKABLE void addStopProcessForUpdateRequest(const QString &process); Q_INVOKABLE void removeStopProcessForUpdateRequest(const QString &process); Q_INVOKABLE void setStopProcessForUpdateRequest(const QString &process, bool requested); QString name() const; QString displayName() const; quint64 updateUncompressedSize(); QUrl repositoryUrl() const; void setRepositoryUrl(const QUrl &url); Q_INVOKABLE void addDependency(const QString &newDependency); QStringList dependencies() const; QStringList autoDependencies() const; void languageChanged(); QString localTempPath() const; bool autoCreateOperations() const; bool operationsCreatedSuccessfully() const; Q_INVOKABLE bool isDefault() const; Q_INVOKABLE bool isAutoDependOn(const QSet &componentsToInstall) const; Q_INVOKABLE void setInstalled(); Q_INVOKABLE bool isInstalled(const QString version = QString()) const; Q_INVOKABLE bool installationRequested() const; bool isSelectedForInstallation() const; Q_INVOKABLE void setUninstalled(); Q_INVOKABLE bool isUninstalled() const; Q_INVOKABLE bool uninstallationRequested() const; Q_INVOKABLE bool isFromOnlineRepository() const; Q_INVOKABLE void setUpdateAvailable(bool isUpdateAvailable); Q_INVOKABLE bool updateRequested(); Q_INVOKABLE bool componentChangeRequested(); bool isVirtual() const; bool isSelected() const; bool forcedInstallation() const; void setValidatorCallbackName(const QString &name); bool validatePage(); public Q_SLOTS: void setAutoCreateOperations(bool autoCreateOperations); Q_SIGNALS: void loaded(); void virtualStateChanged(); void valueChanged(const QString &key, const QString &value); private Q_SLOTS: void updateModelData(const QString &key, const QString &value); private: void setLocalTempPath(const QString &tempPath); Operation *createOperation(const QString &operationName, const QString ¶meter1 = QString(), const QString ¶meter2 = QString(), const QString ¶meter3 = QString(), const QString ¶meter4 = QString(), const QString ¶meter5 = QString(), const QString ¶meter6 = QString(), const QString ¶meter7 = QString(), const QString ¶meter8 = QString(), const QString ¶meter9 = QString(), const QString ¶meter10 = QString()); Operation *createOperation(const QString &operationName, const QStringList ¶meters); private: QString validatorCallbackName; ComponentPrivate *d; }; QDebug operator<<(QDebug dbg, Component *component); } // namespace QInstaller Q_DECLARE_METATYPE(QInstaller::Component*) #endif // COMPONENT_H src/libs/installer/component_p.cpp000066400000000000000000000216751325366651500176470ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "component_p.h" #include "component.h" #include "packagemanagercore.h" #include namespace QInstaller { // -- ComponentPrivate ComponentPrivate::ComponentPrivate(PackageManagerCore *core, Component *qq) : q(qq) , m_core(core) , m_parentComponent(0) , m_licenseOperation(0) , m_minimumProgressOperation(0) , m_newlyInstalled (false) , m_operationsCreated(false) , m_autoCreateOperations(true) , m_operationsCreatedSuccessfully(true) , m_updateIsAvailable(false) { } ComponentPrivate::~ComponentPrivate() { // Before we can delete the added widgets, they need to be removed from the wizard first. foreach (const QString &widgetName, m_userInterfaces.keys()) { m_core->removeWizardPage(q, widgetName); m_core->removeWizardPageItem(q, widgetName); } // Use QPointer here instead of raw pointers. This is a requirement that needs to be met cause possible // Ui elements get added during component script run and might be destroyed by the package manager gui // before the actual component gets destroyed. Avoids a possible delete call on a dangling pointer. foreach (const QPointer widget, m_userInterfaces) delete widget.data(); } ScriptEngine *ComponentPrivate::scriptEngine() const { return m_core->componentScriptEngine(); } // -- ComponentModelHelper ComponentModelHelper::ComponentModelHelper() { setCheckState(Qt::Unchecked); setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable)); } /*! Returns the number of child components. Depending if virtual components are visible or not, the count might differ from what one will get if calling Component::childComponents(...).count(). */ int ComponentModelHelper::childCount() const { if (m_componentPrivate->m_core->virtualComponentsVisible()) return m_componentPrivate->m_allChildComponents.count(); return m_componentPrivate->m_childComponents.count(); } /*! Returns the component at index position in the list. Index must be a valid position in the list (i.e., index >= 0 && index < childCount()). Otherwise it returns 0. */ Component *ComponentModelHelper::childAt(int index) const { if (index < 0 && index >= childCount()) return 0; if (m_componentPrivate->m_core->virtualComponentsVisible()) return m_componentPrivate->m_allChildComponents.value(index, 0); return m_componentPrivate->m_childComponents.value(index, 0); } /*! Returns all descendants of this component depending if virtual components are visible or not. */ QList ComponentModelHelper::childItems() const { QList *components = &m_componentPrivate->m_childComponents; if (m_componentPrivate->m_core->virtualComponentsVisible()) components = &m_componentPrivate->m_allChildComponents; QList result; foreach (Component *const component, *components) { result.append(component); result += component->childItems(); } return result; } /*! Determines if the installation status of the component can be changed. The default value is true. */ bool ComponentModelHelper::isEnabled() const { return (flags() & Qt::ItemIsEnabled) != 0; } /*! Enables or disables the ability to change the installation status of the components. */ void ComponentModelHelper::setEnabled(bool enabled) { changeFlags(enabled, Qt::ItemIsEnabled); } /*! Returns whether the component is tri-state; that is, if it's checkable with three separate states. The default value is false. */ bool ComponentModelHelper::isTristate() const { return (flags() & Qt::ItemIsTristate) != 0; } /*! Sets whether the component is tri-state. If tri-state is true, the component is checkable with three separate states; otherwise, the component is checkable with two states. Note: this also requires that the component is checkable. \sa isCheckable() */ void ComponentModelHelper::setTristate(bool tristate) { changeFlags(tristate, Qt::ItemIsTristate); } /*! Returns whether the component is user-checkable. The default value is true. */ bool ComponentModelHelper::isCheckable() const { return (flags() & Qt::ItemIsUserCheckable) != 0; } /*! Sets whether the component is user-checkable. If checkable is true, the component can be checked by the user; otherwise, the user cannot check the component. The delegate will render a checkable component with a check box next to the component's text. */ void ComponentModelHelper::setCheckable(bool checkable) { if (checkable && !isCheckable()) { // make sure there's data for the check state role if (!data(Qt::CheckStateRole).isValid()) setData(Qt::Unchecked, Qt::CheckStateRole); } changeFlags(checkable, Qt::ItemIsUserCheckable); } /*! Returns whether the component is selectable by the user. The default value is true. */ bool ComponentModelHelper::isSelectable() const { return (flags() & Qt::ItemIsSelectable) != 0; } /*! Sets whether the component is selectable. If selectable is true, the component can be selected by the user; otherwise, the user cannot select the component. */ void ComponentModelHelper::setSelectable(bool selectable) { changeFlags(selectable, Qt::ItemIsSelectable); } ComponentModelHelper::InstallAction ComponentModelHelper::installAction() const { return data(ComponentModelHelper::Action).value(); } void ComponentModelHelper::setInstallAction(ComponentModelHelper::InstallAction action) { setData(QVariant::fromValue(action), ComponentModelHelper::Action); } /*! Returns the item flags for the component. The item flags determine how the user can interact with the component. */ Qt::ItemFlags ComponentModelHelper::flags() const { QVariant variant = data(Qt::UserRole - 1); if (!variant.isValid()) return Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); return Qt::ItemFlags(variant.toInt()); } /*! Sets the item flags for the component to flags. The item flags determine how the user can interact with the component. This is often used to disable an component. */ void ComponentModelHelper::setFlags(Qt::ItemFlags flags) { setData(int(flags), Qt::UserRole - 1); } /*! Returns the checked state of the component. */ Qt::CheckState ComponentModelHelper::checkState() const { return Qt::CheckState(qvariant_cast(data(Qt::CheckStateRole))); } /*! Sets the check state of the component to be state. */ void ComponentModelHelper::setCheckState(Qt::CheckState state) { setData(state, Qt::CheckStateRole); } /*! Returns the component's data for the given role, or an invalid QVariant if there is no data for role. */ QVariant ComponentModelHelper::data(int role) const { return m_values.value((role == Qt::EditRole ? Qt::DisplayRole : role), QVariant()); } /*! Sets the component's data for the given role to the specified value. */ void ComponentModelHelper::setData(const QVariant &value, int role) { m_values.insert((role == Qt::EditRole ? Qt::DisplayRole : role), value); } // -- protected void ComponentModelHelper::setPrivate(ComponentPrivate *componentPrivate) { m_componentPrivate = componentPrivate; } // -- private void ComponentModelHelper::changeFlags(bool enable, Qt::ItemFlags itemFlags) { setFlags(enable ? flags() |= itemFlags : flags() &= ~itemFlags); } } // namespace QInstaller src/libs/installer/component_p.h000066400000000000000000000106141325366651500173030ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef COMPONENT_P_H #define COMPONENT_P_H #include "qinstallerglobal.h" #include #include #include #include namespace QInstaller { class Component; class PackageManagerCore; class ScriptEngine; class ComponentPrivate { QInstaller::Component* const q; public: explicit ComponentPrivate(PackageManagerCore *core, Component *qq); ~ComponentPrivate(); ScriptEngine *scriptEngine() const; PackageManagerCore *m_core; Component *m_parentComponent; OperationList m_operations; Operation *m_licenseOperation; Operation *m_minimumProgressOperation; bool m_newlyInstalled; bool m_operationsCreated; bool m_autoCreateOperations; bool m_operationsCreatedSuccessfully; bool m_updateIsAvailable; QString m_componentName; QUrl m_repositoryUrl; QString m_localTempPath; QJSValue m_scriptContext; QHash m_vars; QList m_childComponents; QList m_allChildComponents; QStringList m_downloadableArchives; QStringList m_stopProcessForUpdateRequests; QHash > m_userInterfaces; // < display name, < file name, file content > > QHash > m_licenses; QList > m_pathsForUninstallation; }; // -- ComponentModelHelper class INSTALLER_EXPORT ComponentModelHelper { public: enum Roles { Action = Qt::UserRole + 1, LocalDisplayVersion, RemoteDisplayVersion, ReleaseDate, UncompressedSize }; enum InstallAction { Install, Uninstall, KeepInstalled, KeepUninstalled }; enum Column { NameColumn = 0, ActionColumn, InstalledVersionColumn, NewVersionColumn, ReleaseDateColumn, UncompressedSizeColumn, LastColumn }; explicit ComponentModelHelper(); int childCount() const; Component* childAt(int index) const; QList childItems() const; bool isEnabled() const; void setEnabled(bool enabled); bool isTristate() const; void setTristate(bool tristate); bool isCheckable() const; void setCheckable(bool checkable); bool isSelectable() const; void setSelectable(bool selectable); InstallAction installAction() const; void setInstallAction(InstallAction action); Qt::ItemFlags flags() const; void setFlags(Qt::ItemFlags flags); Qt::CheckState checkState() const; void setCheckState(Qt::CheckState state); QVariant data(int role = Qt::UserRole + 1) const; void setData(const QVariant &value, int role = Qt::UserRole + 1); protected: void setPrivate(ComponentPrivate *componentPrivate); private: void changeFlags(bool enable, Qt::ItemFlags itemFlags); private: QHash m_values; ComponentPrivate *m_componentPrivate; }; } // namespace QInstaller Q_DECLARE_METATYPE(QInstaller::ComponentModelHelper::InstallAction) #endif // COMPONENT_P_H src/libs/installer/componentchecker.cpp000066400000000000000000000172541325366651500206530ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "componentchecker.h" #include "component.h" #include "constants.h" #include "packagemanagercore.h" #include "globals.h" namespace QInstaller { QStringList ComponentChecker::checkComponent(Component *component) { QStringList checkResult; if (!component) return checkResult; PackageManagerCore *core = component->packageManagerCore(); if (!core) return checkResult; if (component->childCount() && !component->archives().isEmpty()) { checkResult << QString::fromLatin1("Component %1 contains data to be installed " "while having child components. This may not work properly.") .arg(component->name()); } const bool defaultPropertyScriptValue = component->variables().value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0; const bool defaultPropertyValue = component->variables().value(scDefault).compare(scTrue, Qt::CaseInsensitive) == 0; const QStringList autoDependencies = component->autoDependencies(); const QList allComponents = core->components(PackageManagerCore::ComponentType::All); if (!autoDependencies.isEmpty()) { if (component->forcedInstallation()) { checkResult << QString::fromLatin1("Component %1 specifies \"ForcedInstallation\" property " "together with \"AutoDependOn\" list. This combination of states may not work properly.") .arg(component->name()); } if (defaultPropertyScriptValue) { checkResult << QString::fromLatin1("Component %1 specifies script value for \"Default\" " "property together with \"AutoDependOn\" list. This combination of states may not " "work properly.").arg(component->name()); } if (defaultPropertyValue) { checkResult << QString::fromLatin1("Component %1 specifies \"Default\" property together " "with \"AutoDependOn\" list. This combination of states may not work properly.") .arg(component->name()); } if (!core->dependees(component).isEmpty()) { checkResult << QString::fromLatin1("Other components depend on auto dependent " "component %1. This may not work properly.") .arg(component->name()); } } if (component->packageManagerCore()->isInstaller()) { if (component->isTristate()) { if (defaultPropertyScriptValue) { checkResult << QString::fromLatin1("Component %1 specifies script value for \"Default\" " "property while not being a leaf node. The \"Default\" property will get a " "\"false\" value.").arg(component->name()); } if (defaultPropertyValue) { checkResult << QString::fromLatin1("Component %1 specifies \"Default\" property " "while not being a leaf node. The \"Default\" property will get a \"false\" value.") .arg(component->name()); } } if (!component->isCheckable()) { if (defaultPropertyScriptValue) { checkResult << QString::fromLatin1("Component %1 specifies script value for \"Default\" " "property while being not checkable. The \"Default\" property will get a \"false\" " "value.").arg(component->name()); } if (defaultPropertyValue) { checkResult << QString::fromLatin1("Component %1 specifies \"Default\" property " "while being not checkable. The \"Default\" property will get a \"false\" value.") .arg(component->name()); } } if (component->childCount()) { if (!autoDependencies.isEmpty()) { /* Use case: A (depends on C) A.B C Let's say we installed everything. Running maintenance tool and unselecting C will mark A for uninstallation too, while A.B stays marked as installed. After running maintenance tool again, it will check A automatically (since its child is selected), this will also mark C for installation (dependecy). Moreover, the "Next" button will be disabled. */ checkResult << QString::fromLatin1("Component %1 auto depends on other components " "while having child components. This will not work properly.") .arg(component->name()); } if (!component->dependencies().isEmpty()) { checkResult << QString::fromLatin1("Component %1 depends on other components " "while having child components. This will not work properly.") .arg(component->name()); } if (!core->dependees(component).isEmpty()) { /* Use case: A A.B C (depends on A) Selecting C marks A for installation too, A.B is not marked for installation. So after installation, A and C are installed, while A.B is not. Maintenance tool will uncheck A automatically (since none of its children are installed) this will also mark C for uninstallation (dependency). Moreover, the "Next" button will be disabled. */ checkResult << QString::fromLatin1("Other components depend on component %1 " "which has child components. This will not work properly.") .arg(component->name()); } } foreach (const QString &autoDependency, autoDependencies) { Component *autoDependencyComponent = PackageManagerCore::componentByName( autoDependency, allComponents); if (autoDependencyComponent && autoDependencyComponent->childCount()) { checkResult << QString::fromLatin1("Component %1 auto depends on component %2 " "which has children components. This will not work properly.") .arg(component->name(), autoDependencyComponent->name()); } } } return checkResult; } } // namespace QInstaller src/libs/installer/componentchecker.h000066400000000000000000000032421325366651500203100ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef COMPONENTCHECKER_H #define COMPONENTCHECKER_H #include "installer_global.h" #include namespace QInstaller { class Component; class INSTALLER_EXPORT ComponentChecker { Q_DECLARE_TR_FUNCTIONS(ComponentChecker) public: static QStringList checkComponent(Component *component); private: ComponentChecker(); }; } #endif // INSTALLERCALCULATOR_H src/libs/installer/componentmodel.cpp000066400000000000000000000530121325366651500203370ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "componentmodel.h" #include "component.h" #include "packagemanagercore.h" #include namespace QInstaller { /*! \class QInstaller::ComponentModel \inmodule QtInstallerFramework \brief The ComponentModel class holds a data model for visual representation of available components to install. */ /*! \enum ComponentModel::ModelStateFlag This enum value holds the checked state of the components available for installation. \value AllChecked All components are checked. \value AllUnchecked No components are checked. \value DefaultChecked The components to be installed by default are checked. \value PartiallyChecked Some components are checked. */ /*! \fn void ComponentModel::checkStateChanged(const QModelIndex &index) This signal is emitted whenever the checked state of a component is changed. The \a index value indicates the QModelIndex representation of the component as seen from the model. */ /*! \fn void ComponentModel::checkStateChanged(QInstaller::ComponentModel::ModelState state) This signal is emitted whenever the checked state of a model is changed after all state calculations have taken place. The \a state is a combination of \c ModelStateFlag values indicating whether the model has its default checked state, all components are checked or unchecked, or some individual component's checked state has changed. */ class IconCache { public: IconCache() { m_icons.insert(ComponentModelHelper::Install, QIcon(QLatin1String(":/install.png"))); m_icons.insert(ComponentModelHelper::Uninstall, QIcon(QLatin1String(":/uninstall.png"))); m_icons.insert(ComponentModelHelper::KeepInstalled, QIcon(QLatin1String(":/keepinstalled.png"))); m_icons.insert(ComponentModelHelper::KeepUninstalled, QIcon(QLatin1String(":/keepuninstalled.png"))); } QIcon icon(ComponentModelHelper::InstallAction action) const { return m_icons.value(action); } private: QMap m_icons; }; Q_GLOBAL_STATIC(IconCache, iconCache) /*! Constructs a component model with the given number of \a columns and \a core as parent. */ ComponentModel::ComponentModel(int columns, PackageManagerCore *core) : QAbstractItemModel(core) , m_core(core) , m_modelState(DefaultChecked) { m_headerData.insert(0, columns, QVariant()); connect(this, &QAbstractItemModel::modelReset, this, &ComponentModel::slotModelReset); } /*! Destroys the component model. */ ComponentModel::~ComponentModel() { } /*! Returns the item flags for the given \a index. Returns a combination of flags that enables the item (Qt::ItemIsEnabled) and allows it to be selected (Qt::ItemIsSelectable) and to be checked (Qt::ItemIsUserCheckable). */ Qt::ItemFlags ComponentModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; if (Component *component = componentFromIndex(index)) return component->flags(); return Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); } /*! Returns the number of items under the given \a parent. When the parent index is invalid, the returned value is the root item count. */ int ComponentModel::rowCount(const QModelIndex &parent) const { if (Component *component = componentFromIndex(parent)) return component->childCount(); return m_rootComponentList.count(); } /*! Returns the number of columns of the given \a parent. */ int ComponentModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return m_headerData.size(); } /*! Returns the parent item of the given \a child. If the item has no parent, an invalid QModelIndex is returned. */ QModelIndex ComponentModel::parent(const QModelIndex &child) const { if (!child.isValid()) return QModelIndex(); if (Component *childComponent = componentFromIndex(child)) { if (Component *parent = childComponent->parentComponent()) return indexFromComponentName(parent->name()); } return QModelIndex(); } /*! Returns the index of the item in the model specified by the given \a row, \a column, and \a parent index. */ QModelIndex ComponentModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid() && (row >= rowCount(parent) || column >= columnCount())) return QModelIndex(); if (Component *parentComponent = componentFromIndex(parent)) { if (Component *childComponent = parentComponent->childAt(row)) return createIndex(row, column, childComponent); } else if (row < m_rootComponentList.count()) { return createIndex(row, column, m_rootComponentList.at(row)); } return QModelIndex(); } /*! Returns the data stored under the given \a role for the item referred to by the \a index. \note An \e invalid QVariant is returned if the given index is invalid. Qt::CheckStateRole is only supported for the first column of the model. Qt::EditRole, Qt::DisplayRole and Qt::ToolTipRole are specifically handled for columns greater than the first column and translate to \c {Qt::UserRole + index.column()}. */ QVariant ComponentModel::data(const QModelIndex &index, int role) const { if (Component *component = componentFromIndex(index)) { if (index.column() > 0) { if (role == Qt::CheckStateRole) return QVariant(); if (index.column() == ComponentModelHelper::ActionColumn) { if (role == Qt::DecorationRole) return iconCache->icon(component->installAction()); if (role == Qt::ToolTipRole) { switch (component->installAction()) { case ComponentModelHelper::Install: return tr("Component is marked for installation."); case ComponentModelHelper::Uninstall: return tr("Component is marked for uninstallation."); case ComponentModelHelper::KeepInstalled: return tr("Component is installed."); case ComponentModelHelper::KeepUninstalled: return tr("Component is not installed."); default: return QString(); } } return QVariant(); } if (role == Qt::EditRole || role == Qt::DisplayRole || role == Qt::ToolTipRole) return component->data(Qt::UserRole + index.column()); } if (role == Qt::CheckStateRole) { if (!component->isCheckable()) return QVariant(); if (!component->autoDependencies().isEmpty()) return QVariant(); } return component->data(role); } return QVariant(); } /*! Sets the \a role data for the item at \a index to \a value. Returns true if successful; otherwise returns false. The dataChanged() signal is emitted if the data was successfully set. The checkStateChanged() signals are emitted in addition if the checked state of the item is set. */ bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) return false; Component *component = componentFromIndex(index); if (!component) return false; if (role == Qt::CheckStateRole) { if (index.column() != 0) return false; ComponentSet nodes = component->childItems().toSet(); Qt::CheckState newValue = Qt::CheckState(value.toInt()); if (newValue == Qt::PartiallyChecked) { const Qt::CheckState oldValue = component->checkState(); newValue = (oldValue == Qt::Checked) ? Qt::Unchecked : Qt::Checked; } QSet changed = updateCheckedState(nodes << component, newValue); foreach (const QModelIndex &index, changed) { emit dataChanged(index, index); emit checkStateChanged(index); } updateAndEmitModelState(); // update the internal state } else { component->setData(value, role); emit dataChanged(index, index); } return true; } /*! Returns the data for the given \a role and \a section in the header with the specified \a orientation. An \e invalid QVariant is returned if \a section is out of bounds, \a orientation is not Qt::Horizontal or \a role is anything else than Qt::DisplayRole. */ QVariant ComponentModel::headerData(int section, Qt::Orientation orientation, int role) const { if (section >= 0 && section < columnCount() && orientation == Qt::Horizontal && role == Qt::DisplayRole) return m_headerData.at(section); return QVariant(); } /*! Sets the data for the given \a role and \a section in the header with the specified \a orientation to the \a value supplied. Returns \c true if the header's data was updated; otherwise returns \c false. The headerDataChanged() signal is emitted if the data was successfully set. \note Only Qt::Horizontal orientation is supported. */ bool ComponentModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { if (section >= 0 && section < columnCount() && orientation == Qt::Horizontal && (role == Qt::DisplayRole || role == Qt::EditRole)) { m_headerData.replace(section, value); emit headerDataChanged(orientation, section, section); return true; } return false; } /*! Returns a list of checked components. */ QSet ComponentModel::checked() const { return m_currentCheckedState[Qt::Checked]; } /*! Returns a list of partially checked components. */ QSet ComponentModel::partially() const { return m_currentCheckedState[Qt::PartiallyChecked]; } /*! Returns a list of unchecked components. */ QSet ComponentModel::unchecked() const { return m_currentCheckedState[Qt::Unchecked]; } /*! Returns a list of components whose checked state cannot be changed. If package manager core is run with no forced installation argument, the list will always be empty. */ QSet ComponentModel::uncheckable() const { return m_uncheckable; } /*! Returns a pointer to the PackageManagerCore this model belongs to. */ PackageManagerCore *ComponentModel::core() const { return m_core; } /*! Returns the current checked state of the model. */ ComponentModel::ModelState ComponentModel::checkedState() const { return m_modelState; } /*! Translates between a given component \a name and its associated QModelIndex. Returns the QModelIndex that represents the component or an invalid QModelIndex if the component does not exist in the model. */ QModelIndex ComponentModel::indexFromComponentName(const QString &name) const { if (m_indexByNameCache.isEmpty()) { for (int i = 0; i < m_rootComponentList.count(); ++i) collectComponents(m_rootComponentList.at(i), index(i, 0, QModelIndex())); } return m_indexByNameCache.value(name, QModelIndex()); } /*! Translates between a given QModelIndex \a index and its associated Component. Returns the component if the index is valid or \c 0 if an invalid QModelIndex is given. */ Component *ComponentModel::componentFromIndex(const QModelIndex &index) const { if (index.isValid()) return static_cast(index.internalPointer()); return 0; } // -- public slots /*! Sets \a rootComponents to be the list of currently shown components. The model is repopulated and the individual component's checked state is used to show the check mark in front of the visual component representation. The modelAboutToBeReset() and modelReset() signals are emitted. */ void ComponentModel::setRootComponents(QList rootComponents) { beginResetModel(); m_uncheckable.clear(); m_indexByNameCache.clear(); m_rootComponentList.clear(); m_modelState = DefaultChecked; // Initialize these with an empty set for every possible state, cause we compare the hashes later in // updateAndEmitModelState(). The comparison than might lead to wrong results if one of the checked // states is absent initially. m_initialCheckedState[Qt::Checked] = ComponentSet(); m_initialCheckedState[Qt::Unchecked] = ComponentSet(); m_initialCheckedState[Qt::PartiallyChecked] = ComponentSet(); m_currentCheckedState = m_initialCheckedState; // both should be equal // show virtual components only in case we run as updater or if the core engine is set to show them const bool showVirtuals = m_core->isUpdater() || m_core->virtualComponentsVisible(); foreach (Component *const component, rootComponents) { connect(component, &Component::virtualStateChanged, this, &ComponentModel::onVirtualStateChanged); if ((!showVirtuals) && component->isVirtual()) continue; m_rootComponentList.append(component); } endResetModel(); } /*! Sets the checked state of every component in the model to be \a state. The ComponentModel::PartiallyChecked flag is ignored by this function. Note that components are not changed if they are not checkable. The dataChanged() and checkStateChanged() signals are emitted. */ void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag state) { QSet changed; switch (state) { case AllChecked: changed = updateCheckedState(m_currentCheckedState[Qt::Unchecked], Qt::Checked); break; case AllUnchecked: changed = updateCheckedState(m_currentCheckedState[Qt::Checked], Qt::Unchecked); break; case DefaultChecked: // record all changes, to be able to update the UI properly changed = updateCheckedState(m_currentCheckedState[Qt::Checked], Qt::Unchecked); changed += updateCheckedState(m_initialCheckedState[Qt::Checked], Qt::Checked); break; default: break; } if (changed.isEmpty()) return; // notify about changes done to the model foreach (const QModelIndex &index, changed) { emit dataChanged(index, index); emit checkStateChanged(index); } updateAndEmitModelState(); // update the internal state } // -- private slots void ComponentModel::slotModelReset() { ComponentList components = m_rootComponentList; if (!m_core->isUpdater()) { foreach (Component *const component, m_rootComponentList) components += component->childItems(); } ComponentSet checked; foreach (Component *const component, components) { if (component->checkState() == Qt::Checked) checked.insert(component); connect(component, &Component::virtualStateChanged, this, &ComponentModel::onVirtualStateChanged); } updateCheckedState(checked, Qt::Checked); foreach (Component *const component, components) { if (!component->isCheckable()) m_uncheckable.insert(component); m_initialCheckedState[component->checkState()].insert(component); } m_currentCheckedState = m_initialCheckedState; updateAndEmitModelState(); // update the internal state } void ComponentModel::onVirtualStateChanged() { // If the virtual state of a component changes, force a reset of the component model. setRootComponents(m_core->components(PackageManagerCore::ComponentType::Root)); } // -- private void ComponentModel::updateAndEmitModelState() { m_modelState = ComponentModel::DefaultChecked; if (m_initialCheckedState != m_currentCheckedState) m_modelState = ComponentModel::PartiallyChecked; if (checked().count() == 0 && partially().count() == 0) { m_modelState |= ComponentModel::AllUnchecked; m_modelState &= ~ComponentModel::PartiallyChecked; } if (unchecked().count() == 0 && partially().count() == 0) { m_modelState |= ComponentModel::AllChecked; m_modelState &= ~ComponentModel::PartiallyChecked; } emit checkStateChanged(m_modelState); foreach (const Component *component, m_rootComponentList) { emit dataChanged(indexFromComponentName(component->name()), indexFromComponentName(component->name())); QList children = component->childItems(); foreach (const Component *child, children) emit dataChanged(indexFromComponentName(child->name()), indexFromComponentName(child->name())); } } void ComponentModel::collectComponents(Component *const component, const QModelIndex &parent) const { m_indexByNameCache.insert(component->name(), parent); for (int i = 0; i < component->childCount(); ++i) collectComponents(component->childAt(i), index(i, 0, parent)); } namespace ComponentModelPrivate { static Qt::CheckState verifyPartiallyChecked(Component *component) { bool anyChecked = false; bool anyUnchecked = false; const int count = component->childCount(); for (int i = 0; i < count; ++i) { switch (component->childAt(i)->checkState()) { case Qt::Checked: { anyChecked = true; } break; case Qt::Unchecked: { anyUnchecked = true; } break; default: return Qt::PartiallyChecked; } if (anyChecked && anyUnchecked) return Qt::PartiallyChecked; } if (anyChecked) return Qt::Checked; if (anyUnchecked) return Qt::Unchecked; return Qt::PartiallyChecked; // never hit here } } // namespace ComponentModelPrivate QSet ComponentModel::updateCheckedState(const ComponentSet &components, Qt::CheckState state) { // get all parent nodes for the components we're going to update QMap sortedNodesMap; foreach (Component *component, components) { while (component && !sortedNodesMap.values(component->name()).contains(component)) { sortedNodesMap.insertMulti(component->name(), component); component = component->parentComponent(); } } QSet changed; const ComponentList sortedNodes = sortedNodesMap.values(); // we can start in descending order to check node and tri-state nodes properly for (int i = sortedNodes.count(); i > 0; i--) { Component * const node = sortedNodes.at(i - 1); bool checkable = true; if (node->value(scCheckable, scTrue).toLower() == scFalse) { checkable = false; } if ((!node->isCheckable() && checkable) || !node->isEnabled() || !node->autoDependencies().isEmpty()) continue; Qt::CheckState newState = state; const Qt::CheckState recentState = node->checkState(); if (node->isTristate()) newState = ComponentModelPrivate::verifyPartiallyChecked(node); if (recentState == newState) continue; node->setCheckState(newState); changed.insert(indexFromComponentName(node->name())); m_currentCheckedState[Qt::Checked].remove(node); m_currentCheckedState[Qt::Unchecked].remove(node); m_currentCheckedState[Qt::PartiallyChecked].remove(node); switch (newState) { case Qt::Checked: m_currentCheckedState[Qt::Checked].insert(node); break; case Qt::Unchecked: m_currentCheckedState[Qt::Unchecked].insert(node); break; case Qt::PartiallyChecked: m_currentCheckedState[Qt::PartiallyChecked].insert(node); break; } } return changed; } } // namespace QInstaller src/libs/installer/componentmodel.h000066400000000000000000000106171325366651500200100ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef COMPONENTMODEL_H #define COMPONENTMODEL_H #include "qinstallerglobal.h" #include #include #include #include namespace QInstaller { class Component; class PackageManagerCore; class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel { Q_OBJECT typedef QSet ComponentSet; typedef QList ComponentList; public: enum ModelStateFlag { AllChecked = 0x01, AllUnchecked = 0x02, DefaultChecked = 0x04, PartiallyChecked = 0x08 }; Q_DECLARE_FLAGS(ModelState, ModelStateFlag); explicit ComponentModel(int columns, PackageManagerCore *core = 0); ~ComponentModel(); Qt::ItemFlags flags(const QModelIndex &index) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &child) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole); QSet checked() const; QSet partially() const; QSet unchecked() const; QSet uncheckable() const; PackageManagerCore *core() const; ComponentModel::ModelState checkedState() const; QModelIndex indexFromComponentName(const QString &name) const; Component* componentFromIndex(const QModelIndex &index) const; public Q_SLOTS: void setRootComponents(QList rootComponents); void setCheckedState(QInstaller::ComponentModel::ModelStateFlag state); Q_SIGNALS: void checkStateChanged(const QModelIndex &index); void checkStateChanged(QInstaller::ComponentModel::ModelState state); private Q_SLOTS: void slotModelReset(); void onVirtualStateChanged(); private: void updateAndEmitModelState(); void collectComponents(Component *const component, const QModelIndex &parent) const; QSet updateCheckedState(const ComponentSet &components, Qt::CheckState state); private: PackageManagerCore *m_core; ModelState m_modelState; ComponentSet m_uncheckable; QVector m_headerData; ComponentList m_rootComponentList; QHash m_initialCheckedState; QHash m_currentCheckedState; mutable QHash m_indexByNameCache; }; Q_DECLARE_OPERATORS_FOR_FLAGS(ComponentModel::ModelState); } // namespace QInstaller Q_DECLARE_METATYPE(QInstaller::ComponentModel::ModelState); Q_DECLARE_METATYPE(QInstaller::ComponentModel::ModelStateFlag); #endif // COMPONENTMODEL_H src/libs/installer/constants.h000066400000000000000000000111531325366651500167750ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CONSTANTS_H #define CONSTANTS_H #include namespace QInstaller { // constants used throughout several classes static const QLatin1String scTrue("true"); static const QLatin1String scFalse("false"); static const QLatin1String scScript("script"); static const QLatin1String scName("Name"); static const QLatin1String scVersion("Version"); static const QLatin1String scDefault("Default"); static const QLatin1String scDisplayVersion("DisplayVersion"); static const QLatin1String scRemoteDisplayVersion("RemoteDisplayVersion"); static const QLatin1String scInheritVersion("inheritVersionFrom"); static const QLatin1String scReplaces("Replaces"); static const QLatin1String scDownloadableArchives("DownloadableArchives"); static const QLatin1String scEssential("Essential"); static const QLatin1String scTargetDir("TargetDir"); static const QLatin1String scReleaseDate("ReleaseDate"); static const QLatin1String scDescription("Description"); static const QLatin1String scDisplayName("DisplayName"); static const QLatin1String scDependencies("Dependencies"); static const QLatin1String scAutoDependOn("AutoDependOn"); static const QLatin1String scNewComponent("NewComponent"); static const QLatin1String scRepositories("Repositories"); static const QLatin1String scCompressedSize("CompressedSize"); static const QLatin1String scInstalledVersion("InstalledVersion"); static const QLatin1String scUncompressedSize("UncompressedSize"); static const QLatin1String scUncompressedSizeSum("UncompressedSizeSum"); static const QLatin1String scRequiresAdminRights("RequiresAdminRights"); // constants used throughout the components class static const QLatin1String scVirtual("Virtual"); static const QLatin1String scSortingPriority("SortingPriority"); static const QLatin1String scCheckable("Checkable"); // constants used throughout the settings and package manager core class static const QLatin1String scTitle("Title"); static const QLatin1String scPublisher("Publisher"); static const QLatin1String scRunProgram("RunProgram"); static const QLatin1String scRunProgramArguments("RunProgramArguments"); static const QLatin1String scStartMenuDir("StartMenuDir"); static const QLatin1String scRemoveTargetDir("RemoveTargetDir"); static const QLatin1String scRunProgramDescription("RunProgramDescription"); static const QLatin1String scTargetConfigurationFile("TargetConfigurationFile"); static const QLatin1String scAllowNonAsciiCharacters("AllowNonAsciiCharacters"); static const QLatin1String scDisableAuthorizationFallback("DisableAuthorizationFallback"); static const QLatin1String scRepositorySettingsPageVisible("RepositorySettingsPageVisible"); static const QLatin1String scAllowSpaceInPath("AllowSpaceInPath"); static const QLatin1String scWizardStyle("WizardStyle"); static const QLatin1String scStyleSheet("StyleSheet"); static const QLatin1String scTitleColor("TitleColor"); static const QLatin1String scWizardDefaultWidth("WizardDefaultWidth"); static const QLatin1String scWizardDefaultHeight("WizardDefaultHeight"); static const QLatin1String scUrlQueryString("UrlQueryString"); static const QLatin1String scProductUUID("ProductUUID"); static const QLatin1String scAllUsers("AllUsers"); static const QLatin1String scSupportsModify("SupportsModify"); const char scRelocatable[] = "@RELOCATABLE_PATH@"; } #endif // CONSTANTS_H src/libs/installer/consumeoutputoperation.cpp000066400000000000000000000121231325366651500221650ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "consumeoutputoperation.h" #include "packagemanagercore.h" #include "utils.h" #include #include #include #include using namespace QInstaller; ConsumeOutputOperation::ConsumeOutputOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("ConsumeOutput")); } void ConsumeOutputOperation::backup() { } bool ConsumeOutputOperation::performOperation() { // Arguments: // 1. key where the output will be saved // 2. executable path // 3. argument for the executable // 4. more arguments possible ... if (!checkArgumentCount(2, INT_MAX, tr(" " " [argument1] [argument2] [...]"))) return false; PackageManagerCore *const core = packageManager(); if (!core) { setError(UserDefinedError); setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name())); return false; } const QString installerKeyName = arguments().at(0); if (installerKeyName.isEmpty()) { setError(UserDefinedError); setErrorString(tr("Cannot save the output of \"%1\" to an empty installer key value.").arg( QDir::toNativeSeparators(arguments().at(1)))); return false; } QString executablePath = arguments().at(1); QFileInfo executable(executablePath); #ifdef Q_OS_WIN if (!executable.exists() && executable.suffix().isEmpty()) executable = QFileInfo(executablePath + QLatin1String(".exe")); #endif if (!executable.exists() || !executable.isExecutable()) { setError(UserDefinedError); setErrorString(tr("File \"%1\" does not exist or is not an executable binary.").arg( QDir::toNativeSeparators(executable.absoluteFilePath()))); return false; } QByteArray executableOutput; const QStringList processArguments = arguments().mid(2); // in some cases it is not runable, because another process is blocking it(filewatcher ...) int waitCount = 0; while (executableOutput.isEmpty() && waitCount < 3) { QProcess process; process.start(executable.absoluteFilePath(), processArguments, QIODevice::ReadOnly); if (process.waitForFinished(10000)) { if (process.exitStatus() == QProcess::CrashExit) { qWarning() << executable.absoluteFilePath() << processArguments << "crashed with exit code" << process.exitCode() << "standard output: " << process.readAllStandardOutput() << "error output: " << process.readAllStandardError(); setError(UserDefinedError); setErrorString(tr("Running \"%1\" resulted in a crash.").arg( QDir::toNativeSeparators(executable.absoluteFilePath()))); return false; } executableOutput.append(process.readAllStandardOutput()); } if (executableOutput.isEmpty()) { ++waitCount; static const int waitTimeInMilliSeconds = 500; uiDetachedWait(waitTimeInMilliSeconds); } if (process.state() > QProcess::NotRunning ) { qWarning() << executable.absoluteFilePath() << "process is still running, need to kill it."; process.kill(); } } if (executableOutput.isEmpty()) { qWarning() << "Cannot get any query output from executable" << executable.absoluteFilePath(); } core->setValue(installerKeyName, QString::fromLocal8Bit(executableOutput)); return true; } bool ConsumeOutputOperation::undoOperation() { return true; } bool ConsumeOutputOperation::testOperation() { return true; } src/libs/installer/consumeoutputoperation.h000066400000000000000000000034261325366651500216400ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CONSUMEOUTPUTOPERATION_H #define CONSUMEOUTPUTOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT ConsumeOutputOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::ConsumeOutputOperation) public: explicit ConsumeOutputOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); private: }; } // namespace QInstaller #endif // CONSUMEOUTPUTOPERATION_H src/libs/installer/copydirectoryoperation.cpp000066400000000000000000000151361325366651500221410ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "copydirectoryoperation.h" #include #include #include using namespace QInstaller; class AutoPush { public: AutoPush(CopyDirectoryOperation *op) : m_op(op) {} ~AutoPush() { m_op->setValue(QLatin1String("files"), m_files); } QStringList m_files; CopyDirectoryOperation *m_op; }; CopyDirectoryOperation::CopyDirectoryOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("CopyDirectory")); } void CopyDirectoryOperation::backup() { } bool CopyDirectoryOperation::performOperation() { if (!checkArgumentCount(2, 3, tr(" [\"forceOverwrite\"]"))) return false; const QStringList args = arguments(); const QString sourcePath = args.at(0); const QString targetPath = args.at(1); bool overwrite = false; if (args.count() > 2) { const QString overwriteStr = args.at(2); if (overwriteStr == QLatin1String("forceOverwrite")) { overwrite = true; } else { setError(InvalidArguments); setErrorString(tr("Invalid argument in %1: Third argument needs to be forceOverwrite, " "if specified.").arg(name())); return false; } } const QFileInfo sourceInfo(sourcePath); const QFileInfo targetInfo(targetPath); foreach (const QFileInfo &dir, QList() << sourceInfo << targetInfo) { if (!dir.exists() || !dir.isDir()) { setError(InvalidArguments); setErrorString(tr("Invalid argument in %1: Directory \"%2\" is invalid.").arg(name()) .arg(QDir::toNativeSeparators(dir.absolutePath()))); return false; } } const QDir sourceDir = sourceInfo.absoluteDir(); const QDir targetDir = targetInfo.absoluteDir(); AutoPush autoPush(this); QDirIterator it(sourceInfo.absoluteFilePath(), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden, QDirIterator::Subdirectories); while (it.hasNext()) { const QString itemName = it.next(); const QFileInfo itemInfo(sourceDir.absoluteFilePath(itemName)); const QString relativePath = sourceDir.relativeFilePath(itemName); if (itemInfo.isSymLink()) { // Check if symlink target is inside copied directory const QString linkTarget = itemInfo.symLinkTarget(); if (linkTarget.startsWith(sourceDir.absolutePath())) { // create symlink to copied location const QString linkTargetRelative = sourceDir.relativeFilePath(linkTarget); QFile(targetDir.absoluteFilePath(linkTargetRelative)) .link(targetDir.absoluteFilePath(relativePath)); } else { // create symlink pointing to original location QFile(linkTarget).link(targetDir.absoluteFilePath(relativePath)); } // add file entry autoPush.m_files.prepend(targetDir.absoluteFilePath(relativePath)); emit outputTextChanged(autoPush.m_files.first()); } else if (itemInfo.isDir()) { if (!targetDir.mkpath(targetDir.absoluteFilePath(relativePath))) { setError(InvalidArguments); setErrorString(tr("Cannot create directory \"%1\".").arg( QDir::toNativeSeparators(targetDir.absoluteFilePath(relativePath)))); return false; } } else { const QString absolutePath = targetDir.absoluteFilePath(relativePath); if (overwrite && QFile::exists(absolutePath) && !deleteFileNowOrLater(absolutePath)) { setError(UserDefinedError); setErrorString(tr("Failed to overwrite \"%1\".").arg(QDir::toNativeSeparators(absolutePath))); return false; } QFile file(sourceDir.absoluteFilePath(itemName)); if (!file.copy(absolutePath)) { setError(UserDefinedError); setErrorString(tr("Cannot copy file \"%1\" to \"%2\": %3").arg( QDir::toNativeSeparators(sourceDir.absoluteFilePath(itemName)), QDir::toNativeSeparators(targetDir.absoluteFilePath(relativePath)), file.errorString())); return false; } autoPush.m_files.prepend(targetDir.absoluteFilePath(relativePath)); emit outputTextChanged(autoPush.m_files.first()); } } return true; } bool CopyDirectoryOperation::undoOperation() { Q_ASSERT(arguments().count() == 2); QDir dir; const QStringList files = value(QLatin1String("files")).toStringList(); foreach (const QString &file, files) { if (!QFile::remove(file)) { setError(InvalidArguments); setErrorString(tr("Cannot remove file \"%1\".").arg(QDir::toNativeSeparators(file))); return false; } dir.rmdir(QFileInfo(file).absolutePath()); emit outputTextChanged(file); } setValue(QLatin1String("files"), QStringList()); return true; } bool CopyDirectoryOperation::testOperation() { return true; } src/libs/installer/copydirectoryoperation.h000066400000000000000000000034271325366651500216060ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef COPYDIRECTORYOPERATION_H #define COPYDIRECTORYOPERATION_H #include "qinstallerglobal.h" #include namespace QInstaller { class INSTALLER_EXPORT CopyDirectoryOperation : public QObject, public Operation { Q_OBJECT public: explicit CopyDirectoryOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); Q_SIGNALS: void outputTextChanged(const QString &progress); }; } #endif src/libs/installer/copyfiletask.cpp000066400000000000000000000102271325366651500200120ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "copyfiletask.h" #include "observer.h" #include #include #include namespace QInstaller { CopyFileTask::CopyFileTask(const FileTaskItem &item) : AbstractFileTask(item) { } CopyFileTask::CopyFileTask(const QString &source) : AbstractFileTask(source) { } CopyFileTask::CopyFileTask(const QString &source, const QString &target) : AbstractFileTask(source, target) { } void CopyFileTask::doTask(QFutureInterface &fi) { fi.reportStarted(); fi.setExpectedResultCount(1); if (taskItems().isEmpty()) { fi.reportException(TaskException(tr("Invalid task item count."))); fi.reportFinished(); return; // error } const FileTaskItem item = taskItems().first(); FileTaskObserver observer(QCryptographicHash::Sha1); QFile source(item.source()); if (!source.open(QIODevice::ReadOnly)) { fi.reportException(TaskException(tr("Cannot open file \"%1\" for reading: %2") .arg(QDir::toNativeSeparators(source.fileName()), source.errorString()))); fi.reportFinished(); return; // error } observer.setBytesToTransfer(source.size()); QScopedPointer file; const QString target = item.target(); if (target.isEmpty()) { QTemporaryFile *tmp = new QTemporaryFile; tmp->setAutoRemove(false); file.reset(tmp); } else { file.reset(new QFile(target)); } if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) { fi.reportException(TaskException(tr("Cannot open file \"%1\" for writing: %2") .arg(QDir::toNativeSeparators(file->fileName()), file->errorString()))); fi.reportFinished(); return; // error } QByteArray buffer(32768, Qt::Uninitialized); while (!source.atEnd() && source.error() == QFile::NoError) { if (fi.isCanceled()) break; if (fi.isPaused()) fi.waitForResume(); const qint64 read = source.read(buffer.data(), buffer.size()); qint64 written = 0; while (written < read) { const qint64 toWrite = file->write(buffer.constData() + written, read - written); if (toWrite < 0) { fi.reportException(TaskException(tr("Writing to file \"%1\" failed: %2") .arg(QDir::toNativeSeparators(file->fileName()), file->errorString()))); } written += toWrite; } observer.addSample(read); observer.timerEvent(NULL); observer.addBytesTransfered(read); observer.addCheckSumData(buffer.data(), read); fi.setProgressValueAndText(observer.progressValue(), observer.progressText()); } fi.reportResult(FileTaskResult(file->fileName(), observer.checkSum(), item), 0); fi.reportFinished(); } } // namespace QInstaller src/libs/installer/copyfiletask.h000066400000000000000000000034601325366651500174600ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef COPYFILETASK_H #define COPYFILETASK_H #include "abstractfiletask.h" namespace QInstaller { class INSTALLER_EXPORT CopyFileTask : public AbstractFileTask { Q_OBJECT Q_DISABLE_COPY(CopyFileTask) public: CopyFileTask() {} explicit CopyFileTask(const FileTaskItem &item); explicit CopyFileTask(const QString &source); CopyFileTask(const QString &source, const QString &target); void doTask(QFutureInterface &fi); }; } // namespace QInstaller #endif // COPYFILETASK_H src/libs/installer/createdesktopentryoperation.cpp000066400000000000000000000150451325366651500231600ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "createdesktopentryoperation.h" #include "errors.h" #include "fileutils.h" #include #include #include #include #include using namespace QInstaller; QString CreateDesktopEntryOperation::absoluteFileName() { const QString filename = arguments().first(); // give filename is already absolute if (QFileInfo(filename).isAbsolute()) return filename; // we're not searching for the first time, let's re-use the old value if (hasValue(QLatin1String("directory"))) return QDir(value(QLatin1String("directory")).toString()).absoluteFilePath(filename); const QProcessEnvironment env; QStringList XDG_DATA_DIRS = env.value(QLatin1String("XDG_DATA_DIRS")).split(QLatin1Char(':'), QString::SkipEmptyParts); QStringList XDG_DATA_HOME = env.value(QLatin1String("XDG_DATA_HOME")).split(QLatin1Char(':'), QString::SkipEmptyParts); XDG_DATA_DIRS.push_back(QLatin1String("/usr/share")); // default path XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default path const QStringList directories = XDG_DATA_DIRS + XDG_DATA_HOME; QString directory; for (QStringList::const_iterator it = directories.begin(); it != directories.end(); ++it) { if (it->isEmpty()) continue; directory = QDir(*it).absoluteFilePath(QLatin1String("applications")); QDir dir(directory); // let's see whether this dir exists or we're able to create it if (!dir.exists() && !QDir().mkpath(directory)) continue; // we just try whether we're able to open the file in ReadWrite QFile file(QDir(directory).absoluteFilePath(filename)); const bool existed = file.exists(); if (!file.open(QIODevice::ReadWrite)) continue; file.close(); if (!existed) file.remove(); break; } if (!QDir(directory).exists()) QDir().mkpath(directory); setValue(QLatin1String("directory"), directory); return QDir(directory).absoluteFilePath(filename); } CreateDesktopEntryOperation::CreateDesktopEntryOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("CreateDesktopEntry")); } CreateDesktopEntryOperation::~CreateDesktopEntryOperation() { } void CreateDesktopEntryOperation::backup() { const QString filename = absoluteFileName(); QFile file(filename); if (!file.exists()) return; try { setValue(QLatin1String("backupOfExistingDesktopEntry"), generateTemporaryFileName(filename)); } catch (const QInstaller::Error &e) { setErrorString(e.message()); return; } if (!file.copy(value(QLatin1String("backupOfExistingDesktopEntry")).toString())) setErrorString(tr("Cannot backup file \"%1\": %2").arg(QDir::toNativeSeparators(filename), file.errorString())); } bool CreateDesktopEntryOperation::performOperation() { if (!checkArgumentCount(2)) return false; const QString filename = absoluteFileName(); const QString &values = arguments().at(1); QFile file(filename); if (file.exists() && !file.remove()) { setError(UserDefinedError); setErrorString(tr("Failed to overwrite file \"%1\".").arg(QDir::toNativeSeparators(filename))); return false; } if(!file.open(QIODevice::WriteOnly)) { setError(UserDefinedError); setErrorString(tr("Cannot write desktop entry to \"%1\".").arg(QDir::toNativeSeparators(filename))); return false; } QFile::setPermissions(filename, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther | QFile::ExeOwner | QFile::ExeGroup | QFile::ExeOther); QTextStream stream(&file); stream.setCodec("UTF-8"); stream << QLatin1String("[Desktop Entry]") << endl; // Type=Application\nExec=qtcreator\nPath=... const QStringList pairs = values.split(QLatin1Char('\n')); for (QStringList::const_iterator it = pairs.begin(); it != pairs.end(); ++it) stream << *it << endl; return true; } bool CreateDesktopEntryOperation::undoOperation() { const QString filename = absoluteFileName(); // first remove the link QFile file(filename); if (file.exists() && !file.remove()) { qWarning() << "Cannot delete file" << filename << ":" << file.errorString(); return true; } if (!hasValue(QLatin1String("backupOfExistingDesktopEntry"))) return true; QFile backupFile(value(QLatin1String("backupOfExistingDesktopEntry")).toString()); if (!backupFile.exists()) { // do not treat this as a real error: The backup file might have been just nuked by the user. qWarning() << "Cannot restore original desktop entry at" << filename << ": Backup file" << backupFile.fileName() << "does not exist anymore."; return true; } if (!backupFile.rename(filename)) qWarning() << "Cannot restore the file" << filename << ":" << backupFile.errorString(); return true; } bool CreateDesktopEntryOperation::testOperation() { return true; } src/libs/installer/createdesktopentryoperation.h000066400000000000000000000034671325366651500226320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CREATEDESKTOPENTRYOPERATION_H #define CREATEDESKTOPENTRYOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT CreateDesktopEntryOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::CreateDesktopEntryOperation) public: explicit CreateDesktopEntryOperation(PackageManagerCore *core); ~CreateDesktopEntryOperation(); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); QString absoluteFileName(); }; } #endif src/libs/installer/createlinkoperation.cpp000066400000000000000000000054521325366651500213630ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "createlinkoperation.h" #include "link.h" #include #include using namespace QInstaller; CreateLinkOperation::CreateLinkOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("CreateLink")); } void CreateLinkOperation::backup() { } bool CreateLinkOperation::performOperation() { if (!checkArgumentCount(2)) return false; const QStringList args = arguments(); const QString& linkPath = args.at(0); const QString& targetPath = args.at(1); Link link = Link::create(linkPath, targetPath); if (!link.exists()) { setError(UserDefinedError); setErrorString(tr("Cannot create link from \"%1\" to \"%2\".").arg( QDir::toNativeSeparators(linkPath), QDir::toNativeSeparators(targetPath))); return false; } return true; } bool CreateLinkOperation::undoOperation() { QStringList args = arguments(); const QString& linkPath = args.at(0); const QString& targetPath = args.at(1); Link link = Link(linkPath); if (!link.exists()) { return true; } if (!link.remove()) { setError(UserDefinedError); setErrorString(tr("Cannot remove link from \"%1\" to \"%2\".").arg( QDir::toNativeSeparators(linkPath), QDir::toNativeSeparators(targetPath))); return false; } return !QFileInfo(linkPath).exists(); } bool CreateLinkOperation::testOperation() { return true; } src/libs/installer/createlinkoperation.h000066400000000000000000000033071325366651500210250ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CREATELINKOPERATION_H #define CREATELINKOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT CreateLinkOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::CreateLinkOperation) public: explicit CreateLinkOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } #endif src/libs/installer/createlocalrepositoryoperation.cpp000066400000000000000000000346321325366651500236620ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "createlocalrepositoryoperation.h" #include "binarycontent.h" #include "binaryformat.h" #include "errors.h" #include "fileio.h" #include "fileutils.h" #include "copydirectoryoperation.h" #include "lib7z_create.h" #include "lib7z_facade.h" #include "packagemanagercore.h" #include "productkeycheck.h" #include "updateoperations.h" #include #include #include namespace QInstaller { // -- AutoHelper struct AutoHelper { public: AutoHelper(CreateLocalRepositoryOperation *op) : m_op(op) { } ~AutoHelper() { m_op->emitFullProgress(); m_op->setValue(QLatin1String("files"), m_files); } QStringList m_files; CreateLocalRepositoryOperation *m_op; }; // statics namespace Static { static void fixPermissions(const QString &repoPath) { QDirIterator it(repoPath, QDirIterator::Subdirectories); while (it.hasNext() && !it.next().isEmpty()) { if (!it.fileInfo().isFile()) continue; if (!QFile::setPermissions(it.filePath(), QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther)) { throw Error(CreateLocalRepositoryOperation::tr("Cannot set permissions for file \"%1\".") .arg(QDir::toNativeSeparators(it.filePath()))); } } } static void removeDirectory(const QString &path, AutoHelper *const helper) { QInstaller::removeDirectory(path); QStringList files = helper->m_files.filter(path); foreach (const QString &file, files) helper->m_files.removeAll(file); } static void removeFiles(const QString &path, AutoHelper *const helper) { const QFileInfoList entries = QDir(path).entryInfoList(QDir::AllEntries | QDir::Hidden); foreach (const QFileInfo &fi, entries) { if (fi.isSymLink() || fi.isFile()) { QFile f(fi.filePath()); if (!f.remove()) { throw Error(CreateLocalRepositoryOperation::tr("Cannot remove file \"%1\": %2") .arg(QDir::toNativeSeparators(f.fileName()), f.errorString())); } helper->m_files.removeAll(f.fileName()); } } } static QString createArchive(const QString repoPath, const QString &sourceDir, const QString &version , AutoHelper *const helper) { const QString fileName = QString::fromLatin1("/%1meta.7z").arg(version); QFile archive(repoPath + fileName); QInstaller::openForWrite(&archive); Lib7z::createArchive(&archive, QStringList() << sourceDir); removeFiles(sourceDir, helper); // cleanup the files we compressed if (!archive.rename(sourceDir + fileName)) { throw Error(CreateLocalRepositoryOperation::tr("Cannot move file \"%1\" to \"%2\": %3") .arg(QDir::toNativeSeparators(archive.fileName()), QDir::toNativeSeparators(sourceDir + fileName), archive.errorString())); } return archive.fileName(); } } // namespace Statics // -- CreateLocalRepositoryOperation CreateLocalRepositoryOperation::CreateLocalRepositoryOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("CreateLocalRepository")); } void CreateLocalRepositoryOperation::backup() { } bool CreateLocalRepositoryOperation::performOperation() { AutoHelper helper(this); emit progressChanged(0.0); if (!checkArgumentCount(2)) return false; try { const QStringList args = arguments(); const QString binaryPath = QFileInfo(args.at(0)).absoluteFilePath(); // Note the "/" at the end, important to make copy directory operation behave well const QString repoPath = QFileInfo(args.at(1)).absoluteFilePath() + QLatin1Char('/'); // check if this is an offline version, otherwise there will be no binary data PackageManagerCore *const core = packageManager(); if (core && !core->isOfflineOnly()) { throw QInstaller::Error(tr("Installer at \"%1\" needs to be an offline one.") .arg(QDir::toNativeSeparators(binaryPath))); } // if we're running as installer and install into an existing target, remove possible previous repos if (QFile::exists(repoPath)) { Static::fixPermissions(repoPath); QInstaller::removeDirectory(repoPath); } // create the local repository target dir KDUpdater::MkdirOperation mkDirOp; mkDirOp.setArguments(QStringList() << repoPath); mkDirOp.backup(); if (!mkDirOp.performOperation()) { setError(mkDirOp.error()); setErrorString(mkDirOp.errorString()); return false; } setValue(QLatin1String("createddir"), mkDirOp.value(QLatin1String("createddir"))); // copy the whole meta data into local repository CopyDirectoryOperation copyDirOp(core); copyDirOp.setArguments(QStringList() << QLatin1String(":/metadata/") << repoPath); connect(©DirOp, &CopyDirectoryOperation::outputTextChanged, this, &CreateLocalRepositoryOperation::outputTextChanged); const bool success = copyDirOp.performOperation(); helper.m_files = copyDirOp.value(QLatin1String("files")).toStringList(); if (!success) { setError(copyDirOp.error()); setErrorString(copyDirOp.errorString()); return false; } emit progressChanged(0.25); // we need to fix the folder and file permissions here, as copying from read only resource file // systems sets all permissions to a completely bogus value... Static::fixPermissions(repoPath); // open the updates xml file we previously copied QFile updatesXml(repoPath + QLatin1String("Updates.xml")); if (!updatesXml.exists() || !updatesXml.open(QIODevice::ReadOnly)) throw QInstaller::Error(tr("Cannot open file \"%1\" for reading.").arg( QDir::toNativeSeparators(updatesXml.fileName()))); // read the content of the updates xml QString error; QDomDocument doc; if (!doc.setContent(&updatesXml, &error)) throw QInstaller::Error(tr("Cannot read file \"%1\": %2").arg( QDir::toNativeSeparators(updatesXml.fileName()), error)); // build for each available package a name - version mapping QHash nameVersionHash; const QDomElement root = doc.documentElement(); const QDomNodeList rootChildNodes = root.childNodes(); for (int i = 0; i < rootChildNodes.count(); ++i) { const QDomElement element = rootChildNodes.at(i).toElement(); if (element.isNull()) continue; QString name, version; if (element.tagName() == QLatin1String("PackageUpdate")) { const QDomNodeList elementChildNodes = element.childNodes(); for (int j = 0; j < elementChildNodes.count(); ++j) { const QDomElement e = elementChildNodes.at(j).toElement(); if (e.tagName() == QLatin1String("Name")) name = e.text(); else if (e.tagName() == QLatin1String("Version")) version = e.text(); } if (ProductKeyCheck::instance()->isValidPackage(name)) nameVersionHash.insert(name, version); } } emit progressChanged(0.50); QFile file(binaryPath); if (!file.open(QIODevice::ReadOnly)) { throw QInstaller::Error(tr("Cannot open file \"%1\" for reading: %2").arg( QDir::toNativeSeparators(file.fileName()), file.errorString())); } // start to read the binary layout ResourceCollectionManager manager; BinaryContent::readBinaryContent(&file, 0, &manager, 0, BinaryContent::MagicCookie); emit progressChanged(0.65); QDir repo(repoPath); if (!nameVersionHash.isEmpty()) { // extract meta and binary data const QStringList names = nameVersionHash.keys(); for (int i = 0; i < names.count(); ++i) { const QString name = names.at(i); if (!repo.mkpath(name)) { throw QInstaller::Error(tr("Cannot create target directory: \"%1\".") .arg(QDir::toNativeSeparators(repo.filePath(name)))); } // zip the meta files that come with the offline installer helper.m_files.prepend(Static::createArchive(repoPath, repo.filePath(name), nameVersionHash.value(name), &helper)); emit outputTextChanged(helper.m_files.first()); // copy the 7z files that are inside the component index into the target const ResourceCollection collection = manager.collectionByName(name.toUtf8()); foreach (const QSharedPointer &resource, collection.resources()) { const bool isOpen = resource->isOpen(); if ((!isOpen) && (!resource->open())) continue; QFile target(repo.filePath(name) + QDir::separator() + QString::fromUtf8(resource->name())); QInstaller::openForWrite(&target); resource->copyData(&target); helper.m_files.prepend(target.fileName()); emit outputTextChanged(helper.m_files.first()); if (!isOpen) // If we reach that point, either the resource was opened already. resource->close(); // or we did open it and have to close it again. } emit progressChanged(.65f + ((double(i) / double(names.count())) * .25f)); } } emit progressChanged(0.95); try { // remove these, if we fail it doesn't hurt Static::removeDirectory(QDir::cleanPath(repoPath + QLatin1String("/installer-config")), &helper); Static::removeDirectory(QDir::cleanPath(repoPath + QLatin1String("/config")), &helper); const QStringList files = repo.entryList(QStringList() << QLatin1String("*.qrc"), QDir::Files); foreach (const QString &file, files) { if (repo.remove(file)) helper.m_files.removeAll(QDir::cleanPath(repoPath + file)); } } catch (...) {} setValue(QLatin1String("local-repo"), repoPath); } catch (const Lib7z::SevenZipException &e) { setError(UserDefinedError); setErrorString(e.message()); return false; } catch (const QInstaller::Error &e) { setError(UserDefinedError); setErrorString(e.message()); return false; } catch (...) { setError(UserDefinedError); setErrorString(tr("Unknown exception caught: %1.").arg(QLatin1String(Q_FUNC_INFO))); return false; } return true; } bool CreateLocalRepositoryOperation::undoOperation() { Q_ASSERT(arguments().count() == 2); AutoHelper _(this); emit progressChanged(0.0); QDir dir; const QStringList files = value(QLatin1String("files")).toStringList(); foreach (const QString &file, files) { emit outputTextChanged(tr("Removing file \"%1\".").arg(QDir::toNativeSeparators(file))); if (!QFile::remove(file)) { setError(InvalidArguments); setErrorString(tr("Cannot remove file \"%1\".").arg(QDir::toNativeSeparators(file))); return false; } dir.rmpath(QFileInfo(file).absolutePath()); } setValue(QLatin1String("files"), QStringList()); QDir createdDir = QDir(value(QLatin1String("createddir")).toString()); if (createdDir == QDir::root() || !createdDir.exists()) return true; QInstaller::removeSystemGeneratedFiles(createdDir.path()); errno = 0; const bool result = QDir::root().rmdir(createdDir.path()); if (!result) { #if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) char msg[128]; if (strerror_s(msg, sizeof msg, errno) != 0) { setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( QDir::toNativeSeparators(createdDir.path()), QString::fromLocal8Bit(msg))); } #else setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( QDir::toNativeSeparators(createdDir.path()), QString::fromLocal8Bit(strerror(errno)))); #endif } setValue(QLatin1String("files"), QStringList()); return result; } bool CreateLocalRepositoryOperation::testOperation() { return true; } void CreateLocalRepositoryOperation::emitFullProgress() { emit progressChanged(1.0); } } // namespace QInstaller src/libs/installer/createlocalrepositoryoperation.h000066400000000000000000000037451325366651500233300ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CREATELOCALREPOSITORYOPERATION_H #define CREATELOCALREPOSITORYOPERATION_H #include "qinstallerglobal.h" #include namespace QInstaller { class INSTALLER_EXPORT CreateLocalRepositoryOperation : public QObject, public Operation { Q_OBJECT friend struct AutoHelper; public: explicit CreateLocalRepositoryOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); signals: void progressChanged(double progress); void outputTextChanged(const QString &message); private: void emitFullProgress(); }; } // namespace QInstaller #endif // CREATELOCALREPOSITORYOPERATION_H src/libs/installer/createshortcutoperation.cpp000066400000000000000000000253311325366651500222770ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "createshortcutoperation.h" #include "fileutils.h" #include "utils.h" #include #include #include #include using namespace QInstaller; #ifdef Q_OS_WIN #include #include #include #ifndef PIDLIST_ABSOLUTE typedef ITEMIDLIST *PIDLIST_ABSOLUTE; #endif struct DeCoInitializer { DeCoInitializer() : neededCoInit(CoInitialize(NULL) == S_OK) { } ~DeCoInitializer() { if (neededCoInit) CoUninitialize(); } bool neededCoInit; }; #endif struct StartsWith { StartsWith(const QString &searchTerm) : m_searchTerm(searchTerm) {} bool operator()(const QString &searchString) { return searchString.startsWith(m_searchTerm); } QString m_searchTerm; }; static QString parentDirectory(const QString ¤t) { return current.mid(0, current.lastIndexOf(QLatin1Char('/'))); } static QString takeArgument(const QString argument, QStringList *arguments) { // if the arguments contain an option in the form "argument=...", find it and consume it QStringList::iterator it = std::find_if(arguments->begin(), arguments->end(), StartsWith(argument)); if (it == arguments->end()) return QString(); const QString value = it->mid(argument.size()); arguments->erase(it); return value; } static bool createLink(const QString &fileName, const QString &linkName, QString workingDir, const QString &arguments = QString(), const QString &iconPath = QString(), const QString &iconId = QString(), const QString &description = QString()) { #ifdef Q_OS_WIN // CoInitialize cleanup object DeCoInitializer _; IUnknown *iunkn = NULL; if (fileName.toLower().startsWith(QLatin1String("http:")) || fileName.toLower().startsWith(QLatin1String("ftp:"))) { IUniformResourceLocator *iurl = NULL; if (FAILED(CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IUniformResourceLocator, (LPVOID*)&iurl))) { return false; } if (FAILED(iurl->SetURL((wchar_t *)fileName.utf16(), IURL_SETURL_FL_GUESS_PROTOCOL))) { iurl->Release(); return false; } iunkn = iurl; } else { bool success = QFile::link(fileName, linkName); if (!success) { return success; } if (workingDir.isEmpty()) workingDir = QFileInfo(fileName).absolutePath(); workingDir = QDir::toNativeSeparators(workingDir); IShellLink *psl = NULL; if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl))) { return success; } // TODO: implement this server side, since there's not Qt equivalent to set working dir and arguments psl->SetPath((wchar_t *)QDir::toNativeSeparators(fileName).utf16()); psl->SetWorkingDirectory((wchar_t *)workingDir.utf16()); if (!arguments.isNull()) psl->SetArguments((wchar_t*)arguments.utf16()); if (!iconPath.isNull()) psl->SetIconLocation((wchar_t*)(iconPath.utf16()), iconId.toInt()); if (!description.isNull()) psl->SetDescription((wchar_t*)(description.utf16())); iunkn = psl; } IPersistFile *ppf = NULL; if (SUCCEEDED(iunkn->QueryInterface(IID_IPersistFile, (void **)&ppf))) { ppf->Save((wchar_t*)QDir::toNativeSeparators(linkName).utf16(), true); ppf->Release(); } iunkn->Release(); PIDLIST_ABSOLUTE pidl; // Force start menu cache update if (SUCCEEDED(SHGetFolderLocation(0, CSIDL_STARTMENU, 0, 0, &pidl))) { SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidl, 0); CoTaskMemFree(pidl); } if (SUCCEEDED(SHGetFolderLocation(0, CSIDL_COMMON_STARTMENU, 0, 0, &pidl))) { SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidl, 0); CoTaskMemFree(pidl); } return true; #else Q_UNUSED(arguments) Q_UNUSED(workingDir) Q_UNUSED(fileName) Q_UNUSED(linkName) Q_UNUSED(iconPath) Q_UNUSED(iconId) return true; #endif } // -- CreateShortcutOperation CreateShortcutOperation::CreateShortcutOperation(PackageManagerCore *core) : UpdateOperation(core) , m_optionalArgumentsRead(false) { setName(QLatin1String("CreateShortcut")); } void CreateShortcutOperation::backup() { ensureOptionalArgumentsRead(); QDir linkPath(QFileInfo(arguments().at(1)).absolutePath()); QStringList directoriesToCreate; while (!linkPath.exists() && linkPath != QDir::root()) { const QString absolutePath = linkPath.absolutePath(); directoriesToCreate.append(absolutePath); linkPath = parentDirectory(absolutePath); } setValue(QLatin1String("createddirs"), directoriesToCreate); } void CreateShortcutOperation::ensureOptionalArgumentsRead() { if (m_optionalArgumentsRead) return; m_optionalArgumentsRead = true; QStringList args = arguments(); m_iconId = takeArgument(QString::fromLatin1("iconId="), &args); m_iconPath = takeArgument(QString::fromLatin1("iconPath="), &args); m_workingDir = takeArgument(QString::fromLatin1("workingDirectory="), &args); m_description = takeArgument(QString::fromLatin1("description="), &args); setArguments(args); } bool CreateShortcutOperation::performOperation() { ensureOptionalArgumentsRead(); if (!checkArgumentCount(2, 3, tr(" [target arguments] " "[\"workingDirectory=...\"] [\"iconPath=...\"] [\"iconId=...\"] " "[\"description=...\"]"))) { return false; } QStringList args = arguments(); const QString linkTarget = args.at(0); const QString linkLocation = args.at(1); const QString targetArguments = args.value(2); // value() used since it's optional const QString linkPath = QFileInfo(linkLocation).absolutePath().trimmed(); const bool created = QDir(linkPath).exists() || QDir::root().mkpath(linkPath); if (!created) { setError(UserDefinedError); #if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) char msg[128]; if (strerror_s(msg, sizeof msg, errno) != 0) { setErrorString(tr("Cannot create directory \"%1\": %2").arg(QDir::toNativeSeparators(linkPath), QString::fromLocal8Bit(msg))); } #else setErrorString(tr("Cannot create directory \"%1\": %2").arg(QDir::toNativeSeparators(linkPath), QString::fromLocal8Bit(strerror(errno)))); #endif return false; } //remove a possible existing older one QString errorString; if (QFile::exists(linkLocation) && !deleteFileNowOrLater(linkLocation, &errorString)) { setError(UserDefinedError); setErrorString(tr("Failed to overwrite \"%1\": %2").arg(QDir::toNativeSeparators(linkLocation), errorString)); return false; } const bool linked = createLink(linkTarget, linkLocation, m_workingDir, targetArguments, m_iconPath, m_iconId, m_description); if (!linked) { setError(UserDefinedError); setErrorString(tr("Cannot create link \"%1\": %2").arg(QDir::toNativeSeparators(linkLocation), qt_error_string())); return false; } return true; } bool CreateShortcutOperation::undoOperation() { ensureOptionalArgumentsRead(); const QString &linkLocation = arguments().at(1); if (!deleteFileNowOrLater(linkLocation) ) qDebug() << "Cannot delete:" << linkLocation; QDir dir; // remove all directories we created const QStringList directoriesToDelete = value(QLatin1String("createddirs")).toStringList(); foreach (const QString &directory, directoriesToDelete) { QInstaller::removeSystemGeneratedFiles(directory); if (!dir.rmdir(directory)) break; } #ifdef Q_OS_WIN // special case on windows, multiple installations might leave empty folder inside the start menu QSettings user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\" "Explorer\\User Shell Folders"), QSettings::NativeFormat); QSettings system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\" "Explorer\\Shell Folders"), QSettings::NativeFormat); const QString userStartMenu = QDir::fromNativeSeparators(replaceWindowsEnvironmentVariables(user .value(QLatin1String("Programs"), QString()).toString())).toLower(); const QString systemStartMenu = QDir::fromNativeSeparators(system.value(QLatin1String("Common Programs")) .toString()).toLower(); // try to remove every empty folder until we fail QString linkPath = QFileInfo(linkLocation).absolutePath().toLower(); if (linkPath.startsWith(userStartMenu) || linkPath.startsWith(systemStartMenu)) { QInstaller::removeSystemGeneratedFiles(linkPath); while (QDir().rmdir(linkPath)) { linkPath = parentDirectory(linkPath); QInstaller::removeSystemGeneratedFiles(linkPath); } } #endif return true; } bool CreateShortcutOperation::testOperation() { return true; } src/libs/installer/createshortcutoperation.h000066400000000000000000000036341325366651500217460ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CREATESHORTCUTOPERATION_H #define CREATESHORTCUTOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT CreateShortcutOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::CreateShortcutOperation) public: explicit CreateShortcutOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); private: void ensureOptionalArgumentsRead(); bool m_optionalArgumentsRead; QString m_iconId; QString m_iconPath; QString m_workingDir; QString m_description; }; } #endif src/libs/installer/downloadarchivesjob.cpp000066400000000000000000000247741325366651500213600ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "downloadarchivesjob.h" #include "binaryformatenginehandler.h" #include "component.h" #include "messageboxhandler.h" #include "packagemanagercore.h" #include "utils.h" #include "filedownloader.h" #include "filedownloaderfactory.h" #include #include using namespace QInstaller; using namespace KDUpdater; /*! Creates a new DownloadArchivesJob with \a parent. */ DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core) : Job(core) , m_core(core) , m_downloader(0) , m_archivesDownloaded(0) , m_archivesToDownloadCount(0) , m_canceled(false) , m_lastFileProgress(0) , m_progressChangedTimerId(0) { setCapabilities(Cancelable); } /*! Destroys the DownloadArchivesJob. */ DownloadArchivesJob::~DownloadArchivesJob() { if (m_downloader) m_downloader->deleteLater(); } /*! Sets the archives to download. The first value of each pair contains the file name to register the file in the installer's internal file system, the second one the source url. */ void DownloadArchivesJob::setArchivesToDownload(const QList > &archives) { m_archivesToDownload = archives; m_archivesToDownloadCount = archives.count(); } /*! \reimp */ void DownloadArchivesJob::doStart() { m_archivesDownloaded = 0; fetchNextArchiveHash(); } /*! \reimp */ void DownloadArchivesJob::doCancel() { m_canceled = true; if (m_downloader != 0) m_downloader->cancelDownload(); } void DownloadArchivesJob::fetchNextArchiveHash() { if (m_core->testChecksum()) { if (m_canceled) { finishWithError(tr("Canceled")); return; } if (m_archivesToDownload.isEmpty()) { emitFinished(); return; } if (m_downloader) m_downloader->deleteLater(); m_downloader = setupDownloader(QLatin1String(".sha1")); if (!m_downloader) { m_archivesToDownload.removeFirst(); QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection); return; } connect(m_downloader, &FileDownloader::downloadCompleted, this, &DownloadArchivesJob::finishedHashDownload, Qt::QueuedConnection); m_downloader->download(); } else { QMetaObject::invokeMethod(this, "fetchNextArchive", Qt::QueuedConnection); } } void DownloadArchivesJob::finishedHashDownload() { Q_ASSERT(m_downloader != 0); QFile sha1HashFile(m_downloader->downloadedFileName()); if (sha1HashFile.open(QFile::ReadOnly)) { m_currentHash = sha1HashFile.readAll(); fetchNextArchive(); } else { finishWithError(tr("Downloading hash signature failed.")); } } /*! Fetches the next archive and registers it in the installer. */ void DownloadArchivesJob::fetchNextArchive() { if (m_canceled) { finishWithError(tr("Canceled")); return; } if (m_archivesToDownload.isEmpty()) { emitFinished(); return; } if (m_downloader != 0) m_downloader->deleteLater(); m_downloader = setupDownloader(QString(), m_core->value(scUrlQueryString)); if (!m_downloader) { m_archivesToDownload.removeFirst(); QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection); return; } emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount); connect(m_downloader, SIGNAL(downloadProgress(double)), this, SLOT(emitDownloadProgress(double))); connect(m_downloader, &FileDownloader::downloadCompleted, this, &DownloadArchivesJob::registerFile, Qt::QueuedConnection); m_downloader->download(); } /*! Emits the global download progress during a single download in a lazy way (uses a timer to reduce to much processChanged). */ void DownloadArchivesJob::emitDownloadProgress(double progress) { m_lastFileProgress = progress; if (!m_progressChangedTimerId) m_progressChangedTimerId = startTimer(5); } /*! This is used to reduce the progressChanged signals. */ void DownloadArchivesJob::timerEvent(QTimerEvent *event) { if (event->timerId() == m_progressChangedTimerId) { killTimer(m_progressChangedTimerId); m_progressChangedTimerId = 0; emit progressChanged((double(m_archivesDownloaded) + m_lastFileProgress) / m_archivesToDownloadCount); } } /*! Registers the just downloaded file in the installer's file system. */ void DownloadArchivesJob::registerFile() { Q_ASSERT(m_downloader != 0); if (m_canceled) return; if (m_core->testChecksum() && m_currentHash != m_downloader->sha1Sum().toHex()) { //TODO: Maybe we should try to download the file again automatically const QMessageBox::Button res = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while " "downloading failed. This is a temporary error, please retry."), QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel); if (res == QMessageBox::Cancel) { finishWithError(tr("Cannot verify Hash")); return; } } else { ++m_archivesDownloaded; if (m_progressChangedTimerId) { killTimer(m_progressChangedTimerId); m_progressChangedTimerId = 0; emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount); } const QPair pair = m_archivesToDownload.takeFirst(); BinaryFormatEngineHandler::instance()->registerResource(pair.first, m_downloader->downloadedFileName()); } fetchNextArchiveHash(); } void DownloadArchivesJob::downloadCanceled() { emitFinishedWithError(Job::Canceled, m_downloader->errorString()); } void DownloadArchivesJob::downloadFailed(const QString &error) { if (m_canceled) return; const QMessageBox::StandardButton b = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Cannot download archive %1: %2") .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel); if (b == QMessageBox::Retry) QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection); else downloadCanceled(); } void DownloadArchivesJob::finishWithError(const QString &error) { const FileDownloader *const dl = qobject_cast (sender()); const QString msg = tr("Cannot fetch archives: %1\nError while loading %2"); if (dl != 0) emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, dl->url().toString())); else emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, m_downloader->url().toString())); } KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &suffix, const QString &queryString) { KDUpdater::FileDownloader *downloader = 0; const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first); const Component *const component = m_core->componentByName(QFileInfo(fi.path()).fileName()); if (component) { QString fullQueryString; if (!queryString.isEmpty()) fullQueryString = QLatin1String("?") + queryString; const QUrl url(m_archivesToDownload.first().second + suffix + fullQueryString); const QString &scheme = url.scheme(); downloader = FileDownloaderFactory::instance().create(scheme, this); if (downloader) { downloader->setUrl(url); downloader->setAutoRemoveDownloadedFile(false); QAuthenticator auth; auth.setUser(component->value(QLatin1String("username"))); auth.setPassword(component->value(QLatin1String("password"))); downloader->setAuthenticator(auth); connect(downloader, &FileDownloader::downloadCanceled, this, &DownloadArchivesJob::downloadCanceled); connect(downloader, &FileDownloader::downloadAborted, this, &DownloadArchivesJob::downloadFailed, Qt::QueuedConnection); connect(downloader, &FileDownloader::downloadStatus, this, &DownloadArchivesJob::downloadStatusChanged); if (FileDownloaderFactory::isSupportedScheme(scheme)) { downloader->setDownloadedFileName(component->localTempPath() + QLatin1Char('/') + component->name() + QLatin1Char('/') + fi.fileName() + suffix); } emit outputTextChanged(tr("Downloading archive \"%1\" for component %2.") .arg(fi.fileName() + suffix, component->displayName())); } else { emit outputTextChanged(tr("Scheme %1 not supported (URL: %2).").arg(scheme, url.toString())); } } else { emit outputTextChanged(tr("Cannot find component for %1.").arg(QFileInfo(fi.path()).fileName())); } return downloader; } src/libs/installer/downloadarchivesjob.h000066400000000000000000000057421325366651500210170ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef DOWNLOADARCHIVESJOB_H #define DOWNLOADARCHIVESJOB_H #include "job.h" #include QT_BEGIN_NAMESPACE class QTimerEvent; QT_END_NAMESPACE namespace KDUpdater { class FileDownloader; } namespace QInstaller { class MessageBoxHandler; class PackageManagerCore; class DownloadArchivesJob : public Job { Q_OBJECT public: explicit DownloadArchivesJob(PackageManagerCore *core); ~DownloadArchivesJob(); int numberOfDownloads() const { return m_archivesDownloaded; } void setArchivesToDownload(const QList > &archives); Q_SIGNALS: void progressChanged(double progress); void outputTextChanged(const QString &progress); void downloadStatusChanged(const QString &status); protected: void doStart(); void doCancel(); void timerEvent(QTimerEvent *event); protected Q_SLOTS: void registerFile(); void downloadCanceled(); void downloadFailed(const QString &error); void finishWithError(const QString &error); void fetchNextArchive(); void fetchNextArchiveHash(); void finishedHashDownload(); void emitDownloadProgress(double progress); private: KDUpdater::FileDownloader *setupDownloader(const QString &suffix = QString(), const QString &queryString = QString()); private: PackageManagerCore *m_core; KDUpdater::FileDownloader *m_downloader; int m_archivesDownloaded; int m_archivesToDownloadCount; QList > m_archivesToDownload; bool m_canceled; QByteArray m_currentHash; double m_lastFileProgress; int m_progressChangedTimerId; }; } // namespace QInstaller #endif // DOWNLOADARCHIVESJOB_H src/libs/installer/downloadfiletask.cpp000066400000000000000000000403361325366651500206530ustar00rootroot00000000000000 /************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "downloadfiletask.h" #include "downloadfiletask_p.h" #include #include #include #include #include #include #include namespace QInstaller { AuthenticationRequiredException::AuthenticationRequiredException(Type type, const QString &message) : TaskException(message) , m_type(type) { } Downloader::Downloader() : m_finished(0) { connect(&m_timer, &QTimer::timeout, this, &Downloader::onTimeout); connect(&m_nam, &QNetworkAccessManager::finished, this, &Downloader::onFinished); } Downloader::~Downloader() { m_nam.disconnect(); for (const auto &pair : m_downloads) { pair.first->disconnect(); pair.first->abort(); pair.first->deleteLater(); } } void Downloader::download(QFutureInterface &fi, const QList &items, QNetworkProxyFactory *networkProxyFactory) { m_items = items; m_futureInterface = &fi; fi.reportStarted(); fi.setExpectedResultCount(items.count()); m_nam.setProxyFactory(networkProxyFactory); connect(&m_nam, &QNetworkAccessManager::authenticationRequired, this, &Downloader::onAuthenticationRequired); connect(&m_nam, &QNetworkAccessManager::proxyAuthenticationRequired, this, &Downloader::onProxyAuthenticationRequired); QTimer::singleShot(0, this, &Downloader::doDownload); } void Downloader::doDownload() { m_timer.start(1000); // Use a timer to check for canceled downloads. foreach (const FileTaskItem &item, m_items) { if (!startDownload(item)) break; } if (m_items.isEmpty() || m_futureInterface->isCanceled()) { m_futureInterface->reportFinished(); emit finished(); // emit finished, so the event loop can shutdown } } // -- private slots void Downloader::onReadyRead() { if (testCanceled()) { m_futureInterface->reportFinished(); emit finished(); return; // error } QNetworkReply *const reply = qobject_cast(sender()); if (!reply) return; Data &data = *m_downloads[reply]; if (!data.file) { std::unique_ptr file = Q_NULLPTR; const QString target = data.taskItem.target(); if (target.isEmpty()) { std::unique_ptr tmp(new QTemporaryFile); tmp->setAutoRemove(false); file = std::move(tmp); } else { std::unique_ptr tmp(new QFile(target)); file = std::move(tmp); } if (file->exists() && (!QFileInfo(file->fileName()).isFile())) { m_futureInterface->reportException(TaskException(tr("Target file \"%1\" already exists " "but is not a file.").arg(file->fileName()))); return; } if (!file->open(QIODevice::WriteOnly | QIODevice::Truncate)) { //: %2 is a sentence describing the error m_futureInterface->reportException( TaskException(tr("Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(file->fileName()), file->errorString()))); return; } data.file = std::move(file); } if (!data.file->isOpen()) { //: %2 is a sentence describing the error. m_futureInterface->reportException( TaskException(tr("File \"%1\" not open for writing: %2").arg( QDir::toNativeSeparators(data.file->fileName()), data.file->errorString()))); return; } QByteArray buffer(32768, Qt::Uninitialized); while (reply->bytesAvailable()) { if (testCanceled()) { m_futureInterface->reportFinished(); emit finished(); return; // error } const qint64 read = reply->read(buffer.data(), buffer.size()); qint64 written = 0; while (written < read) { const qint64 toWrite = data.file->write(buffer.constData() + written, read - written); if (toWrite < 0) { //: %2 is a sentence describing the error. m_futureInterface->reportException( TaskException(tr("Writing to file \"%1\" failed: %2").arg( QDir::toNativeSeparators(data.file->fileName()), data.file->errorString()))); return; } written += toWrite; } data.observer->addSample(read); data.observer->addBytesTransfered(read); data.observer->addCheckSumData(buffer.data(), read); int progress = m_finished * 100; for (const auto &pair : m_downloads) progress += pair.second->observer->progressValue(); if (!reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) { m_futureInterface->setProgressValueAndText(progress / m_items.count(), data.observer->progressText()); } } } void Downloader::onFinished(QNetworkReply *reply) { Data &data = *m_downloads[reply]; const QString filename = data.file ? data.file->fileName() : QString(); if (!m_futureInterface->isCanceled()) { if (reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) { const QUrl url = reply->url() .resolved(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl()); const QList redirects = m_redirects.values(reply); if (!redirects.contains(url)) { if (data.file) data.file->remove(); FileTaskItem taskItem = data.taskItem; taskItem.insert(TaskRole::SourceFile, url.toString()); QNetworkReply *const redirectReply = startDownload(taskItem); foreach (const QUrl &redirect, redirects) m_redirects.insertMulti(redirectReply, redirect); m_redirects.insertMulti(redirectReply, url); m_downloads.erase(reply); m_redirects.remove(reply); reply->deleteLater(); return; } else { m_futureInterface->reportException(TaskException(tr("Redirect loop detected for \"%1\".") .arg(url.toString()))); return; } } } const QByteArray ba = reply->readAll(); if (!ba.isEmpty()) { data.observer->addSample(ba.size()); data.observer->addBytesTransfered(ba.size()); data.observer->addCheckSumData(ba.data(), ba.size()); } const QByteArray expectedCheckSum = data.taskItem.value(TaskRole::Checksum).toByteArray(); if (!expectedCheckSum.isEmpty()) { if (expectedCheckSum != data.observer->checkSum().toHex()) { m_futureInterface->reportException(TaskException(tr("Checksum mismatch detected for \"%1\".") .arg(reply->url().toString()))); } } m_futureInterface->reportResult(FileTaskResult(filename, data.observer->checkSum(), data.taskItem)); m_downloads.erase(reply); m_redirects.remove(reply); reply->deleteLater(); m_finished++; if (m_downloads.empty() || m_futureInterface->isCanceled()) { m_futureInterface->reportFinished(); emit finished(); // emit finished, so the event loop can shutdown } } void Downloader::onError(QNetworkReply::NetworkError error) { QNetworkReply *const reply = qobject_cast(sender()); if (error == QNetworkReply::ProxyAuthenticationRequiredError) return; // already handled by onProxyAuthenticationRequired if (error == QNetworkReply::AuthenticationRequiredError) return; // already handled by onAuthenticationRequired if (reply) { const Data &data = *m_downloads[reply]; //Do not throw error if Updates.xml not found. The repository might be removed //with RepositoryUpdate in Updates.xml later. //: %2 is a sentence describing the error if (data.taskItem.source().contains(QLatin1String("Updates.xml"), Qt::CaseInsensitive)) { qDebug() << QString::fromLatin1("Network error while downloading '%1': %2.").arg( data.taskItem.source(), reply->errorString()); } else { m_futureInterface->reportException( TaskException(tr("Network error while downloading '%1': %2.").arg( data.taskItem.source(), reply->errorString()))); } } else { //: %1 is a sentence describing the error m_futureInterface->reportException( TaskException(tr("Unknown network error while downloading \"%1\".").arg(error))); } } void Downloader::onSslErrors(const QList &sslErrors) { #ifdef QT_NO_SSL Q_UNUSED(sslErrors); #else foreach (const QSslError &error, sslErrors) qDebug() << "SSL error:" << error.errorString(); #endif } void Downloader::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { Q_UNUSED(bytesReceived) QNetworkReply *const reply = qobject_cast(sender()); if (reply) { const Data &data = *m_downloads[reply]; data.observer->setBytesToTransfer(bytesTotal); } } void Downloader::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) { if (!authenticator || !reply || m_downloads.find(reply) == m_downloads.cend()) return; FileTaskItem *item = &m_downloads[reply]->taskItem; const QAuthenticator auth = item->value(TaskRole::Authenticator).value(); if (auth.user().isEmpty()) { AuthenticationRequiredException e(AuthenticationRequiredException::Type::Server, QCoreApplication::translate("AuthenticationRequiredException", "%1 at %2") .arg(authenticator->realm(), reply->url().host())); item->insert(TaskRole::Authenticator, QVariant::fromValue(QAuthenticator(*authenticator))); e.setFileTaskItem(*item); m_futureInterface->reportException(e); } else { authenticator->setUser(auth.user()); authenticator->setPassword(auth.password()); item->insert(TaskRole::Authenticator, QVariant()); // clear so we fail on next call } } void Downloader::onProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *) { // Report to GUI thread. // (MetadataJob will ask for username/password, and restart the download ...) AuthenticationRequiredException e(AuthenticationRequiredException::Type::Proxy, QCoreApplication::translate("AuthenticationRequiredException", "Proxy requires authentication.")); e.setProxy(proxy); m_futureInterface->reportException(e); } /*! \internal Canceling from the outside will not get noticed if we are waiting on a connection that does not create any events. QNam will drop after 45 seconds, though the user might have canceled the download before. In that case we block until the QNam timeout is reached, worst case resulting in deadlock while the application is shutting down at the same time. */ void Downloader::onTimeout() { if (testCanceled()) { // Inject exception, we can't use QFuturInterface::reportException() as the exception // store is "frozen" once cancel was called. On the other hand, client code could use // QFutureWatcherBase::isCanceled() or QFuture::isCanceled() to check for canceled futures. m_futureInterface->exceptionStore() .setException(TaskException(tr("Network transfers canceled."))); m_futureInterface->reportFinished(); emit finished(); } } // -- private bool Downloader::testCanceled() { // TODO: figure out how to implement pause and resume if (m_futureInterface->isPaused()) { m_futureInterface->togglePaused(); // Note: this will trigger cancel m_futureInterface->reportException( TaskException(tr("Pause and resume not supported by network transfers."))); } return m_futureInterface->isCanceled(); } QNetworkReply *Downloader::startDownload(const FileTaskItem &item) { QUrl const source = item.source(); if (!source.isValid()) { //: %2 is a sentence describing the error m_futureInterface->reportException(TaskException(tr("Invalid source URL \"%1\": %2") .arg(source.toString(), source.errorString()))); return 0; } QNetworkReply *reply = m_nam.get(QNetworkRequest(source)); std::unique_ptr data(new Data(item)); m_downloads[reply] = std::move(data); connect(reply, &QIODevice::readyRead, this, &Downloader::onReadyRead); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError))); #ifndef QT_NO_SSL connect(reply, &QNetworkReply::sslErrors, this, &Downloader::onSslErrors); #endif connect(reply, &QNetworkReply::downloadProgress, this, &Downloader::onDownloadProgress); return reply; } // -- DownloadFileTask DownloadFileTask::DownloadFileTask(const QList &items) : AbstractFileTask() { setTaskItems(items); } void DownloadFileTask::setTaskItem(const FileTaskItem &item) { AbstractFileTask::setTaskItem(item); } void DownloadFileTask::addTaskItem(const FileTaskItem &item) { AbstractFileTask::addTaskItem(item); } void DownloadFileTask::setTaskItems(const QList &items) { AbstractFileTask::setTaskItems(items); } void DownloadFileTask::addTaskItems(const QList &items) { AbstractFileTask::addTaskItems(items); } void DownloadFileTask::setAuthenticator(const QAuthenticator &authenticator) { m_authenticator = authenticator; } void DownloadFileTask::setProxyFactory(KDUpdater::FileDownloaderProxyFactory *factory) { m_proxyFactory.reset(factory); } void DownloadFileTask::doTask(QFutureInterface &fi) { QEventLoop el; Downloader downloader; connect(&downloader, &Downloader::finished, &el, &QEventLoop::quit); QList items = taskItems(); if (!m_authenticator.isNull()) { for (int i = 0; i < items.count(); ++i) { if (items.at(i).value(TaskRole::Authenticator).isNull()) items[i].insert(TaskRole::Authenticator, QVariant::fromValue(m_authenticator)); } } downloader.download(fi, items, (m_proxyFactory.isNull() ? 0 : m_proxyFactory->clone())); el.exec(); // That's tricky here, we need to run our own event loop to keep QNAM working. } } // namespace QInstaller src/libs/installer/downloadfiletask.h000066400000000000000000000070261325366651500203170ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef DOWNLOADFILETASK_H #define DOWNLOADFILETASK_H #include "abstractfiletask.h" #include "filedownloaderfactory.h" #include Q_DECLARE_METATYPE(QAuthenticator) namespace QInstaller { namespace TaskRole { enum { Authenticator = TaskRole::TargetFile + 10 }; } class AuthenticationRequiredException : public TaskException { public: enum struct Type { Proxy, Server }; explicit AuthenticationRequiredException(Type type, const QString &message); ~AuthenticationRequiredException() throw() {} Type type() const { return m_type; } QNetworkProxy proxy() const { return m_proxy; } void setProxy(const QNetworkProxy &proxy) { m_proxy = proxy; } FileTaskItem taskItem() const { return m_fileTaskItem; } void setFileTaskItem(const FileTaskItem &item) { m_fileTaskItem = item; } void raise() const { throw *this; } AuthenticationRequiredException *clone() const { return new AuthenticationRequiredException(*this); } private: Type m_type; QNetworkProxy m_proxy; FileTaskItem m_fileTaskItem; }; class INSTALLER_EXPORT DownloadFileTask : public AbstractFileTask { Q_OBJECT Q_DISABLE_COPY(DownloadFileTask) public: DownloadFileTask() {} explicit DownloadFileTask(const FileTaskItem &item) : AbstractFileTask(item) {} explicit DownloadFileTask(const QList &items); explicit DownloadFileTask(const QString &source) : AbstractFileTask(source) {} DownloadFileTask(const QString &source, const QString &target) : AbstractFileTask(source, target) {} void addTaskItem(const FileTaskItem &items); void addTaskItems(const QList &items); void setTaskItem(const FileTaskItem &items); void setTaskItems(const QList &items); void setAuthenticator(const QAuthenticator &authenticator); void setProxyFactory(KDUpdater::FileDownloaderProxyFactory *factory); void doTask(QFutureInterface &fi); private: friend class Downloader; QAuthenticator m_authenticator; QScopedPointer m_proxyFactory; }; } // namespace QInstaller #endif // DOWNLOADFILETASK_H src/libs/installer/downloadfiletask_p.h000066400000000000000000000064511325366651500206370ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef DOWNLOADFILETASK_P_H #define DOWNLOADFILETASK_P_H #include "downloadfiletask.h" #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QSslError; QT_END_NAMESPACE namespace QInstaller { struct Data { Q_DISABLE_COPY(Data) Data() : file(Q_NULLPTR) , observer(Q_NULLPTR) {} Data(const FileTaskItem &fti) : taskItem(fti) , file(Q_NULLPTR) , observer(new FileTaskObserver(QCryptographicHash::Sha1)) {} FileTaskItem taskItem; std::unique_ptr file; std::unique_ptr observer; }; class Downloader : public QObject { Q_OBJECT Q_DISABLE_COPY(Downloader) public: Downloader(); ~Downloader(); void download(QFutureInterface &fi, const QList &items, QNetworkProxyFactory *networkProxyFactory); signals: void finished(); private slots: void doDownload(); void onReadyRead(); void onFinished(QNetworkReply *reply); void onError(QNetworkReply::NetworkError error); void onSslErrors(const QList &sslErrors); void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); void onProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator); void onTimeout(); private: bool testCanceled(); QNetworkReply *startDownload(const FileTaskItem &item); private: QFutureInterface *m_futureInterface; QTimer m_timer; int m_finished; QNetworkAccessManager m_nam; QList m_items; QMultiHash m_redirects; std::unordered_map> m_downloads; }; } // namespace QInstaller #endif // DOWNLOADFILETASK_P_H src/libs/installer/elevatedexecuteoperation.cpp000066400000000000000000000230171325366651500224130ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "elevatedexecuteoperation.h" #include "environment.h" #include "qprocesswrapper.h" #include #include #include #include using namespace QInstaller; class ElevatedExecuteOperation::Private { public: explicit Private(ElevatedExecuteOperation *qq) : q(qq) , process(0) , showStandardError(false) { } private: ElevatedExecuteOperation *const q; public: void readProcessOutput(); bool run(const QStringList &arguments); QProcessWrapper *process; bool showStandardError; }; ElevatedExecuteOperation::ElevatedExecuteOperation(PackageManagerCore *core) : UpdateOperation(core) , d(new Private(this)) { // this operation has to "overwrite" the Execute operation from KDUpdater setName(QLatin1String("Execute")); } ElevatedExecuteOperation::~ElevatedExecuteOperation() { delete d; } bool ElevatedExecuteOperation::performOperation() { // This operation receives only one argument. It is the complete // command line of the external program to execute. if (!checkArgumentCount(1, INT_MAX)) return false; QStringList args; foreach (const QString &argument, arguments()) { if (argument!=QLatin1String("UNDOEXECUTE")) args.append(argument); else break; //we don't need the UNDOEXECUTE args here } return d->run(args); } bool ElevatedExecuteOperation::Private::run(const QStringList &arguments) { QStringList args = arguments; QString workingDirectory; QStringList filteredWorkingDirectoryArgs = args.filter(QLatin1String("workingdirectory="), Qt::CaseInsensitive); if (!filteredWorkingDirectoryArgs.isEmpty()) { QString workingDirectoryArgument = filteredWorkingDirectoryArgs.at(0); workingDirectory = workingDirectoryArgument; workingDirectory.replace(QLatin1String("workingdirectory="), QString(), Qt::CaseInsensitive); args.removeAll(workingDirectoryArgument); } QString customErrorMessage; QStringList filteredCustomErrorMessage = args.filter(QLatin1String("errormessage="), Qt::CaseInsensitive); if (!filteredCustomErrorMessage.isEmpty()) { QString customErrorMessageArgument = filteredCustomErrorMessage.at(0); customErrorMessage = customErrorMessageArgument; customErrorMessage.replace(QLatin1String("errormessage="), QString(), Qt::CaseInsensitive); args.removeAll(customErrorMessageArgument); } if (args.last().endsWith(QLatin1String("showStandardError"))) { showStandardError = true; args.pop_back(); } QList< int > allowedExitCodes; QRegExp re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$")); if (re.exactMatch(args.first())) { const QStringList numbers = re.cap(1).split(QLatin1Char(',')); for(QStringList::const_iterator it = numbers.constBegin(); it != numbers.constEnd(); ++it) allowedExitCodes.push_back(it->toInt()); args.pop_front(); } else { allowedExitCodes.push_back(0); } const QString callstr = args.join(QLatin1String(" ")); // unix style: when there's an ampersand after the command, it's started detached if (args.count() >= 2 && args.last() == QLatin1String("&")) { args.pop_back(); const bool success = QProcessWrapper::startDetached(args.front(), args.mid(1)); if (!success) { q->setError(UserDefinedError); q->setErrorString(tr("Cannot start detached: \"%1\"").arg(callstr)); } return success; } process = new QProcessWrapper(); if (!workingDirectory.isEmpty()) { process->setWorkingDirectory(workingDirectory); qDebug() << "ElevatedExecuteOperation setWorkingDirectory:" << workingDirectory; } QProcessEnvironment penv; // there is no way to serialize a QProcessEnvironment properly other than per mangled QStringList: // (i.e. no other way to list all keys) process->setEnvironment(KDUpdater::Environment::instance().applyTo(penv).toStringList()); if (showStandardError) process->setProcessChannelMode(QProcessWrapper::MergedChannels); connect(q, &ElevatedExecuteOperation::cancelProcess, process, &QProcessWrapper::cancel); //we still like the none blocking possibility to perform this operation without threads QEventLoop loop; if (QThread::currentThread() == qApp->thread()) { QObject::connect(process, &QProcessWrapper::finished, &loop, &QEventLoop::quit); } //readProcessOutput should only called from this current Thread -> Qt::DirectConnection QObject::connect(process, SIGNAL(readyRead()), q, SLOT(readProcessOutput()), Qt::DirectConnection); process->start(args.front(), args.mid(1)); qDebug() << args.front() << "started, arguments:" << QStringList(args.mid(1)).join(QLatin1String(" ")); bool success = false; //we still like the none blocking possibility to perform this operation without threads if (QThread::currentThread() == qApp->thread()) { success = process->waitForStarted(); } else { success = process->waitForFinished(-1); } bool returnValue = true; if (!success) { q->setError(UserDefinedError); //TODO: pass errorString() through the wrapper */ q->setErrorString(tr("Cannot start: \"%1\": %2").arg(callstr, process->errorString())); returnValue = false; } if (QThread::currentThread() == qApp->thread()) { if (process->state() != QProcessWrapper::NotRunning) { loop.exec(); } readProcessOutput(); } q->setValue(QLatin1String("ExitCode"), process->exitCode()); if (process->exitStatus() == QProcessWrapper::CrashExit) { q->setError(UserDefinedError); q->setErrorString(tr("Program crashed: \"%1\"").arg(callstr)); returnValue = false; } if (!allowedExitCodes.contains(process->exitCode())) { q->setError(UserDefinedError); if (customErrorMessage.isEmpty()) { q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"") .arg(QString::number(process->exitCode()), callstr)); } else { q->setErrorString(customErrorMessage); } QByteArray standardErrorOutput = process->readAllStandardError(); // in error case it would be useful to see something in verbose output if (!standardErrorOutput.isEmpty()) qWarning().noquote() << standardErrorOutput; returnValue = false; } Q_ASSERT(process); Q_ASSERT(process->state() == QProcessWrapper::NotRunning); delete process; process = 0; return returnValue; } /*! Cancels the ElevatedExecuteOperation. This methods tries to terminate the process gracefully by calling QProcessWrapper::terminate. After 10 seconds, the process gets killed. */ void ElevatedExecuteOperation::cancelOperation() { emit cancelProcess(); } void ElevatedExecuteOperation::Private::readProcessOutput() { Q_ASSERT(process); Q_ASSERT(QThread::currentThread() == process->thread()); if (QThread::currentThread() != process->thread()) { qDebug() << Q_FUNC_INFO << "can only be called from the same thread as the process is."; } const QByteArray output = process->readAll(); if (!output.isEmpty()) { if (q->error() == UserDefinedError) qWarning() << output; else qDebug() << output; emit q->outputTextChanged(QString::fromLocal8Bit(output)); } } bool ElevatedExecuteOperation::undoOperation() { QStringList args; bool found = false; foreach (const QString &argument, arguments()) { if (found) args.append(argument); else found = argument == QLatin1String("UNDOEXECUTE"); } if (args.isEmpty()) return true; return d->run(args); } bool ElevatedExecuteOperation::testOperation() { // TODO return true; } void ElevatedExecuteOperation::backup() { } #include "moc_elevatedexecuteoperation.cpp" src/libs/installer/elevatedexecuteoperation.h000066400000000000000000000040341325366651500220560ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef ELEVATEDEXECUTEOPERATION_H #define ELEVATEDEXECUTEOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT ElevatedExecuteOperation : public QObject, public Operation { Q_OBJECT public: explicit ElevatedExecuteOperation(PackageManagerCore *core); ~ElevatedExecuteOperation(); void backup() Q_DECL_OVERRIDE; bool performOperation() Q_DECL_OVERRIDE; bool undoOperation() Q_DECL_OVERRIDE; bool testOperation() Q_DECL_OVERRIDE; Q_SIGNALS: void cancelProcess(); void outputTextChanged(const QString &text); public Q_SLOTS: void cancelOperation(); private: Q_PRIVATE_SLOT(d, void readProcessOutput()) class Private; Private *d; }; } // namespace #endif src/libs/installer/environmentvariablesoperation.cpp000066400000000000000000000222741325366651500235000ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "environmentvariablesoperation.h" #include "qsettingswrapper.h" #include #include "environment.h" #ifdef Q_OS_WIN # include #endif using namespace QInstaller; using namespace KDUpdater; EnvironmentVariableOperation::EnvironmentVariableOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("EnvironmentVariable")); } void EnvironmentVariableOperation::backup() { } #ifdef Q_OS_WIN static void broadcastEnvironmentChange() { // Use SendMessageTimeout to Broadcast a message to the whole system to update settings of all // running applications. This is needed to activate the changes done above without logout+login. DWORD_PTR aResult = 0; LRESULT sendresult = SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) L"Environment", SMTO_BLOCK | SMTO_ABORTIFHUNG, 5000, &aResult); if (sendresult == 0 || aResult != 0) qWarning("Failed to broadcast the WM_SETTINGCHANGE message."); } #endif namespace { bool handleRegExpandSz(const QString ®Path, const QString &name, const QString &value, QString *errorString, bool *error) { bool setAsExpandSZ = false; #ifdef Q_OS_WIN // Account for when it is originally REG_EXPAND_SZ as we don't want // to lose this setting (see Path environment variable) const bool isLocalKey = regPath.startsWith(QStringLiteral("HKEY_LOCAL")); HKEY hkey = isLocalKey ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; // Drop the HKEY...\\ part const QString keyPath = regPath.mid(isLocalKey ? 19 : 18, -1); HKEY handle; LONG res = RegOpenKeyEx(hkey, reinterpret_cast(keyPath.utf16()), 0, KEY_READ, &handle); if (res == ERROR_SUCCESS) { DWORD dataType; DWORD dataSize; res = RegQueryValueEx(handle, reinterpret_cast(name.utf16()), 0, &dataType, 0, &dataSize); setAsExpandSZ = (res == ERROR_SUCCESS) && (dataType == REG_EXPAND_SZ); if (setAsExpandSZ) { RegCloseKey(handle); res = RegOpenKeyEx(hkey, reinterpret_cast(keyPath.utf16()), 0, KEY_SET_VALUE, &handle); if (res == ERROR_SUCCESS) { const QByteArray data(reinterpret_cast(value.utf16()), (value.length() + 1) * 2); res = RegSetValueEx(handle, reinterpret_cast(name.utf16()), 0, REG_EXPAND_SZ, reinterpret_cast(data.constData()), data.size()); RegCloseKey(handle); } if (res != ERROR_SUCCESS) { *errorString = UpdateOperation::tr("Cannot write to registry path %1.").arg(regPath); *error = true; } } } #endif return setAsExpandSZ; } template UpdateOperation::Error writeSetting(const QString ®Path, const QString &name, const QString &value, QString *errorString, QString *oldValue) { oldValue->clear(); SettingsType registry(regPath, QSettingsWrapper::NativeFormat); if (!registry.isWritable()) { *errorString = UpdateOperation::tr("Registry path %1 is not writable.").arg(regPath); return UpdateOperation::UserDefinedError; } // remember old value for undo *oldValue = registry.value(name).toString(); bool error = false; if (handleRegExpandSz(regPath, name, value, errorString, &error)) return error ? UpdateOperation::UserDefinedError : UpdateOperation::NoError; // set the new value registry.setValue(name, value); registry.sync(); if (registry.status() != QSettingsWrapper::NoError) { *errorString = UpdateOperation::tr("Cannot write to registry path %1.").arg(regPath); return UpdateOperation::UserDefinedError; } return UpdateOperation::NoError; } template UpdateOperation::Error undoSetting(const QString ®Path, const QString &name, const QString &value, const QString &oldValue, QString *errorString) { QString actual; { SettingsType registry(regPath, QSettingsWrapper::NativeFormat); actual = registry.value(name).toString(); } if (actual != value) //key changed, don't undo return UpdateOperation::UserDefinedError; bool error = false; if (handleRegExpandSz(regPath, name, oldValue, errorString, &error)) return error ? UpdateOperation::UserDefinedError : UpdateOperation::NoError; QString dontcare; return writeSetting(regPath, name, oldValue, errorString, &dontcare); } } // namespace bool EnvironmentVariableOperation::performOperation() { if (!checkArgumentCount(2, 4)) return false; const QStringList args = arguments(); const QString name = args.at(0); const QString value = args.at(1); #ifdef Q_OS_WIN const bool isPersistent = arguments().count() > 2 ? arguments().at(2) == QLatin1String("true") : true; const bool isSystemWide = arguments().count() > 3 ? arguments().at(3) == QLatin1String("true") : false; QString oldvalue; if (isPersistent) { const QString regPath = isSystemWide ? QLatin1String("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet" "\\Control\\Session Manager\\Environment") : QLatin1String("HKEY_CURRENT_USER\\Environment"); // write the name=value pair to the global environment QString errorString; Error err = NoError; err = writeSetting(regPath, name, value, &errorString, &oldvalue); if (err != NoError) { setError(err); setErrorString(errorString); return false; } broadcastEnvironmentChange(); setValue(QLatin1String("oldvalue"), oldvalue); return true; } Q_ASSERT(!isPersistent); #endif setValue(QLatin1String("oldvalue"), Environment::instance().value(name)); Environment::instance().setTemporaryValue(name, value); return true; } bool EnvironmentVariableOperation::undoOperation() { if (arguments().count() < 2 || arguments().count() > 4) return false; const QString name = arguments().at(0); const QString value = arguments().at(1); const QString oldvalue = this->value(QLatin1String("oldvalue")).toString(); #ifdef Q_OS_WIN const bool isPersistent = arguments().count() >= 3 ? arguments().at(2) == QLatin1String("true") : true; #else const bool isPersistent = false; #endif if (!isPersistent) { const QString actual = Environment::instance().value(name); const bool doUndo = actual == value; if (doUndo) Environment::instance().setTemporaryValue(name, oldvalue); return doUndo; } #ifdef Q_OS_WIN const bool isSystemWide = arguments().count() >= 4 ? arguments().at(3) == QLatin1String("true") : false; const QString regPath = isSystemWide ? QLatin1String("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\" "Control\\Session Manager\\Environment") : QLatin1String("HKEY_CURRENT_USER\\Environment"); QString errorString; const Error err = undoSetting(regPath, name, value, oldvalue, &errorString); if (err != NoError) { setError(err); setErrorString(errorString); return false; } #endif return true; } bool EnvironmentVariableOperation::testOperation() { return true; } src/libs/installer/environmentvariablesoperation.h000066400000000000000000000033661325366651500231460ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef ENVIRONMENTVARIABLESOPERATION_H #define ENVIRONMENTVARIABLESOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT EnvironmentVariableOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::EnvironmentVariableOperation) public: explicit EnvironmentVariableOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } #endif src/libs/installer/errors.h000066400000000000000000000033231325366651500162750ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef ERRORS_H #define ERRORS_H #include #include #include namespace QInstaller { class Error : public std::exception { public: Error() {} explicit Error(const QString &message) : m_message(message) {} virtual ~Error() throw() {} QString message() const { return m_message; } private: QString m_message; }; } #endif // ERRORS_H src/libs/installer/extractarchiveoperation.cpp000066400000000000000000000105721325366651500222550ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "extractarchiveoperation_p.h" #include #include namespace QInstaller { ExtractArchiveOperation::ExtractArchiveOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Extract")); } void ExtractArchiveOperation::backup() { // we need to backup on the fly... } bool ExtractArchiveOperation::performOperation() { if (!checkArgumentCount(2)) return false; const QStringList args = arguments(); const QString archivePath = args.at(0); const QString targetDir = args.at(1); Receiver receiver; Callback callback; connect(&callback, &Callback::currentFileChanged, this, &ExtractArchiveOperation::fileFinished); connect(&callback, &Callback::progressChanged, this, &ExtractArchiveOperation::progressChanged); if (PackageManagerCore *core = packageManager()) { connect(core, &PackageManagerCore::statusChanged, &callback, &Callback::statusChanged); } Runnable *runnable = new Runnable(archivePath, targetDir, &callback); connect(runnable, &Runnable::finished, &receiver, &Receiver::runnableFinished, Qt::QueuedConnection); QEventLoop loop; connect(&receiver, &Receiver::finished, &loop, &QEventLoop::quit); if (QThreadPool::globalInstance()->tryStart(runnable)) { loop.exec(); } else { // HACK: In case there is no availabe thread we should call it directly. runnable->run(); receiver.runnableFinished(true, QString()); } // TODO: Use backups for rollback, too? Doesn't work for uninstallation though. // delete all backups we can delete right now, remember the rest foreach (const Backup &i, callback.backupFiles()) deleteFileNowOrLater(i.second); if (!receiver.success()) { setError(UserDefinedError); setErrorString(receiver.errorString()); return false; } return true; } bool ExtractArchiveOperation::undoOperation() { Q_ASSERT(arguments().count() == 2); const QStringList files = value(QLatin1String("files")).toStringList(); WorkerThread *const thread = new WorkerThread(this, files); connect(thread, &WorkerThread::currentFileChanged, this, &ExtractArchiveOperation::outputTextChanged); connect(thread, &WorkerThread::progressChanged, this, &ExtractArchiveOperation::progressChanged); QEventLoop loop; connect(thread, &QThread::finished, &loop, &QEventLoop::quit, Qt::QueuedConnection); thread->start(); loop.exec(); thread->deleteLater(); return true; } bool ExtractArchiveOperation::testOperation() { return true; } /*! This slot is direct connected to the caller so please don't call it from another thread in the same time. */ void ExtractArchiveOperation::fileFinished(const QString &filename) { QStringList files = value(QLatin1String("files")).toStringList(); files.prepend(filename); setValue(QLatin1String("files"), files); emit outputTextChanged(QDir::toNativeSeparators(filename)); } } // namespace QInstaller src/libs/installer/extractarchiveoperation.h000066400000000000000000000037561325366651500217300ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef EXTRACTARCHIVEOPERATION_H #define EXTRACTARCHIVEOPERATION_H #include "qinstallerglobal.h" #include namespace QInstaller { class INSTALLER_EXPORT ExtractArchiveOperation : public QObject, public Operation { Q_OBJECT friend class WorkerThread; public: explicit ExtractArchiveOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); Q_SIGNALS: void outputTextChanged(const QString &progress); void progressChanged(double); private Q_SLOTS: void fileFinished(const QString &progress); private: class Callback; class Runnable; class Receiver; }; } #endif src/libs/installer/extractarchiveoperation_p.h000066400000000000000000000153241325366651500222410ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef EXTRACTARCHIVEOPERATION_P_H #define EXTRACTARCHIVEOPERATION_P_H #include "extractarchiveoperation.h" #include "fileutils.h" #include "lib7z_extract.h" #include "lib7z_facade.h" #include "packagemanagercore.h" #include #include namespace QInstaller { class WorkerThread : public QThread { Q_OBJECT Q_DISABLE_COPY(WorkerThread) public: WorkerThread(ExtractArchiveOperation *op, const QStringList &files) : m_files(files) , m_op(op) { setObjectName(QLatin1String("ExtractArchive")); } void run() { Q_ASSERT(m_op != 0); int removedCounter = 0; foreach (const QString &file, m_files) { removedCounter++; const QFileInfo fi(file); emit currentFileChanged(QDir::toNativeSeparators(file)); emit progressChanged(double(removedCounter) / m_files.count()); if (fi.isFile() || fi.isSymLink()) { m_op->deleteFileNowOrLater(fi.absoluteFilePath()); } else if (fi.isDir()) { removeSystemGeneratedFiles(file); fi.dir().rmdir(file); // directory may not exist } } } signals: void currentFileChanged(const QString &filename); void progressChanged(double); private: QStringList m_files; ExtractArchiveOperation *m_op; }; typedef QPair Backup; typedef QVector BackupFiles; class ExtractArchiveOperation::Callback : public QObject, public Lib7z::ExtractCallback { Q_OBJECT Q_DISABLE_COPY(Callback) public: Callback() = default; BackupFiles backupFiles() const { return m_backupFiles; } public slots: void statusChanged(QInstaller::PackageManagerCore::Status status) { switch(status) { case PackageManagerCore::Canceled: m_state = E_ABORT; break; case PackageManagerCore::Failure: m_state = E_FAIL; break; default: // ignore all other status values break; } } signals: void currentFileChanged(const QString &filename); void progressChanged(double progress); private: void setCurrentFile(const QString &filename) Q_DECL_OVERRIDE { emit currentFileChanged(QDir::toNativeSeparators(filename)); } static QString generateBackupName(const QString &fn) { const QString bfn = fn + QLatin1String(".tmpUpdate"); QString res = bfn; int i = 0; while (QFile::exists(res)) res = bfn + QString::fromLatin1(".%1").arg(i++); return res; } bool prepareForFile(const QString &filename) Q_DECL_OVERRIDE { if (!QFile::exists(filename)) return true; const QString backup = generateBackupName(filename); QFile f(filename); const bool renamed = f.rename(backup); if (f.exists() && !renamed) { qCritical("Cannot rename %s to %s: %s", qPrintable(filename), qPrintable(backup), qPrintable(f.errorString())); return false; } m_backupFiles.append(qMakePair(filename, backup)); return true; } HRESULT setCompleted(quint64 completed, quint64 total) Q_DECL_OVERRIDE { emit progressChanged(double(completed) / total); return m_state; } private: HRESULT m_state = S_OK; BackupFiles m_backupFiles; }; class ExtractArchiveOperation::Runnable : public QObject, public QRunnable { Q_OBJECT Q_DISABLE_COPY(Runnable) public: Runnable(const QString &archivePath, const QString &targetDir, ExtractArchiveOperation::Callback *callback) : m_archivePath(archivePath) , m_targetDir(targetDir) , m_callback(callback) {} void run() { QFile archive(m_archivePath); if (!archive.open(QIODevice::ReadOnly)) { emit finished(false, tr("Cannot open archive \"%1\" for reading: %2").arg(m_archivePath, archive.errorString())); return; } try { Lib7z::extractArchive(&archive, m_targetDir, m_callback); emit finished(true, QString()); } catch (const Lib7z::SevenZipException& e) { emit finished(false, tr("Error while extracting archive \"%1\": %2").arg(m_archivePath, e.message())); } catch (...) { emit finished(false, tr("Unknown exception caught while extracting \"%1\".") .arg(m_archivePath)); } } signals: void finished(bool success, const QString &errorString); private: QString m_archivePath; QString m_targetDir; ExtractArchiveOperation::Callback *m_callback; }; class ExtractArchiveOperation::Receiver : public QObject { Q_OBJECT Q_DISABLE_COPY(Receiver) public: Receiver() = default; bool success() const { return m_success; } QString errorString() const { return m_errorString; } public slots: void runnableFinished(bool ok, const QString &msg) { m_success = ok; m_errorString = msg; emit finished(); } signals: void finished(); private: bool m_success = false; QString m_errorString; }; } #endif // EXTRACTARCHIVEOPERATION_P_H src/libs/installer/fakestopprocessforupdateoperation.cpp000066400000000000000000000060011325366651500243560ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "fakestopprocessforupdateoperation.h" #include "messageboxhandler.h" #include "packagemanagercore.h" using namespace KDUpdater; using namespace QInstaller; FakeStopProcessForUpdateOperation::FakeStopProcessForUpdateOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("FakeStopProcessForUpdate")); } void FakeStopProcessForUpdateOperation::backup() { } bool FakeStopProcessForUpdateOperation::performOperation() { return true; } bool FakeStopProcessForUpdateOperation::undoOperation() { setError(KDUpdater::UpdateOperation::NoError); if (!checkArgumentCount(1)) return false; PackageManagerCore *const core = packageManager(); if (!core) { setError(KDUpdater::UpdateOperation::UserDefinedError, tr("Cannot get package manager " "core.")); return false; } QStringList processes = arguments().at(0).split(QLatin1Char(','), QString::SkipEmptyParts); for (int i = processes.count() - 1; i >= 0; --i) { if (!core->isProcessRunning(processes.at(i))) processes.removeAt(i); } if (processes.isEmpty()) return true; if (processes.count() == 1) { setError(UpdateOperation::UserDefinedError, tr("This process should be stopped before " "continuing: %1").arg(processes.first())); } else { const QString sep = QString::fromWCharArray(L"\n \u2022 "); // Unicode bullet setError(UpdateOperation::UserDefinedError, tr("These processes should be stopped before " "continuing: %1").arg(sep + processes.join(sep))); } return false; } bool FakeStopProcessForUpdateOperation::testOperation() { return true; } src/libs/installer/fakestopprocessforupdateoperation.h000066400000000000000000000034111325366651500240250ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef FAKESTOPPROCESSFORUPDATEOPERATION_H #define FAKESTOPPROCESSFORUPDATEOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT FakeStopProcessForUpdateOperation : public QObject, public Operation { Q_OBJECT public: explicit FakeStopProcessForUpdateOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } #endif // FAKESTOPPROCESSFORUPDATEOPERATION_H src/libs/installer/fileio.cpp000066400000000000000000000136361325366651500165730ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "fileio.h" #include "errors.h" #include "range.h" #include #include #include #include #include qint64 QInstaller::retrieveInt64(QFileDevice *in) { qint64 n = 0; QInstaller::blockingRead(in, reinterpret_cast(&n), sizeof(n)); return n; } void QInstaller::appendInt64(QFileDevice *out, qint64 n) { QInstaller::blockingWrite(out, reinterpret_cast(&n), sizeof(n)); } Range QInstaller::retrieveInt64Range(QFileDevice *in) { const quint64 start = QInstaller::retrieveInt64(in); const quint64 length = QInstaller::retrieveInt64(in); return Range::fromStartAndLength(start, length); } void QInstaller::appendInt64Range(QFileDevice *out, const Range &r) { QInstaller::appendInt64(out, r.start()); QInstaller::appendInt64(out, r.length()); } QString QInstaller::retrieveString(QFileDevice *in) { return QString::fromUtf8(QInstaller::retrieveByteArray(in)); } void QInstaller::appendString(QFileDevice *out, const QString &str) { QInstaller::appendByteArray(out, str.toUtf8()); } QByteArray QInstaller::retrieveByteArray(QFileDevice *in) { QByteArray ba(QInstaller::retrieveInt64(in), '\0'); QInstaller::blockingRead(in, ba.data(), ba.size()); return ba; } void QInstaller::appendByteArray(QFileDevice *out, const QByteArray &ba) { QInstaller::appendInt64(out, ba.size()); QInstaller::blockingWrite(out, ba.constData(), ba.size()); } QByteArray QInstaller::retrieveData(QFileDevice *in, qint64 size) { QByteArray ba(size, '\0'); QInstaller::blockingRead(in, ba.data(), size); return ba; } void QInstaller::appendData(QFileDevice *out, QFileDevice *in, qint64 size) { Q_ASSERT(!in->isSequential()); QInstaller::blockingCopy(in, out, size); } void QInstaller::openForRead(QFileDevice *dev) { Q_ASSERT(dev); if (!dev->open(QIODevice::ReadOnly)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot open file \"%1\" for reading: %2").arg( QDir::toNativeSeparators(dev->fileName()), dev->errorString())); } } void QInstaller::openForWrite(QFileDevice *dev) { Q_ASSERT(dev); if (!dev->open(QIODevice::WriteOnly)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(dev->fileName()), dev->errorString())); } } void QInstaller::openForAppend(QFileDevice *dev) { Q_ASSERT(dev); if (!dev->open(QIODevice::WriteOnly | QIODevice::Append)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(dev->fileName()), dev->errorString())); } } qint64 QInstaller::blockingRead(QFileDevice *in, char *buffer, qint64 size) { if (in->atEnd()) return 0; qint64 left = size; while (left > 0) { const qint64 n = in->read(buffer, left); if (n < 0) { throw Error(QCoreApplication::translate("QInstaller", "Read failed after %1 bytes: %2").arg(QString::number(size - left), in->errorString())); } left -= n; buffer += n; } return size; } qint64 QInstaller::blockingCopy(QFileDevice *in, QFileDevice *out, qint64 size) { static const qint64 blockSize = 4096; QByteArray ba(blockSize, '\0'); qint64 actual = qMin(blockSize, size); while (actual > 0) { try { QInstaller::blockingRead(in, ba.data(), actual); QInstaller::blockingWrite(out, ba.constData(), actual); size -= actual; actual = qMin(blockSize, size); } catch (const Error &error) { throw Error(QCoreApplication::translate("QInstaller", "Copy failed: %1") .arg(error.message())); } } return size; } qint64 QInstaller::blockingWrite(QFileDevice *out, const QByteArray &data) { return QInstaller::blockingWrite(out, data.constData(), data.size()); } qint64 QInstaller::blockingWrite(QFileDevice *out, const char *data, qint64 size) { qint64 left = size; while (left > 0) { const qint64 n = out->write(data, left); if (n < 0) { throw Error(QCoreApplication::translate("QInstaller", "Write failed after %1 bytes: %2").arg(QString::number(size - left), out->errorString())); } left -= n; } return size; } src/libs/installer/fileio.h000066400000000000000000000053601325366651500162330ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef FILEIO_H #define FILEIO_H #include "installer_global.h" QT_BEGIN_NAMESPACE class QByteArray; class QFileDevice; class QString; QT_END_NAMESPACE template class Range; namespace QInstaller { qint64 INSTALLER_EXPORT retrieveInt64(QFileDevice *in); void INSTALLER_EXPORT appendInt64(QFileDevice *out, qint64 n); Range INSTALLER_EXPORT retrieveInt64Range(QFileDevice *in); void INSTALLER_EXPORT appendInt64Range(QFileDevice *out, const Range &r); QString INSTALLER_EXPORT retrieveString(QFileDevice *in); void INSTALLER_EXPORT appendString(QFileDevice *out, const QString &str); QByteArray INSTALLER_EXPORT retrieveByteArray(QFileDevice *in); void INSTALLER_EXPORT appendByteArray(QFileDevice *out, const QByteArray &ba); QByteArray INSTALLER_EXPORT retrieveData(QFileDevice *in, qint64 size); void INSTALLER_EXPORT appendData(QFileDevice *out, QFileDevice *in, qint64 size); void INSTALLER_EXPORT openForRead(QFileDevice *dev); void INSTALLER_EXPORT openForWrite(QFileDevice *dev); void INSTALLER_EXPORT openForAppend(QFileDevice *dev); qint64 INSTALLER_EXPORT blockingRead(QFileDevice *in, char *buffer, qint64 size); qint64 INSTALLER_EXPORT blockingCopy(QFileDevice *in, QFileDevice *out, qint64 size); qint64 INSTALLER_EXPORT blockingWrite(QFileDevice *out, const QByteArray &data); qint64 INSTALLER_EXPORT blockingWrite(QFileDevice *out, const char *data, qint64 size); } // namespace QInstaller #endif // FILEIO_H src/libs/installer/fileutils.cpp000066400000000000000000000464161325366651500173260ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "fileutils.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_UNIX #include #include #include #endif using namespace QInstaller; // -- TempDirDeleter TempDirDeleter::TempDirDeleter(const QString &path) { m_paths.insert(path); } TempDirDeleter::TempDirDeleter(const QStringList &paths) : m_paths(paths.toSet()) { } TempDirDeleter::~TempDirDeleter() { releaseAndDeleteAll(); } QStringList TempDirDeleter::paths() const { return m_paths.toList(); } void TempDirDeleter::add(const QString &path) { m_paths.insert(path); } void TempDirDeleter::add(const QStringList &paths) { m_paths += paths.toSet(); } void TempDirDeleter::releaseAll() { m_paths.clear(); } void TempDirDeleter::release(const QString &path) { m_paths.remove(path); } void TempDirDeleter::passAndReleaseAll(TempDirDeleter &tdd) { tdd.m_paths = m_paths; releaseAll(); } void TempDirDeleter::passAndRelease(TempDirDeleter &tdd, const QString &path) { tdd.add(path); release(path); } void TempDirDeleter::releaseAndDeleteAll() { foreach (const QString &path, m_paths) releaseAndDelete(path); } void TempDirDeleter::releaseAndDelete(const QString &path) { if (m_paths.contains(path)) { try { m_paths.remove(path); removeDirectory(path); } catch (const Error &e) { qCritical() << Q_FUNC_INFO << "Exception caught:" << e.message(); } catch (...) { qCritical() << Q_FUNC_INFO << "Unknown exception caught."; } } } QString QInstaller::humanReadableSize(const qint64 &size, int precision) { double sizeAsDouble = size; static QStringList measures; if (measures.isEmpty()) measures << QCoreApplication::translate("QInstaller", "bytes") << QCoreApplication::translate("QInstaller", "KiB") << QCoreApplication::translate("QInstaller", "MiB") << QCoreApplication::translate("QInstaller", "GiB") << QCoreApplication::translate("QInstaller", "TiB") << QCoreApplication::translate("QInstaller", "PiB") << QCoreApplication::translate("QInstaller", "EiB") << QCoreApplication::translate("QInstaller", "ZiB") << QCoreApplication::translate("QInstaller", "YiB"); QStringListIterator it(measures); QString measure(it.next()); while (sizeAsDouble >= 1024.0 && it.hasNext()) { measure = it.next(); sizeAsDouble /= 1024.0; } return QString::fromLatin1("%1 %2").arg(sizeAsDouble, 0, 'f', precision).arg(measure); } // -- read, write operations bool QInstaller::isLocalUrl(const QUrl &url) { return url.scheme().isEmpty() || url.scheme().toLower() == QLatin1String("file"); } QString QInstaller::pathFromUrl(const QUrl &url) { if (isLocalUrl(url)) return url.toLocalFile(); const QString str = url.toString(); if (url.scheme() == QLatin1String("resource")) return str.mid(QString::fromLatin1("resource").length()); return str; } void QInstaller::removeFiles(const QString &path, bool ignoreErrors) { const QFileInfoList entries = QDir(path).entryInfoList(QDir::AllEntries | QDir::Hidden); foreach (const QFileInfo &fi, entries) { if (fi.isSymLink() || fi.isFile()) { QFile f(fi.filePath()); if (!f.remove()) { const QString errorMessage = QCoreApplication::translate("QInstaller", "Cannot remove file \"%1\": %2").arg( QDir::toNativeSeparators(f.fileName()), f.errorString()); if (!ignoreErrors) throw Error(errorMessage); qWarning().noquote() << errorMessage; } } } } static QString errnoToQString(int error) { #if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) char msg[128]; if (strerror_s(msg, sizeof msg, error) != 0) return QString::fromLocal8Bit(msg); return QString(); #else return QString::fromLocal8Bit(strerror(error)); #endif } void QInstaller::removeDirectory(const QString &path, bool ignoreErrors) { if (path.isEmpty()) // QDir("") points to the working directory! We never want to remove that one. return; QStringList dirs; QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Dirs | QDir::NoSymLinks | QDir::Hidden, QDirIterator::Subdirectories); while (it.hasNext()) { dirs.prepend(it.next()); removeFiles(dirs.at(0), ignoreErrors); } QDir d; dirs.append(path); removeFiles(path, ignoreErrors); foreach (const QString &dir, dirs) { errno = 0; if (d.exists(path) && !d.rmdir(dir)) { const QString errorMessage = QCoreApplication::translate("QInstaller", "Cannot remove directory \"%1\": %2").arg(QDir::toNativeSeparators(dir), errnoToQString(errno)); if (!ignoreErrors) throw Error(errorMessage); qWarning().noquote() << errorMessage; } } } class RemoveDirectoryThread : public QThread { public: explicit RemoveDirectoryThread(const QString &path, bool ignoreErrors = false, QObject *parent = 0) : QThread(parent) , p(path) , ignore(ignoreErrors) { setObjectName(QLatin1String("RemoveDirectory")); } const QString &error() const { return err; } protected: /*! \reimp */ void run() { try { removeDirectory(p, ignore); } catch (const Error &e) { err = e.message(); } } private: QString err; const QString p; const bool ignore; }; void QInstaller::removeDirectoryThreaded(const QString &path, bool ignoreErrors) { RemoveDirectoryThread thread(path, ignoreErrors); QEventLoop loop; QObject::connect(&thread, &RemoveDirectoryThread::finished, &loop, &QEventLoop::quit); thread.start(); loop.exec(); if (!thread.error().isEmpty()) throw Error(thread.error()); } void QInstaller::removeSystemGeneratedFiles(const QString &path) { if (path.isEmpty()) return; #if defined Q_OS_OSX QFile::remove(path + QLatin1String("/.DS_Store")); #elif defined Q_OS_WIN QFile::remove(path + QLatin1String("/Thumbs.db")); #endif } void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir) { Q_ASSERT(QFileInfo(sourceDir).isDir()); Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir()); if (!QDir().mkpath(targetDir)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".") .arg(QDir::toNativeSeparators(targetDir))); } QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries); while (it.hasNext()) { const QFileInfo i(it.next()); if (i.isDir()) { copyDirectoryContents(QDir(sourceDir).absoluteFilePath(i.fileName()), QDir(targetDir).absoluteFilePath(i.fileName())); } else { QFile f(i.filePath()); const QString target = QDir(targetDir).absoluteFilePath(i.fileName()); if (!f.copy(target)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot copy file from \"%1\" to \"%2\": %3").arg( QDir::toNativeSeparators(f.fileName()), QDir::toNativeSeparators(target), f.errorString())); } } } } void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString &targetDir) { Q_ASSERT(QFileInfo(sourceDir).isDir()); Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir()); if (!QDir().mkpath(targetDir)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".") .arg(QDir::toNativeSeparators(targetDir))); } QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries); while (it.hasNext()) { const QFileInfo i(it.next()); if (i.isDir()) { // only copy directories that are not the target to avoid loop dir creations QString newSource = QDir(sourceDir).absoluteFilePath(i.fileName()); if (QDir(newSource) != QDir(targetDir)) { moveDirectoryContents(newSource, QDir(targetDir).absoluteFilePath(i.fileName())); } } else { QFile f(i.filePath()); const QString target = QDir(targetDir).absoluteFilePath(i.fileName()); if (!f.rename(target)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot move file from \"%1\" to \"%2\": %3").arg( QDir::toNativeSeparators(f.fileName()), QDir::toNativeSeparators(target), f.errorString())); } } } } void QInstaller::mkdir(const QString &path) { errno = 0; if (!QDir().mkdir(QFileInfo(path).absoluteFilePath())) { throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\": %2") .arg(QDir::toNativeSeparators(path), errnoToQString(errno))); } } void QInstaller::mkpath(const QString &path) { errno = 0; if (!QDir().mkpath(QFileInfo(path).absoluteFilePath())) { throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\": %2") .arg(QDir::toNativeSeparators(path), errnoToQString(errno))); } } QString QInstaller::generateTemporaryFileName(const QString &templ) { if (templ.isEmpty()) { QTemporaryFile f; if (!f.open()) { throw Error(QCoreApplication::translate("QInstaller", "Cannot open temporary file: %1").arg(f.errorString())); } return f.fileName(); } static const QString characters = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); QString suffix; qsrand(qrand() * QDateTime::currentDateTime().toTime_t()); for (int i = 0; i < 5; ++i) suffix += characters[qrand() % characters.length()]; const QString tmp = QLatin1String("%1.tmp.%2.%3"); int count = 1; while (QFile::exists(tmp.arg(templ, suffix).arg(count))) ++count; QFile f(tmp.arg(templ, suffix).arg(count)); if (!f.open(QIODevice::WriteOnly)) { throw Error(QCoreApplication::translate("QInstaller", "Cannot open temporary file for template %1: %2").arg(templ, f.errorString())); } f.remove(); return f.fileName(); } #ifdef Q_OS_WIN #include QString QInstaller::getShortPathName(const QString &name) { if (name.isEmpty()) return name; // Determine length, then convert. const LPCTSTR nameC = reinterpret_cast(name.utf16()); // MinGW const DWORD length = GetShortPathName(nameC, NULL, 0); if (length == 0) return name; QScopedArrayPointer buffer(new TCHAR[length]); GetShortPathName(nameC, buffer.data(), length); const QString rc = QString::fromUtf16(reinterpret_cast(buffer.data()), length - 1); return rc; } QString QInstaller::getLongPathName(const QString &name) { if (name.isEmpty()) return name; // Determine length, then convert. const LPCTSTR nameC = reinterpret_cast(name.utf16()); // MinGW const DWORD length = GetLongPathName(nameC, NULL, 0); if (length == 0) return name; QScopedArrayPointer buffer(new TCHAR[length]); GetLongPathName(nameC, buffer.data(), length); const QString rc = QString::fromUtf16(reinterpret_cast(buffer.data()), length - 1); return rc; } QString QInstaller::normalizePathName(const QString &name) { QString canonicalName = getShortPathName(name); if (canonicalName.isEmpty()) return name; canonicalName = getLongPathName(canonicalName); if (canonicalName.isEmpty()) return name; // Upper case drive letter if (canonicalName.size() > 2 && canonicalName.at(1) == QLatin1Char(':')) canonicalName[0] = canonicalName.at(0).toUpper(); return canonicalName; } #pragma pack(push) #pragma pack(2) typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // how many bytes in this resource? DWORD dwImageOffset; // the ID } ICONDIRENTRY; typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource type (1 for icons) WORD idCount; // How many images? ICONDIRENTRY idEntries[1]; // The entries for each image } ICONDIR; typedef struct { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // how many bytes in this resource? WORD nID; // the ID } GRPICONDIRENTRY, *LPGRPICONDIRENTRY; typedef struct { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource type (1 for icons) WORD idCount; // How many images? GRPICONDIRENTRY idEntries[1]; // The entries for each image } GRPICONDIR, *LPGRPICONDIR; #pragma pack(pop) void QInstaller::setApplicationIcon(const QString &application, const QString &icon) { QFile iconFile(icon); if (!iconFile.open(QIODevice::ReadOnly)) { qWarning() << "Cannot use" << icon << "as an application icon:" << iconFile.errorString(); return; } if (QImageReader::imageFormat(icon) != "ico") { qWarning() << "Cannot use" << icon << "as an application icon, unsupported format" << QImageReader::imageFormat(icon).constData(); return; } QByteArray temp = iconFile.readAll(); ICONDIR* ig = reinterpret_cast (temp.data()); DWORD newSize = sizeof(GRPICONDIR) + sizeof(GRPICONDIRENTRY) * (ig->idCount - 1); GRPICONDIR* newDir = reinterpret_cast< GRPICONDIR* >(new char[newSize]); newDir->idReserved = ig->idReserved; newDir->idType = ig->idType; newDir->idCount = ig->idCount; HANDLE updateRes = BeginUpdateResourceW((wchar_t*)QDir::toNativeSeparators(application).utf16(), false); for (int i = 0; i < ig->idCount; ++i) { char* temp1 = temp.data() + ig->idEntries[i].dwImageOffset; DWORD size1 = ig->idEntries[i].dwBytesInRes; newDir->idEntries[i].bWidth = ig->idEntries[i].bWidth; newDir->idEntries[i].bHeight = ig->idEntries[i].bHeight; newDir->idEntries[i].bColorCount = ig->idEntries[i].bColorCount; newDir->idEntries[i].bReserved = ig->idEntries[i].bReserved; newDir->idEntries[i].wPlanes = ig->idEntries[i].wPlanes; newDir->idEntries[i].wBitCount = ig->idEntries[i].wBitCount; newDir->idEntries[i].dwBytesInRes = ig->idEntries[i].dwBytesInRes; newDir->idEntries[i].nID = i + 1; UpdateResourceW(updateRes, RT_ICON, MAKEINTRESOURCE(i + 1), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), temp1, size1); } UpdateResourceW(updateRes, RT_GROUP_ICON, L"IDI_ICON1", MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), newDir, newSize); delete [] newDir; EndUpdateResourceW(updateRes, false); } static quint64 symlinkSizeWin(const QString &path) { WIN32_FILE_ATTRIBUTE_DATA fileAttributeData; if (GetFileAttributesEx((wchar_t*)(path.utf16()), GetFileExInfoStandard, &fileAttributeData) == FALSE) return quint64(0); LARGE_INTEGER size; size.LowPart = fileAttributeData.nFileSizeLow; size.HighPart = fileAttributeData.nFileSizeHigh; return quint64(size.QuadPart); } #endif quint64 QInstaller::fileSize(const QFileInfo &info) { if (!info.isSymLink()) return info.size(); #ifndef Q_OS_WIN struct stat buffer; if (lstat(qPrintable(info.absoluteFilePath()), &buffer) != 0) return quint64(0); return quint64(buffer.st_size); #else return symlinkSizeWin(info.absoluteFilePath()); #endif } bool QInstaller::isInBundle(const QString &path, QString *bundlePath) { #ifdef Q_OS_OSX QFileInfo fi = QFileInfo(path).absoluteFilePath(); while (!fi.isRoot()) { if (fi.isBundle()) { if (bundlePath) *bundlePath = fi.absoluteFilePath(); return true; } fi.setFile(fi.path()); } #else Q_UNUSED(path) Q_UNUSED(bundlePath) #endif return false; } /*! Replaces the path \a before with the path \a after at the beginning of \a path and returns the replaced path. If \a before cannot be found in \a path, the original value is returned. */ QString QInstaller::replacePath(const QString &path, const QString &before, const QString &after) { if (path.isEmpty() || before.isEmpty()) return path; QString pathToPatch = QDir::cleanPath(path); const QString pathToReplace = QDir::cleanPath(before); if (pathToPatch.startsWith(pathToReplace)) return QDir::cleanPath(after) + pathToPatch.mid(pathToReplace.size()); return path; } src/libs/installer/fileutils.h000066400000000000000000000104221325366651500167570ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QINSTALLER_FILEUTILS_H #define QINSTALLER_FILEUTILS_H #include "installer_global.h" #include #include #include QT_BEGIN_NAMESPACE class QFileInfo; class QUrl; QT_END_NAMESPACE namespace QInstaller { class INSTALLER_EXPORT TempDirDeleter { public: explicit TempDirDeleter(const QString &path); explicit TempDirDeleter(const QStringList &paths = QStringList()); ~TempDirDeleter(); QStringList paths() const; void add(const QString &path); void add(const QStringList &paths); void releaseAll(); void release(const QString &path); void passAndReleaseAll(TempDirDeleter &tdd); void passAndRelease(TempDirDeleter &tdd, const QString &path); void releaseAndDeleteAll(); void releaseAndDelete(const QString &path); private: Q_DISABLE_COPY(TempDirDeleter) QSet m_paths; }; QString INSTALLER_EXPORT humanReadableSize(const qint64 &size, int precision = 2); /*! Removes the directory at \a path recursively. @param path The directory to remove @param ignoreErrors if @p true, errors will be silently ignored. Otherwise an exception will be thrown if removing fails. @throws QInstaller::Error if the directory cannot be removed and ignoreErrors is @p false */ void INSTALLER_EXPORT removeFiles(const QString &path, bool ignoreErrors = false); void INSTALLER_EXPORT removeDirectory(const QString &path, bool ignoreErrors = false); void INSTALLER_EXPORT removeDirectoryThreaded(const QString &path, bool ignoreErrors = false); void INSTALLER_EXPORT removeSystemGeneratedFiles(const QString &path); QString INSTALLER_EXPORT generateTemporaryFileName(const QString &templ=QString()); void INSTALLER_EXPORT moveDirectoryContents(const QString &sourceDir, const QString &targetDir); void INSTALLER_EXPORT copyDirectoryContents(const QString &sourceDir, const QString &targetDir); bool INSTALLER_EXPORT isLocalUrl(const QUrl &url); QString INSTALLER_EXPORT pathFromUrl(const QUrl &url); void INSTALLER_EXPORT mkdir(const QString &path); void INSTALLER_EXPORT mkpath(const QString &path); quint64 INSTALLER_EXPORT fileSize(const QFileInfo &info); bool INSTALLER_EXPORT isInBundle(const QString &path, QString *bundlePath = 0); QString replacePath(const QString &path, const QString &pathBefore, const QString &pathAfter); #ifdef Q_OS_WIN QString INSTALLER_EXPORT getLongPathName(const QString &name); QString INSTALLER_EXPORT getShortPathName(const QString &name); /*! Makes sure that capitalization of directories is canonical. */ QString INSTALLER_EXPORT normalizePathName(const QString &name); /*! Sets the .ico file at \a icon as application icon for \a application. */ void INSTALLER_EXPORT setApplicationIcon(const QString &application, const QString &icon); #endif } #endif // QINSTALLER_FILEUTILS_H src/libs/installer/globals.cpp000066400000000000000000000043061325366651500167410ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "globals.h" const char IFW_COMPONENT_CHECKER[] = "ifw.componentChecker"; const char IFW_RESOURCES[] = "ifw.resources"; const char IFW_TRANSLATIONS[] = "ifw.translations"; const char IFW_NETWORK[] = "ifw.network"; namespace QInstaller { Q_LOGGING_CATEGORY(lcComponentChecker, IFW_COMPONENT_CHECKER) Q_LOGGING_CATEGORY(lcResources, IFW_RESOURCES) Q_LOGGING_CATEGORY(lcTranslations, IFW_TRANSLATIONS) Q_LOGGING_CATEGORY(lcNetwork, IFW_NETWORK) QStringList loggingCategories() { static QStringList categories = QStringList() << QLatin1String(IFW_COMPONENT_CHECKER) << QLatin1String(IFW_RESOURCES) << QLatin1String(IFW_TRANSLATIONS) << QLatin1String(IFW_NETWORK); return categories; } Q_GLOBAL_STATIC_WITH_ARGS(QRegExp, staticCommaRegExp, (QLatin1String("\\b(,|, )\\b"))); QRegExp commaRegExp() { return *staticCommaRegExp(); } } // namespace QInstaller src/libs/installer/globals.h000066400000000000000000000034331325366651500164060ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef GLOBALS_H #define GLOBALS_H #include "installer_global.h" #include #include namespace QInstaller { INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcComponentChecker) INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcResources) INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcTranslations) INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcNetwork) QStringList INSTALLER_EXPORT loggingCategories(); QRegExp INSTALLER_EXPORT commaRegExp(); } // QInstaller #endif // GLOBALS_H src/libs/installer/globalsettingsoperation.cpp000066400000000000000000000101261325366651500222550ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "globalsettingsoperation.h" #include "qsettingswrapper.h" using namespace QInstaller; GlobalSettingsOperation::GlobalSettingsOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("GlobalConfig")); } void GlobalSettingsOperation::backup() { } bool GlobalSettingsOperation::performOperation() { QString key, value; QScopedPointer settings(setup(&key, &value, arguments())); if (settings.isNull()) return false; if (!settings->isWritable()) { setError(UserDefinedError); setErrorString(tr("Settings are not writable.")); return false; } const QVariant oldValue = settings->value(key); settings->setValue(key, value); settings->sync(); if (settings->status() != QSettingsWrapper::NoError) { setError(UserDefinedError); setErrorString(tr("Failed to write settings.")); return false; } setValue(QLatin1String("oldvalue"), oldValue); return true; } bool GlobalSettingsOperation::undoOperation() { QString key, val; QScopedPointer settings(setup(&key, &val, arguments())); if (settings.isNull()) return false; // be sure it's still our value and nobody changed it in between const QVariant oldValue = value(QLatin1String("oldvalue")); if (settings->value(key) == val) { // restore the previous state if (oldValue.isNull()) settings->remove(key); else settings->setValue(key, oldValue); } return true; } bool GlobalSettingsOperation::testOperation() { return true; } QSettingsWrapper *GlobalSettingsOperation::setup(QString *key, QString *value, const QStringList &arguments) { if (!checkArgumentCount(3, 5)) return 0; if (arguments.count() == 5) { QSettingsWrapper::Scope scope = QSettingsWrapper::UserScope; if (arguments.at(0) == QLatin1String("SystemScope")) scope = QSettingsWrapper::SystemScope; const QString &company = arguments.at(1); const QString &application = arguments.at(2); *key = arguments.at(3); *value = arguments.at(4); return new QSettingsWrapper(scope, company, application); } else if (arguments.count() == 4) { const QString &company = arguments.at(0); const QString &application = arguments.at(1); *key = arguments.at(2); *value = arguments.at(3); return new QSettingsWrapper(company, application); } else if (arguments.count() == 3) { const QString &filename = arguments.at(0); *key = arguments.at(1); *value = arguments.at(2); return new QSettingsWrapper(filename, QSettingsWrapper::NativeFormat); } return 0; } src/libs/installer/globalsettingsoperation.h000066400000000000000000000036121325366651500217240ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef GLOBALSETTINGSOPERATION_H #define GLOBALSETTINGSOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class QSettingsWrapper; class INSTALLER_EXPORT GlobalSettingsOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::GlobalSettingsOperation) public: explicit GlobalSettingsOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); private: QSettingsWrapper *setup(QString *key, QString *value, const QStringList &args); }; } // namespace QInstaller #endif // GLOBALSETTINGSOPERATION_H src/libs/installer/graph.h000066400000000000000000000074511325366651500160700ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef GRAPH_H #define GRAPH_H #include #include #include #include namespace QInstaller { template class Graph { public: inline Graph() {} explicit Graph(const QList &nodes) { addNodes(nodes); } const QList nodes() const { return m_graph.keys(); } void addNode(const T &node) { m_graph.insert(node, QSet()); } void addNodes(const QList &nodes) { foreach (const T &node, nodes) addNode(node); } QList edges(const T &node) const { return m_graph.value(node).toList(); } void addEdge(const T &node, const T &edge) { m_graph[node].insert(edge); } void addEdges(const T &node, const QList &edges) { foreach (const T &edge, edges) addEdge(node, edge); } bool hasCycle() const { return m_hasCycle; } QPair cycle() const { return m_cycle; } QList sort() const { QSet visitedNodes; QList resolvedNodes; m_hasCycle = false; m_cycle = qMakePair(T(), T()); foreach (const T &node, nodes()) visit(node, &resolvedNodes, &visitedNodes); return resolvedNodes; } QList sortReverse() const { QList result = sort(); std::reverse(result.begin(), result.end()); return result; } private: void visit(const T &node, QList *const resolvedNodes, QSet *const visitedNodes) const { if (m_hasCycle) return; // if we visited this node already if (visitedNodes->contains(node)) { // and if the node is already in the ordered list if (resolvedNodes->contains(node)) return; m_hasCycle = true; m_cycle.second = node; return; // if not yet in the ordered list, we detected a cycle } // mark this node as visited visitedNodes->insert(node); m_cycle.first = node; // recursively visit all adjacency foreach (const T &adjacency, edges(node)) visit(adjacency, resolvedNodes, visitedNodes); // append this node to the ordered list resolvedNodes->append(node); } private: mutable bool m_hasCycle; QHash > m_graph; mutable QPair m_cycle; }; } #endif // GRAPH_H src/libs/installer/init.cpp000066400000000000000000000147761325366651500162750ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "init.h" #include "createshortcutoperation.h" #include "createdesktopentryoperation.h" #include "createlocalrepositoryoperation.h" #include "extractarchiveoperation.h" #include "globalsettingsoperation.h" #include "environmentvariablesoperation.h" #include "registerfiletypeoperation.h" #include "selfrestartoperation.h" #include "installiconsoperation.h" #include "elevatedexecuteoperation.h" #include "fakestopprocessforupdateoperation.h" #include "createlinkoperation.h" #include "simplemovefileoperation.h" #include "copydirectoryoperation.h" #include "replaceoperation.h" #include "linereplaceoperation.h" #include "minimumprogressoperation.h" #include "licenseoperation.h" #include "settingsoperation.h" #include "consumeoutputoperation.h" #include "lib7z_facade.h" #include "utils.h" #include "updateoperationfactory.h" #include "filedownloaderfactory.h" #include #include #include using namespace KDUpdater; using namespace QInstaller; #if defined(QT_STATIC) static void initResources() { Q_INIT_RESOURCE(installer); } #endif static QString trimAndPrepend(QtMsgType type, const QString &msg) { QString ba(msg); // last character is a space from qDebug if (ba.endsWith(QLatin1Char(' '))) ba.chop(1); // remove quotes if the whole message is surrounded with them if (ba.startsWith(QLatin1Char('"')) && ba.endsWith(QLatin1Char('"'))) ba = ba.mid(1, ba.length() - 2); // prepend the message type, skip QtDebugMsg switch (type) { case QtWarningMsg: ba.prepend(QStringLiteral("Warning: ")); break; case QtCriticalMsg: ba.prepend(QStringLiteral("Critical: ")); break; case QtFatalMsg: ba.prepend(QStringLiteral("Fatal: ")); break; default: break; } return ba; } // start timer on construction (so we can use it as static member) class Uptime : public QElapsedTimer { public: Uptime() { start(); } }; void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { // suppress warning from QPA minimal plugin if (msg.contains(QLatin1String("This plugin does not support propagateSizeHints"))) return; static Uptime uptime; QString ba = QLatin1Char('[') + QString::number(uptime.elapsed()) + QStringLiteral("] ") + trimAndPrepend(type, msg); if (type != QtDebugMsg && context.file) { ba += QString(QStringLiteral(" (%1:%2, %3)")).arg( QString::fromLatin1(context.file)).arg(context.line).arg( QString::fromLatin1(context.function)); } if (VerboseWriter *log = VerboseWriter::instance()) log->appendLine(ba); if (type != QtDebugMsg || isVerbose()) std::cout << qPrintable(ba) << std::endl; if (type == QtFatalMsg) { QtMessageHandler oldMsgHandler = qInstallMessageHandler(0); qt_message_output(type, context, msg); qInstallMessageHandler(oldMsgHandler); } } void QInstaller::init() { Lib7z::initSevenZ(); #if defined(QT_STATIC) ::initResources(); #endif UpdateOperationFactory &factory = UpdateOperationFactory::instance(); factory.registerUpdateOperation(QLatin1String("CreateShortcut")); factory.registerUpdateOperation(QLatin1String("CreateDesktopEntry")); factory.registerUpdateOperation(QLatin1String("CreateLocalRepository")); factory.registerUpdateOperation(QLatin1String("Extract")); factory.registerUpdateOperation(QLatin1String("GlobalConfig")); factory.registerUpdateOperation(QLatin1String("EnvironmentVariable")); factory.registerUpdateOperation(QLatin1String("RegisterFileType")); factory.registerUpdateOperation(QLatin1String("SelfRestart")); factory.registerUpdateOperation(QLatin1String("InstallIcons")); factory.registerUpdateOperation(QLatin1String("Execute")); factory.registerUpdateOperation(QLatin1String("FakeStopProcessForUpdate")); factory.registerUpdateOperation(QLatin1String("CreateLink")); factory.registerUpdateOperation(QLatin1String("SimpleMoveFile")); factory.registerUpdateOperation(QLatin1String("CopyDirectory")); factory.registerUpdateOperation(QLatin1String("Replace")); factory.registerUpdateOperation(QLatin1String("LineReplace")); factory.registerUpdateOperation(QLatin1String("MinimumProgress")); factory.registerUpdateOperation(QLatin1String("License")); factory.registerUpdateOperation(QLatin1String("ConsumeOutput")); factory.registerUpdateOperation(QLatin1String("Settings")); FileDownloaderFactory::setFollowRedirects(true); qInstallMessageHandler(messageHandler); } src/libs/installer/init.h000066400000000000000000000027001325366651500157220ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QINSTALLER_INIT_H #define QINSTALLER_INIT_H #include "installer_global.h" namespace QInstaller { void INSTALLER_EXPORT init(); } #endif // QINSTALLER_INIT_H src/libs/installer/installer.pro000066400000000000000000000140421325366651500173270ustar00rootroot00000000000000TEMPLATE = lib TARGET = installer INCLUDEPATH += . .. CONFIG += staticlib include(../7zip/7zip.pri) include(../kdtools/kdtools.pri) include(../../../installerfw.pri) # productkeycheck API # call qmake "PRODUCTKEYCHECK_PRI_FILE=" # content of that pri file needs to be: # SOURCES += $$PWD/productkeycheck.cpp # ... # your files if needed HEADERS += productkeycheck.h !isEmpty(PRODUCTKEYCHECK_PRI_FILE) { # use undocumented no_batch config which disable the implicit rules on msvc compilers # this fixes the problem that same cpp files in different directories are overwritting # each other CONFIG += no_batch include($$PRODUCTKEYCHECK_PRI_FILE) } else { SOURCES += productkeycheck.cpp } DESTDIR = $$IFW_LIB_PATH DLLDESTDIR = $$IFW_APP_PATH DEFINES += BUILD_LIB_INSTALLER QT += \ qml \ network \ xml \ concurrent \ widgets \ core-private \ qml-private win32:QT += winextras HEADERS += packagemanagercore.h \ packagemanagercore_p.h \ packagemanagergui.h \ binaryformat.h \ binaryformatengine.h \ binaryformatenginehandler.h \ repository.h \ utils.h \ errors.h \ component.h \ scriptengine.h \ componentmodel.h \ qinstallerglobal.h \ qtpatch.h \ consumeoutputoperation.h \ replaceoperation.h \ linereplaceoperation.h \ copydirectoryoperation.h \ simplemovefileoperation.h \ extractarchiveoperation.h \ extractarchiveoperation_p.h \ globalsettingsoperation.h \ createshortcutoperation.h \ createdesktopentryoperation.h \ registerfiletypeoperation.h \ environmentvariablesoperation.h \ installiconsoperation.h \ selfrestartoperation.h \ settings.h \ permissionsettings.h \ downloadarchivesjob.h \ init.h \ adminauthorization.h \ elevatedexecuteoperation.h \ fakestopprocessforupdateoperation.h \ lazyplaintextedit.h \ progresscoordinator.h \ minimumprogressoperation.h \ performinstallationform.h \ messageboxhandler.h \ licenseoperation.h \ component_p.h \ qprocesswrapper.h \ qsettingswrapper.h \ constants.h \ packagemanagerproxyfactory.h \ createlocalrepositoryoperation.h \ lib7z_facade.h \ link.h \ createlinkoperation.h \ packagemanagercoredata.h \ globals.h \ graph.h \ settingsoperation.h \ testrepository.h \ packagemanagerpagefactory.h \ abstracttask.h\ abstractfiletask.h \ copyfiletask.h \ downloadfiletask.h \ downloadfiletask_p.h \ unziptask.h \ observer.h \ runextensions.h \ metadatajob.h \ metadatajob_p.h \ installer_global.h \ scriptengine_p.h \ protocol.h \ remoteobject.h \ remoteclient.h \ remoteserver.h \ remoteclient_p.h \ remoteserver_p.h \ remotefileengine.h \ remoteserverconnection.h \ remoteserverconnection_p.h \ fileio.h \ binarycontent.h \ binarylayout.h \ installercalculator.h \ uninstallercalculator.h \ componentchecker.h \ proxycredentialsdialog.h \ serverauthenticationdialog.h \ keepaliveobject.h \ systeminfo.h \ packagesource.h \ lib7z_guid.h \ lib7z_create.h \ lib7z_extract.h \ lib7z_list.h SOURCES += packagemanagercore.cpp \ packagemanagercore_p.cpp \ packagemanagergui.cpp \ binaryformat.cpp \ binaryformatengine.cpp \ binaryformatenginehandler.cpp \ repository.cpp \ fileutils.cpp \ utils.cpp \ component.cpp \ scriptengine.cpp \ componentmodel.cpp \ qtpatch.cpp \ consumeoutputoperation.cpp \ replaceoperation.cpp \ linereplaceoperation.cpp \ copydirectoryoperation.cpp \ simplemovefileoperation.cpp \ extractarchiveoperation.cpp \ globalsettingsoperation.cpp \ createshortcutoperation.cpp \ createdesktopentryoperation.cpp \ registerfiletypeoperation.cpp \ environmentvariablesoperation.cpp \ installiconsoperation.cpp \ selfrestartoperation.cpp \ downloadarchivesjob.cpp \ init.cpp \ elevatedexecuteoperation.cpp \ fakestopprocessforupdateoperation.cpp \ lazyplaintextedit.cpp \ progresscoordinator.cpp \ minimumprogressoperation.cpp \ performinstallationform.cpp \ messageboxhandler.cpp \ licenseoperation.cpp \ component_p.cpp \ qprocesswrapper.cpp \ qsettingswrapper.cpp \ settings.cpp \ permissionsettings.cpp \ packagemanagerproxyfactory.cpp \ createlocalrepositoryoperation.cpp \ lib7z_facade.cpp \ link.cpp \ createlinkoperation.cpp \ packagemanagercoredata.cpp \ globals.cpp \ settingsoperation.cpp \ testrepository.cpp \ packagemanagerpagefactory.cpp \ abstractfiletask.cpp \ copyfiletask.cpp \ downloadfiletask.cpp \ unziptask.cpp \ observer.cpp \ metadatajob.cpp \ protocol.cpp \ remoteobject.cpp \ remoteclient.cpp \ remoteserver.cpp \ remotefileengine.cpp \ remoteserverconnection.cpp \ fileio.cpp \ binarycontent.cpp \ binarylayout.cpp \ installercalculator.cpp \ uninstallercalculator.cpp \ componentchecker.cpp \ proxycredentialsdialog.cpp \ serverauthenticationdialog.cpp \ keepaliveobject.cpp \ systeminfo.cpp \ packagesource.cpp FORMS += proxycredentialsdialog.ui \ serverauthenticationdialog.ui RESOURCES += resources/installer.qrc unix { osx: SOURCES += adminauthorization_mac.cpp else: SOURCES += adminauthorization_x11.cpp } LIBS += -l7z win32 { SOURCES += adminauthorization_win.cpp sysinfo_win.cpp LIBS += -loleaut32 -luser32 # 7zip LIBS += -ladvapi32 -lpsapi # kdtools LIBS += -lole32 -lshell32 # createshortcutoperation win32-g++*:LIBS += -lmpr -luuid win32-g++*:QMAKE_CXXFLAGS += -Wno-missing-field-initializers } target.path = $$[QT_INSTALL_LIBS] INSTALLS += target src/libs/installer/installer_global.h000066400000000000000000000031061325366651500202750ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef INSTALLER_GLOBAL_H #define INSTALLER_GLOBAL_H #include #ifndef QT_STATIC # ifdef BUILD_LIB_INSTALLER # define INSTALLER_EXPORT Q_DECL_EXPORT # else # define INSTALLER_EXPORT Q_DECL_IMPORT # endif #else # define INSTALLER_EXPORT #endif #endif //INSTALLER_GLOBAL_H src/libs/installer/installercalculator.cpp000066400000000000000000000233211325366651500213630ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "installercalculator.h" #include "component.h" #include "packagemanagercore.h" #include namespace QInstaller { InstallerCalculator::InstallerCalculator(const QList &allComponents) : m_allComponents(allComponents) { } void InstallerCalculator::insertInstallReason(Component *component, InstallReasonType installReason, const QString &referencedComponentName) { // keep the first reason if (m_toInstallComponentIdReasonHash.contains(component->name())) return; m_toInstallComponentIdReasonHash.insert(component->name(), qMakePair(installReason, referencedComponentName)); } InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(Component *c) const { return m_toInstallComponentIdReasonHash.value(c->name(), qMakePair(InstallerCalculator::Selected, QString())).first; } QString InstallerCalculator::installReasonReferencedComponent(Component *component) const { return m_toInstallComponentIdReasonHash.value(component->name(), qMakePair(InstallerCalculator::Selected, QString())).second; } QString InstallerCalculator::installReason(Component *component) const { InstallerCalculator::InstallReasonType reason = installReasonType(component); switch (reason) { case Automatic: return QCoreApplication::translate("InstallerCalculator", "Components added as automatic dependencies:"); case Dependent: return QCoreApplication::translate("InstallerCalculator", "Components added as " "dependency for \"%1\":").arg(installReasonReferencedComponent(component)); case Resolved: return QCoreApplication::translate("InstallerCalculator", "Components that have resolved dependencies:"); case Selected: return QCoreApplication::translate("InstallerCalculator", "Selected components without dependencies:"); } return QString(); } QList InstallerCalculator::orderedComponentsToInstall() const { return m_orderedComponentsToInstall; } QString InstallerCalculator::componentsToInstallError() const { return m_componentsToInstallError; } void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version) { if (!component->isInstalled(version) || component->updateRequested()) { m_orderedComponentsToInstall.append(component); m_toInstallComponentIds.insert(component->name()); } } QString InstallerCalculator::recursionError(Component *component) { return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" " "already added with reason: \"%2\"").arg(component->name(), installReason(component)); } bool InstallerCalculator::appendComponentsToInstall(const QList &components) { if (components.isEmpty()) return true; QList notAppendedComponents; // for example components with unresolved dependencies foreach (Component *component, components){ if (m_toInstallComponentIds.contains(component->name())) { const QString errorMessage = recursionError(component); qWarning().noquote() << errorMessage; m_componentsToInstallError.append(errorMessage); Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO, qPrintable(errorMessage)); return false; } if (component->dependencies().isEmpty()) realAppendToInstallComponents(component); else notAppendedComponents.append(component); } foreach (Component *component, notAppendedComponents) { if (!appendComponentToInstall(component)) return false; } QList foundAutoDependOnList; // All regular dependencies are resolved. Now we are looking for auto depend on components. foreach (Component *component, m_allComponents) { // If a components is already installed or is scheduled for installation, no need to check // for auto depend installation. if ((!component->isInstalled() || component->updateRequested()) && !m_toInstallComponentIds.contains(component->name())) { // If we figure out a component requests auto installation, keep it to resolve // their dependencies as well. if (component->isAutoDependOn(m_toInstallComponentIds)) { foundAutoDependOnList.append(component); insertInstallReason(component, InstallerCalculator::Automatic); } } } if (!foundAutoDependOnList.isEmpty()) return appendComponentsToInstall(foundAutoDependOnList); return true; } bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version) { QSet allDependencies = component->dependencies().toSet(); QString requiredDependencyVersion = version; foreach (const QString &dependencyComponentName, allDependencies) { // PackageManagerCore::componentByName returns 0 if dependencyComponentName contains a // version which is not available Component *dependencyComponent = PackageManagerCore::componentByName(dependencyComponentName, m_allComponents); if (!dependencyComponent) { const QString errorMessage = QCoreApplication::translate("InstallerCalculator", "Cannot find missing dependency \"%1\" for \"%2\".").arg(dependencyComponentName, component->name()); qWarning().noquote() << errorMessage; m_componentsToInstallError.append(errorMessage); return false; } //Check if component requires higher version than what might be already installed bool isUpdateRequired = false; if (dependencyComponentName.contains(QChar::fromLatin1('-')) && !dependencyComponent->value(scInstalledVersion).isEmpty()) { QRegExp compEx(QLatin1String("([<=>]+)(.*)")); const QString installedVersion = compEx.exactMatch(dependencyComponent->value(scInstalledVersion)) ? compEx.cap(2) : dependencyComponent->value(scInstalledVersion); QString requiredVersion = dependencyComponentName.section(QLatin1Char('-'), 1); requiredVersion = compEx.exactMatch(requiredVersion) ? compEx.cap(2) : requiredVersion; if (KDUpdater::compareVersion(requiredVersion, installedVersion) >= 1 ) { isUpdateRequired = true; requiredDependencyVersion = requiredVersion; } } //Check dependencies only if //- Dependency is not installed or update requested, nor newer version of dependency component required //- And dependency component is not already added for install //- And component is not already added for install, then dependencies are already resolved if (((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested()) || isUpdateRequired) && (!m_toInstallComponentIds.contains(dependencyComponent->name()) && !m_toInstallComponentIds.contains(component->name()))) { if (m_visitedComponents.value(component).contains(dependencyComponent)) { const QString errorMessage = recursionError(component); qWarning().noquote() << errorMessage; m_componentsToInstallError = errorMessage; Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent), Q_FUNC_INFO, qPrintable(errorMessage)); return false; } m_visitedComponents[component].insert(dependencyComponent); // add needed dependency components to the next run insertInstallReason(dependencyComponent, InstallerCalculator::Dependent, component->name()); if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion)) return false; } } if (!m_toInstallComponentIds.contains(component->name())) { realAppendToInstallComponents(component, requiredDependencyVersion); insertInstallReason(component, InstallerCalculator::Resolved); } return true; } } // namespace QInstaller src/libs/installer/installercalculator.h000066400000000000000000000064301325366651500210320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef INSTALLERCALCULATOR_H #define INSTALLERCALCULATOR_H #include "installer_global.h" #include #include #include #include namespace QInstaller { class Component; class INSTALLER_EXPORT InstallerCalculator { public: InstallerCalculator(const QList &allComponents); enum InstallReasonType { Automatic, // "Component(s) added as automatic dependencies" Dependent, // "Added as dependency for %1." Resolved, // "Component(s) that have resolved Dependencies" Selected // "Selected Component(s) without Dependencies" }; InstallReasonType installReasonType(Component *component) const; QString installReasonReferencedComponent(Component *component) const; QString installReason(Component *component) const; QList orderedComponentsToInstall() const; QString componentsToInstallError() const; bool appendComponentsToInstall(const QList &components); private: void insertInstallReason(Component *component, InstallReasonType installReasonType, const QString &referencedComponentName = QString()); void realAppendToInstallComponents(Component *component, const QString &version = QString()); bool appendComponentToInstall(Component *components, const QString &version = QString()); QString recursionError(Component *component); QList m_allComponents; QHash > m_visitedComponents; QSet m_toInstallComponentIds; //for faster lookups QString m_componentsToInstallError; //calculate installation order variables QList m_orderedComponentsToInstall; //we can't use this reason hash as component id hash, because some reasons are ready before //the component is added QHash > m_toInstallComponentIdReasonHash; }; } #endif // INSTALLERCALCULATOR_H src/libs/installer/installiconsoperation.cpp000066400000000000000000000253751325366651500217520ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "installiconsoperation.h" #include "fileutils.h" #include "packagemanagercore.h" #include #include #include #include using namespace QInstaller; QString InstallIconsOperation::targetDirectory() { // we're not searching for the first time, let's re-use the old value if (hasValue(QLatin1String("targetdirectory"))) return value(QLatin1String("targetdirectory")).toString(); const QProcessEnvironment env; QStringList XDG_DATA_DIRS = env.value(QLatin1String("XDG_DATA_DIRS")).split(QLatin1Char(':'), QString::SkipEmptyParts); XDG_DATA_DIRS.push_back(QLatin1String("/usr/share/pixmaps")); // default path XDG_DATA_DIRS.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share/icons"))); // default path XDG_DATA_DIRS.push_back(QDir::home().absoluteFilePath(QLatin1String(".icons"))); // default path QString directory; const QStringList& directories = XDG_DATA_DIRS; for (QStringList::const_iterator it = directories.begin(); it != directories.end(); ++it) { if (it->isEmpty()) continue; // our default dirs are correct, XDG_DATA_DIRS set via env need "icon" at the end if ((it + 1 == directories.end()) || (it + 2 == directories.end()) || (it + 3 == directories.end())) directory = QDir(*it).absolutePath(); else directory = QDir(*it).absoluteFilePath(QLatin1String("icons")); QDir dir(directory); // let's see if this dir exists or we're able to create it if (!dir.exists() && !QDir().mkpath(directory)) continue; // we just try if we're able to open the file in ReadWrite QFile file(QDir(directory).absoluteFilePath(QLatin1String("tmpfile"))); const bool existed = file.exists(); if (!file.open(QIODevice::ReadWrite)) continue; file.close(); if (!existed) file.remove(); break; } if (!QDir(directory).exists()) QDir().mkpath(directory); setValue(QLatin1String("directory"), directory); return directory; } InstallIconsOperation::InstallIconsOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("InstallIcons")); } InstallIconsOperation::~InstallIconsOperation() { const QStringList backupFiles = value(QLatin1String("backupfiles")).toStringList(); } void InstallIconsOperation::backup() { // we backup on the fly } bool InstallIconsOperation::performOperation() { if (!checkArgumentCount(1, 2, tr(" [vendor prefix]"))) return false; const QStringList args = arguments(); const QString source = args.at(0); const QString vendor = args.value(1); // value() used since it's optional if (source.isEmpty()) { setError(InvalidArguments); setErrorString(tr("Invalid Argument: source directory must not be empty.")); return false; } const QDir sourceDir = QDir(source); const QDir targetDir = QDir(targetDirectory()); QStringList files; QStringList backupFiles; QStringList createdDirectories; PackageManagerCore *const core = packageManager(); // iterate a second time to get the actual work done QDirIterator it(sourceDir.path(), QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it.hasNext()) { const int status = core->status(); if (status == PackageManagerCore::Canceled || status == PackageManagerCore::Failure) return true; const QString source = it.next(); QString target = targetDir.absoluteFilePath(sourceDir.relativeFilePath(source)); emit outputTextChanged(target); const QFileInfo fi = it.fileInfo(); if (!fi.isDir()) { // exchange prefix with vendor if vendor is set if (!vendor.isEmpty()) { // path of the target file const QString targetPath = QFileInfo(target).absolutePath() + QLatin1String("/"); // filename with replaced vendor string const QString targetFile = vendor + fi.baseName().section(QLatin1Char('-'), 1, -1, QString::SectionIncludeLeadingSep) + QLatin1String(".") + fi.completeSuffix(); // target is the file with full path and replaced vendor string target = targetPath + targetFile; } if (QFile(target).exists()) { // first backup... const QString backup = generateTemporaryFileName(target); QFile bf(target); if (!bf.copy(backup)) { setError(UserDefinedError); setErrorString(tr("Cannot backup file \"%1\": %2").arg( QDir::toNativeSeparators(target), bf.errorString())); undoOperation(); return false; } backupFiles.push_back(target); backupFiles.push_back(backup); setValue(QLatin1String("backupfiles"), backupFiles); // then delete it QString errStr; if (!deleteFileNowOrLater(target, &errStr)) { setError(UserDefinedError); setErrorString(tr("Failed to overwrite \"%1\": %2").arg( QDir::toNativeSeparators(target), errStr)); undoOperation(); return false; } } // copy the file to its new location QFile cf(source); if (!cf.copy(target)) { setError(UserDefinedError); setErrorString(tr("Failed to copy file \"%1\": %2").arg( QDir::toNativeSeparators(target), cf.errorString())); undoOperation(); return false; } deleteFileNowOrLater(source); files.push_back(source); files.push_back(target); setValue(QLatin1String("files"), files); } else if (fi.isDir() && !QDir(target).exists()) { if (!QDir().mkpath(target)) { setErrorString(tr("Cannot create directory \"%1\": %2").arg( QDir::toNativeSeparators(target), qt_error_string())); undoOperation(); return false; } createdDirectories.push_front(target); setValue(QLatin1String("createddirectories"), createdDirectories); } } // this should work now if not, it's not _that_ problematic... try { removeDirectory(source); } catch(...) { } return true; } bool InstallIconsOperation::undoOperation() { QStringList warningMessages; // first copy back all files to their origin const QStringList files = value(QLatin1String("files")).toStringList(); for (QStringList::const_iterator it = files.begin(); it != files.end(); it += 2) { const QString& source = *it; const QString& target = *(it + 1); // first make sure the "source" path is valid QDir().mkpath(QFileInfo(source).absolutePath()); QFile installedTarget(target); if (installedTarget.exists() && !(installedTarget.copy(source) && installedTarget.remove())) { warningMessages << QString::fromLatin1("Cannot move file from \"%1\" to \"%2\": %3)").arg( target, source, installedTarget.errorString()); } } // then copy back and remove all backuped files const QStringList backupFiles = value(QLatin1String("backupfiles")).toStringList(); for (QStringList::const_iterator it = backupFiles.begin(); it != backupFiles.end(); it += 2) { const QString& target = *it; const QString& backup = *(it + 1); // remove the target if (QFile::exists(target)) deleteFileNowOrLater(target); // then copy the backup onto the target if (!QFile::copy(backup, target)) { warningMessages << QString::fromLatin1("Cannot restore the backup \"%1\" to \"%2\".").arg( backup, target); } // finally remove the backp if (!deleteFileNowOrLater(backup)) warningMessages << QString::fromLatin1("Cannot remove the backup \"%1\".").arg(backup); } // then remove all directories created by us const QStringList createdDirectories = value(QLatin1String("createddirectories")).toStringList(); for (QStringList::const_iterator it = createdDirectories.begin(); it != createdDirectories.end(); ++it) { const QDir dir(*it); removeSystemGeneratedFiles(dir.absolutePath()); if (dir.exists() && !QDir::root().rmdir(dir.path())) warningMessages << QString::fromLatin1("Cannot remove directory \"%1\".").arg(dir.path()); } if (!warningMessages.isEmpty()) { qWarning() << "Undo of operation" << name() << "with arguments" << arguments().join(QLatin1String(", ")) << "had some problems."; foreach (const QString &message, warningMessages) { qWarning().noquote() << message; } } return true; } bool InstallIconsOperation::testOperation() { return true; } src/libs/installer/installiconsoperation.h000066400000000000000000000035341325366651500214100ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef INSTALLICONSOPERATION_H #define INSTALLICONSOPERATION_H #include "qinstallerglobal.h" #include namespace QInstaller { class INSTALLER_EXPORT InstallIconsOperation : public QObject, public Operation { Q_OBJECT public: explicit InstallIconsOperation(PackageManagerCore *core); ~InstallIconsOperation(); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); Q_SIGNALS: void outputTextChanged(const QString &progress); private: QString targetDirectory(); }; } #endif src/libs/installer/keepaliveobject.cpp000066400000000000000000000037461325366651500204610ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "keepaliveobject.h" #include "remoteclient.h" #include #include namespace QInstaller { KeepAliveObject::KeepAliveObject() : m_timer(0) , m_socket(0) { } void KeepAliveObject::start() { m_timer = new QTimer(this); m_socket = new QLocalSocket(this); connect(m_timer, &QTimer::timeout, [this]() { if (m_socket->state() != QLocalSocket::UnconnectedState) return; m_socket->connectToServer(RemoteClient::instance().socketName()); }); connect(m_socket, &QLocalSocket::connected, [this]() { m_socket->close(); }); m_timer->setInterval(5000); m_timer->start(); } } // namespace QInstaller src/libs/installer/keepaliveobject.h000066400000000000000000000033421325366651500201160ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef KEEPALIVEOBJECT_H #define KEEPALIVEOBJECT_H #include QT_BEGIN_NAMESPACE class QTimer; class QLocalSocket; QT_END_NAMESPACE namespace QInstaller { class KeepAliveObject : public QObject { Q_OBJECT Q_DISABLE_COPY(KeepAliveObject) public: KeepAliveObject(); public slots: void start(); private: QTimer *m_timer; QLocalSocket *m_socket; }; } // namespace QInstaller #endif // KEEPALIVEOBJECT_H src/libs/installer/lazyplaintextedit.cpp000066400000000000000000000063261325366651500211000ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "lazyplaintextedit.h" #include const int INTERVAL = 20; LazyPlainTextEdit::LazyPlainTextEdit(QWidget *parent) : QPlainTextEdit(parent) , m_timerId(0) { } void LazyPlainTextEdit::timerEvent(QTimerEvent *event) { if (event->timerId() == m_timerId) { killTimer(m_timerId); m_timerId = 0; m_cachedOutput.chop(1); //removes the last \n if (!m_cachedOutput.isEmpty()) { appendPlainText(m_cachedOutput); updateCursor(TextCursorPosition::Keep); horizontalScrollBar()->setValue(0); m_cachedOutput.clear(); } } } void LazyPlainTextEdit::append(const QString &text) { m_cachedOutput.append(text + QLatin1String("\n")); if (isVisible() && m_timerId == 0) m_timerId = startTimer(INTERVAL); } void LazyPlainTextEdit::clear() { if (m_timerId) { killTimer(m_timerId); m_timerId = 0; m_cachedOutput.clear(); } QPlainTextEdit::clear(); } void LazyPlainTextEdit::setVisible(bool visible) { if (m_timerId) { killTimer(m_timerId); m_timerId = 0; } if (visible) m_timerId = startTimer(INTERVAL); QPlainTextEdit::setVisible(visible); updateCursor(TextCursorPosition::Keep); } void LazyPlainTextEdit::updateCursor(TextCursorPosition position) { QTextCursor cursor = textCursor(); if ((position == TextCursorPosition::ForceEnd) || cursor.atEnd()) { // Workaround for height calculation issue if scrollbar is set to Qt::ScrollBarAsNeeded. Qt::ScrollBarPolicy policy = horizontalScrollBarPolicy(); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); // enforce always on cursor.movePosition(QTextCursor::End); setTextCursor(cursor); ensureCursorVisible(); setHorizontalScrollBarPolicy(policy); // but reset once we updated the cursor position } } src/libs/installer/lazyplaintextedit.h000066400000000000000000000036001325366651500205350ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LAZYPLAINTEXTEDIT_H #define LAZYPLAINTEXTEDIT_H #include class LazyPlainTextEdit : public QPlainTextEdit { Q_OBJECT public: enum struct TextCursorPosition { Keep, ForceEnd }; explicit LazyPlainTextEdit(QWidget *parent = 0); void updateCursor(TextCursorPosition position); public slots: void append(const QString &text); virtual void clear(); virtual void setVisible ( bool visible ); protected: void timerEvent(QTimerEvent *event); private: int m_timerId; QString m_cachedOutput; }; #endif // LAZYPLAINTEXTEDIT_H src/libs/installer/lib7z_create.h000066400000000000000000000046671325366651500173470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef LIB7Z_CREATE_H #define LIB7Z_CREATE_H #include "installer_global.h" #include #include <7zip/UI/Common/Update.h> QT_BEGIN_NAMESPACE class QFileDevice; class QStringList; QT_END_NAMESPACE namespace Lib7z { enum struct QTmpFile { No, Yes }; enum struct Compression { Non = 0, Fastest = 1, Fast = 3, Normal = 5, Maximum = 7, Ultra = 9 }; class INSTALLER_EXPORT UpdateCallback : public IUpdateCallbackUI2, public CMyUnknownImp { Q_DISABLE_COPY(UpdateCallback) public: UpdateCallback() = default; virtual ~UpdateCallback() {} MY_UNKNOWN_IMP INTERFACE_IUpdateCallbackUI2(;) }; void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sources, Compression level = Compression::Normal, UpdateCallback *callback = 0); void INSTALLER_EXPORT createArchive(const QString &archive, const QStringList &sources, QTmpFile mode, Compression level = Compression::Normal, UpdateCallback *callback = 0); } // namespace Lib7z #endif // LIB7Z_CREATE_H src/libs/installer/lib7z_extract.h000066400000000000000000000056101325366651500175430ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LIB7Z_EXTRACT_H #define LIB7Z_EXTRACT_H #include "installer_global.h" #include #include <7zip/Archive/IArchive.h> #include class CArc; QT_BEGIN_NAMESPACE class QFileDevice; QT_END_NAMESPACE namespace Lib7z { class INSTALLER_EXPORT ExtractCallback : public IArchiveExtractCallback, public CMyUnknownImp { Q_DISABLE_COPY(ExtractCallback) public: ExtractCallback() = default; virtual ~ExtractCallback() = default; void setArchive(CArc *carc) { arc = carc; } void setTarget(const QString &dir) { targetDir = dir; } MY_UNKNOWN_IMP INTERFACE_IArchiveExtractCallback(;) protected: virtual bool prepareForFile(const QString & /*filename*/) { return true; } virtual void setCurrentFile(const QString &filename) { Q_UNUSED(filename) } virtual HRESULT setCompleted(quint64 /*completed*/, quint64 /*total*/) { return S_OK; } private: CArc *arc = 0; QString targetDir; quint64 total = 0; quint64 completed = 0; quint32 currentIndex = 0; }; void INSTALLER_EXPORT extractArchive(QFileDevice *archive, const QString &targetDirectory, ExtractCallback *callback = 0); } // namespace Lib7z #endif // LIB7Z_EXTRACT_H src/libs/installer/lib7z_facade.cpp000066400000000000000000001117031325366651500176300ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "lib7z_facade.h" #include "errors.h" #include "fileio.h" #include "lib7z_create.h" #include "lib7z_extract.h" #include "lib7z_list.h" #include "lib7z_guid.h" #ifndef Q_OS_WIN # include "StdAfx.h" #endif #include <7zCrc.h> #include <7zip/Archive/IArchive.h> #include <7zip/UI/Common/ArchiveCommandLine.h> #include <7zip/UI/Common/OpenArchive.h> #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN HINSTANCE g_hInstance = 0; # define S_IFMT 00170000 # define S_IFLNK 0120000 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) # define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */ # if !defined(Q_CC_MINGW) # include // for localtime_s # endif #else extern "C" int global_use_utf16_conversion; #include #include #endif namespace NArchive { namespace N7z { void registerArcDec7z(); } namespace NXz { void registerArcxz(); } namespace NSplit { void registerArcSplit(); } namespace NLzma { namespace NLzmaAr { void registerArcLzma(); } namespace NLzma86Ar { void registerArcLzma86(); } } } using namespace NWindows; void registerCodecBCJ(); void registerCodecBCJ2(); void registerCodecLZMA(); void registerCodecLZMA2(); void registerCodecCopy(); void registerCodecDelta(); void registerCodecBranch(); void registerCodecByteSwap(); namespace Lib7z { // -- 7z init codecs, archives std::once_flag gOnceFlag; void initSevenZ() { std::call_once(gOnceFlag, [] { CrcGenerateTable(); registerCodecBCJ(); registerCodecBCJ2(); registerCodecLZMA(); registerCodecLZMA2(); registerCodecCopy(); registerCodecDelta(); registerCodecBranch(); registerCodecByteSwap(); NArchive::N7z::registerArcDec7z(); NArchive::NXz::registerArcxz(); NArchive::NSplit::registerArcSplit(); NArchive::NLzma::NLzmaAr::registerArcLzma(); NArchive::NLzma::NLzma86Ar::registerArcLzma86(); #ifndef Q_OS_WIN # ifdef ENV_HAVE_LOCALE const QByteArray locale = qgetenv("LC_ALL").toUpper(); if (!locale.isEmpty() && (locale != "C") && (locale != "POSIX")) global_use_utf16_conversion = 1; # elif defined(LOCALE_IS_UTF8) global_use_utf16_conversion = 1; # else global_use_utf16_conversion = 0; # endif #endif }); } // -- error handling Q_GLOBAL_STATIC(QString, getLastErrorString) Q_GLOBAL_STATIC(QReadWriteLock, lastErrorReadWriteLock) QString lastError() { QReadLocker locker(lastErrorReadWriteLock()); Q_UNUSED(locker) return *getLastErrorString(); } void setLastError(const QString &errorString) { QWriteLocker locker(lastErrorReadWriteLock()); Q_UNUSED(locker) *getLastErrorString() = errorString; } QString errorMessageFrom7zResult(const LONG &extractResult) { if (!lastError().isEmpty()) return lastError(); QString errorMessage = QCoreApplication::translate("Lib7z", "internal code: %1"); switch (extractResult) { case S_OK: qFatal("S_OK value is not a valid error code."); break; case E_NOTIMPL: errorMessage = errorMessage.arg(QLatin1String("E_NOTIMPL")); break; case E_NOINTERFACE: errorMessage = errorMessage.arg(QLatin1String("E_NOINTERFACE")); break; case E_ABORT: errorMessage = errorMessage.arg(QLatin1String("E_ABORT")); break; case E_FAIL: errorMessage = errorMessage.arg(QLatin1String("E_FAIL")); break; case STG_E_INVALIDFUNCTION: errorMessage = errorMessage.arg(QLatin1String("STG_E_INVALIDFUNCTION")); break; case E_OUTOFMEMORY: errorMessage = QCoreApplication::translate("Lib7z", "not enough memory"); break; case E_INVALIDARG: errorMessage = errorMessage.arg(QLatin1String("E_INVALIDARG")); break; default: errorMessage = QCoreApplication::translate("Lib7z", "Error: %1").arg(extractResult); break; } return errorMessage; } /*! RAII class to create a directory (tryCreate()) and delete it on destruction unless released. */ struct DirectoryGuard { explicit DirectoryGuard(const QString &path) : m_path(path) , m_created(false) , m_released(false) { m_path.replace(QLatin1Char('\\'), QLatin1Char('/')); } ~DirectoryGuard() { if (!m_created || m_released) return; QDir dir(m_path); if (!dir.rmdir(m_path)) qWarning() << "Cannot delete directory " << m_path; } /*! Tries to create the directory structure. Returns a list of every directory created. */ QStringList tryCreate() { if (m_path.isEmpty()) return QStringList(); const QFileInfo fi(m_path); if (fi.exists() && fi.isDir()) return QStringList(); if (fi.exists() && !fi.isDir()) { throw SevenZipException(QCoreApplication::translate("DirectoryGuard", "Path \"%1\" exists but is not a directory.").arg(QDir::toNativeSeparators(m_path))); } QStringList created; QDir toCreate(m_path); while (!toCreate.exists()) { QString p = toCreate.absolutePath(); created.push_front(p); p = p.section(QLatin1Char('/'), 0, -2); toCreate = QDir(p); } QDir dir(m_path); m_created = dir.mkpath(m_path); if (!m_created) { throw SevenZipException(QCoreApplication::translate("DirectoryGuard", "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path))); } return created; } void release() { m_released = true; } QString m_path; bool m_created; bool m_released; }; static UString QString2UString(const QString &str) { return str.toStdWString().c_str(); } static QString UString2QString(const UString& str) { return QString::fromStdWString(static_cast(str)); } static NCOM::CPropVariant readProperty(IInArchive *archive, int index, int propId) { NCOM::CPropVariant prop; if (archive->GetProperty(index, propId, &prop) != S_OK) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot retrieve property %1 for item %2.").arg(QString::number(propId), QString::number(index))); } return prop; } static bool IsFileTimeZero(const FILETIME *lpFileTime) { return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0); } static bool getFileTimeFromProperty(IInArchive* archive, int index, int propId, FILETIME *ft) { const NCOM::CPropVariant prop = readProperty(archive, index, propId); if (prop.vt != VT_FILETIME) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Property %1 for item %2 not of type VT_FILETIME but %3.").arg(QString::number(propId), QString::number(index), QString::number(prop.vt))); } *ft = prop.filetime; return !IsFileTimeZero(ft); } static bool getDateTimeProperty(IInArchive *arc, int index, int id, QDateTime *value) { FILETIME ft7z; if (!getFileTimeFromProperty(arc, index, id, &ft7z)) return false; SYSTEMTIME st; if (!BOOLToBool(FileTimeToSystemTime(&ft7z, &st))) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot convert UTC file time to system time.")); } *value = QDateTime(QDate(st.wYear, st.wMonth, st.wDay), QTime(st.wHour, st.wMinute, st.wSecond), Qt::UTC); return value->isValid(); } static quint64 getUInt64Property(IInArchive *archive, int index, int propId, quint64 defaultValue) { UInt64 value; if (ConvertPropVariantToUInt64(readProperty(archive, index, propId), value)) return value; return defaultValue; } static quint32 getUInt32Property(IInArchive *archive, int index, int propId, quint32 defaultValue) { const NCOM::CPropVariant prop = readProperty(archive, index, propId); if (prop.vt == VT_EMPTY) return defaultValue; return static_cast(prop.ulVal); } static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *hasPermissions) { quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0); QFile::Permissions permissions = 0; if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) { if (hasPermissions != 0) *hasPermissions = true; // filter the Unix permissions attributes = (attributes >> 16) & 0777; permissions |= static_cast((attributes & 0700) << 2); // owner rights permissions |= static_cast((attributes & 0070) << 1); // group permissions |= static_cast((attributes & 0007) << 0); // and world rights } else if (hasPermissions != 0) { *hasPermissions = false; } return permissions; } #define LIB7Z_ASSERTS(X, MODE) \ Q_ASSERT(X); \ Q_ASSERT(X->isOpen()); \ Q_ASSERT(X->is ## MODE()); \ Q_ASSERT(!X->isSequential()); class QIODeviceSequentialOutStream : public ISequentialOutStream, public CMyUnknownImp { Q_DISABLE_COPY(QIODeviceSequentialOutStream) public: MY_UNKNOWN_IMP explicit QIODeviceSequentialOutStream(std::unique_ptr device) : ISequentialOutStream() , m_device(std::move(device)) { LIB7Z_ASSERTS(m_device, Writable) } QString errorString() const { return m_errorString; } STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize) *processedSize = 0; const qint64 written = m_device->write(reinterpret_cast(data), size); if (written == -1) { m_errorString = m_device->errorString(); return E_FAIL; } if (processedSize) *processedSize = written; m_errorString.clear(); return S_OK; } private: QString m_errorString; std::unique_ptr m_device; }; class QIODeviceInStream : public IInStream, public CMyUnknownImp { Q_DISABLE_COPY(QIODeviceInStream) public: MY_UNKNOWN_IMP explicit QIODeviceInStream(QIODevice *device) : IInStream() , CMyUnknownImp() , m_device(device) { LIB7Z_ASSERTS(m_device, Readable) } STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize) { if (m_device.isNull()) return E_FAIL; const qint64 actual = m_device->read(reinterpret_cast(data), size); Q_ASSERT(actual != 0 || m_device->atEnd()); if (processedSize) *processedSize = actual; return actual >= 0 ? S_OK : E_FAIL; } STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { if (m_device.isNull()) return E_FAIL; if (seekOrigin > STREAM_SEEK_END) return STG_E_INVALIDFUNCTION; UInt64 np = 0; switch (seekOrigin) { case STREAM_SEEK_SET: np = offset; break; case STREAM_SEEK_CUR: np = m_device->pos() + offset; break; case STREAM_SEEK_END: np = m_device->size() + offset; break; default: return STG_E_INVALIDFUNCTION; } np = qBound(static_cast(0), np, static_cast(m_device->size())); const bool ok = m_device->seek(np); if (newPosition) *newPosition = np; return ok ? S_OK : E_FAIL; } private: QPointer m_device; }; bool operator==(const File &lhs, const File &rhs) { return lhs.path == rhs.path && lhs.utcTime == rhs.utcTime && lhs.isDirectory == rhs.isDirectory && lhs.compressedSize == rhs.compressedSize && lhs.uncompressedSize == rhs.uncompressedSize && (lhs.permissions == rhs.permissions || lhs.permissions == static_cast(-1) || rhs.permissions == static_cast(-1)); } QVector listArchive(QFileDevice *archive) { LIB7Z_ASSERTS(archive, Readable) const qint64 initialPos = archive->pos(); try { CCodecs codecs; if (codecs.Load() != S_OK) throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); COpenOptions op; op.codecs = &codecs; CObjectVector types; op.types = &types; // Empty, because we use a stream. CIntVector excluded; op.excludedFormats = &excluded; const CMyComPtr stream = new QIODeviceInStream(archive); op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). CObjectVector properties; op.props = &properties; CArchiveLink archiveLink; if (archiveLink.Open2(op, nullptr) != S_OK) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot open archive \"%1\".").arg(archive->fileName())); } QVector flat; for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { IInArchive *const arch = archiveLink.Arcs[i].Archive; UInt32 numItems = 0; if (arch->GetNumberOfItems(&numItems) != S_OK) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot retrieve number of items in archive.")); } flat.reserve(flat.size() + numItems); for (uint item = 0; item < numItems; ++item) { UString s; if (archiveLink.Arcs[i].GetItemPath(item, s) != S_OK) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot retrieve path of archive item \"%1\".").arg(item)); } File f; f.archiveIndex.setX(i); f.archiveIndex.setY(item); f.path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')); Archive_IsItem_Folder(arch, item, f.isDirectory); f.permissions = getPermissions(arch, item, 0); getDateTimeProperty(arch, item, kpidMTime, &(f.utcTime)); f.uncompressedSize = getUInt64Property(arch, item, kpidSize, 0); f.compressedSize = getUInt64Property(arch, item, kpidPackSize, 0); flat.append(f); } } return flat; } catch (const char *err) { archive->seek(initialPos); throw SevenZipException(err); } catch (const SevenZipException &e) { archive->seek(initialPos); throw e; // re-throw unmodified } catch (...) { archive->seek(initialPos); throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO))); } return QVector(); // never reached } // -- ExtractCallback STDMETHODIMP ExtractCallback::SetTotal(UInt64 t) { total = t; return S_OK; } STDMETHODIMP ExtractCallback::SetCompleted(const UInt64 *c) { completed = *c; if (total > 0) return setCompleted(completed, total); return S_OK; } // this method will be called by CFolderOutStream::OpenFile to stream via // CDecoder::CodeSpec extracted content to an output stream. STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 /*askExtractMode*/) { *outStream = 0; if (targetDir.isEmpty()) return E_FAIL; Q_ASSERT(arc); currentIndex = index; UString s; if (arc->GetItemPath(index, s) != S_OK) { setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Cannot retrieve path of archive item %1.").arg(index)); return E_FAIL; } const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, UString2QString(s))); DirectoryGuard guard(fi.absolutePath()); const QStringList directories = guard.tryCreate(); bool isDir = false; Archive_IsItem_Folder(arc->Archive, index, isDir); if (isDir) QDir(fi.absolutePath()).mkdir(fi.fileName()); // this makes sure that all directories created get removed as well foreach (const QString &directory, directories) setCurrentFile(directory); if (!isDir && !prepareForFile(fi.absoluteFilePath())) return E_FAIL; setCurrentFile(fi.absoluteFilePath()); if (!isDir) { #ifndef Q_OS_WIN // do not follow symlinks, so we need to remove an existing one if (fi.isSymLink() && (!QFile::remove(fi.absoluteFilePath()))) { setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Cannot remove already existing symlink %1.").arg(fi.absoluteFilePath())); return E_FAIL; } #endif std::unique_ptr file(new QFile(fi.absoluteFilePath())); if (!file->open(QIODevice::WriteOnly)) { setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(fi.absoluteFilePath()), file->errorString())); return E_FAIL; } CMyComPtr stream = new QIODeviceSequentialOutStream(std::move(file)); *outStream = stream.Detach(); // CMyComPtr is needed, otherwise it crashes in Write(). } guard.release(); return S_OK; } STDMETHODIMP ExtractCallback::PrepareOperation(Int32 /*askExtractMode*/) { return S_OK; } STDMETHODIMP ExtractCallback::SetOperationResult(Int32 /*resultEOperationResult*/) { if (targetDir.isEmpty()) return S_OK; UString s; if (arc->GetItemPath(currentIndex, s) != S_OK) { setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Cannot retrieve path of archive item %1.").arg(currentIndex)); return E_FAIL; } const QString absFilePath = QFileInfo(QString::fromLatin1("%1/%2").arg(targetDir, UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')))).absoluteFilePath(); // do we have a symlink? const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0); struct stat stat_info; stat_info.st_mode = attributes >> 16; if (S_ISLNK(stat_info.st_mode)) { #ifdef Q_OS_WIN qFatal(QString::fromLatin1("Creating a link from archive is not implemented for " "windows. Link filename: %1").arg(absFilePath).toLatin1()); // TODO //if (!NFile::NDir::MyCreateHardLink(CFSTR(QDir::toNativeSeparators(absFilePath).utf16()), // CFSTR(QDir::toNativeSeparators(symlinkTarget).utf16())) { // return S_FALSE; //} #else QFileInfo symlinkPlaceHolderFileInfo(absFilePath); if (symlinkPlaceHolderFileInfo.isSymLink()) { setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Cannot create symlink at \"%1\". Another one is already existing.") .arg(absFilePath)); return E_FAIL; } QFile symlinkPlaceHolderFile(absFilePath); if (!symlinkPlaceHolderFile.open(QIODevice::ReadOnly)) { setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Cannot read symlink target from file \"%1\".").arg(absFilePath)); return E_FAIL; } const QByteArray symlinkTarget = symlinkPlaceHolderFile.readAll(); symlinkPlaceHolderFile.close(); symlinkPlaceHolderFile.remove(); QFile targetFile(QString::fromLatin1(symlinkTarget)); if (!targetFile.link(absFilePath)) { setLastError(QCoreApplication::translate("ExtractCallbackImpl", "Cannot create symlink at %1: %2").arg(absFilePath, targetFile.errorString())); return E_FAIL; } return S_OK; #endif } try { // Note: This part might also fail while running a elevated installation. if (!absFilePath.isEmpty()) { // This might fail for archives without all properties, we can only be sure // about modification time, as it's always stored by default in 7z archives. // Also note that we restore modification time on Unix only, as access time // and change time are supposed to be set to the time of installation. FILETIME mTime; const UString fileName = QString2UString(absFilePath); if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidMTime, &mTime)) { NWindows::NFile::NIO::COutFile file; if (file.Open(fileName, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL)) file.SetTime(&mTime, &mTime, &mTime); } #ifdef Q_OS_WIN FILETIME cTime, aTime; if (getFileTimeFromProperty(arc->Archive, currentIndex, kpidCTime, &cTime) && getFileTimeFromProperty(arc->Archive, currentIndex, kpidATime, &aTime)) { NWindows::NFile::NIO::COutFile file; if (file.Open(fileName, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL)) file.SetTime(&cTime, &aTime, &mTime); } #endif } } catch (...) {} bool hasPerm = false; const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm); if (hasPerm) QFile::setPermissions(absFilePath, permissions); return S_OK; } /*! \namespace Lib7z \inmodule QtInstallerFramework \brief The Lib7z namespace contains miscellaneous identifiers used throughout the Lib7z library. */ /*! \fn virtual bool Lib7z::ExtractCallback::prepareForFile(const QString &filename) Implement to prepare for file \a filename to be extracted, e.g. by renaming existing files. Return \c true if the preparation was successful and extraction can be continued. If \c false is returned, the extraction will be aborted. The default implementation returns \c true. */ // -- UpdateCallback HRESULT UpdateCallback::SetTotal(UInt64) { return S_OK; } HRESULT UpdateCallback::SetCompleted(const UInt64*) { return S_OK; } HRESULT UpdateCallback::SetRatioInfo(const UInt64*, const UInt64*) { return S_OK; } HRESULT UpdateCallback::CheckBreak() { return S_OK; } HRESULT UpdateCallback::Finilize() { return S_OK; } HRESULT UpdateCallback::SetNumFiles(UInt64) { return S_OK; } HRESULT UpdateCallback::GetStream(const wchar_t*, bool) { return S_OK; } HRESULT UpdateCallback::OpenFileError(const wchar_t*, DWORD) { return S_OK; } HRESULT UpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) { *password = 0; *passwordIsDefined = false; return S_OK; } HRESULT UpdateCallback::CryptoGetTextPassword(BSTR *password) { *password = 0; return E_NOTIMPL; } HRESULT UpdateCallback::OpenResult(const wchar_t*, HRESULT, const wchar_t*) { return S_OK; } HRESULT UpdateCallback::StartScanning() { return S_OK; } HRESULT UpdateCallback::ScanProgress(UInt64, UInt64, UInt64, const wchar_t*, bool) { return S_OK; } HRESULT UpdateCallback::CanNotFindError(const wchar_t*, DWORD) { return S_OK; } HRESULT UpdateCallback::FinishScanning() { return S_OK; } HRESULT UpdateCallback::StartArchive(const wchar_t*, bool) { return S_OK; } HRESULT UpdateCallback::FinishArchive() { return S_OK; } HRESULT UpdateCallback::SetOperationResult(Int32) { return S_OK; } /*! Function to create an empty 7z container. Using a temporary file only is not working, since 7z checks the output file for a valid signature, otherwise it rejects overwriting the file. */ static QString createTmp7z() { QTemporaryFile file; if (!file.open()) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot create " "temporary file: %1").arg(file.errorString())); } file.write(QByteArray::fromHex("377A.BCAF.271C" // Signature. ".0003.8D9B.D50F.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000" // Crc + Data. )); file.setAutoRemove(false); return file.fileName(); } /*! Creates an archive using the given file device \a archive. \a sourcePaths can contain one or more files, one or more directories or a combination of files and folders. The \c * wildcard is supported also. The value of \a level specifies the compression ratio, the default is set to \c 5 (Normal compression). The \a callback can be used to get information about the archive creation process. If no \a callback is given, an empty implementation is used. \note Throws SevenZipException on error. \note Filenames are stored case-sensitive with UTF-8 encoding. \note The ownership of \a callback is transferred to the function and gets delete on exit. */ void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sources, Compression level, UpdateCallback *callback) { LIB7Z_ASSERTS(archive, Writable) const QString tmpArchive = createTmp7z(); Lib7z::createArchive(tmpArchive, sources, QTmpFile::No, level, callback); try { QFile source(tmpArchive); QInstaller::openForRead(&source); QInstaller::blockingCopy(&source, archive, source.size()); } catch (const QInstaller::Error &error) { throw SevenZipException(error.message()); } } /*! Creates an archive with the given filename \a archive. \a sourcePaths can contain one or more files, one or more directories or a combination of files and folders. Also the \c * wildcard is supported. To be able to use the function during an elevated installation, set \a mode to \c QTmpFile::Yes. The value of \a level specifies the compression ratio, the default is set to \c 5 (Normal compression). The \a callback can be used to get information about the archive creation process. If no \a callback is given, an empty implementation is used. \note Throws SevenZipException on error. \note If \a archive exists, it will be overwritten. \note Filenames are stored case-sensitive with UTF-8 encoding. \note The ownership of \a callback is transferred to the function and gets delete on exit. */ void createArchive(const QString &archive, const QStringList &sources, QTmpFile mode, Compression level, UpdateCallback *callback) { try { QString target = archive; if (mode == QTmpFile::Yes) target = createTmp7z(); CArcCmdLineOptions options; try { UStringVector commandStrings; commandStrings.Add(L"a"); // mode: add commandStrings.Add(L"-t7z"); // type: 7z commandStrings.Add(L"-mtm=on"); // time: modeifier|creation|access commandStrings.Add(L"-mtc=on"); commandStrings.Add(L"-mta=on"); commandStrings.Add(L"-mmt=on"); // threads: multi-threaded #ifdef Q_OS_WIN commandStrings.Add(L"-sccUTF-8"); // files: case-sensitive|UTF8 #endif commandStrings.Add(QString2UString(QString::fromLatin1("-mx=%1").arg(int(level)))); // compression: level commandStrings.Add(QString2UString(QDir::toNativeSeparators(target))); foreach (const QString &source, sources) commandStrings.Add(QString2UString(source)); CArcCmdLineParser parser; parser.Parse1(commandStrings, options); parser.Parse2(options); } catch (const CArcCmdLineException &e) { throw SevenZipException(UString2QString(e)); } CCodecs codecs; if (codecs.Load() != S_OK) throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); CObjectVector types; if (!ParseOpenTypes(codecs, options.ArcType, types)) throw SevenZipException(QCoreApplication::translate("Lib7z", "Unsupported archive type.")); CUpdateErrorInfo errorInfo; CMyComPtr comCallback = callback == 0 ? new UpdateCallback : callback; const HRESULT res = UpdateArchive(&codecs, types, options.ArchiveName, options.Censor, options.UpdateOptions, errorInfo, nullptr, comCallback, true); const QFile tempFile(UString2QString(options.ArchiveName)); if (res != S_OK || !tempFile.exists()) { QString errorMsg; if (res == S_OK) { errorMsg = QCoreApplication::translate("Lib7z", "Cannot create archive \"%1\"") .arg(QDir::toNativeSeparators(tempFile.fileName())); } else { errorMsg = QCoreApplication::translate("Lib7z", "Cannot create archive \"%1\": %2") .arg(QDir::toNativeSeparators(tempFile.fileName()), errorMessageFrom7zResult(res)); } throw SevenZipException(errorMsg); } if (mode == QTmpFile::Yes) { QFile org(archive); if (org.exists() && !org.remove()) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot remove " "old archive \"%1\": %2").arg(QDir::toNativeSeparators(org.fileName()), org.errorString())); } QFile arc(UString2QString(options.ArchiveName)); if(!arc.rename(archive)) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot rename " "temporary archive \"%1\" to \"%2\": %3").arg( QDir::toNativeSeparators(arc.fileName()), QDir::toNativeSeparators(archive), arc.errorString())); } } } catch (const char *err) { throw SevenZipException(err); } catch (SevenZipException &e) { throw e; // re-throw unmodified } catch (const QInstaller::Error &err) { throw SevenZipException(err.message()); } catch (...) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO))); } } /*! Extracts the given \a archive content into target directory \a directory using the provided extract callback \a callback. The output filenames are deduced from the \a archive content. \note Throws SevenZipException on error. \note The ownership of \a callback is not transferred to the function. */ void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallback *callback) { LIB7Z_ASSERTS(archive, Readable) // Guard a given object against unwanted delete. CMyComPtr externCallback = callback; CMyComPtr localCallback; if (!externCallback) { callback = new ExtractCallback; localCallback = callback; } DirectoryGuard outDir(QFileInfo(directory).absolutePath()); try { outDir.tryCreate(); CCodecs codecs; if (codecs.Load() != S_OK) throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); COpenOptions op; op.codecs = &codecs; CObjectVector types; op.types = &types; // Empty, because we use a stream. CIntVector excluded; op.excludedFormats = &excluded; const CMyComPtr stream = new QIODeviceInStream(archive); op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). CObjectVector properties; op.props = &properties; CArchiveLink archiveLink; if (archiveLink.Open2(op, nullptr) != S_OK) { throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot open archive \"%1\".").arg(archive->fileName())); } callback->setTarget(directory); for (unsigned a = 0; a < archiveLink.Arcs.Size(); ++a) { callback->setArchive(&archiveLink.Arcs[a]); IInArchive *const arch = archiveLink.Arcs[a].Archive; const LONG result = arch->Extract(0, static_cast(-1), false, callback); if (result != S_OK) throw SevenZipException(errorMessageFrom7zResult(result)); } } catch (const SevenZipException &e) { externCallback.Detach(); throw e; // re-throw unmodified } catch (...) { externCallback.Detach(); throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO))); } outDir.release(); externCallback.Detach(); } /*! Returns \c true if the given \a archive is supported; otherwise returns \c false. \note Throws SevenZipException on error. */ bool isSupportedArchive(QFileDevice *archive) { LIB7Z_ASSERTS(archive, Readable) const qint64 initialPos = archive->pos(); try { CCodecs codecs; if (codecs.Load() != S_OK) throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot load codecs.")); COpenOptions op; op.codecs = &codecs; CObjectVector types; op.types = &types; // Empty, because we use a stream. CIntVector excluded; op.excludedFormats = &excluded; const CMyComPtr stream = new QIODeviceInStream(archive); op.stream = stream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). CObjectVector properties; op.props = &properties; CArchiveLink archiveLink; const HRESULT result = archiveLink.Open2(op, nullptr); archive->seek(initialPos); return result == S_OK; } catch (const char *err) { archive->seek(initialPos); throw SevenZipException(err); } catch (const SevenZipException &e) { archive->seek(initialPos); throw e; // re-throw unmodified } catch (...) { archive->seek(initialPos); throw SevenZipException(QCoreApplication::translate("Lib7z", "Unknown exception caught (%1).").arg(QString::fromLatin1(Q_FUNC_INFO))); } return false; // never reached } /*! Returns \c true if the given \a archive is supported; otherwise returns \c false. \note Throws SevenZipException on error. */ bool isSupportedArchive(const QString &archive) { QFile file(archive); if (!file.open(QIODevice::ReadOnly)) return false; return isSupportedArchive(&file); } } // namespace Lib7z src/libs/installer/lib7z_facade.h000066400000000000000000000052621325366651500172770ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LIB7Z_FACADE_H #define LIB7Z_FACADE_H #include "installer_global.h" #include "errors.h" #include #include <7zip/UI/Console/PercentPrinter.h> QT_BEGIN_NAMESPACE class QFileDevice; QT_END_NAMESPACE namespace Lib7z { void INSTALLER_EXPORT initSevenZ(); bool INSTALLER_EXPORT isSupportedArchive(QFileDevice *archive); bool INSTALLER_EXPORT isSupportedArchive(const QString &archive); class INSTALLER_EXPORT SevenZipException : public QInstaller::Error { public: explicit SevenZipException(const QString &msg) : QInstaller::Error(msg) {} explicit SevenZipException(const char *msg) : QInstaller::Error(QString::fromLocal8Bit(msg)) {} }; class INSTALLER_EXPORT PercentPrinter : public CPercentPrinter { public: PercentPrinter() : CPercentPrinter(1 << 16) { OutStream = &g_StdOut; } void PrintRatio() { CPercentPrinter::PrintRatio(); } void ClosePrint() { CPercentPrinter::ClosePrint(); } void RePrintRatio() { CPercentPrinter::RePrintRatio(); } void PrintNewLine() { CPercentPrinter::PrintNewLine(); } void PrintString(const char *s) { CPercentPrinter::PrintString(s); } void PrintString(const wchar_t *s) { CPercentPrinter::PrintString(s); } }; } // namespace Lib7z #endif // LIB7Z_FACADE_H src/libs/installer/lib7z_guid.h000066400000000000000000000112711325366651500170210ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LIB7Z_GUID_H #define LIB7Z_GUID_H #include #ifdef __cplusplus #define DEFINE_7Z_GUID(name, b4, b5, b6) extern "C" const GUID IID_ ## name = { 0x23170F69, \ 0x40C1, 0x278A, { 0x00, 0x00, 0x00, b4, b5, b6, 0x00, 0x00 } } #else #define DEFINE_7Z_GUID(name, b4, b5, b6) extern const GUID IID_ ## name = { 0x23170F69, \ 0x40C1, 0x278A, { 0x00, 0x00, 0x00, b4, b5, b6, 0x00, 0x00 } } #endif DEFINE_7Z_GUID(IInArchiveGetStream, 0x06, 0x00, 0x40); DEFINE_7Z_GUID(IInArchive, 0x06, 0x00, 0x60); DEFINE_7Z_GUID(IOutStream, 0x03, 0x00, 0x04); DEFINE_7Z_GUID(IOutStreamFlush, 0x03, 0x00, 0x07); DEFINE_7Z_GUID(IOutArchive, 0x06, 0x00, 0xA0); DEFINE_7Z_GUID(IInStream, 0x03, 0x00, 0x03); DEFINE_7Z_GUID(ISetProperties, 0x06, 0x00, 0x03); DEFINE_7Z_GUID(ISequentialInStream, 0x03, 0x00, 0x01); DEFINE_7Z_GUID(ISequentialOutStream, 0x03, 0x00, 0x02); DEFINE_7Z_GUID(IStreamGetSize, 0x03, 0x00, 0x06); DEFINE_7Z_GUID(IStreamGetProps, 0x03, 0x00, 0x08); DEFINE_7Z_GUID(IStreamGetProps2, 0x03, 0x00, 0x09); DEFINE_7Z_GUID(IArchiveKeepModeForNextOpen, 0x06, 0x00, 0x04); DEFINE_7Z_GUID(IArchiveAllowTail, 0x06, 0x00, 0x05); DEFINE_7Z_GUID(IArchiveOpenCallback, 0x06, 0x00, 0x10); DEFINE_7Z_GUID(IArchiveExtractCallback, 0x06, 0x00, 0x20); DEFINE_7Z_GUID(IArchiveOpenVolumeCallback, 0x06, 0x00, 0x30); DEFINE_7Z_GUID(IArchiveOpenSetSubArchiveName, 0x06, 0x00, 0x50); DEFINE_7Z_GUID(IArchiveOpenSeq, 0x06, 0x00, 0x61); DEFINE_7Z_GUID(IArchiveGetRawProps, 0x06, 0x00, 0x70); DEFINE_7Z_GUID(IArchiveGetRootProps, 0x06, 0x00, 0x71); DEFINE_7Z_GUID(IArchiveUpdateCallback, 0x06, 0x00, 0x80); DEFINE_7Z_GUID(IArchiveUpdateCallback2, 0x06, 0x00, 0x82); DEFINE_7Z_GUID(ICompressProgressInfo, 0x04, 0x00, 0x04); DEFINE_7Z_GUID(ICompressCoder, 0x04, 0x00, 0x05); DEFINE_7Z_GUID(ICompressSetCoderProperties, 0x04, 0x00, 0x20); DEFINE_7Z_GUID(ICompressSetDecoderProperties2, 0x04, 0x00, 0x22); DEFINE_7Z_GUID(ICompressWriteCoderProperties, 0x04, 0x00, 0x23); DEFINE_7Z_GUID(ICompressGetInStreamProcessedSize, 0x04, 0x00, 0x24); DEFINE_7Z_GUID(ICompressSetCoderMt, 0x04, 0x00, 0x25); DEFINE_7Z_GUID(ICompressSetOutStream, 0x04, 0x00, 0x32); DEFINE_7Z_GUID(ICompressSetInStream, 0x04, 0x00, 0x31); DEFINE_7Z_GUID(ICompressSetOutStreamSize, 0x04, 0x00, 0x34); DEFINE_7Z_GUID(ICompressSetBufSize, 0x04, 0x00, 0x35); DEFINE_7Z_GUID(ICompressGetSubStreamSize, 0x04, 0x00, 0x30); DEFINE_7Z_GUID(ICryptoResetInitVector, 0x04, 0x00, 0x8C); DEFINE_7Z_GUID(ICryptoSetPassword, 0x04, 0x00, 0x90); DEFINE_7Z_GUID(ICryptoGetTextPassword, 0x05, 0x00, 0x10); DEFINE_7Z_GUID(ICryptoGetTextPassword2, 0x05, 0x00, 0x11); #undef DEFINE_7Z_GUID #endif // LIB7Z_GUID_H src/libs/installer/lib7z_list.h000066400000000000000000000043141325366651500170440ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LIB7Z_LIST_H #define LIB7Z_LIST_H #include "installer_global.h" #include #include #include namespace Lib7z { struct INSTALLER_EXPORT File { public: QString path; QDateTime utcTime; QPoint archiveIndex; bool isDirectory = false; quint64 compressedSize = 0; quint64 uncompressedSize = 0; QFile::Permissions permissions = 0; }; INSTALLER_EXPORT bool operator==(const File &lhs, const File &rhs); QVector INSTALLER_EXPORT listArchive(QFileDevice *archive); } // namespace Lib7z #endif // LIB7Z_LIST_H src/libs/installer/licenseoperation.cpp000066400000000000000000000070251325366651500206620ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "licenseoperation.h" #include "packagemanagercore.h" #include "settings.h" #include #include #include using namespace QInstaller; LicenseOperation::LicenseOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("License")); } void LicenseOperation::backup() { } bool LicenseOperation::performOperation() { QVariantMap licenses = value(QLatin1String("licenses")).toMap(); if (licenses.isEmpty()) { setError(UserDefinedError); setErrorString(tr("No license files found to copy.")); return false; } PackageManagerCore *const core = packageManager(); if (!core) { setError( UserDefinedError ); setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name())); return false; } QString targetDir = QString::fromLatin1("%1%2%3").arg(core->value(scTargetDir), QDir::separator(), QLatin1String("Licenses")); QDir dir; dir.mkpath(targetDir); setArguments(QStringList(targetDir)); for (QVariantMap::const_iterator it = licenses.constBegin(); it != licenses.constEnd(); ++it) { QFile file(targetDir + QLatin1Char('/') + it.key()); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { setError(UserDefinedError); setErrorString(tr("Can not write license file \"%1\".").arg(QDir::toNativeSeparators(file.fileName()))); return false; } QTextStream stream(&file); stream << it.value().toString(); } return true; } bool LicenseOperation::undoOperation() { const QVariantMap licenses = value(QLatin1String("licenses")).toMap(); if (licenses.isEmpty()) { setError(UserDefinedError); setErrorString(tr("No license files found to delete.")); return false; } QString targetDir = arguments().value(0); for (QVariantMap::const_iterator it = licenses.begin(); it != licenses.end(); ++it) QFile::remove(targetDir + QDir::separator() + it.key()); QDir dir; dir.rmdir(targetDir); return true; } bool LicenseOperation::testOperation() { return true; } src/libs/installer/licenseoperation.h000066400000000000000000000033471325366651500203320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LICENSEOPERATION_H #define LICENSEOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT LicenseOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::LicenseOperation) public: explicit LicenseOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } // namespace QInstaller #endif //LICENSEOPERATION_H src/libs/installer/linereplaceoperation.cpp000066400000000000000000000062571325366651500215310ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "linereplaceoperation.h" #include #include #include using namespace QInstaller; LineReplaceOperation::LineReplaceOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("LineReplace")); } void LineReplaceOperation::backup() { } bool LineReplaceOperation::performOperation() { // Arguments: // 1. filename // 2. startsWith Search-String // 3. Replace-Line-String if (!checkArgumentCount(3)) return false; const QStringList args = arguments(); const QString fileName = args.at(0); const QString searchString = args.at(1); const QString replaceString = args.at(2); QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { setError(UserDefinedError); setErrorString(tr("Cannot open file \"%1\" for reading: %2").arg( QDir::toNativeSeparators(fileName), file.errorString())); return false; } QString replacement; QTextStream stream(&file); while (!stream.atEnd()) { const QString line = stream.readLine(); if (line.trimmed().startsWith(searchString)) replacement.append(replaceString + QLatin1String("\n")); else replacement.append(line + QLatin1String("\n")); } file.close(); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { setError(UserDefinedError); setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(fileName), file.errorString())); return false; } stream.setDevice(&file); stream << replacement; file.close(); return true; } bool LineReplaceOperation::undoOperation() { return true; } bool LineReplaceOperation::testOperation() { return true; } src/libs/installer/linereplaceoperation.h000066400000000000000000000033631325366651500211710ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LINEREPLACEOPERATION_H #define LINEREPLACEOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT LineReplaceOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::LineReplaceOperation) public: explicit LineReplaceOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } // namespace #endif // LINEREPLACEOPERATION_H src/libs/installer/link.cpp000066400000000000000000000241401325366651500162510ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "link.h" #include "utils.h" #include #include #include #ifdef Q_OS_UNIX #include #endif #ifdef Q_OS_WIN #include #include #ifndef Q_CC_MINGW # include #endif #if !defined(REPARSE_DATA_BUFFER_HEADER_SIZE) typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) #endif class FileHandleWrapper { public: FileHandleWrapper(const QString &path) : m_dirHandle(INVALID_HANDLE_VALUE) { QString normalizedPath = QString(path).replace(QLatin1Char('/'), QLatin1Char('\\')); m_dirHandle = CreateFile((wchar_t*)normalizedPath.utf16(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0); if (m_dirHandle == INVALID_HANDLE_VALUE) { qWarning() << "Cannot open" << path << ":" << QInstaller::windowsErrorString(GetLastError()); } } ~FileHandleWrapper() { if (m_dirHandle != INVALID_HANDLE_VALUE) CloseHandle(m_dirHandle); } HANDLE handle() { return m_dirHandle; } private: HANDLE m_dirHandle; }; QString readWindowsSymLink(const QString &path) { QString result; FileHandleWrapper dirHandle(path); if (dirHandle.handle() != INVALID_HANDLE_VALUE) { REPARSE_DATA_BUFFER* reparseStructData = (REPARSE_DATA_BUFFER*)calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); DWORD bytesReturned = 0; if (::DeviceIoControl(dirHandle.handle(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseStructData, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned, 0)) { if (reparseStructData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { int length = reparseStructData->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t); int offset = reparseStructData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); const wchar_t* PathBuffer = &reparseStructData->MountPointReparseBuffer.PathBuffer[offset]; result = QString::fromWCharArray(PathBuffer, length); } else if (reparseStructData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { int length = reparseStructData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); int offset = reparseStructData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t); const wchar_t* PathBuffer = &reparseStructData->SymbolicLinkReparseBuffer.PathBuffer[offset]; result = QString::fromWCharArray(PathBuffer, length); } // cut-off "//?/" and "/??/" if (result.size() > 4 && result.at(0) == QLatin1Char('\\') && result.at(2) == QLatin1Char('?') && result.at(3) == QLatin1Char('\\')) result = result.mid(4); } free(reparseStructData); } return result; } Link createJunction(const QString &linkPath, const QString &targetPath) { if (!QDir().mkpath(linkPath)) { qWarning() << "Cannot create the mount directory" << linkPath; return Link(linkPath); } FileHandleWrapper dirHandle(linkPath); if (dirHandle.handle() == INVALID_HANDLE_VALUE) { qWarning() << "Cannot open" << linkPath << ":" << QInstaller::windowsErrorString(GetLastError()); return Link(linkPath); } const QString szDestDir = QString::fromLatin1("\\??\\").arg(targetPath).replace(QLatin1Char('/'), QLatin1Char('\\')); // Allocates a block of memory for an array of num elements(1) and initializes all its bits to zero. REPARSE_DATA_BUFFER* reparseStructData = (REPARSE_DATA_BUFFER*)calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); reparseStructData->Reserved = 0; reparseStructData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; reparseStructData->MountPointReparseBuffer.PrintNameLength = 0; reparseStructData->MountPointReparseBuffer.SubstituteNameOffset = 0; reparseStructData->MountPointReparseBuffer.SubstituteNameLength = szDestDir.length(); reparseStructData->MountPointReparseBuffer.PrintNameOffset = szDestDir.length() + sizeof(WCHAR); uint spaceAfterGeneralData = sizeof(USHORT) * 5 + sizeof(WCHAR); //should be 12 reparseStructData->ReparseDataLength = szDestDir.length() + spaceAfterGeneralData; #ifndef Q_CC_MINGW StringCchCopy(reparseStructData->MountPointReparseBuffer.PathBuffer, 1024, (wchar_t*)szDestDir.utf16()); #else wcsncpy(reparseStructData->MountPointReparseBuffer.PathBuffer, (wchar_t*)szDestDir.utf16(), 1024); #endif DWORD bytesReturned; if (!::DeviceIoControl(dirHandle.handle(), FSCTL_SET_REPARSE_POINT, reparseStructData, reparseStructData->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE, 0, 0, &bytesReturned, 0)) { qWarning() << "Cannot set the reparse point for" << linkPath << "to" << targetPath << ":" << QInstaller::windowsErrorString(GetLastError()); } return Link(linkPath); } bool removeJunction(const QString &path) { // Allocates a block of memory for an array of num elements(1) and initializes all its bits to zero. REPARSE_DATA_BUFFER* reparseStructData = (REPARSE_DATA_BUFFER*)calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); reparseStructData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; { // extra scope because we need to close the dirHandle before we can remove that directory FileHandleWrapper dirHandle(path); DWORD bytesReturned; if (!::DeviceIoControl(dirHandle.handle(), FSCTL_DELETE_REPARSE_POINT, reparseStructData, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, 0, 0, &bytesReturned, 0)) { qWarning() << "Cannot remove the reparse point" << path << ":" << QInstaller::windowsErrorString(GetLastError()); return false; } } return QDir().rmdir(path); } #else Link createLnSymlink(const QString &linkPath, const QString &targetPath) { int linkedError = symlink(QFileInfo(targetPath).absoluteFilePath().toUtf8(), QFileInfo(linkPath).absoluteFilePath().toUtf8()); if (linkedError != 0) { qWarning() << "Cannot create a symlink from" << linkPath << "to" << targetPath << ":" << linkedError; } return Link(linkPath); } bool removeLnSymlink(const QString &path) { return QFile::remove(path); } #endif Link::Link(const QString &path) : m_path(path) { } Link Link::create(const QString &link, const QString &targetPath) { QStringList pathParts = QFileInfo(link).absoluteFilePath().split(QLatin1Char('/')); pathParts.removeLast(); QString linkPath = pathParts.join(QLatin1String("/")); bool linkPathExists = QFileInfo(linkPath).exists(); if (!linkPathExists) linkPathExists = QDir().mkpath(linkPath); if (!linkPathExists) { qWarning() << "Cannot create the needed directories" << link; return Link(link); } #ifdef Q_OS_WIN if (QFileInfo(targetPath).isDir()) return createJunction(link, targetPath); qWarning() << "At the moment the" << Q_FUNC_INFO << "can not create anything else as " << "junctions for directories under windows"; return Link(link); #else return createLnSymlink(link, targetPath); #endif } QString Link::targetPath() const { #ifdef Q_OS_WIN return readWindowsSymLink(m_path); #else return QFileInfo(m_path).readLink(); #endif } bool Link::exists() { #ifdef Q_OS_WIN return QFileInfo(m_path).exists(); #else return QFileInfo(m_path).isSymLink(); #endif } bool Link::targetExists() { return QFileInfo(targetPath()).exists(); } bool Link::isValid() { return targetExists() && QFileInfo(m_path).exists(); } bool Link::remove() { if (!exists()) return false; #ifdef Q_OS_WIN return removeJunction(m_path); #else return removeLnSymlink(m_path); #endif } src/libs/installer/link.h000066400000000000000000000031751325366651500157230ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef LINK_H #define LINK_H #include class Link { public: explicit Link(const QString &path); static Link create(const QString &link, const QString &targetPath); QString targetPath() const; bool targetExists(); bool exists(); bool isValid(); bool remove(); private: QString m_path; }; #endif // LINK_H src/libs/installer/messageboxhandler.cpp000066400000000000000000000417161325366651500210170ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "messageboxhandler.h" #include #include #include #include /*! \inmodule QtInstallerFramework \class QInstaller::MessageBoxHandler \brief The MessageBoxHandler class provides a modal dialog for informing the user or asking the user a question and receiving an answer. \badcode var result = QMessageBox.question("quit.question", "Installer", "Do you want to quit the installer?", QMessageBox.Yes | QMessageBox.No); if (result == QMessageBox.Yes) { // ... } \endcode \section2 Buttons in Message Boxes \c QMessageBox defines a list of common buttons: \list \li \l{QMessageBox::Ok}{OK} \li \l{QMessageBox::Open}{Open} \li \l{QMessageBox::Save}{Save} \li \l{QMessageBox::Cancel}{Cancel} \li \l{QMessageBox::Close}{Close} \li \l{QMessageBox::Discard}{Discard} \li \l{QMessageBox::Apply}{Apply} \li \l{QMessageBox::Reset}{Reset} \li \l{QMessageBox::RestoreDefaults}{RestoreDefaults} \li \l{QMessageBox::Help}{Help} \li \l{QMessageBox::SaveAll}{SaveAll} \li \l{QMessageBox::Yes}{Yes} \li \l{QMessageBox::YesToAll}{YesToAll} \li \l{QMessageBox::No}{No} \li \l{QMessageBox::NoToAll}{NoToAll} \li \l{QMessageBox::Abort}{Abort} \li \l{QMessageBox::Retry}{Retry} \li \l{QMessageBox::Ignore}{Ignore} \li \l{QMessageBox::NoButton}{NoButton} \endlist */ using namespace QInstaller; // -- MessageBoxHandler /*! \enum MessageBoxHandler::DefaultAction This enum value holds the default action for the message box handler: \value AskUser Ask the end user for confirmation. \value Accept Accept the message box. \value Reject Reject the message box. */ /*! \enum MessageBoxHandler::MessageType This enum value holds the severity level of the message displayed in the message box: \value criticalType Reports critical errors. \value informationType Reports information about normal operations. \value questionType Asks a question during normal operations. \value warningType Reports non-critical errors. */ MessageBoxHandler *MessageBoxHandler::m_instance = 0; MessageBoxHandler::MessageBoxHandler(QObject *parent) : QObject(parent) , m_defaultAction(MessageBoxHandler::AskUser) { } /*! Returns a message box handler instance. */ MessageBoxHandler *MessageBoxHandler::instance() { if (m_instance == 0) m_instance = new MessageBoxHandler(qApp); return m_instance; } /*! Returns the widget or window that is most suitable to become the parent of the message box. Returns \c 0 if an application cannot be found. */ QWidget *MessageBoxHandler::currentBestSuitParent() { if (qobject_cast (qApp) == 0) return 0; if (qApp->activeModalWidget()) return qApp->activeModalWidget(); return qApp->activeWindow(); } /*! Returns an ordered list of buttons to display in the message box. */ QList MessageBoxHandler::orderedButtons() { static QList buttons; if (!buttons.isEmpty()) return buttons; buttons << QMessageBox::YesToAll << QMessageBox::Yes << QMessageBox::Ok << QMessageBox::Apply << QMessageBox::SaveAll << QMessageBox::Save <showMessageBox(criticalType, parent, identifier, title, text, buttons, button); } /*! Opens an information message box with the parent \a parent, identifier \a identifier, title \a title, and text \a text. The standard buttons specified by \a buttons are added to the message box. \a button specifies the button that is used when \key Enter is pressed. \a button must refer to a button that is specified in \a buttons. If \a button is QMessageBox::NoButton, a suitable default is chosen automatically. Returns the identity of the standard button that was clicked. However, if \key Esc was pressed, returns the escape button. */ QMessageBox::StandardButton MessageBoxHandler::information(QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton button) { return instance()->showMessageBox(informationType, parent, identifier, title, text, buttons, button); } /*! Opens a question message box with the parent \a parent, identifier \a identifier, title \a title, and text \a text. The standard buttons specified by \a buttons are added to the message box. \a button specifies the button that is used when \key Enter is pressed. \a button must refer to a button that is specified in \a buttons. If \a button is QMessageBox::NoButton, a suitable default is chosen automatically. Returns the identity of the standard button that was clicked. However, if \key Esc was pressed, returns the escape button. */ QMessageBox::StandardButton MessageBoxHandler::question(QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton button) { return instance()->showMessageBox(questionType, parent, identifier, title, text, buttons, button); } /*! Opens a warning message box with the parent \a parent, identifier \a identifier, title \a title, and text \a text. The standard buttons specified by \a buttons are added to the message box. \a button specifies the button that is used when \key Enter is pressed. \a button must refer to a button that is specified in \a buttons. If \a button is QMessageBox::NoButton, a suitable default is chosen automatically. Returns the identity of the standard button that was clicked. However, if \key Esc was pressed, returns the escape button. */ QMessageBox::StandardButton MessageBoxHandler::warning(QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton button) { return instance()->showMessageBox(warningType, parent, identifier, title, text, buttons, button); } // -- invokable /*! Opens a critical message box with the identifier \a identifier, title \a title, and text \a text. The standard buttons specified by \a buttons are added to the message box. \a button specifies the button that is used when \key Enter is pressed. \a button must refer to a button that is specified in \a buttons. If \a button is QMessageBox::NoButton, a suitable default is chosen automatically. Returns the identity of the standard button that was clicked. However, if \key Esc was pressed, returns the escape button. */ int MessageBoxHandler::critical(const QString &identifier, const QString &title, const QString &text, int buttons, int button) const { return showMessageBox(criticalType, currentBestSuitParent(), identifier, title, text, QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button)); } /*! Opens an information message box with the identifier \a identifier, title \a title, and text \a text. The standard buttons specified by \a buttons are added to the message box. \a button specifies the button that is used when \key Enter is pressed. \a button must refer to a button that is specified in \a buttons. If \a button is QMessageBox::NoButton, a suitable default is chosen automatically. Returns the identity of the standard button that was clicked. However, if \key Esc was pressed, returns the escape button. */ int MessageBoxHandler::information(const QString &identifier, const QString &title, const QString &text, int buttons, int button) const { return showMessageBox(informationType, currentBestSuitParent(), identifier, title, text, QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button)); } /*! Opens a question message box with the identifier \a identifier, title \a title, and text \a text. The standard buttons specified by \a buttons are added to the message box. \a button specifies the button that is used when \key Enter is pressed. \a button must refer to a button that is specified in \a buttons. If \a button is QMessageBox::NoButton, a suitable default is chosen automatically. Returns the identity of the standard button that was clicked. However, if \key Esc was pressed, returns the escape button. */ int MessageBoxHandler::question(const QString &identifier, const QString &title, const QString &text, int buttons, int button) const { return showMessageBox(questionType, currentBestSuitParent(), identifier, title, text, QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button)); } /*! Opens a warning message box with the identifier \a identifier, title \a title, and text \a text. The standard buttons specified by \a buttons are added to the message box. \a button specifies the button that is used when \key Enter is pressed. \a button must refer to a button that is specified in \a buttons. If \a button is QMessageBox::NoButton, a suitable default is chosen automatically. Returns the identity of the standard button that was clicked. However, if \key Esc was pressed, returns the escape button. */ int MessageBoxHandler::warning(const QString &identifier, const QString &title, const QString &text, int buttons, int button) const { return showMessageBox(warningType, currentBestSuitParent(), identifier, title, text, QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button)); } // -- private QMessageBox::StandardButton MessageBoxHandler::autoReply(QMessageBox::StandardButtons buttons) const { if (buttons == QMessageBox::NoButton) return QMessageBox::NoButton; foreach (const QMessageBox::StandardButton ¤tButton, m_buttonOrder) { if ((buttons & currentButton) != 0) return currentButton; } Q_ASSERT(!"the list must have all possible buttons"); return QMessageBox::NoButton; } static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBox::Icon icon, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent); QDialogButtonBox *buttonBox = msgBox.findChild(); Q_ASSERT(buttonBox != 0); uint mask = QMessageBox::FirstButton; while (mask <= QMessageBox::LastButton) { uint sb = buttons & mask; mask <<= 1; if (!sb) continue; QPushButton *button = msgBox.addButton((QMessageBox::StandardButton)sb); // Choose the first accept role as the default if (msgBox.defaultButton()) continue; if ((defaultButton == QMessageBox::NoButton && buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) || (defaultButton != QMessageBox::NoButton && sb == uint(defaultButton))) { msgBox.setDefaultButton(button); } } #if defined(Q_OS_OSX) msgBox.setWindowModality(Qt::WindowModal); #endif if (msgBox.exec() == -1) return QMessageBox::Cancel; return msgBox.standardButton(msgBox.clickedButton()); } QMessageBox::StandardButton MessageBoxHandler::showMessageBox(MessageType messageType, QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) const { static QHash messageTypeHash; if (messageTypeHash.isEmpty()) { messageTypeHash.insert(criticalType, QLatin1String("critical")); messageTypeHash.insert(informationType, QLatin1String("information")); messageTypeHash.insert(questionType, QLatin1String("question")); messageTypeHash.insert(warningType, QLatin1String("warning")); }; qDebug().nospace() << "Created " << messageTypeHash.value(messageType).toUtf8().constData() << " message box " << identifier << ": " << title << ", " << text; if (qobject_cast (qApp) == 0) return defaultButton; if (m_automaticAnswers.contains(identifier)) return m_automaticAnswers.value(identifier); if (m_defaultAction == AskUser) { switch (messageType) { case criticalType: return showNewMessageBox(parent, QMessageBox::Critical, title, text, buttons, defaultButton); case informationType: return showNewMessageBox(parent, QMessageBox::Information, title, text, buttons, defaultButton); case questionType: return showNewMessageBox(parent, QMessageBox::Question, title, text, buttons, defaultButton); case warningType: return showNewMessageBox(parent, QMessageBox::Warning, title, text, buttons, defaultButton); } } else { return autoReply(buttons); } Q_ASSERT_X(false, Q_FUNC_INFO, "Something went really wrong."); return defaultButton; } src/libs/installer/messageboxhandler.h000066400000000000000000000115261325366651500204600ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QINSTALLER_MESSAGEBOXHANDLER_H #define QINSTALLER_MESSAGEBOXHANDLER_H #include #include #include #include namespace QInstaller { class INSTALLER_EXPORT MessageBoxHandler : public QObject { Q_OBJECT public: enum DefaultAction { AskUser, Accept, Reject }; enum MessageType{ criticalType, informationType, questionType, warningType }; static MessageBoxHandler *instance(); static QWidget *currentBestSuitParent(); void setDefaultAction(DefaultAction defaultAction); void setAutomaticAnswer(const QString &identifier, QMessageBox::StandardButton answer); static QMessageBox::StandardButton critical(QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton button = QMessageBox::NoButton); static QMessageBox::StandardButton information(QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton button=QMessageBox::NoButton); static QMessageBox::StandardButton question(QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No, QMessageBox::StandardButton button = QMessageBox::NoButton); static QMessageBox::StandardButton warning(QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton button = QMessageBox::NoButton); Q_INVOKABLE int critical(const QString &identifier, const QString &title, const QString &text, int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton) const; Q_INVOKABLE int information(const QString &identifier, const QString &title, const QString &text, int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton) const; Q_INVOKABLE int question(const QString &identifier, const QString &title, const QString &text, int buttons = QMessageBox::Yes | QMessageBox::No, int button = QMessageBox::NoButton) const; Q_INVOKABLE int warning(const QString &identifier, const QString &title, const QString &text, int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton) const; static QList orderedButtons(); private Q_SLOTS: //this removes the slot from the script area virtual void deleteLater() { QObject::deleteLater(); } private: explicit MessageBoxHandler(QObject *parent); QMessageBox::StandardButton autoReply(QMessageBox::StandardButtons buttons) const; QMessageBox::StandardButton showMessageBox(MessageType messageType, QWidget *parent, const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) const; private: static MessageBoxHandler *m_instance; DefaultAction m_defaultAction; QList m_buttonOrder; QHash m_automaticAnswers; }; } Q_DECLARE_METATYPE(QMessageBox::StandardButton) Q_DECLARE_METATYPE(QMessageBox::StandardButtons) #endif // QINSTALLER_MESSAGEBOXHANDLER_H src/libs/installer/metadatajob.cpp000066400000000000000000000645501325366651500176000ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "metadatajob.h" #include "metadatajob_p.h" #include "packagemanagercore.h" #include "packagemanagerproxyfactory.h" #include "productkeycheck.h" #include "proxycredentialsdialog.h" #include "serverauthenticationdialog.h" #include "settings.h" #include "testrepository.h" #include namespace QInstaller { static QUrl resolveUrl(const FileTaskResult &result, const QString &url) { QUrl u(url); if (u.isRelative()) return QUrl(result.taskItem().source()).resolved(u); return u; } MetadataJob::MetadataJob(QObject *parent) : Job(parent) , m_core(0) , m_addCompressedPackages(false) { setCapabilities(Cancelable); connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &MetadataJob::xmlTaskFinished); connect(&m_metadataTask, &QFutureWatcherBase::finished, this, &MetadataJob::metadataTaskFinished); connect(&m_metadataTask, &QFutureWatcherBase::progressValueChanged, this, &MetadataJob::progressChanged); } MetadataJob::~MetadataJob() { resetCompressedFetch(); reset(); } Repository MetadataJob::repositoryForDirectory(const QString &directory) const { return m_metadata.value(directory).repository; } // -- private slots void MetadataJob::doStart() { if (!m_core) { emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine.")); return; // We can't do anything here without core, so avoid tons of !m_core checks. } const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance(); if (!m_addCompressedPackages) { reset(); emit infoMessage(this, tr("Preparing meta information download...")); const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly(); if (onlineInstaller || m_core->isMaintainer()) { QList items; foreach (const Repository &repo, m_core->settings().repositories()) { if (repo.isEnabled() && productKeyCheck->isValidRepository(repo)) { QAuthenticator authenticator; authenticator.setUser(repo.username()); authenticator.setPassword(repo.password()); if (!repo.isCompressed()) { QString url = repo.url().toString() + QLatin1String("/Updates.xml?"); if (!m_core->value(scUrlQueryString).isEmpty()) url += m_core->value(scUrlQueryString) + QLatin1Char('&'); // also append a random string to avoid proxy caches FileTaskItem item(url.append(QString::number(qrand() * qrand()))); item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator)); items.append(item); } else { qDebug() << "Trying to parse compressed repo as normal repository."\ "Check repository syntax."; } } } if (items.count() > 0) { startXMLTask(items); } else { emitFinished(); } } else { emitFinished(); } } else { resetCompressedFetch(); bool repositoriesFound = false; foreach (const Repository &repo, m_core->settings().temporaryRepositories()) { if (repo.isCompressed() && repo.isEnabled() && productKeyCheck->isValidRepository(repo)) { repositoriesFound = true; startUnzipRepositoryTask(repo); //Set the repository disabled so we don't handle it many times Repository replacement = repo; replacement.setEnabled(false); Settings &s = m_core->settings(); QSet temporaries = s.temporaryRepositories(); if (temporaries.contains(repo)) { temporaries.remove(repo); temporaries.insert(replacement); s.setTemporaryRepositories(temporaries, true); } } } if (!repositoriesFound) { emitFinished(); } else { setProgressTotalAmount(0); emit infoMessage(this, tr("Unpacking compressed repositories. This may take a while...")); } } } void MetadataJob::startXMLTask(const QList items) { DownloadFileTask *const xmlTask = new DownloadFileTask(items); xmlTask->setProxyFactory(m_core->proxyFactory()); m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); } void MetadataJob::doCancel() { reset(); emitFinishedWithError(Job::Canceled, tr("Meta data download canceled.")); } void MetadataJob::startUnzipRepositoryTask(const Repository &repo) { QTemporaryDir tempRepoDir(QDir::tempPath() + QLatin1String("/compressedRepo-XXXXXX")); if (!tempRepoDir.isValid()) { qDebug() << "Cannot create unique temporary directory."; return; } tempRepoDir.setAutoRemove(false); m_tempDirDeleter.add(tempRepoDir.path()); QString url = repo.url().toLocalFile(); UnzipArchiveTask *task = new UnzipArchiveTask(url, tempRepoDir.path()); QFutureWatcher *watcher = new QFutureWatcher(); m_unzipRepositoryTasks.insert(watcher, qobject_cast (task)); connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipRepositoryTaskFinished); connect(watcher, &QFutureWatcherBase::progressValueChanged, this, &MetadataJob::progressChanged); watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); } void MetadataJob::unzipRepositoryTaskFinished() { QFutureWatcher *watcher = static_cast *>(sender()); int error = Job::NoError; QString errorString; try { watcher->waitForFinished(); // trigger possible exceptions QHashIterator *, QObject*> i(m_unzipRepositoryTasks); while (i.hasNext()) { i.next(); if (i.key() == watcher) { UnzipArchiveTask *task = qobject_cast (i.value()); QString url = task->target(); QUrl targetUrl = targetUrl.fromLocalFile(url); Repository repo(targetUrl, false, true); url = repo.url().toString() + QLatin1String("/Updates.xml"); TestRepository testJob(m_core); testJob.setRepository(repo); testJob.start(); testJob.waitForFinished(); error = testJob.error(); errorString = testJob.errorString(); if (error == Job::NoError) { FileTaskItem item(url); item.insert(TaskRole::UserRole, QVariant::fromValue(repo)); m_unzipRepositoryitems.append(item); } else { //Repository is not valid, remove it Repository repository; repository.setUrl(QUrl(task->archive())); Settings &s = m_core->settings(); QSet temporaries = s.temporaryRepositories(); foreach (Repository repository, temporaries) { if (repository.url().toLocalFile() == task->archive()) { temporaries.remove(repository); } } s.setTemporaryRepositories(temporaries, false); } } } delete m_unzipRepositoryTasks.value(watcher); m_unzipRepositoryTasks.remove(watcher); delete watcher; //One can specify many zipped repository items at once. As the repositories are //unzipped one by one, we collect here all items before parsing xml files from those. if (m_unzipRepositoryitems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) { startXMLTask(m_unzipRepositoryitems); } else { if (error != Job::NoError) { emitFinishedWithError(QInstaller::DownloadError, errorString); } } } catch (const UnzipArchiveException &e) { reset(); emitFinishedWithError(QInstaller::ExtractionError, e.message()); } catch (const QUnhandledException &e) { reset(); emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); } catch (...) { reset(); emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during extracting.")); } } void MetadataJob::xmlTaskFinished() { Status status = XmlDownloadFailure; try { m_xmlTask.waitForFinished(); status = parseUpdatesXml(m_xmlTask.future().results()); } catch (const AuthenticationRequiredException &e) { if (e.type() == AuthenticationRequiredException::Type::Proxy) { const QNetworkProxy proxy = e.proxy(); ProxyCredentialsDialog proxyCredentials(proxy); qDebug().noquote() << e.message(); if (proxyCredentials.exec() == QDialog::Accepted) { qDebug() << "Retrying with new credentials ..."; PackageManagerProxyFactory *factory = m_core->proxyFactory(); factory->setProxyCredentials(proxy, proxyCredentials.userName(), proxyCredentials.password()); m_core->setProxyFactory(factory); status = XmlDownloadRetry; } else { reset(); emitFinishedWithError(QInstaller::DownloadError, tr("Missing proxy credentials.")); } } else if (e.type() == AuthenticationRequiredException::Type::Server) { qDebug().noquote() << e.message(); ServerAuthenticationDialog dlg(e.message(), e.taskItem()); if (dlg.exec() == QDialog::Accepted) { Repository original = e.taskItem().value(TaskRole::UserRole) .value(); Repository replacement = original; replacement.setUsername(dlg.user()); replacement.setPassword(dlg.password()); Settings &s = m_core->settings(); QSet temporaries = s.temporaryRepositories(); if (temporaries.contains(original)) { temporaries.remove(original); temporaries.insert(replacement); s.addTemporaryRepositories(temporaries, true); } else { QHash > update; update.insert(QLatin1String("replace"), qMakePair(original, replacement)); if (s.updateDefaultRepositories(update) == Settings::UpdatesApplied || s.updateUserRepositories(update) == Settings::UpdatesApplied) { if (m_core->isMaintainer()) m_core->writeMaintenanceConfigFiles(); } } status = XmlDownloadRetry; } else { reset(); emitFinishedWithError(QInstaller::DownloadError, tr("Authentication failed.")); } } } catch (const TaskException &e) { reset(); emitFinishedWithError(QInstaller::DownloadError, e.message()); } catch (const QUnhandledException &e) { reset(); emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); } catch (...) { reset(); emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during download.")); } if (error() != Job::NoError) return; if (status == XmlDownloadSuccess) { setProcessedAmount(0); DownloadFileTask *const metadataTask = new DownloadFileTask(m_packages); metadataTask->setProxyFactory(m_core->proxyFactory()); m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask)); setProgressTotalAmount(100); emit infoMessage(this, tr("Retrieving meta information from remote repository...")); } else if (status == XmlDownloadRetry) { QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); } else { reset(); emitFinishedWithError(QInstaller::DownloadError, tr("Failure to fetch repositories.")); } } void MetadataJob::unzipTaskFinished() { QFutureWatcher *watcher = static_cast *>(sender()); try { watcher->waitForFinished(); // trigger possible exceptions } catch (const UnzipArchiveException &e) { emitFinishedWithError(QInstaller::ExtractionError, e.message()); } catch (const QUnhandledException &e) { emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); } catch (...) { emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during extracting.")); } if (error() != Job::NoError) return; delete m_unzipTasks.value(watcher); m_unzipTasks.remove(watcher); delete watcher; if (m_unzipTasks.isEmpty()) { setProcessedAmount(100); emitFinished(); } } void MetadataJob::progressChanged(int progress) { setProcessedAmount(progress); } void MetadataJob::setProgressTotalAmount(int maximum) { setTotalAmount(maximum); } void MetadataJob::metadataTaskFinished() { try { m_metadataTask.waitForFinished(); QFuture future = m_metadataTask.future(); if (future.resultCount() > 0) { emit infoMessage(this, tr("Extracting meta information...")); foreach (const FileTaskResult &result, future.results()) { const FileTaskItem item = result.value(TaskRole::TaskItem).value(); UnzipArchiveTask *task = new UnzipArchiveTask(result.target(), item.value(TaskRole::UserRole).toString()); QFutureWatcher *watcher = new QFutureWatcher(); m_unzipTasks.insert(watcher, qobject_cast (task)); connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished); watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); } } else { emitFinished(); } } catch (const TaskException &e) { reset(); emitFinishedWithError(QInstaller::DownloadError, e.message()); } catch (const QUnhandledException &e) { reset(); emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); } catch (...) { reset(); emitFinishedWithError(QInstaller::DownloadError, tr("Unknown exception during download.")); } } // -- private void MetadataJob::reset() { m_packages.clear(); m_metadata.clear(); setError(Job::NoError); setErrorString(QString()); setCapabilities(Cancelable); try { m_xmlTask.cancel(); m_metadataTask.cancel(); } catch (...) {} m_tempDirDeleter.releaseAndDeleteAll(); } void MetadataJob::resetCompressedFetch() { setError(Job::NoError); setErrorString(QString()); m_unzipRepositoryitems.clear(); try { foreach (QFutureWatcher *const watcher, m_unzipTasks.keys()) { watcher->cancel(); watcher->deleteLater(); } foreach (QObject *const object, m_unzipTasks) object->deleteLater(); m_unzipTasks.clear(); foreach (QFutureWatcher *const watcher, m_unzipRepositoryTasks.keys()) { watcher->cancel(); watcher->deleteLater(); } foreach (QObject *const object, m_unzipRepositoryTasks) object->deleteLater(); m_unzipRepositoryTasks.clear(); } catch (...) {} } MetadataJob::Status MetadataJob::parseUpdatesXml(const QList &results) { foreach (const FileTaskResult &result, results) { if (error() != Job::NoError) return XmlDownloadFailure; //If repository is not found, target might be empty. Do not continue parsing the //repository and do not prevent further repositories usage. if (result.target().isEmpty()) { continue; } Metadata metadata; QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX")); if (!tmp.isValid()) { qDebug() << "Cannot create unique temporary directory."; return XmlDownloadFailure; } tmp.setAutoRemove(false); metadata.directory = tmp.path(); m_tempDirDeleter.add(metadata.directory); QFile file(result.target()); if (!file.rename(metadata.directory + QLatin1String("/Updates.xml"))) { qDebug() << "Cannot rename target to Updates.xml:" << file.errorString(); return XmlDownloadFailure; } if (!file.open(QIODevice::ReadOnly)) { qDebug() << "Cannot open Updates.xml for reading:" << file.errorString(); return XmlDownloadFailure; } QString error; QDomDocument doc; if (!doc.setContent(&file, &error)) { qDebug().nospace() << "Cannot fetch a valid version of Updates.xml from repository " << metadata.repository.displayname() << ": " << error; //If there are other repositories, try to use those continue; } file.close(); const FileTaskItem item = result.value(TaskRole::TaskItem).value(); metadata.repository = item.value(TaskRole::UserRole).value(); const bool online = !(metadata.repository.url().scheme()).isEmpty(); bool testCheckSum = true; const QDomElement root = doc.documentElement(); const QDomNode checksum = root.firstChildElement(QLatin1String("Checksum")); if (!checksum.isNull()) testCheckSum = (checksum.toElement().text().toLower() == scTrue); QDomNodeList children = root.childNodes(); for (int i = 0; i < children.count(); ++i) { const QDomElement el = children.at(i).toElement(); if (!el.isNull() && el.tagName() == QLatin1String("PackageUpdate")) { const QDomNodeList c2 = el.childNodes(); QString packageName, packageVersion, packageHash; for (int j = 0; j < c2.count(); ++j) { if (c2.at(j).toElement().tagName() == scName) packageName = c2.at(j).toElement().text(); else if (c2.at(j).toElement().tagName() == scVersion) packageVersion = (online ? c2.at(j).toElement().text() : QString()); else if ((c2.at(j).toElement().tagName() == QLatin1String("SHA1")) && testCheckSum) packageHash = c2.at(j).toElement().text(); } const QString repoUrl = metadata.repository.url().toString(); FileTaskItem item(QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, packageName, packageVersion), metadata.directory + QString::fromLatin1("/%1-%2-meta.7z") .arg(packageName, packageVersion)); QAuthenticator authenticator; authenticator.setUser(metadata.repository.username()); authenticator.setPassword(metadata.repository.password()); item.insert(TaskRole::UserRole, metadata.directory); item.insert(TaskRole::Checksum, packageHash.toLatin1()); item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator)); m_packages.append(item); } } m_metadata.insert(metadata.directory, metadata); // search for additional repositories that we might need to check const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate")); if (repositoryUpdate.isNull()) continue; QHash > repositoryUpdates; children = repositoryUpdate.toElement().childNodes(); for (int i = 0; i < children.count(); ++i) { const QDomElement el = children.at(i).toElement(); if (!el.isNull() && el.tagName() == QLatin1String("Repository")) { const QString action = el.attribute(QLatin1String("action")); if (action == QLatin1String("add")) { // add a new repository to the defaults list Repository repository(resolveUrl(result, el.attribute(QLatin1String("url"))), true); repository.setUsername(el.attribute(QLatin1String("username"))); repository.setPassword(el.attribute(QLatin1String("password"))); repository.setDisplayName(el.attribute(QLatin1String("displayname"))); if (ProductKeyCheck::instance()->isValidRepository(repository)) { repositoryUpdates.insertMulti(action, qMakePair(repository, Repository())); qDebug() << "Repository to add:" << repository.displayname(); } } else if (action == QLatin1String("remove")) { // remove possible default repositories using the given server url Repository repository(resolveUrl(result, el.attribute(QLatin1String("url"))), true); repository.setDisplayName(el.attribute(QLatin1String("displayname"))); repositoryUpdates.insertMulti(action, qMakePair(repository, Repository())); qDebug() << "Repository to remove:" << repository.displayname(); } else if (action == QLatin1String("replace")) { // replace possible default repositories using the given server url Repository oldRepository(resolveUrl(result, el.attribute(QLatin1String("oldUrl"))), true); Repository newRepository(resolveUrl(result, el.attribute(QLatin1String("newUrl"))), true); newRepository.setUsername(el.attribute(QLatin1String("username"))); newRepository.setPassword(el.attribute(QLatin1String("password"))); newRepository.setDisplayName(el.attribute(QLatin1String("displayname"))); if (ProductKeyCheck::instance()->isValidRepository(newRepository)) { // store the new repository and the one old it replaces repositoryUpdates.insertMulti(action, qMakePair(newRepository, oldRepository)); qDebug() << "Replace repository" << oldRepository.displayname() << "with" << newRepository.displayname(); } } else { qDebug() << "Invalid additional repositories action set in Updates.xml fetched " "from" << metadata.repository.displayname() << "line:" << el.lineNumber(); } } } if (!repositoryUpdates.isEmpty()) { Settings &s = m_core->settings(); const QSet temporaries = s.temporaryRepositories(); // in case the temp repository introduced something new, we only want that temporary if (temporaries.contains(metadata.repository)) { QSet tmpRepositories; typedef QPair RepositoryPair; QList values = repositoryUpdates.values(QLatin1String("add")); foreach (const RepositoryPair &value, values) tmpRepositories.insert(value.first); values = repositoryUpdates.values(QLatin1String("replace")); foreach (const RepositoryPair &value, values) tmpRepositories.insert(value.first); tmpRepositories = tmpRepositories.subtract(temporaries); if (tmpRepositories.count() > 0) { s.addTemporaryRepositories(tmpRepositories, true); QFile::remove(result.target()); return XmlDownloadRetry; } } else if (s.updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) { if (m_core->isMaintainer()) m_core->writeMaintenanceConfigFiles(); QFile::remove(result.target()); return XmlDownloadRetry; } } } return XmlDownloadSuccess; } } // namespace QInstaller src/libs/installer/metadatajob.h000066400000000000000000000063341325366651500172410ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef METADATAJOB_H #define METADATAJOB_H #include "downloadfiletask.h" #include "fileutils.h" #include "job.h" #include "repository.h" #include namespace QInstaller { class PackageManagerCore; struct Metadata { QString directory; Repository repository; }; class INSTALLER_EXPORT MetadataJob : public Job { Q_OBJECT Q_DISABLE_COPY(MetadataJob) enum Status { XmlDownloadRetry, XmlDownloadFailure, XmlDownloadSuccess }; public: explicit MetadataJob(QObject *parent = 0); ~MetadataJob(); QList metadata() const { return m_metadata.values(); } Repository repositoryForDirectory(const QString &directory) const; void setPackageManagerCore(PackageManagerCore *core) { m_core = core; } void addCompressedPackages(bool addCompressPackage) { m_addCompressedPackages = addCompressPackage;} private slots: void doStart(); void doCancel(); void xmlTaskFinished(); void unzipTaskFinished(); void metadataTaskFinished(); void progressChanged(int progress); void setProgressTotalAmount(int maximum); void unzipRepositoryTaskFinished(); void startXMLTask(const QList items); private: void startUnzipRepositoryTask(const Repository &repo); void reset(); void resetCompressedFetch(); Status parseUpdatesXml(const QList &results); private: PackageManagerCore *m_core; QList m_packages; TempDirDeleter m_tempDirDeleter; QHash m_metadata; QFutureWatcher m_xmlTask; QFutureWatcher m_metadataTask; QHash *, QObject*> m_unzipTasks; QHash *, QObject*> m_unzipRepositoryTasks; bool m_addCompressedPackages; QList m_unzipRepositoryitems; }; } // namespace QInstaller #endif // METADATAJOB_H src/libs/installer/metadatajob_p.h000066400000000000000000000070121325366651500175520ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef METADATAJOB_P_H #define METADATAJOB_P_H #include "lib7z_extract.h" #include "lib7z_facade.h" #include "metadatajob.h" #include #include namespace QInstaller{ class UnzipArchiveException : public QException { public: UnzipArchiveException() {} ~UnzipArchiveException() throw() {} explicit UnzipArchiveException(const QString &message) : m_message(message) {} void raise() const { throw *this; } QString message() const { return m_message; } UnzipArchiveException *clone() const { return new UnzipArchiveException(*this); } private: QString m_message; }; class UnzipArchiveTask : public AbstractTask { Q_OBJECT Q_DISABLE_COPY(UnzipArchiveTask) public: UnzipArchiveTask(const QString &arcive, const QString &target) : m_archive(arcive), m_targetDir(target) {} QString target() { return m_targetDir; } QString archive() { return m_archive; } void doTask(QFutureInterface &fi) { fi.reportStarted(); fi.setExpectedResultCount(1); if (fi.isCanceled()) { fi.reportFinished(); return; // ignore already canceled } QFile archive(m_archive); if (archive.open(QIODevice::ReadOnly)) { try { Lib7z::extractArchive(&archive, m_targetDir); } catch (const Lib7z::SevenZipException& e) { fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting " "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), e.message()))); } catch (...) { fi.reportException(UnzipArchiveException(MetadataJob::tr("Unknown exception " "caught while extracting archive \"%1\".").arg(QDir::toNativeSeparators(m_archive)))); } } else { fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for " "reading: %2").arg(QDir::toNativeSeparators(m_archive), archive.errorString()))); } fi.reportFinished(); } private: QString m_archive; QString m_targetDir; }; } // namespace QInstaller #endif // METADATAJOB_P_H src/libs/installer/minimumprogressoperation.cpp000066400000000000000000000036161325366651500225020ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "minimumprogressoperation.h" using namespace QInstaller; MinimumProgressOperation::MinimumProgressOperation(PackageManagerCore *core) : UpdateOperation(core) { // This shouldn't be callable by script, but we need a name for the binary format setName(QLatin1String("MinimumProgress")); } void MinimumProgressOperation::backup() { } bool MinimumProgressOperation::performOperation() { progressChanged(1); return true; } bool MinimumProgressOperation::undoOperation() { progressChanged(1); return true; } bool MinimumProgressOperation::testOperation() { return true; } src/libs/installer/minimumprogressoperation.h000066400000000000000000000034701325366651500221450ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef MINIMUMPROGRESSOPERATION_H #define MINIMUMPROGRESSOPERATION_H #include "qinstallerglobal.h" #include namespace QInstaller { class MinimumProgressOperation : public QObject, public Operation { Q_OBJECT public: explicit MinimumProgressOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); signals: void progressChanged(double progress); }; } // namespace QInstaller #endif // MINIMUMPROGRESSOPERATION_H src/libs/installer/observer.cpp000066400000000000000000000126621325366651500171510ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "observer.h" #include namespace QInstaller { FileTaskObserver::FileTaskObserver(QCryptographicHash::Algorithm algorithm) : m_hash(algorithm) { init(); } FileTaskObserver::~FileTaskObserver() { if (m_timerId >= 0) killTimer(m_timerId); } int FileTaskObserver::progressValue() const { if (m_bytesToTransfer <= 0 || m_bytesTransfered > m_bytesToTransfer) return 0; return 100 * m_bytesTransfered / m_bytesToTransfer; } QString FileTaskObserver::progressText() const { QString progressText; if (m_bytesToTransfer > 0) { QString bytesReceived = QInstaller::humanReadableSize(m_bytesTransfered); const QString bytesToReceive = QInstaller::humanReadableSize(m_bytesToTransfer); // remove the unit from the bytesReceived value if bytesToReceive has the same const QString tmp = bytesToReceive.mid(bytesToReceive.indexOf(QLatin1Char(' '))); if (bytesReceived.endsWith(tmp)) bytesReceived.chop(tmp.length()); progressText = tr("%1 of %2").arg(bytesReceived).arg(bytesToReceive); } else { if (m_bytesTransfered > 0) progressText = tr("%1 received.").arg(QInstaller::humanReadableSize(m_bytesTransfered)); } if (!progressText.isEmpty()) progressText += QLatin1Char(' '); progressText += tr("(%1/sec)").arg(QInstaller::humanReadableSize(m_bytesPerSecond)); if (m_bytesToTransfer > 0 && m_bytesPerSecond > 0) { const qint64 time = (m_bytesToTransfer - m_bytesTransfered) / m_bytesPerSecond; int s = time % 60; const int d = time / 86400; const int h = (time / 3600) - (d * 24); const int m = (time / 60) - (d * 1440) - (h * 60); QString days; if (d > 0) days = tr("%n day(s), ", "", d); QString hours; if (h > 0) hours = tr("%n hour(s), ", "", h); QString minutes; if (m > 0) minutes = tr("%n minute(s)", "", m); QString seconds; if (s >= 0 && minutes.isEmpty()) { s = (s <= 0 ? 1 : s); seconds = tr("%n second(s)", "", s); } progressText += tr(" - %1%2%3%4 remaining.").arg(days).arg(hours).arg(minutes).arg(seconds); } else { progressText += tr(" - unknown time remaining."); } return progressText; } QByteArray FileTaskObserver::checkSum() const { return m_hash.result(); } void FileTaskObserver::addCheckSumData(const char *data, int length) { m_hash.addData(data, length); } void FileTaskObserver::addSample(qint64 sample) { m_currentSpeedBin += sample; } void FileTaskObserver::setBytesTransfered(qint64 bytesReceived) { m_bytesTransfered = bytesReceived; } void FileTaskObserver::addBytesTransfered(qint64 bytesReceived) { m_bytesTransfered += bytesReceived; } void FileTaskObserver::setBytesToTransfer(qint64 bytesToReceive) { m_bytesToTransfer = bytesToReceive; } // -- private void FileTaskObserver::init() { m_hash.reset(); m_sampleIndex = 0; m_bytesTransfered = 0; m_bytesToTransfer = 0; m_bytesPerSecond = 0; m_currentSpeedBin = 0; m_timerId = -1; m_timerInterval = 100; memset(m_samples, 0, sizeof(m_samples)); m_timerId = startTimer(m_timerInterval); } void FileTaskObserver::timerEvent(QTimerEvent *event) { Q_UNUSED(event) unsigned int windowSize = sizeof(m_samples) / sizeof(qint64); // add speed of last time bin to the window m_samples[m_sampleIndex % windowSize] = m_currentSpeedBin; m_currentSpeedBin = 0; // reset bin for next time interval // advance the sample index m_sampleIndex++; m_bytesPerSecond = 0; // dynamic window size until the window is completely filled if (m_sampleIndex < windowSize) windowSize = m_sampleIndex; for (unsigned int i = 0; i < windowSize; ++i) m_bytesPerSecond += m_samples[i]; m_bytesPerSecond /= windowSize; // computer average m_bytesPerSecond *= 1000.0 / m_timerInterval; // rescale to bytes/second } } // namespace QInstaller src/libs/installer/observer.h000066400000000000000000000050671325366651500166170ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef OBSERVER_H #define OBSERVER_H #include #include namespace QInstaller { class Observer : public QObject { Q_OBJECT Q_DISABLE_COPY(Observer) public: Observer() {} virtual ~Observer() {} virtual int progressValue() const = 0; virtual QString progressText() const = 0; }; class FileTaskObserver : public Observer { Q_OBJECT Q_DISABLE_COPY(FileTaskObserver) public: FileTaskObserver(QCryptographicHash::Algorithm algorithm); ~FileTaskObserver(); int progressValue() const; QString progressText() const; QByteArray checkSum() const; void addCheckSumData(const char *data, int length); void addSample(qint64 sample); void timerEvent(QTimerEvent *event); void setBytesTransfered(qint64 bytesTransfered); void addBytesTransfered(qint64 bytesTransfered); void setBytesToTransfer(qint64 bytesToTransfer); private: void init(); private: int m_timerId; int m_timerInterval; qint64 m_bytesTransfered; qint64 m_bytesToTransfer; qint64 m_samples[50]; quint32 m_sampleIndex; qint64 m_bytesPerSecond; qint64 m_currentSpeedBin; QCryptographicHash m_hash; }; } // namespace QInstaller #endif // OBSERVER_H src/libs/installer/packagemanagercore.cpp000066400000000000000000003151141325366651500211170ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "packagemanagercore.h" #include "packagemanagercore_p.h" #include "adminauthorization.h" #include "binarycontent.h" #include "component.h" #include "componentmodel.h" #include "downloadarchivesjob.h" #include "errors.h" #include "globals.h" #include "messageboxhandler.h" #include "packagemanagerproxyfactory.h" #include "progresscoordinator.h" #include "qprocesswrapper.h" #include "qsettingswrapper.h" #include "remoteclient.h" #include "remotefileengine.h" #include "settings.h" #include "utils.h" #include "installercalculator.h" #include "uninstallercalculator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sysinfo.h" #include "updateoperationfactory.h" #ifdef Q_OS_WIN # include "qt_windows.h" #endif #include /*! \namespace QInstaller \inmodule QtInstallerFramework \keyword qinstaller-module \brief Contains classes to implement the core functionality of the Qt Installer Framework and the installer UI. */ using namespace QInstaller; /*! \class QInstaller::PackageManagerCore \inmodule QtInstallerFramework \brief The PackageManagerCore class provides the core functionality of the Qt Installer Framework. */ /*! \enum PackageManagerCore::WizardPage This enum type holds the pre-defined package manager pages: \value Introduction \l{Introduction Page} \value TargetDirectory \l{Target Directory Page} \value ComponentSelection \l{Component Selection Page} \value LicenseCheck \l{License Agreement Page} \value StartMenuSelection \l{Start Menu Directory Page} \value ReadyForInstallation \l{Ready for Installation Page} \value PerformInstallation \l{Perform Installation Page} \value InstallationFinished \l{Finished Page} \omitvalue End */ /*! \enum PackageManagerCore::Status This enum type holds the package manager status: \value Success Installation was successful. \value Failure Installation failed. \value Running Installation is in progress. \value Canceled Installation was canceled. \value Unfinished Installation was not completed. \value ForceUpdate */ /*! \property PackageManagerCore::status \brief Installation status. */ /*! \fn PackageManagerCore::aboutCalculateComponentsToInstall() const \sa {installer::aboutCalculateComponentsToInstall}{installer.aboutCalculateComponentsToInstall} */ /*! \fn PackageManagerCore::finishedCalculateComponentsToInstall() const \sa {installer::finishedCalculateComponentsToInstall}{installer.finishedCalculateComponentsToInstall} */ /*! \fn PackageManagerCore::aboutCalculateComponentsToUninstall() const \sa {installer::aboutCalculateComponentsToUninstall}{installer.aboutCalculateComponentsToUninstall} */ /*! \fn PackageManagerCore::finishedCalculateComponentsToUninstall() const \sa {installer::finishedCalculateComponentsToUninstall}{installer.finishedCalculateComponentsToUninstall} */ /*! \fn PackageManagerCore::componentAdded(QInstaller::Component *comp) Emitted when the new root component \a comp is added. \sa {installer::componentAdded}{installer.componentAdded} \sa rootComponentsAdded(), updaterComponentsAdded() */ /*! \fn PackageManagerCore::rootComponentsAdded(QList components) Emitted when the list of root components specified by \a components is added. \sa {installer::rootComponentsAdded}{installer.rootComponentsAdded} \sa componentAdded(), updaterComponentsAdded() */ /*! \fn PackageManagerCore::updaterComponentsAdded(QList components) Emitted when a new list of updater components specified by \a components is added. \sa {installer::updaterComponentsAdded}{installer.updaterComponentsAdded} \sa componentAdded(), rootComponentsAdded() */ /*! \fn PackageManagerCore::valueChanged(const QString &key, const QString &value) Emitted when the value \a value of the key \a key changes. \sa {installer::valueChanged}{installer.valueChanged}, setValue() */ /*! \fn PackageManagerCore::currentPageChanged(int page) Emitted when the current page \a page changes. \sa {installer::currentPageChanged}{installer.currentPageChanged} */ /*! \fn PackageManagerCore::finishButtonClicked() \sa {installer::finishButtonClicked}{installer.finishButtonClicked} */ /*! \fn PackageManagerCore::metaJobProgress(int progress) Triggered with progress updates of the communication with a remote repository. Values of \a progress range from 0 to 100. \sa {installer::metaJobProgress}{installer.metaJobProgress} */ /*! \fn PackageManagerCore::metaJobInfoMessage(const QString &message) Triggered with informative updates, \a message, of the communication with a remote repository. \sa {installer::metaJobInfoMessage}{installer.metaJobInfoMessage} */ /*! \fn PackageManagerCore::startAllComponentsReset() \sa {installer::startAllComponentsReset}{installer.startAllComponentsReset} \sa finishAllComponentsReset() */ /*! \fn PackageManagerCore::finishAllComponentsReset(const QList &rootComponents) Triggered when the list of new root components, \a rootComponents, has been updated. \sa {installer::finishAllComponentsReset}{installer.finishAllComponentsReset} \sa startAllComponentsReset() */ /*! \fn PackageManagerCore::startUpdaterComponentsReset() \sa {installer::startUpdaterComponentsReset}{installer.startUpdaterComponentsReset} */ /*! \fn PackageManagerCore::finishUpdaterComponentsReset(const QList &componentsWithUpdates) Triggered when the list of available remote updates, \a componentsWithUpdates, has been updated. \sa {installer::finishUpdaterComponentsReset}{installer.finishUpdaterComponentsReset} */ /*! \fn PackageManagerCore::installationStarted() \sa {installer::installationStarted}{installer.installationStarted} \sa installationFinished() installationInterrupted() */ /*! \fn PackageManagerCore::installationInterrupted() \sa {installer::installationInterrupted}{installer.installationInterrupted} \sa interrupt() installationStarted() installationFinished() */ /*! \fn PackageManagerCore::installationFinished() \sa {installer::installationFinished}{installer.installationFinished} \sa installationStarted() installationInterrupted() */ /*! \fn PackageManagerCore::updateFinished() \sa {installer::installationFinished}{installer.installationFinished} */ /*! \fn PackageManagerCore::uninstallationStarted() \sa {installer::uninstallationStarted}{installer.uninstallationStarted} \sa uninstallationFinished() */ /*! \fn PackageManagerCore::uninstallationFinished() \sa {installer::uninstallationFinished}{installer.uninstallationFinished} \sa uninstallationStarted() */ /*! \fn PackageManagerCore::titleMessageChanged(const QString &title) Emitted when the text of the installer status (on the PerformInstallation page) changes to \a title. \sa {installer::titleMessageChanged}{installer.titleMessageChanged} */ /*! \fn PackageManagerCore::wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page) Emitted when a custom \a widget is about to be inserted into \a page by addWizardPage(). \sa {installer::wizardPageInsertionRequested}{installer.wizardPageInsertionRequested} */ /*! \fn PackageManagerCore::wizardPageRemovalRequested(QWidget *widget) Emitted when a \a widget is removed by removeWizardPage(). \sa {installer::wizardPageRemovalRequested}{installer.wizardPageRemovalRequested} */ /*! \fn PackageManagerCore::wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page) Emitted when a \a widget is inserted into \a page by addWizardPageItem(). \sa {installer::wizardWidgetInsertionRequested}{installer.wizardWidgetInsertionRequested} */ /*! \fn PackageManagerCore::wizardWidgetRemovalRequested(QWidget *widget) Emitted when a \a widget is removed by removeWizardPageItem(). \sa {installer::wizardWidgetRemovalRequested}{installer.wizardWidgetRemovalRequested} */ /*! \fn PackageManagerCore::wizardPageVisibilityChangeRequested(bool visible, int page) Emitted when the visibility of the page with the ID \a page changes to \a visible. \sa setDefaultPageVisible() \sa {installer::wizardPageVisibilityChangeRequested}{installer.wizardPageVisibilityChangeRequested} */ /*! \fn PackageManagerCore::setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name, const QString &callbackName) Requests that a validator be set for the custom page specified by \a name and \a callbackName for the component \a component. Triggered when setValidatorForCustomPage() is called. \sa {installer::setValidatorForCustomPageRequested}{installer.setValidatorForCustomPageRequested} */ /*! \fn PackageManagerCore::setAutomatedPageSwitchEnabled(bool request) Triggered when the automatic switching from the perform installation to the installation finished page is enabled (\a request is \c true) or disabled (\a request is \c false). The automatic switching is disabled automatically when for example the user expands or unexpands the \uicontrol Details section of the PerformInstallation page. \sa {installer::setAutomatedPageSwitchEnabled}{installer.setAutomatedPageSwitchEnabled} */ /*! \fn PackageManagerCore::coreNetworkSettingsChanged() \sa {installer::coreNetworkSettingsChanged}{installer.coreNetworkSettingsChanged} */ /*! \fn PackageManagerCore::guiObjectChanged(QObject *gui) Emitted when the GUI object is set to \a gui. */ Q_GLOBAL_STATIC(QMutex, globalModelMutex); static QFont *sVirtualComponentsFont = 0; Q_GLOBAL_STATIC(QMutex, globalVirtualComponentsFontMutex); static bool sNoForceInstallation = false; static bool sVirtualComponentsVisible = false; static bool sCreateLocalRepositoryFromBinary = false; static bool componentMatches(const Component *component, const QString &name, const QString &version = QString()) { if (name.isEmpty() || component->name() != name) return false; if (version.isEmpty()) return true; // can be remote or local version return PackageManagerCore::versionMatches(component->value(scVersion), version); } /*! Creates the maintenance tool in the installation directory. */ void PackageManagerCore::writeMaintenanceTool() { if (d->m_needToWriteMaintenanceTool) { try { d->writeMaintenanceTool(d->m_performedOperationsOld + d->m_performedOperationsCurrentSession); bool gainedAdminRights = false; QTemporaryFile tempAdminFile(d->targetDir() + QLatin1String("/testjsfdjlkdsjflkdsjfldsjlfds") + QString::number(qrand() % 1000)); if (!tempAdminFile.open() || !tempAdminFile.isWritable()) { gainAdminRights(); gainedAdminRights = true; } d->m_localPackageHub->writeToDisk(); if (gainedAdminRights) dropAdminRights(); d->m_needToWriteMaintenanceTool = false; } catch (const Error &error) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("WriteError"), tr("Error writing Maintenance Tool"), error.message(), QMessageBox::Ok, QMessageBox::Ok); } } } /*! Creates the maintenance tool configuration files. */ void PackageManagerCore::writeMaintenanceConfigFiles() { d->writeMaintenanceConfigFiles(); } /*! Resets the class to its initial state and applies the values of the parameters specified by \a params. */ void PackageManagerCore::reset(const QHash ¶ms) { d->m_completeUninstall = false; d->m_needsHardRestart = false; d->m_status = PackageManagerCore::Unfinished; d->m_installerBaseBinaryUnreplaced.clear(); d->initialize(params); } /*! Sets the maintenance tool UI to \a gui. */ void PackageManagerCore::setGuiObject(QObject *gui) { if (gui == d->m_guiObject) return; d->m_guiObject = gui; emit guiObjectChanged(gui); } /*! Returns the GUI object. */ QObject *PackageManagerCore::guiObject() const { return d->m_guiObject; } /*! Sets the uninstallation to be \a complete. If \a complete is false, only components deselected by the user will be uninstalled. This option applies only on uninstallation. \sa {installer::setCompleteUninstallation}{installer.setCompleteUninstallation} */ void PackageManagerCore::setCompleteUninstallation(bool complete) { d->m_completeUninstall = complete; } /*! \sa {installer::cancelMetaInfoJob}{installer.cancelMetaInfoJob} */ void PackageManagerCore::cancelMetaInfoJob() { d->m_metadataJob.cancel(); } /*! \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation} */ void PackageManagerCore::componentsToInstallNeedsRecalculation() { d->clearInstallerCalculator(); d->clearUninstallerCalculator(); QList selectedComponentsToInstall = componentsMarkedForInstallation(); d->m_componentsToInstallCalculated = d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall); QList componentsToInstall = d->installerCalculator()->orderedComponentsToInstall(); QList selectedComponentsToUninstall; foreach (Component *component, components(ComponentType::All)) { if (component->uninstallationRequested() && !selectedComponentsToInstall.contains(component)) selectedComponentsToUninstall.append(component); } d->uninstallerCalculator()->appendComponentsToUninstall(selectedComponentsToUninstall); QSet componentsToUninstall = d->uninstallerCalculator()->componentsToUninstall(); foreach (Component *component, components(ComponentType::All)) component->setInstallAction(component->isInstalled() ? ComponentModelHelper::KeepInstalled : ComponentModelHelper::KeepUninstalled); foreach (Component *component, componentsToUninstall) component->setInstallAction(ComponentModelHelper::Uninstall); foreach (Component *component, componentsToInstall) component->setInstallAction(ComponentModelHelper::Install); // update all nodes uncompressed size foreach (Component *const component, components(ComponentType::Root)) component->updateUncompressedSize(); // this is a recursive call } /*! \sa {installer::autoAcceptMessageBoxes}{installer.autoAcceptMessageBoxes} \sa autoRejectMessageBoxes(), setMessageBoxAutomaticAnswer() */ void PackageManagerCore::autoAcceptMessageBoxes() { MessageBoxHandler::instance()->setDefaultAction(MessageBoxHandler::Accept); } /*! \sa {installer::autoRejectMessageBoxes}{installer.autoRejectMessageBoxes} \sa autoAcceptMessageBoxes(), setMessageBoxAutomaticAnswer() */ void PackageManagerCore::autoRejectMessageBoxes() { MessageBoxHandler::instance()->setDefaultAction(MessageBoxHandler::Reject); } /*! Automatically closes the message box with the ID \a identifier as if the user had pressed \a button. This can be used for unattended (automatic) installations. \sa {installer::setMessageBoxAutomaticAnswer}{installer.setMessageBoxAutomaticAnswer} \sa QMessageBox, autoAcceptMessageBoxes(), autoRejectMessageBoxes() */ void PackageManagerCore::setMessageBoxAutomaticAnswer(const QString &identifier, int button) { MessageBoxHandler::instance()->setAutomaticAnswer(identifier, static_cast(button)); } /*! Returns the size of the component \a component as \a value. */ quint64 PackageManagerCore::size(QInstaller::Component *component, const QString &value) const { if (component->installAction() == ComponentModelHelper::Install) return component->value(value).toLongLong(); return quint64(0); } /*! \sa {installer::requiredDiskSpace}{installer.requiredDiskSpace} \sa requiredTemporaryDiskSpace() */ quint64 PackageManagerCore::requiredDiskSpace() const { quint64 result = 0; foreach (QInstaller::Component *component, orderedComponentsToInstall()) result += size(component, scUncompressedSize); return result; } /*! \sa {installer::requiredTemporaryDiskSpace}{installer.requiredTemporaryDiskSpace} \sa requiredDiskSpace() */ quint64 PackageManagerCore::requiredTemporaryDiskSpace() const { if (isOfflineOnly()) return 0; quint64 result = 0; foreach (QInstaller::Component *component, orderedComponentsToInstall()) result += size(component, scCompressedSize); return result; } /*! Returns the number of archives that will be downloaded. \a partProgressSize is reserved for the download progress. */ int PackageManagerCore::downloadNeededArchives(double partProgressSize) { Q_ASSERT(partProgressSize >= 0 && partProgressSize <= 1); QList > archivesToDownload; QList neededComponents = orderedComponentsToInstall(); foreach (Component *component, neededComponents) { // collect all archives to be downloaded const QStringList toDownload = component->downloadableArchives(); foreach (const QString &versionFreeString, toDownload) { archivesToDownload.push_back(qMakePair(QString::fromLatin1("installer://%1/%2") .arg(component->name(), versionFreeString), QString::fromLatin1("%1/%2/%3") .arg(component->repositoryUrl().toString(), component->name(), versionFreeString))); } } if (archivesToDownload.isEmpty()) return 0; ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nDownloading packages...")); DownloadArchivesJob archivesJob(this); archivesJob.setAutoDelete(false); archivesJob.setArchivesToDownload(archivesToDownload); connect(this, &PackageManagerCore::installationInterrupted, &archivesJob, &Job::cancel); connect(&archivesJob, &DownloadArchivesJob::outputTextChanged, ProgressCoordinator::instance(), &ProgressCoordinator::emitLabelAndDetailTextChanged); connect(&archivesJob, &DownloadArchivesJob::downloadStatusChanged, ProgressCoordinator::instance(), &ProgressCoordinator::downloadStatusChanged); ProgressCoordinator::instance()->registerPartProgress(&archivesJob, SIGNAL(progressChanged(double)), partProgressSize); archivesJob.start(); archivesJob.waitForFinished(); if (archivesJob.error() == Job::Canceled) interrupt(); else if (archivesJob.error() != Job::NoError) throw Error(archivesJob.errorString()); if (d->statusCanceledOrFailed()) throw Error(tr("Installation canceled by user.")); ProgressCoordinator::instance()->emitDownloadStatus(tr("All downloads finished.")); return archivesJob.numberOfDownloads(); } /*! Returns \c true if a hard restart of the application is requested. */ bool PackageManagerCore::needsHardRestart() const { return d->m_needsHardRestart; } /*! Enables a component to request a hard restart of the application if \a needsHardRestart is set to \c true. */ void PackageManagerCore::setNeedsHardRestart(bool needsHardRestart) { d->m_needsHardRestart = needsHardRestart; } /*! Cancels the installation and performs the UNDO step of all already executed operations. */ void PackageManagerCore::rollBackInstallation() { emit titleMessageChanged(tr("Cancelling the Installer")); // this unregisters all operation progressChanged connected ProgressCoordinator::instance()->setUndoMode(); const int progressOperationCount = d->countProgressOperations(d->m_performedOperationsCurrentSession); const double progressOperationSize = double(1) / progressOperationCount; // reregister all the undo operations with the new size to the ProgressCoordinator foreach (Operation *const operation, d->m_performedOperationsCurrentSession) { QObject *const operationObject = dynamic_cast (operation); if (operationObject != 0) { const QMetaObject* const mo = operationObject->metaObject(); if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) { ProgressCoordinator::instance()->registerPartProgress(operationObject, SIGNAL(progressChanged(double)), progressOperationSize); } } } while (!d->m_performedOperationsCurrentSession.isEmpty()) { try { Operation *const operation = d->m_performedOperationsCurrentSession.takeLast(); const bool becameAdmin = !RemoteClient::instance().isActive() && operation->value(QLatin1String("admin")).toBool() && gainAdminRights(); if (operation->value(QLatin1String("uninstall-only")).toBool() && QVariant(value(scRemoveTargetDir)).toBool() && (operation->name() == QLatin1String("Mkdir"))) { // We know the mkdir operation which is creating the target path. If we do a // full uninstall, prevent a forced remove of the full install path including the // target, instead try to remove the target only and only if it is empty, // otherwise fail silently. Note: this can only happen if RemoveTargetDir is set, // otherwise the operation does not exist at all. operation->setValue(QLatin1String("forceremoval"), false); } PackageManagerCorePrivate::performOperationThreaded(operation, PackageManagerCorePrivate::Undo); const QString componentName = operation->value(QLatin1String("component")).toString(); if (!componentName.isEmpty()) { Component *component = componentByName(componentName); if (!component) component = d->componentsToReplace().value(componentName).second; if (component) { component->setUninstalled(); d->m_localPackageHub->removePackage(component->name()); } } d->m_localPackageHub->writeToDisk(); if (isInstaller()) { if (d->m_localPackageHub->packageInfoCount() == 0) { QFile file(d->m_localPackageHub->fileName()); file.remove(); } } if (becameAdmin) dropAdminRights(); } catch (const Error &e) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("ElevationError"), tr("Authentication Error"), tr("Some components " "could not be removed completely because administrative rights could not be acquired: %1.") .arg(e.message())); } catch (...) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("unknown"), tr("Unknown error."), tr("Some components could not be removed completely because an unknown " "error happened.")); } } } /*! Returns whether the file extension \a extension is already registered in the Windows registry. Returns \c false on all other platforms. \sa {installer::isFileExtensionRegistered}{installer.isFileExtensionRegistered} */ bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) const { QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettingsWrapper::NativeFormat); return settings.value(QString::fromLatin1(".%1/Default").arg(extension)).isValid(); } /*! Returns \c true if the \a filePath exists; otherwise returns \c false. \note If the file is a symlink that points to a non existing file, \c false is returned. \sa {installer::fileExists}{installer.fileExists} */ bool PackageManagerCore::fileExists(const QString &filePath) const { return QFileInfo(filePath).exists(); } /*! Returns the contents of the file \a filePath using the encoding specified by \a codecName. The file is read in the text mode, that is, end-of-line terminators are translated to the local encoding. \note If the file does not exist or an error occurs while reading the file, an empty string is returned. \sa {installer::readFile}{installer.readFile} */ QString PackageManagerCore::readFile(const QString &filePath, const QString &codecName) const { QFile f(filePath); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return QString(); QTextCodec *codec = QTextCodec::codecForName(qPrintable(codecName)); if (!codec) return QString(); QTextStream stream(&f); stream.setCodec(codec); return stream.readAll(); } // -- QInstaller /*! Used by operation runner to get a fake installer. */ PackageManagerCore::PackageManagerCore() : d(new PackageManagerCorePrivate(this)) { //TODO: Can be removed if installerbase can do what operation runner does. Repository::registerMetaType(); // register, cause we stream the type as QVariant qRegisterMetaType("QInstaller::PackageManagerCore::Status"); qRegisterMetaType("QInstaller::PackageManagerCore::WizardPage"); } /*! Creates an installer or uninstaller and performs sanity checks on the operations specified by \a operations. The magic marker \a magicmaker is a \c quint64 that identifies the type of the binary: \c installer or \c uninstaller. Creates and initializes a remote client. Requests administrator's rights for QFile, QSettings, and QProcess operations. Calls \c init() with \a socketName, \a key, and \a mode to set the server side authorization key. */ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList &operations, const QString &socketName, const QString &key, Protocol::Mode mode) : d(new PackageManagerCorePrivate(this, magicmaker, operations)) { Repository::registerMetaType(); // register, cause we stream the type as QVariant qRegisterMetaType("QInstaller::PackageManagerCore::Status"); qRegisterMetaType("QInstaller::PackageManagerCore::WizardPage"); // Creates and initializes a remote client, makes us get admin rights for QFile, QSettings // and QProcess operations. Init needs to called to set the server side authorization key. if (!d->isUpdater()) { RemoteClient::instance().init(socketName, key, mode, Protocol::StartAs::SuperUser); RemoteClient::instance().setAuthorizationFallbackDisabled(settings().disableAuthorizationFallback()); } d->initialize(QHash()); // // Sanity check to detect a broken installations with missing operations. // Every installed package should have at least one MinimalProgress operation. // QSet installedPackages = d->m_core->localInstalledPackages().keys().toSet(); QSet operationPackages; foreach (QInstaller::Operation *operation, d->m_performedOperationsOld) { if (operation->hasValue(QLatin1String("component"))) operationPackages.insert(operation->value(QLatin1String("component")).toString()); } QSet packagesWithoutOperation = installedPackages - operationPackages; QSet orphanedOperations = operationPackages - installedPackages; if (!packagesWithoutOperation.isEmpty() || !orphanedOperations.isEmpty()) { qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.toList(); qCritical() << "Orphaned operations" << orphanedOperations.toList(); MessageBoxHandler::critical( MessageBoxHandler::currentBestSuitParent(), QLatin1String("Corrupt_Installation_Error"), QCoreApplication::translate("QInstaller", "Corrupt installation"), QCoreApplication::translate("QInstaller", "Your installation seems to be corrupted. " "Please consider re-installing from scratch." )); } else { qDebug() << "Operations sanity check succeeded."; } } class VerboseWriterAdminOutput : public VerboseWriterOutput { public: VerboseWriterAdminOutput(PackageManagerCore *core) : m_core(core) {} virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) { bool gainedAdminRights = false; if (!RemoteClient::instance().isActive()) { m_core->gainAdminRights(); gainedAdminRights = true; } RemoteFileEngine file; file.setFileName(fileName); if (file.open(openMode)) { file.write(data.constData(), data.size()); file.close(); if (gainedAdminRights) m_core->dropAdminRights(); return true; } if (gainedAdminRights) m_core->dropAdminRights(); return false; } private: PackageManagerCore *m_core; }; /*! Destroys the instance. */ PackageManagerCore::~PackageManagerCore() { if (!isUninstaller() && !(isInstaller() && status() == PackageManagerCore::Canceled)) { QDir targetDir(value(scTargetDir)); QString logFileName = targetDir.absoluteFilePath(value(QLatin1String("LogFileName"), QLatin1String("InstallationLog.txt"))); QInstaller::VerboseWriter::instance()->setFileName(logFileName); } delete d; try { PlainVerboseWriterOutput plainOutput; if (!VerboseWriter::instance()->flush(&plainOutput)) { VerboseWriterAdminOutput adminOutput(this); VerboseWriter::instance()->flush(&adminOutput); } } catch (...) { // Intentionally left blank; don't permit exceptions from VerboseWriter // to escape destructor. } RemoteClient::instance().setActive(false); RemoteClient::instance().destroy(); QMutexLocker _(globalVirtualComponentsFontMutex()); delete sVirtualComponentsFont; sVirtualComponentsFont = 0; } /* static */ /*! Returns the virtual components' font. */ QFont PackageManagerCore::virtualComponentsFont() { QMutexLocker _(globalVirtualComponentsFontMutex()); if (!sVirtualComponentsFont) sVirtualComponentsFont = new QFont; return *sVirtualComponentsFont; } /* static */ /*! Sets the virtual components' font to \a font. */ void PackageManagerCore::setVirtualComponentsFont(const QFont &font) { QMutexLocker _(globalVirtualComponentsFontMutex()); if (sVirtualComponentsFont) delete sVirtualComponentsFont; sVirtualComponentsFont = new QFont(font); } /* static */ /*! Returns \c true if virtual components are visible. */ bool PackageManagerCore::virtualComponentsVisible() { return sVirtualComponentsVisible; } /* static */ /*! Shows virtual components if \a visible is \c true. */ void PackageManagerCore::setVirtualComponentsVisible(bool visible) { sVirtualComponentsVisible = visible; } /* static */ /*! Returns \c true if forced installation is not set for all components for which the element is set in the package information file. */ bool PackageManagerCore::noForceInstallation() { return sNoForceInstallation; } /* static */ /*! Overwrites the value specified for the component in the \c element in the package information file with \a value. This enables end users to select or deselect the component in the installer. */ void PackageManagerCore::setNoForceInstallation(bool value) { sNoForceInstallation = value; } /* static */ /*! Returns \c true if a local repository should be created from binary content. */ bool PackageManagerCore::createLocalRepositoryFromBinary() { return sCreateLocalRepositoryFromBinary; } /* static */ /*! Determines that a local repository should be created from binary content if \a create is \c true. */ void PackageManagerCore::setCreateLocalRepositoryFromBinary(bool create) { sCreateLocalRepositoryFromBinary = create; } /*! Returns \c true if the package manager is running and installed packages are found. Otherwise, returns \c false. */ bool PackageManagerCore::fetchLocalPackagesTree() { d->setStatus(Running); if (!isPackageManager()) { d->setStatus(Failure, tr("Application not running in Package Manager mode.")); return false; } LocalPackagesHash installedPackages = d->localInstalledPackages(); if (installedPackages.isEmpty()) { if (status() != Failure) d->setStatus(Failure, tr("No installed packages found.")); return false; } emit startAllComponentsReset(); d->clearAllComponentLists(); QHash components; const QStringList &keys = installedPackages.keys(); foreach (const QString &key, keys) { QScopedPointer component(new QInstaller::Component(this)); component->loadDataFromPackage(installedPackages.value(key)); const QString &name = component->name(); if (components.contains(name)) { qCritical("Cannot register component! Component with identifier %s already registered.", qPrintable(name)); continue; } components.insert(name, component.take()); } if (!d->buildComponentTree(components, false)) return false; updateDisplayVersions(scDisplayVersion); emit finishAllComponentsReset(d->m_rootComponents); d->setStatus(Success); return true; } /*! Returns a list of local installed packages. The list can be empty. */ LocalPackagesHash PackageManagerCore::localInstalledPackages() { return d->localInstalledPackages(); } /*! Emits the coreNetworkSettingsChanged() signal when network settings change. */ void PackageManagerCore::networkSettingsChanged() { cancelMetaInfoJob(); d->m_updates = false; d->m_repoFetched = false; d->m_updateSourcesAdded = false; if (isMaintainer() ) { bool gainedAdminRights = false; QTemporaryFile tempAdminFile(d->targetDir() + QStringLiteral("/XXXXXX")); if (!tempAdminFile.open() || !tempAdminFile.isWritable()) { gainAdminRights(); gainedAdminRights = true; } d->writeMaintenanceConfigFiles(); if (gainedAdminRights) dropAdminRights(); } KDUpdater::FileDownloaderFactory::instance().setProxyFactory(proxyFactory()); emit coreNetworkSettingsChanged(); } /*! Returns a copy of the proxy factory that the package manager uses to determine the proxies to be used for requests. */ PackageManagerProxyFactory *PackageManagerCore::proxyFactory() const { if (d->m_proxyFactory) return d->m_proxyFactory->clone(); return new PackageManagerProxyFactory(this); } /*! Sets the proxy factory for the package manager to be \a factory. A proxy factory is used to determine a more specific list of proxies to be used for a given request, instead of trying to use the same proxy value for all requests. This might only be of use for HTTP or FTP requests. */ void PackageManagerCore::setProxyFactory(PackageManagerProxyFactory *factory) { delete d->m_proxyFactory; d->m_proxyFactory = factory; KDUpdater::FileDownloaderFactory::instance().setProxyFactory(proxyFactory()); } /*! Returns a list of packages available in all the repositories that were looked at. */ PackagesList PackageManagerCore::remotePackages() { return d->remotePackages(); } /*! Checks for compressed packages to install. Returns \c true if newer versions exist and they can be installed. */ bool PackageManagerCore::fetchCompressedPackagesTree() { const LocalPackagesHash installedPackages = d->localInstalledPackages(); if (!isInstaller() && status() == Failure) return false; if (!d->fetchMetaInformationFromCompressedRepositories()) return false; if (!d->addUpdateResourcesFromRepositories(true, true)) { return false; } PackagesList packages; const PackagesList &compPackages = d->compressedPackages(); if (compPackages.isEmpty()) return false; packages.append(compPackages); const PackagesList &rPackages = d->remotePackages(); packages.append(rPackages); return fetchPackagesTree(packages, installedPackages); } /*! Checks for packages to install. Returns \c true if newer versions exist and they can be installed. */ bool PackageManagerCore::fetchRemotePackagesTree() { d->setStatus(Running); if (isUninstaller()) { d->setStatus(Failure, tr("Application running in Uninstaller mode.")); return false; } if (!ProductKeyCheck::instance()->hasValidKey()) { d->setStatus(Failure, ProductKeyCheck::instance()->lastErrorString()); return false; } const LocalPackagesHash installedPackages = d->localInstalledPackages(); if (!isInstaller() && status() == Failure) return false; if (!d->fetchMetaInformationFromRepositories()) return false; if (!d->fetchMetaInformationFromCompressedRepositories()) return false; if (!d->addUpdateResourcesFromRepositories(true)) return false; const PackagesList &packages = d->remotePackages(); if (packages.isEmpty()) return false; return fetchPackagesTree(packages, installedPackages); } bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const LocalPackagesHash installedPackages) { bool success = false; if (!isUpdater()) { success = fetchAllPackages(packages, installedPackages); if (success && !d->statusCanceledOrFailed() && isPackageManager()) { foreach (Package *const update, packages) { if (update->data(scEssential, scFalse).toString().toLower() == scTrue) { const QString name = update->data(scName).toString(); if (!installedPackages.contains(name)) { success = false; break; // unusual, the maintenance tool should always be available } const LocalPackage localPackage = installedPackages.value(name); const QString updateVersion = update->data(scVersion).toString(); if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0) break; // remote version equals or is less than the installed maintenance tool const QDate updateDate = update->data(scReleaseDate).toDate(); if (localPackage.lastUpdateDate >= updateDate) break; // remote release date equals or is less than the installed maintenance tool success = false; break; // we found a newer version of the maintenance tool } } if (!success && !d->statusCanceledOrFailed()) { updateDisplayVersions(scRemoteDisplayVersion); d->setStatus(ForceUpdate, tr("There is an important update available, please run the " "updater first.")); return false; } } } else { success = fetchUpdaterPackages(packages, installedPackages); } updateDisplayVersions(scRemoteDisplayVersion); if (success && !d->statusCanceledOrFailed()) d->setStatus(Success); return success; } /*! \fn PackageManagerCore::addWizardPage(QInstaller::Component * component, const QString & name, int page) Adds the widget with object name \a name registered by \a component as a new page into the installer's GUI wizard. The widget is added before \a page. See \l{Controller Scripting} for the possible values of \a page. Returns \c true if the operation succeeded. \sa {installer::addWizardPage}{installer.addWizardPage} */ bool PackageManagerCore::addWizardPage(Component *component, const QString &name, int page) { if (QWidget* const widget = component->userInterface(name)) { emit wizardPageInsertionRequested(widget, static_cast(page)); return true; } return false; } /*! \fn PackageManagerCore::removeWizardPage(QInstaller::Component * component, const QString & name) Removes the widget with the object name \a name previously added to the installer's wizard by \a component. Returns \c true if the operation succeeded. \sa {installer::removeWizardPage}{installer.removeWizardPage} \sa addWizardPage(), setDefaultPageVisible(), wizardPageRemovalRequested() */ bool PackageManagerCore::removeWizardPage(Component *component, const QString &name) { if (QWidget* const widget = component->userInterface(name)) { emit wizardPageRemovalRequested(widget); return true; } return false; } /*! Sets the visibility of the default page with the ID \a page to \a visible. That is, removes it from or adds it to the wizard. This works only for pages that were in the installer when it was started. Returns \c true. \sa {installer::setDefaultPageVisible}{installer.setDefaultPageVisible} \sa addWizardPage(), removeWizardPage() */ bool PackageManagerCore::setDefaultPageVisible(int page, bool visible) { emit wizardPageVisibilityChangeRequested(visible, page); return true; } /*! \fn PackageManagerCore::setValidatorForCustomPage(QInstaller::Component * component, const QString & name, const QString & callbackName) Sets a validator for the custom page specified by \a name and \a callbackName for the component \a component. When using this, \a name has to match a dynamic page starting with \c Dynamic. For example, if the page is called DynamicReadyToInstallWidget, then \a name should be set to \c ReadyToInstallWidget. The \a callbackName should be set to a function that returns a boolean. When the \c Next button is pressed on the custom page, then it will call the \a callbackName function. If this returns \c true, then it will move to the next page. \sa {installer::setValidatorForCustomPage}{installer.setValidatorForCustomPage} \sa setValidatorForCustomPageRequested() */ void PackageManagerCore::setValidatorForCustomPage(Component *component, const QString &name, const QString &callbackName) { emit setValidatorForCustomPageRequested(component, name, callbackName); } /*! \fn PackageManagerCore::addWizardPageItem(QInstaller::Component * component, const QString & name, int page) Adds the widget with the object name \a name registered by \a component as a GUI element into the installer's GUI wizard. The widget is added on \a page. See \l{Controller Scripting} for the possible values of \a page. If the widget can be found in an UI file for the component, returns \c true and emits the wizardWidgetInsertionRequested() signal. \sa {installer::addWizardPageItem}{installer.addWizardPageItem} \sa removeWizardPageItem(), wizardWidgetInsertionRequested() */ bool PackageManagerCore::addWizardPageItem(Component *component, const QString &name, int page) { if (QWidget* const widget = component->userInterface(name)) { emit wizardWidgetInsertionRequested(widget, static_cast(page)); return true; } return false; } /*! \fn PackageManagerCore::removeWizardPageItem(QInstaller::Component * component, const QString & name) Removes the widget with the object name \a name previously added to the installer's wizard by \a component. If the widget can be found in an UI file for the component, returns \c true and emits the wizardWidgetRemovalRequested() signal. \sa {installer::removeWizardPageItem}{installer.removeWizardPageItem} \sa addWizardPageItem() */ bool PackageManagerCore::removeWizardPageItem(Component *component, const QString &name) { if (QWidget* const widget = component->userInterface(name)) { emit wizardWidgetRemovalRequested(widget); return true; } return false; } /*! Registers additional \a repositories. \sa {installer::addUserRepositories}{installer.addUserRepositories} \sa setTemporaryRepositories() */ void PackageManagerCore::addUserRepositories(const QStringList &repositories) { QSet repositorySet; foreach (const QString &repository, repositories) repositorySet.insert(Repository::fromUserInput(repository)); settings().addUserRepositories(repositorySet); } /*! Sets additional \a repositories for this instance of the installer or updater if \a replace is \c false. Will be removed after invoking it again. \sa {installer::setTemporaryRepositories}{installer.setTemporaryRepositories} \sa addUserRepositories() */ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositories, bool replace, bool compressed) { QSet repositorySet; foreach (const QString &repository, repositories) repositorySet.insert(Repository::fromUserInput(repository, compressed)); settings().setTemporaryRepositories(repositorySet, replace); } /*! Checks whether the downloader should try to download SHA-1 checksums for archives and returns the checksums. */ bool PackageManagerCore::testChecksum() const { return d->m_testChecksum; } /*! The \a test argument determines whether the downloader should try to download SHA-1 checksums for archives. */ void PackageManagerCore::setTestChecksum(bool test) { d->m_testChecksum = test; } /*! Returns the script engine that prepares and runs the component scripts. \sa {Component Scripting} */ ScriptEngine *PackageManagerCore::componentScriptEngine() const { return d->componentScriptEngine(); } /*! Returns the script engine that prepares and runs the control script. \sa {Controller Scripting} */ ScriptEngine *PackageManagerCore::controlScriptEngine() const { return d->controlScriptEngine(); } /*! Appends \a component as the root component to the internal storage for installer or package manager components. To append a component as a child to an already existing component, use Component::appendComponent(). Emits the componentAdded() signal. */ void PackageManagerCore::appendRootComponent(Component *component) { d->m_rootComponents.append(component); emit componentAdded(component); } /*! \enum PackageManagerCore::ComponentType \brief This enum holds the type of the component list to be returned: \value Root Returns a list of root components. \value Descendants Returns a list of all descendant components. In updater mode the list is empty, because component updates cannot have children. \value Dependencies Returns a list of all available dependencies when run as updater. When running as installer, package manager, or uninstaller, this will always result in an empty list. \value Replacements Returns a list of all available replacement components relevant to the run mode. \value AllNoReplacements Returns a list of available components, including root, descendant, and dependency components relevant to the run mode. \value All Returns a list of all available components, including root, descendant, dependency, and replacement components relevant to the run mode. */ /*! \typedef PackageManagerCore::ComponentTypes Synonym for QList. */ /*! Returns a list of components depending on the component types passed in \a mask. */ QList PackageManagerCore::components(ComponentTypes mask) const { QList components; const bool updater = isUpdater(); if (mask.testFlag(ComponentType::Root)) components += updater ? d->m_updaterComponents : d->m_rootComponents; if (mask.testFlag(ComponentType::Replacements)) components += updater ? d->m_updaterDependencyReplacements : d->m_rootDependencyReplacements; if (!updater) { if (mask.testFlag(ComponentType::Descendants)) { foreach (QInstaller::Component *component, d->m_rootComponents) components += component->descendantComponents(); } } else { if (mask.testFlag(ComponentType::Dependencies)) components.append(d->m_updaterComponentsDeps); // No descendants here, updates are always a flat list and cannot have children! } return components; } /*! Appends \a component to the internal storage for updater components. Emits the componentAdded() signal. */ void PackageManagerCore::appendUpdaterComponent(Component *component) { component->setUpdateAvailable(true); d->m_updaterComponents.append(component); emit componentAdded(component); } /*! Returns a component matching \a name. \a name can also contain a version requirement. For example, \c org.qt-project.sdk.qt returns any component with that name, whereas \c{org.qt-project.sdk.qt->=4.5} requires the returned component to have at least version 4.5. If no component matches the requirement, \c 0 is returned. */ Component *PackageManagerCore::componentByName(const QString &name) const { return componentByName(name, components(ComponentType::AllNoReplacements)); } /*! Searches \a components for a component matching \a name and returns it. \a name can also contain a version requirement. For example, \c org.qt-project.sdk.qt returns any component with that name, whereas \c{org.qt-project.sdk.qt->=4.5} requires the returned component to have at least version 4.5. If no component matches the requirement, \c 0 is returned. */ Component *PackageManagerCore::componentByName(const QString &name, const QList &components) { if (name.isEmpty()) return 0; QString fixedVersion; QString fixedName = name; if (name.contains(QChar::fromLatin1('-'))) { // the last part is considered to be the version, then fixedVersion = name.section(QLatin1Char('-'), 1); fixedName = name.section(QLatin1Char('-'), 0, 0); } foreach (Component *component, components) { if (componentMatches(component, fixedName, fixedVersion)) return component; } return 0; } /*! Returns a list of components that are marked for installation. The list can be empty. */ QList PackageManagerCore::componentsMarkedForInstallation() const { QList markedForInstallation; const QList relevant = components(ComponentType::Root | ComponentType::Descendants); if (isUpdater()) { foreach (Component *component, relevant) { if (component->updateRequested()) markedForInstallation.append(component); } } else { // relevant means all components which are not replaced foreach (Component *component, relevant) { // ask for all components which will be installed to get all dependencies // even dependencies which are changed without an increased version if (component->isSelectedForInstallation() || (component->isInstalled() && !component->uninstallationRequested())) { markedForInstallation.append(component); } } } return markedForInstallation; } /*! Determines which components to install based on the current run mode and returns an ordered list of components to install. Also auto installed dependencies are resolved. The aboutCalculateComponentsToInstall() signal is emitted before the calculation starts, the finishedCalculateComponentsToInstall() signal once all calculations are done. \sa {installer::calculateComponentsToInstall}{installer.calculateComponentsToInstall} */ bool PackageManagerCore::calculateComponentsToInstall() const { emit aboutCalculateComponentsToInstall(); if (!d->m_componentsToInstallCalculated) { d->clearInstallerCalculator(); QList selectedComponentsToInstall = componentsMarkedForInstallation(); d->storeCheckState(); d->m_componentsToInstallCalculated = d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall); } emit finishedCalculateComponentsToInstall(); return d->m_componentsToInstallCalculated; } /*! Returns an ordered list of components to install. The list can be empty. */ QList PackageManagerCore::orderedComponentsToInstall() const { return d->installerCalculator()->orderedComponentsToInstall(); } bool PackageManagerCore::calculateComponents(QString *displayString) { QString htmlOutput; QString lastInstallReason; if (!calculateComponentsToUninstall() || !calculateComponentsToInstall()) { htmlOutput.append(QString::fromLatin1("

%1

    ") .arg(tr("Cannot resolve all dependencies."))); //if we have a missing dependency or a recursion we can display it if (!componentsToInstallError().isEmpty()) { htmlOutput.append(QString::fromLatin1("
  • %1
  • ").arg( componentsToInstallError())); } htmlOutput.append(QLatin1String("
")); if (displayString) *displayString = htmlOutput; return false; } // In case of updater mode we don't uninstall components. if (!isUpdater()) { QList componentsToRemove = componentsToUninstall(); if (!componentsToRemove.isEmpty()) { htmlOutput.append(QString::fromLatin1("

%1

    ").arg(tr("Components about to " "be removed."))); foreach (Component *component, componentsToRemove) htmlOutput.append(QString::fromLatin1("
  • %1
  • ").arg(component->name())); htmlOutput.append(QLatin1String("
")); } } foreach (Component *component, orderedComponentsToInstall()) { const QString reason = installReason(component); if (lastInstallReason != reason) { if (!lastInstallReason.isEmpty()) // means we had to close the previous list htmlOutput.append(QLatin1String("")); htmlOutput.append(QString::fromLatin1("

%1

    ").arg(reason)); lastInstallReason = reason; } htmlOutput.append(QString::fromLatin1("
  • %1
  • ").arg(component->name())); } if (displayString) *displayString = htmlOutput; return true; } /*! Calculates a list of components to uninstall based on the current run mode. The aboutCalculateComponentsToUninstall() signal is emitted before the calculation starts, the finishedCalculateComponentsToUninstall() signal once all calculations are done. Always returns \c true. \sa {installer::calculateComponentsToUninstall}{installer.calculateComponentsToUninstall} */ bool PackageManagerCore::calculateComponentsToUninstall() const { emit aboutCalculateComponentsToUninstall(); if (!isUpdater()) { // hack to avoid removing needed dependencies QSet componentsToInstall = d->installerCalculator()->orderedComponentsToInstall().toSet(); QList componentsToUninstall; foreach (Component *component, components(ComponentType::All)) { if (component->uninstallationRequested() && !componentsToInstall.contains(component)) componentsToUninstall.append(component); } d->clearUninstallerCalculator(); d->storeCheckState(); d->uninstallerCalculator()->appendComponentsToUninstall(componentsToUninstall); } emit finishedCalculateComponentsToUninstall(); return true; } /*! Returns a human-readable description of the error that occurred when evaluating the components to install. The error message is empty if no error occurred. \sa calculateComponentsToInstall */ QList PackageManagerCore::componentsToUninstall() const { return d->uninstallerCalculator()->componentsToUninstall().toList(); } /*! Returns errors found in the components that are marked for installation. */ QString PackageManagerCore::componentsToInstallError() const { return d->installerCalculator()->componentsToInstallError(); } /*! Returns the reason why \a component needs to be installed: \list \li The component was scheduled for installation. \li The component was added as a dependency for another component. \li The component was added as an automatic dependency. \endlist */ QString PackageManagerCore::installReason(Component *component) const { return d->installerCalculator()->installReason(component); } /*! Returns a list of components that depend on \a _component. The list can be empty. \note Automatic dependencies are not resolved. */ QList PackageManagerCore::dependees(const Component *_component) const { if (!_component) return QList(); const QList availableComponents = components(ComponentType::All); if (availableComponents.isEmpty()) return QList(); const QLatin1Char dash('-'); QList dependees; foreach (Component *component, availableComponents) { const QStringList &dependencies = component->dependencies(); foreach (const QString &dependency, dependencies) { // the last part is considered to be the version then const QString name = dependency.contains(dash) ? dependency.section(dash, 0, 0) : dependency; const QString version = dependency.contains(dash) ? dependency.section(dash, 1) : QString(); if (componentMatches(_component, name, version)) dependees.append(component); } } return dependees; } /*! Returns the default component model. */ ComponentModel *PackageManagerCore::defaultComponentModel() const { QMutexLocker _(globalModelMutex()); if (!d->m_defaultModel) { d->m_defaultModel = componentModel(const_cast (this), QLatin1String("AllComponentsModel")); } connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel, &ComponentModel::setRootComponents); return d->m_defaultModel; } /*! Returns the updater component model. */ ComponentModel *PackageManagerCore::updaterComponentModel() const { QMutexLocker _(globalModelMutex()); if (!d->m_updaterModel) { d->m_updaterModel = componentModel(const_cast (this), QLatin1String("UpdaterComponentsModel")); } connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel, &ComponentModel::setRootComponents); return d->m_updaterModel; } void PackageManagerCore::updateComponentsSilently() { //Check if there are processes running in the install QStringList excludeFiles; excludeFiles.append(maintenanceToolName()); QStringList runningProcesses = d->runningInstallerProcesses(excludeFiles); if (!runningProcesses.isEmpty()) { qDebug() << "Unable to update components. Please stop these processes: " << runningProcesses << " and try again."; return; } autoAcceptMessageBoxes(); //Prevent infinite loop if installation for some reason fails. setMessageBoxAutomaticAnswer(QLatin1String("installationErrorWithRetry"), QMessageBox::Cancel); fetchRemotePackagesTree(); const QList componentList = components( ComponentType::Root | ComponentType::Descendants); if (componentList.count() == 0) { qDebug() << "No updates available."; } else { // Check if essential components are available (essential components are disabled). // If essential components are found, update first essential updates, // restart installer and install rest of the updates. bool essentialUpdatesFound = false; foreach (Component *component, componentList) { if (component->value(scEssential, scFalse).toLower() == scTrue) essentialUpdatesFound = true; } if (!essentialUpdatesFound) { //Mark all components to be updated foreach (Component *comp, componentList) { comp->setCheckState(Qt::Checked); } } QString htmlOutput; bool componentsOk = calculateComponents(&htmlOutput); if (componentsOk) { if (runPackageUpdater()) { writeMaintenanceTool(); if (essentialUpdatesFound) { qDebug() << "Essential components updated successfully."; } else { qDebug() << "Components updated successfully."; } } } else { qDebug() << htmlOutput; } } } /*! Returns the settings for the package manager. */ Settings &PackageManagerCore::settings() const { return d->m_data.settings(); } /*! Tries to gain admin rights. On success, it returns \c true. \sa {installer::gainAdminRights}{installer.gainAdminRights} \sa dropAdminRights() */ bool PackageManagerCore::gainAdminRights() { if (AdminAuthorization::hasAdminRights()) return true; RemoteClient::instance().setActive(true); if (!RemoteClient::instance().isActive()) throw Error(tr("Error while elevating access rights.")); return true; } /*! \sa {installer::dropAdminRights}{installer.dropAdminRights} \sa gainAdminRights() */ void PackageManagerCore::dropAdminRights() { RemoteClient::instance().setActive(false); } /*! Returns \c true if a process with \a name is running. On Windows, the comparison is case-insensitive. \sa {installer::isProcessRunning}{installer.isProcessRunning} */ bool PackageManagerCore::isProcessRunning(const QString &name) const { return PackageManagerCorePrivate::isProcessRunning(name, runningProcesses()); } /*! Returns \c true if a process with \a absoluteFilePath could be killed or is not running. \note This is implemented in a semi blocking way (to keep the main thread to paint the UI). \sa {installer::killProcess}{installer.killProcess} */ bool PackageManagerCore::killProcess(const QString &absoluteFilePath) const { QString normalizedPath = replaceVariables(absoluteFilePath); normalizedPath = QDir::cleanPath(normalizedPath.replace(QLatin1Char('\\'), QLatin1Char('/'))); QList allProcesses = runningProcesses(); foreach (const ProcessInfo &process, allProcesses) { QString processPath = process.name; processPath = QDir::cleanPath(processPath.replace(QLatin1Char('\\'), QLatin1Char('/'))); if (processPath == normalizedPath) { qDebug().nospace() << "try to kill process " << process.name << " (" << process.id << ")"; //to keep the ui responsible use QtConcurrent::run QFutureWatcher futureWatcher; const QFuture future = QtConcurrent::run(KDUpdater::killProcess, process, 30000); QEventLoop loop; connect(&futureWatcher, &QFutureWatcher::finished, &loop, &QEventLoop::quit, Qt::QueuedConnection); futureWatcher.setFuture(future); if (!future.isFinished()) loop.exec(); qDebug() << process.name << "killed!"; return future.result(); } } return true; } /*! Makes sure the installer runs from a local drive. Otherwise the user will get an appropriate error message. \note This only works on Windows. \sa {installer::setDependsOnLocalInstallerBinary}{installer.setDependsOnLocalInstallerBinary} \sa localInstallerBinaryUsed() */ void PackageManagerCore::setDependsOnLocalInstallerBinary() { d->m_dependsOnLocalInstallerBinary = true; } /*! Returns \c false if the installer is run on Windows, and the installer has been started from a remote file system drive. Otherwise returns \c true. \sa {installer::localInstallerBinaryUsed}{installer.localInstallerBinaryUsed} \sa setDependsOnLocalInstallerBinary() */ bool PackageManagerCore::localInstallerBinaryUsed() { #ifdef Q_OS_WIN return KDUpdater::pathIsOnLocalDevice(qApp->applicationFilePath()); #endif return true; } /*! Starts the program \a program with the arguments \a arguments in a new process and waits for it to finish. \a stdIn is sent as standard input to the application. \a stdInCodec is the name of the codec to use for converting the input string into bytes to write to the standard input of the application. \a stdOutCodec is the name of the codec to use for converting data written by the application to standard output into a string. Returns an empty array if the program could not be executed, otherwise the output of command as the first item, and the return code as the second. \note On Unix, the output is just the output to stdout, not to stderr. \sa {installer::execute}{installer.execute} \sa executeDetached() */ QList PackageManagerCore::execute(const QString &program, const QStringList &arguments, const QString &stdIn, const QString &stdInCodec, const QString &stdOutCodec) const { QProcessWrapper process; QString adjustedProgram = replaceVariables(program); QStringList adjustedArguments; foreach (const QString &argument, arguments) adjustedArguments.append(replaceVariables(argument)); QString adjustedStdIn = replaceVariables(stdIn); process.start(adjustedProgram, adjustedArguments, adjustedStdIn.isNull() ? QIODevice::ReadOnly : QIODevice::ReadWrite); if (!process.waitForStarted()) return QList< QVariant >(); if (!adjustedStdIn.isNull()) { QTextCodec *codec = QTextCodec::codecForName(qPrintable(stdInCodec)); if (!codec) return QList(); QTextEncoder encoder(codec); process.write(encoder.fromUnicode(adjustedStdIn)); process.closeWriteChannel(); } process.waitForFinished(-1); QTextCodec *codec = QTextCodec::codecForName(qPrintable(stdOutCodec)); if (!codec) return QList(); return QList() << QTextDecoder(codec).toUnicode(process.readAllStandardOutput()) << process.exitCode(); } /*! Starts the program \a program with the arguments \a arguments in a new process, and detaches from it. Returns \c true on success; otherwise returns \c false. If the installer exits, the detached process will continue to live. \note Arguments that contain spaces are not passed to the process as separate arguments. \b{Unix:} The started process will run in its own session and act like a daemon. \b{Windows:} Arguments that contain spaces are wrapped in quotes. The started process will run as a regular standalone process. The process will be started in the directory \a workingDirectory. \sa {installer::executeDetached}{installer.executeDetached} */ bool PackageManagerCore::executeDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory) const { QString adjustedProgram = replaceVariables(program); QStringList adjustedArguments; QString adjustedWorkingDir = replaceVariables(workingDirectory); foreach (const QString &argument, arguments) adjustedArguments.append(replaceVariables(argument)); qDebug() << "run application as detached process:" << adjustedProgram << adjustedArguments << adjustedWorkingDir; if (workingDirectory.isEmpty()) return QProcess::startDetached(adjustedProgram, adjustedArguments); else return QProcess::startDetached(adjustedProgram, adjustedArguments, adjustedWorkingDir); } /*! Returns the content of the environment variable \a name. An empty string is returned if the environment variable is not set. \sa {installer::environmentVariable}{installer.environmentVariable} */ QString PackageManagerCore::environmentVariable(const QString &name) const { if (name.isEmpty()) return QString(); #ifdef Q_OS_WIN static TCHAR buffer[32767]; DWORD size = GetEnvironmentVariable(LPCWSTR(name.utf16()), buffer, 32767); QString value = QString::fromUtf16((const unsigned short *) buffer, size); if (value.isEmpty()) { static QLatin1String userEnvironmentRegistryPath("HKEY_CURRENT_USER\\Environment"); value = QSettings(userEnvironmentRegistryPath, QSettings::NativeFormat).value(name).toString(); if (value.isEmpty()) { static QLatin1String systemEnvironmentRegistryPath("HKEY_LOCAL_MACHINE\\SYSTEM\\" "CurrentControlSet\\Control\\Session Manager\\Environment"); value = QSettings(systemEnvironmentRegistryPath, QSettings::NativeFormat).value(name).toString(); } } return value; #else return QString::fromUtf8(qgetenv(name.toLatin1())); #endif } /*! Returns \c true if the operation specified by \a name exists. */ bool PackageManagerCore::operationExists(const QString &name) { return KDUpdater::UpdateOperationFactory::instance().containsProduct(name); } /*! Instantly performs the operation \a name with \a arguments. Returns \c false if the operation cannot be created or executed. \sa {installer::performOperation}{installer.performOperation} */ bool PackageManagerCore::performOperation(const QString &name, const QStringList &arguments) { QScopedPointer op(KDUpdater::UpdateOperationFactory::instance().create(name, this)); if (!op.data()) return false; op->setArguments(replaceVariables(arguments)); op->backup(); if (!PackageManagerCorePrivate::performOperationThreaded(op.data())) { PackageManagerCorePrivate::performOperationThreaded(op.data(), PackageManagerCorePrivate::Undo); return false; } return true; } /*! Returns \c true when \a version matches the \a requirement. \a requirement can be a fixed version number or it can be prefixed by the comparators '>', '>=', '<', '<=' and '='. \sa {installer::versionMatches}{installer.versionMatches} */ bool PackageManagerCore::versionMatches(const QString &version, const QString &requirement) { QRegExp compEx(QLatin1String("([<=>]+)(.*)")); const QString comparator = compEx.exactMatch(requirement) ? compEx.cap(1) : QLatin1String("="); const QString ver = compEx.exactMatch(requirement) ? compEx.cap(2) : requirement; const bool allowEqual = comparator.contains(QLatin1Char('=')); const bool allowLess = comparator.contains(QLatin1Char('<')); const bool allowMore = comparator.contains(QLatin1Char('>')); if (allowEqual && version == ver) return true; if (allowLess && KDUpdater::compareVersion(ver, version) > 0) return true; if (allowMore && KDUpdater::compareVersion(ver, version) < 0) return true; return false; } /*! Finds a library named \a name in \a paths. If \a paths is empty, it gets filled with platform dependent default paths. The resulting path is returned. This method can be used by scripts to check external dependencies. \sa {installer::findLibrary}{installer.findLibrary} \sa findPath() */ QString PackageManagerCore::findLibrary(const QString &name, const QStringList &paths) { QStringList findPaths = paths; #if defined(Q_OS_WIN) return findPath(QString::fromLatin1("%1.lib").arg(name), findPaths); #else #if defined(Q_OS_OSX) if (findPaths.isEmpty()) { findPaths.push_back(QLatin1String("/lib")); findPaths.push_back(QLatin1String("/usr/lib")); findPaths.push_back(QLatin1String("/usr/local/lib")); findPaths.push_back(QLatin1String("/opt/local/lib")); } const QString dynamic = findPath(QString::fromLatin1("lib%1.dylib").arg(name), findPaths); #else if (findPaths.isEmpty()) { findPaths.push_back(QLatin1String("/lib")); findPaths.push_back(QLatin1String("/usr/lib")); findPaths.push_back(QLatin1String("/usr/local/lib")); findPaths.push_back(QLatin1String("/lib64")); findPaths.push_back(QLatin1String("/usr/lib64")); findPaths.push_back(QLatin1String("/usr/local/lib64")); } const QString dynamic = findPath(QString::fromLatin1("lib%1.so*").arg(name), findPaths); #endif if (!dynamic.isEmpty()) return dynamic; return findPath(QString::fromLatin1("lib%1.a").arg(name), findPaths); #endif } /*! Tries to find the file name \a name in one of the paths specified by \a paths. The resulting path is returned. This method can be used by scripts to check external dependencies. \sa {installer::findPath}{installer.findPath} \sa findLibrary() */ QString PackageManagerCore::findPath(const QString &name, const QStringList &paths) { foreach (const QString &path, paths) { const QDir dir(path); const QStringList entries = dir.entryList(QStringList() << name, QDir::Files | QDir::Hidden); if (entries.isEmpty()) continue; return dir.absoluteFilePath(entries.first()); } return QString(); } /*! Sets the \c installerbase binary located at \a path to use when writing the maintenance tool. Set the path if an update to the binary is available. If the path is not set, the executable segment of the running installer or uninstaller will be used. \sa {installer::setInstallerBaseBinary}{installer.setInstallerBaseBinary} */ void PackageManagerCore::setInstallerBaseBinary(const QString &path) { d->m_installerBaseBinaryUnreplaced = path; } /*! Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is returned. Additionally, on Windows, \a key can be a registry key. \sa {installer::value}{installer.value} \sa setValue(), containsValue(), valueChanged() */ QString PackageManagerCore::value(const QString &key, const QString &defaultValue) const { return d->m_data.value(key, defaultValue).toString(); } /*! Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is returned. Additionally, on Windows, \a key can be a registry key. \sa {installer::values}{installer.values} \sa value() */ QStringList PackageManagerCore::values(const QString &key, const QStringList &defaultValue) const { return d->m_data.value(key, defaultValue).toStringList(); } /*! Sets the installer value for \a key to \a value. \sa {installer::setValue}{installer.setValue} \sa value(), containsValue(), valueChanged() */ void PackageManagerCore::setValue(const QString &key, const QString &value) { const QString normalizedValue = replaceVariables(value); if (d->m_data.setValue(key, normalizedValue)) emit valueChanged(key, normalizedValue); } /*! Returns \c true if the installer contains a value for \a key. \sa {installer::containsValue}{installer.containsValue} \sa value(), setValue(), valueChanged() */ bool PackageManagerCore::containsValue(const QString &key) const { return d->m_data.contains(key); } /*! \obsolete Sets a shared flag with the name \a key to \a value. This is one option to share information between scripts. Deprecated since 2.0.0. Use setValue() instead. \sa setValue() */ void PackageManagerCore::setSharedFlag(const QString &key, bool value) { qDebug() << "sharedFlag is deprecated"; d->m_sharedFlags.insert(key, value); } /*! \obsolete Returns the shared flag with the name \a key. This is one option to share information between scripts. Deprecated since 2.0.0. Use value() or values() instead. \sa value(), values() */ bool PackageManagerCore::sharedFlag(const QString &key) const { qDebug() << "sharedFlag is deprecated"; return d->m_sharedFlags.value(key, false); } /*! Returns \c true if the package manager displays detailed information. */ bool PackageManagerCore::isVerbose() const { return QInstaller::isVerbose(); } /*! Determines that the package manager displays detailed information if \a on is \c true. */ void PackageManagerCore::setVerbose(bool on) { QInstaller::setVerbose(on); } PackageManagerCore::Status PackageManagerCore::status() const { return PackageManagerCore::Status(d->m_status); } /*! Returns a human-readable description of the last error that occurred. */ QString PackageManagerCore::error() const { return d->m_error; } /*! Returns \c true if at least one complete installation or update was successful, even if the user cancelled the latest installation process. */ bool PackageManagerCore::finishedWithSuccess() const { return d->m_status == PackageManagerCore::Success || d->m_needToWriteMaintenanceTool; } /*! \sa {installer::interrupt}{installer.interrupt} \sa installationInterrupted() */ void PackageManagerCore::interrupt() { setCanceled(); emit installationInterrupted(); } /*! \sa {installer::setCanceled}{installer.setCanceled} */ void PackageManagerCore::setCanceled() { if (!d->m_repoFetched) cancelMetaInfoJob(); d->setStatus(PackageManagerCore::Canceled); } /*! Replaces all variables within \a str by their respective values and returns the result. */ QString PackageManagerCore::replaceVariables(const QString &str) const { return d->replaceVariables(str); } /*! \overload Replaces all variables in any instance of \a str by their respective values and returns the results. */ QStringList PackageManagerCore::replaceVariables(const QStringList &str) const { QStringList result; foreach (const QString &s, str) result.append(d->replaceVariables(s)); return result; } /*! \overload Replaces all variables within \a ba by their respective values and returns the result. */ QByteArray PackageManagerCore::replaceVariables(const QByteArray &ba) const { return d->replaceVariables(ba); } /*! Returns the path to the installer binary. */ QString PackageManagerCore::installerBinaryPath() const { return d->installerBinaryPath(); } /*! Returns \c true if running as installer. \sa {installer::isInstaller}{installer.isInstaller} \sa isUninstaller(), isUpdater(), isPackageManager() */ bool PackageManagerCore::isInstaller() const { return d->isInstaller(); } /*! Returns \c true if this is an offline-only installer. \sa {installer::isOfflineOnly}{installer.isOfflineOnly} */ bool PackageManagerCore::isOfflineOnly() const { return d->isOfflineOnly(); } /*! \sa {installer::setUninstaller}{installer.setUninstaller} \sa isUninstaller(), setUpdater(), setPackageManager() */ void PackageManagerCore::setUninstaller() { d->m_magicBinaryMarker = BinaryContent::MagicUninstallerMarker; } /*! Returns \c true if running as uninstaller. \sa {installer::isUninstaller}{installer.isUninstaller} \sa setUninstaller(), isInstaller(), isUpdater(), isPackageManager() */ bool PackageManagerCore::isUninstaller() const { return d->isUninstaller(); } /*! \sa {installer::setUpdater}{installer.setUpdater} \sa isUpdater(), setUninstaller(), setPackageManager() */ void PackageManagerCore::setUpdater() { d->m_magicBinaryMarker = BinaryContent::MagicUpdaterMarker; } /*! Returns \c true if running as updater. \sa {installer::isUpdater}{installer.isUpdater} \sa setUpdater(), isInstaller(), isUninstaller(), isPackageManager() */ bool PackageManagerCore::isUpdater() const { return d->isUpdater(); } /*! \sa {installer::setPackageManager}{installer.setPackageManager} */ void PackageManagerCore::setPackageManager() { d->m_magicBinaryMarker = BinaryContent::MagicPackageManagerMarker; } /*! Returns \c true if running as the package manager. \sa {installer::isPackageManager}{installer.isPackageManager} \sa setPackageManager(), isInstaller(), isUninstaller(), isUpdater() */ bool PackageManagerCore::isPackageManager() const { return d->isPackageManager(); } /*! Returns \c true if it is a package manager or an updater. */ bool PackageManagerCore::isMaintainer() const { return isPackageManager() || isUpdater(); } /*! Runs the installer. Returns \c true on success, \c false otherwise. \sa {installer::runInstaller}{installer.runInstaller} */ bool PackageManagerCore::runInstaller() { return d->runInstaller(); } /*! Runs the uninstaller. Returns \c true on success, \c false otherwise. \sa {installer::runUninstaller}{installer.runUninstaller} */ bool PackageManagerCore::runUninstaller() { return d->runUninstaller(); } /*! Runs the updater. Returns \c true on success, \c false otherwise. \sa {installer::runPackageUpdater}{installer.runPackageUpdater} */ bool PackageManagerCore::runPackageUpdater() { return d->runPackageUpdater(); } /*! \sa {installer::languageChanged}{installer.languageChanged} */ void PackageManagerCore::languageChanged() { foreach (Component *component, components(ComponentType::All)) component->languageChanged(); } /*! Runs the installer, uninstaller, updater, or package manager, depending on the type of this binary. Returns \c true on success, otherwise \c false. */ bool PackageManagerCore::run() { if (isInstaller()) return d->runInstaller(); else if (isUninstaller()) return d->runUninstaller(); else if (isMaintainer()) return d->runPackageUpdater(); return false; } /*! Returns the path name of the maintenance tool binary. */ QString PackageManagerCore::maintenanceToolName() const { return d->maintenanceToolName(); } bool PackageManagerCore::updateComponentData(struct Data &data, Component *component) { try { // check if we already added the component to the available components list const QString name = data.package->data(scName).toString(); if (data.components->contains(name)) { qCritical("Cannot register component! Component with identifier %s already registered.", qPrintable(name)); return false; } component->setUninstalled(); const QString localPath = component->localTempPath(); if (isVerbose()) { static QString lastLocalPath; if (lastLocalPath != localPath) qDebug() << "Url is:" << localPath; lastLocalPath = localPath; } const Repository repo = d->m_metadataJob.repositoryForDirectory(localPath); if (repo.isValid()) { component->setRepositoryUrl(repo.url()); component->setValue(QLatin1String("username"), repo.username()); component->setValue(QLatin1String("password"), repo.password()); } // add downloadable archive from xml const QStringList downloadableArchives = data.package->data(scDownloadableArchives).toString() .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); if (component->isFromOnlineRepository()) { foreach (const QString downloadableArchive, downloadableArchives) component->addDownloadableArchive(downloadableArchive); } const QStringList componentsToReplace = data.package->data(scReplaces).toString() .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); if (!componentsToReplace.isEmpty()) { // Store the component (this is a component that replaces others) and all components that // this one will replace. data.replacementToExchangeables.insert(component, componentsToReplace); } if (isInstaller()) { // Running as installer means no component is installed, we do not need to check if the // replacement needs to be marked as installed, just return. return true; } if (data.installedPackages->contains(name)) { // The replacement is already installed, we can mark it as installed and skip the search for // a possible component to replace that might be installed (to mark the replacement as installed). component->setInstalled(); component->setValue(scInstalledVersion, data.installedPackages->value(name).version); return true; } // The replacement is not yet installed, check all components to replace for there install state. foreach (const QString &componentName, componentsToReplace) { if (data.installedPackages->contains(componentName)) { // We found a replacement that is installed. if (isPackageManager()) { // Mark the replacement component as installed as well. Only do this in package manager // mode, otherwise it would not show up in the updaters component list. component->setInstalled(); component->setValue(scInstalledVersion, data.installedPackages->value(componentName).version); break; // Break as soon as we know we found an installed component this one replaces. } } } } catch (...) { return false; } return true; } void PackageManagerCore::storeReplacedComponents(QHash &components, const struct Data &data) { QHash::const_iterator it = data.replacementToExchangeables.constBegin(); // remember all components that got a replacement, required for uninstall for (; it != data.replacementToExchangeables.constEnd(); ++it) { foreach (const QString &componentName, it.value()) { Component *componentToReplace = components.take(componentName); if (!componentToReplace) { // If a component replaces another component which is not existing in the // installer binary or the installed component list, just ignore it. This // can happen when in installer mode and probably package manager mode too. if (isUpdater()) qDebug() << componentName << "- Does not exist in the repositories anymore."; continue; } if (!componentToReplace && !d->componentsToReplace().contains(componentName)) { componentToReplace = new Component(this); componentToReplace->setValue(scName, componentName); } else { // This case can happen when in installer mode as well, a component // is in the installer binary and its replacement component as well. d->replacementDependencyComponents().append(componentToReplace); } d->componentsToReplace().insert(componentName, qMakePair(it.key(), componentToReplace)); } } } bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const LocalPackagesHash &locals) { emit startAllComponentsReset(); d->clearAllComponentLists(); QHash components; Data data; data.components = &components; data.installedPackages = &locals; foreach (Package *const package, remotes) { if (d->statusCanceledOrFailed()) return false; if (!ProductKeyCheck::instance()->isValidPackage(package->data(scName).toString())) continue; QScopedPointer component(new QInstaller::Component(this)); data.package = package; component->loadDataFromPackage(*package); if (updateComponentData(data, component.data())) { const QString name = component->name(); components.insert(name, component.take()); } } foreach (const QString &key, locals.keys()) { QScopedPointer component(new QInstaller::Component(this)); component->loadDataFromPackage(locals.value(key)); const QString &name = component->name(); if (!components.contains(name)) components.insert(name, component.take()); } // store all components that got a replacement storeReplacedComponents(components, data); if (!d->buildComponentTree(components, true)) return false; emit finishAllComponentsReset(d->m_rootComponents); return true; } bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const LocalPackagesHash &locals) { emit startUpdaterComponentsReset(); d->clearUpdaterComponentLists(); QHash components; Data data; data.components = &components; data.installedPackages = &locals; bool foundEssentialUpdate = false; LocalPackagesHash installedPackages = locals; QStringList replaceMes; foreach (Package *const update, remotes) { if (d->statusCanceledOrFailed()) return false; if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString())) continue; QScopedPointer component(new QInstaller::Component(this)); data.package = update; component->loadDataFromPackage(*update); if (updateComponentData(data, component.data())) { // Keep a reference so we can resolve dependencies during update. d->m_updaterComponentsDeps.append(component.take()); // const QString isNew = update->data(scNewComponent).toString(); // if (isNew.toLower() != scTrue) // continue; const QString &name = d->m_updaterComponentsDeps.last()->name(); const QString replaces = data.package->data(scReplaces).toString(); installedPackages.take(name); // remove from local installed packages bool isValidUpdate = locals.contains(name); if (!isValidUpdate && !replaces.isEmpty()) { const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(), QString::SkipEmptyParts); foreach (const QString &possibleName, possibleNames) { if (locals.contains(possibleName)) { isValidUpdate = true; replaceMes << possibleName; } } } // break if the update is not valid and if it's not the maintenance tool (we might get an update // for the maintenance tool even if it's not currently installed - possible offline installation) if (!isValidUpdate && (update->data(scEssential, scFalse).toString().toLower() == scFalse)) continue; // Update for not installed package found, skip it. const LocalPackage &localPackage = locals.value(name); const QString updateVersion = update->data(scVersion).toString(); if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0) continue; // It is quite possible that we may have already installed the update. Lets check the last // update date of the package and the release date of the update. This way we can compare and // figure out if the update has been installed or not. const QDate updateDate = update->data(scReleaseDate).toDate(); if (localPackage.lastUpdateDate > updateDate) continue; if (update->data(scEssential, scFalse).toString().toLower() == scTrue) foundEssentialUpdate = true; // this is not a dependency, it is a real update components.insert(name, d->m_updaterComponentsDeps.takeLast()); } } QHash localReplaceMes; foreach (const QString &key, installedPackages.keys()) { QInstaller::Component *component = new QInstaller::Component(this); component->loadDataFromPackage(installedPackages.value(key)); d->m_updaterComponentsDeps.append(component); // Keep a list of local components that should be replaced if (replaceMes.contains(component->name())) localReplaceMes.insert(component->name(), component); } // store all components that got a replacement, but do not modify the components list storeReplacedComponents(localReplaceMes.unite(components), data); try { if (!components.isEmpty()) { // append all components w/o parent to the direct list foreach (QInstaller::Component *component, components) { appendUpdaterComponent(component); } // after everything is set up, load the scripts foreach (QInstaller::Component *component, components) { if (d->statusCanceledOrFailed()) return false; component->loadComponentScript(); component->setCheckState(Qt::Checked); } // after everything is set up, check installed components foreach (QInstaller::Component *component, d->m_updaterComponentsDeps) { if (d->statusCanceledOrFailed()) return false; // even for possible dependency we need to load the script for example to get archives component->loadComponentScript(); if (component->isInstalled()) { // since we do not put them into the model, which would force a update of e.g. tri state // components, we have to check all installed components ourselves component->setCheckState(Qt::Checked); } } if (foundEssentialUpdate) { foreach (QInstaller::Component *component, components) { if (d->statusCanceledOrFailed()) return false; component->setCheckable(false); component->setSelectable(false); if (component->value(scEssential, scFalse).toLower() == scFalse) { // non essential updates are disabled, not checkable and unchecked component->setEnabled(false); component->setCheckState(Qt::Unchecked); } else { // essential updates are enabled, still not checkable but checked component->setEnabled(true); } } } std::sort(d->m_updaterComponents.begin(), d->m_updaterComponents.end(), Component::SortingPriorityGreaterThan()); } else { // we have no updates, no need to store possible dependencies d->clearUpdaterComponentLists(); } } catch (const Error &error) { d->clearUpdaterComponentLists(); emit finishUpdaterComponentsReset(QList()); d->setStatus(Failure, error.message()); // TODO: make sure we remove all message boxes inside the library at some point. MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), tr("Error"), error.message()); return false; } emit finishUpdaterComponentsReset(d->m_updaterComponents); return true; } void PackageManagerCore::restoreCheckState() { d->restoreCheckState(); } void PackageManagerCore::updateDisplayVersions(const QString &displayKey) { QHash componentsHash; foreach (QInstaller::Component *component, components(ComponentType::All)) componentsHash[component->name()] = component; // set display version for all components in list const QStringList &keys = componentsHash.keys(); foreach (const QString &key, keys) { QHash visited; if (componentsHash.value(key)->isInstalled()) { componentsHash.value(key)->setValue(scDisplayVersion, findDisplayVersion(key, componentsHash, scInstalledVersion, visited)); } visited.clear(); const QString displayVersionRemote = findDisplayVersion(key, componentsHash, scVersion, visited); if (displayVersionRemote.isEmpty()) componentsHash.value(key)->setValue(displayKey, tr("invalid")); else componentsHash.value(key)->setValue(displayKey, displayVersionRemote); } } QString PackageManagerCore::findDisplayVersion(const QString &componentName, const QHash &components, const QString &versionKey, QHash &visited) { if (!components.contains(componentName)) return QString(); const QString replaceWith = components.value(componentName)->value(scInheritVersion); visited[componentName] = true; if (replaceWith.isEmpty()) return components.value(componentName)->value(versionKey); if (visited.contains(replaceWith)) // cycle return QString(); return findDisplayVersion(replaceWith, components, versionKey, visited); } ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, const QString &objectName) const { ComponentModel *model = new ComponentModel(ComponentModelHelper::LastColumn, core); model->setObjectName(objectName); model->setHeaderData(ComponentModelHelper::NameColumn, Qt::Horizontal, ComponentModel::tr("Component Name")); model->setHeaderData(ComponentModelHelper::ActionColumn, Qt::Horizontal, ComponentModel::tr("Action")); model->setHeaderData(ComponentModelHelper::InstalledVersionColumn, Qt::Horizontal, ComponentModel::tr("Installed Version")); model->setHeaderData(ComponentModelHelper::NewVersionColumn, Qt::Horizontal, ComponentModel::tr("New Version")); model->setHeaderData(ComponentModelHelper::ReleaseDateColumn, Qt::Horizontal, ComponentModel::tr("Release Date")); model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal, ComponentModel::tr("Size")); connect(model, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)), this, SLOT(componentsToInstallNeedsRecalculation())); return model; } QStringList PackageManagerCore::filesForDelayedDeletion() const { return d->m_filesForDelayedDeletion; } void PackageManagerCore::addFilesForDelayedDeletion(const QStringList &files) { d->m_filesForDelayedDeletion.append(files); } src/libs/installer/packagemanagercore.h000066400000000000000000000333461325366651500205700ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PACKAGEMANAGERCORE_H #define PACKAGEMANAGERCORE_H #include "binaryformat.h" #include "protocol.h" #include "repository.h" #include "qinstallerglobal.h" #include #include #include #include namespace QInstaller { class Component; class ComponentModel; class ScriptEngine; class PackageManagerCorePrivate; class PackageManagerProxyFactory; class Settings; // -- PackageManagerCore class INSTALLER_EXPORT PackageManagerCore : public QObject { Q_OBJECT Q_DISABLE_COPY(PackageManagerCore) Q_ENUMS(Status WizardPage) Q_PROPERTY(int status READ status NOTIFY statusChanged) public: PackageManagerCore(); PackageManagerCore(qint64 magicmaker, const QList &ops, const QString &socketName = QString(), const QString &key = QLatin1String(Protocol::DefaultAuthorizationKey), Protocol::Mode mode = Protocol::Mode::Production); ~PackageManagerCore(); // status enum Status { Success = EXIT_SUCCESS, Failure = EXIT_FAILURE, Running, Canceled, Unfinished, ForceUpdate }; Status status() const; QString error() const; enum WizardPage { Introduction = 0x1000, TargetDirectory = 0x2000, ComponentSelection = 0x3000, LicenseCheck = 0x4000, StartMenuSelection = 0x5000, ReadyForInstallation = 0x6000, PerformInstallation = 0x7000, InstallationFinished = 0x8000, End = 0xffff }; enum struct ComponentType { Root = 0x1, Descendants = 0x2, Dependencies = 0x4, Replacements = 0x8, AllNoReplacements = (Root | Descendants | Dependencies), All = (Root | Descendants | Dependencies | Replacements) }; Q_DECLARE_FLAGS(ComponentTypes, ComponentType) static QFont virtualComponentsFont(); static void setVirtualComponentsFont(const QFont &font); static bool virtualComponentsVisible(); static void setVirtualComponentsVisible(bool visible); static bool noForceInstallation(); static void setNoForceInstallation(bool value); static bool createLocalRepositoryFromBinary(); static void setCreateLocalRepositoryFromBinary(bool create); static Component *componentByName(const QString &name, const QList &components); bool fetchLocalPackagesTree(); LocalPackagesHash localInstalledPackages(); void networkSettingsChanged(); PackageManagerProxyFactory *proxyFactory() const; void setProxyFactory(PackageManagerProxyFactory *factory); PackagesList remotePackages(); bool fetchRemotePackagesTree(); bool fetchCompressedPackagesTree(); bool run(); void reset(const QHash ¶ms); void setGuiObject(QObject *gui); QObject *guiObject() const; Q_INVOKABLE void setDependsOnLocalInstallerBinary(); Q_INVOKABLE bool localInstallerBinaryUsed(); Q_INVOKABLE QList execute(const QString &program, const QStringList &arguments = QStringList(), const QString &stdIn = QString(), const QString &stdInCodec = QLatin1String("latin1"), const QString &stdOutCodec = QLatin1String("latin1")) const; Q_INVOKABLE bool executeDetached(const QString &program, const QStringList &arguments = QStringList(), const QString &workingDirectory = QString()) const; Q_INVOKABLE QString environmentVariable(const QString &name) const; Q_INVOKABLE bool operationExists(const QString &name); Q_INVOKABLE bool performOperation(const QString &name, const QStringList &arguments); Q_INVOKABLE static bool versionMatches(const QString &version, const QString &requirement); Q_INVOKABLE static QString findLibrary(const QString &name, const QStringList &paths = QStringList()); Q_INVOKABLE static QString findPath(const QString &name, const QStringList &paths = QStringList()); Q_INVOKABLE void setInstallerBaseBinary(const QString &path); // parameter handling Q_INVOKABLE bool containsValue(const QString &key) const; Q_INVOKABLE void setValue(const QString &key, const QString &value); Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const; Q_INVOKABLE QStringList values(const QString &key, const QStringList &defaultValue = QStringList()) const; // a way to have global flags shareable from a component script to another one // Deprecated since 2.0.0 Q_INVOKABLE bool sharedFlag(const QString &key) const; Q_INVOKABLE void setSharedFlag(const QString &key, bool value = true); QString replaceVariables(const QString &str) const; QByteArray replaceVariables(const QByteArray &str) const; QStringList replaceVariables(const QStringList &str) const; void writeMaintenanceTool(); void writeMaintenanceConfigFiles(); QString maintenanceToolName() const; QString installerBinaryPath() const; bool testChecksum() const; void setTestChecksum(bool test); Q_INVOKABLE void addUserRepositories(const QStringList &repositories); Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories, bool replace = false, bool compressed = false); Q_INVOKABLE void autoAcceptMessageBoxes(); Q_INVOKABLE void autoRejectMessageBoxes(); Q_INVOKABLE void setMessageBoxAutomaticAnswer(const QString &identifier, int button); quint64 size(QInstaller::Component *component, const QString &value) const; Q_INVOKABLE bool isFileExtensionRegistered(const QString &extension) const; Q_INVOKABLE bool fileExists(const QString &filePath) const; Q_INVOKABLE QString readFile(const QString &filePath, const QString &codecName) const; public: ScriptEngine *componentScriptEngine() const; ScriptEngine *controlScriptEngine() const; // component handling void appendRootComponent(Component *components); void appendUpdaterComponent(Component *components); QList components(ComponentTypes mask) const; Component *componentByName(const QString &identifier) const; Q_INVOKABLE bool calculateComponentsToInstall() const; QList orderedComponentsToInstall() const; bool calculateComponents(QString *displayString); Q_INVOKABLE bool calculateComponentsToUninstall() const; QList componentsToUninstall() const; QString componentsToInstallError() const; QString installReason(Component *component) const; QList dependees(const Component *component) const; ComponentModel *defaultComponentModel() const; ComponentModel *updaterComponentModel() const; void updateComponentsSilently(); // convenience Q_INVOKABLE bool isInstaller() const; Q_INVOKABLE bool isOfflineOnly() const; Q_INVOKABLE void setUninstaller(); Q_INVOKABLE bool isUninstaller() const; Q_INVOKABLE void setUpdater(); Q_INVOKABLE bool isUpdater() const; Q_INVOKABLE void setPackageManager(); Q_INVOKABLE bool isPackageManager() const; bool isMaintainer() const; bool isVerbose() const; void setVerbose(bool on); Q_INVOKABLE bool gainAdminRights(); Q_INVOKABLE void dropAdminRights(); Q_INVOKABLE quint64 requiredDiskSpace() const; Q_INVOKABLE quint64 requiredTemporaryDiskSpace() const; Q_INVOKABLE bool isProcessRunning(const QString &name) const; Q_INVOKABLE bool killProcess(const QString &absoluteFilePath) const; Settings &settings() const; Q_INVOKABLE bool addWizardPage(QInstaller::Component *component, const QString &name, int page); Q_INVOKABLE bool removeWizardPage(QInstaller::Component *component, const QString &name); Q_INVOKABLE bool addWizardPageItem(QInstaller::Component *component, const QString &name, int page); Q_INVOKABLE bool removeWizardPageItem(QInstaller::Component *component, const QString &name); Q_INVOKABLE bool setDefaultPageVisible(int page, bool visible); Q_INVOKABLE void setValidatorForCustomPage(QInstaller::Component *component, const QString &name, const QString &callbackName); void rollBackInstallation(); int downloadNeededArchives(double partProgressSize); bool needsHardRestart() const; void setNeedsHardRestart(bool needsHardRestart = true); bool finishedWithSuccess() const; QStringList filesForDelayedDeletion() const; void addFilesForDelayedDeletion(const QStringList &files); public Q_SLOTS: bool runInstaller(); bool runUninstaller(); bool runPackageUpdater(); void interrupt(); void setCanceled(); void languageChanged(); void setCompleteUninstallation(bool complete); void cancelMetaInfoJob(); void componentsToInstallNeedsRecalculation(); Q_SIGNALS: void aboutCalculateComponentsToInstall() const; void finishedCalculateComponentsToInstall() const; void aboutCalculateComponentsToUninstall() const; void finishedCalculateComponentsToUninstall() const; void componentAdded(QInstaller::Component *comp); void rootComponentsAdded(QList components); void updaterComponentsAdded(QList components); void valueChanged(const QString &key, const QString &value); void statusChanged(QInstaller::PackageManagerCore::Status); void currentPageChanged(int page); void finishButtonClicked(); void metaJobProgress(int progress); void metaJobTotalProgress(int progress); void metaJobInfoMessage(const QString &message); void startAllComponentsReset(); void finishAllComponentsReset(const QList &rootComponents); void startUpdaterComponentsReset(); void finishUpdaterComponentsReset(const QList &componentsWithUpdates); void installationStarted(); void installationInterrupted(); void installationFinished(); void updateFinished(); void uninstallationStarted(); void uninstallationFinished(); void titleMessageChanged(const QString &title); void wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page); void wizardPageRemovalRequested(QWidget *widget); void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page); void wizardWidgetRemovalRequested(QWidget *widget); void wizardPageVisibilityChangeRequested(bool visible, int page); void setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name, const QString &callbackName); void setAutomatedPageSwitchEnabled(bool request); void coreNetworkSettingsChanged(); void guiObjectChanged(QObject *gui); private: struct Data { Package *package; QHash *components; const LocalPackagesHash *installedPackages; QHash replacementToExchangeables; }; bool updateComponentData(struct Data &data, QInstaller::Component *component); void storeReplacedComponents(QHash &components, const struct Data &data); bool fetchAllPackages(const PackagesList &remotePackages, const LocalPackagesHash &localPackages); bool fetchUpdaterPackages(const PackagesList &remotePackages, const LocalPackagesHash &localPackages); void updateDisplayVersions(const QString &displayKey); QString findDisplayVersion(const QString &componentName, const QHash &components, const QString& versionKey, QHash &visited); ComponentModel *componentModel(PackageManagerCore *core, const QString &objectName) const; QList componentsMarkedForInstallation() const; bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesHash installedPackages); private: PackageManagerCorePrivate *const d; friend class PackageManagerCorePrivate; private: // remove once we deprecate isSelected, setSelected etc... friend class ComponentSelectionPage; void restoreCheckState(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(PackageManagerCore::ComponentTypes) } Q_DECLARE_METATYPE(QInstaller::PackageManagerCore*) #endif // PACKAGEMANAGERCORE_H src/libs/installer/packagemanagercore_p.cpp000066400000000000000000003210761325366651500214420ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "packagemanagercore_p.h" #include "adminauthorization.h" #include "binarycontent.h" #include "binaryformatenginehandler.h" #include "binarylayout.h" #include "component.h" #include "scriptengine.h" #include "componentmodel.h" #include "errors.h" #include "fileio.h" #include "remotefileengine.h" #include "graph.h" #include "messageboxhandler.h" #include "packagemanagercore.h" #include "progresscoordinator.h" #include "qprocesswrapper.h" #include "protocol.h" #include "qsettingswrapper.h" #include "installercalculator.h" #include "uninstallercalculator.h" #include "componentchecker.h" #include "globals.h" #include "selfrestarter.h" #include "filedownloaderfactory.h" #include "updateoperationfactory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #endif #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) namespace QInstaller { class OperationTracer { public: OperationTracer(Operation *operation) : m_operation(0) { // don't create output for that hacky pseudo operation if (operation->name() != QLatin1String("MinimumProgress")) m_operation = operation; } void trace(const QString &state) { if (!m_operation) return; qDebug().noquote() << QString::fromLatin1("%1 %2 operation: %3").arg(state, m_operation->value( QLatin1String("component")).toString(), m_operation->name()); qDebug().noquote() << QString::fromLatin1("\t- arguments: %1").arg(m_operation->arguments() .join(QLatin1String(", "))); } ~OperationTracer() { if (!m_operation) return; qDebug() << "Done"; } private: Operation *m_operation; }; static bool runOperation(Operation *operation, PackageManagerCorePrivate::OperationType type) { OperationTracer tracer(operation); switch (type) { case PackageManagerCorePrivate::Backup: tracer.trace(QLatin1String("backup")); operation->backup(); return true; case PackageManagerCorePrivate::Perform: tracer.trace(QLatin1String("perform")); return operation->performOperation(); case PackageManagerCorePrivate::Undo: tracer.trace(QLatin1String("undo")); return operation->undoOperation(); default: Q_ASSERT(!"unexpected operation type"); } return false; } static QStringList checkRunningProcessesFromList(const QStringList &processList) { const QList allProcesses = runningProcesses(); QStringList stillRunningProcesses; foreach (const QString &process, processList) { if (!process.isEmpty() && PackageManagerCorePrivate::isProcessRunning(process, allProcesses)) stillRunningProcesses.append(process); } return stillRunningProcesses; } static void deferredRename(const QString &oldName, const QString &newName, bool restart = false) { #ifdef Q_OS_WIN QStringList arguments; { QTemporaryFile f(QDir::temp().absoluteFilePath(QLatin1String("deferredrenameXXXXXX.vbs"))); QInstaller::openForWrite(&f); f.setAutoRemove(false); arguments << QDir::toNativeSeparators(f.fileName()) << QDir::toNativeSeparators(oldName) << QDir::toNativeSeparators(QFileInfo(oldName).dir().absoluteFilePath(QFileInfo(newName) .fileName())); QTextStream batch(&f); batch.setCodec("UTF-16"); batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n"; batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n"; batch << QString::fromLatin1("file = \"%1\"\n").arg(arguments[2]); batch << "on error resume next\n"; batch << "while fso.FileExists(file)\n"; batch << " fso.DeleteFile(file)\n"; batch << " WScript.Sleep(1000)\n"; batch << "wend\n"; batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(arguments[1]); if (restart) { //Restart with same command line arguments as first executable QStringList commandLineArguments = QCoreApplication::arguments(); batch << QString::fromLatin1("tmp.exec \"%1 --updater").arg(arguments[2]); //Skip the first argument as that is executable itself for (int i = 1; i < commandLineArguments.count(); i++) { batch << QString::fromLatin1(" %1").arg(commandLineArguments.at(i)); } batch << QString::fromLatin1("\"\n"); } batch << "fso.DeleteFile(WScript.ScriptFullName)\n"; } QProcessWrapper::startDetached(QLatin1String("cscript"), QStringList() << QLatin1String("//Nologo") << arguments[0]); #else QFile::remove(newName); QFile::rename(oldName, newName); SelfRestarter::setRestartOnQuit(restart); #endif } // -- PackageManagerCorePrivate PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) : m_updateFinder(0) , m_compressedFinder(0) , m_localPackageHub(std::make_shared()) , m_core(core) , m_updates(false) , m_repoFetched(false) , m_updateSourcesAdded(false) , m_componentsToInstallCalculated(false) , m_componentScriptEngine(0) , m_controlScriptEngine(0) , m_installerCalculator(0) , m_uninstallerCalculator(0) , m_proxyFactory(0) , m_defaultModel(0) , m_updaterModel(0) , m_guiObject(0) , m_remoteFileEngineHandler(0) { } PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, const QList &performedOperations) : m_updateFinder(0) , m_compressedFinder(0) , m_localPackageHub(std::make_shared()) , m_status(PackageManagerCore::Unfinished) , m_needsHardRestart(false) , m_testChecksum(false) , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) , m_completeUninstall(false) , m_needToWriteMaintenanceTool(false) , m_dependsOnLocalInstallerBinary(false) , m_core(core) , m_updates(false) , m_repoFetched(false) , m_updateSourcesAdded(false) , m_magicBinaryMarker(magicInstallerMaker) , m_componentsToInstallCalculated(false) , m_componentScriptEngine(0) , m_controlScriptEngine(0) , m_installerCalculator(0) , m_uninstallerCalculator(0) , m_proxyFactory(0) , m_defaultModel(0) , m_updaterModel(0) , m_guiObject(0) , m_remoteFileEngineHandler(new RemoteFileEngineHandler) { foreach (const OperationBlob &operation, performedOperations) { QScopedPointer op(KDUpdater::UpdateOperationFactory::instance() .create(operation.name, core)); if (op.isNull()) { qWarning() << "Failed to load unknown operation" << operation.name; continue; } if (!op->fromXml(operation.xml)) { qWarning() << "Failed to load XML for operation" << operation.name; continue; } m_performedOperationsOld.append(op.take()); } connect(this, &PackageManagerCorePrivate::installationStarted, m_core, &PackageManagerCore::installationStarted); connect(this, &PackageManagerCorePrivate::installationFinished, m_core, &PackageManagerCore::installationFinished); connect(this, &PackageManagerCorePrivate::uninstallationStarted, m_core, &PackageManagerCore::uninstallationStarted); connect(this, &PackageManagerCorePrivate::uninstallationFinished, m_core, &PackageManagerCore::uninstallationFinished); } PackageManagerCorePrivate::~PackageManagerCorePrivate() { clearAllComponentLists(); clearUpdaterComponentLists(); clearInstallerCalculator(); clearUninstallerCalculator(); qDeleteAll(m_ownedOperations); qDeleteAll(m_performedOperationsOld); qDeleteAll(m_performedOperationsCurrentSession); delete m_updateFinder; delete m_proxyFactory; delete m_defaultModel; delete m_updaterModel; // at the moment the tabcontroller deletes the m_gui, this needs to be changed in the future // delete m_gui; } /*! Return true, if a process with \a name is running. On Windows, comparison is case-insensitive. */ /* static */ bool PackageManagerCorePrivate::isProcessRunning(const QString &name, const QList &processes) { QList::const_iterator it; for (it = processes.constBegin(); it != processes.constEnd(); ++it) { if (it->name.isEmpty()) continue; #ifndef Q_OS_WIN if (it->name == name) return true; const QFileInfo fi(it->name); if (fi.fileName() == name || fi.baseName() == name) return true; #else if (it->name.toLower() == name.toLower()) return true; if (it->name.toLower() == QDir::toNativeSeparators(name.toLower())) return true; const QFileInfo fi(it->name); if (fi.fileName().toLower() == name.toLower() || fi.baseName().toLower() == name.toLower()) return true; #endif } return false; } /* static */ bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation, OperationType type) { QFutureWatcher futureWatcher; const QFuture future = QtConcurrent::run(runOperation, operation, type); QEventLoop loop; QObject::connect(&futureWatcher, &decltype(futureWatcher)::finished, &loop, &QEventLoop::quit, Qt::QueuedConnection); futureWatcher.setFuture(future); if (!future.isFinished()) loop.exec(); return future.result(); } QString PackageManagerCorePrivate::targetDir() const { return m_core->value(scTargetDir); } QString PackageManagerCorePrivate::configurationFileName() const { return m_core->value(scTargetConfigurationFile, QLatin1String("components.xml")); } QString PackageManagerCorePrivate::componentsXmlPath() const { return QDir::toNativeSeparators(QDir(QDir::cleanPath(targetDir())) .absoluteFilePath(configurationFileName())); } bool PackageManagerCorePrivate::buildComponentTree(QHash &components, bool loadScript) { try { if (statusCanceledOrFailed()) return false; // append all components to their respective parents QHash::const_iterator it; for (it = components.constBegin(); it != components.constEnd(); ++it) { QString id = it.key(); QInstaller::Component *component = it.value(); while (!id.isEmpty() && component->parentComponent() == 0) { id = id.section(QLatin1Char('.'), 0, -2); if (components.contains(id)) components[id]->appendComponent(component); } } // append all components w/o parent to the direct list foreach (QInstaller::Component *component, components) { if (component->parentComponent() == 0) m_core->appendRootComponent(component); } // after everything is set up, load the scripts if needed if (loadScript) { foreach (QInstaller::Component *component, components) component->loadComponentScript(); } // now we can preselect components in the tree foreach (QInstaller::Component *component, components) { // set the checked state for all components without child (means without tristate) if (component->isCheckable() && !component->isTristate()) { if (component->isDefault() && isInstaller()) component->setCheckState(Qt::Checked); else if (component->isInstalled()) component->setCheckState(Qt::Checked); } } std::sort(m_rootComponents.begin(), m_rootComponents.end(), Component::SortingPriorityGreaterThan()); storeCheckState(); foreach (QInstaller::Component *component, components) component->setCheckState(Qt::Checked); clearInstallerCalculator(); if (installerCalculator()->appendComponentsToInstall(components.values()) == false) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), tr("Unresolved dependencies"), installerCalculator()->componentsToInstallError()); return false; } restoreCheckState(); foreach (QInstaller::Component *component, components) { const QStringList warnings = ComponentChecker::checkComponent(component); foreach (const QString &warning, warnings) qCWarning(lcComponentChecker).noquote() << warning; } } catch (const Error &error) { clearAllComponentLists(); emit m_core->finishAllComponentsReset(QList()); setStatus(PackageManagerCore::Failure, error.message()); // TODO: make sure we remove all message boxes inside the library at some point. MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"), tr("Error"), error.message()); return false; } return true; } void PackageManagerCorePrivate::cleanUpComponentEnvironment() { // clean up registered (downloaded) data if (m_core->isMaintainer()) BinaryFormatEngineHandler::instance()->clear(); // there could be still some references to already deleted components, // so we need to remove the current component script engine delete m_componentScriptEngine; m_componentScriptEngine = 0; } ScriptEngine *PackageManagerCorePrivate::componentScriptEngine() const { if (!m_componentScriptEngine) m_componentScriptEngine = new ScriptEngine(m_core); return m_componentScriptEngine; } ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const { if (!m_controlScriptEngine) m_controlScriptEngine = new ScriptEngine(m_core); return m_controlScriptEngine; } void PackageManagerCorePrivate::clearAllComponentLists() { QList toDelete; toDelete << m_rootComponents; m_rootComponents.clear(); m_rootDependencyReplacements.clear(); const QList > list = m_componentsToReplaceAllMode.values(); for (int i = 0; i < list.count(); ++i) toDelete << list.at(i).second; m_componentsToReplaceAllMode.clear(); m_componentsToInstallCalculated = false; qDeleteAll(toDelete); cleanUpComponentEnvironment(); } void PackageManagerCorePrivate::clearUpdaterComponentLists() { QSet usedComponents = QSet::fromList(m_updaterComponents + m_updaterComponentsDeps); const QList > list = m_componentsToReplaceUpdaterMode.values(); for (int i = 0; i < list.count(); ++i) { if (usedComponents.contains(list.at(i).second)) qWarning() << "a replacement was already in the list - is that correct?"; else usedComponents.insert(list.at(i).second); } m_updaterComponents.clear(); m_updaterComponentsDeps.clear(); m_updaterDependencyReplacements.clear(); m_componentsToReplaceUpdaterMode.clear(); m_componentsToInstallCalculated = false; qDeleteAll(usedComponents); cleanUpComponentEnvironment(); } QList &PackageManagerCorePrivate::replacementDependencyComponents() { return (!isUpdater()) ? m_rootDependencyReplacements : m_updaterDependencyReplacements; } QHash > &PackageManagerCorePrivate::componentsToReplace() { return (!isUpdater()) ? m_componentsToReplaceAllMode : m_componentsToReplaceUpdaterMode; } void PackageManagerCorePrivate::clearInstallerCalculator() { delete m_installerCalculator; m_installerCalculator = 0; } InstallerCalculator *PackageManagerCorePrivate::installerCalculator() const { if (!m_installerCalculator) { PackageManagerCorePrivate *const pmcp = const_cast (this); pmcp->m_installerCalculator = new InstallerCalculator( m_core->components(PackageManagerCore::ComponentType::AllNoReplacements)); } return m_installerCalculator; } void PackageManagerCorePrivate::clearUninstallerCalculator() { delete m_uninstallerCalculator; m_uninstallerCalculator = 0; } UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const { if (!m_uninstallerCalculator) { PackageManagerCorePrivate *const pmcp = const_cast (this); QList installedComponents; foreach (const QString &name, pmcp->localInstalledPackages().keys()) { if (Component *component = m_core->componentByName(name)) { if (!component->uninstallationRequested()) installedComponents.append(component); } } pmcp->m_uninstallerCalculator = new UninstallerCalculator(installedComponents); } return m_uninstallerCalculator; } void PackageManagerCorePrivate::initialize(const QHash ¶ms) { m_coreCheckedHash.clear(); m_data = PackageManagerCoreData(params); m_componentsToInstallCalculated = false; #ifdef Q_OS_LINUX if (m_launchedAsRoot) m_data.setValue(scTargetDir, replaceVariables(m_data.settings().adminTargetDir())); #endif if (!m_core->isInstaller()) { #ifdef Q_OS_OSX readMaintenanceConfigFiles(QCoreApplication::applicationDirPath() + QLatin1String("/../../..")); #else readMaintenanceConfigFiles(QCoreApplication::applicationDirPath()); #endif } processFilesForDelayedDeletion(); m_data.setDynamicPredefinedVariables(); disconnect(this, &PackageManagerCorePrivate::installationStarted, ProgressCoordinator::instance(), &ProgressCoordinator::reset); connect(this, &PackageManagerCorePrivate::installationStarted, ProgressCoordinator::instance(), &ProgressCoordinator::reset); disconnect(this, &PackageManagerCorePrivate::uninstallationStarted, ProgressCoordinator::instance(), &ProgressCoordinator::reset); connect(this, &PackageManagerCorePrivate::uninstallationStarted, ProgressCoordinator::instance(), &ProgressCoordinator::reset); if (!isInstaller()) m_localPackageHub->setFileName(componentsXmlPath()); if (isInstaller() || m_localPackageHub->applicationName().isEmpty()) { // TODO: this seems to be wrong, we should ask for ProductName defaulting to applicationName... m_localPackageHub->setApplicationName(m_data.settings().applicationName()); } if (isInstaller() || m_localPackageHub->applicationVersion().isEmpty()) m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); if (isInstaller()) m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0)); m_metadataJob.disconnect(); m_metadataJob.setAutoDelete(false); m_metadataJob.setPackageManagerCore(m_core); connect(&m_metadataJob, &Job::infoMessage, this, &PackageManagerCorePrivate::infoMessage); connect(&m_metadataJob, &Job::progress, this, &PackageManagerCorePrivate::infoProgress); connect(&m_metadataJob, &Job::totalProgress, this, &PackageManagerCorePrivate::totalProgress); KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); } bool PackageManagerCorePrivate::isOfflineOnly() const { if (!isInstaller()) return false; QSettings confInternal(QLatin1String(":/config/config-internal.ini"), QSettings::IniFormat); return confInternal.value(QLatin1String("offlineOnly"), false).toBool(); } QString PackageManagerCorePrivate::installerBinaryPath() const { return qApp->applicationFilePath(); } bool PackageManagerCorePrivate::isInstaller() const { return m_magicBinaryMarker == BinaryContent::MagicInstallerMarker; } bool PackageManagerCorePrivate::isUninstaller() const { return m_magicBinaryMarker == BinaryContent::MagicUninstallerMarker; } bool PackageManagerCorePrivate::isUpdater() const { return m_magicBinaryMarker == BinaryContent::MagicUpdaterMarker; } bool PackageManagerCorePrivate::isPackageManager() const { return m_magicBinaryMarker == BinaryContent::MagicPackageManagerMarker; } bool PackageManagerCorePrivate::statusCanceledOrFailed() const { return m_status == PackageManagerCore::Canceled || m_status == PackageManagerCore::Failure; } void PackageManagerCorePrivate::setStatus(int status, const QString &error) { m_error = error; if (!error.isEmpty()) qDebug() << m_error; if (m_status != status) { m_status = status; emit m_core->statusChanged(PackageManagerCore::Status(m_status)); } } QString PackageManagerCorePrivate::replaceVariables(const QString &str) const { return m_data.replaceVariables(str); } QByteArray PackageManagerCorePrivate::replaceVariables(const QByteArray &ba) const { return m_data.replaceVariables(ba); } /*! \internal Creates an update operation owned by the installer, not by any component. */ Operation *PackageManagerCorePrivate::createOwnedOperation(const QString &type) { m_ownedOperations.append(KDUpdater::UpdateOperationFactory::instance().create(type, m_core)); return m_ownedOperations.last(); } /*! \internal Removes \a operation from the operations owned by the installer, returns the very same operation if the operation was found, otherwise 0. */ Operation *PackageManagerCorePrivate::takeOwnedOperation(Operation *operation) { if (!m_ownedOperations.contains(operation)) return 0; m_ownedOperations.removeAll(operation); return operation; } QString PackageManagerCorePrivate::maintenanceToolName() const { QString filename = m_data.settings().maintenanceToolName(); #if defined(Q_OS_OSX) if (QInstaller::isInBundle(QCoreApplication::applicationDirPath())) filename += QLatin1String(".app/Contents/MacOS/") + filename; #elif defined(Q_OS_WIN) filename += QLatin1String(".exe"); #endif return QString::fromLatin1("%1/%2").arg(targetDir()).arg(filename); } static QNetworkProxy readProxy(QXmlStreamReader &reader) { QNetworkProxy proxy(QNetworkProxy::HttpProxy); while (reader.readNextStartElement()) { if (reader.name() == QLatin1String("Host")) proxy.setHostName(reader.readElementText()); else if (reader.name() == QLatin1String("Port")) proxy.setPort(reader.readElementText().toInt()); else if (reader.name() == QLatin1String("Username")) proxy.setUser(reader.readElementText()); else if (reader.name() == QLatin1String("Password")) proxy.setPassword(reader.readElementText()); else reader.skipCurrentElement(); } return proxy; } static QSet readRepositories(QXmlStreamReader &reader, bool isDefault) { QSet set; while (reader.readNextStartElement()) { if (reader.name() == QLatin1String("Repository")) { Repository repo(QString(), isDefault); while (reader.readNextStartElement()) { if (reader.name() == QLatin1String("Host")) repo.setUrl(reader.readElementText()); else if (reader.name() == QLatin1String("Username")) repo.setUsername(reader.readElementText()); else if (reader.name() == QLatin1String("Password")) repo.setPassword(reader.readElementText()); else if (reader.name() == QLatin1String("DisplayName")) repo.setDisplayName(reader.readElementText()); else if (reader.name() == QLatin1String("Enabled")) repo.setEnabled(bool(reader.readElementText().toInt())); else reader.skipCurrentElement(); } set.insert(repo); } else { reader.skipCurrentElement(); } } return set; } void PackageManagerCorePrivate::writeMaintenanceConfigFiles() { // write current state (variables) to the maintenance tool ini file const QString iniPath = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolIniFile(); QVariantHash variables; // Do not change to QVariantMap! Breaks existing .ini files, // cause the variant types do not match while restoring the variables from the file. QSettingsWrapper cfg(iniPath, QSettingsWrapper::IniFormat); foreach (const QString &key, m_data.keys()) { if (key == scRunProgramDescription || key == scRunProgram || key == scRunProgramArguments) continue; QVariant value = m_data.value(key); if (value.canConvert(QVariant::String)) value = replacePath(value.toString(), targetDir(), QLatin1String(scRelocatable)); variables.insert(key, value); } cfg.setValue(QLatin1String("Variables"), variables); QVariantList repos; // Do not change either! foreach (const Repository &repo, m_data.settings().defaultRepositories()) repos.append(QVariant().fromValue(repo)); cfg.setValue(QLatin1String("DefaultRepositories"), repos); cfg.setValue(QLatin1String("FilesForDelayedDeletion"), m_filesForDelayedDeletion); cfg.sync(); if (cfg.status() != QSettingsWrapper::NoError) { const QString reason = cfg.status() == QSettingsWrapper::AccessError ? tr("Access error") : tr("Format error"); throw Error(tr("Cannot write installer configuration to %1: %2").arg(iniPath, reason)); } QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml")); if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QXmlStreamWriter writer(&file); writer.setCodec("UTF-8"); writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement(QLatin1String("Network")); writer.writeTextElement(QLatin1String("ProxyType"), QString::number(m_data.settings().proxyType())); writer.writeStartElement(QLatin1String("Ftp")); const QNetworkProxy &ftpProxy = m_data.settings().ftpProxy(); writer.writeTextElement(QLatin1String("Host"), ftpProxy.hostName()); writer.writeTextElement(QLatin1String("Port"), QString::number(ftpProxy.port())); writer.writeTextElement(QLatin1String("Username"), ftpProxy.user()); writer.writeTextElement(QLatin1String("Password"), ftpProxy.password()); writer.writeEndElement(); writer.writeStartElement(QLatin1String("Http")); const QNetworkProxy &httpProxy = m_data.settings().httpProxy(); writer.writeTextElement(QLatin1String("Host"), httpProxy.hostName()); writer.writeTextElement(QLatin1String("Port"), QString::number(httpProxy.port())); writer.writeTextElement(QLatin1String("Username"), httpProxy.user()); writer.writeTextElement(QLatin1String("Password"), httpProxy.password()); writer.writeEndElement(); writer.writeStartElement(QLatin1String("Repositories")); foreach (const Repository &repo, m_data.settings().userRepositories()) { writer.writeStartElement(QLatin1String("Repository")); writer.writeTextElement(QLatin1String("Host"), repo.url().toString()); writer.writeTextElement(QLatin1String("Username"), repo.username()); writer.writeTextElement(QLatin1String("Password"), repo.password()); writer.writeTextElement(QLatin1String("Enabled"), QString::number(repo.isEnabled())); writer.writeEndElement(); } writer.writeEndElement(); writer.writeEndElement(); } } void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &targetDir) { QSettingsWrapper cfg(targetDir + QLatin1Char('/') + m_data.settings().maintenanceToolIniFile(), QSettingsWrapper::IniFormat); const QVariantHash v = cfg.value(QLatin1String("Variables")).toHash(); // Do not change to // QVariantMap! Breaks reading from existing .ini files, cause the variant types do not match. for (QVariantHash::const_iterator it = v.constBegin(); it != v.constEnd(); ++it) { m_data.setValue(it.key(), replacePath(it.value().toString(), QLatin1String(scRelocatable), targetDir)); } QSet repos; const QVariantList variants = cfg.value(QLatin1String("DefaultRepositories")) .toList(); // Do not change either! foreach (const QVariant &variant, variants) repos.insert(variant.value()); if (!repos.isEmpty()) m_data.settings().setDefaultRepositories(repos); m_filesForDelayedDeletion = cfg.value(QLatin1String("FilesForDelayedDeletion")).toStringList(); QFile file(targetDir + QLatin1String("/network.xml")); if (!file.open(QIODevice::ReadOnly)) return; QXmlStreamReader reader(&file); while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::StartElement: { if (reader.name() == QLatin1String("Network")) { while (reader.readNextStartElement()) { const QStringRef name = reader.name(); if (name == QLatin1String("Ftp")) { m_data.settings().setFtpProxy(readProxy(reader)); } else if (name == QLatin1String("Http")) { m_data.settings().setHttpProxy(readProxy(reader)); } else if (reader.name() == QLatin1String("Repositories")) { m_data.settings().addUserRepositories(readRepositories(reader, false)); } else if (name == QLatin1String("ProxyType")) { m_data.settings().setProxyType(Settings::ProxyType(reader.readElementText().toInt())); } else { reader.skipCurrentElement(); } } } } break; case QXmlStreamReader::Invalid: { qDebug() << reader.errorString(); } break; default: break; } } } void PackageManagerCorePrivate::callBeginInstallation(const QList &componentList) { foreach (Component *component, componentList) component->beginInstallation(); } void PackageManagerCorePrivate::stopProcessesForUpdates(const QList &components) { QStringList processList; foreach (const Component *component, components) processList << m_core->replaceVariables(component->stopProcessForUpdateRequests()); std::sort(processList.begin(), processList.end()); processList.erase(std::unique(processList.begin(), processList.end()), processList.end()); if (processList.isEmpty()) return; while (true) { const QStringList processes = checkRunningProcessesFromList(processList); if (processes.isEmpty()) return; const QMessageBox::StandardButton button = MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), QLatin1String("stopProcessesForUpdates"), tr("Stop Processes"), tr("These processes " "should be stopped to continue:\n\n%1").arg(QDir::toNativeSeparators(processes .join(QLatin1String("\n")))), QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Retry); if (button == QMessageBox::Ignore) return; if (button == QMessageBox::Cancel) { m_core->setCanceled(); throw Error(tr("Installation canceled by user")); } } } int PackageManagerCorePrivate::countProgressOperations(const OperationList &operations) { int operationCount = 0; foreach (Operation *operation, operations) { if (QObject *operationObject = dynamic_cast (operation)) { const QMetaObject *const mo = operationObject->metaObject(); if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) operationCount++; } } return operationCount; } int PackageManagerCorePrivate::countProgressOperations(const QList &components) { int operationCount = 0; foreach (Component *component, components) operationCount += countProgressOperations(component->operations()); return operationCount; } void PackageManagerCorePrivate::connectOperationToInstaller(Operation *const operation, double operationPartSize) { Q_ASSERT(operationPartSize); QObject *const operationObject = dynamic_cast< QObject*> (operation); if (operationObject != 0) { const QMetaObject *const mo = operationObject->metaObject(); if (mo->indexOfSignal(QMetaObject::normalizedSignature("outputTextChanged(QString)")) > -1) { connect(operationObject, SIGNAL(outputTextChanged(QString)), ProgressCoordinator::instance(), SLOT(emitDetailTextChanged(QString))); } if (mo->indexOfSlot(QMetaObject::normalizedSignature("cancelOperation()")) > -1) connect(m_core, SIGNAL(installationInterrupted()), operationObject, SLOT(cancelOperation())); if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) { ProgressCoordinator::instance()->registerPartProgress(operationObject, SIGNAL(progressChanged(double)), operationPartSize); } } } Operation *PackageManagerCorePrivate::createPathOperation(const QFileInfo &fileInfo, const QString &componentName) { const bool isDir = fileInfo.isDir(); // create an operation with the dir/ file as target, it will get deleted on undo Operation *operation = createOwnedOperation(QLatin1String(isDir ? "Mkdir" : "Copy")); if (isDir) operation->setValue(QLatin1String("createddir"), fileInfo.absoluteFilePath()); operation->setValue(QLatin1String("component"), componentName); operation->setArguments(isDir ? QStringList() << fileInfo.absoluteFilePath() : QStringList() << QString() << fileInfo.absoluteFilePath()); return operation; } /*! This creates fake operations which remove stuff which was registered for uninstallation afterwards */ void PackageManagerCorePrivate::registerPathsForUninstallation( const QList > &pathsForUninstallation, const QString &componentName) { if (pathsForUninstallation.isEmpty()) return; QList >::const_iterator it; for (it = pathsForUninstallation.begin(); it != pathsForUninstallation.end(); ++it) { const bool wipe = it->second; const QString path = replaceVariables(it->first); const QFileInfo fi(path); // create a copy operation with the file as target -> it will get deleted on undo Operation *op = createPathOperation(fi, componentName); if (fi.isDir()) op->setValue(QLatin1String("forceremoval"), wipe ? scTrue : scFalse); addPerformed(takeOwnedOperation(op)); // get recursive afterwards if (fi.isDir() && !wipe) { QDirIterator dirIt(path, QDir::Hidden | QDir::AllEntries | QDir::System | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (dirIt.hasNext()) { dirIt.next(); op = createPathOperation(dirIt.fileInfo(), componentName); addPerformed(takeOwnedOperation(op)); } } } } void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, qint64 size, bool writeBinaryLayout) { QString maintenanceToolRenamedName = maintenanceToolName() + QLatin1String(".new"); qDebug() << "Writing maintenance tool:" << maintenanceToolRenamedName; ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Writing maintenance tool.")); QTemporaryFile out; QInstaller::openForWrite(&out); // throws an exception in case of error if (!input->seek(0)) throw Error(tr("Failed to seek in file %1: %2").arg(input->fileName(), input->errorString())); QInstaller::appendData(&out, input, size); if (writeBinaryLayout) { #ifdef Q_OS_OSX QDir resourcePath(QFileInfo(maintenanceToolRenamedName).dir()); if (!resourcePath.path().endsWith(QLatin1String("Contents/MacOS"))) throw Error(tr("Maintenance tool is not a bundle")); resourcePath.cdUp(); resourcePath.cd(QLatin1String("Resources")); // It's a bit odd to have only the magic in the data file, but this simplifies // other code a lot (since installers don't have any appended data either) QTemporaryFile dataOut; QInstaller::openForWrite(&dataOut); QInstaller::appendInt64(&dataOut, 0); // operations start QInstaller::appendInt64(&dataOut, 0); // operations end QInstaller::appendInt64(&dataOut, 0); // resource count QInstaller::appendInt64(&dataOut, 4 * sizeof(qint64)); // data block size QInstaller::appendInt64(&dataOut, BinaryContent::MagicUninstallerMarker); QInstaller::appendInt64(&dataOut, BinaryContent::MagicCookie); { QFile dummy(resourcePath.filePath(QLatin1String("installer.dat"))); if (dummy.exists() && !dummy.remove()) { throw Error(tr("Cannot remove data file \"%1\": %2").arg(dummy.fileName(), dummy.errorString())); } } if (!dataOut.rename(resourcePath.filePath(QLatin1String("installer.dat")))) { throw Error(tr("Cannot write maintenance tool data to %1: %2").arg(out.fileName(), out.errorString())); } dataOut.setAutoRemove(false); dataOut.setPermissions(dataOut.permissions() | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther); #else QInstaller::appendInt64(&out, 0); // operations start QInstaller::appendInt64(&out, 0); // operations end QInstaller::appendInt64(&out, 0); // resource count QInstaller::appendInt64(&out, 4 * sizeof(qint64)); // data block size QInstaller::appendInt64(&out, BinaryContent::MagicUninstallerMarker); QInstaller::appendInt64(&out, BinaryContent::MagicCookie); #endif } { QFile dummy(maintenanceToolRenamedName); if (dummy.exists() && !dummy.remove()) { throw Error(tr("Cannot remove data file \"%1\": %2").arg(dummy.fileName(), dummy.errorString())); } } if (!out.copy(maintenanceToolRenamedName)) { throw Error(tr("Cannot write maintenance tool to \"%1\": %2").arg(maintenanceToolRenamedName, out.errorString())); } QFile mt(maintenanceToolRenamedName); if (mt.setPermissions(out.permissions() | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser)) { qDebug() << "Wrote permissions for maintenance tool."; } else { qDebug() << "Failed to write permissions for maintenance tool."; } } void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *output, QFile *const input, const OperationList &performedOperations, const BinaryLayout &layout) { const qint64 dataBlockStart = output->pos(); QVector >resourceSegments; QVector >existingResourceSegments = layout.metaResourceSegments; const QString newDefaultResource = m_core->value(QString::fromLatin1("DefaultResourceReplacement")); if (!newDefaultResource.isEmpty()) { QFile file(newDefaultResource); if (file.open(QIODevice::ReadOnly)) { resourceSegments.append(Range::fromStartAndLength(output->pos(), file.size())); QInstaller::appendData(output, &file, file.size()); existingResourceSegments.remove(0); file.remove(); // clear all possible leftovers m_core->setValue(QString::fromLatin1("DefaultResourceReplacement"), QString()); } else { qWarning() << "Cannot replace default resource with" << QDir::toNativeSeparators(newDefaultResource); } } foreach (const Range &segment, existingResourceSegments) { input->seek(segment.start()); resourceSegments.append(Range::fromStartAndLength(output->pos(), segment.length())); QInstaller::appendData(output, input, segment.length()); } const qint64 operationsStart = output->pos(); QInstaller::appendInt64(output, performedOperations.count()); foreach (Operation *operation, performedOperations) { QInstaller::appendString(output, operation->name()); QInstaller::appendString(output, operation->toXml().toString()); // for the ui not to get blocked qApp->processEvents(); } QInstaller::appendInt64(output, performedOperations.count()); const qint64 operationsEnd = output->pos(); // we don't save any component-indexes. const qint64 numComponents = 0; QInstaller::appendInt64(output, numComponents); // for the indexes // we don't save any components. const qint64 compIndexStart = output->pos(); QInstaller::appendInt64(output, numComponents); // and 2 times number of components, QInstaller::appendInt64(output, numComponents); // one before and one after the components const qint64 compIndexEnd = output->pos(); QInstaller::appendInt64Range(output, Range::fromStartAndEnd(compIndexStart, compIndexEnd) .moved(-dataBlockStart)); foreach (const Range segment, resourceSegments) QInstaller::appendInt64Range(output, segment.moved(-dataBlockStart)); QInstaller::appendInt64Range(output, Range::fromStartAndEnd(operationsStart, operationsEnd) .moved(-dataBlockStart)); QInstaller::appendInt64(output, layout.metaResourceSegments.count()); // data block size, from end of .exe to end of file QInstaller::appendInt64(output, output->pos() + 3 * sizeof(qint64) -dataBlockStart); QInstaller::appendInt64(output, BinaryContent::MagicUninstallerMarker); } void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOperations) { bool gainedAdminRights = false; QTemporaryFile tempAdminFile(targetDir() + QLatin1String("/testjsfdjlkdsjflkdsjfldsjlfds") + QString::number(qrand() % 1000)); if (!tempAdminFile.open() || !tempAdminFile.isWritable()) { m_core->gainAdminRights(); gainedAdminRights = true; } const QString targetAppDirPath = QFileInfo(maintenanceToolName()).path(); if (!QDir().exists(targetAppDirPath)) { // create the directory containing the maintenance tool (like a bundle structure on OS X...) Operation *op = createOwnedOperation(QLatin1String("Mkdir")); op->setArguments(QStringList() << targetAppDirPath); performOperationThreaded(op, Backup); performOperationThreaded(op); performedOperations.append(takeOwnedOperation(op)); } #ifdef Q_OS_OSX // if it is a bundle, we need some stuff in it... const QString sourceAppDirPath = QCoreApplication::applicationDirPath(); if (isInstaller() && QInstaller::isInBundle(sourceAppDirPath)) { Operation *op = createOwnedOperation(QLatin1String("Copy")); op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../PkgInfo")) << (targetAppDirPath + QLatin1String("/../PkgInfo"))); performOperationThreaded(op, Backup); performOperationThreaded(op); // copy Info.plist to target directory op = createOwnedOperation(QLatin1String("Copy")); op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Info.plist")) << (targetAppDirPath + QLatin1String("/../Info.plist"))); performOperationThreaded(op, Backup); performOperationThreaded(op); // patch the Info.plist after copying it QFile sourcePlist(sourceAppDirPath + QLatin1String("/../Info.plist")); QInstaller::openForRead(&sourcePlist); QFile targetPlist(targetAppDirPath + QLatin1String("/../Info.plist")); QInstaller::openForWrite(&targetPlist); QTextStream in(&sourcePlist); QTextStream out(&targetPlist); const QString before = QLatin1String("") + QFileInfo(QCoreApplication::applicationFilePath()) .fileName() + QLatin1String(""); const QString after = QLatin1String("") + QFileInfo(maintenanceToolName()).baseName() + QLatin1String(""); while (!in.atEnd()) out << in.readLine().replace(before, after) << endl; // copy qt_menu.nib if it exists op = createOwnedOperation(QLatin1String("Mkdir")); op->setArguments(QStringList() << (targetAppDirPath + QLatin1String("/../Resources/qt_menu.nib"))); if (!op->performOperation()) { qDebug() << "ERROR in Mkdir operation:" << op->errorString(); } op = createOwnedOperation(QLatin1String("CopyDirectory")); op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Resources/qt_menu.nib")) << (targetAppDirPath + QLatin1String("/../Resources/qt_menu.nib"))); performOperationThreaded(op); op = createOwnedOperation(QLatin1String("Mkdir")); op->setArguments(QStringList() << (QFileInfo(targetAppDirPath).path() + QLatin1String("/Resources"))); performOperationThreaded(op, Backup); performOperationThreaded(op); // copy application icons if it exists. const QString icon = QFileInfo(QCoreApplication::applicationFilePath()).fileName() + QLatin1String(".icns"); op = createOwnedOperation(QLatin1String("Copy")); op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Resources/") + icon) << (targetAppDirPath + QLatin1String("/../Resources/") + icon)); performOperationThreaded(op, Backup); performOperationThreaded(op); // finally, copy everything within Frameworks and plugins op = createOwnedOperation(QLatin1String("CopyDirectory")); op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Frameworks")) << (targetAppDirPath + QLatin1String("/../Frameworks"))); performOperationThreaded(op); op = createOwnedOperation(QLatin1String("CopyDirectory")); op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../plugins")) << (targetAppDirPath + QLatin1String("/../plugins"))); performOperationThreaded(op); } #endif try { // 1 - check if we have a installer base replacement // |--- if so, write out the new tool and remove the replacement // |--- remember to restart and that we need to replace the original binary // // 2 - if we do not have a replacement, try to open the binary data file as input // |--- try to read the binary layout // |--- on error (see 2.1) // |--- remember we might to append uncompressed resource data (see 3) // |--- set the installer or maintenance binary as input to take over binary data // |--- in case we did not have a replacement, write out an new maintenance tool binary // |--- remember that we need to replace the original binary // // 3 - open a new binary data file // |--- try to write out the binary data based on the loaded input file (see 2) // |--- on error (see 3.1) // |--- if we wrote a new maintenance tool, take this as output - if not, write out a new // one and set it as output file, remember we did this // |--- append the binary data based on the loaded input file (see 2), make sure we force // uncompressing the resource section if we read from a binary data file (see 4.1). // // 4 - force a deferred rename on the .dat file (see 4.1) // 5 - force a deferred rename on the maintenance file (see 5.1) // 2.1 - Error cases are: no data file (in fact we are the installer or an old installation), // could not find the data file magic cookie (unknown .dat file), failed to read binary // layout (mostly likely the resource section or we couldn't seek inside the file) // // 3.1 - most likely the commit operation will fail // 4.1 - if 3 failed, this makes sure the .dat file will get removed and on the next run all // binary data is read from the maintenance tool, otherwise it get replaced be the new one // 5.1 - this will only happen -if- we wrote out a new binary bool newBinaryWritten = false; bool replacementExists = false; const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced); if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) { qDebug() << "Got a replacement installer base binary:" << installerBaseBinary; QFile replacementBinary(installerBaseBinary); try { QInstaller::openForRead(&replacementBinary); writeMaintenanceToolBinary(&replacementBinary, replacementBinary.size(), true); qDebug() << "Wrote the binary with the new replacement."; newBinaryWritten = true; replacementExists = true; } catch (const Error &error) { qDebug() << error.message(); } if (!replacementBinary.remove()) { // Is there anything more sensible we can do with this error? I think not. It's not serious // enough for throwing / aborting the process. qDebug() << "Cannot remove installer base binary" << installerBaseBinary << "after updating the maintenance tool:" << replacementBinary.errorString(); } else { qDebug() << "Removed installer base binary" << installerBaseBinary << "after updating the maintenance tool."; } m_installerBaseBinaryUnreplaced.clear(); } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) { qWarning() << "The current maintenance tool could not be updated." << installerBaseBinary << "does not exist. Please fix the \"setInstallerBaseBinary()\" call in your script."; } QFile input; BinaryLayout layout; const QString dataFile = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolName() + QLatin1String(".dat"); try { if (isInstaller()) { if (QFile::exists(dataFile)) { qWarning() << "Found binary data file" << dataFile << "but " "deliberately not used. Running as installer requires to read the " "resources from the application binary."; } throw Error(); } input.setFileName(dataFile); QInstaller::openForRead(&input); layout = BinaryContent::binaryLayout(&input, BinaryContent::MagicCookieDat); } catch (const Error &/*error*/) { #ifdef Q_OS_OSX // On Mac, data is always in a separate file so that the binary can be signed QString binaryName = isInstaller() ? installerBinaryPath() : maintenanceToolName(); QDir dataPath(QFileInfo(binaryName).dir()); dataPath.cdUp(); dataPath.cd(QLatin1String("Resources")); input.setFileName(dataPath.filePath(QLatin1String("installer.dat"))); QInstaller::openForRead(&input); layout = BinaryContent::binaryLayout(&input, BinaryContent::MagicCookie); if (!newBinaryWritten) { newBinaryWritten = true; QFile tmp(binaryName); QInstaller::openForRead(&tmp); writeMaintenanceToolBinary(&tmp, tmp.size(), true); } #else input.setFileName(isInstaller() ? installerBinaryPath() : maintenanceToolName()); QInstaller::openForRead(&input); layout = BinaryContent::binaryLayout(&input, BinaryContent::MagicCookie); if (!newBinaryWritten) { newBinaryWritten = true; writeMaintenanceToolBinary(&input, layout.endOfBinaryContent - layout.binaryContentSize, true); } #endif } performedOperations = sortOperationsBasedOnComponentDependencies(performedOperations); m_core->setValue(QLatin1String("installedOperationAreSorted"), QLatin1String("true")); try { QTemporaryFile file; QInstaller::openForWrite(&file); writeMaintenanceToolBinaryData(&file, &input, performedOperations, layout); QInstaller::appendInt64(&file, BinaryContent::MagicCookieDat); QFile dummy(dataFile + QLatin1String(".new")); if (dummy.exists() && !dummy.remove()) { throw Error(tr("Cannot remove data file \"%1\": %2").arg(dummy.fileName(), dummy.errorString())); } if (!file.rename(dataFile + QLatin1String(".new"))) { throw Error(tr("Cannot write maintenance tool binary data to %1: %2") .arg(file.fileName(), file.errorString())); } file.setAutoRemove(false); file.setPermissions(file.permissions() | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther); } catch (const Error &/*error*/) { if (!newBinaryWritten) { newBinaryWritten = true; QFile tmp(isInstaller() ? installerBinaryPath() : maintenanceToolName()); QInstaller::openForRead(&tmp); BinaryLayout tmpLayout = BinaryContent::binaryLayout(&tmp, BinaryContent::MagicCookie); writeMaintenanceToolBinary(&tmp, tmpLayout.endOfBinaryContent - tmpLayout.binaryContentSize, false); } QFile file(maintenanceToolName() + QLatin1String(".new")); QInstaller::openForAppend(&file); file.seek(file.size()); writeMaintenanceToolBinaryData(&file, &input, performedOperations, layout); QInstaller::appendInt64(&file, BinaryContent::MagicCookie); } input.close(); if (m_core->isInstaller()) registerMaintenanceTool(); writeMaintenanceConfigFiles(); deferredRename(dataFile + QLatin1String(".new"), dataFile, false); if (newBinaryWritten) { const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed()) && m_needsHardRestart; deferredRename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName(), restart); qDebug() << "Maintenance tool restart:" << (restart ? "true." : "false."); } } catch (const Error &err) { setStatus(PackageManagerCore::Failure); if (gainedAdminRights) m_core->dropAdminRights(); m_needToWriteMaintenanceTool = false; throw err; } if (gainedAdminRights) m_core->dropAdminRights(); commitSessionOperations(); m_needToWriteMaintenanceTool = false; } QString PackageManagerCorePrivate::registerPath() { #ifdef Q_OS_WIN QString guid = m_data.value(scProductUUID).toString(); if (guid.isEmpty()) { guid = QUuid::createUuid().toString(); m_data.setValue(scProductUUID, guid); writeMaintenanceConfigFiles(); // save uuid persistently } QString path = QLatin1String("HKEY_CURRENT_USER"); if (m_data.value(scAllUsers, scFalse).toString() == scTrue) path = QLatin1String("HKEY_LOCAL_MACHINE"); return path + QLatin1String("\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\") + guid; #endif return QString(); } bool PackageManagerCorePrivate::runInstaller() { bool adminRightsGained = false; try { setStatus(PackageManagerCore::Running); emit installationStarted(); // resets also the ProgressCoordninator // to have some progress for writeMaintenanceTool ProgressCoordinator::instance()->addReservePercentagePoints(1); const QString target = QDir::cleanPath(targetDir().replace(QLatin1Char('\\'), QLatin1Char('/'))); if (target.isEmpty()) throw Error(tr("Variable 'TargetDir' not set.")); if (!QDir(target).exists()) { const QString &pathToTarget = target.mid(0, target.lastIndexOf(QLatin1Char('/'))); if (!QDir(pathToTarget).exists()) { Operation *pathToTargetOp = createOwnedOperation(QLatin1String("Mkdir")); pathToTargetOp->setArguments(QStringList() << pathToTarget); if (!performOperationThreaded(pathToTargetOp)) { adminRightsGained = m_core->gainAdminRights(); if (!performOperationThreaded(pathToTargetOp)) throw Error(pathToTargetOp->errorString()); } } } else if (QDir(target).exists()) { QTemporaryFile tempAdminFile(target + QLatin1String("/adminrights")); if (!tempAdminFile.open() || !tempAdminFile.isWritable()) adminRightsGained = m_core->gainAdminRights(); } // add the operation to create the target directory Operation *mkdirOp = createOwnedOperation(QLatin1String("Mkdir")); mkdirOp->setArguments(QStringList() << target); mkdirOp->setValue(QLatin1String("forceremoval"), true); mkdirOp->setValue(QLatin1String("uninstall-only"), true); performOperationThreaded(mkdirOp, Backup); if (!performOperationThreaded(mkdirOp)) { // if we cannot create the target dir, we try to activate the admin rights adminRightsGained = m_core->gainAdminRights(); if (!performOperationThreaded(mkdirOp)) throw Error(mkdirOp->errorString()); } const QString remove = m_core->value(scRemoveTargetDir); if (QVariant(remove).toBool()) addPerformed(takeOwnedOperation(mkdirOp)); // to show that there was some work ProgressCoordinator::instance()->addManualPercentagePoints(1); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Preparing the installation...")); m_core->calculateComponentsToInstall(); const QList componentsToInstall = m_core->orderedComponentsToInstall(); qDebug() << "Install size:" << componentsToInstall.size() << "components"; callBeginInstallation(componentsToInstall); stopProcessesForUpdates(componentsToInstall); if (m_dependsOnLocalInstallerBinary && !KDUpdater::pathIsOnLocalDevice(qApp->applicationFilePath())) { throw Error(tr("It is not possible to install from network location")); } if (!adminRightsGained) { foreach (Component *component, componentsToInstall) { if (component->value(scRequiresAdminRights, scFalse) == scFalse) continue; m_core->gainAdminRights(); m_core->dropAdminRights(); break; } } const double downloadPartProgressSize = double(1) / double(3); double componentsInstallPartProgressSize = double(2) / double(3); const int downloadedArchivesCount = m_core->downloadNeededArchives(downloadPartProgressSize); // if there was no download we have the whole progress for installing components if (!downloadedArchivesCount) componentsInstallPartProgressSize = double(1); // Force an update on the components xml as the install dir might have changed. m_localPackageHub->setFileName(componentsXmlPath()); // Clear the packages as we might install into an already existing installation folder. m_localPackageHub->clearPackageInfos(); // also update the application name, might be set from a script as well m_localPackageHub->setApplicationName(m_data.value(QLatin1String("ProductName"), m_data.settings().applicationName()).toString()); m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); const int progressOperationCount = countProgressOperations(componentsToInstall) // add one more operation as we support progress + (PackageManagerCore::createLocalRepositoryFromBinary() ? 1 : 0); double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount; foreach (Component *component, componentsToInstall) installComponent(component, progressOperationSize, adminRightsGained); if (m_core->isOfflineOnly() && PackageManagerCore::createLocalRepositoryFromBinary()) { emit m_core->titleMessageChanged(tr("Creating local repository")); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QString()); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Creating local repository")); Operation *createRepo = createOwnedOperation(QLatin1String("CreateLocalRepository")); if (createRepo) { QString binaryFile = QCoreApplication::applicationFilePath(); #ifdef Q_OS_OSX // The installer binary on OSX does not contain the binary content, it's put into // the resources folder as separate file. Adjust the actual binary path. No error // checking here since we will fail later while reading the binary content. QDir resourcePath(QFileInfo(binaryFile).dir()); resourcePath.cdUp(); resourcePath.cd(QLatin1String("Resources")); binaryFile = resourcePath.filePath(QLatin1String("installer.dat")); #endif createRepo->setValue(QLatin1String("uninstall-only"), true); createRepo->setArguments(QStringList() << binaryFile << target + QLatin1String("/repository")); connectOperationToInstaller(createRepo, progressOperationSize); bool success = performOperationThreaded(createRepo); if (!success) { adminRightsGained = m_core->gainAdminRights(); success = performOperationThreaded(createRepo); } if (success) { QSet repos; foreach (Repository repo, m_data.settings().defaultRepositories()) { repo.setEnabled(false); repos.insert(repo); } repos.insert(Repository(QUrl::fromUserInput(createRepo ->value(QLatin1String("local-repo")).toString()), true)); m_data.settings().setDefaultRepositories(repos); addPerformed(takeOwnedOperation(createRepo)); } else { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("installationError"), tr("Error"), createRepo->errorString()); createRepo->undoOperation(); } } } emit m_core->titleMessageChanged(tr("Creating Maintenance Tool")); writeMaintenanceTool(m_performedOperationsOld + m_performedOperationsCurrentSession); // fake a possible wrong value to show a full progress bar const int progress = ProgressCoordinator::instance()->progressInPercentage(); // usually this should be only the reserved one from the beginning if (progress < 100) ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation finished!")); if (adminRightsGained) m_core->dropAdminRights(); setStatus(PackageManagerCore::Success); emit installationFinished(); } catch (const Error &err) { if (m_core->status() != PackageManagerCore::Canceled) { setStatus(PackageManagerCore::Failure); MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("installationError"), tr("Error"), err.message()); qDebug() << "ROLLING BACK operations=" << m_performedOperationsCurrentSession.count(); } m_core->rollBackInstallation(); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation aborted!")); if (adminRightsGained) m_core->dropAdminRights(); emit installationFinished(); return false; } return true; } bool PackageManagerCorePrivate::runPackageUpdater() { bool adminRightsGained = false; if (m_completeUninstall) { return runUninstaller(); } try { setStatus(PackageManagerCore::Running); emit installationStarted(); //resets also the ProgressCoordninator //to have some progress for the cleanup/write component.xml step ProgressCoordinator::instance()->addReservePercentagePoints(1); // check if we need admin rights and ask before the action happens if (!QTemporaryFile(targetDir() + QStringLiteral("/XXXXXX")).open()) adminRightsGained = m_core->gainAdminRights(); const QList componentsToInstall = m_core->orderedComponentsToInstall(); qDebug() << "Install size:" << componentsToInstall.size() << "components"; callBeginInstallation(componentsToInstall); stopProcessesForUpdates(componentsToInstall); if (m_dependsOnLocalInstallerBinary && !KDUpdater::pathIsOnLocalDevice(qApp->applicationFilePath())) { throw Error(tr("It is not possible to run that operation from a network location")); } bool updateAdminRights = false; if (!adminRightsGained) { foreach (Component *component, componentsToInstall) { if (component->value(scRequiresAdminRights, scFalse) == scFalse) continue; updateAdminRights = true; break; } } OperationList undoOperations; OperationList nonRevertedOperations; QHash componentsByName; // order the operations in the right component dependency order // next loop will save the needed operations in reverse order for uninstallation OperationList performedOperationsOld = m_performedOperationsOld; if (m_core->value(QLatin1String("installedOperationAreSorted")) != QLatin1String("true")) performedOperationsOld = sortOperationsBasedOnComponentDependencies(m_performedOperationsOld); // build a list of undo operations based on the checked state of the component foreach (Operation *operation, performedOperationsOld) { const QString &name = operation->value(QLatin1String("component")).toString(); Component *component = componentsByName.value(name, 0); if (!component) component = m_core->componentByName(name); if (component) componentsByName.insert(name, component); if (isUpdater()) { // We found the component, the component is not scheduled for update, the dependency solver // did not add the component as install dependency and there is no replacement, keep it. if ((component && !component->updateRequested() && !componentsToInstall.contains(component) && !m_componentsToReplaceUpdaterMode.contains(name))) { nonRevertedOperations.append(operation); continue; } // There is a replacement, but the replacement is not scheduled for update, keep it as well. if (m_componentsToReplaceUpdaterMode.contains(name) && !m_componentsToReplaceUpdaterMode.value(name).first->updateRequested()) { nonRevertedOperations.append(operation); continue; } } else if (isPackageManager()) { // We found the component, the component is still checked and the dependency solver did not // add the component as install dependency, keep it. if (component && component->installAction() == ComponentModelHelper::KeepInstalled && !componentsToInstall.contains(component)) { nonRevertedOperations.append(operation); continue; } // There is a replacement, but the replacement is not scheduled for update, keep it as well. if (m_componentsToReplaceAllMode.contains(name) && !m_componentsToReplaceAllMode.value(name).first->isSelectedForInstallation()) { nonRevertedOperations.append(operation); continue; } } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid package manager mode!"); } // Filter out the create target dir undo operation, it's only needed for full uninstall. // Note: We filter for unnamed operations as well, since old installations had the remove target // dir operation without the "uninstall-only", which will result in a complete uninstallation // during an update for the maintenance tool. if (operation->value(QLatin1String("uninstall-only")).toBool() || operation->value(QLatin1String("component")).toString().isEmpty()) { nonRevertedOperations.append(operation); continue; } // uninstallation should be in reverse order so prepend it here undoOperations.prepend(operation); updateAdminRights |= operation->value(QLatin1String("admin")).toBool(); } // we did not request admin rights till we found out that a component/ undo needs admin rights if (updateAdminRights && !adminRightsGained) { m_core->gainAdminRights(); m_core->dropAdminRights(); } double undoOperationProgressSize = 0; const double downloadPartProgressSize = double(2) / double(5); double componentsInstallPartProgressSize = double(3) / double(5); if (undoOperations.count() > 0) { undoOperationProgressSize = double(1) / double(5); componentsInstallPartProgressSize = downloadPartProgressSize; undoOperationProgressSize /= countProgressOperations(undoOperations); } ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Preparing the installation...")); // following, we download the needed archives m_core->downloadNeededArchives(downloadPartProgressSize); if (undoOperations.count() > 0) { ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Removing deselected components...")); runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, true); } m_performedOperationsOld = nonRevertedOperations; // these are all operations left: those not reverted const double progressOperationCount = countProgressOperations(componentsToInstall); const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount; foreach (Component *component, componentsToInstall) installComponent(component, progressOperationSize, adminRightsGained); emit m_core->titleMessageChanged(tr("Creating Maintenance Tool")); commitSessionOperations(); //end session, move ops to "old" m_needToWriteMaintenanceTool = true; // fake a possible wrong value to show a full progress bar const int progress = ProgressCoordinator::instance()->progressInPercentage(); // usually this should be only the reserved one from the beginning if (progress < 100) ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate finished!")); if (adminRightsGained) m_core->dropAdminRights(); setStatus(PackageManagerCore::Success); emit installationFinished(); } catch (const Error &err) { if (m_core->status() != PackageManagerCore::Canceled) { setStatus(PackageManagerCore::Failure); MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("installationError"), tr("Error"), err.message()); qDebug() << "ROLLING BACK operations=" << m_performedOperationsCurrentSession.count(); } m_core->rollBackInstallation(); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate aborted!")); if (adminRightsGained) m_core->dropAdminRights(); emit installationFinished(); return false; } return true; } bool PackageManagerCorePrivate::runUninstaller() { emit uninstallationStarted(); bool adminRightsGained = false; try { setStatus(PackageManagerCore::Running); // check if we need to run elevated and ask before the action happens QTemporaryFile tempAdminFile(targetDir() + QLatin1String("/adminrights")); if (!tempAdminFile.open() || !tempAdminFile.isWritable()) adminRightsGained = m_core->gainAdminRights(); OperationList undoOperations = m_performedOperationsOld; std::reverse(undoOperations.begin(), undoOperations.end()); bool updateAdminRights = false; if (!adminRightsGained) { foreach (Operation *op, m_performedOperationsOld) { updateAdminRights |= op->value(QLatin1String("admin")).toBool(); if (updateAdminRights) break; // an operation needs elevation to be able to perform their undo } } // We did not yet request elevated permissions but they are required. if (updateAdminRights && !adminRightsGained) { m_core->gainAdminRights(); m_core->dropAdminRights(); } const int uninstallOperationCount = countProgressOperations(undoOperations); const double undoOperationProgressSize = double(1) / double(uninstallOperationCount); runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, false); // No operation delete here, as all old undo operations are deleted in the destructor. deleteMaintenanceTool(); // this will also delete the TargetDir on Windows // If not on Windows, we need to remove TargetDir manually. if (QVariant(m_core->value(scRemoveTargetDir)).toBool() && !targetDir().isEmpty()) { if (updateAdminRights && !adminRightsGained) adminRightsGained = m_core->gainAdminRights(); removeDirectoryThreaded(targetDir(), true); qDebug() << "Complete uninstallation was chosen."; } unregisterMaintenanceTool(); m_needToWriteMaintenanceTool = false; setStatus(PackageManagerCore::Success); } catch (const Error &err) { if (m_core->status() != PackageManagerCore::Canceled) { setStatus(PackageManagerCore::Failure); MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("installationError"), tr("Error"), err.message()); } } const bool success = (m_core->status() == PackageManagerCore::Success); if (adminRightsGained) m_core->dropAdminRights(); ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QString::fromLatin1("\n%1").arg( success ? tr("Uninstallation completed successfully.") : tr("Uninstallation aborted."))); emit uninstallationFinished(); return success; } void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize, bool adminRightsGained) { const OperationList operations = component->operations(); if (!component->operationsCreatedSuccessfully()) m_core->setCanceled(); const int opCount = operations.count(); // show only components which do something, MinimumProgress is only for progress calculation safeness if (opCount > 1 || (opCount == 1 && operations.at(0)->name() != QLatin1String("MinimumProgress"))) { ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstalling component %1") .arg(component->displayName())); } foreach (Operation *operation, operations) { if (statusCanceledOrFailed()) throw Error(tr("Installation canceled by user")); // maybe this operations wants us to be admin... bool becameAdmin = false; if (!adminRightsGained && operation->value(QLatin1String("admin")).toBool()) { becameAdmin = m_core->gainAdminRights(); qDebug() << operation->name() << "as admin:" << becameAdmin; } connectOperationToInstaller(operation, progressOperationSize); connectOperationCallMethodRequest(operation); // allow the operation to backup stuff before performing the operation performOperationThreaded(operation, PackageManagerCorePrivate::Backup); bool ignoreError = false; bool ok = performOperationThreaded(operation); while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) { qDebug() << QString::fromLatin1("Operation \"%1\" with arguments \"%2\" failed: %3") .arg(operation->name(), operation->arguments().join(QLatin1String("; ")), operation->errorString()); const QMessageBox::StandardButton button = MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), QLatin1String("installationErrorWithRetry"), tr("Installer Error"), tr("Error during installation process (%1):\n%2").arg(component->name(), operation->errorString()), QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Retry); if (button == QMessageBox::Retry) ok = performOperationThreaded(operation); else if (button == QMessageBox::Ignore) ignoreError = true; else if (button == QMessageBox::Cancel) m_core->interrupt(); } if (ok || operation->error() > Operation::InvalidArguments) { // Remember that the operation was performed, that allows us to undo it if a following operation // fails or if this operation failed but still needs an undo call to cleanup. addPerformed(operation); } if (becameAdmin) m_core->dropAdminRights(); if (!ok && !ignoreError) throw Error(operation->errorString()); if (component->value(scEssential, scFalse) == scTrue) m_needsHardRestart = true; } registerPathsForUninstallation(component->pathsForUninstallation(), component->name()); if (!component->stopProcessForUpdateRequests().isEmpty()) { Operation *stopProcessForUpdatesOp = KDUpdater::UpdateOperationFactory::instance() .create(QLatin1String("FakeStopProcessForUpdate"), m_core); const QStringList arguments(component->stopProcessForUpdateRequests().join(QLatin1String(","))); stopProcessForUpdatesOp->setArguments(arguments); addPerformed(stopProcessForUpdatesOp); stopProcessForUpdatesOp->setValue(QLatin1String("component"), component->name()); } // now mark the component as installed m_localPackageHub->addPackage(component->name(), component->value(scVersion), component->value(scDisplayName), component->value(scDescription), component->dependencies(), component->autoDependencies(), component->forcedInstallation(), component->isVirtual(), component->value(scUncompressedSize).toULongLong(), component->value(scInheritVersion), component->isCheckable()); m_localPackageHub->writeToDisk(); component->setInstalled(); component->markAsPerformedInstallation(); } // -- private void PackageManagerCorePrivate::deleteMaintenanceTool() { #ifdef Q_OS_WIN // Since Windows does not support that the maintenance tool deletes itself we have to go with a rather dirty // hack. What we do is to create a batchfile that will try to remove the maintenance tool once per second. Then // we start that batchfile detached, finished our job and close ourselves. Once that's done the batchfile // will succeed in deleting our uninstall.exe and, if the installation directory was created but us and if // it's empty after the uninstall, deletes the installation-directory. const QString batchfile = QDir::toNativeSeparators(QFileInfo(QDir::tempPath(), QLatin1String("uninstall.vbs")).absoluteFilePath()); QFile f(batchfile); if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) throw Error(tr("Cannot prepare uninstall")); QTextStream batch(&f); batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n"; batch << "file = WScript.Arguments.Item(0)\n"; batch << "folderpath = WScript.Arguments.Item(1)\n"; batch << "Set folder = fso.GetFolder(folderpath)\n"; batch << "on error resume next\n"; batch << "while fso.FileExists(file)\n"; batch << " fso.DeleteFile(file)\n"; batch << " WScript.Sleep(1000)\n"; batch << "wend\n"; // batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n"; batch << " Set folder = Nothing\n"; batch << " fso.DeleteFolder folderpath, true\n"; // batch << "end if\n"; batch << "fso.DeleteFile(WScript.ScriptFullName)\n"; f.close(); QStringList arguments; arguments << QLatin1String("//Nologo") << batchfile; // execute the batchfile arguments << QDir::toNativeSeparators(QFileInfo(installerBinaryPath()).absoluteFilePath()); if (!m_performedOperationsOld.isEmpty()) { const Operation *const op = m_performedOperationsOld.first(); if (op->name() == QLatin1String("Mkdir")) // the target directory name arguments << QDir::toNativeSeparators(QFileInfo(op->arguments().first()).absoluteFilePath()); } if (!QProcessWrapper::startDetached(QLatin1String("cscript"), arguments, QDir::rootPath())) throw Error(tr("Cannot start uninstall")); #else // every other platform has no problem if we just delete ourselves now QFile maintenanceTool(QFileInfo(installerBinaryPath()).absoluteFilePath()); maintenanceTool.remove(); # ifdef Q_OS_OSX if (QInstaller::isInBundle(installerBinaryPath())) { const QLatin1String cdUp("/../../.."); removeDirectoryThreaded(QFileInfo(installerBinaryPath() + cdUp).absoluteFilePath()); QFile::remove(QFileInfo(installerBinaryPath() + cdUp).absolutePath() + QLatin1String("/") + configurationFileName()); } else # endif #endif { // finally remove the components.xml, since it still exists now QFile::remove(QFileInfo(installerBinaryPath()).absolutePath() + QLatin1String("/") + configurationFileName()); } } void PackageManagerCorePrivate::registerMaintenanceTool() { #ifdef Q_OS_WIN QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat); settings.setValue(scDisplayName, m_data.value(QLatin1String("ProductName"))); settings.setValue(QLatin1String("DisplayVersion"), m_data.value(QLatin1String("ProductVersion"))); const QString maintenanceTool = QDir::toNativeSeparators(maintenanceToolName()); settings.setValue(QLatin1String("DisplayIcon"), maintenanceTool); settings.setValue(scPublisher, m_data.value(scPublisher)); settings.setValue(QLatin1String("UrlInfoAbout"), m_data.value(QLatin1String("Url"))); settings.setValue(QLatin1String("Comments"), m_data.value(scTitle)); settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString()); settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir())); settings.setValue(QLatin1String("UninstallString"), maintenanceTool); settings.setValue(QLatin1String("ModifyPath"), QString(maintenanceTool + QLatin1String(" --manage-packages"))); // required disk space of the installed components quint64 estimatedSizeKB = m_core->requiredDiskSpace() / 1024; // add required space for the maintenance tool estimatedSizeKB += QFileInfo(maintenanceTool).size() / 1024; if (m_core->createLocalRepositoryFromBinary()) { // add required space for a local repository quint64 result(0); foreach (QInstaller::Component *component, m_core->components(PackageManagerCore::ComponentType::All)) { result += m_core->size(component, scCompressedSize); } estimatedSizeKB += result / 1024; } // Windows can only handle 32bit REG_DWORD (max. recordable installation size is 4TiB) const quint64 limit = std::numeric_limits::max(); // maximum 32 bit value if (estimatedSizeKB <= limit) settings.setValue(QLatin1String("EstimatedSize"), static_cast(estimatedSizeKB)); const bool supportsModify = m_core->value(scSupportsModify, scTrue) == scTrue; if (supportsModify) settings.setValue(QLatin1String("NoModify"), 0); else settings.setValue(QLatin1String("NoModify"), 1); settings.setValue(QLatin1String("NoRepair"), 1); #endif } void PackageManagerCorePrivate::unregisterMaintenanceTool() { #ifdef Q_OS_WIN QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat); settings.remove(QString()); #endif } void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize, bool adminRightsGained, bool deleteOperation) { try { foreach (Operation *undoOperation, undoOperations) { if (statusCanceledOrFailed()) throw Error(tr("Installation canceled by user")); bool becameAdmin = false; if (!adminRightsGained && undoOperation->value(QLatin1String("admin")).toBool()) becameAdmin = m_core->gainAdminRights(); connectOperationToInstaller(undoOperation, progressSize); qDebug() << "undo operation=" << undoOperation->name(); bool ignoreError = false; bool ok = performOperationThreaded(undoOperation, PackageManagerCorePrivate::Undo); const QString componentName = undoOperation->value(QLatin1String("component")).toString(); if (!componentName.isEmpty()) { while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) { const QMessageBox::StandardButton button = MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), QLatin1String("installationErrorWithRetry"), tr("Installer Error"), tr("Error during uninstallation process:\n%1").arg(undoOperation->errorString()), QMessageBox::Retry | QMessageBox::Ignore, QMessageBox::Retry); if (button == QMessageBox::Retry) ok = performOperationThreaded(undoOperation, Undo); else if (button == QMessageBox::Ignore) ignoreError = true; } Component *component = m_core->componentByName(componentName); if (!component) component = componentsToReplace().value(componentName).second; if (component) { component->setUninstalled(); m_localPackageHub->removePackage(component->name()); } } if (becameAdmin) m_core->dropAdminRights(); if (deleteOperation) delete undoOperation; } } catch (const Error &error) { m_localPackageHub->writeToDisk(); throw Error(error.message()); } catch (...) { m_localPackageHub->writeToDisk(); throw Error(tr("Unknown error")); } m_localPackageHub->writeToDisk(); } PackagesList PackageManagerCorePrivate::remotePackages() { if (m_updates && m_updateFinder) return m_updateFinder->updates(); m_updates = false; delete m_updateFinder; m_updateFinder = new KDUpdater::UpdateFinder; m_updateFinder->setAutoDelete(false); m_updateFinder->setPackageSources(m_packageSources); m_updateFinder->setLocalPackageHub(m_localPackageHub); m_updateFinder->run(); if (m_updateFinder->updates().isEmpty()) { setStatus(PackageManagerCore::Failure, tr("Cannot retrieve remote tree %1.") .arg(m_updateFinder->errorString())); return PackagesList(); } m_updates = true; return m_updateFinder->updates(); } PackagesList PackageManagerCorePrivate::compressedPackages() { if (m_compressedUpdates && m_compressedFinder) return m_compressedFinder->updates(); m_compressedUpdates = false; delete m_compressedFinder; m_compressedFinder = new KDUpdater::UpdateFinder; m_compressedFinder->setAutoDelete(false); m_compressedFinder->addCompressedPackage(true); m_compressedFinder->setPackageSources(m_compressedPackageSources); m_compressedFinder->setLocalPackageHub(m_localPackageHub); m_compressedFinder->run(); if (m_compressedFinder->updates().isEmpty()) { setStatus(PackageManagerCore::Failure, tr("Cannot retrieve remote tree %1.") .arg(m_compressedFinder->errorString())); return PackagesList(); } m_compressedUpdates = true; return m_compressedFinder->updates(); } /*! Returns a hash containing the installed package name and it's associated package information. If the application is running in installer mode or the local components file could not be parsed, the hash is empty. */ LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages() { if (isInstaller()) return LocalPackagesHash(); LocalPackagesHash installedPackages; if (m_localPackageHub->error() != LocalPackageHub::NoError) { if (m_localPackageHub->fileName().isEmpty()) m_localPackageHub->setFileName(componentsXmlPath()); else m_localPackageHub->refresh(); if (m_localPackageHub->applicationName().isEmpty()) m_localPackageHub->setApplicationName(m_data.settings().applicationName()); if (m_localPackageHub->applicationVersion().isEmpty()) m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); } if (m_localPackageHub->error() != LocalPackageHub::NoError) { setStatus(PackageManagerCore::Failure, tr("Failure to read packages from %1.") .arg(componentsXmlPath())); } foreach (const LocalPackage &package, m_localPackageHub->packageInfos()) { if (statusCanceledOrFailed()) break; installedPackages.insert(package.name, package); } return installedPackages; } bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories() { if (m_repoFetched) return m_repoFetched; m_updates = false; m_repoFetched = false; m_updateSourcesAdded = false; try { m_metadataJob.start(); m_metadataJob.waitForFinished(); } catch (Error &error) { setStatus(PackageManagerCore::Failure, tr("Cannot retrieve meta information: %1") .arg(error.message())); return m_repoFetched; } if (m_metadataJob.error() != Job::NoError) { switch (m_metadataJob.error()) { case QInstaller::UserIgnoreError: break; // we can simply ignore this error, the user knows about it default: setStatus(PackageManagerCore::Failure, m_metadataJob.errorString()); return m_repoFetched; } } m_repoFetched = true; return m_repoFetched; } bool PackageManagerCorePrivate::fetchMetaInformationFromCompressedRepositories() { bool compressedRepoFetched = false; m_compressedUpdates = false; m_updateSourcesAdded = false; try { //Tell MetadataJob that only compressed packages needed to be fetched and not all. //We cannot do this in general fetch meta method as the compressed packages might be //installed after components tree is generated m_metadataJob.addCompressedPackages(true); m_metadataJob.start(); m_metadataJob.waitForFinished(); m_metadataJob.addCompressedPackages(false); } catch (Error &error) { setStatus(PackageManagerCore::Failure, tr("Cannot retrieve meta information: %1") .arg(error.message())); return compressedRepoFetched; } if (m_metadataJob.error() != Job::NoError) { switch (m_metadataJob.error()) { case QInstaller::UserIgnoreError: break; // we can simply ignore this error, the user knows about it default: //Do not change core status here, we can recover if there is invalid //compressed repository setStatus(m_core->status(), m_metadataJob.errorString()); return compressedRepoFetched; } } compressedRepoFetched = true; return compressedRepoFetched; } bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository) { if (!compressedRepository && m_updateSourcesAdded) return m_updateSourcesAdded; const QList metadata = m_metadataJob.metadata(); if (metadata.isEmpty()) { m_updateSourcesAdded = true; return m_updateSourcesAdded; } if (compressedRepository) { m_compressedPackageSources.clear(); } else { m_packageSources.clear(); m_updates = false; m_updateSourcesAdded = false; if (isInstaller()) m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0)); } foreach (const Metadata &data, metadata) { if (compressedRepository && !data.repository.isCompressed()) { continue; } if (statusCanceledOrFailed()) return false; if (data.directory.isEmpty()) continue; if (parseChecksum) { const QString updatesXmlPath = data.directory + QLatin1String("/Updates.xml"); QFile updatesFile(updatesXmlPath); try { QInstaller::openForRead(&updatesFile); } catch(const Error &e) { qDebug() << "Error opening Updates.xml:" << e.message(); setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information.")); return false; } int line = 0; int column = 0; QString error; QDomDocument doc; if (!doc.setContent(&updatesFile, &error, &line, &column)) { qDebug().nospace() << "Parse error in file" << updatesFile.fileName() << ": " << error << " at line " << line << " col " << column; setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information.")); return false; } const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum")); if (!checksum.isNull()) m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue); } if (compressedRepository) m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1)); else m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1)); ProductKeyCheck::instance()->addPackagesFromXml(data.directory + QLatin1String("/Updates.xml")); } if ((compressedRepository && m_compressedPackageSources.count() == 0 ) || (!compressedRepository && m_packageSources.count() == 0)) { setStatus(PackageManagerCore::Failure, tr("Cannot find any update source information.")); return false; } m_updateSourcesAdded = true; return m_updateSourcesAdded; } void PackageManagerCorePrivate::restoreCheckState() { if (m_coreCheckedHash.isEmpty()) return; foreach (Component *component, m_coreCheckedHash.keys()) component->setCheckState(m_coreCheckedHash.value(component)); m_coreCheckedHash.clear(); m_componentsToInstallCalculated = false; } void PackageManagerCorePrivate::storeCheckState() { m_coreCheckedHash.clear(); const QList components = m_core->components(PackageManagerCore::ComponentType::AllNoReplacements); foreach (Component *component, components) m_coreCheckedHash.insert(component, component->checkState()); } void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation) { QObject *const operationObject = dynamic_cast (operation); if (operationObject != 0) { const QMetaObject *const mo = operationObject->metaObject(); if (mo->indexOfSignal(QMetaObject::normalizedSignature("requestBlockingExecution(QString)")) > -1) { connect(operationObject, SIGNAL(requestBlockingExecution(QString)), this, SLOT(handleMethodInvocationRequest(QString)), Qt::BlockingQueuedConnection); } } } OperationList PackageManagerCorePrivate::sortOperationsBasedOnComponentDependencies(const OperationList &operationList) { OperationList sortedOperations; QHash componentOperationHash; // sort component unrelated operations to the beginning foreach (Operation *operation, operationList) { const QString componentName = operation->value(QLatin1String("component")).toString(); if (componentName.isEmpty()) sortedOperations.append(operation); else componentOperationHash[componentName].append(operation); } const QRegExp dash(QLatin1String("-.*")); Graph componentGraph; // create the complete component graph foreach (const Component* node, m_core->components(PackageManagerCore::ComponentType::All)) { componentGraph.addNode(node->name()); componentGraph.addEdges(node->name(), node->dependencies().replaceInStrings(dash, QString())); } const QStringList resolvedComponents = componentGraph.sort(); if (componentGraph.hasCycle()) { throw Error(tr("Dependency cycle between components \"%1\" and \"%2\" detected.") .arg(componentGraph.cycle().first, componentGraph.cycle().second)); } foreach (const QString &componentName, resolvedComponents) sortedOperations.append(componentOperationHash.value(componentName)); return sortedOperations; } void PackageManagerCorePrivate::handleMethodInvocationRequest(const QString &invokableMethodName) { QObject *obj = QObject::sender(); if (obj != 0) QMetaObject::invokeMethod(obj, qPrintable(invokableMethodName)); } void PackageManagerCorePrivate::processFilesForDelayedDeletion() { if (m_filesForDelayedDeletion.isEmpty()) return; const QStringList filesForDelayedDeletion = std::move(m_filesForDelayedDeletion); foreach (const QString &i, filesForDelayedDeletion) { QFile file(i); //TODO: this should happen asnyc and report errors, I guess if (file.exists() && !file.remove()) { qWarning("Cannot delete file %s: %s", qPrintable(i), qPrintable(file.errorString())); m_filesForDelayedDeletion << i; // try again next time } } } void PackageManagerCorePrivate::findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result) { QString executable; QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Executable | QDir::Files | QDir::System, QDirIterator::Subdirectories ); while (it.hasNext()) { executable = it.next(); foreach (QString exclude, excludeFiles) { if (QDir::toNativeSeparators(executable.toLower()) != QDir::toNativeSeparators(exclude.toLower())) { result->append(executable); } } } } QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringList &excludeFiles) { QStringList resultFiles; findExecutablesRecursive(QCoreApplication::applicationDirPath(), excludeFiles, &resultFiles); return checkRunningProcessesFromList(resultFiles); } } // namespace QInstaller src/libs/installer/packagemanagercore_p.h000066400000000000000000000227351325366651500211070ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PACKAGEMANAGERCORE_P_H #define PACKAGEMANAGERCORE_P_H #include "metadatajob.h" #include "packagemanagercore.h" #include "packagemanagercoredata.h" #include "packagemanagerproxyfactory.h" #include "packagesource.h" #include "qinstallerglobal.h" #include "sysinfo.h" #include "updatefinder.h" #include class Job; QT_FORWARD_DECLARE_CLASS(QFile) QT_FORWARD_DECLARE_CLASS(QFileDevice) QT_FORWARD_DECLARE_CLASS(QFileInfo) using namespace KDUpdater; namespace QInstaller { struct BinaryLayout; class Component; class ScriptEngine; class ComponentModel; class TempDirDeleter; class InstallerCalculator; class UninstallerCalculator; class RemoteFileEngineHandler; class PackageManagerCorePrivate : public QObject { Q_OBJECT friend class PackageManagerCore; Q_DISABLE_COPY(PackageManagerCorePrivate) public: enum OperationType { Backup, Perform, Undo }; explicit PackageManagerCorePrivate(PackageManagerCore *core); explicit PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, const QList &performedOperations); ~PackageManagerCorePrivate(); static bool isProcessRunning(const QString &name, const QList &processes); static bool performOperationThreaded(Operation *op, PackageManagerCorePrivate::OperationType type = PackageManagerCorePrivate::Perform); void initialize(const QHash ¶ms); bool isOfflineOnly() const; bool statusCanceledOrFailed() const; void setStatus(int status, const QString &error = QString()); QString targetDir() const; QString registerPath(); QString maintenanceToolName() const; QString installerBinaryPath() const; void writeMaintenanceConfigFiles(); void readMaintenanceConfigFiles(const QString &targetDir); void writeMaintenanceTool(OperationList performedOperations); QString componentsXmlPath() const; QString configurationFileName() const; bool buildComponentTree(QHash &components, bool loadScript); void cleanUpComponentEnvironment(); ScriptEngine *componentScriptEngine() const; ScriptEngine *controlScriptEngine() const; void clearAllComponentLists(); void clearUpdaterComponentLists(); QList &replacementDependencyComponents(); QHash > &componentsToReplace(); void clearInstallerCalculator(); InstallerCalculator *installerCalculator() const; void clearUninstallerCalculator(); UninstallerCalculator *uninstallerCalculator() const; bool runInstaller(); bool isInstaller() const; bool runUninstaller(); bool isUninstaller() const; void runUpdater(); bool isUpdater() const; bool runPackageUpdater(); bool isPackageManager() const; QString replaceVariables(const QString &str) const; QByteArray replaceVariables(const QByteArray &str) const; void callBeginInstallation(const QList &componentList); void stopProcessesForUpdates(const QList &components); int countProgressOperations(const QList &components); int countProgressOperations(const OperationList &operations); void connectOperationToInstaller(Operation *const operation, double progressOperationPartSize); void connectOperationCallMethodRequest(Operation *const operation); OperationList sortOperationsBasedOnComponentDependencies(const OperationList &operationList); Operation *createOwnedOperation(const QString &type); Operation *takeOwnedOperation(Operation *operation); Operation *createPathOperation(const QFileInfo &fileInfo, const QString &componentName); void registerPathsForUninstallation(const QList > &pathsForUninstallation, const QString &componentName); void addPerformed(Operation *op) { m_performedOperationsCurrentSession.append(op); } void commitSessionOperations() { m_performedOperationsOld += m_performedOperationsCurrentSession; m_performedOperationsCurrentSession.clear(); } void installComponent(Component *component, double progressOperationSize, bool adminRightsGained = false); signals: void installationStarted(); void installationFinished(); void uninstallationStarted(); void uninstallationFinished(); public: UpdateFinder *m_updateFinder; UpdateFinder *m_compressedFinder; QSet m_packageSources; QSet m_compressedPackageSources; std::shared_ptr m_localPackageHub; QStringList m_filesForDelayedDeletion; int m_status; QString m_error; bool m_needsHardRestart; bool m_testChecksum; bool m_launchedAsRoot; bool m_completeUninstall; bool m_needToWriteMaintenanceTool; PackageManagerCoreData m_data; QHash m_sharedFlags; QString m_installerBaseBinaryUnreplaced; QList m_rootComponents; QList m_rootDependencyReplacements; QList m_updaterComponents; QList m_updaterComponentsDeps; QList m_updaterDependencyReplacements; OperationList m_ownedOperations; OperationList m_performedOperationsOld; OperationList m_performedOperationsCurrentSession; bool m_dependsOnLocalInstallerBinary; private slots: void infoMessage(Job *, const QString &message) { emit m_core->metaJobInfoMessage(message); } void infoProgress(Job *, quint64 progress, quint64) { emit m_core->metaJobProgress(progress); } void totalProgress(quint64 total) { emit m_core->metaJobTotalProgress(total); } void handleMethodInvocationRequest(const QString &invokableMethodName); private: void deleteMaintenanceTool(); void registerMaintenanceTool(); void unregisterMaintenanceTool(); void writeMaintenanceToolBinary(QFile *const input, qint64 size, bool writeBinaryLayout); void writeMaintenanceToolBinaryData(QFileDevice *output, QFile *const input, const OperationList &performed, const BinaryLayout &layout); void runUndoOperations(const OperationList &undoOperations, double undoOperationProgressSize, bool adminRightsGained, bool deleteOperation); PackagesList remotePackages(); PackagesList compressedPackages(); LocalPackagesHash localInstalledPackages(); bool fetchMetaInformationFromRepositories(); bool fetchMetaInformationFromCompressedRepositories(); bool addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository = false); void processFilesForDelayedDeletion(); void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result); QStringList runningInstallerProcesses(const QStringList &exludeFiles); private: PackageManagerCore *m_core; MetadataJob m_metadataJob; bool m_updates; bool m_compressedUpdates; bool m_repoFetched; bool m_updateSourcesAdded; qint64 m_magicBinaryMarker; bool m_componentsToInstallCalculated; mutable ScriptEngine *m_componentScriptEngine; mutable ScriptEngine *m_controlScriptEngine; // < name (component to replace), < replacement component, component to replace > > QHash > m_componentsToReplaceAllMode; QHash > m_componentsToReplaceUpdaterMode; InstallerCalculator *m_installerCalculator; UninstallerCalculator *m_uninstallerCalculator; PackageManagerProxyFactory *m_proxyFactory; ComponentModel *m_defaultModel; ComponentModel *m_updaterModel; QObject *m_guiObject; QScopedPointer m_remoteFileEngineHandler; private: // remove once we deprecate isSelected, setSelected etc... void restoreCheckState(); void storeCheckState(); QHash m_coreCheckedHash; }; } // namespace QInstaller #endif // PACKAGEMANAGERCORE_P_H src/libs/installer/packagemanagercoredata.cpp000066400000000000000000000235721325366651500217550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "packagemanagercoredata.h" #include "errors.h" #include "fileutils.h" #include "qsettingswrapper.h" #include "utils.h" #include #include #include #ifdef Q_OS_WIN # include # include #endif namespace QInstaller { PackageManagerCoreData::PackageManagerCoreData(const QHash &variables) { m_variables = variables; setDynamicPredefinedVariables(); // Set some common variables that may used e.g. as placeholder in some of the settings variables or // in a script or... m_variables.insert(scTargetConfigurationFile, QLatin1String("components.xml")); m_variables.insert(QLatin1String("InstallerDirPath"), QCoreApplication::applicationDirPath()); m_variables.insert(QLatin1String("InstallerFilePath"), QCoreApplication::applicationFilePath()); #ifdef Q_OS_WIN m_variables.insert(QLatin1String("os"), QLatin1String("win")); #elif defined(Q_OS_OSX) m_variables.insert(QLatin1String("os"), QLatin1String("mac")); #elif defined(Q_OS_LINUX) m_variables.insert(QLatin1String("os"), QLatin1String("x11")); #else // TODO: add more platforms as needed... #endif m_settings = Settings::fromFileAndPrefix(QLatin1String(":/metadata/installer-config/config.xml"), QLatin1String(":/metadata/installer-config/"), Settings::RelaxedParseMode); // fill the variables defined in the settings m_variables.insert(QLatin1String("ProductName"), m_settings.applicationName()); m_variables.insert(QLatin1String("ProductVersion"), m_settings.version()); m_variables.insert(scTitle, m_settings.title()); m_variables.insert(scPublisher, m_settings.publisher()); m_variables.insert(QLatin1String("Url"), m_settings.url()); m_variables.insert(scStartMenuDir, m_settings.startMenuDir()); m_variables.insert(scTargetConfigurationFile, m_settings.configurationFileName()); m_variables.insert(QLatin1String("LogoPixmap"), m_settings.logo()); m_variables.insert(QLatin1String("WatermarkPixmap"), m_settings.watermark()); m_variables.insert(QLatin1String("BannerPixmap"), m_settings.banner()); const QString description = m_settings.runProgramDescription(); if (!description.isEmpty()) m_variables.insert(scRunProgramDescription, description); m_variables.insert(scTargetDir, replaceVariables(m_settings.targetDir())); m_variables.insert(scRemoveTargetDir, replaceVariables(m_settings.removeTargetDir())); } void PackageManagerCoreData::clear() { m_variables.clear(); m_settings = Settings(); } /*! Set some common variables that may be used e.g. as placeholder in some of the settings variables or in a script or... */ void PackageManagerCoreData::setDynamicPredefinedVariables() { m_variables.insert(QLatin1String("rootDir"), QDir::rootPath()); m_variables.insert(QLatin1String("homeDir"), QDir::homePath()); m_variables.insert(QLatin1String("RootDir"), QDir::rootPath()); m_variables.insert(QLatin1String("HomeDir"), QDir::homePath()); QString dir = QLatin1String("/opt"); #ifdef Q_OS_WIN TCHAR buffer[MAX_PATH + 1] = { 0 }; SHGetFolderPath(0, CSIDL_PROGRAM_FILES, 0, 0, buffer); dir = QString::fromWCharArray(buffer); #elif defined (Q_OS_OSX) dir = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).value(0); #endif m_variables.insert(QLatin1String("ApplicationsDir"), dir); QString dirX86 = dir; QString dirX64 = dir; #ifdef Q_OS_WIN QSettingsWrapper current(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion") , QSettingsWrapper::NativeFormat); BOOL onWow64Or64bit = TRUE; #ifndef Q_OS_WIN64 IsWow64Process(GetCurrentProcess(), &onWow64Or64bit); #endif QString programfilesX86; QString programfilesX64; if (onWow64Or64bit == TRUE) { programfilesX86 = current.value(QLatin1String("ProgramFilesDir (x86)"), QString()).toString(); programfilesX64 = current.value(QLatin1String("ProgramW6432Dir"), QString()).toString(); } else { programfilesX86 = current.value(QLatin1String("ProgramFilesDir"), QString()).toString(); programfilesX64 = programfilesX86; } dirX86 = replaceWindowsEnvironmentVariables(programfilesX86); dirX64 = replaceWindowsEnvironmentVariables(programfilesX64); #endif m_variables.insert(QLatin1String("ApplicationsDirX86"), dirX86); m_variables.insert(QLatin1String("ApplicationsDirX64"), dirX64); #ifdef Q_OS_WIN QSettingsWrapper user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\" "CurrentVersion\\Explorer\\User Shell Folders"), QSettingsWrapper::NativeFormat); QSettingsWrapper system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\" "CurrentVersion\\Explorer\\Shell Folders"), QSettingsWrapper::NativeFormat); const QString programs = user.value(QLatin1String("Programs"), QString()).toString(); const QString allPrograms = system.value(QLatin1String("Common Programs"), QString()) .toString(); QString desktop; if (m_variables.value(QLatin1String("AllUsers")) == scTrue) { desktop = system.value(QLatin1String("Desktop")).toString(); } else { desktop = user.value(QLatin1String("Desktop")).toString(); } m_variables.insert(QLatin1String("DesktopDir"), replaceWindowsEnvironmentVariables(desktop)); m_variables.insert(QLatin1String("UserStartMenuProgramsPath"), replaceWindowsEnvironmentVariables(programs)); m_variables.insert(QLatin1String("AllUsersStartMenuProgramsPath"), replaceWindowsEnvironmentVariables(allPrograms)); #endif } Settings &PackageManagerCoreData::settings() const { return m_settings; } QStringList PackageManagerCoreData::keys() const { return m_variables.keys(); } bool PackageManagerCoreData::contains(const QString &key) const { return m_variables.contains(key) || m_settings.containsValue(key); } bool PackageManagerCoreData::setValue(const QString &key, const QString &normalizedValue) { if (m_variables.contains(key) && m_variables.value(key) == normalizedValue) return false; m_variables.insert(key, normalizedValue); return true; } QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_default) const { if (key == scTargetDir) { QString dir = m_variables.value(key); if (dir.isEmpty()) dir = replaceVariables(m_settings.value(key, _default).toString()); #ifdef Q_OS_WIN return QInstaller::normalizePathName(dir); #else if (dir.startsWith(QLatin1String("~/"))) return QDir::home().absoluteFilePath(dir.mid(2)); return dir; #endif } #ifdef Q_OS_WIN if (!m_variables.contains(key)) { static const QRegExp regex(QLatin1String("\\\\|/")); const QString filename = key.section(regex, 0, -2); const QString regKey = key.section(regex, -1); const QSettingsWrapper registry(filename, QSettingsWrapper::NativeFormat); if (!filename.isEmpty() && !regKey.isEmpty() && registry.contains(regKey)) return registry.value(regKey).toString(); } #endif if (m_variables.contains(key)) return m_variables.value(key); return m_settings.value(key, _default); } QString PackageManagerCoreData::replaceVariables(const QString &str) const { static const QChar at = QLatin1Char('@'); QString res; int pos = 0; while (true) { const int pos1 = str.indexOf(at, pos); if (pos1 == -1) break; const int pos2 = str.indexOf(at, pos1 + 1); if (pos2 == -1) break; res += str.mid(pos, pos1 - pos); const QString name = str.mid(pos1 + 1, pos2 - pos1 - 1); res += value(name).toString(); pos = pos2 + 1; } res += str.mid(pos); return res; } QByteArray PackageManagerCoreData::replaceVariables(const QByteArray &ba) const { static const QChar at = QLatin1Char('@'); QByteArray res; int pos = 0; while (true) { const int pos1 = ba.indexOf(at, pos); if (pos1 == -1) break; const int pos2 = ba.indexOf(at, pos1 + 1); if (pos2 == -1) break; res += ba.mid(pos, pos1 - pos); const QString name = QString::fromLocal8Bit(ba.mid(pos1 + 1, pos2 - pos1 - 1)); res += value(name).toString().toLocal8Bit(); pos = pos2 + 1; } res += ba.mid(pos); return res; } } // namespace QInstaller src/libs/installer/packagemanagercoredata.h000066400000000000000000000042151325366651500214130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef PACKAGEMANAGERCOREDATA_H #define PACKAGEMANAGERCOREDATA_H #include "settings.h" namespace QInstaller { class PackageManagerCoreData { public: PackageManagerCoreData() {} explicit PackageManagerCoreData(const QHash &variables); void clear(); void setDynamicPredefinedVariables(); Settings &settings() const; QStringList keys() const; bool contains(const QString &key) const; bool setValue(const QString &key, const QString &normalizedValue); QVariant value(const QString &key, const QVariant &_default = QVariant()) const; QString replaceVariables(const QString &str) const; QByteArray replaceVariables(const QByteArray &ba) const; private: mutable Settings m_settings; QHash m_variables; }; } // namespace QInstaller #endif // PACKAGEMANAGERCOREDATA_H src/libs/installer/packagemanagergui.cpp000066400000000000000000003405111325366651500207520ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "packagemanagergui.h" #include "component.h" #include "componentmodel.h" #include "errors.h" #include "fileutils.h" #include "messageboxhandler.h" #include "packagemanagercore.h" #include "progresscoordinator.h" #include "performinstallationform.h" #include "settings.h" #include "utils.h" #include "scriptengine.h" #include "productkeycheck.h" #include "sysinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN # include # include # include #endif using namespace KDUpdater; using namespace QInstaller; class DynamicInstallerPage : public PackageManagerPage { Q_OBJECT Q_DISABLE_COPY(DynamicInstallerPage) Q_PROPERTY(bool final READ isFinal WRITE setFinal) Q_PROPERTY(bool commit READ isCommit WRITE setCommit) Q_PROPERTY(bool complete READ isComplete WRITE setComplete) public: explicit DynamicInstallerPage(QWidget *widget, PackageManagerCore *core = 0) : PackageManagerPage(core) , m_widget(widget) { setObjectName(QLatin1String("Dynamic") + widget->objectName()); setPixmap(QWizard::WatermarkPixmap, QPixmap()); setColoredSubTitle(QLatin1String(" ")); setColoredTitle(widget->windowTitle()); m_widget->setProperty("complete", true); m_widget->setProperty("final", false); m_widget->setProperty("commit", false); widget->installEventFilter(this); setLayout(new QVBoxLayout); layout()->addWidget(widget); layout()->setContentsMargins(0, 0, 0, 0); addPageAndProperties(packageManagerCore()->controlScriptEngine()); addPageAndProperties(packageManagerCore()->componentScriptEngine()); } QWidget *widget() const { return m_widget; } bool isComplete() const { return m_widget->property("complete").toBool(); } void setFinal(bool final) { if (isFinal() == final) return; m_widget->setProperty("final", final); } bool isFinal() const { return m_widget->property("final").toBool(); } void setCommit(bool commit) { if (isCommit() == commit) return; m_widget->setProperty("commit", commit); } bool isCommit() const { return m_widget->property("commit").toBool(); } void setComplete(bool complete) { if (isComplete() == complete) return; m_widget->setProperty("complete", complete); } protected: bool eventFilter(QObject *obj, QEvent *event) { if (obj == m_widget) { switch(event->type()) { case QEvent::WindowTitleChange: setColoredTitle(m_widget->windowTitle()); break; case QEvent::DynamicPropertyChange: emit completeChanged(); if (m_widget->property("final").toBool() != isFinalPage()) setFinalPage(m_widget->property("final").toBool()); if (m_widget->property("commit").toBool() != isCommitPage()) setCommitPage(m_widget->property("commit").toBool()); break; default: break; } } return PackageManagerPage::eventFilter(obj, event); } void addPageAndProperties(ScriptEngine *engine) { engine->addToGlobalObject(this); engine->addToGlobalObject(widget()); static const QStringList properties = QStringList() << QStringLiteral("final") << QStringLiteral("commit") << QStringLiteral("complete"); foreach (const QString &property, properties) { engine->evaluate(QString::fromLatin1( "Object.defineProperty(%1, \"%2\", {" "get : function() { return Dynamic%1.%2; }," "set: function(val) { Dynamic%1.%2 = val; }" "});" ).arg(m_widget->objectName(), property)); } } private: QWidget *const m_widget; }; Q_DECLARE_METATYPE(DynamicInstallerPage*) // -- PackageManagerGui::Private class PackageManagerGui::Private { public: Private() : m_currentId(-1) , m_modified(false) , m_autoSwitchPage(true) , m_showSettingsButton(false) , m_silent(false) { m_wizardButtonTypes.insert(QWizard::BackButton, QLatin1String("QWizard::BackButton")); m_wizardButtonTypes.insert(QWizard::NextButton, QLatin1String("QWizard::NextButton")); m_wizardButtonTypes.insert(QWizard::CommitButton, QLatin1String("QWizard::CommitButton")); m_wizardButtonTypes.insert(QWizard::FinishButton, QLatin1String("QWizard::FinishButton")); m_wizardButtonTypes.insert(QWizard::CancelButton, QLatin1String("QWizard::CancelButton")); m_wizardButtonTypes.insert(QWizard::HelpButton, QLatin1String("QWizard::HelpButton")); m_wizardButtonTypes.insert(QWizard::CustomButton1, QLatin1String("QWizard::CustomButton1")); m_wizardButtonTypes.insert(QWizard::CustomButton2, QLatin1String("QWizard::CustomButton2")); m_wizardButtonTypes.insert(QWizard::CustomButton3, QLatin1String("QWizard::CustomButton3")); m_wizardButtonTypes.insert(QWizard::Stretch, QLatin1String("QWizard::Stretch")); } QString buttonType(int wizardButton) { return m_wizardButtonTypes.value(static_cast(wizardButton), QLatin1String("unknown button")); } int m_currentId; bool m_modified; bool m_autoSwitchPage; bool m_showSettingsButton; bool m_silent; QHash m_defaultPages; QHash m_defaultButtonText; QJSValue m_controlScriptContext; QHash m_wizardButtonTypes; }; // -- PackageManagerGui /*! \class QInstaller::PackageManagerGui \inmodule QtInstallerFramework \brief The PackageManagerGui class provides the core functionality for non-interactive installations. */ /*! \fn void PackageManagerGui::interrupted() \sa {gui::interrupted}{gui.interrupted} */ /*! \fn void PackageManagerGui::languageChanged() \sa {gui::languageChanged}{gui.languageChanged} */ /*! \fn void PackageManagerGui::finishButtonClicked() \sa {gui::finishButtonClicked}{gui.finishButtonClicked} */ /*! \fn void PackageManagerGui::gotRestarted() \sa {gui::gotRestarted}{gui.gotRestarted} */ /*! \fn void PackageManagerGui::settingsButtonClicked() \sa {gui::settingsButtonClicked}{gui.settingsButtonClicked} */ /*! \fn void PackageManagerGui::setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name, const QString &callbackName) Sets a validator for the custom page specified by \a name and \a callbackName requested by \a component. */ /*! \fn void PackageManagerGui::packageManagerCore() const Returns the package manager core. */ /*! Constructs a package manager UI with package manager specified by \a core and \a parent as parent. */ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) : QWizard(parent) , d(new Private) , m_core(core) { if (m_core->isInstaller()) setWindowTitle(tr("%1 Setup").arg(m_core->value(scTitle))); else setWindowTitle(tr("Maintain %1").arg(m_core->value(scTitle))); setWindowFlags(windowFlags() &~ Qt::WindowContextHelpButtonHint); #ifndef Q_OS_OSX setWindowIcon(QIcon(m_core->settings().installerWindowIcon())); #else setPixmap(QWizard::BackgroundPixmap, m_core->settings().background()); #endif #ifdef Q_OS_LINUX setWizardStyle(QWizard::ModernStyle); setSizeGripEnabled(true); #endif if (!m_core->settings().wizardStyle().isEmpty()) setWizardStyle(getStyle(m_core->settings().wizardStyle())); // set custom stylesheet const QString styleSheetFile = m_core->settings().styleSheet(); if (!styleSheetFile.isEmpty()) { QFile sheet(styleSheetFile); if (sheet.exists()) { if (sheet.open(QIODevice::ReadOnly)) setStyleSheet(QString::fromLatin1(sheet.readAll())); else qWarning() << "The specified style sheet file can not be opened."; } else { qWarning() << "A style sheet file is specified, but it does not exist."; } } setOption(QWizard::NoBackButtonOnStartPage); setOption(QWizard::NoBackButtonOnLastPage); connect(this, &QDialog::rejected, m_core, &PackageManagerCore::setCanceled); connect(this, &PackageManagerGui::interrupted, m_core, &PackageManagerCore::interrupt); // both queued to show the finished page once everything is done connect(m_core, &PackageManagerCore::installationFinished, this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); connect(m_core, &PackageManagerCore::uninstallationFinished, this, &PackageManagerGui::showFinishedPage, Qt::QueuedConnection); connect(this, &QWizard::currentIdChanged, this, &PackageManagerGui::currentPageChanged); connect(this, &QWizard::currentIdChanged, m_core, &PackageManagerCore::currentPageChanged); connect(button(QWizard::FinishButton), &QAbstractButton::clicked, this, &PackageManagerGui::finishButtonClicked); connect(button(QWizard::FinishButton), &QAbstractButton::clicked, m_core, &PackageManagerCore::finishButtonClicked); // make sure the QUiLoader's retranslateUi is executed first, then the script connect(this, &PackageManagerGui::languageChanged, m_core, &PackageManagerCore::languageChanged, Qt::QueuedConnection); connect(this, &PackageManagerGui::languageChanged, this, &PackageManagerGui::onLanguageChanged, Qt::QueuedConnection); connect(m_core, &PackageManagerCore::wizardPageInsertionRequested, this, &PackageManagerGui::wizardPageInsertionRequested); connect(m_core, &PackageManagerCore::wizardPageRemovalRequested, this, &PackageManagerGui::wizardPageRemovalRequested); connect(m_core, &PackageManagerCore::wizardWidgetInsertionRequested, this, &PackageManagerGui::wizardWidgetInsertionRequested); connect(m_core, &PackageManagerCore::wizardWidgetRemovalRequested, this, &PackageManagerGui::wizardWidgetRemovalRequested); connect(m_core, &PackageManagerCore::wizardPageVisibilityChangeRequested, this, &PackageManagerGui::wizardPageVisibilityChangeRequested, Qt::QueuedConnection); connect(m_core, &PackageManagerCore::setValidatorForCustomPageRequested, this, &PackageManagerGui::setValidatorForCustomPageRequested); connect(m_core, &PackageManagerCore::setAutomatedPageSwitchEnabled, this, &PackageManagerGui::setAutomatedPageSwitchEnabled); connect(this, &QWizard::customButtonClicked, this, &PackageManagerGui::customButtonClicked); for (int i = QWizard::BackButton; i < QWizard::CustomButton1; ++i) d->m_defaultButtonText.insert(i, buttonText(QWizard::WizardButton(i))); m_core->setGuiObject(this); } /*! Destructs a package manager UI. */ PackageManagerGui::~PackageManagerGui() { m_core->setGuiObject(0); delete d; } /*! Returns the style of the package manager UI depending on \a name: \list \li \c Classic - Classic UI style for Windows 7 and earlier. \li \c Modern - Modern UI style for Windows 8. \li \c Mac - UI style for OS X. \li \c Aero - Aero Peek for Windows 7. \endlist */ QWizard::WizardStyle PackageManagerGui::getStyle(const QString &name) { if (name == QLatin1String("Classic")) return QWizard::ClassicStyle; if (name == QLatin1String("Modern")) return QWizard::ModernStyle; if (name == QLatin1String("Mac")) return QWizard::MacStyle; if (name == QLatin1String("Aero")) return QWizard::AeroStyle; return QWizard::ModernStyle; } /*! Hides the GUI when \a silent is \c true. */ void PackageManagerGui::setSilent(bool silent) { d->m_silent = silent; setVisible(!silent); } /*! Returns the current silent state. */ bool PackageManagerGui::isSilent() const { return d->m_silent; } /*! Updates the model of \a object (which must be a QComboBox or QAbstractItemView) such that it contains the given \a items. */ void PackageManagerGui::setTextItems(QObject *object, const QStringList &items) { if (QComboBox *comboBox = qobject_cast(object)) { comboBox->setModel(new QStringListModel(items)); return; } if (QAbstractItemView *view = qobject_cast(object)) { view->setModel(new QStringListModel(items)); return; } qDebug() << "Cannot set text items on object of type" << object->metaObject()->className() << "."; } /*! Enables automatic page switching when \a request is \c true. */ void PackageManagerGui::setAutomatedPageSwitchEnabled(bool request) { d->m_autoSwitchPage = request; } /*! Returns the default text for the button specified by \a wizardButton. \sa {gui::defaultButtonText}{gui.defaultButtonText} */ QString PackageManagerGui::defaultButtonText(int wizardButton) const { return d->m_defaultButtonText.value(wizardButton); } /* Check if we need to "transform" the finish button into a cancel button, caused by the misuse of cancel as the finish button on the FinishedPage. This is only a problem if we run as updater or package manager, as then there will be two button shown on the last page with the cancel button renamed to "Finish". */ static bool swapFinishButton(PackageManagerCore *core, int currentId, int button) { if (button != QWizard::FinishButton) return false; if (currentId != PackageManagerCore::InstallationFinished) return false; if (core->isInstaller() || core->isUninstaller()) return false; return true; } /*! Clicks the button specified by \a wb after the delay specified by \a delay. \sa {gui::clickButton}{gui.clickButton} */ void PackageManagerGui::clickButton(int wb, int delay) { // We need to to swap here, cause scripts expect to call this function with FinishButton on the // finish page. if (swapFinishButton(m_core, currentId(), wb)) wb = QWizard::CancelButton; if (QAbstractButton *b = button(static_cast(wb))) QTimer::singleShot(delay, b, &QAbstractButton::click); else qWarning() << "Button with type: " << d->buttonType(wb) << "not found!"; } /*! Returns \c true if the button specified by \a wb is enabled. Returns \c false if a button of the specified type is not found. \sa {gui::isButtonEnabled}{gui.isButtonEnabled} */ bool PackageManagerGui::isButtonEnabled(int wb) { // We need to to swap here, cause scripts expect to call this function with FinishButton on the // finish page. if (swapFinishButton(m_core, currentId(), wb)) wb = QWizard::CancelButton; if (QAbstractButton *b = button(static_cast(wb))) return b->isEnabled(); qWarning() << "Button with type: " << d->buttonType(wb) << "not found!"; return false; } /*! Sets a validator for the custom page specified by \a name and \a callbackName requested by \a component. */ void PackageManagerGui::setValidatorForCustomPageRequested(Component *component, const QString &name, const QString &callbackName) { component->setValidatorCallbackName(callbackName); const QString componentName = QLatin1String("Dynamic") + name; const QList ids = pageIds(); foreach (const int i, ids) { PackageManagerPage *const p = qobject_cast (page(i)); if (p && p->objectName() == componentName) { p->setValidatePageComponent(component); return; } } } /*! Loads the script specified by \a scriptPath to perform the installation non-interactively. Throws QInstaller::Error if the script is not readable or it cannot be parsed. */ void PackageManagerGui::loadControlScript(const QString &scriptPath) { d->m_controlScriptContext = m_core->controlScriptEngine()->loadInContext( QLatin1String("Controller"), scriptPath); qDebug() << "Loaded control script" << scriptPath; } /*! Calls the control script method specified by \a methodName. */ void PackageManagerGui::callControlScriptMethod(const QString &methodName) { if (d->m_controlScriptContext.isUndefined()) return; try { const QJSValue returnValue = m_core->controlScriptEngine()->callScriptMethod( d->m_controlScriptContext, methodName); if (returnValue.isUndefined()) { qDebug() << "Control script callback" << methodName << "does not exist."; return; } } catch (const QInstaller::Error &e) { qCritical() << qPrintable(e.message()); } } /*! Executes the control script on the page specified by \a pageId. */ void PackageManagerGui::executeControlScript(int pageId) { if (PackageManagerPage *const p = qobject_cast (page(pageId))) callControlScriptMethod(p->objectName() + QLatin1String("Callback")); } /*! Replaces the default button text with translated text when the application language changes. */ void PackageManagerGui::onLanguageChanged() { d->m_defaultButtonText.clear(); for (int i = QWizard::BackButton; i < QWizard::CustomButton1; ++i) d->m_defaultButtonText.insert(i, buttonText(QWizard::WizardButton(i))); } /*! \reimp */ bool PackageManagerGui::event(QEvent *event) { switch(event->type()) { case QEvent::LanguageChange: emit languageChanged(); break; default: break; } return QWizard::event(event); } /*! \reimp */ void PackageManagerGui::showEvent(QShowEvent *event) { if (!event->spontaneous()) { foreach (int id, pageIds()) { const QString subTitle = page(id)->subTitle(); if (subTitle.isEmpty()) { const QWizard::WizardStyle style = wizardStyle(); if ((style == QWizard::ClassicStyle) || (style == QWizard::ModernStyle)) { // otherwise the colors might screw up page(id)->setSubTitle(QLatin1String(" ")); } } } setMinimumSize(size()); if (minimumWidth() < m_core->settings().wizardDefaultWidth()) resize(m_core->settings().wizardDefaultWidth(), height()); if (minimumHeight() < m_core->settings().wizardDefaultHeight()) resize(width(), m_core->settings().wizardDefaultHeight()); } QWizard::showEvent(event); QMetaObject::invokeMethod(this, "dependsOnLocalInstallerBinary", Qt::QueuedConnection); } /*! Requests the insertion of the page specified by \a widget at the position specified by \a page. If that position is already occupied by another page, the value is decremented until an empty slot is found. */ void PackageManagerGui::wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page) { // just in case it was already in there... wizardPageRemovalRequested(widget); int pageId = static_cast(page) - 1; while (QWizard::page(pageId) != 0) --pageId; // add it setPage(pageId, new DynamicInstallerPage(widget, m_core)); } /*! Requests the removal of the page specified by \a widget. */ void PackageManagerGui::wizardPageRemovalRequested(QWidget *widget) { foreach (int pageId, pageIds()) { DynamicInstallerPage *const dynamicPage = qobject_cast(page(pageId)); if (dynamicPage == 0) continue; if (dynamicPage->widget() != widget) continue; removePage(pageId); d->m_defaultPages.remove(pageId); packageManagerCore()->controlScriptEngine()->removeFromGlobalObject(dynamicPage); packageManagerCore()->componentScriptEngine()->removeFromGlobalObject(dynamicPage); } } /*! Requests the insertion of \a widget on \a page. */ void PackageManagerGui::wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page) { Q_ASSERT(widget); if (QWizardPage *const p = QWizard::page(page)) { p->layout()->addWidget(widget); packageManagerCore()->controlScriptEngine()->addToGlobalObject(p); packageManagerCore()->componentScriptEngine()->addToGlobalObject(p); } } /*! Requests the removal of \a widget from installer pages. */ void PackageManagerGui::wizardWidgetRemovalRequested(QWidget *widget) { Q_ASSERT(widget); widget->setParent(0); packageManagerCore()->controlScriptEngine()->removeFromGlobalObject(widget); packageManagerCore()->componentScriptEngine()->removeFromGlobalObject(widget); } /*! Requests changing the visibility of the page specified by \a p to \a visible. */ void PackageManagerGui::wizardPageVisibilityChangeRequested(bool visible, int p) { if (visible && page(p) == 0) { setPage(p, d->m_defaultPages[p]); } else if (!visible && page(p) != 0) { d->m_defaultPages[p] = page(p); removePage(p); } } /*! Returns the page specified by \a id. \sa {gui::pageById}{gui.pageById} */ QWidget *PackageManagerGui::pageById(int id) const { return page(id); } /*! Returns the page specified by the object name \a name from a UI file. \sa {gui::pageByObjectName}{gui.pageByObjectName} */ QWidget *PackageManagerGui::pageByObjectName(const QString &name) const { const QList ids = pageIds(); foreach (const int i, ids) { PackageManagerPage *const p = qobject_cast (page(i)); if (p && p->objectName() == name) return p; } qWarning() << "No page found for object name" << name; return 0; } /*! \sa {gui::currentPageWidget}{gui.currentPageWidget} */ QWidget *PackageManagerGui::currentPageWidget() const { return currentPage(); } /*! For dynamic pages, returns the widget specified by \a name read from the UI file. \sa {gui::pageWidgetByObjectName}{gui.pageWidgetByObjectName} */ QWidget *PackageManagerGui::pageWidgetByObjectName(const QString &name) const { QWidget *const widget = pageByObjectName(name); if (PackageManagerPage *const p = qobject_cast (widget)) { // For dynamic pages, return the contained widget (as read from the UI file), not the // wrapper page if (DynamicInstallerPage *dp = qobject_cast(p)) return dp->widget(); return p; } qWarning() << "No page found for object name" << name; return 0; } /*! \sa {gui::cancelButtonClicked}{gui.cancelButtonClicked} */ void PackageManagerGui::cancelButtonClicked() { const int id = currentId(); if (id == PackageManagerCore::Introduction || id == PackageManagerCore::InstallationFinished) { m_core->setNeedsHardRestart(false); QDialog::reject(); return; } QString question; bool interrupt = false; PackageManagerPage *const page = qobject_cast (currentPage()); if (page && page->isInterruptible() && m_core->status() != PackageManagerCore::Canceled && m_core->status() != PackageManagerCore::Failure) { interrupt = true; question = tr("Do you want to cancel the installation process?"); if (m_core->isUninstaller()) question = tr("Do you want to cancel the uninstallation process?"); } else { question = tr("Do you want to quit the installer application?"); if (m_core->isUninstaller()) question = tr("Do you want to quit the uninstaller application?"); if (m_core->isMaintainer()) question = tr("Do you want to quit the maintenance application?"); } const QMessageBox::StandardButton button = MessageBoxHandler::question(MessageBoxHandler::currentBestSuitParent(), QLatin1String("cancelInstallation"), tr("%1 Question").arg(m_core->value(scTitle)), question, QMessageBox::Yes | QMessageBox::No); if (button == QMessageBox::Yes) { if (interrupt) emit interrupted(); else QDialog::reject(); } } /*! \sa {gui::rejectWithoutPrompt}{gui.rejectWithoutPrompt} */ void PackageManagerGui::rejectWithoutPrompt() { QDialog::reject(); } /*! \reimp */ void PackageManagerGui::reject() { cancelButtonClicked(); } /*! \internal */ void PackageManagerGui::setModified(bool value) { d->m_modified = value; } /*! \sa {gui::showFinishedPage}{gui.showFinishedPage} */ void PackageManagerGui::showFinishedPage() { if (d->m_autoSwitchPage) next(); else qobject_cast(button(QWizard::CancelButton))->setEnabled(false); } /*! Shows the \uicontrol Settings button if \a show is \c true. \sa {gui::showSettingsButton}{gui.showSettingsButton} */ void PackageManagerGui::showSettingsButton(bool show) { if (d->m_showSettingsButton == show) return; d->m_showSettingsButton = show; setOption(QWizard::HaveCustomButton1, show); setButtonText(QWizard::CustomButton1, tr("Settings")); updateButtonLayout(); } /*! Forces an update of our own button layout. Needs to be called whenever a button option has been set. */ void PackageManagerGui::updateButtonLayout() { QVector buttons(12, QWizard::NoButton); if (options() & QWizard::HaveHelpButton) buttons[(options() & QWizard::HelpButtonOnRight) ? 11 : 0] = QWizard::HelpButton; buttons[1] = QWizard::Stretch; if (options() & QWizard::HaveCustomButton1) { buttons[1] = QWizard::CustomButton1; buttons[2] = QWizard::Stretch; } if (options() & QWizard::HaveCustomButton2) buttons[3] = QWizard::CustomButton2; if (options() & QWizard::HaveCustomButton3) buttons[4] = QWizard::CustomButton3; if (!(options() & QWizard::NoCancelButton)) buttons[(options() & QWizard::CancelButtonOnLeft) ? 5 : 10] = QWizard::CancelButton; buttons[6] = QWizard::BackButton; buttons[7] = QWizard::NextButton; buttons[8] = QWizard::CommitButton; buttons[9] = QWizard::FinishButton; setOption(QWizard::NoBackButtonOnLastPage, true); setOption(QWizard::NoBackButtonOnStartPage, true); setButtonLayout(buttons.toList()); } /*! Enables the \uicontrol Settings button by setting \a enabled to \c true. \sa {gui::setSettingsButtonEnabled}{gui.setSettingsButtonEnabled} */ void PackageManagerGui::setSettingsButtonEnabled(bool enabled) { if (QAbstractButton *btn = button(QWizard::CustomButton1)) btn->setEnabled(enabled); } /*! Emits the settingsButtonClicked() signal when the custom button specified by \a which is clicked if \a which is the \uicontrol Settings button. */ void PackageManagerGui::customButtonClicked(int which) { if (QWizard::WizardButton(which) == QWizard::CustomButton1 && d->m_showSettingsButton) emit settingsButtonClicked(); } /*! Prevents installation from a network location by determining that a local installer binary must be used. */ void PackageManagerGui::dependsOnLocalInstallerBinary() { if (m_core->settings().dependsOnLocalInstallerBinary() && !m_core->localInstallerBinaryUsed()) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Installer_Needs_To_Be_Local_Error"), tr("Error"), tr("It is not possible to install from network location.\n" "Please copy the installer to a local drive"), QMessageBox::Ok); rejectWithoutPrompt(); } } /*! Called when the current page changes to \a newId. Calls the leaving() method for the old page and the entering() method for the new one. Also, executes the control script associated with the new page by calling executeControlScript(). Emits the left() and entered() signals. */ void PackageManagerGui::currentPageChanged(int newId) { PackageManagerPage *oldPage = qobject_cast(page(d->m_currentId)); if (oldPage) { oldPage->leaving(); emit oldPage->left(); } d->m_currentId = newId; PackageManagerPage *newPage = qobject_cast(page(d->m_currentId)); if (newPage) { newPage->entering(); emit newPage->entered(); } executeControlScript(newId); } // -- PackageManagerPage /*! \class QInstaller::PackageManagerPage \inmodule QtInstallerFramework \brief The PackageManagerPage class displays information about the product to install. */ /*! \fn PackageManagerPage::~PackageManagerPage() Destructs a package manager page. */ /*! \fn PackageManagerPage::gui() const Returns the wizard this page belongs to. */ /*! \fn PackageManagerPage::isInterruptible() const Returns \c true if the installation can be interrupted. */ /*! \fn PackageManagerPage::setValidatePageComponent(QInstaller::Component *component) Sets \a component as the component that validates the page. */ /*! \fn PackageManagerPage::settingsButtonRequested() const Returns \c true if the page requests the wizard to show the \uicontrol Settings button. */ /*! \fn PackageManagerPage::setSettingsButtonRequested(bool request) Determines that the page should request the \uicontrol Settings button if \a request is \c true. */ /*! \fn PackageManagerPage::entered() This signal is called when a page is entered. */ /*! \fn PackageManagerPage::left() This signal is called when a page is left. */ /*! \fn PackageManagerPage::entering() Called when end users enter the page and the PackageManagerGui:currentPageChanged() signal is triggered. Supports the QWizardPage::​initializePage() function to ensure that the page's fields are properly initialized based on fields from previous pages. Otherwise, \c initializePage() would only be called once if the installer has been set to QWizard::IndependentPages. */ /*! \fn PackageManagerPage::leaving() Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ /*! Constructs a package manager page with \a core as parent. */ PackageManagerPage::PackageManagerPage(PackageManagerCore *core) : m_complete(true) , m_needsSettingsButton(false) , m_core(core) , validatorComponent(0) { if (!m_core->settings().titleColor().isEmpty()) { m_titleColor = m_core->settings().titleColor(); } else { QColor defaultColor = style()->standardPalette().text().color(); m_titleColor = defaultColor.name(); } setPixmap(QWizard::WatermarkPixmap, watermarkPixmap()); setPixmap(QWizard::BannerPixmap, bannerPixmap()); setPixmap(QWizard::LogoPixmap, logoPixmap()); } /*! Returns the package manager core. */ PackageManagerCore *PackageManagerPage::packageManagerCore() const { return m_core; } /*! Returns the watermark pixmap specified in the \c element of the package information file. */ QPixmap PackageManagerPage::watermarkPixmap() const { return QPixmap(m_core->value(QLatin1String("WatermarkPixmap"))); } /*! Returns the banner pixmap specified in the \c element of the package information file. Only used by the modern UI style. */ QPixmap PackageManagerPage::bannerPixmap() const { QPixmap banner(m_core->value(QLatin1String("BannerPixmap"))); if (!banner.isNull()) { int width; if (m_core->settings().containsValue(QLatin1String("WizardDefaultWidth")) ) width = m_core->settings().wizardDefaultWidth(); else width = size().width(); banner = banner.scaledToWidth(width, Qt::SmoothTransformation); } return banner; } /*! Returns the logo pixmap specified in the \c element of the package information file. */ QPixmap PackageManagerPage::logoPixmap() const { return QPixmap(m_core->value(QLatin1String("LogoPixmap"))); } /*! Returns the product name of the application being installed. */ QString PackageManagerPage::productName() const { return m_core->value(QLatin1String("ProductName")); } /*! Sets the font color of \a title. The title is specified in the \c element of the package information file. It is the name of the installer as displayed on the title bar. */ void PackageManagerPage::setColoredTitle(const QString &title) { setTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, title)); } /*! Sets the font color of \a subTitle. */ void PackageManagerPage::setColoredSubTitle(const QString &subTitle) { setSubTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, subTitle)); } /*! Returns \c true if the page is complete; otherwise, returns \c false. */ bool PackageManagerPage::isComplete() const { return m_complete; } /*! Sets the package manager page to complete if \a complete is \c true. Emits the completeChanged() signal. */ void PackageManagerPage::setComplete(bool complete) { m_complete = complete; if (QWizard *w = wizard()) { if (QAbstractButton *cancel = w->button(QWizard::CancelButton)) { if (cancel->hasFocus()) { if (QAbstractButton *next = w->button(QWizard::NextButton)) next->setFocus(); } } } emit completeChanged(); } /*! Sets the \a component that validates the page. */ void PackageManagerPage::setValidatePageComponent(Component *component) { validatorComponent = component; } /*! Returns \c true if the end user has entered complete and valid information. */ bool PackageManagerPage::validatePage() { if (validatorComponent) return validatorComponent->validatePage(); return true; } /*! Inserts \a widget at the position specified by \a offset in relation to another widget specified by \a siblingName. The default position is directly behind the sibling. */ void PackageManagerPage::insertWidget(QWidget *widget, const QString &siblingName, int offset) { QWidget *sibling = findChild<QWidget *>(siblingName); QWidget *parent = sibling ? sibling->parentWidget() : 0; QLayout *layout = parent ? parent->layout() : 0; QBoxLayout *blayout = qobject_cast<QBoxLayout *>(layout); if (blayout) { const int index = blayout->indexOf(sibling) + offset; blayout->insertWidget(index, widget); } } /*! Returns the widget specified by \a objectName. */ QWidget *PackageManagerPage::findWidget(const QString &objectName) const { return findChild<QWidget*> (objectName); } /*! Determines which page should be shown next depending on whether the application is being installed, updated, or uninstalled. The license check page is shown only if a component that provides a license is selected for installation. It is hidden during uninstallation and update. */ int PackageManagerPage::nextId() const { const int next = QWizardPage::nextId(); // the page to show next if (next == PackageManagerCore::LicenseCheck) { // calculate the page after the license page const int nextNextId = gui()->pageIds().value(gui()->pageIds().indexOf(next) + 1, -1); const PackageManagerCore *const core = packageManagerCore(); if (core->isUninstaller()) return nextNextId; // forcibly hide the license page if we run as uninstaller core->calculateComponentsToInstall(); foreach (Component* component, core->orderedComponentsToInstall()) { if (core->isMaintainer() && component->isInstalled()) continue; // package manager or updater, hide as long as the component is installed // The component is about to be installed and provides a license, so the page needs to // be shown. if (!component->licenses().isEmpty()) return next; } return nextNextId; // no component with a license or all components with license installed } return next; // default, show the next page } // -- IntroductionPage /*! \class QInstaller::IntroductionPage \inmodule QtInstallerFramework \brief The IntroductionPage class displays information about the product to install. */ /*! \fn IntroductionPage::packageManagerCoreTypeChanged() This signal is emitted when the package manager core type changes. */ /*! Constructs an introduction page with \a core as parent. */ IntroductionPage::IntroductionPage(PackageManagerCore *core) : PackageManagerPage(core) , m_updatesFetched(false) , m_allPackagesFetched(false) , m_label(0) , m_msgLabel(0) , m_errorLabel(0) , m_progressBar(0) , m_packageManager(0) , m_updateComponents(0) , m_removeAllComponents(0) { setObjectName(QLatin1String("IntroductionPage")); setColoredTitle(tr("Setup - %1").arg(productName())); QVBoxLayout *layout = new QVBoxLayout(this); setLayout(layout); m_msgLabel = new QLabel(this); m_msgLabel->setWordWrap(true); m_msgLabel->setObjectName(QLatin1String("MessageLabel")); m_msgLabel->setText(tr("Welcome to the %1 Setup Wizard.").arg(productName())); QWidget *widget = new QWidget(this); QVBoxLayout *boxLayout = new QVBoxLayout(widget); m_packageManager = new QRadioButton(tr("Add or remove components"), this); m_packageManager->setObjectName(QLatin1String("PackageManagerRadioButton")); boxLayout->addWidget(m_packageManager); m_packageManager->setChecked(core->isPackageManager()); connect(m_packageManager, &QAbstractButton::toggled, this, &IntroductionPage::setPackageManager); m_updateComponents = new QRadioButton(tr("Update components"), this); m_updateComponents->setObjectName(QLatin1String("UpdaterRadioButton")); boxLayout->addWidget(m_updateComponents); m_updateComponents->setChecked(core->isUpdater()); connect(m_updateComponents, &QAbstractButton::toggled, this, &IntroductionPage::setUpdater); m_removeAllComponents = new QRadioButton(tr("Remove all components"), this); m_removeAllComponents->setObjectName(QLatin1String("UninstallerRadioButton")); boxLayout->addWidget(m_removeAllComponents); m_removeAllComponents->setChecked(core->isUninstaller()); connect(m_removeAllComponents, &QAbstractButton::toggled, this, &IntroductionPage::setUninstaller); connect(m_removeAllComponents, &QAbstractButton::toggled, core, &PackageManagerCore::setCompleteUninstallation); boxLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); m_label = new QLabel(this); m_label->setWordWrap(true); m_label->setObjectName(QLatin1String("InformationLabel")); m_label->setText(tr("Retrieving information from remote installation sources...")); boxLayout->addWidget(m_label); m_progressBar = new QProgressBar(this); m_progressBar->setRange(0, 0); boxLayout->addWidget(m_progressBar); m_progressBar->setObjectName(QLatin1String("InformationProgressBar")); boxLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); m_errorLabel = new QLabel(this); m_errorLabel->setWordWrap(true); boxLayout->addWidget(m_errorLabel); m_errorLabel->setObjectName(QLatin1String("ErrorLabel")); layout->addWidget(m_msgLabel); layout->addWidget(widget); layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); core->setCompleteUninstallation(core->isUninstaller()); connect(core, &PackageManagerCore::metaJobProgress, this, &IntroductionPage::onProgressChanged); connect(core, &PackageManagerCore::metaJobTotalProgress, this, &IntroductionPage::setTotalProgress); connect(core, &PackageManagerCore::metaJobInfoMessage, this, &IntroductionPage::setMessage); connect(core, &PackageManagerCore::coreNetworkSettingsChanged, this, &IntroductionPage::onCoreNetworkSettingsChanged); m_updateComponents->setEnabled(ProductKeyCheck::instance()->hasValidKey()); #ifdef Q_OS_WIN if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { m_taskButton = new QWinTaskbarButton(this); connect(core, &PackageManagerCore::metaJobProgress, m_taskButton->progress(), &QWinTaskbarProgress::setValue); } else { m_taskButton = 0; } #endif } /*! Determines which page should be shown next depending on whether the application is being installed, updated, or uninstalled. */ int IntroductionPage::nextId() const { if (packageManagerCore()->isUninstaller()) return PackageManagerCore::ReadyForInstallation; if (packageManagerCore()->isMaintainer()) return PackageManagerCore::ComponentSelection; return PackageManagerPage::nextId(); } /*! For an uninstaller, always returns \c true. For the package manager and updater, at least one valid repository is required. For the online installer, package manager, and updater, valid meta data has to be fetched successfully to return \c true. */ bool IntroductionPage::validatePage() { PackageManagerCore *core = packageManagerCore(); if (core->isUninstaller()) return true; setComplete(false); if (!validRepositoriesAvailable()) { setErrorMessage(QLatin1String("<font color=\"red\">") + tr("At least one valid and enabled " "repository required for this action to succeed.") + QLatin1String("</font>")); return isComplete(); } gui()->setSettingsButtonEnabled(false); if (core->isMaintainer()) { showAll(); setMaintenanceToolsEnabled(false); } else { showMetaInfoUpdate(); } #ifdef Q_OS_WIN if (m_taskButton) { if (!m_taskButton->window()) { if (QWidget *widget = QApplication::activeWindow()) m_taskButton->setWindow(widget->windowHandle()); } m_taskButton->progress()->reset(); m_taskButton->progress()->resume(); m_taskButton->progress()->setVisible(true); } #endif // fetch updater packages if (core->isUpdater()) { if (!m_updatesFetched) { m_updatesFetched = core->fetchRemotePackagesTree(); if (!m_updatesFetched) setErrorMessage(core->error()); } if (m_updatesFetched) { if (core->components(QInstaller::PackageManagerCore::ComponentType::Root).count() <= 0) setErrorMessage(QString::fromLatin1("<b>%1</b>").arg(tr("No updates available."))); else setComplete(true); } } // fetch common packages if (core->isInstaller() || core->isPackageManager()) { bool localPackagesTreeFetched = false; if (!m_allPackagesFetched) { // first try to fetch the server side packages tree m_allPackagesFetched = core->fetchRemotePackagesTree(); if (!m_allPackagesFetched) { QString error = core->error(); if (core->isPackageManager() && core->status() != PackageManagerCore::ForceUpdate) { // if that fails and we're in maintenance mode, try to fetch local installed tree localPackagesTreeFetched = core->fetchLocalPackagesTree(); if (localPackagesTreeFetched) { // if that succeeded, adjust error message error = QLatin1String("<font color=\"red\">") + error + tr(" Only local package " "management available.") + QLatin1String("</font>"); } } setErrorMessage(error); } } if (m_allPackagesFetched || localPackagesTreeFetched) setComplete(true); } if (core->isMaintainer()) { showMaintenanceTools(); setMaintenanceToolsEnabled(true); } else { hideAll(); } gui()->setSettingsButtonEnabled(true); #ifdef Q_OS_WIN if (m_taskButton) m_taskButton->progress()->setVisible(!isComplete()); #endif return isComplete(); } /*! Shows all widgets on the page. */ void IntroductionPage::showAll() { showWidgets(true); } /*! Hides all widgets on the page. */ void IntroductionPage::hideAll() { showWidgets(false); } /*! Hides the widgets on the page except a text label and progress bar. */ void IntroductionPage::showMetaInfoUpdate() { showWidgets(false); m_label->setVisible(true); m_progressBar->setVisible(true); } /*! Shows the options to install, add, and unistall components on the page. */ void IntroductionPage::showMaintenanceTools() { showWidgets(true); m_label->setVisible(false); m_progressBar->setVisible(false); } /*! Sets \a enable to \c true to enable the options to install, add, and uninstall components on the page. */ void IntroductionPage::setMaintenanceToolsEnabled(bool enable) { m_packageManager->setEnabled(enable); m_updateComponents->setEnabled(enable && ProductKeyCheck::instance()->hasValidKey()); m_removeAllComponents->setEnabled(enable); } // -- public slots /*! Displays the message \a msg on the page. */ void IntroductionPage::setMessage(const QString &msg) { m_label->setText(msg); } /*! Updates the value of \a progress on the progress bar. */ void IntroductionPage::onProgressChanged(int progress) { m_progressBar->setValue(progress); } /*! Sets total \a progress value to progress bar. */ void IntroductionPage::setTotalProgress(int totalProgress) { if (m_progressBar) m_progressBar->setRange(0, totalProgress); } /*! Displays the error message \a error on the page. */ void IntroductionPage::setErrorMessage(const QString &error) { QPalette palette; const PackageManagerCore::Status s = packageManagerCore()->status(); if (s == PackageManagerCore::Failure || s == PackageManagerCore::Failure) { palette.setColor(QPalette::WindowText, Qt::red); } else { palette.setColor(QPalette::WindowText, palette.color(QPalette::WindowText)); } m_errorLabel->setText(error); m_errorLabel->setPalette(palette); #ifdef Q_OS_WIN if (m_taskButton) { m_taskButton->progress()->stop(); m_taskButton->progress()->setValue(100); } #endif } /*! Returns \c true if at least one valid and enabled repository is available. */ bool IntroductionPage::validRepositoriesAvailable() const { const PackageManagerCore *const core = packageManagerCore(); bool valid = (core->isInstaller() && core->isOfflineOnly()) || core->isUninstaller(); if (!valid) { foreach (const Repository &repo, core->settings().repositories()) { if (repo.isEnabled() && repo.isValid()) { valid = true; break; } } } return valid; } // -- private slots void IntroductionPage::setUpdater(bool value) { if (value) { entering(); gui()->showSettingsButton(true); packageManagerCore()->setUpdater(); emit packageManagerCoreTypeChanged(); } } void IntroductionPage::setUninstaller(bool value) { if (value) { entering(); gui()->showSettingsButton(false); packageManagerCore()->setUninstaller(); emit packageManagerCoreTypeChanged(); } } void IntroductionPage::setPackageManager(bool value) { if (value) { entering(); gui()->showSettingsButton(true); packageManagerCore()->setPackageManager(); emit packageManagerCoreTypeChanged(); } } /*! Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be fetched again. */ void IntroductionPage::onCoreNetworkSettingsChanged() { m_updatesFetched = false; m_allPackagesFetched = false; } // -- private /*! Initializes the page's fields. */ void IntroductionPage::entering() { setComplete(true); showWidgets(false); setMessage(QString()); setErrorMessage(QString()); setButtonText(QWizard::CancelButton, tr("Quit")); m_progressBar->setValue(0); m_progressBar->setRange(0, 0); PackageManagerCore *core = packageManagerCore(); if (core->isUninstaller() || core->isMaintainer()) { showMaintenanceTools(); setMaintenanceToolsEnabled(true); } setSettingsButtonRequested((!core->isOfflineOnly()) && (!core->isUninstaller())); } /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ void IntroductionPage::leaving() { m_progressBar->setValue(0); m_progressBar->setRange(0, 0); setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton)); } /*! Displays widgets on the page. */ void IntroductionPage::showWidgets(bool show) { m_label->setVisible(show); m_progressBar->setVisible(show); m_packageManager->setVisible(show); m_updateComponents->setVisible(show); m_removeAllComponents->setVisible(show); } /*! Displays the text \a text on the page. */ void IntroductionPage::setText(const QString &text) { m_msgLabel->setText(text); } // -- LicenseAgreementPage::ClickForwarder class LicenseAgreementPage::ClickForwarder : public QObject { Q_OBJECT public: explicit ClickForwarder(QAbstractButton *button) : QObject(button) , m_abstractButton(button) {} protected: bool eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::MouseButtonRelease) { m_abstractButton->click(); return true; } // standard event processing return QObject::eventFilter(object, event); } private: QAbstractButton *m_abstractButton; }; // -- LicenseAgreementPage /*! \class QInstaller::LicenseAgreementPage \inmodule QtInstallerFramework \brief The LicenseAgreementPage presents a license agreement to the end users for acceptance. The license check page is displayed if you specify a license file in the package information file and copy the file to the meta directory. End users must accept the terms of the license agreement for the installation to continue. */ /*! Constructs a license check page with \a core as parent. */ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core) : PackageManagerPage(core) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("LicenseAgreementPage")); setColoredTitle(tr("License Agreement")); m_licenseListWidget = new QListWidget(this); m_licenseListWidget->setObjectName(QLatin1String("LicenseListWidget")); m_licenseListWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); connect(m_licenseListWidget, &QListWidget::currentItemChanged, this, &LicenseAgreementPage::currentItemChanged); m_textBrowser = new QTextBrowser(this); m_textBrowser->setReadOnly(true); m_textBrowser->setOpenLinks(false); m_textBrowser->setOpenExternalLinks(true); m_textBrowser->setObjectName(QLatin1String("LicenseTextBrowser")); m_textBrowser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); connect(m_textBrowser, &QTextBrowser::anchorClicked, this, &LicenseAgreementPage::openLicenseUrl); QVBoxLayout *licenseBoxLayout = new QVBoxLayout(); licenseBoxLayout->addWidget(m_licenseListWidget); licenseBoxLayout->addWidget(m_textBrowser); QVBoxLayout *layout = new QVBoxLayout(this); layout->addLayout(licenseBoxLayout); m_acceptRadioButton = new QRadioButton(this); m_acceptRadioButton->setShortcut(QKeySequence(tr("Alt+A", "agree license"))); m_acceptRadioButton->setObjectName(QLatin1String("AcceptLicenseRadioButton")); ClickForwarder *acceptClickForwarder = new ClickForwarder(m_acceptRadioButton); m_acceptLabel = new QLabel; m_acceptLabel->setWordWrap(true); m_acceptLabel->installEventFilter(acceptClickForwarder); m_acceptLabel->setObjectName(QLatin1String("AcceptLicenseLabel")); m_acceptLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); m_rejectRadioButton = new QRadioButton(this); ClickForwarder *rejectClickForwarder = new ClickForwarder(m_rejectRadioButton); m_rejectRadioButton->setObjectName(QString::fromUtf8("RejectLicenseRadioButton")); m_rejectRadioButton->setShortcut(QKeySequence(tr("Alt+D", "do not agree license"))); m_rejectLabel = new QLabel; m_rejectLabel->setWordWrap(true); m_rejectLabel->installEventFilter(rejectClickForwarder); m_rejectLabel->setObjectName(QLatin1String("RejectLicenseLabel")); m_rejectLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); QGridLayout *gridLayout = new QGridLayout; gridLayout->setColumnStretch(1, 1); gridLayout->addWidget(m_acceptRadioButton, 0, 0); gridLayout->addWidget(m_acceptLabel, 0, 1); gridLayout->addWidget(m_rejectRadioButton, 1, 0); gridLayout->addWidget(m_rejectLabel, 1, 1); layout->addLayout(gridLayout); connect(m_acceptRadioButton, &QAbstractButton::toggled, this, &QWizardPage::completeChanged); connect(m_rejectRadioButton, &QAbstractButton::toggled, this, &QWizardPage::completeChanged); m_rejectRadioButton->setChecked(true); } /*! Initializes the page's fields based on values from fields on previous pages. */ void LicenseAgreementPage::entering() { m_licenseListWidget->clear(); m_textBrowser->setHtml(QString()); m_licenseListWidget->setVisible(false); packageManagerCore()->calculateComponentsToInstall(); foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall()) addLicenseItem(component->licenses()); const int licenseCount = m_licenseListWidget->count(); if (licenseCount > 0) { m_licenseListWidget->setVisible(licenseCount > 1); m_licenseListWidget->setCurrentItem(m_licenseListWidget->item(0)); } updateUi(); } /*! Returns \c true if the accept license radio button is checked; otherwise, returns \c false. */ bool LicenseAgreementPage::isComplete() const { return m_acceptRadioButton->isChecked(); } void LicenseAgreementPage::openLicenseUrl(const QUrl &url) { QDesktopServices::openUrl(url); } void LicenseAgreementPage::currentItemChanged(QListWidgetItem *current) { if (current) m_textBrowser->setText(current->data(Qt::UserRole).toString()); } void LicenseAgreementPage::addLicenseItem(const QHash<QString, QPair<QString, QString> > &hash) { for (QHash<QString, QPair<QString, QString> >::const_iterator it = hash.begin(); it != hash.end(); ++it) { QListWidgetItem *item = new QListWidgetItem(it.key(), m_licenseListWidget); item->setData(Qt::UserRole, it.value().second); } } void LicenseAgreementPage::updateUi() { QString subTitleText; QString acceptButtonText; QString rejectButtonText; if (m_licenseListWidget->count() == 1) { subTitleText = tr("Please read the following license agreement. You must accept the terms " "contained in this agreement before continuing with the installation."); acceptButtonText = tr("I accept the license."); rejectButtonText = tr("I do not accept the license."); } else { subTitleText = tr("Please read the following license agreements. You must accept the terms " "contained in these agreements before continuing with the installation."); acceptButtonText = tr("I accept the licenses."); rejectButtonText = tr("I do not accept the licenses."); } m_licenseListWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); setColoredSubTitle(subTitleText); m_acceptLabel->setText(acceptButtonText); m_rejectLabel->setText(rejectButtonText); } // -- ComponentSelectionPage::Private class ComponentSelectionPage::Private : public QObject { Q_OBJECT public: Private(ComponentSelectionPage *qq, PackageManagerCore *core) : q(qq) , m_core(core) , m_treeView(new QTreeView(q)) , m_allModel(m_core->defaultComponentModel()) , m_updaterModel(m_core->updaterComponentModel()) , m_currentModel(m_allModel) , m_compressedButtonVisible(false) { m_treeView->setObjectName(QLatin1String("ComponentsTreeView")); connect(m_allModel, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)), this, SLOT(onModelStateChanged(QInstaller::ComponentModel::ModelState))); connect(m_updaterModel, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)), this, SLOT(onModelStateChanged(QInstaller::ComponentModel::ModelState))); QHBoxLayout *hlayout = new QHBoxLayout; hlayout->addWidget(m_treeView, 3); m_descriptionLabel = new QLabel(q); m_descriptionLabel->setWordWrap(true); m_descriptionLabel->setObjectName(QLatin1String("ComponentDescriptionLabel")); m_vlayout = new QVBoxLayout; m_vlayout->setObjectName(QLatin1String("VerticalLayout")); m_vlayout->addWidget(m_descriptionLabel); m_sizeLabel = new QLabel(q); m_sizeLabel->setWordWrap(true); m_vlayout->addWidget(m_sizeLabel); m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel")); #ifdef INSTALLCOMPRESSED allowCompressedRepositoryInstall(); #endif m_vlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); hlayout->addLayout(m_vlayout, 2); QVBoxLayout *layout = new QVBoxLayout(q); layout->addLayout(hlayout, 1); m_checkDefault = new QPushButton; connect(m_checkDefault, &QAbstractButton::clicked, this, &ComponentSelectionPage::Private::selectDefault); if (m_core->isInstaller()) { m_checkDefault->setObjectName(QLatin1String("SelectDefaultComponentsButton")); m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+A", "select default components"))); m_checkDefault->setText(ComponentSelectionPage::tr("Def&ault")); } else { m_checkDefault->setEnabled(false); m_checkDefault->setObjectName(QLatin1String("ResetComponentsButton")); m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+R", "reset to already installed components"))); m_checkDefault->setText(ComponentSelectionPage::tr("&Reset")); } hlayout = new QHBoxLayout; hlayout->addWidget(m_checkDefault); m_checkAll = new QPushButton; hlayout->addWidget(m_checkAll); connect(m_checkAll, &QAbstractButton::clicked, this, &ComponentSelectionPage::Private::selectAll); m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton")); m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S", "select all components"))); m_checkAll->setText(ComponentSelectionPage::tr("&Select All")); m_uncheckAll = new QPushButton; hlayout->addWidget(m_uncheckAll); connect(m_uncheckAll, &QAbstractButton::clicked, this, &ComponentSelectionPage::Private::deselectAll); m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton")); m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D", "deselect all components"))); m_uncheckAll->setText(ComponentSelectionPage::tr("&Deselect All")); hlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); layout->addLayout(hlayout); } void allowCompressedRepositoryInstall() { if (m_compressedButtonVisible) { return; } connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int))); connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString))); m_bspLabel = new QLabel(ComponentSelectionPage::tr("To install new "\ "compressed repository, browse the repositories from your computer"),q); m_bspLabel->setWordWrap(true); m_bspLabel->setObjectName(QLatin1String("CompressedButtonLabel")); m_vlayout->addSpacing(50); m_vlayout->addWidget(m_bspLabel); m_progressBar = new QProgressBar(); m_progressBar->setRange(0, 0); m_progressBar->hide(); m_vlayout->addWidget(m_progressBar); m_progressBar->setObjectName(QLatin1String("CompressedInstallProgressBar")); m_installCompressButton = new QPushButton; connect(m_installCompressButton, &QAbstractButton::clicked, this, &ComponentSelectionPage::Private::selectCompressedPackage); m_installCompressButton->setObjectName(QLatin1String("InstallCompressedPackageButton")); m_installCompressButton->setText(ComponentSelectionPage::tr("&Browse QBSP files")); m_vlayout->addWidget(m_installCompressButton); m_compressedButtonVisible = true; } void updateTreeView() { m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager()); if (m_treeView->selectionModel()) { disconnect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ComponentSelectionPage::Private::currentSelectedChanged); } m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel; m_treeView->setModel(m_currentModel); m_treeView->setExpanded(m_currentModel->index(0, 0), true); const bool installActionColumnVisible = m_core->settings().installActionColumnVisible(); if (!installActionColumnVisible) m_treeView->hideColumn(ComponentModelHelper::ActionColumn); m_treeView->header()->setSectionResizeMode( ComponentModelHelper::NameColumn, QHeaderView::ResizeToContents); if (m_core->isInstaller()) { m_treeView->setHeaderHidden(true); for (int i = ComponentModelHelper::InstalledVersionColumn; i < m_currentModel->columnCount(); ++i) m_treeView->hideColumn(i); if (installActionColumnVisible) { m_treeView->header()->setStretchLastSection(false); m_treeView->header()->setSectionResizeMode( ComponentModelHelper::NameColumn, QHeaderView::Stretch); m_treeView->header()->setSectionResizeMode( ComponentModelHelper::ActionColumn, QHeaderView::ResizeToContents); } } else { m_treeView->header()->setStretchLastSection(true); if (installActionColumnVisible) { m_treeView->header()->setSectionResizeMode( ComponentModelHelper::NameColumn, QHeaderView::Interactive); m_treeView->header()->setSectionResizeMode( ComponentModelHelper::ActionColumn, QHeaderView::Interactive); } for (int i = 0; i < m_currentModel->columnCount(); ++i) m_treeView->resizeColumnToContents(i); } bool hasChildren = false; const int rowCount = m_currentModel->rowCount(); for (int row = 0; row < rowCount && !hasChildren; ++row) hasChildren = m_currentModel->hasChildren(m_currentModel->index(row, 0)); m_treeView->setRootIsDecorated(hasChildren); connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ComponentSelectionPage::Private::currentSelectedChanged); m_treeView->setCurrentIndex(m_currentModel->index(0, 0)); } public slots: void currentSelectedChanged(const QModelIndex ¤t) { if (!current.isValid()) return; m_sizeLabel->setText(QString()); m_descriptionLabel->setText(m_currentModel->data(m_currentModel->index(current.row(), ComponentModelHelper::NameColumn, current.parent()), Qt::ToolTipRole).toString()); Component *component = m_currentModel->componentFromIndex(current); if ((m_core->isUninstaller()) || (!component)) return; if (component->isSelected() && (component->value(scUncompressedSizeSum).toLongLong() > 0)) { m_sizeLabel->setText(ComponentSelectionPage::tr("This component " "will occupy approximately %1 on your hard disk drive.") .arg(humanReadableSize(component->value(scUncompressedSizeSum).toLongLong()))); } } void selectAll() { m_currentModel->setCheckedState(ComponentModel::AllChecked); } void deselectAll() { m_currentModel->setCheckedState(ComponentModel::AllUnchecked); } void selectCompressedPackage() { QString defaultDownloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); QStringList fileNames = QFileDialog::getOpenFileNames(NULL, ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory, QLatin1String("QBSP or 7z Files (*.qbsp *.7z)")); QSet<Repository> set; foreach (QString fileName, fileNames) { Repository repository = Repository::fromUserInput(fileName, true); repository.setEnabled(true); set.insert(repository); } if (set.count() > 0) { m_progressBar->show(); m_installCompressButton->hide(); QPushButton *const b = qobject_cast<QPushButton *>(q->gui()->button(QWizard::NextButton)); b->setEnabled(false); m_core->settings().addTemporaryRepositories(set, false); if (!m_core->fetchCompressedPackagesTree()) { setMessage(m_core->error()); } else { updateTreeView(); setMessage(ComponentSelectionPage::tr("To install new "\ "compressed repository, browse the repositories from your computer")); } m_progressBar->hide(); m_installCompressButton->show(); b->setEnabled(true); } } /*! Updates the value of \a progress on the progress bar. */ void onProgressChanged(int progress) { m_progressBar->setValue(progress); } /*! Displays the message \a msg on the page. */ void setMessage(const QString &msg) { QWizardPage *page = q->gui()->currentPage(); if (m_bspLabel && page && page->objectName() == QLatin1String("ComponentSelectionPage")) m_bspLabel->setText(msg); } void selectDefault() { m_currentModel->setCheckedState(ComponentModel::DefaultChecked); } void onModelStateChanged(QInstaller::ComponentModel::ModelState state) { q->setModified(state.testFlag(ComponentModel::DefaultChecked) == false); // If all components in the checked list are only checkable when run without forced // installation, set ComponentModel::AllUnchecked as well, as we cannot uncheck anything. // Helps to keep the UI correct. if ((!m_core->noForceInstallation()) && (m_currentModel->checked() == m_currentModel->uncheckable())) { state |= ComponentModel::AllUnchecked; } // enable the button if the corresponding flag is not set m_checkAll->setEnabled(state.testFlag(ComponentModel::AllChecked) == false); m_uncheckAll->setEnabled(state.testFlag(ComponentModel::AllUnchecked) == false); m_checkDefault->setEnabled(state.testFlag(ComponentModel::DefaultChecked) == false); // update the current selected node (important to reflect possible sub-node changes) if (m_treeView->selectionModel()) currentSelectedChanged(m_treeView->selectionModel()->currentIndex()); } public: ComponentSelectionPage *q; PackageManagerCore *m_core; QTreeView *m_treeView; ComponentModel *m_allModel; ComponentModel *m_updaterModel; ComponentModel *m_currentModel; QLabel *m_sizeLabel; QLabel *m_descriptionLabel; QPushButton *m_checkAll; QPushButton *m_uncheckAll; QPushButton *m_checkDefault; QPushButton *m_installCompressButton; QLabel *m_bspLabel; QProgressBar *m_progressBar; QVBoxLayout *m_vlayout; bool m_compressedButtonVisible; }; // -- ComponentSelectionPage /*! \class QInstaller::ComponentSelectionPage \inmodule QtInstallerFramework \brief The ComponentSelectionPage class changes the checked state of components. */ /*! Constructs a component selection page with \a core as parent. */ ComponentSelectionPage::ComponentSelectionPage(PackageManagerCore *core) : PackageManagerPage(core) , d(new Private(this, core)) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("ComponentSelectionPage")); setColoredTitle(tr("Select Components")); } /*! Destructs a component selection page. */ ComponentSelectionPage::~ComponentSelectionPage() { delete d; } /*! Initializes the page's fields based on values from fields on previous pages. The text to display depends on whether the page is being used in an installer, updater, or uninstaller. */ void ComponentSelectionPage::entering() { static const char *strings[] = { QT_TR_NOOP("Please select the components you want to update."), QT_TR_NOOP("Please select the components you want to install."), QT_TR_NOOP("Please select the components you want to uninstall."), QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.") }; int index = 0; PackageManagerCore *core = packageManagerCore(); if (core->isInstaller()) index = 1; if (core->isUninstaller()) index = 2; if (core->isPackageManager()) index = 3; setColoredSubTitle(tr(strings[index])); d->updateTreeView(); setModified(isComplete()); } /*! Called when the show event \a event occurs. Switching pages back and forth might restore or remove the checked state of certain components the end users have checked or not checked, because the dependencies are resolved and checked when clicking \uicontrol Next. So as not to confuse the end users with newly checked components they did not check, the state they left the page in is restored. */ void ComponentSelectionPage::showEvent(QShowEvent *event) { // remove once we deprecate isSelected, setSelected etc... if (!event->spontaneous()) packageManagerCore()->restoreCheckState(); QWizardPage::showEvent(event); } /*! Selects all components in the component tree. */ void ComponentSelectionPage::selectAll() { d->selectAll(); } /*! Deselects all components in the component tree. */ void ComponentSelectionPage::deselectAll() { d->deselectAll(); } /*! Selects the components that have the \c <Default> element set to \c true in the package information file. */ void ComponentSelectionPage::selectDefault() { if (packageManagerCore()->isInstaller()) d->selectDefault(); } /*! Selects the component with \a id in the component tree. */ void ComponentSelectionPage::selectComponent(const QString &id) { const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id); if (idx.isValid()) d->m_currentModel->setData(idx, Qt::Checked, Qt::CheckStateRole); } /*! Deselects the component with \a id in the component tree. */ void ComponentSelectionPage::deselectComponent(const QString &id) { const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id); if (idx.isValid()) d->m_currentModel->setData(idx, Qt::Unchecked, Qt::CheckStateRole); } void ComponentSelectionPage::allowCompressedRepositoryInstall() { d->allowCompressedRepositoryInstall(); } void ComponentSelectionPage::setModified(bool modified) { setComplete(modified); } /*! Returns \c true if at least one component is checked on the page. */ bool ComponentSelectionPage::isComplete() const { if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater()) return d->m_currentModel->checked().count(); return d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false; } // -- TargetDirectoryPage /*! \class QInstaller::TargetDirectoryPage \inmodule QtInstallerFramework \brief The TargetDirectoryPage class specifies the target directory for the installation. End users can leave the page to continue the installation only if certain criteria are fulfilled. Some of them are checked in the validatePage() function, some in the targetDirWarning() function: \list \li No empty path given as target. \li No relative path given as target. \li Only ASCII characters are allowed in the path if the <AllowNonAsciiCharacters> element in the configuration file is set to \c false. \li The following ambiguous characters are not allowed in the path: [\"~<>|?*!@#$%^&:,;] \li No root or home directory given as target. \li On Windows, path names must be less than 260 characters long. \li No spaces in the path if the <AllowSpaceInPath> element in the configuration file is set to \c false. \endlist */ /*! Constructs a target directory selection page with \a core as parent. */ TargetDirectoryPage::TargetDirectoryPage(PackageManagerCore *core) : PackageManagerPage(core) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("TargetDirectoryPage")); setColoredTitle(tr("Installation Folder")); QVBoxLayout *layout = new QVBoxLayout(this); QLabel *msgLabel = new QLabel(this); msgLabel->setWordWrap(true); msgLabel->setObjectName(QLatin1String("MessageLabel")); msgLabel->setText(tr("Please specify the directory where %1 will be installed.").arg(productName())); layout->addWidget(msgLabel); QHBoxLayout *hlayout = new QHBoxLayout; m_textChangeTimer.setSingleShot(true); m_textChangeTimer.setInterval(200); connect(&m_textChangeTimer, &QTimer::timeout, this, &QWizardPage::completeChanged); m_lineEdit = new QLineEdit(this); m_lineEdit->setObjectName(QLatin1String("TargetDirectoryLineEdit")); connect(m_lineEdit, &QLineEdit::textChanged, &m_textChangeTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); hlayout->addWidget(m_lineEdit); QPushButton *browseButton = new QPushButton(this); browseButton->setObjectName(QLatin1String("BrowseDirectoryButton")); connect(browseButton, &QAbstractButton::clicked, this, &TargetDirectoryPage::dirRequested); browseButton->setShortcut(QKeySequence(tr("Alt+R", "browse file system to choose a file"))); browseButton->setText(tr("B&rowse...")); hlayout->addWidget(browseButton); layout->addLayout(hlayout); QPalette palette; palette.setColor(QPalette::WindowText, Qt::red); m_warningLabel = new QLabel(this); m_warningLabel->setPalette(palette); m_warningLabel->setWordWrap(true); m_warningLabel->setObjectName(QLatin1String("WarningLabel")); layout->addWidget(m_warningLabel); setLayout(layout); } /*! Returns the target directory for the installation. */ QString TargetDirectoryPage::targetDir() const { return m_lineEdit->text().trimmed(); } /*! Sets the directory specified by \a dirName as the target directory for the installation. */ void TargetDirectoryPage::setTargetDir(const QString &dirName) { m_lineEdit->setText(dirName); } /*! Initializes the page. */ void TargetDirectoryPage::initializePage() { QString targetDir = packageManagerCore()->value(scTargetDir); if (targetDir.isEmpty()) { targetDir = QDir::homePath() + QDir::separator(); if (!packageManagerCore()->settings().allowSpaceInPath()) { // prevent spaces in the default target directory if (targetDir.contains(QLatin1Char(' '))) targetDir = QDir::rootPath(); targetDir += productName().remove(QLatin1Char(' ')); } else { targetDir += productName(); } } m_lineEdit->setText(QDir::toNativeSeparators(QDir(targetDir).absolutePath())); PackageManagerPage::initializePage(); } /*! Checks whether the target directory exists and has contents: \list \li Returns \c true if the directory exists and is empty. \li Returns \c false if the directory already exists and contains an installation. \li Returns \c false if the target is a file or a symbolic link. \li Returns \c true or \c false if the directory exists but is not empty, depending on the choice that the end users make in the displayed message box. \endlist */ bool TargetDirectoryPage::validatePage() { m_textChangeTimer.stop(); if (!isComplete()) return false; if (!isVisible()) return true; const QString remove = packageManagerCore()->value(QLatin1String("RemoveTargetDir")); if (!QVariant(remove).toBool()) return true; const QString targetDir = this->targetDir(); const QDir dir(targetDir); // the directory exists and is empty... if (dir.exists() && dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) return true; const QFileInfo fi(targetDir); if (fi.isDir()) { QString fileName = packageManagerCore()->settings().maintenanceToolName(); #if defined(Q_OS_OSX) if (QInstaller::isInBundle(QCoreApplication::applicationDirPath())) fileName += QLatin1String(".app/Contents/MacOS/") + fileName; #elif defined(Q_OS_WIN) fileName += QLatin1String(".exe"); #endif QFileInfo fi2(targetDir + QDir::separator() + fileName); if (fi2.exists()) { return failWithError(QLatin1String("TargetDirectoryInUse"), tr("The directory you selected already " "exists and contains an installation. Choose a different target for installation.")); } return askQuestion(QLatin1String("OverwriteTargetDirectory"), tr("You have selected an existing, non-empty directory for installation.\nNote that it will be " "completely wiped on uninstallation of this application.\nIt is not advisable to install into " "this directory as installation might fail.\nDo you want to continue?")); } else if (fi.isFile() || fi.isSymLink()) { return failWithError(QLatin1String("WrongTargetDirectory"), tr("You have selected an existing file " "or symlink, please choose a different target for installation.")); } return true; } /*! Initializes the page's fields based on values from fields on previous pages. */ void TargetDirectoryPage::entering() { if (QPushButton *const b = qobject_cast<QPushButton *>(gui()->button(QWizard::NextButton))) b->setDefault(true); } /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ void TargetDirectoryPage::leaving() { packageManagerCore()->setValue(scTargetDir, targetDir()); } void TargetDirectoryPage::dirRequested() { const QString newDirName = QFileDialog::getExistingDirectory(this, tr("Select Installation Folder"), targetDir()); if (newDirName.isEmpty() || newDirName == targetDir()) return; m_lineEdit->setText(QDir::toNativeSeparators(newDirName)); } /*! Requests a warning message to be shown to end users upon invalid input. If the input is valid, the \uicontrol Next button is enabled. Returns \c true if a valid path to the target directory is set; otherwise returns \c false. */ bool TargetDirectoryPage::isComplete() const { m_warningLabel->setText(targetDirWarning()); return m_warningLabel->text().isEmpty(); } /*! Returns a warning if the path to the target directory is not set or if it is invalid. Installation can continue only after a valid target path is given. */ QString TargetDirectoryPage::targetDirWarning() const { if (targetDir().isEmpty()) return tr("The installation path cannot be empty, please specify a valid directory."); QDir target(targetDir()); if (target.isRelative()) return tr("The installation path cannot be relative, please specify an absolute path."); QString nativeTargetDir = QDir::toNativeSeparators(target.absolutePath()); if (!packageManagerCore()->settings().allowNonAsciiCharacters()) { for (int i = 0; i < nativeTargetDir.length(); ++i) { if (nativeTargetDir.at(i).unicode() & 0xff80) { return tr("The path or installation directory contains non ASCII characters. This " "is currently not supported! Please choose a different path or installation " "directory."); } } } target = target.canonicalPath(); if (target == QDir::root() || target == QDir::home()) { return tr("As the install directory is completely deleted, installing in %1 is forbidden.") .arg(QDir::toNativeSeparators(target.path())); } #ifdef Q_OS_WIN // folder length (set by user) + maintenance tool name length (no extension) + extra padding if ((nativeTargetDir.length() + packageManagerCore()->settings().maintenanceToolName().length() + 20) >= MAX_PATH) { return tr("The path you have entered is too long, please make sure to " "specify a valid path."); } static QRegularExpression reg(QLatin1String( "^(?<drive>[a-zA-Z]:\\\\)|" "^(\\\\\\\\(?<path>\\w+)\\\\)|" "^(\\\\\\\\(?<ip>\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\\\)")); const QRegularExpressionMatch regMatch = reg.match(nativeTargetDir); const QString ipMatch = regMatch.captured(QLatin1String("ip")); const QString pathMatch = regMatch.captured(QLatin1String("path")); const QString driveMatch = regMatch.captured(QLatin1String("drive")); if (ipMatch.isEmpty() && pathMatch.isEmpty() && driveMatch.isEmpty()) { return tr("The path you have entered is not valid, please make sure to " "specify a valid target."); } if (!driveMatch.isEmpty()) { bool validDrive = false; const QFileInfo drive(driveMatch); foreach (const QFileInfo &driveInfo, QDir::drives()) { if (drive == driveInfo) { validDrive = true; break; } } if (!validDrive) { // right now we can only verify local drives return tr("The path you have entered is not valid, please make sure to " "specify a valid drive."); } nativeTargetDir = nativeTargetDir.mid(2); } if (nativeTargetDir.endsWith(QLatin1Char('.'))) return tr("The installation path must not end with '.', please specify a valid directory."); QString ambiguousChars = QLatin1String("[\"~<>|?*!@#$%^&:,; ]" "|(\\\\CON)(\\\\|$)|(\\\\PRN)(\\\\|$)|(\\\\AUX)(\\\\|$)|(\\\\NUL)(\\\\|$)|(\\\\COM\\d)(\\\\|$)|(\\\\LPT\\d)(\\\\|$)"); #else // Q_OS_WIN QString ambiguousChars = QStringLiteral("[~<>|?*!@#$%^&:,; \\\\]"); #endif // Q_OS_WIN if (packageManagerCore()->settings().allowSpaceInPath()) ambiguousChars.remove(QLatin1Char(' ')); static QRegularExpression ambCharRegEx(ambiguousChars, QRegularExpression::CaseInsensitiveOption); // check if there are not allowed characters in the target path QRegularExpressionMatch match = ambCharRegEx.match(nativeTargetDir); if (match.hasMatch()) { return tr("The installation path must not contain \"%1\", " "please specify a valid directory.").arg(match.captured(0)); } return QString(); } /*! Returns \c true if a warning message specified by \a message with the identifier \a identifier is presented to end users for acknowledgment. */ bool TargetDirectoryPage::askQuestion(const QString &identifier, const QString &message) { QMessageBox::StandardButton bt = MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), identifier, tr("Warning"), message, QMessageBox::Yes | QMessageBox::No); return bt == QMessageBox::Yes; } bool TargetDirectoryPage::failWithError(const QString &identifier, const QString &message) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), identifier, tr("Error"), message); return false; } // -- StartMenuDirectoryPage /*! \class QInstaller::StartMenuDirectoryPage \inmodule QtInstallerFramework \brief The StartMenuDirectoryPage class specifies the program group for the product in the Windows Start menu. */ /*! Constructs a Start menu directory selection page with \a core as parent. */ StartMenuDirectoryPage::StartMenuDirectoryPage(PackageManagerCore *core) : PackageManagerPage(core) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("StartMenuDirectoryPage")); setColoredTitle(tr("Start Menu shortcuts")); setColoredSubTitle(tr("Select the Start Menu in which you would like to create the program's " "shortcuts. You can also enter a name to create a new directory.")); m_lineEdit = new QLineEdit(this); m_lineEdit->setText(core->value(scStartMenuDir, productName())); m_lineEdit->setObjectName(QLatin1String("StartMenuPathLineEdit")); startMenuPath = core->value(QLatin1String("UserStartMenuProgramsPath")); QStringList dirs = QDir(startMenuPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot); if (core->value(scAllUsers, scFalse) == scTrue) { startMenuPath = core->value(QLatin1String("AllUsersStartMenuProgramsPath")); dirs += QDir(startMenuPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot); } dirs.removeDuplicates(); m_listWidget = new QListWidget(this); foreach (const QString &dir, dirs) new QListWidgetItem(dir, m_listWidget); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(m_lineEdit); layout->addWidget(m_listWidget); setLayout(layout); connect(m_listWidget, &QListWidget::currentItemChanged, this, &StartMenuDirectoryPage::currentItemChanged); } /*! Returns the program group for the product in the Windows Start menu. */ QString StartMenuDirectoryPage::startMenuDir() const { return m_lineEdit->text().trimmed(); } /*! Sets \a startMenuDir as the program group for the product in the Windows Start menu. */ void StartMenuDirectoryPage::setStartMenuDir(const QString &startMenuDir) { m_lineEdit->setText(startMenuDir.trimmed()); } /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ void StartMenuDirectoryPage::leaving() { packageManagerCore()->setValue(scStartMenuDir, startMenuPath + QDir::separator() + startMenuDir()); } void StartMenuDirectoryPage::currentItemChanged(QListWidgetItem *current) { if (current) setStartMenuDir(current->data(Qt::DisplayRole).toString()); } // -- ReadyForInstallationPage /*! \class QInstaller::ReadyForInstallationPage \inmodule QtInstallerFramework \brief The ReadyForInstallationPage class informs end users that the installation can begin. */ /*! Constructs a ready for installation page with \a core as parent. */ ReadyForInstallationPage::ReadyForInstallationPage(PackageManagerCore *core) : PackageManagerPage(core) , m_msgLabel(new QLabel) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("ReadyForInstallationPage")); QVBoxLayout *baseLayout = new QVBoxLayout(); baseLayout->setObjectName(QLatin1String("BaseLayout")); QVBoxLayout *topLayout = new QVBoxLayout(); topLayout->setObjectName(QLatin1String("TopLayout")); m_msgLabel->setWordWrap(true); m_msgLabel->setObjectName(QLatin1String("MessageLabel")); m_msgLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); topLayout->addWidget(m_msgLabel); baseLayout->addLayout(topLayout); QVBoxLayout *bottomLayout = new QVBoxLayout(); bottomLayout->setObjectName(QLatin1String("BottomLayout")); bottomLayout->addStretch(); m_taskDetailsBrowser = new QTextBrowser(this); m_taskDetailsBrowser->setReadOnly(true); m_taskDetailsBrowser->setObjectName(QLatin1String("TaskDetailsBrowser")); m_taskDetailsBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_taskDetailsBrowser->setVisible(false); bottomLayout->addWidget(m_taskDetailsBrowser); bottomLayout->setStretch(1, 10); baseLayout->addLayout(bottomLayout); setCommitPage(true); setLayout(baseLayout); } /*! Initializes the page's fields based on values from fields on previous pages. The text to display depends on whether the page is being used in an installer, updater, or uninstaller. */ void ReadyForInstallationPage::entering() { setComplete(false); if (packageManagerCore()->isUninstaller()) { m_taskDetailsBrowser->setVisible(false); setButtonText(QWizard::CommitButton, tr("U&ninstall")); setColoredTitle(tr("Ready to Uninstall")); m_msgLabel->setText(tr("Setup is now ready to begin removing %1 from your computer.<br>" "<font color=\"red\">The program directory %2 will be deleted completely</font>, " "including all content in that directory!") .arg(productName(), QDir::toNativeSeparators(QDir(packageManagerCore()->value(scTargetDir)) .absolutePath()))); setComplete(true); return; } else if (packageManagerCore()->isMaintainer()) { setButtonText(QWizard::CommitButton, tr("U&pdate")); setColoredTitle(tr("Ready to Update Packages")); m_msgLabel->setText(tr("Setup is now ready to begin updating your installation.")); } else { Q_ASSERT(packageManagerCore()->isInstaller()); setButtonText(QWizard::CommitButton, tr("&Install")); setColoredTitle(tr("Ready to Install")); m_msgLabel->setText(tr("Setup is now ready to begin installing %1 on your computer.") .arg(productName())); } QString htmlOutput; bool componentsOk = packageManagerCore()->calculateComponents(&htmlOutput); m_taskDetailsBrowser->setHtml(htmlOutput); m_taskDetailsBrowser->setVisible(!componentsOk || isVerbose()); setComplete(componentsOk); const VolumeInfo tempVolume = VolumeInfo::fromPath(QDir::tempPath()); const VolumeInfo targetVolume = VolumeInfo::fromPath(packageManagerCore()->value(scTargetDir)); const quint64 tempVolumeAvailableSize = tempVolume.availableSize(); const quint64 installVolumeAvailableSize = targetVolume.availableSize(); // at the moment there is no better way to check this if (targetVolume.size() == 0 && installVolumeAvailableSize == 0) { qDebug().nospace() << "Cannot determine available space on device. " "Volume descriptor: " << targetVolume.volumeDescriptor() << ", Mount path: " << targetVolume.mountPath() << ". Continue silently."; return; // TODO: Shouldn't this also disable the "Next" button? } const bool tempOnSameVolume = (targetVolume == tempVolume); if (tempOnSameVolume) { qDebug() << "Tmp and install directories are on the same volume. Volume mount point:" << targetVolume.mountPath() << "Free space available:" << humanReadableSize(installVolumeAvailableSize); } else { qDebug() << "Tmp is on a different volume than the installation directory. Tmp volume mount point:" << tempVolume.mountPath() << "Free space available:" << humanReadableSize(tempVolumeAvailableSize) << "Install volume mount point:" << targetVolume.mountPath() << "Free space available:" << humanReadableSize(installVolumeAvailableSize); } const quint64 extraSpace = 256 * 1024 * 1024LL; quint64 required(packageManagerCore()->requiredDiskSpace()); quint64 tempRequired(packageManagerCore()->requiredTemporaryDiskSpace()); if (required < extraSpace) { required += 0.1 * required; tempRequired += 0.1 * tempRequired; } else { required += extraSpace; tempRequired += extraSpace; } quint64 repositorySize = 0; const bool createLocalRepository = packageManagerCore()->createLocalRepositoryFromBinary(); if (createLocalRepository && packageManagerCore()->isInstaller()) { repositorySize = QFile(QCoreApplication::applicationFilePath()).size(); // if we create a local repository, take that space into account as well required += repositorySize; } qDebug() << "Installation space required:" << humanReadableSize(required) << "Temporary space " "required:" << humanReadableSize(tempRequired) << "Local repository size:" << humanReadableSize(repositorySize); if (tempOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) { m_msgLabel->setText(tr("Not enough disk space to store temporary files and the " "installation. %1 are available, while %2 are at least required.") .arg(humanReadableSize(installVolumeAvailableSize), humanReadableSize(required + tempRequired))); setComplete(false); return; } if (installVolumeAvailableSize < required) { m_msgLabel->setText(tr("Not enough disk space to store all selected components! %1 are available " "while %2 are at least required.").arg(humanReadableSize(installVolumeAvailableSize), humanReadableSize(required))); setComplete(false); return; } if (tempVolumeAvailableSize < tempRequired) { m_msgLabel->setText(tr("Not enough disk space to store temporary files! %1 are available " "while %2 are at least required.").arg(humanReadableSize(tempVolumeAvailableSize), humanReadableSize(tempRequired))); setComplete(false); return; } if (installVolumeAvailableSize - required < 0.01 * targetVolume.size()) { // warn for less than 1% of the volume's space being free m_msgLabel->setText(tr("The volume you selected for installation seems to have sufficient " "space for installation, but there will be less than 1% of the volume's space " "available afterwards. %1").arg(m_msgLabel->text())); } else if (installVolumeAvailableSize - required < 100 * 1024 * 1024LL) { // warn for less than 100MB being free m_msgLabel->setText(tr("The volume you selected for installation seems to have sufficient " "space for installation, but there will be less than 100 MB available afterwards. %1") .arg(m_msgLabel->text())); } m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), tr("Installation will use %1 of disk space.") .arg(humanReadableSize(packageManagerCore()->requiredDiskSpace())))); } /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ void ReadyForInstallationPage::leaving() { setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton)); } // -- PerformInstallationPage /*! \class QInstaller::PerformInstallationPage \inmodule QtInstallerFramework \brief The PerformInstallationPage class shows progress information about the installation state. This class is a container for the PerformInstallationForm class, which constructs the actual UI for the page. */ /*! \fn PerformInstallationPage::isInterruptible() const Returns \c true if the installation can be interrupted. */ /*! \fn PerformInstallationPage::setAutomatedPageSwitchEnabled(bool request) Enables automatic switching of pages when \a request is \c true. */ /*! Constructs a perform installation page with \a core as parent. The page contains a PerformInstallationForm that defines the UI for the page. */ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core) : PackageManagerPage(core) , m_performInstallationForm(new PerformInstallationForm(this)) { setPixmap(QWizard::WatermarkPixmap, QPixmap()); setObjectName(QLatin1String("PerformInstallationPage")); m_performInstallationForm->setupUi(this); connect(ProgressCoordinator::instance(), &ProgressCoordinator::detailTextChanged, m_performInstallationForm, &PerformInstallationForm::appendProgressDetails); connect(ProgressCoordinator::instance(), &ProgressCoordinator::detailTextResetNeeded, m_performInstallationForm, &PerformInstallationForm::clearDetailsBrowser); connect(m_performInstallationForm, &PerformInstallationForm::showDetailsChanged, this, &PerformInstallationPage::toggleDetailsWereChanged); connect(core, &PackageManagerCore::installationStarted, this, &PerformInstallationPage::installationStarted); connect(core, &PackageManagerCore::installationFinished, this, &PerformInstallationPage::installationFinished); connect(core, &PackageManagerCore::uninstallationStarted, this, &PerformInstallationPage::uninstallationStarted); connect(core, &PackageManagerCore::uninstallationFinished, this, &PerformInstallationPage::uninstallationFinished); connect(core, &PackageManagerCore::titleMessageChanged, this, &PerformInstallationPage::setTitleMessage); connect(this, &PerformInstallationPage::setAutomatedPageSwitchEnabled, core, &PackageManagerCore::setAutomatedPageSwitchEnabled); m_performInstallationForm->setDetailsWidgetVisible(true); setCommitPage(true); } /*! Destructs a perform installation page. */ PerformInstallationPage::~PerformInstallationPage() { delete m_performInstallationForm; } /*! Returns \c true if automatically switching to the page is requested. */ bool PerformInstallationPage::isAutoSwitching() const { return !m_performInstallationForm->isShowingDetails(); } // -- protected /*! Initializes the page's fields based on values from fields on previous pages. The text to display depends on whether the page is being used in an installer, updater, or uninstaller. */ void PerformInstallationPage::entering() { setComplete(false); if (packageManagerCore()->isUninstaller()) { setButtonText(QWizard::CommitButton, tr("U&ninstall")); setColoredTitle(tr("Uninstalling %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runUninstaller())); } else if (packageManagerCore()->isMaintainer()) { setButtonText(QWizard::CommitButton, tr("&Update")); setColoredTitle(tr("Updating components of %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater())); } else { setButtonText(QWizard::CommitButton, tr("&Install")); setColoredTitle(tr("Installing %1").arg(productName())); QTimer::singleShot(30, packageManagerCore(), SLOT(runInstaller())); } m_performInstallationForm->enableDetails(); emit setAutomatedPageSwitchEnabled(true); if (isVerbose()) m_performInstallationForm->toggleDetails(); } /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ void PerformInstallationPage::leaving() { setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton)); } // -- public slots /*! Sets \a title as the title of the perform installation page. */ void PerformInstallationPage::setTitleMessage(const QString &title) { setColoredTitle(title); } // -- private slots void PerformInstallationPage::installationStarted() { m_performInstallationForm->startUpdateProgress(); } void PerformInstallationPage::installationFinished() { m_performInstallationForm->stopUpdateProgress(); if (!isAutoSwitching()) { m_performInstallationForm->scrollDetailsToTheEnd(); m_performInstallationForm->setDetailsButtonEnabled(false); setComplete(true); setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::NextButton)); } } void PerformInstallationPage::uninstallationStarted() { m_performInstallationForm->startUpdateProgress(); if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) cancel->setEnabled(false); } void PerformInstallationPage::uninstallationFinished() { installationFinished(); if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) cancel->setEnabled(false); } void PerformInstallationPage::toggleDetailsWereChanged() { emit setAutomatedPageSwitchEnabled(isAutoSwitching()); } // -- FinishedPage /*! \class QInstaller::FinishedPage \inmodule QtInstallerFramework \brief The FinishedPage class completes the installation wizard. You can add the option to open the installed application to the page. */ /*! Constructs an installation finished page with \a core as parent. */ FinishedPage::FinishedPage(PackageManagerCore *core) : PackageManagerPage(core) , m_commitButton(0) { setObjectName(QLatin1String("FinishedPage")); setColoredTitle(tr("Completing the %1 Wizard").arg(productName())); m_msgLabel = new QLabel(this); m_msgLabel->setWordWrap(true); m_msgLabel->setObjectName(QLatin1String("MessageLabel")); m_runItCheckBox = new QCheckBox(this); m_runItCheckBox->setObjectName(QLatin1String("RunItCheckBox")); m_runItCheckBox->setChecked(true); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(m_msgLabel); layout->addWidget(m_runItCheckBox); setLayout(layout); setCommitPage(true); } /*! Initializes the page's fields based on values from fields on previous pages. */ void FinishedPage::entering() { m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.") .arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&'))) .arg(productName())); if (m_commitButton) { disconnect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked); m_commitButton = 0; } if (packageManagerCore()->isMaintainer()) { #ifdef Q_OS_OSX gui()->setOption(QWizard::NoCancelButton, false); #endif if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) { m_commitButton = cancel; cancel->setEnabled(true); cancel->setVisible(true); // we don't use the usual FinishButton so we need to connect the misused CancelButton connect(cancel, &QAbstractButton::clicked, gui(), &PackageManagerGui::finishButtonClicked); connect(cancel, &QAbstractButton::clicked, packageManagerCore(), &PackageManagerCore::finishButtonClicked); // for the moment we don't want the rejected signal connected disconnect(gui(), &QDialog::rejected, packageManagerCore(), &PackageManagerCore::setCanceled); connect(gui()->button(QWizard::CommitButton), &QAbstractButton::clicked, this, &FinishedPage::cleanupChangedConnects); } setButtonText(QWizard::CommitButton, tr("Restart")); setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::FinishButton)); } else { if (packageManagerCore()->isInstaller()) { m_commitButton = wizard()->button(QWizard::FinishButton); if (QPushButton *const b = qobject_cast<QPushButton *>(m_commitButton)) b->setDefault(true); } gui()->setOption(QWizard::NoCancelButton, true); if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) cancel->setVisible(false); } gui()->updateButtonLayout(); if (m_commitButton) { disconnect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked); connect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked); } if (packageManagerCore()->status() == PackageManagerCore::Success) { const QString finishedText = packageManagerCore()->value(QLatin1String("FinishedText")); if (!finishedText.isEmpty()) m_msgLabel->setText(finishedText); if (!packageManagerCore()->isUninstaller() && !packageManagerCore()->value(scRunProgram) .isEmpty()) { m_runItCheckBox->show(); m_runItCheckBox->setText(packageManagerCore()->value(scRunProgramDescription, tr("Run %1 now.")).arg(productName())); return; // job done } } else { // TODO: how to handle this using the config.xml setColoredTitle(tr("The %1 Wizard failed.").arg(productName())); } m_runItCheckBox->hide(); m_runItCheckBox->setChecked(false); } /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ void FinishedPage::leaving() { #ifdef Q_OS_OSX gui()->setOption(QWizard::NoCancelButton, true); #endif if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) cancel->setVisible(false); gui()->updateButtonLayout(); setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton)); setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton)); } /*! Performs the necessary operations when end users select the \uicontrol Finish button. */ void FinishedPage::handleFinishClicked() { const QString program = packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram)); const QStringList args = packageManagerCore()->replaceVariables(packageManagerCore() ->values(scRunProgramArguments)); if (!m_runItCheckBox->isChecked() || program.isEmpty()) return; qDebug() << "starting" << program << args; QProcess::startDetached(program, args); } /*! Removes changed connects from the page. */ void FinishedPage::cleanupChangedConnects() { if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) { // remove the workaround connect from entering page disconnect(cancel, &QAbstractButton::clicked, gui(), &PackageManagerGui::finishButtonClicked); disconnect(cancel, &QAbstractButton::clicked, packageManagerCore(), &PackageManagerCore::finishButtonClicked); connect(gui(), &QDialog::rejected, packageManagerCore(), &PackageManagerCore::setCanceled); disconnect(gui()->button(QWizard::CommitButton), &QAbstractButton::clicked, this, &FinishedPage::cleanupChangedConnects); } } // -- RestartPage /*! \class QInstaller::RestartPage \inmodule QtInstallerFramework \brief The RestartPage class enables restarting the installer. The restart installation page enables end users to restart the wizard. This is useful, for example, if the maintenance tool itself needs to be updated before updating the application components. When updating is done, end users can select \uicontrol Restart to start the maintenance tool. */ /*! \fn RestartPage::restart() This signal is emitted when the installer is restarted. */ /*! Constructs a restart installation page with \a core as parent. */ RestartPage::RestartPage(PackageManagerCore *core) : PackageManagerPage(core) { setObjectName(QLatin1String("RestartPage")); setColoredTitle(tr("Completing the %1 Setup Wizard").arg(productName())); setFinalPage(false); } /*! Returns the introduction page. */ int RestartPage::nextId() const { return PackageManagerCore::Introduction; } /*! Initializes the page's fields based on values from fields on previous pages. */ void RestartPage::entering() { if (!packageManagerCore()->needsHardRestart()) { if (QAbstractButton *finish = wizard()->button(QWizard::FinishButton)) finish->setVisible(false); QMetaObject::invokeMethod(this, "restart", Qt::QueuedConnection); } else { gui()->accept(); } } /*! Called when end users leave the page and the PackageManagerGui:currentPageChanged() signal is triggered. */ void RestartPage::leaving() { } #include "packagemanagergui.moc" #include "moc_packagemanagergui.cpp" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/packagemanagergui.h��������������������������������������������������������������0000664�0000000�0000000�00000030420�13253666515�0020412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PACKAGEMANAGERGUI_H #define PACKAGEMANAGERGUI_H #include "packagemanagercore.h" #include <QtCore/QEvent> #include <QtCore/QMetaType> #include <QtCore/QTimer> #include <QWizard> #include <QWizardPage> // FIXME: move to private classes QT_BEGIN_NAMESPACE class QAbstractButton; class QCheckBox; class QLabel; class QLineEdit; class QListWidget; class QListWidgetItem; class QProgressBar; class QRadioButton; class QTextBrowser; class QWinTaskbarButton; QT_END_NAMESPACE namespace QInstaller { class PackageManagerCore; class PackageManagerPage; class PerformInstallationForm; // -- PackageManagerGui class INSTALLER_EXPORT PackageManagerGui : public QWizard { Q_OBJECT public: explicit PackageManagerGui(PackageManagerCore *core, QWidget *parent = 0); virtual ~PackageManagerGui() = 0; void loadControlScript(const QString& scriptPath); void callControlScriptMethod(const QString& methodName); QWidget *pageById(int id) const; QWidget *pageByObjectName(const QString &name) const; QWidget *currentPageWidget() const; QWidget *pageWidgetByObjectName(const QString &name) const; QString defaultButtonText(int wizardButton) const; void clickButton(int wizardButton, int delayInMs = 0); bool isButtonEnabled(int wizardButton); void showSettingsButton(bool show); void setSettingsButtonEnabled(bool enable); void updateButtonLayout(); static QWizard::WizardStyle getStyle(const QString &name); void setSilent(bool silent); bool isSilent() const; void setTextItems(QObject *object, const QStringList &items); Q_SIGNALS: void interrupted(); void languageChanged(); void finishButtonClicked(); void gotRestarted(); void settingsButtonClicked(); public Q_SLOTS: void cancelButtonClicked(); void reject(); void rejectWithoutPrompt(); void showFinishedPage(); void setModified(bool value); protected Q_SLOTS: void wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page); void wizardPageRemovalRequested(QWidget *widget); void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page); void wizardWidgetRemovalRequested(QWidget *widget); void wizardPageVisibilityChangeRequested(bool visible, int page); void setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name, const QString &callbackName); void setAutomatedPageSwitchEnabled(bool request); private Q_SLOTS: void onLanguageChanged(); void customButtonClicked(int which); void dependsOnLocalInstallerBinary(); void currentPageChanged(int newId); protected: bool event(QEvent *event); void showEvent(QShowEvent *event); PackageManagerCore *packageManagerCore() const { return m_core; } void executeControlScript(int pageId); private: class Private; Private *const d; PackageManagerCore *m_core; }; // -- PackageManagerPage class INSTALLER_EXPORT PackageManagerPage : public QWizardPage { Q_OBJECT public: explicit PackageManagerPage(PackageManagerCore *core); virtual ~PackageManagerPage() {} virtual QPixmap logoPixmap() const; virtual QString productName() const; virtual QPixmap watermarkPixmap() const; virtual QPixmap bannerPixmap() const; void setColoredTitle(const QString &title); void setColoredSubTitle(const QString &subTitle); virtual bool isComplete() const; void setComplete(bool complete); virtual bool isInterruptible() const { return false; } PackageManagerGui* gui() const { return qobject_cast<PackageManagerGui*>(wizard()); } void setValidatePageComponent(QInstaller::Component *component); bool validatePage(); bool settingsButtonRequested() const { return m_needsSettingsButton; } void setSettingsButtonRequested(bool request) { m_needsSettingsButton = request; } signals: void entered(); void left(); protected: PackageManagerCore *packageManagerCore() const; // Inserts widget into the same layout like a sibling identified // by its name. Default position is just behind the sibling. virtual void insertWidget(QWidget *widget, const QString &siblingName, int offset = 1); virtual QWidget *findWidget(const QString &objectName) const; virtual int nextId() const; // reimp // Used to support some kind of initializePage() in the case the wizard has been set // to QWizard::IndependentPages. If that option has been set, initializePage() would be only // called once. So we provide entering() and leaving() based on currentPageChanged() signal. virtual void entering() {} // called on entering virtual void leaving() {} // called on leaving private: bool m_complete; QString m_titleColor; bool m_needsSettingsButton; PackageManagerCore *m_core; QInstaller::Component *validatorComponent; friend class PackageManagerGui; }; // -- IntroductionPage class INSTALLER_EXPORT IntroductionPage : public PackageManagerPage { Q_OBJECT public: explicit IntroductionPage(PackageManagerCore *core); void setText(const QString &text); int nextId() const; bool validatePage(); void showAll(); void hideAll(); void showMetaInfoUpdate(); void showMaintenanceTools(); void setMaintenanceToolsEnabled(bool enable); public Q_SLOTS: void onCoreNetworkSettingsChanged(); void setMessage(const QString &msg); void onProgressChanged(int progress); void setTotalProgress(int totalProgress); void setErrorMessage(const QString &error); Q_SIGNALS: void packageManagerCoreTypeChanged(); private Q_SLOTS: void setUpdater(bool value); void setUninstaller(bool value); void setPackageManager(bool value); private: void entering(); void leaving(); void showWidgets(bool show); bool validRepositoriesAvailable() const; private: bool m_updatesFetched; bool m_allPackagesFetched; QLabel *m_label; QLabel *m_msgLabel; QLabel *m_errorLabel; QProgressBar *m_progressBar; QRadioButton *m_packageManager; QRadioButton *m_updateComponents; QRadioButton *m_removeAllComponents; #ifdef Q_OS_WIN QWinTaskbarButton *m_taskButton; #endif }; // -- LicenseAgreementPage class INSTALLER_EXPORT LicenseAgreementPage : public PackageManagerPage { Q_OBJECT class ClickForwarder; public: explicit LicenseAgreementPage(PackageManagerCore *core); void entering(); bool isComplete() const; private Q_SLOTS: void openLicenseUrl(const QUrl &url); void currentItemChanged(QListWidgetItem *current); private: void addLicenseItem(const QHash<QString, QPair<QString, QString> > &hash); void updateUi(); private: QTextBrowser *m_textBrowser; QListWidget *m_licenseListWidget; QRadioButton *m_acceptRadioButton; QRadioButton *m_rejectRadioButton; QLabel *m_acceptLabel; QLabel *m_rejectLabel; }; // -- ComponentSelectionPage class INSTALLER_EXPORT ComponentSelectionPage : public PackageManagerPage { Q_OBJECT public: explicit ComponentSelectionPage(PackageManagerCore *core); ~ComponentSelectionPage(); bool isComplete() const; Q_INVOKABLE void selectAll(); Q_INVOKABLE void deselectAll(); Q_INVOKABLE void selectDefault(); Q_INVOKABLE void selectComponent(const QString &id); Q_INVOKABLE void deselectComponent(const QString &id); Q_INVOKABLE void allowCompressedRepositoryInstall(); protected: void entering(); void showEvent(QShowEvent *event); private Q_SLOTS: void setModified(bool modified); private: class Private; Private *d; }; // -- TargetDirectoryPage class INSTALLER_EXPORT TargetDirectoryPage : public PackageManagerPage { Q_OBJECT public: explicit TargetDirectoryPage(PackageManagerCore *core); QString targetDir() const; void setTargetDir(const QString &dirName); void initializePage(); bool validatePage(); bool isComplete() const; protected: void entering(); void leaving(); private Q_SLOTS: void dirRequested(); private: QString targetDirWarning() const; bool askQuestion(const QString &identifier, const QString &message); bool failWithError(const QString &identifier, const QString &message); private: QLineEdit *m_lineEdit; QLabel *m_warningLabel; QTimer m_textChangeTimer; }; // -- StartMenuDirectoryPage class INSTALLER_EXPORT StartMenuDirectoryPage : public PackageManagerPage { Q_OBJECT public: explicit StartMenuDirectoryPage(PackageManagerCore *core); QString startMenuDir() const; void setStartMenuDir(const QString &startMenuDir); protected: void leaving(); private Q_SLOTS: void currentItemChanged(QListWidgetItem* current); private: QString startMenuPath; QLineEdit *m_lineEdit; QListWidget *m_listWidget; }; // -- ReadyForInstallationPage class INSTALLER_EXPORT ReadyForInstallationPage : public PackageManagerPage { Q_OBJECT public: explicit ReadyForInstallationPage(PackageManagerCore *core); protected: void entering(); void leaving(); private: QLabel *m_msgLabel; QTextBrowser* m_taskDetailsBrowser; }; // -- PerformInstallationPage class INSTALLER_EXPORT PerformInstallationPage : public PackageManagerPage { Q_OBJECT public: explicit PerformInstallationPage(PackageManagerCore *core); ~PerformInstallationPage(); bool isAutoSwitching() const; protected: void entering(); void leaving(); bool isInterruptible() const { return true; } public Q_SLOTS: void setTitleMessage(const QString& title); Q_SIGNALS: void setAutomatedPageSwitchEnabled(bool request); private Q_SLOTS: void installationStarted(); void installationFinished(); void uninstallationStarted(); void uninstallationFinished(); void toggleDetailsWereChanged(); private: PerformInstallationForm *m_performInstallationForm; }; // -- FinishedPage class INSTALLER_EXPORT FinishedPage : public PackageManagerPage { Q_OBJECT public: explicit FinishedPage(PackageManagerCore *core); public Q_SLOTS: void handleFinishClicked(); void cleanupChangedConnects(); protected: void entering(); void leaving(); private: QLabel *m_msgLabel; QCheckBox *m_runItCheckBox; QAbstractButton *m_commitButton; }; // -- RestartPage class INSTALLER_EXPORT RestartPage : public PackageManagerPage { Q_OBJECT public: explicit RestartPage(PackageManagerCore *core); virtual int nextId() const; protected: void entering(); void leaving(); Q_SIGNALS: void restart(); }; } //namespace QInstaller #endif // PACKAGEMANAGERGUI_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/packagemanagerpagefactory.cpp����������������������������������������������������0000664�0000000�0000000�00000002737�13253666515�0022477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <packagemanagerpagefactory.h> namespace QInstaller { PackageManagerPageFactory &PackageManagerPageFactory::instance() { static PackageManagerPageFactory factory; return factory; } } ���������������������������������src/libs/installer/packagemanagerpagefactory.h������������������������������������������������������0000664�0000000�0000000�00000004015�13253666515�0022133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PACKAGEMANAGERPAGEFACTORY_H #define PACKAGEMANAGERPAGEFACTORY_H #include "genericfactory.h" #include "qinstallerglobal.h" namespace QInstaller { class PackageManagerCore; class PackageManagerPage; class INSTALLER_EXPORT PackageManagerPageFactory : public GenericFactory<PackageManagerPage, int, PackageManagerCore*> { Q_DISABLE_COPY(PackageManagerPageFactory) public: static PackageManagerPageFactory &instance(); template<typename T> void registerPackageManagerPage(int id) { registerProduct<T>(id); } private: PackageManagerPageFactory() = default; }; } // namespace QInstaller #endif // PACKAGEMANAGERPAGEFACTORY_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/packagemanagerproxyfactory.cpp���������������������������������������������������0000664�0000000�0000000�00000011142�13253666515�0022732�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "packagemanagerproxyfactory.h" #include "packagemanagercore.h" #include "settings.h" #include <QNetworkProxy> namespace QInstaller { PackageManagerProxyFactory::PackageManagerProxyFactory(const PackageManagerCore *const core) : m_core(core) { } PackageManagerProxyFactory *PackageManagerProxyFactory::clone() const { PackageManagerProxyFactory *factory = new PackageManagerProxyFactory(m_core); factory->m_proxyCredentials = m_proxyCredentials; return factory; } struct FindProxyCredential { FindProxyCredential(const QString &host, int port) : host(host), port(port) {} bool operator()(const ProxyCredential &c) { return c.host == host && c.port == port; } private: QString host; int port; }; QList<QNetworkProxy> PackageManagerProxyFactory::queryProxy(const QNetworkProxyQuery &query) { const Settings &settings = m_core->settings(); QList<QNetworkProxy> list; if (settings.proxyType() == Settings::SystemProxy) { QList<QNetworkProxy> systemProxies = systemProxyForQuery(query); auto proxyIter = systemProxies.begin(); for (; proxyIter != systemProxies.end(); ++proxyIter) { QNetworkProxy &proxy = *proxyIter; auto p = std::find_if(m_proxyCredentials.constBegin(), m_proxyCredentials.constEnd(), FindProxyCredential(proxy.hostName(), proxy.port())); if (p != m_proxyCredentials.constEnd()) { proxy.setUser(p->user); proxy.setPassword(p->password); } } return systemProxies; } if ((settings.proxyType() == Settings::NoProxy)) return list << QNetworkProxy(QNetworkProxy::NoProxy); if (query.queryType() == QNetworkProxyQuery::UrlRequest) { QNetworkProxy proxy; if (query.url().scheme() == QLatin1String("ftp")) { proxy = settings.ftpProxy(); } else if (query.url().scheme() == QLatin1String("http") || query.url().scheme() == QLatin1String("https")) { proxy = settings.httpProxy(); } auto p = std::find_if(m_proxyCredentials.constBegin(), m_proxyCredentials.constEnd(), FindProxyCredential(proxy.hostName(), proxy.port())); if (p != m_proxyCredentials.constEnd()) { proxy.setUser(p->user); proxy.setPassword(p->password); } return list << proxy; } return list << QNetworkProxy(QNetworkProxy::DefaultProxy); } void PackageManagerProxyFactory::setProxyCredentials(const QNetworkProxy &proxy, const QString &user, const QString &password) { auto p = std::find_if(m_proxyCredentials.begin(), m_proxyCredentials.end(), FindProxyCredential(proxy.hostName(), proxy.port())); if (p == m_proxyCredentials.end()) { ProxyCredential proxyCredential; proxyCredential.host = proxy.hostName(); proxyCredential.port = proxy.port(); proxyCredential.user = user; proxyCredential.password = password; m_proxyCredentials.append(proxyCredential); } else { p->user = user; p->password = password; } } } // QInstaller ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/packagemanagerproxyfactory.h�����������������������������������������������������0000664�0000000�0000000�00000004167�13253666515�0022410�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PACKAGEMANAGERPROXYFACTORY_H #define PACKAGEMANAGERPROXYFACTORY_H #include "filedownloaderfactory.h" namespace QInstaller { class PackageManagerCore; struct ProxyCredential { QString host; QString user; QString password; int port; }; class PackageManagerProxyFactory : public KDUpdater::FileDownloaderProxyFactory { public: explicit PackageManagerProxyFactory(const PackageManagerCore *const core); PackageManagerProxyFactory *clone() const; QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery()); void setProxyCredentials(const QNetworkProxy &proxy, const QString &user, const QString &password); private: QList<ProxyCredential> m_proxyCredentials; const PackageManagerCore *const m_core; }; } // QInstaller #endif // PACKAGEMANAGERPROXYFACTORY_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/packagesource.cpp����������������������������������������������������������������0000664�0000000�0000000�00000006330�13253666515�0020131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "packagesource.h" namespace QInstaller { /*! \inmodule QtInstallerFramework \class QInstaller::PackageSource \brief The PackageSource class specifies a single package source. An package source represents a link to an repository that contains packages applicable by the installer or package maintenance application. This structure describes a single package source in terms of url and priority. While different repositories can host the same packages, packages coming from a higher priority source take precedence over lower priority packages during applicable package computation. */ /*! \fn PackageSource::PackageSource() Constructs an empty package source info object. The object's priority is set to -1. The url is initialized using a \l{default-constructed value}. */ /*! \fn PackageSource::PackageSource(const QUrl &u, int p) Constructs an package source info object. The object's url is set to \a u, while the priority is set to \a p. */ /*! \variable PackageSource::url \brief The URL of the package source. */ /*! \variable PackageSource::priority \brief The priority of the package source. */ /*! Returns the hash value for the \a key, using \a seed to seed the calculation. */ uint qHash(const PackageSource &key, uint seed) { return qHash(key.url, seed) ^ key.priority; } /*! Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false. */ bool operator==(const PackageSource &lhs, const PackageSource &rhs) { return lhs.url == rhs.url && lhs.priority == rhs.priority; } } // namespace QInstaller ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/packagesource.h������������������������������������������������������������������0000664�0000000�0000000�00000004170�13253666515�0017576�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PACKAGESOURCE_H #define PACKAGESOURCE_H #include "installer_global.h" #include <QUrl> namespace QInstaller { struct INSTALLER_EXPORT PackageSource { PackageSource() : priority(-1) {} PackageSource(const QUrl &u, int p) : url(u) , priority(p) {} QUrl url; int priority; }; INSTALLER_EXPORT uint qHash(const PackageSource &key, uint seed); INSTALLER_EXPORT bool operator==(const PackageSource &lhs, const PackageSource &rhs); } // namespace QInstaller #endif // PACKAGESOURCE_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/performinstallationform.cpp������������������������������������������������������0000664�0000000�0000000�00000021473�13253666515�0022302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "performinstallationform.h" #include "lazyplaintextedit.h" #include "progresscoordinator.h" #include <QApplication> #include <QLabel> #include <QProgressBar> #include <QPushButton> #include <QScrollBar> #include <QVBoxLayout> #include <QtCore/QTimer> #ifdef Q_OS_WIN # include <QWinTaskbarButton> # include <QWinTaskbarProgress> #endif using namespace QInstaller; // -- PerformInstallationForm /*! \class QInstaller::PerformInstallationForm \inmodule QtInstallerFramework \brief The PerformInstallationForm class shows progress information about the installation state. A progress bar indicates the progress of the installation, update, or uninstallation. The page contains a button for showing or hiding detailed information about the progress in an \e {details browser}. The text on the button changes depending on whether the details browser is currently shown or hidden. */ /*! \fn PerformInstallationForm::showDetailsChanged() This signal is emitted when the end users select the details button to show or hide progress details. */ /*! Constructs the perform installation UI with \a parent as parent. */ PerformInstallationForm::PerformInstallationForm(QObject *parent) : QObject(parent) , m_progressBar(0) , m_progressLabel(0) , m_detailsButton(0) , m_detailsBrowser(0) , m_updateTimer(0) { #ifdef Q_OS_WIN if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { m_taskButton = new QWinTaskbarButton(this); m_taskButton->progress()->setVisible(true); } else { m_taskButton = 0; } #endif } /*! Sets up the perform installation UI specified by \a widget. */ void PerformInstallationForm::setupUi(QWidget *widget) { QVBoxLayout *baseLayout = new QVBoxLayout(widget); baseLayout->setObjectName(QLatin1String("BaseLayout")); QVBoxLayout *topLayout = new QVBoxLayout(); topLayout->setObjectName(QLatin1String("TopLayout")); m_progressBar = new QProgressBar(widget); m_progressBar->setRange(1, 100); m_progressBar->setObjectName(QLatin1String("ProgressBar")); topLayout->addWidget(m_progressBar); m_progressLabel = new QLabel(widget); m_progressLabel->setObjectName(QLatin1String("ProgressLabel")); m_progressLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); topLayout->addWidget(m_progressLabel); m_downloadStatus = new QLabel(widget); m_downloadStatus->setObjectName(QLatin1String("DownloadStatus")); m_downloadStatus->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); topLayout->addWidget(m_downloadStatus); connect(ProgressCoordinator::instance(), &ProgressCoordinator::downloadStatusChanged, this, &PerformInstallationForm::onDownloadStatusChanged); m_detailsButton = new QPushButton(tr("&Show Details"), widget); m_detailsButton->setObjectName(QLatin1String("DetailsButton")); m_detailsButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); connect(m_detailsButton, &QAbstractButton::clicked, this, &PerformInstallationForm::toggleDetails); topLayout->addWidget(m_detailsButton); QVBoxLayout *bottomLayout = new QVBoxLayout(); bottomLayout->setObjectName(QLatin1String("BottomLayout")); bottomLayout->addStretch(); m_detailsBrowser = new LazyPlainTextEdit(widget); m_detailsBrowser->setReadOnly(true); m_detailsBrowser->setWordWrapMode(QTextOption::NoWrap); m_detailsBrowser->setObjectName(QLatin1String("DetailsBrowser")); m_detailsBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); bottomLayout->addWidget(m_detailsBrowser); bottomLayout->setStretch(1, 10); baseLayout->addLayout(topLayout); baseLayout->addLayout(bottomLayout); m_updateTimer = new QTimer(widget); connect(m_updateTimer, &QTimer::timeout, this, &PerformInstallationForm::updateProgress); //updateProgress includes label m_updateTimer->setInterval(30); m_progressBar->setRange(0, 100); } /*! Shows the details button if \a visible is \c true. */ void PerformInstallationForm::setDetailsWidgetVisible(bool visible) { m_detailsButton->setVisible(visible); } /*! Displays \a details about progress of the installation in the details browser. */ void PerformInstallationForm::appendProgressDetails(const QString &details) { m_detailsBrowser->append(details); } /*! Updates the progress of the installation on the progress bar. */ void PerformInstallationForm::updateProgress() { QInstaller::ProgressCoordinator *progressCoordninator = QInstaller::ProgressCoordinator::instance(); const int progressPercentage = progressCoordninator->progressInPercentage(); m_progressBar->setValue(progressPercentage); #ifdef Q_OS_WIN if (m_taskButton) { if (!m_taskButton->window() && QApplication::activeWindow()) m_taskButton->setWindow(QApplication::activeWindow()->windowHandle()); m_taskButton->progress()->setValue(progressPercentage); } #endif static QString lastLabelText; if (lastLabelText == progressCoordninator->labelText()) return; lastLabelText = progressCoordninator->labelText(); m_progressLabel->setText(m_progressLabel->fontMetrics().elidedText(progressCoordninator->labelText(), Qt::ElideRight, m_progressLabel->width())); } /*! Sets the text of the details button to \uicontrol {Hide Details} or \uicontrol {Show Details} depending on whether the details are currently shown or hidden. Emits the showDetailsChanged() signal. */ void PerformInstallationForm::toggleDetails() { const bool willShow = !isShowingDetails(); m_detailsButton->setText(willShow ? tr("&Hide Details") : tr("&Show Details")); m_detailsBrowser->setVisible(willShow); emit showDetailsChanged(); } /*! Clears the contents of the details browser. */ void PerformInstallationForm::clearDetailsBrowser() { m_detailsBrowser->clear(); } /*! Enables the details button with the text \uicontrol {Show Details} and hides the details browser. */ void PerformInstallationForm::enableDetails() { m_detailsButton->setEnabled(true); m_detailsButton->setText(tr("&Show Details")); m_detailsBrowser->setVisible(false); } /*! Starts the update progress timer. */ void PerformInstallationForm::startUpdateProgress() { m_updateTimer->start(); updateProgress(); } /*! Stops the update progress timer. */ void PerformInstallationForm::stopUpdateProgress() { m_updateTimer->stop(); updateProgress(); } /*! Enables the details button if \a enable is \c true. */ void PerformInstallationForm::setDetailsButtonEnabled(bool enable) { m_detailsButton->setEnabled(enable); } /*! Scrolls to the bottom of the details browser. */ void PerformInstallationForm::scrollDetailsToTheEnd() { m_detailsBrowser->updateCursor(LazyPlainTextEdit::TextCursorPosition::ForceEnd); } /*! Returns \c true if the details browser is visible. */ bool PerformInstallationForm::isShowingDetails() const { return m_detailsBrowser->isVisible(); } /*! Changes the label text according to the changes in the download status specified by \a status. */ void PerformInstallationForm::onDownloadStatusChanged(const QString &status) { m_downloadStatus->setText(m_downloadStatus->fontMetrics().elidedText(status, Qt::ElideRight, m_downloadStatus->width())); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/performinstallationform.h��������������������������������������������������������0000664�0000000�0000000�00000005112�13253666515�0021737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PERFORMINSTALLATIONFORM_H #define PERFORMINSTALLATIONFORM_H #include <QObject> QT_BEGIN_NAMESPACE class QLabel; class QProgressBar; class QPushButton; class QTimer; class QWidget; class QWinTaskbarButton; QT_END_NAMESPACE class LazyPlainTextEdit; namespace QInstaller { class PerformInstallationForm : public QObject { Q_OBJECT public: explicit PerformInstallationForm(QObject *parent); void setupUi(QWidget *widget); void setDetailsWidgetVisible(bool visible); void enableDetails(); void startUpdateProgress(); void stopUpdateProgress(); void setDetailsButtonEnabled(bool enable); void scrollDetailsToTheEnd(); bool isShowingDetails() const; signals: void showDetailsChanged(); public slots: void appendProgressDetails(const QString &details); void updateProgress(); void toggleDetails(); void clearDetailsBrowser(); void onDownloadStatusChanged(const QString &status); private: QProgressBar *m_progressBar; QLabel *m_progressLabel; QLabel *m_downloadStatus; QPushButton *m_detailsButton; LazyPlainTextEdit *m_detailsBrowser; QTimer *m_updateTimer; #ifdef Q_OS_WIN QWinTaskbarButton *m_taskButton; #endif }; } // namespace QInstaller #endif // PERFORMINSTALLATIONFORM_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/permissionsettings.cpp�����������������������������������������������������������0000664�0000000�0000000�00000003104�13253666515�0021262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "permissionsettings.h" #include <QFile> using namespace QInstaller; PermissionSettings::~PermissionSettings() { if (!fileName().isEmpty()) { sync(); QFile file(fileName()); file.setPermissions(file.permissions() | QFile::ReadGroup | QFile::ReadOther); } } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/permissionsettings.h�������������������������������������������������������������0000664�0000000�0000000�00000004354�13253666515�0020737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PERMISSIONSETTINGS_H #define PERMISSIONSETTINGS_H #include <QSettings> namespace QInstaller { class PermissionSettings : public QSettings { public: explicit PermissionSettings(const QString &organization, const QString &application = QString(), QObject *parent = 0) : QSettings(organization, application, parent) {} PermissionSettings(Scope scope, const QString &organization, const QString &application = QString(), QObject *parent = 0) : QSettings(scope, organization, application, parent) {} PermissionSettings(Format format, Scope scope, const QString &organization, const QString &application = QString(), QObject *parent = 0) : QSettings(format, scope, organization, application, parent) {} PermissionSettings(const QString &fileName, Format format, QObject *parent = 0) : QSettings(fileName, format, parent) {} ~PermissionSettings(); }; } #endif // PERMISSIONSETTINGS_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/productkeycheck.cpp��������������������������������������������������������������0000664�0000000�0000000�00000005245�13253666515�0020510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "productkeycheck.h" #include "packagemanagercore.h" class ProductKeyCheckPrivate { }; ProductKeyCheck::ProductKeyCheck() : d(new ProductKeyCheckPrivate()) { } ProductKeyCheck::~ProductKeyCheck() { delete d; } ProductKeyCheck *ProductKeyCheck::instance() { static ProductKeyCheck instance; return &instance; } void ProductKeyCheck::init(QInstaller::PackageManagerCore *core) { Q_UNUSED(core) } bool ProductKeyCheck::hasValidKey() { return true; } bool ProductKeyCheck::applyKey(const QStringList &/*arguments*/) { return true; } QString ProductKeyCheck::lastErrorString() { return QString(); } QString ProductKeyCheck::maintainanceToolDetailErrorNotice() { return QString(); } // to filter none valid licenses bool ProductKeyCheck::isValidLicenseTextFile(const QString &/*fileName*/) { return true; } bool ProductKeyCheck::isValidRepository(const QInstaller::Repository &repository) const { Q_UNUSED(repository) return true; } void ProductKeyCheck::addPackagesFromXml(const QString &xmlPath) { Q_UNUSED(xmlPath) } bool ProductKeyCheck::isValidPackage(const QString &packageName) const { Q_UNUSED(packageName) return true; } QList<int> ProductKeyCheck::registeredPages() const { return QList<int>(); } bool ProductKeyCheck::hasValidLicense() const { return true; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/productkeycheck.h����������������������������������������������������������������0000664�0000000�0000000�00000004602�13253666515�0020151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PRODUCTKEYCHECK_H #define PRODUCTKEYCHECK_H #include "installer_global.h" #include <QString> namespace QInstaller { class PackageManagerCore; class Repository; } // QInstaller class INSTALLER_EXPORT ProductKeyCheck { Q_DISABLE_COPY(ProductKeyCheck) public: ~ProductKeyCheck(); static ProductKeyCheck *instance(); void init(QInstaller::PackageManagerCore *core); // was validLicense bool hasValidKey(); QString lastErrorString(); QString maintainanceToolDetailErrorNotice(); bool applyKey(const QStringList &arguments); // to filter none valid licenses bool isValidLicenseTextFile(const QString &fileName); // to filter repositories not matching the license bool isValidRepository(const QInstaller::Repository &repository) const; void addPackagesFromXml(const QString &xmlPath); bool isValidPackage(const QString &packageName) const; QList<int> registeredPages() const; bool hasValidLicense() const; private: ProductKeyCheck(); class ProductKeyCheckPrivate *const d; }; #endif // PRODUCTKEYCHECK_H ������������������������������������������������������������������������������������������������������������������������������src/libs/installer/progresscoordinator.cpp����������������������������������������������������������0000664�0000000�0000000�00000024705�13253666515�0021433�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "progresscoordinator.h" #include <QtCore/QCoreApplication> #include <QtCore/QDebug> using namespace QInstaller; QT_BEGIN_NAMESPACE uint qHash(QPointer<QObject> key) { return qHash(key.data()); } QT_END_NAMESPACE ProgressCoordinator::ProgressCoordinator(QObject *parent) : QObject(parent) , m_currentCompletePercentage(0) , m_currentBasePercentage(0) , m_manualAddedPercentage(0) , m_reservedPercentage(0) , m_undoMode(false) , m_reachedPercentageBeforeUndo(0) { // it has to be in the main thread to be able refresh the ui with processEvents Q_ASSERT(thread() == qApp->thread()); } ProgressCoordinator::~ProgressCoordinator() { } ProgressCoordinator *ProgressCoordinator::instance() { static ProgressCoordinator *instance = 0; if (instance == 0) instance = new ProgressCoordinator(qApp); return instance; } void ProgressCoordinator::reset() { disconnectAllSenders(); m_installationLabelText.clear(); m_currentCompletePercentage = 0; m_currentBasePercentage = 0; m_manualAddedPercentage = 0; m_reservedPercentage = 0; m_undoMode = false; m_reachedPercentageBeforeUndo = 0; emit detailTextResetNeeded(); } void ProgressCoordinator::registerPartProgress(QObject *sender, const char *signal, double partProgressSize) { Q_ASSERT(sender); Q_ASSERT(QString::fromLatin1(signal).contains(QLatin1String("(double)"))); Q_ASSERT(partProgressSize <= 1); m_senderPartProgressSizeHash.insert(sender, partProgressSize); bool isConnected = connect(sender, signal, this, SLOT(partProgressChanged(double))); Q_UNUSED(isConnected); Q_ASSERT(isConnected); } /*! This slot gets the progress changed signals from different tasks. The values 0 and 1 are handled as special values. 0 - is just ignored, so you can use a timer which gives the progress, e.g. like a downloader does. 1 - means the task is finished, even if there comes another 1 from that task, so it will be ignored. */ void ProgressCoordinator::partProgressChanged(double fraction) { if (fraction < 0 || fraction > 1) { qWarning() << "The fraction is outside from possible value:" << fraction; return; } // no fraction no change if (fraction == 0) return; // ignore senders sending 100% multiple times if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(sender()) && m_senderPendingCalculatedPercentageHash.value(sender()) == 0) { return; } double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0); if (partProgressSize == 0) { qWarning() << "It seems that this sender was not registered in the right way:" << sender(); return; } if (m_undoMode) { //qDebug() << "fraction:" << fraction; double maxSize = m_reachedPercentageBeforeUndo * partProgressSize; double pendingCalculatedPartPercentage = maxSize * fraction; // allPendingCalculatedPartPercentages has negative values double newCurrentCompletePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender()); //we can't check this here, because some round issues can make it little bit under 0 or over 100 //Q_ASSERT(newCurrentCompletePercentage >= 0); //Q_ASSERT(newCurrentCompletePercentage <= 100); if (newCurrentCompletePercentage < 0) { qDebug() << newCurrentCompletePercentage << "is smaller than 0 - this should not happen more than once"; newCurrentCompletePercentage = 0; } if (newCurrentCompletePercentage > 100) { qDebug() << newCurrentCompletePercentage << "is bigger than 100 - this should not happen more than once"; newCurrentCompletePercentage = 100; } // In undo mode, the progress has to go backward, new has to be smaller than current if (qRound(m_currentCompletePercentage) < qRound(newCurrentCompletePercentage)) qDebug("Something is wrong with the calculation of the progress."); m_currentCompletePercentage = newCurrentCompletePercentage; if (fraction == 1) { m_currentBasePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage; m_senderPendingCalculatedPercentageHash.insert(sender(), 0); } else { m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage); } } else { //if (m_undoMode) int availablePercentagePoints = 100 - m_manualAddedPercentage - m_reservedPercentage; double pendingCalculatedPartPercentage = availablePercentagePoints * partProgressSize * fraction; //double checkValue = allPendingCalculatedPartPercentages(sender()); double newCurrentCompletePercentage = m_manualAddedPercentage + m_currentBasePercentage + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender()); //we can't check this here, because some round issues can make it little bit under 0 or over 100 //Q_ASSERT(newCurrentCompletePercentage >= 0); //Q_ASSERT(newCurrentCompletePercentage <= 100); if (newCurrentCompletePercentage < 0) { qDebug() << newCurrentCompletePercentage << "is smaller than 0 - this should not happen more than once"; newCurrentCompletePercentage = 0; } if (newCurrentCompletePercentage > 100) { qDebug() << newCurrentCompletePercentage << "is bigger than 100 - this should not happen more than once"; newCurrentCompletePercentage = 100; } // In normal mode, the progress has to go forward, new has to be larger than current if (qRound(m_currentCompletePercentage) > qRound(newCurrentCompletePercentage)) qDebug("Something is wrong with the calculation of the progress."); m_currentCompletePercentage = newCurrentCompletePercentage; if (fraction == 1) { m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage; m_senderPendingCalculatedPercentageHash.insert(sender(), 0); } else { m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage); } } //if (m_undoMode) } /*! Contains the installation progress percentage. */ int ProgressCoordinator::progressInPercentage() const { int currentValue = qRound(m_currentCompletePercentage); Q_ASSERT( currentValue <= 100); Q_ASSERT( currentValue >= 0); return currentValue; } void ProgressCoordinator::disconnectAllSenders() { foreach (QPointer<QObject> sender, m_senderPartProgressSizeHash.keys()) { if (!sender.isNull()) { bool isDisconnected = sender->disconnect(this); Q_UNUSED(isDisconnected); Q_ASSERT(isDisconnected); } } m_senderPartProgressSizeHash.clear(); m_senderPendingCalculatedPercentageHash.clear(); } void ProgressCoordinator::setUndoMode() { Q_ASSERT(!m_undoMode); m_undoMode = true; disconnectAllSenders(); m_reachedPercentageBeforeUndo = progressInPercentage(); m_currentBasePercentage = m_reachedPercentageBeforeUndo; } void ProgressCoordinator::addManualPercentagePoints(int value) { m_manualAddedPercentage = m_manualAddedPercentage + value; if (m_undoMode) { //we don't do other things in the undomode, maybe later if the last percentage point comes to early return; } m_currentCompletePercentage = m_currentCompletePercentage + value; if (m_currentCompletePercentage > 100.0) m_currentCompletePercentage = 100.0; qApp->processEvents(); //makes the result available in the ui } void ProgressCoordinator::addReservePercentagePoints(int value) { m_reservedPercentage = m_reservedPercentage + value; } void ProgressCoordinator::setLabelText(const QString &text) { if (m_installationLabelText == text) return; m_installationLabelText = text; } /*! Contains the installation progress label text. */ QString ProgressCoordinator::labelText() const { return m_installationLabelText; } void ProgressCoordinator::emitDetailTextChanged(const QString &text) { emit detailTextChanged(text); } void ProgressCoordinator::emitLabelAndDetailTextChanged(const QString &text) { emit detailTextChanged(text); m_installationLabelText = QString(text).remove(QLatin1String("\n")); qApp->processEvents(); //makes the result available in the ui } double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *excludeKeyObject) { double result = 0; QHash<QPointer<QObject>, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin(); while (it != m_senderPendingCalculatedPercentageHash.end()) { if (it.key() != excludeKeyObject) result += it.value(); it++; } return result; } void ProgressCoordinator::emitDownloadStatus(const QString &status) { emit downloadStatusChanged(status); } �����������������������������������������������������������src/libs/installer/progresscoordinator.h������������������������������������������������������������0000664�0000000�0000000�00000005777�13253666515�0021110�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PROGRESSCOORDINATOR_H #define PROGRESSCOORDINATOR_H #include "installer_global.h" #include <QtCore/QHash> #include <QtCore/QObject> #include <QtCore/QPointer> namespace QInstaller { class INSTALLER_EXPORT ProgressCoordinator : public QObject { Q_OBJECT public: static ProgressCoordinator *instance(); ~ProgressCoordinator(); void registerPartProgress(QObject *sender, const char *signal, double partProgressSize); public slots: void reset(); void setUndoMode(); QString labelText() const; void setLabelText(const QString &text); int progressInPercentage() const; void partProgressChanged(double fraction); void addManualPercentagePoints(int value); void addReservePercentagePoints(int value); void emitDetailTextChanged(const QString &text); void emitLabelAndDetailTextChanged(const QString &text); void emitDownloadStatus(const QString &status); signals: void detailTextChanged(const QString &text); void detailTextResetNeeded(); void downloadStatusChanged(const QString &status); protected: explicit ProgressCoordinator(QObject *parent); private: double allPendingCalculatedPartPercentages(QObject *excludeKeyObject = 0); void disconnectAllSenders(); private: QHash<QPointer<QObject>, double> m_senderPendingCalculatedPercentageHash; QHash<QPointer<QObject>, double> m_senderPartProgressSizeHash; QString m_installationLabelText; double m_currentCompletePercentage; double m_currentBasePercentage; int m_manualAddedPercentage; int m_reservedPercentage; bool m_undoMode; double m_reachedPercentageBeforeUndo; }; } //namespace QInstaller #endif //PROGRESSCOORDINATOR_H �src/libs/installer/protocol.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000006621�13253666515�0017161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "protocol.h" #include <QIODevice> namespace QInstaller { typedef qint32 PackageSize; /*! Write a packet containing \a command and \a data to \a device. \note Both client and server need to have the same endianness. */ void sendPacket(QIODevice *device, const QByteArray &command, const QByteArray &data) { // use aliasing for writing payload size into bytes char payloadBytes[sizeof(PackageSize)]; PackageSize *payloadSize = reinterpret_cast<PackageSize*>(&payloadBytes); *payloadSize = command.size() + sizeof(char) + data.size(); QByteArray packet; packet.reserve(sizeof(PackageSize) + *payloadSize); packet.append(payloadBytes, sizeof(PackageSize)); packet.append(command); packet.append('\0'); packet.append(data); forever { const int bytesWritten = device->write(packet); Q_ASSERT(bytesWritten >= 0); if (bytesWritten == packet.size()) break; packet.remove(0, bytesWritten); } } /*! Reads a packet from \a device, and stores its content into \a command and \a data. Returns \c false if the packet in the device buffer is yet incomplete, \c true otherwise. \note Both client and server need to have the same endianness. */ bool receivePacket(QIODevice *device, QByteArray *command, QByteArray *data) { if (device->bytesAvailable() < static_cast<qint64>(sizeof(PackageSize))) return false; // read payload size char payloadBytes[sizeof(PackageSize)]; PackageSize *payloadSize = reinterpret_cast<PackageSize*>(&payloadBytes); device->read(payloadBytes, sizeof(PackageSize)); // not enough data yet? back off ... if (device->bytesAvailable() < *payloadSize) { for (int i = sizeof(PackageSize) - 1; i >= 0; --i) device->ungetChar(payloadBytes[i]); return false; } const QByteArray payload = device->read(*payloadSize); int separator = payload.indexOf('\0'); *command = payload.left(separator); *data = payload.right(payload.size() - separator - 1); return true; } } // namespace QInstaller ���������������������������������������������������������������������������������������������������������������src/libs/installer/protocol.h�����������������������������������������������������������������������0000664�0000000�0000000�00000021226�13253666515�0016624�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PROTOCOL_H #define PROTOCOL_H #include "installer_global.h" QT_FORWARD_DECLARE_CLASS(QIODevice) namespace QInstaller { namespace Protocol { enum struct Mode { Debug, Production }; const char ModeDebug[] = "DEBUG"; const char ModeProduction[] = "PRODUCTION"; enum struct StartAs { User, SuperUser }; const char DefaultSocket[] = "ifw_srv"; const char DefaultAuthorizationKey[] = "DefaultAuthorizationKey"; const char Create[] = "Create"; const char Destroy[] = "Destroy"; const char Shutdown[] = "Shutdown"; const char Authorize[] = "Authorize"; const char Reply[] = "Reply"; // QProcessWrapper const char QProcess[] = "QProcess"; const char QProcessCloseWriteChannel[] = "QProcess::closeWriteChannel"; const char QProcessExitCode[] = "QProcess::exitCode"; const char QProcessExitStatus[] = "QProcess::exitStatus"; const char QProcessKill[] = "QProcess::kill"; const char QProcessReadAll[] = "QProcess::readAll"; const char QProcessReadAllStandardOutput[] = "QProcess::readAllStandardOutput"; const char QProcessReadAllStandardError[] = "QProcess::readAllStandardError"; const char QProcessStartDetached[] = "QProcess::startDetached"; const char QProcessSetWorkingDirectory[] = "QProcess::setWorkingDirectory"; const char QProcessSetEnvironment[] = "QProcess::setEnvironment"; const char QProcessEnvironment[] = "QProcess::environment"; const char QProcessStart3Arg[] = "QProcess::start3"; const char QProcessStart2Arg[] = "QProcess::start2"; const char QProcessState[] = "QProcess::state"; const char QProcessTerminate[] = "QProcess::terminate"; const char QProcessWaitForFinished[] = "QProcess::waitForFinished"; const char QProcessWaitForStarted[] = "QProcess::waitForStarted"; const char QProcessWorkingDirectory[] = "QProcess::workingDirectory"; const char QProcessErrorString[] = "QProcess::errorString"; const char QProcessReadChannel[] = "QProcess::readChannel"; const char QProcessSetReadChannel[] = "QProcess::setReadChannel"; const char QProcessWrite[] = "QProcess::write"; const char QProcessProcessChannelMode[] = "QProcess::processChannelMode"; const char QProcessSetProcessChannelMode[] = "QProcess::setProcessChannelMode"; const char QProcessSetNativeArguments[] = "QProcess::setNativeArguments"; const char GetQProcessSignals[] = "GetQProcessSignals"; const char QProcessSignalBytesWritten[] = "QProcess::bytesWritten"; const char QProcessSignalAboutToClose[] = "QProcess::aboutToClose"; const char QProcessSignalReadChannelFinished[] = "QProcess::readChannelFinished"; const char QProcessSignalError[] = "QProcess::error"; const char QProcessSignalReadyReadStandardOutput[] = "QProcess::readyReadStandardOutput"; const char QProcessSignalReadyReadStandardError[] = "QProcess::readyReadStandardError"; const char QProcessSignalStarted[] = "QProcess::started"; const char QProcessSignalReadyRead[] = "QProcess::readyRead"; const char QProcessSignalStateChanged[] = "QProcess::stateChanged"; const char QProcessSignalFinished[] = "QProcess::finished"; // QSettingsWrapper const char QSettings[] = "QSettings"; const char QSettingsAllKeys[] = "QSettings::allKeys"; const char QSettingsBeginGroup[] = "QSettings::beginGroup"; const char QSettingsBeginWriteArray[] = "QSettings::beginWriteArray"; const char QSettingsBeginReadArray[] = "QSettings::beginReadArray"; const char QSettingsChildGroups[] = "QSettings::childGroups"; const char QSettingsChildKeys[] = "QSettings::childKeys"; const char QSettingsClear[] = "QSettings::clear"; const char QSettingsContains[] = "QSettings::contains"; const char QSettingsEndArray[] = "QSettings::endArray"; const char QSettingsEndGroup[] = "QSettings::endGroup"; const char QSettingsFallbacksEnabled[] = "QSettings::fallbacksEnabled"; const char QSettingsFileName[] = "QSettings::fileName"; const char QSettingsGroup[] = "QSettings::group"; const char QSettingsIsWritable[] = "QSettings::isWritable"; const char QSettingsRemove[] = "QSettings::remove"; const char QSettingsSetArrayIndex[] = "QSettings::setArrayIndex"; const char QSettingsSetFallbacksEnabled[] = "QSettings::setFallbacksEnabled"; const char QSettingsStatus[] = "QSettings::status"; const char QSettingsSync[] = "QSettings::sync"; const char QSettingsSetValue[] = "QSettings::setValue"; const char QSettingsValue[] = "QSettings::value"; const char QSettingsOrganizationName[] = "QSettings::organizationName"; const char QSettingsApplicationName[] = "QSettings::applicationName"; // RemoteFileEngine const char QAbstractFileEngine[] = "QAbstractFileEngine"; const char QAbstractFileEngineAtEnd[] = "QAbstractFileEngine::atEnd"; const char QAbstractFileEngineCaseSensitive[] = "QAbstractFileEngine::caseSensitive"; const char QAbstractFileEngineClose[] = "QAbstractFileEngine::close"; const char QAbstractFileEngineCopy[] = "QAbstractFileEngine::copy"; const char QAbstractFileEngineEntryList[] = "QAbstractFileEngine::entryList"; const char QAbstractFileEngineError[] = "QAbstractFileEngine::error"; const char QAbstractFileEngineErrorString[] = "QAbstractFileEngine::errorString"; const char QAbstractFileEngineFileFlags[] = "QAbstractFileEngine::fileFlags"; const char QAbstractFileEngineFileName[] = "QAbstractFileEngine::fileName"; const char QAbstractFileEngineFlush[] = "QAbstractFileEngine::flush"; const char QAbstractFileEngineHandle[] = "QAbstractFileEngine::handle"; const char QAbstractFileEngineIsRelativePath[] = "QAbstractFileEngine::isRelativePath"; const char QAbstractFileEngineIsSequential[] = "QAbstractFileEngine::isSequential"; const char QAbstractFileEngineLink[] = "QAbstractFileEngine::link"; const char QAbstractFileEngineMkdir[] = "QAbstractFileEngine::mkdir"; const char QAbstractFileEngineOpen[] = "QAbstractFileEngine::open"; const char QAbstractFileEngineOwner[] = "QAbstractFileEngine::owner"; const char QAbstractFileEngineOwnerId[] = "QAbstractFileEngine::ownerId"; const char QAbstractFileEnginePos[] = "QAbstractFileEngine::pos"; const char QAbstractFileEngineRead[] = "QAbstractFileEngine::read"; const char QAbstractFileEngineReadLine[] = "QAbstractFileEngine::readLine"; const char QAbstractFileEngineRemove[] = "QAbstractFileEngine::remove"; const char QAbstractFileEngineRename[] = "QAbstractFileEngine::rename"; const char QAbstractFileEngineRmdir[] = "QAbstractFileEngine::rmdir"; const char QAbstractFileEngineSeek[] = "QAbstractFileEngine::seek"; const char QAbstractFileEngineSetFileName[] = "QAbstractFileEngine::setFileName"; const char QAbstractFileEngineSetPermissions[] = "QAbstractFileEngine::setPermissions"; const char QAbstractFileEngineSetSize[] = "QAbstractFileEngine::setSize"; const char QAbstractFileEngineSize[] = "QAbstractFileEngine::size"; const char QAbstractFileEngineSupportsExtension[] = "QAbstractFileEngine::supportsExtension"; const char QAbstractFileEngineExtension[] = "QAbstractFileEngine::extension"; const char QAbstractFileEngineWrite[] = "QAbstractFileEngine::write"; const char QAbstractFileEngineSyncToDisk[] = "QAbstractFileEngine::syncToDisk"; const char QAbstractFileEngineRenameOverwrite[] = "QAbstractFileEngine::renameOverwrite"; const char QAbstractFileEngineFileTime[] = "QAbstractFileEngine::fileTime"; } // namespace Protocol void INSTALLER_EXPORT sendPacket(QIODevice *device, const QByteArray &command, const QByteArray &data); bool INSTALLER_EXPORT receivePacket(QIODevice *device, QByteArray *command, QByteArray *data); } // namespace QInstaller #endif // PROTOCOL_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/proxycredentialsdialog.cpp�������������������������������������������������������0000664�0000000�0000000�00000004717�13253666515�0022103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "proxycredentialsdialog.h" #include "ui_proxycredentialsdialog.h" #include <QNetworkProxy> namespace QInstaller { ProxyCredentialsDialog::ProxyCredentialsDialog(const QNetworkProxy &proxy, QWidget *parent) : QDialog(parent), ui(new Ui::ProxyCredentialsDialog) { setWindowTitle(tr("Proxy Credentials")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); ui->setupUi(this); setUserName(proxy.user()); setPassword(proxy.password()); const QString proxyString = QString::fromLatin1("%1:%2").arg(proxy.hostName()).arg(proxy.port()); ui->infotext->setText(ui->infotext->text().arg(proxyString)); } ProxyCredentialsDialog::~ProxyCredentialsDialog() { delete ui; } QString ProxyCredentialsDialog::userName() const { return ui->usernameLineEdit->text(); } void ProxyCredentialsDialog::setUserName(const QString &username) { ui->usernameLineEdit->setText(username); } QString ProxyCredentialsDialog::password() const { return ui->passwordLineEdit->text(); } void ProxyCredentialsDialog::setPassword(const QString &passwd) { ui->passwordLineEdit->setText(passwd); } } // namespace QInstaller �������������������������������������������������src/libs/installer/proxycredentialsdialog.h���������������������������������������������������������0000664�0000000�0000000�00000003653�13253666515�0021546�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef PROXYCREDENTIALSDIALOG_H #define PROXYCREDENTIALSDIALOG_H #include <QDialog> QT_FORWARD_DECLARE_CLASS(QNetworkProxy) namespace QInstaller { namespace Ui { class ProxyCredentialsDialog; } class ProxyCredentialsDialog : public QDialog { Q_OBJECT public: explicit ProxyCredentialsDialog(const QNetworkProxy &proxy, QWidget *parent = 0); ~ProxyCredentialsDialog(); QString userName() const; void setUserName(const QString &username); QString password() const; void setPassword(const QString &passwd); private: Ui::ProxyCredentialsDialog *ui; }; } // QInstaller #endif // PROXYCREDENTIALSDIALOG_H �������������������������������������������������������������������������������������src/libs/installer/proxycredentialsdialog.ui��������������������������������������������������������0000664�0000000�0000000�00000005366�13253666515�0021737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QInstaller::ProxyCredentialsDialog</class> <widget class="QDialog" name="QInstaller::ProxyCredentialsDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>262</width> <height>114</height> </rect> </property> <property name="windowTitle"> <string>Dialog</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLabel" name="infotext"> <property name="text"> <string>The proxy %1 requires a username and password.</string> </property> </widget> </item> <item> <layout class="QFormLayout" name="formLayout"> <item row="0" column="0"> <widget class="QLabel" name="usernameLabel"> <property name="text"> <string>Username:</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QLineEdit" name="usernameLineEdit"> <property name="placeholderText"> <string>Username</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="passwordLabel"> <property name="text"> <string>Password:</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QLineEdit" name="passwordLineEdit"> <property name="echoMode"> <enum>QLineEdit::Password</enum> </property> <property name="placeholderText"> <string>Password</string> </property> </widget> </item> </layout> </item> <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> </layout> </widget> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>QInstaller::ProxyCredentialsDialog</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel"> <x>248</x> <y>254</y> </hint> <hint type="destinationlabel"> <x>157</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>QInstaller::ProxyCredentialsDialog</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel"> <x>316</x> <y>260</y> </hint> <hint type="destinationlabel"> <x>286</x> <y>274</y> </hint> </hints> </connection> </connections> </ui> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/qinstallerglobal.h���������������������������������������������������������������0000664�0000000�0000000�00000003765�13253666515�0020332�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QINSTALLER_GLOBAL_H #define QINSTALLER_GLOBAL_H #include <installer_global.h> #include "update.h" #include "updateoperation.h" #include "localpackagehub.h" namespace QInstaller { enum INSTALLER_EXPORT JobError { InvalidUrl = 0x24B04, Timeout, DownloadError, InvalidUpdatesXml, InvalidMetaInfo, ExtractionError, UserIgnoreError, RepositoryUpdatesReceived }; typedef KDUpdater::UpdateOperation Operation; typedef QList<QInstaller::Operation*> OperationList; typedef KDUpdater::Update Package; typedef QList<QInstaller::Package*> PackagesList; typedef QHash<QString, KDUpdater::LocalPackage> LocalPackagesHash; } // namespace QInstaller #endif // QINSTALLER_GLOBAL_H �����������src/libs/installer/qprocesswrapper.cpp��������������������������������������������������������������0000664�0000000�0000000�00000034462�13253666515�0020564�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "qprocesswrapper.h" #include "protocol.h" #include "utils.h" #include <QDir> namespace QInstaller { QProcessWrapper::QProcessWrapper(QObject *parent) : RemoteObject(QLatin1String(Protocol::QProcess), parent) { qRegisterMetaType<QProcess::ExitStatus>(); qRegisterMetaType<QProcess::ProcessError>(); qRegisterMetaType<QProcess::ProcessState>(); m_timer.start(250); connect(&m_timer, &QTimer::timeout, this, &QProcessWrapper::processSignals); connect(&process, &QIODevice::bytesWritten, this, &QProcessWrapper::bytesWritten); connect(&process, &QIODevice::aboutToClose, this, &QProcessWrapper::aboutToClose); connect(&process, &QIODevice::readChannelFinished, this, &QProcessWrapper::readChannelFinished); connect(&process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError))); connect(&process, &QProcess::readyReadStandardOutput, this, &QProcessWrapper::readyReadStandardOutput); connect(&process, &QProcess::readyReadStandardError, this, &QProcessWrapper::readyReadStandardError); connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus))); connect(&process, &QIODevice::readyRead, this, &QProcessWrapper::readyRead); connect(&process, &QProcess::started, this, &QProcessWrapper::started); connect(&process, &QProcess::stateChanged, this, &QProcessWrapper::stateChanged); } QProcessWrapper::~QProcessWrapper() { m_timer.stop(); } void QProcessWrapper::processSignals() { if (!isConnectedToServer()) return; if (!m_lock.tryLockForRead()) return; QList<QVariant> receivedSignals = callRemoteMethod<QList<QVariant> >(QString::fromLatin1(Protocol::GetQProcessSignals)); while (!receivedSignals.isEmpty()) { const QString name = receivedSignals.takeFirst().toString(); if (name == QLatin1String(Protocol::QProcessSignalBytesWritten)) { emit bytesWritten(receivedSignals.takeFirst().value<qint64>()); } else if (name == QLatin1String(Protocol::QProcessSignalAboutToClose)) { emit aboutToClose(); } else if (name == QLatin1String(Protocol::QProcessSignalReadChannelFinished)) { emit readChannelFinished(); } else if (name == QLatin1String(Protocol::QProcessSignalError)) { emit error(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt())); } else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardOutput)) { emit readyReadStandardOutput(); } else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardError)) { emit readyReadStandardError(); } else if (name == QLatin1String(Protocol::QProcessSignalStarted)) { emit started(); } else if (name == QLatin1String(Protocol::QProcessSignalReadyRead)) { emit readyRead(); } else if (name == QLatin1String(Protocol::QProcessSignalStateChanged)) { emit stateChanged(static_cast<QProcess::ProcessState> (receivedSignals.takeFirst() .toInt())); } else if (name == QLatin1String(Protocol::QProcessSignalFinished)) { emit finished(receivedSignals.takeFirst().toInt(), static_cast<QProcess::ExitStatus> (receivedSignals.takeFirst().toInt())); } } m_lock.unlock(); } bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) { QProcessWrapper w; if (w.connectToServer()) { const QPair<bool, qint64> result = w.callRemoteMethod<QPair<bool, qint64> >(QLatin1String(Protocol::QProcessStartDetached), program, arguments, workingDirectory); if (pid != 0) *pid = result.second; w.processSignals(); return result.first; } return QInstaller::startDetached(program, arguments, workingDirectory, pid); } bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments) { return startDetached(program, arguments, QDir::currentPath()); } bool QProcessWrapper::startDetached(const QString &program) { return startDetached(program, QStringList()); } void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode mode) { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessSetProcessChannelMode), static_cast<QProcess::ProcessChannelMode>(mode), dummy); m_lock.unlock(); } else { process.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(mode)); } } /*! Cancels the process. This methods tries to terminate the process gracefully by calling QProcess::terminate. After 10 seconds, the process gets killed. */ void QProcessWrapper::cancel() { if (state() == QProcessWrapper::Running) terminate(); if (!waitForFinished(10000)) kill(); } void QProcessWrapper::setReadChannel(QProcessWrapper::ProcessChannel chan) { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessSetReadChannel), static_cast<QProcess::ProcessChannel>(chan), dummy); m_lock.unlock(); } else { process.setReadChannel(static_cast<QProcess::ProcessChannel>(chan)); } } bool QProcessWrapper::waitForFinished(int msecs) { if (connectToServer()) { m_lock.lockForWrite(); const bool value = callRemoteMethod<bool>(QLatin1String(Protocol::QProcessWaitForFinished), qint32(msecs)); m_lock.unlock(); return value; } return process.waitForFinished(msecs); } bool QProcessWrapper::waitForStarted(int msecs) { if (connectToServer()) { m_lock.lockForWrite(); const bool value = callRemoteMethod<bool>(QLatin1String(Protocol::QProcessWaitForStarted), qint32(msecs)); m_lock.unlock(); return value; } return process.waitForStarted(msecs); } qint64 QProcessWrapper::write(const QByteArray &data) { if (connectToServer()) { m_lock.lockForWrite(); const qint64 value = callRemoteMethod<qint64>(QLatin1String(Protocol::QProcessWrite), data); m_lock.unlock(); return value; } return process.write(data); } void QProcessWrapper::closeWriteChannel() { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessCloseWriteChannel)); m_lock.unlock(); } else { process.closeWriteChannel(); } } int QProcessWrapper::exitCode() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const int value = callRemoteMethod<qint32>(QLatin1String(Protocol::QProcessExitCode)); m_lock.unlock(); return value; } return process.exitCode(); } QProcessWrapper::ExitStatus QProcessWrapper::exitStatus() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const int status = callRemoteMethod<qint32>(QLatin1String(Protocol::QProcessExitStatus)); m_lock.unlock(); return static_cast<QProcessWrapper::ExitStatus>(status); } return static_cast<QProcessWrapper::ExitStatus>(process.exitStatus()); } void QProcessWrapper::kill() { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessKill)); m_lock.unlock(); } else { process.kill(); } } QByteArray QProcessWrapper::readAll() { if (connectToServer()) { m_lock.lockForWrite(); const QByteArray ba = callRemoteMethod<QByteArray>(QLatin1String(Protocol::QProcessReadAll)); m_lock.unlock(); return ba; } return process.readAll(); } QByteArray QProcessWrapper::readAllStandardOutput() { if (connectToServer()) { m_lock.lockForWrite(); const QByteArray ba = callRemoteMethod<QByteArray>(QLatin1String(Protocol::QProcessReadAllStandardOutput)); m_lock.unlock(); return ba; } return process.readAllStandardOutput(); } QByteArray QProcessWrapper::readAllStandardError() { if (connectToServer()) { m_lock.lockForWrite(); const QByteArray ba = callRemoteMethod<QByteArray>(QLatin1String(Protocol::QProcessReadAllStandardError)); m_lock.unlock(); return ba; } return process.readAllStandardError(); } void QProcessWrapper::start(const QString ¶m1, const QStringList ¶m2, QIODevice::OpenMode param3) { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3); m_lock.unlock(); } else { process.start(param1, param2, param3); } } void QProcessWrapper::start(const QString ¶m1, QIODevice::OpenMode param2) { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessStart2Arg), param1, param2); m_lock.unlock(); } else { process.start(param1, param2); } } QProcessWrapper::ProcessState QProcessWrapper::state() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const int state = callRemoteMethod<qint32>(QLatin1String(Protocol::QProcessState)); m_lock.unlock(); return static_cast<QProcessWrapper::ProcessState>(state); } return static_cast<QProcessWrapper::ProcessState>(process.state()); } void QProcessWrapper::terminate() { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessTerminate)); m_lock.unlock(); } else { process.terminate(); } } QProcessWrapper::ProcessChannel QProcessWrapper::readChannel() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const int channel = callRemoteMethod<qint32>(QLatin1String(Protocol::QProcessReadChannel)); m_lock.unlock(); return static_cast<QProcessWrapper::ProcessChannel>(channel); } return static_cast<QProcessWrapper::ProcessChannel>(process.readChannel()); } QProcessWrapper::ProcessChannelMode QProcessWrapper::processChannelMode() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const int mode = callRemoteMethod<qint32>(QLatin1String(Protocol::QProcessProcessChannelMode)); m_lock.unlock(); return static_cast<QProcessWrapper::ProcessChannelMode>(mode); } return static_cast<QProcessWrapper::ProcessChannelMode>(process.processChannelMode()); } QString QProcessWrapper::workingDirectory() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const QString dir = callRemoteMethod<QString>(QLatin1String(Protocol::QProcessWorkingDirectory)); m_lock.unlock(); return dir; } return static_cast<QString>(process.workingDirectory()); } QString QProcessWrapper::errorString() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const QString error = callRemoteMethod<QString>(QLatin1String(Protocol::QProcessErrorString)); m_lock.unlock(); return error; } return static_cast<QString>(process.errorString()); } QStringList QProcessWrapper::environment() const { if ((const_cast<QProcessWrapper *>(this))->connectToServer()) { m_lock.lockForWrite(); const QStringList env = callRemoteMethod<QStringList>(QLatin1String(Protocol::QProcessEnvironment)); m_lock.unlock(); return env; } return process.environment(); } void QProcessWrapper::setEnvironment(const QStringList ¶m1) { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessSetEnvironment), param1, dummy); m_lock.unlock(); } else { process.setEnvironment(param1); } } #ifdef Q_OS_WIN void QProcessWrapper::setNativeArguments(const QString ¶m1) { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessSetNativeArguments), param1, dummy); m_lock.unlock(); } else { process.setNativeArguments(param1); } } #endif void QProcessWrapper::setWorkingDirectory(const QString ¶m1) { if (connectToServer()) { m_lock.lockForWrite(); callRemoteMethod(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1, dummy); m_lock.unlock(); } else { process.setWorkingDirectory(param1); } } } // namespace QInstaller ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/qprocesswrapper.h����������������������������������������������������������������0000664�0000000�0000000�00000010411�13253666515�0020215�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QPROCESSWRAPPER_H #define QPROCESSWRAPPER_H #include "remoteobject.h" #include <QIODevice> #include <QProcess> #include <QReadWriteLock> #include <QTimer> namespace QInstaller { class INSTALLER_EXPORT QProcessWrapper : public RemoteObject { Q_OBJECT Q_DISABLE_COPY(QProcessWrapper) public: enum ProcessState { NotRunning, Starting, Running }; enum ExitStatus { NormalExit, CrashExit }; enum ProcessChannel { StandardOutput = 0, StandardError = 1 }; enum ProcessChannelMode { SeparateChannels = 0, MergedChannels = 1, ForwardedChannels = 2 }; explicit QProcessWrapper(QObject *parent = 0); ~QProcessWrapper(); int exitCode() const; ProcessState state() const; ExitStatus exitStatus() const; QString workingDirectory() const; void setWorkingDirectory(const QString &dir); QStringList environment() const; void setEnvironment(const QStringList &environment); QProcessWrapper::ProcessChannel readChannel() const; void setReadChannel(QProcessWrapper::ProcessChannel channel); QProcessWrapper::ProcessChannelMode processChannelMode() const; void setProcessChannelMode(QProcessWrapper::ProcessChannelMode channel); bool waitForStarted(int msecs = 30000); bool waitForFinished(int msecs = 30000); void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = QIODevice::ReadWrite); void start(const QString &program, QIODevice::OpenMode mode = QIODevice::ReadWrite); void closeWriteChannel(); void kill(); void terminate(); QByteArray readAll(); QByteArray readAllStandardOutput(); QByteArray readAllStandardError(); static bool startDetached(const QString &program); static bool startDetached(const QString &program, const QStringList &arguments); static bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid = 0); QString errorString() const; qint64 write(const QByteArray &byteArray); #ifdef Q_OS_WIN void setNativeArguments(const QString &arguments); #endif Q_SIGNALS: void bytesWritten(qint64); void aboutToClose(); void readChannelFinished(); void error(QProcess::ProcessError); void readyReadStandardOutput(); void readyReadStandardError(); void finished(int exitCode, QProcess::ExitStatus exitStatus); void readyRead(); void started(); void stateChanged(QProcess::ProcessState newState); public Q_SLOTS: void cancel(); private slots: void processSignals(); private: QTimer m_timer; QProcess process; mutable QReadWriteLock m_lock; }; } // namespace QInstaller Q_DECLARE_METATYPE(QProcess::ExitStatus) Q_DECLARE_METATYPE(QProcess::ProcessError) Q_DECLARE_METATYPE(QProcess::ProcessState) #endif // QPROCESSWRAPPER_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/qsettingswrapper.cpp�������������������������������������������������������������0000664�0000000�0000000�00000024145�13253666515�0020743�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "qsettingswrapper.h" #include "permissionsettings.h" #include <QStringList> namespace QInstaller { // -- QSettingsWrapper::Private class QSettingsWrapper::Private { public: Private(const QString &organization, const QString &application) : m_application(application) , m_organization(organization) , m_scope(QSettings::UserScope) , m_format(QSettings::NativeFormat) , settings(organization, application) { } Private(QSettings::Scope scope, const QString &organization, const QString &application) : m_application(application) , m_organization(organization) , m_scope(scope) , m_format(QSettings::NativeFormat) , settings(scope, organization, application) { } Private(QSettings::Format format, QSettings::Scope scope, const QString &organization, const QString &application) : m_application(application) , m_organization(organization) , m_scope(scope) , m_format(format) , settings(format, scope, organization, application) { } Private(const QString &fileName, QSettings::Format format) : m_filename(fileName) , settings(fileName, format) { m_format = format; m_scope = settings.scope(); m_application = settings.applicationName(); m_organization = settings.organizationName(); } QString m_filename; QString m_application; QString m_organization; QSettings::Scope m_scope; QSettings::Format m_format; PermissionSettings settings; }; // -- QSettingsWrapper QSettingsWrapper::QSettingsWrapper(const QString &organization, const QString &application, QObject *parent) : RemoteObject(QLatin1String(Protocol::QSettings), parent) , d(new Private(organization, application)) { } QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Scope scope, const QString &organization, const QString &application, QObject *parent) : RemoteObject(QLatin1String(Protocol::QSettings), parent) , d(new Private(static_cast<QSettings::Scope>(scope), organization, application)) { } QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Format format, QSettingsWrapper::Scope scope, const QString &organization, const QString &application, QObject *parent) : RemoteObject(QLatin1String(Protocol::QSettings), parent) , d(new Private(static_cast<QSettings::Format>(format), static_cast<QSettings::Scope> (scope), organization, application)) { } QSettingsWrapper::QSettingsWrapper(const QString &fileName, QSettingsWrapper::Format format, QObject *parent) : RemoteObject(QLatin1String(Protocol::QSettings), parent) , d(new Private(fileName, static_cast<QSettings::Format>(format))) { } QSettingsWrapper::~QSettingsWrapper() { delete d; } QStringList QSettingsWrapper::allKeys() const { if (createSocket()) return callRemoteMethod<QStringList>(QLatin1String(Protocol::QSettingsAllKeys)); return d->settings.allKeys(); } QString QSettingsWrapper::applicationName() const { if (createSocket()) return callRemoteMethod<QString>(QLatin1String(Protocol::QSettingsApplicationName)); return d->settings.applicationName(); } void QSettingsWrapper::beginGroup(const QString ¶m1) { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsBeginGroup), param1, dummy); else d->settings.beginGroup(param1); } int QSettingsWrapper::beginReadArray(const QString ¶m1) { if (createSocket()) return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), param1); return d->settings.beginReadArray(param1); } void QSettingsWrapper::beginWriteArray(const QString ¶m1, int param2) { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsBeginWriteArray), param1, qint32(param2)); else d->settings.beginWriteArray(param1, param2); } QStringList QSettingsWrapper::childGroups() const { if (createSocket()) return callRemoteMethod<QStringList>(QLatin1String(Protocol::QSettingsChildGroups)); return d->settings.childGroups(); } QStringList QSettingsWrapper::childKeys() const { if (createSocket()) return callRemoteMethod<QStringList>(QLatin1String(Protocol::QSettingsChildKeys)); return d->settings.childKeys(); } void QSettingsWrapper::clear() { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsClear)); else d->settings.clear(); } bool QSettingsWrapper::contains(const QString ¶m1) const { if (createSocket()) return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), param1); return d->settings.contains(param1); } void QSettingsWrapper::endArray() { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsEndArray)); else d->settings.endArray(); } void QSettingsWrapper::endGroup() { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsEndGroup)); else d->settings.endGroup(); } bool QSettingsWrapper::fallbacksEnabled() const { if (createSocket()) return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsFallbacksEnabled)); return d->settings.fallbacksEnabled(); } QString QSettingsWrapper::fileName() const { if (createSocket()) return callRemoteMethod<QString>(QLatin1String(Protocol::QSettingsFileName)); return d->settings.fileName(); } QSettingsWrapper::Format QSettingsWrapper::format() const { // No need to talk to the server, we've setup the local settings object the same way. return static_cast<QSettingsWrapper::Format>(d->settings.format()); } QString QSettingsWrapper::group() const { if (createSocket()) return callRemoteMethod<QString>(QLatin1String(Protocol::QSettingsGroup)); return d->settings.group(); } bool QSettingsWrapper::isWritable() const { if (createSocket()) return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsIsWritable)); return d->settings.isWritable(); } QString QSettingsWrapper::organizationName() const { if (createSocket()) return callRemoteMethod<QString>(QLatin1String(Protocol::QSettingsOrganizationName)); return d->settings.organizationName(); } void QSettingsWrapper::remove(const QString ¶m1) { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsRemove), param1, dummy); else d->settings.remove(param1); } QSettingsWrapper::Scope QSettingsWrapper::scope() const { // No need to talk to the server, we've setup the local settings object the same way. return static_cast<QSettingsWrapper::Scope>(d->settings.scope()); } void QSettingsWrapper::setArrayIndex(int param1) { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1), dummy); else d->settings.setArrayIndex(param1); } void QSettingsWrapper::setFallbacksEnabled(bool param1) { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1, dummy); else d->settings.setFallbacksEnabled(param1); } void QSettingsWrapper::setValue(const QString ¶m1, const QVariant ¶m2) { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsSetValue), param1, param2); else d->settings.setValue(param1, param2); } QSettingsWrapper::Status QSettingsWrapper::status() const { if (createSocket()) { return static_cast<QSettingsWrapper::Status> (callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsStatus))); } return static_cast<QSettingsWrapper::Status>(d->settings.status()); } void QSettingsWrapper::sync() { if (createSocket()) callRemoteMethod(QLatin1String(Protocol::QSettingsSync)); else d->settings.sync(); } QVariant QSettingsWrapper::value(const QString ¶m1, const QVariant ¶m2) const { if (createSocket()) return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), param1, param2); return d->settings.value(param1, param2); } // -- private bool QSettingsWrapper::createSocket() const { if ((d->m_format != QSettings::NativeFormat) && (d->m_format != QSettings::IniFormat)) { Q_ASSERT_X(false, Q_FUNC_INFO, "Settings wrapper only supports QSettingsWrapper::NativeFormat" " and QSettingsWrapper::IniFormat."); } return (const_cast<QSettingsWrapper *>(this))->connectToServer(QVariantList() << d->m_application << d->m_organization << d->m_scope << d->m_format << d->m_filename); } } // namespace QInstaller ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/qsettingswrapper.h���������������������������������������������������������������0000664�0000000�0000000�00000011072�13253666515�0020403�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QSETTINGSWRAPPER_H #define QSETTINGSWRAPPER_H #include "protocol.h" #include "remoteobject.h" #include <QVariant> namespace QInstaller { class INSTALLER_EXPORT QSettingsWrapper : public RemoteObject { Q_OBJECT Q_DISABLE_COPY(QSettingsWrapper) public: enum Status { NoError = 0, AccessError, FormatError }; enum Format { NativeFormat, IniFormat, InvalidFormat = 16 }; enum Scope { UserScope, SystemScope }; explicit QSettingsWrapper(const QString &organization, const QString &application = QString(), QObject *parent = 0); QSettingsWrapper(Scope scope, const QString &organization, const QString &application = QString(), QObject *parent = 0); QSettingsWrapper(Format format, Scope scope, const QString &organization, const QString &application = QString(), QObject *parent = 0); QSettingsWrapper(const QString &fileName, Format format, QObject *parent = 0); ~QSettingsWrapper(); void clear(); void sync(); Status status() const; void beginGroup(const QString &prefix); void endGroup(); QString group() const; int beginReadArray(const QString &prefix); void beginWriteArray(const QString &prefix, int size = -1); void endArray(); void setArrayIndex(int i); QStringList allKeys() const; QStringList childKeys() const; QStringList childGroups() const; bool isWritable() const; void setValue(const QString &key, const QVariant &value); QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; void remove(const QString &key); bool contains(const QString &key) const; void setFallbacksEnabled(bool b); bool fallbacksEnabled() const; QString fileName() const; Format format() const; Scope scope() const; QString organizationName() const; QString applicationName() const; private: bool createSocket() const; private: // we cannot support the following functionality explicit QSettingsWrapper(QObject *parent = 0) : RemoteObject(QLatin1String(Protocol::QSettings), parent) {} void setIniCodec(QTextCodec * /*codec*/); void setIniCodec(const char * /*codecName*/); QTextCodec *iniCodec() const { return 0; } static void setDefaultFormat(Format /*format*/); static Format defaultFormat() { return NativeFormat; } static void setSystemIniPath(const QString & /*dir*/); static void setUserIniPath(const QString & /*dir*/); static void setPath(Format /*format*/, Scope /*scope*/, const QString & /*path*/); typedef QMap<QString, QVariant> SettingsMap; typedef bool(*ReadFunc)(QIODevice &device, SettingsMap &map); typedef bool(*WriteFunc)(QIODevice &device, const SettingsMap &map); static Format registerFormat(const QString &extension, ReadFunc readFunc, WriteFunc writeFunc, Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive) { Q_UNUSED(extension) Q_UNUSED(readFunc) Q_UNUSED(writeFunc) Q_UNUSED(caseSensitivity) return NativeFormat; } private: class Private; Private *d; }; } // namespace QInstaller #endif // QSETTINGSWRAPPER_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/qtpatch.cpp����������������������������������������������������������������������0000664�0000000�0000000�00000016313�13253666515�0016763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "qtpatch.h" #include "utils.h" #include <QString> #include <QStringList> #include <QFileInfo> #include <QProcess> #include <QTextStream> #include <QVector> #include <QTime> #include <QtCore/QDebug> #include <QCoreApplication> #include <QByteArrayMatcher> QHash<QString, QByteArray> QtPatch::readQmakeOutput(const QByteArray &data) { QHash<QString, QByteArray> qmakeValueHash; QTextStream stream(data, QIODevice::ReadOnly); while (!stream.atEnd()) { const QString line = stream.readLine(); const int index = line.indexOf(QLatin1Char(':')); if (index != -1) { QString value = line.mid(index+1); if (value != QLatin1String("**Unknown**") ) qmakeValueHash.insert(line.left(index), value.toUtf8()); } } return qmakeValueHash; } QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteArray *qmakeOutput) { QHash<QString, QByteArray> qmakeValueHash; // in some cases qmake is not runable, because another process is blocking it(filewatcher ...) int waitCount = 0; while (qmakeValueHash.isEmpty() && waitCount < 3) { QFileInfo qmake(qmakePath); if (!qmake.exists()) { qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath)); return qmakeValueHash; } if (!qmake.isExecutable()) { qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath)); return qmakeValueHash; } QStringList args; args << QLatin1String("-query"); QProcess process; process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly); if (process.waitForFinished(10000)) { QByteArray output = process.readAllStandardOutput(); qmakeOutput->append(output); if (process.exitStatus() == QProcess::CrashExit) { qWarning() << qmake.absoluteFilePath() << args << "crashed with exit code" << process.exitCode() << "standard output:" << output << "error output:" << process.readAllStandardError(); return qmakeValueHash; } qmakeValueHash = readQmakeOutput(output); } if (qmakeValueHash.isEmpty()) { ++waitCount; static const int waitTimeInMilliSeconds = 500; QInstaller::uiDetachedWait(waitTimeInMilliSeconds); } if (process.state() > QProcess::NotRunning ) { qDebug() << "qmake process is still running, need to kill it."; process.kill(); } } if (qmakeValueHash.isEmpty()) qDebug() << "Cannot get any query output from qmake."; return qmakeValueHash; } bool QtPatch::patchBinaryFile(const QString &fileName, const QByteArray &oldQtPath, const QByteArray &newQtPath) { QFile file(fileName); if (!file.exists()) { qDebug() << "qpatch: warning: file" << fileName << "not found"; return false; } openFileForPatching(&file); if (!file.isOpen()) { qDebug() << "qpatch: warning: file" << qPrintable(fileName) << "cannot open."; qDebug().noquote() << file.errorString(); return false; } bool isPatched = patchBinaryFile(&file, oldQtPath, newQtPath); file.close(); return isPatched; } // device must be open bool QtPatch::patchBinaryFile(QIODevice *device, const QByteArray &oldQtPath, const QByteArray &newQtPath) { if (!(device->openMode() == QIODevice::ReadWrite)) { qDebug() << "qpatch: warning: This function needs an open device for writing."; return false; } const QByteArray source = device->readAll(); device->seek(0); int offset = 0; QByteArray overwritePath(newQtPath); if (overwritePath.size() < oldQtPath.size()) { QByteArray fillByteArray(oldQtPath.size() - overwritePath.size(), '\0'); overwritePath.append(fillByteArray); } QByteArrayMatcher byteArrayMatcher(oldQtPath); forever { offset = byteArrayMatcher.indexIn(source, offset); if (offset == -1) break; device->seek(offset); device->write(overwritePath); offset += overwritePath.size(); } device->seek(0); //for next reading we should be at the beginning return true; } bool QtPatch::patchTextFile(const QString &fileName, const QHash<QByteArray, QByteArray> &searchReplacePairs) { QFile file(fileName); if (!file.open(QFile::ReadOnly)) { qDebug() << "Cannot open file" << fileName << "for patching:" << file.errorString(); return false; } QByteArray source = file.readAll(); file.close(); QHashIterator<QByteArray, QByteArray> it(searchReplacePairs); while (it.hasNext()) { it.next(); source.replace(it.key(), it.value()); } if (!file.open(QFile::WriteOnly | QFile::Truncate)) { qDebug() << "File" << fileName << "not writable."; return false; } file.write(source); return true; } bool QtPatch::openFileForPatching(QFile *file) { if (file->openMode() == QIODevice::NotOpen) { // in some cases the file cannot be opened, because another process is blocking it (filewatcher ...) int waitCount = 0; while (!file->open(QFile::ReadWrite) && waitCount < 60) { ++waitCount; static const int waitTimeInMilliSeconds = 500; QInstaller::uiDetachedWait(waitTimeInMilliSeconds); } return file->openMode() == QFile::ReadWrite; } qDebug() << "File" << file->fileName() << "is open, so it cannot be opened again."; return false; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/qtpatch.h������������������������������������������������������������������������0000664�0000000�0000000�00000004402�13253666515�0016424�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QTPATCH_H #define QTPATCH_H #include "installer_global.h" #include <QString> #include <QByteArray> #include <QHash> #include <QFile> namespace QtPatch { QHash<QString, QByteArray> INSTALLER_EXPORT readQmakeOutput(const QByteArray &data); QHash<QString, QByteArray> INSTALLER_EXPORT qmakeValues(const QString &qmakePath, QByteArray *qmakeOutput); bool INSTALLER_EXPORT patchBinaryFile(const QString &fileName, const QByteArray &oldQtPath, const QByteArray &newQtPath ); bool INSTALLER_EXPORT patchBinaryFile(QIODevice *device, const QByteArray &oldQtPath, const QByteArray &newQtPath ); bool INSTALLER_EXPORT patchTextFile(const QString &fileName, const QHash<QByteArray, QByteArray> &searchReplacePairs); bool INSTALLER_EXPORT openFileForPatching(QFile *file); } #endif // QTPATCH_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/range.h��������������������������������������������������������������������������0000664�0000000�0000000�00000005223�13253666515�0016056�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef RANGE_H #define RANGE_H #include <algorithm> template <typename T> class Range { public: static Range<T> fromStartAndEnd( const T& start, const T& end ) { Range<T> r; r.m_start = start; r.m_end = end; return r; } static Range<T> fromStartAndLength( const T& start, const T& length ) { Range<T> r; r.m_start = start; r.m_end = start + length; return r; } Range() : m_start( 0 ), m_end( 0 ) {} T start() const { return m_start; } T end() const { return m_end; } void move( const T& by ) { m_start += by; m_end += by; } Range<T> moved( const T& by ) const { Range<T> b = *this; b.move( by ); return b; } T length() const { return m_end - m_start; } Range<T> normalized() const { Range<T> r2( *this ); if ( r2.m_start > r2.m_end ) std::swap( r2.m_start, r2.m_end ); return r2; } bool operator==( const Range<T>& other ) const { return m_start == other.m_start && m_end && other.m_end; } bool operator<( const Range<T>& other ) const { if ( m_start != other.m_start ) return m_start < other.m_start; return m_end < other.m_end; } private: T m_start; T m_end; }; #endif /* RANGE_H_ */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/registerfiletypeoperation.cpp����������������������������������������������������0000664�0000000�0000000�00000020330�13253666515�0022620�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "registerfiletypeoperation.h" #include "constants.h" #include "packagemanagercore.h" #include "qsettingswrapper.h" using namespace QInstaller; #ifdef Q_OS_WIN #include <shlobj.h> struct StartsWithProgId { bool operator()(const QString &s) { return s.startsWith(QLatin1String("ProgId=")); } }; static QString takeProgIdArgument(QStringList &args) { // if the arguments contain an option in the form "ProgId=...", find it and consume it QStringList::iterator it = std::find_if (args.begin(), args.end(), StartsWithProgId()); if (it == args.end()) return QString(); const QString progId = it->mid(QString::fromLatin1("ProgId=").size()); args.erase(it); return progId; } static QVariantHash readHive(QSettingsWrapper *const settings, const QString &hive) { QVariantHash keyValues; settings->beginGroup(hive); foreach (const QString &key, settings->allKeys()) keyValues.insert(key, settings->value(key)); settings->endGroup(); return keyValues; } #endif // -- RegisterFileTypeOperation RegisterFileTypeOperation::RegisterFileTypeOperation(PackageManagerCore *core) : UpdateOperation(core) , m_optionalArgumentsRead(false) { setName(QLatin1String("RegisterFileType")); } void RegisterFileTypeOperation::backup() { } bool RegisterFileTypeOperation::performOperation() { #ifdef Q_OS_WIN ensureOptionalArgumentsRead(); if (!checkArgumentCount(2, 5, tr("<extension> <command> [description [contentType [icon]]]"))) return false; QStringList args = arguments(); bool allUsers = false; PackageManagerCore *const core = packageManager(); if (core && core->value(scAllUsers) == scTrue) allUsers = true; QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER") , QSettingsWrapper::NativeFormat); const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId; const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0)); const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + m_progId; // backup old value setValue(QLatin1String("oldType"), readHive(&settings, classesFileType)); // register new values settings.setValue(QString::fromLatin1("%1/Default").arg(classesFileType), m_progId); settings.setValue(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, m_progId), QString()); settings.setValue(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classesProgId), args.at(1)); settings.setValue(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classesApplications), args.at(1)); // content type (optional) const QString contentType = args.value(3); if (!contentType.isEmpty()) settings.setValue(QString::fromLatin1("%1/Content Type").arg(classesFileType), contentType); // description (optional) const QString description = args.value(2); if (!description.isEmpty()) settings.setValue(QString::fromLatin1("%1/Default").arg(classesProgId), description); // icon (optional) const QString icon = args.value(4); if (!icon.isEmpty()) settings.setValue(QString::fromLatin1("%1/DefaultIcon/Default").arg(classesProgId), icon); // backup new value setValue(QLatin1String("newType"), readHive(&settings, classesFileType)); // force the shell to invalidate its cache SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); return true; #else setError(UserDefinedError); setErrorString(tr("Registering file types is only supported on Windows.")); return false; #endif } bool RegisterFileTypeOperation::undoOperation() { #ifdef Q_OS_WIN ensureOptionalArgumentsRead(); QStringList args = arguments(); if (!checkArgumentCount(2, 5, tr("Register File Type: Invalid arguments"))) return false; bool allUsers = false; PackageManagerCore *const core = packageManager(); if (core && core->value(scAllUsers) == scTrue) allUsers = true; QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER") , QSettingsWrapper::NativeFormat); const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId; const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0)); const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + m_progId; // Quoting MSDN here: When uninstalling an application, the ProgIDs and most other registry information // associated with that application should be deleted as part of the uninstallation.However, applications // that have taken ownership of a file type (by setting the Default value of the file type's // HKEY...\.extension subkey to the ProgID of the application) should not attempt to remove that value // when uninstalling. Leaving the data in place for the Default value avoids the difficulty of // determining whether another application has taken ownership of the file type and overwritten the // Default value after the original application was installed. Windows respects the Default value only // if the ProgID found there is a registered ProgID. If the ProgID is unregistered, it is ignored. // if the hive didn't change since we touched it if (value(QLatin1String("newType")).toHash() == readHive(&settings, classesFileType)) { // reset to the values we found settings.remove(classesFileType); settings.beginGroup(classesFileType); const QVariantHash keyValues = value(QLatin1String("oldType")).toHash(); foreach (const QString &key, keyValues.keys()) settings.setValue(key, keyValues.value(key)); settings.endGroup(); } else { // some changes happened, remove the only save value we know about settings.remove(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, m_progId)); } // remove ProgId and Applications entry settings.remove(classesProgId); settings.remove(classesApplications); // force the shell to invalidate its cache SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); return true; #else setErrorString(tr("Registering file types is only supported on Windows.")); return false; #endif } bool RegisterFileTypeOperation::testOperation() { return true; } void RegisterFileTypeOperation::ensureOptionalArgumentsRead() { #ifdef Q_OS_WIN if (m_optionalArgumentsRead) return; m_optionalArgumentsRead = true; QStringList args = arguments(); m_progId = takeProgIdArgument(args); if (m_progId.isEmpty() && args.count() > 0) m_progId = QString::fromLatin1("%1_auto_file").arg(args.at(0)); setArguments(args); #endif } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/registerfiletypeoperation.h������������������������������������������������������0000664�0000000�0000000�00000003463�13253666515�0022275�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REGISTERFILETYPEOPERATION_H #define REGISTERFILETYPEOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT RegisterFileTypeOperation : public QObject, public Operation { Q_OBJECT public: explicit RegisterFileTypeOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); private: void ensureOptionalArgumentsRead(); bool m_optionalArgumentsRead; QString m_progId; }; } #endif �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteclient.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000005607�13253666515�0020015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "remoteclient.h" #include "remoteclient_p.h" namespace QInstaller { RemoteClient *RemoteClient::s_instance = 0; RemoteClient::RemoteClient() : d_ptr(new RemoteClientPrivate(this)) { } RemoteClient::~RemoteClient() { } RemoteClient &RemoteClient::instance() { if (!s_instance) s_instance = new RemoteClient; return *s_instance; } QString RemoteClient::socketName() const { Q_D(const RemoteClient); return d->m_socketName; } QString RemoteClient::authorizationKey() const { Q_D(const RemoteClient); return d->m_key; } /*! Initializes the client with \a socketName, with the \a key the client sends to authenticate with the server, \a mode and \a startAs. */ void RemoteClient::init(const QString &socketName, const QString &key, Protocol::Mode mode, Protocol::StartAs startAs) { Q_D(RemoteClient); d->init(socketName, key, mode, startAs); } void RemoteClient::setAuthorizationFallbackDisabled(bool disabled) { Q_D(RemoteClient); d->setAuthorizationFallbackDisabled(disabled); } void RemoteClient::shutdown() { Q_D(RemoteClient); d->shutdown(); d_ptr.reset(new RemoteClientPrivate(this)); } void RemoteClient::destroy() { delete s_instance; s_instance = 0; } bool RemoteClient::isActive() const { Q_D(const RemoteClient); return d->m_active; } void RemoteClient::setActive(bool active) { Q_D(RemoteClient); d->m_active = active; if (d->m_active) { d->maybeStartServer(); d->m_active = d->m_serverStarted; } } } // namespace QInstaller �������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteclient.h�������������������������������������������������������������������0000664�0000000�0000000�00000004257�13253666515�0017462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTECLIENT_H #define REMOTECLIENT_H #include "installer_global.h" #include "protocol.h" #include <QScopedPointer> namespace QInstaller { class RemoteClientPrivate; class INSTALLER_EXPORT RemoteClient { Q_DISABLE_COPY(RemoteClient) Q_DECLARE_PRIVATE(RemoteClient) public: static RemoteClient &instance(); void init(const QString &socketName, const QString &key, Protocol::Mode mode, Protocol::StartAs startAs); void setAuthorizationFallbackDisabled(bool disabled); void shutdown(); void destroy(); QString socketName() const; QString authorizationKey() const; bool isActive() const; void setActive(bool active); private: RemoteClient(); ~RemoteClient(); private: static RemoteClient *s_instance; QScopedPointer<RemoteClientPrivate> d_ptr; }; } // namespace QInstaller #endif // REMOTECLIENT_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteclient_p.h�����������������������������������������������������������������0000664�0000000�0000000�00000017653�13253666515�0020005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTECLIENT_P_H #define REMOTECLIENT_P_H #include "adminauthorization.h" #include "keepaliveobject.h" #include "messageboxhandler.h" #include "protocol.h" #include "remoteclient.h" #include "remoteobject.h" #include "utils.h" #include <QCoreApplication> #include <QElapsedTimer> #include <QMutex> #include <QThread> namespace QInstaller { class RemoteClientPrivate : public RemoteObject { Q_DECLARE_PUBLIC(RemoteClient) Q_DISABLE_COPY(RemoteClientPrivate) public: RemoteClientPrivate(RemoteClient *parent) : RemoteObject(QLatin1String("RemoteClientPrivate")) , q_ptr(parent) , m_mutex(QMutex::Recursive) , m_startServerAs(Protocol::StartAs::User) , m_serverStarted(false) , m_active(false) , m_key(QLatin1String(Protocol::DefaultAuthorizationKey)) , m_mode(Protocol::Mode::Debug) , m_authorizationFallbackDisabled(false) { m_thread.setObjectName(QLatin1String("KeepAlive")); } ~RemoteClientPrivate() { shutdown(); } void shutdown() { m_thread.quit(); m_thread.wait(); maybeStopServer(); } void init(const QString &socketName, const QString &key, Protocol::Mode mode, Protocol::StartAs startAs) { m_socketName = socketName; m_key = key; m_mode = mode; if (mode == Protocol::Mode::Production) { m_startServerAs = startAs; m_serverCommand = QCoreApplication::applicationFilePath(); m_serverArguments = QStringList() << QLatin1String("--startserver") << QString::fromLatin1("%1,%2,%3") .arg(QLatin1String(Protocol::ModeProduction)) .arg(socketName) .arg(key); KeepAliveObject *object = new KeepAliveObject; object->moveToThread(&m_thread); QObject::connect(&m_thread, &QThread::started, object, &KeepAliveObject::start); QObject::connect(&m_thread, &QThread::finished, object, &QObject::deleteLater); m_thread.start(); } else if (mode == Protocol::Mode::Debug) { // To be able to debug the client-server connection start and stop the server manually, // e.g. installer --startserver DEBUG. } } void setAuthorizationFallbackDisabled(bool disabled) { m_authorizationFallbackDisabled = disabled; } void maybeStartServer() { if (m_mode == Protocol::Mode::Debug) m_serverStarted = true; // we expect the server to be started by the developer if (m_serverStarted) return; const QMutexLocker ml(&m_mutex); if (m_serverStarted) return; m_serverStarted = false; bool started = false; if (m_startServerAs == Protocol::StartAs::SuperUser) { started = AdminAuthorization::execute(0, m_serverCommand, m_serverArguments); if (!started) { if (m_authorizationFallbackDisabled) { QMessageBox::Button res = QMessageBox::Retry; while (res == QMessageBox::Retry && !started) { res = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("AuthorizationError"), QCoreApplication::translate("RemoteClient", "Cannot get authorization."), QCoreApplication::translate("RemoteClient", "Cannot get authorization that is needed for continuing the installation.\n\n" "Please start the setup program as a user with the appropriate rights.\n" "Or accept the elevation of access rights if being asked."), QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Retry); if (res == QMessageBox::Retry) started = AdminAuthorization::execute(0, m_serverCommand, m_serverArguments); } } else { // something went wrong with authorizing, either user pressed cancel or entered // wrong password const QString fallback = m_serverCommand + QLatin1String(" ") + m_serverArguments .join(QLatin1String(" ")); const QMessageBox::Button res = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("AuthorizationError"), QCoreApplication::translate("RemoteClient", "Cannot get authorization."), QCoreApplication::translate("RemoteClient", "Cannot get authorization that " "is needed for continuing the installation.\n Either abort the " "installation or use the fallback solution by running\n\n%1\n\nas a user " "with the appropriate rights and then clicking OK.").arg(fallback), QMessageBox::Abort | QMessageBox::Ok, QMessageBox::Ok); if (res == QMessageBox::Ok) started = true; } } } else { started = QInstaller::startDetached(m_serverCommand, m_serverArguments, QCoreApplication::applicationDirPath()); } if (started) { QElapsedTimer t; t.start(); // 30 seconds waiting ought to be enough for the app to start while ((!m_serverStarted) && (t.elapsed() < 30000)) m_serverStarted = authorize(); } } void maybeStopServer() { if (m_mode == Protocol::Mode::Debug) m_serverStarted = false; // we never started the server in debug mode if (!m_serverStarted) return; const QMutexLocker ml(&m_mutex); if (!m_serverStarted) return; if (!authorize()) return; m_serverStarted = !callRemoteMethod<bool>(QString::fromLatin1(Protocol::Shutdown)); } private: RemoteClient *q_ptr; QMutex m_mutex; QString m_socketName; Protocol::StartAs m_startServerAs; bool m_serverStarted; bool m_active; QString m_serverCommand; QStringList m_serverArguments; QString m_key; QThread m_thread; Protocol::Mode m_mode; bool m_authorizationFallbackDisabled; }; } // namespace QInstaller #endif // REMOTECLIENT_P_H �������������������������������������������������������������������������������������src/libs/installer/remotefileengine.cpp�������������������������������������������������������������0000664�0000000�0000000�00000035341�13253666515�0020642�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "remotefileengine.h" #include "protocol.h" #include "remoteclient.h" #include <QRegExp> namespace QInstaller { // -- RemoteFileEngineHandler QAbstractFileEngine* RemoteFileEngineHandler::create(const QString &fileName) const { if (!RemoteClient::instance().isActive()) return 0; static QRegExp re(QLatin1String("^[a-z0-9]*://.*$")); if (re.exactMatch(fileName)) // stuff like installer:// return 0; if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":"))) return 0; // empty filename or Qt resource QScopedPointer<RemoteFileEngine> client(new RemoteFileEngine()); client->setFileName(fileName); if (client->isConnectedToServer()) return client.take(); return 0; } // -- RemoteFileEngineIterator class RemoteFileEngineIterator : public QAbstractFileEngineIterator { public: RemoteFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters, const QStringList &files) : QAbstractFileEngineIterator(filters, nameFilters) , index(-1) , entries(files) { } QString next(); bool hasNext() const; QString currentFileName() const; private: qint32 index; QStringList entries; }; /*! Advances the iterator to the next entry, and returns the current file path of this new entry. If hasNext() returns \c false, the function does nothing and returns an empty QString. */ bool RemoteFileEngineIterator::hasNext() const { return index < entries.size() - 1; } /*! Returns \c true if there is at least one more entry in the directory, otherwise returns \c false. */ QString RemoteFileEngineIterator::next() { if (!hasNext()) return QString(); ++index; return currentFilePath(); } /*! Returns the name of the current directory entry, excluding the path. */ QString RemoteFileEngineIterator::currentFileName() const { return entries.at(index); } // -- RemoteFileEngine RemoteFileEngine::RemoteFileEngine() : RemoteObject(QLatin1String(Protocol::QAbstractFileEngine)) { } RemoteFileEngine::~RemoteFileEngine() { } /*! \reimp */ bool RemoteFileEngine::atEnd() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineAtEnd)); return m_fileEngine.atEnd(); } /*! \reimp */ QAbstractFileEngine::Iterator* RemoteFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames) { if (connectToServer()) { QStringList entries = entryList(filters, filterNames); entries.removeAll(QString()); return new RemoteFileEngineIterator(filters, filterNames, entries); } return m_fileEngine.beginEntryList(filters, filterNames); } QAbstractFileEngine::Iterator* RemoteFileEngine::endEntryList() { if (connectToServer()) return 0; // right now all other implementations return 0 too return m_fileEngine.endEntryList(); } /*! \reimp */ bool RemoteFileEngine::caseSensitive() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineCaseSensitive)); return m_fileEngine.caseSensitive(); } /*! \reimp */ bool RemoteFileEngine::close() { if (connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineClose)); return m_fileEngine.close(); } /*! \reimp */ bool RemoteFileEngine::copy(const QString &newName) { if (connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineCopy), newName); return m_fileEngine.copy(newName); } /*! \reimp */ QStringList RemoteFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<QStringList> (QString::fromLatin1(Protocol::QAbstractFileEngineEntryList), static_cast<qint32>(filters), filterNames); } return m_fileEngine.entryList(filters, filterNames); } /*! \reimp */ QFile::FileError RemoteFileEngine::error() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return static_cast<QFile::FileError> (callRemoteMethod<qint32>(QString::fromLatin1(Protocol::QAbstractFileEngineError))); } return m_fileEngine.error(); } /*! \reimp */ QString RemoteFileEngine::errorString() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<QString>(QString::fromLatin1(Protocol::QAbstractFileEngineErrorString)); return m_fileEngine.errorString(); } /*! \reimp */ bool RemoteFileEngine::extension(Extension extension, const ExtensionOption *eo, ExtensionReturn *er) { if (connectToServer()) return false; return m_fileEngine.extension(extension, eo, er); } /*! \reimp */ QAbstractFileEngine::FileFlags RemoteFileEngine::fileFlags(FileFlags type) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return static_cast<QAbstractFileEngine::FileFlags> (callRemoteMethod<qint32>(QString::fromLatin1(Protocol::QAbstractFileEngineFileFlags), static_cast<qint32>(type))); } return m_fileEngine.fileFlags(type); } /*! \reimp */ QString RemoteFileEngine::fileName(FileName file) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<QString>(QString::fromLatin1(Protocol::QAbstractFileEngineFileName), static_cast<qint32>(file)); } return m_fileEngine.fileName(file); } /*! \reimp */ bool RemoteFileEngine::flush() { if (connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineFlush)); return m_fileEngine.flush(); } /*! \reimp */ int RemoteFileEngine::handle() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<qint32>(QString::fromLatin1(Protocol::QAbstractFileEngineHandle)); return m_fileEngine.handle(); } /*! \reimp */ bool RemoteFileEngine::isRelativePath() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineIsRelativePath)); return m_fileEngine.isRelativePath(); } /*! \reimp */ bool RemoteFileEngine::isSequential() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineIsSequential)); return m_fileEngine.isSequential(); } /*! \reimp */ bool RemoteFileEngine::link(const QString &newName) { if (connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineLink), newName); } return m_fileEngine.link(newName); } /*! \reimp */ bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineMkdir), dirName, createParentDirectories); } return m_fileEngine.mkdir(dirName, createParentDirectories); } /*! \reimp */ bool RemoteFileEngine::open(QIODevice::OpenMode mode) { if (connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineOpen), static_cast<qint32>(mode)); } return m_fileEngine.open(mode); } /*! \reimp */ QString RemoteFileEngine::owner(FileOwner owner) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<QString>(QString::fromLatin1(Protocol::QAbstractFileEngineOwner), static_cast<qint32>(owner)); } return m_fileEngine.owner(owner); } /*! \reimp */ uint RemoteFileEngine::ownerId(FileOwner owner) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<quint32>(QString::fromLatin1(Protocol::QAbstractFileEngineOwnerId), static_cast<qint32>(owner)); } return m_fileEngine.ownerId(owner); } /*! \reimp */ qint64 RemoteFileEngine::pos() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<qint64>(QString::fromLatin1(Protocol::QAbstractFileEnginePos)); return m_fileEngine.pos(); } /*! \reimp */ bool RemoteFileEngine::remove() { if (connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineRemove)); return m_fileEngine.remove(); } /*! \reimp */ bool RemoteFileEngine::rename(const QString &newName) { if (connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineRename), newName); } return m_fileEngine.rename(newName); } /*! \reimp */ bool RemoteFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineRmdir), dirName, recurseParentDirectories); } return m_fileEngine.rmdir(dirName, recurseParentDirectories); } /*! \reimp */ bool RemoteFileEngine::seek(qint64 offset) { if (connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineSeek), offset); return m_fileEngine.seek(offset); } /*! \reimp */ void RemoteFileEngine::setFileName(const QString &fileName) { if (connectToServer()) { callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName, dummy); } m_fileEngine.setFileName(fileName); } /*! \reimp */ bool RemoteFileEngine::setPermissions(uint perms) { if (connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineSetPermissions), perms); } return m_fileEngine.setPermissions(perms); } /*! \reimp */ bool RemoteFileEngine::setSize(qint64 size) { if (connectToServer()) { return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineSetSize), size); } return m_fileEngine.setSize(size); } /*! \reimp */ qint64 RemoteFileEngine::size() const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return callRemoteMethod<qint64>(QString::fromLatin1(Protocol::QAbstractFileEngineSize)); return m_fileEngine.size(); } /*! \reimp */ bool RemoteFileEngine::supportsExtension(Extension extension) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) return false; return m_fileEngine.supportsExtension(extension); } /*! \reimp */ qint64 RemoteFileEngine::read(char *data, qint64 maxlen) { if (connectToServer()) { QPair<qint64, QByteArray> result = callRemoteMethod<QPair<qint64, QByteArray> > (QString::fromLatin1(Protocol::QAbstractFileEngineRead), maxlen); if (result.first <= 0) return result.first; QDataStream dataStream(result.second); dataStream.readRawData(data, result.first); return result.first; } return m_fileEngine.read(data, maxlen); } /*! \reimp */ qint64 RemoteFileEngine::readLine(char *data, qint64 maxlen) { if (connectToServer()) { QPair<qint64, QByteArray> result = callRemoteMethod<QPair<qint64, QByteArray> > (QString::fromLatin1(Protocol::QAbstractFileEngineReadLine), maxlen); if (result.first <= 0) return result.first; QDataStream dataStream(result.second); dataStream.readRawData(data, result.first); return result.first; } return m_fileEngine.readLine(data, maxlen); } /*! \reimp */ qint64 RemoteFileEngine::write(const char *data, qint64 len) { if (connectToServer()) { QByteArray ba(data, len); return callRemoteMethod<qint64>(QString::fromLatin1(Protocol::QAbstractFileEngineWrite), ba); } return m_fileEngine.write(data, len); } bool RemoteFileEngine::syncToDisk() { if (connectToServer()) return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineSyncToDisk)); return m_fileEngine.syncToDisk(); } bool RemoteFileEngine::renameOverwrite(const QString &newName) { if (connectToServer()) { return callRemoteMethod<bool> (QString::fromLatin1(Protocol::QAbstractFileEngineRenameOverwrite), newName); } return m_fileEngine.renameOverwrite(newName); } QDateTime RemoteFileEngine::fileTime(FileTime time) const { if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) { return callRemoteMethod<QDateTime> (QString::fromLatin1(Protocol::QAbstractFileEngineFileTime), static_cast<qint32> (time)); } return m_fileEngine.fileTime(time); } } // namespace QInstaller �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remotefileengine.h���������������������������������������������������������������0000664�0000000�0000000�00000010447�13253666515�0020307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTEFILEENGINE_H #define REMOTEFILEENGINE_H #include "remoteobject.h" #include <QtCore/private/qabstractfileengine_p.h> #include <QtCore/private/qfsfileengine_p.h> namespace QInstaller { class INSTALLER_EXPORT RemoteFileEngineHandler : public QAbstractFileEngineHandler { Q_DISABLE_COPY(RemoteFileEngineHandler) public: RemoteFileEngineHandler() : QAbstractFileEngineHandler() {} QAbstractFileEngine* create(const QString &fileName) const Q_DECL_OVERRIDE; }; class RemoteFileEngine : public RemoteObject, public QAbstractFileEngine { Q_DISABLE_COPY(RemoteFileEngine) public: RemoteFileEngine(); ~RemoteFileEngine(); bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE; bool close() Q_DECL_OVERRIDE; bool flush() Q_DECL_OVERRIDE; bool syncToDisk() Q_DECL_OVERRIDE; qint64 size() const Q_DECL_OVERRIDE; qint64 pos() const Q_DECL_OVERRIDE; bool seek(qint64 offset) Q_DECL_OVERRIDE; bool isSequential() const Q_DECL_OVERRIDE; bool remove() Q_DECL_OVERRIDE; bool copy(const QString &newName) Q_DECL_OVERRIDE; bool rename(const QString &newName) Q_DECL_OVERRIDE; bool renameOverwrite(const QString &newName) Q_DECL_OVERRIDE; bool link(const QString &newName) Q_DECL_OVERRIDE; bool mkdir(const QString &dirName, bool createParentDirectories) const Q_DECL_OVERRIDE; bool rmdir(const QString &dirName, bool recurseParentDirectories) const Q_DECL_OVERRIDE; bool setSize(qint64 size) Q_DECL_OVERRIDE; bool caseSensitive() const Q_DECL_OVERRIDE; bool isRelativePath() const Q_DECL_OVERRIDE; QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const Q_DECL_OVERRIDE; FileFlags fileFlags(FileFlags type = FileInfoAll) const Q_DECL_OVERRIDE; bool setPermissions(uint perms) Q_DECL_OVERRIDE; QString fileName(FileName file = DefaultName) const Q_DECL_OVERRIDE; uint ownerId(FileOwner owner) const Q_DECL_OVERRIDE; QString owner(FileOwner owner) const Q_DECL_OVERRIDE; QDateTime fileTime(FileTime time) const Q_DECL_OVERRIDE; void setFileName(const QString &fileName) Q_DECL_OVERRIDE; int handle() const Q_DECL_OVERRIDE; bool atEnd() const; uchar *map(qint64, qint64, QFile::MemoryMapFlags) { return 0; } bool unmap(uchar *) { return true; } Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) Q_DECL_OVERRIDE; Iterator *endEntryList() Q_DECL_OVERRIDE; qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE; qint64 readLine(char *data, qint64 maxlen) Q_DECL_OVERRIDE; qint64 write(const char *data, qint64 len) Q_DECL_OVERRIDE; QFile::FileError error() const; QString errorString() const; bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0) Q_DECL_OVERRIDE; bool supportsExtension(Extension extension) const Q_DECL_OVERRIDE; private: QFSFileEngine m_fileEngine; }; } // namespace QInstaller #endif // REMOTEFILEENGINE_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteobject.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000007326�13253666515�0020005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "remoteobject.h" #include "protocol.h" #include "remoteclient.h" #include <QCoreApplication> #include <QElapsedTimer> #include <QThread> namespace QInstaller { RemoteObject::RemoteObject(const QString &wrappedType, QObject *parent) : QObject(parent) , dummy(0) , m_type(wrappedType) , m_socket(0) { Q_ASSERT_X(!m_type.isEmpty(), Q_FUNC_INFO, "The wrapped Qt type needs to be passed as " "argument and cannot be empty."); } RemoteObject::~RemoteObject() { if (m_socket) { if (QThread::currentThread() == m_socket->thread()) { if (m_type != QLatin1String("RemoteClientPrivate")) writeData(QLatin1String(Protocol::Destroy), m_type, dummy, dummy); } else { Q_ASSERT_X(false, Q_FUNC_INFO, "Socket running in a different Thread than this object."); } delete m_socket; } } bool RemoteObject::authorize() { if (m_socket && (m_socket->state() == QLocalSocket::ConnectedState)) return true; if (m_socket) delete m_socket; m_socket = new QLocalSocket; m_socket->connectToServer(RemoteClient::instance().socketName()); if (m_socket->waitForConnected()) { bool authorized = callRemoteMethod<bool>(QString::fromLatin1(Protocol::Authorize), RemoteClient::instance().authorizationKey()); if (authorized) return true; } delete m_socket; m_socket = 0; return false; } bool RemoteObject::connectToServer(const QVariantList &arguments) { if (!RemoteClient::instance().isActive()) return false; if (m_socket && (m_socket->state() == QLocalSocket::ConnectedState)) return true; if (!authorize()) return false; QByteArray data; QDataStream out(&data, QIODevice::WriteOnly); out << m_type; foreach (const QVariant &arg, arguments) out << arg; sendPacket(m_socket, Protocol::Create, data); m_socket->flush(); return true; } bool RemoteObject::isConnectedToServer() const { if ((!m_socket) || (!RemoteClient::instance().isActive())) return false; if (m_socket && (m_socket->state() == QLocalSocket::ConnectedState)) return true; return false; } void RemoteObject::callRemoteMethod(const QString &name) { writeData(name, dummy, dummy, dummy); } } // namespace QInstaller ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteobject.h�������������������������������������������������������������������0000664�0000000�0000000�00000012232�13253666515�0017442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTEOBJECT_H #define REMOTEOBJECT_H #include "errors.h" #include "installer_global.h" #include "protocol.h" #include <QCoreApplication> #include <QDataStream> #include <QObject> #include <QLocalSocket> namespace QInstaller { class INSTALLER_EXPORT RemoteObject : public QObject { Q_OBJECT Q_DISABLE_COPY(RemoteObject) public: RemoteObject(const QString &wrappedType, QObject *parent = 0); virtual ~RemoteObject() = 0; bool isConnectedToServer() const; void callRemoteMethod(const QString &name); template<typename T1, typename T2> void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2) { writeData(name, arg, arg2, dummy); } template<typename T1, typename T2, typename T3> void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 & arg3) { writeData(name, arg, arg2, arg3); } template<typename T> T callRemoteMethod(const QString &name) const { return callRemoteMethod<T>(name, dummy, dummy, dummy); } template<typename T, typename T1> T callRemoteMethod(const QString &name, const T1 &arg) const { return callRemoteMethod<T>(name, arg, dummy, dummy); } template<typename T, typename T1, typename T2> T callRemoteMethod(const QString &name, const T1 & arg, const T2 &arg2) const { return callRemoteMethod<T>(name, arg, arg2, dummy); } template<typename T, typename T1, typename T2, typename T3> T callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const { writeData(name, arg, arg2, arg3); while (m_socket->bytesToWrite()) m_socket->waitForBytesWritten(); QByteArray command; QByteArray data; while (!receivePacket(m_socket, &command, &data)) { if (!m_socket->waitForReadyRead(-1)) { throw Error(tr("Cannot read all data after sending command: %1. " "Bytes expected: %2, Bytes received: %3. Error: %4").arg(name).arg(0) .arg(m_socket->bytesAvailable()).arg(m_socket->errorString())); } } Q_ASSERT(command == Protocol::Reply); QDataStream stream(&data, QIODevice::ReadOnly); T result; stream >> result; Q_ASSERT(stream.status() == QDataStream::Ok); Q_ASSERT(stream.atEnd()); return result; } protected: bool authorize(); bool connectToServer(const QVariantList &arguments = QVariantList()); // Use this structure to allow derived classes to manipulate the template // function signature of the callRemoteMethod templates, since most of the // generated functions will differ in return type rather given arguments. struct Dummy {}; Dummy *dummy; private: template<typename T> bool isValueType(T) const { return true; } template<typename T> bool isValueType(T *dummy) const { // Force compiler error while passing anything different then Dummy* to the function. // It really doesn't make sense to send any pointer over to the server, so bail early. Q_UNUSED(static_cast<Dummy*> (dummy)) return false; } template<typename T1, typename T2, typename T3> void writeData(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const { QByteArray data; QDataStream out(&data, QIODevice::WriteOnly); if (isValueType(arg)) out << arg; if (isValueType(arg2)) out << arg2; if (isValueType(arg3)) out << arg3; sendPacket(m_socket, name.toLatin1(), data); m_socket->flush(); } private: QString m_type; QLocalSocket *m_socket; }; } // namespace QInstaller #endif // REMOTEOBJECT_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteserver.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000007617�13253666515�0020050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "remoteserver.h" #include "remoteserver_p.h" #include "repository.h" namespace QInstaller { /*! Constructs an remote server object with \a parent. */ RemoteServer::RemoteServer(QObject *parent) : QObject(parent) , d_ptr(new RemoteServerPrivate(this)) { Repository::registerMetaType(); // register, cause we stream the type as QVariant } /*! Destroys the remote server object. */ RemoteServer::~RemoteServer() { Q_D(RemoteServer); d->m_thread.quit(); d->m_thread.wait(); } /*! Starts the server. \note If running in debug mode, the timer that kills the server after 30 seconds without usage is not started, so the server runs in an endless loop. */ void RemoteServer::start() { Q_D(RemoteServer); if (d->m_localServer) return; #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) // avoid writing to stderr: // the parent process has redirected stderr to a pipe to work with sudo, // but is not reading anymore -> writing to stderr will block after a while. if (d->m_mode == Protocol::Mode::Production) fclose(stderr); #endif d->m_localServer = new LocalServer(d->m_socketName, d->m_key); d->m_localServer->moveToThread(&d->m_thread); connect(&d->m_thread, &QThread::finished, d->m_localServer, &QObject::deleteLater); connect(d->m_localServer, &LocalServer::newIncomingConnection, this, &RemoteServer::restartWatchdog); connect(d->m_localServer, &LocalServer::shutdownRequested, this, &QObject::deleteLater); d->m_thread.start(); if (d->m_mode == Protocol::Mode::Production) { connect(d->m_watchdog.data(), &QTimer::timeout, this, &QObject::deleteLater); d->m_watchdog->start(); } } /*! Initializes the server with \a socketName, with \a key, the key the client needs to send to authenticate with the server, and \a mode. */ void RemoteServer::init(const QString &socketName, const QString &key, Protocol::Mode mode) { Q_D(RemoteServer); d->m_socketName = socketName; d->m_key = key; d->m_mode = mode; } /*! Returns the socket name the server is listening on. */ QString RemoteServer::socketName() const { Q_D(const RemoteServer); return d->m_socketName; } /*! Returns the authorization key. */ QString RemoteServer::authorizationKey() const { Q_D(const RemoteServer); return d->m_key; } /*! Restarts the watchdog that tries to kill the server. */ void RemoteServer::restartWatchdog() { Q_D(RemoteServer); if (d->m_watchdog) d->m_watchdog->start(); } } // namespace QInstaller �����������������������������������������������������������������������������������������������������������������src/libs/installer/remoteserver.h�������������������������������������������������������������������0000664�0000000�0000000�00000004012�13253666515�0017477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTESERVER_H #define REMOTESERVER_H #include "installer_global.h" #include "protocol.h" #include <QObject> namespace QInstaller { class RemoteServerPrivate; class INSTALLER_EXPORT RemoteServer : public QObject { Q_OBJECT Q_DISABLE_COPY(RemoteServer) Q_DECLARE_PRIVATE(RemoteServer) public: explicit RemoteServer(QObject *parent = 0); ~RemoteServer(); void start(); void init(const QString &socketName, const QString &authorizationKey, Protocol::Mode mode); QString socketName() const; QString authorizationKey() const; private slots: void restartWatchdog(); private: QScopedPointer<RemoteServerPrivate> d_ptr; }; } // namespace QInstaller #endif // REMOTESERVER_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteserver_p.h�����������������������������������������������������������������0000664�0000000�0000000�00000007027�13253666515�0020027�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTESERVER_P_H #define REMOTESERVER_P_H #include "protocol.h" #include "remoteserver.h" #include "remoteserverconnection.h" #include <QHostAddress> #include <QPointer> #include <QLocalServer> #include <QTimer> namespace QInstaller { class LocalServer : public QLocalServer { Q_OBJECT Q_DISABLE_COPY(LocalServer) public: LocalServer(const QString &socketName, const QString &key) : QLocalServer(0) , m_key(key) , m_shutdown(false) { setSocketOptions(QLocalServer::WorldAccessOption); listen(socketName); } ~LocalServer() { shutdown(); } signals: void shutdownRequested(); void newIncomingConnection(); private slots: void shutdown() { m_shutdown = true; const QList<QThread *> threads = findChildren<QThread *>(); foreach (QThread *thread, threads) { thread->quit(); thread->wait(); } emit shutdownRequested(); } private: void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE { if (m_shutdown) return; RemoteServerConnection *thread = new RemoteServerConnection(socketDescriptor, m_key, this); connect(thread, &QThread::finished, thread, &QObject::deleteLater); connect(thread, &RemoteServerConnection::shutdownRequested, this, &LocalServer::shutdown); thread->start(); emit newIncomingConnection(); } private: QString m_key; bool m_shutdown; }; class RemoteServerPrivate { Q_DECLARE_PUBLIC(RemoteServer) Q_DISABLE_COPY(RemoteServerPrivate) public: explicit RemoteServerPrivate(RemoteServer *server) : q_ptr(server) , m_localServer(0) , m_key(QLatin1String(Protocol::DefaultAuthorizationKey)) , m_mode(Protocol::Mode::Debug) , m_watchdog(new QTimer) { m_watchdog->setInterval(30000); m_watchdog->setSingleShot(true); } private: RemoteServer *q_ptr; LocalServer *m_localServer; QString m_key; QString m_socketName; QThread m_thread; Protocol::Mode m_mode; QScopedPointer<QTimer> m_watchdog; }; } // namespace QInstaller #endif // REMOTESERVER_P_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteserverconnection.cpp�������������������������������������������������������0000664�0000000�0000000�00000054171�13253666515�0022125�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "remoteserverconnection.h" #include "errors.h" #include "protocol.h" #include "remoteserverconnection_p.h" #include "utils.h" #include "permissionsettings.h" #include <QCoreApplication> #include <QDataStream> #include <QLocalSocket> namespace QInstaller { RemoteServerConnection::RemoteServerConnection(qintptr socketDescriptor, const QString &key, QObject *parent) : QThread(parent) , m_socketDescriptor(socketDescriptor) , m_process(0) , m_engine(0) , m_authorizationKey(key) , m_signalReceiver(0) { setObjectName(QString::fromLatin1("RemoteServerConnection(%1)").arg(socketDescriptor)); } // Helper RAII to ensure stream data was correctly (and completely) read struct StreamChecker { StreamChecker(QDataStream *stream) : stream(stream) {} ~StreamChecker() { Q_ASSERT(stream->status() == QDataStream::Ok); Q_ASSERT(stream->atEnd()); } private: QDataStream *stream; }; void RemoteServerConnection::run() { QLocalSocket socket; socket.setSocketDescriptor(m_socketDescriptor); QScopedPointer<PermissionSettings> settings; bool authorized = false; while (socket.state() == QLocalSocket::ConnectedState) { QByteArray cmd; QByteArray data; if (!receivePacket(&socket, &cmd, &data)) { socket.waitForReadyRead(250); continue; } const QString command = QString::fromLatin1(cmd); QBuffer buf; buf.setBuffer(&data); buf.open(QIODevice::ReadOnly); QDataStream stream; stream.setDevice(&buf); StreamChecker streamChecker(&stream); if (authorized && command == QLatin1String(Protocol::Shutdown)) { authorized = false; sendData(&socket, true); socket.flush(); socket.close(); emit shutdownRequested(); return; } else if (command == QLatin1String(Protocol::Authorize)) { QString key; stream >> key; sendData(&socket, (authorized = (key == m_authorizationKey))); socket.flush(); if (!authorized) { socket.close(); return; } } else if (authorized) { if (command.isEmpty()) continue; if (command == QLatin1String(Protocol::Create)) { QString type; stream >> type; if (type == QLatin1String(Protocol::QSettings)) { QVariant application; QVariant organization; QVariant scope, format; QVariant fileName; stream >> application; stream >> organization; stream >> scope; stream >> format; stream >> fileName; if (fileName.toString().isEmpty()) { settings.reset(new PermissionSettings(QSettings::Format(format.toInt()), QSettings::Scope(scope.toInt()), organization.toString(), application .toString())); } else { settings.reset(new PermissionSettings(fileName.toString(), QSettings::Format(format.toInt()))); } } else if (type == QLatin1String(Protocol::QProcess)) { if (m_process) m_process->deleteLater(); m_process = new QProcess; m_signalReceiver = new QProcessSignalReceiver(m_process); } else if (type == QLatin1String(Protocol::QAbstractFileEngine)) { if (m_engine) delete m_engine; m_engine = new QFSFileEngine; } continue; } if (command == QLatin1String(Protocol::Destroy)) { QString type; stream >> type; if (type == QLatin1String(Protocol::QSettings)) { settings.reset(); } else if (type == QLatin1String(Protocol::QProcess)) { m_signalReceiver->m_receivedSignals.clear(); m_process->deleteLater(); m_process = 0; } else if (type == QLatin1String(Protocol::QAbstractFileEngine)) { delete m_engine; m_engine = 0; } return; } if (command == QLatin1String(Protocol::GetQProcessSignals)) { if (m_signalReceiver) { QMutexLocker _(&m_signalReceiver->m_lock); sendData(&socket, m_signalReceiver->m_receivedSignals); socket.flush(); m_signalReceiver->m_receivedSignals.clear(); } continue; } if (command.startsWith(QLatin1String(Protocol::QProcess))) { handleQProcess(&socket, command, stream); } else if (command.startsWith(QLatin1String(Protocol::QSettings))) { handleQSettings(&socket, command, stream, settings.data()); } else if (command.startsWith(QLatin1String(Protocol::QAbstractFileEngine))) { handleQFSFileEngine(&socket, command, stream); } else { qDebug() << "Unknown command:" << command; } socket.flush(); } else { // authorization failed, connection not wanted socket.close(); qDebug() << "Unknown command:" << command; return; } } } template <typename T> void RemoteServerConnection::sendData(QIODevice *device, const T &data) { QByteArray result; QDataStream returnStream(&result, QIODevice::WriteOnly); returnStream << data; sendPacket(device, Protocol::Reply, result); } void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &command, QDataStream &data) { if (command == QLatin1String(Protocol::QProcessCloseWriteChannel)) { m_process->closeWriteChannel(); } else if (command == QLatin1String(Protocol::QProcessExitCode)) { sendData(socket, m_process->exitCode()); } else if (command == QLatin1String(Protocol::QProcessExitStatus)) { sendData(socket, static_cast<qint32> (m_process->exitStatus())); } else if (command == QLatin1String(Protocol::QProcessKill)) { m_process->kill(); } else if (command == QLatin1String(Protocol::QProcessReadAll)) { sendData(socket, m_process->readAll()); } else if (command == QLatin1String(Protocol::QProcessReadAllStandardOutput)) { sendData(socket, m_process->readAllStandardOutput()); } else if (command == QLatin1String(Protocol::QProcessReadAllStandardError)) { sendData(socket, m_process->readAllStandardError()); } else if (command == QLatin1String(Protocol::QProcessStartDetached)) { QString program; QStringList arguments; QString workingDirectory; data >> program; data >> arguments; data >> workingDirectory; qint64 pid = -1; bool success = QInstaller::startDetached(program, arguments, workingDirectory, &pid); sendData(socket, qMakePair< bool, qint64>(success, pid)); } else if (command == QLatin1String(Protocol::QProcessSetWorkingDirectory)) { QString dir; data >> dir; m_process->setWorkingDirectory(dir); } else if (command == QLatin1String(Protocol::QProcessSetEnvironment)) { QStringList env; data >> env; m_process->setEnvironment(env); } else if (command == QLatin1String(Protocol::QProcessEnvironment)) { sendData(socket, m_process->environment()); } else if (command == QLatin1String(Protocol::QProcessStart3Arg)) { QString program; QStringList arguments; qint32 mode; data >> program; data >> arguments; data >> mode; m_process->start(program, arguments, static_cast<QIODevice::OpenMode> (mode)); } else if (command == QLatin1String(Protocol::QProcessStart2Arg)) { QString program; qint32 mode; data >> program; data >> mode; m_process->start(program, static_cast<QIODevice::OpenMode> (mode)); } else if (command == QLatin1String(Protocol::QProcessState)) { sendData(socket, static_cast<qint32> (m_process->state())); } else if (command == QLatin1String(Protocol::QProcessTerminate)) { m_process->terminate(); } else if (command == QLatin1String(Protocol::QProcessWaitForFinished)) { qint32 msecs; data >> msecs; sendData(socket, m_process->waitForFinished(msecs)); } else if (command == QLatin1String(Protocol::QProcessWaitForStarted)) { qint32 msecs; data >> msecs; sendData(socket, m_process->waitForStarted(msecs)); } else if (command == QLatin1String(Protocol::QProcessWorkingDirectory)) { sendData(socket, m_process->workingDirectory()); } else if (command == QLatin1String(Protocol::QProcessErrorString)) { sendData(socket, m_process->errorString()); } else if (command == QLatin1String(Protocol::QProcessReadChannel)) { sendData(socket, static_cast<qint32> (m_process->readChannel())); } else if (command == QLatin1String(Protocol::QProcessSetReadChannel)) { qint32 processChannel; data >> processChannel; m_process->setReadChannel(static_cast<QProcess::ProcessChannel>(processChannel)); } else if (command == QLatin1String(Protocol::QProcessWrite)) { QByteArray byteArray; data >> byteArray; sendData(socket, m_process->write(byteArray)); } else if (command == QLatin1String(Protocol::QProcessProcessChannelMode)) { sendData(socket, static_cast<qint32> (m_process->processChannelMode())); } else if (command == QLatin1String(Protocol::QProcessSetProcessChannelMode)) { qint32 processChannel; data >> processChannel; m_process->setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(processChannel)); } #ifdef Q_OS_WIN else if (command == QLatin1String(Protocol::QProcessSetNativeArguments)) { QString arguments; data >> arguments; m_process->setNativeArguments(arguments); } #endif else if (!command.isEmpty()) { qDebug() << "Unknown QProcess command:" << command; } } void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &command, QDataStream &data, PermissionSettings *settings) { if (!settings) return; if (command == QLatin1String(Protocol::QSettingsAllKeys)) { sendData(socket, settings->allKeys()); } else if (command == QLatin1String(Protocol::QSettingsBeginGroup)) { QString prefix; data >> prefix; settings->beginGroup(prefix); } else if (command == QLatin1String(Protocol::QSettingsBeginWriteArray)) { QString prefix; data >> prefix; qint32 size; data >> size; settings->beginWriteArray(prefix, size); } else if (command == QLatin1String(Protocol::QSettingsBeginReadArray)) { QString prefix; data >> prefix; sendData(socket, settings->beginReadArray(prefix)); } else if (command == QLatin1String(Protocol::QSettingsChildGroups)) { sendData(socket, settings->childGroups()); } else if (command == QLatin1String(Protocol::QSettingsChildKeys)) { sendData(socket, settings->childKeys()); } else if (command == QLatin1String(Protocol::QSettingsClear)) { settings->clear(); } else if (command == QLatin1String(Protocol::QSettingsContains)) { QString key; data >> key; sendData(socket, settings->contains(key)); } else if (command == QLatin1String(Protocol::QSettingsEndArray)) { settings->endArray(); } else if (command == QLatin1String(Protocol::QSettingsEndGroup)) { settings->endGroup(); } else if (command == QLatin1String(Protocol::QSettingsFallbacksEnabled)) { sendData(socket, settings->fallbacksEnabled()); } else if (command == QLatin1String(Protocol::QSettingsFileName)) { sendData(socket, settings->fileName()); } else if (command == QLatin1String(Protocol::QSettingsGroup)) { sendData(socket, settings->group()); } else if (command == QLatin1String(Protocol::QSettingsIsWritable)) { sendData(socket, settings->isWritable()); } else if (command == QLatin1String(Protocol::QSettingsRemove)) { QString key; data >> key; settings->remove(key); } else if (command == QLatin1String(Protocol::QSettingsSetArrayIndex)) { qint32 i; data >> i; settings->setArrayIndex(i); } else if (command == QLatin1String(Protocol::QSettingsSetFallbacksEnabled)) { bool b; data >> b; settings->setFallbacksEnabled(b); } else if (command == QLatin1String(Protocol::QSettingsStatus)) { sendData(socket, settings->status()); } else if (command == QLatin1String(Protocol::QSettingsSync)) { settings->sync(); } else if (command == QLatin1String(Protocol::QSettingsSetValue)) { QString key; QVariant value; data >> key; data >> value; settings->setValue(key, value); } else if (command == QLatin1String(Protocol::QSettingsValue)) { QString key; QVariant defaultValue; data >> key; data >> defaultValue; sendData(socket, settings->value(key, defaultValue)); } else if (command == QLatin1String(Protocol::QSettingsOrganizationName)) { sendData(socket, settings->organizationName()); } else if (command == QLatin1String(Protocol::QSettingsApplicationName)) { sendData(socket, settings->applicationName()); } else if (!command.isEmpty()) { qDebug() << "Unknown QSettings command:" << command; } } void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QString &command, QDataStream &data) { if (command == QLatin1String(Protocol::QAbstractFileEngineAtEnd)) { sendData(socket, m_engine->atEnd()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineCaseSensitive)) { sendData(socket, m_engine->caseSensitive()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineClose)) { sendData(socket, m_engine->close()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineCopy)) { QString newName; data >>newName; sendData(socket, m_engine->copy(newName)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineEntryList)) { qint32 filters; QStringList filterNames; data >>filters; data >>filterNames; sendData(socket, m_engine->entryList(static_cast<QDir::Filters> (filters), filterNames)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineError)) { sendData(socket, static_cast<qint32> (m_engine->error())); } else if (command == QLatin1String(Protocol::QAbstractFileEngineErrorString)) { sendData(socket, m_engine->errorString()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineFileFlags)) { qint32 flags; data >>flags; flags = m_engine->fileFlags(static_cast<QAbstractFileEngine::FileFlags>(flags)); sendData(socket, static_cast<qint32>(flags)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineFileName)) { qint32 file; data >>file; sendData(socket, m_engine->fileName(static_cast<QAbstractFileEngine::FileName> (file))); } else if (command == QLatin1String(Protocol::QAbstractFileEngineFlush)) { sendData(socket, m_engine->flush()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineHandle)) { sendData(socket, m_engine->handle()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineIsRelativePath)) { sendData(socket, m_engine->isRelativePath()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineIsSequential)) { sendData(socket, m_engine->isSequential()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineLink)) { QString newName; data >>newName; sendData(socket, m_engine->link(newName)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineMkdir)) { QString dirName; bool createParentDirectories; data >>dirName; data >>createParentDirectories; sendData(socket, m_engine->mkdir(dirName, createParentDirectories)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineOpen)) { qint32 openMode; data >>openMode; sendData(socket, m_engine->open(static_cast<QIODevice::OpenMode> (openMode))); } else if (command == QLatin1String(Protocol::QAbstractFileEngineOwner)) { qint32 owner; data >>owner; sendData(socket, m_engine->owner(static_cast<QAbstractFileEngine::FileOwner> (owner))); } else if (command == QLatin1String(Protocol::QAbstractFileEngineOwnerId)) { qint32 owner; data >>owner; sendData(socket, m_engine->ownerId(static_cast<QAbstractFileEngine::FileOwner> (owner))); } else if (command == QLatin1String(Protocol::QAbstractFileEnginePos)) { sendData(socket, m_engine->pos()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineRead)) { qint64 maxlen; data >> maxlen; QByteArray byteArray(maxlen, '\0'); const qint64 r = m_engine->read(byteArray.data(), maxlen); sendData(socket, qMakePair<qint64, QByteArray>(r, byteArray)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineReadLine)) { qint64 maxlen; data >> maxlen; QByteArray byteArray(maxlen, '\0'); const qint64 r = m_engine->readLine(byteArray.data(), maxlen); sendData(socket, qMakePair<qint64, QByteArray>(r, byteArray)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineRemove)) { sendData(socket, m_engine->remove()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineRename)) { QString newName; data >>newName; sendData(socket, m_engine->rename(newName)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineRmdir)) { QString dirName; bool recurseParentDirectories; data >>dirName; data >>recurseParentDirectories; sendData(socket, m_engine->rmdir(dirName, recurseParentDirectories)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineSeek)) { quint64 offset; data >>offset; sendData(socket, m_engine->seek(offset)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineSetFileName)) { QString fileName; data >>fileName; m_engine->setFileName(fileName); } else if (command == QLatin1String(Protocol::QAbstractFileEngineSetPermissions)) { uint perms; data >>perms; sendData(socket, m_engine->setPermissions(perms)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineSetSize)) { qint64 size; data >>size; sendData(socket, m_engine->setSize(size)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineSize)) { sendData(socket, m_engine->size()); } else if ((command == QLatin1String(Protocol::QAbstractFileEngineSupportsExtension)) || (command == QLatin1String(Protocol::QAbstractFileEngineExtension))) { // Implemented client side. } else if (command == QLatin1String(Protocol::QAbstractFileEngineWrite)) { QByteArray content; data >> content; sendData(socket, m_engine->write(content.data(), content.size())); } else if (command == QLatin1String(Protocol::QAbstractFileEngineSyncToDisk)) { sendData(socket, m_engine->syncToDisk()); } else if (command == QLatin1String(Protocol::QAbstractFileEngineRenameOverwrite)) { QString newFilename; data >> newFilename; sendData(socket, m_engine->renameOverwrite(newFilename)); } else if (command == QLatin1String(Protocol::QAbstractFileEngineFileTime)) { qint32 filetime; data >> filetime; sendData(socket, m_engine->fileTime(static_cast<QAbstractFileEngine::FileTime> (filetime))); } else if (!command.isEmpty()) { qDebug() << "Unknown QAbstractFileEngine command:" << command; } } } // namespace QInstaller �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteserverconnection.h���������������������������������������������������������0000664�0000000�0000000�00000005067�13253666515�0021572�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTESERVERCONNECTION_H #define REMOTESERVERCONNECTION_H #include <QPointer> #include <QThread> #include <QtCore/private/qfsfileengine_p.h> QT_BEGIN_NAMESPACE class QProcess; class QIODevice; QT_END_NAMESPACE namespace QInstaller { class PermissionSettings; class QProcessSignalReceiver; class RemoteServerConnection : public QThread { Q_OBJECT Q_DISABLE_COPY(RemoteServerConnection) public: RemoteServerConnection(qintptr socketDescriptor, const QString &authorizationKey, QObject *parent); void run() Q_DECL_OVERRIDE; signals: void shutdownRequested(); private: template <typename T> void sendData(QIODevice *device, const T &arg); void handleQProcess(QIODevice *device, const QString &command, QDataStream &data); void handleQSettings(QIODevice *device, const QString &command, QDataStream &data, PermissionSettings *settings); void handleQFSFileEngine(QIODevice *device, const QString &command, QDataStream &data); private: qintptr m_socketDescriptor; QProcess *m_process; QFSFileEngine *m_engine; QString m_authorizationKey; QProcessSignalReceiver *m_signalReceiver; }; } // namespace QInstaller #endif // REMOTESERVERCONNECTION_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/remoteserverconnection_p.h�������������������������������������������������������0000664�0000000�0000000�00000011606�13253666515�0022105�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REMOTESERVERCONNECTION_P_H #define REMOTESERVERCONNECTION_P_H #include "protocol.h" #include <QMutex> #include <QProcess> #include <QVariant> namespace QInstaller { class QProcessSignalReceiver : public QObject { Q_OBJECT friend class RemoteServerConnection; private: explicit QProcessSignalReceiver(QProcess *process) : QObject(process) { connect(process, &QIODevice::bytesWritten, this, &QProcessSignalReceiver::onBytesWritten); connect(process, &QIODevice::aboutToClose, this, &QProcessSignalReceiver::onAboutToClose); connect(process, &QIODevice::readChannelFinished, this, &QProcessSignalReceiver::onReadChannelFinished); connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(onError(QProcess::ProcessError))); connect(process, &QProcess::readyReadStandardOutput, this, &QProcessSignalReceiver::onReadyReadStandardOutput); connect(process, &QProcess::readyReadStandardError, this, &QProcessSignalReceiver::onReadyReadStandardError); connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(onFinished(int,QProcess::ExitStatus))); connect(process, &QIODevice::readyRead, this, &QProcessSignalReceiver::onReadyRead); connect(process, &QProcess::started, this, &QProcessSignalReceiver::onStarted); connect(process, &QProcess::stateChanged, this, &QProcessSignalReceiver::onStateChanged); } private Q_SLOTS: void onBytesWritten(qint64 count) { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalBytesWritten)); m_receivedSignals.append(count); } void onAboutToClose() { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalAboutToClose)); } void onReadChannelFinished() { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadChannelFinished)); } void onError(QProcess::ProcessError error) { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalError)); m_receivedSignals.append(static_cast<int> (error)); } void onReadyReadStandardOutput() { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadyReadStandardOutput)); } void onReadyReadStandardError() { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadyReadStandardError)); } void onFinished(int exitCode, QProcess::ExitStatus exitStatus) { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalFinished)); m_receivedSignals.append(exitCode); m_receivedSignals.append(static_cast<int> (exitStatus)); } void onReadyRead() { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalReadyRead)); } void onStarted() { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalStarted)); } void onStateChanged(QProcess::ProcessState newState) { QMutexLocker _(&m_lock); m_receivedSignals.append(QLatin1String(Protocol::QProcessSignalStateChanged)); m_receivedSignals.append(static_cast<int>(newState)); } private: QMutex m_lock; QVariantList m_receivedSignals; }; } // namespace QInstaller #endif // REMOTESERVERCONNECTION_P_H ��������������������������������������������������������������������������������������������������������������������������src/libs/installer/replaceoperation.cpp�������������������������������������������������������������0000664�0000000�0000000�00000005617�13253666515�0020660�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "replaceoperation.h" #include <QtCore/QDir> #include <QtCore/QFile> #include <QtCore/QTextStream> using namespace QInstaller; ReplaceOperation::ReplaceOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Replace")); } void ReplaceOperation::backup() { } bool ReplaceOperation::performOperation() { // Arguments: // 1. filename // 2. Source-String // 3. Replace-String if (!checkArgumentCount(3)) return false; const QStringList args = arguments(); const QString fileName = args.at(0); const QString before = args.at(1); const QString after = args.at(2); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { setError(UserDefinedError); setErrorString(tr("Cannot open file \"%1\" for reading: %2").arg( QDir::toNativeSeparators(fileName), file.errorString())); return false; } QTextStream stream(&file); QString replacedFileContent = stream.readAll(); file.close(); if (!file.open(QIODevice::WriteOnly)) { setError(UserDefinedError); setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(fileName), file.errorString())); return false; } stream.setDevice(&file); stream << replacedFileContent.replace(before, after); file.close(); return true; } bool ReplaceOperation::undoOperation() { // Need to remove settings again return true; } bool ReplaceOperation::testOperation() { return true; } �����������������������������������������������������������������������������������������������������������������src/libs/installer/replaceoperation.h���������������������������������������������������������������0000664�0000000�0000000�00000003346�13253666515�0020322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REPLACEOPERATION_H #define REPLACEOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT ReplaceOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::ReplaceOperation) public: explicit ReplaceOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } // namespace QInstaller #endif // REPLACEOPERATION_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/repository.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000017704�13253666515�0017543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "repository.h" #include "filedownloaderfactory.h" #include <QDataStream> #include <QFileInfo> #include <QStringList> namespace QInstaller { /* Constructs an invalid Repository object. */ Repository::Repository() : m_default(false) , m_enabled(false) , m_compressed(false) { registerMetaType(); } /*! Constructs a new repository by using all fields of the given repository \a other. */ Repository::Repository(const Repository &other) : m_url(other.m_url) , m_default(other.m_default) , m_enabled(other.m_enabled) , m_username(other.m_username) , m_password(other.m_password) , m_displayname(other.m_displayname) , m_compressed(other.m_compressed) { registerMetaType(); } /*! Constructs a new repository by setting its address to \a url and its default and \a compressed states. */ Repository::Repository(const QUrl &url, bool isDefault, bool compressed) : m_url(url) , m_default(isDefault) , m_enabled(true) , m_compressed(compressed) { registerMetaType(); } /*! Constructs a new repository by setting its address to \a repositoryUrl as string and its \a compressed state. Note: user and password can be inside the \a repositoryUrl string: http://user:password@repository.url */ Repository Repository::fromUserInput(const QString &repositoryUrl, bool compressed) { QUrl url = QUrl::fromUserInput(repositoryUrl); const QStringList supportedSchemes = KDUpdater::FileDownloaderFactory::supportedSchemes(); if (!supportedSchemes.contains(url.scheme()) && QFileInfo(url.toString()).exists()) url = QLatin1String("file:///") + url.toString(); QString userName = url.userName(); QString password = url.password(); url.setUserName(QString()); url.setPassword(QString()); Repository repository(url, false, compressed); repository.setUsername(userName); repository.setPassword(password); return repository; } /*! Returns true if the repository URL is valid; otherwise returns false. Note: The URL is simply run through a conformance test. It is not checked that the repository actually exists. */ bool Repository::isValid() const { return m_url.isValid(); } /*! Returns true if the repository was set using the package manager configuration file; otherwise returns false. */ bool Repository::isDefault() const { return m_default; } /*! Returns the URL of the repository. By default an invalid \sa QUrl is returned. */ QUrl Repository::url() const { return m_url; } /*! Sets the repository URL to the one specified at \a url. */ void Repository::setUrl(const QUrl &url) { m_url = url; } /*! Returns whether the repository is enabled and used during information retrieval. */ bool Repository::isEnabled() const { return m_enabled; } /*! Sets this repository to \n enabled state and thus to use this repository for information retrieval or not. */ void Repository::setEnabled(bool enabled) { m_enabled = enabled; } /*! Returns the user name used for authentication. */ QString Repository::username() const { return m_username; } /*! Sets the user name for authentication to be \a username. */ void Repository::setUsername(const QString &username) { m_username = username; } /*! Returns the password used for authentication. */ QString Repository::password() const { return m_password; } /*! Sets the password for authentication to be \a password. */ void Repository::setPassword(const QString &password) { m_password = password; } /*! Returns the Name for the repository to be displayed instead of the URL */ QString Repository::displayname() const { return m_displayname.isEmpty() ? m_url.toString() : m_displayname; } /*! Sets the DisplayName of the repository to \a displayname. */ void Repository::setDisplayName(const QString &displayname) { m_displayname = displayname; } /*! Returns true if repository is compressed */ bool Repository::isCompressed() const { return m_compressed; } /*! Sets this repository to \a compressed state to know weather the repository needs to be uncompressed before use. */ void Repository::setCompressed(bool compressed) { m_compressed = compressed; } /*! Compares the values of this repository to \a other and returns true if they are equal (same server, default state, enabled state as well as username and password). \sa operator!=() */ bool Repository::operator==(const Repository &other) const { return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled && m_username == other.m_username && m_password == other.m_password && m_displayname == other.m_displayname; } /*! Returns true if the \a other repository is not equal to this repository; otherwise returns false. Two repositories are considered equal if they contain the same elements. \sa operator==() */ bool Repository::operator!=(const Repository &other) const { return !(*this == other); } /*! Assigns the values of repository \a other to this repository. */ const Repository &Repository::operator=(const Repository &other) { if (this == &other) return *this; m_url = other.m_url; m_default = other.m_default; m_enabled = other.m_enabled; m_username = other.m_username; m_password = other.m_password; m_displayname = other.m_displayname; m_compressed = other.m_compressed; return *this; } void Repository::registerMetaType() { qRegisterMetaType<Repository>("Repository"); qRegisterMetaTypeStreamOperators<Repository>("Repository"); } QDataStream &operator>>(QDataStream &istream, Repository &repository) { QByteArray url, username, password, displayname, compressed; istream >> url >> repository.m_default >> repository.m_enabled >> username >> password >> displayname; repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url))); repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username))); repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password))); repository.setDisplayName(QString::fromUtf8(QByteArray::fromBase64(displayname))); return istream; } QDataStream &operator<<(QDataStream &ostream, const Repository &repository) { return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled << repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64() << repository.m_displayname.toUtf8().toBase64(); } } ������������������������������������������������������������src/libs/installer/repository.h���������������������������������������������������������������������0000664�0000000�0000000�00000006314�13253666515�0017203�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REPOSITORY_H #define REPOSITORY_H #include "installer_global.h" #include <QtCore/QMetaType> #include <QtCore/QUrl> namespace QInstaller { class INSTALLER_EXPORT Repository { public: explicit Repository(); Repository(const Repository &other); explicit Repository(const QUrl &url, bool isDefault, bool compressed = false); static void registerMetaType(); static Repository fromUserInput(const QString &repositoryUrl, bool compressed = false); bool isValid() const; bool isDefault() const; QUrl url() const; void setUrl(const QUrl &url); bool isEnabled() const; void setEnabled(bool enabled); QString username() const; void setUsername(const QString &username); QString password() const; void setPassword(const QString &password); QString displayname() const; void setDisplayName(const QString &displayname); bool isCompressed() const; void setCompressed(bool compressed); bool operator==(const Repository &other) const; bool operator!=(const Repository &other) const; uint qHash(const Repository &repository); const Repository &operator=(const Repository &other); friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, Repository &repository); friend INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const Repository &repository); private: QUrl m_url; bool m_default; bool m_enabled; QString m_username; QString m_password; QString m_displayname; bool m_compressed; }; inline uint qHash(const Repository &repository) { return qHash(repository.url()); } INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, Repository &repository); INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const Repository &repository); } // namespace QInstaller Q_DECLARE_METATYPE(QInstaller::Repository) #endif // REPOSITORY_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0016621�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/files-to-patch-macx-emb-arm-qt5����������������������������������������0000664�0000000�0000000�00000000112�13253666515�0024330�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������bin/qmake bin/lrelease bin/qdoc %% *.la *.prl *.pc *.pri *.cmake ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/install.png������������������������������������������������������������0000664�0000000�0000000�00000000414�13253666515�0020774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������;֕J���bKGD�ĸ�ĸ�ĸ ―§“��� pHYs�� �� �šœ���tIMEÞ  "&Ģ(ãá���iTXtComment�����Created with GIMPd.e���pIDAT(Ïc` �0Béĸč%Yó\ē:ÝŠõ0ö•ŌۍØÔ0á3žĢā>y|6Ē‹Ąŧ€…0ö Œ mFVČQðŋ›&ŽĄ‡îT“ß*ĸąyĶo€c+Uâų?ĀbÅВķÉ�D:)uÉ=õõ����IENDŪB`‚����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/installer.icns���������������������������������������������������������0000664�0000000�0000000�00000350320�13253666515�0021477�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������icns�ÐÐis32��·ƒ�1rlr1ˆ�8‘Ž‘8ˆ�@ĻĪĻ@…� FjrtĄĢĒl`V-� “ŅÜŠŽĨ’žŧŠ\� ĨŧŽz†‰f� #ąÉ\UpjoY`p €� 0ŊĘÆJ?Y?GŽo€� :Ŧšš™48Īs€� GŊđĀŧĨ^›ĨĶ”{€� SķģģēÎÚšœ™‚'€� `ÄĮĩĄ˜’•ž›Š-€� fĩķĖÆŧļļĐ Š5€� XœĄ›ĨŦ§ ‘Ž€3€� ,b;Wc]CBDitf€�!€ %#�ƒ�1rlr1ˆ�8‘Ž‘8ˆ�@ĻĪĻ@…� FjrtĄĢĒl`V-� “ŅÜŠŽĨ’žŧŠ\� ĨŧŽ‚z…‰f� #ąÉ\UpjoY_p €� 0ŊĘÆJ@X?GŽo€� :Ŧšš™58Īs€� GŊđĀŧĨ^›ĨĶ”{€� SķģģēÎÚšœ™‚'€� `ÄĮĩĄ˜’•ž›Š-€� fĩķĖÆŧļļĐ Š5€� XœĄ›ĨŦ§ €3€� ,b;Wc]CBDivf€�!€ %#�ƒ�1rlq1ˆ�8‘Ž‘8ˆ�@ĻĪĻ@…� IlttĄĢĒmbX-� —ÕÞŠŽĨ’ĄÁŪ_� Đŋ‘€Ž}‡k� )·Î^TpkpX`–s €� 0īÏĖL>Z?Gģ•s€� =ēŋŋž68•Ļ”x€� GĩŋÅĀŦa Ŧޚ€ €� V―đđļŅßĀĄŸ•†'€� dĘĖŧĐ ˜–šĪĄ‘0€� iŧŧÐËĀ――ŊĶŽ‡8€� ]Ē§ĒŠŊŠĨ•”˜‡5€� 2kCbogKJOokr!€� %"" ' �s8mk�������]·ŧ·]���������væëæv������� |ðõð| ����T‚ŧũúũŧ‚}?���ģųúüĸĸĸüúö‹��� Æĸĸĸĸĸĸĸĸý ���,Ôĸĸĸĸĸĸĸĸþē���;āĸĸĸĸĸĸĸĸĸÃ��KęĸĸĸĸĸĸĸĸĸŌ)��]ņĸĸĸĸĸĸĸĸĸÞ7��qöĸĸĸĸĸĸĸĸĸčH��…úĸĸĸĸĸĸĸĸĸðZ� ™ýĸĸĸĸĸĸĸĸĸõn�ýĸĸĸĸĸĸĸĸĸös� …õúúúúúúúúúë]�G’˜˜˜˜˜˜˜˜˜Š0il32��ω�)bf€hfb%”�0{}€}{0”�+ˆ†‰Š‰†ˆ+”�0ĨŽēēģŽĨ.”�-žĒĢĒž+”�-™ŸĢĨ̟š-�Z^y{{wešĢĪĶĢ̚_iki[M4‡�*ŪĩØßÞᝋĶĒĢĒĨŽŒÁđšĻža‡�8ÆČŽŌÃՉˆŸž Œ|š­Ļwķk‡�GÎąĻäČ݈}–””’–}…ÎđÁgŪv †�VŌÃÞĒ™Šmv‹‡‰ˆ‰z`•ƒmŠ~†�cÓÏÛ:_|Xqzy{y{q^€dOk̇†�tÖĮÕš>aud€k lietc<Đxš"†�ÖĮČÜĀ6LaZ^]^Z_M8œĮp“—.…�‰ÖČĮŧÍē5BUMPMSC4ŠĐuŒ™4…�Ž…ŋĀšžŸ.5I?I5-ŽˆŦ„O€@…� ĀĪÆÁķžŽ‚-%B$.y„‘ž§Ž›I…�%ŽķĨÍ―ŋŧŦ‡,2ƒŽĄĪĄĢ}œU…�1ī•đÄŧššđ‘‰O”†xģ  ›Ļh\…�=ž‹ĩÁļđ·ĐâÞĐŊđÖĪ ž—Ķk‰f „�KÂĶĒÂļīģēĶĖäÞÕŦœ žš™˜u’l„�VÂļÎąģķŊąŪž››•š ›ššŽ—”’t„�dÃŊČðĻĨąŪŽŽĐĨ̟ž–ƒĢŊ‰Žy„�oÁīĐŋņÄ ™žĄžž›“Ž“ŧŧ„‹‰}%ƒ� zÄĩīĻĐŲįÕŧĐĄœžĻļģŧĨ…Š‹+ƒ�}n‘ĩ§Ī”žđÏÖÛÚÕŌī}|„†Jj4ƒ�€ŠŋËÃÃÄ―°ŦĻŽŠĐĒĪŽŪĻĨĢĒ‘ˆ3„�;vyCSZ_emxvS[W[YW]€yxw~h„�#Yf(6KPSW[\8A6886>\qySfN „� Yf?4?KUZ^^GI€EDK_jl\mC„�€�‚€ Ą�‰�)bffhhfb%”�0{}€}{0”�+ˆ†‰Š‰†ˆ+”�.ĨŽēģģŽĨ.”�+žĒĢĒž+”�-™ŸĢĨ̟›-�Z^y{{we™ĢĢĶĢĄš_iki[M4‡�*ŪĩØßÞᝌĶĒĢĒĨŽÁđšĻža‡�8ÆČŽŌÃՉˆ žŸ‹|š­Ļwķk‡�GÎąĻäČ݈}–’”’–~…ÎđÂgŪv †�VŌÃÞ̚Šmv‹‡‰‡‰z`•ƒmŠ~†�cÓÏÛ:`|Xqz{z{{q^dOk̇†�tÖĮÕš>cudikmiidtd<Đxš"†�ÖĮČÜĀ6LaZ^]\ZaL7œĮp“—.…�‰ÖČĮŧÍē5BUMPMSC4ŠĐuŒ™4…�Ž…ŋĀšžŸ/5I?I5-ŽˆŦ„O€@…� ĀĪÆÁķžŽ‚-%B$.y„‘ž§Ž›I…�%ŽķĨÍ―ŋŧŦ‡,0„ŽĄĪĄĢ}œU…�1ī•đÄŧššđ‘‰O“†xģ  ›Ļh\…�=ž‹ĩÁļđ·ĐâÞĐŊđÖĪ ž—Ķk‰f „�KÂĶĒÂļīģēĶĖäÞÕŦœ žš™˜u’l„�VÂļÎąģķŊąŪž››•š ›ššŽ—”’t„�dÃŊČðĻĨąŪŽŽĐĨ̟ž–ƒĢŊ‰Žy„�oÁīĐŋņÄ ™žĄžž›“Ž“ŧŧ„‹‰}%ƒ� zÄĩīĻĐŲįÕŧĐĄœžĻļģŧĨ…Š‹+ƒ�}n‘ĩ§Ī”žđÏÖÛÚÕŌī}|„†Jj4ƒ�€ŠŋËÃÃÄ―°ŦĻŽŠĐĒĪŽŽĻĨĢĒ‘ˆ3„�;vyCSZ_emxvS[W[ZW]€yxw~h„�#Yf(6KPSW[\8A6866>\rzSfN „�Yf?4?KUZ^^GIEFEEK_ln\mC„�€�‚€ Ą�‰�)bf€hfb%”�0{}€}{0”�+ˆ†‰Š‰†ˆ+”�.ĨŽēģģŽĨ.”�-žĒĢĒž+”�-™ŸĢĨ̟š-�Z`}{g™ĢĪĨĪ̚`€m]O9‡�*ģžÞåãæ ‹ĶĒĢĒĨސĮĀĀŽĨe‡�=ĘϰŅÁՈ‰ ž ŠĀą­|žn‡�GÓķŪåČ߈}–’”’–}ˆÔūĮlģy †�YŲĮā͜Žnv‰‰Š‡‹wc˜‡’pŊ„†�eÚÕā8^~Xq|xz{zq\dNqό†�xÜÍÚŋ>bvdkkmkietc;Ŋ}Ÿ”"†�„ÜÍÍäÆ6JaZ_]^Z_L8ĒÎs™.…�ÛÍÍŋŌđ5AUNONUC6ĢąŊ{‘ 7…�’‰ÃÅĀĢĒ’.5I?I5-…“°ŠT†C…�ĨÆĐËĮžĢ’†.%B$/~ˆ–ĪŪ“‡ĄK…�+ēŧŠÓÃÄÁąŠ-2‡’ĶЧĒЂĢW…�5š›ŋĘÁĀŋū˜ĒRšŒ}đĨ§ Ūm“`…�@ÁŧÉŋū―ŊæáŪķūÚŠĶĶΜŽpi „�MČŽĻĮūš·ļŦÏįāÜēĒĶĒ Ÿžz˜q„�YČ―Ó·đ―īĩķĶĄĄ› ĶĪ Ą”š˜y„�gÉĩÎņŊŦ·īēą­ŦĐĨĢĄœ‹Đģ”„�sĮšŊÃņČĶĄĪ§ĨĨĄš•”™ÁÁŒ‘ƒ%ƒ� ~ÉŧšŪŊÞëŨŋ°§ĒĢŽ―šĀŦ‹•ˆ.ƒ�t—ŧ­ŽœĢūŌŨÜÝØÔš“ƒ‚ІŒQp7ƒ�†ŊÄÏÉĮČÁķą­ąąŪĐϰēŪĻǧ–8„�@ƒH\cjpw‚Zc^aa^f‡‘”~ˆo„�&es->UZ_bhg?J=??=DtURmpW „�drE:EV_fjhNRKLLKRqMHnxI„�€‚€ $Ą�l8mk�������������>‚……………‚>���������������������pÚßßßßßÚp���������������������~ðõõõõõð~���������������������{ðõõõôõð{���������������������wïõõõõõïw���������������� xðõõõõõïw ����������^€ƒšũúúúúúũđƒz6���������+Ėųųųųųüĸĸĸĸĸĸĸüųųųųóƒ���������;Þĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸü› ��������Kčĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸþ­��������\ïĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸū��������oõĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĖ'��������‚ųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŲ4�������� •üĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸäC�������ĻýĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸėT�������đþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸóg�������$Éĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸũz�������0Öĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸû �������?áĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸý ������Pëĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸþē������cņĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ ������vöĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŅ,������ ŠúĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÝ;������žüĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸčK�����°þĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸï^����� Āĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõp�����#ŋþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸôr����� üĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸįT�����ŒųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÜA����� wíũũũũũũøøøøøøøøøøøøøøũĘ4�����@ŸŸŸŸŸŸ              r�����*222222222222222222220! ���it32��gh����Ž�€<˜;<Û�Kc™|bKÛ�hž™™žhÛ�“Īŧķ‘ĩķŧĢ“Û�:>€G„F€EDEƒFGGF>:Û�!Vœ—™™›žŸĄĒĪĨĶĻĐŠŠĐĻĶĨĪĒĄŸž›™™—TÛ�!Íąģĩī··ļđššžž―ūū―žžŧšđļ··īĩģąÍ~Û�}―œ€ž€žœūzÛ�|ŋĄƒĢ‹ĪƒĢĄŋyÛ�|ūĄ†Ģ…ΆĢĄūyÛ�{― €Ē‘̀Ǡ―xÛ�zŧ ‚ĒĢ‚Ē ―xÛ�zšŸĄ…Ē…Ģ†ĒŸžvÛ�x🡕ĒĄŸđuÛ�x·ž‚ĄĒ‚ĄžđtÛ�wĩ€ ƒĄ…ĒƒĄ€ ķsÛ�vĩ€Ÿ  Ą  €ŸĩrÛ�uģ›žƒŸ‰ ƒŸž›ģqÛ�t皜žŸžœšēpÛ�q°™›€œ�ž…Ÿ�ž€œ›™°oÛ�rۘ›œˆ€œ›˜ŪnÛ�qŽ—š‚›�œƒ›�œƒ›€š™ŽmÃ�ŌÎÍŨÔŨÐÏŌŌŅŅÐÐÏπÎ�̀ĖË^nŦ–˜€™ƒš„›‚š€™ ˜–Ŧl[šđļļ·ķĩĩīģ ēąŪŠĐŪŠ°ŽĐÆŠ� ‘ÖŌ҇O|ÏŅÓӁŅÐÎÐρÎÍËumĻ•——˜‚™šƒ™€˜ ——–Đjpžŧšđļļ··€ķ ĩĩīģŊŦĻj8]Ķ­ĩšĐ�~ÛŲ–!‹ÝÔÕÔÓŌŌ€Ņ ÐÎÎÏÎÎÍukĶ“––‚—‰˜‚— ––“§ip―ŧšđđļ··€ķ ĩĩīēŊ°€(hīģ_Ļ� X‹ß۝3ƒIœÝ€ÖÔӀŌŅЀπÎujĢ“”€•–‡—–€• ”“Ģhpžŧšđđļ··€ķĩīģģ°ą>…Huģķj5§�WšâÖÜžÎÛÕÕÔÔÓÓŌŌŅ€Ð�ρÎuiĒ‘’’“€”‰•€”“’’‘Ēgoŧ€š��ķ€ĩ īīēą°īŠĪ§°­ķv7§� VĒáŨŨÝÜŲÕØŲÕԀÓŌŌŅŅЁÏÎth ‘‚’“‚’‘  eožŧššđđļ ķķ――ģąģąąŪ­ķ6§�V­āŨŨÖÕÔŨĘĐŅÓŌÓŌÔŌÓŌŅÐÏŌÐÐufž€Ž€€‡‘€€€Ž ždnšđļļ·ķķĩīģąĩĶ·ēŊŪŪ­Ŧē‹7§�XžÞÖցÕ}yŨŌÔÄÁŊšīķšļžąķēgeœŒŒ‚އ‚Ž€Œœbnđ€ļ·ķĩīģēģļJOŊ°ŪŪŽŠ°–:§�[ĮÚրÕØÏh‡ØŅÔÄšĪáīÉÛâåŽŲÐxcšˆ€‹€Œ‹€Œ€‹Ššbnđļļ·ķĩīģēēąŧ[&Ģē­ŽŽŦŪĄ@§�aŅŲրÕØÉd’ÚŅÔÂĩąŨÉĖÓŅŌÉÏÏub˜…ˆ‰‰€Š‹‹€Š ‰‰ˆ…˜anđļļ·ķ€ĩīģ€ēąŧf%›ī­€ŦŽĶC§�eÔŨÕŲĀ_˜ÚÐŌĀīŪŋūđÃū°Á―ēka•…††‡€ˆ…‰€ˆ‡ ††…•_mđļ·ķķĩĩī€ģēą°ŧm"īŽŽŦĐŦĐH§�nŲÖÕÕÔÔŲ·[žÚÐÐūģąđļšđīŧ·ķši`”‚„„€…†€…„„‚”^mļ·ķķĩĩīīģģēēą°šx„ĩŽŦŠĐŠŊQ§�{݀ÔÓÓÚ­X§ŲÐÐÁąžÃŋÁ·īŋ―ļķn_’€‚‚€ƒ„€ƒ ‚‚€’]mļ·ķķĩĩī€ģēēą°ļƒ yģŠŦŠĐĐē\Ķ�U‰ß€ÓŌŌÛĶY°ŨÐŌÃķŊĩķĩą°īīēŊf]Ž}€€‚‰‚‚€}[l··ķ€ĩīģēē€ą°ķ"sī‚Đąi6Ĩ�T—ß€Ó ŌŌڟZļÖÏŌŋŋ€―žū‚―j\Ž{}€~‡€€~}{ŽZk·ķĩīģē€ą°Ŋī—&hīĐϰt5Ĩ�R Þ€Ó ŌŅŲ‘WšÓÎÍπÎ�Í€Ë ĘÉÉČp[Œy{{|}ƒ~}|{{yŒYkĩķ€ĩīģģē€ą°ŊēŸ*\īŠĐ€ĻŊ|4Ĩ�TŦÜÓŌŌŅÐ֋ŠÞÕĖĖ€Í�Ė€ĘÉȀĮnZŠwyyzŒ{€z yyw‹Xiģ°ēĩĩģ€ēąą°ŊŊēĒ.QēŦĐϧ§Ūˆ5Ĩ�Vđ؃ŅÃëþũúų†ø‹Y‡uwxyx wu‰WŒóōÅąī€ē ą°°ŊŊŪŊĶ6FŪŠ€Ļ§Š’8Ĩ�YÃւŅÐÐâūŸĶœšš™—•’‘‹‹‘QT…r€u€v‹wv uur‡SS’emprsvwxyz{„ƒ“?=ĻЧĶĨĨĻ›=Ĩ�_ËÕŅŅ€ÐÏÏːixve`_]ZWTQMKHK+R„pr€st‡ut€srp…P,KHKNQTW\]a`evxj„H8ĨЧĶĨĨ§ AĨ�dŅÔŅŅЁÏΜtz‹‰wrsqnliecae6N‚oqrqo„L7daceilnpssv‡‹{s‘S2ŸŠ€ĶĨĨĢFĨ�lŨÓŅÐЁÏÎΈ‡Š—–ˆ‚}xwsqw?Kmo‚p‹q‚pomI@wqswy|ƒ‡–˜‹‡ˆš]+—Ŧ€ĶĨĪĻPĨ�yÚŅЁπПZ\YT_`URRQP€NS/N€jlmn€mllj€M0R€NPQRRT`_UW\YŽe&­€ĶĨĪŠ]Ī�O†ÜŅ€Ð�ρÎÍp�JCPRJGFDB?<:@jzfhƒi‰jƒi hf{i@:<?BDFGIQPDI!�a°l#„Ŋ§ĶĶĨĪŽh7Ģ�O“ÛŅ€Ð�σ΍ĨŌūž―·°ŦĄ—‡‚|pc€e‘f€ecq|‚‡—ĄŦą·――ūŌŽsŠģv"z°§ĶĶĨĪŽr5Ģ�MÚÐÐυÎ͉zzy{srpmkjea‚bc‚baejkmprszyyœ€lŦĐą"t°€ĶĨĪŽz6Ģ�NŠŲÐσÎÍÍĖĖˌeWmzf[]^›_^][eynWisŦŦĐŊ‹$jŊĶ€ĨĪŠ†5Ģ�P·ÖτÎĖĖ€Ë Ę“aƒVjyfYĢ]YdykV‚fvŽŦŦĐŪ”'^°Ķ€ĨĢĻ‘8Ģ�UÁӃÎÍÍË€Ę ÉČȕ\„VdvdWĄZXcvgVƒ`ŦŦĐĻĻŽš-S­€ĨĢĒ͚=Ģ�ZĘŌπÎÍÍËËĘ€É ČĮĮƘU„V`sbSŸWSaraU„[~ŠĐĐĻĻ§Đ›1IŠĨĪĢĒĒΟAĢ� `ÎŅÏÎÎÍĖËĘÉÉȀĮ ÆÅÄšO„VZo_PS P^m\TƒUƒ§§Ķ€§ ͧ 8@ĨĨĢĢĒĒĪĒHĢ�iÓÏÎÎÍÍËʀČĮĮÆÅÃÂĀÃĘĻH„VUk]N›PN[kVTƒN„ĨĨĶĨĶĨĪĪĶĨ@8ŸĨĒĄĨQĢ�uÖÎÎÍÍĖĘÉÉČĮĮÅÄÂÁĀĮČŋđŸ BƒWQdZKK—L KKXeRTƒGŠĒƒĢĪĢĪĻH4šĶĒĒĄĄ §[9Ą�!Q…ØÎÎÍĖËĘÉÉČĮÆÄÂÁÁÆĀļ·ķĩĄ=WKaWFF•HGFVaMTBˆžŸ  Ÿ  ĒĄĒĄĻQ-”Ķ€Ą Ÿ§g8Ą�M”ŨËÉɂČĮÅÄÂĀÁšķķīąą°œ$5XG]TDC“ECBS]HV;Š››šœ›žž€Ÿ ĄĻ['ŠĨžžžĶs9Ą�#MœÔÆÅÍÏĖÄÆĮĮÅÄÂū―ļ·ĩģŊ­ŽŠĐ›(/}YCVP@?‘A ?@PXDV}4…€•–—™™›žŸ  Đa"}žœŸŸ››Ĩy6Ą�$NĨÏÃĀyMÆÄÆĮĮ·ššļķē°ŽĐ§ĨĢĄ–+)z[>RN=;=$<=MR@W{.$ƒ‘“•—™›žŸŸĐimœj<S›Ģ‚7Ą�%NēĖÉu †ÍÅɰš―đ·ī°ŽĻĨĢ ›š“1"w\;MI:8:%8:IM=Yy'"‚‰‰‹ŒŽ‘’•˜š›žĻsh{L Œ:Ą�&R―ĘÍS.v)qÏČÃĨđÁ―š·ē­Š§ĪŸœ™—•’Œ-t]8GF74‹6&46FI8Zv"+}ƒƒ…‡‰ŠŒ‘”—š›œœŸ§}$hp$qA8›Ÿ•<Ą�'YČĖËģmŽ•ĀÉĮž°ÅĀ―ŧ·ēŪŠ§Ģžš—”‘Ž‹†3p_5CD41‰3'14CD5[r%y}~€‚…‡ŠŒ’”˜™š›žĶ…(^Ģqy…žšDĄ�(_ĖĖĘÏÕÎŅÉʝžĮÃÁŋžļīąŽ§Ģžš–’ŽŠˆ…‚3k`3?@1.‡0$.1??2]n,txy{~ƒ†Š“•™™šžŸĪ%QĶĨĒĒĄ€œ�HĄ�)fÏËÉÉČČĮĖŠ…ÃÅÂÁŋžš·īŊŦ§Ē—“Ž‹…ƒ}<ga0;=.*…,)*.<;0_i/rtvx{}€„ˆŒ“•—š›œžŸŸĒĄq]Ąžœ›šŸPĄ�*sŌÉÉČĮĮĘĀzĐÉÂÁÁŋūžšķģŊŦ͟›–‹…}{x: ac/7:,(ƒ*(+:7.`e3pqsux{„ˆ‹’•˜™œžŸ€  ĒĄ’žž›ššĄ[:Ÿ�MÓȁĮĖ‘}ĮÄ€ÁŋūžđķēŊŦĨĄ›”ˆƒ~zwt>\e-27*%'%)73,a` 6loqsvy~ƒ‡Œ”–™›œ€ž ž €‰Ąžœ€šĄf6Ÿ�IÓ€Į ÆĘđgĪËÃÃÂÁ€Ā7ūŧš·ĩąŦĶĄš”Ž†{xtsCWf,.5(#%%#(40+c[Clkoquz…ŠŽ‘•™›œžŸ€ Ÿ NJf– œšš˜Ÿo7Ÿ�I™Ō€ĮÆÎfÁČÄÀÂ7Áŋŋū―ŧļķēŽ§Ģœ•†€{vpoCQh,,3( &3-*dV8s{ppuz€†Œ‘”—›œœžŸ€ ‚Ÿ žŸœWq̜šš˜žv7Ÿ� IĢŅĮĮÆĮÂeƒÍĂÂ6ÁĀŋūū―ŧđīēŪŠĪž–ŽˆlbnkE�Lh,)2$$1**fP�<eg]iw{‚‰”˜šŸ ŸŸĄ� €Ÿ žĪfG—Ÿš˜˜œ~7Ÿ� J°ÍÆÅÅËĻRœËÂ�Á€Ā/ŋŋū―žŧ·ķģ°­ĻĒ›”~YiumhGFh,(11(*fK�BcgnW6lŠŒ’—œž €Ē€ĄĒĒĄ  ŸŸžĢz0{Ą™˜˜›‡:Ÿ� OšĘÆÅÅ΋RŊĮÂ�Á€Ā3ŋūū―žŧšļķĩģ°Ž§ĄpUwzpkfH?i,'()gEAaejtk!B•˜›ŸĒĒĢĪĪĒĄĒ�Ą€ ŸžĄŽ/^Ą™˜˜™=Ÿ�WÂȀÅČrXŧÆÂÂÁÁ€Āūū€―žŧš€ļ'ķīē°Ļja„…wphdF9i+'h?D`ciq}z76”ĒĄĒĢĪĪĢĢ‚ĒĄĄ  Ÿž€ ž–5Cœš˜——”CŸ� \ĮĮÄÃÄÁa[ūÅÁ€Āŋūū―�ŧ€š)đļ··ķĩģŪ~„—”‡yogbJ3hg8C_cis†aUœĶĪĨĪĢĪ́Ē�Ą€ ŸŸž‚ ˜=8”š—–——GŸ� cĘÅÂÂÅžY\―ĀĀ�ŋ€ū―žŧŧšđļ ķĩĨŊŧĪ—Œ}pf`K05H\ckw„š™Œ‡§ĶĶĨ΀̀ǀĄŸŸ€žžœ™B7š––•™PŸ� oÍÃÂÂÅđZ]žÄĀĀŋ€ū―žŧšđđ!ķķīđÐáŌ̜’‚uh_NI^eo}‹–œŽŪĨĪĻĨĨĪ΂ǀĄ� ‚ž›šœ˜C;™–•”™Y;� F}ÎÃÂÂÅŧ][ķÄŋŋū―ŧ€šđđķķ€ĩēØũüßŦž—‰ylbGF`iv…‘™ĒđÂŧŊĶĨĪĪĢ̀ǀĄ  €žœ›€š œ“E@™•””™d8� F‹ÎÃÂÂÄÁhZĻÆŋŋ€ū―žŧ€š�đ€ļ·ķķĩīąķïĸĸîĀĪ—Š~rkiq|ˆ”ŸąĖŌĘ―§ĢĨĪ̃ĒĄ  Ÿ€žœ›šš™™‰FK“—”“’˜l9�E•Ė€Â ÁĮ}]—Æŋŋū―ž€ŧššđ€ļ·ķƒĩģēŪĩčĸųðåŅĩŒ‚‚‰™ŪđÆŅØŨħĢĨ΃ĒĄĄ €Ÿžœœ›š™˜˜{J]™•”’‘–q7�DžË€Â ÁȔf‡Ãŋūžž€ŧšđđļļ··ķĩīīģ€ēŪŽĖëóúýúōæÝÜåōöÝŌŅÏđĢĢĨĢ́ĒĄĄ Ÿž€œšš™€˜ ›pTqš•”’‘—{8�IŦȀÂÁÄŽt€·ŋžŧšđđ€ļ·ķĩĩ€ī€ģ€ēąŊŠŊÆčþþýûúúûýýāĮķ§ĒĪĨĪ‚Ē�Ą€ €Ÿžžœ››š™™˜˜—˜’l`…˜•“’‘•ƒ<�LķĮÂÁÁĀĀū‰‡ĶÁžŧŧšđļļ·ķķĩīƒģēą€°Ŋ­ĻĻĩÅÖáääáÕ­ĢĒĪĨĪ́Ē�Ą‚ ŸŸž€›š€™˜—–˜†pp“•’’‘‘’Š?�SŋÅÁ€ĀŋÄĨ’ ŧūžŧ€đļļ··ķķĩĩ„ģ ēą°°ŊŪ­­ŽĻĨĒŸž ĢĨĨĪĪ́ĒĄĄ€ �Ÿ€žœ›š€™˜——–•€v‚–“’‘‘ŽD�XÃÁĀŋĀŧ Ķĩ―žŧ€đļļ··ķķĩī„ģ ēą°°ŊŪ­ŽŦŦ€Đϧ§€ĶĨĪ̃ĒĄĄ€ Ÿžž€›š™�˜€— Ž‚€”’’‘H�bÆÁĀĀŋŋ€ūģ°đžššđđķķĩĩīē‚ą°ŊŪŪ­Ž€ŠĐĻ€§ĶĨ€Ī�̂ǀĄ  €žœ›š€™˜——––•Œ‡‹•“’’‘ŽŽ“Oœ�GnÉĀŋŋūū€―ŧšÃĘšļđ·ķķ€ĩ�ģ„ą°°Ŋ€Ū­Ž€ŠĐĻ€§ĶĨĪĪ̀ǁĄ Ÿžœ›€š™˜˜€—––”Œ””“’’‘ŽŽ”X<›�E{ËĀŋū€―žŧšūÍŲÔķķļ··ķķĩĩīģēą€°€Ŋ€Ū­Ŧ€ŠĐĻ€§ĶĨĪ̀ǁĄ Ÿ€žœ›š™˜—–”™˜”“•””“’Ž”a8›�CŠĘŋ€ūžŧ€šļĘãėŲēĩ·ķĩģēąą€°€Ŋ‚ŪŽŦ€ŠĻĻ€§ĶĨĪĢĒǁĄ  Ÿ€žœ›€š™™˜€—–•“œ ›–”’‘ŽŒ“j8›�B’Éūū―žŧšļķÚõøÜąģĩ�ī€ēą°°ŊŪŪŽŦŠĐ€Ļ§Ķ€Ĩ�Ģ€ĒĄĄ  €Ÿžœ›š™€˜ —––•“ĨĒ›“’ŽŒŒ’n9›�BœČūū―ŧ€š đđļķļįûýáąą€ĩ�ģ€ēą°°ŊŊۭށŦŠŠĐ€Ļ§Ķ€Ĩ�Ģ€Ē�Ą€ Ÿžœœ›š™™€˜ —–•““ Ļ§Ą’‘ ŒŒ‹Žv9›�FĻÅū―ŧŧšđļ ·ģ·ëĸĸėđŪģē°°€ŊŪ­Ž€ŦŠŠĐ€Ļ§ĶĶ€Ĩ�Ģ€ĒĄ ƒŸž€œš™€˜ ——–•“•ĨŽŠĒ’€‘€Œ‹Š~:›�IģÂ―€ŧšđ€ļ·ķķēĩæĸĸũĮŦŊąą°°€Ŋ­­ŽŦ€ŠĐϧ€ĶĨĪĪ̀Ǡ €Ÿ€žœ››™™˜˜—––”’›ŦŊ­Ģ’€�€ŽŒ‹ŠŠ‹„?›�QŧĀž€ŧšđļļ·ķ€ĩēąÜĸĸýÜģŠŊ°°ŊŪ­­ŽŦЃĐϧ€ĶĨĨĪĢ̀Ǡ €Ÿ€žœ›š™™˜—––”•Ĩą°ŊĄ€€ŽŒ‹ŠˆB›� Vŋ―ŧŧšđđļļ·ķķĩģ­ĘøýûïČ­Š­Ū­­ŽŠŠƒĐ�ρĶĪĢ̂Ǡ Ÿ€ž›š€™˜—–”•ŸŽēē­›‘‘€Ž€Œ‹€Š€‰�F›�_Â―ŧšđđļ·€ķ€ĩīīģģŽ·âøöōÜūŦĻŦŽŦŠ€Đ€Ļ�§€ĶĨĪ̃ĒĄ  ž€›š™™˜—–”•œ§Ŋ°Ŋ§–’’‘ŽŽ€Œ‹‹Š‰‰ˆˆŒNš�DkÆžšđđﷁķĩīīģē­ŦÅæîčäÕŋ­ĶĶĐŠŠĐĻ€§ĶĶĨĪĪĢ‚ĒĄĄ Ÿžœšš™˜•”–ĨŦޭМ‚’‘ŽŽŒŒ‹‹Š‰ˆˆ‡‡V;™� CxÆŧđđļ··ķķ€ĩīīģģēēąą°ŠŪÅÜâßßÛÍšŠĒĒĨĶ€§ĶĶĨĪĪĢĒǁĄ Ÿžž€œ›š˜—•–› ĨĻĐŠ§ž”’””“’‘ŽŽŒ€‹ Љˆˆ‡††‹_9™�A…Åđđļ�ķ€ĩīīēēą°Ŋ­ĐŪÁÔÞäęņíÜÃŪǟ ĄĢĪĢĒĒ€Ą Ÿžžœ›™–““–ĢŠŽŦЧĶΜ”‘“’Œ ‹‹‰‰ˆˆ†……Še:™� ?ŽÄļ·ķķĩĩķķ€ĩīģēēą°ŊŊŪŽĻŠļÏåõûĸĸýîÜĮļ­Ģž›š™™—–••–šĢ­―ÍÏËÚģ­Ķ ™’ƒ‘Œ ‹‹Š‡†……„„…‹k9™�B—ŋģąķđļīģīĩīģēą°Ŋ‚ŪŽŦЧĨŊÅãüƒĸýôéáÚŌĖÉÄÃĮĘÐÖÞæōýĸĸųįÕÅķŠž–’‘‘…�Ž€Œ€‹ Šˆ…€ƒ„ƒ€ƒˆr<™� =ĢđŪŊƒESœēąģģē€ą�°ŊŪ­­€Ŧ ŠĐĻĶĒĒ­ÂÚîü“ĸņÔđĪ—“€’‘‘ŽŒŒ‹€Š ‰†€|sE3U~…{0™� $°ĩ°‡ ;Ŧēą°°€ŊŪŪ­Ž€ŦŠŠĐ€Ļ §Ī  ŠđÆŨįïõüþƒĸþüõîåÔÂēž”‘‘““’€‘€ŽŒ‹‹Šˆ„~z? V€ƒ�#™�9·ģĩ[_D!īēēą°€ŊۭށŦŠŠĐĻ§Ķ€ĨĢ ššœĄĨŠŪąēēŊŽĶ š–‘‘”€•”’’€‘ŽƒŒ‹€Š€‰ ‡ƒv#C^+;‚‚†.™�D―īķ”6`si€ēą°€ŊŪ­Ž€ŦŠŠĐ€Ļ�§ĶĨĨ΁ĒĄĄŸžœš˜„— ˜˜™˜——––•””€’‘‘ŽŒŒ‹Šˆ †ƒ†Y@€C_ƒ‚‡9™� Gŧķīš°œ°đģ€ą�°€Ŋ�ہŦ�Š€ĐĻϧ€ĶĨĨĪĢ‚ĒĄ  Ÿžœ‚œ›š€™˜—€–•””€’‘€ŽŒŒ‹€Š�‰‚ˆ ††‰}‚…‚‡D™� lŅąŊŪ°ą­ŽŠĐ�Ļ€ĶĪĢ€Ē ĄĄ ŸŸžžœœ‚š�™€˜–•”“’’‘€ŒŠŠ‰‡‡€…„„‚ƒ€€„~ {{|zzxw~‹W™�9āÖę€åää€âäį€äââáāāß߀ÞÝ݁܀ہÚŲÚßßÛÝÞÜŲÜÝÚÛÜÛŨÛÛÖØŲØÕŲŲÔŨØÕԀҀÐÏÏÍĖ„ËÍʁÉʗsš�"v°đļđđŧ―ŋĀģĄąŽŪąēģīĩķ·ļļđšŧŧž―ūŋĀÁ€ÂÃÆŋĶĶđąĶŪŋϧŧ°§ąŋĻĻŋąĐĩÁŠŦÂąŦīĖ€ĘËËÍÏŅŌŌÓÓÔÔÕÖÐĘÏŲØØÚÐ{œ�"$BRUUSW]ce@ 8(.37;>@BCFGHILMOPRSVXZ€[€\aH��4�B��9�>��>�?��>�\WX\`defhijjln[LZsrqqR: ›�$,N]aa_cioqSA:=BEILMOPRTUVXZ\]^_adeg€fhglV D)P G#$L L )M M#icdhlnopqsttuweYh}{{y[>œ�#(L]`a`cioqV><<BEILMPQRTUWYZ\]_`bdeeghlX F+!R J%&N N",O O&icdiknpqrsstuvcYj|zzwY:œ�$J\€`bhmrY:?;BEIKMOQRTUWYZ[]^`bc€e�f€gkY H.#T K((P P$.P Q!)icdiknpqqrsstuaZk{yytW5œ�!H[a``bhmr[ 1?;BDIKMOQRSTWYZ[\^`ace fggkY K2&V N*+S S'1S T$-icdhknoqqrrsts`ZmzxyrT1�EY€`bgms](-9CEHKMOPRSTWYZ[\^`acd‚egjZ L4(X P-.T T)3T U&/icdfi[bXhpqrsr_[nywynR,�CX€`bglr`-((5BIMMOPRSUVYZ[\^`acdd‚ej\ M5)X P..T T*3U U'/i‚cfE€â―bdprsp][pxuxlQ&�@W€`bflsb1+*(-9FMQQRSTVXZ[\^_ac€deiZ M5)X P..T T*3U U'/icfX(ßĸĸ|Kqqrn\]pwtwiO!�<U€`aflrd6%0//-,.6BKRVWWXZ[\]`ab‚deeiZ L4(X P-.T T*3U U&/icf]“ĸŨ0Wpprm\^qutvfL� 9S_``afkrg>.ƒ20029CNUZ]^__`acd‚efjZ L4 )WP. /TT* 3UU& /jfgkR,O:Lmnqrl[^qusucJ� 9Ubcbchnvh>2†4 2137=FKQVZ]_aab7afW'%I:%3R&%L6&7O&&O4%:P%&P2&5bZTRQRUZYP[ddilnh[buwvwdHž� DTUUWZZXLA…B‚CBA@@ABDEF€G‚HGEEFHEFH€F HGGHGGHIFFH€GHDBBCB€A CCDFIKLLQ[€fgO(ž�$€%€&�(€*ƒ+‚,†-„.//1202€3€4 55657769889::9€;:<==>?@@€A@@??A?ĸ�ĸ�ĸ�ĸ�ĸ�ĸ�ĸ�ĸ���Ž�€<˜;<Û�Kc™|bKÛ�hž™™žhÛ�“Īŧķ‘ĩķŧĢ“Û�:>€G„F€EDEƒFGGF>:Û�!Vœ—™™›žŸĄĒĪĨĶĻĐŠŠĐĻĶĨĪĒĄŸž›™™—TÛ�!Íąģĩī··ļđššžž―ūū―žžŧšđļ··īĩģąÍ~Û�}―œ€ž€žœūzÛ�|ŋĄƒĢ‹ĪƒĢĄŋyÛ�|ūĄ†Ģ…ΆĢĄūyÛ�{― €Ē‘̀Ǡ―xÛ�zŧ ‚ĒĢ‚Ē ―xÛ�zšŸĄ…Ē…Ģ†ĒŸžvÛ�x🡕ĒĄŸđuÛ�x·ž‚ĄĒ‚ĄžđtÛ�wĩ€ ƒĄ…ĒƒĄ€ ķsÛ�vĩ€Ÿ  Ą  €ŸĩrÛ�uģ›žƒŸ‰ ƒŸž›ģqÛ�t皜žŸžœšēpÛ�q°™›€œ�ž…Ÿ�ž€œ›™°oÛ�rۘ›œˆ€œ›˜ŪnÛ�qŽ—š‚›�œƒ›�œƒ›€š™ŽmÃ�ŌÎÍŨÔŨÐÏŌŌŅŅÐÐÏπÎ�̀ĖË^nŦ–˜€™ƒš„›‚š€™ ˜–Ŧl[šđļļ·ķĩĩīģ ēąŪŠĐŪŠ°ŽĐÆŠ� ‘ÖŌ҇O|ÏŅÓӁŅÐÎÐρÎÍËumĻ•——˜‚™šƒ™€˜ ——–Đjpžŧšđļļ··€ķ ĩĩīģŊŦĻj8]Ķ­ĩšĐ�~ÛŲ–!‹ÝÔÕÔÓŌŌ€Ņ ÐÎÎÏÎÎÍukĶ“––‚—‰˜‚— ––“§ip―ŧšđđļ··€ķ ĩĩīēŊ°€)hīģ_Ļ� X‹ß۝3ƒIœÝ€ÖÔӀŌŅЀπÎujĢ“”€•–‡—–€• ”“Ģhpžŧšđđļ··€ķĩīģģ°ą>†Huģķj5§�WšâÖÜžÎÛÕÕÔÔÓÓŌŌŅ€Ð�ρÎuiĒ‘’’“€”‰•€”“’’‘Ēgoŧ€š��ķ€ĩ īīēą°īŠĪ§°­ķv7§� VĒáŨŨÝÜŲÕØŲÕԀÓŌŌŅŅЁÏÎth ‘‚’“‚’‘  eožŧššđđļ ķķ――ģąģąąŪ­ķ6§�V­āŨŨÖÕÔŨĘĐŅÓŌÓŌÔŌÓŌŅÐÏŌÐÐufž€Ž€€‡‘€€€Ž ždnšđļļ·ķķĩīģąĩĶ·ēŊŪŪ­Ŧē‹7§�XžÞÖցÕ}yŨŌÔÄÁŊšīķšļžąķēgeœŒŒ‚އ‚Ž€Œœbnđ€ļ·ķĩīģēģļJOŊ°ŪŪŽŠ°–:§�[ĮÚրÕØÏh‡ØŅÔÄšĪáīÉÛâåŽŲÐxcšˆ€‹€Œ‹€Œ€‹Ššbnđļļ·ķĩīģēēąŧ[&Ģē­ŽŽŦŪĄ@§�aŅŲրÕØÉd’ÚŅÔÂĩąŨÉĖÓŅŌÉÏÏub˜…ˆ‰‰€Š‹‹€Š ‰‰ˆ…˜anđļļ·ķ€ĩīģ€ēąŧf%›ī­€ŦŽĶC§�eÔŨÕŲĀ_˜ÚÐŌĀīŪŋūđÃū°Á―ēka•…††‡€ˆ…‰€ˆ‡ ††…•_mđļ·ķķĩĩī€ģēą°ŧm"īŽŽŦĐŦĐH§�nŲÖÕÕÔÔŲ·[žÚÐÐūģąđļšđīŧ·ķši`”‚„„€…†€…„„‚”^mļ·ķķĩĩīīģģēēą°šx„ĩŽŦŠĐŠŊQ§�{݀ÔÓÓÚ­X§ŲÐÐÁąžÃŋÁ·īŋ―ļķn_’€‚‚€ƒ„€ƒ ‚‚€’]mļ·ķķĩĩī€ģēēą°ļƒ yģŠŦŠĐĐē\Ķ�U‰ß€ÓŌŌÛĶY°ŨÐŌÃķŊĩķĩą°īīēŊf]Ž}€€‚‰‚‚€}[l··ķ€ĩīģēē€ą°ķ"sī‚Đąi6Ĩ�T—ß€Ó ŌŌڟZļÖÏŌŋŋ€―žū‚―j\Ž{}€~‡€€~}{ŽZk·ķĩīģē€ą°Ŋī—&hīĐϰt5Ĩ�R Þ€Ó ŌŅŲ‘WšÓÎÍπÎ�Í€Ë ĘÉÉČp[Œy{{|}ƒ~}|{{yŒYkĩķ€ĩīģģē€ą°ŊēŸ*\īŠĐ€ĻŊ|4Ĩ�TŦÜÓŌŌŅÐ֋ŠÞÕĖĖ€Í�Ė€ĘÉȀĮnZŠwyyzŒ{€z yyw‹Xiģ°ēĩĩģ€ēąą°ŊŊēĒ.QēŦĐϧ§Ūˆ5Ĩ�Vđ؃ŅÃëþũúų†ø‹Y‡uwxyx wu‰WŒóōÅąī€ē ą°°ŊŊŪŊĶ6FŪŠ€Ļ§Š’8Ĩ�YÃւŅÐÐâūŸĶœšš™—•’‘‹‹‘QT…r€u€v‹wv uur‡SS’emprsvwxyz{„ƒ“?=ĻЧĶĨĨĻ›=Ĩ�_ËÕŅŅ€ÐÏÏːixve`_]ZWTQMKHK+R„pr€st‡ut€srp…P,KHKNQTW\]a`evxj„H8ĨЧĶĨĨ§ AĨ�dŅÔŅŅЁÏΜtz‹‰wrsqnliecae6N‚oqrqo„L7daceilnpssv‡‹{s‘S2ŸŠ€ĶĨĨĢFĨ�lŨÓŅÐЁÏÎΈ‡Š—–ˆ‚}xwsqw?Kmo‚p‹q‚pomI@wqswy|ƒ‡–˜‹‡ˆš]+—Ŧ€ĶĨĪĻPĨ�yÚŅЁπПZ\YT_`URRQP€NS/N€jlmn€mllj€M0R€NPQRRT`_UW\YŽe&­€ĶĨĪŠ]Ī�O†ÜŅ€Ð�ρÎÍp�JCPRJGFDB?<:@jzfhƒi‰jƒi hf{i@:<?BDFGIQPDI!�a°l#„Ŋ§ĶĶĨĪŽh7Ģ�O“ÛŅ€Ð�σ΍ĨŌūž―·°ŦĄ—‡‚|pc€e‘f€ecq|‚‡—ĄŦą·――ūŌŽsŠģv"z°§ĶĶĨĪŽr5Ģ�MÚÐÐυÎ͉zzy{srpmkjea‚bc‚baejkmprszyyœ€lŦĐą"t°€ĶĨĪŽz6Ģ�NŠŲÐσÎÍÍĖĖˌeWmzf[]^›_^][eynWisŦŦĐŊ‹$jŊĶ€ĨĪŠ†5Ģ�P·ÖτÎĖĖ€Ë Ę“aƒVjyfYĢ]YdykV‚fvŽŦŦĐŪ”'^°Ķ€ĨĢĻ‘8Ģ�UÁӃÎÍÍË€Ę ÉČȕ\„VdvdWĄZXcvgVƒ`ŦŦĐĻĻŽš-S­€ĨĢĒ͚=Ģ�ZĘŌπÎÍÍËËĘ€É ČĮĮƘU„V`sbSŸWSaraU„[~ŠĐĐĻĻ§Đ›1IŠĨĪĢĒĒΟAĢ� `ÎŅÏÎÎÍĖËĘÉÉȀĮ ÆÅÄšO„VZo_PS P^m\TƒUƒ§§Ķ€§ ͧ 8@ĨĨĢĢĒĒĪĒHĢ�iÓÏÎÎÍÍËʀČĮĮÆÅÃÂĀÃĘĻH„VUk]N›PN[kVTƒN„ĨĨĶĨĶĨĪĪĶĨ@8ŸĨĒĄĨQĢ�uÖÎÎÍÍĖĘÉÉČĮĮÅÄÂÁĀĮČŋđŸ BƒWQdZKK—L KKXeRTƒGŠĒƒĢĪĢĪĻH4šĶĒĒĄĄ §[9Ą�!Q…ØÎÎÍĖËĘÉÉČĮÆÄÂÁÁÆĀļ·ķĩĄ=WKaWFF•HGFVaMTBˆžŸ  Ÿ  ĒĄĒĄĻQ-”Ķ€Ą Ÿ§g8Ą�M”ŨËÉɂČĮÅÄÂĀÁšķķīąą°œ$5XG]TDC“ECBS]HV;Š››šœ›žž€Ÿ ĄĻ['ŠĨžžžĶs9Ą�#MœÔÆÅÍÏĖÄÆĮĮÅÄÂū―ļ·ĩģŊ­ŽŠĐ›(/}YCVP@?‘A ?@PXDV}4…€•–—™™›žŸ  Đa"}žœŸŸ››Ĩy6Ą�$NĨÏÃĀyMÆÄÆĮĮ·ššļķē°ŽĐ§ĨĢĄ–+)z[>RN=;=$<=MR@W{.$ƒ‘“•—™›žŸŸĐimœj<S›Ģ‚7Ą�%NēĖÉu †ÍÅɰš―đ·ī°ŽĻĨĢ ›š“1"w\;MI:8:%8:IM=Yy'"‚‰‰‹ŒŽ‘’•˜š›žĻsh{L Œ:Ą�&R―ĘÍS.v)qÏČÃĨđÁ―š·ē­Š§ĪŸœ™—•’Œ-t]8GF74‹6&46FI8Zv"+}ƒƒ…‡‰ŠŒ‘”—š›œœŸ§}$hp$qA8›Ÿ•<Ą�'YČĖËģmŽ•ĀÉĮž°ÅĀ―ŧ·ēŪŠ§Ģžš—”‘Ž‹†3p_5CD41‰3'14CD5[r%y}~€‚…‡ŠŒ’”˜™š›žĶ…(^Ģqy…žšDĄ�(_ĖĖĘÏÕÎŅÉʝžĮÃÁŋžļīąŽ§Ģžš–’ŽŠˆ…‚3k`3?@1.‡0$.1??2]n,txy{~ƒ†Š“•™™šžŸĪ%QĶĨĒĒĄ€œ�HĄ�)fÏËÉÉČČĮĖŠ…ÃÅÂÁŋžš·īŊŦ§Ē—“Ž‹…ƒ}<ga0;=.*…,)*.<;0_i/rtvx{}€„ˆŒ“•—š›œžŸŸĒĄq]Ąžœ›šŸPĄ�*sŌÉÉČĮĮĘĀzĐÉÂÁÁŋūžšķģŊŦ͟›–‹…}{x: ac/7:,(ƒ*(+:7.`e3pqsux{„ˆ‹’•˜™œžŸ€  ĒĄ’žž›ššĄ[:Ÿ�MÓȁĮĖ‘}ĮÄ€ÁŋūžđķēŊŦĨĄ›”ˆƒ~zwt>\e-27*%'%)73,a` 6loqsvy~ƒ‡Œ”–™›œ€ž ž €‰Ąžœ€šĄf6Ÿ�IÓ€Į ÆĘđgĪËÃÃÂÁ€Ā7ūŧš·ĩąŦĶĄš”Ž†{xtsCWf,.5(#%%#(40+c[Clkoquz…ŠŽ‘•™›œžŸ€ Ÿ NJf– œšš˜Ÿo7Ÿ�I™Ō€ĮÆÎfÁČÄÀÂ7Áŋŋū―ŧļķēŽ§Ģœ•†€{vpoCQh,,3( &3-*dV8s{ppuz€†Œ‘”—›œœžŸ€ ‚Ÿ žŸœWq̜šš˜žv7Ÿ� IĢŅĮĮÆĮÂeƒÍĂÂ6ÁĀŋūū―ŧđīēŪŠĪž–ŽˆlbnkE�Lh,)2$$1**fP�<eg]iw{‚‰”˜šŸ ŸŸĄ� €Ÿ žĪfG—Ÿš˜˜œ~7Ÿ� J°ÍÆÅÅËĻRœËÂ�Á€Ā/ŋŋū―žŧ·ķģ°­ĻĒ›”~YiumhGFh,(11(*fK�BcgnW6lŠŒ’—œž €Ē€ĄĒĒĄ  ŸŸžĢz0{Ą™˜˜›‡:Ÿ� OšĘÆÅÅ΋RŊĮÂ�Á€Ā3ŋūū―žŧšļķĩģ°Ž§ĄpUwzpkfH?i,'()gEAaejtk!B•˜›ŸĒĒĢĪĪĒĄĒ�Ą€ ŸžĄŽ/^Ą™˜˜™=Ÿ�WÂȀÅČrXŧÆÂÂÁÁ€Āūū€―žŧš€ļ'ķīē°Ļja„…wphdF9i+'h?D`ciq}z76”ĒĄĒĢĪĪĢĢ‚ĒĄĄ  Ÿž€ ž–5Cœš˜——”CŸ� \ĮĮÄÃÄÁa[ūÅÁ€Āŋūū―�ŧ€š)đļ··ķĩģŪ~„—”‡yogbJ3hg8C_cis†aUœĶĪĨĪĢĪ́Ē�Ą€ ŸŸž‚ ˜=8”š—–——GŸ� cĘÅÂÂÅžY\―ĀĀ�ŋ€ū―žŧŧšđļ ķĩĨŊŧĪ—Œ}pf`K05H\ckw„š™Œ‡§ĶĶĨ΀̀ǀĄŸŸ€žžœ™B7š––•™PŸ� oÍÃÂÂÅđZ]žÄĀĀŋ€ū―žŧšđđ!ķķīđÐáŌ̜’‚uh_NI^eo}‹–œŽŪĨĪĻĨĨĪ΂ǀĄ� ‚ž›šœ˜C;™–•”™Y;� F}ÎÃÂÂÅŧ][ķÄŋŋū―ŧ€šđđķķ€ĩēØũüßŦž—‰ylbGF`iv…‘™ĒđÂŧŊĶĨĪĪĢ̀ǀĄ  €žœ›€š œ“E@™•””™d8� F‹ÎÃÂÂÄÁhZĻÆŋŋ€ū―žŧ€š�đ€ļ·ķķĩīąķïĸĸîĀĪ—Š~rkiq|ˆ”ŸąĖŌĘ―§ĢĨĪ̃ĒĄ  Ÿ€žœ›šš™™‰FK“—”“’˜l9�E•Ė€Â ÁĮ}]—Æŋŋū―ž€ŧššđ€ļ·ķƒĩģēŪĩčĸųðåŅĩŒ‚‚‰™ŪđÆŅØŨħĢĨ΃ĒĄĄ €Ÿžœœ›š™˜˜{J]™•”’‘–q7�DžË€Â ÁȔf‡Ãŋūžž€ŧšđđļļ··ķĩīīģ€ēŪŽĖëóúýúōæÝÜåōöÝŌŅÏđĢĢĨĢ́ĒĄĄ Ÿž€œšš™€˜ ›pTqš•”’‘—{8�IŦȀÂÁÄŽt€·ŋžŧšđđ€ļ·ķĩĩ€ī€ģ€ēąŊŠŊÆčþþýûúúûýýāĮķ§ĒĪĨĪ‚Ē�Ą€ €Ÿžžœ››š™™˜˜—˜’l`…˜•“’‘•ƒ<�LķĮÂÁÁĀĀū‰‡ĶÁžŧŧšđļļ·ķķĩīƒģēą€°Ŋ­ĻĻĩÅÖáääáÕ­ĢĒĪĨĪ́Ē�Ą‚ ŸŸž€›š€™˜—–˜†pp“•’’‘‘’Š?�SŋÅÁ€ĀŋÄĨ’ ŧūžŧ€đļļ··ķķĩĩ„ģ ēą°°ŊŪ­­ŽĻĨĒŸž ĢĨĨĪĪ́ĒĄĄ€ �Ÿ€žœ›š€™˜——–•€v‚–“’‘‘ŽD�XÃÁĀŋĀŧ Ķĩ―žŧ€đļļ··ķķĩī„ģ ēą°°ŊŪ­ŽŦŦ€Đϧ§€ĶĨĪ̃ĒĄĄ€ Ÿžž€›š™�˜€— Ž‚€”’’‘H�bÆÁĀĀŋŋ€ūģ°đžššđđķķĩĩīē‚ą°ŊŪŪ­Ž€ŠĐĻ€§ĶĨ€Ī�̂ǀĄ  €žœ›š€™˜——––•Œ‡‹•“’’‘ŽŽ“Oœ�GnÉĀŋŋūū€―ŧšÃĘšļđ·ķķ€ĩ�ģ„ą°°Ŋ€Ū­Ž€ŠĐĻ€§ĶĨĪĪ̀ǁĄ Ÿžœ›€š™˜˜€—––”Œ””“’’‘ŽŽ”X<›�E{ËĀŋū€―žŧšūÍŲÔķķļ··ķķĩĩīģēą€°€Ŋ€Ū­Ŧ€ŠĐĻ€§ĶĨĪ̀ǁĄ Ÿ€žœ›š™˜—–”™˜”“•””“’Ž”a8›�CŠĘŋ€ūžŧ€šļĘãėŲēĩ·ķĩģēąą€°€Ŋ‚ŪŽŦ€ŠĻĻ€§ĶĨĪĢĒǁĄ  Ÿ€žœ›€š™™˜€—–•“œ ›–”’‘ŽŒ“j8›�B’Éūū―žŧšļķÚõøÜąģĩ�ī€ēą°°ŊŪŪŽŦŠĐ€Ļ§Ķ€Ĩ�Ģ€ĒĄĄ  €Ÿžœ›š™€˜ —––•“ĨĒ›“’ŽŒŒ’n9›�BœČūū―ŧ€š đđļķļįûýáąą€ĩ�ģ€ēą°°ŊŊۭށŦŠŠĐ€Ļ§Ķ€Ĩ�Ģ€Ē�Ą€ Ÿžœœ›š™™€˜ —–•““ Ļ§Ą’‘ ŒŒ‹Žv9›�FĻÅū―ŧŧšđļ ·ģ·ëĸĸėđŪģē°°€ŊŪ­Ž€ŦŠŠĐ€Ļ§ĶĶ€Ĩ�Ģ€ĒĄ ƒŸž€œš™€˜ ——–•“•ĨŽŠĒ’€‘€Œ‹Š~:›�IģÂ―€ŧšđ€ļ·ķķēĩæĸĸũĮŦŊąą°°€Ŋ­­ŽŦ€ŠĐϧ€ĶĨĪĪ̀Ǡ €Ÿ€žœ››™™˜˜—––”’›ŦŊ­Ģ’€�€ŽŒ‹ŠŠ‹„?›�QŧĀž€ŧšđļļ·ķ€ĩēąÜĸĸýÜģŠŊ°°ŊŪ­­ŽŦЃĐϧ€ĶĨĨĪĢ̀Ǡ €Ÿ€žœ›š™™˜—––”•Ĩą°ŊĄ€€ŽŒ‹ŠˆB›� Vŋ―ŧŧšđđļļ·ķķĩģ­ĘøýûïČ­Š­Ū­­ŽŠŠƒĐ�ρĶĪĢ̂Ǡ Ÿ€ž›š€™˜—–”•ŸŽēē­›‘‘€Ž€Œ‹€Š€‰�F›�_Â―ŧšđđļ·€ķ€ĩīīģģŽ·âøöōÜūŦĻŦŽŦŠ€Đ€Ļ�§€ĶĨĪ̃ĒĄ  ž€›š™™˜—–”•œ§Ŋ°Ŋ§–’’‘ŽŽ€Œ‹‹Š‰‰ˆˆŒNš�DkÆžšđđﷁķĩīīģē­ŦÅæîčäÕŋ­ĶĶĐŠŠĐĻ€§ĶĶĨĪĪĢ‚ĒĄĄ Ÿžœšš™˜•”–ĨŦޭМ‚’‘ŽŽŒŒ‹‹Š‰ˆˆ‡‡V;™� CxÆŧđđļ··ķķ€ĩīīģģēēąą°ŠŪÅÜâßßÛÍšŠĒĒĨĶ€§ĶĶĨĪĪĢĒǁĄ Ÿžž€œ›š˜—•–› ĨĻĐŠ§ž”’””“’‘ŽŽŒ€‹ Љˆˆ‡††‹_9™�A…Åđđļ�ķ€ĩīīēēą°Ŋ­ĐŪÁÔÞäęņíÜÃŪǟ ĄĢĪĢĒĒ€Ą Ÿžžœ›™–““–ĢŠŽŦЧĶΜ”‘“’Œ ‹‹‰‰ˆˆ†……Še:™� ?ŽÄļ·ķķĩĩķķ€ĩīģēēą°ŊŊŪŽĻŠļÏåõûĸĸýîÜĮļ­Ģž›š™™—–••–šĢ­―ÍÏËÚģ­Ķ ™’ƒ‘Œ ‹‹Š‡†……„„…‹k9™�B—ŋģąķđļīģīĩīģēą°Ŋ‚ŪŽŦЧĨŊÅãüƒĸýôéáÚŌĖÉÄÃĮĘÐÖÞæōýĸĸųįÕÅķŠž–’‘‘…�Ž€Œ€‹ Šˆ…€ƒ„ƒ€ƒˆr<™� =ĢđŪŊƒESœēąģģē€ą�°ŊŪ­­€Ŧ ŠĐĻĶĒĒ­ÂÚîü“ĸņÔđĪ—“€’‘‘ŽŒŒ‹€Š ‰†€|sE3U~…{0™� $°ĩ°‡ ;Ŧēą°°€ŊŪŪ­Ž€ŦŠŠĐ€Ļ §Ī  ŠđÆŨįïõüþƒĸþüõîåÔÂēž”‘‘““’€‘€ŽŒ‹‹Šˆ„~z? V€ƒ�#™�9·ģĩ[_D!īēēą°€ŊۭށŦŠŠĐĻ§Ķ€ĨĢ ššœĄĨŠŪąēēŊŽĶ š–‘‘”€•”’’€‘ŽƒŒ‹€Š€‰ ‡ƒv#C^+;‚‚†.™�D―īķ”6`si€ēą°€ŊŪ­Ž€ŦŠŠĐ€Ļ�§ĶĨĨ΁ĒĄĄŸžœš˜„— ˜˜™˜——––•””€’‘‘ŽŒŒ‹Šˆ †ƒ†Y@€C_ƒ‚‡9™� Gŧķīš°œ°đģ€ą�°€Ŋ�ہŦ�Š€ĐĻϧ€ĶĨĨĪĢ‚ĒĄ  Ÿžœ‚œ›š€™˜—€–•””€’‘€ŽŒŒ‹€Š�‰‚ˆ ††‰}‚…‚‡D™� lŅąŊŪ°ą­ŽŠĐ�Ļ€ĶĪĢ€Ē ĄĄ ŸŸžžœœ‚š�™€˜–•”“’’‘€ŒŠŠ‰‡‡€…„„‚ƒ€€„~ {{|zzxw~‹W™�9āÖę€åää€âäį€äââáāāß߀ÞÝ݁܀ہÚŲÚßßÛÝÞÜŲÜÝÚÛÜÛŨÛÛÖØŲØÕŲŲÔŨØÕԀҀÐÏÏÍĖ„ËÍʁÉʗsš�"v°đļđđŧ―ŋĀģĄąŽŪąēģīĩķ·ļļđšŧŧž―ūŋĀÁ€ÂÃÆŋĶĶđąĶŪŋϧŧ°§ąŋĻĻŋąĐĩÁŠŦÂąŦīĖ€ĘËËÍÏŅŌŌÓÓÔÔÕÖÐĘÏŲØØÚÐ{œ�"$BRUUSW]de@ 8(.37;>@BCFGHILMOPRSVXZ€[€\aH��4�B��9�>��>�?��>�\WX\`defhijjln[LZsrqqR: ›�$,N]aa_cioqSA:=BEILMOPRTUVXZ\]^_adeg€fhglV D)P G#$L L )M M#icdhlnopqsttuweYh}{{y[>œ�#(L]`a`cinqV><<BEILMPQRTUWYZ\]_`bdeeghlX F+!R J%&N N",O O&icdiknpqrsstuvcYj|zzwY:œ�$J\€`chmrY:?;BEIKMOQRTUWYZ[]^`bc€e�f€gkY H.#T K((P P$.P Q!)icdiknpqqrsstuaZk{yytW5œ�!H[a``bhmr[ 1?;BDIKMOQRSTWYZ[\^`ace fggkY K2&V N*+S S'1S T$-icdhknoqqrrsts`ZmzxyrT1�EY€`bgms](-9CEHKMOPRSTWYZ[\^`acd‚egjZ L4(X P-.T T)3T U&/icdfi[bXhpqrsr_[nywynR,�CX€`bglr`-)(5CIMMOPRSUVYZ[\^`acdd‚ej\ M5)X P..T T*3U U'/i‚cfE€â―bdprsp][pxuxlQ&�@W€`bflsb1,*(-9FMQQRSTVXZ[\^_ac€deiZ M5)X P..T T*3U U'/icfX(ßĸĸ|Kqqrn\]pwtwiO!�<U€`bflrd6%0//--.6BKRVWWXZ[\]`ab‚deeiZ L4(X P-.T T*3U U&/icf]“ĸŨ0Wpprm\^qutvfL� 9S_``afkrg>.ƒ20029CNUZ]^__`acd‚efjZ L4 )WP. /TT* 3UU& /jfgkR,O:Lmnqrl[^qusucJ� 9Ubcbdhnvh>2†4 3137=FKQVZ]_aab7afW'%I:&3R'%L6&7O&&O4%:P%&P2&5bZTRQRUZYP[ddilnh[buwvwdHž� DTUUWYZXLA…B‚CBA@@ABDEF€G‚HGEEFHEFH€F HGGHGGHIFFH€GHDBBCB€A CCDFIKLLQ[€fgO(ž�$€%'&&(€*ƒ+‚,†-„.//1202€3€4 55657769889::9€;:<==>?@@€A@@??A?ĸ�ĸ�ĸ�ĸ�ĸ�ĸ�ĸ�ĸ���Ž�€<˜;<Û�Kc™|bKÛ�hž™™žhÛ�“Īŧķ‘ĩķŧĢ“Û�:>€G„F€EDEƒFGGF>:Û�!Vœ—™™›žŸĄĒĪĨĶĻĐŠŠĐĻĶĨĪĒĄŸž›™™—TÛ�!Íąģĩī··ļđššžž―ūū―žžŧšđļ··īĩģąÍ~Û�}―œ€ž€žœūzÛ�|ŋĄƒĢ‹ĪƒĢĄŋyÛ�|ūĄ†Ģ…ΆĢĄūyÛ�{― €Ē‘̀Ǡ―xÛ�zŧ ‚ĒĢ‚Ē ―xÛ�zšŸĄ…Ē…Ģ†ĒŸžvÛ�x🡕ĒĄŸđuÛ�x·ž‚ĄĒ‚ĄžđtÛ�wĩ€ ƒĄ…ĒƒĄ€ ķsÛ�vĩ€Ÿ  Ą  €ŸĩrÛ�uģ›žƒŸ‰ ƒŸž›ģqÛ�t皜žŸžœšēpÛ�q°™›€œ�ž…Ÿ�ž€œ›™°oÛ�rۘ›œˆ€œ›˜ŪnÛ�qŽ—š‚›�œƒ›�œƒ›€š™ŽmÃ�ÔÓŌÚØÚÕӁՁԀÓ�Ō€ŅÐ`nŦ–˜€™ƒš„›‚š€™ ˜–Ŧl^Āŋū―ŧŧšš€đ ļ··ĩģŊ­ēŊīą­ËŠ�”ÚŨՌSÓÖØØŨÕŨŨԀՁŌŅÏwmĻ•——˜‚™šƒ™€˜——–ĐjsÁÁĀŋūū―žŧ€š đđ·ģ°­p=bŦģŧŧĐ�ƒāޚ'â€Ų�؀ŨÖÖՀÔÓŌŅwkĶ“––‚—‰˜‚— ––“§irÂÁĀŋŋū―ž€ŧ šđļ·ģĩ…/mšđdĻ� \ãßĒ;RĒâ€Ú�؁ØŨրÕÔÓŌwjĢ“”€•–‡—–€• ”“ĢhrÂÁĀŋŋū―ž€ŧšđļ·ĩķ•FRz·žo:§� [žįÛāÆÁÓāÚ€ŲØŨÖ€Õ ÓŌŌŅwiĒ‘’’“€”‰•€” “’’‘ĒgrÁĀŋŋū―€žŧšđļđ·ķĩđŊŦ­ķģž|<§� Z§æÛÜāāÞÚÜÝŲØ€ŨÖÕՀÓŌvh ‘‚’“‚’‘ erÁÁĀŋūū――ž€ŧ ššÁÁķĩļ·ķģēŧ„;§�ZēäÛÚÚŲŲÜÏ­ÕŲØŨÖØÖŨŨÔÔÓÕÓÓwfž€Ž€€‡‘€€€Ž ždqĀŋū――žŧŧš€đļķđŠž·ī€ģąļ‘<§�]ĀâÛڀŲڂ~ÜØÚĮĀŊšīķšļžą·ągeœŒŒ‚އ‚Ž€Œœbqŋ€ū―ŧ€šđđļ··ŧOSķĩģģē°ķœ?§�^ĖßڀŲÜÓnÞŨÚÅšĪáīÉÛâåŽŲÐxcšˆ€‹€Œ‹€Œ€‹Ššbqŋūū―ž€š đļļ··ķŋ_+Đļē€ąīĶD§�eÕÞڀŲÝÎj—ßŨÚÃĩąŨÉĖÓŅŌÉÏÏub˜…ˆ‰‰€Š‹‹€Š ‰‰ˆ…˜aqŋūū―ŧ€šđķĀj*Ąđē€ąēŽH§�iŲہŲÝÅeāÖØÁīŪŋūđÃū°Á―ēka•…††‡€ˆ…‰€ˆ‡ ††…•_pŋū―ŧŧšđđ€ļ·ķĩĀq&–š€ąŊ°ŊM§�rÞÛŲŲØØÞ―aĪßŨÖŋģąđļšđīŧ·ķši`”‚„„€…†€… „„‚”^pū―ŧŧšđļ··ķĩŋ|#‰ŧąą°ŊŊīW§�â€ŲØØāģ^­ßŨÖÁąžÃŋÁ·īŋ―ļķn_’€‚‚€ƒ„€ƒ ‚‚€’]pū―ŧŧšđļ··ķĩ―ˆ#š°ą°ŊŊ·bĶ�Xã‚ØāŽ_ķÝÖÖÂĩŊīĩĩ°°īīēŊf]Ž}€€‚‰‚‚€}[o――ŧšđđļļ··€ķĩž’%xš‚Ŋ·o;Ĩ�W›ä‚ØßĨ`ūÜÕŨÁÁ€ŋ ūŋŋ――ūŋk\Ž{}€~‡€€~ }{ŽZo―žššđđļļ·€ķĩĩš)lšŊŪķx:Ĩ�VĪâØŨߗ]ĀŲÔӀÔÓŌŅŅÐπÎs[Œy{{|}ƒ~}|{{yŒYnŧŧššđđļļ·€ķĩĩļĪ.aš°Ŋ€Ūķ‚9Ĩ�Wąá€ØŨÖݑâۀŌŅÐЀÏ�΁ÍrZŠwyyzŒ{€z yyw‹Xlđĩļšđ�ķĩ ·Ļ3Uļ°ŊŪ­­ī:Ĩ�YūރŨÉïĸųû€ú‡ų‹Y‡uwxyx wu‰WŒôôÉķ�ķĩ ģĩŽ:KģŊŪۭް—>Ĩ�^ČۂŨÖŨæÁ Ķœšš™—•’‘‹‹‘QT…r€u€v‹wv uur‡SS’€gprtvxy{||}†…—CAŪŊ­ŽŦŠŪĄBĨ�cŅÚŨŨ€ÖÕÔŌ“ixve`_]ZWTQMKHK+R„pr€st‡ut€srp…P,KHKNQTW\]a`evxj†N=Ŧ°ŽŦŠŠŽĶGĨ�hÖÚŨŨցÕԞtz‹‰wrsqnliecae6N‚oqrqo„L7daceilnpssv‡‹{s“X7Ĩ°ŽŦŦŠŦĐLĨ�rÜŲŨŨÖÕÕÔÔÓĨˆ‡Š—–ˆ‚}xwsqw?Kmo‚p‹q‚pomI@wqswy|ƒ‡–˜‹‡ˆc0ą€ŦŠĐŪUĨ�~ßŨ€ÖÕÕÓŌŌĄZ\YT_`URRQP€NS/N€jlmn€mllj€M0R€NPQRRT`_UW\Y‘k,•ģŽŦŦŠĐ°bĪ�TŒâŨ€ÖÕԀŌŅr�JCPRJGFDB?<:@jzfhƒi‰jƒi hf{i@:<?BDFGIQPDI!�dķr)Šĩ­ŽŦŠĐēm<Ģ�T™áŨÖÖÕÔӂŌĨŌūž―·°ŦĄ—‡‚|pc€e‘f€ecq|‚‡—ĄŦą·――ūŌŽv°ļ|(€ĩŽŦŦŠĐēw:Ģ�SĒāÖÖÕÔÓӃŌ‹zzy{srpmkjea‚bc‚baejkmprszyyœ€oąŊķ†(zķ€ŦŠŠą;Ģ�TŊßՀÔÓÓŌŌ€Ņ ÐЏeWmzf[]^›_^][eynWiwąąŊĩ*pĩŦ€ŠĐŊ‹:Ģ�VžÜԂÓŌŌŅŅ€Ð Ï–aƒVjyfYĢ]YdykV‚fyēēąŊĩ™-cĩŦ€ŠĐ­–=Ģ�ZĮŲԀÓŌŌ€ŅÐÏÏÎÍ͙\„VdvdWĄZXcvgVƒ`…ąąŊŪŪąŸ0XīŽŠŠĐĻŦŸBĢ�`ÏØÕÓŌ€ŅÐÐπÎ�̀ĖœU„V`sbSŸWSaraU„[‚°ŊŊŪŪ­ŊĄ6M°ŦЀĻŠĢGĢ� eÔŨÕÓŌŅŅÐÏÎÎÍĖ ËĘƟO„VZo_PSP^m\TƒUˆ€­ Ū­­ŽŪĶ>DŦŦĐЀϧLĢ�pŲÕÓŌŌŅÐπÎĖĖËËÉČÆÉÎŦH„VUk]N›PN[kVTƒNˆŠŦŽŦŽŦŠŦŽŠF>ĨŽĐϧĶĶŠWĢ�|܀ŌŅÐÏÎÎÍĖĖËĘČĮĮĖÍÅŋĪ BƒWQdZKK—L KKXeRTƒGĻĻ‚ĐŠĻĐ­N: Ŧ§€ĶĨŽ`>Ą�VŠÜŌŌŅŅÐÏÎÎÍĖËʀĮĘÄū―žŧĶ=WKaWFF•HGFVaMTBŽĨĪĶĨĨͧ§Ķ§§ŊW3™Ŧ€ĶĨĪŦl>Ą�S˜ÜςÎÍÍĖËĘČÆÆÅŋžŧđ·ķīŸ%5XG]TDC“ECBS]HV; ŸŸ ĄĒĪĨĶŪ`,ŠĢĪĒĒĢŦx>Ą�RĄØËÉŅÓÐɀĖËÉĮÁÂūžđļĩēąŊ­Ÿ)/}YCVP@?‘A#?@PXDV}4Ššš››œžŸĄĒĢĪĨĨŊf&ĢĄĪĪĄĄŠ<Ą� RŦÔĮÄ~Q…ĘʀĖÆŧŋĀ―ŧ·ĩąŪŽĐ§Ĩ™+)z[>RN=;=<=MR@W{.$ˆ•”•–—™™œž Ē€Ī Ŋm#rĄn@W• Ļˆ<Ą�%U·ŅĖ{ŠŌËÎĮīŋÂŋžđĩą­Š§ĪĄŸž–2"w\;MI:8:%8:IM=Yy'#†ŽŽ’“•—šœžĄĒĢĢŪw$m"RĒĶ’=Ą�&XÂÐÐY6~2wÔÍÉĐžĮÁŋž·ī°ŽĻĢĄž›˜•.t]8GF74‹6&46FI8Zv",‡ˆŠ‹‘•–˜œž ĒĒĨށ)lt,zL? ĪšBĄ�']ĖŅзt—œÅÏÍĄĩĖÆÃĀ―ļĩ°Ŧ§ĒŸ›—”‘ŽŠ4p_5CD41‰3'14CD5[r&|‚…‡‰‹‘•˜™œŸ ĒĪĪŠŠ-dĐvˆ€ŠĢĢ IĄ�(cŅŅÏÔŲÓÖÏŅĒĢÏČÆÅÁ―šķ°ŽĻĢž™•‘Ž‹‰…4k`3?@1.‡0(.1??2]n.x|~€‚„ˆ‹Ž“–˜šœŸĄĢĪĪĻ“,WŽŠĶϧĄĒĒMĄ�jÔÐπÎ"ÍÓ°‰ČËČĮÄÂĀžļģŊŦĨ š–‘ŽŠ‡„‚=ga0;=.*…,)*.<;0_i0vxz|‚†‰‘”˜šœŸĄĒĒĢĪĶĨvbĶĢĒĒĄ  ĨWĄ�xŨ€ÎÍĖÐÆ~ŪπČÆÄÂūŧļēŪĐΟ™”Š†‚|< ac/7:,(ƒ*+(+:7.`e5tuwy|€„ˆŒ”—šžŸĒĢĢĪĨĶͧĨ•ĒĢĒĒĄ  Ļ`>Ÿ�O†Ø€ÎÍĖŌ•ÎËɀĮÅÃÂūŧķēŊŠĪŸ™’‡‚~{x@\e-27*%',%)73,a` 8psuwz~„ˆ‹“˜›Ÿ ĄĒĢĪĶĶĨĨĢĨ…ĨĒĄĄ Ÿ§k=Ÿ�JM•ØÎÎÍĖÐūkĐŅÉÉČĮÆÅÅÃĀŋžļī°Ŧ͟™’‹…€|xvDWf,.5(#%%#(40+c[Dposuz„ŠŽ’–š ĒĢĢĪ€ĨĪ ϐišĨĄĄ žĶu=Ÿ�GMžØÎÍĖËӒmČÍĘÉČČĮÆÄÄÃÂĀžš·ē­ĻĄš’‹…yrrEQh,,3( &3-*dV:v}tty†‹‘–™ ĄĒ΁ĨĪĨĨĪĢĒĪĄ[vĻĄĄŸžĨ|;Ÿ� MĐÖÍĖËÍČiˆŌɀČ5ĮÆÆÅÄÃÃÁŋ―ŧļīŊĐ̜”Œ…odrnG�Lh,)2$$1**fP�>ij_k|€†Ž”™œ ĢĪ€Ĩ�§€Ķ€Ĩ�Ī€Ē ŠkLĪ ŸŸĢƒ<Ÿ� OĩÓĖËËŅ­WĒŅ€ČĮƁÅ.ÄÂÁĀĀ―ŧš·ģ­§ ™‚[mxpkIFh,(11(*fK�Cfjr[9p‘—ĄĢĨ…§�Ķ€Ĩ�Ī€Ē Đ€5€§ŸŸžĄ?Ÿ�SŋЀËӐVĩÎČČĮĮ€ÆÅĀÂ)ÁÁŋū―ŧšķēŽ§uY|~sniJ?i,'()gEDehnxo&GšĄĨρĐĻĻĐϧĶĨĢ€Ē §“3b§ŸŸž •CŸ� [ČÎËĘÉÏw]ÁĖĮÆÅĂÂ*ÁÁĀŋūūžš·ķ­oeˆˆ{skgH9i+'h?Fcglu‚~<:šĻ§ĐĐρĐĻϧ§€ĶĨĢĒ Ī›;IĒ ž›HŸ� `ÍĖÉÉËĮf`ÂʀÆÅÃÂ�Á€Āŋū€―"ŧšīƒ‰œ˜Š}rjeM3hg8Eagnwƒ’‹fZĒŽŦŠĐĻ€Đϧ§€ĶĨĪĢ‚ĒĢžB>™ €œNŸ�gÐËÉÉËĀ_bÂČÆÅāÁĀĀŋū!―žŧŠīĀĻ›€ticM05J`fo|‰•Ÿž’Ū­ŽŠŠ€ĐĻĻ€§ĶĪĪĢƒĒ ĢŸG<• ›šŸUŸ�tÓÉÉČÉū_bÁÉÅÄÃÀÁĀĀŋū―žŧŧđŋÖįŨ§Ą—‡wkcQ Kaht‚›ĒēģĐŠ­€Š�Đ€Ļ€§ĶĶĪĪĢ‚Ē ĄĄĢH@” œš™ _@� J‚ÔÉČĮÉÁb`ŧČĀÀ€ÁĀŋŋ€ū―žŧŧ€š·Þųþâ°Ē›~pfHHdnz‰–ŸĻĀČĀĩŽ€ŠĐĻ‚§ĶĨ€Ī�Ģ€ĒĄĄ  Ē˜KF•žšš™Ÿi>� I‘ÔÉČĮČĮm_ŪĘĀÀ€ÁĀŋ€ū�žŧššđķŧōĸĸðÅŠœ‚vnnu€šĪ·ŌØÐÃ­ŠŠĐĐĻ §§ĶĨĨĪĢĢĒĒ€Ą ŸŸĢKQ™š™˜r?�JšÓȀĮĖ‚cËÃÂÂÁĀĀŋŋūūž€ŧššđđļļīđėĸüõęÔ𠐇‡ŽžīŋÍØÝÝʭЊЁĻ§§ĶĨ�́ĒĄĄ €Ÿ ΁Pbž›š˜—x>� JĪŅČĮĮÆĖšlĮÂÂÁĀ€ŋū―žŧŧššđđ€ļ··ģąÐïöýýúóæÞÝæōųäŲØÕĀŠĐŠĻ§§ĶĶ€ĨĪ́ĒĄ €Ÿ žžĄvZwĄš™˜˜œ?� KąÎČĮĮÆÉąz…ŧŀÂÁÁŋū―ŧŧšƒđļ·ķķīŊīĖėþþýûúúûýþįΞŪĻŠŠ€ĐϧĶĶ‚Ĩ�΁ĒĄ €Ÿžž˜qfŠŸš™˜˜š‰A�PžĖĮÆÅÅÆÃ‹ŦÆÂÁÁ€Āŋŋūū――žšđ€ļ�·€ķĩĩģŪŊšÉŲãææãŲĮģŠĻ€Š€Đϧ€Ķ€ĨĪ́ǀ  ŸŸžŸŒuv™š€˜—˜D�XÄÉÆÆÅÅÄČŠ—ĨĀÃÁ€Āŋūū€―žšđđƒļ·ķķĩĩīģģēŪŦĐĐĻĻĶͧ€ŠĐϧ§ĶĨĪĢ́ǀ ŸŸž€ œ†{‡™˜˜—–—”I�^ČĮÆÆÅÄÃÅŋĶŽšÃÁÁĀŋŋūū€―ŧšđ„ļ··ķĩĩīģēąą°°Ŋ­Ž­ŽŦ€Š€Đ€Ļ�§ĶĨĪ́Ē�Ą€ Ÿžžž”ˆ…–›™˜—––——N� fËĮÅÄÃÃÂÂÄļĩŋÂ€Āū――ŧššđ…·�ķ€ĩģē€ąŊŊ€ŪŽŦ€Š�Đ€Ļ§ĶĶĨĪĪĢĢĒĒ€Ą ŸŸž›’Œ‘›™™˜€–•™Tœ�LsÎÅÄÂÂÁĀČÏĀūŋ€ū―žŧššđ‚·€ķ‚ĩģē€ąŊŊ€ŪŽŦŠŠĐσ§ĶĨ€Ī�̀ǀĄ Ÿž››š–’™šš™˜––•”š^A›�I€ÐĀÀÂÁÁÄÓÝŲŧžūūžŧŧššđļ··€ķ…ĩģē€ą ŊŊŪŪ­ŽŦŠŠĐρ§ĶĶĨĪĢ̀ǀĄ  Ÿ€žœ›šŸž™™€š™˜—–•”›g=›�HŽÎÃÁ ĀūÏčņÞđžžŧ€šđļļ·ķ…ĩ�īēą°€ŊŪ­€ŦŠĐĻϧ§ĶĶ€ĨĪ́ĒĄĄ ŸŸžžœš™ĄĪĄœ€š ™˜——•””™o>›�H—ÍÃÁÁ ĀĀ――āũúā·ļŧššđ€ļ·ķ‚ĩīģēąą°€Ŋ­Ž€ŦŠĐĻϧĶ́ĨĪĢ€ĒĄĄ Ÿ žœœ›šĢЧ ™˜—–•””’—t>›�HĒĖÃÂÂÁÁŋ žūęýĸåļ·šđđ€ļ·ķ€ĩīģģ€ēąą°€ŊŪŪŽ€ŦŠĐĻϧ͂ĨĪ́ĒĄ €Ÿ žžœ›š˜§Ū­Ķ™€˜——€•“’‘”|?›�JŊʀÂÁŋ ū―đžïĸĸï―ē€ļ·ķķ€ĩīģē�ą°ŊŪ­­ŽŦŠ€ĐϧĶĶ€ĨĪĪ́ĒĄ €Ÿžœœ™›Žģ°Ļ™–˜—––€•“’‘‘“ƒ@›�QļĮÂÂÁÁĀŋŋū―žŧŧļšëĸĸųĖąĩĩ€ķĩīīģģēą‚°ŊŊŪ­­ŽŦЁĐĻĶĶĨ€Ī€ĢĒĒĄ€ Ÿžž€š˜ ąĩīŠ˜•€–€•”“’‘‘‘ŠC›� WĀÅÂÂÁÁĀŋŋū―ž€ŧ·ķā€ĸāļ°īĩĩī€ģąąƒ°ŊŪ­­Ž€ŠĐ§ĶĶĨ€Ģ‚Ē�Ą€  Ÿž›ššŠ·ķīĻ–•€–€• ”“’‘‘ŽH›�]ÄÄÂÁ€Āŋū――ŧ€š đ·ģÏúĸýōÍ簁ģ�瀰ŊŪŪ­­ŦЃĐϧĶĶ΀́Ē�Ą€ žž››Īēļ·īĒ–——––••””€’‘ŽŽL›�cČÃÁĀĀŋū€―žšƒđ ąžæüųöâÄēŊą°°ŊŪ­ŽŦ€ŠĐЀϧ§ĶĪ΀̀ĒĄĄ Ÿžž›œĢŪĩ··­œ—˜˜—––•”“€’‘Ž’Sš� IpËÃĀĀŋū――žŧŧšđļļ·ģąÉëôîčÛÆģŽ­€°�Ŋ€Ū­Ŧ€ŠĐĐĻρ§ĶĨĪ̃ĒĄ Ÿžœ›ĪŽēīīąĢ˜™™˜——––•”€’€Ž“\A™�F~ËĀĀŋ€ū―ŧ€šđđļķīŊģĖâįææáÔĀŊ§ĻŦ­Ū­­Ŧ€ŠĐĐρ§ĶĨĪ΁ǀĄŸœ›Ą§ŽŊ°°ŪĨš˜šš™˜——–€” “‘ŽŽŒŒ‘e@™�FŒËÁŋū―ŧššđļ�ķ€ĩģ°īÆÚåéðöðāĮīŠĶĶĻĻ€Š�ЀϧĶĶĨĨĪĒĒĄŸššĒаģģ°Ŋ­Ŧ̚—˜——–•€”�“€‘Ž€Œ‘l@™�F”Éūžŧžšŧžŧššđļ�ķ‚ĩēŊąūÕëøþĸĸþïßËžēŠĨĢǁ žžœ›šœ ĻēÃŅÕÓÉÁđīŪ§Ÿ™–˜˜——˜€—•”“€’ ‘‘Œ‹ŠŠ‹Œ‘p?™� GžÄļĩŧū―đķđšđ‚ĩīī€ēą­ŦīËčýƒĸýôëäÜÕÏĖĮĮĘÍŌŲāčóýĸĸüíÜËūąĨœ˜—˜„—–••”’€‘ Ž‹†…ˆˆ‰†‰ŽxB™� AĐūēī‡JYĄļķ€ļ·ķķĩīģē ą°ŊŪ­ĐĐģÆÜðü“ĸõÛŋŠž˜—€˜�—–‚•’’‘ Œ†wK8[ƒ„‹4™�(ķšĩ‹ A°·ļļ·ķ€ĩīīģēąą°ŊŪŽŠĶΧŊūËÚčðöüþƒĸ þüöïįŨŷʘ˜™™€˜——–ƒ•”’€‘€ ŽŠƒ~D[ˆ‰Š%™� >―·š`(gN(Ģđ··ķĩģģ€ēąą€°ŊŊŪ€­ŽŽŦĐĶĪĄŸĄĒĶŦŊģĩķķĩąŦĶ ˜––—šœš€™˜—€–€•€”�’€‘�€Ž ‰…z(Lf4@ˆ‡Œ2™� HÂđŧ™=i{oļķ€ĩīģģēēą€°ŊŊŪ€­ŽŦŠŠ€ĐϧĶĨĪĢĒ …€Ÿ�ž€œ›š€™˜—€– ••””““’‘Ž ŒŠ‹_HŠKd‰‡=™� LžđūĩĄĩ―·ķĩĩīģģēą°ŊŪŪ­ŽŦŦŠŠĐϧ§ĶĶĨĪ„Ē�Ą Ÿžœšš™˜—€–••““€’‘ƒŽ Œƒ‡‡‹‡ˆŽI™�pØļīīķļģąŊ ŪŪ­­ŽŠŠĐĐĻ€§ĶĶĨĪĢĢ€ĒĄ Ÿžœœ›€š™˜˜–••””’’‘‘€Œ‹ŠŠ‰ˆ‡€…ƒ„ ‚‚ƒ€~~…’[™�9ãÛëįįæåääããæęæį῁ä�ãâ�áā€ß�ށÝÜÜÛÛÜââÞßáāÝāāÜÝßÝÚÞÝŲÛÜÚŨÛÛŨÛÛ؃ÓŅŅÐςΠÍÍÎÎÍĖËĘË͝yš�}ķŋÁÃÅÆļĢķ°īĩ·ļšŧžž€ūĀÁÁ€ÃÅÆĮČ€É ĘĘËĖÃϧ―ģ§ŊÃĐĐĀģĻģÃĐŠÄģŠļÅŦ­ÆģŽķҁÐŅÓÕÖŨŨØØŲŲÚÛÖÏÕÞÞÝßփ›�$-O`ecbflrsM D28?DGILOQTTVXY[\^`cegjijkrU��>!�M��C�I��I� J��J�keflprsuwwxy{|jZj‚€€`E ›�"4Ykpompw|~_LDFMPTVZ[]^`adefijknoq€tu{b M. "[ Q( (W V#.W X 'wq€prwy|}~€ƒ…revЇˆ†iI œ�!/Xjoonqv{€c HFEMQSVZ[]_abdegijknprt€uzc O1 $] T* +X X% 1Y Z" )wqrwz{}~€‚ƒpfx‰‡ˆ…fCœ�"*Uhoonpv{e#CIDLQSVY[]_abdeghiknprs‚tuzd Q4 &_ V, -Z Z( 3Z \$ -wqrwz{|}~€‚‚nfzˆ…‡‚d>œ�#%Sgoonpu{€h'8IELPSVY[]_abdeghikmprss‚tze T6 )a X/ 0\ ]+ 5\ ^' 0wqrwy|}~~€€€mg{‡…†a:� Pf€npuz€k04BNPSVY[]_abceghikmpq€styf U9 ,c Z1 2^ ^- 8^ `) 3wqruwulpy~~€€lh|‡„†}_3�!Menmmptz€m4//=MUWY[\_`bceghijmoq‚sttyf V: ,c Z3 2_ _- 8_ `* 4vqruY9‡Tdv~~€~ii~†ƒ†y]-�"Jcnmmotz€o9#30/4BPY]^_`bcefhijmoqr‚styf V: ,c Z3 2_ _- 8_ `* 4vqti)&ģNBd~}|ij~…‚…v['�Fa€motz€r?+766436>LW_bccdfhijloqrr‚sxf V:,c Z22^ _,8_ `*3vqtk+*i~}~zhkƒƒtX �B_€mosy€tH5‚;:88:BMZcgjkklnpqs€uyfU: -bZ3 3]^. 9^_+ 4xtt€uyb62`{{~€ygl‚‚pU � Capqpqv}„vI<„> ==<:;@GPW\bgkmo9pqqtb.,TC-:].-W>-?Z,-Z;-BZ--Z9-<og`^]_agijoqrvy{uhpƒ„ƒ…rT� &OabbcdecVI„KLLKKLLKJIIJLMOP†Q NOPPOOQOOQ€P ROPRPPQRPPR€PQM‚KJJLLMPS€U[g€rs[0ž� *++,,++-//ƒ0‚1�2344334434€5€68878€9€:;;<;€=�>€@AA@BCCDDEDDEFFGFEEGF$ĸ�ĸ�ĸ�ĸ�ĸ�ĸ�ĸ�ĸ���t8mk��@�����������������������������������������������tøęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęøn����������������������������������������������������������������������������������������������ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸy����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx����������������������������������������������������������������������*œšššššššššššššššššššššĖĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸƚššššššššššššššššššššœƒ0���������������������������������������������DėĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸņP��������������������������������������������ģĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÁ�������������������������������������������ÏĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÚ������������������������������������������äĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸí������������������������������������������ōĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõ!������������������������������������������*úĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ4������������������������������������������@ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸN������������������������������������������[ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸi������������������������������������������wĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸƒ������������������������������������������Œĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ•������������������������������������������Ąĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ­������������������������������������������žĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸČ�����������������������������������������ÓĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÞ ����������������������������������������įĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸï����������������������������������������óĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸũ$����������������������������������������-ýĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ8����������������������������������������EĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸR����������������������������������������`ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸm����������������������������������������|ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‡����������������������������������������Žĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ˜����������������������������������������Ĩĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸģ����������������������������������������ĀĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĖ���������������������������������������Öĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸá ��������������������������������������ęĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸð��������������������������������������ôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸų'��������������������������������������1ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ<��������������������������������������JĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸW��������������������������������������eĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸs��������������������������������������‚ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŽ��������������������������������������—ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ ��������������������������������������Ŧĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ·��������������������������������������ÄĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÏ������������������������������������Úĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸå������������������������������������îĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸô������������������������������������%øĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸü,������������������������������������5ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸA������������������������������������Oĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ\������������������������������������iĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx������������������������������������‡ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ’������������������������������������šĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĢ������������������������������������Ūĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸž������������������������������������ÉĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÓ���������������������������������� Þĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸč����������������������������������ðĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõ!����������������������������������(ųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸý/����������������������������������9ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸE����������������������������������Sĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸa����������������������������������oĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ~����������������������������������‹ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ•����������������������������������ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ§����������������������������������ģĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÁ����������������������������������ĖĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŨ�������������������������������� âĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸë��������������������������������óĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸũ#��������������������������������*úĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ2��������������������������������=ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸJ��������������������������������Xĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸf��������������������������������sĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‚��������������������������������ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ—�������������������������������� ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŦ��������������������������������ļĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÅ�������������������������������ÐĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÛ������������������������������åĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸï������������������������������ôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸø%������������������������������,üĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ5������������������������������AĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸO������������������������������\ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸj������������������������������yĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‡������������������������������’ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸš������������������������������ĢĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŊ������������������������������žĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÉ�����������������������������ÔĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÞ ����������������������������čĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸņ����������������������������$ũĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸü,����������������������������3ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ:����������������������������FĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸS����������������������������bĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸo����������������������������~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ����������������������������šĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĪ����������������������������Žĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸķ����������������������������ÅĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÏ�������������������������ŧĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÆ�����������������������Pôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸų[����������������������� ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ ���������������������� wĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ„ ���������������������� hĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸs ���������������������� Xĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸd ��������������������� NüĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸU ����������������������� GũĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúN ������������������������ =ėĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸôF ������������������������ 6āĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸį< ������������������������ 1ÓĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÜ6 ������������������������+ÃĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÍ/������������������������)ŋĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÉ,������������������������%·ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸū)������������������������#€ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‰%������������������������#B’ÛîïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïݘE$������������������������5NkˆŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠ‰‚mP6 ������������������������� ,@Q]dfgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggd^RA- �������������������������� !2@JPSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSPJ@2" �������������������������� !+38:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:83," �������������������������� "######################################################################################" ���������������������������  ����������������������������������������������������������������������������������ic09�Â��� jP ‡ ���ftypjp2 ����jp2 ���Ojp2h���ihdr�����������colr��������"cdef����������������������jp2cĸOĸQ�2�������������������������������ĸR� ����ĸ\� PXX`XX`XX`XX`XX`ĸd��Kakadu-3.2ĸ� ���Ø�ĸ“Ïūö—zYÝdŅ€†h&’‚ŋōĨ‡æ CFE°9D“þŪ!Á·"“ĻĮøŨ-B^ð͌ōJY9…[τ„ƒdÏãÂĻoiĸŒŊŋŲøĄWÐËÁ=0B�=ËWšä9ŠL6Úœô([okĄęė™3-ņ‹Ōŧøþųņó™ĘõŽ3ŽĄxĻprųVúh(ôÔĶũ–>ņT=i†SÞ]íģw„NÍxîįé úęĒÆ/S‹WĀ“īBĶĻHŊޜJVĄķœCSlįŠ\ņH ðTę^Å%‰YÉį'cē,­ŠÓ+Œv1[Å,―Š`sī'~qŋÖū ÁóW@Ą ĩķŧG<cóÕ|[“ú­Zv]+XĻøFúA~!wĶý§E°šŧQB|‘ �þõuĨƒŠ„9ā˜jŽĄŽ§íÂ7Ļ} #Æã#F(ŨHĸ) ü[u .ø=e‰MŪ€ÏūÔOþŦ…č5å'ęóÚðǁR/)ļĩKŠ Vkįî+ÅjÍ̆e čŧōÍ'Øï΂Đož đĪß2ãĐĄš'ÄR{wĀž›ýŧ§ē‹[šOŅ#0./`üĢČGŌ}üË7:G*w$ÞåaŠüO‰*™Í°ÕäI=Ā:ϧx.æj”Z#”WŌ·ïßA‡jjĄ‘―TI5ï/ŠēŽ‘ømJjZþúR{ęb(ØĮ™’•ëz‡Âiėh1Ī˜ÝŽŽ5šÉí’ĢW]�aØþáæô “œį9Ė~ÏĘÆŸ•w•vžYZoÜę’]‹a›īs Ôîï2p_ĢQ þü3ĮÆ/6^‹3ã=Ūģ”ą ssk9j"ÁôæÏNdˆ q@o F9:j$]rp]‡ŲĒ„ûÎôwtÉ Ã$ÝÏč„BA ˆ žKnóv%7…h ĶąVx€rŌF‡°AĶÏå^*ô:‚NËÅ|öęáŌbŧÜšï —ƒzo B^ĻŊ,T;ІŧÝp-ß cFŒy*ŸvÆį“ZŋŨ‰!ūÉĪplKη-/éÉ`™ó_i‡Ā-ČéĮK*k­BYĢcĩ6‘Šę*ú™đ@ņį“$1Îr҈…ŦNžŋņx`Ū6Ÿ{ƒ)ą1âXL?íY1!·ņĘĄæĄmķ~z­qLųül Ģ.óÎ| RqüM`3ƒŒ“=3ûØøė äíIĪ“'[ĖŠb Or/í9BÛE€jŧ… ”=P[ÝËIŨēóO™ęGtSYcy1B‰ūēĀQ°ÚÞÅ:Ģ4DņØõ@€·#Ģuv_™čßý$d”‰ÖôŽV’qŋHe#ąGĩÆ>s 9^Æp§†ŧ É,•ēė4B6ĀčOŋWõ;ŽoAāãėÃQĮcãŽíĘÍQ"žÐX§‹Š6ØĨ– o+$€4>+Œ‰3!ĸ`6!ýÎZƒšũVōÝAY_ÅÂ,ŪōÓ°6Áē’ÞúĶ–GpēĮđ$øķķW_.cŌÎÛōL'ū^Üžëg–žÚÐ+͝‚ÃÃj›o>ĄĮĶ{0-ÁôÓáöĐAö§�П[ÜŨðZŨWŨ(oŽ.ÝĨČf‘öģ°ïZ˜öŽŽ>}œ ė&îHZ&æ%ÃŒļk a ð݂7•w8‡ ā‡b2QeMī4e Ų`wŠŠ‘IÓRCÂļë…ũKį=Ŧĸ>.Åm=zäLļþ›eŌýŧņóŅ-8gÆ(ņ›ÚčŪ/žßĻ9ˆęŅĖÔņņ4Õâ-xå%ž"ú=öaũ~RDóO'ЉÓ,~~ŧJ J[/í īsCŠW’ÐņÄiØąúö{ýmUq7ĨkÍßŪ r_zöïYûzÓąĢ%z+īątæïâW=T ˆb~ĸ5>Ü€ĮÞÁ‚äjPĒņL‰ŧÏíL—Ŋ$rž•žņ)íãęĢlĨ`;ƒí |ą 2G}žG°]Â\yW7l‰˜-ņsš‹0Āŧ1œ~>œuĘdîZïg–šF$ĻÏŽŽŠĪüú\ģðŦTä–ĒzĐ"á­ĩýÖĖ–æL”ðTē'œŨ(ßHpÔnČŠiæ(~MÝÄÐ|ąĐaQ2| _ Ȗ/Ērԝ$Jڋ 'ęCÁ€Ÿ3[ƒĮœQmæķ8-û4o~’įJ)önûŦTĒŋáQgí4�ĮœõÓDÅLĨq'įčL‡П dNņųįã <tX#] ō)>árģb_žÁ]Ļ?aóŠKëČØÉĮáŅt~ Įįū�zЧ]ßØ_Õîā]ãë°?Ð8[x­kBÍ―ū›%ŧ"KÎvPĸ_Ÿ—ãÃÍ3ââ]Ŋ"f}§ûctäþâÏVĪ\#ņL8Ÿ ĶõÞVÜņC0S‰…Vƒ_.q­L} ;ÍĸÕ=Ïí”s˜3ÛöĶâD€!Lã č€āß�ô@�†ąM’,…Ï}ģwĶ–įŸ!ĩJ#š–ŲÍWä1NĘÜÃlHMÕŨ"CįÞąēÄížæzų X/bLkŅŋ_ ~“imå›ý>pþ}]4.Ý ·rõjEûtauð%:ųŨUę [€­ķÁĻ_‰[ķ/ØFm*ŠMߘÚo>ŽÔ8DëĶH6ïģÁ x%ëTô*_:YŲl€*c؞ējˆ‹vŨãņäú"āĖ< œČÕÓåŠ―"™NeŊĸOœpŌKy9W^X ņÆĨ\Ō SXÔĶ%{ŠĘĐęĖ Ž6c õp—TÝԂd™ëbEݔēö+€šm,ˆû6šþ7jCŸĢzņ ]ÞÄåovačd2507*ŧŅ,‹RŽäi'$ÚPk^‡āČžC /^0Žĩ'‹uˆQ ­ũyņFëdÕ&Eû#ĩ}(čÄcēūĖ=Ō"KRį:ÞöŠÂš€šĢdJzU +îäoSl·Ž@kq[~°ĩÛų,đ‘ŽÍŪ”Ųúū•™`ö%Ä―/Į ‚3ðÔa'“™ķYīöÍčĸSäÁęÂFûý<—å#oÉĻŋ ð ĨŊāé€ÔĢW9jäZ]‚vxÞp:›••LpTššøĀĢ3Gĩ•ŲęØŠÂwĮĘ{ ‹dzŸ!…b œĪ4C:φ―“I Ō藛l,^›ãI'ō>tĀ@ulđĖė―Zë―·Þž5H˜$^ð­ËÆŋ°62·ž'ę<­šÂtYĸPāÉķ–;ƒŧģ>īBD‘ĸ}›ÕSdĪŊ!o}ĐÝíũˆÎÞYó†ZîßÛŌąļ=YũŋŽß—ĄÁģpëĶĄŦ` šĖÃą&XĄ æįūž>QˆĶFŽïþŽŪÕļqÐÖ 9âØė›zâî—u™cc{)‹ÞáóÎm*ᰓųBjXO·!ýŌĩ’ĖŠîHīŲÜ·Ï0Ž—ŊôĻĨœKi|[Zô-Ō_ļŲ++ï7œ$ ŋ3ĘðbˆÁ>%/e ˜˜Õîģđ>>m™’ĘŪīü•ƒœhKiszlĘU·Ķ9a3É5Ŧh °6`mņŊorāč%)zUŧ'!ūā=jx$2b0ÎÏā–ųK~ÓóĪí^švÎÚJ—a˜―T;l$*ŨÛÉ%”ÚîhÍ ã€ ŠNNd�°‡Ā3aŋ<â2‰V˜Ps„ÝuÚQ{ íūÅ6ÃŦÄo`/†ÖLæ?7ÅÍ8ęĪĄėû3#$ĢrÝÆhPž·'bë ƒĐĖpŊL@-…y<<zôŦ|a]ÖðUÔĘųö6›ŧ_‹aũņÓUÖ „5<6Ā^[ŲąĢTÖŌØĄcĪNÓīÕĄÄĒÆĐLKÃFr@ð\Nä ]ɐ; ΚIg*\ī°Ýˆž!,ßþũ—3Ũ‹ųšišÖvÃÖąsó)ššĮāVĻ.5Šrķ*ā•% 0teÄÖ;óEŅóŠ;#›āj—ÛĩGä8S8]ÐÅQƒR-ĘûIŠ1­ánwÛž&l(ŒdašüÉ'8ŽßPôŠF› Ü3Eūœ–ąĶąįîî?fþũF§”1åӏkFb,öw Õ(ÓÖ Ø1ð}ЌĸČÎÜâF=+‡]ŨeĻĪ<WÛžÅĀ“ĮąW”ÃĻ“r&*JŪ&―,HH, ŪØÅ!„ЀxĨ7”:ÖīieŦžNQ˜áF`üļËŨĮþÎ~ā|5Q�•ĩãij!ÚׁķöÄþ*Þęo$Œ;.­Ýų1áĩĪD‚Õōė o:ðø‡Øą|†B›MųÃK#ï3ÞŪíÏĖ8ÕóįfcĐ]—”ƒ0_ašUž^QâI‰FsŸ!ā )š2ŸX3ßŨwšwC/ŦŸ–ļŽÚģ―ŌĄüąWOOGZÃėĩáö\P}4ãv3ĨĀâ•uņ‰IžĮ "ģ™4K’›Ī2§O?+Q˜î™EAķė\ũ,ˆ~.ÆFޝ­Ŋãïę<Œāü^Ž1ŸŠĖ“ō^Ķā8ÂH°ōDú&æaŪ\6Jëjģ­"Á‰=Kþu•Į@͌pÉUNTøŦïØÂSÚŧ·|ũŊEmoŊĩg&~ķrY$x ąīÞę(ā@Åqa,Ð{M+MOüĶ>_X�ÖýģwčC*‰!ÛEkņțÁ-æÁø>‰)ĄÕ!Û2TÁSĸoyÄ ĖĐwãmû~€'īBĪ\‡Ũq^cš­{ąHjÝXä6OÜyųWŒk.Â.áOsAX{InÉ@cGÕM*väīœÍŧĀqĄqqIÍŠ`/œR:4Ķ-�ã]ę,ÓÁĖâáa`NÐyš+*D!Ú­‚øĻø) Áw-~t`Óaņē“v7ū`uT~ôPqäWÛeäÛöäIÃĩų+u`~Ž‚ņ&ŒæH0fA‘b^G·ķŌū―ÖvđÂxxLŒ€)ķë&ļđũŅ|gN1ĀpÝOģ-OhģąĮŨĢМ–ayč} /\ë!V?ã˜ČdĒ2F_$)ØŠÁaĪI—Āf‘•Q•ÕĘĖΊHĩ@øZ;ÓK}“É&dĐЏmŽ|B[þ·(ī óŽ―ë™gö‡ÉÓI€ĮßSĮáWAö>€Ju(8/*vq)‹3’\JJģ.ÎÚģkŋø§―V&<ģžÛë˜ysÐũÚî‹öœq†•|PO‰7ßC@Ï<ē"ĸĪj“røk™ðö§(bdČŧ@ W—éĐjüցjlũí‘a:—2p‡ÄkĻCÎ3Ũb;Ū“ÃCaÚÐjTĒāé”Ā'Xīĩ<äïéH*'ąJÅļ#šdįE<’]ŊĄšSÛx_9XöšĮ:·ŽŲÔü”‹ÓČWß0���úxčКÓrĸoÄ^þ…æīŽ \ų:‰%/tƒ*ų|?7}s’ŠŋEÖےI$’?^ĮŨ…*öøÅ=yŠZĄÛˆéþnúþÂ5ý8JąsGĩį` đ$’I#c–nķÝӘ,ew’ėč€qkÐĄ‡8ęƒŸ­C*4­ČÐeë x9-Q…†Ü’I$’ÚÁ”Đ—ąįæĩžļĨ֋eóW>G˜‡RT­āõ”š@{iKŠ^F Äģd�›F/Šʰ‘æh2�Ė{$ðvš$Ā<ąPkŽ‚€K‡/DŦē"Nė―šsJâė áĖ5ƒC2ŠØN~ĩƒF™ŨÔļÍ<ĶPŅ §‚Ķ€<)}§ĘĮáė-‡­Ž>ĸ&ļ•-QŊzÅēHÐäČ .ĩļÃíĶ'JP팘īŊJ<ė7ĢŌéȄ79+…Í{}Z<TG:>ČÛS‚sėW›$ŠþNĘð gpŲ”ÍōŒ#cļgÃč°ëqzžųĐdøló{…ŌīzŅýļEÞEhŐB •P°lNV™Ø0ƒdZqŠ Įļ#}ĻŦËŠ9‹éîДýƒB3ũe‡)šÚkmpÐÖøƒ3Û^8$ā UŅaoÓAæāļĮĀa3ΞåšâāýrėĪûÐ,k…>mß<úƒú`äŊ5Ōíģ ”jÏĄĢ{DGUŪå:Äû.{N—Tþ2 õđī8€Šˆ!, ōÚ*Ši·Ā FeĪÄKílÜ7Ī(”ŸÞÎ͜ūÜÜ}#·Á†ÚĄrįŲãó‹"Â―īéØ[‘8-–+ØGŲ‚r?ĘkđÉ.^Ŧœ( ķUgĒPáÓÕL8ë~a•+b+SíáóÞ0·Ķž,7g•d‚P§į―•œŲšœ%·Iþ4Ž-™Đ,°9ϔüô‚,ú’sÂV‚ûJÛ2―Dąķ9ũŌ͖“æ(įgÏ&Fu―Ų†'hŧņ)jvß§ŲƋģgĘŲKƒ‘(|cō%ņ|ݓú>ÛBF_@RĪÆ‚HÖ(éįPËÓOnW,­?"õ[ĒïĖ·’}Vęũķ Â.%“^t ›šX“53 ŧ”ŪÝ­^\Į“žõGķ1þŋÁül%u_›eęųMÎ)Þ8Īœđ˜°īĐXH‚ē{ĢŌËb Č}ĩPā!-F#ۘâüŽdč^N'Ąží�ëĪJĻtkĪ(óØ#ĮhqÅeæL@;ĐĮd=Â)°~Îôw§Pę.ĘÛIū―FõžZņäčŸChĢ:\ĮbÃ{ųûĶĸaĻ_Į’Åß!ĖÚIąĶüÁúæ’ÜŽõ·ū‡ î,G3‹Þ=HM~8oĄņ`Ôi:!GG,õÙäĩQģGU!ÜįĒæ›–m Zå†ú*Ē_*.õž Į:šz*ĢOJ$ū“6Įę=tČNjŲ>+”ĀÛïϐDPڐðÂĨ‚ð<†Z5�GÝØÝ%ɃÕļ$m1ŲĪ3riŠĒâi;8-@”:æũm‘–'—ĩð‹ãUa?úûÎïIĨöJP…!Ĩr|@lõït֏íҘ1đfÛŲi\@ úĄý:ģ9%æ4 –ęĀÝu$‡ í„š6Šuęéĩy-Vø$ĸ ðŠaĖv[")eI‡…íóųYtqĶ>ß+FiøĢ,ÃHƒ”…Š+Yų°aÃĮ?&LbTWŸôZ‚ð~ÓP§>îíž>•t؃-ĢmÏjUđ€#Ól}Į“þCï˜LÐ%i5–ĶĩL6ŪläkŊÅøö|s=Z(bQį…Elâø―œ ķÕáÕNølݖpþwĩNä/訓ë=ēEÎSXIš."ĩŲX“NŅßyŸĸsĻ•ûxĶč2ÔŲA‚qĸPžýĨXœ­õî3œQýõKh'HY“W§ pv’ ČūÞ\ĩ0[™‚ÜÝjŸ,‘õ0æsĩ ąC˜ÃÕ3>V…õ Ö霜œp&eš‡M!dH[Ū;p"Ï WöÃۅą4ōiþ"Vt$rwïŲy*AáQ§{pvöĶ%oGŌ›ŧg=%_ķ‚ĢapžåRĪdĸu>·Z…û]ŽĀ[ é*#lĩO4ąú=W ëí;sÕ|Rŋn�ü§Þõ}›˜œ#ļŪkēï“tôKýÝĻÚĨĖQûÜOōbԌ^Nh† cķ(‹CĒUgáÖ E”vMf―,VÄBÍ)ōäÁ…īĻLhŋ‰3YŽŠ ŸM(.Ֆ`avéĩ/Á܏ރĒÃģH„žęÐĸpÓúЌnŠjÍ™…ąŸÏĘÃōØãSGX~ņâõJÆ3Oüĸ5ܝhâļ‘Ģ•āŪæŸÞ1ĩš°cJa yxÕZ'ŠC/ÅÛŨóĖA™lļĩA“õgöĀ‘Vōēī7ņ%―ÐHÐiû_SĖĒ_ŸXųÂ:ŠŽ‘t°Äĸ#Ā\܀ÐŨŨ&z•īTŒËÞŲbŠíŌ{YŅŊéĢAZGāy ī2þmĸWN—kQŊäŲûæ-Ęøiˊ…pīõnŠĒĀ (þ›Âš“|ƘrՒfDoŸ[ÁŲ·ßTcÕÛÍ7>'aú ,õk“°PŧŲ0áŒH;\þg,ŋ5zæn|,WÄÎĐûŋÞfÂJåĶ@Aãõžß@4 Ėž|Ž\MuŠAŪwĢŧäWfĨVWl/ug§JÞåÃMyóņ^ƒ”Æ 7,ŧr‚þÁYÆ·5ĻgŸūƒ 7t'™‡įĮA\S.|š#Ŋų TJøķ˜î[ÓTūž 1-?óÜ˜ß Ÿ›g'ŋeĒýĨÏFđŽŽõˆÖ0‚CŋPŒHˆ™ÖQfuð -äĩ ų/Ž™ ūmÏĄČņ#�1äí―í`ŨÜ\ÅÛTeÜ]Ve ŊÁlā‡ïgõšāēņNs@r)Öv|#Ėcķ.Ã1á‹J”*FT˜ôĻD[Ģðų}õãûÓ9ēŽÂYÏÆT<xü˜ū_~…Š·‚"‚š@ŌŽÂf:ā…œÂáë]ü)BŅNoý?.wģ‰*> ĄxÁˆĮ'Ęm‰,/XöĪ%ӕ”øÔ%]ûÐð6)=ŋúÞäÅgz ï{īÔĘâÚėéâßYc™V•ɏíÞ„T@ƒ3aĨwwī|2Ï] ĘĀėãŽēúýîīoEÄōG'0>â Į þs}NHUúŸV‚Î_zŲ�QðŌī^Ïą spp bŒ‘(7óaÃ87 @æÃt~|Æv`ČÕEsxį̜ÜNQ{Øž„č™ĸ€ČëŅ\(íķÓ8ØîŒ ŅĮĮi֚Ö;ŧāNīBz­ÔXHĪęZœŊ ŋa Ý‡ŽzLVíįÅQ)†ģ�PôŨÝXaä(hc% 2…ÁëęÖÚ‡ 4‰—3Wđ.Z(>ÞC,úE)C}Î@ýk!ĪrvŦßhqnnX‚”<03#ąČ—BYšü1š–íúŒÞŽų;Ō2’Ýf―8ęŪŦsïLúØs 7+ž° iûŨĘCM’UŲrĄpÓVŦŠÃ9M΅ōøgóœuXĨiĨÉŌDšŊÝĀĄ2{]Žâ\žDŪÂ{ĢŦÄM1EQbdĻąīŽãJFŪ4gĮĀEŦï4’Ä”K\ą,zÕ-ņ[$v ‚·}S‹*•šeÛU:@TŽÄđ‹{čįšĮ## KôŦAÎ`”i\raå–ŋV†Č:óyZkũ˜1ßPÆ_ũS–BŒ―>FW—æ 7ËJcQ%ė75įTŪîJÚØđYĨã‚& ŽYqŨ6„ÃVFßýÜz(ófσ!ųКŦd­ēŲûðÓDhÎdŸÚÐgSŲãŋ”œ"pŊ"åmaŽŊ!ËT$‡"‚rķíBk…ÂóÄø](ē{Šö}Ąt*"”ąīlp>ę3"ŊļĻT…L`ŋu-$R6øŧė\Ó-nxP^OČq5ïv‚Þāīy $B°$ų hø‘(˜DÔnûIT€‚&ę#‰…ęÓœM^žÞz+f‰aßļAwt3ŠÆ$Üõļĸ,cP—īR§Öõð:Ÿ(iĮ’‡zCÉ#ŨlĘDÖ[SC‰ß /^fžjÛZĪÃwĘ{áMJdsÎOëJÅ6ūŸÓ.tÚūîæ"kCoģKIÞ`ĖzŪÐpڐ[Üķ„qðwëR–ī‘ĄŨįƒJĄïöã„ō�†šýmĖ;GgL/ģ-Č(J.xĘĀvĶūXlNĸ-ųl&beŸTLŦ.īĶŋĐųÚ1˜@ėâ—r+Œ~=ÝôŌ︘đðYÂŪũGĨM čũð4ß~ošĮ]ý›ŽE:mŦĩĸ;õ3 “DčŒģkāNšDÄ.Í,ž§=í6ėŊēþY ĀcpxĖû^=ŒčŪÖõĶāRĪĀĻ›ģ>Åk/Ą˜'hęy!KŦ°.ļ 'ôÔ vëũe“öÜŸ—r1,g)Þ)ðr‹lÏå'ļŒ'GSŊāĀĶDÂKބoG?힟ĢēQĐSJjĨÅ;Ob8XâöˆĶ―ôæ0ÉøŠĮˆ·á€žēO‘øiÛlĩsŧjĨÍøĀA}Ë(  €xÄā4A*HÕ ĻėqøōN2Äč™<ð“üä+ëķ}ėäųsó%ĖŒãÚęÜ=ŋíŦ˜^9~ą<E5Į Ý@éo82ĻĒ-Ę`Pļۆü\2IōĖŪüt‚ęš=á#ýšČáŧ*:―ģZ[öþú ĒĨt‡NÉZ�"ʙķOÎq1UĒá^‰ õŧfę_Ór&"%ē~ū•Ī)‹Ä ‰ŋÃðþģR6~?M ïŅc0mïzĖĖÓĄ‚|NhGؐ(?D#ĨYg‡hŪ·P ŠZLüzŌĀÁ‹qÝðŲŲ ä|‚ĢóÎö™Ū^ qŠ2 ĢĒé GōšŌĖÖG ĩB9ėŋаm@O†DĩŪBļ2Ī‚)síÏŊ?'lóž$ðÖ"čŊî<\‰+Ļ,ČĄeØ'†š­ąÕEĄķī”Ų ūír3ĩó˜9ÉG éGnZŊ€5™>ËĮs‡ãÎ/5„?ūŽ bï)áĨÔ˞āƒŧÆáąĸ,UēöÖ§ųĐö‘(nxčá4ž`wt~ĒôîËÁQL64Ū§ĩœēĘûy'NĐX'°gō=Š•ÓÞĢÝėÔi”vģĻr‹™o8ÄÅÎ=Ôŧâ]ÆÜMPīó›ŒHOvėđ@_đ>�JX]§ áŲVfĻ�–cŧðsDĀLðyÏ|úĨ„–TÍo5ðn8<Xö&:ŊÕS>—‚Ã_äĀUaŧËįgņœw:Þ―œƒ Ņ0؀،—],-ŋÞ>€§ŦmŌ,PiýR0ŪFÁЇå͕z―fŨkuô ææĖ•ę§Ąō‹VäŧŨ{úĘWޚ?-hšÉŽŪfžõŨ—ģ˜ęwÔρ2$oQ•#ϔú<KmSČĖ/E… ĖÐL§â°ĢpD9]N…ïYρU3Pø@.æeŧCé8ž`ŋáNáUO6‰ā•ûž_Ÿ?Ķ2iæŋ[0ésYHnĪõŠrĄúŸJüWŠ”ˆ~Ð: ™O Ęã[|SŌH;RF Sâå%͌aۋų,VĢĩũzáIŒ&ÝRŅÃ%Z‡†Ų•Ž&ĐhîšŅ†/Âzž“ØðŅ*†Ŧ]ečœAČeĸQpŽŠrĪ#ĸx[ÎLý•…Ë%ln ·ÉĄž?ÎĒö^=K­ŧOđ3ā|Aī—&cĄÉþ?2 &Ÿ"cPœ›f?ÅzīŦĸ�ý'Y’I@ÓĪŧ{ĀkĢ>ģ•œĻ’š€zĖVrcėPģP‚Ŋ…‹ßĖ:{ÚĜĘŲ1Ņ,‚âðzW] )ËsžKw†ųNĨŋ6ȌŨĢ*^NÓ`bėdݘ›DÄėÎĀÆ‹ŌŅhåüŠÔĒÆéÎÖg–? "š•ÎJ`­cý ­ COäXėôģ™ÃéŅąöUøÅ�ø:îãúîĒYšÃSžFūô†bI`(BōcmšY5zŧøÔdKĖlܒžWÏ‚õNZ›Rō(›8HØØÖýõvw`Ú―GŽĸ)6Ų)BEÂÃ�ĨĘō#ïŊ·iÖã7õz{ëûŽdl%ŋKĒýđx;z(Úzî1lô,ĸmž‘ęHš„M0ø=íĖpęä$…‹Bė1Ķ―ŸĐŸĨƒ­ÃČõėĪEũĢ6Yy Ā/)‘ŧûm…Čéx"ĸ#_įދĒ`GŪ,ĮÁ˜―H{u”;“€Iîņhí†x§đąÂŊ�Üæ:5•ĐŲ·~âīmÝįåð>ĀyĻK .íOiUĪīýPŊvŽÜnDm<D:Óޙe |íáĢØ€qo…+Ŋý ũĩÐn{æ:čÛŅŪø=ĢæBķ,Ēĩâ€ĮßĶŅũé,>ĘP‚ąĻéoũ™ÞBÎ݃BįPßnHAK1:eŽŲ6xE iŪķĐ֖[[@ l?ģĸƒ 3Ðũ 5Ւ­`˜æ:đRä'ÔQįŒP~ɕ›T‘ĮiŠŽs#ZAôU ãý †Mū9ÄM#ø‡TâΧwļEđ;oúÐ*<=Úõ˜Ē6QEžXnl_Í2cýï…ƒ“å Ÿ>$īƒéc]ž�ģđšð^Į)ŋ 1ĸov,>ZÍĐ|ĩĒĨėóh7æŋ:įšáևԅljü‘Ģ@&™/YČ.ž,þß"ŧĖųž ‡Ą‚ĩ|§'K>åï+.wfsI�ú?ŌVđi9ocš˜Ĩ`ėgėCš―ģúģOÉdā‡đčą_œ\ËdŌØ‘–~AÝæšÐ5‰―Q+IÃy œŒUįaå /œ‘LũÁzš $J"ŪÎ~đĄ˜Ēv{ i5 é}ío)r?ŌÃpfōčÅ6T0Ô[īÚ|pkJ˜üÍņtï-ģ5)—č4OâĐP|€—YAĘGNÝņ ž ô3đԇ ïÏM >rŦĐ&Ĩ2{þ óðéÎĀęm—ŠÐ1mģûNPcÏŌЏĸLļÕ�Vi^ =[v?Ēðóˆ°+_… ûˆ?ļpH˜Fe[›†Ī7ÕbķHŪēÓ|ŽšŌĘ~Z\Ĩ°Pyžâ(âĘG-”Ÿå‹hŒÞŸéĮ`zÄąWĐcZÂŦé›BI_Õ Âĸtē'nŊãe"ėķ;8ÐđÝŊDKôt/0 ĻŸÐÅڗ<2Ø/pßB&oÝđˆõBžĘæFeĖR…ąÃAø…ŸˆÚV4Ō6ūφœŧD^’ÁuŪâ ŌLëÝMėÛÜíĩ;ûéĘ 4ðm-§fįTx„‹XĶ%õšÎޔ=ó€ĐC|2âd4dąŲČânēŋ-ÔSgþxę1ÂÓ…Ü'Ë5ĨiĖ’Ęõ!‹ąZ‘eÄŽÂLÄãũö;}}~þâ Ö_sííųú ?D ?VĪþŦįõiëØpČ ĀŧĩýĪØŽIƒw[#g^?‰ï ņî Ŧđ Ī-‡eÅI%&t H“$RKR_!gĖĒ’9Þᨭb]X?„Vë^í<žÃ+6Ú^eŧMŨôkđũ–ÓĐBQđԖ‹Ä BZōļ ^péjIãËôbjŠŊ'tbúÉø(‰ã*Ũŧ·āR.äŧ#čBœŦ^Ųn›aí…ö†)‹ ― Ð3ĄDįŊÚAƒáÓÐEīHŌã3Ŧ}‹đÂcíøáöÝCƒį^ n;”VBBå‚BÁ ”ybFēīÆ.öāāįûķķ?øÎû�ĮÜļ_,˜uÍĀi,ĒĻ­GĩÔŨĀDą\Ÿ`p#�y;hÅ/aȚ˜$ž}r#ĄÐRösąúl”4ÍMbIzĄfÅ{ĩ‡ûŊîÍĨMîI8XÂgbO'Ũ’Í+hŪÍït*Ãâ+Yf?á<WÎV šĨ�ksÜ ŨÍhār•œÕ wSĸ6Įy‘ÜũM%ĐzÞ‡äüīWĐJŒßöU2^<jœŨJm­1Ðũ’Ĩ­ëï 5č‹âIĮIß]”ýÔïüEä_‚gĮîáI‰h|ëîÐbaėũúķ";ģĶÐ Ų;|É>.H’žÆhwŽðī›ū%”&#“ŸgvkĻ7Í äŲ‘—Ū\íÛÔIžhÎįqĶ™HõóŽH7j ûŽ–Åm“ĢŽ`MĨK*ÅAčÏf[)+RؘõmÍKhģo,2FėU˜AĀJ5ŋãŀÅLÎ―‡Ģ>ČĘ~U<ōšŧ9QĒeŪž7ûUA}gaBôąĪ+đmlíeӑq9óĒĩĩ ĖwŽgžEBŲĒÄ"ģ`ī82>%ąTū Â'š$ŒŽ™Ø:YˑËŨšÓĮ-ŽŲ-R2:―bŌųubLŌR,-MJFmĀA?'Æđ=;ģ€gŠååąĪq0Ú Ŧû ðŨ—‘œS‰ŸOņ=ZƒŠŊ1ā>/rC™1‡LÛڍ巭–­\y•G*Š"ŪÝZ3õĒzj=Éf9t`d }‰ø֋}ėp’Ž­ ĨŊK2zǁ Ž„ĸXõ‘tž0ЙĘwfÔ =ā‡DķŊ0ŊðĒCvčiēÕE–@ÂÝđiÎ`û…úĢķ Ģę5ŽÂ< GūĀqë‹āuxž)‹ ŠTEXâKfþĐvÄøyŋ0û―Ŧa‰V4ną…b\?nŽ^Û=hiœĮÜpï )pĀÐŊTа7(û<bÆ>N°qrU*rĸŠcÛÉĘy ?ēÜ.ßÍ —Ģ‚%úŪšX4RZIŨč!đû· Ü>fŚ“ósGZáĻ{bHHkEŽÓG36|8!ƒóÐ _XiĶË@ėó†đ6LˆāŲØ:a2ØðýJ fäôMl€Ѕ“đÝԂŧ?=+ŊoÔSx dŅënlRäŅĘQÓĖúø‰Ũæ”3ŨDĨÐ Įý;'TųãÎdr.b%ãsė†Pæ00;§ĸKHô§Œ pīDZeß/’a3~"ē›v8ŽãÃ_û‹V…Ïr xĘ+vŲĩžNî\į,öó\9ĪĻâĶADdߨï·zÃĢč?“ĸ(čKÅÅ&Ģne‹õ$ųšf5$ëāOŽ —‚.’”đ傄šēÞ\8ąýŒX Ä�xŨ#ũ§ PnBd1Ņņ=‡L’§uÏ ôÆĩõOäg5íRg;蒄x@]þHĸ>:?ŅŅýčMáāT 0ö<ģýËŪ‰*júž,"Ëó^bĻņĄō“/GjfÁčŽy\šŒĒŨCŽŲAŅYˆw/4CĮ5ÞĮŦ=ũ<’0KzÕįäđ[ĸKXæ%ūg°ˍÉ·ÄzųaŠĮ>cČLó+Ÿpk”Ģ=ÓQ+БÉáôö3fß/°3F/Îá0˟z6éĘ]ßVŧö–öX‰b ój^Ôi'”žĀPÏÁ“ëĩ§Å įÜ­øīŊá =ũzŊ@ˆïėNĮâōČā§l0ž’…œ�*2r{L–Ü ÖôÎŅ‹ ›w3ü=GŦčĘÏå Zå3zÍ͚ *!;…“ €ģ ßšó&Đü֓ÃēžzTņz·š iüäūYČķ‚]/݃viGÏB9Ũ9~CĪ\[sZJË1?R―\˜üÕý߉4lōãÜÂāœÚļ#æ‡@ũÕ|w†ķAËBŽØ‹�ĩðčC6aU‚ˆ(ŀDV8mz8eþã1kQFÄtŒ{8BwÚj• 'âāMĀœaÆŨ]ņB,ņčC‹VOßtŸŌdŠCčbđhâį? “„0,œIAPēÛ†4Å[ï뭆•T•Ā6žņÚįŌÃcýČŦÁŌ―nŠË‘ŲĢå0ÚúâØáģðÂģn@7™Ĩûóâũ3XčūÕdõ…ãÄ˃„mžÛœ…Ķ6~ėģĻĩÅķlÏOÆïĩ―‘KiÂĨ3wzUõÔĻm(ä’åúÛIļ‘đ+ųĩAĸ5–@]đâ§lóĢ?fëĢđā(Ôŋ]ĢÖØ(ķåSŋ<‚8hýJVī°īUJį}3�;'ÎkÄYeĒ@öķfĄ†LĖËØŠbUâ‚óISÍmßŦýÁvä“ĢŠ;ššĸ.Č(Ū­û€ úX�R{†OdtXãó]v>á݅ÝÔVØĩļĶīzžŠÜü  Bœ/ )5`^ȑ$?ĩĘO(”Đ.âj=eöݘðåBŦ:)'ôĄĩr•P}ÂŊŠ--ŧŸ2”Āö“îeĢš ÅZ2"ČäĪ~Ļ8Sm–rþðĶÁ"âSCQšˆ―…i:–”åg™…đƒgIRŌë0šTYōÁói Ž„ĪęÁbžrĄ]Út;;LÃ<ØÄÐyÉÏŅí*„ûlž›kļxÂ[ްy”#}„öOŋđ �Ú‚•F?EŸ"ŲĐZ‘4YLĨЇc SēZĸ?―Ŧa:˜d92Xč.M7w` Ä †ķ`…ČZ62Œ’îÓsŌŌ뚂–0gyy2Äøä đĻ6ïŨ€3ëïč&Ë·!E MWđSĖ sšNûrĘĨ§•nåÞïü†ō OAČKņŠÎtI:l'ôÎþ(JŧũŽŠ„9Ú:X·ÍÏŋč™ ˆ]f!Qd$Z—‰ŠmT―õģčķuĨOÓ@ÔŊˆi0Ōæs3ÞüU'|2^méÝÉÖú%čgHŲÃÐxŋv"óI™ĩš9%Ē.Jüxž’4GÅũQüÔg.káB>þH!–qÝCuūӊ�ÆÖYįIہ?âŽŋ’`ū&ÍøÜ…ÎX KC”ÄëW/ÖMk€ĘW?Ųu}�:Vœ}Z{P”ėõQėŧܚ}ĪdYJE^ÆðEu‰īšPõ d:ũ sjĒ­? æĮÂwō!—j—S‘éÉ;·qŧ‰ķÆĶRi{ūÏĪęyĄÅó&·šõōW a vMl2 ĢĸĄkJý›ŌJ(ÅÃ> ÕššÞ~Áš {ĮõĖ_>Ļ RļÄw›+U–ž1ęIĀiE•5 {ķį6Ÿ 1É`!§‡FÝÚU Ų.ĢYÉĮĖüŸ<'ŪŦ―ŋš:ŅU>7ü™š=>6€é­ž@U]Swų ÎGÖ7öŌ“[ĸ[ĄþāØSŽ&oÎÛõ‹-ŠĶfe ʆŪ|ĮVŪä”ŨPSAüōÇZ—þ™ŧÁū-X$ Ž �­CØa•Ԝ"{þ8 úËŨYĶökŪ­å ͆Éˉƒæýí4HĐÕgP.6―ßKó]‡·v%čknÁ‚Æó ģrËÛ{,Ōc m‰ēã?īŠų.“‚@üšón1á‡;Ÿq ó7ŅedÜæPúÕŲĪėIFųĩ-ĐS—LYnÆ9ˆIZĪhŽ7~üĩ =ý9ĒĨ˜å4q ģģ4*=ƒ*uC-š}^šģcĮ*ļiĨē}†ŲBŒ ŠĘĻļŒ1ĻyâŽÅļ„k܇þęËņY-îŊĪōgĸfnY/ YÜUó}ïˆÝx€%ũ ÜØą6čđø#Ygó›vS-73ēŨSÝpģŨ_ģĻݓ->Ä/–!Ī Wƒģ'ë"|K]ŨįŠ+€Q{ŲÓŧƒ—ýöáIÅm ·á5 ž?%ėbūjaäj_ŒhЭ)–~Á"õdõjØ-Ú{КæÂŠ.€”š $P6ðēÕ^ö„_:yŒëūĖS=wyĻŊĮĢšM§‹v󒚄 éWTØ;ßFéSaÁÕđ7MÂX%Č‚æŧĖFciœĖʘĒeŪ‘ipŲ&ÔáۈŠ.@ķõû—ė`™|KqČŋņ”ãW\V=ķÚI]â ÆIYŨüŒ]{<bĒ(Ä%”îĒH·KL݃7ӆĢíhĮ0‘šóĩԂö,īąs― ~vcŠaÏĪVHU’ėÜŦüļRg,ÃbĢ&Á|óļw’þ î›Ï�ī“™ö_픡njØöĶkQįTlķēIÃ<’b@ē.jö;‚čƒ˜?ÉÓzļ›c„'Ęá9+úJˆēÄõĸq ADøkŲeÝĸ)IįTFðŽĐiãŨ…ĸ>Ÿ2ÏlĄÜqŽŽŽ ĻÜyâĒ“áþfę”Wks’^ŒĘŌŋüŨðĮm‹ ųļßéK€\�_―s8IK)ÍĖį{&*ų'�Až@ÜAŠëQŧī9NƒtUÃôEF$Y ūā\‚Í€FŌĮAäÔúaũÜų(hŽ8c;“—O†ĩ3 aåđuË|ČCÄF}ŪŽ)ę=Sm�ÁâN!ã·—™ÖŸ4­ŽüO”Z á§&4CR1D3u‚c}åEcĻŋâ>Ŋ2õ~ķ�œ;Lę[0fÞįí!oÐY\gĪkšešVûĻ—ŅĒwn°čŠfūę’äžĩ’ķčE[čŦf0 "§ķ ązĮŌÛŋ4,™\ČkžÁÚ@3$ÂAĩÀ[—‘Ð)Þ/HķV\Vô@Ęã=CÉ&ÜRĄĖëD1ó8ˆ›ÍŸßíŦcŽH+Qh…° AŅû^+ØļŽŦÉó€<KĢ 9IîvWũ+SŽ~ÐÆójųš+ZA€ŨÄ<uÆbōݟ{üæ þÛķĀÕmŒē§ŪĄ˜‘ý{}‘ÏŪŧ.„ˆPûēv’_€…2$=1ō󹇡ÐIú­ÏPčĮūĢT‚j%eęšā�ˆŸ@ VÆ^ąR ōé"y ŨVZTā$ą5aÉ2fdą°"°ßį 7þXđ-ūS#ķrūFiģę$qm*ØĸĨp,Šé§ÅĀYŠ™þMz 'ÚØĒ>üSB }”ô!Õ ę(zl‡p%ŒOė:JĪXŌÐņu˜gŦiũˆāš Ýf-t6΍ÛXô‹!‘FīŒ‰|Ï+Ā}Šþíž+cxGî+!Ā+ũ―ƚûBv(ÜÓ6·_ÃĢ™X‡G' MĻÎmNũųá&ÍBđ0úw=ÏŲ!d‹ÏĶŋ3v’Đ<žũœuūƒdũÚHu˜‰?ĸoœÝú“�8Yý$Ĩib˜ŊŽXĒlā,đ2)ÆŊR°þÉ-åîS­TëŋŽ ã=sŧũėęY›Ū›T?į ˛mJŽ ČŦ6QJŒ#E3†]f eþÏÝđ]2ž'ë„ÓbÃá!k,iÂ}Fëe"Ŧ-ž?PÎîÂlö\§wÃ!PÝI3TėU ÓâŨč'PmäΆö`|:�˜]§ĘÉ= ųbPģāV•č؛ Ĩbý@įBņīvŪC™ë;’Ņy`ŨéĮüݐōۃÍ$Ķ–Ģ‘ĖÁÜSvôW‚(EÄ4ýJŋ2°ŪÍgœĐ| =šlÜcTînņÓúȌ~ÝĻÎûtĨ„žd™ -û2ąsōNŲÖ`‡ WPÏüîē� ę+ ‘ŠwÃG™ LšŠwîį6žãhŪÖ:$ĨDō"jĪ,Ę__kžr}~+ē%ÎuŸõČŌōąA`ŸÞ•h)`%$vÂ.ÝgýF*fāų"ī Oéâ1!FÜÄkÅ1ĶwGg4ûv'ïn3ÕîËČAüŒS§ƒ? L‹ @ÃF„ĸ[ö'ÔiQ‘ąQ:Ų—þđ Pč?ĸĸ{ÅĄ+xåųóō/,Y7s(Œ q‰ú E.;ÔÛ[3Ŧj æcāŒŊ ã6]ŦVŌ0“ļŽ/ŌĶŽ ŠnäĖĒĶ[Ąīc0?―6đƒ–bjąŋ™ĮôHÂē”đ™ģÉßͰ-ŊKžÐį,W āÅPkŨMÂ7ĶÐû™&=ĮāœW<EÎZû)/P5<hÕɀ€ī<m6ë< Į üĻ9}DŪžM~ÆÁđ‚Gë?ŨY|“{Œđãã–ģÍáDvᜠóˆÏŲÉ+é°Þ—ō,A%pOЃIÃx(ÜÞ<OY{}iNÓ*ĸXWüÛęŽK9ŲĐ ķī†5ž!>8:6TÐëJ=čŽ'åĩ”’ðŲįG*ņų â•ëÃÏŊ3ŽžßqQÚ*Žr?V€dƒM譅�ũ…QĢÉE=|eÍ`'ϑwŋÖ=ŌPō|D @4ąåUÍœæ3C1Ü}><š*įIų‹Ą‡ĩėĊčôFŪgũ75)(ōœ 5‚)ÛHa9 5Ã)ô=ė_e3ûuÎ9­:.롎f‹rq67ˆŦ<<mBð&ŠRļÔdnĖaÎēDU§ų|yģBi­čӊ9J9"ĸ]z§gí°Ïаĸ^ÕŨŋƔ―5v9Ē _ð"zzcŸ9$ÏąŠ ųs ģwø+čņō2ģóŪ>ž\O†ņ 1ûņēúžÉ+`ā_Ū°3fž #}\—БÔÝęĩpīyĻšu(=_/ũØqåúý mÚ§Âø^ >lŪ‚'<_cVŌČÁBJ‘-~ÃY†dÝ[ķs·,Ä֝WVč°yÛÍōvÁk“5ÓéÄ;úkĶ=oúôÅäęHÝ<ĸoö­8Ÿ"'‚Dc`,6ÏýĪTÏäZ1ūĪ :ÛUx‰fŪžQ˜ ÛýĪqwEūŪŪnļgQ9ðbl!’ <š„aFc(5<]k„ęoå)—žĐjˆÍn;.ɔî-―ûNƚLķO̘ž]íßb›ē؛^XŊP§+Nó-^õBÓDYÎĀgYŋŧÚýŪýÐ4PÕUÆžyė^_áyÚG6.·Nå„cŸÖū(ņd(?Ø&˂č)īR0fëđ=Šx*€Ļ‹ckéžÏįŒūãqžŽ…XMīM™›^?é“Ģ4σLßŊŠîÞ)åŦ1~€Ķsk_ë ĢbĮĸ\°ĩ(Wðˆß „îÁėM)EE[)yö„Ņk |")·ËZ•|ô? īųCÔ"/G'’€%―ބZNĮļ7â#zöîL$"  ;ð~ŌâCŠþ  úV'ˆWV9åĢ·ÓŧŨōÎÄυ2ųPĀnč~W49;%QĸU&DVˆįy&ŧ•w8’AnxjÅÛí­loËþŽ&71HĶ%€Ąųpâ;Zģ,SÜ–āt‡ŪœžäJôʇō†ŽÞ qœŦžĒ+ý{“–kÓО xÄðÞĖuƒ7H u6,‰ŠČY…ƒâŌۏ˜YéĪdŠ+ŨĄ2ŲsymA^”čåŅx'>gŦ_,SŸúæUģĘ\ĘāãˆõšØK‡ž‚>ō’@Ē \šqr*dēŊĀ|YlNaĩ+ŽŪß+‰Ĩúîį„āÓ0čš,Ŧ =§š‰ģ6<zWqƒ·īĘföEķ-ҊĨpÞæąbՃYãâŨh{ėĪtD#ý8ßy*„TÝ;8‚CĮĨïÐ@né—JŠMjJįjßĪÞäēWĩý&-ŋ–ė–(Ž< !úVÖZr쉷†ÞBŠzhÞgwVąĸ}ļdō&[^ČÄ1J3ƒŪÉøĒŽĨ<øü!øbS0nôû‚ąüڂ―ã”CDЖŠōþŽįé>k:}í…ý™")=”NƒP"yiZ=CîUU>Čič}ËtXÞ8•“Tŧæa0ŧā?F†éÎeĻÍÆÄÚÄ{Ūr:ģÞpXĒ8ļíweÐŠ1öœ‚ødŠŊ%ėoÛÚÆŧš•…gŠLŊ›žĪóÃLŠ0ũîYð`oËáå3EЉ}’·âØ$7Æå™dđÁ–IR3=­ĩ*čkuâ*žÕao.3?ņ8ŦW~Ž6íĐ0°|„ĐM͜đZj”ŧōïĖ S]Z) øŒbĶP2ˆ‹Š‹ÆvqģŠš3Ôą0uĮūÆJp- Ö§� ä°Ŋ“Kôíß@(|e‚ķdŸŲ‡īÛïðéÔZhžž"ŪuÛÜųĒÛĀ­(Dq�>?sæģþ7|mtO&FžąžŠeÆČĒïÆwüxFD!jóDPkÍ|ę`[yvS–īî§§þc‡ƒē%DOBuũÂfH{úâFWŦĨŋÂŨ k>)"˜†Ū HfŠƒÞ•nĐ4ЉmVóž^ÞÔúÞē4 ŽÚ}Rö˃Úņ8KIöO%0Ž_Îïî$Wü ĖéP˜}åtÜŋ^m*óš2Ĩv7prF·—b­é›Ô€�6ĒeQ5ĐN)0:—šĶZ!I5Ą2œ+“%UWDČÁeåĖSĀ~Z<j ŦĪæõØN/’Ißo|ÃýĩNä‹ žĸ.BÜÎ6õĻz‘ F*Ķ0ĸÛR°/ÛsËšĀčïCdGãû‡/l6õSQ˜+^œãičXĨŨAx{Ü,@Ņ:ląō"4ĩ˚ 7Đ-,6tÛę7!eÎÚ�†&„!)ÍÁĘđQ"ūÍ,R°`a‘D[š\ó°ŸøŪÃ"F Ë)Čx‚Y€ē!Ō†Æ6ÅŋÄŪ3-•RÐn><l>@/ęä)”H89ęïüŸę߯ĸty0īü’ŠPØSûɋ'ÝÁ\Šnbö\Þ€(šž J3ˆÕ�Z CŒ:ŋöG�+ÞäĐdL(Ģ8ē͇J|Ū"WÉ1úërœWŊĄ‡ŠÝC4І Óõî_‡ņĶ™ĻfŧĖöt=…Ä6―ÛÆ ģL"čaQHp­‹šËŠŽ(>`ļ’ƒš9*GÍúVĄŲĘp…Õ>\žYÝ Ãũl8Īō`j‹–/Å0f`kŠžTþšĘGÐŨ]täīm1Ũļå2DŠ"K/Q'Š…Ã]ĄuôݘÐaËg ĒԗLčï9úķ“y?Q1ÉäÛđÍĮ‡“‰k‹Ļ-Ōe\Ėã€óļveÞÎ;æ þPūÅڝ;˜ĩu…eî8nt�§I($ҁ§6v“ģ^üøÆķÄ<3åMā(p(w]/`ÝF tŨÃŅÜ]# T­Ú-\x�ēķûæ\Ôϕīq=ûNû€øŊQí!ŧĘį ôfųÉRgæeëeZRĀjĻĀw,ÎyëîI86} āGnĒEÅn^Vïw“vôŲ :p@ģ1–p…?Þēą2ÓS2õ˜8@ô\JÉ[M#įoðũrFĒÏ|˜Ø―‚íC(†™.)áß%ĻŠ0Ė@ņ9Š�Šīʁ%Ė ŅÂŅaßĪYī”ïŸBž@Ö~KÉ,øCĐĀR24íĘ 0ÓĖÐ;4āĸ·Šķ1$F†5$Ã[žd„{kõöXNXÄĒî|^öf7ĢŪ{ïŠïÜĘ·#}kВ߉BÚŅõæ>ŪvņOIĨ(åūŧųÕö nJÉ(F|z—r+.ŧt€”ŒÔOâp>øŪģCÅ:[F~ ŠÕuũ*ÎGãÕhuŦ°āŲDĨ%ÂãZ!TÁëây+!œ9‚ŊŠ ėø*X­Ąŋ.>ü01 á=ÁßփK ŒMG˜Tt†ÐöčŦŽýYqþŠ0ĸ+ž XĩëŠ(VĶKë›j’/Ę­Ą îę"bČp5Ūvį%œ~Y‡uę=ĖJ^bE9ĐÛ2 hô4Ɏ‹œōjÓ/›Ë‰!ŠĒsÝÍũīôä ĄĘä3rs~ĢYũ0É―ã§llmmPaŠlÝÂáy5ŧŸ0Ķ!D•ęŒôÖ Sfuoô<‘íM9ČÐDá*iRšīĻåČÉÆö\Þ,^mđ5õ)šãŪWØýļ =5’ŅŒ7Ûæ ĄŽ$ܛG”JWȋvNÁ™tÍ5·ņōĶČI\æÚ:_údx{âÞÅ@ý|~2]J Čë́D1 $wŠĢý+ÚÆï2D:Έo™ ßæ! a;}A õžĸ$ÔÎMA5ĪX˜5ŽĨ{sĘŽ%ĩĨmāN\>ĪČ<?ãĢhā§v ŧ)œĘŒ:€`îĨôäPšļÃÝ1ĪēP)ïđjÔļŽD•-”=ؙōE›5ĖØãáüũānJ>wOWLc}ŋĖĒq…đJšë”A? Āc…öĐLÁæ–ō―QĻ*pý@õÝĮ-“žÂŒg:ÝĀõÝ>ĐQ8ĪCÕKT rúļGlƒ “E@.óęÄĢÝČ&…jŧÜĸƒÞ'u­ÉCYݜÝBPAõÕ ’Me’d€K߯Æũä9AYÏ{æfw3ÂGJMōĄ™ā3ą‘ãĸ €°ÃyPƒ˜3?åIBCcōüK3ãŌËĖÐkŲG-Æ{ję7•T0äq}Þũå‰;cšŨ3ļČ·Ģ” ÖÉĘĒÕķÛBëå]-šnã_E{ÐĢúŊpW§z}Ž;ŋBÍ2IsûœŠü]%6€ÁūŽqgæĘéÁąūïœ`ŋËÜvÞyw’5 }…âåĸ4ÜÝo åĐÖ% 4"#AÆÂą\ŽČ‚øŒdĪŧuļ€Náä§ĩU‘ø·…ĮÔË―øčņŽõŲŊ2ģ9zÝŒ™ū6Ä#ršBØs؉ÃíčÎõIßŧa ĒDĘڐå43ģDÖG‚,-YĄå#74ĀôŽÏrHĩ+ý‹ĩä?ŦÓ.c&Lú\4ä á[ƒrrÚĨh–<Á†GdÃïó‡J]ęEĮ„S+―ģ$‘ZUJށįÔÁ9WæŠ+ģ<(☌ė2§°?pOÖĻ\ĸšÄŧ"aLÁčR…wu4;­Z‡ˆÅCJ 3dýóđ‘F―ĢUĀÛÖ8Į™ú‹n/ë&%ÖF@pˆ�­—Å―ę’!5Ēč/'vïŒxãų`›Đ[“ ԎCǰ=yh3=OĘĩg.•CĘ8Ēĸ$ĮųŨĄÁ―{åoAvv!°ã_ؕFÚTýw|fxWDCFŋ7†Ž6§æ^iz‹zkw =•­ēx†˜Š‹-ŧOčYŦŦŸVlŽ%ô7āÄģFîķÜ.ī4ŪÍĀȅoį]ųŠD? óûÄ~°ĨfĸsõôöNB9ûÕÔx' b ˆęý2'Ė+ĐÛ5UՔ‡S…vÎö―ēþO`ÂÞ\:öNđÁB;%üý>W[íÆĐĩI)WAöOY§3ĄŧoDäâtq€ãö9þĮoØõûą}4/؋}L3į­a|ęQgę,ūu íL°h;}\îSüՕŨÚAōŊÅ gčâeÃĢB ^›q$ʐwކÖĀŦąAã˜6qĪQGG$ŲĶ|‰CƅšÎãt[Ņí&û‘Á-ÅØųŠlï8ũũŸōMoÏīOÝâŧYîŊķũžî’ú*“ß‹ë&á‰Mu , Ís�ý“%ó&xFr­ßY?ÎÖO��`ÞreACåĒxi\ÝŪđh|=V7›‚øņCbž5ÝĒö2`ØlÁîWo="Ōę,ŲC :yQƒ jm^BūT1‰…# Āí=ŊZ"U"ÜÖ:Åæ SVdÃ)$ŧĸN3vVžöåiXߘŦge—ŊĒčÃ6‹ƒ&dïĶĖŲįہÛÝ D†ÄĶ˜ðĘė6bįē‘@2ƒŸåėˆČpJ·{••Ō?3�ģ,2ōÛnƒ™ý‡Eøąuz°`Ī=_bē4}~þ|1ôđÜJĒ=ã}wîsüĻofú:dNýöûēl••"kãŋŠōB ؂'~ÔËj9t )Gšŧ0?t@đ˜—á’DŦߨ%ÂK˜@4œ'·ÔūdĒ―X˜óæÂČĻðIXíéH/øj‰íˆíŊKĸElŦ[YðÏŨÍ•ãÞô+ĖpģÎ_qŦ7~kfūŧŧ“qĒĄŨg2Ã+x��oędTÉjobŠ?‚rŒð@C1įŠ`v}Úðę|C·N<qū{DRâ-84KÓG76&ö!<ý™õCļŒũë:T��ŽÁíóū|F*@Ĩí`Ą?þ;Ї܂döÂ$y.ĻídAčŊïų"á Õ`ÅV‰Đ„Ðwï#Ї0ú<ž+ÎĻx`°]‚Cb†8účðéþcÉŊFýЖ 2 ZYHbRƒĶųJ�Âhöč�Đøā'ã5p!nĸ>C}SE/í­x $X6xiāvūT.äĞĒmS7éI…ē`\>éL âAŊCđĪL^C‘f-‡ŒÂ—Āž†Ë ÅīpŽ 4Åü°Ë éIģQu{Éž \b§)íÖ?†N 9<ķœ°ûL…aÎō@ũŪkÚ WęqüŋU5î>áŧ0–Vj“G Ī.;ÁšōŒð�jŠ<ƒLi~BWģ#îóđC7É+ĻuŨl)""đ&k*§Ó4#k ÖÍ|ÕĖkô™ŦÉcÚ]*t™ôU Ļe|$M 5Ža@Féņ”Žr  lŅ‘t›{ó”âëšĘũááS=ņ~JwåŅ›ņĶütßɧ/M.­üšïūM9ÖđðčũÉĒ?F̟iņjßŅĶđ?%ņßģóUŋ3ōR/[?.ėüŪx*#áém>ęøÅ~ŠBųõWįØĸQē8^œŠŪŦö·Ũ–rÆŦŸ€ÓĀópÁāšČŽGŲv›Éø&ynD#ó.ŽbPĩf‹—ĮÞījĶuøha7ŠĄ‘Ķ>BÁčAØģ}FŨgÍ]į þė`―y―“KŠ%Īō�MyZŨ�°bá͒ŧÎTþg}ļ�―EÔþ_YžĸˆiõĘČëSYOäwãągckgi7ÕÕÏģđ�+Ž6úÕ7-!ŪeOquÜ1zĪ(æ ĩęŲar'–%ũ b;?TÂ'c{ëYdÁĸ]Q?þ‘+ $1Å5fš–3ŸüåŦ^ėĨ‚%r�NßÔ^"ÄŨw—Ũˆũä3_X|IgĀžš’?ƒÚĖ–ōU5ŌâŨOm·K|y›cDą”čaŸZ~bņŧ§~" â'Þcm7ÄŊÜbôˆ ?3ą�Č#›ß/vkâqßéõ\’>G؉Ž-+ņ‘™1OõélÂm}ĶDšŊcFÆYeBĖ*,ĮŽ>Ýϰƒ–ôį0 Ø7•FĢkđ’ę5Ī ę ˆÃãÉw%ėÜāŅu5Ąƒö9a+öđŪ_ߞž˜ŠĒíčė„˜ÖN�{ûdrÚČÁY Ë#KéïlåönŌôČōüšé\kĸH r€ožxĻðÄļÎþö*aŒzOį/Á°@–ftøþþŊōĮ?ā‘ĩÁļ-Í(lËh}LĄ•§áå ]ũŅ: ֋öü‰Qí7\ðâœMV}vĄč°íĩāfēÜÃÚŅ�BaĨ j­ĒrþpÏïõŦĘØĀ$~ZíƒF–ÖøĢû tG–ûĩD'ĩ…x`‚ŋ―Á؅rïK?ÐŊ›<ÖūÞ"oļíjw#Îŧ{įä‚é˚ĢB=NÉG›Aâw…ŽŪrŒ‚J nę>î1‚ęoæ.›RąōN#·}]…=“RŊõlWĘ+\d†Īĩļ,5ęu ý 3’ÏĻfįˆlæŪ%ó&ĻT†D-‡@“Ļ)óYs^†ÖĒÉO~([―Ķ_tĐ·~Õ! ‘šE1PŽ·Y‘áŽŅIöĀ?ķ<`Ž”�$\cDČČžÜTĶLĸ ôã’ę”đŽa]ÍKÜIcîv­Kdā[v•Uû\<ž)]2›ûGI<*S’iy„(‡âoē0Ü*Ŋ‡&gSĄØīëōOĸ>ņ„‚ēÂôm2.’dæMÖAïâØéņųNwĐ>R6ã7‚r+žEȉ!į°‡Ð•1ýûœkč―s_[v/ýeÃĸ–ZýlŒ‰@1ÏõÖôƒļŠÜČBIŸ3f1€·°" …–†žąą.ä8Ėïp‰ŋ›0}šŧØĐïa""%ĐÐÓ nŽ~i# VäJ—ČĮwũĘĸ):ļʑđF5ûãĀĨÕaöúx ])xŌÍeōËæĐsΊ~ü;Zö‡‰ķfēNmėgļFĩ]™ų`―į‚BRLĒėČó:eÛŽt ÔÅD―BïL°_]ŽŲbÔēð�Ęnŧō[ęޝ‰Ų8Ÿč>+ö}Āģ·ĸVÚ­Ī„âšŠäĪÚKįĒTģ%ŅáIŽÄÍÜÜÔËÔB=ÔÆo'ášųkö'+BiŌķšŊņ-ÆN‚�mó·Ž›Vé.ZN‹GáŋqHÆ@ÃCÛŲ$@ŲfĒYg―ėa·ZâĮc€Ą^ÂŅicæ>c4�� ūŠĀƒĀJژr^1k#ģ^čĮ9ŦĨ’ĸ~‹Ö;éĸ7(Γúf†*ƑxrœØQSũĨĖþØŠ§.Ī#Øs Qkï—"ŰΕÅ0Öø&;õ†LĄ-úϞąÕgS]}ķ&aį‚gá 7@ÂþĪĩĢTŨXīį՛—ųrüKáóŲ�x:q^QŸÐÚ4a—'dāŊĖVÍôÓNÖWš:Ž%Ž{.(fX;^pIũn[fDBĀÛjå n\F0TĩHĢ!w‰@1Ā}M?„�%vþˆF7üJ�–[’ĘíÉ*jŠNJûL˕áĪ#aXáÅZ*§ØÉŌ8ĐđōōĘbđŪÆ.Á|Aąôē4A­RŽ�ííø]yë$aŋĸ8Ÿsíļ@e^ĐH1 4„:l$$Đ+IföN· &Ķ`ũsÝM›<€1ŽíMXÂ<tnžwėsðiŦ'v‘d ›" „c(ý$rHßš'É6X@”{ēmó°*ų2œĘ|gũ<ð€CôP™� åÓMø*ē!ĩ;‚ė~°Ŧ›õÛ'ՖųîbÁč'VŨIčãé6>Ð@FþŦT$%@đÉĢI'ÏûuŊDįköĶčo1bÐ[ä&Ýb7ŋ_ģqÅāußū%ë<Yáprǰ}Ŋ…øz™!~aüaņC}‘RųÛ/·>rõRįüĨ-ƒĀ|§$ä\€NgŽÍJé˜6ã-‘ÓݒÕcđiŨ4ëūĒ. sÞ80Û+}1Óž .zI38THÁō;R؋•qÓĀ‹fED<îíüĢ|8x° ŠÚÝ*ļQ d@Ž\ë1ÎĪVą&%8:€0žß·â—„ÂÓÞːðŊ6šäIp#z ĒxŽ;ōŒ‰ō…Oļߋ óMÂCĄJD9…üi%†#zŨä+zÞUŪ%ķæ�ŧ= ŋáÝįsƒŪÛ5įĒ0fƒx ŽWvïá%ķ” Ā�%XÔPí9HąÂóm"EūôČ{LœÐ™5Ų={öâļ/W­õPAĮDėw•ö@‹(ÉZöõŪHyŋәĩžâxIČŲš”ė<p@4<hó0ïœ!rIPNl kĪ‚söĀËĐóvËbä[3Օ ļm8’:БĒBJd™Ú‰ũrĘĨ\·VĄ�MÄ 4ŋ藋?0Ö5{ČZLeY?@îYZōéôu[îōWĶiûĪBšƒë—ÝscØ[Āķ'ð $ _ĸ*ŊC;ŊËĖP†+i|ã ôß *1ŲƒĢšŸŦ<ZyDÍļé˜óÝxüĀ0Žĸ&Õohîøĩ‡üĐĶIî7:ĢþFōĪsé[ݧĄ|7îýQŋĀr“áAˆĀâBï"†!ŒōōŸ§#ĮŨ—œā #ßĨĢĪCŌ“Õ„;2‘Ā}C‹™,Røz>Įæ^ũVÜŨÝv~ï@ˆ€2œęœf&~Š]ęžÝ+D3{ņQfÉ\cŨę‘VÜ�ōW]E7ķĢzn">!ýųÆŪ–ŦÂÚKÛý‘á^šŅŒK2<'1܍xFáņ!PÃu›Xƒí^Đ)ŸQŠ‹uR[$šŌLģŋũIŪÂ0Aĩ8J™šVÍUjî>ðķDdvÃd "I�ƒ3ČH”ÔSß(ũ~ƓÕ5MʖÝw?ÅúuąoÂ,7ŌŠkģð;ëæīÂY/ŪAóŠ“ŋųrâĻóå…Ý $ÉäâŦĻ)ýqÖ[I2Ōfý;ÓØæ.wãV—1Ÿ*/ŌAuØÆ§WÉĻĀPÅķū”GÕx <)>―Ï.G ˜&zZ7w.�û—ÞP@a—ŦQ†hd ―ÁˆR�‘^[oĸc_wĒ^Ī‘>LŋÕĄvĘËã4“;ą™;đ ZßĖrï>lzŊۊ0Ŋa€lIĒĻËĖųũĻē_E`„{l—ĖŪģà Š^îóv2SFéJXJHg8ጠî‚`õŪđþČ$7īNģ܆pŠāÄ1 Vn†â:xb–]ĀtzoĮčžđ}šĸ§Iþ{–ķ–EÓnv\žÂ]šĀA ĻĄ4ĸl79` Ūgz™ąÍÓžĀŠü.õËBČ_F ßŨ 8ææ“tŧK Æ.ņ~ÖÁbWžY…Đ­n7’ûš6ģĪCƒfĄŌdĘ-wē ĪI ïĩäpó_Æ+d>ĸ~ÕÖÅ)·Šãl m"ÐŪ—T(âÓWŅ…TČ͎ˮxâÜiK6FÕĩŠö™Ū‡<~Ėþŧū ČļXSĮļ=ĘTv[˧…ĸ2/·ÓÝx`T!ÛЕũTS5IŒ oUŲ‰ũąZėoAߐ%æ2ķënĘčÂf@Į>嚍ęôؘl΃˜yÍۓĪ>%ÕŋÓíMÚvZėŸŧÎKņĄ#oښJß7îóøï@ĻevŋDýnī†*t€_\CŅ„+døÏóZ:Ōïi l’y™į6ßÜSzf0'bï‰užŪ“ëææüēuũųŒīdĄC ë††TzÓÝD*ŦÓĻô•W@-Ū·Ēđ<SšQpQ<„“O’$Ę}õšaf!ÁU5ląÔˆę"Õ9VÜĨSRÂWđ™;Ÿm("ĸ4°k]ïEĖĄŋF ™bp‚—ýāk8ĖHž†ŌKÝ-es<núûváûČqbR]ÝâôxŠĢwé―KG‡kwš‹ĨÅaÂT"°Ŧ.NÝė~ŋÉ4`[č 1ß9ˆå Uj•&@Iĩúī<þIÛ(ĩæ˜íïKCCė!Õœĩ•á)šÂÄúšÚā­z&#fGBÔx§–ē_øũēšėåhųÝžĩÁÛIŽĩ0_•ĩ}Žöܜ?s%6$a:Οs:šƒi$h7LOãåcÓý(ÄÔ5P›nēĶp_Û ðQĨ/Œð9AFxčuĒråŒ@4%W73úø|3t_d-―äÚÁÎÁŌÔóˆšŲƙZÝų}Ø]‹aHŋIŊĸLĸ~Pũ~dĸ–öč'æx  û…ĶĶÄsƒ‰óÕt–hcø•…$‹}ũōVņ™OrĪ@ĘâĖöā+SyņÃ1]yRd'Ų žg~Öu9đäÎą%ziŋ°îLė-=°`><TŦøP@*ŽaE&Ņ'ácÓ7gĪ)R,čsĢPiĀėX™_œ‰Ók:ZŽrĩēH}BųðîĘÉcô ĨøGõßÎÉ sĩ'at„1„„ū0p��ð Ð IÉ·‡Åŋø< ŸïĨecđŌĐÝ CĻüƒH~ÆtrcæFIšQFŌļ qLî–O‰bŒ�Č^ÍŨð•-/T„Tʄ„đGeóMÝŦðk°ÝæģŨOøP@*Ža5Ąnøl_:åb.)ÓĄÎ%dÓwd)ä€�HFĮ~ 7{ÓÕjð|@ Đš§pô’ų‘ n™Ác}U„ę Ą(ÏlËÜjbqĶ2 �€kt‡Å8<]ļČ*Ý$$ƙč*$o.öæIyô‘IH‚zjĄnWĸz:Ze€o[ĮbNQZęũŽn―Čé.Īã3‹Þ‚qï―ĸ æc—EFfoÉVšR ŒOÁ'ŠþxėĻ ŌŽ…ÅĶ Ԝę„ó—VwŅeAQoiŠ$ZæŨFįáčŦĻrv%†Ū݄1}―‹PėA D$€ÁQÏBcÓKÝðļŋĸdō~з:aägRģąý)â6A‰Ødđ5ŠŒŠ QLyÓMXŽtýIŦóÄÜÔÞkÅũ•ĢBKĄC§Ÿ64BĖ{^Em!(`O6Ę/pyŦô―ëIJ'6YĻĖ!*xxÞņj2E™á{ĐB•Qų`nŅŽÛōAĒŽ‹88mĖݏ{Ļ”ó?LrleöŽÄä_ķmÆEKĨïōW€žˆã›ĀœšxũE•ˆģÍo5Bð„ŪžÕë”ųī8Ļ;óôÉöj�ÚÚøgÁėšë€ŠzB1aCJSƒïæGDrģ­åę―Jï Jzi’�―ÎzO ŨAĮŒúŋāéƒÎ(]vŽ6”ųQƒę?ÍIĻ{ŸYš5Ŧ8Óėf•ø{ Ū#ģŊnRēEv[îšíšÕ=rķ˜Ä‡Ãøˆ‘ôF�ód?šô‰Áō[RŪnōOß5§8ņ5fĘą€ðþeˆoúJ~dŋšš~jŊú*‹ųŠßž”~joå3û ũéUøĖAhĐČÖÓōŪ‡Rę_‘Wų cïŒ'æ#:ų?*wó?°õhzxĐCEØÁšuŠÍÔ 3Zäōp ņ MÜėšSc,Î*ūŠ%ąOíûÉųCZxŒqąÏíäŽ―5íÁE\ķ�Hö …ļúšî;ŒiąëüfāÏÛų}ß!u™˜Ãc5’ÅåĐc–äÏš™ĀR •Op‡=ŸÛ°4AčÁ‰ĢMųŠ*é7ϞˆĢfÃˆîÆ âÞ&iņÃ:ą–ģíQb%õuU|AÂjS#Q•#‡žHNy+å›)žéyÉUûÎQÔĖōœÜNB„Dįņ-ŧ“LWR~͉c)<$Ģ0Đßy9að Áðóc$~Ņîļ–ÛU~| ÏŽkbûAqgÔ7IE™u ݏéæÚŽÂŌÂFYÝFHýŒiÎēåû|аÝōÁīw0Üũĩ“7W “||HĘÜ6|ëLÓōu y0Į#3Ĩ=ų8eÓEgÅŽĖψ§žŌ‡V]đŊ~ï7KÆøĻû;%Č:ãvMv •|qÕP ŪĮ?”Ÿ Æ„KI./†lÚRÆŦŋĶ?đē͊�5/ĪHķüÕmTLŊÕEū‘ŽZ&K. ŪРū)ļԉ ›ð.?'*ĸUþŧ`pžŽū (õH >`·3I•3DŠcŸy(žÓõ ‚ļĻoC8+‚ŲDŊSÔū9'å †Ųr°čG„~mY€Ä îWĒäzž7äŸ[âP\čšĪcԄÜ`ŠˆėûŠÜúvÞčņHPïý&'6đŌS…Õ—IJÍą #ZßDÖ·ïõn? ÚÛ·l0ëýØz+,ŧ,āBÔE§čõ'›ūcåŽĻĩ•­qžāķøâŒ#ĖYP€ Y#úRIï·i7žÃ%@���Xõ5cĨü5=-āˆÎ)=ið…˜�0â;ũäa"?JþŒ•���� ôžÓ•ÁPÜŋ5ĘĢŽuo@öĖ øÆūÃ@ųę@ðu(�Í~ēk#œÁ=ö;Ӆp#ûVdžï{ÔMæŽË4&ðũö;ø$@Mû§õDëī[Ī–/‡@õ*'ŪeäQ‡nö�Fïýē<e�Ûa‡Ųßą“FHį7áõÃĻ•ŊTPœ í$ûƒÕõÃĻ•ŊTPœ í$ûƒÕåƒC�BĩDĶÎÃðĩ‘ŨŸCĻë3HĖšB”KMô―6ÕD}5B'õi�9Ęų­ŧŪVŸ+MÛŠÕöƒ„Ų5õ'ŋõ1‹ôó1&Ũ“‡Ûk…rŽRÚ!ũŽŲ~ƖžĩÖŦÏ'*­je}:<*nóGÛũG>ÁHÍ |+ÄþúÝĩŸôųLöVĸ#qI”þþXˆ;ęoŒbކIũ@<Ž18å†ärĐņïDϟÚïgÝï^“0Põ"^!$-D˜Āë.YWv?ÞAßØlÚĮß瀀€€€€€€€Ã‰Ý,ŸoĻ·ˆb|x@}­ĒiĨL=ånlQt?‰0s’åÉ=ø·œ!oˆmÖvāĂq�·Čö™ÁÖ*›ē€â9ÆüĢoNÞVqō"“Ø€bz -oMAOōū%ÝļJ’>ßaYCø=zīû­ rDӏý}lÁրąôT:G· \o_OŋŌ§>ūåQ3·ŋNQÁSĐæšb…ļpM?ížiWڜ- €ßhÉ:€‘Ø”<{iĸ#+ēËmĀŌ[@ÂOSGJIguÂaRN._sVÓŧÕN:þÅÜAbŋ1$›1ö8FĶÐčÍũyûÞ?†Čũ’"‹[J‡Ž5YfCŽýRïxeŽ`GŠŲO‰Ķ―ÐtóÁŠ>ęÄÃüÝþ'ô͝Â7zjîÆ—M‚ģâ‡*ņ8Wų1MÔTŌ&o‰ģs.ũFRā݊L\FFQˆĻšcŽpœē]þô§E•ũ/ør·ręžē+KQ)#œôšĻ!yũ€õņBļ‚,)ʐaÁÜēķę†ĢŋŪg­ĘY…zīžĘƒÏüŽEŅĀ#6(e*2ŠÐėØã)|Ŧ}WÎEŊYŦiæÅŅĄÞē)LØd2‘,ģâü\Ļ—2ļoĒ8+Ä2đ dqiū|ĮExķwð+Å� ŠuýlįöÍN{[֐ˆØjũļæõ@­‡ ŒX2†Ų †‘ū&-§—ņÉnՅāyžúb$ekÕYO6ĄQ tÖ!o‰•šØ!6Ų@ÄÜd%AEöÞÆ›vgą†î(ēŦ_Sx Ž €é6nÏ ­z>™ũăzVĩĨšęŽch <óebBÍ äŸ3:ą.ÓY—Α”n]P;ģŦĖ ģÏæųģšk–ÃįZDí~Y„mĮmēP̅ސå@Y6ˆÛĨ<ŋ‚ŠøĸRdŽČ#Cr c… Tc*`žŧ — 5ÓßÍė_pƞŋOڋãbÞąP@Ӆ'wėüąâÏÏĒý=KG1#ųö–„—•°KG6]dũ+ *'\ۃŒŧCS8þäu-ęä•Ã:i‰ãÜŨYØZ .ņëN›ĘđÚwh>%GŠ™ÄŧmÜæNhsÏl!“>™ŅÔĩÚ\ĻÎēazRŸrœ“ïL;ïôï†q-™ÜŠýWðc5›p·1ú „đėŽ Ã›Tđ†Yj>TÅ17ļ{“ė:IŠę3R·2D'ū9?68ýĄÏžļÉđĻwR‹ļmf*™*•}ŊÕž"ČŊžzäLĸÝ(Ûí]Īú ĸ% ’\”s('Ïß8ŌZ–NÂÍ|KĀsð߁ Y§ũ Uû ›”ÞąÖ|’wJ68j‰Ędõ‹l+ŋŨNyđ ·ÕčzîŽŌrxQĐÓ― :Ę3„2ģyÚG-e$eÓĸhĐÝŨŸ°ö/�_Ëšī<iEÝVCC+Ÿ9ĩ8Z’:Ū€­­[žƒЍ%‡ØX4,ýđ"ÅķmÂ#ĐÁ”HþL hžđ8.SĻŽR3"fÓį^„Ëŧc™Éß F!#Ķ_>ÂĘg‡ôœ°Ŋ/uļā‹3ĸ‹cš% /za~Æw~U:K Á*|‹ÏeVžu˜=€ņ\ŪãŸÕÎcÅŎ0á­/�ÓB:_Ĩϊ}Ĩ Fjð0ÔŋĘH—hj;n—4tÝ7uņJ9D˜kï…EZ$ļw E€ ŧJcēhĩĮÉkXėdŪŨĐ-' ŧ#[Î9Е(E$ĸ:f Ík�jRhũū?QĪžÐ…ÄÚøY‹`› Yū … nÉÁ[~hóēāyFÕ*É_f™(öŦ{ĻþڞëšÞ…KĖ”8JķgrdþóŽ‘Ø’ãUíŽÅaŽdKÂāņQ‘ ͟ĪT‰ķl]šEÜi}‰iø4D|―'á-žĀ{ ýēļ@i[­Í™‘öæa—eĨ$äïSâ†ï՜ǰ‘Ëų§koė&Įüއ ōE”î)æļĘ"§īĒDYØ"ÄŪ=&9nn_ĄÛQ…ƒķė|AšB=>ŋt·ãģž9ž;ŌÄj;!$–Ó%ƒrm?9øä}û-Ē+úVËÄō1š.ŸŪéD�"å- q5æ!U-ęõÃâ počî3‹°sޜqĨ ĀwÖxœô“ÕėzdČÆęØhŦę4!îsąœ G =La–‡ĮJķ” <Ý6đ€ŸūÆÞŠgVÐ45& ˃Ž'cÉė”OĨ‹&§Ïä-\š.Å3Ų‹"įW1lũ:†Ę+VƒŅąüŌĩŠ?wĖiu( €Ņís='Š\Z‰ūƒ ý_"ļX#ýÆĪJŌN‚^xõĖũ—‘ÅāHš–ûÐނ`Xŧ?~KôÎlÕė/š~ŲŦÓg–NŸ• &ĢĻōÓCæ 6ĒÚÃe7īĻŽX"4Pæ`é/PæØ›ō2P%qšj„ũū+gϝŦĸQ,˜é­Ó^Ã―j†š\ŋĮ;\ ŚóėZoŪðļEå]p°>Ž7ä^W‡Š'Ä8°O‹“ū$ģŽ/2*Ž_N‹Cå!·{ßf―yR°ĮÎĩ”ÍÁæi'zė‰ąœĪø—Aĸg]„„Ūh1š―Pę0$­ÂūRŽį‡:�U–ˆ—• =hƒzÔØĶøĀ2Pž°~U~Uëþ،‡·€8EwĄ1ĐŽr Ÿ]pÕjdšĶp ŧ_VûiJųĮWÂËðoŊæ )=Z7+ĸGœĘât€øũ˜fÕ3– į~TkĻtD"j3Ė ČđfAVZĩýĢ-X2‘@ŧÓáŦĀģĪÍTcōœVO4@Ų€·ę Ļüo Ïm8tī\ðV˜‹Ė_ãZ ð˜2båeS§ÓZó“›LzŌsɊčx?#―+_ؖáōđ$„Л:,Ū―s”ō)žzĀŠÅP^Ũ:ũŋ#@K˜ÝÎ ņN9ąR•Ï” ?ÔziÉņ@ĘßÞģļWێĄœî] åÜģļ{ņ^ˆŋ‹ÜÆ Ũ“"Ë"(NŋŸZëʖ$WėŌA9 „26ÔĮÓ·•œ:T]ãĶöĮŨ[NÃn@“gŦĨ†úhQüąNĪ`_­Nō!qÞėûÉēP”ųŌã+RhÆwusČÃI!ān$|tÂņ™"~Ā\<ķØRžhYƒlðķėÃÁ%ëFŦ“õŠėȕÄþI6Ŧ:o €Ļô2;ũîh1siŦŽŒļkĒÛÍžĘĢĪļĸĀ33ƒũcž�YÔí[ÉõÕ$ ýŅX†…ũĪöã―,v‹žūŅŽ ļ7ōn‡āAîÄāîúZ/XsŧvE˜ÂĄ—‹š•BĀuØËėûŪ“į/zĨÁļÛdząMU—ïÏxZ(=™Ó?1’ÞzBø/Kd\ĖņGĀ&šŽÝŋÚO/ųôęQāĄU^ó5zĖ āL@Œ74m€Ÿ ý„N<-Y9ũöxŽī-OmŸ_ƒĒĘŊfļĄŽBŊÖâ°rB|ëåīð3zXû.ÄPLMާ(åT‹j·Å·iĪcäõ [Ë….š —ō8ÎŽ|"Ó­āz=Ļ™7ę-Eã_6ÔCM…äT]pvq1Ó!Ō―WįáæGŦRDĐ(Qōõ‡Ō9æŠčč4Āž�UĨ7čnáA52ŲG/ĩ=Á‰˜·—yҊwNëĩc,€šŌgPļÏĐ―ļ`‹›ÉáLŽü„VßL:ãv– {*j'þ?ӎcsĀÁdŋ(Gý} Mđ Ųþ˜ō—K§ņ_YiīÆōÉ›ųRšó#‰ ­Ŧm˜ŨX„ėŠčáD۝žt]mqcžPSoNÎė[ ÃݧrÜF·…}ŊðhTv3Núāã–38p \~ąb°æĘ{äÔlę­YđéÉĶ#rGō^ Ē(~ ‡„ôļ}OrÐ1čÐQÔ^Ą9ĸiP++0Î>đĖôæXšĘa8ēüfĻtïįl8{bAcīLJ=éXĻÖ4%ļp<éŽ`É―Ę;• 0ęĪ}u:åïŠAW-á"oĖĻ$ē‡dd™ãūh›6Äjd<īĨŽ(<€ēáN•Ÿ-8 [”—ęcÍ8ŠcÔĩīë۝ņÓÏ6•Ýģåk™CÏ ĩhŅJô5Éþ; X–điā9Īþsĸ ĮĀüM0ĮÁ(þޟį$BųŅų<Čð3<u|şČôj$|˜Šï•Ũŋ5HFÕo4˗―ʋ74ë­�*Gاˆē`<éځdÜU—€ßg] rð“AØWŌîBÔ―ē,zČĢĒĪ8ÚŠ’–ŸĄÍüī'ÝkÃęFOč ÏÝĐFâÁÚátr&së)vģ'EųÔ6ýh*\˜~m9Ôŧb8P(“+€fr‰;S~Ā nÝé Ņl§)š’ĒËīЭáešQš xöÏ\;›ĨéésÞ Āþ5ęOe ãLę­Ä’Ĩ#ÛʐꀔE°û–ö…éGØúŒĻ"ØĻĨ4ŠRûĖËX•Gø›Öpä”@P›Ž™Ī­đ؟öðMövŦ8(S”5Ô ũ›Ģ#ĘŽļíM―:ƒItÝ%ÕuîųÛHåB<|ēĶӞÔGâ?[éÛøüK·ŊŠėšõ$x€ōį,öÓķgÛ=ŌųŠúŊ”đ öĶ‡2Ŧ/‰sކ-nūqEže!)>sĄŸíaŧ‚.ŧOĻŪūo›Ņ5žB}ø\š ū:DGË?ũO<9ĄTŲ–KŌ*PwžÏgUë‡Į@ÐÖXFĒ.2ó 4ŲhÞ@ĖĪ%ūŧ‘ýēÄSiÞ yĐÜĸĀwģ{Ï댨!wŅï$qęZô,ĻYDĒO{ĢG€0š„Ė)9đQxåA€ĒUsHÖõ&ô•†QWɇ.%X ‹'Žö}\cŦšFcâËIĐēX°Ï4þ™›·ÅĀô•xiöÅ0VžĮsžŪÖ$ČPë•ļxūjcđk[œČ0T!Įī#žnzKfBï+Ū:ņ7) q ŨŲŦ€ú™ąŧ#ÞC*ð-šŠŠu7ŠWF|–ye_ĸ;Ąėé"Fü„ß’AÛĸKCî(§pj! žā؀npâäF·ŊöÝōdý‘â"Ôm ‘dnÍũH~wœŠa·íqHïDĸŸ& Ž)<n1fSWzÜ.(�­ ũ[€đtí^ė<Á”úēĸ įnUð>ï?ÛņŒZ ą2Ÿĸ=)*AĀޅzÍ+ ûĻqŒgŊN}ЌĶ}‡ÕĮ§Ā-~ėx·J“‘ߝ{ÝZ:ųZ�Óp1•e#ĸ%N҃\Ԏį.L 4Įyšs;„·ŌœŦʙįÉÃkķ!ÃĒë/e›ë4b(Ÿįni]OyJ‘‚įĻĸaņK‘ZĄÉM―]7]Œķ8ÐiņÏ3ržõ|'™õëīĩ§ŽÎūzīõ;֞đwIåĸjcÔoƒBrŨáŠüw•ũ4^Ō|'ĘVŸP{IúīõŧŨ~ĩyûĪôŨSwIw\―A>6ŊÖŋ:T|�ōĨ~JĢ=’)^ÕøÅŲ†·Ëš'Ķû^qžާ›ũ-&�ïo°\cąÃóēūģą1!þ˛CVJYĐø4‚ *ĸێpdjM>Ļ‚Ý^9#üÆV4ĄĨ}LjAÞßkZË–)”BÉWÅbtå22§æ, ŌôDĨߥą/įbvr ŌÞĄęåŠ$cÐc\š§j0P:•Ī‚`ī‘âKiūþH>Š?d[––°IŋBņ}ąˆÓĖ‘/•2Qï/GėbĨĶ!ƒv}Ķ6îöýß勨“3k†Ų:‚‰=ŪāZĀīëƒokÝíÔ·SŽS/rõŸõ#ÏļąðŠ/G°q•F―Ó]ŋ­Öy}r‡Zß=ó•l[ûÉý+Đ}_•I@­AtĒf*Ų% K�·SņũÉą€ pÎC ŸTK‹ó'áÖKī’Ę-Ýۜ0Î3 gšNwî+ĒMķ.~1Œ5þuellМJģÓm!pû€-֟ËŦ̓oUwŅ„ē0R‰ÐÂß͚‚Ši_یOoy*†…é?9`íģyþ $΃­ßjšcĪŋŲÃe#Ũq0ý­“Í-+XčfmóG‡A ÑōoļÎÖVO&X+ķĮ:"Hû‰ôÏŠŧuĪCI"ÎķU’ēJXĨĻĨŒ )ßĀ†ÍĄĮŋrŽ·Úžß#m%dÐŧŠ, 2ąŋCę6H&Ī'—é šũõå ƒÔĄNq2QųBP?ïb7d­đjŨ?ā$šMđöŽĖ z\čBˆ‘ßš°Ðí^†HËH$„ÞŲxīåäVŌÖb,wé!Õŧ%ó„/ęR=ØÄȏōô^Ņ;•·Œ–ËãŦH€äJĀĖÂÉC–ŊŅ”âßð]Ā„į—ŊĒ`|�†ŠzĶTšĒkË9CWËÎs0ĶĶ=žÛóý8,CK'‡ƒŽ­--]ÓAųámE‰"OÃÎ—đÆšŸ)úāVÅJUdÝ â‚ūDšv’$Nđsņv—Ýë[wÃz;:žÕÄQņĖÕcÕu^ģIð“Š>䞆‚H·hį=ļ}į3Žŧsö- ļŠĘíC\s—ƒžFÎ`âĢ―eŠ cÎýčË–CM�ŸM§^ũ+Ό žýŅī­ŲÛöĄå\*)ķx‚QŠ+cք Á–ïŠ54ð ņ•ȉzƎ3Mëu―ôU4{%+Éadm(xŒš ðŪîbĮ,F‘§Š‹<GÝڐBRåƒÏƒęãbØ û"Ą> VÚ1Í\ō�8áOãõBÝ ŽĐuÚ áĸ:°&<5Šž ų[Ũ’t0‚Vée}/øn%$6”Íã9“ęâmÝ„í4ðqaôÞšUkÆ#ō8"ņ8`F*>į­[›đ}ĻÛ?ųė\­XoÂÓý!IÖtļģlg+üÆū?8&9ŊŨMŽUýCÏj”Ý–BÜ2Ÿr4XîG“ó5Ø"ۜ–u[ŌPi3ũ ĩMŽ›Ø9ūņŧÓgE—°—=Ü~Z ÝüzÔ&š†)ĪIfØlcŠÖÍYGH]ĐķĀÌZQ]ĢE7ÏÆļf5ې2R+Jôßþ];f:ï~Ģ(ŠÄœgŸ0bc­éūčdúäAųv_ … å0ąĢŪ…Ë•-@„ģ›+―ëëE3�ĻîEÆ6bē‰žáĢjœ˜Ū}nđ˜ĪÁl#Â]Þú暉Īj­ÁųewÃC ō‚9AŽ?‚ûyë!ð7%ĸ~ÞáŲÝŧkũĸ8æĸoŠų~ Šr›b1dã•°�VÞų*ąhĨ õĀĪÔūŊmiŽ™æO…A‡îįŅAãŠĒÄeIŒ/N”ûÝÚ% ü<­l°-uĄņeČN‹ôė “·ĩąõ\ƒ€†âčJMŧå R›)'IM°rëhdhlsMĖA†rŦ'mĶᙃ{‹kðá3ĩ,Û<Â)Âj„ū KļގžŽĢĪÔę3Ċô5ĸ‚ē’Ø6•ǐ·ķŋ™ãsà ·S=ã<F(ö&ėÉŨú%Ī+Ą˜Šy?;rpØt–p&vW1ÄüˆĄ„ā“ßÁöŧF-ÚO:Į!ðÄŅ)ģŚ(~ũB6īԐoæ&ˆĘÂ�lU :€ƒ­ö”ļ8wóÞUī—Î1czTBŪģåņ Tï >ĐžÆŽ…“1[žë`_I>Ûyfä`"zÝĶž2ó‘T@U~ 2õķŽŽÆ†AhUŋ‚)Íá}ē[ûqmũPu!T 'ū qņwJGõÂF5�7+Nä'Ôëû yO#ŌĘÍ ˆóÍ, 1DÜĮJ2?Pōk˜Š:ė]*;‡ā|Üæ’#ãö‚M|€tï›ŧuĩJ”ų|Ųę“_ ŋQ…F ï`Ōzï5:%R“& Ï†0jÐbz‰ėuūũNú-ā·Ð„n{―ŦKÖbÐwϟõäp’+ĸX} ”ž\ƒīĒņĪ „ˆ°ß '|Eĸuņ”ũŧbąlŒWõÅiž Ø9wææžЍgœ™o3åŠŦ\szˆčĨ˜‰mbÛŊÂJtrXĖØ5ļsßĐf,Čn.ÅRFT―‹ī:#*|€aâĨđ?d‰{ĩ̈́žÅË{…y•5\5RHŸ –ïkŦ“ÃåAtšðĒĒ1y4*S&øČU0ų4Ō°óī•íz•ÎōŊÏēO �ÞÓįr€Ýķ8ĢA,ĸXœ/ˆÅŽã%˜ŠRÜšŨaĪûōąÅ…æÚF&A16­RĀ ö`f0 r_ ŪÁ+ցd Mē@-@ÛÏ&ĶLĸU['};đĻ™:-<ū&ãŒÉnÁ•…°Q·pÝíŋä9Â1'šäģÜkðšT‚ƒð—ŅŠ0$œLĐOķ<Đ­BĮ᠗uMūégƊ1ôûŧĻ”$”ĘÜ(x+t—rīÝÚā|ÔYĒhé|ãhũÖ7Žû§Ūã 2Į­æ›°ï‚lvęYũ) ‚>†xmG…Œņak6"PlÕÄRI2]ÔøXqŅ;ŪaE;/ĩ°ãҐĨ�1ߨãó5=Ũ†Ų5Fo—…0I‹›lÆO`q·ÕŽrŪÕĐâû5zYp/=ūfä›óŽŨĶsšœ æo/Äûį%čßí“ų‰š"ĩŽC”Œ“j@ŅN6ŒË</ĀËWS`šKR•ë=WÝ$L}Z+=Ū@Ē%ķįïĐ|‡‚ģØ@6UûîPœ53a§6 ūūŠ0}OSģFcīĄOÛfZ†“–?ÁwøVD3EgßfÞšŧf ŌōęõyÄ qóü_–‡äzþŒũDfxķ5Bð°ĀĒŪ"wōī üÔóLؐiÞ<ņ,]<˜N/ ㅧņæč,tÚÞo.īhÍŧ?Š ^0âĪT=.Ļ:ÎGˆðš•ŠđÚŅī ―ÝŅ7’Cüz@†Įļ'ī)‘L”Þë%—œF$āÖHÎTÜĒŊ­UÕý(ÍcV‡+ŊķÖ§ūú*R:N5:SÅä^ëųĒą?}}F[=žŲRDsÍWyŠd„}Ū Ÿ~ĒĩQØĄþߗz˜įÍĨņŒØw“õģ:Ž1>úŸĻāõŽ‰|„:ė M[úï4ø8ô9ÓGēõė?‹·đÁc&,1ŲrDâū= YÎþÄæŨÞéVß Vų ĀĐuh]~Kråv/Yâllšð_’e-Tšv―Db§*óžŨĸfqīĄ+Žø/+°ŠRU?AþAĢ4ÂlAAtvlˆJÝeŅ?R‰â#āÔVÄęÉÚ[G䌯! ĸ-WįuŨåkŧuAVwÍ ;hŠeu~A}‡øå(œæo]*Íj “ßí ĖðĪ*.ýŦéĸ/ŧ|ļÕ žûUŋēUýÆÏ•B(9ŨĐĨäu™N 2âŽį [6—óĶý·ž&#6zëōá/ü7ߒ•�QU īxOŅ”}AnĩOšjŽ=â0’fš‹đÕ§ŒPZwîąõÔ*vœŊ"GųV:Cŧ)é˜~v.99ž‚‘,RžVš\†_šËƒ_;  ĐÞ :ú#ðSj(IÎ_Ó$( –―čŒžØƒáˆíVwÅávšƒ-2Zī“DV“č―%p‘ŽāũėAŋÜŊ‘@€ß kĄ"#įėÐUO4ÅÁŧ:‘č*:Ï5€:,և1Ó/ôšA^ö ƒÚØN=7%ē·=ú ãވqÚScėÛýâ0õåAķĶó$ého,įŅzš0‚õ™]: äFrÏÞVô%|ĀÝY͘›YoŅ—…IĻ_ŸDÎWēȈdŪvd―EĄķĐb„ÛGĸo—Aų>ÎöÐņfz~o2úīŸ8SkÜ<ĘĩôVuQe=$a8%ĶĶdųō(Ņb?ų•gŽIČíˆ8Šy !&BeĪąĶH;OÝũ/$ÏĶV7°›<-ķ™é eõ›&Ôē}?Ų@ŌŲf“ ŸÏ.ĀųÝÉËôšęÉä9:]ŧļ39ŲčG̈k(Pũ<wmŠDđĩĘpĐæŪČjÐŦŸģ‚E†ļila­Ũ`("s0]û:KtōäīiŅkëšŲKũ<ãrģÆĨf”.ÓÏô…ums]ąa<NĻdVÅ4ýmbėÕ C-öÓHŅ2įŽb"3īÛO!#ÓėÅvËģųī­oęĶH!FŠĒöæ—ï’œ„ Vú1ÓnšĖît|d#īë”ÅųŪ[Úį°@á-N^ĸoMÎ&ŽĶ–€*ÞqųŽ”jóã8_z‹øÐÓw kũÎ!^ëņ3ʍŸTú @*ûĀŒžYL5ÐøíTėD·™ÍI /ÃziB-ú>ĩÐBw”›Ą ÁÞņu"45ëlūāŨŋ…ŅĖë~œBÆÄEė„ĸŽ^aŪ@―•ōNĄž—d‡"…ĨÜŊåZþE4<ũü![N=CžXAqļ%ŽĨD§·ģ+3{ ŧåŒqÅ\ĶÄô0A˜s#ä"_ų‘üŲ:ÉP�(e��QÐÖR“CŠĶPnd%ĸ|OļØMVö„„…þgÐę qĖâŦÄĘÍ^ėÄ8—ũÛqJ:šb~Ė>ŧÅ}â>Pß ņUËYEW‘›C.čûý(C4ŒæĶ'Á-_;”G&‰F>+,ÛĩþCķbðF_ᎄĖųę\”ag5ũž?%XP4vŨÆŠōFN(G.G ·͇ĻP阎’‡ēGXįïˆ&―Ėķ^>ô0Ļ-q‹ä2qŋĀÓŅķ"+‚ü… õÔ·c�ÂBA‹tz*IxĶČcd=æ—ęHpi ðd\kõâ*ýūGž5"[6;þJžãä‚*ZyHōĢß4(‡=œŌÕõÕ6]W'ŒÎoâW!A,J0ÎQŅl<>ęķ čŠÍo7Ŋn‰vĨ+ïõģ^•Îwƒĩ͓ˆNZņ;ĐóĀՌéģe”—ķķÝ �<Ã4 ß'ĄãbdÜļLļũ)˜rŅÜIý�&įIË֞ãÛBZRX·ÆĪLjīĨ“IäšĐ$Š+ Ôy™Øt=x%ŌåbÛŅ4CLOxëšĻGx?Öōųī— ŊÕ ūˆė ‚ÉAŋņ2—ú ÁæųQ&ā‹R™€^0:čbŌgî'~ÉąÎŨÝVJųĘ%šōßīžúA0}s€ųV9„”GÐY %Ōō$ŔōîÕóôݔŪãĄÃúŊ–ņÏ2(8RR>0 ėŽ[ó·o6€Ú@R‚./Š!žœJģÚ0Ý;ïtvŋ ÃûâþNģcé89įÅ ÍūĢ=Ā &ƒÅŨžļs F Û1ģ­Ž&ŪÅĀ·ŋĨˆķiPilF[ĪXáđ;‰20ÎSÕG-ÆÓD-KHõĶæĀíã=›õ|åˆö˜<·â1nŒ°jNđĐé=wIþÓf'Áú:/šH*sīÞ€1|ĖŊW+Ē~]ņœŲ;„čŠĨŋøj{ÚCN–ú/UūόCĀũÝHÛčLŧpzŠã€ųÎÜ>ąiė :Ũë+‹ĀD AātŠjõĖQŦūmÔvßĻ\ôŋnû–“‹)˜PũāüïϟR87ËjÉĻķSWą öëé<抛 iË šėütQ0ĶŽ§4ÂŧŽü .öޚáÚðÕzŨ†Ní#ōFĸ6ÁeRųÜģ Ģ{°ŋðéŒō‘ãxïŌ―CýšVŦn}Âg˜ÂũÏlî„1”jģļVđņSq3‡ābą…Ú9YwãYĶđ―·ī.usr0ˆ–kdÚMbōūÕŅÏĄïĢxA !Ý­ļ˜mXož^ÄdĪîoĀ™Twh‡ņģhCĀ.éTÔŋå{iŌvö%öąō•ņ€åÝÆ~Þ―gãíQ'Īkx…ņ t|k9x€éɑŅō<zTÂrWœ)í<ÞEoÁÕ'.ÅWPb•w{Uū˜ō HPzp…ülCŽÛôˆ|Ž 4ƒˆÄá�ĶÞQ{a*ííäĶQa&‚uā}°€üü‰x_ð#éóāCįGÁUøk8Þ/ãQãƒÍšļƒIũNŸOņŸ\>ŸŦŽõ‘Šöží5ú?ãĖiQÚCöĐų@ĪøÝ �a tū4„�ˆ>IŠˆy+ē<5OÂķëæųūo›æųŧpïaÁYņŌ?ĩö\š'‘ģÃųĄÅŪï.ķ1Ā5éÐ|q‡Ē‡! liØđ~ĻÛNówýNŒā‰Ķōōú)â•_Ûû€Ōp3ē–eĢÐĮčØïQkĀ5ÎĀIúe0N&h ƒdģčŽ*\ĻīTäËÉŦĶ\ÅįVõ“ î•ĸnðÉ6lŪôö'ŅÐˊQ˜§đGhqĀûŸ1ŅĢX›66å0ÞF˜é‘1FÉøFGâf€îî_<Ęó&Þžã‹ą*:Xðyû=ōÓ?å„b7Ķϝ ëÎŧ&‚Q2†ØD–ņZĒ;hŦÍĸƒ>IdJÜĀû*ÆŅĀ.ü07ĮáÐaoœũ”BZ}+N‡"Ūā}0†ý#Yr~+™ūU킚OģwŲƒDü-8ÎĨäf/ĒSŽä‚Ļ�‚C"?øŨĞĘ)Ŧ€1žMŅ�LFDzĄWEŋ~J€���p/ręIM’ ���ú6ntūē`<–ąöÃøRĻĄÎaĨƒïóÉ,yÕôíjD zŋŦ)îR Õcxé‰8ąÝm óžû)đyĘIŽá2úĸė#äDĢ%j~ÝUđ:^ōœœŲĢý‡äápr'Œĸ+}ðn&·lfƆ0ĻŽLZð0Sø(Ó ĻÖsŠnBmâŲžWāÄ{~™ÄÜGza"ą‡%ӞŽiŽ‹$= Ōôë†SįþZ•Ę�§'qƒ%ˆYþ)•›ÛjÁd#ŽúŅ*Įīė Œ‹ĸčĀuų‰ĒĘ ĸvč4įðFļ PmVøþŠíšO ŦŸ―œ#â^ā[M`Ą&šPĸLbrËRų^éįŦ3ΖŊŋVĐæū―ˆ>ë�éņPó|eđäí“ørĢēą ũ›:@8ėo@ÝĪԘ-ásŊe•b˜Wƒ6i° y―öēCˆÚûĨÞņ=š}DVLËM$šĮg|•æ`d–6H*rŸÎUÃeS›#ŠĄĘ|NÓÖĒ< JxE< /E'­0“*3hgÚ?öŸWö(Ú·ĘDBƒfŽ$žŅSbģŨÏIš5 q'{'+Þī0)$@ŽˆØ5ąø�õŅüOãƒÚÉ~ l;‚ī’Í.dÉnuōÉæs§Þvï}§8 Þ,�ÂD­7M/)7 LÃËw+kīŽ31ï&ØĩĖ~(|=þÖ$ÛþĪŲÝËúĖ&9ģi*5zŋÁ‰øįÁÆ;ðŨĘ:―ŸóŒ8Šĩņa,! -āl%„ęĖôa’ģ<ėô^"Úā!Ąt5[Šú79ӋöN˜›æÉ/bEāQīgī@SĻã”―bđW|/( /?gI8#(‡ãĪ–þ<š†bU·<æãdļDQđ.ĻgÏÞL$t_ŽÃÛkt_T a>•1^á8WÆË†} ïm@ãŊËŧ‹îœxĖs’HØ?;Ln{ŌDð―ýíLÎĢs~vė`ū*ŧĘŧ‚§ÉâÞĨĢnŪ‡…éVÐĪ)M§Ų40ÛÉīMâØ ÛŒOÂ#Ï$ΐCŅÂýøāŲôũˆ§ėíĄr-R Ï}Æ1Šė€€€€€€€€€€á退ŸĶ�õk§5Ļđâ&õękĒ­“ĩÃß —q ‘w>0€D†5X1äĒ>ëõ%96ÓbœvŨûŽČðvĘþë@Žpjb­ÖæÂKģD—‹*čŦĐ{|QhdPļÞq6ųn*Æ&Žk•ņ5PtG~!JÏąJÔĪ $ïļ-M‡ÓÐþŸāK\2ËÁ’û‹ŅWÆv‰ïݰ. ‘ųŠ“ cWėí]XĄ4œGĀm‘öÛļüUįË*q;Ē·îúūp?Į‘7ß‚ȈCŠiē—`"ŏŽóëØÉ{Ž_Ũ7ãĘä‡ĸ}ō\ģÕôf*:ũó$ Ō7”ßþ5ëSīgPęÆDž PˆûŅãy&|v4ģ™ +~[šžĸ=3I '2sÔPaÄdÞL—'é79âÂû) 6dMOėÓgÁNķĶŦöWŋ+`þûIĒŌ§žíē� „åi]|­'đmF4b­3)sūy|š”aœNÝ6\Jļ ;J…~ï zl50 ŅLũķšŲ[åþ^ū†‰Oïų!Ĩåč€jpÞĩJŒĻŪ% ~JļŪ”ęĮ]óWū"&Ëü‹c=ï•DˆMøŊßōč8Ĩ#/+!Ō% lDė䅘AGęEĶũ­>F€Í”˜ž)1ĀĶWWMGr]ÚĘ4âšt,Ļļ…€E|&s€ĩhFÅĒ=L”E!ŲÕíīžÖŽ^}ÐNë—c—st2ÆÜíåŽ ^–ŦÔđoþûÍ)ˆ8l A8ãŽXãęįꘝdŽQ`ėĘ ShéÖ0Q!>ĀÐĀ ˆ$�é õņēŌo�ovø‚Sø:Ų•ļœl€€K“MFÎā8ã°Ûš öQ8íóÞËÚŠd71Ųū  Ą‘Ys1Īž&á‚ĪˆÝ ‰čä#^õšÚ0ũ]x_ųūælAĒ �ž-bRúßUW]3fT‹G„›ŽÄrûĖÄéq'°Ã%ÞÖmC›ëË―}ïä3.b_~MābLĨœĀ(EnėųQï{s °‹:Ų<”ĄËŋŽ�ÚýŨ-Þ$C#'dįRƗÁ`9Ũ (šēĩū8,e:pŅĻí•ߒĻĶqŠx^ûB ØŅókĪaķ5!ՋjyÂÓŅęŋ™w†VĀZrÍûąėlDŽM?4aWmŋ-#Ĩ€gs,đFģđ)ÜiŲ&ļ˜āņ ũ—~RŋƒÎœNÅ―ÉKú-ō—„–‰‰þ.mQīė4ÕŨvsRĢ{é]aŽðÍģNĒKč Īd‘ ä.‚Ýc ‘}xÃj|Ø Tįņ�ŪU{[Tĩß\`Df2šD‘iĨÕGŽüÐúĄÔDÉ!VJōsÔzÁˆ^á€]DD›î>FtÖþ1yŦvôņĸHbÚ0”œ‘)ūŨAeU)Ų3Ņö!õ‚FŽĩÍq†ĐR(ýę|â֚=ģž8ˆß  Áðt|WwAûe`ø;’ƒŪ9ĩ~îHø톌ŊĻŅ$wĮĐ ðÅ%ýÁŲyĀ“öÛ=`?&ímvŧA$ûS'HúĨ|Rژ)óĀWĀS+$fŲ\ž&øŊ8|ðtFŊýC„éį˜<O[sÍ)ËŊ›ˆŦÎÝčsŌzj ŋ7+BOc‰ĢÛ§u$KsŅ›―íQ€§Á]·c‡76öiëÅ"˜áô6î=YļŠ^å‘Ȱ`ž‰OöpHō‰@ŧcf[FCĀt`­čgž_îHfÎĐ5šņ_+C,éíĪ+üÎīO·ÂŧÅČÉVîÞøÏb"V�DVS‹wVôN“Ą)4Fįïėn›Č"·ÓųŦ>Ėŋ§ēeL‡ó!O†*°qdSïÁŠŽĨcŒáŊdhz@Xīĸ„îvnҟ+åŒûo�ĸskđqKūú‡Čę0n&{\*D›ā5Ųt8'fĉð�7>G6”ó{<ų|”XæF ޑŠüvÎ—ĶŽ#+ƒĢí7r“Ųå…Č ē"…éē,X •.―‚·IËödøĒôü$*˜ĩŸēxÞGPV#~+3˜Ūƒ*"dúI-üZ Ģi}i>· nxšX|B%ķĶą‚ģšÁ…ÓïŋÆÃķ ÔvĪëÃENBČ"8õ1ýðKôíņ+‘ĘË:dVÄ ïŸ•Ōœ™0™ Ą‡ÄÔ,-aÍ%YlŽŽh–ŋ^vâėb5ÔS8iBOp”Į*Üáqúš ĒŅĮ3Ð{ ŠĪá{„˜  'ŨIĀ )riŨcéðuĨyGęîņíg éĪÅ(„ivüˆˆ~’ĪŊŸĸXpĀë\59†ŋīüÃíó Ö2YU~žˆ<öÞą^ĐœĀļĀtRR/ðŊKĘįĢßûŽšŅĶÛÎ1-õķ?2€ôå§WqzÅËūú{āŽôÓïĖnãý7Uʁ}Ī€ÜElĻŽEüž #ÄFTd…―ô;2ōˆ‘\ëhų"%îzS ŋŧķĀvęDÅØ:;ŠD2—IÓuåU°ÖS}CۉEšĶÛę$Fũ]ŽīžĀ…Å+ߎEnF î·ÁšĻ6é�›Ĩ T dÆvsÏ =”gŨ7ú’Ëķ§6ÖOˆsÛīū3ŧ؏ķï/ÅĒ5lËgÛ~óo's·élnļę+Ëĸ%MnM6ÆM{”ųũ-}ĸtŒNûHšý*�š’î–S–АĢMye#_ €äŲĪĻ&g•^ōœWfąyÎŊú‰X)f<5Ę|ØŦØí (5#'qųn8Šdėí81ÄnÉ­HÉĖW;ŨXEVžž„Œ”ža8ՊxxˆÁ@“Ī4ž sčēWæ›ģ?Ë­ē+ŽH$æ&y9ŊíŦđw:W‘õ {ĶĮÖV`ÆWgŠ! Í3Žæ°šũ°­š˜Ð šæ{ҘŌėŅ ›d/"ÚieįF$PuģōĨrUŒlsüĒŪčįpÝiР“:ûžXÂo†ųrŧm‚Ÿ/š†^'Ü―…ž‡:>.ßY2ŧøïZ0â(uÍ8Gt0ŋÏī6\ [―ōH8øĀÃ@ĘÁÃ{üžęýšÜ=KÝÎJ%úݔ„H•@ÍAL [fÓč}Šd™C!W5˜�óüķÕĀÎwÍjŨ7Š ųåÎ!g,:ež'ũ. ąļĐŌÓ[š6ÔæFa‹ę@æI›đo@ Ļoecfõī ‹páĘvíģĨm§y Ųųę6Trâ3“ÎÝVšÁéÐCoø-@õU· ŅPŨŠ1™ĸW›Îv!ØE>pĩ €`õ$7nĸ}ÝÝ(Œįž•KN8;˜^yÚB IÞŽ_ą§Š(Aį†ĩūYÁãõ+OÉPZkNýCū۝L™Ũ l[‹rp)íÜóJÁoL ÃQ`3ĸ6ĸ|N:!,ÚÜnþGæ-ģ“K)jþãvP6ÏĨÓX{Ā„ó§#edîy%­`üŦ_#ąžÄœ°_pF dQeߌðYiPJhóEz”~õKŒõín_v(ƒqĘŪģ筈BûĪNŒGčÍUOiÜ#úSp B.íkēüŨ`ų4šŽ‹J―Fų1…gÝ{’ÃÃģAƒĨpIm/ú+ŽÝ1:Ï˘ŧ…H‰öäįĶņZ­ņ"Ā,Yįę š~väŲ&~€(ĐX%`ËC~Ļvqđč ę .�­ ąé Í~ŠV,’_Š#ĸövÐĄZũ­NCŒÝPeM\ú͛ó—Ž3ŧXü‡Zę]{Ųī1ŧÞpfAŨ0R­tŒ3dŽ^túĖ2EЃÅnFZf?]ŽWNðÚcōŽxvļҐãš Ņ{+ ĨĀŠŽD‹ßv Nz$M c ÓĶýNÔ'ÂėRŸ�ššØKÏbæ8`}dŌ'ēĮh w§Œ’Sō :€z#œfr]·šæŋĒÆ0zņD!™ Ķ^8ŋ–ß[ŲŽAao݉Þc‘më=eVóĨjäÖĸy(šfÃ>ģĩŠ_wœTų‚PĄšz]v†]Ģyj^+qÐĸ*†X=RZŨ‡2jƒGېh5tkðÏqÖÆ$ pgâT(™b°9vl%îÕ&#·3=ņ&Ī’zÜg„{j s& k‘XŦČ&.ÆsÅ(!ŽHŌ4.' Ææm[b˜|čËd-‹!Vũ„Wýs›H#Týž8‹z�<xFvéÖŒ”k†6Ō1įĮt_ČŋsõŠÃPŠtą Phžŋ%4õXzi ‡Ð<HĖ4ņŠXĐ[„þĻko€EƒðŪ5-g\öüŒn4‰cščÜ­„īĘÁĸ16äOšōɌ“ks^Ō[<}Úl>ųĪ~'™>ƒīuÅPĸs&þéŸxsČÂ$�ڀũQĐ/ōē‹™r‘\ q”ÂG$.LÖfXŌØ3<wŧ!˜·[J‘ā;ígþmĸj1M—`ķ‘Ä›E-‘ÍmÍ,ŸØÍãFQ§°Ŋ€JŦāú!Ïgė„ėWö™+}õB’>öģ2ŨŊškë°Ðūś)9W1ļΉ‚žUÁØÄÞMģúŊëëķ'ĐÞļúŌvŨõ™ëóퟷ7Ú+Ûãšąë™Ë·ŦĪ]ŋyOåwęyõ |ŲËŨķ'ĩïš ŨghgitGŠõï ӝ[å·ĐŸ9æŦĮ/_~"Ŧ†ßΚČ`$i뇁6Eč\âíäĮŧ0&ģ‡_<ļqiü”‰‹ÁŨq}ÚEÖĶ�>TÍ‚MmšNđ'ïrą%;ŨlIŋíó“ã=ÎĀú85m†7Ãũ›mĒūĩ/+ū/“ũVdJŅĄåK―ÅŨ†ˆþ€‘hÖ)~ßÕ ę-ïšQp”Ģ=ŧąŊf \Tƒž@pÄ…pcT€Ų”åĪXü0w ŠāíÖĮUA6 ™Ŋ…1aĘĀ€Gó=ÖĀŋļĘÍø†;ÝĶauÏ/ĮðĮ}™Ú―Ķ[;°_#ƒ˜ÏhøøãĶųOˆx7ó…°SdšÍI~f2TĘēj0[zᇯ‚ŋ<ļn+ Ž øŨ·WbÓãÉī™a8qĩOQdËß@Ú(QiáîV ÏI=sā{ŋũ9%ņ|{Š―ÜD_=ī›WNŸčA~ČIÔJŒqömĸ5ĉũRĻŪĖobžI>åˆÖĪheDÎÕęĸʇÚpMędŧ§Ū]'žõ\„Â1tk-þ3†A`MĄËę·‹å=Þ"4nā Ī4r0Lĸ/c‡ĸÎ@ÉJęJ>“jYõĮĖ€‘ē=ė°’.ŸýY 04ŪÍ&ņ!6š@"ÉnũĘŠĐ+ßĀyû�ô‹_§ą[ˆåzqy'OÐÓŲĶ}Cĩ%.C‘o<°%§m+‚•ĩ€Faý:“ĸ;ąøV~=Ïîi'Ūu#€Žo—ÉŌëÅēâÜÏĄG–’ÁĘĢDJæ˜QËų-_ļŦž‰?Úæ:el�ũ7QÉÁˆz-]Cnģ€õôYƒH‘d­veŪ|Sg[]ôųŌĀņļ>76›i9S~ae3 ,{V9ŠQŧWĢ~›Ų8ÞūĐëBOb75ØĘžÂGE!3)_ņķ„L‰G_ë迃f OœWQ$°I"rėv‡M!\'8Ú3w†$Bà öĶĢ9ĀįXiróó_Č8.XŅuŊĸCÞüp;ûŸC<ðĐ?j"ÎÍtšc|ĄåÞ°n‘IcyAÂWüó%ŧāĘëĩŸ#nĶU&AIu ‡ð�šO˜xCž \xŲĒEé^üđ‚­úĒ‚J]=íōĒĪð€”�ÜðČņ›rķ” �ÛÖÏĸ/ԎIØ54Įf|9‘žÜN―ĢsâĶĢÖ*Ŧ đ u�yÆŌ$ī”uzw)7EuVĪËŎ}ēÄ(Ũžō­juØÄ3eój•Ûėƒ‡›ŊuåþŦä1ĸzPzĘZū.† dd°ģ„­žHÚ„9�æ( 휨N‰^SSå{‘—Ũ4ß<ë!†ŸĄdøÓŧ45<Ö§vËÕyû[!°\ëéd†QÏ oKOą"ĘŽĮOT=þēðđ–ĮLšŸŦ€č‰žýÃŋS!įÜWꙃčDaÁĀ♇{ƒ†,ūDūÂG*9uinŌTŌg()§ž+n]æiĒïžþ­ ĘĻ1ö–tx+ZŽģ€―­ØņmWåە—/Oš}âYŅRÓĸ)‹äé·QÁJ?Ö™ōįf?o0Tƈ[ÅKŒLŧ…ašŠâ―‹žë%ØšwãA „ĸ"ĪRð։ŽĮG™‹ė.Ü@Ó|þpæŋÉ+‹*0efn ™Ų·Ķw‡0jâ‹ðaų4ĐВ<‘8‰l_‡Œ˜q­Þ`Ņ―mVv[*íP˜ÄŊ;Ýz�û„5"_uų/’ĩöïÖŌrų†wĪĮƒíVgâqPĨäh9ôėĒ�’Žð3mA;ü+q~íŨĶs˜raŪĄSîēGØYúLwRŸ–NFík.ŲĸYQF…ŠĩKņÕđÁKŠe>(]éWedŽŸ6ØeĩĻŠÓt―ÅĢð­ËTTŅĶÂŅ�hĶ’Ķžę›4ēô4̘f›ĩß\/XÃ,y—7"ÁãíŪ;œšíĖņŊ^YĶ/Q xō]oÓåíCąÁĶóėÆĘIĶB°‚ĪƒŸW“1MŸ<I<ÃrRÂ;đøUĐÄÉö‘ÏQqF3Ŧ°þöȩЕŨz9š”ëP˜@ŠEû+eIP>‰aÆßĸ�…Ą0IĒïôÝĻÃ2ļY2ŽqÜ?ĪĪo0I_ũüîÏŪ?—aUâîę;ĸjĒ9O`ŦÂ^~ -âÐÞE‰įõ‡Ög˜~xõib‡}ÎmPȁ:3T6Z@XĘŽÜÛ§…š?ĻĒYúé+{́åšĖÜ#pĮ éŠgä §žĨ^Ëógģþ=―ŰT:Š ]Ó Þ‚5â,sĶ1ēĩk5đäzųĒ}Ōžšg†Ú�ÍĐŦ0Ŧ+Roü­%˰zSՊĀ–>Ðxîj/ÜíšV`ĒīŸ úYb°xįWgŠiĮÛnų[(�ā4ï„MC'\?YVúí*sڋqUðĖ]ĘãAÃuÉ|_DÆÁ ïx‡·„z?ÄvîŽĀŨån/V]ũ4ĸ%―HCÉpÕTķSŲ9Ŧ}5. A* ĒđĐNØōânpáĻeĒ"ĻSģūqgcÖ­°ˆFtđrJœĸ7ÔCkUx/mÕë+Gr~ĩĀŨä§AųÄ|ļ�˜ųþM͉Ɯpd[ôĄÕŽ‚ļ`UÁóŒ‹=üMˆŠŲ°›ÃĄšt„ ՚ÔīVT”­+åĸlšþA‘ôøĒ`Ģ pÞöĸϜP4ÆĶŒ€áģq óĢāî#n īūČïØĩû3$ ;,ËN2„ņw+0ēģņƒE…QĶė<�‚Ĩ‘ g)âXƒ@gAG\ÛÔ^Ū} „Q…o##zÜ0igĒæÃ�^ļyðWõíąnŽ ˊKYg'ĨTßü Ē •kDL›4 ōS”ÎĶų=•EōĨˆä†Ų)š•(B�sÎ\&NÉšûyÓéýdGāEīĀj?pÏČŋžgÃđí$ĨŌĐŪĀŦˆ– däĄŸ3ļjŲŌU—ÂęjŊÐĖŦ CB#l’ÉX Ž€&jķ–MÔ+ŽU=ČkaģÏp-üč7į2ú'ŽjäßũqÍ8·wzØÞÜÉ‹ŠŋNÜ;8ėýË;LߜGŊœü=!lŧ…yxĘkoZŅôÓ?›b<ķųVãųEq„ÁF· ]aŽi(b”`ÚđĻsąb;#ī6Ä'ÓÂ@xãáMŦ`ÃĢēŌ@°đ{EÂ#ëzK(ē oÔSž'ú-7Q—L ;g~(aē8FMEVŪ1ƒË<Y”éāe͐9ŧ)ˆœ‰Óđ(ø™KĀÓ%eÁbō@ĘÏ s23=ŧVâŧžĢ^ÁÔ_ĪPåŋÁI|tÞ6“N0Ļļs97ēņōSIÍ,nįŦ Äļ…ÄOlGia0j&ƒ(Ÿcâ"ÎUlcáquJPĸDŪkéJ&ÖļĘü,c@lĻļ­ å7"Įæķ!ĖvŅPÃaŌíü•„áü%õ’ĐmLð”ŲHíXËͧ~F§"ĀšxÆŧŲ ęøēˆ+ČŌ―f�/‹ĪąĖ‚sbw Ļf?œčlv)‰œę·šØä EoĩÞ4ĸ*đ­Á Q ]ēŋ'šúW.ėžËw> Ķģĸ@�‚=ÅqJŌ›įķõ‰ˆĨo‰ģ‹ûïŌ("`ļ‘36EŒŅÜ:ÚexĒ*Ōp�}ûķ pĪ—~YCY- îwV{Ŋpæ22O�uâŊ’LâåāGČ+Â`Vē$ÏNe7đėDZ”ï†� 'ąĪ(88ÔÖÚ%ë§9ęļxk|<+–v›uĻÐ ^){Ĩ(t–A·óQLv ýÖ-OⰉ•éƒøAå øðUõ;y*ŨÐÃPQZ—ÓœMŅcHĀ}ãCSžĢ6å�čĸāޜ€ų:ô!Dš kļ|šĖxb`m&ðLÄN0_ wgMāSæ†Mv‘õ6ˆŊęÁaņ>'Ü`ĀĶacčŊ/3'°Pø―úÍĪč ,Fëõd3ŦOýŠ)|1Á݃ŋ†ķÏ<‰7A‰$ėÜíZNaPļÂôõĒĢJžóó?Đ <į:m­ŦīČtÉČŋYƒ…ōĻ­T=Ģâ1óqm(šīü–$f1ÞSzÖiéÞø‡ŌFb<&όē+ÏÆüå―å &°ģ-ču"ó‡0#ósvė~wtŒk”Ī’P>'ˆåՆSBũÅ7œōí !mR~Ôĸa1ēŪ”mvð),Ҝƒ Mŋ7[�ūymPL"-ĩČ2›°ï­‰“ŌK&pė1L*kŲ“ĸ!Åh.OĻ*§œ%pz§í]ãX^ĪĨsÛVgMœÓĄ”s†,ýĻ›šÁ Æę;ï·ĻgŲÖĢt&Ė–@h{ļh|#Įtô3ZŅ5ŪųK�%FKČŽÅæ•ū„ ũoÁ�8SęâEkėžgf ú '!ĻüŽĸ2ËT}öi?8vđ„|ß1tVFđYũĘŅa–L2ļäIŒq‚ęĮ·7Ķð<Â:š0ö ôÔÁa€|ÚïÚŲšNQą<ļ·{Mc.OĪžk·‡•\Ÿg"$)ðEúšxõtÏëĒ nōJgc\Qln2e\6Ģ�Ú#R1˜ Vl.Š&IãÏËĨNmâ;j9~Ņ”‹Į,(ŽBÃ{ž„ĩĖ\Ģ5€Ģi1k�”· øė^dýÖ…ę%Fô^šPƒ;6Ú§АËŧ͈‚Ŧ;įþė\DŲ}9ÔQuĒšSĮ°íëōš2ÔýxMķg*íiRDŒæs%ÔåðÏšķ ÝąūnŋOęøĩxpÐŊß){;sAd>6ĩ”šRiŲ€‚üæ‰aš—:LBĐ{ß§NAąmųkāŅҰ+" )Z’*6ócÛ\ū}îﯕ­ĮEsoݰÆ/ĮlâKįƒiĶĀýtIZKÞ[ûkĪ`cŅNĄ<—(°ÁHD߈/5v"ī™ŧÅĮ!\ÎėulƒļƒÆš!ēFĘqÜãōQōœš„Ģ‘Î“zĻQîy@FÜŨĢßRŸN{ĀH~ĨÁ3ĮAČ#e ,ČåÉú/ƒ Đ įļÏšˆ„kíG;ÛýËCŽ‚NĘģ|üŽ–—CCŒ`n!ų诐0‚IĄïu°:ėm~šĻģ―7ĩBdØąJšö/yk Īčˆë I`Ÿp=”įņôÃëä~āėnŨĻŌĀ*ėŋƒ=8xú25DŪÕŨOËŅô9 ædZ*Ą™k|ĻL`fHÉ2<ÏSdžî IÄĒyøB™ųz `ĸH‚ô$ß_›NÏebK.˰ævŋõÐ:ÓĪFÛ1lDïē‹ëk ?ÆŽÞ ĐXņ―ežGÄÎĻ0%BBĀ“Üį [NDŠŅĒvŸĐÁõ˜%‚äÉþ%?^UÞPnәk‡oHĻ\™ÖU0kqĐ2æøxąX Ąbá•Ū*Ų“&­õņ`uîazz„ï‚ÎLEĮVĮöðôŨŪ ŧIßa(JČ(n– ē ÆyŠhš}<šúÛĨT~HĀ ž–n> 8Å}]‚ ŅÓ~á{/OjEßA|˜qŸõ|ĻwLĘīÎï- u#›āŦáŽĸ ™k<‹ŅK|—VЋõ& Ņá|Ž=ĄŽGžAôc’õU’VG„VĶBWš‰1-~|„]ʉMŒlŦSųĪĪ’Ti†ląœeHš ôI…4$/Ę1 éåÔ;ĨÄ"Ņß</ãPÏģôþLĩ™äÛnģ+Ä1éڇ;€åüӕį%DŠÏCfņxlĄ…žïc &žĖīĢ‚qVøu/ŋũ①č—Aįė]ZŅiZčķ ÅåÉĶĩEŲį.ϰÍu ZÏUÞĐ;O ‚^0Ŋ`Č.?(‡ŧũūųŽė@ģD.+Åļš„‰ŋ ”øŋą:ĸ96kbï)s6ã“Ý]’˜RÏøðBt%MG`ū―Ļ֘UĐŦ> OpĨ/ h —[)ˆl‹cåŪeŅu!z,æ“6.5KŲ&‹-―óvĩ+ 0Ŋmj°1üĶJûœ‹XÞÏÄ0’x N<uN.‚ĐŲ2BUÝHaUŽūãŽÆ[î5ÍÅX}û·"ĻúęÕ§Įþŋ^&žŸ°xíÍ?ö rcøÍH)܄4ėîŪ-Ä|:pųæ F,ó*( dpŽ'Û·ž–Ö°4"•툍ķm 0%Ų…°ï8š[=!ę$JV-)/č”9G9TŦ)ŽnGĐÖü>š.K[ĢėlHÕC”/JopNHLNóÂså–Pæđ⨖”ÜÆ  0ÄyËâėôģ<EMrl6Hþz ûÚ[ēûð…ÛÓjŋų_ú•Ņm:•°áŊöĐü·hf9tjk” „r·ņÃĢ+ūü뇷‹…īPéė…ũäÂԙÁqËđh=DČwĸJÞþæ˜<`žpÃRóÃÔx؏“/ĒÎ8™@ĘÞ|z>ø‘…íHq8 AĢ6Ođāɘ<‘NAgeôōĐGM3ĢTZ ŧ0Ņũ6ë\øwĪ t‘õ€†ĖČšü.šđÅ:\ē’C8a{dXoZ˜1ÁÝķ—ũĢ9vŊrjŅĄīTRdQ@âÐĮ* ėóĀ/@þ$šžū/wž_К/Š$U’āÛČ\QX ![âĐ.€Â=0lL„ Ū [ž*īũ\GÚĶcĖBÆāßHzZ)vĩMmÎrÚ,kDvP-CąFüų,ÞĒlÎņ‘îÔYޑžrĨ‰Ï–1„öɀĢ j2õÄŌ‘ÃÖŋöĖzT‹ëpŅ·ß…ĖLïŨÓÐĢ\HMÄ―Áoę\quīó3PTicpļÏd'e:˜XŦĩ/…õÜŋzÕĖBu1ĨpšŋmäēęÚĮÞ/Ø14–ØąVēęωMŨÁևZ~Øój![jw‹h:…’‰D<{šāKQE, š/Iéh6.N!°šĘ›õlƒž4A·ð&C[Ĩû8+Q’ÂNI_JÔØbë·a·ņ=ĶEĨÐû4á0ÂEg!†ģŊü'‰€ð†UóPĖÁەNŌFAGSÎÂĸ]Œ\)ŽÅüIũXâþķj/ų a %öî`<—Õj‚Z§$’I$ŧ™c-’›Y•ũ'<Ō|p}ŽØ:ųö%pÐŨęļl\ˆÃŦÜõ„ĸHžAŦ]/ÛY~}ūŨÁɈúáät‘ŋWŨ5ålk?noÁČíSî)šf§Gķ[û§‹;ðųÏžŠüð!ðjú…ø ąžÝõōĮŽÔfŽŨRĖk )Z―J%éëéXßgŠ ųŽzN0}‹ĩ§ė{^ŪĮž@”VA)<u]{†xŠō‚fŪū„KxÂ6|nÎÖĮe9$―fôëŌðeÃĸRŊUķŠ‹íßōÆóƒÂxøí33üųšg˜™ķŅk– b<Hö 6ō…ņO†äpĮ1üäījSgØô{saĐVŦ^a·.rļ­rŽ}]ÂxdLļÄe{ūéKŧšøpYI~łáęWOßÃjKĩ|ęē˜Fy>YÖ ĐdŸĢ0!•Š*Ą„]N6ĸĸLŽ1ęī~pq'•NĨiīŠ]ÂEÉā%`5Ųqž(°ļ,đåđՒąD/SīÚĨø4ÜÍĘڗ {A+ĘÎ?DĢāØ*JQEŦ%j“ĸ+—ôĄöĮĸ}úQ4ápQ`C–XéÛ·Å.ĒwÍŲ]˜Ïí`<†xyä=6Tųb^KĪ-īķ˟B+#”Ó†FėKs&LĪĢÖĩ/•Ė·Ŧ°S5ûž ›&ŅŅ<ĸÕ-Æ&ē&ĖKl‡Ģku1 ĢuđédŲlčB§ĖŪā%Jsę:ėākY,é‰UÆI†GP6Ųæ2·•-]Û7ĢÞ'ĩ(-5Ōģ>HŽÏpßzŦÓn›ŅJ)ÞqÚo ƒ!fL9~Hˆę?ãl ŲãĒI[äĢ)äZšƒˆâocÉqũԟPá_d›q.SCķ]݇*m[ËL�歔ŨuuŧĪüĮåyáØ8 M1Kž“ÛÄÅaœ~3œĻĻ–\ŧŽ3ŠÂ‡ExFũŨ#ģú4až5Æ-ŲĐ09O‰;?2ČĘĪĻb"-I|ë*VB e.þžœœu~TæaĶ‚}kŌ–#ķE ŽAozôT­ÍmjŒŨ§“É2XEÜ}Õ―›.ŅĖÆãD/DDÞóŨŧkuĸSų_‹JÝ)zŨÆB(Ārû^EÅkoüôcfŪ‡ ­T!‹öƒfIþ>ð&ä(—ÐÜ!ü.Yw;v·Į‡…9ŲČąéLâ#vÂ(óq’Ų’ŅjIāëOę‰x傄UL‡ÍČC’[hh[BīA@ãeĘÐbÝšjHS=jĩŌöIčėņdö鞝 8ÕŪ@ŋSķŦtđ F}ŸPŦœJí`m:ĒÞn—JÛ9Ęū'€ČēFõęD-HÍgÏ ð’šĶŧÃ(ZRýA%XĒŅ‚5›Æ“L[ēŦUë$žĖjefeYwXI˔ķökŋ-Üg9Hod{€ŋ %Šö.B(āĩN†Uå\CUZ‰-.ó›WŸF>;HEķq… |—éöĶï&ĶŌâēļ…ûFß0ķ‹ĸ|iÃhø?óûøíþ!sh_GЃ™―ßĀH€―NĘA$é§ÏH“+Ŋ›aÓāˆđ�]ü>oÁ[Ɏ”0ÓÜY•Mȇ€§‹―Ĩv’É^žIūĀ5Ä(CŌ”Ŧ°‡I˜ ŋŽŪŠYã%”ö%OĪ þhíTfŸ8ņÄ-ČĘBAë ™ókĐÔā5 ͰБ}9MžÃá VXŌ§EŸ?žáXœJC2Š5RI ýëE˜aļŽ0üæ~S(ý"LoÂâŲuūï,ūAwīQFŽšGÄg4F°JÏöÝh3}i_FۚÄÃļęv5ā�ŒL›Āļ7ĪĢb5Ą;‰Øk€úΐ Œ0G=Þ'H[oWŠ5tŧÆĒ]Æ@0-fŽåýÅHÂÖ?Ð#ĨKTÁ~ŊП ”;,ų’·â]$Ē?/[+- ŋ�đ1ļÄÔ R‹ ZņÞ.ÚŪĄŦvŠ%dŋ)Z§­7jŸÅĻį"s %ųpČ#jį�Ú+Ԙô Æwæ2d‘ĮMu2―@^’ƒŊĮó+ ‰Œ?åc“HŅŨqÍĀč/‘.w� ũ(ęgnÞūþ,ëeLŽ#w‹­kW hË ][XÝÕoNå:ģ:pÉ}æá^ņŸ€ˆ–ĖʋTË(ëdIApŠÕ™°�֌HRø1“ƒ=nī–^o—"V:Ðđ…ËLÆ­;pWA ĸJŠÂ[6Py `Ŋ{Z‡îÖŧNõ—/-ŸPGtČ䧜-$Ķ†ĢšôTĢo›E€ß›“”V–F=Â,zD3t0öcߓĻe$œį9Îa?"øy `-,R―GĘœæeÆeŦžÖO†°\Á—y�éb ™U4tËĐD§0›œîvņčåÃÆPšyr Ųm95†j‹NÉB _―Ŋk æĢAĶÂ)öï25ð Ü ÖE1nn•ŧĀÓ@‹,Ø―ÐāHeD·Å[ŸĀäü œ“3― ‘.Ā֔ķlžŠÓē{�‚^Ô@}ЈõIB&{D> ūؔPÜGú7݅> 4ĮÖVd–öTî2Ę|ŠĄąpƒ0ąˆV/nƒGŲĀGšAh“#p%ī'tĪZē: þAČpK9qÞtú‰˜ØŲW5å$=„ÛZRl·m9îŪPÃņzF$˜WžžđÂĸ)ó(HĖËŋŸ5o(Č"@~ĒüÖú5ÖG;t]’šÂō`{ĐÏô�j―濨•ó•ÍxČô Įos Û(ŦAÍdÐæŽ1ŸuLŲ‚" L�ˆĖé)>sÔ(–Je t/1ÔÖyõd/āö‹Õš8Îrņh2oþ/<?åØQ•=+ïî=á$z‹ĄÕø—Î.eäË+ýÄ-(Dø~vnÝų+út­)›|°'ŧ -ð=ûÂââO™-§9[ ―8ôŠ}>z“mÃŅiņī­Óózų(ĩ―"ĒÐ(ëĻ3—<Ąš> DFMvóRō‰^Fp*}l$F&šŋ•ŠË2(†nvIF’iml†™ĢLŲTÎ"GjÚ]Į}M{ŠÁSˆƒJąīwĸVnþ|zķÏŲ”Ū^_ĩlYg ŦÐX�ÅdDÁėRīŅ „öžĐŅÜéÝ­ø‚Ýš‡áÖa$DŽblaqu{D;I7UFĖÁA˕î·ÔúP�ēû7}eZ—­mÚ_ bÜŪøÍ­v0Í4É*―qÐķ•ęÝķC,"ŲÛô€―ž°špčã#Ũ§Ņ€:Õ;DžDĸģÝqX%‰BäŊü NF†qDxJ‚Å20Ú./0åƒŲvúÅCģ^!Í#ĢÝžÜûÃįbŽĄkų݆"˜;åH„ŅųAŽzn<üJåt0õeĻīcÔrŽ+\:yÄ_ØuÎj7þŸ;ÆwNmø ,Ž`mz”s^ ?-ƒ†GRėą―p†$t‡„†ZŽÝÏ‹ĐŠ ,:CN/€0–ÚP”dãŽhŊæ-–ųČ đÝ·—žýtýWÎ(g<æ/ŠÄ3°6Ïā'_ŦŊņ ˘‰ É)â,Œ›ôDdÉ�—:"‚Ôđߕï,.‚ōÖRæŌíūũĀ'ŽP`Ęq―vIzŨtl^IRŋEô7ūŧ–ë5zŧ―!AĻŌrIψ‰ GNˆÂĀ Û:Bā3ĪdŒSō íw�GĪ)?iÝÁpïÉäíÅüøþi4zÛi°°ë!ģ‚hsýqTČlåXH@æx7`ũÐÜĩ“PŨ1}`ė h˜ĢĨþ”ßâÁxĮ�éļëØĒŠ M" ,ähī‚ ˜þW<^æŠâ̐ŽNN{M|[)„}ĨėļōwLF ß ‚]`Ļōī/đ D'ø&å/!ÎęōÛ(Ũ(þėK­%if\f{ūtų_Ļ bō0Ļņ —?^ýĘ\€9V SŽ?ØĪÂv+EÝ[ŧbjų;Óđ"S֍z.ā—núšÍų‹X”SÃz?eYʍˆsaÍ&ã[Ŋ}ÄôUŦ&ōuēˆÕŸW`đę‡ Þ€ĶÆēÐûW°­[köĶaģܕ™ÄĀĀâ!û‰šĘD�Ų9?}&órÍ]öaâ @qųŦúƜ<Ε둗ŠÓĨŸšŠ(1åHŒí|‰þ9‚õýļsģïŸßVÛQ+%˜.ęr<â”J^Ž)ĢÔWY@snĩÅwõØ5:―·Ō4f|ûM Ņ8[2Ø üD+ĘVDãá° ØˆČėĒÜĒMTIýŨó–ŠŸÓ=焯72 ĸy‡cÚ!YØ—ĐžpCÃZ`Ū3hēœŧį›dƕč†Ŧé ôû|$§ó”ä%Zŧ„ÉXÐ^š3yōâ7O^Ÿë4;KČæĢ)ókPÂ]Ūî­Í-‹#x暉5mQÖIlūÂūRā)ö݃Ze1ðČh‡“ÛwLüžØxŅlt·nÂ―+ũū…VÉėØÄÃåU Ũ`OTÚyG,/é(SI{U-―O–ũs8rƒówIäĢgMÄjBy}ʀ‚#DŽôÏ'âŽģj•‚ž pĪ·ŽŠĖQ\žĘ8PÞ|_?ķ6 ‹ũHjFÚ=ĖÁ/QÓĸhŨt ^^”zópüī§d, ÅÁZĪQĸ äûŒGdWãĢ?đĻÞĻâ—ä/”/ôŽ %…ģ$vŽ}ÕŪYéÛo@GÔÎ^ūþ|,ĐGų{’/iŋĩR?SˆČXzœæģ…“°|˂~NðÕxfýā’ïëv"ĢGݟ9H(ää€ó(Ý*n. ЉīwĐũZ%—ÆŲ7ó …Ą ķŨŌF‘>ÚÎdRšį·SÆÁŦŲŌžžbíRÏ ĄÛ|á迁Ķ !‚Ę :ðú8K$ĐâLÆwðĢcæHîßžŲ †ŒÚã§GXŨ@ūáZ4ĄSZR”O,ÓEČ'W “KwÏ=Z—ãÖ˅$–QįŨ(aãjíĸũ…4dëþŧ/Ķ5~ļXþņFgYøŸúUÞŠXæā_*YhóËp†·Ĩ—ÔۜĄ1ŪqæęTéɃ<`š$CÛ;“ÖžĖø4ĢūeŨšąÅuؐ,é·1ØĪøđVžS'~`]ˆZ™ÔF[J\Ÿƒųė†%_{TõíĶÄGm‡‰đÖ}ņ-ŲžýÜÉ„ Ų‰ĨÚB*äąÄ’ķˆëæYį+[ėš,?ž@Éûœå uĸė&j�ŦaąYäõíršmRæÖó& —*·@š-_yú0pWš|ŧPøjîTƚ& ž1ˆæ­)ˆvėh=Y4ķؚUĐŒ  ŸŲ:j&LÚXŅøéęÐđ~žïŸ Ļ%€kŸh–GÁrqM)ûōpÏĒŽ„*_üŽŽJ‰O1$>Éą”q/Ŋýk“ņ2‘žÃĒ,…Øģ႐hMô^zK<ÄDÄ= ēĀCĻ=lāëÚSe7å―ÐuúúGŸ;5HßÓį0ƒfÜāēųUĀ-Cä0û[0“LDV _<:·Ž*6æe#Ėōfųr3Aé'š”[ųųJ‡ŸĮ&ø4x°šÁ―ŽVđtŽmtÔÁä~W‡› +īxôQŊ˜|bQEvH6 ī:·Í9á}K‡ 1ŧ §Ös!ķąû’ Ô*‹ŽXHâ ũĶ‘bxs0ČËú%õ••GÛi\ō>—žŦÂmQ:r§ ·đũ#ķkFĻŋah åk@hoģ:†elŌnū aUðkoĨž‡ úžR?đvśCļŪnE~ÔÝLĄöäĩģRĶažʎqõ9ĒMxžąR=0ĮįE‰Fá§šFK(>1ϘL‡„ÉŪąø\Čl“ßJ’U]éGïu*ú#=ęb;k:aϋ4’ēVõ‘ këÕxH‰–žGÆ4ūE)H‰ŠĄƒęfŋÝčŸ ƒö‚ãã!šRŒ‚4‰k$,C‹)3ĮYSbžė)Í<ëÜ3›SßDËø)lK5ö=vÉÖSY6âĒzÜoUg<Bō‰ŠŪąÏ-5ÂkÅŌŽMđóŽŠoŦNË—Ę øX SîĨ O þMž`„ÓeHÅLþøIšՏOÆíŽŅĐÍ5Ļüô6.%ÆUĸKtDgý8ia|ÝàSsŨŨΘ'›ÏbiaVŸŧl=ŸA­gR4Ðņ†Ģąž”ŌĶūŠčqÓÃÁ †‰NLØS#)ĘUʑzŲ:|r_Kû„-\Ĩ)uLDp-RoČzJÎÃÄÚĨæäcHčĘ�yŠM˜Āœô(—ԏ•Šņc$레‡·•8xíĻ#ĮwEd\Ņīę@M;„ãýIWð…r;ŠÁJ/W`ņ nĮkëĪTđ3냚íÂmōįA.e—tģú”D\eŌÓ~ÝL{Ķ<bhĖ_ĩÔÐx‡6Þĸ0V§û?ÏÞ§(Ÿõđ!Qi1ÍņŋyÃ~'æË]Ö;}\Ԃæ1L'øöxbÞJ[�r0Ļ́�ÃĻVķ&æj:ßÕ2ž°–Æ(ëž\?sl„ÝîvõuË'čŅ–ŦĀ7eoM‹"[”ŠUī&æƒáŨ~ÂüDƒŽ˜ģčSIÝŠčŌ™ÁK'/ŲÍ_ķéĀíŅ—ģŌ%|*ÝųOÓĨzŨ―ŠUb=―iį}Q;ønĀžžĻĶz°'ˆžĢĀšĨG(>Æz|† ˆųËŌ8óCĐáSVKëŧ"_ˆé^uœP(4ö  2ÞĨ%ՒōE~ņ8ß$<€€ŌJä(„dô.ƒ‘Į=•:›iÍĀFô„ēlTäôýĄZÐĨØ�:>ŧHôWë"4NŠ?fßl_ƒ“þÜ$qkˆB™MH7ŋü,}Įä/ŅV4MôdáčÛ⃐~%ßЁ;ð/€õĶh "•ü,õœÝÎÕ%1ŊvP.ÓJ ÞáwVrP?˜ƒ/†ŪqØÚQāó5ŧ[?žŽžƒĪ-Âd7ąÖs\ 8Ahę2Ė―Ž…ČmÄĻË ÓäŲ#ˆŪ"ßkЙĻō/ŋð†=‡ŒĀå/*úEŊ™į—Ëyęōųš\'^ŲMˆŸîiR/ÆÞ sŊ‘•Y-Æ^ŒÃ~˜ķ™ÃręÐú,X•jâ\!N,>I[CŦY5öü0™† ĶcҟoæĘø@ØŠ˜Ų"튚€šWø›‡—‡ĪÛį7H&šlI\T”z°VĒY}ŋŦöļĀ=―^­oģVŠ ŋĨĶĶ} Đ'ĪŅ―aēHÐmͰâb"%ęt:—å#ˆRõm߄æÝYDŅi"ĻJčw‰{ ôĘY!}"dzÕîe "ôAšœÖӠϙ|šúáhãc d‘åę‘CæÉü:‡"%h åÁeGþÕí^ß™ß0žßéģŽ„RáÄj7Îęj^šĀ€�Ā�€Ā�€€€Ā@€€€Ā�€�ÔʋÍđŠéEŽģÔvÃäÝð|?FßŌ@ĸ ŌÕŲ#uӗ-ÏÉĢvũYøN‘DÆÆ5X*Ũåjŋ’}<OåWąŽ?j> –ƒš!þÏ01Ņ4ãÛ?ÆÅ øyUŦ‘+Nã ü r°”å ōa&˧—‘ĻdĶ.î~%Č,ã|aT&KyW„ï)UDoØŨaé7|ĩýNē˜mL4õf‰ •Ī˜ aâiĀPléö•B|q·šËá%f9ŧGŽúý…=ūœ ‰’ZÆŦmüąvįt"ЋPr|2AZ~Đ/žĐŨÆÖĢeÍNxjg25ÅWÔ 'ûƒ ę•%ú5`^• ÞŦábĨՃ$Āˈâ'l„péšxÂĐ ™."úŲÖWe”“SFœ^ē’Ėí“JĒ[·üÔÏ[ÃÂïlĐĶ=ĐWãë”B wŽYŠ]Č(Åĸ|iCÔéw’]Ôr-ŠA7nŗ{$ÃzãįĩŽ‹w‘~ŨįŪe�<‘óßæĒŧļˆ(v!íųŨŠd9ÁĖĪ”S‰ÝC{ÔZþ―™Ï\KW7ħ—gD3ÚŅNįÖĖž'ĶóürDĸ/|æJžčŧĩ-Sū·ßģ肒ųöCWÉåI'īČ+óãQfÃóty‰ČíņÄōØn.DtCĢUŽÍ Yɍþ!ŽÆyJrå)Ũ}<æ–ÉrŦ Pč-m>#D~ú­ Ï@6ßļ—;ō8ß―ĘŊéÜļÜÔķķ3ŋóCÞaP4,ÁÏCmĀĻA4Óī  Ä>f]įūLà Ē@ņļ…ĐóÆ BJ0Ūúæ9�<–ŊßLs„ͰôäŽ[ŅRėžksÞîäĻRZ œ$Ճl`°ŋĨ4ōĢ?Žëv"rhĪt 9X>Ж`Ģ’!Š-Q.”Ē*{%†d H‰ĢYp\Ē ŊŪ8}›Ÿ{ÆSÄЎķ B›JC*š §Þ~―0ŊdB–á·įÉ-ŧųė(aÆ)įSžïÜϖÝ_�ÖF…|7ųW Ÿ”Ųˆ– €ĻiŸóķž1éogjúYEwŊwŒĮC ü– TâXđŸĮSdŧ)Y˜úŠOđŒĻĪ-CW3jg(>iIÍį,“ÚįSvÅz)o[ ôéyJgŠũÁÜŨr^Af6� šøõG8dĄĄÍ­-™üĶ|Žþ|ŠizũLĢš| ,••Ä�Ŋ’Nx .›ģtã‹ģüy‡i‘·;ĀĶΜƁiBč°þí•æ9Čc†"kÕÎĀ#~ÓlSTt’|#ęęÉlF‚øãœÆ/›ŅŽBYåïĢ9ÛaŊ/ąäĮïßīJ§CšŦÕ}|ÝÓ6UĩUP_˜YįĸAæ�X.A6z+g%éœvl0e A<€E'Ðl“(,nŽā>Å0oWĸbgŧKNï5$Ģï―ČÏ!FļŠ›•šÛšôŅO°ņŋé‰6é)ã_ÔaÝpįFŽ<˜ÃOØýū)ėßãøēÓ4Ÿî ôx]Ņ _^ŋģMē*Uڜð>ę–ĒDËCå+Į0 lÄ%\ũ“Øu);b%Æ(2fŲuÂÜ-ëČ\Ã|Pŧ:ëÔŪôSÁû]HÃōįđ§Ė‚Z[ßt™,Ú/9Wáń&ĮäH, ҇ Ih€wYõ„IÖĘ }úÅČ2CL5ē‚Z]“5w3“ô0üŠÚ€˜!ĸnþßJ‡Ð�1Î?IƒĄŊΚæ.{í38sšŨ–ļ&Tģa–zýÜPA`ÏKowð·ýõ7ø}ɊOpŽ`rŪjĨŌØīŧ@ĖkĘÅwU“0!‚šÅ‰@•ÚąËQÍG8Ghϊwîļ%i>ĘĶķÏdĸkÕ ‚W -č·“)D<°‰ûĮfp‘ÅwmGĮóé…Dĸg9Úéįo™+‡æ‰QčBcȜÖýÄų û˰Ü" ”Í>yÅf2[d˜•Ę8}ō|y*jlFôÝÖÉ>‘ˆáāÚÄŲ9‚oČąQ6Ÿ+pl5õq}1YÄĪæÄIWO ^Ī^ic_c―/,|ŠŨŨýg)՟Îgēņ° iĸ&ā‡Š3aÄqe9/åO.Ö„ãëpÏÔēĻ8ĸœC]%m—’ €ÓTĨŠ%îv­T-[‚Ó_šĶÂnÕ^BÆ'^ū KĒļÉ Ýˆ5Ž—Ķ>Ā^ÐÄAßÓF736TPŨ}PŽéuĢ"jDøÄåö>…4Iâü‹�}eÛĮ―đ�t—ķûD–øļæúKņĪĻ—›Žýƒ[Æ45뚆fg`DåÞo1…lßųō0Ėä4$-›ú!―ޑ%9Æ–‚Vâ!·ũŧØ-FfO /_‚/Îð$û:§“åG] ÉĶļ ;―ӋXeD%!ĻÛņ―ĘŽ&81Ņ_Ÿî·ČČÄīå(Ëôm\cËwöÆ7ųË3ˆŅÞNžGޟ=Qx*Gō!ÚӒę,ï:YāÄE9-―Ԁ$·ü`5ðjŨ“kØŦų#57ýq�söA@W’ė[xóxūŠē―‰„Qũև4q™ á·ĀV fNæJŽšF‹­Zŧ4Kúd‰ųÅ@<―ĖŨžnđ"fâWdb7îÝ;?Ā›āx åĩ:S õęBĢzB§ā(đnü–Ũ3ļw–ņ~̙ȑÜļCÛķm-ņisâŌ2Ä;‹rôfôĨ/_/“ęÍũ`­{!ņ„ŊũĀ ž› Îërïm&ŋŒŒņBWŪĢČĻI{t#mųáŨņšéÔVĘĒúæ^Ū@BāOːkðÕīnT8\ĮÏ,“>â�ŽäåEĀØåe§Zŋ§ˆ ’]”up)ĨŧwPüÕP•“ö;>›"ÞÉ{ĮSéŋ uPâÁwÂŽč…Šӛž8ðwčņ*ØëœD< ųvBŌŦ0âýŧŸ:ĐdR6ĻYģ0":äm9a6 ûÐ"Š'‘O]—ę8Ļ)Ī>ĪÏÞ7ōžc5ëÎ|LŒ9mąĀ,UCo\HĐCsļ'$ ĀU;GÜ'ög�ĸ[8đ(iĸkEĻ›áÏîyŽ…k íŽð•‘VÕĘÖqÝšûO^žtX‰++ķ̧ũđ "’ÓːĐæRĮÜb‰Éhƒoýø`ïę­RKĻÖĘûp`UÅų‘mĐí\+ŧq?,ņrƒĖdp2PôD/—ģ•óĮ0+;6œ!‘–E[0ąårŸ<ÜÆj§FŨø==\ÝŧØjN.āąkk’8Ī!d‰&›uēOG}~û€ËĘÚ(Hρu“ōövąį)Öã$}óІ­ûQï°v‚,˜S†ųJН–/qø tæģāĮŽd‰č{ģƚ"=}ÄĖŨ‘%ŒoMĖø:ķwląÄ2ęŽ`Ö1šˆŽžåC5,ĪH…Oyā‡ŦÐt -,™#v…ĄNԘģ1$ZD^æ,ē-’øūŊš0-\ę,øÔë=7č}øģ ûéĸnï}ÕaėGé\æ]5Ū–IŨ Eėį&8ëÏ·)?’ö ī>ũaŦõ<$|đíĨNÆ‹'|=aŅŠi†l�Ģz8í™Ũ"Ũ―É4Möð93!~ũTŅž íŸŌō°šWĘiný‹“(Ûs§ŽH=§öïåƒĐØĀ€�€���|;wÃßß\|6‡Ą“~ ü<øt§āšŋäü>Ą|:9ðt›áëĮÁԐ†[M+úc#”īSŋmnėų‹ųÃaŨ(ýüăŦî6Ģ<1ąôBøD*ųn�ZVäL PŧԌūtCÖ@DŅ$cũŪOGįÜ}sĘPŠķu„öŦSÛ^M(Ą YžŪßĩzh~Ė`’Č3bz‚DXr›‹ã|>ÐÃNŠŪž k!MļӊņZk}― ­ˆeR)f Î/IýēSÜ ą<Örälô‚™­,éqj=Í#_0Ŋë†ä‘FHôÂR=pČŨ:œQNTÅÂĨ•‹‘œþ•7ĖAÉ\Íjęōýnjåu)tkŅ>ÚĒnÞ<Īéšŋ-įĩP8Ü�į4ZŜ<åÏŠžĄLĄ$Ķ%Æ î‚<ö'ˆđ€–9œpā&]՜!:ðėӉFMy1íjpóSí8oGЎē‚+ī2+mÖūĖ ]Ë&å|!sýdōƒošPó0vŪxĘÅyQ[ÕÕEZȌ9†þt—CA[ožå™š|û”aX•_É~cŅ ËŸÝ5ÄX ‹€BuJ%ŅUZ9Ÿ|ēŧH893N,˜eūUū\!ŪJĨŲ úþʏ Ā)æRIxdo+óŋ9˧pa ŒZŽ#—!kĨæļݗB%ĻŠæËĮėԝLÍ<TČp�dÞ„ČöˆäĘī^ŸJú<%D%ŸYdšÚ, ĩ#Û(Ž>áKĀå6j<ĐÚ]4éÉ"úÃŊEq iėič>4<ûŋ9ūCĻŽILK‹/hþmŦ7ōč—YŲÝŽNžX"ðŠ&’�ä,F`"!ĨũŊĨY`ŸÅEme^ƒ<˜Ž=ÃîAŠ`†âĮō·œ‘5f)ïebōÎë2Ė;TpQ mƒk”žŅJ\‚“BB>> 7a…ōÐą Ũwļ›T·ļ’ÚYFšUkÐõíä"Đød3• 6mØš+kĩGõTNīĐ|“�žŨĒ „kšUrŠ\·ŪË]NĻ 0úÆŋ.KydKÚ^žĸ;#xŽįĘZ:„č·î<`å+%ū#m=iŽėĀJ? :Ŧ44/ŋpķhqŪ&})æEoGˆĸhYÝŨę]ž•ų%lÜūšãˆAĻ‚élĻß8PZĸ*W ģōæ2qЙjÏʗĀF�.ˆÜČpĶØK Xļó #Ú·­#…d]ŸíwqIз‘Ķšņīģ…žD-NÏ·RN7•čO@Āīč<Ô­Ų€ÓÔï Óķũĸ><ƒMÐ:TNæ8ģßÓ5KpyWĸ}ÓÞÝļ‘lrģ‹*€Ö`ULzÛφÕ Đ‡nr3šæt9ĩ]ˆĨu‘›r/s­`?MĢŧwÕäi˜w흋ÐpFîyl0ii”Wïåmn ܧ'CĮÐ É{‚ËИ\V―�%FύŠXøčEîũėũÂ8ĩŠåōéðÔ5ųÆ�ū&–þ|õ’VīW<AJMyÎ)†Ōq_Ģöžų+ˆŸ1Č-OdˆģŅûz;ĪĨvð>3ũ€-ŠOd~…pƒá*q ïŋo/ˆ†v?‚4ãegįČSJ$e]*f#ī‘f/―ÆeÄŽpoå Óåę/ŋ††Ä ąy\"}ÏŽÂ{ūWxHð äĶĒrųų8%(�îC\%,ŦĒķï|·ĸqƌDJ1·u,Ę.cû?Ø7ŋÂr``qī[˜bAþØÝŊ6ŊýtŠļ›ŲAp›wŪ ĒNÞ;”3i.ßJ45šåÜÕ_{8ßwÔŋC͜šĩĪÓYƒäŋøŒŒŒjŦ71Ąe�pŊÞĒ Œó1.pÓīēvU :~|W<Ëϋ[^yŦūé'ōØîĻPlļocâ]Zp2󠁓šÍ\ÚRó%ž=[FN"áÆMEĄcÍóÅRø€dëûžÍô3?DCļʆs‚FBN,"@UäúéŲîɍœóåAûŌãL<i”}gþĘ]ūS†/g;Ō1ŪðÓĐ`uäųŅyEÄø MaÕa†ČƒŊÖĘx?kéĶHbØlēĄ“RYzš[MTŽë€éß­ŨĪïୌ6ę4Óa#Æ3�xų;$œOĮi ,Yæ’ĄöUÅÎX—3Õ2h •ŪúáÞRô ttðVHŪ!Ņßo·ŽðþŠŅĒ[ Ôô;­0āųK”?cĀĘA&B'ęþ§ķ`:‚ö\UÍÓ%–Ž‘_Čfgø5~%ŲąųWAŪ›ByÝ]ÃLlôXéFʎsŲÕî^xjąŸdÖá9&К2aāä :hĨúfôh-V·4Viōp ų9Žó�’Á@ÓëâÝĄ'úĪĐ ÖýʙVÖÕßą9 āöáFīÐJņæ�rgG3 Ņք]ŠJ8?ãbr,·Éäą'k<Ũp ë…mě—{Ũō—_·öĻï2äJzh-&”°Û2ōâž"Ļ2Čø_ýe~;&ŒýX^Đ ]ÄōĻJz‘īĮäeed äewėõ Qī„ (™C…CSÄĶx!LWĪĘrvŒÎo§;sėŲ5DĘė–rkš˜"^šÛJþmĪVeMaBvóÕx‹áVŽ)ąâÞsŪȞþ,ōIĐp_Usf%ō”œŸĘĖĶ?ÏũÞ[Hđ0>’õ[Ēt&Gx[âpIU$Sž‰.ā“"Lyõ ˆš-ÖxA`þj(˜á öčÚ@"nŒ2ß*ŒaŠ™)dYžE,UĨÝ߆ūĸ8Ũ =įŌ*x͚+@Ž�!Â,Rô%IĐÖõPÅô>P (ŨĨnþüŌ>ŌÖ·wŦ:uą™œqagŒÜÎ0ÎR‹LWu=0ý ļËąT…ũð9][·ũ™ĖŋjÎo[Ŧū$™ āˆxB·yNĖóŸŲ‚ÔÝĀ&.ĩ^:ŸāŠËĪušŧÂa›éŊAįBcŒ•ŦŧsģîĘåKŊāfˆ_ŸŽp_›ĢcšXmK‰ĄÆÂ\ÛJļ}ûFšF@Ŋ†(-qYœēŦŲïDļýōúĮ…ßP_oĨ7Īq7>HŠË?~―šŌÅV†TsÚSx QVm&íßĸ@û*”ō;cðĨēfVzæÐø­r§Ō‹ÖeÓ�ü4 ˆÓ åã―Å Q”—ŦĐŧXļ x~9DHÂĐÅ·X_Õb6ÞKΆCwžâļĐĖ΍ʉēDą)"K°"ÔÝąÞēˆž›ûM#ēįG4svđdßÕmÅ9“­™ÁiïH=ŪfJ�õÖ]ņõĩ#uØl‹WÝëWæ˜^‰Ŋč}ą<„ø<ķŸv?z“ũ?„å‚ ÚîcGÛŃęZü 8ĒN.ÕđV›RuWS$t īũ[5ĮŨĪJï91>Ģ;ČL{ė|Ũ&–đ"hU@Ė;>8}ý3XûáĄøĐN”…ýX>UNä^ĪZĒ /H­7㉔K‡!đЉ'īģũïCâv­ßcÞ ŌF§Đ‡õÝî%ËqÁ™ž$ YŌ ‡8ĖVũ†ŽŦæāiÔ2S\ár3l‡$Ӆ,ĞĘc‚ PĻI+ƒĸ éÚB6Ĩ6–ķ.00ø`QŦņúSXĄŸēÓē{ˈŧ|.sC ē6Ē‹žÉ#āZ éŽsûėv' F h nĐĢr*nÓ{­é/EÓ·ŪėOž*“€IĄœØHÐ"ÎäĐ6iēåį+'‹ ÔJÐ(J}Íäķ^TķĄŲ}P(—| 7 å_ĶjŨÃĩI ;ˆļ‚Ö}”™§Ü-­hÄķ7‹†čĘ͛Äp!.Ō'ĩ‡Œx|äåĀß8hūY?Wg!6í’.ÁÂAšVAÔĶā/;Ó ŋŊŨ2ûÁíĶú•rbŽ”š°Ą˜#Í―Ŧ]m{ïĩ&uDĄá„4hŠąÔD}Œ§ÄQøŸÜ(•Á�ŋĐ$1ZëP+zwįŅBØRéOQ*|žG=ã‘šūZ}Jņũŋ ãÆōõ›QlD_ZąI$+,T2ÞÁ Ýč‘5ö‹*‰cĸę0lqĶ zŠ"õž‹>gqé?Ŋę—.㐞ęÅXūˆ:‚Ý1Š]ûā$1xÃ―C°Ėų9P˜Ï5øåĻ8ånӓIĸĸ_·6óevûzŽôFųö�ĶŌ|Y’fðī[ã†všŅúQļÄĮ‡}]‘yŦB-ûų§ĶdjÛÉa­éLþ‹>ģ·ô1Ëþũ‰ąØÆē‚HߓOœ,„þ―†VąŽ1ë@‚ĩ%áÕ‘ôâÛũa›JÃFīÔ#āÓšG§MŒÛF%žÄšoĪsӒ�ți/þOzž‚ĪūŲ w}&HÖëCī˜mGrĻUŽSŊSÄ(ŋâčq‚šýsÚu#ë‰eÉw9Ųäî™^tŧäęLä™âN4.έāõļ4ŦžęZ–NK•ljÄn-•ĐĪĨĪ]à Ļnõū“Štúē+ôæĮÍófü\(îĸF þzÚoĪ5ĒUҧ>Š Ą?=HQĸIęĐësõĪvI―*éĐ3wˆÕgI JŲzđŪTŋXRlP Eš$öˆŌ/…Ý8éÓE™…Üšđ2ÅîEBoōÎĀMę*ϊĢM!â‘=—­ŨbĒĮ‡C3·OÉ3Ôy2+‡€YūËɍĂ 7ūt€,…đHïĖxZq$úy˜'ŽgþÔ'g“Œ+c’ĖØBü{_įõ”Ąð&üĨ=9âüå\ËVėéį%“Ē™ÁewŨG’"4+ŌDŊ ī.›ÚĖ[90zøQPIT†2žC\‰ÐúW$jšKIFcą˜Ōš3Üeīo KSÎÞúáĐbÝqŽ$lĻū˜ËÐJ·Æ9͞ÕŲ3ģãК ’’BÉíųÖÓģÝĢžw|ž[Ō&AhJę˜ĨŪjÚ|UČÓŦƒ mĩAh{Sņ8=ŌlbóO& 6Y%‘Þ1Ýŋρš�lĄÛý―7bó§gæĘG*Œ2~ßŒÛ `ĘïúïJTęÛsÐ|8FĢXÜ+Cgė_­:nzkÝUZ3”é?xėGƒ–Û8•“ũþXžË֏âũxÐõ]@<Î―Éxģî ôÚY†eÅĪĄoŌÂÅMT”ÁiJpsĸPÉģ~ĪŊ ĩ…tïÖQäš=ΐf“R;ŽŪž§+ܟēðBۄō"Ėï/,rÏãniÂ~‰éÚہE‰T•âĖeĒų‡Ũô ŽŋmŸNx9!—TEîAå 28„ķÎIĄŨ"CĮóZð Î!—ÏĀ>axS—fÁ æ R6‹―ˆÁÍƟMWŪ‚`Ë ĮB–!380°X›7üđžĩĐ{4đū„SõššuԋN˜UÕĪ<Č3ęuUø :ë%Gœö[ŽÎ#ē;ŪpJŲŲ1ÁīǐėØĩ/_ĩðŋĐ eŋ§ Íú‚ðØĮė‚ĩÞŌÜ^o€$WŅyŊ3tH&Ä —đ„Ïå–ût.0ę>ęŨd“’KFiä*(#u+áÔ9ņœ%=ŠóĮBBĢ5SĀOBS\.ųėuÕ –Æą.ü]Ž:é=Ëb>NEšđņšÁ·–,+ČöWuÝï“īšÂRįhžl/R€$æfúULēÄáĸpU>yØ`<ۏÔ2ó]GšKā›š…ZIĸ|‘wĢąNą<eéžZŽĸM N‹jՀDŽR<ø?5 Ŋ•iĖĩÍņéĄ.S‹Ï~i$Ušw„Ú™IÐõFú€y§āOč€=pþVMl°5Ååē{H1=óP&Ô·bŦË^ė{ԔnØA,< Õūú=osåq—ÔM|šŅ €ŦčMÄ(lð…“j €ZŨQĐæsÚMēác?(‚rÂģŨŪÖŨÂĐíXĪŽÏõ=+æˆ.™5ŪYëšjđ‡”–b-œÞņĶKģŠā[øƒE°ņû6ĖČ` ŅIFú‰ŌŦYZ·ÐŽ:1”@ĢđīΜ~—u0BŸŨáĒ*iņ–?ķ]€æŦ;:q_NQėčČqs;Bžnø,.Žfh{ÞÖÉīþ7ī3SûTũd1Ë$ƒih;ôqĒĖqdīĀQ�‡ņŌØĨÂŸŪ@Ð+)Î]jį&  G3 M‰[ÍŨÝ!ƜĄ;f5Ԇk߄Ā—XbðÔy;u‰ āAc§FŊũŦčĄø‡5ũĮDå(ģ7?jũƒė*―@a%ė\ëĖ ?S™ؗš6™&:ôQVĩ:æ°/Ŧtĸ3Ÿ‹ąÞĮ:ī­„ Ÿ3Öð7 Ãé ’, ĻÄ23Î?ë‹I XŊĐAIý‘d*6ŧ_”æ$XOņdŦ OāˆČ9kzIqc apSî”ûŠC­1#°fĻŠņ™ÖŽM0WîÏåsņÃyĩĄŧ 2ÃģY/tũúū!^jo„ī<­OĘĸžÚPŽo(ûöóĪ*r旛/WzCWTįû”Ûß*æŌÁ<ŠaŨЂ­ū]āč>ų/ĶŲï ĸFqøÏÎГēķF+oƒ Úmėž>0Õvyfۊ:^ėkĢBÄyFĮüý(dSb>] ;Ÿ›‘°ĸ8ēfÛ(ۜ0õ!ŽÁ!Ä4Å9óÕøŧMÍÆß[]ŽÝ"QĶ›'9ĸ (3�SuuK æoĻ{’<ĨÄöîj~Y0―’ę;Ũ"9ēSö•?cũœQûĶŋœ™oĄŲ9ü‚`s<"01kðþ$°ˆŒ‘ĪÎßïTƒr@Páđþäęú%*ųy؝Š&A(#nå>–Į’Þâ<•ēígŨ”ōó*($˜þ‰% SÐČ,K‘4Ũią�暒áøuŦ·ļ]ąü:Ïáïk―YwąĸŅ<_ĶSūŨAßf!ðúm|>ĒþNāüŧōuáÝnęØ.é_‡wü>„þŅŧ؟‡Ņl‡ŨÃîlûmūģþTŋķ?'u>Nŧáwd—o…Úûŧ'áîk―{ø{3ðú …ßFšwÚ;čŨáôŌfŅ}=]öl8Oĸ\â›OĪčÞ%t›ž2ŠOėãœ2ĐũøSSŽM–ݧx-ëĶĄŦūþĀþÝ>aĒvöāõ°ōŽï>ÐĨ”EĘPmėV5—õĮþŽĒæėØŦāÚNv~īķôĐ$'–†zæe,1Î^ƒüņGĪ3?‚Ŧb7GœŦVŌĪŪådŋļbžWŌ5Síã>þ đMęBbvũw5ųI"ö<Ų=jÄu#â•āč^ŋ]o•ŊCŋWô�ŠŽ(å_ĒÆƒĄõÛá&ŒF-ÛÜ8ž�ŸĢūH4s:,"įu4ĮápÛzÖA"V3ūøx|œŒĘ?@ĻoÃ2•]2ēM~æG#Háx”ĖQ‰ĸSîUEĢnQE:dÆįŦŽ9‚Ë™$ģäƒôī―^ãVkžcĄ1˜h/ð_"į0Qž_Ī.*jCgžĒgG#™đ™ėŌū°"0OĻ1™?IŸÍ$5UaœôÂ-*îÂė–ø‹VÜNIĸzŸ‰ũ„n#· cÅÃj[㏭Æ#°,ÔÕxā•Šā›håæī—ĪŠP’ŠŧØØðŧÅŧųðÂv'Ō#jƒcYüHĮĶ’M’%9KaŽ‰ž%Å�9Ė)ĘXIž9ØÕGM40GX'hæ^}S§ô·wRdfuQûóDß&ϝ4 V„xŽ 'Ջ7~ v‘íWBÏ*ãóēl3'ð1F„1QaįāY˜š·m; *"‰ŠÔ7ū@éą ÔĀVÖėch”ņž&̀Dž+ûžvƎÓ$ķ`ƒ§6ÃļÚHĮÖ,vE–~ÜÞ{8Īßh^ Ūúk9—%Cãŧû·ŠĸĻlW˜žĀI+fÝüuœWe&_>Ã5ü:ˆG°ģâo—„Âī°Ý IÛ5ƒÐ’Aœ€4Þ$“ã4ž„Ü݌Fą‹7D:*…vđ‘ĪT‹ėN3Ü"Į7éķaf#ÞķŠöa§(ë<�T ĒýúdsÎYNUį·•{*~Ÿæ*Pš$;Ōî>+ þa°v-|™–Ėi—q?@k m&·`DŲLˆĪÅ؟úŽęûŦό…Īrå˜Ï ݊.0TÐ5Šüú2ôãíUīsāš]ząÉ:âũŲ1zjK4Cq;cĪ€ãŧ“7Œ"ÝÉDĖOwģũÆīÔ4ݍÓS§âĢ6ČŅą6#ËBōÝaÆ7Û4ąÝMųÏņ>1ŸæzPc€wÐ îŪ3€ŽƂ,3°™ÜŠˆv_ę…Ęųnē†h‡wÎrƒ"Ũ~7Wĸ}�ÂŌŋ_Ũ–I,ö|Y ōx./ąĻÅbķVōiŠoz ^•QÐqXžOFīíeyÞX\ŪxLQŧĮHņáī'K^“(§Oü%øwS!3A­s Ð2p6š‡ĩŅ}݀…0ŨYB”"ĻŅ6WĪā`2ŽĶlĶÂZ)ŽE7^TæĨfæ$Ĩ\ÜP<ũČð?‡ 2―ü“tp&īTfTT—°æąą~ŧTŌāpũš†bgŊÍüįã”ōãM3bĨƒĒؒnRcŨ|ó܄ÂN2BQÄ]Ž` '5…U ^M13ūâ<Փ%äØáÍĀd†uÅNīڞ(·pÄÐÕۊcgÄ ýŠ[Õ3īpę’}Ž]~AņzK9sz•ö,čÜē(Ār:Šĸ;É'đ;~ĐŽ`\°­$ZÁ…ˆ^ûØOŅtW”<bM―Ž aßlĢNĀutļÆđhKR#'1—Œ1&Ũ)īfG‚ĒLčTbĪUĨËKÖ\·‰.Î3šÏ1.†~·ĶwũJ}ãA>vš_ÚĢˀšˆķÔ―Ãl7xī(ģ‡Ä†w’Õ•ū…ýl={HƊPËFA0}kāžļb˜oŪ•|yp*ômR!IÉwŅĩĒ‚q†`įš{é‡įĶâ6a‡áG^Ü*,訓ð$"AŒYËÂOœĪ"úˆ ÎŪ„ÝG3!zĖÂRŸŒŦ—AĒ,ēŧåVö’E#wU2Ĩ4ÂkˆI—rkSį8 –Cd~õ!AĶ­9ˆŋŸ Ę',TÐ=yšt‚u#�Ē•.YüQ–Ž ĮIĨ5q1GäISĀkķþŠįũߖ6ÔZđCk6zíōY967äW\s’€ *Œïƒá 5û0―[yâÜnįņ_ݞāL“/?ØÏF{ ‡[<āhœÐØíķÚû(  ÅîððeĮeôŌ~.~ĻÂũHbMĻ~Tû8ĸ`ty „ðāÜxôC›ō8ĶÕÛt+jāÛ-ļōkčãd W‚@ģ‚ˆQ|3\wčVĢÎäi™7žq…čˆĮŋYqeŠ#•KŲip<Ï]G𙑷ī\å`ÛË}mā>bo4ŒyýĢ%āý<ŠØwJ3šāgÆtŅĻÔ&ŽĖ~bHEŦ2û  aƒuLž†5ëV„˜äĨãĐđ €OUƒŸÛĩ9ü”FKKjĢcũGc­ïŠwãČŽÖ6ˌŌÕâxŽō:§LĨj•ŋaā˜ĒŪHã4ŽĢIÔ~]Æŋõžp9œÓÏds@wã―&E§ZŸ Îæƒ8x%áŒFt%–Ū.BŅVh%ŪĩŸäÂŨĘ'.ýDbxęŧ…ÄųE·ZĶ5öNŪÕ/%4ęZ“Ëߖ \fÛ,oĢ―'3­’úæTӊöĘ%jŨq–.p^‚úG™‘ð›°hD°ðÕ�|?ƎÚrū0D‚rôeƒhÃzāįUlQM&ŦÎąîgkũ,æ&·ēr=6ŦÏG=fôõŽb홷Ü�!ū“.xÐŋtĩÔbtš“P#7œĩæHĸgīzAģFyŸãŧō1ø ŋ2)bHbÄú]Ï—Ū›^ũôð€ąë―ŽĖ ĩęƒ$ĮcŧÐL„ŽcZ”Ô5>Ũܗ §îtīmĻse�ËyŠŽ 'ëļdÁÛïŋƒž;}øį"Á)ô ßÖüŽÞ>ōĀ[3ü;ā6"Ķ E͗|Ä{΁]`ԙšÆXŋwÏš0ģX:mœ’øø€ŋȧwKōā­—>røžT·ŅÁRĀ6OF6ŠýÛ*q#ýOĐĻ#Īx*(Ý Es‹>[З|#gĻ'ŒL3FĨE†AĄd7/čķ°a Öčw­Ō@eیr§{NÛt6F9=@ ÉŲûžáo#―Ÿ2Ģę•·OöÔb!t§þĄ&ĀīÁ ēāƒÓ ÛęĢ·þLūÝ―•ēÃd<â€ļÍoŅ7\ʔ‘Ÿ}ĖĀ4lÛí]*zČðČ5`’ƘóÝYFni-ęģXfš&PV8[BJ!b0D~įÐAžs1–Í]ÆĐÏĄ%å{[9ãgæÉĐ58ĄAŽ`ðÄģÔo3OŌēî3€ĸĸsÍËĸ ÍĐ"‡îîųÃū6įïaV[§î'ÅéXw˜ë]hĨāTĀųï{'œpétēÞE\1°_Кųī zĮh‡�RÁØRlēęïėĄĸG―ŪGï5/ŧŅ2–§ŒęƧZz^QRKĐã>Āâ~b”ˁJĻVklUö8l+\ī5Ģ‹IA֐ӷ+&ΐ>é 4VöĨ%m[Qóāýߍ-{Fļ’Ënū·đũ+PģjĖd’Ô͌yAač‚Ó:ĀĢ$ķŽ<ĻQĒâcrH` zl"ėÉœėœ8ŸĖf>·;ĢßéEĀ„Są Ũūj^aáŊ™/;#|Ó-H"9„/BPߎÃDFL “6ô&öæ,jųø á°ÉÏ3ôP åōW5āŊŽķHĩZ™ŊCz17ðūZˆ- cJ›ū-Uø>žŠoō^Ęō$Į”ÕMï=ā~ü*&ĻūX)ô+Q”°†æ õĩ<æÍŲĪúHōūëžpëßĩŋzâdÚ^üjŠtXZYKäpÜÓÓlŌŋu›BŠ! ― ōņT ­yņc 4Ãž"fÞÝėN”‹=‹ōhn ĸ76^K ēĊū2âã-�ð$֊ãŽN“™o#îúč8@_W-þÅó�‚Į˜Bgb–+cĒĶ"V0§/ēuĒíUęo‹eb”ŽWËų1ū‚6†TmË ŅÎnčh7ĘâęĢ&)ÂZĪÁīälÅō™MW˜Â_Ãö/Ą€p°b•°\Ą H=˜Pā˜T{BVÚoÖ (N(Ø…Ã�Ú2b]X—ĀŠ nę*&TĀüŋŌY�đ: aŦSSƒBŸÞޑĖÚ‘ģŊšĪŠ0Î^Öļ―Æ~xNRēŸ­äŽĘāš;Ō0@Ï9^j’b–[xåĢ}R-&Ē|Äý_:…ŧ éļņÄxã;—]…‡ÝĪ`g)8Øö“L5l "=Hð7āpųCû™"ąï™*Ҙ 3ÁéDęŧTr š.%Ū2ēeÜt?ē7PhKdģKóŅĨcāČŽÂz""ĀÐÏ?þĩē1åŽÓɄˆ>?ô Ó5ķ ~Åņvĸ$BŧS·+{Ž<܏R3+ýr5ą‡Î’ĒĨ/…dZíŲ·4áĮ>ãDRl’^ĐŅÂûlÉ) ‚6îi‡ļEd/gwݜŌðHĻ`—PÛrîeĮß8;ûϔ”gŠ3ŠĄûtŠÄ#ŠcŊ6'+Já]SOöf\‚ozûlPf<ī‚—7GblxĒđYđõ )á Uð8Ǝč$ė·°|šÔE>Gžõ7QÎ6ZLŦ†;ĀæŸūVŧŸ4mũÄŋĄûÐ"—é(X;XÍÜō·MÎýPîüø°™° ڇpįËYj ZËPiđ—›Åâö^Z;œ@,JГÅUk="ˍˆŸv3eęT^‹f5 {ĸ[6ĐÕ|Ã6þãŨ_ú”ŦŠTGzķŌcļý1î_ļCÖŽŠŧ@+MTĒĪāĻåÜšĒOwžFd�čKŠĄĻŸtĄÜĨ!Q›tēĮE–l#_gîÁ*æBcný ïŨÁČ藛˜Y2.čįĻOž:ƒ 9žē!0Ĩh<LōĮ+œ1:ĐøđĒ(̃! Mōî `–”8.<xŒ;"„Ów4vý=jBKˆ\ye„ŋ”„ũO†Ŧ2ƒ―/đVĶʀĸ=tõ”c$•bëŠvPÜæB“ē9+GēBT‘øâî딎C5xō‘‚>ąęÎlžĻ‹ë‘'œš'óÕųĒp(v�õÚtĄŊÏFTūÎ/qUōi<ĨiũƒólÁŪ_‘8ëdƒUÏÖš„ÐĸŊëf:Ng(IGM0`ĶŌæewÄ"/ðėžĖr? Ü&9 BŨŲš4Ō`&rĐ/ĒðĢM#–Ā!O"eđ·ß&ë�ÃU‚/z™ž˜SŸb} ûáīÄ äï}ÆTØn‡ ęþÅ G@‹dÍ$Ni'âDÄģŸr†’ŋĨŋr“gŪ—Ygž6šwJĨŋ{œ―ō$āö”Ī­·ã–\°įy ÅĖ]lbF)o ˆXs”5-%ņ&8p„>!Ų―ÆNoĩ IĶ_ŅįÕ ũ=ÏQĒig™žöZV-õŸ†Vn‘"āܑa8 gÍČf‘‡Þ†FāËIâ”Zģ vÛΆ ‡îïãnÉč ÂZgĮä–n'Åv•ĻIđ@ýėHCVčĩ Į”RË·H9(Ã}ĪÜđM„Į|Œ Ķ―/GIpSMS!˛uŒ”<[”―*>ôŸųÏ,ÁĪýï•~HyÚŦKP˜žĐD Ļ˜―Ģ<žØÁgQĪãþ­{ķÛ!Ake4™pĸ “:ŌÓu=Xï*늊ÍėO94Yw1ĩ9õ5cĪ'hë?öģŒ!wB|$ÜŌ?īĢ(Tģ]ÉÎ ÛīÅ?điNŪÝ@åÖËĘtÃcBJu0Do°óÐ_U•54øBĨĖ [ēhôČˈŠj6Îãi4Oüđ ú3MQĪO fOÝÎðŌzį Íģ}SzŒĒVˆÔCU–Ē;':x!lï‘ė+aŲÕï―UBÛ>†Þ]š>ŊqŅF>ĩd!žģĄB7É)ūŦkÂ_WÆ!ŧ$œōZ―ßŋæž―ö[9)ŪÓQ!á{GT鎖Ëé;wëEĄŊOlkDKą{tų*‚o9ɄjG\―xžöKāĒĨ!rXŦß2 đaĘķĸCœą4Œã Aö%žÐūz؃˜ mÁéQ ûnĩ:­‹ŧĩžęÐ6Žūa]6Ōę2ßÚJ+ýŪ@–ÍÚp“úÞ 7žÐŧUüĸÚŨ Ōŧur@K M&„߅ĨæĮŽégNąęÅØ0ĨΒ/}Ī;Ú*ČPmËķ‡ÍÞúƒ]§ÎžÏÅæŠgCÍŽdĩ*ĨÞTҁ9Yģ„O3Rníų› FĶ+ī>ūp‡3ų>ęPČ WÎ{Ÿ‹áaX(Ķä,‚váĄā5å JæUïX‡Į„zÁv>k̰Šņ2,ų•xTƒY|Œ˜ž`Y"Ų‡mïÆ%•ę^ļ“:yL˜WᩋáņlĄtéĀAåmIš­ÚEĒ-€—r-ŋŅw]RÃé 7z îLĄáxŨQ[‰ĩŲ;‚ŪOïÅũ_vįŋvÐ;ÂOúšXĶTĢ­–ÍLĐwä! (æ(ˆ?ÓIžM'xc։ 6Û·Z‹@ã ý�ÓîÐ?§TxŽÐ)·MÆŨ>]nH˜)Üēû6*dŲō*ŊoŅŦƒ_ûN/ģýEÁó$‰ÚŸ/.„û&ądckt&·„Á4ä +ë’Lƒ�žzŸïdaĨä}­þÜŊō ōĀĨ6īcžāÜŅéƒÓ1› „ÎM1ûW<6v!,EEeč°Ūïē9—\ðÍ{û…’āMǰðVį'ū^R�—ęēírӒ;Ņ•ũ”ĪVŠ^Ú@ļ$YŋÔŪGMjðÏņŧŠLãÂ�ę/ŠÔíå’m2aū9ï$ðeŲdkøĮÚĘĨ—ō·’NDäUmeN)�ȧŊâïŦē„ûĪ:i<žČí%a€3rūNöuüĢĒĘ@‹„ÃúÓÁQö í;ô!Ēž3š=@WäËdÓŽœ&åë=PĪxĀþ"AýđEąũ j”HđM$V™ËŲ Â›ŋ|&Ÿ8ō”ÎŨ9n1äŦō‡ÃŪoÝĒĢĐÍåp9qVL“ éÉžîĘČ0В CbØÅ‚œ€6ÆsYëVýše†―qe/,bØšýđŪCï ēÁÖiåÝEå4`öņyĒŨEâj§7ÄĀĒƒ‹ĖO‰ų|EGįh‹‰:ýd{y%yė–Ė+?·ÖïxĪå\�\Óėģũ՟å"[(†Ķ ‰Ņz~x)’ Öà J0@4Ý0UĮþŠ,uĩĩÆ/›QÉōĶþAoi·EÉÝ+å0RÐŪinkz؆PwlXLÐŲVä5…(™”…Ô nÎWw\†_ÛRƒcø7ÉLdj#›€–V(ųŊïā-Į$Øų…]Ž^bŪoÄþtõ ”Š2!NBô x*˜ŊĶÉÓēĶUÏ―ĒøH°éŦēņŠNŲj5ėĪ~āÁéÛyƒėmŦļ—Ę“Æ�Ðþ†’jƒ‡‚üiđL:a‡đŋó\ÔNŨÁˆÖīZķ‰;þÏ ãMjûÍŧÖĨ(˜ƒ2jU·�|ZžYÝý™Ū~Ĩ7 ĮCŅuðÃâ{öҐÜhŲ\ĮŪ4F"^<ŊYö›•=ķŁĪD|Ž"Ý­yC8Ëýe녡[Š.MŪ{Đ― ƒWJ[ĨūÕ|QZņŌÔbyË'āïŨ$!–qēØÜCãŋ;ĪL^Û čŽ`-@vb:ķ†ģ“QēžĪėŽJnĮDøkļ‚ĪíÝ-É&ū―zv·ŦPÔ3Úėƒ‘gXĶų•å” “đLŸ‚b§QįjÎjÆdČæŊW{$ĸBSĩSĘŠ �ų8q,ĒUs‡Nw+ŽâG!æ&e6Ęc=―1ÐČîį>$õNâfĻ<ĩži CdG™ÐÚÎĻl—ėš—Ü;B^$üķ^Ý`ļ.°ZÕtīˆ 3…?Ėņŧ— h˜ųq„ŌFäóę,iHu<h]Įųaž\­Ý)3ÓäA“-Ė”ÔbĘ''1;ËíÝ `TLĮ·ÁÅ$ļ%i[Š#ÁžĒô_–ĖÆĸ%-5äõŋ†éG‰Ķ žŠ!š|ÍĐv'aðëðp“>ËėpYĶ }t4ņuKŒ€t`Á%Þ ąÃĻß<BōïĐ/be°—šx›š>“ŦuüĨ)Đ6…ðâWë4?Q‰Ôgč‰s8[.e}ĸ·KøÔã…P`.5ðLŦš‹ß"įtÝûþS}ŠķĻØīËĻŊ`üŅcN;‹•„Õņ§ âïA)ۜņ0ü•ökķMŸIŪ=3Žņ0þr[þ “˜Š^JˆÍ!(/$ ŒaĖA!ōuķož é?íįČČ@ĪįPwsó‚˜-Q�i„ōĪR‹þ§đų�įĐwcQΈPŧafwDXŌ;)ûÄðDë%îlaBšþ‹F‰evv5(Ã~†Ļã`PŸT•RHOoký#€=Øīc‘ĪðHær’?vp|Â…ėJĢY7!TŪópK îyĖD ‹IRgŅ|‡š–OœĻ‹z}ßgB˜T<Ē ĸBųj‰žŧh4hŊØ}Gú^;D=0ÉVŠ[˜ĒŠËg0î-XČhV‚Ó"͌L)č_[;l—=›ŧ 1"Ŧ2ÚÐtÖõsŨÜĘgŧSÖ-+EŲz€Î–|§€äŠ­úq:Ð*ZtŧÎ� õ{°Ą|ôJM3qtĮķæcēœáŒdēÜx+å=ÓCāĮ֓ãAYd1qŊÛũâ Ž‘ŦFo?MĪûÏa(ĸŨ0ZQ”-YOPbņ ŋÎ=ŋK?J<ÖCĀČŊ2؞ju3v’í9SäðBAÄ{S­ÔdũTŽœQø9^a-Ļ* u8ß">úCƒņ1B|ʉįļÆo’ŠĨ—‰<Š ÏÜöėė2‘ŲÄMڅō0Ó>ŽcW”ãĨÏÝž ˜Ļ8ð!—ę?`BŧڌļÐōīū/ŋqč.ˆ―§˜ÖAŦ;Î―ðĸMZBăāGŒeõëöŽ‘ŋ―ũ„9)Ĩrö!ņ‰8â)îŧR‹O(1BŒœ]™ŽaáãDü•b ~Đq)`AúË gĩV”øY=5Š+‚§Øõ2á;‰Ø&VĖ’ö—–X@ "PĶHkxĘOZģų}îŊûņĐp=đœß‡dÃ<%ņō†—·Ļ€7n•Äb(―YS˜/ķýïųŽÐoSY†‡Ø$)ōiŌæ1-˜NA€Ð&Đ,,ÄTu!cïíĶÛxœÃ6‘SÓō1ŠáĢÒDŽĶ9(6ę8ĪIæÐõåTļoÓ°;ïēxžîo ĄęqūÚČ9Āļ�bXïaføŸ_>šâ uvįq›ō ąšøā=―\„{m’Ģƒ&―ĮŽZ_ą5˜ŧŪë=gÞuŅ0&Æ2:ë…ę"‚)ÝDđ―žã=ÞHŊo“ęē sÁŠ\ã$É37A ÔņŒ­�―šRëé‰`t€šR44*Þ­Ī™ÜÕâą'`’ëič™iJ*ŠĶũļf]lxhÛą@–|ēÓē ëN6‡3\Yū§6đGŧN3F A›h™ ēņÉ8y€ MïaŠ‹ŊsQ@ÄÜ îbū3…fĘĖIŠ,}Įč]&Ū— žob;\owM_߀WnÔ5i&Ą#g­ys‘(CÁtAUBmÎŪÍW·Û=„FN€ĶEx4ÂĖ#TȍACÜZ“[äĖoųäēŧáö8‰‡ËYī­ō3L )·ČŽlÐäôF“Pē·-0―ūĒōGu?œš=dÚķ(mūðïįa3}u/ É9™ųísnʘ*ŅyåTû Oņ�Ų)bŦéB.z;ÓüíĸSâŠ3âCB&<\ÝQœĮZčð”ZX>E3"ˆOƒ9a9œj*7KoĒ‹ėËjĶ”Œŧ-;ÂÖųŅ~Äír%ĐqTĩ‰HÂÞ:Z4xŒĸ~C€úČDŸ [‡zkķD4O ŨˁģóÛqjépð\厛BüÆ8m_‚Ï… ˗Č+0}ņ%Ũ—‹Ū•–xpŽAĢ( 4Ûď“ðl―ïĖt{?}CŠëģŽîŽøĀšđáš~/ĘĢēiŠÚ ß(#sz9‘Ð⊎+ĸ æEų*:^æŽ"Öš’žĐ<ÏĐōv ɋ/;í%Y2w"xtrN_}“°�yž U-ËÞĨ?t•īöVņĨ,”-°íýJ°`äĨ .§?īŪYą"hî{žķŽL‰>`”SųĀj'ŠÏ–iēĶĒõ_ŽJOæļÓý"Hc‹˜ĶÃËÐd―lCŦŪ떓ŧ5Qiœ  Ē“VjІĢӏšžP�ZĮÍZ8ޝeŪæļ˜zÜ­~ÝwŨdgāŪ*ŠRƒoXM§]ČęR_cOÛjÞ2dēČÝ<>KJž­ŸJúĀ’ íHãI§þąĢ­Å ĻĩĄb ņČuRkz�Rϐ―`Ũį›NÎĀËĸRs*’Dr֝wÓŽąQĐ@’jÐt‡øLĩũFaĘŋmb<N{ųR›°,9Ŋ?Ą·!^“؝ ÝHĻÍiŌ…>ƒŽšŨ§8ũ\ýĢÆwÅóW^įC-’ĒHÆÏ"îz·hģÃ9îČ\ÔŠ>&ē][Û6Š`Sû9tAxį—ųûđ˜pëĩgũZB™Ļp;ĒČ1d�ØXNóŌ8ī$Њ WËĢģð‹›í.E-Ïð$$ĢÓß;xelÄĩHâžUš<CĖ9ß―žþūriú�bU―ãÂ%Ę Eņ ­ĮðđâúÚĮā4o7_IM1Weæ*Ū âbŽĒ’zĒ/.ëy*>*ž* ÜĨöÔÉ,ü0žŦĮāŽę”ĐpéŋjLČQ&�|ßĀ2/? „TöšþF™Šą~1šŅÜVxOýÍÆēSūáĀēŲ !a Jäąāßģ ĀŲq”‹ đú†ärú-ƒrļhvYŽĻ‘åCĶüĶCaÚp·Ôæ`ÅV|ĩĄÕ K„b„VMiþïéãĩKĐï°{ÛŠ„ÂŽņØi<žÝvOúÓũ<ŽŌxbĸ?ņsðƒÝy3īåSÜâĶnņ'ۙž  mą{/Šō—Ķ—ÂĻú‘]E˜ W-E‚Ūa&79 e`*ôlŠBŽ„Ū:%NЌķ@I@Ļ*ɛ 7%ė§ÖĮÆjÉŲĒÎŧƒlWë,änh,Ä9rú›@Öė†ÂĻ’ ŠýĪa —ŒÖ ›đÐzÎ!’-2­„ĀïÄNzó‹ĀmáoĨ‡hóü@;ßKÃÉBļ’˜›nĀ�ĨiÃ%wAz“ð m6ædŨÄų­õrŨßyåŒŪe„l]Æ'ÞëõâåVĨå]AÂhÜPhĄB…ėeĻAŨÐïDŽ=\užį3Ý%l(Œ?YQÎ Ð3ŨÏ(ĶÜ}ĘĨ‡ōėč!§$šLwq5õnNŧ{ÜffĖ3(é›4zše2n„ļ ĶíæÞ ôܗųČ B8AôĒ%juÝ‰ ę}Ükš[€/ĨýŨŅķjNĀï™{øģþĸ;°BJ‹/.B3Æē_JR7 þ xûß!ôĪ ’O…ø8þ^o^1Žû™d.üĪ)”įôÛåĖv4ŋ~óƒŠ>Š rdéīƒĖ lpVõ1Šũš„“„‘Ój!XŅŧ@čþ  ā°û“{p―AÕÚÃųÏï…Kžâď˜Å2ôų^äaļļO þmzē•%tdIá˜åJĢ‘ˆ�V Ļ$Ĩ9R##.†Ŧ‹Ã§ŒZKE ›ü(Nîj Q}Wŋōä́õOšŽĪWŧ|(Ymsģƒ§ĄOÆdøŊ7 VũĀW™ĩWTB †›ÝpÂRģG&­ß}ŊÆ šsTŸ <pfӏŽþWĒRætŲ/‡jŠVXÔK+tŜÐä§­L–ļ1"ÄxĶ jũ8k·c“)v,vĒŋUąĒýôRIhS‘Õ‹|ĐXáî8ä>VŒ“ĪU’ÄUūaaÅfŒ>œÓ”ëmŽ{{ƒė‰uZ}ĸ ÃÝá°~n*ˆëá֞ßā^wðūa}ŌgsÉAâ\qČv=Mï\>Į,õ(ū\~nÏN[ð ˆDÄųĘt7āeãŲäJG>zĘÍŊT+4~īó”ð!)́�va 69lĸh-ņ§Ôęžį „ŊČ~ø‡ÂÏF�Č`Å_HÝļūb―°ęÃŋî?|T‚+Íą8ÉĨiY>VR3Ds-c‹Ô–öYK+|―o+noÛÍqŅÆũFDĀēúŸÍģy–ã?*ĩm:Š<ËĮēļ“4ąž§<KÉȃrY?šRi” p`~9ĩv”S_vŠF+Öa—;ĒØ9~ŠÐJ Zwōņ%ÝF,wåāžĨ~J~Ÿ3-h‚;•PÞ^KRæóĻĻĪčļĐ8Z`ūóĸc‘I…Ī^ã9˜ĀĪ. Ø>*œlpŋFoöí;ˆîŧfD Q0. €°õ“ĨK+đï°ŅRūMŊ W ?Ma;ïC„âyo{UĻgÕ­–Ã�Ļ<ۃëÛĩē/Vc+ o™*Ã;ó(žČ/5/ļúüq;lû0õ kœš9)ó°‚ē VáuĀU‡J ļ•|Ø@ NJbōKsr€―Õž%ũ# KÖv/žBs"Ļ‹,ˆ%lä5ī­íķÏäŊĢC`ĩ}‡‘BtCîTôčïmÄ%zĨC<Ū0tĪ'G—"$!)Ķģ>ö Bþaâ_Uĸ[ëÖ·Y^Ķ.•ũYĘ&Bï�ÞEEųsô$é�ŅĩĸN ļI1ïŠvŠ;„ķ˜Ĩ/ã6ŅMÍŸNre —ĩŊÛš3Ō 1Ė:~ÚČ迯[Јaûæ~ÖÕKWęŊÍ Ėá f3!įé<°ÉifŽ}Éh#K‰Ä”šÃ„„x—€/øãyĩĒá] ·_dŌ/iý[BčŦ­·ƒóížž;\tm( ŸoÃ‰ŲÆv<Ģ‘MĪģ6ū.‰„Ā'SY˜ČŦ čû~mĄX‰ Ė Ý/9:·đï E%ĻiDŋ/P;!øž§ļō-ž―!?Ģũ§ŊĢmðę;[GĢ ĘÂ΅uj�Ó ŽŅ朇Ûëmq”Aæü þ.!!öĄjGˆtą„’<ĸŋ`í9―Ј@{̈́č\ųęó)é(‰c!aĸJEéDŲØŠĘC‡óΟc …3ŒWš]$ö$*‚pÓwہ•ēkõÄ3Mō ŲA%ĻlīEEa}~Y0+ÄđŽŊpøÑļĸP ųÄ åoÍ*—ÃÚĶĀĒ ļg#&įEëčSI•đ^‰~EĘoũ‰7tŧ<í ܓâ“ų›Ý‚tïî̆W‡Č2īĄÂģŲ•ŋėî&\ĨĖOijF­ŸHŊį"ŧĀH„p ·úwFsUzÉē 71*—üSޗOĐiŽÅcæ+3îÛ#u.ū[3íðJ47 <(ßļjÖ<Ė5�Ïnۋ­7(JNvmãGu9ø;īĒŌKæ+4Jø;—ã"%�T”‚:W—Gý Rl Ëɗ`þ’õâŽĀĩĩ>§)ūä:vŦGIĻsčoÕËŋą Ãĸ*­ĮgCGsã˜yuÐÁģk@ĘUí'“ģ=ŒļTģ1Í―€JGĶæVoaņÍÜāˆđęWĖŽßHg Đķ(f˜°æ)2ó/Óh1’Į>84ï)Vo,­GTí(„AŊrðŋč‡zY†eô>KŒžČۉ�lzØã†ņžŲ}ØÛ …(—ý|tCÃ+ÆųBĩ™#ā3#ģëý �˜95‚Úxƒø"Œįƒjw‚A'I5Oâ |ų’6™bCnüĩĨlûūŧ—oïÎÂę~!1ņgW#pé­ĒŒ6}6Aw ĪÞðY=dL/QËØƒÝ ášs…ė8 ‹Ë0ČÅOÕõaÖÂČ/‘fÓ/!“æ‘°č#W\Î9ïA$*å{ĀČ8Ž’$C!āa°·ŧMi8ذoöĪČP î‹ėŦÆx·Ļ…ëé!|“ĩ“úëØAũ!š\ečõpĢÎÂĸ/1ĩ‹'ŽŠōĐģ_Ž)B$eæ'ĶÖVC‚­ÝģŪo\ā„awlˆsą9=a™ Ēīęø<˜†­ÍŪЕŌë4ũKŅq}ŒĀŽj˜0ĶÐđē3ĻCĩ ūīU—ō>ˆFųč$nY)žIOØįFũ%E ņ*Ÿ6ŋdðe^;ôCÏJWŦÄ#žŠ$þü‹ƒ?ĸíˆvLTŋjæą4S}HAąƒŌ™9‹)ÞÁŌdĢYÔđÉ C9‚TE  MðĖĩæÆ7ÕáķI(hf`ïüþ:iĶâÖ_ânžs@ëŒÓ mētNՎ~Ž(–ædĖKĀ™Ũ̘\đ‘ābρåôÛøČwS·H–]›YÛŨ˜•QÞn=gūS\Dq?sčŧN'#L~čĀ}ēt§ũB]oĒŠÎ]ļĘ<ÖŠŠ|K,eÂó“Á7•kŧ áÍödåV89 %\õēÃ4ė„öbxšĪ„!)RéĨô�PW9ģi•"Ģ`yÝŸ!|Ä|wšôĢât,čļúÕį‹ā.dÜ>8同č͎čäd‹ÐGüœŒ–`0jÆĐ aîėH å ūZ<ßΧzzÉzčր―qŧuëā.R6†d‰=‰Õ uëô-06~ZņNTER|zpĶU—LbOϑŸŒYč” ÉŲũ|ã(,2ē–°Ķ.ķI–Ÿ}d5E–’]o“ģšđ\W­f%3(#bGZ’â °ÆÞēį‘@Ó§z.yëŋWÛNĄŅ›ŊAĐē^)šw!Ddį�NZîĄũQrõiŊ]i0‡zâíÓM=6 Ę%Ø 2·Ï*ÔIÚ:E�Đš!‰ĶZø―īžĀõÅϒD>ÓKõģĀ%ÖTؘĸ"ŽæjfJĪ"ũœðÔTК&“Œ—íˆĮé\ÜÍŲũŸ57%Č8ĸn›ÆÔKŒÆŒįb ÝÜ&õ‘Ē™FÁÕæÐ #ÚÅEϘ4›ÏęE~Iš;r"ŨĸpâÝlЉĢÔEupĶmMV5cØÉPý)š—ĢDÛĖø&ņøŲrį3*čXO.`ݟCģãÜą–ïįq i7yķ(‰ …ļĩ?ÕbũX_Ð ž€nZûüļ6YxęáÄ[‘kGßŧ|ï‘]ðʝ—ÏōG5Ø-#Ï&GÐXûéŊÓu@žëË/3:äÆXSgâĀӋŧ‚ļl‘ ęĨƒ7Ļ€ĻĖX+rä˜_ËۊĄØHĖž―ÍÓĘ? Úš†”å”\]Ęæ1(ę“ðl=uųî&'ëÅëev§ïGPĻÆŸģmÓý‹b;DëþO3vs„…L‰wšþ–į€öE_8ĶßúrČ ŠÏÜĄ,%e`'Ĩðé7Føþ{Jq&xHIWĨøz@'D`$ÈÆĒ$‡~ĩųÄûc<ųëķÐēž3ÉĪ6øû=Œëˆaý§K+ûÜ9ŠÄí°S{oԆÛ.mŨÕ8Ÿ]•Ē˰ĶčõĶMw‰BÝšŊC\“`‰įWÕY™ /]6öš=ú\ōOï„ĸ]-OÆðÏŪ@˜{ųDŒbŠÜÁ[?ŽÞ•§gđ(cCß­­ŅHNéĢ―Đč6ˆžļž/3O|Ė#sļFēĸ8Õ�ۚļQčū•°Ąš*‰'ë™hˆqÔ#–ÄïČ>G›ŊČĸqÎl쿀Jþā‘ĄöϗŽũĘ3Į Â^uĢNHcÛÐEFāĄŊīŠđ-#M tąÁ™LÕIÁ€·þ™—L�­”7üWöýĮŲ|dŠFĨ(>,§Z%ï%\âĄéq�šRïŸ)―.| ß\šMӍ+Icdógy7~–Vfžęð`ûųVįFŸņŲüUŅfĩXeÍÄđŽąR*~č‚ūÕîâé›F^+ë(GKŅqßŪäVōŋŠ- n,q<ŽŲŊd{ā' ›X.SŽÓïŠÃÃÝŧČÄ<·B:Â@ĐpJĶ1 MFE%õ9ûēƒļ‚0ÖŪBŧ†`"šô&ÄŠÞŲs‹š{ݙ]ëJŦ3+‡…5eÂDČZ—]Ú‰UƒĀŸWĩô Õ#ÝĢ-õu ­ÔˆÛÄ +9V8$ëX,h†jÕ2°\xžžîŠLoĄņãsÐXHíYĻ·5æüĮ+ƒƒŠLbÝáųėã; &zĶv\-ĘBøïös?ž‘6/eQmdf)ė§åŌ\Åė-^ðËã.˜ë€ŌŽLcL‰ĸ]ܚŸRWņ;=°a|žÂõËFđ傧ųrję›Y9—"Ûa•nå _°E!,…~qÓ“`%^ŽīaŲîL9’jmNmó4kŪzF8Ēó}Ē-4güĒ4˜ÎØ>?1Ú{žNÍļÄ W!Ļ―Đt,b2F1iˆ"1)ąNŠļ‚cĩĖü­0îĸjýĘđ*Xî …I=Ē𠟂e°%‰Q5ņŽęԒš™ÅŲĮ`kæ(€ëˆ Qõ–æ§4jâÚn'wŸ8@`Óãå y<ŧāëJ!Ųtąîu*–ÚĨļ*PĐÆd Ýŧņ°ŧ8ŨwŸēÜUíîėÝŠo2ūŋ4}āïdCaį4‘8lœ‹Ó37ÚČó;ZnwWmõĻÐ1æÄĒ–a ūņc)kÎ ĨOŠŽ°ģ=Ž<[ŋ2ĩ7šFj0�ðYą™$ûeW}-ĀFVj34Ÿm=kęî�YU““îëĸ4T`öžæõÝVš%'ø=ũ,}øîĖãsūÛC)/€+ˉ[”ƒ=#5“D<ĮÃCmu‡Ō"Pŧėņí ÁĢ)rč%Š=Ŧå]ōVW !ö°ÎaßÂëóŲ0Z…TđÓčNöģÜŽĄõãžļqũĮĢũ(3aiîāï°Wvû$$ÂK]įĨVÏæ8ûģđÃá ôšö#h(įßmúhœû &øĒŅōc―hp ņSéÜ― Ũ!@ãKļī•ČOó’8Ö?ëĒÃL–m$ĸOĄ˜ ð™GöŪŠÛõëyũĮLŨļÉ"7”ôO0øÛķmRýZfÖ,‚Dû;æ-3ôíņsÂl…ū`üwÔ# r·šhÃvÖ3Ō!vAŨ…ŋ ōkO3œ�ïO…Ā8Pޕ!6]ėq§Ó ØðĄ~ó“ÏÖeā;7ĄHĩ°lýÔMoąÎ°øĶÄÛu5“3ķ1J”?ŌŋÁ@ @  ‚��A�AõqōĸŲ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/installer.ico����������������������������������������������������������0000664�0000000�0000000�00000061176�13253666515�0021325�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �00���h��–��� ���č��þ�����(��æ ��00����Ļ�� �� ����Ļ��ķ������h��^"��00��� �Ļ%��Æ'�� ��� �Ļ��nM����� �h��^��(���0���`��������������������������������������€���€€�€���€�€�€€��€€€�ĀĀĀ���ĸ��ĸ���ĸĸ�ĸ�������ĸĸ��ĸĸĸ���ww�����xx‡wwp�www�ˆĸø‡ww��xˆˆˆw�xˆĸĸĸĸ‡wwččøøøˆwˆˆĸĸĸĸøˆ‡~ŽŽĸøpˆˆĸĸĸĸˆˆxččččøĸĸĸ‡ˆˆĸwĸˆ‡xŪŽŽĸĸøpˆˆĸ""'xwŠĻčččøĸĸĸĸ‡ˆˆĸ""""zŠŠŽŽŽĸĸĸũˆˆĸ"""'zŠŠĻččøĸĸĸĸøpˆˆĸ"""'ŠŠŠŠŽŽĸĸĸĸĸpˆˆĸ**"'ˆŠŠŠĻĸĸĸĸĸĸĸ‡ˆˆĸĒĒ"gˆˆˆŠĸĸĸĸĸĸø‡ˆˆĸ**"xˆˆˆˆĸˆˆĸĸ‡ˆˆĸĒĒ"xøøøøp�ĸøøøũˆˆĸ**"xøøĸ‡ˆˆ~þĒĒĢxøøøĸøˆĸøøøũˆˆï*Šđxø‡ĸ‡ˆˆ~þŦ››xøøøĸĸp�xøččøũˆˆïđđđwĸĸĸøˆþŽŽ‡ˆˆ~þ›››wĸĸĸĸĸĸĸĻčččįˆˆïéđđ·ĸĸĸĸĸĸøŠŽŽŽ‡ˆˆ~þþþþwĸĸĸĸøøˆŠĻččpˆˆïïïïîxĸĸĸĸˆŠŠŽŽpˆˆ~îîîîîįĸĸĸøøøøˆŠĻįˆˆ~îîîîîįĸĸˆˆŠŠ‡ˆˆ~îîîîîîxĸĸøøøøˆŠŠpˆˆ~îînîîîįĸˆˆ§ˆˆ~ææææææîwøøøøøøˆpˆˆ~nnnnnnngxˆ€ˆˆvæ��æææææwwˆˆˆ�ˆˆ~gĸø���nnngwwpˆˆxĸĸĸĸø���æææpˆˆwĸĸĸĸĸĸĸĸø������ˆ‡‡wwwxĸĸĸĸĸĸĸĸø‡wwwwwwwwˆĸĸĸĸĸ€ũwwwwwwwwwxˆˆ‡xxĸũĸĸˆwwwwwwx‡wpĸũĸĸĸĸø‡wwwwĸĸ€ĸĸĸĸĸĸĸĸĸĸøĸĸĸ‡ĸøĸĸĸĸĸĸĸĸøĸĸĸũĸwĸĸĸĸĸĸĸĸĸøxĸĸĸøpwˆĸĸĸĸĸĸĸĸøxĸĸĸĸpwwwˆĸĸĸĸĸĸĸøwĸĸˆ‡wwwxĸĸĸøqxˆˆwqwwwwwwwĸĸĸĸĸ��þ�øĸ��ü�āĸ��ø��€�ĸ��ð������ā����?��ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā����?��ā������ā���ĸ��ā���ĸ��ā���ĸ��ā����ĸ��ā���ĸ��ā���ĸ��ā���ĸ��Ā���ĸ��Ā���ĸ��€���ĸ��€���ĸ��€���ĸ������ĸ��0���ĸ��ĸ��ĸ��ĸþ�8?ĸ��ĸĸĸĸĸĸ��(��� ���@��������������������������������������€���€€�€���€�€�€€��€€€�ĀĀĀ���ĸ��ĸ���ĸĸ�ĸ�������ĸĸ��ĸĸĸ��wwp����‡ø‡wp��xˆw�x‡ĸĸĸ‡wxˆøø‡ˆ‡ĸĸĸˆxŽĸpˆ‡ø'ˆˆxččøøĸũˆ‡ø""'ŠŽŽĸøpˆ‡ø""jŠĻččĸĸĸ€ˆ‡ø""xŠŠŽĸĸĸĸũˆ‡ø*"xˆŠĸˆĸĸøũˆ‡øĒĒxˆˆø�ĸˆˆ‡Ž*#xˆðˆøøøˆ‡čĒđxøøðˆþˆˆ‡č››ĸ�ččøˆ‡ŽđđxøøĸøúŽŽˆˆ‡î››xĸĸŠĻčįˆ‡îîé·øĸĸˆŠŽ€ˆ‡îîîîĸĸøøŠŠpˆ‡ææîîxĸĸˆŠĻˆ‡nnnnįĸøøˆ ˆ‡ā�æææw€ˆ‡ĸ��nnwwwˆ‡ĸĸĸĸ��ææįpˆwwwĸĸĸ����wũwwwwxĸˆˆũwwwwwxˆ‡pĸũĸĸˆ‡wx‡€ĸĸĸĸĸøxĸũwĸĸĸĸĸĸũĸĸũwĸĸĸĸĸĸĸũĸũwwwwĸĸĸũø‡wwwqwqøĸĸðĀĸā��?Ā��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��?€��?€��€��ĸ€��ĸ���������€��ĸĀaĸ(������ ��������������������������������������€���€€�€���€�€�€€��€€€�ĀĀĀ���ĸ��ĸ���ĸĸ�ĸ�������ĸĸ��ĸĸĸ���wpwpĸ‡ˆ‡"xïøpĒzŽĸpŠxĻ�ˆ€đxˆp›úþpŽxĸøĻčįĸø€ŽŽwwĸĸˆ€wwwwpxũˆˆˆðĸĸøwwwwwwᇇøĀxŠ€ĸĸ€�ũ€�‡ø€�xˆ€�ˆĸ€�ũ€‡ø€xˆ€�€ˆ€‡Ž�xˆ�ˆ�ø(���0���`�����������������������������zf‚�����ĸðĸ�ĸúĸ�ĸýĸ�ĸÞý�ĸËõ�Œ‡ˆ�ĸÅÎ�ĸžÅ�ĸĐģ�ýĖŌ�ĸģđ�ĸäæ�ĸģķ�˜op�ôšŧ�ĸ™™�ĸ››�ō““�ĸžž�ĸĄ �ų�Ė‚‚�ĸĪĪ�Ũ‹Š�ĸ§§�ųĨĨ�ĸŠŠ�晙�™ff�ĸ­­�ōĪĪ�•ee�ųŠŠ�‰^^�ĸ°°�Īqq�ĸģģ�yUU�ĸĩĩ�Žzz�Ÿqq�ĩ‚‚�ĸđđ�ōŊŊ�ĸšš�ŒŒ�Ķyy�üđđ�“mm�ĸūū�ĸĀĀ�ĸÅÅ�ĸĮĮ�ZFF�·�ĸËË�ĸĖĖ�ĸÐÐ�ōÅÅ�ĸŌŌ�ĘĶĶ�ū�Ŋ’’�I==�ĸÖÖ�xee�čÄÄ�ĸŲŲ�ĸÝÝ�Ō··�ĸââ�ndd�ĸéé�ŲÆÆ�úįį�ĸîî�ôãã�ÞÐÐ�æŲŲ�âÕÕ�ĸõõ�ĸųų�ĸüü�JII�įåå�VUU�üûû�ũöö�ŌŅŅ�Ÿlk�Ŧut�ԝ›�Į“’�ųÁĀ�ųÆÅ�ųËĘ�óÓŌ�Ģol�ĩ|�ŧ†ƒ�‰fd�­ƒ�ÚŠĻ�æ―ŧ�öÏÍ�îŨÖ�Ōŋū�į°Ž�Îē°�™kf�ĸĖĮ�îäã�Ęķģ�ôíė�ĸĖÂ�đŦĻ�ęÜŲ�ð―­�Ŧub�ĸĖŧ�ôÁŽ�ߎ”�Žy`�ĸĖģ�ĮÃÁ�æäã�ĩ~^�Ė’p�ĸš‘�ĸĖŽ�ÉŠ˜�åŋĶ�ģĨœ�đķī�íķ�ĸř�ėÄĶ�ØđĄ�ũ—�ĸĖĪ�Ėēœ�ĸə�…~x�ëāÖ�ĸʔ�ĸĖ™�ĸ͞�þÓ§�ĸÚĩ�æĖģ�ĸęÕ�ĸðá�îâÖ�ĸųó�öņė�ėčä�Ũ—S�ĸƇ�ĸāĀ�þäÉ�Š�ĸõę�Üx�ĸži�ĸūp�ō·n�æŽi�ĸĀu�ĸÂ{�ę·y�üˏ�áÁ›�ôâĖ�äÞŨ�éäÞ�üēM�þķY�ĸŧd�ûŅ™�įÐē�íÞĘ�ÕÉđ�ų•�ĸĒ�ûŠ4�ōŋu�ũÄx�ûČ|�öϖ�ë˛�ėÓŪ�õÛķ�ĸýú�ëđm�þøî�óíã�―žš�ÃŪ{�ŲØÕ�juH�ēV�ŋÝŪ�M€3�XēF�€Æt�&Œ�š�Ļ�°#�#·0�(Á<�5ÎQ�Hám�[ņ�§ßá�RÍÔ� Đ�IŪü�ŠąŨ�5•ų�Llš�ĸĸĸ�ųųų�ōōō�îîî�ëëë�ééé�æææ�äää�âââ�āāā�ÞÞÞ�ÜÜÜ�ÚÚÚ�ÖÖÖ�ĖĖĖ�ÆÆÆ�ÁÁÁ�ūūū�ŧŧŧ�đđđ�ĩĩĩ�ŊŊŊ�ĒĒĒ�”””�}}}�rrr�ggg�ddd�]]]�===�666�000�����ĸĸĸĸĸĸĸĸĸWúúĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúûWUüþüúúúĸĸĸĸĸĸĸĸĸĸĸĸøøúúúøøĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúũëZóúWUüýþWúúĸĸĸĸĸĸW7'''''7AþUĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúöíVÂÂÂÂČÆöũûWUýþüøĸC#[/>iiD>8!#7þWĸĸĸĸĸĸĸĸĸĸĸĸųôZZSSSSSSSS~ǐųWU'%]469=EFHJNl0#AþĸĸĸĸĸĸĸĸĸĸîZíí›››››››››˜““”Į!d"(.469=EFHJMTr7þĸĸĸĸĸĸĸĸĸõėņõï°ĢĢĢĢĢĢĢĢĢ”ˆ[$(.469=EFHJßßq*7þĸĸĸĸĸĸĸĸõóõîZ°ĢĢĢÍÎËœĢĄŒc$(.469=EFMßßßq*7üĸĸĸĸĸĸĸõííôņ°™™™ÐÐÐÐÓÍĘĮ\$(.469=ETßßßßP!AWĸĸĸĸĸĸõïöóZ‘™™™ŅŅŅŅŅÐÐÐ $(.469Fßßßßßßr#þĸĸĸĸĸĸõîZëČŊ˜˜˜ŌŌŌŌŅÐÐÉe&$(.46Jßßßßßßß07UĸĸĸĸĸõęęęëŊ˜˜˜ÓÓÓÓŅÐÐfm,& .5ßßßßßßßßK#þĸĸĸĸĸôéęęęŊ˜˜˜ÔÔÔÔŌÐÏ%53,&  ßßßßßßßß0AũĸĸĸĸôčéęęŊĄĄĄÕÕÕÕŌÐĖ/953,& ßßßßßßß>'UĸĸĸĸôįčéęŊĄĄĄÕÕÕÕÓÐÉ]:953,&3 ßXYYYYO#þĸĸĸĸôįįčéŊ   ÖÖÖÖÓÐÉi=:953,&H d*gKYsssssssþũĸĸĸôæįįčŊ   ÖÖÖÖÔÐÉi=;:953,MØ „*sœqqqqqqq)Aũĸĸĸôåæįį‘–––ŨŨŨŨÕÚ�m:::::::Rmؖ–ksvvvvvvvgAũĸĸĸôååæį‘–––ŨŨŨŨŲÝ�]:::::::M *ؖŠB$1_`ajbgAũĸĸĸôåååæ‘–––ŨŲÛÛÛÝÞ^::::::;MK[d[ 6$(.469+Aũĸĸĸóäåå呕••ÝÛÛÛÛÝÞ)::::EMTß</ R$(.46%Aĸĸĸĸóãäå呕••ÝÝÛÛÛÝÞ`=JSßßßß3$(.WĸĸĸĸóãķšĀ‘ÜÛÝÛÛÝÝ�Gßßßßßßßß $(]#ĸĸĸĸó•ļļš””””””·ðÜÞÞ0ßßßßßßßßßB &$e'ĸĸĸĸĸōžļļĩš“““““““““‰cKßßßßßßßR:953,&ĸĸĸĸĸōŧļĪĩš’’’’’’’’’’‚*Yßßßßßß =:953,&'ĸĸĸĸĸĸņŦĪĪĩšŸŸŸŸŸŸŸŸŸŸŸ‚\8ßßßßßTFB=:953,&[ĸĸĸĸĸĸņŦĪĪ—ŪŠŠŠŠŠŠŠŠŠŠŠŠˆc?ßßßßRHEB=:953,&\fĸĸĸĸĸĸĸņŦĪ­âŪĶĶĶĶĶĶĶĶĶĶĶĶĶĻo?ßßßM HEB=:953,&"\fĸĸĸĸĸĸĸĸðķ°îėŪģģģģģģģģģģģģģģĻxgsTMJ HEB=:953-\2ĸĸĸĸĸĸĸĸĸðZõôåŪēēļđđššąēēēēēēēē€nNMJ HEB=:`^[@ĸĸĸĸĸĸĸĸĸĸïëëîîŪąđīÅÁūžšđļđšššąąąž|8nDDbih/[gĸĸĸĸĸĸĸĸĸĸĸĸïėõôæŪš―YYááââããĀŋŧąšļđ𹹞€x!!ffĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸïęįāYĄģāāYYááââããäåæįčĩŋÃąšļđđđøüúúĸĸĸĸĸĸĸĸĸĸĸĸĸîXāāāķÄXāāYYááââããäåæįčééęëė·­ÃõúWUøĸĸĸĸĸĸĸĸĸĸĸîXXãė?[8?GOQYááââããäåæįčééęëėėėZZíôöĸĸĸĸĸĸĸĸĸĸĸĸîáęėė0c%%\\))8?GlQVãäåæįčééęëėėėZôôĸĸĸĸĸĸĸĸĸĸĸĸĸîęęęlc%%\\))dd++ee//>nléįčééęëėZõöĸĸĸĸĸĸĸĸĸĸĸĸĸņįįįሁed))dd++ee///^^^OäáYYáâöýĸĸĸĸĸĸĸĸĸĸĸĸĸĸōäää°ģĨП’“Œˆ{{]^///^^^^QáāßSMJJôýĸĸĸĸĸĸĸĸĸĸĸĸĸðæâââÁĨП’“””ƒƒƒ}zwmmhvLHFFHJMZûUĸĸĸĸĸĸĸĸĸĸĸĸņYYYYŽĐŸ’“””ƒƒƒ}}}yyyti=BF JMRSþĸĸĸĸĸĸĸĸĸĸĸĸðX› īП’“””ƒƒƒ}}}yyytt†DF JMRSßíUøĸĸĸĸĸĸĸĸĸĸððōēĨП’“””ƒƒƒ}}}yyyttpøu JMRTßßáøþĸĸĸĸĸĸĸĸĸĸððô§ĐŸ’“””ƒƒƒ}}}yyyttpDûîOMRTßßßßōûúĸĸĸĸĸĸĸĸĸĸĸððōôr††ŽŽ‹……ƒ}}}yyyttppGWĸôRTßßåZðōĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸððððððððð‡luunniiDuøĸĸðîîîîĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸððððððððððĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ��þ�øĸ��ü�āĸ��ø��€�ĸ��ð������ā����?��ā������ā������ā������ā������ā������ā������ā������ā������ā������ā�������ā�������ā�������ā�������ā�������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā����?��ā������ā���ĸ��ā���ĸ��ā���ĸ��ā����ĸ��ā���ĸ��ā���ĸ��ā���ĸ��Ā���ĸ��Ā���ĸ��€���ĸ��€���ĸ��€���ĸ������ĸ������ĸ��Ā��ĸ��þ��?ĸ��ĸĸ�?ĸĸ��(��� ���@�����������������������������vfˆ�����ĸøĸ�ĸüĸ�ą°ą�þæý�ĸôþ�ĸÍņ�}Ž�öĩÎ�ýÄŲ�ØÃË�õōó�óāä�ĸôö�ė§°�þÓŲ��ݔ˜�ĸËÎ�ĸáã�š Ą�ĸœ�Į|~�ü§Ļ�ĸŠŦ�Ɔ‡�ĸÂÃ�ԕ�ĸÔÕ�ĸ™™�ĸž�ĸžž�ĸĄĄ�ĸ§Ķ�·zz�坝�ĸą°�ĸēē�ýēē�ĸķĩ�°~~�Ĩww�rRR�ĸšđ��—nn�úļ·�ķ……�ĸū―�ĸÆÆ�ëķķ�ÖĻĻ�F77�É  �ĸĖĖ�ĸÐÐ�ûĖĖ�|hh�ĸŨŨ�ĸÛÛ�ĮŦŦ��ûÚÚ�QGG�Íĩĩ�4//�ĸčč�ÚĮĮ�ōßß�-**�ĸïï�ÛÏÏ�äÛÛ�čßß�ÓËË�wss�ĸųų�ðęę�ßŲŲ�ƒ€€�ÕŅŅ�jhh�ûøø�ŅÎÎ�CBB�ĸýý�ýüü�ÞÝÝ�ŌŅŅ�ʖ•�ĸÄÃ�ĸËĘ�öĘÉ�Ԋ†�óÄÂ�øÖÔ�ņÐÎ�ēvq�ĸÎÉ�Õš·�ą|u�Μ•�Ĝ–�ýËÂ�óÁ·�ûũö�ū‘…�øÅ·�ðÅš�Õ·Ŋ�ÛĻ—�ĸÍŧ�ëÂģ�ĸųũ�đ{�§‹�―…i�ĸÍī�õïė�ëåâ�Ӟ‚�áĀŪ�ðéå�æķ•�âØŅ�ĸÎŦ�ėčå�ƊZ�öØ�ĸÍĨ�ŌÅš�Ȁ=�ĸïā�ęãÝ�ĸ͚�ĸ͝�óɟ�ōĖĶ�‡s_�Ë·Ģ�ņāÏ�éßÕ�íėë�Ýq��þÂ�ā­x�ĸɎ�þĖ–�ÛĮē�zqg�ûęŨ�þžj�âĻb�ĸĀv�ĸÄ{�ĸÂ{�ĸŌž�ųÞ―�ĸæČ�ó‡��ĸąH�ýļZ�ĸšd�é°b�öʓ�ũÖŽ�üÝķ�þöė�þĐ*�ė/�þŪ>�îŦI�ôđc�ôŋq�îĮŽ�åŅģ�―ī§�ōūl�ōʍ�ÆÂŧ�Ųě�ÚÏš�ŨÓË�ĖËČ�{{U�ĸĸú�įįæ�ŲáŽ�ŅßŪ�ÉÞ°�Îâš�A|,�°ŲĨ�/š�€Įx�<Ž5�™�Ŋ�đ-�(ĮA�5ÕU�Jčs� …B�Yũ“�ĨĐĻ�ĒĖĖ�üĸĸ�ęëë�ÛÜÜ�FĘŅ�äįč�íïð�ęėí�áäæ�JŽų�ˆķÜ�1ō�ĮĘÍ�ŧ―ŋ�øúü�õũų�áãå�ÚÛÜ�ŲÚÛ�ŨØŲ�éíō�öųý�ßáä�ÔÖŲ�ŲÞæ�îóü�ÉĖŅ�DUĢ�üüþ�ŲŲÚ�ĸĸĸ�ééé�âââ�ÚÚÚ�ŲŲŲ�ÔÔÔ�ŌŌŌ�ÐÐÐ�ÎÎÎ�ËËË�ÁÁÁ�···��sss�ppp�\\\�MMM�999�&&&�����ĸĸĸĸĸPüû.ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸPLûUBFUûúĸĸĸĸĸR@55BýûĸĸĸĸĸĸĸĸĸĸR͏ XöøRüýFþü:+.46*+5þRĸĸĸĸĸĸĸøÛņššššrŒt–+#-28<CE=+FUĸĸĸĸĸĸÛöũxĻĻĻrV…s^(,7<Cėx.BUĸĸĸĸóũũö†ĻŋÄÃÁĩŊy!&,[7VėN.FúĸĸĸóÛö†ĻūÅÅÅÅÂe !&,[\CėėėI+þĸĸĸóöá뎅ūÆĮÆÅĀ$(" !"%,[Mėėėė5ųĸĸóõîߎ—―ĮČĮÅđ,%" ėėėėėN+ýĸĸóõîŅŽŸ―ČÉĮÅt,%" ėėWWWtF>ĸYôŧXŽŸ―ÉĘČÅg\(1eedSwwwj=B>ĸōóŧ䎞žĘĘÉË67\8Ξ) †††{A5>ĸōYíލ§žĖĖŌŲ777\;4ÍĶe/_]`n5>ĸņōÐӍĶķŌŨŨŲ\\\;C6b2"&,\45>ĸņåԆƒŲŲŨŲ�hVė  &ZUĸĸņ·Ē­ˆŠÛØØŨéAėėėėė !'*Lĸĸåĩ Ī”‡””‡‰$) ėėėėC2(%" !$+ĸĸĸáĩ ™“““““““^ėėėV<8\,%" #Pĸĸĸáĩ’šœœœœœ›‘uAėėr;8\,%^:ĸĸĸĸá•|ðžĢ˜Ģ˜˜˜˜˜˜u=ęrC;8721^.ĸĸĸĸĸáōÛč§ĄĐŦŦŦŦŦĄĒĒ€0 C<930>ĸĸĸĸĸĸðYÛÚĶŦ—wĶģŪ­ŦĐĐŠ„uk0.>ĸĸĸĸĸĸĸĸðáî㧟ÏÜÝįįâŧŽ}°ŊēŽŠŠ‹BýüĸĸĸĸĸĸĸðíėÐAADINÝ ÔÔÕÓÖäææáķīøøĸĸĸĸĸĸĸïXîå*.**)6AD}IIîXïáåāöĸĸĸĸĸĸĸĸõXÖķuee*))))006OÕÔíŧÍüĸĸĸĸĸĸĸĸĸÛÔâĨĢ‘”||offZZZJjGGþĸĸĸĸĸĸĸĸļÐãw›‘”ˆˆ‚~~vvvlia<<GNüûĸĸĸĸĸĸĸļw§“‘”ˆˆ‚‚~~vvphh?CGMėøþĸĸĸĸĸĸĸļąĒ‘”ˆˆ‚~~vvpphc_ûHVėėXųĸĸĸĸĸĸĸĸļYƒdnzzqqmhcdøõNíðõôKĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸKKKKQKTĸĸKKKKĸĸĸĸĸĸĸĸĸøĸĸðĀā��Ā��Ā��€��€��€��€��€���€���€���€���€���€��€��€��€��€��€��€��€��€��?€��?€��€��ĸ€��ĸ���������€��ĸĀaĸ(������ �����������������������������éčð�§ĄÃ�íéî�����ĸóĸ�ëäę�äÕß�|~�ōūÜ�ÆĨļ�üŧÐ�æĮŅ�ĸóũ�ŲĖÐ�ĸōö�ņāä�ýÁË�ĸËŌ�ĸąš�ĸ·―�˧Š�ĸĪ�ĸÉĖ�ĸÛÝ�ėÚÛ�ũĐŦ�ĸģī�―†‡�ĸÉĘ�ŞŸ�íËĖ�ĸÝÞ�ĸŸž�ĸ  �ĸĄ �üŸŸ�ĸĢĒ�ĸ­Ž�ē||�ĸģģ�üķķ�ĸū―�ŧ�‚cc�`II�ˆii�ĸÆÆ�·‘‘�}}�N>>�ĸÎÎ�ÍĨĨ�tt�ûËË�oZZ�|ee�=22�;11�ĸŲŲ�ĸęę�ÕÃÃ�ÔÃÃ�þėė�þîî�ĸðð�ÔÏÏ�ĸûû�îęę�ėéé�^]]�ĸþþ�úËĘ�ĀĶĨ�ģƒ€�ĸÏÍ�Į°Ŋ�ÉŊ­�ÛĖË�Ɔ�ėÄĀ�čâá�į°Ĩ�ËĶž�ĩ€t�843�ėäâ�ĸÔĮ�ĸõō�qfb�øõô�ė·Ģ�šĢš�ĸÐŧ�įūŽ�ãÐĮ�øôō�ĶĪĢ�ŨÕÔ�ō― �Ō·�ĸÎą�Č­�ØÄļ�âķ˜�øÄ�ĸÎĐ�íåß�îÁ�þĖĄ�ՙ]�öɛ�ĸŌĶ�õŌŊ�éåá�BA@�üķh�ýŋ�ûĮŽ�㷈�ČŠ‰�ü͓�ÝÂĢ�ũ°P�ōŦQ�ĸđ^�ĸÂv�Ō§m�ô˖�ĸĀh�üŋh�úÄx�āąp�áđ€�öÚģ�ĸĮq�û˂�ØŅÆ�óÜģ�įÔī�óáū�æâŲ�ņåÉ�ųóá�ðëÓ�ĸýō�œšc�Ÿt�âōÖ�)Š!�!­%�� �ą,�1ÃB�Sæk�]āĩ�lmm�§ĻĻ�6ĶĘ�a­õ�ęņø�žŋÂ�ÕÖŨ�:–û�âäé�ąąē�ĸĸĸ�ÆÆÆ�ūūū�����“““�ŽŽŽ�ŒŒŒ�„„„�}}}�vvv�sss�nnn�bbb�WWW�PPP�KKK�:::�444�///�...�---�ĸĸĸ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ĻĻĻąģŊŊĻĻŊŪ°ŊĻĻĻĻĻŽ`ErT,-468đĻĻϜa“ŽkN.:><1ļĻĻĪA•”‘!%);Ĩ=9ŊĻĪP˜–’" ĨĨ7ķĻĪU‹™—e KY_H·ĻĪC‰š J[5G3ĩĻĪ…pžĒWB 'ŊĻ~{onyRĨ@$#+ĻĻwv}|€sc2(&ĻĻĻ ˆ†‡‚zmg]/0ĻĻĻĻĄj^�DqŒŠ„ƒXĻĻĻĻĻMfSI* ŸĢ›ĻĻĻĻMˆxthbZQO;?ēīĻĻĻMulid\VLFͧĻĻĻMMMMMMMMMMMMĻĻĻĻá‡õîĀ—―€Įŀ�,%€� €�ė€�ėė€�ýĸ€õ―€Įŀ,€��WW�F>(���0���`���� ����������������������������������������������������������K���p���m���X���?���+��������� ������������������������������������������� ������������������������ ����������������������������������������������������������������� ���&888’ZZZėHHHá666Ō·���œ���Ž���y���`���F���1���#���������������������������&���@���]���p���y���z���y���o���Y���?���&�������������������������������������������������������� ���3HHH·zzzĸŲŲŲĸÏÏÏĸģģģĸĸbbbûSSSî===Ú"""ŋ Ķ���”���€���g���Q���:���(���������(���S0 ™_@@ÍvNNä|SSë|RRė|RRėtMMįR66Ō(ļ���š������S���'��� �����������������������������������������LUUUŨ•••ĸÎÎÎĸæåäĸĸýúĸĸýúĸĸýúĸĸýúĸŲØÕĸ―žšĸ—–•ĸ{zzĸYYYóCCCß111ĖŊ���š���‹���t���gU99­Œ^^óœllĸŋ‹‹ĸÏĒĒĸæŧŧĸæŋŋĸæÃÃĸƧ§ĸķĸ–ffĸŠ\\ũF//Ņ���Ą���z���C�������������������������������������;;;UeeeįąąąĸŅŅŅĸÐÐÐĸįäâĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸėčäĸĮÃÁĸŠĸ…~xĸdddûOOOé?77Ô}SSęĶppĸؚšĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸããĸĸččĸõååĸŲūūĸĶxxĸ‚WWó&Ā���’���T������������������������������SSS)}}}óĮĮĮĸÓÓÓĸËËËĸĘĘĘĸčäáĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸįÐĸĸĖ™ĸĸĖ™ĸĸΟĸÆ ƒĸ–ffĸžĸųŦŦĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸããĸĸččĸĸîîĸĸûûĸĖģģĸ™ffĸF..Ó���˜���V��������������������������```HĄĄĄĸÖÖÖĸŧŧŧĸĸĀĀĀĸčäßĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸóįĸĸĖžĸĸʙĸĸʙĸė·ĸŸliĸŲ‰‰ĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸããĸĸééĸĸĸĸĸĸĸĸĸėââĸŸppĸF..Ó���—���S����������������������aaaHĢĢĢĸĩĩĩĸĄĄĄĸÆÆÆĸÓÓÓĸéãÝĸĸóčĸĸóčĸĸóčĸPĩIĸ€ÆtĸŋÝŪĸöņéĸĸóčĸĸãĘĸĸșĸĸșĸų–ĸĶrlĸĖ€€ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸîîĸĸĸĸĸĸĸĸĸĸĸĸĸėââĸŸppĸF..Ó���Ž���A��� ����������������bbbHĪĪĪĸÍÍÍĸĖĖĖĸ°°°ĸŧŧŧĸéâÛĸĸņãĸĸņãĸĸņãĸĸĸĸĸ4Ū1ĸ`ŊCĸēVĸŋŧsĸƐ|ĸģvvĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸûûĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸæŲŲĸ’aaû&Ā���y���'���������������cccHĨĨĨĸÂÂÂĸ™™™ĸĩĩĩĸŌŌŌĸęâÚĸĸïßĸĸïßĸĸïßĸĻĸĻĸĻĸĻĸ Ķĸ�™�ĸ�™�ĸ“ ĸ™ffĸōĪĪĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸÝÝĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĖģģĸ‚WWó���Ą���S���������������dddHĶĶĶĸĮĮĮĸŌŌŌĸŲŲŲĸØØØĸæßØĸĸėÚĸĸėÚĸĸėÚĸ°#ĸ°#ĸ°#ĸ°#ĸŠĸ�™�ĸ�™�ĸ`y@ĸŋ……ĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸëëĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĶyyĸF//Ņ������(�����������eeeH§§§ĸÜÜÜĸÜÜÜĸÛÛÛĸŲŲŲĸâÝŨĸĸęÕĸĸęÕĸĸęÕĸ đ0ĸ đ0ĸ đ0ĸ đ0ĸĐĸ�™�ĸ�™�ĸi`ĸė­­ĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸģđĸĸšÄĸĸššĸĸÄÄĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŲÆÆĸŠ\\ũ������F��� ��������eeeHĐĐĐĸÞÞÞĸÜÜÜĸÜÜÜĸÛÛÛĸãÝŨĸĸčŅĸĸčŅĸĸčŅĸ(Á=ĸ(Á=ĸ(Á=ĸ(Á=ĸ­ĸ�™�ĸ&ŒĸĶssĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸ―ÉĸĸÓôĸĸĖĸĸĸÏĸĸĸÖûĸĸßîĸĸþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĶyyĸ2""Â���j�����������fffHŠŠŠĸāāāĸÞÞÞĸÜÜÜĸÜÜÜĸäÝŨĸĸåĖĸĸåĖĸĸåĖĸ1ĘIĸ1ĘIĸ1ĘIĸ1ĘIĸą%ĸ�™�ĸM€3ĸŋŒŒĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸĖÓĸĸåĸĸĸŲĸĸĸÄíĸĸÅęĸĸÖúĸĸãĸĸĸðĸĸĸþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÆĐĐĸkGGä���…���$��������gggHŦŦŦĸáááĸāāāĸÞÞÞĸÜÜÜĸåÞŨĸĸãĮĸĸãĮĸĸãĮĸ;ÔYĸ;ÔYĸ;ÔYĸ;ÔYĸ·-ĸ�™�ĸcvFĸĖ››ĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ――ĸĸōĸĸĸāųĸĸĐģĸߍĸԊŠĸōšžĸĸėĸĸĸîĸĸĸúĸĸĸĸĸĸþþþĸüúúĸúũũĸųõõĸųõõĸųõõĸáŅŅĸŠ\\ũ���”���.��������iiiH­­­ĸâââĸáááĸāāāĸÞÞÞĸåÞÖĸĸáÃĸĸáÃĸĸáÃĸDÝfĸDÝfĸDÝfĸDÝfĸ"ŧ3ĸ�™�ĸssMĸæđđĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸááĸĸōĸĸĸšÄĸŋzzĸŸvuĸŊ…}ĸ™ffĸŲÆÆĸĸņĸĸĸúĸĸųöõĸôîėĸôíëĸôíëĸôíëĸôíëĸôíëĸôíëĸ™ffĸ������?��������jjjHŪŪŪĸäääĸâââĸáááĸāāāĸæÞÖĸĸßūĸĸßūĸĸßūĸLåsĸLåsĸLåsĸLåsĸ&ŋ:ĸ�™�ĸssMĸæļļĸĸŌŌĸĸÐÐĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸėėĸĸōĸĸ雛ĸ˜opĸĐäåĸĸßūĸÉŠ˜ĸŸppĸũíðĸĸúĸĸöņïĸïåãĸïåãĸïåãĸïåãĸïåãĸïåãĸïåãĸĐ~}ĸ5$$ļ���E��������kkkHŊŊŊĸåååĸäääĸâââĸáááĸčßÖĸĸÜđĸĸÜđĸĸÜđĸWðƒĸWðƒĸWðƒĸWðƒĸ/ÅQĸ Đĸ|i†ĸæģģĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸōōĸĸōĸĸ掎ĸ–€ƒĸĶęîĸĸÜđĸĸÜđĸ™ffĸîŨÖĸĸúĸĸôíėĸéÜØĸéÜØĸéÜØĸéÜØĸéÜØĸéÜØĸéÜØĸ­ƒƒĸ5$$ļ���E��������lllH°°°ĸæææĸåååĸäääĸâââĸéßÖĸĸÚĩĸĸÚĩĸĸÚĩĸ_øĸ_øĸ_øĸ[ęĒĸOÅÛĸ0ųĸvi†ĸԟŸĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸïïĸĸōĸĸųÏŌĸŸppĸ§ÎÐĸĸÚĩĸėÄĶĸ™ffĸĸģķĸĸúĸĸĸÕÕĸĸ°°ĸüđđĸųÁĀĸųÆÅĸųËĘĸöÏÍĸóÔŌĸŊ‚‚ĸ5$$ļ���E��������mmmHąąąĸįįįĸæææĸåååĸäääĸęāÖĸĸذĸĸذĸĸذĸ\čģĸUÕĖĸOūėĸMģĸĸMģĸĸ0ųĸ`mĶĸƓ“ĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸÏÏĸĸėėĸĸōĸĸĸðĸĸŲÆÆĸŸmkĸģƒyĸŸmkĸĖ€€ĸĸĘŌĸĸúĸĸĸĮĮĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸ쁁ĸ6$$ķ���?��������mmmHēēēĸéééĸįįįĸæææĸåååĸëāÖĸĸÕŽĸĸÕŽĸĸÕŽĸ<Ēĸĸ@ĶĸĸEŦĸĸJ°ĸĸMģĸĸ2‘ųĸNnĩĸŽyyĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸŲŲĸĸėėĸĸüüĸĸĸĸĸĸôĸĸĸåĸĸĸîĸĸōÅÅĸƋ‹ĸ◗ĸĸšÄĸĸîĸĸĸôųĸĸĪĪĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĢrrĸ8&&Ū���.��������nnnHīīīĸęęęĸéééĸįįįĸæææĸëāÕĸĸÓ§ĸĸÓ§ĸĸÓ§ĸ9Ÿĸĸ<Ēĸĸ@ĶĸĸEŦĸĸJ°ĸĸ7—ųĸ0mŌĸ™ffĸųÆÆĸĸŌŌĸĸééĸĸųųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸýĸĸĸįĸĸĸŲĸĸĸÉõĸĸÆįĸĸÐïĸĸãĸĸĸîĸĸĸ―Āĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸõššĸ™ffĸ������"��������oooHĩĩĩĸëëëĸíāÍĸų­:ĸėÓ­ĸėáÕĸĸŅĒĸĸŅĒĸĸŅĒĸŊÜĸ^Ļîĸ<Ēĸĸ@ĶĸĸEŦĸĸ<œųĸ#ißĸ{ayĸŌķķĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúĸĸĸßûĸĸĖĸĸĸÏĸĸĸŲĸĸĸÜõĸĸžÂĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸܟŸĸ…XXï���c�����������pppHķķķĸóÓĢĸĸ™�ĸũ‘�ĸëŅŪĸíáÕĸĸΞĸĸΞĸĸΞĸĸΞĸĸΞĸĸΞĸÏÄķĸđĀÂĸ’ģŅĸZ~ÎĸFVĶĸĶyyĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÕÕĸĸÆĖĸĸÃÏĸĸŋĮĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸŋƒƒĸhFFŌ���@��� ��������rrrHļļļĸũÄxĸũ‘�ĸíˆ�ĸéаĸîâÕĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸřĸĸĶ™ĸĶnlĸŲÆÆĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸööĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸųĨĨĸŸkkĸ4""���!�����������sssHđđđĸōŋxĸíˆ�ĸä�ĸčϰĸðâÕĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸŧ”ĸԌ†ĸŸppĸųõõĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸååĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĖ……ĸ€UUå���C��� ������������tttHšššĸíŧxĸä�ĸÛu�ĸįÎēĸðâÓĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸļĸŽusĸ쌌ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸýýĸĸÞÞĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸō““ĸŸjjĸ:&&€������������������uuuHŧŧŧĸëļyĸÛu�ĸŅk�ĸæĖģĸōâŅĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸųą‚ĸĢojĸŋŸŸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõõĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸųĸŽppĸqKKÂ���)�������������������vvvHžžžĸæģzĸÖ|ĸã―–ĸïïïĸóãÏĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸæ­mĸ™kfĸŋŸŸĸĸĸĸĸĸĸĸĸĸĸĸĸĸîîĸĸååĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸųĐĐĸŽrrĸ„XXÝ���2��� ��������������������vvvHūūūĸîÛĮĸëåÞĸÅÅÅĸÕÕÕĸóâÍĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸæŦdĸŽveĸރƒĸōėėĸĸüüĸĸïïĸĸééĸĸååĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸōŊŊĸŽttĸ‰^^â,8��� �����������������������wwwHŋŋŋĸÓÓÓĸŸŸŸĸ°°°ĸįįįĸôâËĸĸ·ZĸĸĩTĸĸ ĸĸŸĸĸĢĸĸĐ.ĸĸŽ9ĸĸ°Dĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·ZĸõēZĸģaĸ™ffĸ˰°ĸōááĸĸïïĸĸééĸĸååĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸųÅÅĸĖ••ĸŸkkĸ~TTŪ����������������������������������xxxHĀĀĀĸÚÚÚĸŲŲŲĸĮĮĮĸÆÆÆĸõâÉĸĸģOĸĸĨ#ĸųŌ™ĸóíãĸõÛĩĸöϖĸöÃwĸúŪ;ĸýĢĸĸ ĸĸ ĸĸ§)ĸĸŠ2ĸĸ­<ĸĸģOĸĸģOĸĸģOĸߟSĸŽy`ĸ™ffĸđ‘‘ĸŌēēĸæČČĸæÄÄĸōŅŅĸæūūĸŲŽŽĸŋŽŽĸŸllĸ^^ÏnIIY��� �����������������������������������yyyHÁÁÁĸÕÕÕĸŸŸŸĸ°°°ĸäääĸöãČĸĸŽ8ĸûČ|ĸöööĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸîÔŪĸîΞĸōŋrĸö°Gĸų§*ĸĸœ ĸĸŸĸĸĨ$ĸĸĻ,ĸïĄ7ĸϏRĸķ}[ĸĐt_ĸ“ffĸ–ffĸŠccû~VVãpNN§N;;S������ ���������������������������������������{{{HÂÂÂĸÜÜÜĸáááĸøøøĸöööĸøãÆĸþ―^ĸúúúĸøøøĸöööĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸčččĸæææĸäääĸâââĸāāāĸåŅĩĸčȘĸíšnĸņąRĸõĐ6ĸĸœĸĸŸĸĸĢĸĸĨ%ĸoooî���Ķ���€���c���O���1��� �����������������������������������|||HÄÄÄĸûûûĸúúúĸųųųĸøøøĸëÞËĸþøîĸüüüĸúúúĸøøøĸöööĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸčččĸæææĸäääĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸÛÎŧĸßşĸé·lĸŸŸŸû‚‚‚îWWWŲCCCČ666Ĩ���O��������������������������������������{{{IÅÅÅĸüüüĸûûûĸëëëĸÕÕÕĸķĸŸllĸļ’’ĸŋœœĸÐķķĸßÏÏĸãÖÖĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸčččĸæææĸäääĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸÕÕÕĸÔÔÔĸŌŌŌĸÐÐÐĸÎÎÎĸ°°°ĸxxxĖ---2��������������������������������������wwwLÆÆÆĸðððĸÛÛÛĸÖÖÖĸÖÖÖĸĶyyĸĄnnĸĢppĸĨrrĸĻuuĸŠwwĸŽyyĸŪ{{ĸđŒŒĸœœĸŌđđĸÖĀĀĸāÓÓĸčååĸęęęĸčččĸæææĸäääĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸÕÕÕĸÔÔÔĸŌŌŌĸŊŊŊĸggg™���������������������������������������������ssscÄÄÄĸÜÜÜĸÜÜÜĸÜÜÜĸÍĀĀĸĄnnĸĢppĸĨrrĸĻuuĸŠwwĸŽyyĸŪ{{ĸ°}}ĸēĸĸ탃ĸļ……ĸš‡‡ĸž‰‰ĸŋŒŒĸËĨĨĸŅąąĸÕŧŧĸāÝÝĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸŅŅŅĸŸŸŸųOOOt�������������������������������������������������ˆˆˆēâââĸâââĸâââĸâââĸⷊĸŅ“dĸĖ“pĸ―ˆwĸīxĸŽyyĸŪ{{ĸ°}}ĸēĸĸ탃ĸļ……ĸš‡‡ĸž‰‰ĸŋŒŒĸÁŽŽĸАĸđ‘ĸĮ””ĸÛÎÎĸéééĸņņņĸöööĸũũũĸðððĸíííĸåããĸũŸ���9�����������������������������������������������III ···ĸéééĸéééĸéééĸęåßĸýđaĸĸžiĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸõ˜ĸėđ—ĸ㰕ĸÚ§“ĸԟ’ĸ˘ĸĀ‹ĸŋŒŒĸÁŽŽĸАĸđ‘ĸĮ””ĸɖ–ĸâÕÕĸņņņĸųųųĸĸĸĸĸĸøøĸĸððĸĸęęĸĸįįĸŽĻĻĸ­���[����������������������������������������������xxxuäääĸïïïĸïïïĸïïïĸõÚ·ĸĸžiĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸôÁŽĸð―­ĸæģĐĸäąĐĸÛĻĪĸíÝÜĸúįįĸĸããĸĸÜÜĸĸÝÝĸĸããĸĸččĸĸîîĸÖÏÏĸLLLÖ���‚���(������������������������������������������ ĻĻĻÚöööĸöööĸöööĸöööĸüˏĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸäūđĸĸÓÓĸĸŨŨĸĸÞÞĸĸääĸĸččĸĸïïĸĸõõĸĸųųĸ‹‹‹ũ���ž���H��� ������������������������������������bbb1ÕÕÕĸüüüĸüũōĸüáūĸýЙĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸŊĒ ĸäĮĮĸĸÞÞĸĸääĸĸččĸĸïïĸĸõõĸĸųųĸĸĸĸĸÉÉÉĸ000Ä���r��������������������������������������–ØØØĸ···ĸĸķWĸĸžiĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸĸĖÆĸsssãŠĄĄėĸääĸĸééĸĸððĸĸõõĸĸûûĸĸĸĸĸĸĸĸĸōōōĸqqqé���Ž���4�����������������������������������”””‚‚‚‚J’’’Æō·nĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸĸĖÆĸëÂŋĸUUUÆjjj‡ÞŅŅĸĸððĸĸööĸĸüüĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ···ĸ%%%ƒ���2��� ����������������������������������������”””b†‘‘‘ķ’’’ÆŠŸ’ĮēĨ™ĸļϚĸĖēœĸĖēžĸØđĄĸåŋĨĸåŋĶĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸĸĖÆĸĸĖČĸŌĩīĸ###‹���$ĨĢĢáĸööĸĸüüĸĸĸĸĸĸĸĸĸåååĸŌŌŌĸŋŋŋĸ   Øxxxe������������������������������������������������������������������������“““B‰‰‰GŠŠŠX‡ŽŽŽ‰‘‘‘§›—–ÆīĢĮģĨĄņŋŽĶĸĖģ­ĸĖģŪĸæŋŧĸæŋžĸėÂÂĸīĢĢę���=���•••bŋūūĸĀĀĀņĐĐĐɊŠŠ„„„o€€€L��� ����������������������������������������������������������������������������������������������������������������������������”””BŠŠŠG‹‹‹X‘‘‘‡………\��� ���������������������������������������������������������������������������ü�āĸ��ð����ĸ��ā����?��Ā������Ā������Ā������Ā������Ā������Ā������Ā������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā������Ā������Ā������Ā������Ā������Ā������Ā����?��Ā������Ā����?��Ā����?��Ā����?��Ā����ĸ��€���ĸ��€���ĸ��€���ĸ������ĸ������ĸ�������ĸ�������ĸ�������ĸ��Ā����ĸ��ð���ĸ��ĸþ�?ĸ��(��� ���@���� ����������������������������������������������N���s���n���X���D���1���"������������������ ������.���<���@���=���0������������������������������������������&;;;™vvví]]]ä555Ð―§���Œ���y���d���L���5���%���&���C{0 Ē/Ŋ.ąĨ���Œ���s���L���"��� �������������������������&LLLķĢĢĢĸíėëĸôôōĸÝÝÜĸÃÂĀĸ‘ũfffíCDDÝ**+ÆŽ—���„6##›wPPݟqqôĖ–ýŅĶĶþÆ  þ {{ũyRRė=((Ę������p���6������������������BBBoooÅūūūĸÔÔÔĸëčåĸĸĸųĸĸĸûĸĸĸûĸĸýøĸĸûöĸíčäĸË·Ģĸ§”ĸzpeóaJJčķ{{ýĸĸÆÆĸĸÏÏĸĸÚÚĸĸááĸĸééĸōßßĸČĐĐĸvPPëķ������<��� �����������ccc_ūūūĸÁÁÁĸ···ĸëįáĸĸøîĸþöėĸýöëĸĸųöĸĸüüĸĸðáĸĸПĸĸӜĸđ{ĸŅ„…ĸĸŦŦĸĸĩĩĸĸššĸĸÃÃĸĸĖĖĸĸÕÕĸĸÝÝĸĸįįĸĸĸĸĸëââĸ“mmöŧ���~���3�����������iiic···ĸķķķĸÁÂÂĸëäÝĸĸöîĸÎâšĸ<Ž5ĸ€Įxĸ°ŲĨĸŅΗĸïÆĸӞ‚ĸĮ|~ĸĸœĸĸĄĄĸĸŠŠĸĸēēĸĸššĸĸÄÄĸĸĖĖĸĸÔÔĸĸããĸĸþþĸĸĸĸĸņęęĸ‘iiõ ą���k����������jjjbŧŧŧĸąąąĸŋĀĀĸíäÛĸĸóčĸÆÞ°ĸ��ĸ� ĸĄĸœ ĸ/šĸ°zuĸü§ĻĸĸŸŸĸĸ™™ĸĸ  ĸĸŠŠĸĸģģĸĸšđĸĸÄÃĸĸËËĸĸęęĸĸĸĸĸĸĸĸĸĸĸĸĸ娨ĸoJJč���˜���F��� ����kkkbŋŋŋĸØØØĸŲŲÚĸéāØĸĸîßĸËÞ°ĸ °ĸī)ĸ­ĸ�œ�ĸA|,ĸ⚟ĸĸĩĩĸĸ§§ĸĸžžĸĸ™™ĸĸ  ĸĸ§Ĩĸĸą°ĸĸđđĸĸÅÃĸĸųųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸūŸŸþ3!!Ã���m�������kkkbĘĘĘĸáááĸÚÛÜĸčßÖĸĸęŨĸÎßŊĸ―1ĸ)Â=ĸī'ĸ�˜�ĸ{{Uĸĸ―Äĸĸđđĸĸ°°ĸĸ§§ĸĸĸĸ›šĸĸÁŅĸĸËôĸĸÎõĸĸÚęĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸņęęĸnJJæ���‹���.����lllbËËËĸãããĸÛÜÜĸéßÕĸĸæÎĸŅßŪĸ'ĖGĸ7ÐRĸ ū4ĸ ĸ͈yĸĸËÎĸĸÃÃĸĸđđĸĸ°°ĸĸĶĨĸĸŅŲĸĸáûĸøąĖĸôļÏĸĸįĸĸĸõĸĸĸĸĸĸĸĸĸĸýüýĸýüüĸüûûĸІ†ú Ī���=����nnnbÍÍÍĸåååĸÞÞßĸęÞÓĸĸáÅĸÔāŪĸ6Ü^ĸFßhĸ(Č@ĸŠĸĜ–ĸĸÓÕĸĸËËĸĸÃÃĸĸķķĸĸū―ĸĸóýĸė§°ĸ°}wĸą~uĸÓķŧĸĸųĸĸûøøĸöðîĸõîíĸõîėĸûũõĸÉŽŦĸ#°���I����ooobÏÏÏĸįįįĸßāâĸëÞŌĸĸÝžĸŨā­ĸEęuĸTî}ĸ2ÓOĸ …Bĸ˜ŸĸĸÓÔĸĸÍÍĸĸËËĸĸÃÃĸĸŅÐĸĸôüĸƆ‡ĸĒĖĖĸũāūĸ­‚|ĸũîōĸũóōĸęâßĸëâßĸëâßĸðéåĸÏĩīĸB++Â���M����qqqbŅŅŅĸéééĸâãäĸíÞÐĸĸŲīĸÛâŦĸTûŒĸ^ō™ĸLŅÁĸ<ØĸŧŒ•ĸĸÍĖĸĸĖĖĸĸĖĖĸĸÉÉĸĸŨÖĸĸũĸĸÖŦ­ĸ ŽŦĸóÓ°ĸēzsĸüÔŲĸýâãĸúļ·ĸöÄÃĸöĘÉĸøÖÔĸÔ°ŊĸA++Ā���I����rrrbŌŌŌĸęëëĸãæčĸïßÏĸĸŨŠĸÖÔ·ĸ@ÂāĸJģûĸLģĸĸ6†éĸ}ŽĸĸËČĸĸĘĘĸĸËËĸĸÖÖĸĸęéĸĸõĸĸûėûĸËĄžĸēvqĸݔ˜ĸĸðũĸĸÅÆĸĸĶĨĸĸēēĸĸššĸĸĘĘĸÚĶĶĸD--―���=����tttbÔÖØĸíïņĸįãÚĸïÞÍĸĸÔ ĸŌÆžĸ'˜ĸĸ3Ģĸĸ@Ūĸĸ)ŒųĸvfˆĸüÉÃĸĸããĸĸõõĸĸýýĸĸĸĸĸĸüĸĸĸįĸĸĸŅúĸúÂáĸĸãýĸĸÞįĸĸžĸĸŸŸĸĸŠŠĸĸēēĸĸĀĀĸɓ“ĸ)Ÿ���-����uvwbŨÓËĸúŧ[ĸōļeĸïßŅĸĸϜĸōĖĶĸ°ŧÅĸ”ķÔĸ{ķãĸQšëĸDUĢĸÎĩĩĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸóũĸĸËčĸĸĖëĸĸÉØĸĸŠŦĸĸœœĸĸ™™ĸĸ  ĸĸŠŠĸýēēĸĶssũu�������wz€bÞĞĸûŒ�ĸé°bĸņáŅĸĸ˖ĸĸ͙ĸĸϔĸĸϔĸĸ˚ĸóɟĸāĄšĸ瀂ĸũôôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸčæĸĸĮÅĸĸÂÁĸĸ·ĩĸĸ°°ĸĸĻĻĸĸžžĸĸ™™ĸĸĒĒĸĸsLLŨA��� ����y}bÛÁĸë�ĸâĻbĸóáÏĸĸĮŠĸĸɏĸĸɏĸĸɏĸĸɎĸĸʎĸĸɐĸŌ|ĸū ĪĸĸĸĸĸĸĸĸĸĸĸĸĸĸþþĸĸÜÜĸĸÏÏĸĸËĘĸĸÃÃĸĸššĸĸąąĸĸ§§ĸĸžžĸĸœœĸđxxý<((€����������{‚bØ―›ĸÝq�ĸā­xĸõáÉĸĸĀvĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÄ{ĸĸÁĸžlĸÉīļĸĸĸĸĸĸĸĸĸĸũũĸĸßßĸĸŨŨĸĸÐÐĸĸËËĸĸÃÃĸĸđđĸĸąąĸĸŽŽĸԆ†ĸmIIŧ���(�����������|bÛĮēĸ⚏ĸŲŲŲĸõÝūĸĸšdĸĸžhĸĸŧgĸĸžhĸĸžjĸĸ―lĸĸ―lĸĸūkĸųšiĸļ‚dĸÅŦ­ĸüüþĸĸũũĸĸččĸĸááĸĸØØĸĸÐÐĸĸĖĖĸĸÆÆĸĸ――ĸŌĸ€WWÍ *��������������€€€bŌŌŌĸšŋÃĸÉĖŅĸũÛļĸĸ°FĸþŠ/ĸýŊ@ĸþŊ@ĸþ­;ĸĸ­=ĸĸŪ?ĸĸēJĸĸķSĸþļ[ĸƊZĸ牅ĸØÃËĸóāäĸþæįĸĸããĸĸÚÚĸûĖĖĸëķķĸ큁øzRRĒ������������������bŅŅŅĸŧŧŧĸĮĘÍĸũŨ­ĸþ­9ĸöęÖĸóņïĸōãÎĸōÖŠĸōʍĸôŋqĸöđ`ĸúŪAĸþĻ,ĸĸĻ"ĸí1ĸȀ=ĸŋ‰jĸū‘…ĸķĸ牉ý–llėmLLļO44`��������������������������‚‚‚bŨŨŨĸâââĸöøûĸýá·ĸĸėÅĸüĸĸĸøúüĸôũûĸïôûĸėōüĸéíóĸččįĸæáŲĸãÛÎĸåŅģĸíĮŽĸōūlĸîŦIĸëž1ĸė+ĸˆr\ōž���‘���d���,�����������������������bčččĸĸĸĸĸęëëĸĘ·ēĸÍīĩĸÚĮĮĸįÜÜĸïęęĸõũũĸōóóĸïņņĸėîîĸęėíĸåįčĸáäæĸÞáåĸÛßåĸŨÝæĸÖØÚĸÜÏŧĸĮÃŧþ°īĩø‘““éQRR‹�������������������������{{{fÝÝÝĸáááĸÔÖØĸĄv{ĸ›dgĸĪqqĸŦyyĸ°ĸĸÅĒĒĸŅĩĩĸŲĮĮĸāÔÔĸâÛÛĸâÝÝĸáááĸÞÝÝĸÚÚÚĸŨØŲĸÔÖÚĸŲÚÛĸÂÂÂĸnnnĪ...'�������������������������‡‡‡™ßÞÞĸāãæĸÝËžĸ†iĸķrĸ­zvĸŦxxĸ­z{ĸ°|}ĸą}~ĸģ~ĸ큂ĸŧ„…ĸӔĸƟŸĸßŲŲĸęėėĸíïïĸéęęĸįææĸŦŦŦø111­���8���������������������������(((đđđëîïïĸéėðĸöʓĸĸđbĸýÁĸûȕĸöØĸėđ˜ĸ㰗ĸÛĻ—ĸҟ”ĸΛ•ĸĖ™•ĸ˗•ĸʗ–ĸčßßĸúööĸĸööĸĸîîĸĸîîĸĩŽŽúĨ���U���������������������������dęëėýõųþĸõïčĸýÃ{ĸĸÃ~ĸĸĖ—ĸĸ˜ĸĸ͟ĸĸÎĪĸĸÏĐĸĸÏ­ĸĸŅąĸĸÏĩĸýĘķĸøÅ·ĸóÁ·ĸņÐÎĸĸÛÛĸĸÜÜĸĸääĸĸððĸïččĸKLLÓ���|���&�����������������������ģģī·óïëĸþÞķĸþˍĸĸÃ~ĸĸ˗ĸĸ˜ĸĸ˟ĸĸĖĪĸĸ˧ĸĸĖŦĸĸĖŊĸĸĖģĸĸĖ·ĸĸÍžĸĸÍÁĸûĘÁĸīĒĄüûÚÚĸĸččĸĸïïĸĸųųĸĸĸĸĸ———í‘���;��� ��������������������žŸĄ‹ŦŸ‘Öþ·^ĸþÂ~ĸþ˖ĸþ˜ĸĸ˟ĸĸÎĪĸĸÎĻĸĸÎŽĸĸΰĸĸÎīĸĸÎļĸĸÍžĸĸÍĀĸĸÍÅĸðÄĀþ\]]ÂÎÂÂëĸõõĸĸýýĸĸĸĸĸĸĸĸĸÝÝÝĸFFFŽ���/��� ������������������������=’‘x–“ŽŸ’ŠļĨ“žŋЖÉÂŦ›Ó˰žáÛļĨëā―ŠøęÁąĸëÂīĸðÅšĸúĘÂĸĸÎÉĸÚđļýO•””›ïëëĸéééĸÓÔÔéžžžÕ­­­ŧwwwX��� ������������������������������������������������€ƒ† ‡‰‹ƒˆŠ%‡‡ˆ9Ą”DĶ™•XŪžšeŸ˜˜…Ļ›››š““���sss§§§ZDooo'MMM ����������������������������������ā��€��€��€��€��€���€���€���€���€���€���€���€���€���€���€���€���€���€��€��€��€��€��€��������?���?���������€��ð��(������ ���� ����������������������������������?���m���{���p���_���O���H���W���j���m���^���;������GGGZ‰‰‰ÜЧ§ï|~åYXXŨ543Å$ķ\>?ȌhhޓssämTTŲ*ŧ���Œ���O���XXXĄĒĒņŨÕÔĸĸýōĸâōÖĸųóáĸîÁĸƆ~þũĐŦĸĸÆÆĸĸŲŲĸþėėĸÖÃÃýB--Ð������=ccc#ŊŊ°ųÔÏÏĸðëÓĸ!­%ĸ)Š!ĸœšcĸĸĪĸĸ  ĸĸ­Žĸĸū―ĸĸęęĸĸĸĸĸÕÃÃý&ŧ���heee"―――öčâáĸņåÉĸ1ÃBĸ� ĸŸtĸĸ·―ĸĸĄ ĸĸąšĸüŧÐĸĸóũĸĸĸĸĸĸĸĸĸz``ã���‡hhh"ÂÂÃöėäâĸóáūĸSækĸą,ĸČ­ĸĸÉĖĸĸÉĘĸæĮŅĸĮ°ŊĸņāäĸøõôĸøôōĸĀĶĪû���Žlkk"ÅČËöîęęĸóÜģĸ]āĩĸ6ĶĘĸÆĨļĸĸÏÍĸĸÝÞĸäÕßĸšĢšĸíËĖĸûËËĸúËĘĸÎĨĨü���ƒmno"ËÅšööÚģĸõŌŊĸa­õĸ:–ûĸ§ĄÃĸĸõōĸĸûûĸĸóĸĸōūÜĸýÁËĸĸŸžĸĸģģĸˆˆũ���_qv}"ÏĢföōŦQĸĸŌĶĸöɛĸÝÂĢĸËĶžĸíéîĸĸĸĸĸĸððĸĸËŌĸĸģīĸĸĢĒĸüŸŸĸ{RRŋ���.uy}"ŧ„ö㷈ĸĸÂvĸĸđ^ĸĸĀhĸüķhĸŌ·ĸëäęĸĸōöĸĸÛÝĸĸÎÎĸüķķĸŪppâ0 D��� www#đžūöØŅÆĸĸĮqĸû˂ĸúÄxĸüŋhĸũ°Pĸՙ]ĸâķ˜ĸįūŽĸš’’ō‡^^É5""G��� ���lll$ŌŌÓũíåßĸãÐĮĸéčðĸėééĸéåáĸæâŲĸįÔīĸáđ€ĸāąpĸqd`Ý"w����������ooo5ØÛÞúØÄļĸĩ€tĸģƒ€ĸŧĸŞŸĸ˧ŠĸŲĖÐĸęņøĸâäéĸlmmŲl����������ĄĄĒvôōîĸü͓ĸýŋĸøÄĸō― ĸė·Ģĸį°ĨĸėÄĀĸĸęęĸþîîĸSRRŋ���g����������ĻĶ̐óȒøûĮŽþþĖĄĸĸÎĐĸĸÎąĸĸÐŧĸĸÔĮĸÉ­ŠęėÚÛþĸþþĸĀĀĀáC����������ˆˆ‡ —’‹2ŦG§šY –jĩĄ™yÂĐĒūϧۈƒƒcŸŸŸhģģģv˜˜˜TEEE�����������������kb��Ęĸ��áĸ��Üĸ��Öĸ��Ũĸ��Ŋĸ��1ĸ��=ĸ��'ĸ��ĸ�Uĸ�Äĸ�đĸ�°ĸ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/installer.png����������������������������������������������������������0000664�0000000�0000000�00000007574�13253666515�0021341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���0���0���Wų‡��CIDAThĨšil\ŨuĮo}8ÃĄļŠ-R”eZKDÉ"% RT–;čÅčŧA"E[ļŸÔAë5`8hęÄ2 ĢŠ?ĪiļąTķĨ Žck1―ŅĶ8ĒHũCr†g{3ó^?ž’Ã]ÍÞ{ũ‚wþĸ{ÎđįÜs)ðÛ5a―ÎóįÏ5+‚ xUU=ļļļØųôÓOĸH�Ų_|Ņûä“OF�cşëÍĩU“· ęĨ—^*�Ĩëš(IRĒÚwYœ‚,ũ(§Ûbq!�.g% J*ÃÃÏ�Àĸå—_þƒ}ûîųũ>ø`! vßŌ4­#“Éô?óĖ3ï)@ß.Ą%įϟĸv&“Đᐠ\å‚Eĩ6 ’Šh/AÜ'ējīš*‘�rēō=ĸ-ÞâšuuußčïïŸtŧ=ĸØÜ|UÅ-IÜÜ711A(âÔĐS ũtwwĸËóÏ?ĸ:°d7%pþüųgÃáðĮšŪĸÅĀÞo·Xl.ŠðŠĶ.ō VƒÚtÖUÍáŠāðáÃ'xāĖ-5šföŦ*HTVVRYY Āå˗ũ>ũÜs5@ЅivH§Ó%įΝ{gpp†‹âĖō`žÄoÛ,öjjjŠ8ö„ÏwДŲo& 9gŅh”+WŪLũöö:0ŨIßjnĐŠŠjĒđđų/ïŧï>Ž1ðZMӛŒ €EAXËcģïõÆ)îÞÛÐl·ŲÜčš9ožˆ$Á͛7yéĨ—Ú§ĶĶ>>Â[_{íĩnŋßßŦiēĄŅžCãÏRT:ēDŌ0§AÆ0MFĮ|æE_Õ·Ņ{ĻØy§ģŠT 4 2 Þ5 Ū^―šloo�·sā·tdHķ··ĸÛýũßĸ‚ÃáXļŧČ ą/}߉9Ĩb1„L†L<šN:‘ ųh1{šLF!•2Á9x†@~øá 0 ņíü– 7oÞüoŋßĸÂáǗ 8Ōžžĩbšb!‰L<Ž‘H įÄH&ŅS)Ōą‚a Š"Ē("’$!Š"ĐĪ…%2VŦŠŠĪRLš+ŸJAOÏmŪ^―Úô�3lsŸ.]š4~ęÔЋä$`Ÿ>N 0Gš4ŅX„ÅT!™\ļ‘ä›(Šdģ&ŽT*E2™ ›Í"B’ēĪRI&'G“‚ ,�˜Ûįķš”{īWVV~Ãfģ‘ÍfÉfģļaúnt ˇ0â1ÓX·ŲA ŽĖNiНŠ V C'‘HF‰FĢÄb1bąĐT’ššųāÁƒũ�―===·€4Ûô�C–å (Š”””033C:ĶīÔ†ÃĄ‹Ĩ7œ ĻȆĮcĢšÚƒÅ"ãóŲ).ķĒ("šŪŊ+†aŽé“$‰ĶĶ&Õãņü žž|ÏŦŊūúOĀ4°éŠ-EbY–ģ†aāt:q8ĖÏÏ3==M}―‡/ŋ R__†ÝnĄēŌKEE‹ĖŪ]ÅÛļŲØĘū]ŧvņāƒ~Įëõ6―óÎ;ßņûý€ķ‘6–‚`†A^Ün7.—‹ļ­”ßĸny{ ïüXBf,!Óķ`§ięÝĖččhīģģsfvvv.öûýþjĖ]ifK ä'FQĘĘĘÐuīââÉpáäĸ|gDa4!q{A1A'e:Ēvlļd°ˆ ĩ·GôĢ]Ā(0 Œ°U*‘ÉHĨR\ŧv ŊŨˑ#G)&0'ðė`sðŨ§eæ5ļ‘éŒČŒ&$†b*6É܂­8Róė ØžDNDą,L‹Ų*Ũđsĸp2™\BĄĸĸó)ÍØÐ™ 4`ŠĒpüøqÂá0###H;―”Yāgý2Õv‡Š4:Â"ËËp}Ff$&ÖDø,`“Ā.Á§)�ōėŪ@j(Dcc-wÝIii ™ ļ\NBĄE9Žî™xt``čŅššSí33Ã/\šôã·1ƒÚšqa)ķ>õÔSgϞ={ĐĪĪ„Ï?ĸŊŨËącĮKŧøŨNš—§@Á)ƒG‡J-›č8ĮŌö>Ęä=ÔÄWūŌ€ÛíZJ!ōiD:―üžJiī·ûđ}ÛO08øÖ/~ņėßbîHÚęéóq€'NÔŨŨŨĸĐák ņî=čé$†aĩŅ1gšÁþ"ļĮ õNØiƒRŦiŠļū( “Ļĸ†Z[–ï}ïũ8p A°l Þ0 ›•ðz+ĐŠŠ#]ÜW_ßôÍpxęúÂÂt˜UÛŠļōCŨu.v'9ýĘ 7“5TVV" đ•ūCą.L"~p‘ãû||ĸûˆÏįB’L€›Ï§é4Čē‹ĢGĄĪĪŪúĉGĸļ°ŪK`i UFR õf˜ëý DÃĀ!sGRÕČ^ý5-GxüņÓ*·Xķ^ÓLŅu•ÆÆG(/ŋ{ũŨŋþw/ĩ€šŸo’v›Ų' ˆčļ•Ml|U“#0qý"ŧŦ=kĀX­ÍnüJuugÐīčɆ†ãß >š�L�ú +`ģÝ†.ˆÜé,Bꖟl:Ęwŋ{f]‚ĒXĻ…­Ā›ĀápQUĩŸƒÏ<ėė뚊6ŧIB�EdkIƒ2#·8wî8vŧš.�ŊũÎĀ+ŠyôŽ­ÝOiéŪĘÝŧ< ė�Ă@Ķë:E°š›·.,‚a ˆë)·ãĖLSí!ûŲĮtwÏâŠ{œ––† Áƒđ66$"`ŨA ķxSTĘËkĐŊ?ōĩÏŨøĀŲō$mhüM›C�YZþņãÏRœęĮŦ ˜§qEA‹$˜˜™Äïx”“'ïÝ<�ih°ƒœĀÜČí— mĮ7o~ïÜYMqqu5fÕâöZ'6 vÚ2žq<ŒaĖiJęæ/›'ðxŌ De!Ĩg/~?”•ĮcþpAӁČÄģ° U�N]€žūõÁ+ TUUāv—x€€ģĀV&dy2…ķūž$–fTĻÁįóĄŠ*šĢĢāũC �Ąđó�ffĢ; =å§7Zųyë(7šĖá–cāpŽ^UÁåēãtÚihh>ˆ™,·Õā ÃXcBoŸĐĨ"”Ē6Ļá‹åĘ6†NÄ(^*N­lŅĻ)’ÕÕāSīÞð_?ĸwi C#=ƒûq{šØęęĖX Þb1ŨŪĻȃŪëVĀŧŪ åEŨõ5NðHôyė\Ŧģã[ČÐŌŧHM? eJĐp:Ũ5y05 åÂ?Ļņúëņğ[w>Ld:Ä?yƒą`û·ÛlģeąXģËʖģ(JAČ�†ĻČk äss€EÞbˆG‰�*%%%€eþ>O‚š‚ ā+ũP[?x)+!YbÎ#Ëŧu Ž4# e… ÍĮ 'j ūeâ{‚Ã9UëšĻÖÛįQanN"4'’Ð$ Ÿ}ŠÝŪRYjf�’,â°mGīôr>·†ĀJðšnVđWšPt…�R+*ųRáFāc1Óöí*epp//þxkŅ/ Oóøã§)’Í<MU$l6eĐðĩš§æéëûlÐÖē•āu]G)0ĄhĒð„§åĪĪyFF&ðų*6Œ°}}PsŽ6ĀÜÜicCdģQNýŅŨhip-Yė6›U!“ŅÉfut}™I_ßņx$‰đŸÍmhBæę쯉įã…ŋdŪVēO‰D7MÆÆāVÜ[‚pU-Y<ÞK6!Š"ĄŽī_ ŧ{œ™™áâëšPža >°ļJyštö099đenóQÄ륹Š­€džPæ2āuģeýĩ―}ˆÞÞÖ`ˆāÕāu]ąpZíyšpõ™š átú6MĖz'a8E6pXĖŊÝ'j7ßÚ`tt,90ðE/ÐDŨdĢ333Äãņ%Dđ G‰…å%ōÜĪŧÛŋeV™—Œh‚ÏßÔĖÎnNāâÅ6üþũ˜ĩĒ ą†@:Ķĩĩ•ūū>4MC”d‰%‰&“ērjŦžČðp€H$š%øõ"ėÂÂÆāß~ŧūś7Ŋī@ČŪņ‡ÃASScccƒAj=e&Yîņâí^|ŌAŠm‡°e-tt|HcãÃwÞbY>ĨɆ Ā$oūųQæÚĩŸķÝĀ-˚ŅÚ8055Åėė,{öėÁår! ėBŋSþ0ÞC/;ʼn’cĒ‘yørÜÏÖļ=ōƒƒmTT4m|~,5<ų6:â•Wþ—ķķ·ŧ'&_�c:pf ]ŨđëŪŧPU•ŪŪ.ęęępzw$sĸņÕ·ÖĻ·Ú·‚ÖAēŠÔýîï\ÄfsQUÕ°mðŠjja%øþð"ïŋĸģ[WŪ7€NVÜÞ,AŦŦŦS$IzŽĶĶF-..ÆįóĄ( .W‡I`CQ$°)0ūāpŲ1ĪC­hÚee;ąZĨ-ÁŊ|ūûn.üškŨ^ŋÕŅqåð›ÜęÜÞ,čęęZČfģ=ccc'wėØáôų|H’„ÝîĀåܜ@d.Äd_#·[ûôWôžĸ“L_ەŲþþķĄd!™qX­JK}›‚ÏfãtvráÂŪ^ýdņ―ũ^míëûô“øV`ŠU…­Õ·ĢEĨĨĨũŸ:uę§OŸūŧĨĨ…ŌērJË+ LĶ·7ĀøØ―ó988°8===‡C###ĢĄPh3RhKË7ÏėŲsėŒÛíqîÚUGY™‡ŠŠ2dŲ$366ÄÔÔ<]]CÄã‹Iŋĸ―@n·é>ÁtÚÐjðŦ €YĨp� gϞýįÇŸ}ėąĮˆĮ㌍čėėĖD"‘čôôôėøøøøÔÔÔd<c–Ã'1k˜AĖkŌæU‘hŽ­=tĶķöā·ŧŽLQTKîĀG:Lƒ3ãã·'ÆĮŧGrót䀏įl~ÝKïõ·€YūŦnnnþëÝŧwĸq8Ž…ÃáČxŪ ˜ķ8‰Y`šÉõÅ1ģš……X 3ÓŲTՀ°å~ÏČýítnūÉܜŅõV}+ųĶ�åĀ`ĶãäAÏbÞ$æ'YvŽÍ.æDĖÅąįž+3ĩLnž8Ëĸąēeû?öŽÓ7͉����IENDŪB`‚������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/installer.qrc����������������������������������������������������������0000664�0000000�0000000�00000000502�13253666515�0021322�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<RCC> <qresource prefix="/"> <file>installer.png</file> <file>installer.ico</file> <file>installer.icns</file> <file>install.png</file> <file>uninstall.png</file> <file>keepinstalled.png</file> <file>keepuninstalled.png</file> </qresource> </RCC> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/keepinstalled.png������������������������������������������������������0000664�0000000�0000000�00000000357�13253666515�0022160�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������;֕J���bKGD�ĸ�ĸ�ĸ ―§“��� pHYs�� �� �šœ���tIMEÞ  # ˆëūC���iTXtComment�����Created with GIMPd.e���SIDAT(Ï­Ō1� Ājü7OĮʼn@‘Ec<RĒ@ĢÆYĩaËX`vbW°D Õė%€âĐäĒ8 Cl—„b˜Â`Ów^ũHėüËß~Š É!äþBâ����IENDŪB`‚���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/keepuninstalled.png����������������������������������������������������0000664�0000000�0000000�00000000313�13253666515�0022513�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������;֕J���bKGD�ĸ�ĸ�ĸ ―§“��� pHYs�� �� �šœ���tIMEÞ  #*ģ…ž‹���iTXtComment�����Created with GIMPd.e���/IDAT(Ïc` �0Béĸč%Yó&Jœ=ŠyČhfAxJÁbÅВķÉ�ëÐä+­ï����IENDŪB`‚���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/resources/uninstall.png����������������������������������������������������������0000664�0000000�0000000�00000000360�13253666515�0021337�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������‰PNG  ��� IHDR���������;֕J���bKGD�ĸ�ĸ�ĸ ―§“��� pHYs�� �� �šœ���tIMEÞ  "vHĒ���iTXtComment�����Created with GIMPd.e���TIDAT(Ïc` �0Béĸč%Yó&JœM‘ftizjõļg]šÕˆW3C*ÛŋtąŲŋ˜ڌK!ÁКĄýŸ ā?ąâTIÛd�ÜŅ+V­����IENDŪB`‚��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/runextensions.h������������������������������������������������������������������0000664�0000000�0000000�00000037021�13253666515�0017707�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QTCONCURRENT_RUNEX_H #define QTCONCURRENT_RUNEX_H #include <qrunnable.h> #include <qfutureinterface.h> #include <qthreadpool.h> QT_BEGIN_NAMESPACE namespace QtConcurrent { template <typename T, typename FunctionPointer> class StoredInterfaceFunctionCall0 : public QRunnable { public: StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &)) : fn(fn) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { fn(futureInterface); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; }; template <typename T, typename FunctionPointer, typename Class> class StoredInterfaceMemberFunctionCall0 : public QRunnable { public: StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object) : fn(fn), object(object) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { (object->*fn)(futureInterface); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Class *object; }; template <typename T, typename FunctionPointer, typename Arg1> class StoredInterfaceFunctionCall1 : public QRunnable { public: StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), const Arg1 &arg1) : fn(fn), arg1(arg1) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { fn(futureInterface, arg1); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Arg1 arg1; }; template <typename T, typename FunctionPointer, typename Class, typename Arg1> class StoredInterfaceMemberFunctionCall1 : public QRunnable { public: StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1) : fn(fn), object(object), arg1(arg1) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { (object->*fn)(futureInterface, arg1); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Class *object; Arg1 arg1; }; template <typename T, typename FunctionPointer, typename Arg1, typename Arg2> class StoredInterfaceFunctionCall2 : public QRunnable { public: StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2) : fn(fn), arg1(arg1), arg2(arg2) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { fn(futureInterface, arg1, arg2); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Arg1 arg1; Arg2 arg2; }; template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2> class StoredInterfaceMemberFunctionCall2 : public QRunnable { public: StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2) : fn(fn), object(object), arg1(arg1), arg2(arg2) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { (object->*fn)(futureInterface, arg1, arg2); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Class *object; Arg1 arg1; Arg2 arg2; }; template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3> class StoredInterfaceFunctionCall3 : public QRunnable { public: StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { fn(futureInterface, arg1, arg2, arg3); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Arg1 arg1; Arg2 arg2; Arg3 arg3; }; template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3> class StoredInterfaceMemberFunctionCall3 : public QRunnable { public: StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { (object->*fn)(futureInterface, arg1, arg2, arg3); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Class *object; Arg1 arg1; Arg2 arg2; Arg3 arg3; }; template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4> class StoredInterfaceFunctionCall4 : public QRunnable { public: StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { fn(futureInterface, arg1, arg2, arg3, arg4); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; }; template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4> class StoredInterfaceMemberFunctionCall4 : public QRunnable { public: StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { (object->*fn)(futureInterface, arg1, arg2, arg3, arg4); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Class *object; Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; }; template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> class StoredInterfaceFunctionCall5 : public QRunnable { public: StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { fn(futureInterface, arg1, arg2, arg3, arg4, arg5); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; }; template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> class StoredInterfaceMemberFunctionCall5 : public QRunnable { public: StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { } QFuture<T> start() { futureInterface.reportStarted(); QFuture<T> future = futureInterface.future(); QThreadPool::globalInstance()->start(this); return future; } void run() { (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5); futureInterface.reportFinished(); } private: QFutureInterface<T> futureInterface; FunctionPointer fn; Class *object; Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5; }; template <typename T> QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &)) { return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start(); } template <typename T, typename Arg1> QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), const Arg1 &arg1) { return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start(); } template <typename T, typename Arg1, typename Arg2> QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2) { return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start(); } template <typename T, typename Arg1, typename Arg2, typename Arg3> QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) { return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start(); } template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) { return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start(); } template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) { return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start(); } template <typename Class, typename T> QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object) { return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start(); } template <typename Class, typename T, typename Arg1> QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1) { return (new StoredInterfaceMemberFunctionCall1<T, void (Class::*)(QFutureInterface<T> &, Arg1), Class, Arg1>(fn, object, arg1))->start(); } template <typename Class, typename T, typename Arg1, typename Arg2> QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2) { return (new StoredInterfaceMemberFunctionCall2<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2), Class, Arg1, Arg2>(fn, object, arg1, arg2))->start(); } template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3> QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3) { return (new StoredInterfaceMemberFunctionCall3<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class, Arg1, Arg2, Arg3>(fn, object, arg1, arg2, arg3))->start(); } template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4) { return (new StoredInterfaceMemberFunctionCall4<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class, Arg1, Arg2, Arg3, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start(); } template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5) { return (new StoredInterfaceMemberFunctionCall5<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class, Arg1, Arg2, Arg3, Arg4, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start(); } } // namespace QtConcurrent QT_END_NAMESPACE #endif // QTCONCURRENT_RUNEX_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/scriptengine.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000047573�13253666515�0020025�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "scriptengine.h" #include "messageboxhandler.h" #include "errors.h" #include "scriptengine_p.h" #include "systeminfo.h" #include <QMetaEnum> #include <QQmlEngine> #include <QUuid> #include <QWizard> namespace QInstaller { /*! \class QInstaller::ScriptEngine \inmodule QtInstallerFramework \brief The ScriptEngine class is used to prepare and run the component scripts. */ /*! \fn ScriptEngine::globalObject() const Returns a global object. */ QJSValue InstallerProxy::components() const { if (m_core) { const QList<Component*> all = m_core->components(PackageManagerCore::ComponentType::All); QJSValue scriptComponentsObject = m_engine->newArray(all.count()); for (int i = 0; i < all.count(); ++i) { Component *const component = all.at(i); QQmlEngine::setObjectOwnership(component, QQmlEngine::CppOwnership); scriptComponentsObject.setProperty(i, m_engine->newQObject(component)); } return scriptComponentsObject; } return m_engine->newArray(); } QJSValue InstallerProxy::componentByName(const QString &componentName) { if (m_core) return m_engine->newQObject(m_core->componentByName(componentName)); return QJSValue(); } GuiProxy::GuiProxy(ScriptEngine *engine, QObject *parent) : QObject(parent), m_engine(engine), m_gui(0) { } void GuiProxy::setPackageManagerGui(PackageManagerGui *gui) { if (m_gui) { disconnect(m_gui, &PackageManagerGui::interrupted, this, &GuiProxy::interrupted); disconnect(m_gui, &PackageManagerGui::languageChanged, this, &GuiProxy::languageChanged); disconnect(m_gui, &PackageManagerGui::finishButtonClicked, this, &GuiProxy::finishButtonClicked); disconnect(m_gui, &PackageManagerGui::gotRestarted, this, &GuiProxy::gotRestarted); disconnect(m_gui, &PackageManagerGui::settingsButtonClicked, this, &GuiProxy::settingsButtonClicked); } m_gui = gui; if (m_gui) { connect(m_gui, &PackageManagerGui::interrupted, this, &GuiProxy::interrupted); connect(m_gui, &PackageManagerGui::languageChanged, this, &GuiProxy::languageChanged); connect(m_gui, &PackageManagerGui::finishButtonClicked, this, &GuiProxy::finishButtonClicked); connect(m_gui, &PackageManagerGui::gotRestarted, this, &GuiProxy::gotRestarted); connect(m_gui, &PackageManagerGui::settingsButtonClicked, this, &GuiProxy::settingsButtonClicked); } } /*! Returns the installer page specified by \a id. The values of \c id for the available installer pages are provided by QInstaller::WizardPage. */ QJSValue GuiProxy::pageById(int id) const { if (!m_gui) return QJSValue(); return m_engine->newQObject(m_gui->pageById(id)); } /*! Returns the installer page specified by \a name. The value of \c name is the object name set in the UI file that defines the installer page. */ QJSValue GuiProxy::pageByObjectName(const QString &name) const { if (!m_gui) return QJSValue(); return m_engine->newQObject(m_gui->pageByObjectName(name)); } /*! Returns the current wizard page. */ QJSValue GuiProxy::currentPageWidget() const { if (!m_gui) return QJSValue(); return m_engine->newQObject(m_gui->currentPageWidget()); } QJSValue GuiProxy::pageWidgetByObjectName(const QString &name) const { if (!m_gui) return QJSValue(); return m_engine->newQObject(m_gui->pageWidgetByObjectName(name)); } QString GuiProxy::defaultButtonText(int wizardButton) const { if (!m_gui) return QString(); return m_gui->defaultButtonText(wizardButton); } /*! Automatically clicks the button specified by \a wizardButton after a delay in milliseconds specified by \a delayInMs. */ void GuiProxy::clickButton(int wizardButton, int delayInMs) { if (m_gui) m_gui->clickButton(wizardButton, delayInMs); } bool GuiProxy::isButtonEnabled(int wizardButton) { if (!m_gui) return false; return m_gui->isButtonEnabled(wizardButton); } void GuiProxy::showSettingsButton(bool show) { if (m_gui) m_gui->showSettingsButton(show); } void GuiProxy::setSettingsButtonEnabled(bool enable) { if (m_gui) m_gui->setSettingsButtonEnabled(enable); } /*! Returns the first descendant of \a parent that has \a objectName as name. \sa QObject::findChild */ QJSValue GuiProxy::findChild(QObject *parent, const QString &objectName) { return m_engine->newQObject(parent->findChild<QObject*>(objectName)); } /*! Returns all descendants of \a parent that have \a objectName as name. \sa QObject::findChildren */ QList<QJSValue> GuiProxy::findChildren(QObject *parent, const QString &objectName) { QList<QJSValue> children; foreach (QObject *child, parent->findChildren<QObject*>(objectName)) children.append(m_engine->newQObject(child)); return children; } /*! Hides the GUI when \a silent is \c true. */ void GuiProxy::setSilent(bool silent) { if (m_gui) m_gui->setSilent(silent); } void GuiProxy::setTextItems(QObject *object, const QStringList &items) { if (m_gui) m_gui->setTextItems(object, items); } void GuiProxy::cancelButtonClicked() { if (m_gui) m_gui->cancelButtonClicked(); } void GuiProxy::reject() { if (m_gui) m_gui->reject(); } void GuiProxy::rejectWithoutPrompt() { if (m_gui) m_gui->rejectWithoutPrompt(); } void GuiProxy::showFinishedPage() { if (m_gui) m_gui->showFinishedPage(); } void GuiProxy::setModified(bool value) { if (m_gui) m_gui->setModified(value); } /*! Constructs a script engine with \a core as parent. */ ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(core), m_guiProxy(new GuiProxy(this, this)) { QJSValue global = m_engine.globalObject(); global.setProperty(QLatin1String("console"), m_engine.newQObject(new ConsoleProxy)); global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy)); const QJSValue proxy = m_engine.newQObject(new InstallerProxy(this, core)); global.setProperty(QLatin1String("InstallerProxy"), proxy); global.setProperty(QLatin1String("print"), m_engine.newQObject(new ConsoleProxy) .property(QLatin1String("log"))); #if QT_VERSION < 0x050400 global.setProperty(QLatin1String("qsTr"), m_engine.newQObject(new QCoreApplicationProxy) .property(QStringLiteral("qsTr"))); #else m_engine.installTranslatorFunctions(); #endif global.setProperty(QLatin1String("systemInfo"), m_engine.newQObject(new SystemInfo)); global.setProperty(QLatin1String("QInstaller"), generateQInstallerObject()); global.setProperty(QLatin1String("buttons"), generateWizardButtonsObject()); global.setProperty(QLatin1String("QMessageBox"), generateMessageBoxObject()); global.setProperty(QLatin1String("QDesktopServices"), generateDesktopServicesObject()); if (core) { setGuiQObject(core->guiObject()); QQmlEngine::setObjectOwnership(core, QQmlEngine::CppOwnership); global.setProperty(QLatin1String("installer"), m_engine.newQObject(core)); connect(core, &PackageManagerCore::guiObjectChanged, this, &ScriptEngine::setGuiQObject); } else { global.setProperty(QLatin1String("installer"), m_engine.newQObject(new QObject)); } global.setProperty(QLatin1String("gui"), m_engine.newQObject(m_guiProxy)); global.property(QLatin1String("installer")).setProperty(QLatin1String("components"), proxy.property(QLatin1String("components"))); global.property(QLatin1String("installer")).setProperty(QLatin1String("componentByName"), proxy.property(QLatin1String("componentByName"))); } /*! Creates a JavaScript object that wraps the given QObject \a object. Signals and slots, properties and children of \a object are available as properties of the created QJSValue. In addition some helper methods and properties are added: \list \li findChild(), findChildren() recursively search for child objects with the given object name. \li Direct child objects are made accessible as properties under their respective object names. \endlist */ QJSValue ScriptEngine::newQObject(QObject *object) { QJSValue jsValue = m_engine.newQObject(object); if (!jsValue.isQObject()) return jsValue; QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); // add findChild(), findChildren() methods known from QtScript QJSValue findChild = m_engine.evaluate( QLatin1String("(function() { return gui.findChild(this, arguments[0]); })")); QJSValue findChildren = m_engine.evaluate( QLatin1String("(function() { return gui.findChildren(this, arguments[0]); })")); jsValue.setProperty(QLatin1String("findChild"), findChild); jsValue.setProperty(QLatin1String("findChildren"), findChildren); // add all named children as properties foreach (QObject *const child, object->children()) { if (child->objectName().isEmpty()) continue; jsValue.setProperty(child->objectName(), m_engine.newQObject(child)); newQObject(child); } return jsValue; } /*! Creates a JavaScript object of class Array with the specified \a length. */ QJSValue ScriptEngine::newArray(uint length) { return m_engine.newArray(length); } /*! Evaluates \a program, using \a lineNumber as the base line number, and returns the results of the evaluation. \a fileName is used for error reporting. */ QJSValue ScriptEngine::evaluate(const QString &program, const QString &fileName, int lineNumber) { return m_engine.evaluate(program, fileName, lineNumber); } /*! Registers QObject \a object in the engine, and makes it globally accessible under its object name. */ void ScriptEngine::addToGlobalObject(QObject *object) { if (!object || object->objectName().isEmpty()) return; QJSValue value = newQObject(object); globalObject().setProperty(object->objectName(), value); } /*! Removes the \a object name from the global object. */ void ScriptEngine::removeFromGlobalObject(QObject *object) { globalObject().deleteProperty(object->objectName()); } /*! Loads a script into the given \a context at \a fileName inside the ScriptEngine. The installer and all its components as well as other useful stuff are being exported into the script. For more information, see \l {Component Scripting}. Throws Error when either the script at \a fileName could not be opened, or the QScriptEngine could not evaluate the script. TODO: document \a scriptInjection. */ QJSValue ScriptEngine::loadInContext(const QString &context, const QString &fileName, const QString &scriptInjection) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { throw Error(tr("Cannot open script file at %1: %2") .arg(fileName, file.errorString())); } // Create a closure. Put the content in the first line to keep line number order in case of an // exception. Script content will be added as the last argument to the command to prevent wrong // replacements of %1, %2 or %3 inside the javascript code. const QString scriptContent = QLatin1String("(function() {") + scriptInjection + QString::fromUtf8(file.readAll()) + QString::fromLatin1(";" " if (typeof %1 != \"undefined\")" " return new %1;" " else" " throw \"Missing Component constructor. Please check your script.\";" "})();").arg(context); QJSValue scriptContext = evaluate(scriptContent, fileName); scriptContext.setProperty(QLatin1String("Uuid"), QUuid::createUuid().toString()); if (scriptContext.isError()) { throw Error(tr("Exception while loading the component script \"%1\": %2").arg( QDir::toNativeSeparators(QFileInfo(file).absoluteFilePath()), scriptContext.toString().isEmpty() ? tr("Unknown error.") : scriptContext.toString() + QStringLiteral(" ") + tr("on line number: ") + scriptContext.property(QStringLiteral("lineNumber")).toString())); } return scriptContext; } /*! Tries to call the method specified by \a methodName with the arguments specified by \a arguments within the script and returns the result. If the method does not exist or is not callable, an undefined result is returned. If the call to the method succeeds and the return value is still undefined, a null value will be returned instead. If the method call has an exception, its string representation is thrown as an Error exception. \note The method is not called if \a scriptContext is the same method, to avoid infinite recursion. */ QJSValue ScriptEngine::callScriptMethod(const QJSValue &scriptContext, const QString &methodName, const QJSValueList &arguments) { // don't allow a recursion const QString key = scriptContext.property(QLatin1String("Uuid")).toString(); QStringList stack = m_callstack.value(key); if (m_callstack.contains(key) && stack.value(stack.size() - 1).startsWith(methodName)) return QJSValue(QJSValue::UndefinedValue); stack.append(methodName); m_callstack.insert(key, stack); QJSValue method = scriptContext.property(methodName); if (!method.isCallable()) return QJSValue(QJSValue::UndefinedValue); if (method.isError()) { throw Error(method.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : method.toString()); } const QJSValue result = method.call(arguments); if (result.isError()) { throw Error(result.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : result.toString()); } stack.removeLast(); m_callstack.insert(key, stack); return result.isUndefined() ? QJSValue(QJSValue::NullValue) : result; } // -- private slots void ScriptEngine::setGuiQObject(QObject *guiQObject) { m_guiProxy->setPackageManagerGui(qobject_cast<PackageManagerGui*>(guiQObject)); } // -- private #undef SETPROPERTY #define SETPROPERTY(a, x, t) a.setProperty(QLatin1String(#x), QJSValue(t::x)); QJSValue ScriptEngine::generateWizardButtonsObject() { QJSValue buttons = m_engine.newArray(); SETPROPERTY(buttons, BackButton, QWizard) SETPROPERTY(buttons, NextButton, QWizard) SETPROPERTY(buttons, CommitButton, QWizard) SETPROPERTY(buttons, FinishButton, QWizard) SETPROPERTY(buttons, CancelButton, QWizard) SETPROPERTY(buttons, HelpButton, QWizard) SETPROPERTY(buttons, CustomButton1, QWizard) SETPROPERTY(buttons, CustomButton2, QWizard) SETPROPERTY(buttons, CustomButton3, QWizard) return buttons; } /*! \internal generates QMessageBox::StandardButton enum as an QScriptValue array */ QJSValue ScriptEngine::generateMessageBoxObject() { QJSValue messageBox = m_engine.newQObject(MessageBoxHandler::instance()); const QMetaObject &messageBoxMetaObject = QMessageBox::staticMetaObject; const int index = messageBoxMetaObject.indexOfEnumerator("StandardButtons"); QJSValue value = m_engine.newArray(); value.setProperty(QLatin1String("NoButton"), QJSValue(QMessageBox::NoButton)); const QMetaEnum metaEnum = messageBoxMetaObject.enumerator(index); for (int i = 0; i < metaEnum.keyCount(); i++) { const int enumValue = metaEnum.value(i); if (enumValue >= QMessageBox::FirstButton) value.setProperty(QLatin1String(metaEnum.valueToKey(enumValue)), QJSValue(enumValue)); if (enumValue == QMessageBox::LastButton) break; } messageBox.setPrototype(value); return messageBox; } QJSValue ScriptEngine::generateDesktopServicesObject() { QJSValue desktopServices = m_engine.newArray(); SETPROPERTY(desktopServices, DesktopLocation, QStandardPaths) SETPROPERTY(desktopServices, DocumentsLocation, QStandardPaths) SETPROPERTY(desktopServices, FontsLocation, QStandardPaths) SETPROPERTY(desktopServices, ApplicationsLocation, QStandardPaths) SETPROPERTY(desktopServices, MusicLocation, QStandardPaths) SETPROPERTY(desktopServices, MoviesLocation, QStandardPaths) SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths) SETPROPERTY(desktopServices, TempLocation, QStandardPaths) SETPROPERTY(desktopServices, HomeLocation, QStandardPaths) SETPROPERTY(desktopServices, DataLocation, QStandardPaths) SETPROPERTY(desktopServices, CacheLocation, QStandardPaths) SETPROPERTY(desktopServices, GenericDataLocation, QStandardPaths) SETPROPERTY(desktopServices, RuntimeLocation, QStandardPaths) SETPROPERTY(desktopServices, ConfigLocation, QStandardPaths) SETPROPERTY(desktopServices, DownloadLocation, QStandardPaths) SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths) SETPROPERTY(desktopServices, GenericConfigLocation, QStandardPaths) QJSValue object = m_engine.newQObject(new QDesktopServicesProxy); object.setPrototype(desktopServices); // attach the properties return object; } QJSValue ScriptEngine::generateQInstallerObject() { // register ::WizardPage enum in the script connection QJSValue qinstaller = m_engine.newArray(); SETPROPERTY(qinstaller, Introduction, PackageManagerCore) SETPROPERTY(qinstaller, LicenseCheck, PackageManagerCore) SETPROPERTY(qinstaller, TargetDirectory, PackageManagerCore) SETPROPERTY(qinstaller, ComponentSelection, PackageManagerCore) SETPROPERTY(qinstaller, StartMenuSelection, PackageManagerCore) SETPROPERTY(qinstaller, ReadyForInstallation, PackageManagerCore) SETPROPERTY(qinstaller, PerformInstallation, PackageManagerCore) SETPROPERTY(qinstaller, InstallationFinished, PackageManagerCore) SETPROPERTY(qinstaller, End, PackageManagerCore) // register ::Status enum in the script connection SETPROPERTY(qinstaller, Success, PackageManagerCore) SETPROPERTY(qinstaller, Failure, PackageManagerCore) SETPROPERTY(qinstaller, Running, PackageManagerCore) SETPROPERTY(qinstaller, Canceled, PackageManagerCore) SETPROPERTY(qinstaller, Unfinished, PackageManagerCore) SETPROPERTY(qinstaller, ForceUpdate, PackageManagerCore) return qinstaller; } #undef SETPROPERTY } // namespace QInstaller �������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/scriptengine.h�������������������������������������������������������������������0000664�0000000�0000000�00000005302�13253666515�0017452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SCRIPTENGINE_H #define SCRIPTENGINE_H #include "installer_global.h" #include <QJSValue> #include <QJSEngine> namespace QInstaller { class PackageManagerCore; class GuiProxy; class INSTALLER_EXPORT ScriptEngine : public QObject { Q_OBJECT Q_DISABLE_COPY(ScriptEngine) public: explicit ScriptEngine(PackageManagerCore *core = 0); QJSValue globalObject() const { return m_engine.globalObject(); } QJSValue newQObject(QObject *object); QJSValue newArray(uint length = 0); QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); void addToGlobalObject(QObject *object); void removeFromGlobalObject(QObject *object); QJSValue loadInContext(const QString &context, const QString &fileName, const QString &scriptInjection = QString()); QJSValue callScriptMethod(const QJSValue &context, const QString &methodName, const QJSValueList &arguments = QJSValueList()); private slots: void setGuiQObject(QObject *guiQObject); private: QJSValue generateMessageBoxObject(); QJSValue generateQInstallerObject(); QJSValue generateWizardButtonsObject(); QJSValue generateDesktopServicesObject(); private: QJSEngine m_engine; QHash<QString, QStringList> m_callstack; GuiProxy *m_guiProxy; }; } Q_DECLARE_METATYPE(QInstaller::ScriptEngine*) #endif // SCRIPTENGINE_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/scriptengine_p.h�����������������������������������������������������������������0000664�0000000�0000000�00000013261�13253666515�0017774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SCRIPTENGINE_P_H #define SCRIPTENGINE_P_H #include "component.h" #include "packagemanagercore.h" #include "packagemanagergui.h" #include <QDebug> #include <QDesktopServices> #include <QFileDialog> #include <QStandardPaths> namespace QInstaller { class ConsoleProxy : public QObject { Q_OBJECT Q_DISABLE_COPY(ConsoleProxy) public: ConsoleProxy() {} public slots : void log(const QString &log) { qDebug().noquote() << log; } }; class InstallerProxy : public QObject { Q_OBJECT Q_DISABLE_COPY(InstallerProxy) public: InstallerProxy(ScriptEngine *engine, PackageManagerCore *core) : m_engine(engine), m_core(core) {} public slots: QJSValue components() const; QJSValue componentByName(const QString &componentName); private: ScriptEngine *m_engine; PackageManagerCore *m_core; }; class QFileDialogProxy : public QObject { Q_OBJECT Q_DISABLE_COPY(QFileDialogProxy) public: QFileDialogProxy() {} public slots : QString getExistingDirectory(const QString &caption, const QString &dir) const { return QFileDialog::getExistingDirectory(0, caption, dir); } QString getOpenFileName(const QString &caption, const QString &dir, const QString &filter) const { return QFileDialog::getOpenFileName(0, caption, dir, filter); } }; class QDesktopServicesProxy : public QObject { Q_OBJECT Q_DISABLE_COPY(QDesktopServicesProxy) public: QDesktopServicesProxy() {} public slots : bool openUrl(const QString &url) const { QString urlToOpen = url; urlToOpen.replace(QLatin1String("\\\\"), QLatin1String("/")); urlToOpen.replace(QLatin1String("\\"), QLatin1String("/")); return QDesktopServices::openUrl(QUrl::fromUserInput(urlToOpen)); } QString displayName(qint32 location) const { return QStandardPaths::displayName(QStandardPaths::StandardLocation(location)); } QString storageLocation(qint32 location) const { return QStandardPaths::writableLocation(QStandardPaths::StandardLocation(location)); } }; #if QT_VERSION < 0x050400 class QCoreApplicationProxy : public QObject { Q_OBJECT Q_DISABLE_COPY(QCoreApplicationProxy) public: QCoreApplicationProxy() {} public slots: QString qsTr(const QString &text = QString(), const QString &disambiguation = QString(), int n = -1) const { return QCoreApplication::translate(QCoreApplication::applicationName().toUtf8().constData(), text.toUtf8().constData(), disambiguation.toUtf8().constData(), n); } }; #endif class GuiProxy : public QObject { Q_OBJECT Q_DISABLE_COPY(GuiProxy) public: GuiProxy(ScriptEngine *engine, QObject *parent); void setPackageManagerGui(PackageManagerGui *gui); Q_INVOKABLE QJSValue pageById(int id) const; Q_INVOKABLE QJSValue pageByObjectName(const QString &name) const; Q_INVOKABLE QJSValue currentPageWidget() const; Q_INVOKABLE QJSValue pageWidgetByObjectName(const QString &name) const; Q_INVOKABLE QString defaultButtonText(int wizardButton) const; Q_INVOKABLE void clickButton(int wizardButton, int delayInMs = 0); Q_INVOKABLE bool isButtonEnabled(int wizardButton); Q_INVOKABLE void showSettingsButton(bool show); Q_INVOKABLE void setSettingsButtonEnabled(bool enable); Q_INVOKABLE QJSValue findChild(QObject *parent, const QString &objectName); Q_INVOKABLE QList<QJSValue> findChildren(QObject *parent, const QString &objectName); Q_INVOKABLE void setSilent(bool silent); Q_INVOKABLE void setTextItems(QObject *object, const QStringList &items); signals: void interrupted(); void languageChanged(); void finishButtonClicked(); void gotRestarted(); void settingsButtonClicked(); public slots: void cancelButtonClicked(); void reject(); void rejectWithoutPrompt(); void showFinishedPage(); void setModified(bool value); private: ScriptEngine *m_engine; PackageManagerGui *m_gui; }; } // namespace QInstaller Q_DECLARE_METATYPE(QInstaller::ConsoleProxy*) Q_DECLARE_METATYPE(QInstaller::InstallerProxy*) Q_DECLARE_METATYPE(QInstaller::QFileDialogProxy*) Q_DECLARE_METATYPE(QInstaller::QDesktopServicesProxy*) #if QT_VERSION < 0x050400 Q_DECLARE_METATYPE(QInstaller::QCoreApplicationProxy*) #endif #endif // SCRIPTENGINE_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/selfrestartoperation.cpp���������������������������������������������������������0000664�0000000�0000000�00000005165�13253666515�0021601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "selfrestartoperation.h" #include "packagemanagercore.h" #include "selfrestarter.h" using namespace QInstaller; SelfRestartOperation::SelfRestartOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("SelfRestart")); } void SelfRestartOperation::backup() { setValue(QLatin1String("PreviousSelfRestart"), SelfRestarter::restartOnQuit()); } bool SelfRestartOperation::performOperation() { PackageManagerCore *const core = packageManager(); if (!core) { setError(UserDefinedError); setErrorString(tr("Installer object needed in operation %1 is empty.").arg(name())); return false; } if (!core->isMaintainer()) { setError(UserDefinedError); setErrorString(tr("Self Restart: Only valid within updater or packagemanager mode.")); return false; } if (!arguments().isEmpty()) { setError(InvalidArguments); setErrorString(tr("Self Restart: Invalid arguments")); return false; } SelfRestarter::setRestartOnQuit(true); return SelfRestarter::restartOnQuit(); } bool SelfRestartOperation::undoOperation() { SelfRestarter::setRestartOnQuit(value(QLatin1String("PreviousSelfRestart")).toBool()); return true; } bool SelfRestartOperation::testOperation() { return true; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/selfrestartoperation.h�����������������������������������������������������������0000664�0000000�0000000�00000003314�13253666515�0021240�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SELFRESTARTOPERATION_H #define SELFRESTARTOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT SelfRestartOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::SelfRestartOperation) public: explicit SelfRestartOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/serverauthenticationdialog.cpp���������������������������������������������������0000664�0000000�0000000�00000004275�13253666515�0022751�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "serverauthenticationdialog.h" #include "downloadfiletask.h" #include "ui_serverauthenticationdialog.h" #include <QAuthenticator> namespace QInstaller { ServerAuthenticationDialog::ServerAuthenticationDialog(const QString &m, const FileTaskItem &item) : ui(new Ui::ServerAuthenticationDialog) { ui->setupUi(this); ui->siteDescription->setText(m); setWindowFlags(windowFlags() &~Qt::WindowContextHelpButtonHint); const QAuthenticator auth = item.value(TaskRole::Authenticator).value<QAuthenticator>(); ui->userEdit->setText(auth.user()); ui->passwordEdit->setText(auth.password()); } ServerAuthenticationDialog::~ServerAuthenticationDialog() { delete ui; } QString ServerAuthenticationDialog::user() const { return ui->userEdit->text(); } QString ServerAuthenticationDialog::password() const { return ui->passwordEdit->text(); } } // namespace QInstaller �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/serverauthenticationdialog.h�����������������������������������������������������0000664�0000000�0000000�00000003610�13253666515�0022406�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SERVERAUTHENTICATIONDIALOG_H #define SERVERAUTHENTICATIONDIALOG_H #include <QDialog> namespace QInstaller { namespace Ui { class ServerAuthenticationDialog; } class FileTaskItem; class ServerAuthenticationDialog : public QDialog { Q_OBJECT Q_DISABLE_COPY(ServerAuthenticationDialog) public: explicit ServerAuthenticationDialog(const QString &message, const FileTaskItem &item); ~ServerAuthenticationDialog(); QString user() const; QString password() const; private: Ui::ServerAuthenticationDialog *ui; }; } // QInstaller #endif // SERVERAUTHENTICATIONDIALOG_H ������������������������������������������������������������������������������������������������������������������������src/libs/installer/serverauthenticationdialog.ui����������������������������������������������������0000664�0000000�0000000�00000006612�13253666515�0022601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QInstaller::ServerAuthenticationDialog</class> <widget class="QDialog" name="QInstaller::ServerAuthenticationDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>330</width> <height>137</height> </rect> </property> <property name="windowTitle"> <string>Server Requires Authentication</string> </property> <layout class="QGridLayout"> <item row="0" column="0" colspan="2"> <widget class="QLabel" name="label"> <property name="text"> <string>You need to supply a username and password to access this site.</string> </property> <property name="wordWrap"> <bool>false</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Username:</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QLineEdit" name="userEdit"/> </item> <item row="3" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Password:</string> </property> </widget> </item> <item row="3" column="1"> <widget class="QLineEdit" name="passwordEdit"> <property name="echoMode"> <enum>QLineEdit::Password</enum> </property> </widget> </item> <item row="5" column="0" colspan="2"> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string/> </property> </widget> </item> <item row="1" column="1"> <widget class="QLabel" name="siteDescription"> <property name="font"> <font> <weight>75</weight> <bold>true</bold> </font> </property> <property name="text"> <string>%1 at %2</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item row="4" column="0"> <spacer> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>QInstaller::ServerAuthenticationDialog</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel"> <x>165</x> <y>116</y> </hint> <hint type="destinationlabel"> <x>165</x> <y>68</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>QInstaller::ServerAuthenticationDialog</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel"> <x>165</x> <y>116</y> </hint> <hint type="destinationlabel"> <x>165</x> <y>68</y> </hint> </hints> </connection> </connections> </ui> ����������������������������������������������������������������������������������������������������������������������src/libs/installer/settings.cpp���������������������������������������������������������������������0000664�0000000�0000000�00000061755�13253666515�0017171�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "settings.h" #include "errors.h" #include "qinstallerglobal.h" #include "repository.h" #include <QtCore/QFileInfo> #include <QtCore/QStringList> #include <QtGui/QFontMetrics> #include <QtWidgets/QApplication> #include <QRegularExpression> #include <QXmlStreamReader> using namespace QInstaller; static const QLatin1String scInstallerApplicationIcon("InstallerApplicationIcon"); static const QLatin1String scInstallerWindowIcon("InstallerWindowIcon"); static const QLatin1String scLogo("Logo"); static const QLatin1String scPrefix("Prefix"); static const QLatin1String scWatermark("Watermark"); static const QLatin1String scBanner("Banner"); static const QLatin1String scProductUrl("ProductUrl"); static const QLatin1String scBackground("Background"); static const QLatin1String scAdminTargetDir("AdminTargetDir"); static const QLatin1String scMaintenanceToolName("MaintenanceToolName"); static const QLatin1String scUserRepositories("UserRepositories"); static const QLatin1String scTmpRepositories("TemporaryRepositories"); static const QLatin1String scMaintenanceToolIniFile("MaintenanceToolIniFile"); static const QLatin1String scRemoteRepositories("RemoteRepositories"); static const QLatin1String scDependsOnLocalInstallerBinary("DependsOnLocalInstallerBinary"); static const QLatin1String scTranslations("Translations"); static const QLatin1String scCreateLocalRepository("CreateLocalRepository"); static const QLatin1String scInstallActionColumnVisible("InstallActionColumnVisible"); static const QLatin1String scFtpProxy("FtpProxy"); static const QLatin1String scHttpProxy("HttpProxy"); static const QLatin1String scProxyType("ProxyType"); const char scControlScript[] = "ControlScript"; template <typename T> static QSet<T> variantListToSet(const QVariantList &list) { QSet<T> set; foreach (const QVariant &variant, list) set.insert(variant.value<T>()); return set; } static void raiseError(QXmlStreamReader &reader, const QString &error, Settings::ParseMode parseMode) { if (parseMode == Settings::StrictParseMode) { reader.raiseError(error); } else { QFile *xmlFile = qobject_cast<QFile*>(reader.device()); if (xmlFile) { qWarning().noquote().nospace() << "Ignoring following settings reader error in " << xmlFile->fileName() << ", line " << reader.lineNumber() << ", column " << reader.columnNumber() << ": " << error; } else { qWarning("Ignoring following settings reader error: %s", qPrintable(error)); } } } static QStringList readArgumentAttributes(QXmlStreamReader &reader, Settings::ParseMode parseMode, const QString &tagName, bool lc = false) { QStringList arguments; while (QXmlStreamReader::TokenType token = reader.readNext()) { switch (token) { case QXmlStreamReader::StartElement: { if (!reader.attributes().isEmpty()) { raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".") .arg(reader.name().toString()), parseMode); return arguments; } else { if (reader.name().toString() == tagName) { (lc) ? arguments.append(reader.readElementText().toLower()) : arguments.append(reader.readElementText()); } else { raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(reader.name() .toString()), parseMode); return arguments; } } } break; case QXmlStreamReader::Characters: { if (reader.isWhitespace()) continue; arguments.append(reader.text().toString().split(QRegularExpression(QLatin1String("\\s+")), QString::SkipEmptyParts)); } break; case QXmlStreamReader::EndElement: { return arguments; } default: break; } } return arguments; } static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefault, Settings::ParseMode parseMode) { QSet<Repository> set; while (reader.readNextStartElement()) { if (reader.name() == QLatin1String("Repository")) { Repository repo(QString(), isDefault); while (reader.readNextStartElement()) { if (reader.name() == QLatin1String("Url")) { repo.setUrl(reader.readElementText()); } else if (reader.name() == QLatin1String("Username")) { repo.setUsername(reader.readElementText()); } else if (reader.name() == QLatin1String("Password")) { repo.setPassword(reader.readElementText()); } else if (reader.name() == QLatin1String("DisplayName")) { repo.setDisplayName(reader.readElementText()); } else if (reader.name() == QLatin1String("Enabled")) { repo.setEnabled(bool(reader.readElementText().toInt())); } else { raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(reader.name() .toString()), parseMode); } if (!reader.attributes().isEmpty()) { raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".") .arg(reader.name().toString()), parseMode); } } set.insert(repo); } else { raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(reader.name().toString()), parseMode); } if (!reader.attributes().isEmpty()) { raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".").arg(reader .name().toString()), parseMode); } } return set; } // -- Settings::Private class Settings::Private : public QSharedData { public: Private() : m_replacementRepos(false) {} QVariantHash m_data; bool m_replacementRepos; QString absolutePathFromKey(const QString &key, const QString &suffix = QString()) const { const QString value = m_data.value(key).toString(); if (value.isEmpty()) return QString(); const QString path = value + suffix; if (QFileInfo(path).isAbsolute()) return path; return m_data.value(scPrefix).toString() + QLatin1String("/") + path; } }; // -- Settings Settings::Settings() : d(new Private) { } Settings::~Settings() { } Settings::Settings(const Settings &other) : d(other.d) { } Settings& Settings::operator=(const Settings &other) { Settings copy(other); std::swap(d, copy.d); return *this; } /* static */ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix, ParseMode parseMode) { QFile file(path); QFile overrideConfig(QLatin1String(":/overrideconfig.xml")); if (overrideConfig.exists()) file.setFileName(overrideConfig.fileName()); if (!file.open(QIODevice::ReadOnly)) throw Error(tr("Cannot open settings file %1 for reading: %2").arg(path, file.errorString())); QXmlStreamReader reader(&file); if (reader.readNextStartElement()) { if (reader.name() != QLatin1String("Installer")) { reader.raiseError(QString::fromLatin1("Unexpected element \"%1\" as root element.").arg(reader .name().toString())); } } QStringList elementList; elementList << scName << scVersion << scTitle << scPublisher << scProductUrl << scTargetDir << scAdminTargetDir << scInstallerApplicationIcon << scInstallerWindowIcon << scLogo << scWatermark << scBanner << scBackground << scStartMenuDir << scMaintenanceToolName << scMaintenanceToolIniFile << scRemoveTargetDir << scRunProgram << scRunProgramArguments << scRunProgramDescription << scDependsOnLocalInstallerBinary << scAllowSpaceInPath << scAllowNonAsciiCharacters << scDisableAuthorizationFallback << scWizardStyle << scStyleSheet << scTitleColor << scWizardDefaultWidth << scWizardDefaultHeight << scRepositorySettingsPageVisible << scTargetConfigurationFile << scRemoteRepositories << scTranslations << scUrlQueryString << QLatin1String(scControlScript) << scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify; Settings s; s.d->m_data.insert(scPrefix, prefix); while (reader.readNextStartElement()) { const QString name = reader.name().toString(); if (!elementList.contains(name)) raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(name), parseMode); if (!reader.attributes().isEmpty()) { raiseError(reader, QString::fromLatin1("Unexpected attribute for element \"%1\".").arg(name), parseMode); } if (s.d->m_data.contains(name)) reader.raiseError(QString::fromLatin1("Element \"%1\" has been defined before.").arg(name)); if (name == scTranslations) { s.setTranslations(readArgumentAttributes(reader, parseMode, QLatin1String("Translation"), true)); } else if (name == scRunProgramArguments) { s.setRunProgramArguments(readArgumentAttributes(reader, parseMode, QLatin1String("Argument"))); } else if (name == scRemoteRepositories) { s.addDefaultRepositories(readRepositories(reader, true, parseMode)); } else { s.d->m_data.insert(name, reader.readElementText(QXmlStreamReader::SkipChildElements)); } } if (reader.error() != QXmlStreamReader::NoError) { throw Error(QString::fromLatin1("Error in %1, line %2, column %3: %4").arg(path).arg(reader .lineNumber()).arg(reader.columnNumber()).arg(reader.errorString())); } if (s.d->m_data.value(scName).isNull()) throw Error(QString::fromLatin1("Missing or empty <Name> tag in %1.").arg(file.fileName())); if (s.d->m_data.value(scVersion).isNull()) throw Error(QString::fromLatin1("Missing or empty <Version> tag in %1.").arg(file.fileName())); // Add some possible missing values if (!s.d->m_data.contains(scInstallerApplicationIcon)) s.d->m_data.insert(scInstallerApplicationIcon, QLatin1String(":/installer")); if (!s.d->m_data.contains(scInstallerWindowIcon)) { s.d->m_data.insert(scInstallerWindowIcon, QString(QLatin1String(":/installer") + s.systemIconSuffix())); } if (!s.d->m_data.contains(scRemoveTargetDir)) s.d->m_data.insert(scRemoveTargetDir, scTrue); if (s.d->m_data.value(scMaintenanceToolName).toString().isEmpty()) { s.d->m_data.insert(scMaintenanceToolName, // TODO: Remove deprecated 'UninstallerName'. s.d->m_data.value(QLatin1String("UninstallerName"), QLatin1String("maintenancetool")) .toString()); } if (s.d->m_data.value(scTargetConfigurationFile).toString().isEmpty()) s.d->m_data.insert(scTargetConfigurationFile, QLatin1String("components.xml")); if (s.d->m_data.value(scMaintenanceToolIniFile).toString().isEmpty()) { s.d->m_data.insert(scMaintenanceToolIniFile, // TODO: Remove deprecated 'UninstallerIniFile'. s.d->m_data.value(QLatin1String("UninstallerIniFile"), QString(s.maintenanceToolName() + QLatin1String(".ini"))).toString()); } if (!s.d->m_data.contains(scDependsOnLocalInstallerBinary)) s.d->m_data.insert(scDependsOnLocalInstallerBinary, false); if (!s.d->m_data.contains(scRepositorySettingsPageVisible)) s.d->m_data.insert(scRepositorySettingsPageVisible, true); if (!s.d->m_data.contains(scCreateLocalRepository)) s.d->m_data.insert(scCreateLocalRepository, false); if (!s.d->m_data.contains(scInstallActionColumnVisible)) s.d->m_data.insert(scInstallActionColumnVisible, false); return s; } QString Settings::logo() const { return d->absolutePathFromKey(scLogo); } QString Settings::title() const { return d->m_data.value(scTitle).toString(); } QString Settings::applicationName() const { return d->m_data.value(scName).toString(); } QString Settings::version() const { return d->m_data.value(scVersion).toString(); } QString Settings::publisher() const { return d->m_data.value(scPublisher).toString(); } QString Settings::url() const { return d->m_data.value(scProductUrl).toString(); } QString Settings::watermark() const { return d->absolutePathFromKey(scWatermark); } QString Settings::banner() const { return d->absolutePathFromKey(scBanner); } QString Settings::background() const { return d->absolutePathFromKey(scBackground); } QString Settings::wizardStyle() const { return d->m_data.value(scWizardStyle).toString(); } QString Settings::styleSheet() const { return d->absolutePathFromKey(scStyleSheet); } QString Settings::titleColor() const { return d->m_data.value(scTitleColor).toString(); } static int lengthToInt(const QVariant &variant) { QString length = variant.toString().trimmed(); if (length.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) { length.chop(2); return qRound(length.toDouble() * QApplication::fontMetrics().height()); } if (length.endsWith(QLatin1String("ex"), Qt::CaseInsensitive)) { length.chop(2); return qRound(length.toDouble() * QApplication::fontMetrics().xHeight()); } if (length.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) { length.chop(2); } return length.toInt(); } int Settings::wizardDefaultWidth() const { return lengthToInt(d->m_data.value(scWizardDefaultWidth)); } int Settings::wizardDefaultHeight() const { return lengthToInt(d->m_data.value(scWizardDefaultHeight)); } QString Settings::installerApplicationIcon() const { return d->absolutePathFromKey(scInstallerApplicationIcon, systemIconSuffix()); } QString Settings::installerWindowIcon() const { return d->absolutePathFromKey(scInstallerWindowIcon); } QString Settings::systemIconSuffix() const { #if defined(Q_OS_OSX) return QLatin1String(".icns"); #elif defined(Q_OS_WIN) return QLatin1String(".ico"); #endif return QLatin1String(".png"); } QString Settings::removeTargetDir() const { return d->m_data.value(scRemoveTargetDir).toString(); } QString Settings::maintenanceToolName() const { return d->m_data.value(scMaintenanceToolName).toString(); } QString Settings::maintenanceToolIniFile() const { return d->m_data.value(scMaintenanceToolIniFile).toString(); } QString Settings::runProgram() const { return d->m_data.value(scRunProgram).toString(); } QStringList Settings::runProgramArguments() const { const QVariant variant = d->m_data.values(scRunProgramArguments); if (variant.canConvert<QStringList>()) return variant.value<QStringList>(); return QStringList(); } void Settings::setRunProgramArguments(const QStringList &arguments) { d->m_data.insert(scRunProgramArguments, arguments); } QString Settings::runProgramDescription() const { return d->m_data.value(scRunProgramDescription).toString(); } QString Settings::startMenuDir() const { return d->m_data.value(scStartMenuDir).toString(); } QString Settings::targetDir() const { return d->m_data.value(scTargetDir).toString(); } QString Settings::adminTargetDir() const { return d->m_data.value(scAdminTargetDir).toString(); } QString Settings::configurationFileName() const { return d->m_data.value(scTargetConfigurationFile).toString(); } bool Settings::createLocalRepository() const { return d->m_data.value(scCreateLocalRepository).toBool(); } bool Settings::installActionColumnVisible() const { return d->m_data.value(scInstallActionColumnVisible, false).toBool(); } bool Settings::allowSpaceInPath() const { return d->m_data.value(scAllowSpaceInPath, true).toBool(); } bool Settings::allowNonAsciiCharacters() const { return d->m_data.value(scAllowNonAsciiCharacters, false).toBool(); } bool Settings::disableAuthorizationFallback() const { return d->m_data.value(scDisableAuthorizationFallback, false).toBool(); } bool Settings::dependsOnLocalInstallerBinary() const { return d->m_data.value(scDependsOnLocalInstallerBinary).toBool(); } bool Settings::hasReplacementRepos() const { return d->m_replacementRepos; } QSet<Repository> Settings::repositories() const { if (d->m_replacementRepos) return variantListToSet<Repository>(d->m_data.values(scTmpRepositories)); return variantListToSet<Repository>(d->m_data.values(scRepositories) + d->m_data.values(scUserRepositories) + d->m_data.values(scTmpRepositories)); } QSet<Repository> Settings::defaultRepositories() const { return variantListToSet<Repository>(d->m_data.values(scRepositories)); } void Settings::setDefaultRepositories(const QSet<Repository> &repositories) { d->m_data.remove(scRepositories); addDefaultRepositories(repositories); } void Settings::addDefaultRepositories(const QSet<Repository> &repositories) { foreach (const Repository &repository, repositories) d->m_data.insertMulti(scRepositories, QVariant().fromValue(repository)); } static bool apply(const RepoHash &updates, QHash<QUrl, Repository> *reposToUpdate) { bool update = false; QList<QPair<Repository, Repository> > values = updates.values(QLatin1String("replace")); for (int a = 0; a < values.count(); ++a) { const QPair<Repository, Repository> data = values.at(a); if (reposToUpdate->contains(data.first.url())) { update = true; reposToUpdate->remove(data.first.url()); reposToUpdate->insert(data.second.url(), data.second); } } values = updates.values(QLatin1String("remove")); for (int a = 0; a < values.count(); ++a) { const QPair<Repository, Repository> data = values.at(a); if (reposToUpdate->contains(data.first.url())) { update = true; reposToUpdate->remove(data.first.url()); } } values = updates.values(QLatin1String("add")); for (int a = 0; a < values.count(); ++a) { const QPair<Repository, Repository> data = values.at(a); if (!reposToUpdate->contains(data.first.url())) { update = true; reposToUpdate->insert(data.first.url(), data.first); } } return update; } Settings::Update Settings::updateDefaultRepositories(const RepoHash &updates) { if (updates.isEmpty()) return Settings::NoUpdatesApplied; QHash <QUrl, Repository> defaultRepos; foreach (const QVariant &variant, d->m_data.values(scRepositories)) { const Repository repository = variant.value<Repository>(); defaultRepos.insert(repository.url(), repository); } const bool updated = apply(updates, &defaultRepos); if (updated) setDefaultRepositories(defaultRepos.values().toSet()); return updated ? Settings::UpdatesApplied : Settings::NoUpdatesApplied; } QSet<Repository> Settings::temporaryRepositories() const { return variantListToSet<Repository>(d->m_data.values(scTmpRepositories)); } void Settings::setTemporaryRepositories(const QSet<Repository> &repositories, bool replace) { d->m_data.remove(scTmpRepositories); addTemporaryRepositories(repositories, replace); } void Settings::addTemporaryRepositories(const QSet<Repository> &repositories, bool replace) { d->m_replacementRepos = replace; foreach (const Repository &repository, repositories) d->m_data.insertMulti(scTmpRepositories, QVariant().fromValue(repository)); } QSet<Repository> Settings::userRepositories() const { return variantListToSet<Repository>(d->m_data.values(scUserRepositories)); } void Settings::setUserRepositories(const QSet<Repository> &repositories) { d->m_data.remove(scUserRepositories); addUserRepositories(repositories); } void Settings::addUserRepositories(const QSet<Repository> &repositories) { foreach (const Repository &repository, repositories) d->m_data.insertMulti(scUserRepositories, QVariant().fromValue(repository)); } Settings::Update Settings::updateUserRepositories(const RepoHash &updates) { if (updates.isEmpty()) return Settings::NoUpdatesApplied; QHash <QUrl, Repository> reposToUpdate; foreach (const QVariant &variant, d->m_data.values(scUserRepositories)) { const Repository repository = variant.value<Repository>(); reposToUpdate.insert(repository.url(), repository); } const bool updated = apply(updates, &reposToUpdate); if (updated) setUserRepositories(reposToUpdate.values().toSet()); return updated ? Settings::UpdatesApplied : Settings::NoUpdatesApplied; } bool Settings::containsValue(const QString &key) const { return d->m_data.contains(key); } QVariant Settings::value(const QString &key, const QVariant &defaultValue) const { return d->m_data.value(key, defaultValue); } QVariantList Settings::values(const QString &key, const QVariantList &defaultValue) const { QVariantList list = d->m_data.values(key); return list.isEmpty() ? defaultValue : list; } bool Settings::repositorySettingsPageVisible() const { return d->m_data.value(scRepositorySettingsPageVisible, true).toBool(); } void Settings::setRepositorySettingsPageVisible(bool visible) { d->m_data.insert(scRepositorySettingsPageVisible, visible); } Settings::ProxyType Settings::proxyType() const { return Settings::ProxyType(d->m_data.value(scProxyType, Settings::NoProxy).toInt()); } void Settings::setProxyType(Settings::ProxyType type) { d->m_data.insert(scProxyType, type); } QNetworkProxy Settings::ftpProxy() const { const QVariant variant = d->m_data.value(scFtpProxy); if (variant.canConvert<QNetworkProxy>()) return variant.value<QNetworkProxy>(); return QNetworkProxy(); } void Settings::setFtpProxy(const QNetworkProxy &proxy) { d->m_data.insert(scFtpProxy, QVariant::fromValue(proxy)); } QNetworkProxy Settings::httpProxy() const { const QVariant variant = d->m_data.value(scHttpProxy); if (variant.canConvert<QNetworkProxy>()) return variant.value<QNetworkProxy>(); return QNetworkProxy(); } void Settings::setHttpProxy(const QNetworkProxy &proxy) { d->m_data.insert(scHttpProxy, QVariant::fromValue(proxy)); } QStringList Settings::translations() const { const QVariant variant = d->m_data.values(scTranslations); if (variant.canConvert<QStringList>()) return variant.value<QStringList>(); return QStringList(); } void Settings::setTranslations(const QStringList &translations) { d->m_data.insert(scTranslations, translations); } QString Settings::controlScript() const { return d->m_data.value(QLatin1String(scControlScript)).toString(); } bool Settings::supportsModify() const { return d->m_data.value(scSupportsModify, true).toBool(); } �������������������src/libs/installer/settings.h�����������������������������������������������������������������������0000664�0000000�0000000�00000012432�13253666515�0016622�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SETTINGS_H #define SETTINGS_H #include "constants.h" #include "installer_global.h" #include <QtCore/QCoreApplication> #include <QtCore/QSharedDataPointer> #include <QtCore/QStringList> #include <QtCore/QVariant> #include <QtNetwork/QNetworkProxy> namespace QInstaller { class Repository; typedef QHash<QString, QPair<Repository, Repository> > RepoHash; class INSTALLER_EXPORT Settings { Q_DECLARE_TR_FUNCTIONS(Settings) public: enum Update { UpdatesApplied, NoUpdatesApplied }; enum ProxyType { NoProxy, SystemProxy, UserDefinedProxy }; enum ParseMode { StrictParseMode, RelaxedParseMode }; explicit Settings(); ~Settings(); Settings(const Settings &other); Settings &operator=(const Settings &other); static Settings fromFileAndPrefix(const QString &path, const QString &prefix, ParseMode parseMode = StrictParseMode); QString logo() const; QString title() const; QString publisher() const; QString url() const; QString watermark() const; QString banner() const; QString background() const; QString installerApplicationIcon() const; QString installerWindowIcon() const; QString systemIconSuffix() const; QString wizardStyle() const; QString styleSheet() const; QString titleColor() const; int wizardDefaultWidth() const; int wizardDefaultHeight() const; QString applicationName() const; QString version() const; QString runProgram() const; QStringList runProgramArguments() const; void setRunProgramArguments(const QStringList &arguments); QString runProgramDescription() const; QString startMenuDir() const; QString targetDir() const; QString adminTargetDir() const; QString removeTargetDir() const; QString maintenanceToolName() const; QString maintenanceToolIniFile() const; QString configurationFileName() const; bool createLocalRepository() const; bool installActionColumnVisible() const; bool dependsOnLocalInstallerBinary() const; bool hasReplacementRepos() const; QSet<Repository> repositories() const; QSet<Repository> defaultRepositories() const; void setDefaultRepositories(const QSet<Repository> &repositories); void addDefaultRepositories(const QSet<Repository> &repositories); Settings::Update updateDefaultRepositories(const RepoHash &updates); QSet<Repository> temporaryRepositories() const; void setTemporaryRepositories(const QSet<Repository> &repositories, bool replace); void addTemporaryRepositories(const QSet<Repository> &repositories, bool replace); QSet<Repository> userRepositories() const; void setUserRepositories(const QSet<Repository> &repositories); void addUserRepositories(const QSet<Repository> &repositories); Settings::Update updateUserRepositories(const RepoHash &updates); bool allowSpaceInPath() const; bool allowNonAsciiCharacters() const; bool disableAuthorizationFallback() const; bool containsValue(const QString &key) const; QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; QVariantList values(const QString &key, const QVariantList &defaultValue = QVariantList()) const; bool repositorySettingsPageVisible() const; void setRepositorySettingsPageVisible(bool visible); Settings::ProxyType proxyType() const; void setProxyType(Settings::ProxyType type); QNetworkProxy ftpProxy() const; void setFtpProxy(const QNetworkProxy &proxy); QNetworkProxy httpProxy() const; void setHttpProxy(const QNetworkProxy &proxy); QStringList translations() const; void setTranslations(const QStringList &translations); QString controlScript() const; bool supportsModify() const; private: class Private; QSharedDataPointer<Private> d; }; } Q_DECLARE_METATYPE(QInstaller::Settings) #endif // SETTINGS_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/settingsoperation.cpp������������������������������������������������������������0000664�0000000�0000000�00000017056�13253666515�0021105�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "settingsoperation.h" #include "packagemanagercore.h" #include "updateoperations.h" #include "qsettingswrapper.h" #include <QDir> #include <QDebug> using namespace QInstaller; SettingsOperation::SettingsOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Settings")); } void SettingsOperation::backup() { } bool SettingsOperation::checkArguments() { const QString path = argumentKeyValue(QLatin1String("path")); const QString method = argumentKeyValue(QLatin1String("method")); const QString key = argumentKeyValue(QLatin1String("key")); const QString aValue = argumentKeyValue(QLatin1String("value")); QStringList missingArguments; if (path.isEmpty()) missingArguments << QLatin1String("path"); if (method.isEmpty()) missingArguments << QLatin1String("method"); if (key.isEmpty()) missingArguments << QLatin1String("key"); if (method != QLatin1String("remove") && aValue.isEmpty()) missingArguments << QLatin1String("value"); if (!missingArguments.isEmpty()) { setError(InvalidArguments); setErrorString(tr("Missing argument(s) \"%1\" calling %2 with arguments \"%3\".").arg( missingArguments.join(QLatin1String("; ")), name(), arguments().join(QLatin1String("; ")))); return false; } QStringList possibleMethodValues; possibleMethodValues << QLatin1String("set") << QLatin1String("remove") << QLatin1String("add_array_value") << QLatin1String("remove_array_value"); if (!possibleMethodValues.contains(method)) { setError(InvalidArguments); setErrorString(tr("Current method argument calling \"%1\" with arguments \"%2\" is not " "supported. Please use set, remove, add_array_value or remove_array_value.").arg(name(), arguments().join(QLatin1String("; ")))); return false; } return true; } bool SettingsOperation::performOperation() { // Arguments: // 1. path=settings file path or registry path // 2. method=set|remove|add_array_value|remove_array_value // 3. key=can be prepended by a category name separated by slash // 4. value=just the value // optional arguments are // formate=native or ini TODO // backup=true or false (default is true) TODO // NOTE: remove and remove_array_value will do nothing at the undostep if (!checkArguments()) return false; const QString path = argumentKeyValue(QLatin1String("path")); const QString method = argumentKeyValue(QLatin1String("method")); const QString key = argumentKeyValue(QLatin1String("key")); const QString aValue = argumentKeyValue(QLatin1String("value")); // use MkdirOperation to get the path so it can remove it with MkdirOperation::undoOperation later KDUpdater::MkdirOperation mkDirOperation; mkDirOperation.setArguments(QStringList() << QFileInfo(path).absolutePath()); mkDirOperation.backup(); if (!mkDirOperation.performOperation()) { setError(mkDirOperation.error()); setErrorString(mkDirOperation.errorString()); return false; } setValue(QLatin1String("createddir"), mkDirOperation.value(QLatin1String("createddir"))); QSettingsWrapper settings(path, QSettingsWrapper::IniFormat); if (method == QLatin1String("set")) settings.setValue(key, aValue); else if (method == QLatin1String("remove")) settings.remove(key); else if (method == QLatin1String("add_array_value")) { QVariant valueVariant = settings.value(key); if (valueVariant.canConvert<QStringList>()) { QStringList array = valueVariant.toStringList(); array.append(aValue); settings.setValue(key, array); } else { settings.setValue(key, aValue); } } else if (method == QLatin1String("remove_array_value")) { QVariant valueVariant = settings.value(key); if (valueVariant.canConvert<QStringList>()) { QStringList array = valueVariant.toStringList(); array.removeOne(aValue); settings.setValue(key, array); } else { settings.remove(key); } } return true; } bool SettingsOperation::undoOperation() { if (!checkArguments()) return false; const QString path = argumentKeyValue(QLatin1String("path")); const QString method = argumentKeyValue(QLatin1String("method")); const QString key = argumentKeyValue(QLatin1String("key")); const QString aValue = argumentKeyValue(QLatin1String("value")); if (method.startsWith(QLatin1String("remove"))) return true; bool cleanUp = false; { // kill the scope to kill settings object, else remove file will not work QSettingsWrapper settings(path, QSettingsWrapper::IniFormat); if (method == QLatin1String("set")) { settings.remove(key); } else if (method == QLatin1String("add_array_value")) { QVariant valueVariant = settings.value(key); if (valueVariant.canConvert<QStringList>()) { QStringList array = valueVariant.toStringList(); array.removeOne(aValue); if (array.isEmpty()) settings.remove(key); else settings.setValue(key, array); } else { settings.setValue(key, aValue); } } settings.sync(); // be safe cleanUp = settings.allKeys().isEmpty(); } if (cleanUp) { QFile settingsFile(path); if (!settingsFile.remove()) qWarning().noquote() << settingsFile.errorString(); if (!value(QLatin1String("createddir")).toString().isEmpty()) { KDUpdater::MkdirOperation mkDirOperation; mkDirOperation.setArguments(QStringList() << QFileInfo(path).absolutePath()); mkDirOperation.setValue(QLatin1String("createddir"), value(QLatin1String("createddir"))); if (!mkDirOperation.undoOperation()) { qWarning().noquote() << mkDirOperation.errorString(); } } } return true; } bool SettingsOperation::testOperation() { return true; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/settingsoperation.h��������������������������������������������������������������0000664�0000000�0000000�00000003424�13253666515�0020544�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SETTINGSOPERATION_H #define SETTINGSOPERATION_H #include "qinstallerglobal.h" namespace QInstaller { class INSTALLER_EXPORT SettingsOperation : public Operation { Q_DECLARE_TR_FUNCTIONS(QInstaller::SettingsOperation) public: explicit SettingsOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); private: bool checkArguments(); }; } // namespace QInstaller #endif // SETTINGSOPERATION_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/simplemovefileoperation.cpp������������������������������������������������������0000664�0000000�0000000�00000007324�13253666515�0022262�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "simplemovefileoperation.h" #include <QDir> #include <QtCore/QFileInfo> namespace QInstaller { SimpleMoveFileOperation::SimpleMoveFileOperation(PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("SimpleMoveFile")); } void SimpleMoveFileOperation::backup() { } bool SimpleMoveFileOperation::performOperation() { if (!checkArgumentCount(2)) return false; const QStringList args = arguments(); const QString source = args.at(0); const QString target = args.at(1); if (source.isEmpty() || target.isEmpty()) { setError(UserDefinedError); setErrorString(tr("None of the arguments can be empty: source \"%1\", target \"%2\".") .arg(QDir::toNativeSeparators(source), QDir::toNativeSeparators(target))); return false; } // If destination file exists, then we cannot use QFile::copy() because it does not overwrite an existing // file. So we remove the destination file. QFile file(target); if (file.exists()) { if (!file.remove()) { setError(UserDefinedError); setErrorString(tr("Cannot move file from \"%1\" to \"%2\", because the target path exists and is " "not removable.").arg(QDir::toNativeSeparators(source), QDir::toNativeSeparators(target))); return false; } } file.setFileName(source); if (!file.rename(target)) { setError(UserDefinedError); setErrorString(tr("Cannot move file \"%1\" to \"%2\": %3").arg( QDir::toNativeSeparators(source), QDir::toNativeSeparators(target), file.errorString())); return false; } emit outputTextChanged(tr("Moving file \"%1\" to \"%2\".").arg(QDir::toNativeSeparators(source), QDir::toNativeSeparators(target))); return true; } bool SimpleMoveFileOperation::undoOperation() { const QString source = arguments().at(0); const QString target = arguments().at(1); QFile(target).rename(source); emit outputTextChanged(tr("Moving file \"%1\" to \"%2\".").arg(QDir::toNativeSeparators(target), QDir::toNativeSeparators(source))); return true; } bool SimpleMoveFileOperation::testOperation() { return true; } } // namespace QInstaller ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/simplemovefileoperation.h��������������������������������������������������������0000664�0000000�0000000�00000003467�13253666515�0021733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SIMPLEMOVEFILEOPERATION_H #define SIMPLEMOVEFILEOPERATION_H #include "qinstallerglobal.h" #include <QtCore/QObject> namespace QInstaller { class INSTALLER_EXPORT SimpleMoveFileOperation : public QObject, public Operation { Q_OBJECT public: explicit SimpleMoveFileOperation(PackageManagerCore *core); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); Q_SIGNALS: void outputTextChanged(const QString &progress); }; } #endif //SIMPLEMOVEFILEOPERATION_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/sysinfo_win.cpp������������������������������������������������������������������0000664�0000000�0000000�00000016076�13253666515�0017674�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "sysinfo.h" #include "link.h" #ifdef Q_CC_MINGW # ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0501 # endif #endif #include <qt_windows.h> #include <psapi.h> #include <tlhelp32.h> #include <winbase.h> #include <winnetwk.h> #ifndef Q_CC_MINGW #pragma comment(lib, "mpr.lib") #endif #include <QDebug> #include <QDir> #include <QLibrary> namespace KDUpdater { VolumeInfo updateVolumeSizeInformation(const VolumeInfo &info) { ULARGE_INTEGER bytesTotal; ULARGE_INTEGER freeBytesPerUser; VolumeInfo update = info; if (GetDiskFreeSpaceExA(qPrintable(info.volumeDescriptor()), &freeBytesPerUser, &bytesTotal, NULL)) { update.setSize(bytesTotal.QuadPart); update.setAvailableSize(freeBytesPerUser.QuadPart); } return update; } /*! Returns a list of volume info objects that are mounted as network drive shares. */ QList<VolumeInfo> networkVolumeInfosFromMountPoints() { QList<VolumeInfo> volumes; QFileInfoList drives = QDir::drives(); foreach (const QFileInfo &drive, drives) { const QString driveLetter = QDir::toNativeSeparators(drive.canonicalPath()); const uint driveType = GetDriveTypeA(qPrintable(driveLetter)); switch (driveType) { case DRIVE_REMOTE: { char buffer[1024] = ""; DWORD bufferLength = 1024; UNIVERSAL_NAME_INFOA *universalNameInfo = (UNIVERSAL_NAME_INFOA*) &buffer; if (WNetGetUniversalNameA(qPrintable(driveLetter), UNIVERSAL_NAME_INFO_LEVEL, LPVOID(universalNameInfo), &bufferLength) == NO_ERROR) { VolumeInfo info; info.setMountPath(driveLetter); info.setVolumeDescriptor(QLatin1String(universalNameInfo->lpUniversalName)); volumes.append(info); } } break; default: break; } } return volumes; } /*! Returns a list of volume info objects based on the given \a volumeGUID. The function also solves mounted volume folder paths. It does not return any network drive shares. */ QList<VolumeInfo> localVolumeInfosFromMountPoints(PTCHAR volumeGUID) { #ifndef UNICODE #define fromWCharArray fromLatin1 #endif QList<VolumeInfo> volumes; DWORD bufferSize; TCHAR volumeNames[MAX_PATH + 1] = { 0 }; if (GetVolumePathNamesForVolumeName(volumeGUID, volumeNames, MAX_PATH, &bufferSize)) { QStringList mountedPaths = QString::fromWCharArray(volumeNames, bufferSize).split(QLatin1Char(char(0)), QString::SkipEmptyParts); foreach (const QString &mountedPath, mountedPaths) { VolumeInfo info; info.setMountPath(mountedPath); info.setVolumeDescriptor(QString::fromWCharArray(volumeGUID)); volumes.append(info); } } return volumes; #ifndef UNICODE #undef fromWCharArray #endif } QList<VolumeInfo> mountedVolumes() { // suppress message box shown while accessing possible unmounted devices const UINT old = SetErrorMode(SEM_FAILCRITICALERRORS); QList<VolumeInfo> tmp; TCHAR volumeGUID[MAX_PATH + 1] = { 0 }; HANDLE handle = FindFirstVolume(volumeGUID, MAX_PATH); if (handle != INVALID_HANDLE_VALUE) { tmp += localVolumeInfosFromMountPoints(volumeGUID); while (FindNextVolume(handle, volumeGUID, MAX_PATH)) { tmp += localVolumeInfosFromMountPoints(volumeGUID); } FindVolumeClose(handle); } tmp += networkVolumeInfosFromMountPoints(); QList<VolumeInfo> volumes; while (!tmp.isEmpty()) // update volume size information volumes.append(updateVolumeSizeInformation(tmp.takeFirst())); SetErrorMode(old); // reset error mode return volumes; } bool pathIsOnLocalDevice(const QString &path) { if (!QFileInfo(path).exists()) return false; if (path.startsWith(QLatin1String("\\\\"))) return false; QDir dir(path); do { if (QFileInfo(dir, QString()).isSymLink()) { QString currentPath = QFileInfo(dir, QString()).absoluteFilePath(); return pathIsOnLocalDevice(Link(currentPath).targetPath()); } } while (dir.cdUp()); const UINT DRIVE_REMOTE_TYPE = 4; if (path.contains(QLatin1Char(':'))) { const QLatin1Char nullTermination('\0'); // for example "c:\" const QString driveSearchString = path.left(3) + nullTermination; WCHAR wCharDriveSearchArray[4]; driveSearchString.toWCharArray(wCharDriveSearchArray); UINT type = GetDriveType(wCharDriveSearchArray); if (type == DRIVE_REMOTE_TYPE) return false; } return true; } bool CALLBACK TerminateAppEnum(HWND hwnd, LPARAM lParam) { DWORD dwID; GetWindowThreadProcessId(hwnd, &dwID); if (dwID == (DWORD)lParam) PostMessage(hwnd, WM_CLOSE, 0, 0); return true; } bool killProcess(const ProcessInfo &process, int msecs) { DWORD dwTimeout = msecs; if (msecs == -1) dwTimeout = INFINITE; // If we can't open the process with PROCESS_TERMINATE rights, then we give up immediately. HANDLE hProc = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, false, process.id); if (hProc == 0) return false; // TerminateAppEnum() posts WM_CLOSE to all windows whose PID matches your process's. EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)process.id); // Wait on the handle. If it signals, great. If it times out, then kill it. bool returnValue = false; if (WaitForSingleObject(hProc, dwTimeout) != WAIT_OBJECT_0) returnValue = TerminateProcess(hProc, 0); CloseHandle(hProc); return returnValue; } }������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/systeminfo.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000012100�13253666515�0017505�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "systeminfo.h" #include <QSysInfo> namespace QInstaller { /*! \inmodule QtInstallerFramework \class QInstaller::SystemInfo \brief Provides information about the operating system. */ /*! Creates a system info object with the parent \a parent. */ SystemInfo::SystemInfo(QObject *parent) : QObject(parent) { } /*! \property SystemInfo::currentCpuArchitecture The architecture of the CPU that the application is running on, in text format. Possible values include: \list \li "i386" \li "x86_64" \endlist \note This function depends on what the OS will report and may not detect the actual CPU architecture if the OS hides that information or is unable to provide it. For example, a 32-bit OS running on a 64-bit CPU is usually unable to determine whether the CPU is actually capable of running 64-bit programs. \sa QSysInfo::currentCpuArchitecture() */ QString SystemInfo::currentCpuArchitecture() const { return QSysInfo::currentCpuArchitecture(); } /*! \property SystemInfo::kernelType The type of the operating system kernel the installer was compiled for. It is also the kernel the installer is running on, unless the host operating system is running a form of compatibility or virtualization layer. For Windows, Linux, and OS X this will return: \list \li "winnt" \li "linux" \li "darwin" \endlist On Unix systems, it returns the same as the output of \c {uname -s} (lowercased). \sa QSysInfo::kernelType() */ QString SystemInfo::kernelType() const { return QSysInfo::kernelType(); } /*! \property SystemInfo::kernelVersion The release version of the operating system kernel. On Windows, it returns the version of the NT or CE kernel. On Unix systems, including OS X, it returns the same as the \c {uname -r} command would return. Example values are: \list \li "6.1.7601" for Windows 7 with Service Pack 1 \li "3.16.6-2-desktop" for openSUSE 13.2 kernel 3.16.6-2 \li "12.5.0" last release of OS X "Mountain Lion" \endlist \sa QSysInfo::kernelVersion() */ QString SystemInfo::kernelVersion() const { return QSysInfo::kernelVersion(); } /*! \property SystemInfo::productType The product name of the operating system this application is running in. Example values are: \list \li "windows" \li "opensuse" (for the Linux openSUSE distribution) \li "osx" \endlist \sa QSysInfo::productType() */ QString SystemInfo::productType() const { return QSysInfo::productType(); } /*! \property SystemInfo::productVersion The product version of the operating system in string form. If the version could not be determined, this function returns "unknown". Example values are: \list \li "7" for Windows 7 \li "13.2" for openSUSE 13.2 \li "10.8" for OS X Mountain Lion \endlist \sa QSysInfo::productVersion() */ QString SystemInfo::productVersion() const { return QSysInfo::productVersion(); } /*! \property SystemInfo::prettyProductName A prettier form of SystemInfo::productType and SystemInfo::productVersion, containing other tokens like the operating system type, codenames and other information. The result of this function is suitable for displaying to the user. Example values are: \list \li "Windows 7" \li "openSUSE 13.2 (Harlequin) (x86_64)" \li "OS X Mountain Lion (10.8)" \endlist \sa QSysInfo::prettyProductName() */ QString SystemInfo::prettyProductName() const { return QSysInfo::prettyProductName(); } } // namespace QInstaller ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/systeminfo.h���������������������������������������������������������������������0000664�0000000�0000000�00000004274�13253666515�0017167�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SYSTEMINFO_H #define SYSTEMINFO_H #include <QObject> namespace QInstaller { class SystemInfo : public QObject { Q_OBJECT Q_DISABLE_COPY(SystemInfo) Q_PROPERTY(QString currentCpuArchitecture READ currentCpuArchitecture CONSTANT) Q_PROPERTY(QString kernelType READ kernelType CONSTANT) Q_PROPERTY(QString kernelVersion READ kernelVersion CONSTANT) Q_PROPERTY(QString productType READ productType CONSTANT) Q_PROPERTY(QString productVersion READ productVersion CONSTANT) Q_PROPERTY(QString prettyProductName READ prettyProductName CONSTANT) public: explicit SystemInfo(QObject *parent = 0); QString currentCpuArchitecture() const; QString kernelType() const; QString kernelVersion() const; QString productType() const; QString productVersion() const; QString prettyProductName() const; }; } // namespace QInstaller #endif // SYSTEMINFO_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/testrepository.cpp���������������������������������������������������������������0000664�0000000�0000000�00000014473�13253666515�0020443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "testrepository.h" #include "packagemanagercore.h" #include "packagemanagerproxyfactory.h" #include "proxycredentialsdialog.h" #include "serverauthenticationdialog.h" #include <QFile> namespace QInstaller { TestRepository::TestRepository(PackageManagerCore *parent) : Job(parent) , m_core(parent) { setAutoDelete(false); setCapabilities(Cancelable); connect(&m_timer, &QTimer::timeout, this, &TestRepository::onTimeout); connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &TestRepository::downloadCompleted); } TestRepository::~TestRepository() { reset(); } Repository TestRepository::repository() const { return m_repository; } void TestRepository::setRepository(const Repository &repository) { reset(); m_repository = repository; } void TestRepository::doStart() { reset(); if (!m_core) { emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine.")); return; // We can't do anything here without core, so avoid tons of !m_core checks. } const QUrl url = m_repository.url(); if (url.isEmpty()) { emitFinishedWithError(QInstaller::InvalidUrl, tr("Empty repository URL.")); return; } QAuthenticator auth; auth.setUser(m_repository.username()); auth.setPassword(m_repository.password()); FileTaskItem item(m_repository.url().toString() + QLatin1String("/Updates.xml?") + QString::number(qrand() * qrand())); item.insert(TaskRole::Authenticator, QVariant::fromValue(auth)); m_timer.start(10000); DownloadFileTask *const xmlTask = new DownloadFileTask(item); if (m_core) xmlTask->setProxyFactory(m_core->proxyFactory()); m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask)); } void TestRepository::doCancel() { reset(); emitFinishedWithError(Job::Canceled, tr("Download canceled.")); } void TestRepository::onTimeout() { reset(); emitFinishedWithError(Job::Canceled, tr("Timeout while testing repository \"%1\".") .arg(m_repository.displayname())); } void TestRepository::downloadCompleted() { if (error() != Job::NoError) return; try { m_xmlTask.waitForFinished(); m_timer.stop(); QFile file(m_xmlTask.future().results().value(0).target()); if (file.open(QIODevice::ReadOnly)) { QDomDocument doc; QString errorMsg; if (!doc.setContent(&file, &errorMsg)) { emitFinishedWithError(QInstaller::InvalidUpdatesXml, tr("Cannot parse Updates.xml: %1").arg(errorMsg)); } else { emitFinishedWithError(Job::NoError, QString(/*Success*/)); // OPK } } else { emitFinishedWithError(QInstaller::DownloadError, tr("Cannot open Updates.xml for reading: %1").arg(file.errorString())); } } catch (const AuthenticationRequiredException &e) { m_timer.stop(); if (e.type() == AuthenticationRequiredException::Type::Server) { ServerAuthenticationDialog dlg(e.message(), e.taskItem()); if (dlg.exec() == QDialog::Accepted) { m_repository.setUsername(dlg.user()); m_repository.setPassword(dlg.password()); QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, "doCancel", Qt::QueuedConnection); } return; } else if (e.type() == AuthenticationRequiredException::Type::Proxy) { const QNetworkProxy proxy = e.proxy(); ProxyCredentialsDialog proxyCredentials(proxy); if (proxyCredentials.exec() == QDialog::Accepted) { PackageManagerProxyFactory *factory = m_core->proxyFactory(); factory->setProxyCredentials(proxy, proxyCredentials.userName(), proxyCredentials.password()); m_core->setProxyFactory(factory); } QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); return; } else { emitFinishedWithError(QInstaller::DownloadError, tr("Authentication failed.")); } } catch (const TaskException &e) { m_timer.stop(); emitFinishedWithError(QInstaller::DownloadError, e.message()); } catch (const QUnhandledException &e) { m_timer.stop(); emitFinishedWithError(QInstaller::DownloadError, QLatin1String(e.what())); } catch (...) { m_timer.stop(); emitFinishedWithError(QInstaller::DownloadError, tr("Unknown error while testing repository \"%1\".").arg(m_repository.displayname())); } } // -- private void TestRepository::reset() { m_timer.stop(); setError(NoError); setErrorString(QString()); try { if (m_xmlTask.isRunning()) m_xmlTask.cancel(); } catch (...) {} } } // namespace QInstaller �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/testrepository.h�����������������������������������������������������������������0000664�0000000�0000000�00000004175�13253666515�0020106�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef TESTREPOSITORY_H #define TESTREPOSITORY_H #include "downloadfiletask.h" #include "job.h" #include "repository.h" #include <QFutureWatcher> #include <QTimer> namespace QInstaller { class PackageManagerCore; class INSTALLER_EXPORT TestRepository : public Job { Q_OBJECT Q_DISABLE_COPY(TestRepository) public: explicit TestRepository(PackageManagerCore *parent = 0); ~TestRepository(); Repository repository() const; void setRepository(const Repository &repository); private slots: void doStart(); void doCancel(); void onTimeout(); void downloadCompleted(); private: void reset(); private: PackageManagerCore *m_core; QTimer m_timer; Repository m_repository; QFutureWatcher<FileTaskResult> m_xmlTask; }; } // namespace QInstaller #endif // TESTREPOSITORY_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/uninstallercalculator.cpp��������������������������������������������������������0000664�0000000�0000000�00000010762�13253666515�0021733�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "uninstallercalculator.h" #include "component.h" #include "packagemanagercore.h" #include "globals.h" #include <QDebug> namespace QInstaller { UninstallerCalculator::UninstallerCalculator(const QList<Component *> &installedComponents) : m_installedComponents(installedComponents) { } QSet<Component *> UninstallerCalculator::componentsToUninstall() const { return m_componentsToUninstall; } void UninstallerCalculator::appendComponentToUninstall(Component *component) { if (!component) return; if (!component->isInstalled()) return; PackageManagerCore *core = component->packageManagerCore(); // remove all already resolved dependees QSet<Component *> dependees = core->dependees(component).toSet() .subtract(m_componentsToUninstall); foreach (Component *dependee, dependees) appendComponentToUninstall(dependee); m_componentsToUninstall.insert(component); } void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components) { foreach (Component *component, components) appendComponentToUninstall(component); QList<Component*> autoDependOnList; // All regular dependees are resolved. Now we are looking for auto depend on components. foreach (Component *component, m_installedComponents) { // If a components is installed and not yet scheduled for un-installation, check for auto depend. if (component->isInstalled() && !m_componentsToUninstall.contains(component)) { QStringList autoDependencies = component->autoDependencies(); if (autoDependencies.isEmpty()) continue; // This code needs to be enabled once the scripts use isInstalled, installationRequested and // uninstallationRequested... if (autoDependencies.first().compare(scScript, Qt::CaseInsensitive) == 0) { //QScriptValue valueFromScript; //try { // valueFromScript = callScriptMethod(QLatin1String("isAutoDependOn")); //} catch (const Error &error) { // // keep the component, should do no harm // continue; //} //if (valueFromScript.isValid() && !valueFromScript.toBool()) // autoDependOnList.append(component); continue; } foreach (Component *c, m_installedComponents) { const QString replaces = c->value(scReplaces); const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(), QString::SkipEmptyParts) << c->name(); foreach (const QString &possibleName, possibleNames) autoDependencies.removeAll(possibleName); } // A component requested auto installation, keep it to resolve their dependencies as well. if (!autoDependencies.isEmpty()) autoDependOnList.append(component); } } if (!autoDependOnList.isEmpty()) appendComponentsToUninstall(autoDependOnList); } } // namespace QInstaller ��������������src/libs/installer/uninstallercalculator.h����������������������������������������������������������0000664�0000000�0000000�00000003713�13253666515�0021376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef UNINSTALLERCALCULATOR_H #define UNINSTALLERCALCULATOR_H #include "installer_global.h" #include <QHash> #include <QList> #include <QSet> #include <QString> namespace QInstaller { class Component; class INSTALLER_EXPORT UninstallerCalculator { public: UninstallerCalculator(const QList<Component *> &installedComponents); QSet<Component*> componentsToUninstall() const; void appendComponentsToUninstall(const QList<Component*> &components); private: void appendComponentToUninstall(Component *component); QList<Component *> m_installedComponents; QSet<Component *> m_componentsToUninstall; }; } #endif // UNINSTALLERCALCULATOR_H �����������������������������������������������������src/libs/installer/unziptask.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000021702�13253666515�0017345�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "unziptask.h" #include "lib7z_facade.h" #ifdef Q_OS_UNIX # include "StdAfx.h" #endif #include "Common/ComTry.h" // TODO: include once we switch from lib7z_fascade.h //#include "Common/MyInitGuid.h" #include "7zip/Archive/IArchive.h" #include "7zip/Common/FileStreams.h" #include "7zip/UI/Common/OpenArchive.h" #include "Windows/FileDir.h" #include "Windows/PropVariant.h" #include <QDir> namespace QInstaller { class ArchiveExtractCallback : public IArchiveExtractCallback, public CMyUnknownImp { public: MY_UNKNOWN_IMP ArchiveExtractCallback(const QString &target, const CArc &arc, QFutureInterface<QString> &fi) : m_target(target) , m_arc(arc) { m_futureInterface = &fi; } // -- IProgress STDMETHOD(SetTotal)(UInt64 total) { COM_TRY_BEGIN m_totalUnpacked = 0; m_totalUnpackedExpected = total; return S_OK; COM_TRY_END } STDMETHOD(SetCompleted)(const UInt64 *completeValue) { COM_TRY_BEGIN Q_UNUSED(completeValue) return S_OK; // return S_OK here as we do not support sub archive extracting COM_TRY_END } // -- IArchiveExtractCallback STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) { if (m_futureInterface->isCanceled()) return E_FAIL; if (m_futureInterface->isPaused()) m_futureInterface->waitForResume(); COM_TRY_BEGIN *outStream = 0; m_currentIndex = index; if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) return E_FAIL; bool isDir = false; if (Archive_IsItem_Folder(m_arc.Archive, m_currentIndex, isDir) != S_OK) return E_FAIL; bool isEncrypted = false; if (Archive_GetItemBoolProp(m_arc.Archive, m_currentIndex, kpidEncrypted, isEncrypted) != S_OK) return E_FAIL; if (isDir || isEncrypted) return E_FAIL; UString itemPath; if (m_arc.GetItemPath(m_currentIndex, itemPath) != S_OK) return E_FAIL; QDir().mkpath(m_target); m_currentTarget = m_target + QLatin1Char('/') + QString::fromStdWString((const wchar_t*)(itemPath)) .replace(QLatin1Char('\\'), QLatin1Char('/')); m_outFileStream = new COutFileStream; CMyComPtr<ISequentialOutStream> scopedPointer(m_outFileStream); if (!m_outFileStream->Open((wchar_t*)(m_currentTarget.utf16()), CREATE_ALWAYS)) return E_FAIL; m_outFileStreamComPtr = scopedPointer; *outStream = scopedPointer.Detach(); return S_OK; COM_TRY_END } STDMETHOD(PrepareOperation)(Int32 askExtractMode) { COM_TRY_BEGIN Q_UNUSED(askExtractMode) m_futureInterface->setProgressValueAndText(0, QLatin1String("Started to extract archive.")); return S_OK; // return S_OK here as we do not need to prepare anything COM_TRY_END } STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) { COM_TRY_BEGIN switch (resultEOperationResult) { case NArchive::NExtract::NOperationResult::kOK: break; default: // fall through and bail case NArchive::NExtract::NOperationResult::kCRCError: case NArchive::NExtract::NOperationResult::kDataError: case NArchive::NExtract::NOperationResult::kUnsupportedMethod: m_outFileStream->Close(); m_outFileStreamComPtr.Release(); return E_FAIL; } UInt32 attributes; if (GetAttributes(&attributes)) NWindows::NFile::NDir::SetFileAttrib((wchar_t*)(m_currentTarget.utf16()), attributes); FILETIME accessTime, creationTime, modificationTime; const bool writeAccessTime = GetTime(kpidATime, &accessTime); const bool writeCreationTime = GetTime(kpidCTime, &creationTime); const bool writeModificationTime = GetTime(kpidMTime, &modificationTime); m_outFileStream->SetTime((writeCreationTime ? &creationTime : NULL), (writeAccessTime ? &accessTime : NULL), (writeModificationTime ? &modificationTime : NULL)); m_totalUnpacked += m_outFileStream->ProcessedSize; m_outFileStream->Close(); m_outFileStreamComPtr.Release(); m_futureInterface->reportResult(m_currentTarget); m_futureInterface->setProgressValueAndText(100 * m_totalUnpacked / m_totalUnpackedExpected, m_currentTarget); return S_OK; COM_TRY_END } private: bool GetAttributes(UInt32 *attributes) const { *attributes = 0; NWindows::NCOM::CPropVariant property; if (m_arc.Archive->GetProperty(m_currentIndex, kpidAttrib, &property) != S_OK) return false; if (property.vt != VT_UI4) return false; *attributes = property.ulVal; return (property.vt == VT_UI4); } bool GetTime(PROPID propertyId, FILETIME *filetime) const { if (!filetime) return false; filetime->dwLowDateTime = 0; filetime->dwHighDateTime = 0; NWindows::NCOM::CPropVariant property; if (m_arc.Archive->GetProperty(m_currentIndex, propertyId, &property) != S_OK) return false; if (property.vt != VT_FILETIME) return false; *filetime = property.filetime; return (filetime->dwHighDateTime != 0 || filetime->dwLowDateTime != 0); } private: QString m_target; const CArc &m_arc; QFutureInterface<QString> *m_futureInterface; UInt32 m_currentIndex; QString m_currentTarget; UInt64 m_totalUnpacked; UInt64 m_totalUnpackedExpected; COutFileStream *m_outFileStream; CMyComPtr<ISequentialOutStream> m_outFileStreamComPtr; }; // -- UnzipTask UnzipTask::UnzipTask(const QString &source, const QString &target) : m_source(source) , m_target(target) { Lib7z::initSevenZ(); } void UnzipTask::doTask(QFutureInterface<QString> &fi) { fi.reportStarted(); CCodecs codecs; if (codecs.Load() != S_OK) return; COpenOptions op; op.codecs = &codecs; CObjectVector<COpenType> types; op.types = &types; // Empty, because we use a stream. CIntVector excluded; op.excludedFormats = &excluded; const CMyComPtr<CInFileStream> fileStream = new CInFileStream; fileStream->Open((wchar_t*) (m_source.utf16())); op.stream = fileStream; // CMyComPtr is needed, otherwise it crashes in OpenStream(). CObjectVector<CProperty> properties; op.props = &properties; CArchiveLink archiveLink; if (archiveLink.Open2(op, nullptr) != S_OK) return; UINT32 count = 0; for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { const CArc& arc = archiveLink.Arcs[i]; UInt32 numItems = 0; if (arc.Archive->GetNumberOfItems(&numItems) != S_OK) break; count += numItems; } fi.setExpectedResultCount(count); for (unsigned i = 0; i < archiveLink.Arcs.Size(); ++i) { if (fi.isCanceled()) break; if (fi.isPaused()) fi.waitForResume(); const UInt32 extractAll = UInt32(-1); const CArc &arc = archiveLink.Arcs[i]; arc.Archive->Extract(0, extractAll, NArchive::NExtract::NAskMode::kExtract, new ArchiveExtractCallback(m_target, arc, fi)); } fi.reportFinished(); } } // namespace QInstaller ��������������������������������������������������������������src/libs/installer/unziptask.h����������������������������������������������������������������������0000664�0000000�0000000�00000003540�13253666515�0017012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef UNZIPTASK_H #define UNZIPTASK_H #include "abstracttask.h" #include "installer_global.h" namespace QInstaller { class INSTALLER_EXPORT UnzipTask : public AbstractTask<QString> { public: UnzipTask() {} UnzipTask(const QString &source, const QString &target); void doTask(QFutureInterface<QString> &fi); private: void setBytesToExtract(qint64 bytes); void addBytesExtracted(qint64 bytes); private: QString m_source; QString m_target; friend class ArchiveExtractCallback; }; } // namespace QInstaller #endif // UNZIPTASK_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/utils.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000034200�13253666515�0016452�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "utils.h" #include <QCoreApplication> #include <QDateTime> #include <QDir> #include <QProcessEnvironment> #include <QThread> #include <QVector> #if defined(Q_OS_WIN) || defined(Q_OS_WINCE) # include "qt_windows.h" #endif #include <fstream> #include <iostream> #include <sstream> #ifdef Q_OS_UNIX #include <errno.h> #include <signal.h> #include <time.h> #endif void QInstaller::uiDetachedWait(int ms) { QTime timer; timer.start(); do { QCoreApplication::processEvents(QEventLoop::AllEvents, ms); QThread::msleep(10UL); } while (timer.elapsed() < ms); } /*! Starts the program \a program with the arguments \a arguments in a new process, and detaches from it. Returns true on success; otherwise returns false. If the calling process exits, the detached process will continue to live. Note that arguments that contain spaces are not passed to the process as separate arguments. Unix: The started process will run in its own session and act like a daemon. Windows: Arguments that contain spaces are wrapped in quotes. The started process will run as a regular standalone process. The process will be started in the directory \a workingDirectory. If the function is successful then \a *pid is set to the process identifier of the started process. Additional note: The difference in using this function over its equivalent from QProcess appears on Windows. While this function will truly detach and not show a GUI window for the started process, the QProcess version will. */ bool QInstaller::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) { bool success = false; #ifdef Q_OS_WIN PROCESS_INFORMATION pinfo; STARTUPINFOW startupInfo = { sizeof(STARTUPINFO), 0, 0, 0, static_cast<ulong>(CW_USEDEFAULT), static_cast<ulong>(CW_USEDEFAULT), static_cast<ulong>(CW_USEDEFAULT), static_cast<ulong>(CW_USEDEFAULT), 0, 0, 0, STARTF_USESHOWWINDOW, SW_HIDE, 0, 0, 0, 0, 0 }; // That's the difference over QProcess::startDetached(): STARTF_USESHOWWINDOW, SW_HIDE. const QString commandline = QInstaller::createCommandline(program, arguments); if (CreateProcessW(0, (wchar_t*) commandline.utf16(), 0, 0, false, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0, workingDirectory.isEmpty() ? 0 : (wchar_t*) workingDirectory.utf16(), &startupInfo, &pinfo)) { success = true; CloseHandle(pinfo.hThread); CloseHandle(pinfo.hProcess); if (pid) *pid = pinfo.dwProcessId; } #else success = QProcess::startDetached(program, arguments, workingDirectory, pid); #endif return success; } // Returns ["en-us", "en"] for "en-us" QStringList QInstaller::localeCandidates(const QString &locale_) { QStringList candidates; QString locale = locale_; candidates.reserve(locale.count(QLatin1Char('-'))); forever { candidates.append(locale); int r = locale.lastIndexOf(QLatin1Char('-')); if (r <= 0) break; locale.truncate(r); } return candidates; } static bool verb = false; void QInstaller::setVerbose(bool v) { verb = v; } bool QInstaller::isVerbose() { return verb; } std::ostream &QInstaller::operator<<(std::ostream &os, const QString &string) { return os << qPrintable(string); } QByteArray QInstaller::calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo) { Q_ASSERT(device); QCryptographicHash hash(algo); static QByteArray buffer(1024 * 1024, '\0'); while (true) { const qint64 numRead = device->read(buffer.data(), buffer.size()); if (numRead <= 0) return hash.result(); hash.addData(buffer.constData(), numRead); } return QByteArray(); // never reached } QByteArray QInstaller::calculateHash(const QString &path, QCryptographicHash::Algorithm algo) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) return QByteArray(); return calculateHash(&file, algo); } QString QInstaller::replaceVariables(const QHash<QString, QString> &vars, const QString &str) { QString res; int pos = 0; while (true) { int pos1 = str.indexOf(QLatin1Char('@'), pos); if (pos1 == -1) break; int pos2 = str.indexOf(QLatin1Char('@'), pos1 + 1); if (pos2 == -1) break; res += str.mid(pos, pos1 - pos); QString name = str.mid(pos1 + 1, pos2 - pos1 - 1); res += vars.value(name); pos = pos2 + 1; } res += str.mid(pos); return res; } QString QInstaller::replaceWindowsEnvironmentVariables(const QString &str) { const QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString res; int pos = 0; while (true) { int pos1 = str.indexOf(QLatin1Char( '%'), pos); if (pos1 == -1) break; int pos2 = str.indexOf(QLatin1Char( '%'), pos1 + 1); if (pos2 == -1) break; res += str.mid(pos, pos1 - pos); QString name = str.mid(pos1 + 1, pos2 - pos1 - 1); res += env.value(name); pos = pos2 + 1; } res += str.mid(pos); return res; } QInstaller::VerboseWriter::VerboseWriter() { preFileBuffer.open(QIODevice::ReadWrite); stream.setDevice(&preFileBuffer); currentDateTimeAsString = QDateTime::currentDateTime().toString(); } QInstaller::VerboseWriter::~VerboseWriter() { if (preFileBuffer.isOpen()) { PlainVerboseWriterOutput output; (void)flush(&output); } } bool QInstaller::VerboseWriter::flush(VerboseWriterOutput *output) { stream.flush(); if (logFileName.isEmpty()) // binarycreator return true; if (!preFileBuffer.isOpen()) return true; //if the installer installed nothing - there is no target directory - where the logfile can be saved if (!QFileInfo(logFileName).absoluteDir().exists()) return true; QString logInfo; logInfo += QLatin1String("************************************* Invoked: "); logInfo += currentDateTimeAsString; logInfo += QLatin1String("\n"); QBuffer buffer; buffer.open(QIODevice::WriteOnly); buffer.write(logInfo.toLocal8Bit()); buffer.write(preFileBuffer.data()); buffer.close(); if (output->write(logFileName, QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text, buffer.data())) { preFileBuffer.close(); stream.setDevice(0); return true; } return false; } void QInstaller::VerboseWriter::setFileName(const QString &fileName) { logFileName = fileName; } Q_GLOBAL_STATIC(QInstaller::VerboseWriter, verboseWriter) QInstaller::VerboseWriter *QInstaller::VerboseWriter::instance() { return verboseWriter(); } void QInstaller::VerboseWriter::appendLine(const QString &msg) { stream << msg << endl; } QInstaller::VerboseWriterOutput::~VerboseWriterOutput() { } bool QInstaller::PlainVerboseWriterOutput::write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) { QFile output(fileName); if (output.open(openMode)) { output.write(data); return true; } return false; } #ifdef Q_OS_WIN // taken from qcoreapplication_p.h template<typename Char> static QVector<Char*> qWinCmdLine(Char *cmdParam, int length, int &argc) { QVector<Char*> argv(8); Char *p = cmdParam; Char *p_end = p + length; argc = 0; while (*p && p < p_end) { // parse cmd line arguments while (QChar((short)(*p)).isSpace()) // skip white space p++; if (*p && p < p_end) { // arg starts int quote; Char *start, *r; if (*p == Char('\"') || *p == Char('\'')) { // " or ' quote quote = *p; start = ++p; } else { quote = 0; start = p; } r = start; while (*p && p < p_end) { if (quote) { if (*p == quote) { p++; if (QChar((short)(*p)).isSpace()) break; quote = 0; } } if (*p == '\\') { // escape char? p++; if (*p == Char('\"') || *p == Char('\'')) ; // yes else p--; // treat \ literally } else { if (!quote && (*p == Char('\"') || *p == Char('\''))) { // " or ' quote quote = *p++; continue; } else if (QChar((short)(*p)).isSpace() && !quote) break; } if (*p) *r++ = *p++; } if (*p && p < p_end) p++; *r = Char('\0'); if (argc >= (int)argv.size()-1) // expand array argv.resize(argv.size()*2); argv[argc++] = start; } } argv[argc] = 0; return argv; } QStringList QInstaller::parseCommandLineArgs(int argc, char **argv) { Q_UNUSED(argc) Q_UNUSED(argv) QStringList arguments; QString cmdLine = QString::fromWCharArray(GetCommandLine()); QVector<wchar_t*> args = qWinCmdLine<wchar_t>((wchar_t *)cmdLine.utf16(), cmdLine.length(), argc); for (int a = 0; a < argc; ++a) arguments << QString::fromWCharArray(args[a]); return arguments; } #else QStringList QInstaller::parseCommandLineArgs(int argc, char **argv) { QStringList arguments; for (int a = 0; a < argc; ++a) arguments << QString::fromLocal8Bit(argv[a]); return arguments; } #endif #ifdef Q_OS_WIN // taken from qprocess_win.cpp static QString qt_create_commandline(const QString &program, const QStringList &arguments) { QString args; if (!program.isEmpty()) { QString programName = program; if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' '))) { programName = QLatin1Char('\"') + programName + QLatin1Char('\"'); } programName.replace(QLatin1Char('/'), QLatin1Char('\\')); // add the program as the first arg ... it works better args = programName + QLatin1Char(' '); } for (int i = 0; i < arguments.size(); ++i) { QString tmp = arguments.at(i); // in the case of \" already being in the string the \ must also be escaped tmp.replace(QLatin1String("\\\""), QLatin1String("\\\\\"")); // escape a single " because the arguments will be parsed tmp.replace(QLatin1Char('\"'), QLatin1String("\\\"")); if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { // The argument must not end with a \ since this would be interpreted // as escaping the quote -- rather put the \ behind the quote: e.g. // rather use "foo"\ than "foo\" QString endQuote(QLatin1Char('\"')); int i = tmp.length(); while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\')) { --i; endQuote += QLatin1Char('\\'); } args += QLatin1String(" \"") + tmp.left(i) + endQuote; } else { args += QLatin1Char(' ') + tmp; } } return args; } QString QInstaller::createCommandline(const QString &program, const QStringList &arguments) { return qt_create_commandline(program, arguments); } //copied from qsystemerror.cpp in Qt QString QInstaller::windowsErrorString(int errorCode) { QString ret; wchar_t *string = 0; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &string, 0, NULL); ret = QString::fromWCharArray(string); LocalFree((HLOCAL) string); if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND) ret = QCoreApplication::translate("QInstaller", "The specified module could not be found."); ret.append(QLatin1String(" (0x")); ret.append(QString::number(uint(errorCode), 16).rightJustified(8, QLatin1Char('0'))); ret.append(QLatin1String(")")); return ret; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/installer/utils.h��������������������������������������������������������������������������0000664�0000000�0000000�00000007172�13253666515�0016127�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QINSTALLER_UTILS_H #define QINSTALLER_UTILS_H #include "installer_global.h" #include <QtCore/QBuffer> #include <QtCore/QCryptographicHash> #include <QtCore/QHash> #include <QtCore/QUrl> #include <QtCore/QTextStream> #include <ostream> QT_BEGIN_NAMESPACE class QIODevice; QT_END_NAMESPACE namespace QInstaller { void INSTALLER_EXPORT uiDetachedWait(int ms); bool INSTALLER_EXPORT startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid = 0); QByteArray INSTALLER_EXPORT calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo); QByteArray INSTALLER_EXPORT calculateHash(const QString &path, QCryptographicHash::Algorithm algo); QString INSTALLER_EXPORT replaceVariables(const QHash<QString,QString> &vars, const QString &str); QString INSTALLER_EXPORT replaceWindowsEnvironmentVariables(const QString &str); QStringList INSTALLER_EXPORT parseCommandLineArgs(int argc, char **argv); #ifdef Q_OS_WIN QString windowsErrorString(int errorCode); QString createCommandline(const QString &program, const QStringList &arguments); #endif QStringList INSTALLER_EXPORT localeCandidates(const QString &locale); void INSTALLER_EXPORT setVerbose(bool v); bool INSTALLER_EXPORT isVerbose(); INSTALLER_EXPORT std::ostream& operator<<(std::ostream &os, const QString &string); class INSTALLER_EXPORT VerboseWriterOutput { public: virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) = 0; protected: ~VerboseWriterOutput(); }; class INSTALLER_EXPORT PlainVerboseWriterOutput : public VerboseWriterOutput { public: virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data); }; class INSTALLER_EXPORT VerboseWriter { public: VerboseWriter(); ~VerboseWriter(); static VerboseWriter *instance(); bool flush(VerboseWriterOutput *output); void appendLine(const QString &msg); void setFileName(const QString &fileName); private: QTextStream stream; QBuffer preFileBuffer; QString logFileName; QString currentDateTimeAsString; }; } #endif // QINSTALLER_UTILS_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/�����������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0014271�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/LICENSE.LGPL�����������������������������������������������������������������������0000664�0000000�0000000�00000063077�13253666515�0016050�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ The KD Tools Library is Copyright (C) 2001-2009 KlarÃĪlvdalens Datakonsult AB. You may use, distribute and copy the KD Tools Library under the terms of GNU Library General Public License version 2, which is displayed below. ------------------------------------------------------------------------- GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/authenticationdialog.ui������������������������������������������������������������0000664�0000000�0000000�00000006412�13253666515�0021032�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<ui version="4.0" > <class>Dialog</class> <widget class="QDialog" name="Dialog" > <property name="geometry" > <rect> <x>0</x> <y>0</y> <width>389</width> <height>243</height> </rect> </property> <property name="windowTitle" > <string>Http authentication required</string> </property> <layout class="QGridLayout" > <item row="0" column="0" colspan="2" > <widget class="QLabel" name="label" > <property name="text" > <string>You need to supply a Username and Password to access this site.</string> </property> <property name="wordWrap" > <bool>false</bool> </property> </widget> </item> <item row="2" column="0" > <widget class="QLabel" name="label_2" > <property name="text" > <string>Username:</string> </property> </widget> </item> <item row="2" column="1" > <widget class="QLineEdit" name="userEdit" /> </item> <item row="3" column="0" > <widget class="QLabel" name="label_3" > <property name="text" > <string>Password:</string> </property> </widget> </item> <item row="3" column="1" > <widget class="QLineEdit" name="passwordEdit"> <property name="echoMode"> <enum>QLineEdit::Password</enum> </property> </widget> </item> <item row="5" column="0" colspan="2" > <widget class="QDialogButtonBox" name="buttonBox" > <property name="orientation" > <enum>Qt::Horizontal</enum> </property> <property name="standardButtons" > <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> <item row="1" column="0" > <widget class="QLabel" name="label_4" > <property name="text" > <string></string> </property> </widget> </item> <item row="1" column="1" > <widget class="QLabel" name="siteDescription" > <property name="font" > <font> <weight>75</weight> <bold>true</bold> </font> </property> <property name="text" > <string>%1 at %2</string> </property> <property name="wordWrap" > <bool>true</bool> </property> </widget> </item> <item row="4" column="0" > <spacer> <property name="orientation" > <enum>Qt::Vertical</enum> </property> <property name="sizeHint" > <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>Dialog</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel" > <x>248</x> <y>254</y> </hint> <hint type="destinationlabel" > <x>157</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>Dialog</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel" > <x>316</x> <y>260</y> </hint> <hint type="destinationlabel" > <x>286</x> <y>274</y> </hint> </hints> </connection> </connections> </ui> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/environment.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000004647�13253666515�0017354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "environment.h" #include <QHash> #include <QProcess> #include <QProcessEnvironment> using namespace KDUpdater; Environment &Environment::instance() { static Environment s_instance; return s_instance; } QString Environment::value(const QString &key, const QString &defvalue) const { const QHash<QString, QString>::ConstIterator it = m_tempValues.constFind(key); if (it != m_tempValues.constEnd()) return *it; return QProcessEnvironment::systemEnvironment().value(key, defvalue); } void Environment::setTemporaryValue(const QString &key, const QString &value) { m_tempValues.insert(key, value); } QProcessEnvironment Environment::applyTo(const QProcessEnvironment &qpe_) const { QProcessEnvironment qpe(qpe_); QHash<QString, QString>::ConstIterator it = m_tempValues.constBegin(); const QHash<QString, QString>::ConstIterator end = m_tempValues.constEnd(); for ( ; it != end; ++it) qpe.insert(it.key(), it.value()); return qpe; } void Environment::applyTo(QProcess *proc) { proc->setProcessEnvironment(applyTo(proc->processEnvironment())); } �����������������������������������������������������������������������������������������src/libs/kdtools/environment.h����������������������������������������������������������������������0000664�0000000�0000000�00000004063�13253666515�0017011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef ENVIRONMENT_H #define ENVIRONMENT_H #include "kdtoolsglobal.h" #include <QString> #include <QHash> QT_BEGIN_NAMESPACE class QProcess; class QProcessEnvironment; QT_END_NAMESPACE namespace KDUpdater { class KDTOOLS_EXPORT Environment { public: static Environment &instance(); ~Environment() {} QString value(const QString &key, const QString &defaultValue = QString()) const; void setTemporaryValue(const QString &key, const QString &value); QProcessEnvironment applyTo(const QProcessEnvironment &qpe) const; void applyTo(QProcess *process); private: Environment() {} private: Q_DISABLE_COPY(Environment) QHash<QString, QString> m_tempValues; }; } // namespace KDUpdater #endif // ENVIRONMENT_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/filedownloader.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000132727�13253666515�0020007�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "filedownloader_p.h" #include "filedownloaderfactory.h" #include "ui_authenticationdialog.h" #include "fileutils.h" #include <QDialog> #include <QDir> #include <QFile> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkProxyFactory> #include <QPointer> #include <QUrl> #include <QTemporaryFile> #include <QFileInfo> #include <QThreadPool> #include <QDebug> #include <QSslError> #include <QBasicTimer> #include <QTimerEvent> #include <QLoggingCategory> #include <globals.h> #include <QHostInfo> using namespace KDUpdater; using namespace QInstaller; static double calcProgress(qint64 done, qint64 total) { return total ? (double(done) / double(total)) : 0; } // -- KDUpdater::FileDownloader /*! \inmodule kdupdater \class KDUpdater::FileDownloader \brief The FileDownloader class is the base class for file downloaders used in KDUpdater. File downloaders are used by the KDUpdater::Update class to download update files. Each subclass of FileDownloader can download files from a specific category of sources (such as \c local, \c ftp, \c http). This is an internal class, not a part of the public API. Currently we have the following subclasses of FileDownloader: \list \li HttpDownloader to download files over FTP, HTTP, or HTTPS if Qt is built with SSL. \li LocalFileDownloader to copy files from the local file system. \li ResourceFileDownloader to download resource files. \endlist */ /*! \property FileDownloader::autoRemoveDownloadedFile \brief Whether the downloaded file should be automatically removed after it is downloaded and the class goes out of scope. */ /*! \property FileDownloader::url \brief The URL to download files from. */ /*! \property FileDownloader::scheme \brief The scheme to use for downloading files. */ /*! \fn FileDownloader::authenticatorChanged(const QAuthenticator &authenticator) This signal is emitted when the authenticator changes to \a authenticator. */ /*! \fn FileDownloader::canDownload() const = 0 Returns \c true if the file exists and is readable. */ /*! \fn FileDownloader::clone(QObject *parent=0) const = 0 Clones the local file downloader and assigns it the parent \a parent. */ /*! \fn FileDownloader::downloadCanceled() This signal is emitted if downloading a file is canceled. */ /*! \fn FileDownloader::downloadedFileName() const = 0 Returns the file name of the downloaded file. */ /*! \fn FileDownloader::downloadProgress(double progress) This signal is emitted with the current download \a progress. */ /*! \fn FileDownloader::downloadProgress(qint64 bytesReceived, qint64 bytesToReceive) This signal is emitted with the download progress as the number of received bytes, \a bytesReceived, and the total size of the file to download, \a bytesToReceive. */ /*! \fn FileDownloader::downloadSpeed(qint64 bytesPerSecond) This signal is emitted with the download speed in bytes per second as \a bytesPerSecond. */ /*! \fn FileDownloader::downloadStarted() This signal is emitted when downloading a file starts. */ /*! \fn FileDownloader::downloadStatus(const QString &status) This signal is emitted with textual representation of the current download \a status in the following format: "100 MiB of 150 MiB - (DAYS) (HOURS) (MINUTES) (SECONDS) remaining". */ /*! \fn FileDownloader::estimatedDownloadTime(int seconds) This signal is emitted with the estimated download time in \a seconds. */ /*! \fn FileDownloader::isDownloaded() const = 0 Returns \c true if the file is downloaded. */ /*! \fn FileDownloader::onError() = 0 Closes the destination file if an error occurs during copying and stops the download speed timer. */ /*! \fn FileDownloader::onSuccess() = 0 Closes the destination file after it has been successfully copied and stops the download speed timer. */ /*! \fn FileDownloader::setDownloadedFileName(const QString &name) = 0 Sets the file name of the downloaded file to \a name. */ struct KDUpdater::FileDownloader::Private { Private() : m_hash(QCryptographicHash::Sha1) , m_assumedSha1Sum("") , autoRemove(true) , m_speedTimerInterval(100) , m_downloadDeadlineTimerInterval(30000) , m_downloadPaused(false) , m_downloadResumed(false) , m_bytesReceived(0) , m_bytesToReceive(0) , m_bytesBeforeResume(0) , m_totalBytesBeforeResume(0) , m_currentSpeedBin(0) , m_sampleIndex(0) , m_downloadSpeed(0) , m_factory(0) , m_ignoreSslErrors(false) { memset(m_samples, 0, sizeof(m_samples)); } ~Private() { delete m_factory; } QUrl url; QString scheme; QCryptographicHash m_hash; QByteArray m_assumedSha1Sum; QString errorString; bool autoRemove; bool followRedirect; QBasicTimer m_speedIntervalTimer; int m_speedTimerInterval; QBasicTimer m_downloadDeadlineTimer; int m_downloadDeadlineTimerInterval; bool m_downloadPaused; bool m_downloadResumed; qint64 m_bytesReceived; qint64 m_bytesToReceive; qint64 m_bytesBeforeResume; qint64 m_totalBytesBeforeResume; mutable qint64 m_samples[50]; mutable qint64 m_currentSpeedBin; mutable quint32 m_sampleIndex; mutable qint64 m_downloadSpeed; QAuthenticator m_authenticator; FileDownloaderProxyFactory *m_factory; bool m_ignoreSslErrors; }; /*! Creates a file downloader with the scheme \a scheme and parent \a parent. */ KDUpdater::FileDownloader::FileDownloader(const QString &scheme, QObject *parent) : QObject(parent) , d(new Private) { d->scheme = scheme; d->followRedirect = false; } /*! Destroys the file downloader. */ KDUpdater::FileDownloader::~FileDownloader() { delete d; } void KDUpdater::FileDownloader::setUrl(const QUrl &url) { d->url = url; } QUrl KDUpdater::FileDownloader::url() const { return d->url; } /*! Returns the SHA-1 checksum of the downloaded file. */ QByteArray KDUpdater::FileDownloader::sha1Sum() const { return d->m_hash.result(); } /*! Returns the assumed SHA-1 checksum of the file to download. */ QByteArray KDUpdater::FileDownloader::assumedSha1Sum() const { return d->m_assumedSha1Sum; } /*! Sets the assumed SHA-1 checksum of the file to download to \a sum. */ void KDUpdater::FileDownloader::setAssumedSha1Sum(const QByteArray &sum) { d->m_assumedSha1Sum = sum; } /*! Returns an error message. */ QString FileDownloader::errorString() const { return d->errorString; } /*! Sets the human readable description of the last error that occurred to \a error. Emits the downloadStatus() and downloadAborted() signals. */ void FileDownloader::setDownloadAborted(const QString &error) { d->errorString = error; emit downloadStatus(error); emit downloadAborted(error); } /*! Sets the download status to \c completed and displays a status message. If an assumed SHA-1 checksum is set and the actual calculated checksum does not match it, sets the status to \c error. If no SHA-1 is assumed, no check is performed, and status is set to \c success. Emits the downloadCompleted() and downloadStatus() signals on success. */ void KDUpdater::FileDownloader::setDownloadCompleted() { if (d->m_assumedSha1Sum.isEmpty() || (d->m_assumedSha1Sum == sha1Sum())) { onSuccess(); emit downloadCompleted(); emit downloadStatus(tr("Download finished.")); } else { onError(); setDownloadAborted(tr("Cryptographic hashes do not match.")); } } /*! Emits the downloadCanceled() and downloadStatus() signals. */ void KDUpdater::FileDownloader::setDownloadCanceled() { emit downloadCanceled(); emit downloadStatus(tr("Download canceled.")); } QString KDUpdater::FileDownloader::scheme() const { return d->scheme; } void KDUpdater::FileDownloader::setScheme(const QString &scheme) { d->scheme = scheme; } void KDUpdater::FileDownloader::setAutoRemoveDownloadedFile(bool val) { d->autoRemove = val; } /*! Determines that redirects should be followed if \a val is \c true. */ void KDUpdater::FileDownloader::setFollowRedirects(bool val) { d->followRedirect = val; } /*! Returns whether redirects should be followed. */ bool KDUpdater::FileDownloader::followRedirects() const { return d->followRedirect; } bool KDUpdater::FileDownloader::isAutoRemoveDownloadedFile() const { return d->autoRemove; } /*! Downloads files. */ void KDUpdater::FileDownloader::download() { QMetaObject::invokeMethod(this, "doDownload", Qt::QueuedConnection); } /*! Cancels file download. */ void KDUpdater::FileDownloader::cancelDownload() { // Do nothing } /*! Starts the download speed timer. */ void KDUpdater::FileDownloader::runDownloadSpeedTimer() { if (!d->m_speedIntervalTimer.isActive()) d->m_speedIntervalTimer.start(d->m_speedTimerInterval, this); } /*! Stops the download speed timer. */ void KDUpdater::FileDownloader::stopDownloadSpeedTimer() { d->m_speedIntervalTimer.stop(); } /*! Restarts the download deadline timer. */ void KDUpdater::FileDownloader::runDownloadDeadlineTimer() { stopDownloadDeadlineTimer(); d->m_downloadDeadlineTimer.start(d->m_downloadDeadlineTimerInterval, this); } /*! Stops the download deadline timer. */ void KDUpdater::FileDownloader::stopDownloadDeadlineTimer() { d->m_downloadDeadlineTimer.stop(); } /*! Sets the download into a paused state. */ void KDUpdater::FileDownloader::setDownloadPaused(bool paused) { d->m_downloadPaused = paused; } /*! Gets the download paused state. */ bool KDUpdater::FileDownloader::isDownloadPaused() { return d->m_downloadPaused; } /*! Sets the download into a paused state. */ void KDUpdater::FileDownloader::setDownloadResumed(bool resumed) { d->m_downloadResumed = resumed; } /*! Gets the download resumed state. */ bool KDUpdater::FileDownloader::isDownloadResumed() { return d->m_downloadResumed; } /*! Gets the amount of bytes downloaded before download resume. */ qint64 KDUpdater::FileDownloader::bytesDownloadedBeforeResume() { return d->m_bytesBeforeResume; } /*! Gets the total amount of bytes downloaded before download resume. */ qint64 KDUpdater::FileDownloader::totalBytesDownloadedBeforeResume() { return d->m_totalBytesBeforeResume; } /*! Clears the amount of bytes downloaded before download resume. */ void KDUpdater::FileDownloader::clearBytesDownloadedBeforeResume() { d->m_bytesBeforeResume = 0; d->m_totalBytesBeforeResume = 0; } /*! Updates the amount of bytes downloaded before download resume. */ void KDUpdater::FileDownloader::updateBytesDownloadedBeforeResume(qint64 bytes) { d->m_bytesBeforeResume += bytes; } /*! Updates the total amount of bytes downloaded before download resume. */ void KDUpdater::FileDownloader::updateTotalBytesDownloadedBeforeResume() { d->m_totalBytesBeforeResume = d->m_bytesBeforeResume; } /*! Adds \a sample to the current speed bin. */ void KDUpdater::FileDownloader::addSample(qint64 sample) { d->m_currentSpeedBin += sample; } /*! Returns the download speed timer ID. */ int KDUpdater::FileDownloader::downloadSpeedTimerId() const { return d->m_speedIntervalTimer.timerId(); } /*! Returns the download deadline timer ID. */ int KDUpdater::FileDownloader::downloadDeadlineTimerId() const { return d->m_downloadDeadlineTimer.timerId(); } /*! Sets the file download progress to the number of received bytes, \a bytesReceived, and the number of total bytes to receive, \a bytesToReceive. */ void KDUpdater::FileDownloader::setProgress(qint64 bytesReceived, qint64 bytesToReceive) { d->m_bytesReceived = bytesReceived; d->m_bytesToReceive = bytesToReceive; } /*! Calculates the download speed in bytes per second and emits the downloadSpeed() signal. */ void KDUpdater::FileDownloader::emitDownloadSpeed() { unsigned int windowSize = sizeof(d->m_samples) / sizeof(qint64); // add speed of last time bin to the window d->m_samples[d->m_sampleIndex % windowSize] = d->m_currentSpeedBin; d->m_currentSpeedBin = 0; // reset bin for next time interval // advance the sample index d->m_sampleIndex++; d->m_downloadSpeed = 0; // dynamic window size until the window is completely filled if (d->m_sampleIndex < windowSize) windowSize = d->m_sampleIndex; for (unsigned int i = 0; i < windowSize; ++i) d->m_downloadSpeed += d->m_samples[i]; d->m_downloadSpeed /= windowSize; // computer average d->m_downloadSpeed *= 1000.0 / d->m_speedTimerInterval; // rescale to bytes/second emit downloadSpeed(d->m_downloadSpeed); } /*! Builds a textual representation of the download status in the following format: "100 MiB of 150 MiB - (DAYS) (HOURS) (MINUTES) (SECONDS) remaining". Emits the downloadStatus() signal. */ void KDUpdater::FileDownloader::emitDownloadStatus() { QString status; if (d->m_bytesToReceive > 0) { QString bytesReceived = humanReadableSize(d->m_bytesReceived); const QString bytesToReceive = humanReadableSize(d->m_bytesToReceive); // remove the unit from the bytesReceived value if bytesToReceive has the same const QString tmp = bytesToReceive.mid(bytesToReceive.indexOf(QLatin1Char(' '))); if (bytesReceived.endsWith(tmp)) bytesReceived.chop(tmp.length()); status = tr("%1 of %2").arg(bytesReceived).arg(bytesToReceive); } else { if (d->m_bytesReceived > 0) status = tr("%1 downloaded.").arg(humanReadableSize(d->m_bytesReceived)); } status += QLatin1Char(' ') + tr("(%1/sec)").arg(humanReadableSize(d->m_downloadSpeed)); if (d->m_bytesToReceive > 0 && d->m_downloadSpeed > 0) { const qint64 time = (d->m_bytesToReceive - d->m_bytesReceived) / d->m_downloadSpeed; int s = time % 60; const int d = time / 86400; const int h = (time / 3600) - (d * 24); const int m = (time / 60) - (d * 1440) - (h * 60); QString days; if (d > 0) days = tr("%n day(s), ", "", d); QString hours; if (h > 0) hours = tr("%n hour(s), ", "", h); QString minutes; if (m > 0) minutes = tr("%n minute(s)", "", m); QString seconds; if (s >= 0 && minutes.isEmpty()) { s = (s <= 0 ? 1 : s); seconds = tr("%n second(s)", "", s); } status += tr(" - %1%2%3%4 remaining.").arg(days).arg(hours).arg(minutes).arg(seconds); } else { status += tr(" - unknown time remaining."); } emit downloadStatus(status); } /*! Emits dowload progress. */ void KDUpdater::FileDownloader::emitDownloadProgress() { emit downloadProgress(d->m_bytesReceived, d->m_bytesToReceive); } /*! Emits the estimated download time. */ void KDUpdater::FileDownloader::emitEstimatedDownloadTime() { if (d->m_bytesToReceive <= 0 || d->m_downloadSpeed <= 0) { emit estimatedDownloadTime(-1); return; } emit estimatedDownloadTime((d->m_bytesToReceive - d->m_bytesReceived) / d->m_downloadSpeed); } /*! \overload addCheckSumData() */ void KDUpdater::FileDownloader::addCheckSumData(const QByteArray &data) { d->m_hash.addData(data); } /*! Adds the \a length of characters of \a data to the cryptographic hash of the downloaded file. */ void KDUpdater::FileDownloader::addCheckSumData(const char *data, int length) { d->m_hash.addData(data, length); } /*! Resets SHA-1 checksum data of the downloaded file. */ void KDUpdater::FileDownloader::resetCheckSumData() { d->m_hash.reset(); } /*! Returns a copy of the proxy factory that this FileDownloader object is using to determine the proxies to be used for requests. */ FileDownloaderProxyFactory *KDUpdater::FileDownloader::proxyFactory() const { if (d->m_factory) return d->m_factory->clone(); return 0; } /*! Sets the proxy factory for this class to be \a factory. A proxy factory is used to determine a more specific list of proxies to be used for a given request, instead of trying to use the same proxy value for all requests. This might only be of use for HTTP or FTP requests. */ void KDUpdater::FileDownloader::setProxyFactory(FileDownloaderProxyFactory *factory) { delete d->m_factory; d->m_factory = factory; } /*! Returns a copy of the authenticator that this FileDownloader object is using to set the username and password for a download request. */ QAuthenticator KDUpdater::FileDownloader::authenticator() const { return d->m_authenticator; } /*! Sets the authenticator object for this class to be \a authenticator. An authenticator is used to pass on the required authentication information. This might only be of use for HTTP or FTP requests. Emits the authenticator changed signal with the new authenticator in use. */ void KDUpdater::FileDownloader::setAuthenticator(const QAuthenticator &authenticator) { if (d->m_authenticator.isNull() || (d->m_authenticator != authenticator)) { d->m_authenticator = authenticator; emit authenticatorChanged(authenticator); } } /*! Returns \c true if SSL errors should be ignored. */ bool KDUpdater::FileDownloader::ignoreSslErrors() { return d->m_ignoreSslErrors; } /*! Determines that SSL errors should be ignored if \a ignore is \c true. */ void KDUpdater::FileDownloader::setIgnoreSslErrors(bool ignore) { d->m_ignoreSslErrors = ignore; } // -- KDUpdater::LocalFileDownloader /*! \inmodule kdupdater \class KDUpdater::LocalFileDownloader \brief The LocalFileDownloader class is used to copy files from the local file system. The user of KDUpdater might be simultaneously downloading several files; sometimes in parallel to other file downloaders. If copying a local file takes a long time, it will make the other downloads hang. Therefore, a timer is used and one block of data is copied per unit time, even though QFile::copy() does the task of copying local files from one place to another. */ struct KDUpdater::LocalFileDownloader::Private { Private() : source(0) , destination(0) , downloaded(false) , timerId(-1) {} QFile *source; QFile *destination; QString destFileName; bool downloaded; int timerId; }; /*! Creates a local file downloader with the parent \a parent. */ KDUpdater::LocalFileDownloader::LocalFileDownloader(QObject *parent) : KDUpdater::FileDownloader(QLatin1String("file"), parent) , d (new Private) { } /*! Destroys the local file downloader. */ KDUpdater::LocalFileDownloader::~LocalFileDownloader() { if (this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty()) QFile::remove(d->destFileName); delete d; } /*! Returns \c true if the file exists and is readable. */ bool KDUpdater::LocalFileDownloader::canDownload() const { QFileInfo fi(url().toLocalFile()); return fi.exists() && fi.isReadable(); } /*! Returns \c true if the file is copied. */ bool KDUpdater::LocalFileDownloader::isDownloaded() const { return d->downloaded; } void KDUpdater::LocalFileDownloader::doDownload() { // Already downloaded if (d->downloaded) return; // Already started downloading if (d->timerId >= 0) return; // Open source and destination files QString localFile = this->url().toLocalFile(); d->source = new QFile(localFile, this); if (!d->source->open(QFile::ReadOnly)) { onError(); setDownloadAborted(tr("Cannot open file \"%1\" for reading: %2").arg(QFileInfo(localFile) .fileName(), d->source->errorString())); return; } if (d->destFileName.isEmpty()) { QTemporaryFile *file = new QTemporaryFile(this); file->open(); d->destination = file; } else { d->destination = new QFile(d->destFileName, this); d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate); } if (!d->destination->isOpen()) { onError(); setDownloadAborted(tr("Cannot open file \"%1\" for writing: %2") .arg(QFileInfo(d->destination->fileName()).fileName(), d->destination->errorString())); return; } runDownloadSpeedTimer(); // Start a timer and kickoff the copy process d->timerId = startTimer(0); // as fast as possible emit downloadStarted(); emit downloadProgress(0); } /*! Returns the file name of the copied file. */ QString KDUpdater::LocalFileDownloader::downloadedFileName() const { return d->destFileName; } /*! Sets the file name of the copied file to \a name. */ void KDUpdater::LocalFileDownloader::setDownloadedFileName(const QString &name) { d->destFileName = name; } /*! Clones the local file downloader and assigns it the parent \a parent. Returns the new local file downloader. */ KDUpdater::LocalFileDownloader *KDUpdater::LocalFileDownloader::clone(QObject *parent) const { return new LocalFileDownloader(parent); } /*! Cancels copying the file. */ void KDUpdater::LocalFileDownloader::cancelDownload() { if (d->timerId < 0) return; killTimer(d->timerId); d->timerId = -1; onError(); setDownloadCanceled(); } /*! Called when the download timer event \a event occurs. */ void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent *event) { if (event->timerId() == d->timerId) { if (!d->source || !d->destination) return; const qint64 blockSize = 32768; QByteArray buffer; buffer.resize(blockSize); const qint64 numRead = d->source->read(buffer.data(), buffer.size()); qint64 toWrite = numRead; while (toWrite > 0) { const qint64 numWritten = d->destination->write(buffer.constData() + numRead - toWrite, toWrite); if (numWritten < 0) { killTimer(d->timerId); d->timerId = -1; onError(); setDownloadAborted(tr("Writing to file \"%1\" failed: %2").arg( QDir::toNativeSeparators(d->destination->fileName()), d->destination->errorString())); return; } toWrite -= numWritten; } addSample(numRead); addCheckSumData(buffer.data(), numRead); if (numRead > 0) { setProgress(d->source->pos(), d->source->size()); emit downloadProgress(calcProgress(d->source->pos(), d->source->size())); return; } d->destination->flush(); killTimer(d->timerId); d->timerId = -1; setDownloadCompleted(); } else if (event->timerId() == downloadSpeedTimerId()) { emitDownloadSpeed(); emitDownloadStatus(); emitDownloadProgress(); emitEstimatedDownloadTime(); } } /*! Closes the destination file after it has been successfully copied and stops the download speed timer. */ void LocalFileDownloader::onSuccess() { d->downloaded = true; d->destFileName = d->destination->fileName(); if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination)) file->setAutoRemove(false); d->destination->close(); delete d->destination; d->destination = 0; delete d->source; d->source = 0; stopDownloadSpeedTimer(); } /*! Clears the destination file if an error occurs during copying and stops the download speed timer. */ void LocalFileDownloader::onError() { d->downloaded = false; d->destFileName.clear(); delete d->destination; d->destination = 0; delete d->source; d->source = 0; stopDownloadSpeedTimer(); } // -- ResourceFileDownloader /*! \inmodule kdupdater \class KDUpdater::ResourceFileDownloader \brief The ResourceFileDownloader class can be used to download resource files. */ struct KDUpdater::ResourceFileDownloader::Private { Private() : timerId(-1) , downloaded(false) {} int timerId; QFile destFile; bool downloaded; }; /*! Creates a resource file downloader with the parent \a parent. */ KDUpdater::ResourceFileDownloader::ResourceFileDownloader(QObject *parent) : KDUpdater::FileDownloader(QLatin1String("resource"), parent) , d(new Private) { } /*! Destroys the resource file downloader. */ KDUpdater::ResourceFileDownloader::~ResourceFileDownloader() { delete d; } /*! Returns \c true if the file exists and is readable. */ bool KDUpdater::ResourceFileDownloader::canDownload() const { const QFileInfo fi(QInstaller::pathFromUrl(url())); return fi.exists() && fi.isReadable(); } /*! Returns \c true if the file is downloaded. */ bool KDUpdater::ResourceFileDownloader::isDownloaded() const { return d->downloaded; } /*! Downloads a resource file. */ void KDUpdater::ResourceFileDownloader::doDownload() { // Already downloaded if (d->downloaded) return; // Already started downloading if (d->timerId >= 0) return; // Open source and destination files QUrl url = this->url(); url.setScheme(QString::fromLatin1("file")); d->destFile.setFileName(QString::fromLatin1(":%1").arg(url.toLocalFile())); emit downloadStarted(); emit downloadProgress(0); d->destFile.open(QIODevice::ReadOnly); d->timerId = startTimer(0); // start as fast as possible } /*! Returns the file name of the downloaded file. */ QString KDUpdater::ResourceFileDownloader::downloadedFileName() const { return d->destFile.fileName(); } /*! Sets the file name of the downloaded file to \a name. */ void KDUpdater::ResourceFileDownloader::setDownloadedFileName(const QString &/*name*/) { // Not supported! } /*! Clones the resource file downloader and assigns it the parent \a parent. Returns the new resource file downloader. */ KDUpdater::ResourceFileDownloader *KDUpdater::ResourceFileDownloader::clone(QObject *parent) const { return new ResourceFileDownloader(parent); } /*! Cancels downloading the file. */ void KDUpdater::ResourceFileDownloader::cancelDownload() { if (d->timerId < 0) return; killTimer(d->timerId); d->timerId = -1; setDownloadCanceled(); } /*! Called when the download timer event \a event occurs. */ void KDUpdater::ResourceFileDownloader::timerEvent(QTimerEvent *event) { if (event->timerId() == d->timerId) { if (!d->destFile.isOpen()) { onError(); killTimer(d->timerId); emit downloadProgress(1); setDownloadAborted(tr("Cannot read resource file \"%1\": %2").arg(downloadedFileName(), d->destFile.errorString())); return; } QByteArray buffer; buffer.resize(32768); const qint64 numRead = d->destFile.read(buffer.data(), buffer.size()); addSample(numRead); addCheckSumData(buffer.data(), numRead); if (numRead > 0) { setProgress(d->destFile.pos(), d->destFile.size()); emit downloadProgress(calcProgress(d->destFile.pos(), d->destFile.size())); return; } killTimer(d->timerId); d->timerId = -1; setDownloadCompleted(); } else if (event->timerId() == downloadSpeedTimerId()) { emitDownloadSpeed(); emitDownloadStatus(); emitDownloadProgress(); emitEstimatedDownloadTime(); } } /*! Closes the destination file after it has been successfully copied and stops the download speed timer. */ void KDUpdater::ResourceFileDownloader::onSuccess() { d->destFile.close(); d->downloaded = true; stopDownloadSpeedTimer(); } /*! Closes the destination file if an error occurs during copying and stops the download speed timer. */ void KDUpdater::ResourceFileDownloader::onError() { d->destFile.close(); d->downloaded = false; stopDownloadSpeedTimer(); d->destFile.setFileName(QString()); } // -- KDUpdater::HttpDownloader /*! \inmodule kdupdater \class KDUpdater::HttpDownloader \brief The HttpDownloader class is used to download files over FTP, HTTP, or HTTPS. HTTPS is supported if Qt is built with SSL. */ struct KDUpdater::HttpDownloader::Private { explicit Private(HttpDownloader *qq) : q(qq) , http(0) , destination(0) , downloaded(false) , aborted(false) , m_authenticationCount(0) {} HttpDownloader *const q; QNetworkAccessManager manager; QNetworkReply *http; QUrl sourceUrl; QFile *destination; QString destFileName; bool downloaded; bool aborted; int m_authenticationCount; void shutDown(bool closeDestination = true) { if (http) { disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished); disconnect(http, &QNetworkReply::downloadProgress, q, &HttpDownloader::httpReadProgress); void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; disconnect(http, errorSignal, q, &HttpDownloader::httpError); http->deleteLater(); } http = 0; if (closeDestination) { destination->close(); destination->deleteLater(); destination = 0; q->resetCheckSumData(); } } }; /*! Creates an HTTP downloader with the parent \a parent. */ KDUpdater::HttpDownloader::HttpDownloader(QObject *parent) : KDUpdater::FileDownloader(QLatin1String("http"), parent) , d(new Private(this)) { #ifndef QT_NO_SSL connect(&d->manager, &QNetworkAccessManager::sslErrors, this, &HttpDownloader::onSslErrors); #endif connect(&d->manager, &QNetworkAccessManager::authenticationRequired, this, &HttpDownloader::onAuthenticationRequired); connect(&d->manager, &QNetworkAccessManager::networkAccessibleChanged, this, &HttpDownloader::onNetworkAccessibleChanged); } /*! Destroys an HTTP downloader. Removes the downloaded file if FileDownloader::isAutoRemoveDownloadedFile() returns \c true or FileDownloader::setAutoRemoveDownloadedFile() was called with \c true. */ KDUpdater::HttpDownloader::~HttpDownloader() { if (this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty()) QFile::remove(d->destFileName); delete d; } /*! Returns \c true if the file exists and is readable. */ bool KDUpdater::HttpDownloader::canDownload() const { // TODO: Check whether the http file actually exists or not. return true; } /*! Returns \c true if the file is downloaded. */ bool KDUpdater::HttpDownloader::isDownloaded() const { return d->downloaded; } void KDUpdater::HttpDownloader::doDownload() { if (d->downloaded) return; if (d->http) return; startDownload(url()); runDownloadSpeedTimer(); runDownloadDeadlineTimer(); } /*! Returns the file name of the downloaded file. */ QString KDUpdater::HttpDownloader::downloadedFileName() const { return d->destFileName; } /*! Sets the file name of the downloaded file to \a name. */ void KDUpdater::HttpDownloader::setDownloadedFileName(const QString &name) { d->destFileName = name; } /*! Clones the HTTP downloader and assigns it the parent \a parent. Returns the new HTTP downloader. */ KDUpdater::HttpDownloader *KDUpdater::HttpDownloader::clone(QObject *parent) const { return new HttpDownloader(parent); } void KDUpdater::HttpDownloader::httpReadyRead() { if (d->http == 0 || d->destination == 0) return; static QByteArray buffer(16384, '\0'); while (d->http->bytesAvailable()) { const qint64 read = d->http->read(buffer.data(), buffer.size()); qint64 written = 0; while (written < read) { const qint64 numWritten = d->destination->write(buffer.data() + written, read - written); if (numWritten < 0) { const QString error = d->destination->errorString(); const QString fileName = d->destination->fileName(); d->shutDown(); setDownloadAborted(tr("Cannot download %1. Writing to file \"%2\" failed: %3") .arg(url().toString(), fileName, error)); return; } written += numWritten; } addSample(written); addCheckSumData(buffer.data(), read); updateBytesDownloadedBeforeResume(written); } } void KDUpdater::HttpDownloader::httpError(QNetworkReply::NetworkError) { if (!d->aborted) httpDone(true); } /*! Cancels downloading the file. */ void KDUpdater::HttpDownloader::cancelDownload() { d->aborted = true; if (d->http) { d->http->abort(); httpDone(true); } } void KDUpdater::HttpDownloader::httpDone(bool error) { if (error) { if (isDownloadResumed()) { d->shutDown(false); return; } QString err; if (d->http) { err = d->http->errorString(); d->http->deleteLater(); d->http = 0; onError(); } if (d->aborted) { d->aborted = false; setDownloadCanceled(); } else { setDownloadAborted(err); return; } } setDownloadResumed(false); } /*! Closes the destination file if an error occurs during copying and stops the download speed timer. */ void KDUpdater::HttpDownloader::onError() { d->downloaded = false; d->destFileName.clear(); delete d->destination; d->destination = 0; stopDownloadSpeedTimer(); stopDownloadDeadlineTimer(); } /*! Closes the destination file after it has been successfully copied and stops the download speed timer. */ void KDUpdater::HttpDownloader::onSuccess() { d->downloaded = true; if (d->destination) { d->destFileName = d->destination->fileName(); if (QTemporaryFile *file = dynamic_cast<QTemporaryFile *>(d->destination)) file->setAutoRemove(false); } delete d->destination; d->destination = 0; stopDownloadSpeedTimer(); stopDownloadDeadlineTimer(); setDownloadResumed(false); } void KDUpdater::HttpDownloader::httpReqFinished() { const QVariant redirect = d->http == 0 ? QVariant() : d->http->attribute(QNetworkRequest::RedirectionTargetAttribute); const QUrl redirectUrl = redirect.toUrl(); if (followRedirects() && redirectUrl.isValid()) { d->shutDown(); // clean the previous download startDownload(redirectUrl); } else { if (d->http == 0) return; const QUrl url = d->http->url(); if (url.isValid() && QInstaller::lcNetwork().isDebugEnabled()){ const QFileInfo fi(d->http->url().toString()); if (fi.suffix() != QLatin1String("sha1")){ const QString hostName = url.host(); QHostInfo info = QHostInfo::fromName(hostName); QStringList hostAddresses; foreach (const QHostAddress &address, info.addresses()) hostAddresses << address.toString(); qCDebug(QInstaller::lcNetwork) << "Using host:" << hostName << "for" << url.fileName() << "\nIP:" << hostAddresses; } } httpReadyRead(); if (d->destination) d->destination->flush(); setDownloadCompleted(); if (d->http) d->http->deleteLater(); d->http = 0; } } void KDUpdater::HttpDownloader::httpReadProgress(qint64 done, qint64 total) { if (d->http) { const QUrl redirectUrl = d->http->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (followRedirects() && redirectUrl.isValid()) return; // if we are a redirection, do not emit the progress } if (isDownloadResumed()) setProgress(done + totalBytesDownloadedBeforeResume(), total + totalBytesDownloadedBeforeResume()); else setProgress(done, total); runDownloadDeadlineTimer(); if (isDownloadResumed()) emit downloadProgress(calcProgress(done + totalBytesDownloadedBeforeResume(), total + totalBytesDownloadedBeforeResume())); else emit downloadProgress(calcProgress(done, total)); } /*! Called when the download timer event \a event occurs. */ void KDUpdater::HttpDownloader::timerEvent(QTimerEvent *event) { if (event->timerId() == downloadSpeedTimerId()) { emitDownloadSpeed(); emitDownloadStatus(); emitDownloadProgress(); emitEstimatedDownloadTime(); } else if (event->timerId() == downloadDeadlineTimerId()) { d->shutDown(false); resumeDownload(); } } void KDUpdater::HttpDownloader::startDownload(const QUrl &url) { d->sourceUrl = url; d->m_authenticationCount = 0; d->manager.setProxyFactory(proxyFactory()); clearBytesDownloadedBeforeResume(); d->http = d->manager.get(QNetworkRequest(url)); connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead); connect(d->http, &QNetworkReply::downloadProgress, this, &HttpDownloader::httpReadProgress); connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished); void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; connect(d->http, errorSignal, this, &HttpDownloader::httpError); if (d->destFileName.isEmpty()) { QTemporaryFile *file = new QTemporaryFile(this); file->open(); d->destination = file; } else { d->destination = new QFile(d->destFileName, this); d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate); } if (!d->destination->isOpen()) { const QString error = d->destination->errorString(); const QString fileName = d->destination->fileName(); d->shutDown(); setDownloadAborted(tr("Cannot download %1. Cannot create file \"%2\": %3").arg( url.toString(), fileName, error)); } } void KDUpdater::HttpDownloader::resumeDownload() { updateTotalBytesDownloadedBeforeResume(); d->m_authenticationCount = 0; QNetworkRequest request(d->sourceUrl); request.setRawHeader(QByteArray("Range"), QString(QStringLiteral("bytes=%1-")) .arg(bytesDownloadedBeforeResume()) .toLatin1()); setDownloadResumed(true); d->http = d->manager.get(request); connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead); connect(d->http, &QNetworkReply::downloadProgress, this, &HttpDownloader::httpReadProgress); connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished); void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error; connect(d->http, errorSignal, this, &HttpDownloader::httpError); runDownloadSpeedTimer(); runDownloadDeadlineTimer(); } void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) { Q_UNUSED(reply) // first try with the information we have already if (d->m_authenticationCount == 0) { d->m_authenticationCount++; authenticator->setUser(this->authenticator().user()); authenticator->setPassword(this->authenticator().password()); } else if (d->m_authenticationCount == 1) { // we failed to authenticate, ask for new credentials QDialog dlg; Ui::Dialog ui; ui.setupUi(&dlg); dlg.adjustSize(); ui.siteDescription->setText(tr("%1 at %2").arg(authenticator->realm()).arg(url().host())); ui.userEdit->setText(this->authenticator().user()); ui.passwordEdit->setText(this->authenticator().password()); if (dlg.exec() == QDialog::Accepted) { authenticator->setUser(ui.userEdit->text()); authenticator->setPassword(ui.passwordEdit->text()); // update the authenticator we used initially QAuthenticator auth; auth.setUser(ui.userEdit->text()); auth.setPassword(ui.passwordEdit->text()); emit authenticatorChanged(auth); } else { d->shutDown(); setDownloadAborted(tr("Authentication request canceled.")); emit downloadCanceled(); } d->m_authenticationCount++; } } void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) { if (accessible == QNetworkAccessManager::NotAccessible) { d->shutDown(false); setDownloadPaused(true); setDownloadResumed(false); stopDownloadDeadlineTimer(); } else if (accessible == QNetworkAccessManager::Accessible) { if (isDownloadPaused()) { setDownloadPaused(false); resumeDownload(); } } } #ifndef QT_NO_SSL #include "messageboxhandler.h" void KDUpdater::HttpDownloader::onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors) { Q_UNUSED(reply) QString errorString; foreach (const QSslError &error, errors) { if (!errorString.isEmpty()) errorString += QLatin1String(", "); errorString += error.errorString(); } qDebug() << errorString; const QStringList arguments = QCoreApplication::arguments(); if (arguments.contains(QLatin1String("--script")) || arguments.contains(QLatin1String("Script")) || ignoreSslErrors()) { reply->ignoreSslErrors(); return; } // TODO: Remove above code once we have a proper implementation for message box handler supporting // methods used in the following code, right now we return here cause the message box is not scriptable. QMessageBox msgBox(MessageBoxHandler::currentBestSuitParent()); msgBox.setDetailedText(errorString); msgBox.setIcon(QMessageBox::Warning); msgBox.setWindowModality(Qt::WindowModal); msgBox.setWindowTitle(tr("Secure Connection Failed")); msgBox.setText(tr("There was an error during connection to: %1.").arg(url().toString())); msgBox.setInformativeText(QString::fromLatin1("<ul><li>%1</li><li>%2</li></ul>").arg(tr("This could be " "a problem with the server's configuration, or it could be someone trying to impersonate the " "server."), tr("If you have connected to this server successfully in the past or trust this server, " "the error may be temporary and you can try again."))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); msgBox.setButtonText(QMessageBox::Yes, tr("Try again")); msgBox.setDefaultButton(QMessageBox::Cancel); if (msgBox.exec() == QMessageBox::Cancel) { if (!d->aborted) httpDone(true); } else { reply->ignoreSslErrors(); KDUpdater::FileDownloaderFactory::instance().setIgnoreSslErrors(true); } } #endif �����������������������������������������src/libs/kdtools/filedownloader.h�������������������������������������������������������������������0000664�0000000�0000000�00000011551�13253666515�0017443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef FILEDOWNLOADER_H #define FILEDOWNLOADER_H #include "kdtoolsglobal.h" #include <QtCore/QObject> #include <QtCore/QUrl> #include <QtNetwork/QAuthenticator> namespace KDUpdater { class FileDownloaderProxyFactory; class KDTOOLS_EXPORT FileDownloader : public QObject { Q_OBJECT Q_PROPERTY(bool autoRemoveDownloadedFile READ isAutoRemoveDownloadedFile WRITE setAutoRemoveDownloadedFile) Q_PROPERTY(QUrl url READ url WRITE setUrl) Q_PROPERTY(QString scheme READ scheme WRITE setScheme) public: explicit FileDownloader(const QString &scheme, QObject *parent = 0); ~FileDownloader(); QUrl url() const; void setUrl(const QUrl &url); QByteArray sha1Sum() const; QByteArray assumedSha1Sum() const; void setAssumedSha1Sum(const QByteArray &sha1); QString scheme() const; void setScheme(const QString &scheme); QString errorString() const; virtual bool canDownload() const = 0; virtual bool isDownloaded() const = 0; virtual QString downloadedFileName() const = 0; virtual void setDownloadedFileName(const QString &name) = 0; virtual FileDownloader *clone(QObject *parent=0) const = 0; void download(); void setAutoRemoveDownloadedFile(bool val); bool isAutoRemoveDownloadedFile() const; void setFollowRedirects(bool val); bool followRedirects() const; FileDownloaderProxyFactory *proxyFactory() const; void setProxyFactory(FileDownloaderProxyFactory *factory); QAuthenticator authenticator() const; void setAuthenticator(const QAuthenticator &authenticator); bool ignoreSslErrors(); void setIgnoreSslErrors(bool ignore); public Q_SLOTS: virtual void cancelDownload(); protected: virtual void onError() = 0; virtual void onSuccess() = 0; Q_SIGNALS: void downloadStarted(); void downloadCanceled(); void downloadProgress(double progress); void estimatedDownloadTime(int seconds); void downloadSpeed(qint64 bytesPerSecond); void downloadStatus(const QString &status); void downloadProgress(qint64 bytesReceived, qint64 bytesToReceive); void authenticatorChanged(const QAuthenticator &authenticator); void downloadCompleted(); void downloadAborted(const QString &errorMessage); protected: void setDownloadCanceled(); void setDownloadCompleted(); void setDownloadAborted(const QString &error); void runDownloadSpeedTimer(); void stopDownloadSpeedTimer(); void runDownloadDeadlineTimer(); void stopDownloadDeadlineTimer(); void setDownloadPaused(bool paused); bool isDownloadPaused(); void setDownloadResumed(bool resumed); bool isDownloadResumed(); qint64 bytesDownloadedBeforeResume(); qint64 totalBytesDownloadedBeforeResume(); void clearBytesDownloadedBeforeResume(); void updateBytesDownloadedBeforeResume(qint64 bytes); void updateTotalBytesDownloadedBeforeResume(); void addSample(qint64 sample); int downloadSpeedTimerId() const; int downloadDeadlineTimerId() const; void setProgress(qint64 bytesReceived, qint64 bytesToReceive); void emitDownloadSpeed(); void emitDownloadStatus(); void emitDownloadProgress(); void emitEstimatedDownloadTime(); void addCheckSumData(const QByteArray &data); void addCheckSumData(const char *data, int length); void resetCheckSumData(); private Q_SLOTS: virtual void doDownload() = 0; private: struct Private; Private *d; }; } // namespace KDUpdater #endif // FILEDOWNLOADER_H �������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/filedownloader_p.h�����������������������������������������������������������������0000664�0000000�0000000�00000007720�13253666515�0017765�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef FILEDOWNLOADER_P_H #define FILEDOWNLOADER_P_H #include "filedownloader.h" #include <QtNetwork/QNetworkReply> #include <QNetworkAccessManager> // these classes are not a part of the public API namespace KDUpdater { class LocalFileDownloader : public FileDownloader { Q_OBJECT public: explicit LocalFileDownloader(QObject *parent = 0); ~LocalFileDownloader(); bool canDownload() const; bool isDownloaded() const; QString downloadedFileName() const; void setDownloadedFileName(const QString &name); LocalFileDownloader *clone(QObject *parent = 0) const; public Q_SLOTS: void cancelDownload(); protected: void timerEvent(QTimerEvent *te); void onError(); void onSuccess(); private Q_SLOTS: void doDownload(); private: struct Private; Private *d; }; class ResourceFileDownloader : public FileDownloader { Q_OBJECT public: explicit ResourceFileDownloader(QObject *parent = 0); ~ResourceFileDownloader(); bool canDownload() const; bool isDownloaded() const; QString downloadedFileName() const; void setDownloadedFileName(const QString &name); ResourceFileDownloader *clone(QObject *parent = 0) const; public Q_SLOTS: void cancelDownload(); protected: void timerEvent(QTimerEvent *te); void onError(); void onSuccess(); private Q_SLOTS: void doDownload(); private: struct Private; Private *d; }; class HttpDownloader : public FileDownloader { Q_OBJECT public: explicit HttpDownloader(QObject *parent = 0); ~HttpDownloader(); bool canDownload() const; bool isDownloaded() const; QString downloadedFileName() const; void setDownloadedFileName(const QString &name); HttpDownloader *clone(QObject *parent = 0) const; public Q_SLOTS: void cancelDownload(); protected: void onError(); void onSuccess(); void timerEvent(QTimerEvent *event); private Q_SLOTS: void doDownload(); void httpReadyRead(); void httpReadProgress(qint64 done, qint64 total); void httpError(QNetworkReply::NetworkError); void httpDone(bool error); void httpReqFinished(); void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator); void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible); #ifndef QT_NO_SSL void onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors); #endif private: void startDownload(const QUrl &url); void resumeDownload(); private: struct Private; Private *d; }; } // namespace KDUpdater #endif // FILEDOWNLOADER_P_H ������������������������������������������������src/libs/kdtools/filedownloaderfactory.cpp����������������������������������������������������������0000664�0000000�0000000�00000014432�13253666515�0021367�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "filedownloaderfactory.h" #include "filedownloader_p.h" #include <QtNetwork/QSslSocket> using namespace KDUpdater; /*! \inmodule kdupdater \class KDUpdater::FileDownloaderFactory \brief The FileDownloaderFactory class acts as a factory for KDUpdater::FileDownloader. You can register one or more file downloaders with this factory and query them based on their scheme. The class follows the singleton design pattern. Only one instance of this class can be created and its reference can be fetched from the instance() method. */ /*! Returns the file downloader factory instance. */ FileDownloaderFactory& FileDownloaderFactory::instance() { static KDUpdater::FileDownloaderFactory theFactory; return theFactory; } /*! Constructs a file downloader factory and registers the default file downloader set. */ FileDownloaderFactory::FileDownloaderFactory() : d (new FileDownloaderFactoryData) { // Register the default file downloader set registerFileDownloader<LocalFileDownloader>( QLatin1String("file")); registerFileDownloader<HttpDownloader>(QLatin1String("ftp")); registerFileDownloader<HttpDownloader>(QLatin1String("http")); registerFileDownloader<ResourceFileDownloader >(QLatin1String("resource")); #ifndef QT_NO_SSL if (QSslSocket::supportsSsl()) registerFileDownloader<HttpDownloader>(QLatin1String("https")); else qWarning() << "Cannot register file downloader for https protocol: QSslSocket::supportsSsl() returns false"; #endif d->m_followRedirects = false; } /*! Returns whether redirects should be followed. */ bool FileDownloaderFactory::followRedirects() { return FileDownloaderFactory::instance().d->m_followRedirects; } /*! Determines that redirects should be followed if \a val is \c true. */ void FileDownloaderFactory::setFollowRedirects(bool val) { FileDownloaderFactory::instance().d->m_followRedirects = val; } /*! Sets \a factory as the file downloader proxy factory. */ void FileDownloaderFactory::setProxyFactory(FileDownloaderProxyFactory *factory) { delete FileDownloaderFactory::instance().d->m_factory; FileDownloaderFactory::instance().d->m_factory = factory; } /*! Returns \c true if SSL errors should be ignored. */ bool FileDownloaderFactory::ignoreSslErrors() { return FileDownloaderFactory::instance().d->m_ignoreSslErrors; } /*! Determines that SSL errors should be ignored if \a ignore is \c true. */ void FileDownloaderFactory::setIgnoreSslErrors(bool ignore) { FileDownloaderFactory::instance().d->m_ignoreSslErrors = ignore; } /*! Destroys the file downloader factory. */ FileDownloaderFactory::~FileDownloaderFactory() { delete d; } /*! Returns a list of supported schemes. */ QStringList FileDownloaderFactory::supportedSchemes() { return FileDownloaderFactory::instance().d->m_supportedSchemes; } /*! Returns \c true if \a scheme is a supported scheme. */ bool FileDownloaderFactory::isSupportedScheme(const QString &scheme) { return FileDownloaderFactory::instance().d->m_supportedSchemes.contains(scheme , Qt::CaseInsensitive); } /*! Returns a new instance of a KDUpdater::FileDownloader subclass. The instantiation of a subclass depends on the communication protocol string stored in \a scheme with the parent \a parent. \note Ownership of the created object remains with the programmer. */ FileDownloader *FileDownloaderFactory::create(const QString &scheme, QObject *parent) const { FileDownloader *downloader = GenericFactory<FileDownloader, QString, QObject*>::create(scheme, parent); if (downloader != 0) { downloader->setFollowRedirects(d->m_followRedirects); downloader->setIgnoreSslErrors(d->m_ignoreSslErrors); if (d->m_factory) downloader->setProxyFactory(d->m_factory->clone()); } return downloader; } /*! \fn void KDUpdater::FileDownloaderFactory::registerFileDownloader(const QString &scheme) Registers a new file downloader with the factory based on \a scheme. If there is already a downloader with the same scheme, the downloader is replaced. When create() is called with that \a scheme, the file downloader is constructed using its default constructor. */ /*! \inmodule kdupdater \class KDUpdater::FileDownloaderProxyFactory \brief The FileDownloaderProxyFactory class provides fine-grained proxy selection. File downloader objects use a proxy factory to determine a more specific list of proxies to be used for a given request, instead of trying to use the same proxy value for all requests. This might only be of use for HTTP or FTP requests. */ /*! \fn FileDownloaderProxyFactory::~FileDownloaderProxyFactory() Destroys the file downloader proxy factory. */ /*! \fn FileDownloaderProxyFactory::clone() const Clones a file downloader proxy factory. */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/filedownloaderfactory.h������������������������������������������������������������0000664�0000000�0000000�00000006250�13253666515�0021033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef FILEDOWNLOADERFACTORY_H #define FILEDOWNLOADERFACTORY_H #include "genericfactory.h" #include "updater.h" #include <QtCore/QStringList> #include <QtCore/QUrl> #include <QtNetwork/QNetworkProxyFactory> QT_BEGIN_NAMESPACE class QObject; QT_END_NAMESPACE namespace KDUpdater { class FileDownloader; class KDTOOLS_EXPORT FileDownloaderProxyFactory : public QNetworkProxyFactory { public: virtual ~FileDownloaderProxyFactory() {} virtual FileDownloaderProxyFactory *clone() const = 0; }; class KDTOOLS_EXPORT FileDownloaderFactory : public GenericFactory<FileDownloader, QString, QObject*> { Q_DISABLE_COPY(FileDownloaderFactory) struct FileDownloaderFactoryData { FileDownloaderFactoryData() : m_factory(0) {} ~FileDownloaderFactoryData() { delete m_factory; } bool m_followRedirects; bool m_ignoreSslErrors; QStringList m_supportedSchemes; FileDownloaderProxyFactory *m_factory; }; public: static FileDownloaderFactory &instance(); ~FileDownloaderFactory(); template<typename T> void registerFileDownloader(const QString &scheme) { registerProduct<T>(scheme); d->m_supportedSchemes.append(scheme); } FileDownloader *create(const QString &scheme, QObject *parent = 0) const; static bool followRedirects(); static void setFollowRedirects(bool val); static void setProxyFactory(FileDownloaderProxyFactory *factory); static bool ignoreSslErrors(); static void setIgnoreSslErrors(bool ignore); static QStringList supportedSchemes(); static bool isSupportedScheme(const QString &scheme); private: FileDownloaderFactory(); private: FileDownloaderFactoryData *d; }; } // namespace KDUpdater #endif // FILEDOWNLOADERFACTORY_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/genericfactory.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000012121�13253666515�0017776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "genericfactory.h" /*! \inmodule kdupdater \namespace KDUpdater \brief The KDUpdater classes provide functions to automatically detect updates to applications, to retrieve them from external repositories, and to install them. KDUpdater classes are a fork of KDAB's general \l{http://docs.kdab.com/kdtools/2.2.2/group__kdupdater.html}{KDUpdater module}. */ /*! \inmodule kdupdater \class GenericFactory \brief The GenericFactory class implements a template-based generic factory. GenericFactory is an implementation of the factory pattern. It can be used to produce instances of different classes having a common superclass \c BASE. The user of the factory registers those producible classes in the factory by using the identifier \c IDENTIFIER. That identifier can then be used to produce as many instances of the registered product as the user wants. One factory instance is able to produce instances of different types of DERIVED classes only when the constructor of DERIVED or the registered generator function have a common signature for all DERIVED classes. This signature is described by the declaration order of ARGUMENTS. It is referred to as SIGNATURE in the following paragraphs. If a class derived from BASE does not contain a SIGNATURE matching the registered one for the constructor or the generator function, it is not possible to create instances of it using one instance of GenericFactory subclass. In that case, more than one GenericFactory subclass and instance are needed. It is possible to register a subclass of BASE inside an instance of GenericFactory subclass using the registerProduct() function. At least one of the following conditions needs to be met: \list \li A global or static function with SIGNATURE exists. \li The DERIVED class has a constructor with SIGNATURE. \li The DERIVED class has a static function with SIGNATURE. \endlist To get a new instance of DERIVED, one needs to call the create() function. The value of IDENTIFIER determines the product's subclass registered in the factory, while the values of SIGNATURE are the actual arguments passed to the class constructor or the registered generator function. */ /*! \fn GenericFactory::GenericFactory() Creates the generic factory. */ /*! \fn GenericFactory::~GenericFactory() Destroys the generic factory. */ /*! \typedef GenericFactory::FactoryFunction This typedef defines a factory function producing an object of type BASE. */ /*! \fn void GenericFactory::registerProduct(const IDENTIFIER &id) Registers a type DERIVED, identified by \a id in the factory. Any type with the same id gets unregistered. */ /*! \overload \fn void GenericFactory::registerProduct(const IDENTIFIER &id, FactoryFunction func) Registers a function \a func that can create the type DERIVED, identified by \a id in the factory. Any type with the same id gets unregistered. */ /*! \fn bool GenericFactory::containsProduct(const IDENTIFIER &id) const Returns \c true if the factory contains a type with the \a id; otherwise returns false. */ /*! \fn BASE *GenericFactory::create(const IDENTIFIER &id, ARGUMENTS... args) const Creates and returns the type identified by \a id, but automatically upcasted to BASE. Ownership of the type is transferred to the caller. */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/genericfactory.h�������������������������������������������������������������������0000664�0000000�0000000�00000005661�13253666515�0017456�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef GENERICFACTORY_H #define GENERICFACTORY_H #include "kdtoolsglobal.h" #include <QtCore/QHash> template <typename BASE, typename IDENTIFIER = QString, typename... ARGUMENTS> class GenericFactory { public: virtual ~GenericFactory() {} typedef BASE *(*FactoryFunction)(ARGUMENTS...); template <typename DERIVED> void registerProduct(const IDENTIFIER &id) { m_hash.insert(id, &GenericFactory::create<DERIVED>); } void registerProduct(const IDENTIFIER &id, FactoryFunction func) { m_hash.insert(id, func); } bool containsProduct(const IDENTIFIER &id) const { return m_hash.contains(id); } BASE *create(const IDENTIFIER &id, ARGUMENTS... args) const { const auto it = m_hash.constFind(id); if (it == m_hash.constEnd()) return 0; return (*it)(std::forward<ARGUMENTS>(args)...); } protected: GenericFactory() = default; private: template <typename DERIVED> static BASE *create(ARGUMENTS... args) { return new DERIVED(std::forward<ARGUMENTS>(args)...); } GenericFactory(const GenericFactory &) = delete; GenericFactory &operator=(const GenericFactory &) = delete; private: QHash<IDENTIFIER, FactoryFunction> m_hash; }; #endif // GENERICFACTORY_H �������������������������������������������������������������������������������src/libs/kdtools/job.cpp����������������������������������������������������������������������������0000664�0000000�0000000�00000012177�13253666515�0015557�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "job.h" #include <QDebug> #include <QEventLoop> #include <QTimer> // -- Job::Private class Job::Private { Job *const q; public: explicit Private(Job *qq) : q(qq) , error(Job::NoError) , caps(Job::NoCapabilities) , autoDelete(true) , totalAmount(100) , processedAmount(0) , m_timeout(-1) { connect(&m_timer, &QTimer::timeout, q, &Job::cancel); } ~Private() { m_timer.stop(); } void delayedStart() { q->doStart(); emit q->started(q); } void waitForSignal(const char *sig) { QEventLoop loop; q->connect(q, sig, &loop, SLOT(quit())); if (m_timeout >= 0) m_timer.start(m_timeout); else m_timer.stop(); loop.exec(); } int error; QString errorString; Job::Capabilities caps; bool autoDelete; quint64 totalAmount; quint64 processedAmount; int m_timeout; QTimer m_timer; }; // -- Job Job::Job(QObject *parent) : QObject(parent), d(new Private(this)) { connect(this, &Job::finished, this, &Job::onFinished); } Job::~Job() { delete d; } bool Job::autoDelete() const { return d->autoDelete; } void Job::setAutoDelete(bool autoDelete) { d->autoDelete = autoDelete; } int Job::error() const { return d->error; } QString Job::errorString() const { return d->errorString; } void Job::emitFinished() { emit finished(this); } void Job::emitFinishedWithError(int error, const QString &errorString) { d->error = error; d->errorString = errorString; emitFinished(); } void Job::setError(int error) { d->error = error; } void Job::setErrorString(const QString &errorString) { d->errorString = errorString; } void Job::waitForStarted() { d->waitForSignal(SIGNAL(started(Job*))); } void Job::waitForFinished() { d->waitForSignal(SIGNAL(finished(Job*))); } Job::Capabilities Job::capabilities() const { return d->caps; } bool Job::hasCapability(Capability c) const { return d->caps.testFlag(c); } void Job::setCapabilities(Capabilities c) { d->caps = c; } void Job::start() { QMetaObject::invokeMethod(this, "delayedStart", Qt::QueuedConnection); } void Job::cancel() { if (d->caps & Cancelable) { doCancel(); if (error() == NoError) { setError(Canceled); setErrorString(tr("Canceled")); } emitFinished(); } else { qDebug() << "The current job cannot be canceled, missing \"Cancelable\" capability."; } } quint64 Job::totalAmount() const { return d->totalAmount; } quint64 Job::processedAmount() const { return d->processedAmount; } void Job::setTotalAmount(quint64 amount) { d->totalAmount = amount; emit totalProgress(d->totalAmount); } /*! Returns the timeout in milliseconds before the job's cancel slot gets triggered. A return value of -1 means there is currently no timeout used for the job. */ int Job::timeout() const { return d->m_timeout; } /*! Sets the timeout in \a milliseconds before the job's cancel slot gets triggered. \note Only jobs that have the \c Job::Cancelable capability can be canceled by a timeout. A value of -1 will stop the timeout mechanism. */ void Job::setTimeout(int milliseconds) { d->m_timeout = milliseconds; } void Job::setProcessedAmount(quint64 amount) { if (d->processedAmount == amount) return; d->processedAmount = amount; emit progress(this, d->processedAmount, d->totalAmount); } void Job::onFinished() { d->m_timer.stop(); if (d->autoDelete) deleteLater(); } #include "moc_job.cpp" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/job.h������������������������������������������������������������������������������0000664�0000000�0000000�00000006236�13253666515�0015223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef JOB_H #define JOB_H #include "kdtoolsglobal.h" #include <QtCore/QObject> class KDTOOLS_EXPORT Job : public QObject { Q_OBJECT class Private; Q_PROPERTY(int timeout READ timeout WRITE setTimeout) Q_PROPERTY(bool autoDelete READ autoDelete WRITE setAutoDelete) public: explicit Job(QObject *parent = 0); ~Job(); enum Error { NoError = 0, Canceled = 1, UserDefinedError = 128 }; enum Capability { NoCapabilities = 0x0, Cancelable = 0x1 }; Q_DECLARE_FLAGS(Capabilities, Capability) int error() const; QString errorString() const; bool autoDelete() const; void setAutoDelete(bool autoDelete); Capabilities capabilities() const; bool hasCapability(Capability c) const; void waitForStarted(); void waitForFinished(); quint64 totalAmount() const; quint64 processedAmount() const; int timeout() const; void setTimeout(int milliseconds); public Q_SLOTS: void start(); void cancel(); Q_SIGNALS: void started(Job *job); void finished(Job *job); void infoMessage(Job *job, const QString &message); void progress(Job *job, quint64 processed, quint64 total); void totalProgress(quint64 total); protected: virtual void doStart() = 0; virtual void doCancel() = 0; void setCapabilities(Capabilities c); void setTotalAmount(quint64 amount); void setProcessedAmount(quint64 amount); void setError(int error); void setErrorString(const QString &errorString); void emitFinished(); void emitFinishedWithError(int error, const QString &errorString); private Q_SLOTS: void onFinished(); private: Private *d; Q_PRIVATE_SLOT(d, void delayedStart()) }; Q_DECLARE_OPERATORS_FOR_FLAGS(Job::Capabilities) #endif // JOB_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/kdsysinfo_win.cpp������������������������������������������������������������������0000664�0000000�0000000�00000011070�13253666515�0017662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "sysinfo.h" #include <QLibrary> #include <QStringList> #include <qt_windows.h> #include <psapi.h> #include <tlhelp32.h> const int KDSYSINFO_PROCESS_QUERY_LIMITED_INFORMATION = 0x1000; namespace KDUpdater { quint64 installedMemory() { MEMORYSTATUSEX status; status.dwLength = sizeof(status); GlobalMemoryStatusEx(&status); return quint64(status.ullTotalPhys); } struct EnumWindowsProcParam { QList<ProcessInfo> processes; QList<quint32> seenIDs; }; typedef BOOL (WINAPI *QueryFullProcessImageNamePtr)(HANDLE, DWORD, char *, PDWORD); typedef DWORD (WINAPI *GetProcessImageFileNamePtr)(HANDLE, char *, DWORD); QList<ProcessInfo> runningProcesses() { EnumWindowsProcParam param; HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (!snapshot) return param.processes; QStringList deviceList; const DWORD bufferSize = 1024; char buffer[bufferSize + 1] = { 0 }; if (QSysInfo::windowsVersion() <= QSysInfo::WV_5_2) { const DWORD size = GetLogicalDriveStringsA(bufferSize, buffer); deviceList = QString::fromLatin1(buffer, size).split(QLatin1Char(char(0)), QString::SkipEmptyParts); } QLibrary kernel32(QLatin1String("Kernel32.dll")); kernel32.load(); QueryFullProcessImageNamePtr pQueryFullProcessImageNamePtr = (QueryFullProcessImageNamePtr) kernel32 .resolve("QueryFullProcessImageNameA"); QLibrary psapi(QLatin1String("Psapi.dll")); psapi.load(); GetProcessImageFileNamePtr pGetProcessImageFileNamePtr = (GetProcessImageFileNamePtr) psapi .resolve("GetProcessImageFileNameA"); PROCESSENTRY32 processStruct; processStruct.dwSize = sizeof(PROCESSENTRY32); bool foundProcess = Process32First(snapshot, &processStruct); while (foundProcess) { HANDLE procHandle = OpenProcess(QSysInfo::windowsVersion() > QSysInfo::WV_5_2 ? KDSYSINFO_PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION, false, processStruct .th32ProcessID); bool succ = false; QString executablePath; DWORD bufferSize = 1024; if (QSysInfo::windowsVersion() > QSysInfo::WV_5_2) { succ = pQueryFullProcessImageNamePtr(procHandle, 0, buffer, &bufferSize); executablePath = QString::fromLatin1(buffer); } else if (pGetProcessImageFileNamePtr) { succ = pGetProcessImageFileNamePtr(procHandle, buffer, bufferSize); executablePath = QString::fromLatin1(buffer); for (int i = 0; i < deviceList.count(); ++i) { executablePath.replace(QString::fromLatin1( "\\Device\\HarddiskVolume%1\\" ).arg(i + 1), deviceList.at(i)); } } if (succ) { const quint32 pid = processStruct.th32ProcessID; param.seenIDs.append(pid); ProcessInfo info; info.id = pid; info.name = executablePath; param.processes.append(info); } CloseHandle(procHandle); foundProcess = Process32Next(snapshot, &processStruct); } if (snapshot) CloseHandle(snapshot); kernel32.unload(); return param.processes; } } // namespace KDUpdater ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/kdtools.pri������������������������������������������������������������������������0000664�0000000�0000000�00000002632�13253666515�0016467�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������INCLUDEPATH += $$PWD DEFINES += BUILD_SHARED_KDTOOLS FORMS += $$PWD/authenticationdialog.ui HEADERS += $$PWD/kdtoolsglobal.h \ $$PWD/job.h \ $$PWD/genericfactory.h \ $$PWD/selfrestarter.h \ $$PWD/runoncechecker.h \ $$PWD/lockfile.h \ $$PWD/sysinfo.h SOURCES += $$PWD/job.cpp \ $$PWD/selfrestarter.cpp \ $$PWD/runoncechecker.cpp \ $$PWD/lockfile.cpp \ $$PWD/sysinfo.cpp HEADERS += $$PWD/updater.h \ $$PWD/filedownloader.h \ $$PWD/filedownloader_p.h \ $$PWD/filedownloaderfactory.h \ $$PWD/localpackagehub.h \ $$PWD/update.h \ $$PWD/updateoperation.h \ $$PWD/updateoperationfactory.h \ $$PWD/updateoperations.h \ $$PWD/task.h \ $$PWD/updatefinder.h \ $$PWD/updatesinfo_p.h \ $$PWD/environment.h \ $$PWD/updatesinfodata_p.h SOURCES += $$PWD/filedownloader.cpp \ $$PWD/filedownloaderfactory.cpp \ $$PWD/localpackagehub.cpp \ $$PWD/update.cpp \ $$PWD/updateoperation.cpp \ $$PWD/updateoperationfactory.cpp \ $$PWD/updateoperations.cpp \ $$PWD/task.cpp \ $$PWD/updatefinder.cpp \ $$PWD/updatesinfo.cpp \ $$PWD/environment.cpp win32 { SOURCES += $$PWD/lockfile_win.cpp \ $$PWD/kdsysinfo_win.cpp } unix { SOURCES += $$PWD/lockfile_unix.cpp osx: SOURCES += $$PWD/sysinfo_mac.cpp else: SOURCES += $$PWD/sysinfo_x11.cpp } ������������������������������������������������������������������������������������������������������src/libs/kdtools/kdtoolsglobal.h��������������������������������������������������������������������0000664�0000000�0000000�00000003165�13253666515�0017307�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef KDTOOLSGLOBAL_H #define KDTOOLSGLOBAL_H #include <QtCore/QtGlobal> #ifndef QT_STATIC # ifdef BUILD_SHARED_KDTOOLS # define KDTOOLS_EXPORT Q_DECL_EXPORT # else # define KDTOOLS_EXPORT Q_DECL_IMPORT # endif #else // KDTOOLS_SHARED # define KDTOOLS_EXPORT #endif // KDTOOLS_SHARED #endif // KDTOOLSGLOBAL_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/localpackagehub.cpp����������������������������������������������������������������0000664�0000000�0000000�00000044265�13253666515�0020115�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "localpackagehub.h" #include "globals.h" #include "constants.h" #include <QDomDocument> #include <QDomElement> #include <QFileInfo> using namespace KDUpdater; using namespace QInstaller; /*! \inmodule kdupdater \class KDUpdater::LocalPackageHub \brief The LocalPackageHub class provides access to information about packages installed on the application side. This class parses the \e {installation information} XML file specified via the setFileName() method and provides access to the information defined within the file through an API. You can: \list \li Get the application name via the applicationName() method. \li Get the application version via the applicationVersion() method. \li Get information about the number of packages installed and their meta-data via the packageInfoCount() and packageInfo() methods. \endlist */ /*! \enum LocalPackageHub::Error Error codes related to retrieving information about installed packages: \value NoError No error occurred. \value NotYetReadError The installation information was not parsed yet from the XML file. \value CouldNotReadPackageFileError The specified installation information file could not be read (does not exist or is not readable). \value InvalidXmlError The installation information file contains invalid XML. \value InvalidContentError The installation information file contains valid XML, but does not match the expected format for package descriptions. */ struct LocalPackageHub::PackagesInfoData { PackagesInfoData() : error(LocalPackageHub::NotYetReadError), modified(false) {} QString errorMessage; LocalPackageHub::Error error; QString fileName; QString applicationName; QString applicationVersion; bool modified; QMap<QString, LocalPackage> m_packageInfoMap; void addPackageFrom(const QDomElement &packageE); void setInvalidContentError(const QString &detail); }; void LocalPackageHub::PackagesInfoData::setInvalidContentError(const QString &detail) { error = LocalPackageHub::InvalidContentError; errorMessage = tr("%1 contains invalid content: %2").arg(fileName, detail); } /*! Constructs a local package hub. To fully setup the class you have to call setFileName(). \sa setFileName */ LocalPackageHub::LocalPackageHub() : d(new PackagesInfoData()) { } /*! Destructor */ LocalPackageHub::~LocalPackageHub() { writeToDisk(); delete d; } /*! Returns \c true if LocalPackageHub is valid; otherwise returns \c false. You can use the errorString() method to receive a descriptive error message. */ bool LocalPackageHub::isValid() const { return d->error <= NotYetReadError; } /*! Returns a list of all local installed packages. */ QStringList LocalPackageHub::packageNames() const { return d->m_packageInfoMap.keys(); } /*! Returns a human-readable description of the last error that occurred. */ QString LocalPackageHub::errorString() const { return d->errorMessage; } /*! Returns the error that was found during the processing of the installation information XML file. If no error was found, returns NoError. */ LocalPackageHub::Error LocalPackageHub::error() const { return d->error; } /*! Sets the complete file name of the installation information XML file to \a fileName. The function also issues a call to refresh() to reload installation information from the XML file. */ void LocalPackageHub::setFileName(const QString &fileName) { if (d->fileName == fileName) return; d->fileName = fileName; refresh(); } /*! Returns the name of the installation information XML file that this class refers to. */ QString LocalPackageHub::fileName() const { return d->fileName; } /*! Sets the application name to \a name. By default, this is the name specified in the \c <ApplicationName> element of the installation information XML file. */ void LocalPackageHub::setApplicationName(const QString &name) { d->applicationName = name; } /*! Returns the application name. */ QString LocalPackageHub::applicationName() const { return d->applicationName; } /*! Sets the application version to \a version. By default, this is the version specified in the \c <ApplicationVersion> element of the installation information XML file. */ void LocalPackageHub::setApplicationVersion(const QString &version) { d->applicationVersion = version; } /*! Returns the application version. */ QString LocalPackageHub::applicationVersion() const { return d->applicationVersion; } /*! Returns the number of KDUpdater::LocalPackage objects contained in this class. */ int LocalPackageHub::packageInfoCount() const { return d->m_packageInfoMap.count(); } /*! Returns the package info structure whose name is \a pkgName. If no such package was found, this function returns a \l{default-constructed value}. */ LocalPackage LocalPackageHub::packageInfo(const QString &pkgName) const { return d->m_packageInfoMap.value(pkgName); } /*! Returns all package info structures. */ QList<LocalPackage> LocalPackageHub::packageInfos() const { return d->m_packageInfoMap.values(); } /*! Re-reads the installation information XML file and updates itself. Changes to applicationName() and applicationVersion() are lost after this function returns. The function emits a reset() signal after completion. */ void LocalPackageHub::refresh() { // First clear internal variables d->applicationName.clear(); d->applicationVersion.clear(); d->m_packageInfoMap.clear(); d->modified = false; QFile file(d->fileName); // if the file does not exist then we just skip the reading if (!file.exists()) { d->error = NotYetReadError; d->errorMessage = tr("The file %1 does not exist.").arg(d->fileName); return; } // Open Packages.xml if (!file.open(QFile::ReadOnly)) { d->error = CouldNotReadPackageFileError; d->errorMessage = tr("Cannot open %1.").arg(d->fileName); return; } // Parse the XML document QDomDocument doc; QString parseErrorMessage; int parseErrorLine; int parseErrorColumn; if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) { d->error = InvalidXmlError; d->errorMessage = tr("Parse error in %1 at %2, %3: %4") .arg(d->fileName, QString::number(parseErrorLine), QString::number(parseErrorColumn), parseErrorMessage); return; } file.close(); // Now populate information from the XML file. QDomElement rootE = doc.documentElement(); if (rootE.tagName() != QLatin1String("Packages")) { d->setInvalidContentError(tr("Root element %1 unexpected, should be 'Packages'.") .arg(rootE.tagName())); return; } QDomNodeList childNodes = rootE.childNodes(); for (int i = 0; i < childNodes.count(); i++) { QDomNode childNode = childNodes.item(i); QDomElement childNodeE = childNode.toElement(); if (childNodeE.isNull()) continue; if (childNodeE.tagName() == QLatin1String("ApplicationName")) d->applicationName = childNodeE.text(); else if (childNodeE.tagName() == QLatin1String("ApplicationVersion")) d->applicationVersion = childNodeE.text(); else if (childNodeE.tagName() == QLatin1String("Package")) d->addPackageFrom(childNodeE); } d->error = NoError; d->errorMessage.clear(); } /*! Marks the package specified by \a name as installed. Sets the values of \a version, \a title, \a description, \a dependencies, \a autoDependencies, \a forcedInstallation, \a virtualComp, \a uncompressedSize, \a inheritVersionFrom, and \a checkable for the package. */ void LocalPackageHub::addPackage(const QString &name, const QString &version, const QString &title, const QString &description, const QStringList &dependencies, const QStringList &autoDependencies, bool forcedInstallation, bool virtualComp, quint64 uncompressedSize, const QString &inheritVersionFrom, bool checkable) { // TODO: This somewhat unexpected, remove? if (d->m_packageInfoMap.contains(name)) { // TODO: What about the other fields, update? d->m_packageInfoMap[name].version = version; d->m_packageInfoMap[name].lastUpdateDate = QDate::currentDate(); } else { LocalPackage info; info.name = name; info.version = version; info.inheritVersionFrom = inheritVersionFrom; info.installDate = QDate::currentDate(); info.title = title; info.description = description; info.dependencies = dependencies; info.autoDependencies = autoDependencies; info.forcedInstallation = forcedInstallation; info.virtualComp = virtualComp; info.uncompressedSize = uncompressedSize; info.checkable = checkable; d->m_packageInfoMap.insert(name, info); } d->modified = true; } /*! Removes the package specified by \a name. Returns \c false if the package is not found. */ bool LocalPackageHub::removePackage(const QString &name) { if (d->m_packageInfoMap.remove(name) <= 0) return false; d->modified = true; return true; } static void addTextChildHelper(QDomNode *node, const QString &tag, const QString &text, const QString &attributeName = QString(), const QString &attributeValue = QString()) { QDomElement domElement = node->ownerDocument().createElement(tag); QDomText domText = node->ownerDocument().createTextNode(text); domElement.appendChild(domText); if (!attributeName.isEmpty()) domElement.setAttribute(attributeName, attributeValue); node->appendChild(domElement); } /*! Writes the installation information file to disk. */ void LocalPackageHub::writeToDisk() { if (d->modified && (!d->m_packageInfoMap.isEmpty() || QFile::exists(d->fileName))) { QDomDocument doc; QDomElement root = doc.createElement(QLatin1String("Packages")) ; doc.appendChild(root); addTextChildHelper(&root, QLatin1String("ApplicationName"), d->applicationName); addTextChildHelper(&root, QLatin1String("ApplicationVersion"), d->applicationVersion); Q_FOREACH (const LocalPackage &info, d->m_packageInfoMap) { QDomElement package = doc.createElement(QLatin1String("Package")); addTextChildHelper(&package, QLatin1String("Name"), info.name); addTextChildHelper(&package, QLatin1String("Title"), info.title); addTextChildHelper(&package, QLatin1String("Description"), info.description); if (info.inheritVersionFrom.isEmpty()) addTextChildHelper(&package, QLatin1String("Version"), info.version); else addTextChildHelper(&package, QLatin1String("Version"), info.version, QLatin1String("inheritVersionFrom"), info.inheritVersionFrom); addTextChildHelper(&package, QLatin1String("LastUpdateDate"), info.lastUpdateDate .toString(Qt::ISODate)); addTextChildHelper(&package, QLatin1String("InstallDate"), info.installDate .toString(Qt::ISODate)); addTextChildHelper(&package, QLatin1String("Size"), QString::number(info.uncompressedSize)); if (info.dependencies.count()) addTextChildHelper(&package, scDependencies, info.dependencies.join(QLatin1String(","))); if (info.autoDependencies.count()) addTextChildHelper(&package, scAutoDependOn, info.autoDependencies.join(QLatin1String(","))); if (info.forcedInstallation) addTextChildHelper(&package, QLatin1String("ForcedInstallation"), QLatin1String("true")); if (info.virtualComp) addTextChildHelper(&package, QLatin1String("Virtual"), QLatin1String("true")); if (info.checkable) addTextChildHelper(&package, QLatin1String("Checkable"), QLatin1String("true")); root.appendChild(package); } // Open Packages.xml QFile file(d->fileName); if (!file.open(QFile::WriteOnly)) return; file.write(doc.toByteArray(4)); file.close(); d->modified = false; } } void LocalPackageHub::PackagesInfoData::addPackageFrom(const QDomElement &packageE) { if (packageE.isNull()) return; QDomNodeList childNodes = packageE.childNodes(); if (childNodes.count() == 0) return; LocalPackage info; info.forcedInstallation = false; info.virtualComp = false; info.checkable = false; for (int i = 0; i < childNodes.count(); i++) { QDomNode childNode = childNodes.item(i); QDomElement childNodeE = childNode.toElement(); if (childNodeE.isNull()) continue; if (childNodeE.tagName() == QLatin1String("Name")) info.name = childNodeE.text(); else if (childNodeE.tagName() == QLatin1String("Title")) info.title = childNodeE.text(); else if (childNodeE.tagName() == QLatin1String("Description")) info.description = childNodeE.text(); else if (childNodeE.tagName() == QLatin1String("Version")) { info.version = childNodeE.text(); info.inheritVersionFrom = childNodeE.attribute(QLatin1String("inheritVersionFrom")); } else if (childNodeE.tagName() == QLatin1String("Virtual")) info.virtualComp = childNodeE.text().toLower() == QLatin1String("true") ? true : false; else if (childNodeE.tagName() == QLatin1String("Size")) info.uncompressedSize = childNodeE.text().toULongLong(); else if (childNodeE.tagName() == QLatin1String("Dependencies")) { info.dependencies = childNodeE.text().split(QInstaller::commaRegExp(), QString::SkipEmptyParts); } else if (childNodeE.tagName() == QLatin1String("AutoDependOn")) { info.autoDependencies = childNodeE.text().split(QInstaller::commaRegExp(), QString::SkipEmptyParts); } else if (childNodeE.tagName() == QLatin1String("ForcedInstallation")) info.forcedInstallation = childNodeE.text().toLower() == QLatin1String( "true" ) ? true : false; else if (childNodeE.tagName() == QLatin1String("LastUpdateDate")) info.lastUpdateDate = QDate::fromString(childNodeE.text(), Qt::ISODate); else if (childNodeE.tagName() == QLatin1String("InstallDate")) info.installDate = QDate::fromString(childNodeE.text(), Qt::ISODate); else if (childNodeE.tagName() == QLatin1String("Checkable")) info.checkable = childNodeE.text().toLower() == QLatin1String("true") ? true : false; } m_packageInfoMap.insert(info.name, info); } /*! Clears the installed package list. */ void LocalPackageHub::clearPackageInfos() { d->m_packageInfoMap.clear(); d->modified = true; } /*! \inmodule kdupdater \class KDUpdater::LocalPackage \brief The LocalPackage class describes a single installed package in the application. This class contains information about a single installed package in the application. The information contained in this class corresponds to the information described by the <Package> XML element in the installation information XML file. */ /*! \variable LocalPackage::name \brief The name of the package. */ /*! \variable LocalPackage::title */ /*! \variable LocalPackage::description */ /*! \variable LocalPackage::version */ /*! \variable LocalPackage::lastUpdateDate */ /*! \variable LocalPackage::installDate */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/localpackagehub.h������������������������������������������������������������������0000664�0000000�0000000�00000007030�13253666515�0017547�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef LOCALPACKAGEHUB_H #define LOCALPACKAGEHUB_H #include "updater.h" #include <QCoreApplication> #include <QDate> #include <QStringList> namespace KDUpdater { struct KDTOOLS_EXPORT LocalPackage { QString name; QString title; QString description; QString version; QString inheritVersionFrom; QStringList dependencies; QStringList autoDependencies; QDate lastUpdateDate; QDate installDate; bool forcedInstallation; bool virtualComp; quint64 uncompressedSize; bool checkable; }; class KDTOOLS_EXPORT LocalPackageHub { Q_DISABLE_COPY(LocalPackageHub) Q_DECLARE_TR_FUNCTIONS(LocalPackageHub) public: LocalPackageHub(); ~LocalPackageHub(); enum Error { NoError = 0, NotYetReadError, CouldNotReadPackageFileError, InvalidXmlError, InvalidContentError }; bool isValid() const; QStringList packageNames() const; Error error() const; QString errorString() const; QString fileName() const; void setFileName(const QString &fileName); QString applicationName() const; void setApplicationName(const QString &name); QString applicationVersion() const; void setApplicationVersion(const QString &version); void clearPackageInfos(); int packageInfoCount() const; QList<LocalPackage> packageInfos() const; LocalPackage packageInfo(const QString &pkgName) const; void addPackage(const QString &pkgName, const QString &version, // mandatory const QString &title, const QString &description, const QStringList &dependencies, const QStringList &autoDependencies, bool forcedInstallation, bool virtualComp, quint64 uncompressedSize, const QString &inheritVersionFrom, bool checkable); bool removePackage(const QString &pkgName); void refresh(); void writeToDisk(); private: struct PackagesInfoData; PackagesInfoData *d; }; } // KDUpdater #endif // LOCALPACKAGEHUB_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/lockfile.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000003344�13253666515�0016571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "lockfile.h" #include "lockfile_p.h" namespace KDUpdater { LockFile::LockFile(const QString &name) : d(new Private(name)) { } LockFile::~LockFile() { delete d; } bool LockFile::lock() { return d->lock(); } QString LockFile::errorString() const { return d->errorString; } bool LockFile::unlock() { return d->unlock(); } } // namespace KDUpdater ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/lockfile.h�������������������������������������������������������������������������0000664�0000000�0000000�00000003351�13253666515�0016234�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef LOCKFILE_H #define LOCKFILE_H #include "kdtoolsglobal.h" namespace KDUpdater { class KDTOOLS_EXPORT LockFile { Q_DISABLE_COPY(LockFile) public: explicit LockFile(const QString &name); ~LockFile(); QString errorString() const; bool lock(); bool unlock(); private: class Private; Private *d; }; } // namespace KDUpdater #endif // LOCKFILE_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/lockfile_p.h�����������������������������������������������������������������������0000664�0000000�0000000�00000003614�13253666515�0016555�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef LOCKFILE_P_H #define LOCKFILE_P_H #include "lockfile.h" #include <QString> #ifdef Q_OS_WIN # include <qt_windows.h> #endif namespace KDUpdater { class LockFile::Private { public: explicit Private(const QString& name) : filename(name) , handle(0) , locked(false) {} bool lock(); bool unlock(); QString errorString; private: QString filename; #ifdef Q_OS_WIN HANDLE handle; #else int handle; #endif bool locked; }; } // namespace KDUpdater #endif // LOCKFILE_P_H ��������������������������������������������������������������������������������������������������������������������src/libs/kdtools/lockfile_unix.cpp������������������������������������������������������������������0000664�0000000�0000000�00000006516�13253666515�0017640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "lockfile_p.h" #include <QCoreApplication> #include <QDir> #include <cerrno> #include <sys/file.h> #include <unistd.h> namespace KDUpdater { bool LockFile::Private::lock() { if (locked) return true; errorString.clear(); errno = 0; handle = open(filename.toLatin1().constData(), O_CREAT | O_RDWR | O_NONBLOCK, 0600); if (handle == -1) { errorString = QCoreApplication::translate("LockFile", "Cannot create lock file \"%1\": " "%2").arg(QDir::toNativeSeparators(filename), QString::fromLocal8Bit(strerror(errno))); return false; } const QString pid = QString::number(qApp->applicationPid()); const QByteArray data = pid.toLatin1(); errno = 0; qint64 written = 0; while (written < data.size()) { const qint64 n = write(handle, data.constData() + written, data.size() - written); if (n < 0) { errorString = QCoreApplication::translate("LockFile", "Cannot write PID to lock " "file \"%1\": %2").arg(QDir::toNativeSeparators(filename), QString::fromLocal8Bit(strerror(errno))); return false; } written += n; } errno = 0; locked = flock(handle, LOCK_NB | LOCK_EX) != -1; if (!locked) { errorString = QCoreApplication::translate("LockFile", "Cannot obtain the lock for " "file \"%1\": %2").arg(QDir::toNativeSeparators(filename), QString::fromLocal8Bit(strerror(errno))); } return locked; } bool LockFile::Private::unlock() { errorString.clear(); if (!locked) return true; errno = 0; locked = flock(handle, LOCK_UN | LOCK_NB) == -1; if (locked) { errorString = QCoreApplication::translate("LockFile", "Cannot release the lock for " "file \"%1\": %2").arg(QDir::toNativeSeparators(filename), QString::fromLocal8Bit(strerror(errno))); } else { unlink(filename.toLatin1()); } return !locked; } } // namespace KDUpdater ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/lockfile_win.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000006675�13253666515�0017460�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "lockfile.h" #include "lockfile_p.h" #include "utils.h" #include <QCoreApplication> #include <QDir> #include <QFileInfo> namespace KDUpdater { bool LockFile::Private::lock() { if (locked) return locked; errorString.clear(); handle = CreateFile(filename.toStdWString().data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, QFileInfo(filename).exists() ? OPEN_EXISTING : CREATE_NEW, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (handle == INVALID_HANDLE_VALUE) { errorString = QCoreApplication::translate("LockFile", "Cannot create lock file \"%1\": " "%2").arg(QDir::toNativeSeparators(filename), QInstaller::windowsErrorString(GetLastError())); return false; } DWORD bytesWritten; const QByteArray pid = QString::number(QCoreApplication::applicationPid()).toLatin1(); if (!WriteFile(handle, pid.data(), pid.size(), &bytesWritten, NULL)) { errorString = QCoreApplication::translate("LockFile", "Cannot write PID to lock file " "\"%1\": %2").arg(QDir::toNativeSeparators(filename), QInstaller::windowsErrorString(GetLastError())); return false; } FlushFileBuffers(handle); if (!::LockFile(handle, 0, 0, QFileInfo(filename).size(), 0)) { errorString = QCoreApplication::translate("LockFile", "Cannot obtain the lock for " "file \"%1\": %2").arg(QDir::toNativeSeparators(filename), QInstaller::windowsErrorString(GetLastError())); } else { locked = true; } return locked; } bool LockFile::Private::unlock() { errorString.clear(); if (!locked) return true; if (!UnlockFile(handle, 0, 0, QFileInfo(filename).size(), 0)) { errorString = QCoreApplication::translate("LockFile", "Cannot release the lock for " "file \"%1\": %2").arg(QDir::toNativeSeparators(filename), QInstaller::windowsErrorString(GetLastError())); } else { locked = false; CloseHandle(handle); } return !locked; } } // namespace KDUpdater �������������������������������������������������������������������src/libs/kdtools/runoncechecker.cpp�����������������������������������������������������������������0000664�0000000�0000000�00000006140�13253666515�0017774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "runoncechecker.h" #include "lockfile.h" #include "sysinfo.h" #include <QCoreApplication> #include <QDebug> #include <QDir> #include <QFileInfo> #include <QList> #include <algorithm> using namespace KDUpdater; RunOnceChecker::RunOnceChecker(const QString &filename) : m_lockfile(filename) { } RunOnceChecker::~RunOnceChecker() { if (!m_lockfile.unlock()) qWarning().noquote() << m_lockfile.errorString(); } class ProcessnameEquals { public: ProcessnameEquals(const QString &name) #ifdef Q_OS_WIN : m_name(name.toLower()) #else : m_name(name) #endif {} bool operator()(const ProcessInfo &info) { #ifdef Q_OS_WIN const QString infoName = info.name.toLower(); if (infoName == QDir::toNativeSeparators(m_name)) return true; #else const QString infoName = info.name; #endif if (infoName == m_name) return true; const QFileInfo fi(infoName); if (fi.fileName() == m_name || fi.baseName() == m_name) return true; return false; } private: QString m_name; }; bool RunOnceChecker::isRunning(RunOnceChecker::ConditionFlags flags) { if (flags.testFlag(ConditionFlag::ProcessList)) { const QList<ProcessInfo> allProcesses = runningProcesses(); const int count = std::count_if(allProcesses.constBegin(), allProcesses.constEnd(), ProcessnameEquals(QCoreApplication::applicationFilePath())); return (count > 1); } if (flags.testFlag(ConditionFlag::Lockfile)) { const bool locked = m_lockfile.lock(); if (!locked) qWarning().noquote() << m_lockfile.errorString(); return !locked; } return false; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/runoncechecker.h�������������������������������������������������������������������0000664�0000000�0000000�00000003605�13253666515�0017444�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef RUNONCECHECKER_H #define RUNONCECHECKER_H #include "lockfile.h" #include <QString> class KDTOOLS_EXPORT RunOnceChecker { Q_DISABLE_COPY(RunOnceChecker) public: enum struct ConditionFlag { Lockfile = 0x01, ProcessList = 0x02 }; Q_DECLARE_FLAGS(ConditionFlags, ConditionFlag) explicit RunOnceChecker(const QString &filename = QString()); ~RunOnceChecker(); bool isRunning(RunOnceChecker::ConditionFlags flags); private: KDUpdater::LockFile m_lockfile; }; #endif // RUNONCECHECKER_H ���������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/selfrestarter.cpp������������������������������������������������������������������0000664�0000000�0000000�00000005734�13253666515�0017673�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "selfrestarter.h" #include <QtCore/QCoreApplication> #include <QtCore/QDir> #include <QtCore/QProcess> class SelfRestarter::Private { public: Private(int argc, char *argv[]) : restartOnQuit(false) { executable = QString::fromLocal8Bit(argv[0]); workingPath = QDir::currentPath(); for (int i = 1; i < argc; ++i) args << QString::fromLocal8Bit(argv[i]); } Private() { executable = qApp->applicationFilePath(); workingPath = QDir::currentPath(); args = qApp->arguments().mid(1); } ~Private() { if (restartOnQuit) QProcess::startDetached(executable, args, workingPath); } QString executable; QStringList args; bool restartOnQuit; QString workingPath; static SelfRestarter *instance; }; SelfRestarter *SelfRestarter::Private::instance = 0; SelfRestarter::SelfRestarter(int argc, char *argv[]) : d(new Private(argc, argv)) { Q_ASSERT_X(!Private::instance, Q_FUNC_INFO, "Cannot create more than one SelfRestarter instance"); Private::instance = this; } SelfRestarter::~SelfRestarter() { Q_ASSERT_X(Private::instance == this, Q_FUNC_INFO, "Cannot create more than one SelfRestarter instance"); delete d; Private::instance = 0; } void SelfRestarter::setRestartOnQuit(bool restart) { Q_ASSERT_X(Private::instance, Q_FUNC_INFO, "SelfRestarter instance must be created in main()"); if (Private::instance) Private::instance->d->restartOnQuit = restart; } bool SelfRestarter::restartOnQuit() { return Private::instance ? Private::instance->d->restartOnQuit : false; } ������������������������������������src/libs/kdtools/selfrestarter.h��������������������������������������������������������������������0000664�0000000�0000000�00000003255�13253666515�0017334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef SELFRESTARTER_H #define SELFRESTARTER_H #include "kdtoolsglobal.h" class KDTOOLS_EXPORT SelfRestarter { public: SelfRestarter(int argc, char *argv[]); ~SelfRestarter(); static bool restartOnQuit(); static void setRestartOnQuit(bool restart); private: Q_DISABLE_COPY(SelfRestarter) class Private; Private *d; }; #endif // SELFRESTARTER_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/sysinfo.cpp������������������������������������������������������������������������0000664�0000000�0000000�00000011035�13253666515�0016467�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "sysinfo.h" #include <QtCore/QDebug> #include <QtCore/QDir> using namespace KDUpdater; struct PathLongerThan { bool operator()(const VolumeInfo &lhs, const VolumeInfo &rhs) const { return lhs.mountPath().length() > rhs.mountPath().length(); } }; VolumeInfo::VolumeInfo() : m_size(0) , m_availableSize(0) { } VolumeInfo VolumeInfo::fromPath(const QString &path) { QDir targetPath(QDir::cleanPath(path)); QList<VolumeInfo> volumes = mountedVolumes(); // sort by length to get the longest mount point (not just "/") first std::sort(volumes.begin(), volumes.end(), PathLongerThan()); foreach (const VolumeInfo &volume, volumes) { const QDir volumePath(volume.mountPath()); if (targetPath == volumePath) return volume; #ifdef Q_OS_WIN if (QDir::toNativeSeparators(path).toLower().startsWith(volume.mountPath().toLower())) #else // we need to take some care here, as canonical path might return an empty string if the target // does not exist yet if (targetPath.exists()) { // the target exist, we can solve the path and if it fits return if (targetPath.canonicalPath().startsWith(volume.mountPath())) return volume; continue; } // the target directory does not exist yet, we need to cd up till we find the first existing dir QStringList parts = targetPath.absolutePath().split(QDir::separator(),QString::SkipEmptyParts); while (targetPath.absolutePath() != QDir::rootPath()) { if (targetPath.exists()) break; parts.pop_back(); if (parts.isEmpty()) targetPath = QDir(QDir::rootPath()); else targetPath = QDir(QLatin1Char('/') + parts.join(QDir::separator())); } if (targetPath.canonicalPath().startsWith(volume.mountPath())) #endif return volume; } return VolumeInfo(); } QString VolumeInfo::mountPath() const { return m_mountPath; } void VolumeInfo::setMountPath(const QString &path) { m_mountPath = path; } QString VolumeInfo::fileSystemType() const { return m_fileSystemType; } void VolumeInfo::setFileSystemType(const QString &type) { m_fileSystemType = type; } QString VolumeInfo::volumeDescriptor() const { return m_volumeDescriptor; } void VolumeInfo::setVolumeDescriptor(const QString &descriptor) { m_volumeDescriptor = descriptor; } quint64 VolumeInfo::size() const { return m_size; } void VolumeInfo::setSize(const quint64 &size) { m_size = size; } quint64 VolumeInfo::availableSize() const { return m_availableSize; } void VolumeInfo::setAvailableSize(const quint64 &available) { m_availableSize = available; } bool VolumeInfo::operator==(const VolumeInfo &other) const { return m_volumeDescriptor == other.m_volumeDescriptor; } QDebug operator<<(QDebug dbg, VolumeInfo volume) { return dbg << "KDUpdater::Volume(" << volume.mountPath() << ")"; } QDebug operator<<(QDebug dbg, ProcessInfo process) { return dbg << "KDUpdater::ProcessInfo(" << process.id << ", " << process.name << ")"; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/sysinfo.h��������������������������������������������������������������������������0000664�0000000�0000000�00000005233�13253666515�0016137�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef SYSINFO_H #define SYSINFO_H #include "kdtoolsglobal.h" #include <QtCore/QString> namespace KDUpdater { class KDTOOLS_EXPORT VolumeInfo { public: VolumeInfo(); static VolumeInfo fromPath(const QString &path); QString mountPath() const; void setMountPath(const QString &path); QString fileSystemType() const; void setFileSystemType(const QString &type); QString volumeDescriptor() const; void setVolumeDescriptor(const QString &descriptor); quint64 size() const; void setSize(const quint64 &size); quint64 availableSize() const; void setAvailableSize(const quint64 &available); bool operator==(const VolumeInfo &other) const; private: QString m_mountPath; QString m_fileSystemType; QString m_volumeDescriptor; quint64 m_size; quint64 m_availableSize; }; struct ProcessInfo { quint32 id; QString name; }; quint64 installedMemory(); QList<VolumeInfo> mountedVolumes(); QList<ProcessInfo> runningProcesses(); bool killProcess(const ProcessInfo &process, int msecs = 30000); bool pathIsOnLocalDevice(const QString &path); } // namespace KDUpdater QT_BEGIN_NAMESPACE class QDebug; QT_END_NAMESPACE QDebug operator<<(QDebug dbg, KDUpdater::VolumeInfo volume); QDebug operator<<(QDebug dbg, KDUpdater::ProcessInfo process); #endif // SYSINFO_H ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/sysinfo_mac.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000011502�13253666515�0017306�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "sysinfo.h" #include <Carbon/Carbon.h> #include <sys/mount.h> #include <sys/sysctl.h> #include <sys/types.h> #include <QtCore/QList> namespace KDUpdater { quint64 installedMemory() { SInt32 mb = 0; Gestalt(gestaltPhysicalRAMSizeInMegabytes, &mb); return quint64(static_cast<quint64>(mb) * 1024LL * 1024LL); } QList<VolumeInfo> mountedVolumes() { QList<VolumeInfo> result; FSVolumeRefNum volume; FSVolumeInfo info; HFSUniStr255 volName; FSRef ref; int i = 0; while (FSGetVolumeInfo(kFSInvalidVolumeRefNum, ++i, &volume, kFSVolInfoFSInfo, &info, &volName, &ref) == 0) { UInt8 path[PATH_MAX + 1]; if (FSRefMakePath(&ref, path, PATH_MAX) == 0) { FSGetVolumeInfo(volume, 0, 0, kFSVolInfoSizes, &info, 0, 0); VolumeInfo v; v.setSize(quint64(info.totalBytes)); v.setAvailableSize(quint64(info.freeBytes)); v.setMountPath(QString::fromLocal8Bit(reinterpret_cast< char* >(path))); struct statfs data; if (statfs(qPrintable(v.mountPath() + QLatin1String("/.")), &data) == 0) { v.setFileSystemType(QLatin1String(data.f_fstypename)); v.setVolumeDescriptor(QLatin1String(data.f_mntfromname)); } result.append(v); } } return result; } QList<ProcessInfo> runningProcesses() { int mib[4] = { CTL_KERN, KERN_ARGMAX, 0, 0 }; int argMax = 0; size_t argMaxSize = sizeof(argMax); // fetch the maximum process arguments size sysctl(mib, 2, &argMax, &argMaxSize, NULL, 0); char *processArguments = (char*) malloc(argMax); mib[1] = KERN_PROC; mib[2] = KERN_PROC_ALL; size_t processTableSize = 0; // fetch the kernel process table size sysctl(mib, 4, NULL, &processTableSize, NULL, 0); struct kinfo_proc *processTable = (kinfo_proc*) malloc(processTableSize); // fetch the process table sysctl(mib, 4, processTable, &processTableSize, NULL, 0); QList<ProcessInfo> processes; for (size_t i = 0; i < (processTableSize / sizeof(struct kinfo_proc)); ++i) { struct kinfo_proc *process = processTable + i; ProcessInfo processInfo; processInfo.id = process->kp_proc.p_pid; mib[1] = KERN_PROCARGS2; mib[2] = process->kp_proc.p_pid; mib[3] = 0; size_t size = argMax; // fetch the process arguments if (sysctl(mib, 3, processArguments, &size, NULL, 0) != -1) { /* * |-----------------| <-- data returned by sysctl() * | argc | * |-----------------| * | executable path | * |-----------------| * | arguments | * ~~~~~~~~~~~~~~~~~~~ * |-----------------| */ processInfo.name = QString::fromLocal8Bit(processArguments + sizeof(int)); } else { // if we fail, use the name from the process table processInfo.name = QString::fromLocal8Bit(process->kp_proc.p_comm); } processes.append(processInfo); } free(processTable); free(processArguments); return processes; } bool pathIsOnLocalDevice(const QString &path) { Q_UNUSED(path); return true; } bool killProcess(const ProcessInfo &process, int msecs) { Q_UNUSED(process); Q_UNUSED(msecs); return true; } } // namespace KDUpdater ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/sysinfo_x11.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000011237�13253666515�0017164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "sysinfo.h" #include <sys/utsname.h> #include <sys/statvfs.h> #ifdef Q_OS_FREEBSD #include <sys/types.h> #include <sys/sysctl.h> #endif #include <QtCore/QFile> #include <QtCore/QTextStream> #include <QtCore/QDir> #include <QtCore/QFileInfo> #include <QtCore/QRegExp> namespace KDUpdater { quint64 installedMemory() { #ifdef Q_OS_LINUX QFile f(QLatin1String("/proc/meminfo")); f.open(QIODevice::ReadOnly); QTextStream stream(&f); while (true) { const QString s = stream.readLine(); if( !s.startsWith(QLatin1String("MemTotal:" ))) continue; else if (s.isEmpty()) return quint64(); const QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts); return quint64(parts.at(1).toInt() * 1024LL); } #else quint64 physmem; size_t len = sizeof physmem; #ifdef Q_OS_FREEBSD static int mib[2] = { CTL_HW, HW_PHYSMEM }; #else static int mib[2] = { CTL_HW, HW_MEMSIZE }; #endif sysctl(mib, 2, &physmem, &len, 0, 0); return quint64(physmem); #endif return 0; } QList<VolumeInfo> mountedVolumes() { QList<VolumeInfo> result; QFile f(QLatin1String("/etc/mtab")); if (!f.open(QIODevice::ReadOnly)) { qCritical("%s: Cannot open %s: %s", Q_FUNC_INFO, qPrintable(f.fileName()), qPrintable(f.errorString())); return result; //better error-handling? } QTextStream stream(&f); while (true) { const QString s = stream.readLine(); if (s.isNull()) return result; if (!s.startsWith(QLatin1Char('/')) && !s.startsWith(QLatin1String("tmpfs ") + QDir::tempPath())) continue; const QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts); VolumeInfo v; v.setMountPath(parts.at(1)); v.setVolumeDescriptor(parts.at(0)); v.setFileSystemType(parts.value(2)); struct statvfs data; if (statvfs(qPrintable(v.mountPath() + QLatin1String("/.")), &data) == 0) { v.setSize(quint64(static_cast<quint64>(data.f_blocks) * data.f_bsize)); v.setAvailableSize(quint64(static_cast<quint64>(data.f_bavail) * data.f_bsize)); } result.append(v); } return result; } QList<ProcessInfo> runningProcesses() { QList<ProcessInfo> processes; QDir procDir(QLatin1String("/proc")); const QFileInfoList procCont = procDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable); QRegExp validator(QLatin1String("[0-9]+")); Q_FOREACH (const QFileInfo &info, procCont) { if (validator.exactMatch(info.fileName())) { const QString linkPath = QDir(info.absoluteFilePath()).absoluteFilePath(QLatin1String("exe")); const QFileInfo linkInfo(linkPath); if (linkInfo.exists()) { ProcessInfo processInfo; processInfo.name = linkInfo.symLinkTarget(); processInfo.id = info.fileName().toInt(); processes.append(processInfo); } } } return processes; } bool pathIsOnLocalDevice(const QString &path) { Q_UNUSED(path); return true; } bool killProcess(const ProcessInfo &process, int msecs) { Q_UNUSED(process); Q_UNUSED(msecs); return true; } } // namespace KDUpdater �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/task.cpp���������������������������������������������������������������������������0000664�0000000�0000000�00000024260�13253666515�0015743�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "task.h" using namespace KDUpdater; /*! \inmodule kdupdater \class KDUpdater::Task \brief The Task class is the base class for all tasks in KDUpdater. This class is the base class for all task classes in KDUpdater. Task is an activity that occupies certain amount of execution time. It can be started, stopped (or canceled), paused and resumed. Tasks can report progress and error messages which an application can show in any sort of UI. The KDUpdater::Task class provides a common interface for dealing with all kinds of tasks in KDUpdater. User should be careful of these points: \list \li Task classes can be started only once. \li Instances of this class cannot be created. Only instances of the subclasses can. \endlist */ /*! \enum Task::Capability This enum value sets the capabilities of the task. \value NoCapability The task has no capabilities, so it cannot be paused or stopped. \value Pausable The task can be paused. \value Stoppable The task can be stopped. */ /*! \internal */ KDUpdater::Task::Task(const QString &name, int caps, QObject *parent) : QObject(parent) , m_caps(caps) , m_name(name) , m_errorCode(0) , m_started(false) , m_finished(false) , m_paused(false) , m_stopped(false) , m_progressPc(0) , m_autoDelete(true) { } /*! \internal */ Task::~Task() {} /*! Returns the name of the task. */ QString Task::name() const { return m_name; } /*! Returns the capabilities of the task. It is a combination of one or more Task::Capability flags. */ int Task::capabilities() const { return m_caps; } /*! Returns the last reported error code. */ int Task::error() const { return m_errorCode; } /*! Returns the last reported error message text. */ QString Task::errorString() const { return m_errorText; } /*! Returns whether the task has started and is running. */ bool Task::isRunning() const { return m_started; } /*! Returns whether the task has finished or not. \note Stopped (or canceled) tasks are not finished tasks. */ bool Task::isFinished() const { return m_finished; } /*! Returns whether the task is paused or not. */ bool Task::isPaused() const { return m_paused; } /*! Returns whether the task is stopped or not. \note Finished tasks are not stopped classes. */ bool Task::isStopped() const { return m_stopped; } /*! Returns the progress in percentage made by this task. */ int Task::progressPercent() const { return m_progressPc; } /*! Returns a string that describes the progress made by this task as a string. */ QString Task::progressText() const { return m_progressText; } /*! Starts the task. */ void Task::run() { if (m_started) { qDebug("Trying to start an already started task"); return; } if (m_stopped) { qDebug("Trying to start a finished or canceled task"); return; } m_stopped = false; m_finished = false; // for the sake of completeness m_started = true; emit started(); reportProgress(0, tr("%1 started").arg(m_name)); doRun(); } /*! Stops the task, provided the task has the Task::Stoppable capability. \note Once the task is stopped, it cannot be restarted. */ void Task::stop() { if (!(m_caps & Stoppable)) { const QString errorMsg = tr("%1 cannot be stopped").arg(m_name); reportError(ECannotStopTask, errorMsg); return; } if (!m_started) { qDebug("Trying to stop an unstarted task"); return; } if(m_finished || m_stopped) { qDebug("Trying to stop a finished or canceled task"); return; } m_stopped = doStop(); if (!m_stopped) { const QString errorMsg = tr("Cannot stop task %1").arg(m_name); reportError(ECannotStopTask, errorMsg); return; } m_started = false; // the task is not running m_finished = false; // the task is not finished, but was canceled half-way through emit stopped(); if (m_autoDelete) deleteLater(); } /*! Pauses the task, provided the task has the Task::Pausable capability. */ void Task::pause() { if (!(m_caps & Pausable)) { const QString errorMsg = tr("%1 cannot be paused").arg(m_name); reportError(ECannotPauseTask, errorMsg); return; } if (!m_started) { qDebug("Trying to pause an unstarted task"); return; } if (m_finished || m_stopped) { qDebug("Trying to pause a finished or canceled task"); return; } m_paused = doPause(); if (!m_paused) { const QString errorMsg = tr("Cannot pause task %1").arg(m_name); reportError(ECannotPauseTask, errorMsg); return; } // The task state has to be started, paused but not finished or stopped. // We need not set the flags below, but just in case. // Perhaps we should do Q_ASSERT() ??? m_started = true; m_finished = false; m_stopped = false; emit paused(); } /*! Resumes the task if it was paused. */ void Task::resume() { if (!m_paused) { qDebug("Trying to resume an unpaused task"); return; } const bool val = doResume(); if (!val) { const QString errorMsg = tr("Cannot resume task %1").arg(m_name); reportError(ECannotResumeTask, errorMsg); return; } // The task state should be started, but not paused, finished or stopped. // We need not set the flags below, but just in case. // Perhaps we should do Q_ASSERT() ??? m_started = true; m_paused = false; m_finished = false; m_stopped = false; emit resumed(); } /*! \internal */ void Task::reportProgress(int percent, const QString &text) { if (m_progressPc == percent) return; m_progressPc = percent; m_progressText = text; emit progressValue(m_progressPc); emit progressText(m_progressText); } /*! \internal */ void Task::reportError(int errorCode, const QString &errorText) { m_errorCode = errorCode; m_errorText = errorText; emit error(m_errorCode, m_errorText); if (m_autoDelete) deleteLater(); } /*! \internal */ void Task::reportError(const QString &errorText) { reportError(EUnknown, errorText); } /*! \internal */ void Task::reportDone() { QString msg = tr("%1 done"); reportProgress(100, msg); // State should be finished, but not started, paused or stopped. m_finished = true; m_started = false; m_paused = false; m_stopped = false; m_errorCode = 0; m_errorText.clear(); emit finished(); if (m_autoDelete) deleteLater(); } /*! Returns \c true if the task will be automatically deleted. */ bool Task::autoDelete() const { return m_autoDelete; } /*! Automatically deletes the task if \a autoDelete is \c true. */ void Task::setAutoDelete(bool autoDelete) { m_autoDelete = autoDelete; } /*! \fn virtual void KDUpdater::Task::doRun() = 0; Returns \c 0 if the task is run. */ /*! \fn virtual bool KDUpdater::Task::doStop() = 0; Returns \c true if the task is stopped. */ /*! \fn virtual bool KDUpdater::Task::doPause() = 0; Returns \c true if the task is paused. */ /*! \fn virtual bool KDUpdater::Task::doResume() = 0; Returns \c true if the task is resumed. */ /*! \fn void Task::error(int code, const QString &errorText) This signal is emitted to notify an error during the execution of this task. The \a code parameter indicates the error that was found during the execution of the task, while the \a errorText is the human-readable description of the last error that occurred. */ /*! \fn void Task::progressValue(int percent) This signal is emitted to report progress made by the task. The \a percent parameter gives the progress made as a percentage. */ /*! \fn void Task::progressText(const QString &progressText) This signal is emitted to report the progress made by the task. The \a progressText parameter represents the progress made in a human-readable form. */ /*! \fn void Task::started() This signal is emitted when the task has started. */ /*! \fn void Task::paused() This signal is emitted when the task has paused. */ /*! \fn void Task::resumed() This signal is emitted when the task has resumed. */ /*! \fn void Task::stopped() This signal is emitted when the task has stopped (or canceled). */ /*! \fn void Task::finished() This signal is emitted when the task has finished. */ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/task.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000006102�13253666515�0015403�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef TASK_H #define TASK_H #include "updater.h" #include <QObject> namespace KDUpdater { class KDTOOLS_EXPORT Task : public QObject { Q_OBJECT public: enum Capability { NoCapability = 0, Pausable = 1, Stoppable = 2 }; virtual ~Task(); QString name() const; int capabilities() const; int error() const; QString errorString() const; bool isRunning() const; bool isFinished() const; bool isPaused() const; bool isStopped() const; int progressPercent() const; QString progressText() const; bool autoDelete() const; void setAutoDelete(bool autoDelete); public Q_SLOTS: void run(); void stop(); void pause(); void resume(); Q_SIGNALS: void error(int code, const QString &errorText); void progressValue(int percent); void progressText(const QString &progressText); void started(); void paused(); void resumed(); void stopped(); void finished(); protected: explicit Task(const QString &name, int caps = NoCapability, QObject *parent = 0); void reportProgress(int percent, const QString &progressText); void reportError(int errorCode, const QString &errorText); void reportError(const QString &errorText); void reportDone(); // Task interface virtual void doRun() = 0; virtual bool doStop() = 0; virtual bool doPause() = 0; virtual bool doResume() = 0; private: int m_caps; QString m_name; int m_errorCode; QString m_errorText; bool m_started; bool m_finished; bool m_paused; bool m_stopped; int m_progressPc; QString m_progressText; bool m_autoDelete; }; } // namespace KDUpdater #endif // TASK_H ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/update.cpp�������������������������������������������������������������������������0000664�0000000�0000000�00000004331�13253666515�0016260�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "update.h" using namespace KDUpdater; /*! \inmodule kdupdater \class KDUpdater::Update \brief Represents a single update The KDUpdater::Update class contains information about an update. It is created by KDUpdater::UpdateFinder corresponding to the update. The constructor of the KDUpdater::Update class is made protected, because it can be instantiated only by KDUpdater::UpdateFinder (which is a friend class). The destructor however is public. */ /*! \internal */ Update::Update(const QInstaller::PackageSource &packageSource, const UpdateInfo &updateInfo) : m_packageSource(packageSource) , m_updateInfo(updateInfo) { } /*! Returns the data specified by \a name, or an invalid \a defaultValue if the data does not exist. */ QVariant Update::data(const QString &name, const QVariant &defaultValue) const { return m_updateInfo.data.value(name, defaultValue); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/update.h���������������������������������������������������������������������������0000664�0000000�0000000�00000003611�13253666515�0015725�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATE_H #define UPDATE_H #include "packagesource.h" #include "updatesinfo_p.h" #include <QVariant> namespace KDUpdater { class Update { public: QVariant data(const QString &name, const QVariant &defaultValue = QVariant()) const; QInstaller::PackageSource packageSource() const {return m_packageSource; } private: friend class UpdateFinder; Update(const QInstaller::PackageSource &packageSource, const UpdateInfo &updateInfo); private: QInstaller::PackageSource m_packageSource; UpdateInfo m_updateInfo; }; } // namespace KDUpdater #endif // UPDATE_H �����������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updatefinder.cpp�������������������������������������������������������������������0000664�0000000�0000000�00000051422�13253666515�0017453�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "updatefinder.h" #include "update.h" #include "filedownloader.h" #include "filedownloaderfactory.h" #include "updatesinfo_p.h" #include "localpackagehub.h" #include "fileutils.h" #include "globals.h" #include <QCoreApplication> #include <QFileInfo> #include <QRegExp> using namespace KDUpdater; using namespace QInstaller; /*! \inmodule kdupdater \class KDUpdater::UpdateFinder \brief The UpdaterFinder class finds updates applicable for installed packages. The KDUpdater::UpdateFinder class helps in searching for updates and installing them on the application. The class basically processes the application's KDUpdater::PackagesInfo and the UpdateXMLs it aggregates from all the update sources and populates a list of KDUpdater::Update objects. */ // // Private // class UpdateFinder::Private { public: enum struct Resolution { AddPackage, KeepExisting, RemoveExisting }; Private(UpdateFinder *qq) : q(qq) , downloadCompleteCount(0) , m_downloadsToComplete(0) {} ~Private() { clear(); } struct Data { Data() : downloader(0) {} Data(const PackageSource &i, FileDownloader *d = 0) : info(i), downloader(d) {} PackageSource info; FileDownloader *downloader; }; UpdateFinder *q; QHash<QString, Update *> updates; // Temporary structure that notes down information about updates. bool cancel; int downloadCompleteCount; int m_downloadsToComplete; QHash<UpdatesInfo *, Data> m_updatesInfoList; void clear(); void computeUpdates(); void cancelComputeUpdates(); bool downloadUpdateXMLFiles(); bool computeApplicableUpdates(); QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo); void createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList); Resolution checkPriorityAndVersion(const PackageSource &source, const QVariantHash &data) const; void slotDownloadDone(); QSet<PackageSource> packageSources; std::weak_ptr<LocalPackageHub> m_localPackageHub; }; static int computeProgressPercentage(int min, int max, int percent) { return min + qint64(max-min) * percent / 100; } static int computePercent(int done, int total) { return total ? done * Q_INT64_C(100) / total : 0 ; } /*! \internal Releases all internal resources consumed while downloading and computing updates. */ void UpdateFinder::Private::clear() { qDeleteAll(updates); updates.clear(); const QList<Data> values = m_updatesInfoList.values(); foreach (const Data &data, values) delete data.downloader; qDeleteAll(m_updatesInfoList.keys()); m_updatesInfoList.clear(); downloadCompleteCount = 0; m_downloadsToComplete = 0; } /*! \internal This method computes the updates that can be applied on the application by studying the application's KDUpdater::PackagesInfo object and the UpdateXML files from each of the update sources described in QInstaller::PackageSource. This function can take a long time to complete. The following signals are emitted during the execution of this function The function creates KDUpdater::Update objects on the stack. All KDUpdater::Update objects are made children of the application associated with this finder. The update sources are fetched from the QInstaller::PackageSource object associated with the application. Package information is extracted from the KDUpdater::PackagesInfo object associated with the application. \note Each time this function is called, all the previously computed updates are discarded and its resources are freed. */ void UpdateFinder::Private::computeUpdates() { // Computing updates is done in two stages // 1. Downloading Update XML files from all the update sources // 2. Matching updates with Package XML and figuring out available updates if (!q->isCompressedPackage()) clear(); cancel = false; // First do some quick sanity checks on the packages info std::shared_ptr<LocalPackageHub> packages = m_localPackageHub.lock(); if (!packages) { q->reportError(tr("Cannot access the package information of this application.")); return; } if (!packages->isValid()) { q->reportError(packages->errorString()); return; } // Now do some quick sanity checks on the package sources. if (packageSources.count() <= 0) { q->reportError(tr("No package sources set for this application.")); return; } // Now we can start... // Step 1: 0 - 49 percent if (!downloadUpdateXMLFiles() || cancel) { clear(); return; } // Step 2: 50 - 100 percent if (!computeApplicableUpdates() || cancel) { clear(); return; } // All done q->reportProgress(100, tr("%n update(s) found.", "", updates.count())); q->reportDone(); } /*! \internal Cancels the computation of updates. \sa computeUpdates() */ void UpdateFinder::Private::cancelComputeUpdates() { cancel = true; } /*! \internal This function downloads Updates.xml from all the update sources except local files. A single application can potentially have several update sources, hence we need to be asynchronous in downloading updates from different sources. The function basically does this for each update source: a) Create a KDUpdater::FileDownloader and KDUpdater::UpdatesInfo for each update b) Triggers the download of Updates.xml from each file downloader. c) The downloadCompleted(), downloadCanceled() and downloadAborted() signals are connected in each of the downloaders. Once all the downloads are complete and/or aborted, the next stage would be done. The function gets into an event loop until all the downloads are complete. */ bool UpdateFinder::Private::downloadUpdateXMLFiles() { // create UpdatesInfo for each update source foreach (const PackageSource &info, packageSources) { const QUrl url = QString::fromLatin1("%1/Updates.xml").arg(info.url.toString()); if (url.scheme() != QLatin1String("resource") && url.scheme() != QLatin1String("file")) { // create FileDownloader (except for local files and resources) FileDownloader *downloader = FileDownloaderFactory::instance().create(url.scheme(), q); if (!downloader) break; downloader->setUrl(url); downloader->setAutoRemoveDownloadedFile(true); connect(downloader, SIGNAL(downloadCanceled()), q, SLOT(slotDownloadDone())); connect(downloader, SIGNAL(downloadCompleted()), q, SLOT(slotDownloadDone())); connect(downloader, SIGNAL(downloadAborted(QString)), q, SLOT(slotDownloadDone())); m_updatesInfoList.insert(new UpdatesInfo, Data(info, downloader)); } else { UpdatesInfo *updatesInfo = new UpdatesInfo; updatesInfo->setFileName(QInstaller::pathFromUrl(url)); m_updatesInfoList.insert(updatesInfo, Data(info)); } } // Trigger download of Updates.xml file downloadCompleteCount = 0; m_downloadsToComplete = 0; foreach (const Data &data, m_updatesInfoList) { if (data.downloader) { m_downloadsToComplete++; data.downloader->download(); } } // Wait until all downloaders have completed their downloads. while (true) { QCoreApplication::processEvents(); if (cancel) return false; if (downloadCompleteCount == m_downloadsToComplete) break; q->reportProgress(computePercent(downloadCompleteCount, m_downloadsToComplete), tr("Downloading Updates.xml from update sources.")); } // Setup the update info objects with the files from download. foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) { const Data data = m_updatesInfoList.value(updatesInfo); if (data.downloader) { if (!data.downloader->isDownloaded()) { q->reportError(tr("Cannot download package source %1 from \"%2\".").arg(data .downloader->url().fileName(), data.info.url.toString())); } else { updatesInfo->setFileName(data.downloader->downloadedFileName()); } } } // Remove all invalid update info objects. QMutableHashIterator<UpdatesInfo *, Data> it(m_updatesInfoList); while (it.hasNext()) { UpdatesInfo *info = it.next().key(); if (info->isValid()) continue; q->reportError(info->errorString()); delete info; it.remove(); } if (m_updatesInfoList.isEmpty()) return false; q->reportProgress(49, tr("Updates.xml file(s) downloaded from update sources.")); return true; } /*! \internal This function runs through all the KDUpdater::UpdatesInfo objects created during the downloadUpdateXMLFiles() method and compares it with the data contained in KDUpdater::PackagesInfo. Thereby figures out whether an update is applicable for this application or not. */ bool UpdateFinder::Private::computeApplicableUpdates() { int i = 0; foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) { // Fetch updates applicable to this application. QList<UpdateInfo> updates = applicableUpdates(updatesInfo); if (!updates.count()) continue; if (cancel) return false; const PackageSource updateSource = m_updatesInfoList.value(updatesInfo).info; // Create Update objects for updates that have a valid // UpdateFile createUpdateObjects(updateSource, updates); if (cancel) return false; // Report progress q->reportProgress(computeProgressPercentage(51, 100, computePercent(i, m_updatesInfoList.count())), tr("Computing applicable updates.")); ++i; } q->reportProgress(99, tr("Application updates computed.")); return true; } QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesInfo) { const QList<UpdateInfo> dummy; if (!updatesInfo || updatesInfo->updateInfoCount() == 0) return dummy; std::shared_ptr<LocalPackageHub> packages = m_localPackageHub.lock(); if (!packages) return dummy; // Check to see if the updates info contains updates for any application if (updatesInfo->applicationName() != QLatin1String("{AnyApplication}")) { // updatesInfo->applicationName() describes one application or a series of // application names separated by commas. QString appName = updatesInfo->applicationName(); appName = appName.replace(QLatin1String( ", " ), QLatin1String( "," )); appName = appName.replace(QLatin1String( " ," ), QLatin1String( "," )); // Catch hold of app names contained updatesInfo->applicationName() // If the application appName isn't one of the app names, then the updates are not applicable. const QStringList apps = appName.split(QInstaller::commaRegExp(), QString::SkipEmptyParts); if (apps.indexOf([&packages] { return packages->isValid() ? packages->applicationName() : QCoreApplication::applicationName(); } ()) < 0) { return dummy; } } return updatesInfo->updatesInfo(); } void UpdateFinder::Private::createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList) { foreach (const UpdateInfo &info, updateInfoList) { const Resolution value = checkPriorityAndVersion(source, info.data); if (value == Resolution::KeepExisting) continue; const QString name = info.data.value(QLatin1String("Name")).toString(); if (value == Resolution::RemoveExisting) delete updates.take(name); // Create and register the update if (!q->isCompressedPackage() || value == Resolution::AddPackage) updates.insert(name, new Update(source, info)); } } /* If a package of the same name exists, always use the one with the higher version. If the new package has the same version but a higher priority, use the new new package, otherwise keep the already existing package. */ UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion( const PackageSource &source, const QVariantHash &newPackage) const { const QString name = newPackage.value(QLatin1String("Name")).toString(); if (Update *existingPackage = updates.value(name)) { // Bingo, package was previously found elsewhere. const int match = compareVersion(newPackage.value(QLatin1String("Version")).toString(), existingPackage->data(QLatin1String("Version")).toString()); if (match > 0) { // new package has higher version, use qDebug().nospace() << "Remove Package 'Name: " << name << ", Version: " << existingPackage->data(QLatin1String("Version")).toString() << ", Source: " << QFileInfo(existingPackage->packageSource().url.toLocalFile()).fileName() << "' found a package with higher version 'Name: " << name << ", Version: " << newPackage.value(QLatin1String("Version")).toString() << ", Source: " << QFileInfo(source.url.toLocalFile()).fileName() << "'"; return Resolution::RemoveExisting; } if ((match == 0) && (source.priority > existingPackage->packageSource().priority)) { // new package version equals but priority is higher, use qDebug().nospace() << "Remove Package 'Name: " << name << ", Priority: " << existingPackage->packageSource().priority << ", Source: " << QFileInfo(existingPackage->packageSource().url.toLocalFile()).fileName() << "' found a package with higher priority 'Name: " << name << ", Priority: " << source.priority << ", Source: " << QFileInfo(source.url.toLocalFile()).fileName() << "'"; return Resolution::RemoveExisting; } if (q->isCompressedPackage() && match == 0 && source.priority == existingPackage->packageSource().priority) { //Same package with the same priority and version already exists return Resolution::RemoveExisting; } return Resolution::KeepExisting; // otherwise keep existing } return Resolution::AddPackage; } // // UpdateFinder // /*! Constructs an update finder. */ UpdateFinder::UpdateFinder() : Task(QLatin1String("UpdateFinder"), Stoppable), m_compressedPackage(false), d(new Private(this)) { } /*! Destructor */ UpdateFinder::~UpdateFinder() { delete d; } /*! Returns a list of KDUpdater::Update objects. */ QList<Update *> UpdateFinder::updates() const { return d->updates.values(); } void UpdateFinder::setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub) { d->m_localPackageHub = std::move(hub); } /*! Sets the package sources information to use when searching for applicable packages. */ void UpdateFinder::setPackageSources(const QSet<PackageSource> &sources) { d->packageSources = sources; } /*! \internal Implemented from KDUpdater::Task::doRun(). */ void UpdateFinder::doRun() { d->computeUpdates(); } /*! \internal Implemented from KDUpdater::Task::doStop(). */ bool UpdateFinder::doStop() { d->cancelComputeUpdates(); // Wait until the cancel has actually happened, and then return. // Thinking of using QMutex for this. Frank/Till any suggestions? return true; } /*! \internal Implemented from KDUpdater::Task::doStop(). */ bool UpdateFinder::doPause() { // Not a pausable task return false; } /*! \internal Implemented from KDUpdater::Task::doStop(). */ bool UpdateFinder::doResume() { // Not a pausable task, hence it is not resumable as well return false; } /*! \internal */ void UpdateFinder::Private::slotDownloadDone() { ++downloadCompleteCount; int pc = computePercent(downloadCompleteCount, m_downloadsToComplete); pc = computeProgressPercentage(0, 45, pc); q->reportProgress( pc, tr("Downloading Updates.xml from update sources.") ); } /*! \inmodule kdupdater This function compares two version strings \c v1 and \c v2 and returns -1, 0 or +1 based on the following rule \list \li Returns 0 if v1 == v2 \li Returns -1 if v1 < v2 \li Returns +1 if v1 > v2 \endlist The function is very similar to \c strcmp(), except that it works on version strings. Example: \code KDUpdater::compareVersion("2.0", "2.1"); // Returns -1 KDUpdater::compareVersion("2.1", "2.0"); // Returns +1 KDUpdater::compareVersion("2.0", "2.0"); // Returns 0 KDUpdater::compareVersion("2.1", "2.1"); // Returns 0 KDUpdater::compareVersion("2.0", "2.x"); // Returns 0 KDUpdater::compareVersion("2.x", "2.0"); // Returns 0 KDUpdater::compareVersion("2.0.12.4", "2.1.10.4"); // Returns -1 KDUpdater::compareVersion("2.0.12.x", "2.0.x"); // Returns 0 KDUpdater::compareVersion("2.1.12.x", "2.0.x"); // Returns +1 KDUpdater::compareVersion("2.1.12.x", "2.x"); // Returns 0 KDUpdater::compareVersion("2.x", "2.1.12.x"); // Returns 0 \endcode */ int KDUpdater::compareVersion(const QString &v1, const QString &v2) { // For tests refer VersionCompareFnTest testcase. // Check for equality if (v1 == v2) return 0; // Split version numbers across "." const QStringList v1_comps = v1.split(QRegExp(QLatin1String( "\\.|-"))); const QStringList v2_comps = v2.split(QRegExp(QLatin1String( "\\.|-"))); // Check each component of the version int index = 0; while (true) { if (index == v1_comps.count() && index < v2_comps.count()) return -1; if (index < v1_comps.count() && index == v2_comps.count()) return +1; if (index >= v1_comps.count() || index >= v2_comps.count()) break; bool v1_ok, v2_ok; int v1_comp = v1_comps[index].toInt(&v1_ok); int v2_comp = v2_comps[index].toInt(&v2_ok); if (!v1_ok) { if (v1_comps[index] == QLatin1String("x")) return 0; } if (!v2_ok) { if (v2_comps[index] == QLatin1String("x")) return 0; } if (!v1_ok && !v2_ok) return v1_comps[index].compare(v2_comps[index]); if (v1_comp < v2_comp) return -1; if (v1_comp > v2_comp) return +1; // v1_comp == v2_comp ++index; } if (index < v2_comps.count()) return +1; if (index < v1_comps.count()) return -1; // Controversial return. I hope this never happens. return 0; } #include "moc_updatefinder.cpp" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updatefinder.h���������������������������������������������������������������������0000664�0000000�0000000�00000004323�13253666515�0017116�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATEFINDER_H #define UPDATEFINDER_H #include "task.h" #include "packagesource.h" #include <memory> namespace KDUpdater { class LocalPackageHub; class Update; class KDTOOLS_EXPORT UpdateFinder : public Task { Q_OBJECT class Private; public: UpdateFinder(); ~UpdateFinder(); QList<Update *> updates() const; void setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub); void setPackageSources(const QSet<QInstaller::PackageSource> &sources); void addCompressedPackage(bool add) { m_compressedPackage = add; } bool isCompressedPackage() { return m_compressedPackage; } private: void doRun(); bool doStop(); bool doPause(); bool doResume(); private: bool m_compressedPackage; Private *d; Q_PRIVATE_SLOT(d, void slotDownloadDone()) }; } // namespace KDUpdater #endif // UPDATEFINDER_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updateoperation.cpp����������������������������������������������������������������0000664�0000000�0000000�00000041644�13253666515�0020211�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "updateoperation.h" #include "constants.h" #include "fileutils.h" #include "packagemanagercore.h" #include <QDataStream> #include <QDebug> #include <QDir> #include <QFileInfo> #include <QTemporaryFile> using namespace KDUpdater; /*! \inmodule kdupdater \class KDUpdater::UpdateOperation \brief The UpdateOperation class is an abstract base class for update operations. The KDUpdater::UpdateOperation is an abstract class that specifies an interface for update operations. Concrete implementations of this class must perform a single update operation, such as copy, move, or delete. \note Two separate threads cannot be using a single instance of KDUpdater::UpdateOperation at the same time. */ /*! \enum UpdateOperation::Error This enum code specifies error codes related to operation arguments and operation runtime failures. \value NoError No error occurred. \value InvalidArguments Number of arguments does not match or an invalid argument was set. \value UserDefinedError An error occurred during operation run. Use UpdateOperation::errorString() to get the human-readable description of the error that occurred. */ /* \internal Returns a filename for a temporary file based on \a templateName. */ static QString backupFileName(const QString &templateName) { const QFileInfo templ(templateName); QTemporaryFile file( QDir::temp().absoluteFilePath(templ.fileName())); file.open(); const QString name = file.fileName(); file.close(); file.remove(); return name; } /*! \internal */ UpdateOperation::UpdateOperation(QInstaller::PackageManagerCore *core) : m_error(0) , m_core(core) { // Store the value for compatibility reasons. m_values[QLatin1String("installer")] = QVariant::fromValue(core); } /*! \internal */ UpdateOperation::~UpdateOperation() { if (auto *core = packageManager()) core->addFilesForDelayedDeletion(filesForDelayedDeletion()); } /*! Returns the update operation name. \sa setName() */ QString UpdateOperation::name() const { return m_name; } /*! Returns a command line string that describes the update operation. The returned string will be of the form: \c{<name> <arg1> <arg2> <arg3> ....} */ QString UpdateOperation::operationCommand() const { QString argsStr = m_arguments.join(QLatin1String( " " )); return QString::fromLatin1( "%1 %2" ).arg(m_name, argsStr); } /*! Returns \c true if a value called \a name exists, otherwise returns \c false. */ bool UpdateOperation::hasValue(const QString &name) const { return m_values.contains(name); } /*! Clears the value of \a name and removes it. */ void UpdateOperation::clearValue(const QString &name) { m_values.remove(name); } /*! Returns the value of \a name. If the value does not exist, returns an empty QVariant. */ QVariant UpdateOperation::value(const QString &name) const { return m_values.value(name); } /*! Sets the value of \a name to \a value. */ void UpdateOperation::setValue(const QString &name, const QVariant &value) { m_values[name] = value; } /*! Sets the name of the operation to \a name. Subclasses will have to provide a unique name to describe the operation. */ void UpdateOperation::setName(const QString &name) { m_name = name; } /*! Sets the arguments for the update operation to \a args. */ void UpdateOperation::setArguments(const QStringList &args) { m_arguments = args; } /*! Returns the arguments of the update operation. */ QStringList UpdateOperation::arguments() const { return m_arguments; } bool UpdateOperation::checkArgumentCount(int minArgCount, int maxArgCount, const QString &argDescription) { const int argCount = arguments().count(); if (argCount < minArgCount || argCount > maxArgCount) { setError(InvalidArguments); QString countRange; if (minArgCount == maxArgCount) countRange = tr("exactly %1").arg(minArgCount); else if (maxArgCount == INT_MAX) countRange = tr("at least %1").arg(minArgCount); else if (minArgCount == 0) countRange = tr("not more than %1").arg(maxArgCount); else if (minArgCount == maxArgCount - 1) countRange = tr("%1 or %2").arg(minArgCount).arg(maxArgCount); else countRange = tr("%1 to %2").arg(minArgCount).arg(maxArgCount); if (argDescription.isEmpty()) setErrorString(tr("Invalid arguments in %1: %n arguments given, " "%2 arguments expected.", 0, argCount) .arg(name(), countRange)); else setErrorString(tr("Invalid arguments in %1: %n arguments given, " "%2 arguments expected in the form: %3.", 0, argCount) .arg(name(), countRange, argDescription)); return false; } return true; } bool UpdateOperation::checkArgumentCount(int argCount) { return checkArgumentCount(argCount, argCount); } struct StartsWith { StartsWith(const QString &searchTerm) : m_searchTerm(searchTerm) {} bool operator()(const QString &searchString) { return searchString.startsWith(m_searchTerm); } QString m_searchTerm; }; /*! Searches the arguments for the key specified by \a key. If it can find the key, it returns the value set for it. Otherwise, it returns \a defaultValue. Arguments are specified in the following form: \c{key=value}. */ QString UpdateOperation::argumentKeyValue(const QString &key, const QString &defaultValue) const { const QString keySeparater(key + QLatin1String("=")); const QStringList tArguments(arguments()); QStringList::const_iterator it = std::find_if(tArguments.begin(), tArguments.end(), StartsWith(keySeparater)); if (it == tArguments.end()) return defaultValue; const QString value = it->mid(keySeparater.size()); it = std::find_if(++it, tArguments.end(), StartsWith(keySeparater)); if (it != tArguments.end()) { qWarning().nospace() << "There are multiple keys in the arguments calling " << name() << ". " << "Only the first found " << key << " is used: " << arguments().join(QLatin1String("; ")); } return value; } /*! Returns a human-readable description of the last error that occurred. */ QString UpdateOperation::errorString() const { return m_errorString; } /*! Returns the error that was found during the processing of the operation. If no error was found, returns NoError. Subclasses can set more detailed error codes (optional). \note To check if an operation was successful, use the return value of performOperation(), undoOperation(), or testOperation(). */ int UpdateOperation::error() const { return m_error; } /*! Sets the human-readable description of the last error that occurred to \a str. */ void UpdateOperation::setErrorString(const QString &str) { m_errorString = str; } /*! Sets the error condition to be \a error. The human-readable message is set to \a errorString. \sa UpdateOperation::error() \sa UpdateOperation::errorString() */ void UpdateOperation::setError(int error, const QString &errorString) { m_error = error; if (!errorString.isNull()) m_errorString = errorString; } /*! Clears the previously set arguments. */ void UpdateOperation::clear() { m_arguments.clear(); } /*! Returns the list of files that are scheduled for later deletion. */ QStringList UpdateOperation::filesForDelayedDeletion() const { return m_delayedDeletionFiles; } /*! Returns the package manager core this operation belongs to. */ QInstaller::PackageManagerCore *UpdateOperation::packageManager() const { return m_core; } /*! Registers a list of \a files to be deleted later once the application was restarted and the file or files are not used anymore. */ void UpdateOperation::registerForDelayedDeletion(const QStringList &files) { m_delayedDeletionFiles << files; } /*! Tries to delete \a file. If \a file cannot be deleted, it is registered for delayed deletion. If a backup copy of the file cannot be created, returns \c false and displays the error message specified by \a errorString. */ bool UpdateOperation::deleteFileNowOrLater(const QString &file, QString *errorString) { if (file.isEmpty() || QFile::remove(file)) return true; if (!QFile::exists(file)) return true; const QString backup = backupFileName(file); QFile f(file); if (!f.rename(backup)) { if (errorString) *errorString = tr("Renaming file \"%1\" to \"%2\" failed: %3").arg( QDir::toNativeSeparators(file), QDir::toNativeSeparators(backup), f.errorString()); return false; } registerForDelayedDeletion(QStringList(backup)); return true; } /*! \fn virtual void KDUpdater::UpdateOperation::backup() = 0; Subclasses must implement this function to back up any data before performing the action. */ /*! \fn virtual bool KDUpdater::UpdateOperation::performOperation() = 0; Subclasses must implement this function to perform the update operation. Returns \c true if the operation is successful. */ /*! \fn virtual bool KDUpdater::UpdateOperation::undoOperation() = 0; Subclasses must implement this function to perform the undo of the update operation. Returns \c true if the operation is successful. */ /*! \fn virtual bool KDUpdater::UpdateOperation::testOperation() = 0; Subclasses must implement this function to perform the test operation. Returns \c true if the operation is successful. */ /*! Saves operation arguments and values as an XML document and returns the document. You can override this method to store your own extra-data. Extra-data can be any data that you need to store to perform or undo the operation. The default implementation is taking care of arguments and values set via UpdateOperation::setValue(). */ QDomDocument UpdateOperation::toXml() const { QDomDocument doc; QDomElement root = doc.createElement(QLatin1String("operation")); doc.appendChild(root); QDomElement args = doc.createElement(QLatin1String("arguments")); const QString target = m_core ? m_core->value(QInstaller::scTargetDir) : QString(); Q_FOREACH (const QString &s, arguments()) { QDomElement arg = doc.createElement(QLatin1String("argument")); arg.appendChild(doc.createTextNode(QInstaller::replacePath(s, target, QLatin1String(QInstaller::scRelocatable)))); args.appendChild(arg); } root.appendChild(args); if (m_values.isEmpty()) return doc; // append all values set with setValue QDomElement values = doc.createElement(QLatin1String("values")); for (QVariantMap::const_iterator it = m_values.constBegin(); it != m_values.constEnd(); ++it) { // the installer can't be put into XML, ignore if (it.key() == QLatin1String("installer")) continue; QDomElement value = doc.createElement(QLatin1String("value")); QVariant variant = it.value(); value.setAttribute(QLatin1String("name"), it.key()); value.setAttribute(QLatin1String("type"), QLatin1String(variant.typeName())); if (variant.type() != QVariant::List && variant.type() != QVariant::StringList && variant.canConvert(QVariant::String)) { // it can convert to string? great! value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(), target, QLatin1String(QInstaller::scRelocatable)))); } else { // no? then we have to go the hard way... if (variant.type() == QVariant::StringList) { QStringList list = variant.toStringList(); for (int i = 0; i < list.count(); ++i) { list[i] = QInstaller::replacePath(list.at(i), target, QLatin1String(QInstaller::scRelocatable)); } variant = QVariant::fromValue(list); } QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << variant; value.appendChild(doc.createTextNode(QLatin1String( data.toBase64().data()))); } values.appendChild(value); } root.appendChild(values); return doc; } /*! Restores operation arguments and values from the XML document \a doc. Returns \c true on success, otherwise \c false. \note: Clears all previously set values and arguments. */ bool UpdateOperation::fromXml(const QDomDocument &doc) { QString target = QCoreApplication::applicationDirPath(); // Does not change target on non OSX platforms. if (QInstaller::isInBundle(target, &target)) target = QDir::cleanPath(target + QLatin1String("/..")); QStringList args; const QDomElement root = doc.documentElement(); const QDomElement argsElem = root.firstChildElement(QLatin1String("arguments")); Q_ASSERT(! argsElem.isNull()); for (QDomNode n = argsElem.firstChild(); ! n.isNull(); n = n.nextSibling()) { const QDomElement e = n.toElement(); if (!e.isNull() && e.tagName() == QLatin1String("argument")) { args << QInstaller::replacePath(e.text(), QLatin1String(QInstaller::scRelocatable), target); } } setArguments(args); m_values.clear(); const QDomElement values = root.firstChildElement(QLatin1String("values")); for (QDomNode n = values.firstChild(); !n.isNull(); n = n.nextSibling()) { const QDomElement v = n.toElement(); if (v.isNull() || v.tagName() != QLatin1String("value")) continue; const QString name = v.attribute(QLatin1String("name")); const QString type = v.attribute(QLatin1String("type")); const QString value = v.text(); const QVariant::Type t = QVariant::nameToType(type.toLatin1().data()); QVariant var = qVariantFromValue(value); if (t == QVariant::List || t == QVariant::StringList || !var.convert(t)) { QDataStream stream(QByteArray::fromBase64( value.toLatin1())); stream >> var; if (t == QVariant::StringList) { QStringList list = var.toStringList(); for (int i = 0; i < list.count(); ++i) { list[i] = QInstaller::replacePath(list.at(i), QLatin1String(QInstaller::scRelocatable), target); } var = QVariant::fromValue(list); } } m_values[name] = var; } return true; } /*! \overload Restores operation arguments and values from the XML file at path \a xml. Returns \c true on success, otherwise \c false. */ bool UpdateOperation::fromXml(const QString &xml) { QDomDocument doc; QString errorMsg; int errorLine; int errorColumn; if (!doc.setContent( xml, &errorMsg, &errorLine, &errorColumn)) { qWarning() << "Error parsing xml error=" << errorMsg << "line=" << errorLine << "column=" << errorColumn; return false; } return fromXml(doc); } ��������������������������������������������������������������������������������������������src/libs/kdtools/updateoperation.h������������������������������������������������������������������0000664�0000000�0000000�00000007010�13253666515�0017643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATEOPERATION_H #define UPDATEOPERATION_H #include "updater.h" #include <QCoreApplication> #include <QStringList> #include <QVariant> #include <QtXml/QDomDocument> namespace QInstaller { class PackageManagerCore; } namespace KDUpdater { class KDTOOLS_EXPORT UpdateOperation { Q_DECLARE_TR_FUNCTIONS(UpdateOperation) public: enum Error { NoError = 0, InvalidArguments = 1, UserDefinedError = 128 }; explicit UpdateOperation(QInstaller::PackageManagerCore *core); virtual ~UpdateOperation(); QString name() const; QString operationCommand() const; bool hasValue(const QString &name) const; void clearValue(const QString &name); QVariant value(const QString &name) const; void setValue(const QString &name, const QVariant &value); void setArguments(const QStringList &args); QStringList arguments() const; QString argumentKeyValue(const QString & key, const QString &defaultValue = QString()) const; void clear(); QString errorString() const; int error() const; QStringList filesForDelayedDeletion() const; QInstaller::PackageManagerCore *packageManager() const; virtual void backup() = 0; virtual bool performOperation() = 0; virtual bool undoOperation() = 0; virtual bool testOperation() = 0; virtual QDomDocument toXml() const; virtual bool fromXml(const QString &xml); virtual bool fromXml(const QDomDocument &doc); protected: void setName(const QString &name); void setErrorString(const QString &errorString); void setError(int error, const QString &errorString = QString()); void registerForDelayedDeletion(const QStringList &files); bool deleteFileNowOrLater(const QString &file, QString *errorString = 0); bool checkArgumentCount(int minArgCount, int maxArgCount, const QString &argDescription = QString()); bool checkArgumentCount(int argCount); private: QString m_name; QStringList m_arguments; QString m_errorString; int m_error; QVariantMap m_values; QStringList m_delayedDeletionFiles; QInstaller::PackageManagerCore *m_core; }; } // namespace KDUpdater #endif // UPDATEOPERATION_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updateoperationfactory.cpp���������������������������������������������������������0000664�0000000�0000000�00000006671�13253666515�0021602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "updateoperationfactory.h" #include "packagemanagercore.h" #include "updateoperations.h" using namespace KDUpdater; /*! \inmodule kdupdater \class KDUpdater::UpdateOperationFactory \brief The UpdateOperationFactory class is used to create update operations based on their name. This class acts as a factory for \c KDUpdater::UpdateOperation. You can register one or more update operations with this factory and query operations based on their name. This class follows the singleton design pattern. Only one instance of this class can be created and its reference can be fetched from the instance() method. The following operations are registered by default: \list \li Copy operation \li Move operation \li Delete operation \li Mkdir operation \li Rmdir operation \li AppendFile operation \li PrependFile operation \endlist */ /*! \obsolete \fn void KDUpdater::UpdateOperationFactory::registerUpdateOperation(const QString &name) Registers a new update operation with the factory based on \a name. When create() is called with that \a name, the update operation is constructed using its default constructor. Deprecated. Use registerProduct() instead. */ /*! Returns the UpdateOperationFactory instance. The instance is created if needed. */ UpdateOperationFactory &UpdateOperationFactory::instance() { static UpdateOperationFactory theFactory; return theFactory; } /*! Constructor */ UpdateOperationFactory::UpdateOperationFactory() { // Register the default update operation set registerUpdateOperation<CopyOperation>(QLatin1String("Copy")); registerUpdateOperation<MoveOperation>(QLatin1String("Move")); registerUpdateOperation<DeleteOperation>(QLatin1String("Delete")); registerUpdateOperation<MkdirOperation>(QLatin1String("Mkdir")); registerUpdateOperation<RmdirOperation>(QLatin1String("Rmdir")); registerUpdateOperation<AppendFileOperation>(QLatin1String("AppendFile")); registerUpdateOperation<PrependFileOperation>(QLatin1String("PrependFile")); } �����������������������������������������������������������������������src/libs/kdtools/updateoperationfactory.h�����������������������������������������������������������0000664�0000000�0000000�00000004047�13253666515�0021242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATEOPERATIONFACTORY_H #define UPDATEOPERATIONFACTORY_H #include "genericfactory.h" #include "updater.h" namespace QInstaller { class PackageManagerCore; } namespace KDUpdater { class UpdateOperation; class KDTOOLS_EXPORT UpdateOperationFactory : public GenericFactory<UpdateOperation, QString, QInstaller::PackageManagerCore*> { Q_DISABLE_COPY(UpdateOperationFactory) public: static UpdateOperationFactory &instance(); template <class T> void registerUpdateOperation(const QString &name) { registerProduct<T>(name); } private: UpdateOperationFactory(); }; } // namespace KDUpdater #endif // UPDATEOPERATIONFACTORY_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updateoperations.cpp���������������������������������������������������������������0000664�0000000�0000000�00000064270�13253666515�0020374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "updateoperations.h" #include "errors.h" #include "fileutils.h" #include "constants.h" #include "packagemanagercore.h" #include <QDir> #include <QFile> #include <QTextStream> #include <QTemporaryFile> #include <QFileInfo> #include <cerrno> using namespace KDUpdater; static QString errnoToQString(int error) { #if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) char msg[128]; if (strerror_s(msg, sizeof msg, error) != 0) return QString::fromLocal8Bit(msg); return QString(); #else return QString::fromLocal8Bit(strerror(error)); #endif } static bool removeDirectory(const QString &path, QString *errorString, bool force) { Q_ASSERT(errorString); QDir dir = path; const QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden); foreach (const QFileInfo &entry, entries) { if (entry.isDir() && (!entry.isSymLink())) removeDirectory(entry.filePath(), errorString, force); else if (force && (!QFile(entry.filePath()).remove())) return false; } // even remove some hidden, OS-created files in there QInstaller::removeSystemGeneratedFiles(path); errno = 0; const bool success = dir.rmdir(path); if (errno) *errorString = errnoToQString(errno); return success; } /* * \internal * Returns a filename for a temporary file based on \a templateName */ static QString backupFileName(const QString &templateName) { QTemporaryFile file(templateName); file.open(); const QString name = file.fileName(); file.close(); file.remove(); return name; } //////////////////////////////////////////////////////////////////////////// // KDUpdater::CopyOperation //////////////////////////////////////////////////////////////////////////// CopyOperation::CopyOperation(QInstaller::PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Copy")); } CopyOperation::~CopyOperation() { deleteFileNowOrLater(value(QLatin1String("backupOfExistingDestination")).toString()); } QString CopyOperation::sourcePath() { return arguments().first(); } QString CopyOperation::destinationPath() { QString destination = arguments().last(); // if the target is a directory use the source filename to complete the destination path if (QFileInfo(destination).isDir()) destination = QDir(destination).filePath(QFileInfo(sourcePath()).fileName()); return destination; } void CopyOperation::backup() { QString destination = destinationPath(); if (!QFile::exists(destination)) { clearValue(QLatin1String("backupOfExistingDestination")); return; } setValue(QLatin1String("backupOfExistingDestination"), backupFileName(destination)); // race condition: The backup file could get created by another process right now. But this is the same // in QFile::copy... if (!QFile::rename(destination, value(QLatin1String("backupOfExistingDestination")).toString())) setError(UserDefinedError, tr("Cannot backup file \"%1\".").arg(QDir::toNativeSeparators(destination))); } bool CopyOperation::performOperation() { // We need two args to complete the copy operation. First arg provides the complete file name of source // Second arg provides the complete file name of dest if (!checkArgumentCount(2)) return false; QString source = sourcePath(); QString destination = destinationPath(); QFile sourceFile(source); if (!sourceFile.exists()) { setError(UserDefinedError); setErrorString(tr("Cannot copy a non-existent file: %1").arg(QDir::toNativeSeparators(source))); return false; } // If destination file exists, we cannot use QFile::copy() because it does not overwrite an existing // file. So we remove the destination file. QFile destinationFile(destination); if (destinationFile.exists()) { if (!destinationFile.remove()) { setError(UserDefinedError); setErrorString(tr("Cannot remove file \"%1\": %2").arg( QDir::toNativeSeparators(destination), destinationFile.errorString())); return false; } } const bool copied = sourceFile.copy(destination); if (!copied) { setError(UserDefinedError); setErrorString(tr("Cannot copy file \"%1\" to \"%2\": %3").arg( QDir::toNativeSeparators(source), QDir::toNativeSeparators(destination), sourceFile.errorString())); } return copied; } bool CopyOperation::undoOperation() { QString source = sourcePath(); QString destination = destinationPath(); // if the target is a directory use the source filename to complete the destination path if (QFileInfo(destination).isDir()) destination = destination + QDir::separator() + QFileInfo(source).fileName(); QFile destFile(destination); // first remove the dest if (destFile.exists() && !destFile.remove()) { setError(UserDefinedError, tr("Cannot delete file \"%1\": %2").arg( QDir::toNativeSeparators(destination), destFile.errorString())); return false; } // no backup was done: // the copy destination file wasn't existing yet - that's no error if (!hasValue(QLatin1String("backupOfExistingDestination"))) return true; QFile backupFile(value(QLatin1String("backupOfExistingDestination")).toString()); // otherwise we have to copy the backup back: const bool success = backupFile.rename(destination); if (!success) setError(UserDefinedError, tr("Cannot restore backup file into \"%1\": %2").arg( QDir::toNativeSeparators(destination), backupFile.errorString())); return success; } /*! \reimp */ QDomDocument CopyOperation::toXml() const { // we don't want to save the backupOfExistingDestination if (!hasValue(QLatin1String("backupOfExistingDestination"))) return UpdateOperation::toXml(); CopyOperation *const me = const_cast<CopyOperation *>(this); const QVariant v = value(QLatin1String("backupOfExistingDestination")); me->clearValue(QLatin1String("backupOfExistingDestination")); const QDomDocument xml = UpdateOperation::toXml(); me->setValue(QLatin1String("backupOfExistingDestination"), v); return xml; } bool CopyOperation::testOperation() { // TODO return true; } //////////////////////////////////////////////////////////////////////////// // KDUpdater::MoveOperation //////////////////////////////////////////////////////////////////////////// MoveOperation::MoveOperation(QInstaller::PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Move")); } MoveOperation::~MoveOperation() { deleteFileNowOrLater(value(QLatin1String("backupOfExistingDestination")).toString()); } void MoveOperation::backup() { const QString dest = arguments().last(); if (!QFile::exists(dest)) { clearValue(QLatin1String("backupOfExistingDestination")); return; } setValue(QLatin1String("backupOfExistingDestination"), backupFileName(dest)); // race condition: The backup file could get created by another process right now. But this is the same // in QFile::copy... if (!QFile::rename(dest, value(QLatin1String("backupOfExistingDestination")).toString())) setError(UserDefinedError, tr("Cannot backup file \"%1\".").arg(QDir::toNativeSeparators(dest))); } bool MoveOperation::performOperation() { // We need two args to complete the copy operation. // First arg provides the complete file name of // source, second arg provides the complete file name of dest if (!checkArgumentCount(2)) return false; const QStringList args = arguments(); const QString dest = args.at(1); // If destination file exists, then we cannot use QFile::copy() because it does not overwrite an existing // file. So we remove the destination file. if (QFile::exists(dest)) { QFile file(dest); if (!file.remove(dest)) { setError(UserDefinedError); setErrorString(tr("Cannot remove file \"%1\": %2").arg( QDir::toNativeSeparators(dest), file.errorString())); return false; } } // Copy source to destination. QFile file(args.at(0)); if (!file.copy(dest)) { setError(UserDefinedError); setErrorString(tr("Cannot copy file \"%1\" to \"%2\": %3").arg(QDir::toNativeSeparators(file.fileName()), QDir::toNativeSeparators(dest), file.errorString())); return false; } return deleteFileNowOrLater(file.fileName()); } bool MoveOperation::undoOperation() { const QStringList args = arguments(); const QString dest = args.last(); // first: copy back the destination to source QFile destF(dest); if (!destF.copy(args.first())) { setError(UserDefinedError, tr("Cannot copy file \"%1\" to \"%2\": %3").arg( QDir::toNativeSeparators(dest), QDir::toNativeSeparators(args.first()), destF.errorString())); return false; } // second: delete the move destination if (!deleteFileNowOrLater(dest)) { setError(UserDefinedError, tr("Cannot remove file \"%1\".").arg(QDir::toNativeSeparators(dest))); return false; } // no backup was done: // the move destination file wasn't existing yet - that's no error if (!hasValue(QLatin1String("backupOfExistingDestination"))) return true; // otherwise we have to copy the backup back: QFile backupF(value(QLatin1String("backupOfExistingDestination")).toString()); const bool success = backupF.rename(dest); if (!success) setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( QDir::toNativeSeparators(dest), backupF.errorString())); return success; } bool MoveOperation::testOperation() { // TODO return true; } //////////////////////////////////////////////////////////////////////////// // KDUpdater::DeleteOperation //////////////////////////////////////////////////////////////////////////// DeleteOperation::DeleteOperation(QInstaller::PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Delete")); } DeleteOperation::~DeleteOperation() { deleteFileNowOrLater(value(QLatin1String("backupOfExistingFile")).toString()); } void DeleteOperation::backup() { const QString fileName = arguments().first(); setValue(QLatin1String("backupOfExistingFile"), backupFileName(fileName)); QFile file(fileName); if (!file.copy(value(QLatin1String("backupOfExistingFile")).toString())) setError(UserDefinedError, tr("Cannot create backup of file \"%1\": %2").arg( QDir::toNativeSeparators(fileName), file.errorString())); } bool DeleteOperation::performOperation() { // Requires only one parameter. That is the name of the file to remove. if (!checkArgumentCount(1)) return false; return deleteFileNowOrLater(arguments().at(0)); } bool DeleteOperation::undoOperation() { if (!hasValue(QLatin1String("backupOfExistingFile"))) return true; const QString fileName = arguments().first(); QFile backupF(value(QLatin1String("backupOfExistingFile")).toString()); const bool success = backupF.copy(fileName) && deleteFileNowOrLater(backupF.fileName()); if (!success) setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( QDir::toNativeSeparators(fileName), backupF.errorString())); return success; } bool DeleteOperation::testOperation() { // TODO return true; } /*! \reimp */ QDomDocument DeleteOperation::toXml() const { // we don't want to save the backupOfExistingFile if (!hasValue(QLatin1String("backupOfExistingFile"))) return UpdateOperation::toXml(); DeleteOperation *const me = const_cast<DeleteOperation *>(this); const QVariant v = value(QLatin1String("backupOfExistingFile")); me->clearValue(QLatin1String("backupOfExistingFile")); const QDomDocument xml = UpdateOperation::toXml(); me->setValue(QLatin1String("backupOfExistingFile"), v); return xml; } //////////////////////////////////////////////////////////////////////////// // KDUpdater::MkdirOperation //////////////////////////////////////////////////////////////////////////// MkdirOperation::MkdirOperation(QInstaller::PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Mkdir")); } void MkdirOperation::backup() { QString path = arguments().first(); path.replace(QLatin1Char('\\'), QLatin1Char('/')); QDir createdDir = QDir::root(); // find out, which part of the path is the first one we actually need to create int end = 0; while (true) { QString p = path.section(QLatin1Char('/'), 0, ++end); createdDir = QDir(p); if (!createdDir.exists()) break; if (p == path) { // everything did already exist -> nothing to do for us (nothing to revert then, either) createdDir = QDir::root(); break; } } setValue(QLatin1String("createddir"), createdDir.absolutePath()); } bool MkdirOperation::performOperation() { // Requires only one parameter. That is the path which should be created if (!checkArgumentCount(1)) return false; const QString dirName = arguments().at(0); const bool created = QDir::root().mkpath(dirName); if (!created) { setError(UserDefinedError); setErrorString(tr("Cannot create directory \"%1\": %2").arg( QDir::toNativeSeparators(dirName), tr("Unknown error."))); } return created; } bool MkdirOperation::undoOperation() { Q_ASSERT(arguments().count() == 1); QString createdDirValue = value(QLatin1String("createddir")).toString(); if (packageManager()) { createdDirValue = QInstaller::replacePath(createdDirValue, QLatin1String(QInstaller::scRelocatable), packageManager()->value(QInstaller::scTargetDir)); } if (createdDirValue.isEmpty()) createdDirValue = arguments().first(); QDir createdDir = QDir(createdDirValue); const bool forceremoval = QVariant(value(QLatin1String("forceremoval"))).toBool(); // Since refactoring we know the mkdir operation which is creating the target path. If we do a full // uninstall prevent removing the full path including target, instead remove the target only. (QTIFW-46) if (hasValue(QLatin1String("uninstall-only")) && value(QLatin1String("uninstall-only")).toBool()) createdDir = QDir(arguments().first()); if (createdDir == QDir::root()) return true; if (!createdDir.exists()) return true; QString errorString; const bool result = removeDirectory(createdDir.path(), &errorString, forceremoval); if (!result) { if (errorString.isEmpty()) setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( QDir::toNativeSeparators(createdDir.path()), errorString)); else setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( QDir::toNativeSeparators(createdDir.path()), errnoToQString(errno))); } return result; } bool KDUpdater::MkdirOperation::testOperation() { // TODO return true; } //////////////////////////////////////////////////////////////////////////// // KDUpdater::RmdirOperation //////////////////////////////////////////////////////////////////////////// RmdirOperation::RmdirOperation(QInstaller::PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("Rmdir")); setValue(QLatin1String("removed"), false); } void RmdirOperation::backup() { // nothing to backup - rollback will just create the directory } bool RmdirOperation::performOperation() { // Requires only one parameter. That is the name of the file to remove. if (!checkArgumentCount(1)) return false; const QString firstArg = arguments().at(0); QDir dir(firstArg); if (!dir.exists()) { setError(UserDefinedError); setErrorString(tr("Cannot remove directory \"%1\": %2").arg( QDir::toNativeSeparators(firstArg), tr("The directory does not exist."))); return false; } errno = 0; const bool removed = dir.rmdir(firstArg); setValue(QLatin1String("removed"), removed); if (!removed) { setError(UserDefinedError); setErrorString(tr("Cannot remove directory \"%1\": %2").arg( QDir::toNativeSeparators(firstArg), errnoToQString(errno))); } return removed; } bool RmdirOperation::undoOperation() { if (!value(QLatin1String("removed")).toBool()) return true; errno = 0; const QFileInfo fi(arguments().first()); const bool success = fi.dir().mkdir(fi.fileName()); if( !success) setError(UserDefinedError, tr("Cannot recreate directory \"%1\": %2").arg( QDir::toNativeSeparators(fi.fileName()), errnoToQString(errno))); return success; } bool RmdirOperation::testOperation() { // TODO return true; } //////////////////////////////////////////////////////////////////////////// // KDUpdater::AppendFileOperation //////////////////////////////////////////////////////////////////////////// AppendFileOperation::AppendFileOperation(QInstaller::PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("AppendFile")); } void AppendFileOperation::backup() { const QString filename = arguments().first(); QFile file(filename); if (!file.exists()) return; // nothing to backup setValue(QLatin1String("backupOfFile"), backupFileName(filename)); if (!file.copy(value(QLatin1String("backupOfFile")).toString())) { setError(UserDefinedError, tr("Cannot backup file \"%1\": %2").arg( QDir::toNativeSeparators(filename), file.errorString())); clearValue(QLatin1String("backupOfFile")); } } bool AppendFileOperation::performOperation() { // This operation takes two arguments. First argument is the name of the file into which a text has to be // appended. Second argument is the text to append. if (!checkArgumentCount(2)) return false; const QStringList args = this->arguments(); const QString fName = args.at(0); QFile file(fName); if (!file.open(QFile::Append)) { // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion bool error = false; const QString newName = backupFileName(fName); if (!QFile::rename(fName, newName)) error = true; if (!error && !QFile::copy(newName, fName)) { error = true; QFile::rename(newName, fName); } if (!error && !file.open(QFile::Append)) { error = true; deleteFileNowOrLater(newName); } if (error) { setError(UserDefinedError); setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(file.fileName()), file.errorString())); return false; } deleteFileNowOrLater(newName); } QTextStream ts(&file); ts << args.at(1); file.close(); return true; } bool AppendFileOperation::undoOperation() { // backupOfFile being empty -> file didn't exist before -> no error const QString filename = arguments().first(); const QString backupOfFile = value(QLatin1String("backupOfFile")).toString(); if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) { setError(UserDefinedError, tr("Cannot find backup file for \"%1\".").arg( QDir::toNativeSeparators(filename))); return false; } const bool removed = deleteFileNowOrLater(filename); if (!removed) { setError(UserDefinedError, tr("Cannot restore backup file for \"%1\".").arg( QDir::toNativeSeparators(filename))); return false; } // got deleted? We might be done, if it didn't exist before if (backupOfFile.isEmpty()) return true; QFile backupFile(backupOfFile); const bool success = backupFile.rename(filename); if (!success) setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( QDir::toNativeSeparators(filename), backupFile.errorString())); return success; } bool AppendFileOperation::testOperation() { // TODO return true; } //////////////////////////////////////////////////////////////////////////// // KDUpdater::PrependFileOperation //////////////////////////////////////////////////////////////////////////// PrependFileOperation::PrependFileOperation(QInstaller::PackageManagerCore *core) : UpdateOperation(core) { setName(QLatin1String("PrependFile")); } void PrependFileOperation::backup() { const QString filename = arguments().first(); QFile file(filename); if (!file.exists()) return; // nothing to backup setValue(QLatin1String("backupOfFile"), backupFileName(filename)); if (!file.copy(value(QLatin1String("backupOfFile")).toString())) { setError(UserDefinedError, tr("Cannot backup file \"%1\": %2").arg( QDir::toNativeSeparators(filename), file.errorString())); clearValue(QLatin1String("backupOfFile")); } } bool PrependFileOperation::performOperation() { // This operation takes two arguments. First argument is the name // of the file into which a text has to be appended. Second argument // is the text to append. if (!checkArgumentCount(2)) return false; const QStringList args = this->arguments(); const QString fName = args.at(0); // Load the file first. QFile file(fName); if (!file.open(QFile::ReadOnly)) { setError(UserDefinedError); setErrorString(tr("Cannot open file \"%1\" for reading: %2").arg( QDir::toNativeSeparators(file.fileName()), file.errorString())); return false; } // TODO: fix this, use a text stream QString fContents(QLatin1String(file.readAll())); file.close(); // Prepend text to the file text fContents = args.at(1) + fContents; // Now re-open the file in write only mode. if (!file.open(QFile::WriteOnly)) { // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion const QString newName = backupFileName(fName); if (!QFile::rename(fName, newName) && QFile::copy(newName, fName) && file.open(QFile::WriteOnly)) { QFile::rename(newName, fName); setError(UserDefinedError); setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(file.fileName()), file.errorString())); return false; } deleteFileNowOrLater(newName); } QTextStream ts(&file); ts << fContents; file.close(); return true; } bool PrependFileOperation::undoOperation() { // bockupOfFile being empty -> file didn't exist before -> no error const QString filename = arguments().first(); const QString backupOfFile = value(QLatin1String("backupOfFile")).toString(); if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) { setError(UserDefinedError, tr("Cannot find backup file for \"%1\".").arg(QDir::toNativeSeparators(filename))); return false; } if (!deleteFileNowOrLater(filename)) { setError(UserDefinedError, tr("Cannot restore backup file for \"%1\".").arg(QDir::toNativeSeparators(filename))); return false; } // got deleted? We might be done, if it didn't exist before if (backupOfFile.isEmpty()) return true; QFile backupF(backupOfFile); const bool success = backupF.rename(filename); if (!success) setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( QDir::toNativeSeparators(filename), backupF.errorString())); return success; } bool PrependFileOperation::testOperation() { // TODO return true; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updateoperations.h�����������������������������������������������������������������0000664�0000000�0000000�00000007501�13253666515�0020033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATEOPERATIONS_H #define UPDATEOPERATIONS_H #include "updateoperation.h" namespace KDUpdater { class KDTOOLS_EXPORT CopyOperation : public UpdateOperation { Q_DECLARE_TR_FUNCTIONS(KDUpdater::CopyOperation) public: explicit CopyOperation(QInstaller::PackageManagerCore *core = 0); ~CopyOperation(); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); QDomDocument toXml() const; private: QString sourcePath(); QString destinationPath(); }; class KDTOOLS_EXPORT MoveOperation : public UpdateOperation { Q_DECLARE_TR_FUNCTIONS(KDUpdater::MoveOperation) public: explicit MoveOperation(QInstaller::PackageManagerCore *core = 0); ~MoveOperation(); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; class KDTOOLS_EXPORT DeleteOperation : public UpdateOperation { Q_DECLARE_TR_FUNCTIONS(KDUpdater::DeleteOperation) public: explicit DeleteOperation(QInstaller::PackageManagerCore *core = 0); ~DeleteOperation(); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); QDomDocument toXml() const; }; class KDTOOLS_EXPORT MkdirOperation : public UpdateOperation { Q_DECLARE_TR_FUNCTIONS(KDUpdater::MkdirOperation) public: explicit MkdirOperation(QInstaller::PackageManagerCore *core = 0); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; class KDTOOLS_EXPORT RmdirOperation : public UpdateOperation { Q_DECLARE_TR_FUNCTIONS(KDUpdater::RmdirOperation) public: RmdirOperation(QInstaller::PackageManagerCore *core = 0); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; class KDTOOLS_EXPORT AppendFileOperation : public UpdateOperation { Q_DECLARE_TR_FUNCTIONS(KDUpdater::AppendFileOperation) public: explicit AppendFileOperation(QInstaller::PackageManagerCore *core = 0); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; class KDTOOLS_EXPORT PrependFileOperation : public UpdateOperation { Q_DECLARE_TR_FUNCTIONS(KDUpdater::PrependFileOperation) public: explicit PrependFileOperation(QInstaller::PackageManagerCore *core = 0); void backup(); bool performOperation(); bool undoOperation(); bool testOperation(); }; } // namespace KDUpdater #endif // UPDATEOPERATIONS_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updater.h��������������������������������������������������������������������������0000664�0000000�0000000�00000003230�13253666515�0016104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATER_H #define UPDATER_H #include "kdtoolsglobal.h" namespace KDUpdater { enum Error { ENoError = 0, ECannotStartTask, ECannotPauseTask, ECannotResumeTask, ECannotStopTask, EUnknown }; KDTOOLS_EXPORT int compareVersion(const QString &v1, const QString &v2); } #endif // UPDATER_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updatesinfo.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000021522�13253666515�0017320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "updatesinfo_p.h" #include "utils.h" #include <QDomDocument> #include <QFile> #include <QLocale> #include <QPair> #include <QVector> #include <QUrl> using namespace KDUpdater; UpdatesInfoData::UpdatesInfoData() : error(UpdatesInfo::NotYetReadError) { } UpdatesInfoData::~UpdatesInfoData() { } void UpdatesInfoData::setInvalidContentError(const QString &detail) { error = UpdatesInfo::InvalidContentError; errorMessage = tr("Updates.xml contains invalid content: %1").arg(detail); } void UpdatesInfoData::parseFile(const QString &updateXmlFile) { QFile file(updateXmlFile); if (!file.open(QFile::ReadOnly)) { error = UpdatesInfo::CouldNotReadUpdateInfoFileError; errorMessage = tr("Cannot read \"%1\"").arg(updateXmlFile); return; } QDomDocument doc; QString parseErrorMessage; int parseErrorLine, parseErrorColumn; if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) { error = UpdatesInfo::InvalidXmlError; errorMessage = tr("Parse error in %1 at %2, %3: %4").arg(updateXmlFile, QString::number(parseErrorLine), QString::number(parseErrorColumn), parseErrorMessage); return; } QDomElement rootE = doc.documentElement(); if (rootE.tagName() != QLatin1String("Updates")) { setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(rootE.tagName())); return; } QDomNodeList childNodes = rootE.childNodes(); for(int i = 0; i < childNodes.count(); i++) { const QDomElement childE = childNodes.at(i).toElement(); if (childE.isNull()) continue; if (childE.tagName() == QLatin1String("ApplicationName")) applicationName = childE.text(); else if (childE.tagName() == QLatin1String("ApplicationVersion")) applicationVersion = childE.text(); else if (childE.tagName() == QLatin1String("PackageUpdate")) { if (!parsePackageUpdateElement(childE)) return; //error handled in subroutine } } if (applicationName.isEmpty()) { setInvalidContentError(tr("ApplicationName element is missing.")); return; } if (applicationVersion.isEmpty()) { setInvalidContentError(tr("ApplicationVersion element is missing.")); return; } errorMessage.clear(); error = UpdatesInfo::NoError; } bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE) { if (updateE.isNull()) return false; UpdateInfo info; QMap<QString, QString> localizedDescriptions; for (int i = 0; i < updateE.childNodes().count(); i++) { QDomElement childE = updateE.childNodes().at(i).toElement(); if (childE.isNull()) continue; if (childE.tagName() == QLatin1String("ReleaseNotes")) { info.data[childE.tagName()] = QUrl(childE.text()); } else if (childE.tagName() == QLatin1String("Licenses")) { QHash<QString, QVariant> licenseHash; const QDomNodeList licenseNodes = childE.childNodes(); for (int i = 0; i < licenseNodes.count(); ++i) { const QDomNode licenseNode = licenseNodes.at(i); if (licenseNode.nodeName() == QLatin1String("License")) { QDomElement element = licenseNode.toElement(); licenseHash.insert(element.attributeNode(QLatin1String("name")).value(), element.attributeNode(QLatin1String("file")).value()); } } if (!licenseHash.isEmpty()) info.data.insert(QLatin1String("Licenses"), licenseHash); } else if (childE.tagName() == QLatin1String("Version")) { info.data.insert(QLatin1String("inheritVersionFrom"), childE.attribute(QLatin1String("inheritVersionFrom"))); info.data[childE.tagName()] = childE.text(); } else if (childE.tagName() == QLatin1String("DisplayName")) { processLocalizedTag(childE, info.data); } else if (childE.tagName() == QLatin1String("Description")) { if (!childE.hasAttribute(QLatin1String("xml:lang"))) info.data[QLatin1String("Description")] = childE.text(); QString languageAttribute = childE.attribute(QLatin1String("xml:lang"), QLatin1String("en")); localizedDescriptions.insert(languageAttribute.toLower(), childE.text()); } else if (childE.tagName() == QLatin1String("UpdateFile")) { info.data[QLatin1String("CompressedSize")] = childE.attribute(QLatin1String("CompressedSize")); info.data[QLatin1String("UncompressedSize")] = childE.attribute(QLatin1String("UncompressedSize")); } else { info.data[childE.tagName()] = childE.text(); } } QStringList candidates; foreach (const QString &lang, QLocale().uiLanguages()) candidates << QInstaller::localeCandidates(lang.toLower()); foreach (const QString &candidate, candidates) { if (localizedDescriptions.contains(candidate)) { info.data[QLatin1String("Description")] = localizedDescriptions.value(candidate); break; } } if (!info.data.contains(QLatin1String("Name"))) { setInvalidContentError(tr("PackageUpdate element without Name")); return false; } if (!info.data.contains(QLatin1String("Version"))) { setInvalidContentError(tr("PackageUpdate element without Version")); return false; } if (!info.data.contains(QLatin1String("ReleaseDate"))) { setInvalidContentError(tr("PackageUpdate element without ReleaseDate")); return false; } updateInfoList.append(info); return true; } void UpdatesInfoData::processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const { QString languageAttribute = childE.attribute(QLatin1String("xml:lang")).toLower(); if (!info.contains(childE.tagName()) && (languageAttribute.isEmpty())) info[childE.tagName()] = childE.text(); // overwrite default if we have a language specific description if (QLocale().name().startsWith(languageAttribute, Qt::CaseInsensitive)) info[childE.tagName()] = childE.text(); } // // UpdatesInfo // UpdatesInfo::UpdatesInfo() : d(new UpdatesInfoData) { } UpdatesInfo::~UpdatesInfo() { } bool UpdatesInfo::isValid() const { return d->error == NoError; } QString UpdatesInfo::errorString() const { return d->errorMessage; } void UpdatesInfo::setFileName(const QString &updateXmlFile) { if (d->updateXmlFile == updateXmlFile) return; d->applicationName.clear(); d->applicationVersion.clear(); d->updateInfoList.clear(); d->updateXmlFile = updateXmlFile; d->parseFile(d->updateXmlFile); } QString UpdatesInfo::fileName() const { return d->updateXmlFile; } QString UpdatesInfo::applicationName() const { return d->applicationName; } QString UpdatesInfo::applicationVersion() const { return d->applicationVersion; } int UpdatesInfo::updateInfoCount() const { return d->updateInfoList.count(); } UpdateInfo UpdatesInfo::updateInfo(int index) const { if (index < 0 || index >= d->updateInfoList.count()) return UpdateInfo(); return d->updateInfoList.at(index); } QList<UpdateInfo> UpdatesInfo::updatesInfo() const { return d->updateInfoList; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/kdtools/updatesinfo_p.h��������������������������������������������������������������������0000664�0000000�0000000�00000004710�13253666515�0017304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATESINFO_P_H #define UPDATESINFO_P_H #include "updater.h" #include "updatesinfodata_p.h" #include <QHash> #include <QSharedData> #include <QVariant> // They are not a part of the public API // Classes and structures in this header file are for internal use only but still exported for auto tests. namespace KDUpdater { struct KDTOOLS_EXPORT UpdateInfo { QHash<QString, QVariant> data; }; class KDTOOLS_EXPORT UpdatesInfo { public: enum Error { NoError = 0, NotYetReadError, CouldNotReadUpdateInfoFileError, InvalidXmlError, InvalidContentError }; UpdatesInfo(); ~UpdatesInfo(); bool isValid() const; Error error() const; QString errorString() const; QString fileName() const; void setFileName(const QString &updateXmlFile); QString applicationName() const; QString applicationVersion() const; int updateInfoCount() const; UpdateInfo updateInfo(int index) const; QList<UpdateInfo> updatesInfo() const; private: QSharedDataPointer<UpdatesInfoData> d; }; } // namespace KDUpdater #endif // UPDATESINFO_P_H ��������������������������������������������������������src/libs/kdtools/updatesinfodata_p.h����������������������������������������������������������������0000664�0000000�0000000�00000004232�13253666515�0020135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/**************************************************************************** ** ** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef UPDATESINFODATA_P_H #define UPDATESINFODATA_P_H #include <QCoreApplication> #include <QSharedData> QT_FORWARD_DECLARE_CLASS(QDomElement) namespace KDUpdater { struct UpdateInfo; struct UpdatesInfoData : public QSharedData { Q_DECLARE_TR_FUNCTIONS(KDUpdater::UpdatesInfoData) public: UpdatesInfoData(); ~UpdatesInfoData(); int error; QString errorMessage; QString updateXmlFile; QString applicationName; QString applicationVersion; QList<UpdateInfo> updateInfoList; void parseFile(const QString &updateXmlFile); bool parsePackageUpdateElement(const QDomElement &updateE); void setInvalidContentError(const QString &detail); private: void processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const; }; } // namespace KDUpdater #endif // UPDATESINFODATA_P_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/libs/libs.pro�����������������������������������������������������������������������������������0000664�0000000�0000000�00000000111�13253666515�0014256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEMPLATE = subdirs SUBDIRS += 7zip installer installer.depends = 7zip �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/��������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0012442�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/commandlineparser.cpp�����������������������������������������������������������������������0000664�0000000�0000000�00000016260�13253666515�0016656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "commandlineparser.h" #include "constants.h" #include "globals.h" namespace CommandLineOptions { const char KeyValue[] = "Key=Value"; } // namespace CommandLineOptions CommandLineParser::CommandLineParser() { m_parser.addHelpOption(); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::Version), QLatin1String("Displays version information."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::FrameworkVersion), QLatin1String("Displays the version of the Qt Installer Framework."))); m_parser.addOption(QCommandLineOption(QStringList() << QLatin1String(CommandLineOptions::VerboseShort) << QLatin1String(CommandLineOptions::VerboseLong), QLatin1String("Verbose mode. Prints out more information."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::Proxy), QLatin1String("Use system proxy on Windows and Linux. This option has no effect on OS X."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::NoProxy), QLatin1String("Do not use system proxy."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::Script), QLatin1String("Execute the script given as argument."), QLatin1String("file"))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::CheckUpdates), QLatin1String("Check for updates and return an XML description."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::Updater), QLatin1String("Start application in updater mode."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::ManagePackages), QLatin1String("Start application in package manager mode."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::NoForceInstallation), QLatin1String("Allow deselecting components that are marked as forced."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::ShowVirtualComponents), QLatin1String("Show virtual components in installer and package manager."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::LoggingRules), QLatin1String("Enables logging according to passed rules. " "Comma separated logging rules have the following syntax: " "loggingCategory=true/false. " "Passing empty logging rules enables all logging categories. " "The following rules enable a single category: " "ifw.*=false,ifw.category=true " "The following logging categories are available:\n") + QInstaller::loggingCategories().join(QLatin1Char('\n')), QLatin1String("rules"))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::CreateLocalRepository), QLatin1String("Create a local repository inside the installation directory. This option " "has no effect on online installers."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::AddRepository), QLatin1String("Add a local or remote repository to the list of user defined repositories."), QLatin1String("URI,..."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::AddTmpRepository), QLatin1String("Add a local or remote repository to the list of temporary available " "repositories."), QLatin1String("URI,..."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::SetTmpRepository), QLatin1String("Set a local or remote repository as temporary repository, it is the only " "one used during fetch.\nNote: URI must be prefixed with the protocol, i.e. file:///, " "https://, http:// or ftp://."), QLatin1String("URI,..."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::StartServer), QLatin1String("Starts the application as headless process waiting for commands to execute." " Mode can be DEBUG or PRODUCTION. In DEBUG mode, the option values can be omitted." "Note: The server will not shutdown on his own, you need to quit the process by hand."), QLatin1String("mode,socketname,key"))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::StartClient), QString::fromLatin1("Starts the application to debug the client-server communication. If " "a value is omitted, the client will use a default instead. Note: The server process is " "not started by the client application in that case, you need to start it on your own."), QLatin1String("socketname,key"))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::InstallCompressedRepository), QLatin1String("Installs QBSP or 7z file. The QBSP (Board Support Package) file must be a .7z " "file which contains a valid repository."), QLatin1String("URI,..."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::SilentUpdate), QLatin1String("Updates all packages silently."))); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::Platform), QLatin1String("Use the specified platform plugin."), QLatin1String("plugin"))); m_parser.addPositionalArgument(QLatin1String(CommandLineOptions::KeyValue), QLatin1String("Key Value pair to be set.")); m_parser.addOption(QCommandLineOption(QLatin1String(CommandLineOptions::SquishPort), QLatin1String("Give a port where Squish can connect to. If no port is given, default " "port 11233 is used. Note: To enable Squish support you first need to build IFW with " "SQUISH_PATH parameter where SQUISH_PATH is pointing to your Squish installation folder: " "<path_to_qt>/bin/qmake -r SQUISH_PATH=<pat_to_squish>"), QLatin1String("port number"))); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/commandlineparser.h�������������������������������������������������������������������������0000664�0000000�0000000�00000003726�13253666515�0016326�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef COMMANDLINEPARSER_H #define COMMANDLINEPARSER_H #include <QCommandLineParser> class CommandLineParser { public: CommandLineParser(); QString helpText() const { return m_parser.helpText(); } bool isSet(const QString &option) { return m_parser.isSet(option); } QStringList unknownOptionNames() const { return m_parser.unknownOptionNames(); } QStringList positionalArguments() const { return m_parser.positionalArguments(); } bool parse(const QStringList &argumens) { return m_parser.parse(argumens); } QString value(const QString &option) const { return m_parser.value(option); } private: QCommandLineParser m_parser; }; #endif // COMMANDLINEPARSER_H ������������������������������������������src/sdk/console.h�����������������������������������������������������������������������������������0000664�0000000�0000000�00000003342�13253666515�0014257�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CONSOLE_H #define CONSOLE_H #include <QtGlobal> #ifdef Q_OS_WIN #include <fstream> #include <iostream> class Console { public: Console(); ~Console(); private: bool parentConsole; std::ofstream m_newCout; std::ofstream m_newCerr; std::streambuf* m_oldCout; std::streambuf* m_oldCerr; }; #else // Q_OS_WIN class Console { public: Console() {} }; #endif // Q_OS_WIN #endif // CONSOLE_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/console_win.cpp�����������������������������������������������������������������������������0000664�0000000�0000000�00000010737�13253666515�0015475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "console.h" # include <qt_windows.h> # include <wincon.h> # ifndef ENABLE_INSERT_MODE # define ENABLE_INSERT_MODE 0x0020 # endif # ifndef ENABLE_QUICK_EDIT_MODE # define ENABLE_QUICK_EDIT_MODE 0x0040 # endif # ifndef ENABLE_EXTENDED_FLAGS # define ENABLE_EXTENDED_FLAGS 0x0080 # endif static bool isRedirected(HANDLE stdHandle) { if (stdHandle == NULL) // launched from GUI return false; DWORD fileType = GetFileType(stdHandle); if (fileType == FILE_TYPE_UNKNOWN) { // launched from console, but no redirection return false; } // redirected into file, pipe ... return true; } /** * Redirects stdout, stderr output to console * * Console is a RAII class that ensures stdout, stderr output is visible * for GUI applications on Windows. * * If the application is launched from the explorer, startup menu etc * a new console window is created. * * If the application is launched from the console (cmd.exe), output is * printed there. * * If the application is launched from the console, but stdout is redirected * (e.g. into a file), Console does not interfere. */ Console::Console() : m_oldCout(0), m_oldCerr(0) { bool isCoutRedirected = isRedirected(GetStdHandle(STD_OUTPUT_HANDLE)); bool isCerrRedirected = isRedirected(GetStdHandle(STD_ERROR_HANDLE)); if (!isCoutRedirected) { // verbose output only ends up in cout // try to use parent console. else launch & set up new console parentConsole = AttachConsole(ATTACH_PARENT_PROCESS); if (!parentConsole) { AllocConsole(); HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); if (handle != INVALID_HANDLE_VALUE) { COORD largestConsoleWindowSize = GetLargestConsoleWindowSize(handle); largestConsoleWindowSize.X -= 3; largestConsoleWindowSize.Y = 5000; SetConsoleScreenBufferSize(handle, largestConsoleWindowSize); } handle = GetStdHandle(STD_INPUT_HANDLE); if (handle != INVALID_HANDLE_VALUE) SetConsoleMode(handle, ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS); # ifndef Q_CC_MINGW HMENU systemMenu = GetSystemMenu(GetConsoleWindow(), FALSE); if (systemMenu != NULL) RemoveMenu(systemMenu, SC_CLOSE, MF_BYCOMMAND); DrawMenuBar(GetConsoleWindow()); # endif } } if (!isCoutRedirected) { m_oldCout = std::cout.rdbuf(); m_newCout.open("CONOUT$"); std::cout.rdbuf(m_newCout.rdbuf()); } if (!isCerrRedirected) { m_oldCerr = std::cerr.rdbuf(); m_newCerr.open("CONOUT$"); std::cerr.rdbuf(m_newCerr.rdbuf()); } } Console::~Console() { if (!parentConsole) { system("PAUSE"); } else { // simulate enter key to switch to boot prompt PostMessage(GetConsoleWindow(), WM_KEYDOWN, 0x0D, 0); } if (m_oldCerr) std::cerr.rdbuf(m_oldCerr); if (m_oldCout) std::cout.rdbuf(m_oldCout); if (m_oldCout) FreeConsole(); } ���������������������������������src/sdk/constants.h���������������������������������������������������������������������������������0000664�0000000�0000000�00000005004�13253666515�0014626�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef CONSTANTS_H #define CONSTANTS_H namespace CommandLineOptions { const char HelpShort[] = "h"; const char HelpLong[] = "help"; const char Version[] = "version"; const char FrameworkVersion[] = "framework-version"; const char VerboseShort[] = "v"; const char VerboseLong[] = "verbose"; const char Proxy[] = "proxy"; const char NoProxy[] = "no-proxy"; const char Script[] = "script"; const char CheckUpdates[] = "checkupdates"; const char Updater[] = "updater"; const char ManagePackages[] = "manage-packages"; const char NoForceInstallation[] = "no-force-installations"; const char ShowVirtualComponents[] = "show-virtual-components"; const char LoggingRules[] = "logging-rules"; const char CreateLocalRepository[] = "create-local-repository"; const char AddRepository[] = "addRepository"; const char AddTmpRepository[] = "addTempRepository"; const char SetTmpRepository[] = "setTempRepository"; const char StartServer[] = "startserver"; const char StartClient[] = "startclient"; const char InstallCompressedRepository[] = "installCompressedRepository"; const char SilentUpdate[] = "silentUpdate"; const char Platform[] = "platform"; const char SquishPort[] = "squish-port"; } // namespace CommandLineOptions #endif // CONSTANTS_H ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/installerbase.cpp���������������������������������������������������������������������������0000664�0000000�0000000�00000040437�13253666515�0016006�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "constants.h" #include "commandlineparser.h" #include "installerbase.h" #include "installerbasecommons.h" #include "tabcontroller.h" #include <binaryformatenginehandler.h> #include <copydirectoryoperation.h> #include <errors.h> #include <init.h> #include <updateoperations.h> #include <messageboxhandler.h> #include <packagemanagercore.h> #include <packagemanagerproxyfactory.h> #include <qprocesswrapper.h> #include <protocol.h> #include <productkeycheck.h> #include <settings.h> #include <utils.h> #include <globals.h> #include <runoncechecker.h> #include <filedownloaderfactory.h> #include <QDir> #include <QDirIterator> #include <QFontDatabase> #include <QTemporaryFile> #include <QTranslator> #include <QUuid> #include <QLoggingCategory> #ifdef ENABLE_SQUISH #include <qtbuiltinhook.h> #endif InstallerBase::InstallerBase(int &argc, char *argv[]) : SDKApp<QApplication>(argc, argv) , m_core(0) { QInstaller::init(); // register custom operations } InstallerBase::~InstallerBase() { delete m_core; } int InstallerBase::run() { RunOnceChecker runCheck(QDir::tempPath() + QLatin1Char('/') + qApp->applicationName() + QLatin1String("1234865.lock")); if (runCheck.isRunning(RunOnceChecker::ConditionFlag::Lockfile)) { // It is possible that two installers with the same name get executed // concurrently and thus try to access the same lock file. This causes // a warning to be shown (when verbose output is enabled) but let's // just silently ignore the fact that we could not create the lock file // and check the running processes. if (runCheck.isRunning(RunOnceChecker::ConditionFlag::ProcessList)) { QInstaller::MessageBoxHandler::information(0, QLatin1String("AlreadyRunning"), tr("Waiting for %1").arg(qAppName()), tr("Another %1 instance is already running. Wait " "until it finishes, close it, or restart your system.").arg(qAppName())); return EXIT_FAILURE; } } QString fileName = datFile(binaryFile()); quint64 cookie = QInstaller::BinaryContent::MagicCookieDat; if (fileName.isEmpty()) { fileName = binaryFile(); cookie = QInstaller::BinaryContent::MagicCookie; } QFile binary(fileName); QInstaller::openForRead(&binary); qint64 magicMarker; QInstaller::ResourceCollectionManager manager; QList<QInstaller::OperationBlob> oldOperations; QInstaller::BinaryContent::readBinaryContent(&binary, &oldOperations, &manager, &magicMarker, cookie); // Usually resources simply get mapped into memory and therefore the file does not need to be // kept open during application runtime. Though in case of offline installers we need to access // the appended binary content (packages etc.), so we close only in maintenance mode. if (magicMarker != QInstaller::BinaryContent::MagicInstallerMarker) binary.close(); CommandLineParser parser; parser.parse(arguments()); QString loggingRules(QLatin1String("ifw.* = false")); // disable all by default if (QInstaller::isVerbose()) { loggingRules = QString(); // enable all in verbose mode if (parser.isSet(QLatin1String(CommandLineOptions::LoggingRules))) { loggingRules = parser.value(QLatin1String(CommandLineOptions::LoggingRules)) .split(QLatin1Char(','), QString::SkipEmptyParts) .join(QLatin1Char('\n')); // take rules from command line } } QLoggingCategory::setFilterRules(loggingRules); qCDebug(QInstaller::lcTranslations) << "Language:" << QLocale().uiLanguages() .value(0, QLatin1String("No UI language set")).toUtf8().constData(); qDebug().noquote() << "Arguments:" << arguments().join(QLatin1String(", ")); SDKApp::registerMetaResources(manager.collectionByName("QResources")); if (parser.isSet(QLatin1String(CommandLineOptions::StartClient))) { const QStringList arguments = parser.value(QLatin1String(CommandLineOptions::StartClient)) .split(QLatin1Char(','), QString::SkipEmptyParts); m_core = new QInstaller::PackageManagerCore( magicMarker, oldOperations, arguments.value(0, QLatin1String(QInstaller::Protocol::DefaultSocket)), arguments.value(1, QLatin1String(QInstaller::Protocol::DefaultAuthorizationKey)), QInstaller::Protocol::Mode::Debug); } else { m_core = new QInstaller::PackageManagerCore(magicMarker, oldOperations, QUuid::createUuid().toString(), QUuid::createUuid().toString()); } { using namespace QInstaller; ProductKeyCheck::instance()->init(m_core); ProductKeyCheck::instance()->addPackagesFromXml(QLatin1String(":/metadata/Updates.xml")); BinaryFormatEngineHandler::instance()->registerResources(manager.collections()); } dumpResourceTree(); QString controlScript; if (parser.isSet(QLatin1String(CommandLineOptions::Script))) { controlScript = parser.value(QLatin1String(CommandLineOptions::Script)); if (!QFileInfo(controlScript).exists()) throw QInstaller::Error(QLatin1String("Script file does not exist.")); } else if (!m_core->settings().controlScript().isEmpty()) { controlScript = QLatin1String(":/metadata/installer-config/") + m_core->settings().controlScript(); } // From Qt5.8 onwards a separate command line option --proxy is not needed as system // proxy is used by default. If Qt is built with QT_USE_SYSTEM_PROXIES false // then system proxies are not used by default. if ((parser.isSet(QLatin1String(CommandLineOptions::Proxy)) #if QT_VERSION > 0x050800 || QNetworkProxyFactory::usesSystemConfiguration() #endif ) && !parser.isSet(QLatin1String(CommandLineOptions::NoProxy))) { m_core->settings().setProxyType(QInstaller::Settings::SystemProxy); KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); } if (parser.isSet(QLatin1String(CommandLineOptions::ShowVirtualComponents))) { QFont f; f.setItalic(true); QInstaller::PackageManagerCore::setVirtualComponentsFont(f); QInstaller::PackageManagerCore::setVirtualComponentsVisible(true); } if (parser.isSet(QLatin1String(CommandLineOptions::Updater))) { if (m_core->isInstaller()) throw QInstaller::Error(QLatin1String("Cannot start installer binary as updater.")); m_core->setUpdater(); } if (parser.isSet(QLatin1String(CommandLineOptions::ManagePackages))) { if (m_core->isInstaller()) throw QInstaller::Error(QLatin1String("Cannot start installer binary as package manager.")); m_core->setPackageManager(); } if (parser.isSet(QLatin1String(CommandLineOptions::AddRepository))) { const QStringList repoList = repositories(parser .value(QLatin1String(CommandLineOptions::AddRepository))); if (repoList.isEmpty()) throw QInstaller::Error(QLatin1String("Empty repository list for option 'addRepository'.")); m_core->addUserRepositories(repoList); } if (parser.isSet(QLatin1String(CommandLineOptions::AddTmpRepository))) { const QStringList repoList = repositories(parser .value(QLatin1String(CommandLineOptions::AddTmpRepository))); if (repoList.isEmpty()) throw QInstaller::Error(QLatin1String("Empty repository list for option 'addTempRepository'.")); m_core->setTemporaryRepositories(repoList, false); } if (parser.isSet(QLatin1String(CommandLineOptions::SetTmpRepository))) { const QStringList repoList = repositories(parser .value(QLatin1String(CommandLineOptions::SetTmpRepository))); if (repoList.isEmpty()) throw QInstaller::Error(QLatin1String("Empty repository list for option 'setTempRepository'.")); m_core->setTemporaryRepositories(repoList, true); } if (parser.isSet(QLatin1String(CommandLineOptions::InstallCompressedRepository))) { const QStringList repoList = repositories(parser .value(QLatin1String(CommandLineOptions::InstallCompressedRepository))); if (repoList.isEmpty()) throw QInstaller::Error(QLatin1String("Empty repository list for option 'installCompressedRepository'.")); foreach (QString repository, repoList) { if (!QFileInfo::exists(repository)) { qDebug() << "The file " << repository << "does not exist."; return EXIT_FAILURE; } } m_core->setTemporaryRepositories(repoList, false, true); } QInstaller::PackageManagerCore::setNoForceInstallation(parser .isSet(QLatin1String(CommandLineOptions::NoForceInstallation))); QInstaller::PackageManagerCore::setCreateLocalRepositoryFromBinary(parser .isSet(QLatin1String(CommandLineOptions::CreateLocalRepository)) || m_core->settings().createLocalRepository()); QHash<QString, QString> params; const QStringList positionalArguments = parser.positionalArguments(); foreach (const QString &argument, positionalArguments) { if (argument.contains(QLatin1Char('='))) { const QString name = argument.section(QLatin1Char('='), 0, 0); const QString value = argument.section(QLatin1Char('='), 1, 1); params.insert(name, value); m_core->setValue(name, value); } } const QString directory = QLatin1String(":/translations"); const QStringList translations = m_core->settings().translations(); if (translations.isEmpty()) { foreach (const QLocale locale, QLocale().uiLanguages()) { QScopedPointer<QTranslator> qtTranslator(new QTranslator(QCoreApplication::instance())); const bool qtLoaded = qtTranslator->load(locale, QLatin1String("qt"), QLatin1String("_"), directory); if (qtLoaded || locale.language() == QLocale::English) { if (qtLoaded) QCoreApplication::instance()->installTranslator(qtTranslator.take()); QScopedPointer<QTranslator> ifwTranslator(new QTranslator(QCoreApplication::instance())); if (ifwTranslator->load(locale, QLatin1String("ifw"), QLatin1String("_"), directory)) QCoreApplication::instance()->installTranslator(ifwTranslator.take()); // To stop loading other translations it's sufficient that // qt was loaded successfully or we hit English as system language break; } } } else { foreach (const QString &translation, translations) { QScopedPointer<QTranslator> translator(new QTranslator(QCoreApplication::instance())); if (translator->load(translation, QLatin1String(":/translations"))) QCoreApplication::instance()->installTranslator(translator.take()); } } { QDirIterator fontIt(QStringLiteral(":/fonts")); while (fontIt.hasNext()) { const QString path = fontIt.next(); qCDebug(QInstaller::lcResources) << "Registering custom font" << path; if (QFontDatabase::addApplicationFont(path) == -1) qWarning() << "Failed to register font!"; } } //Do not show gui with --silentUpdate, instead update components silently if (parser.isSet(QLatin1String(CommandLineOptions::SilentUpdate))) { if (m_core->isInstaller()) throw QInstaller::Error(QLatin1String("Cannot start installer binary as updater.")); const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance(); if (!productKeyCheck->hasValidLicense()) throw QInstaller::Error(QLatin1String("Silent update not allowed.")); m_core->setUpdater(); m_core->updateComponentsSilently(); } else { //create the wizard GUI TabController controller(0); controller.setManager(m_core); controller.setManagerParams(params); controller.setControlScript(controlScript); if (m_core->isInstaller()) { controller.setGui(new InstallerGui(m_core)); } else { controller.setGui(new MaintenanceGui(m_core)); //Start listening to setValue changes that newly installed components might have connect(m_core, &QInstaller::PackageManagerCore::valueChanged, &controller, &TabController::updateManagerParams); } QInstaller::PackageManagerCore::Status status = QInstaller::PackageManagerCore::Status(controller.init()); if (status != QInstaller::PackageManagerCore::Success) return status; #ifdef ENABLE_SQUISH int squishPort = 11233; if (parser.isSet(QLatin1String(CommandLineOptions::SquishPort))) { squishPort = parser.value(QLatin1String(CommandLineOptions::SquishPort)).toInt(); } if (squishPort != 0) { if (Squish::allowAttaching(squishPort)) qDebug() << "Attaching to squish port " << squishPort << " succeeded"; else qDebug() << "Attaching to squish failed."; } else { qWarning() << "Invalid squish port number: " << squishPort; } #endif const int result = QCoreApplication::instance()->exec(); if (result != 0) return result; if (m_core->finishedWithSuccess()) return QInstaller::PackageManagerCore::Success; status = m_core->status(); switch (status) { case QInstaller::PackageManagerCore::Success: return status; case QInstaller::PackageManagerCore::Canceled: return status; default: break; } return QInstaller::PackageManagerCore::Failure; } return QInstaller::PackageManagerCore::Success; } // -- private void InstallerBase::dumpResourceTree() const { qCDebug(QInstaller::lcResources) << "Resource tree:"; QDirIterator it(QLatin1String(":/"), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden, QDirIterator::Subdirectories); while (it.hasNext()) { if (it.next().startsWith(QLatin1String(":/qt-project.org"))) continue; qCDebug(QInstaller::lcResources) << " " << it.filePath().toUtf8().constData(); } } QStringList InstallerBase::repositories(const QString &list) const { const QStringList items = list.split(QLatin1Char(','), QString::SkipEmptyParts); foreach (const QString &item, items) qDebug().noquote() << "Adding custom repository:" << item; return items; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/installerbase.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000003435�13253666515�0015450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef INSTALLERBASE_H #define INSTALLERBASE_H #include "sdkapp.h" namespace QInstaller { class PackageManagerCore; } class InstallerBase : public SDKApp<QApplication> { Q_OBJECT Q_DISABLE_COPY(InstallerBase) public: InstallerBase(int &argc, char *argv[]); ~InstallerBase(); int run(); private: void dumpResourceTree() const; QStringList repositories(const QString &list) const; private: QInstaller::PackageManagerCore *m_core; }; #endif // INSTALLERBASE_H �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/installerbase.ico���������������������������������������������������������������������������0000664�0000000�0000000�00000061176�13253666515�0016001�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �00���h��–��� ���č��þ�����(��æ ��00����Ļ�� �� ����Ļ��ķ������h��^"��00��� �Ļ%��Æ'�� ��� �Ļ��nM����� �h��^��(���0���`��������������������������������������€���€€�€���€�€�€€��€€€�ĀĀĀ���ĸ��ĸ���ĸĸ�ĸ�������ĸĸ��ĸĸĸ���ww�����xx‡wwp�www�ˆĸø‡ww��xˆˆˆw�xˆĸĸĸĸ‡wwččøøøˆwˆˆĸĸĸĸøˆ‡~ŽŽĸøpˆˆĸĸĸĸˆˆxččččøĸĸĸ‡ˆˆĸwĸˆ‡xŪŽŽĸĸøpˆˆĸ""'xwŠĻčččøĸĸĸĸ‡ˆˆĸ""""zŠŠŽŽŽĸĸĸũˆˆĸ"""'zŠŠĻččøĸĸĸĸøpˆˆĸ"""'ŠŠŠŠŽŽĸĸĸĸĸpˆˆĸ**"'ˆŠŠŠĻĸĸĸĸĸĸĸ‡ˆˆĸĒĒ"gˆˆˆŠĸĸĸĸĸĸø‡ˆˆĸ**"xˆˆˆˆĸˆˆĸĸ‡ˆˆĸĒĒ"xøøøøp�ĸøøøũˆˆĸ**"xøøĸ‡ˆˆ~þĒĒĢxøøøĸøˆĸøøøũˆˆï*Šđxø‡ĸ‡ˆˆ~þŦ››xøøøĸĸp�xøččøũˆˆïđđđwĸĸĸøˆþŽŽ‡ˆˆ~þ›››wĸĸĸĸĸĸĸĻčččįˆˆïéđđ·ĸĸĸĸĸĸøŠŽŽŽ‡ˆˆ~þþþþwĸĸĸĸøøˆŠĻččpˆˆïïïïîxĸĸĸĸˆŠŠŽŽpˆˆ~îîîîîįĸĸĸøøøøˆŠĻįˆˆ~îîîîîįĸĸˆˆŠŠ‡ˆˆ~îîîîîîxĸĸøøøøˆŠŠpˆˆ~îînîîîįĸˆˆ§ˆˆ~ææææææîwøøøøøøˆpˆˆ~nnnnnnngxˆ€ˆˆvæ��æææææwwˆˆˆ�ˆˆ~gĸø���nnngwwpˆˆxĸĸĸĸø���æææpˆˆwĸĸĸĸĸĸĸĸø������ˆ‡‡wwwxĸĸĸĸĸĸĸĸø‡wwwwwwwwˆĸĸĸĸĸ€ũwwwwwwwwwxˆˆ‡xxĸũĸĸˆwwwwwwx‡wpĸũĸĸĸĸø‡wwwwĸĸ€ĸĸĸĸĸĸĸĸĸĸøĸĸĸ‡ĸøĸĸĸĸĸĸĸĸøĸĸĸũĸwĸĸĸĸĸĸĸĸĸøxĸĸĸøpwˆĸĸĸĸĸĸĸĸøxĸĸĸĸpwwwˆĸĸĸĸĸĸĸøwĸĸˆ‡wwwxĸĸĸøqxˆˆwqwwwwwwwĸĸĸĸĸ��þ�øĸ��ü�āĸ��ø��€�ĸ��ð������ā����?��ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā����?��ā������ā���ĸ��ā���ĸ��ā���ĸ��ā����ĸ��ā���ĸ��ā���ĸ��ā���ĸ��Ā���ĸ��Ā���ĸ��€���ĸ��€���ĸ��€���ĸ������ĸ��0���ĸ��ĸ��ĸ��ĸþ�8?ĸ��ĸĸĸĸĸĸ��(��� ���@��������������������������������������€���€€�€���€�€�€€��€€€�ĀĀĀ���ĸ��ĸ���ĸĸ�ĸ�������ĸĸ��ĸĸĸ��wwp����‡ø‡wp��xˆw�x‡ĸĸĸ‡wxˆøø‡ˆ‡ĸĸĸˆxŽĸpˆ‡ø'ˆˆxččøøĸũˆ‡ø""'ŠŽŽĸøpˆ‡ø""jŠĻččĸĸĸ€ˆ‡ø""xŠŠŽĸĸĸĸũˆ‡ø*"xˆŠĸˆĸĸøũˆ‡øĒĒxˆˆø�ĸˆˆ‡Ž*#xˆðˆøøøˆ‡čĒđxøøðˆþˆˆ‡č››ĸ�ččøˆ‡ŽđđxøøĸøúŽŽˆˆ‡î››xĸĸŠĻčįˆ‡îîé·øĸĸˆŠŽ€ˆ‡îîîîĸĸøøŠŠpˆ‡ææîîxĸĸˆŠĻˆ‡nnnnįĸøøˆ ˆ‡ā�æææw€ˆ‡ĸ��nnwwwˆ‡ĸĸĸĸ��ææįpˆwwwĸĸĸ����wũwwwwxĸˆˆũwwwwwxˆ‡pĸũĸĸˆ‡wx‡€ĸĸĸĸĸøxĸũwĸĸĸĸĸĸũĸĸũwĸĸĸĸĸĸĸũĸũwwwwĸĸĸũø‡wwwqwqøĸĸðĀĸā��?Ā��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��€��?€��?€��€��ĸ€��ĸ���������€��ĸĀaĸ(������ ��������������������������������������€���€€�€���€�€�€€��€€€�ĀĀĀ���ĸ��ĸ���ĸĸ�ĸ�������ĸĸ��ĸĸĸ���wpwpĸ‡ˆ‡"xïøpĒzŽĸpŠxĻ�ˆ€đxˆp›úþpŽxĸøĻčįĸø€ŽŽwwĸĸˆ€wwwwpxũˆˆˆðĸĸøwwwwwwᇇøĀxŠ€ĸĸ€�ũ€�‡ø€�xˆ€�ˆĸ€�ũ€‡ø€xˆ€�€ˆ€‡Ž�xˆ�ˆ�ø(���0���`�����������������������������zf‚�����ĸðĸ�ĸúĸ�ĸýĸ�ĸÞý�ĸËõ�Œ‡ˆ�ĸÅÎ�ĸžÅ�ĸĐģ�ýĖŌ�ĸģđ�ĸäæ�ĸģķ�˜op�ôšŧ�ĸ™™�ĸ››�ō““�ĸžž�ĸĄ �ų�Ė‚‚�ĸĪĪ�Ũ‹Š�ĸ§§�ųĨĨ�ĸŠŠ�晙�™ff�ĸ­­�ōĪĪ�•ee�ųŠŠ�‰^^�ĸ°°�Īqq�ĸģģ�yUU�ĸĩĩ�Žzz�Ÿqq�ĩ‚‚�ĸđđ�ōŊŊ�ĸšš�ŒŒ�Ķyy�üđđ�“mm�ĸūū�ĸĀĀ�ĸÅÅ�ĸĮĮ�ZFF�·�ĸËË�ĸĖĖ�ĸÐÐ�ōÅÅ�ĸŌŌ�ĘĶĶ�ū�Ŋ’’�I==�ĸÖÖ�xee�čÄÄ�ĸŲŲ�ĸÝÝ�Ō··�ĸââ�ndd�ĸéé�ŲÆÆ�úįį�ĸîî�ôãã�ÞÐÐ�æŲŲ�âÕÕ�ĸõõ�ĸųų�ĸüü�JII�įåå�VUU�üûû�ũöö�ŌŅŅ�Ÿlk�Ŧut�ԝ›�Į“’�ųÁĀ�ųÆÅ�ųËĘ�óÓŌ�Ģol�ĩ|�ŧ†ƒ�‰fd�­ƒ�ÚŠĻ�æ―ŧ�öÏÍ�îŨÖ�Ōŋū�į°Ž�Îē°�™kf�ĸĖĮ�îäã�Ęķģ�ôíė�ĸĖÂ�đŦĻ�ęÜŲ�ð―­�Ŧub�ĸĖŧ�ôÁŽ�ߎ”�Žy`�ĸĖģ�ĮÃÁ�æäã�ĩ~^�Ė’p�ĸš‘�ĸĖŽ�ÉŠ˜�åŋĶ�ģĨœ�đķī�íķ�ĸř�ėÄĶ�ØđĄ�ũ—�ĸĖĪ�Ėēœ�ĸə�…~x�ëāÖ�ĸʔ�ĸĖ™�ĸ͞�þÓ§�ĸÚĩ�æĖģ�ĸęÕ�ĸðá�îâÖ�ĸųó�öņė�ėčä�Ũ—S�ĸƇ�ĸāĀ�þäÉ�Š�ĸõę�Üx�ĸži�ĸūp�ō·n�æŽi�ĸĀu�ĸÂ{�ę·y�üˏ�áÁ›�ôâĖ�äÞŨ�éäÞ�üēM�þķY�ĸŧd�ûŅ™�įÐē�íÞĘ�ÕÉđ�ų•�ĸĒ�ûŠ4�ōŋu�ũÄx�ûČ|�öϖ�ë˛�ėÓŪ�õÛķ�ĸýú�ëđm�þøî�óíã�―žš�ÃŪ{�ŲØÕ�juH�ēV�ŋÝŪ�M€3�XēF�€Æt�&Œ�š�Ļ�°#�#·0�(Á<�5ÎQ�Hám�[ņ�§ßá�RÍÔ� Đ�IŪü�ŠąŨ�5•ų�Llš�ĸĸĸ�ųųų�ōōō�îîî�ëëë�ééé�æææ�äää�âââ�āāā�ÞÞÞ�ÜÜÜ�ÚÚÚ�ÖÖÖ�ĖĖĖ�ÆÆÆ�ÁÁÁ�ūūū�ŧŧŧ�đđđ�ĩĩĩ�ŊŊŊ�ĒĒĒ�”””�}}}�rrr�ggg�ddd�]]]�===�666�000�����ĸĸĸĸĸĸĸĸĸWúúĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúûWUüþüúúúĸĸĸĸĸĸĸĸĸĸĸĸøøúúúøøĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúũëZóúWUüýþWúúĸĸĸĸĸĸW7'''''7AþUĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúöíVÂÂÂÂČÆöũûWUýþüøĸC#[/>iiD>8!#7þWĸĸĸĸĸĸĸĸĸĸĸĸųôZZSSSSSSSS~ǐųWU'%]469=EFHJNl0#AþĸĸĸĸĸĸĸĸĸĸîZíí›››››››››˜““”Į!d"(.469=EFHJMTr7þĸĸĸĸĸĸĸĸĸõėņõï°ĢĢĢĢĢĢĢĢĢ”ˆ[$(.469=EFHJßßq*7þĸĸĸĸĸĸĸĸõóõîZ°ĢĢĢÍÎËœĢĄŒc$(.469=EFMßßßq*7üĸĸĸĸĸĸĸõííôņ°™™™ÐÐÐÐÓÍĘĮ\$(.469=ETßßßßP!AWĸĸĸĸĸĸõïöóZ‘™™™ŅŅŅŅŅÐÐÐ $(.469Fßßßßßßr#þĸĸĸĸĸĸõîZëČŊ˜˜˜ŌŌŌŌŅÐÐÉe&$(.46Jßßßßßßß07UĸĸĸĸĸõęęęëŊ˜˜˜ÓÓÓÓŅÐÐfm,& .5ßßßßßßßßK#þĸĸĸĸĸôéęęęŊ˜˜˜ÔÔÔÔŌÐÏ%53,&  ßßßßßßßß0AũĸĸĸĸôčéęęŊĄĄĄÕÕÕÕŌÐĖ/953,& ßßßßßßß>'UĸĸĸĸôįčéęŊĄĄĄÕÕÕÕÓÐÉ]:953,&3 ßXYYYYO#þĸĸĸĸôįįčéŊ   ÖÖÖÖÓÐÉi=:953,&H d*gKYsssssssþũĸĸĸôæįįčŊ   ÖÖÖÖÔÐÉi=;:953,MØ „*sœqqqqqqq)Aũĸĸĸôåæįį‘–––ŨŨŨŨÕÚ�m:::::::Rmؖ–ksvvvvvvvgAũĸĸĸôååæį‘–––ŨŨŨŨŲÝ�]:::::::M *ؖŠB$1_`ajbgAũĸĸĸôåååæ‘–––ŨŲÛÛÛÝÞ^::::::;MK[d[ 6$(.469+Aũĸĸĸóäåå呕••ÝÛÛÛÛÝÞ)::::EMTß</ R$(.46%Aĸĸĸĸóãäå呕••ÝÝÛÛÛÝÞ`=JSßßßß3$(.WĸĸĸĸóãķšĀ‘ÜÛÝÛÛÝÝ�Gßßßßßßßß $(]#ĸĸĸĸó•ļļš””””””·ðÜÞÞ0ßßßßßßßßßB &$e'ĸĸĸĸĸōžļļĩš“““““““““‰cKßßßßßßßR:953,&ĸĸĸĸĸōŧļĪĩš’’’’’’’’’’‚*Yßßßßßß =:953,&'ĸĸĸĸĸĸņŦĪĪĩšŸŸŸŸŸŸŸŸŸŸŸ‚\8ßßßßßTFB=:953,&[ĸĸĸĸĸĸņŦĪĪ—ŪŠŠŠŠŠŠŠŠŠŠŠŠˆc?ßßßßRHEB=:953,&\fĸĸĸĸĸĸĸņŦĪ­âŪĶĶĶĶĶĶĶĶĶĶĶĶĶĻo?ßßßM HEB=:953,&"\fĸĸĸĸĸĸĸĸðķ°îėŪģģģģģģģģģģģģģģĻxgsTMJ HEB=:953-\2ĸĸĸĸĸĸĸĸĸðZõôåŪēēļđđššąēēēēēēēē€nNMJ HEB=:`^[@ĸĸĸĸĸĸĸĸĸĸïëëîîŪąđīÅÁūžšđļđšššąąąž|8nDDbih/[gĸĸĸĸĸĸĸĸĸĸĸĸïėõôæŪš―YYááââããĀŋŧąšļđ𹹞€x!!ffĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸïęįāYĄģāāYYááââããäåæįčĩŋÃąšļđđđøüúúĸĸĸĸĸĸĸĸĸĸĸĸĸîXāāāķÄXāāYYááââããäåæįčééęëė·­ÃõúWUøĸĸĸĸĸĸĸĸĸĸĸîXXãė?[8?GOQYááââããäåæįčééęëėėėZZíôöĸĸĸĸĸĸĸĸĸĸĸĸîáęėė0c%%\\))8?GlQVãäåæįčééęëėėėZôôĸĸĸĸĸĸĸĸĸĸĸĸĸîęęęlc%%\\))dd++ee//>nléįčééęëėZõöĸĸĸĸĸĸĸĸĸĸĸĸĸņįįįሁed))dd++ee///^^^OäáYYáâöýĸĸĸĸĸĸĸĸĸĸĸĸĸĸōäää°ģĨП’“Œˆ{{]^///^^^^QáāßSMJJôýĸĸĸĸĸĸĸĸĸĸĸĸĸðæâââÁĨП’“””ƒƒƒ}zwmmhvLHFFHJMZûUĸĸĸĸĸĸĸĸĸĸĸĸņYYYYŽĐŸ’“””ƒƒƒ}}}yyyti=BF JMRSþĸĸĸĸĸĸĸĸĸĸĸĸðX› īП’“””ƒƒƒ}}}yyytt†DF JMRSßíUøĸĸĸĸĸĸĸĸĸĸððōēĨП’“””ƒƒƒ}}}yyyttpøu JMRTßßáøþĸĸĸĸĸĸĸĸĸĸððô§ĐŸ’“””ƒƒƒ}}}yyyttpDûîOMRTßßßßōûúĸĸĸĸĸĸĸĸĸĸĸððōôr††ŽŽ‹……ƒ}}}yyyttppGWĸôRTßßåZðōĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸððððððððð‡luunniiDuøĸĸðîîîîĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸððððððððððĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ��þ�øĸ��ü�āĸ��ø��€�ĸ��ð������ā����?��ā������ā������ā������ā������ā������ā������ā������ā������ā������ā�������ā�������ā�������ā�������ā�������ā������ā������ā������ā������ā������ā������ā������ā������ā������ā����?��ā������ā���ĸ��ā���ĸ��ā���ĸ��ā����ĸ��ā���ĸ��ā���ĸ��ā���ĸ��Ā���ĸ��Ā���ĸ��€���ĸ��€���ĸ��€���ĸ������ĸ������ĸ��Ā��ĸ��þ��?ĸ��ĸĸ�?ĸĸ��(��� ���@�����������������������������vfˆ�����ĸøĸ�ĸüĸ�ą°ą�þæý�ĸôþ�ĸÍņ�}Ž�öĩÎ�ýÄŲ�ØÃË�õōó�óāä�ĸôö�ė§°�þÓŲ��ݔ˜�ĸËÎ�ĸáã�š Ą�ĸœ�Į|~�ü§Ļ�ĸŠŦ�Ɔ‡�ĸÂÃ�ԕ�ĸÔÕ�ĸ™™�ĸž�ĸžž�ĸĄĄ�ĸ§Ķ�·zz�坝�ĸą°�ĸēē�ýēē�ĸķĩ�°~~�Ĩww�rRR�ĸšđ��—nn�úļ·�ķ……�ĸū―�ĸÆÆ�ëķķ�ÖĻĻ�F77�É  �ĸĖĖ�ĸÐÐ�ûĖĖ�|hh�ĸŨŨ�ĸÛÛ�ĮŦŦ��ûÚÚ�QGG�Íĩĩ�4//�ĸčč�ÚĮĮ�ōßß�-**�ĸïï�ÛÏÏ�äÛÛ�čßß�ÓËË�wss�ĸųų�ðęę�ßŲŲ�ƒ€€�ÕŅŅ�jhh�ûøø�ŅÎÎ�CBB�ĸýý�ýüü�ÞÝÝ�ŌŅŅ�ʖ•�ĸÄÃ�ĸËĘ�öĘÉ�Ԋ†�óÄÂ�øÖÔ�ņÐÎ�ēvq�ĸÎÉ�Õš·�ą|u�Μ•�Ĝ–�ýËÂ�óÁ·�ûũö�ū‘…�øÅ·�ðÅš�Õ·Ŋ�ÛĻ—�ĸÍŧ�ëÂģ�ĸųũ�đ{�§‹�―…i�ĸÍī�õïė�ëåâ�Ӟ‚�áĀŪ�ðéå�æķ•�âØŅ�ĸÎŦ�ėčå�ƊZ�öØ�ĸÍĨ�ŌÅš�Ȁ=�ĸïā�ęãÝ�ĸ͚�ĸ͝�óɟ�ōĖĶ�‡s_�Ë·Ģ�ņāÏ�éßÕ�íėë�Ýq��þÂ�ā­x�ĸɎ�þĖ–�ÛĮē�zqg�ûęŨ�þžj�âĻb�ĸĀv�ĸÄ{�ĸÂ{�ĸŌž�ųÞ―�ĸæČ�ó‡��ĸąH�ýļZ�ĸšd�é°b�öʓ�ũÖŽ�üÝķ�þöė�þĐ*�ė/�þŪ>�îŦI�ôđc�ôŋq�îĮŽ�åŅģ�―ī§�ōūl�ōʍ�ÆÂŧ�Ųě�ÚÏš�ŨÓË�ĖËČ�{{U�ĸĸú�įįæ�ŲáŽ�ŅßŪ�ÉÞ°�Îâš�A|,�°ŲĨ�/š�€Įx�<Ž5�™�Ŋ�đ-�(ĮA�5ÕU�Jčs� …B�Yũ“�ĨĐĻ�ĒĖĖ�üĸĸ�ęëë�ÛÜÜ�FĘŅ�äįč�íïð�ęėí�áäæ�JŽų�ˆķÜ�1ō�ĮĘÍ�ŧ―ŋ�øúü�õũų�áãå�ÚÛÜ�ŲÚÛ�ŨØŲ�éíō�öųý�ßáä�ÔÖŲ�ŲÞæ�îóü�ÉĖŅ�DUĢ�üüþ�ŲŲÚ�ĸĸĸ�ééé�âââ�ÚÚÚ�ŲŲŲ�ÔÔÔ�ŌŌŌ�ÐÐÐ�ÎÎÎ�ËËË�ÁÁÁ�···��sss�ppp�\\\�MMM�999�&&&�����ĸĸĸĸĸPüû.ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸPLûUBFUûúĸĸĸĸĸR@55BýûĸĸĸĸĸĸĸĸĸĸR͏ XöøRüýFþü:+.46*+5þRĸĸĸĸĸĸĸøÛņššššrŒt–+#-28<CE=+FUĸĸĸĸĸĸÛöũxĻĻĻrV…s^(,7<Cėx.BUĸĸĸĸóũũö†ĻŋÄÃÁĩŊy!&,[7VėN.FúĸĸĸóÛö†ĻūÅÅÅÅÂe !&,[\CėėėI+þĸĸĸóöá뎅ūÆĮÆÅĀ$(" !"%,[Mėėėė5ųĸĸóõîߎ—―ĮČĮÅđ,%" ėėėėėN+ýĸĸóõîŅŽŸ―ČÉĮÅt,%" ėėWWWtF>ĸYôŧXŽŸ―ÉĘČÅg\(1eedSwwwj=B>ĸōóŧ䎞žĘĘÉË67\8Ξ) †††{A5>ĸōYíލ§žĖĖŌŲ777\;4ÍĶe/_]`n5>ĸņōÐӍĶķŌŨŨŲ\\\;C6b2"&,\45>ĸņåԆƒŲŲŨŲ�hVė  &ZUĸĸņ·Ē­ˆŠÛØØŨéAėėėėė !'*Lĸĸåĩ Ī”‡””‡‰$) ėėėėC2(%" !$+ĸĸĸáĩ ™“““““““^ėėėV<8\,%" #Pĸĸĸáĩ’šœœœœœ›‘uAėėr;8\,%^:ĸĸĸĸá•|ðžĢ˜Ģ˜˜˜˜˜˜u=ęrC;8721^.ĸĸĸĸĸáōÛč§ĄĐŦŦŦŦŦĄĒĒ€0 C<930>ĸĸĸĸĸĸðYÛÚĶŦ—wĶģŪ­ŦĐĐŠ„uk0.>ĸĸĸĸĸĸĸĸðáî㧟ÏÜÝįįâŧŽ}°ŊēŽŠŠ‹BýüĸĸĸĸĸĸĸðíėÐAADINÝ ÔÔÕÓÖäææáķīøøĸĸĸĸĸĸĸïXîå*.**)6AD}IIîXïáåāöĸĸĸĸĸĸĸĸõXÖķuee*))))006OÕÔíŧÍüĸĸĸĸĸĸĸĸĸÛÔâĨĢ‘”||offZZZJjGGþĸĸĸĸĸĸĸĸļÐãw›‘”ˆˆ‚~~vvvlia<<GNüûĸĸĸĸĸĸĸļw§“‘”ˆˆ‚‚~~vvphh?CGMėøþĸĸĸĸĸĸĸļąĒ‘”ˆˆ‚~~vvpphc_ûHVėėXųĸĸĸĸĸĸĸĸļYƒdnzzqqmhcdøõNíðõôKĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸKKKKQKTĸĸKKKKĸĸĸĸĸĸĸĸĸøĸĸðĀā��Ā��Ā��€��€��€��€��€���€���€���€���€���€��€��€��€��€��€��€��€��€��?€��?€��€��ĸ€��ĸ���������€��ĸĀaĸ(������ �����������������������������éčð�§ĄÃ�íéî�����ĸóĸ�ëäę�äÕß�|~�ōūÜ�ÆĨļ�üŧÐ�æĮŅ�ĸóũ�ŲĖÐ�ĸōö�ņāä�ýÁË�ĸËŌ�ĸąš�ĸ·―�˧Š�ĸĪ�ĸÉĖ�ĸÛÝ�ėÚÛ�ũĐŦ�ĸģī�―†‡�ĸÉĘ�ŞŸ�íËĖ�ĸÝÞ�ĸŸž�ĸ  �ĸĄ �üŸŸ�ĸĢĒ�ĸ­Ž�ē||�ĸģģ�üķķ�ĸū―�ŧ�‚cc�`II�ˆii�ĸÆÆ�·‘‘�}}�N>>�ĸÎÎ�ÍĨĨ�tt�ûËË�oZZ�|ee�=22�;11�ĸŲŲ�ĸęę�ÕÃÃ�ÔÃÃ�þėė�þîî�ĸðð�ÔÏÏ�ĸûû�îęę�ėéé�^]]�ĸþþ�úËĘ�ĀĶĨ�ģƒ€�ĸÏÍ�Į°Ŋ�ÉŊ­�ÛĖË�Ɔ�ėÄĀ�čâá�į°Ĩ�ËĶž�ĩ€t�843�ėäâ�ĸÔĮ�ĸõō�qfb�øõô�ė·Ģ�šĢš�ĸÐŧ�įūŽ�ãÐĮ�øôō�ĶĪĢ�ŨÕÔ�ō― �Ō·�ĸÎą�Č­�ØÄļ�âķ˜�øÄ�ĸÎĐ�íåß�îÁ�þĖĄ�ՙ]�öɛ�ĸŌĶ�õŌŊ�éåá�BA@�üķh�ýŋ�ûĮŽ�㷈�ČŠ‰�ü͓�ÝÂĢ�ũ°P�ōŦQ�ĸđ^�ĸÂv�Ō§m�ô˖�ĸĀh�üŋh�úÄx�āąp�áđ€�öÚģ�ĸĮq�û˂�ØŅÆ�óÜģ�įÔī�óáū�æâŲ�ņåÉ�ųóá�ðëÓ�ĸýō�œšc�Ÿt�âōÖ�)Š!�!­%�� �ą,�1ÃB�Sæk�]āĩ�lmm�§ĻĻ�6ĶĘ�a­õ�ęņø�žŋÂ�ÕÖŨ�:–û�âäé�ąąē�ĸĸĸ�ÆÆÆ�ūūū�����“““�ŽŽŽ�ŒŒŒ�„„„�}}}�vvv�sss�nnn�bbb�WWW�PPP�KKK�:::�444�///�...�---�ĸĸĸ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ĻĻĻąģŊŊĻĻŊŪ°ŊĻĻĻĻĻŽ`ErT,-468đĻĻϜa“ŽkN.:><1ļĻĻĪA•”‘!%);Ĩ=9ŊĻĪP˜–’" ĨĨ7ķĻĪU‹™—e KY_H·ĻĪC‰š J[5G3ĩĻĪ…pžĒWB 'ŊĻ~{onyRĨ@$#+ĻĻwv}|€sc2(&ĻĻĻ ˆ†‡‚zmg]/0ĻĻĻĻĄj^�DqŒŠ„ƒXĻĻĻĻĻMfSI* ŸĢ›ĻĻĻĻMˆxthbZQO;?ēīĻĻĻMulid\VLFͧĻĻĻMMMMMMMMMMMMĻĻĻĻá‡õîĀ—―€Įŀ�,%€� €�ė€�ėė€�ýĸ€õ―€Įŀ,€��WW�F>(���0���`���� ����������������������������������������������������������K���p���m���X���?���+��������� ������������������������������������������� ������������������������ ����������������������������������������������������������������� ���&888’ZZZėHHHá666Ō·���œ���Ž���y���`���F���1���#���������������������������&���@���]���p���y���z���y���o���Y���?���&�������������������������������������������������������� ���3HHH·zzzĸŲŲŲĸÏÏÏĸģģģĸĸbbbûSSSî===Ú"""ŋ Ķ���”���€���g���Q���:���(���������(���S0 ™_@@ÍvNNä|SSë|RRė|RRėtMMįR66Ō(ļ���š������S���'��� �����������������������������������������LUUUŨ•••ĸÎÎÎĸæåäĸĸýúĸĸýúĸĸýúĸĸýúĸŲØÕĸ―žšĸ—–•ĸ{zzĸYYYóCCCß111ĖŊ���š���‹���t���gU99­Œ^^óœllĸŋ‹‹ĸÏĒĒĸæŧŧĸæŋŋĸæÃÃĸƧ§ĸķĸ–ffĸŠ\\ũF//Ņ���Ą���z���C�������������������������������������;;;UeeeįąąąĸŅŅŅĸÐÐÐĸįäâĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸĸúöĸėčäĸĮÃÁĸŠĸ…~xĸdddûOOOé?77Ô}SSęĶppĸؚšĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸããĸĸččĸõååĸŲūūĸĶxxĸ‚WWó&Ā���’���T������������������������������SSS)}}}óĮĮĮĸÓÓÓĸËËËĸĘĘĘĸčäáĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸøņĸĸįÐĸĸĖ™ĸĸĖ™ĸĸΟĸÆ ƒĸ–ffĸžĸųŦŦĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸããĸĸččĸĸîîĸĸûûĸĖģģĸ™ffĸF..Ó���˜���V��������������������������```HĄĄĄĸÖÖÖĸŧŧŧĸĸĀĀĀĸčäßĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸöėĸĸóįĸĸĖžĸĸʙĸĸʙĸė·ĸŸliĸŲ‰‰ĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸããĸĸééĸĸĸĸĸĸĸĸĸėââĸŸppĸF..Ó���—���S����������������������aaaHĢĢĢĸĩĩĩĸĄĄĄĸÆÆÆĸÓÓÓĸéãÝĸĸóčĸĸóčĸĸóčĸPĩIĸ€ÆtĸŋÝŪĸöņéĸĸóčĸĸãĘĸĸșĸĸșĸų–ĸĶrlĸĖ€€ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸÝÝĸĸîîĸĸĸĸĸĸĸĸĸĸĸĸĸėââĸŸppĸF..Ó���Ž���A��� ����������������bbbHĪĪĪĸÍÍÍĸĖĖĖĸ°°°ĸŧŧŧĸéâÛĸĸņãĸĸņãĸĸņãĸĸĸĸĸ4Ū1ĸ`ŊCĸēVĸŋŧsĸƐ|ĸģvvĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸŌŌĸĸØØĸĸûûĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸæŲŲĸ’aaû&Ā���y���'���������������cccHĨĨĨĸÂÂÂĸ™™™ĸĩĩĩĸŌŌŌĸęâÚĸĸïßĸĸïßĸĸïßĸĻĸĻĸĻĸĻĸ Ķĸ�™�ĸ�™�ĸ“ ĸ™ffĸōĪĪĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸĸÝÝĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĖģģĸ‚WWó���Ą���S���������������dddHĶĶĶĸĮĮĮĸŌŌŌĸŲŲŲĸØØØĸæßØĸĸėÚĸĸėÚĸĸėÚĸ°#ĸ°#ĸ°#ĸ°#ĸŠĸ�™�ĸ�™�ĸ`y@ĸŋ……ĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸëëĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĶyyĸF//Ņ������(�����������eeeH§§§ĸÜÜÜĸÜÜÜĸÛÛÛĸŲŲŲĸâÝŨĸĸęÕĸĸęÕĸĸęÕĸ đ0ĸ đ0ĸ đ0ĸ đ0ĸĐĸ�™�ĸ�™�ĸi`ĸė­­ĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸģđĸĸšÄĸĸššĸĸÄÄĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŲÆÆĸŠ\\ũ������F��� ��������eeeHĐĐĐĸÞÞÞĸÜÜÜĸÜÜÜĸÛÛÛĸãÝŨĸĸčŅĸĸčŅĸĸčŅĸ(Á=ĸ(Á=ĸ(Á=ĸ(Á=ĸ­ĸ�™�ĸ&ŒĸĶssĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸ―ÉĸĸÓôĸĸĖĸĸĸÏĸĸĸÖûĸĸßîĸĸþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĶyyĸ2""Â���j�����������fffHŠŠŠĸāāāĸÞÞÞĸÜÜÜĸÜÜÜĸäÝŨĸĸåĖĸĸåĖĸĸåĖĸ1ĘIĸ1ĘIĸ1ĘIĸ1ĘIĸą%ĸ�™�ĸM€3ĸŋŒŒĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸĖÓĸĸåĸĸĸŲĸĸĸÄíĸĸÅęĸĸÖúĸĸãĸĸĸðĸĸĸþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÆĐĐĸkGGä���…���$��������gggHŦŦŦĸáááĸāāāĸÞÞÞĸÜÜÜĸåÞŨĸĸãĮĸĸãĮĸĸãĮĸ;ÔYĸ;ÔYĸ;ÔYĸ;ÔYĸ·-ĸ�™�ĸcvFĸĖ››ĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ――ĸĸōĸĸĸāųĸĸĐģĸߍĸԊŠĸōšžĸĸėĸĸĸîĸĸĸúĸĸĸĸĸĸþþþĸüúúĸúũũĸųõõĸųõõĸųõõĸáŅŅĸŠ\\ũ���”���.��������iiiH­­­ĸâââĸáááĸāāāĸÞÞÞĸåÞÖĸĸáÃĸĸáÃĸĸáÃĸDÝfĸDÝfĸDÝfĸDÝfĸ"ŧ3ĸ�™�ĸssMĸæđđĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸááĸĸōĸĸĸšÄĸŋzzĸŸvuĸŊ…}ĸ™ffĸŲÆÆĸĸņĸĸĸúĸĸųöõĸôîėĸôíëĸôíëĸôíëĸôíëĸôíëĸôíëĸ™ffĸ������?��������jjjHŪŪŪĸäääĸâââĸáááĸāāāĸæÞÖĸĸßūĸĸßūĸĸßūĸLåsĸLåsĸLåsĸLåsĸ&ŋ:ĸ�™�ĸssMĸæļļĸĸŌŌĸĸÐÐĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸėėĸĸōĸĸ雛ĸ˜opĸĐäåĸĸßūĸÉŠ˜ĸŸppĸũíðĸĸúĸĸöņïĸïåãĸïåãĸïåãĸïåãĸïåãĸïåãĸïåãĸĐ~}ĸ5$$ļ���E��������kkkHŊŊŊĸåååĸäääĸâââĸáááĸčßÖĸĸÜđĸĸÜđĸĸÜđĸWðƒĸWðƒĸWðƒĸWðƒĸ/ÅQĸ Đĸ|i†ĸæģģĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸōōĸĸōĸĸ掎ĸ–€ƒĸĶęîĸĸÜđĸĸÜđĸ™ffĸîŨÖĸĸúĸĸôíėĸéÜØĸéÜØĸéÜØĸéÜØĸéÜØĸéÜØĸéÜØĸ­ƒƒĸ5$$ļ���E��������lllH°°°ĸæææĸåååĸäääĸâââĸéßÖĸĸÚĩĸĸÚĩĸĸÚĩĸ_øĸ_øĸ_øĸ[ęĒĸOÅÛĸ0ųĸvi†ĸԟŸĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸïïĸĸōĸĸųÏŌĸŸppĸ§ÎÐĸĸÚĩĸėÄĶĸ™ffĸĸģķĸĸúĸĸĸÕÕĸĸ°°ĸüđđĸųÁĀĸųÆÅĸųËĘĸöÏÍĸóÔŌĸŊ‚‚ĸ5$$ļ���E��������mmmHąąąĸįįįĸæææĸåååĸäääĸęāÖĸĸذĸĸذĸĸذĸ\čģĸUÕĖĸOūėĸMģĸĸMģĸĸ0ųĸ`mĶĸƓ“ĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸÏÏĸĸėėĸĸōĸĸĸðĸĸŲÆÆĸŸmkĸģƒyĸŸmkĸĖ€€ĸĸĘŌĸĸúĸĸĸĮĮĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĸËËĸ쁁ĸ6$$ķ���?��������mmmHēēēĸéééĸįįįĸæææĸåååĸëāÖĸĸÕŽĸĸÕŽĸĸÕŽĸ<Ēĸĸ@ĶĸĸEŦĸĸJ°ĸĸMģĸĸ2‘ųĸNnĩĸŽyyĸĸĖĖĸĸĖĖĸĸĖĖĸĸĖĖĸĸŲŲĸĸėėĸĸüüĸĸĸĸĸĸôĸĸĸåĸĸĸîĸĸōÅÅĸƋ‹ĸ◗ĸĸšÄĸĸîĸĸĸôųĸĸĪĪĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸĸĀĀĸĸĮĮĸĢrrĸ8&&Ū���.��������nnnHīīīĸęęęĸéééĸįįįĸæææĸëāÕĸĸÓ§ĸĸÓ§ĸĸÓ§ĸ9Ÿĸĸ<Ēĸĸ@ĶĸĸEŦĸĸJ°ĸĸ7—ųĸ0mŌĸ™ffĸųÆÆĸĸŌŌĸĸééĸĸųųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸýĸĸĸįĸĸĸŲĸĸĸÉõĸĸÆįĸĸÐïĸĸãĸĸĸîĸĸĸ―Āĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸĸššĸõššĸ™ffĸ������"��������oooHĩĩĩĸëëëĸíāÍĸų­:ĸėÓ­ĸėáÕĸĸŅĒĸĸŅĒĸĸŅĒĸŊÜĸ^Ļîĸ<Ēĸĸ@ĶĸĸEŦĸĸ<œųĸ#ißĸ{ayĸŌķķĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúĸĸĸßûĸĸĖĸĸĸÏĸĸĸŲĸĸĸÜõĸĸžÂĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸĸĩĩĸܟŸĸ…XXï���c�����������pppHķķķĸóÓĢĸĸ™�ĸũ‘�ĸëŅŪĸíáÕĸĸΞĸĸΞĸĸΞĸĸΞĸĸΞĸĸΞĸÏÄķĸđĀÂĸ’ģŅĸZ~ÎĸFVĶĸĶyyĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÕÕĸĸÆĖĸĸÃÏĸĸŋĮĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸĸŠŠĸĸ°°ĸŋƒƒĸhFFŌ���@��� ��������rrrHļļļĸũÄxĸũ‘�ĸíˆ�ĸéаĸîâÕĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸĖ™ĸĸřĸĸĶ™ĸĶnlĸŲÆÆĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸööĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĸĪĪĸųĨĨĸŸkkĸ4""���!�����������sssHđđđĸōŋxĸíˆ�ĸä�ĸčϰĸðâÕĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸʔĸĸŧ”ĸԌ†ĸŸppĸųõõĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸååĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸĸ™™ĸĸžžĸĖ……ĸ€UUå���C��� ������������tttHšššĸíŧxĸä�ĸÛu�ĸįÎēĸðâÓĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸĮ‰ĸĸļĸŽusĸ쌌ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸýýĸĸÞÞĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸĸĄĄĸĸ››ĸō““ĸŸjjĸ:&&€������������������uuuHŧŧŧĸëļyĸÛu�ĸŅk�ĸæĖģĸōâŅĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸųą‚ĸĢojĸŋŸŸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõõĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸĸ­­ĸĸ§§ĸųĸŽppĸqKKÂ���)�������������������vvvHžžžĸæģzĸÖ|ĸã―–ĸïïïĸóãÏĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸĸūpĸæ­mĸ™kfĸŋŸŸĸĸĸĸĸĸĸĸĸĸĸĸĸĸîîĸĸååĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸĸđđĸĸģģĸųĐĐĸŽrrĸ„XXÝ���2��� ��������������������vvvHūūūĸîÛĮĸëåÞĸÅÅÅĸÕÕÕĸóâÍĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸĸŧeĸæŦdĸŽveĸރƒĸōėėĸĸüüĸĸïïĸĸééĸĸååĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸĸËËĸĸÅÅĸĸūūĸōŊŊĸŽttĸ‰^^â,8��� �����������������������wwwHŋŋŋĸÓÓÓĸŸŸŸĸ°°°ĸįįįĸôâËĸĸ·ZĸĸĩTĸĸ ĸĸŸĸĸĢĸĸĐ.ĸĸŽ9ĸĸ°Dĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·Zĸĸ·ZĸõēZĸģaĸ™ffĸ˰°ĸōááĸĸïïĸĸééĸĸååĸĸááĸĸÛÛĸĸÖÖĸĸŌŌĸĸĖĖĸųÅÅĸĖ••ĸŸkkĸ~TTŪ����������������������������������xxxHĀĀĀĸÚÚÚĸŲŲŲĸĮĮĮĸÆÆÆĸõâÉĸĸģOĸĸĨ#ĸųŌ™ĸóíãĸõÛĩĸöϖĸöÃwĸúŪ;ĸýĢĸĸ ĸĸ ĸĸ§)ĸĸŠ2ĸĸ­<ĸĸģOĸĸģOĸĸģOĸߟSĸŽy`ĸ™ffĸđ‘‘ĸŌēēĸæČČĸæÄÄĸōŅŅĸæūūĸŲŽŽĸŋŽŽĸŸllĸ^^ÏnIIY��� �����������������������������������yyyHÁÁÁĸÕÕÕĸŸŸŸĸ°°°ĸäääĸöãČĸĸŽ8ĸûČ|ĸöööĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸîÔŪĸîΞĸōŋrĸö°Gĸų§*ĸĸœ ĸĸŸĸĸĨ$ĸĸĻ,ĸïĄ7ĸϏRĸķ}[ĸĐt_ĸ“ffĸ–ffĸŠccû~VVãpNN§N;;S������ ���������������������������������������{{{HÂÂÂĸÜÜÜĸáááĸøøøĸöööĸøãÆĸþ―^ĸúúúĸøøøĸöööĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸčččĸæææĸäääĸâââĸāāāĸåŅĩĸčȘĸíšnĸņąRĸõĐ6ĸĸœĸĸŸĸĸĢĸĸĨ%ĸoooî���Ķ���€���c���O���1��� �����������������������������������|||HÄÄÄĸûûûĸúúúĸųųųĸøøøĸëÞËĸþøîĸüüüĸúúúĸøøøĸöööĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸčččĸæææĸäääĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸÛÎŧĸßşĸé·lĸŸŸŸû‚‚‚îWWWŲCCCČ666Ĩ���O��������������������������������������{{{IÅÅÅĸüüüĸûûûĸëëëĸÕÕÕĸķĸŸllĸļ’’ĸŋœœĸÐķķĸßÏÏĸãÖÖĸõõõĸóóóĸņņņĸïïïĸíííĸëëëĸęęęĸčččĸæææĸäääĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸÕÕÕĸÔÔÔĸŌŌŌĸÐÐÐĸÎÎÎĸ°°°ĸxxxĖ---2��������������������������������������wwwLÆÆÆĸðððĸÛÛÛĸÖÖÖĸÖÖÖĸĶyyĸĄnnĸĢppĸĨrrĸĻuuĸŠwwĸŽyyĸŪ{{ĸđŒŒĸœœĸŌđđĸÖĀĀĸāÓÓĸčååĸęęęĸčččĸæææĸäääĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸÕÕÕĸÔÔÔĸŌŌŌĸŊŊŊĸggg™���������������������������������������������ssscÄÄÄĸÜÜÜĸÜÜÜĸÜÜÜĸÍĀĀĸĄnnĸĢppĸĨrrĸĻuuĸŠwwĸŽyyĸŪ{{ĸ°}}ĸēĸĸ탃ĸļ……ĸš‡‡ĸž‰‰ĸŋŒŒĸËĨĨĸŅąąĸÕŧŧĸāÝÝĸâââĸāāāĸÞÞÞĸÝÝÝĸÛÛÛĸŲŲŲĸŨŨŨĸŅŅŅĸŸŸŸųOOOt�������������������������������������������������ˆˆˆēâââĸâââĸâââĸâââĸⷊĸŅ“dĸĖ“pĸ―ˆwĸīxĸŽyyĸŪ{{ĸ°}}ĸēĸĸ탃ĸļ……ĸš‡‡ĸž‰‰ĸŋŒŒĸÁŽŽĸАĸđ‘ĸĮ””ĸÛÎÎĸéééĸņņņĸöööĸũũũĸðððĸíííĸåããĸũŸ���9�����������������������������������������������III ···ĸéééĸéééĸéééĸęåßĸýđaĸĸžiĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸõ˜ĸėđ—ĸ㰕ĸÚ§“ĸԟ’ĸ˘ĸĀ‹ĸŋŒŒĸÁŽŽĸАĸđ‘ĸĮ””ĸɖ–ĸâÕÕĸņņņĸųųųĸĸĸĸĸĸøøĸĸððĸĸęęĸĸįįĸŽĻĻĸ­���[����������������������������������������������xxxuäääĸïïïĸïïïĸïïïĸõÚ·ĸĸžiĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸôÁŽĸð―­ĸæģĐĸäąĐĸÛĻĪĸíÝÜĸúįįĸĸããĸĸÜÜĸĸÝÝĸĸããĸĸččĸĸîîĸÖÏÏĸLLLÖ���‚���(������������������������������������������ ĻĻĻÚöööĸöööĸöööĸöööĸüˏĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸäūđĸĸÓÓĸĸŨŨĸĸÞÞĸĸääĸĸččĸĸïïĸĸõõĸĸųųĸ‹‹‹ũ���ž���H��� ������������������������������������bbb1ÕÕÕĸüüüĸüũōĸüáūĸýЙĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸŊĒ ĸäĮĮĸĸÞÞĸĸääĸĸččĸĸïïĸĸõõĸĸųųĸĸĸĸĸÉÉÉĸ000Ä���r��������������������������������������–ØØØĸ···ĸĸķWĸĸžiĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸĸĖÆĸsssãŠĄĄėĸääĸĸééĸĸððĸĸõõĸĸûûĸĸĸĸĸĸĸĸĸōōōĸqqqé���Ž���4�����������������������������������”””‚‚‚‚J’’’Æō·nĸĸĀuĸĸńĸĸ˕ĸĸĖ™ĸĸ˜ĸĸĖžĸĸĖĄĸĸĖĪĸĸ˧ĸĸĖĐĸĸĖŦĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸĸĖÆĸëÂŋĸUUUÆjjj‡ÞŅŅĸĸððĸĸööĸĸüüĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ···ĸ%%%ƒ���2��� ����������������������������������������”””b†‘‘‘ķ’’’ÆŠŸ’ĮēĨ™ĸļϚĸĖēœĸĖēžĸØđĄĸåŋĨĸåŋĶĸĸĖŪĸĸĖąĸĸĖģĸĸĖķĸĸĖđĸĸĖŧĸĸĖūĸĸĖÁĸĸĖÄĸĸĖÆĸĸĖČĸŌĩīĸ###‹���$ĨĢĢáĸööĸĸüüĸĸĸĸĸĸĸĸĸåååĸŌŌŌĸŋŋŋĸ   Øxxxe������������������������������������������������������������������������“““B‰‰‰GŠŠŠX‡ŽŽŽ‰‘‘‘§›—–ÆīĢĮģĨĄņŋŽĶĸĖģ­ĸĖģŪĸæŋŧĸæŋžĸėÂÂĸīĢĢę���=���•••bŋūūĸĀĀĀņĐĐĐɊŠŠ„„„o€€€L��� ����������������������������������������������������������������������������������������������������������������������������”””BŠŠŠG‹‹‹X‘‘‘‡………\��� ���������������������������������������������������������������������������ü�āĸ��ð����ĸ��ā����?��Ā������Ā������Ā������Ā������Ā������Ā������Ā������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā�������Ā������Ā������Ā������Ā������Ā������Ā������Ā����?��Ā������Ā����?��Ā����?��Ā����?��Ā����ĸ��€���ĸ��€���ĸ��€���ĸ������ĸ������ĸ�������ĸ�������ĸ�������ĸ��Ā����ĸ��ð���ĸ��ĸþ�?ĸ��(��� ���@���� ����������������������������������������������N���s���n���X���D���1���"������������������ ������.���<���@���=���0������������������������������������������&;;;™vvví]]]ä555Ð―§���Œ���y���d���L���5���%���&���C{0 Ē/Ŋ.ąĨ���Œ���s���L���"��� �������������������������&LLLķĢĢĢĸíėëĸôôōĸÝÝÜĸÃÂĀĸ‘ũfffíCDDÝ**+ÆŽ—���„6##›wPPݟqqôĖ–ýŅĶĶþÆ  þ {{ũyRRė=((Ę������p���6������������������BBBoooÅūūūĸÔÔÔĸëčåĸĸĸųĸĸĸûĸĸĸûĸĸýøĸĸûöĸíčäĸË·Ģĸ§”ĸzpeóaJJčķ{{ýĸĸÆÆĸĸÏÏĸĸÚÚĸĸááĸĸééĸōßßĸČĐĐĸvPPëķ������<��� �����������ccc_ūūūĸÁÁÁĸ···ĸëįáĸĸøîĸþöėĸýöëĸĸųöĸĸüüĸĸðáĸĸПĸĸӜĸđ{ĸŅ„…ĸĸŦŦĸĸĩĩĸĸššĸĸÃÃĸĸĖĖĸĸÕÕĸĸÝÝĸĸįįĸĸĸĸĸëââĸ“mmöŧ���~���3�����������iiic···ĸķķķĸÁÂÂĸëäÝĸĸöîĸÎâšĸ<Ž5ĸ€Įxĸ°ŲĨĸŅΗĸïÆĸӞ‚ĸĮ|~ĸĸœĸĸĄĄĸĸŠŠĸĸēēĸĸššĸĸÄÄĸĸĖĖĸĸÔÔĸĸããĸĸþþĸĸĸĸĸņęęĸ‘iiõ ą���k����������jjjbŧŧŧĸąąąĸŋĀĀĸíäÛĸĸóčĸÆÞ°ĸ��ĸ� ĸĄĸœ ĸ/šĸ°zuĸü§ĻĸĸŸŸĸĸ™™ĸĸ  ĸĸŠŠĸĸģģĸĸšđĸĸÄÃĸĸËËĸĸęęĸĸĸĸĸĸĸĸĸĸĸĸĸ娨ĸoJJč���˜���F��� ����kkkbŋŋŋĸØØØĸŲŲÚĸéāØĸĸîßĸËÞ°ĸ °ĸī)ĸ­ĸ�œ�ĸA|,ĸ⚟ĸĸĩĩĸĸ§§ĸĸžžĸĸ™™ĸĸ  ĸĸ§Ĩĸĸą°ĸĸđđĸĸÅÃĸĸųųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸūŸŸþ3!!Ã���m�������kkkbĘĘĘĸáááĸÚÛÜĸčßÖĸĸęŨĸÎßŊĸ―1ĸ)Â=ĸī'ĸ�˜�ĸ{{Uĸĸ―Äĸĸđđĸĸ°°ĸĸ§§ĸĸĸĸ›šĸĸÁŅĸĸËôĸĸÎõĸĸÚęĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸņęęĸnJJæ���‹���.����lllbËËËĸãããĸÛÜÜĸéßÕĸĸæÎĸŅßŪĸ'ĖGĸ7ÐRĸ ū4ĸ ĸ͈yĸĸËÎĸĸÃÃĸĸđđĸĸ°°ĸĸĶĨĸĸŅŲĸĸáûĸøąĖĸôļÏĸĸįĸĸĸõĸĸĸĸĸĸĸĸĸĸýüýĸýüüĸüûûĸІ†ú Ī���=����nnnbÍÍÍĸåååĸÞÞßĸęÞÓĸĸáÅĸÔāŪĸ6Ü^ĸFßhĸ(Č@ĸŠĸĜ–ĸĸÓÕĸĸËËĸĸÃÃĸĸķķĸĸū―ĸĸóýĸė§°ĸ°}wĸą~uĸÓķŧĸĸųĸĸûøøĸöðîĸõîíĸõîėĸûũõĸÉŽŦĸ#°���I����ooobÏÏÏĸįįįĸßāâĸëÞŌĸĸÝžĸŨā­ĸEęuĸTî}ĸ2ÓOĸ …Bĸ˜ŸĸĸÓÔĸĸÍÍĸĸËËĸĸÃÃĸĸŅÐĸĸôüĸƆ‡ĸĒĖĖĸũāūĸ­‚|ĸũîōĸũóōĸęâßĸëâßĸëâßĸðéåĸÏĩīĸB++Â���M����qqqbŅŅŅĸéééĸâãäĸíÞÐĸĸŲīĸÛâŦĸTûŒĸ^ō™ĸLŅÁĸ<ØĸŧŒ•ĸĸÍĖĸĸĖĖĸĸĖĖĸĸÉÉĸĸŨÖĸĸũĸĸÖŦ­ĸ ŽŦĸóÓ°ĸēzsĸüÔŲĸýâãĸúļ·ĸöÄÃĸöĘÉĸøÖÔĸÔ°ŊĸA++Ā���I����rrrbŌŌŌĸęëëĸãæčĸïßÏĸĸŨŠĸÖÔ·ĸ@ÂāĸJģûĸLģĸĸ6†éĸ}ŽĸĸËČĸĸĘĘĸĸËËĸĸÖÖĸĸęéĸĸõĸĸûėûĸËĄžĸēvqĸݔ˜ĸĸðũĸĸÅÆĸĸĶĨĸĸēēĸĸššĸĸĘĘĸÚĶĶĸD--―���=����tttbÔÖØĸíïņĸįãÚĸïÞÍĸĸÔ ĸŌÆžĸ'˜ĸĸ3Ģĸĸ@Ūĸĸ)ŒųĸvfˆĸüÉÃĸĸããĸĸõõĸĸýýĸĸĸĸĸĸüĸĸĸįĸĸĸŅúĸúÂáĸĸãýĸĸÞįĸĸžĸĸŸŸĸĸŠŠĸĸēēĸĸĀĀĸɓ“ĸ)Ÿ���-����uvwbŨÓËĸúŧ[ĸōļeĸïßŅĸĸϜĸōĖĶĸ°ŧÅĸ”ķÔĸ{ķãĸQšëĸDUĢĸÎĩĩĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸóũĸĸËčĸĸĖëĸĸÉØĸĸŠŦĸĸœœĸĸ™™ĸĸ  ĸĸŠŠĸýēēĸĶssũu�������wz€bÞĞĸûŒ�ĸé°bĸņáŅĸĸ˖ĸĸ͙ĸĸϔĸĸϔĸĸ˚ĸóɟĸāĄšĸ瀂ĸũôôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸčæĸĸĮÅĸĸÂÁĸĸ·ĩĸĸ°°ĸĸĻĻĸĸžžĸĸ™™ĸĸĒĒĸĸsLLŨA��� ����y}bÛÁĸë�ĸâĻbĸóáÏĸĸĮŠĸĸɏĸĸɏĸĸɏĸĸɎĸĸʎĸĸɐĸŌ|ĸū ĪĸĸĸĸĸĸĸĸĸĸĸĸĸĸþþĸĸÜÜĸĸÏÏĸĸËĘĸĸÃÃĸĸššĸĸąąĸĸ§§ĸĸžžĸĸœœĸđxxý<((€����������{‚bØ―›ĸÝq�ĸā­xĸõáÉĸĸĀvĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÂ{ĸĸÄ{ĸĸÁĸžlĸÉīļĸĸĸĸĸĸĸĸĸĸũũĸĸßßĸĸŨŨĸĸÐÐĸĸËËĸĸÃÃĸĸđđĸĸąąĸĸŽŽĸԆ†ĸmIIŧ���(�����������|bÛĮēĸ⚏ĸŲŲŲĸõÝūĸĸšdĸĸžhĸĸŧgĸĸžhĸĸžjĸĸ―lĸĸ―lĸĸūkĸųšiĸļ‚dĸÅŦ­ĸüüþĸĸũũĸĸččĸĸááĸĸØØĸĸÐÐĸĸĖĖĸĸÆÆĸĸ――ĸŌĸ€WWÍ *��������������€€€bŌŌŌĸšŋÃĸÉĖŅĸũÛļĸĸ°FĸþŠ/ĸýŊ@ĸþŊ@ĸþ­;ĸĸ­=ĸĸŪ?ĸĸēJĸĸķSĸþļ[ĸƊZĸ牅ĸØÃËĸóāäĸþæįĸĸããĸĸÚÚĸûĖĖĸëķķĸ큁øzRRĒ������������������bŅŅŅĸŧŧŧĸĮĘÍĸũŨ­ĸþ­9ĸöęÖĸóņïĸōãÎĸōÖŠĸōʍĸôŋqĸöđ`ĸúŪAĸþĻ,ĸĸĻ"ĸí1ĸȀ=ĸŋ‰jĸū‘…ĸķĸ牉ý–llėmLLļO44`��������������������������‚‚‚bŨŨŨĸâââĸöøûĸýá·ĸĸėÅĸüĸĸĸøúüĸôũûĸïôûĸėōüĸéíóĸččįĸæáŲĸãÛÎĸåŅģĸíĮŽĸōūlĸîŦIĸëž1ĸė+ĸˆr\ōž���‘���d���,�����������������������bčččĸĸĸĸĸęëëĸĘ·ēĸÍīĩĸÚĮĮĸįÜÜĸïęęĸõũũĸōóóĸïņņĸėîîĸęėíĸåįčĸáäæĸÞáåĸÛßåĸŨÝæĸÖØÚĸÜÏŧĸĮÃŧþ°īĩø‘““éQRR‹�������������������������{{{fÝÝÝĸáááĸÔÖØĸĄv{ĸ›dgĸĪqqĸŦyyĸ°ĸĸÅĒĒĸŅĩĩĸŲĮĮĸāÔÔĸâÛÛĸâÝÝĸáááĸÞÝÝĸÚÚÚĸŨØŲĸÔÖÚĸŲÚÛĸÂÂÂĸnnnĪ...'�������������������������‡‡‡™ßÞÞĸāãæĸÝËžĸ†iĸķrĸ­zvĸŦxxĸ­z{ĸ°|}ĸą}~ĸģ~ĸ큂ĸŧ„…ĸӔĸƟŸĸßŲŲĸęėėĸíïïĸéęęĸįææĸŦŦŦø111­���8���������������������������(((đđđëîïïĸéėðĸöʓĸĸđbĸýÁĸûȕĸöØĸėđ˜ĸ㰗ĸÛĻ—ĸҟ”ĸΛ•ĸĖ™•ĸ˗•ĸʗ–ĸčßßĸúööĸĸööĸĸîîĸĸîîĸĩŽŽúĨ���U���������������������������dęëėýõųþĸõïčĸýÃ{ĸĸÃ~ĸĸĖ—ĸĸ˜ĸĸ͟ĸĸÎĪĸĸÏĐĸĸÏ­ĸĸŅąĸĸÏĩĸýĘķĸøÅ·ĸóÁ·ĸņÐÎĸĸÛÛĸĸÜÜĸĸääĸĸððĸïččĸKLLÓ���|���&�����������������������ģģī·óïëĸþÞķĸþˍĸĸÃ~ĸĸ˗ĸĸ˜ĸĸ˟ĸĸĖĪĸĸ˧ĸĸĖŦĸĸĖŊĸĸĖģĸĸĖ·ĸĸÍžĸĸÍÁĸûĘÁĸīĒĄüûÚÚĸĸččĸĸïïĸĸųųĸĸĸĸĸ———í‘���;��� ��������������������žŸĄ‹ŦŸ‘Öþ·^ĸþÂ~ĸþ˖ĸþ˜ĸĸ˟ĸĸÎĪĸĸÎĻĸĸÎŽĸĸΰĸĸÎīĸĸÎļĸĸÍžĸĸÍĀĸĸÍÅĸðÄĀþ\]]ÂÎÂÂëĸõõĸĸýýĸĸĸĸĸĸĸĸĸÝÝÝĸFFFŽ���/��� ������������������������=’‘x–“ŽŸ’ŠļĨ“žŋЖÉÂŦ›Ó˰žáÛļĨëā―ŠøęÁąĸëÂīĸðÅšĸúĘÂĸĸÎÉĸÚđļýO•””›ïëëĸéééĸÓÔÔéžžžÕ­­­ŧwwwX��� ������������������������������������������������€ƒ† ‡‰‹ƒˆŠ%‡‡ˆ9Ą”DĶ™•XŪžšeŸ˜˜…Ļ›››š““���sss§§§ZDooo'MMM ����������������������������������ā��€��€��€��€��€���€���€���€���€���€���€���€���€���€���€���€���€���€��€��€��€��€��€��������?���?���������€��ð��(������ ���� ����������������������������������?���m���{���p���_���O���H���W���j���m���^���;������GGGZ‰‰‰ÜЧ§ï|~åYXXŨ543Å$ķ\>?ȌhhޓssämTTŲ*ŧ���Œ���O���XXXĄĒĒņŨÕÔĸĸýōĸâōÖĸųóáĸîÁĸƆ~þũĐŦĸĸÆÆĸĸŲŲĸþėėĸÖÃÃýB--Ð������=ccc#ŊŊ°ųÔÏÏĸðëÓĸ!­%ĸ)Š!ĸœšcĸĸĪĸĸ  ĸĸ­Žĸĸū―ĸĸęęĸĸĸĸĸÕÃÃý&ŧ���heee"―――öčâáĸņåÉĸ1ÃBĸ� ĸŸtĸĸ·―ĸĸĄ ĸĸąšĸüŧÐĸĸóũĸĸĸĸĸĸĸĸĸz``ã���‡hhh"ÂÂÃöėäâĸóáūĸSækĸą,ĸČ­ĸĸÉĖĸĸÉĘĸæĮŅĸĮ°ŊĸņāäĸøõôĸøôōĸĀĶĪû���Žlkk"ÅČËöîęęĸóÜģĸ]āĩĸ6ĶĘĸÆĨļĸĸÏÍĸĸÝÞĸäÕßĸšĢšĸíËĖĸûËËĸúËĘĸÎĨĨü���ƒmno"ËÅšööÚģĸõŌŊĸa­õĸ:–ûĸ§ĄÃĸĸõōĸĸûûĸĸóĸĸōūÜĸýÁËĸĸŸžĸĸģģĸˆˆũ���_qv}"ÏĢföōŦQĸĸŌĶĸöɛĸÝÂĢĸËĶžĸíéîĸĸĸĸĸĸððĸĸËŌĸĸģīĸĸĢĒĸüŸŸĸ{RRŋ���.uy}"ŧ„ö㷈ĸĸÂvĸĸđ^ĸĸĀhĸüķhĸŌ·ĸëäęĸĸōöĸĸÛÝĸĸÎÎĸüķķĸŪppâ0 D��� www#đžūöØŅÆĸĸĮqĸû˂ĸúÄxĸüŋhĸũ°Pĸՙ]ĸâķ˜ĸįūŽĸš’’ō‡^^É5""G��� ���lll$ŌŌÓũíåßĸãÐĮĸéčðĸėééĸéåáĸæâŲĸįÔīĸáđ€ĸāąpĸqd`Ý"w����������ooo5ØÛÞúØÄļĸĩ€tĸģƒ€ĸŧĸŞŸĸ˧ŠĸŲĖÐĸęņøĸâäéĸlmmŲl����������ĄĄĒvôōîĸü͓ĸýŋĸøÄĸō― ĸė·Ģĸį°ĨĸėÄĀĸĸęęĸþîîĸSRRŋ���g����������ĻĶ̐óȒøûĮŽþþĖĄĸĸÎĐĸĸÎąĸĸÐŧĸĸÔĮĸÉ­ŠęėÚÛþĸþþĸĀĀĀáC����������ˆˆ‡ —’‹2ŦG§šY –jĩĄ™yÂĐĒūϧۈƒƒcŸŸŸhģģģv˜˜˜TEEE�����������������kb��Ęĸ��áĸ��Üĸ��Öĸ��Ũĸ��Ŋĸ��1ĸ��=ĸ��'ĸ��ĸ�Uĸ�Äĸ�đĸ�°ĸ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/installerbase.manifest����������������������������������������������������������������������0000664�0000000�0000000�00000002171�13253666515�0017023�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <!-- Make sure Vista UAC does not believe installerbase is an installer --> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!--The ID below indicates app support for Windows Vista --> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!--The ID below indicates app support for Windows 7 --> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <!--The ID below indicates app support for Windows 8 --> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <!--The ID below indicates app support for Windows 8.1 --> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> </application> </compatibility> </assembly> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/installerbase.rc����������������������������������������������������������������������������0000664�0000000�0000000�00000000304�13253666515�0015615�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������IDI_ICON1 ICON DISCARDABLE "installerbase.ico" #define RT_MANIFEST 24 #define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "installerbase.manifest" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/installerbasecommons.cpp��������������������������������������������������������������������0000664�0000000�0000000�00000012443�13253666515�0017376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "installerbasecommons.h" #include <packagemanagercore.h> #include <scriptengine.h> #include <packagemanagerpagefactory.h> #include <productkeycheck.h> #include <settings.h> using namespace QInstaller; // -- InstallerGui InstallerGui::InstallerGui(PackageManagerCore *core) : PackageManagerGui(core, 0) { ProductKeyCheck *checker = ProductKeyCheck::instance(); foreach (const int id, checker->registeredPages()) { PackageManagerPage *page = PackageManagerPageFactory::instance().create(id, core); Q_ASSERT_X(page, Q_FUNC_INFO, qPrintable(QString::fromLatin1("Page with %1 couldn't be " "constructed.").arg(id))); setPage(id, page); } setPage(PackageManagerCore::Introduction, new IntroductionPage(core)); setPage(PackageManagerCore::TargetDirectory, new TargetDirectoryPage(core)); setPage(PackageManagerCore::ComponentSelection, new ComponentSelectionPage(core)); setPage(PackageManagerCore::LicenseCheck, new LicenseAgreementPage(core)); #ifdef Q_OS_WIN setPage(PackageManagerCore::StartMenuSelection, new StartMenuDirectoryPage(core)); #endif setPage(PackageManagerCore::ReadyForInstallation, new ReadyForInstallationPage(core)); setPage(PackageManagerCore::PerformInstallation, new PerformInstallationPage(core)); setPage(PackageManagerCore::InstallationFinished, new FinishedPage(core)); foreach (const int id, pageIds()) { QWizardPage *wizardPage = page(id); packageManagerCore()->controlScriptEngine()->addToGlobalObject(wizardPage); packageManagerCore()->componentScriptEngine()->addToGlobalObject(wizardPage); } } // -- MaintenanceGui MaintenanceGui::MaintenanceGui(PackageManagerCore *core) : PackageManagerGui(core, 0) { ProductKeyCheck *checker = ProductKeyCheck::instance(); foreach (const int id, checker->registeredPages()) { PackageManagerPage *page = PackageManagerPageFactory::instance().create(id, core); Q_ASSERT_X(page, Q_FUNC_INFO, qPrintable(QString::fromLatin1("Page with %1 couldn't be " "constructed.").arg(id))); setPage(id, page); } IntroductionPage *intro = new IntroductionPage(core); connect(intro, &IntroductionPage::packageManagerCoreTypeChanged, this, &MaintenanceGui::updateRestartPage); if (!core->isOfflineOnly() || validRepositoriesAvailable()) { setPage(PackageManagerCore::Introduction, intro); setPage(PackageManagerCore::ComponentSelection, new ComponentSelectionPage(core)); setPage(PackageManagerCore::LicenseCheck, new LicenseAgreementPage(core)); } else { core->setUninstaller(); core->setCompleteUninstallation(true); } setPage(PackageManagerCore::ReadyForInstallation, new ReadyForInstallationPage(core)); setPage(PackageManagerCore::PerformInstallation, new PerformInstallationPage(core)); setPage(PackageManagerCore::InstallationFinished, new FinishedPage(core)); RestartPage *p = new RestartPage(core); connect(p, &RestartPage::restart, this, &PackageManagerGui::gotRestarted); setPage(PackageManagerCore::InstallationFinished + 1, p); if (core->isUninstaller()) wizardPageVisibilityChangeRequested(false, PackageManagerCore::InstallationFinished + 1); foreach (const int id, pageIds()) { QWizardPage *wizardPage = page(id); packageManagerCore()->controlScriptEngine()->addToGlobalObject(wizardPage); packageManagerCore()->componentScriptEngine()->addToGlobalObject(wizardPage); } } void MaintenanceGui::updateRestartPage() { wizardPageVisibilityChangeRequested((packageManagerCore()->isUninstaller() ? false : true), PackageManagerCore::InstallationFinished + 1); } bool MaintenanceGui::validRepositoriesAvailable() const { foreach (const Repository &repo, packageManagerCore()->settings().repositories()) { if (repo.isEnabled() && repo.isValid()) { return true; } } return false; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/installerbasecommons.h����������������������������������������������������������������������0000664�0000000�0000000�00000003643�13253666515�0017045�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef INSTALLERBASECOMMONS_H #define INSTALLERBASECOMMONS_H #include <packagemanagergui.h> // -- InstallerGui class InstallerGui : public QInstaller::PackageManagerGui { Q_OBJECT public: explicit InstallerGui(QInstaller::PackageManagerCore *core); ~InstallerGui() {} }; // -- MaintenanceGui class MaintenanceGui : public QInstaller::PackageManagerGui { Q_OBJECT public: explicit MaintenanceGui(QInstaller::PackageManagerCore *core); ~MaintenanceGui() {} private Q_SLOTS: void updateRestartPage(); private: bool validRepositoriesAvailable() const; }; #endif // INSTALLERBASECOMMONS_H ���������������������������������������������������������������������������������������������src/sdk/main.cpp������������������������������������������������������������������������������������0000664�0000000�0000000�00000022063�13253666515�0014075�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "console.h" #include "constants.h" #include "commandlineparser.h" #include "installerbase.h" #include "sdkapp.h" #include "updatechecker.h" #include <errors.h> #include <selfrestarter.h> #include <remoteserver.h> #include <utils.h> #include <QCommandLineParser> #include <QDateTime> #include <QNetworkProxyFactory> #include <iostream> #if defined(Q_OS_OSX) or defined(Q_OS_UNIX) # include <unistd.h> # include <sys/types.h> #endif #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) #define VERSION "IFW Version: " QUOTE(IFW_VERSION_STR) ", built with Qt " QT_VERSION_STR "." #define BUILDDATE "Build date: " __DATE__ #define SHA "Installer Framework SHA1: " QUOTE(_GIT_SHA1_) static const char PLACEHOLDER[32] = "MY_InstallerCreateDateTime_MY"; int main(int argc, char *argv[]) { #if defined(Q_OS_WIN) if (!qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } #endif // increase maximum numbers of file descriptors #if defined (Q_OS_OSX) QCoreApplication::setSetuidAllowed(true); struct rlimit rl; getrlimit(RLIMIT_NOFILE, &rl); rl.rlim_cur = qMin((rlim_t) OPEN_MAX, rl.rlim_max); setrlimit(RLIMIT_NOFILE, &rl); #endif qsrand(QDateTime::currentDateTime().toTime_t()); // We need to start either a command line application or a GUI application. Since we // fail doing so at least on Linux while parsing the argument using a core application // object and later starting the GUI application, we now parse the arguments first. CommandLineParser parser; parser.parse(QInstaller::parseCommandLineArgs(argc, argv)); QStringList mutually; if (parser.isSet(QLatin1String(CommandLineOptions::CheckUpdates))) mutually << QLatin1String(CommandLineOptions::CheckUpdates); if (parser.isSet(QLatin1String(CommandLineOptions::Updater))) mutually << QLatin1String(CommandLineOptions::Updater); if (parser.isSet(QLatin1String(CommandLineOptions::ManagePackages))) mutually << QLatin1String(CommandLineOptions::ManagePackages); const bool help = parser.isSet(QLatin1String(CommandLineOptions::HelpShort)) || parser.isSet(QLatin1String(CommandLineOptions::HelpLong)); if (help || parser.isSet(QLatin1String(CommandLineOptions::Version)) || parser.isSet(QLatin1String(CommandLineOptions::FrameworkVersion)) || mutually.count() > 1) { Console c; QCoreApplication app(argc, argv); if (parser.isSet(QLatin1String(CommandLineOptions::Version))) { std::cout << VERSION << std::endl << BUILDDATE << std::endl << SHA << std::endl; const QDateTime dateTime = QDateTime::fromString(QLatin1String(PLACEHOLDER), QLatin1String("yyyy-MM-dd - HH:mm:ss")); if (dateTime.isValid()) std::cout << "Installer creation time: " << PLACEHOLDER << std::endl; return EXIT_SUCCESS; } if (parser.isSet(QLatin1String(CommandLineOptions::FrameworkVersion))) { std::cout << QUOTE(IFW_VERSION_STR) << std::endl; return EXIT_SUCCESS; } std::cout << qPrintable(parser.helpText()) << std::endl; if (mutually.count() > 1) { std::cerr << qPrintable(QString::fromLatin1("The following options are mutually " "exclusive: %1.").arg(mutually.join(QLatin1String(", ")))) << std::endl; } return help ? EXIT_SUCCESS : EXIT_FAILURE; } if (parser.isSet(QLatin1String(CommandLineOptions::StartServer))) { const QStringList arguments = parser.value(QLatin1String(CommandLineOptions::StartServer)) .split(QLatin1Char(','), QString::SkipEmptyParts); QString socketName, key; const QString mode = arguments.value(0); bool argumentsValid = (mode.compare(QLatin1String(QInstaller::Protocol::ModeDebug), Qt::CaseInsensitive) == 0); if (argumentsValid) { socketName = arguments.value(1, QLatin1String(QInstaller::Protocol::DefaultSocket)); key = arguments.value(2, QLatin1String(QInstaller::Protocol::DefaultAuthorizationKey)); } else { socketName = arguments.value(1); key = arguments.value(2); } const bool production = (mode.compare(QLatin1String(QInstaller::Protocol::ModeProduction), Qt::CaseInsensitive) == 0); if (production) { argumentsValid = (!key.isEmpty()) && (!socketName.isEmpty()); #if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) /* In production mode detach child so that sudo waiting on us will terminate. */ pid_t child = fork(); if (child <= -1) { std::cerr << "Fatal cannot fork and detach server." << std::endl; return EXIT_FAILURE; } if (child != 0) { return EXIT_SUCCESS; } ::setsid(); #endif } SDKApp<QCoreApplication> app(argc, argv); if (!argumentsValid) { Console c; std::cout << qPrintable(parser.helpText()) << std::endl; std::cerr << "Wrong argument(s) for option --startserver." << std::endl; return EXIT_FAILURE; } #if defined(Q_OS_OSX) // make sure effective == real user id. uid_t realUserId = getuid(); uid_t effectiveUserId = geteuid(); if (realUserId != effectiveUserId) setreuid(effectiveUserId, -1); #endif QInstaller::RemoteServer *server = new QInstaller::RemoteServer; QObject::connect(server, &QInstaller::RemoteServer::destroyed, &app, &decltype(app)::quit); server->init(socketName, key, (production ? QInstaller::Protocol::Mode::Production : QInstaller::Protocol::Mode::Debug)); server->start(); return app.exec(); } try { QScopedPointer<Console> console; if (parser.isSet(QLatin1String(CommandLineOptions::VerboseShort)) || parser.isSet(QLatin1String(CommandLineOptions::VerboseLong))) { console.reset(new Console); QInstaller::setVerbose(true); } // On Windows we need the console window from above, we are a GUI application. const QStringList unknownOptionNames = parser.unknownOptionNames(); if (!unknownOptionNames.isEmpty()) { const QString options = unknownOptionNames.join(QLatin1String(", ")); std::cerr << "Unknown option: " << qPrintable(options) << std::endl; } if (parser.isSet(QLatin1String(CommandLineOptions::Proxy))) { // Make sure we honor the system's proxy settings QNetworkProxyFactory::setUseSystemConfiguration(true); } if (parser.isSet(QLatin1String(CommandLineOptions::NoProxy))) QNetworkProxyFactory::setUseSystemConfiguration(false); if (parser.isSet(QLatin1String(CommandLineOptions::CheckUpdates))) return UpdateChecker(argc, argv).check(); if (QInstaller::isVerbose()) std::cout << VERSION << std::endl << BUILDDATE << std::endl << SHA << std::endl; const SelfRestarter restarter(argc, argv); return InstallerBase(argc, argv).run(); } catch (const QInstaller::Error &e) { std::cerr << qPrintable(e.message()) << std::endl; } catch (const std::exception &e) { std::cerr << e.what() << std::endl; } catch (...) { std::cerr << "Unknown exception caught." << std::endl; } return EXIT_FAILURE; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/sdk.pro�������������������������������������������������������������������������������������0000664�0000000�0000000�00000007176�13253666515�0013760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEMPLATE = app INCLUDEPATH += . .. TARGET = installerbase include(../../installerfw.pri) !isEmpty(SQUISH_PATH) { DEFINES += ENABLE_SQUISH include($$SQUISH_PATH/qtbuiltinhook.pri) } QT += network qml xml widgets # add the minimal plugin in static build to be able to start the installer headless with: # installer-binary --platform minimal # using QT += qpa_minimal_plugin would result in a minimal only compiled version !win32:CONFIG(static, static|shared) { QTPLUGIN += qminimal } CONFIG(static, static|shared) { # prevent qmake from automatically linking in imageformats, bearer, qmltooling plugins QTPLUGIN.imageformats = - QTPLUGIN.bearer = - QTPLUGIN.qmltooling = - } DESTDIR = $$IFW_APP_PATH exists($$LRELEASE) { IB_TRANSLATIONS = $$files($$PWD/translations/*_??.ts) IB_TRANSLATIONS -= $$PWD/translations/ifw_en.ts empty_ts = "<TS></TS>" write_file($$OUT_PWD/translations/ifw_en.ts, empty_ts)|error("Aborting.") IB_TRANSLATIONS += $$OUT_PWD/translations/ifw_en.ts QMAKE_DISTCLEAN += translations/ifw_en.ts qrc_cont = \ "<RCC>" \ " <qresource prefix=\"/\">" for (file, IB_TRANSLATIONS) { lang = $$replace(file, .*_([^/]*)\\.ts, \\1) qfile = $$[QT_INSTALL_TRANSLATIONS]/qtbase_$${lang}.qm !exists($$qfile) { qfile = $$[QT_INSTALL_TRANSLATIONS]/qt_$${lang}.qm !exists($$qfile) { warning("No Qt translation for '$$lang'; skipping.") next() } } qrc_cont += \ " <file>translations/ifw_$${lang}.qm</file>" \ " <file alias=\"translations/qt_$${lang}.qm\">$$qfile</file>" ACTIVE_IB_TRANSLATIONS += $$file RESOURCE_DEPS += $$qfile translations/ifw_$${lang}.qm } qrc_cont += \ " </qresource>" \ "</RCC>" RESOURCE = $$OUT_PWD/installerbase.qrc write_file($$RESOURCE, qrc_cont)|error("Aborting.") QMAKE_DISTCLEAN += $$RESOURCE !isEmpty(ACTIVE_IB_TRANSLATIONS) { updateqm.input = ACTIVE_IB_TRANSLATIONS updateqm.output = translations/${QMAKE_FILE_BASE}.qm updateqm.commands = $$LRELEASE ${QMAKE_FILE_IN} -qm ${QMAKE_FILE_OUT} updateqm.name = LRELEASE ${QMAKE_FILE_IN} updateqm.CONFIG += no_link target_predeps QMAKE_EXTRA_COMPILERS += updateqm exists($$RCC) { runrcc.input = RESOURCE runrcc.output = qrc_${QMAKE_FILE_BASE}.cpp runrcc.commands = $$RCC -name ${QMAKE_FILE_BASE} ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} runrcc.name = RCC ${QMAKE_FILE_IN} runrcc.CONFIG += no_link explicit_dependencies runrcc.depends = $$RESOURCE_DEPS runrcc.variable_out = SOURCES QMAKE_EXTRA_COMPILERS += runrcc } } } FORMS += settingsdialog.ui HEADERS += \ tabcontroller.h \ installerbasecommons.h \ settingsdialog.h \ console.h \ sdkapp.h \ updatechecker.h \ installerbase.h \ constants.h \ commandlineparser.h SOURCES = \ main.cpp \ installerbase.cpp \ tabcontroller.cpp \ installerbasecommons.cpp \ settingsdialog.cpp \ updatechecker.cpp \ commandlineparser.cpp win32 { # Use our own manifest file CONFIG -= embed_manifest_exe RC_FILE = installerbase.rc SOURCES += console_win.cpp } macx:include(../../no_app_bundle.pri) target.path = $$[QT_INSTALL_BINS] INSTALLS += target ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/sdkapp.h������������������������������������������������������������������������������������0000664�0000000�0000000�00000012506�13253666515�0014101�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SDKAPP_H #define SDKAPP_H #include <binarycontent.h> #include <binaryformat.h> #include <fileio.h> #include <fileutils.h> #include <QApplication> #include <QDir> #include <QFileInfo> #include <QResource> template<class T> class SDKApp : public T { public: SDKApp(int& argc, char** argv) : T(argc, argv) { } virtual ~SDKApp() { foreach (const QByteArray &ba, m_resourceMappings) QResource::unregisterResource((const uchar*) ba.data(), QLatin1String(":/metadata")); } bool notify(QObject *receiver, QEvent *event) { try { return T::notify(receiver, event); } catch (std::exception &e) { qFatal("Exception thrown: %s", e.what()); } catch (...) { qFatal("Unknown exception caught."); } return false; } /*! Returns the installer / maintenance tool binary. In case of an installer this will be the installer binary itself, which contains the binary layout and the binary content. In case of an maintenance tool, it will return a binary that has just a binary layout append. Note on OS X: For compatibility reason this function will return the a .dat file located inside the resource folder in the application bundle, as on OS X the binary layout cannot be appended to the actual installer / maintenance tool binary itself because of signing. */ QString binaryFile() const { QString binaryFile = QCoreApplication::applicationFilePath(); #ifdef Q_OS_OSX // The installer binary on OSX does not contain the binary content, it's put into // the resources folder as separate file. Adjust the actual binary path. No error // checking here since we will fail later while reading the binary content. QDir resourcePath(QFileInfo(binaryFile).dir()); resourcePath.cdUp(); resourcePath.cd(QLatin1String("Resources")); return resourcePath.filePath(QLatin1String("installer.dat")); #endif return binaryFile; } /*! Returns the corresponding .dat file for a given installer / maintenance tool binary or an empty string if it fails to find one. */ QString datFile(const QString &binaryFile) const { QFile file(binaryFile); QInstaller::openForRead(&file); const quint64 cookiePos = QInstaller::BinaryContent::findMagicCookie(&file, QInstaller::BinaryContent::MagicCookie); if (!file.seek(cookiePos - sizeof(qint64))) // seek to read the marker return QString(); // ignore error, we will fail later const qint64 magicMarker = QInstaller::retrieveInt64(&file); if (magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) { QFileInfo fi(binaryFile); QString bundlePath; if (QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath)) fi.setFile(bundlePath); return fi.absoluteDir().filePath(fi.baseName() + QLatin1String(".dat")); } return QString(); } void registerMetaResources(const QInstaller::ResourceCollection &collection) { foreach (const QSharedPointer<QInstaller::Resource> &resource, collection.resources()) { const bool isOpen = resource->isOpen(); if ((!isOpen) && (!resource->open())) continue; if (!resource->seek(0)) continue; const QByteArray ba = resource->readAll(); if (ba.isEmpty()) continue; if (QResource::registerResource((const uchar*) ba.data(), QLatin1String(":/metadata"))) m_resourceMappings.append(ba); if (!isOpen) // If we reach that point, either the resource was opened already... resource->close(); // or we did open it and have to close it again. } } private: QList<QByteArray> m_resourceMappings; }; #endif // SDKAPP_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/settingsdialog.cpp��������������������������������������������������������������������������0000664�0000000�0000000�00000040372�13253666515�0016174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "settingsdialog.h" #include "ui_settingsdialog.h" #include <packagemanagercore.h> #include <productkeycheck.h> #include <testrepository.h> #include <QtCore/QFile> #include <QItemSelectionModel> #include <QMessageBox> #include <QTreeWidget> #include <QtXml/QDomDocument> using namespace QInstaller; // -- PasswordDelegate void PasswordDelegate::showPasswords(bool show) { m_showPasswords = show; } void PasswordDelegate::disableEditing(bool disable) { m_disabledEditor = disable; } QString PasswordDelegate::displayText(const QVariant &value, const QLocale &locale) const { const QString tmp = QStyledItemDelegate::displayText(value, locale); if (m_showPasswords) return tmp; return QString(tmp.length(), QChar(0x25CF)); } QWidget *PasswordDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const { if (m_disabledEditor) return 0; QLineEdit *lineEdit = new QLineEdit(parent); lineEdit->setEchoMode(m_showPasswords ? QLineEdit::Normal : QLineEdit::Password); return lineEdit; } // -- RepositoryItem RepositoryItem::RepositoryItem(const QString &label) : QTreeWidgetItem(QTreeWidgetItem::UserType) { setText(0, label); m_repo = QInstaller::Repository(QUrl(), true); } RepositoryItem::RepositoryItem(const Repository &repo) : QTreeWidgetItem(QTreeWidgetItem::UserType) , m_repo(repo) { if (!repo.isDefault()) setFlags(flags() | Qt::ItemIsEditable); } QVariant RepositoryItem::data(int column, int role) const { const QVariant &data = QTreeWidgetItem::data(column, role); switch (role) { case Qt::UserRole: { if (column == 0) return m_repo.isDefault(); } break; case Qt::CheckStateRole: { if (column == 1) return (m_repo.isEnabled() ? Qt::Checked : Qt::Unchecked); } break; case Qt::EditRole: case Qt::DisplayRole:{ switch (column) { case 0: return data.toString().isEmpty() ? QLatin1String(" ") : data; case 2: return m_repo.username(); case 3: return m_repo.password(); case 4: return m_repo.displayname(); default: break; }; } break; case Qt::ToolTipRole: switch (column) { case 1: return SettingsDialog::tr("Check this to use repository during fetch."); case 2: return SettingsDialog::tr("Add the username to authenticate on the server."); case 3: return SettingsDialog::tr("Add the password to authenticate on the server."); case 4: return SettingsDialog::tr("The servers URL that contains a valid repository."); default: return QVariant(); } break; break; }; return data; } void RepositoryItem::setData(int column, int role, const QVariant &value) { switch (role) { case Qt::EditRole: { switch (column) { case 2: m_repo.setUsername(value.toString()); break; case 3: m_repo.setPassword(value.toString()); break; case 4: m_repo.setUrl(QUrl::fromUserInput(value.toString())); break; default: break; }; } break; case Qt::CheckStateRole: { if (column == 1) m_repo.setEnabled(Qt::CheckState(value.toInt()) == Qt::Checked); } break; default: break; } QTreeWidgetItem::setData(column, role, value); } QSet<Repository> RepositoryItem::repositories() const { QSet<Repository> set; for (int i = 0; i < childCount(); ++i) { if (QTreeWidgetItem *item = child(i)) { if (item->type() == QTreeWidgetItem::UserType) { if (RepositoryItem *repoItem = static_cast<RepositoryItem*> (item)) set.insert(repoItem->repository()); } } } return set; } // -- SettingsDialog SettingsDialog::SettingsDialog(PackageManagerCore *core, QWidget *parent) : QDialog(parent) , m_ui(new Ui::SettingsDialog) , m_core(core) , m_showPasswords(false) { m_ui->setupUi(this); setupRepositoriesTreeWidget(); const Settings &settings = m_core->settings(); switch (settings.proxyType()) { case Settings::NoProxy: m_ui->m_noProxySettings->setChecked(true); break; case Settings::SystemProxy: m_ui->m_systemProxySettings->setChecked(true); break; case Settings::UserDefinedProxy: m_ui->m_manualProxySettings->setChecked(true); break; default: m_ui->m_noProxySettings->setChecked(true); Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown proxy type given!"); } const QNetworkProxy &ftpProxy = settings.ftpProxy(); m_ui->m_ftpProxy->setText(ftpProxy.hostName()); m_ui->m_ftpProxyPort->setValue(ftpProxy.port()); const QNetworkProxy &httpProxy = settings.httpProxy(); m_ui->m_httpProxy->setText(httpProxy.hostName()); m_ui->m_httpProxyPort->setValue(httpProxy.port()); connect(m_ui->m_addRepository, &QAbstractButton::clicked, this, &SettingsDialog::addRepository); connect(m_ui->m_showPasswords, &QAbstractButton::clicked, this, &SettingsDialog::updatePasswords); connect(m_ui->m_removeRepository, &QAbstractButton::clicked, this, &SettingsDialog::removeRepository); connect(m_ui->m_useTmpRepositories, &QAbstractButton::clicked, this, &SettingsDialog::useTmpRepositoriesOnly); connect(m_ui->m_repositoriesView, &QTreeWidget::currentItemChanged, this, &SettingsDialog::currentRepositoryChanged); connect(m_ui->m_testRepository, &QAbstractButton::clicked, this, &SettingsDialog::testRepository); useTmpRepositoriesOnly(settings.hasReplacementRepos()); m_ui->m_useTmpRepositories->setChecked(settings.hasReplacementRepos()); m_ui->m_useTmpRepositories->setEnabled(settings.hasReplacementRepos()); m_ui->m_repositoriesView->setCurrentItem(m_rootItems.at(settings.hasReplacementRepos())); if (!settings.repositorySettingsPageVisible()) { // workaround a inconvenience that the page won't hide inside a QTabWidget m_ui->m_repositories->setParent(this); m_ui->m_repositories->setVisible(settings.repositorySettingsPageVisible()); } } void SettingsDialog::accept() { bool settingsChanged = false; Settings newSettings; const Settings &settings = m_core->settings(); // set possible updated default repositories newSettings.setDefaultRepositories((dynamic_cast<RepositoryItem*> (m_rootItems.at(0)))->repositories()); settingsChanged |= (settings.defaultRepositories() != newSettings.defaultRepositories()); // set possible new temporary repositories newSettings.setTemporaryRepositories((dynamic_cast<RepositoryItem*> (m_rootItems.at(1)))->repositories(), m_ui->m_useTmpRepositories->isChecked()); settingsChanged |= (settings.temporaryRepositories() != newSettings.temporaryRepositories()); settingsChanged |= (settings.hasReplacementRepos() != newSettings.hasReplacementRepos()); // set possible new user repositories newSettings.setUserRepositories((dynamic_cast<RepositoryItem*> (m_rootItems.at(2)))->repositories()); settingsChanged |= (settings.userRepositories() != newSettings.userRepositories()); // update proxy type newSettings.setProxyType(Settings::NoProxy); if (m_ui->m_systemProxySettings->isChecked()) newSettings.setProxyType(Settings::SystemProxy); else if (m_ui->m_manualProxySettings->isChecked()) newSettings.setProxyType(Settings::UserDefinedProxy); settingsChanged |= settings.proxyType() != newSettings.proxyType(); if (newSettings.proxyType() == Settings::UserDefinedProxy) { // update ftp proxy settings newSettings.setFtpProxy(QNetworkProxy(QNetworkProxy::HttpProxy, m_ui->m_ftpProxy->text(), m_ui->m_ftpProxyPort->value())); settingsChanged |= (settings.ftpProxy() != newSettings.ftpProxy()); // update http proxy settings newSettings.setHttpProxy(QNetworkProxy(QNetworkProxy::HttpProxy, m_ui->m_httpProxy->text(), m_ui->m_httpProxyPort->value())); settingsChanged |= (settings.httpProxy() != newSettings.httpProxy()); } if (settingsChanged) emit networkSettingsChanged(newSettings); QDialog::accept(); } // -- private slots void SettingsDialog::addRepository() { int index = 0; QTreeWidgetItem *parent = m_ui->m_repositoriesView->currentItem(); if (parent && !m_rootItems.contains(parent)) { parent = parent->parent(); index = parent->indexOfChild(m_ui->m_repositoriesView->currentItem()); } if (parent) { Repository repository; repository.setEnabled(true); RepositoryItem *item = new RepositoryItem(repository); parent->insertChild(index, item); m_ui->m_repositoriesView->editItem(item, 4); m_ui->m_repositoriesView->scrollToItem(item); m_ui->m_repositoriesView->setCurrentItem(item); if (parent == m_rootItems.value(1)) m_ui->m_useTmpRepositories->setEnabled(parent->childCount() > 0); } } void SettingsDialog::testRepository() { RepositoryItem *current = dynamic_cast<RepositoryItem*> (m_ui->m_repositoriesView->currentItem()); if (current && !m_rootItems.contains(current)) { m_ui->tabWidget->setEnabled(false); m_ui->buttonBox->setEnabled(false); TestRepository testJob(m_core); testJob.setRepository(current->repository()); testJob.start(); testJob.waitForFinished(); current->setRepository(testJob.repository()); QMessageBox msgBox(this); msgBox.setIcon(QMessageBox::Question); msgBox.setWindowModality(Qt::WindowModal); msgBox.setDetailedText(testJob.errorString()); const bool isError = (testJob.error() > Job::NoError); const bool isEnabled = current->data(1, Qt::CheckStateRole).toBool(); msgBox.setText(isError ? tr("An error occurred while testing this repository.") : tr("The repository was tested successfully.")); const bool showQuestion = (isError == isEnabled); msgBox.setStandardButtons(showQuestion ? QMessageBox::Yes | QMessageBox::No : QMessageBox::Close); msgBox.setDefaultButton(showQuestion ? QMessageBox::Yes : QMessageBox::Close); if (showQuestion) { msgBox.setInformativeText(isEnabled ? tr("Do you want to disable the repository?") : tr("Do you want to enable the repository?") ); } if (msgBox.exec() == QMessageBox::Yes) current->setData(1, Qt::CheckStateRole, (!isEnabled) ? Qt::Checked : Qt::Unchecked); m_ui->tabWidget->setEnabled(true); m_ui->buttonBox->setEnabled(true); } } void SettingsDialog::updatePasswords() { m_showPasswords = !m_showPasswords; m_delegate->showPasswords(m_showPasswords); m_ui->m_showPasswords->setText(m_showPasswords ? tr("Hide Passwords") : tr("Show Passwords")); // force an tree view update so the delegate has to repaint m_ui->m_repositoriesView->viewport()->update(); } void SettingsDialog::removeRepository() { QTreeWidgetItem *item = m_ui->m_repositoriesView->currentItem(); if (item && !m_rootItems.contains(item)) { QTreeWidgetItem *parent = item->parent(); if (parent) { delete parent->takeChild(parent->indexOfChild(item)); if (parent == m_rootItems.value(1) && parent->childCount() <= 0) { useTmpRepositoriesOnly(false); m_ui->m_useTmpRepositories->setChecked(false); m_ui->m_useTmpRepositories->setEnabled(false); } } } } void SettingsDialog::useTmpRepositoriesOnly(bool use) { m_rootItems.at(0)->setDisabled(use); m_rootItems.at(2)->setDisabled(use); } void SettingsDialog::currentRepositoryChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) { Q_UNUSED(previous) if (current) { const int index = m_rootItems.at(0)->indexOfChild(current); m_ui->m_testRepository->setEnabled(!m_rootItems.contains(current)); m_ui->m_removeRepository->setEnabled(!current->data(0, Qt::UserRole).toBool()); m_ui->m_addRepository->setEnabled((current != m_rootItems.at(0)) & (index == -1)); } } // -- private void SettingsDialog::setupRepositoriesTreeWidget() { QTreeWidget *treeWidget = m_ui->m_repositoriesView; treeWidget->header()->setVisible(true); treeWidget->setHeaderLabels(QStringList() << QString() << tr("Use") << tr("Username") << tr("Password") << tr("Repository")); m_rootItems.append(new RepositoryItem(tr("Default repositories"))); m_rootItems.append(new RepositoryItem(tr("Temporary repositories"))); m_rootItems.append(new RepositoryItem(tr("User defined repositories"))); treeWidget->addTopLevelItems(m_rootItems); const Settings &settings = m_core->settings(); insertRepositories(settings.userRepositories(), m_rootItems.at(2)); insertRepositories(settings.defaultRepositories(), m_rootItems.at(0)); insertRepositories(settings.temporaryRepositories(), m_rootItems.at(1)); treeWidget->expandAll(); for (int i = 0; i < treeWidget->model()->columnCount(); ++i) treeWidget->resizeColumnToContents(i); treeWidget->header()->setSectionResizeMode(0, QHeaderView::Fixed); treeWidget->header()->setSectionResizeMode(1, QHeaderView::Fixed); treeWidget->header()->setMinimumSectionSize(treeWidget->columnWidth(1)); treeWidget->setItemDelegateForColumn(0, new PasswordDelegate(treeWidget)); treeWidget->setItemDelegateForColumn(1, new PasswordDelegate(treeWidget)); treeWidget->setItemDelegateForColumn(3, m_delegate = new PasswordDelegate(treeWidget)); m_delegate->showPasswords(false); m_delegate->disableEditing(false); } void SettingsDialog::insertRepositories(const QSet<Repository> repos, QTreeWidgetItem *rootItem) { rootItem->setFirstColumnSpanned(true); foreach (const Repository &repo, repos) { RepositoryItem *item = new RepositoryItem(repo); rootItem->addChild(item); item->setHidden(!ProductKeyCheck::instance()->isValidRepository(repo)); } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/settingsdialog.h����������������������������������������������������������������������������0000664�0000000�0000000�00000007353�13253666515�0015643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H #include <repository.h> #include <settings.h> #include <QDialog> #include <QStyledItemDelegate> #include <QTreeWidgetItem> QT_BEGIN_NAMESPACE class QAuthenticator; class QLocale; class QVariant; namespace Ui { class SettingsDialog; } QT_END_NAMESPACE namespace QInstaller { class PackageManagerCore; } // -- PasswordDelegate class PasswordDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit PasswordDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) , m_showPasswords(true) , m_disabledEditor(true) {} void showPasswords(bool show); void disableEditing(bool disable); protected: QString displayText(const QVariant &value, const QLocale &locale) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const; private: bool m_showPasswords; bool m_disabledEditor; }; // -- RepositoryItem class RepositoryItem : public QTreeWidgetItem { public: explicit RepositoryItem(const QString &label); explicit RepositoryItem(const QInstaller::Repository &repo); QVariant data(int column, int role) const; void setData(int column, int role, const QVariant &value); QSet<QInstaller::Repository> repositories() const; QInstaller::Repository repository() const { return m_repo; } void setRepository(const QInstaller::Repository &repo) { m_repo = repo; } private: QInstaller::Repository m_repo; }; // -- SettingsDialog class SettingsDialog : public QDialog { Q_OBJECT public: explicit SettingsDialog(QInstaller::PackageManagerCore *core, QWidget *parent = 0); public slots: void accept(); signals: void networkSettingsChanged(const QInstaller::Settings &settings); private slots: void addRepository(); void testRepository(); void updatePasswords(); void removeRepository(); void useTmpRepositoriesOnly(bool use); void currentRepositoryChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); private: void setupRepositoriesTreeWidget(); void insertRepositories(const QSet<QInstaller::Repository> repos, QTreeWidgetItem *rootItem); private: Ui::SettingsDialog *m_ui; PasswordDelegate *m_delegate; QInstaller::PackageManagerCore *m_core; bool m_showPasswords; QList<QTreeWidgetItem*> m_rootItems; }; #endif // SETTINGSDIALOG_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/settingsdialog.ui���������������������������������������������������������������������������0000664�0000000�0000000�00000031664�13253666515�0016033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>SettingsDialog</class> <widget class="QDialog" name="SettingsDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>491</width> <height>443</height> </rect> </property> <property name="windowTitle"> <string>Settings</string> </property> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> <widget class="QTabWidget" name="tabWidget"> <property name="currentIndex"> <number>0</number> </property> <widget class="QWidget" name="m_network"> <attribute name="title"> <string>Network</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QRadioButton" name="m_noProxySettings"> <property name="text"> <string>No proxy</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QRadioButton" name="m_systemProxySettings"> <property name="enabled"> <bool>true</bool> </property> <property name="text"> <string>System proxy settings</string> </property> <property name="checked"> <bool>false</bool> </property> </widget> </item> <item> <widget class="QRadioButton" name="m_manualProxySettings"> <property name="text"> <string>Manual proxy configuration</string> </property> </widget> </item> <item> <widget class="QWidget" name="m_rootWidget" native="true"> <layout class="QGridLayout" name="gridLayout_3"> <item row="0" column="0"> <widget class="QLabel" name="m_httpProxyLabel"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>HTTP proxy:</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QLineEdit" name="m_httpProxy"> <property name="enabled"> <bool>false</bool> </property> </widget> </item> <item row="0" column="2"> <widget class="QLabel" name="m_httpProxyPortLabel"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Port:</string> </property> </widget> </item> <item row="0" column="3"> <widget class="QSpinBox" name="m_httpProxyPort"> <property name="enabled"> <bool>false</bool> </property> <property name="maximum"> <number>65535</number> </property> </widget> </item> <item row="1" column="0"> <widget class="QWidget" name="m_httpAuthWidget" native="true"> <property name="enabled"> <bool>false</bool> </property> <layout class="QGridLayout" name="gridLayout_2"> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> </layout> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="m_ftpProxyLabel"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>FTP proxy:</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QLineEdit" name="m_ftpProxy"> <property name="enabled"> <bool>false</bool> </property> </widget> </item> <item row="2" column="2"> <widget class="QLabel" name="m_ftpProxyPortLabel"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>Port:</string> </property> </widget> </item> <item row="2" column="3"> <widget class="QSpinBox" name="m_ftpProxyPort"> <property name="enabled"> <bool>false</bool> </property> <property name="maximum"> <number>65535</number> </property> </widget> </item> <item row="3" column="0"> <widget class="QWidget" name="m_ftpAuthWidget" native="true"> <property name="enabled"> <bool>false</bool> </property> <layout class="QGridLayout" name="gridLayout"> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> </layout> </widget> </item> <item row="3" column="2"> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>0</height> </size> </property> </spacer> </item> </layout> </widget> </item> </layout> </widget> <widget class="QWidget" name="m_repositories"> <attribute name="title"> <string>Repositories</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QLabel" name="m_httpAuthLabel"> <property name="text"> <string>Add Username and Password for authentication if needed.</string> </property> </widget> </item> <item> <widget class="QTreeWidget" name="m_repositoriesView"> <column> <property name="text"> <string notr="true">1</string> </property> </column> </widget> </item> <item> <widget class="QCheckBox" name="m_useTmpRepositories"> <property name="text"> <string>Use temporary repositories only</string> </property> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QPushButton" name="m_addRepository"> <property name="text"> <string>Add</string> </property> </widget> </item> <item> <widget class="QPushButton" name="m_removeRepository"> <property name="text"> <string>Remove</string> </property> </widget> </item> <item> <widget class="QPushButton" name="m_testRepository"> <property name="text"> <string>Test</string> </property> </widget> </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="m_showPasswords"> <property name="text"> <string>Show Passwords</string> </property> </widget> </item> </layout> </item> </layout> </widget> </widget> </item> <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> </layout> </widget> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>SettingsDialog</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel"> <x>269</x> <y>422</y> </hint> <hint type="destinationlabel"> <x>157</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>SettingsDialog</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel"> <x>337</x> <y>422</y> </hint> <hint type="destinationlabel"> <x>286</x> <y>274</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_httpProxyLabel</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>104</x> <y>74</y> </hint> <hint type="destinationlabel"> <x>121</x> <y>97</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_httpProxy</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>232</x> <y>77</y> </hint> <hint type="destinationlabel"> <x>232</x> <y>97</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_httpProxyPort</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>392</x> <y>74</y> </hint> <hint type="destinationlabel"> <x>392</x> <y>96</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_httpProxyPortLabel</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>349</x> <y>78</y> </hint> <hint type="destinationlabel"> <x>347</x> <y>96</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_ftpProxyPortLabel</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>336</x> <y>77</y> </hint> <hint type="destinationlabel"> <x>336</x> <y>241</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_ftpProxyLabel</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>61</x> <y>76</y> </hint> <hint type="destinationlabel"> <x>109</x> <y>243</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_ftpProxy</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>204</x> <y>78</y> </hint> <hint type="destinationlabel"> <x>203</x> <y>248</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_ftpProxyPort</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>406</x> <y>78</y> </hint> <hint type="destinationlabel"> <x>380</x> <y>252</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_httpAuthWidget</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>54</x> <y>79</y> </hint> <hint type="destinationlabel"> <x>53</x> <y>179</y> </hint> </hints> </connection> <connection> <sender>m_manualProxySettings</sender> <signal>toggled(bool)</signal> <receiver>m_ftpAuthWidget</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>73</x> <y>76</y> </hint> <hint type="destinationlabel"> <x>56</x> <y>298</y> </hint> </hints> </connection> </connections> </ui> ����������������������������������������������������������������������������src/sdk/tabcontroller.cpp���������������������������������������������������������������������������0000664�0000000�0000000�00000014072�13253666515�0016024�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "tabcontroller.h" #include "installerbasecommons.h" #include "settingsdialog.h" #include <packagemanagercore.h> #include <productkeycheck.h> #include <QtCore/QTimer> using namespace QInstaller; // -- TabController::Private class TabController::Private { public: Private(); ~Private(); bool m_init; QString m_controlScript; QHash<QString, QString> m_params; Settings m_settings; bool m_networkSettingsChanged; QInstaller::PackageManagerGui *m_gui; QInstaller::PackageManagerCore *m_core; }; TabController::Private::Private() : m_init(false) , m_networkSettingsChanged(false) , m_gui(0) , m_core(0) { } TabController::Private::~Private() { delete m_gui; } // -- TabController TabController::TabController(QObject *parent) : QObject(parent) , d(new Private) { } TabController::~TabController() { d->m_core->writeMaintenanceTool(); delete d; } void TabController::setGui(QInstaller::PackageManagerGui *gui) { d->m_gui = gui; connect(d->m_gui, &PackageManagerGui::gotRestarted, this, &TabController::restartWizard); } void TabController::setControlScript(const QString &script) { d->m_controlScript = script; } void TabController::setManager(QInstaller::PackageManagerCore *core) { d->m_core = core; } void TabController::setManagerParams(const QHash<QString, QString> ¶ms) { d->m_params = params; } // -- public slots int TabController::init() { if (!d->m_init) { d->m_init = true; // this should called as early as possible, to handle error message boxes for example if (!d->m_controlScript.isEmpty()) { d->m_gui->loadControlScript(d->m_controlScript); qDebug() << "Using control script:" << d->m_controlScript; } connect(d->m_gui, &QWizard::currentIdChanged, this, &TabController::onCurrentIdChanged); connect(d->m_gui, &PackageManagerGui::settingsButtonClicked, this, &TabController::onSettingsButtonClicked); } IntroductionPage *page = qobject_cast<IntroductionPage*> (d->m_gui->page(PackageManagerCore::Introduction)); if (page) { page->setMessage(QString()); page->setErrorMessage(QString()); page->onCoreNetworkSettingsChanged(); } d->m_gui->restart(); d->m_gui->setVisible(!d->m_gui->isSilent()); onCurrentIdChanged(d->m_gui->currentId()); return PackageManagerCore::Success; } // -- private slots void TabController::restartWizard() { if (d->m_networkSettingsChanged) { d->m_core->reset(d->m_params); d->m_networkSettingsChanged = false; d->m_core->settings().setFtpProxy(d->m_settings.ftpProxy()); d->m_core->settings().setHttpProxy(d->m_settings.httpProxy()); d->m_core->settings().setProxyType(d->m_settings.proxyType()); d->m_core->settings().setUserRepositories(d->m_settings.userRepositories()); d->m_core->settings().setDefaultRepositories(d->m_settings.defaultRepositories()); d->m_core->settings().setTemporaryRepositories(d->m_settings.temporaryRepositories(), d->m_settings.hasReplacementRepos()); d->m_core->networkSettingsChanged(); } // Make sure we are writing the .dat file with the list of uninstall operations already now. // Otherwise we will write at the end of the next updater run, with a potentially // empty component list (if no updates are found). d->m_core->writeMaintenanceTool(); // restart and switch back to intro page QTimer::singleShot(0, this, &TabController::init); } void TabController::onSettingsButtonClicked() { SettingsDialog dialog(d->m_core); connect(&dialog, &SettingsDialog::networkSettingsChanged, this, &TabController::onNetworkSettingsChanged); dialog.exec(); if (d->m_networkSettingsChanged) { d->m_core->setCanceled(); IntroductionPage *page = qobject_cast<IntroductionPage*> (d->m_gui->page(PackageManagerCore::Introduction)); if (page) { page->setMessage(QString()); page->setErrorMessage(QString()); } restartWizard(); } } void TabController::onCurrentIdChanged(int newId) { if (d->m_gui) { if (PackageManagerPage *page = qobject_cast<PackageManagerPage *>(d->m_gui->page(newId))) d->m_gui->showSettingsButton(page->settingsButtonRequested()); } } void TabController::onNetworkSettingsChanged(const QInstaller::Settings &settings) { d->m_settings = settings; d->m_networkSettingsChanged = true; } void TabController::updateManagerParams(const QString &key, const QString &value) { d->m_params.insert(key, value); } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/tabcontroller.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000004450�13253666515�0015470�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef TABCONTROLLER_H #define TABCONTROLLER_H #include <QtCore/QHash> #include <QtCore/QObject> namespace QInstaller { class PackageManagerGui; class PackageManagerCore; class Settings; } class IntroductionPageImpl; class TabController : public QObject { Q_OBJECT Q_DISABLE_COPY(TabController) public: explicit TabController(QObject *parent = 0); ~TabController(); void setGui(QInstaller::PackageManagerGui *gui); void setManager(QInstaller::PackageManagerCore *core); void setManagerParams(const QHash<QString, QString> ¶ms); void setControlScript(const QString &script); public Q_SLOTS: int init(); void updateManagerParams(const QString &key, const QString &value); private Q_SLOTS: void restartWizard(); void onSettingsButtonClicked(); void onCurrentIdChanged(int newId); void onNetworkSettingsChanged(const QInstaller::Settings &settings); private: class Private; Private *const d; }; #endif // TABCONTROLLER_H ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/�������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0015163�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/README�������������������������������������������������������������������������0000664�0000000�0000000�00000001273�13253666515�0016046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������You need to have a Qt translation for your new language, otherwise your language won't be loaded at runtime. To add a new language: 1) Run 'cd src/sdk/translations' (change to the parent directory of this file) 2) Run 'make ts-untranslated' 3) Rename ifw_untranslated.ts to ifw_<lang>.ts 4) Run 'make qmake' 5) Do your translation. Just run 'make' whenever you want to test it. 6) Run 'make commit-ts' 7) Submit the translation for review To update an existing translation, just run 'make ts-<lang>' instead of steps 2) to 4). More information is available at https://wiki.qt.io/Qt_Localization - these instructions apply here equally, except for the different directory names. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/ifw_da.ts����������������������������������������������������������������������0000664�0000000�0000000�00000304442�13253666515�0016773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="da"> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>%1 hos %2</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>Proxyen krÃĶver autentifikation.</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>Kan ikke sÃļge til %1 for at lÃĶse handlingsdataene.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>Kan ikke sÃļge til %1 for at lÃĶse ressourcesamlingsblokken.</translation> </message> <message> <source>Cannot open meta resource %1.</source> <translation>Kan ikke ÃĨbne meta-ressourcen %1.</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>Kan ikke sÃļge til %1 for at lÃĶse antallet af indlejret meta-data.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation>Kan ikke sÃļge til %1 for at lÃĶse ressourcesamlingssegmentet.</translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>Uventet uoverensstemmelse af meta-ressourcer. LÃĶste %1, ventede: %2.</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>Http-autentifikation krÃĶvet</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>Du skal angive brugernavn og adgangskode for at tilgÃĨ dette sted.</translation> </message> <message> <source>Username:</source> <translation>Brugernavn:</translation> </message> <message> <source>Password:</source> <translation>Adgangskode:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 hos %2</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path "%1" exists but is not a directory.</source> <translation>Stien "%1" findes men er ikke en mappe.</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>Kan ikke oprette mappen "%1".</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1.</source> <translation>Kan ikke hente stien af arkivposten %1.</translation> </message> <message> <source>Cannot remove already existing symlink %1.</source> <translation>Kan ikke fjerne allerede eksisterende symlink %1.</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> <message> <source>Cannot create symlink at "%1". Another one is already existing.</source> <translation>Kan ikke oprette symlink ved "%1". Et andet findes allerede.</translation> </message> <message> <source>Cannot read symlink target from file "%1".</source> <translation>Kan ikke lÃĶse symlink-mÃĨl fra filen "%1".</translation> </message> <message> <source>Cannot create symlink at %1: %2</source> <translation>Kan ikke oprette symlink ved %1: %2</translation> </message> </context> <context> <name>InstallerBase</name> <message> <source>Waiting for %1</source> <translation>Venter pÃĨ %1</translation> </message> <message> <source>Another %1 instance is already running. Wait until it finishes, close it, or restart your system.</source> <translation>En anden %1-instans kÃļrer allerede. Vent til den er fÃĶrdig, luk den eller genstart dit system.</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>Komponenter tilfÃļjet som automatiske afhÃĶngigheder:</translation> </message> <message> <source>Components added as dependency for "%1":</source> <translation>Komponenter tilfÃļjet som afhÃĶngighed til "%1":</translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>Komponenter som har lÃļste afhÃĶngigheder:</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>Valgte komponenter uden afhÃĶngigheder:</translation> </message> <message> <source>Recursion detected, component "%1" already added with reason: "%2"</source> <translation>Rekursion registreret, komponenten "%1" allerede tilfÃļjet med begrundelsen: "%2"</translation> </message> <message> <source>Cannot find missing dependency "%1" for "%2".</source> <translation>Kan ikke finde manglende afhÃĶngighed "%1" til "%2".</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>Annulleret</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file "%1": %2</source> <translation>Kan ikke sikkerhedskopiere filen "%1": %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> <message> <source>Cannot find backup file for "%1".</source> <translation>Kan ikke finde sikkerhedskopieret fil for "%1".</translation> </message> <message> <source>Cannot restore backup file for "%1".</source> <translation>Kan ikke genskabe sikkerhedskopieret fil for "%1".</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>Kan ikke genskabe sikkerhedskopieret fil for "%1": %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot backup file "%1".</source> <translation>Kan ikke sikkerhedskopiere filen "%1".</translation> </message> <message> <source>Cannot copy a non-existent file: %1</source> <translation>Kan ikke kopiere en ikke-eksisterende fil: %1</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>Kan ikke fjerne filen "%1": %2</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>Kan ikke kopiere filen "%1" til "%2": %3</translation> </message> <message> <source>Cannot delete file "%1": %2</source> <translation>Kan ikke slette filen "%1": %2</translation> </message> <message> <source>Cannot restore backup file into "%1": %2</source> <translation>Kan ikke genskabe sikkerhedskopieret fil ind i "%1": %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of file "%1": %2</source> <translation>Kan ikke oprette sikkerhedskopi af filen "%1": %2</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>Kan ikke genskabe sikkerhedskopieret fil for "%1": %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download finished.</source> <translation>Download fÃĶrdig.</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>Kryptografiske hashes matcher ikke.</translation> </message> <message> <source>Download canceled.</source> <translation>Download annulleret.</translation> </message> <message> <source>%1 of %2</source> <translation>%1 af %2</translation> </message> <message> <source>%1 downloaded.</source> <translation>%1 downloadet.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/sek.)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n dag, </numerusform> <numerusform>%n dage, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n time, </numerusform> <numerusform>%n timer, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n minut</numerusform> <numerusform>%n minutter</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n sekund</numerusform> <numerusform>%n sekunder</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 tilbage.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - ukendt tid tilbage.</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1. Writing to file "%2" failed: %3</source> <translation>Kan ikke downloade %1. Skrivning til filen "%2" fejlede: %3</translation> </message> <message> <source>Cannot download %1. Cannot create file "%2": %3</source> <translation>Kan ikke downloade %1. Kan ikke oprette filen "%2": %3</translation> </message> <message> <source>%1 at %2</source> <translation>%1 hos %2</translation> </message> <message> <source>Authentication request canceled.</source> <translation>Autentifikationsanmodning annulleret.</translation> </message> <message> <source>Secure Connection Failed</source> <translation>Sikker forbindelse fejlede</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>Der opstod en fejl under forbindelse til: %1.</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>Der kan vÃĶre et problem med serverens konfiguration eller det kan vÃĶre nogen som prÃļve at udgive sig for at vÃĶre serveren. </translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>Hvis du fÃļrhen har oprettet forbindelse til denne server eller har tillid til denne server, kan fejlen vÃĶre midlertidig og du kan prÃļve igen.</translation> </message> <message> <source>Try again</source> <translation>PrÃļv igen</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <translation>Skrivning til filen "%1" fejlede: %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Cannot create directory "%1": %2</source> <translation>Kan ikke oprette mappen "%1": %2</translation> </message> <message> <source>Unknown error.</source> <translation>Ukendt fejl.</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>Kan ikke fjerne mappen "%1": %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file "%1".</source> <translation>Kan ikke sikkerhedskopiere filen "%1".</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>Kan ikke fjerne filen "%1": %2</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>Kan ikke kopiere filen "%1" til "%2": %3</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>Kan ikke fjerne filen "%1".</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>Kan ikke genskabe sikkerhedskopieret fil for "%1": %2</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file "%1": %2</source> <translation>Kan ikke sikkerhedskopiere filen "%1": %2</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> <message> <source>Cannot find backup file for "%1".</source> <translation>Kan ikke finde sikkerhedskopieret fil for "%1".</translation> </message> <message> <source>Cannot restore backup file for "%1".</source> <translation>Kan ikke genskabe sikkerhedskopieret fil for "%1".</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>Kan ikke genskabe sikkerhedskopieret fil for "%1": %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1": %2</source> <translation>Kan ikke lÃĶse ressourcefilen "%1": %2</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Cannot remove directory "%1": %2</source> <translation>Kan ikke fjerne mappen "%1": %2</translation> </message> <message> <source>The directory does not exist.</source> <translation>Mappen findes ikke.</translation> </message> <message> <source>Cannot recreate directory "%1": %2</source> <translation>Kan ikke genoprette mappen "%1": %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 startet</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>%1 kan ikke stoppes</translation> </message> <message> <source>Cannot stop task %1</source> <translation>Kan ikke stoppe opgaven %1</translation> </message> <message> <source>%1 cannot be paused</source> <translation>%1 kan ikke sÃĶttes pÃĨ pause</translation> </message> <message> <source>Cannot pause task %1</source> <translation>Kan ikke sÃĶtte opgaven %1 pÃĨ pause</translation> </message> <message> <source>Cannot resume task %1</source> <translation>Kan ikke genoptage opgaven %1</translation> </message> <message> <source>%1 done</source> <translation>%1 fÃĶrdig</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>Kan ikke tilgÃĨ pakkeinformationen til dette program.</translation> </message> <message> <source>No package sources set for this application.</source> <translation>Ingen pakkekilder sat til dette program.</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>%n opdatering fundet.</numerusform> <numerusform>%n opdateringer fundet.</numerusform> </translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>Downloader Updates.xml fra opdateringskilder.</translation> </message> <message> <source>Cannot download package source %1 from "%2".</source> <translation>Kan ikke downloade pakkekilden %1 fra "%2".</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>Updates.xml fil(er) downloadet fra opdateringskilder.</translation> </message> <message> <source>Computing applicable updates.</source> <translation>Udregner anvendelige opdateringer.</translation> </message> <message> <source>Application updates computed.</source> <translation>Programopdateringer udregnet.</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Updates.xml indeholder ugyldigt indhold: %1</translation> </message> <message> <source>Cannot read "%1"</source> <translation>Kan ikke lÃĶse "%1"</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Parse-fejl i %1 ved %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>Rod-elementet %1 uventet, skulle vÃĶre "Updates".</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>ApplicationName-element mangler.</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>ApplicationVersion-element mangler.</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>PackageUpdate-element uden Name</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>PackageUpdate-element uden Version</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>PackageUpdate-element uden ReleaseDate</translation> </message> </context> <context> <name>Lib7z</name> <message> <source>internal code: %1</source> <translation>intern kode: %1</translation> </message> <message> <source>not enough memory</source> <translation>ikke nok hukommelse</translation> </message> <message> <source>Error: %1</source> <translation>Fejl: %1</translation> </message> <message> <source>Cannot retrieve property %1 for item %2.</source> <translation>Kan ikke hente egenskaben %1 for posten %2.</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3.</source> <translation>Egenskaben %1 for posten %2 ikke af typen VT_FILETIME men %3.</translation> </message> <message> <source>Cannot convert UTC file time to system time.</source> <translation>Kan ikke konvertere UTC-filtid til systemets tid.</translation> </message> <message> <source>Cannot load codecs.</source> <translation>Kan ikke indlÃĶse codecs.</translation> </message> <message> <source>Cannot open archive "%1".</source> <translation>Kan ikke ÃĨbne arkivet "%1".</translation> </message> <message> <source>Cannot retrieve number of items in archive.</source> <translation>Kan ikke hente antal poster i arkiv.</translation> </message> <message> <source>Cannot retrieve path of archive item "%1".</source> <translation>Kan ikke hente stien af arkivposten "%1".</translation> </message> <message> <source>Unknown exception caught (%1).</source> <translation>Ukendt undtagelse fanget (%1).</translation> </message> <message> <source>Cannot create temporary file: %1</source> <translation>Kan ikke oprette midlertidig fil: %1</translation> </message> <message> <source>Unsupported archive type.</source> <translation>Ikke-understÃļttet arkivtype.</translation> </message> <message> <source>Cannot create archive "%1"</source> <translation>Kan ikke oprette arkivet "%1"</translation> </message> <message> <source>Cannot create archive "%1": %2</source> <translation>Kan ikke oprette arkivet "%1": %2</translation> </message> <message> <source>Cannot remove old archive "%1": %2</source> <translation>Kan ikke fjerne gamle arkiv "%1": %2</translation> </message> <message> <source>Cannot rename temporary archive "%1" to "%2": %3</source> <translation>Kan ikke omdÃļbe midlertidigt arkiv "%1" til "%2": %3</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Ukendt undtagelse fanget (%1)</translation> </message> </context> <context> <name>LocalPackageHub</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 indeholder ugyldigt indhold: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>Filen %1 findes ikke.</translation> </message> <message> <source>Cannot open %1.</source> <translation>Kan ikke ÃĨbne %1.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Parse-fejl i %1 ved %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>Rod-elementet %1 uventet, skulle vÃĶre 'Packages'.</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file "%1": %2</source> <translation>Kan ikke oprette lÃĨsfilen "%1": %2</translation> </message> <message> <source>Cannot write PID to lock file "%1": %2</source> <translation>Kan ikke skrive PID til lÃĨsfilen "%1": %2</translation> </message> <message> <source>Cannot obtain the lock for file "%1": %2</source> <translation>Kan ikke fÃĨ lÃĨsfilen for filen "%1": %2</translation> </message> <message> <source>Cannot release the lock for file "%1": %2</source> <translation>Kan ikke frigive lÃĨsfilen for filen "%1": %2</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>No marker found, stopped after %1.</source> <translation>Ingen markÃļr fundet, stoppet efter %1.</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>LÃĶsning fejlede efter %1 byte: %2</translation> </message> <message> <source>Copy failed: %1</source> <translation>Kopiering fejlede: %1</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Skrivning fejlede efter %1 byte: %2</translation> </message> <message> <source>bytes</source> <translation>byte</translation> </message> <message> <source>KiB</source> <translation>KiB</translation> </message> <message> <source>MiB</source> <translation>MiB</translation> </message> <message> <source>GiB</source> <translation>GiB</translation> </message> <message> <source>TiB</source> <translation>TiB</translation> </message> <message> <source>PiB</source> <translation>PiB</translation> </message> <message> <source>EiB</source> <translation>EiB</translation> </message> <message> <source>ZiB</source> <translation>ZiB</translation> </message> <message> <source>YiB</source> <translation>YiB</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>Kan ikke fjerne filen "%1": %2</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>Kan ikke fjerne mappen "%1": %2</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>Kan ikke oprette mappen "%1".</translation> </message> <message> <source>Cannot copy file from "%1" to "%2": %3</source> <translation>Kan ikke kopiere filen fra "%1" til "%2": %3</translation> </message> <message> <source>Cannot move file from "%1" to "%2": %3</source> <translation>Kan ikke flytte filen fra "%1" til "%2": %3</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>Kan ikke oprette mappen "%1": %2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>Kan ikke ÃĨbne midlertidig fil: %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>Kan ikke ÃĨbne midlertidig fil til skabelonen %1: %2</translation> </message> <message> <source>Corrupt installation</source> <translation>Ødelagt installation</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>Din installation ser ud til at vÃĶre Ãļdelagt. Overvej venligst at geninstallere from bunden.</translation> </message> <message> <source>The specified module could not be found.</source> <translation>Det specificerede modul blev ikke fundet.</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Components cannot have children in updater mode.</source> <translation>Komponenter mÃĨ ikke have bÃļrn i opdateringstilstand.</translation> </message> <message> <source>Cannot open the requested UI file "%1": %2</source> <translation>Kan ikke ÃĨbne den anmodede UI-fil "%1": %2</translation> </message> <message> <source>Cannot load the requested UI file "%1": %2</source> <translation>Kan ikke indlÃĶse den anmodede UI-fil "%1": %2</translation> </message> <message> <source>Cannot open the requested license file "%1": %2</source> <translation>Kan ikke ÃĨbne den anmodede licensfil "%1": %2</translation> </message> <message> <source>Error</source> <translation>Fejl</translation> </message> <message> <source>Error: Operation %1 does not exist.</source> <translation>Fejl: handlingen %1 findes ikke.</translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>Kan ikke lÃļse isDefault i %1</translation> </message> <message> <source>Update Info: </source> <translation>Opdateringsinfo:</translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component is marked for installation.</source> <translation>Komponenten er mÃĶrket til installation.</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>Komponenten er mÃĶrket til afinstallation.</translation> </message> <message> <source>Component is installed.</source> <translation>Komponenten er installeret.</translation> </message> <message> <source>Component is not installed.</source> <translation>Komponenten er ikke installeret.</translation> </message> <message> <source>Component Name</source> <translation>Komponentnavn</translation> </message> <message> <source>Action</source> <translation>Handling</translation> </message> <message> <source>Installed Version</source> <translation>Installeret version</translation> </message> <message> <source>New Version</source> <translation>Ny version</translation> </message> <message> <source>Release Date</source> <translation>Udgivelsesdato</translation> </message> <message> <source>Size</source> <translation>StÃļrrelse</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translation>Alt+S</translation> </message> <message> <source>Def&ault</source> <translation>&Standard</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translation>Alt+N</translation> </message> <message> <source>&Reset</source> <translation>&Nulstil</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translation>Alt+V</translation> </message> <message> <source>&Select All</source> <translation>&VÃĶlg alle</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translation>Alt+F</translation> </message> <message> <source>&Deselect All</source> <translation>&FravÃĶlg alle</translation> </message> <message> <source>To install new compressed repository, browse the repositories from your computer</source> <translation>Gennemse repositorierne fra din computer, for at installere nyt komprimeret repository</translation> </message> <message> <source>&Browse QBSP files</source> <translation>&Gennemse QBSP-filer</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source> <translation>VÃĶlg de komponenter du vil installere. FravÃĶlg installeret komponenter for at afinstallere dem. Komponenter som allerede er installeret vil ikke blive opdateret.</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>Denne komponent vil optage cirka %1 pÃĨ dit harddisk-drev.</translation> </message> <message> <source>Open File</source> <translation>Åbn fil</translation> </message> <message> <source>Select Components</source> <translation>VÃĶlg komponenter</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>VÃĶlg venligst de komponenter du vil opdatere.</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>VÃĶlg venligst de komponenter du vil installere.</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>VÃĶlg venligst de komponenter du vil afinstallere.</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source><to be saved installer key name> <executable> [argument1] [argument2] [...]</source> <translation><installer-nÃļglenavn som skal gammes> <eksekverbar> [argument1] [argument2] [...]</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>NÃļdvendigt installer-objekt i %1-handling er tomt.</translation> </message> <message> <source>Cannot save the output of "%1" to an empty installer key value.</source> <translation>Kan ikke gemme outputtet fra "%1" til en tom installer-nÃļglevÃĶrdi.</translation> </message> <message> <source>File "%1" does not exist or is not an executable binary.</source> <translation>Filen "%1" findes ikke eller er ikke en eksekverbar binÃĶr.</translation> </message> <message> <source>Running "%1" resulted in a crash.</source> <translation>KÃļrsel af "%1" resulterede i at det holdt op med at virke.</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source><source> <target> ["forceOverwrite"]</source> <translation><kilde> <mÃĨl> ["forceOverwrite"]</translation> </message> <message> <source>Invalid argument in %1: Third argument needs to be forceOverwrite, if specified.</source> <translation>Ugyldigt argument i %1: tredje argument skal vÃĶre forceOverwrite, hvis specificeret.</translation> </message> <message> <source>Invalid argument in %1: Directory "%2" is invalid.</source> <translation>Ugyldigt argument i %1: mappen "%2" er ugyldig.</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>Kan ikke oprette mappen "%1".</translation> </message> <message> <source>Failed to overwrite "%1".</source> <translation>Kunne ikke overskrive "%1".</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>Kan ikke kopiere filen "%1" til "%2": %3</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>Kan ikke fjerne filen "%1".</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation>Ugyldigt antal poster i opgave.</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <translation>Skrivning til filen "%1" fejlede: %2</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Cannot backup file "%1": %2</source> <translation>Kan ikke sikkerhedskopiere filen "%1": %2</translation> </message> <message> <source>Failed to overwrite file "%1".</source> <translation>Kunne ikke overskrive filen "%1".</translation> </message> <message> <source>Cannot write desktop entry to "%1".</source> <translation>Kan ikke skrive skrivebordspost til "%1".</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Cannot create link from "%1" to "%2".</source> <translation>Kan ikke oprette link fra "%1" til "%2".</translation> </message> <message> <source>Cannot remove link from "%1" to "%2".</source> <translation>Kan ikke fjerne link fra "%1" til "%2".</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set permissions for file "%1".</source> <translation>Kan ikke sÃĶtte tilladelser for filen "%1".</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>Kan ikke fjerne filen "%1": %2</translation> </message> <message> <source>Cannot move file "%1" to "%2": %3</source> <translation>Kan ikke flytte filen "%1" til "%2": %3</translation> </message> <message> <source>Installer at "%1" needs to be an offline one.</source> <translation>Installeren ved "%1" skal vÃĶre af typen offline.</translation> </message> <message> <source>Cannot open file "%1" for reading.</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning.</translation> </message> <message> <source>Cannot read file "%1": %2</source> <translation>Kan ikke lÃĶse filen "%1": %2</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> <message> <source>Cannot create target directory: "%1".</source> <translation>Kan ikke oprette mÃĨl-mappen: "%1".</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>Ukendt undtagelse fanget: %1.</translation> </message> <message> <source>Removing file "%1".</source> <translation>Fjerner filen "%1".</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>Kan ikke fjerne filen "%1".</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>Kan ikke fjerne mappen "%1": %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source><target> <link location> [target arguments] ["workingDirectory=..."] ["iconPath=..."] ["iconId=..."] ["description=..."]</source> <translation><mÃĨl> <linkplacering> [mÃĨl-argumenter] ["workingDirectory=..."] ["iconPath=..."] ["iconId=..."] ["description=..."]</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>Kan ikke oprette mappen "%1": %2</translation> </message> <message> <source>Failed to overwrite "%1": %2</source> <translation>Kunne ikke overskrive "%1": %2</translation> </message> <message> <source>Cannot create link "%1": %2</source> <translation>Kan ikke oprette linket "%1": %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>Annulleret</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>Download af hash-signatur fejlede.</translation> </message> <message> <source>Download Error</source> <translation>Download-fejl</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>Hash-verifikation under download fejlede. Dette er en midlertidig fejl, prÃļv venligst igen.</translation> </message> <message> <source>Cannot verify Hash</source> <translation>Kan ikke verificere hash</translation> </message> <message> <source>Cannot download archive %1: %2</source> <translation>Kan ikke downloade arkivet %1: %2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>Kan ikke hente arkiver: %1 Fejl under indlÃĶsning af %2</translation> </message> <message> <source>Downloading archive "%1" for component %2.</source> <translation>Downloader arkivet "%1" til komponenten %2.</translation> </message> <message> <source>Scheme %1 not supported (URL: %2).</source> <translation>Skemaet %1 understÃļttes ikke (URL: %2).</translation> </message> <message> <source>Cannot find component for %1.</source> <translation>Kan ikke finde komponent til %1.</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Target file "%1" already exists but is not a file.</source> <translation>MÃĨl-filen "%1" findes allerede men er ikke en fil.</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> <message> <source>File "%1" not open for writing: %2</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Filen "%1" er ikke ÃĨben til skrivning: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Skrivning til filen "%1" fejlede: %2</translation> </message> <message> <source>Redirect loop detected for "%1".</source> <translation>OmdirigeringslÃļkke registreret for "%1".</translation> </message> <message> <source>Checksum mismatch detected for "%1".</source> <translation>Tjeksum uoverensstemmelse registreret for "%1".</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <translation>NetvÃĶrksfejl under download af '%1': %2.</translation> </message> <message> <source>Unknown network error while downloading "%1".</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>Ukendt netvÃĶrksfejl under download af "%1".</translation> </message> <message> <source>Network transfers canceled.</source> <translation>NetvÃĶrksoverfÃļrsler annulleret.</translation> </message> <message> <source>Pause and resume not supported by network transfers.</source> <translation>Pause og genoptag understÃļttes ikke af netvÃĶrksoverfÃļrsler.</translation> </message> <message> <source>Invalid source URL "%1": %2</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Ugyldig kilde-URL "%1": %2</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Cannot start detached: "%1"</source> <translation>Kan ikke starte afkoblet: "%1"</translation> </message> <message> <source>Cannot start: "%1": %2</source> <translation>Kan ikke starte: "%1": %2</translation> </message> <message> <source>Program crashed: "%1"</source> <translation>Programmet holdt op med at virke: "%1"</translation> </message> <message> <source>Execution failed (Unexpected exit code: %1): "%2"</source> <translation>Eksekvering fejlede (uventet afslutningskode: %1): "%2"</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open archive "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne arkivet "%1" til lÃĶsning: %2</translation> </message> <message> <source>Error while extracting archive "%1": %2</source> <translation>Fejl under udtrÃĶkning af arkivet "%1": %2</translation> </message> <message> <source>Unknown exception caught while extracting "%1".</source> <translation>Ukendt undtagelse fanget under udtrÃĶkning af "%1".</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Cannot get package manager core.</source> <translation>Kan ikke fÃĨ pakkehÃĨndterings-kerne.</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>Denne proces bÃļr stoppes inden der fortsÃĶttes: %1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>Disse processer bÃļr stoppes inden der fortsÃĶttes: %1</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%1 af %2</translation> </message> <message> <source>%1 received.</source> <translation>%1 modtaget.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/sek.)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n dag, </numerusform> <numerusform>%n dage, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n time, </numerusform> <numerusform>%n timer, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n minut</numerusform> <numerusform>%n minutter</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n sekund</numerusform> <numerusform>%n sekunder</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 tilbage.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - ukendt tid tilbage.</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>FuldfÃļrer %1 assistenten</translation> </message> <message> <source>Click %1 to exit the %2 Wizard.</source> <translation>Klik pÃĨ %1 for at afslutte %2 assistenten.</translation> </message> <message> <source>Restart</source> <translation>Genstart</translation> </message> <message> <source>Run %1 now.</source> <translation>KÃļr %1 nu.</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>%1 assistenten fejlede.</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable.</source> <translation>Indstillinger er ikke skrivbare.</translation> </message> <message> <source>Failed to write settings.</source> <translation>Kunne ikke skrive indstillinger.</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source><source path> [vendor prefix]</source> <translation><kildesti> [producent prÃĶfiks]</translation> </message> <message> <source>Invalid Argument: source directory must not be empty.</source> <translation>Ugyldigt argument: kildemappen mÃĨ ikke vÃĶre tom.</translation> </message> <message> <source>Cannot backup file "%1": %2</source> <translation>Kan ikke sikkerhedskopiere filen "%1": %2</translation> </message> <message> <source>Failed to overwrite "%1": %2</source> <translation>Kunne ikke overskrive "%1": %2</translation> </message> <message> <source>Failed to copy file "%1": %2</source> <translation>Kunne ikke kopiere filen "%1": %2</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>Kan ikke oprette mappen "%1": %2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>OpsÃĶtning - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>Velkommen til opsÃĶtningsassistenten for %1.</translation> </message> <message> <source>Add or remove components</source> <translation>TilfÃļj eller fjern komponenter</translation> </message> <message> <source>Update components</source> <translation>Opdater komponenter</translation> </message> <message> <source>Remove all components</source> <translation>Fjern alle komponenter</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>Henter information fra fjern-installationskilder...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>Mindst ÃĐt gyldigt og aktiveret repository krÃĶvet for at denne handling skal lykkedes.</translation> </message> <message> <source>No updates available.</source> <translation>Ingen tilgÃĶngelige opdateringer.</translation> </message> <message> <source> Only local package management available.</source> <translation> Kun lokal pakkehÃĨndtering tilgÃĶngeligt.</translation> </message> <message> <source>Quit</source> <translation>Afslut</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>Licensaftale</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translation>Alt+A</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translation>Alt+I</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>LÃĶs venligst fÃļlgende licensaftale. Du skal acceptere vilkÃĨrene i denne aftale for at fortsÃĶtte installationen.</translation> </message> <message> <source>I accept the license.</source> <translation>Jeg accepterer licensen.</translation> </message> <message> <source>I do not accept the license.</source> <translation>Jeg accepterer ikke licensen.</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>LÃĶs venligst fÃļlgende licensaftaler. Du skal acceptere vilkÃĨrene i disse aftaler for at fortsÃĶtte installationen.</translation> </message> <message> <source>I accept the licenses.</source> <translation>Jeg accepterer licenserne.</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>Jeg accepterer ikke licenserne.</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>Ingen licensfil fundet til kopiering.</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>NÃļdvendigt installer-objekt i %1-handling er tomt.</translation> </message> <message> <source>Can not write license file "%1".</source> <translation>Kan ikke skrive licensfilen "%1".</translation> </message> <message> <source>No license files found to delete.</source> <translation>Ingen licensfil fundet til sletning.</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>Manglende pakkehÃĨndterings-kernemotor.</translation> </message> <message> <source>Preparing meta information download...</source> <translation>Forbedreder download af meta-information...</translation> </message> <message> <source>Unpacking compressed repositories...</source> <translation>Udpakker komprimerede repositories...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>Download af meta-data annulleret.</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>Ukendt undtagelse under udtrÃĶkning.</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>Manglende proxy-loginoplysninger.</translation> </message> <message> <source>Authentication failed.</source> <translation>Autentifikation fejlede.</translation> </message> <message> <source>Unknown exception during download.</source> <translation>Ukendt undtagelse under download.</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>Henter meta-information fra fjern-repository...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>Kunne ikke hente repositories.</translation> </message> <message> <source>Extracting meta information...</source> <translation>UdtrÃĶkker meta-information...</translation> </message> <message> <source>Error while extracting archive "%1": %2</source> <translation>Fejl under udtrÃĶkning af arkivet "%1": %2</translation> </message> <message> <source>Unknown exception caught while extracting archive "%1".</source> <translation>Ukendt undtagelse fanget under udtrÃĶkning af arkivet "%1".</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source>Error writing Maintenance Tool</source> <translation>Fejl ved skrivning af vedligeholdelsesvÃĶrktÃļj</translation> </message> <message> <source> Downloading packages...</source> <translation> Downloader pakker...</translation> </message> <message> <source>Installation canceled by user.</source> <translation>Installation annulleret af bruger.</translation> </message> <message> <source>All downloads finished.</source> <translation>Alle downloads fÃĶrdige.</translation> </message> <message> <source>Cancelling the Installer</source> <translation>Annullerer installeren</translation> </message> <message> <source>Authentication Error</source> <translation>Autentifikationsfejl</translation> </message> <message> <source>Some components could not be removed completely because administrative rights could not be acquired: %1.</source> <translation>Nogle komponenter kunne ikke fjernes fuldstÃĶndigt da administrative rettigheder ikke kunne anskaffes: %1.</translation> </message> <message> <source>Unknown error.</source> <translation>Ukendt fejl.</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>Nogle komponenter kunne ikke fjernes fuldstÃĶndigt da der opstod en ukendt fejl.</translation> </message> <message> <source>Application not running in Package Manager mode.</source> <translation>Program kÃļrer ikke i pakkehÃĨndteringstilstand.</translation> </message> <message> <source>No installed packages found.</source> <translation>Ingen installeret pakker fundet.</translation> </message> <message> <source>Application running in Uninstaller mode.</source> <translation>Program kÃļrer i afinstallationstilstand.</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>Der er en vigtig opdatering tilgÃĶngelig, kÃļr venligst opdateringen fÃļrst.</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>Kan ikke lÃļse alle afhÃĶngigheder.</translation> </message> <message> <source>Components about to be removed.</source> <translation>Komponenter som er ved at blive fjernet.</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>Fejl under ophÃļjelse af adgangsrettigheder.</translation> </message> <message> <source>Error</source> <translation>Fejl</translation> </message> <message> <source>invalid</source> <translation>ugyldig</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Unresolved dependencies</source> <translation>UlÃļste afhÃĶngigheder</translation> </message> <message> <source>Error</source> <translation>Fejl</translation> </message> <message> <source>Access error</source> <translation>Adgangsfejl</translation> </message> <message> <source>Format error</source> <translation>Format-fejl</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>Kan ikke skrive installerens konfiguration til %1: %2</translation> </message> <message> <source>Stop Processes</source> <translation>Stop processer</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>Disse processer bÃļr stoppes inden der fortsÃĶttes: %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>Installation annulleret af bruger</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>Skriver vedligeholdelsesvÃĶrktÃļj.</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>Kunne ikke sÃļge i filen %1: %2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation>VedligeholdelsesvÃĶrktÃļjet er ikke et bundt</translation> </message> <message> <source>Cannot remove data file "%1": %2</source> <translation>Kan ikke fjerne data-filen "%1": %2</translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>Kan ikke skrive vedligeholdelsesvÃĶrktÃļjets data til %1: %2</translation> </message> <message> <source>Cannot write maintenance tool to "%1": %2</source> <translation>Kan ikke skrive vedligeholdelsesvÃĶrktÃļjet til "%1": %2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>Kan ikke skrive vedligeholdelsesvÃĶrktÃļjets binÃĶre data til %1: %2</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>Variablen 'TargetDir' ikke sat.</translation> </message> <message> <source>Preparing the installation...</source> <translation>KlargÃļr installationen...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>Det er ikke muligt at installere fra netvÃĶrksplacering</translation> </message> <message> <source>Creating local repository</source> <translation>Opretter lokal repository</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>Opretter vedligeholdelsesvÃĶrktÃļj</translation> </message> <message> <source> Installation finished!</source> <translation> Installation fÃĶrdig!</translation> </message> <message> <source> Installation aborted!</source> <translation> Installation afbrudt!</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>Det er ikke muligt at kÃļre handlingen fra en netvÃĶrksplacering</translation> </message> <message> <source>Removing deselected components...</source> <translation>Fjerner fravalgte komponenter...</translation> </message> <message> <source> Update finished!</source> <translation> Opdatering fÃĶrdig!</translation> </message> <message> <source> Update aborted!</source> <translation> Opdatering afbrudt!</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>Afinstallation fuldfÃļrt med succes.</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>Afinstallation afbrudt.</translation> </message> <message> <source> Installing component %1</source> <translation> Installerer komponenten %1</translation> </message> <message> <source>Installer Error</source> <translation>Installer-fejl</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>Fejl under installationsprocessen (%1): %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>Kan ikke klargÃļre afinstallation</translation> </message> <message> <source>Cannot start uninstall</source> <translation>Kan ikke starte afinstallation</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>Fejl under afinstallationsprocessen: %1</translation> </message> <message> <source>Unknown error</source> <translation>Ukendt fejl</translation> </message> <message> <source>Cannot retrieve remote tree %1.</source> <translation>Kan ikke hente fjern-trÃĶet %1.</translation> </message> <message> <source>Failure to read packages from %1.</source> <translation>Kunne ikke lÃĶse pakker fra %1.</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>Kan ikke hente meta-information: %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>Kan ikke tilfÃļje kildeinformation for midlertidig opdatering.</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>Kan ikke finde nogen kildeinformation for opdatering.</translation> </message> <message> <source>Dependency cycle between components "%1" and "%2" detected.</source> <translation>AfhÃĶngighedscyklus registreret mellem komponenterne "%1" og "%2".</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>%1 opsÃĶtning</translation> </message> <message> <source>Maintain %1</source> <translation>Vedligehold %1</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>Vil du annullere installationsprocessen?</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>Vil du annullere afinstallationsprocessen?</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>Vil du afslutte installationsprogrammet?</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>Vil du afslutte afinstallationsprogrammet?</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>Vil du afslutte vedligeholdelsesprogrammet?</translation> </message> <message> <source>%1 Question</source> <translation>%1 spÃļrgsmÃĨl</translation> </message> <message> <source>Settings</source> <translation>Indstillinger</translation> </message> <message> <source>Error</source> <translation>Fejl</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>Det er ikke muligt at installere fra netvÃĶrksplacering. KopiÃĐr venligst installeren til et lokalt drev</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>&Vis detaljer</translation> </message> <message> <source>&Hide Details</source> <translation>&Skjul detaljer</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>&Afinstaller</translation> </message> <message> <source>Uninstalling %1</source> <translation>Afinstallerer %1</translation> </message> <message> <source>&Update</source> <translation>&Opdater</translation> </message> <message> <source>Updating components of %1</source> <translation>Opdaterer komponenter af %1</translation> </message> <message> <source>&Install</source> <translation>&Installer</translation> </message> <message> <source>Installing %1</source> <translation>Installerer %1</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation>Dialog</translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>Proxyen %1 krÃĶver brugernavn og adgangskode.</translation> </message> <message> <source>Username:</source> <translation>Brugernavn:</translation> </message> <message> <source>Username</source> <translation>Brugernavn</translation> </message> <message> <source>Password:</source> <translation>Adgangskode:</translation> </message> <message> <source>Password</source> <translation>Adgangskode</translation> </message> <message> <source>Proxy Credentials</source> <translation>Proxy-loginoplysninger</translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>&Afinstaller</translation> </message> <message> <source>Ready to Uninstall</source> <translation>Klar til afinstallation</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>OpsÃĶtningen er nu klar til at begynde fjernelsen af %1 fra din computer.<br><font color="red">Programmappen %2 slettes fuldstÃĶndigt</font>, inklusiv alt indhold i mappen!</translation> </message> <message> <source>U&pdate</source> <translation>&Opdater</translation> </message> <message> <source>Ready to Update Packages</source> <translation>Klar til opdateringspakker</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>OpsÃĶtningen er nu klar til at begynde opdateringen af din installation.</translation> </message> <message> <source>&Install</source> <translation>&Installer</translation> </message> <message> <source>Ready to Install</source> <translation>Klar til installation</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>OpsÃĶtningen er nu klar til at begynde installationen af %1 pÃĨ din computer.</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation. %1 are available, while %2 are at least required.</source> <translation>Ikke nok displads til at lagre midlertidige filer og installationen. %1 er tilgÃĶngelige, mens mindst %2 krÃĶves.</translation> </message> <message> <source>Not enough disk space to store all selected components! %1 are available while %2 are at least required.</source> <translation>Ikke nok displads til at lagre alle valgte komponenter! %1 er tilgÃĶngelige, mens mindst %2 krÃĶves.</translation> </message> <message> <source>Not enough disk space to store temporary files! %1 are available while %2 are at least required.</source> <translation>Ikke nok displads til at lagre midlertidige filer! %1 er tilgÃĶngelige, mens mindst %2 krÃĶves.</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>Det bind du har valg til installation ser ud til at have tilstrÃĶkkelig plads til installationen, men der vil efterfÃļlgende vÃĶre mindre end 1% af bindets plads tilgÃĶngeligt. %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>Det bind du har valg til installation ser ud til at have tilstrÃĶkkelig plads til installationen, men der vil efterfÃļlgende vÃĶre mindre end 100 MB tilgÃĶngeligt. %1</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>Installationen vil bruge %1 diskplads.</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source><extension> <command> [description [contentType [icon]]]</source> <translation><udvidelse> <kommando> [beskrivelse [indholdsType [ikon]]]</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>Tilknytning af filtyper understÃļttes kun i Windows.</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>Tilknytning af filtype: ugyldige argumenter</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Kan ikke lÃĶse alle data efter afsendelse af kommando: %1. Byte ventet: %2, byte modtaget: %3. Fejl: %4</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til lÃĶsning: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>Kan ikke ÃĨbne filen "%1" til skrivning: %2</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Cannot open resource %1 for reading.</source> <translation>Kan ikke ÃĨbne ressourcen %1 til lÃĶsning.</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>LÃĶsning fejlede efter %1 byte: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Skrivning fejlede efter %1 byte: %2</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>FuldfÃļrer opsÃĶtningsassistenten for %1</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open script file at %1: %2</source> <translation>Kan ikke ÃĨbne script-filen ved %1: %2</translation> </message> <message> <source>Exception while loading the component script "%1": %2</source> <translation>Undtagelse under indlÃĶsning af komponent-scriptet "%1": %2</translation> </message> <message> <source>Unknown error.</source> <translation>Ukendt fejl.</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in operation %1 is empty.</source> <translation>NÃļdvendigt installer-objekt i %1-handling er tomt.</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>Selv-genstart: kun gyldigt i opdaterings- eller pakkehÃĨndteringstilstand.</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>Selv-genstart: ugyldige argumenter</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>Serveren krÃĶver autentifikation</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>Du skal angive brugernavn og adgangskode for at tilgÃĨ dette sted.</translation> </message> <message> <source>Username:</source> <translation>Brugernavn:</translation> </message> <message> <source>Password:</source> <translation>Adgangskode:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 hos %2</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) "%1" calling %2 with arguments "%3".</source> <translation>Manglende argument(er) "%1" kalder %2 med argumenterne "%3".</translation> </message> <message> <source>Current method argument calling "%1" with arguments "%2" is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>Aktuelle metode-argument som kalder "%1" med argumenterne "%2" understÃļttes ikke. Brug venligst set, remove, add_array_value eller remove_array_value.</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>None of the arguments can be empty: source "%1", target "%2".</source> <translation>Ingen af argumenterne mÃĨ vÃĶre tomme: kilde "%1", mÃĨl "%2".</translation> </message> <message> <source>Cannot move file from "%1" to "%2", because the target path exists and is not removable.</source> <translation>Kan ikke flytte filen fra "%1" to "%2", da mÃĨl-stien findes og ikke er flytbar.</translation> </message> <message> <source>Cannot move file "%1" to "%2": %3</source> <translation>Kan ikke flytte filen "%1" til "%2": %3</translation> </message> <message> <source>Moving file "%1" to "%2".</source> <translation>Flytter filen "%1" til "%2".</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>Genveje i menuen Start</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new directory.</source> <translation>VÃĶlg hvor i menuen Start du Ãļnsker programmernes genveje oprettet. Du kan ogsÃĨ indtaste et navn for at oprette en ny mappe.</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>Installationsmappe</translation> </message> <message> <source>Please specify the directory where %1 will be installed.</source> <translation>Specificer venligst mappen hvor %1 skal installeres.</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translation>Alt+G</translation> </message> <message> <source>B&rowse...</source> <translation>&Gennemse...</translation> </message> <message> <source>The directory you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>Mappen du har valgt findes allerede og indeholder en installation. VÃĶlg et andet mÃĨl til installationen.</translation> </message> <message> <source>You have selected an existing, non-empty directory for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this directory as installation might fail. Do you want to continue?</source> <translation>Du har valgt en eksisterende, ikke-tom mappe til installation. BemÃĶrk at den vil blive fuldstÃĶndigt ryddet ved afinstallation af dette program. Det anbefales ikke at installere i denne mappe eftersom installationen kan fejle. Vil du fortsÃĶtte?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>Du har valgt en eksisterende fil eller symlink, vÃĶlg venligst et andet mÃĨl for installationen.</translation> </message> <message> <source>Select Installation Folder</source> <translation>VÃĶlg installationsmappe</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid directory.</source> <translation>Installationsstien mÃĨ ikke vÃĶre tom, specificer venligst en gyldig mappe.</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>Installationsstien mÃĨ ikke vÃĶre relativ, specificer venligst en absolut sti.</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>Stien eller installationsmappen indeholder ikke-ASCII-tegn. Det understÃļttes ikke i Ãļjeblikket! VÃĶlg venligst en anden sti eller installationsmappe.</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>Da installationsmappen er fuldstÃĶndigt slettet, er installation i %1 forbudt.</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>Stien du har indtastet er for lang, sÃļrg venligst for at specificere en gyldig sti.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>Stien du har indtastet er ikke gyldig, sÃļrg venligst for at specificere et gyldigt mÃĨl.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>Stien du har indtastet er ikke gyldig, sÃļrg venligst for at specificere et gyldigt drev.</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid directory.</source> <translation>Installationsstien mÃĨ ikke slutte med '.', specificer venligst en gyldig mappe.</translation> </message> <message> <source>The installation path must not contain "%1", please specify a valid directory.</source> <translation>Installationsstien mÃĨ ikke indholde "%1", specificer venligst en gyldig mappe.</translation> </message> <message> <source>Warning</source> <translation>Advarsel</translation> </message> <message> <source>Error</source> <translation>Fejl</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Missing package manager core engine.</source> <translation>Manglende pakkehÃĨndterings-kernemotor.</translation> </message> <message> <source>Empty repository URL.</source> <translation>Tom repository-URL.</translation> </message> <message> <source>Download canceled.</source> <translation>Download annulleret.</translation> </message> <message> <source>Timeout while testing repository "%1".</source> <translation>Timeout ved test af repository'et "%1".</translation> </message> <message> <source>Cannot parse Updates.xml: %1</source> <translation>Kan ikke parse Updates.xml: %1</translation> </message> <message> <source>Cannot open Updates.xml for reading: %1</source> <translation>Kan ikke ÃĨbne Updates.xml til lÃĶsning: %1</translation> </message> <message> <source>Authentication failed.</source> <translation>Autentifikation fejlede.</translation> </message> <message> <source>Unknown error while testing repository "%1".</source> <translation>Ukendt fejl ved test af repository'et "%1".</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>Godkendelse krÃĶves</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>Indtast din adgangskode til godkendelse for sudo:</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>Fejl ved anskaffelse af administrator rettigheder</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>Kan ikke fÃĨ godkendelse.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Please start the setup program as a user with the appropriate rights. Or accept the elevation of access rights if being asked.</source> <translation>Kan ikke fÃĨ godkendelse som er nÃļdvendigt for at fortsÃĶtte installationen. Start venligst opsÃĶtningsprogrammet som en bruger med de fornÃļdne rettigheder. Eller acceptÃĐr ophÃļjelsen af adgangsrettigheder hvis du bliver spurgt.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as a user with the appropriate rights and then clicking OK.</source> <translation>Kan ikke fÃĨ godkendelse som er nÃļdvendigt for at fortsÃĶtte installationen. Abryd enten installationen eller brug tilbagefaldslÃļsningen ved at kÃļre %1 som en bruger med de fornÃļdne rettigheder og klik sÃĨ pÃĨ OK.</translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>Kan ikke ÃĨbne ressourcen %1: %2</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>Kan ikke ÃĨbne indstillingsfilen %1 til lÃĶsning: %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>Indstillinger</translation> </message> <message> <source>Network</source> <translation>NetvÃĶrk</translation> </message> <message> <source>No proxy</source> <translation>Ingen proxy</translation> </message> <message> <source>System proxy settings</source> <translation>Systemets proxy-indstillinger</translation> </message> <message> <source>Manual proxy configuration</source> <translation>Manuel proxy-konfiguration</translation> </message> <message> <source>HTTP proxy:</source> <translation>HTTP-proxy:</translation> </message> <message> <source>Port:</source> <translation>Port:</translation> </message> <message> <source>FTP proxy:</source> <translation>FTP-proxy:</translation> </message> <message> <source>Repositories</source> <translation>Repositories</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>TilfÃļj brugernavn og adgangskode til autentifikation hvis det er nÃļdvendigt.</translation> </message> <message> <source>Use temporary repositories only</source> <translation>Brug kun midlertidige repositories</translation> </message> <message> <source>Add</source> <translation>TilfÃļj</translation> </message> <message> <source>Remove</source> <translation>Fjern</translation> </message> <message> <source>Test</source> <translation>Test</translation> </message> <message> <source>Show Passwords</source> <translation>Vis adgangskoder</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>TilvÃĶlg denne for at bruge repository under hentning.</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>TilfÃļj brugernavnet til at autentificere pÃĨ serveren.</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>TilfÃļj adgangskoden til at autentificere pÃĨ serveren.</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>Serverens URL som indeholder et gyldigt repository.</translation> </message> <message> <source>An error occurred while testing this repository.</source> <translation>Der opstod en fejl under test af dette repository.</translation> </message> <message> <source>The repository was tested successfully.</source> <translation>Repository'et blev testet med succes.</translation> </message> <message> <source>Do you want to disable the repository?</source> <translation>Vil du deaktivere repository'et? </translation> </message> <message> <source>Do you want to enable the repository?</source> <translation>Vil du aktivere repository'et?</translation> </message> <message> <source>Hide Passwords</source> <translation>Skjul adgangskoder</translation> </message> <message> <source>Use</source> <translation>Brug</translation> </message> <message> <source>Username</source> <translation>Brugernavn</translation> </message> <message> <source>Password</source> <translation>Adgangskode</translation> </message> <message> <source>Repository</source> <translation>Repository</translation> </message> <message> <source>Default repositories</source> <translation>Standard repositories</translation> </message> <message> <source>Temporary repositories</source> <translation>Midlertidige repositories</translation> </message> <message> <source>User defined repositories</source> <translation>Brugerdefinerede repositories</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable.</source> <translation>Registreringsdatabasestien %1 er ikke skrivbar.</translation> </message> <message> <source>Cannot write to registry path %1.</source> <translation>Kan ikke skrive til registreringsdatabasestien %1.</translation> </message> <message> <source>exactly %1</source> <translation>prÃĶcist %1</translation> </message> <message> <source>at least %1</source> <translation>mindst %1</translation> </message> <message> <source>not more than %1</source> <translation>ikke flere end %1</translation> </message> <message> <source>%1 or %2</source> <translation>%1 eller %2</translation> </message> <message> <source>%1 to %2</source> <translation>%1 til %2</translation> </message> <message numerus="yes"> <source>Invalid arguments in %1: %n arguments given, %2 arguments expected.</source> <translation> <numerusform>Ugyldigt argument i %1: %n argument givet, %2 argument ventet.</numerusform> <numerusform>Ugyldige argumenter i %1: %n argumenter givet, %2 argumenter ventet.</numerusform> </translation> </message> <message numerus="yes"> <source>Invalid arguments in %1: %n arguments given, %2 arguments expected in the form: %3.</source> <translation> <numerusform>Ugyldigt argument i %1: %n argument givet, %2 argument ventet i udformningen: %3.</numerusform> <numerusform>Ugyldige argumenter i %1: %n argumenter givet, %2 argumenter ventet i udformningen: %3.</numerusform> </translation> </message> <message> <source>Renaming file "%1" to "%2" failed: %3</source> <translation>OmdÃļbning af filen "%1" til "%2" fejlede: %3</translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/ifw_de.ts����������������������������������������������������������������������0000664�0000000�0000000�00000324453�13253666515�0017003�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="de_DE"> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>%1 auf %2</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>Proxy verlangt Autentifizierung.</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>Konnte nicht bis zur Anweisungsliste an Position %1 springen.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>Konnte nicht bis zur Resourcensammlung an Position %1 suchen.</translation> </message> <message> <source>Cannot open meta resource. Error: %1</source> <translation>Konnte Metainformationen nicht Ãķffnen. Fehlermeldung: %1</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>Konnte nicht bis %1 suchen, um die eingebettete Metadatenanzahl zu lesen.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation>Konnte nicht bis %1 suchen, um die Resourcensammlung zu lesen.</translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>Unerwarteter Unterschied zwischen Metadaten. %1 gelesen, %2 erwartet.</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>HTTP-Authentifizierung erforderlich</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>Benutzername und Passwort fÞr die Authentifizierung benÃķtigt.</translation> </message> <message> <source>Username:</source> <translation>Nutzername:</translation> </message> <message> <source>Password:</source> <translation>Passwort:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 auf %2</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path exists but is not a folder: %1</source> <translation>Pfad %1 existiert, aber ist kein Ordner.</translation> </message> <message> <source>Cannot create folder: %1</source> <translation>Konnte Ordner %1 nicht anlegen.</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Konnte Pfad des Archivs %1 nicht feststellen.</translation> </message> <message> <source>Cannot remove already existing symlink. %1</source> <translation>Konnte existierende VerknÞpfung (Symlink) %1 nicht entfernen.</translation> </message> <message> <source>Cannot open file: %1 (%2)</source> <translation>Konnte Datei %1 nicht Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot create symlink at '%1'. Another one is already existing.</source> <translation>Konnte VerknÞpfung (Symlink) '%1' nicht erstellen. Es existiert bereits eine an dieser Stelle.</translation> </message> <message> <source>Cannot read symlink target from file '%1'.</source> <translation>Konnte Ziel der VerknÞpfung (Symlink) '%1' nicht lesen.</translation> </message> <message> <source>Cannot create symlink at %1. %2</source> <translation>Konnte VerknÞpfung (Symlink) %1 nicht anlegen. Fehlermeldung: %2</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>Komponenten, die als automatische AbhÃĪngigkeiten hinzugefÞgt wurden:</translation> </message> <message> <source>Components added as dependency for '%1':</source> <translation>Komponenten, die als AbhÃĪngigkeiten fÞr '%1' hinzugefÞgt wurden:</translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>Komponenten, die aufgelÃķste AbhÃĪngigkeiten besitzen:</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>AusgewÃĪhlte Komponenten ohne AbhÃĪngigkeiten:</translation> </message> <message> <source>Recursion detected, component '%1' already added with reason: '%2'</source> <translation>Rekursion entdeckt, Komponente '%1' wurde bereits aufgrund von '%2' hinzugefÞgt</translation> </message> <message> <source>Cannot find missing dependency '%1' for '%2'.</source> <translation>Konnte fehlende AbhÃĪngigkeit '%1' fÞr '%2' nicht finden.</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>Abgebrochen</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file '%1': %2</source> <translation>Konnte keine Sperrdatei %1 anlegen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot write PID to lock file '%1': %2</source> <translation>Konnte PID nicht in die Sperrdatei %1 schreiben. Fehlermeldung: %2</translation> </message> <message> <source>Cannot obtain the lock for file '%1': %2</source> <translation>Konnte Sperre fÞr Datei '%1' nicht anlegen: '%2'</translation> </message> <message> <source>Cannot release the lock for file '%1': %2</source> <translation>Konnte Sperre fÞr Datei '%1' nicht aufheben: '%2'</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Konnte Datei %1 nicht sichern. Fehlermeldung: %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 2</source> <translation>genau 2</translation> </message> <message> <source>Cannot open file '%1' for writing: %2</source> <translation>Konnte Datei '%1' nicht zum Schreiben Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Konnte Sicherungsdatei fÞr %1 nicht finden.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Konnte Datei %1 nicht wiederherstellen.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Konnte Datei %1 nicht wiederherstellen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>UngÞltige Argumente: %1 Argumente erhalten, 2 erwartet.</translation> </message> <message> <source>Cannot backup file %1.</source> <translation>Konnte Datei %1 nicht sichern.</translation> </message> <message> <source>Cannot copy a non-existent file: %1</source> <translation>Konnte nicht existierende Datei nicht kopieren: %1</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Konnte Zieldatei %1 nicht entfernen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Konnte Datei %1 nicht nach %2 kopieren. Fehlermeldung: %3</translation> </message> <message> <source>Cannot delete file %1: %2</source> <translation>Konnte Datei %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot restore backup file into %1: %2</source> <translation>Konnte Datei %1 nicht wiederherstellen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of %1: %2</source> <translation>Konnte Datei %1 nicht sichern. Fehlermeldung: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>UngÞltige Argumente: %1 Argumente erhalten, 1 erwartet.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Konnte Datei %1 nicht wiederherstellen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download canceled.</source> <translation>Herunterladen abgebrochen.</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>PrÞfsummen stimmen nicht Þberein.</translation> </message> <message> <source>Download finished.</source> <translation>Heruntergeladen abgeschlossen.</translation> </message> <message> <source>%1 of %2</source> <translation>%1 von %2</translation> </message> <message> <source>%1 downloaded.</source> <translation>%1 heruntergeladen.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/s)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n Tag, </numerusform> <numerusform>%n Tage, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n Stunde, </numerusform> <numerusform>%n Stunden, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n Minute</numerusform> <numerusform>%n Minuten</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n Sekunde</numerusform> <numerusform>%n Sekunden</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 verbleibend.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - verbleibende Zeit unbekannt.</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1: Writing to file '%2' failed: %3</source> <translation>Konnte %1 nicht herunterladen. Schreiben in Datei '%2' fehlgeschlagen. Fehlermeldung: %3</translation> </message> <message> <source>Cannot download %1: Cannot create %2: %3</source> <translation>Konnte %1 nicht herunterladen. Erstellen der Datei %2 fehlgeschlagen. Fehlermeldung: %3</translation> </message> <message> <source>%1 at %2</source> <translation>%1 auf %2</translation> </message> <message> <source>Authentication request canceled.</source> <translation>Authentifizierung abgebrochen.</translation> </message> <message> <source>Secure Connection Failed</source> <translation>Sichere Verbindung fehlgeschlagen</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>Beim Aufbau der Verbindung zu '%1' ist ein Fehler aufgetreten.</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>Dies kann ein Fehler in der Konfiguration sein oder es versucht jemand, diesen Server vorzutÃĪuschen.</translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>Wenn frÞhere Verbindungen zu diesem Server erfolgreich waren, kann dieser Fehler temporÃĪr sein und bei einem erneuten Versuch verschwinden.</translation> </message> <message> <source>Try again</source> <translation>Erneut versuchen</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open source file '%1' for reading.</source> <translation>Konnte Ausgangsdatei '%1' nicht zum Lesen Ãķffnen.</translation> </message> <message> <source>Cannot open destination file '%1' for writing.</source> <translation>Konnte Zieldatei '%1' nicht zum Schreiben Ãķffnen.</translation> </message> <message> <source>Writing to %1 failed: %2</source> <translation>Konnte Datei %1 nicht zum Schreiben Ãķffnen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>UngÞltige Argumente: %1 Argumente erhalten, 1 erwartet.</translation> </message> <message> <source>Cannot create folder %1: Unknown error.</source> <translation>Konnte Ordner %1 nicht anlegen. Unbekannter Fehler.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Konnte Ordner %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file %1.</source> <translation>Konnte Datei %1 nicht sichern.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>UngÞltige Argumente: %1 Argumente erhalten, 2 erwartet.</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Konnte Zieldatei %1 nicht entfernen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Konnte Datei %1 nicht nach %2 kopieren. Fehlermeldung: %3</translation> </message> <message> <source>Cannot remove file %1.</source> <translation>Konnte Datei %1 nicht lÃķschen.</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Konnte Datei %1 nicht nach %2 kopieren. Fehlermeldung: %3</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Konnte Datei %1 nicht wiederherstellen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::PackagesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>Inhalt von Datei %1 ungÞltig: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>Datei %1 existiert nicht.</translation> </message> <message> <source>Cannot open %1.</source> <translation>Konnte Datei %1 nicht Ãķffnen.</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>Unerwartetes Wurzelelement %1, erwartet wird 'Packages'.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>UngÞltiges XML in Datei %1, Zeile %2, Spalte %3. Fehlermeldung: %4</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Konnte Datei %1 nicht sichern. Fehlermeldung: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>UngÞltige Argumente: %1 Argumente erhalten, 2 erwartet.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Konnte Datei %1 nicht zum Lesen Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Konnte Datei %1 nicht zum Schreiben Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Konnte Sicherungsdatei fÞr %1 nicht finden.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Konnte Datei %1 nicht wiederherstellen.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Konnte Datei %1 nicht wiederherstellen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1". Reason:</source> <translation>Konnte Ressourcendatei %1 nicht zum Lesen Ãķffnen. Grund:</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>UngÞltige Argumente: %1 Argumente erhalten, 1 erwartet.</translation> </message> <message> <source>Cannot remove folder %1: The folder does not exist.</source> <translation>Konnte Ordner %1 nicht entfernen. Der Ordner existiert nicht.</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Konnte Ordner %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot recreate directory %1: %2</source> <translation>Konnte Ordner %1 nicht wiederherstellen. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 gestartet</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>%1 kann nicht angehalten werden</translation> </message> <message> <source>Cannot stop task %1</source> <translation>%1 kann nicht angehalten werden</translation> </message> <message> <source>%1 cannot be paused</source> <translation>%1 kann nicht pausiert werden</translation> </message> <message> <source>Cannot pause task %1</source> <translation>%1 kann nicht pausiert werden</translation> </message> <message> <source>Cannot resume task %1</source> <translation>%1 kann nicht wiederaufgenommen werden</translation> </message> <message> <source>%1 done</source> <translation>%1 abgeschlossen</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>Konnte nicht auf die Paketinformationen dieser Anwendung zugreifen.</translation> </message> <message> <source>Cannot access the update sources information of this application.</source> <translation>Konnte nicht auf die Aktualisierungsinformationen dieser Anwendung zugreifen.</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>%n Aktualisierung gefunden.</numerusform> <numerusform>%n Aktualisierungen gefunden.</numerusform> </translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>Datei Updates.xml wird von der Aktualisierungsquelle heruntergeladen.</translation> </message> <message> <source>Cannot download update source %1 from ('%2')</source> <translation>Konnte Aktualisierungen nicht von %1 ('%2') herunterladen.</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>Datei Updates.xml von der Aktualisierungsquelle heruntergeladen.</translation> </message> <message> <source>Computing applicable updates.</source> <translation>Anwendbare Aktualisierungen werden ermittelt.</translation> </message> <message> <source>Application updates computed.</source> <translation>Anwendbare Aktualisierungen ermittelt.</translation> </message> </context> <context> <name>KDUpdater::UpdateSourcesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>Datei %1 enthÃĪlt ungÞltige Inhalte: %2</translation> </message> <message> <source>Cannot read "%1"</source> <translation>Konnte Datei "%1" nicht lesen.</translation> </message> <message> <source>XML Parse error in %1 at %2, %3: %4</source> <translation>UngÞltiges XML in Datei %1, Zeile %2, Spalte %3. Fehlermeldung: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "UpdateSources"</source> <translation>Unerwartetes Wurzelelement %1, erwartet wird "UpdateSources".</translation> </message> <message> <source>Cannot save changes to "%1": %2</source> <translation>Konnte Änderungen nicht in Datei %1 speichern. Fehlermeldung: %2</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Cannot read "%1"</source> <translation>Konnte Datei "%1" nicht lesen.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>UngÞltiges XML in Datei %1, Zeile %2, Spalte %3. Fehlermeldung: %4</translation> </message> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Inhalt von Updates.xml ungÞltig: %1</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>Unerwartetes Wurzelelement %1, erwartet wird "Updates".</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>Element ApplicationName fehlt.</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>Element ApplicationVersion fehlt.</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>Element "PackageUpdate" braucht ein Feld "Name".</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>Element "PackageUpdate" braucht ein Feld "Version".</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>Element "PackageUpdate" braucht ein Feld "ReleaseDate".</translation> </message> </context> <context> <name>Lib7z</name> <message> <source>Cannot retrieve number of items in archive</source> <translation>Konnte Anzahl Dateien im Archiv nicht feststellen.</translation> </message> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Konnte Pfad des Archivs %1 nicht feststellen.</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Unbekannte Ausnahmebedingung (%1).</translation> </message> <message> <source>internal code: %1</source> <translation>Interner Fehlercode: %1</translation> </message> <message> <source>not enough memory</source> <translation>nicht genug Speicher</translation> </message> <message> <source>Error: %1</source> <translation>Fehler: %1</translation> </message> <message> <source>Cannot load codecs</source> <translation>Konnte Codecs nicht laden.</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Konnte Standardformat nicht finden.</translation> </message> <message> <source>Cannot create archive %1. %2</source> <translation>Konnte kein Archiv %1 anlegen. Fehlermeldung: %2</translation> </message> <message> <source>CArc index %1 out of bounds [0, %2]</source> <translation>CArc Index %1 ausserhalb der Grenzen [0, %2].</translation> </message> <message> <source>Item index %1 out of bounds [0, %2]</source> <translation>Itemindex %1 ausserhalb der Grenzen [0, %2].</translation> </message> <message> <source>Cannot create output file for writing: %1</source> <translation>Konnte Ausgabedatei nicht zum Schreiben Ãķffnen. Fehlermeldung: %1</translation> </message> </context> <context> <name>Lib7z::ExtractItemJob</name> <message> <source>Cannot list archive: QIODevice not set or already destroyed.</source> <translation>Kann Archiv nicht anzeigen: QIODevice ist nicht gesetzt oder bereits zerstÃķrt.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Fehler beim Auspacken von '%1'. Fehlermeldung: %2</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Unbekannte Ausnahmebedingung (%1).</translation> </message> <message> <source>Failed</source> <translation>Fehlgeschlagen</translation> </message> </context> <context> <name>Lib7z::ListArchiveJob</name> <message> <source>Cannot list archive: QIODevice already destroyed.</source> <translation>Kann Archiv nicht anzeigen: QIODevice ist bereits zerstÃķrt.</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Unbekannte Ausnahmebedingung (%1).</translation> </message> <message> <source>Failed</source> <translation>Fehlgeschlagen</translation> </message> </context> <context> <name>OpenArchiveInfo</name> <message> <source>Cannot load codecs</source> <translation>Konnte Codecs nicht laden.</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Konnte Standardformat nicht finden.</translation> </message> <message> <source>Cannot open archive</source> <translation>Konnte Archiv nicht Ãķffnen.</translation> </message> <message> <source>No CArc found</source> <translation>Keine CArc gefunden.</translation> </message> </context> <context> <name>QIODeviceSequentialOutStream</name> <message> <source>No device set for output stream</source> <translation>Kein GerÃĪt fÞr den Ausgabestrom gesetzt</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>bytes</source> <translation>Bytes</translation> </message> <message> <source>KiB</source> <translation>KiB</translation> </message> <message> <source>MiB</source> <translation>MiB</translation> </message> <message> <source>GiB</source> <translation>GiB</translation> </message> <message> <source>TiB</source> <translation>TiB</translation> </message> <message> <source>PiB</source> <translation>PiB</translation> </message> <message> <source>EiB</source> <translation>EiB</translation> </message> <message> <source>ZiB</source> <translation>ZiB</translation> </message> <message> <source>YiB</source> <translation>YiB</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Konnte Datei %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Konnte Ordner %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot create folder %1</source> <translation>Konnte Ordner %1 nicht anlegen.</translation> </message> <message> <source>Cannot copy file from %1 to %2: %3</source> <translation>Konnte Datei %1 nicht nach %2 kopieren. Fehlermeldung: %3</translation> </message> <message> <source>Cannot move file from %1 to %2: %3</source> <translation>Konnte Datei %1 nicht nach %2 verschieben. Fehlermeldung: %3</translation> </message> <message> <source>Cannot create folder %1: %2</source> <translation>Konnte Ordner %1 nicht anlegen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>Konnte temporÃĪre Datei nicht Ãķffnen. Fehlermeldung: %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>Konnte keine temporÃĪre Datei fÞr die Vorlage %1 Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>No marker found, stopped after %1.</source> <translation>Keine Markierung gefunden, abgebrochen nach %1.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Konnte Datei %1 nicht zum Lesen Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Konnte Datei %1 nicht zum Schreiben Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>Das Lesen ist nach %1 Bytes fehlgeschlagen. Fehlermeldung: %2</translation> </message> <message> <source>Copy failed. Error: %1</source> <translation>Kopieren ist fehlgeschlagen. Fehlermeldung: %1</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Das Schreiben ist nach %1 Bytes fehlgeschlagen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot create temporary file</source> <translation>Konnte temporÃĪre Datei nicht anlegen.</translation> </message> <message> <source>Cannot retrieve property %1 for item %2</source> <translation>Konnte Eigenschaft %1 von %2 nicht erhalten.</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3</source> <translation>Eigenschaft %1 von %2 ist nicht vom Typ VT_FILETIME, sondern vom Typ %3.</translation> </message> <message> <source>Cannot convert file time to local time</source> <translation>Konnte die Dateizeit nicht in die lokale Zeit umwandeln.</translation> </message> <message> <source>Cannot convert local file time to system time</source> <translation>Konnte die lokale Dateizeit nicht in die Systemzeit umwandeln.</translation> </message> <message> <source>Corrupt installation</source> <translation>Installation beschÃĪdigt</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>Ihre Installation scheint beschÃĪdigt zu sein. Komplette Neuinstallation empfohlen.</translation> </message> <message> <source>The specified module could not be found.</source> <translation>Das angegebene Modul konnte nicht gefunden werden.</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Components cannot have children in updater mode.</source> <translation>Komponenten kÃķnnen im Updater-Modus keine Kinder haben.</translation> </message> <message> <source>Cannot open the requested translation file '%1'.</source> <translation>Konnte angeforderte Übersetzungsdatei %1 nicht Ãķffnen.</translation> </message> <message> <source>Cannot open the requested UI file '%1'. Error: %2</source> <translation>Konnte angeforderte UI-Datei '%1' nicht Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot load the requested UI file '%1'. Error: %2</source> <translation>Konnte angeforderte UI-Datei '%1' nicht laden. Fehlermeldung: %2</translation> </message> <message> <source>Cannot open the requested license file '%1'. Error: %2</source> <translation>Konnte angeforderte Lizenzdatei '%1' nicht Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Error</source> <translation>Fehler</translation> </message> <message> <source>Error: Operation %1 does not exist</source> <translation>Fehler: Anweisung %1 existiert nicht.</translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>Kann isDefault in %1 nicht auflÃķsen.</translation> </message> <message> <source>Update Info: </source> <translation>Aktualisierungsinformation: </translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component Name</source> <translation>Komponentenname</translation> </message> <message> <source>Action</source> <translation>Aktion</translation> </message> <message> <source>Installed Version</source> <translation>Installierte Version</translation> </message> <message> <source>New Version</source> <translation>Neue Version</translation> </message> <message> <source>Release Date</source> <translation>VerÃķffentlichungsdatum</translation> </message> <message> <source>Size</source> <translation>GrÃķße</translation> </message> <message> <source>Component is marked for installation.</source> <translation>Komponente wird installiert.</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>Komponente wird deinstalliert.</translation> </message> <message> <source>Component is installed.</source> <translation>Komponente ist installiert.</translation> </message> <message> <source>Component is not installed.</source> <translation>Komponente ist nicht installiert.</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translation>Alt+A</translation> </message> <message> <source>Def&ault</source> <translation>St&andard</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translation>Alt+Z</translation> </message> <message> <source>&Reset</source> <translation>&ZurÞcksetzen</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translation>Alt+S</translation> </message> <message> <source>&Select All</source> <translation>Alle au&swÃĪhlen</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translation>Alt+B</translation> </message> <message> <source>&Deselect All</source> <translation>Alle a&bwÃĪhlen</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>Diese Komponente wird ungefÃĪhr %1 auf Ihrer Festplatte belegen.</translation> </message> <message> <source>Select Components</source> <translation>Komponenten auswÃĪhlen</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>Bitte wÃĪhlen Sie die Komponenten aus, die Sie aktualisieren mÃķchten.</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>Bitte wÃĪhlen Sie die Komponenten aus, die Sie installieren mÃķchten.</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>Bitte wÃĪhlen Sie die Komponenten aus, die Sie entfernen mÃķchten.</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them.</source> <translation>Bitte wÃĪhlen Sie die Komponenten aus, die Sie installieren mÃķchten. WÃĪhlen Sie die Komponenten ab, die Sie entfernen mÃķchten.</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>at least 2</source> <translation>mindestens 2</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Das fÞr die Anweisung %1 benÃķtigte Installerobjekt ist leer.</translation> </message> <message> <source>Can not save the output of %1 to an empty installer key value.</source> <translation>Konnte die Ausgabe von %1 nicht in einen leeren SchlÞsselwert des Installers speichern.</translation> </message> <message> <source>File '%1' does not exist or is not an executable binary.</source> <translation>Datei '%1' existiert nicht oder ist keine ausfÞhrbare BinÃĪrdatei.</translation> </message> <message> <source>Running '%1' resulted in a crash.</source> <translation>AusfÞhren von '%1' fÞhrte zu einem Absturz.</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source>2 or 3</source> <translation>2 oder 3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source> (<source> <target> [forceOverwrite])</source> <translation> (<Quelle> <Ziel> [forceOverwrite])</translation> </message> <message> <source>Invalid argument in %0: Third argument needs to be forceOverwrite, if specified</source> <translation>UngÞltiges Argument in %0: Drittes Argument muss forceOverwrite sein, wenn es angegeben wird</translation> </message> <message> <source>Invalid arguments in %0: Directories are invalid: %1 %2</source> <translation>UngÞltige Argumentein %0: Ordner %1 und %2 ungÞltig.</translation> </message> <message> <source>Cannot create %0</source> <translation>Konnte Ordner "%1" nicht anlegen.</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Konnte Datei %1 nicht Þberschreiben</translation> </message> <message> <source>Cannot copy %0 to %1, error was: %3</source> <translation>Konnte %0 nicht nach %1 kopieren. Fehlermeldung: %3</translation> </message> <message> <source>Cannot remove %0</source> <translation>Konnte Datei %0 nicht lÃķschen.</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation>UngÞltige Anzahl task items.</translation> </message> <message> <source>Cannot open source '%1' for read. Error: %2.</source> <translation>Konnte Quelle '%1' nicht zum Lesen Ãķffnen. Fehlermeldung: %2.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <translation>Konnte Ziel '%1' nicht zum Schreiben Ãķffnen. Fehlermeldung: %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <translation>Konnte Datei '%1' nicht zum Schreiben Ãķffnen. Fehlermeldung: %2</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Konnte Datei %1 nicht sichern. Fehlermeldung: %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 2</source> <translation>genau 2</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Konnte Datei %1 nicht Þberschreiben.</translation> </message> <message> <source>Cannot write Desktop Entry at %1</source> <translation>Konnte keinen Eintrag %1 auf dem Arbeitsplatz anlegen.</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 2</source> <translation>genau 2</translation> </message> <message> <source>Cannot create link from %1 to %2.</source> <translation>Konnte keinen Link von %1 nach %2 erstellen.</translation> </message> <message> <source>Cannot remove link from %1 to %2.</source> <translation>Konnte Link von %1 nach %2 nicht entfernen.</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set file permissions %1!</source> <translation>Konnte Dateiberechtigungen auf Datei %1 nicht setzen.</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Konnte Datei %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot move file %1 to %2. Error: %3</source> <translation>Konnte Datei %1 nicht nach %2 verschieben. Fehlermeldung: %3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 2</source> <translation>genau 2</translation> </message> <message> <source>Installer needs to be an offline version: %1.</source> <translation>Installer muss eine Offline-Version sein: %1.</translation> </message> <message> <source>Cannot open file: %1</source> <translation>Konnte Datei %1 nicht Ãķffnen.</translation> </message> <message> <source>Cannot read: %1. Error: %2</source> <translation>Konnte Datei %1 nicht lesen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot open file: %1. Error: %2</source> <translation>Konnte Datei %1 nicht Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot create target dir: %1.</source> <translation>Konnte Zielordner %1. nicht anlegen.</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>Unbekannte Ausnahmebedingung: %1</translation> </message> <message> <source>Removing file: %0</source> <translation>Datei %0 wird entfernt</translation> </message> <message> <source>Cannot remove %0.</source> <translation>Konnte Datei %0 nicht lÃķschen.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Konnte Ordner %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>2 or 3</source> <translation>2 oder 3</translation> </message> <message> <source> (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</source> <translation> (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</translation> </message> <message> <source>Cannot create folder %1: %2.</source> <translation>Konnte Ordner %1 nicht anlegen. Fehlermeldung: %2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>Konnte Datei %1 nicht Þberschreiben. Fehlermeldung: %2</translation> </message> <message> <source>Cannot create link %1: %2</source> <translation>Konnte Verweis %1 nicht anlegen. Fehlermeldung: %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>Abgebrochen</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>Herunterladen der PrÞfsumme fehlgeschlagen.</translation> </message> <message> <source>Download Error</source> <translation>Fehler beim Herunterladen</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>PrÞfsumme ungÞltig beim Herunterladen. Dies ist ein kurzzeitiger Fehler, bitte erneut versuchen.</translation> </message> <message> <source>Cannot verify Hash</source> <translation>PrÞfsumme konnte nicht geprÞft werden.</translation> </message> <message> <source>Cannot download archive: %1 : %2</source> <translation>Konnte Archiv %1 nicht herunterladen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>Konnte Archiv nicht laden. Fehler: %1 Fehler beim Laden von %2</translation> </message> <message> <source>Downloading archive '%1' for component: %2</source> <translation>Archiv '%1' fÞr Komponente %2 wird heruntergeladen.</translation> </message> <message> <source>Scheme not supported: %1 (%2)</source> <translation>Schema "%1" nicht unterstÞtzt in "%2".</translation> </message> <message> <source>Cannot find component for: %1.</source> <translation>Konnte keine Komponente fÞr Datei %1 finden.</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Target '%1' not open for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Ziel '%1' nicht zum Schreiben geÃķffnet. Fehlermeldung: %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Schreiben in Datei '%1' fehlgeschlagen. Fehlermeldung: %2.</translation> </message> <message> <source>Redirect loop detected '%1'.</source> <translation>Schleife in Umleitung erkannt '%1'.</translation> </message> <message> <source>Checksum mismatch detected '%1'.</source> <translation>Checksummen stimmen nicht Þberein '%1'.</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Netzwerkfehler beim Herunterladen von '%1': %2.</translation> </message> <message> <source>Unknown network error while downloading: %1.</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>Unbekannter Netzwerkfehler beim Herunterladen: '%1'.</translation> </message> <message> <source>Pause and resume not supported by network transfers.</source> <translation>Pausieren und Fortsetzen werden bei NetzwerkÞbertragungen nicht unterstÞtzt.</translation> </message> <message> <source>Invalid source '%1'. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>UngÞltige Quelle '%1'. Fehler: %2.</translation> </message> <message> <source>Target file '%1' already exists but is not a file.</source> <translation>Zieldatei '%1' existiert bereits aber ist keine Datei.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Konnte Ziel '%1' nicht zum Schreiben Ãķffnen. Fehler: %2.</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>at least 1</source> <translation>mindestens 1</translation> </message> <message> <source>Execution failed: Cannot start detached: "%1"</source> <translation>AusfÞhrung fehlgeschlagen: Konnte %1 nicht losgelÃķst starten.</translation> </message> <message> <source>Execution failed: Cannot start: "%1"(%2)</source> <translation>AusfÞhrung fehlgeschlagen: Konnte '%1' nicht starten. Fehlermeldung: %2</translation> </message> <message> <source>Execution failed(Crash): "%1"</source> <translation>AusfÞhrung fehlgeschlagen (Absturz): "%1"</translation> </message> <message> <source>Execution failed(Unexpected exit code: %1): "%2"</source> <translation>AusfÞhrung fehlgeschlagen (Unerwarteter Fehlercode %1): "%2"</translation> </message> </context> <context> <name>QInstaller::EnvironmentVariableOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>2 to 4</source> <translation>2 bis 4</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 2</source> <translation>genau 2</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open %1 for reading: %2.</source> <translation>Konnte Datei %1 nicht zum Lesen Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Fehler beim Auspacken von '%1'. Fehlermeldung: %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Beim Auspacken von %1 ist eine unbekannte Ausnahmebedingung aufgetreten.</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Number of arguments does not match: one is required</source> <translation>Unpassende Anzahl Argumente: Genau eins wird verlangt</translation> </message> <message> <source>Cannot get package manager core.</source> <translation>Konnte PackageManagerCore nicht erhalten.</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>Dieser Prozess sollte beendet werden, um fortsetzen zu kÃķnnen: %1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>Diese Prozesse sollten beendet werden, um fortsetzen zu kÃķnnen: %1</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%1 von %2</translation> </message> <message> <source>%1 received.</source> <translation>%1 empfangen.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/s)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n Tag, </numerusform> <numerusform>%n Tage, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n Stunde, </numerusform> <numerusform>%n Stunden, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n Minute</numerusform> <numerusform>%n Minuten</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n Sekunde</numerusform> <numerusform>%n Sekunden</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 verbleibend.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - verbleibende Zeit unbekannt.</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>Den %1-Assistent abschließen.</translation> </message> <message> <source>Click Done to exit the %1 Wizard.</source> <translation>Klicken Sie "Abschließen", um den %1 Assistenten zu beenden.</translation> </message> <message> <source>Click Finish to exit the %1 Wizard.</source> <translation>Klicken Sie "Abschließen", um den %1 Assistenten zu beenden.</translation> </message> <message> <source>Restart</source> <translation>Neu starten</translation> </message> <message> <source>Run %1 now.</source> <translation>Starte jetzt %1.</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>Der %1-Assistent ist fehlgeschlagen.</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable</source> <translation>Einstellungen konnten nicht geschrieben werden.</translation> </message> <message> <source>Failed to write settings</source> <translation>Einstellungen konnten nicht geschrieben werden.</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>3, 4 or 5</source> <translation>3, 4 oder 5</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>1 or 2</source> <translation>1 oder 2</translation> </message> <message> <source> (Sourcepath, [Vendorprefix])</source> <translation> (Quellpfad, [Vendorprefix])</translation> </message> <message> <source>Invalid Argument: source folder must not be empty.</source> <translation>UngÞltiges Argument: Quellordner darf nicht leer sein.</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation>Konnte Datei %1 nicht sichern. Fehlermeldung: %2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>Konnte Datei %1 nicht Þberschreiben. Fehlermeldung: %2</translation> </message> <message> <source>Failed to copy file %1: %2</source> <translation>Konnte Datei nicht nach %1 kopieren. Fehlermeldung: %2</translation> </message> <message> <source>Cannot create folder at %1: %2</source> <translation>Konnte Ordner %1 nicht anlegen. Fehlermeldung: %2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>Einrichten - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>Willkommen zum %1-Einrichtungsassistenten.</translation> </message> <message> <source>Add or remove components</source> <translation>Komponenten hinzufÞgen oder entfernen</translation> </message> <message> <source>Update components</source> <translation>Komponenten aktualisieren</translation> </message> <message> <source>Remove all components</source> <translation>Alle Komponenten entfernen</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>Daten werden vom Installationsserver empfangen ...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>Mindestens ein gÞltiges und aktiviertes Repository wird benÃķtigt, um diese Aktion erfolgreich abzuschließen.</translation> </message> <message> <source>No updates available.</source> <translation>Keine Aktualisierungen verfÞgbar.</translation> </message> <message> <source> Only local package management available.</source> <translation> Nur lokale Paketverwaltung verfÞgbar.</translation> </message> <message> <source>Quit</source> <translation>Beenden</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>Lizenzabkommen</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translatorcomment>Lizenz akzeptieren</translatorcomment> <translation>Alt+A</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>Bitte lesen Sie das folgende Lizenzabkommen. Sie mÞssen die Bedingungen in diesem Abkommen akzeptieren, um die Installation fortsetzen zu kÃķnnen.</translation> </message> <message> <source>I accept the license.</source> <translation>Ich akzeptiere die Lizenzvereinbarung.</translation> </message> <message> <source>I do not accept the license.</source> <translation>Ich akzeptiere die Lizenzvereinbarung nicht.</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>Bitte lesen Sie die folgenden Lizenzabkommen. Sie mÞssen die Bedingungen in diesen Abkommen akzeptieren, um die Installation fortsetzen zu kÃķnnen.</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>Ich akzeptiere die Lizenzvereinbarungen nicht.</translation> </message> <message> <source>I accept the licenses.</source> <translation>Ich akzeptiere die Lizenzvereinbarungen.</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translatorcomment>Der Lizenz nicht zustimmen</translatorcomment> <translation>Alt+N</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>Keine Lizenzdateien zum Kopieren gefunden.</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Das fÞr die Anweisung %1 benÃķtigte Installer-Objekt ist leer.</translation> </message> <message> <source>Can not write license file: %1.</source> <translation>Konnte Lizenzdatei %1 nicht schreiben.</translation> </message> <message> <source>No license files found to delete.</source> <translation>Keine Lizenzdateien zum LÃķschen gefunden.</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 3</source> <translation>genau 3</translation> </message> <message> <source>Failed to open '%1' for reading.</source> <translation>Konnte Datei '%1' nicht zum Lesen Ãķffnen.</translation> </message> <message> <source>Failed to open '%1' for writing.</source> <translation>Konnte Datei '%1' nicht zum Schreiben Ãķffnen.</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>Fehlende Paketmanager-Kernkomponente.</translation> </message> <message> <source>Preparing meta information download...</source> <translation>Herunterladen der Metainformationen wird vorbereitet ...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>Herunterladen der Metainformationen abgebrochen.</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>Fehlende Proxy-Zugangsdaten.</translation> </message> <message> <source>Authentication failed.</source> <translation>Authentifizierung fehlgeschlagen.</translation> </message> <message> <source>Unknown exception during download.</source> <translation>Beim Herunterladen ist eine unbekannte Ausnahmebedingung aufgetreten.</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>Metainformationen werden vom Installationsserver empfangen ...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>Herunterladen von Paketquellen fehlgeschlagen.</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>Beim Auspacken ist eine unbekannte Ausnahmebedingung aufgetreten.</translation> </message> <message> <source>Extracting meta information...</source> <translation>Metainformationen werden ausgepackt ...</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Fehler beim Auspacken von '%1'. Fehlermeldung: %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Beim Auspacken von %1 ist eine unbekannte Ausnahmebedingung aufgetreten.</translation> </message> <message> <source>Cannot open %1 for reading. Error: %2</source> <translation>Konnte Datei %1 nicht zum Lesen Ãķffnen. Fehlermeldung: %2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source> Downloading packages...</source> <translation> Pakete werden heruntergeladen ...</translation> </message> <message> <source>Installation canceled by user</source> <translation>Installation durch den Benutzer abgebrochen</translation> </message> <message> <source>All downloads finished.</source> <translation>Alle Herunterladeprozesse abgeschlossen.</translation> </message> <message> <source>Error</source> <translation>Fehler</translation> </message> <message> <source>Cancelling the Installer</source> <translation>Der Installationsvorgang wird abgebrochen</translation> </message> <message> <source>Error writing Maintenance Tool</source> <translation>Fehler beim Schreiben des Verwaltungswerkzeugs</translation> </message> <message> <source>Authentication Error</source> <translation>Autentifizierungsfehler</translation> </message> <message> <source>Some components could not be removed completely because admin rights could not be acquired: %1.</source> <translation>Einige Komponenten konnten nicht vollstÃĪndig entfernt werden, weil die nÃķtigen Administratorrechte nicht erlangt werden konnten. Fehlermeldung: %1</translation> </message> <message> <source>Unknown error.</source> <translation>Unbekannter Fehler.</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>Einige Komponenten konnten nicht vollstÃĪndig entfernt werden, weil ein unbekannter Fehler aufgetreten ist.</translation> </message> <message> <source>Application not running in Package Manager mode!</source> <translation>Die Anwendung lÃĪuft nicht im Paketverwaltungsmodus.</translation> </message> <message> <source>No installed packages found.</source> <translation>Keine installierten Pakete gefunden.</translation> </message> <message> <source>Application running in Uninstaller mode!</source> <translation>Die Anwendung lÃĪuft im Deinstallationsmodus.</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>Es gibt eine wichtige Aktualisierung, bitte zuerst den Updater starten.</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>Fehler beim Erlangen von Administratorrechten.</translation> </message> <message> <source>invalid</source> <translation>ungÞltig</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Error</source> <translation>Fehler</translation> </message> <message> <source>Access error</source> <translation>Zugriffsfehler</translation> </message> <message> <source>Format error</source> <translation>Formatfehler</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>Konnte Einstellungen des Installers nicht nach %1 schreiben. Fehlermeldung: %2</translation> </message> <message> <source>Stop Processes</source> <translation>Prozesse anhalten</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>Diese Prozesse sollten beendet werden, um fortsetzen zu kÃķnnen: %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>Installation durch den Benutzer abgebrochen</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>TargetDir muss gesetzt sein.</translation> </message> <message> <source>Preparing the installation...</source> <translation>Installation wird vorbereitet ...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>Es ist nicht mÃķglich, von einem Netzwerk-Speicherort aus zu installieren</translation> </message> <message> <source>Creating local repository</source> <translation>Lokale Quelle wird erstellt</translation> </message> <message> <source> Installation finished!</source> <translation> Installation abgeschlossen!</translation> </message> <message> <source> Installation aborted!</source> <translation> Installation abgebrochen!</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>Es ist nicht mÃķglich, diese Oparation von einem Netzwerk-Speicherort aus zu starten</translation> </message> <message> <source>Removing deselected components...</source> <translation>AbgewÃĪhlte Komponenten werden entfernt ...</translation> </message> <message> <source> Update finished!</source> <translation> Aktualisierung beendet!</translation> </message> <message> <source> Update aborted!</source> <translation> Aktualisierung abgebrochen!</translation> </message> <message> <source>Unresolved dependencies</source> <translation>Nicht aufgelÃķste AbhÃĪngigkeiten</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>Schreiben des Verwaltungswerkzeugs.</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>Suchen in Datei %1 fehlgeschlagen. Fehlermeldung: %2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation>Verwaltungswerkzeug ist kein Bundle</translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>Konnte Daten des Verwaltungswerkzeugs nicht nach %1 schreiben. Fehlermeldung: %2</translation> </message> <message> <source>Cannot remove data file '%1': %2</source> <translation>Konnte Datei %1 nicht lÃķschen. Fehlermeldung: %2</translation> </message> <message> <source>Cannot write maintenance tool to %1: %2</source> <translation>Konnte Verwaltungswerkzeug nicht nach %1 schreiben: %2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>Konnte BinÃĪrdaten des Verwaltungswerkzeugs nicht nach %1 schreiben: %2</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>Verwaltungswerkzeug wird erstellt</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>Deinstallation erfolgreich abgeschlossen.</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>Deinstallation abgebrochen.</translation> </message> <message> <source> Installing component %1</source> <translation> Komponente %1 wird installiert</translation> </message> <message> <source>Installer Error</source> <translation>Installationsfehler</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>Fehler beim Installieren von Komponente %1: %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>Kann Deinstallation nicht vorbereiten</translation> </message> <message> <source>Cannot start uninstall</source> <translation>Kann Deinstallation nicht starten</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>Fehler bei der Deinstallation: %1</translation> </message> <message> <source>Unknown error</source> <translation>Unbekannter Fehler</translation> </message> <message> <source>Cannot retrieve remote tree: %1.</source> <translation>Kann entfernten Baum nicht empfangen: %1</translation> </message> <message> <source>Failure to read packages from: %1.</source> <translation>Fehler beim Lesen der Pakete von %1</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>Konnte die Metainformationen nicht empfangen: %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>Konnte Informationen nicht zu temporÃĪren Aktualisierungsquellen hinzufÞgen.</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>Konnte keine Informationen zu Aktualisierungsquellen finden.</translation> </message> <message> <source>Dependency cycle between components detected: '%1' and '%2'.</source> <translation>Zyklische AbhÃĪngigkeit zwischen Komponenten entdeckt: '%1' und '%2'.</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>%1 Einrichtung</translation> </message> <message> <source>Maintain %1</source> <translation>%1 verwalten</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>MÃķchten Sie den Installationsprozess abbrechen?</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>MÃķchten Sie den Deinstallationsprozess abbrechen?</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>MÃķchten Sie die Installationsanwendung beenden?</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>MÃķchten Sie die Deinstallationsanwendung beenden?</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>MÃķchten Sie das Verwaltungswerkzeug beenden?</translation> </message> <message> <source>Question</source> <translation>Frage</translation> </message> <message> <source>Settings</source> <translation>Einstellungen</translation> </message> <message> <source>Error</source> <translation>Fehler</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>Es ist nicht mÃķglich, von einem Netzwerk-Speicherort aus zu installieren. Bitte kopieren Sie den Installer auf ein lokales Laufwerk</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>Details &anzeigen</translation> </message> <message> <source>&Hide Details</source> <translation>Details aus&blenden</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>&Deinstallieren</translation> </message> <message> <source>Uninstalling %1</source> <translation>%1 wird deinstalliert</translation> </message> <message> <source>&Update</source> <translation>&Aktualisieren</translation> </message> <message> <source>Updating components of %1</source> <translation>Komponenten von %1 werden aktualisiert</translation> </message> <message> <source>&Install</source> <translation>&Installieren</translation> </message> <message> <source>Installing %1</source> <translation>%1 wird installiert</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation>Dialog</translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>Der Proxy %1 verlangt einen Benutzernamen und Passwort.</translation> </message> <message> <source>Username:</source> <translation>Benutzername:</translation> </message> <message> <source>Username</source> <translation>Benutzername</translation> </message> <message> <source>Password:</source> <translation>Passwort:</translation> </message> <message> <source>Password</source> <translation>Passwort</translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>&Deinstallieren</translation> </message> <message> <source>Ready to Uninstall</source> <translation>Bereit zum Deinstallieren</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>Das Einrichtungsprogramm ist jetzt bereit, %1 von Ihrem Computer zu entfernen. <br><font color="red">Das Programmverzeichnis %2 wird vollstÃĪndig gelÃķscht</font>, inklusive allen Inhalten in diesem Ordner!</translation> </message> <message> <source>U&pdate</source> <translation>&Aktualisieren</translation> </message> <message> <source>Ready to Update Packages</source> <translation>Bereit zum Aktualisieren der Pakete</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>Das Einrichtungsprogramm ist jetzt bereit, Ihre Installation zu aktualisieren.</translation> </message> <message> <source>&Install</source> <translation>&Installieren</translation> </message> <message> <source>Ready to Install</source> <translation>Bereit zum Installieren</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>Das Einrichtungsprogramm ist jetzt bereit, %1 auf Ihrem Computer zu installieren.</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation! Available space: %1, at least required %2.</source> <translation>Nicht genÞgend Festplattenplatz fÞr temporÃĪre Dateien und die Installation! VerfÞgbarer Platz: %1, mindestens benÃķtigt: %2.</translation> </message> <message> <source>Not enough disk space to store all selected components! Available space: %1, at least required: %2.</source> <translation>Nicht genÞgend Festplattenplatz fÞr alle ausgewÃĪhlten Komponenten! VerfÞgbarer Platz: %1, mindestens benÃķtigt: %2.</translation> </message> <message> <source>Not enough disk space to store temporary files! Available space: %1, at least required: %2.</source> <translation>Nicht genÞgend Festplattenplatz fÞr temporÃĪre Dateien! VerfÞgbarer Platz: %1, mindestens benÃķtigt: %2.</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>Die fÞr die Installation ausgewÃĪhlte Partition scheint genÞgend Platz zu bieten, aber es werden anschließend weniger als 1% der PartitionsgrÃķße verfÞgbar sein. %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>Die fÞr die Installation ausgewÃĪhlte Partition scheint genÞgend Platz zu bieten, aber es werden anschließend weniger als 100 MB verfÞgbar sein. %1</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>Die Installation wird %1 Festplattenplatz verwenden.</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>Konnte nicht alle AbhÃĪngigkeiten auflÃķsen.</translation> </message> <message> <source>Components about to be removed.</source> <translation>Komponenten, die entfernt werden.</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>2 to 5</source> <translation>2 bis 5</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>Dateitypenregistrierung wird nur unter Windows unterstÞtzt.</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>Dateitypenregistrierung: UngÞltige Argumente</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Konnte nicht alle Daten nach dem Senden des Befehls '%1' lesen. Bytes erwartet: %2, Bytes erhalten: %3. Fehler: %4</translation> </message> </context> <context> <name>QInstaller::RemoteServerConnection</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Konnte nicht alle Daten nach dem Senden des Befehls '%1' lesen. Bytes erwartet: %2, Bytes erhalten: %3. Fehler: %4</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 3</source> <translation>genau 3</translation> </message> <message> <source>Failed to open %1 for reading</source> <translation>Konnte Datei %1 nicht zum Lesen Ãķffnen</translation> </message> <message> <source>Failed to open %1 for writing</source> <translation>Konnte Datei %1 nicht zum Schreiben Ãķffnen</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Cannot open Resource '%1' read-only.</source> <translation>Konnte Ressourcendatei '%1' nicht schreibgeschÞtzt Ãķffnen.</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>Das Lesen ist nach %1 Bytes fehlgeschlagen: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Das Schreiben ist nach %1 Bytes fehlgeschlagen: %2</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>Der %1-Assistent wird abgeschlossen</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open the requested script file at %1: %2.</source> <translation>Konnte angeforderte Skriptdatei '%1' nicht Ãķffnen. Fehlermeldung: %2</translation> </message> <message> <source>Exception while loading the component script '%1'. (%2)</source> <translation>Ausnahme beim Laden des Komponentenskripts '%1'. (%2)</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in '%1' operation is empty.</source> <translation>Das fÞr die Anweisung '%1' benÃķtigte Installer-Objekt ist leer.</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>Automatischer Neustart: Nur im Aktualisierungs- und Pakatverwaltungs-Modus erlaubt.</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>Automatischer Neustart: UngÞltige Argumente</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>Server verlangt Authentifizierung</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>Benutzername und Passwort fÞr den Zugriff benÃķtigt.</translation> </message> <message> <source>Username:</source> <translation>Benutzername:</translation> </message> <message> <source>Password:</source> <translation>Passwort:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 auf %2</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) '%1' calling '%2' with arguments '%3'.</source> <translation>Fehlende Argumente '%1' beim Aufruf von '%2' mit den Argumenten '%3'.</translation> </message> <message> <source>Current method argument calling '%1' with arguments '%2' is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>Aufruf von '%1' mit den Argumenten '%2' nicht unterstÞtzt. Bitte 'set', 'remove', 'add_array_value' oder 'remove_array_value' verwenden.</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>UngÞltige Argumente in %0: %1 Argumente erhalten, %2 erwartet%3.</translation> </message> <message> <source>exactly 2</source> <translation>genau 2</translation> </message> <message> <source>None of the arguments can be empty: source '%1', target '%2'.</source> <translation>Keines der Argumente darf leer sein: Quelle '%1', Ziel '%2'.</translation> </message> <message> <source>Cannot move source '%1' to target '%2', because target exists and is not removable.</source> <translation>Konnte Quelle '%1' nicht nach Ziel '%2' verschieben, weil das Ziel bereits existiert und nicht entfernt werden kann.</translation> </message> <message> <source>Cannot move source '%1' to target '%2': %3</source> <translation>Konnte Datei '%1' nicht nach '%2' verschieben: %3</translation> </message> <message> <source>Move '%1' to '%2'.</source> <translation>'%1' nach '%2' verschieben.</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>VerknÞpfungen im StartmenÞ</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.</source> <translation>WÃĪhlen Sie den Ordner im StartmenÞ, in dem die VerknÞpfungen zur Anwendung erstellt werden sollen. Sie kÃķnnen einen Namen angeben, um einen neuen Ordner anzulegen.</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>Installationsordner</translation> </message> <message> <source>Please specify the folder where %1 will be installed.</source> <translation>Bitte geben Sie den Ordner an, in dem %1 installiert werden soll.</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translatorcomment>Dateisystem durchsuchen, um eine Datei auszuwÃĪhlen</translatorcomment> <translation>Alt+D</translation> </message> <message> <source>B&rowse...</source> <translation>&Durchsuchen ...</translation> </message> <message> <source>The folder you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>Der ausgewÃĪhlte Ordner existiert bereits und enthÃĪlt eine Installation. Bitte wÃĪhlen Sie einen anderen Zielordner aus.</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation>Sie haben einen existierenden, nicht leeren Ordner fÞr die Installation ausgewÃĪhlt. Dieser wird bei der Deinstallation dieser Anwendung komplett gelÃķscht werden. Es wird nicht empfohlen, in diesen Ordner zu installieren. MÃķchten Sie trotzdem fortsetzen?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>Existierende Datei oder Symlink ausgewÃĪhlt, bitte ein anderes Installationsziel auswÃĪhlen.</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid folder.</source> <translation>Der Installationspfad darf nicht leer sein. Bitte einen gÞltigen Ordner angeben.</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>Der Installationspfad darf nicht relativ sein. Bitte einen absoluten Pfad angeben.</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>Der Pfad zum Installationsverzeichnis enthÃĪlt Zeichen ausserhalb des ASCII-Zeichensatzes. Dies ist zur Zeit nicht unterstÞtzt. Bitte wÃĪhlen Sie einen anderen Pfad oder Installationsordner.</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>Der von Ihnen eingegebene Pfad ist zu lang, bitte geben Sie einen gÞltigen Pfad ein.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>Der von Ihnen eingegebene Pfad ist ungÞltig, bitte geben Sie ein gÞltiges Ziel ein.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>Der von Ihnen eingegebene Pfad ist ungÞltig, bitte geben Sie ein gÞltiges Laufwerk an.</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid folder.</source> <translation>Der Installationspfad darf nicht auf '.' enden, bitte geben Sie einen gÞltigen Ordner ein.</translation> </message> <message> <source>The installation path must not contain '%1', please specify a valid folder.</source> <translation>Der Installationspfad darf nicht %1 enthalten, bitte geben Sie einen gÞltigen Ordner ein.</translation> </message> <message> <source>Error</source> <translation>Fehler</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>Da das Installationsverzeichnis komplett gelÃķscht wird, ist eine Installation nach %1 nicht zulÃĪssig.</translation> </message> <message> <source>Warning</source> <translation>Achtung</translation> </message> <message> <source>Select Installation Folder</source> <translation>Installationsordner auswÃĪhlen.</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Empty repository URL.</source> <translation>Leere Quelladresse.</translation> </message> <message> <source>URL scheme not supported: %1 (%2).</source> <translation>Adressschema '%1' nicht unterstÞtzt in Adresse '%2'.</translation> </message> <message> <source>Got a timeout while testing: '%1'</source> <translation>ZeitÞberschreitung beim Testen von: '%1'</translation> </message> <message> <source>Cannot parse Updates.xml! Error: %1.</source> <translation>UngÞltiges Format der Updates.xml. Fehlermeldung: %1.</translation> </message> <message> <source>Updates.xml could not be opened for reading!</source> <translation>Konnte Updates.xml nicht zum Lesen Ãķffnen.</translation> </message> <message> <source>Updates.xml could not be found on server!</source> <translation>Updates.xml konnte auf dem Server nicht gefunden werden.</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>Autorisierung benÃķtigt.</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>Geben Sie Ihr Passwort ein, um sich fÞr sudo zu authentifizieren:</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>Fehler beim Erlangen von Administratorrechten.</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>Konnte Autorisierung nicht erhalten.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as root and then clicking OK.</source> <translation>Konnte die Autorisierung, die zum Fortsetzen der Installation nÃķtig ist, nicht erhalten. Brechen Sie entweder die Installation ab, oder verwenden Sie die Fallback-LÃķsung, indem Sie %1 als root aufrufen und dann "Ok" auswÃĪhlen. </translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>Konnte Ressource %1 nicht Ãķffnen: %2</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>Konnte Einstellungsdatei %1 nicht zum Lesen Ãķffnen. Fehlermeldung: %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>Einstellungen</translation> </message> <message> <source>Network</source> <translation>Netzwerk</translation> </message> <message> <source>No proxy</source> <translation>Kein Proxy</translation> </message> <message> <source>System proxy settings</source> <translation>Systemeinstellungen fÞr Proxy</translation> </message> <message> <source>Manual proxy configuration</source> <translation>Manuelle Konfiguration des Proxy</translation> </message> <message> <source>HTTP proxy:</source> <translation>HTTP-Proxy:</translation> </message> <message> <source>Port:</source> <translation>Port:</translation> </message> <message> <source>FTP proxy:</source> <translation>FTP-Proxy:</translation> </message> <message> <source>Repositories</source> <translation>Quellen</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>Benutzername und Passwort fÞr die Autentifizierung hinzufÞgen, falls benÃķtigt.</translation> </message> <message> <source>Use temporary repositories only</source> <translation>Ausschließlich temporÃĪre Quellen verwenden</translation> </message> <message> <source>Add</source> <translation>HinzufÞgen</translation> </message> <message> <source>Remove</source> <translation>Entfernen</translation> </message> <message> <source>Test</source> <translation>Testen</translation> </message> <message> <source>Show Passwords</source> <translation>PasswÃķrter anzeigen</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>AuswÃĪhlen, um die Quelle zu verwenden.</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>Benutzernamen eintragen, um sich gegenÞber der Quelle zu authentifizieren.</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>Passwort eintragen, um sich gegenÞber der Quelle zu authentifizieren.</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>Adresse angeben, die auf eine gÞltige Quelle zeigt.</translation> </message> <message> <source>There was an error testing this repository.</source> <translation>Beim Testen der Quelle ist ein Fehler aufgetreten.</translation> </message> <message> <source>Do you want to disable the tested repository?</source> <translation>Soll die getestete Quelle deaktiviert werden?</translation> </message> <message> <source>Hide Passwords</source> <translation>PasswÃķrter ausblenden</translation> </message> <message> <source>Use</source> <translation>Verwende</translation> </message> <message> <source>Username</source> <translation>Benutzername</translation> </message> <message> <source>Password</source> <translation>Passwort</translation> </message> <message> <source>Repository</source> <translation>Quelle</translation> </message> <message> <source>Default repositories</source> <translation>Standardquellen</translation> </message> <message> <source>Temporary repositories</source> <translation>TemporÃĪre Quellen</translation> </message> <message> <source>User defined repositories</source> <translation>Benutzerdefinierte Quellen</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable</source> <translation>Registrierungspfad %1 ist nicht beschreibbar</translation> </message> <message> <source>Cannot write to registry path %1</source> <translation>Konnte nicht in Registrierungspfad %1 schreiben</translation> </message> <message> <source>Renaming %1 into %2 failed with %3.</source> <translation>Umbenennung von %1 nach %2 ist mit Meldung %3 fehlgeschlagen.</translation> </message> </context> </TS> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/ifw_es.ts����������������������������������������������������������������������0000664�0000000�0000000�00000335002�13253666515�0017012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="es_ES"> <context> <name>Component</name> <message> <source>Cannot open archive %1: %2</source> <translation>No se puede abrir el archivo %1: %2</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>AutenticaciÃģn Http requerida</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>Tienes que suministrar un nombre de usuario y contraseÃąa para poder acceder a este sitio.</translation> </message> <message> <source>Username:</source> <translation>Nombre de usuario:</translation> </message> <message> <source>Password:</source> <translation>ContraseÃąa:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 en %2</translation> </message> </context> <context> <name>IntroductionPageImpl</name> <message> <source>Package manager</source> <translation>Gestor de paquetes</translation> </message> <message> <source>Update components</source> <translation>Actualizar componentes</translation> </message> <message> <source>Remove all components</source> <translation>Quitar todos los componentes</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>Recuperando informaciÃģn de fuentes de instalaciÃģn remotas...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>Necesitas tener al menos un repositorio habilitado para que esta acciÃģn se realice con ÃĐxito.</translation> </message> <message> <source>No updates available.</source> <translation>No hay actualizaciones disponibles.</translation> </message> <message> <source> Only local package management available.</source> <translation>SÃģlo estÃĄ disponible la gestiÃģn de paquetes de forma local.</translation> </message> <message> <source>Quit</source> <translation>Salir</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>Cancelado</translation> </message> </context> <context> <name>KDSaveFile</name> <message> <source>Append mode not supported.</source> <translation>Modo aÃąadir no admitido.</translation> </message> <message> <source>Read-only access not supported.</source> <translation>Acceso de sÃģlo lectura no admitido.</translation> </message> <message> <source>Cannot backup existing file %1: %2</source> <translation>No se puede hacer una copia de seguridad del archivo existente %1: %2</translation> </message> <message> <source>TODO</source> <translation>Tareas</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>No se puede hacer una copia de seguridad del archivo %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, 2 esperados.</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>No se puede abrir el archivo %1 en modo escritura: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>No se puede localizar la copia de seguridad de %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>No se puede restaurar la copia de seguridad de %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>No se puede restaurar la copia de seguridad del archivo %1: %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot backup file %1.</source> <translation>No se puede hacer una copia de seguridad del archivo %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, 2 esperados.</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>No se puede eliminar el archivo de destino %1: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>No se puede copiar %1 a %2: %3</translation> </message> <message> <source>Cannot delete file %1: %2</source> <translation>No se puede eliminar el archivo %1: %2</translation> </message> <message> <source>Cannot restore backup file into %1: %2</source> <translation>No se puede restaurar la copia de seguridad del archivo como %1: %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of %1: %2</source> <translation>No se puede hacer una copia de seguridad de %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, 2 esperados.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>No se puede restaurar la copia de seguridad del archivo %1: %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download canceled.</source> <translation>Descarga cancelada.</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>Los hashes criptogrÃĄficos no coinciden.</translation> </message> <message> <source>Download finished.</source> <translation>Descarga.finalizada.</translation> </message> <message> <source> of </source> <translation> de </translation> </message> <message> <source> downloaded.</source> <translation> descargado.</translation> </message> <message> <source>/sec</source> <translation>/seg</translation> </message> <message> <source> day</source> <translation> día</translation> </message> <message> <source> days</source> <translation> días</translation> </message> <message> <source> hour</source> <translation> hora</translation> </message> <message> <source> hours</source> <translation> horas</translation> </message> <message> <source> minute</source> <translation> minuto</translation> </message> <message> <source> minutes</source> <translation> minutos</translation> </message> <message> <source> second</source> <translation> segundo</translation> </message> <message> <source> seconds</source> <translation> segundos</translation> </message> <message> <source> - </source> <translation> - </translation> </message> <message> <source> remaining.</source> <translation> resstante.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - tiempo restante desconocido.</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1: Writing to file '%2' failed: %3</source> <translation>No se puede descargar %1: ha fallado la escritura del archivo '%2': %3</translation> </message> <message> <source>Cannot download %1: Cannot create %2: %3</source> <translation>No se puede descargar %1: No se puede crear %2: %3</translation> </message> <message> <source>%1 at %2</source> <translation>%1 en %2</translation> </message> <message> <source>Authentication request canceled.</source> <translation>PeticiÃģn de autenticaciÃģn cancelada.</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open source file '%1' for reading.</source> <translation>No se puede abrir el archivo de origen '%1' en modo lectura.</translation> </message> <message> <source>Cannot open destination file '%1' for writing.</source> <translation>No se puede abrir el archivo de destino '%1' en modo escritura.</translation> </message> <message> <source>Writing to %1 failed: %2</source> <translation>La escritura en %1 ha fallado: %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, 2 esperados.</translation> </message> <message> <source>Cannot create folder %1: Unknown error.</source> <translation>No se puede crear la carpeta %1: error desconocido.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>No se puede eliminar el directorio %1: %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file %1.</source> <translation>No se puede hacer una copia de seguridad del archivo %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, 2 esperados.</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>No se puede eliminar el archivo de destino %1: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>No se puede copiar %1 a %2: %3</translation> </message> <message> <source>Cannot remove file %1.</source> <translation>No se puede eliminar el archivo %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>No se puede restaurar la copia de seguridad del archivo %1: %2</translation> </message> </context> <context> <name>KDUpdater::PackagesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 tiene contenido no vÃĄlido: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>El archivo %1 no existe.</translation> </message> <message> <source>Cannot open %1.</source> <translation>No se puede abrir el archivo %1.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Error al analizar en %1 en %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>Elemento raíz %1 no esperado, debería ser 'Packages'.</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>No se puede hacer una copia de seguridad del archivo %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, 2 esperados.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>No se puede abrir el archivo %1 en modo lectura: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>No se puede abrir el archivo %1 en modo escritura: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>No se puede localizar la copia de seguridad de %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>No se puede restaurar la copia de seguridad de %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>No se puede restaurar la copia de seguridad del archivo %1: %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1". Reason:</source> <translation>No se puede leer el archivo de recursos "%1". Motivo:</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, 1 esperado.</translation> </message> <message> <source>Cannot remove folder %1: The folder does not exist.</source> <translation>No se puede eliminar la carpeta %1: la carpeta no existe.</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>No se puede eliminar la carpeta %1: %2</translation> </message> <message> <source>Cannot recreate directory %1: %2</source> <translation>No se puede recrear el directorio %1: %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 empezada</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>No se puede parar %1</translation> </message> <message> <source>Cannot stop task %1</source> <translation>No se puede parar la tarea %1</translation> </message> <message> <source>%1 cannot be paused</source> <translation>No se puede pausar %1</translation> </message> <message> <source>Cannot pause task %1</source> <translation>No se puede pausar la tarea %1</translation> </message> <message> <source>Cannot resume task %1</source> <translation>No se puede reanudar la tarea %1</translation> </message> <message> <source>%1 done</source> <translation>%1 hecha</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>No se puede acceder a la informaciÃģn del paquete de esta aplicaciÃģn.</translation> </message> <message> <source>Cannot access the update sources information of this application.</source> <translation>No se puede acceder a la informaciÃģn de las fuentes de actualizaciones.</translation> </message> <message> <source>%1 updates found.</source> <translation>Hay %1 actualizaciones.</translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>Descargando Updates.xml de las fuentes de actualizaciones.</translation> </message> <message> <source>Cannot download updates from %1 ('%2')</source> <translation>No se pueden descargar las actualizaciones de %1 ('%2')</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>Archivo(s) Updates.xml descargados de las fuentes de actualizaciones.</translation> </message> <message> <source>Computing applicable updates.</source> <translation>Comprobando quÃĐ actualizaciones son necesarias.</translation> </message> <message> <source>Application updates computed.</source> <translation>Actualizaciones de la aplicaciÃģn comprobadas.</translation> </message> </context> <context> <name>KDUpdater::UpdateSourcesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 tiene contenido no vÃĄlido: %2</translation> </message> <message> <source>Cannot read "%1"</source> <translation>No se puede leer "%1"</translation> </message> <message> <source>XML Parse error in %1 at %2, %3: %4</source> <translation>Error al analizar XML en %1 en %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "UpdateSources"</source> <translation>Elemento raíz %1 no esperado, debería ser 'UpdateSources'</translation> </message> <message> <source>Cannot save changes to "%1": %2</source> <translation>No se pueden guardar los cambios en "%1": %2</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Cannot read "%1"</source> <translation>No se puede leer "%1"</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Error al analizar en %1 en %2, %3: %4</translation> </message> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Updates.xml tiene contenido no vÃĄlido: %1</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>Elemento raíz %1 no esperado, debería ser "Updates".</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>Falta el elemento ApplicationName.</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>Falta el elemento ApplicationVersion.</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>Elemento PackageUpdate sin "Name"</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>Elemento PackageUpdate sin "Version"</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>Elemento PackageUpdate sin "ReleaseDate"</translation> </message> </context> <context> <name>Lib7z::ExtractItemJob</name> <message> <source>Cannot list archive: QIODevice not set or already destroyed.</source> <translation>No se puede listar el archivo: QIODevice no estÃĄ establecido o ya estÃĄ destruido.</translation> </message> </context> <context> <name>Lib7z::ListArchiveJob</name> <message> <source>Cannot list archive: QIODevice already destroyed.</source> <translation>No se puede listar el archivo: QIODevice ya estÃĄ destruido.</translation> </message> </context> <context> <name>QInstaller::AddQtCreatorArrayValueOperation</name> <message> <source>exactly 4</source> <translation>exactamente 4</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source> (group, arrayname, key, value)</source> <translation> (group, arrayname, key, value)</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Se necesita el objeto del instalador en %1 la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>There is no value set for %1 on the installer object.</source> <translation>No se ha asignado un valor a %1 en el objeto del instalador.</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Cannot open the requested translation file '%1'.</source> <translation>No se puede abrir el archivo de traducciÃģn %1' solicitado.</translation> </message> <message> <source>Cannot open the requested UI file '%1'. Error: %2</source> <translation>No se puede abrir el archivo de UI '%1' solicitado. Error: %2</translation> </message> <message> <source>Cannot load the requested UI file '%1'. Error: %2</source> <translation>No se puede cargar el archivo de UI '%1' solicitado. Error: %2</translation> </message> <message> <source>An error has occurred while reading the UI file.</source> <translation>Se ha producido un error al leer el archivo del UI.</translation> </message> <message> <source>Cannot open the requested license file '%1'. Error: %2</source> <translation>No se puede abrir el archivo de licencia %1' solicitado. Error: %2</translation> </message> <message> <source>Error</source> <translation>Error</translation> </message> <message> <source>Error: Operation %1 does not exist</source> <translation>Error: la operaciÃģn %1 no existe</translation> </message> <message> <source>Can't resolve isAutoDependOn in %1</source> <translation>No se puede resolver isAutoDependOn en %1</translation> </message> <message> <source>Can't resolve isDefault in %1</source> <translation>No se puede resolver isDefault en %1</translation> </message> <message> <source>Update Info: </source> <translation>InformaciÃģn de actualizaciÃģn:</translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component Name</source> <translation>Nombre del componente</translation> </message> <message> <source>Installed Version</source> <translation>VersiÃģn instalada</translation> </message> <message> <source>New Version</source> <translation>Nueva versiÃģn</translation> </message> <message> <source>Size</source> <translation>TamaÃąo</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translation>Alt+A</translation> </message> <message> <source>Def&ault</source> <translation>P&redeterminado</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translation>Alt+R</translation> </message> <message> <source>&Reset</source> <translation>&Restablecer</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translation>Alt+S</translation> </message> <message> <source>&Select All</source> <translation>&Seleccionar todo</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translation>Alt+D</translation> </message> <message> <source>&Deselect All</source> <translation>&Deseleccionar todo</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>Este componente ocuparÃĄ aproximadamente %1 de tu disco duro.</translation> </message> <message> <source>Select Components</source> <translation>Seleccionar componentes</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>Por favor, selecciona los componentes que quieres actualizar.</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>Por favor, selecciona los componentes que quieres instalar.</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>Por favor, selecciona los componentes que quieres desinstalar.</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them.</source> <translation>Selecciona los componentes para instalarlos. Deselecciona los componentes instalados para desinstalarlos.</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>at least 2</source> <translation>por lo menos 2</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Se necesita el objeto del instalador en %1 la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>Can not save the output of %1 to an empty installer key value.</source> <translation>No se puede guardar la salida de %1 en un valor vacío de la clave del instalador.</translation> </message> <message> <source>File '%1' does not exist or is not an executable binary.</source> <translation>El archivo '%1' no existe o no es un binario ejecutable.</translation> </message> <message> <source>Running '%1' resulted in a crash.</source> <translation>'%1' se ha cerrado de forma inesperada.</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source>2 or 3</source> <translation>2 Ãģ 3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source> (<source> <target> [forceOverwrite])</source> <translation> (<source> <target> [forceOverwrite])</translation> </message> <message> <source>Invalid argument in %0: Third argument needs to be forceOverwrite, if specified</source> <translation>Argumento no vÃĄlido en %0: si el tercer argumento estÃĄ definido, tiene que ser forceOverwrite</translation> </message> <message> <source>Invalid arguments in %0: Directories are invalid: %1 %2</source> <translation>Argumentos no vÃĄlidos en %0: directorios no vÃĄlidos: %1 %2</translation> </message> <message> <source>Cannot create %0</source> <translation>No se puede crear %0</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Fallo al sobrescribir %1</translation> </message> <message> <source>Cannot copy %0 to %1, error was: %3</source> <translation>No se puede copiar %0 a %1, error: %3</translation> </message> <message> <source>Cannot remove %0</source> <translation>No se puede eliminar %0</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Fallo al sobrescribir %1</translation> </message> <message> <source>Cannot write Desktop Entry at %1</source> <translation>No se puede escribir la entrada de escritorio en %1</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set file permissions %1!</source> <translation>ÂĄNo se pueden dar los permisos %1!</translation> </message> <message> <source>Cannot move file %1 to %2. Error: %3</source> <translation>No se puede mover el archivo %1 a %2. Error: %3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>Installer needs to be an offline version: %1.</source> <translation>El instalador tiene que ser la versiÃģn sin conexiÃģn: %1.</translation> </message> <message> <source>Cannot open file: %1</source> <translation>No se puede abrir el archivo: %1</translation> </message> <message> <source>Cannot read: %1. Error: %2</source> <translation>No se puede leer: %1. Error: %2</translation> </message> <message> <source>Cannot open file: %1. Error: %2</source> <translation>No se puede leer el archivo %1. Error: %2</translation> </message> <message> <source>Cannot create target dir: %1.</source> <translation>No se puede crear el directorio de destino: %1.</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>ExcepciÃģn desconocida capturada: %1.</translation> </message> <message> <source>Removing file: %0</source> <translation>Eliminando archivo: %0</translation> </message> <message> <source>Cannot remove %0.</source> <translation>No se puede eliminar %0.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>No se puede eliminar el directorio %1: %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>2 or 3</source> <translation>2 Ãģ 3</translation> </message> <message> <source> (optional: 'workingDirectory=...')</source> <translation> (opcional: 'workingDirectory=...')</translation> </message> <message> <source>Cannot create folder %1: %2.</source> <translation>No se puede crear la carpeta %1: %2.</translation> </message> <message> <source>Cannot create link %1: %2</source> <translation>No se puede crear el enlace %1: %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>Cancelado</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>La descarga de la firma del hash ha fallado.</translation> </message> <message> <source>Download Error</source> <translation>Error de descarga</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>La verificaciÃģn del hash ha fallado al descargar. Es un error temporal,por favor intÃĐntalo de nuevo.</translation> </message> <message> <source>Cannot verify Hash</source> <translation>No se puede verificar el hash</translation> </message> <message> <source>Cannot download archive: %1 : %2</source> <translation>No se puede descargar el archivo %1: %2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>No se pueden traer los archivos: %1 Error al cargar %2</translation> </message> <message> <source>Downloading archive hash for component: %1</source> <translation>Descargando el hash del archivo para el componente: %1</translation> </message> <message> <source>Downloading archive for component: %1</source> <translation>Descargando archivo para el componente: %1</translation> </message> <message> <source>Scheme not supported: %1 (%2)</source> <translation>Esquema no admitido: %1 (%2)</translation> </message> <message> <source>Cannot find component for: %1.</source> <translation>No se puede localizar el componente para: %1.</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>at least 1</source> <translation>por lo menos 1</translation> </message> <message> <source>Execution failed: Cannot start detached: "%1"</source> <translation>La ejecuciÃģn ha fallado: no se puede iniciar separada: "%1"</translation> </message> <message> <source>Execution failed: Cannot start: "%1"(%2)</source> <translation>La ejecuciÃģn ha fallado: no se puede iniciar: "%1"(%2)</translation> </message> <message> <source>Execution failed(Crash): "%1"</source> <translation>La ejecuciÃģn ha fallado (cuelgue): "%1"</translation> </message> <message> <source>Execution failed(Unexpected exit code: %1): "%2"</source> <translation>La ejecuciÃģn ha fallado (cÃģdigo de salida inesperado: %1): "%2"</translation> </message> </context> <context> <name>QInstaller::EnvironmentVariableOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>2 or 3</source> <translation>2 Ãģ 3</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open %1 for reading: %2.</source> <translation>No se puede abrir el archivo %1 en modo lectura: %2.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Error al extraer '%1': %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>ExcepciÃģn desconocida capturada al extraer %1.</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>Completando el asistente de %1</translation> </message> <message> <source>Click Done to exit the %1 Wizard.</source> <translation>Haz clic en hecho para salir del asistente de %1.</translation> </message> <message> <source>Click Finish to exit the %1 Wizard.</source> <translation>Haz clic en terminar para salir del asistente de %1.</translation> </message> <message> <source>Restart</source> <translation>Reiniciar</translation> </message> <message> <source>Run %1 now.</source> <translation>Ejecutar %1 ahora.</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>El asistente de %1 ha fallado.</translation> </message> </context> <context> <name>QInstaller::GetRepositoryMetaInfoJob</name> <message> <source>Empty repository URL.</source> <translation>URL del repositorio vacía.</translation> </message> <message> <source>Retrieving component meta information...</source> <translation>Recuperando metadatos del componente...</translation> </message> <message> <source>Invalid repository URL: %1</source> <translation>URL del repositorio no vÃĄlida: %1</translation> </message> <message> <source>URL scheme not supported: %1 (%2)</source> <translation>Esquema de URL no admitido: %1 (%2)</translation> </message> <message> <source>Cannot move Updates.xml to target location. Error: %1</source> <translation>No se puede mover Updates.xml a la ubicaciÃģn de destino. Error: %1</translation> </message> <message> <source>Cannot open Updates.xml for reading. Error: %1</source> <translation>No se puede abrir Updates.xml en modo lectura. Error: %1</translation> </message> <message> <source>Cannot fetch a valid version of Updates.xml from repository: %1. Error: %2</source> <translation>No se puede traer una versiÃģn vÃĄlida de Updates.xml del repositorio: %1. Error: %2</translation> </message> <message> <source>Download Error</source> <translation>Error de descarga</translation> </message> <message> <source>Parsing component meta information...</source> <translation>Analizando los metadatos del componente...</translation> </message> <message> <source>Repository updates received.</source> <translation>Actualizaciones del repositorio obtenidas.</translation> </message> <message> <source>Finished updating component meta information.</source> <translation>ActualizaciÃģn de los metadatos del componente finalizada.</translation> </message> <message> <source>Cannot fetch Updates.xml from repository: %1. Error: %2</source> <translation>No se puede traer Updates.xml del repositorio: %1. Error: %2</translation> </message> <message> <source>Retrieving component information from remote repository...</source> <translation>Recuperando informaciÃģn del componente del repositorio remoto...</translation> </message> <message> <source>Cannot open meta info archive: %1. Error: %2</source> <translation>No se puede abrir el archivo de metadatos %1. Error: %2</translation> </message> <message> <source>The hash of one component does not match the expected one.</source> <translation>El hash de un componente no coincide con el esperado.</translation> </message> <message> <source>Bad hash.</source> <translation>Hash errÃģneo.</translation> </message> <message> <source>Cannot download meta information for component: %1. Error: %2</source> <translation>No se pueden descargar los metadatos del componente: %1. Error: %2</translation> </message> </context> <context> <name>QInstaller::GetRepositoryMetaInfoJob::ZipRunnable</name> <message> <source>Error while extracting '%1': %2</source> <translation>Error al extraer '%1': %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>ExcepciÃģn desconocida capturada al extraer %1.</translation> </message> <message> <source>Cannot open %1 for reading. Error: %2</source> <translation>No se puede abrir %1 en modo lectura. Error: %2</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable</source> <translation>No se puede escribir en la configuraciÃģn</translation> </message> <message> <source>Failed to write settings</source> <translation>Fallo al escribir la configuraciÃģn</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>3 or 4</source> <translation>3 Ãģ 4</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>1 or 2</source> <translation>1 Ãģ 2</translation> </message> <message> <source> (Sourcepath, [Vendorprefix])</source> <translation> (Sourcepath, [Vendorprefix])</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>InstalaciÃģn - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>Bienvenido al asistente de instalaciÃģn de %1.</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>Acuerdo de licencia</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translation>Alt+A</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>Por favor, lee el siguiente acuerdo de licencia. Tienes que aceptar los tÃĐrminos de este acuerdo para poder continuar con la instalaciÃģn.</translation> </message> <message> <source>I accept the license.</source> <translation>Acepto la licencia.</translation> </message> <message> <source>I do not accept the license.</source> <translation>No acepto la licencia.</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>Por favor, lee el siguiente acuerdo de licencia. Tienes que aceptar los tÃĐrminos de este acuerdo para poder continuar con la instalaciÃģn.</translation> </message> <message> <source>I accept the licenses.</source> <translation>Acepto las licencias.</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>No acepto las licencias.</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translation>Alt+D</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>No se han localizado los archivos de licencia para copiar.</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Se necesita el objeto del instalador en %1 la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>Can not write license file: %1.</source> <translation>No se puede escribir el archivo de licencia: %1.</translation> </message> <message> <source>No license files found to delete.</source> <translation>No se han localizado archivos de licencia para eliminar.</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 3</source> <translation>exactamente 3</translation> </message> </context> <context> <name>QInstaller::MacReplaceInstallNamesOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>at least 3</source> <translation>por lo menos 3</translation> </message> <message> <source>One of the given arguments is empty. Argument1=%1; Argument2=%2, Argument3=%3</source> <translation>Uno de los argumentos dados estÃĄ vacío. Argumento1=%1; Argumento2=%2, Argumento3=%3</translation> </message> <message> <source>Can't invoke otool. Is Xcode installed?</source> <translation>No se puede invocar otool. ÂŋEstÃĄ Xcode instalado?</translation> </message> <message> <source>Can't start process %0.</source> <translation>No se puede iniciar el proceso %0.</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source>Error writing Uninstaller</source> <translation>Error al escribir el desinstalador</translation> </message> <message> <source> Downloading packages...</source> <translation> Descargando paquetes...</translation> </message> <message> <source>Installation canceled by user</source> <translation>InstalaciÃģn cancelada por el usuario</translation> </message> <message> <source>All downloads finished.</source> <translation>Todas las descargas han terminado.</translation> </message> <message> <source>Error</source> <translation>Error</translation> </message> <message> <source>Cancelling the Installer</source> <translation>Cancelando el instalador</translation> </message> <message> <source>Authentication Error</source> <translation>Error de autenticaciÃģn</translation> </message> <message> <source>Some components could not be removed completely because admin rights could not be acquired: %1.</source> <translation>Algunos componentes no se han podido desinstalar completamente por falta de permisos de administrador: %1.</translation> </message> <message> <source>Unknown error.</source> <translation>Error desconocido.</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>Algunos componentes no se han podido desinstalar completamente porque se ha producido un error desconocido.</translation> </message> <message> <source>Application not running in Package Manager mode!</source> <translation>ÂĄLa aplicaciÃģn no se estÃĄ ejecutando en modo gestiÃģn de paquetes!</translation> </message> <message> <source>No installed packages found.</source> <translation>No se han encontrado paquetes instalados.</translation> </message> <message> <source>Application running in Uninstaller mode!</source> <translation>ÂĄLa aplicaciÃģn se estÃĄ ejecutando en modo desinstalador!</translation> </message> <message> <source>invalid</source> <translation>no vÃĄlido</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Error</source> <translation>Error</translation> </message> <message> <source>Component(s) added as automatic dependencies</source> <translation>Componente(s) aÃąadidos como dependencias automÃĄticas</translation> </message> <message> <source>Added as dependency for %1.</source> <translation>AÃąadido como dependencia de %1.</translation> </message> <message> <source>Component(s) that have resolved Dependencies</source> <translation>Componente(s) que tienen dependencias resueltas</translation> </message> <message> <source>Selected Component(s) without Dependencies</source> <translation>Componente(s) seleccionados sin dependencias</translation> </message> <message> <source>Access error</source> <translation>Error de acceso</translation> </message> <message> <source>Format error</source> <translation>Error de formato</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>No se puede escribir la configuraciÃģn del instalador en %1: %2</translation> </message> <message> <source>Stop Processes</source> <translation>Parar procesos</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>Estos procesos se tienen que parar para poder continuar: %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>InstalaciÃģn cancelada por el usuario</translation> </message> <message> <source>Writing uninstaller.</source> <translation>Escribiendo el desinstalador.</translation> </message> <message> <source>Uninstaller is not a bundle</source> <translation>El desinstalador no es un paquete</translation> </message> <message> <source>Cannot write uninstaller data to %1: %2</source> <translation>No se pueden escribir los datos del desinstalador en %1: %2</translation> </message> <message> <source>Cannot write uninstaller to %1: %2</source> <translation>No se pueden escribir el desinstalador en %1: %2</translation> </message> <message> <source>Found a binary data file, but we are the installer and we should read the binary resource from our very own binary!</source> <translation>Se ha localizado un archivo de datos binarios, pero ÂĄsÃģlo el instalador debería leer el recurso binario desde su propio binario!</translation> </message> <message> <source>Cannot write uninstaller binary data to %1: %2</source> <translation>No se pueden escribir los datos binarios del desinstalador en %1: %2</translation> </message> <message> <source>ProductName should be set</source> <translation>Se tiene que establecer ProductName</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>Variable 'TargetDir' sin establecer.</translation> </message> <message> <source>Preparing the installation...</source> <translation>Preparando la instalaciÃģn...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>No es posible instalar desde una ubicaciÃģn de red</translation> </message> <message> <source>Creating local repository</source> <translation>Creando repositorio local</translation> </message> <message> <source>Creating Uninstaller</source> <translation>Creando desinstalador</translation> </message> <message> <source> Installation finished!</source> <translation> ÂĄInstalaciÃģn terminada!</translation> </message> <message> <source> Installation aborted!</source> <translation> ÂĄInstalaciÃģn cancelada!</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>No es posible ejecutar esa operaciÃģn desde una ubicaciÃģn de red</translation> </message> <message> <source>Removing deselected components...</source> <translation>Eliminando componentes desmarcados...</translation> </message> <message> <source> Update finished!</source> <translation> ÂĄActualizaciÃģn terminada!</translation> </message> <message> <source> Update aborted!</source> <translation> ÂĄActualizaciÃģn cancelada!</translation> </message> <message> <source> Uninstallation completed successfully!</source> <translation> ÂĄDesinstalaciÃģn completada con ÃĐxito!</translation> </message> <message> <source> Uninstallation aborted!</source> <translation> ÂĄInstalaciÃģn cancelada!</translation> </message> <message> <source> Installing component %1</source> <translation> Instalando componente %1</translation> </message> <message> <source>Installer Error</source> <translation>Error del instalador</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>Error durante el proceso de instalaciÃģn (%1): %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>No se puede prepara la desinstalaciÃģn</translation> </message> <message> <source>Cannot start uninstall</source> <translation>No se puede iniciar la desinstalaciÃģn</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>Error durante el proceso de desinstalaciÃģn: %1</translation> </message> <message> <source>Unknown error</source> <translation>Error desconocido</translation> </message> <message> <source>Cannot retrieve remote tree: %1.</source> <translation>No se puede recuperar el ÃĄrbol remoto: %1.</translation> </message> <message> <source>Failure to read packages from: %1.</source> <translation>Error al leer los paquetes de: %1.</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>No se pueden recuperar los metadatos: %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>No se puede aÃąadir informaciÃģn sobre la fuente de actualizaciones temporal.</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>No se puede localizar ninguna informaciÃģn sobre la fuente de actualizaciones.</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>InstalaciÃģn de %1</translation> </message> <message> <source>Maintain %1</source> <translation>Mantener %1</translation> </message> <message> <source>Question</source> <translation>Pregunta</translation> </message> <message> <source>Do you want to abort the %1 process?</source> <translation>ÂŋQuieres cancelar el proceso %1?</translation> </message> <message> <source>uninstallation</source> <translation>desinstalaciÃģn</translation> </message> <message> <source>installation</source> <translation>instalaciÃģn</translation> </message> <message> <source>installer</source> <translation>instalador</translation> </message> <message> <source>uninstaller</source> <translation>desinstalador</translation> </message> <message> <source>maintenance</source> <translation>mantenimiento</translation> </message> <message> <source>Do you want to quit the %1 application?</source> <translation>ÂŋQuieres salir de la aplicaciÃģn %1?</translation> </message> <message> <source>Settings</source> <translation>ConfiguraciÃģn</translation> </message> <message> <source>Error</source> <translation>Error</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>No es posible instalar desde una ubicaciÃģn de red. Por favor, copia el instalador a un disco local</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>&Mostrar detalles</translation> </message> <message> <source>&Hide Details</source> <translation>&Ocultar detalles</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>D&esinstalar</translation> </message> <message> <source>Uninstalling %1</source> <translation>Desinstalando %1</translation> </message> <message> <source>&Update</source> <translation>&Actualizar</translation> </message> <message> <source>Updating components of %1</source> <translation>Actualizando componentes de %1</translation> </message> <message> <source>&Install</source> <translation>&Instalar</translation> </message> <message> <source>Installing %1</source> <translation>Instalando %1</translation> </message> </context> <context> <name>QInstaller::QtPatchOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 3</source> <translation>exactamente 3</translation> </message> <message> <source>Needed installer object in "%1" operation is empty.</source> <translation>Se necesita el objeto del instalador en "%1" la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>First argument should be 'linux', 'mac' or 'windows'. No other type is supported at this time.</source> <translation>El primer argumento tiene que ser 'linux', 'mac' o 'windows'. Por el momento no se admiten otros tipos.</translation> </message> <message> <source>Cannot find the needed QmakeOutputInstallerKey(%1) value on the installer object. The ConsumeOutput operation on the valid qmake needs to be called first.</source> <translation>No se puede localizar el valor necesario de QmakeOutputInstallerKey(%1) en el objeto del instalador. La operaciÃģn ConsumeOutput en el qmake vÃĄlido se tiene que invocar antes.</translation> </message> <message> <source>QMake from the current Qt version (%1)is not existing. Please file a bugreport with this dialog at https://bugreports.qt-project.org.</source> <translation>No existe un QMake de la versiÃģn actual) de Qt (%1). Por favor, rellena un informe de fallos haciendo referencia a este diÃĄlogo en https://bugreports.qt-project.org.</translation> </message> <message> <source>The output of %1 -query is not parseable. Please file a bugreport with this dialog https://bugreports.qt-project.org. output: "%2"</source> <translation>La salida de '%1 -query' no es analizable. Por favor, rellena un informe de fallos haciendo referencia a este diÃĄlogo en https://bugreports.qt-project.org. salida: "%2"</translation> </message> <message> <source>Qt patch error: new Qt dir(%1) needs to be less than 255 characters.</source> <translation>Error del parche de Qt: el nuevo directorio de Qt (%1) tiene que ser de menos de 255 caracteres.</translation> </message> <message> <source>Qt patch error: Can not open %1.(%2)</source> <translation>Error del parche de Qt: No se puede abrir %1.(%2)</translation> </message> <message> <source>The installer was not able to get the unpatched path from %1.(maybe it is broken or removed) It tried to patch the Qt binaries, but all other files in Qt are unpatched. This could result in a broken Qt version. Sometimes it helps to restart the installer with a switched off antivirus software.</source> <translation>El instalador no ha podido obtener la ubicaciÃģn no parcheada de %1. (tal vez sea incorrecta o se haya eliminado) Se ha intentado parchear los binarios de Qt, pero el resto de archivos en Qt estÃĄn sin parchear. El resultado de ÃĐsto podría ser una versiÃģn de Qt estropeada. A veces ayuda reiniciar el instalador con el antivirus deshabilitado.</translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>&Show Details</source> <translation>&Mostrar detalles</translation> </message> <message> <source>U&ninstall</source> <translation>D&esinstalar</translation> </message> <message> <source>Ready to Uninstall</source> <translation>Preparado para la desinstalaciÃģn</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>El instalador estÃĄ preparado para empezar a eliminar %1 de tu ordenador.<br><font color="red">El directorio del programa %2 se va a eliminar por completo</font>, ÂĄincluyendo todo el contenido de ese directorio!</translation> </message> <message> <source>U&pdate</source> <translation>&Actualizar</translation> </message> <message> <source>Ready to Update Packages</source> <translation>Preparado para actualizar paquetes</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>El instalador estÃĄ preparado para empezar a actualizar tu instalaciÃģn.</translation> </message> <message> <source>&Install</source> <translation>&Instalar</translation> </message> <message> <source>Ready to Install</source> <translation>Preparado para la instalaciÃģn</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>El instalador estÃĄ preparado para empezar a instalar %1 en tu ordenador.</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation! Available space: %1, at least required %2.</source> <translation>ÂĄNo hay suficiente espacio en disco para almacenar archivos temporales y la instalaciÃģn! Espacio disponible %1, se necesitan por lo menos %2.</translation> </message> <message> <source>Not enough disk space to store all selected components! Available space: %1, at least required: %2.</source> <translation>ÂĄNo hay suficiente espacio en disco para almacenar todos los componentes seleccionados! Espacio disponible %1, se necesitan por lo menos %2.</translation> </message> <message> <source>Not enough disk space to store temporary files! Available space: %1, at least required: %2.</source> <translation>ÂĄNo hay suficiente espacio en disco para almacenar archivos temporales! Espacio disponible %1, se necesitan por lo menos %2.</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>El volumen que has seleccionado para la instalaciÃģn parece ser que tiene suficiente espacio para la instalaciÃģn pero despuÃĐs habrÃĄ menos de 1% de espacio disponible en el volumen. %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>El volumen que has seleccionado para la instalaciÃģn parece ser que tiene suficiente espacio para la instalaciÃģn pero despuÃĐs quedarÃĄn menos de 100 MB disponibles. %1</translation> </message> <message> <source>Can not resolve all dependencies!</source> <translation>ÂĄNo se pueden resolver todas las dependencias!</translation> </message> <message> <source>Components about to be removed.</source> <translation>Componentes que se van a quitar.</translation> </message> <message> <source>&Hide Details</source> <translation>&Ocultar detalles</translation> </message> </context> <context> <name>QInstaller::RegisterDefaultDebuggerOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, 2 expected.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, 2 esperados.</translation> </message> <message> <source>Needed installer object in "%1" operation is empty.</source> <translation>Se necesita el objeto del instalador en "%1" la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>There is no value set for %1 on the installer object.</source> <translation>No se ha asignado un valor a %1 en el objeto del instalador.</translation> </message> <message> <source>Can't read from tool chains xml file(%1) correctly.</source> <translation>No se puede leer correctamente el archivo xml de la cadena de herramientas (%1).</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>2 to 5</source> <translation>de 2 a 5</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>Registro de tipo de archivo: argumentos no vÃĄlidos</translation> </message> </context> <context> <name>QInstaller::RegisterQtInCreatorQNXOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>at least 5</source> <translation>por lo menos 5</translation> </message> <message> <source>Needed installer object in "%1" operation is empty.</source> <translation>Se necesita el objeto del instalador en "%1" la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>There is no value set for %1 on the installer object.</source> <translation>No se ha asignado un valor a %1 en el objeto del instalador.</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, minimum 4 expected.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, se esperaban al menos 4.</translation> </message> </context> <context> <name>QInstaller::RegisterToolChainOperation</name> <message> <source>at least 4</source> <translation>por lo menos 4</translation> </message> <message> <source>Needed installer object in '%1' operation is empty.</source> <translation>Se necesita el objeto del instalador en '%1' la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>There is no value set for '%1' on the installer object.</source> <translation>No se ha asignado un valor a '%1' en el objeto del instalador.</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, minimum 4 expected.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, se esperaban al menos 4.</translation> </message> <message> <source>Needed installer object in "%1" operation is empty.</source> <translation>Se necesita el objeto del instalador en "%1" la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>Can't read from tool chains xml file(%1) correctly.</source> <translation>No se puede leer correctamente el archivo xml de la cadena de herramientas (%1).</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>Some arguments are not right in %1 operation.</source> <translation>Algunos argumentos no son correctos en la operaciÃģn %1.</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 3</source> <translation>exactamente 3</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>Completando el asistente de instalaciÃģn %1</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open the requested script file at %1: %2.</source> <translation>No se puede abrir el archivo de script %1 solicitado: %2.</translation> </message> <message> <source>Exception while loading the component script: '%1'</source> <translation>ExcepciÃģn al cargar el script de componente: '%1'</translation> </message> <message> <source>Cannot load the component script inside a script context: '%1'</source> <translation>No se puede cargar el script de componente dentro de un contexto de script: '%1'</translation> </message> <message> <source>Fatal error while evaluating a script.</source> <translation>Error fatal al evaluar un script.</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in '%1' operation is empty.</source> <translation>Se necesita el objeto del instalador en "%1" la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>Autoreinicio: sÃģlo es vÃĄlido en el ÃĄmbito del actualizador o del modo de gestor de paquetes.</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>Autoreinicio: argumentos no vÃĄlidos</translation> </message> </context> <context> <name>QInstaller::SetDemosPathOnQtOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>The output of '%1 -query' is not parseable. Please file a bugreport with this dialog at https://bugreports.qt-project.org. output: %2</source> <translation>La salida de '%1 -query' no es analizable. Por favor, rellena un informe de fallos haciendo referencia a este diÃĄlogo en https://bugreports.qt-project.org. salida: %2</translation> </message> <message> <source>Qt patch error: new Qt demo path '%1' needs to be less than 255 characters.</source> <translation>Error del parche de Qt: la nueva ubicaciÃģn de las demos de Qt '%1' tiene que ser de menos de 255 caracteres.</translation> </message> </context> <context> <name>QInstaller::SetExamplesPathOnQtOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>The output of '%1 -query' is not parseable. Please file a bugreport with this dialog at https://bugreports.qt-project.org. output: %2</source> <translation>La salida de '%1 -query' no es analizable. Por favor, rellena un informe de fallos haciendo referencia a este diÃĄlogo en https://bugreports.qt-project.org. salida: %2</translation> </message> <message> <source>Qt patch error: new Qt example path '%1' needs to be less than 255 characters.</source> <translation>Error del parche de Qt: la nueva ubicaciÃģn del ejemplo de Qt '%1' tiene que ser de menos de 255 caracteres.</translation> </message> </context> <context> <name>QInstaller::SetImportsPathOnQtCoreOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>Qt patch error: new Qt imports path '%1' needs to be less than 255 characters.</source> <translation>Error del parche de Qt: la nueva ubicaciÃģn de los imports de Qt '%1' tiene que ser de menos de 255 caracteres.</translation> </message> </context> <context> <name>QInstaller::SetPathOnQtCoreOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 3</source> <translation>exactamente 3</translation> </message> <message> <source>The second type/value needs to be one of: %1</source> <translation>El segundo tipo/valor tiene que ser uno de: %1</translation> </message> </context> <context> <name>QInstaller::SetPluginPathOnQtCoreOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>Qt patch error: new Qt plugin path '%1' needs to be less than 255 characters.</source> <translation>Error del parche de Qt: la nueva ubicaciÃģn del complemento de Qt '%1' tiene que ser de menos de 255 caracteres.</translation> </message> </context> <context> <name>QInstaller::SetQtCreatorValueOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 4</source> <translation>exactamente 4</translation> </message> <message> <source> (rootInstallPath, group, key, value)</source> <translation> (rootInstallPath, group, key, value)</translation> </message> <message> <source>Needed installer object in "%1" operation is empty.</source> <translation>Se necesita el objeto del instalador en "%1" la operaciÃģn estÃĄ vacía.</translation> </message> <message> <source>There is no value set for '%1' on the installer object.</source> <translation>No se ha asignado un valor a %1 en el objeto del instalador.</translation> </message> <message> <source>Needed installer object in '%1' operation is empty.</source> <translation>Se necesita el objeto del instalador en "%1" la operaciÃģn estÃĄ vacía.</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argumentos no vÃĄlidos en %0: %1 argumentos dados, %2 esperados %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactamente 2</translation> </message> <message> <source>None of the arguments can be empty: source '%1', target '%2'.</source> <translation>Ninguno de los argumentos puede estar vacío: origen '%1', destino '%2'.</translation> </message> <message> <source>Can not move source '%1' to target '%2', because target exists and is not removable.</source> <translation>No se puede mover el origen '%1' al destino '%2', porque el destino ya existe y no se puede eliminar.</translation> </message> <message> <source>Can not move source '%1' to target '%2': %3</source> <translation>No se puede mover el origen '%1' al destino '%2': %3</translation> </message> <message> <source>Move '%1' to '%2'.</source> <translation>Mover '%1' a '%2'.</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>Accesos directos del menÚ de inicio</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.</source> <translation>Selecciona el menÚ de inicio en el que te gustaría crear los accesos directos del programa. TambiÃĐn puedes introducir un nombre para crear una carpeta nueva.</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>Carpeta de instalaciÃģn</translation> </message> <message> <source>Please specify the folder where %1 will be installed.</source> <translation>Por favor, especifica la carpeta donde se instalarÃĄ %1.</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translation>Alt+R</translation> </message> <message> <source>B&rowse...</source> <translation>E&xaminar...</translation> </message> <message> <source>Error</source> <translation>Error</translation> </message> <message> <source>The install directory cannot be empty, please specify a valid folder.</source> <translation>El directorio de instalaciÃģn no puede estar vacío, por favor especifica una carpeta vÃĄlida.</translation> </message> <message> <source>As the install directory is completely deleted on uninstall, installing in %1 is forbidden.</source> <translation>Como el directorio de instalaciÃģn se elimina completamente en la desinstalaciÃģn, se prohibe la instalaciÃģn en %1.</translation> </message> <message> <source>Warning</source> <translation>Advertencia</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation>Has seleccionado una carpeta que ya existe y que no estÃĄ vacía para la instalaciÃģn. Ten en cuenta que se eliminarÃĄ completamente cuando se desinstale esta aplicaciÃģn. No se recomienda realizar la instalaciÃģn en esta carpeta ya que puede fallar. ÂŋQuieres continuar?</translation> </message> <message> <source>Select Installation Folder</source> <translation>Selecciona una carpeta de instalaciÃģn</translation> </message> </context> <context> <name>QInstallerCreator::Archive</name> <message> <source>Cannot create %1: %2</source> <translation>No se puede crear %1: %2</translation> </message> <message> <source>Cannot open archive file %1 for reading.</source> <translation>No se puede abrir el archivo %1 en modo lectura.</translation> </message> <message> <source>Cannot create archive from %1: Not a file.</source> <translation>No se puede crear el archivo de %1: no es un archivo.</translation> </message> <message> <source>Error while packing directory at %1</source> <translation>Error al empaquetar el directorio en %1</translation> </message> </context> <context> <name>QObject</name> <message> <source>Searched whole file, no marker found</source> <translation>BÚsqueda en todo el archivo terminada, marcador no encontrado</translation> </message> <message> <source>Cannot seek to %1 in file %2: %3</source> <translation>No se puede solicitar %1 en el archivo %2: %3</translation> </message> <message> <source>No marker found, stopped after %1.</source> <translation>No se ha encontrado ningÚn marcador, se ha parado despuÃĐs de %1.</translation> </message> <message> <source>No marker found, unknown exception caught.</source> <translation>No se ha encontrado ningÚn marcador, excepciÃģn desconocida capturada.</translation> </message> <message> <source>Cannot create zipped file for path %1: %2</source> <translation>No se puede crear el archivo comprimido para la ubicaciÃģn %1: %2</translation> </message> <message> <source>Cannot seek to in-binary resource. (offset: %1, length: %2)</source> <translation>No se puede realizar una solicitud - recurso binario. (offset: %1, longitud: %2)</translation> </message> <message> <source>Cannot register in-binary resource.</source> <translation>No se puede registrar - recurso binario.</translation> </message> <message> <source>Cannot open binary %1: %2</source> <translation>No se puede abrir el binario %1: %2</translation> </message> <message> <source>Cannot seek to binary layout section.</source> <translation>No se puede solicitar la secciÃģn de la disposiciÃģn del binario.</translation> </message> <message> <source>Cannot seek to metadata index.</source> <translation>No se puede solicitar el índice de los metadatos.</translation> </message> <message> <source>Cannot seek to operation list.</source> <translation>No se puede solicitar la lista de operaciones.</translation> </message> <message> <source>Cannot seek to component index information.</source> <translation>No se puede solicitar la informaciÃģn del índice del componente.</translation> </message> <message> <source>Cannot seek to component index.</source> <translation>No se puede solicitar el índice del componente.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>No se puede abrir el archivo %1 en modo lectura: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>No se puede abrir el archivo %1 en modo escritura: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>La escritura ha fallado despuÃĐs de %1 bytes: %2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>La lectura ha fallado despuÃĐs de %1 bytes: %2</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>No se puede eliminar el archivo %1: %2</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>No se puede eliminar la carpeta %1: %2</translation> </message> <message> <source>Cannot create folder %1</source> <translation>No se puede crear la carpeta %1</translation> </message> <message> <source>Cannot copy file from %1 to %2: %3</source> <translation>No se puede copiar el archivo de %1 a %2: %3</translation> </message> <message> <source>Cannot move file from %1 to %2: %3</source> <translation>No se puede mover el archivo de %1 a %2: %3</translation> </message> <message> <source>Cannot create folder %1: %2</source> <translation>No se puede crear la carpeta %1: %2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>No se puede abrir el archivo temporal: %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>No se puede abrir el archivo temporal para la plantilla %1: %2</translation> </message> <message> <source>Cannot create temporary folder for template %1: %2</source> <translation>No se puede crear la carpeta temporal para la plantilla %1: %2</translation> </message> <message> <source>Cannot create lock file %1: %2</source> <translation>No se puede crear el archivo de bloqueo %1: %2</translation> </message> <message> <source>Cannot write PID to lock file %1: %2</source> <translation>No se puede escribir el PID para bloquear el archivo %1: %2</translation> </message> <message> <source>Cannot lock lock file %1: %2</source> <translation>No se puede bloquear el archivo de bloqueo %1: %2</translation> </message> <message> <source>Cannot unlock lock file %1: %2</source> <translation>No se puede desbloquear el archivo de bloqueo %1: %2</translation> </message> <message> <source>Path exists but is not a folder: %1</source> <translation>La ubicaciÃģn existe pero no es una carpeta: %1</translation> </message> <message> <source>Cannot create folder: %1</source> <translation>No se puede crear la carpeta: %1</translation> </message> <message> <source>Cannot create temporary file</source> <translation>No se puede crear el archivo temporal</translation> </message> <message> <source>Cannot retrieve property %1 for item %2</source> <translation>No se puede recuperar la propiedad %1 del elemento %2</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3</source> <translation>La propiedad %1 del elemento %2 no es del tipo VT_FILETIME pero sí de %3</translation> </message> <message> <source>Cannot convert file time to local time</source> <translation>No se puede convertir la hora del archivo a hora local</translation> </message> <message> <source>Cannot convert local file time to system time</source> <translation>No se puede convertir la hora local del archivo a hora del sistema</translation> </message> <message> <source>No device set for output stream</source> <translation>No se ha asignado un dispositivo para el flujo de salida</translation> </message> <message> <source>Cannot load codecs</source> <translation>No se pueden cargar los cÃģdecs</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>No se puede recuperar el formato predeterminado</translation> </message> <message> <source>Cannot open archive</source> <translation>No se puede abrir el archivo</translation> </message> <message> <source>No CArc found</source> <translation>No se ha localizado ningÚn CArc</translation> </message> <message> <source>Cannot retrieve number of items in archive</source> <translation>No se puede recuperar el nÚmero de elementos en el archivo</translation> </message> <message> <source>Cannot retrieve path of archive item %1</source> <translation>No se puede recuperar la ubicaciÃģn del elemento %1 del archivo</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>ExcepciÃģn desconocida capturada (%1)</translation> </message> <message> <source>Failed</source> <translation>Fallo</translation> </message> <message> <source>Cannot remove already existing symlink. %1</source> <translation>No se puede eliminar el enlace simbÃģlico que ya hay. %1</translation> </message> <message> <source>Cannot open file: %1 (%2)</source> <translation>No se puede abrir el archivo: %1 (%2)</translation> </message> <message> <source>Cannot create symlink at '%1'. Another one is already existing.</source> <translation>No se puede crear el enlace simbÃģlico en '%1'. Ya hay otro.</translation> </message> <message> <source>Cannot read symlink target from file '%1'.</source> <translation>No se puede leer el enlace simbÃģlico de destino del archivo '%1'.</translation> </message> <message> <source>Cannot create symlink at %1. %2</source> <translation>No se puede crear el enlace simbÃģlico en %1. %2</translation> </message> <message> <source>internal code: %1</source> <translation>cÃģdigo interno: %1</translation> </message> <message> <source>not enough memory</source> <translation>no hay suficiente memoria</translation> </message> <message> <source>Error: %1</source> <translation>Error: %1</translation> </message> <message> <source>Cannot create archive %1. %2</source> <translation>No se puede crear el archivo %1. %2</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Error al extraer '%1': %2</translation> </message> <message> <source>CArc index %1 out of bounds [0, %2]</source> <translation>El índice %1 de CArc estÃĄ fuera de los límites [0, %2]</translation> </message> <message> <source>Item index %1 out of bounds [0, %2]</source> <translation>El índice %1 del elemento estÃĄ fuera de los límites [0, %2]</translation> </message> <message> <source>Cannot create output file for writing: %1</source> <translation>No se puede crear el archivo de salida para su escritura: %1</translation> </message> <message> <source>Authorization required</source> <translation>AutorizaciÃģn requerida</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>Introduce tu contraseÃąa para autorizar a sudo:</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>Error al adquirir permisos de administrador</translation> </message> <message> <source>Cannot backup file %1</source> <translation>No se puede hacer una copia de seguridad del archivo %1</translation> </message> <message> <source>Cannot delete file %1</source> <translation>No se puede eliminar el archivo %1</translation> </message> <message> <source>Cannot restore backup file into %1</source> <translation>No se puede restaurar la copia de seguridad del archivo como %1</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>Fallo al sobrescribir %1: %2</translation> </message> <message> <source>Registry path %1 is not writable</source> <translation>No se puede escribir en la ubicaciÃģn %1 del registro</translation> </message> <message> <source>Cannot write to registry path %1</source> <translation>No se puede escribir en la ubicaciÃģn %1 del registro</translation> </message> <message> <source>Invalid Argument: source folder must not be empty.</source> <translation>Argumento no vÃĄlido: la carpeta de origen no tiene que estar vacía.</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation>No se puede hacer una copia de seguridad del archivo %1: %2</translation> </message> <message> <source>Failed to copy file %1: %2</source> <translation>Fallo al copiar el archivo %1: %2</translation> </message> <message> <source>Cannot create folder at %1: %2</source> <translation>No se puede crear la carpeta %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, %2 to %3 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, de %2 a %3 esperados.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, %2 expected.</source> <translation>Argumentos no vÃĄlidos: %1 argumentos dados, %2 esperados.</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>Error al dar permisos de acceso.</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>Fallo al solicitar el archivo %1: %2</translation> </message> <message> <source>Failed to open %1 for reading</source> <translation>Fallo al abrir %1 en modo lectura</translation> </message> <message> <source>Failed to open %1 for writing</source> <translation>Fallo al abrir %1 en modo escritura</translation> </message> <message> <source>Failed to seek in file %1. Reason: %2.</source> <translation>Fallo al solicitar el archivo %1. Motivo: %2.</translation> </message> <message> <source>Cannot create link from %1 to %2.</source> <translation>No se puede crear el enlace de %1 a %2.</translation> </message> <message> <source>Cannot remove link from %1 to %2.</source> <translation>No se puede eliminar el enlace de %1 a %2.</translation> </message> <message> <source>Authorization Error</source> <translation>Error de autorizaciÃģn</translation> </message> <message> <source>Couldn't get authorization.</source> <translation>No se ha podido obtener la autorizaciÃģn.</translation> </message> <message> <source>Couldn't get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as root and then clicking ok.</source> <translation>No se ha podido obtener la autorizaciÃģn necesaria para continuar con la instalaciÃģn. Cancela la instalaciÃģn o bien usa la soluciÃģn alternativa ejecutando %1 como root y haciendo clic en OK.</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>El registro de tipos de archivo sÃģlo estÃĄ soportado en Windows.</translation> </message> <message> <source>Failed to open '%1' for reading.</source> <translation>Fallo al abrir '%1' en modo lectura.</translation> </message> <message> <source>Failed to open '%1' for writing.</source> <translation>Fallo al abrir %1 en modo escritura.</translation> </message> <message> <source>Number of arguments does not match: one is required</source> <translation>El nÚmero de argumentos no coincide: es necesario que haya uno</translation> </message> <message> <source>Cannot get package manager core.</source> <translation>No se puede obtener el nÚcleo del gestor de paquetes.</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>Este proceso se tiene que parar antes de continuar: %1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>Estos procesos se tienen que parar antes de continuar: %1</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>No se puede abrir el archivo de configuraciÃģn %1 en modo lectura: %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>ConfiguraciÃģn</translation> </message> <message> <source>Network</source> <translation>Red</translation> </message> <message> <source>No proxy</source> <translation>Sin proxy</translation> </message> <message> <source>System proxy settings</source> <translation>ConfiguraciÃģn del proxy del sistema</translation> </message> <message> <source>Manual proxy configuration</source> <translation>ConfiguraciÃģn manual del proxy</translation> </message> <message> <source>HTTP proxy:</source> <translation>Proxy HTTP:</translation> </message> <message> <source>Port:</source> <translation>Puerto:</translation> </message> <message> <source>HTTP proxy requires authentication</source> <translation>El proxy HTTP requiere autenticaciÃģn</translation> </message> <message> <source>Username:</source> <translation>Nombre de usuario:</translation> </message> <message> <source>Password:</source> <translation>ContraseÃąa:</translation> </message> <message> <source>FTP proxy:</source> <translation>Proxy del FTP:</translation> </message> <message> <source>FTP proxy requires authentication</source> <translation>El proxy del FTP requiere autenticaciÃģn</translation> </message> <message> <source>Repositories</source> <translation>Repositorios</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>Si es necesario, aÃąade un nombre de usuario y contraseÃąa para la autenticaciÃģn.</translation> </message> <message> <source>Use temporary repositories only</source> <translation>SÃģlo usar repositorios temporales</translation> </message> <message> <source>Add</source> <translation>AÃąadir</translation> </message> <message> <source>Remove</source> <translation>Eliminar</translation> </message> <message> <source>Test</source> <translation>Probar</translation> </message> <message> <source>Show Passwords</source> <translation>Mostrar contraseÃąas</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>Marca esto para usar el repositorio durante la obtenciÃģn.</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>AÃąade el nombre de usuario para autenticarse en el servidor.</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>AÃąade la contraseÃąa para autenticarse en el servidor.</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>La URL del servidor que contiene un repositorio vÃĄlido.</translation> </message> <message> <source>There was an error testing this repository.</source> <translation>Se ha producido un error al probar este repositorio.</translation> </message> <message> <source>Do you want to disable the tested repository?</source> <translation>ÂŋQuieres deshabilitar el repositorio probado?</translation> </message> <message> <source>Hide Passwords</source> <translation>Ocultar contraseÃąas</translation> </message> <message> <source>Use</source> <translation>Usar</translation> </message> <message> <source>Username</source> <translation>Nombre de usuario</translation> </message> <message> <source>Password</source> <translation>ContraseÃąa</translation> </message> <message> <source>Repository</source> <translation>Repositorio</translation> </message> <message> <source>Default repositories</source> <translation>Repositorios predeterminados</translation> </message> <message> <source>Temporary repositories</source> <translation>Repositorios temporales</translation> </message> <message> <source>User defined repositories</source> <translation>Repositorios definidos por el usuario</translation> </message> </context> <context> <name>TargetDirectoryPageImpl</name> <message> <source>The installation path cannot be empty, please specify a valid folder.</source> <translation>La ruta de instalaciÃģn no puede estar vacía. Por favor ,especifica una carpeta vÃĄlida.</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>La ruta de la instalaciÃģn no puede ser relativa. Por favor ,especifica una ruta absoluta.</translation> </message> <message> <source>Warning</source> <translation>Advertencia</translation> </message> <message> <source>Error</source> <translation>Error</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>La ruta o el directorio de instalaciÃģn contiene caracteres que no son ASCII. ÂĄActualmente ÃĐsto no estÃĄ soportado! Por favor, escoge una ruta o directorio de instalaciÃģn diferente.</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>La ruta que has introducido es demasiado larga. Por favor, asegÚrate que especificas una ruta vÃĄlida.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>La ruta que has introducido no es vÃĄlida. Por favor, asegÚrate que especificas un volÚmen de disco vÃĄlido.</translation> </message> <message> <source>The installation path must not contain %1, please specify a valid folder.</source> <translation>La ruta de la instalaciÃģn no puede contener %1. Por favor ,especifica una carpeta vÃĄlida.</translation> </message> <message> <source>As the install directory is completely deleted installing in %1 is forbidden.</source> <translation>Como el directorio de instalaciÃģn se elimina completamente, se prohibe la instalaciÃģn en %1.</translation> </message> <message> <source>The folder you selected exists already and contains an installation. Do you want to overwrite it?</source> <translation>La carpeta que has seleccionado ya existe y contiene una instalaciÃģn. ÂŋQuieres sobrescribirla?</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation>Has seleccionado una carpeta que ya existe y que no estÃĄ vacía para la instalaciÃģn. Ten en cuenta que se eliminarÃĄ completamente cuando se desinstale esta aplicaciÃģn. No se recomienda realizar la instalaciÃģn en esta carpeta ya que puede fallar. ÂŋQuieres continuar?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>Has seleccionado un archivo o enlace simbÃģlico que ya existe. Por favor, elige un destino diferente para la instalaciÃģn.</translation> </message> </context> <context> <name>TestRepository</name> <message> <source>Empty repository URL.</source> <translation>URL del repositorio vacía.</translation> </message> <message> <source>URL scheme not supported: %1 (%2).</source> <translation>Esquema de URL no admitido: %1 (%2).</translation> </message> <message> <source>Cannot parse Updates.xml! Error: %1.</source> <translation>ÂĄError al analizar Updates.xml! Error: %1.</translation> </message> <message> <source>Updates.xml could not be opened for reading!</source> <translation>ÂĄNo se puede abrir Updates.xml en modo lectura!</translation> </message> <message> <source>Updates.xml could not be found on server!</source> <translation>ÂĄNo se puede localizar Updates.xml en el servidor!</translation> </message> </context> </TS> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/ifw_fr.ts����������������������������������������������������������������������0000664�0000000�0000000�00000331160�13253666515�0017013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="fr_FR"> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>%1 sur %2</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>Le proxy requiert une authentification.</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>Impossible de rechercher dans %1 pour lire les donnÃĐes d'exploitation.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>Impossible de rechercher dans %1 pour lire l'ensemble des ressources.</translation> </message> <message> <source>Cannot open meta resource. Error: %1</source> <translation>Impossible d'ouvrir les mÃĐtadonnÃĐes des ressources. Erreur : %1</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>Impossible de rechercher dans %1 pour lire le nombre de mÃĐtadonnÃĐes.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation>Impossible de rechercher dans %1 pour lire le segment de l'ensemble des ressources.</translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>IncohÃĐrence relevÃĐe sur les mÃĐtadonnÃĐes. Lues %1, attendues : %2.</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>Authentification HTTP requise</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>Vous devez saisir un identifiant et un mot de passe pour accÃĐder à ce site.</translation> </message> <message> <source>Username:</source> <translation>Identifiant :</translation> </message> <message> <source>Password:</source> <translation>Mot de passe :</translation> </message> <message> <source>%1 at %2</source> <translation>%1 à %2</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path exists but is not a folder: %1</source> <translation>Le chemin existe mais n'est pas un dossier : %1</translation> </message> <message> <source>Cannot create folder: %1</source> <translation>Impossible de crÃĐer le dossier : %1</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Impossible de rÃĐcupÃĐrer le chemin de l'ÃĐlÃĐment %1</translation> </message> <message> <source>Cannot remove already existing symlink. %1</source> <translation>Impossible de supprimer le lien symbolique existant. %1</translation> </message> <message> <source>Cannot open file: %1 (%2)</source> <translation>Impossible d'ouvrir le fichier %1 (%2)</translation> </message> <message> <source>Cannot create symlink at '%1'. Another one is already existing.</source> <translation>Impossible de crÃĐer le lien symbolique à '%1'. Un autre existe dÃĐjà.</translation> </message> <message> <source>Cannot read symlink target from file '%1'.</source> <translation>Impossible de rÃĐcupÃĐrer la cible du lien symbolique du fichier '%1'.</translation> </message> <message> <source>Cannot create symlink at %1. %2</source> <translation>Impossible de crÃĐer le lien symbolique à %1. %2</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>Composants ajoutÃĐs comme dÃĐpendances automatiques : </translation> </message> <message> <source>Components added as dependency for '%1':</source> <translation>Composants ajoutÃĐs comme dÃĐpendances pour %1 : </translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>Composants ayant des dÃĐpendances rÃĐsolues :</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>Composants sÃĐlectionnÃĐs ne possÃĐdant pas de dÃĐpendance : </translation> </message> <message> <source>Recursion detected, component '%1' already added with reason: '%2'</source> <translation>RÃĐcursion dÃĐtectÃĐe, composant '%1' ajoutÃĐ via le contexte : '%2'</translation> </message> <message> <source>Cannot find missing dependency '%1' for '%2'.</source> <translation>Impossible de satisfaire la dÃĐpendance '%1' pour '%2".</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>AnnulÃĐ</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file '%1': %2</source> <translation>Impossible de poser un fichier de verrouillage '%1' : %2</translation> </message> <message> <source>Cannot write PID to lock file '%1': %2</source> <translation>Impossible d'ÃĐcrire le PID pour le verrou de fichier '%1' : '%2'</translation> </message> <message> <source>Cannot obtain the lock for file '%1': %2</source> <translation>Impossible d'obtenir le verrou pour le fichier '%1' : %2</translation> </message> <message> <source>Cannot release the lock for file '%1': %2</source> <translation>Impossible de relÃĒcher le verrou pour le fichier '%1' : %2</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Impossible de sauvegarder le fichier %1 : %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactement 2</translation> </message> <message> <source>Cannot open file '%1' for writing: %2</source> <translation>Impossible d'ouvrir le fichier %1 en ÃĐcriture : %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Impossible de trouver la sauvegarde du fichier %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Impossible de charger la sauvegarde du fichier %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Impossible de restaurer la sauvegarde du fichier %1 : %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot backup file %1.</source> <translation>Impossible de faire une sauvegarde du fichier %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Arguments invalides : %1 arguments fournis, 2 attendus.</translation> </message> <message> <source>Cannot copy a non-existent file: %1</source> <translation>Impossible de copier un fichier non-existant : %1</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Impossible de supprimer le fichier de destination %1 : %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Impossible de copier %1 vers %2 : %3</translation> </message> <message> <source>Cannot delete file %1: %2</source> <translation>Impossible de supprimer le fichier %1 : %2</translation> </message> <message> <source>Cannot restore backup file into %1: %2</source> <translation>Impossible de restaurer la sauvegarde du fichier vers %1 : %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of %1: %2</source> <translation>Impossible de crÃĐer la sauvegarde de %1 : %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Arguments invalides : %1 arguments fournis, 1 seul attendu.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Impossible de restaurer le fichier de sauvegarde pour %1 : %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download canceled.</source> <translation>TÃĐlÃĐchargement annulÃĐ.</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>Les empreintes cryptographiques ne correspondent pas.</translation> </message> <message> <source>Download finished.</source> <translation>TÃĐlÃĐchargement terminÃĐ.</translation> </message> <message> <source>%1 of %2</source> <translation>%1 sur %2</translation> </message> <message> <source>%1 downloaded.</source> <translation>%1 tÃĐlÃĐchargÃĐ.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/s)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n jour, </numerusform> <numerusform>%n jours, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n heure, </numerusform> <numerusform>%n heures, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n minute</numerusform> <numerusform>%n minutes</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n seconde</numerusform> <numerusform>%n secondes</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 restant.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - impossible d'estimer le temps restant.</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1: Writing to file '%2' failed: %3</source> <translation>Impossible de tÃĐlÃĐcharger %1 : l'ÃĐcriture du fichier '%2' à ÃĐchouÃĐ : %3</translation> </message> <message> <source>Cannot download %1: Cannot create %2: %3</source> <translation>Impossible de tÃĐlÃĐcharger %1 : impossible de crÃĐer %2 : %3</translation> </message> <message> <source>%1 at %2</source> <translation>%1 sur %2</translation> </message> <message> <source>Authentication request canceled.</source> <translation>Demande d'authentification annulÃĐe.</translation> </message> <message> <source>Secure Connection Failed</source> <translation>Échec de la connexion sÃĐcurisÃĐe</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>Une erreur s'est produite pendant la connection à : %1.</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>Cela pourrait Être un problÃĻme avec la configuration du serveur, ou quelqu'un essaie de se faire passer pour le serveur.</translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>Si vous vous Êtes dÃĐjà connectÃĐ Ã  ce serveur avec succÃĻs par le passÃĐ ou si vous faites confiance à ce serveur, l'erreur peut Être temporaire et vous pouvez essayer de nouveau.</translation> </message> <message> <source>Try again</source> <translation>Essayer à nouveau</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open source file '%1' for reading.</source> <translation>Impossible d'ouvrir le fichier source '%1' en lecture.</translation> </message> <message> <source>Cannot open destination file '%1' for writing.</source> <translation>Impossible d'ouvrir le fichier %1 en ÃĐcriture.</translation> </message> <message> <source>Writing to %1 failed: %2</source> <translation>L'ÃĐcriture de %1 à ÃĐchouÃĐe : %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Arguments invalides : %1 arguments fournis, 1 seul attendu.</translation> </message> <message> <source>Cannot create folder %1: Unknown error.</source> <translation>Impossible de crÃĐer le dossier %1 : erreur indÃĐterminÃĐe.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Impossible de supprimer le dossier %1 : %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file %1.</source> <translation>Impossible de sauvegarder le fichier %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Arguments invalides : %1 arguments fournis, 2 attendus.</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Impossible de supprimer le fichier de destination %1 : %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Impossible de copier %1 vers %2 : %3</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Impossible de copier %1 vers %2 : %3</translation> </message> <message> <source>Cannot remove file %1.</source> <translation>Impossible de supprimer le fichier %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation></translation> </message> </context> <context> <name>KDUpdater::PackagesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 contient des informations non valides : %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>Le fichier %1 n'existe pas.</translation> </message> <message> <source>Cannot open %1.</source> <translation>Impossible d'ouvrir %1.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Erreur d'analyse syntaxique dans %1 à %2, %3 : %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>ÉlÃĐment racine %1 inattendu, il devrait se trouver dans 'Packages'.</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Impossible de sauvegarder le fichier %1 : %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Arguments invalides : %1 arguments fournis, 2 attendus.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Impossible d'ouvrir le fichier %1 en lecture : %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Impossible d'ouvrir le fichier %1 en ÃĐcriture : %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Impossible de trouver la sauvegarde du fichier %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Impossible de restaurer le fichier de sauvegarde pour %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Impossible de restaurer le fichier de sauvegarde pour %1 : %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1". Reason:</source> <translation>Impossible de lire le fichier de ressources "%1". Raison : </translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Arguments invalides : %1 arguments fournis, 1 seul attendu.</translation> </message> <message> <source>Cannot remove folder %1: The folder does not exist.</source> <translation>Impossible de supprimer le dossier %1 : ce dossier n'existe pas.</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Impossible de supprimer le dossier %1 : %2</translation> </message> <message> <source>Cannot recreate directory %1: %2</source> <translation>Impossible de recrÃĐer le dossier %1 : %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 commencÃĐe</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>%1 ne peut Être stoppÃĐe</translation> </message> <message> <source>Cannot stop task %1</source> <translation>Impossible d'arrÊter la tÃĒche %1</translation> </message> <message> <source>%1 cannot be paused</source> <translation>%1 ne peut Être mise en pause</translation> </message> <message> <source>Cannot pause task %1</source> <translation>Impossible de mettre en pause %1</translation> </message> <message> <source>Cannot resume task %1</source> <translation>Impossible de reprendre l'exÃĐcution de la tÃĒche %1</translation> </message> <message> <source>%1 done</source> <translation>%1 terminÃĐe</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>Impossible d'accÃĐder aux informations contenues dans ce paquet pour cette application.</translation> </message> <message> <source>Cannot access the update sources information of this application.</source> <translation>Impossible d'accÃĐder aux informations de mise à jour pour cette application.</translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>TÃĐlÃĐchargement du fichier Updates.xml à partir des sources de mises à jour.</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>%n mise à jour trouvÃĐe.</numerusform> <numerusform>%n mises à jour trouvÃĐes.</numerusform> </translation> </message> <message> <source>Cannot download update source %1 from ('%2')</source> <translation>Impossible de tÃĐlÃĐcharger l'emplacement des mises à jour pour %1 ('%2')</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>Fichier(s) Updates.xml tÃĐlÃĐchargÃĐ(s) à partir des sources de mise à jour.</translation> </message> <message> <source>Computing applicable updates.</source> <translation>Calcul des mises à jour à appliquer.</translation> </message> <message> <source>Application updates computed.</source> <translation>Mises à jour de l'application calculÃĐes.</translation> </message> </context> <context> <name>KDUpdater::UpdateSourcesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 contient des informations invalides : %2</translation> </message> <message> <source>Cannot read "%1"</source> <translation>Impossible de lire "%1"</translation> </message> <message> <source>XML Parse error in %1 at %2, %3: %4</source> <translation>Erreur d'analyse syntaxique du XML dans %1 à %2, %3 : %4</translation> </message> <message> <source>Root element %1 unexpected, should be "UpdateSources"</source> <translation>ÉlÃĐment racine %1 inattendu, il devrait se trouver dans "UpdateSources"</translation> </message> <message> <source>Cannot save changes to "%1": %2</source> <translation>Impossible de sauvegarder les changements dans "%1" : %2</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Cannot read "%1"</source> <translation>Impossible de lire "%1"</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Erreur d'analyse syntaxique dans %1 à %2, %3 : %4</translation> </message> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Updates.xml contient des informations invalides : %1</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>ÉlÃĐment racine %1 inattendu, "Updates" aurait dÃŧ Être trouvÃĐ.</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>L'ÃĐlÃĐment 'ApplicationName' est manquant.</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>L'ÃĐlÃĐment 'ApplicationVersion' est manquant.</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>L'ÃĐlÃĐment 'PackageUpdate' ne possÃĻde pas l'attribut 'Name'</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>L'ÃĐlÃĐment 'PackageUpdate' ne possÃĻde pas l'attribut 'Version'</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>L'ÃĐlÃĐment 'PackageUpdate' ne possÃĻde pas l'attribut 'ReleaseDate'</translation> </message> </context> <context> <name>Lib7z</name> <message> <source>Cannot retrieve number of items in archive</source> <translation>Impossible de rÃĐcupÃĐrer le nombre d'ÃĐlÃĐments dans l'archive</translation> </message> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Impossible de rÃĐcupÃĐrer le chemin de l'ÃĐlÃĐment %1</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Une exception de type inconnue a ÃĐtÃĐ attrapÃĐe (%1)</translation> </message> <message> <source>internal code: %1</source> <translation>code interne : %1</translation> </message> <message> <source>not enough memory</source> <translation>pas assez de mÃĐmoire</translation> </message> <message> <source>Error: %1</source> <translation>Erreur : %1</translation> </message> <message> <source>Cannot load codecs</source> <translation>Impossible de charger les codecs</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Impossible de rÃĐcupÃĐrer le format par dÃĐfaut</translation> </message> <message> <source>Cannot create archive %1. %2</source> <translation>Impossible de crÃĐer l'archive %1. %2</translation> </message> <message> <source>CArc index %1 out of bounds [0, %2]</source> <translation>Index CArc %1 hors limites [0, %2]</translation> </message> <message> <source>Item index %1 out of bounds [0, %2]</source> <translation>Index de l'ÃĐlÃĐment %1 hors limites [0, %2]</translation> </message> <message> <source>Cannot create output file for writing: %1</source> <translation>Impossible de crÃĐer le fichier de sortie : %1</translation> </message> </context> <context> <name>Lib7z::ExtractItemJob</name> <message> <source>Cannot list archive: QIODevice not set or already destroyed.</source> <translation>Impossible de lister l'archive : QIODevice n'est pas renseignÃĐ ou à dÃĐjà ÃĐtÃĐ dÃĐtruit.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Erreur lors de l'extraction '%1' : %2</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Une exception de type inconnue a ÃĐtÃĐ attrapÃĐe (%1)</translation> </message> <message> <source>Failed</source> <translation>Échec</translation> </message> </context> <context> <name>Lib7z::ListArchiveJob</name> <message> <source>Cannot list archive: QIODevice already destroyed.</source> <translation>Impossible de lister l'archive : QIODevice n'est pas renseignÃĐ ou à dÃĐjà ÃĐtÃĐ dÃĐtruit.</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Une exception de type inconnue a ÃĐtÃĐ attrapÃĐe (%1)</translation> </message> <message> <source>Failed</source> <translation>Échec</translation> </message> </context> <context> <name>OpenArchiveInfo</name> <message> <source>Cannot load codecs</source> <translation>Impossible de charger les codecs</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Impossible de rÃĐcupÃĐrer le format par dÃĐfaut</translation> </message> <message> <source>Cannot open archive</source> <translation>Impossible d'ouvrir l'archive</translation> </message> <message> <source>No CArc found</source> <translation>Aucun CArc n'a ÃĐtÃĐ trouvÃĐ</translation> </message> </context> <context> <name>QIODeviceSequentialOutStream</name> <message> <source>No device set for output stream</source> <translation>Aucun dispositif n'est prÊt pour le flux de sortie</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>No marker found, stopped after %1.</source> <translation>Aucun marqueur n'a ÃĐtÃĐ trouvÃĐ, arrÊt aprÃĻs %1.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Impossible d'ouvrir le fichier %1 en lecture : %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Impossible d'ouvrir le fichier %1 en ÃĐcriture : %2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>La lecture a ÃĐchouÃĐe aprÃĻs %1 octets : %2</translation> </message> <message> <source>Copy failed. Error: %1</source> <translation>La copie a ÃĐchouÃĐe. Erreur : %1</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>L'ÃĐcriture à ÃĐchouÃĐ aprÃĻs %1 octets : %2</translation> </message> <message> <source>bytes</source> <translation>octets</translation> </message> <message> <source>KiB</source> <translation>KiB</translation> </message> <message> <source>MiB</source> <translation>MiB</translation> </message> <message> <source>GiB</source> <translation>GiB</translation> </message> <message> <source>TiB</source> <translation>TiB</translation> </message> <message> <source>PiB</source> <translation>PiB</translation> </message> <message> <source>EiB</source> <translation>EiB</translation> </message> <message> <source>ZiB</source> <translation>ZiB</translation> </message> <message> <source>YiB</source> <translation>YiB</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Impossible de supprimer le fichier %1 : %2</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Impossible de supprimer le dossier %1 : %2</translation> </message> <message> <source>Cannot create folder %1</source> <translation>Impossible de crÃĐer le dossier %1</translation> </message> <message> <source>Cannot copy file from %1 to %2: %3</source> <translation>Impossible de copier le fichier de %1 vers %2 : %3</translation> </message> <message> <source>Cannot move file from %1 to %2: %3</source> <translation>Impossible de dÃĐplacer le fichier de %1 vers %2 : %3</translation> </message> <message> <source>Cannot create folder %1: %2</source> <translation>Impossible de crÃĐer le dossier %1 : %2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>Impossible d'ouvrir le fichier temporaire : %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>Impossible d'ouvrir le fichier temporaire pour le modÃĻle %1 : %2</translation> </message> <message> <source>Cannot create temporary file</source> <translation>Impossible de crÃĐer le fichier temporaire</translation> </message> <message> <source>Cannot retrieve property %1 for item %2</source> <translation>Impossible de rÃĐcupÃĐrer la propriÃĐtÃĐ %1 pour l'ÃĐlÃĐment %2</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3</source> <translation>PropriÃĐtÃĐ %1 pour l'ÃĐlÃĐment %2 n'est pas de type VT_FILETIME mais %3</translation> </message> <message> <source>Cannot convert file time to local time</source> <translation>Impossible de convertir l'heure du fichier vers l'heure locale</translation> </message> <message> <source>Cannot convert local file time to system time</source> <translation>Impossible de convertir l'heure du fichier vers l'heure du systÃĻme</translation> </message> <message> <source>Corrupt installation</source> <translation>Installation corrompue</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>Votre installation semble Être corrompue. Veuillez retenter une nouvelle installation.</translation> </message> <message> <source>The specified module could not be found.</source> <translation>Le module spÃĐcifiÃĐ ne peut Être trouvÃĐ.</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Components cannot have children in updater mode.</source> <translation>Les composants ne peuvent avoir de composants fils en mode mise-à-jour.</translation> </message> <message> <source>Cannot open the requested translation file '%1'.</source> <translation>Impossible d'ouvrir le fichier de traduction '%1'.</translation> </message> <message> <source>Cannot open the requested UI file '%1'. Error: %2</source> <translation>Impossible d'ouvir le fichier d'IHM '%1'. Erreur : %2</translation> </message> <message> <source>Cannot load the requested UI file '%1'. Error: %2</source> <translation>Impossible de charger le fichier d'IHM '%1'. Erreur : %2</translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>Impossible d'analyser 'isDefault' dans %1</translation> </message> <message> <source>Cannot open the requested license file '%1'. Error: %2</source> <translation>Impossible d'ouvrir le fichier de licence '%1'. Erreur %2</translation> </message> <message> <source>Error</source> <translation>Erreur</translation> </message> <message> <source>Error: Operation %1 does not exist</source> <translation>Erreur : l'opÃĐration %1 n'existe pas</translation> </message> <message> <source>Update Info: </source> <translation>Informations de mises à jour : </translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component Name</source> <translation>Nom du composant</translation> </message> <message> <source>Action</source> <translation>Action</translation> </message> <message> <source>Installed Version</source> <translation>Version installÃĐe</translation> </message> <message> <source>New Version</source> <translation>Nouvelle version</translation> </message> <message> <source>Release Date</source> <translation>Date de sortie</translation> </message> <message> <source>Size</source> <translation>Taille</translation> </message> <message> <source>Component is marked for installation.</source> <translation>Le composant est marquÃĐ pour installation.</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>Le composant est marquÃĐ pour dÃĐsinstallation.</translation> </message> <message> <source>Component is installed.</source> <translation>Le composant est installÃĐ.</translation> </message> <message> <source>Component is not installed.</source> <translation>Le composant n'est pas installÃĐ.</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translatorcomment>SÃĐlection des composants par dÃĐfaut</translatorcomment> <translation>Alt+A</translation> </message> <message> <source>Def&ault</source> <translation>DÃĐf&aut</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translatorcomment>Revenir vers la liste des composants dÃĐjà installÃĐs</translatorcomment> <translation>Alt+R</translation> </message> <message> <source>&Reset</source> <translation>&Effacer</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translatorcomment>SÃĐlectionner tous les composants</translatorcomment> <translation>Alt+S</translation> </message> <message> <source>&Select All</source> <translation>&SÃĐlectionner tout</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translatorcomment>DÃĐsÃĐlectionner tous les composants</translatorcomment> <translation>Alt+D</translation> </message> <message> <source>&Deselect All</source> <translation>&DÃĐsÃĐlectionner tout</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>Ce composant va occuper environ %1 sur le disque dur.</translation> </message> <message> <source>Select Components</source> <translation>SÃĐlection des composants</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>Veuillez sÃĐlectionner les composants que souhaitez mettre à jour.</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>Veuillez sÃĐlectionner les composants que vous souhaitez installer.</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>Veuillez sÃĐlectionner les composants que vous souhaitez dÃĐsinstaller.</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them.</source> <translation>SÃĐlection des composants à installer. La dÃĐsÃĐlection d'un composant installÃĐ entraÃŪne sa dÃĐsinstallation.</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>at least 2</source> <translation>au moins 2</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Objet installeur requis dans %1 l'opÃĐration est vide.</translation> </message> <message> <source>Can not save the output of %1 to an empty installer key value.</source> <translation>Impossible de sauvegarder la sortie de %1 vers un installeur vide.</translation> </message> <message> <source>File '%1' does not exist or is not an executable binary.</source> <translation>Le fichier '%1' n'existe pas ou n'est pas un fichier binaire exÃĐcutable.</translation> </message> <message> <source>Running '%1' resulted in a crash.</source> <translation>Le lancement de '%1' s'est soldÃĐ par un crash.</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source>2 or 3</source> <translation>2 ou 3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source> (<source> <target> [forceOverwrite])</source> <translation> (<source> <cible> [forceOverwrite])</translation> </message> <message> <source>Invalid argument in %0: Third argument needs to be forceOverwrite, if specified</source> <translation>Argument invalide dans %0 : le troisiÃĻme argument devrait Être à 'forceOverwrite', si spÃĐcifiÃĐ</translation> </message> <message> <source>Invalid arguments in %0: Directories are invalid: %1 %2</source> <translation>Arguments invalides dans %0 : les dossier sont invalides : %1 %2</translation> </message> <message> <source>Cannot create %0</source> <translation>Impossible de crÃĐer %0</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>L'ÃĐcrasement de %1 à ÃĐchouÃĐ</translation> </message> <message> <source>Cannot copy %0 to %1, error was: %3</source> <translation>Impossible de copier %0 vers %1, l'erreur rencontrÃĐe est : %3</translation> </message> <message> <source>Cannot remove %0</source> <translation>Impossible de supprimer %0</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation>Nombre incorrect d'ÃĐlÃĐments de la tÃĒche.</translation> </message> <message> <source>Cannot open source '%1' for read. Error: %2.</source> <translation>Impossible d'ouvrir le fichier source '%1' en lecture. Erreur : %2.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <translation>Impossible d'ouvrir le fichier source '%1' en ÃĐcriture. Erreur : %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <translation>Échec de l'ÃĐcriture de la cible '%1'. Erreur : %2.</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Impossible de faire une sauvegarde du fichier %1 : %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactement 2</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>L'ÃĐcrasement de %1 à ÃĐchouÃĐ</translation> </message> <message> <source>Cannot write Desktop Entry at %1</source> <translation>Impossible d'ÃĐcrire un ÃĐlÃĐment 'Desktop Entry' vers %1</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactement 2</translation> </message> <message> <source>Cannot create link from %1 to %2.</source> <translation>Impossible de crÃĐer le lien symbolique de %1 vers %2.</translation> </message> <message> <source>Cannot remove link from %1 to %2.</source> <translation>Impossible de supprimer le lien de %1 vers %2.</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set file permissions %1!</source> <translation>Impossible d'attribuer les autorisations du fichier %1 !</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Impossible de supprimer le fichier %1 : %2</translation> </message> <message> <source>Cannot move file %1 to %2. Error: %3</source> <translation>Impossible de dÃĐplacer le fichier %1 vers %2. Erreur : %3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactement 2</translation> </message> <message> <source>Installer needs to be an offline version: %1.</source> <translation>L'installeur devrait Être une version hors ligne : %1.</translation> </message> <message> <source>Cannot open file: %1</source> <translation>Impossible d'ouvrir le fichier %1</translation> </message> <message> <source>Cannot read: %1. Error: %2</source> <translation>Impossible de lire : %1. Erreur : %2</translation> </message> <message> <source>Cannot open file: %1. Error: %2</source> <translation>Impossible d'ouvrir le fichier %1. Erreur : %2</translation> </message> <message> <source>Cannot create target dir: %1.</source> <translation>Impossible de crÃĐer le dossier cible : %1.</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>Une exception de type inconnue a ÃĐtÃĐ attrapÃĐe : %1.</translation> </message> <message> <source>Removing file: %0</source> <translation>Suppression du fichier : %0</translation> </message> <message> <source>Cannot remove %0.</source> <translation>Impossible de supprimer %0.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Impossible de supprimer le dossier %1 : %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>2 or 3</source> <translation>2 ou 3</translation> </message> <message> <source> (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</source> <translation>(optionnel : 'workingDirectory=...', 'iconPath=...', 'iconId=...')</translation> </message> <message> <source>Cannot create folder %1: %2.</source> <translation>Impossible de crÃĐer le dossier %1 : %2.</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>L'ÃĐcrasement de %1 à ÃĐchouÃĐ : %2</translation> </message> <message> <source>Cannot create link %1: %2</source> <translation>Impossible de crÃĐer le raccourci %1 : %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>AnnulÃĐ</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>Le tÃĐlÃĐchargement de l'empreinte de hashage à ÃĐchouÃĐ.</translation> </message> <message> <source>Download Error</source> <translation>Erreur de tÃĐlÃĐchargement</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>La vÃĐrification de l'empreinte pendant le tÃĐlÃĐchargement à ÃĐchouÃĐ. C'est une erreur temporaire, veuillez rÃĐessayer.</translation> </message> <message> <source>Cannot verify Hash</source> <translation>Impossible de vÃĐrifier l'empreinte</translation> </message> <message> <source>Cannot download archive: %1 : %2</source> <translation>Impossible de tÃĐlÃĐcharger l'archive : %1 : %2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>Impossible de charger les archives : %1 Erreur pendant le chargement %2</translation> </message> <message> <source>Downloading archive '%1' for component: %2</source> <translation>TÃĐlÃĐchargement de l'archive '%1' pour le composant : %2</translation> </message> <message> <source>Scheme not supported: %1 (%2)</source> <translation>SchÃĐma non supportÃĐ : %1 (%2)</translation> </message> <message> <source>Cannot find component for: %1.</source> <translation>Impossible de trouver le composant pour : %1.</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Target '%1' not open for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>La cible '%1' n'est pas ouverte en ÃĐcriture. Erreur : %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Échec de l'ÃĐcriture de la cible '%1'. Erreur : %2.</translation> </message> <message> <source>Redirect loop detected '%1'.</source> <translation>Cycle de redirection dÃĐtectÃĐ '%1'.</translation> </message> <message> <source>Checksum mismatch detected '%1'.</source> <translation>Sommes de contrÃīle diffÃĐrentes dÃĐtectÃĐ '%1'.</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Erreur rÃĐseau pendant le tÃĐlÃĐchargement de '%1' : %2.</translation> </message> <message> <source>Unknown network error while downloading: %1.</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>Erreur rÃĐseau indÃĐterminÃĐe pendant le tÃĐlÃĐchargement : %1.</translation> </message> <message> <source>Pause and resume not supported by network transfers.</source> <translation>La mise en pause et la reprise ne sont pas supportÃĐs lors des transferts rÃĐseaux.</translation> </message> <message> <source>Invalid source '%1'. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Source invalide '%1'. Erreur : %2.</translation> </message> <message> <source>Target file '%1' already exists but is not a file.</source> <translation>Le fichier cible '%1' existe dÃĐjà mais il n'est pas de type fichier.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Impossible d'ouvrir le fichier cible '%1' en ÃĐcriture. Erreur : %2.</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>at least 1</source> <translation>au moins 1</translation> </message> <message> <source>Execution failed: Cannot start detached: "%1"</source> <translation>L'exÃĐcution à ÃĐchouÃĐe : impossible de dÃĐmarrer en mode arriÃĻre plan : "%1"</translation> </message> <message> <source>Execution failed: Cannot start: "%1"(%2)</source> <translation>L'exÃĐcution à ÃĐchouÃĐe : impossible de dÃĐmarrer "%1" (%2)</translation> </message> <message> <source>Execution failed(Crash): "%1"</source> <translation>L'exÃĐcution à ÃĐchouÃĐe (plantage) : "%1"</translation> </message> <message> <source>Execution failed(Unexpected exit code: %1): "%2"</source> <translation>L'exÃĐcution à ÃĐchouÃĐe (code de retour inattendu : %1) : "%2"</translation> </message> </context> <context> <name>QInstaller::EnvironmentVariableOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>2 to 4</source> <translation>2 sur 4</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactement 2</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open %1 for reading: %2.</source> <translation>Impossible d'ouvrir %1 en lecture : %2.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Erreur lors de l'extraction '%1' : %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Une exception de type inconnue a ÃĐtÃĐ attrapÃĐe pendant l'extraction de %1.</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Number of arguments does not match: one is required</source> <translation>Le nombre d'arguments ne correspond pas : un seul est requis</translation> </message> <message> <source>Cannot get package manager core.</source> <translation>Impossible de rÃĐcupÃĐrer le noyau du gestionnaire de paquets.</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>Le processus suivant devrait Être stoppÃĐ avant de continuer : %1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>Les processus suivant devraient Être stoppÃĐs avant de continuer : %1</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%1 sur %2</translation> </message> <message> <source>%1 received.</source> <translation>%1 reçu.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/s)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n jour, </numerusform> <numerusform>%n jours, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n heure, </numerusform> <numerusform>%n heures, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n minute</numerusform> <numerusform>%n minutes</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n seconde</numerusform> <numerusform>%n secondes</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 restant.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - impossible d'estimer le temps restant.</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>Finalisation de l'Assistant de %1</translation> </message> <message> <source>Click Done to exit the %1 Wizard.</source> <translation>Cliquer sur Terminer pour quitter %1 Assistant.</translation> </message> <message> <source>Click Finish to exit the %1 Wizard.</source> <translation>Cliquer sur Terminer pour quitter %1 Assistant.</translation> </message> <message> <source>Restart</source> <translation>RedÃĐmarrer</translation> </message> <message> <source>Run %1 now.</source> <translation>Lancer %1 maintenant.</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>%1 Assistant à ÃĐchouÃĐ.</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable</source> <translation>Les prÃĐfÃĐrences ne sont pas accessibles en ÃĐcriture</translation> </message> <message> <source>Failed to write settings</source> <translation>Impossible de sauvegarder les prÃĐfÃĐrences</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>3, 4 or 5</source> <translation>3, 4 ou 5</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>1 or 2</source> <translation>1 ou 2</translation> </message> <message> <source> (Sourcepath, [Vendorprefix])</source> <translation> (Sourcepath, [Vendorprefix])</translation> </message> <message> <source>Invalid Argument: source folder must not be empty.</source> <translation>Argument invalide : le dossier source ne peut Être vide.</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation>Impossible de faire une sauvegarde du fichier %1 : %2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>L'ÃĐcrasement de %1 à ÃĐchouÃĐ : %2</translation> </message> <message> <source>Failed to copy file %1: %2</source> <translation>La copie du fichier %1 à ÃĐchouÃĐ : %2</translation> </message> <message> <source>Cannot create folder at %1: %2</source> <translation>Impossible de crÃĐer le dossier %1 : %2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>Installation - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>Bienvenue dans l'Assitant d'Installation de : %1.</translation> </message> <message> <source>Add or remove components</source> <translation>Ajouter ou supprimer des modules</translation> </message> <message> <source>Update components</source> <translation>Mettre à jour les modules</translation> </message> <message> <source>Remove all components</source> <translation>Supprimer tous les modules</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>RÃĐcupÃĐration des informations nÃĐcessaires à partir d'une source distante...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>Au moins un dÃĐpÃīt valide et actif est requis pour pouvoir continuer.</translation> </message> <message> <source>No updates available.</source> <translation>Aucune mise à jour n'est disponible.</translation> </message> <message> <source> Only local package management available.</source> <translation>La gestion des modules n'est disponible qu'en local.</translation> </message> <message> <source>Quit</source> <translation>Quitter</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>Contrat de Licence</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translatorcomment>Accepter la licence</translatorcomment> <translation>Alt+A</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>Veuillez lire le contrat de licence suivant. Vous devez en accepter les termes avant de poursuivre l'installation.</translation> </message> <message> <source>I accept the license.</source> <translation>J'accepte la licence.</translation> </message> <message> <source>I do not accept the license.</source> <translation>Je n'accepte pas la licence.</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>Veuillez lire les contrats de licence suivants. Vous devez en accepter les termes avant de poursuivre l'installation.</translation> </message> <message> <source>I accept the licenses.</source> <translation>J'accepte les licences.</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>Je n'accepte pas les licences.</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translatorcomment>Refuser les contrats de licence</translatorcomment> <translation>Alt+D</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>Aucun fichier de licence n'a trouvÃĐ Ã  la copie.</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Objet installeur requis dans %1 l'opÃĐration est vide.</translation> </message> <message> <source>Can not write license file: %1.</source> <translation>Impossible d'ÃĐcrire le fichier de licence : %1.</translation> </message> <message> <source>No license files found to delete.</source> <translation>Aucun fichier de licence n'a ÃĐtÃĐ trouvÃĐ Ã  la suppression.</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 3</source> <translation>exactement 3</translation> </message> <message> <source>Failed to open '%1' for reading.</source> <translation>Impossible d'ouvrir le fichier '%1' en lecture.</translation> </message> <message> <source>Failed to open '%1' for writing.</source> <translation>Impossible d'ouvrir le fichier '%1' en ÃĐcriture.</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>Le moteur du gestionnaire de paquets est absent.</translation> </message> <message> <source>Preparing meta information download...</source> <translation>PrÃĐparation du tÃĐlÃĐchargement des mÃĐtadonnÃĐes...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>Le tÃĐlÃĐchargement des mÃĐtadonnÃĐes a ÃĐtÃĐ annulÃĐ.</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>Les identifiants du proxy sont absents.</translation> </message> <message> <source>Authentication failed.</source> <translation>L'authentification a ÃĐchouÃĐ.</translation> </message> <message> <source>Unknown exception during download.</source> <translation>Une exception non spÃĐcifiÃĐe s'est produite pendant le tÃĐlÃĐchargement.</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>RÃĐcupÃĐration des mÃĐtadonnÃĐes à partir du dÃĐpÃīt distant...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>Échec lors de la rÃĐcupÃĐration de la liste des dÃĐpÃīts.</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>Une exception non spÃĐcifiÃĐe a ÃĐtÃĐ attrapÃĐe pendant l'extraction.</translation> </message> <message> <source>Extracting meta information...</source> <translation>Extraction des mÃĐtadonnÃĐes...</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Erreur lors de l'extraction de '%1' : %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Une exception non spÃĐcifiÃĐe a ÃĐtÃĐ attrapÃĐe pendant l'extraction de %1.</translation> </message> <message> <source>Cannot open %1 for reading. Error: %2</source> <translation>Impossible d'ouvrir %1 en lecture : %2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source> Downloading packages...</source> <translation> TÃĐlÃĐchargement des paquets...</translation> </message> <message> <source>Installation canceled by user</source> <translation>L'installation a ÃĐtÃĐ annulÃĐe par l'utilisateur</translation> </message> <message> <source>All downloads finished.</source> <translation>Tous les tÃĐlÃĐchargements sont terminÃĐs.</translation> </message> <message> <source>Error</source> <translation>Erreur</translation> </message> <message> <source>Cancelling the Installer</source> <translation>Annulation de l'Installeur</translation> </message> <message> <source>Error writing Maintenance Tool</source> <translation>Erreur lors de l'ÃĐcriture de l'Outil de Maintenance</translation> </message> <message> <source>Authentication Error</source> <translation>Erreur d'authentification</translation> </message> <message> <source>Some components could not be removed completely because admin rights could not be acquired: %1.</source> <translation>Certains composants n'ont pu Être supprimÃĐs totalement car les droits d'administrateur n'ont pu Être obtenus : %1.</translation> </message> <message> <source>Unknown error.</source> <translation>Erreur non dÃĐterminÃĐe.</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>Certains composants n'ont pu Être supprimÃĐs car une erreur indÃĐterminÃĐe s'est produite.</translation> </message> <message> <source>Application not running in Package Manager mode!</source> <translation>L'application ne fonctionne pas en mode 'Gestion des Paquets' !</translation> </message> <message> <source>No installed packages found.</source> <translation>Aucun paquet installÃĐ n'a ÃĐtÃĐ localisÃĐ.</translation> </message> <message> <source>Application running in Uninstaller mode!</source> <translation>L'application fonctionne en mode DÃĐsinstallation !</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>Une mise à jour importante est disponible, veuillez l'exÃĐcuter en premier.</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>Erreur lors de l'ÃĐlÃĐvation des privilÃĻges.</translation> </message> <message> <source>invalid</source> <translation>invalide</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Error</source> <translation>Erreur</translation> </message> <message> <source>Access error</source> <translation>Erreur d'accÃĻs</translation> </message> <message> <source>Format error</source> <translation>Erreur de formatage</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>Impossible d'ÃĐcrire la configuration de l'installeur vers %1 : %2</translation> </message> <message> <source>Stop Processes</source> <translation>ArrÊter les processus</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>Les processus suivants devraient Être arrÊter pour continuer : %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>L'installation a ÃĐtÃĐ annulÃĐe par l'utilisateur</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>La variable 'TargetDir' n'est pas renseignÃĐe.</translation> </message> <message> <source>Preparing the installation...</source> <translation>PrÃĐparation de l'installation...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>Il n'est pas possible de procÃĐder à l'installation à partir d'un emplacement rÃĐseau</translation> </message> <message> <source>Creating local repository</source> <translation>CrÃĐation du dÃĐpÃīt en local</translation> </message> <message> <source> Installation finished!</source> <translation> Installation terminÃĐe !</translation> </message> <message> <source> Installation aborted!</source> <translation> Installation annulÃĐe !</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>Il n'est pas possible d'effectuer cette opÃĐration à partir d'un emplacement rÃĐseau</translation> </message> <message> <source>Removing deselected components...</source> <translation>Suppression des ÃĐlÃĐments dÃĐsÃĐlectionnÃĐs...</translation> </message> <message> <source> Update finished!</source> <translation> Mise à jour terminÃĐe !</translation> </message> <message> <source> Update aborted!</source> <translation> Mise à jour annulÃĐe !</translation> </message> <message> <source>Unresolved dependencies</source> <translation>Impossible de rÃĐsoudre les dÃĐpendances</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>Écriture de l'Outil de Maintenance.</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>Impossible de rechercher dans le fichier %1 : %2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation>L'Outil de Maintenance n'est pas un Bundle</translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>Impossible d'ÃĐcrire les donnÃĐes de l'Outil de Maintenance vers %1 : %2</translation> </message> <message> <source>Cannot remove data file '%1': %2</source> <translation>Impossible de supprimer le fichier '%1' : %2</translation> </message> <message> <source>Cannot write maintenance tool to %1: %2</source> <translation>Impossible d'ÃĐcrire l'Outil de Maintenance vers %1 : %2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>Impossible d'ÃĐcrire les donnÃĐes de l'Outil de Maintenance vers %1 : %2</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>CrÃĐation de l'Outil de Maintenance</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>La dÃĐsinstallation s'est terminÃĐe avec succÃĻs.</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>La dÃĐsinstallation a ÃĐtÃĐ annulÃĐe.</translation> </message> <message> <source> Installing component %1</source> <translation> Installation du composant %1</translation> </message> <message> <source>Installer Error</source> <translation>Erreur dans l'Installeur</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>Erreur pendant le processus d'installation (%1) : %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>Impossible de prÃĐparer la dÃĐsinstallation</translation> </message> <message> <source>Cannot start uninstall</source> <translation>Impossible de dÃĐmarrer la dÃĐsinstallation</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>Erreur pendant le processus de dÃĐsinstallation : %1</translation> </message> <message> <source>Unknown error</source> <translation>Erreur non dÃĐterminÃĐe</translation> </message> <message> <source>Cannot retrieve remote tree: %1.</source> <translation>Impossible de rÃĐcupÃĐrer l'arborescence distante : %1.</translation> </message> <message> <source>Failure to read packages from: %1.</source> <translation>Impossible de lire les paquets à partir de : %1.</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>Impossible de rÃĐcupÃĐrer les mÃĐtadonnÃĐes : %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>Impossible d'ajouter des information de source de mise à jour temporaire.</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>Impossible de trouver des informations de source de mise à jour.</translation> </message> <message> <source>Dependency cycle between components detected: '%1' and '%2'.</source> <translation>DÃĐpendance cyclique dÃĐtectÃĐe pour les modules suivants : '%1' et '%2'.</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>%1 Installateur</translation> </message> <message> <source>Maintain %1</source> <translation>Maintenir %1</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>Êtes-vous sÃŧr de vouloir annuler cette installation ?</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>Êtes-vous sÃŧr de vouloir annuler cette dÃĐsinstallation ?</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>Êtes-vous sÃŧr de vouloir quitter cet assistant d'installation ?</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>Êtes-vous sÃŧr de vouloir quitter cet assistant de dÃĐsinstallation ?</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>Êtes-vous sÃŧr de vouloir quitter cet outil de maintenance ?</translation> </message> <message> <source>Question</source> <translation>Question</translation> </message> <message> <source>Settings</source> <translation>ParamÃĻtres</translation> </message> <message> <source>Error</source> <translation>Erreur</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>Il n'est possible de procÃĐder à l'installation à partir d'un emplacement rÃĐseau. Veuillez copier cet installateur sur un disque local</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>&Voir le dÃĐtail</translation> </message> <message> <source>&Hide Details</source> <translation>&Masquer le dÃĐtail</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>&DÃĐsinstaller</translation> </message> <message> <source>Uninstalling %1</source> <translation>DÃĐsinstallation de %1</translation> </message> <message> <source>&Update</source> <translation>&Mise à jour</translation> </message> <message> <source>Updating components of %1</source> <translation>Mise à jour du composant %1</translation> </message> <message> <source>&Install</source> <translation>&Installation</translation> </message> <message> <source>Installing %1</source> <translation>Installation de %1</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation>Dialog</translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>Le proxy %1 requiert une authentification par identifiant et mot de passe.</translation> </message> <message> <source>Username:</source> <translation>Identifiant :</translation> </message> <message> <source>Username</source> <translation>Identifiant</translation> </message> <message> <source>Password:</source> <translation>Mot de passe :</translation> </message> <message> <source>Password</source> <translation>Mot de passe</translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>&DÃĐsinstaller</translation> </message> <message> <source>Ready to Uninstall</source> <translation>PrÊt à dÃĐsinstaller</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>L'installateur est maintenant prÊt à supprimer %1 de votre ordinateur. <br><font color="red">Le rÃĐpertoire du programme %2 va Être complÃĻtement supprimÃĐ</font>, en incluant tout le contenu de ce dossier !</translation> </message> <message> <source>U&pdate</source> <translation>&Mise à jour</translation> </message> <message> <source>Ready to Update Packages</source> <translation>PrÊt à mettre à jour les paquets</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>L'installateur est prÊt à mettre à jour votre installation.</translation> </message> <message> <source>&Install</source> <translation>&Installation</translation> </message> <message> <source>Ready to Install</source> <translation>PrÊt à installer</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>L'installateur est maintenant prÊt à effectuer la copie de %1 sur votre ordinateur.</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation! Available space: %1, at least required %2.</source> <translation>Il n'y a pas assez d'espace disque pour stocker les fichiers temporaires ainsi que le programme ! Espace disponible : %1, nÃĐcessite au moins %2.</translation> </message> <message> <source>Not enough disk space to store all selected components! Available space: %1, at least required: %2.</source> <translation>Il n'y a pas assez d'espace disque pour stocker tous les composants sÃĐlectionnÃĐs ! Espace disponible : %1, nÃĐcessite au moins %2.</translation> </message> <message> <source>Not enough disk space to store temporary files! Available space: %1, at least required: %2.</source> <translation>Il n'y a pas assez d'espace disque pour stocker les fichiers temporaires ! Espace disponible : %1, nÃĐcessite au moins %2.</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>Le volume que vous avez sÃĐlectionnÃĐ pour l'installation semble avoir assez d'espace disponible, mais disposera de moins 1% d'espace libre ensuite. %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>Le volume que vous avez sÃĐlectionnÃĐ pour l'installation semble avoir assez d'espace disponible, mais disposera de moins de 100 Mo d'espace libre ensuite. %1</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>L'installation va occuper %1 d'espace disque.</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>Impossible de rÃĐsoudre les dÃĐpendances.</translation> </message> <message> <source>Components about to be removed.</source> <translation>Composants sur le point d'Être supprimÃĐs.</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>2 to 5</source> <translation>2 sur 5</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>L'association d'une ou plusieurs extensions n'est supportÃĐ que sous Windows.</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>Engistrement des exensions par dÃĐfaut : arguments invalides</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Impossible de lire les donnÃĐes aprÃĻs envoi de la commande : %1. Octets attendus : %2, reçus : %3. Erreur : %4</translation> </message> </context> <context> <name>QInstaller::RemoteServerConnection</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Impossible de lire les donnÃĐes aprÃĻs envoi de la commande : %1. Octets attendus : %2, reçus : %3. Erreur : %4</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 3</source> <translation>exactement 3</translation> </message> <message> <source>Failed to open %1 for reading</source> <translation>Impossible d'ouvrir le fichier %1 en lecture</translation> </message> <message> <source>Failed to open %1 for writing</source> <translation>Impossible d'ouvrir le fichier %1 en ÃĐcriture</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Cannot open Resource '%1' read-only.</source> <translation>Le fichier de ressource '%1' ne peut Être ouvert en lecture seule.</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>La lecture a ÃĐchouÃĐe aprÃĻs %1 octets : %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>L'ÃĐcriture à ÃĐchouÃĐ aprÃĻs %1 octets : %2</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>Finalisation de l'Assistant d'installation de %1</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open the requested script file at %1: %2.</source> <translation>Impossible d'ouvrir le fichier de script requis à %1 : %2.</translation> </message> <message> <source>Exception while loading the component script '%1'. (%2)</source> <translation>Exception levÃĐe pendant le chargement du composant de script : '%1' (%2)</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in '%1' operation is empty.</source> <translation>Objet installeur requis dans '%1' l'opÃĐration est vide.</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>Rechargement automatique : valide uniquement dans les modes Mise à jour ou Gestionnaire de paquets.</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>Rechargement automatique : arguments invalides</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>Le serveur requiert une authentification</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>Vous devez saisir un identifiant et un mot de passe pour accÃĐder à ce site.</translation> </message> <message> <source>Username:</source> <translation>Identifiant :</translation> </message> <message> <source>Password:</source> <translation>Mot de passe :</translation> </message> <message> <source>%1 at %2</source> <translation>%1 à %2</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) '%1' calling '%2' with arguments '%3'.</source> <translation>Argument(s) manquant(s) : '%1' appelle '%2' avec les arguments '%3'.</translation> </message> <message> <source>Current method argument calling '%1' with arguments '%2' is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>MÃĐthode actuelle appelant '%1' avec les arguments '%2' n'est pas supportÃĐ. Veuillez utiliser 'set', 'remove', 'add_array_value' ou 'remove_array_value'.</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Arguments invalides dans %0 : %1 arguments fournis, %2 attendus %3.</translation> </message> <message> <source>exactly 2</source> <translation>exactement 2</translation> </message> <message> <source>None of the arguments can be empty: source '%1', target '%2'.</source> <translation>Aucun des arguments fournis ne peut Être vide : source '%1', destination '%2'.</translation> </message> <message> <source>Cannot move source '%1' to target '%2', because target exists and is not removable.</source> <translation>Impossible de dÃĐplacer la source '%1' vers la cible '%2', car la destination existe et ne peut Être supprimÃĐe.</translation> </message> <message> <source>Cannot move source '%1' to target '%2': %3</source> <translation>Impossible de dÃĐplacer la source '%1' vers la cible '%2' : %3</translation> </message> <message> <source>Move '%1' to '%2'.</source> <translation>DÃĐplacement de '%1' vers '%2'.</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>Raccourcis du Menu DÃĐmarrer</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.</source> <translation>SÃĐlectionnez l'endroit dans le Menu DÃĐmarrer oÃđ vous souhaitez placer un raccourci. Vous pouvez ÃĐgalement saisir un nom ou crÃĐer un nouveau dossier.</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>Dossier d'installation</translation> </message> <message> <source>Please specify the folder where %1 will be installed.</source> <translation>Veuillez indiquer le dossier oÃđ %1 sera installÃĐ.</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translatorcomment>Naviguer dans l'explorateur de fichier pour sÃĐlectionner un fichier</translatorcomment> <translation>Alt+R</translation> </message> <message> <source>B&rowse...</source> <translation>&Parcourir...</translation> </message> <message> <source>The folder you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>Le dossier que vous avez sÃĐlectionnÃĐ existe dÃĐjà et contient une installation prÃĐcÃĐdente.Veuillez choisir une cible diffÃĐrente pour l'installation.</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation>Vous avez sÃĐlectionnÃĐ un dossier existant et non-vide pour cette installation. Veuillez prendre note qu'il sera complÃĻtement supprimÃĐ lors de la dÃĐsinstallation de cette application. Il est dÃĐconseillÃĐ d'installer dans ce dossier dans le cas oÃđ l'installation ÃĐchouerait. Êtes-vous sÃŧr de vouloir continuer ?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>Vous avez sÃĐlectionnÃĐ un fichier ou lien symbolique existant, veuillez choisir une cible diffÃĐrente pour l'installation.</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid folder.</source> <translation>Le chemin d'installation ne peut Être vide, veuillez indiquer un dossier valide.</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>Le chemin d'installation ne peut Être relatif, veuillez indiquer un chemin absolu.</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>Le chemin ou le dossier d'installation contient des caractÃĻres non ASCII. Ceci n'est pas supportÃĐ Ã  l'heure actuelle ! Veuillez choisir un chemin diffÃĐrent ou un autre dossier d'installation.</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>Étant donnÃĐ que le dossier d'installation est complement supprimÃĐ, il est interdit d'installer dans %1.</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>Le chemin que vous avez entrÃĐ est trop long, veuillez vous assurer d'entrer un chemin valide.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>Le chemin que vous avez entrÃĐ est incorrect, veuillez vous assurer de choisir une cible valide.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>Le chemin que vous avez entrÃĐ est incorrect, veuillez vous assurer de choisir un lecteur valide.</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid folder.</source> <translation>Le chemin d'installation ne peut pas contenir '.', veuillez entrer un dossier valide.</translation> </message> <message> <source>The installation path must not contain '%1', please specify a valid folder.</source> <translation>Le chemin d'installation ne peut pas contenir %1, veuillez entrer un dossier valide.</translation> </message> <message> <source>Error</source> <translation>Erreur</translation> </message> <message> <source>Warning</source> <translation>Attention</translation> </message> <message> <source>Select Installation Folder</source> <translation>SÃĐlectionnez le dossier d'installation</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Empty repository URL.</source> <translation>L'URL du dÃĐpÃīt est vide.</translation> </message> <message> <source>URL scheme not supported: %1 (%2).</source> <translation>Format d'URL non supportÃĐ : %1 (%2).</translation> </message> <message> <source>Got a timeout while testing: '%1'</source> <translation>DÃĐlai d'attente dÃĐpassÃĐ pendant le test de : '%1'</translation> </message> <message> <source>Cannot parse Updates.xml! Error: %1.</source> <translation>Impossible d'analyser 'Updates.xml'. Erreur : %1.</translation> </message> <message> <source>Updates.xml could not be opened for reading!</source> <translation>Impossible d'ouvrir 'Updates.xml' en lecture !</translation> </message> <message> <source>Updates.xml could not be found on server!</source> <translation>Impossible d'ouvrir 'Updates.xml' sur le serveur !</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>Authentification requise</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>Entrez votre mot de passe pour authentifier 'sudo' :</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>Erreur lors de l'acquisition des droits admin</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>Impossible d'obtenir les autorisations nÃĐcessaires.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as root and then clicking OK.</source> <translation>Impossible d'obtenir les autorisations nÃĐcessaires pour continuer cette installation. Vous avez la possibilitÃĐ d'annuler cette installation ou de trouver une solution de repli en lançant %1 en tant que root et en cliquant sur OK.</translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>Impossible d'ouvrir la ressource %1 : %2</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>Impossible d'ouvrir le fichier de prÃĐfÃĐrences %1 en lecture : %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>ParamÃĻtres</translation> </message> <message> <source>Network</source> <translation>RÃĐseau</translation> </message> <message> <source>No proxy</source> <translation>Pas de serveur mandataire</translation> </message> <message> <source>System proxy settings</source> <translation>ParamÃĻtres du serveur mandataire systÃĻme</translation> </message> <message> <source>Manual proxy configuration</source> <translation>Configuration manuelle du serveur mandataire</translation> </message> <message> <source>HTTP proxy:</source> <translation>Proxy HTTP :</translation> </message> <message> <source>Port:</source> <translation>Port :</translation> </message> <message> <source>FTP proxy:</source> <translation>Proxy FTP :</translation> </message> <message> <source>Repositories</source> <translation>DÃĐpÃīts</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>Si nÃĐcessaire, ajouter l'identifiant et le mot de passe pour l'authentification.</translation> </message> <message> <source>Use temporary repositories only</source> <translation>Utiliser des dÃĐpÃīts temporaires uniquement</translation> </message> <message> <source>Add</source> <translation>Ajouter</translation> </message> <message> <source>Remove</source> <translation>Supprimer</translation> </message> <message> <source>Test</source> <translation>Test</translation> </message> <message> <source>Show Passwords</source> <translation>Montrer les mots de passe</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>Cocher pour utiliser les dÃĐpÃīts pendant la rÃĐcupÃĐration.</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>Ajouter l'identifiant pour l'authentification sur le serveur.</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>Ajouter le mot de passe pour l'authentification sur le serveur.</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>Liste des URL des serveurs contenant des dÃĐpÃīts valides.</translation> </message> <message> <source>There was an error testing this repository.</source> <translation>Une erreur s'est produite pendant le test de ce dÃĐpÃīt.</translation> </message> <message> <source>Do you want to disable the tested repository?</source> <translation>Êtes-vous sÃŧr de vouloir dÃĐsactiver le dÃĐpÃīt testÃĐ ?</translation> </message> <message> <source>Hide Passwords</source> <translation>Masquer les mots de passe</translation> </message> <message> <source>Use</source> <translation>Utiliser</translation> </message> <message> <source>Username</source> <translation>Identifiant</translation> </message> <message> <source>Password</source> <translation>Mot de passe</translation> </message> <message> <source>Repository</source> <translation>DÃĐpÃīt</translation> </message> <message> <source>Default repositories</source> <translation>DÃĐpÃīts par dÃĐfaut</translation> </message> <message> <source>Temporary repositories</source> <translation>DÃĐpÃīts temporaires</translation> </message> <message> <source>User defined repositories</source> <translation>DÃĐpÃīts dÃĐfinis par l'utilisateur</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable</source> <translation>Le chemin du registre %1 n'est pas accessible en ÃĐcriture</translation> </message> <message> <source>Cannot write to registry path %1</source> <translation>Impossible d'ÃĐcrire dans le registre le chemin %1</translation> </message> <message> <source>Renaming %1 into %2 failed with %3.</source> <translation>Échec du renommage de %1 vers %2, raison : %3.</translation> </message> </context> </TS> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/ifw_it.ts����������������������������������������������������������������������0000664�0000000�0000000�00000315653�13253666515�0017031�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="it_IT"> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>%1 a %2</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>Il proxy richiede l'autenticazione.</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>Impossibile spostarsi alla posizione %1 per leggere i dati di funzionamento.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>Impossibile spostarsi alla posizione %1 per leggere il blocco con le risorse.</translation> </message> <message> <source>Cannot open meta resource. Error: %1</source> <translation>Impossibile aprire i meta pacchetti.Errore: %1</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>Impossibile eseguire il seek a %1 per leggere il conteggio dei meta dati embedded.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation></translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>Non corrispondenza dei meta dati. Letti %1, aspettati: %2.</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>Richiesta autentificazione HTTP</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>E' necessario fornire un nome utente e una password per accedere a questo sito.</translation> </message> <message> <source>Username:</source> <translation>Nome Utente:</translation> </message> <message> <source>Password:</source> <translation></translation> </message> <message> <source>%1 at %2</source> <translation>%1 a %2</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path exists but is not a folder: %1</source> <translation>Il percorso esiste ma non ÃĻ una cartella: %1</translation> </message> <message> <source>Cannot create folder: %1</source> <translation>Impossibile creare la cartella: %1</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Impossibile recuperare il path dell'elemento dell'archivio %1</translation> </message> <message> <source>Cannot remove already existing symlink. %1</source> <translation>Impossibile rimuovere il collegamento già esistente. %1</translation> </message> <message> <source>Cannot open file: %1 (%2)</source> <translation>Impossibile aprire il file: %1 (%2)</translation> </message> <message> <source>Cannot create symlink at '%1'. Another one is already existing.</source> <translation>Impossibile creare il collegamento a '%1'. Un altro collegamento ÃĻ già esistente.</translation> </message> <message> <source>Cannot read symlink target from file '%1'.</source> <translation>Impossibile leggere dal file %1 puntato dal collegamento.</translation> </message> <message> <source>Cannot create symlink at %1. %2</source> <translation>Impossibile creare il collegamento a %1. %2</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>Componenti aggiunti come dipendenze automatiche:</translation> </message> <message> <source>Components added as dependency for '%1':</source> <translation>Componenti aggiunti come dipendenza per '%1':</translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>Componenti che hanno le dipendenze risolte:</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>Componenti selezionati senza dipendenze:</translation> </message> <message> <source>Recursion detected, component '%1' already added with reason: '%2'</source> <translation>Rilevata ricorsione, componente '%1' già aggiunto per questo motivo: '%2'</translation> </message> <message> <source>Cannot find missing dependency '%1' for '%2'.</source> <translation>Impossibile trovare la dipendenza mancante '%1' per '%2'.</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>Annullato</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file '%1': %2</source> <translation>Impossibile creare il file '%1': %2</translation> </message> <message> <source>Cannot write PID to lock file '%1': %2</source> <translation>Impossibile scrivere il PID nel file lockkato '%1': %2</translation> </message> <message> <source>Cannot obtain the lock for file '%1': %2</source> <translation>Impossibile ottenere il lock del file '%1': %2</translation> </message> <message> <source>Cannot release the lock for file '%1': %2</source> <translation>Impossibile rilasciare il lock del file '%1': %2</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Impossibile eseguire il backup di %1: %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 2</source> <translation>esattamente 2</translation> </message> <message> <source>Cannot open file '%1' for writing: %2</source> <translation>Impossibile aprire il file %1 in scrittura: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Impossibile trovare il file di backup per %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Impossibile ripristinare il file di backup per %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Impossibile ripristinare il file di backup per %1: %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot backup file %1.</source> <translation>Impossibile eseguire il backup del file %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Argomenti invalidi: argomenti forniti %1, richiesti 2.</translation> </message> <message> <source>Cannot copy a non-existent file: %1</source> <translation>Impossibile copiare un file non esistente: %1</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Impossibile rimuovere il file di destinazione %1: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Impossibile copiare %1 in %2: %3</translation> </message> <message> <source>Cannot delete file %1: %2</source> <translation>Impossibile eliminare il file %1: %2</translation> </message> <message> <source>Cannot restore backup file into %1: %2</source> <translation>Impossibile ripristinare il file di backup in %1: %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of %1: %2</source> <translation>Impossibile creare il backup di %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Argomenti invalidi: argomenti forniti %1, richiesti 1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Impossibile ripristinare il file di backup %1: %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download canceled.</source> <translation>Download annullato.</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>Cryptographic hashes non corrispondono.</translation> </message> <message> <source>Download finished.</source> <translation>Download finito.</translation> </message> <message> <source>%1 of %2</source> <translation>%1 di %2</translation> </message> <message> <source>%1 downloaded.</source> <translation>%1 scaricati.</translation> </message> <message> <source>(%1/sec)</source> <translation></translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n giorni, </numerusform> <numerusform></numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n ore, </numerusform> <numerusform></numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n muniti</numerusform> <numerusform></numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n secondi</numerusform> <numerusform></numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation>- rimanenti %1%2%3%4.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - tempo rimanente sconosciuto.</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1: Writing to file '%2' failed: %3</source> <translation>Impossibile scaricare %1: Scrittura nel file '%2' fallita: %3</translation> </message> <message> <source>Cannot download %1: Cannot create %2: %3</source> <translation>Impossibile scaricare %1: Impossibile creare %2: %3</translation> </message> <message> <source>%1 at %2</source> <translation>%1 a %2</translation> </message> <message> <source>Authentication request canceled.</source> <translation>Richiesta di autenticazione annullata.</translation> </message> <message> <source>Secure Connection Failed</source> <translation>Connessione sicura non riuscita</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>Si ÃĻ verificato un errore durante la connessione a: %1.</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>Questo potrebbe essere un problema con la configurazione del server, o qualcuno prova a spacciarsi per il server.</translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>Se tu hai avuto una connessione con questo server nel passato o ti fidi del server, l'errore potrebbe essere temporaneo e puoi provare ancora.</translation> </message> <message> <source>Try again</source> <translation>Prova ancora</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open source file '%1' for reading.</source> <translation>Impossibile aprire il file sorgente '%1' in lettura.</translation> </message> <message> <source>Cannot open destination file '%1' for writing.</source> <translation>Impossibile aprire il file di destinazione '%1' in scrittura.</translation> </message> <message> <source>Writing to %1 failed: %2</source> <translation>Scrittura in %1 fallita: %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Argomenti invalidi: argomenti forniti %1, richiesti 1.</translation> </message> <message> <source>Cannot create folder %1: Unknown error.</source> <translation>Impossibile creare la cartella %1: Errore sconosciuto.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Impossibile rimuovere la cartella %1: %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file %1.</source> <translation>Impossibile eseguire il backup del file %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Argomenti invalidi: argomenti forniti %1, richiesti 2.</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Impossibile rimuovere il file di destinazione %1: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Impossibile copiare %1 in %2: %3</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Impossibile copiare %1 in %2: %3</translation> </message> <message> <source>Cannot remove file %1.</source> <translation>Impossibile rimuovere il file %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Impossibile ripristinare il file di backup %1: %2</translation> </message> </context> <context> <name>KDUpdater::PackagesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 contiene dati invalidi: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>Il file %1 non esiste.</translation> </message> <message> <source>Cannot open %1.</source> <translation>Impossibile aprire %1.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Errore durante il parse in %1 a %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>Elemento di root %1 inaspettato, dovrebbe essere 'Packages'.</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Impossibile eseguire il backup di %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Argomenti invalidi: argomenti forniti %1, richiesti 2.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Impossibile aprire %1 in lettura: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Impossibile aprire il file %1 in scrittura: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Impossibile trovare il file di backup per %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Impossibile ripristinare il file di backup per %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Impossibile ripristinare il file di backup per %1: %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1". Reason:</source> <translation>Impossibile leggere il file di risorse "%1". Motivo:</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Argomenti invalidi: argomenti forniti %1, richiesti 1.</translation> </message> <message> <source>Cannot remove folder %1: The folder does not exist.</source> <translation>Impossibile rimuovere la cartella %1: La cartella non esiste.</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Impossibile rimuovere la cartella %1: %2</translation> </message> <message> <source>Cannot recreate directory %1: %2</source> <translation>Impossibile ricreare la cartella %1: %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 iniziato</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>%1 non puÃē essere fermato</translation> </message> <message> <source>Cannot stop task %1</source> <translation>Impossibile fermare il task %1</translation> </message> <message> <source>%1 cannot be paused</source> <translation>%1 non puÃē essere sospeso</translation> </message> <message> <source>Cannot pause task %1</source> <translation>Impossibile mettere in pausa il task %1</translation> </message> <message> <source>Cannot resume task %1</source> <translation>Impossibile ripristinare il task %1</translation> </message> <message> <source>%1 done</source> <translation>%1 fatto</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>Impossibile accedere alle informazioni del pacchetto si questa applicazione.</translation> </message> <message> <source>Cannot access the update sources information of this application.</source> <translation>Impossibile accedere alle informazioni degli aggiornamenti di questa applicazione.</translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>Downloading Updates.xml in corso.</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>%n aggiornamento(i) trovati.</numerusform> <numerusform></numerusform> </translation> </message> <message> <source>Cannot download update source %1 from ('%2')</source> <translation>Impossibile scaricare l'aggiornamento %1 da ('%2')</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>File Updates.xml scaricato dagli aggiornamenti.</translation> </message> <message> <source>Computing applicable updates.</source> <translation>Calcolo aggiornamenti applicabili.</translation> </message> <message> <source>Application updates computed.</source> <translation>Aggiornamenti dell'applicazione calcolati.</translation> </message> </context> <context> <name>KDUpdater::UpdateSourcesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 contiene dati invalidi: %2</translation> </message> <message> <source>Cannot read "%1"</source> <translation>Impossibile leggere "%1"</translation> </message> <message> <source>XML Parse error in %1 at %2, %3: %4</source> <translation>Errore durante il parse del file %1 in %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "UpdateSources"</source> <translation>Elemento di Root %1 inaspettato, dovrebbe essere "UpdateSources"</translation> </message> <message> <source>Cannot save changes to "%1": %2</source> <translation>Impossibile salvare i cambiamenti in "%1": %2</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Cannot read "%1"</source> <translation>Impossibile leggere "%1"</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Errore durante il parse in %1 a %2, %3: %4</translation> </message> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Updates.xml contiene dati invalidi: %1</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>Elemento di root %1 inaspettato, dovrebbe essere "Updates".</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>L'elemento ApplicationName ÃĻ mancante.</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>L'elemento ApplicationVersion ÃĻ mancante.</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>L'elemento PackageUpdate ÃĻ senza nome</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>L'elemento PackageUpdate ÃĻ senza versione</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>L'elemento PackageUpdate ÃĻ senza data di relascio</translation> </message> </context> <context> <name>Lib7z</name> <message> <source>Cannot retrieve number of items in archive</source> <translation>Impossibile recuperare il numero di oggetti in archivio</translation> </message> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Impossibile recuperare il path dell'oggetto in archivio %1</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Trovata un eccezzione sconosciuta (%1)</translation> </message> <message> <source>internal code: %1</source> <translation>codice interno: %1</translation> </message> <message> <source>not enough memory</source> <translation>memoria insufficiente</translation> </message> <message> <source>Error: %1</source> <translation>Errore: %1</translation> </message> <message> <source>Cannot load codecs</source> <translation>Impossibile caricare i codec</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Impossibile recuperare il formato di default</translation> </message> <message> <source>Cannot create archive %1. %2</source> <translation>Impossibile creare l'archivio %1. %2</translation> </message> <message> <source>CArc index %1 out of bounds [0, %2]</source> <translation>L'indice CArc %1 ÃĻ fuori dai limiti [0, %2]</translation> </message> <message> <source>Item index %1 out of bounds [0, %2]</source> <translation>L'indice dell'oggetto %1 ÃĻ fuori dai limiti [0, %2]</translation> </message> <message> <source>Cannot create output file for writing: %1</source> <translation>Impossibile creare il file di output aperto in scrittura: %1</translation> </message> </context> <context> <name>Lib7z::ExtractItemJob</name> <message> <source>Cannot list archive: QIODevice not set or already destroyed.</source> <translation>Impossibile accedere al contenuto dell'archivio: QIODevice non configurato o già distrutto.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Errore durante l'estrazione '%1': %2</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Trovata un eccezzione sconosciuta (%1)</translation> </message> <message> <source>Failed</source> <translation>Fallito</translation> </message> </context> <context> <name>Lib7z::ListArchiveJob</name> <message> <source>Cannot list archive: QIODevice already destroyed.</source> <translation>Impossibile accedere all'archivio: QIODevice già distrutto.</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Trovata un eccezzione sconosciuta (%1)</translation> </message> <message> <source>Failed</source> <translation></translation> </message> </context> <context> <name>OpenArchiveInfo</name> <message> <source>Cannot load codecs</source> <translation>Impossibile caricare i codec</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Impossibile recuperare il formato di default</translation> </message> <message> <source>Cannot open archive</source> <translation>Impossibile aprire l'archivio</translation> </message> <message> <source>No CArc found</source> <translation>Nessun CArc trovato</translation> </message> </context> <context> <name>QIODeviceSequentialOutStream</name> <message> <source>No device set for output stream</source> <translation>Nessun dispositivo settato per lo stream di output</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>No marker found, stopped after %1.</source> <translation>Nessun marker trovato, stoppato dopo %1.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Impossibile aprire il file %1 in lettura: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Impossibile aprire il file %1 in scrittura: %2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>Lettura fallita dopo %1 bytes: %2</translation> </message> <message> <source>Copy failed. Error: %1</source> <translation>Copia fallita. Errore: %1</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Scrittura fallita dopo %1 bytes: %2</translation> </message> <message> <source>bytes</source> <translation></translation> </message> <message> <source>KiB</source> <translation></translation> </message> <message> <source>MiB</source> <translation></translation> </message> <message> <source>GiB</source> <translation></translation> </message> <message> <source>TiB</source> <translation></translation> </message> <message> <source>PiB</source> <translation></translation> </message> <message> <source>EiB</source> <translation></translation> </message> <message> <source>ZiB</source> <translation></translation> </message> <message> <source>YiB</source> <translation></translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Impossibile rimuovere il file %1: %2</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Impossibile rimuovere la cartella %1: %2</translation> </message> <message> <source>Cannot create folder %1</source> <translation>Impossibile creare la cartella %1</translation> </message> <message> <source>Cannot copy file from %1 to %2: %3</source> <translation>Impossibile copiare il file da %1 a %2: %3</translation> </message> <message> <source>Cannot move file from %1 to %2: %3</source> <translation>Impossibile muovere il file da %1 a %2: %3</translation> </message> <message> <source>Cannot create folder %1: %2</source> <translation>Impossibile creare la cartella %1: %2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>Impossibile aprire il file temporaneo: %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>Impossibile aprire il file temporaneo per template %1: %2</translation> </message> <message> <source>Cannot create temporary file</source> <translation>Impossibile creare il file temporaneo</translation> </message> <message> <source>Cannot retrieve property %1 for item %2</source> <translation>Impossibile recuperare la proprietà %1 per l'oggetto %2</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3</source> <translation>La propietà %1 per l'oggetto %2 non ÃĻ di tipo VT_FILETIME ma %3</translation> </message> <message> <source>Cannot convert file time to local time</source> <translation>Impossibile convertire l'orario del file in ora locale</translation> </message> <message> <source>Cannot convert local file time to system time</source> <translation>Impossible convertire l'ora del file in orario di sistema</translation> </message> <message> <source>Corrupt installation</source> <translation>Installazione corrotta</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>L'installazione potrebbe esserre corrotta. Ti consigliamo di re-installare da zero.</translation> </message> <message> <source>The specified module could not be found.</source> <translation>Il modulo specificato non ÃĻ stato trovato.</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Components cannot have children in updater mode.</source> <translation>Componenti non possono avere figli nella modalità di aggiornamento.</translation> </message> <message> <source>Cannot open the requested translation file '%1'.</source> <translation>Impossibile aprire il file di traduzioni richiesto '%1'.</translation> </message> <message> <source>Cannot open the requested UI file '%1'. Error: %2</source> <translation>Impossibile aprire il file UI '%1'. Errore: %2</translation> </message> <message> <source>Cannot load the requested UI file '%1'. Error: %2</source> <translation>Impossibile caricare il file UI '%1'. Errore: %2</translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>Impossibile risolvere isDefault in %1</translation> </message> <message> <source>Cannot open the requested license file '%1'. Error: %2</source> <translation>Impossibile aprire il file di licenza richiesto '%1'. Errore: %2</translation> </message> <message> <source>Error</source> <translation>Errore</translation> </message> <message> <source>Error: Operation %1 does not exist</source> <translation>Errore: L'operazione %1 non esiste</translation> </message> <message> <source>Update Info: </source> <translation>Informazioni di aggiornamento: </translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component Name</source> <translation>Nome Componente</translation> </message> <message> <source>Action</source> <translation>Azione</translation> </message> <message> <source>Installed Version</source> <translation>Versione Installata</translation> </message> <message> <source>New Version</source> <translation>Nuova Versione</translation> </message> <message> <source>Release Date</source> <translation>Data Di Rilascio</translation> </message> <message> <source>Size</source> <translation>Dimensione</translation> </message> <message> <source>Component is marked for installation.</source> <translation>Il componente ÃĻ segnato per l'installazione.</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>Il componente ÃĻ segnato per la disinstallazione.</translation> </message> <message> <source>Component is installed.</source> <translation>Il componente ÃĻ installato.</translation> </message> <message> <source>Component is not installed.</source> <translation>Il componente non ÃĻ installato.</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translation></translation> </message> <message> <source>Def&ault</source> <translation></translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translation></translation> </message> <message> <source>&Reset</source> <translation></translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translation></translation> </message> <message> <source>&Select All</source> <translation></translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translation></translation> </message> <message> <source>&Deselect All</source> <translation></translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>Questo componete occuperà circa %1 sul tuo hard disk.</translation> </message> <message> <source>Select Components</source> <translation>Componenti selezionati</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>Selezionare i componenti che vuoi aggiornare.</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>Selezionare i componenti che vuoi installare.</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>Selezionare i componenti che vuoi disinstallare.</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them.</source> <translation>Selezionare i componenti da installare. Deselezionare i componenti installati per disinstallarli.</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>at least 2</source> <translation>almeno 2</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Oggetto installer necessario in %1 l'operazione ÃĻ vuota.</translation> </message> <message> <source>Can not save the output of %1 to an empty installer key value.</source> <translation type="unfinished"></translation> </message> <message> <source>File '%1' does not exist or is not an executable binary.</source> <translation>Il file '%1' non esiste o non ÃĻ un eseguibile.</translation> </message> <message> <source>Running '%1' resulted in a crash.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source>2 or 3</source> <translation>2 o 3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source> (<source> <target> [forceOverwrite])</source> <translation></translation> </message> <message> <source>Invalid argument in %0: Third argument needs to be forceOverwrite, if specified</source> <translation>Parametro invalido in %0; Terzo parametro deve essere forceOverwrite, se specificato</translation> </message> <message> <source>Invalid arguments in %0: Directories are invalid: %1 %2</source> <translation>Argomenti invalidi in %0: Le cartelle sono invalide: %1 %2</translation> </message> <message> <source>Cannot create %0</source> <translation>Impossibile creare %0</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Impossibile sovrascrivere %1</translation> </message> <message> <source>Cannot copy %0 to %1, error was: %3</source> <translation>Impossibile copiare il file da %0 a %1, errore: %3</translation> </message> <message> <source>Cannot remove %0</source> <translation>Impossibile rimuovere %0</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation type="unfinished"></translation> </message> <message> <source>Cannot open source '%1' for read. Error: %2.</source> <translation>Impossibile aprire il file sorgente '%1' in lettura. Errore: %2.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <translation>Impossibile aprire il file di destinazione '%1' in scrittura. Errore: %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <translation>Scrittura nel target '%1' fallita. Errore: %2.</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Impossibile eseguire il backup del file %1: %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 2</source> <translation>esattamente 2</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Impossibile sovrascrivere %1</translation> </message> <message> <source>Cannot write Desktop Entry at %1</source> <translation></translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 2</source> <translation>esattamente 2</translation> </message> <message> <source>Cannot create link from %1 to %2.</source> <translation>Impossibile creare il link per %1 a %2.</translation> </message> <message> <source>Cannot remove link from %1 to %2.</source> <translation>Impossibile rimuovere il link per %1 a %2.</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set file permissions %1!</source> <translation>Impossibile settare i permessi del file %1!</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Impossibile rimuovere il file %1: %2</translation> </message> <message> <source>Cannot move file %1 to %2. Error: %3</source> <translation>Impossibile muovere il file da %1 a %2: Errore %3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 2</source> <translation>esattamente 2</translation> </message> <message> <source>Installer needs to be an offline version: %1.</source> <translation>Installer deve essere una versione offline: %1.</translation> </message> <message> <source>Cannot open file: %1</source> <translation>Impossibile aprire il file: %1</translation> </message> <message> <source>Cannot read: %1. Error: %2</source> <translation>Impossibile leggere: %1. Errore: %2</translation> </message> <message> <source>Cannot open file: %1. Error: %2</source> <translation>Impossibile aprire il file: %1. Errore %2</translation> </message> <message> <source>Cannot create target dir: %1.</source> <translation>Impossibile creare la cartella di destinazione: %1.</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>Trovata un eccezzione sconosciuta (%1).</translation> </message> <message> <source>Removing file: %0</source> <translation>Rimozione file: %0</translation> </message> <message> <source>Cannot remove %0.</source> <translation>Impossibile rimuovere %0.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Impossibile rimuovere la cartella %1: %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>2 or 3</source> <translation>2 o 3</translation> </message> <message> <source> (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</source> <translation> (opzionale: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</translation> </message> <message> <source>Cannot create folder %1: %2.</source> <translation>Impossibile creare la cartella %1: %2.</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>Impossibile sovrascrivere %1: %2</translation> </message> <message> <source>Cannot create link %1: %2</source> <translation>Impossibile creare il link %1: %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>Annullato</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>Scaricamento firma hash fallito.</translation> </message> <message> <source>Download Error</source> <translation></translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>Verifica Hash durante il download fallita. Questo ÃĻ un errore temporaneo, riprovare.</translation> </message> <message> <source>Cannot verify Hash</source> <translation>Impossibile verificare l'hash</translation> </message> <message> <source>Cannot download archive: %1 : %2</source> <translation>Impossibile scaricare l'archivio %1. %2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>Impossibile eseguire il fetch dell'archivio: %1 Errore durante lo scaricamento %2</translation> </message> <message> <source>Downloading archive '%1' for component: %2</source> <translation>Scaricamento archivio '%1' per componente: %2</translation> </message> <message> <source>Scheme not supported: %1 (%2)</source> <translation>Schema non supportato: %1 (%2)</translation> </message> <message> <source>Cannot find component for: %1.</source> <translation>Impossibile trovare il componente per : %1.</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Target '%1' not open for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Target '%1' non aperto in scrittura. Errore: %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Scrittura nel target '%1' fallita. Errore: %2.</translation> </message> <message> <source>Redirect loop detected '%1'.</source> <translation>Rilevato loop di reindirizzamento '%1'.</translation> </message> <message> <source>Checksum mismatch detected '%1'.</source> <translation>Rilevato checksum non corrispondente '%1'.</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Errore di rete durante lo scaricamento di '%1': %2.</translation> </message> <message> <source>Unknown network error while downloading: %1.</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>Errore di rete sconosciuto durante lo scaricamento: %1.</translation> </message> <message> <source>Pause and resume not supported by network transfers.</source> <translation>Sospensione e ripristino non sono supportati dal trasferimento di rete.</translation> </message> <message> <source>Invalid source '%1'. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Sorgente invalida '%1'. Errore: %2.</translation> </message> <message> <source>Target file '%1' already exists but is not a file.</source> <translation>Il file di destinazione '%1' ÃĻ gia esistente ma non ÃĻ un file.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Impossibile aprire il file di destinazione '%1' in scrittura. Errore: %2.</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>at least 1</source> <translation>almeno 1</translation> </message> <message> <source>Execution failed: Cannot start detached: "%1"</source> <translation>Esecuzione fallita:Impossibile iniziare: "%1"</translation> </message> <message> <source>Execution failed: Cannot start: "%1"(%2)</source> <translation>Esecuzione fallita:Impossibile iniziare: "%1"(%2)</translation> </message> <message> <source>Execution failed(Crash): "%1"</source> <translation>Esecuzione fallita(Crash): "%1"</translation> </message> <message> <source>Execution failed(Unexpected exit code: %1): "%2"</source> <translation>Esecuzione fallita(Codice di uscita inaspettato: %1): "%2"</translation> </message> </context> <context> <name>QInstaller::EnvironmentVariableOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>2 to 4</source> <translation>da 2 a 4</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 2</source> <translation>esattamente 2</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open %1 for reading: %2.</source> <translation>Impossibile aprire %1 in lettura: %2.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Errore durante l'estrazione '%1': %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Eccezzione sconosciuta durante l'estrazione %1.</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Number of arguments does not match: one is required</source> <translation>Il numero di argomenti non corrisponde: solo uno ÃĻ richiesto</translation> </message> <message> <source>Cannot get package manager core.</source> <translation>Impossibile prendere il cuore del gestore pacchetti.</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>Questo processo dovrebbe essere fermato prima di continuare: %1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>Questi processi dovrebbe essere fermato prima di continuare: %1</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%1 di %2</translation> </message> <message> <source>%1 received.</source> <translation>ricevuti %1.</translation> </message> <message> <source>(%1/sec)</source> <translation></translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n giorni, </numerusform> <numerusform></numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n ore, </numerusform> <numerusform></numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n muniti</numerusform> <numerusform></numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n secondi</numerusform> <numerusform></numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation>- rimanenti %1%2%3%4.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - tempo rimanente sconosciuto.</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>Completata la procedura guidata per %1</translation> </message> <message> <source>Click Done to exit the %1 Wizard.</source> <translation>Clicca Fatto per uscire dalla procedura guidata di %1.</translation> </message> <message> <source>Click Finish to exit the %1 Wizard.</source> <translation>Clicca Finito per uscire dalla procedura guidata di %1.</translation> </message> <message> <source>Restart</source> <translation>Riavvio</translation> </message> <message> <source>Run %1 now.</source> <translation>Inizia %1 ora.</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>La procedura guidata di %1 ÃĻ fallita.</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable</source> <translation>Le impostazioni non sono scrivibili</translation> </message> <message> <source>Failed to write settings</source> <translation>Fallita la scrittura della configurazione</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>3, 4 or 5</source> <translation>3,4 o 5</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>1 or 2</source> <translation>1 o 2</translation> </message> <message> <source> (Sourcepath, [Vendorprefix])</source> <translation></translation> </message> <message> <source>Invalid Argument: source folder must not be empty.</source> <translation>Argomenti invalidi: la cartella sorgente non deve essere vuota.</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation>Impossibile eseguire il backup del file %1: %2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>Impossibile sovrascrivere %1: %2</translation> </message> <message> <source>Failed to copy file %1: %2</source> <translation>Impossibile copiare %1: %2</translation> </message> <message> <source>Cannot create folder at %1: %2</source> <translation>Impossibile creare la cartella %1: %2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation type="unfinished"></translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation type="unfinished"></translation> </message> <message> <source>Add or remove components</source> <translation>Aggiungere o rimuovere componenti</translation> </message> <message> <source>Update components</source> <translation>Aggiornamento componenti</translation> </message> <message> <source>Remove all components</source> <translation>Rimozione di tutti i componenti</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation type="unfinished"></translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation type="unfinished"></translation> </message> <message> <source>No updates available.</source> <translation>Nessun aggiornamento disponibile.</translation> </message> <message> <source> Only local package management available.</source> <translation type="unfinished"></translation> </message> <message> <source>Quit</source> <translation>Uscita</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>Contratto di licenza</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translation></translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>Leggere il seguente contratto di licenza. E' necessario accettare i termini contenuti in questo contratto prima di continuare con l'installazione.</translation> </message> <message> <source>I accept the license.</source> <translation>Accetto la licenza.</translation> </message> <message> <source>I do not accept the license.</source> <translation>Non accetto la licenza.</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>Leggere il seguente contratto di licenza. E' necessario accettare i termini contenuti in questo contratto prima di continuare con l'installazione.</translation> </message> <message> <source>I accept the licenses.</source> <translation>Accetto le licenze.</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>Non accetto le licenze.</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translation></translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>Nessun file di licenza trovato da copiare.</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Oggetto installer necessario in %1 l'operazione ÃĻ vuota.</translation> </message> <message> <source>Can not write license file: %1.</source> <translation>Impossibile scrivere il file di licenza: %1.</translation> </message> <message> <source>No license files found to delete.</source> <translation>Nessun file di licenza trovato da eliminare.</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 3</source> <translation>esattamente 3</translation> </message> <message> <source>Failed to open '%1' for reading.</source> <translation>Fallita l'apertura del file %1 in lettura.</translation> </message> <message> <source>Failed to open '%1' for writing.</source> <translation>Fallita l'apertura del file %1 in scrittura.</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>Manca il motore per la gestione dei pacchetti.</translation> </message> <message> <source>Preparing meta information download...</source> <translation>Preparazione scaricamento metadati...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>Scaricamento dei meta dati annullato.</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>Le credenziali del proxy sono mancanti.</translation> </message> <message> <source>Authentication failed.</source> <translation>Autentificazione fallita.</translation> </message> <message> <source>Unknown exception during download.</source> <translation>Eccezzione sconosciuta durante download.</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>Recupero metadati da repository remoto...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>Fallito il fetch del repository.</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>Eccezzione sconosciuta durante l'estrazione.</translation> </message> <message> <source>Extracting meta information...</source> <translation>Estrazione metadati ...</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Errore durante l'estrazione di '%1': %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Eccezzione sconosciuta durante l'estrazione %1.</translation> </message> <message> <source>Cannot open %1 for reading. Error: %2</source> <translation>Impossibile aprire %1 in lettura. Errore: %2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source> Downloading packages...</source> <translation> Scaricamento pacchetti...</translation> </message> <message> <source>Installation canceled by user</source> <translation>Installazione cancellata dall'utente</translation> </message> <message> <source>All downloads finished.</source> <translation>Tutti i downloads sono finiti.</translation> </message> <message> <source>Error</source> <translation>Errore</translation> </message> <message> <source>Cancelling the Installer</source> <translation>Annullamento del programma di installazione</translation> </message> <message> <source>Error writing Maintenance Tool</source> <translation>Errore di scrittura Maintenance Tool</translation> </message> <message> <source>Authentication Error</source> <translation>Errore di autentificazione</translation> </message> <message> <source>Some components could not be removed completely because admin rights could not be acquired: %1.</source> <translation>Alcuni componenti non sono stati rimossi completamente perchÃĻ i diritti di amministratore non sono stati acquisiti: %1.</translation> </message> <message> <source>Unknown error.</source> <translation>Errore sconosciuto.</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>Alcuni componenti non sono stati rimossi completamente perchÃĻ si ÃĻ presentato un errore sconosciuto.</translation> </message> <message> <source>Application not running in Package Manager mode!</source> <translation>Applicazione non in esecuzione in modalità Package Manager!</translation> </message> <message> <source>No installed packages found.</source> <translation>Nessun pacchetto di installazione trovato.</translation> </message> <message> <source>Application running in Uninstaller mode!</source> <translation>Applicazione avviata in modalità di disinstallazione!</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>E' disponibile un aggiornamento importante, eseguire l'aggiornamento prima.</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>Errore durante l'elevazione dei diritti di accesso.</translation> </message> <message> <source>invalid</source> <translation>invalido</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Error</source> <translation>Errore</translation> </message> <message> <source>Access error</source> <translation>Errore di accesso</translation> </message> <message> <source>Format error</source> <translation>Errore di formato</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>Impossibile scrivere la configurazione dell'installer in %1: %2</translation> </message> <message> <source>Stop Processes</source> <translation>Stop Processi</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>Questi processi debbono essere fermati per continuare: %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>Installazione fermata dall'utente</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>Variabile 'TargetDir' non impostata.</translation> </message> <message> <source>Preparing the installation...</source> <translation>Preparazione dell'installazione...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>Impossibile installare dal percorso di rete</translation> </message> <message> <source>Creating local repository</source> <translation>Creazione repository locale</translation> </message> <message> <source> Installation finished!</source> <translation> Installazione finita!</translation> </message> <message> <source> Installation aborted!</source> <translation> Installazione interrotta!</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>Impossibile eseguire l'operazione dal percorso di rete</translation> </message> <message> <source>Removing deselected components...</source> <translation>Rimozione componenti deselezionati...</translation> </message> <message> <source> Update finished!</source> <translation> Aggiornamento finito!</translation> </message> <message> <source> Update aborted!</source> <translation>Aggiornamento interrotto!</translation> </message> <message> <source>Unresolved dependencies</source> <translation>Dipendenze non risolte</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>Scrittura tool di mantenimento.</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>Fallito lo spostamento nel file %1: %2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation></translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>Impossibile scrivere i dati del tool di mantenimento in %1: %2</translation> </message> <message> <source>Cannot remove data file '%1': %2</source> <translation>Impossibile rimuovere i dati del file '%1': %2</translation> </message> <message> <source>Cannot write maintenance tool to %1: %2</source> <translation>Impossibile scrivere il tool di mantenimento in %1: %2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>Impossibile scrivere i dati del tool di mantenimento in %1: %2</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>Creazione tool di mantenimento</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>Disinstallazione completata con successo.</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>Disinstallazione interrotta.</translation> </message> <message> <source> Installing component %1</source> <translation> Installazione componenti %1</translation> </message> <message> <source>Installer Error</source> <translation>Errore Installer</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>Errore durante il processo di installazione (%1) %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>Impossibile preparare la disinstallazione</translation> </message> <message> <source>Cannot start uninstall</source> <translation>Impossibile iniziare la disinstallazione</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>Errore durante il processo di disinstallazione: %1</translation> </message> <message> <source>Unknown error</source> <translation>Errore sconosciuto</translation> </message> <message> <source>Cannot retrieve remote tree: %1.</source> <translation></translation> </message> <message> <source>Failure to read packages from: %1.</source> <translation>Fallita la lettura del pacchetto da: %1.</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>Impossibile recuperare i meta dati: %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation></translation> </message> <message> <source>Cannot find any update source information.</source> <translation>Impossibile trovare le informazioni per l'aggiornamento.</translation> </message> <message> <source>Dependency cycle between components detected: '%1' and '%2'.</source> <translation>Rilevato ciclo di dipendenza tra i componenti: '%1' e '%2'.</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>Configurazione di %1</translation> </message> <message> <source>Maintain %1</source> <translation>Mantenimento di %1</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>Vuoi annullare il processo di installazione?</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>Vuoi annullare il processo di disinstallazione?</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>Vuoi uscire dall'applicazione di installazione?</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>Vuoi uscire dall'applicazione di disinstallazione?</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>Vuoi uscire dall'applicazione di mantenimento?</translation> </message> <message> <source>Question</source> <translation>Domanda</translation> </message> <message> <source>Settings</source> <translation>Configurazioni</translation> </message> <message> <source>Error</source> <translation>Errore</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>E' impossibile installare dal percorso di rete. Copiare l'installer in locale</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>&Mostra Dettagli</translation> </message> <message> <source>&Hide Details</source> <translation>&Nascondi Dettagli</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>Disi&nstalla</translation> </message> <message> <source>Uninstalling %1</source> <translation>Disinstallazione %1</translation> </message> <message> <source>&Update</source> <translation></translation> </message> <message> <source>Updating components of %1</source> <translation>Aggiornamento componenti di %1</translation> </message> <message> <source>&Install</source> <translation>&Installa</translation> </message> <message> <source>Installing %1</source> <translation>Installazione %1</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation>Finestra</translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>Il proxy %1 richiede un nome utente e una password.</translation> </message> <message> <source>Username:</source> <translation>Nome Utente:</translation> </message> <message> <source>Username</source> <translation>Nome utente</translation> </message> <message> <source>Password:</source> <translation></translation> </message> <message> <source>Password</source> <translation></translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>Disi&nstalla</translation> </message> <message> <source>Ready to Uninstall</source> <translation>Pronto per la disinstallazione</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>Il programma adesso ÃĻ pronto per iniziare a rimuovere %1 dal tuo computer.<br><font color="red">La cartella del programma %2 sarà completamente cancellata</font> incluso tutto il suo contenuto!</translation> </message> <message> <source>U&pdate</source> <translation></translation> </message> <message> <source>Ready to Update Packages</source> <translation>Pronto per aggiornare i pacchetti</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>Il programma adesso ÃĻ pronto per iniziare l'aggiornamento dell'installazione.</translation> </message> <message> <source>&Install</source> <translation>&Installa</translation> </message> <message> <source>Ready to Install</source> <translation>Pronto per l'installazione</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>Il programma adesso ÃĻ pronto per iniziare l'installazione di %1 sul tuo computer.</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation! Available space: %1, at least required %2.</source> <translation>Spazio su disco insufficiente per creare i file temporanei e per l'installazione! Spazio disponibile: %1, minimo necessario %2.</translation> </message> <message> <source>Not enough disk space to store all selected components! Available space: %1, at least required: %2.</source> <translation>Spazio su disco insufficiente per installare tutti i pacchetti selezionati! Spazio disponibile: %1, minimo necessario %2.</translation> </message> <message> <source>Not enough disk space to store temporary files! Available space: %1, at least required: %2.</source> <translation>Spazio su disco insufficiente per archiviare i file temporanei! Spazio disponibile: %1, minimo necessario %2.</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>Il volume che hai selezionato per l'installazione ha spazio sufficiente, ma dopo ci sarà meno dell 1% di spazio disponibile sul volume. %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>Il volume che hai selezionato per l'installazione ha spazio sufficiente, ma dopo ci saranno meno di 100 MB disponibili. %1</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>L'installazione userà %1 di spazio sul disco.</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>Impossibile risolvere tutte le dipendenze.</translation> </message> <message> <source>Components about to be removed.</source> <translation>Compenenti da rimuovere.</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>2 to 5</source> <translation>da 2 a 5</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>File di tipo registro sono supportati solo su WINDOWS.</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>Tipi di file di registro: Argomenti invalidi</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Impossibile leggere tutti i dati dopo l'invio del comando: %1. Bytes aspettati: %2, Bytes ricevuti: %3. Errori: %4</translation> </message> </context> <context> <name>QInstaller::RemoteServerConnection</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation type="vanished">Impossibile leggere tutti i dati dopo l'invio del comando: %1. Bytes aspettati: %2, Bytes ricevuti: %3. Errori: %4</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 3</source> <translation>esattamente 3</translation> </message> <message> <source>Failed to open %1 for reading</source> <translation>Fallita l'apertura del file %1 in lettura</translation> </message> <message> <source>Failed to open %1 for writing</source> <translation>Fallita l'apertura del file %1 in scrittura</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Cannot open Resource '%1' read-only.</source> <translation>Impossibile aprire la Risorsa '%1' in sola lettura.</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>Lettura fallita dopo %1 bytes: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Scrittura fallita dopo %1 bytes: %2</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>Completamento dell'installazione guidata del %1</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open the requested script file at %1: %2.</source> <translation>Impossibile aprire lo script %1: %2.</translation> </message> <message> <source>Exception while loading the component script '%1'. (%2)</source> <translation>Si ÃĻ verificata un eccezzione durante il caricamento dei componenti dello script '%1'. (%2)</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in '%1' operation is empty.</source> <translation>Oggetto installer necessario in %1 l'operazione ÃĻ vuota.</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>Auto riavvio: Valido solo con aggiornamento o in modalità packagemanager.</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>Riavvio automatico: Argomenti invalidi</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>Il server richiede l'autenticazione</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>E' necessario fornire un nome utente e una password per accedere a questo sito.</translation> </message> <message> <source>Username:</source> <translation>Nome Utente:</translation> </message> <message> <source>Password:</source> <translation></translation> </message> <message> <source>%1 at %2</source> <translation>%1 a %2</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) '%1' calling '%2' with arguments '%3'.</source> <translation>Argomenti mancanti '%1' con chiamata '%2' con argomenti '%3'.</translation> </message> <message> <source>Current method argument calling '%1' with arguments '%2' is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>Il corrente metodo di chiamata argomenti '%1' con argomenti '%2' non ÃĻ supportato. Perfavore usare imposta, rimuovere, add_array_value o remove_array_value.</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Argomenti invalidi in %0: forniti %1, %2 richiesti %3.</translation> </message> <message> <source>exactly 2</source> <translation>esattamente 2</translation> </message> <message> <source>None of the arguments can be empty: source '%1', target '%2'.</source> <translation>Nessuno degli argomenti puÃē essere vuoto: sorgente '%1', destinazione '%2'.</translation> </message> <message> <source>Cannot move source '%1' to target '%2', because target exists and is not removable.</source> <translation>Impossibile muovere '%1' in '%2', perchÃĻ il target esiste e non ÃĻ eliminabile.</translation> </message> <message> <source>Cannot move source '%1' to target '%2': %3</source> <translation>Impossibile muovere il file da %1 a %2: %3</translation> </message> <message> <source>Move '%1' to '%2'.</source> <translation>Muovi '%1' in '%2'.</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>Collegamento al menu di Avvio</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.</source> <translation>Selezionare la voce del menu di avvio in cui si desidera creare il collegamento al programma. È anche possibile inserire un nome per creare una nuova cartella.</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>Cartella di installazione</translation> </message> <message> <source>Please specify the folder where %1 will be installed.</source> <translation>Specificare la cartella dove %1 sarà installato.</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translation></translation> </message> <message> <source>B&rowse...</source> <translation></translation> </message> <message> <source>The folder you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>La cartella selezionata ÃĻ già esistente e contiene già un installazione. Scegliere una cartella diversa per l'installazione.</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation>Hai selezionato una cartella esistente e non vuota per l'installazione. Ricordati che sarà tutto cancellato durante la disinstallazione di questo programma. Non ÃĻ consigliabile installare in questa cartella, l'installazione potrebbe fallire. Vuoi continuare?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>Hai selezionato un file o collegamento esistente, scegliere una cartella diversa per l'installazione.</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid folder.</source> <translation>Il percorso di installazione non puÃē essere vuoto, specificare una cartella valida.</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>Il percorso di installazione non puÃē essere relativo, specificare un percorso assoluto.</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>Il percorso o la cartella di installazione contengono caratteri non ASCII. Questo al momento non ÃĻ supportato! Scegliere un percorso o una cartella differenti.</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>Installare in %1 ÃĻ vietato.</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>Il percorso inserito ÃĻ troppo lungo, assicurarsi di inserire un percorso valido.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>Il percorso inserito non ÃĻ valido, assicurarsi di inserire una destinazione valida.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>Il percorso inserito non ÃĻ valido, assicurarsi di inserire un drive valido.</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid folder.</source> <translation>Il percorso di installazione non puÃē finire con '.', specificare una cartella valida.</translation> </message> <message> <source>The installation path must not contain '%1', please specify a valid folder.</source> <translation>Il percorso di installazione non puÃē contenere '%1', specificare una cartella valida.</translation> </message> <message> <source>Error</source> <translation>Errore</translation> </message> <message> <source>Warning</source> <translation></translation> </message> <message> <source>Select Installation Folder</source> <translation>Selezionare la cartella di installazione</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Empty repository URL.</source> <translation>URL repository vuoto.</translation> </message> <message> <source>URL scheme not supported: %1 (%2).</source> <translation>Schema URL non supportato: %1 (%2).</translation> </message> <message> <source>Got a timeout while testing: '%1'</source> <translation>E' scaduto un timeout durante il test: '%1'</translation> </message> <message> <source>Cannot parse Updates.xml! Error: %1.</source> <translation>Impossibile analizzare Updates.xml! Errore: %1.</translation> </message> <message> <source>Updates.xml could not be opened for reading!</source> <translation>Impossibile aprire Update.xml in lettura!</translation> </message> <message> <source>Updates.xml could not be found on server!</source> <translation>Impossibile trovare Update.xml sul server!</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>Richiesta autorizzazione</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>Inserisci la tua password di autorizzazione per sudo:</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>Errore durante l'acquisizione dei diritti di amministratore</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>Impossibile ottenere l'autorizzazione.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as root and then clicking OK.</source> <translation>Impossibile ottenere l'autorizzazione necessaria per continuare l'installazione. Interrompere l'installazione o eseguire %1 come root e premere ok.</translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>Impossibile aprire la risorsa %1: %2</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>Impossibile aprire il file di configurazione %1 in lettura: %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>Configurazione</translation> </message> <message> <source>Network</source> <translation></translation> </message> <message> <source>No proxy</source> <translation>Nessun proxy</translation> </message> <message> <source>System proxy settings</source> <translation>Configurazione proxy di sistema</translation> </message> <message> <source>Manual proxy configuration</source> <translation>Configurazione proxy manuale</translation> </message> <message> <source>HTTP proxy:</source> <translation>Proxy HTTP:</translation> </message> <message> <source>Port:</source> <translation>Porta:</translation> </message> <message> <source>FTP proxy:</source> <translation>Proxy FTP:</translation> </message> <message> <source>Repositories</source> <translation>Repository</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>Aggiungi il nome utente e la password per l'autorizzazione se necessario.</translation> </message> <message> <source>Use temporary repositories only</source> <translation>Usa solo repository temporanei</translation> </message> <message> <source>Add</source> <translation>Aggiungi</translation> </message> <message> <source>Remove</source> <translation>Rimuovi</translation> </message> <message> <source>Test</source> <translation></translation> </message> <message> <source>Show Passwords</source> <translation>Mostra Password</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>Controlla questo per usare repository durante il fecth.</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>Aggiungere il nome utente per l'autentificazione sul server.</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>Aggiungere la password per l'autenticazione sul server.</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>L'URL del server che contiene un repository valido.</translation> </message> <message> <source>There was an error testing this repository.</source> <translation>Si ÃĻ verificato un errore durante il test di questo repository.</translation> </message> <message> <source>Do you want to disable the tested repository?</source> <translation>Vuoi disabilitare il repository testato?</translation> </message> <message> <source>Hide Passwords</source> <translation>Nascondi Passwords</translation> </message> <message> <source>Use</source> <translation>Uso</translation> </message> <message> <source>Username</source> <translation>Nome utente</translation> </message> <message> <source>Password</source> <translation></translation> </message> <message> <source>Repository</source> <translation></translation> </message> <message> <source>Default repositories</source> <translation></translation> </message> <message> <source>Temporary repositories</source> <translation>Repository temporanei</translation> </message> <message> <source>User defined repositories</source> <translation>Repository edfiniti dall'utente</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable</source> <translation>La chiave di registro %1 non puÃē essere scritta</translation> </message> <message> <source>Cannot write to registry path %1</source> <translation>Impossibile scrivere la chiave di registro %1</translation> </message> <message> <source>Renaming %1 into %2 failed with %3.</source> <translation>La rinominazione di %1 in %2 ÃĻ fallita : %3.</translation> </message> </context> </TS> �������������������������������������������������������������������������������������src/sdk/translations/ifw_ja.ts����������������������������������������������������������������������0000664�0000000�0000000�00000522673�13253666515�0017011�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ja_JP"> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>HTTP ãƒĶマã‚ķãƒžčŠčĻžãŒåŋ…čĶã§ã™</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>こãŪã‚ĩã‚ĪトãŦã‚Ēã‚Ŋã‚ŧã‚đするãŦãŊãƒĶマã‚ķマ名ãĻパã‚đãƒŊマドがåŋ…čĶã§ã™ã€‚</translation> </message> <message> <source>Username:</source> <translation>ãƒĶマã‚ķマ名:</translation> </message> <message> <source>Password:</source> <translation>パã‚đãƒŊマド:</translation> </message> <message> <source>%1 at %2</source> <translation>%2 ãŪ %1</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>キãƒĢãƒģã‚ŧãƒŦしãūした</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 をバッã‚Ŋã‚Ēップできãūせん: %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 2</source> <translation type="vanished">2個</translation> </message> <message> <source>Cannot open file '%1' for writing: %2</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ '%1' を開けãūせんでした: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦがčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした。</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした: %2</translation> </message> <message> <source>Cannot backup file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をバッã‚Ŋã‚Ēップできãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot find backup file for "%1".</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした。</translation> </message> <message> <source>Cannot restore backup file for "%1".</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした。</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot backup file %1.</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 をバッã‚Ŋã‚Ēップできãūせんでした。</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation type="vanished">į„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ2個です。</translation> </message> <message> <source>Cannot backup file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をバッã‚Ŋã‚Ēップできãūせんでした。</translation> </message> <message> <source>Cannot copy a non-existent file: %1</source> <translation>存åœĻã—ãŠã„ãƒ•ã‚Ąã‚ĪãƒŦãŊã‚ģピマできãūせん: %1</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãŦã‚ģピマできãūせんでした: %3</translation> </message> <message> <source>Cannot delete file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot restore backup file into "%1": %2</source> <translation>バッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦを "%1" ãļåūĐ元できãūせんでした: %2</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation type="vanished">åŊūčąĄãƒ•ã‚Ąã‚ĪãƒŦ %1 を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation type="vanished">%1 を %2 ãŦã‚ģピマできãūせんでした: %3</translation> </message> <message> <source>Cannot delete file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot restore backup file into %1: %2</source> <translation type="vanished">バッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦを %1 ãļåūĐ元できãūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of %1: %2</source> <translation type="vanished">%1 ãŪバッã‚Ŋã‚Ēãƒƒãƒ—ã‚’ä―œæˆã§ããūせん: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation type="vanished">į„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ1個です。</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせん: %2</translation> </message> <message> <source>Cannot create backup of file "%1": %2</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download canceled.</source> <translation>ダã‚ĶãƒģロマドをキãƒĢãƒģã‚ŧãƒŦしãūした。</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>暗号å­Ķįš„ãƒãƒƒã‚·ãƒĨãŪå€Īが合č‡īしãūせん。</translation> </message> <message> <source>Download finished.</source> <translation>ダã‚ĶãƒģロマドがåŪŒäš†ã—ãūした。</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - æŪ‹ã‚Šæ™‚é–“: äļæ˜Žã€‚</translation> </message> <message> <source>%1 of %2</source> <translation>%1 / %2</translation> </message> <message> <source>%1 downloaded.</source> <translation>%1 ダã‚Ķãƒģロマドしãūした。</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/į§’)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n æ—Ĩ </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n 時間 </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n 分 </numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n į§’ </numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation>- æŪ‹ã‚Šæ™‚é–“ %1%2%3%4。</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1: Writing to file '%2' failed: %3</source> <translation type="vanished">%1 をダã‚Ķãƒģロマドできãūせん: ãƒ•ã‚Ąã‚ĪãƒŦ '%2' ãļãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした: %3</translation> </message> <message> <source>Cannot download %1: Cannot create %2: %3</source> <translation type="vanished">%1 をダã‚Ķãƒģロマドできãūせん: %2 ã‚’ä―œæˆã§ããūせんでした: %3</translation> </message> <message> <source>Cannot download %1. Writing to file "%2" failed: %3</source> <translation>%1 をダã‚Ķãƒģロマドできãūせん: ãƒ•ã‚Ąã‚ĪãƒŦ '%2' ãļãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした: %3</translation> </message> <message> <source>Cannot download %1. Cannot create file "%2": %3</source> <translation>%1 をダã‚Ķãƒģロマドできãūせん: %2 ã‚’ä―œæˆã§ããūせんでした: %3</translation> </message> <message> <source>%1 at %2</source> <translation>%2 ãŪ %1</translation> </message> <message> <source>Authentication request canceled.</source> <translation>詍čĻžčĶæą‚ãŒã‚­ãƒĢãƒģã‚ŧãƒŦされãūした。</translation> </message> <message> <source>Secure Connection Failed</source> <translation>åŪ‰å…ĻおæŽĨįķšãŒã§ããūせんでした</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>%1 ãļãŪæŽĨįķšäļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>ã‚ĩマバマãŪčĻ­åۚãŦå•éĄŒãŒã‚ã‚‹ã‹ã€ã‚ã‚‹ã„ãŊã‚ĩマバマãŦ成りすãūそうãĻしãĶいるもãŪがいるかもしれãūせん。</translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>äŧĨ前ãŦこãŪã‚ĩマバマãļãŪæŽĨįķšãŦ成功しãĶいるかこãŪã‚ĩマバがäŋĄé žã§ãã‚‹ãŪであれば、ã‚ĻãƒĐマãŊä™‚įš„ãŠã‚‚ãŪであるåŊčƒ―æ€§ãŒã‚ã‚Šãūす。そãŪå ī合、再čĐĶčĄŒã—ãĶください。</translation> </message> <message> <source>Try again</source> <translation>再čĐĶ行</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open source file '%1' for reading.</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦã‚―ãƒžã‚đãƒ•ã‚Ąã‚ĪãƒŦ '%1' を開けãūせん。</translation> </message> <message> <source>Cannot open destination file '%1' for writing.</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦåŊūčąĄãƒ•ã‚Ąã‚ĪãƒŦ '%1' を開けãūせん。</translation> </message> <message> <source>Writing to %1 failed: %2</source> <translation type="vanished">%1 ãļãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした: %2</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãļãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした: %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation type="vanished">į„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ1個です。</translation> </message> <message> <source>Cannot create folder %1: Unknown error.</source> <translation type="vanished">フã‚ĐãƒŦダ %1 ã‚’ä―œæˆã§ããūせんでした: 朊įŸĨãŪã‚ĻãƒĐマです。</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation type="vanished">デã‚Ģノã‚Ŋトナ %1 を削é™Īできãūせん: %2</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Unknown error.</source> <translation>朊įŸĨãŪã‚ĻãƒĐマ。</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" を削é™Īできãūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file %1.</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 をバッã‚Ŋã‚Ēップできãūせんでした。</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation type="vanished">į„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ2個です。</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation type="vanished">åŊūčąĄãƒ•ã‚Ąã‚ĪãƒŦ %1 を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation type="vanished">%1 を %2 ãŦã‚ģピマできãūせんでした: %3</translation> </message> <message> <source>Cannot remove file %1.</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を削é™Īできãūせん。</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせん: %2</translation> </message> <message> <source>Cannot backup file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をバッã‚Ŋã‚Ēップできãūせんでした。</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãŦã‚ģピマできãūせんでした: %3</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした。</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::PackagesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation type="vanished">%1 ãŦį„ĄåŠđおã‚ģãƒģテãƒģツがåŦãūれãĶいãūす: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 が存åœĻしãūせん。</translation> </message> <message> <source>Cannot open %1.</source> <translation type="vanished">%1 を開けãūせんでした。</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation type="vanished">パマã‚đã‚ĻãƒĐマ %1 ãŪ%2行%3列: %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation type="vanished">ãƒŦマトã‚ĻãƒŽãƒĄãƒģトãŦ %1 ãŊä―ŋį”Ļできãūせん。'Packages'をä―ŋį”ĻしãĶください。</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 をバッã‚Ŋã‚Ēップできãūせん: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation type="vanished">į„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ2個です。</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ %1 を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ %1 を開けãūせんでした: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦがčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせん。</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation type="vanished">%1 į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせん: %2</translation> </message> <message> <source>Cannot backup file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をバッã‚Ŋã‚Ēップできãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot find backup file for "%1".</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦがčĶ‹ãĪかりãūせんでした。</translation> </message> <message> <source>Cannot restore backup file for "%1".</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした。</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>"%1" į”ĻãŪバッã‚Ŋã‚Ēãƒƒãƒ—ãƒ•ã‚Ąã‚ĪãƒŦをåūĐ元できãūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1". Reason:</source> <translation type="vanished">ãƒŠã‚―ãƒžã‚đãƒ•ã‚Ąã‚ĪãƒŦ "%1" ã‚’čŠ­ãŋčūžãŋできãūã›ã‚“ã§ã—ãŸã€‚į†į”ą:</translation> </message> <message> <source>Cannot read resource file "%1": %2</source> <translation>ãƒŠã‚―ãƒžã‚đãƒ•ã‚Ąã‚ĪãƒŦ "%1" ã‚’čŠ­ãŋčūžãŋできãūせんでした:%2</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation type="vanished">į„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ1個です。</translation> </message> <message> <source>Cannot remove folder %1: The folder does not exist.</source> <translation type="vanished">フã‚ĐãƒŦダ %1 を削é™Īできãūせんでした: フã‚ĐãƒŦダが存åœĻしãūせん。</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation type="vanished">フã‚ĐãƒŦダ %1 を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot recreate directory %1: %2</source> <translation type="vanished">デã‚Ģノã‚Ŋトナ %1 ãŒå†ä―œæˆã§ããūせん: %2</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>The directory does not exist.</source> <translation>フã‚ĐãƒŦダãŊ存åœĻしãūせん。</translation> </message> <message> <source>Cannot recreate directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 を開始しãūした</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>%1 ãŊäļ­æ­Ēできãūせん</translation> </message> <message> <source>Cannot stop task %1</source> <translation>ã‚ŋã‚đã‚Ŋ %1 ãŊäļ­æ­Ēできãūせん</translation> </message> <message> <source>%1 cannot be paused</source> <translation>%1 ãŊä™‚停æ­Ēできãūせん</translation> </message> <message> <source>Cannot pause task %1</source> <translation>ã‚ŋã‚đã‚Ŋ %1 ãŊä™‚停æ­Ēできãūせん</translation> </message> <message> <source>Cannot resume task %1</source> <translation>ã‚ŋã‚đã‚Ŋ %1 ãŊ再開できãūせん</translation> </message> <message> <source>%1 done</source> <translation>%1 がįĩ‚䚆しãūした</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>こãŪã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģãŪãƒ‘ãƒƒã‚ąãƒžã‚ļæƒ…å ąãŦã‚Ēã‚Ŋã‚ŧã‚đできãūせんでした。</translation> </message> <message> <source>Cannot access the update sources information of this application.</source> <translation type="vanished">こãŪã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģãŪæ›īæ–°å…ƒæƒ…å ąãŦã‚Ēã‚Ŋã‚ŧã‚đできãūせんでした。</translation> </message> <message> <source>No package sources set for this application.</source> <translation>こãŪã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģį”ĻãŦãƒ‘ãƒƒã‚ąãƒžã‚ļãŊį”Ļ意されãĶいãūせん。</translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>æ›ī新元から Updates.xml をダã‚ĶãƒģロマドしãĶいãūす。</translation> </message> <message> <source>Cannot download package source %1 from "%2".</source> <translation>%1 から %2 ãļãƒ‘ãƒƒã‚ąãƒžã‚ļをダã‚Ķãƒģロマドできãūせんでした。</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>æ›ī新元から Updates.xml ãƒ•ã‚Ąã‚ĪãƒŦをダã‚Ķãƒģロマドしãūした。</translation> </message> <message> <source>Computing applicable updates.</source> <translation>æ›ī新をéĐį”ĻしãĶいãūす。</translation> </message> <message> <source>Application updates computed.</source> <translation>ã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģãŦæ›ī新をéĐį”Ļしãūした。</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>%n個ãŪæ›ī新がčĶ‹ãĪかりãūした。</numerusform> </translation> </message> <message> <source>Cannot download update source %1 from ('%2')</source> <translation type="vanished">('%2') からæ›ī新元 %1 をダã‚Ķãƒģロマドできãūせんでした</translation> </message> </context> <context> <name>KDUpdater::UpdateSourcesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation type="vanished">%1 ãŦį„ĄåŠđおã‚ģãƒģテãƒģツがåŦãūれãĶいãūす: %2</translation> </message> <message> <source>Cannot read "%1"</source> <translation type="vanished">"%1" ã‚’čŠ­ãŋčūžã‚ãūせんでした</translation> </message> <message> <source>XML Parse error in %1 at %2, %3: %4</source> <translation type="vanished">XMLパマã‚đã‚ĻãƒĐマ %1 ãŪ%2行%3列: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "UpdateSources"</source> <translation type="vanished">ãƒŦマトã‚ĻãƒŽãƒĄãƒģトãŦ %1 ãŊä―ŋį”Ļできãūせん。"UpdateSources"をä―ŋį”ĻしãĶください</translation> </message> <message> <source>Cannot save changes to "%1": %2</source> <translation type="vanished">"%1" ãļãŪåΉæ›īをäŋå­˜ã§ããūせんでした: %2</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Cannot read "%1"</source> <translation>"%1" ã‚’čŠ­ãŋčūžã‚ãūせんでした</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>パマã‚đã‚ĻãƒĐマ %1 ãŪ%2行%3列: %4</translation> </message> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Updates.xml ãŦį„ĄåŠđおã‚ģãƒģテãƒģツがåŦãūれãĶいãūす: %1</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>ãƒŦマトã‚ĻãƒŽãƒĄãƒģトãŦ %1 ãŊä―ŋį”Ļできãūせん。"Updates"をä―ŋį”ĻしãĶください。</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>ApplicationName ã‚ĻãƒŽãƒĄãƒģトがありãūせん。</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>ApplicationVersion ã‚ĻãƒŽãƒĄãƒģトがありãūせん。</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>PackageUpdate ã‚ĻãƒŽãƒĄãƒģトãŦ Name がありãūせん</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>PackageUpdate ã‚ĻãƒŽãƒĄãƒģトãŦ Version がありãūせん</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>PackageUpdate ã‚ĻãƒŽãƒĄãƒģトãŦ ReleaseDate がありãūせん</translation> </message> </context> <context> <name>Lib7z::ExtractItemJob</name> <message> <source>Cannot list archive: QIODevice not set or already destroyed.</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブからナã‚đトを取åū—できãūせんでした: QIODevice がčĻ­åŪšã•ã‚ŒãĶいおいか、æ—ĒãŦį īæĢ„されãĶいãūす。</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation type="vanished">'%1' ãŪåą•é–‹äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation type="vanished">朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした (%1)</translation> </message> <message> <source>Failed</source> <translation type="vanished">åĪąæ•—</translation> </message> </context> <context> <name>Lib7z::ListArchiveJob</name> <message> <source>Cannot list archive: QIODevice already destroyed.</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブからナã‚đトを取åū—できãūせんでした: QIODevice がæ—ĒãŦį īæĢ„されãĶいãūす。</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation type="vanished">朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした (%1)</translation> </message> <message> <source>Failed</source> <translation type="vanished">åĪąæ•—</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>bytes</source> <translation>バã‚Īト</translation> </message> <message> <source>KiB</source> <translation>KB</translation> </message> <message> <source>MiB</source> <translation>MB</translation> </message> <message> <source>GiB</source> <translation>GB</translation> </message> <message> <source>TiB</source> <translation>TB</translation> </message> <message> <source>PiB</source> <translation>PB</translation> </message> <message> <source>EiB</source> <translation>EB</translation> </message> <message> <source>ZiB</source> <translation>ZB</translation> </message> <message> <source>YiB</source> <translation>YB</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Cannot copy file from "%1" to "%2": %3</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãŦã‚ģピマできãūせんでした: %3</translation> </message> <message> <source>Cannot move file from "%1" to "%2": %3</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãļį§ŧ動できãūせんでした: %3</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>No marker found, stopped after %1.</source> <translation>ママã‚ŦマがčĶ‹ãĪからおかãĢたため、%1 で停æ­Ēしãūした。</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ %1 を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ %1 を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>%1 バã‚ĪトãŪ芭ãŋčūžãŋåūŒãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Copy failed: %1</source> <translation>ã‚ģピマãŦåĪąæ•—ã—ãūした: %1</translation> </message> <message> <source>Copy failed. Error: %1</source> <translation type="vanished">ã‚ģピマãŦåĪąæ•—ã—ãūした。ã‚ĻãƒĐマ: %1</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>%1 バã‚ĪトãŪæ›ļきčūžãŋåūŒãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation type="vanished">フã‚ĐãƒŦダ %1 を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot create folder %1</source> <translation type="vanished">フã‚ĐãƒŦダ %1 ã‚’ä―œæˆã§ããūせんでした</translation> </message> <message> <source>Cannot copy file from %1 to %2: %3</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を %2 ãŦã‚ģピマできãūせんでした: %3</translation> </message> <message> <source>Cannot move file from %1 to %2: %3</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を %2 ãļį§ŧ動できãūせんでした: %3</translation> </message> <message> <source>Cannot create folder %1: %2</source> <translation type="vanished">フã‚ĐãƒŦダ %1 ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>ä™‚ãƒ•ã‚Ąã‚ĪãƒŦを開けãūせんでした: %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>テãƒģプノマト %1 į”ĻãŪä™‚ãƒ•ã‚Ąã‚ĪãƒŦを開けãūせんでした: %2</translation> </message> <message> <source>Cannot create temporary file</source> <translation type="vanished">ä™‚ãƒ•ã‚Ąã‚ĪãƒŦã‚’ä―œæˆã§ããūせんでした</translation> </message> <message> <source>Cannot retrieve property %1 for item %2</source> <translation type="vanished">ã‚Ēã‚Īテム %2 からプロパテã‚Ģ %1 を取åū—できãūせんでした</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3</source> <translation type="vanished">ã‚Ēã‚Īテム %2 ãŪプロパテã‚Ģ %1 ãŪ型が VT_FILETIME でãŊおく %3 ãŦおãĢãĶいãūす</translation> </message> <message> <source>Cannot convert file time to local time</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦãŪ時åˆŧをロマã‚ŦãƒŦã‚ŋã‚ĪムãŦåĪ‰æ›ã§ããūせんでした</translation> </message> <message> <source>Cannot convert local file time to system time</source> <translation type="vanished">ロマã‚ŦãƒŦãƒ•ã‚Ąã‚ĪãƒŦãŪ時åˆŧをシã‚đテムãŪ時åˆŧãļåĪ‰æ›ã§ããūせんでした</translation> </message> <message> <source>Corrupt installation</source> <translation>į ī損したã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģį’°åǃ</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>あおたãŪã‚Īãƒģã‚đトマãƒŦしたã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģį’°åǃãŊį ī損しãĶいるようです。再ã‚Īãƒģã‚đトマãƒŦをæĪœčĻŽã—ãĶください。</translation> </message> <message> <source>The specified module could not be found.</source> <translation>指åŪšã•ã‚ŒãŸãƒĒã‚ļãƒĨマãƒŦがčĶ‹ãĪかりãūせんでした。</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Cannot open the requested translation file '%1'.</source> <translation type="vanished">čĶæą‚ã•ã‚ŒãŸįŋŧčĻģãƒ•ã‚Ąã‚ĪãƒŦ '%1' を開けãūせんでした。</translation> </message> <message> <source>Cannot open the requested UI file '%1'. Error: %2</source> <translation type="vanished">čĶæą‚ã•ã‚ŒãŸ UI ãƒ•ã‚Ąã‚ĪãƒŦ '%1' を開けãūせんでした。ã‚ĻãƒĐマ: %2</translation> </message> <message> <source>Cannot load the requested UI file '%1'. Error: %2</source> <translation type="vanished">čĶæą‚ã•ã‚ŒãŸ UI ãƒ•ã‚Ąã‚ĪãƒŦ '%1' をロマドできãūせんでした。ã‚ĻãƒĐマ: %2</translation> </message> <message> <source>Cannot open the requested license file '%1'. Error: %2</source> <translation type="vanished">čĶæą‚ã•ã‚ŒãŸãƒĐã‚Īã‚ŧãƒģã‚đãƒ•ã‚Ąã‚ĪãƒŦ '%1' を開けãūせんでした。ã‚ĻãƒĐマ: %2</translation> </message> <message> <source>Error</source> <translation>ã‚ĻãƒĐマ</translation> </message> <message> <source>Error: Operation %1 does not exist</source> <translation type="vanished">ã‚ĻãƒĐマ: æ“ä―œ %1 ãŊ存åœĻしãūせん</translation> </message> <message> <source>Update Info: </source> <translation>æ›īæ–°æƒ…å ą: </translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>%1 ãŪ isDefault をč§Ģæąšã§ããūせん</translation> </message> <message> <source>Components cannot have children in updater mode.</source> <translation>ã‚ģãƒģポマネãƒģトãŊã‚ĒップデマトãƒĒマドで子č́įī ã‚’持ãĶãūせん。</translation> </message> <message> <source>Cannot open the requested UI file "%1": %2</source> <translation>čĶæą‚ã•ã‚ŒãŸ UI ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot load the requested UI file "%1": %2</source> <translation>čĶæą‚ã•ã‚ŒãŸ UI ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をロマドできãūせんでした: %2</translation> </message> <message> <source>Cannot open the requested license file "%1": %2</source> <translation>čĶæą‚ã•ã‚ŒãŸãƒĐã‚Īã‚ŧãƒģã‚đãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Error: Operation %1 does not exist.</source> <translation>ã‚ĻãƒĐマ: æ“ä―œ %1 ãŊ存åœĻしãūせん。</translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component Name</source> <translation>ã‚ģãƒģポマネãƒģト名</translation> </message> <message> <source>Installed Version</source> <translation>ã‚Īãƒģã‚đトマãƒŦæļˆãŋバマã‚ļョãƒģ</translation> </message> <message> <source>New Version</source> <translation>新čĶãƒãƒžã‚ļョãƒģ</translation> </message> <message> <source>Size</source> <translation>ã‚ĩã‚Ī゚</translation> </message> <message> <source>Release Date</source> <translation>ナナマã‚đæ—Ĩ</translation> </message> <message> <source>Component is marked for installation.</source> <translation>ã‚ģãƒģポマネãƒģトãŊã‚Īãƒģã‚đトマãƒŦåŊūčąĄã§ã™ã€‚</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>ã‚ģãƒģポマネãƒģトãŊã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦåŊūčąĄã§ã™ã€‚</translation> </message> <message> <source>Component is installed.</source> <translation>ã‚ģãƒģポマネãƒģトãŊã‚Īãƒģã‚đトマãƒŦæļˆãŋです。</translation> </message> <message> <source>Component is not installed.</source> <translation>ã‚ģãƒģポマネãƒģトãŊ朊ã‚Īãƒģã‚đトマãƒŦです。</translation> </message> <message> <source>Action</source> <translation>ã‚Ēã‚Ŋショãƒģ</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translation>Alt+A</translation> </message> <message> <source>Def&ault</source> <translation>デフã‚ĐãƒŦト(&A)</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translation>Alt+R</translation> </message> <message> <source>&Reset</source> <translation>ナã‚ŧット(&R)</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translation>Alt+S</translation> </message> <message> <source>&Select All</source> <translation>すãđãĶをéļ択(&S)</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translation>Alt+D</translation> </message> <message> <source>&Deselect All</source> <translation>すãđãĶãŪéļ択をč§Ģé™Ī(&D)</translation> </message> <message> <source>To install new compressed repository, browse the repositories from your computer</source> <translation>新しく圧įļŪナポã‚ļトナãĻしãĶčŋ―加するナポã‚ļトナをこãŪã‚ģãƒģピãƒĨマã‚ŋマからéļ択しãĶください。</translation> </message> <message> <source>&Browse QBSP files</source> <translation>QBSPãƒ•ã‚Ąã‚ĪãƒŦã‚’å‚į…§ã™ã‚‹(&B)</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>こãŪã‚ģãƒģポマネãƒģトãŊハマドデã‚Ģã‚đã‚ŊäļŠãŦおよそ %1 åŋ…č́ãĻしãūす。</translation> </message> <message> <source>Open File</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦを開く</translation> </message> <message> <source>Select Components</source> <translation>ã‚ģãƒģポマネãƒģトãŪéļ択</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>æ›ī新したいã‚ģãƒģポマネãƒģトをéļ択しãĶください。</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>ã‚Īãƒģã‚đトマãƒŦしたいã‚ģãƒģポマネãƒģトをéļ択しãĶください。</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦしたいã‚ģãƒģポマネãƒģトをéļ択しãĶください。</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source> <translation>ã‚Īãƒģã‚đトマãƒŦするã‚ģãƒģポマネãƒģトをéļ択しãĶください。ã‚Īãƒģã‚đトマãƒŦæļˆãŋãŪã‚ģãƒģポマネãƒģトをã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦするå ī合ãŊéļ択をč§Ģé™ĪしãĶください。ã‚Īãƒģã‚đトマãƒŦæļˆãŋãŪã‚ģãƒģポマネãƒģトãŊæ›ī新されãūせん。</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒŦするã‚ģãƒģポマネãƒģトをéļ択しãĶください。ã‚Īãƒģã‚đトマãƒŦæļˆãŋãŪã‚ģãƒģポマネãƒģトをã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦするå ī合ãŊéļ択をč§Ģé™ĪしãĶください。</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>at least 2</source> <translation type="vanished">少おくãĻも2個</translation> </message> <message> <source><to be saved installer key name> <executable> [argument1] [argument2] [...]</source> <translation><äŋå­˜ã™ã‚‹ã‚Īãƒģã‚đトマãƒĐãŪキマ名> <åŪŸčĄŒãƒ•ã‚Ąã‚ĪãƒŦ> [垕数1][垕数2][...]</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>%1 ãŪã‚Īãƒģã‚đトマãƒĐä―œæˆãŦåŋ…čĶãŠæ“ä―œãŒčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Cannot save the output of "%1" to an empty installer key value.</source> <translation>ã‚Īãƒģã‚đトマãƒĐãŪキマãŪå€ĪがįĐšãŪため、"%1" ãŪ凚力をäŋå­˜ã§ããūせん。</translation> </message> <message> <source>File "%1" does not exist or is not an executable binary.</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" が存åœĻしおいか、åŪŸčĄŒåŊčƒ―ãŠãƒã‚ĪナナでãŊありãūせん。</translation> </message> <message> <source>Running "%1" resulted in a crash.</source> <translation>"%1" ãŪåŪŸčĄŒäļ­ãŦã‚ŊãƒĐッシãƒĨしãūした。</translation> </message> <message> <source>Can not save the output of %1 to an empty installer key value.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒĐãŪキマãŪå€ĪがįĐšãŪため、%1 ãŪ凚力をäŋå­˜ã§ããūせん。</translation> </message> <message> <source>File '%1' does not exist or is not an executable binary.</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ '%1' が存åœĻしおいか、åŪŸčĄŒåŊčƒ―ãŠãƒã‚ĪナナでãŊありãūせん。</translation> </message> <message> <source>Running '%1' resulted in a crash.</source> <translation type="vanished">'%1' ãŪåŪŸčĄŒäļ­ãŦã‚ŊãƒĐッシãƒĨしãūした。</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source>2 or 3</source> <translation type="vanished">2あるいãŊ3個</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source> (<source> <target> [forceOverwrite])</source> <translation type="vanished"> (<ã‚―ãƒžã‚đ> <ã‚ŋマã‚ēット> [forceOverwrite])</translation> </message> <message> <source>Invalid argument in %0: Third argument needs to be forceOverwrite, if specified</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: 3į•Šį›ŪãŪ垕数を指åŪšã™ã‚‹å ī合ãŊ "forceOverwrite" であるåŋ…čĶãŒã‚ã‚Šãūす</translation> </message> <message> <source>Invalid arguments in %0: Directories are invalid: %1 %2</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: デã‚Ģノã‚ŊãƒˆãƒŠãŒį„ĄåŠđです: %1 %2</translation> </message> <message> <source>Cannot create %0</source> <translation type="vanished">%0 ã‚’ä―œæˆã§ããūせんでした</translation> </message> <message> <source>Failed to overwrite %1</source> <translation type="vanished">%1 をä›ļきできãūせん</translation> </message> <message> <source>Cannot copy %0 to %1, error was: %3</source> <translation type="vanished">%0 を %1 ãŦã‚ģピマできãūせんでした。ã‚ĻãƒĐマ: %3</translation> </message> <message> <source>Cannot remove %0</source> <translation type="vanished">%0 を削é™Īできãūせんでした</translation> </message> <message> <source><source> <target> ["forceOverwrite"]</source> <translation><ã‚―ãƒžã‚đ> <ã‚ŋマã‚ēット> ["forceOverwrite"]</translation> </message> <message> <source>Invalid argument in %1: Third argument needs to be forceOverwrite, if specified.</source> <translation>%1 ãŦį„ĄåŠđお垕数: 3į•Šį›ŪãŪ垕数を指åŪšã™ã‚‹å ī合ãŊ "forceOverwrite" であるåŋ…čĶãŒã‚ã‚Šãūす。</translation> </message> <message> <source>Invalid argument in %1: Directory "%2" is invalid.</source> <translation>%1 ãŦį„ĄåŠđお垕数: フã‚ĐãƒŦダ "%2" ãŊį„ĄåŠđです。</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした。</translation> </message> <message> <source>Failed to overwrite "%1".</source> <translation>"%1" をä›ļきできãūせんでした。</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãŦã‚ģピマできãūせんでした: %3</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした。</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 2</source> <translation type="vanished">2個</translation> </message> <message> <source>Failed to overwrite %1</source> <translation type="vanished">%1 ãŦä›ļきできãūせん</translation> </message> <message> <source>Cannot write Desktop Entry at %1</source> <translation type="vanished">%1 ãļデã‚đã‚Ŋトップã‚Ļãƒģトナマをæ›ļきčūžã‚€ã“ãĻができãūせんでした</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 をバッã‚Ŋã‚Ēップできãūせんでした: %2</translation> </message> <message> <source>Cannot backup file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をバッã‚Ŋã‚Ēップできãūせんでした: %2</translation> </message> <message> <source>Failed to overwrite file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をä›ļきできãūせんでした。</translation> </message> <message> <source>Cannot write desktop entry to "%1".</source> <translation>"%1" ãļデã‚đã‚Ŋトップã‚Ļãƒģトナマをæ›ļきčūžã‚€ã“ãĻができãūせんでした。</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 2</source> <translation type="vanished">2個</translation> </message> <message> <source>Cannot create link from %1 to %2.</source> <translation type="vanished">%1 から %2 ãļãŪナãƒģã‚Ŋã‚’ä―œæˆã§ããūせんでした。</translation> </message> <message> <source>Cannot remove link from %1 to %2.</source> <translation type="vanished">%1 から %2 ãļãŪナãƒģã‚Ŋを削é™Īできãūせんでした。</translation> </message> <message> <source>Cannot create link from "%1" to "%2".</source> <translation>"%1" から "%2" ãļãŪナãƒģã‚Ŋã‚’ä―œæˆã§ããūせんでした。</translation> </message> <message> <source>Cannot remove link from "%1" to "%2".</source> <translation>"%1" から "%2" ãļãŪナãƒģã‚Ŋを削é™Īできãūせんでした。</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set file permissions %1!</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 ãŦã‚Ēã‚Ŋã‚ŧã‚đæĻĐ限をčĻ­åŪšã§ããūせんでした!</translation> </message> <message> <source>Cannot move file %1 to %2. Error: %3</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を %2 ãļį§ŧ動できãūせんでした。ã‚ĻãƒĐマ: %3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 2</source> <translation type="vanished">2個</translation> </message> <message> <source>Installer needs to be an offline version: %1.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒĐãŊりフãƒĐã‚Īãƒģバマã‚ļョãƒģであるåŋ…čĶãŒã‚ã‚Šãūす: %1</translation> </message> <message> <source>Cannot open file: %1</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦを開けãūせんでした: %1</translation> </message> <message> <source>Cannot read: %1. Error: %2</source> <translation type="vanished">%1 ã‚’čŠ­ãŋčūžãŋできãūせんでした。ã‚ĻãƒĐマ: %2</translation> </message> <message> <source>Cannot open file: %1. Error: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を開けãūせんでした。ã‚ĻãƒĐマ: %2</translation> </message> <message> <source>Cannot create target dir: %1.</source> <translation type="vanished">ã‚ŋマã‚ēットデã‚Ģノã‚ŊãƒˆãƒŠã‚’ä―œæˆã§ããūせんでした: %1</translation> </message> <message> <source>Cannot set permissions for file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãŦã‚Ēã‚Ŋã‚ŧã‚đæĻĐ限をčĻ­åŪšã§ããūせんでした。</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot move file "%1" to "%2": %3</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãļį§ŧ動できãūせんでした: %3</translation> </message> <message> <source>Installer at "%1" needs to be an offline one.</source> <translation>"%1" ãŪã‚Īãƒģã‚đトマãƒĐãŊりフãƒĐã‚Īãƒģバマã‚ļョãƒģであるåŋ…čĶãŒã‚ã‚Šãūす。</translation> </message> <message> <source>Cannot open file "%1" for reading.</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした。</translation> </message> <message> <source>Cannot read file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" ã‚’čŠ­ã‚ãūせんでした。</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot create target directory: "%1".</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした。</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした: %1</translation> </message> <message> <source>Removing file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™ĪしãĶいãūす。</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした。</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Removing file: %0</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦを削é™ĪしãĶいãūす: %0</translation> </message> <message> <source>Cannot remove %0.</source> <translation type="vanished">%0 を削é™Īできãūせんでした。</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation type="vanished">デã‚Ģノã‚Ŋトナ %1 を削é™Īできãūせん: %2</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 を削é™Īできãūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>2 or 3</source> <translation type="vanished">2あるいãŊ3個</translation> </message> <message> <source> (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</source> <translation type="vanished"> (りプショãƒģ: 'workingDirectory=...', 'iconPath=...', 'iconId=...') </translation> </message> <message> <source>Cannot create folder %1: %2.</source> <translation type="vanished">フã‚ĐãƒŦダ %1 ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Cannot create link %1: %2</source> <translation type="vanished">ナãƒģã‚Ŋ %1 ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation type="vanished">%1 ãŦä›ļきできãūせん: %2</translation> </message> <message> <source><target> <link location> [target arguments] ["workingDirectory=..."] ["iconPath=..."] ["iconId=..."] ["description=..."]</source> <translation><ã‚ŋマã‚ēット> <ナãƒģã‚Ŋ先> [ã‚ŋマã‚ēット垕数]["workingDirectory=..."] ["iconPath=..."] ["iconId=..."] ["description=..."]</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Failed to overwrite "%1": %2</source> <translation>"%1" をä›ļきできãūせんでした: %2</translation> </message> <message> <source>Cannot create link "%1": %2</source> <translation>ナãƒģã‚Ŋ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>キãƒĢãƒģã‚ŧãƒŦしãūした</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>ハッシãƒĨå€ĪãŪダã‚ĶãƒģロマドãŦåĪąæ•—ã—ãūした。</translation> </message> <message> <source>Download Error</source> <translation>ダã‚Ķãƒģロマドã‚ĻãƒĐマ</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>ダã‚Ķãƒģロマドäļ­ãŪハッシãƒĨå€ĪãŪį…§åˆãŦåĪąæ•—ã—ãūした。これãŊä™‚įš„ãŠã‚ĻãƒĐマですãŪで、再čĐĶčĄŒã—ãĶください。</translation> </message> <message> <source>Cannot verify Hash</source> <translation>ハッシãƒĨå€ĪãŪį…§åˆãŒã§ããūせんでした</translation> </message> <message> <source>Cannot download archive %1: %2</source> <translation>ã‚Ēマã‚Ŧã‚Īブ "%1" をダã‚Ķãƒģロマドできãūせんでした: %2</translation> </message> <message> <source>Downloading archive "%1" for component %2.</source> <translation>ã‚ģãƒģポマネãƒģトãŪã‚Ēマã‚Ŧã‚Īブ "%1" ãŪダã‚Ķãƒģロマドäļ­: %2</translation> </message> <message> <source>Scheme %1 not supported (URL: %2).</source> <translation>ã‚đキマム %1 ãŊã‚ĩポマトしãĶいãūせんURL: %2。</translation> </message> <message> <source>Cannot find component for %1.</source> <translation>ã‚ģãƒģポマネãƒģト %1 をčĶ‹ãĪけるこãĻができãūせんでした。</translation> </message> <message> <source>Cannot download archive: %1 : %2</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブ %1 をダã‚Ķãƒģロマドできãūせんでした: %2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>ã‚Ēマã‚Ŧã‚Īブを取åū—できãūせんでした: %1 %2 ãŪ芭ãŋčūžãŋäļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした</translation> </message> <message> <source>Scheme not supported: %1 (%2)</source> <translation type="vanished">こãŪã‚đキマムãŊã‚ĩポマトしãĶãūせん: %1 (%2)</translation> </message> <message> <source>Cannot find component for: %1.</source> <translation type="vanished">ã‚ģãƒģポマネãƒģト %1 をčĶ‹ãĪけるこãĻができãūせんでした。</translation> </message> <message> <source>Downloading archive '%1' for component: %2</source> <translation type="vanished">ã‚ģãƒģポマネãƒģトãŪã‚Ēマã‚Ŧã‚Īブ '%1' ãŪダã‚Ķãƒģロマドäļ­: %2</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>at least 1</source> <translation type="vanished">少おくãĻも1個</translation> </message> <message> <source>Execution failed: Cannot start detached: "%1"</source> <translation type="vanished">åŪŸčĄŒãŦåĪąæ•—ã—ãūした: "%1" をデã‚ŋッチしãĶčĩ·å‹•できãūせんでした</translation> </message> <message> <source>Execution failed: Cannot start: "%1"(%2)</source> <translation type="vanished">åŪŸčĄŒãŦåĪąæ•—ã—ãūした: "%1" をčĩ·å‹•できãūせんでした (%2)</translation> </message> <message> <source>Execution failed(Crash): "%1"</source> <translation type="vanished">åŪŸčĄŒãŦåĪąæ•—ã—ãūした(ã‚ŊãƒĐッシãƒĨ): "%1"</translation> </message> <message> <source>Execution failed(Unexpected exit code: %1): "%2"</source> <translation type="vanished">åŪŸčĄŒãŦåĪąæ•—ã—ãūした(æƒģåۚåĪ–ãŪįĩ‚䚆ã‚ģマド: %1): "%2"</translation> </message> <message> <source>Cannot start detached: "%1"</source> <translation>"%1" をデã‚ŋッチしãĶčĩ·å‹•できãūせんでした。</translation> </message> <message> <source>Cannot start: "%1": %2</source> <translation>"%1" をčĩ·å‹•できãūせんでした: %2</translation> </message> <message> <source>Program crashed: "%1"</source> <translation>プログãƒĐムãŊã‚ŊãƒĐッシãƒĨしãūした: "%1"</translation> </message> <message> <source>Execution failed (Unexpected exit code: %1): "%2"</source> <translation>åŪŸčĄŒãŦåĪąæ•—ã—ãūした(æƒģåۚåĪ–ãŪįĩ‚䚆ã‚ģマド: %1): "%2"</translation> </message> </context> <context> <name>QInstaller::EnvironmentVariableOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>2 to 4</source> <translation type="vanished">2から4個</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 2</source> <translation type="vanished">2個</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open %1 for reading: %2.</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦ %1 を開けãūせんでした: %2</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation type="vanished">'%1' ãŪåą•é–‹äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation type="vanished">%1 ãŪåą•é–‹äļ­ãŦ朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Cannot open archive "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦã‚Ēマã‚Ŧã‚Īブ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Error while extracting archive "%1": %2</source> <translation>ã‚Ēマã‚Ŧã‚Īブ "%1" ãŪåą•é–‹äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Unknown exception caught while extracting "%1".</source> <translation>"%1" ãŪåą•é–‹äļ­ãŦ朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした。</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>%1 ãŪã‚Ķã‚Ģã‚ķマドãŪåŪŒäš†</translation> </message> <message> <source>Click Done to exit the %1 Wizard.</source> <translation type="vanished">%1 ãŪã‚Ķã‚Ģã‚ķマドをįĩ‚䚆するãŦãŊ「åŪŒäš†ã€ã‚’ã‚Ŋナッã‚ŊしãĶください。</translation> </message> <message> <source>Click Finish to exit the %1 Wizard.</source> <translation type="vanished">%1 ãŪã‚Ķã‚Ģã‚ķマドをįĩ‚䚆するãŦãŊ「åŪŒäš†ã€ã‚’ã‚Ŋナッã‚ŊしãĶください。</translation> </message> <message> <source>Click %1 to exit the %2 Wizard.</source> <translation>%2 ãŪã‚Ķã‚Ģã‚ķマドをįĩ‚䚆するãŦãŊ "%1" をã‚Ŋナッã‚ŊしãĶください。</translation> </message> <message> <source>Restart</source> <translation>再čĩ·å‹•</translation> </message> <message> <source>Run %1 now.</source> <translation>%1 をåŪŸčĄŒäļ­ã€‚</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>%1 ãŪã‚Ķã‚Ģã‚ķマドãŦåĪąæ•—ã—ãūした。</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable</source> <translation type="vanished">čĻ­åŪšãŒæ›ļきčūžãŋåŊčƒ―ã§ãŊありãūせん</translation> </message> <message> <source>Failed to write settings</source> <translation type="vanished">čĻ­åۚãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>3, 4 or 5</source> <translation type="vanished">3、4あるいãŊ5個</translation> </message> <message> <source>Settings are not writable.</source> <translation>čĻ­åŪšãŒæ›ļきčūžãŋåŊčƒ―ã§ãŊありãūせん。</translation> </message> <message> <source>Failed to write settings.</source> <translation>čĻ­åۚãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした。</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>1 or 2</source> <translation type="vanished">1あるいãŊ2個</translation> </message> <message> <source> (Sourcepath, [Vendorprefix])</source> <translation type="vanished"> (ã‚―ãƒžã‚đパã‚đ, [ベãƒģダマプノフã‚Ģッã‚Ŋã‚đ])</translation> </message> <message> <source>Invalid Argument: source folder must not be empty.</source> <translation type="vanished">į„ĄåŠđお垕数: įĐšãŪフã‚ĐãƒŦãƒ€ã‚’ã‚―ãƒžã‚đãŦ指åŪšã§ããūせん。</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 をバッã‚Ŋã‚Ēップできãūせんでした: %2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation type="vanished">%1 ãŦä›ļきできãūせん: %2</translation> </message> <message> <source>Failed to copy file %1: %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ %1 ãļãŪã‚ģピマãŦåĪąæ•—ã—ãūした: %2</translation> </message> <message> <source>Cannot create folder at %1: %2</source> <translation type="vanished">%1 ãŦフã‚ĐãƒŦãƒ€ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source><source path> [vendor prefix]</source> <translation><ã‚―ãƒžã‚đパã‚đ>, [ベãƒģダマプノフã‚Ģッã‚Ŋã‚đ]</translation> </message> <message> <source>Invalid Argument: source directory must not be empty.</source> <translation>į„ĄåŠđお垕数: įĐšãŪフã‚ĐãƒŦãƒ€ã‚’ã‚―ãƒžã‚đãŦ指åŪšã§ããūせん。</translation> </message> <message> <source>Cannot backup file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をバッã‚Ŋã‚Ēップできãūせんでした: %2</translation> </message> <message> <source>Failed to overwrite "%1": %2</source> <translation>"%1" をä›ļきできãūせんでした: %2</translation> </message> <message> <source>Failed to copy file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãŪã‚ģピマãŦåĪąæ•—ã—ãūした: %2</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>ã‚ŧットã‚Ēップ - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>%1 ãŪã‚ŧットã‚Ēップã‚Ķã‚Ģã‚ķマドãļようこそ。</translation> </message> <message> <source>Add or remove components</source> <translation>ã‚ģãƒģポマネãƒģトãŪčŋ―加ãūたãŊ削é™Ī</translation> </message> <message> <source>Update components</source> <translation>ã‚ģãƒģポマネãƒģトãŪæ›ī新</translation> </message> <message> <source>Remove all components</source> <translation>すãđãĶãŪã‚ģãƒģポマネãƒģトãŪ削é™Ī</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>ナãƒĒマトãŪã‚Īãƒģã‚đトマãƒŦå…ƒã‹ã‚‰æƒ…å ąã‚’å–åū—しãĶいãūす...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>こãŪã‚Ēã‚ŊショãƒģãŪåŪŸčĄŒãŦãŊãēãĻãĪäŧĨäļŠãŪ有åŠđおナポã‚ļトナがåŋ…čĶã§ã™ã€‚</translation> </message> <message> <source>No updates available.</source> <translation>新しいæ›ī新ãŊありãūせん。</translation> </message> <message> <source> Only local package management available.</source> <translation> ロマã‚ŦãƒŦãŪãƒ‘ãƒƒã‚ąãƒžã‚ļįŪĄį†ãŪãŋåˆĐį”Ļできãūす。</translation> </message> <message> <source>Quit</source> <translation>įĩ‚䚆</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>ãƒĐã‚Īã‚ŧãƒģã‚đæĄé …ãŪ同意</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translation>Alt+A</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>äļ‹čϘãŪãƒĐã‚Īã‚ŧãƒģã‚đæĄé …ã‚’ãŠčŠ­ãŋください。朎ãƒĐã‚Īã‚ŧãƒģã‚đæĄé …ãŦ同意されおいå ī合、ã‚Īãƒģã‚đトマãƒŦをįķ™įķšã™ã‚‹ã“ãĻãŊできãūせん。</translation> </message> <message> <source>I accept the license.</source> <translation>ãƒĐã‚Īã‚ŧãƒģã‚đãŦ同意する。</translation> </message> <message> <source>I do not accept the license.</source> <translation>ãƒĐã‚Īã‚ŧãƒģã‚đãŦ同意しおい。</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>äļ‹čϘãŪãƒĐã‚Īã‚ŧãƒģã‚đæĄé …ã‚’ãŠčŠ­ãŋください。これらãŪãƒĐã‚Īã‚ŧãƒģã‚đæĄé …ãŦ同意されおいå ī合、ã‚Īãƒģã‚đトマãƒŦをįķ™įķšã™ã‚‹ã“ãĻãŊできãūせん。</translation> </message> <message> <source>I accept the licenses.</source> <translation>ãƒĐã‚Īã‚ŧãƒģã‚đãŦ同意する。</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>ãƒĐã‚Īã‚ŧãƒģã‚đãŦ同意しおい。</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translation>Alt+D</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>ã‚ģピマするãƒĐã‚Īã‚ŧãƒģã‚đãƒ•ã‚Ąã‚ĪãƒŦがčĶ‹ãĪかりãūせんでした。</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>%1 ãŪã‚Īãƒģã‚đトマãƒĐä―œæˆãŦåŋ…čĶãŠæ“ä―œãŒčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Can not write license file "%1".</source> <translation>ãƒĐã‚Īã‚ŧãƒģã‚đãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãŦæ›ļきčūžãŋできãūせん。</translation> </message> <message> <source>Can not write license file: %1.</source> <translation type="vanished">ãƒĐã‚Īã‚ŧãƒģã‚đãƒ•ã‚Ąã‚ĪãƒŦãŦæ›ļきčūžãŋできãūせん: %1</translation> </message> <message> <source>No license files found to delete.</source> <translation>削é™ĪするãƒĐã‚Īã‚ŧãƒģã‚đãƒ•ã‚Ąã‚ĪãƒŦがčĶ‹ãĪかりãūせんでした。</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 3</source> <translation type="vanished">3個</translation> </message> <message> <source>Failed to open '%1' for reading.</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦ '%1' を開くãŪãŦåĪąæ•—ã—ãūした。</translation> </message> <message> <source>Failed to open '%1' for writing.</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦ '%1' を開くãŪãŦåĪąæ•—ã—ãūした。</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source> Downloading packages...</source> <translation> ãƒ‘ãƒƒã‚ąãƒžã‚ļãŪダã‚Ķãƒģロマドäļ­...</translation> </message> <message> <source>Installation canceled by user</source> <translation type="vanished">ãƒĶマã‚ķãŦよãĢãĶã‚Īãƒģã‚đトマãƒŦがキãƒĢãƒģã‚ŧãƒŦされãūした</translation> </message> <message> <source>All downloads finished.</source> <translation>すãđãĶãŪダã‚ĶãƒģロマドがåŪŒäš†ã—ãūした。</translation> </message> <message> <source>Error</source> <translation>ã‚ĻãƒĐマ</translation> </message> <message> <source>Cancelling the Installer</source> <translation>ã‚Īãƒģã‚đトマãƒĐãŪキãƒĢãƒģã‚ŧãƒŦ</translation> </message> <message> <source>Authentication Error</source> <translation>詍čĻžã‚ĻãƒĐマ</translation> </message> <message> <source>Some components could not be removed completely because admin rights could not be acquired: %1.</source> <translation type="vanished">įŪĄį†č€…æĻĐ限が取åū—できおかãĢたため、いくãĪかãŪã‚ģãƒģポマネãƒģトをåیå…ĻãŦ削é™ĪするこãĻができãūせんでした: %1</translation> </message> <message> <source>Unknown error.</source> <translation>朊įŸĨãŪã‚ĻãƒĐマ。</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>朊įŸĨãŪã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãŸãŸã‚ã€ã„ããĪかãŪã‚ģãƒģポマネãƒģトをåیå…ĻãŦ削é™ĪするこãĻができãūせんでした。</translation> </message> <message> <source>Application not running in Package Manager mode!</source> <translation type="vanished">ã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģãŒãƒ‘ãƒƒã‚ąãƒžã‚ļマネマã‚ļãƒĢãƒĒãƒžãƒ‰ã§å‹•ä―œã—ãĶいãūせん!</translation> </message> <message> <source>No installed packages found.</source> <translation>ã‚Īãƒģã‚đトマãƒŦã•ã‚ŒãŸãƒ‘ãƒƒã‚ąãƒžã‚ļがčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Application running in Uninstaller mode!</source> <translation type="vanished">ã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģãŊã‚Ēãƒģã‚Īãƒģã‚đトマãƒĐãƒĒマドでåŪŸčĄŒäļ­ã§ã™!</translation> </message> <message> <source>invalid</source> <translation>į„ĄåŠđ</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>重čĶãŠæ›ī新がåˆĐį”ĻåŊčƒ―ã§ã™ã€‚å…ˆãŦã‚Ēップデマã‚ŋをåŪŸčĄŒã—ãĶください。</translation> </message> <message> <source>Error writing Maintenance Tool</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦæ›ļきčūžãŋäļ­ãŪã‚ĻãƒĐマ</translation> </message> <message> <source>Installation canceled by user.</source> <translation>ãƒĶマã‚ķマãŦよãĢãĶã‚Īãƒģã‚đトマãƒŦがキãƒĢãƒģã‚ŧãƒŦされãūした。</translation> </message> <message> <source>Some components could not be removed completely because administrative rights could not be acquired: %1.</source> <translation>įŪĄį†č€…æĻĐ限が取åū—できおかãĢたため、いくãĪかãŪã‚ģãƒģポマネãƒģトをåیå…ĻãŦ削é™ĪするこãĻができãūせんでした: %1</translation> </message> <message> <source>Application not running in Package Manager mode.</source> <translation>ã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģãŒãƒ‘ãƒƒã‚ąãƒžã‚ļマネマã‚ļãƒĢãƒĒãƒžãƒ‰ã§å‹•ä―œã—ãĶいãūせん。</translation> </message> <message> <source>Application running in Uninstaller mode.</source> <translation>ã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģãŊã‚Ēãƒģã‚Īãƒģã‚đトマãƒĐãƒĒマドでåŪŸčĄŒäļ­ã§ã™ã€‚</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>すãđãĶãŪäūå­˜é–Ēäŋ‚ã‚’č§Ģæąšã§ããūせん。</translation> </message> <message> <source>Components about to be removed.</source> <translation>削é™Īされるã‚ģãƒģポマネãƒģト。</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>ã‚Ēã‚Ŋã‚ŧã‚đæĻĐ限ãŪ昇栞äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした。</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Error</source> <translation>ã‚ĻãƒĐマ</translation> </message> <message> <source>Access error</source> <translation>ã‚Ēã‚Ŋã‚ŧã‚đã‚ĻãƒĐマ</translation> </message> <message> <source>Format error</source> <translation>フã‚Đママットã‚ĻãƒĐマ</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>ã‚Īãƒģã‚đトマãƒĐãŪčĻ­åŪšã‚’ %1 ãŦæ›ļきčūžã‚ãūせんでした: %2</translation> </message> <message> <source>Stop Processes</source> <translation>プロã‚ŧã‚đãŪ停æ­Ē</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>įķščĄŒã™ã‚‹ãŦãŊæŽĄãŪã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģをįĩ‚䚆しãĶください: %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>ãƒĶマã‚ķマãŦよãĢãĶã‚Īãƒģã‚đトマãƒŦがキãƒĢãƒģã‚ŧãƒŦされãūした</translation> </message> <message> <source>Cannot remove data file "%1": %2</source> <translation>デマã‚ŋãƒ•ã‚Ąã‚ĪãƒŦ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot write maintenance tool to "%1": %2</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦを "%1" ãŦæ›ļきčūžã‚ãūせんでした: %2</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>'TargetDir' åĪ‰æ•°ãŒã‚ŧットされãĶいãūせん。</translation> </message> <message> <source>Preparing the installation...</source> <translation>ã‚Īãƒģã‚đトマãƒŦãŪæš–å‚™äļ­...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>ネットãƒŊマã‚Ŋからã‚Īãƒģã‚đトマãƒŦできãūせん</translation> </message> <message> <source>Creating local repository</source> <translation>ロマã‚ŦãƒŦãŪナポã‚ļãƒˆãƒŠã‚’ä―œæˆã—ãĶいãūす</translation> </message> <message> <source> Installation finished!</source> <translation> ã‚Īãƒģã‚đトマãƒŦがåŪŒäš†ã—ãūした!</translation> </message> <message> <source> Installation aborted!</source> <translation> ã‚Īãƒģã‚đトマãƒŦがäļ­æ–­ã•れãūした!</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>ネットマãƒŊマã‚ŊäļŠã‹ã‚‰ã“ãŪæ“ä―œã‚’åŪŸčĄŒã™ã‚‹ã“ãĻãŊできãūせん</translation> </message> <message> <source>Removing deselected components...</source> <translation>éļ択č§Ģé™Īしたã‚ģãƒģポマネãƒģトを削é™Īäļ­...</translation> </message> <message> <source> Update finished!</source> <translation> ã‚ĒップデマトがåŪŒäš†ã—ãūした!</translation> </message> <message> <source> Update aborted!</source> <translation> ã‚Ēップデマトがäļ­æ–­ã•れãūした!</translation> </message> <message> <source> Installing component %1</source> <translation> ã‚ģãƒģポマネãƒģトãŪã‚Īãƒģã‚đトマãƒŦäļ­: %1</translation> </message> <message> <source>Installer Error</source> <translation>ã‚Īãƒģã‚đトマãƒĐã‚ĻãƒĐマ</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>ã‚Īãƒģã‚đトマãƒŦ(%1)äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: (%2)</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦãŪ暖備ができãūせん</translation> </message> <message> <source>Cannot start uninstall</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦを開始できãūせん</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦäļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %1</translation> </message> <message> <source>Unknown error</source> <translation>朊įŸĨãŪã‚ĻãƒĐマ</translation> </message> <message> <source>Cannot retrieve remote tree %1.</source> <translation>ナãƒĒマトãŪツナマを取åū—できãūせんでした: %1</translation> </message> <message> <source>Failure to read packages from %1.</source> <translation>"%1" ã‹ã‚‰ãƒ‘ãƒƒã‚ąãƒžã‚ļãŪ芭ãŋčūžãŋãŦåĪąæ•—ã—ãūした。</translation> </message> <message> <source>Dependency cycle between components "%1" and "%2" detected.</source> <translation>æĪœå‡šã•れたã‚ģãƒģポマネãƒģト "%1" ãĻ "%2" ãŪ間ãŦäūå­˜é–Ēäŋ‚ãŪåūŠį’°ãŒã‚りãūす。</translation> </message> <message> <source>Cannot retrieve remote tree: %1.</source> <translation type="vanished">ナãƒĒマトãŪツナマを取åū—できãūせんでした: %1</translation> </message> <message> <source>Failure to read packages from: %1.</source> <translation type="vanished">åģčĻ˜ã‹ã‚‰ãŪãƒ‘ãƒƒã‚ąãƒžã‚ļãŪ芭ãŋčūžãŋãŦåĪąæ•—ã—ãūした: %1</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>ãƒĄã‚ŋæƒ…å ąã‚’å–åū—できãūせんでした: %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>ä™‚įš„ãŠæ›īæ–°å…ƒæƒ…å ąã‚’čŋ―加できãūせんでした。</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>æ›īæ–°å…ƒæƒ…å ąãŒčĶ‹ãĪかりãūせんでした。</translation> </message> <message> <source>Unresolved dependencies</source> <translation>朊č§ĢæąšãŪäūå­˜é–Ēäŋ‚</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦãŪæ›ļきčūžãŋäļ­ã€‚</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ %1 でãŪシマã‚ŊãŦåĪąæ•—ã—ãūした: %2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦãŊバãƒģドãƒŦされãĶいãūせん</translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦãŪデマã‚ŋを %1 ãŦæ›ļきčūžã‚ãūせんでした: %2</translation> </message> <message> <source>Cannot remove data file '%1': %2</source> <translation type="vanished">デマã‚ŋãƒ•ã‚Ąã‚ĪãƒŦ '%1' を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot write maintenance tool to %1: %2</source> <translation type="vanished">ãƒĄãƒģテナãƒģã‚đツマãƒŦを %1 ãŦæ›ļきčūžã‚ãūせんでした: %2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦãŪバã‚Īナナデマã‚ŋを %1 ãŦæ›ļきčūžã‚ãūせんでした: %2</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦã‚’ä―œæˆã—ãĶいãūす</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦãŦ成功しãūした。</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦがäļ­æ–­ã•れãūした。</translation> </message> <message> <source>Dependency cycle between components detected: '%1' and '%2'.</source> <translation type="vanished">æĪœå‡šã•れたã‚ģãƒģポマネãƒģト: '%1' ãĻ '%2' ãŪ間ãŦäūå­˜é–Ēäŋ‚ãŪåūŠį’°ãŒã‚りãūす。</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>%1ãŪã‚ŧットã‚Ēップ</translation> </message> <message> <source>Maintain %1</source> <translation>%1ãŪãƒĄãƒģテナãƒģã‚đ</translation> </message> <message> <source>Question</source> <translation type="vanished">įĒščŠ</translation> </message> <message> <source>Settings</source> <translation>čĻ­åۚ</translation> </message> <message> <source>Error</source> <translation>ã‚ĻãƒĐマ</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>ネットãƒŊマã‚ŊäļŠã‹ã‚‰ãŪã‚Īãƒģã‚đトマãƒŦができãūせん。 ã‚Īãƒģã‚đトマãƒĐをロマã‚ŦãƒŦドãƒĐã‚ĪブãŦã‚ģピマしãĶください</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>ã‚Īãƒģã‚đトマãƒŦプロã‚ŧã‚đをキãƒĢãƒģã‚ŧãƒŦしãūすか</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦプロã‚ŧã‚đをキãƒĢãƒģã‚ŧãƒŦしãūすか</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>ã‚Īãƒģã‚đトマãƒĐをįĩ‚䚆しãūすか</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒĐをįĩ‚䚆しãūすか</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>ãƒĄãƒģテナãƒģã‚đツマãƒŦをįĩ‚䚆しãūすか</translation> </message> <message> <source>%1 Question</source> <translation>%1 ãŪįĒščŠ</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>čĐģįī°ã‚’čĄĻįĪšã™ã‚‹(&S)</translation> </message> <message> <source>&Hide Details</source> <translation>čĐģįī°ã‚’隠す(&H)</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>Uninstalling %1</source> <translation>%1ãŪã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦ</translation> </message> <message> <source>&Update</source> <translation>æ›ī新(&U)</translation> </message> <message> <source>Updating components of %1</source> <translation>%1ãŪã‚ģãƒģポマネãƒģトãŪæ›ī新</translation> </message> <message> <source>&Install</source> <translation>ã‚Īãƒģã‚đトマãƒŦ(&I)</translation> </message> <message> <source>Installing %1</source> <translation>%1ãŪã‚Īãƒģã‚đトマãƒŦ</translation> </message> <message> <source>U&ninstall</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦ(&N)</translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦ(&N)</translation> </message> <message> <source>Ready to Uninstall</source> <translation>ã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦãŪæš–å‚™åŪŒäš†</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>こãŪã‚ģãƒģピãƒĨマã‚ŋから %1 を削é™Īする暖備ができãūした。<br><font color="red">こãŪプログãƒĐムがã‚Īãƒģã‚đトマãƒŦされãĶいたフã‚ĐãƒŦダ %2 ãŊåیå…ĻãŦ削é™Īされãūす</font>。フã‚ĐãƒŦダãŪ内åŪđすãđãĶが削é™ĪãŪåŊūčąĄã§ã™!</translation> </message> <message> <source>U&pdate</source> <translation>æ›ī新(&P)</translation> </message> <message> <source>Ready to Update Packages</source> <translation>ãƒ‘ãƒƒã‚ąãƒžã‚ļæ›ī新ãŪæš–å‚™åŪŒäš†</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>ã‚Īãƒģã‚đトマãƒŦæļˆãŋãƒ‘ãƒƒã‚ąãƒžã‚ļをæ›ī新する暖備ができãūした。</translation> </message> <message> <source>&Install</source> <translation>ã‚Īãƒģã‚đトマãƒŦ(&I)</translation> </message> <message> <source>Ready to Install</source> <translation>ã‚Īãƒģã‚đトマãƒŦãŪæš–å‚™åŪŒäš†</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>こãŪã‚ģãƒģピãƒĨマã‚ŋãŦ %1 をã‚Īãƒģã‚đトマãƒŦする暖備ができãūした。</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation. %1 are available, while %2 are at least required.</source> <translation>デã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量がäļčķģしãĶいるため、ä™‚ãƒ•ã‚Ąã‚ĪãƒŦãŪä―œæˆãŠã‚ˆãģã‚Īãƒģã‚đトマãƒŦができãūせん。 åŋ…čĶãŠåŪđ量 %2 ãŦåŊūしãĶ、įĐšãåŪđ量ãŊ %1 です。</translation> </message> <message> <source>Not enough disk space to store all selected components! %1 are available while %2 are at least required.</source> <translation>デã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量がäļčķģしãĶいるため、éļ択されたすãđãĶãŪã‚ģãƒģポマネãƒģトをã‚Īãƒģã‚đトマãƒŦできãūせん! åŋ…čĶãŠåŪđ量 %2 ãŦåŊūしãĶ、įĐšãåŪđ量ãŊ %1 です。</translation> </message> <message> <source>Not enough disk space to store temporary files! %1 are available while %2 are at least required.</source> <translation>デã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量がäļčķģしãĶいるため、ä™‚ãƒ•ã‚Ąã‚ĪãƒŦãŒä―œæˆã§ããūせん! åŋ…čĶãŠåŪđ量 %2 ãŦåŊūしãĶ、įĐšãåŪđ量ãŊ %1 です。</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation! Available space: %1, at least required %2.</source> <translation type="vanished">デã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量がäļčķģしãĶいるため、ä™‚ãƒ•ã‚Ąã‚ĪãƒŦãŪä―œæˆãŠã‚ˆãģã‚Īãƒģã‚đトマãƒŦができãūせん! åŋ…čĶãŠåŪđ量 %2 ãŦåŊūしãĶ、įĐšãåŪđ量ãŊ %1 です。</translation> </message> <message> <source>Not enough disk space to store all selected components! Available space: %1, at least required: %2.</source> <translation type="vanished">デã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量がäļčķģしãĶいるため、éļ択されたすãđãĶãŪã‚ģãƒģポマネãƒģトをã‚Īãƒģã‚đトマãƒŦできãūせん! åŋ…čĶãŠåŪđ量 %2 ãŦåŊūしãĶ、įĐšãåŪđ量ãŊ %1 です。</translation> </message> <message> <source>Not enough disk space to store temporary files! Available space: %1, at least required: %2.</source> <translation type="vanished">デã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量がäļčķģしãĶいるため、ä™‚ãƒ•ã‚Ąã‚ĪãƒŦãŒä―œæˆã§ããūせん! åŋ…čĶãŠåŪđ量 %2 ãŦåŊūしãĶ、įĐšãåŪđ量ãŊ %1 です。</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>指åŪšã•ã‚ŒãŸãƒ‡ã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量ãŊã‚Īãƒģã‚đトマãƒŦåŊčƒ―ãŠãƒŽãƒ™ãƒŦだãĻ思われãūすが、ã‚Īãƒģã‚đトマãƒŦåūŒãŪįĐšãåŪđ量ãŊ 1% äŧĨäļ‹ãĻおるčĶ‹čūžãŋです:。 %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>指åŪšã•ã‚ŒãŸãƒ‡ã‚Ģã‚đã‚ŊãŪįĐšãåŪđ量ãŊã‚Īãƒģã‚đトマãƒŦåŊčƒ―ãŠãƒŽãƒ™ãƒŦだãĻ思われãūすが、ã‚Īãƒģã‚đトマãƒŦåūŒãŪįĐšãåŪđ量ãŊ 100 MB äŧĨäļ‹ãĻおるčĶ‹čūžãŋです:。 %1</translation> </message> <message> <source>Components about to be removed.</source> <translation type="vanished">削é™Īされるã‚ģãƒģポマネãƒģト。</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>%1 ãŪデã‚Ģã‚đã‚ŊåŪđ量をä―ŋį”Ļしãūす。</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation type="vanished">すãđãĶãŪäūå­˜é–Ēäŋ‚ã‚’č§Ģæąšã§ããūせん。</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>2 to 5</source> <translation type="vanished">2から5個</translation> </message> <message> <source><extension> <command> [description [contentType [icon]]]</source> <translation><æ‹Ąåžĩ> <ã‚ģマãƒģド> [čĐģįī° [ã‚ģãƒģテãƒģツã‚ŋã‚Īプ [ã‚Ēã‚Īã‚ģãƒģ]]]</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦå―Ē垏ãŪį™ŧéŒē: į„ĄåŠđお垕数</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦå―Ē垏ãŪį™ŧéŒēãŊ Windows でãŪãŋã‚ĩポマトしãĶいãūす。</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 3</source> <translation type="vanished">3個</translation> </message> <message> <source>Failed to open %1 for reading</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦ %1 を開くãŪãŦåĪąæ•—ã—ãūした</translation> </message> <message> <source>Failed to open %1 for writing</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦ %1 を開くãŪãŦåĪąæ•—ã—ãūした</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>%1 ãŪã‚ŧットã‚Ēップã‚Ķã‚Ģã‚ķマドをåŪŒäš†ã—ãūした</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open the requested script file at %1: %2.</source> <translation type="vanished">čĶæą‚ã•ã‚ŒãŸã‚đã‚ŊãƒŠãƒ—ãƒˆãƒ•ã‚Ąã‚ĪãƒŦ %1 を開けãūせんでした: %2</translation> </message> <message> <source>Exception while loading the component script '%1'. (%2)</source> <translation type="vanished">ã‚ģãƒģポマネãƒģトã‚đã‚Ŋナプト '%1' ãŪロマドäļ­ãŦäū‹åĪ–ãŒį™šį”Ÿã—ãūした。(%2)</translation> </message> <message> <source>Cannot open script file at %1: %2</source> <translation>%1 ãŪã‚đã‚ŊãƒŠãƒ—ãƒˆãƒ•ã‚Ąã‚ĪãƒŦを開けãūせんでした。</translation> </message> <message> <source>Exception while loading the component script "%1": %2</source> <translation>ã‚ģãƒģポマネãƒģトã‚đã‚Ŋナプト "%1" ãŪロマドäļ­ãŦäū‹åĪ–ãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Unknown error.</source> <translation>朊įŸĨãŪã‚ĻãƒĐマ。</translation> </message> <message> <source>on line number: </source> <translation>čĄŒį•Šå·:</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in '%1' operation is empty.</source> <translation type="vanished">'%1' ãŪã‚Īãƒģã‚đトマãƒĐä―œæˆãŦåŋ…čĶãŠæ“ä―œãŒčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Installer object needed in operation %1 is empty.</source> <translation>%1 ãŪã‚Īãƒģã‚đトマãƒĐä―œæˆãŦåŋ…čĶãŠæ“ä―œãŒčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>č‡Šå·ąå†čĩ·å‹•: ã‚Ēップデマã‚ŋあるいãŊãƒ‘ãƒƒã‚ąãƒžã‚ļマネマã‚ļãƒĢãƒĒマドでãŪãŋ有åŠđです。</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>č‡Šå·ąå†čĩ·å‹•: į„ĄåŠđお垕数</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) '%1' calling '%2' with arguments '%3'.</source> <translation type="vanished">'%2' を垕数 '%3' で呞ãģ凚しãūしたが、'%1' ãŪ垕数がäļčķģしãĶいãūす。</translation> </message> <message> <source>Current method argument calling '%1' with arguments '%2' is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation type="vanished">'%1' ãŪ呞ãģ凚し時ãŦ method 垕数ãŪå€ĪãĻしãĶ '%2' ãŊã‚ĩポマトされãĶいãūせん。set, remove, add_array_value, remove_array_value をä―ŋį”ĻしãĶください。</translation> </message> <message> <source>Missing argument(s) "%1" calling %2 with arguments "%3".</source> <translation>%2 を垕数 "%3" で呞ãģ凚しãūしたが、"%1" ãŪ垕数がäļčķģしãĶいãūす。</translation> </message> <message> <source>Current method argument calling "%1" with arguments "%2" is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>"%1" ãŪ呞ãģ凚し時ãŦ ãƒĄã‚―ãƒƒãƒ‰ãŪ垕数ãŪå€ĪãĻしãĶ "%2" ãŊã‚ĩポマトされãĶいãūせん。set, remove, add_array_value, remove_array_value をä―ŋį”ĻしãĶください。</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>exactly 2</source> <translation type="vanished">2個</translation> </message> <message> <source>None of the arguments can be empty: source '%1', target '%2'.</source> <translation type="vanished">ãĐãĄã‚‰ãŪ垕数もįĐšãŦãŊできãūせん: ã‚―ãƒžã‚đ '%1', ã‚ŋマã‚ēット: '%2'</translation> </message> <message> <source>Move '%1' to '%2'.</source> <translation type="vanished">'%1' を '%2' ãļį§ŧ動。</translation> </message> <message> <source>Cannot move source '%1' to target '%2', because target exists and is not removable.</source> <translation type="vanished">ã‚―ãƒžã‚đ '%1' をã‚ŋマã‚ēット '%2' ãŦį§ŧ動できãūせん。ã‚ŋマã‚ēットが存åœĻしãĶおり、かãĪ削é™Īできãūせん。</translation> </message> <message> <source>Cannot move source '%1' to target '%2': %3</source> <translation type="vanished">ã‚―ãƒžã‚đ '%1' をã‚ŋマã‚ēット '%2' ãŦį§ŧ動できãūせん: %3</translation> </message> <message> <source>None of the arguments can be empty: source "%1", target "%2".</source> <translation>ãĐãĄã‚‰ãŪ垕数もįĐšãŦãŊできãūせん: ã‚―ãƒžã‚đ "%1", ã‚ŋマã‚ēット: "%2"</translation> </message> <message> <source>Cannot move file from "%1" to "%2", because the target path exists and is not removable.</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦを "%1" から "%2" ãŦį§ŧ動できãūせん。ã‚ŋマã‚ēットが存åœĻしãĶおり、かãĪ削é™Īできãūせん。</translation> </message> <message> <source>Cannot move file "%1" to "%2": %3</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãļį§ŧ動できãūせんでした: %3</translation> </message> <message> <source>Moving file "%1" to "%2".</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" を "%2" ãļį§ŧ動しãĶいãūす。</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>ã‚đã‚ŋãƒžãƒˆãƒĄãƒ‹ãƒĨマãŪショマトã‚Ŧット</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new directory.</source> <translation>プログãƒĐムãļãŪショマトã‚Ŧãƒƒãƒˆã‚’ä―œæˆã—ãŸã„ã‚đã‚ŋãƒžãƒˆãƒĄãƒ‹ãƒĨマをéļ択しãĶください。新čĶä―œæˆã™ã‚‹ãƒ•ã‚ĐãƒŦダ名をå…Ĩ力するこãĻもできãūす。</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.</source> <translation type="vanished">プログãƒĐムãļãŪショマトã‚Ŧãƒƒãƒˆã‚’ä―œæˆã—ãŸã„ã‚đã‚ŋãƒžãƒˆãƒĄãƒ‹ãƒĨマをéļ択しãĶください。新čĶä―œæˆã™ã‚‹ãƒ•ã‚ĐãƒŦダ名をå…Ĩ力するこãĻもできãūす。</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>ã‚Īãƒģã‚đトマãƒŦ先フã‚ĐãƒŦダ</translation> </message> <message> <source>Please specify the folder where %1 will be installed.</source> <translation type="vanished">%1 をã‚Īãƒģã‚đトマãƒŦするフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translation>Alt+R</translation> </message> <message> <source>B&rowse...</source> <translation>å‚į…§(&B)...</translation> </message> <message> <source>The directory you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>éļ択されたフã‚ĐãƒŦダãŊæ—ĒãŦ存åœĻし、ã‚Īãƒģã‚đトマãƒŦæļˆãŋです。äŧ–ãŪã‚Īãƒģã‚đトマãƒŦ先をéļ択しãĶください。</translation> </message> <message> <source>You have selected an existing, non-empty directory for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this directory as installation might fail. Do you want to continue?</source> <translation>æ—Ē存ãŪįĐšã§ãŊおいフã‚ĐãƒŦダをã‚Īãƒģã‚đトマãƒŦ先ãŦéļ択しãūした。 こãŪã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģをã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦする時ãŦãŊこãŪフã‚ĐãƒŦダすãđãĶがæķˆåŽŧされるこãĻãŦæģĻæ„ã—ãĶください。 こãŪフã‚ĐãƒŦダãļãŪã‚Īãƒģã‚đトマãƒŦãŊåĪąæ•—ã™ã‚‹åŊčƒ―æ€§ã‚‚ã‚ã‚ŠæŽĻåĨĻされãūせん。 ã‚Īãƒģã‚đトマãƒŦをįķ™įķšã—ãūすか?</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid directory.</source> <translation>ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŊᜁį•Ĩできãūせん。有åŠđおフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid directory.</source> <translation>ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŪ最åūŒãŦ '.' ãŊä―ŋį”Ļできãūせん。有åŠđおフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> <message> <source>The installation path must not contain "%1", please specify a valid directory.</source> <translation>ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŦ "%1" ãŊä―ŋį”Ļできãūせん。有åŠđおフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> <message> <source>Error</source> <translation>ã‚ĻãƒĐマ</translation> </message> <message> <source>Warning</source> <translation>č­Ķ告</translation> </message> <message> <source>Please specify the directory where %1 will be installed.</source> <translation>%1 をã‚Īãƒģã‚đトマãƒŦするフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> <message> <source>Select Installation Folder</source> <translation>ã‚Īãƒģã‚đトマãƒŦ先フã‚ĐãƒŦダãŪéļ択</translation> </message> <message> <source>The folder you selected already exists and contains an installation. Choose a different target for installation.</source> <translation type="vanished">éļ択されたフã‚ĐãƒŦダãŊæ—ĒãŦ存åœĻし、ã‚Īãƒģã‚đトマãƒŦæļˆãŋです。äŧ–ãŪã‚Īãƒģã‚đトマãƒŦ先をéļ択しãĶください。</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation type="vanished">æ—Ē存ãŪįĐšã§ãŊおいフã‚ĐãƒŦダをã‚Īãƒģã‚đトマãƒŦ先ãŦéļ択しãūした。 こãŪã‚Ēãƒ—ãƒŠã‚ąãƒžã‚·ãƒ§ãƒģをã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦする時ãŦãŊこãŪフã‚ĐãƒŦダすãđãĶがæķˆåŽŧされるこãĻãŦæģĻæ„ã—ãĶください。 こãŪフã‚ĐãƒŦダãļãŪã‚Īãƒģã‚đトマãƒŦãŊåĪąæ•—ã™ã‚‹åŊčƒ―æ€§ã‚‚ã‚ã‚ŠæŽĻåĨĻされãūせん。 ã‚Īãƒģã‚đトマãƒŦをįķ™įķšã—ãūすか?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>æ—Ē存ãŪãƒ•ã‚Ąã‚ĪãƒŦあるいãŊシãƒģボナッã‚Ŋナãƒģã‚Ŋをéļ択しãūした。äŧ–ãŪã‚Īãƒģã‚đトマãƒŦ先をéļ択しãĶください。</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid folder.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŊᜁį•Ĩできãūせん。有åŠđおフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŦį›ļåŊūパã‚đãŊä―ŋį”Ļできãūせん。įĩķåŊūパã‚đで指åŪšã—ãĶください。</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŦ非ASCII文字がåŦãūれãĶいãūす。そãŪようおパã‚đãļãŪã‚Īãƒģã‚đトマãƒŦãŊã‚ĩポマトされãĶいãūせん。åˆĨãŪパã‚đをéļ択しãĶください。</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>ã‚Īãƒģã‚đトマãƒŦしたフã‚ĐãƒŦダãŊã‚Ēãƒģã‚Īãƒģã‚đトマãƒŦ時ãŦåیå…ĻãŦ削é™Īされるため、%1 ãļãŪã‚Īãƒģã‚đトマãƒŦãŊį́æ­ĒされãĶいãūす。</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>å…Ĩ力したパã‚đが長すぎãūす。有åŠđおパã‚đを指åŪšã—ãĶください。</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>å…Ĩ力したパã‚đãŊį„ĄåŠđです。有åŠđおã‚ŋマã‚ēットを指åŪšã—ãĶください。</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>å…Ĩ力したパã‚đãŊį„ĄåŠđです。有åŠđおドãƒĐã‚Īブを指åŪšã—ãĶください。</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid folder.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŪ最åūŒãŦ '.' ãŊä―ŋį”Ļできãūせん。有åŠđおフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> <message> <source>The installation path must not contain '%1', please specify a valid folder.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒŦ先ãŪパã‚đãŦ %1 ãŊä―ŋį”Ļできãūせん。有åŠđおフã‚ĐãƒŦダを指åŪšã—ãĶください。</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Missing package manager core engine.</source> <translation>ãƒ‘ãƒƒã‚ąãƒžã‚ļマネマã‚ļãƒĢãŪã‚ģã‚Ēã‚Ļãƒģã‚ļãƒģがčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Empty repository URL.</source> <translation>ナポã‚ļトナãŪ URL がįĐšã§ã™ã€‚</translation> </message> <message> <source>Download canceled.</source> <translation>ダã‚ĶãƒģロマドをキãƒĢãƒģã‚ŧãƒŦしãūした。</translation> </message> <message> <source>Timeout while testing repository "%1".</source> <translation>ナポã‚ļトナ "%1" ãŪテã‚đトäļ­ãŦã‚ŋã‚Īムã‚Ēã‚ĶãƒˆãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Cannot parse Updates.xml: %1</source> <translation>Updates.xml をč§Ģ析できãūせんでした: %1</translation> </message> <message> <source>Cannot open Updates.xml for reading: %1</source> <translation>芭ãŋčūžãŋį”ĻãŦ Updates.xml を開けãūせんでした: %1</translation> </message> <message> <source>Authentication failed.</source> <translation>詍čĻžåĪąæ•—ã€‚</translation> </message> <message> <source>Unknown error while testing repository "%1".</source> <translation>ナポã‚ļトナ "%1" をテã‚đトäļ­ãŦ朊įŸĨãŪã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>URL scheme not supported: %1 (%2).</source> <translation type="vanished">こãŪ URL ã‚đキマムãŊã‚ĩポマトしãĶãūせん: %1 (%2)</translation> </message> <message> <source>Got a timeout while testing: '%1'</source> <translation type="vanished">テã‚đトäļ­ãŦã‚ŋã‚Īムã‚Ēã‚ĶãƒˆãŒį™šį”Ÿã—ãūした: '%1'</translation> </message> <message> <source>Cannot parse Updates.xml! Error: %1.</source> <translation type="vanished">Updates.xml をč§Ģ析できãūせんでした! ã‚ĻãƒĐマ: %1</translation> </message> <message> <source>Updates.xml could not be opened for reading!</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦ Updates.xml を開けãūせんでした!</translation> </message> <message> <source>Updates.xml could not be found on server!</source> <translation type="vanished">ã‚ĩマバäļŠãŦ Updates.xml がčĶ‹ãĪかりãūせんでした!</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>詍čĻžčĶæą‚</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>sudo ãŪ詍čĻžį”ĻãŦパã‚đãƒŊマドをå…Ĩ力しãĶください:</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>įŪĄį†č€…æĻĐ限ãŪ取åū—äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦčĻ­åŪšãƒ•ã‚Ąã‚ĪãƒŦ %1 を開けãūせんでした: %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>čĻ­åۚ</translation> </message> <message> <source>Network</source> <translation>ネットãƒŊマã‚Ŋ</translation> </message> <message> <source>No proxy</source> <translation>プロキシをä―ŋį”Ļしおい</translation> </message> <message> <source>System proxy settings</source> <translation>シã‚đテムãŪプロキシčĻ­åŪšã‚’ä―ŋį”Ļする</translation> </message> <message> <source>Manual proxy configuration</source> <translation>手動でプロキシをčĻ­åŪšã™ã‚‹</translation> </message> <message> <source>HTTP proxy:</source> <translation>HTTP プロキシ:</translation> </message> <message> <source>Port:</source> <translation>ポマト:</translation> </message> <message> <source>FTP proxy:</source> <translation>FTP プロキシ:</translation> </message> <message> <source>Repositories</source> <translation>ナポã‚ļトナ</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>詍čĻžãŒåŋ…čĶãŠå ī合ãŊãƒĶマã‚ķマ名ãĻパã‚đãƒŊマドをčϘčŋ°ã—ãĶください。</translation> </message> <message> <source>Use temporary repositories only</source> <translation>ä™‚ナポã‚ļトナãŪãŋをä―ŋį”Ļする</translation> </message> <message> <source>Add</source> <translation>čŋ―加</translation> </message> <message> <source>Remove</source> <translation>削é™Ī</translation> </message> <message> <source>Test</source> <translation>テã‚đト</translation> </message> <message> <source>Show Passwords</source> <translation>パã‚đãƒŊマドをčĄĻįĪšã™ã‚‹</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>こãŪナポã‚ļトナãŪ内åŪđを取åū—するå ī合ãŊチェッã‚ŊしãĶください。</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>ã‚ĩマバでãŪ詍čĻžį”ĻãƒĶマã‚ķマ名をčŋ―加しãĶください。</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>ã‚ĩマバでãŪ詍čĻžį”Ļパã‚đãƒŊマドをčŋ―加しãĶください。</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>有åŠđおナポã‚ļトナをåŦむã‚ĩマバãŪURLです。</translation> </message> <message> <source>There was an error testing this repository.</source> <translation type="vanished">こãŪナポã‚ļトナãŪテã‚đトäļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Do you want to disable the tested repository?</source> <translation type="vanished">こãŪテã‚đトæļˆãŋナポã‚ļãƒˆãƒŠã‚’į„ĄåŠđãŦしãūすか?</translation> </message> <message> <source>An error occurred while testing this repository.</source> <translation>こãŪナポã‚ļトナをテã‚đトäļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>The repository was tested successfully.</source> <translation>ナポã‚ļトナãŪテã‚đトが成功しãūした。</translation> </message> <message> <source>Do you want to disable the repository?</source> <translation>ナポã‚ļãƒˆãƒŠã‚’į„ĄåŠđãŦしãūすか</translation> </message> <message> <source>Do you want to enable the repository?</source> <translation>ナポã‚ļトナを有åŠđãŦしãūすか</translation> </message> <message> <source>Hide Passwords</source> <translation>パã‚đãƒŊマドを隠す</translation> </message> <message> <source>Use</source> <translation>åˆĐį”Ļ</translation> </message> <message> <source>Username</source> <translation>ãƒĶマã‚ķマ名</translation> </message> <message> <source>Password</source> <translation>パã‚đãƒŊマド</translation> </message> <message> <source>Repository</source> <translation>ナポã‚ļトナ</translation> </message> <message> <source>Default repositories</source> <translation>デフã‚ĐãƒŦトナポã‚ļトナ</translation> </message> <message> <source>Temporary repositories</source> <translation>ä™‚ナポã‚ļトナ</translation> </message> <message> <source>User defined repositories</source> <translation>ãƒĶマã‚ķマåۚįūĐナポã‚ļトナ</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation>ダã‚Īã‚Ēログ</translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>プロキシ %1 ãŊ、ãƒĶマã‚ķマ名ãĻパã‚đãƒŊマドがåŋ…čĶã§ã™ã€‚</translation> </message> <message> <source>Username:</source> <translation>ãƒĶマã‚ķマ名:</translation> </message> <message> <source>Username</source> <translation>ãƒĶマã‚ķマ名</translation> </message> <message> <source>Password:</source> <translation>パã‚đãƒŊマド:</translation> </message> <message> <source>Password</source> <translation>パã‚đãƒŊマド</translation> </message> <message> <source>Proxy Credentials</source> <translation>ãƒ—ãƒ­ã‚­ã‚·čŠčĻž</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>ã‚ĩマバマãŦãŊ詍čĻžãŒåŋ…čĶã§ã™</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>こãŪã‚ĩã‚ĪトãŦã‚Ēã‚Ŋã‚ŧã‚đするãŦãŊãƒĶマã‚ķマ名ãĻパã‚đãƒŊマドがåŋ…čĶã§ã™ã€‚</translation> </message> <message> <source>Username:</source> <translation>ãƒĶマã‚ķマ名:</translation> </message> <message> <source>Password:</source> <translation>パã‚đãƒŊマド:</translation> </message> <message> <source>%1 at %2</source> <translation>%2 ãŪ %1</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>埋めčūžãūã‚ŒãŸãƒĄã‚ŋデマã‚ŋæ•°ã‚’čŠ­ãŋčūžã‚€ãŸã‚ãŦ %1 ãŦシマã‚Ŋ凚æĨãūせんでした。</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation>ãƒŠã‚―ãƒžã‚đã‚ģノã‚Ŋショãƒģã‚ŧã‚°ãƒĄãƒģãƒˆã‚’čŠ­ãŋčūžã‚€ãŸã‚ãŦ %1 ãŦシマã‚Ŋ凚æĨãūせんでした。</translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>ãƒĄã‚ŋãƒŠã‚―ãƒžã‚đãŪ䚈期しおいäļäļ€č‡ī。取åū—å€Ī %1、æƒģåۚå€Ī: %2。</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>æ“ä―œãƒ‡ãƒžã‚ŋã‚’čŠ­ãŋčūžã‚€ãŸã‚ãŦ %1 ãŦシマã‚Ŋ凚æĨãūせんでした。</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>ãƒŠã‚―ãƒžã‚đã‚ģノã‚Ŋショãƒģブロッã‚Ŋã‚’čŠ­ãŋčūžã‚€ãŸã‚ãŦ %1 ãŦシマã‚Ŋ凚æĨãūせんでした。</translation> </message> <message> <source>Cannot open meta resource %1.</source> <translation>ãƒĄã‚ŋãƒŠã‚―ãƒžã‚đ %1 を開けãūせんでした。</translation> </message> <message> <source>Cannot open meta resource. Error: %1</source> <translation type="vanished">ãƒĄã‚ŋãƒŠã‚―ãƒžã‚đを開けãūせんでした。ã‚ĻãƒĐマ: %1</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Cannot open Resource '%1' read-only.</source> <translation type="vanished">芭ãŋčūžãŋ専į”Ļã§ãƒŠã‚―ãƒžã‚đ %1 を開けãūせんでした。</translation> </message> <message> <source>Cannot open resource %1 for reading.</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒŠã‚―ãƒžã‚đ %1 を開けãūせんでした。</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>%1 バã‚ĪトãŪ芭ãŋčūžãŋåūŒãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>%1 バã‚ĪトãŪæ›ļきčūžãŋåūŒãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>ãƒŠã‚―ãƒžã‚đ %1 を開けãūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation>ã‚ŋã‚đã‚Ŋã‚Ēã‚Īãƒ†ãƒ æ•°ãŒį„ĄåŠđです。</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãļãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした: %2</translation> </message> <message> <source>Cannot open source '%1' for read. Error: %2.</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦã‚―ãƒžã‚đ '%1' を開けãūせんでした。ã‚ĻãƒĐマ: %2。</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦåŊū蹥 '%1' を開けãūせんでした。ã‚ĻãƒĐマ: %2</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <translation type="vanished">åŊū蹥 '%1' ãļãŪæ›ļきčūžãŋäļ­ãŦåĪąæ•—ã—ãūした。ã‚ĻãƒĐマ: %2</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Target '%1' not open for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation type="vanished">æ›ļきčūžãŋãŪためãŦåŊū蹥 '%1' を開けãūせんでした。ã‚ĻãƒĐマ: %2 。</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation type="vanished">åŊū蹥 '%1' ãļãŪæ›ļきčūžãŋäļ­ãŦåĪąæ•—ã—ãūした。ã‚ĻãƒĐマ: %2。</translation> </message> <message> <source>Redirect loop detected '%1'.</source> <translation type="vanished">'%1' でナダã‚Īノã‚ŊトãƒŦマプをæĪœå‡šã—ãūした。</translation> </message> <message> <source>Checksum mismatch detected '%1'.</source> <translation type="vanished">'%1' でチェッã‚Ŋã‚ĩムãŪäļäļ€č‡īをæĪœå‡šã—ãūした。</translation> </message> <message> <source>Target file "%1" already exists but is not a file.</source> <translation>åŊūčąĄãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãŊæ—ĒãŦ存åœĻしãūã™ãŒã€ãƒ•ã‚Ąã‚ĪãƒŦでãŊありãūせん。</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>File "%1" not open for writing: %2</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãļãŪæ›ļきčūžãŋãŦåĪąæ•—ã—ãūした: %2</translation> </message> <message> <source>Redirect loop detected for "%1".</source> <translation>"%1" でナダã‚Īノã‚ŊトãƒŦマプをæĪœå‡šã—ãūした。</translation> </message> <message> <source>Checksum mismatch detected for "%1".</source> <translation>"%1" でチェッã‚Ŋã‚ĩムãŪäļäļ€č‡īをæĪœå‡šã—ãūした。</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <translation>'%1' をダã‚Ķãƒģロマドäļ­ãŦ通äŋĄã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2 。</translation> </message> <message> <source>Unknown network error while downloading "%1".</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>"%1" をダã‚Ķãƒģロマドäļ­ãŦäļæ˜ŽãŠé€šäŋĄã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Network transfers canceled.</source> <translation>送äŋĄã‚’ã‚­ãƒĢãƒģã‚ŧãƒŦしãūした。</translation> </message> <message> <source>Invalid source URL "%1": %2</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>į„ĄåŠđãŠã‚―ãƒžã‚đURL "%1": %2</translation> </message> <message> <source>Unknown network error while downloading: %1.</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation type="vanished">ダã‚Ķãƒģロマドäļ­ãŦäļæ˜ŽãŠé€šäŋĄã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %1。</translation> </message> <message> <source>Pause and resume not supported by network transfers.</source> <translation>ネットãƒŊマã‚Ŋ通äŋĄãŪä™‚停æ­ĒãĻ再開ãŊįūåœĻã‚ĩポマトされãĶいãūせん。</translation> </message> <message> <source>Invalid source '%1'. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation type="vanished">į„ĄåŠđãŠã‚―ãƒžã‚đ '%1'。ã‚ĻãƒĐマ: %2。</translation> </message> <message> <source>Target file '%1' already exists but is not a file.</source> <translation type="vanished">åŊūčąĄãƒ•ã‚Ąã‚ĪãƒŦ '%1' ãŊæ—ĒãŦ存åœĻしãūã™ãŒã€ãƒ•ã‚Ąã‚ĪãƒŦでãŊありãūせん。</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation type="vanished">åŊū蹥 '%1' ãŊæ›ļきčūžãŋį”ĻãŦ開けãūせんでした。ã‚ĻãƒĐマ: %2。</translation> </message> </context> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>%2 ãŪ %1</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>プロキシãŊ詍čĻžãŒåŋ…čĶã§ã™ã€‚</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable</source> <translation type="vanished">ノã‚ļã‚đトナãŪパã‚đ %1 ãŦæ›ļきčūžãŋできãūせん</translation> </message> <message> <source>Cannot write to registry path %1</source> <translation type="vanished">ノã‚ļã‚đトナãŪパã‚đ %1 ãļæ›ļきčūžã‚ãūせんでした</translation> </message> <message> <source>Renaming %1 into %2 failed with %3.</source> <translation type="vanished">%1 から %2 ãļãŪ名前ãŪåΉæ›īが %3 でåĪąæ•—ã—ãūした。</translation> </message> <message> <source>Cannot write to registry path %1.</source> <translation>ノã‚ļã‚đトナãŪパã‚đ %1. ãļæ›ļきčūžã‚ãūせんでした。</translation> </message> <message> <source>Registry path %1 is not writable.</source> <translation>ノã‚ļã‚đトナãŪパã‚đ %1 ãŦæ›ļきčūžãŋできãūせん。</translation> </message> <message> <source>exactly %1</source> <translation>%1個</translation> </message> <message> <source>at least %1</source> <translation>少おくãĻも%1個</translation> </message> <message> <source>not more than %1</source> <translation>%1äŧĨäļ‹</translation> </message> <message> <source>%1 or %2</source> <translation>%1あるいわ%2個</translation> </message> <message> <source>%1 to %2</source> <translation>%1から%2個</translation> </message> <message numerus="yes"> <source>Invalid arguments in %1: %n arguments given, %2 arguments expected.</source> <translation> <numerusform>%1 ãŦį„ĄåŠđお垕数: %n個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です。</numerusform> </translation> </message> <message numerus="yes"> <source>Invalid arguments in %1: %n arguments given, %2 arguments expected in the form: %3.</source> <translation> <numerusform>%1 ãŦį„ĄåŠđお垕数: %n個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%3ãŪå―Ē垏で%2です。</numerusform> </translation> </message> <message> <source>Renaming file "%1" to "%2" failed: %3</source> <translation>"%1" から "%2" ãļãƒ•ã‚Ąã‚ĪãƒŦ名ãŪåΉæ›īãŦåĪąæ•—ã—ãūした: %3</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Number of arguments does not match: one is required</source> <translation type="vanished">垕数ãŪ数がäļ€č‡īしãūせん: äļ€ãĪãŪãŋ指åŪšã—ãĶください</translation> </message> <message> <source>Cannot get package manager core.</source> <translation>ãƒ‘ãƒƒã‚ąãƒžã‚ļマネマã‚ļãƒĢãŪã‚ģã‚Ēを取åū—できãūせん。</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>įķščĄŒã™ã‚‹ãŦãŊこãŪプロã‚ŧã‚đをįĩ‚䚆しãĶください: %1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>įķščĄŒã™ã‚‹ãŦãŊこれらãŪプロã‚ŧã‚đをįĩ‚䚆しãĶください: %1</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>č‡Šå‹•įš„ãŠäūå­˜é–Ēäŋ‚ãŪč§ĢæąšãŦよりčŋ―加されたã‚ģãƒģポマネãƒģト:</translation> </message> <message> <source>Components added as dependency for '%1':</source> <translation type="vanished">'%1' がäūå­˜ã—ãĶいるためãŦčŋ―加されたã‚ģãƒģポマネãƒģト:</translation> </message> <message> <source>Components added as dependency for "%1":</source> <translation>"%1" がäūå­˜ã—ãĶいるためãŦčŋ―加されたã‚ģãƒģポマネãƒģト:</translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>äūå­˜é–Ēäŋ‚ã‚’č§Ģæąšã—ãŸã‚ģãƒģポマネãƒģト:</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>éļ択されたã‚ģãƒģポマネãƒģトãŊäūå­˜é–Ēäŋ‚がありãūせん:</translation> </message> <message> <source>Recursion detected, component "%1" already added with reason: "%2"</source> <translation>再åļ°ã‚’æĪœå‡šã—ãūした、ã‚ģãƒģポマネãƒģト "%1" ãŊ、"%2" ãŪį†į”ąãŦよãĢãĶæ—ĒãŦčŋ―加されãĶいãūす</translation> </message> <message> <source>Cannot find missing dependency "%1" for "%2".</source> <translation>"%2" ãŪためãŦäļčķģしãĶいるäūå­˜é–Ēäŋ‚ "%1" をčĶ‹ãĪけるこãĻができãūせんでした。</translation> </message> <message> <source>Recursion detected, component '%1' already added with reason: '%2'</source> <translation type="vanished">再åļ°ã‚’æĪœå‡šã—ãūした、ã‚ģãƒģポマネãƒģト '%1' ãŊ、'%2' ãŪį†į”ąãŦよãĢãĶæ—ĒãŦčŋ―加されãĶいãūす</translation> </message> <message> <source>Cannot find missing dependency '%1' for '%2'.</source> <translation type="vanished">'%2' ãŪためãŦäļčķģしãĶいるäūå­˜é–Ēäŋ‚ '%1' をčĶ‹ãĪけるこãĻができãūせん。</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path exists but is not a folder: %1</source> <translation type="vanished">パã‚đが存åœĻしãĶいãūすが、フã‚ĐãƒŦダでãŊありãūせん: %1</translation> </message> <message> <source>Cannot create folder: %1</source> <translation type="vanished">フã‚ĐãƒŦãƒ€ã‚’ä―œæˆã§ããūせんでした: %1</translation> </message> <message> <source>Path "%1" exists but is not a directory.</source> <translation>パã‚đ "%1" が存åœĻしãĶいãūすが、フã‚ĐãƒŦダでãŊありãūせん。</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>フã‚ĐãƒŦダ "%1" ã‚’ä―œæˆã§ããūせんでした。</translation> </message> </context> <context> <name>QIODeviceSequentialOutStream</name> <message> <source>No device set for output stream</source> <translation type="vanished">ã‚đトナマムを凚力するデバã‚Īã‚đが指åŪšã•ã‚ŒãĶいãūせん</translation> </message> </context> <context> <name>OpenArchiveInfo</name> <message> <source>Cannot load codecs</source> <translation type="vanished">ã‚ģマデッã‚Ŋをロマドできãūせんでした</translation> </message> <message> <source>Cannot retrieve default format</source> <translation type="vanished">デフã‚ĐãƒŦトフã‚Đママットを取åū—できãūせんでした</translation> </message> <message> <source>Cannot open archive</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブを開けãūせんでした</translation> </message> <message> <source>No CArc found</source> <translation type="vanished">CArc がčĶ‹ãĪかりãūせん</translation> </message> </context> <context> <name>Lib7z</name> <message> <source>Cannot retrieve number of items in archive</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブ内ãŪã‚Ēã‚Īテム数が取åū—できãūせんでした</translation> </message> <message> <source>Cannot retrieve path of archive item %1</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブã‚Ēã‚Īテム %1 ãŪパã‚đが取åū—できãūせんでした</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした (%1)</translation> </message> <message> <source>internal code: %1</source> <translation>内éƒĻã‚ģマド: %1</translation> </message> <message> <source>not enough memory</source> <translation>ãƒĄãƒĒナがäļčķģしãĶいãūす</translation> </message> <message> <source>Error: %1</source> <translation>ã‚ĻãƒĐマ: %1</translation> </message> <message> <source>Cannot retrieve property %1 for item %2.</source> <translation>ã‚Ēã‚Īテム %2. からプロパテã‚Ģ %1 を取åū—できãūせんでした。</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3.</source> <translation>ã‚Ēã‚Īテム %2 ãŪプロパテã‚Ģ %1 ãŪ型が VT_FILETIME でãŊおく %3. ãŦおãĢãĶいãūす。</translation> </message> <message> <source>Cannot convert UTC file time to system time.</source> <translation>UTCãŪãƒ•ã‚Ąã‚ĪãƒŦãŪ時åˆŧをシã‚đテムãŪ時åˆŧãļåĪ‰æ›ã§ããūせんでした。</translation> </message> <message> <source>Cannot load codecs.</source> <translation>ã‚ģマデッã‚Ŋをロマドできãūせんでした。</translation> </message> <message> <source>Cannot open archive "%1".</source> <translation>ã‚Ēマã‚Ŧã‚Īブ "%1" を開けãūせんでした。</translation> </message> <message> <source>Cannot retrieve number of items in archive.</source> <translation>ã‚Ēマã‚Ŧã‚Īブ内ãŪã‚Ēã‚Īテム数が取åū—できãūせんでした。</translation> </message> <message> <source>Cannot retrieve path of archive item "%1".</source> <translation>ã‚Ēマã‚Ŧã‚Īブã‚Ēã‚Īテム "%1" ãŪパã‚đが取åū—できãūせんでした。</translation> </message> <message> <source>Unknown exception caught (%1).</source> <translation>朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした (%1)。</translation> </message> <message> <source>Cannot create temporary file: %1</source> <translation>ä™‚ãƒ•ã‚Ąã‚ĪãƒŦã‚’ä―œæˆã§ããūせんでした: %1</translation> </message> <message> <source>Unsupported archive type.</source> <translation>ã‚ĩポマトされãĶいおいã‚Ēマã‚Ŧã‚Īブå―Ē垏です。</translation> </message> <message> <source>Cannot create archive "%1"</source> <translation>ã‚Ēマã‚Ŧã‚Īブ "%1" ãŒä―œæˆã§ããūせんでした。</translation> </message> <message> <source>Cannot create archive "%1": %2</source> <translation>ã‚Ēマã‚Ŧã‚Īブ "%1" ãŒä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Cannot remove old archive "%1": %2</source> <translation>åĪいã‚Ēマã‚Ŧã‚Īブ "%1" を削é™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot rename temporary archive "%1" to "%2": %3</source> <translation>ä™‚ã‚Ēマã‚Ŧã‚ĪブãŪ名前を "%1" から "%2" ãļåΉæ›īできãūせんでした: %3</translation> </message> <message> <source>Cannot load codecs</source> <translation type="vanished">ã‚ģマデッã‚Ŋをロマドできãūせんでした</translation> </message> <message> <source>Cannot retrieve default format</source> <translation type="vanished">デフã‚ĐãƒŦトフã‚Đママットを取åū—できãūせんでした</translation> </message> <message> <source>Cannot create archive %1. %2</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブ %1 ãŒä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>CArc index %1 out of bounds [0, %2]</source> <translation type="vanished">CArc ãŪã‚Īãƒģデッã‚Ŋã‚đ %1 がįŊ„å›ēåĪ–ã§ã™ [0, %2]</translation> </message> <message> <source>Item index %1 out of bounds [0, %2]</source> <translation type="vanished">ã‚Ēã‚ĪテムãŪã‚Īãƒģデッã‚Ŋã‚đ %1 がįŊ„å›ēåĪ–ã§ã™ [0, %2]</translation> </message> <message> <source>Cannot create output file for writing: %1</source> <translation type="vanished">æ›ļきčūžãŋį”ĻãŦå‡šåŠ›ãƒ•ã‚Ąã‚ĪãƒŦã‚’ä―œæˆã§ããūせんでした: %1</translation> </message> <message> <source>Could not convert path: %1.</source> <translation type="vanished">パã‚đをåĪ‰æ›ã§ããūせんでした: %1</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1</source> <translation type="vanished">ã‚Ēマã‚Ŧã‚Īブã‚Ēã‚Īテム %1 ãŪパã‚đが取åū—できãūせんでした</translation> </message> <message> <source>Cannot remove already existing symlink. %1</source> <translation type="vanished">すでãŦ存åœĻするシãƒģボナッã‚Ŋナãƒģã‚ŊãŊ削é™Īできãūせん: %1</translation> </message> <message> <source>Cannot open file: %1 (%2)</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦが開けãūせん: %1 (%2)</translation> </message> <message> <source>Cannot create symlink at '%1'. Another one is already existing.</source> <translation type="vanished">'%1' ãŦシãƒģボナッã‚Ŋナãƒģã‚Ŋã‚’ä―œæˆã§ããūせんでした。äŧ–ãŪナãƒģã‚ŊがすでãŦ存åœĻしãūす。</translation> </message> <message> <source>Cannot read symlink target from file '%1'.</source> <translation type="vanished">シãƒģボナッã‚Ŋナãƒģã‚ŊãŪå‚į…§å…ˆãŪãƒ•ã‚Ąã‚ĪãƒŦ '%1' ã‚’čŠ­ãŋčūžãŋį”ĻãŦ開けãūせんでした。</translation> </message> <message> <source>Cannot create symlink at %1. %2</source> <translation type="vanished">%1 ãŦシãƒģボナッã‚Ŋナãƒģã‚Ŋã‚’ä―œæˆã§ããūせんでした。 %2</translation> </message> <message> <source>Cannot retrieve path of archive item %1.</source> <translation>ã‚Ēマã‚Ŧã‚Īブã‚Ēã‚Īテム %1. ãŪパã‚đが取åū—できãūせんでした。</translation> </message> <message> <source>Cannot remove already existing symlink %1.</source> <translation>すでãŦ存åœĻするシãƒģボナッã‚Ŋナãƒģã‚ŊãŊ削é™Īできãūせん: %1</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>æ›ļきčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> <message> <source>Cannot create symlink at "%1". Another one is already existing.</source> <translation>"%1" ãŦシãƒģボナッã‚Ŋナãƒģã‚Ŋã‚’ä―œæˆã§ããūせんでした。äŧ–ãŪナãƒģã‚ŊがすでãŦ存åœĻしãūす。</translation> </message> <message> <source>Cannot read symlink target from file "%1".</source> <translation>シãƒģボナッã‚Ŋナãƒģã‚ŊãŪå‚į…§å…ˆãŪãƒ•ã‚Ąã‚ĪãƒŦ "%1" ã‚’čŠ­ãŋčūžãŋį”ĻãŦ開けãūせんでした。</translation> </message> <message> <source>Cannot create symlink at %1: %2</source> <translation>%1 ãŦシãƒģボナッã‚Ŋナãƒģã‚Ŋã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>ãƒ‘ãƒƒã‚ąãƒžã‚ļマネマã‚ļãƒĢãŪã‚ģã‚Ēã‚Ļãƒģã‚ļãƒģがčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>Preparing meta information download...</source> <translation>ãƒĄã‚ŋæƒ…å ąãŪダã‚ĶãƒģロマドãŪæš–å‚™...</translation> </message> <message> <source>Unpacking compressed repositories. This may take a while...</source> <translation>ナポã‚ļトナをč§Ģ凍äļ­: こãŪå‡ĶᐆãŊ時間がかかるå ī合がありãūす ...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>ãƒĄã‚ŋデマã‚ŋãŪダã‚ĶãƒģロマドをキãƒĢãƒģã‚ŧãƒŦしãūした。</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>プロキシãŪ詍čĻžæƒ…å ąãŒã‚ã‚Šãūせん。</translation> </message> <message> <source>Authentication failed.</source> <translation>詍čĻžåĪąæ•—ã€‚</translation> </message> <message> <source>Unknown exception during download.</source> <translation>ダã‚Ķãƒģロマドäļ­ãŦ朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>ナãƒĒマトナポã‚ļãƒˆãƒŠã‹ã‚‰ãƒĄã‚ŋæƒ…å ąã‚’å–åū—äļ­...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>ナポã‚ļトナãŪ取åū—ãŦåĪąæ•—ã—ãūした。</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>åą•é–‹äļ­ãŦ朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Extracting meta information...</source> <translation>ãƒĄã‚ŋæƒ…å ąã‚’åą•é–‹äļ­...</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation type="vanished">'%1' ãŪåą•é–‹äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation type="vanished">%1 ãŪåą•é–‹äļ­ãŦ朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Cannot open %1 for reading. Error: %2</source> <translation type="vanished">芭ãŋčūžãŋį”ĻãŦ %1 を開けãūせんでした。ã‚ĻãƒĐマ: %2</translation> </message> <message> <source>Error while extracting archive "%1": %2</source> <translation>ã‚Ēマã‚Ŧã‚Īブ "%1" ãŪåą•é–‹äļ­ãŦã‚ĻãƒĐãƒžãŒį™šį”Ÿã—ãūした: %2</translation> </message> <message> <source>Unknown exception caught while extracting archive "%1".</source> <translation>"%1" ãŪåą•é–‹äļ­ãŦ朊įŸĨãŪäū‹åĪ–ãŒį™šį”Ÿã—ãūした。</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>芭ãŋčūžãŋį”ĻãŦãƒ•ã‚Ąã‚ĪãƒŦ "%1" を開けãūせんでした: %2</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%1 / %2</translation> </message> <message> <source>%1 received.</source> <translation>%1 受äŋĄæļˆãŋ</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/į§’)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n æ—Ĩ, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n 時間, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n 分</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n į§’</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation>- æŪ‹ã‚Šæ™‚é–“ %1%2%3%4。</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - æŪ‹ã‚Šæ™‚é–“: äļæ˜Žã€‚</translation> </message> </context> <context> <name>QtPatchOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation type="vanished">%0 ãŦį„ĄåŠđお垕数: %1個ãŪ垕数がæļĄã•れãūしたが、åŋ…čĶãŠãŪãŊ%2です%3。</translation> </message> <message> <source>3 or 4</source> <translation type="vanished">3あるいãŊ4個</translation> </message> <message> <source>Needed installer object in "%1" operation is empty.</source> <translation type="vanished">"%1" ãŪã‚Īãƒģã‚đトマãƒĐä―œæˆãŦåŋ…čĶãŠæ“ä―œãŒčĶ‹ãĪかりãūせん。</translation> </message> <message> <source>First argument should be 'linux', 'mac' or 'windows'. No other type is supported at this time.</source> <translation type="vanished">最初ãŪ垕数ãŊ 'linux', 'mac', 'windows' ãŪいずれかを指åŪšã—ãĶください。それäŧĨåĪ–ãŊã‚ĩポマトしãĶいãūせん。</translation> </message> <message> <source>Cannot find the needed QmakeOutputInstallerKey(%1) value on the installer object. The ConsumeOutput operation on the valid qmake needs to be called first.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒĐãŦåŋ…čĶãŠ QmakeOutputInstallerKey(%1) ãŪå€ĪをčĶ‹ãĪけるこãĻができãūせんでした。éĐ切お qmake で ConsumeOutput æ“ä―œã‚’æœ€åˆãŦåŪŸčĄŒã™ã‚‹åŋ…čĶãŒã‚ã‚Šãūす。</translation> </message> <message> <source>QMake from the current Qt version (%1)is not existing. Please file a bugreport with this dialog at https://bugreports.qt-project.org.</source> <translation type="vanished">įūåœĻãŪ Qt ãŪバマã‚ļョãƒģ(%1)ãŪ QMake がčĶ‹ãĪかりãūせん。こãŪダã‚Īã‚Ēログから https://bugreports.qt-project.org ãļãƒã‚°å ąå‘Šã‚’ã—ãĶください。</translation> </message> <message> <source>The output of %1 -query is not parseable. Please file a bugreport with this dialog https://bugreports.qt-project.org. output: "%2"</source> <translation type="vanished">äŧĨäļ‹ãŪ凚力がパマã‚đできãūせん。 %1 -query こãŪダã‚Īã‚Ēログから https://bugreports.qt-project.org ãļãƒã‚°å ąå‘Šã‚’ã—ãĶください。 凚力: "%2"</translation> </message> <message> <source>Qt patch error: new Qt dir(%1) needs to be less than 255 characters.</source> <translation type="vanished">Qt パッチã‚ĻãƒĐマ: 新しい Qt ãŪパã‚đ(%1) ãŊ255文字äŧĨäļ‹ã§ã‚ã‚‹åŋ…čĶãŒã‚ã‚Šãūす。</translation> </message> <message> <source>Qt patch error: Can not open %1.(%2)</source> <translation type="vanished">Qt パッチã‚ĻãƒĐマ: %1 を開けãūせん。(%2)</translation> </message> <message> <source>The installer was not able to get the unpatched path from %1.(maybe it is broken or removed) It tried to patch the Qt binaries, but all other files in Qt are unpatched. This could result in a broken Qt version. Sometimes it helps to restart the installer with a switched off antivirus software.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒĐãŊパッチ朊éĐį”Ļ時ãŪパã‚đを %1 から取åū—できãūせんでした。 (ãŠãã‚‰ããƒ•ã‚Ąã‚ĪãƒŦがåĢŠã‚ŒãĶいるか削é™ĪされãĶいãūす) Qt ãŪバã‚ĪナナãŦパッチをéĐį”ĻしようãĻしãūしたが、Qt ãŪäŧ–ãŪすãđãĶãŪãƒ•ã‚Ąã‚ĪãƒŦãŦåŊūしãĶパッチãŊéĐį”ĻされãĶいãūせん。 こãŪため、こãŪ Qt ãŊæ­ĢåļļおįŠķ態ãŦį„Ąã„åŊčƒ―æ€§ãŒã‚ã‚Šãūす。 ã‚Ēãƒģチã‚Ķã‚ĢãƒŦã‚đã‚―ãƒ•ãƒˆã‚Ķェã‚ĒをりフãŦしãĶã‚Īãƒģã‚đトマãƒĐを再čĩ·å‹•するこãĻãŦよãĢãĶæ”đ善されるかもしれãūせん。</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>詍čĻžã§ããūせんでした。</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Please start the setup program as a user with the appropriate rights. Or accept the elevation of access rights if being asked.</source> <translation>ã‚Īãƒģã‚đトマãƒŦãŪįķ™įķšãŦåŋ…čĶãŠčŠčĻžãŒã§ããūせんでした。 éĐ切おæĻĐ限を持ãĢたãƒĶマã‚ķマでã‚ŧットã‚ĒッププログãƒĐムをåŪŸčĄŒã™ã‚‹ã‹ã€ æĻĐ限昇栞をįĒščŠã•ã‚ŒãŸãĻきãŦæ‰ŋčŠã—ãĶください。</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as a user with the appropriate rights and then clicking OK.</source> <translation>ã‚Īãƒģã‚đトマãƒŦãŪįķ™įķšãŦåŋ…čĶãŠčŠčĻžãŒã§ããūせんでした。 ã‚Īãƒģã‚đトマãƒŦを「äļ­æ­Ē」するか、åˆĨãŪ手æŪĩãĻしãĶéĐ切おæĻĐ限を持ãĢたãƒĶマã‚ķマで %1 をåŪŸčĄŒã—ãŸåūŒãŦ「OK」をã‚Ŋナッã‚ŊしãĶください。</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as root and then clicking OK.</source> <translation type="vanished">ã‚Īãƒģã‚đトマãƒŦãŪįķ™įķšãŦåŋ…čĶãŠčŠčĻžãŒã§ããūせんでした。 ã‚Īãƒģã‚đトマãƒŦを「äļ­æ­Ē」するか、åˆĨãŪ手æŪĩãĻしãĶ root で %1 をåŪŸčĄŒã—ãŸåūŒãŦ「OK」をã‚Ŋナッã‚ŊしãĶください。</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>ã‚ģマãƒģドを送äŋĄã—たåūŒã€ã™ãđãĶãŪデマã‚ŋã‚’čŠ­ãŋčūžã‚ãūせんでした: %1。æƒģåŪšãƒã‚Īト数: %2 、受äŋĄãƒã‚Īト数: %3。ã‚ĻãƒĐマ: %4</translation> </message> </context> <context> <name>QInstaller::RemoteServerConnection</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation type="vanished">ã‚ģマãƒģドを送äŋĄã—たåūŒã€ã™ãđãĶãŪデマã‚ŋã‚’čŠ­ãŋčūžã‚ãūせんでした: %1。æƒģåŪšãƒã‚Īト数: %2 、受äŋĄãƒã‚Īト数: %3。ã‚ĻãƒĐマ: %4</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file '%1': %2</source> <translation type="vanished">ロッã‚Ŋãƒ•ã‚Ąã‚ĪãƒŦ '%1' ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Cannot write PID to lock file '%1': %2</source> <translation type="vanished">ロッã‚Ŋãƒ•ã‚Ąã‚ĪãƒŦ '%1' ãŦ PID をæ›ļきčūžã‚ãūせんでした: %2</translation> </message> <message> <source>Cannot obtain the lock for file '%1': %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ '%1' をロッã‚Ŋできãūせんでした: %2</translation> </message> <message> <source>Cannot release the lock for file '%1': %2</source> <translation type="vanished">ãƒ•ã‚Ąã‚ĪãƒŦ '%1' ãŪロッã‚Ŋをč§Ģé™Īできãūせんでした: %2</translation> </message> <message> <source>Cannot create lock file "%1": %2</source> <translation>ロッã‚Ŋãƒ•ã‚Ąã‚ĪãƒŦ "%1" ã‚’ä―œæˆã§ããūせんでした: %2</translation> </message> <message> <source>Cannot write PID to lock file "%1": %2</source> <translation>ロッã‚Ŋãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãŦ PID をæ›ļきčūžã‚ãūせんでした: %2</translation> </message> <message> <source>Cannot obtain the lock for file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" をロッã‚Ŋできãūせんでした: %2</translation> </message> <message> <source>Cannot release the lock for file "%1": %2</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ "%1" ãŪロッã‚Ŋをč§Ģé™Īできãūせんでした: %2</translation> </message> </context> <context> <name>LocalPackageHub</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 ãŦį„ĄåŠđおã‚ģãƒģテãƒģツがåŦãūれãĶいãūす: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>ãƒ•ã‚Ąã‚ĪãƒŦ %1 が存åœĻしãūせん。</translation> </message> <message> <source>Cannot open %1.</source> <translation>%1 を開けãūせんでした。</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>パマã‚đã‚ĻãƒĐマ %1 ãŪ%2行%3列: %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>ãƒŦマトã‚ĻãƒŽãƒĄãƒģトãŦ %1 ãŊä―ŋį”Ļできãūせん。'Packages'をä―ŋį”ĻしãĶください。</translation> </message> </context> <context> <name>InstallerBase</name> <message> <source>Waiting for %1</source> <translation>%1 をåū…æĐŸäļ­</translation> </message> <message> <source>Another %1 instance is already running. Wait until it finishes, close it, or restart your system.</source> <translation>åˆĨãŪã‚Īãƒģã‚đã‚ŋãƒģã‚đ %1 がæ—ĒãŦåŪŸčĄŒäļ­ã§ã™ã€‚įĩ‚䚆するãŪをåū…ãĪか、シã‚đテムを再čĩ·å‹•しãĶください。</translation> </message> </context> </TS> ���������������������������������������������������������������������src/sdk/translations/ifw_pl.ts����������������������������������������������������������������������0000664�0000000�0000000�00000317707�13253666515�0017032�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="pl_PL" sourcelanguage="en_US"> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>%1 w %2</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>Proxy wymaga autoryzacji.</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>Nie moÅžna przesunąć wskaÅšnika pozycji pliku do %1 w celu odczytania danych operacji.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>Nie moÅžna przesunąć wskaÅšnika pozycji pliku do %1 w celu odczytania bloku kolekcji zasobÃģw.</translation> </message> <message> <source>Cannot open meta resource. Error: %1</source> <translation>Nie moÅžna otworzyć metazasobÃģw. Błąd: %1</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>Nie moÅžna przesunąć wskaÅšnika pozycji pliku do %1 w celu odczytania ilości danych wbudowanych.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation>Nie moÅžna przesunąć wskaÅšnika pozycji pliku do %1 w celu odczytania segmentu z kolekcją zasobÃģw.</translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>Nieoczekiwane dane metazasobÃģw. Przeczytano %1, oczekiwano %2.</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>Wymagana autoryzacja HTTP</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>NaleÅžy podać nazwę uÅžytkownia i hasło aby uzystać dostęp do tej strony.</translation> </message> <message> <source>Username:</source> <translation>Nazwa uÅžytkownika:</translation> </message> <message> <source>Password:</source> <translation>Hasło:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 w %2</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path exists but is not a folder: %1</source> <translation>Isniejąca ścieÅžka %1 nie jest katalogiem</translation> </message> <message> <source>Cannot create folder: %1</source> <translation>Nie moÅžna utworzyć katalogu: %1</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Nie moÅžna odczytać ścieÅžki elementu archiwum %1</translation> </message> <message> <source>Cannot remove already existing symlink. %1</source> <translation>Nie moÅžna usunąc istniejącego dowiązania symbolicznego %1</translation> </message> <message> <source>Cannot open file: %1 (%2)</source> <translation>Nie moÅžna otworzyć pliku %1: %2</translation> </message> <message> <source>Cannot create symlink at '%1'. Another one is already existing.</source> <translation>Nie moÅžna utworzyć dowiązania symbolicznego "%1". Istnieje juÅž dowiązanie do innego pliku.</translation> </message> <message> <source>Cannot read symlink target from file '%1'.</source> <translation>Nie moÅžna odczytać docelowego pliku "%1" wynikającego z dowiązania.</translation> </message> <message> <source>Cannot create symlink at %1. %2</source> <translation>Nie moÅžna utworzyć dowiązania symbolicznego "%1": %2</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>Komponenty dodane w wyniku automatycznych zaleÅžności:</translation> </message> <message> <source>Components added as dependency for '%1':</source> <translation>Komponenty dodane w wyniku zaleÅžności dla "%1":</translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>Komponenty z rozwiązanymi zaleÅžnościami:</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>Wybrane komponenty bez zaleÅžności:</translation> </message> <message> <source>Recursion detected, component '%1' already added with reason: '%2'</source> <translation>Wykryto cykliczną zaleÅžność, komponent "%1" został uprzednio dodany z powodu: "%2"</translation> </message> <message> <source>Cannot find missing dependency '%1' for '%2'.</source> <translation>Nie moÅžna odnaleŚć zaleÅžnego komponentu "%1" dla komponentu "%2".</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>Anulowano</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file '%1': %2</source> <translation>Nie moÅžna zablokować pliku "%1": %2</translation> </message> <message> <source>Cannot write PID to lock file '%1': %2</source> <translation>Nie moÅžna zapisać PID w celu zablokowania pliku "%1": %2</translation> </message> <message> <source>Cannot obtain the lock for file '%1': %2</source> <translation>Nie moÅžna uzyskać wyłączności dostępu do pliku "%1": %2</translation> </message> <message> <source>Cannot release the lock for file '%1': %2</source> <translation>Nie moÅžna odblokować pliku "%1": %2</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Nie moÅžna utworzyć kopii zapasowej pliku %1: %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 2</source> <translation>dokładnie 2</translation> </message> <message> <source>Cannot open file '%1' for writing: %2</source> <translation>Nie moÅžna otworzyć pliku "%1" do zapisu: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Nie moÅžna odnaleŚć kopii zapasowej pliku %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Nie moÅžna przywrÃģcić kopii zapasowej pliku %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Nie moÅžna przywrÃģcić kopii zapasowej pliku %1: %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot backup file %1.</source> <translation>Nie moÅžna utworzyć kopii zapasowej pliku %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Niewłaściwe argumenty: ilość przekazanych argumentÃģw %1, oczekiwano 2.</translation> </message> <message> <source>Cannot copy a non-existent file: %1</source> <translation>Nie moÅžna skopiować nieistniejącego pliku: %1</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Nie moÅžna usunąć pliku docelowego %1: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Nie moÅžna skopiować pliku z %1 do %2: %3</translation> </message> <message> <source>Cannot delete file %1: %2</source> <translation>Nie moÅžna usunąć pliku %1: %2</translation> </message> <message> <source>Cannot restore backup file into %1: %2</source> <translation>Nie moÅžna przywrÃģcić kopii zapasowej pliku %1: %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of %1: %2</source> <translation>Nie moÅžna utworzyć kopii zapasowej pliku %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Niewłaściwe argumenty: ilość przekazanych argumentÃģw %1, oczekiwano 1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Nie moÅžna przywrÃģcić kopii zapasowej pliku %1: %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download finished.</source> <translation>Zakończono pobieranie.</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>Wartości haszu kryptograficznego nie zgadzają się.</translation> </message> <message> <source>Download canceled.</source> <translation>Anulowano pobieranie.</translation> </message> <message> <source>%1 of %2</source> <translation>%1 z %2</translation> </message> <message> <source>%1 downloaded.</source> <translation>Pobrano %1.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/sek.)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n dzień, </numerusform> <numerusform>%n dni, </numerusform> <numerusform>%n dni, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n godzina, </numerusform> <numerusform>%n godziny, </numerusform> <numerusform>%n godzin, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n minuta</numerusform> <numerusform>%n minuty</numerusform> <numerusform>%n minut</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n sekunda</numerusform> <numerusform>%n sekundy</numerusform> <numerusform>%n sekund</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - pozostało %1%2%3%4.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - nieznany czas trwania.</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1: Writing to file '%2' failed: %3</source> <translation>Nie moÅžna pobrać %1: błąd zapisu do %2: %3</translation> </message> <message> <source>Cannot download %1: Cannot create %2: %3</source> <translation>Nie moÅžna pobrać %1: błąd tworzenia %2: %3</translation> </message> <message> <source>%1 at %2</source> <translation>%1 w %2</translation> </message> <message> <source>Authentication request canceled.</source> <translation>Anulowano Şądanie autoryzacji.</translation> </message> <message> <source>Secure Connection Failed</source> <translation>Błąd bezpiecznego połączenia</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>Wystąpił błąd w trakcie łączenia z: %1.</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>MoÅže to być problem konfiguracji serwera lub osoba trzecia podszywa się pod serwer.</translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>Jeśli poprzednio moÅžliwe było połączenie się z serwerem lub jeśli jest to zaufany serwer, moÅže to być jedynie tymczasowy błąd i moÅžna ponowić prÃģbę.</translation> </message> <message> <source>Try again</source> <translation>SprÃģbuj ponownie</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open source file '%1' for reading.</source> <translation>Nie moÅžna otworzyć pliku ÅšrÃģdłowego "%1" do odczytu.</translation> </message> <message> <source>Cannot open destination file '%1' for writing.</source> <translation>Nie moÅžna otworzyć docelowego pliku "%1" do zapisu.</translation> </message> <message> <source>Writing to %1 failed: %2</source> <translation>Błąd zapisu do "%1": %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Niewłaściwe argumenty: ilość przekazanych argumentÃģw %1, oczekiwano 1.</translation> </message> <message> <source>Cannot create folder %1: Unknown error.</source> <translation>Nie moÅžna utworzyć katalogu %1: Nieznany błąd.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Nie moÅžna usunąć katalogu %1: %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file %1.</source> <translation>Nie moÅžna utworzyć kopii zapasowej pliku %1.</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Niewłaściwe argumenty: ilość przekazanych argumentÃģw %1, oczekiwano 2.</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>Nie moÅžna usunąc pliku docelowego %1: %2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Nie moÅžna skopiować pliku z %1 do %2: %3</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>Nie moÅžna skopiować %1 do %2: %3</translation> </message> <message> <source>Cannot remove file %1.</source> <translation>Nie moÅžna usunąć pliku %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Nie moÅžna przywrÃģcić kopii zapasowej pliku %1: %2</translation> </message> </context> <context> <name>KDUpdater::PackagesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>Niepoprawna zawartość %1: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>Plik %1 nie istnieje.</translation> </message> <message> <source>Cannot open %1.</source> <translation>Nie moÅžna otworzyć %1.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Błąd parsowania %1 w linii %2, w kolumnie %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>Nieoczekiwany głÃģwny element %1, oczekiwano <Packages>.</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Nie moÅžna utworzyć kopii zapasowej pliku %1: %2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>Niewłaściwe argumenty: ilość przekazanych argumentÃģw %1, oczekiwano 2.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Nie moÅžna otworzyć pliku %1 do odczytu: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Nie moÅžna otworzyć pliku %1 do zapisu: %2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>Nie moÅžna odnaleŚć kopii zapasowej pliku %1.</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>Nie moÅžna przywrÃģcić kopii zapasowej pliku %1.</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>Nie moÅžna przywrÃģcić kopii zapasowej pliku %1: %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1". Reason:</source> <translation>Nie moÅžna odczytać pliku z zasobami "%1". PowÃģd:</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>Niewłaściwe argumenty: ilość przekazanych argumentÃģw %1, oczekiwano 1.</translation> </message> <message> <source>Cannot remove folder %1: The folder does not exist.</source> <translation>Nie moÅžna usunąć katalogu %1: Katalog nie istnieje.</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Nie moÅžna usunąć katalogu %1: %2</translation> </message> <message> <source>Cannot recreate directory %1: %2</source> <translation>Nie moÅžna ponownie utworzyć katalogu %1: %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>Rozpoczęto %1</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>Nie moÅžna zatrzymać %1</translation> </message> <message> <source>Cannot stop task %1</source> <translation>Nie moÅžna zatrzymać zadania %1</translation> </message> <message> <source>%1 cannot be paused</source> <translation>Nie moÅžna wstrzymać %1</translation> </message> <message> <source>Cannot pause task %1</source> <translation>Nie moÅžna wstrzymać zadania %1</translation> </message> <message> <source>Cannot resume task %1</source> <translation>Nie moÅžna wznowić zadania %1</translation> </message> <message> <source>%1 done</source> <translation>Zakończono %1</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>Brak dostępu do informacji o pakiecie dla tej aplikacji.</translation> </message> <message> <source>Cannot access the update sources information of this application.</source> <translation>Brak dostępu do informacji o ÅšrÃģdłach aktualizacji dla tej aplikacji.</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>Znaleziono %n uaktualnienie.</numerusform> <numerusform>Znaleziono %n uaktualnienia.</numerusform> <numerusform>Znaleziono %n uaktualnień.</numerusform> </translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>Pobieranie Updates.xml ze ÅšrÃģdeł uaktualnień.</translation> </message> <message> <source>Cannot download update source %1 from ('%2')</source> <translation>Nie moÅžna pobrać ÅšrÃģdła uaktualnienia %1 z ("%2")</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>Plikii Updates.xml pobrane ze ÅšrÃģdeł uaktualnień.</translation> </message> <message> <source>Computing applicable updates.</source> <translation>Sporządzanie listy aktualizacji.</translation> </message> <message> <source>Application updates computed.</source> <translation>Sporządzono listę aktualizacji.</translation> </message> </context> <context> <name>KDUpdater::UpdateSourcesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>Niepoprawna zawartość %1: %2</translation> </message> <message> <source>Cannot read "%1"</source> <translation>Nie moÅžna odczytać "%1"</translation> </message> <message> <source>XML Parse error in %1 at %2, %3: %4</source> <translation>Błąd parsowania XML w pliku %1, w linii %2, w kolumnie %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "UpdateSources"</source> <translation>Nieoczekiwany głÃģwny element %1, oczekiwano <UpdateSources></translation> </message> <message> <source>Cannot save changes to "%1": %2</source> <translation>Nie moÅžna zachować zmian w "%1": %2</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Niepoprawna zawartość Updates.xml: %1</translation> </message> <message> <source>Cannot read "%1"</source> <translation>Nie moÅžna odczytać "%1"</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>Błąd parsowania %1 w linii %2, w kolumnie %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>Nieoczekiwany głÃģwny element %1, oczekiwano <Updates>.</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>Brak elementu <ApplicationName>.</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>Brak elementu <ApplicationVersion>.</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>Brak <Name> w elemencie <PackageUpdate></translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>Brak <Version> w elemencie <PackageUpdate></translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>Brak <ReleaseDate> w elemencie <PackageUpdate></translation> </message> </context> <context> <name>Lib7z</name> <message> <source>Cannot retrieve number of items in archive</source> <translation>Nie moÅžna odczytać liczby elementÃģw w archiwum</translation> </message> <message> <source>Cannot retrieve path of archive item %1</source> <translation>Nie moÅžna odczytać ścieÅžki elementu archiwum %1</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Złapano nieznany wyjątek (%1)</translation> </message> <message> <source>internal code: %1</source> <translation>kod wewnętrzny: %1</translation> </message> <message> <source>not enough memory</source> <translation>brak pamięci</translation> </message> <message> <source>Error: %1</source> <translation>Błąd: %1</translation> </message> <message> <source>Cannot load codecs</source> <translation>Nie moÅžna załadować kodekÃģw</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Nie moÅžna odczytać domyślnego formatu</translation> </message> <message> <source>Cannot create archive %1. %2</source> <translation>Nie moÅžna utworzyć archiwum %1. %2</translation> </message> <message> <source>CArc index %1 out of bounds [0, %2]</source> <translation>Indeks CArc %1 poza zakresem [0, %2]</translation> </message> <message> <source>Item index %1 out of bounds [0, %2]</source> <translation>Indeks elementu %1 poza zakresem [0, %2]</translation> </message> <message> <source>Cannot create output file for writing: %1</source> <translation>Nie moÅžna otworzyć pliku wyjściowego do zapisu: %1</translation> </message> </context> <context> <name>Lib7z::ExtractItemJob</name> <message> <source>Cannot list archive: QIODevice not set or already destroyed.</source> <translation>Nie moÅžna uzyskać listy zawartości archiwum: nie ustawiono QIODevice lub został on juÅž zlikwidowany.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Błąd rozpakowywania "%1": %2</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Złapano nieznany wyjątek (%1)</translation> </message> <message> <source>Failed</source> <translation>Nie powiodło się</translation> </message> </context> <context> <name>Lib7z::ListArchiveJob</name> <message> <source>Cannot list archive: QIODevice already destroyed.</source> <translation>Nie moÅžna uzyskać listy zawartości archiwum: QIODevice został juÅž zlikwidowany.</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>Złapano nieznany wyjątek (%1)</translation> </message> <message> <source>Failed</source> <translation>Nie powiodło się</translation> </message> </context> <context> <name>OpenArchiveInfo</name> <message> <source>Cannot load codecs</source> <translation>Nie moÅžna załadować kodekÃģw</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>Nie moÅžna odczytać domyślnego formatu</translation> </message> <message> <source>Cannot open archive</source> <translation>Nie moÅžna otworzyć archiwum</translation> </message> <message> <source>No CArc found</source> <translation>Brak CArc</translation> </message> </context> <context> <name>QIODeviceSequentialOutStream</name> <message> <source>No device set for output stream</source> <translation>Nie ustawiono urządzenia dla strumienia wyjściowego</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>No marker found, stopped after %1.</source> <translation>Nie odnaleziono znacznika, zatrzymano po %1.</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>Nie moÅžna otworzyć pliku %1 do odczytu: %2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>Nie moÅžna otworzyć pliku %1 do zapisu: %2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>Błąd odczytu po %1 bajtach: %2</translation> </message> <message> <source>Copy failed. Error: %1</source> <translation>Błąd kopiowania: %1</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Błąd zapisu po %1 bajtach: %2</translation> </message> <message> <source>bytes</source> <translation>bajtÃģw</translation> </message> <message> <source>KiB</source> <translation>KiB</translation> </message> <message> <source>MiB</source> <translation>MiB</translation> </message> <message> <source>GiB</source> <translation>GiB</translation> </message> <message> <source>TiB</source> <translation>TiB</translation> </message> <message> <source>PiB</source> <translation>PiB</translation> </message> <message> <source>EiB</source> <translation>EiB</translation> </message> <message> <source>ZiB</source> <translation>ZiB</translation> </message> <message> <source>YiB</source> <translation>YiB</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Nie moÅžna usunąć pliku %1: %2</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>Nie moÅžna usunąć katalogu %1: %2</translation> </message> <message> <source>Cannot create folder %1</source> <translation>Nie moÅžna utworzyć katalogu %1</translation> </message> <message> <source>Cannot copy file from %1 to %2: %3</source> <translation>Nie moÅžna skopiować pliku z %1 do %2: %3</translation> </message> <message> <source>Cannot move file from %1 to %2: %3</source> <translation>Nie moÅžna przenieść pliku z %1 do %2: %3</translation> </message> <message> <source>Cannot create folder %1: %2</source> <translation>Nie moÅžna utworzyć katalogu %1: %2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>Nie moÅžna otworzyć pliku tymczasowego: %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>Nie moÅžna otworzyć pliku tymczasowego dla szablonu %1: %2</translation> </message> <message> <source>Cannot create temporary file</source> <translation>Nie moÅžna utworzyć pliku tymczasowego</translation> </message> <message> <source>Cannot retrieve property %1 for item %2</source> <translation>Nie moÅžna pobrać właściwości %1 z elementu %2</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3</source> <translation>Właściwość %1 elementu %2 nie jest typu VT_FILETIME, tylko %3</translation> </message> <message> <source>Cannot convert file time to local time</source> <translation>Nie moÅžna skonwertować czasu zapisu pliku do czasu lokalnego</translation> </message> <message> <source>Cannot convert local file time to system time</source> <translation>Nie moÅžna skonwertować lokalnego czasu do czasu systemowego</translation> </message> <message> <source>Corrupt installation</source> <translation>Instalacja uszkodzona</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>Instalacja wygląda na uszkodzoną. Zaleca się ponowną instalację.</translation> </message> <message> <source>The specified module could not be found.</source> <translation>Nie moÅžna odnaleÅšc podanego modułu.</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Components cannot have children in updater mode.</source> <translation>Komponenty nie mogą posiadać dzieci w trybie aktualizacji.</translation> </message> <message> <source>Cannot open the requested translation file '%1'.</source> <translation>Nie moÅžna otworzyć wymaganego pliku z tłumaczeniami "%1".</translation> </message> <message> <source>Cannot open the requested UI file '%1'. Error: %2</source> <translation>Nie moÅžna otworzyć wymaganego pliku UI "%1". Błąd: %2</translation> </message> <message> <source>Cannot load the requested UI file '%1'. Error: %2</source> <translation>Nie moÅžna załadować wymaganego pliku UI "%1". Błąd: %2</translation> </message> <message> <source>Cannot open the requested license file '%1'. Error: %2</source> <translation>Nie moÅžna otworzyć wymaganego pliku z licencją "%1". Błąd: %2</translation> </message> <message> <source>Error</source> <translation>Błąd</translation> </message> <message> <source>Error: Operation %1 does not exist</source> <translation>Błąd: operacja %1 nie istnieje</translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>Nie moÅžna rozwiązać "isDefault" w %1</translation> </message> <message> <source>Update Info: </source> <translation>Informacja o aktualizacji: </translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component is marked for installation.</source> <translation>Komponent wybrany do zainstalowania.</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>Komponent wybrany do dezinstalacji.</translation> </message> <message> <source>Component is installed.</source> <translation>Komponent zainstalowany.</translation> </message> <message> <source>Component is not installed.</source> <translation>Komponent niezainstalowany.</translation> </message> <message> <source>Component Name</source> <translation>Nazwa komponentu</translation> </message> <message> <source>Action</source> <translation>Akcja</translation> </message> <message> <source>Installed Version</source> <translation>Zainstalowana wersja</translation> </message> <message> <source>New Version</source> <translation>Nowa wersja</translation> </message> <message> <source>Release Date</source> <translation>Data wydania</translation> </message> <message> <source>Size</source> <translation>Rozmiar</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translation>Alt+A</translation> </message> <message> <source>Def&ault</source> <translation>D&omyślne</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translation>Alt+R</translation> </message> <message> <source>&Reset</source> <translation>Z&resetuj</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translation>Alt+S</translation> </message> <message> <source>&Select All</source> <translation>Zaznacz w&szystkie</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translation>Alt+D</translation> </message> <message> <source>&Deselect All</source> <translation>O&dznacz wszystkie</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>Ten komponent zajmie około %1 miejsca na twardym dysku.</translation> </message> <message> <source>Select Components</source> <translation>Zaznacz komponenty</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>Zaznacz komponenty do uaktualnienia.</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>Zaznacz komponenty do zainstalowania.</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>Zaznacz komponenty do dezinstalacji.</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them.</source> <translation>Zaznacz komponenty do instalacji. Odznacz zainstalowane komponenty do dezinstalacji.</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>at least 2</source> <translation>przynajmniej 2</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Wymagany obiekt instalacji %1 jest pusty.</translation> </message> <message> <source>Can not save the output of %1 to an empty installer key value.</source> <translation>Nie moÅžna zapisać wyniku %1 do pustej wartości klucza installera.</translation> </message> <message> <source>File '%1' does not exist or is not an executable binary.</source> <translation>Plik "%1" nie istnieje lub nie jest plikiem wykonywalnym.</translation> </message> <message> <source>Running '%1' resulted in a crash.</source> <translation>Uruchomienie "%1" zakończone błędem.</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>2 or 3</source> <translation>2 lub 3</translation> </message> <message> <source> (<source> <target> [forceOverwrite])</source> <translation type="unfinished"></translation> </message> <message> <source>Invalid argument in %0: Third argument needs to be forceOverwrite, if specified</source> <translation>Niewłaściwe argumenty w %0: JeÅželi trzeci argument jest podany, moÅže to być tylko "forceOverwrite"</translation> </message> <message> <source>Invalid arguments in %0: Directories are invalid: %1 %2</source> <translation>Niewłaściwe argumenty w %0: katalogi są niewłaściwe: %1, %2</translation> </message> <message> <source>Cannot create %0</source> <translation>Nie moÅžna utworzyć %0</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Nie moÅžna nadpisać %1</translation> </message> <message> <source>Cannot copy %0 to %1, error was: %3</source> <translation>Nie moÅžna skopiować pliku z %0 do %1: %3</translation> </message> <message> <source>Cannot remove %0</source> <translation>Nie moÅžna usunąć %0</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation>Niepoprawna ilość zadań.</translation> </message> <message> <source>Cannot open source '%1' for read. Error: %2.</source> <translation>Nie moÅžna otworzyć ÅšrÃģdła "%1" do odczytu. Błąd: %2.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <translation>Nie moÅžna otworzyć "%1" do zapisu. Błąd: %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <translation>Błąd zapisu pliku docelowego "%1": %2.</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>Nie moÅžna utworzyć kopii zapasowej pliku %1: %2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 2</source> <translation>dokładnie 2</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>Nie moÅžna nadpisać %1</translation> </message> <message> <source>Cannot write Desktop Entry at %1</source> <translation>Nie moÅžna zapisać Desktop Entry w %1</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 2</source> <translation>dokładnie 2</translation> </message> <message> <source>Cannot create link from %1 to %2.</source> <translation>Nie moÅžna utworzyć dowiązania z %1 do %2.</translation> </message> <message> <source>Cannot remove link from %1 to %2.</source> <translation>Nie moÅžna usunąć dowiązania z %1 do %2.</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set file permissions %1!</source> <translation>Nie moÅžna ustawić praw dostępu %1.</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>Nie moÅžna usunąć pliku %1: %2</translation> </message> <message> <source>Cannot move file %1 to %2. Error: %3</source> <translation>Nie moÅžna przenieść pliku z %1 do %2: %3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 2</source> <translation>dokładnie 2</translation> </message> <message> <source>Installer needs to be an offline version: %1.</source> <translation>Instalator musi być w wersji offline: %1.</translation> </message> <message> <source>Cannot open file: %1</source> <translation>Nie moÅžna otworzyć pliku %1</translation> </message> <message> <source>Cannot read: %1. Error: %2</source> <translation>Błąd odczytu %1: %2</translation> </message> <message> <source>Cannot open file: %1. Error: %2</source> <translation>Nie moÅžna otworzyć pliku %1: %2</translation> </message> <message> <source>Cannot create target dir: %1.</source> <translation>Nie moÅžna utworzyć katalogu docelowego %1.</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>Złapano nieznany wyjątek: %1.</translation> </message> <message> <source>Removing file: %0</source> <translation>Usuwanie pliku %0</translation> </message> <message> <source>Cannot remove %0.</source> <translation>Nie moÅžna usunąć %0.</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>Nie moÅžna usunąć katalogu %1: %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>2 or 3</source> <translation>2 lub 3</translation> </message> <message> <source> (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</source> <translation> (opcjonalnie: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</translation> </message> <message> <source>Cannot create folder %1: %2.</source> <translation>Nie moÅžna utworzyć katalogu %1: %2.</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>Nie moÅžna nadpisać %1: %2</translation> </message> <message> <source>Cannot create link %1: %2</source> <translation>Nie moÅžna utworzyć dowiązania %1: %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>Anulowano</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>Nie moÅžna pobrać sygnatury hash.</translation> </message> <message> <source>Download Error</source> <translation>Błąd pobierania</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>Weryfikacja hasha podczas pobierania nie powiodła się. Jest to tymczasowy błąd, sprÃģbuj ponownie.</translation> </message> <message> <source>Cannot verify Hash</source> <translation>Nie moÅžna zweryfikować hasha</translation> </message> <message> <source>Cannot download archive: %1 : %2</source> <translation>Nie moÅžna pobrać archiwum %1: %2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>Nie moÅžna pobrać archiwÃģw: %1 Błąd podczas ładowania %2</translation> </message> <message> <source>Downloading archive '%1' for component: %2</source> <translation>Pobieranie archiwum "%1" dla komponentu %2</translation> </message> <message> <source>Scheme not supported: %1 (%2)</source> <translation>Nieobsługiwany schemat %1 (%2)</translation> </message> <message> <source>Cannot find component for: %1.</source> <translation>Brak komponentu dla %1.</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Target '%1' not open for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Nie moÅžna otworzyć pliku docelowego "%1" do odczytu. Błąd: %2.</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>Błąd zapisu pliku docelowego "%1": %2.</translation> </message> <message> <source>Redirect loop detected '%1'.</source> <translation>Wykryto zapętlenie "%1".</translation> </message> <message> <source>Checksum mismatch detected '%1'.</source> <translation>Wykryto niezgodność sumy kontrolnej "%1".</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Błąd sieci podczas pobierania "%1": %2.</translation> </message> <message> <source>Unknown network error while downloading: %1.</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>Nieznany błąd sieci podczas pobierania: %1.</translation> </message> <message> <source>Pause and resume not supported by network transfers.</source> <translation>Wstrzymanie i wznowienie nie są obsługiwane przez transfery sieciowe.</translation> </message> <message> <source>Invalid source '%1'. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Niepoprawne ÅšrÃģdło "%1". Błąd: %2.</translation> </message> <message> <source>Target file '%1' already exists but is not a file.</source> <translation>ŚcieÅžka docelowa "%1" juÅž istnieje, lecz nie jest ona plikiem.</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>Nie moÅžna otworzyć pliku docelowego "%1" do zapisu. Błąd: %2.</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>at least 1</source> <translation>przynajmniej 1</translation> </message> <message> <source>Execution failed: Cannot start detached: "%1"</source> <translation>Błąd wykonywania. Nie moÅžna odrębnie uruchomić "%1"</translation> </message> <message> <source>Execution failed: Cannot start: "%1"(%2)</source> <translation>Błąd wykonywania. Nie moÅžna uruchomić "%1": %2</translation> </message> <message> <source>Execution failed(Crash): "%1"</source> <translation>Błąd wykonywania "%1"</translation> </message> <message> <source>Execution failed(Unexpected exit code: %1): "%2"</source> <translation>Błąd wykonywania "%2" (nieoczekiwany kod wyjściowy: %1)</translation> </message> </context> <context> <name>QInstaller::EnvironmentVariableOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>2 to 4</source> <translation>od 2 do 4</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 2</source> <translation>dokładnie 2</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open %1 for reading: %2.</source> <translation>Nie moÅžna otworzyć pliku %1 do odczytu: %2.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Błąd rozpakowywania "%1": %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Złapano nieznany wyjątek podczas rozpakowywania %1.</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Number of arguments does not match: one is required</source> <translation>Nieoczekiwana liczba argumentÃģw, wymagany jest tylko jeden</translation> </message> <message> <source>Cannot get package manager core.</source> <translation>Brak dostępu do "package manager core".</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>Proces "%1" powinien zostać zatrzymany przed kontynuowaniem</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>Procesy "%1" powinny zostać zatrzymane przed kontynuowaniem</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%1 z %2</translation> </message> <message> <source>%1 received.</source> <translation>Otrzymano %1.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/sek.)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n dzień, </numerusform> <numerusform>%n dni, </numerusform> <numerusform>%n dni, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n godzina, </numerusform> <numerusform>%n godziny, </numerusform> <numerusform>%n godzin, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n minuta</numerusform> <numerusform>%n minuty</numerusform> <numerusform>%n minut</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n sekunda</numerusform> <numerusform>%n sekundy</numerusform> <numerusform>%n sekund</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - pozostało %1%2%3%4.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - nieznany czas trwania.</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>Zakończenie kreatora %1</translation> </message> <message> <source>Click Done to exit the %1 Wizard.</source> <translation>Naciśnij "Zrobione" aby opuścić kreatora %1.</translation> </message> <message> <source>Click Finish to exit the %1 Wizard.</source> <translation>Naciśnij "Zakończ" aby opuścić kreatora %1.</translation> </message> <message> <source>Restart</source> <translation>Zrestartuj</translation> </message> <message> <source>Run %1 now.</source> <translation>Uruchom %1 teraz.</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>Błąd kreatora %1.</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable</source> <translation>Nie moÅžna zapisać ustawień</translation> </message> <message> <source>Failed to write settings</source> <translation>Błąd zapisu ustawień</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>3, 4 or 5</source> <translation>3, 4 lub 5</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>1 or 2</source> <translation>1 lub 2</translation> </message> <message> <source> (Sourcepath, [Vendorprefix])</source> <translation> (Sourcepath, [Vendorprefix])</translation> </message> <message> <source>Invalid Argument: source folder must not be empty.</source> <translation>Niepoprawny argument: nazwa katalogu ÅšrÃģdłowego nie moÅže być pusta.</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation>Nie moÅžna utworzyć kopii zapasowej pliku %1: %2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>Nie moÅžna nadpisać %1: %2</translation> </message> <message> <source>Failed to copy file %1: %2</source> <translation>Nie moÅžna nadpisać %1: %2</translation> </message> <message> <source>Cannot create folder at %1: %2</source> <translation>Nie moÅžna utworzyć katalogu %1: %2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>Ustawienia - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>Kreator ustawień %1.</translation> </message> <message> <source>Add or remove components</source> <translation>Dodaj lub usuń komponenty</translation> </message> <message> <source>Update components</source> <translation>Uaktualnij komponenty</translation> </message> <message> <source>Remove all components</source> <translation>Usuń wszystkie komponenty</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>Otrzymywanie informacji ze zdalnych ÅšrÃģdeł instalacji...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>Wymagane jest przynajmniej jedno poprawne i dostępne repozytorium.</translation> </message> <message> <source>No updates available.</source> <translation>Brak dostępnych uaktualnień.</translation> </message> <message> <source> Only local package management available.</source> <translation>MoÅžliwe jest tylko lokalne zarządzanie pakietami.</translation> </message> <message> <source>Quit</source> <translation>Zakończ</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>Umowa licencyjna</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translation>Alt+A</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translation>Alt+D</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>Proszę dokładnie przeczytać poniÅžsze warunki licencji. Instalacja, bez akceptacji licencji, nie jest moÅžliwa.</translation> </message> <message> <source>I accept the license.</source> <translation>Akceptuję licencję.</translation> </message> <message> <source>I do not accept the license.</source> <translation>Nie akceptuję licencji.</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>Proszę dokładnie przeczytać poniÅžsze warunki licencji. Instalacja, bez akceptacji licencji, nie jest moÅžliwa.</translation> </message> <message> <source>I accept the licenses.</source> <translation>Akceptuję licencje.</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>Nie akceptuję licencji.</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>Brak plikÃģw z licencją do skopiowania.</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>Wymagany obiekt instalacji %1 jest pusty.</translation> </message> <message> <source>Can not write license file: %1.</source> <translation>Nie moÅžna zapisać pliku z licencją %1.</translation> </message> <message> <source>No license files found to delete.</source> <translation>Brak plikÃģw z licencją do usunięcia.</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 3</source> <translation>dokładnie 3</translation> </message> <message> <source>Failed to open '%1' for reading.</source> <translation>Nie moÅžna otworzyć %1 do odczytu.</translation> </message> <message> <source>Failed to open '%1' for writing.</source> <translation>Nie moÅžna otworzyć %1 do zapisu.</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>Brak silnika "package manager core".</translation> </message> <message> <source>Preparing meta information download...</source> <translation>Przygotowywanie pobrania metainformacji...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>Anulowano pobieranie metadanych.</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>Brak list uwierzytelniających dla proxy.</translation> </message> <message> <source>Authentication failed.</source> <translation>Błąd autoryzacji.</translation> </message> <message> <source>Unknown exception during download.</source> <translation>Nieznany błąd pobierania.</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>Pobieranie metainformacji ze zdalnego repozytorium...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>Błąd pobierania repozytoriÃģw.</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>Nieznany wyjątek podczas rozpakowywania.</translation> </message> <message> <source>Extracting meta information...</source> <translation>Rozpakowywanie metainformacji...</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>Błąd rozpakowywania "%1": %2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>Złapano nieznany wyjątek podczas rozpakowywania %1.</translation> </message> <message> <source>Cannot open %1 for reading. Error: %2</source> <translation>Nie moÅžna otworzyć pliku %1 do odczytu: %2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source>Error writing Maintenance Tool</source> <translation>Błąd przy zapisie narzędzia konserwacji</translation> </message> <message> <source> Downloading packages...</source> <translation> Pobieranie pakietÃģw...</translation> </message> <message> <source>Installation canceled by user</source> <translation>Instalacja anulowana przez uÅžytkownika</translation> </message> <message> <source>All downloads finished.</source> <translation>Zakończono pobieranie.</translation> </message> <message> <source>Cancelling the Installer</source> <translation>Anulowanie instalacji</translation> </message> <message> <source>Authentication Error</source> <translation>Błąd autoryzacji</translation> </message> <message> <source>Some components could not be removed completely because admin rights could not be acquired: %1.</source> <translation>NiektÃģre komponenty nie zostały całkowicie usunięte z powodu braku wymaganych uprawnień administratora: %1.</translation> </message> <message> <source>Unknown error.</source> <translation>Nieznany błąd.</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>NiektÃģre komponenty nie zostały całkowicie usunięte, poniewaÅž wystąpił nieznany błąd.</translation> </message> <message> <source>Application not running in Package Manager mode!</source> <translation>Aplikacja nie jest uruchomiona w trybie "Package Manager".</translation> </message> <message> <source>No installed packages found.</source> <translation>Brak zainstalowanych pakietÃģw.</translation> </message> <message> <source>Application running in Uninstaller mode!</source> <translation>Aplikacja uruchomiona w trybie "Uninstaller".</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>Dostępne jest waÅžne uaktualnienie, ktÃģre naleÅžy zainstalować w pierwszej kolejności.</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>Błąd ustalania praw dostępu.</translation> </message> <message> <source>Error</source> <translation>Błąd</translation> </message> <message> <source>invalid</source> <translation>Niepoprawny</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Unresolved dependencies</source> <translation>Nierozwiązane zaleÅžności</translation> </message> <message> <source>Error</source> <translation>Błąd</translation> </message> <message> <source>Access error</source> <translation>Błąd dostępu</translation> </message> <message> <source>Format error</source> <translation>Błędny format</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>Nie moÅžna zapisać konfiguracji instalatora do %1: %2</translation> </message> <message> <source>Stop Processes</source> <translation>Zatrzymaj przetwarzanie</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>Aby kontynuować, następujące procesy powinny zostać zatrzymane: %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>Instalacja anulowana przez uÅžytkownika</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>Zapisywanie narzędzia konserwacji.</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>Nie moÅžna przesunąć wskaÅšnika pozycji pliku %1: %2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation>Narzędzie konserwacji nie jest pakietem</translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>Nie moÅžna zapisać danych narzędzia konserwacji do %1: %2</translation> </message> <message> <source>Cannot remove data file '%1': %2</source> <translation>Nie moÅžna usunąć pliku z danymi "%1": %2</translation> </message> <message> <source>Cannot write maintenance tool to %1: %2</source> <translation>Nie moÅžna zapisać narzędzia konserwacji do %1: %2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>Nie moÅžna zapisać binarnych danych narzędzia konserwacji do %1: %2</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>Zmienna "TargetDir" nie została ustawiona.</translation> </message> <message> <source>Preparing the installation...</source> <translation>Przygotowywanie instalacji...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>Instalacja z sieci nie jest moÅžliwa</translation> </message> <message> <source>Creating local repository</source> <translation>Tworzenie lokalnego repozytorium</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>Tworzenie narzędzia konserwacji</translation> </message> <message> <source> Installation finished!</source> <translation> Instalacja zakończona.</translation> </message> <message> <source> Installation aborted!</source> <translation> Instalacja przerwana.</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>Uruchomienie tej operacji z sieci nie jest moÅžliwe</translation> </message> <message> <source>Removing deselected components...</source> <translation>Usuwanie odznaczonych komponentÃģw...</translation> </message> <message> <source> Update finished!</source> <translation> Zakończono uaktualnianie.</translation> </message> <message> <source> Update aborted!</source> <translation> Przerwano uaktualnianie.</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>Dezinstalacja pomyślnie zakończona.</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>Dezinstalacja przerwana.</translation> </message> <message> <source> Installing component %1</source> <translation> Instalacja komponentu %1</translation> </message> <message> <source>Installer Error</source> <translation>Błąd instalacji</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>Błąd podczas instalacji (%1): %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>Nie moÅžna przygotować dezinstalacji</translation> </message> <message> <source>Cannot start uninstall</source> <translation>Nie moÅžna uruchomić dezinstalacji</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>Błąd podczas dezinstalacji: %1</translation> </message> <message> <source>Unknown error</source> <translation>Nieznany błąd</translation> </message> <message> <source>Cannot retrieve remote tree: %1.</source> <translation>Nie moÅžna odczytać zdalnego drzewa: %1.</translation> </message> <message> <source>Failure to read packages from: %1.</source> <translation>Nie moÅžna odczytać pakietÃģw z: %1.</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>Nie moÅžna odczytać metainformacji: %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>Nie moÅžna dodać tymczasowej informacji o ÅšrÃģdłach aktualizacji.</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>Brak informacji o ÅšrÃģdłach aktualizacji.</translation> </message> <message> <source>Dependency cycle between components detected: '%1' and '%2'.</source> <translation>Wykryto cykliczną zaleÅžność pomiędzy komponentami "%1" i "%2".</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>Ustawienia %1</translation> </message> <message> <source>Maintain %1</source> <translation>Konserwacja %1</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>Czy anulować instalację?</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>Czy anulować dezinstalację?</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>Czy zakończyć instalację?</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>Czy zakończyć dezinstalację?</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>Czy zakończyć konserwację?</translation> </message> <message> <source>Question</source> <translation>Pytanie</translation> </message> <message> <source>Settings</source> <translation>Ustawienia</translation> </message> <message> <source>Error</source> <translation>Błąd</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>Instalacja z sieci nie jest moÅžliwa. Skopiuj instalator na lokalny dysk.</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>&PokaÅž szczegÃģły</translation> </message> <message> <source>&Hide Details</source> <translation>&Ukryj szczegÃģły</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>Zdezi&nstaluj</translation> </message> <message> <source>Uninstalling %1</source> <translation>Dezinstalowanie %1</translation> </message> <message> <source>&Update</source> <translation>&Uaktualnij</translation> </message> <message> <source>Updating components of %1</source> <translation>Uaktualnianie komponentÃģw %1</translation> </message> <message> <source>&Install</source> <translation>Za&instaluj</translation> </message> <message> <source>Installing %1</source> <translation>Instalowanie %1</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation>Dialog</translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>Proxy %1 wymaga nazwy uÅžytkownika i hasła.</translation> </message> <message> <source>Username:</source> <translation>Nazwa uÅžytkownika:</translation> </message> <message> <source>Username</source> <translation>Nazwa uÅžytkownika</translation> </message> <message> <source>Password:</source> <translation>Hasło:</translation> </message> <message> <source>Password</source> <translation>Hasło</translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>Zdezi&nstaluj</translation> </message> <message> <source>Ready to Uninstall</source> <translation>Gotowy do dezinstalacji</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>Konfiguracja gotowa do dezinstalacji %1.<br><font color="red">Katalog programu %2 zostanie całkowicie usunięty.</font>, włączając całą jego zawartość.</translation> </message> <message> <source>U&pdate</source> <translation>&Uaktualnij</translation> </message> <message> <source>Ready to Update Packages</source> <translation>Gotowy do uaktualnienia pakietÃģw</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>Konfiguracja gotowa do rozpoczęcia aktualizacji.</translation> </message> <message> <source>&Install</source> <translation>Za&instaluj</translation> </message> <message> <source>Ready to Install</source> <translation>Gotowy do instalacji</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>Konfiguracja gotowa do rozpoczęcia instalacji %1.</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation! Available space: %1, at least required %2.</source> <translation>Niewystarczająca ilość wolnego miejsca do przechowania plikÃģw tymczasowych i instalacji. Dostępna ilość wolnego miejsca: %1, wymagana ilość miejsca: %2.</translation> </message> <message> <source>Not enough disk space to store all selected components! Available space: %1, at least required: %2.</source> <translation>Niewystarczająca ilość wolnego miejsca do przechowania wszystkich wybranych komponentÃģw. Dostępna ilość wolnego miejsca: %1, wymagana ilość miejsca: %2.</translation> </message> <message> <source>Not enough disk space to store temporary files! Available space: %1, at least required: %2.</source> <translation>Niewystarczająca ilość wolnego miejsca do przechowania plikÃģw tymczasowych. Dostępna ilość wolnego miejsca: %1, wymagana ilość miejsca: %2.</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>Wybrany dysk posiada wystarczająca ilość miejsca na instalację, lecz po instalacji pozostanie na nim mniej niÅž 1% wolnego miejsca. %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>Wybrany dysk posiada wystarczająca ilość miejsca na instalację, lecz po instalacji pozostanie na nim mniej niÅž 100MB wolnego miejsca. %1</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>Instalacja zajmie %1 wolnego miejsca na dysku.</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>Nie moÅžna rozwiązać wszystkich zaleÅžności.</translation> </message> <message> <source>Components about to be removed.</source> <translation>Komponenty do usunięcia.</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>2 to 5</source> <translation>od 2 do 5</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>Rejestrowanie typÃģw plikÃģw moÅžliwe jest jedynie na Windows.</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>Rejestracja typÃģw plikÃģw: niepoprawne argumenty</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Nie moÅžna odczytać wszystkich danych po wysłaniu komendy: %1. Oczekiwano %2 bajtÃģw, otrzymano %3 bajtÃģw. Błąd: %4</translation> </message> </context> <context> <name>QInstaller::RemoteServerConnection</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>Nie moÅžna odczytać wszystkich danych po wysłaniu komendy: %1. Oczekiwano %2 bajtÃģw, otrzymano %3 bajtÃģw. Błąd: %4</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 3</source> <translation>dokładnie 3</translation> </message> <message> <source>Failed to open %1 for reading</source> <translation>Nie moÅžna otworzyć %1 do odczytu</translation> </message> <message> <source>Failed to open %1 for writing</source> <translation>Nie moÅžna otworzyć %1 do zapisu</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Cannot open Resource '%1' read-only.</source> <translation>Nie moÅžna otworzyć zasobu "%1" do odczytu.</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>Błąd odczytu po %1 bajtach: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>Błąd zapisu po %1 bajtach: %2</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>Zakończenie kreatora ustawień %1</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open the requested script file at %1: %2.</source> <translation>Nie moÅžna otworzyć wymaganego pliku ze skryptem "%1": %2.</translation> </message> <message> <source>Exception while loading the component script '%1'. (%2)</source> <translation>Wyjątek podczas ładowania skryptu komponentu "%1". (%2)</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in '%1' operation is empty.</source> <translation>Wymagany obiekt installer w operacji %1 jest pusty.</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>Ponownie uruchomienie: MoÅžliwe tylko w trybie akutalizacji albo w trybie menadÅžera pakietÃģw.</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>Ponownie uruchomienie: Niewłaściwe argumenty</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>Serwer wymaga autoryzacji</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>NaleÅžy podać nazwę uÅžytkownia i hasło aby uzystać dostęp do tej strony.</translation> </message> <message> <source>Username:</source> <translation>Nazwa uÅžytkownika:</translation> </message> <message> <source>Password:</source> <translation>Hasło:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 w %2</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) '%1' calling '%2' with arguments '%3'.</source> <translatorcomment>What is %3? Looks like broken.</translatorcomment> <translation type="unfinished"></translation> </message> <message> <source>Current method argument calling '%1' with arguments '%2' is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>Wywołanie metody "%1" z argumentami "%2" nie jest obsługiwane. NaleÅžy uÅžyć "add", "remove", "add_array_value" lub "remove_array_value".</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>Niewłaściwe argumenty w %0: ilość przekazanych argumentÃģw %1, oczekiwano %2, %3.</translation> </message> <message> <source>exactly 2</source> <translation>dokładnie 2</translation> </message> <message> <source>None of the arguments can be empty: source '%1', target '%2'.</source> <translation>Åŧaden z argumentÃģw nie moÅže być pusty: plik ÅšrÃģdłowy "%1", plik docelowy "%2".</translation> </message> <message> <source>Cannot move source '%1' to target '%2', because target exists and is not removable.</source> <translation>Nie moÅžna przenieść pliku ÅšrÃģdłowego "%1" do miejsca docelowego "%2", poniewaÅž dolecowy plik juÅž istnieje i nie moÅžna go usunąć.</translation> </message> <message> <source>Cannot move source '%1' to target '%2': %3</source> <translation>Nie moÅžna przenieść pliku ÅšrÃģdłowego "%1" do miejsca docelowego "%2": %3</translation> </message> <message> <source>Move '%1' to '%2'.</source> <translation>Przenoszenie "%1" do "%2".</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>SkrÃģt menu startowego</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.</source> <translation>Wybierz menu startowe, w ktÃģrym utworzyć skrÃģt do programu. MoÅžesz rÃģwnieÅž podać nazwę nowego katalogu.</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>Katalog instalacji</translation> </message> <message> <source>Please specify the folder where %1 will be installed.</source> <translation>Podaj katalog w ktÃģrym zostanie zainstalowany %1.</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translation>Alt+R</translation> </message> <message> <source>B&rowse...</source> <translation>&Przeglądaj...</translation> </message> <message> <source>The folder you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>Wybrany katalog istnieje i zawiera instalację. Wybierz inny katalog docelowy.</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation>Wybrano istniejący, niepusty katalog do instalacji. ZwrÃģć uwagę, Åže zostanie on całkowicie skasowany w trakcie dezinstalacji aplikacji. Nie zaleca się instalacji do tego katalogu, gdyÅž instalacja moÅže się nie powieść. Czy kontynuować?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>Wybrano istniejący plik lub dowiązanie symboliczne. Wybierz inne miejsce docelowe instalacji.</translation> </message> <message> <source>Select Installation Folder</source> <translation>Wybierz katalog instalacji</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid folder.</source> <translation>ŚcieÅžka instalacji nie moÅže być pusta. Podaj nazwę poprawnego katalogu.</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>ŚcieÅžka instalacji nie moÅže być względna. Podaj pełną ścieÅžkę do katalogu.</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>ŚcieÅžka instalacji posiada znaki z poza ASCII. Nie jest to obecnie obsługiwane. Podaj inną ścieÅžkę lub katalog instalacji.</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>Instalowanie w %1 jest niedozwolone, gdyÅž katalog instalacji zostanie kompletnie usunięty.</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>Podana ścieÅžka jest za długa.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>Podana ścieÅžka jest niepoprawna.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>Podana ścieÅžka zawiera niepoprawny napęd.</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid folder.</source> <translation>ŚcieÅžka instalacji nie moÅže być zakończona znakiem ".".</translation> </message> <message> <source>The installation path must not contain '%1', please specify a valid folder.</source> <translation>ŚcieÅžka instalacji nie moÅže zawierać "%1".</translation> </message> <message> <source>Warning</source> <translation>OstrzeÅženie</translation> </message> <message> <source>Error</source> <translation>Błąd</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Empty repository URL.</source> <translation>Pusty URL repozytorium.</translation> </message> <message> <source>URL scheme not supported: %1 (%2).</source> <translation>Nieobsługiwany schemat URL: %1 (%2).</translation> </message> <message> <source>Got a timeout while testing: '%1'</source> <translation>Przekroczono maksymalny czas oczekiwania na zakończenie testowania: "%1"</translation> </message> <message> <source>Cannot parse Updates.xml! Error: %1.</source> <translation>Nie moÅžna sparsować Updates.xml. Błąd: %1.</translation> </message> <message> <source>Updates.xml could not be opened for reading!</source> <translation>Nie moÅžna otworzyć Updates.xml do odczytu.</translation> </message> <message> <source>Updates.xml could not be found on server!</source> <translation>Nie znaleziono Updates.xml na serwerze.</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>Wymagana autoryzacja</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>Podaj hasło do autoryzacji sudo:</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>Błąd nabywania praw administratora</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>Nie moÅžna uzyskać autoryzacji.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as root and then clicking OK.</source> <translation>Nie moÅžna uzyskać autoryzacji wymaganej do dalszej instalacji. Przerwij instalację albo uÅžyj rozwiązania awaryjnego wykonując: %1 jako administrator, po czym naciśnij OK.</translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>Nie moÅžna otworzyć pliku z zasobami %1: %2</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>Nie moÅžna otworzyć pliku z ustawieniami %1 do odczytu: %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>Ustawienia</translation> </message> <message> <source>Network</source> <translation>Sieć</translation> </message> <message> <source>No proxy</source> <translation>Brak proxy</translation> </message> <message> <source>System proxy settings</source> <translation>Ustawienia systemowego proxy</translation> </message> <message> <source>Manual proxy configuration</source> <translation>Ręczna konfiguracja proxy</translation> </message> <message> <source>HTTP proxy:</source> <translation>HTTP proxy:</translation> </message> <message> <source>Port:</source> <translation>Port:</translation> </message> <message> <source>FTP proxy:</source> <translation>FTP proxy:</translation> </message> <message> <source>Repositories</source> <translation>Repozytoria</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>Dodaj nazwę uÅžytkownia i hasło do autoryzacji, jeśli wymagane.</translation> </message> <message> <source>Use temporary repositories only</source> <translation>UÅžywaj tylko tymczasowych repozytoriÃģw</translation> </message> <message> <source>Add</source> <translation>Dodaj</translation> </message> <message> <source>Remove</source> <translation>Usuń</translation> </message> <message> <source>Test</source> <translation>Przetestuj</translation> </message> <message> <source>Show Passwords</source> <translation>PokaÅž hasła</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>Zaznacz aby uÅžyć repozytorium podczas pobierania.</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>Dodaj nazwę uÅžytkownia w celu autoryzacji na serwerze.</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>Dodaj hasło w celu autoryzacji na serwerze.</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation type="unfinished"></translation> </message> <message> <source>There was an error testing this repository.</source> <translation>Wystąpił błąd podczas testowania tego repozytorium.</translation> </message> <message> <source>Do you want to disable the tested repository?</source> <translation>Czy zdezaktywować przetestowane repozytorium?</translation> </message> <message> <source>Hide Passwords</source> <translation>Ukryj hasła</translation> </message> <message> <source>Use</source> <translation>UÅžyj</translation> </message> <message> <source>Username</source> <translation>Nazwa uÅžytkownika</translation> </message> <message> <source>Password</source> <translation>Hasło</translation> </message> <message> <source>Repository</source> <translation>Repozytorium</translation> </message> <message> <source>Default repositories</source> <translation>Domyślne repozytoria</translation> </message> <message> <source>Temporary repositories</source> <translation>Tymczasowe repozytoria</translation> </message> <message> <source>User defined repositories</source> <translation>Własne repozytoria</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable</source> <translation>ŚcieÅžka %1 rejestru jest tylko do odczytu</translation> </message> <message> <source>Cannot write to registry path %1</source> <translation>Nie moÅžna zapisać do ścieÅžki rejestru %1</translation> </message> <message> <source>Renaming %1 into %2 failed with %3.</source> <translation>Zmiana nazwy %1 na %2 zakończona błędem %3.</translation> </message> </context> </TS> ���������������������������������������������������������src/sdk/translations/ifw_ru.ts����������������������������������������������������������������������0000664�0000000�0000000�00000344033�13253666515�0017035�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="ru_RU"> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>%1 Ðē %2</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>ÐŸŅ€ÐūÐšŅÐļ-ҁÐĩŅ€ÐēÐĩŅ€ ҂ҀÐĩÐąŅƒÐĩŅ‚ Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļŅŽ.</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩÐđŅ‚Ðļ Ðē ÐŋÐūзÐļ҆ÐļŅŽ %1 ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ ÐīÐ°Ð―Ð―Ņ‹Ņ… ÐūÐŋÐĩŅ€Ð°Ņ†ÐļÐđ.</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩÐđŅ‚Ðļ Ðē ÐŋÐūзÐļ҆ÐļŅŽ %1 ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ ÐąÐŧÐūКа Ð―Ð°ÐąÐūŅ€Ð° Ņ€ÐĩŅŅƒŅ€ŅÐūÐē.</translation> </message> <message> <source>Cannot open meta resource %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ ОÐĩŅ‚Ð°Ņ€ÐĩŅŅƒŅ€ŅŅ‹ %1.</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩÐđŅ‚Ðļ Ðē ÐŋÐūзÐļ҆ÐļŅŽ %1 ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ ҁ҇ґ҂҇ÐļКа Ðēҁ҂ҀÐūÐĩÐ―Ð―Ņ‹Ņ… ОÐĩŅ‚Ð°ÐīÐ°Ð―Ð―Ņ‹Ņ….</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩÐđŅ‚Ðļ Ðē ÐŋÐūзÐļ҆ÐļŅŽ %1 ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ ҁÐĩÐģОÐĩÐ―Ņ‚Ð° Ð―Ð°ÐąÐūŅ€Ð° Ņ€ÐĩŅŅƒŅ€ŅÐūÐē.</translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>НÐĩÐūÐķÐļÐīÐ°Ð―Ð―ÐūÐĩ Ð―ÐĩҁÐūÐēÐŋаÐīÐĩÐ―ÐļÐĩ ОÐĩŅ‚Ð°Ņ€ÐĩŅŅƒŅ€ŅÐūÐē. ÐŸŅ€Ðū҇ÐļŅ‚Ð°Ð―Ðū %1, ÐūÐķÐļÐīаÐŧÐūҁҌ: %2.</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>ÐĒŅ€ÐĩÐąŅƒÐĩŅ‚ŅŅ http-Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļŅ</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>ДÐŧŅ ÐīÐūŅŅ‚ŅƒÐŋа К ŅŅ‚ÐūÐžŅƒ ŅÐ°ÐđŅ‚Ņƒ Ð’Ņ‹ ÐīÐūÐŧÐķÐ―Ņ‹ ÐēÐēÐĩҁ҂Ðļ ÐŧÐūÐģÐļÐ― Ðļ ÐŋÐ°Ņ€ÐūÐŧҌ.</translation> </message> <message> <source>Username:</source> <translation>ЛÐūÐģÐļÐ―:</translation> </message> <message> <source>Password:</source> <translation>ÐŸÐ°Ņ€ÐūÐŧҌ:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 Ðē %2</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path "%1" exists but is not a directory.</source> <translation>ÐŸŅƒŅ‚ŅŒ ÂŦ%1Âŧ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚, Ð―Ðū Ð―Ðĩ ŅÐēÐŧŅÐĩŅ‚ŅŅ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģÐūО.</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ.</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūÐŋŅ€ÐĩÐīÐĩÐŧÐļŅ‚ŅŒ ÐŋŅƒŅ‚ŅŒ ŅÐŧÐĩОÐĩÐ―Ņ‚Ð° Ð°Ņ€Ņ…ÐļÐēа %1.</translation> </message> <message> <source>Cannot remove already existing symlink %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ ŅŅƒŅ‰Ðĩҁ҂ÐēŅƒŅŽŅ‰ŅƒŅŽ ҁÐļОÐēÐūÐŧŅŒÐ―ŅƒŅŽ ҁҁҋÐŧÐšŅƒ %1.</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>Cannot create symlink at "%1". Another one is already existing.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ҁÐļОÐēÐūÐŧŅŒÐ―ŅƒŅŽ ҁҁҋÐŧÐšŅƒ ÂŦ%1Âŧ, ÐŋÐūŅ‚ÐūÐžŅƒ ҇҂Ðū ҁҁҋÐŧКа ҃ÐķÐĩ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚.</translation> </message> <message> <source>Cannot read symlink target from file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€Ðū҇ÐļŅ‚Ð°Ņ‚ŅŒ ҆ÐĩÐŧҌ ҁÐļОÐēÐūÐŧŅŒÐ―ÐūÐđ ҁҁҋÐŧКÐļ Ðļз Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot create symlink at %1: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ҁÐļОÐēÐūÐŧŅŒÐ―ŅƒŅŽ ҁҁҋÐŧÐšŅƒ %1: %2</translation> </message> </context> <context> <name>InstallerBase</name> <message> <source>Waiting for %1</source> <translation>ОÐķÐļÐīÐ°Ð―ÐļÐĩ %1</translation> </message> <message> <source>Another %1 instance is already running. Wait until it finishes, close it, or restart your system.</source> <translation>Ð”Ņ€ŅƒÐģÐūÐđ ŅÐšÐ·ÐĩОÐŋÐŧŅŅ€ %1 ҃ÐķÐĩ Ņ€Ð°ÐąÐūŅ‚Ð°ÐĩŅ‚. ДÐūÐķÐīÐļŅ‚ÐĩҁҌ ÐĩÐģÐū заÐēÐĩŅ€ŅˆÐĩÐ―ÐļŅ, Ð·Ð°ÐšŅ€ÐūÐđŅ‚Ðĩ ÐĩÐģÐū ÐļÐŧÐļ ÐŋÐĩŅ€ÐĩзаÐģŅ€ŅƒÐ·ÐļŅ‚Ðĩ ҁÐļҁ҂ÐĩÐžŅƒ.</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹, ÐīÐūÐąÐ°ÐēÐŧÐĩÐ―Ð―Ņ‹Ðĩ КаК аÐēŅ‚ÐūÐžÐ°Ņ‚Ðļ҇ÐĩŅÐšÐļÐĩ заÐēÐļҁÐļОÐūҁ҂Ðļ:</translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹ ҁ Ņ€Ð°Ð·Ņ€ÐĩŅˆŅ‘Ð―Ð―Ņ‹ÐžÐļ заÐēÐļҁÐļОÐūŅŅ‚ŅÐžÐļ:</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>Ð’Ņ‹ÐąŅ€Ð°Ð―Ð―Ņ‹Ðĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹ ÐąÐĩз заÐēÐļҁÐļОÐūҁ҂ÐĩÐđ:</translation> </message> <message> <source>Components added as dependency for "%1":</source> <translation>КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹, ÐīÐūÐąÐ°ÐēÐŧÐĩÐ―Ð―Ņ‹Ðĩ КаК заÐēÐļҁÐļОÐūҁ҂Ðļ ÐīÐŧŅ ÂŦ%1Âŧ:</translation> </message> <message> <source>Recursion detected, component "%1" already added with reason: "%2"</source> <translation>ÐžÐąÐ―Ð°Ņ€ŅƒÐķÐĩÐ―Ð° Ņ€ÐĩÐšŅƒŅ€ŅÐļŅ: КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ ÂŦ%1Âŧ ҃ÐķÐĩ ÐīÐūÐąÐ°ÐēÐŧÐĩÐ― ÐŋÐū ÐŋŅ€Ðļ҇ÐļÐ―Ðĩ: ÂŦ%2Âŧ</translation> </message> <message> <source>Cannot find missing dependency "%1" for "%2".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ð―Ð°ÐđŅ‚Ðļ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ ÂŦ%1Âŧ, Ð―ÐĩÐūÐąŅ…ÐūÐīÐļÐžŅ‹Ðđ ÐīÐŧŅ ÂŦ%2Âŧ.</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>ÐžŅ‚ÐžÐĩÐ―ÐĩÐ―Ðū</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>Cannot find backup file for "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ð―Ð°ÐđŅ‚Ðļ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot restore backup file for "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐēÐūŅŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐēÐūŅŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot copy a non-existent file: %1</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅÐšÐūÐŋÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ ÐūŅ‚ŅŅƒŅ‚ŅŅ‚ÐēŅƒŅŽŅ‰ÐļÐđ Ņ„Ð°ÐđÐŧ: %1</translation> </message> <message> <source>Cannot backup file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅÐšÐūÐŋÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ Ðļз ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Cannot delete file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot restore backup file into "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐēÐūŅŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ðē ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐēÐūŅŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download finished.</source> <translation>ЗаÐģŅ€ŅƒÐ·ÐšÐ° заÐēÐĩŅ€ŅˆÐĩÐ―Ð°.</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>ÐĨÐĩ҈-ŅŅƒÐžÐžŅ‹ Ð―Ðĩ ҁÐūÐēÐŋаÐīÐ°ŅŽŅ‚.</translation> </message> <message> <source>Download canceled.</source> <translation>ЗаÐģŅ€ŅƒÐ·ÐšÐ° ÐūŅ‚ÐžÐĩÐ―ÐĩÐ―Ð°.</translation> </message> <message> <source>%1 of %2</source> <translation>%1 Ðļз %2</translation> </message> <message> <source>%1 downloaded.</source> <translation>заÐģŅ€ŅƒÐķÐĩÐ―Ðū %1.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/ҁ)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n ÐīÐĩÐ―ŅŒ, </numerusform> <numerusform>%n ÐīÐ―Ņ, </numerusform> <numerusform>%n ÐīÐ―ÐĩÐđ, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n Ņ‡Ð°Ņ, </numerusform> <numerusform>%n Ņ‡Ð°ŅÐ°, </numerusform> <numerusform>%n Ņ‡Ð°ŅÐūÐē, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n ОÐļÐ―ŅƒŅ‚Ð°</numerusform> <numerusform>%n ОÐļÐ―ŅƒŅ‚Ņ‹</numerusform> <numerusform>%n ОÐļÐ―ŅƒŅ‚</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n ҁÐĩÐšŅƒÐ―Ðīа</numerusform> <numerusform>%n ҁÐĩÐšŅƒÐ―ÐīŅ‹</numerusform> <numerusform>%n ҁÐĩÐšŅƒÐ―Ðī</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - ÐūŅŅ‚Ð°ÐŧÐūҁҌ %1%2%3%4.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - ÐēŅ€ÐĩÐžŅ ÐūКÐūÐ―Ņ‡Ð°Ð―ÐļŅ заÐģŅ€ŅƒÐ·ÐšÐļ Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―Ðū.</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>%1 at %2</source> <translation>%1 Ðē %2</translation> </message> <message> <source>Authentication request canceled.</source> <translation>ЗаÐŋŅ€Ðūҁ Ð―Ð° Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļŅŽ ÐūŅ‚ÐžÐĩÐ―Ņ‘Ð―.</translation> </message> <message> <source>Secure Connection Failed</source> <translation>ÐĄÐąÐūÐđ ÐąÐĩзÐūÐŋÐ°ŅÐ―ÐūÐģÐū ҁÐūÐĩÐīÐļÐ―ÐĩÐ―ÐļŅ</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>ВÐūÐ·Ð―ÐļКÐŧа Ðū҈ÐļÐąÐšÐ° ÐŋÐūÐīКÐŧŅŽŅ‡ÐĩÐ―ÐļŅ К: %1.</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>Ð­Ņ‚Ðū ÐēÐūзОÐūÐķÐ―Ðū ÐļÐŧÐļ Ðļз-за ÐŋŅ€ÐūÐąÐŧÐĩО ҁ Ð―Ð°ŅŅ‚Ņ€ÐūÐđКÐūÐđ ҁÐĩŅ€ÐēÐĩŅ€Ð°, ÐļÐŧÐļ Ðļз-за ÐŋÐūÐŋŅ‹Ņ‚ÐšÐļ зÐŧÐūŅƒÐžŅ‹ŅˆÐŧÐĩÐ―Ð―ÐļКа ÐŋÐūÐīОÐĩÐ―ÐļŅ‚ŅŒ ҁÐĩŅ€ÐēÐĩŅ€.</translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>Ð•ŅÐŧÐļ ÐēŅ‹ Ņ€Ð°Ð―ŅŒŅˆÐĩ ҃ҁÐŋÐĩŅˆÐ―Ðū ÐŋÐūÐīКÐŧŅŽŅ‡Ð°ÐŧÐļҁҌ К ŅŅ‚ÐūÐžŅƒ ҁÐĩŅ€ÐēÐĩŅ€Ņƒ ÐļÐŧÐļ ÐīÐūÐēÐĩŅ€ŅÐĩŅ‚Ðĩ ÐĩÐžŅƒ, Ņ‚Ðū Ðū҈ÐļÐąÐšÐ° ОÐūÐķÐĩŅ‚ ÐąŅ‹Ņ‚ŅŒ ÐēŅ€ÐĩОÐĩÐ―Ð―ÐūÐđ, Ðļ ÐēŅ‹ ОÐūÐķÐĩŅ‚Ðĩ ÐŋÐūÐēŅ‚ÐūŅ€ÐļŅ‚ŅŒ Ðĩ҉ґ Ņ€Ð°Ð·.</translation> </message> <message> <source>Try again</source> <translation>ПÐūÐēŅ‚ÐūŅ€ÐļŅ‚ŅŒ</translation> </message> <message> <source>Cannot download %1. Writing to file "%2" failed: %3</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ ÂŦ%1Âŧ: НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ Ðē Ņ„Ð°ÐđÐŧ ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Cannot download %1. Cannot create file "%2": %3</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ ÂŦ%1Âŧ. НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÂŦ%2Âŧ: %3</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ Ðē ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Cannot create directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Unknown error.</source> <translation>НÐĩÐļзÐēÐĩŅŅ‚Ð―Ð°Ņ Ðū҈ÐļÐąÐšÐ°.</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅÐšÐūÐŋÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐēÐūŅŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>Cannot find backup file for "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ð―Ð°ÐđŅ‚Ðļ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot restore backup file for "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐēÐūŅŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot restore backup file for "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐēÐūŅŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1": %2</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋŅ€Ðū҇ÐļŅ‚Ð°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ Ņ€ÐĩŅŅƒŅ€ŅÐūÐē ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Cannot remove directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>The directory does not exist.</source> <translation>ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ Ð―Ðĩ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚.</translation> </message> <message> <source>Cannot recreate directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 заÐŋŅƒŅ‰ÐĩÐ―Ð°</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>%1 Ð―Ðĩ ОÐūÐķÐĩŅ‚ ÐąŅ‹Ņ‚ŅŒ ÐūŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð°</translation> </message> <message> <source>Cannot stop task %1</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐūŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ заÐīÐ°Ņ‡Ņƒ %1</translation> </message> <message> <source>%1 cannot be paused</source> <translation>%1 Ð―Ðĩ ОÐūÐķÐĩŅ‚ ÐąŅ‹Ņ‚ŅŒ ÐŋŅ€ÐļÐūŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð°</translation> </message> <message> <source>Cannot pause task %1</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋŅ€ÐļÐūŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ ÐēŅ‹ÐŋÐūÐŧÐ―ÐĩÐ―ÐļÐĩ заÐīÐ°Ņ‡Ðļ %1</translation> </message> <message> <source>Cannot resume task %1</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋŅ€ÐūÐīÐūÐŧÐķÐļŅ‚ŅŒ ÐēŅ‹ÐŋÐūÐŧÐ―ÐĩÐ―ÐļÐĩ заÐīÐ°Ņ‡Ðļ %1</translation> </message> <message> <source>%1 done</source> <translation>%1 ÐēŅ‹ÐŋÐūÐŧÐ―ÐĩÐ―Ð°</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>ÐŸŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļÐĩ Ð―Ðĩ ОÐūÐķÐĩŅ‚ ÐŋÐūÐŧŅƒŅ‡ÐļŅ‚ŅŒ ÐīÐūŅŅ‚ŅƒÐŋ К ÐļÐ―Ņ„ÐūŅ€ÐžÐ°Ņ†ÐļÐļ Ðū ÐŋаКÐĩŅ‚Ð°Ņ….</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>ÐžÐąÐ―Ð°Ņ€ŅƒÐķÐĩÐ―Ðū %n ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐĩ.</numerusform> <numerusform>ÐžÐąÐ―Ð°Ņ€ŅƒÐķÐĩÐ―Ðū %n ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅ.</numerusform> <numerusform>ÐžÐąÐ―Ð°Ņ€ŅƒÐķÐĩÐ―Ðū %n ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐđ.</numerusform> </translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>ЗаÐģŅ€ŅƒÐķаÐĩŅ‚ŅŅ Ņ„Ð°ÐđÐŧ Updates.xml ҁ ҁÐĩŅ€ÐēÐĩŅ€Ð° ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐđ.</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>ЗаÐģŅ€ŅƒÐ·ÐšÐ° Ņ„Ð°ÐđÐŧа Updates.xml заÐēÐĩŅ€ŅˆÐĩÐ―Ð°.</translation> </message> <message> <source>Computing applicable updates.</source> <translation>ПÐūÐīÐģÐūŅ‚ÐūÐēКа ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐđ ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Application updates computed.</source> <translation>ÐžÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅ ÐŋÐūÐīÐģÐūŅ‚ÐūÐēÐŧÐĩÐ―Ņ‹.</translation> </message> <message> <source>No package sources set for this application.</source> <translation>Ð˜ŅŅ…ÐūÐīÐ―ÐļКÐļ ÐŋаКÐĩŅ‚Ð° Ð―Ðĩ заÐīÐ°Ð―Ņ‹ ÐīÐŧŅ ŅŅ‚ÐūÐģÐū ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Cannot download package source %1 from "%2".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ Ðļҁ҅ÐūÐīÐ―ÐļК ÐŋаКÐĩŅ‚Ð° ÂŦ%1Âŧ Ðļз ÂŦ%2Âŧ.</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Updates.xml contains invalid content: %1</source> <translation>ÐĪаÐđÐŧ Updates.xml ҁÐūÐīÐĩŅ€ÐķÐļŅ‚ Ð―ÐĩÐīÐūÐŋŅƒŅŅ‚ÐļÐžŅ‹Ðĩ ÐīÐ°Ð―Ð―Ņ‹Ðĩ: %1</translation> </message> <message> <source>Cannot read "%1"</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋŅ€Ðū҇ÐļŅ‚Ð°Ņ‚ŅŒ ÂŦ%1Âŧ</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>ÐžŅˆÐļÐąÐšÐ° Ņ€Ð°Ð·ÐąÐūŅ€Ð° XML Ðē %1 Ðē %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>НÐĩÐŋŅ€ÐĩÐīÐēÐļÐīÐĩÐ―Ð―Ņ‹Ðđ КÐūŅ€Ð―ÐĩÐēÐūÐđ ŅÐŧÐĩОÐĩÐ―Ņ‚ %1, ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ ÂŦUpdatesÂŧ.</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>ÐžŅ‚ŅŅƒŅ‚ŅŅ‚Ðē҃ÐĩŅ‚ ŅÐŧÐĩОÐĩÐ―Ņ‚ "ApplicationName".</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>ÐžŅ‚ŅŅƒŅ‚ŅŅ‚Ðē҃ÐĩŅ‚ ŅÐŧÐĩОÐĩÐ―Ņ‚ "ApplicationVersion".</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>ÐĢ ŅÐŧÐĩОÐĩÐ―Ņ‚Ð° PackageUpdate ÐūŅ‚ŅŅƒŅ‚ŅŅ‚Ðē҃ÐĩŅ‚ ÐŋÐūÐŧÐĩ "Name"</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>ÐĢ ŅÐŧÐĩОÐĩÐ―Ņ‚Ð° PackageUpdate ÐūŅ‚ŅŅƒŅ‚ŅŅ‚Ðē҃ÐĩŅ‚ ÐŋÐūÐŧÐĩ "Version"</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>ÐĢ ŅÐŧÐĩОÐĩÐ―Ņ‚Ð° PackageUpdate ÐūŅ‚ŅŅƒŅ‚ŅŅ‚Ðē҃ÐĩŅ‚ ÐŋÐūÐŧÐĩ "ReleaseDate"</translation> </message> </context> <context> <name>Lib7z</name> <message> <source>Unknown exception caught (%1)</source> <translation>ВÐūÐ·Ð―ÐļКÐŧÐū Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―ÐūÐĩ ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ (%1)</translation> </message> <message> <source>internal code: %1</source> <translation>ÐēÐ―ŅƒŅ‚Ņ€ÐĩÐ―Ð―ÐļÐđ КÐūÐī Ðū҈ÐļÐąÐšÐļ: %1</translation> </message> <message> <source>not enough memory</source> <translation>Ð―ÐĩÐīÐūŅŅ‚Ð°Ņ‚ÐūŅ‡Ð―Ðū ÐŋÐ°ÐžŅŅ‚Ðļ</translation> </message> <message> <source>Error: %1</source> <translation>ÐžŅˆÐļÐąÐšÐ°: %1</translation> </message> <message> <source>Cannot retrieve property %1 for item %2.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐūÐŧŅƒŅ‡ÐļŅ‚ŅŒ ҁÐēÐūÐđҁ҂ÐēÐū %1 ÐīÐŧŅ ŅÐŧÐĩОÐĩÐ―Ņ‚Ð° %2.</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3.</source> <translation>ÐĄÐēÐūÐđҁ҂ÐēÐū %1 ŅÐŧÐĩОÐĩÐ―Ņ‚Ð° %2 ÐūŅ‚Ð―ÐūҁÐļŅ‚ŅŅ Ð―Ðĩ К Ņ‚ÐļÐŋ҃ VT_FILETIME, а К %3.</translation> </message> <message> <source>Cannot convert UTC file time to system time.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€ÐĩÐūÐąŅ€Ð°Ð·ÐūÐēÐ°Ņ‚ŅŒ UTC ÐēŅ€ÐĩÐžŅ Ņ„Ð°ÐđÐŧа Ðē ҁÐļҁ҂ÐĩÐžÐ―ÐūÐĩ ÐēŅ€ÐĩÐžŅ.</translation> </message> <message> <source>Cannot load codecs.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ КÐūÐīÐĩКÐļ.</translation> </message> <message> <source>Cannot open archive "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ð°Ņ€Ņ…ÐļÐē ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot retrieve number of items in archive.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūÐŋŅ€ÐĩÐīÐĩÐŧÐļŅ‚ŅŒ КÐūÐŧÐļ҇Ðĩҁ҂ÐēÐū ŅÐŧÐĩОÐĩÐ―Ņ‚ÐūÐē Ð°Ņ€Ņ…ÐļÐēа.</translation> </message> <message> <source>Cannot retrieve path of archive item "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūÐŋŅ€ÐĩÐīÐĩÐŧÐļŅ‚ŅŒ ÐŋŅƒŅ‚ŅŒ ŅÐŧÐĩОÐĩÐ―Ņ‚Ð° Ð°Ņ€Ņ…ÐļÐēа ÂŦ%1Âŧ.</translation> </message> <message> <source>Unknown exception caught (%1).</source> <translation>ВÐūÐ·Ð―ÐļКÐŧÐū Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―ÐūÐĩ ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ (%1).</translation> </message> <message> <source>Cannot create temporary file: %1</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÐēŅ€ÐĩОÐĩÐ―Ð―Ņ‹Ðđ Ņ„Ð°ÐđÐŧ: %1</translation> </message> <message> <source>Unsupported archive type.</source> <translation>НÐĩÐŋÐūÐīÐīÐĩŅ€ÐķÐļÐēаÐĩÐžŅ‹Ðđ Ņ‚ÐļÐŋ Ð°Ņ€Ņ…ÐļÐēа.</translation> </message> <message> <source>Cannot create archive "%1"</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ð°Ņ€Ņ…ÐļÐē ÂŦ%1Âŧ</translation> </message> <message> <source>Cannot create archive "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ð°Ņ€Ņ…ÐļÐē ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot remove old archive "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ ŅŅ‚Ð°Ņ€Ņ‹Ðđ Ð°Ņ€Ņ…ÐļÐē ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot rename temporary archive "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩÐļОÐĩÐ―ÐūÐēÐ°Ņ‚ŅŒ ŅŅ‚Ð°Ņ€Ņ‹Ðđ Ð°Ņ€Ņ…ÐļÐē ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> </context> <context> <name>LocalPackageHub</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 ҁÐūÐīÐĩŅ€ÐķÐļŅ‚ Ð―ÐĩÐīÐūÐŋŅƒŅŅ‚ÐļÐžŅ‹Ðĩ ÐīÐ°Ð―Ð―Ņ‹Ðĩ: %2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>ÐĪаÐđÐŧ %1 Ð―Ðĩ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚.</translation> </message> <message> <source>Cannot open %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ %1.</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>ÐžŅˆÐļÐąÐšÐ° Ņ€Ð°Ð·ÐąÐūŅ€Ð° Ðē %1 Ðē %2, %3: %4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>НÐĩÐŋŅ€ÐĩÐīÐēÐļÐīÐĩÐ―Ð―Ņ‹Ðđ КÐūŅ€Ð―ÐĩÐēÐūÐđ ŅÐŧÐĩОÐĩÐ―Ņ‚ %1, ÐīÐūÐŧÐķÐĩÐ― ÐąŅ‹Ņ‚ŅŒ ÂŦPackagesÂŧ.</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÐąÐŧÐūКÐļŅ€ÐūÐēКÐļ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot write PID to lock file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ PID Ðē Ņ„Ð°ÐđÐŧ ÐąÐŧÐūКÐļŅ€ÐūÐēКÐļ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot obtain the lock for file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ð·Ð°ÐąÐŧÐūКÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot release the lock for file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ņ€Ð°Ð·ÐąÐŧÐūКÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>bytes</source> <translation>ÐąÐ°ÐđŅ‚(ÐūÐē)</translation> </message> <message> <source>KiB</source> <translation>КБ</translation> </message> <message> <source>MiB</source> <translation>МБ</translation> </message> <message> <source>GiB</source> <translation>ГБ</translation> </message> <message> <source>TiB</source> <translation>ÐĒБ</translation> </message> <message> <source>PiB</source> <translation>ПБ</translation> </message> <message> <source>EiB</source> <translation>ЭБ</translation> </message> <message> <source>ZiB</source> <translation>ЗБ</translation> </message> <message> <source>YiB</source> <translation>ИБ</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ ÐēŅ€ÐĩОÐĩÐ―Ð―Ņ‹Ðđ Ņ„Ð°ÐđÐŧ %1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ ÐēŅ€ÐĩОÐĩÐ―Ð―Ņ‹Ðđ Ņ„Ð°ÐđÐŧ ÐīÐŧŅ ŅˆÐ°ÐąÐŧÐūÐ―Ð° %1: %2</translation> </message> <message> <source>Corrupt installation</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа ÐŋÐūÐēŅ€ÐĩÐķÐīÐĩÐ―Ð°</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>ВÐļÐīÐļОÐū, ŅƒŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð―ÐūÐĩ ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļÐĩ ÐŋÐūÐēŅ€ÐĩÐķÐīÐĩÐ―Ðū. ПÐūÐŋŅ€ÐūÐąŅƒÐđŅ‚Ðĩ ÐĩÐģÐū Ð·Ð°Ð―ÐūÐēÐū ÐŋÐĩŅ€ÐĩŅƒŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ.</translation> </message> <message> <source>No marker found, stopped after %1.</source> <translation>ÐœÐ°Ņ€ÐšÐĩŅ€ Ð―Ðĩ Ð―Ð°ÐđÐīÐĩÐ―, ÐūŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ðū ÐŋÐūҁÐŧÐĩ %1.</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€Ðū҇ÐļŅ‚Ð°Ņ‚ŅŒ ÐŋÐūҁÐŧÐĩ %1 ÐąÐ°ÐđŅ‚: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>ÐĄÐąÐūÐđ заÐŋÐļҁÐļ ҁ %1 ÐąÐ°ÐđŅ‚Ð°: %2</translation> </message> <message> <source>The specified module could not be found.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ð―Ð°ÐđŅ‚Ðļ ŅƒÐšÐ°Ð·Ð°Ð―Ð―Ņ‹Ðđ ОÐūÐī҃ÐŧҌ.</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>Copy failed: %1</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅÐšÐūÐŋÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ: %1</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot copy file from "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅÐšÐūÐŋÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Cannot move file from "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩОÐĩҁ҂ÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Components cannot have children in updater mode.</source> <translation>ÐĢ ÐšÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē Ð―Ðĩ ОÐūÐķÐĩŅ‚ ÐąŅ‹Ņ‚ŅŒ ÐŋÐūŅ‚ÐūОКÐūÐē Ðē Ņ€ÐĩÐķÐļОÐĩ ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Error</source> <translation>ÐžŅˆÐļÐąÐšÐ°</translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐēŅ‹ÐŋÐūÐŧÐ―ÐļŅ‚ŅŒ ОÐĩŅ‚ÐūÐī isDefault Ðē ҁ҆ÐĩÐ―Ð°Ņ€ÐļÐļ %1</translation> </message> <message> <source>Update Info: </source> <translation>Ð˜Ð―Ņ„ÐūŅ€ÐžÐ°Ņ†ÐļŅ ÐūÐą ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐļ:</translation> </message> <message> <source>Cannot open the requested UI file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ заÐŋŅ€Ðū҈ÐĩÐ―Ð―Ņ‹Ðđ UI Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot load the requested UI file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ заÐŋŅ€Ðū҈ÐĩÐ―Ð―Ņ‹Ðđ UI Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot open the requested license file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ заÐŋŅ€Ðū҈ÐĩÐ―Ð―Ņ‹Ðđ Ņ„Ð°ÐđÐŧ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐļ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Error: Operation %1 does not exist.</source> <translation>ÐžŅˆÐļÐąÐšÐ°: ÐūÐŋÐĩŅ€Ð°Ņ†ÐļŅ %1 Ð―Ðĩ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚.</translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component Name</source> <translation>Ð˜ÐžŅ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ð°</translation> </message> <message> <source>Action</source> <translation>ДÐĩÐđҁ҂ÐēÐļÐĩ</translation> </message> <message> <source>Installed Version</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð―Ð°Ņ ÐēÐĩҀҁÐļŅ</translation> </message> <message> <source>New Version</source> <translation>НÐūÐēÐ°Ņ ÐēÐĩҀҁÐļŅ</translation> </message> <message> <source>Release Date</source> <translation>Ð”Ð°Ņ‚Ð° ÐēŅ‹ÐŋŅƒŅÐšÐ°</translation> </message> <message> <source>Size</source> <translation>РазОÐĩŅ€</translation> </message> <message> <source>Component is marked for installation.</source> <translation>КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ ÐēŅ‹ÐąŅ€Ð°Ð― ÐīÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ.</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ ÐēŅ‹ÐąŅ€Ð°Ð― ÐīÐŧŅ ҃ÐīаÐŧÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Component is installed.</source> <translation>КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―.</translation> </message> <message> <source>Component is not installed.</source> <translation>КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ Ð―Ðĩ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―.</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translatorcomment>Ð―Ð°ÐąÐūŅ€ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē ÐŋÐū ŅƒÐžÐūÐŧŅ‡Ð°Ð―ÐļŅŽ</translatorcomment> <translation>Alt+A</translation> </message> <message> <source>Def&ault</source> <translation>&ПÐū ŅƒÐžÐūÐŧŅ‡Ð°Ð―ÐļŅŽ</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translatorcomment>ÐūŅ‚ÐžÐĩÐ―ÐļŅ‚ŅŒ ÐēŅ‹ÐąÐūŅ€ Ð―ÐūÐēҋ҅ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē</translatorcomment> <translation>Alt+R</translation> </message> <message> <source>&Reset</source> <translation>&ÐžŅ‚ÐžÐĩÐ―ÐļŅ‚ŅŒ</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translatorcomment>ÐēŅ‹ÐąŅ€Ð°Ņ‚ŅŒ ÐēҁÐĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹</translatorcomment> <translation>Alt+S</translation> </message> <message> <source>&Select All</source> <translation>&Ð’Ņ‹ÐąŅ€Ð°Ņ‚ŅŒ Ðēҁґ</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translatorcomment>ŅÐ―ŅŅ‚ŅŒ ÐūŅ‚ÐžÐĩŅ‚ÐšÐļ ÐēŅ‹ÐąÐūŅ€Ð° ҁÐū ÐēҁÐĩŅ… КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē</translatorcomment> <translation>Alt+D</translation> </message> <message> <source>&Deselect All</source> <translation>&ÐĄÐ―ŅŅ‚ŅŒ ÐūŅ‚ÐžÐĩŅ‚ÐšÐļ ÐēŅ‹ÐąÐūŅ€Ð° ҁÐū ÐēҁÐĩŅ… КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē</translation> </message> <message> <source>&Browse QBSP files</source> <translation>&ÐžÐąÐ·ÐūŅ€ Ņ„Ð°ÐđÐŧÐūÐē QBSP</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>Ð­Ņ‚ÐūŅ‚ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ заÐđÐžŅ‘Ņ‚ ÐŋŅ€ÐļÐąÐŧÐļзÐļŅ‚ÐĩÐŧŅŒÐ―Ðū %1 Ð―Ð° ÐķÐĩŅŅ‚ÐšÐūО ÐīÐļŅÐšÐĩ.</translation> </message> <message> <source>Select Components</source> <translation>Ð’Ņ‹ÐąÐūŅ€ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>ПÐūÐķаÐŧ҃ÐđŅŅ‚Ð°, ÐēŅ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹, КÐūŅ‚ÐūҀҋÐĩ ÐēŅ‹ Ņ…ÐūŅ‚ÐļŅ‚Ðĩ ÐūÐąÐ―ÐūÐēÐļŅ‚ŅŒ.</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>ПÐūÐķаÐŧ҃ÐđŅŅ‚Ð°, ÐēŅ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹, КÐūŅ‚ÐūҀҋÐĩ ÐēŅ‹ Ņ…ÐūŅ‚ÐļŅ‚Ðĩ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ.</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>ПÐūÐķаÐŧ҃ÐđŅŅ‚Ð°, ÐēŅ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹, КÐūŅ‚ÐūҀҋÐĩ ÐēŅ‹ Ņ…ÐūŅ‚ÐļŅ‚Ðĩ ҃ÐīаÐŧÐļŅ‚ŅŒ.</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated.</source> <translation>Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹ ÐīÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ. ДÐŧŅ ҃ÐīаÐŧÐĩÐ―ÐļŅ ҃ÐķÐĩ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð―Ņ‹Ņ… КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē ŅÐ―ÐļОÐļŅ‚Ðĩ ÐūŅ‚ÐžÐĩŅ‚ÐšÐļ ÐēŅ‹ÐąÐūŅ€Ð°. ÐĢÐķÐĩ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð―Ņ‹Ðĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹ Ð―Ðĩ ÐąŅƒÐīŅƒŅ‚ ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―Ņ‹.</translation> </message> <message> <source>To install new compressed repository, browse the repositories from your computer</source> <translation>ДÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ð―ÐūÐēÐūÐģÐū Ņ…Ņ€Ð°Ð―ÐļÐŧÐļŅ‰Ð° ŅƒÐšÐ°ÐķÐļŅ‚Ðĩ ÐŋŅƒŅ‚ŅŒ К Ð―ÐĩÐžŅƒ Ð―Ð° ÐēÐ°ŅˆÐĩО КÐūОÐŋŅŒŅŽŅ‚ÐĩŅ€Ðĩ</translation> </message> <message> <source>Open File</source> <translation>ÐžŅ‚ÐšŅ€Ņ‹Ņ‚ÐļÐĩ Ņ„Ð°ÐđÐŧа</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>В ÐūÐŋÐĩŅ€Ð°Ņ†ÐļÐļ ÂŦ%1Âŧ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļÐžŅ‹Ðđ ÐūÐąŅŠÐĩÐšŅ‚ ŅƒŅŅ‚Ð°Ð―ÐūÐē҉ÐļКа ÐŋŅƒŅŅ‚.</translation> </message> <message> <source><to be saved installer key name> <executable> [argument1] [argument2] [...]</source> <translation><ҁÐūŅ…Ņ€Ð°Ð―ŅÐĩОÐūÐĩ ÐļÐžŅ КÐŧŅŽŅ‡Ð° ŅƒŅŅ‚Ð°Ð―ÐūÐē҉ÐļКа> <ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžÐ°> [ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ1] [ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ2] [...]</translation> </message> <message> <source>Cannot save the output of "%1" to an empty installer key value.</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ҁÐūŅ…Ņ€Ð°Ð―ÐļŅ‚ŅŒ ÐēŅ‹ÐēÐūÐī ÂŦ%1Âŧ Ðē ÐŋŅƒŅŅ‚ÐūÐĩ Ð·Ð―Ð°Ņ‡ÐĩÐ―ÐļÐĩ КÐŧŅŽŅ‡Ð° ŅƒŅŅ‚Ð°Ð―ÐūÐē҉ÐļКа.</translation> </message> <message> <source>File "%1" does not exist or is not an executable binary.</source> <translation>ÐĪаÐđÐŧ ÂŦ%1Âŧ Ð―Ðĩ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚ ÐļÐŧÐļ Ð―Ðĩ ŅÐēÐŧŅÐĩŅ‚ŅŅ ÐļҁÐŋÐūÐŧÐ―ŅÐĩÐžŅ‹Ðž.</translation> </message> <message> <source>Running "%1" resulted in a crash.</source> <translation>ЗаÐŋŅƒŅÐš ÂŦ%1Âŧ заÐēÐĩŅ€ŅˆÐļÐŧŅŅ ÐšŅ€Ð°Ņ…ÐūО.</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source><source> <target> ["forceOverwrite"]</source> <translation><Ðļҁ҂ÐūŅ‡Ð―ÐļК> <Ð―Ð°Ð·Ð―Ð°Ņ‡ÐĩÐ―ÐļÐĩ> ["forceOverwrite"]</translation> </message> <message> <source>Invalid argument in %1: Third argument needs to be forceOverwrite, if specified.</source> <translation>НÐĩÐīÐūÐŋŅƒŅŅ‚ÐļÐžŅ‹Ðđ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ Ðē %1: ҂ҀÐĩŅ‚ŅŒÐļО ÐŋÐ°Ņ€Ð°ÐžÐĩ҂ҀÐūО ÐīÐūÐŧÐķÐĩÐ― ÐąŅ‹Ņ‚ŅŒ "forceOverwrite", ÐĩҁÐŧÐļ ÐūÐ― заÐīÐ°Ð―.</translation> </message> <message> <source>Invalid argument in %1: Directory "%2" is invalid.</source> <translation>НÐĩÐīÐūÐŋŅƒŅŅ‚ÐļÐžŅ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ Ðē %1: ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%2Âŧ Ð―ÐĩÐēÐĩŅ€ÐĩÐ―.</translation> </message> <message> <source>Cannot create directory "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ.</translation> </message> <message> <source>Failed to overwrite "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩзаÐŋÐļŅÐ°Ņ‚ŅŒ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot copy file "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅÐšÐūÐŋÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ.</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation>НÐĩÐēÐĩŅ€Ð―ÐūÐĩ ҇ÐļҁÐŧÐū ŅÐŧÐĩОÐĩÐ―Ņ‚ÐūÐē task.</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ Ðē ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Cannot backup file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Failed to overwrite file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩзаÐŋÐļŅÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot write desktop entry to "%1".</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū заÐŋÐļŅÐ°Ņ‚ŅŒ Desktop Entry Ðē ÂŦ%1Âŧ.</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Cannot create link from "%1" to "%2".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ҁҁҋÐŧÐšŅƒ ҁ ÂŦ%1Âŧ Ð―Ð° ÂŦ%2Âŧ.</translation> </message> <message> <source>Cannot remove link from "%1" to "%2".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ ҁҁҋÐŧÐšŅƒ ҁ ÂŦ%1Âŧ Ð―Ð° ÂŦ%2Âŧ.</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Unknown exception caught: %1.</source> <translation>ВÐūÐ·Ð―ÐļКÐŧÐū Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―ÐūÐĩ ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ: %1.</translation> </message> <message> <source>Cannot set permissions for file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ ÐŋŅ€Ð°Ðēа ÐīÐūŅŅ‚ŅƒÐŋа К Ņ„Ð°ÐđÐŧ҃ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot remove file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot move file "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩОÐĩҁ҂ÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Installer at "%1" needs to be an offline one.</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐē҉ÐļК Ðē ÂŦ%1Âŧ ÐīÐūÐŧÐķÐĩÐ― ÐąŅ‹Ņ‚ŅŒ Ðū҄҄ÐŧаÐđÐ―ÐūÐēŅ‹Ðž.</translation> </message> <message> <source>Cannot open file "%1" for reading.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ.</translation> </message> <message> <source>Cannot read file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€Ðū҇ÐļŅ‚Ð°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> <message> <source>Cannot create target directory: "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ҆ÐĩÐŧÐĩÐēÐūÐđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ.</translation> </message> <message> <source>Removing file "%1".</source> <translation>ÐĢÐīаÐŧÐĩÐ―ÐļÐĩ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot remove file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot remove directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source><target> <link location> [target arguments] ["workingDirectory=..."] ["iconPath=..."] ["iconId=..."] ["description=..."]</source> <translation><҆ÐĩÐŧҌ> <Ņ€Ð°Ð·ÐžÐĩ҉ÐĩÐ―ÐļÐĩ ҁҁҋÐŧКÐļ> [ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ ҆ÐĩÐŧÐļ] ["workingDirectory=..."] ["iconPath=..."] ["iconId=..."] ["description=..."]</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Failed to overwrite "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩзаÐŋÐļŅÐ°Ņ‚ŅŒ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot create link "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ҁҁҋÐŧÐšŅƒ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>ÐžŅ‚ÐžÐĩÐ―ÐĩÐ―Ðū</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ Ņ…Ðĩ҈-ҁÐļÐģÐ―Ð°Ņ‚ŅƒŅ€Ņƒ.</translation> </message> <message> <source>Download Error</source> <translation>ÐžŅˆÐļÐąÐšÐ° заÐģŅ€ŅƒÐ·ÐšÐļ</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€ÐūÐēÐĩŅ€ÐļŅ‚ŅŒ ҆ÐĩÐŧÐūŅŅ‚Ð―ÐūŅŅ‚ŅŒ Ņ…ÐĩŅˆÐ° Ðē ÐŋŅ€Ðū҆ÐĩҁҁÐĩ заÐģŅ€ŅƒÐ·ÐšÐļ. ПÐūÐķаÐŧ҃ÐđŅŅ‚Ð°, ÐŋÐūÐēŅ‚ÐūŅ€ÐļŅ‚Ðĩ ÐūÐŋÐĩŅ€Ð°Ņ†ÐļŅŽ.</translation> </message> <message> <source>Cannot verify Hash</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋŅ€ÐūÐēÐĩŅ€ÐļŅ‚ŅŒ Ņ…Ðĩ҈</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋÐūÐŧŅƒŅ‡ÐļŅ‚ŅŒ Ð°Ņ€Ņ…ÐļÐēŅ‹ :%1 ÐžŅˆÐļÐąÐšÐ° Ðē ÐŋŅ€Ðū҆ÐĩҁҁÐĩ заÐģŅ€ŅƒÐ·ÐšÐļ %2</translation> </message> <message> <source>Cannot download archive %1: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ Ð°Ņ€Ņ…ÐļÐē ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Downloading archive "%1" for component %2.</source> <translation>ЗаÐģŅ€ŅƒÐ·ÐšÐ° Ð°Ņ€Ņ…ÐļÐēа ÂŦ%1Âŧ ÐīÐŧŅ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ð° ÂŦ%2Âŧ.</translation> </message> <message> <source>Scheme %1 not supported (URL: %2).</source> <translation>ÐĄŅ…ÐĩОа %1 Ð―Ðĩ ÐŋÐūÐīÐīÐĩŅ€ÐķÐļÐēаÐĩŅ‚ŅŅ (URL: %2).</translation> </message> <message> <source>Cannot find component for %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ð―Ð°ÐđŅ‚Ðļ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ ÐīÐŧŅ %1.</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Pause and resume not supported by network transfers.</source> <translation>ÐŸŅ€ÐļÐūŅŅ‚Ð°Ð―ÐūÐēКа Ðļ ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļÐĩ Ð―Ðĩ ÐŋÐūÐīÐīÐĩŅ€ÐķÐļÐēÐ°ŅŽŅ‚ŅŅ ҁÐĩŅ‚ÐĩÐēŅ‹ÐžÐļ ÐŋŅ€ÐūŅ‚ÐūКÐūÐŧаОÐļ.</translation> </message> <message> <source>Target file "%1" already exists but is not a file.</source> <translation>ÂŦ%1Âŧ ҃ÐķÐĩ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚, Ð―Ðū Ð―Ðĩ ŅÐēÐŧŅÐĩŅ‚ŅŅ Ņ„Ð°ÐđÐŧÐūО.</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>File "%1" not open for writing: %2</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> <message> <source>Writing to file "%1" failed: %2</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ Ðē ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Redirect loop detected for "%1".</source> <translation>ÐžÐąÐ―Ð°Ņ€ŅƒÐķÐĩÐ―Ðū КÐūÐŧŅŒŅ†Ðū ÐŋÐĩŅ€ÐĩÐ―Ð°ÐŋŅ€Ð°ÐēÐŧÐĩÐ―ÐļÐđ ÂŦ%1Âŧ.</translation> </message> <message> <source>Checksum mismatch detected for "%1".</source> <translation>ÐžÐąÐ―Ð°Ņ€ŅƒÐķÐĩÐ―Ðū Ð―ÐĩҁÐūÐēÐŋаÐīÐĩÐ―ÐļÐĩ КÐūÐ―Ņ‚Ņ€ÐūÐŧŅŒÐ―ÐūÐđ ŅŅƒÐžÐžŅ‹ ÂŦ%1Âŧ.</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <translation>ВÐūÐ·Ð―ÐļКÐŧа Ðū҈ÐļÐąÐšÐ° ҁÐĩŅ‚Ðļ ÐŋŅ€Ðļ заÐģŅ€ŅƒÐ·ÐšÐĩ ÂŦ%1Âŧ: %2.</translation> </message> <message> <source>Unknown network error while downloading "%1".</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>ВÐūÐ·Ð―ÐļКÐŧа Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―Ð°Ņ Ðū҈ÐļÐąÐšÐ° ҁÐĩŅ‚Ðļ ÐēÐū ÐēŅ€ÐĩÐžŅ заÐģŅ€ŅƒÐ·ÐšÐļ ÂŦ%1Âŧ.</translation> </message> <message> <source>Network transfers canceled.</source> <translation>ÐĄÐĩŅ‚ÐĩÐēŅ‹Ðĩ ÐŋÐĩŅ€ÐĩÐīÐ°Ņ‡Ðļ ÐūŅ‚ÐžÐĩÐ―ÐĩÐ―Ņ‹.</translation> </message> <message> <source>Invalid source URL "%1": %2</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðđ URL Ðļҁ҂ÐūŅ‡Ð―ÐļКа ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Cannot start detached: "%1"</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋŅƒŅŅ‚ÐļŅ‚ŅŒ Ðū҂҆ÐĩÐŋÐŧŅ‘Ð―Ð―Ņ‹Ðž: ÂŦ%1Âŧ</translation> </message> <message> <source>Cannot start: "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋŅƒŅŅ‚ÐļŅ‚ŅŒ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Program crashed: "%1"</source> <translation>ÐŸŅ€ÐūÐģŅ€Ð°ÐžÐžÐ° заÐēÐĩŅ€ŅˆÐļÐŧÐ°ŅŅŒ ÐšŅ€Ð°Ņ…ÐūО: ÂŦ%1Âŧ</translation> </message> <message> <source>Execution failed (Unexpected exit code: %1): "%2"</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐļҁÐŋÐūÐŧÐ―ÐļŅ‚ŅŒ (Ð―ÐĩÐūÐķÐļÐīÐ°Ð―Ð―Ņ‹Ðđ КÐūÐī заÐēÐĩŅ€ŅˆÐĩÐ―ÐļŅ: %1): ÂŦ%2Âŧ</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open archive "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ð°Ņ€Ņ…ÐļÐē ÂŦ%1Âŧ ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ: %2</translation> </message> <message> <source>Error while extracting archive "%1": %2</source> <translation>ÐžŅˆÐļÐąÐšÐ° ÐļзÐēÐŧÐĩ҇ÐĩÐ―ÐļŅ Ðļз Ð°Ņ€Ņ…ÐļÐēа ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Unknown exception caught while extracting "%1".</source> <translation>В ÐŋŅ€Ðū҆ÐĩҁҁÐĩ ÐļзÐēÐŧÐĩ҇ÐĩÐ―ÐļŅ ÂŦ%1Âŧ ÐēÐūÐ·Ð―ÐļКÐŧÐū Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―ÐūÐĩ ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ.</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Cannot get package manager core.</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋÐūÐŧŅƒŅ‡ÐļŅ‚ŅŒ ŅÐīŅ€Ðū ОÐĩÐ―ÐĩÐīÐķÐĩŅ€Ð° ÐŋаКÐĩŅ‚ÐūÐē.</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>ДÐŧŅ ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļŅ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļОÐū заÐēÐĩŅ€ŅˆÐļŅ‚ŅŒ ÐŋŅ€Ðū҆Ðĩҁҁ %1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>ДÐŧŅ ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļŅ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļОÐū заÐēÐĩŅ€ŅˆÐļŅ‚ŅŒ ŅŅ‚Ðļ ÐŋŅ€Ðū҆Ðĩҁҁҋ: %1</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%1 Ðļз %2</translation> </message> <message> <source>%1 received.</source> <translation>%1 ÐŋÐūÐŧŅƒŅ‡ÐĩÐ―Ðū.</translation> </message> <message> <source>(%1/sec)</source> <translation>(%1/ҁ)</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n ÐīÐĩÐ―ŅŒ, </numerusform> <numerusform>%n ÐīÐ―Ņ, </numerusform> <numerusform>%n ÐīÐ―ÐĩÐđ, </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n Ņ‡Ð°Ņ, </numerusform> <numerusform>%n Ņ‡Ð°ŅÐ°, </numerusform> <numerusform>%n Ņ‡Ð°ŅÐūÐē, </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n ОÐļÐ―ŅƒŅ‚Ð°</numerusform> <numerusform>%n ОÐļÐ―ŅƒŅ‚Ņ‹</numerusform> <numerusform>%n ОÐļÐ―ŅƒŅ‚</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n ҁÐĩÐšŅƒÐ―Ðīа</numerusform> <numerusform>%n ҁÐĩÐšŅƒÐ―ÐīŅ‹</numerusform> <numerusform>%n ҁÐĩÐšŅƒÐ―Ðī</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - ÐūŅŅ‚Ð°ÐŧÐūҁҌ %1%2%3%4.</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - ÐēŅ€ÐĩÐžŅ ÐūКÐūÐ―Ņ‡Ð°Ð―ÐļŅ заÐģŅ€ŅƒÐ·ÐšÐļ Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―Ðū.</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>ЗаÐēÐĩŅ€ŅˆÐĩÐ―ÐļÐĩ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ %1</translation> </message> <message> <source>Restart</source> <translation>ПÐĩŅ€ÐĩзаÐŋŅƒŅŅ‚ÐļŅ‚ŅŒ</translation> </message> <message> <source>Run %1 now.</source> <translation>ЗаÐŋŅƒŅŅ‚ÐļŅ‚ŅŒ %1 ҁÐĩÐđŅ‡Ð°Ņ.</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа %1 Ð―Ðĩ ҃ÐīаÐŧÐ°ŅŅŒ.</translation> </message> <message> <source>Click %1 to exit the %2 Wizard.</source> <translation>НаÐķОÐļŅ‚Ðĩ ÂŦ%1Âŧ ÐīÐŧŅ Ðēҋ҅ÐūÐīа Ðļз ÐžÐ°ŅŅ‚ÐĩŅ€Ð° %2.</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable.</source> <translation>ÐÐ°ŅŅ‚Ņ€ÐūÐđКÐļ Ņ‚ÐūÐŧŅŒÐšÐū ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Failed to write settings.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ Ð―Ð°ŅŅ‚Ņ€ÐūÐđКÐļ.</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source><source path> [vendor prefix]</source> <translation><ÐŋŅƒŅ‚ŅŒ К Ðļҁ҅ÐūÐīÐ―ÐļКаО> [ÐŋŅ€ÐĩŅ„ÐļÐšŅ ÐēÐĩÐ―ÐīÐūŅ€Ð°]</translation> </message> <message> <source>Invalid Argument: source directory must not be empty.</source> <translation>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðđ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ: Ðļҁ҅ÐūÐīÐ―Ņ‹Ðđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ Ð―Ðĩ ОÐūÐķÐĩŅ‚ ÐąŅ‹Ņ‚ŅŒ ÐŋŅƒŅŅ‚Ņ‹Ðž.</translation> </message> <message> <source>Cannot backup file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ Ņ€ÐĩзÐĩŅ€ÐēÐ―ŅƒŅŽ КÐūÐŋÐļŅŽ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Failed to overwrite "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩзаÐŋÐļŅÐ°Ņ‚ŅŒ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Failed to copy file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ŅÐšÐūÐŋÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot create directory "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҁÐūзÐīÐ°Ņ‚ŅŒ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÂŦ%1Âŧ: %2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>ДÐūÐąŅ€Ðū ÐŋÐūÐķаÐŧÐūÐēÐ°Ņ‚ŅŒ Ðē ÐžÐ°ŅŅ‚ÐĩŅ€ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ %1.</translation> </message> <message> <source>Add or remove components</source> <translation>ДÐūÐąÐ°ÐēÐŧÐĩÐ―ÐļÐĩ ÐļÐŧÐļ ҃ÐīаÐŧÐĩÐ―ÐļÐĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē</translation> </message> <message> <source>Update components</source> <translation>ÐžÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē</translation> </message> <message> <source>Remove all components</source> <translation>ÐĢÐīаÐŧÐĩÐ―ÐļÐĩ ÐēҁÐĩŅ… КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>ПÐūÐŧŅƒŅ‡ÐĩÐ―ÐļÐĩ ÐļÐ―Ņ„ÐūŅ€ÐžÐ°Ņ†ÐļÐļ Ðļз ҃ÐīаÐŧŅ‘Ð―Ð―Ņ‹Ņ… Ðļҁ҂ÐūŅ‡Ð―ÐļКÐūÐē...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>ДÐŧŅ ÐēŅ‹ÐŋÐūÐŧÐ―ÐĩÐ―ÐļŅ ŅŅ‚ÐūÐģÐū ÐīÐĩÐđҁ҂ÐēÐļŅ Ð―ŅƒÐķÐ―Ðū Ņ…ÐūŅ‚Ņ ÐąŅ‹ ÐūÐīÐ―Ðū ÐēКÐŧŅŽŅ‡Ņ‘Ð―Ð―ÐūÐĩ Ðļ ÐīÐĩÐđҁ҂ÐēŅƒŅŽŅ‰ÐĩÐĩ Ņ…Ņ€Ð°Ð―ÐļÐŧÐļ҉Ðĩ.</translation> </message> <message> <source>No updates available.</source> <translation>НÐĩŅ‚ ÐīÐūŅŅ‚ŅƒÐŋÐ―Ņ‹Ņ… ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐđ.</translation> </message> <message> <source> Only local package management available.</source> <translation> ДÐūŅŅ‚ŅƒÐŋÐ―Ðū Ņ‚ÐūÐŧŅŒÐšÐū ÐŧÐūКаÐŧŅŒÐ―ÐūÐĩ ҃ÐŋŅ€Ð°ÐēÐŧÐĩÐ―ÐļÐĩ ÐŋаКÐĩŅ‚Ð°ÐžÐļ.</translation> </message> <message> <source>Quit</source> <translation>Ð’Ņ‹ÐđŅ‚Ðļ</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>ЛÐļ҆ÐĩÐ―Ð·ÐļÐūÐ―Ð―ÐūÐĩ ҁÐūÐģÐŧÐ°ŅˆÐĩÐ―ÐļÐĩ</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translation>Alt+A</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>ПÐūÐķаÐŧ҃ÐđŅŅ‚Ð°, ÐŋŅ€Ðū҇ÐļŅ‚Ð°ÐđŅ‚Ðĩ ҁÐŧÐĩÐīŅƒŅŽŅ‰ÐĩÐĩ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐūÐ―Ð―ÐūÐĩ ҁÐūÐģÐŧÐ°ŅˆÐĩÐ―ÐļÐĩ. Ð’Ņ‹ ÐīÐūÐŧÐķÐ―Ņ‹ ҁÐūÐģÐŧÐ°ŅÐļŅ‚ŅŒŅŅ ҁÐū ÐēҁÐĩОÐļ ҃ҁÐŧÐūÐēÐļŅÐžÐļ ŅŅ‚ÐūÐģÐū ҁÐūÐģÐŧÐ°ŅˆÐĩÐ―ÐļŅ ÐŋÐĩŅ€ÐĩÐī ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļÐĩО ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ.</translation> </message> <message> <source>I accept the license.</source> <translation>ÐŊ ҁÐūÐģÐŧÐ°ŅÐĩÐ―(а) ҁ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐĩÐđ.</translation> </message> <message> <source>I do not accept the license.</source> <translation>ÐŊ Ð―Ðĩ ҁÐūÐģÐŧÐ°ŅÐĩÐ―(а) ҁ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐĩÐđ.</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>ПÐūÐķаÐŧ҃ÐđŅŅ‚Ð°, ÐŋŅ€Ðū҇ÐļŅ‚Ð°ÐđŅ‚Ðĩ ҁÐŧÐĩÐīŅƒŅŽŅ‰ÐļÐĩ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐūÐ―Ð―Ņ‹Ðĩ ҁÐūÐģÐŧÐ°ŅˆÐĩÐ―ÐļŅ. Ð’Ņ‹ ÐīÐūÐŧÐķÐ―Ņ‹ ҁÐūÐģÐŧÐ°ŅÐļŅ‚ŅŒŅŅ ҁÐū ÐēҁÐĩОÐļ ҃ҁÐŧÐūÐēÐļŅÐžÐļ ŅŅ‚ÐļŅ… ҁÐūÐģÐŧÐ°ŅˆÐĩÐ―ÐļÐđ ÐŋÐĩŅ€ÐĩÐī ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļÐĩО ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ.</translation> </message> <message> <source>I accept the licenses.</source> <translation>ÐŊ ҁÐūÐģÐŧÐ°ŅÐĩÐ―(а) ҁ ÐŧÐļ҆ÐĩÐ―ÐļÐĩÐđ.</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translation>Alt+D</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>ÐŊ Ð―Ðĩ ҁÐūÐģÐŧÐ°ŅÐĩÐ―(а) ҁ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐĩÐđ.</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>НÐĩ Ð―Ð°ÐđÐīÐĩÐ―Ņ‹ Ņ„Ð°ÐđÐŧŅ‹ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐļ ÐīÐŧŅ КÐūÐŋÐļŅ€ÐūÐēÐ°Ð―ÐļŅ.</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>В ÐūÐŋÐĩŅ€Ð°Ņ†ÐļÐļ ÂŦ%1Âŧ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļÐžŅ‹Ðđ ÐūÐąŅŠÐĩÐšŅ‚ ŅƒŅŅ‚Ð°Ð―ÐūÐē҉ÐļКа ÐŋŅƒŅŅ‚.</translation> </message> <message> <source>No license files found to delete.</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐļ: Ņ„Ð°ÐđÐŧ Ð―Ðĩ Ð―Ð°ÐđÐīÐĩÐ―.</translation> </message> <message> <source>Can not write license file "%1".</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÐŧÐļ҆ÐĩÐ―Ð·ÐļÐļ ÂŦ%1Âŧ.</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>ÐžŅ‚ŅŅƒŅ‚ŅŅ‚Ðē҃ÐĩŅ‚ ОÐĩÐ―ÐĩÐīÐķÐĩŅ€ ÐŋаКÐĩŅ‚ÐūÐē.</translation> </message> <message> <source>Preparing meta information download...</source> <translation>ПÐūÐīÐģÐūŅ‚ÐūÐēКа К заÐģŅ€ŅƒÐ·ÐšÐĩ ОÐĩŅ‚Ð°ÐīÐ°Ð―Ð―Ņ‹Ņ…...</translation> </message> <message> <source>Unpacking compressed repositories. This may take a while...</source> <translation>Ð Ð°ŅÐŋаКÐūÐēКа ҁÐķÐ°Ņ‚Ņ‹Ņ… Ņ…Ņ€Ð°Ð―ÐļÐŧÐļ҉. Ð­Ņ‚Ðū ОÐūÐķÐĩŅ‚ Ð·Ð°Ð―ŅŅ‚ŅŒ Ð―ÐĩКÐūŅ‚ÐūŅ€ÐūÐĩ ÐēŅ€ÐĩÐžŅ...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>ЗаÐģŅ€ŅƒÐ·ÐšÐ° ОÐĩŅ‚Ð°ÐīÐ°Ð―Ð―Ņ‹Ņ… ÐūŅ‚ÐžÐĩÐ―ÐĩÐ―Ð°.</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>ÐŸŅ€ÐūÐšŅÐļ-ҁÐĩŅ€ÐēÐĩŅ€Ņƒ ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļŅ.</translation> </message> <message> <source>Authentication failed.</source> <translation>ÐžŅˆÐļÐąÐšÐ° Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļÐļ.</translation> </message> <message> <source>Unknown exception during download.</source> <translation>ВÐūÐ·Ð―ÐļКÐŧÐū Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―ÐūÐĩ ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ ÐēÐū ÐēŅ€ÐĩÐžŅ заÐģŅ€ŅƒÐ·ÐšÐļ.</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>ПÐūÐŧŅƒŅ‡ÐĩÐ―ÐļÐĩ ОÐĩŅ‚Ð°ÐīÐ°Ð―Ð―Ņ‹Ņ… Ðļз ÐēÐ―ÐĩŅˆÐ―ÐĩÐģÐū Ņ…Ņ€Ð°Ð―ÐļÐŧÐļŅ‰Ð°...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ Ņ…Ņ€Ð°Ð―ÐļÐŧÐļŅ‰Ð°.</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>ВÐūÐ·Ð―ÐļКÐŧÐū Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―ÐūÐĩ ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ ÐēÐū ÐēŅ€ÐĩÐžŅ ÐļзÐēÐŧÐĩ҇ÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Extracting meta information...</source> <translation>ИзÐēÐŧÐĩ҇ÐĩÐ―ÐļÐĩ ОÐĩŅ‚Ð°ÐīÐ°Ð―Ð―Ņ‹Ņ…...</translation> </message> <message> <source>Error while extracting archive "%1": %2</source> <translation>ÐžŅˆÐļÐąÐšÐ° ÐļзÐēÐŧÐĩ҇ÐĩÐ―ÐļŅ Ðļз Ð°Ņ€Ņ…ÐļÐēа ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Unknown exception caught while extracting archive "%1".</source> <translation>В ÐŋŅ€Ðū҆ÐĩҁҁÐĩ ÐļзÐēÐŧÐĩ҇ÐĩÐ―ÐļŅ Ðļз Ð°Ņ€Ņ…ÐļÐēа ÂŦ%1Âŧ ÐēÐūÐ·Ð―ÐļКÐŧÐū Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―ÐūÐĩ ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ.</translation> </message> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source>Error writing Maintenance Tool</source> <translation>ÐžŅˆÐļÐąÐšÐ° заÐŋÐļҁÐļ Maintenance Tool</translation> </message> <message> <source> Downloading packages...</source> <translation> ЗаÐģŅ€ŅƒÐ·ÐšÐ° ÐŋаКÐĩŅ‚ÐūÐē...</translation> </message> <message> <source>All downloads finished.</source> <translation>Ð’ŅÐĩ заÐģŅ€ŅƒÐ·ÐšÐļ заÐēÐĩŅ€ŅˆÐĩÐ―Ņ‹.</translation> </message> <message> <source>Cancelling the Installer</source> <translation>ÐžŅ‚ÐžÐĩÐ―Ð° ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅ‹ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ</translation> </message> <message> <source>Authentication Error</source> <translation>ÐžŅˆÐļÐąÐšÐ° Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļÐļ</translation> </message> <message> <source>Unknown error.</source> <translation>НÐĩÐļзÐēÐĩŅŅ‚Ð―Ð°Ņ Ðū҈ÐļÐąÐšÐ°.</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>НÐĩКÐūŅ‚ÐūҀҋÐĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹ Ð―Ðĩ ÐąŅ‹ÐŧÐļ ҃ÐīаÐŧÐĩÐ―Ņ‹ ÐŋÐūÐŧÐ―ÐūŅŅ‚ŅŒŅŽ: ÐēÐūÐ·Ð―ÐļКÐŧа Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―Ð°Ņ Ðū҈ÐļÐąÐšÐ°.</translation> </message> <message> <source>No installed packages found.</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð―Ņ‹Ðĩ ÐŋаКÐĩ҂ҋ Ð―Ðĩ Ð―Ð°ÐđÐīÐĩÐ―Ņ‹.</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>ДÐūŅŅ‚ŅƒÐŋÐ―Ðū ÐēаÐķÐ―ÐūÐĩ ÐļҁÐŋŅ€Ð°ÐēÐŧÐĩÐ―ÐļÐĩ, ŅÐ―Ð°Ņ‡Ð°Ðŧа заÐŋŅƒŅŅ‚ÐļŅ‚Ðĩ ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅƒ ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>ÐžŅˆÐļÐąÐšÐ° ÐŋŅ€Ðļ ÐŋÐūÐŋŅ‹Ņ‚ÐšÐĩ ÐŋÐūÐēҋҁÐļŅ‚ŅŒ ŅƒŅ€ÐūÐēÐĩÐ―ŅŒ ÐīÐūŅŅ‚ŅƒÐŋа.</translation> </message> <message> <source>Error</source> <translation>ÐžŅˆÐļÐąÐšÐ°</translation> </message> <message> <source>invalid</source> <translation>Ð―ÐĩÐīÐūÐŋŅƒŅŅ‚ÐļÐžÐ°Ņ ÐēÐĩҀҁÐļŅ</translation> </message> <message> <source>Installation canceled by user.</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа ÐūŅ‚ÐžÐĩÐ―ÐĩÐ―Ð° ÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ÐĩÐŧÐĩО.</translation> </message> <message> <source>Some components could not be removed completely because administrative rights could not be acquired: %1.</source> <translation>НÐĩКÐūŅ‚ÐūҀҋÐĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹ Ð―Ðĩ ÐąŅ‹ÐŧÐļ ҃ÐīаÐŧÐĩÐ―Ņ‹ ÐŋÐūÐŧÐ―ÐūŅŅ‚ŅŒŅŽ. ДÐŧŅ ÐļŅ… ÐŋÐūÐŧÐ―ÐūÐģÐū ҃ÐīаÐŧÐĩÐ―ÐļŅ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļÐžŅ‹ ÐŋŅ€Ð°Ðēа АÐīОÐļÐ―ÐļŅŅ‚Ņ€Ð°Ņ‚ÐūŅ€Ð°: %1.</translation> </message> <message> <source>Application not running in Package Manager mode.</source> <translation>ÐŸŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļÐĩ Ð―Ðĩ заÐŋŅƒŅ‰ÐĩÐ―Ðū Ðē Ņ€ÐĩÐķÐļОÐĩ ОÐĩÐ―ÐĩÐīÐķÐĩŅ€Ð° ÐŋаКÐĩŅ‚ÐūÐē.</translation> </message> <message> <source>Application running in Uninstaller mode.</source> <translation>ÐŸŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļÐĩ заÐŋŅƒŅ‰ÐĩÐ―Ðū Ðē Ņ€ÐĩÐķÐļОÐĩ ҃ÐīаÐŧÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ņ€Ð°Ð·Ņ€Ðĩ҈ÐļŅ‚ŅŒ ÐēҁÐĩ заÐēÐļҁÐļОÐūҁ҂Ðļ.</translation> </message> <message> <source>Components about to be removed.</source> <translation>ÐĢÐīаÐŧŅÐĩÐžŅ‹Ðĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ņ‹.</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Error</source> <translation>ÐžŅˆÐļÐąÐšÐ°</translation> </message> <message> <source>Access error</source> <translation>ÐžŅˆÐļÐąÐšÐ° ÐīÐūŅŅ‚ŅƒÐŋа</translation> </message> <message> <source>Format error</source> <translation>ÐžŅˆÐļÐąÐšÐ° Ņ„ÐūŅ€ÐžÐ°Ņ‚ÐļŅ€ÐūÐēÐ°Ð―ÐļŅ</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū заÐŋÐļŅÐ°Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ КÐūÐ―Ņ„ÐļÐģŅƒŅ€Ð°Ņ†ÐļÐļ ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅ‹ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ðē %1: %2</translation> </message> <message> <source>Stop Processes</source> <translation>ÐžŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ ÐŋŅ€Ðū҆Ðĩҁҁҋ</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>ДÐŧŅ ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļŅ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļОÐū ÐūŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ ҁÐŧÐĩÐīŅƒŅŽŅ‰ÐļÐĩ ÐŋŅ€Ðū҆Ðĩҁҁҋ: %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа ÐūŅ‚ÐžÐĩÐ―ÐĩÐ―Ð° ÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ÐĩÐŧÐĩО</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>ПÐĩŅ€ÐĩОÐĩÐ―Ð―Ð°Ņ 'TargetDir' Ð―Ðĩ ÐūÐŋŅ€ÐĩÐīÐĩÐŧÐĩÐ―Ð°.</translation> </message> <message> <source>Preparing the installation...</source> <translation>ПÐūÐīÐģÐūŅ‚ÐūÐēКа К ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐĩ...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋŅ€ÐūÐļзÐēÐĩҁ҂Ðļ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐšŅƒ Ðļз ҁÐĩŅ‚ÐĩÐēÐūÐģÐū Ðļҁ҂ÐūŅ‡Ð―ÐļКа</translation> </message> <message> <source>Creating local repository</source> <translation>ÐĄÐūзÐīÐ°Ņ‘Ņ‚ŅŅ ÐŧÐūКаÐŧŅŒÐ―Ņ‹Ðđ Ņ€ÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐđ</translation> </message> <message> <source> Installation finished!</source> <translation> ÐĢŅŅ‚Ð°Ð―ÐūÐēКа заÐēÐĩŅ€ŅˆÐĩÐ―Ð°!</translation> </message> <message> <source> Installation aborted!</source> <translation> ÐĢŅŅ‚Ð°Ð―ÐūÐēКа ÐŋŅ€ÐĩŅ€ÐēÐ°Ð―Ð°!</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐēŅ‹ÐŋÐūÐŧÐ―ÐļŅ‚ŅŒ ŅŅ‚Ņƒ ÐūÐŋÐĩŅ€Ð°Ņ†ÐļŅŽ ÐŋÐū ҁÐĩŅ‚Ðļ</translation> </message> <message> <source>Removing deselected components...</source> <translation>ÐĢÐīаÐŧÐĩÐ―ÐļÐĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē...</translation> </message> <message> <source> Update finished!</source> <translation> ÐžÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐĩ заÐēÐĩŅ€ŅˆÐĩÐ―Ðū!</translation> </message> <message> <source> Update aborted!</source> <translation> ÐžÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐĩ ÐŋŅ€ÐĩŅ€ÐēÐ°Ð―Ðū!</translation> </message> <message> <source>Unresolved dependencies</source> <translation>НÐĩŅ€Ð°Ð·Ņ€ÐĩŅˆŅ‘Ð―Ð―Ņ‹Ðĩ заÐēÐļҁÐļОÐūҁ҂Ðļ</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>ЗаÐŋÐļҁҌ Maintenance Tool.</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>ÐĄÐąÐūÐđ ÐŋÐūÐļŅÐšÐ° Ðē Ņ„Ð°ÐđÐŧÐĩ %1: %2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation>Maintenance Tool Ð―Ðĩ ŅÐēÐŧŅÐĩŅ‚ŅŅ ÐŋаКÐĩŅ‚ÐūО</translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ ÐīÐ°Ð―Ð―Ņ‹Ðĩ Maintenance Tool Ðē %1: %2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ ÐīÐēÐūÐļŅ‡Ð―Ņ‹Ðĩ ÐīÐ°Ð―Ð―Ņ‹Ðĩ Maintenance Tool Ðē %1: %2</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>ÐĄÐūзÐīÐ°Ð―ÐļÐĩ Maintenance Tool</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>ÐĢÐīаÐŧÐĩÐ―ÐļÐĩ ҃ҁÐŋÐĩŅˆÐ―Ðū заÐēÐĩŅ€ŅˆÐĩÐ―Ðū.</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>ÐĢÐīаÐŧÐĩÐ―ÐļÐĩ ÐŋŅ€ÐĩŅ€ÐēÐ°Ð―Ðū.</translation> </message> <message> <source> Installing component %1</source> <translation> ÐĢŅŅ‚Ð°Ð―ÐūÐēКа КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ð° %1</translation> </message> <message> <source>Installer Error</source> <translation>ÐžŅˆÐļÐąÐšÐ° ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅ‹ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>ÐžŅˆÐļÐąÐšÐ° ÐēÐū ÐēŅ€ÐĩÐžŅ ÐŋŅ€Ðū҆ÐĩŅŅÐ° ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ (%1): %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋÐūÐīÐģÐūŅ‚ÐūÐēÐļŅ‚ŅŒŅŅ К ҃ÐīаÐŧÐĩÐ―ÐļŅŽ</translation> </message> <message> <source>Cannot start uninstall</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū Ð―Ð°Ņ‡Ð°Ņ‚ŅŒ ҃ÐīаÐŧÐĩÐ―ÐļÐĩ</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>ÐžŅˆÐļÐąÐšÐ° Ðē ÐŋŅ€Ðū҆ÐĩҁҁÐĩ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ: %1</translation> </message> <message> <source>Unknown error</source> <translation>НÐĩÐļзÐēÐĩŅŅ‚Ð―Ð°Ņ Ðū҈ÐļÐąÐšÐ°</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ ОÐĩŅ‚Ð°ÐīÐ°Ð―Ð―Ņ‹Ðĩ: %1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐīÐūÐąÐ°ÐēÐļŅ‚ŅŒ ÐļÐ―Ņ„ÐūŅ€ÐžÐ°Ņ†ÐļŅŽ Ðū ÐēŅ€ÐĩОÐĩÐ―Ð―ÐūО ҁÐĩŅ€ÐēÐĩŅ€Ðĩ ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū Ð―Ð°ÐđŅ‚Ðļ ÐļÐ―Ņ„ÐūŅ€ÐžÐ°Ņ†ÐļŅŽ ÐūÐą Ðļҁ҂ÐūŅ‡Ð―ÐļÐšÐ°Ņ… ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Cannot remove data file "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ҃ÐīаÐŧÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÐīÐ°Ð―Ð―Ņ‹Ņ… ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot write maintenance tool to "%1": %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅƒ ÐūÐąŅÐŧ҃ÐķÐļÐēÐ°Ð―ÐļŅ Ðē ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Cannot retrieve remote tree %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐģŅ€ŅƒÐ·ÐļŅ‚ŅŒ ҃ÐīаÐŧŅ‘Ð―Ð―ÐūÐĩ ÐīÐĩŅ€ÐĩÐēÐū: %1.</translation> </message> <message> <source>Failure to read packages from %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€Ðū҇ÐļŅ‚Ð°Ņ‚ŅŒ ÐŋаКÐĩ҂ҋ Ðļз %1.</translation> </message> <message> <source>Dependency cycle between components "%1" and "%2" detected.</source> <translation>ÐžÐąÐ―Ð°Ņ€ŅƒÐķÐĩÐ―Ð° ҆ÐļКÐŧÐļ҇ÐĩŅÐšÐ°Ņ заÐēÐļҁÐļОÐūŅŅ‚ŅŒ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē ÂŦ%1Âŧ Ðļ ÂŦ%2Âŧ.</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа %1</translation> </message> <message> <source>Maintain %1</source> <translation>ÐĄÐĩŅ€ÐēÐļŅÐ―Ņ‹Ðđ Ņ€ÐĩÐķÐļО %1</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>ÐžŅ‚ÐžÐĩÐ―ÐļŅ‚ŅŒ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐšŅƒ?</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>ÐžŅ‚ÐžÐĩÐ―ÐļŅ‚ŅŒ ҃ÐīаÐŧÐĩÐ―ÐļÐĩ?</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>Ð’Ņ‹ÐđŅ‚Ðļ Ðļз ŅƒŅŅ‚Ð°Ð―ÐūÐē҉ÐļКа?</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>Ð’Ņ‹ÐđŅ‚Ðļ Ðļз ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļŅ ҃ÐīаÐŧÐĩÐ―ÐļŅ?</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>Ð’Ņ‹ÐđŅ‚Ðļ Ðļз ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļŅ ÐūÐąŅÐŧ҃ÐķÐļÐēÐ°Ð―ÐļŅ?</translation> </message> <message> <source>Settings</source> <translation>ÐÐ°ŅŅ‚Ņ€ÐūÐđКÐļ</translation> </message> <message> <source>Error</source> <translation>ÐžŅˆÐļÐąÐšÐ°</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>Ð―ÐĩÐēÐūзОÐūÐķÐ―Ðū ÐŋŅ€ÐūÐļзÐēÐĩҁ҂Ðļ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐšŅƒ Ðļз ҁÐĩŅ‚ÐĩÐēÐūÐģÐū Ðļҁ҂ÐūŅ‡Ð―ÐļКа. ПÐūÐķаÐŧ҃ÐđŅŅ‚Ð°, ҁÐūŅ…Ņ€Ð°Ð―ÐļŅ‚Ðĩ ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅƒ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ð―Ð° ÐķŅ‘ŅÐšŅ‚Ðđ ÐīÐļŅÐš Ð’Ð°ŅˆÐĩÐģÐū КÐūОÐŋŅŒŅŽŅ‚ÐĩŅ€Ð°</translation> </message> <message> <source>%1 Question</source> <translatorcomment>ВÐūŅ‚ ŅŅ‚Ðū Ð―ÐĩÐŋÐūÐ―ŅŅ‚Ð―Ð°Ņ Ņ…Ņ€ÐĩÐ―ŅŒ.</translatorcomment> <translation>ВÐūÐŋŅ€Ðūҁ ÐŋÐū %1</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>&ПÐūÐšÐ°Ð·Ð°Ņ‚ŅŒ ÐīÐĩŅ‚Ð°ÐŧÐļ</translation> </message> <message> <source>&Hide Details</source> <translation>&ÐĄÐšŅ€Ņ‹Ņ‚ŅŒ ÐīÐĩŅ‚Ð°ÐŧÐļ</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>&ÐĢÐīаÐŧÐļŅ‚ŅŒ</translation> </message> <message> <source>Uninstalling %1</source> <translation>ÐĢÐīаÐŧÐĩÐ―ÐļÐĩ %1</translation> </message> <message> <source>&Update</source> <translation>&ÐžÐąÐ―ÐūÐēÐļŅ‚ŅŒ</translation> </message> <message> <source>Updating components of %1</source> <translation>ÐžÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļÐĩ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē %1</translation> </message> <message> <source>&Install</source> <translation>&ÐĢŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ</translation> </message> <message> <source>Installing %1</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа %1</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation></translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>ÐŸŅ€ÐūÐšŅÐļ %1 ҂ҀÐĩÐąŅƒÐĩŅ‚ ÐļÐžŅ ÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ÐĩÐŧŅ Ðļ ÐŋÐ°Ņ€ÐūÐŧҌ.</translation> </message> <message> <source>Username:</source> <translation>Ð˜ÐžŅ:</translation> </message> <message> <source>Username</source> <translation>Ð˜ÐžŅ ÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ÐĩÐŧŅ</translation> </message> <message> <source>Password:</source> <translation>ÐŸÐ°Ņ€ÐūÐŧҌ:</translation> </message> <message> <source>Password</source> <translation>ÐŸÐ°Ņ€ÐūÐŧҌ</translation> </message> <message> <source>Proxy Credentials</source> <translation>ÐŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļŅ Ð―Ð° ÐŋŅ€ÐūÐšŅÐļ-ҁÐĩŅ€ÐēÐĩŅ€Ðĩ</translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>ÐĢ&ÐīаÐŧÐļŅ‚ŅŒ</translation> </message> <message> <source>Ready to Uninstall</source> <translation>Ð’ŅŅ‘ ÐģÐūŅ‚ÐūÐēÐū К ҃ÐīаÐŧÐĩÐ―ÐļŅŽ</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>ÐŸŅ€ÐūÐģŅ€Ð°ÐžÐžÐ° ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ ÐģÐūŅ‚ÐūÐēа Ð―Ð°Ņ‡Ð°Ņ‚ŅŒ ҃ÐīаÐŧÐĩÐ―ÐļÐĩ %1 ҁ ÐēÐ°ŅˆÐĩÐģÐū КÐūОÐŋŅŒŅŽŅ‚ÐĩŅ€Ð°. <br><font color="red">ДÐļŅ€ÐĩÐšŅ‚ÐūŅ€ÐļŅ ҁ ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžÐūÐđ %2 ÐąŅƒÐīÐĩŅ‚ ÐŋÐūÐŧÐ―ÐūŅŅ‚ŅŒŅŽ ҃ÐīаÐŧÐĩÐ―Ð°</font>, ÐēКÐŧŅŽŅ‡Ð°Ņ ҁÐūÐīÐĩŅ€ÐķÐļОÐūÐĩ ŅŅ‚ÐūÐđ ÐīÐļŅ€ÐĩÐšŅ‚ÐūŅ€ÐļÐļ!</translation> </message> <message> <source>U&pdate</source> <translation>О&ÐąÐ―ÐūÐēÐļŅ‚ŅŒ</translation> </message> <message> <source>Ready to Update Packages</source> <translation>ГÐūŅ‚ÐūÐē К ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅŽ ÐŋаКÐĩŅ‚ÐūÐē</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>ÐŸŅ€ÐūÐģŅ€Ð°ÐžÐžÐ° ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ ÐģÐūŅ‚ÐūÐēа К ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅŽ Ņ„Ð°ÐđÐŧÐūÐē.</translation> </message> <message> <source>&Install</source> <translation>&ÐĢŅŅ‚Ð°Ð―ÐūÐēÐļŅ‚ŅŒ</translation> </message> <message> <source>Ready to Install</source> <translation>Ð’ŅŅ‘ ÐģÐūŅ‚ÐūÐēÐū К ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐĩ</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>ÐŸŅ€ÐūÐģŅ€Ð°ÐžÐžÐ° ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ ÐģÐūŅ‚ÐūÐēа Ð―Ð°Ņ‡Ð°Ņ‚ŅŒ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐšŅƒ %1 Ð―Ð° ÐēÐ°Ņˆ КÐūОÐŋŅŒŅŽŅ‚ÐĩŅ€.</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>На ÐēŅ‹ÐąŅ€Ð°Ð―Ð―ÐūО ВаОÐļ ÐīÐļŅÐšÐĩ ÐīÐūŅŅ‚Ð°Ņ‚ÐūŅ‡Ð―Ðū ОÐĩŅŅ‚Ð° ÐīÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ. ÐĒÐĩО Ð―Ðĩ ОÐĩÐ―ÐĩÐĩ, ÐŋÐūҁÐŧÐĩ заÐēÐĩŅ€ŅˆÐĩÐ―ÐļŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ð―Ð° ÐīÐļŅÐšÐĩ ÐūŅŅ‚Ð°Ð―ÐĩŅ‚ŅŅ ОÐĩÐ―ŅŒŅˆÐĩ 1% ҁÐēÐūÐąÐūÐīÐ―ÐūÐģÐū ОÐĩŅŅ‚Ð°. %1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>На ÐēŅ‹ÐąŅ€Ð°Ð―Ð―ÐūО ВаОÐļ ÐīÐļŅÐšÐĩ ÐīÐūŅŅ‚Ð°Ņ‚ÐūŅ‡Ð―Ðū ОÐĩŅŅ‚Ð° ÐīÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ. ÐĒÐĩО Ð―Ðĩ ОÐĩÐ―ÐĩÐĩ, ÐŋÐūҁÐŧÐĩ заÐēÐĩŅ€ŅˆÐĩÐ―ÐļŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ð―Ð° ÐīÐļŅÐšÐĩ ÐūŅŅ‚Ð°Ð―ÐĩŅ‚ŅŅ ОÐĩÐ―ŅŒŅˆÐĩ 100 МБ ҁÐēÐūÐąÐūÐīÐ―ÐūÐģÐū ОÐĩŅŅ‚Ð°. %1</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>ДÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ ÐŋÐū҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ %1 ÐīÐļŅÐšÐūÐēÐūÐģÐū ÐŋŅ€ÐūŅŅ‚Ņ€Ð°Ð―ŅŅ‚Ðēа.</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation. %1 are available, while %2 are at least required.</source> <translation>НÐĩÐīÐūŅŅ‚Ð°Ņ‚ÐūŅ‡Ð―Ðū ОÐĩŅŅ‚Ð° Ð―Ð° ÐīÐļŅÐšÐĩ ÐīÐŧŅ ÐēŅ€ÐĩОÐĩÐ―Ð―Ņ‹Ņ… Ņ„Ð°ÐđÐŧÐūÐē Ðļ Ņ„Ð°ÐđÐŧÐūÐē ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ. ДÐūŅŅ‚ŅƒÐŋÐ―Ðū %1, а ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ ОÐļÐ―ÐļÐžŅƒÐž %2.</translation> </message> <message> <source>Not enough disk space to store all selected components! %1 are available while %2 are at least required.</source> <translation>НÐĩÐīÐūŅŅ‚Ð°Ņ‚ÐūŅ‡Ð―Ðū ОÐĩŅŅ‚Ð° Ð―Ð° ÐīÐļŅÐšÐĩ ÐīÐŧŅ ҁÐūŅ…Ņ€Ð°Ð―ÐĩÐ―ÐļŅ ÐēҁÐĩŅ… ÐēŅ‹ÐąŅ€Ð°Ð―Ð―Ņ‹Ņ… КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚ÐūÐē. ДÐūŅŅ‚ŅƒÐŋÐ―Ðū %1, а ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ ОÐļÐ―ÐļÐžŅƒÐž: %2.</translation> </message> <message> <source>Not enough disk space to store temporary files! %1 are available while %2 are at least required.</source> <translation>НÐĩÐīÐūŅŅ‚Ð°Ņ‚ÐūŅ‡Ð―Ðū ОÐĩŅŅ‚Ð° Ð―Ð° ÐīÐļŅÐšÐĩ ÐīÐŧŅ ÐēŅ€ÐĩОÐĩÐ―Ð―Ņ‹Ņ… Ņ„Ð°ÐđÐŧÐūÐē. ДÐūŅŅ‚ŅƒÐŋÐ―Ðū %1, а ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ ОÐļÐ―ÐļÐžŅƒÐž %2.</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Registering file types is only supported on Windows.</source> <translation>РÐĩÐģÐļŅŅ‚Ņ€Ð°Ņ†ÐļŅ Ņ‚ÐļÐŋÐūÐē Ņ„Ð°ÐđÐŧÐūÐē ÐēÐūзОÐūÐķÐ―Ð° Ņ‚ÐūÐŧŅŒÐšÐū Ðē Windows.</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>РÐĩÐģÐļŅŅ‚Ņ€Ð°Ņ†ÐļŅ Ņ‚ÐļÐŋÐūÐē Ņ„Ð°ÐđÐŧÐūÐē: Ð―ÐĩÐīÐūÐŋŅƒŅŅ‚ÐļÐžŅ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ</translation> </message> <message> <source><extension> <command> [description [contentType [icon]]]</source> <translation><Ņ€Ð°ŅŅˆÐļŅ€ÐĩÐ―ÐļÐĩ> <КÐūÐžÐ°Ð―Ðīа> [ÐūÐŋÐļŅÐ°Ð―ÐļÐĩ [Ņ‚ÐļÐŋÐĄÐūÐīÐĩŅ€ÐķÐļОÐūÐģÐū [Ð·Ð―Ð°Ņ‡ÐūК]]]</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€Ðū҇ÐļŅ‚Ð°Ņ‚ŅŒ ÐēҁÐĩ ÐīÐ°Ð―Ð―Ņ‹Ðĩ ÐŋÐūҁÐŧÐĩ ÐūŅ‚ÐŋŅ€Ð°ÐēКÐļ КÐūÐžÐ°Ð―ÐīŅ‹: %1. ОÐķÐļÐīаÐŧÐūҁҌ ÐąÐ°ÐđŅ‚: %2; ÐŋÐūÐŧŅƒŅ‡ÐĩÐ―Ðū ÐąÐ°ÐđŅ‚: %3. ÐžŅˆÐļÐąÐšÐ°: %4</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Cannot open file "%1" for reading: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> <message> <source>Cannot open file "%1" for writing: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ð―Ð° заÐŋÐļҁҌ: %2</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Read failed after %1 bytes: %2</source> <translation>ÐĄÐąÐūÐđ ҇҂ÐĩÐ―ÐļŅ ҁ %1 ÐąÐ°ÐđŅ‚Ð°: %2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>ÐĄÐąÐūÐđ заÐŋÐļҁÐļ ҁ %1 ÐąÐ°ÐđŅ‚Ð°: %2</translation> </message> <message> <source>Cannot open resource %1 for reading.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ€ÐĩŅŅƒŅ€Ņ %1 ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ.</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>ЗаÐēÐĩŅ€ŅˆÐĩÐ―ÐļÐĩ %1 ÐžÐ°ŅŅ‚ÐĩŅ€Ð° ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open script file at %1: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ ҁ҆ÐĩÐ―Ð°Ņ€ÐļŅ ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Exception while loading the component script "%1": %2</source> <translation>ВÐūÐ·Ð―ÐļКÐŧÐū ÐļŅÐšÐŧŅŽŅ‡ÐĩÐ―ÐļÐĩ ÐŋŅ€Ðļ заÐģŅ€ŅƒÐ·ÐšÐĩ ҁ҆ÐĩÐ―Ð°Ņ€ÐļŅ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ð° ÂŦ%1Âŧ: %2</translation> </message> <message> <source>Unknown error.</source> <translation>НÐĩÐļзÐēÐĩŅŅ‚Ð―Ð°Ņ Ðū҈ÐļÐąÐšÐ°.</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>АÐēŅ‚ÐūÐžÐ°Ņ‚Ðļ҇ÐĩŅÐšÐ°Ņ ÐŋÐĩŅ€ÐĩзаÐģŅ€ŅƒÐ·ÐšÐ°: ÐŋÐūÐīŅ…ÐūÐīÐļŅ‚ Ņ‚ÐūÐŧŅŒÐšÐū ÐīÐŧŅ ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅ‹ ÐūÐąÐ―ÐūÐēÐŧÐĩÐ―ÐļŅ ÐļÐŧÐļ ÐīÐŧŅ Ņ€ÐĩÐķÐļОа ОÐĩÐ―ÐĩÐīÐķÐĩŅ€Ð° ÐŋаКÐĩŅ‚ÐūÐē.</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>АÐēŅ‚ÐūÐžÐ°Ņ‚Ðļ҇ÐĩŅÐšÐ°Ņ ÐŋÐĩŅ€ÐĩзаÐģŅ€ŅƒÐ·ÐšÐ°: Ð―ÐĩÐīÐūÐŋŅƒŅŅ‚ÐļÐžŅ‹Ðđ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ</translation> </message> <message> <source>Installer object needed in operation %1 is empty.</source> <translation>ÐžÐąŅŠÐĩÐšŅ‚ ŅƒŅŅ‚Ð°Ð―ÐūÐē҉ÐļКа, Ð―ÐĩÐūÐąŅ…ÐūÐīÐļÐžŅ‹Ðđ Ðē ÐūÐŋÐĩŅ€Ð°Ņ†ÐļÐļ ÂŦ%1Âŧ, ÐŋŅƒŅŅ‚.</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>ÐĄÐĩŅ€ÐēÐĩŅ€ ҂ҀÐĩÐąŅƒÐĩŅ‚ Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļÐļ</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>ÐĄÐ°ÐđŅ‚ ҂ҀÐĩÐąŅƒÐĩŅ‚ ÐēÐēÐĩҁ҂Ðļ ÐļÐžŅ ÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ÐĩÐŧŅ Ðļ ÐŋÐ°Ņ€ÐūÐŧҌ.</translation> </message> <message> <source>Username:</source> <translation>Ð˜ÐžŅ:</translation> </message> <message> <source>Password:</source> <translation>ÐŸÐ°Ņ€ÐūÐŧҌ:</translation> </message> <message> <source>%1 at %2</source> <translation>%1 Ðē %2</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) "%1" calling %2 with arguments "%3".</source> <translation>ÐžŅ‚ŅŅƒŅ‚ŅŅ‚ÐēŅƒŅŽŅ‚ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ(Ņ‹) ÂŦ%1Âŧ ÐŋŅ€Ðļ ÐēŅ‹Ð·ÐūÐēÐĩ ÂŦ%2Âŧ ҁ ÐŋÐ°Ņ€Ð°ÐžÐĩŅ‚Ņ€Ð°ÐžÐļ ÂŦ%3Âŧ.</translation> </message> <message> <source>Current method argument calling "%1" with arguments "%2" is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>ÐĒÐĩÐšŅƒŅ‰ÐļÐđ ҁÐŋÐūҁÐūÐą ÐēŅ‹Ð·ÐūÐēа ÂŦ%1Âŧ ҁ Ð°Ņ€ÐģŅƒÐžÐĩÐ―Ņ‚Ð°ÐžÐļ ÂŦ%2Âŧ Ð―Ðĩ ÐŋÐūÐīÐīÐĩŅ€ÐķÐļÐēаÐĩŅ‚ŅŅ. Ð˜ŅÐŋÐūÐŧŅŒÐ·ŅƒÐđŅ‚Ðĩ set, remove, add_array_value ÐļÐŧÐļ remove_array_value.</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>None of the arguments can be empty: source "%1", target "%2".</source> <translation>Ð’ŅÐĩ Ð°Ņ€ÐģŅƒÐžÐĩÐ―Ņ‚Ņ‹ ÐīÐūÐŧÐķÐ―Ņ‹ ÐąŅ‹Ņ‚ŅŒ Ð―ÐĩÐŋŅƒŅŅ‚Ņ‹ÐžÐļ: Ðļҁ҂ÐūŅ‡Ð―ÐļК ÂŦ%1Âŧ, Ð―Ð°Ð·Ð―Ð°Ņ‡ÐĩÐ―ÐļÐĩ ÂŦ%2Âŧ.</translation> </message> <message> <source>Cannot move file from "%1" to "%2", because the target path exists and is not removable.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩОÐĩҁ҂ÐļŅ‚ŅŒ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ, Ņ‚Ð°Ðš КаК Ņ„Ð°ÐđÐŧ Ð―Ð°Ð·Ð―Ð°Ņ‡ÐĩÐ―ÐļŅ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚ Ðļ Ð―Ðĩ ҃ÐīаÐŧŅÐĩО.</translation> </message> <message> <source>Cannot move file "%1" to "%2": %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩОÐĩҁ҂ÐļŅ‚ŅŒ Ņ„Ð°ÐđÐŧ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> <message> <source>Moving file "%1" to "%2".</source> <translation>ПÐĩŅ€ÐĩОÐĩ҉ÐĩÐ―ÐļÐĩ Ņ„Ð°ÐđÐŧа ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ.</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>ÐŊŅ€ÐŧŅ‹ÐšÐļ ОÐĩÐ―ŅŽ "ÐŸŅƒŅÐš"</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new directory.</source> <translation>Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐŋаÐŋÐšŅƒ Ðē ОÐĩÐ―ŅŽ ÂŦÐŸŅƒŅÐšÂŧ ÐīÐŧŅ Ņ€Ð°Ð·ÐžÐĩ҉ÐĩÐ―ÐļŅ ŅŅ€ÐŧŅ‹ÐšÐūÐē ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅ‹. Ð§Ņ‚ÐūÐąŅ‹ ҁÐūзÐīÐ°Ņ‚ŅŒ Ð―ÐūÐēŅƒŅŽ ÐŋаÐŋÐšŅƒ, ÐēÐēÐĩÐīÐļŅ‚Ðĩ ÐĩŅ‘ ÐļÐžŅ.</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translatorcomment>ÐūŅ‚ÐšŅ€Ņ‹ÐēаÐĩŅ‚ ÐūÐšÐ―Ðū ÐēŅ‹ÐąÐūŅ€Ð° Ņ„Ð°ÐđÐŧа</translatorcomment> <translation>Alt+R</translation> </message> <message> <source>B&rowse...</source> <translation>О&ÐąÐ·ÐūŅ€...</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>Ð’Ņ‹ÐąŅ€Ð°Ð―Ð―Ņ‹Ðđ Ņ„Ð°ÐđÐŧ ÐļÐŧÐļ ҁÐļОÐēÐūÐŧŅŒÐ―Ð°Ņ ҁҁҋÐŧКа ҃ÐķÐĩ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚. Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐīŅ€ŅƒÐģÐūÐđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ.</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>ÐŸŅƒŅ‚ŅŒ К ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ҃ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ð―Ðĩ ОÐūÐķÐĩŅ‚ ÐąŅ‹Ņ‚ŅŒ ÐūŅ‚Ð―ÐūҁÐļŅ‚ÐĩÐŧŅŒÐ―Ņ‹Ðž. ЗаÐīаÐđŅ‚Ðĩ Ð°ÐąŅÐūÐŧŅŽŅ‚Ð―Ņ‹Ðđ ÐŋŅƒŅ‚ŅŒ.</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>В ҁ҂ҀÐūКÐĩ ÐŋŅƒŅ‚Ðļ ÐļÐŧÐļ Ðē ÐļОÐĩÐ―Ðļ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģа ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ ҁÐūÐīÐĩŅ€ÐķÐļŅ‚ŅŅ ҁÐļОÐēÐūÐŧ, Ð―Ðĩ ÐūŅ‚Ð―ÐūŅŅŅ‰ÐļÐđŅŅ К ASCII. В Ð―Ð°ŅŅ‚ÐūŅŅ‰ÐĩÐĩ ÐēŅ€ÐĩÐžŅ Ņ‚Ð°ÐšÐļÐĩ ҁÐļОÐēÐūÐŧŅ‹ Ð―Ðĩ ÐŋÐūÐīÐīÐĩŅ€ÐķÐļÐēÐ°ŅŽŅ‚ŅŅ. Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐīŅ€ŅƒÐģÐūÐđ ÐŋŅƒŅ‚ŅŒ ÐļÐŧÐļ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ.</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>ÐĢŅŅ‚Ð°Ð―ÐūÐēКа Ðē %1 заÐŋŅ€Ðĩ҉ÐĩÐ―Ð°, Ņ‚Ð°Ðš КаК ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ ÐŋÐūÐŧÐ―ÐūŅŅ‚ŅŒŅŽ ҃ÐīаÐŧŅ‘Ð―.</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>ВÐēÐĩÐīŅ‘Ð―Ð―Ņ‹Ðđ ÐŋŅƒŅ‚ŅŒ ҁÐŧÐļŅˆÐšÐūО ÐīÐŧÐļÐ―Ð―Ņ‹Ðđ, ÐēÐēÐĩÐīÐļŅ‚Ðĩ КÐūҀҀÐĩÐšŅ‚Ð―Ņ‹Ðđ ÐŋŅƒŅ‚ŅŒ.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>ВÐēÐĩÐīŅ‘Ð― Ð―ÐĩÐēÐĩŅ€Ð―Ņ‹Ðđ ÐŋŅƒŅ‚ŅŒ, ÐŋŅ€ÐūÐēÐĩŅ€ŅŒŅ‚Ðĩ ÐŋŅ€Ð°ÐēÐļÐŧŅŒÐ―ÐūŅŅ‚ŅŒ ŅƒÐšÐ°Ð·Ð°Ð―ÐļŅ ÐŋŅƒŅ‚Ðļ К ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ҃.</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>ÐĢÐšÐ°Ð·Ð°Ð― Ð―ÐĩÐēÐĩŅ€Ð―Ņ‹Ðđ ÐŋŅƒŅ‚ŅŒ, ÐŋŅ€ÐūÐēÐĩŅ€ŅŒŅ‚Ðĩ ÐļÐžŅ ÐīÐļŅÐšÐ°.</translation> </message> <message> <source>Error</source> <translation>ÐžŅˆÐļÐąÐšÐ°</translation> </message> <message> <source>Warning</source> <translation>ÐŸŅ€ÐĩÐī҃ÐŋŅ€ÐĩÐķÐīÐĩÐ―ÐļÐĩ</translation> </message> <message> <source>Select Installation Folder</source> <translation>Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÐīÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ</translation> </message> <message> <source>Please specify the directory where %1 will be installed.</source> <translation>ÐĢКаÐķÐļŅ‚Ðĩ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ÐīÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ %1.</translation> </message> <message> <source>The directory you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>Ð’Ņ‹ÐąŅ€Ð°Ð―Ð―Ņ‹Ðđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ ŅŅƒŅ‰Ðĩҁ҂Ðē҃ÐĩŅ‚ Ðļ ҁÐūÐīÐĩŅ€ÐķÐļŅ‚ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐŧÐĩÐ―Ð―ÐūÐĩ ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļÐĩ. Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐīŅ€ŅƒÐģÐūÐđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ.</translation> </message> <message> <source>You have selected an existing, non-empty directory for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this directory as installation might fail. Do you want to continue?</source> <translation>ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ, ÐēŅ‹ÐąŅ€Ð°Ð―Ð―Ņ‹Ðđ ÐīÐŧŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļŅ, ҃ÐķÐĩ ҁÐūÐīÐĩŅ€ÐķÐļŅ‚ Ņ„Ð°ÐđÐŧŅ‹. ÐžÐ― ÐąŅƒÐīÐĩŅ‚ ҁ҂ґҀ҂ ÐēОÐĩҁ҂Ðĩ ҁÐū ÐēҁÐĩО ҁÐūÐīÐĩŅ€ÐķÐļÐžŅ‹Ðž ÐŋŅ€Ðļ ҃ÐīаÐŧÐĩÐ―ÐļÐļ ÐŋŅ€ÐļÐŧÐūÐķÐĩÐ―ÐļŅ. ÐŸŅ€ÐūÐļзÐēÐūÐīÐļŅ‚ŅŒ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐšŅƒ Ðē ŅŅ‚ÐūŅ‚ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ Ð―Ðĩ Ņ€ÐĩКÐūОÐĩÐ―Ðī҃ÐĩŅ‚ŅŅ, Ņ‚Ð°Ðš КаК Ð―Ðĩ ÐļŅÐšÐŧŅŽŅ‡Ņ‘Ð― ŅÐąÐūÐđ. ÐŸŅ€ÐūÐīÐūÐŧÐķÐļŅ‚ŅŒ?</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid directory.</source> <translation>НÐĩÐūÐąŅ…ÐūÐīÐļОÐū заÐīÐ°Ņ‚ŅŒ ÐŋŅƒŅ‚ŅŒ К ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ҃ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ. Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐŋÐūÐīŅ…ÐūÐīŅŅ‰ÐļÐđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ.</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid directory.</source> <translation>ÐŸŅƒŅ‚ŅŒ К ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ҃ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ð―Ðĩ ОÐūÐķÐĩŅ‚ ÐūÐšÐ°Ð―Ņ‡ÐļÐēÐ°Ņ‚ŅŒŅŅ Ņ‚ÐūŅ‡ÐšÐūÐđ. Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐīŅ€ŅƒÐģÐūÐđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ.</translation> </message> <message> <source>The installation path must not contain "%1", please specify a valid directory.</source> <translation>ÐŸŅƒŅ‚ŅŒ К ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ҃ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ Ð―Ðĩ ОÐūÐķÐĩŅ‚ ҁÐūÐīÐĩŅ€ÐķÐ°Ņ‚ŅŒ ÂŦ%1Âŧ. Ð’Ņ‹ÐąÐĩŅ€ÐļŅ‚Ðĩ ÐīŅ€ŅƒÐģÐūÐđ ÐšÐ°Ņ‚Ð°ÐŧÐūÐģ.</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Empty repository URL.</source> <translation>ÐŸŅƒŅŅ‚ÐūÐđ URL Ņ…Ņ€Ð°Ð―ÐļÐŧÐļŅ‰Ð°.</translation> </message> <message> <source>Missing package manager core engine.</source> <translation>ÐžŅ‚ŅŅƒŅ‚ŅŅ‚Ðē҃ÐĩŅ‚ ОÐĩÐ―ÐĩÐīÐķÐĩŅ€ ÐŋаКÐĩŅ‚ÐūÐē.</translation> </message> <message> <source>Download canceled.</source> <translation>ЗаÐģŅ€ŅƒÐ·ÐšÐ° ÐūŅ‚ÐžÐĩÐ―ÐĩÐ―Ð°.</translation> </message> <message> <source>Timeout while testing repository "%1".</source> <translation>Ð˜ŅŅ‚ÐĩКÐŧÐū ÐēŅ€ÐĩÐžŅ ÐēŅ€ÐĩÐžŅ Ņ‚Ðĩҁ҂ÐļŅ€ÐūÐēÐ°Ð―ÐļÐļ Ņ…Ņ€Ð°Ð―ÐļÐŧÐļŅ‰Ð° ÂŦ%1Âŧ.</translation> </message> <message> <source>Cannot parse Updates.xml: %1</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ Ņ€Ð°Ð·ÐūÐąŅ€Ð°Ņ‚ŅŒ Updates.xml: %1</translation> </message> <message> <source>Cannot open Updates.xml for reading: %1</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ Updates.xml ÐīÐŧŅ ҇҂ÐĩÐ―ÐļŅ: %1</translation> </message> <message> <source>Authentication failed.</source> <translation>ÐžŅˆÐļÐąÐšÐ° Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļÐļ.</translation> </message> <message> <source>Unknown error while testing repository "%1".</source> <translation>ВÐūÐ·Ð―ÐļКÐŧа Ð―ÐĩÐļзÐēÐĩŅŅ‚Ð―Ð°Ņ Ðū҈ÐļÐąÐšÐ° ÐŋŅ€Ðļ Ņ‚Ðĩҁ҂ÐļŅ€ÐūÐēÐ°Ð―ÐļÐļ Ņ…Ņ€Ð°Ð―ÐļÐŧÐļŅ‰Ð° ÂŦ%1Âŧ.</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>ÐĒŅ€ÐĩÐąŅƒÐĩŅ‚ŅŅ Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļŅ</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>ВÐēÐĩÐīÐļŅ‚Ðĩ ÐŋÐ°Ņ€ÐūÐŧҌ ÐīÐŧŅ ÐīÐūŅŅ‚ŅƒÐŋа К "sudo":</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>ÐžŅˆÐļÐąÐšÐ° ÐŋŅ€Ðļ ÐŋÐūÐŋŅ‹Ņ‚ÐšÐĩ ÐŋÐūÐŧŅƒŅ‡ÐĩÐ―ÐļŅ ÐŋŅ€Ð°Ðē аÐīОÐļÐ―ÐļŅŅ‚Ņ€Ð°Ņ‚ÐūŅ€Ð°</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ аÐēŅ‚ÐūŅ€ÐļзÐūÐēÐ°Ņ‚ŅŒŅŅ.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Please start the setup program as a user with the appropriate rights. Or accept the elevation of access rights if being asked.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€ÐūÐđŅ‚Ðļ аÐēŅ‚ÐūŅ€ÐļÐ·Ð°Ņ†ÐļŅŽ, КÐūŅ‚ÐūŅ€Ð°Ņ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļОа ÐīÐŧŅ ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ. ЗаÐŋŅƒŅŅ‚ÐļŅ‚Ðĩ ÐŋŅ€ÐūÐģŅ€Ð°ÐžÐžŅƒ ÐūŅ‚ ÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ÐĩÐŧŅ ҁ ÐŋÐūÐīŅ…ÐūÐīŅŅ‰ÐļОÐļ ÐŋŅ€Ð°ÐēаОÐļ. ИÐŧÐļ Ņ€Ð°Ð·Ņ€Ðĩ҈ÐļŅ‚Ðĩ ÐŋÐūÐēŅ‹ŅˆÐĩÐ―ÐļÐĩ ÐŋŅ€Ð°Ðē, КÐūÐģÐīа ÐūÐą ŅŅ‚ÐūО ÐąŅƒÐīÐĩŅ‚ ÐŋÐūÐŋŅ€Ðū҈ÐĩÐ―Ðū.</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as a user with the appropriate rights and then clicking OK.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋŅ€ÐūÐđŅ‚Ðļ аÐēŅ‚ÐūŅ€ÐļÐ·Ð°Ņ†ÐļŅŽ, КÐūŅ‚ÐūŅ€Ð°Ņ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļОа ÐīÐŧŅ ÐŋŅ€ÐūÐīÐūÐŧÐķÐĩÐ―ÐļŅ ŅƒŅŅ‚Ð°Ð―ÐūÐēКÐļ. МÐūÐķÐ―Ðū ÐŋŅ€ÐĩŅ€ÐēÐ°Ņ‚ŅŒ ŅƒŅŅ‚Ð°Ð―ÐūÐēÐšŅƒ ÐļÐŧÐļ ÐŋÐūÐŋŅ‹Ņ‚Ð°Ņ‚ŅŒŅŅ ŅƒŅŅ‚Ņ€Ð°Ð―ÐļŅ‚ŅŒ ÐŋŅ€ÐūÐąÐŧÐĩÐžŅƒ заÐŋŅƒŅŅ‚ÐļÐē %1 ÐūŅ‚ ÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ÐĩÐŧŅ ҁ ÐŋÐūÐīŅ…ÐūÐīŅŅ‰ÐļОÐļ ÐŋŅ€Ð°ÐēаОÐļ Ðļ Ð―Ð°ÐķаÐē ОК.</translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ€ÐĩŅŅƒŅ€Ņ %1: %2</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>НÐĩÐēÐūзОÐūÐķÐ―Ðū ÐūŅ‚ÐšŅ€Ņ‹Ņ‚ŅŒ Ņ„Ð°ÐđÐŧ Ð―Ð°ŅŅ‚Ņ€ÐūÐĩК %1 Ð―Ð° ҇҂ÐĩÐ―ÐļÐĩ: %2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>ÐÐ°ŅŅ‚Ņ€ÐūÐđКÐļ</translation> </message> <message> <source>Network</source> <translation>ÐĄÐĩŅ‚ŅŒ</translation> </message> <message> <source>No proxy</source> <translation>БÐĩз ÐŋŅ€ÐūÐšŅÐļ-ҁÐĩŅ€ÐēÐĩŅ€Ð°</translation> </message> <message> <source>System proxy settings</source> <translation>ÐÐ°ŅŅ‚Ņ€ÐūÐđКÐļ ҁÐļҁ҂ÐĩÐžÐ―ÐūÐģÐū ÐŋŅ€ÐūÐšŅÐļ</translation> </message> <message> <source>Manual proxy configuration</source> <translation>Ð ŅƒŅ‡Ð―Ð°Ņ Ð―Ð°ŅŅ‚Ņ€ÐūÐđКа ÐŋŅ€ÐūÐšŅÐļ-ҁÐĩŅ€ÐēÐĩŅ€Ð°</translation> </message> <message> <source>HTTP proxy:</source> <translation>HTTP ÐŋŅ€ÐūÐšŅÐļ-ҁÐĩŅ€ÐēÐĩŅ€:</translation> </message> <message> <source>Port:</source> <translation>ПÐūҀ҂:</translation> </message> <message> <source>FTP proxy:</source> <translation>FTP ÐŋŅ€ÐūÐšŅÐļ-ҁÐĩŅ€ÐēÐĩŅ€:</translation> </message> <message> <source>Repositories</source> <translation>РÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐļ</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>ДÐūÐąÐ°ÐēÐļŅ‚ŅŒ ÐŧÐūÐģÐļÐ― Ðļ ÐŋÐ°Ņ€ÐūÐŧҌ ÐīÐŧŅ Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļÐļ ÐŋŅ€Ðļ Ð―ÐĩÐūÐąŅ…ÐūÐīÐļОÐūҁ҂Ðļ.</translation> </message> <message> <source>Use temporary repositories only</source> <translation>ÐļҁÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ŅŒ Ņ‚ÐūÐŧŅŒÐšÐū ÐēŅ€ÐĩОÐĩÐ―Ð―Ņ‹Ðĩ Ņ€ÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐļ</translation> </message> <message> <source>Add</source> <translation>ДÐūÐąÐ°ÐēÐļŅ‚ŅŒ</translation> </message> <message> <source>Remove</source> <translation>ÐĢÐīаÐŧÐļŅ‚ŅŒ</translation> </message> <message> <source>Test</source> <translation>ÐĒÐĩҁ҂ÐļŅ€ÐūÐēÐ°Ņ‚ŅŒ</translation> </message> <message> <source>Show Passwords</source> <translation>ПÐūÐšÐ°Ð·Ņ‹ÐēÐ°Ņ‚ŅŒ ÐŋÐ°Ņ€ÐūÐŧҌ</translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>ПÐūŅŅ‚Ð°ÐēŅŒŅ‚Ðĩ Ņ„ÐŧаÐķÐūК, ҇҂ÐūÐąŅ‹ ÐļҁÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ŅŒ Ņ€ÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐđ Ðē ÐŋŅ€Ðū҆ÐĩҁҁÐĩ ÐŋÐūÐŧŅƒŅ‡ÐĩÐ―ÐļŅ.</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>ДÐūÐąÐ°ÐēÐļŅ‚ŅŒ ÐŧÐūÐģÐļÐ― ÐīÐŧŅ Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļÐļ Ð―Ð° ҁÐĩŅ€ÐēÐĩŅ€Ðĩ.</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>ДÐūÐąÐ°ÐēÐļŅ‚ŅŒ ÐŋÐ°Ņ€ÐūÐŧҌ ÐīÐŧŅ Ð°ŅƒŅ‚ÐĩÐ―Ņ‚ÐļŅ„ÐļÐšÐ°Ņ†ÐļÐļ Ð―Ð° ҁÐĩŅ€ÐēÐĩŅ€Ðĩ.</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>АÐīŅ€ÐĩŅÐ° ҁÐĩŅ€ÐēÐĩŅ€ÐūÐē, КÐūŅ‚ÐūҀҋÐĩ ҁÐūÐīÐĩŅ€ÐķÐ°Ņ‚ Ņ€Ð°ÐąÐū҇ÐļÐĩ Ņ€ÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐđ.</translation> </message> <message> <source>Hide Passwords</source> <translation>ÐĄÐšŅ€Ņ‹Ņ‚ŅŒ ÐŋÐ°Ņ€ÐūÐŧÐļ</translation> </message> <message> <source>Use</source> <translation>Ð˜ŅÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ŅŒ</translation> </message> <message> <source>Username</source> <translation>ЛÐūÐģÐļÐ―</translation> </message> <message> <source>Password</source> <translation>ÐŸÐ°Ņ€ÐūÐŧҌ</translation> </message> <message> <source>Repository</source> <translation>РÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐđ</translation> </message> <message> <source>Default repositories</source> <translation>РÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐļ ÐŋÐū ŅƒÐžÐūÐŧŅ‡Ð°Ð―ÐļŅŽ</translation> </message> <message> <source>Temporary repositories</source> <translation>Ð’Ņ€ÐĩОÐĩÐ―Ð―Ņ‹Ðĩ Ņ€ÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐļ</translation> </message> <message> <source>User defined repositories</source> <translation>Ð˜ŅÐŋÐūÐŧŅŒÐ·ÐūÐēÐ°Ņ‚ŅŒ Ð―Ð°Ð·Ð―Ð°Ņ‡ÐĩÐ―Ð―Ņ‹Ðĩ Ņ€ÐĩÐŋÐūзÐļŅ‚ÐūŅ€ÐļÐļ</translation> </message> <message> <source>An error occurred while testing this repository.</source> <translation>ВÐūÐ·Ð―ÐļКÐŧа Ðū҈ÐļÐąÐšÐ° ÐŋŅ€Ðļ Ņ‚Ðĩҁ҂ÐļŅ€ÐūÐēÐ°Ð―ÐļÐļ Ņ…Ņ€Ð°Ð―ÐļÐŧÐļŅ‰Ð°.</translation> </message> <message> <source>The repository was tested successfully.</source> <translation>ÐĨŅ€Ð°Ð―ÐļÐŧÐļ҉Ðĩ ÐŋŅ€ÐūŅ‚Ðĩҁ҂ÐļŅ€ÐūÐēÐ°Ð―Ðū ҃ҁÐŋÐĩŅˆÐ―Ðū.</translation> </message> <message> <source>Do you want to disable the repository?</source> <translation>ЖÐĩÐŧаÐĩŅ‚Ðĩ ÐūŅ‚ÐšÐŧŅŽŅ‡ÐļŅ‚ŅŒ Ņ…Ņ€Ð°Ð―ÐļÐŧÐļ҉Ðĩ?</translation> </message> <message> <source>Do you want to enable the repository?</source> <translation>ЖÐĩÐŧаÐĩŅ‚Ðĩ ÐēКÐŧŅŽŅ‡ÐļŅ‚ŅŒ Ņ…Ņ€Ð°Ð―ÐļÐŧÐļ҉Ðĩ?</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable.</source> <translation>ÐŸŅƒŅ‚ŅŒ Ņ€ÐĩÐĩŅŅ‚Ņ€Ð° %1 Ð―ÐĩÐīÐūŅŅ‚ŅƒÐŋÐĩÐ― ÐīÐŧŅ заÐŋÐļҁÐļ.</translation> </message> <message> <source>Cannot write to registry path %1.</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ заÐŋÐļŅÐ°Ņ‚ŅŒ КÐŧŅŽŅ‡ %1 Ðē Ņ€ÐĩÐĩҁ҂Ҁ.</translation> </message> <message> <source>exactly %1</source> <translation>Ņ‚ÐūŅ‡Ð―Ðū %1</translation> </message> <message> <source>at least %1</source> <translation>ОÐļÐ―ÐļÐžŅƒÐž %1</translation> </message> <message> <source>not more than %1</source> <translation>Ð―Ðĩ ÐąÐūÐŧÐĩÐĩ %1</translation> </message> <message> <source>%1 or %2</source> <translation>%1 ÐļÐŧÐļ %2</translation> </message> <message> <source>%1 to %2</source> <translation>ÐūŅ‚ %1 ÐīÐū %2</translation> </message> <message numerus="yes"> <source>Invalid arguments in %1: %n arguments given, %2 arguments expected.</source> <translation> <numerusform>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ Ðē %1: %n ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ ÐŋÐĩŅ€ÐĩÐīÐ°Ð―, Ð―Ðū %2 ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ.</numerusform> <numerusform>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ Ðē %1: %n ÐŋÐ°Ņ€Ð°ÐžÐĩŅ‚Ņ€Ð° ÐŋÐĩŅ€ÐĩÐīÐ°Ð―Ðū, Ð―Ðū %2 ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ.</numerusform> <numerusform>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ Ðē %1: %n ÐŋÐ°Ņ€Ð°ÐžÐĩ҂ҀÐūÐē ÐŋÐĩŅ€ÐĩÐīÐ°Ð―Ðū, Ð―Ðū %2 ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ.</numerusform> </translation> </message> <message numerus="yes"> <source>Invalid arguments in %1: %n arguments given, %2 arguments expected in the form: %3.</source> <translation> <numerusform>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ Ðē %1: %n ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁ ÐŋÐĩŅ€ÐĩÐīÐ°Ð―, Ð―Ðū %2 ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ Ðē Ņ„ÐūŅ€ÐžÐĩ: %3.</numerusform> <numerusform>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ Ðē %1: %n ÐŋÐ°Ņ€Ð°ÐžÐĩŅ‚Ņ€Ð° ÐŋÐĩŅ€ÐĩÐīÐ°Ð―Ðū, Ð―Ðū %2 ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ Ðē Ņ„ÐūŅ€ÐžÐĩ: %3.</numerusform> <numerusform>НÐĩÐēÐĩŅ€Ð―Ņ‹Ðĩ ÐŋÐ°Ņ€Ð°ÐžÐĩ҂Ҁҋ Ðē %1: %n ÐŋÐ°Ņ€Ð°ÐžÐĩ҂ҀÐūÐē ÐŋÐĩŅ€ÐĩÐīÐ°Ð―Ðū, Ð―Ðū %2 ҂ҀÐĩÐąŅƒÐĩŅ‚ŅŅ Ðē Ņ„ÐūŅ€ÐžÐĩ: %3.</numerusform> </translation> </message> <message> <source>Renaming file "%1" to "%2" failed: %3</source> <translation>НÐĩ ҃ÐīаÐŧÐūҁҌ ÐŋÐĩŅ€ÐĩÐļОÐĩÐ―ÐūÐēÐ°Ņ‚ŅŒ ÂŦ%1Âŧ Ðē ÂŦ%2Âŧ: %3</translation> </message> </context> </TS> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/translations/ifw_zh_CN.ts�������������������������������������������������������������������0000664�0000000�0000000�00000304626�13253666515�0017414�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="zh_CN"> <context> <name>AuthenticationRequiredException</name> <message> <source>%1 at %2</source> <translation>ä―äšŽ %2 įš„ %1</translation> </message> <message> <source>Proxy requires authentication.</source> <translation>äŧĢį†éœ€čĶéŠŒčŊã€‚</translation> </message> </context> <context> <name>BinaryContent</name> <message> <source>Cannot seek to %1 to read the operation data.</source> <translation>无æģ•æ‰ū到 %1 äŧĨčŊŧå–æ“ä―œæ•°æŪ。</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection block.</source> <translation>无æģ•æ‰ū到 %1 äŧĨčŊŧ取čĩ„暐集合块。</translation> </message> <message> <source>Cannot open meta resource. Error: %1</source> <translation>无æģ•打垀元čĩ„暐。错čŊŊïžš%1</translation> </message> </context> <context> <name>BinaryLayout</name> <message> <source>Cannot seek to %1 to read the embedded meta data count.</source> <translation>无æģ•æ‰ū到 %1 äŧĨčŊŧ取åĩŒå…Ĩ元äŋĄæŊ数æŪæ€ŧ量。</translation> </message> <message> <source>Cannot seek to %1 to read the resource collection segment.</source> <translation>无æģ•æ‰ū到 %1 äŧĨčŊŧ取čĩ„æšé›†į‰‡æŪĩ。</translation> </message> <message> <source>Unexpected mismatch of meta resources. Read %1, expected: %2.</source> <translation>意åĪ–äļåŒđé…įš„å…ƒčĩ„暐。čŊŧ取 %1期望 %2。</translation> </message> </context> <context> <name>Dialog</name> <message> <source>Http authentication required</source> <translation>需č́ Http čšŦäŧ―驌čŊ</translation> </message> <message> <source>You need to supply a Username and Password to access this site.</source> <translation>æ‚Ļ需čĶæäū›į”Ļ户名和åŊ†į æ‰čƒ―čŪŋé—Ūæ­ĪįŦ™į‚đ。</translation> </message> <message> <source>Username:</source> <translation>į”Ļ户名</translation> </message> <message> <source>Password:</source> <translation>åŊ†į ïžš</translation> </message> <message> <source>%1 at %2</source> <translation>ä―äšŽ %2 įš„ %1</translation> </message> </context> <context> <name>DirectoryGuard</name> <message> <source>Path exists but is not a folder: %1</source> <translation>č·Ŋåū„å­˜åœĻïžŒä―†äļæ˜Ŋ文äŧķåĪđïžš%1</translation> </message> <message> <source>Cannot create folder: %1</source> <translation>无æģ•创åŧšæ–‡äŧķåĪđïžš%1</translation> </message> </context> <context> <name>ExtractCallbackImpl</name> <message> <source>Cannot retrieve path of archive item %1</source> <translation>无æģ•čŽ·å–å­˜æĄĢéĄđį›Ū %1 įš„č·Ŋåū„</translation> </message> <message> <source>Cannot remove already existing symlink. %1</source> <translation>无æģ•删é™Īå·ēįŧå­˜åœĻįš„įŽĶ号é“ūæŽĨ。%1</translation> </message> <message> <source>Cannot open file: %1 (%2)</source> <translation>无æģ•打垀文äŧķïžš%1 (%2)</translation> </message> <message> <source>Cannot create symlink at '%1'. Another one is already existing.</source> <translation>无æģ•åœĻ“%1”创åŧšįŽĶ号é“ūæŽĨ。åĶäļ€äļŠįŽĶ号é“ūæŽĨå·ēįŧå­˜åœĻ。</translation> </message> <message> <source>Cannot read symlink target from file '%1'.</source> <translation>无æģ•äŧŽæ–‡äŧķ“%1”äļ­čŊŧ取įŽĶ号é“ūæŽĨį›Ū标。</translation> </message> <message> <source>Cannot create symlink at %1. %2</source> <translation>无æģ•åœĻ %1 创åŧšįŽĶ号é“ūæŽĨ。%2</translation> </message> </context> <context> <name>InstallerCalculator</name> <message> <source>Components added as automatic dependencies:</source> <translation>å·ēæ·ŧ加äļšč‡ŠåŠĻäūčĩ–įš„įŧ„äŧķïžš</translation> </message> <message> <source>Components added as dependency for '%1':</source> <translation>å·ēæ·ŧ加äļšâ€œ%1â€įš„äūčĩ–ïžš</translation> </message> <message> <source>Components that have resolved dependencies:</source> <translation>å·ēč§Ģ析äūčĩ–éĄđįš„įŧ„äŧķïžš</translation> </message> <message> <source>Selected components without dependencies:</source> <translation>å·ē选åŪšįš„æēĄæœ‰äūčĩ–éĄđįš„įŧ„äŧķïžš</translation> </message> <message> <source>Recursion detected, component '%1' already added with reason: '%2'</source> <translation>æĢ€æĩ‹åˆ°é€’å―’ïžŒįŧ„äŧķ“%1”å·ēįŧå› äļšïžšâ€œ%2”čĒŦæ·ŧ加</translation> </message> <message> <source>Cannot find missing dependency '%1' for '%2'.</source> <translation>无æģ•æ‰ū到“%2â€įžšå°‘įš„äūčĩ–“%1”。</translation> </message> </context> <context> <name>Job</name> <message> <source>Canceled</source> <translation>å·ē取æķˆ</translation> </message> </context> <context> <name>LockFile</name> <message> <source>Cannot create lock file '%1': %2</source> <translation>无æģ•创åŧšé”æ–‡äŧķ“%1”%2</translation> </message> <message> <source>Cannot write PID to lock file '%1': %2</source> <translation>无æģ•å°† PID 写å…Ĩ锁文äŧķ“%1”%2</translation> </message> <message> <source>Cannot obtain the lock for file '%1': %2</source> <translation>无æģ•äļšæ–‡äŧķ“%1â€čŽ·å–é”ïžšâ€œ%2”</translation> </message> <message> <source>Cannot release the lock for file '%1': %2</source> <translation>无æģ•äļšæ–‡äŧķ“%1”释æ”ū锁%2</translation> </message> </context> <context> <name>KDUpdater::AppendFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>无æģ•å·äŧ―文äŧķ %1ïžš%2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>无æģ•æ‰ū到 %1 įš„å·äŧ―文äŧķ。</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>无æģ•æĒåĪ %1 įš„å·äŧ―文äŧķ。</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>无æģ•æĒåĪ %1 įš„å·äŧ―文äŧķ:%2</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 2</source> <translation>恰åĨ― 2 äļŠ</translation> </message> <message> <source>Cannot open file '%1' for writing: %2</source> <translation>无æģ•打垀文äŧķ“%1”čŋ›čĄŒå†™å…Ĩïžš%2</translation> </message> </context> <context> <name>KDUpdater::CopyOperation</name> <message> <source>Cannot backup file %1.</source> <translation>无æģ•å·äŧ―文äŧķ %1。</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>参数无效å·ēįŧ™åۚ %1 äļŠå‚数嚔äļš 2 äļŠã€‚</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>无æģ•删é™Īį›Ū标文äŧķ %1ïžš%2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>无æģ•å°† %1 åĪåˆķ到 %2ïžš%3</translation> </message> <message> <source>Cannot delete file %1: %2</source> <translation>无æģ•删é™Ī文äŧķ %1ïžš%2</translation> </message> <message> <source>Cannot restore backup file into %1: %2</source> <translation>无æģ•å°†å·äŧ―文äŧķæĒåĪåˆ° %1 äļ­ïžš%2</translation> </message> <message> <source>Cannot copy a non-existent file: %1</source> <translation>无æģ•åĪåˆķäļå­˜åœĻįš„æ–‡äŧķïžš%1</translation> </message> </context> <context> <name>KDUpdater::DeleteOperation</name> <message> <source>Cannot create backup of %1: %2</source> <translation>无æģ•创åŧš %1 įš„å·äŧ―ïžš%2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>参数无效å·ēįŧ™åۚ %1 äļŠå‚数嚔äļš 1 äļŠã€‚</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>无æģ•æĒåĪ %1 įš„å·äŧ―文äŧķïžš%2</translation> </message> </context> <context> <name>KDUpdater::FileDownloader</name> <message> <source>Download finished.</source> <translation>äļ‹č――åŪŒæˆã€‚</translation> </message> <message> <source>Cryptographic hashes do not match.</source> <translation>åŊ†į æ•Ģ列äļåŒđ配。</translation> </message> <message> <source>Download canceled.</source> <translation>å·ē取æķˆäļ‹č――。</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - å‰Đä―™æ—ķé—ī朊įŸĨ。</translation> </message> <message> <source>%1 of %2</source> <translation>%2 įš„ %1</translation> </message> <message> <source>%1 downloaded.</source> <translation>å·ēäļ‹č―― %1。</translation> </message> <message> <source>(%1/sec)</source> <translation>%1/į§’ïž‰</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n åĪĐ</numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n 小æ—ķ </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n 分钟</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n į§’</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 å‰Đä―™ã€‚</translation> </message> </context> <context> <name>KDUpdater::HttpDownloader</name> <message> <source>Cannot download %1: Writing to file '%2' failed: %3</source> <translation>无æģ•äļ‹č―― %1写å…Ĩ文äŧķ“%2”åĪąčīĨïžš%3</translation> </message> <message> <source>Cannot download %1: Cannot create %2: %3</source> <translation>无æģ•äļ‹č―― %1ïžšæ— æģ•创åŧš %2:%3</translation> </message> <message> <source>%1 at %2</source> <translation>ä―äšŽ %2 įš„ %1</translation> </message> <message> <source>Authentication request canceled.</source> <translation>å·ē取æķˆčšŦäŧ―驌čŊčŊ·æą‚。</translation> </message> <message> <source>Secure Connection Failed</source> <translation>åŪ‰å…ĻčŋžæŽĨåĪąčīĨ</translation> </message> <message> <source>There was an error during connection to: %1.</source> <translation>čŋžæŽĨ“%1”æ—ķå‘į”Ÿé”™čŊŊ。</translation> </message> <message> <source>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</source> <translation>čŋ™åŊčƒ―æ˜ŊæœåŠĄå™Ļ配į―Ūįš„é—ŪéĒ˜ïžŒæˆ–č€…æ˜Ŋ有䚚尝čŊ•å†’å……æœåŠĄå™Ļ。</translation> </message> <message> <source>If you have connected to this server successfully in the past or trust this server, the error may be temporary and you can try again.</source> <translation>åĶ‚æžœæ‚ĻäŧĨ前成功čŋžæŽĨ到čŋ™äļŠæœåŠĄå™Ļæˆ–č€…äŋĄäŧŧæ­ĪæœåŠĄå™Ļčŋ™äļŠé”™čŊŊåŊčƒ―æ˜Ŋ暂æ—ķįš„ïžŒčŊ·å†æŽĄå°čŊ•。</translation> </message> <message> <source>Try again</source> <translation>å†æŽĄå°čŊ•</translation> </message> </context> <context> <name>KDUpdater::LocalFileDownloader</name> <message> <source>Cannot open source file '%1' for reading.</source> <translation>无æģ•打垀暐文äŧķ“%1”čŋ›čĄŒčŊŧ取。</translation> </message> <message> <source>Cannot open destination file '%1' for writing.</source> <translation>无æģ•打垀į›Ū标文äŧķ“%1”čŋ›čĄŒå†™å…Ĩ。</translation> </message> <message> <source>Writing to %1 failed: %2</source> <translation>写å…Ĩ %1 åĪąčīĨïžš%2</translation> </message> </context> <context> <name>KDUpdater::MkdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>参数无效å·ēįŧ™åۚ %1 äļŠå‚数嚔äļš 1 äļŠã€‚</translation> </message> <message> <source>Cannot create folder %1: Unknown error.</source> <translation>无æģ•创åŧšæ–‡äŧķåĪđ %1朊įŸĨ错čŊŊ。</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>无æģ•删é™Īį›Ūå―• %1ïžš%2</translation> </message> </context> <context> <name>KDUpdater::MoveOperation</name> <message> <source>Cannot backup file %1.</source> <translation>无æģ•å·äŧ―文äŧķ %1。</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>参数无效å·ēįŧ™åۚ %1 äļŠå‚数嚔äļš 2 äļŠã€‚</translation> </message> <message> <source>Cannot remove destination file %1: %2</source> <translation>无æģ•删é™Īį›Ū标文äŧķ %1ïžš%2</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>无æģ•å°† %1 åĪåˆķ到 %2ïžš%3</translation> </message> <message> <source>Cannot copy %1 to %2: %3</source> <translation>无æģ•å°† %1 åĪåˆķ到 %2ïžš%3</translation> </message> <message> <source>Cannot remove file %1.</source> <translation>无æģ•删é™Ī文äŧķ %1。</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>无æģ•æĒåĪ %1 įš„å·äŧ―文äŧķïžš%2</translation> </message> </context> <context> <name>KDUpdater::PackagesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 包åŦæ— æ•ˆįš„å†…åŪđïžš%2</translation> </message> <message> <source>The file %1 does not exist.</source> <translation>文äŧķ %1 äļå­˜åœĻ。</translation> </message> <message> <source>Cannot open %1.</source> <translation>无æģ•打垀 %1。</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>%1 äļ­å­˜åœĻč§Ģ析错čŊŊïžŒä―äšŽ %2%3ïžš%4</translation> </message> <message> <source>Root element %1 unexpected, should be 'Packages'.</source> <translation>æ đ元įī  %1 äļŽéĒ„æœŸäļįŽĶ嚔äļšâ€œåŒ…”。</translation> </message> </context> <context> <name>KDUpdater::PrependFileOperation</name> <message> <source>Cannot backup file %1: %2</source> <translation>无æģ•å·äŧ―文äŧķ %1ïžš%2</translation> </message> <message> <source>Invalid arguments: %1 arguments given, 2 expected.</source> <translation>参数无效å·ēįŧ™åۚ %1 äļŠå‚数嚔äļš 2 äļŠã€‚</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>无æģ•打垀文äŧķ %1 čŋ›čĄŒčŊŧ取%2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>无æģ•打垀文äŧķ %1 čŋ›čĄŒå†™å…Ĩïžš%2</translation> </message> <message> <source>Cannot find backup file for %1.</source> <translation>无æģ•æ‰ū到 %1 įš„å·äŧ―文äŧķ。</translation> </message> <message> <source>Cannot restore backup file for %1.</source> <translation>无æģ•æĒåĪ %1 įš„å·äŧ―文äŧķ。</translation> </message> <message> <source>Cannot restore backup file for %1: %2</source> <translation>无æģ•æĒåĪ %1 įš„å·äŧ―文äŧķïžš%2</translation> </message> </context> <context> <name>KDUpdater::ResourceFileDownloader</name> <message> <source>Cannot read resource file "%1". Reason:</source> <translation>无æģ•čŊŧ取暐文äŧķ“%1”。原因</translation> </message> </context> <context> <name>KDUpdater::RmdirOperation</name> <message> <source>Invalid arguments: %1 arguments given, 1 expected.</source> <translation>参数无效å·ēįŧ™åۚ %1 äļŠå‚数嚔äļš 1 äļŠã€‚</translation> </message> <message> <source>Cannot remove folder %1: The folder does not exist.</source> <translation>无æģ•删é™Ī文äŧķåĪđ %1ïžščŊĨ文äŧķåĪđäļå­˜åœĻ。</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>无æģ•删é™Ī文äŧķåĪđ %1ïžš%2</translation> </message> <message> <source>Cannot recreate directory %1: %2</source> <translation>无æģ•重新创åŧšį›Ūå―• %1ïžš%2</translation> </message> </context> <context> <name>KDUpdater::Task</name> <message> <source>%1 started</source> <translation>%1 å·ēåŊåŠĻ</translation> </message> <message> <source>%1 cannot be stopped</source> <translation>无æģ•停æ­Ē %1</translation> </message> <message> <source>Cannot stop task %1</source> <translation>无æģ•停æ­ĒäŧŧåŠĄ %1</translation> </message> <message> <source>%1 cannot be paused</source> <translation>无æģ•暂停 %1</translation> </message> <message> <source>Cannot pause task %1</source> <translation>无æģ•暂停äŧŧåŠĄ %1</translation> </message> <message> <source>Cannot resume task %1</source> <translation>无æģ•æĒåĪäŧŧåŠĄ %1</translation> </message> <message> <source>%1 done</source> <translation>%1 å·ēåŪŒæˆ</translation> </message> </context> <context> <name>KDUpdater::UpdateFinder</name> <message> <source>Cannot access the package information of this application.</source> <translation>无æģ•čŪŋé—Ūæ­Īåš”į”ĻįĻ‹åšįš„åŒ…äŋĄæŊ。</translation> </message> <message> <source>Cannot access the update sources information of this application.</source> <translation>无æģ•čŪŋé—Ūæ­Īåš”į”ĻįĻ‹åšįš„æ›ī新暐äŋĄæŊ。</translation> </message> <message> <source>Downloading Updates.xml from update sources.</source> <translation>æ­ĢåœĻäŧŽæ›ī新暐äļ‹č―― Updates.xml。</translation> </message> <message> <source>Updates.xml file(s) downloaded from update sources.</source> <translation>å·ēäŧŽæ›ī新暐äļ‹č―― Updates.xml 文äŧķ。</translation> </message> <message> <source>Computing applicable updates.</source> <translation>æ­ĢåœĻčŪĄįۗ适į”Ļįš„æ›ī新。</translation> </message> <message> <source>Application updates computed.</source> <translation>åš”į”ĻįĻ‹åšæ›ī新čŪĄįŪ—åیæŊ•。</translation> </message> <message numerus="yes"> <source>%n update(s) found.</source> <translation> <numerusform>å·ēæ‰ū到 %n äļŠæ›ī新。</numerusform> </translation> </message> <message> <source>Cannot download update source %1 from ('%2')</source> <translation>无æģ•äŧŽïžˆâ€œ%2”äļ‹č――æ›ī新čĩ„暐 %1</translation> </message> </context> <context> <name>KDUpdater::UpdateSourcesInfo</name> <message> <source>%1 contains invalid content: %2</source> <translation>%1 包åŦæ— æ•ˆįš„å†…åŪđïžš%2</translation> </message> <message> <source>Cannot read "%1"</source> <translation>无æģ•čŊŧ取“%1”</translation> </message> <message> <source>XML Parse error in %1 at %2, %3: %4</source> <translation>%1 äļ­å­˜åœĻ XML č§Ģ析错čŊŊïžŒä―äšŽ %2%3ïžš%4</translation> </message> <message> <source>Root element %1 unexpected, should be "UpdateSources"</source> <translation>æ đ元įī  %1 äļŽéĒ„æœŸäļįŽĶ嚔äļšâ€œæ›ī新暐”</translation> </message> <message> <source>Cannot save changes to "%1": %2</source> <translation>无æģ•å°†æ›īæ”đäŋå­˜åˆ°â€œ%1”%2</translation> </message> </context> <context> <name>KDUpdater::UpdatesInfoData</name> <message> <source>Updates.xml contains invalid content: %1</source> <translation>Updates.xml 包åŦæ— æ•ˆįš„å†…åŪđïžš%1</translation> </message> <message> <source>Cannot read "%1"</source> <translation>无æģ•čŊŧ取“%1”</translation> </message> <message> <source>Parse error in %1 at %2, %3: %4</source> <translation>%1 äļ­å­˜åœĻč§Ģ析错čŊŊïžŒä―äšŽ %2%3ïžš%4</translation> </message> <message> <source>Root element %1 unexpected, should be "Updates".</source> <translation>æ đ元įī  %1 äļŽéĒ„æœŸäļįŽĶ嚔äļšâ€œæ›ī新”。</translation> </message> <message> <source>ApplicationName element is missing.</source> <translation>įžšå°‘ ApplicationName 元įī ã€‚</translation> </message> <message> <source>ApplicationVersion element is missing.</source> <translation>įžšå°‘ ApplicationVersion 元įī ã€‚</translation> </message> <message> <source>PackageUpdate element without Name</source> <translation>PackageUpdate 元įī įžšå°‘ Name</translation> </message> <message> <source>PackageUpdate element without Version</source> <translation>PackageUpdate 元įī įžšå°‘ Version</translation> </message> <message> <source>PackageUpdate element without ReleaseDate</source> <translation>PackageUpdate 元įī įžšå°‘ ReleaseDate</translation> </message> </context> <context> <name>Lib7z</name> <message> <source>Cannot retrieve number of items in archive</source> <translation>无æģ•æĢ€įīĒ存æĄĢäļ­įš„éĄđį›Ū数量</translation> </message> <message> <source>Cannot retrieve path of archive item %1</source> <translation>无æģ•čŽ·å–å­˜æĄĢéĄđį›Ū %1 įš„č·Ŋåū„</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>æ•čŽ·æœŠįŸĨåž‚åļļ(%1)</translation> </message> <message> <source>internal code: %1</source> <translation>内éƒĻäŧĢį ïžš%1</translation> </message> <message> <source>not enough memory</source> <translation>内存äļčķģ</translation> </message> <message> <source>Error: %1</source> <translation>错čŊŊïžš%1</translation> </message> <message> <source>Cannot load codecs</source> <translation>无æģ•åŠ č――č§Ģ᠁å™Ļ</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>无æģ•æĢ€įīĒéŧ˜čŪĪæ žåž</translation> </message> <message> <source>Cannot create archive %1. %2</source> <translation>无æģ•创åŧšå­˜æĄĢ %1。%2</translation> </message> <message> <source>CArc index %1 out of bounds [0, %2]</source> <translation>CArc įīĒåž• %1 čķ…凚 [0, %2] įš„čŒƒå›ī</translation> </message> <message> <source>Item index %1 out of bounds [0, %2]</source> <translation>éĄđį›ŪįīĒåž• %1 čķ…凚 [0, %2] įš„čŒƒå›ī</translation> </message> <message> <source>Cannot create output file for writing: %1</source> <translation>无æģ•创åŧščū“凚文äŧķčŋ›čĄŒå†™å…Ĩïžš%1</translation> </message> </context> <context> <name>Lib7z::ExtractItemJob</name> <message> <source>Cannot list archive: QIODevice not set or already destroyed.</source> <translation>无æģ•列凚存æĄĢïžšQIODevice 尚朊čŪūį―Ūæˆ–å·ē损坏。</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>提取“%1”æ—ķå‘į”Ÿé”™čŊŊïžš%2</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>æ•čŽ·æœŠįŸĨåž‚åļļ(%1)</translation> </message> <message> <source>Failed</source> <translation>åĪąčīĨ</translation> </message> </context> <context> <name>Lib7z::ListArchiveJob</name> <message> <source>Cannot list archive: QIODevice already destroyed.</source> <translation>无æģ•列凚存æĄĢïžšQIODevice å·ē损坏。</translation> </message> <message> <source>Unknown exception caught (%1)</source> <translation>æ•čŽ·æœŠįŸĨåž‚åļļ(%1)</translation> </message> <message> <source>Failed</source> <translation>åĪąčīĨ</translation> </message> </context> <context> <name>OpenArchiveInfo</name> <message> <source>Cannot load codecs</source> <translation>无æģ•åŠ č――č§Ģ᠁å™Ļ</translation> </message> <message> <source>Cannot retrieve default format</source> <translation>无æģ•æĢ€įīĒéŧ˜čŪĪæ žåž</translation> </message> <message> <source>Cannot open archive</source> <translation>无æģ•打垀存æĄĢ</translation> </message> <message> <source>No CArc found</source> <translation>朊æ‰ū到 CArc</translation> </message> </context> <context> <name>QIODeviceSequentialOutStream</name> <message> <source>No device set for output stream</source> <translation>æēĄæœ‰äļščū“凚æĩčŪūį―ŪčŪūå·</translation> </message> </context> <context> <name>QInstaller</name> <message> <source>No marker found, stopped after %1.</source> <translation>朊æ‰ū到标čŪ°ïžŒå·ēåœĻ %1 后停æ­Ē。</translation> </message> <message> <source>Cannot open file %1 for reading: %2</source> <translation>无æģ•打垀文äŧķ %1 čŋ›čĄŒčŊŧ取%2</translation> </message> <message> <source>Cannot open file %1 for writing: %2</source> <translation>无æģ•打垀文äŧķ %1 čŋ›čĄŒå†™å…Ĩïžš%2</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>čŊŧ取 %1 å­—čŠ‚åŽåĪąčīĨïžš%2</translation> </message> <message> <source>Copy failed. Error: %1</source> <translation>åĪåˆķåĪąčīĨ。错čŊŊïžš%1</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>写å…Ĩ %1 å­—čŠ‚åŽåĪąčīĨïžš%2</translation> </message> <message> <source>bytes</source> <translation>å­—čŠ‚</translation> </message> <message> <source>KiB</source> <translatorcomment>MB</translatorcomment> <translation>KB</translation> </message> <message> <source>MiB</source> <translation>MB</translation> </message> <message> <source>GiB</source> <translation>GB</translation> </message> <message> <source>TiB</source> <translation>TB</translation> </message> <message> <source>PiB</source> <translation>PB</translation> </message> <message> <source>EiB</source> <translation>EB</translation> </message> <message> <source>ZiB</source> <translation>ZB</translation> </message> <message> <source>YiB</source> <translation>YB</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>无æģ•删é™Ī文äŧķ %1ïžš%2</translation> </message> <message> <source>Cannot remove folder %1: %2</source> <translation>无æģ•删é™Ī文äŧķåĪđ %1ïžš%2</translation> </message> <message> <source>Cannot create folder %1</source> <translation>无æģ•创åŧšæ–‡äŧķåĪđ %1</translation> </message> <message> <source>Cannot copy file from %1 to %2: %3</source> <translation>无æģ•将文äŧķäŧŽ %1 åĪåˆķ到 %2ïžš%3</translation> </message> <message> <source>Cannot move file from %1 to %2: %3</source> <translation>无æģ•将文äŧķäŧŽ %1 į§ŧåŠĻ到 %2ïžš%3</translation> </message> <message> <source>Cannot create folder %1: %2</source> <translation>无æģ•创åŧšæ–‡äŧķåĪđ %1ïžš%2</translation> </message> <message> <source>Cannot open temporary file: %1</source> <translation>无æģ•打垀äļīæ—ķæ–‡äŧķïžš%1</translation> </message> <message> <source>Cannot open temporary file for template %1: %2</source> <translation>无æģ•打垀æĻĄæŋ %1 įš„äļīæ—ķæ–‡äŧķïžš%2</translation> </message> <message> <source>Cannot create temporary file</source> <translation>无æģ•创åŧšäļīæ—ķæ–‡äŧķ</translation> </message> <message> <source>Cannot retrieve property %1 for item %2</source> <translation>无æģ•æĢ€įīĒ %2 éĄđį›Ūįš„ %1 åąžæ€§</translation> </message> <message> <source>Property %1 for item %2 not of type VT_FILETIME but %3</source> <translation>%2 éĄđį›Ūįš„ %1 åąžæ€§äļåąžäšŽ VT_FILETIME įąŧåž‹ïžŒč€Œæ˜Ŋ %3</translation> </message> <message> <source>Cannot convert file time to local time</source> <translation>无æģ•将文äŧķæ—ķé—īč―ŽæĒäļšæœŽåœ°æ—ķé—ī</translation> </message> <message> <source>Cannot convert local file time to system time</source> <translation>无æģ•将朎地文äŧķæ—ķé—īč―ŽæĒäļšįģŧįŧŸæ—ķé—ī</translation> </message> <message> <source>Corrupt installation</source> <translation>åŪ‰čĢ…å·ē损坏</translation> </message> <message> <source>Your installation seems to be corrupted. Please consider re-installing from scratch.</source> <translation>æ‚Ļįš„åŪ‰čĢ…äžžäđŽå·ēįŧæŸåã€‚čŊ·æ‚Ļč€ƒč™‘é‡æ–°åŪ‰čĢ…ã€‚</translation> </message> <message> <source>The specified module could not be found.</source> <translation>无æģ•æ‰ū到æ‚Ļ指åŪšįš„æĻĄå—。</translation> </message> </context> <context> <name>QInstaller::Component</name> <message> <source>Error</source> <translation>错čŊŊ</translation> </message> <message> <source>Error: Operation %1 does not exist</source> <translation>错čŊŊïžščŋįŪ— %1 äļå­˜åœĻ</translation> </message> <message> <source>Update Info: </source> <translation>æ›ī新äŋĄæŊïžš</translation> </message> <message> <source>Components cannot have children in updater mode.</source> <translation>åœĻå‡įš§æĻĄåžäļ‹įŧ„äŧķæ— æģ•åŦ有子įŧ„äŧķ。</translation> </message> <message> <source>Cannot open the requested translation file '%1'.</source> <translation>无æģ•打垀čŊ·æą‚įš„įŋŧčŊ‘æ–‡äŧķ“%1”。</translation> </message> <message> <source>Cannot open the requested UI file '%1'. Error: %2</source> <translation>无æģ•打垀čŊ·æą‚įš„UI文äŧķ“%1”。错čŊŊïžš%2</translation> </message> <message> <source>Cannot load the requested UI file '%1'. Error: %2</source> <translation>无æģ•åŠ č――čŊ·æą‚įš„UI文äŧķ“%1”。错čŊŊïžš%2</translation> </message> <message> <source>Cannot open the requested license file '%1'. Error: %2</source> <translation>无æģ•打垀čŊ·æą‚įš„čŪļåŊ文äŧķ“%1”。错čŊŊïžš%2</translation> </message> <message> <source>Cannot resolve isDefault in %1</source> <translation>无æģ•č§Ģ析 %1 äļ­įš„ isDefault</translation> </message> </context> <context> <name>QInstaller::ComponentModel</name> <message> <source>Component Name</source> <translation>įŧ„äŧķåį§°</translation> </message> <message> <source>Installed Version</source> <translation>å·ēåŪ‰čĢ…į‰ˆæœŽ</translation> </message> <message> <source>New Version</source> <translation>æ–°į‰ˆæœŽ</translation> </message> <message> <source>Size</source> <translation>åĪ§å°</translation> </message> <message> <source>Component is marked for installation.</source> <translation>įŧ„äŧķå·ēčĒŦ标čŪ°äļšåŪ‰čĢ…ã€‚</translation> </message> <message> <source>Component is marked for uninstallation.</source> <translation>įŧ„äŧķå·ēčĒŦ标čŪ°äļšåļč――ã€‚</translation> </message> <message> <source>Component is installed.</source> <translation>įŧ„äŧķå·ēčĒŦåŪ‰čĢ…ã€‚</translation> </message> <message> <source>Component is not installed.</source> <translation>įŧ„äŧķæœŠčĒŦåŪ‰čĢ…ã€‚</translation> </message> <message> <source>Action</source> <translation>åŠĻä―œ</translation> </message> <message> <source>Release Date</source> <translation>发å—Ĩ期</translation> </message> </context> <context> <name>QInstaller::ComponentSelectionPage</name> <message> <source>Alt+A</source> <comment>select default components</comment> <translation>Alt+A</translation> </message> <message> <source>Def&ault</source> <translation>éŧ˜čŪĪ(&A)</translation> </message> <message> <source>Alt+R</source> <comment>reset to already installed components</comment> <translation>Alt+R</translation> </message> <message> <source>&Reset</source> <translation>重į―Ū(&R)</translation> </message> <message> <source>Alt+S</source> <comment>select all components</comment> <translation>Alt+S</translation> </message> <message> <source>&Select All</source> <translation>å…Ļ选(&S)</translation> </message> <message> <source>Alt+D</source> <comment>deselect all components</comment> <translation>Alt+D</translation> </message> <message> <source>&Deselect All</source> <translation>取æķˆå…Ļ选(&D)</translation> </message> <message> <source>This component will occupy approximately %1 on your hard disk drive.</source> <translation>æ­Īįŧ„äŧķ将占į”Ļæ‚ĻåΧįšĶ %1 įš„įĄŽį›˜įĐšé—ī。</translation> </message> <message> <source>Select Components</source> <translation>选æ‹Đįŧ„äŧķ</translation> </message> <message> <source>Please select the components you want to update.</source> <translation>čŊ·é€‰æ‹Đæ‚Ļæƒģč́æ›īæ–°įš„įŧ„äŧķ。</translation> </message> <message> <source>Please select the components you want to install.</source> <translation>čŊ·é€‰æ‹Đæ‚Ļæƒģč́åŪ‰čĢ…įš„įŧ„äŧķ。</translation> </message> <message> <source>Please select the components you want to uninstall.</source> <translation>čŊ·é€‰æ‹Đæ‚Ļæƒģč́åļč――įš„įŧ„äŧķ。</translation> </message> <message> <source>Select the components to install. Deselect installed components to uninstall them.</source> <translation>选æ‹Đč́åŪ‰čĢ…įš„įŧ„äŧķ。取æķˆé€‰æ‹Đå·ēåŪ‰čĢ…įš„įŧ„äŧķäŧĨ将å…ķåļč――ã€‚</translation> </message> </context> <context> <name>QInstaller::ConsumeOutputOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>at least 2</source> <translation>č‡ģ少 2 äļŠ</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>%1 čŋįŪ—äļ­æ‰€éœ€įš„åŪ‰čĢ…įĻ‹åšåŊđ蹥äļšįĐšã€‚</translation> </message> <message> <source>Can not save the output of %1 to an empty installer key value.</source> <translation>无æģ•äŋå­˜ %1 įš„čū“凚到äļ€äļŠįĐšįš„åŪ‰čĢ…é”Ū倞。</translation> </message> <message> <source>File '%1' does not exist or is not an executable binary.</source> <translation>文äŧķ“%1”äļå­˜åœĻæˆ–č€…äļæ˜Ŋäļ€äļŠåŊæ‰§čĄŒæ–‡äŧķ。</translation> </message> <message> <source>Running '%1' resulted in a crash.</source> <translation>čŋčĄŒâ€œ%1”åŊžč‡īåīĐæšƒã€‚</translation> </message> </context> <context> <name>QInstaller::CopyDirectoryOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>2 or 3</source> <translation>2 或 3 äļŠ</translation> </message> <message> <source> (<source> <target> [forceOverwrite])</source> <translation> (<source> <target> [forceOverwrite])</translation> </message> <message> <source>Invalid argument in %0: Third argument needs to be forceOverwrite, if specified</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°:åĶ‚æžœå·ē指åŪšïžŒįŽŽäļ‰äļŠå‚æ•°åŋ…éĄŧäļš forceOverwrite</translation> </message> <message> <source>Invalid arguments in %0: Directories are invalid: %1 %2</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°:į›Ūå―•æ— æ•ˆïžš%1 %2</translation> </message> <message> <source>Cannot create %0</source> <translation>无æģ•创åŧš %0</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>č͆ᛖ %1 åĪąčīĨ</translation> </message> <message> <source>Cannot copy %0 to %1, error was: %3</source> <translation>无æģ•å°† %0 åĪåˆķ到 %1错čŊŊäļšïžš%3</translation> </message> <message> <source>Cannot remove %0</source> <translation>无æģ•删é™Ī %0</translation> </message> </context> <context> <name>QInstaller::CopyFileTask</name> <message> <source>Invalid task item count.</source> <translation>æ— æ•ˆįš„äŧŧåŠĄéĄđæ€ŧ数。</translation> </message> <message> <source>Cannot open source '%1' for read. Error: %2.</source> <translation>无æģ•打垀文äŧķ“%1”čŋ›čĄŒčŊŧ取。错čŊŊïžš%2。</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <translation>无æģ•打垀į›Ū标“%1”čŋ›čĄŒå†™å…Ĩ。错čŊŊïžš%2。</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <translation>写å…Ĩį›Ū标“%1”åĪąčīĨ。错čŊŊïžš%2。</translation> </message> </context> <context> <name>QInstaller::CreateDesktopEntryOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 2</source> <translation>恰åĨ― 2 äļŠ</translation> </message> <message> <source>Failed to overwrite %1</source> <translation>č͆ᛖ %1 åĪąčīĨ</translation> </message> <message> <source>Cannot write Desktop Entry at %1</source> <translation>无æģ•写å…Ĩä―äšŽ %1 įš„æĄŒéĒæĄį›Ū</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation>无æģ•å·äŧ―文äŧķ %1: %2</translation> </message> </context> <context> <name>QInstaller::CreateLinkOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 2</source> <translation>恰åĨ― 2 äļŠ</translation> </message> <message> <source>Cannot create link from %1 to %2.</source> <translation>无æģ•创åŧšäŧŽ %1 到 %2 įš„é“ūæŽĨ。</translation> </message> <message> <source>Cannot remove link from %1 to %2.</source> <translation>无æģ•删é™ĪäŧŽ %1 到 %2 įš„é“ūæŽĨ。</translation> </message> </context> <context> <name>QInstaller::CreateLocalRepositoryOperation</name> <message> <source>Cannot set file permissions %1!</source> <translation>无æģ•čŪūį―Ūæ–‡äŧķæƒé™ %1!</translation> </message> <message> <source>Cannot move file %1 to %2. Error: %3</source> <translation>无æģ•将文äŧķ %1 į§ŧåŠĻ到 %2。错čŊŊïžš%3</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 2</source> <translation>恰åĨ― 2 äļŠ</translation> </message> <message> <source>Installer needs to be an offline version: %1.</source> <translation>åŪ‰čĢ…įĻ‹åšåŋ…éĄŧäļšįĶŧįšŋį‰ˆæœŽïžš%1.</translation> </message> <message> <source>Cannot open file: %1</source> <translation>无æģ•打垀文äŧķïžš%1</translation> </message> <message> <source>Cannot read: %1. Error: %2</source> <translation>无æģ•čŊŧ取%1.错čŊŊïžš%2</translation> </message> <message> <source>Cannot open file: %1. Error: %2</source> <translation>无æģ•打垀文äŧķïžš%1.错čŊŊïžš%2</translation> </message> <message> <source>Cannot create target dir: %1.</source> <translation>无æģ•创åŧšį›Ū标į›Ūå―•ïžš%1.</translation> </message> <message> <source>Unknown exception caught: %1.</source> <translation>æ•čŽ·æœŠįŸĨåž‚åļļïžš%1.</translation> </message> <message> <source>Removing file: %0</source> <translation>æ­ĢåœĻ删é™Ī文äŧķïžš%0</translation> </message> <message> <source>Cannot remove %0.</source> <translation>无æģ•删é™Ī %0。</translation> </message> <message> <source>Cannot remove directory %1: %2</source> <translation>无æģ•删é™Īį›Ūå―• %1ïžš%2</translation> </message> <message> <source>Cannot remove file %1: %2</source> <translation>无æģ•删é™Ī文äŧķ %1ïžš%2</translation> </message> </context> <context> <name>QInstaller::CreateShortcutOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>2 or 3</source> <translation>2 或 3 äļŠ</translation> </message> <message> <source>Cannot create folder %1: %2.</source> <translation>无æģ•创åŧšæ–‡äŧķåĪđ %1ïžš%2.</translation> </message> <message> <source>Cannot create link %1: %2</source> <translation>无æģ•创åŧšé“ūæŽĨ %1ïžš%2</translation> </message> <message> <source> (optional: 'workingDirectory=...', 'iconPath=...', 'iconId=...')</source> <translation> (åŊ选“workingDirectory=...”,“iconPath=...”“iconId=...”)</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>č͆ᛖ %1 åĪąčīĨïžš%2</translation> </message> </context> <context> <name>QInstaller::DownloadArchivesJob</name> <message> <source>Canceled</source> <translation>å·ē取æķˆ</translation> </message> <message> <source>Downloading hash signature failed.</source> <translation>äļ‹č――æ•Ģ列į­ū名åĪąčīĨ。</translation> </message> <message> <source>Download Error</source> <translation>äļ‹č――错čŊŊ</translation> </message> <message> <source>Hash verification while downloading failed. This is a temporary error, please retry.</source> <translation>äļ‹č――æ—ķįš„æ•Ģ列銌čŊåĪąčīĨ。čŋ™æ˜Ŋäļ€äļŠäļīæ—ķ错čŊŊčŊ·é‡čŊ•。</translation> </message> <message> <source>Cannot verify Hash</source> <translation>无æģ•éЌčŊæ•Ģ列</translation> </message> <message> <source>Cannot download archive: %1 : %2</source> <translation>无æģ•äļ‹č――存æĄĢïžš%1ïžš%2</translation> </message> <message> <source>Cannot fetch archives: %1 Error while loading %2</source> <translation>无æģ•提取存æĄĢïžš%1 åŠ č―― %2 æ—ķå‡šįŽ°é”™čŊŊ</translation> </message> <message> <source>Scheme not supported: %1 (%2)</source> <translation>äļæ”ŊæŒįš„æ–đæĄˆïžš%1 (%2)</translation> </message> <message> <source>Cannot find component for: %1.</source> <translation>无æģ•äļ‹č――äŧĨäļ‹éĄđį›Ūįš„įŧ„äŧķïžš%1.</translation> </message> <message> <source>Downloading archive '%1' for component: %2</source> <translation>æ­ĢåœĻäļšįŧ„äŧķ %2 äļ‹č――存æĄĢ文äŧķ“%1”</translation> </message> </context> <context> <name>QInstaller::Downloader</name> <message> <source>Target '%1' not open for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>į›Ū标“%1”朊打垀äŧĨčŋ›čĄŒå†™å…Ĩ。错čŊŊïžš%2。</translation> </message> <message> <source>Writing to target '%1' failed. Error: %2.</source> <extracomment>%2 is a sentence describing the error.</extracomment> <translation>写å…Ĩį›Ū标“%1”åĪąčīĨ。错čŊŊïžš%2。</translation> </message> <message> <source>Redirect loop detected '%1'.</source> <translation>æĢ€æĩ‹åˆ°é‡åŪšå‘åūŠįŽŊ“%1”。</translation> </message> <message> <source>Checksum mismatch detected '%1'.</source> <translation>æĢ€æĩ‹åˆ°æ ĄéŠŒå’ŒäļåŒđ配“%1”。</translation> </message> <message> <source>Network error while downloading '%1': %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>äļ‹č――“%1”æ—ķå‘į”Ÿį―‘įŧœé”™čŊŊïžš%2。</translation> </message> <message> <source>Unknown network error while downloading: %1.</source> <extracomment>%1 is a sentence describing the error</extracomment> <translation>äļ‹č――ïžš%1æ—ķå‡šįŽ°æœŠįŸĨį―‘įŧœé”™čŊŊ。</translation> </message> <message> <source>Pause and resume not supported by network transfers.</source> <translation>į―‘įŧœäž čū“äļæ”Ŋ持暂停和æĒåĪã€‚</translation> </message> <message> <source>Invalid source '%1'. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>无效čĩ„暐“%1”。错čŊŊïžš%2。</translation> </message> <message> <source>Target file '%1' already exists but is not a file.</source> <translation>į›Ū标文äŧķ“%1”å·ē存åœĻïžŒä―†åۃäļæ˜Ŋäļ€äļŠæ–‡äŧķ。</translation> </message> <message> <source>Cannot open target '%1' for write. Error: %2.</source> <extracomment>%2 is a sentence describing the error</extracomment> <translation>无æģ•打垀į›Ū标“%1”äŧĨčŋ›čĄŒå†™å…Ĩ。错čŊŊïžš%2。</translation> </message> </context> <context> <name>QInstaller::ElevatedExecuteOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>at least 1</source> <translation>č‡ģ少 1 äļŠ</translation> </message> <message> <source>Execution failed: Cannot start detached: "%1"</source> <translation>æ‰§čĄŒåĪąčīĨïžšæ— æģ•垀始分įĶŧ“%1”</translation> </message> <message> <source>Execution failed(Crash): "%1"</source> <translation>æ‰§čĄŒåĪąčīĨ(åīĐæšƒ)“%1”</translation> </message> <message> <source>Execution failed(Unexpected exit code: %1): "%2"</source> <translation>æ‰§čĄŒåĪąčīĨ(意åĪ–é€€å‡šäŧĢį ïžš%1)“%2”</translation> </message> <message> <source>Execution failed: Cannot start: "%1"(%2)</source> <translation>æ‰§čĄŒåĪąčīĨïžšæ— æģ•åŊåŠĻ“%1”%2</translation> </message> </context> <context> <name>QInstaller::EnvironmentVariableOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>2 to 4</source> <translation>2 到 4 äļŠ</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 2</source> <translation>恰åĨ― 2 äļŠ</translation> </message> </context> <context> <name>QInstaller::ExtractArchiveOperation::Runnable</name> <message> <source>Cannot open %1 for reading: %2.</source> <translation>无æģ•打垀 %1 čŋ›čĄŒčŊŧ取%2.</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>提取“%1”æ—ķå‡šįŽ°é”™čŊŊïžš%2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>提取 %1 æ—ķæ•čŽ·æœŠįŸĨåž‚åļļ。</translation> </message> </context> <context> <name>QInstaller::FakeStopProcessForUpdateOperation</name> <message> <source>Number of arguments does not match: one is required</source> <translation>参数数量äļåŒđ配需č́äļ€äļŠ</translation> </message> <message> <source>Cannot get package manager core.</source> <translation>无æģ•获åū—包įŪĄį†å™Ļ内æ ļ。</translation> </message> <message> <source>This process should be stopped before continuing: %1</source> <translation>åŋ…éĄŧ先停æ­Ēæ­Īčŋ›įĻ‹æ‰čƒ―įŧ§įŧ­æ“ä―œïžš%1</translation> </message> <message> <source>These processes should be stopped before continuing: %1</source> <translation>åŋ…éĄŧ先停æ­ĒäŧĨäļ‹čŋ›įĻ‹æ‰čƒ―įŧ§įŧ­æ“ä―œïžš%1</translation> </message> </context> <context> <name>QInstaller::FileTaskObserver</name> <message> <source>%1 of %2</source> <translation>%2 įš„ %1</translation> </message> <message> <source>%1 received.</source> <translation>å·ēæ”ķ到 %1。</translation> </message> <message> <source>(%1/sec)</source> <translation>%1/į§’ïž‰</translation> </message> <message numerus="yes"> <source>%n day(s), </source> <translation> <numerusform>%n åĪĐ </numerusform> </translation> </message> <message numerus="yes"> <source>%n hour(s), </source> <translation> <numerusform>%n 小æ—ķ </numerusform> </translation> </message> <message numerus="yes"> <source>%n minute(s)</source> <translation> <numerusform>%n 分钟</numerusform> </translation> </message> <message numerus="yes"> <source>%n second(s)</source> <translation> <numerusform>%n į§’</numerusform> </translation> </message> <message> <source> - %1%2%3%4 remaining.</source> <translation> - %1%2%3%4 å‰Đä―™ã€‚</translation> </message> <message> <source> - unknown time remaining.</source> <translation> - å‰Đä―™æ—ķé—ī朊įŸĨ。</translation> </message> </context> <context> <name>QInstaller::FinishedPage</name> <message> <source>Completing the %1 Wizard</source> <translation>æ­ĢåœĻåŪŒæˆ %1 向åŊž</translation> </message> <message> <source>Click Done to exit the %1 Wizard.</source> <translation>单å‡ŧ“åŪŒæˆâ€äŧĨ退凚 %1 向åŊžã€‚</translation> </message> <message> <source>Click Finish to exit the %1 Wizard.</source> <translation>单å‡ŧ“åŪŒæˆâ€äŧĨ退凚 %1 向åŊžã€‚</translation> </message> <message> <source>Restart</source> <translation>重新åŊåŠĻ</translation> </message> <message> <source>Run %1 now.</source> <translation>įŦ‹åģčŋčĄŒ %1。</translation> </message> <message> <source>The %1 Wizard failed.</source> <translation>%1 向åŊžåĪąčīĨ。</translation> </message> </context> <context> <name>QInstaller::GlobalSettingsOperation</name> <message> <source>Settings are not writable</source> <translation>čŪūį―ŪäļåŊ写å…Ĩ</translation> </message> <message> <source>Failed to write settings</source> <translation>写å…ĨčŪūį―ŪåĪąčīĨ</translation> </message> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>3, 4 or 5</source> <translation>34或5äļŠ</translation> </message> </context> <context> <name>QInstaller::InstallIconsOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>1 or 2</source> <translation>1 或 2 äļŠ</translation> </message> <message> <source> (Sourcepath, [Vendorprefix])</source> <translation> (Sourcepath、[Vendorprefix])</translation> </message> <message> <source>Invalid Argument: source folder must not be empty.</source> <translation>参数无效暐文äŧķåĪđäļåū—äļšįĐšã€‚</translation> </message> <message> <source>Cannot backup file %1: %2</source> <translation>无æģ•å·äŧ―文äŧķ %1ïžš%2</translation> </message> <message> <source>Failed to overwrite %1: %2</source> <translation>č͆ᛖ %1 åĪąčīĨïžš%2</translation> </message> <message> <source>Failed to copy file %1: %2</source> <translation>åĪåˆķ文äŧķ %1 åĪąčīĨïžš%2</translation> </message> <message> <source>Cannot create folder at %1: %2</source> <translation>无æģ•åœĻ %1 创åŧšæ–‡äŧķåĪđïžš%2</translation> </message> </context> <context> <name>QInstaller::IntroductionPage</name> <message> <source>Setup - %1</source> <translation>čŪūį―Ū - %1</translation> </message> <message> <source>Welcome to the %1 Setup Wizard.</source> <translation>æŽĒčŋŽä―ŋį”Ļ %1 čŪūį―Ū向åŊžã€‚</translation> </message> <message> <source>Add or remove components</source> <translation>æ·ŧ加或į§ŧé™Īįŧ„äŧķ</translation> </message> <message> <source>Update components</source> <translation>æ›ī新įŧ„äŧķ</translation> </message> <message> <source>Remove all components</source> <translation>删é™Ī所有įŧ„äŧķ</translation> </message> <message> <source>Retrieving information from remote installation sources...</source> <translation>æ­ĢåœĻäŧŽčŋœįĻ‹åŪ‰čĢ…æšæĢ€įīĒäŋĄæŊ...</translation> </message> <message> <source>At least one valid and enabled repository required for this action to succeed.</source> <translation>č́įŧ§įŧ­æ­Īæ“ä―œïžŒč‡ģ少需č́äļ€äļŠæœ‰æ•ˆäļ”å·ēåŊį”Ļįš„å‚Ļ存嚓。</translation> </message> <message> <source>No updates available.</source> <translation>无æ›ī新åŊį”Ļ。</translation> </message> <message> <source> Only local package management available.</source> <translation> äŧ…朎地包įŪĄį†åŊį”Ļ。</translation> </message> <message> <source>Quit</source> <translation>退凚</translation> </message> </context> <context> <name>QInstaller::LicenseAgreementPage</name> <message> <source>License Agreement</source> <translation>čŪļåŊ协čŪŪ</translation> </message> <message> <source>Alt+A</source> <comment>agree license</comment> <translation>Alt+A</translation> </message> <message> <source>Alt+D</source> <comment>do not agree license</comment> <translation>Alt+D</translation> </message> <message> <source>Please read the following license agreement. You must accept the terms contained in this agreement before continuing with the installation.</source> <translation>čŊ·é˜…čŊŧäŧĨäļ‹čŪļåŊ协čŪŪ。åœĻįŧ§įŧ­åŪ‰čĢ…äđ‹å‰ïžŒæ‚Ļåŋ…éĄŧæŽĨ受æ­Ī协čŪŪäļ­åŒ…åŦįš„æĄæŽū。</translation> </message> <message> <source>I accept the license.</source> <translation>我æŽĨ受æ­ĪčŪļåŊ。</translation> </message> <message> <source>I do not accept the license.</source> <translation>我äļæŽĨ受æ­ĪčŪļåŊ。</translation> </message> <message> <source>Please read the following license agreements. You must accept the terms contained in these agreements before continuing with the installation.</source> <translation>čŊ·é˜…čŊŧäŧĨäļ‹čŪļåŊ协čŪŪ。åœĻįŧ§įŧ­åŪ‰čĢ…äđ‹å‰ïžŒæ‚Ļåŋ…éĄŧæŽĨ受čŋ™äš›åčŪŪäļ­åŒ…åŦįš„æĄæŽū。</translation> </message> <message> <source>I accept the licenses.</source> <translation>我æŽĨ受čŋ™äš›čŪļåŊ。</translation> </message> <message> <source>I do not accept the licenses.</source> <translation>我äļæŽĨ受čŋ™äš›čŪļåŊ。</translation> </message> </context> <context> <name>QInstaller::LicenseOperation</name> <message> <source>No license files found to copy.</source> <translation>朊æ‰ū到åŊäŧĨåĪåˆķįš„čŪļåŊ文äŧķ。</translation> </message> <message> <source>Needed installer object in %1 operation is empty.</source> <translation>%1 čŋįŪ—äļ­æ‰€éœ€įš„åŪ‰čĢ…įĻ‹åšåŊđ蹥äļšįĐšã€‚</translation> </message> <message> <source>Can not write license file: %1.</source> <translation>无æģ•写å…ĨčŪļåŊ文äŧķïžš%1.</translation> </message> <message> <source>No license files found to delete.</source> <translation>朊æ‰ū到åŊäŧĨ删é™Īįš„čŪļåŊ文äŧķ。</translation> </message> </context> <context> <name>QInstaller::LineReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 3</source> <translation>恰åĨ― 3 äļŠ</translation> </message> <message> <source>Failed to open '%1' for reading.</source> <translation>打垀“%1”čŊŧ取åĪąčīĨ。</translation> </message> <message> <source>Failed to open '%1' for writing.</source> <translation>打垀“%1”写å…ĨåĪąčīĨ。</translation> </message> </context> <context> <name>QInstaller::MetadataJob</name> <message> <source>Missing package manager core engine.</source> <translation>įžšå°‘åŒ…įŪĄį†å™Ļ内æ ļ垕擎。</translation> </message> <message> <source>Preparing meta information download...</source> <translation>æ­ĢåœĻ准å·äļ‹č――元äŋĄæŊ...</translation> </message> <message> <source>Meta data download canceled.</source> <translation>å·ē取æķˆäļ‹č――元äŋĄæŊ。</translation> </message> <message> <source>Missing proxy credentials.</source> <translation>įžšå°‘äŧĢᐆčŊäđĶ。</translation> </message> <message> <source>Authentication failed.</source> <translation>čšŦäŧ―čŪĪčŊåĪąčīĨ。</translation> </message> <message> <source>Unknown exception during download.</source> <translation>äļ‹č――æ—ķå‘į”Ÿåž‚åļļ。</translation> </message> <message> <source>Retrieving meta information from remote repository...</source> <translation>æ­ĢåœĻäŧŽčŋœįĻ‹å‚Ļ存嚓æĢ€įīĒ元äŋĄæŊ...</translation> </message> <message> <source>Failure to fetch repositories.</source> <translation>čŽ·å–å­˜å‚Ļåš“åĪąčīĨ。</translation> </message> <message> <source>Unknown exception during extracting.</source> <translation>提取æ—ķå‘į”ŸæœŠįŸĨåž‚åļļ。</translation> </message> <message> <source>Extracting meta information...</source> <translation>æ­ĢåœĻ提取元äŋĄæŊ...</translation> </message> <message> <source>Error while extracting '%1': %2</source> <translation>提取“%1”æ—ķå‡šįŽ°é”™čŊŊïžš%2</translation> </message> <message> <source>Unknown exception caught while extracting %1.</source> <translation>提取 %1 æ—ķæ•čŽ·æœŠįŸĨåž‚åļļ。</translation> </message> <message> <source>Cannot open %1 for reading. Error: %2</source> <translation>无æģ•打垀 %1 čŊŧ取。错čŊŊïžš%2</translation> </message> </context> <context> <name>QInstaller::PackageManagerCore</name> <message> <source> Downloading packages...</source> <translation> æ­ĢåœĻäļ‹č――包...</translation> </message> <message> <source>Installation canceled by user</source> <translation>åŪ‰čĢ…å·ēčĒŦį”Ļ户取æķˆ</translation> </message> <message> <source>All downloads finished.</source> <translation>å·ēåŪŒæˆæ‰€æœ‰äļ‹č――。</translation> </message> <message> <source>Cancelling the Installer</source> <translation>æ­ĢåœĻ取æķˆåŪ‰čĢ…įĻ‹åš</translation> </message> <message> <source>Authentication Error</source> <translation>čšŦäŧ―驌čŊé”™čŊŊ</translation> </message> <message> <source>Some components could not be removed completely because admin rights could not be acquired: %1.</source> <translation>į”ąäšŽæ— æģ•取åū—įŪĄį†å‘˜æƒé™ïžŒå› æ­Ī无æģ•åیå…Ļ删é™Ī某䚛įŧ„äŧķïžš%1.</translation> </message> <message> <source>Unknown error.</source> <translation>朊įŸĨ错čŊŊ。</translation> </message> <message> <source>Some components could not be removed completely because an unknown error happened.</source> <translation>į”ąäšŽå‘į”ŸæœŠįŸĨ错čŊŊ因æ­Ī无æģ•åیå…Ļ删é™Ī某䚛įŧ„äŧķ。</translation> </message> <message> <source>Application not running in Package Manager mode!</source> <translation>åš”į”ĻįĻ‹åšæēĄæœ‰åœĻ包įŪĄį†å™ĻæĻĄåžäļ‹čŋčĄŒ!</translation> </message> <message> <source>No installed packages found.</source> <translation>朊æ‰ū到å·ēåŪ‰čĢ…įš„åŒ…ã€‚</translation> </message> <message> <source>Application running in Uninstaller mode!</source> <translation>åš”į”ĻįĻ‹åšæ­ĢåœĻåļč――įĻ‹åšæĻĄåžäļ‹čŋčĄŒ!</translation> </message> <message> <source>Error</source> <translation>错čŊŊ</translation> </message> <message> <source>invalid</source> <translation>无效</translation> </message> <message> <source>Error writing Maintenance Tool</source> <translation>写å…ĨįŧĪå·Ĩ具æ—ķå‘į”Ÿé”™čŊŊ</translation> </message> <message> <source>There is an important update available, please run the updater first.</source> <translation>å‘įŽ°é‡č́æ›ī新åŊį”Ļ。 čŊ·å…ˆčŋčĄŒæ›ī新įĻ‹åšã€‚</translation> </message> <message> <source>Error while elevating access rights.</source> <translation>å‡įš§čŪŋé—Ū权限æ—ķå‡šįŽ°é”™čŊŊ。</translation> </message> </context> <context> <name>QInstaller::PackageManagerCorePrivate</name> <message> <source>Error</source> <translation>错čŊŊ</translation> </message> <message> <source>Access error</source> <translation>čŪŋé—Ū错čŊŊ</translation> </message> <message> <source>Format error</source> <translation>栞垏错čŊŊ</translation> </message> <message> <source>Cannot write installer configuration to %1: %2</source> <translation>无æģ•å°†åŪ‰čĢ…įĻ‹åšé…į―Ū写å…Ĩ %1ïžš%2</translation> </message> <message> <source>Stop Processes</source> <translation>停æ­Ēčŋ›įĻ‹</translation> </message> <message> <source>These processes should be stopped to continue: %1</source> <translation>åŋ…éĄŧ先停æ­ĒäŧĨäļ‹čŋ›įĻ‹æ‰čƒ―įŧ§įŧ­æ“ä―œïžš %1</translation> </message> <message> <source>Installation canceled by user</source> <translation>åŪ‰čĢ…å·ēčĒŦį”Ļ户取æķˆ</translation> </message> <message> <source>Variable 'TargetDir' not set.</source> <translation>朊čŪūį―Ū变量"TargetDir"。</translation> </message> <message> <source>Preparing the installation...</source> <translation>æ­ĢåœĻ准å·åŪ‰čĢ…...</translation> </message> <message> <source>It is not possible to install from network location</source> <translation>äļčƒ―äŧŽį―‘įŧœä―į―Ūčŋ›čĄŒåŪ‰čĢ…</translation> </message> <message> <source>Creating local repository</source> <translation>æ­ĢåœĻ创åŧšæœŽåœ°å‚Ļ存嚓</translation> </message> <message> <source> Installation finished!</source> <translation> åŪ‰čĢ…åŪŒæˆ!</translation> </message> <message> <source> Installation aborted!</source> <translation> åŪ‰čĢ…äļ­æ­Ē!</translation> </message> <message> <source>It is not possible to run that operation from a network location</source> <translation>äļčƒ―äŧŽį―‘įŧœä―į―ŪčŋčĄŒčŊĨæ“ä―œ</translation> </message> <message> <source>Removing deselected components...</source> <translation>æ­ĢåœĻ删é™Ī取æķˆé€‰åŪšįš„įŧ„äŧķ...</translation> </message> <message> <source> Update finished!</source> <translation> æ›ī新åŪŒæˆ!</translation> </message> <message> <source> Update aborted!</source> <translation> æ›ī新äļ­æ­Ē!</translation> </message> <message> <source> Installing component %1</source> <translation> æ­ĢåœĻåŪ‰čĢ…įŧ„äŧķ %1</translation> </message> <message> <source>Installer Error</source> <translation>åŪ‰čĢ…įĻ‹åšé”™čŊŊ</translation> </message> <message> <source>Error during installation process (%1): %2</source> <translation>åŪ‰čĢ…čŋ›įĻ‹(%1)čŋčĄŒæœŸé—īå‡šįŽ°é”™čŊŊïžš %2</translation> </message> <message> <source>Cannot prepare uninstall</source> <translation>无æģ•准å·åļč――</translation> </message> <message> <source>Cannot start uninstall</source> <translation>无æģ•垀始åļč――</translation> </message> <message> <source>Error during uninstallation process: %1</source> <translation>åļč――čŋ›įĻ‹čŋčĄŒæœŸé—īå‡šįŽ°é”™čŊŊïžš %1</translation> </message> <message> <source>Unknown error</source> <translation>朊įŸĨ错čŊŊ</translation> </message> <message> <source>Cannot retrieve remote tree: %1.</source> <translation>无æģ•æĢ€įīĒčŋœįĻ‹æ ‘ïžš%1.</translation> </message> <message> <source>Failure to read packages from: %1.</source> <translation>æœŠčƒ―äŧŽäŧĨäļ‹ä―į―ŪčŊŧ取包%1.</translation> </message> <message> <source>Cannot retrieve meta information: %1</source> <translation>无æģ•æĢ€įīĒ元äŋĄæŊïžš%1</translation> </message> <message> <source>Cannot add temporary update source information.</source> <translation>无æģ•æ·ŧ加äļīæ—ķæ›īæ–°æšäŋĄæŊ。</translation> </message> <message> <source>Cannot find any update source information.</source> <translation>无æģ•æ‰ū到äŧŧä―•æ›ī新暐äŋĄæŊ。</translation> </message> <message> <source>Unresolved dependencies</source> <translation>无æģ•č§Ģ析äūčĩ–</translation> </message> <message> <source>Writing maintenance tool.</source> <translation>写å…ĨįŧĪå·Ĩ具。</translation> </message> <message> <source>Failed to seek in file %1: %2</source> <translation>无æģ•åœĻ文äŧķ %1 äļ­æ‰ū到äŧĨäļ‹å†…åŪđïžš%2</translation> </message> <message> <source>Maintenance tool is not a bundle</source> <translation>įŧĪå·Ĩ具äļæ˜Ŋ捆įŧ‘åĨ—äŧķ</translation> </message> <message> <source>Cannot write maintenance tool data to %1: %2</source> <translation>无æģ•å°†įŧĪå·Ĩ具数æŪ写å…Ĩ到 %1ïžš%2</translation> </message> <message> <source>Cannot remove data file '%1': %2</source> <translation>无æģ•删é™Ī数æŪ文äŧķ“%1”%2</translation> </message> <message> <source>Cannot write maintenance tool to %1: %2</source> <translation>无æģ•å°†įŧĪå·Ĩ具写å…Ĩ到 %1ïžš%2</translation> </message> <message> <source>Cannot write maintenance tool binary data to %1: %2</source> <translation>无æģ•å°†įŧĪå·Ĩ具䚌čŋ›åˆķ数æŪ写å…Ĩ %1ïžš%2</translation> </message> <message> <source>Creating Maintenance Tool</source> <translation>æ­ĢåœĻ创åŧšįŧĪå·Ĩ具</translation> </message> <message> <source>Uninstallation completed successfully.</source> <translation>å·ē成功åŪŒæˆåļč――ã€‚</translation> </message> <message> <source>Uninstallation aborted.</source> <translation>åļč――äļ­æ­Ē。</translation> </message> <message> <source>Dependency cycle between components detected: '%1' and '%2'.</source> <translation>æĢ€æĩ‹åˆ°įŧ„äŧķé—īåūŠįŽŊäūčĩ–“%1”和“%2”。</translation> </message> </context> <context> <name>QInstaller::PackageManagerGui</name> <message> <source>%1 Setup</source> <translation>%1 čŪūį―Ū</translation> </message> <message> <source>Maintain %1</source> <translation>įŧΠ%1</translation> </message> <message> <source>Question</source> <translation>é—Ūéǘ</translation> </message> <message> <source>Settings</source> <translation>čŪūį―Ū</translation> </message> <message> <source>Error</source> <translation>错čŊŊ</translation> </message> <message> <source>It is not possible to install from network location. Please copy the installer to a local drive</source> <translation>äļčƒ―äŧŽį―‘įŧœä―į―Ūčŋ›čĄŒåŪ‰čĢ…ã€‚ čŊ·å°†åŪ‰čĢ…įĻ‹åšåĪåˆķ到朎地įĢį›˜</translation> </message> <message> <source>Do you want to cancel the installation process?</source> <translation>æ‚Ļæ˜ŊåĶæƒģčĶå–æķˆåŪ‰čĢ…čŋ›įĻ‹ïžŸ</translation> </message> <message> <source>Do you want to cancel the uninstallation process?</source> <translation>æ‚Ļæ˜ŊåĶæƒģčĶå–æķˆåļč――čŋ›įĻ‹ïžŸ</translation> </message> <message> <source>Do you want to quit the installer application?</source> <translation>æ‚Ļæ˜ŊåĶæƒģčĶé€€å‡šåŪ‰čĢ…įĻ‹åšïžŸ</translation> </message> <message> <source>Do you want to quit the uninstaller application?</source> <translation>æ‚Ļæ˜ŊåĶæƒģčĶé€€å‡šåļč――įĻ‹åšïžŸ</translation> </message> <message> <source>Do you want to quit the maintenance application?</source> <translation>æ‚Ļæ˜ŊåĶæƒģčĶé€€å‡šįŧĪįĻ‹åšïžŸ</translation> </message> </context> <context> <name>QInstaller::PerformInstallationForm</name> <message> <source>&Show Details</source> <translation>æ˜ūįĪščŊĶįŧ†äŋĄæŊ(&S)</translation> </message> <message> <source>&Hide Details</source> <translation>隐藏čŊĶįŧ†äŋĄæŊ(&H)</translation> </message> </context> <context> <name>QInstaller::PerformInstallationPage</name> <message> <source>U&ninstall</source> <translation>åļč――(&U)</translation> </message> <message> <source>Uninstalling %1</source> <translation>æ­ĢåœĻåļč―― %1</translation> </message> <message> <source>&Update</source> <translation>æ›ī新(&U)</translation> </message> <message> <source>Updating components of %1</source> <translation>æ­ĢåœĻæ›ī新 %1 įš„įŧ„äŧķ</translation> </message> <message> <source>&Install</source> <translation>åŪ‰čĢ…(&I)</translation> </message> <message> <source>Installing %1</source> <translation>æ­ĢåœĻåŪ‰čĢ… %1</translation> </message> </context> <context> <name>QInstaller::ProxyCredentialsDialog</name> <message> <source>Dialog</source> <translation>åŊđčŊæĄ†</translation> </message> <message> <source>The proxy %1 requires a username and password.</source> <translation>äŧĢᐆ %1 需č́į”Ļ户名和åŊ†į ã€‚</translation> </message> <message> <source>Username:</source> <translation>į”Ļ户名</translation> </message> <message> <source>Username</source> <translation>į”Ļ户名</translation> </message> <message> <source>Password:</source> <translation>åŊ†į ïžš</translation> </message> <message> <source>Password</source> <translation>åŊ†į </translation> </message> </context> <context> <name>QInstaller::ReadyForInstallationPage</name> <message> <source>U&ninstall</source> <translation>åļč――(&U)</translation> </message> <message> <source>Ready to Uninstall</source> <translation>å·ē做åĨ―åļč――å‡†å·</translation> </message> <message> <source>Setup is now ready to begin removing %1 from your computer.<br><font color="red">The program directory %2 will be deleted completely</font>, including all content in that directory!</source> <translation>čŪūį―ŪįĻ‹åšįŽ°å·ē准åĪ‡å°ąįŧŠïžŒåŊäŧĨ垀始äŧŽæ‚Ļįš„čŪĄįŪ—æœšäļ­åˆ é™Ī %1。<br><font color="red">įĻ‹åšį›Ūå―• %2 将čĒŦåیå…Ļ删é™Ī</font>包拎čŊĨį›Ūå―•äļ­įš„æ‰€æœ‰å†…åŪđ!</translation> </message> <message> <source>U&pdate</source> <translation>æ›ī新(&U)</translation> </message> <message> <source>Ready to Update Packages</source> <translation>å·ē做åĨ―æ›īæ–°åŒ…įš„å‡†å·</translation> </message> <message> <source>Setup is now ready to begin updating your installation.</source> <translation>čŪūį―ŪįĻ‹åšįŽ°å·ē准åĪ‡å°ąįŧŠïžŒåŊäŧĨ垀始æ›ī新æ‚Ļįš„åŪ‰čĢ…æ–‡äŧķ。</translation> </message> <message> <source>&Install</source> <translation>åŪ‰čĢ…(&I)</translation> </message> <message> <source>Ready to Install</source> <translation>å·ē做åĨ―åŪ‰č̅准å·</translation> </message> <message> <source>Setup is now ready to begin installing %1 on your computer.</source> <translation>čŪūį―ŪįĻ‹åšįŽ°å·ē准åĪ‡å°ąįŧŠïžŒåŊäŧĨ垀始åœĻæ‚Ļįš„čŪĄįŪ—æœšäļŠåŪ‰čĢ… %1。</translation> </message> <message> <source>Not enough disk space to store temporary files and the installation! Available space: %1, at least required %2.</source> <translation>įĢį›˜įĐšé—īäļčķģ无æģ•å­˜å‚Ļäļīæ—ķæ–‡äŧķ和åŪ‰čĢ…æ–‡äŧķ!åŊį”ĻįĐšé—īïžš%1č‡ģ少需č́ %2。</translation> </message> <message> <source>Not enough disk space to store all selected components! Available space: %1, at least required: %2.</source> <translation>įĢį›˜įĐšé—īäļčķģ无æģ•å­˜å‚Ļ所有选åŪšįš„įŧ„äŧķ!åŊį”ĻįĐšé—īïžš%1č‡ģ少需čĶïžš%2。</translation> </message> <message> <source>Not enough disk space to store temporary files! Available space: %1, at least required: %2.</source> <translation>įĢį›˜įĐšé—īäļčķģ无æģ•å­˜å‚Ļäļīæ—ķæ–‡äŧķ!åŊį”ĻįĐšé—īïžš%1č‡ģ少需čĶïžš%2。</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 1% of the volume's space available afterwards. %1</source> <translation>æ‚Ļ选åۚį”Ļ䚎åŪ‰čĢ…æ–‡äŧķįš„å·äžžäđŽæœ‰čķģåĪŸįš„įĐšé—ī存å‚ĻåŪ‰čĢ…æ–‡äŧķïžŒä―†å­˜å‚Ļ后čŊĨå·įš„åŊį”ĻįĐšé—ī将äļåˆ° 1%。%1</translation> </message> <message> <source>The volume you selected for installation seems to have sufficient space for installation, but there will be less than 100 MB available afterwards. %1</source> <translation>æ‚Ļ选åۚį”Ļ䚎åŪ‰čĢ…æ–‡äŧķįš„å·äžžäđŽæœ‰čķģåĪŸįš„įĐšé—ī存å‚ĻåŪ‰čĢ…æ–‡äŧķïžŒä―†å­˜å‚Ļ后čŊĨå·įš„åŊį”ĻįĐšé—ī将äļåˆ° 100 MB。%1</translation> </message> <message> <source>Components about to be removed.</source> <translation>įŧ„äŧķåģ将čĒŦ删é™Ī。</translation> </message> <message> <source>Installation will use %1 of disk space.</source> <translation>åŪ‰čĢ…å°†å į”Ļ %1 įĢį›˜įĐšé—ī。</translation> </message> <message> <source>Cannot resolve all dependencies.</source> <translation>无æģ•č§Ģ析所有äūčĩ–。</translation> </message> </context> <context> <name>QInstaller::RegisterFileTypeOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°:å·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>2 to 5</source> <translation>2 到 5 äļŠ</translation> </message> <message> <source>Register File Type: Invalid arguments</source> <translation>åŊ„å­˜å™Ļ文äŧķįąŧ型参数无效</translation> </message> <message> <source>Registering file types is only supported on Windows.</source> <translation>äŧ…æ”Ŋ持åœĻ Windows äģĻ册文äŧķįąŧ型。</translation> </message> </context> <context> <name>QInstaller::RemoteObject</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>å‘é€å‘―äŧĪïžš %1 后无æģ•čŊŧ取所有数æŪ。 期望 %2å­—čŠ‚ïžŒ æ”ķ到 %3å­—čŠ‚ã€‚ 错čŊŊïžš %4</translation> </message> </context> <context> <name>QInstaller::RemoteServerConnection</name> <message> <source>Cannot read all data after sending command: %1. Bytes expected: %2, Bytes received: %3. Error: %4</source> <translation>å‘é€å‘―äŧĪïžš %1 后无æģ•čŊŧ取所有数æŪ。 期望 %2å­—čŠ‚ïžŒ æ”ķ到 %3å­—čŠ‚ã€‚ 错čŊŊïžš %4</translation> </message> </context> <context> <name>QInstaller::ReplaceOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 3</source> <translation>恰åĨ― 3 äļŠ</translation> </message> <message> <source>Failed to open %1 for reading</source> <translation>打垀 %1 čŊŧ取åĪąčīĨ</translation> </message> <message> <source>Failed to open %1 for writing</source> <translation>打垀 %1 写å…ĨåĪąčīĨ</translation> </message> </context> <context> <name>QInstaller::Resource</name> <message> <source>Cannot open Resource '%1' read-only.</source> <translation>无æģ•äŧĨ及čŊŧæ–đ垏打垀čĩ„暐“%1”。</translation> </message> <message> <source>Read failed after %1 bytes: %2</source> <translation>čŊŧ取 %1 å­—čŠ‚åŽåĪąčīĨïžš%2</translation> </message> <message> <source>Write failed after %1 bytes: %2</source> <translation>写å…Ĩ %1 å­—čŠ‚åŽåĪąčīĨïžš%2</translation> </message> </context> <context> <name>QInstaller::RestartPage</name> <message> <source>Completing the %1 Setup Wizard</source> <translation>æ­ĢåœĻåŪŒæˆ %1 čŪūį―Ū向åŊž</translation> </message> </context> <context> <name>QInstaller::ScriptEngine</name> <message> <source>Cannot open the requested script file at %1: %2.</source> <translation>无æģ•æ‰“åž€ä―äšŽ %1 įš„čŊ·æą‚č„šæœŽæ–‡äŧķïžš%2。</translation> </message> <message> <source>Exception while loading the component script '%1'. (%2)</source> <translation>åŠ č――įŧ„äŧķč„šæœŽæ—ķå‡šįŽ°åž‚åļļ“%1”。%2</translation> </message> </context> <context> <name>QInstaller::SelfRestartOperation</name> <message> <source>Installer object needed in '%1' operation is empty.</source> <translation>“%1”čŋįŪ—äļ­æ‰€éœ€įš„åŪ‰čĢ…įĻ‹åšåŊđ蹥äļšįĐšã€‚</translation> </message> <message> <source>Self Restart: Only valid within updater or packagemanager mode.</source> <translation>臩重åŊïžšäŧ…åœĻæ›ī新įĻ‹åšæˆ–åŒ…įŪĄį†å™ĻæĻĄåžäœ‰æ•ˆã€‚</translation> </message> <message> <source>Self Restart: Invalid arguments</source> <translation>臩重åŊ参数无效</translation> </message> </context> <context> <name>QInstaller::ServerAuthenticationDialog</name> <message> <source>Server Requires Authentication</source> <translation>æœåŠĄå™Ļ需č́čšŦäŧ―驌čŊ</translation> </message> <message> <source>You need to supply a username and password to access this site.</source> <translation>æ‚Ļ需čĶæäū›į”Ļ户名和åŊ†į æĨčŪŋé—Ūæ­ĪįŦ™į‚đ。</translation> </message> <message> <source>Username:</source> <translation>į”Ļ户名</translation> </message> <message> <source>Password:</source> <translation>åŊ†į ïžš</translation> </message> <message> <source>%1 at %2</source> <translation>ä―äšŽ %2 įš„ %1</translation> </message> </context> <context> <name>QInstaller::SettingsOperation</name> <message> <source>Missing argument(s) '%1' calling '%2' with arguments '%3'.</source> <translation>įžšå°‘å‚æ•°â€œ%1”ä―ŋį”Ļ参数“%3”æĨ调į”Ļ“%2”。</translation> </message> <message> <source>Current method argument calling '%1' with arguments '%2' is not supported. Please use set, remove, add_array_value or remove_array_value.</source> <translation>äļæ”ŊæŒå―“å‰åļĶæœ‰å‚数“%2â€įš„æ–đæģ•å‚æ•°č°ƒį”Ļ“%1”。čŊ·ä―ŋį”Ļsetremoveadd_array_valueæˆ–č€…remove_array_value。</translation> </message> </context> <context> <name>QInstaller::SimpleMoveFileOperation</name> <message> <source>Invalid arguments in %0: %1 arguments given, %2 expected%3.</source> <translation>%0 äļ­å­˜åœĻæ— æ•ˆįš„å‚æ•°ïžšå·ēįŧ™åۚ %1 äļŠå‚数%2 åš”äļš %3 äļŠã€‚</translation> </message> <message> <source>exactly 2</source> <translation>恰åĨ― 2 äļŠ</translation> </message> <message> <source>None of the arguments can be empty: source '%1', target '%2'.</source> <translation>参数均äļåū—äļšįĐšïžšæšâ€œ%1”į›Ū标“%2”。</translation> </message> <message> <source>Move '%1' to '%2'.</source> <translation>将“%1”į§ŧåŠĻ到“%2”。</translation> </message> <message> <source>Cannot move source '%1' to target '%2', because target exists and is not removable.</source> <translation>无æģ•将暐“%1”į§ŧåŠĻ到į›Ū标“%2”因äļšį›Ū标å·ēįŧå­˜åœĻäļ”äļåŊ删é™Ī。</translation> </message> <message> <source>Cannot move source '%1' to target '%2': %3</source> <translation>无æģ•将暐“%1”į§ŧåŠĻ到į›Ū标“%2”%3</translation> </message> </context> <context> <name>QInstaller::StartMenuDirectoryPage</name> <message> <source>Start Menu shortcuts</source> <translation>åž€å§‹čœå•åŋŦ捷æ–đ垏</translation> </message> <message> <source>Select the Start Menu in which you would like to create the program's shortcuts. You can also enter a name to create a new folder.</source> <translation>选æ‹Đæ‚Ļåœ›åœĻå…ķäļ­åˆ›åŧšįĻ‹åšåŋŦ捷æ–đåžįš„åž€å§‹čœå•ã€‚æ‚Ļčŋ˜åŊäŧĨčū“å…Ĩåį§°äŧĨ创åŧšæ–°æ–‡äŧķåĪđ。</translation> </message> </context> <context> <name>QInstaller::TargetDirectoryPage</name> <message> <source>Installation Folder</source> <translation>åŪ‰čĢ…æ–‡äŧķåĪđ</translation> </message> <message> <source>Please specify the folder where %1 will be installed.</source> <translation>čŊ·æŒ‡åŪšå°†åœĻå…ķäļ­åŪ‰čĢ… %1 įš„æ–‡äŧķåĪđ。</translation> </message> <message> <source>Alt+R</source> <comment>browse file system to choose a file</comment> <translation>Alt+R</translation> </message> <message> <source>B&rowse...</source> <translation>æĩč§ˆ(&R)...</translation> </message> <message> <source>Error</source> <translation>错čŊŊ</translation> </message> <message> <source>As the install directory is completely deleted, installing in %1 is forbidden.</source> <translation>į”ąäšŽåŪ‰čĢ…į›Ūå―•å·ēåیå…Ļ删é™Ī因æ­Īį́æ­ĒåœĻ %1 äļ­čŋ›čĄŒåŪ‰čĢ…ã€‚</translation> </message> <message> <source>Warning</source> <translation>č­Ķ告</translation> </message> <message> <source>Select Installation Folder</source> <translation>选æ‹ĐåŪ‰čĢ…æ–‡äŧķåĪđ</translation> </message> <message> <source>The folder you selected already exists and contains an installation. Choose a different target for installation.</source> <translation>æ‚Ļ选æ‹Đįš„æ–‡äŧķåĪđå·ēįŧå­˜åœĻåđķ包åŦåŪ‰čĢ…æ–‡äŧķ。 čŊ·é€‰æ‹Đå…ķäŧ–åŪ‰čĢ…į›Ū标。</translation> </message> <message> <source>You have selected an existing, non-empty folder for installation. Note that it will be completely wiped on uninstallation of this application. It is not advisable to install into this folder as installation might fail. Do you want to continue?</source> <translation>æ‚Ļå·ēäļšåŪ‰čĢ…æ–‡äŧķ选æ‹Đ乆äļ€äļŠįŽ°æœ‰įš„éžįĐšæ–‡äŧķåĪđ。 čŊ·æģĻæ„ïžŒåļč――æ­Īåš”į”ĻįĻ‹åšæ—ķäžšå°†čŊĨ文äŧķåĪđåیå…Ļæ“Ķé™Ī。 äļåŧščŪŪæ‚ĻåœĻčŊĨ文äŧķåĪđäļ­åŪ‰čĢ…åš”į”ĻįĻ‹åšïžŒå› äļšåŪ‰čĢ…åŊčƒ―äžšåĪąčīĨ。 æ‚Ļæ˜ŊåĶč́įŧ§įŧ­?</translation> </message> <message> <source>You have selected an existing file or symlink, please choose a different target for installation.</source> <translation>æ‚Ļå·ēäļšåŪ‰čĢ…æ–‡äŧķ选æ‹Đ乆äļ€äļŠįŽ°æœ‰įš„æ–‡äŧķæˆ–įŽĶ号é“ūæŽĨčŊ·é€‰æ‹Đå…ķäŧ–åŪ‰čĢ…į›Ū标。</translation> </message> <message> <source>The installation path cannot be empty, please specify a valid folder.</source> <translation>åŪ‰čĢ…č·Ŋåū„äļčƒ―äļšįĐšïžŒčŊ·æŒ‡åۚäļ€äļŠæœ‰æ•ˆįš„æ–‡äŧķåĪđ。</translation> </message> <message> <source>The installation path cannot be relative, please specify an absolute path.</source> <translation>åŪ‰čĢ…č·Ŋåū„äļčƒ―æ˜Ŋį›ļåŊđč·Ŋåū„čŊ·æŒ‡åۚäļ€äļŠįŧåŊđč·Ŋåū„。</translation> </message> <message> <source>The path or installation directory contains non ASCII characters. This is currently not supported! Please choose a different path or installation directory.</source> <translation>č·Ŋåū„或åŪ‰čĢ…į›Ūå―•åŒ…åŦ非 ASCII 字įŽĶ。į›Ū前äļæ”Ŋ持æ­Īįąŧ字įŽĶ!čŊ·é€‰æ‹Đå…ķäŧ–č·Ŋåū„或åŪ‰čĢ…į›Ūå―•ã€‚</translation> </message> <message> <source>The path you have entered is too long, please make sure to specify a valid path.</source> <translation>æ‚Ļčū“å…Ĩįš„č·Ŋåū„čŋ‡é•ŋčŊ·åŠĄåŋ…指åۚäļ€äļŠæœ‰æ•ˆįš„č·Ŋåū„。</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid target.</source> <translation>æ‚Ļčū“å…Ĩįš„č·Ŋåū„无效čŊ·åŠĄåŋ…指åۚäļ€äļŠæœ‰æ•ˆįš„į›Ū标。</translation> </message> <message> <source>The path you have entered is not valid, please make sure to specify a valid drive.</source> <translation>æ‚Ļčū“å…Ĩįš„č·Ŋåū„无效čŊ·åŠĄåŋ…指åۚäļ€äļŠæœ‰æ•ˆįš„įĢį›˜ã€‚</translation> </message> <message> <source>The installation path must not end with '.', please specify a valid folder.</source> <translation>åŪ‰čĢ…č·Ŋåū„äļčƒ―äŧĨ“.”įŧ“束。 čŊ·æŒ‡åۚäļ€äļŠæœ‰æ•ˆįš„æ–‡äŧķåĪđ。</translation> </message> <message> <source>The installation path must not contain '%1', please specify a valid folder.</source> <translation>åŪ‰čĢ…č·Ŋåū„äļåū—包åŦ“%1”čŊ·æŒ‡åۚäļ€äļŠæœ‰æ•ˆįš„æ–‡äŧķåĪđ。</translation> </message> </context> <context> <name>QInstaller::TestRepository</name> <message> <source>Empty repository URL.</source> <translation>å‚Ļ存嚓 URL äļšįĐšã€‚</translation> </message> <message> <source>URL scheme not supported: %1 (%2).</source> <translation>äļæ”ŊæŒįš„ URL æ–đæĄˆïžš%1 (%2)。</translation> </message> <message> <source>Got a timeout while testing: '%1'</source> <translation>æĩ‹čŊ•“%1”æ—ķčķ…æ—ķ</translation> </message> <message> <source>Cannot parse Updates.xml! Error: %1.</source> <translation>无æģ•č§Ģ析 Updates.xml! 错čŊŊïžš%1。</translation> </message> <message> <source>Updates.xml could not be opened for reading!</source> <translation>无æģ•打垀 Updates.xml čŋ›čĄŒčŊŧ取!</translation> </message> <message> <source>Updates.xml could not be found on server!</source> <translation>无æģ•åœĻæœåŠĄå™Ļä‰ū到 Updates.xml!</translation> </message> </context> <context> <name>QObject</name> <message> <source>Authorization required</source> <translation>需čĶæŽˆæƒ</translation> </message> <message> <source>Enter your password to authorize for sudo:</source> <translation>čū“å…Ĩæ‚Ļįš„ sudo åŊ†į äŧĨčŋ›čĄŒæŽˆæƒïžš</translation> </message> <message> <source>Error acquiring admin rights</source> <translation>čŽ·å–įŪĄį†å‘˜æƒé™æ—ķå‡šįŽ°é”™čŊŊ</translation> </message> </context> <context> <name>RemoteClient</name> <message> <source>Cannot get authorization.</source> <translation>无æģ•获åū—授权。</translation> </message> <message> <source>Cannot get authorization that is needed for continuing the installation. Either abort the installation or use the fallback solution by running %1 as root and then clicking OK.</source> <translation>无æģ•获åū—įŧ§įŧ­åŪ‰čĢ…æ‰€éœ€įš„æŽˆæƒã€‚ æ‚ĻåŊäŧĨäļ­æ­ĒåŪ‰čĢ…ïžŒäđŸåŊäŧĨä―ŋį”Ļå·į”Ļč§Ģå†ģæ–đæĄˆïžŒäŧĨæ đį”Ļ户čšŦäŧ―čŋčĄŒ %1 į„ķ后单å‡ŧ“įĄŪåŪšâ€ã€‚</translation> </message> </context> <context> <name>ResourceCollectionManager</name> <message> <source>Cannot open resource %1: %2</source> <translation>无æģ•打垀čĩ„暐 %1ïžš%2</translation> </message> </context> <context> <name>Settings</name> <message> <source>Cannot open settings file %1 for reading: %2</source> <translation>无æģ•打垀čŪūį―Ūæ–‡äŧķ %1 čŋ›čĄŒčŊŧ取%2</translation> </message> </context> <context> <name>SettingsDialog</name> <message> <source>Settings</source> <translation>čŪūį―Ū</translation> </message> <message> <source>Network</source> <translation>į―‘įŧœ</translation> </message> <message> <source>No proxy</source> <translation>无äŧĢᐆ</translation> </message> <message> <source>System proxy settings</source> <translation>įģŧįŧŸäŧĢᐆčŪūį―Ū</translation> </message> <message> <source>Manual proxy configuration</source> <translation>手åŠĻčŪūį―ŪäŧĢᐆ</translation> </message> <message> <source>HTTP proxy:</source> <translation>HTTP äŧĢį†ïžš</translation> </message> <message> <source>Port:</source> <translation>įŦŊåĢïžš</translation> </message> <message> <source>FTP proxy:</source> <translation>FTP äŧĢį†ïžš</translation> </message> <message> <source>Repositories</source> <translation>å‚Ļ存嚓</translation> </message> <message> <source>Add Username and Password for authentication if needed.</source> <translation>åĶ‚æžœéœ€čĶïžŒčŊ·æ·ŧ加į”Ļ䚎čšŦäŧ―驌čŊįš„į”Ļ户名和åŊ†į ã€‚</translation> </message> <message> <source>Use temporary repositories only</source> <translation>åŠčƒ―ä―ŋį”Ļäļīæ—ķå‚Ļ存嚓</translation> </message> <message> <source>Add</source> <translation>æ·ŧ加</translation> </message> <message> <source>Remove</source> <translation>删é™Ī</translation> </message> <message> <source>Test</source> <translation>æĩ‹čŊ•</translation> </message> <message> <source>Show Passwords</source> <translation>æ˜ūįĪšåŊ†į </translation> </message> <message> <source>Check this to use repository during fetch.</source> <translation>选äļ­æ­ĪéĄđåŊåœĻ提取期é—īä―ŋį”Ļå‚Ļ存嚓。</translation> </message> <message> <source>Add the username to authenticate on the server.</source> <translation>åœĻæœåŠĄå™Ļä·ŧ加į”Ļ䚎čšŦäŧ―驌čŊįš„į”Ļ户名。</translation> </message> <message> <source>Add the password to authenticate on the server.</source> <translation>åœĻæœåŠĄå™Ļä·ŧ加į”Ļ䚎čšŦäŧ―驌čŊįš„åŊ†į ã€‚</translation> </message> <message> <source>The servers URL that contains a valid repository.</source> <translation>包åŦ有效å‚Ļå­˜åš“įš„æœåŠĄå™Ļ URL。</translation> </message> <message> <source>There was an error testing this repository.</source> <translation>æĩ‹čŊ•å‚Ļ存嚓æ—ķå‡šįŽ°é”™čŊŊ。</translation> </message> <message> <source>Do you want to disable the tested repository?</source> <translation>æ‚Ļæ˜ŊåĶč́į́į”Ļæĩ‹čŊ•čŋ‡įš„å‚Ļ存嚓?</translation> </message> <message> <source>Hide Passwords</source> <translation>隐藏åŊ†į </translation> </message> <message> <source>Use</source> <translation>ä―ŋį”Ļ</translation> </message> <message> <source>Username</source> <translation>į”Ļ户名</translation> </message> <message> <source>Password</source> <translation>åŊ†į </translation> </message> <message> <source>Repository</source> <translation>å‚Ļ存嚓</translation> </message> <message> <source>Default repositories</source> <translation>éŧ˜čŪĪå‚Ļ存嚓</translation> </message> <message> <source>Temporary repositories</source> <translation>äļīæ—ķå‚Ļ存嚓</translation> </message> <message> <source>User defined repositories</source> <translation>į”Ļ户åۚäđ‰å‚Ļ存嚓</translation> </message> </context> <context> <name>UpdateOperation</name> <message> <source>Registry path %1 is not writable</source> <translation>æģĻ册č·Ŋåū„ %1 äļåŊ写å…Ĩ</translation> </message> <message> <source>Cannot write to registry path %1</source> <translation>无æģ•写å…ĨæģĻ册č·Ŋåū„ %1</translation> </message> <message> <source>Renaming %1 into %2 failed with %3.</source> <translation>é‡å‘―å %1 äļš %2 åĪąčīĨ因äļš %3。</translation> </message> </context> </TS> ����������������������������������������������������������������������������������������������������������src/sdk/translations/translations.pro���������������������������������������������������������������0000664�0000000�0000000�00000002654�13253666515�0020435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEMPLATE = aux include(../../../installerfw.pri) !exists($$LUPDATE): return() IB_TRANSLATIONS = $$files($$PWD/*_??.ts) IB_TRANSLATIONS -= $$PWD/ifw_en.ts wd = $$toNativeSeparators($$IFW_SOURCE_TREE) sources = src lupdate_opts = -locations relative -no-ui-lines -no-sort IB_ALL_TRANSLATIONS = $$IB_TRANSLATIONS $$PWD/ifw_untranslated.ts for(file, IB_ALL_TRANSLATIONS) { lang = $$replace(file, .*_([^/]*)\\.ts, \\1) v = ts-$${lang}.commands $$v = cd $$wd && $$LUPDATE $$lupdate_opts $$sources -ts $$file QMAKE_EXTRA_TARGETS += ts-$$lang } ts-all.commands = cd $$wd && $$LUPDATE $$lupdate_opts $$sources -ts $$IB_ALL_TRANSLATIONS QMAKE_EXTRA_TARGETS += ts-all isEqual(QMAKE_DIR_SEP, /) { commit-ts.commands = \ cd $$wd; \ git add -N src/sdk/translations/*_??.ts && \ for f in `git diff-files --name-only src/sdk/translations/*_??.ts`; do \ $$LCONVERT -locations none -i \$\$f -o \$\$f; \ done; \ git add src/sdk/translations/*_??.ts && git commit } else { commit-ts.commands = \ cd $$wd && \ git add -N src/sdk/translations/*_??.ts && \ for /f usebackq %%f in (`git diff-files --name-only src/sdk/translations/*_??.ts`) do \ $$LCONVERT -locations none -i %%f -o %%f $$escape_expand(\\n\\t) \ cd $$wd && git add src/sdk/translations/*_??.ts && git commit } QMAKE_EXTRA_TARGETS += commit-ts ������������������������������������������������������������������������������������src/sdk/updatechecker.cpp���������������������������������������������������������������������������0000664�0000000�0000000�00000011470�13253666515�0015760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "updatechecker.h" #include <binaryformatenginehandler.h> #include <component.h> #include <errors.h> #include <init.h> #include <runoncechecker.h> #include <packagemanagercore.h> #include <productkeycheck.h> #include <QDir> #include <QDomDocument> #include <iostream> UpdateChecker::UpdateChecker(int &argc, char *argv[]) : SDKApp<QCoreApplication>(argc, argv) { QInstaller::init(); // register custom operations } int UpdateChecker::check() { RunOnceChecker runCheck(QDir::tempPath() + QLatin1Char('/') + qApp->applicationName() + QLatin1String("15021976.lock")); if (runCheck.isRunning(RunOnceChecker::ConditionFlag::Lockfile)) { // It is possible to install an application and thus the maintenance tool into a // directory that requires elevated permission to create a lock file. Since this // cannot be done without requesting credentials from the user, we silently ignore // the fact that we could not create the lock file and check the running processes. if (runCheck.isRunning(RunOnceChecker::ConditionFlag::ProcessList)) throw QInstaller::Error(QLatin1String("An instance is already checking for updates.")); } QString fileName = datFile(binaryFile()); quint64 cookie = QInstaller::BinaryContent::MagicCookieDat; if (fileName.isEmpty()) { fileName = binaryFile(); cookie = QInstaller::BinaryContent::MagicCookie; } QFile binary(fileName); QInstaller::openForRead(&binary); qint64 magicMarker; QList<QInstaller::OperationBlob> operations; QInstaller::ResourceCollectionManager manager; QInstaller::BinaryContent::readBinaryContent(&binary, &operations, &manager, &magicMarker, cookie); if (magicMarker == QInstaller::BinaryContent::MagicInstallerMarker) throw QInstaller::Error(QLatin1String("Installers cannot check for updates.")); SDKApp::registerMetaResources(manager.collectionByName("QResources")); QInstaller::PackageManagerCore core(QInstaller::BinaryContent::MagicUpdaterMarker, operations); QInstaller::PackageManagerCore::setVirtualComponentsVisible(true); { using namespace QInstaller; ProductKeyCheck::instance()->init(&core); ProductKeyCheck::instance()->addPackagesFromXml(QLatin1String(":/metadata/Updates.xml")); BinaryFormatEngineHandler::instance()->registerResources(manager.collections()); } if (!core.fetchRemotePackagesTree()) throw QInstaller::Error(core.error()); const QList<QInstaller::Component *> components = core.components(QInstaller::PackageManagerCore::ComponentType::Root); if (components.isEmpty()) throw QInstaller::Error(QLatin1String("There are currently no updates available.")); QDomDocument doc; QDomElement root = doc.createElement(QLatin1String("updates")); doc.appendChild(root); foreach (QInstaller::Component *component, components) { QDomElement update = doc.createElement(QLatin1String("update")); update.setAttribute(QLatin1String("name"), component->value(QInstaller::scDisplayName)); update.setAttribute(QLatin1String("version"), component->value(QInstaller::scVersion)); update.setAttribute(QLatin1String("size"), component->value(QInstaller::scUncompressedSize)); root.appendChild(update); } std::cout << qPrintable(doc.toString(4)) << std::endl; return EXIT_SUCCESS; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/sdk/updatechecker.h�����������������������������������������������������������������������������0000664�0000000�0000000�00000003053�13253666515�0015423�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef UPDATECHECKER_H #define UPDATECHECKER_H #include "sdkapp.h" class UpdateChecker : public SDKApp<QCoreApplication> { Q_OBJECT Q_DISABLE_COPY(UpdateChecker) public: UpdateChecker(int &argc, char *argv[]); int check(); }; #endif // UPDATECHECKER_H �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������src/src.pro�����������������������������������������������������������������������������������������0000664�0000000�0000000�00000000116�13253666515�0013170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEMPLATE = subdirs SUBDIRS += libs sdk sdk/translations sdk.depends = libs ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sync.profile����������������������������������������������������������������������������������������0000664�0000000�0000000�00000000210�13253666515�0013421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%dependencies = ( "qtbase" => "", "qtdeclarative" => "", "qttools" => "", "qtwinextras" => "", ); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/����������������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0012234�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/README����������������������������������������������������������������������������������������0000664�0000000�0000000�00000000115�13253666515�0013111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������These are files for the unfinished test framework of the installer framework.���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/�����������������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0013204�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/auto.pro���������������������������������������������������������������������������������0000664�0000000�0000000�00000000063�13253666515�0014675�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEMPLATE = subdirs SUBDIRS += \ installer �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/�������������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0015201�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/binaryformat/������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0017676�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/binaryformat/binaryformat.pro��������������������������������������������������0000664�0000000�0000000�00000000126�13253666515�0023114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += xml SOURCES += tst_binaryformat.cpp ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/binaryformat/tst_binaryformat.cpp����������������������������������������������0000664�0000000�0000000�00000040535�13253666515�0024000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <binarycontent.h> #include <binaryformat.h> #include <errors.h> #include <fileio.h> #include <updateoperation.h> #include <QTest> #include <QTemporaryFile> static const qint64 scTinySize = 72704LL; static const qint64 scSmallSize = 524288LL; static const qint64 scLargeSize = 2097152LL; using namespace QInstaller; struct Layout : public QInstaller::BinaryLayout { qint64 metaSegmentsCount; qint64 operationsCount; qint64 collectionCount; }; class TestOperation : public KDUpdater::UpdateOperation { public: TestOperation(const QString &name) : KDUpdater::UpdateOperation(0) { setName(name); } virtual void backup() {} virtual bool performOperation() { return true; } virtual bool undoOperation() { return true; } virtual bool testOperation() { return true; } virtual KDUpdater::UpdateOperation *clone() const { return 0; } }; class tst_BinaryFormat : public QObject { Q_OBJECT private slots: void initTestCase() { TestOperation op(QLatin1String("Operation 1")); op.setValue(QLatin1String("key"), QLatin1String("Operation 1 value.")); op.setArguments(QStringList() << QLatin1String("arg1") << QLatin1String("arg2")); m_operations.append(OperationBlob(op.name(), op.toXml().toString())); op = TestOperation(QLatin1String("Operation 2")); op.setValue(QLatin1String("key"), QLatin1String("Operation 2 value.")); op.setArguments(QStringList() << QLatin1String("arg1") << QLatin1String("arg2")); m_operations.append(OperationBlob(op.name(), op.toXml().toString())); } void findMagicCookieSmallFile() { QTemporaryFile file; file.open(); try { QInstaller::blockingWrite(&file, QByteArray(scSmallSize, '1')); QInstaller::appendInt64(&file, QInstaller::BinaryContent::MagicCookie); QCOMPARE(QInstaller::BinaryContent::findMagicCookie(&file, QInstaller::BinaryContent::MagicCookie), scSmallSize); } catch (const QInstaller::Error &error) { QFAIL(qPrintable(error.message())); } catch (...) { QFAIL("Unexpected error."); } } void findMagicCookieLargeFile() { QTemporaryFile file; file.open(); try { QInstaller::blockingWrite(&file, QByteArray(scLargeSize, '1')); QInstaller::appendInt64(&file, QInstaller::BinaryContent::MagicCookie); QInstaller::blockingWrite(&file, QByteArray(scTinySize, '2')); QCOMPARE(QInstaller::BinaryContent::findMagicCookie(&file, QInstaller::BinaryContent::MagicCookie), scLargeSize); } catch (const QInstaller::Error &error) { QFAIL(qPrintable(error.message())); } catch (...) { QFAIL("Unexpected error."); } } void findMagicCookieWithError() { QTemporaryFile file; file.open(); try { QInstaller::blockingWrite(&file, QByteArray(scTinySize, '1')); // throws QInstaller::BinaryContent::findMagicCookie(&file, QInstaller::BinaryContent::MagicCookie); } catch (const QInstaller::Error &error) { QCOMPARE(qPrintable(error.message()), "No marker found, stopped after 71.00 KiB."); } catch (...) { QFAIL("Unexpected error."); } } void writeBinaryContent() { QTemporaryFile binary; QInstaller::openForWrite(&binary); QInstaller::blockingWrite(&binary, QByteArray(scTinySize, '1')); Layout layout; layout.endOfExectuable = binary.pos(); layout.magicMarker = BinaryContent::MagicInstallerMarker; layout.magicCookie = BinaryContent::MagicCookie; qint64 start = binary.pos(); // write default resource (fake) QInstaller::blockingWrite(&binary, QByteArray("Default resource data.")); qint64 end = binary.pos(); layout.metaResourceSegments.append(Range<qint64>::fromStartAndEnd(start, end)); start = end; // // write additional resource (fake) QInstaller::blockingWrite(&binary, QByteArray("Additional resource data.")); end = binary.pos(); layout.metaResourceSegments.append(Range<qint64>::fromStartAndEnd(start, end)); layout.metaResourcesSegment = Range<qint64>::fromStartAndEnd(layout.metaResourceSegments .first().start(), layout.metaResourceSegments.last().end()); layout.metaSegmentsCount = layout.metaResourceSegments.count(); start = end; layout.operationsCount = m_operations.count(); QInstaller::appendInt64(&binary, layout.operationsCount); foreach (const OperationBlob &operation, m_operations) { QInstaller::appendString(&binary, operation.name); QInstaller::appendString(&binary, operation.xml); } QInstaller::appendInt64(&binary, layout.operationsCount); end = binary.pos(); layout.operationsSegment = Range<qint64>::fromStartAndEnd(start, end); QTemporaryFile data; QInstaller::openForWrite(&data); QInstaller::blockingWrite(&data, QByteArray("Collection 1, Resource 1.")); data.close(); QSharedPointer<Resource> resource(new Resource(data.fileName(), QByteArray("Resource 1"))); ResourceCollection collection(QByteArray("Collection 1")); collection.appendResource(resource); QTemporaryFile data2; QInstaller::openForWrite(&data2); QInstaller::blockingWrite(&data2, QByteArray("Collection 2, Resource 2.")); data2.close(); QSharedPointer<Resource> resource2(new Resource(data2.fileName(), QByteArray("Resource 2"))); ResourceCollection collection2(QByteArray("Collection 2")); collection2.appendResource(resource2); ResourceCollectionManager manager; manager.insertCollection(collection); manager.insertCollection(collection2); layout.collectionCount = manager.collectionCount(); layout.resourceCollectionsSegment = manager.write(&binary, -layout.endOfExectuable); QInstaller::appendInt64Range(&binary, layout.resourceCollectionsSegment.moved(-layout .endOfExectuable)); foreach (const Range<qint64> &segment, layout.metaResourceSegments) QInstaller::appendInt64Range(&binary, segment.moved(-layout.endOfExectuable)); QInstaller::appendInt64Range(&binary, layout.operationsSegment.moved(-layout .endOfExectuable)); QInstaller::appendInt64(&binary, layout.metaSegmentsCount); layout.binaryContentSize = (binary.pos() + (3 * sizeof(qint64))) - layout.endOfExectuable; QInstaller::appendInt64(&binary, layout.binaryContentSize); QInstaller::appendInt64(&binary, layout.magicMarker); QInstaller::appendInt64(&binary, layout.magicCookie); layout.endOfBinaryContent = binary.pos(); binary.close(); binary.setAutoRemove(false); m_layout = layout; m_binary = binary.fileName(); } void readBinaryContent() { QFile binary(m_binary); QInstaller::openForRead(&binary); QCOMPARE(QInstaller::retrieveData(&binary, scTinySize), QByteArray(scTinySize, '1')); Layout layout; layout.endOfExectuable = binary.pos(); QCOMPARE(layout.endOfExectuable, m_layout.endOfExectuable); const qint64 pos = BinaryContent::findMagicCookie(&binary, BinaryContent::MagicCookie); layout.endOfBinaryContent = pos + sizeof(qint64); QCOMPARE(layout.endOfBinaryContent, m_layout.endOfBinaryContent); binary.seek(layout.endOfBinaryContent - (4 * sizeof(qint64))); layout.metaSegmentsCount = QInstaller::retrieveInt64(&binary); QCOMPARE(layout.metaSegmentsCount, m_layout.metaSegmentsCount); const qint64 offsetCollectionIndexSegments = layout.endOfBinaryContent - ((layout.metaSegmentsCount * (2 * sizeof(qint64))) // minus size of the meta segments + (8 * sizeof(qint64))); // meta count, offset/length collection index, marker, cookie... binary.seek(offsetCollectionIndexSegments); layout.resourceCollectionsSegment = QInstaller::retrieveInt64Range(&binary) .moved(layout.endOfExectuable); QCOMPARE(layout.resourceCollectionsSegment, m_layout.resourceCollectionsSegment); for (int i = 0; i < layout.metaSegmentsCount; ++i) { layout.metaResourceSegments.append(QInstaller::retrieveInt64Range(&binary) .moved(layout.endOfExectuable)); } layout.metaResourcesSegment = Range<qint64>::fromStartAndEnd(layout.metaResourceSegments .first().start(), layout.metaResourceSegments.last().end()); QCOMPARE(layout.metaResourcesSegment, m_layout.metaResourcesSegment); QCOMPARE(layout.metaResourceSegments.first(), m_layout.metaResourceSegments.first()); QCOMPARE(layout.metaResourceSegments.last(), m_layout.metaResourceSegments.last()); layout.operationsSegment = QInstaller::retrieveInt64Range(&binary).moved(layout .endOfExectuable); QCOMPARE(layout.operationsSegment, m_layout.operationsSegment); QCOMPARE(layout.metaSegmentsCount, QInstaller::retrieveInt64(&binary)); layout.binaryContentSize = QInstaller::retrieveInt64(&binary); QCOMPARE(layout.binaryContentSize, m_layout.binaryContentSize); QCOMPARE(layout.endOfExectuable, layout.endOfBinaryContent - layout.binaryContentSize); layout.magicMarker = QInstaller::retrieveInt64(&binary); QCOMPARE(layout.magicMarker, m_layout.magicMarker); layout.magicCookie = QInstaller::retrieveInt64(&binary); QCOMPARE(layout.magicCookie, m_layout.magicCookie); binary.seek(layout.operationsSegment.start()); layout.operationsCount = QInstaller::retrieveInt64(&binary); QCOMPARE(layout.operationsCount, m_layout.operationsCount); for (int i = 0; i < layout.operationsCount; ++i) { QCOMPARE(m_operations.at(i).name, QInstaller::retrieveString(&binary)); QCOMPARE(m_operations.at(i).xml, QInstaller::retrieveString(&binary)); } layout.operationsCount = QInstaller::retrieveInt64(&binary); QCOMPARE(layout.operationsCount, m_layout.operationsCount); layout.collectionCount = QInstaller::retrieveInt64(&binary); QCOMPARE(layout.collectionCount, m_layout.collectionCount); binary.seek(layout.resourceCollectionsSegment.start()); m_manager.read(&binary, layout.endOfExectuable); const QList<ResourceCollection> collections = m_manager.collections(); QCOMPARE(collections.count(), m_layout.collectionCount); ResourceCollection collection = m_manager.collectionByName(QByteArray("Collection 1")); QCOMPARE(collection.resources().count(), 1); QSharedPointer<Resource> resource(collection.resourceByName(QByteArray("Resource 1"))); QCOMPARE(resource.isNull(), false); QCOMPARE(resource->isOpen(), false); QCOMPARE(resource->open(), true); QCOMPARE(resource->readAll(), QByteArray("Collection 1, Resource 1.")); resource->close(); collection = m_manager.collectionByName(QByteArray("Collection 2")); QCOMPARE(collection.resources().count(), 1); resource = collection.resourceByName(QByteArray("Resource 2")); QCOMPARE(resource.isNull(), false); QCOMPARE(resource->isOpen(), false); QCOMPARE(resource->open(), true); QCOMPARE(resource->readAll(), QByteArray("Collection 2, Resource 2.")); resource->close(); } void testWriteBinaryContentFunction() { ResourceCollection collection(QByteArray("QResources")); foreach (const Range<qint64> &segment, m_layout.metaResourceSegments) collection.appendResource(QSharedPointer<Resource>(new Resource(m_binary, segment))); m_manager.insertCollection(collection); QList<OperationBlob> operations; foreach (const OperationBlob &operation, m_operations) operations.append(operation); QTemporaryFile file; QInstaller::openForWrite(&file); QInstaller::blockingWrite(&file, QByteArray(scTinySize, '1')); BinaryContent::writeBinaryContent(&file, operations, m_manager, m_layout.magicMarker, m_layout.magicCookie); file.close(); QFile existingBinary(m_binary); QInstaller::openForRead(&existingBinary); QInstaller::openForRead(&file); QCOMPARE(file.readAll(), existingBinary.readAll()); } void testReadBinaryContentFunction() { QFile file(m_binary); QInstaller::openForRead(&file); qint64 magicMarker; QList<OperationBlob> operations; ResourceCollectionManager manager; BinaryContent::readBinaryContent(&file, &operations, &manager, &magicMarker, m_layout.magicCookie); file.close(); QCOMPARE(magicMarker, m_layout.magicMarker); ResourceCollection collection = manager.collectionByName("QResources"); QCOMPARE(collection.resources().count(), m_layout.metaResourceSegments.count()); for (int i = 0; i < collection.resources().count(); ++i) QCOMPARE(collection.resources().at(i)->segment(), m_layout.metaResourceSegments.at(i)); QCOMPARE(operations.count(), m_operations.count()); for (int i = 0; i < operations.count(); ++i) { QCOMPARE(operations.at(i).name, m_operations.at(i).name); QCOMPARE(operations.at(i).xml, m_operations.at(i).xml); } QCOMPARE(manager.collectionCount(), m_manager.collectionCount()); collection = manager.collectionByName(QByteArray("Collection 1")); QCOMPARE(collection.resources().count(), 1); QSharedPointer<Resource> resource(collection.resourceByName(QByteArray("Resource 1"))); QCOMPARE(resource.isNull(), false); QCOMPARE(resource->isOpen(), false); QCOMPARE(resource->open(), true); QCOMPARE(resource->readAll(), QByteArray("Collection 1, Resource 1.")); resource->close(); collection = manager.collectionByName(QByteArray("Collection 2")); QCOMPARE(collection.resources().count(), 1); resource = collection.resourceByName(QByteArray("Resource 2")); QCOMPARE(resource.isNull(), false); QCOMPARE(resource->isOpen(), false); QCOMPARE(resource->open(), true); QCOMPARE(resource->readAll(), QByteArray("Collection 2, Resource 2.")); resource->close(); } void cleanupTestCase() { m_manager.clear(); QFile::remove(m_binary); } private: Layout m_layout; QString m_binary; QList<OperationBlob> m_operations; ResourceCollectionManager m_manager; }; QTEST_MAIN(tst_BinaryFormat) #include "tst_binaryformat.moc" �������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/clientserver/������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0017706�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/clientserver/clientserver.pro��������������������������������������������������0000664�0000000�0000000�00000000132�13253666515�0023131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT += network QT -= gui SOURCES += tst_clientserver.cpp ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/clientserver/tst_clientserver.cpp����������������������������������������������0000664�0000000�0000000�00000044420�13253666515�0024015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <protocol.h> #include <qprocesswrapper.h> #include <qsettingswrapper.h> #include <remoteclient.h> #include <remotefileengine.h> #include <remoteserver.h> #include <QBuffer> #include <QSettings> #include <QLocalSocket> #include <QTest> #include <QSignalSpy> #include <QTemporaryFile> #include <QUuid> #include <QLocalServer> using namespace QInstaller; class tst_ClientServer : public QObject { Q_OBJECT private: template<typename T> void sendCommand(QIODevice *device, const QByteArray &cmd, T t) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << t; sendPacket(device, cmd, data); } template<typename T> void receiveCommand(QIODevice *device, QByteArray *cmd, T *t) { QByteArray data; while (!receivePacket(device, cmd, &data)) device->waitForReadyRead(-1); QDataStream stream(&data, QIODevice::ReadOnly); stream >> *t; QCOMPARE(stream.status(), QDataStream::Ok); QVERIFY(stream.atEnd()); } private slots: void initTestCase() { RemoteClient::instance().setActive(true); } void sendReceivePacket() { QByteArray validPackage; typedef qint32 PackageSize; // first try sendPacket ... { QBuffer device(&validPackage); device.open(QBuffer::WriteOnly); const QByteArray cmd = "say"; const QByteArray data = "hello" ; QInstaller::sendPacket(&device, cmd, data); // 1 is delimiter (\0) QCOMPARE(device.buffer().size(), (int)sizeof(PackageSize) + cmd.size() + 1 + data.size()); QCOMPARE(device.buffer().right(data.size()), data); QCOMPARE(device.buffer().mid(sizeof(PackageSize), cmd.size()), cmd); } // now try successful receivePacket ... { QBuffer device(&validPackage); device.open(QBuffer::ReadOnly); QByteArray cmd; QByteArray data; QCOMPARE(QInstaller::receivePacket(&device, &cmd, &data), true); QCOMPARE(device.pos(), device.size()); QCOMPARE(cmd, QByteArray("say")); QCOMPARE(data, QByteArray("hello")); } // now try read of incomplete packet ... { QByteArray incompletePackage = validPackage; char toStrip = validPackage.at(validPackage.size() - 1); incompletePackage.resize(incompletePackage.size() - 1); QBuffer device(&incompletePackage); device.open(QBuffer::ReadOnly); QByteArray cmd; QByteArray data; QCOMPARE(QInstaller::receivePacket(&device, &cmd, &data), false); QCOMPARE(device.pos(), 0); QCOMPARE(cmd, QByteArray()); QCOMPARE(data, QByteArray()); // make packet complete again, retry device.buffer().append(toStrip); QCOMPARE(device.buffer(), validPackage); QCOMPARE(QInstaller::receivePacket(&device, &cmd, &data), true); QCOMPARE(device.pos(), device.size()); QCOMPARE(cmd, QByteArray("say")); QCOMPARE(data, QByteArray("hello")); } } void localSocket() { // // test roundtrip of a (big) packet via QLocalSocket // const QString socketName(__FUNCTION__); QLocalServer::removeServer(socketName); QEventLoop loop; const QByteArray command = "HELLO"; const QByteArray message(10905, '0'); QLocalServer server; { // server QLocalSocket *rcv = 0; auto srvDataArrived = [&]() { QByteArray command, message; if (!receivePacket(rcv, &command, &message)) return; sendPacket(rcv, command, message); }; connect(&server, &QLocalServer::newConnection, [&,srvDataArrived]() { rcv = server.nextPendingConnection(); connect(rcv, &QLocalSocket::readyRead, srvDataArrived); }); server.listen(socketName); } QLocalSocket snd; { // client auto clientDataArrived = [&]() { QByteArray cmd, msg; if (!receivePacket(&snd, &cmd, &msg)) return; QCOMPARE(cmd, command); QCOMPARE(msg, message); loop.exit(); }; connect(&snd, &QLocalSocket::readyRead, clientDataArrived); QTimer::singleShot(0, [&]() { snd.connect(&snd, &QLocalSocket::connected, [&](){ sendPacket(&snd, command, message); }); snd.connectToServer(socketName); }); } loop.exec(); } void testServerConnectDebug() { RemoteServer server; QString socketName = QUuid::createUuid().toString(); server.init(socketName, QString(Protocol::DefaultAuthorizationKey), Protocol::Mode::Debug); server.start(); QLocalSocket socket; socket.connectToServer(socketName); QVERIFY2(socket.waitForConnected(), "Cannot connect to server."); QCOMPARE(socket.state() == QLocalSocket::ConnectedState, true); sendCommand(&socket, Protocol::Authorize, QString(Protocol::DefaultAuthorizationKey)); { QByteArray command; bool authorized; receiveCommand(&socket, &command, &authorized); QCOMPARE(command, QByteArray(Protocol::Reply)); QCOMPARE(authorized, true); } sendCommand(&socket, Protocol::Authorize, QString::fromLatin1("Some Key")); { QByteArray command; bool authorized; receiveCommand(&socket, &command, &authorized); QCOMPARE(command, QByteArray(Protocol::Reply)); QCOMPARE(authorized, false); } } void testServerConnectRelease() { RemoteServer server; QString socketName = QUuid::createUuid().toString(); server.init(socketName, QString("SomeKey"), Protocol::Mode::Production); server.start(); QLocalSocket socket; socket.connectToServer(socketName); QVERIFY2(socket.waitForConnected(), "Cannot connect to server."); QCOMPARE(socket.state() == QLocalSocket::ConnectedState, true); sendCommand(&socket, Protocol::Authorize, QString::fromLatin1("SomeKey")); { QByteArray command; bool authorized; receiveCommand(&socket, &command, &authorized); QCOMPARE(command, QByteArray(Protocol::Reply)); QCOMPARE(authorized, true); } sendCommand(&socket, Protocol::Authorize, QString::fromLatin1(Protocol::DefaultAuthorizationKey)); { QByteArray command; bool authorized; receiveCommand(&socket, &command, &authorized); QCOMPARE(command, QByteArray(Protocol::Reply)); QCOMPARE(authorized, false); } } void testQSettingsWrapper() { RemoteServer server; QString socketName = QUuid::createUuid().toString(); server.init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Production); server.start(); RemoteClient::instance().init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Debug, Protocol::StartAs::User); QSettingsWrapper wrapper("digia", "clientserver"); QCOMPARE(wrapper.isConnectedToServer(), false); wrapper.clear(); QCOMPARE(wrapper.isConnectedToServer(), true); wrapper.sync(); wrapper.setFallbacksEnabled(false); QSettings settings("digia", "clientserver"); settings.setFallbacksEnabled(false); QCOMPARE(settings.fileName(), wrapper.fileName()); QCOMPARE(int(settings.format()), int(wrapper.format())); QCOMPARE(int(settings.scope()), int(wrapper.scope())); QCOMPARE(settings.organizationName(), wrapper.organizationName()); QCOMPARE(settings.applicationName(), wrapper.applicationName()); QCOMPARE(settings.fallbacksEnabled(), wrapper.fallbacksEnabled()); wrapper.setValue("key", "value"); wrapper.setValue("contains", "value"); wrapper.sync(); QCOMPARE(wrapper.value("key").toString(), QLatin1String("value")); QCOMPARE(settings.value("key").toString(), QLatin1String("value")); QCOMPARE(wrapper.contains("contains"), true); QCOMPARE(settings.contains("contains"), true); wrapper.remove("contains"); wrapper.sync(); QCOMPARE(wrapper.contains("contains"), false); QCOMPARE(settings.contains("contains"), false); wrapper.clear(); wrapper.sync(); QCOMPARE(wrapper.contains("key"), false); QCOMPARE(settings.contains("key"), false); wrapper.beginGroup("group"); wrapper.setValue("key", "value"); wrapper.endGroup(); wrapper.sync(); wrapper.beginGroup("group"); settings.beginGroup("group"); QCOMPARE(wrapper.value("key").toString(), QLatin1String("value")); QCOMPARE(settings.value("key").toString(), QLatin1String("value")); QCOMPARE(wrapper.group(), QLatin1String("group")); QCOMPARE(settings.group(), QLatin1String("group")); settings.endGroup(); wrapper.endGroup(); wrapper.beginWriteArray("array"); wrapper.setArrayIndex(0); wrapper.setValue("key", "value"); wrapper.endArray(); wrapper.sync(); wrapper.beginReadArray("array"); settings.beginReadArray("array"); wrapper.setArrayIndex(0); settings.setArrayIndex(0); QCOMPARE(wrapper.value("key").toString(), QLatin1String("value")); QCOMPARE(settings.value("key").toString(), QLatin1String("value")); settings.endArray(); wrapper.endArray(); wrapper.setValue("fridge/color", 3); wrapper.setValue("fridge/size", QSize(32, 96)); wrapper.setValue("sofa", true); wrapper.setValue("tv", false); wrapper.remove("group"); wrapper.remove("array"); wrapper.sync(); QStringList keys = wrapper.allKeys(); QCOMPARE(keys.count(), 4); QCOMPARE(keys.contains("fridge/color"), true); QCOMPARE(keys.contains("fridge/size"), true); QCOMPARE(keys.contains("sofa"), true); QCOMPARE(keys.contains("tv"), true); wrapper.beginGroup("fridge"); keys = wrapper.allKeys(); QCOMPARE(keys.count(), 2); QCOMPARE(keys.contains("color"), true); QCOMPARE(keys.contains("size"), true); wrapper.endGroup(); keys = wrapper.childKeys(); QCOMPARE(keys.count(), 2); QCOMPARE(keys.contains("sofa"), true); QCOMPARE(keys.contains("tv"), true); wrapper.beginGroup("fridge"); keys = wrapper.childKeys(); QCOMPARE(keys.count(), 2); QCOMPARE(keys.contains("color"), true); QCOMPARE(keys.contains("size"), true); wrapper.endGroup(); QStringList groups = wrapper.childGroups(); QCOMPARE(groups.count(), 1); QCOMPARE(groups.contains("fridge"), true); wrapper.beginGroup("fridge"); groups = wrapper.childGroups(); QCOMPARE(groups.count(), 0); wrapper.endGroup(); } void testQProcessWrapper() { RemoteServer server; QString socketName = QUuid::createUuid().toString(); server.init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Production); server.start(); RemoteClient::instance().init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Debug, Protocol::StartAs::User); { QProcess process; QProcessWrapper wrapper; QCOMPARE(wrapper.isConnectedToServer(), false); QCOMPARE(int(wrapper.state()), int(QProcessWrapper::NotRunning)); QCOMPARE(wrapper.isConnectedToServer(), true); QCOMPARE(process.workingDirectory(), wrapper.workingDirectory()); process.setWorkingDirectory(QDir::tempPath()); wrapper.setWorkingDirectory(QDir::tempPath()); QCOMPARE(process.workingDirectory(), wrapper.workingDirectory()); QCOMPARE(process.environment(), wrapper.environment()); process.setEnvironment(QProcess::systemEnvironment()); wrapper.setEnvironment(QProcess::systemEnvironment()); QCOMPARE(process.environment(), wrapper.environment()); QCOMPARE(int(process.readChannel()), int(wrapper.readChannel())); process.setReadChannel(QProcess::StandardError); wrapper.setReadChannel(QProcessWrapper::StandardError); QCOMPARE(int(process.readChannel()), int(wrapper.readChannel())); QCOMPARE(int(process.processChannelMode()), int(wrapper.processChannelMode())); process.setProcessChannelMode(QProcess::ForwardedChannels); wrapper.setProcessChannelMode(QProcessWrapper::ForwardedChannels); QCOMPARE(int(process.processChannelMode()), int(wrapper.processChannelMode())); } { QProcessWrapper wrapper; QCOMPARE(wrapper.isConnectedToServer(), false); QCOMPARE(int(wrapper.exitCode()), 0); QCOMPARE(wrapper.isConnectedToServer(), true); QCOMPARE(int(wrapper.state()), int(QProcessWrapper::NotRunning)); QCOMPARE(int(wrapper.exitStatus()), int(QProcessWrapper::NormalExit)); QString fileName; { QTemporaryFile file(QDir::tempPath() + #ifdef Q_OS_WIN QLatin1String("/XXXXXX.bat") #else QLatin1String("/XXXXXX.sh") #endif ); file.setAutoRemove(false); QCOMPARE(file.open(), true); #ifdef Q_OS_WIN file.write("@echo off\necho Mega test output!"); #else file.write("#!/bin/bash\necho Mega test output!"); #endif file.setPermissions(file.permissions() | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser); fileName = file.fileName(); } QSignalSpy spy(&wrapper, SIGNAL(started())); QSignalSpy spy2(&wrapper, SIGNAL(finished(int, QProcess::ExitStatus))); #ifdef Q_OS_WIN wrapper.start(fileName); #else wrapper.start("sh", QStringList() << fileName); #endif QCOMPARE(wrapper.waitForStarted(), true); QCOMPARE(int(wrapper.state()), int(QProcessWrapper::Running)); QCOMPARE(wrapper.waitForFinished(), true); QCOMPARE(int(wrapper.state()), int(QProcessWrapper::NotRunning)); QCOMPARE(wrapper.readAll().trimmed(), QByteArray("Mega test output!")); QTest::qWait(500); QCOMPARE(spy.count(), 1); QCOMPARE(spy2.count(), 1); QList<QVariant> arguments = spy2.takeFirst(); QCOMPARE(arguments.first().toInt(), 0); QCOMPARE(arguments.last().toInt(), int(QProcessWrapper::NormalExit)); QFile::remove(fileName); } } void testRemoteFileEngine() { RemoteServer server; QString socketName = QUuid::createUuid().toString(); server.init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Production); server.start(); RemoteClient::instance().init(socketName, QLatin1String("SomeKey"), Protocol::Mode::Debug, Protocol::StartAs::User); QString filename; { QTemporaryFile file; file.setAutoRemove(false); QCOMPARE(file.open(), true); file.write(QProcess::systemEnvironment().join(QLatin1String("\n")).toLocal8Bit()); filename = file.fileName(); } RemoteFileEngineHandler handler; QFile file; file.setFileName(filename); file.open(QIODevice::ReadWrite); const QByteArray ba = file.readLine(); file.seek(0); QCOMPARE(file.atEnd(), false); QByteArray ba2(32 * 1024 * 1024, '\0'); file.readLine(ba2.data(), ba2.size()); file.resize(0); file.write(QProcess::systemEnvironment().join(QLatin1String("\n")).toLocal8Bit()); QCOMPARE(file.atEnd(), true); } void cleanupTestCase() { RemoteClient::instance().setActive(false); RemoteClient::instance().shutdown(); } }; QTEST_MAIN(tst_ClientServer) #include "tst_clientserver.moc" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/componentmodel/����������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0020224�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/componentmodel/componentmodel.pro����������������������������������������������0000664�0000000�0000000�00000000203�13253666515�0023764�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += network xml qml SOURCES += tst_componentmodel.cpp RESOURCES += components.qrc ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/componentmodel/components.qrc��������������������������������������������������0000664�0000000�0000000�00000000144�13253666515�0023117�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<RCC> <qresource prefix="/"> <file>data/updates.xml</file> </qresource> </RCC> ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/componentmodel/data/�����������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0021135�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/componentmodel/data/updates.xml������������������������������������������������0000664�0000000�0000000�00000016456�13253666515�0023340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<Updates> <ApplicationName>Your application</ApplicationName> <ApplicationVersion>1.2.3</ApplicationVersion> <Checksum>true</Checksum> <PackageUpdate> <Name>com.vendor.product</Name> <DisplayName>The root component</DisplayName> <DisplayName xml:lang="ru_RU">КÐūŅ€Ð―ÐĩÐēÐ°Ņ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ð°</DisplayName> <DisplayName xml:lang="de_DE">Wurzel Komponente</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Default>true</Default> <Script>installscript.qs</Script> <SortingPriority>1</SortingPriority> <ForcedInstallation>true</ForcedInstallation> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.second.product</Name> <DisplayName>The second root component</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Default>false</Default> <Script>installscript.qs</Script> <SortingPriority>1</SortingPriority> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.second.product.subnode</Name> <DisplayName>A sub node component for the second root</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Default>false</Default> <Script>installscript.qs</Script> <SortingPriority>1</SortingPriority> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.second.product.subnode.sub</Name> <DisplayName>A subcomponent for the second sub node of the second root</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Default>false</Default> <Script>installscript.qs</Script> <SortingPriority>1</SortingPriority> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.second.product.sub</Name> <DisplayName>A subcomponent for the second root</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Script>installscript.qs</Script> <Default>true</Default> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.second.product.sub1</Name> <DisplayName>A subcomponent for the second root</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Default>false</Default> <Script>installscript.qs</Script> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.second.product.virtual</Name> <DisplayName>A virtual subcomponent for the second root</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Script>installscript.qs</Script> <Virtual>true</Virtual> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.third.product.virtual</Name> <DisplayName>A virtual root component</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Default>true</Default> <Script>installscript.qs</Script> <SortingPriority>0</SortingPriority> <Virtual>true</Virtual> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.fourth.product.checkable</Name> <DisplayName>A checkable root component</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Checkable>true</Checkable> <Default>false</Default> <Script>installscript.qs</Script> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.fifth.product.noncheckable</Name> <DisplayName>A non-checkable root component</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Script>installscript.qs</Script> <Checkable>false</Checkable> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> <PackageUpdate> <Name>com.vendor.fifth.product.noncheckable.sub</Name> <DisplayName>A sub component for non-checkable root component</DisplayName> <Description>Install this example.</Description> <Version>0.1.0-1</Version> <ReleaseDate>2010-09-21</ReleaseDate> <Script>installscript.qs</Script> <SortingPriority>0</SortingPriority> <UpdateFile UncompressedSize="61" CompressedSize="61"/> <Licenses> <License name="Beer Public License Agreement" file="license.txt"/> </Licenses> </PackageUpdate> </Updates> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/componentmodel/tst_componentmodel.cpp������������������������������������������0000664�0000000�0000000�00000054713�13253666515�0024657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "component.h" #include "componentmodel.h" #include "updatesinfo_p.h" #include "packagemanagercore.h" #include <QTest> #include <QtCore/QLocale> using namespace KDUpdater; using namespace QInstaller; #define EXPECTED_COUNT_VIRTUALS_VISIBLE 11 #define EXPECTED_COUNT_VIRTUALS_INVISIBLE 10 static const char vendorProduct[] = "com.vendor.product"; static const char vendorSecondProduct[] = "com.vendor.second.product"; static const char vendorSecondProductSub[] = "com.vendor.second.product.sub"; static const char vendorSecondProductSub1[] = "com.vendor.second.product.sub1"; static const char vendorSecondProductVirtual[] = "com.vendor.second.product.virtual"; static const char vendorSecondProductSubnode[] = "com.vendor.second.product.subnode"; static const char vendorSecondProductSubnodeSub[] = "com.vendor.second.product.subnode.sub"; static const char vendorThirdProductVirtual[] = "com.vendor.third.product.virtual"; static const char vendorFourthProductCheckable[] = "com.vendor.fourth.product.checkable"; static const char vendorFifthProductNonCheckable[] = "com.vendor.fifth.product.noncheckable"; static const char vendorFifthProductSub[] = "com.vendor.fifth.product.noncheckable.sub"; static const QMap<QString, QString> rootComponentDisplayNames = { {"", QLatin1String("The root component")}, {"ru_ru", QString::fromUtf8("КÐūŅ€Ð―ÐĩÐēÐ°Ņ КÐūОÐŋÐūÐ―ÐĩÐ―Ņ‚Ð°")}, {"de_de", QString::fromUtf8("Wurzel Komponente")} }; class tst_ComponentModel : public QObject { Q_OBJECT public: enum Option { NoFlags = 0x00, VirtualsVisible = 0x01, NoForcedInstallation = 0x02 }; Q_DECLARE_FLAGS(Options, Option); private slots: void initTestCase() { m_defaultChecked << vendorProduct << vendorSecondProductSub; m_defaultPartially << vendorSecondProduct; m_defaultUnchecked << vendorSecondProductSub1 << vendorSecondProductSubnode << vendorSecondProductSubnodeSub << vendorFourthProductCheckable << vendorFifthProductSub; m_uncheckable << vendorFifthProductNonCheckable; } void testNameToIndexAndIndexToName() { setPackageManagerOptions(NoFlags); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); // all names should be resolvable, virtual components are not indexed if they are not visible QStringList all; all << m_defaultChecked << m_defaultPartially << m_defaultUnchecked << m_uncheckable; foreach (const QString &name, all) { QVERIFY(model.indexFromComponentName(name).isValid()); QVERIFY(model.componentFromIndex(model.indexFromComponentName(name)) != 0); QCOMPARE(model.componentFromIndex(model.indexFromComponentName(name))->name(), name); } foreach (Component *const component, rootComponents) delete component; } void testNameToIndexAndIndexToNameVirtualsVisible() { setPackageManagerOptions(VirtualsVisible); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); // all names should be resolvable, including virtual components QStringList all; all << m_defaultChecked << m_defaultPartially << m_defaultUnchecked << m_uncheckable << vendorSecondProductVirtual << vendorThirdProductVirtual; foreach (const QString &name, all) { QVERIFY(model.indexFromComponentName(name).isValid()); QVERIFY(model.componentFromIndex(model.indexFromComponentName(name)) != 0); QCOMPARE(model.componentFromIndex(model.indexFromComponentName(name))->name(), name); } foreach (Component *const component, rootComponents) delete component; } void testDefault() { setPackageManagerOptions(NoFlags); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); QCOMPARE(model.core(), &m_core); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable); foreach (Component *const component, rootComponents) delete component; } void testVirtualsVisible() { setPackageManagerOptions(VirtualsVisible); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); // the virtual and non-checkable components are not checked testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductVirtual) << vendorThirdProductVirtual); foreach (Component *const component, rootComponents) delete component; } void testNoForcedInstallation() { setPackageManagerOptions(NoForcedInstallation); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable); foreach (Component *const component, rootComponents) delete component; } void testVirtualsVisibleNoForcedInstallation() { setPackageManagerOptions(Options(VirtualsVisible | NoForcedInstallation)); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); // the virtual components are not checked testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductVirtual) << vendorThirdProductVirtual); foreach (Component *const component, rootComponents) delete component; } void testSelect() { setPackageManagerOptions(NoFlags); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); // select all possible components. // Also uncheckable is checked as that is only 'visually' uncheckedable. model.setCheckedState(ComponentModel::AllChecked); QCOMPARE(model.checkedState(), ComponentModel::AllChecked); testModelState(&model, m_defaultChecked + m_defaultPartially + m_defaultUnchecked + m_uncheckable, QStringList(), QStringList()); // deselect all possible components // as the first root is a forced install, should result in partially checked state model.setCheckedState(ComponentModel::AllUnchecked); QCOMPARE(model.checkedState(), ComponentModel::PartiallyChecked); testModelState(&model, QStringList() << vendorProduct, QStringList(), m_defaultPartially + m_defaultUnchecked + QStringList(vendorSecondProductSub) + m_uncheckable); // reset all possible components model.setCheckedState(ComponentModel::DefaultChecked); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable); foreach (Component *const component, rootComponents) delete component; } void testSelectVirtualsVisible() { setPackageManagerOptions(VirtualsVisible); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); // select all possible components. model.setCheckedState(ComponentModel::AllChecked); QCOMPARE(model.checkedState(), ComponentModel::AllChecked); testModelState(&model, m_defaultChecked + m_defaultPartially + m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductVirtual) << vendorThirdProductVirtual, QStringList(), QStringList()); // deselect all possible components // as the first root is a forced install, should result in partially checked state model.setCheckedState(ComponentModel::AllUnchecked); QCOMPARE(model.checkedState(), ComponentModel::PartiallyChecked); testModelState(&model, QStringList() << vendorProduct, QStringList(), m_defaultPartially + m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductSub) << vendorSecondProductVirtual << vendorThirdProductVirtual); // reset all possible components model.setCheckedState(ComponentModel::DefaultChecked); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductVirtual) << vendorThirdProductVirtual); foreach (Component *const component, rootComponents) delete component; } void testSelectNoForcedInstallation() { setPackageManagerOptions(NoForcedInstallation); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); // select all possible components. model.setCheckedState(ComponentModel::AllChecked); QCOMPARE(model.checkedState(), ComponentModel::AllChecked); testModelState(&model, m_defaultChecked + m_defaultPartially + m_defaultUnchecked+ m_uncheckable, QStringList(), QStringList()); // deselect all possible components model.setCheckedState(ComponentModel::AllUnchecked); QCOMPARE(model.checkedState(), ComponentModel::AllUnchecked); testModelState(&model, QStringList(), QStringList(), m_defaultPartially + m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductSub) << vendorProduct); // reset all possible components model.setCheckedState(ComponentModel::DefaultChecked); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable); foreach (Component *const component, rootComponents) delete component; } void testSelectVirtualsVisibleNoForcedInstallation() { setPackageManagerOptions(Options(VirtualsVisible | NoForcedInstallation)); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); testDefaultInheritedModelBehavior(&model, 1); // select all possible components. model.setCheckedState(ComponentModel::AllChecked); QCOMPARE(model.checkedState(), ComponentModel::AllChecked); testModelState(&model, m_defaultChecked + m_defaultPartially + m_defaultUnchecked +m_uncheckable + QStringList(vendorSecondProductVirtual) << vendorThirdProductVirtual, QStringList(), QStringList()); // deselect all possible components model.setCheckedState(ComponentModel::AllUnchecked); QCOMPARE(model.checkedState(), ComponentModel::AllUnchecked); testModelState(&model, QStringList(), QStringList(), m_defaultPartially + m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductSub) << vendorSecondProductVirtual << vendorProduct << vendorThirdProductVirtual); // reset all possible components model.setCheckedState(ComponentModel::DefaultChecked); QCOMPARE(model.checkedState(), ComponentModel::DefaultChecked); testModelState(&model, m_defaultChecked, m_defaultPartially, m_defaultUnchecked + m_uncheckable + QStringList(vendorSecondProductVirtual) << vendorThirdProductVirtual); foreach (Component *const component, rootComponents) delete component; } void testComponentsLocalization() { QStringList localesToTest = { "en_US", "ru_RU", "de_DE", "fr_FR" }; foreach (const QString &localeToTest, localesToTest) { QLocale::setDefault(localeToTest); QString expectedName = rootComponentDisplayNames.contains(localeToTest.toLower()) ? rootComponentDisplayNames[localeToTest.toLower()] : rootComponentDisplayNames[QString()]; setPackageManagerOptions(NoFlags); QList<Component*> rootComponents = loadComponents(); testComponentsLoaded(rootComponents); // setup the model with 1 column ComponentModel model(1, &m_core); model.setRootComponents(rootComponents); const QModelIndex root = model.indexFromComponentName(vendorProduct); QCOMPARE(model.data(root, Qt::DisplayRole).toString(), expectedName); qDeleteAll(rootComponents); } } private: void setPackageManagerOptions(Options flags) const { m_core.setNoForceInstallation(flags.testFlag(NoForcedInstallation)); m_core.setVirtualComponentsVisible(flags.testFlag(VirtualsVisible)); } void testComponentsLoaded(const QList<Component *> &rootComponents) const { // we need to have five root components QCOMPARE(rootComponents.count(), 5); QList<Component*> components = rootComponents; foreach (Component *const component, rootComponents) components.append(component->childItems()); // will differ between 6 and 7 components, (2 root nodes, 1 sub node, 3 non virtual and 1 virtual) QCOMPARE(components.count(), m_core.virtualComponentsVisible() ? EXPECTED_COUNT_VIRTUALS_VISIBLE : EXPECTED_COUNT_VIRTUALS_INVISIBLE); } void testDefaultInheritedModelBehavior(ComponentModel *model, int columnCount) const { // row count with invalid model index should return: if (m_core.virtualComponentsVisible()) QCOMPARE(model->rowCount(), 5); // 5 (4 non virtual and 1 virtual root component) else QCOMPARE(model->rowCount(), 4); // 4 (the 4 non virtual root components) QCOMPARE(model->columnCount(), columnCount); const QModelIndex firstParent = model->indexFromComponentName(vendorProduct); const QModelIndex secondParent = model->indexFromComponentName(vendorSecondProduct); const QModelIndex thirdParent = model->indexFromComponentName(vendorThirdProductVirtual); // return invalid indexes, as they are the root components QCOMPARE(model->parent(firstParent), QModelIndex()); QCOMPARE(model->parent(secondParent), QModelIndex()); QCOMPARE(model->parent(thirdParent), QModelIndex()); // test valid indexes QVERIFY(firstParent.isValid()); QVERIFY(secondParent.isValid()); QVERIFY(model->indexFromComponentName(vendorSecondProductSub).isValid()); QVERIFY(model->indexFromComponentName(vendorSecondProductSub1).isValid()); QVERIFY(model->indexFromComponentName(vendorSecondProductSubnode).isValid()); QVERIFY(model->indexFromComponentName(vendorSecondProductSubnodeSub).isValid()); // they should have the same parent QCOMPARE(model->parent(model->indexFromComponentName(vendorSecondProductSub)), secondParent); QCOMPARE(model->parent(model->indexFromComponentName(vendorSecondProductSub1)), secondParent); QCOMPARE(model->parent(model->indexFromComponentName(vendorSecondProductSubnode)), secondParent); const QModelIndex forthParent = model->indexFromComponentName(vendorSecondProductSubnode); QCOMPARE(model->parent(model->indexFromComponentName(vendorSecondProductSubnodeSub)), forthParent); // row count should be 0, as they have no children QCOMPARE(model->rowCount(firstParent), 0); QCOMPARE(model->rowCount(model->indexFromComponentName(vendorSecondProductSub)), 0); QCOMPARE(model->rowCount(model->indexFromComponentName(vendorSecondProductSub1)), 0); QCOMPARE(model->rowCount(model->indexFromComponentName(vendorSecondProductSubnodeSub)), 0); if (m_core.virtualComponentsVisible()) { // test valid index QVERIFY(thirdParent.isValid()); QCOMPARE(model->rowCount(thirdParent), 0); QVERIFY(model->indexFromComponentName(vendorSecondProductVirtual).isValid()); QCOMPARE(model->rowCount(model->indexFromComponentName(vendorSecondProductVirtual)), 0); QCOMPARE(model->parent(model->indexFromComponentName(vendorSecondProductVirtual)), secondParent); // row count should be 4, (2 standard, 1 virtual, 1 sub node) QCOMPARE(model->rowCount(secondParent), 4); } else { // test invalid index QVERIFY(!thirdParent.isValid()); // row count should be 3, (2 standard, 1 sub node, omit the virtual) QCOMPARE(model->rowCount(secondParent), 3); } // row count should be 1, 1 sub node QCOMPARE(model->rowCount(model->indexFromComponentName(vendorSecondProductSubnode)), 1); } void testModelState(ComponentModel *model, const QStringList &checked, const QStringList &partially, const QStringList &unchecked) const { QCOMPARE(model->checked().count(), checked.count()); QCOMPARE(model->partially().count(), partially.count()); QCOMPARE(model->unchecked().count(), unchecked.count()); // these components should have checked state foreach (Component *const component, model->checked()) QVERIFY(checked.contains(component->name())); // these components should not have partially checked state foreach (Component *const component, model->partially()) QVERIFY(partially.contains(component->name())); // these components should not have checked state foreach (Component *const component, model->unchecked()) QVERIFY(unchecked.contains(component->name())); } QList<Component*> loadComponents() const { UpdatesInfo updatesInfo; updatesInfo.setFileName(":///data/updates.xml"); const QList<UpdateInfo> updateInfos = updatesInfo.updatesInfo(); QHash<QString, Component*> components; foreach (const UpdateInfo &info, updateInfos) { Component *component = new Component(const_cast<PackageManagerCore *>(&m_core)); // we need at least these to be able to test the model component->setValue("Name", info.data.value("Name").toString()); component->setValue("Default", info.data.value("Default").toString()); component->setValue("Virtual", info.data.value("Virtual").toString()); component->setValue("DisplayName", info.data.value("DisplayName").toString()); component->setValue("Checkable", info.data.value("Checkable").toString()); QString forced = info.data.value("ForcedInstallation", scFalse).toString().toLower(); if (m_core.noForceInstallation()) forced = scFalse; component->setValue("ForcedInstallation", forced); if (forced == scTrue) { component->setEnabled(false); component->setCheckable(false); component->setCheckState(Qt::Checked); } components.insert(component->name(), component); } QList <Component*> rootComponents; foreach (QString id, components.keys()) { QInstaller::Component *component = components.value(id); while (!id.isEmpty() && component->parentComponent() == 0) { id = id.section(QLatin1Char('.'), 0, -2); if (components.contains(id)) components[id]->appendComponent(component); } } foreach (QInstaller::Component *component, components) { if (component->parentComponent() == 0) rootComponents.append(component); if (component->isCheckable() && component->isDefault() && (!component->isTristate())) component->setCheckState(Qt::Checked); } return rootComponents; } private: PackageManagerCore m_core; QStringList m_defaultChecked; QStringList m_defaultPartially; QStringList m_defaultUnchecked; QStringList m_uncheckable; }; Q_DECLARE_OPERATORS_FOR_FLAGS(tst_ComponentModel::Options) QTEST_MAIN(tst_ComponentModel) #include "tst_componentmodel.moc" �����������������������������������������������������tests/auto/installer/consumeoutputoperationtest/����������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0022754�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/consumeoutputoperationtest/consumeoutputoperationtest.pro����������������������0000664�0000000�0000000�00000000257�13253666515�0031255�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += testlib qml SOURCES = tst_consumeoutputoperationtest.cpp DEFINES += "QMAKE_BINARY=$$fromNativeSeparators($$QMAKE_BINARY)" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/consumeoutputoperationtest/tst_consumeoutputoperationtest.cpp������������������0000664�0000000�0000000�00000010337�13253666515�0032131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <init.h> #include <packagemanagercore.h> #include <consumeoutputoperation.h> #include <qinstallerglobal.h> #include <fileutils.h> #include <errors.h> #include <QObject> #include <QTest> #include <QProcess> #include <QDir> #include <QEventLoop> #include <QDebug> #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) using namespace KDUpdater; using namespace QInstaller; class tst_consumeoutputoperationtest : public QObject { Q_OBJECT private slots: void initTestCase() { // be aware that this would enable eating the qDebug output //QInstaller::init(); m_fakeQtPath = QDir::toNativeSeparators(qApp->applicationDirPath()) + QDir::separator() + "fakeQt" + QDir::separator(); QVERIFY(QDir().mkpath(m_fakeQtPath + "bin")); } void testMissingArguments() { ConsumeOutputOperation operation(0); QVERIFY(operation.testOperation()); QVERIFY(!operation.performOperation()); // undo does nothing so this should be true QVERIFY(operation.undoOperation()); QCOMPARE(UpdateOperation::Error(operation.error()), UpdateOperation::InvalidArguments); //qDebug() << operation.errorString(); QString compareString("Invalid arguments in ConsumeOutput: 0 arguments given, at least 2 " "arguments expected in the form: <to be saved installer key name> " "<executable> [argument1] [argument2] [...]."); //qDebug() << compareString; QCOMPARE(operation.errorString(), compareString); } void testGetOutputFromQmake() { QString testOutput = getOutputFrom(QUOTE(QMAKE_BINARY), QStringList("-query")); ConsumeOutputOperation operation(&m_core); operation.setArguments(QStringList() << "testConsumeOutputKey" << QUOTE(QMAKE_BINARY) << "-query"); QVERIFY2(operation.performOperation(), qPrintable(operation.errorString())); QCOMPARE(Operation::Error(operation.error()), Operation::NoError); QCOMPARE(m_core.value("testConsumeOutputKey"), testOutput); } void cleanupTestCase() { try { removeDirectory(m_fakeQtPath); } catch (const QInstaller::Error &error) { QFAIL(qPrintable(error.message())); } } private: QString getOutputFrom(const QString &binary, const QStringList &arguments = QStringList()) { QEventLoop loop; QProcess process; QObject::connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); process.start(binary, arguments, QIODevice::ReadOnly); if (process.state() != QProcess::NotRunning) loop.exec(); return process.readAllStandardOutput(); } PackageManagerCore m_core; QString m_fakeQtPath; }; QTEST_MAIN(tst_consumeoutputoperationtest) #include "tst_consumeoutputoperationtest.moc" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/copyoperationtest/�������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0020774�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/copyoperationtest/copyoperationtest.pro����������������������������������������0000664�0000000�0000000�00000000136�13253666515�0025311�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += testlib SOURCES = tst_copyoperationtest.cpp ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp������������������������������������0000664�0000000�0000000�00000014131�13253666515�0026165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <init.h> #include <updateoperations.h> #include <utils.h> #include <QDir> #include <QObject> #include <QTest> #include <QFile> #include <QDebug> using namespace KDUpdater; using namespace QInstaller; class tst_copyoperationtest : public QObject { Q_OBJECT private slots: void initTestCase() { //QInstaller::init(); m_testDestinationPath = qApp->applicationDirPath() + QDir::toNativeSeparators("/test"); m_testDestinationFilePath = QDir(m_testDestinationPath).absoluteFilePath(QFileInfo( qApp->applicationFilePath()).fileName()); if (QDir(m_testDestinationPath).exists()) { QFAIL("Remove test folder first!"); } } void testMissingArguments() { CopyOperation op; QVERIFY(op.testOperation()); QVERIFY(!op.performOperation()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments); QCOMPARE(op.errorString(), QString("Invalid arguments in Copy: " "0 arguments given, exactly 2 arguments expected.")); } void testCopySomething_data() { QTest::addColumn<QString>("source"); QTest::addColumn<QString>("destination"); QTest::newRow("full path syntax") << qApp->applicationFilePath() << m_testDestinationFilePath; QTest::newRow("short destination syntax") << qApp->applicationFilePath() << m_testDestinationPath; QTest::newRow("short destination syntax with ending separator") << qApp->applicationFilePath() << m_testDestinationPath + QDir::separator(); } void testCopySomething() { QFETCH(QString, source); QFETCH(QString, destination); QVERIFY2(QFileInfo(source).exists(), QString("Source file \"%1\" does not exist.").arg(source).toLatin1()); CopyOperation op; op.setArguments(QStringList() << source << destination); op.backup(); QVERIFY2(op.performOperation(), op.errorString().toLatin1()); QVERIFY2(QFileInfo(m_testDestinationFilePath).exists(), QString("Copying from \"%1\" to \"%2\" was " "not working: '%3' does not exist").arg(source, destination, m_testDestinationFilePath).toLatin1()); QVERIFY2(op.undoOperation(), op.errorString().toLatin1()); QVERIFY2(!QFileInfo(m_testDestinationFilePath).exists(), QString("Undo of copying from \"%1\" to " "\"%2\" was not working.").toLatin1()); } void testCopyIfDestinationExist_data() { testCopySomething_data(); } void testCopyIfDestinationExist() { QFETCH(QString, source); QFETCH(QString, destination); QByteArray testString("This file is generated by QTest\n"); QFile testFile(m_testDestinationFilePath); testFile.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&testFile); out << testString; testFile.close(); QByteArray testFileHash = QInstaller::calculateHash(m_testDestinationFilePath, QCryptographicHash::Sha1); QVERIFY2(QFileInfo(source).exists(), QString("Source file \"%1\" does not exist.").arg(source).toLatin1()); CopyOperation op; op.setArguments(QStringList() << source << destination); op.backup(); QVERIFY2(!op.value("backupOfExistingDestination").toString().isEmpty(), "The CopyOperation didn't saved any backup."); QVERIFY2(op.performOperation(), op.errorString().toLatin1()); // checking that perform did something QByteArray currentFileHash = QInstaller::calculateHash(m_testDestinationFilePath, QCryptographicHash::Sha1); QVERIFY(testFileHash != currentFileHash); QVERIFY2(QFileInfo(m_testDestinationFilePath).exists(), QString("Copying from \"%1\" to \"%2\" was " "not working: \"%3\" does not exist").arg(source, destination, m_testDestinationFilePath).toLatin1()); // undo should replace the new one with the old backuped one QVERIFY2(op.undoOperation(), op.errorString().toLatin1()); currentFileHash = QInstaller::calculateHash(m_testDestinationFilePath, QCryptographicHash::Sha1); QVERIFY(testFileHash == currentFileHash); } void init() { QVERIFY2(!QFileInfo(m_testDestinationFilePath).exists(), QString("Destination \"%1\" should not exist " "to test the copy operation.").arg(m_testDestinationFilePath).toLatin1()); QDir().mkpath(m_testDestinationPath); } void cleanup() { QFile(m_testDestinationFilePath).remove(); QDir().rmpath(m_testDestinationPath); } private: QString m_testDestinationPath; QString m_testDestinationFilePath; }; QTEST_MAIN(tst_copyoperationtest) #include "tst_copyoperationtest.moc" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/extractarchiveoperationtest/���������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0023036�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/extractarchiveoperationtest/data.qrc�������������������������������������������0000664�0000000�0000000�00000000207�13253666515�0024455�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<RCC> <qresource prefix="/"> <file>data/valid.7z</file> <file>data/invalid.7z</file> </qresource> </RCC> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/extractarchiveoperationtest/data/����������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0023747�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/extractarchiveoperationtest/data/invalid.7z������������������������������������0000664�0000000�0000000�00000062000�13253666515�0025655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/extractarchiveoperationtest/data/valid.7z��������������������������������������0000664�0000000�0000000�00000001666�13253666515�0025341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������7zžŊ'�ŠÖD������R�������šnī>ĸïk]��oýĸĸĢ·ĸG>Hr9aQļ’(æĢ†ųîä‚Ó/Å:<Ką~ɊŠM/Ģ ŲĶãŒ#SāYÅuŠâwøķ” jĀÞtIdâé\SēØũD Ŧ_ mFéåÃvˆ·–WŽķMáioûKˆlBˈ?\�ÐNŊ&(”q=$ápž§#_ė(Ë…Ņ•˜Š~*‘ō'uũĀ˜M˜ýØŊՐÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ•(FĐü|Þûš0.VĀ…óƒĀeÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðħ KcŸï+�ėsS§ýūŪ|1Ÿ·1npž§#_ė(Ë…Ņ•˜Š~*‘ō'uũĀ˜M˜ýØŊՐÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ•(FĐü|Þûš0.VĀ…óƒĀeÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ•(FĐü|Þûš0.VĀ…óƒĀeÄ% HņҐ!Ú�š�ėsS§ýūŪ|1Ÿ·1npž§#_ė(Ë…Ņ•˜Š~*‘ō'uũĀ˜M˜ýØŊՐÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ!‘ČÚ��� ƒD� �!! ā��P� uģ;É���������� �v�a�l�i�d��� �FķNEÎ� �������������������������������������������������������������������������������tests/auto/installer/extractarchiveoperationtest/extractarchiveoperationtest.pro��������������������0000664�0000000�0000000�00000000177�13253666515�0031422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += testlib RESOURCES += data.qrc SOURCES = tst_extractarchiveoperationtest.cpp �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/extractarchiveoperationtest/tst_extractarchiveoperationtest.cpp����������������0000664�0000000�0000000�00000006121�13253666515�0032271�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "init.h" #include "extractarchiveoperation.h" #include <QDir> #include <QObject> #include <QTest> using namespace KDUpdater; using namespace QInstaller; class tst_extractarchiveoperationtest : public QObject { Q_OBJECT private slots: void initTestCase() { QInstaller::init(); } void testMissingArguments() { ExtractArchiveOperation op(0); QVERIFY(op.testOperation()); QVERIFY(!op.performOperation()); //QVERIFY(!op.undoOperation()); Can't test for failure as we run into Q_ASSERT QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments); QCOMPARE(op.errorString(), QString("Invalid arguments in Extract: " "0 arguments given, exactly 2 arguments expected.")); } void testExtractOperationValidFile() { ExtractArchiveOperation op(0); op.setArguments(QStringList() << ":///data/valid.7z" << QDir::tempPath()); QVERIFY(op.testOperation()); QVERIFY(op.performOperation()); QVERIFY(op.undoOperation()); } void testExtractOperationInvalidFile() { ExtractArchiveOperation op(0); op.setArguments(QStringList() << ":///data/invalid.7z" << QDir::tempPath()); QVERIFY(op.testOperation()); QVERIFY(!op.performOperation()); QVERIFY(op.undoOperation()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::UserDefinedError); QCOMPARE(op.errorString(), QString("Error while extracting archive \":///data/invalid.7z\": " "Cannot open archive \":///data/invalid.7z\".")); } }; QTEST_MAIN(tst_extractarchiveoperationtest) #include "tst_extractarchiveoperationtest.moc" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/factory/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0016650�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/factory/factory.pro������������������������������������������������������������0000664�0000000�0000000�00000000106�13253666515�0021036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui SOURCES += tst_factory.cpp ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/factory/tst_factory.cpp��������������������������������������������������������0000664�0000000�0000000�00000017144�13253666515�0021724�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <genericfactory.h> #include <QDebug> #include <QTest> #include <functional> class Food { public: explicit Food(int amount) : m_amount(amount) {} virtual ~Food() {} int available() const { return m_amount; } virtual QDate expireDate() const = 0; template <typename T, typename... Args> static Food *create(Args... args) { qDebug().nospace().noquote() << "Create function."; return new T(std::forward<Args>(args)...); } private: int m_amount; }; class EggStore : public GenericFactory<Food, QString, int> { public: static EggStore &instance() { static EggStore factory; return factory; } }; class Bread : public Food { public: Bread(int amount, const QDate &expireDate) : Food(amount) , m_expireDate(expireDate) { qDebug().nospace().noquote() << "Constructor."; } QDate expireDate() const { return m_expireDate; } private: QDate m_expireDate; }; class Butter : public Food { public: QDate expireDate() const { return m_expireDate; } static Food *create(int amount, const QDate expireDate) { qDebug().nospace().noquote() << "Create function."; return new Butter(amount, expireDate); } private: Butter(int amount, const QDate &expireDate) : Food(amount) , m_expireDate(expireDate) { qDebug().nospace().noquote() << "Constructor."; } private: QDate m_expireDate; }; class ColdCuts : public Food { public: ColdCuts(int amount, const QDate &expireDate) : Food(amount) , m_expireDate(expireDate) { qDebug().nospace().noquote() << "Constructor."; } QDate expireDate() const { return m_expireDate; } private: QDate m_expireDate; }; class FoodStore : public GenericFactory<Food, QString, int, QDate> { public: static FoodStore &instance() { static FoodStore factory; return factory; } }; class tst_Factory : public QObject { Q_OBJECT private slots: void testSingleArg() { class Egg : public Food { public: explicit Egg(int amount) : Food(amount) { qDebug().nospace().noquote() << "Constructor."; } QDate expireDate() const { return QDate::currentDate().addDays(1); } private: QDate m_expireDate; }; class EggStore : public GenericFactory<Food, QString, int> { public: static EggStore &instance() { static EggStore factory; return factory; } }; EggStore::instance().registerProduct<Egg>("Egg"); QCOMPARE(EggStore::instance().containsProduct("Egg"), true); // EggStore::instance().registerProduct<Bread>("Bread"); // Does not compile. QTest::ignoreMessage(QtDebugMsg, "Constructor."); auto food = EggStore::instance().create("Egg", 100); QCOMPARE(food->available(), 100); QCOMPARE(food->expireDate(), QDate::currentDate().addDays(1)); QTest::ignoreMessage(QtDebugMsg, "Create function."); QTest::ignoreMessage(QtDebugMsg, "Constructor."); EggStore::instance().registerProduct("Egg", &Egg::create<Egg, int>); food = EggStore::instance().create("Egg", 10); QCOMPARE(food->available(), 10); QCOMPARE(food->expireDate(), QDate::currentDate().addDays(1)); auto lambda = [](int amount) -> Food* { return new Egg(amount); }; EggStore::instance().registerProduct("Egg", lambda); QTest::ignoreMessage(QtDebugMsg, "Constructor."); food = EggStore::instance().create("Egg", 20); QCOMPARE(food->available(), 20); QCOMPARE(food->expireDate(), QDate::currentDate().addDays(1)); } void testMultiArg() { FoodStore::instance().registerProduct<Bread>("Bread"); FoodStore::instance().registerProduct("Butter", &Butter::create); FoodStore::instance().registerProduct<ColdCuts>("ColdCuts"); // FoodStore::instance().registerProduct<Eggs>("Eggs"); // Does not compile. QCOMPARE(FoodStore::instance().containsProduct("Bread"), true); QCOMPARE(FoodStore::instance().containsProduct("Butter"), true); QCOMPARE(FoodStore::instance().containsProduct("ColdCuts"), true); QCOMPARE(FoodStore::instance().containsProduct("Sandwich"), false); QTest::ignoreMessage(QtDebugMsg, "Constructor."); auto food = FoodStore::instance().create("Bread", 10, QDate::currentDate().addDays(7)); QCOMPARE(food->available(), 10); QCOMPARE(food->expireDate(), QDate::currentDate().addDays(7)); QTest::ignoreMessage(QtDebugMsg, "Create function."); QTest::ignoreMessage(QtDebugMsg, "Constructor."); food = FoodStore::instance().create("Butter", 2, QDate::currentDate().addDays(3)); QCOMPARE(food->available(), 2); QCOMPARE(food->expireDate(), QDate::currentDate().addDays(3)); QTest::ignoreMessage(QtDebugMsg, "Constructor."); food = FoodStore::instance().create("ColdCuts", 50, QDate::currentDate().addDays(5)); QCOMPARE(food->available(), 50); QCOMPARE(food->expireDate(), QDate::currentDate().addDays(5)); food = FoodStore::instance().create("Sandwich", 50, QDate::currentDate()); QCOMPARE(food == Q_NULLPTR, true); // overwrites the existing registration FoodStore::instance().registerProduct("ColdCuts", &ColdCuts::create<ColdCuts, int, QDate>); QTest::ignoreMessage(QtDebugMsg, "Create function."); QTest::ignoreMessage(QtDebugMsg, "Constructor."); food = FoodStore::instance().create("ColdCuts", 100, QDate::currentDate().addDays(4)); QCOMPARE(food->available(), 100); QCOMPARE(food->expireDate(), QDate::currentDate().addDays(4)); } }; QTEST_MAIN(tst_Factory) #include "tst_factory.moc" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/fakestopprocessforupdateoperation/���������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0024247�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/fakestopprocessforupdateoperation/fakestopprocessforupdateoperation.pro��������0000664�0000000�0000000�00000000163�13253666515�0034037�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += network qml SOURCES += tst_fakestopprocessforupdateoperation.cpp �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/fakestopprocessforupdateoperation/tst_fakestopprocessforupdateoperation.cpp����0000664�0000000�0000000�00000004670�13253666515�0034722�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "fakestopprocessforupdateoperation.h" #include "packagemanagercore.h" #include <QFileInfo> #include <QString> #include <QTest> using namespace KDUpdater; using namespace QInstaller; class tst_FakeStopProcessForUpdateOperation : public QObject { Q_OBJECT private slots: void testMissingArgument() { FakeStopProcessForUpdateOperation op(&m_core); QVERIFY(op.testOperation()); QVERIFY(op.performOperation()); QVERIFY(!op.undoOperation()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments); QCOMPARE(op.errorString(), QString("Invalid arguments in FakeStopProcessForUpdate: " "0 arguments given, exactly 1 arguments expected.")); } void testMissingPackageManagerCore() { FakeStopProcessForUpdateOperation op(0); op.setArguments(QStringList() << QFileInfo(QCoreApplication::applicationFilePath()).fileName()); QVERIFY(op.testOperation()); QVERIFY(op.performOperation()); QVERIFY(!op.undoOperation()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::UserDefinedError); QCOMPARE(op.errorString(), QString("Cannot get package manager core.")); } void testRunningApplication() { const QString app = QFileInfo(QCoreApplication::applicationFilePath()).fileName(); FakeStopProcessForUpdateOperation op(&m_core); op.setArguments(QStringList() << app); QVERIFY(op.testOperation()); QVERIFY(op.performOperation()); QVERIFY(!op.undoOperation()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::UserDefinedError); QCOMPARE(op.errorString(), QString::fromLatin1("This process should be stopped before " "continuing: %1").arg(app)); } void testRunningNonApplication() { FakeStopProcessForUpdateOperation op(&m_core); op.setArguments(QStringList() << "dummy.exe"); QVERIFY(op.testOperation()); QVERIFY(op.performOperation()); QVERIFY(op.undoOperation()); QCOMPARE(op.errorString(), QString()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::NoError); } private: PackageManagerCore m_core; }; QTEST_MAIN(tst_FakeStopProcessForUpdateOperation) #include "tst_fakestopprocessforupdateoperation.moc" ������������������������������������������������������������������������tests/auto/installer/installer.pro������������������������������������������������������������������0000664�0000000�0000000�00000001073�13253666515�0017721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������TEMPLATE = subdirs SUBDIRS += \ settings \ repository \ componentmodel \ fakestopprocessforupdateoperation \ messageboxhandler \ extractarchiveoperationtest \ lib7zfacade \ unicodeexecutable \ scriptengine \ consumeoutputoperationtest \ mkdiroperationtest \ copyoperationtest \ solver \ binaryformat \ packagemanagercore \ settingsoperation \ task \ clientserver \ factory win32 { SUBDIRS += registerfiletypeoperation } scriptengine.depends += unicodeexecutable ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/lib7zfacade/�������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0017354�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/lib7zfacade/data.qrc�����������������������������������������������������������0000664�0000000�0000000�00000000207�13253666515�0020773�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<RCC> <qresource prefix="/"> <file>data/valid.7z</file> <file>data/invalid.7z</file> </qresource> </RCC> �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/lib7zfacade/data/��������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0020265�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/lib7zfacade/data/invalid.7z����������������������������������������������������0000664�0000000�0000000�00000062000�13253666515�0022173�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/lib7zfacade/data/valid.7z������������������������������������������������������0000664�0000000�0000000�00000001666�13253666515�0021657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������7zžŊ'�ŠÖD������R�������šnī>ĸïk]��oýĸĸĢ·ĸG>Hr9aQļ’(æĢ†ųîä‚Ó/Å:<Ką~ɊŠM/Ģ ŲĶãŒ#SāYÅuŠâwøķ” jĀÞtIdâé\SēØũD Ŧ_ mFéåÃvˆ·–WŽķMáioûKˆlBˈ?\�ÐNŊ&(”q=$ápž§#_ė(Ë…Ņ•˜Š~*‘ō'uũĀ˜M˜ýØŊՐÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ•(FĐü|Þûš0.VĀ…óƒĀeÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðħ KcŸï+�ėsS§ýūŪ|1Ÿ·1npž§#_ė(Ë…Ņ•˜Š~*‘ō'uũĀ˜M˜ýØŊՐÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ•(FĐü|Þûš0.VĀ…óƒĀeÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ•(FĐü|Þûš0.VĀ…óƒĀeÄ% HņҐ!Ú�š�ėsS§ýūŪ|1Ÿ·1npž§#_ė(Ë…Ņ•˜Š~*‘ō'uũĀ˜M˜ýØŊՐÄ%Søõ‘61Ĩ°îoÁpMG ґЭ`šÎą'\Y†éfRXūévŽYäå[ųĮÚ­üûR+tÍ[ BųÝS=ø)d ;€Ë*lßĩ;ðÄ―._Š>KfBĸ“øqxYø Íĸ!‘ČÚ��� ƒD� �!! ā��P� uģ;É���������� �v�a�l�i�d��� �FķNEÎ� �������������������������������������������������������������������������������tests/auto/installer/lib7zfacade/lib7zfacade.pro����������������������������������������������������0000664�0000000�0000000�00000000157�13253666515�0022254�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += testlib RESOURCES += data.qrc SOURCES = tst_lib7zfacade.cpp �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/lib7zfacade/tst_lib7zfacade.cpp������������������������������������������������0000664�0000000�0000000�00000013563�13253666515�0023135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <lib7z_create.h> #include <lib7z_extract.h> #include <lib7z_facade.h> #include <lib7z_list.h> #include <QDir> #include <QObject> #include <QTemporaryFile> #include <QTest> class tst_lib7zfacade : public QObject { Q_OBJECT private slots: void initTestCase() { Lib7z::initSevenZ(); m_file.path = "valid"; m_file.permissions = 0; m_file.compressedSize = 836; m_file.uncompressedSize = 5242880; m_file.isDirectory = false; m_file.archiveIndex = QPoint(0, 0); m_file.utcTime = QDateTime(QDate::fromJulianDay(2456413), QTime(10, 50, 42), Qt::UTC); } void testIsSupportedArchive() { QCOMPARE(Lib7z::isSupportedArchive(":///data/valid.7z"), true); QCOMPARE(Lib7z::isSupportedArchive(":///data/invalid.7z"), false); try { QFile file(":///data/valid.7z"); QVERIFY(file.open(QIODevice::ReadOnly)); QCOMPARE(Lib7z::isSupportedArchive(&file), true); } catch (...) { QFAIL("Unexpected error during Lib7z::isSupportedArchive."); } try { QFile file(":///data/invalid.7z"); QVERIFY(file.open(QIODevice::ReadOnly)); QCOMPARE(Lib7z::isSupportedArchive(&file), false); } catch (...) { QFAIL("Unexpected error during Lib7z::isSupportedArchive."); } } void testListArchive() { try { QFile file(":///data/valid.7z"); QVERIFY(file.open(QIODevice::ReadOnly)); QVector<Lib7z::File> files = Lib7z::listArchive(&file); QCOMPARE(files.count(), 1); QCOMPARE(files.first(), m_file); } catch (...) { QFAIL("Unexpected error during list archive."); } try { QFile file(":///data/invalid.7z"); QVERIFY(file.open(QIODevice::ReadOnly)); QVector<Lib7z::File> files = Lib7z::listArchive(&file); } catch (const Lib7z::SevenZipException& e) { QCOMPARE(e.message(), QString("Cannot open archive \":///data/invalid.7z\".")); } catch (...) { QFAIL("Unexpected error during list archive."); } } void testCreateArchive() { try { const QString path = tempSourceFile("Source File 1."); const QString path2 = tempSourceFile("Source File 2."); QTemporaryFile target; QVERIFY(target.open()); Lib7z::createArchive(&target, QStringList() << path << path2); QCOMPARE(Lib7z::listArchive(&target).count(), 2); } catch (const Lib7z::SevenZipException& e) { QFAIL(e.message().toUtf8()); } catch (...) { QFAIL("Unexpected error during create archive."); } try { const QString path1 = tempSourceFile( "Source File 1.", QDir::tempPath() + "/temp file with spaces.XXXXXX" ); const QString path2 = tempSourceFile( "Source File 2.", QDir::tempPath() + "/temp file with spaces.XXXXXX" ); QTemporaryFile target(QDir::tempPath() + "/target file with spaces.XXXXXX"); QVERIFY(target.open()); Lib7z::createArchive(&target, QStringList() << path1 << path2); QCOMPARE(Lib7z::listArchive(&target).count(), 2); } catch (const Lib7z::SevenZipException& e) { QFAIL(e.message().toUtf8()); } catch (...) { QFAIL("Unexpected error during create archive."); } } void testExtractArchive() { QFile source(":///data/valid.7z"); QVERIFY(source.open(QIODevice::ReadOnly)); try { Lib7z::extractArchive(&source, QDir::tempPath()); QCOMPARE(QFile::exists(QDir::tempPath() + QString("/valid")), true); } catch (const Lib7z::SevenZipException& e) { QFAIL(e.message().toUtf8()); } catch (...) { QFAIL("Unexpected error during extract archive."); } } private: QString tempSourceFile(const QByteArray &data, const QString &templateName = QString()) { QTemporaryFile source; if (!templateName.isEmpty()) { source.setFileTemplate(templateName); } source.open(); source.write(data); source.setAutoRemove(false); return source.fileName(); } private: Lib7z::File m_file; }; QTEST_MAIN(tst_lib7zfacade) #include "tst_lib7zfacade.moc" ���������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/messageboxhandler/�������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0020674�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/messageboxhandler/messageboxhandler.pro����������������������������������������0000664�0000000�0000000�00000000130�13253666515�0025103�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT += qml widgets SOURCES += tst_messageboxhandler.cpp ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/messageboxhandler/tst_messageboxhandler.cpp������������������������������������0000664�0000000�0000000�00000010242�13253666515�0025764�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <messageboxhandler.h> #include <qinstallerglobal.h> #include <scriptengine.h> #include <packagemanagercore.h> #include <QTest> #include <QMetaEnum> #include <QDebug> #include <stdlib.h> /* srand, rand */ #include <time.h> /* time */ using namespace QInstaller; QT_BEGIN_NAMESPACE namespace QTest { template<> char *toString(const QMessageBox::StandardButton &button) { QString buttonAsString(QString::number(button)); return qstrdup(buttonAsString.toLatin1().data()); } } QT_END_NAMESPACE class tst_MessageBoxHandler : public QObject { Q_OBJECT public: private slots: void initTestCase() { m_maxStandardButtons = 0; const QMetaObject &messageBoxMetaObject = QMessageBox::staticMetaObject; int index = messageBoxMetaObject.indexOfEnumerator("StandardButtons"); QMetaEnum metaEnum = messageBoxMetaObject.enumerator(index); for (int i = 0; i < metaEnum.keyCount(); i++) { int enumValue = metaEnum.value(i); if (enumValue < QMessageBox::FirstButton) continue; m_standardButtonValueMap.insert(static_cast<QMessageBox::StandardButton>(enumValue), metaEnum.valueToKey(metaEnum.value(i))); m_maxStandardButtons += enumValue; if (enumValue == QMessageBox::LastButton) break; } } void testScriptButtonValues() { PackageManagerCore core; ScriptEngine *scriptEngine = new ScriptEngine(&core); QMapIterator<QMessageBox::StandardButton, QString> i(m_standardButtonValueMap); while (i.hasNext()) { i.next(); const QString scriptString = QString::fromLatin1("QMessageBox.%1").arg(i.value()); const QJSValue scriptValue = scriptEngine->evaluate(scriptString); QVERIFY2(!scriptValue.isError(), qPrintable(scriptValue.toString())); QVERIFY2(!scriptValue.isUndefined(), qPrintable( QString::fromLatin1("It seems that %1 is undefined.").arg(scriptString))); QCOMPARE(static_cast<QMessageBox::StandardButton>(scriptValue.toInt()), i.key()); } } void testDefaultAction() { const char ignoreMessage[] = "Created critical message box \"TestError\": \"A test error\", " "\"This is a test error message.\""; srand(time(0)); /* initialize random seed: */ int standardButtons = QMessageBox::NoButton; MessageBoxHandler::instance()->setDefaultAction(MessageBoxHandler::Reject); const QList<QMessageBox::Button> orderedButtons = MessageBoxHandler::orderedButtons(); do { standardButtons += QMessageBox::FirstButton; /* generate secret number between 1 and 10: */ const int iSecret = rand() % 10 + 1; // use only every 5th run to reduce the time which it takes to run this test if (iSecret > 2) continue; QTest::ignoreMessage(QtDebugMsg, ignoreMessage); int returnButton = MessageBoxHandler::instance()->critical(QLatin1String("TestError"), QLatin1String("A test error"), QLatin1String("This is a test error message."), static_cast<QMessageBox::StandardButton>(standardButtons)); QMessageBox::StandardButton wantedButton = QMessageBox::NoButton; // find the last button which is the wanted reject button in the current // standardButtons combination foreach (QMessageBox::StandardButton button, orderedButtons) { if (standardButtons & button) wantedButton = button; } QVERIFY2(wantedButton != QMessageBox::NoButton, "Cannot find a wantedButton."); QCOMPARE(static_cast<QMessageBox::StandardButton>(returnButton), wantedButton); } while (standardButtons < m_maxStandardButtons); } private: QMap<QMessageBox::StandardButton, QString> m_standardButtonValueMap; int m_maxStandardButtons; }; QTEST_MAIN(tst_MessageBoxHandler) #include "tst_messageboxhandler.moc" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/mkdiroperationtest/������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0021130�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/mkdiroperationtest/mkdiroperationtest.pro��������������������������������������0000664�0000000�0000000�00000000137�13253666515�0025602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += testlib SOURCES = tst_mkdiroperationtest.cpp ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/mkdiroperationtest/tst_mkdiroperationtest.cpp����������������������������������0000664�0000000�0000000�00000013317�13253666515�0026462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "init.h" #include "updateoperations.h" #include <QDir> #include <QObject> #include <QTest> #include <QFile> #include <QTextStream> using namespace KDUpdater; using namespace QInstaller; class tst_mkdiroperationtest : public QObject { Q_OBJECT private slots: void initTestCase() { QInstaller::init(); QString path = QDir::current().path() + QDir::toNativeSeparators("/test"); if (QDir(path).exists()) { QFAIL("Remove test folder first!"); } } void testMissingArguments() { MkdirOperation op; QVERIFY(op.testOperation()); QVERIFY(!op.performOperation()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments); QCOMPARE(op.errorString(), QString("Invalid arguments in Mkdir: " "0 arguments given, exactly 1 arguments expected.")); } void testCreateDirectory_data() { QTest::addColumn<QString>("directory"); QTest::newRow("/test") << "/test"; QTest::newRow("/test/test") << "/test/test"; QTest::newRow("/test/test/test") << "/test/test/test"; } void testCreateDirectory() { QFETCH(QString, directory); QString path = QDir::current().path() + QDir::toNativeSeparators(directory); QVERIFY2(!QDir(path).exists(), path.toLatin1()); MkdirOperation op; op.setArguments(QStringList() << path); op.backup(); QVERIFY2(op.performOperation(), op.errorString().toLatin1()); QVERIFY2(QDir(path).exists(), path.toLatin1()); QVERIFY2(op.undoOperation(), op.errorString().toLatin1()); QVERIFY2(!QDir(path).exists(), path.toLatin1()); } void testCreateDirectory_customFile_data() { QTest::addColumn<QString>("directory"); QTest::addColumn<QString>("filename"); QTest::newRow("/test") << "/test" << "/test/file.txt"; QTest::newRow("/test/test") << "/test/test" << "/test/file.txt"; QTest::newRow("/test/test/test") << "/test/test/test" << "/test/test/test/file.txt"; } void testCreateDirectory_customFile() { QFETCH(QString, directory); QFETCH(QString, filename); QString path = QDir::current().path() + QDir::toNativeSeparators(directory); QString filepath = QDir::current().path() + QDir::toNativeSeparators(filename); QVERIFY2(!QDir(path).exists(), path.toLatin1()); MkdirOperation op; op.setArguments(QStringList() << path); op.backup(); QVERIFY2(op.performOperation(), op.errorString().toLatin1()); QVERIFY2(QDir(path).exists(), path.toLatin1()); QFile file(filepath); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << "This file is generated by QTest\n"; file.close(); QVERIFY2(!op.undoOperation(), op.errorString().toLatin1()); QVERIFY2(file.exists(), filepath.toLatin1()); QVERIFY2(QDir(filepath).remove(filepath), "Cannot remove file"); QVERIFY2(!file.exists(), filepath.toLatin1()); QVERIFY2(op.undoOperation(), op.errorString().toLatin1()); QVERIFY2(!QDir(path).exists(), path.toLatin1()); } void testCreateDirectory_customFile_force_data() { testCreateDirectory_customFile_data(); } void testCreateDirectory_customFile_force() { QFETCH(QString, directory); QFETCH(QString, filename); QString path = QDir::current().path() + QDir::toNativeSeparators(directory); QString filepath = QDir::current().path() + QDir::toNativeSeparators(filename); QVERIFY2(!QDir(path).exists(), path.toLatin1()); MkdirOperation op; op.setArguments(QStringList() << path); op.setValue("forceremoval",true); op.backup(); QVERIFY2(op.performOperation(), op.errorString().toLatin1()); QVERIFY2(QDir(path).exists(), path.toLatin1()); QFile file(filepath); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << "This file is generated by QTest\n"; file.close(); QVERIFY2(op.undoOperation(), op.errorString().toLatin1()); QVERIFY2(!file.exists(), path.toLatin1()); } }; QTEST_MAIN(tst_mkdiroperationtest) #include "tst_mkdiroperationtest.moc" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/packagemanagercore/������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0021000�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/packagemanagercore/installer-config/�������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0024240�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/packagemanagercore/installer-config/config.xml���������������������������������0000664�0000000�0000000�00000000170�13253666515�0026225�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="utf-8"?> <Installer> <Name>test</Name> <Version>1.0.0</Version> </Installer> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/packagemanagercore/packagemanagercore.pro��������������������������������������0000664�0000000�0000000�00000000165�13253666515�0025323�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT += qml SOURCES += tst_packagemanagercore.cpp RESOURCES += \ settings.qrc �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/packagemanagercore/settings.qrc������������������������������������������������0000664�0000000�0000000�00000000167�13253666515�0023353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<RCC> <qresource prefix="/metadata"> <file>installer-config/config.xml</file> </qresource> </RCC> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp����������������������������������0000664�0000000�0000000�00000024030�13253666515�0026174�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <binarycontent.h> #include <component.h> #include <errors.h> #include <fileutils.h> #include <packagemanagercore.h> #include <progresscoordinator.h> #include <QDir> #include <QTemporaryFile> #include <QTest> using namespace QInstaller; class DummyComponent : public Component { public: DummyComponent(PackageManagerCore *core) : Component(core) { setCheckState(Qt::Checked); } void beginInstallation() { throw Error(tr("Force crash to test rollback!")); } ~DummyComponent() { } }; class NamedComponent : public Component { public: NamedComponent(PackageManagerCore *core, const QString &name) : Component(core) { setValue(scName, name); setValue(scVersion, QLatin1String("1.0.0")); } NamedComponent(PackageManagerCore *core, const QString &name, const QString &version) : Component(core) { setValue(scName, name); setValue(scVersion, version); } }; class tst_PackageManagerCore : public QObject { Q_OBJECT private: void setIgnoreMessage(const QString &testDirectory) { const QString message = "\t- arguments: %1"; QTest::ignoreMessage(QtDebugMsg, "Operations sanity check succeeded."); QTest::ignoreMessage(QtDebugMsg, "backup operation: Mkdir"); QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(testDirectory))); QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(testDirectory))); QTest::ignoreMessage(QtDebugMsg, qPrintable(message.arg(testDirectory))); QTest::ignoreMessage(QtDebugMsg, "perform operation: Mkdir"); QTest::ignoreMessage(QtDebugMsg, "Install size: 1 components"); QTest::ignoreMessage(QtDebugMsg, "ROLLING BACK operations= 1"); QTest::ignoreMessage(QtDebugMsg, "undo operation: Mkdir"); QTest::ignoreMessage(QtDebugMsg, "Done"); QTest::ignoreMessage(QtDebugMsg, "Done"); QTest::ignoreMessage(QtDebugMsg, "Done"); } private slots: void testRollBackInstallationKeepTarget() { const QString testDirectory = QInstaller::generateTemporaryFileName(); QVERIFY(QDir().mkpath(testDirectory)); setIgnoreMessage(testDirectory); PackageManagerCore core(QInstaller::BinaryContent::MagicInstallerMarker, QList<QInstaller::OperationBlob>()); // cancel the installer in error case core.autoRejectMessageBoxes(); core.appendRootComponent(new DummyComponent(&core)); core.setValue(QLatin1String("TargetDir"), testDirectory); core.setValue(QLatin1String("RemoveTargetDir"), QLatin1String("true")); QVERIFY(core.calculateComponentsToInstall()); { QTemporaryFile dummy(testDirectory + QLatin1String("/dummy")); dummy.open(); core.runInstaller(); QVERIFY(QDir(testDirectory).exists()); QVERIFY(QFileInfo(dummy.fileName()).exists()); } QDir().rmdir(testDirectory); ProgressCoordinator::instance()->reset(); } void testRollBackInstallationRemoveTarget() { const QString testDirectory = QInstaller::generateTemporaryFileName(); QVERIFY(QDir().mkpath(testDirectory)); setIgnoreMessage(testDirectory); PackageManagerCore core(QInstaller::BinaryContent::MagicInstallerMarker, QList<QInstaller::OperationBlob>()); // cancel the installer in error case core.autoRejectMessageBoxes(); core.appendRootComponent(new DummyComponent(&core)); core.setValue(QLatin1String("TargetDir"), testDirectory); core.setValue(QLatin1String("RemoveTargetDir"), QLatin1String("true")); QVERIFY(core.calculateComponentsToInstall()); core.runInstaller(); QVERIFY(!QDir(testDirectory).exists()); ProgressCoordinator::instance()->reset(); } void testComponentSetterGetter() { { PackageManagerCore core; core.setPackageManager(); QCOMPARE(core.components(PackageManagerCore::ComponentType::Root).count(), 0); QCOMPARE(core.components(PackageManagerCore::ComponentType::All).count(), 0); Component *root = new NamedComponent(&core, QLatin1String("root1")); root->appendComponent(new NamedComponent(&core, QLatin1String("root1.foo"), QLatin1String("1.0.1"))); root->appendComponent(new NamedComponent(&core, QLatin1String("root1.bar"))); core.appendRootComponent(root); QCOMPARE(core.components(PackageManagerCore::ComponentType::Root).count(), 1); QCOMPARE(core.components(PackageManagerCore::ComponentType::All).count(), 3); Component *foo = core.componentByName(QLatin1String("root1.foo-1.0.1")); QVERIFY(foo != 0); QCOMPARE(foo->name(), QLatin1String("root1.foo")); QCOMPARE(foo->value(scVersion), QLatin1String("1.0.1")); foo->appendComponent(new NamedComponent(&core, QLatin1String("root1.foo.child"))); Component *v = new NamedComponent(&core, QLatin1String("root1.foo.virtual.child")); v->setValue(scVirtual, QLatin1String("true")); foo->appendComponent(v); QCOMPARE(core.components(PackageManagerCore::ComponentType::Root).count(), 1); QCOMPARE(core.components(PackageManagerCore::ComponentType::All).count(), 5); core.appendRootComponent(new NamedComponent(&core, QLatin1String("root2"))); QCOMPARE(core.components(PackageManagerCore::ComponentType::Root).count(), 2); QCOMPARE(core.components(PackageManagerCore::ComponentType::All).count(), 6); } { PackageManagerCore core; core.setUpdater(); Component *root = new NamedComponent(&core, QLatin1String("root1")); try { root->appendComponent(new NamedComponent(&core, QLatin1String("root1.foo"))); QFAIL("Components cannot have children in updater mode."); } catch (const QInstaller::Error &error) { QCOMPARE(error.message(), QString("Components cannot have children in updater mode.")); } core.appendUpdaterComponent(root); core.appendUpdaterComponent(new NamedComponent(&core, QLatin1String("root2"))); Component *v = new NamedComponent(&core, QLatin1String("root3"), QLatin1String("2.0.1")); v->setValue(scVirtual, QLatin1String("true")); core.appendUpdaterComponent(v); QCOMPARE(core.components(PackageManagerCore::ComponentType::Root).count(), 3); QCOMPARE(core.components(PackageManagerCore::ComponentType::All).count(), 3); Component *root3 = core.componentByName(QLatin1String("root3->2.0.2")); QVERIFY(root3 == 0); root3 = core.componentByName(QLatin1String("root3->2.0.0")); QVERIFY(root3 != 0); QCOMPARE(root3->name(), QLatin1String("root3")); QCOMPARE(root3->value(scVersion), QLatin1String("2.0.1")); } } void testRequiredDiskSpace() { // test installer QTest::ignoreMessage(QtDebugMsg, "Operations sanity check succeeded."); PackageManagerCore core(QInstaller::BinaryContent::MagicInstallerMarker, QList<QInstaller::OperationBlob>()); DummyComponent *root = new DummyComponent(&core); root->setValue(scName, "root"); root->setValue(scUncompressedSize, QString::number(1000)); core.appendRootComponent(root); DummyComponent *child1 = new DummyComponent(&core); child1->setValue(scName, "root.child1"); child1->setValue(scUncompressedSize, QString::number(1500)); root->appendComponent(child1); DummyComponent *child2 = new DummyComponent(&core); child2->setValue(scName, "root.child2"); child2->setValue(scUncompressedSize, QString::number(250)); root->appendComponent(child2); // install root, child1 (child2 remains uninstalled) root->setUninstalled(); child1->setUninstalled(); child2->setInstalled(); core.calculateComponentsToInstall(); QCOMPARE(core.requiredDiskSpace(), 2500ULL); // additionally install child2 root->setInstalled(); child1->setInstalled(); child2->setUninstalled(); core.componentsToInstallNeedsRecalculation(); core.calculateComponentsToInstall(); QCOMPARE(core.requiredDiskSpace(), 250ULL); } }; QTEST_MAIN(tst_PackageManagerCore) #include "tst_packagemanagercore.moc" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/registerfiletypeoperation/�����������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0022510�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/registerfiletypeoperation/registerfiletypeoperation.pro������������������������0000664�0000000�0000000�00000000156�13253666515�0030543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT -= gui QT += testlib network SOURCES = tst_registerfiletypeoperation.cpp ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/registerfiletypeoperation/tst_registerfiletypeoperation.cpp��������������������0000664�0000000�0000000�00000011354�13253666515�0031421�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "init.h" #include "registerfiletypeoperation.h" #include "packagemanagercore.h" #include <QDir> #include <QObject> #include <QTest> #include <QFile> #include <QTextStream> #include <QSettings> #include "qsettingswrapper.h" using namespace KDUpdater; using namespace QInstaller; class tst_registerfiletypeoperation : public QObject { Q_OBJECT private slots: void initTestCase() { QInstaller::init(); QString randomString = ""; const QString possible = "abcdefghijklmnopqrstuvwxyz0123456789"; qsrand(QTime::currentTime().msec()); for (int i = 0; i < 5; i++) { int index = qrand() % possible.length(); QChar nextChar = possible.at(index); randomString.append(nextChar); } m_fileType = randomString; m_command = m_core.environmentVariable("SystemRoot") + "\\notepad.exe"; m_progId = "QtProject.QtInstallerFramework." + m_fileType; } void testMissingArguments() { RegisterFileTypeOperation op(&m_core); QVERIFY(op.testOperation()); QVERIFY(!op.performOperation()); QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments); QCOMPARE(op.errorString(), QString("Invalid arguments in RegisterFileType: 0 arguments given, " "2 to 5 arguments expected in the form: <extension> <command> [description [contentType [icon]]].")); } void testRegisterFileType() { RegisterFileTypeOperation op(&m_core); op.setArguments(QStringList() << m_fileType << m_command << "test filetype" << "text/plain" << 0 << "ProgId="+m_progId); const QString settingsPath = QString::fromLatin1("HKEY_CURRENT_USER\\Software\\Classes\\"); QSettings settings(settingsPath, QSettings::NativeFormat); QVERIFY(op.testOperation()); QVERIFY(op.performOperation()); QString defaultKey = "."+m_fileType+ "/Default"; QString openWithProgIdkey = "." + m_fileType + "/OpenWithProgIds/" +m_progId; QString shellKey = m_progId + "/shell/Open/Command/Default/"; QString shellAppkey = "/Applications/" + m_progId + "/shell/Open/Command/Default/"; QCOMPARE(settings.value(defaultKey).toString(), m_progId); QCOMPARE(settings.value(openWithProgIdkey).toString(), QString()); QCOMPARE(settings.value(shellKey).toString(), m_command); QCOMPARE(settings.value(shellAppkey).toString(), m_command); QVERIFY(op.undoOperation()); //Test that values have been removed after undo operation QCOMPARE(settings.value(defaultKey).toString(), QString()); QCOMPARE(settings.value(openWithProgIdkey).toString(), QString()); QCOMPARE(settings.value(shellKey).toString(), QString()); QCOMPARE(settings.value(shellAppkey).toString(), QString()); } private: QString m_fileType; QString m_command; QString m_progId; PackageManagerCore m_core; }; QTEST_MAIN(tst_registerfiletypeoperation) #include "tst_registerfiletypeoperation.moc" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/repository/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0017420�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/repository/repository.pro������������������������������������������������������0000664�0000000�0000000�00000000130�13253666515�0022353�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT += network QT -= gui SOURCES += tst_repository.cpp ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/repository/tst_repository.cpp��������������������������������������������������0000664�0000000�0000000�00000006225�13253666515�0023242�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "repository.h" #include <QDataStream> #include <QString> #include <QTest> using namespace QInstaller; class tst_Repository : public QObject { Q_OBJECT private slots: void testRepository() { Repository repo; QCOMPARE(repo.isValid(), false); QCOMPARE(repo.isDefault(), false); QCOMPARE(repo.isEnabled(), false); QCOMPARE(repo.url(), QUrl()); QCOMPARE(repo.username(), QString()); QCOMPARE(repo.password(), QString()); repo.setUrl(QUrl("http://www.digia.com")); QCOMPARE(repo.isValid(), true); QCOMPARE(repo.url(), QUrl("http://www.digia.com")); repo.setEnabled(true); QCOMPARE(repo.isEnabled(), true); repo.setUsername("tester"); QCOMPARE(repo.username(), QString("tester")); repo.setPassword("test"); QCOMPARE(repo.password(), QString("test")); } void testRepositoryFromUrl() { Repository repo(QUrl("http://www.digia.com"), true); QCOMPARE(repo.isValid(), true); QCOMPARE(repo.isDefault(), true); QCOMPARE(repo.isEnabled(), true); QCOMPARE(repo.url(), QUrl("http://www.digia.com")); QCOMPARE(repo.username(), QString()); QCOMPARE(repo.password(), QString()); repo.setUrl(QUrl()); QCOMPARE(repo.isValid(), false); repo.setEnabled(false); QCOMPARE(repo.isEnabled(), false); repo.setUsername("tester"); QCOMPARE(repo.username(), QString("tester")); repo.setPassword("test"); QCOMPARE(repo.password(), QString("test")); } void testRepositoryFromUserInput() { Repository repo = Repository::fromUserInput("ftp://tester:test@www.digia.com"); QCOMPARE(repo.isValid(), true); QCOMPARE(repo.isDefault(), false); QCOMPARE(repo.isEnabled(), true); QCOMPARE(repo.url(), QUrl("ftp://www.digia.com")); QCOMPARE(repo.username(), QString("tester")); QCOMPARE(repo.password(), QString("test")); repo.setUrl(QUrl()); QCOMPARE(repo.isValid(), false); repo.setEnabled(false); QCOMPARE(repo.isEnabled(), false); repo.setUsername(""); QCOMPARE(repo.username(), QString()); repo.setPassword(""); QCOMPARE(repo.password(), QString()); } void testRepositoryOperators() { Repository lhs, rhs; // operator== QVERIFY(lhs == rhs); // assignment operator rhs = Repository::fromUserInput("ftp://tester:test@www.digia.com"); // operator!= QVERIFY(lhs != rhs); // copy constructor Repository clhs(rhs); // operator== QVERIFY(clhs == rhs); QByteArray ba1, ba2; QDataStream s1(&ba1, QIODevice::ReadWrite), s2(&ba2, QIODevice::ReadWrite); // QDatastream operator s1 << clhs; s2 << rhs; QCOMPARE(ba1, ba2); Repository r1, r2; s1 >> r1; s2 >> r2; QCOMPARE(r1, r2); } }; QTEST_MAIN(tst_Repository) #include "tst_repository.moc" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0017673�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/data/�������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0020604�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/data/addOperation.qs����������������������������������������������0000664�0000000�0000000�00000004401�13253666515�0023561�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { } Component.prototype.createOperations = function () { console.log("Component::createOperations()"); component.createOperations(); component.addOperation("EmptyArg", "Arg", "Arg2", ""); component.addOperation("EmptyArg", "Arg", "", "Arg3"); component.addOperation("EmptyArg", "", "Arg2", "Arg3"); component.addOperation("EmptyArg", ["Arg", "Arg2", ""]); component.addElevatedOperation("EmptyArg", "eArg", "eArg2", ""); component.addElevatedOperation("EmptyArg", "eArg", "", "eArg3"); component.addElevatedOperation("EmptyArg", "", "eArg2", "eArg3"); component.addElevatedOperation("EmptyArg", ["eArg", "eArg2", ""]); } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/data/auto-install.qs����������������������������������������������0000664�0000000�0000000�00000003677�13253666515�0023602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Controller() { installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes); installer.setValue("GuiTestValue", "hello"); } Controller.prototype.IntroductionPageCallback = function() { var widget = gui.currentPageWidget(); var updaterRadioButton = widget.findChild("UpdaterRadioButton"); updaterRadioButton.checked = true; gui.clickButton(buttons.NextButton); } Controller.prototype.ComponentSelectionPageCallback = function() { var notExistingButtonId = 9999999; gui.clickButton(notExistingButtonId); } Controller.prototype.FinishedPageCallback = function() { print("FinishedPageCallback - OK") } �����������������������������������������������������������������tests/auto/installer/scriptengine/data/broken_connect.qs��������������������������������������������0000664�0000000�0000000�00000002706�13253666515�0024147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function BrokenConnect() { emiter.emitted.connect(receive) } function receive() { print("function receive()"); // this will print an error. foo.bar = "test"; } ����������������������������������������������������������tests/auto/installer/scriptengine/data/component1.qs������������������������������������������������0000664�0000000�0000000�00000004422�13253666515�0023236�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { print("Component constructor - OK"); } Component.prototype.retranslateUi = function() { // arguments.callee to get the current function name doesn't work in that case print("retranslateUi - OK"); // no default implementation for this method // component.languageChanged(); } Component.prototype.createOperationsForPath = function(path) { print("createOperationsForPath - OK"); component.createOperationsForPath(path); } Component.prototype.createOperationsForArchive = function(archive) { print("createOperationsForArchive - OK"); component.createOperationsForArchive(archive); } Component.prototype.beginInstallation = function() { print("beginInstallation - OK"); component.beginInstallation(); } Component.prototype.createOperations = function() { print("createOperations - OK"); component.createOperations(); } Component.prototype.isDefault = function() { print("isDefault - OK"); return false; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/data/component2.qs������������������������������������������������0000664�0000000�0000000�00000002644�13253666515�0023243�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { print("script function: " + arguments.callee.name); // adding some broken javascript code here broken + 789634 } ��������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/data/dynamicpage.qs�����������������������������������������������0000664�0000000�0000000�00000004426�13253666515�0023440�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function assert(condition) { if (!condition) throw new Error("Assertion failed!"); } function Controller() { var widget = gui.pageWidgetByObjectName("DynamicWidget"); assert(typeof widget === 'object'); var button = widget.Button; assert(typeof button === 'object'); } Controller.prototype.ReadAndSetValues = function() { var widget = gui.pageWidgetByObjectName("DynamicWidget"); installer.setValue("DynamicWidget.final", widget.final); installer.setValue("DynamicWidget.commit", widget.commit); installer.setValue("DynamicWidget.complete", widget.complete); } Controller.prototype.UpdateAndSetValues = function () { var widget = gui.pageWidgetByObjectName("DynamicWidget"); widget.final = false; widget.commit = false; widget.complete = true; installer.setValue("DynamicWidget.final", widget.final); installer.setValue("DynamicWidget.commit", widget.commit); installer.setValue("DynamicWidget.complete", widget.complete); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/data/enteringpage.qs����������������������������������������������0000664�0000000�0000000�00000002702�13253666515�0023622�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Controller() { } Controller.prototype.EnteringPageCallback = function() { var page = gui.pageWidgetByObjectName("EnteringPage"); page.callbackInvoked(); } ��������������������������������������������������������������tests/auto/installer/scriptengine/data/form.ui������������������������������������������������������0000664�0000000�0000000�00000001247�13253666515�0022112�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>form</class> <widget class="QWidget" name="form"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> <widget class="QCheckBox" name="checkBox"> <property name="geometry"> <rect> <x>90</x> <y>90</y> <width>78</width> <height>19</height> </rect> </property> <property name="text"> <string>CheckBox</string> </property> </widget> </widget> <resources/> <connections/> </ui> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/data/userinterface.qs���������������������������������������������0000664�0000000�0000000�00000002661�13253666515�0024015�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ function Component() { // check that the .ui file has been properly loaded console.log("checked: " + component.userInterface("form").checkBox.checked); } �������������������������������������������������������������������������������tests/auto/installer/scriptengine/scriptengine.pro��������������������������������������������������0000664�0000000�0000000�00000000246�13253666515�0023111�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include(../../qttest.pri) QT += qml widgets SOURCES += tst_scriptengine.cpp DEFINES += "BUILDDIR=\\\"$$OUT_PWD\\\"" RESOURCES += \ scriptengine.qrc ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/scriptengine/scriptengine.qrc��������������������������������������������������0000664�0000000�0000000�00000000666�13253666515�0023104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<RCC> <qresource prefix="/"> <file>data/auto-install.qs</file> <file>data/component1.qs</file> <file>data/component2.qs</file> <file>data/broken_connect.qs</file> <file>data/dynamicpage.qs</file> <file>data/enteringpage.qs</file> <file>data/form.ui</file> <file>data/userinterface.qs</file> <file>data/addOperation.qs</file> </qresource> </RCC> ��������������������������������������������������������������������������tests/auto/installer/scriptengine/tst_scriptengine.cpp����������������������������������������������0000664�0000000�0000000�00000054327�13253666515�0023776�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include <component.h> #include <errors.h> #include <updateoperation.h> #include <updateoperationfactory.h> #include <packagemanagercore.h> #include <packagemanagergui.h> #include <scriptengine.h> #include <../unicodeexecutable/stringdata.h> #include <QTest> #include <QSet> #include <QFile> #include <QString> using namespace QInstaller; class TestGui : public QInstaller::PackageManagerGui { Q_OBJECT public: explicit TestGui(QInstaller::PackageManagerCore *core) : PackageManagerGui(core, 0) { setPage(PackageManagerCore::Introduction, new IntroductionPage(core)); setPage(PackageManagerCore::ComponentSelection, new ComponentSelectionPage(core)); setPage(PackageManagerCore::InstallationFinished, new FinishedPage(core)); foreach (const int id, pageIds()) { packageManagerCore()->controlScriptEngine()->addToGlobalObject(page(id)); packageManagerCore()->componentScriptEngine()->addToGlobalObject(page(id)); } } virtual void init() {} void callProtectedDelayedExecuteControlScript(int id) { executeControlScript(id); } }; class DynamicPageGui : public PackageManagerGui { Q_OBJECT public: explicit DynamicPageGui(PackageManagerCore *core) : PackageManagerGui(core) {} void init() { m_widget = new QWidget; m_widget->setObjectName("Widget"); QWidget *button = new QWidget(m_widget); button->setObjectName("Button"); packageManagerCore()->wizardPageInsertionRequested(m_widget, PackageManagerCore::Introduction); } QWidget *widget() const { return m_widget; } private: QWidget *m_widget; }; class EnteringPage : public PackageManagerPage { Q_OBJECT Q_PROPERTY(QStringList invocationOrder READ invocationOrder) public: explicit EnteringPage(PackageManagerCore *core) : PackageManagerPage(core) { setObjectName(QLatin1String("EnteringPage")); } QStringList invocationOrder() const { return m_invocationOrder; } public slots: Q_INVOKABLE void enteringInvoked() { m_invocationOrder << QLatin1String("Entering"); } Q_INVOKABLE void callbackInvoked() { m_invocationOrder << QLatin1String("Callback"); } protected: void entering() { enteringInvoked(); } private: QStringList m_invocationOrder; }; class EnteringGui : public PackageManagerGui { Q_OBJECT public: explicit EnteringGui(PackageManagerCore *core) : PackageManagerGui(core) {} EnteringPage *enteringPage() const { return m_enteringPage; } void init() { m_enteringPage = new EnteringPage(packageManagerCore()); setPage(0, m_enteringPage); } private: EnteringPage *m_enteringPage; }; class EmitSignalObject : public QObject { Q_OBJECT public: EmitSignalObject() {} ~EmitSignalObject() {} void produceSignal() { emit emitted(); } signals: void emitted(); }; class EmptyArgOperation : public KDUpdater::UpdateOperation { public: explicit EmptyArgOperation(QInstaller::PackageManagerCore *core) : KDUpdater::UpdateOperation(core) { setName("EmptyArg"); } void backup() {} bool performOperation() { return true; } bool undoOperation() { return true; } bool testOperation() { return true; } }; // -- tst_ScriptEngine class tst_ScriptEngine : public QObject { Q_OBJECT private slots: void initTestCase() { m_component = new Component(&m_core); // append the component to the package manager which deletes it at destructor // (it calls clearAllComponentLists which calls qDeleteAll(m_rootComponents);) m_core.appendRootComponent(m_component); m_component->setValue("Default", "Script"); m_component->setValue(scName, "component.test.name"); Component *component = new Component(&m_core); component->setValue(scName, "component.test.addOperation"); m_core.appendRootComponent(component); m_scriptEngine = m_core.componentScriptEngine(); } void testDefaultScriptEngineValues() { const QJSValue global = m_scriptEngine->globalObject(); QCOMPARE(global.hasProperty(QLatin1String("print")), true); QCOMPARE(global.hasProperty(QLatin1String("installer")), true); QCOMPARE(global.property(QLatin1String("installer")) .hasProperty(QLatin1String("componentByName")), true); QCOMPARE(global.property(QLatin1String("installer")) .hasProperty(QLatin1String("components")), true); QCOMPARE(global.hasProperty(QLatin1String("console")), true); QCOMPARE(global.property(QLatin1String("console")) .hasProperty(QLatin1String("log")), true); QCOMPARE(global.hasProperty(QLatin1String("QFileDialog")), true); QCOMPARE(global.property(QLatin1String("QFileDialog")) .hasProperty(QLatin1String("getExistingDirectory")), true); QCOMPARE(global.property(QLatin1String("QFileDialog")) .hasProperty(QLatin1String("getOpenFileName")), true); QCOMPARE(global.hasProperty(QLatin1String("InstallerProxy")), true); QCOMPARE(global.property(QLatin1String("InstallerProxy")) .hasProperty(QLatin1String("componentByName")), true); QCOMPARE(global.property(QLatin1String("InstallerProxy")) .hasProperty(QLatin1String("components")), true); QCOMPARE(global.hasProperty(QLatin1String("QDesktopServices")), true); QCOMPARE(global.property(QLatin1String("QDesktopServices")) .hasProperty(QLatin1String("openUrl")), true); QCOMPARE(global.property(QLatin1String("QDesktopServices")) .hasProperty(QLatin1String("displayName")), true); QCOMPARE(global.property(QLatin1String("QDesktopServices")) .hasProperty(QLatin1String("storageLocation")), true); QCOMPARE(global.hasProperty(QLatin1String("buttons")), true); QCOMPARE(global.hasProperty(QLatin1String("QInstaller")), true); QCOMPARE(global.hasProperty(QLatin1String("QMessageBox")), true); QCOMPARE(global.hasProperty(QLatin1String("gui")), true); QCOMPARE(global.hasProperty(QLatin1String("qsTr")), true); QCOMPARE(global.hasProperty(QLatin1String("systemInfo")), true); QJSValue sinfo = global.property(QLatin1String("systemInfo")); QCOMPARE(sinfo.property(QLatin1String("currentCpuArchitecture")).toString(), QSysInfo::currentCpuArchitecture()); QCOMPARE(sinfo.property(QLatin1String("kernelType")).toString(), QSysInfo::kernelType()); QCOMPARE(sinfo.property(QLatin1String("kernelVersion")).toString(), QSysInfo::kernelVersion()); QCOMPARE(sinfo.property(QLatin1String("productType")).toString(), QSysInfo::productType()); QCOMPARE(sinfo.property(QLatin1String("productVersion")).toString(), QSysInfo::productVersion()); QCOMPARE(sinfo.property(QLatin1String("prettyProductName")).toString(), QSysInfo::prettyProductName()); } void testBrokenJSMethodConnect() { #if QT_VERSION <= 0x50400 QSKIP("Behavior changed from 5.4.1 onwards"); #endif EmitSignalObject emiter; m_scriptEngine->globalObject().setProperty(QLatin1String("emiter"), m_scriptEngine->newQObject(&emiter)); QJSValue context = m_scriptEngine->loadInContext(QLatin1String("BrokenConnect"), ":///data/broken_connect.qs"); if (context.isError()) { QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg( context.toString()))); } QCOMPARE(context.isError(), false); // ignore Output from script setExpectedScriptOutput("function receive()"); QTest::ignoreMessage(QtWarningMsg, ":38: ReferenceError: foo is not defined"); emiter.produceSignal(); const QJSValue value = m_scriptEngine->evaluate(""); QCOMPARE(value.isError(), false); } void testScriptPrint() { setExpectedScriptOutput("test"); const QJSValue value = m_scriptEngine->evaluate("print(\"test\");"); if (value.isError()) { QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg( value.toString()))); } } void testExistingInstallerObject() { setExpectedScriptOutput("object"); const QJSValue value = m_scriptEngine->evaluate("print(typeof installer)"); if (value.isError()) { QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg( value.toString()))); } } void testComponentByName() { const QString script = QString::fromLatin1("var component = installer.componentByName('%1');" "\n" "print(component.name);").arg(m_component->name()); setExpectedScriptOutput("component.test.name"); const QJSValue value = m_scriptEngine->evaluate(script); if (value.isError()) { QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg( value.toString()))); } } void testComponentByWrongName() { const QString script = QString::fromLatin1( "var component = installer.componentByName('%1');" "\n" "print(brokenComponent.name);").arg("NotExistingComponentName"); const QJSValue value = m_scriptEngine->evaluate(script); QCOMPARE(value.isError(), true); } void testComponents() { const QString script = QString::fromLatin1("var components = installer.components();" "\n" "print(components[0].name);"); setExpectedScriptOutput("component.test.name"); const QJSValue value = m_scriptEngine->evaluate(script); if (value.isError()) { QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error:\n %1").arg( value.toString()))); } } void loadSimpleComponentScript() { try { // ignore retranslateUi which is called by loadComponentScript setExpectedScriptOutput("Component constructor - OK"); setExpectedScriptOutput("retranslateUi - OK"); m_component->loadComponentScript(":///data/component1.qs"); setExpectedScriptOutput("retranslateUi - OK"); m_component->languageChanged(); setExpectedScriptOutput("createOperationsForPath - OK"); m_component->createOperationsForPath(":///data/"); setExpectedScriptOutput("createOperationsForArchive - OK"); // ignore createOperationsForPath which is called by createOperationsForArchive setExpectedScriptOutput("createOperationsForPath - OK"); m_component->createOperationsForArchive("test.7z"); setExpectedScriptOutput("beginInstallation - OK"); m_component->beginInstallation(); setExpectedScriptOutput("createOperations - OK"); m_component->createOperations(); setExpectedScriptOutput("isDefault - OK"); bool returnIsDefault = m_component->isDefault(); QCOMPARE(returnIsDefault, false); } catch (const Error &error) { QFAIL(qPrintable(error.message())); } } void loadBrokenComponentScript() { Component *testComponent = new Component(&m_core); testComponent->setValue(scName, "broken.component"); // m_core becomes the owner of testComponent, it will delete it in the destructor m_core.appendRootComponent(testComponent); try { // ignore Output from script setExpectedScriptOutput("script function: Component"); testComponent->loadComponentScript(":///data/component2.qs"); } catch (const Error &error) { const QString debugMessage( QString("create Error-Exception: \"Exception while loading the component script \"%1\": " "ReferenceError: broken is not defined\"").arg(QDir::toNativeSeparators(":///data/component2.qs"))); QVERIFY2(debugMessage.contains(error.message()), "(ReferenceError: broken is not defined)"); } } void loadComponentUserInterfaces() { try { setExpectedScriptOutput("checked: false"); m_component->loadUserInterfaces(QDir(":///data"), QStringList() << QLatin1String("form.ui")); m_component->loadComponentScript(":///data/userinterface.qs"); } catch (const Error &error) { QFAIL(qPrintable(error.message())); } } void loadSimpleAutoRunScript() { try { TestGui testGui(&m_core); setExpectedScriptOutput("Loaded control script \":///data/auto-install.qs\" "); testGui.loadControlScript(":///data/auto-install.qs"); QCOMPARE(m_core.value("GuiTestValue"), QString("hello")); testGui.show(); // inside the auto-install script we are clicking the next button, with a not existing button QTest::ignoreMessage(QtWarningMsg, "Button with type: \"unknown button\" not found! "); testGui.callProtectedDelayedExecuteControlScript(PackageManagerCore::ComponentSelection); setExpectedScriptOutput("FinishedPageCallback - OK"); testGui.callProtectedDelayedExecuteControlScript(PackageManagerCore::InstallationFinished); } catch (const Error &error) { QFAIL(qPrintable(error.message())); } } void testDynamicPage() { DynamicPageGui gui(&m_core); gui.init(); setExpectedScriptOutput("Loaded control script \":///data/dynamicpage.qs\" "); gui.loadControlScript(":///data/dynamicpage.qs"); gui.callControlScriptMethod("ReadAndSetValues"); QCOMPARE(m_core.value("DynamicWidget.final"), QString("false")); QCOMPARE(gui.widget()->property("final").toString(), QString("false")); QCOMPARE(m_core.value("DynamicWidget.commit"), QString("false")); QCOMPARE(gui.widget()->property("commit").toString(), QString("false")); QCOMPARE(m_core.value("DynamicWidget.complete"), QString("true")); QCOMPARE(gui.widget()->property("complete").toString(), QString("true")); gui.widget()->setProperty("final", true); gui.widget()->setProperty("commit", true); gui.widget()->setProperty("complete", false); gui.callControlScriptMethod("ReadAndSetValues"); QCOMPARE(m_core.value("DynamicWidget.final"), QString("true")); QCOMPARE(gui.widget()->property("final").toString(), QString("true")); QCOMPARE(m_core.value("DynamicWidget.commit"), QString("true")); QCOMPARE(gui.widget()->property("commit").toString(), QString("true")); QCOMPARE(m_core.value("DynamicWidget.complete"), QString("false")); QCOMPARE(gui.widget()->property("complete").toString(), QString("false")); gui.callControlScriptMethod("UpdateAndSetValues"); QCOMPARE(m_core.value("DynamicWidget.final"), QString("false")); QCOMPARE(gui.widget()->property("final").toString(), QString("false")); QCOMPARE(m_core.value("DynamicWidget.commit"), QString("false")); QCOMPARE(gui.widget()->property("commit").toString(), QString("false")); QCOMPARE(m_core.value("DynamicWidget.complete"), QString("true")); QCOMPARE(gui.widget()->property("complete").toString(), QString("true")); } void testInstallerExecuteEncodings_data() { QTest::addColumn<QString>("argumentsToInstallerExecute"); QTest::addColumn<QString>("expectedOutput"); QTest::addColumn<int>("expectedExitCode"); QTest::newRow("default_encoding_ascii_output_exit_code_0") << QString::fromLatin1("['ascii', '0']") << QString::fromLatin1(asciiText) << 0; QTest::newRow("default_encoding_ascii_output_exit_code_52") << QString::fromLatin1("['ascii', '52']") << QString::fromLatin1(asciiText) << 52; QTest::newRow("latin1_encoding_ascii_output") << QString::fromLatin1("['ascii', '0'], '', 'latin1', 'latin1'") << QString::fromLatin1(asciiText) << 0; QTest::newRow("latin1_encoding_utf8_output") << QString::fromLatin1("['utf8', '0'], '', 'latin1', 'latin1'") << QString::fromLatin1(utf8Text) << 0; QTest::newRow("utf8_encoding_ascii_output") << QString::fromLatin1("['ascii', '0'], '', 'utf8', 'utf8'") << QString::fromUtf8(asciiText) << 0; QTest::newRow("utf8_encoding_utf8_output") << QString::fromLatin1("['utf8', '0'], '', 'utf8', 'utf8'") << QString::fromUtf8(utf8Text) << 0; } void testInstallerExecuteEncodings() { QString unicodeExecutableName = QLatin1String(BUILDDIR "/../unicodeexecutable/unicodeexecutable"); #if defined(Q_OS_WIN) unicodeExecutableName += QLatin1String(".exe"); #endif QFileInfo unicodeExecutable(unicodeExecutableName); if (!unicodeExecutable.isExecutable()) { QFAIL(qPrintable(QString::fromLatin1("ScriptEngine error: test program %1 is not executable") .arg(unicodeExecutable.absoluteFilePath()))); return; } const QString testProgramPath = unicodeExecutable.absoluteFilePath(); QFETCH(QString, argumentsToInstallerExecute); QFETCH(QString, expectedOutput); QFETCH(int, expectedExitCode); QJSValue result = m_scriptEngine->evaluate(QString::fromLatin1("installer.execute('%1', %2);") .arg(testProgramPath) .arg(argumentsToInstallerExecute)); QCOMPARE(result.isArray(), true); QCOMPARE(result.property(0).toString(), expectedOutput); QCOMPARE(result.property(1).toString(), QString::number(expectedExitCode)); } void checkEnteringCalledBeforePageCallback() { EnteringGui gui(&m_core); gui.init(); setExpectedScriptOutput("Loaded control script \":///data/enteringpage.qs\" "); gui.loadControlScript(":///data/enteringpage.qs"); gui.show(); EnteringPage *enteringPage = gui.enteringPage(); QStringList expectedOrder; expectedOrder << QLatin1String("Entering") << QLatin1String("Callback"); QCOMPARE(enteringPage->invocationOrder(), expectedOrder); } void testAddOperation_AddElevatedOperation() { #if QT_VERSION < 0x50600 QSKIP("Behavior changed from 5.6.0 onwards."); #endif using namespace KDUpdater; UpdateOperationFactory &factory = UpdateOperationFactory::instance(); factory.registerUpdateOperation<EmptyArgOperation>(QLatin1String("EmptyArg")); try { m_core.setPackageManager(); Component *component = m_core.componentByName("component.test.addOperation"); component->loadComponentScript(":///data/addOperation.qs"); setExpectedScriptOutput("Component::createOperations()"); component->createOperations(); const OperationList operations = component->operations(); QCOMPARE(operations.count(), 8); struct { const char* args[3]; const char* operator[](int i) const { return args[i]; } } expectedArgs[] = { { "Arg", "Arg2", "" }, { "Arg", "", "Arg3" }, { "", "Arg2", "Arg3" }, { "Arg", "Arg2", "" }, { "eArg", "eArg2", "" }, { "eArg", "", "eArg3" }, { "", "eArg2", "eArg3" }, { "eArg", "eArg2", "" } }; for (int i = 0; i < operations.count(); ++i) { const QStringList arguments = operations[i]->arguments(); QCOMPARE(arguments.count(), 3); for (int j = 0; j < 3; ++j) QCOMPARE(arguments[j], QString(expectedArgs[i][j])); } } catch (const QInstaller::Error &error) { QFAIL(qPrintable(error.message())); } } private: void setExpectedScriptOutput(const char *message) { // Using setExpectedScriptOutput(...); inside the test method // as a simple test that the scripts are called. QTest::ignoreMessage(QtDebugMsg, message); } PackageManagerCore m_core; Component *m_component; ScriptEngine *m_scriptEngine; }; QTEST_MAIN(tst_ScriptEngine) #include "tst_scriptengine.moc" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/settings/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0017041�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/settings/data/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�13253666515�0017752�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/settings/data/empty_config.xml�������������������������������������������������0000664�0000000�0000000�00000000103�13253666515�0023151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <Installer> </Installer> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tests/auto/installer/settings/data/full_config.xml��������������������������������������������������0000664�0000000�0000000�00000004235�13253666515�0022767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!-- File should contain all elements we allow in a config.xml --> <Installer> <Name>Your application</Name> <Version>1.2.3</Version> <Title>Your application Installer Your vendor Your vendor @HomeDir@InstallationDirectory @RootDir@InstallationDirectory icon icon logo watermark banner background Modern 800 600 Super App maintenancetool maintenancetool.ini true myapp foo bar Launch MyApp true true true true false false components.xml http://www.yourcompany.com/packages 1 user password de_de qt_de controlscript.js true tests/auto/installer/settings/data/length_units_invalid.xml000066400000000000000000000003561325366651500247110ustar00rootroot00000000000000 Your application 1.2.3 800pt 600pt tests/auto/installer/settings/data/length_units_valid_em.xml000066400000000000000000000003561325366651500250430ustar00rootroot00000000000000 Your application 1.2.3 800em 600em tests/auto/installer/settings/data/length_units_valid_ex.xml000066400000000000000000000003561325366651500250560ustar00rootroot00000000000000 Your application 1.2.3 800ex 600ex tests/auto/installer/settings/data/length_units_valid_px.xml000066400000000000000000000003561325366651500250710ustar00rootroot00000000000000 Your application 1.2.3 800px 600px tests/auto/installer/settings/data/malformed_config.xml000066400000000000000000000004641325366651500237730ustar00rootroot00000000000000 Your application 1.2.3 Your application Installer Your vendor Super App @RootDir@InstallationDirectory tests/auto/installer/settings/data/minimal_config.xml000066400000000000000000000002041325366651500234430ustar00rootroot00000000000000 Your application 1.2.3 tests/auto/installer/settings/data/minimal_config_tag_defaults.xml000066400000000000000000000004451325366651500261740ustar00rootroot00000000000000 Your application 1.2.3 tests/auto/installer/settings/data/tutorial_config.xml000066400000000000000000000005021325366651500236610ustar00rootroot00000000000000 Your application 1.2.3 Your application Installer Your vendor Super App @RootDir@InstallationDirectory tests/auto/installer/settings/data/unexpectedattribute_config.xml000066400000000000000000000003471325366651500261150ustar00rootroot00000000000000 Your application 1.2.3 Test tests/auto/installer/settings/data/unexpectedtag_config.xml000066400000000000000000000003221325366651500246560ustar00rootroot00000000000000 Your application 1.2.3 Bar tests/auto/installer/settings/data/unknown_element_config.xml000066400000000000000000000003141325366651500252270ustar00rootroot00000000000000 Your application 1.2.3 Your application Installer tests/auto/installer/settings/settings.pro000066400000000000000000000001721325366651500214230ustar00rootroot00000000000000include(../../qttest.pri) QT += network QT -= gui SOURCES += tst_settings.cpp RESOURCES += \ settings.qrc tests/auto/installer/settings/settings.qrc000066400000000000000000000013231325366651500214070ustar00rootroot00000000000000 data/empty_config.xml data/full_config.xml data/malformed_config.xml data/minimal_config.xml data/tutorial_config.xml data/unknown_element_config.xml data/minimal_config_tag_defaults.xml data/unexpectedtag_config.xml data/unexpectedattribute_config.xml data/length_units_valid_px.xml data/length_units_valid_em.xml data/length_units_valid_ex.xml data/length_units_invalid.xml tests/auto/installer/settings/tst_settings.cpp000066400000000000000000000223331325366651500223020ustar00rootroot00000000000000#include "settings.h" #include "errors.h" #include "repository.h" #include #include #include using namespace QInstaller; class tst_Settings : public QObject { Q_OBJECT private slots: void loadTutorialConfig(); void loadFullConfig(); void loadEmptyConfig(); void loadNotExistingConfig(); void loadMalformedConfig(); void loadUnknownElementConfigInStrictParseMode(); void loadUnknownElementConfigInRelaxedParseMode(); void loadMinimalConfigTagDefaults(); void loadUnexpectedAttributeConfig(); void loadUnexpectedTagConfig(); void loadConfigWithValidLengthUnits(); void loadConfigWithInvalidLengthUnits(); }; void tst_Settings::loadTutorialConfig() { Settings settings = Settings::fromFileAndPrefix(":///data/tutorial_config.xml", ":///data"); // specified values QCOMPARE(settings.applicationName(), QLatin1String("Your application")); QCOMPARE(settings.version(), QLatin1String("1.2.3")); QCOMPARE(settings.title(), QLatin1String("Your application Installer")); QCOMPARE(settings.publisher(), QLatin1String("Your vendor")); QCOMPARE(settings.startMenuDir(), QLatin1String("Super App")); QCOMPARE(settings.targetDir(), QLatin1String("@RootDir@InstallationDirectory")); // default values QCOMPARE(settings.logo(), QString()); QCOMPARE(settings.url(), QString()); QCOMPARE(settings.watermark(), QString()); QCOMPARE(settings.banner(), QString()); QCOMPARE(settings.background(), QString()); #if defined(Q_OS_WIN) QCOMPARE(settings.installerApplicationIcon(), QLatin1String(":/installer.ico")); QCOMPARE(settings.installerWindowIcon(), QLatin1String(":/installer.ico")); QCOMPARE(settings.systemIconSuffix(), QLatin1String(".ico")); #elif defined(Q_OS_OSX) QCOMPARE(settings.installerApplicationIcon(), QLatin1String(":/installer.icns")); QCOMPARE(settings.installerWindowIcon(), QLatin1String(":/installer.icns")); QCOMPARE(settings.systemIconSuffix(), QLatin1String(".icns")); #else QCOMPARE(settings.installerApplicationIcon(), QLatin1String(":/installer.png")); QCOMPARE(settings.installerWindowIcon(), QLatin1String(":/installer.png")); QCOMPARE(settings.systemIconSuffix(), QLatin1String(".png")); #endif QCOMPARE(settings.wizardStyle(), QString()); QCOMPARE(settings.wizardDefaultWidth(), 0); QCOMPARE(settings.wizardDefaultHeight(), 0); QCOMPARE(settings.titleColor(), QString()); QCOMPARE(settings.runProgram(), QString()); QCOMPARE(settings.runProgramArguments(), QStringList()); QCOMPARE(settings.runProgramDescription(), QString()); QCOMPARE(settings.adminTargetDir(), QString()); QCOMPARE(settings.removeTargetDir(), QLatin1String("true")); QCOMPARE(settings.maintenanceToolName(), QLatin1String("maintenancetool")); QCOMPARE(settings.maintenanceToolIniFile(), QLatin1String("maintenancetool.ini")); QCOMPARE(settings.configurationFileName(), QLatin1String("components.xml")); QCOMPARE(settings.dependsOnLocalInstallerBinary(), false); QCOMPARE(settings.repositorySettingsPageVisible(), true); QCOMPARE(settings.allowSpaceInPath(), true); QCOMPARE(settings.allowNonAsciiCharacters(), false); QCOMPARE(settings.disableAuthorizationFallback(), false); QCOMPARE(settings.createLocalRepository(), false); QCOMPARE(settings.installActionColumnVisible(), false); QCOMPARE(settings.hasReplacementRepos(), false); QCOMPARE(settings.repositories(), QSet()); QCOMPARE(settings.defaultRepositories(), QSet()); QCOMPARE(settings.temporaryRepositories(), QSet()); QCOMPARE(settings.userRepositories(), QSet()); QCOMPARE(settings.proxyType(), Settings::NoProxy); QCOMPARE(settings.ftpProxy(), QNetworkProxy()); QCOMPARE(settings.httpProxy(), QNetworkProxy()); QCOMPARE(settings.translations(), QStringList()); QCOMPARE(settings.controlScript(), QString()); QCOMPARE(settings.supportsModify(), true); } void tst_Settings::loadFullConfig() { Settings settings = Settings::fromFileAndPrefix(":///data/full_config.xml", ":///data"); } void tst_Settings::loadEmptyConfig() { try { Settings::fromFileAndPrefix(":/data/empty_config.xml", ":/data"); } catch (const Error &error) { QCOMPARE(error.message(), QLatin1String("Missing or empty tag in :/data/empty_config.xml.")); return; } QFAIL("No exception thrown"); } void tst_Settings::loadNotExistingConfig() { QString configFile = QLatin1String(":/data/inexisting_config.xml"); QFile file(configFile); QString errorString; if (!file.open(QIODevice::ReadOnly)) { errorString = file.errorString(); } try { Settings::fromFileAndPrefix(configFile, ":/data"); } catch (const Error &error) { QCOMPARE(error.message(), QString::fromLatin1("Cannot open settings file " "%1 for reading: %2").arg(configFile).arg(errorString)); return; } QFAIL("No exception thrown"); } void tst_Settings::loadMalformedConfig() { try { Settings::fromFileAndPrefix(":/data/malformed_config.xml", ":/data"); } catch (const Error &error) { QCOMPARE(error.message(), QLatin1String("Error in :/data/malformed_config.xml, line 9, column 0: " "Premature end of document.")); return; } QFAIL("No exception thrown"); } void tst_Settings::loadUnknownElementConfigInStrictParseMode() { try { Settings::fromFileAndPrefix(":/data/unknown_element_config.xml", ":/data"); } catch (const Error &error) { QCOMPARE(error.message(), QLatin1String("Error in :/data/unknown_element_config.xml, line 5, " "column 13: Unexpected element \"unknown\".")); return; } QFAIL("No exception thrown"); } void tst_Settings::loadUnknownElementConfigInRelaxedParseMode() { QTest::ignoreMessage(QtWarningMsg, "Ignoring following settings reader error in " ":/data/unknown_element_config.xml, line 5, column 13: Unexpected element \"unknown\"."); try { Settings settings = Settings::fromFileAndPrefix(":/data/unknown_element_config.xml", ":/data", Settings::RelaxedParseMode); QCOMPARE(settings.title(), QLatin1String("Your application Installer")); } catch (const Error &error) { QFAIL(qPrintable(QString::fromLatin1("Got an exception in TolerantParseMode: %1").arg(error.message()))); } } void tst_Settings::loadMinimalConfigTagDefaults() { Settings settings = Settings::fromFileAndPrefix(":///data/minimal_config_tag_defaults.xml", ":///data"); // These tags are not mandatory, though need to be set to default values. QCOMPARE(settings.configurationFileName(), QLatin1String("components.xml")); QCOMPARE(settings.maintenanceToolName(), QLatin1String("maintenancetool")); QCOMPARE(settings.maintenanceToolIniFile(), QLatin1String("maintenancetool.ini")); } void tst_Settings::loadUnexpectedAttributeConfig() { try { Settings::fromFileAndPrefix(":///data/unexpectedattribute_config.xml", ":///data"); } catch (const Error &error) { QCOMPARE(error.message(), QLatin1String("Error in :///data/unexpectedattribute_config.xml," " line 6, column 27: Unexpected attribute for element \"Argument\".")); return; } QFAIL("No exception thrown"); return; } void tst_Settings::loadUnexpectedTagConfig() { try { Settings::fromFileAndPrefix(":///data/unexpectedtag_config.xml", ":///data"); } catch (const Error &error) { QCOMPARE(error.message(), QLatin1String("Error in :///data/unexpectedtag_config.xml," " line 6, column 12: Unexpected element \"Foo\".")); return; } QFAIL("No exception thrown"); return; } void tst_Settings::loadConfigWithValidLengthUnits() { try { Settings settings = Settings::fromFileAndPrefix(":///data/length_units_valid_px.xml", ":///data"); QCOMPARE(settings.wizardDefaultWidth(), 800); QCOMPARE(settings.wizardDefaultHeight(), 600); // Cannot test the parsed values for these units portably since the // pixel value depends on the font metrics. Let's just check for parse // errors. (void)Settings::fromFileAndPrefix(":///data/length_units_valid_em.xml", ":///data"); (void)Settings::fromFileAndPrefix(":///data/length_units_valid_ex.xml", ":///data"); } catch (const Error &error) { QFAIL(qPrintable(QString::fromLatin1("Exception caught: %1").arg(error.message()))); } } void tst_Settings::loadConfigWithInvalidLengthUnits() { try { Settings settings = Settings::fromFileAndPrefix(":///data/length_units_invalid.xml", ":///data"); QCOMPARE(settings.wizardDefaultWidth(), 0); QCOMPARE(settings.wizardDefaultHeight(), 0); } catch (const Error &error) { QFAIL(qPrintable(QString::fromLatin1("Exception caught: %1").arg(error.message()))); } } QTEST_MAIN(tst_Settings) #include "tst_settings.moc" tests/auto/installer/settingsoperation/000077500000000000000000000000001325366651500207625ustar00rootroot00000000000000tests/auto/installer/settingsoperation/settingsoperation.pro000066400000000000000000000001371325366651500252660ustar00rootroot00000000000000include(../../qttest.pri) QT -= gui QT += testlib SOURCES += tst_settingsoperation.cpp tests/auto/installer/settingsoperation/tst_settingsoperation.cpp000066400000000000000000000334731325366651500261530ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include #include #include #include #include #include using namespace KDUpdater; using namespace QInstaller; class tst_settingsoperation : public QObject { Q_OBJECT private slots: // called before all tests void initTestCase() { m_testSettingsDirPath = qApp->applicationDirPath(); m_testSettingsFilename = "test.ini"; QSettings testSettings(QDir(m_testSettingsDirPath).filePath(m_testSettingsFilename), QSettings::IniFormat); m_cleanupFilePaths << QDir(m_testSettingsDirPath).filePath(m_testSettingsFilename); testSettings.setValue("testkey", "testvalue"); testSettings.setValue("testcategory/categorytestkey", "categorytestvalue"); testSettings.setValue("testcategory/categoryarrayvalue1", QStringList() << "value1" << "value2" << "value3"); testSettings.setValue("testcategory/categoryarrayvalue2", "value1,value2,value3"); testSettings.setValue("testcategory/categoryarrayvalue3", "value1 ,value2, value3"); } void testWrongArguments() { SettingsOperation noArgumentsOperation(0); QVERIFY(noArgumentsOperation.testOperation()); // operation should do nothing if there are no arguments QCOMPARE(noArgumentsOperation.performOperation(), false); QCOMPARE(UpdateOperation::Error(noArgumentsOperation.error()), UpdateOperation::InvalidArguments); QString compareString("Missing argument(s) \"path; method; key; value\" calling Settings " "with arguments \"\"."); QCOMPARE(noArgumentsOperation.errorString(), compareString); // same for undo QCOMPARE(noArgumentsOperation.undoOperation(), false); SettingsOperation wrongMethodArgumentOperation(0); wrongMethodArgumentOperation.setArguments(QStringList() << "path=first" << "method=second" << "key=third" << "value=fourth"); QVERIFY(wrongMethodArgumentOperation.testOperation()); // operation should do nothing if there are no arguments QCOMPARE(wrongMethodArgumentOperation.performOperation(), false); QCOMPARE(UpdateOperation::Error(wrongMethodArgumentOperation.error()), UpdateOperation::InvalidArguments); compareString = "Current method argument calling \"Settings\" with arguments \"path=first; " "method=second; key=third; value=fourth\" is not supported. Please use set, remove, " "add_array_value or remove_array_value."; QCOMPARE(wrongMethodArgumentOperation.errorString(), compareString); // same for undo QCOMPARE(wrongMethodArgumentOperation.undoOperation(), false); } void setSettingsValue() { const QString verifyFilePath = createFilePath(QTest::currentTestFunction()); const QString testFilePath = createFilePath(QString("_") + QTest::currentTestFunction()); m_cleanupFilePaths << verifyFilePath << testFilePath; const QString key = "category/key"; const QString value = "value"; { QSettings testSettings(verifyFilePath, QSettings::IniFormat); testSettings.setValue(key, value); } SettingsOperation settingsOperation(0); settingsOperation.setArguments(QStringList() << QString("path=%1").arg(testFilePath) << "method=set" << QString("key=%1").arg(key) << QString("value=%1").arg(value)); settingsOperation.backup(); QVERIFY2(settingsOperation.performOperation(), settingsOperation.errorString().toLatin1()); QVERIFY2(compareFiles(verifyFilePath, testFilePath), QString("\"%1\" and \"%2\" are different.") .arg(verifyFilePath, testFilePath).toLatin1()); } void undoSettingsValueInCreatedSubDirectories() { const QString testFilePath = createFilePath(QString("sub/directory/") + QTest::currentTestFunction()); const QString key = "key"; const QString value = "value"; SettingsOperation settingsOperation(0); settingsOperation.setArguments(QStringList() << QString("path=%1").arg(testFilePath) << "method=set" << QString("key=%1").arg(key) << QString("value=%1").arg(value)); settingsOperation.backup(); QVERIFY2(settingsOperation.performOperation(), settingsOperation.errorString().toLatin1()); QCOMPARE(QFile(testFilePath).exists(), true); QVERIFY2(settingsOperation.undoOperation(), settingsOperation.errorString().toLatin1()); QCOMPARE(QFile(testFilePath).exists(), false); QCOMPARE(QDir(QFileInfo(testFilePath).absolutePath()).exists(), false); } void removeSettingsValue() { const QString testFilePath = createFilePath(QTest::currentTestFunction()); QFile testFile(QDir(m_testSettingsDirPath).filePath(m_testSettingsFilename)); QVERIFY2(testFile.copy(testFilePath), testFile.errorString().toLatin1()); m_cleanupFilePaths << testFilePath; const QString key = "testkey"; QString testValueString; { QSettings testSettings(testFilePath, QSettings::IniFormat); testValueString = testSettings.value(key).toString(); } QCOMPARE(testValueString.isEmpty(), false); SettingsOperation settingsOperation(0); settingsOperation.setArguments(QStringList() << QString("path=%1").arg(testFilePath) << "method=remove" << QString("key=%1").arg(key)); settingsOperation.backup(); QVERIFY2(settingsOperation.performOperation(), settingsOperation.errorString().toLatin1()); QVariant testValueVariant; { QSettings testSettings(testFilePath, QSettings::IniFormat); testValueVariant = testSettings.value(key); } QVERIFY(testValueVariant.isNull()); } void addSettingsArrayValue() { const QString testFilePath = createFilePath(QTest::currentTestFunction()); QFile contentFile(QDir(m_testSettingsDirPath).filePath(m_testSettingsFilename)); QVERIFY2(contentFile.open(QIODevice::ReadOnly | QIODevice::Text), contentFile.errorString().toLatin1()); QFile testFile(testFilePath); QVERIFY2(testFile.open(QIODevice::WriteOnly | QIODevice::Text), contentFile.errorString().toLatin1()); QTextStream out(&testFile); QTextStream in(&contentFile); QString line = in.readLine(); while (!line.isNull()) { // remove the " to have some maybe invalid data out << line.replace("\"", QLatin1String("")) << QLatin1String("\n"); line = in.readLine(); } testFile.close(); QMap testSettingsOperationMap; testSettingsOperationMap["testcategory/categoryarrayvalue1"] = new SettingsOperation(0); testSettingsOperationMap["testcategory/categoryarrayvalue2"] = new SettingsOperation(0); testSettingsOperationMap["testcategory/categoryarrayvalue3"] = new SettingsOperation(0); testSettingsOperationMap["testcategory/categoryarrayvalue4"] = new SettingsOperation(0); QMap::iterator i = testSettingsOperationMap.begin(); while (i != testSettingsOperationMap.end()) { i.value()->setArguments(QStringList() << QString("path=%1").arg(testFilePath) << "method=add_array_value" << QString("key=%1").arg(i.key()) << "value=value4"); i.value()->backup(); QVERIFY2(i.value()->performOperation(), i.value()->errorString().toLatin1()); ++i; } QStringList testKeys(testSettingsOperationMap.keys()); { QSettings verifySettings(testFilePath, QSettings::IniFormat); QCOMPARE(verifySettings.value(testKeys.at(0)).isNull(), false); QCOMPARE(verifySettings.value(testKeys.at(0)), verifySettings.value(testKeys.at(1))); QCOMPARE(verifySettings.value(testKeys.at(1)), verifySettings.value(testKeys.at(2))); QCOMPARE(verifySettings.value(testKeys.at(3)).toString(), QLatin1String("value4")); } i = testSettingsOperationMap.begin(); while (i != testSettingsOperationMap.end()) { i.value()->setArguments(QStringList() << QString("path=%1").arg(testFilePath) << "method=add_array_value" << QString("key=%1").arg(i.key()) << "value=value4"); QVERIFY2(i.value()->undoOperation(), i.value()->errorString().toLatin1()); ++i; } { QStringList verifyStringList; verifyStringList << "value1" << "value2" << "value3"; QVariant verifyUndoValue = verifyStringList; QSettings verifySettings(testFilePath, QSettings::IniFormat); QCOMPARE(verifySettings.value(testKeys.at(0)), verifyUndoValue); QCOMPARE(verifySettings.value(testKeys.at(1)), verifyUndoValue); QCOMPARE(verifySettings.value(testKeys.at(2)), verifyUndoValue); // checking the none array value is removed QVERIFY(verifySettings.value(testKeys.at(3)).isNull()); } } void removeSettingsArrayValue() { const QString testFilePath = createFilePath(QTest::currentTestFunction()); QFile contentFile(QDir(m_testSettingsDirPath).filePath(m_testSettingsFilename)); QVERIFY2(contentFile.open(QIODevice::ReadOnly | QIODevice::Text), contentFile.errorString() .toLatin1()); QFile testFile(testFilePath); QVERIFY2(testFile.open(QIODevice::WriteOnly | QIODevice::Text), contentFile.errorString() .toLatin1()); QTextStream out(&testFile); QTextStream in(&contentFile); QString line = in.readLine(); while (!line.isNull()) { // remove the " to have some maybe invalid data out << line.replace("\"", QLatin1String("")) << QLatin1String("\n"); line = in.readLine(); } testFile.close(); QMap testSettingsOperationMap; testSettingsOperationMap["testcategory/categoryarrayvalue1"] = new SettingsOperation(0); testSettingsOperationMap["testcategory/categoryarrayvalue2"] = new SettingsOperation(0); testSettingsOperationMap["testcategory/categoryarrayvalue3"] = new SettingsOperation(0); QMap::iterator i = testSettingsOperationMap.begin(); while (i != testSettingsOperationMap.end()) { i.value()->setArguments(QStringList() << QString("path=%1").arg(testFilePath) << "method=remove_array_value" << QString("key=%1").arg(i.key()) << "value=value3"); i.value()->backup(); QVERIFY2(i.value()->performOperation(), i.value()->errorString().toLatin1()); ++i; } QStringList testKeys(testSettingsOperationMap.keys()); { QSettings verifySettings(testFilePath, QSettings::IniFormat); QCOMPARE(verifySettings.value(testKeys.at(0)).isNull(), false); QStringList verifyFirstValue = verifySettings.value(testKeys.at(0)).toStringList(); QCOMPARE(verifyFirstValue.contains(QLatin1String("value3")), false); QCOMPARE(verifySettings.value(testKeys.at(0)), verifySettings.value(testKeys.at(1))); QCOMPARE(verifySettings.value(testKeys.at(1)), verifySettings.value(testKeys.at(2))); } } // called after all tests void cleanupTestCase() { foreach (const QString &filePath, m_cleanupFilePaths) QFile(filePath).remove(); } private: QString createFilePath(const QString &fileNamePrependix) { return QDir(m_testSettingsDirPath).filePath(QString(fileNamePrependix) + m_testSettingsFilename); } bool compareFiles(const QString &filePath1, const QString &filePath2) { if (!QFile::exists(filePath1) || !QFile::exists(filePath2)) return false; const QByteArray fileHash1 = QInstaller::calculateHash(filePath1, QCryptographicHash::Sha1); const QByteArray fileHash2 = QInstaller::calculateHash(filePath2, QCryptographicHash::Sha1); return fileHash1 == fileHash2; } QString m_testSettingsFilename; QString m_testSettingsDirPath; QStringList m_cleanupFilePaths; }; QTEST_MAIN(tst_settingsoperation) #include "tst_settingsoperation.moc" tests/auto/installer/solver/000077500000000000000000000000001325366651500165135ustar00rootroot00000000000000tests/auto/installer/solver/solver.pro000066400000000000000000000001201325366651500205400ustar00rootroot00000000000000include(../../qttest.pri) QT -= gui QT += qml SOURCES += tst_solver.cpp tests/auto/installer/solver/tst_solver.cpp000066400000000000000000000335751325366651500214400ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include #include #include #include #include #include using namespace QInstaller; typedef QMap ComponentToStringList; class Data { public: Data() {} explicit Data(const QString &data) : m_data(data) {} inline uint qHash(const Data &test); QString data() const { return m_data; } bool operator==(const Data &rhs) const { return m_data == rhs.m_data; } const Data &operator=(const Data &rhs) { if (this != &rhs) { m_data = rhs.m_data; } return *this; } private: QString m_data; }; inline uint qHash(const Data &data) { return qHash(data.data()); } class NamedComponent : public Component { public: NamedComponent(PackageManagerCore *core, const QString &name) : Component(core) { setValue(scName, name); setValue(scVersion, QLatin1String("1.0.0")); } NamedComponent(PackageManagerCore *core, const QString &name, const QString &version) : Component(core) { setValue(scName, name); setValue(scVersion, version); } }; class tst_Solver : public QObject { Q_OBJECT private slots: // TODO: add failing cases void sortGraph() { Graph graph; graph.addNode("Hut"); graph.addEdge("Jacke", "Shirt"); graph.addEdge("Guertel", "Hose"); graph.addEdge("Guertel", "Shirt"); graph.addEdge("Shirt", "Socken"); graph.addEdge("Socken", "Unterwaesche"); graph.addEdge("Shirt", "Unterwaesche"); graph.addEdges("Hose", QStringList() << "Unterwaesche" << "Socken"); graph.addEdges("Krawatte", QStringList() << "Shirt" << "Hose" << "Guertel"); graph.addEdges("Schuhe", QStringList() << "Socken" << "Unterwaesche" << "Hose"); graph.addEdges("Jacke", QStringList() << "Hose" << "Shirt" << "Guertel" << "Schuhe" << "Krawatte"); QList resolved = graph.sort(); foreach (const QString &value, resolved) qDebug("%s", qPrintable(value)); } void sortGraphReverse() { Graph graph; graph.addEdge("Krawatte", "Jacke"); graph.addEdge("Guertel", "Jacke"); graph.addEdge("Shirt", "Guertel"); graph.addEdges("Shirt", QList() << "Krawatte" << "Schuhe"); graph.addEdges("Hose", QList() << "Schuhe" << "Guertel" << "Shirt"); graph.addEdges("Socken", QList() << "Schuhe" << "Hose"); graph.addEdges("Unterwaesche", QList() << "Socken" << "Hose" << "Guertel" << "Shirt" << "Krawatte" << "Schuhe"); QList resolved = graph.sortReverse(); foreach (const QString &value, resolved) qDebug("%s", qPrintable(value)); } void sortGraphCycle() { Data a("A"), b("B"), c("C"), d("D"), e("E"); Graph graph; graph.addEdge(a, b); graph.addEdge(b, c); graph.addEdge(c, d); graph.addEdge(d, e); graph.addEdge(e, a); QList resolved = graph.sort(); foreach (const Data &value, resolved) qDebug("%s", qPrintable(value.data())); QPair cycle = graph.cycle(); qDebug("Found cycle: %s", graph.hasCycle() ? "true" : "false"); qDebug("(%s) has a indirect dependency on (%s).", qPrintable(cycle.second.data()), qPrintable(cycle.first.data())); } void resolveInstaller_data() { QTest::addColumn("core"); QTest::addColumn >("selectedComponents"); QTest::addColumn >("expectedResult"); QTest::addColumn >("installReason"); PackageManagerCore *core = new PackageManagerCore(); core->setPackageManager(); NamedComponent *componentA = new NamedComponent(core, QLatin1String("A")); NamedComponent *componentAA = new NamedComponent(core, QLatin1String("A.A")); NamedComponent *componentAB = new NamedComponent(core, QLatin1String("A.B")); componentA->appendComponent(componentAA); componentA->appendComponent(componentAB); NamedComponent *componentB = new NamedComponent(core, QLatin1String("B")); NamedComponent *componentB_NewVersion = new NamedComponent(core, QLatin1String("B_version"), QLatin1String("2.0.0")); componentB->addDependency(QLatin1String("A.B")); componentAB->addDependency(QLatin1String("B_version->=2.0.0")); core->appendRootComponent(componentA); core->appendRootComponent(componentB); core->appendRootComponent(componentB_NewVersion); QTest::newRow("Installer resolved") << core << (QList() << componentB) << (QList() << componentB_NewVersion << componentAB << componentB) << (QList() << InstallerCalculator::Dependent << InstallerCalculator::Dependent << InstallerCalculator::Resolved); } void resolveInstaller() { QFETCH(PackageManagerCore *, core); QFETCH(QList , selectedComponents); QFETCH(QList , expectedResult); QFETCH(QList, installReason); InstallerCalculator calc(core->components(PackageManagerCore::ComponentType::AllNoReplacements)); calc.appendComponentsToInstall(selectedComponents); QList result = calc.orderedComponentsToInstall(); QCOMPARE(result.count(), expectedResult.count()); for (int i = 0; i < result.count(); i++) { QCOMPARE(result.at(i), expectedResult.at(i)); QCOMPARE((int)calc.installReasonType(result.at(i)), installReason.at(i)); } delete core; } void unresolvedDependencyVersion_data() { QTest::addColumn("core"); QTest::addColumn >("selectedComponents"); QTest::addColumn >("expectedResult"); PackageManagerCore *core = new PackageManagerCore(); core->setPackageManager(); NamedComponent *componentA = new NamedComponent(core, QLatin1String("A")); NamedComponent *componentB = new NamedComponent(core, QLatin1String("B"), QLatin1String("1.0.0")); componentA->addDependency(QLatin1String("B->=2.0.0")); core->appendRootComponent(componentA); core->appendRootComponent(componentB); QTest::newRow("Installer resolved") << core << (QList() << componentA) << (QList()); } void unresolvedDependencyVersion() { QFETCH(PackageManagerCore *, core); QFETCH(QList , selectedComponents); QFETCH(QList , expectedResult); InstallerCalculator calc(core->components(PackageManagerCore::ComponentType::AllNoReplacements)); QTest::ignoreMessage(QtWarningMsg, "Cannot find missing dependency \"B->=2.0.0\" for \"A\"."); calc.appendComponentsToInstall(selectedComponents); QList result = calc.orderedComponentsToInstall(); QCOMPARE(result.count(), expectedResult.count()); delete core; } void resolveUninstaller_data() { QTest::addColumn("core"); QTest::addColumn >("selectedToUninstall"); QTest::addColumn >("installedComponents"); QTest::addColumn >("expectedResult"); PackageManagerCore *core = new PackageManagerCore(); core->setPackageManager(); NamedComponent *componentA = new NamedComponent(core, QLatin1String("A")); NamedComponent *componentAA = new NamedComponent(core, QLatin1String("A.A")); NamedComponent *componentAB = new NamedComponent(core, QLatin1String("A.B")); componentA->appendComponent(componentAA); componentA->appendComponent(componentAB); NamedComponent *componentB = new NamedComponent(core, QLatin1String("B")); componentB->addDependency(QLatin1String("A.B")); core->appendRootComponent(componentA); core->appendRootComponent(componentB); componentA->setInstalled(); componentB->setInstalled(); componentAB->setInstalled(); QTest::newRow("Uninstaller resolved") << core << (QList() << componentAB) << (QList() << componentA << componentB) << (QSet() << componentAB << componentB); core = new PackageManagerCore(); core->setPackageManager(); NamedComponent *compA = new NamedComponent(core, QLatin1String("A")); NamedComponent *compB = new NamedComponent(core, QLatin1String("B")); NamedComponent *compC = new NamedComponent(core, QLatin1String("C")); compB->addDependency(QLatin1String("A")); compC->addDependency(QLatin1String("B")); core->appendRootComponent(compA); core->appendRootComponent(compB); core->appendRootComponent(compC); compA->setInstalled(); compB->setInstalled(); QTest::newRow("Cascade dependencies") << core << (QList() << compA) << (QList() << compB) << (QSet() << compA << compB); } void resolveUninstaller() { QFETCH(PackageManagerCore *, core); QFETCH(QList , selectedToUninstall); QFETCH(QList , installedComponents); QFETCH(QSet , expectedResult); UninstallerCalculator calc(installedComponents); calc.appendComponentsToUninstall(selectedToUninstall); QSet result = calc.componentsToUninstall(); QCOMPARE(result.count(), expectedResult.count()); QCOMPARE(result, expectedResult); delete core; } void checkComponent_data() { QTest::addColumn >("componentsToCheck"); QTest::addColumn("expectedResult"); PackageManagerCore *core = new PackageManagerCore(); core->setPackageManager(); NamedComponent *componentA = new NamedComponent(core, QLatin1String("A")); NamedComponent *componentB = new NamedComponent(core, QLatin1String("B")); componentB->setValue(QLatin1String("AutoDependOn"), QLatin1String("A")); componentB->setValue(QLatin1String("Default"), QLatin1String("true")); core->appendRootComponent(componentA); core->appendRootComponent(componentB); ComponentToStringList result; result[componentB].append(QLatin1String("Component B specifies \"Default\" property " "together with \"AutoDependOn\" list. This combination of states " "may not work properly.")); QTest::newRow("AutoDepend and default") << (QList() << componentA << componentB) << result; NamedComponent *componentC = new NamedComponent(core, QLatin1String("C")); NamedComponent *componentD = new NamedComponent(core, QLatin1String("D")); NamedComponent *componentE = new NamedComponent(core, QLatin1String("E")); componentD->setValue(QLatin1String("AutoDependOn"), QLatin1String("C")); componentE->addDependency(QLatin1String("D")); core->appendRootComponent(componentC); core->appendRootComponent(componentD); core->appendRootComponent(componentE); result.clear(); result[componentD].append(QLatin1String("Other components depend on auto dependent " "component D. This may not work properly.")); QTest::newRow("AutoDepend and dependency") << (QList() << componentC << componentD << componentE) << result; } void checkComponent() { QFETCH(QList, componentsToCheck); QFETCH(ComponentToStringList, expectedResult); foreach (Component *component, componentsToCheck) { const QStringList result = ComponentChecker::checkComponent(component); QCOMPARE(result, expectedResult.value(component)); } } }; QTEST_MAIN(tst_Solver) #include "tst_solver.moc" tests/auto/installer/task/000077500000000000000000000000001325366651500161435ustar00rootroot00000000000000tests/auto/installer/task/task.pro000066400000000000000000000001351325366651500176260ustar00rootroot00000000000000include(../../qttest.pri) QT -= gui QT += network concurrent SOURCES += tst_task.cpp tests/auto/installer/task/tst_task.cpp000066400000000000000000000101721325366651500205040ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include #include #include #include #include #include using namespace QInstaller; static const qint64 scLargeSize = 4194304LL; class tst_Task : public QObject { Q_OBJECT private slots: void copyFile() { QTemporaryFile file; file.setAutoRemove(false); if (file.open()) { const QString filename = file.fileName(); QInstaller::blockingWrite(&file, QByteArray(scLargeSize, '1')); file.close(); CopyFileTask fileTask(filename); QFutureWatcher watcher; QSignalSpy started(&watcher, SIGNAL(started())); QSignalSpy finished(&watcher, SIGNAL(finished())); QSignalSpy progress(&watcher, SIGNAL(progressValueChanged(int))); watcher.setFuture(QtConcurrent::run(&CopyFileTask::doTask, &fileTask)); watcher.waitForFinished(); QTest::qWait(10); // Spin the event loop to deliver queued signals. QCOMPARE(started.count(), 1); QCOMPARE(finished.count(), 1); FileTaskResult result = watcher.result(); QCOMPARE(watcher.future().resultCount(), 1); QVERIFY(QFile(result.target()).exists()); QCOMPARE(file.size(), QFile(result.target()).size()); QCOMPARE(result.checkSum().toHex(), QByteArray("85304f87b8d90554a63c6f6d1e9cc974fbef8d32")); } } void downloadFile() { QTemporaryFile file; file.setAutoRemove(false); if (file.open()) { const QString filename = file.fileName(); QInstaller::blockingWrite(&file, QByteArray(scLargeSize, '1')); file.close(); DownloadFileTask fileTask(QLatin1String("file:///") + filename); QFutureWatcher watcher; QSignalSpy started(&watcher, SIGNAL(started())); QSignalSpy finished(&watcher, SIGNAL(finished())); QSignalSpy progress(&watcher, SIGNAL(progressValueChanged(int))); watcher.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, &fileTask)); watcher.waitForFinished(); QTest::qWait(10); // Spin the event loop to deliver queued signals. QCOMPARE(started.count(), 1); QCOMPARE(finished.count(), 1); FileTaskResult result = watcher.result(); QCOMPARE(watcher.future().resultCount(), 1); QVERIFY(QFile(result.target()).exists()); QCOMPARE(file.size(), QFile(result.target()).size()); QCOMPARE(result.checkSum().toHex(), QByteArray("85304f87b8d90554a63c6f6d1e9cc974fbef8d32")); } } }; QTEST_MAIN(tst_Task) #include "tst_task.moc" tests/auto/installer/unicodeexecutable/000077500000000000000000000000001325366651500206715ustar00rootroot00000000000000tests/auto/installer/unicodeexecutable/main.c000066400000000000000000000036601325366651500217660ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "stringdata.h" #include #include #include #include int main(int argc, char **argv) { assert(argc == 3 || !"incorrect number of arguments"); if (!strcmp(argv[1], "utf8")) { printf("%s", utf8Text); } else { printf("%s", asciiText); } return atoi(argv[2]); } tests/auto/installer/unicodeexecutable/stringdata.h000066400000000000000000000035201325366651500232020ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef STRINGDATA_H #define STRINGDATA_H const char asciiText[] = "This is some ASCII text."; const char utf8Text[] = "\x46\x6F\x6F\x20\xC2\xA9\x20\x62\x61\x72\x20\xF0\x9D\x8C\x86\x20\x62\x61\x7A\x20\xE2\x98\x83\x20\x71\x75\x78"; #endif // !defined(STRINGDATA_H) tests/auto/installer/unicodeexecutable/unicodeexecutable.pro000066400000000000000000000001461325366651500251040ustar00rootroot00000000000000SOURCES = main.c CONFIG -= qt app_bundle CONFIG += console win32: DESTDIR = $$OUT_PWD QT = tests/auto/qttest.pri000066400000000000000000000007701325366651500152500ustar00rootroot00000000000000include(../../installerfw.pri) #include(qttestrpath.pri) isEmpty(TEMPLATE):TEMPLATE=app QT += testlib CONFIG += qt warn_on console depend_includepath testcase DEFINES -= QT_NO_CAST_FROM_ASCII # prefix test binary with tst_ !contains(TARGET, ^tst_.*):TARGET = $$join(TARGET,,"tst_") #win32 { # lib = ../../ # lib ~= s,/,\\,g # # the below gets added to later by testcase.prf # check.commands = cd . & set PATH=$$lib;%PATH%& cmd /c #} macx:include(../../no_app_bundle.pri) tests/downloadspeed/000077500000000000000000000000001325366651500150645ustar00rootroot00000000000000tests/downloadspeed/downloadspeed.pro000066400000000000000000000003201325366651500204310ustar00rootroot00000000000000TEMPLATE = app INCLUDEPATH += . .. TARGET = downloadspeed include(../../installerfw.pri) QT -= gui QT += network CONFIG += console SOURCES += main.cpp macx:include(../../no_app_bundle.pri) tests/downloadspeed/main.cpp000066400000000000000000000134041325366651500165160ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include #include #include #include #include #include #include // -- Receiver class Receiver : public QObject { Q_OBJECT public: Receiver() : QObject(), m_downloadFinished(false) {} ~Receiver() {} inline bool downloaded() { return m_downloadFinished; } public slots: void downloadStarted() { m_downloadFinished = false; } void downloadFinished() { m_downloadFinished = true; } void downloadAborted(const QString &error) { m_downloadFinished = true; qDebug() << "Error:" << error; } void downloadSpeed(qint64 speed) { qDebug() << "Download speed:" << QInstaller::humanReadableSize(speed) + QLatin1String("/sec"); } void downloadProgress(double progress) { qDebug() << "Progress:" << progress; } void estimatedDownloadTime(int time) { if (time <= 0) { qDebug() << "Unknown time remaining."; return; } const int d = time / 86400; const int h = (time / 3600) - (d * 24); const int m = (time / 60) - (d * 1440) - (h * 60); const int s = time % 60; QString days; if (d > 0) days = QString::number(d) + QLatin1String(d < 2 ? " day" : " days") + QLatin1String(", "); QString hours; if (h > 0) hours = QString::number(h) + QLatin1String(h < 2 ? " hour" : " hours") + QLatin1String(", "); QString minutes; if (m > 0) minutes = QString::number(m) + QLatin1String(m < 2 ? " minute" : " minutes"); QString seconds; if (s >= 0 && minutes.isEmpty()) seconds = QString::number(s) + QLatin1String(s < 2 ? " second" : " seconds"); qDebug() << days + hours + minutes + seconds + tr("remaining."); } void downloadStatus(const QString &status) { qDebug() << status; } void downloadProgress(qint64 bytesReceived, qint64 bytesToReceive) { qDebug() << "Bytes received:" << bytesReceived << ", Bytes to receive:" << bytesToReceive; } private: bool m_downloadFinished; }; // http://get.qt.nokia.com/qt/source/qt-mac-opensource-4.7.4.dmg // ftp://ftp.trolltech.com/qt/source/qt-mac-opensource-4.7.4.dmg class ProxyFactory : public KDUpdater::FileDownloaderProxyFactory { public: ProxyFactory() {} ProxyFactory *clone() const { return new ProxyFactory(); } QList queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery()) { return QNetworkProxyFactory::systemProxyForQuery(query); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); if (a.arguments().count() < 2) return EXIT_FAILURE; const QUrl url(a.arguments().value(1)); KDUpdater::FileDownloaderFactory::setFollowRedirects(true); qDebug() << url.toString(); KDUpdater::FileDownloader *loader = KDUpdater::FileDownloaderFactory::instance().create(url.scheme(), 0); if (loader) { loader->setUrl(url); loader->setProxyFactory(new ProxyFactory()); Receiver r; r.connect(loader, &KDUpdater::FileDownloader::downloadStarted, &r, &Receiver::downloadStarted); r.connect(loader, &KDUpdater::FileDownloader::downloadCanceled, &r, &Receiver::downloadFinished); r.connect(loader, &KDUpdater::FileDownloader::downloadCompleted, &r, &Receiver::downloadFinished); r.connect(loader, &KDUpdater::FileDownloader::downloadAborted, &r, &Receiver::downloadAborted); r.connect(loader, &KDUpdater::FileDownloader::downloadSpeed, &r, &Receiver::downloadSpeed); r.connect(loader, &KDUpdater::FileDownloader::downloadStatus, &r, &Receiver::downloadStatus); r.connect(loader, SIGNAL(downloadProgress(double)), &r, SLOT(downloadProgress(double))); r.connect(loader, &KDUpdater::FileDownloader::estimatedDownloadTime, &r, &Receiver::estimatedDownloadTime); r.connect(loader, SIGNAL(downloadProgress(qint64,qint64)), &r, SLOT(downloadProgress(qint64,qint64))); loader->download(); while (!r.downloaded()) QCoreApplication::processEvents(); delete loader; } return EXIT_SUCCESS; } #include "main.moc" tests/environmentvariable/000077500000000000000000000000001325366651500163065ustar00rootroot00000000000000tests/environmentvariable/environmentvariable.pro000066400000000000000000000004161325366651500231030ustar00rootroot00000000000000TEMPLATE = app INCLUDEPATH += . .. TARGET = environmentvariable include(../../installerfw.pri) QT -= gui QT += testlib CONFIG += console HEADERS = environmentvariabletest.h SOURCES = environmentvariabletest.cpp macx:include(../../no_app_bundle.pri) tests/environmentvariable/environmentvariabletest.cpp000066400000000000000000000062611325366651500237710ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "environmentvariabletest.h" #include "environmentvariablesoperation.h" #include "init.h" #include #include #include #include #include EnvironmentVariableTest::EnvironmentVariableTest() { QInstaller::init(); } void EnvironmentVariableTest::testPersistentNonSystem() { #ifndef Q_OS_WIN QSKIP("This operation only works on Windows"); #endif QString key = QLatin1String("IFW_TestKey"); QString value = QLatin1String("IFW_TestValue"); QInstaller::EnvironmentVariableOperation op(0); op.setArguments( QStringList() << key << value << QLatin1String("true") << QLatin1String("false")); const bool ok = op.performOperation(); QVERIFY2(ok, qPrintable(op.errorString())); // Verify now... QSettings settings(QLatin1String("HKEY_CURRENT_USER\\Environment"), QSettings::NativeFormat); QVERIFY(value == settings.value(key).toString()); // Remove the setting QEXPECT_FAIL("", "Undo Operation not implemented yet", Continue); QVERIFY(op.undoOperation()); //QVERIFY(settings.value(key).toString().isEmpty()); settings.remove(key); } void EnvironmentVariableTest::testNonPersistentNonSystem() { #ifndef Q_OS_WIN QSKIP("This operation only works on Windows"); #endif QString key = QLatin1String("IFW_TestKey"); QString value = QLatin1String("IFW_TestValue"); QInstaller::EnvironmentVariableOperation op(0); op.setArguments( QStringList() << key << value << QLatin1String("false") << QLatin1String("false")); const bool ok = op.performOperation(); QVERIFY2(ok, qPrintable(op.errorString())); QString comp = QString::fromLocal8Bit(qgetenv(qPrintable(key))); QCOMPARE(value, comp); } QTEST_MAIN(EnvironmentVariableTest) tests/environmentvariable/environmentvariabletest.h000066400000000000000000000032161325366651500234330ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef EXTRACTIONARCHIVEOPERATIONTEST_H #define EXTRACTIONARCHIVEOPERATIONTEST_H #include #include class EnvironmentVariableTest : public QObject { Q_OBJECT public: EnvironmentVariableTest(); private Q_SLOTS: void testPersistentNonSystem(); void testNonPersistentNonSystem(); }; #endif // EXTRACTARCHIVEOPERATIONTEST_H tests/test-framework/000077500000000000000000000000001325366651500152065ustar00rootroot00000000000000tests/test-framework/README000066400000000000000000000263021325366651500160710ustar00rootroot00000000000000= Testing Framework = == Overview == The "testing framework" was developed to continuously run automated, non-interactive tests for the Nokia NDK installers on a set of test systems. It uses remote-controlled VMWare virtual machines (VMs) to execute the tests on. Both the actual controlling process and the post installation checks on the VMs are implemented in Python. Additionally, a set of test cases is created and configured. A test case consists of a) a QtScript script specifying the user input for non-interactive installation and b) post-installation checks to perform after installation. This way various user behaviour can be tested (e.g. different component selections) and whether installation succeeds and certain post-conditions are met (e.g. files end up in in specific locations). A test case can also contain additional steps to test updating and package management. For each compatible VM/Test case combination, the VM is reset to a predefined state, the installer is run using the QtScript script and the post-installation checks are performed. The test results are aggregated and submitted to a CDash dashboard. == Setup at Nokia == The VMs run on a VMWare server installation on bersrv16723. The VMWare server web frontend requires a IE (32-bit) plugin to get the guest system's shell. (Nokia internal: http://hegel.europe.nokia.com/binaries/vmware_client_plugin/vmware-vmrc-win32-x86.exe). As far as I know there is no way atm to get it running on other platforms. Web frontend URL: http://bersrv16723:8222/ui (redirect to https://bersrv16723:8333/ui, but in some cases it is not working without this redirection) The testing framework itself is configured and runs on the controller VM. It it supposed to be accessed via ssh. Controller VM: 172.25.167.111 [get a fixed IP/hostname]. Attach/Start a screen session: screen -D -R Start the test runner: cd $HOME/test/installerfw/installer/test-framework vmware/run.py -s 2010-05-17 site/host-config.cfg # insert your date here For more explanations on run.py see below. The CDash location presenting the build results is http://172.25.167.111/CDash (the controller VM). The project used is NDKInstallerTests (TODO: actually use that one). The current setup with working examples of configuration files is in $TODO:/home/kdab/test/ (TODO: commit to repo) == run.py == The central python script executing test runs is vmware/run.py. Usage:run.py [options] host-config Options: -s | --only-since YYYY-MM-DD when checking /path/to/vmware/run.py -s 2010-05-14 host-config.cfg run.py does the following: It collects the information about configured VMs and test cases from the host configuration file. It then checks the configured installer sources for new installers, and sequentially executes the test for each new installer on all VM/test case combinations that apply to the installer (there can be specific tests cases for certain platforms only). For each installer x VM x test case run, the result is added to CDash. Once all installers on the FTP server (or the ones matching the --only-since restriction) were tested, the process goes to sleep and checks every hour for new installers. run.py only executes all test runs sequentially, with a single test at a time. If this turns out to be too much of a bottleneck, multiple run.py instances in different working directories and with different host-config files could be run in parallel (e.g. each one monitoring a different path on the FTP server). == Virtual Machine Setup == Any VMWare virtual machine can be used for testing, if some preparations are made: * Add a virtual CD/DVD drive (for VMWare Tools; Choose a random ISO file when asked) * Install VMWare Tools (important: The test run might just hang and do nothing if the VM has no VMWare tools installed) * Install Python 2.6 * Create a snapshot of the VM in the state that should be used as initial state for each test run [TODO: relate to UI] * Create a configuration file for the VM. The configuration file provides information to run.py about the VM, such as the user/password combination to use to login, the location of the python installation or the path in the VM to use for temporary files, such as the downloaded installer and result output files. (See Virtual Machine Configuration File Options for details) * To enable the VM, add the path to the VM config file to the host config file (See Host Configuration File Options). == Virtual Machine Configuration File Options == Example: TODO === Section: General === snapshot: identifier (Default: "base") The baseline snapshot to revert to when start a test run. This option is only relevant VMWare Fusion and Workstation (as Server/ESX only support a single unnamed snapshot) username: string password: string The username and password to authenticate with inside the VM. vmx: string The path to the VM's vmx file. Relative paths are understood relative to the configuration file. For remote setups, the name as listed by the TODO tool must be used. TODO: explain how to list snapshots. tempDir: guest path Path inside the VM to use for temporary data (installer, checker copy, test script...). It must be writable by the user. os: windows|linux|mac The OS running inside the VM python: guest path (Default: "python") The path to the installed python executable inside the VM. == Test Cases == === Configuration File === General/platforms: string list comma-separated list of platforms the testcase should be run on. Valid platform names are "windows", "linux", "mac" General/targetDirectory: guest path The target directory to use for the post-installation checks (see below). Note that the actual installation directory is specified in the installscript and must be kept in sync. See the section on post-installation checks for details. General/maintenanceToolLocation The path to the maintenance tool (updater/package manager) after installation. Required for multi-step tests ==== Steps ==== Each test case can consist of of n steps and must have at least one. The first step is the actual installation and will execute the installer, the following steps will start the maintenance tool. Step section must be consecutive and start at 0: Step0, Step1, Step2 etc. StepN/installscript: path The path to the QtScript used for non-interactive execution of the installer/maintenance tool. relative paths are interpreted relative to the configuration file. StepN/checkerTestDir: string The path to a directory containing a set of tests to perform after a successful installer/maintenance tool run. relative paths are interpreted relative to the configuration file. Example: [General] platforms=windows,linux targetDirectory=c:\testinstall maintenanceToolLocation=c:\testInstall\TestUninstaller.exe [Step0] installscript=testscript.qs checkerTestDir=checker === Install scripts === see non-interactive installation section in installerbuilder/installerbuilder.dox TODO: merge === Post-installation checks === The framework can execute post-installation tests on the VM to check if the installation is in the expected state. The python code executing those checks is called "checker". Each testcase can contain a "checker test directory" to contain checks to be executed. The framework copies both the checker code and the test case into the VM, executes the checks and reports the results. Currently, the following checks are supported: ==== Files ==== It can be checked for files to exist in the installation and their file size and md5sum. To add such a check, place a file with extension .filelist in the test directory. The format of a .filelist file is: filename; file size; md5sum file size and md5sum are optional and can be empty. Example: components.xml; 2050; 6813144fd09f7d39764702e5adb91679wrong index.html; 46; fd40a94472ea1d13d93221c5ce62c321 include\QtGui\qwidget.h; 108; 67dc776dd5aa66741dab6a2eeec4ac3c Relative paths are understood relative to the targetDirectory. Such files can be generated using the script test-framework/checker/scripts/generate-filelist.py ==== Windows Registry ==== Similar to .filelist, .registrylist files can be used to check for the existance and content of windows registry keys. The format is ; ; HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion; SM_GamesName; Games ==== Arbitrary python code ==== Not yet done but easy to implement: Run tests from python code from .py files in the test dir. == Host Configuration File Options == === Section: General === vmrun: path (Default: "vmrun") vmrun specifies the location of the vmrun executable to use. vmrun is part of VMRun Workstation, Fusion, Server and ESX. If the framework not on the same system as the VMWare server, you must sure that the executable installed supports the VMWare edition used (At the time of writing, this is VMWare Server). checkerInstallation: path The path to the "checker" python files which will be copied to each VM and executed to perform postinstallation checks. Example: checkerInstallation=/home/kdab/test/installerfw/installer/test-framework/checker testcase{0..n}: path The configuration files of the test cases to use. Relative paths are understood relative to the configuration file. vm{0..n}: path n configuration files of the virtual machines to use. Relative paths are understood relative to the configuration file. gui: True|False (default: False TODO: ensure) Whether VMWare/vmrun should bring up . Only relevant when used with VMWare Workstation or Fusion. createErrorSnapshots True|False (default: False TODO: ensure) Specifies if the framework should create error snapshots if the installation or post-installation checks fail. This is currently only supported for VMWare Workstation and Fusion (the server editions unfortunately only support a single snapshot per VM, which is reserved for the baseline. === Section: Host === This section is optional. It is used if the VMWare host is different from the local machine (VMWare Server or esx). type: string The VMWare edition to talk against, one of "server" and "esx" location: URL The URL of the server/esx installation to talk to Example: location=https://172.25.167.23:8333/sdk username: string The user used to login on the VMWare server password: string The user used to login on the VMWare server === Section: CDash === The framework is able to generate test reports in CDash format. If this section does not exist, the results are just printed to stdout. host: hostname The host of the CDash installation location: The path to the CDash installation on host project The CDash project to report the test results to. Example: [Host] host=localhost location=/CDash project=NokiaSDKInstallerTest === Section: Source{0..n} === Each source provides installers to be tested, for a specific platform. Currently, FTP is assumed and the only supported protocol. The Example: [Source0] host=hegel path=/projects/ndk/installers/windows platform=windows [Source1] host=hegel path=/projects/ndk/installers/linux platform=linux tests/test-framework/checker/000077500000000000000000000000001325366651500166125ustar00rootroot00000000000000tests/test-framework/checker/run.py000066400000000000000000000057041325366651500177760ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import optparse, sys from testrunner import testrunner from xml.sax.saxutils import XMLGenerator from xml.sax.xmlreader import AttributesNSImpl class Writer: def __init__( self, out ): self._out = out self.gen = XMLGenerator( out, 'utf-8' ) self.gen.startDocument() self.gen.startElement( 'results', {} ) def addResult( self, name, status, errstr ): self.gen.startElement('result', { 'name':name, 'status':status } ) self.gen.characters( errstr ) self.gen.endElement('result' ) def addFailed( self, name, errstr ): self.addResult( name, "failed", errstr ) def addPassed( self, status, errstr ): self.addResult( name, "passed", errstr ) def finalize( self ): self.gen.endElement( 'results' ) self.gen.endDocument self._out.write( '\n' ) optionParser = optparse.OptionParser(usage="%prog [options] testcaseDir", version="%prog 0.1") optionParser.add_option("-p", "--omit-prefix", dest="prefix", help="for file checks, prefix relative paths with this prefix", metavar="PREFIX" ) optionParser.add_option("-o", "--output", dest="output", help="write results to file (instead of stdout)", metavar="OUTPUT" ) (options, args) = optionParser.parse_args() try: testdir = args[0] except IndexError: optionParser.print_usage( sys.stderr ) sys.exit( 1 ) out = sys.stdout if options.output != None: out = file( options.output, 'wb' ) writer = Writer( out ) try: runner = testrunner.TestRunner( testdir, options.prefix, writer ) runner.run() finally: writer.finalize()tests/test-framework/checker/scripts/000077500000000000000000000000001325366651500203015ustar00rootroot00000000000000tests/test-framework/checker/scripts/generate-filelist.py000066400000000000000000000051761325366651500242670ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import optparse, os, sys from testrunner import files, testrunner class GenerateException( Exception ): def __init__( self, value ): self.value = value def __str__( self ): return repr( self.value ) def relpath( path, prefix ): if prefix != None: return os.path.relpath( path, prefix ) else: return path; out = sys.stdout def walker( prefix, current_dir, children ): for c in children: child = current_dir + os.sep + c if os.path.isdir( child ): continue fileObj = file( child, 'rb' ) md5 = files.md5sum( fileObj ) out.write( "{0}; {1}; {2}\n".format( relpath( child, prefix ), os.path.getsize( child ), md5 ) ) optionParser = optparse.OptionParser(usage="%prog [options] directory", version="%prog 0.1") optionParser.add_option("-p", "--omit-prefix", dest="prefix", help="make entries relative to this prefix", metavar="PREFIX" ) optionParser.add_option("-o", "--output", dest="output", help="save file list to file (instead of stdout)", metavar="OUTPUT" ) (options, args) = optionParser.parse_args() try: directory = args[0] except IndexError: raise GenerateException( "No directory given.") if options.output != None: out = file( options.output, 'wb' ) os.path.walk( directory, walker, options.prefix ) tests/test-framework/checker/testrunner/000077500000000000000000000000001325366651500210235ustar00rootroot00000000000000tests/test-framework/checker/testrunner/__init__.py000066400000000000000000000000131325366651500231260ustar00rootroot00000000000000# init.py tests/test-framework/checker/testrunner/files.py000066400000000000000000000050021325366651500224740ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# from testexception import TestException import fnmatch, hashlib, os def md5sum( fileObj ): md5 = hashlib.md5() while True: chunk = fileObj.read( 4096 ) if not chunk: break md5.update( chunk ) return md5.hexdigest() def locateFiles( rootPath, pattern ): for path, dirs, files in os.walk( os.path.abspath( rootPath ) ): for filename in fnmatch.filter( files, pattern ): yield os.path.join( path, filename ) def checkFileImpl( path, expectedSize=-1, expectedMd5=None ): #TODO: normalize path/convert to platform if not os.path.exists( path ): raise TestException( '{0}: file does not exist'.format( path ) ) size = os.path.getsize( path ) if expectedSize >= 0 and size != expectedSize: raise TestException( '{0}: unexpected size. Actual: {1} Expected: {2}'.format( path, size, expectedSize ) ) if ( expectedMd5 != None ): fileObj = file( path, 'rb' ) md5 = md5sum( fileObj ) fileObj.close() if md5 != expectedMd5: raise TestException( '{0}: md5sum mismatch. Actual: {1} Expected: {2}'.format( path, md5, expectedMd5 ) ) tests/test-framework/checker/testrunner/logger.py000066400000000000000000000000001325366651500226420ustar00rootroot00000000000000tests/test-framework/checker/testrunner/registry.py000066400000000000000000000042011325366651500232420ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# from testexception import TestException import _winreg _registry = dict() _registry["HKEY_CLASSES_ROOT"] = _winreg.HKEY_CLASSES_ROOT _registry["HKEY_CURRENT_USER"] = _winreg.HKEY_CURRENT_USER _registry["HKEY_LOCAL_MACHINE"] = _winreg.HKEY_LOCAL_MACHINE _registry["HKEY_USERS"] = _winreg.HKEY_USERS _registry["HKEY_CURRENT_CONFIG"] = _winreg.HKEY_CURRENT_CONFIG def splitKey( key ): key, separator, subKey = key.partition( '\\' ) return _registry[key], subKey def checkKey( key, value, expectedData ): baseKey, subKey = splitKey( key ) keyHandle = _winreg.OpenKey( baseKey, subKey ) data, _ = _winreg.QueryValueEx( keyHandle, value ) if data != expectedData: raise TestException( '{0}: unexpected registry data. Actual: {1} Expected: {2}'.format( key, data, expectedData ) ) tests/test-framework/checker/testrunner/testexception.py000066400000000000000000000027011325366651500242730ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# class TestException( Exception ): def __init__( self, value ): self.value = value def __str__( self ): return repr( self.value ) tests/test-framework/checker/testrunner/testrunner.py000066400000000000000000000110321325366651500236030ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import files, os, string, platform from testexception import TestException if ( platform.system() == "Windows" ): import registry def makeAbsolutePath( path, relativeTo ): if os.path.isabs( path ) or relativeTo == None: return path else: return relativeTo + os.sep + path class TestRunner: def __init__( self, testDir, basedir, result ): self._testDir = testDir self._basedir = basedir self._result = result def checkFile( self, name, size=-1, expectedMd5=None ): try: files.checkFileImpl( name, size, expectedMd5 ) except TestException as e: self._result.addFailed( name, e.value ) def checkFileList( self, path ): lineNum = 0 haveError = False with open( path, 'r' ) as f: while True: line = f.readline() lineNum += 1 if not line: break line = string.strip( line ) if len( line ) == 0: continue segments = string.split( line, ';' ) if len( segments ) == 3: fp = makeAbsolutePath( segments[0], self._basedir ) try: fs = int( segments[1] ) except ValueError: fs = -1 #TODO handle error femd5 = segments[2] femd5 = string.strip( femd5 ) self.checkFile( fp, fs, femd5 ) else: self._result.addFailed( path + '_' + str( lineNum ), "Could not parse file list entry: " + line ) haveError = True if not haveError: self._result.addPassed( path, "" ) def checkRegistryList( self, path ): haveError = False lineNum = 0 with open( path, 'r' ) as f: while True: lineNum += 1 line = f.readline() if not line: break line = string.strip( line ) if len( line ) == 0: continue segments = string.split( line, ';' ) if len( segments ) == 3: key = segments[0].strip() value = segments[1].strip() expectedData = segments[2].strip() registry.checkKey( key, value, expectedData ) else: self._result.addFailed( path + '_' + str( lineNum ), "Could not parse registry list entry: " + line ) haveError = True if not haveError: self._result.addPassed( path, "" ) def run( self ): fileLists = files.locateFiles( self._testDir, "*.filelist" ) for i in fileLists: self.checkFileList( i ) if ( platform.system() == "Windows" ): registryLists = files.locateFiles( self._testDir, "*.registrylist" ) for i in registryLists: self.checkRegistryList( i ) # run all .py's in testdir # execute all filelists tests/test-framework/site/000077500000000000000000000000001325366651500161525ustar00rootroot00000000000000tests/test-framework/site/TestCases/000077500000000000000000000000001325366651500200505ustar00rootroot00000000000000tests/test-framework/site/TestCases/testcase-linux64/000077500000000000000000000000001325366651500231725ustar00rootroot00000000000000tests/test-framework/site/TestCases/testcase-linux64/checker/000077500000000000000000000000001325366651500245765ustar00rootroot00000000000000tests/test-framework/site/TestCases/testcase-linux64/checker/testinstall.filelist000066400000000000000000000004531325366651500307030ustar00rootroot00000000000000components.xml; 2050; 6813144fd09f7d39764702e5adb91679wrong index.html; 46; fd40a94472ea1d13d93221c5ce62c321 uninstall.exe; 13598393; 44945e7d3507d294b5e9e096ac3269b1 include\QtCore\qobject.h; 108; 9e50d789f32d1651e16b6ae55699eb71 include\QtGui\qwidget.h; 108; 67dc776dd5aa66741dab6a2eeec4ac3c tests/test-framework/site/TestCases/testcase-linux64/testcase-linux64.cfg000066400000000000000000000001731325366651500267760ustar00rootroot00000000000000[General] installscript=testscript.qs platforms=linux64 targetDirectory=/home/kdab/testinstall checkerTestDir=checker tests/test-framework/site/TestCases/testcase-linux64/testscript.qs000066400000000000000000000063021325366651500257440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Controller() { installer.autoRejectMessageBoxes installer.setMessageBoxAutomaticAnswer( "overwriteTargetDirectory", QMessageBox.Yes ) } Controller.prototype.IntroductionPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.LicenseAgreementPageCallback = function() { var page = gui.pageWidgetByObjectName( "LicenseAgreementPage" ) page.acceptLicenseRB.setChecked( true ) gui.clickButton( buttons.NextButton ) } Controller.prototype.TargetDirectoryPageCallback = function() { var page = gui.pageWidgetByObjectName( "TargetDirectoryPage" ) page.targetDirectoryLE.setText( "/home/kdab/testinstall" ) gui.clickButton( buttons.NextButton ) } Controller.prototype.ComponentSelectionPageCallback = function() { var page = gui.pageWidgetByObjectName( "ComponentSelectionPage" ) page.deselectComponent( "com.nokia.ndk.tools.maemo.usbdriver" ) gui.clickButton( buttons.NextButton ) } Controller.prototype.DynamicQtGuiPageCallback = function() { var page = gui.pageWidgetByObjectName( "DynamicQtGuiPage" ) page.checkBoxLib.setChecked( false ) gui.clickButton( buttons.NextButton ) } Controller.prototype.DynamicErrorPageCallback = function() { var page = gui.pageWidgetByObjectName( "DynamicErrorPage" ) page.checkBoxMakeSure.setChecked( true ) gui.clickButton( buttons.NextButton ) } Controller.prototype.ReadyForInstallationPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.StartMenuDirectoryPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.PerformInstallationPageCallback = function() { var page = gui.pageWidgetByObjectName( "PerformInstallationPage" ) page.details.button.click } Controller.prototype.FinishedPageCallback = function() { gui.clickButton( buttons.FinishButton ) } tests/test-framework/site/TestCases/testcase1/000077500000000000000000000000001325366651500217445ustar00rootroot00000000000000tests/test-framework/site/TestCases/testcase1/checker/000077500000000000000000000000001325366651500233505ustar00rootroot00000000000000tests/test-framework/site/TestCases/testcase1/checker/testinstall.filelist000066400000000000000000000004531325366651500274550ustar00rootroot00000000000000components.xml; 2050; 6813144fd09f7d39764702e5adb91679wrong index.html; 46; fd40a94472ea1d13d93221c5ce62c321 uninstall.exe; 13598393; 44945e7d3507d294b5e9e096ac3269b1 include\QtCore\qobject.h; 108; 9e50d789f32d1651e16b6ae55699eb71 include\QtGui\qwidget.h; 108; 67dc776dd5aa66741dab6a2eeec4ac3c tests/test-framework/site/TestCases/testcase1/testcase1.cfg000066400000000000000000000001711325366651500243200ustar00rootroot00000000000000[General] installscript=testscript.qs platforms=windows,linux targetDirectory=c:\testinstall checkerTestDir=checker tests/test-framework/site/TestCases/testcase1/testscript.qs000066400000000000000000000062731325366651500245250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Controller() { installer.autoRejectMessageBoxes installer.setMessageBoxAutomaticAnswer( "overwriteTargetDirectory", QMessageBox.Yes ) } Controller.prototype.IntroductionPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.LicenseAgreementPageCallback = function() { var page = gui.pageWidgetByObjectName( "LicenseAgreementPage" ) page.acceptLicenseRB.setChecked( true ) gui.clickButton( buttons.NextButton ) } Controller.prototype.TargetDirectoryPageCallback = function() { var page = gui.pageWidgetByObjectName( "TargetDirectoryPage" ) page.targetDirectoryLE.setText( "c:\\testinstall" ) gui.clickButton( buttons.NextButton ) } Controller.prototype.ComponentSelectionPageCallback = function() { var page = gui.pageWidgetByObjectName( "ComponentSelectionPage" ) page.deselectComponent( "com.nokia.ndk.tools.maemo.usbdriver" ) gui.clickButton( buttons.NextButton ) } Controller.prototype.DynamicQtGuiPageCallback = function() { var page = gui.pageWidgetByObjectName( "DynamicQtGuiPage" ) page.checkBoxLib.setChecked( false ) gui.clickButton( buttons.NextButton ) } Controller.prototype.DynamicErrorPageCallback = function() { var page = gui.pageWidgetByObjectName( "DynamicErrorPage" ) page.checkBoxMakeSure.setChecked( true ) gui.clickButton( buttons.NextButton ) } Controller.prototype.ReadyForInstallationPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.StartMenuDirectoryPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.PerformInstallationPageCallback = function() { var page = gui.pageWidgetByObjectName( "PerformInstallationPage" ) page.details.button.click } Controller.prototype.FinishedPageCallback = function() { gui.clickButton( buttons.FinishButton ) } tests/test-framework/site/VMConfigs/000077500000000000000000000000001325366651500200055ustar00rootroot00000000000000tests/test-framework/site/VMConfigs/LinuxUbuntu9.1064Bit.cfg000066400000000000000000000002661325366651500241150ustar00rootroot00000000000000[General] snapshot=base username=kdab password=kdab vmx=[standard] Linux Ubuntu 9.10 64 Bit/Linux Ubuntu 9.10 64 Bit.vmx tempDir=/home/kdab os=linux64 python=/usr/bin/python tests/test-framework/site/VMConfigs/WindowsVista32Bit.cfg000066400000000000000000000003021325366651500237260ustar00rootroot00000000000000[General] snapshot=base username=kdab password=kdab vmx=[standard] Windows Vista 32 Bit/Windows Vista 32 Bit.vmx tempDir=c:\Users\kdab\Desktop os=windows python=c:\Python26\python.exe tests/test-framework/site/VMConfigs/WindowsVista64Bit.cfg000066400000000000000000000003221325366651500237350ustar00rootroot00000000000000[General] snapshot=base username=kdab password=kdab vmx=[standard] Windows XP 32 Bit/Windows XP 32 Bit.vmx tempDir=c:\Dokumente und Einstellungen\kdab\Desktop os=windows python=c:\Python26\python.exe tests/test-framework/site/VMConfigs/WindowsXp32Bit.cfg000066400000000000000000000003221325366651500232310ustar00rootroot00000000000000[General] snapshot=base username=kdab password=kdab vmx=[standard] Windows XP 32 Bit/Windows XP 32 Bit.vmx tempDir=c:\Dokumente und Einstellungen\kdab\Desktop os=windows python=c:\Python26\python.exe tests/test-framework/site/VMConfigs/WindowsXp64Bit.cfg000066400000000000000000000003151325366651500232400ustar00rootroot00000000000000[General] snapshot=base username=kdab password=kdab vmx=[standard] Windows XP 64 Bit/Windows XP 64 Bit.vmx tempDir=c:\Documents and Settings\kdab\Desktop os=windows python=c:\Python26\python.exe tests/test-framework/site/host-config.cfg000066400000000000000000000014351325366651500210560ustar00rootroot00000000000000[General] vmrun=vmrun checkerInstallation=/home/kdab/test/installerfw/installer/test-framework/checker testcase0=TestCases/testcase1/testcase1.cfg testcase1=TestCases/testcase-linux64/testcase-linux64.cfg vm0=VMConfigs/WindowsXp32Bit.cfg vm1=VMConfigs/WindowsXp64Bit.cfg vm2=VMConfigs/WindowsVista32Bit.cfg vm3=VMConfigs/LinuxUbuntu9.1064Bit.cfg gui=False createErrorSnapshots=False [Host] type=server location=https://172.25.167.23:8333/sdk username=kdab password=kdab [CDash] host=localhost location=/CDash project=test1 [Source0] host=hegel path=/projects/ndk/installers/windows platform=windows [Source1] host=hegel path=/projects/ndk/installers/linux/x32 platform=linux32 [Source2] host=hegel path=/projects/ndk/installers/linux/x64 platform=linux64 tests/test-framework/site/listVMs.sh000066400000000000000000000025421325366651500201120ustar00rootroot00000000000000############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# vmrun -u kdab -p kdab -T server -h https://172.25.167.23:8333/sdk listRegisteredVM tests/test-framework/tests/000077500000000000000000000000001325366651500163505ustar00rootroot00000000000000tests/test-framework/tests/simpletest.py000066400000000000000000000027761325366651500211270ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import sys from testrunner import testrunner class Result: def addError( self, errstr ): print errstr result = Result() runner = testrunner.TestRunner( sys.argv[1], result ) runner.run() tests/test-framework/tests/testfiles/000077500000000000000000000000001325366651500203525ustar00rootroot00000000000000tests/test-framework/tests/testfiles/test.filelist000066400000000000000000000002141325366651500230630ustar00rootroot00000000000000/home/frank/.ssh/id_dsa.pub; 1113; 1fddf250c364370b7936c1b7256c7eda /home/frank/.ssh/id_dsa.pub; 1113; 1fddf250c364370b7936c1b7256c7edb tests/test-framework/vmware/000077500000000000000000000000001325366651500165075ustar00rootroot00000000000000tests/test-framework/vmware/cdashreporter.py000066400000000000000000000173521325366651500217360ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import datetime, socket, os, sys, traceback, time, tempfile, httplib, urllib from xml.sax.saxutils import XMLGenerator from xml.sax.xmlreader import AttributesNSImpl import result, utils from result import exitStatusAsString from xmlutils import startElement, endElement, writeElement checkerResultAsString = { result.CheckerResult.Passed:u"passed", result.CheckerResult.Failed:u"failed", result.CheckerResult.NotRun:u"notRun" } def formatDate( date ): return date.strftime( '%Y%m%d-%H%M' ) def writeExecutionResult( gen, name, r ): if r == None or r.hasError(): stat = "failed" else: stat = "passed" startElement( gen, None, "Test", { u"Status":stat } ) writeElement( gen, None, "Name", name ) writeElement( gen, None, "FullName", name ) startElement( gen, None, "NamedMeasurement", { u"type":u"text/string", u"name":u"Exit Code"} ) if r: msg = "(Unexpected)" ec = r.exitCode if ec == 0: msg = "(Success)" elif ec == 1: msg = "(Failed)" elif ec == 2: msg = "(Canceled)" writeElement( gen, None, "Value", "Exit status: {0}; Installer exit code: {1} {2}".format( exitStatusAsString( r.exitStatus ), str( r.exitCode ), msg ) ) else: writeElement( gen, None, "Value", "Could not determine installation result." ) endElement( gen, None, "NamedMeasurement" ) if r: startElement( gen, None, "NamedMeasurement", { u"type":u"numeric/double", u"name":u"Execution Time"} ) writeElement( gen, None, "Value", str( r.executionTime ) ) endElement( gen, None, "NamedMeasurement" ) endElement( gen, None, "Test" ) def writeInternalError( gen, errorstr, num ): startElement( gen, None, "Test", { u"Status":u"failed"} ) name = "InternalError{0}".format( num ) writeElement( gen, None, "Name", name ) writeElement( gen, None, "FullName", name ) startElement( gen, None, "NamedMeasurement", { u"type":u"text/string", u"name":u"Completion Status"} ) writeElement( gen, None, "Value", errorstr ) endElement( gen, None, "NamedMeasurement" ) endElement( gen, None, "Test" ) def writeTest( gen, test ): startElement( gen, None, "Test", { u"Status":checkerResultAsString[test.result]} ) writeElement( gen, None, "Name", test.name ) writeElement( gen, None, "FullName", test.name ) startElement( gen, None, "NamedMeasurement", { u"type":u"text/string", u"name":u"Completion Status"} ) writeElement( gen, None, "Value", test.errorString ) endElement( gen, None, "NamedMeasurement" ) endElement( gen, None, "Test" ) class CDashReporter: def __init__( self, host, location, project ): self._host = host self._location = location self._project = project def reportException( self ): sys.stderr.write( traceback.format_exc( 15 ) + '\n' ) #TODO def reportResult( self, result ): self._buildStamp = '{0}-Nightly'.format( datetime.datetime.now().strftime( '%Y%m%d-%H%M' ), utils.randomString( 4 ) ) try: #generate XML self.genXml( self.genTestXml, result ) #generate Tests.xml #submitFile('Tests.xml') except: raise def genTestXml( self, gen, result ): name = "{0} on {1}".format( result._installer.sourceFilename, result._vm.name() ) startElement( gen, None, "Site", { u"BuildStamp":self._buildStamp, u"Name":name, u"Generator":u"VMTester 0.1" } ) startElement( gen, None, "Testing" ) writeElement( gen, None, "StartDateTime", formatDate( result._testStart ) ) startElement( gen, None, "TestList" ) internal = result._internalErrors for i in range( 0, len( internal ) - 1 ): writeElement( gen, None, "Test", "InternalError{0}".format( i ) ) for stepNum in range( len( result._stepResults ) ): step = result._stepResults[stepNum] writeElement( gen, None, "Test", "installer-run{0}".format( stepNum ) ) for i in step.checkerResults: writeElement( gen, None, "Test", i.name ) endElement( gen, None, "TestList" ) for i in range( 0, len( internal ) - 1 ): writeInternalError( gen, internal[i], i ) for stepNum in range( len( result._stepResults ) ): step = result._stepResults[stepNum] writeExecutionResult( gen, "installer-run{0}".format( stepNum ), step.executionResult ) for i in step.checkerResults: writeTest( gen, i ) endElement( gen, None, "Testing" ) endElement( gen, None, "Site" ) def genXml( self, fillFunc, result ): f = None try: f = tempfile.NamedTemporaryFile( delete=False ) gen = XMLGenerator( f, 'utf-8' ) gen.startDocument() fillFunc( gen, result ) gen.endDocument() f.close() self.submitFile( f.name ) finally: if f: os.unlink( f.name ) def submitFile( self, path ): f = file( path ) params = urllib.urlencode( {'project': self._project} ) conn = None try: try: conn = httplib.HTTPConnection( self._host ) conn.request( "POST", "{0}/submit.php?project={1}".format( self._location, self._project ), f ) response = conn.getresponse() print response.status, response.reason #TODO check result finally: if conn: conn.close() except: #TODO: if submitting to cdash fails, try to notify the admin (mail?) raise if __name__ == "__main__": r = result.Result() r.testStarted() r.addCheckerResult( result.CheckerResult( "test1", result.CheckerResult.Passed, "" ) ) r.addCheckerResult( result.CheckerResult( "test2", result.CheckerResult.Failed, "Something went wrong, dude!" ) ) time.sleep( 1 ) r.testFinished() cr = CDashReporter( "http://localhost", "/CDash", "test1" ) cr.reportResult( r ) tests/test-framework/vmware/control.py000066400000000000000000000257361325366651500205560ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import ConfigParser, datetime, os, string, sys, time, platform import testcase, utils, result, virtualmachine from virtualmachine import VMException from xml.sax import make_parser from xml.sax.handler import ContentHandler class ControlException( Exception ): def __init__( self, value ): self.value = value def __str__( self ): return repr( self.value ) class Handler( ContentHandler ): def __init__( self, res ): self._res = res self._inResult = False self._buf = "" def startElement( self, name, attrs ): self._inResult = False if name == "result": self._inResult = True self._name = attrs['name'] self._status = attrs['status'] def endElement( self, name ): if name == 'result': trimmed = string.strip( self._buf ) if self._status == "passed": stat = result.CheckerResult.Passed else: stat = result.CheckerResult.Failed self._res.addCheckerResult( result.CheckerResult( self._name, stat, trimmed ) ) self._inResult = False self._buf = "" def characters( self, ch ): if self._inResult: self._buf += ch class Control: def __init__( self, vmrun, checkerDir, source, reporter ): self._vmrun = vmrun self._checkerDir = checkerDir self._vms = [] self._testcases = [] self._source = source self._reporter = reporter self._guiEnabled = True self._createErrorSnapshots = False self._hostType = "" self._hostLocation = "" self._hostUsername = "" self._hostPassword = "" def setGuiEnabled( self, usegui ): self._guiEnabled = usegui for i in self._vms: i.setGuiEnabled( usegui ) def setCreateErrorSnapshots( self, createSnapshots ): self._createErrorSnapshots = createSnapshots def setRemoteHost( self, type, loc, user, pw ): self._hostType = type self._hostLocation = loc self._hostUsername = user self._hostPassword = pw for vm in self._vms: if not vm.isRemote(): vm.setRemoteHost( self._hostType, self._hostLocation, self._hostUsername, self._hostPassword ) def addVM( self, cfgpath ): config = ConfigParser.SafeConfigParser() config.read( cfgpath ) vm = virtualmachine.fromVMRunAndPath( self._vmrun, cfgpath ) vm.setGuiEnabled( self._guiEnabled ) if len( self._hostType ) > 0 and not vm.isRemote(): vm.setRemoteHost( self._hostType, self._hostLocation, self._hostUsername, self._hostPassword ) self._vms.append( vm ) #TODO catch/transform exceptions def addTestCase( self, path ): self._testcases.append( testcase.TestCase( path ) ) def run( self ): while True: try: inst = self._source.nextInstaller() if inst == None: print( "** Installer source returned None, aborting" ) return if inst.error: raise ControlException( inst.error ) print( "** New installer: {0}".format( inst.path ) ) self.testInstaller( inst, inst.platform ) except KeyboardInterrupt: raise except: self._reporter.reportException() def testInstaller( self, inst, platform ): for vm in self._vms: if vm.ostype() != platform: continue for case in self._testcases: if not case.supportsPlatform( platform ): continue res = result.Result() try: try: res.setInstaller( inst ) res.setTestCase( case ) res.setVirtualMachine( vm ) res.testStarted() self.run_test( inst.path, vm, case, res ) res.testFinished() inst.markAsTested() except KeyboardInterrupt: raise except: res.addException() finally: self._reporter.reportResult( res ) def convertCheckerResults( self, filename, res ): parser = make_parser() parser.setContentHandler( Handler( res ) ) f = file( filename, 'rb' ) parser.parse( f ) def run_test( self, installerPath, vm, testcase, res ): steps = testcase.steps() if len( steps ) == 0: raise ControlException( "No steps found for testcase {0}".format( testcase.name() ) ) revertStatus, _ = vm.revertToSnapshot() if revertStatus != 0: raise VMException( "Failed to revert to snapshot '{0}'".format( vm.snapshot() ) ) time.sleep( 5 ) # Trying to avoid a possible race between restore and start vm.start() try: try: vm.checkPythonInstalled() wrapperpath = vm.copyToTemp( utils.execution_path( 'guest.py' ) ) for stepNum in range( len( steps ) ): needSnapshot = False step = steps[stepNum] if stepNum == 0: executableguestpath = vm.copyToTemp( installerPath ) else: executableguestpath = testcase.maintenanceToolLocation() outputFileName = 'output{0}.log'.format( stepNum ) outputpath = vm.mkTempPath( outputFileName ) scriptguestpath = vm.copyToTemp( step.installscript() ) timeout = step.timeout() checkerguestpath = vm.copyToTemp( step.checkerTestDir(), "checkerTestDir{0}".format( stepNum ) ) if len( string.strip( step.checkerTestDir() ) ) > 0 else None vm.command( 'Execute installer', "runProgramInGuest", "'{0}' '{1}' '{2}' '{3}' '{4}' --script '{5}'".format( vm.python(), wrapperpath, outputpath, timeout, executableguestpath, scriptguestpath ) ) vm.copyFromTemp( outputFileName, outputFileName ) r = ConfigParser.SafeConfigParser() r.read( outputFileName ) try: s = r.get( 'Result', 'ExitCode' ) exitCode = int( s ) except ValueError: res.addInternalError( "Could not parse integer exit code from '{0}'".format( r.get( 'Result', 'ExitCode' ) ) ) exitCode = -1 try: s = r.get( 'Result', 'ExecutionTime' ) executionTime = float( s ) except ValueError: res.addInternalError( "Could not parse float execution time from '{0}'".format( r.get( 'Result', 'ExecutionTime' ) ) ) executionTime = 0.0 exitStatus = result.exitStatusFromString( r.get( 'Result', 'ExitStatus' ) ) instR = result.ExecutionResult( exitCode, exitStatus, executionTime ) if instR.hasError(): needSnapshot = True checkerResults = [] if checkerguestpath and not instR.hasError(): if ( platform.system() == "Darwin" ): # Have to sleep to work around VMware Fusion bug time.sleep( 30 ) run_py = vm.copyToTemp( self._checkerDir ) + vm.pathSep() + "run.py" if ( platform.system() == "Darwin" ): # Have to sleep to work around VMware Fusion bug time.sleep( 30 ) checkeroutputFileName = 'checker-output{0}.xml'.format( stepNum ) checkeroutput = vm.mkTempPath( checkeroutputFileName ) vm.command( 'Execute checker tests', "runProgramInGuest", "'{0}' '{1}' '{2}' -o '{3}' -p '{4}'".format( vm.python(), run_py, checkerguestpath, checkeroutput, testcase.targetDirectory() ) ) vm.copyFromTemp( checkeroutputFileName, checkeroutputFileName ) self.convertCheckerResults( localcheckeroutput, checkerResults ) if res.hasCheckerErrors(): needSnapshot = True if self._createErrorSnapshots and needSnapshot: snapshot = 'error-{0}-{1}'.format( datetime.datetime.now().strftime( '%Y%m%d_%H%M%S' ), utils.randomString( 4 ) ) status, _ = vm.createSnapshot( snapshot ) if status == 0: res.setErrorSnapshot( snapshot ) else: res.addInternalError( 'Could not create error snapshot "{0}"'.format( snapshot ) ) res.addStepResult( result.StepResult( instR, checkerResults ) ) #TODO handle timeouts? finally: vm.kill() except e: print( e ) res.addInternalError( str( e ) ) tests/test-framework/vmware/ftpsource.py000066400000000000000000000123261325366651500210770ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# from ftplib import FTP import datetime, functools, os, time, tempfile from source import Installer import utils def _timestampFromFilename( platform, fn ): # windows: assumed format is YYYY_mm_dd_HH_MM as prefix # linux: assumed format is YYYY-mm-dd as suffix if platform.startswith( 'windows' ): format = '%Y_%m_%d_%H_%M' length = len( 'YYYY_mm_dd_HH_MM' ) return datetime.datetime.strptime( fn[0:length], format ) else: format = '%Y-%m-%d' length = len( 'YYYY-mm-dd' ) return datetime.datetime.strptime( fn[-length:], format ) def timestampFromFilename( platform, fn ): try: return _timestampFromFilename( platform, fn ) except ValueError: return None def filesNewerThan( files, dt ): if dt: return filter( lambda (x,y): y >= dt, files ) else: return files #internal to FtpSource class Location: def __init__( self, host, path, platform ): self.host = host self.path = path self.platform = platform self.testedFiles = [] def ls( self ): ftp = FTP( self.host ) ftp.login() try: return map( utils.basename, ftp.nlst( self.path ) ) finally: ftp.close() def filesSortedByTimestamp( self ): files = self.ls() withTS = [( i, timestampFromFilename( self.platform, i ) ) for i in files] filtered = filter( lambda (x,y): y != None, withTS ) filtered.sort( key=lambda (x,y): y ) return filtered def untestedFilesSortedByTimestamp( self ): l = self.filesSortedByTimestamp() return filter( lambda ( x, y ): not self.isTested( x ), l ) def markAsTested( self, filename ): self.testedFiles.append( filename ) def isTested( self, filename ): return filename in self.testedFiles def description( self ): return "host={0} path={1} platform={2}".format( self.host, self.path, self.platform ) def download( self, fn, target ): ftp = FTP( self.host ) ftp.login() try: ftp.retrbinary( 'RETR {0}/{1}'.format( self.path, fn ), target.write ) finally: ftp.close() class FtpSource: def __init__( self, delay=60*60, tempdir='/tmp' ): self._locations = [] self._delay = delay self._tempdir = tempdir self._startDate = None def setStartDate( self, date ): self._startDate = date def addLocation( self, host, path, platform ): self._locations.append( Location( host, path, platform ) ) def nextInstaller( self ): while True: for i in self._locations: print( "** Checking FTP location: " + i.description() ) files = i.untestedFilesSortedByTimestamp() if self._startDate != None: files = filesNewerThan( files, self._startDate ) if len( files ) == 0: continue; fn, ts = files[0] print( "** Downloading new installer: {0}...".format( fn ) ) tmp = tempfile.NamedTemporaryFile( dir=self._tempdir, prefix=fn ) i.download( fn, tmp ) print( "** Download completed. ({0})".format( tmp.name ) ) i.lastTested = ts inst = Installer( tmp.name, i.platform, i, ts, tmp ) inst.sourceFilename = fn return inst print( "** No installers found. Going to sleep for {0} seconds...".format( self._delay ) ) time.sleep( self._delay ) if __name__ == "__main__": src = FtpSource() src.addLocation( "hegel", "/projects/ndk/installers/windows", "windows" ) while True: inst = src.nextInstaller() print inst inst.markAsTested() tests/test-framework/vmware/guest.py000066400000000000000000000135211325366651500202120ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# # -*- coding: utf-8 -*- import sys, subprocess, ConfigParser, platform, os # begin Autobuild/helpers/runcommand.py import os import re import time import sys import signal from subprocess import Popen from threading import Thread from subprocess import PIPE import subprocess import platform def DebugN( l, m ): pass def windowskill( pid ): """ replace os.kill on Windows, where it is not available""" Cmd = 'taskkill /PID ' + str( int( pid ) ) + ' /T /F' if os.system( Cmd ) == 0: DebugN( 4, 'windowskill: process ' + str( pid ) + ' killed.' ) def kill( pid, signal ): if 'Windows' in platform.platform(): windowskill( pid ) else: os.kill( pid, signal ) class CommandRunner( Thread ): def __init__ ( self, Cmd ): Thread.__init__( self ) self.__started = None self.mCmd = Cmd self.mOutput = () self.mPid = -1 self.mReturnCode = -1 self.mError = () self.__combinedOutput = False def setCombinedOutput(self, combine): if combine: # make sure combine is usable as a boolean self.__combinedOutput = True else: self.__combinedOutput = False def getCombineOutput(self): return self.__combinedOutput def run( self ): DebugN( 4, 'RunCommand: ' + str( self.mCmd ) ) stderrValue = subprocess.PIPE if self.__combinedOutput: stderrValue = subprocess.STDOUT self.__started = True #nokia-sdk: changed to shell=False, as shell=True requires manual quoting of args p = Popen ( self.mCmd, shell = False, stdout=subprocess.PIPE, stderr=stderrValue ) self.mPid = p.pid self.mOutput, self.mError = p.communicate() self.mReturnCode = p.returncode DebugN( 4, 'ReturnCode of RunCommand: ' + str( p.returncode ) ) def started(self): return self.__started def output( self ): return self.mOutput, self.mError def terminate( self ): # FIXME logic? if self.mPid != -1: kill( self.mPid, signal.SIGTERM ) if self.mPid == True: self.join( 5 ) kill( self.mPid, signal.SIGKILL ) self.mPid = -1 def RunCommand( Cmd, TimeOutSeconds=-1, CombineOutput = False ): timeoutString = ' without a timeout' if TimeOutSeconds > 0: timeoutString = ' with timeout of ' + str( TimeOutSeconds ) combinedOutputString = ' and separate output for stdout and stderr' if CombineOutput: combinedOutputString = ' and combined stdout and stderr output' DebugN ( 3, 'RunCommand: executing ' + str( Cmd ) + timeoutString + combinedOutputString ) runner = CommandRunner ( Cmd ) runner.setCombinedOutput( CombineOutput ) runner.start() # poor man's mutex: while not runner.started(): time.sleep( 0.1 ) # if "CYGWIN" in platform.platform() or 'Windows' in platform.platform(): # time.sleep( 1 ) if TimeOutSeconds == -1: runner.join() else: runner.join( TimeOutSeconds ) if runner.isAlive(): runner.terminate() # if "CYGWIN" in platform.platform() or 'Windows' in platform.platform(): # time.sleep(1) runner.join( 5 ) DebugN( 3, 'RunCommand: command timed out, returncode is ' + str( runner.mReturnCode ) ) return ( runner.mReturnCode, runner.output(), True ) else: DebugN( 3, 'RunCommand: command completed, returncode is ' + str( runner.mReturnCode ) ) return ( runner.mReturnCode, runner.output(), False ) # end Autobuild/helpers/runcommand.py def printUsage(): print( "Usage: {0} [*]".format( sys.argv[0] ) ) if len( sys.argv ) < 4: printUsage() sys.exit( 1 ) output = sys.argv[1] timeout = int( sys.argv[2] ) cmd = sys.argv[3:] start = time.clock() exitCode, pout, timedOut = RunCommand( cmd, timeout ) end = time.clock() if timedOut: exitStatus = 'Timeout' else: exitStatus = 'Normal' #TODO: detect crash config = ConfigParser.SafeConfigParser() config.add_section( 'Result' ) config.set( 'Result', 'ExitCode', str( exitCode ) ) config.set( 'Result', 'ExitStatus', exitStatus ) config.set( 'Result', 'Filename', cmd[0] ) config.set( 'Result', 'Arguments', " ".join( cmd[1:] ) ) config.set( 'Result', 'Timeout', str( timeout ) ) config.set( 'Result', 'ExecutionTime', str( end - start ) ) config.set( 'Result', 'Stdout', pout[0] ) config.set( 'Result', 'Stderr', pout[1] ) config.add_section( 'Platform' ) config.set( 'Platform', 'system', platform.system() ) config.set( 'Platform', 'release', platform.release() ) config.set( 'Platform', 'version', platform.version() ) config.set( 'Platform', 'machine', platform.machine() ) with open( output, 'w' ) as configFile: config.write(configFile ) sys.exit( exitCode ) tests/test-framework/vmware/guestconfig.py000066400000000000000000000034321325366651500214000ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import os, ConfigParser, tempfile, platform config = ConfigParser.SafeConfigParser() config.add_section('Guest') config.set('Guest', 'temp_dir', str(tempfile.gettempdir())) config.set('Guest', 'path_sep', os.sep) config.set('Guest', 'system', platform.system()) config.set('Guest', 'release', platform.release()) config.set('Guest', 'version', platform.version()) config.set('Guest', 'machine', platform.machine()) with open('example.cfg', 'wb') as configFile: config.write(configFile) tests/test-framework/vmware/reporter.py000066400000000000000000000121541325366651500207260ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import datetime, socket, sys, traceback from xml.sax.saxutils import XMLGenerator from xml.sax.xmlreader import AttributesNSImpl import result from result import exitStatusAsString from xmlutils import startElement, endElement, writeElement class Reporter: def __init__( self ): pass def reportException( self ): sys.stderr.write( traceback.format_exc( 15 ) + '\n' ) def reportResult( self, result ): try: self.toXml( result, sys.stdout ) finally: sys.stdout.flush() def toXml( self, result, out ): atom = "http://www.w3.org/2005/Atom" tf = "http://sdk.nokia.com/test-framework/ns/1.0" gen = XMLGenerator( out, 'utf-8' ) gen.startDocument() gen.startPrefixMapping( 'atom', atom) gen.startPrefixMapping( 'tf', tf ) startElement( gen, atom, 'entry' ) writeElement( gen, atom, 'title', result.constructTitle() ) writeElement( gen, atom, 'updated', datetime.datetime.now().isoformat() ) writeElement( gen, tf, 'errorSummary', exitStatusAsString( result.status() ) ) writeElement( gen, tf, 'host', socket.gethostname() ) if result._testStart != None: writeElement( gen, tf, 'testStart', result._testStart.isoformat() ) else: result._internalErrors.append( "Result generator: no start timestamp found." ) if result._testEnd != None: writeElement( gen, tf, 'testEnd', result._testEnd.isoformat() ) else: result._internalErrors.append( "Result generator: no end timestamp found." ) startElement( gen, tf, 'installer' ) writeElement( gen, tf, 'sourceUrl', result._installerSourceLocation ) writeElement( gen, tf, 'platform', result._installerTargetPlatform ) #TODO revision endElement( gen, tf, 'installer' ) if result._testcase != None: startElement( gen, tf, 'testCase' ) writeElement( gen, tf, 'name', result._testcase.name() ) writeElement( gen, tf, 'path', result._testcase.path() ) writeElement( gen, tf, 'installScript', result._testcase.installscript() ) endElement( gen, tf, 'testCase' ) else: result._internalErrors.append( "Result generator: No test case given." ) if result._installationResult != None: startElement( gen, tf, 'installationResult' ) writeElement( gen, tf, 'exitCode', str( result._installationResult.exitCode ) ) writeElement( gen, tf, 'exitStatus', exitStatusAsString( result._installationResult.exitStatus ) ) endElement( gen, tf, 'installationResult' ) else: result._internalErrors.append( "Result generator: No installation result given." ) startElement( gen, tf, 'checkerResult' ) for err in result._checkerErrors: writeElement( gen, tf, 'error', err ) endElement( gen, tf, 'checkerResult' ) startElement( gen, tf, 'virtualMachine' ) writeElement( gen, tf, 'path', result._vm.vmxPath() ) writeElement( gen, tf, 'platform', result._vm.ostype() ) writeElement( gen, tf, 'snapshot', result._vm.snapshot() ) endElement( gen, tf, 'virtualMachine' ) startElement( gen, tf, 'internalErrors' ) for i in result._internalErrors: writeElement( gen, tf, 'internalError', str( i ) ) endElement( gen, tf, 'internalErrors' ) if result._errorSnapshot != None: writeElement( gen, tf, 'errorSnapshot', result._errorSnapshot ) endElement( gen, atom, 'entry' ) gen.endDocument() tests/test-framework/vmware/result.py000066400000000000000000000142141325366651500204010ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import control, testcase, datetime, source, tempfile, traceback, utils from source import Installer class ExecutionResult: #return codes: Success = 0 InstallationFailed = 1 InstallationCanceled = 2 #exit status: Normal = 0 Crash = 1 Timeout = 2 def __init__( self, exitCode, exitStatus, executionTime ): self.exitCode = exitCode self.exitStatus = exitStatus self.executionTime = executionTime def hasError( self ): return self.exitCode != 0 or self.exitStatus != ExecutionResult.Normal class CheckerResult: Passed = 0 Failed = 1 NotRun = 2 def hasError( list ): #the following would more efficient with something find_if-like (stopping if test with error is found) return len( [x for i in list if i.hasError() ] ) > 0 def __init__( self, name, result, errorString ): self.name = utils.randomString( 3 ) + '_' + name self.result = result self.errorString = errorString def hasError( self ): return self.result != CheckerResult.Passed class StepResult: def __init__( self, executionResult, checkerResults ): self.executionResult = executionResult self.checkerResults = checkerResults def hasCheckerErrors( self ): return CheckerResult.hasError( self.checkerResults ) def exitStatusFromString( s ): if s == 'Crash': return ExecutionResult.Crash if s == 'Timeout': return ExecutionResult.Timeout if s == 'Normal': return ExecutionResult.Normal raise control.ControlException( "Unknown exit status string: {0}".format( s ) ) #there is probably a cooler way with introspection and stuff: def exitStatusAsString( status ): if status == ExecutionResult.Crash: return 'Crash' if status == ExecutionResult.Normal: return 'Normal' if status == ExecutionResult.Timeout: return 'Timeout' raise control.ControlException( "Unknown exit status: {0}".format( status ) ) class Result: NoError=0 # Everything ok InstallerError=1 #Installer failed, or post-conditions not met InternalError=2 #Internal test framework error def __init__( self ): self._internalErrors = [] self._stepResults = [] self._installer = None self._testStart = None self._testEnd = None self._testcase = None self._vm = None self._errorSnapshot = None self._revision = "revision-todo" def setInstaller( self, installer ): self._installer = installer def testStarted( self ): self._testStart = datetime.datetime.now() def testFinished( self ): self._testEnd = datetime.datetime.now() def setTestCase( self, testcase ): self._testcase = testcase def setErrorSnapshot( self, name ): self._errorSnapshot = name def setVirtualMachine( self, vm ): self._vm = vm def addInternalError( self, errstr ): self._internalErrors.append( errstr ) def hasInternalErrors( self ): return len( self._internalErrors ) > 0 def addException( self ): s = 'Unexpected exception: {0}'.format( traceback.format_exc( 15 ) ) self._internalErrors.append( s ) def hasCheckerErrors( self ): for step in self._stepResults: if CheckerResult.hasError( step.checkerResults ): return True return False def addStepResult( self, result ): self._stepResults.append( result ) def status( self ): if len( self._internalErrors ) > 0: return Result.InternalError if len( self._stepResults ) == 0: return Result.InternalError for step in self._stepResults: if step.executionResult.exitCode != ExecutionResult.Success or step.exitStatus != ExecutionResult.Normal: return Result.InstallerError if step.hasCheckerErrors(): return Result.InstallerError #TODO: check test results return Result.NoError def statusAsNiceString( self ): s = self.status() if s == Result.InternalError: return "Internal framework errors" if s == Result.InstallerError: return "Installation error" return "OK" def constructTitle( self ): smiley = ":-)" if self.status() == Result.NoError else ":-(" return "{0}: {1} on {2} testing {3} - {4} {5}".format( self._revision, utils.basename( self._installerSourceLocation ), self._vm.name(), self._testcase.name(), self.statusAsNiceString(), smiley ) tests/test-framework/vmware/run-test.py000066400000000000000000000137641325366651500206550ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import sys, os, ConfigParser, optparse, time, virtualmachine, utils # TODO: - Look in sensible locations for python in the guest VM # - Try and work-around VMware Fusion needing to be closed def check_option( option, optionName ): if option: return print("** Could not find a value for {0}".format(optionName) ) print("** Please specify it on the commandline or configuration file.") print optionParser.print_help(); sys.exit(-1) def die(message): print "** " + str(message) sys.exit(1); # # Parse options # optionParser = optparse.OptionParser(usage="%prog [options] vmx-file", version="%prog 0.9") optionParser.add_option("-u", "--username", dest="username", help="username for VM", metavar="USERNAME" ) optionParser.add_option("-p", "--password", dest="password", help="password for VM", metavar="PASSWORD" ) optionParser.add_option("-S", "--script", dest="script", help="script for VM", metavar="SCRIPT" ) optionParser.add_option("-s", "--snapshot", dest="snapshot", help="snapshot for VM", metavar="SNAPSHOT" ) optionParser.add_option("-P", "--python", dest="python", help="python location in VM", metavar="PYTHON" ) optionParser.add_option("-v", "--vmrun", dest="vmrun", help="vmrun command for VM", metavar="VMRUN") optionParser.add_option("-c", "--config", dest="config", help="configuration file for VM", metavar="CONFIG") optionParser.add_option("-i", "--installer", dest="installer", help="installer executable to command inside the VM", metavar="INSTALLER") optionParser.add_option("-e", "--installscript", dest="installscript", help="QtScript script to use for non-interactive installation", metavar="INSTALLSCRIPT") (options, args) = optionParser.parse_args() try: options.vmx = args[0] except IndexError: options.vmx = None options.ostype = None options.guestTempDir = None config = ConfigParser.SafeConfigParser() if options.config: print("** Reading config: " + options.config ) config.read( options.config ) else: print("** No config given") options.username = utils.get_config_option( config, options.username, "username", "nokia" ) options.password = utils.get_config_option( config, options.password, "password", "nokia" ) options.script = utils.get_config_option( config, options.script, "script", "guest.py" ) options.python = utils.get_config_option( config, options.python, "python", "c:/python26/python.exe" ) options.snapshot = utils.get_config_option( config, options.snapshot, "snapshot", "base" ) options.vmrun = utils.get_config_option( config, options.vmrun, "vmrun" ) options.vmx = utils.get_config_option( config, options.vmx, "vmx" ) options.guestTempDir = utils.get_config_option( config, options.guestTempDir, "tempDir", "c:\\windows\\temp" ) options.ostype = utils.get_config_option( config, options.ostype, "os", "windows" ) check_option( options.vmx, "vmx-file" ) # Search the PATH and the a few extra locations for 'vmrun' if not options.vmrun: options.vmrun = utils.findVMRun() check_option( options.vmrun, "VMRUN" ) check_option( options.installer, "INSTALLER" ) check_option( options.installscript, "INSTALLSCRIPT" ) # # VM actions # vm = virtualmachine.VirtualMachine( options.vmrun, options.vmx, options.username, options.password, options.guestTempDir, options.ostype ) snapshotExists = vm.snapshotExists( options.snapshot ) if not snapshotExists: die("Could not find '{0}' snapshot, please create it in the VM.".format( options.snapshot ) ) revertStatus, _ = vm.revertToSnapshot( options.snapshot ) if revertStatus != 0: die("Failed to revert to snapshot") time.sleep( 5 ) # Trying to avoid a possible race between restore and start vm.start() try: pythonStatus, _ = vm.command("Checking for guest installed Python", "fileExistsInGuest", options.python) if pythonStatus != 0: raise virtualmachine.VMException("Please install Python in the VM from http://www.python.org/download/") wrapperpath = vm.copyToTemp( 'guest.py' ) installerpath = vm.copyToTemp( options.installer ) scriptpath = vm.copyToTemp( options.installscript ) outputpath = vm.mkTempPath( 'output.log' ) vm.command( 'Execute installer', "runProgramInGuest", "-interactive -activeWindow '{0}' '{1}' '{2}' '{3}' --script '{4}'".format( options.python, wrapperpath, outputpath, installerpath, scriptpath ) ) vm.copyFromTemp( 'output.log', 'output.log' ) result = ConfigParser.SafeConfigParser() result.read( 'output.log' ) print( "Installer exit code: " + result.get( 'Result', 'ExitCode' ) ) #TODO parse output and do something with it except virtualmachine.VMException as exception: die( exception ) finally: vm.kill() tests/test-framework/vmware/run.py000066400000000000000000000135621325366651500176740ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# # -*- coding: utf-8 -*- import ConfigParser, datetime, optparse, os, sys from functools import partial import cdashreporter, control, ftpsource, source, reporter, utils def die( msg ): sys.stderr( msg + '\n' ) sys.exit( 1 ) optionParser = optparse.OptionParser(usage="%prog [options] configfile installer0 [installer1 ...]", version="%prog 0.1") optionParser.add_option("-r", "--vmrun", dest="vmrun", help="vmrun executable to use", metavar="VMRUN" ) optionParser.add_option("-s", "--only-since", dest="since", help="test only installers newer than timestamp (YYYY-MM-DD or YYYY-MM-DD-hh-mm)", metavar="SINCE" ) optionParser.add_option("-c", "--checkerInstallation", dest="checkerInstallation", help="checker installation to use for post-installation checks", metavar="CHECKERINSTALLATION" ) (options, args) = optionParser.parse_args() try: configpath = utils.makeAbsolutePath( args[0], os.getcwd() ) except IndexError: optionParser.print_usage( sys.stderr ) sys.exit( 1 ) config = ConfigParser.SafeConfigParser() config.read( configpath ) #make an unary functor to create absolute paths abspath = partial( utils.makeAbsolutePath, relativeTo=os.path.dirname( configpath ) ) vmrun = utils.get_config_option( config, options.vmrun, "vmrun", utils.findVMRun() ) useGui = utils.get_config_option( config, None, "gui", "true" ).lower() == "true" createErrorSnapshots = utils.get_config_option( config, None, "createErrorSnapshots", "true" ).lower() == "true" hostType = utils.get_config_option( config, None, "type", "", "Host" ) hostLocation = utils.get_config_option( config, None, "location", "", "Host" ) hostUsername = utils.get_config_option( config, None, "username", "", "Host" ) hostPassword = utils.get_config_option( config, None, "password", "", "Host" ) cdashHost = utils.get_config_option( config, None, "host", "", "CDash" ) cdashLocation = utils.get_config_option( config, None, "location", "", "CDash" ) cdashProject = utils.get_config_option( config, None, "project", "", "CDash" ) if vmrun == None: die( "Could not find vmrun executable. Please specify it in the config file (vmrun=...) or via the --vmrun option" ) checkerInstallation = utils.get_config_option( config, options.checkerInstallation, "checkerInstallation" ) if checkerInstallation == None: die( "Could not find checker installation. Please specify it in the config file (checkerInstallation=...) or via the --checkerInstallation option" ) #apply functor to list to get absolute paths: testcases = map( abspath, utils.get_enumerated_config_option( config, 'testcase' ) ) if len( testcases ) == 0: die( "No testcases specified. Please specify at least one test case in the configuration" ) vms = map( abspath, utils.get_enumerated_config_option( config, 'vm' ) ) if len( vms ) == 0: die( "No VMs specified. Please specify at least one VM in the configuration" ) installers = args[1:] if len( installers ) > 0: source = source.Source() for i in installers: source.addDummy( 5, i ) else: source = ftpsource.FtpSource() if options.since: try: sdt = datetime.datetime.strptime( options.since, '%Y-%m-%d' ) except ValueError: sdt = datetime.datetime.strptime( options.since, '%Y-%m-%d-%H-%M' ) source.setStartDate( sdt ) found = True nextsec = 0 while found: sec = "Source{0}".format( nextsec ) nextsec += 1 try: host = config.get( sec, "host" ) path = config.get( sec, "path" ) platform = config.get( sec, "platform" ) print( "** Add FTP location {0}:{1} ({2})".format( host, path, platform ) ) source.addLocation( host, path, platform ) except ConfigParser.NoSectionError: found = False cdashHost = utils.get_config_option( config, None, "host", "", "CDash" ) cdashLocation = utils.get_config_option( config, None, "location", "", "CDash" ) cdashProject = utils.get_config_option( config, None, "project", "", "CDash" ) if len( cdashHost ) > 0: reporter = cdashreporter.CDashReporter( cdashHost, cdashLocation, cdashProject ) else: reporter = reporter.Reporter() control = control.Control( vmrun, checkerInstallation, source, reporter ) control.setGuiEnabled( useGui ) control.setCreateErrorSnapshots( createErrorSnapshots ) if len( hostType ) > 0: control.setRemoteHost( hostType, hostLocation, hostUsername, hostPassword ) for i in vms: control.addVM( i ) for i in testcases: control.addTestCase( i ) control.run() tests/test-framework/vmware/source.py000066400000000000000000000046341325366651500203700ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import os, time class Installer: def __init__( self, path, platform, location, timestamp=None, tempfile=None ): self.path = path self.platform = platform self.error = None self.timestamp = timestamp self.tempfile = tempfile self.sourceLocation = location def markAsTested( self ): self.sourceLocation.markAsTested( self.sourceFilename ) class Source: def __init__( self ): self._dummies = [] def nextInstaller( self ): if len( self._dummies ) == 0: return None delay, path = self._dummies.pop() time.sleep( delay ) if os.path.exists( path ): inst = Installer( path, "linux", None ) inst.sourceFilename = path return inst else: inst = Installer( None, None ) #simulating download errors inst.error = "Installer '{0}' does not exist".format( path ) return inst def addDummy( self, delay, path ): self._dummies.insert( 0, ( delay, path ) ) tests/test-framework/vmware/testcase.py000066400000000000000000000073721325366651500207050ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import ConfigParser, os, utils class Step: def __init__( self, installscript, checkerTestDir, timeout ): self._installscript = installscript self._checkerTestDir = checkerTestDir self._timeout = timeout def installscript( self ): return self._installscript def checkerTestDir( self ): return self._checkerTestDir def timeout( self ): return self._timeout class TestCase: def __init__( self, path ): self._steps = [] config = ConfigParser.SafeConfigParser() config.read( path ) self._path = path self._platforms = [] found = True stepNum = 0 while found: sec = "Step{0}".format( stepNum ) if not config.has_section( sec ): found = False continue stepNum += 1 installscript = utils.makeAbsolutePath( utils.get_config_option( config, None, "installscript", None, sec ), os.path.dirname( path ) ) checkerTestDir = utils.get_config_option( config, None, "checkerTestDir", None, sec ) checkerTestDir = utils.makeAbsolutePath( checkerTestDir, os.path.dirname( path ) ) if checkerTestDir else "" timeout = int( utils.get_config_option( config, None, "timeout", 60 * 60, sec ) ) self._steps.append( Step( installscript, checkerTestDir, timeout ) ) self._name = utils.get_config_option( config, None, "name", utils.basename( path ) ) self._targetDirectory = utils.get_config_option( config, None, "targetDirectory", "" ) self._maintenanceToolLocation = utils.get_config_option( config, None, "maintenanceToolLocation", "" ) platforms = utils.get_config_option( config, None, "platforms" ) if platforms != None: self._platforms = platforms.split( ',' ) def supportsPlatform( self, platform ): return platform in self._platforms def installerTimeout( self ): return self._installerTimeout def platforms( self ): return self._platforms def name( self ): return self._name def targetDirectory( self ): return self._targetDirectory def steps( self ): return self._steps def path( self ): return self._path def maintenanceToolLocation( self ): return self._maintenanceToolLocation tests/test-framework/vmware/utils.py000066400000000000000000000064171325366651500202310ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import ConfigParser, inspect, os, string, sys from random import Random def findVMRun(): searchDirectories = os.environ['PATH'].split(os.pathsep) searchDirectories.append("/Library/Application Support/VMware Fusion") for directory in searchDirectories: possibleFile = os.path.join(directory, 'vmrun') if os.path.isfile(possibleFile): return possibleFile return None def basename( path ): if path.endswith( os.path.sep ): return os.path.basename( path[0,-1] ) else: return os.path.basename( path ) def makeAbsolutePath( path, relativeTo ): if os.path.isabs( path ) or relativeTo == None: return path else: return relativeTo + os.sep + path def execution_path( filename ): return os.path.join( os.path.dirname( inspect.getfile( sys._getframe( 1 ) ) ), filename ) def unixPathSep( s ): return s.replace( '\\', '/' ) def get_config_option( config, initial, option, default=None, section="General" ): # If there's already a valid option from the commandline, # override the value from the configuration file. if initial != None: print( "** Overridden {0}={1}".format( option, initial ) ) return initial try: tmp = config.get( section, option ) print( "** Read config value {0}={1}".format( option, tmp ) ) return tmp except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): print( "** Using default value {0}={1}".format( option, default ) ) return default def get_enumerated_config_option( config, option ): res = [] i = 0 while True: key = option + str( i ) val = get_config_option( config, None, key ) if val == None: print res return res res.append( val ) i += 1 def randomString( length ): return ''.join( Random().sample( string.letters + string.digits, length ) ) tests/test-framework/vmware/virtualmachine.py000066400000000000000000000201621325366651500220750ustar00rootroot00000000000000# -*- coding: utf-8 -*- #!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import commands, ConfigParser, os, string, utils, platform class VMException(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class VirtualMachine: def __init__( self, vmrun, vmx, username, password, tempDir, ostype ): self._vmrun = vmrun self._vmx = vmx self._username = username self._password = password self._tempDir = tempDir self._ostype = ostype self._useGui = True self._name = "no name" self._hostType = "" self._hostLocation = None self._hostUsername = None self._hostPassword = None def setGuiEnabled( self, usegui ): self._useGui = usegui def name( self ): return self._name def isRemote( self ): return len( self._hostType ) > 0; def setRemoteHost( self, type, loc, user, pw ): self._hostType = type self._hostLocation = loc self._hostUsername = user self._hostPassword = pw def command( self, message, command, parameters=None ): if len( self._hostType ) > 0: remote = "-T {0} -h '{1}' -u {2} -p {3}".format( self._hostType, self._hostLocation, self._hostUsername, self._hostPassword ) else: remote = "" vmcommand = "'{0}' {1} -gu {2} -gp {3} {4} '{5}' ".format( self._vmrun, remote, self._username, self._password, command, self._vmx ) if parameters: vmcommand += parameters print "** " + message status, output = commands.getstatusoutput(vmcommand) if status != 0: print "* Return code: {0}".format(status) print "* Output:" print output print return status, output def copyFileToGuest( self, source, target ): #TODO convert paths to guest system? status, _ = self.command( 'Copying {0} to {1}'.format( source, target ), 'copyFileFromHostToGuest', "'{0}' '{1}'".format( source, target ) ) if status != 0: raise VMException( "Could not copy from host to guest: source {0} target {1}".format( source, target ) ) def copyFileFromGuest( self, source, target ): #TODO convert paths to guest system? status, _ = self.command( 'Copying {0} to {1}'.format( source, target ), 'copyFileFromGuestToHost', "'{0}' '{1}'".format( source, target ) ) if status != 0: raise VMException( "Could not copy from guest to host: source {0} target {1}".format( source, target ) ) def ostype( self ): return self._ostype def vmxPath( self ): return self._vmx def mkTempPath( self, filename ): return self._tempDir + self.pathSep() + utils.basename( filename ) def pathSep( self ): if self._ostype == "windows": return "\\" else: return "/" def copyToTemp( self, source, targetN=None ): if source == None or len( string.strip( source ) ) == 0: return None if targetN == None: targetN = source target = self.mkTempPath( targetN ) self.copyFileToGuest( source, target ) return target def copyFromTemp( self, filename, target ): source = self.mkTempPath( filename ) self.copyFileFromGuest( source, target ) return target def start( self ): arg = "gui" if self._useGui else "nogui" self.command("Starting VM ({0})".format( arg ), "start", arg ) def kill( self ): self.command("Stopping VM", "stop", "hard" ) def isRunning( self ): _, vmList = self.command("Checking running VMs", "list") _, _, vmFilename = self._vmx.rpartition( os.sep ) return vmList.find(vmFilename) is not -1 def snapshotExists( self, snapshot ): _, output = self.command("Checking snapshots", "listSnapshots") snapshotList = output.split("\n") # Remove first entry which contains number of snapshots snapshotList.pop(0) return snapshotList.count( snapshot ) > 0 def checkPythonInstalled( self ): pp = utils.unixPathSep( self._python ) pythonStatus, _ = self.command("Checking for guest installed Python", "fileExistsInGuest", pp ) print pythonStatus if pythonStatus != 0: raise VMException("Could not find python in {0}: Please specify the path/install Python in the VM from http://www.python.org/download/".format( pp ) ) else: print("Python found ({0})".format( pp ) ) def python( self ): return self._python def snapshot( self ): return self._snapshot def revertToSnapshot( self, snapshot=None ): if snapshot != None: snap = snapshot else: snap = self._snapshot # VMware Fusion needs to be closed before you can restore snapshots so kill it if ( platform.system() == "Darwin" ): commands.getstatusoutput( "ps x|grep 'VMware Fusion'|cut -d ' ' -f1|xargs kill" ) return self.command("Reverting to '{0}' snapshot".format( snap ), "revertToSnapshot", snap ) def createSnapshot( self, name ): return self.command("Creating error snapshot '{0}'".format( name ), "snapshot", name ) def fromVMRunAndPath( vmrun, path ): config = ConfigParser.SafeConfigParser() config.read( path ) hostType = utils.get_config_option( config, None, "type", "", "Host" ) hostLocation = utils.get_config_option( config, None, "location", "", "Host" ) hostUsername = utils.get_config_option( config, None, "username", "", "Host" ) hostPassword = utils.get_config_option( config, None, "password", "", "Host" ) vmxVal = utils.get_config_option( config, None, "vmx" ) if not len( hostType ) == 0: vmx = utils.makeAbsolutePath( vmxVal, os.path.dirname( path ) ) else: vmx = vmxVal username = utils.get_config_option( config, None, "username", "nokia" ) password = utils.get_config_option( config, None, "password", "nokia" ) tempDir = utils.get_config_option( config, None, "tempDir", "c:\\windows\\temp" ) ostype = utils.get_config_option( config, None, "os", "windows" ) vm = VirtualMachine( vmrun, vmx, username, password, tempDir, ostype ) vm._name = utils.get_config_option( config, None, "name", utils.basename( path ) ) vm._snapshot = utils.get_config_option( config, None, "snapshot", "base" ) vm._python = utils.get_config_option( config, None, "python", "c:/python26/python.exe" ) vm._hostType = hostType vm._hostLocation = hostLocation vm._hostUsername = hostUsername vm._hostPassword = hostPassword return vm tests/test-framework/vmware/xmlutils.py000066400000000000000000000033061325366651500207440ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# def startElement( gen, ns, name, attrsb={} ): attrs = {} for i in attrsb: attrs[(None, i)] = attrsb[i] gen.startElementNS( ( ns, name ), name, attrs ) def endElement( gen, ns, name ): gen.endElementNS( ( ns, name ), name ) def writeElement( gen, ns, name, text, attrs={} ): startElement( gen, ns, name, attrs ) gen.characters( text ) endElement( gen, ns, name ) tests/test-installer/000077500000000000000000000000001325366651500152065ustar00rootroot00000000000000tests/test-installer/BatchSubstitute.bat000066400000000000000000000037351325366651500210230ustar00rootroot00000000000000::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: :: Copyright (C) 2017 The Qt Company Ltd. :: Contact: http://www.qt.io/licensing/ :: :: This file is part of the Qt Installer Framework. :: $QT_BEGIN_LICENSE:GPL-EXCEPT$ :: Commercial License Usage :: Licensees holding valid commercial Qt licenses may use this file in :: accordance with the commercial license agreement provided with the :: Software or, alternatively, in accordance with the terms contained in :: a written agreement between you and The Qt Company. For licensing terms :: and conditions see https://www.qt.io/terms-conditions. For further :: information use the contact form at https://www.qt.io/contact-us. :: :: GNU General Public License Usage :: Alternatively, this file may be used under the terms of the GNU :: General Public License version 3 as published by the Free Software :: Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT :: included in the packaging of this file. Please review the following :: information to ensure the GNU General Public License requirements will :: be met: https://www.gnu.org/licenses/gpl-3.0.html. :: :: $QT_END_LICENSE$ :: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: @echo off REM -- Prepare the Command Processor -- SETLOCAL ENABLEEXTENSIONS SETLOCAL DISABLEDELAYEDEXPANSION ::BatchSubstitude - parses a File line by line and replaces a substring" ::syntax: BatchSubstitude.bat OldStr NewStr File :: OldStr [in] - string to be replaced :: NewStr [in] - string to replace with :: File [in] - file to be parsed :$changed 20100115 :$source http://www.dostips.com if "%~1"=="" findstr "^::" "%~f0"&GOTO:EOF for /f "tokens=1,* delims=]" %%A in ('"type %3|find /n /v """') do ( set "line=%%B" if defined line ( call set "line=echo.%%line:%~1=%~2%%" for /f "delims=" %%X in ('"echo."%%line%%""') do %%~X ) ELSE echo. ) tests/test-installer/auto_installations_script.qs000066400000000000000000000073121325366651500230560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ var installerTargetDirectory="c:\\auto-test-installation"; function Controller() { installer.autoRejectMessageBoxes; installer.setMessageBoxAutomaticAnswer( "OverwriteTargetDirectory", QMessageBox.Yes); //maybe we want something like this //installer.execute("D:\\cleanup_directory.bat", new Array(installerTargetDirectory)); installer.setMessageBoxAutomaticAnswer( "stopProcessesForUpdates", QMessageBox.Ignore); } Controller.prototype.IntroductionPageCallback = function() { gui.clickButton(buttons.NextButton); } Controller.prototype.TargetDirectoryPageCallback = function() { var page = gui.pageWidgetByObjectName("TargetDirectoryPage"); page.TargetDirectoryLineEdit.setText(installerTargetDirectory); gui.clickButton(buttons.NextButton); } Controller.prototype.ComponentSelectionPageCallback = function() { var page = gui.pageWidgetByObjectName("ComponentSelectionPage"); gui.clickButton(buttons.NextButton); } Controller.prototype.LicenseAgreementPageCallback = function() { var page = gui.pageWidgetByObjectName("LicenseAgreementPage"); page.AcceptLicenseRadioButton.setChecked( true); gui.clickButton(buttons.NextButton); } ////in the current installer we don't have this //Controller.prototype.DynamicQtGuiPageCallback = function() //{ // var page = gui.pageWidgetByObjectName("DynamicQtGuiPage"); // page.checkBoxLib.setChecked( false); // gui.clickButton(buttons.NextButton); //} ////in the current installer we don't have this //Controller.prototype.DynamicErrorPageCallback = function() //{ // var page = gui.pageWidgetByObjectName("DynamicErrorPage"); // page.checkBoxMakeSure.setChecked( true); // gui.clickButton(buttons.NextButton); //} Controller.prototype.StartMenuDirectoryPageCallback = function() { var page = gui.pageWidgetByObjectName("StartMenuDirectoryPage"); //page.LineEdit.text = "test"; gui.clickButton(buttons.NextButton); } Controller.prototype.ReadyForInstallationPageCallback = function() { gui.clickButton(buttons.NextButton); } Controller.prototype.PerformInstallationPageCallback = function() { var page = gui.pageWidgetByObjectName("PerformInstallationPage"); gui.clickButton(buttons.NextButton); } Controller.prototype.FinishedPageCallback = function() { var page = gui.pageWidgetByObjectName("FinishedPage"); gui.clickButton(buttons.FinishButton); } tests/test-installer/create-test-installer.bat000066400000000000000000000105201325366651500221070ustar00rootroot00000000000000::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: :: Copyright (C) 2017 The Qt Company Ltd. :: Contact: http://www.qt.io/licensing/ :: :: This file is part of the Qt Installer Framework. :: $QT_BEGIN_LICENSE:GPL-EXCEPT$ :: Commercial License Usage :: Licensees holding valid commercial Qt licenses may use this file in :: accordance with the commercial license agreement provided with the :: Software or, alternatively, in accordance with the terms contained in :: a written agreement between you and The Qt Company. For licensing terms :: and conditions see https://www.qt.io/terms-conditions. For further :: information use the contact form at https://www.qt.io/contact-us. :: :: GNU General Public License Usage :: Alternatively, this file may be used under the terms of the GNU :: General Public License version 3 as published by the Free Software :: Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT :: included in the packaging of this file. Please review the following :: information to ensure the GNU General Public License requirements will :: be met: https://www.gnu.org/licenses/gpl-3.0.html. :: :: $QT_END_LICENSE$ :: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: @IF "%1" EQU "" ( @set OFFLINE_INSTALLER=true @set ONLINE_INSTALLER=true @set REPOGEN=true @set TEST_ONLINE_INSTALLER=false @set TEST_OFFLINE_INSTALLER=false ) else ( @set OFFLINE_INSTALLER=false @set ONLINE_INSTALLER=false @set REPOGEN=false @set TEST_ONLINE_INSTALLER=false @set TEST_OFFLINE_INSTALLER=false ) @for %%i in (%1,%2,%3,%4,%5,%6,%7,%8,%9) DO ( @IF "%%i" EQU "offline" ( @set OFFLINE_INSTALLER=true ) @IF "%%i" EQU "online" ( @set ONLINE_INSTALLER=true ) @IF "%%i" EQU "repogen" ( @set REPOGEN=true ) @IF "%%i" EQU "test_online" ( @set TEST_ONLINE_INSTALLER=true ) @IF "%%i" EQU "test_offline" ( @set TEST_OFFLINE_INSTALLER=true ) ) @set AUTO_INSTALLATION_SCRIPT=--script %CD%\auto_installations_script.qs @set BINARY_PATH_RELATIVE=%CD%\..\..\bin @pushd . @cd %BINARY_PATH_RELATIVE% @set BINARY_PATH_ABSOLUTE=%CD% @popd @set LOCAL_REPOSITORY=file:///%BINARY_PATH_ABSOLUTE%/repository @set LOCAL_REPOSITORY_PATH=%LOCAL_REPOSITORY:\=/% call BatchSubstitute.bat http://www.xxxx.com/repository %LOCAL_REPOSITORY_PATH% ..\..\examples\testapp\config\config.xml > ..\..\examples\testapp\config\config.xml_new @if %ERRORLEVEL% NEQ 0 goto error_marker copy /Y ..\..\examples\testapp\config\config.xml ..\..\examples\testapp\config\config.xml_old @if %ERRORLEVEL% NEQ 0 goto error_marker move /Y ..\..\examples\testapp\config\config.xml_new ..\..\examples\testapp\config\config.xml @if %ERRORLEVEL% NEQ 0 goto error_marker IF "%OFFLINE_INSTALLER%" EQU "true" ( echo create offline installer ..\..\bin\binarycreator -t ..\..\bin\installerbase.exe -v -p ..\..\examples\testapp\packages -c ..\..\examples\testapp\config\config.xml --offline-only ..\..\bin\test-installer-offline.exe @if %ERRORLEVEL% NEQ 0 goto error_marker ELSE goto done_marker ) IF "%ONLINE_INSTALLER%" EQU "true" ( echo create online installer ..\..\bin\binarycreator -t ..\..\bin\installerbase.exe -v -n -p ..\..\examples\testapp\packages -c ..\..\examples\testapp\config\config.xml ..\..\bin\test-installer-online.exe @if %ERRORLEVEL% NEQ 0 goto error_marker ELSE goto done_marker ) IF "%REPOGEN%" EQU "true" ( echo create online repository @IF exist ..\..\bin\repository rmdir /S /Q ..\..\bin\repository ..\..\bin\repogen.exe -p ..\..\examples\testapp\packages ..\..\bin\repository @if %ERRORLEVEL% NEQ 0 goto error_marker ELSE goto done_marker ) @IF "%TEST_OFFLINE_INSTALLER%" EQU "true" ( ..\..\bin\test-installer-offline.exe --verbose %AUTO_INSTALLATION_SCRIPT% @if %ERRORLEVEL% NEQ 0 goto error_marker ELSE goto done_marker ) @IF "%TEST_ONLINE_INSTALLER%" EQU "true" ( ..\..\bin\test-installer-online.exe --verbose %AUTO_INSTALLATION_SCRIPT% @if %ERRORLEVEL% NEQ 0 goto error_marker ELSE goto done_marker ) copy /Y ..\..\examples\testapp\config\config.xml_old ..\..\examples\testapp\config\config.xml @if %ERRORLEVEL% NEQ 0 goto error_marker goto done_marker :error_marker echo *** Error compiling ifw *** pause :done_marker echo ...done tests/test-installer/create-test-installer.sh000077500000000000000000000027041325366651500217630ustar00rootroot00000000000000############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# ../../bin/binarycreator --offline-only -v -t ../../bin/installerbase -p ../../examples/testapp/packages -c ../../examples/testapp/config/config.xml ../../bin/test-installer-offline tests/testcases/000077500000000000000000000000001325366651500142325ustar00rootroot00000000000000tests/testcases/result-example.xml000066400000000000000000000032061325366651500177240ustar00rootroot00000000000000 ab12cd34ef - installer-20090310.exe - checks failed :-( 2010-03-10T14:00Z test-machine_1.local 2010-03-10T13:00Z 2010-03-10T13:49Z ftp://buildmachine/installers/installer-20090310.exe ab12cd34ef windows /home/testuser/VMs/WindowsXp/WindowsXp.vmx windows base Basic Installation /home/testuser/testcases/basic/basic.cfg /home/testuser/testcases/basic/testscript.qs 0 normal NoError c:\sdk\bin\qmake.exe: md5 sum mismatch: expected: abcd1234 actual: 1234abcd InstallerError InstallerError tests/testcases/testcase1/000077500000000000000000000000001325366651500161265ustar00rootroot00000000000000tests/testcases/testcase1/packagemanagement.qs000066400000000000000000000043621325366651500221300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Controller() { installer.autoRejectMessageBoxes this.componentSelectionCounter = 0 } Controller.prototype.UpdaterSelectedCallback = function() { tabController.setCurrentTab( TabController.PACKAGE_MANAGER ) } Controller.prototype.ComponentSelectionPageCallback = function() { if ( this.componentSelectionCounter == 0 ) { print( "first time, uninstall" ) var page = gui.pageWidgetByObjectName( "ComponentSelectionPage" ) page.deselectComponent( "com.nokia.sdk.doc.qtcreator" ) gui.clickButton( buttons.NextButton, 3000 ) this.componentSelectionCounter += 1 } else { print( "second time, click cancel" ) gui.clickButton( buttons.CancelButton ) } } Controller.prototype.ReadyForInstallationPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.FinishedPageCallback = function() { gui.clickButton( buttons.CommitButton ) } tests/testcases/testcase1/test-uninstall.qs000066400000000000000000000042351325366651500214650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Controller() { installer.autoRejectMessageBoxes } Controller.prototype.IntroductionPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.ComponentSelectionPageCallback = function() { var page = gui.pageWidgetByObjectName( "ComponentSelectionPage" ) page.keepSelectedComponentsRB.setChecked( true ) page.deselectComponent( "com.nokia.sdk.qt.gui" ) page.deselectComponent( "hgrmpfl (non-existing package)" ) // bad case for component lookup page.selectComponent( "hgrmpfl2 (another non-existing package)" ) // bad case for component lookup gui.clickButton( buttons.NextButton ) } Controller.prototype.ReadyForInstallationPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.FinishedPageCallback = function() { gui.clickButton( buttons.FinishButton ) } tests/testcases/testcase1/testcase1.cfg000066400000000000000000000002141325366651500205000ustar00rootroot00000000000000[General] installscript=testscript.qs platforms=windows,linux targetDirectory=c:\Users\kdab\Desktop\testinstall checkerTestDir=checker tests/testcases/testcase1/testscript.qs000066400000000000000000000066161325366651500207100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the FOO module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function Controller() { installer.autoRejectMessageBoxes installer.setMessageBoxAutomaticAnswer( "overwriteTargetDirectory", QMessageBox.Yes ) } Controller.prototype.IntroductionPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.LicenseAgreementPageCallback = function() { var page = gui.pageWidgetByObjectName( "LicenseAgreementPage" ) page.acceptLicenseRB.setChecked( true ) gui.clickButton( buttons.NextButton ) } Controller.prototype.TargetDirectoryPageCallback = function() { var page = gui.pageWidgetByObjectName( "TargetDirectoryPage" ) page.targetDirectoryLE.setText( "c:\\Users\\kdab\\Desktop\\testinstall" ) gui.clickButton( buttons.NextButton ) } Controller.prototype.ComponentSelectionPageCallback = function() { var page = gui.pageWidgetByObjectName( "ComponentSelectionPage" ) page.deselectComponent( "com.nokia.sdk.qtcreator" ) page.deselectComponent( "hgrmpfl (non-existing package)" ) // bad case for component lookup page.selectComponent( "hgrmpfl2 (another non-existing package)" ) // bad case for component lookup gui.clickButton( buttons.NextButton ) } Controller.prototype.DynamicQtGuiPageCallback = function() { var page = gui.pageWidgetByObjectName( "DynamicQtGuiPage" ) page.checkBoxLib.setChecked( false ) gui.clickButton( buttons.NextButton ) } Controller.prototype.DynamicErrorPageCallback = function() { var page = gui.pageWidgetByObjectName( "DynamicErrorPage" ) page.checkBoxMakeSure.setChecked( true ) gui.clickButton( buttons.NextButton ) } Controller.prototype.ReadyForInstallationPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.StartMenuDirectoryPageCallback = function() { gui.clickButton( buttons.NextButton ) } Controller.prototype.PerformInstallationPageCallback = function() { var page = gui.pageWidgetByObjectName( "PerformInstallationPage" ) page.details.button.click } Controller.prototype.FinishedPageCallback = function() { gui.clickButton( buttons.FinishButton ) } tests/testreturn/000077500000000000000000000000001325366651500144535ustar00rootroot00000000000000tests/testreturn/main.cpp000066400000000000000000000044431325366651500161100ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include #include static void crash() { QString* nemesis = 0; nemesis->clear(); } int main( int argc, char** argv ) { std::cout << "Hello." << std::endl; if ( argc < 2 ) return 0; const QString arg = QString::fromLocal8Bit( argv[1] ); bool ok = false; const int num = arg.toInt( &ok ); if ( ok ) return num; if ( arg == QLatin1String("crash") ) { std::cout << "Yeth, mather. I Crash." << std::endl; crash(); } if ( arg == QLatin1String("--script") ) { const QString fn = QString::fromLocal8Bit( argv[2] ); QFile f( fn ); if ( !f.open( QIODevice::ReadOnly ) ) { std::cerr << "Could not open file for reading: " << qPrintable(f.errorString()) << std::endl; crash(); } bool ok; const int rv = QString::fromLatin1( f.readAll() ).toInt( &ok ); if ( ok ) return rv; else crash(); } } tests/testreturn/testreturn.pro000066400000000000000000000001551325366651500174150ustar00rootroot00000000000000TEMPLATE = app TARGET = INCLUDEPATH += . QT -= gui CONFIG += console # Input SOURCES += main.cpp tests/tests.pro000066400000000000000000000001511325366651500141150ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = \ auto \ downloadspeed \ environmentvariable tools/000077500000000000000000000000001325366651500122325ustar00rootroot00000000000000tools/archivegen/000077500000000000000000000000001325366651500143455ustar00rootroot00000000000000tools/archivegen/archive.cpp000066400000000000000000000162031325366651500164740ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include #include #include #include #include #include #include using namespace QInstaller; class FailOnErrorCallback : public Lib7z::UpdateCallback { HRESULT OpenFileError(const wchar_t*, DWORD) { return S_FALSE; } HRESULT CanNotFindError(const wchar_t*, DWORD) { return S_FALSE; } HRESULT OpenResult(const wchar_t*, HRESULT result, const wchar_t*) { return result; } }; class VerbosePrinterCallback : public Lib7z::UpdateCallback { public: ~VerbosePrinterCallback() { m_PercentPrinter.ClosePrint(); } private: HRESULT SetTotal(UInt64 size) { m_PercentPrinter.SetTotal(size); return S_OK; } HRESULT SetCompleted(const UInt64 *size) { if (size) { m_PercentPrinter.SetRatio(*size); m_PercentPrinter.PrintRatio(); } return S_OK; } HRESULT OpenResult(const wchar_t *file, HRESULT result, const wchar_t*) { if (result != S_OK) { printBlock(QCoreApplication::translate("archivegen", "Cannot update file \"%1\". " "Unsupported archive.").arg(QDir::toNativeSeparators(QString::fromWCharArray(file))), Q_NULLPTR); } return result; } HRESULT OpenFileError(const wchar_t *file, DWORD) { printBlock(QCoreApplication::translate("archivegen", "Cannot open file "), file); return S_FALSE; } HRESULT CanNotFindError(const wchar_t *file, DWORD) { printBlock(QCoreApplication::translate("archivegen", "Cannot find file "), file); return S_FALSE; } HRESULT StartArchive(const wchar_t *name, bool) { printLine(QCoreApplication::translate("archivegen", "Create archive.")); if (name) { m_PercentPrinter.PrintNewLine(); m_PercentPrinter.PrintString(name); } return S_OK; } HRESULT FinishArchive() { m_PercentPrinter.PrintNewLine(); printLine(QCoreApplication::translate("archivegen", "Finished archive.")); return S_OK; } void printLine(const QString &message) { m_PercentPrinter.PrintString(message.toStdWString().c_str()); } void printBlock(const QString &message, const wchar_t *message2) { m_PercentPrinter.PrintNewLine(); m_PercentPrinter.PrintString(message.toStdWString().c_str()); if (message2) m_PercentPrinter.PrintString(message2); m_PercentPrinter.PrintNewLine(); } Lib7z::PercentPrinter m_PercentPrinter; }; int main(int argc, char *argv[]) { try { QCoreApplication app(argc, argv); #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) QCoreApplication::setApplicationVersion(QLatin1String(QUOTE(IFW_VERSION_STR))); #undef QUOTE #undef QUOTE_ QCommandLineParser parser; const QCommandLineOption help = parser.addHelpOption(); const QCommandLineOption version = parser.addVersionOption(); QCommandLineOption verbose(QLatin1String("verbose"), QCoreApplication::translate("archivegen", "Verbose mode. Prints out more information.")); const QCommandLineOption compression = QCommandLineOption(QStringList() << QLatin1String("c") << QLatin1String("compression"), QCoreApplication::translate("archivegen", "0 (No compression)\n" "1 (Fastest compressing)\n" "3 (Fast compressing)\n" "5 (Normal compressing)\n" "7 (Maximum compressing)\n" "9 (Ultra compressing)\n" "Defaults to 5 (Normal compression)." ), QLatin1String("5"), QLatin1String("5")); parser.addOption(verbose); parser.addOption(compression); parser.addPositionalArgument(QLatin1String("archive"), QCoreApplication::translate("archivegen", "Compressed archive to create.")); parser.addPositionalArgument(QLatin1String("sources"), QCoreApplication::translate("archivegen", "List of files and directories to compress.")); parser.parse(app.arguments()); if (parser.isSet(help)) { std::cout << parser.helpText() << std::endl; return EXIT_SUCCESS; } if (parser.isSet(version)) { parser.showVersion(); return EXIT_SUCCESS; } const QStringList args = parser.positionalArguments(); if (args.count() < 2) { std::cerr << QCoreApplication::translate("archivegen", "Wrong argument count. See " "'archivgen --help'.") << std::endl; return EXIT_FAILURE; } bool ok = false; const int values[6] = { 0, 1, 3, 5, 7, 9 }; const int value = parser.value(compression).toInt(&ok); if (!ok || (std::find(std::begin(values), std::end(values), value) == std::end(values))) { throw QInstaller::Error(QCoreApplication::translate("archivegen", "Unknown compression level \"%1\". See 'archivgen --help'.").arg(value)); } Lib7z::initSevenZ(); Lib7z::createArchive(args[0], args.mid(1), Lib7z::QTmpFile::No, Lib7z::Compression(value), [&] () -> Lib7z::UpdateCallback * { if (parser.isSet(verbose)) return new VerbosePrinterCallback; return new FailOnErrorCallback; } () ); return EXIT_SUCCESS; } catch (const QInstaller::Error &e) { std::cerr << e.message() << std::endl; } catch (...) { std::cerr << QCoreApplication::translate("archivegen", "Unknown exception caught.") << std::endl; } return EXIT_FAILURE; } tools/archivegen/archivegen.pro000066400000000000000000000006101325366651500171770ustar00rootroot00000000000000TEMPLATE = app TARGET = archivegen INCLUDEPATH += . .. ../common include(../../installerfw.pri) QT -= gui QT += qml xml LIBS += -l7z CONFIG += console DESTDIR = $$IFW_APP_PATH SOURCES += archive.cpp \ ../common/repositorygen.cpp HEADERS += ../common/repositorygen.h macx:include(../../no_app_bundle.pri) target.path = $$[QT_INSTALL_BINS] INSTALLS += target tools/binarycreator/000077500000000000000000000000001325366651500150765ustar00rootroot00000000000000tools/binarycreator/binarycreator.cpp000066400000000000000000001177171325366651500204640ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "common/repositorygen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_MACOS #include #include #include #include #include #endif using namespace QInstaller; struct Input { QString outputPath; QString installerExePath; QInstallerTools::PackageInfoVector packages; QInstaller::ResourceCollectionManager manager; }; class BundleBackup { public: explicit BundleBackup(const QString &bundle = QString()) : bundle(bundle) { if (!bundle.isEmpty() && QFileInfo(bundle).exists()) { backup = generateTemporaryFileName(bundle); QFile::rename(bundle, backup); } } ~BundleBackup() { if (!backup.isEmpty()) { removeDirectory(bundle); QFile::rename(backup, bundle); } } void release() const { if (!backup.isEmpty()) removeDirectory(backup); backup.clear(); } private: const QString bundle; mutable QString backup; }; #ifndef Q_OS_WIN static void chmod755(const QString &absolutFilePath) { QFile::setPermissions(absolutFilePath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther); } #endif #ifdef Q_OS_MACOS template T readInt(QIODevice *ioDevice, bool *ok, bool swap, bool peek = false) { const auto bytes = peek ? ioDevice->peek(sizeof(T)) : ioDevice->read(sizeof(T)); if (bytes.size() != sizeof(T)) { if (ok) *ok = false; return T(); } if (ok) *ok = true; T n = *reinterpret_cast(bytes.constData()); return swap ? qbswap(n) : n; } static QVersionNumber readMachOMinimumSystemVersion(QIODevice *device) { bool ok; std::vector machoOffsets; qint64 pos = device->pos(); uint32_t magic = readInt(device, &ok, false); if (magic == FAT_MAGIC || magic == FAT_CIGAM || magic == FAT_MAGIC_64 || magic == FAT_CIGAM_64) { bool swap = magic == FAT_CIGAM || magic == FAT_CIGAM_64; uint32_t nfat = readInt(device, &ok, swap); if (!ok) return QVersionNumber(); for (uint32_t n = 0; n < nfat; ++n) { const bool is64bit = magic == FAT_MAGIC_64 || magic == FAT_CIGAM_64; fat_arch_64 fat_arch; fat_arch.cputype = static_cast(readInt(device, &ok, swap)); if (!ok) return QVersionNumber(); fat_arch.cpusubtype = static_cast(readInt(device, &ok, swap)); if (!ok) return QVersionNumber(); fat_arch.offset = is64bit ? readInt(device, &ok, swap) : readInt(device, &ok, swap); if (!ok) return QVersionNumber(); fat_arch.size = is64bit ? readInt(device, &ok, swap) : readInt(device, &ok, swap); if (!ok) return QVersionNumber(); fat_arch.align = readInt(device, &ok, swap); if (!ok) return QVersionNumber(); fat_arch.reserved = is64bit ? readInt(device, &ok, swap) : 0; if (!ok) return QVersionNumber(); machoOffsets.push_back(static_cast(fat_arch.offset)); } } else if (!ok) { return QVersionNumber(); } // Wasn't a fat file, so we just read a thin Mach-O from the original offset if (machoOffsets.empty()) machoOffsets.push_back(pos); std::vector versions; for (const auto &offset : machoOffsets) { if (!device->seek(offset)) return QVersionNumber(); bool swap = false; mach_header_64 header; header.magic = readInt(device, nullptr, swap); switch (header.magic) { case MH_CIGAM: case MH_CIGAM_64: swap = true; break; case MH_MAGIC: case MH_MAGIC_64: break; default: return QVersionNumber(); } header.cputype = static_cast(readInt(device, &ok, swap)); if (!ok) return QVersionNumber(); header.cpusubtype = static_cast(readInt(device, &ok, swap)); if (!ok) return QVersionNumber(); header.filetype = readInt(device, &ok, swap); if (!ok) return QVersionNumber(); header.ncmds = readInt(device, &ok, swap); if (!ok) return QVersionNumber(); header.sizeofcmds = readInt(device, &ok, swap); if (!ok) return QVersionNumber(); header.flags = readInt(device, &ok, swap); if (!ok) return QVersionNumber(); header.reserved = header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64 ? readInt(device, &ok, swap) : 0; if (!ok) return QVersionNumber(); for (uint32_t i = 0; i < header.ncmds; ++i) { const uint32_t cmd = readInt(device, nullptr, swap); const uint32_t cmdsize = readInt(device, nullptr, swap); if (cmd == 0 || cmdsize == 0) return QVersionNumber(); switch (cmd) { case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: case LC_VERSION_MIN_TVOS: case LC_VERSION_MIN_WATCHOS: const uint32_t version = readInt(device, &ok, swap, true); if (!ok) return QVersionNumber(); versions.push_back(QVersionNumber( static_cast(version >> 16), static_cast((version >> 8) & 0xff), static_cast(version & 0xff))); break; } const qint64 remaining = static_cast(cmdsize - sizeof(cmd) - sizeof(cmdsize)); if (device->read(remaining).size() != remaining) return QVersionNumber(); } } std::sort(versions.begin(), versions.end()); return !versions.empty() ? versions.front() : QVersionNumber(); } #endif static int assemble(Input input, const QInstaller::Settings &settings, const QString &signingIdentity) { #ifdef Q_OS_OSX if (QInstaller::isInBundle(input.installerExePath)) { const QString bundle = input.installerExePath; // if the input file was a bundle const QSettings s(QString::fromLatin1("%1/Contents/Info.plist").arg(input.installerExePath), QSettings::NativeFormat); input.installerExePath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(bundle) .arg(s.value(QLatin1String("CFBundleExecutable"), QFileInfo(input.installerExePath).completeBaseName()).toString()); } const bool createDMG = input.outputPath.endsWith(QLatin1String(".dmg")); if (createDMG) input.outputPath.replace(input.outputPath.length() - 4, 4, QLatin1String(".app")); const bool isBundle = input.outputPath.endsWith(QLatin1String(".app")); const QString bundle = isBundle ? input.outputPath : QString(); const BundleBackup bundleBackup(bundle); if (isBundle) { // output should be a bundle const QFileInfo fi(input.outputPath); QString minimumSystemVersion; QFile file(input.installerExePath); if (file.open(QIODevice::ReadOnly)) minimumSystemVersion = readMachOMinimumSystemVersion(&file).normalized().toString(); const QString contentsResourcesPath = fi.filePath() + QLatin1String("/Contents/Resources/"); QInstaller::mkpath(fi.filePath() + QLatin1String("/Contents/MacOS")); QInstaller::mkpath(contentsResourcesPath); { QFile pkgInfo(fi.filePath() + QLatin1String("/Contents/PkgInfo")); pkgInfo.open(QIODevice::WriteOnly); QTextStream pkgInfoStream(&pkgInfo); pkgInfoStream << QLatin1String("APPL????") << endl; } QString iconFile; if (QFile::exists(settings.installerApplicationIcon())) { iconFile = settings.installerApplicationIcon(); } else { iconFile = QString::fromLatin1(":/resources/default_icon_mac.icns"); } const QString iconTargetFile = fi.completeBaseName() + QLatin1String(".icns"); QFile::copy(iconFile, contentsResourcesPath + iconTargetFile); if (QDir(qApp->applicationDirPath() + QLatin1String("/qt_menu.nib")).exists()) { copyDirectoryContents(qApp->applicationDirPath() + QLatin1String("/qt_menu.nib"), contentsResourcesPath + QLatin1String("/qt_menu.nib")); } QFile infoPList(fi.filePath() + QLatin1String("/Contents/Info.plist")); infoPList.open(QIODevice::WriteOnly); QTextStream plistStream(&infoPList); plistStream << QLatin1String("") << endl; plistStream << QLatin1String("") << endl; plistStream << QLatin1String("") << endl; plistStream << QLatin1String("") << endl; plistStream << QLatin1String("\tCFBundleIconFile") << endl; plistStream << QLatin1String("\t") << iconTargetFile << QLatin1String("") << endl; plistStream << QLatin1String("\tCFBundlePackageType") << endl; plistStream << QLatin1String("\tAPPL") << endl; plistStream << QLatin1String("\tCFBundleGetInfoString") << endl; #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) plistStream << QLatin1String("\t") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("") << endl; #undef QUOTE #undef QUOTE_ plistStream << QLatin1String("\tCFBundleSignature") << endl; plistStream << QLatin1String("\t\?\?\?\?") << endl; plistStream << QLatin1String("\tCFBundleExecutable") << endl; plistStream << QLatin1String("\t") << fi.completeBaseName() << QLatin1String("") << endl; plistStream << QLatin1String("\tCFBundleIdentifier") << endl; plistStream << QLatin1String("\tcom.yourcompany.installerbase") << endl; plistStream << QLatin1String("\tNOTE") << endl; plistStream << QLatin1String("\tThis file was generated by Qt Installer Framework.") << endl; plistStream << QLatin1String("\tNSPrincipalClass") << endl; plistStream << QLatin1String("\tNSApplication") << endl; if (!minimumSystemVersion.isEmpty()) { plistStream << QLatin1String("\tLSMinimumSystemVersion") << endl; plistStream << QLatin1String("\t") << minimumSystemVersion << QLatin1String("") << endl; } plistStream << QLatin1String("") << endl; plistStream << QLatin1String("") << endl; input.outputPath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(input.outputPath) .arg(fi.completeBaseName()); } #elif defined(Q_OS_LINUX) Q_UNUSED(settings) #endif QTemporaryFile file(input.outputPath); if (!file.open()) { throw Error(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(input.installerExePath, input.outputPath, file.errorString())); } const QString tempFile = file.fileName(); file.close(); file.remove(); QFile instExe(input.installerExePath); if (!instExe.copy(tempFile)) { throw Error(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(instExe.fileName(), tempFile, instExe.errorString())); } QtPatch::patchBinaryFile(tempFile, QByteArray("MY_InstallerCreateDateTime_MY"), QDateTime::currentDateTime().toString(QLatin1String("yyyy-MM-dd - HH:mm:ss")).toLatin1()); input.installerExePath = tempFile; #if defined(Q_OS_WIN) // setting the windows icon must happen before we append our binary data - otherwise they get lost :-/ if (QFile::exists(settings.installerApplicationIcon())) { // no error handling as this is not fatal setApplicationIcon(tempFile, settings.installerApplicationIcon()); } #elif defined(Q_OS_OSX) if (isBundle) { // no error handling as this is not fatal const QString copyscript = QDir::temp().absoluteFilePath(QLatin1String("copylibsintobundle.sh")); QFile::copy(QLatin1String(":/resources/copylibsintobundle.sh"), copyscript); QFile::rename(tempFile, input.outputPath); chmod755(copyscript); QProcess p; p.start(copyscript, QStringList() << bundle); p.waitForFinished(-1); QFile::rename(input.outputPath, tempFile); QFile::remove(copyscript); } #endif QTemporaryFile out; QString targetName = input.outputPath; #ifdef Q_OS_OSX QDir resourcePath(QFileInfo(input.outputPath).dir()); resourcePath.cdUp(); resourcePath.cd(QLatin1String("Resources")); targetName = resourcePath.filePath(QLatin1String("installer.dat")); #endif { QFile target(targetName); if (target.exists() && !target.remove()) { qCritical("Cannot remove target %s: %s", qPrintable(target.fileName()), qPrintable(target.errorString())); QFile::remove(tempFile); return EXIT_FAILURE; } } try { QInstaller::openForWrite(&out); QFile exe(input.installerExePath); #ifdef Q_OS_OSX if (!exe.copy(input.outputPath)) { throw Error(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(exe.fileName(), input.outputPath, exe.errorString())); } #else QInstaller::openForRead(&exe); QInstaller::appendData(&out, &exe, exe.size()); #endif foreach (const QInstallerTools::PackageInfo &info, input.packages) { QInstaller::ResourceCollection collection; collection.setName(info.name.toUtf8()); qDebug() << "Creating resource archive for" << info.name; foreach (const QString &file, info.copiedFiles) { const QSharedPointer resource(new Resource(file)); qDebug().nospace() << "Appending " << file << " (" << humanReadableSize(resource->size()) << ")"; collection.appendResource(resource); } input.manager.insertCollection(collection); } const QList operations; BinaryContent::writeBinaryContent(&out, operations, input.manager, BinaryContent::MagicInstallerMarker, BinaryContent::MagicCookie); } catch (const Error &e) { qCritical("Error occurred while assembling the installer: %s", qPrintable(e.message())); QFile::remove(tempFile); return EXIT_FAILURE; } if (!out.rename(targetName)) { qCritical("Cannot write installer to %s: %s", targetName.toUtf8().constData(), out.errorString().toUtf8().constData()); QFile::remove(tempFile); return EXIT_FAILURE; } out.setAutoRemove(false); #ifndef Q_OS_WIN chmod755(out.fileName()); #endif QFile::remove(tempFile); #ifdef Q_OS_OSX if (isBundle && !signingIdentity.isEmpty()) { qDebug() << "Signing .app bundle..."; QProcess p; p.start(QLatin1String("codesign"), QStringList() << QLatin1String("--force") << QLatin1String("--deep") << QLatin1String("--sign") << signingIdentity << bundle); if (!p.waitForFinished(-1)) { qCritical("Failed to sign app bundle: error while running '%s %s': %s", p.program().toUtf8().constData(), p.arguments().join(QLatin1Char(' ')).toUtf8().constData(), p.errorString().toUtf8().constData()); return EXIT_FAILURE; } if (p.exitStatus() == QProcess::NormalExit) { if (p.exitCode() != 0) { qCritical("Failed to sign app bundle: running codesign failed " "with exit code %d: %s", p.exitCode(), p.readAllStandardError().constData()); return EXIT_FAILURE; } } qDebug() << "done."; } bundleBackup.release(); if (createDMG) { qDebug() << "creating a DMG disk image..."; const QString volumeName = QFileInfo(input.outputPath).fileName(); const QString imagePath = QString::fromLatin1("%1/%2.dmg") .arg(QFileInfo(bundle).path()) .arg(volumeName); // no error handling as this is not fatal QProcess p; p.start(QLatin1String("/usr/bin/hdiutil"), QStringList() << QLatin1String("create") << imagePath << QLatin1String("-srcfolder") << bundle << QLatin1String("-ov") << QLatin1String("-volname") << volumeName << QLatin1String("-fs") << QLatin1String("HFS+")); qDebug() << "running " << p.program() << p.arguments(); p.waitForFinished(-1); qDebug() << "removing" << bundle; QDir(bundle).removeRecursively(); qDebug() << "done."; } #else Q_UNUSED(signingIdentity) #endif return EXIT_SUCCESS; } QT_BEGIN_NAMESPACE int runRcc(int argc, char *argv[]); QT_END_NAMESPACE static int runRcc(const QStringList &args) { const int argc = args.count(); QVector argv(argc, 0); for (int i = 0; i < argc; ++i) argv[i] = qstrdup(qPrintable(args[i])); // Note: this does not run the rcc provided by Qt, this one is using the compiled in binarycreator // version. If it happens that resource mapping fails, we might need to adapt the code here... const int result = runRcc(argc, argv.data()); foreach (char *arg, argv) delete [] arg; return result; } class WorkingDirectoryChange { public: explicit WorkingDirectoryChange(const QString &path) : oldPath(QDir::currentPath()) { QDir::setCurrent(path); } virtual ~WorkingDirectoryChange() { QDir::setCurrent(oldPath); } private: const QString oldPath; }; static QSharedPointer createDefaultResourceFile(const QString &directory, const QString &binaryName) { QTemporaryFile projectFile(directory + QLatin1String("/rccprojectXXXXXX.qrc")); if (!projectFile.open()) throw Error(QString::fromLatin1("Cannot create temporary file for generated rcc project file")); projectFile.close(); const WorkingDirectoryChange wd(directory); const QString projectFileName = QFileInfo(projectFile.fileName()).absoluteFilePath(); // 1. create the .qrc file if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-project") << QLatin1String("-o") << projectFileName) != EXIT_SUCCESS) { throw Error(QString::fromLatin1("Cannot create rcc project file.")); } // 2. create the binary resource file from the .qrc file if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-binary") << QLatin1String("-o") << binaryName << projectFileName) != EXIT_SUCCESS) { throw Error(QString::fromLatin1("Cannot compile rcc project file.")); } return QSharedPointer(new QInstaller::Resource(binaryName, binaryName .toUtf8())); } static QList > createBinaryResourceFiles(const QStringList &resources) { QList > result; foreach (const QString &resource, resources) { QFile file(resource); if (file.exists()) { const QString binaryName = generateTemporaryFileName(); const QString fileName = QFileInfo(file.fileName()).absoluteFilePath(); const int status = runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-binary") << QLatin1String("-o") << binaryName << fileName); if (status != EXIT_SUCCESS) continue; result.append(QSharedPointer (new QInstaller::Resource(binaryName, binaryName.toUtf8()))); } } return result; } static void printUsage() { QString suffix; #ifdef Q_OS_WIN suffix = QLatin1String(".exe"); #endif const QString appName = QFileInfo(QCoreApplication::applicationFilePath()).fileName(); std::cout << "Usage: " << appName << " [options] target" << std::endl; std::cout << std::endl; std::cout << "Options:" << std::endl; std::cout << " -t|--template file Use file as installer template binary" << std::endl; std::cout << " If this parameter is not given, the template used" << std::endl; std::cout << " defaults to installerbase." << std::endl; QInstallerTools::printRepositoryGenOptions(); std::cout << " -c|--config file The file containing the installer configuration" << std::endl; std::cout << " -n|--online-only Do not add any package into the installer" << std::endl; std::cout << " (for online only installers)" << std::endl; std::cout << " -f|--offline-only Forces the installer to act as an offline installer, " << std::endl; std::cout << " i.e. never access online repositories" << std::endl; std::cout << " -r|--resources r1,.,rn include the given resource files into the binary" << std::endl; std::cout << " -v|--verbose Verbose output" << std::endl; std::cout << " -rcc|--compile-resource Compiles the default resource and outputs the result into" << std::endl; std::cout << " 'update.rcc' in the current path." << std::endl; #ifdef Q_OS_OSX std::cout << " -s|--sign identity Sign generated app bundle using the given code " << std::endl; std::cout << " signing identity" << std::endl; #endif std::cout << std::endl; std::cout << "Packages are to be found in the current working directory and get listed as " "their names" << std::endl << std::endl; std::cout << "Example (offline installer):" << std::endl; char sep = QDir::separator().toLatin1(); std::cout << " " << appName << " --offline-only -c installer-config" << sep << "config.xml -p " "packages-directory -t installerbase" << suffix << " SDKInstaller" << suffix << std::endl; std::cout << "Creates an offline installer for the SDK, containing all dependencies." << std::endl; std::cout << std::endl; std::cout << "Example (online installer):" << std::endl; std::cout << " " << appName << " -c installer-config" << sep << "config.xml -p packages-directory " "-e org.qt-project.sdk.qt,org.qt-project.qtcreator -t installerbase" << suffix << " SDKInstaller" << suffix << std::endl; std::cout << std::endl; std::cout << "Creates an installer for the SDK without qt and qt creator." << std::endl; std::cout << std::endl; std::cout << "Example update.rcc:" << std::endl; std::cout << " " << appName << " -c installer-config" << sep << "config.xml -p packages-directory " "-rcc" << std::endl; } void copyConfigData(const QString &configFile, const QString &targetDir) { qDebug() << "Begin to copy configuration file and data."; const QString sourceConfigFile = QFileInfo(configFile).absoluteFilePath(); const QString targetConfigFile = targetDir + QLatin1String("/config.xml"); QInstallerTools::copyWithException(sourceConfigFile, targetConfigFile, QLatin1String("configuration")); QFile configXml(targetConfigFile); QInstaller::openForRead(&configXml); QDomDocument dom; dom.setContent(&configXml); configXml.close(); // iterate over all child elements, searching for relative file names const QDomNodeList children = dom.documentElement().childNodes(); const QString sourceConfigFilePath = QFileInfo(sourceConfigFile).absolutePath(); for (int i = 0; i < children.count(); ++i) { QDomElement domElement = children.at(i).toElement(); if (domElement.isNull()) continue; const QString tagName = domElement.tagName(); const QString elementText = domElement.text(); qDebug().noquote() << QString::fromLatin1("Read dom element: <%1>%2.").arg(tagName, elementText); QString newName = domElement.text().replace(QRegExp(QLatin1String("\\\\|/|\\.|:")), QLatin1String("_")); QString targetFile; QFileInfo elementFileInfo; if (tagName == QLatin1String("Icon") || tagName == QLatin1String("InstallerApplicationIcon")) { #if defined(Q_OS_OSX) const QString suffix = QLatin1String(".icns"); #elif defined(Q_OS_WIN) const QString suffix = QLatin1String(".ico"); #else const QString suffix = QLatin1String(".png"); #endif elementFileInfo = QFileInfo(sourceConfigFilePath, elementText + suffix); targetFile = targetDir + QLatin1Char('/') + newName + suffix; } else { elementFileInfo = QFileInfo(sourceConfigFilePath, elementText); const QString suffix = elementFileInfo.completeSuffix(); if (!suffix.isEmpty()) newName.append(QLatin1Char('.') + suffix); targetFile = targetDir + QLatin1Char('/') + newName; } if (!elementFileInfo.exists() || elementFileInfo.isDir()) continue; domElement.replaceChild(dom.createTextNode(newName), domElement.firstChild()); QInstallerTools::copyWithException(elementFileInfo.absoluteFilePath(), targetFile, tagName); } QInstaller::openForWrite(&configXml); QTextStream stream(&configXml); dom.save(stream, 4); qDebug() << "done.\n"; } static int printErrorAndUsageAndExit(const QString &err) { std::cerr << qPrintable(err) << std::endl << std::endl; printUsage(); return EXIT_FAILURE; } int main(int argc, char **argv) { QCoreApplication app(argc, argv); QInstaller::init(); QString templateBinary = QLatin1String("installerbase"); QString suffix; #ifdef Q_OS_WIN suffix = QLatin1String(".exe"); templateBinary = templateBinary + suffix; #endif if (!QFileInfo(templateBinary).exists()) templateBinary = QString::fromLatin1("%1/%2").arg(qApp->applicationDirPath(), templateBinary); QString target; QString configFile; QStringList packagesDirectories; bool onlineOnly = false; bool offlineOnly = false; QStringList resources; QStringList filteredPackages; QInstallerTools::FilterType ftype = QInstallerTools::Exclude; bool compileResource = false; QString signingIdentity; const QStringList args = app.arguments().mid(1); for (QStringList::const_iterator it = args.begin(); it != args.end(); ++it) { if (*it == QLatin1String("-h") || *it == QLatin1String("--help")) { printUsage(); return 0; } else if (*it == QLatin1String("-p") || *it == QLatin1String("--packages")) { ++it; if (it == args.end()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Packages parameter missing argument.")); } if (!QFileInfo(*it).exists()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Package directory not found at the " "specified location.")); } packagesDirectories.append(*it); } else if (*it == QLatin1String("-e") || *it == QLatin1String("--exclude")) { ++it; if (!filteredPackages.isEmpty()) return printErrorAndUsageAndExit(QString::fromLatin1("Error: --include and --exclude are mutually " "exclusive. Use either one or the other.")); if (it == args.end() || it->startsWith(QLatin1String("-"))) return printErrorAndUsageAndExit(QString::fromLatin1("Error: Package to exclude missing.")); filteredPackages = it->split(QLatin1Char(',')); } else if (*it == QLatin1String("-i") || *it == QLatin1String("--include")) { ++it; if (!filteredPackages.isEmpty()) return printErrorAndUsageAndExit(QString::fromLatin1("Error: --include and --exclude are mutually " "exclusive. Use either one or the other.")); if (it == args.end() || it->startsWith(QLatin1String("-"))) return printErrorAndUsageAndExit(QString::fromLatin1("Error: Package to include missing.")); filteredPackages = it->split(QLatin1Char(',')); ftype = QInstallerTools::Include; } else if (*it == QLatin1String("-v") || *it == QLatin1String("--verbose")) { QInstaller::setVerbose(true); } else if (*it == QLatin1String("-n") || *it == QLatin1String("--online-only")) { if (!filteredPackages.isEmpty()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: 'online-only' option cannot be used " "in conjunction with the 'include' or 'exclude' option. An 'online-only' installer will never " "contain any components apart from the root component.")); } onlineOnly = true; } else if (*it == QLatin1String("-f") || *it == QLatin1String("--offline-only")) { offlineOnly = true; } else if (*it == QLatin1String("-t") || *it == QLatin1String("--template")) { ++it; if (it == args.end()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Template parameter missing argument.")); } templateBinary = *it; #ifdef Q_OS_WIN if (!templateBinary.endsWith(suffix)) templateBinary = templateBinary + suffix; #endif if (!QFileInfo(templateBinary).exists()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Template not found at the specified " "location.")); } } else if (*it == QLatin1String("-c") || *it == QLatin1String("--config")) { ++it; if (it == args.end()) return printErrorAndUsageAndExit(QString::fromLatin1("Error: Config parameter missing argument.")); const QFileInfo fi(*it); if (!fi.exists()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Config file %1 not found at the " "specified location.").arg(*it)); } if (!fi.isFile()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Configuration %1 is not a file.") .arg(*it)); } if (!fi.isReadable()) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Config file %1 is not readable.") .arg(*it)); } configFile = *it; } else if (*it == QLatin1String("-r") || *it == QLatin1String("--resources")) { ++it; if (it == args.end() || it->startsWith(QLatin1String("-"))) return printErrorAndUsageAndExit(QString::fromLatin1("Error: Resource files to include are missing.")); resources = it->split(QLatin1Char(',')); } else if (*it == QLatin1String("--ignore-translations") || *it == QLatin1String("--ignore-invalid-packages")) { continue; } else if (*it == QLatin1String("-rcc") || *it == QLatin1String("--compile-resource")) { compileResource = true; #ifdef Q_OS_OSX } else if (*it == QLatin1String("-s") || *it == QLatin1String("--sign")) { ++it; if (it == args.end() || it->startsWith(QLatin1String("-"))) return printErrorAndUsageAndExit(QString::fromLatin1("Error: No code signing identity specified.")); signingIdentity = *it; #endif } else { if (it->startsWith(QLatin1String("-"))) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Unknown option \"%1\" used. Maybe you " "are using an old syntax.").arg(*it)); } else if (target.isEmpty()) { target = *it; #ifdef Q_OS_WIN if (!target.endsWith(suffix)) target = target + suffix; #endif } else { return printErrorAndUsageAndExit(QString::fromLatin1("Error: You are using an old syntax please add the " "component name with the include option") .arg(*it)); } } } if (onlineOnly && offlineOnly) { return printErrorAndUsageAndExit(QString::fromLatin1("You cannot use --online-only and " "--offline-only at the same time.")); } if (onlineOnly) { filteredPackages.append(QLatin1String("X_fake_filter_component_for_online_only_installer_X")); ftype = QInstallerTools::Include; } if (target.isEmpty() && !compileResource) return printErrorAndUsageAndExit(QString::fromLatin1("Error: Target parameter missing.")); if (configFile.isEmpty()) return printErrorAndUsageAndExit(QString::fromLatin1("Error: No configuration file selected.")); if (packagesDirectories.isEmpty()) return printErrorAndUsageAndExit(QString::fromLatin1("Error: Package directory parameter missing.")); qDebug() << "Parsed arguments, ok."; Input input; int exitCode = EXIT_FAILURE; QTemporaryDir tmp; tmp.setAutoRemove(false); const QString tmpMetaDir = tmp.path(); QTemporaryDir tmp2; tmp2.setAutoRemove(false); const QString tmpRepoDir = tmp2.path(); try { const Settings settings = Settings::fromFileAndPrefix(configFile, QFileInfo(configFile) .absolutePath()); // Note: the order here is important // 1; create the list of available packages QInstallerTools::PackageInfoVector packages = QInstallerTools::createListOfPackages(packagesDirectories, &filteredPackages, ftype); // 2; copy the packages data and setup the packages vector with the files we copied, // must happen before copying meta data because files will be compressed if // needed and meta data generation relies on this QInstallerTools::copyComponentData(packagesDirectories, tmpRepoDir, &packages); // 3; copy the meta data of the available packages, generate Updates.xml QInstallerTools::copyMetaData(tmpMetaDir, tmpRepoDir, packages, settings .applicationName(), settings.version()); // 4; copy the configuration file and and icons etc. copyConfigData(configFile, tmpMetaDir + QLatin1String("/installer-config")); { QSettings confInternal(tmpMetaDir + QLatin1String("/config/config-internal.ini") , QSettings::IniFormat); // assume offline installer if there are no repositories and no //--online-only not set offlineOnly = offlineOnly | settings.repositories().isEmpty(); if (onlineOnly) offlineOnly = !onlineOnly; confInternal.setValue(QLatin1String("offlineOnly"), offlineOnly); } #ifdef Q_OS_OSX // on mac, we enforce building a bundle if (!target.endsWith(QLatin1String(".app")) && !target.endsWith(QLatin1String(".dmg"))) target += QLatin1String(".app"); #endif if (!compileResource) { // 5; put the copied resources into a resource file QInstaller::ResourceCollection metaCollection("QResources"); metaCollection.appendResource(createDefaultResourceFile(tmpMetaDir, generateTemporaryFileName())); metaCollection.appendResources(createBinaryResourceFiles(resources)); input.manager.insertCollection(metaCollection); input.packages = packages; input.outputPath = target; input.installerExePath = templateBinary; qDebug() << "Creating the binary"; exitCode = assemble(input, settings, signingIdentity); } else { createDefaultResourceFile(tmpMetaDir, QDir::currentPath() + QLatin1String("/update.rcc")); exitCode = EXIT_SUCCESS; } } catch (const Error &e) { QFile::remove(input.outputPath); std::cerr << "Caught exception: " << e.message() << std::endl; } catch (...) { QFile::remove(input.outputPath); std::cerr << "Unknown exception caught" << std::endl; } qDebug() << "Cleaning up..."; const QInstaller::ResourceCollection collection = input.manager.collectionByName("QResources"); foreach (const QSharedPointer &resource, collection.resources()) QFile::remove(QString::fromUtf8(resource->name())); QInstaller::removeDirectory(tmpMetaDir, true); QInstaller::removeDirectory(tmpRepoDir, true); return exitCode; } tools/binarycreator/binarycreator.pro000066400000000000000000000007131325366651500204650ustar00rootroot00000000000000TEMPLATE = app TARGET = binarycreator INCLUDEPATH += . .. rcc ../common include(../../installerfw.pri) QT -= gui QT += qml xml CONFIG += console DESTDIR = $$IFW_APP_PATH SOURCES = binarycreator.cpp \ rcc/rcc.cpp \ rcc/rccmain.cpp \ ../common/repositorygen.cpp HEADERS = rcc/rcc.h RESOURCES += binarycreator.qrc macx:include(../../no_app_bundle.pri) target.path = $$[QT_INSTALL_BINS] INSTALLS += target tools/binarycreator/binarycreator.qrc000066400000000000000000000004001325366651500204430ustar00rootroot00000000000000 resources/default_icon_mac.icns resources/copylibsintobundle.sh ../../src/sdk/installerbase.ico tools/binarycreator/rcc/000077500000000000000000000000001325366651500156455ustar00rootroot00000000000000tools/binarycreator/rcc/qcorecmdlineargs_p.h000066400000000000000000000036531325366651500216660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCORECMDLINEARGS_P_H #define QCORECMDLINEARGS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "QtCore/qstring.h" #include "QtCore/qstringlist.h" QT_BEGIN_NAMESPACE static inline QStringList qCmdLineArgs(int argc, char *argv[]) { QStringList args; for (int i = 0; i != argc; ++i) args += QString::fromLocal8Bit(argv[i]); return args; } QT_END_NAMESPACE #endif // QCORECMDLINEARGS_WIN_P_H tools/binarycreator/rcc/rcc.cpp000066400000000000000000001053011325366651500171200ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "rcc.h" #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE enum { CONSTANT_USENAMESPACE = 1, CONSTANT_COMPRESSLEVEL_DEFAULT = -1, CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70 }; #define writeString(s) write(s, sizeof(s)) void RCCResourceLibrary::write(const char *str, int len) { --len; // trailing \0 on string literals... int n = m_out.size(); m_out.resize(n + len); memcpy(m_out.data() + n, str, len); } void RCCResourceLibrary::writeByteArray(const QByteArray &other) { m_out.append(other); } static inline QString msgOpenReadFailed(const QString &fname, const QString &why) { return QString::fromUtf8("Unable to open %1 for reading: %2\n").arg(fname).arg(why); } /////////////////////////////////////////////////////////// // // RCCFileInfo // /////////////////////////////////////////////////////////// class RCCFileInfo { public: enum Flags { NoFlags = 0x00, Compressed = 0x01, Directory = 0x02 }; RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(), QLocale::Language language = QLocale::C, QLocale::Country country = QLocale::AnyCountry, uint flags = NoFlags, int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT, int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT); ~RCCFileInfo(); QString resourceName() const; public: qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage); qint64 writeDataName(RCCResourceLibrary &, qint64 offset); void writeDataInfo(RCCResourceLibrary &lib); int m_flags; QString m_name; QLocale::Language m_language; QLocale::Country m_country; QFileInfo m_fileInfo; RCCFileInfo *m_parent; QHash m_children; int m_compressLevel; int m_compressThreshold; qint64 m_nameOffset; qint64 m_dataOffset; qint64 m_childOffset; }; RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language, QLocale::Country country, uint flags, int compressLevel, int compressThreshold) { m_name = name; m_fileInfo = fileInfo; m_language = language; m_country = country; m_flags = flags; m_parent = 0; m_nameOffset = 0; m_dataOffset = 0; m_childOffset = 0; m_compressLevel = compressLevel; m_compressThreshold = compressThreshold; } RCCFileInfo::~RCCFileInfo() { qDeleteAll(m_children); } QString RCCFileInfo::resourceName() const { QString resource = m_name; for (RCCFileInfo *p = m_parent; p; p = p->m_parent) resource = resource.prepend(p->m_name + QLatin1Char('/')); return QLatin1Char(':') + resource; } void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) { const bool text = (lib.m_format == RCCResourceLibrary::C_Code); //some info if (text) { if (m_language != QLocale::C) { lib.writeString(" // "); lib.writeByteArray(resourceName().toLocal8Bit()); lib.writeString(" ["); lib.writeByteArray(QByteArray::number(m_country)); lib.writeString("::"); lib.writeByteArray(QByteArray::number(m_language)); lib.writeString("[\n "); } else { lib.writeString(" // "); lib.writeByteArray(resourceName().toLocal8Bit()); lib.writeString("\n "); } } //pointer data if (m_flags & RCCFileInfo::Directory) { // name offset lib.writeNumber4(m_nameOffset); // flags lib.writeNumber2(m_flags); // child count lib.writeNumber4(m_children.size()); // first child offset lib.writeNumber4(m_childOffset); } else { // name offset lib.writeNumber4(m_nameOffset); // flags lib.writeNumber2(m_flags); // locale lib.writeNumber2(m_country); lib.writeNumber2(m_language); //data offset lib.writeNumber4(m_dataOffset); } if (text) lib.writeChar('\n'); } qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage) { const bool text = (lib.m_format == RCCResourceLibrary::C_Code); //capture the offset m_dataOffset = offset; //find the data to be written QFile file(m_fileInfo.absoluteFilePath()); if (!file.open(QFile::ReadOnly)) { *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString()); return 0; } QByteArray data = file.readAll(); #ifndef QT_NO_COMPRESS // Check if compression is useful for this file if (m_compressLevel != 0 && data.size() != 0) { QByteArray compressed = qCompress(reinterpret_cast(data.data()), data.size(), m_compressLevel); int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size()); if (compressRatio >= m_compressThreshold) { data = compressed; m_flags |= Compressed; } } #endif // QT_NO_COMPRESS // some info if (text) { lib.writeString(" // "); lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit()); lib.writeString("\n "); } // write the length lib.writeNumber4(data.size()); if (text) lib.writeString("\n "); offset += 4; // write the payload const char *p = data.constData(); if (text) { for (int i = data.size(), j = 0; --i >= 0; --j) { lib.writeHex(*p++); if (j == 0) { lib.writeString("\n "); j = 16; } } } else { for (int i = data.size(); --i >= 0; ) lib.writeChar(*p++); } offset += data.size(); // done if (text) lib.writeString("\n "); return offset; } qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset) { const bool text = (lib.m_format == RCCResourceLibrary::C_Code); // capture the offset m_nameOffset = offset; // some info if (text) { lib.writeString(" // "); lib.writeByteArray(m_name.toLocal8Bit()); lib.writeString("\n "); } // write the length lib.writeNumber2(m_name.length()); if (text) lib.writeString("\n "); offset += 2; // write the hash lib.writeNumber4(qt_hash(m_name)); if (text) lib.writeString("\n "); offset += 4; // write the m_name const QChar *unicode = m_name.unicode(); for (int i = 0; i < m_name.length(); ++i) { lib.writeNumber2(unicode[i].unicode()); if (text && i % 16 == 0) lib.writeString("\n "); } offset += m_name.length()*2; // done if (text) lib.writeString("\n "); return offset; } /////////////////////////////////////////////////////////// // // RCCResourceLibrary // /////////////////////////////////////////////////////////// RCCResourceLibrary::Strings::Strings() : TAG_RCC(QLatin1String("RCC")), TAG_RESOURCE(QLatin1String("qresource")), TAG_FILE(QLatin1String("file")), ATTRIBUTE_LANG(QLatin1String("lang")), ATTRIBUTE_PREFIX(QLatin1String("prefix")), ATTRIBUTE_ALIAS(QLatin1String("alias")), ATTRIBUTE_THRESHOLD(QLatin1String("threshold")), ATTRIBUTE_COMPRESS(QLatin1String("compress")) { } RCCResourceLibrary::RCCResourceLibrary() : m_root(0), m_format(C_Code), m_verbose(false), m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT), m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT), m_treeOffset(0), m_namesOffset(0), m_dataOffset(0), m_useNameSpace(CONSTANT_USENAMESPACE), m_errorDevice(0) { m_out.reserve(30 * 1000 * 1000); } RCCResourceLibrary::~RCCResourceLibrary() { delete m_root; } enum RCCXmlTag { RccTag, ResourceTag, FileTag }; bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice, const QString &fname, QString currentPath, bool ignoreErrors) { Q_ASSERT(m_errorDevice); const QChar slash = QLatin1Char('/'); if (!currentPath.isEmpty() && !currentPath.endsWith(slash)) currentPath += slash; QXmlStreamReader reader(inputDevice); QStack tokens; QString prefix; QLocale::Language language = QLocale::c().language(); QLocale::Country country = QLocale::c().country(); QString alias; int compressLevel = m_compressLevel; int compressThreshold = m_compressThreshold; while (!reader.atEnd()) { QXmlStreamReader::TokenType t = reader.readNext(); switch (t) { case QXmlStreamReader::StartElement: if (reader.name() == m_strings.TAG_RCC) { if (!tokens.isEmpty()) reader.raiseError(QLatin1String("expected tag")); else tokens.push(RccTag); } else if (reader.name() == m_strings.TAG_RESOURCE) { if (tokens.isEmpty() || tokens.top() != RccTag) { reader.raiseError(QLatin1String("unexpected tag")); } else { tokens.push(ResourceTag); QXmlStreamAttributes attributes = reader.attributes(); language = QLocale::c().language(); country = QLocale::c().country(); if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) { QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString(); QLocale lang = QLocale(attribute); language = lang.language(); if (2 == attribute.length()) { // Language only country = QLocale::AnyCountry; } else { country = lang.country(); } } prefix.clear(); if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX)) prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString(); if (!prefix.startsWith(slash)) prefix.prepend(slash); if (!prefix.endsWith(slash)) prefix += slash; } } else if (reader.name() == m_strings.TAG_FILE) { if (tokens.isEmpty() || tokens.top() != ResourceTag) { reader.raiseError(QLatin1String("unexpected tag")); } else { tokens.push(FileTag); QXmlStreamAttributes attributes = reader.attributes(); alias.clear(); if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS)) alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString(); compressLevel = m_compressLevel; if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) compressLevel = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString().toInt(); compressThreshold = m_compressThreshold; if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD)) compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt(); // Special case for -no-compress. Overrides all other settings. if (m_compressLevel == -2) compressLevel = 0; } } else { reader.raiseError(QString(QLatin1String("unexpected tag: %1")).arg(reader.name().toString())); } break; case QXmlStreamReader::EndElement: if (reader.name() == m_strings.TAG_RCC) { if (!tokens.isEmpty() && tokens.top() == RccTag) tokens.pop(); else reader.raiseError(QLatin1String("unexpected closing tag")); } else if (reader.name() == m_strings.TAG_RESOURCE) { if (!tokens.isEmpty() && tokens.top() == ResourceTag) tokens.pop(); else reader.raiseError(QLatin1String("unexpected closing tag")); } else if (reader.name() == m_strings.TAG_FILE) { if (!tokens.isEmpty() && tokens.top() == FileTag) tokens.pop(); else reader.raiseError(QLatin1String("unexpected closing tag")); } break; case QXmlStreamReader::Characters: if (reader.isWhitespace()) break; if (tokens.isEmpty() || tokens.top() != FileTag) { reader.raiseError(QLatin1String("unexpected text")); } else { QString fileName = reader.text().toString(); if (fileName.isEmpty()) { const QString msg = QString::fromLatin1("RCC: Warning: Null node in XML of '%1'\n").arg(fname); m_errorDevice->write(msg.toUtf8()); } if (alias.isNull()) alias = fileName; alias = QDir::cleanPath(alias); while (alias.startsWith(QLatin1String("../"))) alias.remove(0, 3); alias = QDir::cleanPath(m_resourceRoot) + prefix + alias; QString absFileName = fileName; if (QDir::isRelativePath(absFileName)) absFileName.prepend(currentPath); QFileInfo file(absFileName); if (!file.exists()) { m_failedResources.push_back(absFileName); const QString msg = QString::fromLatin1("RCC: Error in '%1': Cannot find file '%2'\n").arg(fname).arg(fileName); m_errorDevice->write(msg.toUtf8()); if (ignoreErrors) continue; else return false; } else if (file.isFile()) { const bool arc = addFile(alias, RCCFileInfo(alias.section(slash, -1), file, language, country, RCCFileInfo::NoFlags, compressLevel, compressThreshold) ); if (!arc) m_failedResources.push_back(absFileName); } else { QDir dir; if (file.isDir()) { dir.setPath(file.filePath()); } else { dir.setPath(file.path()); dir.setNameFilters(QStringList(file.fileName())); if (alias.endsWith(file.fileName())) alias = alias.left(alias.length()-file.fileName().length()); } if (!alias.endsWith(slash)) alias += slash; QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); QFileInfo child(it.fileInfo()); if (child.fileName() != QLatin1String(".") && child.fileName() != QLatin1String("..")) { const bool arc = addFile(alias + child.fileName(), RCCFileInfo(child.fileName(), child, language, country, child.isDir() ? RCCFileInfo::Directory : RCCFileInfo::NoFlags, compressLevel, compressThreshold) ); if (!arc) m_failedResources.push_back(child.fileName()); } } } } break; default: break; } } if (reader.hasError()) { if (ignoreErrors) return true; int errorLine = reader.lineNumber(); int errorColumn = reader.columnNumber(); QString errorMessage = reader.errorString(); QString msg = QString::fromLatin1("RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage); m_errorDevice->write(msg.toUtf8()); return false; } if (m_root == 0) { const QString msg = QString::fromUtf8("RCC: Warning: No resources in '%1'.\n").arg(fname); m_errorDevice->write(msg.toUtf8()); if (!ignoreErrors && m_format == Binary) { // create dummy entry, otherwise loading with QResource will crash m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory); } } return true; } bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file) { Q_ASSERT(m_errorDevice); if (file.m_fileInfo.size() > 0xffffffff) { const QString msg = QString::fromUtf8("File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath()); m_errorDevice->write(msg.toUtf8()); return false; } if (!m_root) m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory); RCCFileInfo *parent = m_root; const QStringList nodes = alias.split(QLatin1Char('/')); for (int i = 1; i < nodes.size()-1; ++i) { const QString node = nodes.at(i); if (node.isEmpty()) continue; if (!parent->m_children.contains(node)) { RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory); s->m_parent = parent; parent->m_children.insert(node, s); parent = s; } else { parent = parent->m_children[node]; } } const QString filename = nodes.at(nodes.size()-1); RCCFileInfo *s = new RCCFileInfo(file); s->m_parent = parent; if (parent->m_children.contains(filename)) { foreach (const QString &fileName, m_fileNames) qWarning("%s: Warning: potential duplicate alias detected: '%s'", qPrintable(fileName), qPrintable(filename)); } parent->m_children.insertMulti(filename, s); return true; } void RCCResourceLibrary::reset() { if (m_root) { delete m_root; m_root = 0; } m_errorDevice = 0; m_failedResources.clear(); } bool RCCResourceLibrary::readFiles(bool ignoreErrors, QIODevice &errorDevice) { reset(); m_errorDevice = &errorDevice; //read in data if (m_verbose) { const QString msg = QString::fromUtf8("Processing %1 files [%2]\n") .arg(m_fileNames.size()).arg(static_cast(ignoreErrors)); m_errorDevice->write(msg.toUtf8()); } for (int i = 0; i < m_fileNames.size(); ++i) { QFile fileIn; QString fname = m_fileNames.at(i); QString pwd; if (fname == QLatin1String("-")) { fname = QLatin1String("(stdin)"); pwd = QDir::currentPath(); fileIn.setFileName(fname); if (!fileIn.open(stdin, QIODevice::ReadOnly)) { m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8()); return false; } } else { pwd = QFileInfo(fname).path(); fileIn.setFileName(fname); if (!fileIn.open(QIODevice::ReadOnly)) { m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8()); return false; } } if (m_verbose) { const QString msg = QString::fromUtf8("Interpreting %1\n").arg(fname); m_errorDevice->write(msg.toUtf8()); } if (!interpretResourceFile(&fileIn, fname, pwd, ignoreErrors)) return false; } return true; } QStringList RCCResourceLibrary::dataFiles() const { QStringList ret; QStack pending; if (!m_root) return ret; pending.push(m_root); while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); for (QHash::iterator it = file->m_children.begin(); it != file->m_children.end(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) pending.push(child); ret.append(child->m_fileInfo.filePath()); } } return ret; } // Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m) { typedef QHash::const_iterator ChildConstIterator; const QChar slash = QLatin1Char('/'); const ChildConstIterator cend = m_root->m_children.constEnd(); for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) { const RCCFileInfo *child = it.value(); QString childName = path; childName += slash; childName += child->m_name; if (child->m_flags & RCCFileInfo::Directory) { resourceDataFileMapRecursion(child, childName, m); } else { m.insert(childName, child->m_fileInfo.filePath()); } } } RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap() const { ResourceDataFileMap rc; if (m_root) resourceDataFileMapRecursion(m_root, QString(QLatin1Char(':')), rc); return rc; } bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &errorDevice) { m_errorDevice = &errorDevice; //write out if (m_verbose) m_errorDevice->write("Outputting code\n"); if (!writeHeader()) { m_errorDevice->write("Cannot write header\n"); return false; } if (m_root) { if (!writeDataBlobs()) { m_errorDevice->write("Cannot write data blobs.\n"); return false; } if (!writeDataNames()) { m_errorDevice->write("Cannot write file names\n"); return false; } if (!writeDataStructure()) { m_errorDevice->write("Cannot write data tree\n"); return false; } } if (!writeInitializer()) { m_errorDevice->write("Cannot write footer\n"); return false; } outDevice.write(m_out.constData(), m_out.size()); return true; } void RCCResourceLibrary::writeHex(quint8 tmp) { const char digits[] = "0123456789abcdef"; writeChar('0'); writeChar('x'); if (tmp < 16) { writeChar(digits[tmp]); } else { writeChar(digits[tmp >> 4]); writeChar(digits[tmp & 0xf]); } writeChar(','); } void RCCResourceLibrary::writeNumber2(quint16 number) { if (m_format == RCCResourceLibrary::Binary) { writeChar(number >> 8); writeChar(number); } else { writeHex(number >> 8); writeHex(number); } } void RCCResourceLibrary::writeNumber4(quint32 number) { if (m_format == RCCResourceLibrary::Binary) { writeChar(number >> 24); writeChar(number >> 16); writeChar(number >> 8); writeChar(number); } else { writeHex(number >> 24); writeHex(number >> 16); writeHex(number >> 8); writeHex(number); } } bool RCCResourceLibrary::writeHeader() { if (m_format == C_Code) { writeString("/****************************************************************************\n"); writeString("** Resource object code\n"); writeString("**\n"); writeString("** Created: "); writeByteArray(QDateTime::currentDateTime().toString().toLatin1()); writeString("\n** by: The Resource Compiler for Qt version "); writeByteArray(QT_VERSION_STR); writeString("\n**\n"); writeString("** WARNING! All changes made in this file will be lost!\n"); writeString( "*****************************************************************************/\n\n"); writeString("#include \n\n"); } else if (m_format == Binary) { writeString("qres"); writeNumber4(0); writeNumber4(0); writeNumber4(0); writeNumber4(0); } return true; } bool RCCResourceLibrary::writeDataBlobs() { Q_ASSERT(m_errorDevice); if (m_format == C_Code) writeString("static const unsigned char qt_resource_data[] = {\n"); else if (m_format == Binary) m_dataOffset = m_out.size(); QStack pending; if (!m_root) return false; pending.push(m_root); qint64 offset = 0; QString errorMessage; while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); for (QHash::iterator it = file->m_children.begin(); it != file->m_children.end(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) pending.push(child); else { offset = child->writeDataBlob(*this, offset, &errorMessage); if (offset == 0) { m_errorDevice->write(errorMessage.toUtf8()); return false; } } } } if (m_format == C_Code) writeString("\n};\n\n"); return true; } bool RCCResourceLibrary::writeDataNames() { if (m_format == C_Code) writeString("static const unsigned char qt_resource_name[] = {\n"); else if (m_format == Binary) m_namesOffset = m_out.size(); QHash names; QStack pending; if (!m_root) return false; pending.push(m_root); qint64 offset = 0; while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); for (QHash::iterator it = file->m_children.begin(); it != file->m_children.end(); ++it) { RCCFileInfo *child = it.value(); if (child->m_flags & RCCFileInfo::Directory) pending.push(child); if (names.contains(child->m_name)) { child->m_nameOffset = names.value(child->m_name); } else { names.insert(child->m_name, offset); offset = child->writeDataName(*this, offset); } } } if (m_format == C_Code) writeString("\n};\n\n"); return true; } static bool qt_rcc_compare_hash(const RCCFileInfo *left, const RCCFileInfo *right) { return qt_hash(left->m_name) < qt_hash(right->m_name); } bool RCCResourceLibrary::writeDataStructure() { if (m_format == C_Code) writeString("static const unsigned char qt_resource_struct[] = {\n"); else if (m_format == Binary) m_treeOffset = m_out.size(); QStack pending; if (!m_root) return false; //calculate the child offsets (flat) pending.push(m_root); int offset = 1; while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); file->m_childOffset = offset; //sort by hash value for binary lookup QList m_children = file->m_children.values(); qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash); //write out the actual data now for (int i = 0; i < m_children.size(); ++i) { RCCFileInfo *child = m_children.at(i); ++offset; if (child->m_flags & RCCFileInfo::Directory) pending.push(child); } } //write out the structure (ie iterate again!) pending.push(m_root); m_root->writeDataInfo(*this); while (!pending.isEmpty()) { RCCFileInfo *file = pending.pop(); //sort by hash value for binary lookup QList m_children = file->m_children.values(); qSort(m_children.begin(), m_children.end(), qt_rcc_compare_hash); //write out the actual data now for (int i = 0; i < m_children.size(); ++i) { RCCFileInfo *child = m_children.at(i); child->writeDataInfo(*this); if (child->m_flags & RCCFileInfo::Directory) pending.push(child); } } if (m_format == C_Code) writeString("\n};\n\n"); return true; } void RCCResourceLibrary::writeMangleNamespaceFunction(const QByteArray &name) { if (m_useNameSpace) { writeString("QT_MANGLE_NAMESPACE("); writeByteArray(name); writeChar(')'); } else { writeByteArray(name); } } void RCCResourceLibrary::writeAddNamespaceFunction(const QByteArray &name) { if (m_useNameSpace) { writeString("QT_PREPEND_NAMESPACE("); writeByteArray(name); writeChar(')'); } else { writeByteArray(name); } } bool RCCResourceLibrary::writeInitializer() { if (m_format == C_Code) { //write("\nQT_BEGIN_NAMESPACE\n"); QString initName = m_initName; if (!initName.isEmpty()) { initName.prepend(QLatin1Char('_')); initName.replace(QRegExp(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_")); } //init if (m_useNameSpace) writeString("QT_BEGIN_NAMESPACE\n\n"); if (m_root) { writeString("extern Q_CORE_EXPORT bool qRegisterResourceData\n " "(int, const unsigned char *, " "const unsigned char *, const unsigned char *);\n\n"); writeString("extern Q_CORE_EXPORT bool qUnregisterResourceData\n " "(int, const unsigned char *, " "const unsigned char *, const unsigned char *);\n\n"); } if (m_useNameSpace) writeString("QT_END_NAMESPACE\n\n\n"); QString initResources = QLatin1String("qInitResources"); initResources += initName; writeString("int "); writeMangleNamespaceFunction(initResources.toLatin1()); writeString("()\n{\n"); if (m_root) { writeString(" "); writeAddNamespaceFunction("qRegisterResourceData"); writeString("\n (0x01, qt_resource_struct, " "qt_resource_name, qt_resource_data);\n"); } writeString(" return 1;\n"); writeString("}\n\n"); writeString("Q_CONSTRUCTOR_FUNCTION("); writeMangleNamespaceFunction(initResources.toLatin1()); writeString(")\n\n"); //cleanup QString cleanResources = QLatin1String("qCleanupResources"); cleanResources += initName; writeString("int "); writeMangleNamespaceFunction(cleanResources.toLatin1()); writeString("()\n{\n"); if (m_root) { writeString(" "); writeAddNamespaceFunction("qUnregisterResourceData"); writeString("\n (0x01, qt_resource_struct, " "qt_resource_name, qt_resource_data);\n"); } writeString(" return 1;\n"); writeString("}\n\n"); writeString("Q_DESTRUCTOR_FUNCTION("); writeMangleNamespaceFunction(cleanResources.toLatin1()); writeString(")\n\n"); } else if (m_format == Binary) { int i = 4; char *p = m_out.data(); p[i++] = 0; // 0x01 p[i++] = 0; p[i++] = 0; p[i++] = 1; p[i++] = (m_treeOffset >> 24) & 0xff; p[i++] = (m_treeOffset >> 16) & 0xff; p[i++] = (m_treeOffset >> 8) & 0xff; p[i++] = (m_treeOffset >> 0) & 0xff; p[i++] = (m_dataOffset >> 24) & 0xff; p[i++] = (m_dataOffset >> 16) & 0xff; p[i++] = (m_dataOffset >> 8) & 0xff; p[i++] = (m_dataOffset >> 0) & 0xff; p[i++] = (m_namesOffset >> 24) & 0xff; p[i++] = (m_namesOffset >> 16) & 0xff; p[i++] = (m_namesOffset >> 8) & 0xff; p[i++] = (m_namesOffset >> 0) & 0xff; } return true; } QT_END_NAMESPACE tools/binarycreator/rcc/rcc.h000066400000000000000000000112411325366651500165640ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef RCC_H #define RCC_H #include #include #include QT_BEGIN_NAMESPACE class RCCFileInfo; class QIODevice; class QTextStream; class RCCResourceLibrary { RCCResourceLibrary(const RCCResourceLibrary &); RCCResourceLibrary &operator=(const RCCResourceLibrary &); public: RCCResourceLibrary(); ~RCCResourceLibrary(); bool output(QIODevice &out, QIODevice &errorDevice); bool readFiles(bool ignoreErrors, QIODevice &errorDevice); enum Format { Binary, C_Code }; void setFormat(Format f) { m_format = f; } Format format() const { return m_format; } void setInputFiles(const QStringList &files) { m_fileNames = files; } QStringList inputFiles() const { return m_fileNames; } QStringList dataFiles() const; // Return a map of resource identifier (':/newPrefix/images/p1.png') to file. typedef QHash ResourceDataFileMap; ResourceDataFileMap resourceDataFileMap() const; void setVerbose(bool b) { m_verbose = b; } bool verbose() const { return m_verbose; } void setInitName(const QString &name) { m_initName = name; } QString initName() const { return m_initName; } void setCompressLevel(int c) { m_compressLevel = c; } int compressLevel() const { return m_compressLevel; } void setCompressThreshold(int t) { m_compressThreshold = t; } int compressThreshold() const { return m_compressThreshold; } void setResourceRoot(const QString &root) { m_resourceRoot = root; } QString resourceRoot() const { return m_resourceRoot; } void setUseNameSpace(bool v) { m_useNameSpace = v; } bool useNameSpace() const { return m_useNameSpace; } QStringList failedResources() const { return m_failedResources; } private: struct Strings { Strings(); const QString TAG_RCC; const QString TAG_RESOURCE; const QString TAG_FILE; const QString ATTRIBUTE_LANG; const QString ATTRIBUTE_PREFIX; const QString ATTRIBUTE_ALIAS; const QString ATTRIBUTE_THRESHOLD; const QString ATTRIBUTE_COMPRESS; }; friend class RCCFileInfo; void reset(); bool addFile(const QString &alias, const RCCFileInfo &file); bool interpretResourceFile(QIODevice *inputDevice, const QString &file, QString currentPath = QString(), bool ignoreErrors = false); bool writeHeader(); bool writeDataBlobs(); bool writeDataNames(); bool writeDataStructure(); bool writeInitializer(); void writeMangleNamespaceFunction(const QByteArray &name); void writeAddNamespaceFunction(const QByteArray &name); void writeHex(quint8 number); void writeNumber2(quint16 number); void writeNumber4(quint32 number); void writeChar(char c) { m_out.append(c); } void writeByteArray(const QByteArray &); void write(const char *, int len); const Strings m_strings; RCCFileInfo *m_root; QStringList m_fileNames; QString m_resourceRoot; QString m_initName; Format m_format; bool m_verbose; int m_compressLevel; int m_compressThreshold; int m_treeOffset; int m_namesOffset; int m_dataOffset; bool m_useNameSpace; QStringList m_failedResources; QIODevice *m_errorDevice; QByteArray m_out; }; QT_END_NAMESPACE #endif // RCC_H tools/binarycreator/rcc/rccmain.cpp000066400000000000000000000220631325366651500177700ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include "qcorecmdlineargs_p.h" #include #include #include #include #include QT_BEGIN_NAMESPACE void showHelp(const QString &argv0, const QString &error) { fprintf(stderr, "Qt resource compiler\n"); if (!error.isEmpty()) fprintf(stderr, "%s: %s\n", qPrintable(argv0), qPrintable(error)); fprintf(stderr, "Usage: %s [options] \n\n" "Options:\n" " -o file write output to file rather than stdout\n" " -name name create an external initialization function with name\n" " -threshold level threshold to consider compressing files\n" " -compress level compress input files by level\n" " -root path prefix resource access path with root path\n" " -no-compress disable all compression\n" " -binary output a binary file for use as a dynamic resource\n" " -namespace turn off namespace macros\n" " -project Output a resource file containing all\n" " files from the current directory\n" " -version display version\n" " -help display this information\n", qPrintable(argv0)); } void dumpRecursive(const QDir &dir, QTextStream &out) { QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); foreach (const QFileInfo &entry, entries) { if (entry.isDir()) { dumpRecursive(entry.filePath(), out); } else { out << QLatin1String("") << entry.filePath() << QLatin1String("\n"); } } } int createProject(const QString &outFileName) { QDir currentDir = QDir::current(); QString currentDirName = currentDir.dirName(); if (currentDirName.isEmpty()) currentDirName = QLatin1String("root"); QFile file; bool isOk = false; if (outFileName.isEmpty()) { isOk = file.open(stdout, QFile::WriteOnly | QFile::Text); } else { file.setFileName(outFileName); isOk = file.open(QFile::WriteOnly | QFile::Text); } if (!isOk) { fprintf(stderr, "Unable to open %s: %s\n", outFileName.isEmpty() ? qPrintable(outFileName) : "standard output", qPrintable(file.errorString())); return 1; } QTextStream out(&file); out << QLatin1String("\n" "\n"); // use "." as dir to get relative file paths dumpRecursive(QDir(QLatin1String(".")), out); out << QLatin1String("\n" "\n"); return 0; } int runRcc(int argc, char *argv[]) { QString outFilename; bool helpRequested = false; bool list = false; bool projectRequested = false; QStringList filenamesIn; QStringList args = qCmdLineArgs(argc, argv); RCCResourceLibrary library; //parse options QString errorMsg; for (int i = 1; i < args.count() && errorMsg.isEmpty(); i++) { if (args[i].isEmpty()) continue; if (args[i][0] == QLatin1Char('-')) { // option QString opt = args[i]; if (opt == QLatin1String("-o")) { if (!(i < argc-1)) { errorMsg = QLatin1String("Missing output name"); break; } outFilename = args[++i]; } else if (opt == QLatin1String("-name")) { if (!(i < argc-1)) { errorMsg = QLatin1String("Missing target name"); break; } library.setInitName(args[++i]); } else if (opt == QLatin1String("-root")) { if (!(i < argc-1)) { errorMsg = QLatin1String("Missing root path"); break; } library.setResourceRoot(QDir::cleanPath(args[++i])); if (library.resourceRoot().isEmpty() || library.resourceRoot().at(0) != QLatin1Char('/')) errorMsg = QLatin1String("Root must start with a /"); } else if (opt == QLatin1String("-compress")) { if (!(i < argc-1)) { errorMsg = QLatin1String("Missing compression level"); break; } library.setCompressLevel(args[++i].toInt()); } else if (opt == QLatin1String("-threshold")) { if (!(i < argc-1)) { errorMsg = QLatin1String("Missing compression threshold"); break; } library.setCompressThreshold(args[++i].toInt()); } else if (opt == QLatin1String("-binary")) { library.setFormat(RCCResourceLibrary::Binary); } else if (opt == QLatin1String("-namespace")) { library.setUseNameSpace(!library.useNameSpace()); } else if (opt == QLatin1String("-verbose")) { library.setVerbose(true); } else if (opt == QLatin1String("-list")) { list = true; } else if (opt == QLatin1String("-version") || opt == QLatin1String("-v")) { fprintf(stderr, "Qt Resource Compiler version %s\n", QT_VERSION_STR); return 1; } else if (opt == QLatin1String("-help") || opt == QLatin1String("-h")) { helpRequested = true; } else if (opt == QLatin1String("-no-compress")) { library.setCompressLevel(-2); } else if (opt == QLatin1String("-project")) { projectRequested = true; } else { errorMsg = QString::fromLatin1("Unknown option: '%1'").arg(args[i]); } } else { if (!QFile::exists(args[i])) { qWarning("%s: File does not exist '%s'", qPrintable(args[0]), qPrintable(args[i])); return 1; } filenamesIn.append(args[i]); } } if (projectRequested && !helpRequested) { return createProject(outFilename); } if (!filenamesIn.size() || !errorMsg.isEmpty() || helpRequested) { showHelp(args[0], errorMsg); return 1; } QFile errorDevice; errorDevice.open(stderr, QIODevice::WriteOnly|QIODevice::Text); if (library.verbose()) errorDevice.write("Qt resource compiler\n"); library.setInputFiles(filenamesIn); if (!library.readFiles(list, errorDevice)) return 1; // open output QFile out; QIODevice::OpenMode mode = QIODevice::WriteOnly; if (library.format() == RCCResourceLibrary::C_Code) mode |= QIODevice::Text; if (outFilename.isEmpty() || outFilename == QLatin1String("-")) { // using this overload close() only flushes. out.open(stdout, mode); } else { out.setFileName(outFilename); if (!out.open(mode)) { const QString msg = QString::fromUtf8("Unable to open %1 for writing: %2\n").arg(outFilename).arg(out.errorString()); errorDevice.write(msg.toUtf8()); return 1; } } // do the task if (list) { const QStringList data = library.dataFiles(); for (int i = 0; i < data.size(); ++i) { out.write(qPrintable(QDir::cleanPath(data.at(i)))); out.write("\n"); } return 0; } return library.output(out, errorDevice) ? 0 : 1; } QT_END_NAMESPACE tools/binarycreator/resources/000077500000000000000000000000001325366651500171105ustar00rootroot00000000000000tools/binarycreator/resources/copylibsintobundle.sh000066400000000000000000000160071325366651500233600ustar00rootroot00000000000000#!/bin/sh ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# # this script puts all libs directly needed by the bundle into it QTDIR="" IS_DEBUG=0 HAVE_CORE=0 HAVE_SVG=0 HAVE_PHONON=0 HAVE_SCRIPT=0 HAVE_SQL=0 HAVE_WEBKIT=0 function handleFile() { local FILE=$1 local BUNDLE=$2 # all dynamic libs directly needed by the bundle, which are not in /System/Library or in /usr/lib (which are system default libs, which we don't want) local LIBS=`xcrun otool -L $FILE | grep -v 'executable_path' | grep -v '/System/Library' | grep -v '/usr/lib' | grep '/' | sed -ne 's,^ *\(.*\) (.*,\1,p'` local lib for lib in $LIBS; do local NAME=`basename $lib` if echo $NAME | grep 'QtCore' >/dev/null; then HAVE_CORE=1 QTDIR=`echo $lib | sed -ne 's,^\(.*\)/lib/[^/]*QtCore.*$,\1,p'` if echo $NAME | grep 'debug' >/dev/null; then IS_DEBUG=1 fi elif echo $NAME | grep 'QtSvg' >/dev/null; then HAVE_SVG=1 elif echo $NAME | grep 'phonon' >/dev/null; then HAVE_PHONON=1 elif echo $NAME | grep 'QtScript' >/dev/null; then HAVE_SCRIPT=1 elif echo $NAME | grep 'QtSql' >/dev/null; then HAVE_SQL=1 elif echo $NAME | grep 'QtWebKit' >/dev/null; then HAVE_WEBKIT=1 fi if [ `basename $FILE` != $NAME ]; then # this part handles libraries which are Mac OS X frameworks if echo $lib | grep '\.framework' >/dev/null; then local FRAMEWORKPATH=`echo $lib | sed -ne 's,\(.*\.framework\).*,\1,p'` local FRAMEWORKNAME=`basename $FRAMEWORKPATH` local NEWFRAMEWORKPATH=`echo $lib | sed -ne "s,.*\($FRAMEWORKNAME\),\1,p"` # Qt installed via the precompled binaries... if [ $FRAMEWORKPATH = $FRAMEWORKNAME ]; then FRAMEWORKPATH="/Library/Frameworks/$FRAMEWORKNAME" if [ ! -e "$FRAMEWORKPATH" ]; then echo "Framework $FRAMEWORKNAME not found." exit 1 fi fi if [ ! -e "$BUNDLE/Contents/Frameworks/$NEWFRAMEWORKPATH" ]; then echo Embedding framework $FRAMEWORKNAME # copy the framework into the bundle cp -R $FRAMEWORKPATH $BUNDLE/Contents/Frameworks # remove debug libs we've copied find $BUNDLE/Contents/Frameworks/$FRAMEWORKNAME -regex '.*_debug\(\.dSYM\)*' | xargs rm -rf handleFile "$BUNDLE/Contents/Frameworks/$NEWFRAMEWORKPATH" "$BUNDLE" fi # and inform the dynamic linker about this xcrun install_name_tool -change $lib @executable_path/../Frameworks/$NEWFRAMEWORKPATH $FILE # this part handles 'normal' dynamic libraries (.dylib) else if [ ! -e "$BUNDLE/Contents/Frameworks/$NAME" ]; then echo Embedding library $NAME # Qt installed via the precompled binaries... if [ $lib = $NAME ]; then lib="/Library/Frameworks/$NAME" if [ ! -e "$lib" ]; then lib="/usr/lib/$NAME" fi if [ ! -e "$lib" ]; then echo "Library $NAME not found." exit 1 fi fi # copy the lib into the bundle cp $lib $BUNDLE/Contents/Frameworks handleFile "$BUNDLE/Contents/Frameworks/$NAME" "$BUNDLE" fi # and inform the dynamic linker about this xcrun install_name_tool -change $lib @executable_path/../Frameworks/$NAME $FILE fi fi done } function handleQtPlugins() { local PLUGINPATH=$QTDIR/plugins # QTDIR was not found, then we're using /Developer/Applications/Qt if [ "$PLUGINPATH" = "/plugins" ]; then PLUGINPATH="/Developer/Applications/Qt/plugins" fi CLASS=$1 EXECUTABLE=$2 BUNDLE=$3 mkdir -p $BUNDLE/Contents/plugins/$CLASS echo Add $CLASS plugins for plugin in `ls $PLUGINPATH/$CLASS/*`; do plugin=`basename $plugin` if echo $plugin | grep 'debug' >/dev/null; then #if [ $IS_DEBUG -eq 1 ]; then cp "$PLUGINPATH/$CLASS/$plugin" $BUNDLE/Contents/plugins/$CLASS xcrun install_name_tool -change $plugin @executable_path/../plugins/$CLASS/$plugin $EXECUTABLE handleFile $BUNDLE/Contents/plugins/$CLASS/$plugin $BUNDLE #fi else #if [ $IS_DEBUG -eq 0 ]; then cp "$PLUGINPATH/$CLASS/$plugin" $BUNDLE/Contents/plugins/$CLASS xcrun install_name_tool -change $plugin @executable_path/../plugins/$CLASS/$plugin $EXECUTABLE handleFile $BUNDLE/Contents/plugins/$CLASS/$plugin $BUNDLE #fi fi done } # the app bundle we're working with BUNDLE=$1 # the executable inside of the bundle EXECUTABLE=$BUNDLE/Contents/MacOS/`xargs < $BUNDLE/Contents/Info.plist | sed -ne 's,.*CFBundleExecutable \([^<]*\).*,\1,p'` mkdir -p $BUNDLE/Contents/Frameworks handleFile $EXECUTABLE $BUNDLE if [ $HAVE_CORE -eq 1 ]; then handleQtPlugins "imageformats" "$EXECUTABLE" "$BUNDLE" fi if [ $HAVE_SVG -eq 1 ]; then handleQtPlugins "iconengines" "$EXECUTABLE" "$BUNDLE" fi if [ $HAVE_PHONON -eq 1 ]; then handleQtPlugins "phonon_backend" "$EXECUTABLE" "$BUNDLE" fi if [ $HAVE_SQL -eq 1 ]; then handleQtPlugins "sqldrivers" "$EXECUTABLE" "$BUNDLE" fi if [ $HAVE_WEBKIT -eq 1 ]; then handleQtPlugins "codecs" "$EXECUTABLE" "$BUNDLE" fi tools/binarycreator/resources/default_icon_mac.icns000066400000000000000000003503201325366651500232450ustar00rootroot00000000000000icnsÐÐis32·ƒ1rlr1ˆ8‘Ž‘8ˆ@ĻĪĻ@… FjrtĄĢĒl`V- “ŅÜŠŽĨ’žŧŠ\ ĨŧŽz†‰f #ąÉ\UpjoY`p € 0ŊĘÆJ?Y?GŽo€ :Ŧšš™48Īs€ GŊđĀŧĨ^›ĨĶ”{€ SķģģēÎÚšœ™‚'€ `ÄĮĩĄ˜’•ž›Š-€ fĩķĖÆŧļļĐ Š5€ XœĄ›ĨŦ§ ‘Ž€3€ ,b;Wc]CBDitf€!€ %#ƒ1rlr1ˆ8‘Ž‘8ˆ@ĻĪĻ@… FjrtĄĢĒl`V- “ŅÜŠŽĨ’žŧŠ\ ĨŧŽ‚z…‰f #ąÉ\UpjoY_p € 0ŊĘÆJ@X?GŽo€ :Ŧšš™58Īs€ GŊđĀŧĨ^›ĨĶ”{€ SķģģēÎÚšœ™‚'€ `ÄĮĩĄ˜’•ž›Š-€ fĩķĖÆŧļļĐ Š5€ XœĄ›ĨŦ§ €3€ ,b;Wc]CBDivf€!€ %#ƒ1rlq1ˆ8‘Ž‘8ˆ@ĻĪĻ@… IlttĄĢĒmbX- —ÕÞŠŽĨ’ĄÁŪ_ Đŋ‘€Ž}‡k )·Î^TpkpX`–s € 0īÏĖL>Z?Gģ•s€ =ēŋŋž68•Ļ”x€ GĩŋÅĀŦa Ŧޚ€ € V―đđļŅßĀĄŸ•†'€ dĘĖŧĐ ˜–šĪĄ‘0€ iŧŧÐËĀ――ŊĶŽ‡8€ ]Ē§ĒŠŊŠĨ•”˜‡5€ 2kCbogKJOokr!€ %"" ' s8mk]·ŧ·]væëæv |ðõð| T‚ŧũúũŧ‚}?ģųúüĸĸĸüúö‹ Æĸĸĸĸĸĸĸĸý ,Ôĸĸĸĸĸĸĸĸþē;āĸĸĸĸĸĸĸĸĸÃKęĸĸĸĸĸĸĸĸĸŌ)]ņĸĸĸĸĸĸĸĸĸÞ7qöĸĸĸĸĸĸĸĸĸčH…úĸĸĸĸĸĸĸĸĸðZ ™ýĸĸĸĸĸĸĸĸĸõnýĸĸĸĸĸĸĸĸĸös …õúúúúúúúúúë]G’˜˜˜˜˜˜˜˜˜Š0il32ω)bf€hfb%”0{}€}{0”+ˆ†‰Š‰†ˆ+”0ĨŽēēģŽĨ.”-žĒĢĒž+”-™ŸĢĨ̟š-Z^y{{wešĢĪĶĢ̚_iki[M4‡*ŪĩØßÞᝋĶĒĢĒĨŽŒÁđšĻža‡8ÆČŽŌÃՉˆŸž Œ|š­Ļwķk‡GÎąĻäČ݈}–””’–}…ÎđÁgŪv †VŌÃÞĒ™Šmv‹‡‰ˆ‰z`•ƒmŠ~†cÓÏÛ:_|Xqzy{y{q^€dOk̇†tÖĮÕš>aud€k lietc<Đxš"†ÖĮČÜĀ6LaZ^]^Z_M8œĮp“—.…‰ÖČĮŧÍē5BUMPMSC4ŠĐuŒ™4…Ž…ŋĀšžŸ.5I?I5-ŽˆŦ„O€@… ĀĪÆÁķžŽ‚-%B$.y„‘ž§Ž›I…%ŽķĨÍ―ŋŧŦ‡,2ƒŽĄĪĄĢ}œU…1ī•đÄŧššđ‘‰O”†xģ  ›Ļh\…=ž‹ĩÁļđ·ĐâÞĐŊđÖĪ ž—Ķk‰f „KÂĶĒÂļīģēĶĖäÞÕŦœ žš™˜u’l„VÂļÎąģķŊąŪž››•š ›ššŽ—”’t„dÃŊČðĻĨąŪŽŽĐĨ̟ž–ƒĢŊ‰Žy„oÁīĐŋņÄ ™žĄžž›“Ž“ŧŧ„‹‰}%ƒ zÄĩīĻĐŲįÕŧĐĄœžĻļģŧĨ…Š‹+ƒ}n‘ĩ§Ī”žđÏÖÛÚÕŌī}|„†Jj4ƒ€ŠŋËÃÃÄ―°ŦĻŽŠĐĒĪŽŪĻĨĢĒ‘ˆ3„;vyCSZ_emxvS[W[YW]€yxw~h„#Yf(6KPSW[\8A6886>\qySfN „ Yf?4?KUZ^^GI€EDK_jl\mC„€‚€ Ą‰)bffhhfb%”0{}€}{0”+ˆ†‰Š‰†ˆ+”.ĨŽēģģŽĨ.”+žĒĢĒž+”-™ŸĢĨ̟›-Z^y{{we™ĢĢĶĢĄš_iki[M4‡*ŪĩØßÞᝌĶĒĢĒĨŽÁđšĻža‡8ÆČŽŌÃՉˆ žŸ‹|š­Ļwķk‡GÎąĻäČ݈}–’”’–~…ÎđÂgŪv †VŌÃÞ̚Šmv‹‡‰‡‰z`•ƒmŠ~†cÓÏÛ:`|Xqz{z{{q^dOk̇†tÖĮÕš>cudikmiidtd<Đxš"†ÖĮČÜĀ6LaZ^]\ZaL7œĮp“—.…‰ÖČĮŧÍē5BUMPMSC4ŠĐuŒ™4…Ž…ŋĀšžŸ/5I?I5-ŽˆŦ„O€@… ĀĪÆÁķžŽ‚-%B$.y„‘ž§Ž›I…%ŽķĨÍ―ŋŧŦ‡,0„ŽĄĪĄĢ}œU…1ī•đÄŧššđ‘‰O“†xģ  ›Ļh\…=ž‹ĩÁļđ·ĐâÞĐŊđÖĪ ž—Ķk‰f „KÂĶĒÂļīģēĶĖäÞÕŦœ žš™˜u’l„VÂļÎąģķŊąŪž››•š ›ššŽ—”’t„dÃŊČðĻĨąŪŽŽĐĨ̟ž–ƒĢŊ‰Žy„oÁīĐŋņÄ ™žĄžž›“Ž“ŧŧ„‹‰}%ƒ zÄĩīĻĐŲįÕŧĐĄœžĻļģŧĨ…Š‹+ƒ}n‘ĩ§Ī”žđÏÖÛÚÕŌī}|„†Jj4ƒ€ŠŋËÃÃÄ―°ŦĻŽŠĐĒĪŽŽĻĨĢĒ‘ˆ3„;vyCSZ_emxvS[W[ZW]€yxw~h„#Yf(6KPSW[\8A6866>\rzSfN „Yf?4?KUZ^^GIEFEEK_ln\mC„€‚€ Ą‰)bf€hfb%”0{}€}{0”+ˆ†‰Š‰†ˆ+”.ĨŽēģģŽĨ.”-žĒĢĒž+”-™ŸĢĨ̟š-Z`}{g™ĢĪĨĪ̚`€m]O9‡*ģžÞåãæ ‹ĶĒĢĒĨސĮĀĀŽĨe‡=ĘϰŅÁՈ‰ ž ŠĀą­|žn‡GÓķŪåČ߈}–’”’–}ˆÔūĮlģy †YŲĮā͜Žnv‰‰Š‡‹wc˜‡’pŊ„†eÚÕā8^~Xq|xz{zq\dNqό†xÜÍÚŋ>bvdkkmkietc;Ŋ}Ÿ”"†„ÜÍÍäÆ6JaZ_]^Z_L8ĒÎs™.…ÛÍÍŋŌđ5AUNONUC6ĢąŊ{‘ 7…’‰ÃÅĀĢĒ’.5I?I5-…“°ŠT†C…ĨÆĐËĮžĢ’†.%B$/~ˆ–ĪŪ“‡ĄK…+ēŧŠÓÃÄÁąŠ-2‡’ĶЧĒЂĢW…5š›ŋĘÁĀŋū˜ĒRšŒ}đĨ§ Ūm“`…@ÁŧÉŋū―ŊæáŪķūÚŠĶĶΜŽpi „MČŽĻĮūš·ļŦÏįāÜēĒĶĒ Ÿžz˜q„YČ―Ó·đ―īĩķĶĄĄ› ĶĪ Ą”š˜y„gÉĩÎņŊŦ·īēą­ŦĐĨĢĄœ‹Đģ”„sĮšŊÃņČĶĄĪ§ĨĨĄš•”™ÁÁŒ‘ƒ%ƒ ~ÉŧšŪŊÞëŨŋ°§ĒĢŽ―šĀŦ‹•ˆ.ƒt—ŧ­ŽœĢūŌŨÜÝØÔš“ƒ‚ІŒQp7ƒ†ŊÄÏÉĮČÁķą­ąąŪĐϰēŪĻǧ–8„@ƒH\cjpw‚Zc^aa^f‡‘”~ˆo„&es->UZ_bhg?J=??=DtURmpW „drE:EV_fjhNRKLLKRqMHnxI„€‚€ $Ąl8mk>‚……………‚>pÚßßßßßÚp~ðõõõõõð~{ðõõõôõð{wïõõõõõïw xðõõõõõïw ^€ƒšũúúúúúũđƒz6+Ėųųųųųüĸĸĸĸĸĸĸüųųųųóƒ;Þĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸü› Kčĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸþ­\ïĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸūoõĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĖ'‚ųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŲ4 •üĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸäCĻýĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸėTđþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸóg$Éĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸũz0Öĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸû ?áĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸý Pëĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸþēcņĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ vöĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŅ, ŠúĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÝ;žüĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸčK°þĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸï^ Āĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõp#ŋþĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸôr üĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸįTŒųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÜA wíũũũũũũøøøøøøøøøøøøøøũĘ4@ŸŸŸŸŸŸ              r*222222222222222222220! it32ghŽ€<˜;<ÛKc™|bKÛhž™™žhÛ“Īŧķ‘ĩķŧĢ“Û:>€G„F€EDEƒFGGF>:Û!Vœ—™™›žŸĄĒĪĨĶĻĐŠŠĐĻĶĨĪĒĄŸž›™™—TÛ!Íąģĩī··ļđššžž―ūū―žžŧšđļ··īĩģąÍ~Û}―œ€ž€žœūzÛ|ŋĄƒĢ‹ĪƒĢĄŋyÛ|ūĄ†Ģ…ΆĢĄūyÛ{― €Ē‘̀Ǡ―xÛzŧ ‚ĒĢ‚Ē ―xÛzšŸĄ…Ē…Ģ†ĒŸžvÛx🡕ĒĄŸđuÛx·ž‚ĄĒ‚ĄžđtÛwĩ€ ƒĄ…ĒƒĄ€ ķsÛvĩ€Ÿ  Ą  €ŸĩrÛuģ›žƒŸ‰ ƒŸž›ģqÛt皜žŸžœšēpÛq°™›€œž…Ÿž€œ›™°oÛrۘ›œˆ€œ›˜ŪnÛqŽ—š‚›œƒ›œƒ›€š™ŽmÃŌÎÍŨÔŨÐÏŌŌŅŅÐÐÏπÎ̀ĖË^nŦ–˜€™ƒš„›‚š€™ ˜–Ŧl[šđļļ·ķĩĩīģ ēąŪŠĐŪŠ°ŽĐÆŠ ‘ÖŌ҇O|ÏŅÓӁŅÐÎÐρÎÍËumĻ•——˜‚™šƒ™€˜ ——–Đjpžŧšđļļ··€ķ ĩĩīģŊŦĻj8]Ķ­ĩšĐ~ÛŲ–!‹ÝÔÕÔÓŌŌ€Ņ ÐÎÎÏÎÎÍukĶ“––‚—‰˜‚— ––“§ip―ŧšđđļ··€ķ ĩĩīēŊ°€(hīģ_Ļ X‹ß۝3ƒIœÝ€ÖÔӀŌŅЀπÎujĢ“”€•–‡—–€• ”“Ģhpžŧšđđļ··€ķĩīģģ°ą>…Huģķj5§WšâÖÜžÎÛÕÕÔÔÓÓŌŌŅ€ÐρÎuiĒ‘’’“€”‰•€”“’’‘Ēgoŧ€šļ€·ķ€ĩ īīēą°īŠĪ§°­ķv7§ VĒáŨŨÝÜŲÕØŲÕԀÓŌŌŅŅЁÏÎth ‘‚’“‚’‘  eožŧššđđļ ķķ――ģąģąąŪ­ķ6§V­āŨŨÖÕÔŨĘĐŅÓŌÓŌÔŌÓŌŅÐÏŌÐÐufž€Ž€€‡‘€€€Ž ždnšđļļ·ķķĩīģąĩĶ·ēŊŪŪ­Ŧē‹7§XžÞÖցÕ}yŨŌÔÄÁŊšīķšļžąķēgeœŒŒ‚އ‚Ž€Œœbnđ€ļ·ķĩīģēģļJOŊ°ŪŪŽŠ°–:§[ĮÚրÕØÏh‡ØŅÔÄšĪáīÉÛâåŽŲÐxcšˆ€‹€Œ‹€Œ€‹Ššbnđļļ·ķĩīģēēąŧ[&Ģē­ŽŽŦŪĄ@§aŅŲրÕØÉd’ÚŅÔÂĩąŨÉĖÓŅŌÉÏÏub˜…ˆ‰‰€Š‹‹€Š ‰‰ˆ…˜anđļļ·ķ€ĩīģ€ēąŧf%›ī­€ŦŽĶC§eÔŨÕŲĀ_˜ÚÐŌĀīŪŋūđÃū°Á―ēka•…††‡€ˆ…‰€ˆ‡ ††…•_mđļ·ķķĩĩī€ģēą°ŧm"īŽŽŦĐŦĐH§nŲÖÕÕÔÔŲ·[žÚÐÐūģąđļšđīŧ·ķši`”‚„„€…†€…„„‚”^mļ·ķķĩĩīīģģēēą°šx„ĩŽŦŠĐŠŊQ§{݀ÔÓÓÚ­X§ŲÐÐÁąžÃŋÁ·īŋ―ļķn_’€‚‚€ƒ„€ƒ ‚‚€’]mļ·ķķĩĩī€ģēēą°ļƒ yģŠŦŠĐĐē\ĶU‰ß€ÓŌŌÛĶY°ŨÐŌÃķŊĩķĩą°īīēŊf]Ž}€€‚‰‚‚€}[l··ķ€ĩīģēē€ą°ķ"sī‚Đąi6ĨT—ß€Ó ŌŌڟZļÖÏŌŋŋ€―žū‚―j\Ž{}€~‡€€~}{ŽZk·ķĩīģē€ą°Ŋī—&hīĐϰt5ĨR Þ€Ó ŌŅŲ‘WšÓÎÍπÎÍ€Ë ĘÉÉČp[Œy{{|}ƒ~}|{{yŒYkĩķ€ĩīģģē€ą°ŊēŸ*\īŠĐ€ĻŊ|4ĨTŦÜÓŌŌŅÐ֋ŠÞÕĖĖ€ÍĖ€ĘÉȀĮnZŠwyyzŒ{€z yyw‹Xiģ°ēĩĩģ€ēąą°ŊŊēĒ.QēŦĐϧ§Ūˆ5ĨVđ؃ŅÃëþũúų†ø‹Y‡uwxyx wu‰WŒóōÅąī€ē ą°°ŊŊŪŊĶ6FŪŠ€Ļ§Š’8ĨYÃւŅÐÐâūŸĶœšš™—•’‘‹‹‘QT…r€u€v‹wv uur‡SS’emprsvwxyz{„ƒ“?=ĻЧĶĨĨĻ›=Ĩ_ËÕŅŅ€ÐÏÏːixve`_]ZWTQMKHK+R„pr€st‡ut€srp…P,KHKNQTW\]a`evxj„H8ĨЧĶĨĨ§ AĨdŅÔŅŅЁÏΜtz‹‰wrsqnliecae6N‚oqrqo„L7daceilnpssv‡‹{s‘S2ŸŠ€ĶĨĨĢFĨlŨÓŅÐЁÏÎΈ‡Š—–ˆ‚}xwsqw?Kmo‚p‹q‚pomI@wqswy|ƒ‡–˜‹‡ˆš]+—Ŧ€ĶĨĪĻPĨyÚŅЁπПZ\YT_`URRQP€NS/N€jlmn€mllj€M0R€NPQRRT`_UW\YŽe&­€ĶĨĪŠ]ĪO†ÜŅ€ÐρÎÍpJCPRJGFDB?<:@jzfhƒi‰jƒi hf{i@:RN=;=$<=MR@W{.$ƒ‘“•—™›žŸŸĐimœj\e-27*%'%)73,a` 6loqsvy~ƒ‡Œ”–™›œ€ž ž €‰Ąžœ€šĄf6ŸIÓ€Į ÆĘđgĪËÃÃÂÁ€Ā7ūŧš·ĩąŦĶĄš”Ž†{xtsCWf,.5(#%%#(40+c[Clkoquz…ŠŽ‘•™›œžŸ€ Ÿ NJf– œšš˜Ÿo7ŸI™Ō€ĮÆÎfÁČÄÀÂ7Áŋŋū―ŧļķēŽ§Ģœ•†€{vpoCQh,,3( &3-*dV8s{ppuz€†Œ‘”—›œœžŸ€ ‚Ÿ žŸœWq̜šš˜žv7Ÿ IĢŅĮĮÆĮÂeƒÍĂÂ6ÁĀŋūū―ŧđīēŪŠĪž–ŽˆlbnkELh,)2$$1**fP@BCFGHILMOPRSVXZ€[€\aH4B9>>?>\WX\`defhijjln[LZsrqqR: ›$,N]aa_cioqSA:=BEILMOPRTUVXZ\]^_adeg€fhglV D)P G#$L L )M M#icdhlnopqsttuweYh}{{y[>œ#(L]`a`cioqV><.ƒ20029CNUZ]^__`acd‚efjZ L4 )WP. /TT* 3UU& /jfgkR,O:Lmnqrl[^qusucJ 9Ubcbchnvh>2†4 2137=FKQVZ]_aab7afW'%I:%3R&%L6&7O&&O4%:P%&P2&5bZTRQRUZYP[ddilnh[buwvwdHž DTUUWZZXLA…B‚CBA@@ABDEF€G‚HGEEFHEFH€F HGGHGGHIFFH€GHDBBCB€A CCDFIKLLQ[€fgO(ž$€%€&(€*ƒ+‚,†-„.//1202€3€4 55657769889::9€;:<==>?@@€A@@??A?ĸĸĸĸĸĸĸĸŽ€<˜;<ÛKc™|bKÛhž™™žhÛ“Īŧķ‘ĩķŧĢ“Û:>€G„F€EDEƒFGGF>:Û!Vœ—™™›žŸĄĒĪĨĶĻĐŠŠĐĻĶĨĪĒĄŸž›™™—TÛ!Íąģĩī··ļđššžž―ūū―žžŧšđļ··īĩģąÍ~Û}―œ€ž€žœūzÛ|ŋĄƒĢ‹ĪƒĢĄŋyÛ|ūĄ†Ģ…ΆĢĄūyÛ{― €Ē‘̀Ǡ―xÛzŧ ‚ĒĢ‚Ē ―xÛzšŸĄ…Ē…Ģ†ĒŸžvÛx🡕ĒĄŸđuÛx·ž‚ĄĒ‚ĄžđtÛwĩ€ ƒĄ…ĒƒĄ€ ķsÛvĩ€Ÿ  Ą  €ŸĩrÛuģ›žƒŸ‰ ƒŸž›ģqÛt皜žŸžœšēpÛq°™›€œž…Ÿž€œ›™°oÛrۘ›œˆ€œ›˜ŪnÛqŽ—š‚›œƒ›œƒ›€š™ŽmÃŌÎÍŨÔŨÐÏŌŌŅŅÐÐÏπÎ̀ĖË^nŦ–˜€™ƒš„›‚š€™ ˜–Ŧl[šđļļ·ķĩĩīģ ēąŪŠĐŪŠ°ŽĐÆŠ ‘ÖŌ҇O|ÏŅÓӁŅÐÎÐρÎÍËumĻ•——˜‚™šƒ™€˜ ——–Đjpžŧšđļļ··€ķ ĩĩīģŊŦĻj8]Ķ­ĩšĐ~ÛŲ–!‹ÝÔÕÔÓŌŌ€Ņ ÐÎÎÏÎÎÍukĶ“––‚—‰˜‚— ––“§ip―ŧšđđļ··€ķ ĩĩīēŊ°€)hīģ_Ļ X‹ß۝3ƒIœÝ€ÖÔӀŌŅЀπÎujĢ“”€•–‡—–€• ”“Ģhpžŧšđđļ··€ķĩīģģ°ą>†Huģķj5§WšâÖÜžÎÛÕÕÔÔÓÓŌŌŅ€ÐρÎuiĒ‘’’“€”‰•€”“’’‘Ēgoŧ€šļ€·ķ€ĩ īīēą°īŠĪ§°­ķv7§ VĒáŨŨÝÜŲÕØŲÕԀÓŌŌŅŅЁÏÎth ‘‚’“‚’‘  eožŧššđđļ ķķ――ģąģąąŪ­ķ6§V­āŨŨÖÕÔŨĘĐŅÓŌÓŌÔŌÓŌŅÐÏŌÐÐufž€Ž€€‡‘€€€Ž ždnšđļļ·ķķĩīģąĩĶ·ēŊŪŪ­Ŧē‹7§XžÞÖցÕ}yŨŌÔÄÁŊšīķšļžąķēgeœŒŒ‚އ‚Ž€Œœbnđ€ļ·ķĩīģēģļJOŊ°ŪŪŽŠ°–:§[ĮÚրÕØÏh‡ØŅÔÄšĪáīÉÛâåŽŲÐxcšˆ€‹€Œ‹€Œ€‹Ššbnđļļ·ķĩīģēēąŧ[&Ģē­ŽŽŦŪĄ@§aŅŲրÕØÉd’ÚŅÔÂĩąŨÉĖÓŅŌÉÏÏub˜…ˆ‰‰€Š‹‹€Š ‰‰ˆ…˜anđļļ·ķ€ĩīģ€ēąŧf%›ī­€ŦŽĶC§eÔŨÕŲĀ_˜ÚÐŌĀīŪŋūđÃū°Á―ēka•…††‡€ˆ…‰€ˆ‡ ††…•_mđļ·ķķĩĩī€ģēą°ŧm"īŽŽŦĐŦĐH§nŲÖÕÕÔÔŲ·[žÚÐÐūģąđļšđīŧ·ķši`”‚„„€…†€…„„‚”^mļ·ķķĩĩīīģģēēą°šx„ĩŽŦŠĐŠŊQ§{݀ÔÓÓÚ­X§ŲÐÐÁąžÃŋÁ·īŋ―ļķn_’€‚‚€ƒ„€ƒ ‚‚€’]mļ·ķķĩĩī€ģēēą°ļƒ yģŠŦŠĐĐē\ĶU‰ß€ÓŌŌÛĶY°ŨÐŌÃķŊĩķĩą°īīēŊf]Ž}€€‚‰‚‚€}[l··ķ€ĩīģēē€ą°ķ"sī‚Đąi6ĨT—ß€Ó ŌŌڟZļÖÏŌŋŋ€―žū‚―j\Ž{}€~‡€€~}{ŽZk·ķĩīģē€ą°Ŋī—&hīĐϰt5ĨR Þ€Ó ŌŅŲ‘WšÓÎÍπÎÍ€Ë ĘÉÉČp[Œy{{|}ƒ~}|{{yŒYkĩķ€ĩīģģē€ą°ŊēŸ*\īŠĐ€ĻŊ|4ĨTŦÜÓŌŌŅÐ֋ŠÞÕĖĖ€ÍĖ€ĘÉȀĮnZŠwyyzŒ{€z yyw‹Xiģ°ēĩĩģ€ēąą°ŊŊēĒ.QēŦĐϧ§Ūˆ5ĨVđ؃ŅÃëþũúų†ø‹Y‡uwxyx wu‰WŒóōÅąī€ē ą°°ŊŊŪŊĶ6FŪŠ€Ļ§Š’8ĨYÃւŅÐÐâūŸĶœšš™—•’‘‹‹‘QT…r€u€v‹wv uur‡SS’emprsvwxyz{„ƒ“?=ĻЧĶĨĨĻ›=Ĩ_ËÕŅŅ€ÐÏÏːixve`_]ZWTQMKHK+R„pr€st‡ut€srp…P,KHKNQTW\]a`evxj„H8ĨЧĶĨĨ§ AĨdŅÔŅŅЁÏΜtz‹‰wrsqnliecae6N‚oqrqo„L7daceilnpssv‡‹{s‘S2ŸŠ€ĶĨĨĢFĨlŨÓŅÐЁÏÎΈ‡Š—–ˆ‚}xwsqw?Kmo‚p‹q‚pomI@wqswy|ƒ‡–˜‹‡ˆš]+—Ŧ€ĶĨĪĻPĨyÚŅЁπПZ\YT_`URRQP€NS/N€jlmn€mllj€M0R€NPQRRT`_UW\YŽe&­€ĶĨĪŠ]ĪO†ÜŅ€ÐρÎÍpJCPRJGFDB?<:@jzfhƒi‰jƒi hf{i@:RN=;=$<=MR@W{.$ƒ‘“•—™›žŸŸĐimœj\e-27*%'%)73,a` 6loqsvy~ƒ‡Œ”–™›œ€ž ž €‰Ąžœ€šĄf6ŸIÓ€Į ÆĘđgĪËÃÃÂÁ€Ā7ūŧš·ĩąŦĶĄš”Ž†{xtsCWf,.5(#%%#(40+c[Clkoquz…ŠŽ‘•™›œžŸ€ Ÿ NJf– œšš˜Ÿo7ŸI™Ō€ĮÆÎfÁČÄÀÂ7Áŋŋū―ŧļķēŽ§Ģœ•†€{vpoCQh,,3( &3-*dV8s{ppuz€†Œ‘”—›œœžŸ€ ‚Ÿ žŸœWq̜šš˜žv7Ÿ IĢŅĮĮÆĮÂeƒÍĂÂ6ÁĀŋūū―ŧđīēŪŠĪž–ŽˆlbnkELh,)2$$1**fP@BCFGHILMOPRSVXZ€[€\aH4B9>>?>\WX\`defhijjln[LZsrqqR: ›$,N]aa_cioqSA:=BEILMOPRTUVXZ\]^_adeg€fhglV D)P G#$L L )M M#icdhlnopqsttuweYh}{{y[>œ#(L]`a`cinqV><.ƒ20029CNUZ]^__`acd‚efjZ L4 )WP. /TT* 3UU& /jfgkR,O:Lmnqrl[^qusucJ 9Ubcbdhnvh>2†4 3137=FKQVZ]_aab7afW'%I:&3R'%L6&7O&&O4%:P%&P2&5bZTRQRUZYP[ddilnh[buwvwdHž DTUUWYZXLA…B‚CBA@@ABDEF€G‚HGEEFHEFH€F HGGHGGHIFFH€GHDBBCB€A CCDFIKLLQ[€fgO(ž$€%'&&(€*ƒ+‚,†-„.//1202€3€4 55657769889::9€;:<==>?@@€A@@??A?ĸĸĸĸĸĸĸĸŽ€<˜;<ÛKc™|bKÛhž™™žhÛ“Īŧķ‘ĩķŧĢ“Û:>€G„F€EDEƒFGGF>:Û!Vœ—™™›žŸĄĒĪĨĶĻĐŠŠĐĻĶĨĪĒĄŸž›™™—TÛ!Íąģĩī··ļđššžž―ūū―žžŧšđļ··īĩģąÍ~Û}―œ€ž€žœūzÛ|ŋĄƒĢ‹ĪƒĢĄŋyÛ|ūĄ†Ģ…ΆĢĄūyÛ{― €Ē‘̀Ǡ―xÛzŧ ‚ĒĢ‚Ē ―xÛzšŸĄ…Ē…Ģ†ĒŸžvÛx🡕ĒĄŸđuÛx·ž‚ĄĒ‚ĄžđtÛwĩ€ ƒĄ…ĒƒĄ€ ķsÛvĩ€Ÿ  Ą  €ŸĩrÛuģ›žƒŸ‰ ƒŸž›ģqÛt皜žŸžœšēpÛq°™›€œž…Ÿž€œ›™°oÛrۘ›œˆ€œ›˜ŪnÛqŽ—š‚›œƒ›œƒ›€š™ŽmÃÔÓŌÚØÚÕӁՁԀÓŌ€ŅÐ`nŦ–˜€™ƒš„›‚š€™ ˜–Ŧl^Āŋū―ŧŧšš€đ ļ··ĩģŊ­ēŊīą­ËŠ”ÚŨՌSÓÖØØŨÕŨŨԀՁŌŅÏwmĻ•——˜‚™šƒ™€˜——–ĐjsÁÁĀŋūū―žŧ€š đđ·ģ°­p=bŦģŧŧĐƒāޚ'â€Ų؀ŨÖÖՀÔÓŌŅwkĶ“––‚—‰˜‚— ––“§irÂÁĀŋŋū―ž€ŧ šđļ·ģĩ…/mšđdĻ \ãßĒ;RĒâ€Ú؁ØŨրÕÔÓŌwjĢ“”€•–‡—–€• ”“ĢhrÂÁĀŋŋū―ž€ŧšđļ·ĩķ•FRz·žo:§ [žįÛāÆÁÓāÚ€ŲØŨÖ€Õ ÓŌŌŅwiĒ‘’’“€”‰•€” “’’‘ĒgrÁĀŋŋū―€žŧšđļđ·ķĩđŊŦ­ķģž|<§ Z§æÛÜāāÞÚÜÝŲØ€ŨÖÕՀÓŌvh ‘‚’“‚’‘ erÁÁĀŋūū――ž€ŧ ššÁÁķĩļ·ķģēŧ„;§ZēäÛÚÚŲŲÜÏ­ÕŲØŨÖØÖŨŨÔÔÓÕÓÓwfž€Ž€€‡‘€€€Ž ždqĀŋū――žŧŧš€đļķđŠž·ī€ģąļ‘<§]ĀâÛڀŲڂ~ÜØÚĮĀŊšīķšļžą·ągeœŒŒ‚އ‚Ž€Œœbqŋ€ū―ŧ€šđđļ··ŧOSķĩģģē°ķœ?§^ĖßڀŲÜÓnÞŨÚÅšĪáīÉÛâåŽŲÐxcšˆ€‹€Œ‹€Œ€‹Ššbqŋūū―ž€š đļļ··ķŋ_+Đļē€ąīĶD§eÕÞڀŲÝÎj—ßŨÚÃĩąŨÉĖÓŅŌÉÏÏub˜…ˆ‰‰€Š‹‹€Š ‰‰ˆ…˜aqŋūū―ŧ€šđķĀj*Ąđē€ąēŽH§iŲہŲÝÅeāÖØÁīŪŋūđÃū°Á―ēka•…††‡€ˆ…‰€ˆ‡ ††…•_pŋū―ŧŧšđđ€ļ·ķĩĀq&–š€ąŊ°ŊM§rÞÛŲŲØØÞ―aĪßŨÖŋģąđļšđīŧ·ķši`”‚„„€…†€… „„‚”^pū―ŧŧšđļ··ķĩŋ|#‰ŧąą°ŊŊīW§â€ŲØØāģ^­ßŨÖÁąžÃŋÁ·īŋ―ļķn_’€‚‚€ƒ„€ƒ ‚‚€’]pū―ŧŧšđļ··ķĩ―ˆ#š°ą°ŊŊ·bĶXã‚ØāŽ_ķÝÖÖÂĩŊīĩĩ°°īīēŊf]Ž}€€‚‰‚‚€}[o――ŧšđđļļ··€ķĩž’%xš‚Ŋ·o;ĨW›ä‚ØßĨ`ūÜÕŨÁÁ€ŋ ūŋŋ――ūŋk\Ž{}€~‡€€~ }{ŽZo―žššđđļļ·€ķĩĩš)lšŊŪķx:ĨVĪâØŨߗ]ĀŲÔӀÔÓŌŅŅÐπÎs[Œy{{|}ƒ~}|{{yŒYnŧŧššđđļļ·€ķĩĩļĪ.aš°Ŋ€Ūķ‚9ĨWąá€ØŨÖݑâۀŌŅÐЀÏ΁ÍrZŠwyyzŒ{€z yyw‹Xlđĩļšđķĩ ·Ļ3Uļ°ŊŪ­­ī:ĨYūރŨÉïĸųû€ú‡ų‹Y‡uwxyx wu‰WŒôôÉķķĩ ģĩŽ:KģŊŪۭް—>Ĩ^ČۂŨÖŨæÁ Ķœšš™—•’‘‹‹‘QT…r€u€v‹wv uur‡SS’€gprtvxy{||}†…—CAŪŊ­ŽŦŠŪĄBĨcŅÚŨŨ€ÖÕÔŌ“ixve`_]ZWTQMKHK+R„pr€st‡ut€srp…P,KHKNQTW\]a`evxj†N=Ŧ°ŽŦŠŠŽĶGĨhÖÚŨŨցÕԞtz‹‰wrsqnliecae6N‚oqrqo„L7daceilnpssv‡‹{s“X7Ĩ°ŽŦŦŠŦĐLĨrÜŲŨŨÖÕÕÔÔÓĨˆ‡Š—–ˆ‚}xwsqw?Kmo‚p‹q‚pomI@wqswy|ƒ‡–˜‹‡ˆc0ą€ŦŠĐŪUĨ~ßŨ€ÖÕÕÓŌŌĄZ\YT_`URRQP€NS/N€jlmn€mllj€M0R€NPQRRT`_UW\Y‘k,•ģŽŦŦŠĐ°bĪTŒâŨ€ÖÕԀŌŅrJCPRJGFDB?<:@jzfhƒi‰jƒi hf{i@:DŦŦĐЀϧLĢpŲÕÓŌŌŅÐπÎĖĖËËÉČÆÉÎŦH„VUk]N›PN[kVTƒNˆŠŦŽŦŽŦŠŦŽŠF>ĨŽĐϧĶĶŠWĢ|܀ŌŅÐÏÎÎÍĖĖËĘČĮĮĖÍÅŋĪ BƒWQdZKK—L KKXeRTƒGĻĻ‚ĐŠĻĐ­N: Ŧ§€ĶĨŽ`>ĄVŠÜŌŌŅŅÐÏÎÎÍĖËʀĮĘÄū―žŧĶ=WKaWFF•HGFVaMTBŽĨĪĶĨĨͧ§Ķ§§ŊW3™Ŧ€ĶĨĪŦl>ĄS˜ÜςÎÍÍĖËĘČÆÆÅŋžŧđ·ķīŸ%5XG]TDC“ECBS]HV; ŸŸ ĄĒĪĨĶŪ`,ŠĢĪĒĒĢŦx>ĄRĄØËÉŅÓÐɀĖËÉĮÁÂūžđļĩēąŊ­Ÿ)/}YCVP@?‘A#?@PXDV}4Ššš››œžŸĄĒĢĪĨĨŊf&ĢĄĪĪĄĄŠ<Ą RŦÔĮÄ~Q…ĘʀĖÆŧŋĀ―ŧ·ĩąŪŽĐ§Ĩ™+)z[>RN=;=<=MR@W{.$ˆ•”•–—™™œž Ē€Ī Ŋm#rĄn@W• Ļˆ<Ą%U·ŅĖ{ŠŌËÎĮīŋÂŋžđĩą­Š§ĪĄŸž–2"w\;MI:8:%8:IM=Yy'#†ŽŽ’“•—šœžĄĒĢĢŪw$m"RĒĶ’=Ą&XÂÐÐY6~2wÔÍÉĐžĮÁŋž·ī°ŽĻĢĄž›˜•.t]8GF74‹6&46FI8Zv",‡ˆŠ‹‘•–˜œž ĒĒĨށ)lt,zL? ĪšBĄ']ĖŅзt—œÅÏÍĄĩĖÆÃĀ―ļĩ°Ŧ§ĒŸ›—”‘ŽŠ4p_5CD41‰3'14CD5[r&|‚…‡‰‹‘•˜™œŸ ĒĪĪŠŠ-dĐvˆ€ŠĢĢ IĄ(cŅŅÏÔŲÓÖÏŅĒĢÏČÆÅÁ―šķ°ŽĻĢž™•‘Ž‹‰…4k`3?@1.‡0(.1??2]n.x|~€‚„ˆ‹Ž“–˜šœŸĄĢĪĪĻ“,WŽŠĶϧĄĒĒMĄjÔÐπÎ"ÍÓ°‰ČËČĮÄÂĀžļģŊŦĨ š–‘ŽŠ‡„‚=ga0;=.*…,)*.<;0_i0vxz|‚†‰‘”˜šœŸĄĒĒĢĪĶĨvbĶĢĒĒĄ  ĨWĄxŨ€ÎÍĖÐÆ~ŪπČÆÄÂūŧļēŪĐΟ™”Š†‚|< ac/7:,(ƒ*+(+:7.`e5tuwy|€„ˆŒ”—šžŸĒĢĢĪĨĶͧĨ•ĒĢĒĒĄ  Ļ`>ŸO†Ø€ÎÍĖŌ•ÎËɀĮÅÃÂūŧķēŊŠĪŸ™’‡‚~{x@\e-27*%',%)73,a` 8psuwz~„ˆ‹“˜›Ÿ ĄĒĢĪĶĶĨĨĢĨ…ĨĒĄĄ Ÿ§k=ŸJM•ØÎÎÍĖÐūkĐŅÉÉČĮÆÅÅÃĀŋžļī°Ŧ͟™’‹…€|xvDWf,.5(#%%#(40+c[Dposuz„ŠŽ’–š ĒĢĢĪ€ĨĪ ϐišĨĄĄ žĶu=ŸGMžØÎÍĖËӒmČÍĘÉČČĮÆÄÄÃÂĀžš·ē­ĻĄš’‹…yrrEQh,,3( &3-*dV:v}tty†‹‘–™ ĄĒ΁ĨĪĨĨĪĢĒĪĄ[vĻĄĄŸžĨ|;Ÿ MĐÖÍĖËÍČiˆŌɀČ5ĮÆÆÅÄÃÃÁŋ―ŧļīŊĐ̜”Œ…odrnGLh,)2$$1**fP>ij_k|€†Ž”™œ ĢĪ€Ĩ§€Ķ€ĨĪ€Ē ŠkLĪ ŸŸĢƒ<Ÿ OĩÓĖËËŅ­WĒŅ€ČĮƁÅ.ÄÂÁĀĀ―ŧš·ģ­§ ™‚[mxpkIFh,(11(*fKCfjr[9p‘—ĄĢĨ…§Ķ€ĨĪ€Ē Đ€5€§ŸŸžĄ?ŸSŋЀËӐVĩÎČČĮĮ€ÆÅĀÂ)ÁÁŋū―ŧšķēŽ§uY|~sniJ?i,'()gEDehnxo&GšĄĨρĐĻĻĐϧĶĨĢ€Ē §“3b§ŸŸž •CŸ [ČÎËĘÉÏw]ÁĖĮÆÅĂÂ*ÁÁĀŋūūžš·ķ­oeˆˆ{skgH9i+'h?Fcglu‚~<:šĻ§ĐĐρĐĻϧ§€ĶĨĢĒ Ī›;IĒ ž›HŸ `ÍĖÉÉËĮf`ÂʀÆÅÃÂÁ€Āŋū€―"ŧšīƒ‰œ˜Š}rjeM3hg8Eagnwƒ’‹fZĒŽŦŠĐĻ€Đϧ§€ĶĨĪĢ‚ĒĢžB>™ €œNŸgÐËÉÉËĀ_bÂČÆÅāÁĀĀŋū!―žŧŠīĀĻ›€ticM05J`fo|‰•Ÿž’Ū­ŽŠŠ€ĐĻĻ€§ĶĪĪĢƒĒ ĢŸG<• ›šŸUŸtÓÉÉČÉū_bÁÉÅÄÃÀÁĀĀŋū―žŧŧđŋÖįŨ§Ą—‡wkcQ Kaht‚›ĒēģĐŠ­€ŠĐ€Ļ€§ĶĶĪĪĢ‚Ē ĄĄĢH@” œš™ _@ J‚ÔÉČĮÉÁb`ŧČĀÀ€ÁĀŋŋ€ū―žŧŧ€š·Þųþâ°Ē›~pfHHdnz‰–ŸĻĀČĀĩŽ€ŠĐĻ‚§ĶĨ€ĪĢ€ĒĄĄ  Ē˜KF•žšš™Ÿi> I‘ÔÉČĮČĮm_ŪĘĀÀ€ÁĀŋ€ūžŧššđķŧōĸĸðÅŠœ‚vnnu€šĪ·ŌØÐÃ­ŠŠĐĐĻ §§ĶĨĨĪĢĢĒĒ€Ą ŸŸĢKQ™š™˜r?JšÓȀĮĖ‚cËÃÂÂÁĀĀŋŋūūž€ŧššđđļļīđėĸüõęÔ𠐇‡ŽžīŋÍØÝÝʭЊЁĻ§§ĶĨ́ĒĄĄ €Ÿ ΁Pbž›š˜—x> JĪŅČĮĮÆĖšlĮÂÂÁĀ€ŋū―žŧŧššđđ€ļ··ģąÐïöýýúóæÞÝæōųäŲØÕĀŠĐŠĻ§§ĶĶ€ĨĪ́ĒĄ €Ÿ žžĄvZwĄš™˜˜œ? KąÎČĮĮÆÉąz…ŧŀÂÁÁŋū―ŧŧšƒđļ·ķķīŊīĖėþþýûúúûýþįΞŪĻŠŠ€ĐϧĶĶ‚Ĩ΁ĒĄ €Ÿžž˜qfŠŸš™˜˜š‰APžĖĮÆÅÅÆÃ‹ŦÆÂÁÁ€Āŋŋūū――žšđ€ļ·€ķĩĩģŪŊšÉŲãææãŲĮģŠĻ€Š€Đϧ€Ķ€ĨĪ́ǀ  ŸŸžŸŒuv™š€˜—˜DXÄÉÆÆÅÅÄČŠ—ĨĀÃÁ€Āŋūū€―žšđđƒļ·ķķĩĩīģģēŪŦĐĐĻĻĶͧ€ŠĐϧ§ĶĨĪĢ́ǀ ŸŸž€ œ†{‡™˜˜—–—”I^ČĮÆÆÅÄÃÅŋĶŽšÃÁÁĀŋŋūū€―ŧšđ„ļ··ķĩĩīģēąą°°Ŋ­Ž­ŽŦ€Š€Đ€Ļ§ĶĨĪĢĒĄ€ Ÿžžž”ˆ…–›™˜—––——N fËĮÅÄÃÃÂÂÄļĩŋÂ€Āū――ŧššđ…·ķ€ĩģē€ąŊŊ€ŪŽŦ€ŠĐ€Ļ§ĶĶĨĪĪĢĢĒĒ€Ą ŸŸž›’Œ‘›™™˜€–•™TœLsÎÅÄÂÂÁĀČÏĀūŋ€ū―žŧššđ‚·€ķ‚ĩģē€ąŊŊ€ŪŽŦŠŠĐσ§ĶĨ€Ī̀ǀĄ Ÿž››š–’™šš™˜––•”š^A›I€ÐĀÀÂÁÁÄÓÝŲŧžūūžŧŧššđļ··€ķ…ĩģē€ą ŊŊŪŪ­ŽŦŠŠĐρ§ĶĶĨĪĢ̀ǀĄ  Ÿ€žœ›šŸž™™€š™˜—–•”›g=›HŽÎÃÁ ĀūÏčņÞđžžŧ€šđļļ·ķ…ĩīēą°€ŊŪ­€ŦŠĐĻϧ§ĶĶ€ĨĪ́ĒĄĄ ŸŸžžœš™ĄĪĄœ€š ™˜——•””™o>›H—ÍÃÁÁ ĀĀ――āũúā·ļŧššđ€ļ·ķ‚ĩīģēąą°€Ŋ­Ž€ŦŠĐĻϧĶ́ĨĪĢ€ĒĄĄ Ÿ žœœ›šĢЧ ™˜—–•””’—t>›HĒĖÃÂÂÁÁŋ žūęýĸåļ·šđđ€ļ·ķ€ĩīģģ€ēąą°€ŊŪŪŽ€ŦŠĐĻϧ͂ĨĪ́ĒĄ €Ÿ žžœ›š˜§Ū­Ķ™€˜——€•“’‘”|?›JŊʀÂÁŋ ū―đžïĸĸï―ē€ļ·ķķ€ĩīģēą°ŊŪ­­ŽŦŠ€ĐϧĶĶ€ĨĪĪ́ĒĄ €Ÿžœœ™›Žģ°Ļ™–˜—––€•“’‘‘“ƒ@›QļĮÂÂÁÁĀŋŋū―žŧŧļšëĸĸųĖąĩĩ€ķĩīīģģēą‚°ŊŊŪ­­ŽŦЁĐĻĶĶĨ€Ī€ĢĒĒĄ€ Ÿžž€š˜ ąĩīŠ˜•€–€•”“’‘‘‘ŠC› WĀÅÂÂÁÁĀŋŋū―ž€ŧ·ķā€ĸāļ°īĩĩī€ģąąƒ°ŊŪ­­Ž€ŠĐ§ĶĶĨ€Ģ‚ĒĄ€  Ÿž›ššŠ·ķīĻ–•€–€• ”“’‘‘ŽH›]ÄÄÂÁ€Āŋū――ŧ€š đ·ģÏúĸýōÍ簁ģ瀰ŊŪŪ­­ŦЃĐϧĶĶĪ€ĢĒĄ€ žž››Īēļ·īĒ–——––••””€’‘ŽŽL›cČÃÁĀĀŋū€―žšƒđ ąžæüųöâÄēŊą°°ŊŪ­ŽŦ€ŠĐЀϧ§ĶĪ΀̀ĒĄĄ Ÿžž›œĢŪĩ··­œ—˜˜—––•”“€’‘Ž’Sš IpËÃĀĀŋū――žŧŧšđļļ·ģąÉëôîčÛÆģŽ­€°Ŋ€Ū­Ŧ€ŠĐĐĻρ§ĶĨĪ̃ĒĄ Ÿžœ›ĪŽēīīąĢ˜™™˜——––•”€’€Ž“\A™F~ËĀĀŋ€ū―ŧ€šđđļķīŊģĖâįææáÔĀŊ§ĻŦ­Ū­­Ŧ€ŠĐĐρ§ĶĨĪ΁ǀĄŸœ›Ą§ŽŊ°°ŪĨš˜šš™˜——–€” “‘ŽŽŒŒ‘e@™FŒËÁŋū―ŧššđļķ€ĩģ°īÆÚåéðöðāĮīŠĶĶĻĻ€ŠĐ€Ļ§ĶĶĨĨĪĒĒĄŸššĒаģģ°Ŋ­Ŧ̚—˜——–•€”“€‘Ž€Œ‘l@™F”Éūžŧžšŧžŧššđļķ‚ĩēŊąūÕëøþĸĸþïßËžēŠĨĢǁ žžœ›šœ ĻēÃŅÕÓÉÁđīŪ§Ÿ™–˜˜——˜€—•”“€’ ‘‘Œ‹ŠŠ‹Œ‘p?™ GžÄļĩŧū―đķđšđ‚ĩīī€ēą­ŦīËčýƒĸýôëäÜÕÏĖĮĮĘÍŌŲāčóýĸĸüíÜËūąĨœ˜—˜„—–••”’€‘ Ž‹†…ˆˆ‰†‰ŽxB™ AĐūēī‡JYĄļķ€ļ·ķķĩīģē ą°ŊŪ­ĐĐģÆÜðü“ĸõÛŋŠž˜—€˜—–‚•’’‘ Œ†wK8[ƒ„‹4™(ķšĩ‹ A°·ļļ·ķ€ĩīīģēąą°ŊŪŽŠĶΧŊūËÚčðöüþƒĸ þüöïįŨŷʘ˜™™€˜——–ƒ•”’€‘€ ŽŠƒ~D[ˆ‰Š%™ >―·š`(gN(Ģđ··ķĩģģ€ēąą€°ŊŊŪ€­ŽŽŦĐĶĪĄŸĄĒĶŦŊģĩķķĩąŦĶ ˜––—šœš€™˜—€–€•€”’€‘€Ž ‰…z(Lf4@ˆ‡Œ2™ HÂđŧ™=i{oļķ€ĩīģģēēą€°ŊŊŪ€­ŽŦŠŠ€ĐϧĶĨĪĢĒ …€Ÿž€œ›š€™˜—€– ••””““’‘Ž ŒŠ‹_HŠKd‰‡=™ LžđūĩĄĩ―·ķĩĩīģģēą°ŊŪŪ­ŽŦŦŠŠĐϧ§ĶĶĨĪ„ĒĄ Ÿžœšš™˜—€–••““€’‘ƒŽ Œƒ‡‡‹‡ˆŽI™pØļīīķļģąŊ ŪŪ­­ŽŠŠĐĐĻ€§ĶĶĨĪĢĢ€ĒĄ Ÿžœœ›€š™˜˜–••””’’‘‘€Œ‹ŠŠ‰ˆ‡€…ƒ„ ‚‚ƒ€~~…’[™9ãÛëįįæåääããæęæį῁äãâáā€ßށÝÜÜÛÛÜââÞßáāÝāāÜÝßÝÚÞÝŲÛÜÚŨÛÛŨÛÛ؃ÓŅŅÐςΠÍÍÎÎÍĖËĘË͝yš}ķŋÁÃÅÆļĢķ°īĩ·ļšŧžž€ūĀÁÁ€ÃÅÆĮČ€É ĘĘËĖÃϧ―ģ§ŊÃĐĐĀģĻģÃĐŠÄģŠļÅŦ­ÆģŽķҁÐŅÓÕÖŨŨØØŲŲÚÛÖÏÕÞÞÝßփ›$-O`ecbflrsM D28?DGILOQTTVXY[\^`cegjijkrU>!MCII JJkeflprsuwwxy{|jZj‚€€`E ›"4Ykpompw|~_LDFMPTVZ[]^`adefijknoq€tu{b M. "[ Q( (W V#.W X 'wq€prwy|}~€ƒ…revЇˆ†iI œ!/Xjoonqv{€c HFEMQSVZ[]_abdegijknprt€uzc O1 $] T* +X X% 1Y Z" )wqrwz{}~€‚ƒpfx‰‡ˆ…fCœ"*Uhoonpv{e#CIDLQSVY[]_abdeghiknprs‚tuzd Q4 &_ V, -Z Z( 3Z \$ -wqrwz{|}~€‚‚nfzˆ…‡‚d>œ#%Sgoonpu{€h'8IELPSVY[]_abdeghikmprss‚tze T6 )a X/ 0\ ]+ 5\ ^' 0wqrwy|}~~€€€mg{‡…†a: Pf€npuz€k04BNPSVY[]_abceghikmpq€styf U9 ,c Z1 2^ ^- 8^ `) 3wqruwulpy~~€€lh|‡„†}_3!Menmmptz€m4//=MUWY[\_`bceghijmoq‚sttyf V: ,c Z3 2_ _- 8_ `* 4vqruY9‡Tdv~~€~ii~†ƒ†y]-"Jcnmmotz€o9#30/4BPY]^_`bcefhijmoqr‚styf V: ,c Z3 2_ _- 8_ `* 4vqti)&ģNBd~}|ij~…‚…v['Fa€motz€r?+766436>LW_bccdfhijloqrr‚sxf V:,c Z22^ _,8_ `*3vqtk+*i~}~zhkƒƒtX B_€mosy€tH5‚;:88:BMZcgjkklnpqs€uyfU: -bZ3 3]^. 9^_+ 4xtt€uyb62`{{~€ygl‚‚pU  Capqpqv}„vI<„> ==<:;@GPW\bgkmo9pqqtb.,TC-:].-W>-?Z,-Z;-BZ--Z9-€@AA@BCCDDEDDEFFGFEEGF$ĸĸĸĸĸĸĸĸt8mk@tøęęęęęęęęęęęęęęęęęęęęęęęęęęęęęęønĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸy~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx*œšššššššššššššššššššššĖĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸƚššššššššššššššššššššœƒ0DėĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸņPģĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÁÏĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÚäĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸíōĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõ!*úĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ4@ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸN[ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸiwĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸƒŒĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ•Ąĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ­žĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸČÓĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÞ įĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸïóĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸũ$-ýĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ8EĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸR`ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸm|ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‡Žĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ˜ĨĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸģĀĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĖÖĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸá ęĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸðôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸų'1ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ<JĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸWeĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸs‚ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŽ—ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ Ŧĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ·ÄĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÏÚĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸåîĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸô%øĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸü,5ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸAOĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ\iĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸx‡ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ’šĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĢŪĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸžÉĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÓ Þĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸčðĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸõ!(ųĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸý/9ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸESĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸaoĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ~‹ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ•ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ§ģĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÁĖĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŨ âĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸëóĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸũ#*úĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ2=ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸJXĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸfsĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‚ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ— ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŦļĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÅÐĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÛåĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸïôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸø%,üĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ5AĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸO\ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸjyĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‡’ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸšĢĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸŊžĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÉÔĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÞ čĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸņ$ũĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸü,3ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ:FĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸSbĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸo~ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸšĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĪŽĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸķÅĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÏŧĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÆPôĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸų[ ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ  wĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ„  hĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸs  Xĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸd  NüĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸU  GũĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸúN  =ėĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸôF  6āĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸį<  1ÓĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÜ6 +ÃĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÍ/)ŋĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸÉ,%·ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸū)#€ĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸĸ‰%#B’ÛîïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïݘE$5NkˆŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠŠ‰‚mP6  ,@Q]dfgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggd^RA-  !2@JPSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSPJ@2"  !+38:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:83,"  "######################################################################################"   ic09 jP ‡ ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cĸOĸQ2ĸR ĸ\ PXX`XX`XX`XX`XX`ĸdKakadu-3.2ĸ Øĸ“Ïūö—zYÝdŅ€†h&’‚ŋōĨ‡æ CFE°9D“þŪ!Á·"“ĻĮøŨ-B^ð͌ōJY9…[τ„ƒdÏãÂĻoiĸŒŊŋŲøĄWÐËÁ=0B=ËWšä9ŠL6Úœô([okĄęė™3-ņ‹Ōŧøþųņó™ĘõŽ3ŽĄxĻprųVúh(ôÔĶũ–>ņT=i†SÞ]íģw„NÍxîįé úęĒÆ/S‹WĀ“īBĶĻHŊޜJVĄķœCSlįŠ\ņH ðTę^Å%‰YÉį'cē,­ŠÓ+Œv1[Å,―Š`sī'~qŋÖū ÁóW@Ą ĩķŧG<cóÕ|[“ú­Zv]+XĻøFúA~!wĶý§E°šŧQB|‘ þõuĨƒŠ„9ā˜jŽĄŽ§íÂ7Ļ} #Æã#F(ŨHĸ) ü[u .ø=e‰MŪ€ÏūÔOþŦ…č5å'ęóÚðǁR/)ļĩKŠ Vkįî+ÅjÍ̆e čŧōÍ'Øï΂Đož đĪß2ãĐĄš'ÄR{wĀž›ýŧ§ē‹[šOŅ#0./`üĢČGŌ}üË7:G*w$ÞåaŠüO‰*™Í°ÕäI=Ā:ϧx.æj”Z#”WŌ·ïßA‡jjĄ‘―TI5ï/ŠēŽ‘ømJjZþúR{ęb(ØĮ™’•ëz‡Âiėh1Ī˜ÝŽŽ5šÉí’ĢW]aØþáæô “œį9Ė~ÏĘÆŸ•w•vžYZoÜę’]‹a›īs Ôîï2p_ĢQ þü3ĮÆ/6^‹3ã=Ūģ”ą ssk9j"ÁôæÏNdˆ q@o F9:j$]rp]‡ŲĒ„ûÎôwtÉ Ã$ÝÏč„BA ˆ žKnóv%7…h ĶąVx€rŌF‡°AĶÏå^*ô:‚NËÅ|öęáŌbŧÜšï —ƒzo B^ĻŊ,T;ІŧÝp-ß cFŒy*ŸvÆį“ZŋŨ‰!ūÉĪplKη-/éÉ`™ó_i‡Ā-ČéĮK*k­BYĢcĩ6‘Šę*ú™đ@ņį“$1Îr҈…ŦNžŋņx`Ū6Ÿ{ƒ)ą1âXL?íY1!·ņĘĄæĄmķ~z­qLųül Ģ.óÎ| RqüM`3ƒŒ“=3ûØøė äíIĪ“'[ĖŠb Or/í9BÛE€jŧ… ”=P[ÝËIŨēóO™ęGtSYcy1B‰ūēĀQ°ÚÞÅ:Ģ4DņØõ@€·#Ģuv_™čßý$d”‰ÖôŽV’qŋHe#ąGĩÆ>s 9^Æp§†ŧ É,•ēė4B6ĀčOŋWõ;ŽoAāãėÃQĮcãŽíĘÍQ"žÐX§‹Š6ØĨ– o+$€4>+Œ‰3!ĸ`6!ýÎZƒšũVōÝAY_ÅÂ,ŪōÓ°6Áē’ÞúĶ–GpēĮđ$øķķW_.cŌÎÛōL'ū^Üžëg–žÚÐ+͝‚ÃÃj›o>ĄĮĶ{0-ÁôÓáöĐAö§ŠŸ[ÜŨðZŨWŨ(oŽ.ÝĨČf‘öģ°ïZ˜öŽŽ>}œ ė&îHZ&æ%ÃŒļk a ð݂7•w8‡ ā‡b2QeMī4e Ų`wŠŠ‘IÓRCÂļë…ũKį=Ŧĸ>.Åm=zäLļþ›eŌýŧņóŅ-8gÆ(ņ›ÚčŪ/žßĻ9ˆęŅĖÔņņ4Õâ-xå%ž"ú=öaũ~RDóO'ЉÓ,~~ŧJ J[/í īsCŠW’ÐņÄiØąúö{ýmUq7ĨkÍßŪ r_zöïYûzÓąĢ%z+īątæïâW=T ˆb~ĸ5>Ü€ĮÞÁ‚äjPĒņL‰ŧÏíL—Ŋ$rž•žņ)íãęĢlĨ`;ƒí |ą 2G}žG°]Â\yW7l‰˜-ņsš‹0Āŧ1œ~>œuĘdîZïg–šF$ĻÏŽŽŠĪüú\ģðŦTä–ĒzĐ"á­ĩýÖĖ–æL”ðTē'œŨ(ßHpÔnČŠiæ(~MÝÄÐ|ąĐaQ2| _ Ȗ/Ērԝ$Jڋ 'ęCÁ€Ÿ3[ƒĮœQmæķ8-û4o~’įJ)önûŦTĒŋáQgí4ĮœõÓDÅLĨq'įčL‡П dNņųįã <tX#] ō)>árģb_žÁ]Ļ?aóŠKëČØÉĮáŅt~ ĮįūzЧ]ßØ_Õîā]ãë°?Ð8[x­kBÍ―ū›%ŧ"KÎvPĸ_Ÿ—ãÃÍ3ââ]Ŋ"f}§ûctäþâÏVĪ\#ņL8Ÿ ĶõÞVÜņC0S‰…Vƒ_.q­L} ;ÍĸÕ=Ïí”s˜3ÛöĶâD€!Lã č€āßô@†ąM’,…Ï}ģwĶ–įŸ!ĩJ#š–ŲÍWä1NĘÜÃlHMÕŨ"CįÞąēÄížæzų X/bLkŅŋ_ ~“imå›ý>pþ}]4.Ý ·rõjEûtauð%:ųŨUę [€­ķÁĻ_‰[ķ/ØFm*ŠMߘÚo>ŽÔ8DëĶH6ïģÁ x%ëTô*_:YŲl€*c؞ējˆ‹vŨãņäú"āĖ< œČÕÓåŠ―"™NeŊĸOœpŌKy9W^X ņÆĨ\Ō SXÔĶ%{ŠĘĐęĖ Ž6c õp—TÝԂd™ëbEݔēö+€šm,ˆû6šþ7jCŸĢzņ ]ÞÄåovačd2507*ŧŅ,‹RŽäi'$ÚPk^‡āČžC /^0Žĩ'‹uˆQ ­ũyņFëdÕ&Eû#ĩ}(čÄcēūĖ=Ō"KRį:ÞöŠÂš€šĢdJzU +îäoSl·Ž@kq[~°ĩÛų,đ‘ŽÍŪ”Ųúū•™`ö%Ä―/Į ‚3ðÔa'“™ķYīöÍčĸSäÁęÂFûý<—å#oÉĻŋ ð ĨŊāé€ÔĢW9jäZ]‚vxÞp:›••LpTššøĀĢ3Gĩ•ŲęØŠÂwĮĘ{ ‹dzŸ!…b œĪ4C:φ―“I Ō藛l,^›ãI'ō>tĀ@ulđĖė―Zë―·Þž5H˜$^ð­ËÆŋ°62·ž'ę<­šÂtYĸPāÉķ–;ƒŧģ>īBD‘ĸ}›ÕSdĪŊ!o}ĐÝíũˆÎÞYó†ZîßÛŌąļ=YũŋŽß—ĄÁģpëĶĄŦ` šĖÃą&XĄ æįūž>QˆĶFŽïþŽŪÕļqÐÖ 9âØė›zâî—u™cc{)‹ÞáóÎm*ᰓųBjXO·!ýŌĩ’ĖŠîHīŲÜ·Ï0Ž—ŊôĻĨœKi|[Zô-Ō_ļŲ++ï7œ$ ŋ3ĘðbˆÁ>%/e ˜˜Õîģđ>>m™’ĘŪīü•ƒœhKiszlĘU·Ķ9a3É5Ŧh °6`mņŊorāč%)zUŧ'!ūā=jx$2b0ÎÏā–ųK~ÓóĪí^švÎÚJ—a˜―T;l$*ŨÛÉ%”ÚîhÍ ã€ ŠNNd°‡Ā3aŋ<â2‰V˜Ps„ÝuÚQ{ íūÅ6ÃŦÄo`/†ÖLæ?7ÅÍ8ęĪĄėû3#$ĢrÝÆhPž·'bë ƒĐĖpŊL@-…y<_XÖýģwčC*‰!ÛEkņțÁ-æÁø>‰)ĄÕ!Û2TÁSĸoyÄ ĖĐwãmû~€'īBĪ\‡Ũq^cš­{ąHjÝXä6OÜyųWŒk.Â.áOsAX{InÉ@cGÕM*väīœÍŧĀqĄqqIÍŠ`/œR:4Ķ-ã]ę,ÓÁĖâáa`NÐyš+*D!Ú­‚øĻø) Áw-~t`Óaņē“v7ū`uT~ôPqäWÛeäÛöäIÃĩų+u`~Ž‚ņ&ŒæH0fA‘b^G·ķŌū―ÖvđÂxxLŒ€)ķë&ļđũŅ|gN1ĀpÝOģ-OhģąĮŨĢМ–ayč} /\ë!V?ã˜ČdĒ2F_$)ØŠÁaĪI—Āf‘•Q•ÕĘĖΊHĩ@øZ;ÓK}“É&dĐЏmŽ|B[þ·(ī óŽ―ë™gö‡ÉÓI€ĮßSĮáWAö>€Ju(8/*vq)‹3’\JJģ.ÎÚģkŋø§―V&<ģžÛë˜ysÐũÚî‹öœq†•|PO‰7ßC@Ï<ē"ĸĪj“røk™ðö§(bdČŧ@ W—éĐjüցjlũí‘a:—2p‡ÄkĻCÎ3Ũb;Ū“ÃCaÚÐjTĒāé”Ā'Xīĩ<äïéH*'ąJÅļ#šdįE<’]ŊĄšSÛx_9XöšĮ:·ŽŲÔü”‹ÓČWß0úxčКÓrĸoÄ^þ…æīŽ \ų:‰%/tƒ*ų|?7}s’ŠŋEÖےI$’?^ĮŨ…*öøÅ=yŠZĄÛˆéþnúþÂ5ý8JąsGĩį` đ$’I#c–nķÝӘ,ew’ėč€qkÐĄ‡8ęƒŸ­C*4­ČÐeë x9-Q…†Ü’I$’ÚÁ”Đ—ąįæĩžļĨ֋eóW>G˜‡RT­āõ”š@{iKŠ^F Äģd›F/Šʰ‘æh2Ė{$ðvš$Ā<ąPkŽ‚€K‡/DŦē"Nė―šsJâė áĖ5ƒC2ŠØN~ĩƒF™ŨÔļÍ<ĶPŅ §‚Ķ€<)}§ĘĮáė-‡­Ž>ĸ&ļ•-QŊzÅēHÐäČ .ĩļÃíĶ'JP팘īŊJ<ė7ĢŌéȄ79+…Í{}ZČÛS‚sėW›$ŠþNĘð gpŲ”ÍōŒ#cļgÃč°ëqzžųĐdøló{…ŌīzŅýļEÞEhŐB •P°lNV™Ø0ƒdZqŠ Įļ#}ĻŦËŠ9‹éîДýƒB3ũe‡)šÚkmpÐÖøƒ3Û^8$ā UŅaoÓAæāļĮĀa3ΞåšâāýrėĪûÐ,k…>mß<úƒú`äŊ5Ōíģ ”jÏĄĢ{DGUŪå:Äû.{N—Tþ2 õđī8€Šˆ!, ōÚ*Ši·Ā FeĪÄKílÜ7Ī(”ŸÞÎ͜ūÜÜ}#·Á†ÚĄrįŲãó‹"Â―īéØ[‘8-–+ØGŲ‚r?ĘkđÉ.^Ŧœ( ķUgĒPáÓÕL8ë~a•+b+SíáóÞ0·Ķž,7g•d‚P§į―•œŲšœ%·Iþ4Ž-™Đ,°9ϔüô‚,ú’sÂV‚ûJÛ2―Dąķ9ũŌ͖“æ(įgÏ&Fu―Ų†'hŧņ)jvß§ŲƋģgĘŲKƒ‘(|cō%ņ|ݓú>ÛBF_@RĪÆ‚HÖ(éįPËÓOnW,­?"õ[ĒïĖ·’}Vęũķ Â.%“^t ›šX“53 ŧ”ŪÝ­^\Į“žõGķ1þŋÁül%u_›eęųMÎ)Þ8Īœđ˜°īĐXH‚ē{ĢŌËb Č}ĩPā!-F#ۘâüŽdč^N'ĄžíëĪJĻtkĪ(óØ#ĮhqÅeæL@;ĐĮd=Â)°~Îôw§Pę.ĘÛIū―FõžZņäčŸChĢ:\ĮbÃ{ųûĶĸaĻ_Į’Åß!ĖÚIąĶüÁúæ’ÜŽõ·ū‡ î,G3‹Þ=HM~8oĄņ`Ôi:!GG,õÙäĩQģGU!ÜįĒæ›–m Zå†ú*Ē_*.õž Į:šz*ĢOJ$ū“6Įę=tČNjŲ>+”ĀÛïϐDPڐðÂĨ‚ð<†Z5GÝØÝ%ɃÕļ$m1ŲĪ3riŠĒâi;8-@”:æũm‘–'—ĩð‹ãUa?úûÎïIĨöJP…!Ĩr|@lõït֏íҘ1đfÛŲi\@ úĄý:ģ9%æ4 –ęĀÝu$‡ í„š6Šuęéĩy-Vø$ĸ ðŠaĖv[")eI‡…íóųYtqĶ>ß+FiøĢ,ÃHƒ”…Š+Yų°aÃĮ?&LbTWŸôZ‚ð~ÓP§>îíž>•t؃-ĢmÏjUđ€#Ól}Į“þCï˜LÐ%i5–ĶĩL6ŪläkŊÅøö|s=Z(bQį…Elâø―œ ķÕáÕNølݖpþwĩNä/訓ë=ēEÎSXIš."ĩŲX“NŅßyŸĸsĻ•ûxĶč2ÔŲA‚qĸPžýĨXœ­õî3œQýõKh'HY“W§ pv’ ČūÞ\ĩ0[™‚ÜÝjŸ,‘õ0æsĩ ąC˜ÃÕ3>V…õ Ö霜œp&eš‡M!dH[Ū;p"Ï WöÃۅą4ōiþ"Vt$rwïŲy*AáQ§{pvöĶ%oGŌ›ŧg=%_ķ‚ĢapžåRĪdĸu>·Z…û]ŽĀ[ é*#lĩO4ąú=W ëí;sÕ|Rŋnü§Þõ}›˜œ#ļŪkēï“tôKýÝĻÚĨĖQûÜOōbԌ^Nh† cķ(‹CĒUgáÖ E”vMf―,VÄBÍ)ōäÁ…īĻLhŋ‰3YŽŠ ŸM(.Ֆ`avéĩ/Á܏ރĒÃģH„žęÐĸpÓúЌnŠjÍ™…ąŸÏĘÃōØãSGX~ņâõJÆ3Oüĸ5ܝhâļ‘Ģ•āŪæŸÞ1ĩš°cJa yxÕZ'ŠC/ÅÛŨóĖA™lļĩA“õgöĀ‘Vōēī7ņ%―ÐHÐiû_SĖĒ_ŸXųÂ:ŠŽ‘t°Äĸ#Ā\܀ÐŨŨ&z•īTŒËÞŲbŠíŌ{YŅŊéĢAZGāy ī2þmĸWN—kQŊäŲûæ-Ęøiˊ…pīõnŠĒĀ (þ›Âš“|ƘrՒfDoŸ[ÁŲ·ßTcÕÛÍ7>'aú ,õk“°PŧŲ0áŒH;\þg,ŋ5zæn|,WÄÎĐûŋÞfÂJåĶ@Aãõžß@4 Ėž|Ž\MuŠAŪwĢŧäWfĨVWl/ug§JÞåÃMyóņ^ƒ”Æ 7,ŧr‚þÁYÆ·5ĻgŸūƒ 7t'™‡įĮA\S.|š#Ŋų TJøķ˜î[ÓTūž 1-?óÜ˜ß Ÿ›g'ŋeĒýĨÏFđŽŽõˆÖ0‚CŋPŒHˆ™ÖQfuð -äĩ ų/Ž™ ūmÏĄČņ#1äí―í`ŨÜ\ÅÛTeÜ]Ve ŊÁlā‡ïgõšāēņNs@r)Öv|#Ėcķ.Ã1á‹J”*FT˜ôĻD[Ģðų}õãûÓ9ēŽÂYÏÆT ĄxÁˆĮ'Ęm‰,/XöĪ%ӕ”øÔ%]ûÐð6)=ŋúÞäÅgz ï{īÔĘâÚėéâßYc™V•ɏíÞ„T@ƒ3aĨwwī|2Ï] ĘĀėãŽēúýîīoEÄōG'0>â Į þs}NHUúŸV‚Î_zŲQðŌī^Ïą spp bŒ‘(7óaÃ87 @æÃt~|Æv`ČÕEsxį̜ÜNQ{Øž„č™ĸ€ČëŅ\(íķÓ8ØîŒ ŅĮĮi֚Ö;ŧāNīBz­ÔXHĪęZœŊ ŋa Ý‡ŽzLVíįÅQ)†ģPôŨÝXaä(hc% 2…ÁëęÖÚ‡ 4‰—3Wđ.Z(>ÞC,úE)C}Î@ýk!ĪrvŦßhqnnX‚”<03#ąČ—BYšü1š–íúŒÞŽų;Ō2’Ýf―8ęŪŦsïLúØs 7+ž° iûŨĘCM’UŲrĄpÓVŦŠÃ9M΅ōøgóœuXĨiĨÉŌDšŊÝĀĄ2{]Žâ\žDŪÂ{ĢŦÄM1EQbdĻąīŽãJFŪ4gĮĀEŦï4’Ä”K\ą,zÕ-ņ[$v ‚·}S‹*•šeÛU:@TŽÄđ‹{čįšĮ## KôŦAÎ`”i\raå–ŋV†Č:óyZkũ˜1ßPÆ_ũS–BŒ―>FW—æ 7ËJcQ%ė75įTŪîJÚØđYĨã‚& ŽYqŨ6„ÃVFßýÜz(ófσ!ųКŦd­ēŲûðÓDhÎdŸÚÐgSŲãŋ”œ"pŊ"åmaŽŊ!ËT$‡"‚rķíBk…ÂóÄø](ē{Šö}Ąt*"”ąīlp>ę3"ŊļĻT…L`ŋu-$R6øŧė\Ó-nxP^OČq5ïv‚Þāīy $B°$ų hø‘(˜DÔnûIT€‚&ę#‰…ęÓœM^žÞz+f‰aßļAwt3ŠÆ$Üõļĸ,cP—īR§Öõð:Ÿ(iĮ’‡zCÉ#ŨlĘDÖ[SC‰ß /^fžjÛZĪÃwĘ{áMJdsÎOëJÅ6ūŸÓ.tÚūîæ"kCoģKIÞ`ĖzŪÐpڐ[Üķ„qðwëR–ī‘ĄŨįƒJĄïöã„ō†šýmĖ;GgL/ģ-Č(J.xĘĀvĶūXlNĸ-ųl&beŸTLŦ.īĶŋĐųÚ1˜@ėâ—r+Œ~=ÝôŌ︘đðYÂŪũGĨM čũð4ß~ošĮ]ý›ŽE:mŦĩĸ;õ3 “DčŒģkāNšDÄ.Í,ž§=í6ėŊēþY ĀcpxĖû^=ŒčŪÖõĶāRĪĀĻ›ģ>Åk/Ą˜'hęy!KŦ°.ļ 'ôÔ vëũe“öÜŸ—r1,g)Þ)ðr‹lÏå'ļŒ'GSŊāĀĶDÂKބoG?힟ĢēQĐSJjĨÅ;Ob8XâöˆĶ―ôæ0ÉøŠĮˆ·á€žēO‘øiÛlĩsŧjĨÍøĀA}Ë(  €xÄā4A*HÕ ĻėqøōN2Äč™<ð“üä+ëķ}ėäųsó%ĖŒãÚęÜ=ŋíŦ˜^9~ąËĮs‡ãÎ/5„?ūŽ bï)áĨÔ˞āƒŧÆáąĸ,UēöÖ§ųĐö‘(nxčá4ž`wt~ĒôîËÁQL64Ū§ĩœēĘûy'NĐX'°gō=Š•ÓÞĢÝėÔi”vģĻr‹™o8ÄÅÎ=Ôŧâ]ÆÜMPīó›ŒHOvėđ@_đ>JX]§ áŲVfĻ–cŧðsDĀLðyÏ|úĨ„–TÍo5ðn8—‚Ã_äĀUaŧËįgņœw:Þ―œƒ Ņ0؀،—],-ŋÞ>€§ŦmŌ,PiýR0ŪFÁЇå͕z―fŨkuô ææĖ•ę§Ąō‹VäŧŨ{úĘWޚ?-hšÉŽŪfžõŨ—ģ˜ęwÔρ2$oQ•#ϔúģ•œĻ’š€zĖVrcėPģP‚Ŋ…‹ßĖ:{ÚĜĘŲ1Ņ,‚âðzW] )ËsžKw†ųNĨŋ6ȌŨĢ*^NÓ`bėdݘ›DÄėÎĀÆ‹ŌŅhåüŠÔĒÆéÎÖg–? "š•ÎJ`­cý ­ COäXėôģ™ÃéŅąöUøÅø:îãúîĒYšÃSžFūô†bI`(BōcmšY5zŧøÔdKĖlܒžWÏ‚õNZ›Rō(›8HØØÖýõvw`Ú―GŽĸ)6Ų)BEÂÃĨĘō#ïŊ·iÖã7õz{ëûŽdl%ŋKĒýđx;z(Úzî1lô,ĸmž‘ęHš„M0ø=íĖpęä$…‹Bė1Ķ―ŸĐŸĨƒ­ÃČõėĪEũĢ6Yy Ā/)‘ŧûm…Čéx"ĸ#_įދĒ`GŪ,ĮÁ˜―H{u”;“€Iîņhí†x§đąÂŊÜæ:5•ĐŲ·~âīmÝįåð>ĀyĻK .íOiUĪīýPŊvŽÜnDmĘP‚ąĻéoũ™ÞBÎ݃BįPßnHAK1:eŽŲ6xE iŪķĐ֖[[@ l?ģĸƒ 3Ðũ 5Ւ­`˜æ:đRä'ÔQįŒP~ɕ›T‘ĮiŠŽs#ZAôU ãý †Mū9ÄM#ø‡TâΧwļEđ;oúÐ*<=Úõ˜Ē6QEžXnl_Í2cýï…ƒ“å Ÿ>$īƒéc]žģđšð^Į)ŋ 1ĸov,>ZÍĐ|ĩĒĨėóh7æŋ:įšáևԅljü‘Ģ@&™/YČ.ž,þß"ŧĖųž ‡Ą‚ĩ|§'K>åï+.wfsIú?ŌVđi9ocš˜Ĩ`ėgėCš―ģúģOÉdā‡đčą_œ\ËdŌØ‘–~AÝæšÐ5‰―Q+IÃy œŒUįaå /œ‘LũÁzš $J"ŪÎ~đĄ˜Ēv{ i5 é}ío)r?ŌÃpfōčÅ6T0Ô[īÚ|pkJ˜üÍņtï-ģ5)—č4OâĐP|€—YAĘGNÝņ ž ô3đԇ ïÏM >rŦĐ&Ĩ2{þ óðéÎĀęm—ŠÐ1mģûNPcÏŌЏĸLļÕVi^ =[v?Ēðóˆ°+_… ûˆ?ļpH˜Fe[›†Ī7ÕbķHŪēÓ|ŽšŌĘ~Z\Ĩ°Pyžâ(âĘG-”Ÿå‹hŒÞŸéĮ`zÄąWĐcZÂŦé›BI_Õ Âĸtē'nŊãe"ėķ;8ÐđÝŊDKôt/0 ĻŸÐÅڗ<2Ø/pßB&oÝđˆõBžĘæFeĖR…ąÃAø…ŸˆÚV4Ō6ūφœŧD^’ÁuŪâ ŌLëÝMėÛÜíĩ;ûéĘ 4ðm-§fįTx„‹XĶ%õšÎޔ=ó€ĐC|2âd4dąŲČânēŋ-ÔSgþxę1ÂÓ…Ü'Ë5ĨiĖ’Ęõ!‹ąZ‘eÄŽÂLÄãũö;}}~þâ Ö_sííųú ?D ?VĪþŦįõiëØpČ ĀŧĩýĪØŽIƒw[#g^?‰ï ņî Ŧđ Ī-‡eÅI%&t H“$RKR_!gĖĒ’9Þᨭb]X?„Vë^í<žÃ+6Ú^eŧMŨôkđũ–ÓĐBQđԖ‹Ä BZōļ ^péjIãËôbjŠŊ'tbúÉø(‰ã*Ũŧ·āR.äŧ#čBœŦ^Ųn›aí…ö†)‹ ― Ð3ĄDįŊÚAƒáÓÐEīHŌã3Ŧ}‹đÂcíøáöÝCƒį^ n;”VBBå‚BÁ ”ybFēīÆ.öāāįûķķ?øÎûĮÜļ_,˜uÍĀi,ĒĻ­GĩÔŨĀDą\Ÿ`p#y;hÅ/aȚ˜$ž}r#ĄÐRösąúl”4ÍMbIzĄfÅ{ĩ‡ûŊîÍĨMîI8XÂgbO'Ũ’Í+hŪÍït*Ãâ+Yf?á.H’žÆhwŽðī›ū%”&#“ŸgvkĻ7Í äŲ‘—Ū\íÛÔIžhÎįqĶ™HõóŽH7j ûŽ–Åm“ĢŽ`MĨK*ÅAčÏf[)+RؘõmÍKhģo,2FėU˜AĀJ5ŋãŀÅLÎ―‡Ģ>ČĘ~U<ōšŧ9QĒeŪž7ûUA}gaBôąĪ+đmlíeӑq9óĒĩĩ ĖwŽgžEBŲĒÄ"ģ`ī82>%ąTū Â'š$ŒŽ™Ø:YˑËŨšÓĮ-ŽŲ-R2:―bŌųubLŌR,-MJFmĀA?'Æđ=;ģ€gŠååąĪq0Ú Ŧû ðŨ—‘œS‰ŸOņ=ZƒŠŊ1ā>/rC™1‡LÛڍ巭–­\y•G*Š"ŪÝZ3õĒzj=Éf9t`d }‰ø֋}ėp’Ž­ ĨŊK2zǁ Ž„ĸXõ‘tž0ЙĘwfÔ =ā‡DķŊ0ŊðĒCvčiēÕE–@ÂÝđiÎ`û…úĢķ Ģę5ŽÂ< GūĀqë‹āuxž)‹ ŠTEXâKfþĐvÄøyŋ0û―Ŧa‰V4ną…b\?nŽ^Û=hiœĮÜpï )pĀÐŊTа7(ûN°qrU*rĸŠcÛÉĘy ?ēÜ.ßÍ —Ģ‚%úŪšX4RZIŨč!đû· Ü>fŚ“ósGZáĻ{bHHkEŽÓG36|8!ƒóÐ _XiĶË@ėó†đ6LˆāŲØ:a2ØðýJ fäôMl€Ѕ“đÝԂŧ?=+ŊoÔSx dŅënlRäŅĘQÓĖúø‰Ũæ”3ŨDĨÐ Įý;'TųãÎdr.b%ãsė†Pæ00;§ĸKHô§Œ pīDZeß/’a3~"ē›v8ŽãÃ_û‹V…Ïr xĘ+vŲĩžNî\į,öó\9ĪĻâĶADdߨï·zÃĢč?“ĸ(čKÅÅ&Ģne‹õ$ųšf5$ëāOŽ —‚.’”đ傄šēÞ\8ąýŒX ÄxŨ#ũ§ PnBd1Ņņ=‡L’§uÏ ôÆĩõOäg5íRg;蒄x@]þHĸ>:?ŅŅýčMáāT 0ö<ģýËŪ‰*júž,"Ëó^bĻņĄō“/GjfÁčŽy\šŒĒŨCŽŲAŅYˆw/4CĮ5ÞĮŦ=ũ<’0KzÕįäđ[ĸKXæ%ūg°ˍÉ·ÄzųaŠĮ>cČLó+Ÿpk”Ģ=ÓQ+БÉáôö3fß/°3F/Îá0˟z6éĘ]ßVŧö–öX‰b ój^Ôi'”žĀPÏÁ“ëĩ§Å įÜ­øīŊá =ũzŊ@ˆïėNĮâōČā§l0ž’…œ*2r{L–Ü ÖôÎŅ‹ ›w3ü=GŦčĘÏå Zå3zÍ͚ *!;…“ €ģ ßšó&Đü֓ÃēžzTņz·š iüäūYČķ‚]/݃viGÏB9Ũ9~CĪ\[sZJË1?R―\˜üÕý߉4lōãÜÂāœÚļ#æ‡@ũÕ|w†ķAËBŽØ‹ĩðčC6aU‚ˆ(ŀDV8mz8eþã1kQFÄtŒ{8BwÚj• 'âāMĀœaÆŨ]ņB,ņčC‹VOßtŸŌdŠCčbđhâį? “„0,œIAPēÛ†4Å[ï뭆•T•Ā6žņÚįŌÃcýČŦÁŌ―nŠË‘ŲĢå0ÚúâØáģðÂģn@7™Ĩûóâũ3XčūÕdõ…ãÄ˃„mžÛœ…Ķ6~ėģĻĩÅķlÏOÆïĩ―‘KiÂĨ3wzUõÔĻm(ä’åúÛIļ‘đ+ųĩAĸ5–@]đâ§lóĢ?fëĢđā(Ôŋ]ĢÖØ(ķåSŋ<‚8hýJVī°īUJį}3;'ÎkÄYeĒ@öķfĄ†LĖËØŠbUâ‚óISÍmßŦýÁvä“ĢŠ;ššĸ.Č(Ū­û€ úXR{†OdtXãó]v>á݅ÝÔVØĩļĶīzžŠÜü  Bœ/ )5`^ȑ$?ĩĘO(”Đ.âj=eöݘðåBŦ:)'ôĄĩr•P}ÂŊŠ--ŧŸ2”Āö“îeĢš ÅZ2"ČäĪ~Ļ8Sm–rþðĶÁ"âSCQšˆ―…i:–”åg™…đƒgIRŌë0šTYōÁói Ž„ĪęÁbžrĄ]Út;;LÃ<ØÄÐyÉÏŅí*„ûlž›kļxÂ[ްy”#}„öOŋđ Ú‚•F?EŸ"ŲĐZ‘4YLĨЇc SēZĸ?―Ŧa:˜d92Xč.M7w` Ä †ķ`…ČZ62Œ’îÓsŌŌ뚂–0gyy2Äøä đĻ6ïŨ€3ëïč&Ë·!E MWđSĖ sšNûrĘĨ§•nåÞïü†ō OAČKņŠÎtI:l'ôÎþ(JŧũŽŠ„9Ú:X·ÍÏŋč™ ˆ]f!Qd$Z—‰ŠmT―õģčķuĨOÓ@ÔŊˆi0Ōæs3ÞüU'|2^méÝÉÖú%čgHŲÃÐxŋv"óI™ĩš9%Ē.Jüxž’4GÅũQüÔg.káB>þH!–qÝCuūӊÆÖYįIہ?âŽŋ’`ū&ÍøÜ…ÎX KC”ÄëW/ÖMk€ĘW?Ųu}:Vœ}Z{P”ėõQėŧܚ}ĪdYJE^ÆðEu‰īšPõ d:ũ sjĒ­? æĮÂwō!—j—S‘éÉ;·qŧ‰ķÆĶRi{ūÏĪęyĄÅó&·šõōW a vMl2 ĢĸĄkJý›ŌJ(ÅÃ> ÕššÞ~Áš {ĮõĖ_>Ļ RļÄw›+U–ž1ęIĀiE•5 {ķį6Ÿ 1É`!§‡FÝÚU Ų.ĢYÉĮĖüŸ<'ŪŦ―ŋš:ŅU>7ü™š=>6€é­ž@U]Swų ÎGÖ7öŌ“[ĸ[ĄþāØSŽ&oÎÛõ‹-ŠĶfe ʆŪ|ĮVŪä”ŨPSAüōÇZ—þ™ŧÁū-X$ Ž ­CØa•Ԝ"{þ8 úËŨYĶökŪ­å ͆Éˉƒæýí4HĐÕgP.6―ßKó]‡·v%čknÁ‚Æó ģrËÛ{,Ōc m‰ēã?īŠų.“‚@üšón1á‡;Ÿq ó7ŅedÜæPúÕŲĪėIFųĩ-ĐS—LYnÆ9ˆIZĪhŽ7~üĩ =ý9ĒĨ˜å4q ģģ4*=ƒ*uC-š}^šģcĮ*ļiĨē}†ŲBŒ ŠĘĻļŒ1ĻyâŽÅļ„k܇þęËņY-îŊĪōgĸfnY/ YÜUó}ïˆÝx€%ũ ÜØą6čđø#Ygó›vS-73ēŨSÝpģŨ_ģĻݓ->Ä/–!Ī Wƒģ'ë"|K]ŨįŠ+€Q{ŲÓŧƒ—ýöáIÅm ·á5 ž?%ėbūjaäj_ŒhЭ)–~Á"õdõjØ-Ú{КæÂŠ.€”š $P6ðēÕ^ö„_:yŒëūĖS=wyĻŊĮĢšM§‹v󒚄 éWTØ;ßFéSaÁÕđ7MÂX%Č‚æŧĖFciœĖʘĒeŪ‘ipŲ&ÔáۈŠ.@ķõû—ė`™|KqČŋņ”ãW\V=ķÚI]â ÆIYŨüŒ]{<bĒ(Ä%”îĒH·KL݃7ӆĢíhĮ0‘šóĩԂö,īąs― ~vcŠaÏĪVHU’ėÜŦüļRg,ÃbĢ&Á|óļw’þ î›Ïī“™ö_픡njØöĶkQįTlķēIÃ<’b@ē.jö;‚čƒ˜?ÉÓzļ›c„'Ęá9+úJˆēÄõĸq ADøkŲeÝĸ)IįTFðŽĐiãŨ…ĸ>Ÿ2ÏlĄÜqŽŽŽ ĻÜyâĒ“áþfę”Wks’^ŒĘŌŋüŨðĮm‹ ųļßéK€\_―s8IK)ÍĖį{&*ų'Až@ÜAŠëQŧī9NƒtUÃôEF$Y ūā\‚Í€FŌĮAäÔúaũÜų(hŽ8c;“—O†ĩ3 aåđuË|ČCÄF}ŪŽ)ę=SmÁâN!ã·—™ÖŸ4­ŽüO”Z á§&4CR1D3u‚c}åEcĻŋâ>Ŋ2õ~ķœ;Lę[0fÞįí!oÐY\gĪkšešVûĻ—ŅĒwn°čŠfūę’äžĩ’ķčE[čŦf0 "§ķ ązĮŌÛŋ4,™\ČkžÁÚ@3$ÂAĩÀ[—‘Ð)Þ/HķV\Vô@Ęã=CÉ&ÜRĄĖëD1ó8ˆ›ÍŸßíŦcŽH+Qh…° AŅû^+ØļŽŦÉó€üSB }”ô!Õ ę(zl‡p%ŒOė:JĪXŌÐņu˜gŦiũˆāš Ýf-t6΍ÛXô‹!‘FīŒ‰|Ï+Ā}Šþíž+cxGî+!Ā+ũ―ƚûBv(ÜÓ6·_ÃĢ™X‡G' MĻÎmNũųá&ÍBđ0úw=ÏŲ!d‹ÏĶŋ3v’Đ<žũœuūƒdũÚHu˜‰?ĸoœÝú“8Yý$Ĩib˜ŊŽXĒlā,đ2)ÆŊR°þÉ-åîS­TëŋŽ ã=sŧũėęY›Ū›T?į ˛mJŽ ČŦ6QJŒ#E3†]f eþÏÝđ]2ž'ë„ÓbÃá!k,iÂ}Fëe"Ŧ-ž?PÎîÂlö\§wÃ!PÝI3TėU ÓâŨč'PmäΆö`|:˜]§ĘÉ= ųbPģāV•č؛ Ĩbý@įBņīvŪC™ë;’Ņy`ŨéĮüݐōۃÍ$Ķ–Ģ‘ĖÁÜSvôW‚(EÄ4ýJŋ2°ŪÍgœĐ| =šlÜcTînņÓúȌ~ÝĻÎûtĨ„žd™ -û2ąsōNŲÖ`‡ WPÏüîē ę+ ‘ŠwÃG™ LšŠwîį6žãhŪÖ:$ĨDō"jĪ,Ę__kžr}~+ē%ÎuŸõČŌōąA`ŸÞ•h)`%$vÂ.ÝgýF*fāų"ī Oéâ1!FÜÄkÅ1ĶwGg4ûv'ïn3ÕîËČAüŒS§ƒ? L‹ @ÃF„ĸ[ö'ÔiQ‘ąQ:Ų—þđ Pč?ĸĸ{ÅĄ+xåųóō/,Y7s(Œ q‰ú E.;ÔÛ[3Ŧj æcāŒŊ ã6]ŦVŌ0“ļŽ/ŌĶŽ ŠnäĖĒĶ[Ąīc0?―6đƒ–bjąŋ™ĮôHÂē”đ™ģÉßͰ-ŊKžÐį,W āÅPkŨMÂ7ĶÐû™&=ĮāœW8:6TÐëJ=čŽ'åĩ”’ðŲįG*ņų â•ëÃÏŊ3ŽžßqQÚ*Žr?V€dƒM譅ũ…QĢÉE=|eÍ`'ϑwŋÖ=ŌPō|D @4ąåUÍœæ3C1Ü}><š*įIų‹Ą‡ĩėĊčôFŪgũ75)(ōœ 5‚)ÛHa9 5Ã)ô=ė_e3ûuÎ9­:.롎f‹rq67ˆŦ<ž\O†ņ 1ûņēúžÉ+`ā_Ū°3fž #}\—БÔÝęĩpīyĻšu(=_/ũØqåúý mÚ§Âø^ >lŪ‚'<_cVŌČÁBJ‘-~ÃY†dÝ[ķs·,Ä֝WVč°yÛÍōvÁk“5ÓéÄ;úkĶ=oúôÅäęHÝ<ĸoö­8Ÿ"'‚Dc`,6ÏýĪTÏäZ1ūĪ :ÛUx‰fŪžQ˜ ÛýĪqwEūŪŪnļgQ9ðbl!’ <š„aFc(5<]k„ęoå)—žĐjˆÍn;.ɔî-―ûNƚLķO̘ž]íßb›ē؛^XŊP§+Nó-^õBÓDYÎĀgYŋŧÚýŪýÐ4PÕUÆžyė^_áyÚG6.·Nå„cŸÖū(ņd(?Ø&˂č)īR0fëđ=Šx*€Ļ‹ckéžÏįŒūãqžŽ…XMīM™›^?é“Ģ4σLßŊŠîÞ)åŦ1~€Ķsk_ë ĢbĮĸ\°ĩ(Wðˆß „îÁėM)EE[)yö„Ņk |")·ËZ•|ô? īųCÔ"/G'’€%―ބZNĮļ7â#zöîL$"  ;ð~ŌâCŠþ  úV'ˆWV9åĢ·ÓŧŨōÎÄυ2ųPĀnč~W49;%QĸU&DVˆįy&ŧ•w8’AnxjÅÛí­loËþŽ&71HĶ%€Ąųpâ;Zģ,SÜ–āt‡ŪœžäJôʇō†ŽÞ qœŦžĒ+ý{“–kÓО xÄðÞĖuƒ7H u6,‰ŠČY…ƒâŌۏ˜YéĪdŠ+ŨĄ2ŲsymA^”čåŅx'>gŦ_,SŸúæUģĘ\ĘāãˆõšØK‡ž‚>ō’@Ē \šqr*dēŊĀ|YlNaĩ+ŽŪß+‰Ĩúîį„āÓ0čš,Ŧ =§š‰ģ6k:}í…ý™")=”NƒP"yiZ=CîUU>Čič}ËtXÞ8•“Tŧæa0ŧā?F†éÎeĻÍÆÄÚÄ{Ūr:ģÞpXĒ8ļíweÐŠ1öœ‚ødŠŊ%ėoÛÚÆŧš•…gŠLŊ›žĪóÃLŠ0ũîYð`oËáå3EЉ}’·âØ$7Æå™dđÁ–IR3=­ĩ*čkuâ*žÕao.3?ņ8ŦW~Ž6íĐ0°|„ĐM͜đZj”ŧōïĖ S]Z) øŒbĶP2ˆ‹Š‹ÆvqģŠš3Ôą0uĮūÆJp- Ö§ ä°Ŋ“Kôíß@(|e‚ķdŸŲ‡īÛïðéÔZhžž"ŪuÛÜųĒÛĀ­(Dq>?sæģþ7|mtO&FžąžŠeÆČĒïÆwüxFD!jóDPkÍ|ę`[yvS–īî§§þc‡ƒē%DOBuũÂfH{úâFWŦĨŋÂŨ k>)"˜†Ū HfŠƒÞ•nĐ4ЉmVóž^ÞÔúÞē4 ŽÚ}Rö˃Úņ8KIöO%0Ž_Îïî$Wü ĖéP˜}åtÜŋ^m*óš2Ĩv7prF·—b­é›Ô€6ĒeQ5ĐN)0:—šĶZ!I5Ą2œ+“%UWDČÁeåĖSĀ~Z<j ŦĪæõØN/’Ißo|ÃýĩNä‹ žĸ.BÜÎ6õĻz‘ F*Ķ0ĸÛR°/ÛsËšĀčïCdGãû‡/l6õSQ˜+^œãičXĨŨAx{Ü,@Ņ:ląō"4ĩ˚ 7Đ-,6tÛę7!eÎچ&„!)ÍÁĘđQ"ūÍ,R°`a‘D[š\ó°ŸøŪÃ"F Ë)Čx‚Y€ē!Ō†Æ6ÅŋÄŪ3-•RÐn>@/ęä)”H89ęïüŸę߯ĸty0īü’ŠPØSûɋ'ÝÁ\Šnbö\Þ€(šž J3ˆÕZ CŒ:ŋöG+ÞäĐdL(Ģ8ē͇J|Ū"WÉ1úërœWŊĄ‡ŠÝC4І Óõî_‡ņĶ™ĻfŧĖöt=…Ä6―ÛÆ ģL"čaQHp­‹šËŠŽ(>`ļ’ƒš9*GÍúVĄŲĘp…Õ>\žYÝ Ãũl8Īō`j‹–/Å0f`kŠžTþšĘGÐŨ]täīm1Ũļå2DŠ"K/Q'Š…Ã]ĄuôݘÐaËg ĒԗLčï9úķ“y?Q1ÉäÛđÍĮ‡“‰k‹Ļ-Ōe\Ėã€óļveÞÎ;æ þPūÅڝ;˜ĩu…eî8nt§I($ҁ§6v“ģ^üøÆķÄ<3åMā(p(w]/`ÝF tŨÃŅÜ]# T­Ú-\xēķûæ\Ôϕīq=ûNû€øŊQí!ŧĘį ôfųÉRgæeëeZRĀjĻĀw,ÎyëîI86} āGnĒEÅn^Vïw“vôŲ :p@ģ1–p…?Þēą2ÓS2õ˜8@ô\JÉ[M#įoðũrFĒÏ|˜Ø―‚íC(†™.)áß%ĻŠ0Ė@ņ9ŠŠīʁ%Ė ŅÂŅaßĪYī”ïŸBž@Ö~KÉ,øCĐĀR24íĘ 0ÓĖÐ;4āĸ·Šķ1$F†5$Ã[žd„{kõöXNXÄĒî|^öf7ĢŪ{ïŠïÜĘ·#}kВ߉BÚŅõæ>ŪvņOIĨ(åūŧųÕö nJÉ(F|z—r+.ŧt€”ŒÔOâp>øŪģCÅ:[F~ ŠÕuũ*ÎGãÕhuŦ°āŲDĨ%ÂãZ!TÁëây+!œ9‚ŊŠ ėø*X­Ąŋ.>ü01 á=ÁßփK ŒMG˜Tt†ÐöčŦŽýYqþŠ0ĸ+ž XĩëŠ(VĶKë›j’/Ę­Ą îę"bČp5Ūvį%œ~Y‡uę=ĖJ^bE9ĐÛ2 hô4Ɏ‹œōjÓ/›Ë‰!ŠĒsÝÍũīôä ĄĘä3rs~ĢYũ0É―ã§llmmPaŠlÝÂáy5ŧŸ0Ķ!D•ęŒôÖ Sfuoô<‘íM9ČÐDá*iRšīĻåČÉÆö\Þ,^mđ5õ)šãŪWØýļ =5’ŅŒ7Ûæ ĄŽ$ܛG”JWȋvNÁ™tÍ5·ņōĶČI\æÚ:_údx{âÞÅ@ý|~2]J Čë́D1 $wŠĢý+ÚÆï2D:Έo™ ßæ! a;}A õžĸ$ÔÎMA5ĪX˜5ŽĨ{sĘŽ%ĩĨmāN\>ĪČ<?ãĢhā§v ŧ)œĘŒ:€`îĨôäPšļÃÝ1ĪēP)ïđjÔļŽD•-”=ؙōE›5ĖØãáüũānJ>wOWLc}ŋĖĒq…đJšë”A? Āc…öĐLÁæ–ō―QĻ*pý@õÝĮ-“žÂŒg:ÝĀõÝ>ĐQ8ĪCÕKT rúļGlƒ “E@.óęÄĢÝČ&…jŧÜĸƒÞ'u­ÉCYݜÝBPAõÕ ’Me’d€K߯Æũä9AYÏ{æfw3ÂGJMōĄ™ā3ą‘ãĸ €°ÃyPƒ˜3?åIBCcōüK3ãŌËĖÐkŲG-Æ{ję7•T0äq}Þũå‰;cšŨ3ļČ·Ģ” ÖÉĘĒÕķÛBëå]-šnã_E{ÐĢúŊpW§z}Ž;ŋBÍ2IsûœŠü]%6€ÁūŽqgæĘéÁąūïœ`ŋËÜvÞyw’5 }…âåĸ4ÜÝo åĐÖ% 4"#AÆÂą\ŽČ‚øŒdĪŧuļ€Náä§ĩU‘ø·…ĮÔË―øčņŽõŲŊ2ģ9zÝŒ™ū6Ä#ršBØs؉ÃíčÎõIßŧa ĒDĘڐå43ģDÖG‚,-YĄå#74ĀôŽÏrHĩ+ý‹ĩä?ŦÓ.c&Lú\4ä á[ƒrrÚĨh–<Á†GdÃïó‡J]ęEĮ„S+―ģ$‘ZUJށįÔÁ9WæŠ+ģ<(☌ė2§°?pOÖĻ\ĸšÄŧ"aLÁčR…wu4;­Z‡ˆÅCJ 3dýóđ‘F―ĢUĀÛÖ8Į™ú‹n/ë&%ÖF@pˆ­—Å―ę’!5Ēč/'vïŒxãų`›Đ[“ ԎCǰ=yh3=OĘĩg.•CĘ8Ēĸ$ĮųŨĄÁ―{åoAvv!°ã_ؕFÚTýw|fxWDCFŋ7†Ž6§æ^iz‹zkw =•­ēx†˜Š‹-ŧOčYŦŦŸVlŽ%ô7āÄģFîķÜ.ī4ŪÍĀȅoį]ųŠD? óûÄ~°ĨfĸsõôöNB9ûÕÔx' b ˆęý2'Ė+ĐÛ5UՔ‡S…vÎö―ēþO`ÂÞ\:öNđÁB;%üý>W[íÆĐĩI)WAöOY§3ĄŧoDäâtq€ãö9þĮoØõûą}4/؋}L3į­a|ęQgę,ūu íL°h;}\îSüՕŨÚAōŊÅ gčâeÃĢB ^›q$ʐwކÖĀŦąAã˜6qĪQGG$ŲĶ|‰CƅšÎãt[Ņí&û‘Á-ÅØųŠlï8ũũŸōMoÏīOÝâŧYîŊķũžî’ú*“ß‹ë&á‰Mu , Ísý“%ó&xFr­ßY?ÎÖO`ÞreACåĒxi\ÝŪđh|=V7›‚øņCbž5ÝĒö2`ØlÁîWo="Ōę,ŲC :yQƒ jm^BūT1‰…# Āí=ŊZ"U"ÜÖ:Åæ SVdÃ)$ŧĸN3vVžöåiXߘŦge—ŊĒčÃ6‹ƒ&dïĶĖŲįہÛÝ D†ÄĶ˜ðĘė6bįē‘@2ƒŸåėˆČpJ·{••Ō?3ģ,2ōÛnƒ™ý‡Eøąuz°`Ī=_bē4}~þ|1ôđÜJĒ=ã}wîsüĻofú:dNýöûēl••"kãŋŠōB ؂'~ÔËj9t )Gšŧ0?t@đ˜—á’DŦߨ%ÂK˜@4œ'·ÔūdĒ―X˜óæÂČĻðIXíéH/øj‰íˆíŊKĸElŦ[YðÏŨÍ•ãÞô+ĖpģÎ_qŦ7~kfūŧŧ“qĒĄŨg2Ã+xoędTÉjobŠ?‚rŒð@C1įŠ`v}Úðę|C·N<qū{DRâ-84KÓG76&ö!<ý™õCļŒũë:TŽÁíóū|F*@Ĩí`Ą?þ;Ї܂döÂ$y.ĻídAčŊïų"á Õ`ÅV‰Đ„Ðwï#Ї0ú<ž+ÎĻx`°]‚Cb†8účðéþcÉŊFýЖ 2 ZYHbRƒĶųJÂhöčĐøā'ã5p!nĸ>C}SE/í­x $X6xiāvūT.äĞĒmS7éI…ē`\>éL âAŊCđĪL^C‘f-‡ŒÂ—Āž†Ë ÅīpŽ 4Åü°Ë éIģQu{Éž \b§)íÖ?†N 9<ķœ°ûL…aÎō@ũŪkÚ WęqüŋU5î>áŧ0–Vj“G Ī.;ÁšōŒðjŠ<ƒLi~BWģ#îóđC7É+ĻuŨl)""đ&k*§Ó4#k ÖÍ|ÕĖkô™ŦÉcÚ]*t™ôU Ļe|$M 5Ža@Féņ”Žr  lŅ‘t›{ó”âëšĘũááS=ņ~JwåŅ›ņĶütßɧ/M.­üšïūM9ÖđðčũÉĒ?F̟iņjßŅĶđ?%ņßģóUŋ3ōR/[?.ėüŪx*#áém>ęøÅ~ŠBųõWįØĸQē8^œŠŪŦö·Ũ–rÆŦŸ€ÓĀópÁāšČŽGŲv›Éø&ynD#ó.ŽbPĩf‹—ĮÞījĶuøha7ŠĄ‘Ķ>BÁčAØģ}FŨgÍ]į þė`―y―“KŠ%ĪōMyZŨ°bá͒ŧÎTþg}ļ―EÔþ_YžĸˆiõĘČëSYOäwãągckgi7ÕÕÏģđ+Ž6úÕ7-!ŪeOquÜ1zĪ(æ ĩęŲar'–%ũ b;?TÂ'c{ëYdÁĸ]Q?þ‘+ $1Å5fš–3ŸüåŦ^ėĨ‚%rNßÔ^"ÄŨw—Ũˆũä3_X|IgĀžš’?ƒÚĖ–ōU5ŌâŨOm·K|y›cDą”čaŸZ~bņŧ§~" â'Þcm7ÄŊÜbôˆ ?3ąČ#›ß/vkâqßéõ\’>G؉Ž-+ņ‘™1OõélÂm}ĶDšŊcFÆYeBĖ*,ĮŽ>Ýϰƒ–ôį0 Ø7•FĢkđ’ę5Ī ę ˆÃãÉw%ėÜāŅu5Ąƒö9a+öđŪ_ߞž˜ŠĒíčė„˜ÖN{ûdrÚČÁY Ë#KéïlåönŌôČōüšé\kĸH r€ožxĻðÄļÎþö*aŒzOį/Á°@–ftøþþŊōĮ?ā‘ĩÁļ-Í(lËh}LĄ•§áå ]ũŅ: ֋öü‰Qí7\ðâœMV}vĄč°íĩāfēÜÃÚŅBaĨ j­ĒrþpÏïõŦĘØĀ$~ZíƒF–ÖøĢû tG–ûĩD'ĩ…x`‚ŋ―Á؅rïK?ÐŊ›<ÖūÞ"oļíjw#Îŧ{įä‚é˚ĢB=NÉG›Aâw…ŽŪrŒ‚J nę>î1‚ęoæ.›RąōN#·}]…=“RŊõlWĘ+\d†Īĩļ,5ęu ý 3’ÏĻfįˆlæŪ%ó&ĻT†D-‡@“Ļ)óYs^†ÖĒÉO~([―Ķ_tĐ·~Õ! ‘šE1PŽ·Y‘áŽŅIöĀ?ķ<`Ž”$\cDČČžÜTĶLĸ ôã’ę”đŽa]ÍKÜIcîv­Kdā[v•Uû\<ž)]2›ûGI<*S’iy„(‡âoē0Ü*Ŋ‡&gSĄØīëōOĸ>ņ„‚ēÂôm2.’dæMÖAïâØéņųNwĐ>R6ã7‚r+žEȉ!į°‡Ð•1ýûœkč―s_[v/ýeÃĸ–ZýlŒ‰@1ÏõÖôƒļŠÜČBIŸ3f1€·°" …–†žąą.ä8Ėïp‰ŋ›0}šŧØĐïa""%ĐÐÓ nŽ~i# VäJ—ČĮwũĘĸ):ļʑđF5ûãĀĨÕaöúx ])xŌÍeōËæĐsΊ~ü;Zö‡‰ķfēNmėgļFĩ]™ų`―į‚BRLĒėČó:eÛŽt ÔÅD―BïL°_]ŽŲbÔēðĘnŧō[ęޝ‰Ų8Ÿč>+ö}Āģ·ĸVÚ­Ī„âšŠäĪÚKįĒTģ%ŅáIŽÄÍÜÜÔËÔB=ÔÆo'ášųkö'+BiŌķšŊņ-ÆN‚mó·Ž›Vé.ZN‹GáŋqHÆ@ÃCÛŲ$@ŲfĒYg―ėa·ZâĮc€Ą^ÂŅicæ>c4 ūŠĀƒĀJژr^1k#ģ^čĮ9ŦĨ’ĸ~‹Ö;éĸ7(Γúf†*ƑxrœØQSũĨĖþØŠ§.Ī#Øs Qkï—"ŰΕÅ0Öø&;õ†LĄ-úϞąÕgS]}ķ&aį‚gá 7@ÂþĪĩĢTŨXīį՛—ųrüKáóŲx:q^QŸÐÚ4a—'dāŊĖVÍôÓNÖWš:Ž%Ž{.(fX;^pIũn[fDBĀÛjå n\F0TĩHĢ!w‰@1Ā}M?„%vþˆF7üJ–[’ĘíÉ*jŠNJûL˕áĪ#aXáÅZ*§ØÉŌ8ĐđōōĘbđŪÆ.Á|Aąôē4A­RŽííø]yë$aŋĸ8Ÿsíļ@e^ĐH1 4„:l$$Đ+IföN· &Ķ`ũsÝM›<€1ŽíMXÂÐ@FþŦT$%@đÉĢI'ÏûuŊDįköĶčo1bÐ[ä&Ýb7ŋ_ģqÅāußū%ërõRįüĨ-ƒĀ|§$ä\€NgŽÍJé˜6ã-‘ÓݒÕcđiŨ4ëūĒ. sÞ80Û+}1Óž .zI38THÁō;R؋•qÓĀ‹fED<îíüĢ|8x° ŠÚÝ*ļQ d@Ž\ë1ÎĪVą&%8:€0žß·â—„ÂÓÞːðŊ6šäIp#z ĒxŽ;ōŒ‰ō…Oļߋ óMÂCĄJD9…üi%†#zŨä+zÞUŪ%ķæŧ= ŋáÝįsƒŪÛ5įĒ0fƒx ŽWvïá%ķ” Ā%XÔPí9HąÂóm"EūôČ{LœÐ™5Ų={öâļ/W­õPAĮDėw•ö@‹(ÉZöõŪHyŋәĩžâxIČŲš”ėĮæ^ũVÜŨÝv~ï@ˆ€2œęœf&~Š]ęžÝ+D3{ņQfÉ\cŨę‘V܏ōW]E7ķĢzn">!ýųÆŪ–ŦÂÚKÛý‘á^šŅŒK2<'1܍xFáņ!PÃu›Xƒí^Đ)ŸQŠ‹uR[$šŌLģŋũIŪÂ0Aĩ8J™šVÍUjî>ðķDdvÃd "Iƒ3ČH”ÔSß(ũ~ƓÕ5MʖÝw?ÅúuąoÂ,7ŌŠkģð;ëæīÂY/ŪAóŠ“ŋųrâĻóå…Ý $ÉäâŦĻ)ýqÖ[I2Ōfý;ÓØæ.wãV—1Ÿ*/ŌAuØÆ§WÉĻĀPÅķū”GÕx <)>―Ï.G ˜&zZ7w.û—ÞP@a—ŦQ†hd ―ÁˆR‘^[oĸc_wĒ^Ī‘>LŋÕĄvĘËã4“;ą™;đ ZßĖrï>lzŊۊ0Ŋa€lIĒĻËĖųũĻē_E`„{l—ĖŪģà Š^îóv2SFéJXJHg8ጠî‚`õŪđþČ$7īNģ܆pŠāÄ1 Vn†â:xb–]ĀtzoĮčžđ}šĸ§Iþ{–ķ–EÓnv\žÂ]šĀA ĻĄ4ĸl79` Ūgz™ąÍÓžĀŠü.õËBČ_F ßŨ 8ææ“tŧK Æ.ņ~ÖÁbWžY…Đ­n7’ûš6ģĪCƒfĄŌdĘ-wē ĪI ïĩäpó_Æ+d>ĸ~ÕÖÅ)·Šãl m"ÐŪ—T(âÓWŅ…TČ͎ˮxâÜiK6FÕĩŠö™Ū‡<~Ėþŧū ČļXSĮļ=ĘTv[˧…ĸ2/·ÓÝx`T!ÛЕũTS5IŒ oUŲ‰ũąZėoAߐ%æ2ķënĘčÂf@Į>嚍ęôؘl΃˜yÍۓĪ>%ÕŋÓíMÚvZėŸŧÎKņĄ#oښJß7îóøï@ĻevŋDýnī†*t€_\CŅ„+døÏóZ:Ōïi l’y™į6ßÜSzf0'bï‰užŪ“ëææüēuũųŒīdĄC ë††TzÓÝD*ŦÓĻô•W@-Ū·Ēđ`·3I•3DŠcŸy(žÓõ ‚ļĻoC8+‚ŲDŊSÔū9'å †Ųr°čG„~mY€Ä îWĒäzž7äŸ[âP\čšĪcԄÜ`ŠˆėûŠÜúvÞčņHPïý&'6đŌS…Õ—IJÍą #ZßDÖ·ïõn? ÚÛ·l0ëýØz+,ŧ,āBÔE§čõ'›ūcåŽĻĩ•­qžāķøâŒ#ĖYP€ Y#úRIï·i7žÃ%@Xõ5cĨü5=-āˆÎ)=ið…˜0â;ũäa"?JþŒ• ôžÓ•ÁPÜŋ5ĘĢŽuo@öĖ øÆūÃ@ųę@ðu(Í~ēk#œÁ=ö;Ӆp#ûVdžï{ÔMæŽË4&ðũö;ø$@Mû§õDëī[Ī–/‡@õ*'ŪeäQ‡nöFïýēÁHÍ |+ÄþúÝĩŸôųLöVĸ#qI”þþXˆ;ęoŒbކIũ@<Ž18å†ärĐņïDϟÚïgÝï^“0Põ"^!$-D˜Āë.YWv?ÞAßØlÚĮß瀀€€€€€€€Ã‰Ý,ŸoĻ·ˆb|x@}­ĒiĨL=ånlQt?‰0s’åÉ=ø·œ!oˆmÖvāĂq·Čö™ÁÖ*›ē€â9ÆüĢoNÞVqō"“Ø€bz -oMAOōū%ÝļJ’>ßaYCø=zīû­ rDӏý}lÁրąôT:G· \o_OŋŌ§>ūåQ3·ŋNQÁSĐæšb…ļpM?ížiWڜ- €ßhÉ:€‘Ø”<{iĸ#+ēËmĀŌ[@ÂOSGJIguÂaRN._sVÓŧÕN:þÅÜAbŋ1$›1ö8FĶÐčÍũyûÞ?†Čũ’"‹[J‡Ž5YfCŽýRïxeŽ`GŠŲO‰Ķ―ÐtóÁŠ>ęÄÃüÝþ'ô͝Â7zjîÆ—M‚ģâ‡*ņ8Wų1MÔTŌ&o‰ģs.ũFRā݊L\FFQˆĻšcŽpœē]þô§E•ũ/ør·ręžē+KQ)#œôšĻ!yũ€õņBļ‚,)ʐaÁÜēķę†ĢŋŪg­ĘY…zīžĘƒÏüŽEŅĀ#6(e*2ŠÐėØã)|Ŧ}WÎEŊYŦiæÅŅĄÞē)LØd2‘,ģâü\Ļ—2ļoĒ8+Ä2đ dqiū|ĮExķwð+Å ŠuýlįöÍN{[֐ˆØjũļæõ@­‡ ŒX2†Ų †‘ū&-§—ņÉnՅāyžúb$ekÕYO6ĄQ tÖ!o‰•šØ!6Ų@ÄÜd%AEöÞÆ›vgą†î(ēŦ_Sx Ž €é6nÏ ­z>™ũăzVĩĨšęŽch <óebBÍ äŸ3:ą.ÓY—Α”n]P;ģŦĖ ģÏæųģšk–ÃįZDí~Y„mĮmēP̅ސå@Y6ˆÛĨ<ŋ‚ŠøĸRdŽČ#Cr c… Tc*`žŧ — 5ÓßÍė_pƞŋOڋãbÞąP@Ӆ'wėüąâÏÏĒý=KG1#ųö–„—•°KG6]dũ+ *'\ۃŒŧCS8þäu-ęä•Ã:i‰ãÜŨYØZ .ņëN›ĘđÚwh>%GŠ™ÄŧmÜæNhsÏl!“>™ŅÔĩÚ\ĻÎēazRŸrœ“ïL;ïôï†q-™ÜŠýWðc5›p·1ú „đėŽ Ã›Tđ†Yj>TÅ17ļ{“ė:IŠę3R·2D'ū9?68ýĄÏžļÉđĻwR‹ļmf*™*•}ŊÕž"ČŊžzäLĸÝ(Ûí]Īú ĸ% ’\”s('Ïß8ŌZ–NÂÍ|KĀsð߁ Y§ũ Uû ›”ÞąÖ|’wJ68j‰Ędõ‹l+ŋŨNyđ ·ÕčzîŽŌrxQĐÓ― :Ę3„2ģyÚG-e$eÓĸhĐÝŨŸ°ö/_ËšīÂĘg‡ôœ°Ŋ/uļā‹3ĸ‹cš% /za~Æw~U:K Á*|‹ÏeVžu˜=€ņ\ŪãŸÕÎcÅŎ0á­/ÓB:_Ĩϊ}Ĩ Fjð0ÔŋĘH—hj;n—4tÝ7uņJ9D˜kï…EZ$ļw E€ ŧJcēhĩĮÉkXėdŪŨĐ-' ŧ#[Î9Е(E$ĸ:f ÍkjRhũū?QĪžÐ…ÄÚøY‹`› Yū … nÉÁ[~hóēāyFÕ*É_f™(öŦ{ĻþڞëšÞ…KĖ”8JķgrdþóŽ‘Ø’ãUíŽÅaŽdKÂāņQ‘ ͟ĪT‰ķl]šEÜi}‰iø4D|―'á-žĀ{ ýēļ@i[­Í™‘öæa—eĨ$äïSâ†ï՜ǰ‘Ëų§koė&Įüއ ōE”î)æļĘ"§īĒDYØ"ÄŪ=&9nn_ĄÛQ…ƒķė|AšB=>ŋt·ãģž9ž;ŌÄj;!$–Ó%ƒrm?9øä}û-Ē+úVËÄō1š.ŸŪéD"å- q5æ!U-ęõÃâ počî3‹°sޜqĨ ĀwÖxœô“ÕėzdČÆęØhŦę4!îsąœ G =La–‡ĮJķ” <Ý6đ€ŸūÆÞŠgVÐ45& ˃Ž'cÉė”OĨ‹&§Ïä-\š.Å3Ų‹"įW1lũ:†Ę+VƒŅąüŌĩŠ?wĖiu( €Ņís='Š\Z‰ūƒ ý_"ļX#ýÆĪJŌN‚^xõĖũ—‘ÅāHš–ûÐނ`Xŧ?~KôÎlÕė/š~ŲŦÓg–NŸ• &ĢĻōÓCæ 6ĒÚÃe7īĻŽX"4Pæ`é/PæØ›ō2P%qšj„ũū+gϝŦĸQ,˜é­Ó^Ã―j†š\ŋĮ;\ ŚóėZoŪðļEå]p°>Ž7ä^W‡Š'Ä8°O‹“ū$ģŽ/2*Ž_N‹Cå!·{ßf―yR°ĮÎĩ”ÍÁæi'zė‰ąœĪø—Aĸg]„„Ūh1š―Pę0$­ÂūRŽį‡:U–ˆ—• =hƒzÔØĶøĀ2Pž°~U~Uëþ،‡·€8EwĄ1ĐŽr Ÿ]pÕjdšĶp ŧ_VûiJųĮWÂËðoŊæ )=Z7+ĸGœĘât€øũ˜fÕ3– į~TkĻtD"j3Ė ČđfAVZĩýĢ-X2‘@ŧÓáŦĀģĪÍTcōœVO4@Ų€·ę Ļüo Ïm8tī\ðV˜‹Ė_ãZ ð˜2båeS§ÓZó“›LzŌsɊčx?#―+_ؖáōđ$„Л:,Ū―s”ō)žzĀŠÅP^Ũ:ũŋ#@K˜ÝÎ ņN9ąR•Ï” ?ÔziÉņ@ĘßÞģļWێĄœî] åÜģļ{ņ^ˆŋ‹ÜÆ Ũ“"Ë"(NŋŸZëʖ$WėŌA9 „26ÔĮÓ·•œ:T]ãĶöĮŨ[NÃn@“gŦĨ†úhQüąNĪ`_­Nō!qÞėûÉēP”ųŌã+RhÆwusČÃI!ān$|tÂņ™"~Ā\<ķØRžhYƒlðķėÃÁ%ëFŦ“õŠėȕÄþI6Ŧ:o €Ļô2;ũîh1siŦŽŒļkĒÛÍžĘĢĪļĸĀ33ƒũcžYÔí[ÉõÕ$ ýŅX†…ũĪöã―,v‹žūŅŽ ļ7ōn‡āAîÄāîúZ/XsŧvE˜ÂĄ—‹š•BĀuØËėûŪ“į/zĨÁļÛdząMU—ïÏxZ(=™Ó?1’ÞzBø/Kd\ĖņGĀ&šŽÝŋÚO/ųôęQāĄU^ó5zĖ āL@Œ74m€Ÿ ý„N<-Y9ũöxŽī-OmŸ_ƒĒĘŊfļĄŽBŊÖâ°rB|ëåīð3zXû.ÄPLMާ(åT‹j·Å·iĪcäõ [Ë….š —ō8ÎŽ|"Ó­āz=Ļ™7ę-Eã_6ÔCM…äT]pvq1Ó!Ō―WįáæGŦRDĐ(Qōõ‡Ō9æŠčč4ĀžUĨ7čnáA52ŲG/ĩ=Á‰˜·—yҊwNëĩc,€šŌgPļÏĐ―ļ`‹›ÉáLŽü„VßL:ãv– {*j'þ?ӎcsĀÁdŋ(Gý} Mđ Ųþ˜ō—K§ņ_YiīÆōÉ›ųRšó#‰ ­Ŧm˜ŨX„ėŠčáD۝žt]mqcžPSoNÎė[ ÃݧrÜF·…}ŊðhTv3Núāã–38p \~ąb°æĘ{äÔlę­YđéÉĶ#rGō^ Ē(~ ‡„ôļ}OrÐ1čÐQÔ^Ą9ĸiP++0Î>đĖôæXšĘa8ēüfĻtïįl8{bAcīLJ=éXĻÖ4%ļp<éŽ`É―Ę;• 0ęĪ}u:åïŠAW-á"oĖĻ$ē‡dd™ãūh›6Äjd<īĨŽ(<€ēáN•Ÿ-8 [”—ęcÍ8ŠcÔĩīë۝ņÓÏ6•Ýģåk™CÏ ĩhŅJô5Éþ; X–điā9Īþsĸ ĮĀüM0ĮÁ(þޟį$BųŅų<Čð3sĄŸíaŧ‚.ŧOĻŪūo›Ņ5žB}ø\š ū:DGË?ũO<9ĄTŲ–KŌ*PwžÏgUë‡Į@ÐÖXFĒ.2ó 4ŲhÞ@ĖĪ%ūŧ‘ýēÄSiÞ yĐÜĸĀwģ{Ï댨!wŅï$qęZô,ĻYDĒO{ĢG€0š„Ė)9đQxåA€ĒUsHÖõ&ô•†QWɇ.%X ‹'Žö}\cŦšFcâËIĐēX°Ï4þ™›·ÅĀô•xiöÅ0VžĮsžŪÖ$ČPë•ļxūjcđk[œČ0T!Įī#žnzKfBï+Ū:ņ7) q ŨŲŦ€ú™ąŧ#ÞC*ð-šŠŠu7ŠWF|–ye_ĸ;Ąėé"Fü„ß’AÛĸKCî(§pj! žā؀npâäF·ŊöÝōdý‘â"Ôm ‘dnÍũH~wœŠa·íqHïDĸŸ& Ž)ï?ÛņŒZ ą2Ÿĸ=)*AĀޅzÍ+ ûĻqŒgŊN}ЌĶ}‡ÕĮ§Ā-~ėx·J“‘ߝ{ÝZ:ųZÓp1•e#ĸ%N҃\Ԏį.L 4Įyšs;„·ŌœŦʙįÉÃkķ!ÃĒë/e›ë4b(Ÿįni]OyJ‘‚įĻĸaņK‘ZĄÉM―]7]Œķ8ÐiņÏ3ržõ|'™õëīĩ§ŽÎūzīõ;֞đwIåĸjcÔoƒBrŨáŠüw•ũ4^Ō|'ĘVŸP{IúīõŧŨ~ĩyûĪôŨSwIw\―A>6ŊÖŋ:T|ōĨ~JĢ=’)^ÕøÅŲ†·Ëš'Ķû^qžާ›ũ-&ïo°\cąÃóēūģą1!þ˛CVJYĐø4‚ *ĸێpdjM>Ļ‚Ý^9#üÆV4ĄĨ}LjAÞßkZË–)”BÉWÅbtå22§æ, ŌôDĨߥą/įbvr ŌÞĄęåŠ$cÐc\š§j0P:•Ī‚`ī‘âKiūþH>Š?d[––°IŋBņ}ąˆÓĖ‘/•2Qï/GėbĨĶ!ƒv}Ķ6îöýß勨“3k†Ų:‚‰=ŪāZĀīëƒokÝíÔ·SŽS/rõŸõ#ÏļąðŠ/G°q•F―Ó]ŋ­Öy}r‡Zß=ó•l[ûÉý+Đ}_•I@­AtĒf*Ų% K·SņũÉą€ pÎC ŸTK‹ó'áÖKī’Ę-Ýۜ0Î3 gšNwî+ĒMķ.~1Œ5þuellМJģÓm!pû€-֟ËŦ̓oUwŅ„ē0R‰ÐÂß͚‚Ši_یOoy*†…é?9`íģyþ $΃­ßjšcĪŋŲÃe#Ũq0ý­“Í-+XčfmóG‡A ÑōoļÎÖVO&X+ķĮ:"Hû‰ôÏŠŧuĪCI"ÎķU’ēJXĨĻĨŒ )ßĀ†ÍĄĮŋrŽ·Úžß#m%dÐŧŠ, 2ąŋCę6H&Ī'—é šũõå ƒÔĄNq2QųBP?ïb7d­đjŨ?ā$šMđöŽĖ z\čBˆ‘ßš°Ðí^†HËH$„ÞŲxīåäVŌÖb,wé!Õŧ%ó„/ęR=ØÄȏōô^Ņ;•·Œ–ËãŦH€äJĀĖÂÉC–ŊŅ”âßð]Ā„į—ŊĒ`|†ŠzĶTšĒkË9CWËÎs0ĶĶ=žÛóý8,CK'‡ƒŽ­--]ÓAųámE‰"OÃÎ—đÆšŸ)úāVÅJUdÝ â‚ūDšv’$Nđsņv—Ýë[wÃz;:žÕÄQņĖÕcÕu^ģIð“Š>䞆‚H·hį=ļ}į3Žŧsö- ļŠĘíC\s—ƒžFÎ`âĢ―eŠ cÎýčË–CMŸM§^ũ+Ό žýŅī­ŲÛöĄå\*)ķx‚QŠ+cք Á–ïŠ54ð ņ•ȉzƎ3Mëu―ôU4{%+Éadm(xŒš ðŪîbĮ,F‘§Š‹ VÚ1Í\ō8áOãõBÝ ŽĐuÚ áĸ:°&<5Šž ų[Ũ’t0‚Vée}/øn%$6”Íã9“ęâmÝ„í4ðqaôÞšUkÆ#ō8"ņ8`F*>į­[›đ}ĻÛ?ųė\­XoÂÓý!IÖtļģlg+üÆū?8&9ŊŨMŽUýCÏj”Ý–BÜ2Ÿr4XîG“ó5Ø"ۜ–u[ŌPi3ũ ĩMŽ›Ø9ūņŧÓgE—°—=Ü~Z ÝüzÔ&š†)ĪIfØlcŠÖÍYGH]ĐķĀÌZQ]ĢE7ÏÆļf5ې2R+Jôßþ];f:ï~Ģ(ŠÄœgŸ0bc­éūčdúäAųv_ … å0ąĢŪ…Ë•-@„ģ›+―ëëE3ĻîEÆ6bē‰žáĢjœ˜Ū}nđ˜ĪÁl#Â]Þú暉Īj­ÁųewÃC ō‚9AŽ?‚ûyë!ð7%ĸ~ÞáŲÝŧkũĸ8æĸoŠų~ Šr›b1dã•°VÞų*ąhĨ õĀĪÔūŊmiŽ™æO…A‡îįŅAãŠĒÄeIŒ/N”ûÝÚ% ü<­l°-uĄņeČN‹ôė “·ĩąõ\ƒ€†âčJMŧå R›)'IM°rëhdhlsMĖA†rŦ'mĶᙃ{‹kðá3ĩ,Û<Â)Âj„ū KļގžŽĢĪÔę3Ċô5ĸ‚ē’Ø6•ǐ·ķŋ™ãsà ·S=ãĐžÆŽ…“1[žë`_I>Ûyfä`"zÝĶž2ó‘T@U~ 2õķŽŽÆ†AhUŋ‚)Íá}ē[ûqmũPu!T 'ū qņwJGõÂF57+Nä'Ôëû yO#ŌĘÍ ˆóÍ, 1DÜĮJ2?Pōk˜Š:ė]*;‡ā|Üæ’#ãö‚M|€tï›ŧuĩJ”ų|Ųę“_ ŋQ…F ï`Ōzï5:%R“& Ï†0jÐbz‰ėuūũNú-ā·Ð„n{―ŦKÖbÐwϟõäp’+ĸX} ”ž\ƒīĒņĪ „ˆ°ß '|Eĸuņ”ũŧbąlŒWõÅiž Ø9wææžЍgœ™o3åŠŦ\szˆčĨ˜‰mbÛŊÂJtrXĖØ5ļsßĐf,Čn.ÅRFT―‹ī:#*|€aâĨđ?d‰{ĩ̈́žÅË{…y•5\5RHŸ –ïkŦ“ÃåAtšðĒĒ1y4*S&øČU0ų4Ō°óī•íz•ÎōŊÏēO ÞÓįr€Ýķ8ĢA,ĸXœ/ˆÅŽã%˜ŠRÜšŨaĪûōąÅ…æÚF&A16­RĀ ö`f0 r_ ŪÁ+ցd Mē@-@ÛÏ&ĶLĸU['};đĻ™:-<ū&ãŒÉnÁ•…°Q·pÝíŋä9Â1'šäģÜkðšT‚ƒð—ŅŠ0$œLĐOķ<Đ­BĮ᠗uMūégƊ1ôûŧĻ”$”ĘÜ(x+t—rīÝÚā|ÔYĒhé|ãhũÖ7Žû§Ūã 2Į­æ›°ï‚lvęYũ) ‚>†xmG…Œņak6"PlÕÄRI2]ÔøXqŅ;ŪaE;/ĩ°ãҐĨ1ߨãó5=Ũ†Ų5Fo—…0I‹›lÆO`q·ÕŽrŪÕĐâû5zYp/=ūfä›óŽŨĶsšœ æo/Äûį%čßí“ų‰š"ĩŽC”Œ“j@ŅN6ŒËúŸĻāõŽ‰|„:ė M[úï4ø8ô9ÓGēõė?‹·đÁc&,1ŲrDâū= YÎþÄæŨÞéVß Vų ĀĐuh]~Kråv/Yâllšð_’e-Tšv―Db§*óžŨĸfqīĄ+Žø/+°ŠRU?AþAĢ4ÂlAAtvlˆJÝeŅ?R‰â#āÔVÄęÉÚ[G䌯! ĸ-WįuŨåkŧuAVwÍ ;hŠeu~A}‡øå(œæo]*Íj “ßí ĖðĪ*.ýŦéĸ/ŧ|ļÕ žûUŋēUýÆÏ•B(9ŨĐĨäu™N 2âŽį [6—óĶý·ž&#6zëōá/ü7ߒ•QU īxOŅ”}AnĩOšjŽ=â0’fš‹đÕ§ŒPZwîąõÔ*vœŊ"GųV:Cŧ)é˜~v.99ž‚‘,RžVš\†_šËƒ_;  ĐÞ :ú#ðSj(IÎ_Ó$( –―čŒžØƒáˆíVwÅávšƒ-2Zī“DV“č―%p‘ŽāũėAŋÜŊ‘@€ß kĄ"#įėÐUO4ÅÁŧ:‘č*:Ï5€:,և1Ó/ôšA^ö ƒÚØN=7%ē·=ú ãވqÚScėÛýâ0õåAķĶó$ého,įŅzš0‚õ™]: äFrÏÞVô%|ĀÝY͘›YoŅ—…IĻ_ŸDÎWēȈdŪvd―EĄķĐb„ÛGĸo—Aų>ÎöÐņfz~o2úīŸ8SkÜ<ĘĩôVuQe=$a8%ĶĶdųō(Ņb?ų•gŽIČíˆ8Šy !&BeĪąĶH;OÝũ/$ÏĶV7°›<-ķ™é eõ›&Ôē}?Ų@ŌŲf“ ŸÏ.ĀųÝÉËôšęÉä9:]ŧļ39ŲčG̈k(PũĩÐBw”›Ą ÁÞņu"45ëlūāŨŋ…ŅĖë~œBÆÄEė„ĸŽ^aŪ@―•ōNĄž—d‡"…ĨÜŊåZþE4<ũü![N=CžXAqļ%ŽĨD§·ģ+3{ ŧåŒqÅ\ĶÄô0A˜s#ä"_ų‘üŲ:ÉP(eQÐÖR“CŠĶPnd%ĸ|OļØMVö„„…þgÐę qĖâŦÄĘÍ^ėÄ8—ũÛqJ:šb~Ė>ŧÅ}â>Pß ņUËYEW‘›C.čûý(C4ŒæĶ'Á-_;”G&‰F>+,ÛĩþCķbðF_ᎄĖųę\”ag5ũž?%XP4vŨÆŠōFN(G.G ·͇ĻP阎’‡ēGXįïˆ&―Ėķ^>ô0Ļ-q‹ä2qŋĀÓŅķ"+‚ü… õÔ·cÂBA‹tz*IxĶČcd=æ—ęHpi ðd\kõâ*ýūGž5"[6;þJžãä‚*ZyHōĢß4(‡=œŌÕõÕ6]W'ŒÎoâW!A,J0ÎQŅl<>ęķ čŠÍo7Ŋn‰vĨ+ïõģ^•Îwƒĩ͓ˆNZņ;ĐóĀՌéģe”—ķķÝ <Ã4 ß'ĄãbdÜļLļũ)˜rŅÜIý&įIË֞ãÛBZRX·ÆĪLjīĨ“IäšĐ$Š+ Ôy™Øt=x%ŌåbÛŅ4CLOxëšĻGx?Öōųī— ŊÕ ūˆė ‚ÉAŋņ2—ú ÁæųQ&ā‹R™€^0:čbŌgî'~ÉąÎŨÝVJųĘ%šōßīžúA0}s€ųV9„”GÐY %Ōō$ŔōîÕóôݔŪãĄÃúŊ–ņÏ2(8RR>0 ėŽ[ó·o6€Ú@R‚./Š!žœJģÚ0Ý;ïtvŋ ÃûâþNģcé89įÅ ÍūĢ=Ā &ƒÅŨžļs F Û1ģ­Ž&ŪÅĀ·ŋĨˆķiPilF[ĪXáđ;‰20ÎSÕG-ÆÓD-KHõĶæĀíã=›õ|åˆö˜<·â1nŒ°jNđĐé=wIþÓf'Áú:/šH*sīÞ€1|ĖŊW+Ē~]ņœŲ;„čŠĨŋøj{ÚCN–ú/UūόCĀũÝHÛčLŧpzŠã€ųÎÜ>ąiė :Ũë+‹ĀD AātŠjõĖQŦūmÔvßĻ\ôŋnû–“‹)˜PũāüïϟR87ËjÉĻķSWą öëé<抛 iË šėütQ0ĶŽ§4ÂŧŽü .öޚáÚðÕzŨ†Ní#ōFĸ6ÁeRųÜģ Ģ{°ŋðéŒō‘ãxïŌ―CýšVŦn}Âg˜ÂũÏlî„1”jģļVđņSq3‡ābą…Ú9YwãYĶđ―·ī.usr0ˆ–kdÚMbōūÕŅÏĄïĢxA !Ý­ļ˜mXož^ÄdĪîoĀ™Twh‡ņģhCĀ.éTÔŋå{iŌvö%öąō•ņ€åÝÆ~Þ―gãíQ'Īkx…ņ t|k9x€éɑŅōŸŦŽõ‘Šöží5ú?ãĖiQÚCöĐų@ĪøÝ a tū4„ˆ>IŠˆy+ē<5OÂķëæųūo›æųŧpïaÁYņŌ?ĩö\š'‘ģÃųĄÅŪï.ķ1Ā5éÐ|q‡Ē‡! liØđ~ĻÛNówýNŒā‰Ķōōú)â•_Ûû€Ōp3ē–eĢÐĮčØïQkĀ5ÎĀIúe0N&h ƒdģčŽ*\ĻīTäËÉŦĶ\ÅįVõ“ î•ĸnðÉ6lŪôö'ŅÐˊQ˜§đGhqĀûŸ1ŅĢX›66å0ÞF˜é‘1FÉøFGâf€îî_<Ęó&Þžã‹ą*:Xðyû=ōÓ?å„b7Ķϝ ëÎŧ&‚Q2†ØD–ņZĒ;hŦÍĸƒ>IdJÜĀû*ÆŅĀ.ü07ĮáÐaoœũ”BZ}+N‡"Ūā}0†ý#Yr~+™ūU킚OģwŲƒDü-8ÎĨäf/ĒSŽä‚Ļ‚C"?øŨĞĘ)Ŧ€1žMŅLFDzĄWEŋ~J€p/ręIM’ ú6ntūē`<–ąöÃøRĻĄÎaĨƒïóÉ,yÕôíjD zŋŦ)îR Õcxé‰8ąÝm óžû)đyĘIŽá2úĸė#äDĢ%j~ÝUđ:^ōœœŲĢý‡äápr'Œĸ+}ðn&·lfƆ0ĻŽLZð0Sø(Ó ĻÖsŠnBmâŲžWāÄ{~™ÄÜGza"ą‡%ӞŽiŽ‹$= Ōôë†SįþZ•ʧ'qƒ%ˆYþ)•›ÛjÁd#ŽúŅ*Įīė Œ‹ĸčĀuų‰ĒĘ ĸvč4įðFļ PmVøþŠíšO ŦŸ―œ#â^ā[M`Ą&šPĸLbrËRų^éįŦ3ΖŊŋVĐæū―ˆ>ëéņPó|eđäí“ørĢēą ũ›:@8ėo@ÝĪԘ-ásŊe•b˜Wƒ6i° y―öēCˆÚûĨÞņ=š}DVLËM$šĮg|•æ`d–6H*rŸÎUÃeS›#ŠĄĘ|NÓÖĒ< JxE< /E'­0“*3hgÚ?öŸWö(Ú·ĘDBƒfŽ$žŅSbģŨÏIš5 q'{'+Þī0)$@ŽˆØ5ąøõŅüOãƒÚÉ~ l;‚ī’Í.dÉnuōÉæs§Þvï}§8 Þ,ÂD­7M/)7 LÃËw+kīŽ31ï&ØĩĖ~(|=þÖ$ÛþĪŲÝËúĖ&9ģi*5zŋÁ‰øįÁÆ;ðŨĘ:―ŸóŒ8Šĩņa,! -āl%„ęĖôa’ģ<ėô^"Úā!Ąt5[Šú79ӋöN˜›æÉ/bEāQīgī@SĻã”―bđW|/( /?gI8#(‡ãĪ–þ<š†bU·<æãdļDQđ.ĻgÏÞL$t_ŽÃÛkt_T a>•1^á8WÆË†} ïm@ãŊËŧ‹îœxĖs’HØ?;Ln{ŌDð―ýíLÎĢs~vė`ū*ŧĘŧ‚§ÉâÞĨĢnŪ‡…éVÐĪ)M§Ų40ÛÉīMâØ ÛŒOÂ#Ï$ΐCŅÂýøāŲôũˆ§ėíĄr-R Ï}Æ1Šė€€€€€€€€€€á退ŸĶõk§5Ļđâ&õękĒ­“ĩÃß —q ‘w>0€D†5X1äĒ>ëõ%96ÓbœvŨûŽČðvĘþë@Žpjb­ÖæÂKģD—‹*čŦĐ{|QhdPļÞq6ųn*Æ&Žk•ņ5PtG~!JÏąJÔĪ $ïļ-M‡ÓÐþŸāK\2ËÁ’û‹ŅWÆv‰ïݰ. ‘ųŠ“ cWėí]XĄ4œGĀm‘öÛļüUįË*q;Ē·îúūp?Į‘7ß‚ȈCŠiē—`"ŏŽóëØÉ{Ž_Ũ7ãĘä‡ĸ}ō\ģÕôf*:ũó$ Ō7”ßþ5ëSīgPęÆDž PˆûŅãy&|v4ģ™ +~[šžĸ=3I '2sÔPaÄdÞL—'é79âÂû) 6dMOėÓgÁNķĶŦöWŋ+`þûIĒŌ§žíē „åi]|­'đmF4b­3)sūy|š”aœNÝ6\Jļ ;J…~ï zl50 ŅLũķšŲ[åþ^ū†‰Oïų!Ĩåč€jpÞĩJŒĻŪ% ~JļŪ”ęĮ]óWū"&Ëü‹c=ï•DˆMøŊßōč8Ĩ#/+!Ō% lDė䅘AGęEĶũ­>F€Í”˜ž)1ĀĶWWMGr]ÚĘ4âšt,Ļļ…€E|&s€ĩhFÅĒ=L”E!ŲÕíīžÖŽ^}ÐNë—c—st2ÆÜíåŽ ^–ŦÔđoþûÍ)ˆ8l A8ãŽXãęįꘝdŽQ`ėĘ ShéÖ0Q!>ĀÐĀ ˆ$é õņēŌoovø‚Sø:Ų•ļœl€€K“MFÎā8ã°Ûš öQ8íóÞËÚŠd71Ųū  Ą‘Ys1Īž&á‚ĪˆÝ ‰čä#^õšÚ0ũ]x_ųūælAĒ ž-bRúßUW]3fT‹G„›ŽÄrûĖÄéq'°Ã%ÞÖmC›ëË―}ïä3.b_~MābLĨœĀ(EnėųQï{s °‹:Ų<”ĄËŋŽÚýŨ-Þ$C#'dįRƗÁ`9Ũ (šēĩū8,e:pŅĻí•ߒĻĶqŠx^ûB ØŅókĪaķ5!ՋjyÂÓŅęŋ™w†VĀZrÍûąėlDŽM?4aWmŋ-#Ĩ€gs,đFģđ)ÜiŲ&ļ˜āņ ũ—~RŋƒÎœNÅ―ÉKú-ō—„–‰‰þ.mQīė4ÕŨvsRĢ{é]aŽðÍģNĒKč Īd‘ ä.‚Ýc ‘}xÃj|Ø TįņŪU{[Tĩß\`Df2šD‘iĨÕGŽüÐúĄÔDÉ!VJōsÔzÁˆ^á€]DD›î>FtÖþ1yŦvôņĸHbÚ0”œ‘)ūŨAeU)Ų3Ņö!õ‚FŽĩÍq†ĐR(ýę|â֚=ģž8ˆß  Áðt|WwAûe`ø;’ƒŪ9ĩ~îHø톌ŊĻŅ$wĮĐ ðÅ%ýÁŲyĀ“öÛ=`?&ímvŧA$ûS'HúĨ|Rژ)óĀWĀS+$fŲ\ž&øŊ8|ðtFŊýC„éį˜Ėŋ§ēeL‡ó!O†*°qdSïÁŠŽĨcŒáŊdhz@Xīĸ„îvnҟ+åŒûoĸskđqKūú‡Čę0n&{\*D›ā5Ųt8'fĉð7>G6”ó{<ų|”XæF ޑŠüvÎ—ĶŽ#+ƒĢí7r“Ųå…Č ē"…éē,X •.―‚·IËödøĒôü$*˜ĩŸēxÞGPV#~+3˜Ūƒ*"dúI-üZ Ģi}i>· nxšX|B%ķĶą‚ģšÁ…ÓïŋÆÃķ ÔvĪëÃENBČ"8õ1ýðKôíņ+‘ĘË:dVÄ ïŸ•Ōœ™0™ Ą‡ÄÔ,-aÍ%YlŽŽh–ŋ^vâėb5ÔS8iBOp”Į*Üáqúš ĒŅĮ3Ð{ ŠĪá{„˜  'ŨIĀ )riŨcéðuĨyGęîņíg éĪÅ(„ivüˆˆ~’ĪŊŸĸXpĀë\59†ŋīüÃíó Ö2YU~žˆ<öÞą^ĐœĀļĀtRR/ðŊKĘįĢßûŽšŅĶÛÎ1-õķ?2€ôå§WqzÅËūú{āŽôÓïĖnãý7Uʁ}Ī€ÜElĻŽEüž #ÄFTd…―ô;2ōˆ‘\ëhų"%îzS ŋŧķĀvęDÅØ:;ŠD2—IÓuåU°ÖS}CۉEšĶÛę$Fũ]ŽīžĀ…Å+ߎEnF î·ÁšĻ6é›Ĩ T dÆvsÏ =”gŨ7ú’Ëķ§6ÖOˆsÛīū3ŧ؏ķï/ÅĒ5lËgÛ~óo's·élnļę+Ëĸ%MnM6ÆM{”ųũ-}ĸtŒNûHšý*š’î–S–АĢMye#_ €äŲĪĻ&g•^ōœWfąyÎŊú‰X)f<5Ę|ØŦØí (5#'qųn8Šdėí81ÄnÉ­HÉĖW;ŨXEVžž„Œ”ža8ՊxxˆÁ@“Ī4ž sčēWæ›ģ?Ë­ē+ŽH$æ&y9ŊíŦđw:W‘õ {ĶĮÖV`ÆWgŠ! Í3Žæ°šũ°­š˜Ð šæ{ҘŌėŅ ›d/"ÚieįF$PuģōĨrUŒlsüĒŪčįpÝiР“:ûžXÂo†ųrŧm‚Ÿ/š†^'Ü―…ž‡:>.ßY2ŧøïZ0â(uÍ8Gt0ŋÏī6\ [―ōH8øĀÃ@ĘÁÃ{üžęýšÜ=KÝÎJ%úݔ„H•@ÍAL [fÓč}Šd™C!W5˜óüķÕĀÎwÍjŨ7Š ųåÎ!g,:ež'ũ. ąļĐŌÓ[š6ÔæFa‹ę@æI›đo@ Ļoecfõī ‹páĘvíģĨm§y Ųųę6Trâ3“ÎÝVšÁéÐCoø-@õU· ŅPŨŠ1™ĸW›Îv!ØE>pĩ €`õ$7nĸ}ÝÝ(Œįž•KN8;˜^yÚB IÞŽ_ą§Š(Aį†ĩūYÁãõ+OÉPZkNýCū۝L™Ũ l[‹rp)íÜóJÁoL ÃQ`3ĸ6ĸ|N:!,ÚÜnþGæ-ģ“K)jþãvP6ÏĨÓX{Ā„ó§#edîy%­`üŦ_#ąžÄœ°_pF dQeߌðYiPJhóEz”~õKŒõín_v(ƒqĘŪģ筈BûĪNŒGčÍUOiÜ#úSp B.íkēüŨ`ų4šŽ‹J―Fų1…gÝ{’ÃÃģAƒĨpIm/ú+ŽÝ1:Ï˘ŧ…H‰öäįĶņZ­ņ"Ā,Yįę š~väŲ&~€(ĐX%`ËC~Ļvqđč ę .­ ąé Í~ŠV,’_Š#ĸövÐĄZũ­NCŒÝPeM\ú͛ó—Ž3ŧXü‡Zę]{Ųī1ŧÞpfAŨ0R­tŒ3dŽ^túĖ2EЃÅnFZf?]ŽWNðÚcōŽxvļҐãš Ņ{+ ĨĀŠŽD‹ßv Nz$M c ÓĶýNÔ'ÂėRŸššØKÏbæ8`}dŌ'ēĮh w§Œ’Sō :€z#œfr]·šæŋĒÆ0zņD!™ Ķ^8ŋ–ß[ŲŽAao݉Þc‘më=eVóĨjäÖĸy(šfÃ>ģĩŠ_wœTų‚PĄšz]v†]Ģyj^+qÐĸ*†X=RZŨ‡2jƒGېh5tkðÏqÖÆ$ pgâT(™b°9vl%îÕ&#·3=ņ&Ī’zÜg„{j s& k‘XŦČ&.ÆsÅ(!ŽHŌ4.' Ææm[b˜|čËd-‹!Vũ„Wýs›H#Týž8‹zųĪ~'™>ƒīuÅPĸs&þéŸxsČÂ$ڀũQĐ/ōē‹™r‘\ q”ÂG$.LÖfXŌØ3öģ2ŨŊškë°Ðūś)9W1ļΉ‚žUÁØÄÞMģúŊëëķ'ĐÞļúŌvŨõ™ëóퟷ7Ú+Ûãšąë™Ë·ŦĪ]ŋyOåwęyõ |ŲËŨķ'ĩïš ŨghgitGŠõï ӝ[å·ĐŸ9æŦĮ/_~"Ŧ†ßΚČ`$i뇁6Eč\âíäĮŧ0&ģ‡_<ļqiü”‰‹ÁŨq}ÚEÖĶ>TÍ‚MmšNđ'ïrą%;ŨlIŋíó“ã=ÎĀú85m†7Ãũ›mĒūĩ/+ū/“ũVdJŅĄåK―ÅŨ†ˆþ€‘hÖ)~ßÕ ę-ïšQp”Ģ=ŧąŊf \Tƒž@pÄ…pcT€Ų”åĪXü0w ŠāíÖĮUA6 ™Ŋ…1aĘĀ€Gó=ÖĀŋļĘÍø†;ÝĶauÏ/ĮðĮ}™Ú―Ķ[;°_#ƒ˜ÏhøøãĶųOˆx7ó…°SdšÍI~f2TĘēj0[zᇯ‚ŋ<ļn+ Ž øŨ·WbÓãÉī™a8qĩOQdËß@Ú(QiáîV ÏI=sā{ŋũ9%ņ|{Š―ÜD_=ī›WNŸčA~ČIÔJŒqömĸ5ĉũRĻŪĖobžI>åˆÖĪheDÎÕęĸʇÚpMędŧ§Ū]'žõ\„Â1tk-þ3†A`MĄËę·‹å=Þ"4nā Ī4r0Lĸ/c‡ĸÎ@ÉJęJ>“jYõĮĖ€‘ē=ė°’.ŸýY 04ŪÍ&ņ!6š@"ÉnũĘŠĐ+ßĀyûô‹_§ą[ˆåzqy'OÐÓŲĶ}Cĩ%.C‘o<°%§m+‚•ĩ€Faý:“ĸ;ąøV~=Ïîi'Ūu#€Žo—ÉŌëÅēâÜÏĄG–’ÁĘĢDJæ˜QËų-_ļŦž‰?Úæ:elũ7QÉÁˆz-]Cnģ€õôYƒH‘d­veŪ|Sg[]ôųŌĀņļ>76›i9S~ae3 ,{V9ŠQŧWĢ~›Ų8ÞūĐëBOb75ØĘžÂGE!3)_ņķ„L‰G_ë迃f OœWQ$°I"rėv‡M!\'8Ú3w†$Bà öĶĢ9ĀįXiróó_Č8.XŅuŊĸCÞüp;ûŸC<ðĐ?j"ÎÍtšc|ĄåÞ°n‘IcyAÂWüó%ŧāĘëĩŸ#nĶU&AIu ‡ðšO˜xCž \xŲĒEé^üđ‚­úĒ‚J]=íōĒĪð€”ÜðČņ›rķ” ÛÖÏĸ/ԎIØ54Įf|9‘žÜN―ĢsâĶĢÖ*Ŧ đ uyÆŌ$ī”uzw)7EuVĪËŎ}ēÄ(Ũžō­juØÄ3eój•Ûėƒ‡›ŊuåþŦä1ĸzPzĘZū.† dd°ģ„­žHÚ„9æ( 휨N‰^SSå{‘—Ũ4ß<ë!†ŸĄdøÓŧ45<Ö§vËÕyû[!°\ëéd†QÏ oKOą"ĘŽĮOT=þēðđ–ĮLšŸŦ€č‰žýÃŋS!įÜWꙃčDaÁĀ♇{ƒ†,ūDūÂG*9uinŌTŌg()§ž+n]æiĒïžþ­ ĘĻ1ö–tx+ZŽģ€―­ØņmWåە—/Oš}âYŅRÓĸ)‹äé·QÁJ?Ö™ōįf?o0Tƈ[ÅKŒLŧ…ašŠâ―‹žë%ØšwãA „ĸ"ĪRð։ŽĮG™‹ė.Ü@Ó|þpæŋÉ+‹*0efn ™Ų·Ķw‡0jâ‹ðaų4ĐВ<‘8‰l_‡Œ˜q­Þ`Ņ―mVv[*íP˜ÄŊ;Ýzû„5"_uų/’ĩöïÖŌrų†wĪĮƒíVgâqPĨäh9ôėĒ’Žð3mA;ü+q~íŨĶs˜raŪĄSîēGØYúLwRŸ–NFík.ŲĸYQF…ŠĩKņÕđÁKŠe>(]éWedŽŸ6ØeĩĻŠÓt―ÅĢð­ËTTŅĶÂŅhĶ’Ķžę›4ēô4̘f›ĩß\/XÃ,y—7"ÁãíŪ;œšíĖņŊ^YĶ/Q xō]oÓåíCąÁĶóėÆĘIĶB°‚ĪƒŸW“1MŸ‰aÆßĸ…Ą0IĒïôÝĻÃ2ļY2ŽqÜ?ĪĪo0I_ũüîÏŪ?—aUâîę;ĸjĒ9O`ŦÂ^~ -âÐÞE‰įõ‡Ög˜~xõib‡}ÎmPȁ:3T6Z@XĘŽÜÛ§…š?ĻĒYúé+{́åšĖÜ#pĮ éŠgä §žĨ^Ëógģþ=―ŰT:Š ]Ó Þ‚5â,sĶ1ēĩk5đäzųĒ}Ōžšg†ÚÍĐŦ0Ŧ+Roü­%˰zSՊĀ–>Ðxîj/ÜíšV`ĒīŸ úYb°xįWgŠiĮÛnų[(ā4ï„MC'\?YVúí*sڋqUðĖ]ĘãAÃuÉ|_DÆÁ ïx‡·„z?ÄvîŽĀŨån/V]ũ4ĸ%―HCÉpÕTķSŲ9Ŧ}5. A* ĒđĐNØōânpáĻeĒ"ĻSģūqgcÖ­°ˆFtđrJœĸ7ÔCkUx/mÕë+Gr~ĩĀŨä§AųÄ|ļ˜ųþM͉Ɯpd[ôĄÕŽ‚ļ`UÁóŒ‹=üMˆŠŲ°›ÃĄšt„ ՚ÔīVT”­+åĸlšþA‘ôøĒ`Ģ pÞöĸϜP4ÆĶŒ€áģq óĢāî#n īūČïØĩû3$ ;,ËN2„ņw+0ēģņƒE…QĶė<‚Ĩ‘ g)âXƒ@gAG\ÛÔ^Ū} „Q…o##zÜ0igĒæÃ^ļyðWõíąnŽ ˊKYg'ĨTßü Ē •kDL›4 ōS”ÎĶų=•EōĨˆä†Ų)š•(BsÎ\&NÉšûyÓéýdGāEīĀj?pÏČŋžgÃđí$ĨŌĐŪĀŦˆ– däĄŸ3ļjŲŌU—ÂęjŊÐĖŦ CB#l’ÉX Ž€&jķ–MÔ+ŽU=ČkaģÏp-üč7į2ú'ŽjäßũqÍ8·wzØÞÜÉ‹ŠŋNÜ;8ėýË;LߜGŊœü=!lŧ…yxĘkoZŅôÓ?›b<ķųVãųEq„ÁF· ]aŽi(b”`ÚđĻsąb;#ī6Ä'ÓÂ@xãáMŦ`ÃĢēŌ@°đ{EÂ#ëzK(ē oÔSž'ú-7Q—L ;g~(aē8FMEVŪ1ƒË Ķģĸ@‚=ÅqJŌ›įķõ‰ˆĨo‰ģ‹ûïŌ("`ļ‘36EŒŅÜ:ÚexĒ*Ōp}ûķ pĪ—~YCY- îwV{Ŋpæ22OuâŊ’LâåāGČ+Â`Vē$ÏNe7đėDZ”ï† 'ąĪ(88ÔÖÚ%ë§9ęļxk|<+–v›uĻÐ ^){Ĩ(t–A·óQLv ýÖ-OⰉ•éƒøAå øðUõ;y*ŨÐÃPQZ—ÓœMŅcHĀ}ãCSžĢ6åčĸāޜ€ų:ô!Dš kļ|šĖxb`m&ðLÄN0_ wgMāSæ†Mv‘õ6ˆŊęÁaņ>'Ü`ĀĶacčŊ/3'°Pø―úÍĪč ,Fëõd3ŦOýŠ)|1Á݃ŋ†ķÏ<‰7A‰$ėÜíZNaPļÂôõĒĢJžóó?Đ <į:m­ŦīČtÉČŋYƒ…ōĻ­T=Ģâ1óqm(šīü–$f1ÞSzÖiéÞø‡ŌFb<&όē+ÏÆüå―å &°ģ-ču"ó‡0#ósvė~wtŒk”Ī’P>'ˆåՆSBũÅ7œōí !mR~Ôĸa1ēŪ”mvð),Ҝƒ Mŋ7[ūymPL"-ĩČ2›°ï­‰“ŌK&pė1L*kŲ“ĸ!Åh.OĻ*§œ%pz§í]ãX^ĪĨsÛVgMœÓĄ”s†,ýĻ›šÁ Æę;ï·ĻgŲÖĢt&Ė–@h{ļh|#Įtô3ZŅ5ŪųK%FKČŽÅæ•ū„ ũoÁ8SęâEkėžgf ú '!ĻüŽĸ2ËT}öi?8vđ„|ß1tVFđYũĘŅa–L2ļäIŒq‚ęĮ·7Ķð<Â:š0ö ôÔÁa€|ÚïÚŲšNQą<ļ·{Mc.OĪžk·‡•\Ÿg"$)ðEúšxõtÏëĒ nōJgc\Qln2e\6ĢÚ#R1˜ Vl.Š&IãÏËĨNmâ;j9~Ņ”‹Į,(ŽBÃ{ž„ĩĖ\Ģ5€Ģi1k”· øė^dýÖ…ę%Fô^šPƒ;6Ú§АËŧ͈‚Ŧ;įþė\DŲ}9ÔQuĒšSĮ°íëōš2ÔýxMķg*íiRDŒæs%ÔåðÏšķ ÝąūnŋOęøĩxpÐŊß){;sAd>6ĩ”šRiŲ€‚üæ‰aš—:LBĐ{ß§NAąmųkāŅҰ+" )Z’*6ócÛ\ū}îﯕ­ĮEsoݰÆ/ĮlâKįƒiĶĀýtIZKÞ[ûkĪ`cŅNĄ<—(°ÁHD߈/5v"ī™ŧÅĮ!\ÎėulƒļƒÆš!ēFĘqÜãōQōœš„Ģ‘Î“zĻQîy@FÜŨĢßRŸN{ĀH~ĨÁ3ĮAČ#e ,ČåÉú/ƒ Đ įļÏšˆ„kíG;ÛýËCŽ‚NĘģ|üŽ–—CCŒ`n!ų诐0‚IĄïu°:ėm~šĻģ―7ĩBdØąJšö/yk Īčˆë I`Ÿp=”įņôÃëä~āėnŨĻŌĀ*ėŋƒ=8xú25DŪÕŨOËŅô9 ædZ*Ą™k|ĻL`fHÉ2<ÏSdžî IÄĒyøB™ųz `ĸH‚ô$ß_›NÏebK.˰ævŋõÐ:ÓĪFÛ1lDïē‹ëk ?ÆŽÞ ĐXņ―ežGÄÎĻ0%BBĀ“Üį [NDŠŅĒvŸĐÁõ˜%‚äÉþ%?^UÞPnәk‡oHĻ\™ÖU0kqĐ2æøxąX Ąbá•Ū*Ų“&­õņ`uîazz„ï‚ÎLEĮVĮöðôŨŪ ŧIßa(JČ(n– ē ÆyŠhš}<šúÛĨT~HĀ ž–n> 8Å}]‚ ŅÓ~á{/OjEßA|˜qŸõ|ĻwLĘīÎï- u#›āŦáŽĸ ™k<‹ŅK|—VЋõ& Ņá|Ž=ĄŽGžAôc’õU’VG„VĶBWš‰1-~|„]ʉMŒlŦSųĪĪ’Ti†ląœeHš ôI…4$/Ę1 éåÔ;ĨÄ"Ņß OpĨ/ h —[)ˆl‹cåŪeŅu!z,æ“6.5KŲ&‹-―óvĩ+ 0Ŋmj°1üĶJûœ‹XÞÏÄ0’x Nš.K[ĢėlHÕC”/JopNHLNóÂså–Pæđ⨖”ÜÆ  0ÄyËâėôģø‘…íHq8 AĢ6Ođāɘ<‘NAgeôōĐGM3ĢTZ ŧ0Ņũ6ë\øwĪ t‘õ€†ĖČšü.šđÅ:\ē’C8a{dXoZ˜1ÁÝķ—ũĢ9vŊrjŅĄīTRdQ@âÐĮ* ėóĀ/@þ$šžū/wž_К/Š$U’āÛČ\QX ![âĐ.€Â=0lL„ Ū [ž*īũ\GÚĶcĖBÆāßHzZ)vĩMmÎrÚ,kDvP-CąFüų,ÞĒlÎņ‘îÔYޑžrĨ‰Ï–1„öɀĢ j2õÄŌ‘ÃÖŋöĖzT‹ëpŅ·ß…ĖLïŨÓÐĢ\HMÄ―Áoę\quīó3PTicpļÏd'e:˜XŦĩ/…õÜŋzÕĖBu1ĨpšŋmäēęÚĮÞ/Ø14–ØąVēęωMŨÁևZ~Øój![jw‹h:…’‰D<{šāKQE, š/Iéh6.N!°šĘ›õlƒž4A·ð&C[Ĩû8+Q’ÂNI_JÔØbë·a·ņ=ĶEĨÐû4á0ÂEg!†ģŊü'‰€ð†UóPĖÁەNŌFAGSÎÂĸ]Œ\)ŽÅüIũXâþķj/ų a %öî`<—Õj‚Z§$’I$ŧ™c-’›Y•ũ'<Ō|p}ŽØ:ųö%pÐŨęļl\ˆÃŦÜõ„ĸHžAŦ]/ÛY~}ūŨÁɈúáät‘ŋWŨ5ålk?noÁČíSî)šf§Gķ[û§‹;ðųÏžŠüð!ðjú…ø ąžÝõōĮŽÔfŽŨRĖk )Z―J%éëéXßgŠ ųŽzN0}‹ĩ§ė{^ŪĮž@”VA)YÖ ĐdŸĢ0!•Š*Ą„]N6ĸĸLŽ1ęī~pq'•NĨiīŠ]ÂEÉā%`5Ųqž(°ļ,đåđՒąD/SīÚĨø4ÜÍĘڗ {A+ĘÎ?DĢāØ*JQEŦ%j“ĸ+—ôĄöĮĸ}úQ4ápQ`C–XéÛ·Å.ĒwÍŲ]˜Ïí`<†xyä=6Tųb^KĪ-īķ˟B+#”Ó†FėKs&LĪĢÖĩ/•Ė·Ŧ°S5ûž ›&ŅŅ<ĸÕ-Æ&ē&ĖKl‡Ģku1 ĢuđédŲlčB§ĖŪā%Jsę:ėākY,é‰UÆI†GP6Ųæ2·•-]Û7ĢÞ'ĩ(-5Ōģ>HŽÏpßzŦÓn›ŅJ)ÞqÚo ƒ!fL9~Hˆę?ãl ŲãĒI[äĢ)äZšƒˆâocÉqũԟPá_d›q.SCķ]݇*m[ËL歔ŨuuŧĪüĮåyáØ8 M1Kž“ÛÄÅaœ~3œĻĻ–\ŧŽ3ŠÂ‡ExFũŨ#ģú4až5Æ-ŲĐ09O‰;?2ČĘĪĻb"-I|ë*VB e.þžœœu~TæaĶ‚}kŌ–#ķE ŽAozôT­ÍmjŒŨ§“É2XEÜ}Õ―›.ŅĖÆãD/DDÞóŨŧkuĸSų_‹JÝ)zŨÆB(Ārû^EÅkoüôcfŪ‡ ­T!‹öƒfIþ>ð&ä(—ÐÜ!ü.Yw;v·Į‡…9ŲČąéLâ#vÂ(óq’Ų’ŅjIāëOę‰x傄UL‡ÍČC’[hh[BīA@ãeĘÐbÝšjHS=jĩŌöIčėņdö鞝 8ÕŪ@ŋSķŦtđ F}ŸPŦœJí`m:ĒÞn—JÛ9Ęū'€ČēFõęD-HÍgÏ ð’šĶŧÃ(ZRýA%XĒŅ‚5›Æ“L[ēŦUë$žĖjefeYwXI˔ķökŋ-Üg9Hod{€ŋ %Šö.B(āĩN†Uå\CUZ‰-.ó›WŸF>;HEķq… |—éöĶï&ĶŌâēļ…ûFß0ķ‹ĸ|iÃhø?óûøíþ!sh_GЃ™―ßĀH€―NĘA$é§ÏH“+Ŋ›aÓāˆđ]ü>oÁ[Ɏ”0ÓÜY•Mȇ€§‹―Ĩv’É^žIūĀ5Ä(CŌ”Ŧ°‡I˜ ŋŽŪŠYã%”ö%OĪ þhíTfŸ8ņÄ-ČĘBAë ™ókĐÔā5 ͰБ}9MžÃá VXŌ§EŸ?žáXœJC2Š5RI ýëE˜aļŽ0üæ~S(ý"LoÂâŲuūï,ūAwīQFŽšGÄg4F°JÏöÝh3}i_FۚÄÃļęv5āŒL›Āļ7ĪĢb5Ą;‰Øk€úΐ Œ0G=Þ'H[oWŠ5tŧÆĒ]Æ@0-fŽåýÅHÂÖ?Ð#ĨKTÁ~ŊП ”;,ų’·â]$Ē?/[+- ŋđ1ļÄÔ R‹ ZņÞ.ÚŪĄŦvŠ%dŋ)Z§­7jŸÅĻį"s %ųpČ#jįÚ+Ԙô Æwæ2d‘ĮMu2―@^’ƒŊĮó+ ‰Œ?åc“HŅŨqÍĀč/‘.w ũ(ęgnÞūþ,ëeLŽ#w‹­kW hË ][XÝÕoNå:ģ:pÉ}æá^ņŸ€ˆ–ĖʋTË(ëdIApŠÕ™°ÖŒHRø1“ƒ=nī–^o—"V:Ðđ…ËLÆ­;pWA ĸJŠÂ[6Py `Ŋ{Z‡îÖŧNõ—/-ŸPGtČ䧜-$Ķ†ĢšôTĢo›E€ß›“”V–F=Â,zD3t0öcߓĻe$œį9Îa?"øy `-,R―GĘœæeÆeŦžÖO†°\Á—yéb ™U4tËĐD§0›œîvņčåÃÆPšyr Ųm95†j‹NÉB _―Ŋk æĢAĶÂ)öï25ð Ü ÖE1nn•ŧĀÓ@‹,Ø―ÐāHeD·Å[ŸĀäü œ“3― ‘.Ā֔ķlžŠÓē{‚^Ô@}ЈõIB&{D> ūؔPÜGú7݅> 4ĮÖVd–öTî2Ę|ŠĄąpƒ0ąˆV/nƒGŲĀGšAh“#p%ī'tĪZē: þAČpK9qÞtú‰˜ØŲW5å$=„ÛZRl·m9îŪPÃņzF$˜WžžđÂĸ)ó(HĖËŋŸ5o(Č"@~ĒüÖú5ÖG;t]’šÂō`{ĐÏôj―濨•ó•ÍxČô Įos Û(ŦAÍdÐæŽ1ŸuLŲ‚" LˆĖé)>sÔ(–Je t/1ÔÖyõd/āö‹Õš8Îrņh2oþ/<?åØQ•=+ïî=á$z‹ĄÕø—Î.eäË+ýÄ-(Dø~vnÝų+út­)›|°'ŧ -ð=ûÂââO™-§9[ ―8ôŠ}>z“mÃŅiņī­Óózų(ĩ―"ĒÐ(ëĻ3—<Ąš> DFMvóRō‰^Fp*}l$F&šŋ•ŠË2(†nvIF’iml†™ĢLŲTÎ"GjÚ]Į}M{ŠÁSˆƒJąīwĸVnþ|zķÏŲ”Ū^_ĩlYg ŦÐXÅdDÁėRīŅ „öžĐŅÜéÝ­ø‚Ýš‡áÖa$DŽblaqu{D;I7UFĖÁA˕î·ÔúPēû7}eZ—­mÚ_ bÜŪøÍ­v0Í4É*―qÐķ•ęÝķC,"ŲÛô€―ž°špčã#Ũ§Ņ€:Õ;DžDĸģÝqX%‰BäŊü NF†qDxJ‚Å20Ú./0åƒŲvúÅCģ^!Í#ĢÝžÜûÃįbŽĄkų݆"˜;åH„ŅųAŽzn<üJåt0õeĻīcÔrŽ+\:yÄ_ØuÎj7þŸ;ÆwNmø ,Ž`mz”s^ ?-ƒ†GRėą―p†$t‡„†ZŽÝÏ‹ĐŠ ,:CN/€0–ÚP”dãŽhŊæ-–ųČ đÝ·—žýtýWÎ(g<æ/ŠÄ3°6Ïā'_ŦŊņ ˘‰ É)â,Œ›ôDdɗ:"‚Ôđߕï,.‚ōÖRæŌíūũĀ'ŽP`Ęq―vIzŨtl^IRŋEô7ūŧ–ë5zŧ―!AĻŌrIψ‰ GNˆÂĀ Û:Bā3ĪdŒSō íwGĪ)?iÝÁpïÉäíÅüøþi4zÛi°°ë!ģ‚hsýqTČlåXH@æx7`ũÐÜĩ“PŨ1}`ė h˜ĢĨþ”ßâÁxĮéļëØĒŠ M" ,ähī‚ ˜þW<^æŠâ̐ŽNN{M|[)„}ĨėļōwLF ß ‚]`Ļōī/đ D'ø&å/!ÎęōÛ(Ũ(þėK­%if\f{ūtų_Ļ bō0Ļņ —?^ýĘ\€9V SŽ?ØĪÂv+EÝ[ŧbjų;Óđ"S֍z.ā—núšÍų‹X”SÃz?eYʍˆsaÍ&ã[Ŋ}ÄôUŦ&ōuēˆÕŸW`đę‡ Þ€ĶÆēÐûW°­[köĶaģܕ™ÄĀĀâ!û‰šĘDŲ9?}&órÍ]öaâ @qųŦúƜ<Ε둗ŠÓĨŸšŠ(1åHŒí|‰þ9‚õýļsģïŸßVÛQ+%˜.ęr<â”J^Ž)ĢÔWY@snĩÅwõØ5:―·Ō4f|ûM Ņ8[2Ø üD+ĘVDãá° ØˆČėĒÜĒMTIýŨó–ŠŸÓ=焯72 ĸy‡cÚ!YØ—ĐžpCÃZ`Ū3hēœŧį›dƕč†Ŧé ôû|$§ó”ä%Zŧ„ÉXÐ^š3yōâ7O^Ÿë4;KČæĢ)ókPÂ]Ūî­Í-‹#x暉5mQÖIlūÂūRā)ö݃Ze1ðČh‡“ÛwLüžØxŅlt·nÂ―+ũū…VÉėØÄÃåU Ũ`OTÚyG,/é(SI{U-―O–ũs8rƒówIäĢgMÄjBy}ʀ‚#DŽôÏ'âŽģj•‚ž pĪ·ŽŠĖQ\žĘ8PÞ|_?ķ6 ‹ũHjFÚ=ĖÁ/QÓĸhŨt ^^”zópüī§d, ÅÁZĪQĸ äûŒGdWãĢ?đĻÞĻâ—ä/”/ôŽ %…ģ$vŽ}ÕŪYéÛo@GÔÎ^ūþ|,ĐGų{’/iŋĩR?SˆČXzœæģ…“°|˂~NðÕxfýā’ïëv"ĢGݟ9H(ää€ó(Ý*n. ЉīwĐũZ%—ÆŲ7ó …Ą ķŨŌF‘>ÚÎdRšį·SÆÁŦŲŌžžbíRÏ ĄÛ|á迁Ķ !‚Ę :ðú8K$ĐâLÆwðĢcæHîßžŲ †ŒÚã§GXŨ@ūáZ4ĄSZR”O,ÓEČ'W “KwÏ=Z—ãÖ˅$–QįŨ(aãjíĸũ…4dëþŧ/Ķ5~ļXþņFgYøŸúUÞŠXæā_*YhóËp†·Ĩ—ÔۜĄ1ŪqæęTéɃ<`š$CÛ;“ÖžĖø4ĢūeŨšąÅuؐ,é·1ØĪøđVžS'~`]ˆZ™ÔF[J\Ÿƒųė†%_{TõíĶÄGm‡‰đÖ}ņ-ŲžýÜÉ„ Ų‰ĨÚB*äąÄ’ķˆëæYį+[ėš,?ž@Éûœå uĸė&jŦaąYäõíršmRæÖó& —*·@š-_yú0pWš|ŧPøjîTƚ& ž1ˆæ­)ˆvėh=Y4ķؚUĐŒ  ŸŲ:j&LÚXŅøéęÐđ~žïŸ Ļ%€kŸh–GÁrqM)ûōpÏĒŽ„*_üŽŽJ‰O1$>Éą”q/Ŋýk“ņ2‘žÃĒ,…Øģ႐hMô^zK<ÄDÄ= ēĀCĻ=lāëÚSe7å―ÐuúúGŸ;5HßÓį0ƒfÜāēųUĀ-Cä0û[0“LDV _<:·Ž*6æe#Ėōfųr3Aé'š”[ųųJ‡ŸĮ&ø4x°šÁ―ŽVđtŽmtÔÁä~W‡› +īxôQŊ˜|bQEvH6 ī:·Í9á}K‡ 1ŧ §Ös!ķąû’ Ô*‹ŽXHâ ũĶ‘bxs0ČËú%õ••GÛi\ō>—žŦÂmQ:r§ ·đũ#ķkFĻŋah åk@hoģ:†elŌnū aUðkoĨž‡ úžR?đvśCļŪnE~ÔÝLĄöäĩģRĶažʎqõ9ĒMxžąR=0ĮįE‰Fá§šFK(>1ϘL‡„ÉŪąø\Čl“ßJ’U]éGïu*ú#=ęb;k:aϋ4’ēVõ‘ këÕxH‰–žGÆ4ūE)H‰ŠĄƒęfŋÝčŸ ƒö‚ãã!šRŒ‚4‰k$,C‹)3ĮYSbžė)Í<ëÜ3›SßDËø)lK5ö=vÉÖSY6âĒzÜoUgÆz|† ˆųËŌ8óCĐáSVKëŧ"_ˆé^uœP(4ö  2ÞĨ%ՒōE~ņ8ß$<€€ŌJä(„dô.ƒ‘Į=•:›iÍĀFô„ēlTäôýĄZÐĨØ:>ŧHôWë"4NŠ?fßl_ƒ“þÜ$qkˆB™MH7ŋü,}Įä/ŅV4MôdáčÛ⃐~%ßЁ;ð/€õĶh "•ü,õœÝÎÕ%1ŊvP.ÓJ ÞáwVrP?˜ƒ/†ŪqØÚQāó5ŧ[?žŽžƒĪ-Âd7ąÖs\ 8Ahę2Ė―Ž…ČmÄĻË ÓäŲ#ˆŪ"ßkЙĻō/ŋð†=‡ŒĀå/*úEŊ™į—Ëyęōųš\'^ŲMˆŸîiR/ÆÞ sŊ‘•Y-Æ^ŒÃ~˜ķ™ÃręÐú,X•jâ\!N,>I[CŦY5öü0™† ĶcҟoæĘø@ØŠ˜Ų"튚€šWø›‡—‡ĪÛį7H&šlI\T”z°VĒY}ŋŦöļĀ=―^­oģVŠ ŋĨĶĶ} Đ'ĪŅ―aēHÐmͰâb"%ęt:—å#ˆRõm߄æÝYDŅi"ĻJčw‰{ ôĘY!}"dzÕîe "ôAšœÖӠϙ|šúáhãc d‘åę‘CæÉü:‡"%h åÁeGþÕí^ß™ß0žßéģŽ„RáÄj7Îęj^šĀ€€€€€Ā@€€€Ā€ÔʋÍđŠéEŽģÔvÃäÝð|?FßŌ@ĸ ŌÕŲ#uӗ-ÏÉĢvũYøN‘DÆÆ5X*Ũåjŋ’} –ƒš!þÏ01Ņ4ãÛ?ÆÅ øyUŦ‘+Nã ü r°”å ōa&˧—‘ĻdĶ.î~%Č,ã|aT&KyW„ï)UDoØŨaé7|ĩýNē˜mL4õf‰ •Ī˜ aâiĀPléö•B|q·šËá%f9ŧGŽúý…=ūœ ‰’ZÆŦmüąvįt"ЋPr|2AZ~Đ/žĐŨÆÖĢeÍNxjg25ÅWÔ 'ûƒ ę•%ú5`^• ÞŦábĨՃ$Āˈâ'l„péšxÂĐ ™."úŲÖWe”“SFœ^ē’Ėí“JĒ[·üÔÏ[ÃÂïlĐĶ=ĐWãë”B wŽYŠ]Č(Åĸ|iCÔéw’]Ôr-ŠA7nŗ{$ÃzãįĩŽ‹w‘~ŨįŪe<‘óßæĒŧļˆ(v!íųŨŠd9ÁĖĪ”S‰ÝC{ÔZþ―™Ï\KW7ħ—gD3ÚŅNįÖĖž'ĶóürDĸ/|æJžčŧĩ-Sū·ßģ肒ųöCWÉåI'īČ+óãQfÃóty‰ČíņÄōØn.DtCĢUŽÍ Yɍþ!ŽÆyJrå)Ũ}<æ–ÉrŦ Pč-m>#D~ú­ Ï@6ßļ—;ō8ß―ĘŊéÜļÜÔķķ3ŋóCÞaP4,ÁÏCmĀĻA4Óī  Ä>f]įūLà Ē@ņļ…ĐóÆ BJ0Ūúæ9<–ŊßLs„ͰôäŽ[ŅRėžksÞîäĻRZ œ$Ճl`°ŋĨ4ōĢ?Žëv"rhĪt 9X>Ж`Ģ’!Š-Q.”Ē*{%†d H‰ĢYp\Ē ŊŪ8}›Ÿ{ÆSÄЎķ B›JC*š §Þ~―0ŊdB–á·įÉ-ŧųė(aÆ)įSžïÜϖÝ_ÖF…|7ųW Ÿ”Ųˆ– €ĻiŸóķž1éogjúYEwŊwŒĮC ü– TâXđŸĮSdŧ)Y˜úŠOđŒĻĪ-CW3jg(>iIÍį,“ÚįSvÅz)o[ ôéyJgŠũÁÜŨr^Af6 šøõG8dĄĄÍ­-™üĶ|Žþ|ŠizũLĢš| ,••ÄŊ’Nx .›ģtã‹ģüy‡i‘·;ĀĶΜƁiBč°þí•æ9Čc†"kÕÎĀ#~ÓlSTt’|#ęęÉlF‚øãœÆ/›ŅŽBYåïĢ9ÛaŊ/ąäĮïßīJ§CšŦÕ}|ÝÓ6UĩUP_˜YįĸAæX.A6z+g%éœvl0e A<€E'Ðl“(,nŽā>Å0oWĸbgŧKNï5$Ģï―ČÏ!FļŠ›•šÛšôŅO°ņŋé‰6é)ã_ÔaÝpįFŽ<˜ÃOØýū)ėßãøēÓ4Ÿî ôx]Ņ _^ŋģMē*Uڜð>ę–ĒDËCå+Į0 lÄ%\ũ“Øu);b%Æ(2fŲuÂÜ-ëČ\Ã|Pŧ:ëÔŪôSÁû]HÃōįđ§Ė‚Z[ßt™,Ú/9Wáń&ĮäH, ҇ Ih€wYõ„IÖĘ }úÅČ2CL5ē‚Z]“5w3“ô0üŠÚ€˜!ĸnþßJ‡Ð1Î?IƒĄŊΚæ.{í38sšŨ–ļ&Tģa–zýÜPA`ÏKowð·ýõ7ø}ɊOpŽ`rŪjĨŌØīŧ@ĖkĘÅwU“0!‚šÅ‰@•ÚąËQÍG8Ghϊwîļ%i>ĘĶķÏdĸkÕ ‚W -č·“)D<°‰ûĮfp‘ÅwmGĮóé…Dĸg9Úéįo™+‡æ‰QčBcȜÖýÄų û˰Ü" ”Í>yÅf2[d˜•Ę8}ō|y*jlFôÝÖÉ>‘ˆáāÚÄŲ9‚oČąQ6Ÿ+pl5õq}1YÄĪæÄIWO ^Ī^ic_c―/,|ŠŨŨýg)՟Îgēņ° iĸ&ā‡Š3aÄqe9/åO.Ö„ãëpÏÔēĻ8ĸœC]%m—’ €ÓTĨŠ%îv­T-[‚Ó_šĶÂnÕ^BÆ'^ū KĒļÉ Ýˆ5Ž—Ķ>Ā^ÐÄAßÓF736TPŨ}PŽéuĢ"jDøÄåö>…4Iâü‹}eÛĮ―đt—ķûD–øļæúKņĪĻ—›Žýƒ[Æ45뚆fg`DåÞo1…lßųō0Ėä4$-›ú!―ޑ%9Æ–‚Vâ!·ũŧØ-FfO /_‚/Îð$û:§“åG] ÉĶļ ;―ӋXeD%!ĻÛņ―ĘŽ&81Ņ_Ÿî·ČČÄīå(Ëôm\cËwöÆ7ųË3ˆŅÞNžGޟ=Qx*Gō!ÚӒę,ï:YāÄE9-―Ԁ$·ü`5ðjŨ“kØŦų#57ýqsöA@W’ė[xóxūŠē―‰„Qũև4q™ á·ĀV fNæJŽšF‹­Zŧ4Kúd‰ųÅ@<―ĖŨžnđ"fâWdb7îÝ;?Ā›āx åĩ:S õęBĢzB§ā(đnü–Ũ3ļw–ņ~̙ȑÜļCÛķm-ņisâŌ2Ä;‹rôfôĨ/_/“ęÍũ`­{!ņ„ŊũĀ ž› Îërïm&ŋŒŒņBWŪĢČĻI{t#mųáŨņšéÔVĘĒúæ^Ū@BāOːkðÕīnT8\ĮÏ,“>âŽäåEĀØåe§Zŋ§ˆ ’]”up)ĨŧwPüÕP•“ö;>›"ÞÉ{ĮSéŋ uPâÁwÂŽč…Šӛž8ðwčņ*ØëœD< ųvBŌŦ0âýŧŸ:ĐdR6ĻYģ0":äm9a6 ûÐ"Š'‘O]—ę8Ļ)Ī>ĪÏÞ7ōžc5ëÎ|LŒ9mąĀ,UCo\HĐCsļ'$ ĀU;GÜ'ögĸ[8đ(iĸkEĻ›áÏîyŽ…k íŽð•‘VÕĘÖqÝšûO^žtX‰++ķ̧ũđ "’ÓːĐæRĮÜb‰Éhƒoýø`ïę­RKĻÖĘûp`UÅų‘mĐí\+ŧq?,ņrƒĖdp2PôD/—ģ•óĮ0+;6œ!‘–E[0ąårŸ<ÜÆj§FŨø==\ÝŧØjN.āąkk’8Ī!d‰&›uēOG}~û€ËĘÚ(Hρu“ōövąį)Öã$}óІ­ûQï°v‚,˜S†ųJН–/qø tæģāĮŽd‰č{ģƚ"=}ÄĖŨ‘%ŒoMĖø:ķwląÄ2ęŽ`Ö1šˆŽžåC5,ĪH…Oyā‡ŦÐt -,™#v…ĄNԘģ1$ZD^æ,ē-’øūŊš0-\ę,øÔë=7č}øģ ûéĸnï}ÕaėGé\æ]5Ū–IŨ Eėį&8ëÏ·)?’ö ī>ũaŦõ<$|đíĨNÆ‹'|=aŅŠi†lĢz8í™Ũ"Ũ―É4Möð93!~ũTŅž íŸŌō°šWĘiný‹“(Ûs§ŽH=§öïåƒĐØĀ€€|;wÃßß\|6‡Ą“~ ü<øt§āšŋäü>Ą|:9ðt›áëĮÁԐ†[M+úc#”īSŋmnėų‹ųÃaŨ(ýüăŦî6Ģ<1ąôBøD*ųnZVäL PŧԌūtCÖ@DŅ$cũŪOGįÜ}sĘPŠķu„öŦSÛ^M(Ą YžŪßĩzh~Ė`’Č3bz‚DXr›‹ã|>ÐÃNŠŪž k!MļӊņZk}― ­ˆeR)f Î/IýēSÜ ą<Örälô‚™­,éqj=Í#_0Ŋë†ä‘FHôÂR=pČŨ:œQNTÅÂĨ•‹‘œþ•7ĖAÉ\Íjęōýnjåu)tkŅ>ÚĒnÞ<Īéšŋ-įĩP8Üį4ZŜ<åÏŠžĄLĄ$Ķ%Æ î‚<ö'ˆđ€–9œpā&]՜!:ðėӉFMy1íjpóSí8oGЎē‚+ī2+mÖūĖ ]Ë&å|!sýdōƒošPó0vŪxĘÅyQ[ÕÕEZȌ9†þt—CA[ožå™š|û”aX•_É~cŅ ËŸÝ5ÄX ‹€BuJ%ŅUZ9Ÿ|ēŧH893N,˜eūUū\!ŪJĨŲ úþʏ Ā)æRIxdo+óŋ9˧pa ŒZŽ#—!kĨæļݗB%ĻŠæËĮėԝLÍáKĀå6j<ĐÚ]4éÉ"úÃŊEq iėič>4<ûŋ9ūCĻŽILK‹/hþmŦ7ōč—YŲÝŽNžX"ðŠ&’ä,F`"!ĨũŊĨY`ŸÅEme^ƒ<˜Ž=ÃîAŠ`†âĮō·œ‘5f)ïebōÎë2Ė;TpQ mƒk”žŅJ\‚“BB>> 7a…ōÐą Ũwļ›T·ļ’ÚYFšUkÐõíä"Đød3• 6mØš+kĩGõTNīĐ|“žŨĒ „kšUrŠ\·ŪË]NĻ 0úÆŋ.KydKÚ^žĸ;#xŽįĘZ:„č·î<`å+%ū#m=iŽėĀJ? :Ŧ44/ŋpķhqŪ&})æEoGˆĸhYÝŨę]ž•ų%lÜūšãˆAĻ‚élĻß8PZĸ*W ģōæ2qЙjÏʗĀF.ˆÜČpĶØK Xļó #Ú·­#…d]ŸíwqIз‘Ķšņīģ…žD-NÏ·RN7•čO@Āīč<Ô­Ų€ÓÔï Óķũĸ><ƒMÐ:TNæ8ģßÓ5KpyWĸ}ÓÞÝļ‘lrģ‹*€Ö`ULzÛφÕ Đ‡nr3šæt9ĩ]ˆĨu‘›r/s­`?MĢŧwÕäi˜w흋ÐpFîyl0ii”Wïåmn ܧ'CĮÐ É{‚ËИ\V―%FύŠXøčEîũėũÂ8ĩŠåōéðÔ5ųÆū&–þ|õ’VīW3ũ€-ŠOd~…pƒá*q ïŋo/ˆ†v?‚4ãegįČSJ$e]*f#ī‘f/―ÆeÄŽpoå Óåę/ŋ††Ä ąy\"}ÏŽÂ{ūWxHð äĶĒrųų8%(îC\%,ŦĒķï|·ĸqƌDJ1·u,Ę.cû?Ø7ŋÂr``qī[˜bAþØÝŊ6ŊýtŠļ›ŲAp›wŪ ĒNÞ;”3i.ßJ45šåÜÕ_{8ßwÔŋC͜šĩĪÓYƒäŋøŒŒŒjŦ71ĄepŊÞĒ Œó1.pÓīēvU :~|W<Ëϋ[^yŦūé'ōØîĻPlļocâ]Zp2󠁓šÍ\ÚRó%ž=[FN"áÆMEĄcÍóÅRø€dëûžÍô3?DCļʆs‚FBN,"@UäúéŲîɍœóåAûŌãL’õ[Ēt&Gx[âpIU$Sž‰.ā“"Lyõ ˆš-ÖxA`þj(˜á öčÚ@"nŒ2ß*ŒaŠ™)dYžE,UĨÝ߆ūĸ8Ũ =įŌ*x͚+@ސ!Â,Rô%IĐÖõPÅô>P (ŨĨnþüŌ>ŌÖ·wŦ:uą™œqagŒÜÎ0ÎR‹LWu=0ý ļËąT…ũð9][·ũ™ĖŋjÎo[Ŧū$™ āˆxB·yNĖóŸŲ‚ÔÝĀ&.ĩ^:ŸāŠËĪušŧÂa›éŊAįBcŒ•ŦŧsģîĘåKŊāfˆ_ŸŽp_›ĢcšXmK‰ĄÆÂ\ÛJļ}ûFšF@Ŋ†(-qYœēŦŲïDļýōúĮ…ßP_oĨ7Īq7>HŠË?~―šŌÅV†TsÚSx QVm&íßĸ@û*”ō;cðĨēfVzæÐø­r§Ō‹ÖeÓü4 ˆÓ åã―Å Q”—ŦĐŧXļ x~9DHÂĐÅ·X_Õb6ÞKΆCwžâļĐĖ΍ʉēDą)"K°"ÔÝąÞēˆž›ûM#ēįG4svđdßÕmÅ9“­™ÁiïH=ŪfJõÖ]ņõĩ#uØl‹WÝëWæ˜^‰Ŋč}ą<„ø<ķŸv?z“ũ?„å‚ ÚîcGÛŃęZü 8ĒN.ÕđV›RuWS$t īũ[5ĮŨĪJï91>Ģ;ČL{ė|Ũ&–đ"hU@Ė;>8}ý3XûáĄøĐN”…ýX>UNä^ĪZĒ /H­7㉔K‡!đЉ'īģũïCâv­ßcÞ ŌF§Đ‡õÝî%ËqÁ™ž$ YŌ ‡8ĖVũ†ŽŦæāiÔ2S\ár3l‡$Ӆ,ĞĘc‚ PĻI+ƒĸ éÚB6Ĩ6–ķ.00ø`QŦņúSXĄŸēÓē{ˈŧ|.sC ē6Ē‹žÉ#āZ éŽsûėv' F h nĐĢr*nÓ{­é/EÓ·ŪėOž*“€IĄœØHÐ"ÎäĐ6iēåį+'‹ ÔJÐ(J}Íäķ^TķĄŲ}P(—| 7 å_ĶjŨÃĩI ;ˆļ‚Ö}”™§Ü-­hÄķ7‹†čĘ͛Äp!.Ō'ĩ‡Œx|äåĀß8hūY?Wg!6í’.ÁÂAšVAÔĶā/;Ó ŋŊŨ2ûÁíĶú•rbŽ”š°Ą˜#Í―Ŧ]m{ïĩ&uDĄá„4hŠąÔD}Œ§ÄQøŸÜ(•ÁŋĐ$1ZëP+zwįŅBØRéOQ*|žG=ã‘šūZ}Jņũŋ ãÆōõ›QlD_ZąI$+,T2ÞÁ Ýč‘5ö‹*‰cĸę0lqĶ zŠ"õž‹>gqé?Ŋę—.㐞ęÅXūˆ:‚Ý1Š]ûā$1xÃ―C°Ėų9P˜Ï5øåĻ8ånӓIĸĸ_·6óevûzŽôFųöĶŌ|Y’fðī[ã†všŅúQļÄĮ‡}]‘yŦB-ûų§ĶdjÛÉa­éLþ‹>ģ·ô1Ëþũ‰ąØÆē‚HߓOœ,„þ―†VąŽ1ë@‚ĩ%áÕ‘ôâÛũa›JÃFīÔ#āÓšG§MŒÛF%žÄšoĪsӒți/þOzž‚ĪūŲ w}&HÖëCī˜mGrĻUŽSŊSÄ(ŋâčq‚šýsÚu#ë‰eÉw9Ųäî™^tŧäęLä™âN4.έāõļ4ŦžęZ–NK•ljÄn-•ĐĪĨĪ]à Ļnõū“Štúē+ôæĮÍófü\(îĸF þzÚoĪ5ĒUҧ>Š Ą?=HQĸIęĐësõĪvI―*éĐ3wˆÕgI JŲzđŪTŋXRlP Eš$öˆŌ/…Ý8éÓE™…Üšđ2ÅîEBoōÎĀMę*ϊĢM!â‘=—­ŨbĒĮ‡C3·OÉ3Ôy2+‡€YūËɍĂ 7ūt€,…đHïĖxZq$úy˜'ŽgþÔ'g“Œ+c’ĖØBü{_įõ”Ąð&üĨ=9âüå\ËVėéį%“Ē™ÁewŨG’"4+ŌDŊ ī.›ÚĖ[90zøQPIT†2žC\‰ÐúW$jšKIFcą˜Ōš3Üeīo KSÎÞúáĐbÝqŽ$lĻū˜ËÐJ·Æ9͞ÕŲ3ģãК ’’BÉíųÖÓģÝĢžw|ž[Ō&AhJę˜ĨŪjÚ|UČÓŦƒ mĩAh{Sņ8=ŌlbóO& 6Y%‘Þ1ÝŋρšlĄÛý―7bó§gæĘG*Œ2~ßŒÛ `ĘïúïJTęÛsÐ|8FĢXÜ+Cgė_­:nzkÝUZ3”é?xėGƒ–Û8•“ũþXžË֏âũxÐõ]@<Î―Éxģî ôÚY†eÅĪĄoŌÂÅMT”ÁiJpsĸPÉģ~ĪŊ ĩ…tïÖQäš=ΐf“R;ŽŪž§+ܟēðBۄō"Ėï/,rÏãniÂ~‰éÚہE‰T•âĖeĒų‡Ũô ŽŋmŸNx9!—TEîAå 28„ķÎIĄŨ"CĮóZð Î!—ÏĀ>axS—fÁ æ R6‹―ˆÁÍƟMWŪ‚`Ë ĮB–!380°X›7üđžĩĐ{4đū„SõššuԋN˜UÕĪ<Č3ęuUø :ë%Gœö[ŽÎ#ē;ŪpJŲŲ1ÁīǐėØĩ/_ĩðŋĐ eŋ§ Íú‚ðØĮė‚ĩÞŌÜ^o€$WŅyŊ3tH&Ä —đ„Ïå–ût.0ę>ęŨd“’KFiä*(#u+áÔ9ņœ%=ŠóĮBBĢ5SĀOBS\.ųėuÕ –Æą.ü]Ž:é=Ëb>NEšđņšÁ·–,+ČöWuÝï“īšÂRįhžl/R€$æfúULēÄáĸpU>yØ`<ۏÔ2ó]GšKā›š…ZIĸ|‘wĢąNąų/ĶŲï ĸFqøÏÎГēķF+oƒ Úmėž>0Õvyfۊ:^ėkĢBÄyFĮüý(dSb>] ;Ÿ›‘°ĸ8ēfÛ(ۜ0õ!ŽÁ!Ä4Å9óÕøŧMÍÆß[]ŽÝ"QĶ›'9ĸ (3SuuK æoĻ{’<ĨÄöîj~Y0―’ę;Ũ"9ēSö•?cũœQûĶŋœ™oĄŲ9ü‚`s<"01kðþ$°ˆŒ‘ĪÎßïTƒr@Páđþäęú%*ųy؝Š&A(#nå>–Į’Þâ<•ēígŨ”ōó*($˜þ‰% SÐČ,K‘4Ũią暒áøuŦ·ļ]ąü:Ïáïk―YwąĸŅ<_ĶSūŨAßf!ðúm|>ĒþNāüŧōuáÝnęØ.é_‡wü>„þŅŧ؟‡Ņl‡ŨÃîlûmūģþTŋķ?'u>Nŧáwd—o…Úûŧ'áîk―{ø{3ðú …ßFšwÚ;čŨáôŌfŅ}=]öl8Oĸ\â›OĪčÞ%t›ž2ŠOėãœ2ĐũøSSŽM–ݧx-ëĶĄŦūþĀþÝ>aĒvöāõ°ōŽï>ÐĨ”EĘPmėV5—õĮþŽĒæėØŦāÚNv~īķôĐ$'–†zæe,1Î^ƒüņGĪ3?‚Ŧb7GœŦVŌĪŪådŋļbžWŌ5Síã>þ đMęBbvũw5ųI"ö<Ų=jÄu#â•āč^ŋ]o•ŊCŋWôŠŽ(å_ĒÆƒĄõÛá&ŒF-ÛÜ8žŸĢūH4s:,"įu4ĮápÛzÖA"V3ūøx|œŒĘ?@ĻoÃ2•]2ēM~æG#Háx”ĖQ‰ĸSîUEĢnQE:dÆįŦŽ9‚Ë™$ģäƒôī―^ãVkžcĄ1˜h/ð_"į0Qž_Ī.*jCgžĒgG#™đ™ėŌū°"0OĻ1™?IŸÍ$5UaœôÂ-*îÂė–ø‹VÜNIĸzŸ‰ũ„n#· cÅÃj[㏭Æ#°,ÔÕxā•Šā›håæī—ĪŠP’ŠŧØØðŧÅŧųðÂv'Ō#jƒcYüHĮĶ’M’%9KaŽ‰ž%Å9Ė)ĘXIž9ØÕGM40GX'hæ^}S§ô·wRdfuQûóDß&ϝ4 V„xŽ 'Ջ7~ v‘íWBÏ*ãóēl3'ð1F„1QaįāY˜š·m; *"‰ŠÔ7ū@éą ÔĀVÖėch”ņž&̀Dž+ûžvƎÓ$ķ`ƒ§6ÃļÚHĮÖ,vE–~ÜÞ{8Īßh^ Ūúk9—%Cãŧû·ŠĸĻlW˜žĀI+fÝüuœWe&_>Ã5ü:ˆG°ģâo—„Âī°Ý IÛ5ƒÐ’Aœ€4Þ$“ã4ž„Ü݌Fą‹7D:*…vđ‘ĪT‹ėN3Ü"Į7éķaf#ÞķŠöa§(ë<T ĒýúdsÎYNUį·•{*~Ÿæ*Pš$;Ōî>+ þa°v-|™–Ėi—q?@k m&·`DŲLˆĪÅ؟úŽęûŦό…Īrå˜Ï ݊.0TÐ5Šüú2ôãíUīsāš]ząÉ:âũŲ1zjK4Cq;cĪ€ãŧ“7Œ"ÝÉDĖOwģũÆīÔ4ݍÓS§âĢ6ČŅą6#ËBōÝaÆ7Û4ąÝMųÏņ>1ŸæzPc€wÐ îŪ3€ŽƂ,3°™ÜŠˆv_ę…Ęųnē†h‡wÎrƒ"Ũ~7Wĸ}ÂŌŋ_Ũ–I,ö|Y ōx./ąĻÅbķVōiŠoz ^•QÐqXžOFīíeyÞX\ŪxLQŧĮHņáī'K^“(§Oü%øwS!3A­s Ð2p6š‡ĩŅ}݀…0ŨYB”"ĻŅ6WĪā`2ŽĶlĶÂZ)ŽE7^TæĨfæ$Ĩ\ÜP<ũČð?‡ 2―ü“tp&īTfTT—°æąą~ŧTŌāpũš†bgŊÍüįã”ōãM3bĨƒĒؒnRcŨ|ó܄ÂN2BQÄ]Ž` '5…U ^M13ūâ<Փ%äØáÍĀd†uÅNīڞ(·pÄÐÕۊcgÄ ýŠ[Õ3īpę’}Ž]~AņzK9sz•ö,čÜē(Ār:Šĸ;É'đ;~ĐŽ`\°­$ZÁ…ˆ^ûØOŅtW”vš_ÚĢˀšˆķÔ―Ãl7xī(ģ‡Ä†w’Õ•ū…ýl={HƊPËFA0}kāžļb˜oŪ•|yp*ômR!IÉwŅĩĒ‚q†`įš{é‡įĶâ6a‡áG^Ü*,訓ð$"AŒYËÂOœĪ"úˆ ÎŪ„ÝG3!zĖÂRŸŒŦ—AĒ,ēŧåVö’E#wU2Ĩ4ÂkˆI—rkSį8 –Cd~õ!AĶ­9ˆŋŸ Ę',TÐ=yšt‚u#Ē•.YüQ–Ž ĮIĨ5q1GäISĀkķþŠįũߖ6ÔZđCk6zíōY967äW\s’€ *Œïƒá 5û0―[yâÜnįņ_ݞāL“/?ØÏF{ ‡[<āhœÐØíķÚû(  ÅîððeĮeôŌ~.~ĻÂũHbMĻ~Tû8ĸ`ty „ðāÜxôC›ō8ĶÕÛt+jāÛ-ļōkčãd W‚@ģ‚ˆQ|3\wčVĢÎäi™7žq…čˆĮŋYqeŠ#•KŲip<Ï]G𙑷ī\å`ÛË}mā>bo4ŒyýĢ%āý<ŠØwJ3šāgÆtŅĻÔ&ŽĖ~bHEŦ2û  aƒuLž†5ëV„˜äĨãĐđ €OUƒŸÛĩ9ü”FKKjĢcũGc­ïŠwãČŽÖ6ˌŌÕâxŽō:§LĨj•ŋaā˜ĒŪHã4ŽĢIÔ~]Æŋõžp9œÓÏds@wã―&E§ZŸ Îæƒ8x%áŒFt%–Ū.BŅVh%ŪĩŸäÂŨĘ'.ýDbxęŧ…ÄųE·ZĶ5öNŪÕ/%4ęZ“Ëߖ \fÛ,oĢ―'3­’úæTӊöĘ%jŨq–.p^‚úG™‘ð›°hD°ðÕ|?ƎÚrū0D‚rôeƒhÃzāįUlQM&ŦÎąîgkũ,æ&·ēr=6ŦÏG=fôõŽb홷Ü!ū“.xÐŋtĩÔbtš“P#7œĩæHĸgīzAģFyŸãŧō1ø ŋ2)bHbÄú]Ï—Ū›^ũôð€ąë―ŽĖ ĩęƒ$ĮcŧÐL„ŽcZ”Ô5>Ũܗ §îtīmĻseËyŠŽ 'ëļdÁÛïŋƒž;}øį"Á)ô ßÖüŽÞ>ōĀ[3ü;ā6"Ķ E͗|Ä{΁]`ԙšÆXŋwÏš0ģX:mœ’øø€ŋȧwKōā­—>røžT·ŅÁRĀ6OF6ŠýÛ*q#ýOĐĻ#Īx*(Ý Es‹>[З|#gĻ'ŒL3FĨE†AĄd7/čķ°a Öčw­Ō@eیr§{NÛt6F9=@ ÉŲûžáo#―Ÿ2Ģę•·OöÔb!t§þĄ&ĀīÁ ēāƒÓ ÛęĢ·þLūÝ―•ēÃd<â€ļÍoŅ7\ʔ‘Ÿ}ĖĀ4lÛí]*zČðČ5`’ƘóÝYFni-ęģXfš&PV8[BJ!b0D~įÐAžs1–Í]ÆĐÏĄ%å{[9ãgæÉĐ58ĄAŽ`ðÄģÔo3OŌēî3€ĸĸsÍËĸ ÍĐ"‡îîųÃū6įïaV[§î'ÅéXw˜ë]hĨāTĀųï{'œpétēÞE\1°_Кųī zĮh‡RÁØRlēęïėĄĸG―ŪGï5/ŧŅ2–§ŒęƧZz^QRKĐã>Āâ~b”ˁJĻVklUö8l+\ī5Ģ‹IA֐ӷ+&ΐ>é 4VöĨ%m[Qóāýߍ-{Fļ’Ënū·đũ+PģjĖd’Ô͌yAač‚Ó:ĀĢ$ķŽ<ĻQĒâcrH` zl"ėÉœėœ8ŸĖf>·;ĢßéEĀ„Są Ũūj^aáŊ™/;#|Ó-H"9„/BPߎÃDFL “6ô&öæ,jųø á°ÉÏ3ôP åōW5āŊŽķHĩZ™ŊCz17ðūZˆ- cJ›ū-Uø>žŠoō^Ęō$Į”ÕMï=ā~ü*&ĻūX)ô+Q”°†æ õĩ<æÍŲĪúHōūëžpëßĩŋzâdÚ^üjŠtXZYKäpÜÓÓlŌŋu›BŠ! ― ōņT ­yņc 4Ãž"fÞÝėN”‹=‹ōhn ĸ76^K ēĊū2âã-ð$֊ãŽN“™o#îúč8@_W-þÅó‚Į˜Bgb–+cĒĶ"V0§/ēuĒíUęo‹eb”ŽWËų1ū‚6†TmË ŅÎnčh7ĘâęĢ&)ÂZĪÁīälÅō™MW˜Â_Ãö/Ą€p°b•°\Ą H=˜Pā˜T{BVÚoÖ (N(Ø…ÃÚ2b]X—ĀŠ nę*&TĀüŋŌYđ: aŦSSƒBŸÞޑĖÚ‘ģŊšĪŠ0Î^Öļ―Æ~xNRēŸ­äŽĘāš;Ō0@Ï9^j’b–[xåĢ}R-&Ē|Äý_:…ŧ éļņÄxã;—]…‡ÝĪ`g)8Øö“L5l "=Hð7āpųCû™"ąï™*Ҙ 3ÁéDęŧTr š.%Ū2ēeÜt?ē7PhKdģKóŅĨcāČŽÂz""ĀÐÏ?þĩē1åŽÓɄˆ>?ô Ó5ķ ~Åņvĸ$BŧS·+{Ž<܏R3+ýr5ą‡Î’ĒĨ/…dZíŲ·4áĮ>ãDRl’^ĐŅÂûlÉ) ‚6îi‡ļEd/gwݜŌðHĻ`—PÛrîeĮß8;ûϔ”gŠ3ŠĄûtŠÄ#ŠcŊ6'+Já]SOöf\‚ozûlPf<ī‚—7GblxĒđYđõ )á Uð8Ǝč$ė·°|šÔE>Gžõ7QÎ6ZLŦ†;ĀæŸūVŧŸ4mũÄŋĄûÐ"—é(X;XÍÜō·MÎýPîüø°™° ڇpįËYj ZËPiđ—›Åâö^Z;œ@,JГÅUk="ˍˆŸv3eęT^‹f5 {ĸ[6ĐÕ|Ã6þãŨ_ú”ŦŠTGzķŌcļý1î_ļCÖŽŠŧ@+MTĒĪāĻåÜšĒOwžFdčKŠĄĻŸtĄÜĨ!Q›tēĮE–l#_gîÁ*æBcný ïŨÁČ藛˜Y2.čįĻOž:ƒ 9žē!0ĨhąęÎlžĻ‹ë‘'œš'óÕųĒp(võÚtĄŊÏFTūÎ/qUōi<ĨiũƒólÁŪ_‘8ëdƒUÏÖš„ÐĸŊëf:Ng(IGM0`ĶŌæewÄ"/ðėžĖr? Ü&9 BŨŲš4Ō`&rĐ/ĒðĢM#–Ā!O"eđ·ß&ëÃU‚/z™ž˜SŸb} ûáīÄ äï}ÆTØn‡ ęþÅ G@‹dÍ$Ni'âDÄģŸr†’ŋĨŋr“gŪ—Ygž6šwJĨŋ{œ―ō$āö”Ī­·ã–\°įy ÅĖ]lbF)o ˆXs”5-%ņ&8p„>!Ų―ÆNoĩ IĶ_ŅįÕ ũ=ÏQĒig™žöZV-õŸ†Vn‘"āܑa8 gÍČf‘‡Þ†FāËIâ”Zģ vÛΆ ‡îïãnÉč ÂZgĮä–n'Åv•ĻIđ@ýėHCVčĩ Į”RË·H9(Ã}ĪÜđM„Į|Œ Ķ―/GIpSMS!˛uŒ”<[”―*>ôŸųÏ,ÁĪýï•~HyÚŦKP˜žĐD Ļ˜―Ģ<žØÁgQĪãþ­{ķÛ!Ake4™pĸ “:ŌÓu=Xï*늊ÍėO94Yw1ĩ9õ5cĪ'hë?öģŒ!wB|$ÜŌ?īĢ(Tģ]ÉÎ ÛīÅ?điNŪÝ@åÖËĘtÃcBJu0Do°óÐ_U•54øBĨĖ [ēhôČˈŠj6Îãi4Oüđ ú3MQĪO fOÝÎðŌzį Íģ}SzŒĒVˆÔCU–Ē;':x!lï‘ė+aŲÕï―UBÛ>†Þ]š>ŊqŅF>ĩd!žģĄB7É)ūŦkÂ_WÆ!ŧ$œōZ―ßŋæž―ö[9)ŪÓQ!á{GT鎖Ëé;wëEĄŊOlkDKą{tų*‚o9ɄjG\―xžöKāĒĨ!rXŦß2 đaĘķĸCœą4Œã Aö%žÐūz؃˜ mÁéQ ûnĩ:­‹ŧĩžęÐ6Žūa]6Ōę2ßÚJ+ýŪ@–ÍÚp“úÞ 7žÐŧUüĸÚŨ Ōŧur@K M&„߅ĨæĮŽégNąęÅØ0ĨΒ/}Ī;Ú*ČPmËķ‡ÍÞúƒ]§ÎžÏÅæŠgCÍŽdĩ*ĨÞTҁ9Yģ„O3Rníų› FĶ+ī>ūp‡3ų>ęPČ WÎ{Ÿ‹áaX(Ķä,‚váĄā5å JæUïX‡Į„zÁv>k̰Šņ2,ų•xTƒY|Œ˜ž`Y"Ų‡mïÆ%•ę^ļ“:yL˜WᩋáņlĄtéĀAåmIš­ÚEĒ-€—r-ŋŅw]RÃé 7z îLĄáxŨQ[‰ĩŲ;‚ŪOïÅũ_vįŋvÐ;ÂOúšXĶTĢ­–ÍLĐwä! (æ(ˆ?ÓIžM'xc։ 6Û·Z‹@ã ýÓîÐ?§TxŽÐ)·MÆŨ>]nH˜)Üēû6*dŲō*ŊoŅŦƒ_ûN/ģýEÁó$‰ÚŸ/.„û&ądckt&·„Á4ä +ë’LƒžzŸïdaĨä}­þÜŊō ōĀĨ6īcžāÜŅéƒÓ1› „ÎM1ûW<6v!,EEeč°Ūïē9—\ðÍ{û…’āMǰðVį'ū^R—ęēírӒ;Ņ•ũ”ĪVŠ^Ú@ļ$YŋÔŪGMjðÏņŧŠLãÂę/ŠÔíå’m2aū9ï$ðeŲdkøĮÚĘĨ—ō·’NDäUmeN)ȧŊâïŦē„ûĪ:i<žČí%a€3rūNöuüĢĒĘ@‹„ÃúÓÁQö í;ô!Ēž3š=@WäËdÓŽœ&åë=PĪxĀþ"AýđEąũ j”HđM$V™ËŲ Â›ŋ|&Ÿ8ō”ÎŨ9n1äŦō‡ÃŪoÝĒĢĐÍåp9qVL“ éÉžîĘČ0В CbØÅ‚œ€6ÆsYëVýše†―qe/,bØšýđŪCï ēÁÖiåÝEå4`öņyĒŨEâj§7ÄĀĒƒ‹ĖO‰ų|EGįh‹‰:ýd{y%yė–Ė+?·ÖïxĪå\\Óėģũ՟å"[(†Ķ ‰Ņz~x)’ Öà J0@4Ý0UĮþŠ,uĩĩÆ/›QÉōĶþAoi·EÉÝ+å0RÐŪinkz؆PwlXLÐŲVä5…(™”…Ô nÎWw\†_ÛRƒcø7ÉLdj#›€–V(ųŊïā-Į$Øų…]Ž^bŪoÄþtõ ”Š2!NBô x*˜ŊĶÉÓēĶUÏ―ĒøH°éŦēņŠNŲj5ėĪ~āÁéÛyƒėmŦļ—Ę“ÆÐþ†’jƒ‡‚üiđL:a‡đŋó\ÔNŨÁˆÖīZķ‰;þÏ ãMjûÍŧÖĨ(˜ƒ2jU·|ZžYÝý™Ū~Ĩ7 ĮCŅuðÃâ{öҐÜhŲ\ĮŪ4F"^<ŊYö›•=ķŁĪD|Ž"Ý­yC8Ëýe녡[Š.MŪ{Đ― ƒWJ[ĨūÕ|QZņŌÔbyË'āïŨ$!–qēØÜCãŋ;ĪL^Û čŽ`-@vb:ķ†ģ“QēžĪėŽJnĮDøkļ‚ĪíÝ-É&ū―zv·ŦPÔ3Úėƒ‘gXĶų•å” “đLŸ‚b§QįjÎjÆdČæŊW{$ĸBSĩSĘŠ ų8q,ĒUs‡Nw+ŽâG!æ&e6Ęc=―1ÐČîį>$õNâfĻ<ĩži CdG™ÐÚÎĻl—ėš—Ü;B^$üķ^Ý`ļ.°ZÕtīˆ 3…?Ėņŧ— h˜ųq„ŌFäóę,iHuËėpYĶ }t4ņuKŒ€t`Á%Þ ąÃĻߓŦuüĨ)Đ6…ðâWë4?Q‰Ôgč‰s8[.e}ĸ·KøÔã…P`.5ðLŦš‹ß"įtÝûþS}ŠķĻØīËĻŊ`üŅcN;‹•„Õņ§ âïA)ۜņ0ü•ökķMŸIŪ=3Žņ0þr[þ “˜Š^JˆÍ!(/$ ŒaĖA!ōuķož é?íįČČ@ĪįPwsó‚˜-Qi„ōĪR‹þ§đųįĐwcQΈPŧafwDXŌ;)ûÄðDë%îlaBšþ‹F‰evv5(Ã~†Ļã`PŸT•RHOoký#€=Øīc‘ĪðHær’?vp|Â…ėJĢY7!TŪópK îyĖD ‹IRgŅ|‡š–OœĻ‹z}ßgB˜T<Ē ĸBųj‰žŧh4hŊØ}Gú^;D=0ÉVŠ[˜ĒŠËg0î-XČhV‚Ó"͌L)č_[;l—=›ŧ 1"Ŧ2ÚÐtÖõsŨÜĘgŧSÖ-+EŲz€Î–|§€äŠ­úq:Ð*ZtŧÎ õ{°Ą|ôJM3qtĮķæcēœáŒdēÜx+å=ÓCāĮ֓ãAYd1qŊÛũâ Ž‘ŦFo?MĪûÏa(ĸŨ0ZQ”-YOPbņ ŋÎ=ŋK?J<ÖCĀČŊ2؞ju3v’í9SäðBAÄ{S­ÔdũTŽœQø9^a-Ļ* u8ß">úCƒņ1B|ʉįļÆo’ŠĨ—‰<Š ÏÜöėė2‘ŲÄMڅō0Ó>ŽcW”ãĨÏÝž ˜Ļ8ð!—ę?`BŧڌļÐōīū/ŋqč.ˆ―§˜ÖAŦ;Î―ðĸMZBăāGŒeõëöŽ‘ŋ―ũ„9)Ĩrö!ņ‰8â)îŧR‹O(1BŒœ]™ŽaáãDü•b ~Đq)`AúË gĩV”øY=5Š+‚§Øõ2á;‰Ø&VĖ’ö—–X@ "PĶHkxĘOZģų}îŊûņĐp=đœß‡dÃ<%ņō†—·Ļ€7n•Äb(―YS˜/ķýïųŽÐoSY†‡Ø$)ōiŌæ1-˜NA€Ð&Đ,,ÄTu!cïíĶÛxœÃ6‘SÓō1ŠáĢÒDŽĶ9(6ę8ĪIæÐõåTļoÓ°;ïēxžîo ĄęqūÚČ9ĀļbXïaføŸ_>šâ uvįq›ō ąšøā=―\„{m’Ģƒ&―ĮŽZ_ą5˜ŧŪë=gÞuŅ0&Æ2:ë…ę"‚)ÝDđ―žã=ÞHŊo“ęē sÁŠ\ã$É37A ÔņŒ­―šRëé‰`t€šR44*Þ­Ī™ÜÕâą'`’ëič™iJ*ŠĶũļf]lxhÛą@–|ēÓē ëN6‡3\Yū§6đGŧN3F A›h™ ēņÉ8y€ MïaŠ‹ŊsQ@ÄÜ îbū3…fĘĖIŠ,}Įč]&Ū— žob;\owM_߀WnÔ5i&Ą#g­ys‘(CÁtAUBmÎŪÍW·Û=„FN€ĶEx4ÂĖ#TȍACÜZ“[äĖoųäēŧáö8‰‡ËYī­ō3L )·ČŽlÐäôF“Pē·-0―ūĒōGu?œš=dÚķ(mūðïįa3}u/ É9™ųísnʘ*ŅyåTû OņŲ)bŦéB.z;ÓüíĸSâŠ3âCB&<\ÝQœĮZčð”ZX>E3"ˆOƒ9a9œj*7KoĒ‹ėËjĶ”Œŧ-;ÂÖųŅ~Äír%ĐqTĩ‰HÂÞ:Z4xŒĸ~C€úČDŸ [‡zkķD4O ŨˁģóÛqjépð\厛BüÆ8m_‚Ï… ˗Č+0}ņ%Ũ—‹Ū•–xpŽAĢ( 4Ûď“ðl―ïĖt{?}CŠëģŽîŽøĀšđáš~/ĘĢēiŠÚ ß(#sz9‘Ð⊎+ĸ æEų*:^æŽ"Öš’žĐ<ÏĐōv ɋ/;í%Y2w"xtrN_}“°yž U-ËÞĨ?t•īöVņĨ,”-°íýJ°`äĨ .§?īŪYą"hî{žķŽL‰>`”SųĀj'ŠÏ–iēĶĒõ_ŽJOæļÓý"Hc‹˜ĶÃËÐd―lCŦŪ떓ŧ5Qiœ  Ē“VjІĢӏšžPZĮÍZ8ޝeŪæļ˜zÜ­~ÝwŨdgāŪ*ŠRƒoXM§]ČęR_cOÛjÞ2dēČÝ<>KJž­ŸJúĀ’ íHãI§þąĢ­Å ĻĩĄb ņČuRkzRϐ―`Ũį›NÎĀËĸRs*’Dr֝wÓŽąQĐ@’jÐt‡øLĩũFaĘŋmbƒŽšŨ§8ũ\ýĢÆwÅóW^įC-’ĒHÆÏ"îz·hģÃ9îČ\ÔŠ>&ē][Û6Š`Sû9tAxį—ųûđ˜pëĩgũZB™Ļp;ĒČ1dØXNóŌ8ī$Њ WËĢģð‹›í.E-Ïð$$ĢÓß;xelÄĩHâžUš<CĖ9ß―žþūriúbU―ãÂ%Ę Eņ ­ĮðđâúÚĮā4o7_IM1Weæ*Ū âbŽĒ’zĒ/.ëy*>*ž* ÜĨöÔÉ,ü0žŦĮāŽę”ĐpéŋjLČQ&|ßĀ2/? „TöšþF™Šą~1šŅÜVxOýÍÆēSūáĀēŲ !a Jäąāßģ ĀŲq”‹ đú†ärú-ƒrļhvYŽĻ‘åCĶüĶCaÚp·Ôæ`ÅV|ĩĄÕ K„b„VMiþïéãĩKĐï°{ÛŠ„ÂŽņØi<žÝvOúÓũ<ŽŌxbĸ?ņsðƒÝy3īåSÜâĶnņ'ۙž  mą{/Šō—Ķ—ÂĻú‘]E˜ W-E‚Ūa&79 e`*ôlŠBŽ„Ū:%NЌķ@I@Ļ*ɛ 7%ė§ÖĮÆjÉŲĒÎŧƒlWë,änh,Ä9rú›@Öė†ÂĻ’ ŠýĪa —ŒÖ ›đÐzÎ!’-2­„ĀïÄNzó‹ĀmáoĨ‡hóü@;ßKÃÉBļ’˜›nĀĨiÃ%wAz“ð m6ædŨÄų­õrŨßyåŒŪe„l]Æ'ÞëõâåVĨå]AÂhÜPhĄB…ėeĻAŨÐïDŽ=\užį3Ý%l(Œ?YQÎ Ð3ŨÏ(ĶÜ}ĘĨ‡ōėč!§$šLwq5õnNŧ{ÜffĖ3(é›4zše2n„ļ ĶíæÞ ôܗųČ B8AôĒ%juÝ‰ ę}Ükš[€/ĨýŨŅķjNĀï™{øģþĸ;°BJ‹/.B3Æē_JR7 þ xûß!ôĪ ’O…ø8þ^o^1Žû™d.üĪ)”įôÛåĖv4ŋ~óƒŠ>Š rdéīƒĖ lpVõ1Šũš„“„‘Ój!XŅŧ@čþ  ā°û“{p―AÕÚÃųÏï…Kžâď˜Å2ôų^äaļļO þmzē•%tdIá˜åJĢ‘ˆV Ļ$Ĩ9R##.†Ŧ‹Ã§ŒZKE ›ü(Nîj Q}Wŋōä́õOšŽĪWŧ|(Ymsģƒ§ĄOÆdøŊ7 VũĀW™ĩWTB †›ÝpÂRģG&­ß}ŊÆ šsTŸ VŒ“ĪU’ÄUūaaÅfŒ>œÓ”ëmŽ{{ƒė‰uZ}ĸ ÃÝá°~n*ˆëá֞ßā^wðūa}ŌgsÉAâ\qČv=Mï\>Į,õ(ū\~nÏN[ð ˆDÄųĘt7āeãŲäJG>zĘÍŊT+4~īó”ð!)́va 69lĸh-ņ§Ôęžį „ŊČ~ø‡ÂÏFČ`Å_HÝļūb―°ęÃŋî?|T‚+Íą8ÉĨiY>VR3Ds-c‹Ô–öYK+|―o+noÛÍqŅÆũFDĀēúŸÍģy–ã?*ĩm:Š<ËĮēļ“4ąž§*œlpŋFoöí;ˆîŧfD Q0. €°õ“ĨK+đï°ŅRūMŊ W ?Ma;ïC„âyo{UĻgÕ­–ÃĻ<ۃëÛĩē/Vc+ o™*Ã;ó(žČ/5/ļúüq;lû0õ kœš9)ó°‚ē VáuĀU‡J ļ•|Ø@ NJbōKsr€―Õž%ũ# KÖv/žBs"Ļ‹,ˆ%lä5ī­íķÏäŊĢC`ĩ}‡‘BtCîTôčïmÄ%zĨC<Ū0tĪ'G—"$!)Ķģ>ö Bþaâ_Uĸ[ëÖ·Y^Ķ.•ũYĘ&BïÞEEųsô$éŅĩĸN ļI1ïŠvŠ;„ķ˜Ĩ/ã6ŅMÍŸNre —ĩŊÛš3Ō 1Ė:~ÚČ迯[Јaûæ~ÖÕKWęŊÍ Ėá f3!įé<°ÉifŽ}Éh#K‰Ä”šÃ„„x—€/øãyĩĒá] ·_dŌ/iý[BčŦ­·ƒóížž;\tm( ŸoÃ‰ŲÆv<Ģ‘MĪģ6ū.‰„Ā'SY˜ČŦ čû~mĄX‰ Ė Ý/9:·đï E%ĻiDŋ/P;!øž§ļō-ž―!?Ģũ§ŊĢmðę;[GĢ ĘÂ΅ujÓ ŽŅ朇Ûëmq”Aæü þ.!!öĄjGˆtą„’<ĸŋ`í9―Ј@{̈́č\ųęó)é(‰c!aĸJEéDŲØŠĘC‡óΟc …3ŒWš]$ö$*‚pÓwہ•ēkõÄ3Mō ŲA%ĻlīEEa}~Y0+ÄđŽŊpøÑļĸP ųÄ åoÍ*—ÃÚĶĀĒ ļg#&įEëčSI•đ^‰~EĘoũ‰7tŧ<í ܓâ“ų›Ý‚tïî̆W‡Č2īĄÂģŲ•ŋėî&\ĨĖOijF­ŸHŊį"ŧĀH„p ·úwFsUzÉē 71*—üSޗOĐiŽÅcæ+3îÛ#u.ū[3íðJ47 <(ßļjÖ<Ė5Ïnۋ­7(JNvmãGu9ø;īĒŌKæ+4Jø;—ã"%T”‚:W—Gý Rl Ëɗ`þ’õâŽĀĩĩ>§)ūä:vŦGIĻsčoÕËŋą Ãĸ*­ĮgCGsã˜yuÐÁģk@ĘUí'“ģ=ŒļTģ1Í―€JGĶæVoaņÍÜāˆđęWĖŽßHg Đķ(f˜°æ)2ó/Óh1’Į>84ï)Vo,­GTí(„AŊrðŋč‡zY†eô>KŒžČۉlzØã†ņžŲ}ØÛ …(—ý|tCÃ+ÆųBĩ™#ā3#ģëý ˜95‚Úxƒø"Œįƒjw‚A'I5Oâ |ų’6™bCnüĩĨlûūŧ—oïÎÂę~!1ņgW#pé­ĒŒ6}6Aw ĪÞðY=dL/QËØƒÝ ášs…ė8 ‹Ë0ČÅOÕõaÖÂČ/‘fÓ/!“æ‘°č#W\Î9ïA$*å{ĀČ8Ž’$C!āa°·ŧMi8ذoöĪČP î‹ėŦÆx·Ļ…ëé!|“ĩ“úëØAũ!š\ečõpĢÎÂĸ/1ĩ‹'ŽŠōĐģ_Ž)B$eæ'ĶÖVC‚­ÝģŪo\ā„awlˆsą9=a™ Ēīęø<˜†­ÍŪЕŌë4ũKŅq}ŒĀŽj˜0ĶÐđē3ĻCĩ ūīU—ō>ˆFųč$nY)žIOØįFũ%E ņ*Ÿ6ŋdðe^;ôCÏJWŦÄ#žŠ$þü‹ƒ?ĸíˆvLTŋjæą4S}HAąƒŌ™9‹)ÞÁŌdĢYÔđÉ C9‚TE  MðĖĩæÆ7ÕáķI(hf`ïüþ:iĶâÖ_ânžs@ëŒÓ mētNՎ~Ž(–ædĖKĀ™Ũ̘\đ‘ābρåôÛøČwS·H–]›YÛŨ˜•QÞn=gūS\Dq?sčŧN'#L~čĀ}ēt§ũB]oĒŠÎ]ļĘ<ÖŠŠ|K,eÂó“Á7•kŧ áÍödåV89 %\õēÃ4ė„öbxšĪ„!)RéĨôPW9ģi•"Ģ`yÝŸ!|Ä|wšôĢât,čļúÕį‹ā.dÜ>8同č͎čäd‹ÐGüœŒ–`0jÆĐ aîėH å ūZ<ßΧzzÉzčր―qŧuëā.R6†d‰=‰Õ uëô-06~ZņNTER|zpĶU—LbOϑŸŒYč” ÉŲũ|ã(,2ē–°Ķ.ķI–Ÿ}d5E–’]o“ģšđ\W­f%3(#bGZ’â °ÆÞēį‘@Ó§z.yëŋWÛNĄŅ›ŊAĐē^)šw!DdįNZîĄũQrõiŊ]i0‡zâíÓM=6 Ę%Ø 2·Ï*ÔIÚ:EĐš!‰ĶZø―īžĀõÅϒD>ÓKõģĀ%ÖTؘĸ"ŽæjfJĪ"ũœðÔTК&“Œ—íˆĮé\ÜÍŲũŸ57%Č8ĸn›ÆÔKŒÆŒįb ÝÜ&õ‘Ē™FÁÕæÐ #ÚÅEϘ4›ÏęE~Iš;r"ŨĸpâÝlЉĢÔEupĶmMV5cØÉPý)š—ĢDÛĖø&ņøŲrį3*čXO.`ݟCģãÜą–ïįq i7yķ(‰ …ļĩ?ÕbũX_Ð ž€nZûüļ6YxęáÄ[‘kGßŧ|ï‘]ðʝ—ÏōG5Ø-#Ï&GÐXûéŊÓu@žëË/3:äÆXSgâĀӋŧ‚ļl‘ ęĨƒ7Ļ€ĻĖX+rä˜_ËۊĄØHĖž―ÍÓĘ? Úš†”å”\]Ęæ1(ę“ðl=uųî&'ëÅëev§ïGPĻÆŸģmÓý‹b;DëþO3vs„…L‰wšþ–į€öE_8ĶßúrČ ŠÏÜĄ,%e`'Ĩðé7Føþ{Jq&xHIWĨøz@'D`$ÈÆĒ$‡~ĩųÄûc<ųëķÐēž3ÉĪ6øû=Œëˆaý§K+ûÜ9ŠÄí°S{oԆÛ.mŨÕ8Ÿ]•Ē˰ĶčõĶMw‰BÝšŊC\“`‰įWÕY™ /]6öš=ú\ōOï„ĸ]-OÆðÏŪ@˜{ųDŒbŠÜÁ[?ŽÞ•§gđ(cCß­­ŅHNéĢ―Đč6ˆžļž/3O|Ė#sļFēĸ8ÕۚļQčū•°Ąš*‰'ë™hˆqÔ#–ÄïČ>G›ŊČĸqÎl쿀Jþā‘ĄöϗŽũĘ3Į Â^uĢNHcÛÐEFāĄŊīŠđ-#M tąÁ™LÕIÁ€·þ™—L­”7üWöýĮŲ|dŠFĨ(>,§Z%ï%\âĄéqšRïŸ)―.| ß\šMӍ+Icdógy7~–Vfžęð`ûųVįFŸņŲüUŅfĩXeÍÄđŽąR*~č‚ūÕîâé›F^+ë(GKŅqßŪäVōŋŠ- n,q<ŽŲŊd{ā' ›X.SŽÓïŠÃÃÝŧČÄ<·B:Â@ĐpJĶ1 MFE%õ9ûēƒļ‚0ÖŪBŧ†`"šô&ÄŠÞŲs‹š{ݙ]ëJŦ3+‡…5eÂDČZ—]Ú‰UƒĀŸWĩô Õ#ÝĢ-õu ­ÔˆÛÄ +9V8$ëX,h†jÕ2°\xžžîŠLoĄņãsÐXHíYĻ·5æüĮ+ƒƒŠLbÝáųėã; &zĶv\-ĘBøïös?ž‘6/eQmdf)ė§åŌ\Åė-^ðËã.˜ë€ŌŽLcL‰ĸ]ܚŸRWņ;=°a|žÂõËFđ傧ųrję›Y9—"Ûa•nå _°E!,…~qÓ“`%^ŽīaŲîL9’jmNmó4kŪzF8Ēó}Ē-4güĒ4˜ÎØ>?1Ú{žNÍļÄ W!Ļ―Đt,b2F1iˆ"1)ąNŠļ‚cĩĖü­0îĸjýĘđ*Xî …I=Ē𠟂e°%‰Q5ņŽęԒš™ÅŲĮ`kæ(€ëˆ Qõ–æ§4jâÚn'wŸ8@`Óãå y<ŧāëJ!Ųtąîu*–ÚĨļ*PĐÆd Ýŧņ°ŧ8ŨwŸēÜUíîėÝŠo2ūŋ4}āïdCaį4‘8lœ‹Ó37ÚČó;ZnwWmõĻÐ1æÄĒ–a ūņc)kÎ ĨOŠŽ°ģ=Ž<[ŋ2ĩ7šFj0ðYą™$ûeW}-ĀFVj34Ÿm=kęîYU““îëĸ4T`öžæõÝVš%'ø=ũ,}øîĖãsūÛC)/€+ˉ[”ƒ=#5“D<ĮÃCmu‡Ō"Pŧėņí ÁĢ)rč%Š=Ŧå]ōVW !ö°ÎaßÂëóŲ0Z…TđÓčNöģÜŽĄõãžļqũĮĢũ(3aiîāï°Wvû$$ÂK]įĨVÏæ8ûģđÃá ôšö#h(įßmúhœû &øĒŅōc―hp ņSéÜ― Ũ!@ãKļī•ČOó’8Ö?ëĒÃL–m$ĸOĄ˜ ð™GöŪŠÛõëyũĮLŨļÉ"7”ôO0øÛķmRýZfÖ,‚Dû;æ-3ôíņsÂl…ū`üwÔ# r·šhÃvÖ3Ō!vAŨ…ŋ ōkO3œïO…Ā8Pޕ!6]ėq§Ó ØðĄ~ó“ÏÖeā;7ĄHĩ°lýÔMoąÎ°øĶÄÛu5“3ķ1J”?ŌŋÁ@ @  ‚AAõqōĸŲtools/build_installer.py000077500000000000000000000132661325366651500157730ustar00rootroot00000000000000#!/usr/bin/env python ############################################################################# ## ## Copyright (C) 2017 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Qt Installer Framework. ## ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ ## Commercial License Usage ## Licensees holding valid commercial Qt licenses may use this file in ## accordance with the commercial license agreement provided with the ## Software or, alternatively, in accordance with the terms contained in ## a written agreement between you and The Qt Company. For licensing terms ## and conditions see https://www.qt.io/terms-conditions. For further ## information use the contact form at https://www.qt.io/contact-us. ## ## GNU General Public License Usage ## Alternatively, this file may be used under the terms of the GNU ## General Public License version 3 as published by the Free Software ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ## included in the packaging of this file. Please review the following ## information to ensure the GNU General Public License requirements will ## be met: https://www.gnu.org/licenses/gpl-3.0.html. ## ## $QT_END_LICENSE$ ## ############################################################################# import argparse import os import shutil import string import subprocess import sys args = {} src_dir = '' build_dir = '' package_dir = '' archive_file = '' target_path = '' def parse_arguments(): global args parser = argparse.ArgumentParser(description='Build installer for installer framework.') parser.add_argument('--clean', dest='clean', action='store_true', help='delete all previous build artifacts') parser.add_argument('--static-qmake', dest='qmake', required=True, help='path to qmake that will be used to build the tools') parser.add_argument('--doc-qmake', dest='doc_qmake', required=True, help='path to qmake that will be used to generate the documentation') parser.add_argument('--make', dest='make', required=True, help='make command') parser.add_argument('--targetdir', dest='target_dir', required=True, help='directory the generated installer will be placed in') if sys.platform == 'darwin': parser.add_argument('--qt_menu_nib', dest='menu_nib', required=True, help='location of qt_menu.nib (usually src/gui/mac/qt_menu.nib)') args = parser.parse_args() def run(args): print 'calling ' + string.join(args) subprocess.check_call(args) def init(): global src_dir global build_dir global package_dir global target_path src_dir = os.path.dirname(os.path.abspath(os.path.dirname(sys.argv[0]))) root_dir = os.path.dirname(src_dir) basename = os.path.basename(src_dir) build_dir = os.path.join(root_dir, basename + '_build') package_dir = os.path.join(root_dir, basename + '_pkg') target_path = os.path.join(args.target_dir, 'Qt Installer Framework') print 'source dir: ' + src_dir print 'build dir: ' + build_dir print 'package dir: ' + package_dir print 'target path: ' + target_path if args.clean and os.path.exists(build_dir): print 'delete existing build dir ...' shutil.rmtree(build_dir) if not os.path.exists(build_dir): os.makedirs(build_dir) if os.path.exists(args.target_dir): print 'delete existing target dir ...' shutil.rmtree(args.target_dir) os.makedirs(args.target_dir) if os.path.exists(package_dir): print 'delete existing package dir ...' shutil.rmtree(package_dir) os.makedirs(package_dir) def build_docs(): print 'building documentation ...' os.chdir(build_dir) run((args.doc_qmake, src_dir)) run((args.make, 'docs')) print 'success!' def build(): print 'building sources ...' os.chdir(build_dir) run((args.qmake, src_dir)) run((args.make)) def package(): global package_dir print 'package ...' os.chdir(package_dir) shutil.copytree(os.path.join(build_dir, 'bin'), os.path.join(package_dir, 'bin'), ignore = shutil.ignore_patterns("*.exe.manifest","*.exp","*.lib")) if sys.platform == 'linux2': run(('strip',os.path.join(package_dir, 'bin/archivegen'))) run(('strip',os.path.join(package_dir, 'bin/binarycreator'))) run(('strip',os.path.join(package_dir, 'bin/devtool'))) run(('strip',os.path.join(package_dir, 'bin/installerbase'))) run(('strip',os.path.join(package_dir, 'bin/repogen'))) shutil.copytree(os.path.join(build_dir, 'doc'), os.path.join(package_dir, 'doc')) shutil.copytree(os.path.join(src_dir, 'examples'), os.path.join(package_dir, 'examples')) shutil.copy(os.path.join(src_dir, 'README'), package_dir) # create 7z archive_file = os.path.join(src_dir, 'dist', 'packages', 'org.qtproject.ifw.binaries', 'data', 'data.7z') if not os.path.exists(os.path.dirname(archive_file)): os.makedirs(os.path.dirname(archive_file)) run((os.path.join(package_dir, 'bin', 'archivegen'), archive_file, '*')) # run installer binary_creator = os.path.join(build_dir, 'bin', 'binarycreator') config_file = os.path.join(src_dir, 'dist', 'config', 'config.xml') package_dir = os.path.join(src_dir, 'dist', 'packages') installer_path = os.path.join(src_dir, 'dist', 'packages') run((binary_creator, '--offline-only', '-c', config_file, '-p', package_dir, target_path)) if sys.platform == 'darwin': shutil.copytree(args.menu_nib, target_path + '.app/Contents/Resources/qt_menu.nib') parse_arguments() init() build_docs() build() package() print 'DONE, installer is at ' + target_path tools/common/000077500000000000000000000000001325366651500135225ustar00rootroot00000000000000tools/common/repositorygen.cpp000066400000000000000000000761131325366651500171470ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "repositorygen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace QInstallerTools; void QInstallerTools::printRepositoryGenOptions() { std::cout << " -p|--packages dir The directory containing the available packages." << std::endl; std::cout << " This entry can be given multiple times." << std::endl; std::cout << " -e|--exclude p1,...,pn Exclude the given packages." << std::endl; std::cout << " -i|--include p1,...,pn Include the given packages and their dependencies" << std::endl; std::cout << " from the repository." << std::endl; std::cout << " --ignore-translations Do not use any translation" << std::endl; std::cout << " --ignore-invalid-packages Ignore all invalid packages instead of aborting." << std::endl; } QString QInstallerTools::makePathAbsolute(const QString &path) { if (QFileInfo(path).isRelative()) return QDir::current().absoluteFilePath(path); return path; } void QInstallerTools::copyWithException(const QString &source, const QString &target, const QString &kind) { qDebug() << "Copying associated" << kind << "file" << source; const QFileInfo targetFileInfo(target); if (!targetFileInfo.dir().exists()) QInstaller::mkpath(targetFileInfo.absolutePath()); QFile sourceFile(source); if (!sourceFile.copy(target)) { qDebug() << "failed!\n"; throw QInstaller::Error(QString::fromLatin1("Cannot copy the %1 file from \"%2\" to \"%3\": " "%4").arg(kind, QDir::toNativeSeparators(source), QDir::toNativeSeparators(target), /* in case of an existing target the error String does not show the file */ (targetFileInfo.exists() ? QLatin1String("Target already exist.") : sourceFile.errorString()))); } qDebug() << "done.\n"; } static QStringList copyFilesFromNode(const QString &parentNode, const QString &childNode, const QString &attr, const QString &kind, const QDomNode &package, const PackageInfo &info, const QString &targetDir) { QStringList copiedFiles; const QDomNodeList nodes = package.firstChildElement(parentNode).childNodes(); for (int i = 0; i < nodes.count(); ++i) { const QDomNode node = nodes.at(i); if (node.nodeName() != childNode) continue; const QDir dir(QString::fromLatin1("%1/meta").arg(info.directory)); const QString filter = attr.isEmpty() ? node.toElement().text() : node.toElement().attribute(attr); const QStringList files = dir.entryList(QStringList(filter), QDir::Files); if (files.isEmpty()) { throw QInstaller::Error(QString::fromLatin1("Cannot find any %1 matching \"%2\" " "while copying %1 of \"%3\".").arg(kind, filter, info.name)); } foreach (const QString &file, files) { const QString source(QString::fromLatin1("%1/meta/%2").arg(info.directory, file)); const QString target(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, file)); copyWithException(source, target, kind); copiedFiles.append(file); } } return copiedFiles; } void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &metaDataDir, const PackageInfoVector &packages, const QString &appName, const QString &appVersion) { const QString targetDir = makePathAbsolute(_targetDir); if (!QFile::exists(targetDir)) QInstaller::mkpath(targetDir); QDomDocument doc; QDomElement root; QFile existingUpdatesXml(QFileInfo(metaDataDir, QLatin1String("Updates.xml")).absoluteFilePath()); if (existingUpdatesXml.open(QIODevice::ReadOnly) && doc.setContent(&existingUpdatesXml)) { root = doc.documentElement(); // remove entry for this component from existing Updates.xml, if found foreach (const PackageInfo &info, packages) { const QDomNodeList packageNodes = root.childNodes(); for (int i = packageNodes.count() - 1; i >= 0; --i) { const QDomNode node = packageNodes.at(i); if (node.nodeName() != QLatin1String("PackageUpdate")) continue; if (node.firstChildElement(QLatin1String("Name")).text() != info.name) continue; root.removeChild(node); } } existingUpdatesXml.close(); } else { root = doc.createElement(QLatin1String("Updates")); root.appendChild(doc.createElement(QLatin1String("ApplicationName"))).appendChild(doc .createTextNode(appName)); root.appendChild(doc.createElement(QLatin1String("ApplicationVersion"))).appendChild(doc .createTextNode(appVersion)); root.appendChild(doc.createElement(QLatin1String("Checksum"))).appendChild(doc .createTextNode(QLatin1String("true"))); } foreach (const PackageInfo &info, packages) { if (!QDir(targetDir).mkpath(info.name)) throw QInstaller::Error(QString::fromLatin1("Cannot create directory \"%1\".").arg(info.name)); const QString packageXmlPath = QString::fromLatin1("%1/meta/package.xml").arg(info.directory); qDebug() << "Copy meta data for package" << info.name << "using" << packageXmlPath; QFile file(packageXmlPath); QInstaller::openForRead(&file); QString errMsg; int line = 0; int column = 0; QDomDocument packageXml; if (!packageXml.setContent(&file, &errMsg, &line, &column)) { throw QInstaller::Error(QString::fromLatin1("Cannot parse \"%1\": line: %2, column: %3: %4 (%5)") .arg(QDir::toNativeSeparators(packageXmlPath)).arg(line).arg(column).arg(errMsg, info.name)); } QDomElement update = doc.createElement(QLatin1String("PackageUpdate")); QDomNode nameElement = update.appendChild(doc.createElement(QLatin1String("Name"))); nameElement.appendChild(doc.createTextNode(info.name)); // list of current unused or later transformed tags QStringList blackList; blackList << QLatin1String("UserInterfaces") << QLatin1String("Translations") << QLatin1String("Licenses") << QLatin1String("Name"); bool foundDefault = false; bool foundVirtual = false; bool foundDisplayName = false; bool foundDownloadableArchives = false; bool foundCheckable = false; const QDomNode package = packageXml.firstChildElement(QLatin1String("Package")); const QDomNodeList childNodes = package.childNodes(); for (int i = 0; i < childNodes.count(); ++i) { const QDomNode node = childNodes.at(i); const QString key = node.nodeName(); if (key == QLatin1String("Default")) foundDefault = true; if (key == QLatin1String("Virtual")) foundVirtual = true; if (key == QLatin1String("DisplayName")) foundDisplayName = true; if (key == QLatin1String("DownloadableArchives")) foundDownloadableArchives = true; if (key == QLatin1String("Checkable")) foundCheckable = true; if (node.isComment() || blackList.contains(key)) continue; // just skip comments and some tags... QDomElement element = doc.createElement(key); for (int j = 0; j < node.attributes().size(); ++j) { element.setAttribute(node.attributes().item(j).toAttr().name(), node.attributes().item(j).toAttr().value()); } update.appendChild(element).appendChild(doc.createTextNode(node.toElement().text())); } if (foundDefault && foundVirtual) { throw QInstaller::Error(QString::fromLatin1("Error: and elements are " "mutually exclusive in file \"%1\".").arg(QDir::toNativeSeparators(packageXmlPath))); } if (foundDefault && foundCheckable) { throw QInstaller::Error(QString::fromLatin1("Error: and " "elements are mutually exclusive in file \"%1\".") .arg(QDir::toNativeSeparators(packageXmlPath))); } if (!foundDisplayName) { qWarning() << "No DisplayName tag found at" << info.name << ", using component Name instead."; QDomElement displayNameElement = doc.createElement(QLatin1String("DisplayName")); update.appendChild(displayNameElement).appendChild(doc.createTextNode(info.name)); } // get the size of the data quint64 componentSize = 0; quint64 compressedComponentSize = 0; const QDir::Filters filters = QDir::Files | QDir::NoDotAndDotDot; const QDir dataDir = QString::fromLatin1("%1/%2/data").arg(metaDataDir, info.name); const QFileInfoList entries = dataDir.exists() ? dataDir.entryInfoList(filters | QDir::Dirs) : QDir(QString::fromLatin1("%1/%2").arg(metaDataDir, info.name)).entryInfoList(filters); qDebug() << "calculate size of directory" << dataDir.absolutePath(); foreach (const QFileInfo &fi, entries) { try { if (fi.isDir()) { QDirIterator recursDirIt(fi.filePath(), QDirIterator::Subdirectories); while (recursDirIt.hasNext()) { recursDirIt.next(); const quint64 size = QInstaller::fileSize(recursDirIt.fileInfo()); componentSize += size; compressedComponentSize += size; } } else if (Lib7z::isSupportedArchive(fi.filePath())) { // if it's an archive already, list its files and sum the uncompressed sizes QFile archive(fi.filePath()); compressedComponentSize += archive.size(); QInstaller::openForRead(&archive); QVector::const_iterator fileIt; const QVector files = Lib7z::listArchive(&archive); for (fileIt = files.begin(); fileIt != files.end(); ++fileIt) componentSize += fileIt->uncompressedSize; } else { // otherwise just add its size const quint64 size = QInstaller::fileSize(fi); componentSize += size; compressedComponentSize += size; } } catch (const QInstaller::Error &error) { qDebug().noquote() << error.message(); } catch(...) { // ignore, that's just about the sizes - and size doesn't matter, you know? } } QDomElement fileElement = doc.createElement(QLatin1String("UpdateFile")); fileElement.setAttribute(QLatin1String("UncompressedSize"), componentSize); fileElement.setAttribute(QLatin1String("CompressedSize"), compressedComponentSize); // adding the OS attribute to be compatible with old sdks fileElement.setAttribute(QLatin1String("OS"), QLatin1String("Any")); update.appendChild(fileElement); root.appendChild(update); // copy script file const QString script = package.firstChildElement(QLatin1String("Script")).text(); if (!script.isEmpty()) { QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script)); if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) { throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".") .arg(QDir::toNativeSeparators(scriptFile.fileName()))); } const QString scriptContent = QLatin1String("(function() {") + QString::fromUtf8(scriptFile.readAll()) + QLatin1String(";" " if (typeof Component == \"undefined\")" " throw \"Missing Component constructor. Please check your script.\";" "})();"); // if the user isn't aware of the downloadable archives value we will add it automatically later foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive")) || scriptContent.contains(QLatin1String("removeDownloadableArchive")); static QInstaller::ScriptEngine testScriptEngine; const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName()); if (value.isError()) { throw QInstaller::Error(QString::fromLatin1("Exception while loading component " "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()), value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") : value.toString() + QStringLiteral(" on line number: ") + value.property(QStringLiteral("lineNumber")).toString())); } const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script)); copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript); } // write DownloadableArchives tag if that is missed by the user if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) { QStringList realContentFiles; foreach (const QString &filePath, info.copiedFiles) { if (!filePath.endsWith(QLatin1String(".sha1"), Qt::CaseInsensitive)) { const QString fileName = QFileInfo(filePath).fileName(); // remove unnecessary version string from filename and add it to the list realContentFiles.append(fileName.mid(info.version.count())); } } update.appendChild(doc.createElement(QLatin1String("DownloadableArchives"))).appendChild(doc .createTextNode(realContentFiles.join(QChar::fromLatin1(',')))); } // copy user interfaces const QStringList uiFiles = copyFilesFromNode(QLatin1String("UserInterfaces"), QLatin1String("UserInterface"), QString(), QLatin1String("user interface"), package, info, targetDir); if (!uiFiles.isEmpty()) { update.appendChild(doc.createElement(QLatin1String("UserInterfaces"))).appendChild(doc .createTextNode(uiFiles.join(QChar::fromLatin1(',')))); } // copy translations QStringList trFiles; if (!qApp->arguments().contains(QString::fromLatin1("--ignore-translations"))) { trFiles = copyFilesFromNode(QLatin1String("Translations"), QLatin1String("Translation"), QString(), QLatin1String("translation"), package, info, targetDir); if (!trFiles.isEmpty()) { update.appendChild(doc.createElement(QLatin1String("Translations"))).appendChild(doc .createTextNode(trFiles.join(QChar::fromLatin1(',')))); } } // copy license files const QStringList licenses = copyFilesFromNode(QLatin1String("Licenses"), QLatin1String("License"), QLatin1String("file"), QLatin1String("license"), package, info, targetDir); if (!licenses.isEmpty()) { foreach (const QString &trFile, trFiles) { // Copy translated license file based on the assumption that it will have the same base name // as the original license plus the file name of an existing translation file without suffix. foreach (const QString &license, licenses) { const QFileInfo untranslated(license); const QString translatedLicense = QString::fromLatin1("%2_%3.%4").arg(untranslated .baseName(), QFileInfo(trFile).baseName(), untranslated.completeSuffix()); // ignore copy failure, that's just about the translations QFile::copy(QString::fromLatin1("%1/meta/%2").arg(info.directory).arg(translatedLicense), QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, translatedLicense)); } } update.appendChild(package.firstChildElement(QLatin1String("Licenses")).cloneNode()); } } doc.appendChild(root); QFile targetUpdatesXml(targetDir + QLatin1String("/Updates.xml")); QInstaller::openForWrite(&targetUpdatesXml); QInstaller::blockingWrite(&targetUpdatesXml, doc.toByteArray()); } PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packagesDirectories, QStringList *packagesToFilter, FilterType filterType) { qDebug() << "\nCollecting information about available packages..."; bool ignoreInvalidPackages = qApp->arguments().contains(QString::fromLatin1("--ignore-invalid-packages")); PackageInfoVector dict; QFileInfoList entries; foreach (const QString &packagesDirectory, packagesDirectories) entries.append(QDir(packagesDirectory).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)); for (QFileInfoList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it) { if (filterType == Exclude) { // Check for current file in exclude list, if found, skip it and remove it from exclude list if (packagesToFilter->contains(it->fileName())) { packagesToFilter->removeAll(it->fileName()); continue; } } else { // Check for current file in include list, if not found, skip it; if found, remove it from include list if (!packagesToFilter->contains(it->fileName())) continue; packagesToFilter->removeAll(it->fileName()); } qDebug() << "Found subdirectory" << it->fileName(); // because the filter is QDir::Dirs - filename means the name of the subdirectory if (it->fileName().contains(QLatin1Char('-'))) { if (ignoreInvalidPackages) continue; throw QInstaller::Error(QString::fromLatin1("Component \"%1\" must not contain '-'. This is not " "allowed, because dashes are used as the separator between the component name and the " "version number internally.").arg(QDir::toNativeSeparators(it->fileName()))); } QFile file(QString::fromLatin1("%1/meta/package.xml").arg(it->filePath())); QFileInfo fileInfo(file); if (!fileInfo.exists()) { if (ignoreInvalidPackages) continue; throw QInstaller::Error(QString::fromLatin1("Component \"%1\" does not contain a package " "description (meta/package.xml is missing).").arg(QDir::toNativeSeparators(it->fileName()))); } file.open(QIODevice::ReadOnly); QDomDocument doc; QString error; int errorLine = 0; int errorColumn = 0; if (!doc.setContent(&file, &error, &errorLine, &errorColumn)) { if (ignoreInvalidPackages) continue; throw QInstaller::Error(QString::fromLatin1("Component package description in \"%1\" is invalid. " "Error at line: %2, column: %3 -> %4").arg(QDir::toNativeSeparators(fileInfo.absoluteFilePath()), QString::number(errorLine), QString::number(errorColumn), error)); } const QDomElement packageElement = doc.firstChildElement(QLatin1String("Package")); const QString name = packageElement.firstChildElement(QLatin1String("Name")).text(); if (!name.isEmpty() && name != it->fileName()) { qWarning().nospace() << "The tag in the file " << fileInfo.absoluteFilePath() << " is ignored - the installer uses the path element right before the 'meta'" << " (" << it->fileName() << ")"; } QString releaseDate = packageElement.firstChildElement(QLatin1String("ReleaseDate")).text(); if (releaseDate.isEmpty()) { qWarning("Release date for \"%s\" is empty! Using the current date instead.", qPrintable(fileInfo.absoluteFilePath())); releaseDate = QDate::currentDate().toString(Qt::ISODate); } if (!QDate::fromString(releaseDate, Qt::ISODate).isValid()) { if (ignoreInvalidPackages) continue; throw QInstaller::Error(QString::fromLatin1("Release date for \"%1\" is invalid! %2" ". Supported format: YYYY-MM-DD").arg(QDir::toNativeSeparators(fileInfo.absoluteFilePath()), releaseDate)); } PackageInfo info; info.name = it->fileName(); info.version = packageElement.firstChildElement(QLatin1String("Version")).text(); if (!QRegExp(QLatin1String("[0-9]+((\\.|-)[0-9]+)*")).exactMatch(info.version)) { if (ignoreInvalidPackages) continue; throw QInstaller::Error(QString::fromLatin1("Component version for \"%1\" is invalid! %2") .arg(QDir::toNativeSeparators(fileInfo.absoluteFilePath()), info.version)); } info.dependencies = packageElement.firstChildElement(QLatin1String("Dependencies")).text() .split(QInstaller::commaRegExp(), QString::SkipEmptyParts); info.directory = it->filePath(); dict.push_back(info); qDebug() << "- it provides the package" << info.name << " - " << info.version; } if (!packagesToFilter->isEmpty() && packagesToFilter->at(0) != QString::fromLatin1( "X_fake_filter_component_for_online_only_installer_X")) { qWarning() << "The following explicitly given packages could not be found\n in package directory:" << *packagesToFilter; } if (dict.isEmpty()) qDebug() << "No available packages found at the specified location."; return dict; } QHash QInstallerTools::buildPathToVersionMapping(const PackageInfoVector &info) { QHash map; foreach (const PackageInfo &inf, info) map[inf.name] = inf.version; return map; } static void writeSHA1ToNodeWithName(QDomDocument &doc, QDomNodeList &list, const QByteArray &sha1sum, const QString &nodename) { qDebug() << "searching sha1sum node for" << nodename; for (int i = 0; i < list.size(); ++i) { QDomNode curNode = list.at(i); QDomNode nameTag = curNode.firstChildElement(QLatin1String("Name")); if (!nameTag.isNull() && nameTag.toElement().text() == nodename) { QDomNode sha1Node = doc.createElement(QLatin1String("SHA1")); sha1Node.appendChild(doc.createTextNode(QString::fromLatin1(sha1sum.toHex().constData()))); curNode.appendChild(sha1Node); } } } void QInstallerTools::compressMetaDirectories(const QString &repoDir, const QString &baseDir, const QHash &versionMapping) { QDomDocument doc; QDomElement root; // use existing Updates.xml, if any QFile existingUpdatesXml(QFileInfo(QDir(repoDir), QLatin1String("Updates.xml")).absoluteFilePath()); if (!existingUpdatesXml.open(QIODevice::ReadOnly) || !doc.setContent(&existingUpdatesXml)) { qDebug() << "Cannot find Updates.xml"; } else { root = doc.documentElement(); } existingUpdatesXml.close(); QDir dir(repoDir); const QStringList sub = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); QDomNodeList elements = doc.elementsByTagName(QLatin1String("PackageUpdate")); foreach (const QString &i, sub) { QDir sd(dir); sd.cd(i); const QString path = QString(i).remove(baseDir); const QString versionPrefix = versionMapping[path]; if (path.isNull()) continue; const QString absPath = sd.absolutePath(); const QString fn = QLatin1String(versionPrefix.toLatin1() + "meta.7z"); const QString tmpTarget = repoDir + QLatin1String("/") +fn; Lib7z::createArchive(tmpTarget, QStringList() << absPath, Lib7z::QTmpFile::No); // remove the files that got compressed QInstaller::removeFiles(absPath, true); QFile tmp(tmpTarget); tmp.open(QFile::ReadOnly); const QByteArray sha1Sum = QInstaller::calculateHash(&tmp, QCryptographicHash::Sha1); writeSHA1ToNodeWithName(doc, elements, sha1Sum, path); const QString finalTarget = absPath + QLatin1String("/") + fn; if (!tmp.rename(finalTarget)) { throw QInstaller::Error(QString::fromLatin1("Cannot move file \"%1\" to \"%2\".").arg( QDir::toNativeSeparators(tmpTarget), QDir::toNativeSeparators(finalTarget))); } } QInstaller::openForWrite(&existingUpdatesXml); QInstaller::blockingWrite(&existingUpdatesXml, doc.toByteArray()); existingUpdatesXml.close(); } void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QString &repoDir, PackageInfoVector *const infos) { for (int i = 0; i < infos->count(); ++i) { const PackageInfo info = infos->at(i); const QString name = info.name; qDebug() << "Copying component data for" << name; const QString namedRepoDir = QString::fromLatin1("%1/%2").arg(repoDir, name); if (!QDir().mkpath(namedRepoDir)) { throw QInstaller::Error(QString::fromLatin1("Cannot create repository directory for component \"%1\".") .arg(name)); } QStringList compressedFiles; QStringList filesToCompress; foreach (const QString &packageDir, packageDirs) { const QDir dataDir(QString::fromLatin1("%1/%2/data").arg(packageDir, name)); foreach (const QString &entry, dataDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files)) { QFileInfo fileInfo(dataDir.absoluteFilePath(entry)); if (fileInfo.isFile() && !fileInfo.isSymLink()) { const QString absoluteEntryFilePath = dataDir.absoluteFilePath(entry); if (Lib7z::isSupportedArchive(absoluteEntryFilePath)) { QFile tmp(absoluteEntryFilePath); QString target = QString::fromLatin1("%1/%3%2").arg(namedRepoDir, entry, info.version); qDebug() << "Copying archive from" << tmp.fileName() << "to" << target; if (!tmp.copy(target)) { throw QInstaller::Error(QString::fromLatin1("Cannot copy file \"%1\" to \"%2\": %3") .arg(QDir::toNativeSeparators(tmp.fileName()), QDir::toNativeSeparators(target), tmp.errorString())); } compressedFiles.append(target); } else { filesToCompress.append(absoluteEntryFilePath); } } else if (fileInfo.isDir()) { qDebug() << "Compressing data directory" << entry; QString target = QString::fromLatin1("%1/%3%2.7z").arg(namedRepoDir, entry, info.version); Lib7z::createArchive(target, QStringList() << dataDir.absoluteFilePath(entry), Lib7z::QTmpFile::No); compressedFiles.append(target); } else if (fileInfo.isSymLink()) { filesToCompress.append(dataDir.absoluteFilePath(entry)); } } } if (!filesToCompress.isEmpty()) { qDebug() << "Compressing files found in data directory:" << filesToCompress; QString target = QString::fromLatin1("%1/%3%2").arg(namedRepoDir, QLatin1String("content.7z"), info.version); Lib7z::createArchive(target, filesToCompress, Lib7z::QTmpFile::No); compressedFiles.append(target); } foreach (const QString &target, compressedFiles) { (*infos)[i].copiedFiles.append(target); QFile archiveFile(target); QFile archiveHashFile(archiveFile.fileName() + QLatin1String(".sha1")); qDebug() << "Hash is stored in" << archiveHashFile.fileName(); qDebug() << "Creating hash of archive" << archiveFile.fileName(); try { QInstaller::openForRead(&archiveFile); const QByteArray hashOfArchiveData = QInstaller::calculateHash(&archiveFile, QCryptographicHash::Sha1).toHex(); archiveFile.close(); QInstaller::openForWrite(&archiveHashFile); archiveHashFile.write(hashOfArchiveData); qDebug() << "Generated sha1 hash:" << hashOfArchiveData; (*infos)[i].copiedFiles.append(archiveHashFile.fileName()); archiveHashFile.close(); } catch (const QInstaller::Error &/*e*/) { archiveFile.close(); archiveHashFile.close(); throw; } } } } tools/common/repositorygen.h000066400000000000000000000051011325366651500166010ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef QINSTALLER_REPOSITORYGEN_H #define QINSTALLER_REPOSITORYGEN_H #include #include #include #include namespace QInstallerTools { struct PackageInfo { QString name; QString version; QString directory; QStringList dependencies; QStringList copiedFiles; }; typedef QVector PackageInfoVector; enum FilterType { Include, Exclude }; void printRepositoryGenOptions(); QString makePathAbsolute(const QString &path); void copyWithException(const QString &source, const QString &target, const QString &kind = QString()); PackageInfoVector createListOfPackages(const QStringList &packagesDirectories, QStringList *packagesToFilter, FilterType ftype); QHash buildPathToVersionMapping(const PackageInfoVector &info); void compressMetaDirectories(const QString &repoDir, const QString &baseDir, const QHash &versionMapping); void copyMetaData(const QString &outDir, const QString &dataDir, const PackageInfoVector &packages, const QString &appName, const QString& appVersion); void copyComponentData(const QStringList &packageDir, const QString &repoDir, PackageInfoVector *const infos); } // namespace QInstallerTools #endif // QINSTALLER_REPOSITORYGEN_H tools/devtool/000077500000000000000000000000001325366651500137065ustar00rootroot00000000000000tools/devtool/binarydump.cpp000066400000000000000000000134331325366651500165700ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "binarydump.h" #include #include #include #include #include #include int BinaryDump::dump(const QInstaller::ResourceCollectionManager &manager, const QString &target) { QDir targetDir(QFileInfo(target).absoluteFilePath()); if (targetDir.exists()) { if (!targetDir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries).isEmpty()) { std::cerr << qPrintable(QString::fromLatin1("Target directory \"%1\" already exists and " "is not empty.").arg(QDir::toNativeSeparators(targetDir.path()))) << std::endl; return EXIT_FAILURE; } } else { if (!QDir().mkpath(targetDir.path())) { std::cerr << qPrintable(QString::fromLatin1("Cannot create \"%1\".").arg( QDir::toNativeSeparators(targetDir.path()))) << std::endl; return EXIT_FAILURE; } } QInstaller::CopyDirectoryOperation copyMetadata(0); copyMetadata.setArguments(QStringList() << QLatin1String(":/") << (targetDir.path() + QLatin1Char('/'))); // Add "/" at the end to make operation work. if (!copyMetadata.performOperation()) { std::cerr << qPrintable(copyMetadata.errorString()) << std::endl; return EXIT_FAILURE; } if (!targetDir.cd(QLatin1String("metadata"))) { std::cerr << qPrintable(QString::fromLatin1("Cannot switch to \"%1/metadata\".") .arg(QDir::toNativeSeparators(targetDir.path()))) << std::endl; return EXIT_FAILURE; } int result = EXIT_FAILURE; try { QFile updatesXml(targetDir.filePath(QLatin1String("Updates.xml"))); QInstaller::openForRead(&updatesXml); QString error; QDomDocument doc; if (!doc.setContent(&updatesXml, &error)) { throw QInstaller::Error(QString::fromLatin1("Cannot read: \"%1\": %2").arg( QDir::toNativeSeparators(updatesXml.fileName()), error)); } QHash versionMap; const QDomElement root = doc.documentElement(); const QDomNodeList rootChildNodes = root.childNodes(); for (int i = 0; i < rootChildNodes.count(); ++i) { const QDomElement element = rootChildNodes.at(i).toElement(); if (element.isNull()) continue; QString name, version; if (element.tagName() == QLatin1String("PackageUpdate")) { const QDomNodeList elementChildNodes = element.childNodes(); for (int j = 0; j < elementChildNodes.count(); ++j) { const QDomElement e = elementChildNodes.at(j).toElement(); if (e.tagName() == QLatin1String("Name")) name = e.text(); else if (e.tagName() == QLatin1String("Version")) version = e.text(); } versionMap.insert(name, version); } } foreach (const QString &name, versionMap.keys()) { const QInstaller::ResourceCollection c = manager.collectionByName(name.toUtf8()); if (c.resources().count() <= 0) continue; if (!targetDir.mkpath(name)) { throw QInstaller::Error(QString::fromLatin1("Cannot create target dir: %1.") .arg(targetDir.filePath(name))); } foreach (const QSharedPointer &resource, c.resources()) { const bool isOpen = resource->isOpen(); if ((!isOpen) && (!resource->open())) continue; // TODO: should we throw here? QFile target(targetDir.filePath(name) + QDir::separator() + QString::fromUtf8(resource->name())); QInstaller::openForWrite(&target); resource->copyData(&target); // copy the 7z files into the target directory if (!isOpen) // If we reach that point, either the resource was opened already... resource->close(); // or we did open it and have to close it again. } } result = EXIT_SUCCESS; } catch (const QInstaller::Error &error) { std::cerr << qPrintable(error.message()) << std::endl; } catch (...) { std::cerr << "Unknown exception caught." << std::endl; } return result; } tools/devtool/binarydump.h000066400000000000000000000030441325366651500162320ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef BINARYDUMP_H #define BINARYDUMP_H #include class BinaryDump { Q_DISABLE_COPY(BinaryDump) public: BinaryDump() {} int dump(const QInstaller::ResourceCollectionManager &manager, const QString &target); }; #endif // BINARYDUMP_H tools/devtool/binaryreplace.cpp000066400000000000000000000165541325366651500172450ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "binaryreplace.h" #include #include #include #include #include #include #include #include #include #include #include #include BinaryReplace::BinaryReplace(const QInstaller::BinaryLayout &layout) : m_binaryLayout(layout) {} int BinaryReplace::replace(const QString &source, const QString &target) { const QUrl url = QUrl::fromUserInput(source); QFutureWatcher taskWatcher; if (url.isRelative() || url.isLocalFile()) { taskWatcher.setFuture(QtConcurrent::run(&QInstaller::CopyFileTask::doTask, new QInstaller::CopyFileTask(QInstaller::FileTaskItem(url.isRelative() ? QFileInfo(source).absoluteFilePath() : url.toLocalFile())))); } else { taskWatcher.setFuture(QtConcurrent::run(&QInstaller::DownloadFileTask::doTask, new QInstaller::DownloadFileTask(QInstaller::FileTaskItem(url.toString())))); } bool result = EXIT_FAILURE; try { taskWatcher.waitForFinished(); // throws on error const QFuture future = taskWatcher.future(); if (future.resultCount() <= 0) return result; QString newInstallerBasePath = future.result().target(); if (Lib7z::isSupportedArchive(newInstallerBasePath)) { QFile archive(newInstallerBasePath); if (archive.open(QIODevice::ReadOnly)) { try { Lib7z::extractArchive(&archive, QDir::tempPath()); const QVector files = Lib7z::listArchive(&archive); newInstallerBasePath = QDir::tempPath() + QLatin1Char('/') + files.value(0) .path; result = EXIT_SUCCESS; } catch (const Lib7z::SevenZipException& e) { std::cerr << qPrintable(QString::fromLatin1("Error while extracting \"%1\": %2") .arg(QDir::toNativeSeparators(newInstallerBasePath), e.message())) << std::endl; } catch (...) { std::cerr << qPrintable(QString::fromLatin1("Unknown exception caught while " "extracting \"%1\".").arg(QDir::toNativeSeparators(newInstallerBasePath))) << std::endl; } } else { std::cerr << qPrintable(QString::fromLatin1("Cannot open \"%1\" for reading: %2") .arg(QDir::toNativeSeparators(newInstallerBasePath), archive.errorString())) << std::endl; } if (!archive.remove()) { std::cerr << qPrintable(QString::fromLatin1("Cannot delete file \"%1\": %2") .arg(QDir::toNativeSeparators(newInstallerBasePath), archive.errorString())) << std::endl; } if (result != EXIT_SUCCESS) return result; } result = EXIT_FAILURE; try { QFile installerBaseNew(newInstallerBasePath); #ifndef Q_OS_OSX QFile installerBaseOld(target); QInstaller::openForAppend(&installerBaseNew); installerBaseNew.seek(installerBaseNew.size()); if (m_binaryLayout.magicMarker == QInstaller::BinaryContent::MagicInstallerMarker) { QInstaller::openForRead(&installerBaseOld); installerBaseOld.seek(m_binaryLayout.metaResourcesSegment.start()); QInstaller::appendData(&installerBaseNew, &installerBaseOld, installerBaseOld .size() - installerBaseOld.pos()); installerBaseOld.close(); } else { QInstaller::appendInt64(&installerBaseNew, 0); QInstaller::appendInt64(&installerBaseNew, 4 * sizeof(qint64)); QInstaller::appendInt64(&installerBaseNew, QInstaller::BinaryContent::MagicUninstallerMarker); QInstaller::appendInt64(&installerBaseNew, QInstaller::BinaryContent::MagicCookie); } installerBaseNew.close(); #else QString bundlePath; QInstaller::isInBundle(target, &bundlePath); QFile installerBaseOld(QDir(bundlePath).filePath(bundlePath + QLatin1String("/Contents/MacOS/") + QFileInfo(bundlePath).completeBaseName())); #endif QFile backup(installerBaseOld.fileName() + QLatin1String(".bak")); if (backup.exists() && (!backup.remove())) { std::cerr << qPrintable(QString::fromLatin1("Cannot delete \"%1\": %2") .arg(QDir::toNativeSeparators(backup.fileName()), backup.errorString())) << std::endl; } const QString oldBasePath = installerBaseOld.fileName(); if (!installerBaseOld.rename(oldBasePath + QLatin1String(".bak"))) { std::cerr << qPrintable(QString::fromLatin1("Cannot rename \"%1\" to \"%2\": %3") .arg(oldBasePath, oldBasePath + QLatin1String(".bak"), installerBaseOld.errorString())) << std::endl; } if (!installerBaseNew.rename(oldBasePath)) { std::cerr << qPrintable(QString::fromLatin1("Cannot copy \"%1\" to \"%2\": %3") .arg(installerBaseNew.fileName(), oldBasePath, installerBaseNew.errorString())) << std::endl; } else { result = EXIT_SUCCESS; installerBaseNew.setPermissions(installerBaseOld.permissions()); } } catch (const QInstaller::Error &error) { std::cerr << qPrintable(error.message()) << std::endl; } } catch (const QInstaller::TaskException &e) { std::cerr << qPrintable(e.message()) << std::endl; } catch (const QUnhandledException &e) { std::cerr << e.what() << std::endl; } catch (...) { std::cerr << "Unknown exception caught."<< std::endl; } return result; } tools/devtool/binaryreplace.h000066400000000000000000000031701325366651500167000ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef BINARYREPLACE_H #define BINARYREPLACE_H #include class BinaryReplace { Q_DISABLE_COPY(BinaryReplace) public: BinaryReplace(const QInstaller::BinaryLayout &layout); int replace(const QString &source, const QString &target); private: QInstaller::BinaryLayout m_binaryLayout; }; #endif // BINARYREPLACE_H tools/devtool/devtool.pro000066400000000000000000000006351325366651500161100ustar00rootroot00000000000000TEMPLATE = app TARGET = devtool QT = core network qml xml include(../../installerfw.pri) CONFIG += console DESTDIR = $$IFW_APP_PATH HEADERS += operationrunner.h \ binaryreplace.h \ binarydump.h SOURCES += main.cpp \ operationrunner.cpp \ binaryreplace.cpp \ binarydump.cpp osx:include(../../no_app_bundle.pri) target.path = $$[QT_INSTALL_BINS] INSTALLS += target tools/devtool/main.cpp000066400000000000000000000246771325366651500153560ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "binarydump.h" #include "binaryreplace.h" #include "operationrunner.h" #include #include #include #include #include #include #include #include #include #include #include #include #include struct Command { const char* command; const char* description; qint32 argC; const char* arguments; const char* argDescription; } Commands[] = { { "dump", "Dumps the binary content that belongs to an installer or maintenance tool into " "target directory.", 2, " ", "The containing the data to " "dump.\nThe to dump the data in." }, { "update", "Updates existing installer or maintenance tool with a new installer base.", 2, " ", "The to update.\nThe to use as update." }, { "operation", "Executes an operation with with a given mode and a list of arguments. ", 2, " ", "The to run the operation with.\n" " 'mode' can be DO or UNDO. 'name' of the operation. 'args,...' " "used to run the operation." } }; #define DESCRITION_LENGTH 60 #define SETW_ALIGNLEFT(X) std::setw(X) << std::setiosflags(std::ios::left) static int fail(const QString &message) { std::cerr << qPrintable(message) << " See 'devtool --help'." << std::endl; return EXIT_FAILURE; } static QStringList split(int index, const QString &description) { QStringList result; if (description.length() <= DESCRITION_LENGTH) return result << description; const int lastIndexOf = description.left(index + DESCRITION_LENGTH) .lastIndexOf(QLatin1Char(' ')); result << description.left(lastIndexOf); return result + split(lastIndexOf + 1, description.mid(lastIndexOf + 1)); } // -- main int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); app.setApplicationVersion(QLatin1String("1.0.0")); QCommandLineParser parser; QCommandLineOption help = parser.addHelpOption(); QCommandLineOption version = parser.addVersionOption(); QCommandLineOption verbose(QLatin1String("verbose"), QLatin1String("Verbose mode. Prints out " "more information.")); parser.addOption(verbose); parser.parse(app.arguments()); if (parser.isSet(version)) { parser.showVersion(); return EXIT_SUCCESS; } if (parser.isSet(help)) { const QString command = parser.positionalArguments().value(0); if (!command.isEmpty()) { for (const auto &c : Commands) { if (QLatin1String(c.command) == command) { parser.clearPositionalArguments(); parser.addPositionalArgument(QString::fromLatin1("%1 %2").arg(QLatin1String(c .command), QLatin1String(c.arguments)), QLatin1String(c.argDescription)); parser.showHelp(EXIT_SUCCESS); } } return fail(QString::fromLatin1("\"%1\" is not a devtool command.").arg(command)); } QString helpText = parser.helpText(); helpText.insert(helpText.indexOf(QLatin1Char(']')) + 1, QLatin1String(" command ")); std::cout << qPrintable(helpText) << std::endl; std::cout << "Available commands (mutually exclusive):" << std::endl; for (const auto &c : Commands) { QStringList lines = split(0, QLatin1String(c.description)); std::cout << SETW_ALIGNLEFT(2) << " " << SETW_ALIGNLEFT(16) << c.command << SETW_ALIGNLEFT(DESCRITION_LENGTH) << qPrintable(lines.takeFirst()) << std::endl; foreach (const QString &line, lines) { std::cout << SETW_ALIGNLEFT(18) << QByteArray(18, ' ').constData() << qPrintable(line) << std::endl; } } std::cout << std::endl << "Use 'devtool --help ' to read about a specific command." << std::endl; return EXIT_SUCCESS; } QStringList arguments = parser.positionalArguments(); if (arguments.isEmpty()) return fail(QLatin1String("Missing command.")); bool found = false; const QString command = arguments.takeFirst(); for (const auto &c : Commands) { if ((found = (QLatin1String(c.command) == command))) { if (arguments.count() != c.argC) return fail(QString::fromLatin1("%1: wrong argument count.").arg(command)); break; } } if (!found) return fail(QString::fromLatin1("\"%1\" is not a devtool command.").arg(command)); QInstaller::init(); QInstaller::setVerbose(parser.isSet(verbose)); QString bundlePath; QString path = QFileInfo(arguments.first()).absoluteFilePath(); if (QInstaller::isInBundle(path, &bundlePath)) path = QDir(bundlePath).filePath(QLatin1String("Contents/Resources/installer.dat")); int result = EXIT_FAILURE; QVector resourceMappings; quint64 cookie = QInstaller::BinaryContent::MagicCookie; try { { QFile tmp(path); QInstaller::openForRead(&tmp); if (!tmp.seek(QInstaller::BinaryContent::findMagicCookie(&tmp, cookie) - sizeof(qint64))) throw QInstaller::Error(QLatin1String("Cannot seek to read magic marker.")); QInstaller::BinaryLayout layout; layout.magicMarker = QInstaller::retrieveInt64(&tmp); if (layout.magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) { QFileInfo fi(path); if (QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath)) fi.setFile(bundlePath); path = fi.absolutePath() + QLatin1Char('/') + fi.baseName() + QLatin1String(".dat"); tmp.close(); tmp.setFileName(path); QInstaller::openForRead(&tmp); cookie = QInstaller::BinaryContent::MagicCookieDat; } layout = QInstaller::BinaryContent::binaryLayout(&tmp, cookie); tmp.close(); if (command == QLatin1String("update")) { BinaryReplace br(layout); // To update the binary we do not need any mapping. return br.replace(arguments.last(), QFileInfo(arguments.first()) .absoluteFilePath()); } } QFile file(path); QInstaller::openForRead(&file); qint64 magicMarker; QList operations; QInstaller::ResourceCollectionManager manager; QInstaller::BinaryContent::readBinaryContent(&file, &operations, &manager, &magicMarker, cookie); // map the inbuilt resources const QInstaller::ResourceCollection meta = manager.collectionByName("QResources"); foreach (const QSharedPointer &resource, meta.resources()) { const bool isOpen = resource->isOpen(); if ((!isOpen) && (!resource->open())) continue; // TODO: should we throw here? const QByteArray ba = resource->readAll(); if (!QResource::registerResource((const uchar*) ba.data(), QLatin1String(":/metadata"))) throw QInstaller::Error(QLatin1String("Cannot register in-binary resource.")); resourceMappings.append(ba); if (!isOpen) resource->close(); } if (command == QLatin1String("dump")) { // To dump the content we do not need the binary format engine. BinaryDump bd; result = bd.dump(manager, arguments.last()); } else if (command == QLatin1String("operation")) { QInstaller::BinaryFormatEngineHandler::instance()->registerResources(manager .collections()); // setup the binary format engine OperationRunner runner(magicMarker, operations); const QStringList operationArguments = arguments.last().split(QLatin1Char(',')); if (operationArguments.first() == QLatin1String("DO")) result = runner.runOperation(operationArguments.mid(1), OperationRunner::RunMode::Do); else if (operationArguments.first() == QLatin1String("UNDO")) result = runner.runOperation(operationArguments.mid(1), OperationRunner::RunMode::Undo); else std::cerr << "Malformed argument: " << qPrintable(operationArguments.last()) << std::endl; } } catch (const QInstaller::Error &error) { std::cerr << qPrintable(error.message()) << std::endl; } catch (...) { std::cerr << "Unknown exception caught." << std::endl; } // unmap resources foreach (const QByteArray &rccData, resourceMappings) QResource::unregisterResource((const uchar*) rccData.data(), QLatin1String(":/metadata")); return result; } tools/devtool/operationrunner.cpp000066400000000000000000000067061325366651500176550ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "operationrunner.h" #include #include #include #include #include OperationRunner::OperationRunner(qint64 magicMarker, const QList &oldOperations) : m_core(new QInstaller::PackageManagerCore(magicMarker, oldOperations)) { // We need a package manager core as some operations expect them to be available. } OperationRunner::~OperationRunner() { delete m_core; } int OperationRunner::runOperation(QStringList arguments, RunMode mode) { int result = EXIT_FAILURE; try { const QString name = arguments.takeFirst(); QScopedPointer op(KDUpdater::UpdateOperationFactory::instance() .create(name, m_core)); if (!op) { std::cerr << "Cannot instantiate operation: " << qPrintable(name) << std::endl; return EXIT_FAILURE; } if (QObject *const object = dynamic_cast (op.data())) { const QMetaObject *const mo = object->metaObject(); if (mo->indexOfSignal(mo->normalizedSignature("outputTextChanged(QString)")) > -1) connect(object, SIGNAL(outputTextChanged(QString)), this, SLOT(print(QString))); } op->setArguments(arguments); bool readyPerformed = false; if (mode == RunMode::Do) readyPerformed = op->performOperation(); if (mode == RunMode::Undo) readyPerformed = op->undoOperation(); if (readyPerformed) { result = EXIT_SUCCESS; std::cout << "Operation finished successfully." << std::endl; } else { std::cerr << "An error occurred while performing the operation: " << qPrintable(op->errorString()) << std::endl; } } catch (const QInstaller::Error &e) { std::cerr << qPrintable(e.message()) << std::endl; } catch (...) { std::cerr << "Unknown exception caught." << std::endl; } return result; } void OperationRunner::print(const QString &value) { std::cout << qPrintable(value) << std::endl; } tools/devtool/operationrunner.h000066400000000000000000000037001325366651500173110ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef OPERATIONRUNNER_H #define OPERATIONRUNNER_H #include #include namespace QInstaller { struct OperationBlob; class PackageManagerCore; } class OperationRunner : public QObject { Q_OBJECT Q_DISABLE_COPY(OperationRunner) public: enum struct RunMode { Do, Undo }; OperationRunner(qint64 magicMarker, const QList &oldOperations); ~OperationRunner(); int runOperation(QStringList arguments, RunMode mode); private slots: void print(const QString &value); private: QInstaller::PackageManagerCore *m_core; }; #endif // OPERATIONRUNNER_H tools/repocompare/000077500000000000000000000000001325366651500145465ustar00rootroot00000000000000tools/repocompare/main.cpp000066400000000000000000000047101325366651500162000ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include #include #include #include "mainwindow.h" #include "repositorymanager.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QCoreApplication::setApplicationName(QLatin1String("IFW_repocompare")); if (a.arguments().contains(QLatin1String("-i"))) { if (a.arguments().count() != 5) { qWarning() << "Usage: " << a.arguments().at(0) << " -i "; return -1; } const QString productionRepo = a.arguments().at(2); const QString updateRepo = a.arguments().at(3); const QString outputFile = a.arguments().at(4); RepositoryManager manager; manager.setProductionRepository(productionRepo); manager.setUpdateRepository(updateRepo); a.connect(&manager, &RepositoryManager::repositoriesCompared, &a, &QApplication::quit); qDebug() << "Waiting for server reply..."; a.exec(); qDebug() << "Writing into " << outputFile; manager.writeUpdateFile(outputFile); return 0; } else { MainWindow w; w.show(); return a.exec(); } } tools/repocompare/mainwindow.cpp000066400000000000000000000123501325366651500174270ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include namespace { const QLatin1String productionIdentifier = QLatin1String("ProductionRepositories"); const QLatin1String updateIdentifier = QLatin1String("UpdateRepositories"); void uniqueAppend(QComboBox* box, const QString &url) { const int itemCount = box->count(); for (int i = 0; i < itemCount; ++i) { if (box->itemText(i) == url) return; } box->insertItem(0, url); } QStringList itemsToList(QComboBox* box) { QStringList result; const int itemCount = box->count(); for (int i = 0; i < itemCount; ++i) { result.append(box->itemText(i)); } return result; } } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); QSettings settings; ui->productionRepo->insertItems(0, settings.value(productionIdentifier).toStringList()); ui->updateRepo->insertItems(0, settings.value(updateIdentifier).toStringList()); connect(ui->actionExit, &QAction::triggered, this, &QWidget::close); connect(ui->productionButton, &QAbstractButton::clicked, this, &MainWindow::getProductionRepository); connect(ui->updateButton, &QAbstractButton::clicked, this, &MainWindow::getUpdateRepository); connect(ui->exportButton, &QAbstractButton::clicked, this, &MainWindow::createExportFile); connect(&manager, &RepositoryManager::repositoriesCompared, this, &MainWindow::displayRepositories); } MainWindow::~MainWindow() { QSettings settings; settings.setValue(productionIdentifier, itemsToList(ui->productionRepo)); settings.setValue(updateIdentifier, itemsToList(ui->updateRepo)); delete ui; } void MainWindow::getProductionRepository() { manager.setProductionRepository(ui->productionRepo->currentText()); } void MainWindow::getUpdateRepository() { manager.setUpdateRepository(ui->updateRepo->currentText()); } void MainWindow::displayRepositories() { uniqueAppend(ui->productionRepo, ui->productionRepo->currentText()); uniqueAppend(ui->updateRepo, ui->updateRepo->currentText()); // First we put everything into the treeview for (int i = 0; i < 2; ++i) { QMap* map; if (i == 0) map = manager.productionComponents(); else map = manager.updateComponents(); int indexIncrement = 4*i; for (QMap::iterator it = map->begin(); it != map->end(); ++it) { QList list = ui->treeWidget->findItems(it.key(), Qt::MatchExactly); QTreeWidgetItem* item; if (list.size()) item = list.at(0); else item = new QTreeWidgetItem(ui->treeWidget); item->setText(0, it.key()); item->setText(indexIncrement + 3, it.value().version); item->setText(indexIncrement + 4, it.value().releaseDate.toString(QLatin1String("yyyy-MM-dd"))); item->setText(indexIncrement + 5, it.value().checksum); item->setText(indexIncrement + 6, it.value().updateText); if (i != 0) { QString errorText; if (manager.updateRequired(it.key(), &errorText)) item->setText(1, QLatin1String("Yes")); else item->setText(1, QLatin1String("No")); item->setText(2, errorText); } } } } void MainWindow::createExportFile() { QString fileName = QFileDialog::getSaveFileName(this, QLatin1String("Export File")); if (fileName.isEmpty()) return; manager.writeUpdateFile(fileName); } tools/repocompare/mainwindow.h000066400000000000000000000040531325366651500170750ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "repositorymanager.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void displayRepositories(); void getProductionRepository(); void getUpdateRepository(); void createExportFile(); private: void createRepositoryMap(const QByteArray &data, QMap &map); Ui::MainWindow *ui; RepositoryManager manager; }; #endif // MAINWINDOW_H tools/repocompare/mainwindow.ui000066400000000000000000000121741325366651500172660ustar00rootroot00000000000000 MainWindow 0 0 877 613 RepoCompare 0 0 Production Repository: 0 0 true Receive Update Repository: 0 0 true Receive Component Name Update required Status Old Version Old Date Old Checksum Old Update Text New Version New Date New Checksum New Update Text Qt::Horizontal 40 20 Export Update File 0 0 877 21 File Exit tools/repocompare/repocompare.pro000066400000000000000000000005031325366651500176020ustar00rootroot00000000000000TEMPLATE = app INCLUDEPATH += . .. TARGET = repocompare include(../../installerfw.pri) QT += widgets network SOURCES += main.cpp\ mainwindow.cpp \ repositorymanager.cpp HEADERS += mainwindow.h \ repositorymanager.h FORMS += mainwindow.ui osx:include(../../no_app_bundle.pri) tools/repocompare/repositorymanager.cpp000066400000000000000000000165521325366651500210350ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "repositorymanager.h" #include #include #include #include #include #include #include #include #include namespace { qreal createVersionNumber(const QString &text) { QStringList items = text.split(QLatin1Char('.')); QString last = items.takeLast(); items.append(last.split(QLatin1Char('-'))); qreal value = 0; if (items.count() == 4) value += qreal(0.01) * items.takeLast().toInt(); int multiplier = 10000; do { value += multiplier * items.takeFirst().toInt(); multiplier /= 100; } while (items.count()); return value; } } RepositoryManager::RepositoryManager(QObject *parent) : QObject(parent) { manager = new QNetworkAccessManager(this); connect(manager, &QNetworkAccessManager::finished, this, &RepositoryManager::receiveRepository); productionMap.clear(); updateMap.clear(); } void RepositoryManager::setProductionRepository(const QString &repo) { QUrl url(repo); if (!url.isValid()) { QMessageBox::critical(0, QLatin1String("Error"), QLatin1String("Specified URL is not valid")); return; } QNetworkRequest request(url); productionReply = manager->get(request); } void RepositoryManager::setUpdateRepository(const QString &repo) { QUrl url(repo); if (!url.isValid()) { QMessageBox::critical(0, QLatin1String("Error"), QLatin1String("Specified URL is not valid")); return; } QNetworkRequest request(url); updateReply = manager->get(request); } void RepositoryManager::receiveRepository(QNetworkReply *reply) { QByteArray data = reply->readAll(); if (reply == productionReply) { createRepositoryMap(data, productionMap); } else if (reply == updateReply) { createRepositoryMap(data, updateMap); } reply->deleteLater(); if (productionMap.size() && updateMap.size()) compareRepositories(); } void RepositoryManager::createRepositoryMap(const QByteArray &data, QMap &map) { QXmlStreamReader reader(data); QString currentItem; ComponentDescription currentDescription; while (!reader.atEnd()) { QXmlStreamReader::TokenType type = reader.readNext(); if (type == QXmlStreamReader::StartElement) { if (reader.name() == QLatin1String("PackageUpdate")) { // new package if (!currentItem.isEmpty()) map.insert(currentItem, currentDescription); currentDescription.updateText.clear(); currentDescription.version.clear(); currentDescription.checksum.clear(); } if (reader.name() == QLatin1String("SHA1")) currentDescription.checksum = reader.readElementText(); else if (reader.name() == QLatin1String("Version")) currentDescription.version = reader.readElementText(); else if (reader.name() == QLatin1String("ReleaseDate")) { currentDescription.releaseDate = QDate::fromString(reader.readElementText(), QLatin1String("yyyy-MM-dd")); } else if (reader.name() == QLatin1String("UpdateText")) currentDescription.updateText = reader.readElementText(); else if (reader.name() == QLatin1String("Name")) currentItem = reader.readElementText(); } } // Insert the last item if (!currentItem.isEmpty()) map.insert(currentItem, currentDescription); } void RepositoryManager::compareRepositories() { for (QMap::iterator it = updateMap.begin(); it != updateMap.end(); ++it) { // New item in the update if (!productionMap.contains(it.key())) { it.value().update = true; continue; } it.value().update = updateRequired(it.key()); } emit repositoriesCompared(); } bool RepositoryManager::updateRequired(const QString &componentName, QString *message) { if (!productionMap.contains(componentName) || !updateMap.contains(componentName)) qFatal("Accessing non existing component"); const ComponentDescription &productionDescription = productionMap.value(componentName); const ComponentDescription &updateDescription = updateMap.value(componentName); if (createVersionNumber(productionDescription.version) < createVersionNumber(updateDescription.version)) { if (productionDescription.releaseDate > updateDescription.releaseDate) { if (message) { *message = QString::fromLatin1("Error: Component %1 has wrong release date %2") .arg(componentName).arg(updateDescription.releaseDate.toString()); } return false; } else if (productionDescription.updateText == updateDescription.updateText) if (message) { *message = QString::fromLatin1("Warning: Component %1 has no new update text: %2") .arg(componentName).arg(updateDescription.updateText); } if (message && message->isEmpty()) *message = QLatin1String("Ok"); return true; } return false; } void RepositoryManager::writeUpdateFile(const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { QMessageBox::critical(0, QLatin1String("Error"), QString::fromLatin1("Cannot open file \"%1\" for writing: %2").arg( QDir::toNativeSeparators(fileName), file.errorString())); return; } QStringList items; for (QMap::const_iterator it = updateMap.constBegin(); it != updateMap.constEnd(); ++it) { if (it.value().update) items.append(it.key()); } file.write(items.join(QLatin1String(",")).toLatin1()); file.close(); } tools/repocompare/repositorymanager.h000066400000000000000000000051121325366651500204700ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #ifndef REPOSITORYMANAGER_H #define REPOSITORYMANAGER_H #include #include #include #include struct ComponentDescription { QString version; QDate releaseDate; QString checksum; QString updateText; bool update; }; class RepositoryManager : public QObject { Q_OBJECT public: explicit RepositoryManager(QObject *parent = 0); bool updateRequired(const QString &componentName, QString *message = 0); QMap* productionComponents() { return &productionMap; } QMap* updateComponents() { return &updateMap; } signals: void repositoriesCompared(); public slots: void setProductionRepository(const QString &repo); void setUpdateRepository(const QString &repo); void writeUpdateFile(const QString &fileName); void receiveRepository(QNetworkReply *reply); void compareRepositories(); private: void createRepositoryMap(const QByteArray &data, QMap &map); QNetworkReply *productionReply; QNetworkReply *updateReply; QNetworkAccessManager *manager; QMap productionMap; QMap updateMap; }; #endif // REPOSITORYMANAGER_H tools/repogen/000077500000000000000000000000001325366651500136715ustar00rootroot00000000000000tools/repogen/repogen.cpp000066400000000000000000000306121325366651500160360ustar00rootroot00000000000000/************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** **************************************************************************/ #include "common/repositorygen.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define QUOTE_(x) #x #define QUOTE(x) QUOTE_(x) using namespace QInstaller; static void printUsage() { const QString appName = QFileInfo(QCoreApplication::applicationFilePath()).fileName(); std::cout << "Usage: " << appName << " [options] repository-dir" << std::endl; std::cout << std::endl; std::cout << "Options:" << std::endl; QInstallerTools::printRepositoryGenOptions(); std::cout << " -r|--remove Force removing target directory if existent." << std::endl; std::cout << " --update Update a set of existing components (defined by " << std::endl; std::cout << " --include or --exclude) in the repository" << std::endl; std::cout << " --update-new-components Update a set of existing components (defined by " << std::endl; std::cout << " --include or --exclude) in the repository with all new components" << std::endl; std::cout << " -v|--verbose Verbose output" << std::endl; std::cout << std::endl; std::cout << "Example:" << std::endl; std::cout << " " << appName << " -p ../examples/packages repository/" << std::endl; } static int printErrorAndUsageAndExit(const QString &err) { std::cerr << qPrintable(err) << std::endl << std::endl; printUsage(); return 1; } int main(int argc, char** argv) { QString tmpMetaDir; int exitCode = EXIT_FAILURE; try { QCoreApplication app(argc, argv); QInstaller::init(); QStringList args = app.arguments().mid(1); QStringList filteredPackages; bool updateExistingRepository = false; QStringList packagesDirectories; QInstallerTools::FilterType filterType = QInstallerTools::Exclude; bool remove = false; bool updateExistingRepositoryWithNewComponents = false; //TODO: use a for loop without removing values from args like it is in binarycreator.cpp //for (QStringList::const_iterator it = args.begin(); it != args.end(); ++it) { while (!args.isEmpty() && args.first().startsWith(QLatin1Char('-'))) { if (args.first() == QLatin1String("--verbose") || args.first() == QLatin1String("-v")) { args.removeFirst(); setVerbose(true); } else if (args.first() == QLatin1String("--exclude") || args.first() == QLatin1String("-e")) { args.removeFirst(); if (!filteredPackages.isEmpty()) { return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller", "Error: --include and --exclude are mutually exclusive. Use either one or " "the other.")); } if (args.isEmpty() || args.first().startsWith(QLatin1Char('-'))) { return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller", "Error: Package to exclude missing")); } filteredPackages = args.first().split(QLatin1Char(',')); args.removeFirst(); } else if (args.first() == QLatin1String("--include") || args.first() == QLatin1String("-i")) { args.removeFirst(); if (!filteredPackages.isEmpty()) { return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller", "Error: --include and --exclude are mutual exclusive options. Use either " "one or the other.")); } if (args.isEmpty() || args.first().startsWith(QLatin1Char('-'))) { return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller", "Error: Package to include missing")); } filteredPackages = args.first().split(QLatin1Char(',')); args.removeFirst(); filterType = QInstallerTools::Include; } else if (args.first() == QLatin1String("--update")) { args.removeFirst(); updateExistingRepository = true; } else if (args.first() == QLatin1String("--update-new-components")) { args.removeFirst(); updateExistingRepositoryWithNewComponents = true; } else if (args.first() == QLatin1String("-p") || args.first() == QLatin1String("--packages")) { args.removeFirst(); if (args.isEmpty()) { return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller", "Error: Packages parameter missing argument")); } if (!QFileInfo(args.first()).exists()) { return printErrorAndUsageAndExit(QCoreApplication::translate("QInstaller", "Error: Package directory not found at the specified location")); } packagesDirectories.append(args.first()); args.removeFirst(); } else if (args.first() == QLatin1String("--ignore-translations") || args.first() == QLatin1String("--ignore-invalid-packages")) { args.removeFirst(); } else if (args.first() == QLatin1String("-r") || args.first() == QLatin1String("--remove")) { remove = true; args.removeFirst(); } else { printUsage(); return 1; } } if (packagesDirectories.isEmpty() || (args.count() != 1)) { printUsage(); return 1; } const bool update = updateExistingRepository || updateExistingRepositoryWithNewComponents; if (remove && update) { throw QInstaller::Error(QCoreApplication::translate("QInstaller", "Argument -r|--remove and --update|--update-new-components are mutually exclusive!")); } const QString repositoryDir = QInstallerTools::makePathAbsolute(args.first()); if (remove) QInstaller::removeDirectory(repositoryDir); if (!update && QFile::exists(repositoryDir) && !QDir(repositoryDir).entryList( QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) { throw QInstaller::Error(QCoreApplication::translate("QInstaller", "Repository target directory \"%1\" already exists.").arg(QDir::toNativeSeparators(repositoryDir))); } QInstallerTools::PackageInfoVector packages = QInstallerTools::createListOfPackages(packagesDirectories, &filteredPackages, filterType); if (updateExistingRepositoryWithNewComponents) { QDomDocument doc; QFile file(repositoryDir + QLatin1String("/Updates.xml")); if (file.open(QFile::ReadOnly) && doc.setContent(&file)) { const QDomElement root = doc.documentElement(); if (root.tagName() != QLatin1String("Updates")) { throw QInstaller::Error(QCoreApplication::translate("QInstaller", "Invalid content in \"%1\".").arg(QDir::toNativeSeparators(file.fileName()))); } file.close(); // close the file, we read the content already // read the already existing updates xml content const QDomNodeList children = root.childNodes(); QHash hash; for (int i = 0; i < children.count(); ++i) { const QDomElement el = children.at(i).toElement(); if ((!el.isNull()) && (el.tagName() == QLatin1String("PackageUpdate"))) { QInstallerTools::PackageInfo info; const QDomNodeList c2 = el.childNodes(); for (int j = 0; j < c2.count(); ++j) { if (c2.at(j).toElement().tagName() == scName) info.name = c2.at(j).toElement().text(); else if (c2.at(j).toElement().tagName() == scVersion) info.version = c2.at(j).toElement().text(); } hash.insert(info.name, info); } } // remove all components that have no update (decision based on the version tag) for (int i = packages.count() - 1; i >= 0; --i) { const QInstallerTools::PackageInfo info = packages.at(i); if (!hash.contains(info.name)) continue; // the component is not there, keep it if (KDUpdater::compareVersion(info.version, hash.value(info.name).version) < 1) packages.remove(i); // the version did not change, no need to update the component } if (packages.isEmpty()) { std::cout << QString::fromLatin1("Cannot find new components to update \"%1\".") .arg(repositoryDir) << std::endl; return EXIT_SUCCESS; } } } QHash pathToVersionMapping = QInstallerTools::buildPathToVersionMapping(packages); foreach (const QInstallerTools::PackageInfo &package, packages) { const QFileInfo fi(repositoryDir, package.name); if (fi.exists()) removeDirectory(fi.absoluteFilePath()); } QTemporaryDir tmp; tmp.setAutoRemove(false); tmpMetaDir = tmp.path(); QInstallerTools::copyComponentData(packagesDirectories, repositoryDir, &packages); QInstallerTools::copyMetaData(tmpMetaDir, repositoryDir, packages, QLatin1String("{AnyApplication}"), QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION))); QInstallerTools::compressMetaDirectories(tmpMetaDir, tmpMetaDir, pathToVersionMapping); QDirIterator it(repositoryDir, QStringList(QLatin1String("Updates*.xml")), QDir::Files | QDir::CaseSensitive); while (it.hasNext()) { it.next(); QFile::remove(it.fileInfo().absoluteFilePath()); } QInstaller::moveDirectoryContents(tmpMetaDir, repositoryDir); exitCode = EXIT_SUCCESS; } catch (const Lib7z::SevenZipException &e) { std::cerr << "Caught 7zip exception: " << e.message() << std::endl; } catch (const QInstaller::Error &e) { std::cerr << "Caught exception: " << e.message() << std::endl; } catch (...) { std::cerr << "Unknown exception caught" << std::endl; } QInstaller::removeDirectory(tmpMetaDir, true); return exitCode; } tools/repogen/repogen.pro000066400000000000000000000005721325366651500160560ustar00rootroot00000000000000TEMPLATE = app TARGET = repogen INCLUDEPATH += . .. ../common include(../../installerfw.pri) QT -= gui QT += qml xml CONFIG += console DESTDIR = $$IFW_APP_PATH SOURCES += repogen.cpp \ ../common/repositorygen.cpp HEADERS += ../common/repositorygen.h macx:include(../../no_app_bundle.pri) target.path = $$[QT_INSTALL_BINS] INSTALLS += target tools/tools.pro000066400000000000000000000001721325366651500141140ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += \ archivegen \ binarycreator \ repogen \ devtool \ repocompare