diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb11c0a33..347dd9af6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks -exclude: '^3rdparty|COPYING|.gitmodules' +exclude: '^3rdparty|COPYING|.gitmodules|src/libpsi/tools/zip/minizip' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 diff --git a/src/libpsi/.pre-commit-config.yaml b/src/libpsi/.pre-commit-config.yaml new file mode 100644 index 000000000..65cc5ad1d --- /dev/null +++ b/src/libpsi/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + +exclude: '^3rdparty|tools/zip/minizip|COPYING' +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + - id: check-merge-conflict +- repo: https://github.com/doublify/pre-commit-clang-format + # for clang-tidy we can take github.com/pocc/pre-commit-hooks + rev: f4c4ac5948aff384af2b439bfabb2bdd65d2b3ac + hooks: + - id: clang-format +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.7 + hooks: + - id: forbid-crlf + - id: remove-crlf + - id: forbid-tabs + - id: remove-tabs +- repo: https://github.com/openstack-dev/bashate + rev: 2.0.0 + hooks: + - id: bashate + args: ['--ignore', 'E006'] diff --git a/src/libpsi/COPYING b/src/libpsi/COPYING new file mode 100644 index 000000000..db09c1656 --- /dev/null +++ b/src/libpsi/COPYING @@ -0,0 +1,295 @@ + + As a special exception, the copyright holder(s) give permission to link + this program with the Qt Library (commercial or non-commercial edition), + and distribute the resulting executable, without including the source + code for the Qt library in the source distribution. + + As a special exception, the copyright holder(s) give permission to link + this program with any other library, and distribute the resulting + executable, without including the source code for the library in the + source distribution, provided that the library interfaces with this + program only via the following plugin interfaces: + + 1. The Qt Plugin APIs, only as authored by Trolltech + 2. The QCA Plugin API, only as authored by Justin Karneges + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +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 software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, 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 redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. 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 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. + + END OF TERMS AND CONDITIONS diff --git a/src/libpsi/README b/src/libpsi/README new file mode 100644 index 000000000..2504a78f1 --- /dev/null +++ b/src/libpsi/README @@ -0,0 +1 @@ +libpsi is internal library for Psi and Psi+ projects diff --git a/src/libpsi/dialogs/CMakeLists.txt b/src/libpsi/dialogs/CMakeLists.txt new file mode 100644 index 000000000..7b34c3efa --- /dev/null +++ b/src/libpsi/dialogs/CMakeLists.txt @@ -0,0 +1,24 @@ +unset(HEADERS) +unset(FORMS) +unset(SOURCES) +unset(UI_FORMS) +unset(EXTRA_LDFLAGS) + +include_directories(libpsi/dialogs) + +list(APPEND HEADERS + grepshortcutkeydialog.h + ) + +list(APPEND SOURCES + grepshortcutkeydialog.cpp + ) + +list(APPEND FORMS + grepshortcutkeydialog.ui + ) + +qt_wrap_ui(UI_FORMS ${FORMS}) +add_library(libpsi_dialogs STATIC ${HEADERS} ${SOURCES} ${UI_FORMS}) +target_link_libraries(libpsi_dialogs ${QT_LIBRARIES}) +target_include_directories(libpsi_dialogs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/libpsi/dialogs/grepshortcutkeydialog.cpp b/src/libpsi/dialogs/grepshortcutkeydialog.cpp new file mode 100644 index 000000000..83fc1e92c --- /dev/null +++ b/src/libpsi/dialogs/grepshortcutkeydialog.cpp @@ -0,0 +1,108 @@ +/* + * grepshortcutkeydialog.cpp - a dialog which greps a KeySequence and + * emits a signal with this KeySequence as Parameter + * Copyright (C) 2006 Cestonaro Thilo + * + * 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 2 + * 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 . + * + */ + +#include "grepshortcutkeydialog.h" + +GrepShortcutKeyDialog::GrepShortcutKeyDialog() : QDialog(), gotKey(false) +{ + setAttribute(Qt::WA_DeleteOnClose); + ui_.setupUi(this); + setWindowTitle(tr("Press shortcut")); + displayPressedKeys(QKeySequence()); +} + +/** + * Grabs the keyboard and proceeds with the default show() call. + */ +void GrepShortcutKeyDialog::show() +{ + QDialog::show(); + grabKeyboard(); + setFocus(); +} + +/** + * Releases the grabbed keyboard and proceeds with the default close() call. + */ +void GrepShortcutKeyDialog::closeEvent(QCloseEvent *event) +{ + releaseKeyboard(); + event->accept(); +} + +void GrepShortcutKeyDialog::displayPressedKeys(const QKeySequence &keys) +{ + QString str = keys.toString(QKeySequence::NativeText); + if (str.isEmpty()) + str = tr("Set Keys"); + ui_.shortcutPreview->setText(str); +} + +QKeySequence GrepShortcutKeyDialog::getKeySequence(QKeyEvent *event) const +{ + return QKeySequence((isValid(event->key()) ? event->key() : 0) | (event->modifiers() & ~Qt::KeypadModifier)); +} + +void GrepShortcutKeyDialog::keyPressEvent(QKeyEvent *event) +{ + displayPressedKeys(getKeySequence(event)); + + if (!isValid(event->key()) || gotKey) + return; + + gotKey = true; + emit newShortcutKey(getKeySequence(event)); + close(); +} + +void GrepShortcutKeyDialog::keyReleaseEvent(QKeyEvent *event) { displayPressedKeys(getKeySequence(event)); } + +/** + * Returns true if \param key could be used in a shortcut. + */ +bool GrepShortcutKeyDialog::isValid(int key) const +{ + switch (key) { + case 0: + case Qt::Key_unknown: + return false; + } + + return !isModifier(key); +} + +/** + * Returns true if \param key is modifier. + */ +bool GrepShortcutKeyDialog::isModifier(int key) const +{ + switch (key) { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Meta: + case Qt::Key_Alt: + case Qt::Key_AltGr: + case Qt::Key_Super_L: + case Qt::Key_Super_R: + case Qt::Key_Menu: + return true; + } + return false; +} diff --git a/src/libpsi/dialogs/grepshortcutkeydialog.h b/src/libpsi/dialogs/grepshortcutkeydialog.h new file mode 100644 index 000000000..24f2c7cb6 --- /dev/null +++ b/src/libpsi/dialogs/grepshortcutkeydialog.h @@ -0,0 +1,57 @@ +/* + * grepshortcutkeydialog.h - a dialog which greps a KeySequence and + * emits a signal with this KeySequence as Parameter + * Copyright (C) 2006 Cestonaro Thilo + * + * 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 2 + * 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 . + * + */ + +#ifndef GREPSHORTCUTKEYDIALOG_H +#define GREPSHORTCUTKEYDIALOG_H + +#include "ui_grepshortcutkeydialog.h" + +#include +#include +#include + +class GrepShortcutKeyDialog : public QDialog { + Q_OBJECT +public: + GrepShortcutKeyDialog(); + + // reimplemented + void show(); + +protected: + // reimplemented + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + void closeEvent(QCloseEvent *event); + +signals: + void newShortcutKey(const QKeySequence &key); + +private: + Ui::GrepShortcutKeyDialog ui_; + bool gotKey; + + void displayPressedKeys(const QKeySequence &keys); + QKeySequence getKeySequence(QKeyEvent *event) const; + bool isValid(int key) const; + bool isModifier(int key) const; +}; + +#endif // GREPSHORTCUTKEYDIALOG_H diff --git a/src/libpsi/dialogs/grepshortcutkeydialog.ui b/src/libpsi/dialogs/grepshortcutkeydialog.ui new file mode 100644 index 000000000..56091eab9 --- /dev/null +++ b/src/libpsi/dialogs/grepshortcutkeydialog.ui @@ -0,0 +1,77 @@ + + GrepShortcutKeyDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 255 + 50 + + + + + 0 + 0 + 0 + 0 + + + + Qt::NoContextMenu + + + + 9 + + + 6 + + + + + Qt::NoFocus + + + Qt::AlignHCenter + + + true + + + + + + + Qt::NoFocus + + + Cancel + + + + + + + + + pushButton + clicked() + GrepShortcutKeyDialog + close() + + + 207 + 83 + + + 100 + 23 + + + + + diff --git a/src/libpsi/tools/CMakeLists.txt b/src/libpsi/tools/CMakeLists.txt new file mode 100644 index 000000000..a71b842cb --- /dev/null +++ b/src/libpsi/tools/CMakeLists.txt @@ -0,0 +1,280 @@ +set(CMAKE_CXX_STANDARD 17) + +unset(HEADERS) +unset(FORMS) +unset(SOURCES) +unset(UI_FORMS) +unset(EXTRA_LDFLAGS) + +if(APPLE) + if(USE_GROWL) + list(APPEND EXTRA_LDFLAGS + "-framework Growl -framework Cocoa" + ) + list(APPEND HEADERS + growlnotifier/growlnotifier.h + ) + list(APPEND SOURCES + growlnotifier/growlnotifier.mm + ) + endif() + if(USE_MAC_DOC) + list(APPEND EXTRA_LDFLAGS + "-framework Carbon" + ) + list(APPEND HEADERS + mac_dock/mac_dock.h + mac_dock/privateqt_mac.h + ) + list(APPEND SOURCES + mac_dock/mac_dock.mm + mac_dock/privateqt_mac.mm + ) + endif() +endif() + +list(APPEND HEADERS + # tools + priorityvalidator.h + + # idle + idle/idle.h + + # systemwatch + systemwatch/systemwatch.h + + # globalshortcut + globalshortcut/globalshortcuttrigger.h + + # tools + maybe.h + iodeviceopener.h + languagemanager.h + + # atomicxmlfile + atomicxmlfile/atomicxmlfile.h + + # globalshortcut + globalshortcut/globalshortcutmanager.h + + # simplecli + simplecli/simplecli.h + + # spellchecker + spellchecker/spellchecker.h + spellchecker/spellhighlighter.h + ) + +list(APPEND SOURCES + # tools + priorityvalidator.cpp + languagemanager.cpp + + # spellchecker + spellchecker/spellchecker.cpp + spellchecker/spellhighlighter.cpp + + # tools + iodeviceopener.cpp + + # idle + idle/idle.cpp + + # atomicxmlfile + atomicxmlfile/atomicxmlfile.cpp + + # globalshortcut + globalshortcut/globalshortcutmanager.cpp + + # systemwatch + systemwatch/systemwatch.cpp + + # simplecli + simplecli/simplecli.cpp + ) + +if(APPLE) + list(APPEND HEADERS + mac_dock/mac_dock.h + mac_dock/privateqt_mac.h + ) + + list(APPEND SOURCES + # globalshortcut + globalshortcut/globalshortcutmanager_mac.mm + globalshortcut/NDKeyboardLayout.m + + mac_dock/mac_dock.mm + mac_dock/privateqt_mac.mm + ) + + list(APPEND HEADERS + # systemwatch + systemwatch/systemwatch_mac.h + + # globalshortcut + globalshortcut/NDKeyboardLayout.h + ) + + list(APPEND SOURCES + #idle + idle/idle_mac.cpp + + # systemwatch + systemwatch/systemwatch_mac.cpp + ) +elseif(WIN32) + list(APPEND HEADERS + # spellchecker + spellchecker/hunspellchecker.h + ) + + list(APPEND SOURCES + #idle + idle/idle_win.cpp + + # systemwatch + systemwatch/systemwatch_win.cpp + + # globalshortcut + globalshortcut/globalshortcutmanager_win.cpp + + # spellchecker + spellchecker/hunspellchecker.cpp + ) +elseif(HAIKU) + list(APPEND HEADERS + # systemwatch + systemwatch/systemwatch_unix.h + ) + + list(APPEND SOURCES + #idle + idle/idle_x11.cpp + + # systemwatch + systemwatch/systemwatch_unix.cpp + + # globalshortcut + globalshortcut/globalshortcutmanager_haiku.cpp + ) +elseif(USE_X11) + list(APPEND HEADERS + # systemwatch + systemwatch/systemwatch_unix.h + ) + + list(APPEND SOURCES + #idle + idle/idle_x11.cpp + + # systemwatch + systemwatch/systemwatch_unix.cpp + + # globalshortcut + globalshortcut/globalshortcutmanager_x11.cpp + ) +else() + list(APPEND HEADERS + # systemwatch + systemwatch/systemwatch_unix.h + ) + + list(APPEND SOURCES + #idle + idle/idle_x11.cpp + + # systemwatch + systemwatch/systemwatch_unix.cpp + + # globalshortcut + globalshortcut/globalshortcutmanager_stub.cpp + ) +endif() + +# spellchecker +if(USE_ENCHANT) + if(Enchant_VERSION) + if(${Enchant_VERSION} VERSION_LESS "2.0") + add_definitions(-DHAVE_ENCHANT) + else() + add_definitions(-DHAVE_ENCHANT2) + endif() + message(STATUS "Enchant version - ${Enchant_VERSION}") + else() + add_definitions(-DHAVE_ENCHANT) + endif() + + include_directories( + ${Enchant_INCLUDE_DIR} + ) + + list(APPEND EXTRA_LDFLAGS + ${Enchant_LIBRARY} + ) + + list(APPEND HEADERS + spellchecker/enchantchecker.h + ) + + list(APPEND SOURCES + spellchecker/enchantchecker.cpp + ) +elseif(USE_HUNSPELL) + add_definitions(-DHAVE_HUNSPELL) + + if(MSVC) + add_definitions(-DHUNSPELL_STATIC) + endif() + + include_directories( + ${HUNSPELL_INCLUDE_DIR} + ) + + list(APPEND EXTRA_LDFLAGS + ${HUNSPELL_LIBRARY} + ) + + list(APPEND HEADERS + spellchecker/hunspellchecker.h + ) + + list(APPEND SOURCES + spellchecker/hunspellchecker.cpp + ) +elseif(APPLE) + list(APPEND SOURCES + spellchecker/macspellchecker.mm + ) + + list(APPEND HEADERS + spellchecker/macspellchecker.h + ) +elseif(USE_ASPELL) + add_definitions(-DHAVE_ASPELL) + include_directories( + ${ASPELL_INCLUDE_DIR} + ) + list(APPEND EXTRA_LDFLAGS + ${ASPELL_LIBRARIES} + ) + list(APPEND HEADERS + spellchecker/aspellchecker.h + ) + list(APPEND SOURCES + spellchecker/aspellchecker.cpp + ) +endif() + +if(LINUX AND USE_XSS) + find_package(X11 COMPONENTS Xss REQUIRED) + include_directories(${X11_Xscreensaver_INCLUDE_PATH}) + list(APPEND EXTRA_LDFLAGS ${X11_Xscreensaver_LIB}) +endif() + +qt_wrap_ui(UI_FORMS ${FORMS}) +add_library(libpsi_tools STATIC ${SOURCES} ${HEADERS} ${UI_FORMS}) +target_link_libraries(libpsi_tools iris ${QT_LIBRARIES} tools ${EXTRA_LDFLAGS}) +target_include_directories(libpsi_tools PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +add_subdirectory(zip) diff --git a/src/libpsi/tools/atomicxmlfile/atomicxmlfile.cpp b/src/libpsi/tools/atomicxmlfile/atomicxmlfile.cpp new file mode 100644 index 000000000..e3e0bd869 --- /dev/null +++ b/src/libpsi/tools/atomicxmlfile/atomicxmlfile.cpp @@ -0,0 +1,134 @@ +/* + * atomicxmlfile.cpp - atomic saving of QDomDocuments in files + * Copyright (C) 2007 Michail Pishchagin + * + * 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 2 + * 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 . + * + */ + +#include "atomicxmlfile.h" + +#include +#include +#include + +/** + * Creates new instance of AtomicXmlFile class that will be able to + * atomically save config file, so if application is terminated while + * saving config file, data is not lost. + */ +AtomicXmlFile::AtomicXmlFile(const QString &fileName) : fileName_(fileName) { } + +QStringList AtomicXmlFile::loadCandidateList() const +{ + QStringList fileNames; + fileNames << fileName_ << tempFileName() << backupFileName(); + return fileNames; +} + +/** + * Returns name of the file the config is first written to. + */ +QString AtomicXmlFile::tempFileName() const { return fileName_ + ".temp"; } + +/** + * Returns name of the back up file. + */ +QString AtomicXmlFile::backupFileName() const { return fileName_ + ".backup"; } + +bool AtomicXmlFile::saveDocument(const QDomDocument &doc, QString fileName) const +{ + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + return false; + } + + QTextStream text(&file); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + text.setCodec("UTF-8"); +#else + text.setEncoding(QStringConverter::Utf8); +#endif + text << doc.toString(); + text.flush(); + + bool res = (file.error() == QFile::NoError); + if (res) + res = file.flush(); + file.close(); + + return res; +} + +bool AtomicXmlFile::loadDocument(QDomDocument *doc, QString fileName) const +{ + Q_ASSERT(doc); + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + return false; + } + + return bool(doc->setContent(&file)); +} + +bool AtomicXmlFile::saveDocument(AtomicXmlFileWriter *writer, QString fileName) const +{ + Q_ASSERT(writer); + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) { + return false; + } + + if (!writer->write(&file)) { + return false; + } + + return file.error() == QFile::NoError; +} + +bool AtomicXmlFile::loadDocument(AtomicXmlFileReader *reader, QString fileName) const +{ + Q_ASSERT(reader); + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + return false; + } + + if (!reader->read(&file)) { + qWarning("Parse error in file %s at line %d, column %d:\n%s", qPrintable(fileName), (int)reader->lineNumber(), + (int)reader->columnNumber(), qPrintable(reader->errorString())); + + return false; + } + + return true; +} + +/** + * Check if an AtomicXmlFile exists. + * returns true if any of the files loadDocument tries to read exists, + * it *doesn't* check that there is at least one uncorupted file. + */ +bool AtomicXmlFile::exists(const QString &fileName) +{ + AtomicXmlFile tmp(fileName); + + const QStringList &fileNames(tmp.loadCandidateList()); + for (const QString &fileName : fileNames) { + if (QFile::exists(fileName)) { + return true; + } + } + return false; +} diff --git a/src/libpsi/tools/atomicxmlfile/atomicxmlfile.h b/src/libpsi/tools/atomicxmlfile/atomicxmlfile.h new file mode 100644 index 000000000..c21bae513 --- /dev/null +++ b/src/libpsi/tools/atomicxmlfile/atomicxmlfile.h @@ -0,0 +1,113 @@ +/* + * atomicxmlfile.h - atomic saving of QDomDocuments in files + * Copyright (C) 2007 Michail Pishchagin + * + * 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 2 + * 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 . + * + */ + +#ifndef ATOMICXMLFILE_H +#define ATOMICXMLFILE_H + +#include +#include +#include +#include +#include + +class QDomDocument; + +class AtomicXmlFileReader : public QXmlStreamReader { +public: + virtual ~AtomicXmlFileReader() { } + virtual bool read(QIODevice *device) = 0; +}; + +class AtomicXmlFileWriter : public QXmlStreamWriter { +public: + virtual ~AtomicXmlFileWriter() { } + virtual bool write(QIODevice *device) = 0; +}; + +class AtomicXmlFile { +public: + AtomicXmlFile(const QString &fileName); + + /** + * Atomically save \a writer to specified name. Prior to saving, back up + * of old config data is created, and only then data is saved. + */ + template bool saveDocument(T writer) const + { + if (!saveDocument(writer, tempFileName())) { + qWarning("AtomicXmlFile::saveDocument(): Unable to save '%s'. Possibly drive is full.", + qPrintable(tempFileName())); + return false; + } + + if (QFile::exists(fileName_)) { + if (QFile::exists(backupFileName())) + QFile::remove(backupFileName()); + if (!QFile::rename(fileName_, backupFileName())) { + qWarning("AtomicXmlFile::saveDocument(): Unable to rename '%s' to '%s'.", qPrintable(fileName_), + qPrintable(backupFileName())); + return false; + } + } + + if (!QFile::rename(tempFileName(), fileName_)) { + qWarning("AtomicXmlFile::saveDocument(): Unable to rename '%s' to '%s'.", qPrintable(tempFileName()), + qPrintable(fileName_)); + return false; + } + + return true; + } + + /** + * Tries to load \a reader from config file, or if that fails, from a back up. + */ + template bool loadDocument(T reader) const + { + Q_ASSERT(reader); + + const QStringList &fileNames(loadCandidateList()); + for (const QString &fileName : fileNames) { + if (loadDocument(reader, fileName)) { + return true; + } + if (QFile::exists(fileName)) { + // The file exists, but it is incorrect. Remove it. Otherwise, enter the backup. + QFile::remove(fileName); + } + } + + return false; + } + + static bool exists(const QString &fileName); + +private: + QString fileName_; + + QString tempFileName() const; + QString backupFileName() const; + QStringList loadCandidateList() const; + bool saveDocument(const QDomDocument &doc, QString fileName) const; + bool loadDocument(QDomDocument *doc, QString fileName) const; + bool saveDocument(AtomicXmlFileWriter *writer, QString fileName) const; + bool loadDocument(AtomicXmlFileReader *reader, QString fileName) const; +}; + +#endif // ATOMICXMLFILE_H diff --git a/src/libpsi/tools/globalshortcut/NDKeyboardLayout.h b/src/libpsi/tools/globalshortcut/NDKeyboardLayout.h new file mode 100644 index 000000000..ada0985cb --- /dev/null +++ b/src/libpsi/tools/globalshortcut/NDKeyboardLayout.h @@ -0,0 +1,149 @@ +/* + NDKeyboardLayout.h + + Created by Nathan Day on 01.18.10 under a MIT-style license. + Copyright (c) 2010 Nathan Day + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/*! + @header NDKeyboardLayout.h + @abstract Header file for NDKeyboardLayout + @author Nathan Day + */ + +#import +#import +#import + +struct ReverseMappingEntry; + +extern NSString *const NDKeyboardLayoutSelectedKeyboardInputSourceChangedNotification; +extern NSString *const NDKeyboardLayoutPreviousKeyboardLayoutUserInfoKey; + +/*! + @function NDCocoaModifierFlagsForCarbonModifierFlags + Convert Carbon modifer flags to Cocoa modifier flags. + @param modifierFlags one or more of the flags shiftKey, controlKey, optionKey, + cmdKey + */ +NSUInteger NDCocoaModifierFlagsForCarbonModifierFlags(NSUInteger modifierFlags); +/*! + @function NDCarbonModifierFlagsForCocoaModifierFlags + Convert Cocoa modifer flags to Carbon modifier flags. + @param modifierFlags one or more of the flags NSShiftKeyMask, NSControlKeyMask, + NSAlternateKeyMask, NSCommandKeyMask + */ +NSUInteger NDCarbonModifierFlagsForCocoaModifierFlags(NSUInteger modifierFlags); + +/*! + @class NDKeyboardLayout + @abstract Class for translating between key codes and key characters. + @discussion The key code for each key character can change between hardware and with localisation, + NDKeyboardLayout handles translation between key codes and key characters as well as for generating strings + for display purposes. + @helps Used by NDHotKeyEvent. + */ +@interface NDKeyboardLayout : NSObject { + CFDataRef keyboardLayoutData; + struct ReverseMappingEntry *mappings; + NSUInteger numberOfMappings; +} + +@property (readonly, nonatomic) const UCKeyboardLayout *keyboardLayoutPtr; + +/*! + @method keyboardLayout + Get a keyboard layout for the current keyboard + */ ++ (id)keyboardLayout; + +/*! + @method init + initialise a keyboard layout for the current keyboard, if that fails a keyboard layout for one of the languages + returned from [NSLocale preferredLanguages] is attempted and if finally if that fails a keyboard layout + for the most recently used ASCII-capable keyboard is created. If that fails then this method returns nil. + */ +- (id)init; +/*! + @method initWithLanguage: + @abstract initialise a keyboard layout. + @discussion Initialises a KeyboardLayout with an TISInputSourceRef for the supplied language. + */ +- (id)initWithLanguage:(NSString *)langauge; +/*! + @method initWithInputSource: + @abstract initialise a keyboard layout. + @discussion Initialises a KeyboardLayout with an TISInputSourceRef, this method is called with the result + from initWithInputSource:TISCopyCurrentKeyboardInputSource(). + */ +- (id)initWithInputSource:(TISInputSourceRef)source; + +/*! + @method stringForCharacter:modifierFlags: + @abstract Get a string for display purposes. + @discussion stringForCharacter:modifierFlags: returns a string that can be displayed to the user, For + example command-z would produce ⌘Z, shift-T would produce ⇧T. + @param character The unmodified character on the keyboard. + @param modifierFlags Modifier flags NSControlKeyMask, NSAlternateKeyMask, NSShiftKeyMask, + NSCommandKeyMask and NSNumericPadKeyMask. + */ +- (NSString *)stringForCharacter:(unichar)character modifierFlags:(UInt32)modifierFlags; +/*! + @method stringForKeyCode:modifierFlags: + @abstract Get a string for display purposes. + @discussion stringForKeyCode:modifierFlags: returns a string that can be displayed to the user. This method + is called by stringForCharacter::modifierFlags and is problem more useful most of the time. + @param keyCode A value specifying the virtual key code that is to be translated. For ADB keyboards, virtual key + codes are in the range from 0 to 127. + @param modifierFlags Modifier flags NSControlKeyMask, NSAlternateKeyMask, NSShiftKeyMask, + NSCommandKeyMask and NSNumericPadKeyMask. + */ +- (NSString *)stringForKeyCode:(UInt16)keyCode modifierFlags:(UInt32)modifierFlags; +/*! + @method characterForKeyCode: + @abstract Get the key character for a given key code. + @discussion The character returned is the unmodified version on the keyboard. + @param keyCode A value specifying the virtual key code that is to be translated. For ADB keyboards, virtual key + codes are in the range from 0 to 127. + @result The character for the unmodified version of the key. + */ +- (unichar)characterForKeyCode:(UInt16)keyCode; +/*! + @method keyCodeForCharacter:numericPad: + @abstract Get the key code for a given key character. + @discussion The character pass in must be the unshifter character for the key, for example to get the key code for + the '?' on keyboards where you type shift-/ to get '?' you should pass in the character '/" + @param character The unmodified character on the keyboard. + @param numericPad For the keycode of a key on the keypad where the same character is also on the main keyboard this + flag needs to be YES. + */ +- (UInt16)keyCodeForCharacter:(unichar)character numericPad:(BOOL)numericPad; +/*! + @method keyCodeForCharacter: + @abstract Get the key code for a given key character. + @discussion Calls keyCodeForCharacter:numericPad: with the keypad flag set to NO + @param character The unmodified character on the keyboard. + @result A value specifying the virtual key code that is to be translated. For ADB keyboards, virtual key codes are + in the range from 0 to 127. + */ +- (UInt16)keyCodeForCharacter:(unichar)character; + +@end diff --git a/src/libpsi/tools/globalshortcut/NDKeyboardLayout.m b/src/libpsi/tools/globalshortcut/NDKeyboardLayout.m new file mode 100644 index 000000000..11e7cf25e --- /dev/null +++ b/src/libpsi/tools/globalshortcut/NDKeyboardLayout.m @@ -0,0 +1,471 @@ +/* + NDKeyboardLayout.m + + Created by Nathan Day on 01.18.10 under a MIT-style license. + Copyright (c) 2010 Nathan Day + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#import "NDKeyboardLayout.h" + +#include + +NSString *const NDKeyboardLayoutSelectedKeyboardInputSourceChangedNotification + = @"NDKeyboardLayoutSelectedKeyboardInputSourceChanged"; +NSString *const NDKeyboardLayoutPreviousKeyboardLayoutUserInfoKey = @"NDKeyboardLayoutPreviousKeyboardLayout"; + +struct ReverseMappingEntry { + UniChar character; + BOOL keypad; + UInt16 keyCode; +}; + +struct UnmappedEntry { + UniChar character; + UInt16 keyCode; + unichar description[4]; +}; + +struct UnmappedEntry unmappedKeys[] = { + { NSDeleteFunctionKey, 0x33, { 0x232B, '\0', '\0', '\0' } }, + { NSF17FunctionKey, 0x40, { 'F', '1', '7', '\0' } }, + { NSClearDisplayFunctionKey, 0x47, { 0x2327, '\0', '\0', '\0' } }, + { NSF18FunctionKey, 0x4F, { 'F', '1', '8', '\0' } }, + { NSF19FunctionKey, 0x50, { 'F', '1', '9', '\0' } }, + { NSF5FunctionKey, 0x60, { 'F', '5', '\0', '\0' } }, + { NSF6FunctionKey, 0x61, { 'F', '6', '\0', '\0' } }, + { NSF7FunctionKey, 0x62, { 'F', '7', '\0', '\0' } }, + { NSF3FunctionKey, 0x63, { 'F', '3', '\0', '\0' } }, + { NSF8FunctionKey, 0x64, { 'F', '8', '\0', '\0' } }, + { NSF9FunctionKey, 0x65, { 'F', '9', '\0', '\0' } }, + { NSF11FunctionKey, 0x67, { 'F', '1', '1', '\0' } }, + { NSF14FunctionKey, 0x68, { 'F', '1', '4', '\0' } }, + { NSF13FunctionKey, 0x69, { 'F', '1', '3', '\0' } }, + { NSF16FunctionKey, 0x6A, { 'F', '1', '6', '\0' } }, + { NSF10FunctionKey, 0x6D, { 'F', '1', '0', '\0' } }, + { NSF12FunctionKey, 0x6F, { 'F', '1', '2', '\0' } }, + { NSF15FunctionKey, 0x71, { 'F', '1', '5', '\0' } }, + { NSHomeFunctionKey, 0x73, { 0x21F1, '\0', '\0', '\0' } }, + { NSPageUpFunctionKey, 0x74, { 0x21DE, '\0', '\0', '\0' } }, + { NSDeleteCharFunctionKey, 0x75, { 0x2326, '\0', '\0', '\0' } }, + { NSF4FunctionKey, 0x76, { 'F', '4', '\0', '\0' } }, + { NSEndFunctionKey, 0x77, { 0x21F2, '\0', '\0', '\0' } }, + { NSF2FunctionKey, 0x78, { 'F', '2', '\0', '\0' } }, + { NSPageDownFunctionKey, 0x79, { 0x21DF, '\0', '\0', '\0' } }, + { NSF1FunctionKey, 0x7A, { 'F', '1', '\0', '\0' } }, + { NSLeftArrowFunctionKey, 0x7B, { 0x2190, '\0', '\0', '\0' } }, + { NSRightArrowFunctionKey, 0x7C, { 0x2192, '\0', '\0', '\0' } }, + { NSDownArrowFunctionKey, 0x7D, { 0x2193, '\0', '\0', '\0' } }, + { NSUpArrowFunctionKey, 0x7E, { 0x2191, '\0', '\0', '\0' } } + // {NSF20FunctionKey, 0xXXXX}, + // {NSF21FunctionKey, 0xXXXX}, + // {NSF22FunctionKey, 0xXXXX}, + // {NSF23FunctionKey, 0xXXXX}, + // {NSF24FunctionKey, 0xXXXX}, + // {NSF25FunctionKey, 0xXXXX}, + // {NSF26FunctionKey, 0xXXXX}, + // {NSF27FunctionKey, 0xXXXX}, + // {NSF28FunctionKey, 0xXXXX}, + // {NSF29FunctionKey, 0xXXXX}, + // {NSF30FunctionKey, 0xXXXX}, + // {NSF31FunctionKey, 0xXXXX}, + // {NSF32FunctionKey, 0xXXXX}, + // {NSF33FunctionKey, 0xXXXX}, + // {NSF34FunctionKey, 0xXXXX}, + // {NSF35FunctionKey, 0xXXXX}, + // {NSInsertFunctionKey, 0xXXXX}, + // {NSBeginFunctionKey, 0xXXXX}, + // {NSPrintScreenFunctionKey, 0xXXXX}, + // {NSScrollLockFunctionKey, 0xXXXX}, + // {NSPauseFunctionKey, 0xXXXX}, + // {NSSysReqFunctionKey, 0xXXXX}, + // {NSBreakFunctionKey, 0xXXXX}, + // {NSResetFunctionKey, 0xXXXX}, + // {NSStopFunctionKey, 0xXXXX}, + // {NSMenuFunctionKey, 0xXXXX}, + // {NSUserFunctionKey, 0xXXXX}, + // {NSSystemFunctionKey, 0xXXXX}, + // {NSPrintFunctionKey, 0xXXXX}, + // {NSClearLineFunctionKey, 0xXXXX}, + // {NSInsertLineFunctionKey, 0xXXXX}, + // {NSDeleteLineFunctionKey, 0xXXXX}, + // {NSInsertCharFunctionKey, 0xXXXX}, + // {NSPrevFunctionKey, 0xXXXX}, + // {NSNextFunctionKey, 0xXXXX}, + // {NSSelectFunctionKey, 0xXXXX}, + // {NSExecuteFunctionKey, 0xXXXX}, + // {NSUndoFunctionKey, 0xXXXX}, + // {NSRedoFunctionKey, 0xXXXX}, + // {NSFindFunctionKey, 0xXXXX}, + // {NSHelpFunctionKey, 0xXXXX}, + // {NSModeSwitchFunctionKey, 0xXXXX} +}; + +/*@interface NDKeyboardLayout () +{ +@private + CFDataRef keyboardLayoutData; + struct ReverseMappingEntry * mappings; + NSUInteger numberOfMappings; +} + +@property(readonly,nonatomic) const UCKeyboardLayout * keyboardLayoutPtr; + +@end*/ + +static int _reverseMappingEntryCmpFunc(const void *a, const void *b) +{ + struct ReverseMappingEntry *theA = (struct ReverseMappingEntry *)a, *theB = (struct ReverseMappingEntry *)b; + return theA->character != theB->character ? theA->character - theB->character : theA->keypad - theB->keypad; +} + +static struct ReverseMappingEntry *_searchreverseMapping(struct ReverseMappingEntry *aMapping, NSUInteger aLength, + struct ReverseMappingEntry *aSearchValue) +{ + NSInteger low = 0, high = aLength - 1, mid, result; + + while (low <= high) { + mid = (low + high) >> 1; + result = _reverseMappingEntryCmpFunc(&aMapping[mid], aSearchValue); + if (result > 0) + high = mid - 1; + else if (result < 0) + low = mid + 1; + else + return &aMapping[mid]; + } + return NULL; +} + +static struct UnmappedEntry *_unmappedEntryForKeyCode(UInt16 aKeyCode) +{ + NSInteger low = 0, high = sizeof(unmappedKeys) / sizeof(*unmappedKeys) - 1, mid, result; + + while (low <= high) { + mid = (low + high) >> 1; + result = unmappedKeys[mid].keyCode - aKeyCode; + if (result > 0) + high = mid - 1; + else if (result < 0) + low = mid + 1; + else + return &unmappedKeys[mid]; + } + return '\0'; +} + +static const size_t kBufferSize = 4; +static NSUInteger _characterForModifierFlags(unichar aBuff[kBufferSize], UInt32 aModifierFlags) +{ + NSUInteger thePos = 0; + memset(aBuff, 0, kBufferSize); + if (aModifierFlags & NSControlKeyMask) + aBuff[thePos++] = kControlUnicode; + + if (aModifierFlags & NSAlternateKeyMask) + aBuff[thePos++] = kOptionUnicode; + + if (aModifierFlags & NSShiftKeyMask) + aBuff[thePos++] = kShiftUnicode; + + if (aModifierFlags & NSCommandKeyMask) + aBuff[thePos++] = kCommandUnicode; + return thePos; +} + +/* + * NDCocoaModifierFlagsForCarbonModifierFlags() + */ +NSUInteger NDCocoaModifierFlagsForCarbonModifierFlags(NSUInteger aModifierFlags) +{ + NSUInteger theCocoaModifierFlags = 0; + + if (aModifierFlags & shiftKey) + theCocoaModifierFlags |= NSShiftKeyMask; + + if (aModifierFlags & controlKey) + theCocoaModifierFlags |= NSControlKeyMask; + + if (aModifierFlags & optionKey) + theCocoaModifierFlags |= NSAlternateKeyMask; + + if (aModifierFlags & cmdKey) + theCocoaModifierFlags |= NSCommandKeyMask; + + return theCocoaModifierFlags; +} + +/* + * NDCarbonModifierFlagsForCocoaModifierFlags() + */ +NSUInteger NDCarbonModifierFlagsForCocoaModifierFlags(NSUInteger aModifierFlags) +{ + NSUInteger theCarbonModifierFlags = 0; + + if (aModifierFlags & NSShiftKeyMask) + theCarbonModifierFlags |= shiftKey; + + if (aModifierFlags & NSControlKeyMask) + theCarbonModifierFlags |= controlKey; + + if (aModifierFlags & NSAlternateKeyMask) + theCarbonModifierFlags |= optionKey; + + if (aModifierFlags & NSCommandKeyMask) + theCarbonModifierFlags |= cmdKey; + + return theCarbonModifierFlags; +} + +@implementation NDKeyboardLayout + +#pragma mark Utility Methods + +- (void)generateMappings +{ + mappings = (struct ReverseMappingEntry *)calloc(128 + sizeof(unmappedKeys) / sizeof(*unmappedKeys), + sizeof(struct ReverseMappingEntry)); + + numberOfMappings = 0; + + for (NSUInteger i = 0; i < 128; i++) { + UInt32 theDeadKeyState = 0; + UniCharCount theLength = 0; + + if (UCKeyTranslate(self.keyboardLayoutPtr, i, kUCKeyActionDisplay, 0, LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, &theDeadKeyState, 1, &theLength, + &mappings[numberOfMappings].character) + == noErr + && theLength > 0 && isprint(mappings[numberOfMappings].character)) { + mappings[numberOfMappings].keyCode = i; + numberOfMappings++; + } + } + + /* add unmapped keys */ + for (NSUInteger i = 0; i < sizeof(unmappedKeys) / sizeof(*unmappedKeys); i++) { + mappings[numberOfMappings].character = unmappedKeys[i].character; + mappings[numberOfMappings].keyCode = unmappedKeys[i].keyCode; + numberOfMappings++; + } + + mappings = (struct ReverseMappingEntry *)realloc((void *)mappings, + numberOfMappings * sizeof(struct ReverseMappingEntry)); + + // sort so we can perform binary searches + qsort((void *)mappings, numberOfMappings, sizeof(struct ReverseMappingEntry), _reverseMappingEntryCmpFunc); + + /* find keypad keys and set the keypad flag */ + for (NSUInteger i = 1; i < numberOfMappings; i++) { + NSParameterAssert(mappings[i - 1].keyCode != mappings[i].keyCode); + if (mappings[i - 1].character == mappings[i].character) // assume large keycode is a keypad + { + if (mappings[i - 1].keyCode > mappings[i].keyCode) // make the keypad entry is second + { + UInt16 theTemp = mappings[i - 1].keyCode; + mappings[i - 1].keyCode = mappings[i].keyCode; + mappings[i].keyCode = theTemp; + } + mappings[i].keypad = YES; + } + } + +#ifdef DEBUGGING_CODE + for (NSUInteger i = 1; i < numberOfMappings; i++) { + fprintf(stderr, "%d -> %c[%d]%s\n", mappings[i].keyCode, (char)mappings[i].character, mappings[i].character, + mappings[i].keypad ? " keypad" : ""); + NSAssert3(mappings[i - 1].character <= mappings[i].character, @"[%d] %d <= %d", i, mappings[i - 1].character, + mappings[i].character); + } +#endif +} + +#pragma mark Constructor Methods + +static volatile NDKeyboardLayout *kCurrentKeyboardLayout = nil; + +void NDKeyboardLayoutNotificationCallback(CFNotificationCenterRef aCenter, void *self, CFStringRef aName, + const void *anObj, CFDictionaryRef aUserInfo) +{ + (void)aCenter; + (void)aName; + (void)anObj; + (void)aUserInfo; + NSDictionary *theUserInfo = [NSDictionary dictionaryWithObject:kCurrentKeyboardLayout + forKey:NDKeyboardLayoutPreviousKeyboardLayoutUserInfoKey]; + @synchronized(self) { + [kCurrentKeyboardLayout release], kCurrentKeyboardLayout = nil; + } + [[NSNotificationCenter defaultCenter] + postNotificationName:NDKeyboardLayoutSelectedKeyboardInputSourceChangedNotification + object:self + userInfo:theUserInfo]; +} + ++ (void)initialize +{ + CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), (const void *)self, + NDKeyboardLayoutNotificationCallback, kTISNotifySelectedKeyboardInputSourceChanged, + NULL, CFNotificationSuspensionBehaviorDeliverImmediately); +} + ++ (id)keyboardLayout +{ + if (kCurrentKeyboardLayout == nil) { + @synchronized(self) { /* + Try different method until we succeed. + */ + TISInputSourceRef (*theInputSourceFunctions[])() + = { TISCopyInputMethodKeyboardLayoutOverride, TISCopyCurrentKeyboardLayoutInputSource, + TISCopyCurrentASCIICapableKeyboardLayoutInputSource }; + + for (NSUInteger i = 0; i < sizeof(theInputSourceFunctions) / sizeof(*theInputSourceFunctions) + && kCurrentKeyboardLayout == nil; + i++) { + TISInputSourceRef theInputSource = theInputSourceFunctions[i](); + if (theInputSource != NULL) { + kCurrentKeyboardLayout = [[self alloc] initWithInputSource:theInputSource]; + CFRelease(theInputSource); + } + } + } + } + + return kCurrentKeyboardLayout; +} + +- (id)init +{ + [self release]; + return [[NDKeyboardLayout keyboardLayout] retain]; +} + +- (id)initWithLanguage:(NSString *)aLangauge +{ + return [self initWithInputSource:TISCopyInputSourceForLanguage((CFStringRef)aLangauge)]; +} + +- (id)initWithInputSource:(TISInputSourceRef)aSource +{ + if ((self = [super init]) != nil) { + if (aSource != NULL + && (keyboardLayoutData + = (CFDataRef)CFMakeCollectable(TISGetInputSourceProperty(aSource, kTISPropertyUnicodeKeyLayoutData))) + != nil) { + CFRetain(keyboardLayoutData); + } else + self = nil, [self release]; + } + return self; +} + +- (void)dealloc +{ + if (mappings != NULL) + free((void *)mappings); + if (keyboardLayoutData != NULL) + CFRelease(keyboardLayoutData); + [super dealloc]; +} + +- (NSString *)stringForCharacter:(unichar)aCharacter modifierFlags:(UInt32)aModifierFlags +{ + return [self stringForKeyCode:[self keyCodeForCharacter:aCharacter + numericPad:(aModifierFlags & NSNumericPadKeyMask) != 0] + modifierFlags:aModifierFlags]; +} + +- (NSString *)stringForKeyCode:(UInt16)aKeyCode modifierFlags:(UInt32)aModifierFlags +{ + NSString *theResult = nil; + struct UnmappedEntry *theEntry = _unmappedEntryForKeyCode(aKeyCode); // is it one of the unmapped values + + if (theEntry != NULL) { + unichar theCharacter[sizeof(theEntry->description) / sizeof(*theEntry->description) + 4 + 1]; + memset(theCharacter, 0, sizeof(theCharacter)); + NSUInteger thePos = _characterForModifierFlags(theCharacter, aModifierFlags); + memcpy(theCharacter + thePos, theEntry->description, sizeof(theEntry->description)); + theResult = + [NSString stringWithCharacters:theCharacter + length:sizeof(theEntry->description) / sizeof(*theEntry->description) + thePos]; + } else { + UInt32 theDeadKeyState = 0; + UniCharCount theLength = 0; + UniChar theCharacter[260]; + + NSUInteger thePos = _characterForModifierFlags(theCharacter, aModifierFlags); + + if (UCKeyTranslate(self.keyboardLayoutPtr, aKeyCode, kUCKeyActionDisplay, + NDCarbonModifierFlagsForCocoaModifierFlags(aModifierFlags), LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, &theDeadKeyState, + sizeof(theCharacter) / sizeof(*theCharacter) - thePos, &theLength, theCharacter + thePos) + == noErr) { + + theResult = [[NSString stringWithCharacters:theCharacter length:theLength + thePos] uppercaseString]; + } + } + return theResult; +} + +- (unichar)characterForKeyCode:(UInt16)aKeyCode +{ + unichar theChar = 0; + struct UnmappedEntry *theEntry = _unmappedEntryForKeyCode(aKeyCode); + + if (theEntry == NULL) // is it one of the unmapped values + { + UInt32 theDeadKeyState = 0; + UniCharCount theLength = 0; + UniChar theCharacter[256]; + + if (UCKeyTranslate(self.keyboardLayoutPtr, aKeyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, &theDeadKeyState, sizeof(theCharacter) / sizeof(*theCharacter), + &theLength, theCharacter) + == noErr) { + theChar = theCharacter[0]; + } + } else + theChar = theEntry->character; + return toupper(theChar); +} + +- (UInt16)keyCodeForCharacter:(unichar)aCharacter +{ + return [self keyCodeForCharacter:aCharacter numericPad:NO]; +} + +- (UInt16)keyCodeForCharacter:(unichar)aCharacter numericPad:(BOOL)aNumericPad +{ + struct ReverseMappingEntry theSearchValue = { tolower(aCharacter), aNumericPad, 0 }; + struct ReverseMappingEntry *theEntry = NULL; + if (mappings == NULL) + [self generateMappings]; + theEntry = _searchreverseMapping(mappings, numberOfMappings, &theSearchValue); + return theEntry ? theEntry->keyCode : '\0'; +} + +#pragma mark - private + +- (const UCKeyboardLayout *)keyboardLayoutPtr +{ + return (const UCKeyboardLayout *)CFDataGetBytePtr(keyboardLayoutData); +} + +@end diff --git a/src/libpsi/tools/globalshortcut/globalshortcutmanager.cpp b/src/libpsi/tools/globalshortcut/globalshortcutmanager.cpp new file mode 100644 index 000000000..281bcc411 --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcutmanager.cpp @@ -0,0 +1,87 @@ +/* + * globalshortcutmanager.cpp - Class managing global shortcuts + * Copyright (C) 2006 Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#include "globalshortcutmanager.h" + +#include "globalshortcuttrigger.h" + +#include + +/** + * \brief Constructs new GlobalShortcutManager. + */ +GlobalShortcutManager::GlobalShortcutManager() : QObject(QCoreApplication::instance()) { } + +GlobalShortcutManager::~GlobalShortcutManager() { clear(); } + +GlobalShortcutManager *GlobalShortcutManager::instance_ = nullptr; + +/** + * \brief Returns the instance of GlobalShortcutManager. + */ +GlobalShortcutManager *GlobalShortcutManager::instance() +{ + if (!instance_) + instance_ = new GlobalShortcutManager(); + return instance_; +} + +/** + * \brief Connects a key sequence with a slot. + * \param key, global shortcut to be connected + * \param receiver, object which should receive the notification + * \param slot, the SLOT() of the \a receiver which should be triggerd if the \a key is activated + */ +void GlobalShortcutManager::connect(const QKeySequence &key, QObject *receiver, const char *slot) +{ + KeyTrigger *t = instance()->triggers_[key]; + if (!t) { + t = new KeyTrigger(key); + instance()->triggers_.insert(key, t); + } + + QObject::connect(t, SIGNAL(triggered()), receiver, slot); +} + +/** + * \brief Disonnects a key sequence from a slot. + * \param key, global shortcut to be disconnected + * \param receiver, object which \a slot is about to be disconnected + * \param slot, the SLOT() of the \a receiver which should no longer be triggerd if the \a key is activated + */ +void GlobalShortcutManager::disconnect(const QKeySequence &key, QObject *receiver, const char *slot) +{ + KeyTrigger *t = instance()->triggers_[key]; + if (!t) { + return; + } + + QObject::disconnect(t, SIGNAL(triggered()), receiver, slot); + + if (!t->isUsed()) { + delete instance()->triggers_.take(key); + } +} + +void GlobalShortcutManager::clear() +{ + for (KeyTrigger *t : std::as_const(instance()->triggers_)) + delete t; + instance()->triggers_.clear(); +} diff --git a/src/libpsi/tools/globalshortcut/globalshortcutmanager.h b/src/libpsi/tools/globalshortcut/globalshortcutmanager.h new file mode 100644 index 000000000..373e772d0 --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcutmanager.h @@ -0,0 +1,45 @@ +/* + * globalshortcutmanager.h - Class managing global shortcuts + * Copyright (C) 2006 Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#ifndef GLOBALSHORTCUTMANAGER_H +#define GLOBALSHORTCUTMANAGER_H + +#include +#include +#include + +class KeyTrigger; +class QObject; + +class GlobalShortcutManager : public QObject { +public: + static GlobalShortcutManager *instance(); + static void connect(const QKeySequence &key, QObject *receiver, const char *slot); + static void disconnect(const QKeySequence &key, QObject *receiver, const char *slot); + static void clear(); + +private: + GlobalShortcutManager(); + ~GlobalShortcutManager(); + static GlobalShortcutManager *instance_; + class KeyTrigger; + QMap triggers_; +}; + +#endif // GLOBALSHORTCUTMANAGER_H diff --git a/src/libpsi/tools/globalshortcut/globalshortcutmanager_haiku.cpp b/src/libpsi/tools/globalshortcut/globalshortcutmanager_haiku.cpp new file mode 100644 index 000000000..bf93f3cd5 --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcutmanager_haiku.cpp @@ -0,0 +1,302 @@ +/* + * globalshortcutmanager_haiku.cpp - Haiku implementation of global shortcuts by Vitaly (Diger) + * Based on X11 implementation of global shortcuts + * Copyright (C) 2003-2006 Justin Karneges, Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#include "globalshortcutmanager.h" +#include "globalshortcuttrigger.h" + +#include +#include +#include +#include +#include +#include + +class HKeyTrigger { +public: + virtual ~HKeyTrigger() { } + virtual void activate() = 0; + virtual bool isAccepted(int qkey) const = 0; +}; + +class HKeyTriggerManager : public QObject { +public: + static HKeyTriggerManager *instance() + { + if (!instance_) + instance_ = new HKeyTriggerManager(); + return instance_; + } + + void addTrigger(HKeyTrigger *trigger) { triggers_ << trigger; } + + void removeTrigger(HKeyTrigger *trigger) { triggers_.removeAll(trigger); } + + struct Qt_HK_Keygroup { + char num; + int sym[3]; + }; + +protected: + // reimplemented + bool eventFilter(QObject *o, QEvent *e) + { + if (e->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast(e); + int qkey = k->key(); + if (k->modifiers() & Qt::ShiftModifier) + qkey |= Qt::SHIFT; + if (k->modifiers() & Qt::ControlModifier) + qkey |= Qt::CTRL; + if (k->modifiers() & Qt::AltModifier) + qkey |= Qt::ALT; + if (k->modifiers() & Qt::MetaModifier) + qkey |= Qt::META; + + foreach (HKeyTrigger *trigger, triggers_) { + if (trigger->isAccepted(qkey)) { + trigger->activate(); + return true; + } + } + } + + return QObject::eventFilter(o, e); + } + +private: + HKeyTriggerManager() : QObject(QCoreApplication::instance()) + { + QCoreApplication::instance()->installEventFilter(this); + } + + static HKeyTriggerManager *instance_; + QList triggers_; + +private: + struct Qt_HK_Keymap { + int key; + Qt_HK_Keygroup hk; + }; + + static Qt_HK_Keymap qt_hk_table[]; + static long alt_mask; + static long meta_mask; + static long super_mask; + static long hyper_mask; + static long numlock_mask; + static bool haveMods; + + // adapted from qapplication_x11.cpp + static void ensureModifiers() + { + if (haveMods) + return; + } + +public: + static bool convertKeySequence(const QKeySequence &ks, unsigned int *_mod, Qt_HK_Keygroup *_kg) + { + int code = ks[0]; + ensureModifiers(); + + unsigned int mod = 0; + /* + if (code & Qt::META) + mod |= meta_mask; + if (code & Qt::SHIFT) + mod |= ShiftMask; + if (code & Qt::CTRL) + mod |= ControlMask; + if (code & Qt::ALT) + mod |= alt_mask; + */ + Qt_HK_Keygroup kg; + kg.num = 0; + kg.sym[0] = 0; + code &= ~Qt::KeyboardModifierMask; + + bool found = false; + for (int n = 0; qt_hk_table[n].key != Qt::Key_unknown; ++n) { + if (qt_hk_table[n].key == code) { + kg = qt_hk_table[n].hk; + found = true; + break; + } + } + + if (!found) { + // try latin1 + if (code >= 0x20 && code <= 0x7f) { + kg.num = 1; + kg.sym[0] = code; + } + } + + if (!kg.num) + return false; + + if (_mod) + *_mod = mod; + if (_kg) + *_kg = kg; + + return true; + } + + static QList ignModifiersList() + { + QList ret; + /* + if (numlock_mask) { + ret << 0 << LockMask << numlock_mask << (LockMask | numlock_mask); + } + else { + ret << 0 << LockMask; + } + */ + return ret; + } +}; + +HKeyTriggerManager *HKeyTriggerManager::instance_ = NULL; +class GlobalShortcutManager::KeyTrigger::Impl : public HKeyTrigger { +private: + KeyTrigger *trigger_; + int qkey_; + + struct GrabbedKey { + int code; + uint mod; + }; + QList grabbedKeys_; + + static bool failed; + +public: + /** + * Constructor registers the hotkey. + */ + Impl(GlobalShortcutManager::KeyTrigger *t, const QKeySequence &ks) : trigger_(t), qkey_(ks[0]) + { + HKeyTriggerManager::instance()->addTrigger(this); + + HKeyTriggerManager::Qt_HK_Keygroup kg; + unsigned int mod; + } + + /** + * Destructor unregisters the hotkey. + */ + ~Impl() { HKeyTriggerManager::instance()->removeTrigger(this); } + + void activate() { emit trigger_->triggered(); } + + bool isAccepted(int qkey) const { return qkey_ == qkey; } +}; + +bool GlobalShortcutManager::KeyTrigger::Impl::failed; +long HKeyTriggerManager::alt_mask = 0; +long HKeyTriggerManager::meta_mask = 0; +long HKeyTriggerManager::super_mask = 0; +long HKeyTriggerManager::hyper_mask = 0; +long HKeyTriggerManager::numlock_mask = 0; +bool HKeyTriggerManager::haveMods = false; + +HKeyTriggerManager::Qt_HK_Keymap HKeyTriggerManager::qt_hk_table[] = { + { Qt::Key_Escape, { 1, { B_ESCAPE } } }, + { Qt::Key_Tab, { 1, { B_TAB } } }, + { Qt::Key_Backtab, { 0, { 0 } } }, + { Qt::Key_Backspace, { 1, { B_BACKSPACE } } }, + { Qt::Key_Return, { 1, { B_RETURN } } }, + { Qt::Key_Enter, { 1, { B_ENTER } } }, + { Qt::Key_Insert, { 1, { B_INSERT } } }, + { Qt::Key_Delete, { 1, { B_DELETE } } }, + { Qt::Key_Pause, { 1, { B_PAUSE_KEY } } }, + { Qt::Key_Print, { 1, { B_PRINT_KEY } } }, + { Qt::Key_SysReq, { 0, { 0 } } }, + //{ Qt::Key_Clear, B_CLEAR_KEY}, + { Qt::Key_Home, { 1, { B_HOME } } }, + { Qt::Key_End, { 1, { B_END } } }, + { Qt::Key_Left, { 1, { B_LEFT_ARROW } } }, + { Qt::Key_Up, { 1, { B_UP_ARROW } } }, + { Qt::Key_Right, { 1, { B_RIGHT_ARROW } } }, + { Qt::Key_Down, { 1, { B_DOWN_ARROW } } }, + { Qt::Key_PageUp, { 1, { B_PAGE_UP } } }, + { Qt::Key_PageDown, { 1, { B_PAGE_DOWN } } }, + { Qt::Key_Shift, { 1, { B_SHIFT_KEY } } }, + { Qt::Key_Control, { 1, { B_CONTROL_KEY } } }, + { Qt::Key_Meta, { 1, { B_LEFT_OPTION_KEY } } }, + { Qt::Key_Alt, { 1, { B_MENU_KEY } } }, + { Qt::Key_CapsLock, { 1, { B_CAPS_LOCK } } }, + { Qt::Key_NumLock, { 1, { B_NUM_LOCK } } }, + { Qt::Key_ScrollLock, { 1, { B_SCROLL_KEY } } }, + { Qt::Key_F1, { 1, { B_F1_KEY } } }, + { Qt::Key_F2, { 1, { B_F2_KEY } } }, + { Qt::Key_F3, { 1, { B_F3_KEY } } }, + { Qt::Key_F4, { 1, { B_F4_KEY } } }, + { Qt::Key_F5, { 1, { B_F5_KEY } } }, + { Qt::Key_F6, { 1, { B_F6_KEY } } }, + { Qt::Key_F7, { 1, { B_F7_KEY } } }, + { Qt::Key_F8, { 1, { B_F8_KEY } } }, + { Qt::Key_F9, { 1, { B_F9_KEY } } }, + { Qt::Key_F10, { 1, { B_F10_KEY } } }, + { Qt::Key_F11, { 1, { B_F11_KEY } } }, + { Qt::Key_F12, { 1, { B_F12_KEY } } }, + { Qt::Key_F13, { 0, { 0 } } }, + { Qt::Key_F14, { 0, { 0 } } }, + { Qt::Key_F15, { 0, { 0 } } }, + { Qt::Key_F16, { 0, { 0 } } }, + { Qt::Key_F17, { 0, { 0 } } }, + { Qt::Key_F18, { 0, { 0 } } }, + { Qt::Key_F19, { 0, { 0 } } }, + { Qt::Key_F20, { 0, { 0 } } }, + { Qt::Key_F21, { 0, { 0 } } }, + { Qt::Key_F22, { 0, { 0 } } }, + { Qt::Key_F23, { 0, { 0 } } }, + { Qt::Key_F24, { 0, { 0 } } }, + { Qt::Key_F25, { 0, { 0 } } }, + { Qt::Key_F26, { 0, { 0 } } }, + { Qt::Key_F27, { 0, { 0 } } }, + { Qt::Key_F28, { 0, { 0 } } }, + { Qt::Key_F29, { 0, { 0 } } }, + { Qt::Key_F30, { 0, { 0 } } }, + { Qt::Key_F31, { 0, { 0 } } }, + { Qt::Key_F32, { 0, { 0 } } }, + { Qt::Key_F33, { 0, { 0 } } }, + { Qt::Key_F34, { 0, { 0 } } }, + { Qt::Key_F35, { 0, { 0 } } }, + { Qt::Key_Super_L, { 0, { 0 } } }, + { Qt::Key_Super_R, { 0, { 0 } } }, + { Qt::Key_Menu, { 0, { 0 } } }, + { Qt::Key_Hyper_L, { 0, { 0 } } }, + { Qt::Key_Hyper_R, { 0, { 0 } } }, + { Qt::Key_Help, { 0, { 0 } } }, + { Qt::Key_Direction_L, { 0, { 0 } } }, + { Qt::Key_Direction_R, { 0, { 0 } } }, + + { Qt::Key_unknown, { 0, { 0 } } }, +}; +GlobalShortcutManager::KeyTrigger::KeyTrigger(const QKeySequence &key) { d = new Impl(this, key); } + +GlobalShortcutManager::KeyTrigger::~KeyTrigger() +{ + delete d; + d = 0; +} diff --git a/src/libpsi/tools/globalshortcut/globalshortcutmanager_mac.mm b/src/libpsi/tools/globalshortcut/globalshortcutmanager_mac.mm new file mode 100644 index 000000000..3bad28dbd --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcutmanager_mac.mm @@ -0,0 +1,228 @@ +/* + * globalshortcutmanager_mac.cpp - Mac OS X implementation of global shortcuts + * Copyright (C) 2003-2007 Eric Smith, Michail Pishchagin + * + * 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 2 + * 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 . + * + */ + +#import "NDKeyboardLayout.h" + +#include "globalshortcutmanager.h" +#include "globalshortcuttrigger.h" + +#include +#include + +// TODO: +// - don't invoke hotkey if there is a modal dialog? +// - do multi-mapping, like the x11 version + +class MacKeyTrigger { +public: + virtual ~MacKeyTrigger() { } + virtual void activate() = 0; + virtual bool isAccepted(int id) const = 0; +}; + +class MacKeyTriggerManager : public QObject { +public: + static MacKeyTriggerManager *instance() + { + if (!instance_) + instance_ = new MacKeyTriggerManager(); + return instance_; + } + + void addTrigger(MacKeyTrigger *trigger) { triggers_ << trigger; } + + void removeTrigger(MacKeyTrigger *trigger) { triggers_.removeAll(trigger); } + +private: + MacKeyTriggerManager() : QObject(QCoreApplication::instance()) + { + hot_key_function_ = NewEventHandlerUPP(hotKeyHandler); + EventTypeSpec type; + type.eventClass = kEventClassKeyboard; + type.eventKind = kEventHotKeyPressed; + InstallApplicationEventHandler(hot_key_function_, 1, &type, this, NULL); + } + + /** + * Callback function invoked when the user hits a hot-key. + */ + static pascal OSStatus hotKeyHandler(EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void *userData) + { + EventHotKeyID hkID; + GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, + &hkID); + static_cast(userData)->activated(hkID.id); + return noErr; + } + + void activated(int id) + { + foreach (MacKeyTrigger *trigger, triggers_) { + if (trigger->isAccepted(id)) { + trigger->activate(); + break; + } + } + } + + static MacKeyTriggerManager *instance_; + QList triggers_; + + static EventHandlerUPP hot_key_function_; + +private: + struct Qt_Mac_Keymap { + int qt_key; + int mac_key; + }; + + static Qt_Mac_Keymap qt_keymap[]; + +public: + static bool convertKeySequence(const QKeySequence &ks, quint32 *_key, quint32 *_mod) + { + int code = ks[0]; + + quint32 mod = 0; + if (code & Qt::META) + mod |= controlKey; + if (code & Qt::SHIFT) + mod |= shiftKey; + if (code & Qt::CTRL) + mod |= cmdKey; + if (code & Qt::ALT) + mod |= optionKey; + + code &= ~Qt::KeyboardModifierMask; + quint32 key = 0; + for (int n = 0; qt_keymap[n].qt_key != Qt::Key_unknown; ++n) { + if (qt_keymap[n].qt_key == code) { + key = qt_keymap[n].mac_key; + break; + } + } + if (key == 0) { + key = [[NDKeyboardLayout keyboardLayout] keyCodeForCharacter:(code & 0xffff)]; + } + + if (_mod) + *_mod = mod; + if (_key) + *_key = key; + + return true; + } +}; + +MacKeyTriggerManager *MacKeyTriggerManager::instance_ = NULL; +EventHandlerUPP MacKeyTriggerManager::hot_key_function_ = NULL; + +class GlobalShortcutManager::KeyTrigger::Impl : public MacKeyTrigger { +private: + KeyTrigger *trigger_; + EventHotKeyRef hotKey_; + int id_; + static int nextId; + +public: + /** + * Constructor registers the hotkey. + */ + Impl(GlobalShortcutManager::KeyTrigger *t, const QKeySequence &ks) : trigger_(t), id_(0) + { + MacKeyTriggerManager::instance()->addTrigger(this); + + quint32 key, mod; + if (MacKeyTriggerManager::convertKeySequence(ks, &key, &mod)) { + EventHotKeyID hotKeyID; + hotKeyID.signature = 'QtHK'; + hotKeyID.id = nextId; + + OSStatus ret = RegisterEventHotKey(key, mod, hotKeyID, GetApplicationEventTarget(), 0, &hotKey_); + if (ret != 0) { + qWarning("RegisterEventHotKey(%d, %d): %d", key, mod, (int)ret); + return; + } + + id_ = nextId++; + } + } + + /** + * Destructor unregisters the hotkey. + */ + ~Impl() + { + MacKeyTriggerManager::instance()->removeTrigger(this); + + if (id_) + UnregisterEventHotKey(hotKey_); + } + + void activate() { emit trigger_->triggered(); } + + bool isAccepted(int id) const { return id_ == id; } +}; + +/* + * The following table is from Apple sample-code. + * Apple's headers don't appear to define any constants for the virtual key + * codes of special keys, but these constants are somewhat documented in the chart at + * + * + * The constants on the chartappear to be the same values as are used in Apple's iGetKeys + * sample. + * . + * + * See also . + */ +MacKeyTriggerManager::Qt_Mac_Keymap MacKeyTriggerManager::qt_keymap[] + = { { Qt::Key_Escape, 0x35 }, { Qt::Key_Tab, 0x30 }, { Qt::Key_Backtab, 0 }, { Qt::Key_Backspace, 0x33 }, + { Qt::Key_Return, 0x24 }, { Qt::Key_Enter, 0x4c }, // Return & Enter are different on the Mac + { Qt::Key_Insert, 0 }, { Qt::Key_Delete, 0x75 }, { Qt::Key_Pause, 0 }, { Qt::Key_Print, 0 }, + { Qt::Key_SysReq, 0 }, { Qt::Key_Clear, 0x47 }, { Qt::Key_Home, 0x73 }, { Qt::Key_End, 0x77 }, + { Qt::Key_Left, 0x7b }, { Qt::Key_Up, 0x7e }, { Qt::Key_Right, 0x7c }, { Qt::Key_Down, 0x7d }, + { Qt::Key_PageUp, 0x74 }, // Page Up + { Qt::Key_PageDown, 0x79 }, // Page Down + { Qt::Key_Shift, 0x38 }, { Qt::Key_Control, 0x3b }, { Qt::Key_Meta, 0x37 }, // Command + { Qt::Key_Alt, 0x3a }, // Option + { Qt::Key_CapsLock, 57 }, { Qt::Key_NumLock, 0 }, { Qt::Key_ScrollLock, 0 }, { Qt::Key_F1, 0x7a }, + { Qt::Key_F2, 0x78 }, { Qt::Key_F3, 0x63 }, { Qt::Key_F4, 0x76 }, { Qt::Key_F5, 0x60 }, + { Qt::Key_F6, 0x61 }, { Qt::Key_F7, 0x62 }, { Qt::Key_F8, 0x64 }, { Qt::Key_F9, 0x65 }, + { Qt::Key_F10, 0x6d }, { Qt::Key_F11, 0x67 }, { Qt::Key_F12, 0x6f }, { Qt::Key_F13, 0x69 }, + { Qt::Key_F14, 0x6b }, { Qt::Key_F15, 0x71 }, { Qt::Key_F16, 0 }, { Qt::Key_F17, 0 }, + { Qt::Key_F18, 0 }, { Qt::Key_F19, 0 }, { Qt::Key_F20, 0 }, { Qt::Key_F21, 0 }, + { Qt::Key_F22, 0 }, { Qt::Key_F23, 0 }, { Qt::Key_F24, 0 }, { Qt::Key_F25, 0 }, + { Qt::Key_F26, 0 }, { Qt::Key_F27, 0 }, { Qt::Key_F28, 0 }, { Qt::Key_F29, 0 }, + { Qt::Key_F30, 0 }, { Qt::Key_F31, 0 }, { Qt::Key_F32, 0 }, { Qt::Key_F33, 0 }, + { Qt::Key_F34, 0 }, { Qt::Key_F35, 0 }, { Qt::Key_Super_L, 0 }, { Qt::Key_Super_R, 0 }, + { Qt::Key_Menu, 0 }, { Qt::Key_Hyper_L, 0 }, { Qt::Key_Hyper_R, 0 }, { Qt::Key_Help, 0x72 }, + { Qt::Key_Direction_L, 0 }, { Qt::Key_Direction_R, 0 }, + + { Qt::Key_unknown, 0 } }; + +int GlobalShortcutManager::KeyTrigger::Impl::nextId = 1; + +GlobalShortcutManager::KeyTrigger::KeyTrigger(const QKeySequence &key) { d = new Impl(this, key); } + +GlobalShortcutManager::KeyTrigger::~KeyTrigger() +{ + delete d; + d = 0; +} diff --git a/src/libpsi/tools/globalshortcut/globalshortcutmanager_stub.cpp b/src/libpsi/tools/globalshortcut/globalshortcutmanager_stub.cpp new file mode 100644 index 000000000..febef86f9 --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcutmanager_stub.cpp @@ -0,0 +1,28 @@ +/* + * globalshortcutmanager_x11.cpp - X11 implementation of global shortcuts + * Copyright (C) 2003-2007 Justin Karneges, Michail Pishchagin + * + * 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 2 + * 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 . + * + */ + +#include "globalshortcutmanager.h" +#include "globalshortcuttrigger.h" + +#include +#include + +GlobalShortcutManager::KeyTrigger::KeyTrigger(const QKeySequence &) { } + +GlobalShortcutManager::KeyTrigger::~KeyTrigger() { } diff --git a/src/libpsi/tools/globalshortcut/globalshortcutmanager_win.cpp b/src/libpsi/tools/globalshortcut/globalshortcutmanager_win.cpp new file mode 100644 index 000000000..0f696e659 --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcutmanager_win.cpp @@ -0,0 +1,218 @@ +/* + * globalshortcutmanager_win.cpp - Windows implementation of global shortcuts + * Copyright (C) 2003-2006 Justin Karneges, Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#include "globalshortcutmanager.h" +#include "globalshortcuttrigger.h" + +#include +#include +#include +#include + +class GlobalShortcutManager::KeyTrigger::Impl : public QWidget { + class WinEventFilter : public QAbstractNativeEventFilter { + GlobalShortcutManager::KeyTrigger::Impl *impl; + + public: + WinEventFilter(GlobalShortcutManager::KeyTrigger::Impl *parent) : impl(parent) + { + qApp->installNativeEventFilter(this); + } + + virtual bool nativeEventFilter(const QByteArray &eventType, void *m, long *result) Q_DECL_OVERRIDE + { + if (eventType == "windows_generic_MSG") { + return impl->nativeEvent(eventType, static_cast(m), result); + } + return false; + } + }; + +public: + /** + * Constructor registers the hotkey. + */ + Impl(GlobalShortcutManager::KeyTrigger *t, const QKeySequence &ks) : + filter(new WinEventFilter(this)), trigger_(t), id_(0) + { + UINT mod = 0, key = 0; + if (convertKeySequence(ks, &mod, &key)) + if (RegisterHotKey(HWND(winId()), nextId, mod, key)) + id_ = nextId++; + } + + /** + * Destructor unregisters the hotkey. + */ + ~Impl() + { + delete filter; + if (id_) + UnregisterHotKey(HWND(winId()), id_); + } + + /** + * Triggers triggered() signal when the hotkey is activated. + */ + bool nativeEvent(const QByteArray &eventType, MSG *m, long *result) + { + Q_UNUSED(eventType); + if (m->message == WM_HOTKEY && m->wParam == id_) { + emit trigger_->triggered(); + return true; + } + Q_UNUSED(result); + return false; + } + +private: + WinEventFilter *filter; + KeyTrigger *trigger_; + WPARAM id_; + static WPARAM nextId; + +private: + struct Qt_VK_Keymap { + int key; + UINT vk; + }; + static Qt_VK_Keymap qt_vk_table[]; + + static bool convertKeySequence(const QKeySequence &ks, UINT *mod_, UINT *key_) + { + int code = ks[0]; + + UINT mod = 0; + if (code & Qt::META) + mod |= MOD_WIN; + if (code & Qt::SHIFT) + mod |= MOD_SHIFT; + if (code & Qt::CTRL) + mod |= MOD_CONTROL; + if (code & Qt::ALT) + mod |= MOD_ALT; + + UINT key = 0; + code &= ~Qt::KeyboardModifierMask; + if (code >= 0x20 && code <= 0x7f) + key = code; + else { + for (int n = 0; qt_vk_table[n].key != Qt::Key_unknown; ++n) { + if (qt_vk_table[n].key == code) { + key = qt_vk_table[n].vk; + break; + } + } + if (!key) + return false; + } + + if (mod) + *mod_ = mod; + if (key) + *key_ = key; + + return true; + } +}; + +GlobalShortcutManager::KeyTrigger::Impl::Qt_VK_Keymap GlobalShortcutManager::KeyTrigger::Impl::qt_vk_table[] = { + { Qt::Key_Escape, VK_ESCAPE }, + { Qt::Key_Tab, VK_TAB }, + { Qt::Key_Backtab, 0 }, + { Qt::Key_Backspace, VK_BACK }, + { Qt::Key_Return, VK_RETURN }, + { Qt::Key_Enter, VK_RETURN }, + { Qt::Key_Insert, VK_INSERT }, + { Qt::Key_Delete, VK_DELETE }, + { Qt::Key_Pause, VK_PAUSE }, + { Qt::Key_Print, VK_PRINT }, + { Qt::Key_SysReq, 0 }, + { Qt::Key_Clear, VK_CLEAR }, + { Qt::Key_Home, VK_HOME }, + { Qt::Key_End, VK_END }, + { Qt::Key_Left, VK_LEFT }, + { Qt::Key_Up, VK_UP }, + { Qt::Key_Right, VK_RIGHT }, + { Qt::Key_Down, VK_DOWN }, + { Qt::Key_PageUp, VK_PRIOR }, + { Qt::Key_PageDown, VK_NEXT }, + { Qt::Key_Shift, VK_SHIFT }, + { Qt::Key_Control, VK_CONTROL }, + { Qt::Key_Meta, VK_LWIN }, + { Qt::Key_Alt, VK_MENU }, + { Qt::Key_CapsLock, VK_CAPITAL }, + { Qt::Key_NumLock, VK_NUMLOCK }, + { Qt::Key_ScrollLock, VK_SCROLL }, + { Qt::Key_F1, VK_F1 }, + { Qt::Key_F2, VK_F2 }, + { Qt::Key_F3, VK_F3 }, + { Qt::Key_F4, VK_F4 }, + { Qt::Key_F5, VK_F5 }, + { Qt::Key_F6, VK_F6 }, + { Qt::Key_F7, VK_F7 }, + { Qt::Key_F8, VK_F8 }, + { Qt::Key_F9, VK_F9 }, + { Qt::Key_F10, VK_F10 }, + { Qt::Key_F11, VK_F11 }, + { Qt::Key_F12, VK_F12 }, + { Qt::Key_F13, VK_F13 }, + { Qt::Key_F14, VK_F14 }, + { Qt::Key_F15, VK_F15 }, + { Qt::Key_F16, VK_F16 }, + { Qt::Key_F17, VK_F17 }, + { Qt::Key_F18, VK_F18 }, + { Qt::Key_F19, VK_F19 }, + { Qt::Key_F20, VK_F20 }, + { Qt::Key_F21, VK_F21 }, + { Qt::Key_F22, VK_F22 }, + { Qt::Key_F23, VK_F23 }, + { Qt::Key_F24, VK_F24 }, + { Qt::Key_F25, 0 }, + { Qt::Key_F26, 0 }, + { Qt::Key_F27, 0 }, + { Qt::Key_F28, 0 }, + { Qt::Key_F29, 0 }, + { Qt::Key_F30, 0 }, + { Qt::Key_F31, 0 }, + { Qt::Key_F32, 0 }, + { Qt::Key_F33, 0 }, + { Qt::Key_F34, 0 }, + { Qt::Key_F35, 0 }, + { Qt::Key_Super_L, 0 }, + { Qt::Key_Super_R, 0 }, + { Qt::Key_Menu, 0 }, + { Qt::Key_Hyper_L, 0 }, + { Qt::Key_Hyper_R, 0 }, + { Qt::Key_Help, 0 }, + { Qt::Key_Direction_L, 0 }, + { Qt::Key_Direction_R, 0 }, + + { Qt::Key_unknown, 0 }, +}; + +WPARAM GlobalShortcutManager::KeyTrigger::Impl::nextId = 1; + +GlobalShortcutManager::KeyTrigger::KeyTrigger(const QKeySequence &key) { d = new Impl(this, key); } + +GlobalShortcutManager::KeyTrigger::~KeyTrigger() +{ + delete d; + d = nullptr; +} diff --git a/src/libpsi/tools/globalshortcut/globalshortcutmanager_x11.cpp b/src/libpsi/tools/globalshortcut/globalshortcutmanager_x11.cpp new file mode 100644 index 000000000..5534a0d13 --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcutmanager_x11.cpp @@ -0,0 +1,486 @@ +/* + * globalshortcutmanager_x11.cpp - X11 implementation of global shortcuts + * Copyright (C) 2003-2007 Justin Karneges, Michail Pishchagin + * + * 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 2 + * 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 . + * + */ + +#include "globalshortcutmanager.h" +#include "globalshortcuttrigger.h" + +#include +#include +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#endif +#include +#include +#include + +#ifdef KeyPress +// defined by X11 headers +const int XKeyPress = KeyPress; +const int XKeyRelease = KeyRelease; +#undef KeyPress +#endif + +namespace { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +unsigned long getRootWindow() +{ + auto x11app = qApp->nativeInterface(); + if (!x11app) { + return -1; // wayland? + } + return DefaultRootWindow(x11app->display()); + // auto screen = xcb_setup_roots_iterator ( xcb_get_setup (x11app->connection()) ).data; + // auto gc = xcb_generate_id ( x11app->connection() ); + + // /* root window */ + // auto draw = screen->root; +} +#endif +} + +class X11KeyTrigger { +public: + virtual ~X11KeyTrigger() { } + virtual void activate() = 0; + virtual bool isAccepted(const QKeySequence &qkey) const = 0; +}; + +class X11KeyTriggerManager : public QObject { +public: + static X11KeyTriggerManager *instance() + { + if (!instance_) + instance_ = new X11KeyTriggerManager(); + return instance_; + } + + void addTrigger(X11KeyTrigger *trigger) { triggers_ << trigger; } + + void removeTrigger(X11KeyTrigger *trigger) { triggers_.removeAll(trigger); } + + struct Qt_XK_Keygroup { + char num; + int sym[3]; + }; + +protected: + // reimplemented + bool eventFilter(QObject *o, QEvent *e) + { + if (e->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast(e); + int qkey = k->key(); + if (k->modifiers() & Qt::ShiftModifier) + qkey |= Qt::SHIFT; + if (k->modifiers() & Qt::ControlModifier) + qkey |= Qt::CTRL; + if (k->modifiers() & Qt::AltModifier) + qkey |= Qt::ALT; + if (k->modifiers() & Qt::MetaModifier) + qkey |= Qt::META; + + for (X11KeyTrigger *trigger : std::as_const(triggers_)) { + if (trigger->isAccepted(QKeySequence(qkey))) { + trigger->activate(); + return true; + } + } + } + + return QObject::eventFilter(o, e); + } + +private: + X11KeyTriggerManager() : QObject(QCoreApplication::instance()) + { + QCoreApplication::instance()->installEventFilter(this); + } + + static X11KeyTriggerManager *instance_; + QList triggers_; + +private: + struct Qt_XK_Keymap { + int key; + Qt_XK_Keygroup xk; + }; + + static Qt_XK_Keymap qt_xk_table[]; + static long alt_mask; + static long meta_mask; + static long super_mask; + static long hyper_mask; + static long numlock_mask; + static bool haveMods; + + // adapted from qapplication_x11.cpp + static void ensureModifiers() + { + if (haveMods) + return; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (!QX11Info::isPlatformX11()) { + return; // wayland? + } + + Display *appDpy = QX11Info::display(); +#else + auto x11app = qApp->nativeInterface(); + if (!x11app) { + return; // wayland? + } + Display *appDpy = x11app->display(); +#endif +#if !defined(LIMIT_X11_USAGE) + XModifierKeymap *map = XGetModifierMapping(appDpy); +#endif + if (map) { + // XKeycodeToKeysym helper code adapeted from xmodmap + int min_keycode, max_keycode, keysyms_per_keycode_return, keysyms_per_keycode = 1; + XDisplayKeycodes(appDpy, &min_keycode, &max_keycode); + XFree(XGetKeyboardMapping(appDpy, min_keycode, (max_keycode - min_keycode + 1), &keysyms_per_keycode)); + + int i, maskIndex = 0, mapIndex = 0; + for (maskIndex = 0; maskIndex < 8; maskIndex++) { + for (i = 0; i < map->max_keypermod; i++) { + if (map->modifiermap[mapIndex]) { + KeySym *sym = nullptr; + int symIndex = 0; + do { + if (sym) + XFree(sym); + sym = XGetKeyboardMapping(appDpy, map->modifiermap[mapIndex], 1, + &keysyms_per_keycode_return); + symIndex++; + } while (!sym[0] && symIndex < keysyms_per_keycode); + if (alt_mask == 0 && (sym[0] == XK_Alt_L || sym[0] == XK_Alt_R)) { + alt_mask = 1 << maskIndex; + } + if (meta_mask == 0 && (sym[0] == XK_Meta_L || sym[0] == XK_Meta_R)) { + meta_mask = 1 << maskIndex; + } + if (super_mask == 0 && (sym[0] == XK_Super_L || sym[0] == XK_Super_R)) { + super_mask = 1 << maskIndex; + } + if (hyper_mask == 0 && (sym[0] == XK_Hyper_L || sym[0] == XK_Hyper_R)) { + hyper_mask = 1 << maskIndex; + } + if (numlock_mask == 0 && (sym[0] == XK_Num_Lock)) { + numlock_mask = 1 << maskIndex; + } + XFree(sym); + } + mapIndex++; + } + } + + XFreeModifiermap(map); + + // logic from qt source see gui/kernel/qkeymapper_x11.cpp + if (meta_mask == 0 || meta_mask == alt_mask) { + // no meta keys... s,meta,super, + meta_mask = super_mask; + if (meta_mask == 0 || meta_mask == alt_mask) { + // no super keys either? guess we'll use hyper then + meta_mask = hyper_mask; + } + } + } else { + // assume defaults + alt_mask = Mod1Mask; + meta_mask = Mod4Mask; + } + + haveMods = true; + } + +public: + static bool convertKeySequence(const QKeySequence &ks, unsigned int *_mod, Qt_XK_Keygroup *_kg) + { + int code = ks[0]; + Qt_XK_Keygroup kg; + kg.num = 0; + kg.sym[0] = 0; + + ensureModifiers(); + + unsigned int mod = 0; + if (code & Qt::META) + mod |= meta_mask; + if (code & Qt::SHIFT) + mod |= ShiftMask; + if (code & Qt::CTRL) + mod |= ControlMask; + if (code & Qt::ALT) + mod |= alt_mask; + + code &= ~Qt::KeyboardModifierMask; + + bool found = false; + for (int n = 0; qt_xk_table[n].key != Qt::Key_unknown; ++n) { + if (qt_xk_table[n].key == code) { + kg = qt_xk_table[n].xk; + found = true; + break; + } + } + + if (!found) { + // try latin1 + if (code >= 0x20 && code <= 0x7f) { + kg.num = 1; + kg.sym[0] = code; + } + } + + if (!kg.num) + return false; + + if (_mod) + *_mod = mod; + if (_kg) + *_kg = kg; + + return true; + } + + static QList ignModifiersList() + { + QList ret; + if (numlock_mask) { + ret << 0 << LockMask << numlock_mask << (LockMask | numlock_mask); + } else { + ret << 0 << LockMask; + } + return ret; + } +}; + +X11KeyTriggerManager *X11KeyTriggerManager::instance_ = nullptr; + +class GlobalShortcutManager::KeyTrigger::Impl : public X11KeyTrigger { +private: + KeyTrigger *trigger_; + QKeySequence qkey_; + + struct GrabbedKey { + int code; + uint mod; + }; + QList grabbedKeys_; + + static bool failed; + static int XGrabErrorHandler(Display *, XErrorEvent *e) + { + char answ[256]; + XGetErrorText(e->display, e->error_code, answ, sizeof(answ)); + qDebug("failed to grab key, Code: %d, %s", e->error_code, answ); + failed = true; + return 0; + } + + void bind(int keysym, unsigned int mod) + { +#if defined(LIMIT_X11_USAGE) + return; +#endif +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (!QX11Info::isPlatformX11()) { + return; // wayland? + } + + Display *appDpy = QX11Info::display(); + auto rootWindow = QX11Info::appRootWindow(); +#else + auto x11app = qApp->nativeInterface(); + if (!x11app) { + return; // wayland? + } + Display *appDpy = x11app->display(); + auto rootWindow = getRootWindow(); +#endif + + int code = XKeysymToKeycode(appDpy, keysym); + + // don't grab keys with empty code (because it means just the modifier key) + if (keysym && !code) + return; + + failed = false; + XErrorHandler savedErrorHandler = XSetErrorHandler(XGrabErrorHandler); + const auto &modifiers = X11KeyTriggerManager::ignModifiersList(); + for (long mask_mod : modifiers) { + XGrabKey(appDpy, code, mod | mask_mod, rootWindow, False, GrabModeAsync, GrabModeAsync); + GrabbedKey grabbedKey; + grabbedKey.code = code; + grabbedKey.mod = mod | mask_mod; + grabbedKeys_ << grabbedKey; + } + XSync(appDpy, False); + XSetErrorHandler(savedErrorHandler); + } + +public: + /** + * Constructor registers the hotkey. + */ + Impl(GlobalShortcutManager::KeyTrigger *t, const QKeySequence &ks) : trigger_(t), qkey_(ks) + { + X11KeyTriggerManager::instance()->addTrigger(this); + + X11KeyTriggerManager::Qt_XK_Keygroup kg; + unsigned int mod; + if (X11KeyTriggerManager::convertKeySequence(ks, &mod, &kg)) + for (int n = 0; n < kg.num; ++n) + bind(kg.sym[n], mod); + } + + /** + * Destructor unregisters the hotkey. + */ + ~Impl() + { + X11KeyTriggerManager::instance()->removeTrigger(this); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (!QX11Info::isPlatformX11()) { + return; // wayland? + } + + Display *appDpy = QX11Info::display(); + auto rootWindow = QX11Info::appRootWindow(); +#else + auto x11app = qApp->nativeInterface(); + if (!x11app) { + return; // wayland? + } + Display *appDpy = x11app->display(); + auto rootWindow = getRootWindow(); +#endif + + for (GrabbedKey key : std::as_const(grabbedKeys_)) + XUngrabKey(appDpy, key.code, key.mod, rootWindow); + } + + void activate() { emit trigger_->triggered(); } + + bool isAccepted(const QKeySequence &qkey) const { return qkey_ == qkey; } +}; + +bool GlobalShortcutManager::KeyTrigger::Impl::failed; +long X11KeyTriggerManager::alt_mask = 0; +long X11KeyTriggerManager::meta_mask = 0; +long X11KeyTriggerManager::super_mask = 0; +long X11KeyTriggerManager::hyper_mask = 0; +long X11KeyTriggerManager::numlock_mask = 0; +bool X11KeyTriggerManager::haveMods = false; + +X11KeyTriggerManager::Qt_XK_Keymap X11KeyTriggerManager::qt_xk_table[] = { + { Qt::Key_Escape, { 1, { XK_Escape } } }, + { Qt::Key_Tab, { 2, { XK_Tab, XK_KP_Tab } } }, + { Qt::Key_Backtab, { 1, { XK_ISO_Left_Tab } } }, + { Qt::Key_Backspace, { 1, { XK_BackSpace } } }, + { Qt::Key_Return, { 1, { XK_Return } } }, + { Qt::Key_Enter, { 1, { XK_KP_Enter } } }, + { Qt::Key_Insert, { 2, { XK_Insert, XK_KP_Insert } } }, + { Qt::Key_Delete, { 3, { XK_Delete, XK_KP_Delete, XK_Clear } } }, + { Qt::Key_Pause, { 1, { XK_Pause } } }, + { Qt::Key_Print, { 1, { XK_Print } } }, + { Qt::Key_SysReq, { 1, { XK_Sys_Req } } }, + { Qt::Key_Clear, { 1, { XK_KP_Begin } } }, + { Qt::Key_Home, { 2, { XK_Home, XK_KP_Home } } }, + { Qt::Key_End, { 2, { XK_End, XK_KP_End } } }, + { Qt::Key_Left, { 2, { XK_Left, XK_KP_Left } } }, + { Qt::Key_Up, { 2, { XK_Up, XK_KP_Up } } }, + { Qt::Key_Right, { 2, { XK_Right, XK_KP_Right } } }, + { Qt::Key_Down, { 2, { XK_Down, XK_KP_Down } } }, + { Qt::Key_PageUp, { 2, { XK_Prior, XK_KP_Prior } } }, + { Qt::Key_PageDown, { 2, { XK_Next, XK_KP_Next } } }, + { Qt::Key_Shift, { 3, { XK_Shift_L, XK_Shift_R, XK_Shift_Lock } } }, + { Qt::Key_Control, { 2, { XK_Control_L, XK_Control_R } } }, + { Qt::Key_Meta, { 2, { XK_Meta_L, XK_Meta_R } } }, + { Qt::Key_Alt, { 2, { XK_Alt_L, XK_Alt_R } } }, + { Qt::Key_CapsLock, { 1, { XK_Caps_Lock } } }, + { Qt::Key_NumLock, { 1, { XK_Num_Lock } } }, + { Qt::Key_ScrollLock, { 1, { XK_Scroll_Lock } } }, + { Qt::Key_Space, { 2, { XK_space, XK_KP_Space } } }, + { Qt::Key_Equal, { 2, { XK_equal, XK_KP_Equal } } }, + { Qt::Key_Asterisk, { 2, { XK_asterisk, XK_KP_Multiply } } }, + { Qt::Key_Plus, { 2, { XK_plus, XK_KP_Add } } }, + { Qt::Key_Comma, { 2, { XK_comma, XK_KP_Separator } } }, + { Qt::Key_Minus, { 2, { XK_minus, XK_KP_Subtract } } }, + { Qt::Key_Period, { 2, { XK_period, XK_KP_Decimal } } }, + { Qt::Key_Slash, { 2, { XK_slash, XK_KP_Divide } } }, + { Qt::Key_F1, { 1, { XK_F1 } } }, + { Qt::Key_F2, { 1, { XK_F2 } } }, + { Qt::Key_F3, { 1, { XK_F3 } } }, + { Qt::Key_F4, { 1, { XK_F4 } } }, + { Qt::Key_F5, { 1, { XK_F5 } } }, + { Qt::Key_F6, { 1, { XK_F6 } } }, + { Qt::Key_F7, { 1, { XK_F7 } } }, + { Qt::Key_F8, { 1, { XK_F8 } } }, + { Qt::Key_F9, { 1, { XK_F9 } } }, + { Qt::Key_F10, { 1, { XK_F10 } } }, + { Qt::Key_F11, { 1, { XK_F11 } } }, + { Qt::Key_F12, { 1, { XK_F12 } } }, + { Qt::Key_F13, { 1, { XK_F13 } } }, + { Qt::Key_F14, { 1, { XK_F14 } } }, + { Qt::Key_F15, { 1, { XK_F15 } } }, + { Qt::Key_F16, { 1, { XK_F16 } } }, + { Qt::Key_F17, { 1, { XK_F17 } } }, + { Qt::Key_F18, { 1, { XK_F18 } } }, + { Qt::Key_F19, { 1, { XK_F19 } } }, + { Qt::Key_F20, { 1, { XK_F20 } } }, + { Qt::Key_F21, { 1, { XK_F21 } } }, + { Qt::Key_F22, { 1, { XK_F22 } } }, + { Qt::Key_F23, { 1, { XK_F23 } } }, + { Qt::Key_F24, { 1, { XK_F24 } } }, + { Qt::Key_F25, { 1, { XK_F25 } } }, + { Qt::Key_F26, { 1, { XK_F26 } } }, + { Qt::Key_F27, { 1, { XK_F27 } } }, + { Qt::Key_F28, { 1, { XK_F28 } } }, + { Qt::Key_F29, { 1, { XK_F29 } } }, + { Qt::Key_F30, { 1, { XK_F30 } } }, + { Qt::Key_F31, { 1, { XK_F31 } } }, + { Qt::Key_F32, { 1, { XK_F32 } } }, + { Qt::Key_F33, { 1, { XK_F33 } } }, + { Qt::Key_F34, { 1, { XK_F34 } } }, + { Qt::Key_F35, { 1, { XK_F35 } } }, + { Qt::Key_Super_L, { 1, { XK_Super_L } } }, + { Qt::Key_Super_R, { 1, { XK_Super_R } } }, + { Qt::Key_Menu, { 1, { XK_Menu } } }, + { Qt::Key_Hyper_L, { 1, { XK_Hyper_L } } }, + { Qt::Key_Hyper_R, { 1, { XK_Hyper_R } } }, + { Qt::Key_Help, { 1, { XK_Help } } }, + { Qt::Key_Direction_L, { 0, { 0 } } }, + { Qt::Key_Direction_R, { 0, { 0 } } }, + + { Qt::Key_unknown, { 0, { 0 } } }, +}; + +GlobalShortcutManager::KeyTrigger::KeyTrigger(const QKeySequence &key) { d = new Impl(this, key); } + +GlobalShortcutManager::KeyTrigger::~KeyTrigger() +{ + delete d; + d = nullptr; +} diff --git a/src/libpsi/tools/globalshortcut/globalshortcuttrigger.h b/src/libpsi/tools/globalshortcut/globalshortcuttrigger.h new file mode 100644 index 000000000..d6a20e200 --- /dev/null +++ b/src/libpsi/tools/globalshortcut/globalshortcuttrigger.h @@ -0,0 +1,57 @@ +/* + * globalshortcuttrigger.h - Helper class activating global shortcut + * Copyright (C) 2006 Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#ifndef GLOBALSHORTCUTTRIGGER_H +#define GLOBALSHORTCUTTRIGGER_H + +#include "globalshortcutmanager.h" + +#include + +class GlobalShortcutManager::KeyTrigger : public QObject { + Q_OBJECT +public: + /** + * Is there any slot connected to this hotkey? + */ + bool isUsed() const { return QObject::receivers(SIGNAL(triggered())) > 0; } + +signals: + void triggered(); + +private: + /** + * Registers the \a key. + */ + KeyTrigger(const QKeySequence &key); + /** + * Unregisters the key. + */ + ~KeyTrigger(); + + friend class GlobalShortcutManager; + + /** + * Platform-specific helper + */ + class Impl; + Impl *d; +}; + +#endif // GLOBALSHORTCUTTRIGGER_H diff --git a/src/libpsi/tools/growlnotifier/ChangeLog b/src/libpsi/tools/growlnotifier/ChangeLog new file mode 100644 index 000000000..c49e07e9e --- /dev/null +++ b/src/libpsi/tools/growlnotifier/ChangeLog @@ -0,0 +1,9 @@ +2005-05-05, Remko Troncon + * Created a more extensive example + * Added timeout signal (new in Growl 0.7) + +2005-04-15, Remko Troncon + * Implemented the click notification + +2005-04-11, Remko Troncon + * Start diff --git a/src/libpsi/tools/growlnotifier/growlnotifier.cpp b/src/libpsi/tools/growlnotifier/growlnotifier.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/libpsi/tools/growlnotifier/growlnotifier.h b/src/libpsi/tools/growlnotifier/growlnotifier.h new file mode 100644 index 000000000..72e1c00a3 --- /dev/null +++ b/src/libpsi/tools/growlnotifier/growlnotifier.h @@ -0,0 +1,56 @@ +/* + * growlnotifier.h - A simple Qt interface to Growl + * + * Copyright (C) 2005 Remko Troncon + * Copyright (C) 2012 Evgeny Khryukin + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#ifndef GROWLNOTIFIER_H +#define GROWLNOTIFIER_H + +#include +#include + +class GrowlNotifierSignaler; + +/** + * \brief A simple interface to Growl. + */ +class GrowlNotifier { +public: + GrowlNotifier(const QStringList ¬ifications, const QStringList &default_notifications, + const QString &app_name = ""); + + virtual ~GrowlNotifier(); + + void notify(const QString &name, const QString &title, const QString &description, const QPixmap &icon = QPixmap(), + bool sticky = false, const QObject *receiver = 0, const char *clicked_slot = 0, + const char *timeout_slot = 0, void *context = 0); + + static bool isRunning(); + +private: + class Private; + Private *d; +}; + +#endif // GROWLNOTIFIER_H diff --git a/src/libpsi/tools/growlnotifier/growlnotifier.mm b/src/libpsi/tools/growlnotifier/growlnotifier.mm new file mode 100644 index 000000000..4def1e6ed --- /dev/null +++ b/src/libpsi/tools/growlnotifier/growlnotifier.mm @@ -0,0 +1,250 @@ +/* + * growlnotifier.mm - A simple Qt interface to Growl + * + * Copyright (C) 2005 Remko Troncon + * Copyright (C) 2012 Evgeny Khryukin + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "growlnotifier.h" + +#include +#include +#include +#include + +//------------------------------------------------------------------------------ + +/** + * \brief A class for emitting a clicked signal to the interested party. + */ +class GrowlNotifierSignaler : public QObject { + Q_OBJECT +public: + GrowlNotifierSignaler() : QObject() { } + ~GrowlNotifierSignaler() { } + + void emitNotification(void *context) { emit notification(context); } + +signals: + void notification(void *); +}; + +NSString *qString2NSString(const QString &str) { return [NSString stringWithUTF8String:str.toUtf8().constData()]; } + +NSArray *qStringList2NSArray(const QStringList &list) +{ + int cnt = list.count(); + if (!cnt) + return nil; + + NSString *strings[cnt]; + for (int i = 0; i < cnt; i++) { + strings[i] = qString2NSString(list.at(i)); + } + return [NSArray arrayWithObjects:strings count:cnt]; +} + +NSData *qPixmap2NSData(const QPixmap &p) +{ + if (p.isNull()) { + return nil; + } + + QByteArray img_data; + QBuffer buffer(&img_data); + buffer.open(QIODevice::WriteOnly); + p.save(&buffer, "PNG"); + return [NSData dataWithBytes:img_data.constData() length:img_data.size()]; +} + +@interface GrowlController : NSObject { +@private + NSArray *allNotifications; + NSArray *defaultNotifications; + NSString *appName; +} +- (void)setAllNotifications:(NSArray *)all setDefaultNotifications:(NSArray *)def setName:(NSString *)name; +@end + +@implementation GrowlController + +- (id)init +{ + self = [super init]; + if (self) { + allNotifications = nil; + defaultNotifications = nil; + appName = nil; + } + return self; +} + +- (void)dealloc +{ + if (allNotifications) + [allNotifications release]; + if (defaultNotifications) + [defaultNotifications release]; + if (appName) + [appName release]; + [super dealloc]; +} + +- (void)setAllNotifications:(NSArray *)all setDefaultNotifications:(NSArray *)def setName:(NSString *)name +{ + allNotifications = all; + defaultNotifications = def; + appName = name; + if (allNotifications) + [allNotifications retain]; + if (defaultNotifications) + [defaultNotifications retain]; + if (appName) + [appName retain]; +} + +- (NSDictionary *)registrationDictionaryForGrowl +{ + + NSDictionary *dict = + [NSDictionary dictionaryWithObjectsAndKeys:allNotifications, GROWL_NOTIFICATIONS_ALL, defaultNotifications, + GROWL_NOTIFICATIONS_DEFAULT, nil]; + return dict; +} + +- (NSString *)applicatioNameForGrowl +{ + return appName; +} + +- (NSData *)applicationIconDataForGrowl +{ + return nil; +} + +- (void)growlNotificationWasClicked:(id)clickContext +{ + if (!clickContext) + return; + + GrowlNotifierSignaler *signaler = *(GrowlNotifierSignaler **)([[clickContext objectAtIndex:0] bytes]); + const QObject *receiver = *(const QObject **)([[clickContext objectAtIndex:1] bytes]); + const char *slot = *(const char **)([[clickContext objectAtIndex:3] bytes]); + void *qcontext = *(void **)([[clickContext objectAtIndex:2] bytes]); + + QObject::connect(signaler, SIGNAL(notification(void *)), receiver, slot); + signaler->emitNotification(qcontext); + QObject::disconnect(signaler, SIGNAL(notification(void *)), receiver, slot); +} + +- (void)growlNotificationTimedOut:(id)clickContext +{ + if (!clickContext) + return; + + GrowlNotifierSignaler *signaler = *(GrowlNotifierSignaler **)([[clickContext objectAtIndex:0] bytes]); + const QObject *receiver = *(const QObject **)([[clickContext objectAtIndex:1] bytes]); + const char *slot = *(const char **)([[clickContext objectAtIndex:4] bytes]); + void *qcontext = *(void **)([[clickContext objectAtIndex:2] bytes]); + + QObject::connect(signaler, SIGNAL(notification(void *)), receiver, slot); + signaler->emitNotification(qcontext); + QObject::disconnect(signaler, SIGNAL(notification(void *)), receiver, slot); +} + +@end + +//------------------------------------------------------------------------------ +class GrowlNotifier::Private : public GrowlNotifierSignaler { +public: + Private() : GrowlNotifierSignaler() { } + ~Private() { } + + GrowlController *controller_; +}; + +/** + * Constructs a GrowlNotifier. + * + * \param notifications the list names of all notifications that can be sent + * by this notifier. + * \param default_notifications the list of names of the notifications that + * should be enabled by default. + * \param app the name of the application under which the notifier should + * register with growl. + */ +GrowlNotifier::GrowlNotifier(const QStringList ¬ifications, const QStringList &default_notifications, + const QString &app) +{ + d = new Private; + d->controller_ = [[GrowlController alloc] init]; + [d->controller_ setAllNotifications:qStringList2NSArray(notifications) + setDefaultNotifications:qStringList2NSArray(default_notifications) + setName:qString2NSString(app)]; + + [GrowlApplicationBridge setGrowlDelegate:d->controller_]; +} + +GrowlNotifier::~GrowlNotifier() +{ + [d->controller_ release]; + delete d; +} + +/** + * \brief Sends a notification to Growl. + * + * \param name the registered name of the notification. + * \param title the title for the notification. + * \param description the description of the notification. + * \param icon the icon of the notification. + * \param sticky whether the notification should be sticky (i.e. require a + * click to discard. + * \param receiver the receiving object which will be signaled when the + * notification is clicked. May be NULL. + * \param slot the slot to be signaled when the notification is clicked. + * \param context the context which will be passed back to the slot + * May be NULL. + */ +void GrowlNotifier::notify(const QString &name, const QString &title, const QString &description, const QPixmap &p, + bool sticky, const QObject *receiver, const char *clicked_slot, const char *timeout_slot, + void *qcontext) +{ + NSArray *con = [NSArray arrayWithObjects:[NSData dataWithBytes:&d length:sizeof(GrowlNotifierSignaler *)], + [NSData dataWithBytes:&receiver length:sizeof(const QObject *)], + [NSData dataWithBytes:&qcontext length:sizeof(void *)], + [NSData dataWithBytes:&clicked_slot length:sizeof(const char *)], + [NSData dataWithBytes:&timeout_slot length:sizeof(const char *)], nil]; + [GrowlApplicationBridge notifyWithTitle:qString2NSString(title) + description:qString2NSString(description) + notificationName:qString2NSString(name) + iconData:qPixmap2NSData(p) + priority:0 + isSticky:sticky ? YES : NO + clickContext:con]; +} + +bool GrowlNotifier::isRunning() { return [GrowlApplicationBridge isGrowlRunning]; } + +//----------------------------------------------------------------------------- + +#include "growlnotifier.moc" diff --git a/src/libpsi/tools/growlnotifier/growltest.cpp b/src/libpsi/tools/growlnotifier/growltest.cpp new file mode 100644 index 000000000..f715c1d55 --- /dev/null +++ b/src/libpsi/tools/growlnotifier/growltest.cpp @@ -0,0 +1,130 @@ +/* + * growltest.cpp: A test program for the GrowlNotifier class + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "growlnotifier.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class GrowlTestWidget : public QWidget { + Q_OBJECT + +public: + GrowlTestWidget(QWidget *parent = 0); + +public slots: + void do_notification1(); + void do_notification2(); + void notification_clicked(); + +private: + QLineEdit *text, *title; + QCheckBox *sticky; + GrowlNotifier *growlNotifier; +}; + +GrowlTestWidget::GrowlTestWidget(QWidget *parent) : QWidget(parent) +{ + // Initialize widgets + QGridLayout *layout = new QGridLayout(this); + + layout->addWidget(new QLabel("Title", this), 0, 0); + title = new QLineEdit(this); + title->setText("My Text"); + layout->addWidget(title, 0, 1); + + layout->addWidget(new QLabel("Text", this), 1, 0); + text = new QLineEdit(this); + text->setText("My Description"); + layout->addWidget(text, 1, 1); + + // layout->addWidget(new QLabel("Sticky",this),2,0); + // sticky = new QCheckBox(this); + // sticky->setTristate(); + // layout->addWidget(sticky,2,1); + + QPushButton *notification1 = new QPushButton("Notification 1", this); + connect(notification1, SIGNAL(clicked()), SLOT(do_notification1())); + layout->addWidget(notification1, 3, 0); + + QPushButton *notification2 = new QPushButton("Notification 2", this); + connect(notification2, SIGNAL(clicked()), SLOT(do_notification2())); + layout->addWidget(notification2, 3, 1); + + // Initialize GrowlNotifier + QStringList nots, defaults; + nots << "Notification 1" << "Notification 2"; + defaults << "Notification 1"; + growlNotifier = new GrowlNotifier(nots, defaults, "GrowlNotifierTest"); +} + +int main(int argc, char **argv) +{ + QApplication a(argc, argv); + GrowlTestWidget w; + w.show(); + return a.exec(); +} + +void GrowlTestWidget::do_notification1() +{ + // if (sticky->state() != QButton::NoChange) { + // growlNotifier->notify("Notification 1", title->text(), text->text(), QPixmap(), sticky->isChecked(), this, + // SLOT(notification_clicked())); + //} + // else { + // growlNotifier->notify("Notification 1", title->text(), text->text(), QPixmap()); + //} + growlNotifier->notify("Notification 1", title->text(), text->text(), QPixmap(), false, this, + SLOT(notification_clicked())); +} + +void GrowlTestWidget::do_notification2() +{ + // if (sticky->state() != QButton::NoChange) { + // growlNotifier->notify("Notification 2", title->text(), text->text(), QPixmap(), sticky->isChecked(), this, + // SLOT(notification_clicked())); + //} + // else { + // growlNotifier->notify("Notification 2", title->text(), text->text(), QPixmap()); + //} + growlNotifier->notify("Notification 2", title->text(), text->text(), QPixmap(), false, this, + SLOT(notification_clicked())); +} + +void GrowlTestWidget::notification_clicked() +{ + QMessageBox::information(0, "Information", "Notification was clicked\n"); +} + +#include "growltest.moc" diff --git a/src/libpsi/tools/idle/idle.cpp b/src/libpsi/tools/idle/idle.cpp new file mode 100644 index 000000000..dbe37dc02 --- /dev/null +++ b/src/libpsi/tools/idle/idle.cpp @@ -0,0 +1,127 @@ +/* + * idle.cpp - detect desktop idle time + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "idle.h" + +#include +#include +#include + +static IdlePlatform *platform = nullptr; +static int platform_ref = 0; + +class Idle::Private { +public: + Private() = default; + + QPoint lastMousePos; + QDateTime idleSince; + + bool active = false; + int idleTime = 0; + QDateTime startTime; + QTimer checkTimer; +}; + +Idle::Idle() +{ + d = new Private; + + // try to use platform idle + if (!platform) { + IdlePlatform *p = new IdlePlatform; + if (p->init()) + platform = p; + else + delete p; + } + if (platform) + ++platform_ref; + + connect(&d->checkTimer, SIGNAL(timeout()), SLOT(doCheck())); +} + +Idle::~Idle() +{ + if (platform) { + --platform_ref; + if (platform_ref == 0) { + delete platform; + platform = nullptr; + } + } + delete d; +} + +bool Idle::isActive() const { return d->active; } + +bool Idle::usingPlatform() const { return (platform ? true : false); } + +void Idle::start() +{ + d->startTime = QDateTime::currentDateTime(); + + if (!platform) { + // generic idle + d->lastMousePos = QCursor::pos(); + d->idleSince = QDateTime::currentDateTime(); + } + + // poll every 5 seconds (use a lower value if you need more accuracy) + d->checkTimer.start(5000); +} + +void Idle::stop() { d->checkTimer.stop(); } + +void Idle::doCheck() +{ + int i; + if (platform) + i = platform->secondsIdle(); + else { + QPoint curMousePos = QCursor::pos(); + QDateTime curDateTime = QDateTime::currentDateTime(); + if (d->lastMousePos != curMousePos) { + d->lastMousePos = curMousePos; + d->idleSince = curDateTime; + } + i = d->idleSince.secsTo(curDateTime); + } + + // set 'beginIdle' to the beginning of the idle time (by backtracking 'i' seconds from now) + QDateTime beginIdle = QDateTime::currentDateTime().addSecs(-i); + + // set 't' to hold the number of seconds between 'beginIdle' and 'startTime' + int t = beginIdle.secsTo(d->startTime); + + // beginIdle later than (or equal to) startTime? + if (t <= 0) { + // scoot ourselves up to the new idle start + d->startTime = beginIdle; + } + // beginIdle earlier than startTime? + else if (t > 0) { + // do nothing + } + + // how long have we been idle? + int idleTime = d->startTime.secsTo(QDateTime::currentDateTime()); + + emit secondsIdle(idleTime); +} diff --git a/src/libpsi/tools/idle/idle.h b/src/libpsi/tools/idle/idle.h new file mode 100644 index 000000000..0cbe4f943 --- /dev/null +++ b/src/libpsi/tools/idle/idle.h @@ -0,0 +1,62 @@ +/* + * idle.h - detect desktop idle time + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef IDLE_H +#define IDLE_H + +#include + +class IdlePlatform; + +class Idle : public QObject { + Q_OBJECT +public: + Idle(); + ~Idle(); + + bool isActive() const; + bool usingPlatform() const; + void start(); + void stop(); + +signals: + void secondsIdle(int); + +private slots: + void doCheck(); + +private: + class Private; + Private *d; +}; + +class IdlePlatform { +public: + IdlePlatform(); + ~IdlePlatform(); + + bool init(); + int secondsIdle(); + +private: + class Private; + Private *d; +}; + +#endif // IDLE_H diff --git a/src/libpsi/tools/idle/idle_mac.cpp b/src/libpsi/tools/idle/idle_mac.cpp new file mode 100644 index 000000000..6e4ee483b --- /dev/null +++ b/src/libpsi/tools/idle/idle_mac.cpp @@ -0,0 +1,154 @@ +/* + * idle_mac.cpp - detect desktop idle time + * Copyright (C) 2003 Tarkvara Design Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "idle.h" + +#include + +// Why does Apple have to make this so complicated? +static OSStatus LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr) +{ + OSStatus err; + FSRef frameworksFolderRef; + CFURLRef baseURL; + CFURLRef bundleURL; + + if (bundlePtr == nil) + return (-1); + + *bundlePtr = nil; + + baseURL = nil; + bundleURL = nil; + + err = FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, true, &frameworksFolderRef); + if (err == noErr) { + baseURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &frameworksFolderRef); + if (baseURL == nil) { + err = coreFoundationUnknownErr; + } + } + if (err == noErr) { + bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, framework, false); + if (bundleURL == nil) { + err = coreFoundationUnknownErr; + } + } + if (err == noErr) { + *bundlePtr = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL); + if (*bundlePtr == nil) { + err = coreFoundationUnknownErr; + } + } + if (err == noErr) { + if (!CFBundleLoadExecutable(*bundlePtr)) { + err = coreFoundationUnknownErr; + } + } + + // Clean up. + if (err != noErr && *bundlePtr != nil) { + CFRelease(*bundlePtr); + *bundlePtr = nil; + } + if (bundleURL != nil) { + CFRelease(bundleURL); + } + if (baseURL != nil) { + CFRelease(baseURL); + } + + return err; +} + +class IdlePlatform::Private { +public: + EventLoopTimerRef mTimerRef; + int mSecondsIdle; + + Private() : mTimerRef(0), mSecondsIdle(0) { } + + static pascal void IdleTimerAction(EventLoopTimerRef, EventLoopIdleTimerMessage inState, void *inUserData); +}; + +pascal void IdlePlatform::Private::IdleTimerAction(EventLoopTimerRef, EventLoopIdleTimerMessage inState, + void *inUserData) +{ + switch (inState) { + case kEventLoopIdleTimerStarted: + case kEventLoopIdleTimerStopped: + // Get invoked with this constant at the start of the idle period, + // or whenever user activity cancels the idle. + ((IdlePlatform::Private *)inUserData)->mSecondsIdle = 0; + break; + case kEventLoopIdleTimerIdling: + // Called every time the timer fires (i.e. every second). + ((IdlePlatform::Private *)inUserData)->mSecondsIdle++; + break; + } +} + +IdlePlatform::IdlePlatform() { d = new Private(); } + +IdlePlatform::~IdlePlatform() +{ + RemoveEventLoopTimer(d->mTimerRef); + delete d; +} + +// Typedef for the function we're getting back from CFBundleGetFunctionPointerForName. +typedef OSStatus (*InstallEventLoopIdleTimerPtr)(EventLoopRef inEventLoop, EventTimerInterval inFireDelay, + EventTimerInterval inInterval, EventLoopIdleTimerUPP inTimerProc, + void *inTimerData, EventLoopTimerRef *outTimer); + +bool IdlePlatform::init() +{ + // May already be init'ed. + if (d->mTimerRef) { + return true; + } + + // According to the docs, InstallEventLoopIdleTimer is new in 10.2. + // According to the headers, it has been around since 10.0. + // One of them is lying. We'll play it safe and weak-link the function. + + // Load the "Carbon.framework" bundle. + CFBundleRef carbonBundle; + if (LoadFrameworkBundle(CFSTR("Carbon.framework"), &carbonBundle) != noErr) { + return false; + } + + // Load the Mach-O function pointers for the routine we will be using. + InstallEventLoopIdleTimerPtr myInstallEventLoopIdleTimer + = (InstallEventLoopIdleTimerPtr)CFBundleGetFunctionPointerForName(carbonBundle, + CFSTR("InstallEventLoopIdleTimer")); + if (myInstallEventLoopIdleTimer == 0) { + return false; + } + + EventLoopIdleTimerUPP timerUPP = NewEventLoopIdleTimerUPP(Private::IdleTimerAction); + if ((*myInstallEventLoopIdleTimer)(GetMainEventLoop(), kEventDurationSecond, kEventDurationSecond, timerUPP, 0, + &d->mTimerRef)) { + return true; + } + + return false; +} + +int IdlePlatform::secondsIdle() { return d->mSecondsIdle; } diff --git a/src/libpsi/tools/idle/idle_win.cpp b/src/libpsi/tools/idle/idle_win.cpp new file mode 100644 index 000000000..3e9707162 --- /dev/null +++ b/src/libpsi/tools/idle/idle_win.cpp @@ -0,0 +1,100 @@ +/* + * idle_win.cpp - detect desktop idle time + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "idle.h" + +#include +#include + +#if defined(Q_OS_WIN32) && !defined(Q_CC_GNU) && (_WIN32_WINNT < 0x0500) +typedef struct tagLASTINPUTINFO { + UINT cbSize; + DWORD dwTime; +} LASTINPUTINFO, *PLASTINPUTINFO; +#endif + +class IdlePlatform::Private { +public: + Private() + { + GetLastInputInfo = 0; + IdleUIGetLastInputTime = 0; + lib = 0; + } + + typedef BOOL(__stdcall *GetLastInputInfoFunc)(PLASTINPUTINFO); + typedef DWORD(__stdcall *IdleUIGetLastInputTimeFunc)(void); + GetLastInputInfoFunc GetLastInputInfo; + IdleUIGetLastInputTimeFunc IdleUIGetLastInputTime; + QLibrary *lib; +}; + +IdlePlatform::IdlePlatform() { d = new Private; } + +IdlePlatform::~IdlePlatform() +{ + delete d->lib; + delete d; +} + +bool IdlePlatform::init() +{ + if (d->lib) + return true; + + // try to find the built-in Windows 2000 function + d->lib = new QLibrary("user32"); + if (d->lib->load() && (d->GetLastInputInfo = (Private::GetLastInputInfoFunc)d->lib->resolve("GetLastInputInfo"))) { + return true; + } else { + delete d->lib; + d->lib = 0; + } + + // fall back on idleui + d->lib = new QLibrary("idleui"); + if (d->lib->load() + && (d->IdleUIGetLastInputTime + = (Private::IdleUIGetLastInputTimeFunc)d->lib->resolve("IdleUIGetLastInputTime"))) { + return true; + } else { + delete d->lib; + d->lib = 0; + } + + return false; +} + +int IdlePlatform::secondsIdle() +{ + int i; + if (d->GetLastInputInfo) { + LASTINPUTINFO li; + li.cbSize = sizeof(LASTINPUTINFO); + bool ok = d->GetLastInputInfo(&li); + if (!ok) + return 0; + i = li.dwTime; + } else if (d->IdleUIGetLastInputTime) { + i = d->IdleUIGetLastInputTime(); + } else + return 0; + + return (GetTickCount() - i) / 1000; +} diff --git a/src/libpsi/tools/idle/idle_x11.cpp b/src/libpsi/tools/idle/idle_x11.cpp new file mode 100644 index 000000000..d50cc3d45 --- /dev/null +++ b/src/libpsi/tools/idle/idle_x11.cpp @@ -0,0 +1,154 @@ +/* + * idle_x11.cpp - detect desktop idle time + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "idle.h" + +#ifdef HAVE_XSS +#include +#include +#include +#include +#include +#include + +static XErrorHandler old_handler = 0; +extern "C" int xerrhandler(Display *dpy, XErrorEvent *err) +{ + if (err->error_code == BadDrawable) + return 0; + + return (*old_handler)(dpy, err); +} +#endif // HAVE_XSS + +#ifdef USE_DBUS + +#include +#include +#include +#include +#include + +// Screen Saver dbus services +static const QLatin1String COMMON_SS_SERV("org.freedesktop.ScreenSaver"); +static const QLatin1String COMMON_SS_PATH("/ScreenSaver"); +static const QLatin1String KDE_SS_SERV("org.kde.screensaver"); +static const QLatin1String GNOME_SS_SERV("org.gnome.Mutter.IdleMonitor"); +static const QLatin1String GNOME_SS_PATH("/org/gnome/Mutter/IdleMonitor/Core"); +// Screen saver functions +static const QLatin1String GNOME_SS_F("GetIdletime"); +static const QLatin1String COMMON_SS_F("GetSessionIdleTime"); + +#endif // USE_DBUS + +class IdlePlatform::Private { +public: + Private() { } + +#ifdef USE_DBUS + QString dbusService = QString(); +#endif // USE_DBUS +#ifdef HAVE_XSS + XScreenSaverInfo *ss_info = nullptr; +#endif // HAVE_XSS +}; + +IdlePlatform::IdlePlatform() +{ +#if defined(HAVE_XSS) || defined(USE_DBUS) + d = new Private; +#else + d = nullptr; +#endif +} + +IdlePlatform::~IdlePlatform() +{ +#ifdef HAVE_XSS + if (d->ss_info) + XFree(d->ss_info); + if (old_handler) { + XSetErrorHandler(old_handler); + old_handler = 0; + } +#endif // HAVE_XSS + if (d) + delete d; +} + +bool IdlePlatform::init() +{ +#ifdef USE_DBUS + // if DBUS idle is available using it else try to use XSS functions + const auto services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); + const QStringList idleServices = { COMMON_SS_SERV, KDE_SS_SERV, GNOME_SS_SERV }; + // find first available dbus-service + for (const auto &service : idleServices) { + if (services.contains(service)) { + d->dbusService = service; + return true; + } + } +#endif // USE_DBUS +#ifdef HAVE_XSS + if (!QX11Info::isPlatformX11()) + return false; + + if (d->ss_info) + return true; + + old_handler = XSetErrorHandler(xerrhandler); + + int event_base, error_base; + if (XScreenSaverQueryExtension(QX11Info::display(), &event_base, &error_base)) { + d->ss_info = XScreenSaverAllocInfo(); + return true; + } +#endif // HAVE_XSS + return false; +} + +int IdlePlatform::secondsIdle() +{ +#ifdef USE_DBUS + if (!d->dbusService.isEmpty()) { + // KDE and freedesktop uses the same path interface and method but gnome uses other + bool isNotGnome = d->dbusService == COMMON_SS_SERV || d->dbusService == KDE_SS_SERV; + const QLatin1String iface = isNotGnome ? COMMON_SS_SERV : GNOME_SS_SERV; + const QLatin1String path = isNotGnome ? COMMON_SS_PATH : GNOME_SS_PATH; + const QLatin1String method = isNotGnome ? COMMON_SS_F : GNOME_SS_F; + auto interface = QDBusInterface(d->dbusService, path, iface); + if (interface.isValid()) { + QDBusReply reply = interface.call(method); + // probably reply value for freedesktop and kde need to be converted to seconds + if (reply.isValid()) + return isNotGnome ? reply.value() / 1000 : reply.value(); + } + } +#endif // USE_DBUS +#ifdef HAVE_XSS + if (!d->ss_info) + return 0; + + if (!XScreenSaverQueryInfo(QX11Info::display(), QX11Info::appRootWindow(), d->ss_info)) + return 0; + return d->ss_info->idle / 1000; +#endif // HAVE_XSS + return 0; +} diff --git a/src/libpsi/tools/iodeviceopener.cpp b/src/libpsi/tools/iodeviceopener.cpp new file mode 100644 index 000000000..d505532de --- /dev/null +++ b/src/libpsi/tools/iodeviceopener.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#include "iodeviceopener.h" + +IODeviceOpener::IODeviceOpener(QIODevice *device, QIODevice::OpenModeFlag mode) : device_(device), close_(false) +{ + if (!device_->isOpen()) { + if (!device_->open(mode)) { + isOpen_ = false; + return; + } + close_ = true; + } else if (!(device_->openMode() & mode)) { + isOpen_ = false; + return; + } + isOpen_ = true; +} + +IODeviceOpener::~IODeviceOpener() +{ + if (close_) { + device_->close(); + } +} diff --git a/src/libpsi/tools/iodeviceopener.h b/src/libpsi/tools/iodeviceopener.h new file mode 100644 index 000000000..163245537 --- /dev/null +++ b/src/libpsi/tools/iodeviceopener.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#ifndef IODEVICEOPENER_H +#define IODEVICEOPENER_H + +#include +#include + +/** + * An IODeviceOpener is used to ensure that an IODevice is opened. + * If the QIODevice was not open when the opener was created, it will be closed + * again when the opener is destroyed. + * + * Example: + * void foo(QIODevice* device) { + * IODeviceOpener opener(device, QIODevice::ReadOnly); + * if (!opener.isOpen()) { + * qDebug() << "Error opening QIODevice"; + * return; + * } + * ... + * device->readAll() + * ... + * } + */ +class IODeviceOpener { +public: + /** + * Opens an QIODevice in a specific mode if the device was not opened + * yet. + * If the device was already open but in a different, non-compatible + * mode than the one requested, isOpen() will return false. + */ + IODeviceOpener(QIODevice *device, QIODevice::OpenModeFlag mode); + + /** + * Closes the QIODevice passed to the constructor if the IODevice was not + * opened at that time. + */ + ~IODeviceOpener(); + + /** + * Checks whether the io device was opened succesfully in the mode + * requested. + */ + bool isOpen() const { return isOpen_; } + +private: + QPointer device_; + bool close_; + bool isOpen_; +}; + +#endif // IODEVICEOPENER_H diff --git a/src/libpsi/tools/languagemanager.cpp b/src/libpsi/tools/languagemanager.cpp new file mode 100644 index 000000000..bbad81a2f --- /dev/null +++ b/src/libpsi/tools/languagemanager.cpp @@ -0,0 +1,254 @@ +#include "languagemanager.h" + +#include +#include + +LanguageManager::LangId LanguageManager::fromString(const QString &langDesc) +{ + QLocale loc(langDesc); + LangId id; + if (loc == QLocale::c()) { + return id; // It's default initialized to any lang, any country, any script. consider as a error + } + int cnt = langDesc.count(QRegularExpression("[_-]")); + id.language = loc.language(); + if (cnt) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + id.country = loc.country(); // supposing if there are two components then it's lways country and not script +#else + id.country = loc.territory(); +#endif + if (cnt > 1) { // lang_script_country + id.script = loc.script(); + } + } + return id; +} + +// returns [lang][-script][-country] +QString LanguageManager::toString(const LanguageManager::LangId &id) +{ + QLocale loc((QLocale::Language)id.language, (QLocale::Script)id.script, (QLocale::Country)id.country); + QStringList ret; + QStringList langCountry = loc.name().split('_'); + if (id.language) { + ret.append(langCountry[0]); // language + } + if (id.script) { + QStringList items = loc.bcp47Name().split(QRegularExpression("[.@]"))[0].split('-'); + if (items.count() == 3) { // we have script + ret.append(items[1]); + } + } + if (id.country) { + ret.append(langCountry[1]); + } + return ret.join('-'); +} + +/** + * @brief LanguageManager::bestUiMatch + * + * Lookups the best match from available locales against each next + * locale from QLocale::uiLanguages. + * For example available is comprised of en_ANY, en_US, ru_ANY (depends on LangId fields), + * and uiLanguages has ru_RU then "ru_ANY" will be selected and returned. + * If uiLanguages locale is en_US for the example above, then en_US + * will be selected with language and country in LangId. + * + * Another case is when available have something like en_US, ru_RU, ru_UA but + * uiLanguages has just "ru" then system locale will be checked for country. + * In case of Russia, ru_RU will be selected for Belarus nothing will selected. + * + * Exmaples: + * available | ui | selected | + * ------------------------------------------------- + * en_ANY en_US | en_US | en_US + * en_ANY | en_US | en_ANY + * en_US | en | en_US if system is US. nothing otherwise + * + * + * @param avail available languages to select from. + * @param justOne just one langId in result is enough + * @return priority sorted languages list. best match comes first + */ +QList LanguageManager::bestUiMatch(const QSet &avail, bool justOne) +{ + QLocale def; // default locale (or system locale if default is not set). FIXME get from settings + static QSet uiLangs; + if (uiLangs.isEmpty()) { + const auto languages = QLocale::system().uiLanguages(); + for (auto const &l : languages) { + auto id = fromString(l); + if (id.language) { + uiLangs.insert(id); + } + } + } + QList ret; + QList toCheck; + toCheck.reserve(4); + for (auto uiId : uiLangs) { + toCheck.clear(); + // check if ui locale looks like system locale and set missed parts + if (uiId.language == def.language()) { // matches with system. consider country and script from system to be + // preferred if not set in ui +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + auto country = def.country(); +#else + auto country = def.territory(); +#endif + if (!uiId.country && (!uiId.script || uiId.script == def.script())) { + uiId.country = country; + } + if (!uiId.script && (!uiId.country || uiId.country == country)) { + uiId.script = def.script(); + } + } + // now when everything is filled we can fallback from example 3 to 2 if it was system. + // of course it's still possible to have just language in ui. + + // next things are simple. + // first try uiId match as is and add to result if it's within available. + // then check if we can remove script or country and try again. + // repeat till we have just language + toCheck.append(uiId); + + auto copyId = uiId; + // try to check with any script + if (uiId.script != QLocale::AnyScript) { + uiId.script = QLocale::AnyScript; + toCheck.append(uiId); + uiId = copyId; + } + // try to check with any country + if (uiId.country != QLocale::AnyCountry) { + uiId.country = QLocale::AnyCountry; + toCheck.append(uiId); + uiId = copyId; + } + // try to check any script and any country + if (uiId.script != QLocale::AnyScript && uiId.country != QLocale::AnyCountry) { + uiId.script = QLocale::AnyScript; + uiId.country = QLocale::AnyCountry; + toCheck.append(uiId); + } + + for (auto const &id : toCheck) { + if (avail.contains(id)) { + ret.append(id); + if (justOne) { + return ret; + } + } + } + } + LangId defLangId; + if (avail.contains(defLangId)) { + ret.append(defLangId); + } + return ret; +} + +QString LanguageManager::bestUiMatch(QHash langToText) +{ + QHash langs; + for (auto l = langToText.constBegin(); l != langToText.constEnd(); ++l) { + langs.insert(LanguageManager::fromString(l.key()), + l.value()); // FIXME: all unknown languages will be converted to C/default locale + } +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + auto preferred = LanguageManager::bestUiMatch(langs.keys().toSet(), true); +#else + auto preferred = LanguageManager::bestUiMatch(QSet(langs.keyBegin(), langs.keyEnd()), true); +#endif + if (preferred.count()) { + return langs.value(preferred.first()); + } + return QString(); +} + +QString LanguageManager::languageName(const LanguageManager::LangId &id) +{ + bool needCountry = true; + if (id.language == QLocale::AnyLanguage) { + return QObject::tr("Any Language"); + } + QLocale loc((QLocale::Language)id.language, (QLocale::Script)id.script, (QLocale::Country)id.country); + + QString name; + if (loc.language() == QLocale::English || loc.language() == QLocale::Spanish) { + // english and espanol use country in language name + if (id.country) { + needCountry = false; + } else { + name = loc.language() == QLocale::English ? QStringLiteral("English") : QStringLiteral("Español"); + } + } + + if (name.isEmpty()) { + name = loc.nativeLanguageName(); + } + if (name.isEmpty()) { + name = QLocale::languageToString(loc.language()); + } else if (loc.script() != QLocale::LatinScript + && loc.script() + != QLocale().script()) { // if not latin and not deafuls, then probaby it's somethingunreadable + name += (" [" + QLocale::languageToString(loc.language()) + "]"); + } + if (id.script) { + name += " - " + QLocale::scriptToString(loc.script()); + } + if (needCountry && id.country) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + name += " - " + loc.nativeCountryName(); +#else + name += " - " + loc.nativeTerritoryName(); +#endif + } + return name; +} + +QString LanguageManager::countryName(const LanguageManager::LangId &id) +{ + QLocale loc((QLocale::Language)id.language, (QLocale::Script)id.script, (QLocale::Country)id.country); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QString ret = loc.nativeCountryName(); +#else + QString ret = loc.nativeTerritoryName(); +#endif + if (loc.language() != QLocale().language() && loc.script() != QLocale::LatinScript) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + ret += " (" + loc.countryToString(loc.country()) + ")"; +#else + ret += " (" + loc.territoryToString(loc.territory()) + ")"; +#endif + } + return ret; +} + +QSet LanguageManager::deserializeLanguageSet(const QString &str) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList langs = str.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); +#else + QStringList langs = str.split(QRegularExpression("\\s+"), QString::SkipEmptyParts); +#endif + QSet ret; + for (auto const &l : langs) { + auto id = fromString(l); + if (id.language) { + ret.insert(id); + } + } + return ret; +} + +QString LanguageManager::serializeLanguageSet(const QSet &langs) +{ + QStringList ret; + for (auto const &l : langs) { + ret.append(toString(l)); + } + return ret.join(' '); +} diff --git a/src/libpsi/tools/languagemanager.h b/src/libpsi/tools/languagemanager.h new file mode 100644 index 000000000..2a55cf73a --- /dev/null +++ b/src/libpsi/tools/languagemanager.h @@ -0,0 +1,43 @@ +#ifndef LANGUAGEMANAGER_H +#define LANGUAGEMANAGER_H + +#include +#include + +class LanguageManager { +public: + struct LangId { + quint16 language = QLocale::AnyLanguage; + quint16 country = QLocale::AnyCountry; + quint8 script = QLocale::AnyScript; // in qt-5.9.2 it's less than 256 + }; + + static LangId fromString(const QString &langDesc); + static QString toString(const LangId &id); + static QList bestUiMatch(const QSet &avail, bool justOne = false); + static QString bestUiMatch(QHash langToText); + static QString languageName(const LangId &id); + static QString countryName(const LangId &id); + static QSet deserializeLanguageSet(const QString &); + static QString serializeLanguageSet(const QSet &langs); +}; + +inline uint qHash(const LanguageManager::LangId &t) { return qHash(t.language) ^ qHash(t.country) ^ qHash(t.script); } + +// weird sorting operator +inline bool operator<(const LanguageManager::LangId &a, const LanguageManager::LangId &b) +{ + if (a.script == b.script) { + return ((a.language << 16) + a.country) < ((b.language << 16) + b.country); + } + return a.script < b.script; +} + +inline bool operator==(const LanguageManager::LangId &a, const LanguageManager::LangId &b) +{ + return a.language == b.language && a.country == b.country && a.script == b.script; +} + +Q_DECLARE_METATYPE(LanguageManager::LangId) + +#endif // LANGUAGEMANAGER_H diff --git a/src/libpsi/tools/mac_dock/docktest.cpp b/src/libpsi/tools/mac_dock/docktest.cpp new file mode 100644 index 000000000..afb7dc70c --- /dev/null +++ b/src/libpsi/tools/mac_dock/docktest.cpp @@ -0,0 +1,74 @@ +/* + * docktest.cpp: A test program for the dock class + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "mac_dock.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class DockTestWidget : public QWidget { + Q_OBJECT + +public: + DockTestWidget(QWidget *parent = 0); + +public slots: + void do_overlay(); + +private: + QLineEdit *text; +}; + +DockTestWidget::DockTestWidget(QWidget *parent) : QWidget(parent) +{ + // Initialize widgets + QGridLayout *layout = new QGridLayout(this); + + layout->addWidget(new QLabel("Text", this), 0, 0); + text = new QLineEdit(this); + text->setText("1"); + layout->addWidget(text, 0, 1); + + QPushButton *overlay = new QPushButton("Overlay", this); + connect(overlay, SIGNAL(clicked()), SLOT(do_overlay())); + layout->addWidget(overlay, 1, 0); +} + +int main(int argc, char **argv) +{ + QApplication a(argc, argv); + DockTestWidget w; + w.show(); + return a.exec(); +} + +void DockTestWidget::do_overlay() { MacDock::overlay(text->text()); } + +#include "docktest.moc" diff --git a/src/libpsi/tools/mac_dock/mac_dock.h b/src/libpsi/tools/mac_dock/mac_dock.h new file mode 100644 index 000000000..b8906cd2e --- /dev/null +++ b/src/libpsi/tools/mac_dock/mac_dock.h @@ -0,0 +1,17 @@ +#ifndef MAC_DOCK_H +#define MAC_DOCK_H + +#include + +class MacDock { +public: + static void startBounce(); + static void stopBounce(); + static void overlay(const QString &text = QString()); + +private: + static bool isBouncing; + static bool overlayed; +}; + +#endif // MAC_DOCK_H diff --git a/src/libpsi/tools/mac_dock/mac_dock.mm b/src/libpsi/tools/mac_dock/mac_dock.mm new file mode 100644 index 000000000..8a08ecb8d --- /dev/null +++ b/src/libpsi/tools/mac_dock/mac_dock.mm @@ -0,0 +1,22 @@ +#include "mac_dock.h" + +#include "privateqt_mac.h" + +#include + +static NSInteger requestType = 0; + +void MacDock::startBounce() { requestType = [NSApp requestUserAttention:NSCriticalRequest]; } + +void MacDock::stopBounce() +{ + if (requestType) { + [NSApp cancelUserAttentionRequest:requestType]; + requestType = 0; + } +} + +void MacDock::overlay(const QString &text) +{ + [[NSApp dockTile] setBadgeLabel:(NSString *)QtCFString::toCFStringRef(text)]; +} diff --git a/src/libpsi/tools/mac_dock/privateqt_mac.h b/src/libpsi/tools/mac_dock/privateqt_mac.h new file mode 100644 index 000000000..ccadcf58d --- /dev/null +++ b/src/libpsi/tools/mac_dock/privateqt_mac.h @@ -0,0 +1,91 @@ +/* + * privateqt_mac.h + * Copyright (C) 2009 Yandex LLC (Michail Pishchagin) + * based on http://doc.trolltech.com/solutions/4/qtspellcheckingtextedit/ + * + * 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 2 + * 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 . + * + */ + +#ifndef PRIVATEQT_MAC_H +#define PRIVATEQT_MAC_H + +#include + +#include + +template class QtCFType { +public: + inline QtCFType(const T &t = 0) : type(t) { } + inline QtCFType(const QtCFType &helper) : type(helper.type) + { + if (type) + CFRetain(type); + } + inline ~QtCFType() + { + if (type) + CFRelease(type); + } + inline operator T() { return type; } + inline QtCFType operator=(const QtCFType &helper) + { + if (helper.type) + CFRetain(helper.type); + CFTypeRef type2 = type; + type = helper.type; + if (type2) + CFRelease(type2); + return *this; + } + inline T *operator&() { return &type; } + static QtCFType constructFromGet(const T &t) + { + CFRetain(t); + return QtCFType(t); + } + +protected: + T type; +}; + +class QtCFString : public QtCFType { +public: + inline QtCFString(const QString &str) : QtCFType(0), string(str) { } + inline QtCFString(const CFStringRef cfstr = 0) : QtCFType(cfstr) { } + inline QtCFString(const QtCFType &other) : QtCFType(other) { } + operator QString() const; + operator CFStringRef() const; + static QString toQString(CFStringRef cfstr); + static CFStringRef toCFStringRef(const QString &str); + +private: + QString string; +}; + +class QtMacCocoaAutoReleasePool { +private: + void *pool; + +public: + QtMacCocoaAutoReleasePool(); + ~QtMacCocoaAutoReleasePool(); + + inline void *handle() const { return pool; } +}; + +struct _NSRange; +typedef struct _NSRange NSRange; + +#endif // PRIVATEQT_MAC_H diff --git a/src/libpsi/tools/mac_dock/privateqt_mac.mm b/src/libpsi/tools/mac_dock/privateqt_mac.mm new file mode 100644 index 000000000..b2dd02420 --- /dev/null +++ b/src/libpsi/tools/mac_dock/privateqt_mac.mm @@ -0,0 +1,66 @@ +/* + * privateqt_mac.mm + * Copyright (C) 2009 Yandex LLC (Michail Pishchagin) + * based on http://doc.trolltech.com/solutions/4/qtspellcheckingtextedit/ + * + * 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 2 + * 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 . + * + */ + +#include "privateqt_mac.h" + +#import +#include +#include + +QString QtCFString::toQString(CFStringRef str) +{ + if (!str) + return QString(); + CFIndex length = CFStringGetLength(str); + const UniChar *chars = CFStringGetCharactersPtr(str); + if (chars) + return QString(reinterpret_cast(chars), length); + + QVarLengthArray buffer(length); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); + return QString(reinterpret_cast(buffer.constData()), length); +} + +QtCFString::operator QString() const +{ + if (string.isEmpty() && type) + const_cast(this)->string = toQString(type); + return string; +} + +CFStringRef QtCFString::toCFStringRef(const QString &string) +{ + return CFStringCreateWithCharacters(0, reinterpret_cast(string.unicode()), string.length()); +} + +QtCFString::operator CFStringRef() const +{ + if (!type) + const_cast(this)->type = toCFStringRef(string); + return type; +} + +QtMacCocoaAutoReleasePool::QtMacCocoaAutoReleasePool() +{ + NSApplicationLoad(); + pool = (void *)[[NSAutoreleasePool alloc] init]; +} + +QtMacCocoaAutoReleasePool::~QtMacCocoaAutoReleasePool() { [(NSAutoreleasePool *)pool release]; } diff --git a/src/libpsi/tools/maybe.h b/src/libpsi/tools/maybe.h new file mode 100644 index 000000000..e231ebd29 --- /dev/null +++ b/src/libpsi/tools/maybe.h @@ -0,0 +1,65 @@ +/* + * maybe.h + * Copyright (C) 2006 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#ifndef MAYBE_H +#define MAYBE_H + +/** + * \brief A template class either containing no value or a specific value. + * This container is especially handy for types that do not have an isNull() + * or isEmpty() (such as primitive types). + * + * Example: Returns the division of numer and denom if it is an integer + * \code + * Maybe integer_divide(int numer, int denom) { + * if (numer % denom == 0) + * return Maybe(numer / denom); + * else + * return Maybe(); + * } + * \endcode + */ +template class Maybe { +public: + /** + * \brief Constructs a Maybe container with no value. + */ + Maybe() : hasValue_(false) { } + + /** + * \brief Constructs a Maybe container with a value. + */ + Maybe(const T &value) : value_(value), hasValue_(true) { } + + /** + * \brief Checks whether this container has a value. + */ + bool hasValue() const { return hasValue_; } + + /** + * \brief Returns the value of the container. + */ + const T &value() const { return value_; } + +private: + T value_; + bool hasValue_; +}; + +#endif // MAYBE_H diff --git a/src/libpsi/tools/priorityvalidator.cpp b/src/libpsi/tools/priorityvalidator.cpp new file mode 100644 index 000000000..1d1bf2be9 --- /dev/null +++ b/src/libpsi/tools/priorityvalidator.cpp @@ -0,0 +1,37 @@ +/* + * priorityvalidator.cpp - XMPP priority validator + * Copyright (C) 2010 Dmitriy.trt + * + * 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 2 + * 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 . + * + */ + +#include "priorityvalidator.h" + +QValidator::State PriorityValidator::validate(QString &input, int & /*pos*/) const +{ + if (input.isEmpty()) { + return QValidator::Acceptable; + } else if (input == "-") { + return QValidator::Intermediate; + } else { + bool ok = false; + int val = input.toInt(&ok); + if (ok && val >= -128 && val <= 127) { + return QValidator::Acceptable; + } else { + return QValidator::Invalid; + } + } +} diff --git a/src/libpsi/tools/priorityvalidator.h b/src/libpsi/tools/priorityvalidator.h new file mode 100644 index 000000000..2d0cc9f44 --- /dev/null +++ b/src/libpsi/tools/priorityvalidator.h @@ -0,0 +1,32 @@ +/* + * priorityvalidator.h - XMPP priority validator + * Copyright (C) 2010 Dmitriy.trt + * + * 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 2 + * 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 . + * + */ + +#ifndef PRIORITYVALIDATOR_H +#define PRIORITYVALIDATOR_H + +#include + +class PriorityValidator : public QValidator { + Q_OBJECT +public: + PriorityValidator(QObject *parent = nullptr) : QValidator(parent) {}; + virtual State validate(QString &input, int &pos) const; +}; + +#endif // PRIORITYVALIDATOR_H diff --git a/src/libpsi/tools/simplecli/simplecli.cpp b/src/libpsi/tools/simplecli/simplecli.cpp new file mode 100644 index 000000000..ea0809634 --- /dev/null +++ b/src/libpsi/tools/simplecli/simplecli.cpp @@ -0,0 +1,258 @@ +/* + * simplecli.cpp - Simple CommandLine Interface parser / manager + * Copyright (C) 2009 Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#include "simplecli.h" + +#include + +/** + * \class SimpleCli + * \brief Simple Commandline Interface parser. + * + * This class allows you to define Commandline Interface for your application + * and then use this information to convert argv to a map of options and their values. + * + * Please note that support for short options (-x) is very limited + * and provided only to support options like -h and -v. + */ + +/** + * \brief Define a switch (option that does not have a value) + */ +void SimpleCli::defineSwitch(const QByteArray &name, const QString &help) +{ + argdefs[name] = Arg(name, "", help, false); + aliases[name] = name; +} + +/** + * \brief Define a parameter (option that requires a value) + */ +void SimpleCli::defineParam(const QByteArray &name, const QString &valueHelp, const QString &help) +{ + argdefs[name] = Arg(name, valueHelp, help, true); + aliases[name] = name; +} + +/** + * \brief Add alias for already existing option. + * \a alias will be mapped to \a originalName in parse() result. + */ +void SimpleCli::defineAlias(const QByteArray &alias, const QByteArray &originalName) +{ + if (!argdefs.contains(originalName)) { + qDebug("CLI: cannot add alias '%s' because name '%s' does not exist", alias.constData(), + originalName.constData()); + return; + } + argdefs[originalName].aliases.append(alias); + aliases[alias] = originalName; + if (alias.length() == 1 && argdefs[originalName].shortName.isNull()) { + argdefs[originalName].shortName = alias.at(0); + } +} + +/** + * \brief Parse \a argv into a name,value map. + * \param terminalArgs stop parsing when one of these options is found (it will be included in result) + * \param safeArgs if not NULL, will be used to pass number of arguments before terminal argument (or argc if there was + * no terminal argument) + * + * Supported options syntax: --switch; --param=value; --param value; -switch; -param=value; -param value. + * Additionally on Windows: /switch; /param:value; /param value. + * + * When creating the map, alias names are converted to original option names. + * + * Use \a terminalArgs if you want need to stop parsing after certain options for security reasons, etc. + */ +QHash SimpleCli::parse(int argc, char *argv[], const QList &terminalArgs, + int *safeArgc) +{ +#ifdef Q_OS_WIN + const bool winmode = true; +#else + const bool winmode = false; +#endif + + QHash map; + int safe = 1; + int n = 1; + for (; n < argc; ++n) { + QByteArray str = QByteArray(argv[n]); + QByteArray left, right; + int sep = str.indexOf('='); + bool explicitValue = false; + if (sep == -1) { + left = str; + } else { + left = str.mid(0, sep); + right = str.mid(sep + 1); + explicitValue = true; + } + + bool unnamedArgument = true; + if (left.startsWith("--")) { + left = left.mid(2); + unnamedArgument = false; + } else if (left.startsWith('-') || (left.startsWith('/') && winmode)) { + left = left.mid(1); + unnamedArgument = false; + } else if (n == 1 && left.startsWith("xmpp:")) { + unnamedArgument = false; + left = "uri"; + right = str; + } + + QByteArray name, value; + if (unnamedArgument) { + value = left; + } else { + name = left; + value = right; + if (aliases.contains(name)) { + name = argdefs[aliases[name]].name; + bool needsValue = argdefs[name].needsValue; + if (!needsValue && explicitValue) { + continue; // ignore strange switch with value + } + if (needsValue && value.isNull() && n + 1 < argc) { + value = QByteArray(argv[++n]); + } + } + } + + if (map.contains(name)) { + qDebug("CLI: Ignoring next value ('%s') for '%s' arg.", value.constData(), name.constData()); + } else { + map[name] = value; + } + + if (terminalArgs.contains(name)) { + break; + } else { + safe = n + 1; + } + } + + if (safeArgc) { + *safeArgc = safe; + } + return map; +} + +/** + * \brief Produce options description, for use in --help. + * \param textWidth wrap text when wider than \a textWidth + */ +QString SimpleCli::optionsHelp(int textWidth) +{ + QString ret; + + int margin = 2; + + int longest = -1; + bool foundShort = false; + + for (const Arg &arg : std::as_const(argdefs)) { + if (arg.needsValue) { + longest = qMax(arg.name.length() + arg.valueHelp.length() + 1, longest); + } else { + longest = qMax(arg.name.length(), longest); + } + + foundShort = foundShort || !arg.shortName.isNull(); + } + longest += 2; // 2 = length("--") + int helpPadding = longest + 6; // 6 = 2 (left margin) + 2 (space before help) + 2 (next line indent) + if (foundShort) { + helpPadding += 4; // 4 = length("-x, ") + } + + for (const Arg &arg : std::as_const(argdefs)) { + QString line; + line.fill(' ', margin); + if (foundShort) { + if (arg.shortName.isNull()) { + line += " "; + } else { + line += '-' + arg.shortName + ", "; + } + } + QString longarg = "--" + arg.name; + if (arg.needsValue) { + longarg += '=' + arg.valueHelp; + } + line += longarg; + line += QString().fill(' ', longest - longarg.length() + 2); // 2 (space before help) + line += arg.help; + + ret += wrap(line, textWidth, helpPadding, 0); + } + + return ret; +} + +/** + * \brief Wrap text for printing on console. + * \param text text to be wrapped + * \param width width of the text + * \param margin left margin (filled with spaces) + * \param firstMargin margin in first line + * Note: This function is designed for text that do not contain tabs or line breaks. + * Results may be not pretty if such \a text is passed. + */ +QString SimpleCli::wrap(QString text, int width, int margin, int firstMargin) +{ + if (firstMargin < 0) { + firstMargin = margin; + } + + QString output; + + int prevBreak = -1; + int currentMargin = firstMargin; + int nextBreak; + + do { + nextBreak = prevBreak + width + 1 - currentMargin; + if (nextBreak < text.length()) { + int lastSpace = text.lastIndexOf(' ', nextBreak); + if (lastSpace > prevBreak) { + nextBreak = lastSpace; + } else { + // will be a bit longer... + nextBreak = text.indexOf(' ', nextBreak); + if (nextBreak == -1) { + nextBreak = text.length(); + } + } + } else { + nextBreak = text.length(); + } + + output += QString().fill(' ', currentMargin); + output += QStringView { text }.mid(prevBreak + 1, nextBreak - prevBreak - 1); + output += '\n'; + + prevBreak = nextBreak; + currentMargin = margin; + } while (nextBreak < text.length()); + + return output; +} diff --git a/src/libpsi/tools/simplecli/simplecli.h b/src/libpsi/tools/simplecli/simplecli.h new file mode 100644 index 000000000..af8f1220b --- /dev/null +++ b/src/libpsi/tools/simplecli/simplecli.h @@ -0,0 +1,64 @@ +/* + * simplecli.h - Simple CommandLine Interface parser / manager + * Copyright (C) 2009 Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#ifndef SIMPLECLI_H +#define SIMPLECLI_H + +#include +#include +#include + +class SimpleCli : public QObject { +public: + void defineSwitch(const QByteArray &name, const QString &help = QString()); + void defineParam(const QByteArray &name, const QString &valueHelp = QString("ARG"), + const QString &help = QString()); + + void defineAlias(const QByteArray &alias, const QByteArray &originalName); + + QHash + parse(int argc, char *argv[], const QList &terminalArgs = QList(), int *safeArgc = nullptr); + + QString optionsHelp(int textWidth); + static QString wrap(QString text, int width, int margin = 0, int firstMargin = -1); + +private: + struct Arg { + QByteArray name; + QList aliases; + QChar shortName; + + bool needsValue; + + QString help; + QString valueHelp; + + Arg(const QByteArray &argName, const QString &argValueHelp, const QString &argHelp, bool argNeedsValue) : + name(argName), needsValue(argNeedsValue), help(argHelp), valueHelp(argValueHelp) + { + } + + Arg() : needsValue(false) { } // needed only by QMap + }; + + QMap argdefs; + QMap aliases; +}; + +#endif // SIMPLECLI_H diff --git a/src/libpsi/tools/spellchecker/aspellchecker.cpp b/src/libpsi/tools/spellchecker/aspellchecker.cpp new file mode 100644 index 000000000..182534627 --- /dev/null +++ b/src/libpsi/tools/spellchecker/aspellchecker.cpp @@ -0,0 +1,154 @@ +/* + * aspellchecker.cpp + * + * Copyright (C) 2006 Remko Troncon + * Thanks to Ephraim. + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "aspellchecker.h" + +#include "aspell.h" + +#include +#include +#include + +ASpellChecker::ASpellChecker() : config_(new_aspell_config()) +{ + aspell_config_replace(config_, "encoding", "utf-8"); +#ifdef Q_OS_WIN + aspell_config_replace(config_, "conf-dir", QDir::homePath().toLocal8Bit().data()); + aspell_config_replace(config_, "data-dir", + QString("%1/aspell").arg(QCoreApplication::applicationDirPath()).toLocal8Bit().data()); + aspell_config_replace(config_, "dict-dir", + QString("%1/aspell").arg(QCoreApplication::applicationDirPath()).toLocal8Bit().data()); +#endif + setActiveLanguages(getAllLanguages()); +} + +ASpellChecker::~ASpellChecker() +{ + if (config_) { + delete_aspell_config(config_); + config_ = NULL; + } + + clearSpellers(); +} + +bool ASpellChecker::isCorrect(const QString &word) +{ + if (spellers_.isEmpty()) + return true; + + for (AspellSpeller *speller : spellers_) { + if (aspell_speller_check(speller, word.toUtf8().constData(), -1) != 0) + return true; + } + return false; +} + +QList ASpellChecker::suggestions(const QString &word) +{ + QList words; + + for (AspellSpeller *speller : spellers_) { + const AspellWordList *list = aspell_speller_suggest(speller, word.toUtf8(), -1); + AspellStringEnumeration *elements = aspell_word_list_elements(list); + const char *c_word; + while ((c_word = aspell_string_enumeration_next(elements)) != NULL) { + QString suggestion = QString::fromUtf8(c_word); + if (suggestion.size() > 2) + words.append(suggestion); + } + delete_aspell_string_enumeration(elements); + } + return words; +} + +bool ASpellChecker::add(const QString &word) +{ + bool result = false; + if (config_ && !spellers_.empty()) { + QString trimmed_word = word.trimmed(); + if (!word.isEmpty()) { + aspell_speller_add_to_personal(spellers_.first(), trimmed_word.toUtf8(), trimmed_word.toUtf8().length()); + aspell_speller_save_all_word_lists(spellers_.first()); + result = true; + } + } + return result; +} + +bool ASpellChecker::available() const { return !spellers_.isEmpty(); } + +bool ASpellChecker::writable() const { return false; } + +QSet ASpellChecker::getAllLanguages() const +{ + QSet langs; + + AspellDictInfoList *dict_info_list = get_aspell_dict_info_list(config_); + + if (!aspell_dict_info_list_empty(dict_info_list)) { + AspellDictInfoEnumeration *dict_info_enum = aspell_dict_info_list_elements(dict_info_list); + + while (!aspell_dict_info_enumeration_at_end(dict_info_enum)) { + const AspellDictInfo *dict_info = aspell_dict_info_enumeration_next(dict_info_enum); + auto id = LanguageManager::fromString(QString::fromLatin1(dict_info->code)); + if (id.language) { + langs.insert(id); + } + } + + delete_aspell_dict_info_enumeration(dict_info_enum); + } + + return langs; +} + +void ASpellChecker::setActiveLanguages(const QSet &langs) +{ + clearSpellers(); + + for (auto const &lang : langs) { + AspellConfig *conf = aspell_config_clone(config_); + aspell_config_replace( + conf, "lang", + LanguageManager::toString(lang).replace(QLatin1Char('-'), QLatin1Char('_')).toUtf8().constData()); + AspellCanHaveError *ret = new_aspell_speller(conf); + if (aspell_error_number(ret) == 0) { + spellers_.append(to_aspell_speller(ret)); + } else { + qDebug() << QString("Aspell error: %1").arg(aspell_error_message(ret)); + } + delete_aspell_config(conf); + } +} + +void ASpellChecker::clearSpellers() +{ + for (AspellSpeller *speller : spellers_) + delete_aspell_speller(speller); + + spellers_.clear(); +} diff --git a/src/libpsi/tools/spellchecker/aspellchecker.h b/src/libpsi/tools/spellchecker/aspellchecker.h new file mode 100644 index 000000000..a57332889 --- /dev/null +++ b/src/libpsi/tools/spellchecker/aspellchecker.h @@ -0,0 +1,60 @@ +/* + * aspellchecker.h + * + * Copyright (C) 2006 Remko Troncon + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#ifndef ASPELLCHECKER_H +#define ASPELLCHECKER_H + +#include "spellchecker.h" + +#include +#include + +struct AspellConfig; +struct AspellSpeller; + +class ASpellChecker : public SpellChecker { +public: + ASpellChecker(); + ~ASpellChecker(); + virtual QList suggestions(const QString &); + virtual bool isCorrect(const QString &); + virtual bool add(const QString &); + virtual bool available() const; + virtual bool writable() const; + + virtual void setActiveLanguages(const QSet &langs); + virtual QSet getAllLanguages() const; + +private: + void clearSpellers(); + +private: + AspellConfig *config_; + + typedef QList ASpellers; + ASpellers spellers_; +}; + +#endif // ASPELLCHECKER_H diff --git a/src/libpsi/tools/spellchecker/enchantchecker.cpp b/src/libpsi/tools/spellchecker/enchantchecker.cpp new file mode 100644 index 000000000..991265752 --- /dev/null +++ b/src/libpsi/tools/spellchecker/enchantchecker.cpp @@ -0,0 +1,157 @@ +/* + * enchantchecker.cpp + * + * Copyright (C) 2009 Caolán McNamara + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "enchantchecker.h" + +#include "enchant++.h" + +#include +#include +#include +#include + +static enchant::Broker *broker; + +EnchantChecker::EnchantChecker() +{ +#ifdef HAVE_ENCHANT2 + broker = new enchant::Broker(); +#else + broker = enchant::Broker::instance(); +#endif + if (broker) { + broker->list_dicts(enchantDictDescribeFn, static_cast(this)); + QTimer::singleShot(1000, [=]() { this->setActiveLanguages(getAllLanguages()); }); + } +} + +EnchantChecker::~EnchantChecker() +{ + clearSpellers(); +#ifdef HAVE_ENCHANT2 + delete broker; +#endif +} + +bool EnchantChecker::isCorrect(const QString &word) +{ + if (spellers_.isEmpty()) + return true; + + for (enchant::Dict *speller : spellers_) { + if (speller->check(word.toUtf8().constData())) + return true; + } + return false; +} + +QList EnchantChecker::suggestions(const QString &word) +{ + QList words; + + for (enchant::Dict *speller : spellers_) { + std::vector out_suggestions; + speller->suggest(word.toUtf8().constData(), out_suggestions); + std::vector::iterator aE = out_suggestions.end(); + for (std::vector::iterator aI = out_suggestions.begin(); aI != aE; ++aI) { + words += QString::fromUtf8(aI->c_str()); + } + } + return words; +} + +bool EnchantChecker::add(const QString &word) +{ + bool result = false; + if (!spellers_.isEmpty()) { + QString trimmed_word = word.trimmed(); + if (!word.isEmpty()) { +#ifdef HAVE_ENCHANT2 + spellers_.first()->add(word.toUtf8().constData()); +#else + spellers_.first()->add_to_pwl(word.toUtf8().constData()); +#endif + result = true; + } + } + return result; +} + +bool EnchantChecker::available() const { return (spellers_.isEmpty() != true); } + +bool EnchantChecker::writable() const { return false; } + +QSet EnchantChecker::getAllLanguages() const +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + return allLanguages_.keys().toSet(); +#else + return QSet(allLanguages_.keyBegin(), allLanguages_.keyEnd()); +#endif +} + +void EnchantChecker::setActiveLanguages(const QSet &langs) +{ + clearSpellers(); + + for (auto const &lang : langs) { + auto it = allLanguages_.constFind(lang); + if (it == allLanguages_.constEnd()) + continue; + + try { + spellers_ << broker->request_dict(it.value().toStdString()); + } catch (enchant::Exception &e) { + qWarning() << QString("Enchant error: %1").arg(e.what()); + } + } +} + +void EnchantChecker::clearSpellers() +{ + qDeleteAll(spellers_); + spellers_.clear(); +} + +void EnchantChecker::enchantDictDescribeFn(const char *const lang_tag, const char *const provider_name, + const char *const provider_desc, const char *const provider_file, + void *user_data) +{ + Q_UNUSED(provider_name); + Q_UNUSED(provider_desc); + Q_UNUSED(provider_file); + EnchantChecker *enchantChecker = static_cast(user_data); + + QString lang(QString::fromLatin1(lang_tag)); + auto id = LanguageManager::fromString(lang); + if (!id.language) { + id = LanguageManager::fromString(lang.section(QLatin1Char('_'), 0, 0)); + } + if (id.language) { + if (!enchantChecker->allLanguages_.contains(id)) { + enchantChecker->allLanguages_.insert(id, lang); + } + } +} diff --git a/src/libpsi/tools/spellchecker/enchantchecker.h b/src/libpsi/tools/spellchecker/enchantchecker.h new file mode 100644 index 000000000..a421823bc --- /dev/null +++ b/src/libpsi/tools/spellchecker/enchantchecker.h @@ -0,0 +1,61 @@ +/* + * enchantchecker.h + * + * Copyright (C) 2009 Caolán McNamara + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#ifndef ENCHANTCHECKER_H +#define ENCHANTCHECKER_H + +#include "spellchecker.h" + +#include + +namespace enchant { +class Dict; +} + +class EnchantChecker : public SpellChecker { +public: + EnchantChecker(); + ~EnchantChecker(); + virtual QList suggestions(const QString &); + virtual bool isCorrect(const QString &); + virtual bool add(const QString &); + virtual bool available() const; + virtual bool writable() const; + + virtual void setActiveLanguages(const QSet &langs); + virtual QSet getAllLanguages() const; + +private: + static void enchantDictDescribeFn(const char *const lang_tag, const char *const provider_name, + const char *const provider_desc, const char *const provider_file, + void *user_data); + void clearSpellers(); + + typedef QList EnchantDictList; + EnchantDictList spellers_; + QHash allLanguages_; +}; + +#endif // ENCHANTCHECKER_H diff --git a/src/libpsi/tools/spellchecker/hunspellchecker.cpp b/src/libpsi/tools/spellchecker/hunspellchecker.cpp new file mode 100644 index 000000000..bd28db2b5 --- /dev/null +++ b/src/libpsi/tools/spellchecker/hunspellchecker.cpp @@ -0,0 +1,242 @@ +/* + * hunspellchecker.cpp + * + * Copyright (C) 2015 Sergey Ilinykh, Vitaly Tonkacheyev + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "hunspellchecker.h" + +#ifdef Q_OS_WIN +#include "applicationinfo.h" +#endif +#include "config.h" +#include "languagemanager.h" + +#include +// #include +#include +#include +#include +#include +#include +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#endif +#include + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#define HS_STRING(text) li.codec->fromUnicode(text).toStdString() +#define QT_STRING(text) QString(li.codec->toUnicode(item.c_str())) +#else +#define HS_STRING(text) QByteArray(li.encoder(text)).toStdString() +#define QT_STRING(text) li.decoder(QByteArray::fromStdString(item)) +#endif + +HunspellChecker::HunspellChecker() +{ + getDictPaths(); + getSupportedLanguages(); +} + +HunspellChecker::~HunspellChecker() { } + +void HunspellChecker::getDictPaths() +{ + if (dictPaths_.isEmpty()) { + QSet dictPathSet; + QString pathFromEnv = QString::fromLocal8Bit(qgetenv("MYSPELL_DICT_DIR")); + if (!pathFromEnv.isEmpty()) + dictPathSet << pathFromEnv; +#if defined(Q_OS_WIN) + dictPathSet << QCoreApplication::applicationDirPath() + QLatin1String("/myspell/dicts") + << ApplicationInfo::homeDir(ApplicationInfo::DataLocation) + QLatin1String("/myspell/dicts"); + +#elif defined(Q_OS_MAC) + dictPathSet << QCoreApplication::applicationDirPath() + + QLatin1String("/../Resources/myspell/dicts") // relative path to dictionaries inside app bundle + << QLatin1String("/opt/local/share/myspell"); // MacPorts standard paths +#elif defined(Q_OS_HAIKU) + dictPathSet << QLatin1String("/system/data/hunspell"); +#else + dictPathSet << QLatin1String("/usr/share/myspell") << QLatin1String("/usr/share/hunspell") + << QLatin1String("/usr/local/share/myspell") << QLatin1String("/usr/local/share/hunspell") + << QString("%1/.local/share/myspell").arg(QDir::home().absolutePath()) + << QString("%1/.local/share/hunspell").arg(QDir::home().absolutePath()); +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + dictPaths_ = dictPathSet.values(); +#else + dictPaths_ = dictPathSet.toList(); +#endif +#if defined(Q_OS_LINUX) && defined(SHARE_SUFF) + // Special hack for correct work of AppImage, snap and flatpak builds + static const QString &&additionalPath + = QDir().absoluteFilePath(qApp->applicationDirPath() + "/../share/" SHARE_SUFF); + dictPaths_.prepend(additionalPath + "/hunspell"); + dictPaths_.prepend(additionalPath + "/myspell"); +#endif + } +} + +bool HunspellChecker::scanDictPaths(const QString &language, QFileInfo &aff, QFileInfo &dic) +{ + for (const QString &dictPath : std::as_const(dictPaths_)) { + QDir dir(dictPath); + if (dir.exists()) { + QFileInfo affInfo(dir.filePath(language + QLatin1String(".aff"))); + QFileInfo dicInfo(dir.filePath(language + QLatin1String(".dic"))); + if (affInfo.isReadable() && dicInfo.isReadable()) { + aff = affInfo; + dic = dicInfo; + return true; + } + } + } + return false; +} + +void HunspellChecker::getSupportedLanguages() +{ + QSet retHash; + for (const QString &dictPath : std::as_const(dictPaths_)) { + QDir dir(dictPath); + if (!dir.exists()) { + continue; + } + const auto fileInfoList = dir.entryInfoList(QStringList() << "*.dic", QDir::Files); + for (const QFileInfo &fi : fileInfoList) { + auto id = LanguageManager::fromString(fi.baseName()); + if (id.language) { + retHash.insert(id); + } + } + } + supportedLangs_ = retHash; +} + +void HunspellChecker::addLanguage(const LanguageManager::LangId &langId) +{ + QString language = LanguageManager::toString(langId).replace('-', '_'); + QFileInfo aff, dic; + if (scanDictPaths(language, aff, dic)) { + LangItem li; + // TODO on windows it makes sense to use "\\\\?\\" prefix to paths + li.hunspell_ = HunspellPtr(new Hunspell(aff.absoluteFilePath().toUtf8(), dic.absoluteFilePath().toLocal8Bit())); + QByteArray codecName(li.hunspell_->get_dic_encoding()); + if (codecName.startsWith("microsoft-cp125")) { + codecName.replace(0, sizeof("microsoft-cp") - 1, "Windows-"); + } else if (codecName.startsWith("TIS620-2533")) { + codecName.resize(sizeof("TIS620") - 1); + } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + li.codec = QTextCodec::codecForName(codecName); + if (li.codec) { +#else + li.encoder = QStringEncoder(codecName.data()); + li.decoder = QStringDecoder(codecName.data()); + if (li.encoder.isValid() && li.decoder.isValid()) { +#endif + li.info.langId = langId; + li.info.filename = dic.filePath(); + languages_.push_back(std::move(li)); + } else { + qDebug("Unsupported myspell dict encoding: \"%s\" for %s", codecName.data(), qPrintable(dic.fileName())); + } + } +} + +QList HunspellChecker::suggestions(const QString &word) +{ + QStringList qtResult; + for (LangItem &li : languages_) { + std::vector result = li.hunspell_->suggest(HS_STRING(word)); + if (!result.empty()) { + for (const std::string &item : result) { + qtResult << QT_STRING(item); // QString(li.codec->toUnicode(item.c_str())); + } + } + } + return std::move(qtResult); +} + +bool HunspellChecker::isCorrect(const QString &word) +{ + for (LangItem &li : languages_) { + if (li.hunspell_->spell(HS_STRING(word)) != 0) { + return true; + } + } + return false; +} +bool HunspellChecker::add(const QString &word) +{ + if (!word.isEmpty()) { + QString trimmed_word = word.trimmed(); + for (LangItem &li : languages_) { + if (li.hunspell_->add(HS_STRING(trimmed_word)) != 0) { + return true; + } + } + } + return false; +} +bool HunspellChecker::available() const +{ + for (const LangItem &li : languages_) { + if (li.hunspell_) { + return true; + } + } + return false; +} +bool HunspellChecker::writable() const { return false; } + +void HunspellChecker::unloadLanguage(const LanguageManager::LangId &langId) +{ + for (auto it = languages_.begin(); it != languages_.end();) { + if ((*it).info.langId == langId) + it = languages_.erase(it); + else + ++it; + } +} + +QSet HunspellChecker::getAllLanguages() const { return supportedLangs_; } + +void HunspellChecker::setActiveLanguages(const QSet &newLangs) +{ + QSet loadedLangs; + for (const LangItem &item : std::as_const(languages_)) { + loadedLangs << item.info.langId; + } + QSet langsToUnload = loadedLangs - newLangs; + QSet langsToLoad = newLangs - loadedLangs; + QSetIterator it(langsToUnload); + while (it.hasNext()) { + unloadLanguage(it.next()); + } + it = langsToLoad; + while (it.hasNext()) { + addLanguage(it.next()); + } +} diff --git a/src/libpsi/tools/spellchecker/hunspellchecker.h b/src/libpsi/tools/spellchecker/hunspellchecker.h new file mode 100644 index 000000000..24a986208 --- /dev/null +++ b/src/libpsi/tools/spellchecker/hunspellchecker.h @@ -0,0 +1,86 @@ +/* + * hunspellchecker.h + * + * Copyright (C) 2015 Sergey Ilinykh, Vitaly Tonkacheyev + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ +#ifndef HUNSPELLCHECKER_H +#define HUNSPELLCHECKER_H + +#include "languagemanager.h" +#include "spellchecker.h" + +#include +#include +#include +#include +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#include +#endif + +class Hunspell; +class QTextCodec; + +typedef QSharedPointer HunspellPtr; + +class HunspellChecker : public SpellChecker { +public: + HunspellChecker(); + ~HunspellChecker(); + virtual QList suggestions(const QString &); + virtual bool isCorrect(const QString &word); + virtual bool add(const QString &word); + virtual bool available() const; + virtual bool writable() const; + virtual void setActiveLanguages(const QSet &langs); + virtual QSet getAllLanguages() const; + +private: + struct DictInfo { + LanguageManager::LangId langId; + QString filename; + }; + struct LangItem { + HunspellPtr hunspell_; + DictInfo info; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QTextCodec *codec; +#else + QStringEncoder encoder; + QStringDecoder decoder; +#endif + }; + void getSupportedLanguages(); + void addLanguage(const LanguageManager::LangId &langId); + void getDictPaths(); + bool scanDictPaths(const QString &language, QFileInfo &aff, QFileInfo &dic); + void unloadLanguage(const LanguageManager::LangId &langId); + +private: + std::list languages_; + QStringList dictPaths_; + QSet supportedLangs_; +}; + +#endif // HUNSPELLCHECKER_H diff --git a/src/libpsi/tools/spellchecker/macspellchecker.h b/src/libpsi/tools/spellchecker/macspellchecker.h new file mode 100644 index 000000000..999f9fe5b --- /dev/null +++ b/src/libpsi/tools/spellchecker/macspellchecker.h @@ -0,0 +1,45 @@ +/* + * macspellchecker.h + * + * Copyright (C) 2006 Remko Troncon + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#ifndef MACSPELLCHECKER_H +#define MACSPELLCHECKER_H + +#include "spellchecker.h" + +#include +#include + +class MacSpellChecker : public SpellChecker { +public: + MacSpellChecker(); + ~MacSpellChecker(); + virtual QList suggestions(const QString &); + virtual bool isCorrect(const QString &); + virtual bool add(const QString &); + virtual bool available() const; + virtual bool writable() const; +}; + +#endif // MACSPELLCHECKER_H diff --git a/src/libpsi/tools/spellchecker/macspellchecker.mm b/src/libpsi/tools/spellchecker/macspellchecker.mm new file mode 100644 index 000000000..d02627aeb --- /dev/null +++ b/src/libpsi/tools/spellchecker/macspellchecker.mm @@ -0,0 +1,59 @@ +/* + * macspellchecker.cpp + * + * Copyright (C) 2006 Remko Troncon + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "macspellchecker.h" + +#include + +MacSpellChecker::MacSpellChecker() { } + +MacSpellChecker::~MacSpellChecker() { } + +bool MacSpellChecker::isCorrect(const QString &word) +{ + NSString *ns_word = [NSString stringWithUTF8String:word.toUtf8().data()]; + NSRange range = { 0, 0 }; + range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:ns_word startingAt:0]; + return (range.length == 0); +} + +QList MacSpellChecker::suggestions(const QString &word) +{ + QList s; + + NSString *ns_word = [NSString stringWithUTF8String:word.toUtf8().data()]; + NSArray *ns_suggestions = [[NSSpellChecker sharedSpellChecker] guessesForWord:ns_word]; + for (unsigned int i = 0; i < [ns_suggestions count]; i++) { + s += QString::fromUtf8([[ns_suggestions objectAtIndex:i] UTF8String]); + } + + return s; +} + +bool MacSpellChecker::add(const QString & /*word*/) { return false; } + +bool MacSpellChecker::available() const { return true; } + +bool MacSpellChecker::writable() const { return false; } diff --git a/src/libpsi/tools/spellchecker/spellchecker.cpp b/src/libpsi/tools/spellchecker/spellchecker.cpp new file mode 100644 index 000000000..4689e5e85 --- /dev/null +++ b/src/libpsi/tools/spellchecker/spellchecker.cpp @@ -0,0 +1,72 @@ +/* + * spellchecker.cpp + * + * Copyright (C) 2006 Remko Troncon + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#include "spellchecker.h" + +#if defined(Q_OS_MAC) && !defined(HAVE_HUNSPELL) +#include "macspellchecker.h" +#elif defined(HAVE_ENCHANT) +#include "enchantchecker.h" +#elif defined(HAVE_ASPELL) +#include "aspellchecker.h" +#elif defined(HAVE_HUNSPELL) +#include "hunspellchecker.h" +#endif + +#include + +SpellChecker *SpellChecker::instance() +{ + if (!instance_) { +#if defined(Q_OS_MAC) && !defined(HAVE_HUNSPELL) + instance_ = new MacSpellChecker(); +#elif defined(HAVE_ENCHANT) + instance_ = new EnchantChecker(); +#elif defined(HAVE_ASPELL) + instance_ = new ASpellChecker(); +#elif defined(HAVE_HUNSPELL) + instance_ = new HunspellChecker(); +#else + instance_ = new SpellChecker(); +#endif + } + return instance_; +} + +SpellChecker::SpellChecker() : QObject(QCoreApplication::instance()) { } + +SpellChecker::~SpellChecker() { } + +bool SpellChecker::available() const { return false; } + +bool SpellChecker::writable() const { return true; } + +bool SpellChecker::isCorrect(const QString &) { return true; } + +QList SpellChecker::suggestions(const QString &) { return QList(); } + +bool SpellChecker::add(const QString &) { return false; } + +SpellChecker *SpellChecker::instance_ = nullptr; diff --git a/src/libpsi/tools/spellchecker/spellchecker.h b/src/libpsi/tools/spellchecker/spellchecker.h new file mode 100644 index 000000000..7c27fb303 --- /dev/null +++ b/src/libpsi/tools/spellchecker/spellchecker.h @@ -0,0 +1,56 @@ +/* + * spellchecker.h + * + * Copyright (C) 2006 Remko Troncon + * + * 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 2 + * of the License, or (at your option) any later version. + * + * You can also redistribute and/or modify this program under the + * terms of the Psi License, specified in the accompanied COPYING + * file, as published by the Psi Project; either dated January 1st, + * 2005, 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 . + * + */ + +#ifndef SPELLCHECKER_H +#define SPELLCHECKER_H + +#include "languagemanager.h" + +#include +#include +#include +#include + +class SpellChecker : public QObject { +public: + static SpellChecker *instance(); + virtual bool available() const; + virtual bool writable() const; + virtual QList suggestions(const QString &); + virtual bool isCorrect(const QString &); + virtual bool add(const QString &); + + virtual void setActiveLanguages(const QSet &) { } + virtual QSet getAllLanguages() const { return QSet(); } + +protected: + SpellChecker(); + virtual ~SpellChecker(); + +private: + static SpellChecker *instance_; +}; + +#endif // SPELLCHECKER_H diff --git a/src/libpsi/tools/spellchecker/spellhighlighter.cpp b/src/libpsi/tools/spellchecker/spellhighlighter.cpp new file mode 100644 index 000000000..5292df161 --- /dev/null +++ b/src/libpsi/tools/spellchecker/spellhighlighter.cpp @@ -0,0 +1,32 @@ +#include "spellhighlighter.h" + +#include "common.h" +#include "spellchecker.h" +#include + +void SpellHighlighter::highlightBlock(const QString &text) +{ + // Underline + QTextCharFormat tcf; + tcf.setUnderlineColor(QColor(255, 0, 0)); + if (qVersionInt() >= 0x040400 && qVersionInt() < 0x040402) { + tcf.setUnderlineStyle(QTextCharFormat::DotLine); + } else { + tcf.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); + } + + // Match words (minimally) + QRegularExpression expression("\\b\\w+\\b", QRegularExpression::UseUnicodePropertiesOption); + QRegularExpression digit("^\\d+$"); + + // Iterate through all words + QRegularExpressionMatch match; + int index = text.indexOf(expression, 0, &match); + while (index >= 0) { + int length = match.capturedLength(); + QString word = match.captured(); + if (!digit.match(word).hasMatch() && !SpellChecker::instance()->isCorrect(word)) + setFormat(index, length, tcf); + index = text.indexOf(expression, index + length, &match); + } +} diff --git a/src/libpsi/tools/spellchecker/spellhighlighter.h b/src/libpsi/tools/spellchecker/spellhighlighter.h new file mode 100644 index 000000000..cc936830e --- /dev/null +++ b/src/libpsi/tools/spellchecker/spellhighlighter.h @@ -0,0 +1,15 @@ +#ifndef SPELLHIGHLIGHTER_H +#define SPELLHIGHLIGHTER_H + +#include + +class QString; + +class SpellHighlighter : public QSyntaxHighlighter { +public: + using QSyntaxHighlighter::QSyntaxHighlighter; + + virtual void highlightBlock(const QString &text); +}; + +#endif // SPELLHIGHLIGHTER_H diff --git a/src/libpsi/tools/systemwatch/systemwatch.cpp b/src/libpsi/tools/systemwatch/systemwatch.cpp new file mode 100644 index 000000000..cb9987945 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch.cpp @@ -0,0 +1,53 @@ +/* + * systemwatch.h - Detect changes in the system state. + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#include "systemwatch.h" + +#if defined(Q_OS_MAC) +#include "systemwatch_mac.h" +#elif defined(Q_OS_WIN32) +#include "systemwatch_win.h" +#else +#include "systemwatch_unix.h" +#endif + +#include + +SystemWatch::SystemWatch() : QObject(qApp) { } + +SystemWatch *SystemWatch::instance() +{ + if (!instance_) { +#if defined(Q_OS_MAC) + instance_ = new MacSystemWatch(); +#elif defined(Q_OS_WIN) + instance_ = new WinSystemWatch(); +#else + instance_ = new UnixSystemWatch(); +#endif + } + return instance_; +} + +void SystemWatch::proceedWithSleep() +{ + // do nothing in base impl +} + +SystemWatch *SystemWatch::instance_ = nullptr; diff --git a/src/libpsi/tools/systemwatch/systemwatch.h b/src/libpsi/tools/systemwatch/systemwatch.h new file mode 100644 index 000000000..aedd6b703 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch.h @@ -0,0 +1,44 @@ +/* + * systemwatch.h - Detect changes in the system state. + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#ifndef SYSTEMWATCH_H +#define SYSTEMWATCH_H + +#include + +class SystemWatch : public QObject { + Q_OBJECT + +public: + static SystemWatch *instance(); + virtual void proceedWithSleep(); + +signals: + void sleep(); + void idleSleep(); + void wakeup(); + +protected: + SystemWatch(); + +private: + static SystemWatch *instance_; +}; + +#endif // SYSTEMWATCH_H diff --git a/src/libpsi/tools/systemwatch/systemwatch_mac.cpp b/src/libpsi/tools/systemwatch/systemwatch_mac.cpp new file mode 100644 index 000000000..ba5aa60a9 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch_mac.cpp @@ -0,0 +1,83 @@ +/* + * systemwatch_mac.cpp - Detect changes in the system state (Mac OS X). + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#include "systemwatch_mac.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +// ----------------------------------------------------------------------------- +// Callbacks +// ----------------------------------------------------------------------------- + +io_connect_t root_port; + +void sleepCallBack(void *systemWatch, io_service_t, natural_t messageType, void *messageArgument) +{ + // printf("messageType %08lx, arg %08lx\n", + // (long unsigned int)messageType, (long unsigned int)messageArgument); + + MacSystemWatch *macSystemWatch = static_cast(systemWatch); + switch (messageType) { + case kIOMessageSystemWillSleep: + // Sleep + macSystemWatch->emitSleep(); + IOAllowPowerChange(root_port, (long)messageArgument); + break; + + case kIOMessageCanSystemSleep: + // Idle time sleep + + // TODO: Check if file transfers are running, and don't go to sleep + // if there are. + // IOCancelPowerChange(root_port, (long)messageArgument); + + macSystemWatch->emitIdleSleep(); + IOAllowPowerChange(root_port, (long)messageArgument); + break; + + case kIOMessageSystemHasPoweredOn: + // Wakeup + macSystemWatch->emitWakeup(); + break; + } +} + +// ----------------------------------------------------------------------------- +// MacSystemWatch +// ----------------------------------------------------------------------------- + +MacSystemWatch::MacSystemWatch() +{ + // Initialize sleep callback + IONotificationPortRef notify; + io_object_t anIterator; + root_port = IORegisterForSystemPower(this, ¬ify, sleepCallBack, &anIterator); + if (!root_port) { + printf("IORegisterForSystemPower failed\n"); + } else { + CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notify), kCFRunLoopCommonModes); + } +} diff --git a/src/libpsi/tools/systemwatch/systemwatch_mac.h b/src/libpsi/tools/systemwatch/systemwatch_mac.h new file mode 100644 index 000000000..be1230a37 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch_mac.h @@ -0,0 +1,35 @@ +/* + * systemwatch_mac.h - Detect changes in the system state (Mac OS X). + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#ifndef SYSTEMWATCH_MAC_H +#define SYSTEMWATCH_MAC_H + +#include "systemwatch.h" + +class MacSystemWatch : public SystemWatch { +public: + MacSystemWatch(); + + // These shouldn't be called from outside, but i can't hide them atm. + void emitSleep() { emit sleep(); } + void emitIdleSleep() { emit idleSleep(); } + void emitWakeup() { emit wakeup(); } +}; + +#endif // SYSTEMWATCH_MAC_H diff --git a/src/libpsi/tools/systemwatch/systemwatch_unix.cpp b/src/libpsi/tools/systemwatch/systemwatch_unix.cpp new file mode 100644 index 000000000..51fbfcdb5 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch_unix.cpp @@ -0,0 +1,90 @@ +/* + * systemwatch_unix.h - Detect changes in the system state (Unix). + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#include "systemwatch_unix.h" + +#include "applicationinfo.h" + +#ifdef USE_DBUS +#include +#include +#include +#include +#endif +#include + +UnixSystemWatch::UnixSystemWatch() +{ +#ifdef USE_DBUS + // TODO: check which service we should listen to + QDBusConnection conn = QDBusConnection::systemBus(); + // listen to systemd's logind + // TODO: use delaying Inhibitor locks + conn.connect("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", + "PrepareForSleep", this, SLOT(prepareForSleep(bool))); + // listen to UPower + conn.connect("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "Sleeping", this, + SLOT(sleeping())); + conn.connect("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "Resuming", this, + SLOT(resuming())); + + takeSleepLock(); +#endif +} + +void UnixSystemWatch::takeSleepLock() +{ +#ifdef USE_DBUS + /* dbus-send --system --print-reply --dest=org.freedesktop.login1 /org/freedesktop/login1 \ + "org.freedesktop.login1.Manager.Inhibit" string:"sleep" string:"Psi" \ + string:"Closing connections..." string:"delay" + */ + QDBusInterface login1iface("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", + QDBusConnection::systemBus()); + QDBusReply repl + = login1iface.call("Inhibit", "sleep", ApplicationInfo::name(), "Closing connections...", "delay"); + // we could delay "shutdown" as well probably Qt session manager already can do this + if (repl.isValid()) { + lockFd = repl.value(); + } else { + lockFd = QDBusUnixFileDescriptor(); + } +#endif +} + +void UnixSystemWatch::proceedWithSleep() +{ +#ifdef USE_DBUS + lockFd = QDBusUnixFileDescriptor(); // null descriptor should release it +#endif +} + +void UnixSystemWatch::prepareForSleep(bool beforeSleep) +{ + if (beforeSleep) { + emit sleep(); + } else { + emit wakeup(); + takeSleepLock(); + } +} + +void UnixSystemWatch::sleeping() { emit sleep(); } + +void UnixSystemWatch::resuming() { emit wakeup(); } diff --git a/src/libpsi/tools/systemwatch/systemwatch_unix.h b/src/libpsi/tools/systemwatch/systemwatch_unix.h new file mode 100644 index 000000000..17827f4a5 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch_unix.h @@ -0,0 +1,47 @@ +/* + * systemwatch_unix.h - Detect changes in the system state (Unix). + * Copyright (C) 2005 Remko Troncon + * + * 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 2 + * 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 . + * + */ + +#ifndef SYSTEMWATCH_UNIX_H +#define SYSTEMWATCH_UNIX_H + +#include "systemwatch.h" + +#ifdef USE_DBUS +#include +#endif + +class UnixSystemWatch : public SystemWatch { + Q_OBJECT + +#ifdef USE_DBUS + QDBusUnixFileDescriptor lockFd; // systemd inhibitor for sleep. system goes to sleep as soon as lock is closed +#endif + void takeSleepLock(); + +public: + UnixSystemWatch(); + void proceedWithSleep(); + +private slots: + void prepareForSleep(bool beforeSleep); + void sleeping(); + void resuming(); +}; + +#endif // SYSTEMWATCH_UNIX_H diff --git a/src/libpsi/tools/systemwatch/systemwatch_win.cpp b/src/libpsi/tools/systemwatch/systemwatch_win.cpp new file mode 100644 index 000000000..e367135d7 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch_win.cpp @@ -0,0 +1,114 @@ +/* + * systemwatch_win.cpp - Detect changes in the system state (Windows). + * Copyright (C) 2005, 2008 James Chaldecott, Maciej Niedzielski + * + * 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 2 + * 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 . + * + */ + +#include "systemwatch_win.h" + +#include +#include +#include +#include + +// workaround for the very old MinGW version bundled with Qt +#ifndef PBT_APMSUSPEND +#include +#endif + +/* + Implementor notes: + + This class needs to get Windows messages. + The easiest way is to get them from a top level QWidget instance. + There was an attempt to use QApplication::winEventFilter(), + but - as its name says - this is a filter, so all messages to + all widgets go through it. So as a consequence, sleep() and wakeup() + are emited many times during one event. + + Right now, there is a dummy window created just for SystemWatch. + This may seem to be an unnecesary waste of resources, but the example + above shows that too aggressive optimizations may hurt. + A possible solution "in between" would be to catch events in already + existing window (main window, probably) + and pass them (by using ugly casting) directly to processWinEvent() + But this would break the beauty of this tool. +*/ + +// ----------------------------------------------------------------------------- +// WinSystemWatch +// ----------------------------------------------------------------------------- + +class WinSystemWatch::EventFilter : public QAbstractNativeEventFilter { + WinSystemWatch *syswatch; + +public: + EventFilter(WinSystemWatch *parent) : syswatch(parent) { qApp->installNativeEventFilter(this); } + + virtual bool nativeEventFilter(const QByteArray &eventType, void *m, long *result) Q_DECL_OVERRIDE + { + if (eventType == "windows_generic_MSG") { + return syswatch->processWinEvent(static_cast(m), result); + } + return false; + } +}; + +WinSystemWatch::WinSystemWatch() { d = new EventFilter(this); } + +WinSystemWatch::~WinSystemWatch() +{ + delete d; + d = 0; +} + +bool WinSystemWatch::processWinEvent(MSG *m, long *result) +{ + Q_UNUSED(result); + if (WM_POWERBROADCAST == m->message) { + switch (m->wParam) { + case PBT_APMSUSPEND: + emit sleep(); + break; + + case PBT_APMRESUMESUSPEND: + emit wakeup(); + break; + + case PBT_APMRESUMECRITICAL: + // The system previously went into SUSPEND state (suddenly) + // without sending PBT_APMSUSPEND. Net connections are + // probably invalid. Not sure what to do about this. + // Maybe: + emit sleep(); + emit wakeup(); + break; + + case PBT_APMQUERYSUSPEND: + // TODO: Check if file transfers are running, and don't go + // to sleep if there are. To refuse to suspend, we somehow + // need to return BROADCAST_QUERY_DENY from the actual + // windows procedure. + break; + } + } else if (WM_QUERYENDSESSION == m->message) { + // TODO : If we allow the user to cancel suspend if they + // are doing a file transfer, we should probably also give + // them the chance to cancel a shutdown or log-off + } + + return false; // Let Qt handle the right return value. +} diff --git a/src/libpsi/tools/systemwatch/systemwatch_win.h b/src/libpsi/tools/systemwatch/systemwatch_win.h new file mode 100644 index 000000000..97eb29975 --- /dev/null +++ b/src/libpsi/tools/systemwatch/systemwatch_win.h @@ -0,0 +1,19 @@ +#ifndef SYSTEMWATCH_WIN_H +#define SYSTEMWATCH_WIN_H + +#include "systemwatch.h" + +#include + +class WinSystemWatch : public SystemWatch { +public: + WinSystemWatch(); + ~WinSystemWatch(); + +private: + class EventFilter; + EventFilter *d; + bool processWinEvent(MSG *m, long *result); +}; + +#endif // SYSTEMWATCH_WIN_H diff --git a/src/libpsi/tools/unittest/iodeviceopenertest.cpp b/src/libpsi/tools/unittest/iodeviceopenertest.cpp new file mode 100644 index 000000000..6d9a6e71c --- /dev/null +++ b/src/libpsi/tools/unittest/iodeviceopenertest.cpp @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2007 Remko Troncon + */ + +#include "utilities/iodeviceopener.h" +#include "qttestutil/qttestutil.h" + +#include +#include +#include +#include +#include + +class IODeviceOpenerTest : public QObject { + Q_OBJECT + +private slots: + void testConstructor() + { + QBuffer buffer; + + IODeviceOpener opener(&buffer, QIODevice::WriteOnly); + + QVERIFY(buffer.isOpen()); + QVERIFY(QIODevice::WriteOnly == buffer.openMode()); + QVERIFY(opener.isOpen()); + } + + void testConstructor_Open() + { + QBuffer buffer; + buffer.open(QIODevice::ReadOnly); + + IODeviceOpener opener(&buffer, QIODevice::ReadOnly); + + QVERIFY(buffer.isOpen()); + QVERIFY(QIODevice::ReadOnly == buffer.openMode()); + QVERIFY(opener.isOpen()); + } + + void testConstructor_OpenInSubsetMode() + { + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + + IODeviceOpener opener(&buffer, QIODevice::WriteOnly); + + QVERIFY(buffer.isOpen()); + QVERIFY(QIODevice::ReadWrite == buffer.openMode()); + QVERIFY(opener.isOpen()); + } + + void testConstructor_OpenInWrongMode() + { + QBuffer buffer; + buffer.open(QIODevice::ReadOnly); + + IODeviceOpener opener(&buffer, QIODevice::WriteOnly); + + QVERIFY(buffer.isOpen()); + QVERIFY(QIODevice::ReadOnly == buffer.openMode()); + QVERIFY(!opener.isOpen()); + } + + void testConstructor_Unopenable() + { + UnopenableBuffer buffer; + + IODeviceOpener opener(&buffer, QIODevice::WriteOnly); + + QVERIFY(!opener.isOpen()); + } + + void testDestructor() + { + QBuffer buffer; + + { + IODeviceOpener opener(&buffer, QIODevice::WriteOnly); + } + + QVERIFY(!buffer.isOpen()); + } + + void testDestructor_Open() + { + QBuffer buffer; + buffer.open(QIODevice::ReadOnly); + + { + IODeviceOpener opener(&buffer, QIODevice::ReadOnly); + } + + QVERIFY(buffer.isOpen()); + } + + void testDestructor_OpenInWrongMode() + { + QBuffer buffer; + buffer.open(QIODevice::ReadOnly); + + { + IODeviceOpener opener(&buffer, QIODevice::WriteOnly); + } + + QVERIFY(buffer.isOpen()); + QVERIFY(QIODevice::ReadOnly == buffer.openMode()); + } + +private: + class UnopenableBuffer : public QBuffer { + public: + UnopenableBuffer() : QBuffer() { } + + bool open(QIODevice::OpenMode) { return false; } + }; +}; + +QTTESTUTIL_REGISTER_TEST(IODeviceOpenerTest); +#include "iodeviceopenertest.moc" diff --git a/src/libpsi/tools/zip/CMakeLists.txt b/src/libpsi/tools/zip/CMakeLists.txt new file mode 100644 index 000000000..10b8a217e --- /dev/null +++ b/src/libpsi/tools/zip/CMakeLists.txt @@ -0,0 +1,46 @@ +unset(HEADERS) +unset(FORMS) +unset(SOURCES) +unset(UI_FORMS) +unset(EXTRA_LDFLAGS) + +find_package(ZLIB REQUIRED) + +include_directories(${ZLIB_INCLUDE_DIRS}) + +find_package(MINIZIP QUIET) + +set(DEPLIBS + ${ZLIB_LIBRARIES} + ) + +if(NOT MINIZIP_FOUND) + set(USE_PSI_MINIZIP ON) +else() + include_directories(${MINIZIP_INCLUDE_DIR}) + set(DEPLIBS + ${DEPLIBS} + ${MINIZIP_LIBRARY} + ) +endif() + +set(PLAIN_HEADERS + zip.h + ) + +set(PLAIN_SOURCES + zip.cpp + ) + +if(USE_PSI_MINIZIP) + set(PLAIN_SOURCES + ${PLAIN_SOURCES} + minizip/unzip.c + minizip/ioapi.c + ) + add_definitions(-DPSIMINIZIP) +endif() + +add_library(zip STATIC ${PLAIN_SOURCES} ${PLAIN_HEADERS}) +target_link_libraries(zip ${QT_LIBRARIES} ${DEPLIBS}) +target_include_directories(zip PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} minizip ..) diff --git a/src/libpsi/tools/zip/minizip/Makefile b/src/libpsi/tools/zip/minizip/Makefile new file mode 100644 index 000000000..84eaad20d --- /dev/null +++ b/src/libpsi/tools/zip/minizip/Makefile @@ -0,0 +1,25 @@ +CC=cc +CFLAGS=-O -I../.. + +UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a +ZIP_OBJS = minizip.o zip.o ioapi.o ../../libz.a + +.c.o: + $(CC) -c $(CFLAGS) $*.c + +all: miniunz minizip + +miniunz: $(UNZ_OBJS) + $(CC) $(CFLAGS) -o $@ $(UNZ_OBJS) + +minizip: $(ZIP_OBJS) + $(CC) $(CFLAGS) -o $@ $(ZIP_OBJS) + +test: miniunz minizip + ./minizip test readme.txt + ./miniunz -l test.zip + mv readme.txt readme.old + ./miniunz test.zip + +clean: + /bin/rm -f *.o *~ minizip miniunz diff --git a/src/libpsi/tools/zip/minizip/Makefile.am b/src/libpsi/tools/zip/minizip/Makefile.am new file mode 100644 index 000000000..d343011eb --- /dev/null +++ b/src/libpsi/tools/zip/minizip/Makefile.am @@ -0,0 +1,45 @@ +lib_LTLIBRARIES = libminizip.la + +if COND_DEMOS +bin_PROGRAMS = miniunzip minizip +endif + +zlib_top_srcdir = $(top_srcdir)/../.. +zlib_top_builddir = $(top_builddir)/../.. + +AM_CPPFLAGS = -I$(zlib_top_srcdir) +AM_LDFLAGS = -L$(zlib_top_builddir) + +if WIN32 +iowin32_src = iowin32.c +iowin32_h = iowin32.h +endif + +libminizip_la_SOURCES = \ + ioapi.c \ + mztools.c \ + unzip.c \ + zip.c \ + ${iowin32_src} + +libminizip_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:0:0 -lz + +minizip_includedir = $(includedir)/minizip +minizip_include_HEADERS = \ + crypt.h \ + ioapi.h \ + mztools.h \ + unzip.h \ + zip.h \ + ${iowin32_h} + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = minizip.pc + +EXTRA_PROGRAMS = miniunzip minizip + +miniunzip_SOURCES = miniunz.c +miniunzip_LDADD = libminizip.la + +minizip_SOURCES = minizip.c +minizip_LDADD = libminizip.la -lz diff --git a/src/libpsi/tools/zip/minizip/MiniZip64_Changes.txt b/src/libpsi/tools/zip/minizip/MiniZip64_Changes.txt new file mode 100644 index 000000000..13a1bd91a --- /dev/null +++ b/src/libpsi/tools/zip/minizip/MiniZip64_Changes.txt @@ -0,0 +1,6 @@ + +MiniZip 1.1 was derrived from MiniZip at version 1.01f + +Change in 1.0 (Okt 2009) + - **TODO - Add history** + diff --git a/src/libpsi/tools/zip/minizip/MiniZip64_info.txt b/src/libpsi/tools/zip/minizip/MiniZip64_info.txt new file mode 100644 index 000000000..57d715242 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/MiniZip64_info.txt @@ -0,0 +1,74 @@ +MiniZip - Copyright (c) 1998-2010 - by Gilles Vollant - version 1.1 64 bits from Mathias Svensson + +Introduction +--------------------- +MiniZip 1.1 is built from MiniZip 1.0 by Gilles Vollant ( http://www.winimage.com/zLibDll/minizip.html ) + +When adding ZIP64 support into minizip it would result into risk of breaking compatibility with minizip 1.0. +All possible work was done for compatibility. + + +Background +--------------------- +When adding ZIP64 support Mathias Svensson found that Even Rouault have added ZIP64 +support for unzip.c into minizip for a open source project called gdal ( http://www.gdal.org/ ) + +That was used as a starting point. And after that ZIP64 support was added to zip.c +some refactoring and code cleanup was also done. + + +Changed from MiniZip 1.0 to MiniZip 1.1 +--------------------------------------- +* Added ZIP64 support for unzip ( by Even Rouault ) +* Added ZIP64 support for zip ( by Mathias Svensson ) +* Reverted some changed that Even Rouault did. +* Bunch of patches received from Gulles Vollant that he received for MiniZip from various users. +* Added unzip patch for BZIP Compression method (patch create by Daniel Borca) +* Added BZIP Compress method for zip +* Did some refactoring and code cleanup + + +Credits + + Gilles Vollant - Original MiniZip author + Even Rouault - ZIP64 unzip Support + Daniel Borca - BZip Compression method support in unzip + Mathias Svensson - ZIP64 zip support + Mathias Svensson - BZip Compression method support in zip + + Resources + + ZipLayout http://result42.com/projects/ZipFileLayout + Command line tool for Windows that shows the layout and information of the headers in a zip archive. + Used when debugging and validating the creation of zip files using MiniZip64 + + + ZIP App Note http://www.pkware.com/documents/casestudies/APPNOTE.TXT + Zip File specification + + +Notes. + * To be able to use BZip compression method in zip64.c or unzip64.c the BZIP2 lib is needed and HAVE_BZIP2 need to be defined. + +License +---------------------------------------------------------- + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +---------------------------------------------------------- + diff --git a/src/libpsi/tools/zip/minizip/configure.ac b/src/libpsi/tools/zip/minizip/configure.ac new file mode 100644 index 000000000..5b1197097 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/configure.ac @@ -0,0 +1,32 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_INIT([minizip], [1.2.11], [bugzilla.redhat.com]) +AC_CONFIG_SRCDIR([minizip.c]) +AM_INIT_AUTOMAKE([foreign]) +LT_INIT + +AC_MSG_CHECKING([whether to build example programs]) +AC_ARG_ENABLE([demos], AC_HELP_STRING([--enable-demos], [build example programs])) +AM_CONDITIONAL([COND_DEMOS], [test "$enable_demos" = yes]) +if test "$enable_demos" = yes +then + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + +case "${host}" in + *-mingw* | mingw*) + WIN32="yes" + ;; + *) + ;; +esac +AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"]) + + +AC_SUBST([HAVE_UNISTD_H], [0]) +AC_CHECK_HEADER([unistd.h], [HAVE_UNISTD_H=1], []) +AC_CONFIG_FILES([Makefile minizip.pc]) +AC_OUTPUT diff --git a/src/libpsi/tools/zip/minizip/crypt.h b/src/libpsi/tools/zip/minizip/crypt.h new file mode 100644 index 000000000..1e9e8200b --- /dev/null +++ b/src/libpsi/tools/zip/minizip/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const z_crc_t* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const z_crc_t* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const z_crc_t* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/src/libpsi/tools/zip/minizip/ioapi.c b/src/libpsi/tools/zip/minizip/ioapi.c new file mode 100644 index 000000000..7f5c191b2 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/ioapi.c @@ -0,0 +1,247 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(__APPLE__) || defined(IOAPI_NO_64) +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == MAXU32) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = FOPEN_FUNC((const char*)filename, mode_fopen); + return file; +} + + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = FTELLO_FUNC((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/libpsi/tools/zip/minizip/ioapi.h b/src/libpsi/tools/zip/minizip/ioapi.h new file mode 100644 index 000000000..544a0e992 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/ioapi.h @@ -0,0 +1,213 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif + +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) || defined(__HAIKU__) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef __FreeBSD__ +#define fopen64 fopen +#define ftello64 ftello +#define fseeko64 fseeko +#endif +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#define MAXU32 0xffffffff + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +// Workaround for cppcheck error: syntax error +#ifndef ZPOS64_T +typedef unsigned long long int ZPOS64_T; +#endif + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libpsi/tools/zip/minizip/iowin32.c b/src/libpsi/tools/zip/minizip/iowin32.c new file mode 100644 index 000000000..274f39eb1 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/iowin32.c @@ -0,0 +1,462 @@ +/* iowin32.c -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + + +// see Include/shared/winapifamily.h in the Windows Kit +#if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API))) +#if WINAPI_FAMILY_ONE_PARTITION(WINAPI_FAMILY, WINAPI_PARTITION_APP) +#define IOWIN32_USING_WINRT_API 1 +#endif +#endif + +voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); +uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +uLong ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +ZPOS64_T ZCALLBACK win32_tell64_file_func OF((voidpf opaque, voidpf stream)); +long ZCALLBACK win32_seek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +int ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream)); +int ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + + +static void win32_translate_open_mode(int mode, + DWORD* lpdwDesiredAccess, + DWORD* lpdwCreationDisposition, + DWORD* lpdwShareMode, + DWORD* lpdwFlagsAndAttributes) +{ + *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + *lpdwDesiredAccess = GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + *lpdwShareMode = FILE_SHARE_READ; + } + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + } + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = CREATE_ALWAYS; + } +} + +static voidpf win32_build_iowin(HANDLE hFile) +{ + voidpf ret=NULL; + + if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + + if (ret==NULL) + CloseHandle(hFile); + else + *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + +voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API +#ifdef UNICODE + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#endif +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCWSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition,NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + +#ifdef IOWIN32_USING_WINRT_API +#ifdef UNICODE + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile2((LPCTSTR)filename, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + { + WCHAR filenameW[FILENAME_MAX + 0x200 + 1]; + MultiByteToWideChar(CP_ACP,0,(const char*)filename,-1,filenameW,FILENAME_MAX + 0x200); + hFile = CreateFile2(filenameW, dwDesiredAccess, dwShareMode, dwCreationDisposition, NULL); + } +#endif +#else + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); +#endif + + return win32_build_iowin(hFile); +} + + +uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + + +uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + +static BOOL MySetFilePointerEx(HANDLE hFile, LARGE_INTEGER pos, LARGE_INTEGER *newPos, DWORD dwMoveMethod) +{ +#ifdef IOWIN32_USING_WINRT_API + return SetFilePointerEx(hFile, pos, newPos, dwMoveMethod); +#else + LONG lHigh = pos.HighPart; + DWORD dwNewPos = SetFilePointer(hFile, pos.LowPart, &lHigh, dwMoveMethod); + BOOL fOk = TRUE; + if (dwNewPos == 0xFFFFFFFF) + if (GetLastError() != NO_ERROR) + fOk = FALSE; + if ((newPos != NULL) && (fOk)) + { + newPos->LowPart = dwNewPos; + newPos->HighPart = lHigh; + } + return fOk; +#endif +} + +long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream) +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + LARGE_INTEGER pos; + pos.QuadPart = 0; + + if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)pos.LowPart; + } + return ret; +} + +ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret= (ZPOS64_T)-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + if (hFile) + { + LARGE_INTEGER pos; + pos.QuadPart = 0; + + if (!MySetFilePointerEx(hFile, pos, &pos, FILE_CURRENT)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = (ZPOS64_T)-1; + } + else + ret=pos.QuadPart; + } + return ret; +} + + +long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + LARGE_INTEGER pos; + pos.QuadPart = offset; + if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + long ret=-1; + + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile) + { + LARGE_INTEGER pos; + pos.QuadPart = offset; + if (!MySetFilePointerEx(hFile, pos, NULL, dwMoveMethod)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream) +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream) +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/src/libpsi/tools/zip/minizip/iowin32.h b/src/libpsi/tools/zip/minizip/iowin32.h new file mode 100644 index 000000000..0ca0969a7 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/iowin32.h @@ -0,0 +1,28 @@ +/* iowin32.h -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif diff --git a/src/libpsi/tools/zip/minizip/miniunz.c b/src/libpsi/tools/zip/minizip/miniunz.c new file mode 100644 index 000000000..3d65401be --- /dev/null +++ b/src/libpsi/tools/zip/minizip/miniunz.c @@ -0,0 +1,660 @@ +/* + miniunz.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) +*/ + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#ifdef __APPLE__ +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +#endif + + +#include "unzip.h" + +#define CASESENSITIVITY (0) +#define WRITEBUFFERSIZE (8192) +#define MAXFILENAME (256) + +#ifdef _WIN32 +#define USEWIN32IOAPI +#include "iowin32.h" +#endif +/* + mini unzip, demo of unzip package + + usage : + Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir] + + list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT + if it exists +*/ + + +/* change_file_date : change the date/time of a file + filename : the filename of the file where date/time must be modified + dosdate : the new date at the MSDos format (4 bytes) + tmu_date : the SAME new date at the tm_unz format */ +void change_file_date(filename,dosdate,tmu_date) + const char *filename; + uLong dosdate; + tm_unz tmu_date; +{ +#ifdef _WIN32 + HANDLE hFile; + FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite; + + hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE, + 0,NULL,OPEN_EXISTING,0,NULL); + GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite); + DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal); + LocalFileTimeToFileTime(&ftLocal,&ftm); + SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); + CloseHandle(hFile); +#else +#ifdef unix || __APPLE__ + struct utimbuf ut; + struct tm newdate; + newdate.tm_sec = tmu_date.tm_sec; + newdate.tm_min=tmu_date.tm_min; + newdate.tm_hour=tmu_date.tm_hour; + newdate.tm_mday=tmu_date.tm_mday; + newdate.tm_mon=tmu_date.tm_mon; + if (tmu_date.tm_year > 1900) + newdate.tm_year=tmu_date.tm_year - 1900; + else + newdate.tm_year=tmu_date.tm_year ; + newdate.tm_isdst=-1; + + ut.actime=ut.modtime=mktime(&newdate); + utime(filename,&ut); +#endif +#endif +} + + +/* mymkdir and change_file_date are not 100 % portable + As I don't know well Unix, I wait feedback for the unix portion */ + +int mymkdir(dirname) + const char* dirname; +{ + int ret=0; +#ifdef _WIN32 + ret = _mkdir(dirname); +#elif unix + ret = mkdir (dirname,0775); +#elif __APPLE__ + ret = mkdir (dirname,0775); +#endif + return ret; +} + +int makedir (newdir) + char *newdir; +{ + char *buffer ; + char *p; + int len = (int)strlen(newdir); + + if (len <= 0) + return 0; + + buffer = (char*)malloc(len+1); + if (buffer==NULL) + { + printf("Error allocating memory\n"); + return UNZ_INTERNALERROR; + } + strcpy(buffer,newdir); + + if (buffer[len-1] == '/') { + buffer[len-1] = '\0'; + } + if (mymkdir(buffer) == 0) + { + free(buffer); + return 1; + } + + p = buffer+1; + while (1) + { + char hold; + + while(*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + if ((mymkdir(buffer) == -1) && (errno == ENOENT)) + { + printf("couldn't create directory %s\n",buffer); + free(buffer); + return 0; + } + if (hold == 0) + break; + *p++ = hold; + } + free(buffer); + return 1; +} + +void do_banner() +{ + printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n"); + printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n"); +} + +void do_help() +{ + printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \ + " -e Extract without pathname (junk paths)\n" \ + " -x Extract with pathname\n" \ + " -v list files\n" \ + " -l list files\n" \ + " -d directory to extract into\n" \ + " -o overwrite files without prompting\n" \ + " -p extract crypted file using password\n\n"); +} + +void Display64BitsSize(ZPOS64_T n, int size_char) +{ + /* to avoid compatibility problem , we do here the conversion */ + char number[21]; + int offset=19; + int pos_string = 19; + number[20]=0; + for (;;) { + number[offset]=(char)((n%10)+'0'); + if (number[offset] != '0') + pos_string=offset; + n/=10; + if (offset==0) + break; + offset--; + } + { + int size_display_string = 19-pos_string; + while (size_char > size_display_string) + { + size_char--; + printf(" "); + } + } + + printf("%s",&number[pos_string]); +} + +int do_list(uf) + unzFile uf; +{ + uLong i; + unz_global_info64 gi; + int err; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); + printf(" ------ ------ ---- ----- ---- ---- ------ ----\n"); + for (i=0;i0) + ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size); + + /* display a '*' if the file is crypted */ + if ((file_info.flag & 1) != 0) + charCrypt='*'; + + if (file_info.compression_method==0) + string_method="Stored"; + else + if (file_info.compression_method==Z_DEFLATED) + { + uInt iLevel=(uInt)((file_info.flag & 0x6)/2); + if (iLevel==0) + string_method="Defl:N"; + else if (iLevel==1) + string_method="Defl:X"; + else if ((iLevel==2) || (iLevel==3)) + string_method="Defl:F"; /* 2:fast , 3 : extra fast*/ + } + else + if (file_info.compression_method==Z_BZIP2ED) + { + string_method="BZip2 "; + } + else + string_method="Unkn. "; + + Display64BitsSize(file_info.uncompressed_size,7); + printf(" %6s%c",string_method,charCrypt); + Display64BitsSize(file_info.compressed_size,7); + printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n", + ratio, + (uLong)file_info.tmu_date.tm_mon + 1, + (uLong)file_info.tmu_date.tm_mday, + (uLong)file_info.tmu_date.tm_year % 100, + (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min, + (uLong)file_info.crc,filename_inzip); + if ((i+1)='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + } + + if (rep == 'N') + skip = 1; + + if (rep == 'A') + *popt_overwrite=1; + } + + if ((skip==0) && (err==UNZ_OK)) + { + fout=FOPEN_FUNC(write_filename,"wb"); + /* some zipfile don't contain directory alone before file */ + if ((fout==NULL) && ((*popt_extract_without_path)==0) && + (filename_withoutpath!=(char*)filename_inzip)) + { + char c=*(filename_withoutpath-1); + *(filename_withoutpath-1)='\0'; + makedir(write_filename); + *(filename_withoutpath-1)=c; + fout=FOPEN_FUNC(write_filename,"wb"); + } + + if (fout==NULL) + { + printf("error opening %s\n",write_filename); + } + } + + if (fout!=NULL) + { + printf(" extracting: %s\n",write_filename); + + do + { + err = unzReadCurrentFile(uf,buf,size_buf); + if (err<0) + { + printf("error %d with zipfile in unzReadCurrentFile\n",err); + break; + } + if (err>0) + if (fwrite(buf,err,1,fout)!=1) + { + printf("error in writing extracted file\n"); + err=UNZ_ERRNO; + break; + } + } + while (err>0); + if (fout) + fclose(fout); + + if (err==0) + change_file_date(write_filename,file_info.dosDate, + file_info.tmu_date); + } + + if (err==UNZ_OK) + { + err = unzCloseCurrentFile (uf); + if (err!=UNZ_OK) + { + printf("error %d with zipfile in unzCloseCurrentFile\n",err); + } + } + else + unzCloseCurrentFile(uf); /* don't lose the error */ + } + + free(buf); + return err; +} + + +int do_extract(uf,opt_extract_without_path,opt_overwrite,password) + unzFile uf; + int opt_extract_without_path; + int opt_overwrite; + const char* password; +{ + uLong i; + unz_global_info64 gi; + int err; + FILE* fout=NULL; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + + for (i=0;i insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +miniunzip - uncompress and examine ZIP archives +.SH SYNOPSIS +.B miniunzip +.RI [ -exvlo ] +zipfile [ files_to_extract ] [-d tempdir] +.SH DESCRIPTION +.B minizip +is a simple tool which allows the extraction of compressed file +archives in the ZIP format used by the MS-DOS utility PKZIP. It was +written as a demonstration of the +.IR zlib (3) +library and therefore lack many of the features of the +.IR unzip (1) +program. +.SH OPTIONS +A number of options are supported. With the exception of +.BI \-d\ tempdir +these must be supplied before any +other arguments and are: +.TP +.BI \-l\ ,\ \-\-v +List the files in the archive without extracting them. +.TP +.B \-o +Overwrite files without prompting for confirmation. +.TP +.B \-x +Extract files (default). +.PP +The +.I zipfile +argument is the name of the archive to process. The next argument can be used +to specify a single file to extract from the archive. + +Lastly, the following option can be specified at the end of the command-line: +.TP +.BI \-d\ tempdir +Extract the archive in the directory +.I tempdir +rather than the current directory. +.SH SEE ALSO +.BR minizip (1), +.BR zlib (3), +.BR unzip (1). +.SH AUTHOR +This program was written by Gilles Vollant. This manual page was +written by Mark Brown . The -d tempdir option +was added by Dirk Eddelbuettel . diff --git a/src/libpsi/tools/zip/minizip/minizip.1 b/src/libpsi/tools/zip/minizip/minizip.1 new file mode 100644 index 000000000..1154484c1 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/minizip.1 @@ -0,0 +1,46 @@ +.\" Hey, EMACS: -*- nroff -*- +.TH minizip 1 "May 2, 2001" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +minizip - create ZIP archives +.SH SYNOPSIS +.B minizip +.RI [ -o ] +zipfile [ " files" ... ] +.SH DESCRIPTION +.B minizip +is a simple tool which allows the creation of compressed file archives +in the ZIP format used by the MS-DOS utility PKZIP. It was written as +a demonstration of the +.IR zlib (3) +library and therefore lack many of the features of the +.IR zip (1) +program. +.SH OPTIONS +The first argument supplied is the name of the ZIP archive to create or +.RI -o +in which case it is ignored and the second argument treated as the +name of the ZIP file. If the ZIP file already exists it will be +overwritten. +.PP +Subsequent arguments specify a list of files to place in the ZIP +archive. If none are specified then an empty archive will be created. +.SH SEE ALSO +.BR miniunzip (1), +.BR zlib (3), +.BR zip (1). +.SH AUTHOR +This program was written by Gilles Vollant. This manual page was +written by Mark Brown . + diff --git a/src/libpsi/tools/zip/minizip/minizip.c b/src/libpsi/tools/zip/minizip/minizip.c new file mode 100644 index 000000000..250d17571 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/minizip.c @@ -0,0 +1,520 @@ +/* + minizip.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) +*/ + + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#ifdef __APPLE__ +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +# include +#endif + +#include "zip.h" + +#ifdef _WIN32 + #define USEWIN32IOAPI + #include "iowin32.h" +#endif + + + +#define WRITEBUFFERSIZE (16384) +#define MAXFILENAME (256) + +#ifdef _WIN32 +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret = 0; + { + FILETIME ftLocal; + HANDLE hFind; + WIN32_FIND_DATAA ff32; + + hFind = FindFirstFileA(f,&ff32); + if (hFind != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal); + FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0); + FindClose(hFind); + ret = 1; + } + } + return ret; +} +#else +#ifdef unix || __APPLE__ +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret=0; + struct stat s; /* results of stat() */ + struct tm* filedate; + time_t tm_t=0; + + if (strcmp(f,"-")!=0) + { + char name[MAXFILENAME+1]; + int len = strlen(f); + if (len > MAXFILENAME) + len = MAXFILENAME; + + strncpy(name, f,MAXFILENAME-1); + /* strncpy doesnt append the trailing NULL, of the string is too long. */ + name[ MAXFILENAME ] = '\0'; + + if (name[len - 1] == '/') + name[len - 1] = '\0'; + /* not all systems allow stat'ing a file with / appended */ + if (stat(name,&s)==0) + { + tm_t = s.st_mtime; + ret = 1; + } + } + filedate = localtime(&tm_t); + + tmzip->tm_sec = filedate->tm_sec; + tmzip->tm_min = filedate->tm_min; + tmzip->tm_hour = filedate->tm_hour; + tmzip->tm_mday = filedate->tm_mday; + tmzip->tm_mon = filedate->tm_mon ; + tmzip->tm_year = filedate->tm_year; + + return ret; +} +#else +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + return 0; +} +#endif +#endif + + + + +int check_exist_file(filename) + const char* filename; +{ + FILE* ftestexist; + int ret = 1; + ftestexist = FOPEN_FUNC(filename,"rb"); + if (ftestexist==NULL) + ret = 0; + else + fclose(ftestexist); + return ret; +} + +void do_banner() +{ + printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n"); + printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n"); +} + +void do_help() +{ + printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \ + " -o Overwrite existing file.zip\n" \ + " -a Append to existing file.zip\n" \ + " -0 Store only\n" \ + " -1 Compress faster\n" \ + " -9 Compress better\n\n" \ + " -j exclude path. store only the file name.\n\n"); +} + +/* calculate the CRC32 of a file, + because to encrypt a file, we need known the CRC32 of the file before */ +int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc) +{ + unsigned long calculate_crc=0; + int err=ZIP_OK; + FILE * fin = FOPEN_FUNC(filenameinzip,"rb"); + + unsigned long size_read = 0; + unsigned long total_read = 0; + if (fin==NULL) + { + err = ZIP_ERRNO; + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + calculate_crc = crc32(calculate_crc,buf,size_read); + total_read += size_read; + + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + *result_crc=calculate_crc; + printf("file %s crc %lx\n", filenameinzip, calculate_crc); + return err; +} + +int isLargeFile(const char* filename) +{ + int largeFile = 0; + ZPOS64_T pos = 0; + FILE* pFile = FOPEN_FUNC(filename, "rb"); + + if(pFile != NULL) + { + int n = FSEEKO_FUNC(pFile, 0, SEEK_END); + pos = FTELLO_FUNC(pFile); + + printf("File : %s is %llu bytes\n", filename, (unsigned long long)pos); + + if(pos >= 0xffffffff) + largeFile = 1; + + fclose(pFile); + } + + return largeFile; +} + +int main(argc,argv) + int argc; + char *argv[]; +{ + int i; + int opt_overwrite=0; + int opt_compress_level=Z_DEFAULT_COMPRESSION; + int opt_exclude_path=0; + int zipfilenamearg = 0; + char filename_try[MAXFILENAME+16]; + int zipok; + int err=0; + int size_buf=0; + void* buf=NULL; + const char* password=NULL; + + + do_banner(); + if (argc==1) + { + do_help(); + return 0; + } + else + { + for (i=1;i='0') && (c<='9')) + opt_compress_level = c-'0'; + if ((c=='j') || (c=='J')) + opt_exclude_path = 1; + + if (((c=='p') || (c=='P')) && (i+1='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + if (rep=='N') + zipok = 0; + if (rep=='A') + opt_overwrite = 2; + } + } + + if (zipok==1) + { + zipFile zf; + int errclose; +# ifdef USEWIN32IOAPI + zlib_filefunc64_def ffunc; + fill_win32_filefunc64A(&ffunc); + zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); +# else + zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); +# endif + + if (zf == NULL) + { + printf("error opening %s\n",filename_try); + err= ZIP_ERRNO; + } + else + printf("creating %s\n",filename_try); + + for (i=zipfilenamearg+1;(i='0') || (argv[i][1]<='9'))) && + (strlen(argv[i]) == 2))) + { + FILE * fin = NULL; + int size_read; + const char* filenameinzip = argv[i]; + const char *savefilenameinzip; + zip_fileinfo zi; + unsigned long crcFile=0; + int zip64 = 0; + + zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = + zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; + zi.dosDate = 0; + zi.internal_fa = 0; + zi.external_fa = 0; + filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); + +/* + err = zipOpenNewFileInZip(zf,filenameinzip,&zi, + NULL,0,NULL,0,NULL / * comment * /, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level); +*/ + if ((password != NULL) && (err==ZIP_OK)) + err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); + + zip64 = isLargeFile(filenameinzip); + + /* The path name saved, should not include a leading slash. */ + /*if it did, windows/xp and dynazip couldn't read the zip file. */ + savefilenameinzip = filenameinzip; + while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) + { + savefilenameinzip++; + } + + /*should the zip file contain any path at all?*/ + if( opt_exclude_path ) + { + const char *tmpptr; + const char *lastslash = 0; + for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) + { + if( *tmpptr == '\\' || *tmpptr == '/') + { + lastslash = tmpptr; + } + } + if( lastslash != NULL ) + { + savefilenameinzip = lastslash+1; // base filename follows last slash. + } + } + + /**/ + err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, + NULL,0,NULL,0,NULL /* comment*/, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level,0, + /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */ + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + password,crcFile, zip64); + + if (err != ZIP_OK) + printf("error in opening %s in zipfile\n",filenameinzip); + else + { + fin = FOPEN_FUNC(filenameinzip,"rb"); + if (fin==NULL) + { + err=ZIP_ERRNO; + printf("error in opening %s for reading\n",filenameinzip); + } + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + { + err = zipWriteInFileInZip (zf,buf,size_read); + if (err<0) + { + printf("error in writing %s in the zipfile\n", + filenameinzip); + } + + } + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + if (err<0) + err=ZIP_ERRNO; + else + { + err = zipCloseFileInZip(zf); + if (err!=ZIP_OK) + printf("error in closing %s in the zipfile\n", + filenameinzip); + } + } + } + errclose = zipClose(zf,NULL); + if (errclose != ZIP_OK) + printf("error in closing %s\n",filename_try); + } + else + { + do_help(); + } + + free(buf); + return 0; +} diff --git a/src/libpsi/tools/zip/minizip/minizip.pc.in b/src/libpsi/tools/zip/minizip/minizip.pc.in new file mode 100644 index 000000000..69b5b7fdc --- /dev/null +++ b/src/libpsi/tools/zip/minizip/minizip.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/minizip + +Name: minizip +Description: Minizip zip file manipulation library +Requires: +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lminizip +Libs.private: -lz +Cflags: -I${includedir} diff --git a/src/libpsi/tools/zip/minizip/mztools.c b/src/libpsi/tools/zip/minizip/mztools.c new file mode 100644 index 000000000..00de7a4d7 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/mztools.c @@ -0,0 +1,291 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +/* Code */ +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#define READ_8(adr) ((unsigned char)*(adr)) +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) + +#define WRITE_8(buff, n) do { \ + *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ +} while(0) +#define WRITE_16(buff, n) do { \ + WRITE_8((unsigned char*)(buff), n); \ + WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ +} while(0) +#define WRITE_32(buff, n) do { \ + WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ + WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ +} while(0) + +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) +const char* file; +const char* fileOut; +const char* fileOutTmp; +uLong* nRecovered; +uLong* bytesRecovered; +{ + int err = Z_OK; + FILE* fpZip = fopen(file, "rb"); + FILE* fpOut = fopen(fileOut, "wb"); + FILE* fpOutCD = fopen(fileOutTmp, "wb"); + if (fpZip != NULL && fpOut != NULL && fpOutCD != NULL) { + int entries = 0; + uLong totalBytes = 0; + char header[30]; + char filename[1024]; + char extra[1024]; + int offset = 0; + int offsetCD = 0; + while ( fread(header, 1, 30, fpZip) == 30 ) { + int currentOffset = offset; + + /* File entry */ + if (READ_32(header) == 0x04034b50) { + unsigned int version = READ_16(header + 4); + unsigned int gpflag = READ_16(header + 6); + unsigned int method = READ_16(header + 8); + unsigned int filetime = READ_16(header + 10); + unsigned int filedate = READ_16(header + 12); + unsigned int crc = READ_32(header + 14); /* crc */ + unsigned int cpsize = READ_32(header + 18); /* compressed size */ + unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ + unsigned int fnsize = READ_16(header + 26); /* file name length */ + unsigned int extsize = READ_16(header + 28); /* extra field length */ + filename[0] = extra[0] = '\0'; + + /* Header */ + if (fwrite(header, 1, 30, fpOut) == 30) { + offset += 30; + } else { + err = Z_ERRNO; + break; + } + + /* Filename */ + if (fnsize > 0) { + if (fnsize < sizeof(filename)) { + if (fread(filename, 1, fnsize, fpZip) == fnsize) { + if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { + offset += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (extsize < sizeof(extra)) { + if (fread(extra, 1, extsize, fpZip) == extsize) { + if (fwrite(extra, 1, extsize, fpOut) == extsize) { + offset += extsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Data */ + { + int dataSize = cpsize; + if (dataSize == 0) { + dataSize = uncpsize; + } + if (dataSize > 0) { + char* data = malloc(dataSize); + if (data != NULL) { + if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { + if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { + offset += dataSize; + totalBytes += dataSize; + } else { + err = Z_ERRNO; + } + } else { + err = Z_ERRNO; + } + free(data); + if (err != Z_OK) { + break; + } + } else { + err = Z_MEM_ERROR; + break; + } + } + } + + /* Central directory entry */ + { + char header[46]; + char* comment = ""; + int comsize = (int) strlen(comment); + WRITE_32(header, 0x02014b50); + WRITE_16(header + 4, version); + WRITE_16(header + 6, version); + WRITE_16(header + 8, gpflag); + WRITE_16(header + 10, method); + WRITE_16(header + 12, filetime); + WRITE_16(header + 14, filedate); + WRITE_32(header + 16, crc); + WRITE_32(header + 20, cpsize); + WRITE_32(header + 24, uncpsize); + WRITE_16(header + 28, fnsize); + WRITE_16(header + 30, extsize); + WRITE_16(header + 32, comsize); + WRITE_16(header + 34, 0); /* disk # */ + WRITE_16(header + 36, 0); /* int attrb */ + WRITE_32(header + 38, 0); /* ext attrb */ + WRITE_32(header + 42, currentOffset); + /* Header */ + if (fwrite(header, 1, 46, fpOutCD) == 46) { + offsetCD += 46; + + /* Filename */ + if (fnsize > 0) { + if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { + offsetCD += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { + offsetCD += extsize; + } else { + err = Z_ERRNO; + break; + } + } + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { + offsetCD += comsize; + } else { + err = Z_ERRNO; + break; + } + } + + + } else { + err = Z_ERRNO; + break; + } + } + + /* Success */ + entries++; + + } else { + break; + } + } + + /* Final central directory */ + { + int entriesZip = entries; + char header[22]; + char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; + int comsize = (int) strlen(comment); + if (entriesZip > 0xffff) { + entriesZip = 0xffff; + } + WRITE_32(header, 0x06054b50); + WRITE_16(header + 4, 0); /* disk # */ + WRITE_16(header + 6, 0); /* disk # */ + WRITE_16(header + 8, entriesZip); /* hack */ + WRITE_16(header + 10, entriesZip); /* hack */ + WRITE_32(header + 12, offsetCD); /* size of CD */ + WRITE_32(header + 16, offset); /* offset to CD */ + WRITE_16(header + 20, comsize); /* comment */ + + /* Header */ + if (fwrite(header, 1, 22, fpOutCD) == 22) { + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { + err = Z_ERRNO; + } + } + + } else { + err = Z_ERRNO; + } + } + + /* Final merge (file + central directory) */ + fclose(fpOutCD); + if (err == Z_OK) { + fpOutCD = fopen(fileOutTmp, "rb"); + if (fpOutCD != NULL) { + int nRead; + char buffer[8192]; + while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { + if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { + err = Z_ERRNO; + break; + } + } + fclose(fpOutCD); + } + } + + /* Close */ + fclose(fpZip); + fclose(fpOut); + + /* Wipe temporary file */ + (void)remove(fileOutTmp); + + /* Number of recovered entries */ + if (err == Z_OK) { + if (nRecovered != NULL) { + *nRecovered = entries; + } + if (bytesRecovered != NULL) { + *bytesRecovered = totalBytes; + } + } + } else { + err = Z_STREAM_ERROR; + } + return err; +} diff --git a/src/libpsi/tools/zip/minizip/mztools.h b/src/libpsi/tools/zip/minizip/mztools.h new file mode 100644 index 000000000..a49a426ec --- /dev/null +++ b/src/libpsi/tools/zip/minizip/mztools.h @@ -0,0 +1,37 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +#ifndef _zip_tools_H +#define _zip_tools_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#include "unzip.h" + +/* Repair a ZIP file (missing central directory) + file: file to recover + fileOut: output file after recovery + fileOutTmp: temporary file name used for recovery +*/ +extern int ZEXPORT unzRepair(const char* file, + const char* fileOut, + const char* fileOutTmp, + uLong* nRecovered, + uLong* bytesRecovered); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/libpsi/tools/zip/minizip/unzip.c b/src/libpsi/tools/zip/minizip/unzip.c new file mode 100644 index 000000000..bcfb9416e --- /dev/null +++ b/src/libpsi/tools/zip/minizip/unzip.c @@ -0,0 +1,2125 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been successfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == MAXU32) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == MAXU32) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if ((err==UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/src/libpsi/tools/zip/minizip/unzip.h b/src/libpsi/tools/zip/minizip/unzip.h new file mode 100644 index 000000000..2104e3915 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzCloseCurrentFile before call unzClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/src/libpsi/tools/zip/minizip/zip.c b/src/libpsi/tools/zip/minizip/zip.c new file mode 100644 index 000000000..384f39c73 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/zip.c @@ -0,0 +1,2007 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automatically added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignment */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writing_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writing_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writing_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + (crcForCrypting); + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if (level==2) + zi->ci.flag |= 4; + if (level==1) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writing_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } +#ifdef HAVE_BZIP2 + else if(zi->ci.method == Z_BZIP2ED) + { + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; + } +#endif + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff ) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + else + err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writing_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writing_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; + if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/src/libpsi/tools/zip/minizip/zip.h b/src/libpsi/tools/zip/minizip/zip.h new file mode 100644 index 000000000..8aaebb623 --- /dev/null +++ b/src/libpsi/tools/zip/minizip/zip.h @@ -0,0 +1,362 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/src/libpsi/tools/zip/zip.cpp b/src/libpsi/tools/zip/zip.cpp new file mode 100644 index 000000000..c8684cbb4 --- /dev/null +++ b/src/libpsi/tools/zip/zip.cpp @@ -0,0 +1,214 @@ +/* + * zip.cpp - zip/unzip files + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "zip.h" + +#ifdef PSIMINIZIP +#include "minizip/unzip.h" +#else +#include +#endif + +#include +#include +#include + +class UnZipPrivate { +public: + UnZipPrivate() : caseSensitive(UnZip::CS_Default) { } + + QString name; + unzFile uf; + QStringList listing; + UnZip::CaseSensitivity caseSensitive; +}; + +UnZip::UnZip(const QString &name) +{ + d = new UnZipPrivate; + d->uf = nullptr; + d->name = name; +} + +UnZip::UnZip(UnZip &&o) : d(o.d) { o.d = nullptr; } + +UnZip::~UnZip() +{ + if (d) { + close(); + delete d; + } +} + +void UnZip::setCaseSensitivity(CaseSensitivity state) { d->caseSensitive = state; } + +void UnZip::setName(const QString &name) { d->name = name; } + +const QString &UnZip::name() const { return d->name; } + +bool UnZip::open() +{ + close(); + + d->uf = unzOpen(QFile::encodeName(d->name).data()); + if (!d->uf) + return false; + + return getList(); +} + +void UnZip::close() +{ + if (d->uf) { + unzClose(d->uf); + d->uf = nullptr; + } + + d->listing.clear(); +} + +QHash UnZip::unpackAll() +{ + close(); + QHash ret; + d->uf = unzOpen(QFile::encodeName(d->name).data()); + if (!d->uf) + return ret; + + unz_global_info gi; + int err = unzGetGlobalInfo(d->uf, &gi); + if (err != UNZ_OK) { + close(); + return ret; + } + + for (int n = 0; n < (int)gi.number_entry; ++n) { + char filename_inzip[256]; + unz_file_info file_info; + int err + = unzGetCurrentFileInfo(d->uf, &file_info, filename_inzip, sizeof(filename_inzip), nullptr, 0, nullptr, 0); + if (err != UNZ_OK) + break; + + QString filename = QString::fromUtf8(filename_inzip); + QByteArray data; + if (!readCurrentFile(&data)) + break; + + ret.insert(filename, data); + + if ((n + 1) < (int)gi.number_entry) { + err = unzGoToNextFile(d->uf); + if (err != UNZ_OK) + break; + } + } + + close(); + return ret; +} + +const QStringList &UnZip::list() const { return d->listing; } + +bool UnZip::getList() +{ + unz_global_info gi; + int err = unzGetGlobalInfo(d->uf, &gi); + if (err != UNZ_OK) + return false; + + QStringList l; + for (int n = 0; n < (int)gi.number_entry; ++n) { + char filename_inzip[256]; + unz_file_info file_info; + int err + = unzGetCurrentFileInfo(d->uf, &file_info, filename_inzip, sizeof(filename_inzip), nullptr, 0, nullptr, 0); + if (err != UNZ_OK) + return false; + + l += filename_inzip; + + if ((n + 1) < (int)gi.number_entry) { + err = unzGoToNextFile(d->uf); + if (err != UNZ_OK) + return false; + } + } + + d->listing = l; + + return true; +} + +bool UnZip::readCurrentFile(QByteArray *buf, int max) +{ + int err = unzOpenCurrentFile(d->uf); + if (err != UNZ_OK) + return false; + + QByteArray a; + QByteArray chunk; + chunk.resize(16384); + while (1) { + err = unzReadCurrentFile(d->uf, chunk.data(), chunk.size()); + if (err < 0) { + unzCloseCurrentFile(d->uf); + return false; + } + if (err == 0) + break; + + int oldsize = a.size(); + if (max > 0 && oldsize + err > max) + err = max - oldsize; + a.resize(oldsize + err); + memcpy(a.data() + oldsize, chunk.data(), err); + + if (max > 0 && (int)a.size() >= max) + break; + } + + err = unzCloseCurrentFile(d->uf); + if (err != UNZ_OK) + return false; + + *buf = a; + return true; +} + +bool UnZip::readFile(const QString &fname, QByteArray *buf, int max) +{ + int err = unzLocateFile(d->uf, QFile::encodeName(fname).data(), d->caseSensitive); + if (err != UNZ_OK) + return false; + + char filename_inzip[256]; + unz_file_info file_info; + err = unzGetCurrentFileInfo(d->uf, &file_info, filename_inzip, sizeof(filename_inzip), nullptr, 0, nullptr, 0); + if (err != UNZ_OK) + return false; + + return readCurrentFile(buf, max); +} + +bool UnZip::fileExists(const QString &fname) +{ + int err = unzLocateFile(d->uf, QFile::encodeName(fname).data(), d->caseSensitive); + return err == UNZ_OK; +} diff --git a/src/libpsi/tools/zip/zip.h b/src/libpsi/tools/zip/zip.h new file mode 100644 index 000000000..8deed1bf6 --- /dev/null +++ b/src/libpsi/tools/zip/zip.h @@ -0,0 +1,59 @@ +/* + * zip.h - zip/unzip files + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef CS_ZIP_H +#define CS_ZIP_H + +#include +#include + +class QByteArray; + +class UnZip { +public: + enum CaseSensitivity { + CS_Default = 0, // system default + CS_Sensitive = 1, + CS_Insensitive = 2 + }; + + UnZip(const QString &fname = QString()); + UnZip(UnZip &&o); + ~UnZip(); + + void setCaseSensitivity(CaseSensitivity state = CS_Default); + void setName(const QString &); + const QString &name() const; + + bool open(); + void close(); + + QHash unpackAll(); + const QStringList &list() const; + bool readFile(const QString &, QByteArray *, int max = 0); + bool fileExists(const QString &); + +private: + class UnZipPrivate *d; + + bool getList(); + bool readCurrentFile(QByteArray *buf, int max = 0); +}; + +#endif // CS_ZIP_H diff --git a/src/libpsi/widgets/groupchatbrowsewindow.cpp b/src/libpsi/widgets/groupchatbrowsewindow.cpp new file mode 100644 index 000000000..c6f46666f --- /dev/null +++ b/src/libpsi/widgets/groupchatbrowsewindow.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2009 Barracuda Networks, Inc. + * + * 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 2 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 . + * + */ + +#include "groupchatbrowsewindow.h" + +#include "ui_groupchatbrowsewindow.h" + +#include +#include +#include +#include +#include + +// TODO: recent list of joins, like original gcjoindlg? + +Q_DECLARE_METATYPE(XMPP::Jid) +Q_DECLARE_METATYPE(GroupChatBrowseWindow::RoomOptions) +Q_DECLARE_METATYPE(GroupChatBrowseWindow::RoomInfo) + +enum Role { RoomInfoRole = Qt::UserRole, InternalRowRole }; + +// TODO: handle duplicate rooms somehow +class RoomModel : public QStandardItemModel { + Q_OBJECT + +public: + QList list; + QPixmap icon; + + RoomModel(QObject *parent = 0) : QStandardItemModel(parent) + { + QStringList headers; + headers += tr("Groupchat name"); + // headers += tr("Participants"); + headers += tr("Auto-join"); + setHorizontalHeaderLabels(headers); + // setSortRole(RoomItem::PositionRole); + } + + void addRooms(const QList &alist) + { + for (int n = 0; n < alist.count(); ++n) { + const GroupChatBrowseWindow::RoomInfo &info = alist[n]; + + QList clist; + clist.append(new QStandardItem(icon, info.roomName)); + clist.first()->setData(list.count(), InternalRowRole); + clist.first()->setData(qVariantFromValue(info), RoomInfoRole); + // clist.append(new QStandardItem(QString::number(info.participants))); + clist.append(new QStandardItem); + clist.last()->setData(list.count(), InternalRowRole); + clist.last()->setCheckable(true); + if (info.autoJoin) { + clist.last()->setCheckState(Qt::Checked); + } + clist.first()->setEditable(false); + clist.last()->setEditable(false); + + list += info; + appendRow(clist); + } + } + + void removeRoom(int at) + { + list.removeAt(at); + removeRow(at); + } + + GroupChatBrowseWindow::RoomInfo &roomInfo(const QModelIndex &index) + { + QStandardItem *item = itemFromIndex(index); + return list[item->data(InternalRowRole).toInt()]; + } +}; + +class PsiGroupChatBrowseWindow::Private : public QObject { + Q_OBJECT + +public: + PsiGroupChatBrowseWindow *q; + Ui::GroupChatBrowseWindowUI ui; + XMPP::Jid server; + RoomModel *model; + QObject *controller; // FIXME: remove this + + XMPP::Jid roomBeingCreated, roomBeingDestroyed; + QPushButton *pb_create, *pb_join; + + Private(PsiGroupChatBrowseWindow *_q) : QObject(_q), q(_q), controller(0) + { + ui.setupUi(q); + + model = new RoomModel(this); + + ui.tv_rooms->setAllColumnsShowFocus(true); + ui.tv_rooms->setRootIsDecorated(false); + ui.tv_rooms->setSortingEnabled(true); + ui.tv_rooms->sortByColumn(0, Qt::AscendingOrder); + ui.tv_rooms->header()->setMovable(false); + ui.tv_rooms->header()->setClickable(true); + ui.tv_rooms->setModel(model); + ui.buttonBox->setStandardButtons(QDialogButtonBox::Close); + pb_create = new QPushButton("Cre&ate...", q); + pb_join = new QPushButton("&Join", q); + pb_join->setDefault(true); + ui.buttonBox->addButton(pb_create, QDialogButtonBox::ActionRole); + ui.buttonBox->addButton(pb_join, QDialogButtonBox::AcceptRole); + + ui.tv_rooms->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui.tv_rooms, SIGNAL(customContextMenuRequested(const QPoint &)), + SLOT(rooms_contextMenuRequested(const QPoint &))); + connect(ui.tv_rooms, SIGNAL(activated(const QModelIndex &)), SLOT(rooms_activated(const QModelIndex &))); + + connect(ui.tv_rooms->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + SLOT(rooms_selectionChanged(const QItemSelection &, const QItemSelection &))); + connect(ui.le_room, SIGNAL(textChanged(const QString &)), SLOT(room_textChanged(const QString &))); + + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + SLOT(model_dataChanged(const QModelIndex &, const QModelIndex &))); + + connect(pb_create, SIGNAL(clicked()), SLOT(doCreate())); + connect(pb_join, SIGNAL(clicked()), SLOT(doJoin())); + connect(ui.buttonBox, SIGNAL(rejected()), SLOT(doClose())); + pb_join->setEnabled(false); + + q->resize(560, 420); + } + + void setWidgetsEnabled(bool enabled) + { + ui.tv_rooms->setEnabled(enabled); + ui.le_room->setEnabled(enabled); + ui.le_nick->setEnabled(enabled); + pb_create->setEnabled(enabled); + pb_join->setEnabled(enabled); + } + + void roomDestroyed() + { + int at = -1; + for (int n = 0; n < model->list.count(); ++n) { + if (model->list[n].jid == roomBeingDestroyed) { + at = n; + break; + } + } + if (at == -1) + return; + + model->removeRoom(at); + } + +public slots: + void rooms_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) + { + Q_UNUSED(deselected); + + if (!selected.indexes().isEmpty() || !ui.le_room->text().isEmpty()) + pb_join->setEnabled(true); + else + pb_join->setEnabled(false); + } + + void rooms_contextMenuRequested(const QPoint &pos) + { + QItemSelection selected = ui.tv_rooms->selectionModel()->selection(); + + if (!selected.indexes().isEmpty()) { + QPoint gpos = ui.tv_rooms->viewport()->mapToGlobal(pos); + QMenu menu(q); + QAction *destroyAction = menu.addAction(tr("Destroy")); + QAction *act = menu.exec(gpos); + if (act == destroyAction) { + QModelIndex index = selected.indexes().first(); + // ### async is bad + QMetaObject::invokeMethod(this, "destroyRoom", Qt::QueuedConnection, + Q_ARG(XMPP::Jid, model->roomInfo(index).jid)); + } + } + } + + void rooms_activated(const QModelIndex &index) + { + XMPP::Jid room = model->roomInfo(index).jid; + + if (!room.isEmpty()) { + setWidgetsEnabled(false); + + emit q->onJoin(room); + } + } + + void room_textChanged(const QString &text) + { + Q_UNUSED(text); + + QItemSelection selected = ui.tv_rooms->selectionModel()->selection(); + + if (!selected.indexes().isEmpty() || !ui.le_room->text().isEmpty()) + pb_join->setEnabled(true); + else + pb_join->setEnabled(false); + } + + void model_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) + { + Q_UNUSED(bottomRight); + + QModelIndex index = topLeft; + QStandardItem *i = model->itemFromIndex(index); + bool isChecked = false; + if (i->checkState() == Qt::Checked) + isChecked = true; + + bool previousState = model->roomInfo(index).autoJoin; + model->roomInfo(index).autoJoin = isChecked; + + if (previousState != isChecked) + emit q->onSetAutoJoin(QList() << model->roomInfo(index).jid, isChecked); + } + + void doCreate() + { + QString room = QInputDialog::getText(q, tr("Create Groupchat"), + tr("Choose a name for the groupchat you want to create:")); + + if (!room.isEmpty()) { + setWidgetsEnabled(false); + + roomBeingCreated = server.withNode(room); + emit q->onCreate(roomBeingCreated); + } + } + + void doJoin() + { + XMPP::Jid room; + + QString manualRoom = ui.le_room->text(); + if (!manualRoom.isEmpty()) { + if (manualRoom.indexOf('@') != -1) + room = manualRoom; + else + room = server.withNode(manualRoom); + } else { + QItemSelection selection = ui.tv_rooms->selectionModel()->selection(); + if (selection.indexes().isEmpty()) + return; + QModelIndex index = selection.indexes().first(); + + room = model->roomInfo(index).jid; + } + + if (!room.isEmpty()) { + setWidgetsEnabled(false); + + emit q->onJoin(room); + } + } + + void doClose() { q->close(); } + + void createFinalize() + { + emit q->onCreateFinalize(roomBeingCreated, true); + q->close(); + } + + void destroyRoom(const XMPP::Jid &room) + { + setWidgetsEnabled(false); + roomBeingDestroyed = room; + emit q->onDestroy(room); + } +}; + +PsiGroupChatBrowseWindow::PsiGroupChatBrowseWindow(QWidget *parent) : GroupChatBrowseWindow(parent) +{ + qRegisterMetaType(); + qRegisterMetaType(); + + d = new Private(this); +} + +PsiGroupChatBrowseWindow::~PsiGroupChatBrowseWindow() { delete d; } + +void PsiGroupChatBrowseWindow::resizeEvent(QResizeEvent *event) +{ + // int sort_margin = d->ui.tv_rooms->header()->style()->pixelMetric(QStyle::PM_HeaderMargin); + int grip_width = d->ui.tv_rooms->header()->style()->pixelMetric(QStyle::PM_HeaderGripMargin); + int frame_width = d->ui.tv_rooms->frameWidth(); + // printf("s=%d,g=%d,f=%d,h=%d\n", sort_margin, grip_width, frame_width, d->ui.tv_rooms->header()->frameWidth()); + grip_width *= 2; // HACK: this is certainly wrong, but some styles need extra pixel shifting + frame_width *= 2; // frame on left and right side + int widget_width = d->ui.tv_rooms->width(); + // int right_column_ideal = qMax(d->ui.tv_rooms->header()->minimumSectionSize(), + // d->ui.tv_rooms->header()->sectionSizeHint(1)); + int right_column_ideal = 132; // d->ui.tv_rooms->header()->sectionSizeHint(1); + int left_column_width = widget_width - right_column_ideal - grip_width - frame_width; + d->ui.tv_rooms->header()->resizeSection(0, left_column_width); + GroupChatBrowseWindow::resizeEvent(event); +} + +QObject *PsiGroupChatBrowseWindow::controller() const { return d->controller; } + +void PsiGroupChatBrowseWindow::setController(QObject *controller) { d->controller = controller; } + +void PsiGroupChatBrowseWindow::setGroupChatIcon(const QPixmap &icon) { d->model->icon = icon; } + +void PsiGroupChatBrowseWindow::setServer(const XMPP::Jid &roomServer) +{ + d->server = roomServer; + d->ui.le_server->setText(d->server.full()); + d->ui.le_server->setCursorPosition(0); + + // FIXME: we shouldn't do this here + QMetaObject::invokeMethod(this, "onBrowse", Qt::QueuedConnection, Q_ARG(XMPP::Jid, d->server)); +} + +void PsiGroupChatBrowseWindow::setServerVisible(bool b) +{ + d->ui.lb_server->setVisible(b); + d->ui.le_server->setVisible(b); + d->ui.pb_browse->setVisible(b); +} + +void PsiGroupChatBrowseWindow::setNicknameVisible(bool b) +{ + d->ui.lb_nick->setVisible(b); + d->ui.le_nick->setVisible(b); +} + +void PsiGroupChatBrowseWindow::handleBrowseResultsReady(const QList &list) +{ + d->model->addRooms(list); +} + +void PsiGroupChatBrowseWindow::handleBrowseError(const QString &reason) +{ + // TODO + Q_UNUSED(reason); +} + +void PsiGroupChatBrowseWindow::handleJoinSuccess() { close(); } + +void PsiGroupChatBrowseWindow::handleJoinError(const QString &reason) +{ + d->setWidgetsEnabled(true); + + QMessageBox::information(this, tr("Error"), tr("Unable to join groupchat.\nReason: %1").arg(reason)); +} + +void PsiGroupChatBrowseWindow::handleCreateSuccess(const GroupChatBrowseWindow::RoomOptions &defaultOptions) +{ + QMetaObject::invokeMethod(this, "onCreateConfirm", Qt::QueuedConnection, + Q_ARG(GroupChatBrowseWindow::RoomOptions, defaultOptions)); +} + +void PsiGroupChatBrowseWindow::handleCreateConfirmed() +{ + QMetaObject::invokeMethod(d, "createFinalize", Qt::QueuedConnection); +} + +void PsiGroupChatBrowseWindow::handleCreateError(const QString &reason) +{ + d->setWidgetsEnabled(true); + + QMessageBox::information(this, tr("Error"), tr("Unable to create groupchat.\nReason: %1").arg(reason)); +} + +void PsiGroupChatBrowseWindow::handleDestroySuccess() +{ + d->setWidgetsEnabled(true); + + d->roomDestroyed(); +} + +void PsiGroupChatBrowseWindow::handleDestroyError(const QString &reason) +{ + d->setWidgetsEnabled(true); + + QMessageBox::information(this, tr("Error"), tr("Unable to destroy groupchat.\nReason: %1").arg(reason)); +} + +#include "groupchatbrowsewindow.moc" diff --git a/src/libpsi/widgets/groupchatbrowsewindow.h b/src/libpsi/widgets/groupchatbrowsewindow.h new file mode 100644 index 000000000..8256d19ec --- /dev/null +++ b/src/libpsi/widgets/groupchatbrowsewindow.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2009 Barracuda Networks, Inc. + * + * 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 2 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 . + * + */ + +#ifndef GROUPCHATBROWSEWINDOW_H +#define GROUPCHATBROWSEWINDOW_H + +#include + +#include +#include + +class GroupChatBrowseWindow : public QWidget { + Q_OBJECT + +public: + class RoomOptions { + public: + XMPP::Jid jid; + QString roomName; + bool visible; + + RoomOptions() : visible(false) { } + }; + + class RoomInfo { + public: + XMPP::Jid jid; + bool remove; + + // the following are only valid if 'remove' is false + QString roomName; + bool autoJoin; + bool owner; + int participants; + + RoomInfo() : remove(false), autoJoin(false), owner(false), participants(-1) { } + }; + + GroupChatBrowseWindow(QWidget *parent = 0) : QWidget(parent) { } + + // FIXME: remove this + virtual QObject *controller() const = 0; + virtual void setController(QObject *controller) = 0; + + virtual void setGroupChatIcon(const QPixmap &icon) = 0; + virtual void setServer(const XMPP::Jid &roomServer) = 0; + virtual void setServerVisible(bool b) = 0; + virtual void setNicknameVisible(bool b) = 0; + +signals: + void onBrowse(const XMPP::Jid &roomServer); + void onJoin(const XMPP::Jid &room); + void onCreate(const XMPP::Jid &room); + void onCreateConfirm(const GroupChatBrowseWindow::RoomOptions &options); + void onCreateCancel(const XMPP::Jid &room); // no ack + void onCreateFinalize(const XMPP::Jid &room, bool join); // no-ack if join=false + void onDestroy(const XMPP::Jid &room); + void onSetAutoJoin(const QList &rooms, bool enabled); + +public slots: + // onBrowse + virtual void handleBrowseResultsReady(const QList &list) = 0; + virtual void handleBrowseError(const QString &reason) = 0; + + // onJoin or onCreateFinalize + virtual void handleJoinSuccess() = 0; + virtual void handleJoinError(const QString &reason) = 0; + + // from onCreate + virtual void handleCreateSuccess(const GroupChatBrowseWindow::RoomOptions &defaultOptions) = 0; + + // from onCreateConfirm + virtual void handleCreateConfirmed() = 0; + + // from onCreate or onCreateConfirm + virtual void handleCreateError(const QString &reason) = 0; + + // from onDestroy + virtual void handleDestroySuccess() = 0; + virtual void handleDestroyError(const QString &reason) = 0; +}; + +class PsiGroupChatBrowseWindow : public GroupChatBrowseWindow { + Q_OBJECT + +public: + PsiGroupChatBrowseWindow(QWidget *parent = 0); + ~PsiGroupChatBrowseWindow(); + + // FIXME: remove this + virtual QObject *controller() const; + virtual void setController(QObject *controller); + + // from qwidget + virtual void resizeEvent(QResizeEvent *event); + + virtual void setGroupChatIcon(const QPixmap &icon); + virtual void setServer(const XMPP::Jid &roomServer); + virtual void setServerVisible(bool b); + virtual void setNicknameVisible(bool b); + +public slots: + virtual void handleBrowseResultsReady(const QList &list); + virtual void handleBrowseError(const QString &reason); + virtual void handleJoinSuccess(); + virtual void handleJoinError(const QString &reason); + virtual void handleCreateSuccess(const GroupChatBrowseWindow::RoomOptions &defaultOptions); + virtual void handleCreateConfirmed(); + virtual void handleCreateError(const QString &reason); + virtual void handleDestroySuccess(); + virtual void handleDestroyError(const QString &reason); + +private: + class Private; + Private *d; +}; + +#endif // GROUPCHATBROWSEWINDOW_H diff --git a/src/libpsi/widgets/groupchatbrowsewindow.ui b/src/libpsi/widgets/groupchatbrowsewindow.ui new file mode 100644 index 000000000..8ba323e8c --- /dev/null +++ b/src/libpsi/widgets/groupchatbrowsewindow.ui @@ -0,0 +1,83 @@ + + GroupChatBrowseWindowUI + + + + 0 + 0 + 411 + 296 + + + + Groupchat + + + + + + + + Groupchat server: + + + + + + + + + + &Browse + + + + + + + + + + + + + + Specify groupchat name manually: + + + + + + + + + + Show your name in the groupchat as: + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + le_server + pb_browse + tv_rooms + le_room + le_nick + buttonBox + + + +