pax_global_header00006660000000000000000000000064147673353420014530gustar00rootroot0000000000000052 comment=40430dbe1c260620b4509ae0f898864288f9179b tea-qt-63.3.0/000077500000000000000000000000001476733534200130145ustar00rootroot00000000000000tea-qt-63.3.0/.github/000077500000000000000000000000001476733534200143545ustar00rootroot00000000000000tea-qt-63.3.0/.github/workflows/000077500000000000000000000000001476733534200164115ustar00rootroot00000000000000tea-qt-63.3.0/.github/workflows/cmake-mac.yml000066400000000000000000000020701476733534200207510ustar00rootroot00000000000000name: CMake for MacOS on: workflow_dispatch env: BUILD_TYPE: Release jobs: build: runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Install Qt uses: jurplel/install-qt-action@v3 with: version: '6.5.0' host: 'mac' target: 'desktop' arch: 'clang_64' install-deps: 'true' modules: 'qt5compat' cache: 'true' set-env: 'true' - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} # - name: Test # working-directory: ${{github.workspace}}/build # # Execute tests defined by the CMake configuration. # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # run: ctest -C ${{env.BUILD_TYPE}} - name: Archive production artifacts uses: actions/upload-artifact@v3 with: name: tea-mac-binary path: ${{github.workspace}}/build/tea.app tea-qt-63.3.0/.github/workflows/cmake.yml000066400000000000000000000020701476733534200202130ustar00rootroot00000000000000name: CMake for Linux on: workflow_dispatch env: BUILD_TYPE: Release jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install Qt uses: jurplel/install-qt-action@v3 with: version: '6.5.0' host: 'linux' target: 'desktop' arch: 'gcc_64' install-deps: 'true' modules: 'qt5compat' cache: 'true' set-env: 'true' - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} # - name: Test # working-directory: ${{github.workspace}}/build # # Execute tests defined by the CMake configuration. # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail # run: ctest -C ${{env.BUILD_TYPE}} - name: Archive production artifacts uses: actions/upload-artifact@v3 with: name: tea-ubuntu-binary path: ${{github.workspace}}/build/tea tea-qt-63.3.0/.gitignore000066400000000000000000000014271476733534200150100ustar00rootroot00000000000000# based on https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore # and https://github.com/github/gitignore/blob/master/Qt.gitignore # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app # Qt-es object_script.*.Release object_script.*.Debug *_plugin_import.cpp /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp moc_*.h qrc_*.cpp ui_*.h *.qmlc *.jsc Makefile* *build-* # Qt unit tests target_wrapper.* # QtCreator *.autosave # QtCreator Qml *.qmlproject.user *.qmlproject.user.* # QtCreator CMake CMakeLists.txt.user* tea-qt-63.3.0/ABOUT-PL.txt000066400000000000000000000043141476733534200147420ustar00rootroot00000000000000TEA to darmowy edytor tekstu dla systemów Linux, * BSD, OS / 2, Windows, Haicu. Zależy to od Qt 4.6+ lub Qt 5 lub Qt6, zlib i opcjonalnie od Aspell lub Hunspell. Stara (ale odnowiona) gałąź, TEA-GTK, zależy od GTK + 3 i GtkSourceView 3. Możliwości: Zrozumiała dokumentacja Funkcje IDE Uruchamianie zewnętrznych programów z aktualnie otwartym plikiem Funkcje (dla Qt) Mały rozmiar Skalowalny interfejs Wsparcie dla zapisu: zwykły tekst Obsługa odczytu: zwykły tekst, FB2, EPUB, ODT, RTF, DOCX, Abiword, KWord KWD, SWX (stary format OpenOffice.org), PDF, DJVU Wbudowany menedżer plików podobny do MC sprawdzanie pisowni (przy użyciu silników Aspell i/lub Hunspell) Silnik układu z zakładkami Podświetlanie składni dla C, C ++, Bash script, BASIC, C #, D, Fortran, Java, LilyPond, Lout, Lua, NASM, NSIS, Pascal, Perl, PHP, PO (gettext), Python, Seed7, TeX / LaTeX, Vala, Verilog, XML, HTML, XHTML, Dokuwiki, MediaWiki. Wsparcie dla wszystkich możliwych kodowań Automatyczne wykrywanie kodowania Obsługa etykiet (znaczników) w tekście Obsługa fragmentów kodu i szablonów Skrypty (Python, Perl, Ruby, Bash, Lua) Wtyczki JavaScript Dostosowywanie skrótów klawiszowych Możliwość przypisania „skrótów klawiszowych” do wszystkich pozycji menu, w tym dynamicznych, takich jak fragmenty Dziesiątki funkcji edytora tekstu Funkcja „Otwórz przy kursorze” dla plików HTML i obrazów Różne narzędzia HTML, narzędzia do edycji [X]HTML, Dokuwiki, MediaWiki, Docbook, LaTeX, Lout, Markdown Podgląd w zewnętrznych przeglądarkach Funkcje obsługi ciągów, takie jak sortowanie, odwracanie, usuwanie formatu, przycinanie, filtrowanie, konwersje, przetwarzanie tabel tekstowych itp. Uniwersalny analizator tekstu UNITAZ Zakładki Szablony Tłumacz alfabetu Morse'a Wbudowany kalendarz/organizator Obsługa motywów i palet, zmiana motywów, motywy projektowe Wsparcie Drag'n'drop (z plikami tekstowymi i obrazkami) Wbudowana przeglądarka obrazów (PNG, JPEG, GIF, WBMP, BMP, TIFF, TGA itp.) Wbudowany konwerter obrazu i zmiana rozmiaru Masowa zmiana rozmiaru zdjęć, ich formatu Wbudowany program pakujący/rozpakowujący ZIP z selektorem zestawu znaków nazw plików Obliczenie RMS dla 16-bitowych plików PCM WAV tea-qt-63.3.0/AUTHORS000066400000000000000000000040671476733534200140730ustar00rootroot00000000000000Code and design: Peter Semiletov - peter.semiletov@gmail.com, https://psemiletov.github.io Contributors from github: toddy15, ryandesign, yurikoles, grahamperrin, yurchor PHP hl module by Boo-boo Code from other projects: Diego Iastrubni, Qxt Foundation, Trolltech ASA, Franz Schmid, Adam Rogoyski, Michael Protasov, Angius Fabrizio, Kostya Gancov Pugixml (https://pugixml.org) by Arseny Kapoulkine ZIP/Miniz - https://github.com/kuba--/zip single application module (one of two) - Berenger Bramas (http://berenger.eu/blog/c-qt-singleapplication-single-app-instance/) exif.h/cpp - public domain code from http://imonad.com (based on Exif Jpeg header manipulation tool http://www.sentex.net/~mwandel/jhead/) Some code snippets were taken from qtcentre programming forum. ## Translators: Russian UI translation and documentation: Peter Semiletov English documentation (the terrible one): Peter Semiletov Polish UI and Manual translation by Krzysztof Jaśkiewicz Spanish UI translation Luis Montgomery German UI translation: Tobias Quathamer Frech UI translation: gholafox ## Acknowledgements: Thanks to (an incomplete and unsorted list): Ronald Fischer Vladimir Shatilo Сергей (post2) Elbert Pol Настя Глазуу z_hunter Товарищ У 6y_6y Niels Rasmussen Alexey Svetlichniy Alexander Natalenko Michèle Garoche Zhu Baoshi Michael Shigorin Dmitri Yurchenko Detlef Bloch Victor Soroka Shlomi Loubaton Aleksey Kirpichnikov Dmitry Shevchenko Dmitry Shurupov Oleg Bartunov Segiy Burachek Dmitri Yurchenko Paul Whalley Tom Veidt Lungu Sergey Maciej Szumieluk Sebastian Schlingmann Sergey Zhumatiy Mad Deer Jose Alexey Boyko Miguel Latorre Hetdegon Svetlana Semyonova Dmitriy K Geert Theys Meka[ni] Salvador Koutsomhtsos Alexey Dokuchaev Bart Alberti Peter Müller Peter Weller Victor Ananjevsky Sharon Kimble Giovanni Giovanzana Yanmarshus Oleg Matviychuk scp Oleksandr Natalenko Serkan Calis Jordan Mantha Sam Cater Yanmarshus Dmitriy Shilov Goran Mekić Dejan Čabrilo Giovanni Bechis Bill Gribble Smirftsch Steven Penny Andrey Vasilkin tea-qt-63.3.0/CMakeLists.txt000066400000000000000000000134671476733534200155670ustar00rootroot00000000000000#cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.21.1) set (QT_MIN_VERSION "5.4.0") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) set(PROJECT "tea-qt") project ($PROJECT VERSION 63.3.0 LANGUAGES CXX C) enable_language(CXX) enable_language(C) #find_package(Qt6 COMPONENTS Core Widgets OPTIONAL_COMPONENTS PrintSupport) find_package(Qt6 COMPONENTS Core Widgets) if (NOT Qt6_FOUND) find_package(Qt5 5.15 REQUIRED COMPONENTS Core Widgets) endif() if (Qt6_FOUND) message("+ Qt6 found") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) #find_package(Qt6 COMPONENTS Core5Compat REQUIRED) endif() #qt_standard_project_setup() set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) qt_add_resources(QT_RESOURCES resources.qrc) add_definitions(-DVERSION_NUMBER="\\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\\"") option(USE_NUSPELL "Use Nuspell" OFF) option(USE_ASPELL "Use Aspell" OFF) option(USE_HUNSPELL "Use Hunspell" ON) option(USE_PRINTER "Use printer support" OFF) option(USE_PDF "Use libpoppler" OFF) option(USE_DJVU "Use djvu support" OFF) option(USE_MAC "Build for Mac" OFF) option(USE_SPEECH "Use Speech Dispatcher" OFF) if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") add_definitions(-DQ_OS_LINUX) add_definitions(-DQ_OS_UNIX) endif() message("CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${QtWidgets_EXECUTABLE_COMPILE_FLAGS}") file(GLOB tea_SRCS "src/*.c" "src/*.cpp") file(GLOB tea_HEADERS "src/*.h" "src/.*hpp") set(tea_ICONPNG32 ./icons/32/tea.png ) set(tea_ICONPNG48 ./icons/48/tea.png ) set(tea_ICONPNG64 ./icons/64/tea.png ) set(tea_ICONPNG128 ./icons/128/tea.png ) set(tea_ICONSVG ./icons/svg/tea.svg ) set(tea_DESKTOP ./desktop/tea.desktop ) add_custom_target(dist COMMAND git archive --format=tar --prefix=${PROJECT}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}/ HEAD | gzip >${PROJECT}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.tar.gz WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) add_executable(tea ${tea_SRCS} ${QT_RESOURCES}) #if (USE_MAC) #set_target_properties(tea PROPERTIES MACOSX_BUNDLE TRUE) #endif() set_target_properties(tea PROPERTIES WIN32_EXECUTABLE ON MACOSX_BUNDLE ON ) find_package(PkgConfig REQUIRED) if (Qt6_FOUND) if(USE_NUSPELL) pkg_check_modules(nuspell QUIET nuspell) if(nuspell_FOUND) add_definitions(-DNUSPELL_ENABLE) target_link_libraries(tea ${nuspell_LIBRARIES}) include_directories(${nuspell_INCLUDE_DIRS}) message("+ nuspell support") endif() endif() endif() #end of Qt6/Nuspell if(USE_HUNSPELL) pkg_check_modules(hunspell QUIET hunspell) if(hunspell_FOUND) add_definitions(-DHUNSPELL_ENABLE) message("+ hunspell support") endif() endif() if(USE_SPEECH) pkg_check_modules(SPEECH QUIET speech-dispatcher>=0.0.1) if (SPEECH_FOUND) Message ("+ speech support") add_definitions(-DSPEECH_ENABLE) target_include_directories(tea PUBLIC ${SPEECH_INCLUDE_DIRS}) target_link_libraries(tea ${SPEECH_LIBRARIES}) endif() endif() if(USE_PRINTER) find_package(Qt6PrintSupport QUIET) if (Qt6PrintSupport_FOUND) Message ("+ Qt6 printer support") add_definitions(-DPRINTER_ENABLE) target_link_libraries(tea Qt6::PrintSupport) else() find_package(Qt5PrintSupport QUIET) if (Qt5PrintSupport_FOUND) Message ("+ Qt5 printer support") add_definitions(-DPRINTER_ENABLE) target_link_libraries(tea Qt5::PrintSupport) endif() endif() endif() if (EXISTS "/usr/include/linux/joystick.h") message("+JOYSTICK_SUPPORTED") add_definitions(-DJOYSTICK_SUPPORTED) endif() if(USE_ASPELL) message("SEARCH ASPELL") find_path(ASPELL_INCLUDES aspell.h "/usr/include/" "/usr/local/" ) if(ASPELL_INCLUDES) message("+ aspell support") add_definitions(-DASPELL_ENABLE) if(OS2) target_link_libraries(tea aspell_dll.a) else() target_link_libraries(tea libaspell.so) endif() endif() endif() if(USE_PDF) find_package(PkgConfig) pkg_check_modules(popplercpp QUIET poppler-cpp) if(popplercpp_FOUND) add_definitions(-DPOPPLER_ENABLE) target_link_libraries(tea ${popplercpp_LIBRARIES}) include_directories(${popplercpp_INCLUDE_DIRS}) message("+ popplercpp support") endif() endif() if(USE_DJVU) find_package(PkgConfig) pkg_check_modules(ddjvuapi QUIET ddjvuapi) if(ddjvuapi_FOUND) add_definitions(-DDJVU_ENABLE) target_link_libraries(tea ${ddjvuapi_LIBRARIES}) include_directories(${ddjvuapi_INCLUDE_DIRS}) message("+ djvuapi support") endif() endif() #if(UNIX OR MINGW OR OS2) # find_package(ZLIB REQUIRED) #else(UNIX OR MINGW) # set(ZLIB_INCLUDE_DIRS "${QT_ROOT}/src/3rdparty/zlib" CACHE STRING "Path to ZLIB headers of Qt") # set(ZLIB_LIBRARIES "") # if(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h") # message("Please specify a valid zlib include dir") # endif(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h") #endif(UNIX OR MINGW OR OS2) #if (ZLIB_FOUND) # include_directories(${ZLIB_INCLUDE_DIRS} ) # target_link_libraries(tea ${ZLIB_LIBRARIES} ) #endif( ZLIB_FOUND ) if(hunspell_FOUND) include_directories(${hunspell_INCLUDE_DIRS} ) target_link_libraries(tea ${hunspell_LIBRARIES} ) endif() #if (Qt6_FOUND) #target_link_libraries(tea Qt::Core5Compat) #endif() target_link_libraries(tea Qt::Widgets Qt::Core) install (TARGETS tea DESTINATION bin) install (FILES ${tea_ICONSVG} DESTINATION share/icons/hicolor/scalable/apps) install (FILES ${tea_ICONPNG32} DESTINATION share/icons/hicolor/32x32/apps) install (FILES ${tea_ICONPNG48} DESTINATION share/icons/hicolor/48x48/apps) install (FILES ${tea_ICONPNG64} DESTINATION share/icons/hicolor/64x64/apps) install (FILES ${tea_ICONPNG128} DESTINATION share/icons/hicolor/128x128/apps) install (FILES ${tea_DESKTOP} DESTINATION share/applications)tea-qt-63.3.0/COPYING000066400000000000000000001045131476733534200140530ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . tea-qt-63.3.0/ChangeLog000066400000000000000000000675101476733534200145770ustar00rootroot00000000000000//it's lazy updated file, so the real ChangeLog is the activity on github 63.3.0 * "Save as different" renamed to "Save more" + File - Save more - Save all existing * in some cases, such as IDE - header/source switch, TEA now just opens file and not reload it if the file is already opened - per spellchecker user dict in a favour of the common one, for all languages + Nuspell support - Qt6::Core5Compat dependency - zlib dependency - Quazip bundled and dependency 62.1.2 * Spellchecker fix 62.1.1 * PDF fix 62.1.0 + new version of pugixml XML parser * cmake support is rewritten a lot. * 62.0.1 quick fix + Edit - Select all * Qt6 copy text with new lines - fixed * Markdown functions - fixes + View - Preview Markdown //Qt 5.14+ + QT6 only: Functions - Math - Subtitles: shift timecode by msecs put msecs to FIF. msecs can be negative, i.e "-2000" shifts timecodes by 2000 msecs earlier //works also for Youtube subs + Options - Interface - Show tabs and spaces * find in files FIX * TEA theme FIX - Individual ODT/SXW reader class, the functionality moved to common zipped XML reader + Tune - Functions - Misc - Show ebooks fine (adds spaces before each paragraph) * old bookmarks file automatically converts to new format * bookmarks and recent files has a new format (with a separator *) * recent files list will be updated to new format on use, so the old records will be lost * xml parser changed to pugixml + FB2.ZIP, FBZ support * single application mode fixes + FB2 support improvements + poppler-qt6 support with cmake * DJVU support with cmake - fixed + autosave * braces hl megafix * cmake + hunspell detection fix + Polish UI and Manual translation by Krzysztof Jaśkiewicz + Rust hl support + The time consuming operations such as "Find in files" can be interrupted. * Dates panel upd + SRT hightlighting + Basic Haskell hightlighting * Good old bug with syntax hl engine (related to partial hl module file) is fixed + Youtube subtitles highlighting support + Fn - Case - Capitalize sentences + qt 4.x compat again * Spellcheker module rewrite + lua is added for script intepritators (Fn - Script) + Fn::Script: bat, btm scripts support (Win) + Fn::Script: cmd //REXX * old format for keywords (in syntax hl files) with ";" as the delimeter is dropped, use current one (regexp) instead if you write own hl files see /hls dir for examples + Ctrl-mouse wheel to zoom text at current tab + Functions - Analysis - UNITAZ sorting length + Functions - Filter - Filter by repetitions + Functions - Sort - Sort by length + os2:: single application mode * so2:: tea-qmake.pro is destined to Qt5 now * single application mode - fixed on UNIX/Win * fixes cmake file for install icons and desktop file * OS/2 fix * cmake's make dist creates tarball with dirname as at github, i.e. tea-qt- + Tune::UI Mode:: Classic/Docked - Tune::Interface::Override locale + Tune::Interface::UI Language list * CMake:: TEA QML stuff is disabled by default. To enable: cmake -DUSE_QML=True * Meson:: QML stuff is disabled * Text to HTML - fixed * drag and drop - fixed * Nav - Prev/Next tab, now circled + Tune - Interface :: new, simplified font selectors (also workaround of Qt 5.13 font bug) + Fm - Checksum menu + Fm - Checksum menu - MD4, MD5, SHA1, SHA 224, all SHA-2, 3, Keccak + Functions - Text - Anagram + Tune::Common - Use Enca for charset detection Enca binary can be used to detect encoding. TEA's own detection engine works with Russian/Ukrainian charsets, so Enca is the better option, when installed. + UTF-8 detection has been improved + When open the recent file, editor tab is activated * qt4 compilation fix * "search in files" results window close automatically with TEA * main windows destructor FIX //using deleteLater() * logmemo is scriptable again + Objective C support (hl, header/source switch) * Qt 4.x fix + "Nav - Toggle header/source" moved to IDE menu + --m TEA command line option to enable multiply instance mode + Prg menu:: Run program, Build program, Clean program + Tune :: Logmemo font settings + Profile saves logmemo and GUI font settings + meson/ninja build system support * quazip fixes + Tune::Common:: Syntax highlighting enabled - turn on/off syntax hl globally (useful for large documents on slow machines) 2018 jul 45.0.2 - BSD fix 45.0.1 - MacOS fix 2018 jan + recent list and bookmarks saves the word wrap 2017 nov * desktop/icons stuff fixes 2017 july - User loaded external fonts //causes segfault on Qt 5.6 * other fixes 2017 may * IMPORTANT changes for qmake pro file. The PREFIX for qmake now NOT CONTAINS "bin", i.e. by default was "usr/local/bin", but now is "/usr/local". TEA now installs tea.desktop file to $$PREFIX/share/applications, and tea binary to $$PREFIX/bin 2017 april + coords translation tool * single application mode megafix 2017 + basic block selections 2017 feb + cliptpl.txt to format clipboard pieces captured to the storage file + Fm - Multi-rename + Zero pad file names + Delete N first chars at file names + Replace in file names + Apply template 2016 sept - 43.1.0 * Fixes to the manuals * segfault (43.0.0 affected) on exit fixed * Russian translation update * some new options 2016 aug-sept //for more see github stats + Tune - Images - Use EXIF orientation at image viewer + Tune - Images - Apply hard rotation by EXIF data + new themes and palettes + new hls file format + optional libpoppler support (see README) for text extraction from PDF + optional DJVU text extractor + Tune - Interface - Cursor blink time (msecs), set to zero to turn blinking off + Tune - Interface - Cursor width * Tune pages are scrollable + Functions - Tools - Scale image * %fext, %ffilename, %fdir, %fbasename macros are %ext, %filename, %dir, %basename now + Search - Mark all found + Search - Unmark - Alt-S, Alt-E hardcoded + Tune::Common - Use Left Alt + WASD as additional cursor keys LALT + WASD = cursor movement LALT + E, C = page up/down LWIN and the same keys = select text + Functions - Repeat last * all hls updated to the new format + GIF animation support on the image preview + File - Do not add to recent + prefs UI redesign + Functions - Text - Compress //removes all whitespaces from selection + Functions - Sort - Flip a list with separator, Sort case sensitively, with separator * Instr menu renamed to Tools and moved into Functions + initial themed icons support 2016 summer + basic Markdown support + file path elements in command line to call external program * palettes fix * English manual fix by Dr. Tobias Quathamer. * other fixes 2016 + Functions - math - Sum by last column 2015/September *goto line function fixed 2015/July * qmake prefix option fixed * built-in calc fixes 2015/April - 41.0.0 + themes engine * many fixes 2015/feb + New icons set * source configuration options via qmake has been changed (see README) 2015/jan + Functions - Cells - Sort table by column ABC + Functions - Cells Swap cells + Functions - Cells - Delete by column + Functions - Cells - Copy by column[s] + Partial Eclipse themes support from http://eclipsecolorthemes.org/ (put them into tea Palettes directory) 2014/december + Sorting modes at File Manager * OS/2 building fixes * documentation fixes 2014/november + single instance application mode + new TEA icons + Search - From cursor (option, ON by default) + Tune - Common - Use Alt key to access main menu (option, default is OFF) * misc. fixes 2014/june-october + File - Notes + QML plugins support + items from Programs menu can be used with TEA's file manager (for the current file at the File manager) + code and docs cleanup + Tune - Common - Charset for file open from command line 2014/feb-march + Functions - Statistics - Words lengths + Programs from Run menu can be opened with "file at cursor". Use %i macro. For example: gimp %i Set cursor to filename at the text, then use Run - gimp 2014/jan + zip unpacker can work with multiply selected files 2013/nov * LaTeX support fixes * hardcoded keyboard shortcuts can be redefined 2013/sept-oct * 37.0.0 - many new functions and fixes 2013/sept * LaTeX hl fixed + Functions - Text - Double quotes to TeX quotes //work with "" 2013/aug * some 2013/july * "open at cursor" compatible with local id-labels + grey background of the tab widget to indicate that there is no any new files by default 2013/may-june + new syntax hl engine + full Qt5 support + CLANG support 2013/march + more natural line ending handling. + file manager mult. selection via INS 2013/february + "Wikitext" markup mode is changed to MediaWiki and DokuWiki modes. For the automatical mode switch use files with extensions "mediawiki" and "dokuwiki" 2013/january + new FB2 and ABW parsers + Hunspell on Win32 * all spellchecker stuff is fixed + more QT5 compatibility + @@snippetname as parameter to Functions - Text - Apply to each line 2012/12/24 + Qt 5 compatibility 2012/12/11 //33.3.4 2012/09/18 * File - Save timestamped version now has a different file name format: filename + date + time + ext instead of the old one: date + time + filename + ext 2012/08/28 + --p command lin option for portable mode ON * image converter/scaler fixed 2012/07/22 * Undo fix after "replace all" * built-in calculator now supports braces 2012/04/28 + Qt5 alpha compatibility * Win32/OS2: Aspell path selection from UI - fixed 2012/03/11 * UI styles switching fixed 2012/03/03 + Python hl //very ugly + moon calendar + much more 2012/01/13 + Edit - Set as storage file + Edit - Copy to storage file + Edit - Start/stop capture clipboard to storage file 2012/01/11 + Tune - Common - Documents tabs align + Tune - Common - UI tabs align 2012/01/04 + Calendar - Go to current date * almost all menus are tearable now 2011/12/31 * replacement tables now works also with seletect files //from the file manager * replace all - works with selected files in file manager mode * ODT reader - fixed 2011/12/24 * editor widget redraw optimizations 2011/12/17 * built-in calculator - unner resolution has been changed from float to double 2011/11/16 * fb2 charset support 2011/09/19 * 31.0.0 2011/08/31 * change profiles - fixed 2011/08/07 * 30.1.0 * new stuff etc. 2011/07/03 * zip library support cleanup * TEA can deal with urls from Chrome addr bar //fix 2011/07/02 * OS/2 fixes 2011/06/01 * xml syntax hl fixes + labels 2011/05/20 * more fixes 2011/04/25 + Markup - [X]HTML tools - Rename selected file + Perl hl fixes 2011/04/09 * drag and drop - fixed 100%!!! 2011/04/04 * drag and drop fixed 2011/04/01 * native file save/open dialogs were been disabled to gain the ability of modification 2011/03/09 + some new palettes 2011/03/06 * image processing speed-up * misc fixes and code cleanup 2010/10/15 - all screen-shooting stuff has been removed as buggy and UI non-friendly 2010/07/28 * tabulation width has been fixed * documentation has been updated 2010/07/10 + French UI translation by gholafox 2010/06/24 * drag'n'drop files to TEA - the charset from the file manager's charset list is used * HTML/XML commenting is fixed //28.0.0 2010/05/14 * search backwards - fixed 2010/05/12 + Functions - Images - Save image from clipboard to file 2010/05/06 + task-oriented main menu + Calendar menu //visible when the todo panel is active + Calendar - Add or subtract - years/months/days use the negative value to represent N-days before the selected date + Calendar - Days between two dates 2010/05/05 * all files at TEA config directory will be saved automatically on closing * files from the TEA config directory are not adding to the recent files list + main ui - "todo" panel + Tune - Common - Start week on Sunday * editor area and logmemo manual resizing - fixed + Tune - Images page. All image-related options weve moved here 2010/05/02 + Functions - Images - Capture desktop 2010/04/30 + Functions - Images - Show image from clipboard + Functions - Images - Capture active window + Tune - Images - Screen capture delay, seconds 2010/04/25 + Edit - Comment selection 2010/04/18 + Fman panel - "?" button //the Magical charset guesser button 2010/04/16 * more fman fixes 2010/04/11 //27.1.0 2010/04/04 * drag from the fman fix 2010/04/02 * The Manuals have been updated * Edit- Copy now copies text from the manual, if the Learn tab is active 2010/03/31 //27.0.2 + File - File actions - Set UNIX end of line + File - File actions - Set Windows end of line + File - File actions - Set traditional Mac end of line * Fm - File information - Full info //now detects the end of line of the selected file 2010/03/23 //27.0.1 * Web-gallery tool fixed 2010/02/28 * Morse encoder can handle the lower case text * a few Russian manual fixes 2010/02/25 //27.0.0 + Fm - Select by regexp + Fm - Deselect by regexp for example, to select all *.txt-files, put the following regexps into the FIF:".*\.txt$" (without quotes!) then, use Select by regexp. Then you can press Open button to open all selected files + Fm - File info - Count lines in selected files //select some files, then use this function 2010/02/24 + when launching a program from TEA, the output goes into Logmemo 2010/02/19 * new brackets hl code //from qwriter + app.version() - script func., returns the TEA version 2010/02/15 + View - Profiles + View - Save profile 2010/02/11 + Search - Find in files 2010/02/06 + German UI translation by Tobias Quathamer 2010/02/05 * Win32 version uses ini-file config instead of Registry 2010/02/04 + app.call_menuitem (item_name) function is available from QtScript/JS script you can activate any TEA's menu item by its caption. For example: fif.setText ("hello~hallo"); app.call_menuitem ("Replace all"); please note that the menu item name is a localized version, i.e. call the translated menu item, if the translation is exists 2010/01/25 + line numbers area //based on qwriter code 2010/01/23 //26.2.2 * xml/html, clike hl modules fixed + new FIF - the editable combobox instead of the old line edit 2010/01/22 * old FIF autocompletion mode is coming back 2010/01/07 * XML/HTML syntax hl improvements 2010/01/04 //26.2.0 * some fixes to make TEA GCC4x compatible * other fixes 2009/12/27 + Edit - Indent by first line 2009/12/22 + Fman - Images - Create web gallery * "Fman - Image conversion" has been renamed to "Images" + "Tune - Functions - Web gallery options" section * Tune page UI improvemets 2009/12/16 + new FIF autocompletion UI 2009/12/07 //26.1.0 2009/12/04 + LilyPond basic hl + NASM hl * TEA now loads the last selected palette. And old mechanism via "def_palette" file now is obsolete. To tweak the palette, create the own one. 2009/11/14 + Functions - Filter - Remove before delimiter at each line + Functions - Filter - Remove after delimiter at each line 2009/11/06 + "margin_color" color name for palette files. If no margin_color is defined, TEA takes the text color as one. + Bash script hl module 2009/10/20 * syntax hl inner changes. Strikeout and underline font style parameteres has been added 2009/10/17 * syntax hl engine fixes 2009/09/31 -- 26.0.0 + Functions - Remove from dictionary //This function is Hunspell-only. An updated dictionary will be loaded after the next session. + Vala syntax hl module * hl engine fixes (now keywords are bold only with "fontstyle=bold" attribute at the hl file) * "replace all" function now can be case-insensetive * misc documentation fixes and additions 2009/09/26 + Lua syntax hl module + Perl syntax hl module 2009/09/21 + "File - Edit bookmarks" menu * "Add to bookmarks" has been moved to "Edit bookmarks" + File - Edit bookmarks - Find obsolete paths //all obsolete bookmarks will be prefixed with # * dynamic menu items those started from # are not visible anymore, so you can comment out any line at the list-like config file such as external programs list, etc. + qmake options: USE_ASPELL=true/false //true by default USE_HUNSPELL=true/false //true by default example to disable Hunspell, but with the Aspell supported: qmake USE_HUNSPELL=false to disable just Aspell: qmake USE_ASPELL=false to disable both: qmake USE_HUNSPELL=false USE_ASPELL=false to enable all: qmake 2009/09/20 + Tune::Functions - Hunspell dictionaries directory + Tune::Functions - Spell checker engine 2009/09/15 + qmake PREFIX=path works fine 2009/08/12 + Search - Replace all in opened files 2009/08/11 * document tabs are movable 2009/08/10 //25.1.0 2009/08/06 + Tune::Interface - "Cursor center on scroll" option //can be buggy * very cool fixes 2009/08/05 * MRU list fixes + Margin options at the Tune::Interface page 2009/07/25 //25.0.0 2009/07/10 + --charset=codepage command line option is re-implemented 2009/07/07 + new KWD and DOCX format readers 2009/07/02 + new ODT format reader 2009/07/01 + FIF can search in the File manager //case-insensetive, with MatchStartsWith option * spell checker progress shows correctly 2009/06/01 * all dynamic menus can be teared off * Lout IncludeGraphic support on Insert image 2009/05/28 + Functions - Math - Binary to decimal //works with unsigned int binary numbers only + Search - Regexp mode //affect the searching, tables + more UI tweaks for smalls-screen devices * all options from UI::Tune::Rare moved to UI::Tune::Common * "Tab width in spaces" option moved to UI::Tune::Interface * fixes at the tables engine 2009/05/27 //23.7.0 * main window can be resized to any size //useful for EEE PC 701 users 2009/05/26 + Functions - Tables 2009/05/24 //23.6.0 * win32: Templates and snippets were been fixed 2009/05/23 + Functions - Math - Decimal to binary //use with decimal representation + Functions - Math - Flip bits (bitwise complement) //use with binary representation 2009/05/21 + Markup - HTML tools - Strip HTML tags 2009/05/16 + "+" and "-" keys scales image at the image viewer + drag from the TEA's file manager to outside 2009/04/16 * some file manager improvenents 2009/04/04 + View - Highlighting mode menu to set the hl-mode manually + File manager::places - configs item //to speed-up access to TEA config files + Markup - Mode - Lout 2009/04/03 + FIF works to search at the Tune/Keyboard/Shortcuts list + Lout syntax HL (if the file has the extension "lout" - foobar.lout) 2009/04/01 * Tune window fixes 2009/03/25 + PHP syntax hl - made by Boo-boo 2009/03/24 //23.3.0 + D syntax hl + "Fm - File information - Full info" can read WAV file properties and calculate the RMS. RMS is calculated for 16 bit PCM files only. 2009/03/10 + Enter key work at the file name entry at the file manager. If user used Open file, Enter key acts to open file, if the "Save as" is used, Enter acts like fileman's "Save as" button 2009/03/07 //23.2.0 + FB2 format read-only support (via the inner ABW tio module) * file manager fixes 2009/03/06 * string list functions fixes * OOP design fixes, tea binary site has been reduced by ~10 kbytes * resourses cleanup 2009/03/05 + Functions - Sort - Sort case insensitively 2009/03/03 //23.1.1 * indent/unindent fixed + Verilog hl + Tune - Rare - Use wrap setting from highlighting module + some focusing fixes for the file manager 2009/02/28 //23.1.0 + Edit - Indent + Edit - Un-indent 2009/02/27 + Tune - Rare page * "Use traditional File Save/Open dialogs" and "Override locale" options have been moved to the Rare page * templates - fixed * LaTeX markup mode small fixes 2009/02/26 + tab - indent the selected block by space or tab + shift-tab - unindent by the one space or tab //depending of "Use spaces instead of tabs" options - zip code:: crypt.h //because TEA doesn't support passworded ZIP-files 2009/02/23 + Tune - Common - Automatic indent + Tune - Common - Use spaces instead of tabs + Tune - Functions - Tab width in spaces + Tab key at the editor indents selection (if some text is selected) 2009/02/22 + image viewer::rotate by [ and ] keys 2009/02/16 * Morse code table fixed + image viewer (not a automatic previewer) can handle Space, PageUp/Down, Home and End keys to navigate through the images in the current directory * statistics::line count for the selected text - fixed 2009/02/12 * for "Automatic preview images" TEA reads the thumbnails from the .thumbnails directory //*nix only 2009/02/11 + Tune - Common - Automatic preview images at file manager //off by default + Fm - Preview image //for the current selected at the file manager * "File - Open at cursor" works in a same way when the file manager is focused 2009/02/09 //23.0.0 + BASIC syntax hl + new logo at the About window + LaTeX syntax hl 2009/02/07 + Fm - ZIP - Create new ZIP + Fm - ZIP - Add to ZIP + Fm - ZIP - Save ZIP 2009/02/05 + SLA (Scribus) format //read only 2009/02/04 + Functions - Text - Escape regexp + weak RTF support //read only 2009/02/03 + input box for Save session //instead of file name in FIF 2009/02/02 + ODT, SXW (old OOo format), KWD (old KWord format), ABW (AbiWord), DOCX documents //read only 2009/01/26 + Fman - Image conversion - Scale by side + Fman - Image conversion - Scale by percentages //1. put the val into the FIF 2. select images 3. apply the function * Fortran hl fixes + Tune - Functions - Image conversion output format + Tune - Functions - Scale images with bilinear filtering + Tune - Functions - Output images quality //mainly for JPEG. Use -1 for default settings, otherwise 0..100 + Tune - Functions - Zip directory with processed images 2009/01/21 //22.3.0 + Initial Fortran syntax hl. The fortran.xml is based on Fortran 90 spec + C# syntax hl 2009/01/20 + Functions - Text - Remove trailing spaces //on the each line 2009/01/20 //22.2.1 * Aplly to each line - fixed 2009/01/17 //22.2.0 + Snippets, Scripts, Sessions and Templates now can hold submenus - Qt version checking in src.pro is removed (it never worked fine on some distros) 2009/01/14 * Search options are saving now * Image viewer hides itself when the file manager lost a focus + Autocompletion for the FIF 2009/01/11 //22.1.0 * MD5 checksum evaluation - fixed 2009/01/10 * paired braces hl - fixed 2009/01/09 * Toggle header/source - fixed + Functions - Analyze - Count the substring (regexp) + Functions - Analyze - Count the substring //the "Search - Case sensitive" is used as an option //Use the FIF to define a substring. 2009/01/07 * text to html - fixed * several memory leaks - fixed * markup engine inner changes 2009/01/02 //22.0.1 * shortcuts bug - fixed * "keys" file is renamed to "shortcuts", so all your previous hotkeys were lost assign them again! * "famous input field" is used instead of FEF :) 2009/01/02 * shortcuts engine - fixed * the "fte" script object is renamed to "fef" according to the FTE > FEF renaming 2008/12/31 + File manager :: backspace navigates the fman to the directory at the upper level - old image viewer * syntax hl engine can be case insensetive 2008/12/29 + Seed7 syntax hl + much better C/C++ hl 2008/12/27 + Search - Whole words option + Search - Case sensitive 2008/12/25 * the "setup" tab has been renamed to "tune" * "replace all" - fixed 2008/12/22 + Functions - Analyze - UNITAZ quantity sorting + Functions - Analyze - UNITAZ sorting alphabet - Functions - Analyze - UNITAZ * Ctrl-F can be assigned twise - for Search and for the Focus the Famous entry field. Please remove the first assignment manually to make the second one working properly 2008/12/21 //21.1.3 * some UNITAZ fixes and improvements 2008/12/20 //21.1.2 + Functions - Analyze - UNITAZ 2008/12/19 * c/c++ hl fixed 2008/12/17 //21.1.1 * hl fixes * Pascal hl file fixed 2008/12/16 //21.1.0 * "Functions - Statistics" is renamed to Analyze * Extraction - Exract words is moved to Analyze - Extraction submenu - Single application mode 2008/12/15 * misc inner changes * single_application engine is updated to 1.0 2008/12/10 //21.0.5 + File - File actions - Reload //i.e. revert to saved + File - File actions - Reload with encoding //use double click to reload current document with the selected charset 2008/12/08 //21.0.4 * Pascal hl fixed * some other bug fixes 2008/11/29 //21.0.3 * spellchecker is fixed!!! + progressbar for the spellchecker 2008/11/27 //21.0.2 * src.pro: QT version checking is disabled due to some reasons * the inner changes to improve the loading speed 2008/11/24 * IDE switched to QDevelop ;) * Ctrl-F is now assigned for Focus the Famous text entry * some bugs were fixed 2008/11/19 - 21.0.0 2008/11/14 + Fm - File information - MD5 checksum + Fm - File information - MD4 checksum + Fm - File information - SHA1 checksum 2008/11/12 + new file manager + menu Fm + Fm - File operations - Create new directory + Fm - File operations - Rename + Fm - File operations - Delete file * the < and > buttons of the Famous text entry is working + Famous text entry is the search field for the Manual browser - Manual browser own search entry 2008/10/24 * QTextEdit is replaced by QTextEdit to improve large files editing + Java Script as built-in macro-language //see the Manual 2008/10/09 + new manual browser 2008/10/01 + Java 3.0 syntax highlighting + Pascal syntax highlighting (Turbo/Borland Pascal, Object Pascal, Free Pascal) + new highlighting engine + View - Palettes 2008/09/24 + Markup - [X]HTML Tools - Preview selected color //the color can be in the form of the hex value or the named color (darkBlue, red, white, etc) * Last opened file is renamed to Last closed file 2008/09/08 * "Additional highlighting" is renamed to "Highlight current line" + "Highlight paired brackets" option 2008/09/03 //19.1.1 * Apply to each line - fixed 2008/09/01 //19.1.0 - good 2008/08/29 + Statistics::author's sheets + File - Sessions + File - Save different - Save session + Prefs::Restore the last session on start-up 2008/08/18 + View - Stay on top 2008/08/15 + Qt version detection on the qmake stage + Functions - Math - Enumerate Enumerates lines from the selected text. Params = Famous text entry. Syntax: step~zero_padding~prefix Examples: "1~1~) " //without quotes "1~3~ " "10" "10~5" 2008/08/04 //19.0.5 * norv.wood theme bug is fixed 2008/08/04 //19.0.4 * spellchecker words parser is fixed 2008/08/04 //19.0.3 * more fixes 2008/08/04 //19.0.2 * some file manager fixes 2008/08/04 //19.0.1 + Preferences - Override locale option //to redefine the program's locale, use the two-letters code: en, ru, uk, etc. + Single instance mode for Windows 2008/08/01 //19.0.0 2008/07/30 + Functions - Text - Quotes to facing quotes 2008/07/28 + Dialogs remembers their sizes * SingleApplication library copy is relicensed to GPL (from LGPL) for an accordance of the main program license. see LGPL, section 3: "You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices". 2008/07/25 + UNIX: File manager + UNIX: single-instance mode * UNIX: "Insert image" calls the file manager page + UNIX: Preferences - Use traditional File save/open dialogs - TEAVisor 2008/07/18 * fixed: restore the positions of toolbars 2008/07/17 + NorwegianWood UI style //from QT demo 2008/07/16 //18.1.1 * templates/snippets/scripts shortcuts at File Save/Open dialogs are working by default. The reason of a non-working state: when the hidden files visibility of off, those shortcurs are not working 2008/07/16 //18.1.0 * obscure "QObject: Do not delete object, 'unnamed', during its event handler" is fixed 2008/07/15 + Qt 4.4 is minimum requirement * fixed: open file from the command line with a full path as the parameter 2008/07/14 - 18.0.3 + Qt 4.4 compatibility. TEA doesn't crushes on start anymore * more fixes 2008/07/12 * antispam e-mail - fixed 2008/07/11 + Scripts support //compatible with TEA-GTK * some fixes 2008/07/08 * "Highlight current line" option is changed to "Additional highlighting" + with Additional highlighting enabled, TEA highlights the current line and pair brackers 2008/04/12 - initial release of TEA Qt branch tea-qt-63.3.0/FUNDING.yml000066400000000000000000000000221476733534200146230ustar00rootroot00000000000000patreon: semiletovtea-qt-63.3.0/NEWS000066400000000000000000000006551476733534200135210ustar00rootroot00000000000000====NEWS ABOUT TEA==== TEA 63.3.0, March 2025 ------------------------------------------------- http://tea.ourproject.org ------------------------------------------------- * "Save as different" renamed to "Save more" + File - Save more - Save all existing * In some cases, such as IDE - header/source switch, TEA now just opens file and not reload it if the file is already opened (bug fixed) Stay tuned. Peter Semiletov tea-qt-63.3.0/NEWS-RU000066400000000000000000000011561476733534200140420ustar00rootroot00000000000000=====НОВОСТИ ПРО ТИА==== ТИА 63.3.0, март 2025 ====================== http://tea.ourproject.org --------------------------------------------------- Исправлено давнео зло, то есть недоработка, которая приводила к неправильному поведению ТИА при открытии уже открытых файлов. Добавлена функция "Сохранить все существующие открытые файлы". С кирпичным пролетарским приветом, Петр Семилетов!tea-qt-63.3.0/README-PL.txt000066400000000000000000000136611476733534200150320ustar00rootroot00000000000000ZAWARTOŚĆ NINIEJSZEGO README: 01 - INSTALACJA OD ŹRÓDŁA 02 - UWAGI DLA KONSERWATORÓW PAKIETÓW 03 - UWAGA DLA UŻYTKOWNIKÓW UBUNTU 04 - UWAGI LICENCYJNE 01: INSTALACJA OD ŹRÓDŁA Możesz zainstalować TEA ze źródła na 4 sposoby, używając systemów budowania qmake / make, meson / ninja, cmake / make, cmake / ninja. Ale najpierw musisz zainstalować kilka bibliotek programistycznych. Obowiązkowy: Qt 4.8 lub Qt 5.4+ lub Qt 6, zlib Opcjonalny: libaspell (do silnika sprawdzania pisowni), libhunspell (do silnika sprawdzania pisowni), poppler-qt5 lub poppler-qt6 (do czytania tekstu z PDF), ddjvuap (do czytania tekstu z DJVU) Uwaga dla użytkowników FreeBSD : potrzebujesz pakietu pkgconf - pkg install pkgconf Którego systemu kompilacji należy użyć? Użyj qmake dla: Qt 4, starych dystrybucji i Windows. Użyj mezon lub cmake dla nowoczesnych dystrybucji. cmake jest głównym systemem budowania dla TEA. 01.01 CMAKE Dzięki cmake TEA obsługuje kompilację Qt5 i Qt6. Jeśli chcesz zbudować i zainstalować TEA za pomocą cmake + make, uruchom na źródle TEA reż: mkdir b cd b cmake .. make make install (jako root lub sudo) Aby zbudować i zainstalować TEA z cmake / ninja i GCC, wykonaj: mkdir b cd b cmake -GNinja .. ninja ninja install Domyślnie cmake buduje TEA bez niektórych funkcji: obsługi drukarki i aspell, libpoppler i djvuapi. Aby je włączyć, użyj z katalogu kompilacji: cmake .. -DUSE_ASPELL = ON -DUSE_PRINTER = ON -DUSE_PDF = ON -DUSE_DJVU = ON Jeśli Qt5 i Qt6 są obecne w systemie, użyj zmiennej CMAKE_PREFIX_PATH, aby ustawić ścieżkę do QtN. W przeciwnym razie preferowany będzie Qt6. Przykłady: cmake -DCMAKE_PREFIX_PATH=/usr/lib/qt .. //usr/lib/qt is the directory with qt5 cmake -DCMAKE_PREFIX_PATH=/usr/lib/qt6 .. //usr/lib/qt6 is the directory with qt6 cmake -DCMAKE_PREFIX_PATH=$HOME/Qt/6.0.0/gcc_64/lib/cmake .. //tutaj wskazujemy na lokalnie zainstalowany Qt6 01.02 MESON Dzięki mezonowi TEA obsługuje kompilację Qt5. Aby zbudować i zainstalować TEA z meson / ninja i GCC, wykonaj: mkdir b meson cd b ninja ninja install Aby zbudować i zainstalować TEA z meson / ninja i CLANG, wykonaj: mkdir b CC = clang CXX = clang ++ meson b cd b ninja ninja install Aby włączyć obsługę ekstrakcji tekstu PDF i DJVU oraz obsługę Aspell (domyślnie wyłączona, a także obsługa drukowania): mkdir b meson b meson configure -Dpdf = włączone -Ddjvu = włączone -Daspell = włączone b cd b ninja ninja install 01.03 QMAKE Dzięki qmake TEA obsługuje kompilację Qt4 i Qt5. Z qmake budowanie jest proste: qmake make make install (jako root lub sudo) Aby dokonać konfiguracji źródła (za pomocą qmake), użyj zmiennej CONFIG w parametrze wiersza poleceń qmake. Na przykład: qmake "CONFIG + = useclang" "CONFIG + = noaspell" Możesz użyć kilku wartości: nosingleapp - nie buduj TEA z obsługą trybu pojedynczej aplikacji nodesktop - nie instaluj plików pulpitu i ikon useclang - TEA zostanie skompilowana z Clang noaspell - wyłącz Aspell (jeśli masz go zainstalowanego, ale nie chcesz kompilować TEA z Aspell support) nohunspell - wyłącz Hunspell dla TEA usepoppler - użyj libpoppler-qt5 lub qt4 do importu warstwy tekstowej PDF. WYŁĄCZONE domyślnie usedjvu - użyj libdjvulibre do czytania tekstu plików DJVU (tylko do odczytu). WYŁĄCZONE domyślnie noprinter - wyłącz obsługę drukowania ** Uwagi: ** Jeśli zainstalowałeś zarówno Qt4, jak i Qt5, użyj qmake z Qt4 lub Qt5, aby skonfigurować TEA z dokładną wersją QT. Typowym rozwiązaniem jest utworzenie dowiązania symbolicznego do qmake z Qt5 i nazwanie go qmake5, a następnie użycie qmake5 zamiast zwykłego qmake. Jeśli menu kontekstowe w TEA nie są zlokalizowane, zainstaluj pakiet qttranslations lub qt-Translations z repozytorium swojej dystrybucji. / * Podstawowy fragment kodu dla użytkowników Ubuntu (kompilacja Qt5) - uruchom go z Terminala w katalogu źródłowym TEA (rozpakowany): sudo apt-get install g ++ pkg-config sudo apt-get install zlib1g-dev libaspell-dev libhunspell-dev sudo apt-get install qt5-default qttools5-dev-tools sudo apt-get install libqt5qml5 libqt5quick5 qtdeclarative5-dev qmake make sudo make zainstalować Snippet dla użytkowników Ubuntu (kompilacja Qt4): sudo apt-get install g ++ pkg-config sudo apt-get install zlib1g-dev libaspell-dev libhunspell-dev sudo apt-get install libqt4-dev qt4-dev-tools qmake make sudo make install * / 02: UWAGI DLA KONSERWATORÓW PAKIETÓW Dziękujemy za opakowanie TEA! Chociaż TEA ma dwie strony domowe, lepiej jest użyć wersji Github jako źródła: https://github.com/psemiletov/tea-qt/archive/$pkgver.tar.gz Pamiętaj, że katalog źródłowy TEA po rozpakowaniu będzie wyglądał następująco: tea-qt - $ {pkgver} TEA po kompilacji to pojedynczy plik binarny (z osadzonymi zasobami). TEA obsługuje 3 systemy kompilacji: qmake - tradycyjny, dobry dla kompilacji Qt4-Win32-OS / 2-Slackware. Plik projektu qmake firmy TEA jest stary i niejasny. cmake - dobre dla kompilacji Qt5 / Qt6, referencyjne dla TEA. Polecam użyć cmake do zbudowania pakietu TEA. mezon - używam go wewnętrznie. Nie ma obsługi drukarki. W przypadku kompilacji qmake, aby zastąpić domyślną ścieżkę instalacyjną (/ usr / local, binarną w / usr / local / bin) podłoże: qmake PREFIX = twoja_ścieżka make make install 03: UWAGA DLA UŻYTKOWNIKÓW UBUNTU Skróty klawiszowe zdefiniowane przez użytkownika mogą nie działać z powodu funkcji globalnego Qt5 i Jedności. Aby skasować menu globalnego w aplikacjach Qt5, zrób sudo apt-get autoremove appmenu-qt5 lub, jeśli chcesz usunąć globalne menu GTK, gra: sudo apt-get autorove appmenu-gtk appmenu-gtk3 appmenu-qt5 04: UWAGI LICENCYJNE Kod TEA jest objęty licencją na licencji GPL V3 i pracuje jako domena publiczna. Media TEA (obrazy itp.), Wydania podręczników i tłumaczenia są wydawane. Uwaga dla współpracowników - prosimy o umieszczenie swoich tłumaczeń w domenie publicznej lub na licencji GPL. ==== tea-qt-63.3.0/README.md000066400000000000000000000142531476733534200143000ustar00rootroot00000000000000# TEA # TEA is a C++, Qt(4,5,6) text editor with the hundreds of features for Linux, *BSD, Mac, Windows, OS/2 and Haiku. Home site > http://tea.ourproject.org Development > https://github.com/psemiletov/tea-qt My hot AUR package > https://aur.archlinux.org/packages/tea-qt-git/ Donate > PayPal: peter.semiletov@gmail.com BuyMeACoffee: https://www.buymeacoffee.com/semiletov Patreon: https://www.patreon.com/semiletov Communities > https://t.me/teaqt https://www.facebook.com/groups/766324686841748/ ## CONTENTS OF THIS README: ## 01 - INSTALLATION FROM THE SOURCE 02 - NOTES FOR PACKAGE MAINTAINERS 03 - NOTE FOR UBUNTU USERS 04 - LICENSE NOTES ### 01: INSTALLATION FROM THE SOURCE ### You can install TEA from the source in many ways, using build systems qmake/make, cmake/make, cmake/ninja. But first, you need to install some development libraries. **Mandatory:** Qt 4.8 or Qt 5.4+ or Qt 6 **Optional:** TEA has some optional dependencies, those extends the functionality and can be turned on/off during pre-build configuration. Here is a list with library names and cmake command line switches: libaspell: -DUSE_ASPELL=ON //enables Aspell spell checker, OFF by default nuspell: -DUSE_NUSPELL=ON //enables Nuspell engine, that uses Hunspell dictionaries, OFF by default libhunspell: -DUSE_HUNSPELL=ON //enables Hunspell engine, ON by default poppler-cpp: -DUSE_PDF=ON //to read the text from PDF, OFF by default ddjvuapi: -DUSE_DJVU=ON //to read the text from DJVU, OFF by default speech-dispatcher: -DUSE_SPEECH=ON //makes TEA the speech-dispatcher client, to speach the text. OFF by default, and can be turned of when build with it. **Note for FreeBSD users**: you need the pkgconf package - pkg install pkgconf Which build system you should use? Use qmake for: Qt 4, old distros and Windows. Use cmake for modern distros. cmake is the mainline build system for TEA. #### 01.01 CMAKE #### With cmake, TEA supports Qt5 and Qt6 build. If you want to build and install TEA with cmake + make, run at the TEA source dir: mkdir b cd b cmake .. make make install (as root or with sudo) To build and install TEA with cmake/ninja and GCC, do: mkdir b cd b cmake -GNinja .. ninja ninja install By default, cmake builds TEA without some features: printer and aspell support, libpoppler and djvuapi. To enable them, use from the build directory: cmake .. -DUSE_ASPELL=ON -DUSE_PRINTER=ON -DUSE_PDF=ON -DUSE_DJVU=ON If the Qt5 and Qt6 both are present on the system, use CMAKE_PREFIX_PATH variable to set the path to the QtN. Otherwise, Qt6 will be prefered. Examples: cmake -DCMAKE_PREFIX_PATH=/usr/lib/qt .. //usr/lib/qt is the directory with qt5 cmake -DCMAKE_PREFIX_PATH=/usr/lib/qt6 .. //usr/lib/qt6 is the directory with qt6 cmake -DCMAKE_PREFIX_PATH=$HOME/Qt/6.0.0/gcc_64/lib/cmake .. //here we point to the locally installed Qt6 #### 01.02 QMAKE #### QMake building system is outdated and supported by TEA just as legacy and to make TEA works at old distros or systems (Windows XP for example). TEA's qmake project file supports Qt4 and Qt5 build. With qmake to build is simple: qmake make make install (as root or with sudo) To make some source configuration (with qmake), use CONFIG variable at qmake command line parameter. For example: qmake "CONFIG+=useclang" "CONFIG+=noaspell" You can use some values: nosingleapp - do not build TEA with the single application mode support nodesktop - do not install desktop files and icons useclang - TEA will be compiled with Clang noaspell - disable the Aspell (if you have it installed, but do not want to compile TEA with Aspell support) nohunspell - disable Hunspell for TEA usepoppler - use libpoppler-cpp for PDF text layer import. DISABLED by default usedjvu - use libdjvulibre to read DJVU files text (read only). DISABLED by default noprinter - disable printing support ** Notes: ** 1. If you have installed both Qt4 and Qt5, use the qmake from Qt4 or Qt5 to configure TEA with exact version of QT. The common solution is to make symlink to qmake from Qt5 and name it qmake5, then use qmake5 instead of the usual qmake. 2. If the context menus in TEA are not localized, install the qttranslations or qt-translations package from your distro's repository. /* Some outdated snippets for Ubuntu. Basic snippet for Ubuntu users (Qt5 build) - run this from Terminal at the TEA source directory (unpacked): sudo apt-get install g++ pkg-config sudo apt-get install zlib1g-dev libaspell-dev libhunspell-dev sudo apt-get install qt5-default qttools5-dev-tools qmake make sudo make install Snippet for very old Ubuntu: (Qt4 build): sudo apt-get install g++ pkg-config sudo apt-get install zlib1g-dev libaspell-dev libhunspell-dev sudo apt-get install libqt4-dev qt4-dev-tools qmake make sudo make install */ ### 02: NOTES FOR PACKAGE MAINTAINERS ### 0. Thank you for packaging TEA! 1. Altough TEA has two home sites, it is better to use Github releases as the source: https://github.com/psemiletov/tea-qt/archive/$pkgver.tar.gz Please note, that TEA source dir after unpacking will be tea-qt-${pkgver} 2. TEA after the compilation is a single binary file (with embedded resources). 3. TEA supports 3 build systems: **qmake** - the traditional one, good for Qt4-Win32-OS/2-Slackware builds. TEA's qmake project file is old and obscure. **cmake** - good for Qt5/Qt6 build, the reference number one for TEA. I recommend to use cmake to build TEA package. 4. For the qmake build, to override the default installation path (/usr/local, with binary at /usr/local/bin) use: qmake PREFIX=your_path make make install ### 03: NOTE FOR UBUNTU USERS ### User defined hotkeys may not work due to Qt5 and Unity global menu feature. To remove global menu support in Qt5 apps, do sudo apt-get autoremove appmenu-qt5 or, if you want to remove also GTK global menus, use: sudo apt-get autoremove appmenu-gtk appmenu-gtk3 appmenu-qt5 ### 04: LICENSE NOTES ### TEA code is licensed under GPL V3 and, partially, as a Public Domain. TEA media (images, etc), manuals and translations are public domain. Note to contributors - please put your translations into the public domain or GPL. tea-qt-63.3.0/TODO000066400000000000000000000054751476733534200135170ustar00rootroot00000000000000user dict - сделать независимым от регистра переопределить контекстное меню! * темы оформления в Win32 глючат при стиле интерфейса Windows XP //так и должно быть make single application with Dbus? опять single app не пашет при открытии файлов из thunar show empty spaces (at EOL? for Markdown) Chess visualizer window "Места" в ФП remove lout support? https://api.kde.org/frameworks/syntax-highlighting/html/index.html *вернуться к SVG-иконкам? ПЕЧАТЬ НЕ РАБОТАЕТ под вындой! Фильтр - Больше размера > - удаляет всё? OS/2::File manager, double click on folders work with the right double click only cmake dist creates wrong tarball that cannot be build with meson JAMSpell support? (https://github.com/bakwc/JamSpell) при повторении последней функции, сендер-ту становится от пункта меню "повторить" избранное для ЗПВ если перенос строк выключен в настройках, то из меню Вид перенос строк не работает при переключении темы, иконки меняются только после перезагрузки TEA file compare filesToolbar странно меняет размер иконки спеллчекер плохо работает с кавычками елочкой вставка картинки как base64? в сборке Qt5, шорткаты с Alt стали языко-зависимыми? CSound, Ada, gas, List hl массовая перекодировка выделенных файлов. окно ИЗ - В вытащить меню таблиц мультизамен в режим фприказчика, проверить обработку batch-обработка картинок: обрезка, масштабирование, поворот, зеркало? при закрытии календарной записи будет автоматический возврат на панель календаря autosave bookmarks? autocompletion broken local links checking Highlight all search results gradient colorization in palettes "entity to text" function autoscroll //useful for musicians or youtube bloggers. define the speed in bpm? numbers into words ctags support units conversion unicode table window split document panel play the chords by name, i.e. select AmDm, then use something like "Play melody" chords to tabs, heh heh parse the compiler output, goto the error by clicking at logmemo 0-9 markers goto start/end of the code block gz file save? использовать libretta::CPainFile вместо QSettings? tea-qt-63.3.0/desktop/000077500000000000000000000000001476733534200144655ustar00rootroot00000000000000tea-qt-63.3.0/desktop/tea.desktop000066400000000000000000000007771476733534200166440ustar00rootroot00000000000000[Desktop Entry] Name=TEA Terminal=false Type=Application Icon=tea Exec=tea %F Categories=Utility;TextEditor; StartupNotify=false MimeType=text/plain;application/epub+zip;application/fb2;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.oasis.opendocument.text;application/x-tex; NoDisplay=false Version=1.1 Keywords=text editor;text;editor; Keywords[de]=Texteditor;Text;Editor; Comment=Text editor with hundreds of functions Comment[de]=Texteditor mit hunderten Funktionen tea-qt-63.3.0/encsign/000077500000000000000000000000001476733534200144425ustar00rootroot00000000000000tea-qt-63.3.0/encsign/CP1251000066400000000000000000000001221476733534200151730ustar00rootroot00000000000000 , , , , , , , , , , , , , , tea-qt-63.3.0/encsign/CP866000066400000000000000000000001231476733534200151270ustar00rootroot00000000000000 , , , , , , , , , , , , , , ,tea-qt-63.3.0/encsign/KOI8-R000066400000000000000000000001221476733534200152710ustar00rootroot00000000000000 , , , , , , , , , , , , , , tea-qt-63.3.0/encsign/KOI8-U000066400000000000000000000001151476733534200152760ustar00rootroot00000000000000 , צ , ˦ , , Φ , , , ͦ , ' , Ц , , ަ , tea-qt-63.3.0/hls/000077500000000000000000000000001476733534200136025ustar00rootroot00000000000000tea-qt-63.3.0/hls/awk.xml000066400000000000000000000025241476733534200151110ustar00rootroot00000000000000 \b(BEGIN|END|if|for|function|in|else|while|do|break|continue|exit)\b \b(ARGIND|ARGC|ARGV|BINMODE|ENVIRON|ERRNO|FILENAME|FIELDWIDTHS|FS|FNR|IGNORECASE|IF|LINT|PROCINFO|TEXTDOMAIN|NF|NR|OFMT|OFS|ORS|RLENGTH|RS|RSTART|SUBSEP)\b \b(print|printf|atan2|cos|exp|int|log|rand|sin|sqrt|srand|asort|asorti|gsub|index|length|match|split|sprintf|strtonum|sub|substr|tolower|toupper|systime|mktime|strftime|and|compl|lshift|rshift|or|xor|close|delete|exit|fflush|getline|next|nextfile|return|system)\b ^#.* ("[^\"]*") ('[^']*') #.* /\* \*/ #%s #%s tea-qt-63.3.0/hls/bash.xml000066400000000000000000000021211476733534200152350ustar00rootroot00000000000000 \b(break|builtin|caller|case|command|continue|coproc|declare|do|done|elif|else|enable|esac|exit|export|fi|for|getopts|if|let|local|logout|mapfile|return|select|set|shift|shopt|test|then|typeset|until|while)\b \b(alias|bind|cd|chown|echo|eval|exec|hash|help|mdir|mkdir|printf|pwd|read|readarray|readonly|rm|source|times|trap|type|ulimit|umask|unalias|unset)\b ^#include ("[^\"]*") ('[^']*') #.* /\* \*/ #%s tea-qt-63.3.0/hls/basic.xml000066400000000000000000000031131476733534200154030ustar00rootroot00000000000000 \b(and|andalso|as|byref|call|cast|circle|cls|color|const|cptr|declare|defint|defsng|defstr|delete|dim|do|draw|dynamic|else|end|enum|eqv|erase|exit)|explicit|extern|for|function|get|getkey|if|imp|inkey|input|is|line|locate|loop|mod|new|next|not|option|or|orelse|peek|poke|printprivate|procptr|public|put|redim|return|scope|screen|screenres|shared|shl|shr|sleep|strptr|sub|then|type|union|until|var|varptr|wend|while|with|xor\b \b(byte|const|double|integer|long|longint|pointer|ptr|short|single|static|string|ubyte|uinteger|ulong|ulongint|unsigned|wstring|zstring)\b [-=\+\*\/\|] [\(\)\[\]] ^#include ^#.* ("[^\"]*") '[^\n]* rem [^\n]* /\* \*/ ' %s tea-qt-63.3.0/hls/clike.xml000066400000000000000000000035051476733534200154160ustar00rootroot00000000000000 \b(asm|asm|auto|break|case|catch|catch|class|const|const_cast|const_cast|continue|default|delete|do|dynamic_cast|dynamic_cast|else|enum|explicit|export|extern|false|for|foreach|friend|goto|if|inline|mutable|namespace|namespace|new|noexcept|operator|private|protected|public|register|reinterpret_cast|reinterpret_cast|return|signals|sizeof|slots|static|static_cast|static_cast|struct|switch|template|this|throw|throw|true|try|typedef|typeid|typename|union|using|virtual|volatile|while)\b \b(bool|char|double|float|int|long|short|signed|size_t|unsigned|uint|void|wchar_t)\b \bQ[A-Za-z0-9_]+\b \bC[A-Za-z0-9_]+\b [-=\+\*\/\|] [\(\)\[\]] ^#include ^[a-zA-Z_][a-zA-Z0-9_]*\:$ ^#.* ("[^\"]*") ('[^']*') //[^\n]* /\* \*/ /*%s*/ //%s tea-qt-63.3.0/hls/cs.xml000066400000000000000000000026351476733534200147370ustar00rootroot00000000000000 \b(as|base|break|case|catch|checked|class|const|continue|default|delegate|do|else|enum|event|explicit|extern|false|finally|fixed|for|foreach|goto|if|implicit|in|interface|internal|is|lock|namespace|new|null|operator|out|override|params|private|protected|public|readonly|ref|return|sealed|sizeof|stackalloc|static|struct|switch|this|throw|true|try|typeof|unchecked|unsafe|using|virtual|volatile|while)\b \b(bool|byte|char|decimal|double|float|int|long|object|sbyte|short|string|uint|ulong|ushort|void)\b [-=\+\*\/\|] [\(\)\[\]] ^#.* ("[^\"]*") ('[^']*') //[^\n]* /\* \*/ /*%s*/ //%s tea-qt-63.3.0/hls/d.xml000066400000000000000000000024401476733534200145470ustar00rootroot00000000000000 \b(alias|align|assert|auto|body|break|case|cast|catch|class|const|continue|default|delegate|delete|do|else|enum|finally|for|foreach|function|if|import|in|is|module|new|out|private|private|public|return|scope|static|struct|super|switch|template|this|throw|try|typedef|typeof|union|version|volatile|while)\b \b(bit|bool||byte|cdouble|cent|cfloat|char|creal|dchar|double|float|ifloat|ifoduble|int|ireal|long|real|short|ubyte|ucent|uint|ulong|ushort|void|wchar)\b [-=\+\*\/\|] [\(\)\[\]] ("[^\"]*") ('[^']*') //[^\n]* /\* \*/ /*%s*/ //%s tea-qt-63.3.0/hls/fortran.xml000066400000000000000000000025561476733534200160070ustar00rootroot00000000000000 \b(.and.|.eq.|.eqv.|.ge.|.gt.|.le.|.lt.|.ne.|.neqv.|.not.|.or.|call|case|contains|continue|cycle|default|do|else|elsewhere|end|enddo|ednforall|endwhere|endif|exit|forall|function|if|implicit|include|interface|module|module|print|program|recursive|return|select|stop|subroutine|then|type|use|where|while|write)\b \b(character|complex|data|double|integer|logical|real)\b \b(parameter|pointer|target|allocatable|dimension|pure|public|private|intent|optional|result|save|external|intrinsic)\b ("[^\"]*") ('[^']*') [-=\+\*\/\|] [\(\)\[\]] ![^\n]* /\* \*/ !%s tea-qt-63.3.0/hls/hs.xml000066400000000000000000000023111476733534200147330ustar00rootroot00000000000000 \b(as|case|class|of|data|family|instance|default|deriving|do|forall|foreign|hiding|if|then|else|import|infix|infixl|infixr|let|in|mdo|module|newtype|not|proc|qualified|rec|type|family|where)\b \b(head|init|tail|last|fst|snd|even)\b \b(bool|char|double|float|int|integer|realfloat)\b [-=\+\*\/\|] [\(\)\[\]] ("[^\"]*") ('[^']*') --[^\n]* {- -} /{-%s-} --%s tea-qt-63.3.0/hls/java.xml000066400000000000000000000027211476733534200152470ustar00rootroot00000000000000 \b(boolean|byte|char|double|float|int|long|short|void)\b \b(abstract|assert|break|byte|case|catch|class|const|continue|default|do|else|enum|extends|false|final|finally|for|goto|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|true|try|volatile|while)\b \b[A-Za-z0-9_]+(?=\() ^#import ^#.* ("[^\"]*") ('[^']*') [-=\+\*\/\|] [\(\)\[\]] //[^\n]* /\* \*/ /*%s*/ //%s tea-qt-63.3.0/hls/lilypond.xml000066400000000000000000000012211476733534200161520ustar00rootroot00000000000000 \\[A-Za-z]+\b ("[^\"]*") ('[^']*') %[^\n]* %\{ %\} %{%s%} %%s tea-qt-63.3.0/hls/lout.xml000066400000000000000000000011371476733534200153110ustar00rootroot00000000000000 @[A-Z,a-z]+ ("[^\"]*") ('[^']*') #[^\n]* /\* \*/ #%s tea-qt-63.3.0/hls/lua.xml000066400000000000000000000024221476733534200151050ustar00rootroot00000000000000 \b(and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b \b(_G|_VERSION|assert|collectgarbage|dofile|error|getenv|getmetatable|ipairs|load|loadfile|loadstring|module|next|pairs|pcall|print|rawequal|rawget|rawset|require|select|setfenv|setmetatable|tonumber|tostring|type|unpack|xpcall)\b ^#.* [-=\+\*\/\|] [\(\)\[\]] ("[^\"]*") ('[^']*') --[^\n]* --\[\[ --\]\] --[[%s--]] --%s tea-qt-63.3.0/hls/nasm.xml000066400000000000000000000326161476733534200152720ustar00rootroot00000000000000 \b(aaa|aad|aam|aas|abb|adc|add|addpd|addps|addsd|addss|addsubpd|addsubps|aepl|aesdec|aesdeclast|aesenc|aesenclast|aesimc|aeskeygenassist|and|andl|andnpd|andnps|andpd|andps|blendpd|blendps|blendvpd|blendvps|bound|bsf|bswap|bt|btc|btr|bts|call|cbw|cdq|cdqe|clc|cld|clflush|clgi|cli|clts|cmc|cmovcc|cmp|cmpeqpd|cmpeqps|cmpeqsd|cmpeqss|cmplepd|cmpleps|cmplesd|cmpless|cmpltpd|cmpltps|cmpltsd|cmpltss|cmpneqpd|cmpneqps|cmpneqsd|cmpneqss|cmpnlepd|cmpnleps|cmpnlesd|cmpnless|cmpnltpd|cmpnltps|cmpnltsd|cmpnltss|cmpordpd|cmpordps|cmpordsd|cmpordss|cmppd|cmpps|cmpsb|cmpsd|cmpsq|cmpss|cmpsw|cmpunordpd|cmpunordps|cmpunordsd|cmpunordss|cmpxchg|cmpxchg16b|cmpxchg8b|comeqpd|comeqps|comeqsd|comeqss|comfalsepd|comfalseps|comfalsesd|comfalsess|comisd|comiss|comlepd|comleps|comlesd|comless|comltpd|comltps|comltsd|comltss|comneqpd|comneqps|comneqsd|comneqss|comnlepd|comnleps|comnlesd|comnless|comnltpd|comnltps|comnltsd|comnltss|comordpd|comordps|comordsd|comordss|compd|comps|comsd|comss|comtruepd|comtrueps|comtruesd|comtruess|comueqpd|comueqps|comueqsd|comueqss|comulepd|comuleps|comulesd|comuless|comultpd|comultps|comultsd|comultss|comuneqpd|comuneqps|comuneqsd|comuneqss|comunlepd|comunleps|comunlesd|comunless|comunltpd|comunltps|comunltsd|comunltss|comunordpd|comunordps|comunordsd|comunordss|cqo|crc32|cvtdq2pd|cvtdq2ps|cvtpd2dq|cvtpd2pi|cvtpd2ps|cvtph2ps|cvtpi2pd|cvtpi2ps|cvtps2dq|cvtps2pd|cvtps2ph|cvtps2pi|cvtsd2si|cvtsd2ss|cvtsi2sd|cvtsi2ss|cvtss2sd|cvtss2si|cvttpd2dq|cvttpd2pi|cvttps2dq|cvttps2pi|cvttsd2si|cvttss2si|cwd|cwde|db|dd|dec|div|divpd|divps|divsd|divss|do|dppd|dpps|dq|dt|dw|dy|emms|endstruc|enter|equ|extractps|extrq|fadd|faddp|fbld|fbstp|fchs|fclex|fcmovb|fcmovbe|fcmove|fcmovnb|fcmovnbe|fcmovne|fcmovnu|fcmovu|fcom|fcomi|fcomip|fcomp|fcompp|fcos|fdecstp|fdisi|fdiv|fdivp|fdivr|fdivrp|femms|feni|ffree|ffreep|fiadd|ficom|ficomp|fidiv|fidivr|fild|fimul|fincstp|finit|fist|fistp|fisttp|fisub|fisubr|fld|fld1|fldcw|fldenv|fldl2e|fldl2t|fldlg2|fldln2|fldpi|fldz|fmaddpd|fmaddps|fmaddsd|fmaddss|fmsubpd|fmsubps|fmsubsd|fmsubss|fmul|fmulp|fnclex|fndisi|fneni|fninit|fnmaddpd|fnmaddps|fnmaddsd|fnmaddss|fnmsubpd|fnmsubps|fnmsubsd|fnmsubss|fnop|fnsave|fnstcw|fnstenv|fnstsw|fpatan|fprem|fprem1|fptan|frczpd|frczps|frczsd|frczss|frndint|frstor|fsave|fscale|fsetpm|fsin|fsincos|fsqrt|fst|fstcw|fstenv|fstp|fstsw|fsub|fsubp|fsubr|fsubrp|ftst|fucom|fucomi|fucomip|fucomp|fucompp|fwait|fxam|fxch|fxrstor|fxsave|getsec|haddpd|haddps|hlt|hsubpd|hsubps|ibts|icebp|idiv|imul|in|inc|incbin|insb|insd|insertps|insertq|insw|int|int01|int03|int1|int3|into|invd|invept|invlpg|invlpga|invvpid|iret|iretd|iretq|iretw|ja|jae|jb|jbe|jcc|jcxz|je|jecxz|jge|jl|jle|jmp|jmpe|jne|jo|jrcxz|jz|lahf|lar|lddqu|ldmxcsr|lds|lea|leave|les|lfence|lfs|lgdt|lgs|lidt|lldt|lmsw|loadall|loadall286|lock|lodsb|lodsd|lodsq|lodsw|loop|loope|loopne|loopnz|loopx|loopz|lsl|lss|ltr|lzcnt|maskmovdqu|maskmovq|maxpd|maxps|maxsd|maxss|mfence|minpd|minps|minsd|minss|monitor|montmul|mov|movapd|movaps|movbe|movd|movddup|movdq2q|movdqa|movdqu|movhlps|movhpd|movhps|movl|movlhps|movlpd|movlps|movmskpd|movmskps|movntdq|movntdqa|movnti|movntpd|movntps|movntq|movntsd|movntss|movq|movq2dq|movs|movsb|movsbl|movsbw|movsd|movshdup|movsldup|movsq|movss|movsw|movswl|movsx|movsxd|movupd|movups|movz|movzx|mpsadbw|mul|mulpd|mulps|mulsd|mulss|mwait|neg|nop|not|notl|or|orl|orpd|orps|out|outsb|outsd|outsw|pabsb|pabsd|pabsw|packssdw|packsswb|packusdw|packuswb|paddb|paddd|paddq|paddsb|paddsiw|paddsw|paddusb|paddusw|paddw|palignr|pand|pandn|pause|paveb|pavgb|pavgusb|pavgw|pblendvb|pblendw|pclmulhqhqdq|pclmulhqlqdq|pclmullqhqdq|pclmullqlqdq|pclmulqdq|pcmov|pcmpeqb|pcmpeqd|pcmpeqq|pcmpeqw|pcmpestri|pcmpestrm|pcmpgtb|pcmpgtd|pcmpgtq|pcmpgtw|pcmpistri|pcmpistrm|pcomb|pcomd|pcomeqb|pcomeqd|pcomeqq|pcomequb|pcomequd|pcomequq|pcomequw|pcomeqw|pcomfalseb|pcomfalsed|pcomfalseq|pcomfalseub|pcomfalseud|pcomfalseuq|pcomfalseuw|pcomfalsew|pcomgeb|pcomged|pcomgeq|pcomgeub|pcomgeud|pcomgeuq|pcomgeuw|pcomgew|pcomgtb|pcomgtd|pcomgtq|pcomgtub|pcomgtud|pcomgtuq|pcomgtuw|pcomgtw|pcomleb|pcomled|pcomleq|pcomleub|pcomleud|pcomleuq|pcomleuw|pcomlew|pcomltb|pcomltd|pcomltq|pcomltub|pcomltud|pcomltuq|pcomltuw|pcomltw|pcomneqb|pcomneqd|pcomneqq|pcomnequb|pcomnequd|pcomnequq|pcomnequw|pcomneqw|pcomq|pcomtrueb|pcomtrued|pcomtrueq|pcomtrueub|pcomtrueud|pcomtrueuq|pcomtrueuw|pcomtruew|pcomub|pcomud|pcomuq|pcomuw|pcomw|pdistib|permpd|permps|pextrb|pextrd|pextrq|pextrw|pf2id|pf2iw|pfacc|pfadd|pfcmpeq|pfcmpge|pfcmpgt|pfmax|pfmin|pfmul|pfnacc|pfpnacc|pfrcp|pfrcpit1|pfrcpit2|pfrcpv|pfrsqit1|pfrsqrt|pfrsqrtv|pfsub|pfsubr|phaddbd|phaddbq|phaddbw|phaddd|phadddq|phaddsw|phaddubd|phaddubq|phaddubw|phaddudq|phadduwd|phadduwq|phaddw|phaddwd|phaddwq|phminposuw|phsubbw|phsubd|phsubdq|phsubsw|phsubw|phsubwd|pi2fd|pi2fw|pinsrb|pinsrd|pinsrq|pinsrw|pmachriw|pmacsdd|pmacsdqh|pmacsdql|pmacssdd|pmacssdqh|pmacssdql|pmacsswd|pmacssww|pmacswd|pmacsww|pmadcsswd|pmadcswd|pmaddubsw|pmaddwd|pmagw|pmaxsb|pmaxsd|pmaxsw|pmaxub|pmaxud|pmaxuw|pminsb|pminsd|pminsw|pminub|pminud|pminuw|pmovmskb|pmovsxbd|pmovsxbq|pmovsxbw|pmovsxdq|pmovsxwd|pmovsxwq|pmovzxbd|pmovzxbq|pmovzxbw|pmovzxdq|pmovzxwd|pmovzxwq|pmuldq|pmulhriw|pmulhrsw|pmulhrwa|pmulhrwc|pmulhuw|pmulhw|pmulld|pmullw|pmuludq|pmvgezb|pmvlzb|pmvnzb|pmvzb|pop|popa|popad|popaw|popcnt|popf|popfd|popfq|popfw|por|pperm|prefetch|prefetchnta|prefetcht0|prefetcht1|prefetcht2|prefetchw|protb|protd|protq|protw|psadbw|pshab|pshad|pshaq|pshaw|pshlb|pshld|pshlq|pshlw|pshufb|pshufd|pshufhw|pshuflw|pshufw|psignb|psignd|psignw|pslld|pslldq|psllq|psllw|psrad|psraw|psrld|psrldq|psrlq|psrlw|psubb|psubd|psubq|psubsb|psubsiw|psubsw|psubusb|psubusw|psubw|pswapd|ptest|punpckhbw|punpckhdq|punpckhqdq|punpckhwd|punpcklbw|punpckldq|punpcklqdq|punpcklwd|push|pusha|pushad|pushaw|pushf|pushfd|pushfq|pushfw|pxor|rcl|rcpps|rcpss|rcr|rdm|rdmsr|rdpmc|rdshr|rdtsc|rdtscp|resb|resd|reso|resq|rest|resw|resy|ret|retf|retn|rol|ror|roundpd|roundps|roundsd|roundss|rsdc|rsldt|rsm|rsqrtps|rsqrtss|rsts|sahf|sal|salc|sar|sbb|scasb|scasd|scasq|scasw|scl|scr|setcc|sfence|sgdt|shl|shld|shr|shrd|shufpd|shufps|sidt|skinit|sldt|smi|smint|smintold|smsw|sqrtpd|sqrtps|sqrtsd|sqrtss|stc|std|stgi|sti|stmxcsr|stosb|stosd|stosq|stosw|str|strict|struc|sub|subpd|subps|subsd|subss|svdc|svldt|svts|swapgs|syscall|sysenter|sysexit|sysret|systenter|test|times|ucomisd|ucomiss|ud0|ud1|ud2|ud2a|ud2b|umov|unpckhpd|unpckhps|unpcklpd|unpcklps|vaddpd|vaddps|vaddsd|vaddss|vaddsubpd|vaddsubps|vaesdec|vaesdeclast|vaesenc|vaesenclast|vaesimc|vaeskeygenassist|vandnpd|vandnps|vandpd|vandps|vblendpd|vblendps|vblendvpd|vblendvps|vbroadcastf128|vbroadcastsd|vbroadcastss|vcmpeq_ospd|vcmpeq_osps|vcmpeq_ossd|vcmpeq_osss|vcmpeq_uqpd|vcmpeq_uqps|vcmpeq_uqsd|vcmpeq_uqss|vcmpeq_uspd|vcmpeq_usps|vcmpeq_ussd|vcmpeq_usss|vcmpeqpd|vcmpeqps|vcmpeqsd|vcmpeqss|vcmpfalse_ospd|vcmpfalse_osps|vcmpfalse_ossd|vcmpfalse_osss|vcmpfalsepd|vcmpfalseps|vcmpfalsesd|vcmpfalsess|vcmpge_oqpd|vcmpge_oqps|vcmpge_oqsd|vcmpge_oqss|vcmpgepd|vcmpgeps|vcmpgesd|vcmpgess|vcmpgt_oqpd|vcmpgt_oqps|vcmpgt_oqsd|vcmpgt_oqss|vcmpgtpd|vcmpgtps|vcmpgtsd|vcmpgtss|vcmple_oqpd|vcmple_oqps|vcmple_oqsd|vcmple_oqss|vcmplepd|vcmpleps|vcmplesd|vcmpless|vcmplt_oqpd|vcmplt_oqps|vcmplt_oqsd|vcmplt_oqss|vcmpltpd|vcmpltps|vcmpltsd|vcmpltss|vcmpneq_oqpd|vcmpneq_oqps|vcmpneq_oqsd|vcmpneq_oqss|vcmpneq_ospd|vcmpneq_osps|vcmpneq_ossd|vcmpneq_osss|vcmpneq_uspd|vcmpneq_usps|vcmpneq_ussd|vcmpneq_usss|vcmpneqpd|vcmpneqps|vcmpneqsd|vcmpneqss|vcmpnge_uqpd|vcmpnge_uqps|vcmpnge_uqsd|vcmpnge_uqss|vcmpngepd|vcmpngeps|vcmpngesd|vcmpngess|vcmpngt_uqpd|vcmpngt_uqps|vcmpngt_uqsd|vcmpngt_uqss|vcmpngtpd|vcmpngtps|vcmpngtsd|vcmpngtss|vcmpnle_uqpd|vcmpnle_uqps|vcmpnle_uqsd|vcmpnle_uqss|vcmpnlepd|vcmpnleps|vcmpnlesd|vcmpnless|vcmpnlt_uqpd|vcmpnlt_uqps|vcmpnlt_uqsd|vcmpnlt_uqss|vcmpnltpd|vcmpnltps|vcmpnltsd|vcmpnltss|vcmpord_spd|vcmpord_sps|vcmpord_ssd|vcmpord_sss|vcmpordpd|vcmpordps|vcmpordsd|vcmpordss|vcmppd|vcmpps|vcmpsd|vcmpss|vcmptrue_uspd|vcmptrue_usps|vcmptrue_ussd|vcmptrue_usss|vcmptruepd|vcmptrueps|vcmptruesd|vcmptruess|vcmpunord_spd|vcmpunord_sps|vcmpunord_ssd|vcmpunord_sss|vcmpunordpd|vcmpunordps|vcmpunordsd|vcmpunordss|vcomisd|vcomiss|vcvtdq2pd|vcvtdq2ps|vcvtpd2dq|vcvtpd2ps|vcvtph2ps|vcvtps2dq|vcvtps2pd|vcvtps2ph|vcvtsd2si|vcvtsd2ss|vcvtsi2sd|vcvtsi2ss|vcvtss2sd|vcvtss2si|vcvttpd2dq|vcvttps2dq|vcvttsd2si|vcvttss2si|vdivpd|vdivps|vdivsd|vdivss|vdppd|vdpps|verr|verw|vextractf128|vextractps|vfmadd123pd|vfmadd123ps|vfmadd123sd|vfmadd123ss|vfmadd132pd|vfmadd132ps|vfmadd132sd|vfmadd132ss|vfmadd213pd|vfmadd213ps|vfmadd213sd|vfmadd213ss|vfmadd231pd|vfmadd231ps|vfmadd231sd|vfmadd231ss|vfmadd312pd|vfmadd312ps|vfmadd312sd|vfmadd312ss|vfmadd321pd|vfmadd321ps|vfmadd321sd|vfmadd321ss|vfmaddpd|vfmaddps|vfmaddsd|vfmaddss|vfmaddsub123pd|vfmaddsub123ps|vfmaddsub132pd|vfmaddsub132ps|vfmaddsub213pd|vfmaddsub213ps|vfmaddsub231pd|vfmaddsub231ps|vfmaddsub312pd|vfmaddsub312ps|vfmaddsub321pd|vfmaddsub321ps|vfmaddsubpd|vfmaddsubps|vfmsub123pd|vfmsub123ps|vfmsub123sd|vfmsub123ss|vfmsub132pd|vfmsub132ps|vfmsub132sd|vfmsub132ss|vfmsub213pd|vfmsub213ps|vfmsub213sd|vfmsub213ss|vfmsub231pd|vfmsub231ps|vfmsub231sd|vfmsub231ss|vfmsub312pd|vfmsub312ps|vfmsub312sd|vfmsub312ss|vfmsub321pd|vfmsub321ps|vfmsub321sd|vfmsub321ss|vfmsubadd123pd|vfmsubadd123ps|vfmsubadd132pd|vfmsubadd132ps|vfmsubadd213pd|vfmsubadd213ps|vfmsubadd231pd|vfmsubadd231ps|vfmsubadd312pd|vfmsubadd312ps|vfmsubadd321pd|vfmsubadd321ps|vfmsubaddpd|vfmsubaddps|vfmsubpd|vfmsubps|vfmsubsd|vfmsubss|vfnmadd123pd|vfnmadd123ps|vfnmadd123sd|vfnmadd123ss|vfnmadd132pd|vfnmadd132ps|vfnmadd132sd|vfnmadd132ss|vfnmadd213pd|vfnmadd213ps|vfnmadd213sd|vfnmadd213ss|vfnmadd231pd|vfnmadd231ps|vfnmadd231sd|vfnmadd231ss|vfnmadd312pd|vfnmadd312ps|vfnmadd312sd|vfnmadd312ss|vfnmadd321pd|vfnmadd321ps|vfnmadd321sd|vfnmadd321ss|vfnmaddpd|vfnmaddps|vfnmaddsd|vfnmaddss|vfnmsub123pd|vfnmsub123ps|vfnmsub123sd|vfnmsub123ss|vfnmsub132pd|vfnmsub132ps|vfnmsub132sd|vfnmsub132ss|vfnmsub213pd|vfnmsub213ps|vfnmsub213sd|vfnmsub213ss|vfnmsub231pd|vfnmsub231ps|vfnmsub231sd|vfnmsub231ss|vfnmsub312pd|vfnmsub312ps|vfnmsub312sd|vfnmsub312ss|vfnmsub321pd|vfnmsub321ps|vfnmsub321sd|vfnmsub321ss|vfnmsubpd|vfnmsubps|vfnmsubsd|vfnmsubss|vfrczpd|vfrczps|vfrczsd|vfrczss|vhaddpd|vhaddps|vhsubpd|vhsubps|vinsertf128|vinsertps|vlddqu|vldmxcsr|vldqqu|vmaskmovdqu|vmaskmovpd|vmaskmovps|vmaxpd|vmaxps|vmaxsd|vmaxss|vmcall|vmclear|vminpd|vminps|vminsd|vminss|vmlaunch|vmload|vmmcall|vmovapd|vmovaps|vmovd|vmovddup|vmovdqa|vmovdqu|vmovhlps|vmovhpd|vmovhps|vmovlhps|vmovlpd|vmovlps|vmovmskpd|vmovmskps|vmovntdq|vmovntdqa|vmovntpd|vmovntps|vmovntqq|vmovq|vmovqqa|vmovqqu|vmovsd|vmovshdup|vmovsldup|vmovss|vmovupd|vmovups|vmpsadbw|vmptrld|vmptrst|vmread|vmresume|vmrun|vmsave|vmulpd|vmulps|vmulsd|vmulss|vmwrite|vmxoff|vmxon|vorpd|vorps|vpabsb|vpabsd|vpabsw|vpackssdw|vpacksswb|vpackusdw|vpackuswb|vpaddb|vpaddd|vpaddq|vpaddsb|vpaddsw|vpaddusb|vpaddusw|vpaddw|vpalignr|vpand|vpandn|vpavgb|vpavgw|vpblendvb|vpblendw|vpclmulhqhqdq|vpclmulhqlqdq|vpclmullqhqdq|vpclmullqlqdq|vpclmulqdq|vpcmov|vpcmpeqb|vpcmpeqd|vpcmpeqq|vpcmpeqw|vpcmpestri|vpcmpestrm|vpcmpgtb|vpcmpgtd|vpcmpgtq|vpcmpgtw|vpcmpistri|vpcmpistrm|vpcomb|vpcomd|vpcomq|vpcomub|vpcomud|vpcomuq|vpcomuw|vpcomw|vperm2f128|vpermil2pd|vpermil2ps|vpermilmo2pd|vpermilmo2ps|vpermilmz2pd|vpermilmz2ps|vpermilpd|vpermilps|vpermiltd2pd|vpermiltd2ps|vpextrb|vpextrd|vpextrq|vpextrw|vphaddbd|vphaddbq|vphaddbw|vphaddd|vphadddq|vphaddsw|vphaddubd|vphaddubq|vphaddubwd|vphaddudq|vphadduwd|vphadduwq|vphaddw|vphaddwd|vphaddwq|vphminposuw|vphsubbw|vphsubd|vphsubdq|vphsubsw|vphsubw|vphsubwd|vpinsrb|vpinsrd|vpinsrq|vpinsrw|vpmacsdd|vpmacsdqh|vpmacsdql|vpmacssdd|vpmacssdqh|vpmacssdql|vpmacsswd|vpmacssww|vpmacswd|vpmacsww|vpmadcsswd|vpmadcswd|vpmaddubsw|vpmaddwd|vpmaxsb|vpmaxsd|vpmaxsw|vpmaxub|vpmaxud|vpmaxuw|vpminsb|vpminsd|vpminsw|vpminub|vpminud|vpminuw|vpmovmskb|vpmovsxbd|vpmovsxbq|vpmovsxbw|vpmovsxdq|vpmovsxwd|vpmovsxwq|vpmovzxbd|vpmovzxbq|vpmovzxbw|vpmovzxdq|vpmovzxwd|vpmovzxwq|vpmuldq|vpmulhrsw|vpmulhuw|vpmulhw|vpmulld|vpmullw|vpmuludq|vpor|vpperm|vprotb|vprotd|vprotq|vprotw|vpsadbw|vpshab|vpshad|vpshaq|vpshaw|vpshlb|vpshld|vpshlq|vpshlw|vpshufb|vpshufd|vpshufhw|vpshuflw|vpsignb|vpsignd|vpsignw|vpslld|vpslldq|vpsllq|vpsllw|vpsrad|vpsraw|vpsrld|vpsrldq|vpsrlq|vpsrlw|vpsubb|vpsubd|vpsubq|vpsubsb|vpsubsw|vpsubusb|vpsubusw|vpsubw|vptest|vpunpckhbw|vpunpckhdq|vpunpckhqdq|vpunpckhwd|vpunpcklbw|vpunpckldq|vpunpcklqdq|vpunpcklwd|vpxor|vrcpps|vrcpss|vroundpd|vroundps|vroundsd|vroundss|vrsqrtps|vrsqrtss|vshufpd|vshufps|vsqrtpd|vsqrtps|vsqrtsd|vsqrtss|vstmxcsr|vsubpd|vsubps|vsubsd|vsubss|vtestpd|vtestps|vucomisd|vucomiss|vunpckhpd|vunpckhps|vunpcklpd|vunpcklps|vxorpd|vxorps|vzeroall|vzeroupper|wait|wbinvd|wrmsr|wrshr|xadd|xbts|xchg|xchgb|xchgl|xchgw|xcryptcbc|xcryptcfb|xcryptctr|xcryptecb|xcryptofb|xgetbv|xlat|xlatb|xor|xorl|xorpd|xorps|xrstor|xsave|xsetbv|xsha1|xsha256|xstore|segment)\b %[A-Za-z]*\b ^[a-zA-Z_][a-zA-Z0-9_]*\: ^#include ("[^\"]*") ('[^']*') ;[^\n]* /\* \*/ #%s tea-qt-63.3.0/hls/nsis.xml000066400000000000000000000032731476733534200153050ustar00rootroot00000000000000 \b(abort|break|call|case|crccheck|createdirectory|default|do|else|endif|endswitch|endwhile|file|for|function|functionend|goto|if|installdir|insttype|loopuntil|name|name|next|outfile|outfile|page|pageex|pageexend|requestexecutionlevel|requestexecutionlevel|section|sectionend|sectionend|sectiongroup|sectionin|setcompressor|setoutpath|setshellvarcontext|showinstdetails|showuninstdetails|switch|var|while|writeregstr|writeuninstaller)\b \b(addbrandingimage|allowrootdirinstall|autoclosewindow|bgfont|bggradient|brandingtext|caption|changeui|checkbitmap|completedtext|componenttext|crccheck|detailsbuttontext|dirtext|dirvar|dirverify|fileerrortext|icon|installbuttontext|installcolors|installdir|installdirregkey)\b \b(createshortcut|delete|deleteregkey|detailprint|dialog|findwindow|getdlgitem|intcmp|intop|messagebox|readregstr|rmdir|sectiongettext|sendmessage|setctlcolors|strcpy)\b ("[^\"]*") ('[^']*') ^\!.* \;[^\n]* /\* \*/ ;%s tea-qt-63.3.0/hls/pascal.xml000066400000000000000000000043051476733534200155710ustar00rootroot00000000000000 [-=\+\*\/\|] [\(\)\[\]] \b(absolute|and|array|as|asm|begin|break|case|class|const|constructor|continue|destructor|dispose|div|do|downto|else|end|except|exit|exports|false|file|finalization|finally|for|function|goto|if|implementation|in|inherited|initialization|inline|interface|is|label|library|mod|new|nil|not|object|of|on|on|operator|or|out|packed|procedure|program|property|raise|record|reintroduce|repeat|self|set|shl|shr|string|then|threadvar|to|true|try|type|unit|until|uses|var|while|with|xor)\b \b(abstract|alias|assembler|cdecl|cppdecl|default|export|external|far|far16|forward|index|local|name|near|nostackframe|oldfpccall|override|pascal|private|protected|public|published|read|register|safecall|softfloat|stdcall|virtual|write)\b \b(ansistring|boolean|byte|bytebool|cardinal|char|comp|currency|double|extended|int64|integer|longbool|longint|longword|pchar|qword|real|shortint|single|smallint|string|widechar|word)\b \bT[A-Za-z0-9_]+\b \bP[A-Za-z0-9_]+\b \bR[A-Za-z0-9_]+\b ^[a-zA-Z_][a-zA-Z0-9_]*\: ^#uses ('[^']*') ('[^']*') //[^\n]* \{ \} {%s} //%s tea-qt-63.3.0/hls/perl.xml000066400000000000000000000017031476733534200152670ustar00rootroot00000000000000 \b(\bmy|chdir|chomp|close|closedir|continue|defined|die|do|elsif|eof|eval|exists|foreach|getpwnam|given|glob|goto|grep|if|import|join|last|last|length|local|next|open|pop|print|printf|push|readline|redo|require|reset|return|say|scalar|shift|split|split|stat|sub|syscall|system|undef|unimport|unless|until|use|wait|waitpid|wantarray|warn|when|while)\b ("[^\"]*") ('[^']*') #[^\n]* #%s =begin* *=end tea-qt-63.3.0/hls/php.xml000066400000000000000000000024531476733534200151170ustar00rootroot00000000000000 \b(\b(and|abstract|array|as|break|case|catch|cfunction|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|false|final|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|interface|isset|list|namespace|new|old_function|or|or|parent|print|private|protected|public|require|require_once|return|self|static|switch|throw|true|try|unset|use|var|while|xor)\b \$[A-Za-z_][0-9A-Za-z_]*\b [-=\+\*\/\|] [\(\)\[\]] ('[^']*') ("[^\"]*") //.*$ //%s /\* \*/ tea-qt-63.3.0/hls/po.xml000066400000000000000000000010551476733534200147430ustar00rootroot00000000000000 \b(msgid|msgstr)\b ("[^\"]*") ^#.* #%s /\* \*/ tea-qt-63.3.0/hls/python.xml000066400000000000000000000030231476733534200156430ustar00rootroot00000000000000 \b(and|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|yield)\b \b(__import__|abs|all|any|apply|basestring|bin|bool|buffer|bytearray|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b [-=\+\*\/\|] [\(\)\[\]] ("[^\"]*"|'[^']*') ('[^']*') #[^\n]* """ """ """%s\n""" #/%s tea-qt-63.3.0/hls/r.xml000066400000000000000000000027671476733534200146010ustar00rootroot00000000000000 \b(FALSE|Inf|NA|NA_character_|NA_complex_|NA_integer_|NA_real_|NULL|NaN|NextMethod|TRUE|UseMethod|apply|break|else|for|for|function|if|in|lapply|next|repeat|switch|tapply|while)\b \b(array|complex|matrix|library)\b \b(logical|integer|real|complex|character|raw)\b \bC[A-Za-z]+\b [-=\+\*\/\|] [\(\)\[\]] ^#include ^[a-zA-Z_][a-zA-Z0-9_]*\:$ ^#.* ("[^\"]*") ('[^']*') #.* /\* \*/ /*%s*/ #%s tea-qt-63.3.0/hls/rust.xml000066400000000000000000000027341476733534200153270ustar00rootroot00000000000000 \b(as|break|const|continue|crate|else|enum|extern|false|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|Self|self|static|struct|super|trait|true|type|unsafe|use|where|while|async|await|dyn|abstract|become|box|final|macro|override|priv|typeof|unsized|virtual|yield|try|union)\b \b(bool|char|i8|i16|i32|i64|isize|u8|u16|u32|u64|usize|f32|f64|array|slice|str|tuple)\b [-=\+\*\/\|] [\(\)\[\]] ^#include ^'[a-zA-Z_][a-zA-Z0-9_]*\:$ ^#.* ("[^\"]*") ('[^']*') //[^\n]* /\* \*/ /*%s*/ //%s tea-qt-63.3.0/hls/seed7.xml000066400000000000000000000023031476733534200153310ustar00rootroot00000000000000 \b(action|and|array|attr|begin|case|category|const|conv|cross|digits|div|do|downto|else|elsif|end|for|found|hash|if|in|inout|is|local|loop|lpad|lpad0|mdiv|mod|not|of|otherwise|parse|range|ref|rem|repeat|result|rpad|search|set|struct|system|then|to|until|var|when|while)\b \b(biginteger|bigrational|boolean|char|color|complex|duration|expr|file|float|func|integer|object|proc|rational|string|text|time|void)\b .*include.*" ("[^\"]*") ('[^']*') #[^\n]* \(\* \*\) (* %s *) #%s tea-qt-63.3.0/hls/subtitles.xml000066400000000000000000000003051476733534200163400ustar00rootroot00000000000000 \b\d{1,}:\d{1,}:\d{1,}(\,|\.)\d{1,}\b tea-qt-63.3.0/hls/tex.xml000066400000000000000000000011561476733534200151270ustar00rootroot00000000000000 \\(?:[\w@]+|.) (``[^\``]*'') (`[^`]*') %.* %%s /\* \*/ tea-qt-63.3.0/hls/vala.xml000066400000000000000000000031111476733534200152430ustar00rootroot00000000000000 [-=\+\*\/\|] [\(\)\[\]] \b(break|case|catch|continue|default|do|else|false|finally|for|foreach|if|in|lock|null|return|switch|throw|true|try|using|while|yield)\b \b(abstract|base|class|const|construct|delegate|dynamic|ensures|enum|errordomain|extern|get|interface|namespace|out|override|private|protected|public|ref|requires|set|signal|static|struct|throws|unowned|value|var|virtual|weak|yields)\b \b(bool|char|double|float|int|int16|int32|int64|int8|long|short|size_t|ssize_t|string|uchar|uint|uint16|uint32|uint64|uint8|ulong|unichar|ushort|void)\b ^#include ^#.* ("[^\"]*") ('[^']*') //[^\n]* /\* \*/ /*%s*/ //%s tea-qt-63.3.0/hls/verilog.xml000066400000000000000000000044251476733534200160000ustar00rootroot00000000000000 \b(\cleartrace|\error|\listb;log|always|always_comb|always_ff|always_latch|and|assert|assign|begin|bitstoread|break|buf|bufif0|bufif1|case|casex|casez|cast|class|cmos|constraint|continue|countdrivers|covergroup|coverpoint|deassign|default|defparam|disable|display|do|edge|else|end|endcase|endclass|endfunction|endgroup|endinterface|endmodule|endprimitive|endprogram|endproperty|endsequence|endspecify|endtable|endtask|enum|event|exit|export|fclose|fdisplay|final|finish|fmonitor|fopen|for|force|foreach|forever|fork|fstrobe|function|fwrite|getpattern|highz0|highz1|history|if|ifnone|import|incsave|initial|inout|input|input|interface|itor|join|key|large|macromodule|medium|module|monitor|nand|negedge|new|nmos|nokey|nor|not|notif0|notif1|or|output|packed|parameter|pmos|posedge|primitive|priority|program|property|pull0|pull1|pulldown|pullup|rcmos|readmemb|readmemh|real|realtime|reg|release|repeat|req|return|rnmos|rpmos|rtran|rtranif0|rtranif1|scalared|scope|sequence|settrace|showscopes|showvars|small|specify|specparam|stop|strength|strong0|strong1|struct|struct|supply0|supply1|table|task|time|time|tran|tranif0|tranif1|tri|tri0|tri1|triand|trior|trireg|typedef|unique|vectored|virtual|wait|wand|weak0|weak1|while|wire|wor|xnor|xor)\b \b(bit|byte|chandle|int|integer|logic|logint|rand|randc|real|reg|shortint|shortreal|signed|string|time|unsigned|void)\b \bQ[A-Za-z]+\b ^`include ^`.* ("[^\"]*") ('[^']*') //[^\n]* /\* \*/ /*%s*/ //%s tea-qt-63.3.0/hls/wikitext.xml000066400000000000000000000015371476733534200162020ustar00rootroot00000000000000 (<[a-zA-Z\d:]+\b|<\?[a-zA-Z\d:]+\b|\?>|>|/>|</[a-zA-Z\d:]+>) [a-zA-Z:]+= &[a-zA-Z_][a-zA-Z0-9.:_-]*; \[|\] ("[^\"]*"|'[^']*') <!-- --> <!-- %s --> <!-- %s --> tea-qt-63.3.0/hls/xml.xml000066400000000000000000000011551476733534200151260ustar00rootroot00000000000000 <(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+> ("[^\"]*"|'[^']*') <!-- --> <!-- %s --> <!-- %s --> tea-qt-63.3.0/icons/000077500000000000000000000000001476733534200141275ustar00rootroot00000000000000tea-qt-63.3.0/icons/128/000077500000000000000000000000001476733534200144415ustar00rootroot00000000000000tea-qt-63.3.0/icons/128/tea.png000066400000000000000000000223051476733534200157220ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwx?3M"$vlɲ*qӳ'ٻlr&Onf78.68t;cqؖlI#%R)61,$~9w*hƹ,@9ɹD@gOaF~Yb~sM$+OL>١8Uŕ|UPP"Ӟ1C U;UkG;2& v;P 4!a >$M ,291zʇț:r="' [-:XNC|9 k;{RR-]tELS/ӞQz[.y3ﰓv̀SB~Y>|p[:@Ip#z/;UK%G7,?H|h@XîtC`3"C{/";J7TQqO|~-kF~Yr17^ESળnWEvֵϚ_.5sGh輜ẴtŒ{/2X=|8 ɿHw?N~Yz+@[މSHk0&hNY ǫșaÜҊ-_7-$Ϥi%,}gs'ֶɟ(O۽a"kCb2kز/#ImN6iG5Wtn!$wi!,93+]ϖzR~ߥ0׍\QG$qzS۵sz#Լ`'t7$pr[ x#]%rhI{6:dfuO04 $T/,0 ~F$Gv_J_Qf]FoL~ǥQN߉CuP2|.Ky4 [L*3#htQsoB=Gώ?w_Y u/(#ٔ5P1@ΌoU}^NrI3+V:Xk-L?YzӚ &(|@žlTUg]St|E54^VW:06'kz~YضێނC]-]%!^k+_;xVXlF'[AO$X6UēcE}mZn_3hI6,zQm^Owpqq8`!vWk\W(eԢ0onx@ž@i?6cw pYWk|ҁZ_f4oOrl \Cx*i,ƶUl^SSnG>BFe>Z ۆEd eJeʞfUxx "czG$8R s_e)ij*m":NT1JGqH䝥#)8*ld_TE/6f ScErLC d-`aCiDV`)uK96Ŷ(3:QÃFA3`W`g5&N)tl䗥dOJb.Ҋl+$H)[`Whs,Jfl>Qm- 1akN4u\E0ko$ ݿpICƒon}ضܩb QW2gD6dZn3pOw?^?εO276獕L<_ŃwаݶD|kl&~Bߺq8>cZQ1Hx>,%̇)T|&3W;UdhK>WGJsE2\Qlw|%C> `ۛ5R掫3끏%:'1/K_i~2S9͍O3f2:_7:YAA QbV9iRuɒR4FLe3g3A1'KV(̐xҊWT9%T+1 ֝ܺd'R~_ol|Eu?wZ{PD۝J\ [|7y/@JFT lp*qQGo>_7>3z&u]y}q =ޓHn 754ڪ_IJ.Q /OUS,dlʬP>iA6W,;j9>6_w*nzbڀ$[S[˒_f. g:+FѨ%kYP(r 埰ۙ'uS!^}~+űi*/L ,3s:&ꏠ9h8qNn8C~3oI?vE\qn[Y4nE=3㣦Bʇի pCUvgƒ*l@-L>cFƞ1FTzg{j ({Gb0UWc 5Zc YPV1B*.]qW⁴'܈jPGdɛ*Uiv11Mu*n&˳i5>jP/?LSv%;DuroD e_/r}ᙓ lhb~?#ӐʽQfmdrҩQ= ۅ̧\3O웾6x#¾G-;\j}˵K0?\vF NxWȔƽ+T.6y%u.&[l c{5G+uyE œZT>\M.>"Lč"e6K@QuR8ڮk-ÓҼ5 <'̮{5~Ye*xGjUx}< /lC[}k66nGj+m11I#؜#<5A@Ut<"1fD2q eAhԱ(k}vbW.Ǣa۩d˟فsjsor8,@Sr\QoF'FyqDXwWeQIKrfh,+)@b.GO#omnCCipCt4;Q WM&G~_MҼ l 8zzQ^Gibع{{Zod;(us:9[>ayBH ]`;n؜ԙl #i(~Y%Dq~93>]e{j5~ܭ#QT 55҃XN?Iq*EK@U|#C،k8WGi!+r|'UlB#?Uȑ $6 M} Y2gLN^f{L8&U}֌E`1Hֲ~3gsi\b]P(<Ч~ӯ͝< MeM qԋ9It O8Ǝ+XwmIE}H_)l:vYx{m MdL^v.zgZn4Zw$qŁ L}8?'̣$C06hƴZM&M5n2M8ڌ׆R-#I]ץ\qn6OYRUuFgkDNelHqԏ+fc\}M}XuԤ>7Wgrp*vdG4']  3 '58bԐ]*(0}{f]&(L{F m%,zspwZE5=Y16ԣrcfwpE(4n UϞY\5V]̜z-UJgV,!t"}3zT(C&;UmS(cRD/PxP"jJf^5H`8졅mE6fΞ)4t66 =M2LE顳3c;# p#5Dt#O̴l|cҨW)0RL:1s4Mf c0Q;g a,qeۋ_#F0RD L@8@$A:U=p$ِsAӷ7MwKHxg\e$|DtٌUpj9ʼn9\G>D>Pij0)jġוaŶ0.Ǎ/N*ڲŞqCmФ3o Qxh@-mu]B`0l%gX/ib)N'CTBqG xeLs: j@XwlllM%rϺ͂}x7VIJ =P'&xV8f;gCI2'ƨ^Kۇ;َgjfdr?+Ms\ j`A~ J_%߻~ݯت;F̪<*uɜ9[INsPqL?8!1'z+fC:{:VH|cg0h4:T'Wj_Qs/k bW@ۀ/p,EufCQ#0\wA?✵cn?-n9$55'.5xTŐ+#Jpj H;:2Mk`E==EotD-vHC@N)7l #b[Kom+n#K4[8nT4S9؜{ u mj^wP~jg*d-?xxPW90%x( ɖ'C>@)w-mo1p"x.3'+z$oݴUc+v&3c&GɈ:P3(;j{]ƕ+£X 핯H<Gp [I9jYOQR{RtƔ4r4ƴg8Rix/ ldakıh \EpM'= ?RNs!϶F90a쨨:|Z)F$x,Ec\or ]&c9/%:'a`,] DL҆YRR 1?[ZDvL4H5B}geH7ŭ ]Ed/|G$,Sqq;Si(Vv pc;J+*.Z5Uh6b*qhf8svP2mP܀j-'.Dam|N ;] PzʇTO&q%UIR&E~Y{L UTۄ V0vaG`G\ .-**{5^W8,{'Yi^˅s0r@DoU;|zޢWͩ K`{廠 (4^88tu]fCC{*+ ɯ9̀ `~bKd:J ]hx6f<@.Ƣj!o]v9 WE,;Ŷ ײe]':tɀ~5(p*. Q1԰Pm-1嵦ó> %?e_Cl[]%{/ZRk  uٖj_)<Gj(S0^v>f;fmKIf2ucۛHisP(y urO2b6ck&!UH6p6p63>JFk.!fOCUO-z+"Xv# v#&#;;Z#A&̻؝VL~Y|A#KP6IDAT$.e|xG@뿗_n~ 3H73cMave:O$4bZ/K; Quyi0:0`V/Keˆh2mgr\aSInVRn>WC]%k4q:b3$9i'y $nNm\}Zxkt0oS[ kF~Yj~ X>yA߂:OJhheK9$ʕ*)%L~QuP9DM߅iW|* *}TK$р$a֞ᗥ;mvM|IU3? =G(ߗ({5N/KB~J*) nA81=@_q }\բ˻Mbg8%7>@\g4F|kֿd01EoQzZʶos)yN)N _灄$w8* +WڕFO+1ʨ`nnsEFW# ¹LV0, ;I9z^|M* { e U2e*sa= [™1V@ӗec8- ~Y -p',T25S;SqT\8 DGX+#Ӟ1=+)#jmiO LٍN o֖/ ؃> HqtC˒؁N]h-Ϣv,~)`Ɯri'pЈH!^^o?gă_Y$";ˉ߉^?yc,z{lzճ @V*-]IENDB`tea-qt-63.3.0/icons/32/000077500000000000000000000000001476733534200143535ustar00rootroot00000000000000tea-qt-63.3.0/icons/32/tea.png000066400000000000000000000037631476733534200156430ustar00rootroot00000000000000PNG  IHDR szzbKGD@I pHYs B(xtIME "jnUIDATXåilW{7=xf<׮ 84 qBA(M+VhTQR);" jՒj"JNuﻗhws#FZ8M`bA/<KD#4@8XZڏZgkU| L Ue,q&d]CJ[?MDXUi? 5&<AK#tdF ׾G2t1D4~t%X]r}Gk$ y HIX,t˫,yS'b-8:V=رP #I?o+Ը\.sڢgFnWk_~㙭g;y`$<ݪh zxfꈌwiSV׾Dž$jg\9Ʃm;izsQpU͝x%R~SWykHC.vm4Bsjwzn-l?̑-#Y-Oio||:t}# U LkK*K*iqZ8E+\+! ^ p1-+zdΟDi aHҞb;Y$/+zwr)܇mCyse;ȶ2onAc m%]2<9␲!qkyolyL_6Y"S- s o"M[jMޕ&".L[F;LF8Z28prS03$WJ K 5Gx,ĐMh8~Sft)"|iߞҽ7<#xop8'y͡d凴70g R. 62S]!Ac_J5|1͗*\ I2@[p[H? Y]JG  tKOdz;ía SOma2Z8;f{>s˼6q9  {.tqx2Rɂ Uh,MeaS\o ](6h"%rLτ1T%J[&ಽW/d/şgETΠ"r5xr>0t.%I hAڵT`R70f*"n9|ԩXxYM\ȁقrsuьp4*X Y2RuMDQ<90^z{#+TqĖ#>OFrGHVym{1BH˃p_&x&`vi7 ÑjDJ[$6i[Fik30RԖUVb}J1@`0:04 Z&hGbmIENDB`tea-qt-63.3.0/icons/48/000077500000000000000000000000001476733534200143625ustar00rootroot00000000000000tea-qt-63.3.0/icons/48/tea.png000066400000000000000000000072041476733534200156440ustar00rootroot00000000000000PNG  IHDR00WbKGD@I pHYs B(xtIME  &O-IDATh޵i\UvW{uWn xP`@ DF(fE2b*1 h$a!2`@M݋M}kyݛ6v/6HO*ջ9=  =ݤӿ3]@P X"0WK%\A{w7h6w!RGig(s}K fZQ:J$_PB}C#zV^eUE:[hPB  F F<\llAfC`*c J$}ސ*-@#iBn*hMj( ,'y wDRSáREtx 17c]pGdCMu4fw?mSj d nyG>k]aZ% K1hͮiݲb5xT"J Uߨ{!D6u~h2`i`?`lƊB'6*U21rT[X[iz#Htɕ=pA0'z[W(X .r5#8v+_m hxdu|mS;݃UJ(|W#y1+H uKe/Sh:k*/F;˗?Fc Q<vRKU|Wy+k0KQv'XW{ #u˩g ~suf|"|olK yCڅoXp})o| -ު bzDt=gXF)ݴNvlÃM~[ryzqQaC@Պo4<U#\_N~oB|;`$ܻNQmKU\-Qady>fC 2C]QEdYQaoL7Ѣ*/D1f4nSS@lxm{aOiZpMZ'Q@ x{!j Y`aaY;Ksyâ#Tjsfov?A:2=H%ӲuZgl0o|,ֱ5ij5}|EENK81ۘT`-1 Dm/*ኘ6!AGR|gPyhf4ON|%btw.kiT"LAn"2]ͦM^03QJ,G>`wÝ6%8@k@Jbg=$l qK#Uqa[6)g)kY(h_¸cR61~9ʵ{ g7,>v9 '&<.h6٦>cה?-){Ĭr T”n4,jV/C.7$d_/am-#{}N> 7T)U)vW*Z"5;#k8jKZ+BʹLQl,:CS;H5T*W`:mm~E(;P &._OzHkvh IrصdóJgJU✾hTe 糚]^H1v%^R, Fz&k!F&nX>bєb>aOlC3Z dM8~SRC&5S= 1Nx[[Λb!hm R'Fwba(J[.ʣnƙ<5]b 3<;RT摽4l:݊2֩΅B$Iz>HO JSZǨC+ǯl8g_ *OF\b+h}vuUFXssjӕ)LTKnı,|<0Po\9?! |󕣈gSd9Z;♦ˏތUZ<8 ,qG[keF+Kbћծ}nz>@*Sd?櫈V~7Ć.f"㼶%eNb:t9s b+'FݩDmx#_vaéտ'4Az!`i+*,r1zwe(4g6a-ˏYi$E2P<#s׫CRiϑ)1k$L1k(rLď3^G68gPiFl'~7۳qthfID]F{Lc`W0%(dϵJ}9Nx>K,C^bzЈ;R^U8~SV[b;bfpZ;1ݕP v~OiT"sҬut`1L˺wt( |YY#FJ%F  v/u;_Ofl[3vϟ rgopbVR>;:0w-'TB}f+ j~XϋH%|z6p …G8T9>;`;,F@ 8J$K{C<uNBJN]5)cs*i8%>f5'ȓD\ _1[(B%S=|)V?G>!L1XQ/ϥ rA *ً Ϻn1$y2Dx8+7IENDB`tea-qt-63.3.0/icons/64/000077500000000000000000000000001476733534200143605ustar00rootroot00000000000000tea-qt-63.3.0/icons/64/tea.png000066400000000000000000000113151476733534200156400ustar00rootroot00000000000000PNG  IHDR@@iqbKGD@I pHYs B(xtIME 7ZIDATx՛ip\ՕzZnٖ,K,jab@6*J C*He*dj" 0T%as^l#K[%Y~;^?#˛ܪ.u^{s+x47 'X4hląpKyFO^Qj-4J8"2YHdP*0c8F5x c~R2Z? x466ݺ.L<Zhj)OH"P(Ba@hIk8G.jÕ6ROzW=0o_p ‘?9}Kq^;VHoA:ʚ.jPX)`wMB\KD)t.`I-IYS{R/ƣ5sPd%Yr+%(ftF߳$Mu[pطD  T+ztƕvƂQxy CYL Epx4x!PA y#Yɼvλhq Mu/!`m;vVƮc8rGc/ʭf)J 2޿3d%s3)^BHYk2?$p^p̱A\= gՍ 5A-ezsu[88wa͗<\eJM6PT/;Jh%P&ncV>ƞ?'/V@Znm3;U[*i4BKl Vk4~y trꎲs *{J-9RDs_}%Gc[2ri G2C?+䘠4޽2ja2aL6M^[DF;J]:7W767tv H灲P*;2X\e._rLBC@G]0΄ovI!HZlYAZ*mK4k Q ź2yCY4սpM.| ׄI)]:5,%ق'Y`W0w9QV ;כHeRŘ ‚ f:Z0CItK8ΫV`@R3J:v7Z˞fb]dzJTAV$y w p;9TrCģ v m6W5Am{u& +~ݭhOhR#(H_d8[w]Ki]($ I |67§ >R*vO*y禱}6fa:fW j<oTwdIYF(I9_=ye& `cr:/ 9AjX<$О|qe:֗+H-ߌGcu n-Ï,aP0Ye: 9^婽p)< ]~xu$im\E ƍ;the'b6u:"MX%(z dzp"S@WcŖWL&LX, -d)p2U-|Ӵy}Pq_+*^x [pD>F)<*ߴ4,.T6+Zo;>O\BW{  iM4R^3p-OV/GH G }FLϯv9u(T` Dhe@E;Fl@hN #xZn"0F[p% -ȗV(LM,E ?KU%]I! /\Fj ]eS@[RckȞ*rFOF%0.`֕W-TR@`Fdk)&BC^"A %i40n~c+>uA+ Rx= (gK\~kTwB M"0 ~g "8^A9c-AKi@sYpGIPzx#gW:L}k5-)MXJ:4]z0!7㥶Ê鰢tG0%0>rf/Q}ְ"O0>w.d$ƁG GͺPѿShKJALj^S|rÓG]au?܏y76 㳾%8ͫQr1J].K@ݻW=Bsx4ftItL@:+]F@:\K7F4(dʾ%l' i E~31R|p~Yc˚r ~Ԥ2(.p^QW.~wDt=M-b12ecvrr,켊yHxqثP;%\t{ @{<kGcH:ꌂuLP /{\xy㬆 e6o~{h<#4svtY|3y;1pXYU t3NP4+@p`2 -&v7R ޙ|zͰ"i#6 m+WP3{'v[7f46o- SQ4J c})hV8E#fl {KHmhIx#+v1l\t%t{͖!Ef*%ħ49r=5]WqPIkVz Rhvd.C (#װLHo-/6ujkcM&W:DnaN_gv#=9ꕨ ,'HЎ w|'Cl>ߎGcwscç'tJE>q!Dh77x4v+xD*݋^]N.Fy#/x4v&Dgm,J[||rX29T[|U<o6'H2=1x `gn 糴PX{s;}4}cg`;0Ǖ5GW HEvP]hxP&G*v~Bg^ >UdoͫǛ1vCL#Kz-cѫӀ3>=yUsf"<ޡy%la"8CP5sP<\E8+YwVP).jW(/hǗiq#xhpK8`ԴN 182E*0xxAF g?GcϼgiGNQ[Ez儵xY<z _J Ux82N>>? > v_hP?HFIENDB`tea-qt-63.3.0/icons/create-dir.png000066400000000000000000000044501476733534200166570ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATx{p?g @L&% q:t:֩tZaӊ-aZm#bG(hՎ#e<6luodl&;sg{sW\0dZLcL "6Y69LCL ;%@D<|4yƢ.zlu}:s9 "ӁIyz7 v`N7syp׵AX`.1&n Dd0<=a2h\_"Rμ#NM9UnRxXZV08 l Z勞v="TqGEd(/wum-i4- #yX`ޕ}OG9A9(2Ar*C=hPN% ]L`7SV\jK!Y [B^ >`_*E>95WYUY]b TJ.FYH6؁#@i\y"omvo^htTQ(Ua윾>kNP+ Dƻ: "b)~]:TSx>XݻܶC]VTwY`zh|fΔx0 vglTbcON}سxNxpոq׸y~IjJau|~Jb7yx?ݣC}R<9 ,'`-?0nX\֎ze⥷SUY/_x/]!ň)lٺ-d8Ϟ}#5 -p|W2yR>7͝rc^'sRUYNUe9K1屯}x?1s.l6[x<]$`TTmӍٳ)TVu/eSLʚJs㆙!FןTO@Q(^딘 0kPuxp%Ξ|Bk|́ a"u4J=F3<'("ӀJBf ڀ.`:QsP2S pVΏIENDB`tea-qt-63.3.0/icons/current-list.png000066400000000000000000000031311476733534200172660ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATx[lU;iK6"n i%-RbJ$0 h4D.r UC^ Q"*ƒTj@-BRӖ-!Bt=3w9sfv$`#N^fO1Eی)tS<8Kw1E@#Nmj~'/=x<n7Wz=ݞu7WUþ X4mm׌l$''aOf( 6 :nCQl9UF48w'#0 15  l{ Df(^o}b0{hrx .lu PťRqBZ(--ء,( \#(Pw7H?Bu_&IX,<4hǸ L`vfc0.Amm=^Kooo_z}}ruYfr= b>5Gu ΢UV+VjE>|, #??ψX gՇ1? WcVv* QӉ#{{Ynooҥj S=b`[M8&yf h Իj!čpmc\p8*4n)8y;z{}Tj8|o@!())TMt8P[#X+P@"◔&$ISRR]UN\\\G.{|1M%zE\23 ̘~6>9z*@)p$̎4GwH3b M6ԑӭ/wp`XYIENDB`tea-qt-63.3.0/icons/edit-copy-active.png000066400000000000000000000031151476733534200200030ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATxklU}tK7RGMEB VILH4 @PPhDDmL)@y`+( ]B X3nvg'7{{9?s+ !H:aO#Il @Jv !nuZ+0#@ ,*^`-`O2 IRpp <|>wB M>7]4\"p'M-F 8 t5$ $)hpiZ۝n(.’Bf^9c>nrs'Qj8ӭ< Qch)%ILΉ_RE$IX,(ɈV&Mc9clqz`!ͺW4Pa=YqHt?3RN طo?ͷuZr ɓ&xa/mZ,j5Ӝ0k }EO}XtDF2zqltK>]/p9dYAe~?,#+JDcur(̻WRP@WrrE}#oGuYl7%% 4#'LCьq)O"tpaGoᰫdG؆Z7d`ݱ$O>O;g>jzt?* +ZWeEP |Ȟiɧ_~%=Pah@]x k_5D:COtj[Ev-O?fμM{@7Wl6NgT^Wی t^7O>1ѐQi]aKxM>,>Sfg>j\\O}Hޕc!g;ٱ'6}Zlv,@#~r'G=6w/_gǮΜ9KM(^Tjm"7ExQ!puc< 'g ;bۋM%GQpu-,! 3s(ϵu4GXXMMt"l^xOmmmfg:t456l(rM7KEglfL{CJ>'a]\\q9qKLYY8 kkBu4=2zPZYntauiSᔔ6! K)۹qƪ?ZN 8t+ @Ե |kQUUMUUoC/\grX .)]Ė.%=jrleRVC4C}~P/`J` {h|[M@-S&EeOpDH0WԶ9IfJ%;A3}kil /w !Pc4M*fN7NJd&i !r?7u⫝̸IENDB`tea-qt-63.3.0/icons/edit-copy.png000066400000000000000000000024351476733534200165360ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATx_lE?sbK ?hjLC PU#`1@@%J1/P Oj{65VZ*BUjam+9]g'nf~fBJI:-@7. !b@sQ)UJ)n20HMxp'TBB,Y]0dx89xXvZdv)LJmC[W2/oQne l.vf+c-𥔲4r^XPTtG+bϫ4 aDY;)]d=[fBAE̼*֭]݅wt:jSu)SO!L|VSͶp~A;<֤D7X~&)zHi0cnI{f}#4::ʍKW&hd7 % %Bz:O:j4'纤ٍ9uh#&cn4 Yy6 BfӜ*Oͩr8Ȉjs%[O-X2`q_6 ?6e.Sƒݻ6ᆽ^out`tX{靜MBB x#zJ-X2`qI1a`@d_'TOK]ƞe 5\_ 1@d -@7)+ &q 0cɀ'WJ9SO9 Lpp{W8l|’mNTzڰe΄’OOVRǓgfΓHRyeܹr9R]0ze)w`g^~cjUQG˶;^>2Ҝ'wlgppFf7`!ISϕ?͛t mmŎ.~ @'p$ A"4g^%k,?6=>:74F RDgiHX\/RTގ5_W^-g" *p6^xPHekRCDO5-mNOܬ2v\1)JIv|8/5S{8)i~s0w[^vIENDB`tea-qt-63.3.0/icons/edit-cut-active.png000066400000000000000000000047241476733534200176330ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< QIDATx}l?<Tk ve8X`"|e& 0;BDɢ&K` #θ!!cX2kssK_o_S%'>=;sGT29=@OS\%"Ô/"V)T1C= )- q<<%= SH; @@4sKrۤ08q"z\[p8N:~V)T0`@|щ" СCMAA= {BuED(..c̨dHDD="FDJx|@~~>%%%?77;X U=h@qq1FKe"Ry :[UL JJZ 10OUMPkU}YgUM{/PkZk5333r~#"cǏW@ %H$ u=(c"=XBEDi0d0ZsU@. xyyy͛7[oOD"mk2dI+..FU] |bDDf=䓲w^c{*U[UmLcG}T=&>Ĭgk-,,m߾]{nӧOu) {޲e.Z1cNcyi]]ZkZsrrbƘC]vm3&M+E>9z@vjsa͍cN/&sf̟?_E$\1g#H|ɒ%Z__ԩS:bĈ1,@= ~;noe@ȑ#d|mmǹ)Bh4kjVϝ;(oaC|FzKwN f[Z%%%qٚ?WDرc444̙3PK7"RUVV֢Z}q n1qo222b/&g婧J?n1gϞ*7nT@I&ylҥKƿ-fЊp.ZHѬ,oժUn5k$_m:nذ1{Ց#G*ӦM#Gi-[u]v 2@… W^گ_?]n][k3|j{OVkVUUiQQ:c =qD?~\cƘn1T>Atƍnպ:[=q1i1Ȗ+<9sF9sqZ<'XkKӦc:pgϔ)S4''G'Oh?Qc555`Ͳo Ƙ&Lnݺ1k5+W6_YY-gddQ.ŋ'3gΨV7oެH3|7 no>Z^^S5333n *Ƨcμ)[UjaǏͮr ROM]$}JEjf~2(U5? ~GڢzFFO6/?y)] !2>~d/saډΒ$&LJe4,81m.hJ"8 =@@bLgIENDB`tea-qt-63.3.0/icons/edit-cut.png000066400000000000000000000046651476733534200163660ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< 2IDATxlV?=[euؘH4q ͩc$AFhe :ؠ3 ۍ8HXaHGii{g6y~[QU> 5 0/Pj"D _q]y9_nApsYUȃT꺮[`yAD>7pXgAMfK J7&N7t Jw1(,,c6Hv@3̹KU vjF.#(`+EUD~ 0c5kPkDq*"q`U@7nܨZPPc!"> X+"10xS cN͝;Wz!D"qC@8Դ#.xKVV֪VW\A"~G+))I_mjզ&0aB_ #dwc}GTEDm TY~5ā_D<x٣vRcL\DD{4 cL̙3%K%1攈.\.\Њ 6lk\׫FXKKK:nܸ1@L` ǏUVVV<#FE"}}!l@wUTUU%/ ,cL1&tRmllTkhvvv`t @_vUXku o6|m~u̘11ċ8M(ŋncIs.>677OFI s˗;eAEEE" 6hEEEnuuu'A]qZ;5$Z]lYb29s)55]nTܬw}w\D܁a ."+Vt)@ssN45\nwЩS555">4 ac] `cǎiZZk \NKKmذA[[[hѢׂm9,"G}!^s 8S,̫.JaSxU<(%Ȃo=r:7 2nh"1 8?ǫ "rJ:::\Bq- 0.HIENDB`tea-qt-63.3.0/icons/edit-paste-active.png000066400000000000000000000054431476733534200201530ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxyx?'ɂX[KZڒvSh"BI)b A-Q;AT-JEIlum, ddi2I{s}ϻ͙s(k+PJu"b@2b-JӊxxV ɀ4j@ΟMHHDt@vޢ }=9===$QB- /svj52gǩIRkAh z}y:َ_lmmH\Fz@\];JB|zxzzH˜E D)%_| i|?|h)S 8HreeK:S;RޫȻn=$AI{([82il YPJ!=)ݤIc6FN&4jԀ-Q:GGZ-vZ;Z vvvhl}M:&[?Wތ~fMWVD??#3frAV-M: "|tOFDpwo)SɱaS'g-ssbb~٥qqg[OϞT"_x-jƉ)`hOT^TV];s4hz RRRXiQ-ΖSKy3NNeطw/P)Tڵt 6si0zSz?='ݺgD$-H7vEN+C~$$$/+_ԯXfß;F< GGRo˷ "gRD$Ğ:SB.!t::wDNǂȹgkk6kfg}8؛.yz]z̀cX`nc=k>̇ٳ˜O+p?>YLd(9kUM@D  o-uF.9[vA2o c/UKժvЊX Ϩصk*U][ T,Ndn^~A [Y@kae+ؾ*'j}  %`4 aܻw*F% ~ xmXڞ"GoDDR@ʕkzѫ?G||Cu;ii iii-g zZ666sTTxEDX?J_"ֵP$(( 9ȕ潽YIENDB`tea-qt-63.3.0/icons/edit-paste.png000066400000000000000000000057751476733534200167120ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< zIDATxyxU$A&! B*JPxb̥Z, ފԊQ $( Hd$0&9o9L;>xm9O]^߷cHYyz0:o`TOY#K@.-zjZC||5jp5\DL p Pn]u$[Y(9 -!*6V(=O^s{D(](4w)Ϻ;_ w=-|r,uM¼==@:֭o][+5ޭ{B^^^>?["@Rn6m :4>dM|Y+sԭ[ןNZt5n|bckdxYfWWvnxTO&ܗ_*pPpp;dPgJ͞=]:u젋'AWscݞ ba>][jǦa!Ux/УG0G`ڌ9VYBvovѢwް۶nKҥJ * -qW= J0 ?]mʗ;61~X?aIغeG2{4$XS\pH%O^4NW Uֹz~=;eʳ*W|K&>u󀊌o q3gXV;volOWQT8|}}e6ǯq@M6O-_|4h @AA:k:WڶU'E@=z+ "M3&"ZMO;^R\Cx|!6`ܸ1t1˒'Sok9B|{ZY={ޮ˗]Ap ڳPZ87-שEJݵmۻҸ{GrrOWx_ַR޹ԩ[2Yr^s_N'-Gyz V8 ][:!!w У}ģcCFs80D|||,{ډ԰aCZCh]j64Bvڵk+@LBp|Ϟp\Ωt֯__z]I G }SGkd{+!>pK/B#G>$@c-E5<ƌY*ԔD5j'@k>Y0x'P好{wFZf l^noyyy?ֆ.-[\1{ p*]YalS_PfMu!C oF , #̚=J\F^=Nn]ٷwaak%LzM-(r8ڵi  [|Y]]j޼Ξ-PWu ]pJ030 yyF/9K>_J#xM>>6ҚZM<|@;w4)>44#xEEHqQ&OdvO=5;Ø~,,Tv) Т^ q +{*y<8L-ǜ%5k43 ixDQB^HxЭV^nz9QQ m֍@妛 ///6^ /6SW\\Z:yԩ)@ZltwkIqf̘jGM{E_ls3?yoO-_ȱfY[G HhZjA&||gؼy3mgI իկ﫥KڟTAB Ut*ojua k[;}o= A.z sGD`={9W=I@k +ˁMI>Y̾м93伒]#m%EX\KrjۻCm۶-nҦ~Peaẗ́?u]ŋrOA;zs`[yP5YZMS¥KxlܓD+W0iB$۷Va O<8{6s&%%P- 6yb i6''Oi}~Gسs3#zFK֭xd9<8lyy6f[ LxvnylVV6 c޼xyyW_aur5b97"di̞=n6oF@@K|6b;.dobɒeUۋ w0N=wq`vBRIK;WDi ???gjJbBaF7'OTZ'7V0  Xa+Пcv FС|}}yw1n9GkHJNB=.b |Oӧ`vB=J£`F J+Ec)K x0(vy|a r], ?856 sIENDB`tea-qt-63.3.0/icons/file-new.png000066400000000000000000000012351476733534200163440ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATx훿N@L9 e^wHtI< @^ Lj DJEpIsC|n;4jw=;uU%e 9sfˑ}yLV!l-@ =z3! T p@5G^m\` ggww7YEFF)qW|Ry*?/K(n\8F\>G6piӦU( yJA~ƯUkWG.gNg߯Ɉ$0p:~AQEJogx)&Ֆ'E8z@&}V9uj jv’ پm +e} rO^+5emB=vt * @Y`{?5j|s>嗋%.nJ\<.RW!QQ*a: &.5KbI >T6~~KL~kxxYf%*wKyLN:a;%44DR}.Mcʔ0, B >/ÇJ0oXo2{T倫|<)]mѓZD}2x 9Ϭ*4)XC>:",5{+/!ffv7VҮovlKJܥҚvgNe2?Vm'MNied= >?n/((NVPL7JQѣ|yppAK%&b[tm VVƟcUlٲ??IlS4^Z*Y,~y!3fe2YG93U?W+IM{@!,tel[Pz3:HaFoX|%-`otkS%87k/((?=M@`(c~5T̙F1%o^^͟爐 [[f>R EFVABzXf{l}]]=?1c'ϓ9';7{{{s ݔW5aW|Fa#Ut whe]>dfeիg{SE ٗTU]3{  ՙQ?\od׍ ̘1)++'an;;[`鲕7Ңit:ڴg5!"3 n#%( tNڬ뛷;;;3djE$J4248n)C}7}AMMM{u%B̪̬l)qUol-* `pZ-GUސBM?T4+k֬[75\J1q .HeES$2rLa۽C 8<0i½$v[Russ.Htl4Gvx[*U0BQ@ZeƿKu,Γ _jfwd_(v^]}|m[7v*o,˗,[Dn{KE*n|2d`l,ZC 5zdžuҿ,X0Gr^1q>Y)*i{oV( w U{^_W!|G$s̒%ğ` #ߜ9iϝryL=D2qbhg{L+INo"8ks2t|#16VuooۤHNQIخuTHpSM}UYhxyyʆصF/P!L)&m&P""&ɨQ-Pk( kS-Z(ҫ7Wa@df 퓆JSb9גs˅^S]*^*HF@2wn&gNc&!!%]o]n- 8x7`޷玎 '|nNKXDqnv5e[gK7E1 n( Mauvb7lf9t:>y3f-YYND$ 2ڵB|}:և. 1˗-hϭo:4rٶM~澸Gr*~ׯYnVV׬#!Vx`зƳr\]]qxemwf]ٷDubf«хTg>'%%ӣN3_p@/c<Ly"c'8v>XΞM8Z߈HIl#:Х#8|:Аq899(qE,W--y湭Z%%r/KSQ}cWR(;r$/_ h ^`Ҥ Ve;sݼiHjZ0yruȊK\`'p2Ynn 1aO[Am-7@mfip.Vd[iTUՐ ASf&)„Ч,n]]V9"b\ ?ewt@Qu_3`7F>aMm61 48׃?ܼ%4HA_CC#W]G@cc#iPz"N\ȥlѪfRI_hTH@DD;hY8;;2lbm@@vV>FАq8;;[[MȺ7_{/-SYyJDz?t^^XSNVo)wwk%H,/`@)Q_F3p`+˲ ߻, =gW>\""wMc<!6f63,0TUU[Qmmw>ym@#SF0mTtzY_``C$%\H]N^jHk=i7f#@Q7eS;s(V D`)"?Ti IENDB`tea-qt-63.3.0/icons/file-save-active.png000066400000000000000000000017441476733534200177670ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<aIDATx?H#Y?ϑMT% ,`qqe`lZrq h!88'{.q{|` y )%qN!@r!OROG|o~/uS@1 #HٶMq4M*wwwJ)=y@jtt(5j'NsxxՀ.^D,"^j5 v$`qrr8bbb!q j\]]122⻐.Z ۶(J \.'= h[[[*>F`XZZR ZC,lV#$= h뺜I 묬mB l@&agg'ľހST*q}}J/ tH$B}}}hmm'i,L&pRd8e.ۋ#[qh^ *>R HB0;^L 8&d=>e=<h6C\1WI,;g8b?ˋJ<ǟA$pvhX=]ꇦ\t 4u7?;@^.R^n,%dnnMcܽ[*9Q=Ծ]ɴO~&2󿇧zخ;|"'ۤ{+'Ko-n[ϸ p2dv5.͘WL2cp?rܔ]Q`GGD&$^*<ޟqYLMM>؊daqQS>'Q~goE^7n4W_#&Ҧ}[`hhgZ-f gZ@]Z(? bò:uj1Kx$}|s!r᛿0y.\j˱, {nYsn3`^f62 vO-mՄq*EdYFe["xx&UˮT V\n5EW?WC<*Vg5^ngAL,q WqƁDCAQi=(:I׭Xt)~ǀ2͠2m+B$rJd-frJO$7 ,@ww7*Tաwǻ4Z[qWVpi[\C ~ϲ-=j+SҥKy{.:{>l2X}T'Ohϟ_&@`ȦbB<KW\azzn+BIhooGf'2n+ª5B7rV 5W3:B Pm0B&/JDE`h$Z$IM;-d=en>Nr 뙲T^,%e "fkR6、ŏ6/#{$4XQ))7n{͐_ndG#, τ=}GST3gΐoƍ6 X DS&pV+Qvd2.]ń.X\1`CUU9tP9rɋaebY#Uc8``xx_~&K8\L&tvvNWnbX$a:&߉b\~ P%Ycc"W?.x 4ʞ`9d2at]ag/.9`i30,/wR^R?^ՊYK0 IENDB`tea-qt-63.3.0/icons/file-save.png000066400000000000000000000017761476733534200165230ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<{IDATx?H#Y?H"]cdP"XmX6:;> VvvvXlVQ/E 0J?$-"hdg{ę y7d~¼Rd;)hW1$YJ鈔?p[;wW^B^_!Dq\.繿 :V D{{{Sqf4ս/2hmm՞7 p`g8::X,Ң]]]\P(!HRB8 &ɸR)a ښN-OHó}}}LMMNAd2I2ԩ a XT8>>֩45 dnnNYW666hooWـx<֖ϗ@&VWR)QΣd@.JYƴQ2 Nka 8;;cuuՄO+Ż6EI_ %3WQHH$iJ`hm -6B6H$DQy]f.%bhi[Nj͆KQ:::T ՍGOX__TD6:ž=!H /&4-u驎ŀ}id ZnL҅o~M>qm@4ezzڄH)\011aB5_...z>ݥT*+? ,abޫ}얚Fsq!/` i$J|>O\KJ8X9ZAݞ:ah=BaෟRh@ok`S~FIENDB`tea-qt-63.3.0/icons/fn-spell-check.png000066400000000000000000000030521476733534200174300ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATxkUUku|L@XFaL"iD؃,5M}) "QXQY"Շow{<ε{>kQUes8"Pc8R 'UԀ:c)q88S"ґ*HS@DF]EUUUE8D,"17e~/"#R\r.BK~S W(|Jk 3 DtK+,$`9BPݔXӒ*|QE,Af+4`vR;kgp>ČFB!"'=}6 geORDw! rd|["c=ygLuGG/ȹ:!}\:k;~U=$@m.Mt;łtOUYoxV`UtnhV5堊'JӿG͚IENDB`tea-qt-63.3.0/icons/labels.png000066400000000000000000000024311476733534200160770ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATxoUNV kk~PZ!дD 4xa4x! 7^H"hk*MA( M#mҝ׋mhk;=l}9s>s͈R̰&6a 2^(iU۽أJu}>/DU`i&|, "I@g]jn}뷑[7*$_ D9 $.f7`3`b UVG'̷"(۴)vl&DN:X*YU C+"Czp 3m " G?hr%@qn:`pp o l޼x|@BDDkr"7vmCKu/z'X la0b>2p\<}_3gϞHԚH,7^e玞qmH, \E2PyG/%&6JM l;t]t:dqigN=m.\b,¹ԀWQy*i_6o#;7@cM 5H=Y~]`6Hh4mGm۶gm뙶˃qZ[%X5cI#owۛ9 t$+~, 6Q laØc]MO/?0>>ǫ)tum1Eۜu-xqFzmPD T2=wʲgN3== On1B1~墑qF}C%&6JM lƶz8Μ ߛ~q&&'q1rTPUxeéS=N $x I]]2{4v5B$Y^{|tnFDDB,, FI8u%'@( 6Qr  ~nM甧`h^@#I;NJ|;[]@ݠ֝:y k8Êxcc)X `?kvs㆙Mu]vg//lT? 55qm{FUehx# \p.U=+o#d~[Z~LgN XCxOU$ BC /I\IENDB`tea-qt-63.3.0/icons/svg/000077500000000000000000000000001476733534200147265ustar00rootroot00000000000000tea-qt-63.3.0/icons/svg/tea.svg000066400000000000000000000117701476733534200162260ustar00rootroot00000000000000 image/svg+xml tea-qt-63.3.0/icons/tea-icon-v3-01.png000066400000000000000000000223051476733534200171020ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwx?3M"$vlɲ*qӳ'ٻlr&Onf78.68t;cqؖlI#%R)61,$~9w*hƹ,@9ɹD@gOaF~Yb~sM$+OL>١8Uŕ|UPP"Ӟ1C U;UkG;2& v;P 4!a >$M ,291zʇț:r="' [-:XNC|9 k;{RR-]tELS/ӞQz[.y3ﰓv̀SB~Y>|p[:@Ip#z/;UK%G7,?H|h@XîtC`3"C{/";J7TQqO|~-kF~Yr17^ESળnWEvֵϚ_.5sGh輜ẴtŒ{/2X=|8 ɿHw?N~Yz+@[މSHk0&hNY ǫșaÜҊ-_7-$Ϥi%,}gs'ֶɟ(O۽a"kCb2kز/#ImN6iG5Wtn!$wi!,93+]ϖzR~ߥ0׍\QG$qzS۵sz#Լ`'t7$pr[ x#]%rhI{6:dfuO04 $T/,0 ~F$Gv_J_Qf]FoL~ǥQN߉CuP2|.Ky4 [L*3#htQsoB=Gώ?w_Y u/(#ٔ5P1@ΌoU}^NrI3+V:Xk-L?YzӚ &(|@žlTUg]St|E54^VW:06'kz~YضێނC]-]%!^k+_;xVXlF'[AO$X6UēcE}mZn_3hI6,zQm^Owpqq8`!vWk\W(eԢ0onx@ž@i?6cw pYWk|ҁZ_f4oOrl \Cx*i,ƶUl^SSnG>BFe>Z ۆEd eJeʞfUxx "czG$8R s_e)ij*m":NT1JGqH䝥#)8*ld_TE/6f ScErLC d-`aCiDV`)uK96Ŷ(3:QÃFA3`W`g5&N)tl䗥dOJb.Ҋl+$H)[`Whs,Jfl>Qm- 1akN4u\E0ko$ ݿpICƒon}ضܩb QW2gD6dZn3pOw?^?εO276獕L<_ŃwаݶD|kl&~Bߺq8>cZQ1Hx>,%̇)T|&3W;UdhK>WGJsE2\Qlw|%C> `ۛ5R掫3끏%:'1/K_i~2S9͍O3f2:_7:YAA QbV9iRuɒR4FLe3g3A1'KV(̐xҊWT9%T+1 ֝ܺd'R~_ol|Eu?wZ{PD۝J\ [|7y/@JFT lp*qQGo>_7>3z&u]y}q =ޓHn 754ڪ_IJ.Q /OUS,dlʬP>iA6W,;j9>6_w*nzbڀ$[S[˒_f. g:+FѨ%kYP(r 埰ۙ'uS!^}~+űi*/L ,3s:&ꏠ9h8qNn8C~3oI?vE\qn[Y4nE=3㣦Bʇի pCUvgƒ*l@-L>cFƞ1FTzg{j ({Gb0UWc 5Zc YPV1B*.]qW⁴'܈jPGdɛ*Uiv11Mu*n&˳i5>jP/?LSv%;DuroD e_/r}ᙓ lhb~?#ӐʽQfmdrҩQ= ۅ̧\3O웾6x#¾G-;\j}˵K0?\vF NxWȔƽ+T.6y%u.&[l c{5G+uyE œZT>\M.>"Lč"e6K@QuR8ڮk-ÓҼ5 <'̮{5~Ye*xGjUx}< /lC[}k66nGj+m11I#؜#<5A@Ut<"1fD2q eAhԱ(k}vbW.Ǣa۩d˟فsjsor8,@Sr\QoF'FyqDXwWeQIKrfh,+)@b.GO#omnCCipCt4;Q WM&G~_MҼ l 8zzQ^Gibع{{Zod;(us:9[>ayBH ]`;n؜ԙl #i(~Y%Dq~93>]e{j5~ܭ#QT 55҃XN?Iq*EK@U|#C،k8WGi!+r|'UlB#?Uȑ $6 M} Y2gLN^f{L8&U}֌E`1Hֲ~3gsi\b]P(<Ч~ӯ͝< MeM qԋ9It O8Ǝ+XwmIE}H_)l:vYx{m MdL^v.zgZn4Zw$qŁ L}8?'̣$C06hƴZM&M5n2M8ڌ׆R-#I]ץ\qn6OYRUuFgkDNelHqԏ+fc\}M}XuԤ>7Wgrp*vdG4']  3 '58bԐ]*(0}{f]&(L{F m%,zspwZE5=Y16ԣrcfwpE(4n UϞY\5V]̜z-UJgV,!t"}3zT(C&;UmS(cRD/PxP"jJf^5H`8졅mE6fΞ)4t66 =M2LE顳3c;# p#5Dt#O̴l|cҨW)0RL:1s4Mf c0Q;g a,qeۋ_#F0RD L@8@$A:U=p$ِsAӷ7MwKHxg\e$|DtٌUpj9ʼn9\G>D>Pij0)jġוaŶ0.Ǎ/N*ڲŞqCmФ3o Qxh@-mu]B`0l%gX/ib)N'CTBqG xeLs: j@XwlllM%rϺ͂}x7VIJ =P'&xV8f;gCI2'ƨ^Kۇ;َgjfdr?+Ms\ j`A~ J_%߻~ݯت;F̪<*uɜ9[INsPqL?8!1'z+fC:{:VH|cg0h4:T'Wj_Qs/k bW@ۀ/p,EufCQ#0\wA?✵cn?-n9$55'.5xTŐ+#Jpj H;:2Mk`E==EotD-vHC@N)7l #b[Kom+n#K4[8nT4S9؜{ u mj^wP~jg*d-?xxPW90%x( ɖ'C>@)w-mo1p"x.3'+z$oݴUc+v&3c&GɈ:P3(;j{]ƕ+£X 핯H<Gp [I9jYOQR{RtƔ4r4ƴg8Rix/ ldakıh \EpM'= ?RNs!϶F90a쨨:|Z)F$x,Ec\or ]&c9/%:'a`,] DL҆YRR 1?[ZDvL4H5B}geH7ŭ ]Ed/|G$,Sqq;Si(Vv pc;J+*.Z5Uh6b*qhf8svP2mP܀j-'.Dam|N ;] PzʇTO&q%UIR&E~Y{L UTۄ V0vaG`G\ .-**{5^W8,{'Yi^˅s0r@DoU;|zޢWͩ K`{廠 (4^88tu]fCC{*+ ɯ9̀ `~bKd:J ]hx6f<@.Ƣj!o]v9 WE,;Ŷ ײe]':tɀ~5(p*. Q1԰Pm-1嵦ó> %?e_Cl[]%{/ZRk  uٖj_)<Gj(S0^v>f;fmKIf2ucۛHisP(y urO2b6ck&!UH6p6p63>JFk.!fOCUO-z+"Xv# v#&#;;Z#A&̻؝VL~Y|A#KP6IDAT$.e|xG@뿗_n~ 3H73cMave:O$4bZ/K; Quyi0:0`V/Keˆh2mgr\aSInVRn>WC]%k4q:b3$9i'y $nNm\}Zxkt0oS[ kF~Yj~ X>yA߂:OJhheK9$ʕ*)%L~QuP9DM߅iW|* *}TK$р$a֞ᗥ;mvM|IU3? =G(ߗ({5N/KB~J*) nA81=@_q }\բ˻Mbg8%7>@\g4F|kֿd01EoQzZʶos)yN)N _灄$w8* +WڕFO+1ʨ`nnsEFW# ¹LV0, ;I9z^|M* { e U2e*sa= [™1V@ӗec8- ~Y -p',T25S;SqT\8 DGX+#Ӟ1=+)#jmiO LٍN o֖/ ؃> HqtC˒؁N]h-Ϣv,~)`Ɯri'pЈH!^^o?gă_Y$";ˉ߉^?yc,z{lzճ @V*-]IENDB`tea-qt-63.3.0/icons/tea-icon-v3-02.png000066400000000000000000000215721476733534200171100ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwxTUހ3-P% RVDWqW˧ kE]]] +EP E)&Z3-3$3<=fo=W !B_+,:F!DoMTqB0d~瘔#_\ h~zQe>XmR^==u'BZH/ԮR#(aX*L)ׅ!a { _Wң!KK)^\ \:jCpQODt?%(*+Wgr # ҳ!=K?;< s)eNפ!=<8ՅӰg=\I.Á4؛OAF+g.-B`;:=8*4V!&X,xDs2/S>WBgι&@1xh@ 6Z^{rq1p䌳#󀏀W9y8sT̀9m@Ц]/Ɇ `PLR~'P% <LLÝAMo?{)(_!)yPJ s%'qL ka&nQB` x9yN]0 !wfM) O# AM~kٶ!<no;P^g ;yY7J)no0Tb`@YG+]7jc>(NjF N/ ]}ѫ~>Xa@@z1W9OjҌ=;۶e3<.N/'?e/`?/  bݔ)<@xj._Y?²;6}kh\nS Ty1N7놇3_?҅9}n-=f_zK֮MMѴ)?ogl8s]Y Wr{)VO0^FJ9< iy8{f"Cޝ=  (|!?%ՠ\ѰQױ- ~~<Щ&O _gZY<~{>zYLPث57-e*G6|8z"Q_l;tu^:]Bf6)Bd>$?!W ?xi8a|!U@Q~ hWd> sת#;t6:v/ZC|}럅1$P1G8;ȳ?L%4lrBkJz8_@ӕAh` St鱆kml λtx0An/$E#u}ilT[IvHw#x1nona|=nS9].]CXNq0h^%}}8w?=ugz4y2[ER-b ֿ !J 1Zvwpp8GfX\aׇBMya6OfX98K$Ym}:7x3?8;ũ5P*҆ x>e6iS+4U&q[zdQq1;N 6*ʥKܕĚ^yA5ZJ N++\8^#5B?E0+of^;r,u(T+GLp!#ۯ":q҉_is?J)gL^@l*B6n elM)a.مgO^>ڡ{W~=S,0L #3 (c@qrRYm06:xEYHmI) SSB~KGuvN˄a+6S޿J|B\١5l:>_AmHn m׎_X&SRS<%y[p1%P|F4'O̙9u>K곕ZI] }LFKjЎK)?HZ4΃7ԳDWWЩYz ‡%ˎ__>3T;ElFȠ gtδSZ!!S3r9vd`&X>F9wr-O4M3?PCU?:76dդI>38))ټ%chv<ܵ+ڴ1x9c޽ܕDA/DLd +)Z+kg>p]bD%,}_kئ(X{4uaO xoJ>[nƩoGh<*Y{Yg_INff°^uѬV-ؑ=\d͌9Sv[z2B:SYYt{MNO6|{uDs􁧏H)?/ ]Epv4YS CYf^#_cp:KCoZL! k\yWzTV ܠ~dd&Ғ%tmmĉX*piixm/x5vF[ϤUR~%zGbkmm|&zp0}ycOAQoȻ+VƩSРA^OH)gۢ Tys(Zhv11~nW󇧆,zuRRZLȽ&Zfu/̜͛ -{Pif ޡ Tta%Lb lRO[L-X*}rtӌ9/Ŀ7ljl7) ͛[Ȳ:~oԈ@?;۹\[z:;J @ .05{D1q5/G?}1vYZ5?^,ޱCc5RJB'Bhs?:~w|D5y&̙CUOt #cg_-b ҇/ߍq-,:z1ȕ@Ϩpx4^_MBee1i|bMddž9H &Rt.khdg3 @K Pﰠy OP3L]FӦҥF[-۵ fPv@[pNj~رu"+7FwI .Ғ%46-⼉o(}|&ji;8M~}PǂiZ%z)DŽ7T~&n`9L{wrݴԨm(pĄI8{Q7hR&Ƒ=(n  Tn7wFIMw׹`HnMQ1d8s6hC?r5BBʂ н:nz\5}o"S!]Vn9Z MLΓvTB@c˜UAA5#&q(ċvz}49!S_Çar"Clhq(JEEOVnaN¬Llfװ8X_|8@E4-+31|'*?@tb>OIў/e\Bi/Ĝ ty@?շ/=uot#I 8@,vCP1|&'3Cksc.Br,qgs=Tk@KYDŽD>xCM]2[AUHk,GG75h1>oF'*n]غ*A /#aofB}{׻7kgG ɛa}%2_.7wS__~׳_c~+!(cqr!%!ƇpӷF+ uZr%O?պfUB`ㄔ2|0+ V6]h&Dc#ynJJ:ZJ>6pX|Ycоfs֭y8r;t> t@M&͟ϝ3f<5~C`))Gb8\:ǎ(ЬƷKL_Ux#:Cg}4s?^*֮'Mv`MZQJ[;(fXJ^'9?n,l [vr2'$8ި$F]!A'VQ "QF)%X8~sj5$INL=Z wp;R^:168N,5B 9nJN6{)` [cl?WUS-e}R t/crQQwPY,t)IGq^XXخ*J]R/z "*nqPZq\f`1  pxVΊZ󭿯*Dݱ>ܪ> x\bt)!`Td2$ĺ^uF@FՕ_;+ojG_C`Dzݻ`Y\tª2raaMRO`RL6ב,@Đ˗ pRx*|57*nӷ|o025l0D?CTٳuٳ=[esiB%0Gkv0Tt+aA0qƅJQ>~`O*w")aB`*R&=< 4ä́eힻx!3fp켗KY7~^S;/9W8n~PgwUO$q1It:MΏj]\-.G&(0H=_RJSCR~ NOgss{$^<$%:Z,~+}\uG5h-JIb;~5~nt>ݘGsV2H _VOk+WU:V6QNvXҸ1ƎuZ*RM᫯U";qm=0PKrS v~U9Ů*.N\chn4t( "Zwb…ϞRFs}ɹ /ӗ+tj\)S3J]+f2U|jz$T.X~JeO3ɣVd#h)<4W&emСjof6m*nʬWYdCbcv^&:fIUISR:%BhLOU қl?JvGخ~}1,. Wޕ+,߳;vtNc2 ++ɃW'tҗ5<`AY 6JM%o~Lz m׎[ 2: sZ17?ҳٗΒ;Ywo96Pf\o}RRMXˌlþD0*pIJm-!ՃNpB9CzN 5yʤImEyoRZ^R%%BQ֑- <oSu1,=Bn°KjyoKhhc5ߕr IMpg{Ie(9Ed5v1g8o}c+:5SQGu 0mV"{ vPmYy{(ju_:f\FJïtpC*$P:}+l;<X}Yy*g~)tW=nMxg 0XG_RjvJYTXQ>6 *6e,VxPxUFOOTzI)7wBEWdn f_KWj(cPf6/{,iZK:v6%t[@1/vVIDAT׍PDfP 7ʝfR5G@_MY wFrUʧ߁I1B 8)a"~S |zFJ9S!v bVEox2`:GXI) >wq(~~^ʜ`µkE~}f ܱ+ 50hciл nYɪDJ,ܤYB0W.wURKdy?l@{+ β&J)u(1P,L)PZ*˷)tJ8׋OVJ @1%1PbY_ZY峸3W"/T씗*!D`*~б!ZmТ&אWRL2ٲ|P*w&CU5%%XcGӀ.T=Dy6RsC SXҳUiJcgӓ҃'<[{m1@jN -XTq|aLR\X:J2Ē=5/%XSa$pfTϓR^mG tB D;fFϳ勿 eq] r hJ{pWx!~g-rC# DM /~ET\uz}znɨ3IENDB`tea-qt-63.3.0/icons/tea-icon-v3-03.png000066400000000000000000000235031476733534200171050ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxw|TוSUF] $$ P4c87/uI6q6~l\ooq0Ʀ4B@26myeTgDwG=s4n (h0 j?5Alu4כE! n7OvvbV`63b# -}% 0B0xmv&#AvZ&1qj=mY`"ze(L~ DtCOteQVFS7ftN&*"FWRUIyT jCv+-إ {R̢0w'($и5k[}pE'RX53mĪ5\5` OǏnnledzaJui{jԄՒXHΠsK>K xx"*, } ifXns2Wr}7p2-9_nj[wZ(3Seg+wkd%5v x"ziE!4l̼AL:8>>dshq G@^lp, 9K3L84ʨ^mWqrCTרV^~՛^(&ϙBfWU5le'5A E@F j2 jԆ֑? &(8kL@ujZ|N_OZ>PEoa,<vo_ĖnJcBCw<#O65l9j[B 0D's RXLB~ߎO*䳹h2ۭ7KjCrͷP-x4/V ˵.-SgQ~_gM8u3dƜ(b|1{#eV)+0"{y?jV̢lA!,[uC`O >1&b*Tqx .-fgOf4`Yn64Cˣ#˅DWE>MG|c íegIjmƝ,]7ڊ[f-;98~̢l@e}x;X4n%cGcA$z'IY~vG)MUqkc S)"z z<Efy&q:+3s8CI5 N{5R;ud bxN& z}xx 6Nَ[#:`fO=fR瓹kц8OcnQ|c_P M#lOe_V6}H툤޶ {и]ǭndTY16 ~ܕ]0w۴^3:tNڤXMYīkí?(URLvL(9ޠ br^H{G#H+NCa.3o b󷛽:M#ﲬrL٣Q&-MX~I"UNsvX] +Y))2)Ԡa^R~3Jwm{L= o)lGEN Yx-czn Av];/n)wk1,lle_hp)ݳV_zJGYq615MY<*}^[¹v~ !4*EY/ 񙺣<Oƍ^ _`] I9qK7O`| IYQ~3+F_E!3wjhY]eӲ'ʥ>RWaac v<bň ՟33QT &yS1Uߵ(} S)H,.o$aG{%aq.!H+n1~"C(:?Dg.#oGcPB{xA7Vt4:Ӹ56}c%g:Ei%ЗpOK5"naD`ˋ_3B(z:>+_&}[OjkֆKS14Gy9R3:6j|],/<w;ŜgR]Ӯ1, ~rl ]'+_V?Mrpח.KCA)wb.!UK在=z~QL;ҭqs1e1TFTSQCȚ#'(Sm3we˱ԧ4ZZ} ca#AgY?;>|""ϊ^ )_Ղg9-t>o#Qj@zCÏ nE֐a13R |k46+,W-au=ރ}z .siqӭq}|v4 ˉp.@t؜ʽ1UQܹa.qv7š9]1,vWq{5F2*], Y<{&sXY3M$%2H ڷu]9_|^qn| Ql)\qN12|2b6uu(ۭ_r#NAQpW .lI|Y޶K=jΩݓ}l\}pobEW^ח+R c]*Գ}1?}dF̢v'mY_}^ KcѦYɦ-7XC =" `1x^b#Ky89wP+ թ/M]Z_s|`P3NAjlBY:$\QsY!uه['n~,O~Ks][9##cɝU޷jTqe w~=WѐmP$+|2aHvWnT:9?Eld PA ojSRY*TMc"25z>YFq&-Obw%3ʛ)kK'[w,q9߿IF: "-h] >jw~3}JBA "VsU[ L8<8=_Fr>98 {U޸NfGW7Ptk ̤ۘ5[#k))KNgȱKU&zG?XFdM+K̇,j |˖`gX7s1}J1ٜl, PӘx9 <x!}QXuz2e{ {)&]81x5wiYV_zO.%)q ϙ_G ߜ Rl}IO m[TZqfˁ\c1|Ab|)  o&[d]Je.Wj4F^Js^Kqj()X-ڃC29>qu-o\x^RvHxQOA ^ Ы|~'IZşK4LSܸ9P5.Zx4̊Syaw7ٟ[6 љ{U[Tц8G~?Sx O$(`Yw}"W/k >= ëH쉻\Z%A:-׌΋i+x1m"g(${r3|d^Sy}*e|S/=AK,7I5Ģ嗴zx 5Gd Eϲ~?crl?#2M#y&^wb~VrW]/Z蒆8Wam"@ )E.ިP̎E}FTOz7|ͯѭȜ{I0PzOf >=˖R{%9Z=/%]R͢`fh1UR(S*z՟`NmS\4z~)EfpL>8gʇ[ER64lă٥uqjG)sHPX̀@ZBDMlq^<ɿ-4?y&E"-TZw&龅&#}Pa[W*u_=_r\fJ-LTT68%]GHC0ßMz~ȯ9KYA1,@<_]p k4ypc &SrM*.*4CM$ 5@ܵv^2ϩ-l䩜;j]u(Y Sԋfl6/n O+Wɣ*{Ǘűd MWusx7.糱c|3)6q GJWoM ?o}–0!r8gzj&MiW2EvQƬS{2N%YKo|puIHE,&rI;.MuYIœMgNPn5 Ruk}ݣ{]O5mpkTF*=M2ojGeK M6`&u aZDX7ez >H5)qrLA(!XvSDʭp-THo EbRmI#(HBAj)̫,l[5Q H̞b *~x+`O!q&w(os׸5z"hV'AQ+yԌX_# Jq$sD^J*Rj#@BKwUە>otdoSgW]$;9 ux~qOg ʾ#T n>VGgڱ:w0/ XAɱd{:I<)] "6UX҇|bV"qlʒ {+h7ajRR䑸IXTMYnaF.jyqKq3e%#r+u! BϾVnj+쾷߈X;{̎GSݧxT,Z V $x6ū.a2K{5A`FNg?OTxIYrq"q*Q iٞ,l0(WEݸx8if܍Q;%HF-h$(Eh-2I yEP*Z\oȕw4FnYnʧƣP Һ 8}Foi)@aC@3{e_n .3uGyWPeOP_^-Os"u#@il`4! }U^{e21oe˓g[[üfäҧ9.IQY-_ȔEA(i*"jgw7nx1 w9ۦ3|#,A,kpj L m\ FC>1lqT@"V'qKl59*q k}UݓX!meK"75_^:XPep'e1ruCǗGUy=KL<2<'ʈjMDԆ]њ7;YZΥHK25\ZcO,:Zt` Nls~AFkKWH̷HD},#G^E[/A $ugk}ld5]7]Iٸ## A5ˋ%-5gVt.H3ŪڧY˖poЀ>+ XQb7V@HyTe@ >P-y5v_pQc U=ܠ"A3leF_q62_ _wR+|A!pjepAh 3%(vS](woSpc۟U֕P$H.JdΎ>k,0 E7xGFeq!U䥼6NJ@ͺ_TE9]N.+V{mV g`eсO-5Zx6w)'}滚q],/o$+ vB۹P#mDOW"aL8P>U;*]ޣ W36g /YPrӺ,8'`^V˴?adzn6!g@n'/O޼۫>\aCHE1[M7o._t$klKǢMgBu ϟ}S'z7nWgW,m&_aKU =Aq_gڻfQ삶@vD\rkgmRkjL'ΐpugO IX\Eb oK?[p?P̢wcc,#ϪK_X*tUgU&9q?u+TO9cYu5'1k-qɺ9!QW-;euAf@|L'(M|9siDZAGa.i֕ڮuis Y'-IWzYE1d IDAT9ۧuB)1mBv)؈i _ĚƁ8Δ}Q=-#XyX5"Z<-e:>5nef}T&З1S'kH K>ď.r*e5X8P<t>zָp: dĽ>.ihN3rU{^_8uN?NAC>(},ц>hCrXS(UL3g `8E?"ϫWK`%geCΦhSHFcG g`qkh70|2s2I7w|пX#[:3e0eӲ'0. '75\KT%6Tͤ&|fʈjV.L82Ѭ_NwG7 Iu1wtFi8,I%PVKFF]H}ީ#f"f"f"Cz:jBxo.<'hZ}jouҺSkQkhhTJxsw㶷i46/$šy"P#-fQǼU#sw|R 8tNV/X 3Azfzd>N<[yzht;bj|wF̢ YEгhl)~s45ɼx][Edƭa\rSkтu(qS; Y^O˞Cw{)duj[/{*fQxxٮNf֎~@ʹE-gf`Ew YxlE47ΡJ,OMޑGNwIENDB`tea-qt-63.3.0/icons/tea_icon_v2.png000066400000000000000000000065471476733534200170410ustar00rootroot00000000000000PNG  IHDR@@PLTEV>" wpJ _7(UCj6(h&0$fz8*(8)8+/# " L "SJ&P< rfp :-MJ64* 8+]E, z"jb"n}F64)8)/%  n 2&NJU"J:!~@0R," ^F+"P<& r >1 NF* x9+<j:- {A1 6( B2 pjJ7* RRB:ZBZXD&F2 G8J8 O;2 2&fZ*(ig]~ pl -"> v1%xwt7)N>.`f-# F6bJ`^83A1* :,K$F2&! 8* _!YCDF60+2(6 +"HDg J7 NN_* 1&"  1%:++pn;+F5@0>8N:VU!.">.>/bZ% ?/!PM*! o." fB2^0$"^FB@0 ~2']&?PM ,"@0D!."PeAN&,XG*EC\2fUAZ|Aw~0O(Q=_칭Fȗxݣdر}Q99uM/Џ.#oFg6 qXGgԧHЧg5q0,&Pu@&w㿾V:bXT Jq`0BDee˵T˘;yAIGQ77rp< griĦ/Mt_Տ ;%8W.tgp)PWG@7bl(e3ڔk)I:cc/wb-OC"uuFdrՊۧ¬;@:e|umUӑS,ܞ. Y8KfSـƴME[,L߻ײ|X`g ,ENgz P f}&Ӈ<(ߺa pYNvN5Td"@ڑac~rc== )hr!LTg`5h@`:t/xO&8N ]|"C:*b<,vI}&Uu2zHӔA(g&Mj2R~KjS@:fH3F5%D}e r @QӀc4e `eB!E>ʢ  @,tA E=L!O&ĔwB< A7هi<," 2gh od\ЧH "PbK OjLrdP[+ 4=ј!q|$A,^@(è LݛOYDzPĀa Z 3StS+dTC4epHxSڝ,~xzPI4 Pfedh퉉#:y4\]\P^|=0j=&#R)-p+OFȸ ]|{yj?.0xG[[O[pp-!nB H !^FO"Z 'zWMV\>S<D?αajvcKj!oab@jrKsP&rI<0/"b` \ai@mԻ&<F!x e 4?ՇR0uV5po55{JvZŨ#XL)E wL|ȱ‡囗\ѓ. .yN--lXs}`m`Y%^'g r?tu{W[Zb_qT*]x,yB3ary mTCB }z۶mkty_V6=p\x|p#Rbq8 r2px8^_[OHk+ɰI8MO!h:`ܨ^a#؃AJ'ndޥᔤ8av[jfXA$BzEOI$M&aw888,; e{|p#xs1ӜD _< \rc_e>,y3 Qn Bܬȃq{W-ݟDʌR c`;mQyݰI\_2de 3x¾zg h wtH[<=X4 khد:K1+&[׏nR_I$tc ~u=Sh{B fuQW^'!ka݃{y/MCZ"! ]QܽEtQ{"DLwɏkA/![u RF+>8Fs ʚXIpknV;ݶ-\mD^b8^_۪IENDB`tea-qt-63.3.0/images/000077500000000000000000000000001476733534200142615ustar00rootroot00000000000000tea-qt-63.3.0/images/moon-phases.png000066400000000000000000001114361476733534200172260ustar00rootroot00000000000000PNG  IHDRzpZ^sRGB`PLTE___wwwgggoooOOOWWWGGG///777???'''ϵF pHYs--aitIME1M4tEXtCommentCreator: PolyView? Version 4.22 by Polybytes Quality: 80 Software: Polybytes PolyImagePro Comments: Software: Polybytes PolyImagePro QCV IDATx}L,%@Fdi2#~?3է 2e1Yp8?ϰOƕsae >_f! G{>׻PC*ٯdRz,|G=[&Bϥ>_Wϥ[fZ&1b~3k s ޫπ|a);?ןw~_\o #/5ŕ)[gK 5Z&+."K ?FQ?V/b }/_P_ 7| o _S[s-1GY1V4"]Uow}\Z7r")V`VD>YlW#nJv [TD?bJAX~qRY2~tGOb z+?XmS|,+6oOQaTV(~AAz@|.&_JD:E ?Af;TLg1?ˊu*b]tOC2_d Wl𙿣BP~gP1;P!a\VJ-%cY!mTLYR+k$9_X`ؗX2s s 5Ѝ*,\Tʌl(Inʚ%8&*֝܁<gHm,y R:l˰OeŰܭ*j{uEծV8Ęٳzj7zT>ږ ?n?oeB|j$?=v"6LS=VQ3AQAhQvvy~`TWT2GQ o㚣^_ AAagxqfO<  Vl #KT? a~?Qf^V2x& ؗP}B/t~**^>Uʷj1^3s<P1\$}0)*T/Ļ!lg16')A s2QBL?I\AR<,gFŋs^/8ŬzO\a-ئ!J8IB^$4Cbo|GVq!EY$ k3߷U Jq#;n5ۃ7]M H2$Cu`oS`"e::sA)oQ1\LՋ0и=K~ߠb 9 \#;X{L:^,ۘ+6ɖ*| o!' qhb̲m|~f>-z3T^/x[@ړ *sˌXqXV#`K01֓-T#s4^QޤN1 ̩GkmVste*s 'B=4FyԱ;0~^3ڠAsI 9TzM37^B_ԖQzTVF5*<4GŬT̼v] Taq&+T&drs҄Z$>Pfg0gMM䚻=Pr<Onv5eE-A?֧-ϵԏPf~T&*98'~|S{[P7xZ]|346y2wkG$׭oQF[+c?\xwPq|YC*B?ӎQd|{ߗz}+oS[4nbgR;{'ARq |6輏7夭c;vhcy1GaQ*ʜ0>DGQs[S؈𫏋>@`N]Qqe Tdm$WCt&Q,I+Ux{CT3->*|9ܑ|ZERoߠ@[Ӹ46Crݖ`0ig5#`VtQ |qczQxCS-&[fxʟ0.UTb=*7 @ŨN|>^Oml⥠iOK-/kwPG`Al@ygY apDm68_ [Tșjj ڃQ~]TL?Љr%u⻵S%T7xE&ۨxSZ<~uf}ç 3[9[Dx>B|^O@R>N}tqx[兗$ϲTݽ!,rD8BiHyuƪ *X3CT ɓ:\K}A rO.?/ Ob+}S:8g d(h^NV$ڏ>to9Od.ɬ9B6eE^t?ՙn J Z 6$ivsF K 2 YN;˭xpy9LQ,v)azΊ[>*w  T<-ɣX}P8^T'n(. + 74MA[ X~o淨Nj -Thy<9W*M2=fA`Gx0y"-C ֌񾬘 >nA;^UڽEKIORm-oJVi{OO},̪~&Ylx7^{}Pq|[ERsvqU_ȊI5)I9RR5ӓQ*$yv;|d@5g[^m4i f?l+_o.J b=ox-u 1%[xE9qϰ-;944K[=]Ͻ<6b-MD(M2/5 UF\>Eii=逤E[x SJ<}:6PaCX<z38TדeŏOe\O87*zUBv\lCمua /Qb; 룪Cc~cL/DD?t]wNKXAsڞkbQNBcIj5q>*p k@PL6-6LS|Y#Q|\M됼 c9xt$-(+4mMm4EУ"(Ӟ|+ܒ=ţͱ>rxkj^FQA"^Nc"/6ۿ3`4@}%TJ4ct,g8M o2*ZO$鋩1PaգewUV )YܙnSم<4kja=0Z !Ézi/e!-&.![dQ(͛뼬(^l#AANas<XgPAjQWyU`$'BX9Iigs/(>CEO1EQmj5@ilgJn;~S!Ϥ6>̴Yy5 B ZXw `;:L ڦ󶵖_ք)`<:[|(+\zSx1ݚYg |;k`b"Am;*P٦8G3QCyOԋ*>`Ȋ$: ]v>޺Ar9XOÃݻ[uZ٘7dŨRSr#Zh醋`yԡ5*zf<3:-W 4쬌zZQY` .h\i$Kduք=T \)ΊkCoĆ1*)'|&+$$B8N74g_Gy {N'Hslڥ% waB* R?Vt>%cdN{h:RBg+J,^Be l )򘫥dmd~NWOymz;]_t㦨ؙB3EGPEEݴutz 3>oˊȊUb0A[H4l80i7Z5|Ϙ-Tž𱉈YF dF"'A /㎤iGJ¨W|ٗ$q{lsҼv྿b.[Ѡ 7UTm?+I`cظ~ ֿ8g͒=Տz +~p2ON4~0Y=f[ehB;Vm}T`,UlmNd?8oU/~r?߰ɔoEV{A! JkߣBks33I ׿ˊ rTqVQaSu~s}+lN=q_ڻQjj?vcTɕ)/e?76]h8dRQE/A~'u*} U_bo,-6GŒ~Wmƾ Jb`LDFPRonїذ|^cT0*~Gѓn)=oʊ-TAZj~ <%4نOrVQF?cVk>B}|'?ޖD2>uR&߆lз^Vū6x kظi6advxm˥:H5]]^ѿ.*ƍI}1Ч.Y1fXZIYHEdY7EjKaO4i|e:jd^qg7麃aosl x=ec8ȨۄJ9 fi0}:EK,gc<$AlwF\J1*+۳jb[V[G%ѵة_ڷgƕ>s=hМv, \ϓuPG>PK{0pK=ٳysx7 "5`jrƤ4=j~v;J#L Lkb<^0{c, ثunj~mϷiQQ)QQwY]p9t,D| P3/coIgk볕|ַ!l^ZԵc<{,&GQ/ړl 0tPQmjio `)<qMT\8X4*YܱA([׳/r$jVyގr1.\&<:o՘"̕%'$}WT2/"o KK]ܞ/'Ŵuy֫--T4@gjaXt+p?{ZP/E.NR]8-Cnʣ<,򪮊4s9ex\jm 0D6s+슮g6A*Vp2W3T}6(^[/P1:s?O?zX^[AO֬>Tm`BG^VV5uGG ىe<(&*M' ؅ةi -*ؾ8X̢ج6j| ݇ EKDY7U%+!d2^VGx,2[{H/Zl;PE5&[TewP^ 6zODwۖ]J0\b"MҢZr׀CYYVe}/x ݣ%j:;áWۥ6PFCChtnmI.hA^paV,VP=X ,;lڕ]AԆT yv`UWjU9 .}EQ:?bh7=Ӡ} #r!unw)tR+Hb9`ߪH/Ҭ{>P/Q!py6cw?A׳{] "6 P3h{4'KqD IDAT{nFEOWJMev@l~&dKB 6w KwEޥG[DX dztY%6:Пx.BVo4iW0ZAƝnf\i(6%mcu ;('LYbGFxrT5ToZ VҜ hk"N:߫hȊȨ6Ks&^@!lyΪN&P=*c C;e]T<HoJ0.,us}2ZAĄLT#6 @6/loZ;st y2X6)]ڱA^$b>dA }0ʫJLoSbW%*r;-yWIEix8'5A}q1$[{\ jtZn.(S4*mo:hZ3G3lgy꒹ <`lj4t/ Y1/mA%κGDK-ѳD.JZK8U./_B-P1p;i b3:|.}BM $eR@%nʊ?/+9v EUėUceu4]e67uf\HoeEaNg#oPgFPCXT[]o:BTuQp(!`zoMTa 4dƞ!rV5Q 5Ćt)*$"x -6JϏt[5A/ T\Oy9I.k0J%<7:-%48"2ҲȊ4- -=/ u%Yy]3;*\Y~P%evP,:|-zݏJST a4?t6*T*9^91~ ySc2-4a0QFQz(n! IWVT9nMTQt̂ʊ~˽}YR8^eUeibCq8dcw7:FjBɤBJkPAZAr?Ҧr2FY-'3<_C:]|]T0?0A(lɹnU/P{`8+&DZu XZdyRd_V|*B@3j["n1/%,Z&N qrfp1!Շ `@uEOw4zvu%Ե9^{203vQqғ^Bʊ(vV2ۨ8J6Koّm;o6&J^/QR3^ϳImWQ``W]W%XzeCjr:r `bX6P!/%o qYV{?/AƢNLp*Ц MqnzT~ iy 9Snͅ/d'9TI4 >bGz :PyUە]TWڦAe!").nm,I\ p$=pmUkw]l- )h8ӕe sxpemu̷* TL\IjSC%w6@"K9!X,phߗ Kqv܇M8*]j> 軖Ai{FE-6DR^<XLEI +R0sn.*F|D@xL xY83=PwV&̣ m[("|9\ߍd:R~B5*2 I08Ֆ,RӅ;}Q]Ϊ>*]jW]s9Bo'hJn7M7,B0zj,*Leop*ۢ.*Ը$wN*n齅TX*tmXF*-7kǽ eG~oMP$&5=^!y''IP*\nx]MU0 +]j ]NS@;l8]6[ yMq*hɺyxvH2&\u-kc3fuw]J0{) #&39DŠ]UVE9;SSȊ竝u2*Z%JQ}X  ȫاuKn V%]YRӬO\o{ZuKaq8Xޖ\q`i<$^RVK(;5*Lr`%v4%_E1 $(zAn ijܠ澪38`9[&@&RcOA:p =sowA~Ɖ$xKBH+D%C\ھȆJy>7BM5lqDMUiTλ,k3 Qj^8F *tTͮ'&}[yR5/.l•iaZRhp_GN+c\_oF QޏRz|'_2Oor |;]X7](qOgW/mH׏(L >* ]ƭ8++,t* F@JĊGNS67jG繗d]aiv"mT =韶y}{ qxR]}9z˥lY}kI jHw䯊HQK~:qGS0=sGsNj .SiZ_=SN**W&/}[o^ 7Q1xeJ7p{H ].EG[-T+Js|,fI\j=vI̪t..;$D/PH 2E;JgEEWUwp͕H+RdXWdkUkE*(+|V}$KJaUzrA{0_⻥ ->жf hȆ+$Hk,xr]) >Kk;6%fK?0HFNOCT$a֠ ,*ik${DP mIS@6G>ޫniCB'Su]vY<Z<~=zC.]mƾ,RD0W@MEVbW:I= M+I"]T\x "Vlg+tQ 4Hp>TMs{QF0޶J9Ͻ겫*\j 6b:Uy;os*t oxƆd9+;,uy_*lAm)ҿAq;>DD=(@6Woɴ#̵sH4LKT<'_ /գC{ВL kSUm6awƴ t sJ`֔?} ;&yƫ$GȒJ Ue:Kbd{5.`?:m3̤l 3NsD,L''=ɩ9AG;ByXyn>9+0Wl}!="q/&կf2vKReNZ/벭|uVQN[&qN2zh;5bmsg5@= Xy\𾃈!k-x? Lm|&Gg&欪皅{q-0~M<=_,%1tDS#cug0_>R)_3W7Iڼ̽ B+FMΚe*|uY1?.-$V||4OՃMWf3,Ӭrcǥο[H͗V)"zr0iueʞK4D#sݠvFN1?kN+ރ|ys)G2φoljk)4Bys.H֧_תOrsF?=Y+XUs_߂`ܿ1ͻ e>_XA'jp2=g]~_yoP/fě+ä4Ũ4:c6) ]NJȐsyăw̙xS-Oދ~_ƽnOKٿMh3Zdza,ݛ1GTrN۫r| 8uzxx}G cM? @]|gRismN\a.hY`[g8K(:񑥫oW]cÏ!f~^q9^g;? ,|rǍhs ne^9mjq9CM>R+֕NVJ.SHz k6V+ ŏn%i%ڀȴK{ n>@_ԣ53 ojc!~Z oGQ>Sl+FTh>WenA=I Cb}iv $w> fL?ok[]oVYDxO}X{P_*6>Pa>^ͣjf(f"+wbD'Qhj?{۠x?|̂w2 ~5zњ=+^ i[ǦRౡA ]=67*6g~aeŨuu)*= hUQ1$[2 rcZ 8 PٞGMbJJr fa;D~Jt>uȶT5mjOh:''ywbi[JSgvs5#Uc^CcE"Ib5rËr=TV-}{np  )4}a;É~+7<`ipʼ{ss]Gc:%[eUGz]Y*9L#-.BBMq545K IDAT~2 8cxhu3ɹifFvlQieW?=UQ2ުUk8FGCGxվD_(M")ODX S66y|42ܳ˧=x~'PXIT{8T&MQ#jSL_9 \csG\}T(Ģ%deE'Vy i,rg=p5( $}JZÔnecM CjnjpAJƩ_偊\.!2eG城s=tEV Ygw5aLݲtYB>E*rp1N7]՚Hu93`%!S0CGERI]a]EfoTB|3a(XlAiu+|2{xNC k3JV*pЧ\~E+H|mSW膮F9eʤ'mSX:^!$(}iPxb3WRs­YZM%ZEyE偧c2e}&84Vux"Ȉ ./ڿ9GßKq+$5-dQr~jEUI&.A`K^I _x_L4.o6t`Xea;+BT͢cl[w*"i@_QvGS6DgO{9ӕq( P_/ ]fhZ"-.%5ۖi/⨮A9VzGcR[,1g.(`, {[Be_qIVM΄wU]zq`mJmM+hjDd5V6FM'idJ8Jk]R0]^DfBUcx2ვ}zP1~E%9eY0:klnkl. fuAW-"`;'{qR4$FUX3tb=a,v*\ײenx$V#T,: #(r+h"G`اD)\ [ )n6),8V52@t5j71O##+WG=7( şa xHu8ٍڱ~ȑ/-_F8f-Ã[Tu]^$f0hʰor*TEFuQiUDY03Sfo Cuo'=cSZxVTzP]S$lӜqj"?ʑjښYZ9DUgq*$sugG^+e cY2xq9, u A v[zչQ 2@˾d+airKCCWik_WP!z(; Ig| Z^b-˥aH睌TZ C0D;=b].d?m2Nc͡eVC9fӢ8@y@(2lR<{ɔ,lP>4W2Ye$mi"BqsNS vR,KLZ`̇lDdCcSEA;_CܡBrgn3?G^Amb m#N2F);¦c]vQa2ƅ_F-VD[)M"W*Fšgg%z'YYL<:J|vaju d:-Dj9obC$ MqX%~ (8=QKv |q> RI@>8 ..VSW Od lRTZ$?NrVi[EyoPCt8L^pn#mf\C @Ӵ#*iZQCœ S4*(-d*YтWLVĄ["Si#9Ćʜ^X@C@  G׫>ڼ,R#?_7 //%@IG<ǘrQTCEý;a+IVM~x&G0CEl@6- C/%%W@Ee*@^:*IQF#ϾHz͚S,wN~2g8.<^>*bm'C-=*>@ 5<ˠZ}=Skǽ ($>&(`[i$H(^V\4OG|e;=sڑ[<؛kɃnTd x0w4+t=Uv e-2 BX4Ryekn(9-۱PqIKp1VKG==GDE)O8ԫ:Qʏʱ~48HXX ?r/В{nL l8.lˁc.G.Emk6! 9>WxYn~~Z۲"O ݧlTɔߗ? :˘?d1eT<"dQ9 3TXT 3IT}3ﮱڒ Kk5h#]*$r> *Pdvzz}6`[D d~ܥy'K$]CE)AةCIg" ;릎\W .hV4kel7LQ!L' [ GϴT(Ӳ,}IuElq]PT(y[ 1[zD$J3)t~'\m( R N0E2gS9*q{-$KP$UERK%y k`d-ߦ0"#XW ue]w|w6w\e0SH,K;/'gƮ;Q1A\BiSbU`#lZM.a$"n 4C}0BKJ-*Ғ Hر x_m^^T\ő;@+|= #cn⎋<R8aǑԍIZ\!MOXXrA䍩VT/ndشLhlܠZEyrIS_#QAihӉ"*qS:",ZDE Cm.4WVcQf܅lG$FG\.#(e=^WOGsmG`@dbE [.%˦23k sTֵ8,-L X<$3V9B=(܌YT)XjRc8ⶫZr}u!_>nFJM"Ey]eX*(oтkQ/}<0S*XWX9D;0p'P }z RQk=^d9q,\˓AAYB &TpaFг\[|< 6KsfQPqH̑X0I&4'j|X̨WHXm3H&N [8,S\:ET9RxLq1/=>EغS:kkm 96lMT/ZV4x`lE]לAyDj3lSgHGro09aw* ~# xy:*L+2b&VR=@q,2f ݳu2mxYB'A2'1p!! @PA(9sTbM/aWKH]'MĢ*mb3YdT )I$Àb<_nos"gH .aq{鐰ʅų\̮yA&:ɶɔ z2!i `αWN_Ϋp]]q3NnT?u4,8pBU; *Fs몔+ zJ|1=`VD&LV*q*5nevRTyն\յpxv3ϕHrQ*OɊp$BSxuFJ#_ v ]T ڔ`ttKgl~d73uha@hvSH\Dn`G0@qc()aUv4BE<4TUs\Vmvup`[WxfO+GrKYaCUID.Mt JN* ZT2B?-[ztޜpm.bZ;GF /(ɃF;dGa)*J;IDv$c"}_{m)aA4*x5mll k0ce BbSKT0#«xV fxڷqVUAI,Q!W @!%gU{ JgZ^52ihee;nEV$n8$13UV@/!] —u:cC.[lS<Ċb$UuA%_0 hjF:3h]$p'Z>tW9u%X`Q},hGQTHaN7۝} h5YaAYγ$), a0s4ՐI`wYypo>"uS܆ 9A.:J3:x2I.%/ԵxY |כ 8" 8<%枍7Yq%?W' D~ ҾcV۳xyD.{\B?H#*,c8_GttKTA`1[DԗĝC|9X@u0v<#xtJ҄YOW YG [2P]vZ2G FPaVl'@;(j ?qBvdG$VJC1͔sCz)cX|FNY"yeeAWzQ Ǖ vf"syY} Ք+~(d5c*lBd9K{_enTrpKsE32ze3$q` +c&`=%9;un}(#G @4 ۚAbGVL2ǿrK&IҤ_zV}lW| DZ0K: ><@8Jެ&d >tk|oQA^*5W20m)u835*?9#z,@a2'#2EWHAFhFך稈ˮ_Eb#IrHCzᮗtIʾoH1&{@Mm @ˌku@`QN?.NL*CÕc=A~):; IDAT[jmKTj#!EƖTs=`"z6ح(d>+bh6华j؁<*X)s FY,1L|4I6#kn]Wnϖ9q0oBA8ca1gV to:SV9 I2XE䲞9tk3uhI-׿TuniāYXܡ6r,hPƑ.Ɨ 8$ُ //@ Vid㮠E$a8";51bJxb,q )dcEMAAv4e011Hy*?YH% qޱѐ+zNPɠ泌PFGzK_At5p*$`9"ddL6IӼ輳"@v{F#W!bL;HD&Rnni47 Jvp<ΜWthͬI 7V1DƖmMWJlt׍䍤!\ >w \@ҵ7;ܻvJ7 b -ɘO!$zNŻEЭQaUzy6S,90cʚ`2ʼn* @Co{ »H3,TLu`yUf6nMdQ晡*Zk0~%XM;c+%S{{bAX'gy"ƏΧ,]xN|@bg7=wLNA6WGXBN]ZAA4%In8Q{y`6< 57^ni2$9*%;9@a+&TO#؄GMl 1Ö9~#,D'  &}T"UL h69B j!vց|AK8ɾN",tgp sQwCJ״jaqZy\S0:lƱH&^!G¦L{*_?654%J' 4EZ{) *g7ئ-F؄aE+)Z3q}"Dj^Z A6P-ns* ȩ2Nm1)Ð17꫄f:#2QjĦnKYYY&ƎB{I6Wܺ_l|R4<d3 zJ8a-L*R ieTE^)~ 5+B")oFfT~ܜ;Y`'2zb`eu q95xP5M .ZPP}~%v=<46Q5:Q.6I}[OPF=BѲ {GU혞hA#²!NL߾oVE4DgT9Xֳd&sd5TL'0\B_Y!!S荼kc+^BZ\(OhZtV@D 3,3LoU b;aopj`ʢ9I x(g Bis_y`>w\Yχ*~eN2q>N9rc:idE?bYg5`\⤉F8eCo:yhwoQޱWL?g ̍a_@ūnLA#dG<-OHgOKj^ h4 2v.~K6a⽣All{ PU Ԝ̨il&d˹!f.**` X=CLΒ 61yv8h0FTXdCâZ0O޵q 8f{Nlb[DM Y>~M{Ceem\f}a1\7OJ2ˊ7,Ca׷yNV c5{T M;6L܇yoؗ8Y ^Z>6N5W>:YďW׺>R|ÞQpP}MZ!-y_?pwQy/ns<́2LllݗT6)}Kn g븯VLv>1GXn|pM(}Wiz=7f뛟[:e(+ѱ;Vced/ٽ }`͌/M@?A򾆕&T<0:|*;YT(L2+*Veӷc';9ɗ} }3*+f<%3gC&^f XioEEyHDx&T.T0-UTѷ9_CRo q=suT<IN`|7o𦻢dy2k܀-YOeEړͨOE[e1xяȊVVX^4 md` *Ǎrsגc>~@+VB/b31KʏzJl^oa O)ܑӸ0P2Du``aJU}O3 ˔Zn`y9)YVHT0Kq'g^mWز3^Ee71ܤ5VZN7{m;A3Z& a lkKŠ}Sƍ(Z\ < gh)jſagOs1Ie j3 Pq[L TXlPPor`B?Y?ߡS1b?ea}@.ZP#òqMI` ʌYHM#w* ۝I܁ MGDmBy9m6N=F")|N Ķ^LYzזI\/GyKKVifSyR 4ZYsN7XM sP/ l3\Q B3 AK+kpm=;x:d0HDK` f!mA%[b Q`ڼ T и~Nf.Oe$I/GlՇȝF0F2`~坨3]^>u"[?Er.P2e<f8jf p0:A4'.Tg˓4z{~bT : _,'P!/&PejX*Z4yP' -Lj uqa[\1ݚJ–Ub`C0.tfƄN )&GHj/˔_t8\g.T*7QLCt OsEjnBz b]grØI30k g8kFS&gǚ+E`r3=*+ fs-z/$EƗeL .<G8I@43"7 ;^ufA,4H"Y-Y1wxęB-ȝi-7*vǪ/Qo?qW> ,Ls^Pa xD5d5`S>i#MB3<)`ũ gI#*꘤I,GH!@gAXTa1ո#OImhi!˘M9[q'S5*lcOԱ TH+ig DZiCˍ -,2dx5 VP,WAϏ ^aZ&q]O5T o嬆A/4uXqD8s,LaLXv>Ju]׆iΗ$ 8J(>g֚zHz. *l1Oz f@\e~#5T z90'#PPVd2eL8FGfpuUT4i*9a -BxpOs_APWQ^my Wlp/6trGxtT9aGEsb-R1!Pδ{iTV$¶PhBiR! *xS {4nfTdLQA|̉ eh_407*06{"D>XK "dRX B_K>%pc3P (:ͱ,TH`n,:Q Hpt+B|b 362T趎Mք+a5$O$*bi{kPqj)X(f* 4e[VXPJpwЗ_!2g8w[nڌҟ̒1i T>LUJI\,iAHG PQ);`1>e}hpڣ"ɇ^V'ó̯ 8y,AoTۂ:K 3 S txy'癢I@D/Pd~g+p+0(3[P2ϠYQ H:PajH9\ wM YBW{vB]Culk3 wN7˥2HO$(k Χ{&.暽eBU2*c`pO=PaSoa1>I<%*BpD.Ht4gE%QΙMl#ξQ0*Lf$hɠۖyP!1"*h ~@I X(c-fUQQ@.N4&>U,z;*XxT*l!1&%#&" R$z纒J6 ĤB@,ys3L0/a,Q*q`aAĈr4Υ4"4 bE6ZQDBQjx%9菳 `*)ey*AhBQqCU7AYPF" = h:0Y%Ds7?1 ~)zTTea]O|iZÒ$*[hscV!SZuk*'P!?YX (dvb ʯÏeiI8LmMGQ\ B>drU8QAa |13R7QUQ׵ WP׋8*O]c gOwT"`V",X}9B) ɧAUT`eP!dT#c~v+lDE9sǼfs`l"*f̨PNP1,7۩A tAqLt]YTIL@&ex:S`1h"8*έOmD쨸FUQ65I5i4a8xN NTQ)N&rWqY7VTeZIx8{u|f@ViLb:^Wv t32l3 5I-ݨ௳H8t9G*XZl}cgt'$zu 2az wBa"M )iHs%.P=|[ d* 'LlW0񱷠Yyv_} 4GӅQ.tH`1łPX|OP?Z.13* (Hц 0-U4т$b:e3K k]Ca5lU=NM0R$7V{ &WBV.P2!XP|\QeaL%_<Ɋ Dis`QؓB{ܙɋNAQVS˨ cUT\JuIp2Y!*"3x8=Dd4CD!nDE*gZTCvVQ79+,imS*̮N6c˫,H5d.P]{j}Ng 7 GARa``_,L5:fT$;:1J1J%H!,"!mqniGעܛgBE^OOa!|} *v9WZ#` d O>B$ł+E^`|;$TDFz7:uxwB!?a`Nv6VFT2PI * '7^Dn"Ӗ[tڝ " 0/Q!y"u2GC. 'P,Uk_4?:V}ZLr:6)oI0~vgCBpD&4Xu=!n=*0VxUTlG+B04ȉ_s̷p]/Qe=@ET,+'19cA$!ɞE. } "#Ut2r1;SMA"I+geP-5 b ;yqڳ9 \ ,@O{_8na 6T csa <̢ Hƥdd=TiCaD*: be'TrK(TjHӡh{b/A!!X,p y+~K"5iֱʰʕ'멻mA"td.F1\%΂`K+5TsyX.$cBاĝjҡ*$ Q)TP9Z(:*XmwμQE1IUB˨ '`vN gL.1΂x) R~~K%r p-2*Pq0!Qi@bFTȗ|<ZX^Qܓ PE$<5AV0&tv bLz`PAX SfQ, PTN:}JuG2PW9TP]a TS+XZ-G˔2]ς`;*z&P$,|rR`TC{=B6CDCQuҢ;S]WC!}:i$Wκ;{{.ϧH(JϢBT~KY)Sol /PJ3ş }Dz.KwV' ք֮9 VR vK22p̫X>woN 6I)^W`ޅp?Lz‹i}ݩB;fk s<}7.|%*7\i^)hڮk QUnzv?C(K8 _T'ewkZ2Q+i#EnQ! ݮk5Ka/P,;+ *N[bOK2*P}Gl@ӪPv6BZ2AלemwoNtB:@~[QVPO}Ĥ,Qa(bG ]v PtI6K%+CȘ<;*Ի~ʴ2?YA#juxj:QE=10_l褶U\bYkg?$ˢ5_yN4T"qߝڠmB=^r:aU$0X}nUSǺ,|Ej E܅`r(N3u*ʦ )8Q]\+L, $KaA:,WɂmΏMoHԀq=Ig`.ej)Wބ/SKS!D)ܫ8y LB.0ع@}긳+%玞T7@GKVh؝Mq9.n;ָ/XcGx؏ n9r,wV$AyݍmL=3{#.frYk Y;aO$u(Uʭ\̲'\bt>3\,_Aq3pRqAԩɽ/|ȵL=dYXy_ktSkU^aȰ19M|.P$W/ ]·bdUg!׾aBkGV(_nRۼԃ|n 5-=5@kqu YXtf\6szkr8-d;P!)B>?Aw'>Wv; w{ǾnnY@*򤷷ۅf[@q_ņc[cFcح>p?{y37]=h(40oP# oSQ;[`|_ϲ0.j?:'Δo9h'Z3fx&5jeN#'W ΧO~xO e z9AI}mش}^='zJ^KN j]p~(f#-(i0/ذAp0G )M{hg=9b t}KJLܾo'm]>k LQ/_osƎ f ?raҽ\\n=wc1Y&/GAS'|ڙ% exW7!}-+.8L!<氙m[v{LZ"x/**` %׌lKOiP12M2y#*86^8/$)}uTQV|ZoK˔opu܂1-fX&? CS` fY!LlӆH kϟVD`oE +$[ٻyEƟ&xJړhȸ(^`O& 2Ʉۈ ~Cb;[!VBfsVTAWl%vƍͳjX0K]f0Ϛ?S7b%uIUvm#}&7alA?_݈ emy6GBoȹ>5D9ߎ * oj8g o%_Yp/[wجBt-KwxPDTmC/wj7*$6$& GvKn'#} 䋊*TYq}jPaH4o;۔E>e->HTXB͸ az>KThըlc/  C|;*01)ϢѥR{4Mӑԋߘ]qOD,*WgFjsgF 8lFH4Z2E63x̊ <wy\ l'^, PQgQl>AIZ 7 xjOAXV 1U] {O7gc''BW|*$ݼMVP"/u;F3`4 b8rNK> |5GA>tDᡥ77\EeS0;?\CTz~Q`y[`l2֎ F&7osQ>Z5aul Z `uQ)^ v^,a\|:ξl$L! ~Mq|DŷJcQk=@ڎ Lөp\s:C0 ؿ=;3w5!1/两 `ؚ5Tj}-5D̊/*>BZʧsF1aAEҺEH|&QEgBJ3EN NyNo_Adtrr/H0kgŦ|efY|h0֏'P!<AޙxQQx9մJ 2K#/*>O--8};.+p5"]3L m5۔ft TEA Fine Manual

TEA Fine Manual


Table of content



Introduction
Interface
File manager
Dates panel
File
Calendar
Edit
Markup
Search
Functions
Functions - Speech
Functions - Spell checking
IDE
Run
Nav
Fm
View
Options tab
Options - Keyboard
On autosaving
Secret power
Notes
Other projects by the author
Support the development

Introduction

TEA has a long development history. At the autumn of the year 2000, there was first implementation of TEA, written in Object Pascal using Borland Delphi. Since then, TEA has been many times rewritten from zero (Pascal - C/Gtk - C++/Qt) and opened its source. The initial TEA UI was inspired by Impulse Tracker and Sound Forge. Later, the conception of sliding panels and tabs has been implemented.

Now TEA has THREE branches. This, TEA-Qt, is the modern one. There also outdated TEA-GTK (Gtk 2) and TEA-GTK (GTK 3), the buggy and forgotten one. So use TEA-Qt.

TEA home is and tea.ourproject.org. Happy Arch/Manjaro/etc. users can use my "tea-qt-git" package from AUR (aur.archlinux.org/packages/tea-qt-git). Also, welcome to github.com/psemiletov/tea-qt - always up to date, but can be unstable.

TEA name means nothing. It's not "Text Editor Of Atomic Era" or somethink that you may read at TEA reviews. Initially, TEA = "Text Editing and Authoring". Then, TEA was associated with a common drink (I like to drink the tea). But for now, TEA is just TEA or ТИА.

Normally, TEA holds its configuration data and other support files at $HOME/.config/tea (or, under Window, at C:\Documents and Settings\username\tea). But if you want to start TEA from the USB stick in the portable mode, TEA must read/write configuration data into the directory with the TEA binary. To start TEA in the portable mode, use "--p" command line option:

tea --p

As you know, TEA under *NIX is a single binary. Under Windows TEA installation directory contains all what you need to start TEA portable. Please note, that your NORMAL-MODE settings are stills exists and available anytime when you start TEA WITHOUT the "--p" key. TEA preferences of portable and normal modes are each other independent and exists in parallel.


Interface

TEA has two interface mode - the Classical one and the new, so called Docked mode. You can switch between them at Options - Interface - UI mode. At the docked mode, you can tear off and/or move the Logmemo and FIF (see below). The default UI is the Classical. I'm personally use the Classical.

From the top to bottom, there are:

1. The main menu. Note that you can tear submenus off and place them somewhere over the desktop. Menu items are highly depended on the current task or even editor mode. Some menus can be visible or hidden according to the task (editing, file browsing, etc.).

2. The toolbars area. Actually, toolbars can be parked at the any side of TEA window. Positions are saved between sessions. To turn the toolbar on and off, use the context menu for the toolbar (right click on the toolbar). If no toolbar is visible, do the right click on the menu bar.

3. The tabbed area. It contains 5 main tabs - editor, files, options, , and manual. Editor - that where you see opened documents. Files - a built-in file manager. Options - here you can change TEA preferences. All changes will be applied immediately. Dates - the simple calendar with the organizer functions. Manual - the built-in User manual. This, the excellent one!

4. Logmemo. It is a notification area for all warnings, information messages and the results of some text-processing functions.

5. The Famous input field (FIF). An input text field to put here values for text-processing functions. Also it is used for Find/Replace (read about a Search menu for details). FIF has three buttons - Find, Find previous and Find next. FIF is used also for a search in the TEA manual, menu items list (at the hotkeys customization page) and even the built-in file manager.

6. The status bar. You can setup the status bar's content via the preferences Interface page.


File manager

The TEA's file manager is hidden under the files tab. It is not an additional bloatware tool, but an essential part of the editor. It replaces the standard File save/open dialogs (by the way, you can bring them back using Options > Common > Use traditional File Save/Open dialogs checkbox).

Let's see on the shining force of TEA's file manager.

The directory bar is located on the top. There is an entry with the current directory path, and a nice toolbar with following buttons:

Go - navigates to the directory with the path from the text entry.

Home - teleports you to your cozy home directory.

Refresh - refreshes a files list to your pleasure.

File operations button with submenu. It contains some utility actions such as Create new directory, Rename and Delete file. Please note that TEA does not delete the directories.

The right panel of the file manager.

At the top you see the file name field. Here you can give the name for a file to open or save it. To save the current file under some name, just put the filename into the Name text entry and press the Save as button. To open a file, do the double click on the file name at the files list, or put the file name into the Name entry and press the Open button. You can use the Enter key inside of this input field. The action is related to the menu item (File open or Save as) that you used to open the file manager tab.

You can also select several files (Ctrl-click or INSERT key) and then press the Open button.

The selected file is saved or opened with the encoding that is selected at the character set list below from the Name entry.

Bookmarks list. You can easily add and remove the directory bookmarks using the "+" and "-" buttons. The "+" button adds a bookmark to the file system tree's current directory. The "-" button removes the selected bookmark. To activate the bookmark and navigate to the respective directory, double click on the bookmark.

There are some special bookmarks at the top of the list: snippets, templates, scripts. You can't remove them. They point to the TEA special directories for user's items such as templates, snippets, etc.

You can enable the Options > Common > Automatic preview images at file manager option to show a small preview window near the file manager. Also, when the cursor is located on the image file, you can use Open at cursor (F2) function to view the image at the full-size in another built-in image viewer (see the Open at cursor description).


Dates panel

As you can notice, TEA has the built-in calendar-organizer under the dates tab of the main window. By double-clicking on the days, you create or open the file-per-day text documents. When you close such file, its content will be saved automatically. To remove the day record, use Calendar > Remove day record.

At the text you can specify the time signature for the alarm event. This signature has the format [hh:mm] in the 24 hour day. All text after such signature and before the next one will be treated as the alarm message. For example:

[20:00] go to the theater
[23:00] howl at the moon

For sure, you need to have TEA running to see the alarm message!

Single click on the date cell shows the day record (if any) at the logmemo area.

At the Options page, you can set the Start week on Sunday option to the "on". The calendar widget can work in two modes - the normal and the lunar one. Read the Calendar section for details.


File

New. Impossible! It's a kind of magic. This menu item creates a new blank document where you can type any words you want. For example, type the word "hello". It's a very cheerful word. That what we usually say to all good people around. Hello! And your world becomes brighter.

Open - for sure, this menu item helps you to open any file that you already stored at your hard disk.

If you are using the build-in file manager, read the File manager chapter. Otherwise, read the words of true about the File open dialog window.

TEA can open all plain text files, and also ODT (OpenDocument Text), KWD (old KWord's format), FB2, Epub, DOCX, SXW (old OOo/StarOffice format), ABW (Abiword) formats. But only the plain text files can be saved with TEA. About other mentioned formats - TEA just can extract text from them, trying to preserve the basic layout (indents, paragraphs). When compiled with PDF and DVJU support, TEA extracts text from them too. When use TEA's Search in files, TEA looks inside of all files of these formats.

Save - if your file is already saved, this menu item saves it again. That is how it works. But, if a file is not saved, you shall see a surprise! A File manager (if the traditional open/save dialogs are turned off at Options > Common > Use traditional File Save/Open dialogs). There, put the file name into the Name entry and press the Save as button.

Last closed file - opens a last closed file. This sentence is a greater paradox of XXI century CE.

Open at cursor, or F2 as a shortcut - this feature transforms a user into the so-called "F2 addict". Strange mind and body transformations can happen, so you are warned!

Put the cursor at the file name in the href-link or LaTeX includegraphics, input, include etc. Press F2. See THAT miracle? TEA opens a file with the file name that was pointed. If this file is already opened, its tab will be focused. If the file is an image, TEA show it in the build-in image browser. But, what if you don't work with the [X]HTML code, but use a real file names in your text and want to open them? TEA can handle that - just select the file name (not just put a cursor on it) and press F2.

What is the difference? TEA can open a "file under cursor" when the file name is enclosed with quotes. As in HTML or most of the code sources. In other cases, you need to select file name manually with Shift-cursor keys or using the mouse.

And a bit more - using Open at cursor you can navigate through local href-links.

You can also use Open at cursor at the built-in file manager to open the image file with the preview window.

Also, you can open file at cursor with any external program. Read about the "Run" menu for details.

Crapbook - opens a special file where you can put some notes, pieces of a text, etc. The crapbook file will be saved automatically when you close it. If the Crapbook file is opened and you exiting TEA, the content of a Crapbook is saved too.

Notes - a kind of crapbook, but connected to the current file. Useful to put the notes related to the current file's content. The .notes file is autosaved on exit.

Save as different > Save .bak - saves a content of the current document with name, where the "bak" extension added.

Save as different > Save timestamped version - saves a content of the current document with name, where the timestamp is added. So you shall know the date and time related to the saved version. Note about timestamp format. Is is yyyy-MM-dd-hh-mm-ss. So the first comes a year, then a month, then days, hours, minutes and seconds.

Recent files. From this submenu you can open one of the last dozen opened files. TEA remembers the name, the encoding and the current position of the file when you close the document, so at the next time TEA already know the charset and the position, where to start an editing.

Templates - to create a new file from the template, select a template from this menu. To create a new template, save a text file into the TEA templates directory. It is available from templates bookmark at the TEA's file manager (or at the left sidebar at File save dialog). And vice versa, to open the template use the file manager's templates bookmark again. The character set of templates is always UTF-8. TEA sets it automatically even if you choose another encoding.

To delete or rename the template, use the file manager or File save/open dialogs.

Sessions - at this menu sessions are available. The session is a simple list of opened files. To load them all, just choose a session name from this menu. To save a session, put the name of the session into the Famous input field, then use File > Save different > Save session. At the preferences Common page you can check the Restore the last session on start-up option. If this option is turned on, TEA will save the session on exit (under the name def-session-777), and then load this session when starting.

Bookmarks - this menu is similar to Recent files, but the files list is updating by you, not by TEA. To add a current file to this list, use Edit bookmarks > Add to bookmarks function. If the bookmarks list already contains the adding file, its record will be updated with new position and charset values. To delete the bookmark, open the bookmarks list from the File > Configs menu, delete the needed line and save the list in a usual way. The wrong bookmark can be temporarily disabled with the "#" prefix. To search wrong paths and disable such bookmarks automatically, use Edit bookmarks > Find obsolete paths function.

File actions submenu. From this menu you can do some magical things. Set UNIX end of line, Set Windows end of line, Set traditional Mac end of line - those functions allow you to override the file's end of line (EOL). When you save the file, the chosen EOL will be applied. To check the file's EOL, select a file at the built-in file manager and use Fm > File information > Full info.

Do not add to recent - temporarily disable the recent files list update. This function is useful if you need to open many files and do not want to overwrite your recent files list. So use "do not add" before close "unwanted" files, work with them, close, then switch "do not add" off to bring the normal recent list functionality.

Exit - closes TEA. To reduce the annoyance level, TEA does not ask to confirm it. And another thing related to confirmations - TEA automatically proposes to save the modified existing files only. I.e. if you close (with TEA or by Ctrl-W) the modified new, no-named file, TEA silently will close it.


Calendar

This menu is available only when the todo panel is active.

Moon mode on/off - switch calendar widget to the moon/normal modes. In the first one, the moon phases are shown. Also you'll see the moon day number after the slash (before the Gregorian day number). I.e. the format is: normal day number / moon day number.

Some words about the calculation of moon day. First of all, you need to select the right Earth hemisphere at Options - Common - Northern hemisphere (on/off). The other important thing is the algorithm (Options - Common - Moon phase algorithm). The speed and precision vary, try for yourself. There are several algorithms were been used. Some of them are based on Ben Danglish's article at www.ben-daglish.net/moon.shtml, another one on the Alexander Leushkanov's article - www.astrogalaxy.ru/275.html.

Due to some technical source, the image of moon phase for days 12 and 13 are the same.

Add or subtract years/months/days - allows one to navigate through the calendar back and forward on N years, or months, or days. For example, you need to see the day +6 after the current one. Put "6" into the Famous Input Field, and then use Add or subtract > days. If you want to move backward, put the negative value into the FIF, i.e. "-6" instead of "6".

Number of days between two dates - calculates the number of days between two dates. Select the first date at the Calendar and use Mark first date menu item. Select the last date at the Calendar and use Mark last date. Use Number of days between two dates functions to measure the difference between the first and second dates.

Calculate moon days between dates - creates a list with moon days between two dates (Mark first/last date).


Edit

About the rectangular or block selection. TEA has such limited ability that does not fit TEA's architecture well, and text-processing functions cannot be used with block selections. All that you can do with them it is to select using Block start and Block end, and then Copy block, Cut block and Paste block.

Some notes about Indent/Un-indent functions. There are two hardcoded keyboard shortcuts are assigned to this functions - Tab and Shift-Tab. If no text selected, Indent (Tab) inserts a tab character or a space (spaces). Such behavior is depended from Options > Interface > Use spaces instead of tabs and Tab width in spaces options.

If Use spaces instead of tabs is checked on, the editor will insert (by Tab key) the number of spaces that defined at Tab width in spaces. Otherwise, TEA inserts a single tab.

TEA also has the Automatic indent option, disabled by default.

Indent by first line - this function indents all selected lines with the same indent as the first lines of selection indent.

Storage files - you can use some file as a "storage" file to copy text directly to it from different sources. Set as storage file chooses the current opened file as the storage file. Then, if you want copy and paste some text into that file, just use Copy to storage file. Also you can capture the system's clipboard text and put it to the storage using Start/stop capture clipboard to storage file. You may have just one storage file per session. When you close the storage file, all further pasting to it will be disabled until you not set a storage file again.

To control the text that inserts to the storage you can use the template file called cliptpl.txt at the TEA config directory. Just create such file with some macros, for example:

---
%date
%time
%s
---
Here is: %s - the text from the clipboard, %date - current date, %time - current time.

Markup

The functions from this menu are mainly working depending of the selected markup Mode. There are several modes available - HTML, XHTML, LaTeX, Wikitext, Markdown, Lout and DocBook. When you select some mode from the Mode menu, you set the markup mode for the current document. So different documents have different, separated modes. It allows one to edit one file as HTML and another one as DocBook XML with the same menu item. So, menu items act differently according to the document's markup mode. To use Wikitext mode, set this mode manually from the menu Mode, or use .wiki as the filename extension.

You can select a text and use some function, for example the "Paragraph". If you don't select any text, TEA just insert tags at the cursor position.

Beside the tag-related functions, Markup menu has a set of [X]HTML tools.

[X]HTML tools - Rename selected file - renames the file and changes the selected text. For example, you have (in your HTML or LaTeX document) a link to some file. You want to rename that file. Select its file name at the text, then put into the FIF a new file name (without the full path, just the name.ext), and use this function. Voila!

[X]HTML tools > Document weight - calculates a total document weight including all SRC-staff (images, flashes etc). Look for the result at the Logmemo.

[X]HTML tools > Convert tags to entities - if you want to show some HTML-tagged code in your [X]HTML-document, select your code and use this function. You'll get, for example:

&lt;b&gt;demo&lt;/b&gt;

[X]HTML tools > Text to HTML - this function converts a plain text into the nice HTML/XHTML-formatted code using CSS.

[X]HTML tools > Preview selected color - previews the selected (within the text) color at the FIF. The color can be in the form of the hex value or the named color (darkBlue, red, white, etc).

Antispam e-mail - makes a selected mailto-link possibly invisible to those damned spammer e-mail harvesters, by converting an address into integer-coded entities.



Search

TEA has no dialog windows for find/replace functions. TEA uses the Famous input field (FIF) and menu-driven interface.

To find a substring within the text, put your substring into FIF (Use Ctrl-F to focus it) and press Enter. Then you can use Find next or Find previous functions. There are also three buttons near the FIF. They are: Find, Find previous and Find next. On search, TEA starts from the cursor position. You can set Whole words and Case sensitive options by checking the respective menu items.

To replace the selected (i.e. text that been found) text with a new one, put the new value to the FIF and use the Replace with function. Thus, you can walk through the text using Find next and apply Replace with when you need.

To replace all occurrences of a substring in the selected text, put a special command into the FIF:

old value~new value

So, the "~" is the delimiter. For example, you want to replace all occurrences of the word "cat" to the "mouse". The command will be:

cat~mouse

So put such command to the FIF and apply the Replace all function.

If you want to delete some substring from the text, use the substring~ formula. For example, to remove all occurrences of the word "weapon" from the text you can use "weapon~".

While search or replace you can use regular expressions (TEA via QT uses PCRE2 library "that implement regular expression pattern matching using the same syntax and semantics as Perl 5"). To do so, turn on the Regexp mode option (below the Whole words). For example, to convert HTML italic tag into the TeX tag you can use the following formulae:

<i>([^<]*)</i>~\emph{\1}

Please note that \1 acts like a content of the captured (with "parentheses") part of the source string. To have the access to the second such part, use \2, \3 for the third, etc.

Another example - how to find all numbers at the text? Use the (\d+) regexp at the FIF.

Don't forget to disable the Regexp mode when you need to do a usual searching. Also the Regexp mode overrides the Case insensitive mode, i.e. with Regexp mode the Case sensitive is ON by default.

Fuzzy mode - enables very basic "fuzzy search" support. For example, you need to find some word, but don't remember how exactly it is written. So in fuzzy mode, TEA can find similar word (if they have the same length, i.e "fender", "lender", etc). The factor of similarity can be defined at Options - Functions - Miscellaneous - Fuzzy search factor.

In the file manager mode, "Replace all" works with all selected files and the charset from the charset selector of the file manager panel.

At the last, Replace all in opened files works as the Replace all, but in the scope of all opened files.

Search in files - searches the text given at FIF within all text files, starting from the current directory at the built-in file manager (the Files tab), and deeper into the subdirectories. For a text encoding, TEA uses UTF-8.

Actually, TEA searches not in the ALL files. For the central processing unit, there is no difference between text files or mp3, or images. Programs must know the difference, but how? And why it is so important to identify the text file? If the program cannot do that, it will search the text in all files - images, sounds, videos, etc.. Too much time! Thus, TEA must know the difference. So TEA presumes that text files are the files with extensions those TEA recognizes for syntax highlighting. Also, TEA knows that text files are txt, odt, kwd, docx and other formats those TEA support. Finally, TEA know about some definitely text files those have names such as README, ChangeLog, etc.

So, TEA cannot search in all other files and currently there is no way to define custom file-name mask or something like that.

Mark all found/Unmark - colorize or decolorize all search results at the text. This function can produce incorrect text color settings when using View - Darker, until you restart TEA or open and work with another file.


Functions

To use text-processing functions, you need to select some text first. Some functions (such as Text statistics) can work with a whole text if no fragment is selected. Also there are some special cases when TEA can treat the current word as a selection when there is no text is actually selected. For example, it is a behavior of UPCASE and lowcase functions. But such behavior is a rare exception from the rule.

Some functions take the parameter that you must place into the Famous Input Field (FIF, the input entry at the bottom of the main window). For example, the function Remove lines < N size gets N value from the Famous input field, so first you type the value, then call a function. There are no default values.

Functions > Repeat last - repeat the last used function. Actually, it works with almost all menu items, not just at the Functions menu.

Functions > Capitalize sentences - make all selected sensences with the upper case first letter of the first word. Useful for auto-generated subtitles from Youtube, where you manually add ".", "!" and "?", and then apply this function.

Functions > Tools > Scale image - scales the image. In the text, select the image file name or set the cursor on it. Put into the FIF parameters of the scaling. Apply this function.

The format of parameters is: filename~new size

Filename is the output filename. It can be "hardcoded" (i.e. something like "out.jpg") or contain macros: %filename (filename + extension), %basename (the file name without extension), %ext (extension). Thus, the full source file name is %basename.%ext. The full path with the filename is the macro %s. If you don't want to overwrite the source file, you can use some prefix or suffix to alter the file name:

foo-%basename-bar.%ext~1024

In this example, the output image file name shall be prefixed with "foo-", and suffixed "with -bar".

The second parameter is a new size. It can be in pixels or percents (just add "%" after the value):

foo-%basename.%ext~1024

foo-%basename.%ext~50%

Cells submenu is used for all table-like data processing. It can be LaTeX tables, CSV-data, etc. In all cases you need to define (at FIF) the column separator to tell TEA how to parse the strings.

Functions > Cells > Sort table by column ABC - sorts the text table by the column with the ABC order.

Format string for FIF: separator~column number

Columns range starts from 0, i.e. the first columns is 0, the second is 1, etc.

For example, we have LaTeX table with 4 rows, 2 columns:

dog&cat
lamb&cow
snake&wombat
wolf&tiger

Now we want to sort it by the second (1) column (where the "cat", "cow", etc.). Select the text, put "&~1" into the FIF, and call this function. The table will be sorted by the second column.

It is good to process the table without line-breaks (such as "\\" in LaTeX), and then add line-breaks to the result using the "Apply to each line" function with FIF value "%s\\".

Functions > Cells > Swap cells - swaps the cells of the table by columns.

FIF format string: separator~column1~column2

For example (with the previous sample table) we want to swap column 0 with 1 - the column with "cat" must be before "dog" column. Select the text, put into the FIF "&~0~1", and call Swap cells.

Functions > Cells > Delete by column - delete column by the given number (0..n).

FIF format: separator~column number

0 - first column, 1 - second, etc.

Functions > Cells > Copy by column[s] - copy columns to the clipboard.

FIF format: separator~column 1[~column 2]

If the column2 is not given, the column1 will be copied, else - from column1 to column 2.

Functions > Analysis > Text statistics - outputs text statistics for the whole text or the selection into the Logmemo.

Functions > Analysis > Words lengths - how much words with length from 1 to 32.

Functions > Analysis > UNITAZ - the UNIversal Text AnalyZer that computes frequencies of words and gives some other related information.

Functions > Analysis > Count the substring - the number of occurrences of the substring (put it to the Famous Input Field) in the document. See the result at the logmemo. The "Search - Case sensitive" is used as an option.

Functions > Analysis > Extract words - extracts all word from the document and place them into the newly created one.

Functions > Tables

In TEA, the tables is a something like a set of rules for the text replacing. At the first, you need to create the file with a table (key=value format). Here is an example:

cat=dog
mew=bow-wow

Then save this file as a replacement table file at the special TEA directory called tables. It is available at the file manager's bookmarks panel or file save dialog favourities. After you save the table under some name, it shows at the Functions > Tables menu.

Now try to apply this table. Create a new file and write some text like this: The cat say: "mew". Select this text, then select your table from the menu and see what will happen. As a result we have a new sentence: The dog say: "bow-wow". So the tables is a way to do a multiple replacement.

If the Regexp mode at the Search options is turned on, the table will use the keys as the Perl5 regexps (via PCRE2 library).

In the file manager mode, tables work with all selected files and the charset from the charset selector of the file manager panel.

Functions > Snippets

The snippet is the piece of code, which you can insert into the text. TEA keeps each snippet as the single file in the $HOME/.tea/snippets directory. The file name of a snippet is the menu item in Snippets-menu, i.e. the content of Snippets menu is the list of files from $HOME/.tea/snippets. To create a new snippet, you need:

1. Write some text.
2. File > Save As. Click the "snippets" bookmark to open a snippets directory, then save your file as a usual one. Note: Do not save a snippet under the name of any other TEA menu item! Menu item names should not duplicates, otherwise TEA cannot set keyboard shortcuts properly. It is important when you give a name for the snippet, or the script, template, session name, etc.
3. Enjoy :)

You can create a snippet that uses a text selection in some way. For example, you want to make a snippet which encloses the selected text into some HTML-tags. The %s macro represents a text selection. Here is the example of such snippet:

<a href="%s">%s</a>

When this snippet will be applied, %s will be replaced with the selected text. If there no text is selected, snippet content will be inserted into the text (but without the "%s" macro).

To open a snippet for the editing, use snippets directory bookmark at the TEA file manager or File save/open dialog. From there you can also rename or delete snippets (use the context menu - the right click - in File save/open dialogs).

Functions > Scripts

Scripts from the user's point of view

With TEA you can use scripts to process a selected text, just as you use a built-in TEA functions. If the script can handle some parameters, put them into the Famous input field.

TEA can work with scripts those written in Python, Perl, Ruby, Lua, Bash (Sh), 2/REXX, and Windows batch format. To install a script, just put it to $HOME/.config/tea/scripts (you can use "scripts" shortcut at the Save As dialog to save your file as a TEA script). TEA will "see" the newly installed scripts after restart or when you save the script from TEA. The scripts management stuff is similar to snippets.

Scripts from the developer's point of view

How to write a script for TEA? It's quite simple. But the first thing that you must to know it is how TEA gives a text to the script and how TEA takes a processed text back to replace the selection with it.

TEA runs each script with one or two parameters. Both of them are file names. The first file contains the text (UTF-8). This text is a selected text that TEA gets in the current document. And the second file (if exists) contains a text (UTF-8) from the Famous input field. So your script can read the first file to obtain the text data, and read the second file to get some optional parameters for your script.

Be careful with the text handling - TEA internally operates with UTF-8 encoding, so all text processing at the script must be UTF-8-safe. OK, now let's see how a script can return a processed text. Just write that processed text to the first file again, i.e. to the file, which file name you take from the first parameter of your script.

Below these lines you shall see an example of UTF8-safe Python-script that takes a text, swaps the case, and returns the processed text back to the TEA.

import sys
import string
import codecs
f = codecs.open(sys.argv[1], "r", "utf-8" )
u = f.read()
f.close
u = u.swapcase()
f = codecs.open(sys.argv[1], "w+", "utf-8" )
f.write (u)
f.close

sys.argv[1] contains a name of file with the text from TEA. We read the whole content of that file, then we process this content, and then we write it back to the sys.argv[1]-file. Voila! Please note how to use the codecs.

And another example - the "inline" calculator. Here we don't use any codecs because we work with a numerical data:

import sys
f = file (sys.argv[1], 'r')
s = f.read()
f.close
t = eval (s)
f = file (sys.argv[1], 'w+')
f.write (str (t))
f.close

But what if we need to get some additional user's parameters to the script? They are available as a string, and you can read it from the file that named at sys.argv[2] (in Python). At Bash-script, use $1 to get the first parameter, and $2 for the second. Summing this all, the second parameter is a file which contains a text from the Famous input field. Note that Ruby scripts take the first parameter in ARGV[0], and the second in ARGV[1].

Some notes about the BASH-scripts and sed/ed/etc stuff. The trick is to use a temporal file. First copy the $1 into some temp. file, then process this temporal file and put the result back into the $1. Here is an example (replacing all "dog" with "cat") of such script:

cp $1 /tmp/xyz.xyz
sed 's/dog/cat/g' /tmp/xyz.xyz > $1

If you wish to contribute some scripts to the TEA site scripts repository, please consider contributing your script with a public domain status. Or at least with the Free license. There is also will be useful to put the credits and the description somewhere in the comments in your script.

Sort > Sort case sensitively - sorts selected lines alphabetically and case sensitively.

Sort > Sort case sensitively, with separator - sorts substring those separated by the delimiter. Put the delimiter into the FIF, then use this function on the text. Thus, you can sort "three|four|one|two" with the "|" as the delimiter.

Sort > Sort by length - sorts selected lines by the size. Provide the size at FIF.

Sort > Flip a list - reverses lines order. Was:

Dog
Cat
Cow

Will be:

Cow
Cat
Dog

Sort > Flip a list with separator - reverses substrings order separated by the delimiter at FIF.

Filter > Remove duplicates - useful when the string list has duplicated elements. Any duplicated item AFTER the original one will be removed. Can be slow on slow machines.

Filter > Remove empty lines - no empty lines will surrender! Empty lines are useless.

Filter > Remove lines < N size - remove lines with the length less than N. N is the number of characters. Write the number into the Famous input field.

Filter > Remove lines > N size - acts like the previous function, but removes all strings those larger than N characters.

Filter > by repetitions - leaves strings with the pattern, where "0" is any character, and "1" is the same character.

For example, the pattern for "kitten" will be "001100", where "11" is for "tt".

Filter > Filter with regexp - filter a list using the regular expression. For more info, see www.regular-expressions.info. However, here is a brief example. Let's presume you have a list:

hello.doc
example.txt
nature.jpeg
mytext.txt

And you want to filter all items with "txt"-extension. So the regexp (regular expression) for this will be:

.*\.txt$

Put it into the Famous input field, select the text and apply a function. Voila!

Filter > Remove after delimiter at each line. Removes the substring after the delimiter at each selected line. You need to put the delimiter into the FIF. For example, the delimiter is ")", and the selected text is:

1.) Nirvana
2.) Grajdanskaya Oborona
3.) The Cranberries

Apply the filter, and you'll get this:

1.
2.
3.

Filter > Remove before delimiter at each line. Removes the substring BEFORE the delimiter at each selected line. You need to put the delimiter into the FIF. For example, the delimiter is ")", and the selected text is:

1.) Nirvana
2.) Grajdanskaya Oborona
3.) The Cranberries

Apply the filter, and you'll get this:

Nirvana
Grajdanskaya Oborona
The Cranberries

Math > deg min sec > dec degrees converts coordinates from "degrees minutes seconds" format to "decimal degrees". Example:

Was: 40° 26′ 46″ N 79° 58′ 56″ W, result: 40.446° N 79.982° W.

Math > dec degrees > deg min sec converts coordinates from "decimal degrees" format to "degrees minutes seconds". Example:

Was: 40.446° N 79.982° W, result: 40° 26′ 46″ N 79° 58′ 56″ W.

Math > Sum by last column - sums values of each last column at the selection. For example, we can calculate the simple text list:

Carrots 14
Apples 45.8
Oranges 11

Math > Evaluate - calculates the expression that selected at the text. For example you can write 2+2, then select it and apply Evaluate. After such action you shall see the result at the logmemo. In your expression can may use the following operators: +, -, *, /, ^ (power), % (get percent value). The braces are supported. Here are some examples:

2/3*123
5+5+5
1-200*89
2^8
250%4 //what is 4 per cent of 250?

Math > Decimal to binary - converts the decimal integer into its binary representation. Example: was 255, will be 0000 0000 0000 0000 0000 0000 1111 1111.

Math > Binary to decimal - works with unsigned int binary numbers only.

Math > Flip bits (bitwise complement) -use this function to flip a bit at the binary value. Example: was 0000 0010, will be 1111 1101.

Math > Enumerate - Enumerates lines from the selected text. Place the parameter string of this function into the Famous input field. Syntax: step~zero_padding~prefix.

Step is the step of an increment. For example, step 1 gives a numbers 1, 2, 3 etc. Step = 10 will give us 10, 20, 30.

Zero padding defines how many digits are in each number. For example, if zero padding = 3, and step = 1, we shall have 001, 002, 003 etc.

Prefix is a prefix after the number and before the string. Prefix can have significant trailing spaces.

Here are some examples and results:

example 1, parameters are "1~3~) " (without quotes). Result is:

001) dog
002) cat
003) mouse

example 2, parameters are "10~1 " (without quotes). Result is:

10 dog
20 cat
30 mouse

example 3, the parameter is "1" (without quotes). Result is:

1dog
2cat
3mouse

You can use this function even without the parameters - in such case, padding = 0, step = 1, and the prefix is an empty string. You can use the single step parameter or the pair step and padding, or all three parameters.

Math > Arabic to Roman - converts "usual" numbers into the Roman form (i.e 20 to XX).

Math > Subtitles: shift timecode by msecs - shift timecode by msecs.
Open the subtitles file (SRT or Youtube format), select text, put msecs to FIF. msecs can be negative, i.e "-2000" shifts timecodes by 2000 msecs earlier.

Functions > Morse code

From Morse To English. I am not a guru in Morse codes, but I hope that my effort to implement such function is right. With this menu item you can translate the English text into the Morse code. For example, was:

tea and coffee

Will be:

- . .- .- -. -.. -.-. --- ..-. ..-. . .

Note that TEA puts a single space between two Morse codes.

From Morse To English. The inverse action for the previous function. You can decode any English Morse-coded message. You must know that TEA supposes that there are single spaces between Morse-codes, as in the example written before.

Text > Compress - removes all whitespaces at the selection.

Text > Anagram - find all anagrams of the selected text. Can be VERY slow and eats a lot of memory if the word is large. So TEA will not froze, it just thinks. Anagram example for "dog":

dgo
dog
gdo
god
odg
ogd

Text > Remove formatting at each line - removes the formatting (tabs, new lines, double spaces etc.) at each selected line.

Text > Remove formatting - removes the formatting from the whole selected text, so the text will be converted to the one big string without new lines, double spaces, etc.

Text > Apply to each line is a powerful tool to add some text into each line of the selection. And again we use the Famous input field. For example, I want to add br-tag at the end of each line. So I type into the entry:

%s<br>

And then I apply this function to get br-tag added at the end of each line. The %s macro points to each line of the selection. So consider that the %s represents a text of each line. In another example, I want to enclose each line into a pair of li-tags. The formula for the Famous input field will be:

<li>%s</li>

And another example:

<a href="%s">%s</a>

You can also apply the snippets (from the TEA's snippets directory). To do such thing, use the @@snippetname at the FIF. For example, if you have the snipped named "myitalic", use @@myitalic. As you remember, snippets can contain the %s macro to substitute the selected text. In the case of "Apply to each line" function, %s will mean the text of each selected line.

Text > Escape regexp useful to escape regexp special characters such as $, *, +, [, ], etc .

Text > Check regexp match check the selected text against the regexp (put the regexp to FIF).



Functions > Spell-checking

TEA can use the power of three spell checking engines: Aspell, Hunspell and Nuspell (TEA Qt6-build only). TEA can be built with or without them. In a positive case, to switch between engines use the list Spell checker engine at Options - Functions page.

Aspell engine uses system-wide installed dictionaries (use your Linux distro package manager to install them). At the Windows, you need some extra things to do. First, download and install Aspell full installer from the http://aspell.net/win32/. Then, from the same page, download and install some dictionaries. You need to put them into the directory, where Aspell is installed. By default, it is C:\Program Files\Aspell. Finally, at TEA, you need to set the path to the Aspell - go to the Options - Functions, change the Aspell directory option to the correct one and restart TEA.

For Hunspell, you also need to set the path to the dictionaries by yourself, manually. It is possible that the dictionaries are already installed because Hunspell is the spell checking engine for Firefox and LibreOffice.org. For example, Firefox (in a case of the user local home directory installation) holds the dictionaries at firefox/dictionaries directory. Also you can download dictionaries from LibreOffice extension repositories.

There are files with OXT extension. Internally they are common ZIP-files. So download and rename filename.oxt tofilenname.zip. Create some directory for dictionaries and unpack the .aff and .dic files from ZIP. Then select this directory using Select button at the Options - Functions - Hunspell dictionaries path option.

To be clear - we need only .aff and .dic files at the directory that you set at TEA options. No subdirectories.

Nuspell searches the dictionaries in its own way, let's say it is system-wide installed Hunspell and LibreOffice dictionaries.

When the dictionary stuff is configured, you need to go to the menu Functions - Spell checker languages and select the language what you need. Without this step the spell checking will fail. Alsom set the current language in this way after you switch between the engines (Aspell, Hunspell, Nuspell).

It's a good idea to restart TEA after adding the dictionary.

Spell-checker languages - At this menu you can find the list of dictionaries which installed for the current spell-checker. To do the spell checking, use one of those menu items. It works with the whole text, not with the selection only.

The menu item with the language name is used also to set the default spell-checker language. The default value is the language of your locale, but sometimes it went wrong, so select the language/dict manually is a good idea.

To do a simple check with the default (last selected) language, use Spell check menu item.

Suggest - shows the list with suggestions for the current misspelled word (actually, the word under the cursor). To use this function, use Spell check first. Possibly incorrect words will be underlined.

You can fix them with the Suggest functions or by the hand. When you fix the error you can see that the fixed word is colored as before. That is because the nature of the TEA spellchecker - it updates manually, so to update error marks select the same spell checking menu item again. To turn error marks off, use View > Hide error marks.

Add to dictionary - add the underlined as incorrect word to the user's local dictionary. Actually, this function adds to the dictionary any word at the cursor.

Beware that some correct words TEA can mark as incorrect. It's due to the current word parsing, it will be fixed soon as possible.

Remove from dictionary- removes the word from the user's dict.




Functions > Speech

Currenly the Speech thing at TEA is experimental. To use it, you need that TEA build with Speech Dispatcher support.

If it is, install some speech synth that Speech Dispatcher can use as the output module, for example Festival or RHVoice. Then install voices for such synth. Configure Speech Dispatcher. The following example is for RHVoice:

1. Copy the /etc/speech-dispatcher directory to $HOME/.config

2. There, edit speechd.conf, where uncomment the line with AddModule "rhVoice" and add the line: DefaultModule rhvoice

3. To avoid crackles when run other audio software (such as Reaper or Ardour), if you use Pipewire or Pulse, uncomment AudioOutputMethod "pulse"

At Options page of TEA, section Speech, you can turn on and off Speech support, and select the voice. If Enable locale only voices is On, you'll get the list of locale voices only, not for all languages available. For stability purposes, these setting take effect after TEA restart.



IDE

TEA has some basic IDE functions. The use TEA as IDE is simple.

The first, you need to create the project file. To do this, just create the new file and insert TEA project file template from Functions - Place - TEA project template.

Save it at the your project's source top directory, under any file name, but with the "teaproject" extention, i.e. filename.teaproject.

teaproject file is a simple key=value text file. It holds the project settings. Thus, you can hold many teaproject file at the same directory. Here are, for example, TEA's teaproject files for different build systems (qmake, cmake and meson):

tea-make.teaproject

command_build=make
command_clean=make clean
command_run=bin/tea --m&

tea-meson.teaproject

dir_build=b
command_build=ninja
command_clean=ninja -t clean
command_run=./tea --m&

tea-cmake.teaproject

dir_build=cmake
command_build=make
command_clean=make clean
command_run=./tea --m&

The syntax is clear:

dir_build - the directory, where command_build will run for the building. If there is no dir_build, then the teaproject file directory will be used as dir_build. The directory can be absolute, if not, the relative one to the teaproject file directory will be used.

command_build can be any, as you wish: make, ninja, etc.

command_run - runs at dir_build to run compiled binary.

command_clean - runs at dir_build to clean the project.

When you open, save or switch (by the tab) to the teaproject file, TEA reads it into the inner structures and uses the commands from teaproject when run menu items from IDE menu: Run program, Build program, Clean program. Please note that TEA reads the SAVED contents of the teaproject file, not the current edited state.

The IDE menu is useful in a pair with View - Profiles menu. For example, you can create the "ide-profile" with some font settings, wide window, no word wrap; and "text-profile" with another font set, word wrap, smaller window, etc. Then you can quickly switch between the profiles via menu or by the hotkeys.

When building program, TEA shows compiler output at Logmemo. Double-click on the bold text (filename:line:column) to open the error or the warning place at the editor.



Run

This menu is designed for running the current file with external programs, browsers for example. To add some items to menu, go to File > Configs > Programs list config, edit that file and then save it. The configuration file has the simple ini-like format. Each line has the following format:

meni item caption=command line

An example of the command line for a browser starting:

FF=firefox %s

%s is a needful macro for a current filename, so use it in the proper place. For Win32 you need to use the full path for the executable file, with the double quotes (macro %s needs its quotes too!). For example:

opera="C:\Program Files\Opera\opera.exe" "%s"

Some more precise macros are available: %basename, %filename, %ext, %dir. This allows to use the parts of the path. Thus, if we have the full path "/mnt/foo/bar.txt", %dir = "/mnt/foo", %basename = "bar", %filename = "bar.txt", %fext = "txt". So the full path at the command line can be presented in the following way: %dir/%basename.%ext

To open the currently selected file or file at cursor at the text (as by F2, but with an external program) use %i macro.

For example, if you want to open the image file with gimp, put the following command line to the config file:

gimp=gimp %i

Then, at HTML or LaTeX file, move the cursor to the image file name. Then call "gimp" menu item from the "Run" menu. Enjoy.


Nav

Labels, Update labels. You can put some specially formatted labels into the text and then switch between them from the Labels menu (or drop-down list on the toolbar). How does it work? The label is just a text embraced with the opening and the ending sequences ("[?" and "?]" by default, this values can be redefined at Options - Functions - Labels).

For example, put into your text some lines such as "[? dogs ?]", "[? cats ?]". Then make TEA know this labels using Update labels menu item or press the button on the toolbar. The Labels menu and drop-down list will be filled with labels "dogs" and "cats". You can select them from the menu or the list and navigate to the text near the selected label. Please note that labels are updating manually only.

Go to line - moves the cursor position to the line number that you provide in the Famous input field.

Save position/Go to saved position - these functions are good to make a quick jump through a large text, when you need to look something at the one place, then return to the currently editing place, etc.


Fm

Fm is meaning the File manager. This menu contains functions those related to TEA file manager. For example, you can rename files or directories or get the MD5 checksum. Explore and use!

For some function such as MD5 checksum, select the file first, and then apply the function.

Fm > Multi-rename submenu helps to flexibly rename file names. All those functions use the parameter from FIF.

Fm > Multi-rename > Zero pad file names - prepend zeroes to each selected filename that has the numbering as the part of file name. For example: page1, page100. Such files cannot be sorted properly.

So set the filename length at FIF. Files will be numbered correctly, but with the removal of all non-digit characters. Example:

We have files:
page1.txt
page100.txt

Put 5 to the FIF, apply the function.

Now we have:
00001.txt
00100.txt

Fm > Multi-rename > Delete N first chars at file names - N is a number from FIF. Deletes N first characters from each selected file name.

Fm > Multi-rename > Replace in file names - replaces the substing at each selected file name. FIF format: old~new. Example: .jpeg~.jpg

Fm > Multi-rename > Apply template - apply the template to each selected file name. Available macros with parts of the original file name: %filename (= filename.ext), %ext (= ext), %basename ( = filename). For example, you need to rename 01.jpeg, 02.jpeg to 01.jpg, 02.jpg. So the template must be: %basename.jpg, i.e. %basename (file name without extension) will be appended with ".jpg".

Fm > Select by regexp/Deselect by regexp - use this functions to select or deselect files by regexp pattern. For example, to select all txt-files at the current dir, put the following regexps into the FIF:".*\.txt$" (without quotes!) then, use Select by regexp. Then you can press Open button to open all selected files.

Fm > File information > Full info - gives you a full information about the file (creation/modification time, etc). For the 16 bit PCM WAV files it calculates the RMS (for the both channels). For other WAV's shows just the properties such as bit depth, etc.

Fm > File information > Count lines in selected files - please note that empty lines will be also counted.

Fm > Images > Scale by percentages/Scale by side - among other non-text related functions TEA has the ability of batch conversion of images. It is useful when you want to scale them or convert to another format.

For example, we want to scale selected images by 50 per cents, and put the processed images into the new directory:

1. Select files at the File manager. To select them multiply, use Ctrl-Click (or Ctrl-A to select all).
2. Put the desired percentage into the Famous Input Field. The value must be a simple integer number without the % sign.
3. Apply the Scale by percentages function.

After that, TEA creates a new directory as a subdirectory of a current one. This new directory has the random name that starts with "images-out-". Into this directory TEA put all converted images, leaving the originals unmodified.

You can want to scale images in a more precise way and define the size manually. To do that, put the image side size into the Famous Input Field and apply Scale by side. As you know, each photo has dimensions. For example, 640x480. If the width is larger than height, the image has a horizontal orientation (TEA does not support EXIF data), and vise versa. So we can use the side - the larger side of an image. Usually all photos have a standard dimensions with some aspect ratio in a mind, so the larger side for all of photos is the same.

For example, we want to scale all images (photos with a right aspect ratio/dimension) to width 640 and, if the image has a vertical orientation, to the height 640. Put that 640 into the Famous Input Field and apply Scale by side.

Image conversion options are places at the Options > Images page. With the Image conversion output format list you can set the output format. The Output images quality (from 0 to 100) is used mainly for JPEG. A default value is -1 that means the internally defined value. The Scale images with bilinear filtering checkbox affects images when you scale them, to smooth the image and make it less "rough".

Fm > Images > Create web gallery - yes, TEA can create the simple oldschool table-based web gallery. Do the following steps:

1. Put your images in some directory. Save the html-file there. Open that file with TEA.

2. Go to the TEA's file manager, select image files those you want to put into the web gallery.

3. Use the Create web gallery function.

4. The thumbnails and the HTML-table shall be created.

You can customize the gallery using the Options > Images > Web gallery options. There is a thumbnail size, cells per row, et cetera.


View

Highlighting mode - from this submenu you can set the highlighting mode manually. It is useful for new unsaved files or for a file without extensions.

Hide error marks - hides the underlined marks those were set by the spell-checker.

Toggle word wrap - turn word wrapping on/off for the current document.

Palettes - from this submenu you can select the color scheme that affects a text editing area. To define your own colors you can create your own palette based on some build-in palettes (see the source directory, palettes subdir). Then save your palette into the $HOME/.config/tea/palettes directory and choose one from the Palettes menu.

TEA partially supports Eclipse IDE themes (eclipsecolorthemes.org) - just put XML file of the theme to TEA palettes directory and select it from the menu as the native TEA palette. It is a miracle! Please note that TEA palette format supports more highlighting elements than Eclipse Color Theme plugin, so Eclipse themes will be not so colorful as TEA native palettes.

Profiles - this menu allows you to switch between view profiles (they includes parameters such as window position, size, fonts, word wrap and line numbering settings, etc.). So you can save your current settings via Save profile function, and named profile will appear in the Profiles menu. In any time you can select it to recall saved settings.

Keyboards - the list of user-defined keyboards. TEA show such "keyboard" as the window with buttons. Each button can be the letter or the word or any string, to simplify inputm for example, foreign alphabet letters or difficult words.

The "keyboard" is a simple text file that you put to $HOME/.config/tea/keyboards directory. Each line of the file contain letters or words separated by "|", for example:

dog|cat
apple|orange


Options tab

On Keyboard tab you can assign the shortcuts with the menu items. To set a new hotkey, select a menu item from the list, then press some hotkey combination at the entry to the right from the list, and press the Assign button. To remove a hotkey, select the menu item from the list and press Remove button. To reassign the hotkey that already in use, do Remove for this shortcut first.

For Linux, you can use the joystick/joypad to scroll the text and move the cursor. It is disabled by default and can be enabled via Preferences - Common - Use joystick as cursor keys. When enabled, it reads joystick device each 100 milliseconds. I was needed in joystick support to scroll texts for Youtube online videos, when I need to sit in the same position.

Word wrap - use word wrapping globally for all documents, including new ones.

Use wrap setting from highlighting module on the Interface page means that the word wrap settings will be taken from the syntax highlighting modules, not from the global Word wrap option. Some modules have word wrapping turned on, and some turned off. For example, for C++ the word wrap setting is on. For the plain text or for the HTML - is off.


Keyboard

In addition to the standard keyboard operations TEA supports left-handed cursor operations those can be enabled at Options - Common - Use Left Alt WASD.

When enabled, use Left Alt plus WASD for cursor movement, Left Alt plus E and C for page up/down. Use Left Win key with WASDEC to select the text.


On autosaving

TEA has some features related to the auto-saved files.

The first one is saving on unsaved files. When you create a new file, it exist as the buffer. You use "Temporary save unsaved buffers on exit" option (at Options - Common). If it is turned on, TEA save such buffers on TEA exit (not on the closing buffer tab manually), and then restores them on the next start. So, it is like the session saving. But, if you just close some unsaved file tab, it will not be saved.

Another option there, is "Autosave buffers and autosaving files (each N seconds)". It saves buffers each N seconds, also it saves the ordinary files that checked as "autosaved". You can set/unset file as autosaved at File - File actions - Set as autosaving file/Unset the autosaving file. Such autosaved files also automatically saved when you close them.


The secret power

Know the truth! The reptiloids will prevail over the humankind if you do not have the courage and the knowledge of TEA secret options. They don't have GUI and you need to add and edit them manually to the TEA main config file $HOME/.config/tea\tea.conf (or, under Window, at drive:\Documents and Settings\username\tea\tea.conf). Some of the changes will work after TEA restart. So know the truth:

recent_list.max_items=maximum recent list items, default 21


Notes

The legacy "--charset=codepage" command line option is supported. This command sets the codepage for the following files. For example:

tea --charset=window-1251 file1.txt file2.txt --charset=utf-8 file3.txt

By default (if no charset defined manually) TEA uses UTF-8.

This manual and all TEA media stuff (palettes, images, etc) is the public domain. TEA source code is licensed under GPL v3 and public domain.

User's syntax hl files can be added to the $HOME/.config/tea/hls directory. See the working hl files at the TEA source directory, the hls subdirectory. Feel free to contribute your hl files. Please use color references from the palette files (look at palettes directory at the source). I.e. use "preproc" or "types" color instead of the hexadecimal value.


Other projects by the author

My Github page.


Support the development

If you want to support TEA development, here is my Paypal address: peter.semiletov@gmail.com

tea-qt-63.3.0/manuals/pl.html000066400000000000000000005660451476733534200157750ustar00rootroot00000000000000 Edytor TEA

Edytor TEA - Przewodnik po działaniu


Spis treści



Wprowadzenie
Interfejs
Menedżer plików
Panel dat
Plik
Kalendarz
Edycja
Znaczniki
Szukaj
Funkcje
Sprawdzanie pisowni
IDE
Uruchom
Nawigacja
Menedżer plików
Widok
Zakładka Opcje
Klawiatura
Sekretna moc
Uwagi
Inne projekty autora
Wspieraj rozwój

Wprowadzenie

TEA ma długą historię rozwoju. Jesienią 2000 roku miało miejsce pierwsze wdrożenie TEA, napisane w Object Pascal przy użyciu Borland Delphi. Od tego czasu TEA była wielokrotnie przepisywana od zera (Pascal - C/Gtk - C ++/Qt) i otwierana jej źródło. Początkowy interfejs TEA został zainspirowany przez Impulse Tracker i Sound Forge. Później koncepcja przesuwanych paneli i zakładek została wdrożona.

Teraz TEA ma TRZY gałęzie. To, TEA-Qt, jest nowoczesne. Są też przestarzałe TEA-GTK (Gtk 2) i TEA-GTK (GTK 3), buggy i zapomniany. Więc użyj TEA-Qt.

TEA ma dwie strony domowe: semiletov.org/tea i tea.ourproject.org. Jeśli jeden z nich nie działa, przejdź do innego. Happy Arch/Manjaro/ itp. użytkownicy mogą używać mojego pakietu „tea-qt” z AUR ( aur.archlinux.org/packages/tea-qt ). Witaj na github.com/psemiletov/tea-qt - zawsze aktualne, ale może być niestabilne.

Nazwa TEA nic nie znaczy. To nie jest „Text Editor Of Atomic Era” czy coś, co można przeczytać w recenzjach TEA. Początkowo TEA = "Edycja i tworzenie tekstu". Wtedy TEA kojarzyła się ze zwykłym napojem (lubię pić herbatę). Ale na razie TEA to po prostu TEA lub ТИА.

Zwykle TEA przechowuje swoje dane konfiguracyjne i inne pliki pomocnicze w $ HOME /.config/tea (lub w oknie C: \ Documents and Settings \ nazwa_użytkownika \ herbata). Ale jeśli chcesz uruchomić TEA z pendrive'a w trybie przenośnym, TEA musi czytać/ zapisywać dane konfiguracyjne w katalogu z binarnym TEA. Aby uruchomić TEA w trybie przenośnym, użyj opcji wiersza poleceń „--p”:

tea - p

Jak wiesz, TEA pod * NIX to pojedynczy plik binarny. W katalogu instalacyjnym Windows TEA znajduje się wszystko, co jest potrzebne do uruchomienia przenośnego TEA. Należy pamiętać, że ustawienia TRYBU NORMALNEGO nadal istnieją i są dostępne w dowolnym momencie, gdy uruchomisz HERBATĘ BEZ klawisza „--p”. Preferencje TEA trybu przenośnego i normalnego są od siebie niezależne i istnieją równolegle.

Interfejs

TEA ma dwa tryby interfejsu - klasyczny i nowy, tzw. zadokowany. Możesz przełączać się między nimi w Opcje - Interfejs - Tryb interfejsu użytkownika. W trybie zadokowanym możesz oderwać i/lub przenieść Logmemo i FIF (patrz poniżej). Domyślnym interfejsem użytkownika jest klasyczny. Osobiście używam klasycznej.

Od góry do dołu są:

1. Menu główne. Zauważ, że możesz oderwać podmenu i umieścić je gdzieś na pulpicie. Pozycje menu w dużym stopniu zależą od bieżącego zadania, a nawet trybu edytora. Niektóre menu mogą być widoczne lub ukryte w zależności od zadania (edycja, przeglądanie plików itp.).

2. Obszar pasków narzędzi. W rzeczywistości paski narzędzi można zaparkować po dowolnej stronie okna TEA. Pozycje są zapisywane między sesjami. Aby włączyć lub wyłączyć pasek narzędzi, użyj menu kontekstowego paska narzędzi (kliknij prawym przyciskiem myszy pasek narzędzi). Jeśli żaden pasek narzędzi nie jest widoczny, kliknij prawym przyciskiem myszy na pasku menu.

3. Obszar z zakładkami. Zawiera 5 głównych zakładek - edytor, pliki, opcje, i podręcznik. Edytor - to miejsce, w którym widzisz otwarte dokumenty. Pliki - wbudowany menedżer plików. Opcje - tutaj możesz zmienić preferencje TEA. Wszystkie zmiany zostaną zastosowane natychmiast. Daty - prosty kalendarz z funkcjami organizatora. Instrukcja - wbudowana instrukcja obsługi. Ta doskonała!

4. Logmemo. Jest to obszar powiadamiania o wszystkich ostrzeżeniach, wiadomościach informacyjnych i wynikach niektórych funkcji przetwarzania tekstu.

5. Pole wejściowe Znane (FIF). Pole tekstu wejściowego, w którym można umieścić wartości funkcji przetwarzania tekstu. Służy również do wyszukiwania/zamieniania (szczegółowe informacje można znaleźć w menu Wyszukaj ). FIF ma trzy przyciski - Znajdź, Znajdź poprzedni i Znajdź następny. FIF jest również używany do wyszukiwania w podręczniku TEA, liście elementów menu (na stronie dostosowywania skrótów klawiszowych), a nawet wbudowanym menedżerze plików.

6. Pasek stanu. Zawartość paska stanu można skonfigurować na stronie preferencji Interfejs.

Menedżer plików

Menedżer plików TEA jest ukryty w zakładce pliki. Nie jest to dodatkowe narzędzie typu bloatware, ale istotna część edytora. Zastępuje standardowe okna dialogowe zapisywania/ otwierania plików (przy okazji można je przywrócić, używając pola wyboru Opcje > Wspólne > Użyj tradycyjnych okien dialogowych Zapisz/Otwórz plik ).

Zobaczmy, jak olśniewa siła menedżera plików TEA.

Pasek katalogu znajduje się u góry. Znajduje się tam wpis z aktualną ścieżką do katalogu oraz ładny pasek narzędzi z następującymi przyciskami:

Idź - nawiguje do katalogu ze ścieżką z wpisu tekstowego.

Strona główna - przenosi cię do przytulnego katalogu domowego.

Odśwież - odświeża listę plików dla Twojej przyjemności.

Przycisk Operacje na plikach z podmenu. Zawiera kilka działań narzędziowych, takich jak Utwórz nowy katalog, Zmień nazwę i Usuń plik. Należy pamiętać, że TEA nie usuwa katalogów.

Prawy panel menedżera plików.

U góry znajduje się pole nazwy pliku. Tutaj możesz podać nazwę pliku, aby go otworzyć lub zapisać. Aby zapisać bieżący plik pod jakąś nazwą, po prostu umieść nazwę pliku we wpisie tekstowym Nazwa i naciśnij przycisk Zapisz jako. Aby otworzyć plik, kliknij dwukrotnie nazwę pliku na liście plików lub umieść nazwę pliku we wpisie Nazwa i naciśnij przycisk Otwórz. Możesz użyć klawisza Enter wewnątrz tego pola wejściowego. Akcja jest powiązana z pozycją menu (Otwórz plik lub Zapisz jako), której użyłeś do otwarcia karty menedżera plików.

Możesz także wybrać kilka plików (kliknij z wciśniętym klawiszem Ctrl lub klawisz INSERT), a następnie naciśnij przycisk Otwórz.

Wybrany plik jest zapisywany lub otwierany z kodowaniem wybranym na liście zestawu znaków poniżej w pozycji Nazwa. Istnieje również znak „?” przycisk. Wybierz plik i naciśnij ten przycisk - próbuje automatycznie wykryć zestaw znaków pliku i aktywować go na liście kodowań. Obecnie tylko pliki HTML lub rosyjskie zestawy znaków są obsługiwane przez wbudowane automatyczne wykrywanie i inne za pomocą narzędzia Enca (patrz Opcje - Wspólne ).


Lista zakładek. Możesz łatwo dodawać i usuwać zakładki katalogów za pomocą przycisków „+” i „-”. Przycisk „+” dodaje zakładkę do bieżącego katalogu drzewa systemu plików. Przycisk „-” usuwa wybraną zakładkę. Aby aktywować zakładkę i przejść do odpowiedniego katalogu, kliknij dwukrotnie zakładkę.

Na górze listy znajdują się specjalne zakładki: fragmenty, szablony, skrypty. Nie możesz ich usunąć. Wskazują na specjalne katalogi TEA dla elementów użytkownika, takich jak szablony, fragmenty, itp.

Możesz włączyć Opcje > Wspólne > Opcja automatycznego podglądu obrazów w menedżerze plików, aby pokazać małe okno podglądu w pobliżu menedżera plików. Ponadto, gdy kursor znajduje się na pliku obrazu, możesz użyć funkcji Otwórz przy kursorze (F2), aby wyświetlić obraz w pełnym rozmiarze w innej wbudowanej przeglądarce obrazów (patrz Otwórz przy kursorze opis).

Panel dat

Jak możesz zauważyć, TEA ma wbudowany organizator kalendarza w zakładce daty w oknie głównym. Dwukrotne kliknięcie dni umożliwia tworzenie lub otwieranie dokumentów tekstowych typu plik dziennie. Po zamknięciu takiego pliku jego zawartość zostanie zapisana automatycznie. Aby usunąć rekord dnia, użyj opcji Kalendarz > Usuń rekord dnia.

W tekście możesz określić sygnaturę czasową zdarzenia alarmowego. Ten podpis ma format [gg: mm] w 24-godzinnym dniu. Cały tekst po takim podpisie a przed kolejnym będzie traktowany jako komunikat alarmowy. Na przykład:

[20:00] idź do teatru
[23:00] wyj do księżyca

Na pewno TEA musi być uruchomiona, aby zobaczyć komunikat alarmowy!

Pojedyncze kliknięcie komórki daty pokazuje zapis dnia (jeśli istnieje) w obszarze logmemo.

Na stronie Opcje można ustawić opcję Rozpoczynanie tygodnia w niedzielę na „wł.”. Widżet kalendarza może działać w dwóch trybach - normalnym i księżycowym. Przeczytaj sekcję Kalendarz, aby uzyskać szczegółowe informacje.

Plik

Nowość. Niemożliwe! To jest rodzaj magii. Ta pozycja menu tworzy nowy, pusty dokument, w którym możesz wpisać dowolne słowa. Na przykład wpisz słowo „cześć”. To bardzo wesołe słowo. To jest to, co zwykle mówimy wszystkim dobrym ludziom dookoła. Witaj! Twój świat staje się jaśniejszy.

Otwórz - z pewnością ta pozycja menu pomaga otworzyć każdy plik, który jest już zapisany na dysku twardym.

Jeśli korzystasz z wbudowanego menedżera plików, przeczytaj rozdział Menedżer plików. W przeciwnym razie przeczytaj słowa true dotyczące okna dialogowego Otwieranie pliku.

W oknie dialogowym Otwieranie pliku znajduje się mały sekret. Zestaw znaków combobox. Tak. Dzięki temu wspaniałemu narzędziu w pudełku możesz otworzyć plik tekstowy w dowolnym kodowaniu znanym człowiekowi (właściwie libiconv ). Ale bądź ostrożny! Musisz dokładnie wiedzieć, jakiego kodowania potrzebujesz, aby poprawnie odczytać plik. W przeciwnym razie zobaczysz wiele dziwnych liter, które mogą naruszyć twój spokój ducha. Jeśli tak, zamknij szybko oczy i siedem razy napisz „cześć”! Możesz też spróbować załadować plik z innym wybranym kodowaniem. Więc wybieraj mądrze. Nawiasem mówiąc, TEA może spróbować automatycznie wykryć kodowanie - wystarczy nacisnąć „?” przycisk (po prawej stronie listy zestawu znaków). Lepiej jest włączyć opcję Użyj Enca do wykrywania zestawu znaków na stronie Opcje - Wspólne, jeśli Enca jest zainstalowana.

TEA może otwierać wszystkie zwykłe pliki tekstowe, a także formaty ODT (OpenDocument Text), KWD (stary format KWorda), FB2, DOCX, RTF, SXW (stary format OOo/StarOffice), ABW (Abiword) i pliki spakowane gzipem. Ale za pomocą TEA można zapisać tylko zwykłe pliki tekstowe. O innych wymienionych formatach - TEA może po prostu wyodrębnić z nich tekst, starając się zachować podstawowy układ (wcięcia, akapity). Po skompilowaniu z obsługą PDF i DVJU, TEA również wyodrębnia z nich tekst. W przypadku korzystania z funkcji Szukaj w plikach TEA, TEA zagląda do wszystkich plików w tych formatach.

Zapisz - jeśli plik jest już zapisany, ta pozycja menu zapisuje go ponownie. Tak to działa. Ale jeśli plik nie zostanie zapisany, zobaczysz niespodziankę! Menedżer plików. Tam umieść nazwę pliku w polu Nazwa i naciśnij przycisk Zapisz jako.

Ostatni zamknięty plik - otwiera ostatni zamknięty plik. To zdanie jest większym paradoksem XXI wieku n.e.

Otwórz przy kursorze lub F2 jako skrót - ta funkcja przekształca użytkownika w tzw. „uzależnionego od F2”. Mogą zdarzyć się dziwne przemiany umysłu i ciała, więc ostrzegamy!

Umieść kursor na nazwie pliku w href-link lub LaTeX, w tym grafikach, wejściach, dołączaniu itp. Naciśnij F2. Widzisz ten cud? TEA otwiera plik o wskazanej nazwie. Jeśli ten plik jest już otwarty, jego zakładka będzie aktywna. Jeśli plik jest obrazem, TEA pokaże go we wbudowanej przeglądarce obrazów. Ale co, jeśli nie pracujesz z kodem [X] HTML, ale używasz prawdziwych nazw plików w tekście i chcesz je otworzyć? TEA sobie z tym poradzi - po prostu wybierz nazwę pliku (nie tylko umieść na niej kursor) i naciśnij F2.

Jaka jest różnica? TEA może otworzyć „plik pod kursorem”, gdy nazwa pliku jest ujęta w cudzysłów. Jak w HTML lub w większości źródeł kodu. W innych przypadkach musisz ręcznie wybrać nazwę pliku za pomocą klawiszy Shift-kursor lub myszy.

I trochę więcej - za pomocą Otwórz przy kursorze możesz poruszać się po lokalnych linkach href.

Możesz także użyć Otwórz przy kursorze we wbudowanym menedżerze plików, aby otworzyć plik obrazu w oknie podglądu.

Możesz także otworzyć plik w pozycji kursora za pomocą dowolnego programu zewnętrznego. Przeczytaj o menu „Uruchom”, aby uzyskać szczegółowe informacje.

Brudnopis (Crapbook)  - otwiera specjalny plik, w którym możesz umieścić notatki, fragmenty tekstu itp. Plik crapbook zostanie automatycznie zapisany po zamknięciu. Jeśli plik Crapbook jest otwarty i wychodzisz z TEA, zawartość Crapbooka również jest zapisywana.

Notatki - coś w rodzaju crapbooka, ale połączonego z bieżącym plikiem. Przydatne do umieszczania notatek związanych z zawartością bieżącego pliku. Plik.notes jest automatycznie zapisywany przy wyjściu.

Zapisz jako inny > Zapisz.bak - zapisuje zawartość aktualnego dokumentu wraz z nazwą, do której dodano rozszerzenie „bak”.

Zapisz jako inny > Zapisz wersję z sygnaturą czasową - zapisuje zawartość aktualnego dokumentu wraz z nazwą, w której dodawany jest znacznik czasu. Będziesz więc znać datę i godzinę związaną z zapisaną wersją. Uwaga dotycząca formatu sygnatury czasowej. Czy to rrrr-MM-dd-gg-mm-ss. Więc najpierw jest rok, potem miesiąc, potem dni, godziny, minuty i sekundy.

Najnowsze pliki. Z tego podmenu można otworzyć jeden z kilkunastu ostatnio otwartych plików. TEA zapamiętuje nazwę, kodowanie i bieżącą pozycję pliku po zamknięciu dokumentu, więc następnym razem TEA już zna zestaw znaków i pozycję, od której ma rozpocząć edycję.

Szablony - aby utworzyć nowy plik z szablonu, wybierz szablon z tego menu. Aby utworzyć nowy szablon, zapisz plik tekstowy w katalogu TEA templates. Jest dostępny w zakładce szablony w menedżerze plików TEA (lub na lewym pasku bocznym w oknie dialogowym Zapisz plik ). I odwrotnie, aby otworzyć szablon, ponownie użyj zakładki szablony menedżera plików. Zestaw znaków szablonów to zawsze UTF-8. TEA ustawia to automatycznie, nawet jeśli wybierzesz inne kodowanie.

Aby usunąć szablon lub zmienić jego nazwę, użyj menedżera plików lub okien dialogowych Zapisz/otwórz plik.

Sesje - w tym menu dostępne są sesje. Sesja to prosta lista otwartych plików. Aby załadować je wszystkie, po prostu wybierz nazwę sesji z tego menu. Aby zapisać sesję, wpisz nazwę sesji w polu Słynne, a następnie użyj przycisku Plik > Zapisz inny > Zapisz sesję. Na stronie preferencji Wspólne można zaznaczyć opcję Przywróć ostatnią sesję przy uruchomieniu. Jeśli ta opcja jest włączona, TEA zapisze sesję przy wyjściu (pod nazwą def-session-777 ), a następnie załaduje tę sesję podczas uruchamiania.

Zakładki - to menu jest podobne do Ostatnie pliki, ale lista plików jest aktualizowana przez Ciebie, a nie przez TEA. Aby dodać bieżący plik do tej listy, użyj przycisku Edytuj zakładki > Dodaj do zakładek. Jeśli lista zakładek zawiera już dodawany plik, jego rekord zostanie zaktualizowany o nowe wartości pozycji i zestawu znaków. Aby usunąć zakładkę, otwórz listę zakładek na stronie Plik > Konfiguracja, usuń potrzebną linię i zapisz listę w zwykły sposób. Błędną zakładkę można tymczasowo wyłączyć za pomocą prefiksu „#”. Aby wyszukać błędne ścieżki i automatycznie wyłączyć takie zakładki, użyj opcji Edytuj zakładki > Znajdź przestarzałe ścieżki.

Podmenu

Operacje na plikach. Z tego menu możesz zrobić kilka magicznych rzeczy. Ustaw koniec wiersza w systemie UNIX, Ustaw koniec wiersza w systemie Windows, Ustaw tradycyjny koniec wiersza w systemie Mac - te funkcje umożliwiają nadpisanie końca wiersza linia (EOL). Kiedy zapiszesz plik, wybrany EOL zostanie zastosowany. Aby sprawdzić EOL pliku, wybierz plik we wbudowanym menedżerze plików i użyj Fm > Informacje o pliku > Pełne informacje.

Nie dodawaj do najnowszych - tymczasowo wyłącz aktualizację listy ostatnich plików. Ta funkcja jest przydatna, jeśli chcesz otworzyć wiele plików i nie chcesz nadpisywać listy ostatnich plików. Dlatego użyj „nie dodawaj” przed zamknięciem „niechcianych” plików, pracuj z nimi, zamknij, a następnie wyłącz opcję „nie dodawaj”, aby przywrócić normalną funkcję listy ostatnich ostatnich.

Wyjście - zamyka TEA. Aby zmniejszyć poziom uciążliwości, TEA nie prosi o potwierdzenie. I jeszcze jedna sprawa związana z potwierdzeniami - TEA automatycznie proponuje zapisywanie tylko zmodyfikowanych istniejących plików. To znaczy, jeśli zamkniesz (za pomocą TEA lub Ctrl-W) zmodyfikowany nowy, nienazwany plik, TEA po cichu go zamknie.

Kalendarz

To menu jest dostępne tylko wtedy, gdy panel do zrobienia jest aktywny.

Włączanie/wyłączanie trybu księżycowego - przełącz widżet kalendarza na tryb księżycowy/normalny. W pierwszym pokazane są fazy księżyca. Zobaczysz również numer dnia księżycowego po ukośniku (przed numerem dnia gregoriańskiego). To znaczy format to: normalny numer dnia/księżycowy numer dnia.

Kilka słów o obliczaniu dnia księżycowego. Przede wszystkim musisz wybrać odpowiednią półkulę Ziemi w Opcje - Wspólna - Półkula północna (wł./Wył.). Inną ważną rzeczą jest algorytm ( Opcje - Wspólne - Algorytm fazy księżyca ). Szybkość i precyzja są różne, spróbuj sam. Wykorzystano kilka algorytmów. Niektóre z nich są oparte na artykule Bena Danglisha na www.ben-daglish.net/moon.shtml, a inny na artykule Alexandra Leushkanova - www.astrogalaxy.ru/275.html.

Z powodów technicznych obraz fazy księżyca w dniach 12 i 13 jest taki sam.

Dodaj lub odejmij lata/miesiące/dni - pozwala na nawigację po kalendarzu wstecz i do przodu o N lat, miesięcy lub dni. Na przykład, musisz zobaczyć dzień +6 po obecnym. Wpisz „6” w Znane pole wprowadzania, a następnie użyj przycisku Dodaj lub odejmij > dni. Jeśli chcesz się cofnąć, wpisz wartość ujemną do FIF, tj. „-6” zamiast „6”.

Liczba dni między dwiema datami - oblicza liczbę dni między dwiema datami. Wybierz pierwszą datę w Kalendarzu i użyj opcji menu Zaznacz pierwszą datę. Wybierz ostatnią datę w kalendarzu i użyj opcji Zaznacz ostatnią datę. Użyj funkcji Liczba dni między dwiema datami, aby zmierzyć różnicę między pierwszą a drugą datą.

Oblicz dni księżycowe między datami - tworzy listę dni księżycowych między dwiema datami ( Zaznacz pierwszą/ ostatnią datę ).

Edycja

Informacje o zaznaczeniu prostokątnym lub blokowym. TEA ma tak ograniczone możliwości, że nie pasuje dobrze do architektury TEA, a funkcje przetwarzania tekstu nie mogą być używane z selekcjami bloków. Wszystko, co możesz z nimi zrobić, to wybrać za pomocą Początek bloku i Koniec bloku, a następnie Kopiuj blok, Wytnij blok i Wklej blok.

Kilka uwag na temat funkcji Wcięcie/Usuń wcięcie. Istnieją dwa zakodowane na stałe skróty klawiaturowe przypisane do tej funkcji - Tab i Shift-Tab. Jeśli żaden tekst nie jest zaznaczony, Wcięcie (tabulator) wstawia znak tabulacji lub spację (spacje). Takie zachowanie zależy od Opcje > Interfejs > Użyj spacji zamiast tabulatorów i opcji Szerokość tabulatora w spacjach.

Jeśli opcja Użyj spacji zamiast tabulatorów jest zaznaczona, edytor wstawi (za pomocą klawisza Tab) liczbę spacji zdefiniowaną przy Szerokość tabulatora w spacjach. W przeciwnym razie TEA wstawia pojedynczy tabulator.

TEA ma również opcję Automatyczne wcięcie, która jest domyślnie wyłączona.

Wcięcie pierwszej linii - ta funkcja wcina wszystkie zaznaczone wiersze z takim samym wcięciem jak pierwsze wiersze wcięcia zaznaczenia.

Pliki do przechowywania - możesz użyć jakiegoś pliku jako pliku „przechowywania”, aby skopiować bezpośrednio do niego tekst z różnych źródeł. Ustaw jako plik pamięci wybiera aktualnie otwarty plik jako plik pamięci. Następnie, jeśli chcesz skopiować i wkleić tekst do tego pliku, po prostu użyj opcji Kopiuj do pliku pamięci. Możesz także przechwycić tekst ze schowka systemowego i umieścić go w pamięci za pomocą Uruchom/zatrzymaj przechwytywanie schowka do pliku. Możesz mieć tylko jeden plik pamięci na sesję. Gdy zamkniesz plik magazynu, dalsze wklejanie do niego zostanie wyłączone, dopóki nie ustawisz go ponownie.

Aby kontrolować tekst wstawiany do magazynu, możesz użyć pliku szablonu o nazwie cliptpl.txt w katalogu konfiguracyjnym TEA. Po prostu stwórz taki plik z kilkoma makrami, na przykład:

---
%date
%time
%s
---
Gdzie: % s - tekst ze schowka, % date - aktualna data, % time - aktualny czas.

 

Znaczniki

Funkcje z tego menu działają głównie w zależności od wybranego trybu znacznika. Dostępnych jest kilka trybów - HTML, XHTML, LaTeX, Wikitext, Markdown, Lout i DocBook. Wybierając tryb z menu Tryb, ustawiasz tryb znaczników dla bieżącego dokumentu. Tak więc różne dokumenty mają różne, oddzielne tryby. Pozwala edytować jeden plik jako HTML, a drugi jako DocBook XML z tą samą pozycją menu. Zatem pozycje menu działają różnie w zależności od trybu znaczników dokumentu. Aby użyć trybu Wikitext, ustaw ten tryb ręcznie w menu Tryb lub użyj.wiki jako rozszerzenia nazwy pliku.

Możesz zaznaczyć tekst i użyć jakiejś funkcji, na przykład „Akapit”. Jeśli nie zaznaczysz żadnego tekstu, TEA po prostu wstaw znaczniki w miejscu kursora.

Oprócz funkcji związanych ze znacznikami, menu Znaczniki zawiera zestaw narzędzi [X]HTML.

[X]HTML Narzędzia  - Zmień nazwę wybranego pliku - zmienia nazwę pliku i zmienia zaznaczony tekst. Na przykład masz (w dokumencie HTML lub LaTeX) łącze do jakiegoś pliku. Chcesz zmienić nazwę tego pliku. Wybierz nazwę pliku w tekście, a następnie umieść w FIF nową nazwę pliku (bez pełnej ścieżki, tylko nazwa.rozkład) i użyj tej funkcji. Voila!

[X]HTML Narzędzia > Waga dokumentu - oblicza całkowitą wagę dokumentu z uwzględnieniem wszystkich pracowników SRC (obrazy, błyski itp.). Poszukaj wyniku w Logmemo.

[X]HTML Narzędzia > Konwertuj tagi na encje - jeśli chcesz pokazać kod otagowany HTML w dokumencie [X]HTML, wybierz swój kod i użyj tej funkcji. Otrzymasz na przykład:

&lt;b&gt;demo&lt;/b&gt;

[X]HTML narzędzia > Text to HTML - ta funkcja konwertuje zwykły tekst na ładny kod w formacie HTML/XHTML przy użyciu CSS.

[X]HTML narzędzia > Podgląd wybranego koloru - podgląd zaznaczonego koloru (w tekście) w FIF. Kolor może mieć postać wartości szesnastkowej lub nazwanego koloru (ciemnoniebieski, czerwony, biały itp.).

Antyspamowa poczta e-mail - sprawia, że ​​wybrany odsyłacz mailto jest prawdopodobnie niewidoczny dla tych przeklętych spamerów zbierających pocztę e-mail, poprzez konwersję adresu na encje zakodowane w liczbach całkowitych. Na przykład, jeśli spojrzysz na źródło tego dokumentu, mailto: tea@list.ru będzie wyglądać jak kupa śmieci. Mam nadzieję, że zbieracze spamu tego nie rozumieją.


Szukaj

TEA nie ma okien dialogowych dla funkcji znajdź/zamień. TEA używa słynnego pola wejściowego (FIF) i interfejsu opartego na menu.

Aby znaleźć podciąg w tekście, umieść go w FIF (użyj Ctrl-F, aby go wyostrzyć) i naciśnij Enter. Następnie możesz użyć funkcji Znajdź następny lub Znajdź poprzedni. W pobliżu FIF znajdują się również trzy przyciski. Są to: Znajdź, Znajdź poprzedni i Znajdź następny. Wyszukiwanie w TEA zaczyna się od pozycji kursora. Możesz ustawić opcje Całe słowa i Rozróżniana wielkość liter, zaznaczając odpowiednie pozycje menu.

Aby zamienić zaznaczony tekst (tj. znaleziony tekst) na nowy, umieść nową wartość w FIF i użyj funkcji Zamień na. W ten sposób możesz przejść przez tekst za pomocą przycisku Znajdź następny i zastosować Zamień na, gdy zajdzie taka potrzeba.

Aby zamienić wszystkie wystąpienia podłańcucha w zaznaczonym tekście, umieść specjalne polecenie w FIF:

stara wartość ~ nowa wartość

Zatem „~” jest ogranicznikiem. Na przykład chcesz zamienić wszystkie wystąpienia słowa „kot” na „mysz”. Polecenie będzie brzmiało:

kot ~ mysz

Więc umieść takie polecenie w FIF i zastosuj funkcję Zamień wszystko.

Jeśli chcesz usunąć jakiś podciąg z tekstu, użyj formuły podciąg ~. Na przykład, aby usunąć wszystkie wystąpienia słowa „broń” z tekstu, możesz użyć słowa „broń ~”.

Podczas wyszukiwania lub zamiany możesz używać wyrażeń regularnych (TEA przez QT używa biblioteki PCRE2 ", która implementuje dopasowywanie wzorców wyrażeń regularnych przy użyciu tej samej składni i semantyki co Perl 5"). Aby to zrobić, włącz opcję Tryb wyrażenia regularnego (Regexp mode) (poniżej Całe słowa ). Na przykład, aby przekonwertować znacznik kursywy HTML na znacznik TeX, możesz użyć następujących formuł:

<i>([^<]*)</i>~\emph{\1}

Zwróć uwagę, że \1 zachowuje się jak zawartość przechwyconej (z „nawiasami”) części ciągu źródłowego. Aby mieć dostęp do drugiej takiej części, użyj \2, \3 dla trzeciej itd.

Inny przykład - jak znaleźć wszystkie liczby w tekście? Użyj wyrażenia regularnego (regexp (\d +) w FIF.

Nie zapomnij wyłączyć trybu wyrażenia regularnego (regexp mode), gdy musisz przeprowadzić zwykłe wyszukiwanie. Ponadto tryb Wyrażenia regularne (regexp modezastępuje tryb Bez rozróżniania wielkości liter, tj. w Trybie wyrażenia regularnego Rozróżnianie wielkości liter jest domyślnie WŁĄCZONE.

Tryb rozmyty - włącza bardzo podstawową obsługę "wyszukiwania rozmytego". Na przykład musisz znaleźć jakieś słowo, ale nie pamiętasz, jak dokładnie jest napisane. Tak więc w trybie rozmytym TEA może znaleźć podobne słowo (jeśli mają tę samą długość, np. „paczka”, „kaczka” itp.). Współczynnik podobieństwa można zdefiniować w Opcje - Funkcje - Różne - Współczynnik wyszukiwania rozmytego.

W trybie menedżera plików „Zastąp wszystko” działa ze wszystkimi wybranymi plikami i zestawem znaków z selektora zestawu znaków w panelu menedżera plików.

Ostatecznie  Zastąp wszystko w otwartych plikach działa jak Zastąp wszystko, ale w zakresie wszystkich otwartych plików.

Szukaj w plikach - przeszukuje tekst podany w FIF we wszystkich plikach tekstowych, zaczynając od bieżącego katalogu we wbudowanym menedżerze plików (zakładka Pliki ), i głębiej w podkatalogach. Do kodowania tekstu TEA używa zestawu znaków wybranego z listy zestawów znaków na karcie Pliki.

Właściwie TEA nie wyszukuje we WSZYSTKICH plikach. W przypadku jednostki centralnej nie ma różnicy między plikami tekstowymi, plikami mp3 lub obrazami. Programy muszą znać różnicę, ale jak? I dlaczego tak ważna jest identyfikacja pliku tekstowego? Jeśli program nie może tego zrobić, przeszuka tekst we wszystkich plikach - obrazach, dźwiękach, filmach itp. Za dużo czasu! Dlatego TEA musi znać różnicę. Dlatego TEA zakłada, że ​​pliki tekstowe to pliki z rozszerzeniami, które TEA rozpoznaje przy podświetlaniu składni. TEA wie również, że pliki tekstowe to txt, odt, kwd, docx i inne formaty obsługiwane przez TEA. Wreszcie TEA wie o niektórych zdecydowanie tekstowych plikach, które mają nazwy takie jak README, ChangeLog itp.

Dlatego TEA nie może wyszukiwać we wszystkich innych plikach i obecnie nie ma możliwości zdefiniowania niestandardowej maski nazwy pliku lub czegoś podobnego.

Zaznacz wszystkie znalezione/Odznacz - kolorowanie lub odbarwianie wszystkich wyników wyszukiwania w tekście. Ta funkcja może powodować niepoprawne ustawienia koloru tekstu podczas korzystania z opcji Widok - Ciemniejszy, do czasu ponownego uruchomienia TEA lub otwarcia i pracy z innym plikiem.


Funkcje

Aby korzystać z funkcji przetwarzania tekstu, musisz najpierw zaznaczyć tekst. Niektóre funkcje (takie jak Statystyka tekstu ) mogą działać z całym tekstem, jeśli nie jest zaznaczony żaden fragment. Istnieją również specjalne przypadki, w których TEA może traktować bieżące słowo jako zaznaczenie, gdy w rzeczywistości nie ma tekstu. Na przykład jest to zachowanie funkcji wielkie litery i małe litery. Ale takie zachowanie jest rzadkim wyjątkiem od reguły.

Niektóre funkcje przyjmują parametr, który należy umieścić w słynnym polu wejściowym (FIF, wpis wejściowy na dole okna głównego). Na przykład funkcja Usuń linie > Rozmiar N pobiera wartość N z pola wejściowego Znane, więc najpierw wpisujesz wartość, a następnie wywołujesz funkcję. Brak wartości domyślnych.

Funkcje > Powtórz ostatnie - powtórz ostatnio używaną funkcję. Właściwie działa z prawie wszystkimi pozycjami menu, nie tylko z menu Funkcje.

Funkcje > Zdania pisane wielką literą - wszystkie zaznaczone znaczenia zamieniaj wielką literą na pierwszą literę pierwszego słowa. Przydatne w przypadku automatycznie generowanych napisów z YouTube, gdzie ręcznie dodajesz „.”, „!” i „?”, a następnie zastosuj tę funkcję.

Funkcje > Narzędzia > Skaluj obraz - skaluje obraz. W tekście wybierz nazwę pliku obrazu lub ustaw na nim kursor. Wstawiamy do FIF parametry skalowania. Zastosuj tę funkcję.

Format parametrów to: nazwa pliku ~ nowy rozmiar

Nazwa pliku to nazwa pliku wyjściowego. Może być zakodowany na stałe (np. „Out.jpg”) lub zawierać makra:% filename (nazwa pliku + rozszerzenie),% basename (nazwa pliku bez rozszerzenia),% ext (rozszerzenie). Zatem pełna nazwa pliku źródłowego to% basename.% Ext. Pełna ścieżka z nazwą pliku to makro% s. Jeśli nie chcesz nadpisywać pliku źródłowego, możesz użyć przedrostka lub przyrostka, aby zmienić nazwę pliku:

foo-%basename-bar.%ext~1024

W tym przykładzie nazwa wyjściowego pliku obrazu powinna być poprzedzona przedrostkiem „foo-” i zakończona końcówką „z -bar”.

Drugi parametr to nowy rozmiar. Może być w pikselach lub procentach (wystarczy dodać „%” po wartości):

foo-%basename.%ext~1024

foo-%basename.%ext~50%

Podmenu Komórki jest używane do przetwarzania wszystkich danych przypominających tabelę. Mogą to być tabele LaTeX, dane CSV itp. We wszystkich przypadkach musisz zdefiniować (w FIF) separator kolumn, aby powiedzieć TEA, jak analizować ciągi.

Funkcje >Komórki > Sortuj tabelę według kolumny ABC - sortuje tabelę tekstową według kolumny w kolejności ABC.

Ciąg formatu dla FIF: separator ~ numer kolumny

Zakres kolumn zaczyna się od 0, tj. pierwsza kolumna to 0, druga to 1 itd.

Na przykład mamy tabelę LaTeX z 4 wierszami, 2 kolumnami:

pies&kot
jagnię&krowa
wąż&wombat
wilk&tygrys

Teraz chcemy posortować je według drugiej (1) kolumny (gdzie „kot”, „krowa” itp.). Zaznacz tekst, umieść "&~1" w FIF i wywołaj tę funkcję. Tabela zostanie posortowana według drugiej kolumny.

Dobrze jest przetworzyć tabelę bez znaków końca wiersza (np. "\\" w LaTeX), a następnie dodać podziały wierszy do wyniku za pomocą funkcji „Zastosuj do każdego wiersza” z wartością FIF "%s\\".

Funkcje > Komórki > Zamień komórki - zamienia komórki tabeli kolumnami.

Ciąg formatu FIF: separator~kolumna1~kolumna2

Na przykład (z poprzednią tabelą przykładową) chcemy zamienić kolumnę 0 na 1 - kolumna z „cat” musi znajdować się przed kolumną „dog”. Zaznacz tekst, umieść go w FIF „& amp; ~ 0 ~ 1” i wywołaj Zamień komórki.

Funkcje > Komórki > Usuń według kolumny - usuwa kolumnę według podanego numeru (0..n).

Format FIF: separator~numer kolumny

0 - pierwsza kolumna, 1 - druga itd.

Funkcje > Komórki > Kopiuj według kolumn[y] - skopiuj kolumny do schowka.

Format FIF: separator~kolumna 1 [~ kolumna 2]

Jeśli nie podano kolumny 2, skopiowana zostanie kolumna 1, w przeciwnym razie - z kolumny 1 do kolumny 2.

Funkcje > Analiza > Statystyki tekstu - wyprowadza statystyki tekstu dla całego tekstu lub zaznaczenia do Logmemo.

Funkcje > Analiza > Długość słów - ile słów o długości od 1 do 32.

Funkcje > Analiza > UNITAZ - UNIversal Text AnalyZer, który oblicza częstotliwości słów i podaje inne powiązane informacje.

Funkcje > Analiza > Policz podciąg - liczbę wystąpień podciągu (umieść go w słynnym polu wejściowym) w dokumencie. Zobacz wynik na logmemo. Opcja „Wyszukiwanie z rozróżnianiem wielkości liter” jest używana jako opcja.

Funkcje > Analiza > Wyodrębnij słowa - wyodrębnia wszystkie słowa z dokumentu i umieszcza je w nowo utworzonym.

Funkcje > Tabele

W TEA, tabele to coś w rodzaju zestawu reguł dotyczących zastępowania tekstu. Najpierw musisz utworzyć plik z tabelą (format klucz = wartość ). Oto przykład:

kot=pies
miau=hau-hau

Następnie zapisz ten plik jako zastępczy plik tabeli w specjalnym katalogu TEA o nazwie tabele. Jest dostępny w panelu zakładek menedżera plików lub w oknie dialogowym zapisywania plików. Po zapisaniu tabeli pod jakąś nazwą pojawi się ona na stronie Funkcje > Menu Tabele.

Teraz spróbuj zastosować tę tabelę. Utwórz nowy plik i napisz tekst w ten sposób: Kot powie: „miau”. Wybierz ten tekst, a następnie wybierz swój stół z menu i zobacz, co się stanie. W rezultacie otrzymujemy nowe zdanie: Pies mówi: „hau-hau”. Zatem tabele są sposobem na wielokrotne zastąpienie.

Jeśli włączony jest tryb Regexp w Opcjach wyszukiwania, tabela użyje kluczy jako wyrażeń regularnych Perl5 (poprzez bibliotekę PCRE2 ).

W trybie menedżera plików tabele działają ze wszystkimi wybranymi plikami i zestawem znaków z selektora zestawu znaków w panelu menedżera plików.

Funkcje > Fragmenty

Fragment to fragment kodu, który możesz wstawić do tekstu. TEA przechowuje każdy fragment kodu jako pojedynczy plik w katalogu $HOME/.tea/snippets. Nazwa pliku skrawka to pozycja menu w Snippets-menu, tj. Zawartość menu Snippets to lista plików z $HOME/.tea/snippets. Aby utworzyć nowy fragment, zrób tak:

1. Napisz jakiś tekst.
2. Plik > Zapisz jako. Kliknij zakładkę „fragmenty”, aby otworzyć katalog skrawków, a następnie zapisz plik w zwykły sposób. Uwaga: nie zapisuj fragmentu pod nazwą żadnej innej pozycji menu TEA! Nazwy pozycji menu nie powinny się powielać, w przeciwnym razie TEA nie może poprawnie ustawić skrótów klawiaturowych. Jest to ważne, gdy podajesz nazwę dla fragmentu lub skryptu, szablonu, nazwy sesji itp.
3. Ciesz się :)

Możesz utworzyć urywek, który w jakiś sposób wykorzystuje zaznaczenie tekstu. Na przykład chcesz utworzyć fragment, który zamyka wybrany tekst w niektórych tagach HTML. Makro % s reprezentuje zaznaczenie tekstu. Oto przykład takiego fragmentu:

<a href="%s">%s</a>

Po zastosowaniu tego fragmentu %s zostanie zastąpiony wybranym tekstem. Jeśli żaden tekst nie jest zaznaczony, fragment kodu zostanie wstawiony do tekstu (ale bez makra "%s").

Aby otworzyć fragment kodu do edycji, użyj zakładki katalogu fragmenty w menedżerze plików TEA lub w oknie dialogowym Zapisz/otwórz plik. Stamtąd możesz również zmienić nazwę lub usunąć fragmenty (użyj menu kontekstowego - kliknij prawym przyciskiem myszy - w oknach dialogowych Zapisz/otwórz plik).

Funkcje > Skrypty

Skrypty z punktu widzenia użytkownika

Dzięki TEA możesz używać skryptów do przetwarzania zaznaczonego tekstu, tak jak używasz wbudowanych funkcji TEA. Jeśli skrypt obsługuje jakieś parametry, umieść je w polu Znane.

TEA może współpracować ze skryptami napisanymi w formacie wsadowym języków Python, Perl, Ruby, Lua, Bash (Sh), 2/REXX i Windows. Aby zainstalować skrypt, po prostu umieść go w  $HOME/.config/tea/scripts (możesz użyć skrótu „scripts” w oknie dialogowym Zapisz jako, aby zapisać plik jako skrypt TEA). TEA „zobaczy” nowo zainstalowane skrypty po ponownym uruchomieniu lub po zapisaniu skryptu z TEA. Zarządzanie skryptami jest podobne do fragmentów.

Skrypty z punktu widzenia programisty

Jak napisać skrypt dla TEA? To całkiem proste. Ale pierwszą rzeczą, którą musisz wiedzieć, jest to, w jaki sposób TEA przekazuje tekst do skryptu i jak TEA przyjmuje przetworzony tekst z powrotem, aby zastąpić nim zaznaczenie.

TEA uruchamia każdy skrypt z jednym lub dwoma parametrami. Oba są nazwami plików. Pierwszy plik zawiera tekst (UTF-8). Ten tekst jest zaznaczonym tekstem, który TEA pobiera w bieżącym dokumencie. Drugi plik (jeśli istnieje) zawiera tekst (UTF-8) z pola wejściowego Znane. Twój skrypt może więc odczytać pierwszy plik, aby uzyskać dane tekstowe, i odczytać drugi plik, aby uzyskać opcjonalne parametry skryptu.

Bądź ostrożny z obsługą tekstu - TEA wewnętrznie działa z kodowaniem UTF-8, więc całe przetwarzanie tekstu w skrypcie musi być bezpieczne dla UTF-8. OK, zobaczmy teraz, jak skrypt może zwrócić przetworzony tekst. Po prostu zapisz ten przetworzony tekst ponownie do pierwszego pliku, tj. Do pliku, którego nazwę przyjmujesz z pierwszego parametru twojego skryptu.

Poniżej tych linii zobaczysz przykład skryptu Python z bezpiecznym kodem UTF8, który pobiera tekst, zamienia wielkość liter i zwraca przetworzony tekst z powrotem do TEA.

import sys
import string
import codecs
f = codecs.open(sys.argv[1], "r", "utf-8" )
u = f.read()
f.close
u = u.swapcase()
f = codecs.open(sys.argv[1], "w+", "utf-8" )
f.write (u)
f.close

sys.argv[1] zawiera nazwę pliku z tekstem z TEA. Czytamy całą zawartość tego pliku, następnie przetwarzamy tę zawartość, a następnie zapisujemy ją z powrotem do sys.argv[1]-file. Voila! Zwróć uwagę, jak używać kodeków.

I kolejny przykład - kalkulator „wbudowany”. Tutaj nie używamy żadnych kodeków, ponieważ pracujemy z danymi liczbowymi:

import sys
f = file (sys.argv[1], 'r')
s = f.read()
f.close
t = eval (s)
f = file (sys.argv[1], 'w+')
f.write (str (t))
f.close

Ale co, jeśli potrzebujemy uzyskać do skryptu dodatkowe parametry użytkownika? Są dostępne jako łańcuch i można je odczytać z pliku o nazwie sys.argv [2] (w Pythonie). W Bash-script, użyj $1, aby uzyskać pierwszy parametr i $2, aby uzyskać drugi. Podsumowując, drugim parametrem jest plik, który zawiera tekst z pola wejściowego Znane. Zauważ, że skrypty Ruby przyjmują pierwszy parametr w ARGV[0], a drugi w  ARGV[1].

Kilka uwag na temat skryptów BASH i rzeczy z sed/ed/etc. Sztuczka polega na użyciu pliku czasowego. Najpierw skopiuj $1 do jakiegoś pliku temp., następnie przetwórz ten plik tymczasowy i umieść wynik z powrotem w $1. Oto przykład (zastąpienie słowa „pies” słowem „kot”) takiego skryptu:

cp $1 /tmp/xyz.xyz
sed 's/pies/kot/g' /tmp/xyz.xyz > $1

Jeśli chcesz wnieść jakieś skrypty do repozytorium skryptów witryny TEA, rozważ nadanie skryptu statusu domeny publicznej. Lub przynajmniej z wolną licencją. Przyda się również umieszczenie napisów końcowych i opisu w komentarzach w swoim skrypcie.

Sortuj > Sortuj z uwzględnieniem wielkości liter - sortuje wybrane wiersze alfabetycznie z uwzględnieniem wielkości liter.

Sortuj > Sortuj z uwzględnieniem wielkości liter, używając separatora - sortuje podciągi oddzielone ogranicznikiem. Umieść separator w FIF, a następnie użyj tej funkcji na tekście. W ten sposób możesz sortować "trzy | cztery | jeden | dwa" za pomocą "|" jako separator.

Sortuj > Sortuj według długości - sortuje wybrane wiersze według rozmiaru. Podaj rozmiar w FIF.

Sortuj > Odwróć listę - odwraca kolejność wierszy. Było:

Pies
Kot
Krowa

Będzie:

Krowa
Kot
Pies

Sortuj > Odwróć listę za pomocą separatora - odwraca kolejność podciągów oddzielonych separatorem w FIF.

Filtr > Usuń duplikaty - przydatne, gdy lista ciągów zawiera zduplikowane elementy. Wszelkie zduplikowane elementy PO oryginalnym zostaną usunięte. Może działać wolno na wolnych komputerach.

Filtr > Usuń puste linie - puste linie są tak samo przydatne jak puste oczy, w przeciwieństwie do pustych butelek. W związku z tym potrzebne jest narzędzie do usuwania pustych ciągów. I TEA to ma.

Filtr > Usuń linie < Rozmiar N - usuwa wiersze o długości mniejszej niż N. N to liczba znaków. Wpisz liczbę w polu Znane.

Filtr > Usuń linie > Rozmiar N - działa jak poprzednia funkcja, ale usuwa wszystkie ciągi dłuższe niż N znaków.

Filtr > przez powtórzenia - pozostawia ciągi ze wzorem, gdzie "0" to dowolny znak, a "1" to ten sam znak.

Na przykład wzorzec "kitten" to  "001100", gdzie "11" oznacza "tt".

Filtr > Filtruj za pomocą wyrażenia regularnego - filtruj listę przy użyciu wyrażenia regularnego. Aby uzyskać więcej informacji, zobacz www.regular-expressions.info. Jednak tutaj jest krótki przykład. Załóżmy, że masz listę:

hello.doc
example.txt
nature.jpeg
mytext.txt

I chcesz filtrować wszystkie elementy za pomocą rozszerzenia "txt". Zatem wyrażenie regularne (regexp) dla tego będzie wyglądać następująco:

.*\.txt$

Umieść go w polu Znane, zaznacz tekst i zastosuj funkcję. Voila!

Filtr > Usuń po separatorze w każdym wierszu. Usuwa podciąg po separatorze w każdym wybranym wierszu. Musisz umieścić separator w FIF. Na przykład separatorem jest ")", a wybrany tekst to:

1.) Nirvana
2.) Grajdanskaya Oborona
3.) The Cranberries

Zastosuj filtr, a otrzymasz:

1.
2.
3. 

Filtr > Usuń przed separatorem w każdym wierszu. Usuwa podciąg PRZED ogranicznikiem w każdym wybranym wierszu. Musisz umieścić separator w FIF. Na przykład separatorem jest ")", a wybrany tekst to:

1.) Nirvana
2.) Grajdanskaya Oborona
3.) The Cranberries

Zastosuj filtr, a otrzymasz:

Nirvana
Grajdanskaya Oborona
The Cranberries

Math > deg min sec > dec degrees konwertuje współrzędne z formatu „stopnie, minuty i sekundy” na „stopnie dziesiętne”. Przykład:

było: 40° 26′ 46″ N 79° 58′ 56″ W, wynik: 40.446° N 79.982° W

Math > dec degrees > deg min sec konwertuje współrzędne z formatu „stopni dziesiętnych” na „stopnie minuty sekundy”. Przykład:

było: 40.446° N 79.982° W, wynik: 40° 26′ 46″ N 79° 58′ 56″ W.

Math > Suma według ostatniej kolumny - sumuje wartości każdej ostatniej kolumny w zaznaczeniu. Na przykład możemy obliczyć prostą listę tekstową:

Marchew 14
Jabłka 45,8
Pomarańcze 11

Math > Oblicz - oblicza wyrażenie wybrane w tekście. Na przykład możesz napisać 2+2, a następnie wybrać go i zastosować Oblicz. Po takiej akcji zobaczysz wynik na logmemo. W swoim wyrażeniu można użyć następujących operatorów: +, -, *, /, ^(potęga),% (pobierz wartość procentową). Nawiasy klamrowe są obsługiwane. Oto kilka przykładów:

2/3*123
5+5+5
1-200*89
2^8
250%4 //what is 4 per cent of 250?

Math > Dziesiętne na dwójkowe - konwertuje dziesiętną liczbę całkowitą na jej binarną reprezentację. Przykład: było 255, będzie 0000 0000 0000 0000 0000 0000 1111 1111.

Math > Dwójkowe na dziesiętne - działa tylko z liczbami binarnymi bez znaku int.

Math > Odwróć bity (dopełnienie bitowe) - Odwróć bity (dopełnienie bitowe) - użyj tej funkcji do odwrócenia bitu wartości binarnej. Przykład: było 0000 0010, będzie 1111 1101.

Math > Wylicz wiersze z zaznaczonego tekstu. Umieść ciąg parametrów tej funkcji w polu Znane dane wejściowe. Składnia: step~zero_padding~prefix.

Krok to krok inkrementacji. Na przykład krok 1 daje liczby 1, 2, 3 itd. Krok = 10 da nam 10, 20, 30.

Wypełnienie zerami określa, ile cyfr znajduje się w każdej liczbie. Na przykład, jeśli wypełnienie zerami, czyli zero padding = 3, a krok, czyli step = 1, otrzymamy 001, 002, 003 itd.

Prefix to przedrostek po liczbie, a przed ciągiem. Przedrostek może mieć znaczące spacje końcowe.

Oto kilka przykładów i wyników:

przykład 1, parametry to "1~3~) " (bez cudzysłowów). Wynik to:

001) dog
002) cat
003) mouse

przykład 2, parametry to "10~1 " (bez cudzysłowów). Wynik to:

10 dog
20 cat
30 mouse

przykład 3, parametr to "1"  (bez cudzysłowów). Wynik to:

1dog
2cat
3mouse

Możesz użyć tej funkcji nawet bez parametrów - w takim przypadku padding = 0, step = 1, a prefiks jest pustym ciągiem. Możesz użyć pojedynczego parametru step lub pary step i dopełnienia lub wszystkich trzech parametrów.

Math > Arabskie na rzymskie - konwertuje „zwykłe” liczby na rzymskie (np. 20 na XX).

Funkcje > Kod Morse'a

From Morse To English. Z alfabetu Morse'a na angielski. Nie jestem guru alfabetu Morse'a, ale mam nadzieję, że mój wysiłek, aby wdrożyć taką funkcję jest słuszny. Za pomocą tej pozycji menu możesz przetłumaczyć angielski tekst na alfabet Morse'a. Na przykład było:

tea and coffee

Będzie:

-..-.- -. -.. -.-. ---..-...-...

Zauważ, że TEA umieszcza pojedynczą spację między dwoma kodami Morse'a.

From English To Morse. Odwrotna akcja dla poprzedniej funkcji. Możesz zdekodować dowolną wiadomość zakodowaną w alfabecie Morse'a. Musisz wiedzieć, że TEA zakłada, że ​​między kodami Morse'a są pojedyncze spacje, tak jak w powyższym przykładzie.

Tekst > Kompresuj - usuwa wszystkie białe znaki w zaznaczeniu.

Tekst > Anagram - znajdź wszystkie anagramy zaznaczonego tekstu. Może być BARDZO powolny i zjada dużo pamięci, jeśli słowo jest duże. Więc TEA nie zamarznie, po prostu myśli. Przykład anagramu dla „dog”:

dgo
dog
gdo
god
odg
ogd

Tekst > Usuń formatowanie w każdym wierszu - usuwa formatowanie (tabulatory, nowe wiersze, podwójne spacje itp.) W każdym wybranym wierszu.

Tekst > Usuń formatowanie - usuwa formatowanie z całego zaznaczonego tekstu, dzięki czemu tekst zostanie przekonwertowany na jeden duży ciąg bez nowych linii, podwójnych spacji itp.

Tekst > Zastosuj do każdego wiersza to potężne narzędzie do dodawania tekstu do każdego wiersza zaznaczenia. I znowu używamy pola Znane. Na przykład chcę dodać br-tag na końcu każdego wiersza. Więc wpisuję we wpisie:

%s<br>

Następnie stosuję tę funkcję, aby dodać znacznik br na końcu każdej linii. Makro %s wskazuje na każdy wiersz wyboru. Więc weź pod uwagę, że %s reprezentuje tekst w każdym wierszu. W innym przykładzie chcę zawrzeć każdy wiersz w parze znaczników li. Formuła pola wejściowego Znane będzie wyglądać następująco:

<li>%s</li>

I kolejny przykład:

<a href="%s">%s</a>

Możesz także zastosować fragmenty (z katalogu fragmentów TEA). Aby to zrobić, użyj @@ snippetname na FIF. Na przykład, jeśli masz wycięty fragment o nazwie „myitalic”, użyj @@ myitalic. Jak pamiętasz, fragmenty mogą zawierać makro% s zastępujące zaznaczony tekst. W przypadku funkcji „Zastosuj do każdego wiersza”% s będzie oznaczać tekst każdej zaznaczonej linii.

Tekst > Uciekaj przed znakami specjalnymi (Escape regexp) - przydatne przy ucieczce przed znakami specjalnymi wyrażenia regularnego, takimi jak $, *, +, [,] itd.

Tekst > Sprawdź dopasowanie wyrażenia regularnego (regexp) - sprawdź wybrany tekst względem wyrażenia regularnego (umieść wyrażenie regularne na FIF).

Funkcje > Sprawdzanie pisowni

TEA może korzystać z mocy dwóch silników sprawdzania pisowni: Aspell i Hunspell. TEA można zbudować z nimi lub bez nich. W pozytywnym przypadku, aby przełączać się między silnikami, użyj listy Mechanizm sprawdzania pisowni na stronie Opcje - Funkcje.

Silnik Aspell używa słowników zainstalowanych w całym systemie (użyj menedżera pakietów dystrybucji Linuksa, aby je zainstalować). W systemie Windows potrzebujesz kilku dodatkowych rzeczy do zrobienia. Najpierw pobierz i zainstaluj pełny instalator Aspell ze strony http://aspell.net/win32/. Następnie z tej samej strony pobierz i zainstaluj kilka słowników. Musisz je umieścić w katalogu, w którym jest zainstalowany Aspell. Domyślnie (w Windows) jest to C:\Program Files\Aspell. Na koniec w TEA musisz ustawić ścieżkę do Aspell - przejdź do Opcje - Funkcje, zmień opcję Katalog Aspell na poprawną i zrestartuj TEA. 

W przypadku Hunspell musisz również samodzielnie ustawić ścieżkę do słowników, ręcznie. Możliwe, że słowniki są już zainstalowane, ponieważ Hunspell to mechanizm sprawdzania pisowni dla przeglądarek Firefox i LibreOffice.org. Na przykład Firefox (w przypadku instalacji lokalnego katalogu domowego użytkownika) przechowuje słowniki w katalogu firefox/dictionaries. Możesz także pobrać słowniki z repozytoriów rozszerzeń LibreOffice.

Istnieją pliki z rozszerzeniem OXT. Wewnętrznie są to zwykłe pliki ZIP. Więc pobierz i zmień nazwę filename.oxt tofilenname.zip. Utwórz katalog na słowniki i rozpakuj pliki.aff i.dic z ZIP. Następnie wybierz ten katalog za pomocą przycisku Wybierz w opcji Opcje - Funkcje - Ścieżka do słowników Hunspell.

Po skonfigurowaniu słownika musisz przejść do menu Funkcje - Języki sprawdzania pisowni i wybrać język, którego potrzebujesz. Bez tego ostatniego kroku sprawdzanie pisowni zakończy się niepowodzeniem. Również wybierz język w ten sposób po zmianie silnika z Aspell na Hunspell lub odwrotnie.

Po dodaniu słownika dobrze jest ponownie uruchomić TEA.

Języki sprawdzania pisowni - w tym menu możesz znaleźć listę słowników zainstalowanych dla aspell (z dystrybucji lub czegoś podobnego). Aby sprawdzić pisownię, użyj jednej z tych pozycji menu. Działa z całym tekstem, a nie tylko z zaznaczeniem.

Pozycja menu z nazwą języka służy również do ustawienia domyślnego języka sprawdzania pisowni. Wartością domyślną jest język Twojej lokalizacji. Aby dokonać prostego sprawdzenia z domyślnym (ostatnio wybranym) językiem, użyj opcji menu Sprawdź pisownię.

Sugeruj - pokazuje listę z sugestiami dotyczącymi bieżącego błędnie napisanego słowa (właściwie słowa pod kursorem). Aby skorzystać z tej funkcji, najpierw użyj Sprawdzania pisowni. Prawdopodobnie niepoprawne słowa zostaną podkreślone.

Możesz je naprawić za pomocą funkcji Sugeruj lub ręcznie. Kiedy naprawisz błąd, zobaczysz, że poprawione słowo jest kolorowe jak poprzednio. Dzieje się tak ze względu na naturę modułu sprawdzania pisowni TEA - aktualizuje się on ręcznie, więc aby zaktualizować znaki błędów, należy ponownie wybrać tę samą pozycję menu sprawdzania pisowni. Aby wyłączyć znaki błędów, użyj przycisku Widok > Ukryj znaki błędów.

Dodaj do słownika - dodaj słowo podkreślone jako nieprawidłowe do lokalnego słownika użytkownika. Właściwie ta funkcja dodaje do słownika dowolne słowo pod kursorem.

Pamiętaj, że niektóre poprawne słowa TEA mogą oznaczać jako nieprawidłowe. Jest to spowodowane obecnym parsowaniem słów, zostanie to naprawione najszybciej, jak to możliwe.

Usuń ze słownika - ta funkcja jest dostępna tylko dla Hunspell. Zaktualizowany słownik zostanie załadowany po następnej sesji.

IDE

TEA ma kilka podstawowych funkcji IDE. Używanie TEA jako IDE jest proste.

Po pierwsze, musisz utworzyć plik projektu. Aby to zrobić, po prostu utwórz nowy plik i wstaw szablon pliku projektu TEA z Funkcje - Umieść - Szablon projektu TEA.

Zapisz go w głównym katalogu źródłowym swojego projektu, pod dowolną nazwą, ale z rozszerzeniem "teaproject", tj. filename.teaproject.

plik teaproject jest prostym plikiem tekstowym key=value (klucz=wartość). Zawiera ustawienia projektu. W ten sposób możesz przechowywać wiele plików teaproject w tym samym katalogu. Oto na przykład pliki teaproject TEA dla różnych systemów kompilacji (qmake, cmake i meson):

tea-make.teaproject

command_build=make
command_clean=make clean
command_run=bin/tea --m&

tea-meson.teaproject

dir_build=b
command_build=ninja
command_clean=ninja -t clean
command_run=./tea --m&

tea-cmake.teaproject

dir_build=cmake
command_build=make
command_clean=make clean
command_run=./tea --m&

Składnia jest jasna:

dir_build  - katalog, w którym command_build będzie działać dla budowania. Jeśli nie ma dir_build, to katalog pliku teaproject zostanie użyty jako  dir_build. Katalog może być katalogiem bezwzględnym, jeśli nie, zostanie użyty katalog względny do katalogu pliku teaproject.

command_build może być dowolne, jak chcesz: make, ninja itp.

command_run - działa na  dir_build, aby uruchomić skompilowany plik binarny.

command_clean - uruchamia się pod adresem  dir_build, aby wyczyścić projekt.

Kiedy otwierasz, zapisujesz lub przełączasz (za pomocą zakładki) do pliku teaproject, TEA wczytuje go do wewnętrznych struktur i używa poleceń z teaproject po uruchomieniu elementów menu z menu IDE : Uruchom program, Kompiluj program, Wyczyść program. Należy pamiętać, że TEA czyta ZAPISANĄ zawartość pliku teaproject, a nie bieżący stan edycji.

Menu IDE jest przydatne w połączeniu z menu Widok - Profile. Na przykład, możesz utworzyć "ide-profile" z pewnymi ustawieniami czcionki, szerokim oknem, bez zawijania wyrazów; i "text-profile" z innym zestawem czcionek, zawijaniem wyrazów, mniejszym oknem itp. Następnie możesz szybko przełączać się między profilami za pomocą menu lub skrótów klawiszowych.

Podczas budowania programu TEA wyświetla dane wyjściowe kompilatora w Logmemo. Kliknij dwukrotnie pogrubiony tekst (nazwa pliku: linia: kolumna), aby otworzyć błąd lub ostrzeżenie w edytorze.

Uruchom

To menu jest przeznaczone do uruchamiania bieżącego pliku za pomocą zewnętrznych programów, na przykład przeglądarek. Aby dodać kilka pozycji do menu, przejdź do Plik > Konfiguracje > Konfiguracja listy programów, edytuj ten plik, a następnie zapisz go. Plik konfiguracyjny ma prosty format podobny do ini. Każda linia ma następujący format:

meni item caption=command line  (podpis pozycji menu=wiersz poleceń)

Przykład wiersza poleceń dla przeglądarki zaczynającej się:

FF=firefox %s

%s jest makrem potrzebnym dla aktualnej nazwy pliku, więc użyj go we właściwym miejscu. W przypadku Win32 musisz użyć pełnej ścieżki do pliku wykonywalnego, z podwójnymi cudzysłowami (makro% s również potrzebuje cudzysłowów!). Na przykład:

opera="C:\Program Files\Opera\opera.exe" "%s"

Dostępne są bardziej precyzyjne makra: %basename, %filename, %ext, %dir. Pozwala to na wykorzystanie fragmentów ścieżki. Tak więc, jeśli mamy pełną ścieżkę "/mnt/foo/bar.txt", %dir = "/mnt/foo", %basename = "bar", %filename = "bar.txt", %fext = "txt". Tak więc pełną ścieżkę w wierszu poleceń można przedstawić w następujący sposób: %dir/%basename.%ext

Aby otworzyć aktualnie wybrany plik lub plik w miejscu kursora na tekście (jak przy F2, ale z zewnętrznym programem) użyj makra% i.

Na przykład, jeśli chcesz otworzyć plik obrazu za pomocą gimp, umieść następującą linię poleceń w pliku konfiguracyjnym:

gimp=gimp %i

Następnie w pliku HTML lub LaTeX przesuń kursor na nazwę pliku obrazu. Następnie wywołaj pozycję menu „gimp” z menu "Uruchom". Ciesz się.

Nawigacja

Etykiety, Aktualizuj etykiety. Możesz umieścić w tekście specjalnie sformatowane etykiety, a następnie przełączać się między nimi z menu Etykiety (or lista rozwijana na pasku narzędzi). Jak to działa? Etykieta to po prostu tekst objęty sekwencją otwierającą i końcową (domyślnie "[?" i "?]", Wartości te można przedefiniować w Opcje - Funkcje - Etykiety).

Na przykład umieść w swoim tekście kilka wierszy, takich jak "[? dogs ?]", "[? cats ?]". Następnie poinformuj TEA o tych etykietach za pomocą pozycji menu Aktualizuj etykiety lub naciśnij na pasku narzędzi. Menu Etykiety i lista rozwijana będą wypełnione etykietami "dogs" i "cats". Możesz wybrać je z menu lub listy i przejść do tekstu obok wybranej etykiety. Pamiętaj, że etykiety są aktualizowane tylko ręcznie.

Idź do wiersza - przesuwa kursor do numeru wiersza, który podasz w polu Znane.

Zapisz pozycję/Idź do zapisanej pozycji - te funkcje są dobre, aby szybko przeskoczyć przez duży tekst, gdy chcesz coś spojrzeć w jedno miejsce, a następnie wrócić do aktualnie edytowanego miejsca itp.

Menedżer plików

Fm oznacza Menedżera plików. To menu zawiera funkcje związane z menedżerem plików TEA. Na przykład możesz zmienić nazwy plików lub katalogów lub uzyskać sumę kontrolną MD5. Przeglądaj i używaj!

W przypadku niektórych funkcji, takich jak suma kontrolna MD5, najpierw wybierz plik, a następnie zastosuj funkcję.

Fm > Podmenu Multi-rename pomaga elastycznie zmieniać nazwy plików. Wszystkie te funkcje używają parametru z FIF.

Fm > Wiele zmian nazwy > Nazwy plików z odstępami zerowymi - dołącz zera do każdej wybranej nazwy pliku, której numeracja jest częścią nazwy pliku. Na przykład: strona1, strona100. Takich plików nie można poprawnie posortować.

Ustaw więc długość nazwy pliku na FIF. Pliki będą numerowane poprawnie, ale po usunięciu wszystkich znaków niebędących cyframi. Przykład:

Mamy pliki:

page1.txt
page100.txt

Wpisz 5 do FIF, zastosuj funkcję.

Teraz mamy:
00001.txt
00100.txt

Fm > Wiele zmian nazwy > Usuń N pierwszych znaków w nazwach plików - N to liczba z FIF. Usuwa N pierwszych znaków z każdej wybranej nazwy pliku.

Fm > Wiele zmian nazwy > Zastąp w nazwach plików - zastępuje podstawianie w każdej wybranej nazwie pliku. Format FIF:  old~new ( stary~nowy). Przykład:

.jpeg~.jpg

Fm > Wiele zmian nazwy > Zastosuj szablon - zastosuj szablon do każdej wybranej nazwy pliku. Dostępne makra z częściami oryginalnej nazwy pliku: %filename (= filename.ext), %ext (= ext), %basename ( = filename). Na przykład, musisz zmienić nazwę 01.jpeg, 02.jpeg to 01.jpg, 02.jpg. Tak więc szablon musi wyglądać następująco:  %basename.jpg, tzn.%basename (nazwa pliku bez rozszerzenia) zostanie uzupełnione o ".jpg".

Fm > Wybierz według wyrażenia regularnego/Odznacz według wyrażenia regularnego - użyj tej funkcji, aby zaznaczyć lub odznaczyć pliki według wzorca wyrażenia regularnego. Na przykład, aby wybrać wszystkie pliki txt w bieżącym katalogu, umieść następujące wyrażenia regularne w FIF: ".*\.txt$" (bez cudzysłowów!), a następnie użyj Wybierz według wyrażenia regularnego ( Select by regexp). Następnie możesz nacisnąć przycisk Otwórz, aby otworzyć wszystkie wybrane pliki.

Fm > Informacje o pliku > Pełne informacje - zawiera pełne informacje o pliku (czas utworzenia/modyfikacji itp.). Dla 16-bitowych plików PCM WAV oblicza RMS (dla obu kanałów). W przypadku innych plików WAV pokazuje tylko właściwości, takie jak głębia bitowa itp.

Fm > Informacje o pliku > Policz wiersze w wybranych plikach - pamiętaj, że zliczane będą również puste wiersze.

Fm > Obrazy > Skalowanie procentowe/Skalowanie według boku - wśród innych funkcji niezwiązanych z tekstem TEA ma możliwość wsadowej konwersji obrazów. Jest to przydatne, gdy chcesz je przeskalować lub przekonwertować na inny format.

Na przykład chcemy przeskalować wybrane obrazy o 50 procent, umieścić przetworzone obrazy w nowym katalogu i spakować ten katalog:

1. Wybierz pliki w menedżerze plików. Aby je zaznaczyć, pomnożyć, użyj Ctrl-Click (lub Ctrl-A, aby zaznaczyć wszystkie).
2. Umieść żądaną wartość procentową w słynnym polu wprowadzania. Wartość musi być prostą liczbą całkowitą bez znaku %.
3. Na stronie Opcje > Obrazy zaznacz opcję Katalog Zip z przetworzonymi obrazami.
4. Zastosuj funkcję Skaluj w procentach.

Następnie TEA tworzy nowy katalog jako podkatalog aktualnego. Ten nowy katalog ma losową nazwę zaczynającą się od "images-out-". W tym katalogu TEA umieszcza wszystkie przekonwertowane obrazy, pozostawiając niezmodyfikowane oryginały.

Możesz chcieć dokładniej przeskalować obrazy i ręcznie zdefiniować rozmiar. Aby to zrobić, umieść rozmiar strony obrazu w słynnym polu wprowadzania i zastosuj Skaluj według boku ( Scale by side). Jak wiesz, każde zdjęcie ma wymiary. Na przykład 640x480. Jeśli szerokość jest większa niż wysokość, obraz ma orientację poziomą (TEA nie obsługuje danych EXIF) i odwrotnie. Możemy więc użyć boku - większego boku obrazu. Zwykle wszystkie zdjęcia mają standardowe wymiary z uwzględnieniem pewnych proporcji, więc większy bok dla wszystkich zdjęć jest taki sam.

Na przykład chcemy przeskalować wszystkie obrazy (zdjęcia o odpowiednich proporcjach/wymiarach) do szerokości 640 i, jeśli obraz ma orientację pionową, do wysokości 640. Umieść to 640 w Znanym polu wprowadzania i zastosuj Skaluj według boku.

Opcje konwersji obrazu znajdują się na stronie Opcje > Obrazy. Za pomocą listy Format wyjściowy konwersji obrazu można ustawić format wyjściowy. Jakość obrazów wyjściowych (od 0 do 100) jest używana głównie w przypadku plików JPEG. Wartość domyślna to -1, co oznacza wewnętrznie zdefiniowaną wartość. Pole wyboru Skaluj obrazy z filtrowaniem bilinearnym wpływa na obrazy podczas ich skalowania, aby je wygładzić i uczynić mniej "szorstkimi".

Fm > Obrazy > Utwórz galerię internetową - tak, TEA może stworzyć prostą galerię internetową opartą na oldschoolowych tabelach. Wykonaj następujące kroki:

1. Umieść swoje obrazy w jakimś katalogu. Zapisz tam plik html. Otwórz ten plik za pomocą TEA.

2. Przejdź do menedżera plików TEA, wybierz pliki graficzne, które chcesz umieścić w galerii internetowej.

3. Użyj funkcji Utwórz galerię internetową.

4. Zostaną utworzone miniatury i tabela HTML.

Galerię można dostosować za pomocą przycisku Opcje > Obrazy > Opcje galerii internetowej. Istnieje rozmiar miniatury, liczba komórek w wierszu itd.

TEA jako urządzenie pakujące/rozpakowujące ZIP

TEA zapewnia proste funkcje do pakowania plików (nie katalogów) do archiwum. Aby to zrobić, ustaw najpierw nazwę archiwum (zrób to w katalogu, w którym chcesz utworzyć archiwum):
Fm > ZIP > Utwórz nowy plik ZIP
Zostanie wyświetlone okno dialogowe do wprowadzania danych. Następnie przejrzyj swoje katalogi. Wybierz dowolne pliki i użyj polecenia Dodaj do ZIP, aby dodać je do archiwum. Uwaga - wszystko to dzieje się wirtualnie, dopóki nie użyjesz funkcji Zapisz ZIP. Aby fizycznie spakować pliki, zastosuj Zapisz ZIP.

Wszystkie pliki zostaną spakowane do archiwum, które ma jedną nazwę podkatalogu jako nazwę archiwum, ale bez rozszerzenia.zip.

Możesz także rozpakować wybrany plik ZIP do bieżącego katalogu. Użyj funkcji Rozpakuj ZIP do bieżącego katalogu. Zwróć uwagę, że zestaw znaków nazw plików (wewnątrz archiwum ZIP) jest kontrolowany przez opcje Opcje - Wspólne - Rozpakowywanie ZIP: zestaw znaków nazw plików/Pakowanie ZIP: zestaw znaków nazw plików. Tak więc, jeśli zobaczysz dziwne znaki po rozpakowaniu pliku ZIP, spróbuj Opcje zestawu znaków. Aby zrobić to mniej bolesnym, wybierz zestaw znaków przy rozpakowywaniu ZIP: zestaw znaków nazw plików i wypróbuj, jak wygląda nazwa pliku - użyj opcji menu Lista zawartości ZIP. Po prostu wyświetla zawartość archiwum (nazwy plików) w Logmemo, używając wybranego zestawu znaków.

I jeszcze jedna uwaga - TEA nie obsługuje plików ZIP chronionych hasłem.

Widok

Tryb podświetlania - z tego podmenu można ręcznie ustawić tryb podświetlania. Jest to przydatne w przypadku nowych niezapisanych plików lub pliku bez rozszerzeń.

Ukryj znaki błędów - ukrywa podkreślone znaki, które zostały ustawione przez moduł sprawdzania pisowni.

Przełącz zawijanie słów - włącz/wyłącz zawijanie słów w bieżącym dokumencie.

Palety - z tego podmenu można wybrać schemat kolorów, który ma wpływ na obszar edycji tekstu. Aby zdefiniować własne kolory, możesz stworzyć własną paletę w oparciu o kilka wbudowanych palet (patrz katalog źródłowy, podkatalog palet). Następnie zapisz paletę w katalogu $HOME/.config/tea/palettes i wybierz jedną z menu Palettes.

TEA częściowo obsługuje motywy Eclipse IDE ( eclipsecolorthemes.org ) - wystarczy umieścić plik XML motywu w katalogu palet TEA i wybrać go z menu jako natywna paleta TEA. To jest cud! Należy pamiętać, że format palety TEA obsługuje więcej elementów wyróżniających niż wtyczka Eclipse Color Theme, więc motywy Eclipse nie będą tak kolorowe, jak natywne palety TEA.

Profile - to menu pozwala na przełączanie się między profilami widoku (zawierają parametry takie jak pozycja okna, rozmiar, czcionki, zawijanie wyrazów i ustawienia numeracji wierszy itp.). Możesz więc zapisać swoje bieżące ustawienia za pomocą funkcji Zapisz profil, a nazwany profil pojawi się w menu Profile. W dowolnym momencie możesz wybrać, aby przywołać zapisane ustawienia.

Zakładka Opcje

Na zakładce Klawiatura możesz przypisać skróty do elementów menu. Aby ustawić nowy klawisz skrótu, wybierz pozycję menu z listy, a następnie naciśnij kombinację klawiszy skrótu przy wpisie po prawej stronie listy i naciśnij przycisk Przypisz. Aby usunąć klawisz skrótu, wybierz pozycję menu z listy i naciśnij przycisk Usuń. Aby zmienić przypisanie już używanego skrótu, wykonaj najpierw Usuń dla tego skrótu.

W systemie Linux możesz użyć joysticka/joypada do przewijania tekstu i przesuwania kursora. Jest domyślnie wyłączone i można je włączyć za pomocą Preferencje - Wspólne - Użyj joysticka jako klawiszy kursora. Po włączeniu odczytuje urządzenie joysticka co 100 milisekund. Byłem potrzebny w obsłudze joysticka, aby przewijać teksty do filmów z YouTube online, kiedy muszę siedzieć w tej samej pozycji.

Zawijanie słów - używaj zawijania słów globalnie dla wszystkich dokumentów, w tym nowych.

Użyj ustawienia zawijania z modułu podświetlania na stronie Interfejs oznacza, że ​​ustawienia zawijania wyrazów będą pobierane z modułów podświetlania składni, a nie z globalnej opcji zawijania tekstu. Niektóre moduły mają włączone zawijanie słów, a inne wyłączone. Na przykład dla C ++ ustawienie zawijania wyrazów jest włączone. Dla zwykłego tekstu lub dla HTML - jest wyłączone.

Klawiatura

Oprócz standardowych operacji klawiaturowych TEA obsługuje leworęczne operacje kursora, które można włączyć w Opcje - Wspólne - Użyj lewego Alt WASD.

Gdy ta opcja jest włączona, użyj Lewego Alt + WASD do przesuwania kursora, Lewego Alt + E i C do przewijania strony w górę/w dół. Użyj lewego klawisza Win z WASDEC, aby zaznaczyć tekst.

Sekretna moc

Poznaj prawdę! Gady zdominują ludzkość, jeśli nie masz odwagi i wiedzy na temat tajnych opcji TEA. Nie mają GUI i musisz je dodawać i edytować ręcznie w głównym pliku konfiguracyjnym TEA $HOME/.config/tea\tea.conf (lub w oknie, na dysku: \Documents and Settings\username\tea\tea.conf). Niektóre zmiany będą działać po ponownym uruchomieniu TEA. Więc poznaj prawdę:

recent_list.max_items=maximum recent list items, default 21

Uwagi

Obsługiwana jest starsza opcja wiersza poleceń "--charset=codepage". To polecenie ustawia stronę kodową dla następujących plików. Na przykład:

tea --charset=window-1251 file1.txt file2.txt --charset=utf-8 file3.txt

Domyślnie (jeśli nie zdefiniowano ręcznie zestawu znaków) TEA używa UTF-8.

Ten podręcznik i wszystkie materiały multimedialne TEA (palety, obrazy itp.) są własnością publiczną. Kod źródłowy TEA jest objęty licencją GPL v3 i domeną publiczną.

Pliki hl składni użytkownika można dodać do katalogu  $HOME/.config/tea/hls. Zobacz robocze pliki hl w katalogu źródłowym TEA, podkatalogu  hls. Zapraszam do przesyłania plików hl. Użyj odniesień kolorów z plików palet (spójrz na katalog  palettes w źródle). To znaczy użyj koloru "preproc" lub "types" zamiast wartości szesnastkowej.


Inne projekty autora

Moja witryna domowa z prozą, programami, muzyką i innymi rzeczami.

Moja witryna domowa z prozą, programami, muzyką i innymi rzeczami.

Moja muzyka


Wesprzyj rozwój

Jeśli chcesz wesprzeć rozwój TEA, oto moja strona Patreon..

tea-qt-63.3.0/manuals/ru.html000066400000000000000000004027621476733534200160030ustar00rootroot00000000000000 Руководство

Руководство к действию


Содержание



Введение
Интерфейс
Панель Даты
Файловый приказчик
Файл
Правка
Вёрстка
Поиск
Календарь
Функции
Функции - Проверка правописания
Запуск
ИДЕ
Фп
Нав
Вид
Настройки
Клавиатура
Слово про автосохранение
Тайная власть
Примечания
Другие проекты автора Поддержать разработку

Введение

История TEA началась в 2000 году, когда я, тогда еще пользователь Windows, для своих нужд создал текстовый редактор Typewriter. Спустя год я понял, что редактор может пригодиться не только мне, и поделился им с миром, переименовав в TEA. Эту историю подробно можно прочесть на страничке Музей ТИА.

Редактор был написан на Delphi и развивался ударными темпами, пока в 2003 году моей основной системой не стал Linux. В нем я воссоздал TEA с нуля, уже на языке Си с использованием тулкита Gtk+2. Конечно, имелись отличия между вындовой и линуксовой версиями, к тому же я продолжал работать над вындовыми ипостасями ТИА уже на других движках, что продолжалась по 2006-2007 годы.

К 2007 году я воплотил в линуксовом ТИА всё, что мне тогда было нужно. Он был портирован на тучу систем. Он работал, как часы. Исходник мне не нравился, ибо когда я начинал писать линусковый ТИА, то откат от ООП к процедурному программированию на Си и псевдо-ООП GTK плохо сказался на архитектуре программы. Мне не хотелось больше возиться с исходником, он был плох. Тогда я решил написать маленький офисный пакет, на С++, и избрал в качестве тулкита Qt. Вместо офисного пакета у меня снова стал получиться ТИА, поэтому я заморозил разработку сишного ТИА на GTK и продолжил разработку уже на C++/Qt. Вы читаете руководство как раз к этой испостаси ТИА.

ТИА всегда отражает мои текущие потребности в функциях текстового редактора. С самого начала интерфейс был вдохновлен двумя программами из звукомузыкального мира, Sound Forge и Impulse Tracker. Ощущение от последнего вылилось в интерфейс, где всё разбито на экраны (в современном контексте вкладки), а из Sound Forge я взял основной принцип - выделить данные, применить к ним эффект. В случае ТИА - функцию обработки текста.

Поскольку я использую ТИА постоянно, многократно каждый день, это является гарантией, что программа работает надежно. Со всеми ошибками я сталкиваюсь первым.

Основной сайт TEA - tea.ourproject.org. Есть еще старый, на SourceForge, он не обновляется, а с SourceForge невозможно удалить проект, поэтому я оставил там сайт, чтобы не было пустой страницы.

Пара слов о запуске TEA. В обычном режиме TEA хранит файлы настройки и прочие сопутствующие в $HOME/.config/tea (а под Windows в диск:\Documents and Settings\имя пользователя\tea). И это здорово! Но у некоторых пользователей возникает желание использовать portable-вариант TEA, запуская его например с флэшки. В таком случае надо, чтобы TEA хранил свои настройки на самой флэшке в каталоге, где находится исполняемый файл TEA. Чтобы TEA понял, откуда ему читать свои данные и куда их писать, есть ключик командной строки "--p". Если запустить TEA вот так: "tea --p", то редактор будет запущен в portable-режиме, и файлы его настроек и сопутствующие (включая crapbook, сниппеты и т.д.) будут сохраняться/читаться в каталоге с исполняемым файлом TEA, то есть на флэшке. А если запускать без ключика "--p", то всё будет как обычно, с настройками из стандартного места хранения. Настройки portable и обычного режима не переносятся автоматически туда-сюда и существуют параллельно.

Я видел в сети какие-то вындовые portable-версии TEA, так вот, их делал не я, я не знаю как они работают и что туда сунули. Официальная "моя" вындовая сборка выкладывается на гитхабе программы и доступна по ссылке с офсайта ТИА. Оттуда, кажется, ТИА попадает на разные софтпорталы, и это хорошо, лишь бы не делали репаки.

TEA в сборке для *NIX представляет собой один бинарный файл - "tea". Для запуска Windows-версии TEA достаточно содержимого каталога, куда установлен TEA - там есть все нужные ему библиотеки.


Интерфейс

Ежели считать сверху вниз, то в главном окне расположены:

1. Главное меню. Обратите внимание, что у каждого подменю есть эдакая полоска отрыва. Если взяться за нее, то подменю можно оторвать и поместить где-нибудь в сторонке. Меню зависит от текущей задачи. Например, в задаче "Правка" будут одни пункты, в "Наладке" часть скроется, в "Файлах" часть откроется. Это сделано, чтобы TEA не занимал много места в ширину. Экраны-то разные бывают. Забота о ближнем!

2. Область инструментальных панелей. Вообще говоря, панели эти перемещаются и вовне главного окна, а паркуются по внутренним его краям сверху, снизу, слева и справа. Чтобы отключить панель, сделать ее невидимой, щелкните на ней правой кнопкой мыши. Появится меню, в котором поставьте либо снимите галочку с названия панели. Если все панели выключены, то для вызова контекстного меню панелей щелкните правой кнопкой мыши по главному меню.

3. Область вкладок. Состоит из пяти основных вкладок, соответствующих разным задачам. Правка - тут открываются документы, тоже каждый в своей вкладке. Файлы - файловый приказчик. О нем подробно в следующей главе. Наладка - под этой чудной надписью скрываются настройки. То, что в других программах обычно выносится в отдельное окно Настроек, в TEA находится в том же окне, где всё остальное. Изменение настроек сразу же вступает в силу и сохраняется автоматически.

Даты

- тут календарь с возможностью ведения разных дел. Становится доступным и особое меню. Наконец, на вкладке Руководство доступно руководство, которое вы сейчас читаете. Спасибо вам за это!

4. Логмемо. Этим ужасным названием именуется область, куда выводятся всякие сообщения. Эти всякие сообщения могут просто отмечать, что файл был сохранен, а могут и уведомлять про ошибку - как правило, текст сообщения при этом назойливо-красный, чтобы привлечь ваше внимание.

5. Знаменитое поле ввода (ЗПВ). Служит для ввода всяких параметров для функций обработки текста, а также для поиска и замены введенных слов. Подробности читайте в разделе про меню Поиск. ЗПВ работает для поиска как в тексте документа, так и руководства, которое вы сейчас читаете. А также умеет искать в списке пунктов меню для "горячих клавиш" и в файловом приказчике.

6. Строка состояния. Её содержимым можно управлять через настройки - например, отключить отображение положения курсора. Вычисление этого положения отнимает некоторый процессорный ресурс, что может быть заметно на совсем стареньких компьютерах при редактировании больших текстов.


Панель Даты

Это только с виду - обычный календарь. В него можно записывать всякие дела. Дважды щелкаем мышью по дню - и создается или открывается файл с таким же именем, как выбранная дата. Вписали туда что надо, закрыли - файл сам по себе сохранился где надо. А точнее, в служебном каталоге TEA. А если просто один раз на дате щелкнуть, то, ежели есть запись по дню, она покажется в логмемо.

В тексте записи можно еще указать время, чтобы напомнить себе о важном деле. Просто в тексте надобно предварить напоминание временем в квадратных скобках, в 24-часовом формате [чч:мм]. То есть ежели надо написать 9 утра, то пишем [09:00], а не [9] и не [9:0]. Помним про нули! Вот примеры:

[20:00] сходить в театр
[21:00]посетить буфет и туалет, как будто в театр ходят чтоб пожрать и

Напоминалки TEA показывает в отдельном окне в левом верхнем углу экрана. Происходит сие зачастую с опозданием в пределах меньше одной минуты - так уж программно сделано.

Чтобы удалить запись, относящуюся к выбранному дню, воспользуйтесь пунктом меню Календарь > Удалить данные о дне.

Календарь может работать в режиме лунного, для этого есть переключатель в меню Календарь - Лунный режим вкл/выкл. Больше о функциях, связанных в календарем, читайте в разделе Календарь, а покамест отмечу еще две штуки. Лунные дни вычисляются по такому-то алгоритму, который выбирается в Наладка - Общие - Алгоритм фазы Луны. Там же, для правильного отображения фаз, надобно выбрать, где вы находитесь - в северном или южном полушарии. Когда Луны не видно, это не ошибка, а новолуние.

В "лунном" режиме, даты в календаре отображаются через косую черту. Сначала идет "григорианский" номер дня, потом, через косую черту - лунный день.


Файловый приказчик

Поясню, почему приказчик. В русском языке есть отличные слова - приказчик, управляющий. Нет нужды в "менеджере".

Этот приказчик в TEA служит вместо набивших оскомину диалоговых окон Открыть файл/Сохранить как. Впрочем, можете пользоваться ими, поставив галочку в настройках на Наладка > Общие > Использовать традиционные окна Открыть/Сохранить.

Давайте разберемся, как работать с файловым приказчиком. Клавиша Backspace позволяет перейти в каталог выше уровнем. В самом верху приказчика находится текстовое поле, отображающее путь текущего каталога. Можно набрать там другой каталог и нажать "Enter" или кнопку "Перейти". Справа от поля - панель с кнопками:

Кнопка Перейти - направляет приказчика в каталог, указанный в поле.

Кнопка Домой - переносит вас в домашний каталог.

Кнопка Освежить - обновить содержимое дерева файлов.

Кнопка Действия - открывает подменю, из которого можно переименовать выделенный файл или каталог, удалить файл либо создать новый каталог. Эти же функции повторяются в пункте Фп главного меню. Зачем? Чтобы вы могли навесить на них свои сочетания клавиш.

Теперь поглядим на правую панель приказчика.

Сверху нее расположено поле имени файла. Чтобы открыть файл, можно вписать туда его имя и нажать кнопку Открыть. Имя может быть как полным путем, как и просто названием файла - в последнем случае подставляется текущий каталог. В поле работает клавиша Enter - в зависимости от того, как вы попали на его вкладку - через пункт меню Открыть или Сохранить как. Если первое, то Enter открывает вписанное имя файл, второе - сохраняет.

Дабы открыть несколько файлов, выделите их в списке файлов (с помощью нажатой клавиши Ctrl и мыши, либо клавиши Insert вы можете выделять несколько пунктов) и нажмите опять-таки кнопку Открыть. Рядом с полем имени файла есть список кодировок. Все файлы открываются согласно выбранной кодировке.

Файл можно открыть также двойным щелчком по его имени в списке файлов.

А чтобы сохранить текущий файл под нужным вам именем, достаточно вписать это имя в поле имени файла и нажать кнопку Сохранить как. По умолчанию TEA настроен таким образом, что при выборе пункта меню Сохранить как или Открыть вы переноситесь в файловый приказчик, где и делаете всё, что вам нужно.

Еще ниже - панель закладок. Вот уж лепота так лепота. Кнопкой + туда добавляется закладка на текущий каталог, а кнопкой "-" удаляется выбранная в списке закладка. Двойной щелчок по закладке - и приказчик переносит вас в нужный каталог. Первые пункты в списке - шаблоны, сниппеты и тому подобное - предустановленные, их удалять нельзя. С их помощью вы попадаете в каталоги шаблонов и сниппетов, которые TEA отображает в особых менюшках.

Если поставлена галочка в Наладка > Общие > Авто-показ картинок в файловом приказчике, то при установке курсора на файл с картинкой, эта картинка будет показана уменьшенной в окошке рядом с окном TEA. Другой способ просмотра картинки - нажать на ее файле F2 (меню Файл > Открыть под курсором).


Файл

Новый. Создает новый пустой документ. Можете текст набирать, а можете просто на белый фон смотреть.

Открыть - открыть файл. Появится файловый приказчик либо диалоговое окно открытия файла (смотря по настройкам). Там есть список "Кодировка". Выберите нужную, потом открывайте файл.

Вообще почитайте главу Файловый приказчик - там подробно описано, как с ним работать для открытия и сохранения файлов. Диалоговые окна не столь удобны.

О поддерживаемых форматах. TEA читает/пишет обычные текстовые файлы, а также может вытаскивать текст из ODT (OpenOffice.org Writer), FB2, EPUB, DOCX, KWD (старый формат KWord), ABW (Abiword) и SXW (старый формат OOo/StarOffice). Относительно офисных форматов, то поддержка их в состоянии разной степени кривизны. Но читать можно. Если же ТИА собран с поддержкой PDF и DJVU, то ТИА тоже умеет вытаскивать из них текст. Но такая поддержка тянет за собой дополнительные библиотеки и утяжеляет ТИА.

Сохранить - сохранить файл. Если он не был сохранен ранее, то редактор спросит, под каким именем сохранить. В окне выбора имени файла (или в файловом приказчике) можно также указать кодировку. Я советую использовать UTF-8. Кстати, внутренне TEA держит текст в UTF-16. UTF-8 и UTF-16 - варианты юникода.

Последний открытый - открывает последний открытый файл, который был закрыт. Проще говоря, первый файл из списка Последние файлы.

Открыть под курсором (F2) - одна из моих любимых вещей в TEA. Позволяет открыть файл, на имени которого (в HTML иил LaTeX документе) стоит курсор. Если картинка - редактор вам покажет ее. Ежели текстовый файл - откроет. Если уже открыт - сделает текущим его вкладку. Коли имя файла не заключено в кавычки, выделите его - тогда тоже можно применить Открыть под курсором. Пользуйтесь на здоровье! Кроме того, эта штука умеет переносить вас по локальным href-ссылкам.

Если же курсор стоит не на тексте, а в списке файлов в приказчике, то нажатие F2 на файле с картинкой (любой из поддерживаемых TEA форматов от JPEG до TIFF) откроет уменьшенное изображение во встроенной смотрелке.

Существует также способ открывать файл по месту курсора вообще в произвольной программе. Как это сделать, читайте в разделе про меню "Запуск".

Фигня - открывает особый файл, чтобы вы заносили туда всякие заметки и прочую ерунду. Файл автоматически сохраняется при закрытии себя отдельно или всего TEA.

Заметки - почти то же, что Фигня, только для текущего файла. TEA создает для него файл с расширением .notes, автоматически сохраняемый, куда вы можете помещать заметки, относящиеся к текущему файлу. Мне, например, удобно выкидывать туда текст, я хочу временно или навсегда убрать из файла, или разные сведения для дальнейшего использования в файле.

Сохранить иначе > Сохранить запасную копию - сохраняет содержимое текущего файла под именем, составленным из имени текущего файла и расширения "bak".

Сохранить иначе > Сохранить версию по времени - тоже сохраняет копию, только имя этой копии составляется с добавлением к нему текущих даты и времени (включая миллисекунды).

Последние файлы. Из этого подменю вам доступен для быстрого открытия любой из дюжины последних открытых файлов. При этом TEA знает, в какой файл кодировке, а также перемещает курсор в место текста, с которым вы работали в последний раз.

Шаблоны - чтобы создать новый файл на основе шаблона, просто выберите в этом меню имя файла-шаблона. Как создать свой шаблон? Достаточно сохранить любой файл в папке шаблонов. Для этого выберите пункт меню Сохранить как и, в открывшемся файловом приказчике воспользуйтесь закладкой шаблоны (или в стандартном диалоговом окне сохранения файла, щелкните на templates в левой панели быстрого доступа).

Сходным образом, для открытия шаблона на редактирование, открывайте файл-шаблон как обычный файл. Удалить или переименовать шаблон можно либо в файловом приказчике, или в диалоговом окне через контекстное меню (правая кнопка мыши).

Шаблоны должны быть в кодировке UTF-8. При сохранении шаблона внутри TEA (не в другом редакторе), TEA принудительно устанавливает эту кодировку.

Сессии. Сессия - это просто список файлов. Чтобы можно было их сразу загрузить. Выбор сессии в этом меню открывает все файлы, которые включены в сессию. Чтобы сохранить сессию, надо набрать ее имя в ЗПВ, а потом воспользоваться менюшкой Файл > Сохранить иначе > Сохранить сессию. В настройках, на вкладке Общие, можно поставить галочку на Загружать последнюю сессию при запуске. Это значит, что при выходе список файлов будет сохраняться в сессию по умолчанию, а потом при запуске редактора - загружаться.

Закладки - меню, подобное Последним файлам за исключением того, что список ведется вручную, вами самими. Правильнее было бы назвать это меню Избранное. Итак, вы можете добавить туда любой файл - пунктом Править закладки > Добавить в закладки. Для редактирования файла закладок надо пойти в Конфиги > Список закладок. Откроется в режиме редактирования обычный текстовый файл со списком закладок. Каждая строка списка содержит в себе полный путь к файлу, кодировку и положение курсора. Удаляете ненужные строки, сохраняете файл - и меню Закладки автоматически обновляется.

Строки закладок можно временно отключать, предваряя их символом #. Такая строчка не будет отображаться в меню. Функция Править закладки > Найти ошибочные пути проверит пути к файлам, на кои указывают закладки, и закомментирует те закладки, для которых файлы не найдены по указанному в закладке пути.

Действия над файлом - полезное подменю. Кроме прочего, отсюда можно жестко задать параметры конца строки. Установить конец строки как в UNIX, Установить конец строки как в Windows, Установить конец строки как в Mac - этими функциями вы указываете TEA, какой конец строки применить при сохранении. А чтобы проверить, какой конец строки у файла, пойдите в файловый приказчик, установите курсор на нужный файл, и выберите пункт меню Фм > Сведения о файле > Полные сведения.

Не добавлять в Последние файлы - временно отключает добавление закрываемых файлов в список "Последние файлы". Удобно, если вы не хотите нарушить свой список последних открытых файлов какими-то случайными гостями. Отключите добавление, поработайте с этими ненужными для списка файлами, потом включите добавление обратно.

Выход - закрывает TEA. Обратите внимание, что редактор не задает лишних вопросов. TEA может разве что полюбопытствовать, желаете ли вы сохранить измененный файл - и то, в случае, если он уже был сохранен ранее. То бишь, подтверждение на сохранение не запрашивается, если файл новый, безымянный. Это же относится и к функции Закрыть текущий файл (Ctrl-W). Почему так, а не иначе? Чтобы не раздражать вас лишними запросами подтверждений.


Правка

О прямоугольном или блочном выделении. Оно не шибко вписывается в архитектуру TEA. Функции обработки текста к нему неприменимы. Всё, что можно делать с прямоугольным выделением, это создать его при помощи Начало блока и Конец блока, а затем Копировать блок, Вставить блок или Вырезать блок.

Пара замечаний о пунктах меню Отступ и Отменить отступ. За ними железно закреплены клавиши Tab и Shift-Tab. Если выделен текст (много строк), то происходит его сдвиг влево или вправо путем вставки или удаления пробелов либо табов. Если текст не выделен, в место курсора вставляется один таб либо пробелы числом, заданным в Наладка > Функции > Ширина таба в пробелах. На поведение нажатия Tab влияет опция Наладка > Интерфейс > Использовать пробелы вместо табов.

Отступ по первой строке - делает отступ всем выделенным строкам такой же, как в первой выделенной строке.

Файлы-хранилища.

Некоторые люди испытывают потребность копировать текст из множества файлов в какой-то один. И приходится переключаться туда-сюда с документа на документ. Чтобы избежать этой, в прямом смысле, мышиной возни, в TEA есть возможность пометить файл как хранилище, и затем копировать текст напрямую в него, без дополнительных усилий. Для этого достаточно при открытом документе, который вы хотите сделать хранилищем, использовать пункт меню Установить как файл хранилища. Затем, в каком-нибудь другом документе, выделяем текст, и, чтобы скопировать его сразу в хранилище, воспользуемся функцией Копировать в файл хранилища. Если вы потом закроете файл хранилища, то Копировать в файл хранилища не будет работать, пока вы не сделаете другой файл - хранилищем.

Захватить/нет буфер обмена в файл хранилища - если включено, то, когда в буфер обмена попадает какой-то текст, он будет скопирован на файл-хранилище. Под MacOS сие работает в пределах только TEA, в других системах - на всю систему, то бишь если вы копируете что-то например в браузере, скопированный текст автоматом помещается в хранилище.

Для форматирования помещаемого в хранилище текста можно использовать файл-шаблон под именем cliptpl.txt, который надо сохранить в основном каталоге конфига TEA. Пример такого файла с макросами:

---
%date
%time
%s
---
Здесь: %s - текст из буфера обмена, %date - текущая дата, %time - текущее время.

Вёрстка

В этом меню собраны функции для быстрой разметки текста в режимах Markdown, HTML, XHTML, Docbook, Wikitext, Lout и LaTeX. Режим разметки переключается в списке Режим и устанавливается локально для текущего документа. То бишь разные документы могут иметь разные режимы разметки. При загрузке файла для него автоматически выбирается режим смотря по расширению файла. Если расширение файла - .wiki, либо выбран вручную режим Wikitext, то работает разметки Wikitext.

В основном пункты меню Вёрстка служат для ввода тэгов - в зависимости от выбранного режима размётки вставляется нужный тэг. Если был выделен текст, он будет обрамлен тэгами (в случае, ежели таковые предназначены обрамлять).

Кроме того, в меню содержится ряд инструментов, полезных для работы с HTML и XHTML-документами.

Инструменты [X]HTML > Переименовать выделенный файл - переименовывает файл, чье имя выделено в тексте, а попутно и сам выделенный текст. Допустим, у нас в HTML (или LaTeX) документе есть ссылка на файл dog.html. Мы хотим одним действием переименовать его на диске в cat.html, а также чтобы в самой ссылке текст, выделенное имя файла, превратилось в cat. Выделяем имя файла в ссылке href (ну или в img src, где угодно). Затем, в Знаменитом Поле Ввода набираем "cat". Применяем Переименовать выделенный файл. Готово! При этом, TEA понимает относительные пути, а также еще учитывает некоторые мелочи в формате путей.

Инструменты [X]HTML > Взвесить документ - вычисляет общий объем не токмо HTML-страницы, но и связанных с оной объектов - картинок, флэшек и так далее. Короче говоря всего, что упомянуто в SRC. Итоги взвешивания выводятся в логмемо.

Инструменты [X]HTML > Смотреть выделенный цвет - чтобы узреть цвет воочию, выделите его в текста, примените сию функцию и глядите в логмемо - там будет образец. Выделять надо шестнадцатеричное значение либо название вроде red, magenta и тому подобное.

Инструменты [X]HTML > Перевести тэги в сущности - как известно, в HTML недопустимы некоторые символы (например, "больше" и "меньше"), поскольку они используются как ограничители тэгов. Поэтому, чтобы показать такие символы на веб-странице, желательно перевести их в так называемые сущности - записанные в особом формате символьные обозначения тэгов. Итак, если вам нужно где-то на веб-странице показать пример HTML-кода, выделите его и примените Перевести тэги в сущности. Получится примерно такое:

&lt;b&gt;демо&lt;/b&gt;

При отрисовке веб-страницы такой пример будет выглядеть как обычный HTML-код, как исходник.

Инструменты [X]HTML > Из текста в [X]HTML - переводит обычный текст в [X]HTML, красиво форматированный с помощью CSS. Режим HTML или XHTML зависит от режима вёрстки документа.

Кодировать адрес e-mail супротив спама - функция нужна для перевода mailto-ссылок в числовой код символов, составляющих адрес e-mail. Такой код отображается браузером как обычные буквы, однако для спаммерских программ, собирающих адреса электронной почты, он выглядит невразумительно и не воспринимается как почтовый адрес.


Поиск

В TEA нет диалоговых окон поиска и замены. Вместо этого используется Знаменитое поле ввода. Допустим, вы хотите найти в тексте (документа или руководства) слово. Набираете его в Знаменитом поле ввода (Ctrl-F для быстрого перемещения в него) и жмете Enter. Всё! Затем, чтобы найти следующее или предыдущее вхождение слова в текст, есть пункты меню "Найти дальше" (клавиша F3), "Найти назад" - им же соответствуют и кнопки рядом со Знаменитым полем ввода. Там три кнопки. Первая - Поиск, вторая - найти предыдущее, третья - найти дальше. При первом поиске TEA ищет слова, начиная с места курсора.

Управлять настройками поиска можно, помечая галочками пункты меню Целый слова и Чуткость к регистру и Нечеткое условие. Последнее - это когда вы хотите найти слово, но не знаете точно, как оно пишется. Например - Семилетов или Самолётов. И вот при включенном Нечетком условии редактор найдет оба похожих слова. Степень похожести можно настроить в Наладка - Функции - Разное - Коэффициент поиска по нечеткому условию. Чем он меньше, тем больше похожих слов найдет TEA. Ограничение - TEA находит в этом режиме лишь слова с одинаковым количеством букв.

Чтобы заменить найденный текст (он будет выделен) на другой, надобно ввести этот другой текст опять же в Знаменитое поле ввода и применить пункт меню "Заменить на". Это удобно в сочетании с "Найти дальше". Нашли, посмотрели - надо заменить - жмем "Заменить на", не надо - делаем "Найти дальше".

Либо, как я делаю - копирую в буфер обмена слово, на которое хочу производить замену. Потом по "Найти дальше" (F3) шагаю по заменам, и где хочу, там нажимаю Ctrl-V (вставить).

А чтобы заменить некое слово на другое, по выделенному тексту, надо в ЗПВ поместить правило особого формата, вот такого:

старое слово~новое слово

То бишь разделителем между ними служит символ "тильда" - "~". Пример - мы хотим поменять в тексте все слова "кошка" на "мышка":

кошка~мышка

Затем применяем "Заменить всё". Генная инженерия бессильна объяснить результат.

Функцией замены можно пользоваться также, чтобы убирать из текста нежелательные подстроки. Для этого не пишем после тильды второй параметр. Например, чтобы убрать из текста все слова "оружие", надо задать в ЗПВ "оружие~" и применить Заменить всё.

Можно включить опцию В режиме регулярных выражений - в этом случае, при поиске и замене текст из ЗПВ будет трактоваться как регэксп. Например, чтобы перевести HTML-курсив в TeX-аналог, можно использовать такую формулу в ЗПВ:

<i>([^<]*)</i>~\emph{\1}

Обратите внимание, что \1 служит как бы обозначением той части текста, которая была "захвачена" регулярным выражением. Вот тут в примере мы поймали весь текст между тэгами. "Улов" по второму условию доступен как \2, по третьему как \3 и так далее.

А вот другой пример. Чтобы найти в тексте только числа - любые числа - вводим в ЗПВ следующее регулярное выражение: (\d+).

Не забудьте отключить режим регулярных выражений, когда захотите вернуться к обычному поиску или замене. Замечание - при включенном режиме регулярных выражений, переключатель Чуткость к регистру расценивается как включенный, даже если он выключен.

В режиме файлового приказчика, функция Заменить всё работает с выделенными файлами, используя в качестве кодировки кодировку, выбранную в приказчике в списке кодировок.

Заменить всё в открытых файлах - действует так же, как и обычная Заменить всё, однако применительно ко всем файлам, которые сейчас открыты в TEA.

Найти в файлах - ищет слово, заданное в Знаменитом поле ввода, начиная в текущем каталоге файлового приказчика (на вкладке Файлы) и далее по вложенным каталогам. Для ускорения поиска, TEA смотрит только в файлы, которые считает текстовыми - а это файлы исходников, для которых TEA поддерживает подсветку синтаксиса, а также файлы с расширением txt и все форматы документов вроде ODT, DOCX, FB2, Epub, которые понимаются TEA. Это касается также PDF и DJVU, если ТИА собран с их поддержкой. Кроме того, TEA глядит в файлы с именами вроде Changelog. Поиск осуществляется в кодировке, которая выбрана в том же приказчике в списке кодировок. Завершив поиск, TEA выводит окно со списком файлов, где найден текст. Двойным щелчком мыши по названию файла вы откроете его в редакторе.

Пометить всё найденное/Снять пометки - помечает в тексте цветом все найденные подстроки или снимает пометки. Эта функция может переглючить цвета вообще, при использовании одновременно с Вид - Темнее. Всё исправится после перезапуска редактора либо в новом открытом документе. Ничего страшного. Ничё-ничё! Луна не падает.


Календарь

В этом меню, доступном лишь когда вы находитесь на вкладке Даты и глядите на календарь, собраны всякие связанные с ним функции.

Добавить или вычесть - дни, месяцы, годы. Допустим, вы хотите посмотреть, какой будет день через 7 дней. Вводим в ЗПВ число 7, и выбираем пункт меню Добавить или вычесть > дни. Хотим 7 лет? Делаем то же самое, только выбираем уже не "дни", но "годы". А если нужно не ЧЕРЕЗ, а ДО? Какой день был 15 дней назад? Указываем в ЗПВ число с минусом, то есть "-15".

Количество дней между двумя датами - подсчитывает, сколько дней находится между двумя датами. Выбираем в календаре первую дату и закрепляем её выбором пункта меню Пометить первую дату (это впрочем никак не отобразится на календаре). Затем выбираем в календаре вторую дату и вызываем Пометить вторую дату. Сразу получаем разницу в днях.

Вычислить лунные дни между датами - составляет эдакий лунный календарик между из дней между двумя отмеченными датами.


Функции

Чтобы использовать функции обработки текста, надо сначала этот текст выделить. Некоторые функции, например Статистика по тексту, могут работать вдобавок и со всем текстом документа. В совсем редких случаях, таких как изменение регистра, если текст не выделен, то в качестве обрабатываемых данных берется слово, на котором стоит курсор.

Некоторые функции принимают параметры. Параметры следует прописывать в Знаменитом Поле Ввода (ЗПВ, строка внизу главного окна) - до применения функции. Например, функция Удалить строки < размера N должна получить этот самый размер, число, из Знаменитого поля ввода. Значений по умолчанию нет, зато всегда можно выполнить отмену примененной функции.

Функции > Предложения с большой буквы - чтобы предложения начинались с большой буквы. Удобно для автоматических субтитров с Ютуба. Расставили точки и знаки ?, !, а потом применили эту функцию.

Функции > Инструменты > Масштабировать картинку - масштабирует картинку, на имени которой в тексте стоит курсор или имя просто выделено. Пропишите в ЗПВ параметры преобразования и примените эту функцию.

Формат параметров: имя файла~новый размер

Имя файлы это имя выходного файла, то есть файла-результата. Оно может быть просто задано жестко, например "out.jpg", а может быть составлено из исходного при помощи макросов %basename (имя файла без расширения), %ext (расширения). Таким образом полное исходное имя составляется из строки %basename.%ext

Имя файла + расширение заменяются также макросом %filename

Однако если вы не хотите записывать масштабированный файл под исходным именем, можно добавить какую-то приставку или добавить что-то после имени. Например:

перед-%basename-bar.%после~1024

В этом примере, исходное имя файла будет предварено"перед-", и дополнено "-после".

Второй параметр - новый размер картинки, может быть в пикселах и процентах (добавьте % после числа):

новое-%basename.%ext~1024

новое-%basename.%ext~50%

Ячейки - это подменю используется для обработки табличных данных, коими могут быть, например, таблицы LaTeX или CSV-строки. Все функции требуют, чтобы вы задали в ЗПВ по крайней мере один параметр - разделитель столбцов, чтобы TEA знал, как ему разбирать строки.

Функции > Ячейки > Упорядочить таблицу по столбцу в алфавитном порядке - делает то, что сказано.

Строка формата для ЗПВ: разделитель~номер столбца

Столбцы (колонки) нумеруются с нуля, то есть нулевой это первый, первый - второй, и так далее.

Например, у нас есть LaTeX-таблица с 5 рядами по 2 столбца:

собака&кот
баран&корова
змея&суслик
волк&тигр

И теперь мы хотим сортировать таблицу по второму столбцу, там где "кот", "корова". Выделим текст, в ЗПВ вобьем формат "&~1" и применим эту функцию. Таблица будет упорядочена по второму столбцу.

Удобно обрабатывать таблицу, на включая в нее поначалу переносы строк, например "\\" в LaTeX. Их лучше добавить после обработки, посредством Текст - Применить к каждой строке со значением "%s\\" в ЗПВ.

Функции > Ячейки > Поменять местами ячейки - меняет местами столбцы по номеру.

Формат значения для ЗПВ: разделитель~столбец1~столбец2

Скажем, в случае таблицы из примера выше, мы хотим поменять местами столбы 0 и 1, то есть чтобы столбце с "кот" был перед столбцом с "пес". Выберем текст, поместим в ЗПВ значение "&~0~1", и применим Поменять местами ячейки.

Функции > Ячейки > Удалить по столбцу - удаляется столбец по его номеру (нумерация с нуля).

Формат для ЗПВ: разделитель~номер столбца

0 - первый столбец, 1 - второй, и так далее.

Функции > Ячейки > Копировать по столбцу[цам] - копирует столбцы в буфер обмена.

Формат для ЗПВ: разделитель~столбец 1[~столбец 2]

Если столбец 2 не задан, будет скопирован только столбец 1, иначе - от столбца 1 по столбец 2 включительно. Хо-хо-хоооо!

Функции > Анализ > Статистика по тексту - выводит в логмемо разную статистику по всем или выделенному тексту - количество символов с пробелами, без них и так далее.

Функции > Анализ > УНИТАЗ - УНИТАЗ, или УНИверсальный Текстовый АналиЗатор - это научное средство для частотного анализа текста. Дается статистика частоты употребления слов, а также некоторые соотношения, например, слов повторяющихся и без повторов. Что имею в виду? Допустим, в тексте трижды встречается слово "кот". Так вот, количество этого слова без повторов равно 1, а с повторами - 3. Именно такие количественные определения действуют в УНИТАЗе.

Функции > Анализ > Извлечь слова - извлекает из текста все слова и помещает их списком в новый документ. Полезно для создания словарей. Может занять большое время в зависимости от размера обрабатываемого файла и скорости вашего компьютера.

Функции > Анализ > Подсчитать вхождение подстроки - вычисляет, сколько раз подстрока (заданная в Знаменитом поле ввода), входит в текст документа. И выводит полученное число в логмемо.

Функции > Анализ > Длины слов - выводит статистику, сколько слов такой-то длины в документе.

Функции > Таблицы

Таблица - это чудесное средство для замены в одном документе сразу множества слов, а не по одному. Сначала создаем таблицу - в виде обычного текстового файла. Примерно так:

кошка=собака
мяукает=лает

Сохраняем этот файл в особом каталоге - он доступен под названием таблицы на панели закладок в файловом приказчике или в окне сохранения. Под именем, с которым вы сохранили файл, он появится в меню Функции > Таблицы. Попробуем применить таблицу.

В новом файле введем (конечно, не введем, а скопируем туда) текст: "кошка мяукает", выделяем его, затем выбираем в меню нашу таблицу, применяя ее к тексту. Что получилось? В тексте произошли множественные замены и теперь он гласит: "собака лает". Замечательно!

Если в меню Поиск включен пункт В режиме регулярных выражений, это сказывается и на обработке текста таблицами. При этом в таблице заменяемые слова задаются регэкспами.

В режиме файлового приказчика, функция замены при помощи таблиц работает с выделенными файлами, используя в качестве кодировки кодировку, выбранную в приказчике в списке кодировок.

Функции > Сниппеты

Сниппет - это кусочек текста, который можно быстро вставить в документ. TEA хранит сниппеты как отдельные файлы в каталоге $HOME/.tea/snippets. Выбор сниппета из меню вставляет текст сниппета в документ по месту курсора.

Чтобы создать сниппет, достаточно бодро сохранить файл (в котором набран текст сниппета) в указанный выше каталог. Перейти туда можно по закладке сниппеты в файловом приказчике либо snippets в окне Сохранить как. Там же сниппеты можно переименовывать и удалять (в диалоге Сохранить как эти функции - в контекстном меню по правому щелчку мыши). Давая имя сниппету, скрипту, шаблону или сессии, следите, чтобы оно не совпадало с уже существующими названиями пунктов меню. Иначе редактор не сможет правильно назначать "горячие клавиши".

Сниппеты пригодны не только для простой вставки в текст. Ими можно обрамлять выделение. Например, мы хотим сниппетом оформить тэгами выделенный текст. Пример сниппета, который помещает выделенный текст в тэг ссылки (A с параметром HREF):

<a href="%s">%s</a>

Здесь в теле сниппета мы используем макрос %s, который представляет выделенный текст. Когда сниппет будет применен, т.е. выбран из меню, он заменит собой выделенный в документе текст, однако вместо макроса %s будет подставлено содержимое выделения. Если же текст не был выделен, сниппет просто вставится по месту курсора, и при вставке макрос %s чудесным образом исчезнет.

Открыть сниппет для редактирования можно тем же способом, что и обычный файл.

Функции > Скрипты

Скрипты с точки зрения пользователя

В TEA, вы можете использовать скрипты для обработки текста точно так же, как и встроенные функции. То есть, грубо говоря - выделяете текст, вызываете из меню скрипт. Если скрипт принимает какой-нибудь управляющий параметр, то его, этот параметр, надо вписать в Знаменитое поле ввода.

TEA "понимает" скрипты, написанные на Python, Perl, Lua, Ruby, Bash (Sh), 2/Rexx и вындовые bat-файлы. Чтобы установить скрипт, просто скопируйте его в $HOME/.config/tea/scripts (либо сохраните его в этой папке, выбрав в файловом приказчике папку скрипты. Если скрипт находится в архиве, то распакуйте его. TEA "увидит" установленные скрипты после перезапуска редактора либо сохранения скрипта. Удалять и переименовывать скрипты можно так же, как и сниппеты, шаблоны и т.д. - через файловый приказчик либо диалоговые окна открытия/сохранения.

Скрипты с точки зрения разработчика

Как написать скрипт для TEA? Да очень просто. Во-первых, вам нужно знать, как TEA передает текст в скрипт, и как получает обработанный текст обратно, чтобы заменить им выделение.

TEA запускает каждый скрипт с двумя параметрами. Первый параметр всегда указывает на имя файла, в котором содержится переданный из TEA текст (в UTF-8). Текст этот - выделенный фрагмент из текущего документа. То есть, чтобы получить из TEA текст, вам нужно в коде скрипта прочитать содержимое файла, чье имя передано в первом параметре к скрипту. Второй параметр (если существует) содержит имя файла, в который TEA записывает текст (UTF-8) из Знаменитого поля ввода. Итак, чтобы получить содержимое Знаменитого поля ввода, вам нужно внутри скрипта прочитать файл, на имя которого указывает второй параметр скрипта.

При обработке текста обратите внимание на кодировку - TEA передает текст в скрипты в кодировке UTF-8 и предполагает, что в этой же кодировке текст будет возвращен ему обратно. А как возвращать? Тоже просто. Надо записать обработанный текст в тот же файл, откуда текст был прочитан. То есть в файл, чье имя в первом параметре к скрипту.

Ниже я приведу пример UTF-8-безопасного скрипта на Python. Этот скрипт "переворачивает" регистр символов полученного текста и возвращает обработанный текст обратно в TEA.

import sys
import string
import codecs
f = codecs.open(sys.argv[1], "r", "utf-8" )
u = f.read()
f.close
u = u.swapcase()
f = codecs.open(sys.argv[1], "w+", "utf-8" )
f.write (u)
f.close

Итак, sys.argv[1] (первый параметр скрипта) содержит имя файла, который нам нужно прочитать, чтобы получить текст из TEA. Когда мы обработали текст, мы записываем его все в тот же файл, имя которого содержится в sys.argv[1]. Обратите внимание на использование кодеков (в Python). Это необходимо для правильной работы скрипта с UTF-8.

И другой пример - эдакий калькулятор. Он получает выделенный текст, а в тексте - какое-то математическое выражение. Скрипт вычисляет его и возвращает результат. Поскольку мы имеем дело только с числами, мы можем не заботиться о кодировке:

import sys
f = file (sys.argv[1], 'r')
s = f.read()
f.close
t = eval (s)
f = file (sys.argv[1], 'w+')
f.write (str (t))
f.close

А что, ежели нам нужно получить некий параметр, с помощью которого пользователь, введя этот параметр в Знаменитое поле ввода, хочет повлиять на работу скрипта? В Python, для получения этого параметра, надо прочитать файл, чье имя находится в sys.argv[2]. А в Bash-скрипте, используйте $1 для получения имени файла с текстом документа, и $2 - для получения имени файла с содержимым Знаменитого поля ввода.

Примечания о BASH-скриптах и вызовах sed и подобных программ. Для их правильной работы внутри скрипта надо скопировать буферный файл TEA - $1 в какой-нибудь временный файл, обработать этот временный файл и вывести результат обратно в $1. Вот пример такого скрипта (он заменяет все слова cat на dog):

cp $1 /tmp/xyz.xyz
sed 's/cat/dog/g' /tmp/xyz.xyz > $1

Если вы собираетесь пожертвовать какие-то скрипты для коллекции скриптов на сайте TEA, то было бы здорово, если б вы дали свой скрипт со статусом "общественное достояние" (public domain). Во всяком случае, не под собственнической (proprietary) лицензией. Еще в комментариях внутри скрипта укажите авторство и описание скрипта.

Сортировка > Сортировать учитывая регистр - сортирует выделенные строки по алфавиту, учитывая регистр. То бишь "собака" это не "Собака".

Сортировка > Перевернуть список - переворачивает порядок строк. Было:

Собака
Кот
Коза

Будет:

Коза
Кот
Собака

Сортировка > Сортировать зависимо от регистра, по разделителю - упорядочивает по алфавиту слова в строке, разделенные неким разделителем, который надо прописать в ЗПВ. Например, разделителем может быть "|", а строка: "собака|кот|попугай".

Сортировка > Перевернуть список, размежеванный разделителями - переворачивает слова, разделенные неким ограничителем. Например, можно в обратном порядке выстроить слова в таком тексте: "собака|кот|коза". Разделитель записываем предварительно в ЗПВ.

Фильтр > Фильтровать повторы - оставить в списке только слова, удовлетворяющие образцу, где есть повторяющиеся буквы. Например, мы хотим оставить в списке все слова из 5 букв, где первая и последняя будут одинаковы. В ЗПВ вводим образец 10001, выделяем текст и применяем функцию. Или, образец для слова "собака" будет таков: "000101".

Фильтр > Удалить повторения - удобно, когда у вас список строк, содержащий повторы. После применения этой функции, все повторы, следующие за первым встречающимся повторно элементом, будут удалены. На слабых компьютерах выполнение сей функции может затянуться.

Фильтр > Удалить пустые строки - пустые строки столь же полезны, как пустые глаза, в отличие от пустых бутылок. Поэтому необходимо средство для удаления пустых строк. И в TEA оно есть.

Фильтр > Удалить строки < размера N - удаляет в списке строки, чья длина меньше, чем значение N. N - число, которое надо ввести в Знаменитое поле ввода.

Фильтр > Удалить строки > размера N - почти то же самое, только удаляет строки с длиной больше заданной.

Фильтр > Фильтровать по регэкспу. Регэксп - сокращение от регулярного выражения. Регулярное выражение это, грубо говоря, шаблон для задания нечеткого критерия отбора. Допустим, можно отфильтровать текст, начинающейся с некоего сочетfния букв, или текст, содержащий только цифры. Регулярные выражения - большая тема, здесь я не буду ее касаться, читайте лучше сайт regexp.ru. ТИА обрабатывает регэкспы с помощью библиотеки PCRE2.

Пример. Допустим, у нас есть список с именами файлов:

hello.doc
example.txt
nature.jpeg
mytext.txt

И вот мы хотим, чтобы в этом списке остались лишь имена, заканчивающиеся на ".txt". Для этого регулярное выражение (которое надобно ввести в Знаменитое поле ввода) будет таким:

.*\.txt$

Итак, задаем этот регэксп, выделяем список, применяем функцию. Потрясаем руками с возгласами: "Отлично! Превосходно!" и идем гулять с ручным жуком-богомолом.

Фильтр > Удалить после разделителя в каждой строке. Удаляет из каждой строки часть, находящуюся после разделителя, заданного в Знаменитом поле ввода. Например, если разделителем вписать ")" и выделить следующий текст:

1.) Янка
2.) Гражданская Оборона
3.) Нирвана

То после применения фильтра получим:

1.
2.
3.

Фильтр > Удалить до разделителя в каждой строке. Удаляет из каждой строки часть, находящуюся ДО разделителя, заданного в Знаменитом поле ввода. Например, если разделителем вписать ")" и выделить следующий текст:

1.) Янка
2.) Гражданская Оборона
3.) Нирвана

То после применения фильтра вот что останется:

Янка
Гражданская Оборона
Нирвана

Математика > град мин сек > дес град переводит координаты из формата "градусы минуты секунды" в "десятичные градусы". Пример:

Было: 40° 26′ 46″ N 79° 58′ 56″ W, будет: 40.446° N 79.982° W.

Математика > дес град > град мин сек переводит координаты из формата "десятичные градусы" в "градусы минуты секунды". Пример:

Было: 40.446° N 79.982° W, Будет: 40° 26′ 46″ N 79° 58′ 56″ W.

Математика > Сложить по последнему столбцу - складывает числа из каждого последнего столбца выделения. Пример - можно вычислить вот такой текст:

картошка 60
морковка 40,5
капуста 14

TEA сложит из каждой строки последние колонки (столбцы).

Математика > Десятичное в двоичное - переводит десятичное целое число в его двоичное представление. Пример: было 255, становится 0000 0000 0000 0000 0000 0000 1111 1111.

Математика > Двоичное в десятичное - а эт наоборот. Работает токмо для беззнаковых целых чисел.

Математика > Перевернуть биты - поясню на примере: было 0000 0010, станет 1111 1101.

Математика > Вычислить - вычисляет математическое выражение, выделенное в тексте, и выводит результат в логмемо. Например, просто наберите в тексте "2+2", выделите это дело, а затем примените "Вычислить". В мат. выражениях можно использовать следующие операторы: +, -, *, /, ^ (возведение в степень), % (проценты). Скобки поддерживаются. Вот еще несколько примеров:

2/3*123
5+5+5
1-200*89
2^8
250%5 //сколько будет - 5 процентов от числа 250?

Математика > Арабское в римское - переводит "обычные" числа в римские (т.е. 20 в XX).

Математика > Нумеровать - нумерует выделенные строки согласно заданному образцу, который надобно поместить в Знаменитое поле ввода. Синтаксис следующий: шаг~количество цифр~префикс.

Шаг - шаг, с которым будет увеличиваться значение счетчика. Например, шаг 1 даст нам числа 1, 2, 3 и так далее. Шаг, равный 10, даст нам ряд 10, 20, 30.

Количество цифр определяет, как много цифр должен содержать счетчик - недостающие будут заменены нулями. Например, если количество цифр = 3, а шаг = 1, то мы получим 001, 002, 003, 004 и так далее.

Префикс - это префикс, который будет вставлен в каждую обрабатываемую строку после счетчика, но до текста строки. Пробелы в конце префикса - значимые, они тоже идут в ход.

Несколько примеров:

пример 1, параметры таковы "1~3~) " (без кавычек). Получается:

001) собака
002) кот
003) мышь

пример 2, параметры следующие: "10~1 " (без кавычек). Получается:

10 собака
20 кот
30 мышь

пример 3, параметр "1" (без кавычек). Выходит это:

1собака
2кот
3мышь

Вы можете использовать эту функцию и без параметров, в таком случае шаг будет равен 1, количество цифр - нулю (т.е. нули не будут добавляться), префикс - пустой строке. Каждый последующий параметр - необязателен. То есть, можно задать только шаг, или только шаг и количество цифр, либо шаг, количество цифр и префикс.

Математика > Субтитры: сдвинуть таймкоды на миллисекунды - с помощью этой функции можно сдвинуть таймкоды в субтитрах форматов SRT и Ютуба. Сдвиг с миллисекундах надо вписывать ЗПВ. Сдвиг может быть отрицательным, например "-2000" это минус 2000 миллисекунд, или 2 секунды. Выделите текст, введите миллисекунды, примените функцию.

Функции > Азбука Морзе

Поддерживаются русский и английский. После перевода в морзянку, коды букв разделены пробелами. Пробелы между словами теряются. Эти функции полезны, наверное, для начинающих радиолюбителей и жертв кораблекрушения - чтоб фонариками о беде сигналить. Пролетает самолет - посигналь. Проплывает мимо баржа - посигналь.

Статистика > Статистика по тексту - показывает количество символов всего, символов без пробелов и прочие сведения, которые полезно знать приличному человеку.

Текст > Сжать - удаляет из выделения все пробелы, переводы строки и символы табуляции.

Текст > Анаграмма - вычисляет все анаграммы к выделенному тексту. В зависимости от длины слова, вычисление может занять ОЧЕНЬ много времени и памяти, но не думайте, что редактор завис. Пример анаграммы для слова "кот":

кот
кто
окт
отк
тко
ток

Text > Anagram - find all anagrams of the selected text. Can be VERY slow and eats a lot of memory if the word is large. Example: dog

dgo
dog
gdo
god
odg
ogd

Текст > Удалить форматирование в каждой строке - убирает форматирование (табуляцию, пустые строки, многократные пробелы) в каждой строке выделенного текста. В итоге у вас получается набор строк, в каждой из которых нет форматирования.

Текст > Удалить форматирование - удаляет вообще всё форматирование в выделенном тексте. То бишь набор вероятно форматированных строк превращается в одну здоровенную строку без каких-либо разделителей.

Текст > Применить к каждой строке - мощнейшее средство для добавления заданного вами текста к каждой строке выделения. Например, вы хотите добавить тэг BR в конец каждой строки. Пишем в Знаменитом поле ввода:

%s<br>

Затем выделяем текст и используем Применить к каждой строке. Как видите, макрос %s служит заменителем изначального содержимого обрабатываемой строки. Вот другой пример - мы хотим заключить каждую строку в пару тэгов LI. Очевидно, что в "формуле" макрос %s следует поместить между тэгами, дабы указать, где именно в каждой строке надо размещать текст из этой строки. Формула для Знаменитого поля ввода будет такова:

<li>%s</li>

И еще пример:

<a href="%s">%s</a>

Можно использовать и сниппеты. Чтобы применить сниппет к каждой строке, надо ЗПВ до использования Применить к каждой строке поместить @@имя_сниппета. Например, если у вас сниппет (файл в каталоге сниппетов) называется hello, то надо будет написать: @@hello. Поскольку в сниппете используется макрос %s для подстановки выделенного текста, то в случае Применить к каждой строке роль выделенного текста будет играть каждая выделенная строка.

Текст > Перевернуть - просто переворачивает строку. Было "носоглотка", станет "актолгосон".

Текст > Проверить совпадение по регэкспу - загоняем регэксп в ЗПВ, выделяем в файле текст, применяем, смотрим ответ в логе.


Запуск

Это меню предназначено для запуска внешних программ с текущим открытым файлом. В частности, для запуска браузеров. Список программ настраивается через обычный текстовый файл, который открывается пунктом меню Файл > Конфиги > Список программ. Файл состоит из записей вида:

название пункта меню=командная строка

Например, в Linux, строчка для Firefox может быть такой:

FF=firefox %s

%s - макрос, вместо коего при запуске программы будет подставлено имя текущего файла. В Windows, в командной строке вам надо прописывать полный путь к исполняемому файлу, и желательно заключать его и макрос %s в кавычки (отдельные пары кавычек и для пути, и для макроса). Пример:

opera="C:\Program Files\Opera\opera.exe" "%s"

Есть и более подробные макросы: %basename, %filename, %ext, %dir. Они позволяют использовать части полного пути. Так, если у нас есть полный путь "/mnt/foo/bar.txt", то %dir = "/mnt/foo", %basename = "bar", %filename = "bar.txt", %ext = "txt". И полный путь текущего файла можно задать так: %dir/%basename.%ext

Также можно вместо макроса %s использовать %i, который при вызове программы будет заменен на имя файла, на коем стоит курсор в тексте. Иными словами, функция та же, что по "Открыть под курсором", только с нужной вам программой. Так удобно, скажем, использовать какой-нибудь графический редактор, если вам надо быстро подправить картинку в HTML или LaTeX. Пример:

gimp=gimp %s


Функции > Озвучивание

Будучи собран с поддержкой Speech Dispatcher (Linux), ТИА может зачитывать вслух текст, покамест эта возможность скромна и обкатывается. А по умолчанию еще и отключена - включить ее надо в Наладка - Озвучивание - Разрешить чтение вслух. Там же опция Показывать только голоса текущей локали полезна, чтобы в списке ниже были доступны только голоса языка вашей системы, а не все вообще, которых может быть очень много.

В плане воспроизведения голоса ТИА целиком зависит от настроек Speech Dispatcher, то есть - установив Speech Dispatcher, надо к нему доустановить какой-нибудь речевой синтезатор, например Festival или RHVoice. Затем уже к этому синту установить, если они не установлены, файлы голосов. Потом настроить Speech Dispatcher для вывода на выбранный синтезатор. Ниже пример для RHVoice:

1. Скопируйте каталог /etc/speech-dispatcher в $HOME/.config

2. Там редактируйте speechd.conf, где раскоментируйте строку с AddModule "rhVoice" и добавьте строку: DefaultModule rhvoice

3. Чтобы избежать треска и щелчков при работе другого звукового ПО (например Reaper или Ardour) по время работы ТИА (при включеной озвучке), то, если вы используете Pipewire или Pulse, раскоментируйте строку AudioOutputMethod "pulse"


Функции > Проверка правописания

Для проверки правописания в TEA используются движкиASpell, Hunspell и Nuspell (доступен только для сборки при Qt6).

Во-первых, надо решить, какой движок использовать. Выбрать движок можно на странице Наладка > Функции, там есть список Движок проверки правописания. Количество пунктов в нем зависит от вашей сборки TEA - я не знаю, какая у вас. В Linux, TEA может быть собран со всеми тремя движками, с каким-то одним, или вообще без проверки правописания. Для Windows я делаю сборки сам, с Hunspell и Aspell. Сам использую в работе Hunspell.

Как настроить Aspell для Windows? Скачайте полный установщик Aspell с http://aspell.net/win32/. Затем, с той же страницы, скачайте и установите словари - при установке вас будут спрашивать, в какую папку - и предлагать папку, где уже установлен Aspell. Соглашайтесь. По умолчанию это C:\Program Files\Aspell. Иногда при установке словаря появляется консольное окошко с запросом - мол, переписать ли файл или тому подобное - там надо нажать "A" (английскую), - мол, All - переписать все файлы. Далее, установив словари, задаем в TEA путь к Aspell, в Наладка - Функции - Папка Aspell. И перезагружаем TEA. Теперь в Функции - Языки проверки правописания, выбираем язык, а потом через Функции - Проверка правописания, запускаем проверку.

В Linux, при использовании Aspell предполагается, что у вас в системе установлены Aspell-словари, соответствующие вашей локали. При первом запуске TEA полагает, что такой словарь есть, поэтому по умолчанию таковой будет использован. Иначе же можно сменить язык проверки орфографии вручную, с помощью меню Языки проверки правописания. Выбранный язык сохранится до следующего раза работы с TEA и далее, пока вы снова его не поменяете.

Для Hunspell в ТИА нет "общесистемных" словарей, и существует два варианта установки словарей для этого движка. Вариант первый - скачать архивы со словарями с хранилищ расширений для LibreOffice. Там предлагается скачать файлы с расширением OXT. На деле это обычный ZIP-архив. Просто переименуйте скачанный файл, дав ему расширение ZIP. Создайте каталог для словарей, распакуйте туда получившийся ZIP. Собственно, оттуда нужны только файлы с расширениями AFF и DIC. Каталог со словарными файлами укажите в настройках TEA, в поле Каталог со словарями для Hunspell на странице Наладка > Функции, а затем выбрать нужный словарь в меню Функции > Языки проверки правописания. Этот же пункт выбираем после каждой смены движка. И он запомнится - можно будет потом просто вызывать "Проверить правописание", без постоянного выбора языка. Хороший русский словарь можно взять по адресу http://code.google.com/p/hunspell-ru/. После добавления словарей лучше перезапустить TEA.

Второй вариант - подключение к TEA словарей Hunspell, которые уже установлены для других программ - например Firefox или LibreOffice. При установленном в локальный каталог пользователя Firefox, словари лежат в firefox/dictionaries.

Nuspell. При использовании задействует словари от Hunspell и LibreOffice, находя их своим, келейным способом.

Для всех движков и языков ТИА предоставляет свой отдельный механизм пользовательского словаря, куда можно добавлять правильные слова и удалять их оттуда.

Пункт Проверить правописание проверяет текст используя именно этот вот запомненный язык. Ошибочные слова (вернее те, которых нет в словаре) будут подчеркнуты красным. Исправить слово можно вручную, либо выбрав вариант из списка, предложенного TEA по выбору пункта меню Предположить. После этого исправленное слово остается подчеркнутым, ибо движок проверки орфографии действует в ручном режиме и не обновляет раскраску ошибок до повторной проверки. А чтобы скрыть раскраску вообще, есть пункт меню Вид > Скрыть помеченное как ошибки. Исправленное слово, да и вообще любое, на котором стоит курсор, можно добавить в словарь с помощью пункта Добавить в словарь.

Удалить из словаря - удаляет слово (по месту курсора) из пользовательского словаря.


Нав

Метки, Обновить метки. Эта штука полезна для правки больших текстов. Вы можете прямо в тексте делать поименованные метки и потом быстро перемещаться между ними при помощи меню Метки либо через выпадающий список рядом с кнопкой на панели инструментов. Как создавать метки? Пишете к тексте любое слово и обрамляете его "[?" и "?]". Эти вот начальные и замыкающие отметки можно переопределить в Наладка - Функции - Метки. Итак, написали такую метку, нажали или выбрали пункт меню Обновить метки, и к меню Меток и к выпадающему списку добавится новая метка. Выбираем её - перемещаемся в то место текста, где она написана. Метки обновляются только вручную, то есть если вы загрузили файл с метками, надо всё равно их обновить.

Пример. Создадим в разных местах текста метки "[? собаки ?]" и "[? коты ?]". Обновляем метки. Готово!

К строке - перемещает курсор на строку по номеру, указанному в Знаменитом поле ввода.

Запомнить место/К сохраненному месту - парные функции, служащие для быстрого перемещения туда-сюда по документу. Запомнили место - Запомнить место, потом прокрутили куда надо, посмотрели, потом захотели вернуться назад - выбрали К сохраненному месту. Удобно в работе с большими документами!


ИДЕ

В TEA есть некоторые функции IDE, которые помогают в разработке программ, не усложняя при этом жизнь и не заморачивая вас ненужными настройками и кнопками. Отмечу, что как IDE, ТИА проще использовать на пару с возможностью использования профилей (в меню Вид). Создаете один профиль для допустим правки обычых текстовых файлов, другой профиль для разработки, для правки исходников - и переключаетесь между ними. Например у меня в "текстовом" профиле включен перенос слов, размер окна небольшой, шрифы такие-то. А в профиле "разработки" - перенос строк отключен, окно шире, шрифты другие.

Но как же использовать ТИА в качестве IDE? Сначала надо создать файл проекта ТИА. Проще всего сделать это через Функции - Поместить - Шаблон проекта ТИА.

Сохраните его в корневом каталоге исходника вашей программы, вашего проекта - под любым именем, но с расширением teaproject, например myproject.teaproject.

файл .teaproject это простой текстовый файл с записями вида переменная=значение. Они отражают настройки проекта. Вы можете держать несколько .teaproject-файлов в одном каталоге, под разные конфигурации сборки - скажем, один файла проекта под cmake, другой под meson.

Вот примеры трех файлов проекта, которые я использую при разработке TEA - длоя сборки при помощи qmake/make, meson/ninja и cmake/make.

tea-make.teaproject

command_build=make
command_clean=make clean
command_run=bin/tea --m&

tea-meson.teaproject

dir_build=b
command_build=ninja
command_clean=ninja -t clean
command_run=./tea --m&

tea-cmake.teaproject

dir_build=cmake
command_build=make
command_clean=make clean
command_run=./tea --m&

Синтаксис очень прост:

dir_build - каталог, в котором будет запущена команда сборки command_build. Если dir_build не указана, то в ее качестве будет использован каталог, где лежит файл .teaproject. dir_build может быть как абсолютным путем, так и относительным - в последнем случае за основу берется, опять же, каталог с .teaproject, то есть корневой каталог вашей программы.

command_build может быть любой, на ваш выбор: make, ninja, и т.д.

command_run - запускается dir_build для выполнения скомпилированной программы.

command_clean - запускается в dir_build для очистки проекта.

Когда вы открываете или сохраняете .teaproject-файл, или переключаетесь на его вкладку, TEA перечитывает С ДИСКА его содержимое и делает проект текущим, именно с ним будут работать команды из меню IDE (по запуску, очистке и сборке). Повторюсь - TEA считывает файл-проекст С ДИСКА, а не тот текст файла-проекта, который редактируется в окне. Пока вы этот текст не сохраните, ТИА его не увидит.

Итак, можно открыть несколько файлов-проектов и быстро переключаться между ними. При этом, загруженный файл проекта не влияет на загруженные файлы исходников, вы можете одновременно с этим править любые файлы из любых проектов. Файл проекта, фактически, никак не привязан к исходным файлам, это просто надстройка для сборки программы.

Во время сборки, сообщения компилятора будут выводиться в Логмемо. Если дважды щелкнуть мышью по выделенному жирным шрифтом указанию на место ошибки, TИА откроет нужный файл в нужном месте.


Фп

Как вы уже догадались, ФП - это сокращение от "файловый приказчик". В меню этом сосредоточены функции, относящиеся к ФП. Часть их повторяет действия, закрепленные за кнопками на вкладке приказчика, часть же - дополняет.

Чтобы использовать некоторые функции, например такие как получение проверочной суммы (а вернее, хэш-суммы) по алгоритму MD5, надо сначала выделить файл (сделать его текущим в файловом приказчике с помощью мыши или клавиатуры), а уже потом применить функцию.

ФП > Переименование - это подменю позволяет гибко переименовыми имена файлов. Все функции этого подменю используют переметр из ЗПВ.

ФП > Переименование > Добавить нули к именам - предваряет нулями каждое выбранное имя файла. Имя файла должно содержать нумерацию, например: page1, page100. Такие файлы, без нулей перед числом, не могут быть правильно отсортированы. Как быть?

Надо ввести длину имени файла в ЗПВ и применить функцию Добавить нули к именам. Из имен файлов будут удалены все нечисловые символы, а числа дополнены нужным количество нулей до заданной длины имени файла. Пример:

У нас есть файлы:
page1.txt
page100.txt

Поместим 5 в ЗПВ, применим функцию.

Имена файлов станут такими:
00001.txt
00100.txt

ФП > Переименование > Удалить первые N символов из имен - N это количество, заданное в ЗПВ. Функция удаляет N первых символов из каждого выделенного имени файла.

ФП > Переименование > Заменить в именах файлов - заменить подстроку в каждом выделенном имени файлов. Формат для ЗПВ: старое значение~новое. Например: .jpeg~.jpg

ФП > Переименование > Применить шаблон - применяет шаблон к каждому выделенному имени файла. Доступны макросы, обозначающие части исходного имени файла: %filename (= filename.ext), %ext (= ext), %basename ( = filename). Например, мы хотим переименовать 01.jpeg, 02.jpeg в 01.jpg, 02.jpg. Шаблон для ЗПВ будет таким: %basename.jpg - %basename (имя файла без расширения) и к нему добавится ".jpg".

Смотреть картинку - то же, что нажать F2 на имени файла-картинки. Отображает оный файл во встроенной гляделке.

Отметить по регэкспу/Снять выделение по регэкспу - функции для выделения и его снятия по шаблону, задающемуся с помощью регулярного выражения. Например, чтобы выделить все файлы с расширением txt, надобно в Знаменитое поле ввода вписать регэксп ".*\.txt$" (без кавычек!) и применить Отметить по регэкспу. Затем можно нажать кнопку Открыть, чтобы загрузить выделенные файлы.

Подменю сведения о файле

Тут находится, кроме прочего, любопытнейшая функция Полные сведения. Она не токмо выдает полные сведения о времени создания файла и тому подобным вещам, но и, ежели файл этот в формате WAV, выводит его параметры, а для 16-битного PCM быстро подсчитывает RMS обоих каналов.

А функция Подсчитать строки в выбранных файлах делает то, что написано, считая при этом и пустые строки.

Подменю Картинки

Функции Масштабировать по стороне и Масштабировать в процентах служат для массового изменения размера картинок - выделенных файлов. Измененные картинки копируются в новосозданный каталог в той же папке, где вы выделили картинки. Имя этого каталога состоит из слова images-out и случайного числа. На вкладке Наладка > Функции можно выбрать формат выходных файлов - в списке Выходной формат преобразованных картинок, и включить билинейную фильтрацию, которая сглаживает получившиеся изображения.

Итак, чтобы масштабировать картинки, выделите их, а затем, введя в Знаменитое поле ввода количество пикселов или процентов, примените один из пунктов меню: Масштабировать по стороне или Масштабировать в процентах. В чем разница?

Масштабировать в процентах - допустим, вы хотите уменьшить каждую картинку на 50 процентов. Выделите файлы, введите в Знаменитое поле ввода число 50 и примените эту функцию.

Масштабировать по стороне - в этом случае в Знаменитое поле ввода надо ввести размер наибольшей стороны. У обычной, прямоугольной фотографии всегда есть такая сторона - для вертикальной фотки это высота, а для горизонтальной - ширина. Например, вы скинули с мобилы или цифровика фотографии и хотите их преобразовать в разрешение 640x480 (а вертикальные в 480x640). Стало быть, в поле ввода надо задать значение 640. Примечание: нет, TEA тут не читает данные EXIF.

Создать веб-галерею - это чтобы быстро склепать простенькую табличную веб-галерею. Накидайте в каталог файлов с картинками, там же сохраните HTML-файл и откройте его в TEA. Потом в файловом приказчике перейдите в каталог с этим файлом и картинками. Выделите картинки, примените Создать веб-галерею. Создадутся миниатюры и HTML-табличка, которая вставится в файл по месту курсора.

Всякие настройки веб-галереи (размер миниатюр, количество ячеек в ряду) доступны на странице Наладка > Картинки.


Вид

Палитры - благодать-то какая! Можно выбрать палитру, чтоб иными цветами текст заиграл. Чудо-чудное, диво-дивное. А кто Кулибин, тот может на основе палитр из исходника (см. каталог palettes) создавать свои и сохранять в каталог $HOME/.config/tea/palettes. У пользователей Windows он где-то во глубине сибирских руд. А именно в Documents and Settings/имя пользователя/tea/palettes. А затем выбрать свою палитру в меню Вид > Палитры.

Профили - в этом подменю можно выбрать профиль - поименованный набор настроек, предварительно сохраненный с помощью пункта меню Сохранить профиль. В профиле сохраняются размер, положение главного окна, настройки переноса и нумерации строк и тому подобное.

Скрыть помеченное как ошибки - скрывает цветные подчеркивания, оставленные проверкой правописания.

Переключить перенос строк - переключает оный для текущего документа.

Клавиатуры - список созданных пользователем виртуальных "клавиатур", которые открываются в отдельных окнах. Каждая такая клавиатура - обычный текстовый файл, положенный в $HOME/.config/tea/keyboards. В таком файле содержатся строки, разделенные символом "|". Каждая строка - один ряд создаваемой клавиатуры. Пример:

dog|cat
apple|orange

Таким образом можно создать клавиатуру для быстрого ввода символов какого-либо алфавита или целых слов, строк.


Настройки

В TEA нет отдельного окна настроек. Все настройки доступны прямо из главного окна, на вкладке Наладка. Изменения вступают в силу сразу же и сохраняются автоматически.

Дополнительная подсветка - включает подсветку текущей строки и парных скобок.

На вкладке Клавиатура каждому пункту меню, включая разные сниппеты, закладки и тому подобное, можно назначить сочетание "горячих клавиш". Для этого выберите в списке пункт меню, потом в поле ввода справа от списка нажмите нужное вам сочетание клавиш, а затем нажмите кнопку Назначить. Чтобы удалить привязку клавиш, выберите в списке пункт меню и нажмите кнопку Удалить привязку. Чтобы переназначить сочетание клавиш, надо сначала Удалить привязку для пункта, которому было назначено сочетание, а затем назначить его другому пункту.

Перенос строк - включить или выключить оный для всех открытых документов, а также для последующих новых и открываемых.

А вот еще любопытная опция Брать настройку переноса строк из модуля подсветки на странице Интерфейс. Если она включена, то сведения о переносе строк будут браться не из опции Перенос строк, а из настроек, заданных в модулях подсветки. Например, для С++ перенос строк отключен. Для обычного текста или для HTML - включен.


Клавиатура

При включенной опции Наладка -Общие - Использовать джойстик как курсорные клавиши удобно прокручивать текст, если допустим вы сидите далеко от монитора и читаете текст с экрана, записывая видео для Ютуба. Замечу, что при этом считываются единичные нажатия на крестовину джойпада, а не удержание ее в определенном направлении.


Слово про автосохранение

Расцвела буйным цветом в саду вишня. В ТИА есть ряд функций, которые объединены общим понятием автосохранения, но они имеют разную природу.

Во-первых, есть возможность временно автоматически сохранять несохраненные новые файлы. Пока вы их не сохранили под каким-то именем, можно считать их безымянными буферами. ТИА может сохранять их при выходе (не просто при закрытии вкладки, а именно при выходе из ТИА), если включить опцию Наладка - Общие - Автосохранение - Временно сохранять несохраненные буферы при выходе.

Также в ТИА есть понятие автосохраняемых файлов. Любой текущий файл может быть добавлен в список автосохраняемых через меню Файл - Действия над файлом - Пометить как автосохраняемый файл (и ниже есть пункт отмены этого дела). Такой автосохраняемый файл будет сохраняться автоматически при закрытии (закрытии вкладки или просто закрытии ТИА вообще).

Буфера и автосохранямые файлы могут также сохраняться периодически, по времени, что управляется через Наладка - Общие - Автосохранение - Сохранять буферы и автосохраняемые файлы каждые N секунд.


Тайная власть

Познай истину! Рептилоиды возобладают над родом человеческим, если ты не знаешь тайных настроек TEA. К ним нет графического интерфейса, их можно добавлять и править только через главный файл настроек TEA, который сокрыт от любопытных глаз в $HOME/.config/tea\tea.conf (или, под Windows - буква:\Documents and Settings\username\tea\tea.conf). Некоторые изменения вступят в силу после перезапуска TEA. Познай же истину:

recent_list.max_items=максимальное количество элементов в списке последних файлов, по умолчанию 21


Примечания

Поддерживается старорежимная опция командной строки "--charset=codepage". С её помощью вы можете принудительно задать (по умолчанию-то используется UTF-8) кодировку для файла, открываемого через командную строку. Например:

tea --charset=CP-1251 file1.txt file2.txt --charset=utf-8 file3.txt

Документация, все картинки прочее в TEA, кроме исходных кодов, являются общественным достоянием (public domain). Код TEA лицензирован под GPL версии 3, текст которой вы можете прочитать в меню Помощь - Лицензия, а неофициальный перевод с английского - по этой ссылке.

О пользователь, взалчущий создавать свои правила подсветки для разных языков программирования. К тебе эти строки! Правила подсветки задаются в xml-файлах, примеры коих ты имеешь счастье видеть в каталоге hls исходника. Свои же файлы правил надобно помещать в каталог настроек TEA, подкаталог hls. Для *nix это $HOME/.config/tea/hls, а под Windows Documents and Settings/имя пользователя/tea/hls. Цвета в правилах подсветки лучше прописывать, ссылаясь на имена из палитры (см. файлы палитр в исходнике), т.е. пишите "preproc" или "types" вместо шестнадцатеричных значений цветов. Это нужно для правильной работы цветовых схем.

Давайте свои палитры и правила подсветки в TEA! Просьба отдавать это со статусом "общественное достояние" - public domain. Именно так распространяются все файлы TEA, кроме исходника. Почему? Хлопот меньше. Не надо таскать с программой лишние лицензии.


Мои иные проекты

Мой айтишный сайт

Студия Дрымба - сайт независимой киностудии "Дрымба", снимающей любительские фильмы практически с нулевым бюджетом. Я там один из режиссеров и актеров.


Поддержать разработку

Поддержать разработку ТИА вы можете через Paypal, послав денежку на peter.semiletov@gmail.com

tea-qt-63.3.0/palettes/000077500000000000000000000000001476733534200146355ustar00rootroot00000000000000tea-qt-63.3.0/palettes/Berry000066400000000000000000000006761476733534200156540ustar00rootroot00000000000000class=#ff4a83 label=#ffd060 type=#ff418d functions=#55aaff digits=#e8e81f digits-float=#e8e81f operator=white include=darkGreen preproc=darkBlue single comment=gray quotes=#26c5ff mcomment-start=gray text=white background=#663e94 sel-text=black sel-background=#a6ffdd keywords=#ffaa00 error=red cur_line_color=#2e3436 tags=#ff2063 modifiers=#b5b500 brackets=yellow margin_color=gray linenums_bg=#e4e4e4 backgroundmark=#ff79f4 foregroundmark=whitetea-qt-63.3.0/palettes/Linux MC000066400000000000000000000006171476733534200161430ustar00rootroot00000000000000class=#ffffe9 type=#ffffe9 functions=#eeffee digits=#ffff00 include=#4ad5ff preproc=#4ad5ff single comment=#edeaff quotes=#ffff00 mcomment-start=#edeaff text=white background=#3238aa sel-text=#3238aa sel-background=white keywords=#d38448 error=#ff557f cur_line_color=#aaaa7f tags=#eee4f5 modifiers=#f3ffff linenums_bg=#3e4348 brackets=#deddda backgroundmark=red foregroundmark=white operator=#c4a000tea-qt-63.3.0/palettes/Morning000066400000000000000000000006711476733534200161750ustar00rootroot00000000000000class=#0c084b label=#ff557f type=#5714dd functions=blue digits=darkGreen digits-float=#005500 operators=#ffaa00 include=darkGreen preproc=darkBlue single comment=gray quotes=#ab0c34 mcomment-start=gray text=black background=#729fcf sel-text=white sel-background=black keywords=black error=red cur_line_color=#EEF6FF tags=darkBlue modifiers=#55557f brackets=#613583 margin_color=gray linenums_bg=#e4e4e4 backgroundmark=blue foregroundmark=redtea-qt-63.3.0/palettes/Navigator000066400000000000000000000006711476733534200165160ustar00rootroot00000000000000class=#0c084b label=#ff557f type=#deddda functions=blue digits=darkGreen digits-float=#005500 operators=#ffaa00 include=darkGreen preproc=darkBlue single comment=gray quotes=#8ae234 mcomment-start=gray text=white background=#555753 sel-text=white sel-background=black keywords=black error=red cur_line_color=#2e3436 tags=darkBlue modifiers=#55557f brackets=#26a269 margin_color=gray linenums_bg=#e4e4e4 backgroundmark=blue foregroundmark=redtea-qt-63.3.0/palettes/Pines000066400000000000000000000006331476733534200156400ustar00rootroot00000000000000class=#e6eaff type=#e6eaff functions=#e6eaff digits=#e6eaff include=#ffaa7f preproc=#ffaa7f single comment=#f7f0ff quotes=#ffff00 mcomment-start=#f7f0ff text=white background=#002a00 sel-text=#0b7d35 sel-background=white keywords=#fff6fc error=#ff7b72 cur_line_color=#0c75ff tags=white modifiers=#e6eaff brackets=#ff5500 label=#ffaa7f linenums_bg=#002b00 backgroundmark=red foregroundmark=black operator=#50d0ebtea-qt-63.3.0/palettes/TEA000066400000000000000000000006721476733534200151760ustar00rootroot00000000000000class=#5714dd label=#ff557f type=#5714dd functions=blue digits=darkGreen digits-float=#005500 operators=#ffaa00 include=darkGreen preproc=darkBlue single comment=gray quotes=#ab0c34 mcomment-start=gray text=black background=white sel-text=white sel-background=black keywords=darkBlue error=red cur_line_color=#EEF6FF tags=darkBlue modifiers=#55557f brackets=#241f31 margin_color=gray linenums_bg=#e4e4e4 backgroundmark=blue foregroundmark=redtea-qt-63.3.0/palettes/Turbo90000066400000000000000000000006231476733534200160250ustar00rootroot00000000000000class=#ffffe9 type=#ffffe9 functions=#eeffee digits=#ffff00 include=#4ad5ff preproc=#4ad5ff single comment=#edeaff quotes=#ffff00 mcomment-start=#edeaff text=#ffff00 background=#1818b2 sel-text=#1818b2 sel-background=#b2b2b2 keywords=#ffff00 error=#ff557f cur_line_color=#aaaa7f tags=#eee4f5 modifiers=#f3ffff linenums_bg=#3e4348 brackets=#55aaff backgroundmark=red foregroundmark=white operator=#fcaf3etea-qt-63.3.0/palettes/UNIX000066400000000000000000000006031476733534200153420ustar00rootroot00000000000000class=#b0c5ff type=white functions=#c0ccff digits=green include=#f3ffbc preproc=#f3ffbc single comment=#f5edff quotes=#ff7386 mcomment-start=#f5edff text=#00ff00 background=black sel-text=black sel-background=#00ff00 keywords=white tags=white modifiers=white brackets=#ffaa00 label=#ff007f linenums_bg=#0a0d4b backgroundmark=red foregroundmark=white operator=green cur_line_color=#555753tea-qt-63.3.0/palettes/Vinyl000066400000000000000000000006441476733534200156650ustar00rootroot00000000000000class=#ffe9e4 type=#ffebf2 functions=#aaffff digits=#00ff00 include=#4ad5ff preproc=#ffff7f single comment=#e2f9ff quotes=#ff70bd mcomment-start=#e2f9ff text=#f3f4ff background=#64523e sel-text=#64523e sel-background=#f3f4ff keywords=#37dbff error=#ff708f cur_line_color=#452e0c tags=#a0ffa3 modifiers=#f3ffca brackets=#ff7800 label=#ffaa00 linenums_bg=#53552f backgroundmark=yellow foregroundmark=black operator=#edd400tea-qt-63.3.0/palettes/paper-old000066400000000000000000000006751476733534200164530ustar00rootroot00000000000000class=darkBlue label=#ff557f type=darkBlue functions=blue digits=darkGreen digits-float=#005500 operators=#ffaa00 include=darkGreen preproc=darkBlue single comment=gray quotes=#0000ff mcomment-start=gray text=black background=#fff7ef sel-text=#fff7ef sel-background=black keywords=black error=red cur_line_color=#EEF6FF tags=darkBlue modifiers=#55557f brackets=#241f31 margin_color=gray linenums_bg=#e4e4e4 backgroundmark=blue foregroundmark=redtea-qt-63.3.0/palettes/paper-toilet000066400000000000000000000006731476733534200171730ustar00rootroot00000000000000class=#2e0e8c label=#ff557f type=#2e0e8c functions=blue digits=darkGreen digits-float=#005500 operators=#ffaa00 include=darkGreen preproc=darkBlue single comment=gray quotes=#7f1d3d mcomment-start=gray text=black background=#f3f3f3 sel-text=#f3f3f3 sel-background=black keywords=black error=red cur_line_color=#EEF6FF tags=darkBlue modifiers=#55557f brackets=#3d3846 margin_color=gray linenums_bg=#e4e4e4 backgroundmark=blue foregroundmark=redtea-qt-63.3.0/resources.qrc000066400000000000000000000064271476733534200155460ustar00rootroot00000000000000 encsign/KOI8-R encsign/KOI8-U encsign/CP1251 encsign/CP866 icons/file-new.png icons/file-open.png icons/file-open-active.png icons/file-save.png icons/file-save-active.png icons/file-save-as.png icons/edit-copy.png icons/edit-copy-active.png icons/edit-cut.png icons/edit-cut-active.png icons/edit-paste.png icons/edit-paste-active.png icons/current-list.png icons/fn-spell-check.png icons/tea_icon_v2.png icons/tea-icon-v3-01.png icons/tea-icon-v3-02.png icons/tea-icon-v3-03.png icons/labels.png translations/ru.qm translations/de.qm translations/fr.qm translations/pl.qm translations/es.qm images/moon-phases.png manuals/en.html manuals/ru.html manuals/pl.html text-data/lorem-ipsum text-data/morse-en text-data/morse-ru text-data/template-html text-data/template-html5 text-data/template-teaproject text-data/tpl_cpp.cpp text-data/tpl_c.c text-data/cm-tf-names themes/TEA/stylesheet.css themes/Cotton/stylesheet.css themes/Plum/stylesheet.css themes/Smaragd/stylesheet.css themes/Turbo/stylesheet.css themes/Vegan/stylesheet.css themes/Yagodnaya/stylesheet.css palettes/paper-old palettes/paper-toilet palettes/TEA palettes/UNIX palettes/Linux MC palettes/Pines palettes/Vinyl palettes/Berry palettes/Turbo90 palettes/Morning palettes/Navigator hls/awk.xml hls/xml.xml hls/clike.xml hls/po.xml hls/java.xml hls/python.xml hls/pascal.xml hls/seed7.xml hls/nsis.xml hls/fortran.xml hls/cs.xml hls/basic.xml hls/tex.xml hls/verilog.xml hls/d.xml hls/php.xml hls/lout.xml hls/lua.xml hls/perl.xml hls/vala.xml hls/bash.xml hls/nasm.xml hls/lilypond.xml hls/wikitext.xml hls/r.xml hls/subtitles.xml hls/hs.xml hls/rust.xml README.md NEWS TODO NEWS-RU COPYING AUTHORS ChangeLog tea-qt-63.3.0/src/000077500000000000000000000000001476733534200136035ustar00rootroot00000000000000tea-qt-63.3.0/src/calendar.cpp000066400000000000000000000201401476733534200160550ustar00rootroot00000000000000/* this code is Public Domain */ #include #include #include #include #include #include //#include "utils.h" #include "calendar.h" inline int tropical_year (int year) { return 1 + (year % 19); } int moon_phase_leueshkanov (int year, int month, int day) { int L = tropical_year (year); int phase = L * 11 - 14 + day + month + 3; return phase % 30; } /* moon phase - based on Ben Daglish JavaScript code from based on http://www.ben-daglish.net/moon.shtml */ int moon_phase_trig2 (int year, int month, int day) { double n = floor (12.37 * (year - 1900 + ((1.0 * month - 0.5) / 12.0))); double RAD = 3.14159265 / 180.0; double t = n / 1236.85; double t2 = t * t; double as = 359.2242 + 29.105356 * n; double am = 306.0253 + 385.816918 * n + 0.010730 * t2; double xtra = 0.75933 + 1.53058868 * n + ((1.178e-4) - (1.55e-7) * t) * t2; xtra += (0.1734 - 3.93e-4 * t) * sin (RAD * as) - 0.4068 * sin (RAD * am); double i = (xtra > 0.0 ? floor (xtra) : ceil (xtra - 1.0)); QDate d (year, month, day); int j1 = d.toJulianDay(); int jd = (2415020 + 28 * n) + i; int r = (j1 - jd + 30) % 30; if (r == 0) r = 30; return r; } /*This simply mods the difference between the date and a known new moon date (1970-01-07) by the length of the lunar period. For this reason, it is only valid from 1970 onwards.*/ int moon_phase_simple (int year, int month, int day) { int lp = 2551443; QDateTime now (QDate (year, month - 1, day), QTime (20, 35, 0)); QDateTime new_moon (QDate (1970, 0, 7), QTime (20, 35, 0)); double phase = ((now.toMSecsSinceEpoch() - new_moon.toMSecsSinceEpoch()) / 1000) % lp; return floor (phase / (24 * 3600)) + 1; } /* Conway This is based on a 'do it in your head' algorithm by John Conway. In its current form, it's only valid for the 20th and 21st centuries, but I'm sure John's got refinements. :) */ int moon_phase_conway (int year, int month, int day) { int r = year % 100; r %= 19; if (r > 9) r -= 19; r = ((r * 11) % 30) + month + day; if (month < 3) r += 2; r -= ((year < 2000) ? 4 : 8.3); r = (int) floor (r + 0.5) % 30; return (r < 0) ? r + 30 : r; } /*Trig1 This is based on some Basic code by Roger W. Sinnot from Sky & Telescope magazine, March 1985. I don't pretend to understand it - something to do with a first-order approximation to the 'real' calculation of the position of the bodies involved - which I'm still working on... :) */ inline double GetFrac (double fr) { return (fr - floor (fr)); } int moon_phase_trig1 (int year, int month, int day) { QDate d (year, month, day); int thisJD = d.toJulianDay(); double degToRad = 3.14159265 / 180; double K0, T, T2, T3, J0, F0, M0, M1, B1; int oldJ = 0; K0 = floor ((year - 1900) * 12.3685); T = (year - 1899.5) / 100; T2 = T * T; T3 = T * T * T; J0 = 2415020 + 29*K0; F0 = 0.0001178 * T2 - 0.000000155 * T3 + (0.75933 + 0.53058868 * K0) - (0.000837 * T + 0.000335 * T2); M0 = 360 * (GetFrac (K0 * 0.08084821133)) + 359.2242 - 0.0000333 *T2 - 0.00000347 * T3; M1 = 360 * (GetFrac (K0 * 0.07171366128)) + 306.0253 + 0.0107306 * T2 + 0.00001236 * T3; B1 = 360 * (GetFrac (K0 * 0.08519585128)) + 21.2964 - (0.0016528 * T2) - (0.00000239 * T3); int phase = 0; int jday = 0; while (jday < thisJD) { double F = F0 + 1.530588 * phase; double M5 = (M0 + phase * 29.10535608) * degToRad; double M6 = (M1 + phase * 385.81691806) * degToRad; double B6 = (B1 + phase * 390.67050646) * degToRad; F -= 0.4068 * sin (M6) + (0.1734 - 0.000393 * T) * sin (M5); F += 0.0161 * sin (2 * M6) + 0.0104 * sin (2 * B6); F -= 0.0074 * sin (M5 - M6) - 0.0051 * sin (M5 + M6); F += 0.0021 * sin (2 * M5) + 0.0010 * sin (2 * B6 - M6); F += 0.5 / 1440; oldJ = jday; jday = J0 + 28 * phase + floor(F); phase++; } return (thisJD - oldJ) % 30; } int moon_phase_by_algo (int v, int year, int month, int day) { int r = 0; switch (v) { case MOON_PHASE_TRIG2: r = moon_phase_trig2 (year, month, day); break; case MOON_PHASE_TRIG1: r = moon_phase_trig1 (year, month, day); break; case MOON_PHASE_CONWAY: r = moon_phase_conway (year, month, day); break; case MOON_PHASE_LEUESHKANOV: r = moon_phase_leueshkanov (year, month, day); break; case MOON_PHASE_SIMPLE: r = moon_phase_simple (year, month, day); break; }; return r; } CCalendarWidget::CCalendarWidget (QWidget *parent, const QString &a_dir_days): QCalendarWidget (parent) { dir_days = a_dir_days; moon_phase_algo = MOON_PHASE_TRIG2; moon_mode = false; if (! moon_tiles.load (":/images/moon-phases.png")) qDebug() << ":/images/moon-phases.png" << " is not loaded"; northern_hemisphere = true; //setHeaderTextFormat (const QTextCharFormat & format); QTextCharFormat tformat; tformat.setForeground (QBrush (Qt::white)); tformat.setBackground (QBrush (Qt::black)); tformat.setFontWeight (QFont::Bold); setWeekdayTextFormat (Qt::Monday, tformat); setWeekdayTextFormat (Qt::Tuesday, tformat); setWeekdayTextFormat (Qt::Wednesday, tformat); setWeekdayTextFormat (Qt::Thursday, tformat); setWeekdayTextFormat (Qt::Friday, tformat); setWeekdayTextFormat (Qt::Saturday, tformat); setWeekdayTextFormat (Qt::Sunday, tformat); } #if QT_VERSION < 0x060000 void CCalendarWidget::paintCell (QPainter *painter, const QRect &rect, const QDate &date) const #else void CCalendarWidget::paintCell (QPainter *painter, const QRect &rect, QDate date) const #endif { QSize fsize = fontMetrics().size (Qt::TextSingleLine, "A"); if (moon_mode) { int moon_day = moon_phase_by_algo (moon_phase_algo, date.year(), date.month(), date.day()); bool has_image = true; if (moon_day == 0 || moon_day == 30 || moon_day == 1) has_image = false; //вычисляем ряд и колонку int cursorOffset = moon_day; /* int off = 0; int row = 0; while (cursorOffset >= (off + 8)) { off += 7; row++; } int col = cursorOffset - off; */ int row = moon_day / 7; if ((moon_day % 7 == 0) && (row != 0)) row--; int col = cursorOffset - (row * 7); /* qDebug() << "moon day = " << moon_day; qDebug() << "moon_day / 7 = " << (double) moon_day / 7; qDebug() << "trow = " << trow; qDebug() << "row = " << row; qDebug() << "col = " << col; qDebug() << "tcol = " << tcol; */ //вычисляем, откуда копировать int pad = 3; int x = (col - 1) * 73 + (pad * col) - pad; int y = row * 73 + (pad * row); QRect r (x, y, 66, 73); QImage tile = moon_tiles.copy (r); QColor bg_color (Qt::black); painter->fillRect (rect, bg_color); if (has_image) { if (northern_hemisphere) painter->drawImage (rect.x(), rect.y(), tile); else painter->drawImage (rect.x(), rect.y(), tile.mirrored (true, false)); } painter->setPen (QPen (Qt::yellow)); QTextCharFormat tcf = dateTextFormat (date); if (tcf.fontStrikeOut()) painter->setPen (QPen (Qt::magenta)); else if (tcf.fontUnderline()) painter->setPen (QPen (Qt::red)); painter->drawText (QPoint (rect.x() + 5, rect.y() + fsize.height()), date.toString("dd") + " / " + QString::number (moon_day)); if (selectedDate() == date) { QPen dpen (Qt::yellow); dpen.setWidth (5); painter->setPen (dpen); painter->drawRect (rect); } } else QCalendarWidget::paintCell (painter, rect, date); } void CCalendarWidget::do_update() { updateCells(); } tea-qt-63.3.0/src/calendar.h000066400000000000000000000020001476733534200155150ustar00rootroot00000000000000#ifndef CALENDAR_H #define CALENDAR_H #include class CCalendarWidget: public QCalendarWidget { Q_OBJECT protected: #if QT_VERSION < 0x060000 void paintCell (QPainter *painter, const QRect &rect, const QDate &date) const; #else void paintCell (QPainter *painter, const QRect &rect, QDate date) const; #endif public: QImage moon_tiles; QString dir_days; bool moon_mode; bool northern_hemisphere; int moon_phase_algo; CCalendarWidget (QWidget *parent, const QString &a_dir_days); void do_update(); }; enum { MOON_PHASE_TRIG2 = 0, MOON_PHASE_TRIG1, MOON_PHASE_CONWAY, MOON_PHASE_LEUESHKANOV, MOON_PHASE_SIMPLE }; int moon_phase_by_algo (int v, int year, int month, int day); int moon_phase_trig2 (int year, int month, int day); int moon_phase_simple (int year, int month, int day); int moon_phase_conway (int year, int month, int day); int moon_phase_trig1 (int year, int month, int day); int moon_phase_leueshkanov (int year, int month, int day); #endif // CALENDAR_H tea-qt-63.3.0/src/document.cpp000066400000000000000000002337361476733534200161430ustar00rootroot00000000000000/*************************************************************************** * 2007-2025 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************/ /* some code by Copyright (C) 2006-2008 Trolltech ASA. All rights reserved. */ /* code from qPEditor: Copyright (C) 2007 by Michael Protasov, mik-protasov@anyqsoft.com */ /* Diego Iastrubni //some GPL'ed code from new-editor-diego-3, found on qtcentre forum */ /* code from qwriter: * Copyright (C) 2009 by Gancov Kostya * * kossne@mail.ru * */ //#include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include "document.h" #include "utils.h" #include "gui_utils.h" #include "pugixml.hpp" #define SK_A 38 #define SK_D 40 #define SK_W 25 #define SK_S 39 #define SK_E 26 #define SK_C 54 QHash global_palette; QSettings *settings; QMenu *menu_current_files; int recent_list_max_items; bool b_recent_off; bool b_destroying_all; QStringList text_get_bookmarks (const QString &s, const QString &sstart, const QString &send) { QStringList result; int c = s.size(); int i = 0; while (i < c) { int start = s.indexOf (sstart, i, Qt::CaseInsensitive); if (start == -1) break; int end = s.indexOf (send, start + sstart.length()); if (end == -1) break; result.prepend (s.mid (start + sstart.length(), (end - start) - send.length())); i = end + 1; } return result; } QTextCharFormat tformat_from_style (const QString &fontstyle, const QString &color, int darker_val) { QTextCharFormat tformat; tformat.setForeground (QBrush (QColor (color).darker (darker_val))); if (fontstyle.isEmpty()) return tformat; if (fontstyle.contains ("bold")) tformat.setFontWeight (QFont::Bold); if (fontstyle.contains ("italic")) tformat.setFontItalic (true); if (fontstyle.contains ("underline")) tformat.setFontUnderline (true); if (fontstyle.contains ("strikeout")) tformat.setFontStrikeOut (true); return tformat; } CSyntaxHighlighter::CSyntaxHighlighter (QTextDocument *parent, CDocument *doc, const QString &fname): QSyntaxHighlighter (parent) { document = doc; casecare = true; } #if QT_VERSION < 0x050000 CSyntaxHighlighterQRegExp::CSyntaxHighlighterQRegExp (QTextDocument *parent, CDocument *doc, const QString &fname): CSyntaxHighlighter (parent, doc, fname) { document = doc; load_from_xml_pugi (fname); } /* void CSyntaxHighlighterQRegExp::load_from_xml (const QString &fname) { if (! file_exists (fname)) return; cs = Qt::CaseSensitive; comment_start_expr = make_pair (QRegExp(), false); comment_end_expr = make_pair (QRegExp(), false); QString temp = qstring_load (fname); if (temp.isEmpty()) return; int darker_val = settings->value ("darker_val", 100).toInt(); QXmlStreamReader xml (temp); while (! xml.atEnd()) { xml.readNext(); QString tag_name = xml.name().toString().toLower(); if (xml.isStartElement()) { //if (tag_name == "document") // { // exts = xml.attributes().value ("exts").toString(); // langs = xml.attributes().value ("langs").toString(); // } if (tag_name == "item") { QString attr_type = xml.attributes().value ("type").toString(); QString attr_name = xml.attributes().value ("name").toString(); if (attr_name == "options") { QString s_casecare = xml.attributes().value ("casecare").toString(); if (! s_casecare.isEmpty()) if (s_casecare == "0" || s_casecare == "false") casecare = false; if (! casecare) cs = Qt::CaseInsensitive; } if (attr_type == "keywords") { QString color = hash_get_val (global_palette, xml.attributes().value ("color").toString(), "darkBlue"); QTextCharFormat fmt = tformat_from_style (xml.attributes().value ("fontstyle").toString(), color, darker_val); QString element = xml.readElementText().trimmed().remove('\n'); if (element.isEmpty()) continue; QRegExp rg (element, cs); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else hl_rules.push_back (make_pair (rg, fmt)); } //keywords else if (attr_type == "item") { QString color = hash_get_val (global_palette, xml.attributes().value ("color").toString(), "darkBlue"); QTextCharFormat fmt = tformat_from_style (xml.attributes().value ("fontstyle").toString(), color, darker_val); QString element = xml.readElementText().trimmed().remove('\n'); if (element.isEmpty()) continue; QRegExp rg (element, cs); if (rg.isValid()) hl_rules.push_back (make_pair (rg, fmt)); } else if (attr_type == "mcomment-start") { QString color = hash_get_val (global_palette, xml.attributes().value ("color").toString(), "gray"); QTextCharFormat fmt = tformat_from_style (xml.attributes().value ("fontstyle").toString(), color, darker_val); fmt_multi_line_comment = fmt; QString element = xml.readElementText().trimmed().remove('\n'); if (! element.isEmpty()) //commentStartExpression = QRegExp (element, cs, QRegExp::RegExp); { comment_start_expr.first = QRegExp (element, cs, QRegExp::RegExp); comment_start_expr.second = true; } } else if (attr_type == "mcomment-end") { QString element = xml.readElementText().trimmed().remove('\n'); if (! element.isEmpty()) //commentEndExpression = QRegExp (element, cs, QRegExp::RegExp); { comment_end_expr.first = QRegExp (element, cs, QRegExp::RegExp); comment_end_expr.second = true; } } else if (attr_type == "comment") { QString element = xml.readElementText().trimmed().remove('\n'); if (element.isEmpty()) continue; if (xml.attributes().value ("name").toString() == "cm_mult") comment_mult = element; else if (xml.attributes().value ("name").toString() == "cm_single") comment_single = element; } }//item }//is start if (xml.hasError()) qDebug() << "xml parse error"; } //cycle } */ void CSyntaxHighlighterQRegExp::highlightBlock (const QString &text) { if (hl_rules.size() == 0) return; /* for (auto &p: hl_rules) { int index = text.indexOf (p.first); while (index >= 0) { int length = p.first.matchedLength(); if (length == 0) continue; setFormat (index, length, p.second); index = text.indexOf (p.first, index + length); } }*/ for (vector >::iterator p = hl_rules.begin(); p != hl_rules.end(); ++p) { int index = text.indexOf (p->first); while (index >= 0) { int length = p->first.matchedLength(); if (length == 0) continue; setFormat (index, length, p->second); index = text.indexOf (p->first, index + length); } } if (! comment_start_expr.second && ! comment_end_expr.second) return; setCurrentBlockState (0); int startIndex = 0; //if (commentStartExpression.isEmpty() || commentEndExpression.isEmpty()) // return; if (previousBlockState() != 1) startIndex = text.indexOf (comment_start_expr.first); while (startIndex >= 0) { int endIndex = comment_end_expr.first.indexIn (text, startIndex); int commentLength; if (endIndex == -1) { setCurrentBlockState (1); commentLength = text.length() - startIndex; } else commentLength = endIndex - startIndex + comment_end_expr.first.matchedLength(); setFormat (startIndex, commentLength, fmt_multi_line_comment); startIndex = text.indexOf (comment_start_expr.first, startIndex + commentLength); } } class CXMLHL_walker: public pugi::xml_tree_walker { public: CSyntaxHighlighterQRegExp *hl; int darker_val; bool for_each (pugi::xml_node& node); }; bool CXMLHL_walker::for_each (pugi::xml_node &node) { if (node.type() != pugi::node_element) return true; QString tag_name = node.name(); if (tag_name == "item") { pugi::xml_attribute attr = node.attribute ("type"); QString attr_type = attr.as_string(); attr = node.attribute ("name"); QString attr_name = attr.as_string(); if (attr_name == "options") { attr = node.attribute ("casecare"); QString s_casecare = attr.as_string(); if (! s_casecare.isEmpty()) if (s_casecare == "0" || s_casecare == "false") hl->casecare = false; if (! hl->casecare) hl->cs = Qt::CaseInsensitive; } if (attr_type == "keywords") { attr = node.attribute ("color"); QString color = hash_get_val (global_palette, attr.as_string(), "darkBlue"); attr = node.attribute ("fontstyle"); QTextCharFormat fmt = tformat_from_style (attr.as_string(), color, darker_val); QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; QRegExp rg (element, hl->cs); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else hl->hl_rules.push_back (make_pair (rg, fmt)); } //keywords else if (attr_type == "item") { attr = node.attribute ("color"); QString color = hash_get_val (global_palette, attr.as_string(), "darkBlue"); attr = node.attribute ("fontstyle"); QTextCharFormat fmt = tformat_from_style (attr.as_string(), color, darker_val); QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; QRegExp rg (element, hl->cs); if (rg.isValid()) hl->hl_rules.push_back (make_pair (rg, fmt)); } else if (attr_type == "mcomment-start") { attr = node.attribute ("color"); QString color = hash_get_val (global_palette, attr.as_string(), "gray"); attr = node.attribute ("fontstyle"); QString fontstyle = attr.as_string(); QTextCharFormat fmt = tformat_from_style (fontstyle, color, darker_val); hl->fmt_multi_line_comment = fmt; QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; if (! element.isEmpty()) { hl->comment_start_expr.first = QRegExp (element, hl->cs, QRegExp::RegExp); hl->comment_start_expr.second = true; } } else if (attr_type == "mcomment-end") { QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; if (! element.isEmpty()) { hl->comment_end_expr.first = QRegExp (element, hl->cs, QRegExp::RegExp); hl->comment_end_expr.second = true; } } else if (attr_type == "comment") { attr = node.attribute ("name"); QString name = attr.as_string(); QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; if (name == "cm_mult") hl->comment_mult = element; else if (name == "cm_single") hl->comment_single = element; } }//item return true; } void CSyntaxHighlighterQRegExp::load_from_xml_pugi (const QString &fname) { if (! file_exists (fname)) return; cs = Qt::CaseSensitive; comment_start_expr = make_pair (QRegExp(), false); comment_end_expr = make_pair (QRegExp(), false); QString temp = qstring_load (fname); if (temp.isEmpty()) return; int darker_val = settings->value ("darker_val", 100).toInt(); pugi::xml_document doc; pugi::xml_parse_result result = doc.load_buffer (temp.utf16(), temp.size() * 2, pugi::parse_default, pugi::encoding_utf16); //pugi::xml_parse_result result = doc.load_buffer (temp.toUtf8().data(), // temp.toUtf8().size()); if (! result) return; CXMLHL_walker walker; walker.darker_val = darker_val; walker.hl = this; doc.traverse (walker); } #endif #if QT_VERSION >= 0x050000 CSyntaxHighlighterQRegularExpression::CSyntaxHighlighterQRegularExpression (QTextDocument *parent, CDocument *doc, const QString &fname): CSyntaxHighlighter (parent, doc, fname) { document = doc; load_from_xml_pugi (fname); } /* void CSyntaxHighlighterQRegularExpression::load_from_xml (const QString &fname) { if (! file_exists (fname)) return; casecare = true; comment_start_expr = make_pair (QRegularExpression(), false); comment_end_expr = make_pair (QRegularExpression(), false); QString temp = qstring_load (fname); if (temp.isEmpty()) return; int darker_val = settings->value ("darker_val", 100).toInt(); QXmlStreamReader xml (temp); while (! xml.atEnd()) { xml.readNext(); QString tag_name = xml.name().toString().toLower(); if (xml.isStartElement()) { if (tag_name == "item") { QString attr_type = xml.attributes().value ("type").toString(); QString attr_name = xml.attributes().value ("name").toString(); if (attr_name == "options") { QString s_casecare = xml.attributes().value ("casecare").toString(); if (! s_casecare.isEmpty()) if (s_casecare == "0" || s_casecare == "false") casecare = false; if (! casecare) pattern_opts = pattern_opts | QRegularExpression::CaseInsensitiveOption; } if (attr_type == "keywords") { QString color = hash_get_val (global_palette, xml.attributes().value ("color").toString(), "darkBlue"); QTextCharFormat fmt = tformat_from_style (xml.attributes().value ("fontstyle").toString(), color, darker_val); QString element = xml.readElementText().trimmed().remove('\n'); if (element.isEmpty()) continue; QRegularExpression rg = QRegularExpression (element, pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else hl_rules.push_back (make_pair (rg, fmt)); } //keywords else if (attr_type == "item") { QString color = hash_get_val (global_palette, xml.attributes().value ("color").toString(), "darkBlue"); QTextCharFormat fmt = tformat_from_style (xml.attributes().value ("fontstyle").toString(), color, darker_val); QString element = xml.readElementText().trimmed().remove('\n'); if (element.isEmpty()) continue; QRegularExpression rg = QRegularExpression (element, pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else hl_rules.push_back (make_pair (rg, fmt)); } else if (attr_type == "mcomment-start") { QString color = hash_get_val (global_palette, xml.attributes().value ("color").toString(), "gray"); QTextCharFormat fmt = tformat_from_style (xml.attributes().value ("fontstyle").toString(), color, darker_val); fmt_multi_line_comment = fmt; QString element = xml.readElementText().trimmed().remove('\n'); if (element.isEmpty()) continue; QRegularExpression rg = QRegularExpression (element, pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else { comment_start_expr.first = rg; comment_start_expr.second = true; } } else if (attr_type == "mcomment-end") { QString element = xml.readElementText().trimmed().remove('\n'); if (element.isEmpty()) continue; QRegularExpression rg = QRegularExpression (element, pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else { comment_end_expr.first = rg; comment_end_expr.second = true; } } else if (attr_type == "comment") { if (xml.attributes().value ("name").toString() == "cm_mult") comment_mult = xml.readElementText().trimmed(); else if (xml.attributes().value ("name").toString() == "cm_single") comment_single = xml.readElementText().trimmed(); } }//item }//end of "is start" if (xml.hasError()) qDebug() << "xml parse error"; } //cycle } */ class CXMLHL_walker: public pugi::xml_tree_walker { public: CSyntaxHighlighterQRegularExpression *hl; int darker_val; bool for_each (pugi::xml_node& node); }; bool CXMLHL_walker::for_each (pugi::xml_node &node) { if (node.type() != pugi::node_element) return true; // std::cout << "for_each name = " << node.name() << std::endl; QString tag_name = node.name(); if (tag_name == "item") { pugi::xml_attribute attr = node.attribute ("type"); QString attr_type = attr.as_string(); attr = node.attribute ("name"); QString attr_name = attr.as_string(); if (attr_name == "options") { // attr = node.attribute ("casecare"); QString s_casecare = node.attribute ("casecare").as_string(); if (! s_casecare.isEmpty()) if (s_casecare == "0" || s_casecare == "false") hl->casecare = false; if (! hl->casecare) hl->pattern_opts = hl->pattern_opts | QRegularExpression::CaseInsensitiveOption; } if (attr_type == "keywords") { //attr = node.attribute ("color"); QString color = hash_get_val (global_palette, node.attribute ("color").as_string(), "darkBlue"); //attr = node.attribute ("fontstyle"); QTextCharFormat fmt = tformat_from_style (node.attribute ("fontstyle").as_string(), color, darker_val); QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; QRegularExpression rg = QRegularExpression (element, hl->pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else hl->hl_rules.push_back (make_pair (rg, fmt)); } //keywords else if (attr_type == "item") { // attr = node.attribute ("color"); QString color = hash_get_val (global_palette, node.attribute ("color").as_string(), "darkBlue"); // attr = node.attribute ("fontstyle"); QTextCharFormat fmt = tformat_from_style (node.attribute ("fontstyle").as_string(), color, darker_val); QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; QRegularExpression rg = QRegularExpression (element, hl->pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else hl->hl_rules.push_back (make_pair (rg, fmt)); } else if (attr_type == "mcomment-start") { // attr = node.attribute ("color"); QString color = hash_get_val (global_palette, node.attribute ("color").as_string(), "gray"); //attr = node.attribute ("fontstyle"); QString fontstyle = node.attribute ("fontstyle").as_string(); QTextCharFormat fmt = tformat_from_style (fontstyle, color, darker_val); hl->fmt_multi_line_comment = fmt; QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; QRegularExpression rg = QRegularExpression (element, hl->pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else { hl->comment_start_expr.first = rg; hl->comment_start_expr.second = true; } } else if (attr_type == "mcomment-end") { QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; QRegularExpression rg = QRegularExpression (element, hl->pattern_opts); if (! rg.isValid()) qDebug() << "! valid " << rg.pattern(); else { hl->comment_end_expr.first = rg; hl->comment_end_expr.second = true; } } else if (attr_type == "comment") { //attr = node.attribute ("name"); QString name = node.attribute ("name").as_string(); QString t = node.text().as_string(); QString element = t.trimmed().remove('\n'); if (element.isEmpty()) return true; if (name == "cm_mult") hl->comment_mult = element; else if (name == "cm_single") hl->comment_single = element; } }//item return true; } void CSyntaxHighlighterQRegularExpression::load_from_xml_pugi (const QString &fname) { if (! file_exists (fname)) return; casecare = true; comment_start_expr = make_pair (QRegularExpression(), false); comment_end_expr = make_pair (QRegularExpression(), false); QString temp = qstring_load (fname); if (temp.isEmpty()) return; int darker_val = settings->value ("darker_val", 100).toInt(); pugi::xml_document doc; pugi::xml_parse_result result = doc.load_buffer (temp.utf16(), temp.size() * 2, pugi::parse_default, pugi::encoding_utf16); if (! result) return; CXMLHL_walker walker; walker.darker_val = darker_val; walker.hl = this; doc.traverse (walker); } void CSyntaxHighlighterQRegularExpression::highlightBlock (const QString &text) { if (hl_rules.size() == 0) return; for (vector >::iterator p = hl_rules.begin(); p != hl_rules.end(); ++p) { QRegularExpressionMatch m = p->first.match (text); int index = m.capturedStart(); while (index >= 0) { int length = m.capturedLength(); if (length == 0) continue; setFormat (index, length, p->second); m = p->first.match (text, index + length); index = m.capturedStart(); } } /* for (const auto& p: hl_rules) { QRegularExpressionMatch m = p.first.match (text); int index = m.capturedStart(); while (index >= 0) { int length = m.capturedLength(); if (length == 0) continue; setFormat (index, length, p.second); m = p.first.match (text, index + length); index = m.capturedStart(); } } */ if (! comment_start_expr.second && ! comment_end_expr.second) return; setCurrentBlockState (0); int startIndex = 0; QRegularExpressionMatch m_start = comment_start_expr.first.match (text); if (previousBlockState() != 1) startIndex = m_start.capturedStart(); while (startIndex >= 0) { QRegularExpressionMatch m_end = comment_end_expr.first.match (text, startIndex); int endIndex = m_end.capturedStart(); int commentLength; if (endIndex == -1) { setCurrentBlockState (1); commentLength = text.length() - startIndex; } else commentLength = endIndex - startIndex + m_end.capturedLength(); setFormat (startIndex, commentLength, fmt_multi_line_comment); m_start = comment_start_expr.first.match (text, startIndex + commentLength); startIndex = m_start.capturedStart(); } } #endif QMimeData* CDocument::createMimeDataFromSelection() const { if (has_rect_selection()) { QMimeData *md = new QMimeData; md->setText (rect_sel_get()); return md; } return QPlainTextEdit::createMimeDataFromSelection(); } bool CDocument::canInsertFromMimeData (const QMimeData *source) const { // if (source->hasFormat ("text/uri-list")) // return true; //else return true; } void CDocument::insertFromMimeData (const QMimeData *source) { QString fname; QFileInfo info; bool b_ins_plain_text = ! source->hasUrls(); if (source->hasUrls() && source->urls().at(0).scheme() != "file") b_ins_plain_text = true; if (b_ins_plain_text) { QPlainTextEdit::insertFromMimeData (source); return; } QList l = source->urls(); for (QList ::iterator u = l.begin(); u != l.end(); ++u) { fname = u->toLocalFile(); info.setFile(fname); if (info.isFile()) holder->open_file (fname, "UTF-8"); } } void CDocument::paintEvent (QPaintEvent *event) { if (draw_margin || highlight_current_line) { QPainter painter (viewport()); if (highlight_current_line) { QRect r = cursorRect(); r.setX (0); r.setWidth (viewport()->width()); painter.fillRect (r, QBrush (current_line_color)); } if (draw_margin) { QPen pen = painter.pen(); pen.setColor (margin_color); painter.setPen (pen); painter.drawLine (margin_x, 0, margin_x, viewport()->height()); } } QPlainTextEdit::paintEvent (event); } void CDocument::keyPressEvent (QKeyEvent *event) { // qDebug() << event->nativeScanCode() ; // qDebug() << int_to_binary (event->nativeModifiers()); // std::bitset<32> btst (event->nativeModifiers()); /* for (std::size_t i = 0; i < btst.size(); ++i) { qDebug() << "btst[" << i << "]: " << btst[i]; } */ //LSHIFT = 0 //LCTRL = 2 //LALT = 3 //LWIN = 6 /* if (settings->value ("wasd", "0").toBool()) { bitset <32> btst (event->nativeModifiers()); QTextCursor cr = textCursor(); QTextCursor::MoveMode m = QTextCursor::MoveAnchor; if (btst[3] == 1 || btst[6] == 1) //LALT or LWIN { int visible_lines; if (btst[6] == 1) m = QTextCursor::KeepAnchor; switch (event->nativeScanCode()) { case SK_W: cr.movePosition (QTextCursor::Up, m); setTextCursor (cr); return; case SK_S: cr.movePosition (QTextCursor::Down, m); setTextCursor (cr); return; case SK_A: cr.movePosition (QTextCursor::Left, m); setTextCursor (cr); return; case SK_D: cr.movePosition (QTextCursor::Right, m); setTextCursor (cr); return; case SK_E: visible_lines = height() / fontMetrics().height(); cr.movePosition (QTextCursor::Down, m, visible_lines); setTextCursor (cr); return; case SK_C: visible_lines = height() / fontMetrics().height(); cr.movePosition (QTextCursor::Up, m, visible_lines); setTextCursor (cr); return; } } } */ if (auto_indent && event->key() == Qt::Key_Return) { calc_auto_indent(); QPlainTextEdit::keyPressEvent (event); textCursor().insertText (indent_val); return; } if (event->key() == Qt::Key_Tab) { indent(); event->accept(); return; } if (event->key() == Qt::Key_Backtab) { un_indent(); event->accept(); return; } if (event->key() == Qt::Key_Insert) { setOverwriteMode (! overwriteMode()); event->accept(); return; } /* #if QT_VERSION >= 0x050000 if (event->key() == Qt::Key_C && (event->modifiers().testFlag(Qt::ControlModifier))) { QString t = get(); if (t.isEmpty()) { event->accept(); return; } #if defined(Q_OS_WIN) || defined(Q_OS_OS2) t = t.replace (QChar::ParagraphSeparator, "\r\n"); #elif defined(Q_OS_UNIX) t = t.replace (QChar::ParagraphSeparator, "\n"); #endif QClipboard *clipboard = QApplication::clipboard(); clipboard->setText (t); event->accept(); return; } #endif */ if (event->key() == Qt::Key_C && (event->modifiers().testFlag(Qt::ControlModifier))) { ed_copy(); event->accept(); return; } if (event->key() == Qt::Key_X && (event->modifiers().testFlag(Qt::ControlModifier))) { ed_cut(); event->accept(); return; } if (event->key() == Qt::Key_V && (event->modifiers().testFlag(Qt::ControlModifier))) { ed_paste(); event->accept(); return; } QPlainTextEdit::keyPressEvent (event); } void CDocument::resizeEvent (QResizeEvent *e) { QPlainTextEdit::resizeEvent (e); QRect cr = contentsRect(); line_num_area->setGeometry (QRect (cr.left(), cr.top(), line_number_area_width(), cr.height())); } void CDocument::wheelEvent (QWheelEvent *e) { if (e->modifiers() & Qt::ControlModifier) { #if QT_VERSION < 0x050000 const int delta = e->delta(); #else const int delta = e->angleDelta().y(); #endif QFont f = font(); int sz = f.pointSize(); if (delta > 0) sz++; if (delta < 0) sz--; f.setPointSize(sz); setFont(f); return; } QPlainTextEdit::wheelEvent(e); } void CDocument::contextMenuEvent (QContextMenuEvent *event) { QMenu *menu = new QMenu (this); QAction *a = menu->addAction (tr("Copy")); connect (a, SIGNAL(triggered()), this, SLOT(ed_copy())); a = menu->addAction (tr("Cut")); connect (a, SIGNAL(triggered()), this, SLOT(ed_cut())); a = menu->addAction (tr("Paste")); connect (a, SIGNAL(triggered()), this, SLOT(ed_paste())); menu->addSeparator(); a = menu->addAction (tr("Undo")); connect (a, SIGNAL(triggered()), this, SLOT(undo())); a = menu->addAction (tr("Redo")); connect (a, SIGNAL(triggered()), this, SLOT(redo())); menu->exec(event->globalPos()); delete menu; } CDocument::CDocument (CDox *hldr, QWidget *parent): QPlainTextEdit (parent) { holder = hldr; highlighter = 0; tab_page = 0; markup_mode = "HTML"; file_name = tr ("new[%1]").arg (QTime::currentTime().toString ("hh-mm-ss")); cursor_xy_visible = true; charset = "UTF-8"; text_to_search = ""; position = 0; margin_pos = 72; margin_x = brace_width * margin_pos; draw_margin = false; hl_brackets = false; auto_indent = false; tab_sp_width = 8; spaces_instead_of_tabs = true; highlight_current_line = false; show_tabs_and_spaces = false; #if defined(Q_OS_WIN) || defined(Q_OS_OS2) eol = "\r\n"; #elif defined(Q_OS_UNIX) eol = "\n"; #endif rect_sel_reset(); setup_brace_width(); line_num_area = new CLineNumberArea (this); connect (this, SIGNAL(selectionChanged()), this, SLOT(slot_selectionChanged())); connect (this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth())); connect (this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); connect (this, SIGNAL(cursorPositionChanged()), this, SLOT(cb_cursorPositionChanged())); updateLineNumberAreaWidth(); document()->setUseDesignMetrics (true); QString s_sel_back_color = hash_get_val (global_palette, "sel-background", "black"); QString s_sel_text_color = hash_get_val (global_palette, "sel-text", "white"); int darker_val = settings->value ("darker_val", 100).toInt(); sel_text_color = QColor (s_sel_text_color).darker(darker_val).name(); sel_back_color = QColor (s_sel_back_color).darker(darker_val).name(); current_line_color = QColor (hash_get_val (global_palette, "cur_line_color", "#EEF6FF")).darker (settings->value ("darker_val", 100).toInt()).name(); holder->items.push_back (this); int tab_index = holder->tab_widget->addTab (this, file_name); tab_page = holder->tab_widget->widget (tab_index); //QAction *actCopy; // QAction *actCut; //QAction *actPaste; //actCopy = new QAction (tr("Copy"), this); //connect (actCopy, SIGNAL(triggered()), this, SLOT(ed_copy())); // setContextMenuPolicy (Qt::PreventContextMenu); /* set dots color, put to hl init spaceFormat = QtGui.QTextCharFormat() spaceFormat.setForeground(QtCore.Qt.red) self.highlightingRules.append((QtCore.QRegExp(" "), spaceFormat)) */ setFocus (Qt::OtherFocusReason); } CDocument::~CDocument() { if (holder->autosave_files.contains (file_name)) { file_save_with_name (file_name, charset); document()->setModified (false); } if (file_name.endsWith (".notes") && document()->isModified()) file_save_with_name_plain (file_name); else if (file_name.startsWith (holder->dir_config) && document()->isModified()) file_save_with_name_plain (file_name); else if (document()->isModified() && file_exists (file_name)) if (QMessageBox::warning (0, "TEA", tr ("%1 has been modified.\n" "Do you want to save your changes?").arg (file_name), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) file_save_with_name (file_name, charset); if (! file_name.startsWith (holder->dir_config) && ! file_name.endsWith (".notes")) { holder->add_to_recent (this); holder->update_recent_menu(); } if (file_name.startsWith (holder->todo.dir_days)) holder->todo.load_dayfile(); // QMainWindow *w = qobject_cast (holder->parent_wnd); QMainWindow *w = holder->parent_wnd; w->setWindowTitle (""); int i = holder->tab_widget->indexOf (tab_page); if (i != -1) holder->tab_widget->removeTab (i); } QString CDocument::get() const { return textCursor().selectedText(); } void CDocument::put (const QString &value) { textCursor().insertText (value); } void CDocument::ed_copy() { if (! has_selection()) return; QString t = get(); #if defined(Q_OS_UNIX) t = t.replace (QChar::ParagraphSeparator, "\n"); #endif #if defined(Q_OS_WIN) || defined(Q_OS_OS2) t = t.replace (QChar::ParagraphSeparator, "\r\n"); #endif QClipboard *clipboard = QApplication::clipboard(); clipboard->setText (t); } void CDocument::ed_cut() { if (! has_selection()) return; QString t = get(); #if defined(Q_OS_UNIX) t = t.replace (QChar::ParagraphSeparator, "\n"); #endif #if defined(Q_OS_WIN) || defined(Q_OS_OS2) t = t.replace (QChar::ParagraphSeparator, "\r\n"); #endif QClipboard *clipboard = QApplication::clipboard(); clipboard->setText (t); textCursor().insertText (""); } void CDocument::ed_paste() { QClipboard *clipboard = QApplication::clipboard(); textCursor().insertText (clipboard->text()); } bool CDocument::has_selection() { return textCursor().hasSelection(); } /* QMenu* CDocument::createStandardContextMenu() { QMenu *m = new QMenu(); m->addAction (actCopy); return m; } */ bool CDocument::file_open (const QString &fileName, const QString &codec) { qDebug() << "CDocument::file_open " << fileName; CTio *tio = holder->tio_handler.get_for_fname (fileName); // qDebug() << "tio->metaObject()->className()" << tio->metaObject()->className(); if (! tio) return false; if (fileName.contains (holder->dir_config)) tio->charset = "UTF-8"; else tio->charset = codec; if (! tio->load (fileName)) { holder->log->log (tr ("cannot open %1 because of: %2") .arg (fileName) .arg (tio->error_string)); return false; } setPlainText (tio->data); charset = tio->charset; eol = tio->eol; file_name = fileName; holder->update_project (file_name); set_tab_caption (QFileInfo (file_name).fileName()); set_hl(); set_markup_mode(); document()->setModified (false); holder->log->log (tr ("%1 is open").arg (file_name)); QMutableListIterator i (holder->recent_files); while (i.hasNext()) { QStringList lt = i.next().split ("*"); if (lt.size() > 0) if (lt.at(0) == file_name) i.remove(); } return true; } bool CDocument::file_save_with_name (const QString &fileName, const QString &codec) { CTio *tio = holder->tio_handler.get_for_fname (fileName); if (! tio) return false; if (fileName.contains (holder->dir_config)) tio->charset = "UTF-8"; else tio->charset = codec; tio->data = toPlainText(); if (eol != "\n") tio->data.replace ("\n", eol); if (! tio->save (fileName)) { holder->log->log (tr ("cannot save %1 because of: %2") .arg (fileName) .arg (tio->error_string)); return false; } if (! b_destroying_all) { charset = tio->charset; file_name = fileName; set_tab_caption (QFileInfo (file_name).fileName()); holder->log->log (tr ("%1 is saved").arg (file_name)); update_title (settings->value ("full_path_at_window_title", 1).toBool()); update_status(); document()->setModified (false); holder->update_current_files_menu(); holder->update_project (file_name); } return true; } bool CDocument::file_save_with_name_plain (const QString &fileName) { QFile file (fileName); if (! file.open (QFile::WriteOnly)) return false; //file_save_with_name (fileName, charset); //QTextCodec *codec = QTextCodec::codecForName (charset.toUtf8().data()); //if (! codec) // return false; //QByteArray ba = codec->fromUnicode (toPlainText()); //file.write (ba); //file.close(); CTio *tio = holder->tio_handler.get_for_fname (fileName); if (! tio) return false; if (fileName.contains (holder->dir_config)) tio->charset = "UTF-8"; else tio->charset = charset; tio->data = toPlainText(); if (eol != "\n") tio->data.replace ("\n", eol); if (! tio->save (fileName)) { holder->log->log (tr ("cannot save %1 because of: %2") .arg (fileName) .arg (tio->error_string)); return false; } holder->update_current_files_menu(); return true; } int CDocument::get_tab_idx() { return holder->tab_widget->indexOf (tab_page); } QString CDocument::get_triplex() { if (! file_exists (file_name)) return QString (""); QString s (file_name); s += "*"; s += charset; s += "*"; s += QString::number (textCursor().position()); s += "*"; if (! get_word_wrap()) s+="0"; else s+="1"; return s; } QString CDocument::get_filename_at_cursor() { if (textCursor().hasSelection()) { QFileInfo nf (file_name); QDir cd (nf.absolutePath()); return cd.cleanPath (cd.absoluteFilePath (textCursor().selectedText())); } QString s = textCursor().block().text(); if (s.isEmpty()) return QString(); QString x; QString sep_start; QString sep_end; if (markup_mode == "LaTeX") { sep_start = "{"; sep_end = "}"; } else if (markup_mode == "Markdown") { sep_start = "("; sep_end = ")"; } if (markup_mode == "LaTeX" || markup_mode == "Markdown") { int pos = textCursor().positionInBlock(); int end = s.indexOf (sep_end, pos); if (end == -1) return x; int start = s.lastIndexOf (sep_start, pos); if (start == -1) return x; x = s.mid (start + 1, end - (start + 1)); QFileInfo inf (file_name); QDir cur_dir (inf.absolutePath()); QString result = cur_dir.cleanPath (cur_dir.absoluteFilePath(x)); if (file_exists (result)) return result; int i = x.lastIndexOf ("/"); if (i < 0) i = x.lastIndexOf ("\\"); if (i < 0) return QString(); x = x.mid (i + 1); result = cur_dir.cleanPath (cur_dir.absoluteFilePath(x)); return result; } else { int pos = textCursor().positionInBlock(); int end = s.indexOf ("\"", pos); if (end == -1) return x; int start = s.lastIndexOf ("\"", pos); if (start == -1) return x; x = s.mid (start + 1, end - (start + 1)); if (x.startsWith("#")) return x; QFileInfo inf (file_name); QDir cur_dir (inf.absolutePath()); return cur_dir.cleanPath (cur_dir.absoluteFilePath(x)); } } QStringList CDocument::get_words() { QStringList result; QTextCursor cr = textCursor(); QString text = toPlainText(); cr.setPosition (0); cr.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor); do { QChar c = text[cr.position()]; if (char_is_bad (c)) while (char_is_bad (c)) { cr.movePosition (QTextCursor::NextCharacter); c = text[cr.position()]; } cr.movePosition (QTextCursor::EndOfWord, QTextCursor::KeepAnchor); c = text[cr.position()]; QString stext = cr.selectedText(); if (! stext.isEmpty() && stext.endsWith ("\"")) { cr.movePosition (QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); stext = cr.selectedText(); } if (! stext.isEmpty()) result.append (stext); } while (cr.movePosition (QTextCursor::NextWord)); return result; } void CDocument::goto_pos (int pos) { QTextCursor cr = textCursor(); cr.setPosition (pos); setTextCursor (cr); } void CDocument::set_tab_caption (const QString &fileName) { holder->tab_widget->setTabText (get_tab_idx(), fileName); } void CDocument::set_hl (bool mode_auto, const QString &theext) { if (highlighter) delete highlighter; highlighter = 0; if (! settings->value ("hl_enabled", 1).toBool()) return; QString ext; if (mode_auto) ext = file_get_ext (file_name); else ext = theext; if (ext.isEmpty()) return; QString fname; #if QT_VERSION >= 0x050000 for (vector >::iterator p = holder->hl_files.begin(); p != holder->hl_files.end(); ++p) { if (p->first.isValid()) if (p->first.match(file_name).hasMatch()) { fname = p->second; break; } } #else for (vector >::iterator p = holder->hl_files.begin(); p != holder->hl_files.end(); ++p) { if (p->first.isValid()) if (p->first.exactMatch(file_name)) { fname = p->second; break; } } #endif if (fname.isEmpty() || ! file_exists (fname)) return; #if QT_VERSION >= 0x050000 highlighter = new CSyntaxHighlighterQRegularExpression (document(), this, fname); #else highlighter = new CSyntaxHighlighterQRegExp (document(), this, fname); #endif } void CDocument::set_markup_mode() { markup_mode = holder->markup_mode; QString e = file_get_ext (file_name); QString t = holder->markup_modes[e]; if (! t.isEmpty()) markup_mode = t; } void CDocument::insert_image (const QString &full_path) { put (get_insert_image (file_name, full_path, markup_mode)); } void CDocument::reload (const QString &enc) { if (file_exists (file_name)) file_open (file_name, enc); } void CDocument::update_status() { holder->l_charset->setText (charset); if (! cursor_xy_visible) return; int x = textCursor().position() - textCursor().block().position() + 1; int y = textCursor().block().blockNumber() + 1; holder->l_status_bar->setText (QString ("%1%2[%3]").arg ( QString::number (y), -10).arg ( QString::number (x), -10).arg ( QString::number (blockCount(), 10))); holder->l_charset->setText (charset); } void CDocument::update_title (bool fullname) { if (! holder->parent_wnd) return; // QMainWindow *w = qobject_cast (holder->parent_wnd); QMainWindow *w = holder->parent_wnd; if (fullname) w->setWindowTitle (file_name); else w->setWindowTitle (QFileInfo (file_name).fileName()); } void CDocument::update_labels() { labels.clear(); labels = text_get_bookmarks (toPlainText(), settings->value ("label_start", "[?").toString(), settings->value ("label_end", "?]").toString()); } void CDocument::set_show_linenums (bool enable) { draw_linenums = enable; updateLineNumberAreaWidth(); update(); } void CDocument::set_show_margin (bool enable) { draw_margin = enable; update(); } void CDocument::set_margin_pos (int mp) { margin_pos = mp; margin_x = brace_width * margin_pos; update(); } void CDocument::set_hl_cur_line (bool enable) { highlight_current_line = enable; update(); } void CDocument::set_hl_brackets (bool enable) { hl_brackets = enable; update(); } void CDocument::set_word_wrap (bool wrap) { if (wrap) setWordWrapMode (QTextOption::WrapAtWordBoundaryOrAnywhere); else setWordWrapMode (QTextOption::NoWrap); } bool CDocument::get_word_wrap() { return wordWrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere; } void CDocument::indent() { if (! textCursor().hasSelection()) { QString fl; fl = fl.fill (' ', tab_sp_width); if (spaces_instead_of_tabs) textCursor().insertText (fl); else textCursor().insertText ("\t"); return; } QStringList l = textCursor().selectedText().split (QChar::ParagraphSeparator); if (l.size() == 0) return; QString fl; fl = fl.fill (' ', tab_sp_width); for (QList ::iterator i = l.begin(); i != l.end(); ++i) { if (spaces_instead_of_tabs) i->prepend (fl); else i->prepend ("\t"); } textCursor().insertText (l.join ("\n")); QTextCursor cur = textCursor(); cur.movePosition (QTextCursor::Up, QTextCursor::KeepAnchor, l.size() - 1); cur.movePosition (QTextCursor::StartOfLine, QTextCursor::KeepAnchor); setTextCursor (cur); } void CDocument::un_indent() { QStringList l = textCursor().selectedText().split (QChar::ParagraphSeparator); if (l.size() == 0) return; for (QList ::iterator t = l.begin(); t != l.end(); ++t) { if (! t->isEmpty()) if (t->at(0) == '\t' || t->at(0) == ' ') (*t) = t->mid (1);//eat first } textCursor().insertText (l.join ("\n")); QTextCursor cur = textCursor(); cur.movePosition (QTextCursor::Up, QTextCursor::KeepAnchor, l.size() - 1); cur.movePosition (QTextCursor::StartOfLine, QTextCursor::KeepAnchor); setTextCursor (cur); } //если строка пустая - пофиг, а надо бы смотреть тогда строку выше void CDocument::calc_auto_indent() { QTextCursor cur = textCursor(); cur.movePosition (QTextCursor::StartOfLine, QTextCursor::KeepAnchor); int aindent = 0; QString s = cur.selectedText(); int len = s.size(); QChar t = ' '; //what to detect - space or tab? if (s.indexOf ('\t') != -1) t = '\t'; if (t != '\t') { for (int i = 0; i < len; i++) if (! s.at(i).isSpace()) { aindent = i; break; } } else { for (int i = 0; i < len; i++) if (s.at(i) != '\t') { aindent = i; break; } } if (aindent != 0) indent_val = indent_val.fill (t, aindent); else indent_val.clear(); } void CDocument::setup_brace_width() { QFontMetrics *fm = new QFontMetrics (font()); brace_width = fm->averageCharWidth(); } void CDocument::brace_highlight() { brace_selection.format.setBackground (brackets_color); QTextDocument *doc = document(); QTextCursor cursor = textCursor(); QTextCursor beforeCursor = cursor; cursor.movePosition (QTextCursor::NextCharacter, QTextCursor::KeepAnchor); QString brace = cursor.selectedText(); beforeCursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); QString beforeBrace = beforeCursor.selectedText(); if ((brace != "{") && (brace != "}") && (brace != "[") && (brace != "]") && (brace != "(") && (brace != ")") && (brace != "<") && (brace != ">")) { if ((beforeBrace == "{") || (beforeBrace == "}") || (beforeBrace == "[") || (beforeBrace == "]") || (beforeBrace == "(") || (beforeBrace == ")") || (beforeBrace == "<") || (beforeBrace == ">")) { cursor = beforeCursor; brace = cursor.selectedText(); } else return; } QString openBrace; QString closeBrace; if ((brace == "{") || (brace == "}")) { openBrace = "{"; closeBrace = "}"; } else if ((brace == "[") || (brace == "]")) { openBrace = "["; closeBrace = "]"; } else if ((brace == "(") || (brace == ")")) { openBrace = "("; closeBrace = ")"; } else if ((brace == "<") || (brace == ">")) { openBrace = "<"; closeBrace = ">"; } if (brace == openBrace) { QTextCursor cursor1 = doc->find (closeBrace, cursor); QTextCursor cursor2 = doc->find (openBrace, cursor); if (cursor2.isNull()) { brace_selection.cursor = cursor; extra_selections.append (brace_selection); brace_selection.cursor = cursor1; extra_selections.append (brace_selection); setExtraSelections (extra_selections); } else { while (cursor1.position() > cursor2.position()) { cursor1 = doc->find (closeBrace, cursor1); cursor2 = doc->find (openBrace, cursor2); if (cursor2.isNull()) break; } brace_selection.cursor = cursor; extra_selections.append (brace_selection); brace_selection.cursor = cursor1; extra_selections.append (brace_selection); setExtraSelections (extra_selections); } } else { if (brace == closeBrace) { QTextCursor cursor1 = doc->find (openBrace, cursor, QTextDocument::FindBackward); QTextCursor cursor2 = doc->find (closeBrace, cursor, QTextDocument::FindBackward); if (cursor2.isNull()) { brace_selection.cursor = cursor; extra_selections.append (brace_selection); brace_selection.cursor = cursor1; extra_selections.append (brace_selection); setExtraSelections (extra_selections); } else { while (cursor1.position() < cursor2.position()) { cursor1 = doc->find (openBrace, cursor1, QTextDocument::FindBackward); cursor2 = doc->find (closeBrace, cursor2, QTextDocument::FindBackward); if (cursor2.isNull()) break; } brace_selection.cursor = cursor; extra_selections.append (brace_selection); brace_selection.cursor = cursor1; extra_selections.append (brace_selection); setExtraSelections (extra_selections); } } } } void CDocument::update_ext_selections() { extra_selections.clear(); setExtraSelections (extra_selections); rect_sel_upd(); brace_highlight(); } void CDocument::rect_block_start() { int x = textCursor().position() - textCursor().block().position(); int y = textCursor().block().blockNumber(); rect_sel_start.setX (x); rect_sel_start.setY (y); update_ext_selections(); } void CDocument::rect_block_end() { int x = textCursor().position() - textCursor().block().position(); int y = textCursor().block().blockNumber(); rect_sel_end.setX (x); rect_sel_end.setY (y); update_ext_selections(); } bool CDocument::has_rect_selection() const { if (rect_sel_start.y() == -1 || rect_sel_end.y() == -1) return false; return true; } void CDocument::rect_sel_reset() { rect_sel_start.setX (-1); rect_sel_start.setY (-1); rect_sel_end.setX (-1); rect_sel_end.setY (-1); } void CDocument::rect_sel_replace (const QString &s, bool insert) { /* 1. Определить с какой строки начинаем вставку 2. Определить как вставляемый текст перекрывает старый. Если строк в старом меньше (или конец файла), добавляем в выходной буфер строки. 3. Составляем выходной буфер из строк старого и нового. 4. Заменяем старый текст на выходной буфер. */ int y1 = std::min (rect_sel_start.y(), rect_sel_end.y()); int y2 = std::max (rect_sel_start.y(), rect_sel_end.y()); int ydiff = y2 - y1; int x1 = std::min (rect_sel_start.x(), rect_sel_end.x()); int x2 = std::max (rect_sel_start.x(), rect_sel_end.x()); QStringList sl_source; for (int line = y1; line <= y2; line++) { QTextBlock b = document()->findBlockByNumber (line); sl_source.append (b.text()); } QStringList sl_insert = s.split ("\n"); QStringList sl_dest; for (int line = 0; line < sl_insert.size(); line++) { QString t; if (line >= sl_source.size()) { t = sl_insert [line]; sl_dest.append (t); continue; } t = sl_source[line].left (x1); t += sl_insert [line]; if (! insert) t += sl_source[line].mid (x2); else t += sl_source[line].mid (x1); sl_dest.append (t); } QString new_text = sl_dest.join ("\n"); //теперь выделить при помощи курсора всё от y1 до y2 и обычным способом заменить текст QTextCursor cursor = textCursor(); cursor.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor); cursor.movePosition (QTextCursor::NextBlock, QTextCursor::MoveAnchor, y1); cursor.movePosition (QTextCursor::NextBlock, QTextCursor::KeepAnchor, ydiff); cursor.movePosition (QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); cursor.removeSelectedText(); textCursor().insertText (new_text); } void CDocument::rect_sel_upd() { if (rect_sel_start.y() == -1 || rect_sel_end.y() == -1) return; QTextEdit::ExtraSelection rect_selection; int y1 = std::min (rect_sel_start.y(), rect_sel_end.y()); int y2 = std::max (rect_sel_start.y(), rect_sel_end.y()); int x1 = std::min (rect_sel_start.x(), rect_sel_end.x()); int x2 = std::max (rect_sel_start.x(), rect_sel_end.x()); int xdiff = x2 - x1; int correction = 0; QTextCursor cursor = textCursor(); cursor.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor); cursor.movePosition (QTextCursor::NextBlock, QTextCursor::MoveAnchor, y1); for (int y = y1; y <= y2; y++) { QTextBlock b = document()->findBlockByNumber (y); if (b.text().length() == 0) { correction++; continue; } int sel_len = xdiff; if ((b.text().length() - x1) < xdiff) sel_len = b.text().length() - x1; cursor.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, x1 + correction); cursor.movePosition (QTextCursor::Right, QTextCursor::KeepAnchor, sel_len); rect_selection.cursor = cursor; rect_selection.format.setBackground (sel_back_color); rect_selection.format.setForeground (sel_text_color); extra_selections.append (rect_selection); cursor.movePosition (QTextCursor::NextBlock, QTextCursor::MoveAnchor); if (b.text().length() != 0) correction = 0; } setExtraSelections (extra_selections); } QString CDocument::rect_sel_get() const { QString result; int y1 = std::min (rect_sel_start.y(), rect_sel_end.y()); int y2 = std::max (rect_sel_start.y(), rect_sel_end.y()); int x1 = std::min (rect_sel_start.x(), rect_sel_end.x()); int x2 = std::max (rect_sel_start.x(), rect_sel_end.x()); int xdiff = x2 - x1; //sel length for (int y = y1; y <= y2; y++) { QTextBlock b = document()->findBlockByNumber (y); QString t = b.text(); result += t.mid (x1, xdiff); if (y != y2) result += '\n'; } return result; } void CDocument::rect_sel_cut (bool just_del) { int y1 = std::min (rect_sel_start.y(), rect_sel_end.y()); int y2 = std::max (rect_sel_start.y(), rect_sel_end.y()); int ydiff = y2 - y1; int x1 = std::min (rect_sel_start.x(), rect_sel_end.x()); int x2 = std::max (rect_sel_start.x(), rect_sel_end.x()); QStringList sl_source; for (int line = y1; line <= y2; line++) { QTextBlock b = document()->findBlockByNumber (line); sl_source.append (b.text()); } QStringList sl_dest; QStringList sl_copy; for (int line = 0; line < sl_source.size(); line++) { QString t; t = sl_source[line].left (x1); t += sl_source[line].mid (x2); sl_dest.append (t); sl_copy.append (sl_source[line].mid (x1, x2 - x1)); } QString new_text = sl_dest.join ("\n"); //теперь выделить при помощи курсора всё от y1 до y2 и обычным способом заменить текст QTextCursor cursor = textCursor(); cursor.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor); cursor.movePosition (QTextCursor::NextBlock, QTextCursor::MoveAnchor, y1); cursor.movePosition (QTextCursor::NextBlock, QTextCursor::KeepAnchor, ydiff); cursor.movePosition (QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); cursor.beginEditBlock(); cursor.removeSelectedText(); cursor.insertText (new_text); cursor.endEditBlock(); if (! just_del) QApplication::clipboard()->setText (sl_copy.join ("\n")); } void CDocument::lineNumberAreaPaintEvent (QPaintEvent *event) { if (! draw_linenums) return; QPainter painter (line_num_area); painter.fillRect (event->rect(), linenums_bg); painter.setPen (text_color); QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); int top = (int) blockBoundingGeometry (block).translated (contentOffset()).top(); int bottom = top + (int) blockBoundingRect (block).height(); int w = line_num_area->width(); int h = fontMetrics().height(); while (block.isValid() && top <= event->rect().bottom()) { if (block.isVisible() && bottom >= event->rect().top()) { QString number = QString::number (blockNumber + 1) + " "; painter.drawText (0, top, w, h, Qt::AlignRight, number); } block = block.next(); top = bottom; bottom = top + (int) blockBoundingRect(block).height(); ++blockNumber; } } int CDocument::line_number_area_width() { if (! draw_linenums) return 0; int digits = 1; int max = qMax (1, blockCount()); while (max >= 10) { max /= 10; ++digits; } return (brace_width * 2) + (brace_width * digits); } void CDocument::updateLineNumberAreaWidth() { setViewportMargins (line_number_area_width(), 0, 0, 0); } void CDocument::cb_cursorPositionChanged() { viewport()->update(); update_status(); if (hl_brackets) update_ext_selections(); } void CDocument::updateLineNumberArea (const QRect &rect, int dy) { if (dy) line_num_area->scroll (0, dy); else line_num_area->update (0, rect.y(), line_num_area->width(), rect.height()); if (rect.contains (viewport()->rect())) updateLineNumberAreaWidth(); } void CDocument::slot_selectionChanged() { QTextCursor cursor = this->textCursor(); if (cursor.selectionStart() != cursor.selectionEnd()) { rect_sel_start.setX (-1); rect_sel_end.setX (-1); update_ext_selections(); } } CDox::CDox() { markup_modes.insert ("htm", "HTML"); markup_modes.insert ("html", "HTML"); markup_modes.insert ("xhtml", "XHTML"); markup_modes.insert ("xml", "Docbook"); markup_modes.insert ("tex", "LaTeX"); markup_modes.insert ("lout", "Lout"); markup_modes.insert ("dokuwiki", "DokuWiki"); markup_modes.insert ("mediawiki", "MediaWiki"); markup_modes.insert ("md", "Markdown"); markup_modes.insert ("markdown", "Markdown"); menu_recent = 0; main_tab_widget = 0; tab_widget = 0; parent_wnd = 0; log = 0; l_status_bar = 0; l_charset = 0; //timer_autosave = new QTimer (this); timer_autosave.setInterval (settings->value ("timer_autosave_period", "1000").toInt() * 1000); connect(&timer_autosave, SIGNAL(timeout()), this, SLOT(autosave())); //timer_joystick = new QTimer (this); timer_joystick.setInterval (100); #if defined(JOYSTICK_SUPPORTED) joystick = new CJoystick (0, this); if (joystick->initialized) { connect(&timer_joystick, SIGNAL(timeout()), joystick, SLOT(read_joystick())); if (settings->value ("use_joystick", "0").toBool()) timer_joystick.start(); } #endif } CDox::~CDox() { b_destroying_all = true; if (items.size() > 0) for (vector ::size_type i = 0; i < items.size(); i++) delete items[i]; qstring_save (recent_list_fname, recent_files.join ("\n")); #if defined(JOYSTICK_SUPPORTED) delete joystick; #endif } void CDox::update_project (const QString &fileName) { if (file_get_ext (fileName) == "teaproject") { fname_current_project = fileName; hash_project.clear(); hash_project = hash_load_keyval (fileName); } } void CDox::reload_recent_list (void) { if (! file_exists (recent_list_fname)) return; recent_files = qstring_load (recent_list_fname).split ("\n"); } void CDox::add_to_recent (CDocument *d) { if (b_recent_off) return; if (! file_exists (d->file_name)) return; QString s (d->file_name); s += "*"; s += d->charset; s += "*"; s += QString::number (d->textCursor().position()); s += "*"; if (! d->get_word_wrap()) s+= "0"; else s+= "1"; recent_files.prepend (s); if (recent_files.size() > recent_list_max_items) recent_files.removeLast(); } void CDox::update_recent_menu() { menu_recent->clear(); create_menu_from_list (this, menu_recent, recent_files, SLOT(open_recent())); } void CDox::update_current_files_menu() { QStringList current_files; if (items.size() > 0) for (vector ::size_type i = 0; i < items.size(); i++) current_files.prepend (items[i]->file_name); menu_current_files->clear(); create_menu_from_list (this, menu_current_files, current_files, SLOT(open_current())); } void CDox::move_cursor (QTextCursor::MoveOperation mo) { CDocument *d = get_current(); if (! d) return; QTextCursor cr = d->textCursor(); if (cr.isNull()) return; if (mo != QTextCursor::NoMove) { cr.movePosition (mo, QTextCursor::MoveAnchor); d->setTextCursor (cr); } } CDocument* CDox::create_new() { CDocument *doc = new CDocument (this, 0); doc->markup_mode = markup_mode; tab_widget->setCurrentIndex (tab_widget->indexOf (doc->tab_page)); apply_settings_single (doc); doc->update_title (settings->value ("full_path_at_window_title", 1).toBool()); doc->update_status(); update_current_files_menu(); return doc; } CDocument* CDox::open_file (const QString &fileName, const QString &codec) { if (! file_exists (fileName) || ! path_is_file (fileName)) return 0; if (is_image (fileName)) { CDocument *td = get_current(); if (td) { td->insert_image (fileName); td->setFocus (Qt::OtherFocusReason); } return td; } CDocument *d = get_document_by_fname (fileName); if (d) { tab_widget->setCurrentIndex (tab_widget->indexOf (d->tab_page)); d->reload (codec); return d; } //else truly create the new doc d = create_new(); d->file_open (fileName, codec); dir_last = get_file_path (d->file_name); d->update_status(); d->update_title (settings->value ("full_path_at_window_title", 1).toBool()); main_tab_widget->setCurrentIndex (0); update_current_files_menu(); return d; } CDocument* CDox::open_file_without_reload (const QString &fileName, const QString &codec) { if (! file_exists (fileName) || ! path_is_file (fileName)) return 0; if (is_image (fileName)) { CDocument *td = get_current(); if (td) { td->insert_image (fileName); td->setFocus (Qt::OtherFocusReason); } return td; } CDocument *d = get_document_by_fname (fileName); if (d) { tab_widget->setCurrentIndex (tab_widget->indexOf (d->tab_page)); //d->reload (codec); return d; } //else truly create the new doc d = create_new(); d->file_open (fileName, codec); dir_last = get_file_path (d->file_name); d->update_status(); d->update_title (settings->value ("full_path_at_window_title", 1).toBool()); main_tab_widget->setCurrentIndex (0); update_current_files_menu(); return d; } CDocument* CDox::open_file_triplex (const QString &triplex) { //qDebug() << "open_file_triplex "; //QString tp = triplex; // tp.remove ("&"); QStringList sl = triplex.split ("*"); if (sl.size() < 3) return 0; CDocument *d = open_file (sl[0], sl[1]); if (! d) return 0; d->goto_pos (sl[2].toInt()); if (sl.size() >= 4) { if (sl[3] == "1") d->set_word_wrap (true); else d->set_word_wrap (false); } return d; } CDocument* CDox::get_document_by_fname (const QString &fileName) { if (fileName.isEmpty() || items.size() == 0) return 0; for (vector ::iterator i = items.begin(); i != items.end(); ++i) if ((*i)->file_name == fileName) return *i; return 0; } CDocument* CDox::get_current() { int i = tab_widget->currentIndex(); if (i < 0) return 0; return items[i]; } void CDox::close_by_idx (int i) { if (i < 0) return; delete items[i]; items.erase (items.begin() + i); update_current_files_menu(); } void CDox::close_current() { close_by_idx (tab_widget->currentIndex()); } void CDox::save_to_session (const QString &fileName) { if (items.size() == 0) return; fname_current_session = fileName; QString l; for (vector ::iterator i = items.begin(); i != items.end(); ++i) { QString t = (*i)->get_triplex(); if (! t.isEmpty()) { l += t; l += "\n"; } } qstring_save (fileName, l.trimmed()); } void CDox::load_from_session (const QString &fileName) { if (! file_exists (fileName)) return; QStringList l = qstring_load (fileName).split ("\n"); if (l.size() < 0) return; for (int i = 0; i < l.size(); i++) open_file_triplex (l[i]); fname_current_session = fileName; } void CDox::save_buffers (const QString &fileName) { QFile::remove (fileName); if (items.size() == 0) return; fname_current_session = fileName; QString l; for (vector ::iterator i = items.begin(); i != items.end(); ++i) { if (! file_exists ((*i)->file_name)) { l += (*i)->toPlainText(); l += '\f'; } } qstring_save (fileName, l.trimmed()); } void CDox::load_from_buffers (const QString &fileName) { if (! file_exists (fileName)) return; QStringList l = qstring_load (fileName).split ("\f"); if (l.size() < 0) return; for (int i = 0; i < l.size(); i++) { CDocument *d = create_new(); d->put (l[i]); QTextCursor cr = d->textCursor(); if (! cr.isNull()) { cr.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor); d->setTextCursor (cr); } } fname_current_session = fileName; } void CDox::apply_settings() { if (items.size() == 0) return; for (vector ::size_type i = 0; i < items.size(); i++) apply_settings_single (items[i]); } void CDox::apply_settings_single (CDocument *d) { int darker_val = settings->value ("darker_val", 100).toInt(); d->setCursorWidth (settings->value ("cursor_width", 2).toInt()); d->setCenterOnScroll (settings->value ("center_on_scroll", true).toBool()); QString s_sel_back_color = hash_get_val (global_palette, "sel-background", "black"); QString s_sel_text_color = hash_get_val (global_palette, "sel-text", "white"); d->sel_text_color = QColor (s_sel_text_color).darker(darker_val).name(); d->sel_back_color = QColor (s_sel_back_color).darker(darker_val).name(); d->tab_sp_width = settings->value ("tab_sp_width", 8).toInt(); d->spaces_instead_of_tabs = settings->value ("spaces_instead_of_tabs", true).toBool(); #if (QT_VERSION_MAJOR <= 5 && QT_VERSION_MINOR < 10) d->setTabStopWidth (d->tab_sp_width * d->brace_width); #else d->setTabStopDistance (d->tab_sp_width * d->brace_width); #endif d->setup_brace_width(); d->set_show_linenums (settings->value ("show_linenums", false).toBool()); d->show_tabs_and_spaces = settings->value ("show_tabs_and_spaces", false).toBool(); QTextOption option = d->document()->defaultTextOption(); if (d->show_tabs_and_spaces) option.setFlags(option.flags() | QTextOption::ShowTabsAndSpaces); else option.setFlags(option.flags() & ~QTextOption::ShowTabsAndSpaces); option.setFlags (option.flags() | QTextOption::AddSpaceForLineAndParagraphSeparators); d->document()->setDefaultTextOption (option); d->set_show_margin (settings->value ("show_margin", false).toBool()); d->set_margin_pos (settings->value ("margin_pos", 72).toInt()); d->highlight_current_line = settings->value ("additional_hl", false).toBool(); d->hl_brackets = settings->value ("hl_brackets", false).toBool(); d->brackets_color = QColor (hash_get_val (global_palette, "brackets", "yellow")).darker (darker_val); d->current_line_color = QColor (hash_get_val (global_palette, "cur_line_color", "#EEF6FF")).darker (darker_val); d->cursor_xy_visible = settings->value ("cursor_xy_visible", "2").toBool(); d->auto_indent = settings->value ("auto_indent", false).toBool(); QString text_color = hash_get_val (global_palette, "text", "black"); QString t_text_color = QColor (text_color).darker(darker_val).name(); d->text_color = QColor (t_text_color); QString back_color = hash_get_val (global_palette, "background", "white"); d->margin_color = QColor (hash_get_val (global_palette, "margin_color", text_color)).darker(darker_val); d->linenums_bg = QColor (hash_get_val (global_palette, "linenums_bg", back_color)).darker(darker_val); d->set_word_wrap (settings->value ("word_wrap", true).toBool()); d->repaint(); d->set_hl(); } void CDox::open_recent() { QAction *act = qobject_cast(sender()); //QString t = act->text(); QString t = act->data().toString(); //t.remove ("&"); qDebug() << "t:" << t; // int i = recent_files.indexOf (t); // if (i == -1) // return; // CDocument *d = open_file_triplex (recent_files[i]); CDocument *d = open_file_triplex (t); if (d) dir_last = get_file_path (d->file_name); update_recent_menu(); } void CDox::open_current() { QAction *act = qobject_cast(sender()); CDocument *d = get_document_by_fname (act->text()); if (d) tab_widget->setCurrentIndex (tab_widget->indexOf (d->tab_page)); } void CDox::autosave() { if (! settings->value ("autosave", false).toBool()) return; if (items.size() == 0) return; for (vector ::iterator i = items.begin(); i != items.end(); ++i) { if (autosave_files.contains ((*i)->file_name)) (*i)->file_save_with_name ((*i)->file_name, (*i)->charset); } save_buffers (fname_saved_buffers); } void CDox::move_cursor_up() { move_cursor (QTextCursor::Up); } void CDox::move_cursor_down() { move_cursor (QTextCursor::Down); } void CDox::move_cursor_left() { move_cursor (QTextCursor::Left); } void CDox::move_cursor_right() { move_cursor (QTextCursor::Right); } void CDox::move_cursor_x (double v) { if (v < 0) move_cursor (QTextCursor::Right); if (v > 0) move_cursor (QTextCursor::Left); } void CDox::move_cursor_y (double v) { if (v < 0) move_cursor (QTextCursor::Up); if (v > 0) move_cursor (QTextCursor::Down); } #if defined(JOYSTICK_SUPPORTED) bool CDox::event (QEvent *ev) { if (static_cast(ev->type() == evtJoystickAxis)) { CJoystickAxisEvent* custom_event = reinterpret_cast(ev); handle_joystick_event (custom_event); custom_event->accept(); return true; } return QObject::event(ev); } void CDox::handle_joystick_event (CJoystickAxisEvent *event) { QTextCursor::MoveOperation mo = QTextCursor::NoMove; if (event->axis == 1 && event->value < 0) //up mo = QTextCursor::Up; if (event->axis == 1 && event->value > 0) //down mo = QTextCursor::Down; if (event->axis == 0 && event->value < 0) //left mo = QTextCursor::Left; if (event->axis == 0 && event->value > 0) //right mo = QTextCursor::Right; move_cursor (mo); } #endif tea-qt-63.3.0/src/document.h000066400000000000000000000235001476733534200155720ustar00rootroot00000000000000/*************************************************************************** * 2007-2024 by Peter Semiletov * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************/ /* Copyright (C) 2006-2008 Trolltech ASA. All rights reserved. */ /* Diego Iastrubni //some GPL'ed code from new-editor-diego-3, found on qtcentre forum */ /* code from qwriter: * Copyright (C) 2009 by Gancov Kostya * * kossne@mail.ru * */ #ifndef DOCUMENT_H #define DOCUMENT_H #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050000 #include #else #include #endif #if defined (JOYSTICK_SUPPORTED) #include "myjoystick.h" #endif #include "logmemo.h" #include "tio.h" #include "todo.h" using namespace std; class CDox; class CDocument; class CLineNumberArea; class CSyntaxHighlighter: public QSyntaxHighlighter { public: CDocument *document; bool casecare; QString comment_mult; QString comment_single; QTextCharFormat fmt_multi_line_comment; CSyntaxHighlighter (QTextDocument *parent = 0, CDocument *doc = 0, const QString &fname = "none"); }; #if QT_VERSION < 0x050000 class CSyntaxHighlighterQRegExp: public CSyntaxHighlighter { Q_OBJECT protected: void highlightBlock (const QString &text); public: vector > hl_rules; pair comment_start_expr; pair comment_end_expr; Qt::CaseSensitivity cs; CSyntaxHighlighterQRegExp (QTextDocument *parent = 0, CDocument *doc = 0, const QString &fname = "none"); //void load_from_xml (const QString &fname); void load_from_xml_pugi (const QString &fname); }; #endif #if QT_VERSION >= 0x050000 class CSyntaxHighlighterQRegularExpression: public CSyntaxHighlighter { Q_OBJECT protected: void highlightBlock (const QString &text); public: vector > hl_rules; QRegularExpression::PatternOptions pattern_opts; pair comment_start_expr; pair comment_end_expr; CSyntaxHighlighterQRegularExpression (QTextDocument *parent = 0, CDocument *doc = 0, const QString &fname = "none"); //void load_from_xml (const QString &fname); void load_from_xml_pugi (const QString &fname); }; #endif class CDocument: public QPlainTextEdit { Q_OBJECT private: CLineNumberArea *line_num_area; QList extra_selections; QTextEdit::ExtraSelection brace_selection; protected: QAction *actCopy; QAction *actCut; QAction *actPaste; QMimeData* createMimeDataFromSelection() const; bool canInsertFromMimeData (const QMimeData *source) const; void insertFromMimeData (const QMimeData *source); void paintEvent (QPaintEvent *event); void keyPressEvent (QKeyEvent *event); void resizeEvent (QResizeEvent *event); void wheelEvent (QWheelEvent *e); void contextMenuEvent (QContextMenuEvent *event); //QMenu* createStandardContextMenu(); public: CDox *holder; //uplink QWidget *tab_page; //pointer CSyntaxHighlighter *highlighter; QStringList labels; QString eol; QString markup_mode; QString file_name; QString text_to_search; QString charset; QString indent_val; QPoint rect_sel_start; //rect selection QPoint rect_sel_end; //rect selection QColor current_line_color; QColor brackets_color; QColor margin_color; QColor linenums_bg; QColor text_color; QColor sel_text_color; QColor sel_back_color; bool cursor_xy_visible; bool highlight_current_line; bool hl_brackets; bool draw_margin; bool draw_linenums; bool auto_indent; bool spaces_instead_of_tabs; bool show_tabs_and_spaces; int position; int tab_sp_width; //in spaces int brace_width; //in pixels int margin_pos; //in chars int margin_x; //in pixels CDocument (CDox *hldr, QWidget *parent = 0); ~CDocument(); QString get() const; //return selected text void put (const QString &value); //replace selection or insert text at cursor bool has_selection(); bool file_open (const QString &fileName, const QString &codec); bool file_save_with_name (const QString &fileName, const QString &codec); bool file_save_with_name_plain (const QString &fileName); int get_tab_idx(); QString get_triplex(); QString get_filename_at_cursor(); QStringList get_words(); void goto_pos (int pos); void set_tab_caption (const QString &fileName); void set_hl (bool mode_auto = true, const QString &theext = "txt"); void set_markup_mode(); void insert_image (const QString &full_path); void reload (const QString &enc); void update_status(); void update_title (bool fullname = true); void update_labels(); void set_show_linenums (bool enable); void set_show_margin (bool enable); void set_margin_pos (int mp); void set_hl_cur_line (bool enable); void set_hl_brackets (bool enable); void set_word_wrap (bool wrap); bool get_word_wrap(); void indent(); void un_indent(); void calc_auto_indent(); void setup_brace_width(); void brace_highlight(); void update_ext_selections(); void rect_block_start(); void rect_block_end(); bool has_rect_selection() const; void rect_sel_reset(); void rect_sel_replace (const QString &s, bool insert = false); void rect_sel_upd(); QString rect_sel_get() const; void rect_sel_cut (bool just_del = false); void lineNumberAreaPaintEvent (QPaintEvent *event); int line_number_area_width(); public slots: void updateLineNumberAreaWidth(); void cb_cursorPositionChanged(); void updateLineNumberArea (const QRect &, int); void slot_selectionChanged(); void ed_copy(); void ed_cut(); void ed_paste(); }; class CDox: public QObject { Q_OBJECT public: QStringList recent_files; //regexp pattern and file name of syntax hl rules #if QT_VERSION >= 0x050000 std::vector > hl_files; #else std::vector > hl_files; #endif std::vector items; QHash markup_modes; QHash hash_project; QHash autosave_files; CTioHandler tio_handler; CTodo todo; QString dir_last; QString fname_current_session; QString fname_current_project; QString dir_config; QString fname_crapbook; QString fname_saved_buffers; QString markup_mode; QString recent_list_fname; QLabel *l_status_bar; QLabel *l_charset; CLogMemo *log; //uplink QMainWindow *parent_wnd; //uplink QTabWidget *tab_widget; //uplink QTabWidget *main_tab_widget; //uplink QMenu *menu_recent; //uplink QTimer timer_joystick; QTimer timer_autosave; #if defined(JOYSTICK_SUPPORTED) CJoystick *joystick; #endif CDox(); ~CDox(); void update_project (const QString &fileName); void reload_recent_list(); void add_to_recent (CDocument *d); void update_recent_menu(); void update_current_files_menu(); void move_cursor (QTextCursor::MoveOperation mo); CDocument* create_new(); CDocument* open_file (const QString &fileName, const QString &codec); CDocument* open_file_without_reload (const QString &fileName, const QString &codec); //for IDE CDocument* open_file_triplex (const QString &triplex); CDocument* get_document_by_fname (const QString &fileName); CDocument* get_current(); void close_by_idx (int i); void close_current(); void save_to_session (const QString &fileName); void load_from_session (const QString &fileName); void save_buffers (const QString &fileName); void load_from_buffers (const QString &fileName); void apply_settings(); void apply_settings_single (CDocument *d); #if defined(JOYSTICK_SUPPORTED) bool event (QEvent *ev); void handle_joystick_event (CJoystickAxisEvent *ev); #endif public slots: void open_recent(); void open_current(); void autosave(); void move_cursor_up(); void move_cursor_down(); void move_cursor_left(); void move_cursor_right(); void move_cursor_x (double v); void move_cursor_y (double v); }; class CLineNumberArea: public QWidget { public: CDocument *code_editor; //uplink CLineNumberArea (CDocument *editor = 0): QWidget (editor), code_editor (editor) {} QSize sizeHint() const { return QSize (code_editor->line_number_area_width(), 0); } protected: void paintEvent (QPaintEvent *event) { code_editor->lineNumberAreaPaintEvent (event); } }; #endif tea-qt-63.3.0/src/enc.cpp000066400000000000000000001736371476733534200150750ustar00rootroot00000000000000//#include #include #include #include #include "enc.h" std::map create_UTF16_to_CP1251() { std::map UTF16_to_CP1251; UTF16_to_CP1251[0x0020] = 0x20; // SPACE UTF16_to_CP1251[0x0021] = 0x21; // EXCLAMATION MARK UTF16_to_CP1251[0x0022] = 0x22; // QUOTATION MARK UTF16_to_CP1251[0x0023] = 0x23; // NUMBER SIGN UTF16_to_CP1251[0x0024] = 0x24; // DOLLAR SIGN UTF16_to_CP1251[0x0025] = 0x25; // PERCENT SIGN UTF16_to_CP1251[0x0026] = 0x26; // AMPERSAND UTF16_to_CP1251[0x0027] = 0x27; // APOSTROPHE UTF16_to_CP1251[0x0028] = 0x28; // LEFT PARENTHESIS UTF16_to_CP1251[0x0029] = 0x29; // RIGHT PARENTHESIS UTF16_to_CP1251[0x002A] = 0x2A; // ASTERISK UTF16_to_CP1251[0x002B] = 0x2B; // PLUS SIGN UTF16_to_CP1251[0x002C] = 0x2C; // COMMA UTF16_to_CP1251[0x002D] = 0x2D; // HYPHEN-MINUS UTF16_to_CP1251[0x002E] = 0x2E; // FULL STOP UTF16_to_CP1251[0x002F] = 0x2F; // SOLIDUS UTF16_to_CP1251[0x0030] = 0x30; // DIGIT ZERO UTF16_to_CP1251[0x0031] = 0x31; // DIGIT ONE UTF16_to_CP1251[0x0032] = 0x32; // DIGIT TWO UTF16_to_CP1251[0x0033] = 0x33; // DIGIT THREE UTF16_to_CP1251[0x0034] = 0x34; // DIGIT FOUR UTF16_to_CP1251[0x0035] = 0x35; // DIGIT FIVE UTF16_to_CP1251[0x0036] = 0x36; // DIGIT SIX UTF16_to_CP1251[0x0037] = 0x37; // DIGIT SEVEN UTF16_to_CP1251[0x0038] = 0x38; // DIGIT EIGHT UTF16_to_CP1251[0x0039] = 0x39; // DIGIT NINE UTF16_to_CP1251[0x003A] = 0x3A; // COLON UTF16_to_CP1251[0x003B] = 0x3B; // SEMICOLON UTF16_to_CP1251[0x003C] = 0x3C; // LESS-THAN SIGN UTF16_to_CP1251[0x003D] = 0x3D; // EQUALS SIGN UTF16_to_CP1251[0x003E] = 0x3E; // GREATER-THAN SIGN UTF16_to_CP1251[0x003F] = 0x3F; // QUESTION MARK UTF16_to_CP1251[0x0040] = 0x40; // COMMERCIAL AT UTF16_to_CP1251[0x0041] = 0x41; // LATIN CAPITAL LETTER A UTF16_to_CP1251[0x0042] = 0x42; // LATIN CAPITAL LETTER B UTF16_to_CP1251[0x0043] = 0x43; // LATIN CAPITAL LETTER C UTF16_to_CP1251[0x0044] = 0x44; // LATIN CAPITAL LETTER D UTF16_to_CP1251[0x0045] = 0x45; // LATIN CAPITAL LETTER E UTF16_to_CP1251[0x0046] = 0x46; // LATIN CAPITAL LETTER F UTF16_to_CP1251[0x0047] = 0x47; // LATIN CAPITAL LETTER G UTF16_to_CP1251[0x0048] = 0x48; // LATIN CAPITAL LETTER H UTF16_to_CP1251[0x0049] = 0x49; // LATIN CAPITAL LETTER I UTF16_to_CP1251[0x004A] = 0x4A; // LATIN CAPITAL LETTER J UTF16_to_CP1251[0x004B] = 0x4B; // LATIN CAPITAL LETTER K UTF16_to_CP1251[0x004C] = 0x4C; // LATIN CAPITAL LETTER L UTF16_to_CP1251[0x004D] = 0x4D; // LATIN CAPITAL LETTER M UTF16_to_CP1251[0x004E] = 0x4E; // LATIN CAPITAL LETTER N UTF16_to_CP1251[0x004F] = 0x4F; // LATIN CAPITAL LETTER O UTF16_to_CP1251[0x0050] = 0x50; // LATIN CAPITAL LETTER P UTF16_to_CP1251[0x0051] = 0x51; // LATIN CAPITAL LETTER Q UTF16_to_CP1251[0x0052] = 0x52; // LATIN CAPITAL LETTER R UTF16_to_CP1251[0x0053] = 0x53; // LATIN CAPITAL LETTER S UTF16_to_CP1251[0x0054] = 0x54; // LATIN CAPITAL LETTER T UTF16_to_CP1251[0x0055] = 0x55; // LATIN CAPITAL LETTER U UTF16_to_CP1251[0x0056] = 0x56; // LATIN CAPITAL LETTER V UTF16_to_CP1251[0x0057] = 0x57; // LATIN CAPITAL LETTER W UTF16_to_CP1251[0x0058] = 0x58; // LATIN CAPITAL LETTER X UTF16_to_CP1251[0x0059] = 0x59; // LATIN CAPITAL LETTER Y UTF16_to_CP1251[0x005A] = 0x5A; // LATIN CAPITAL LETTER Z UTF16_to_CP1251[0x005B] = 0x5B; // LEFT SQUARE BRACKET UTF16_to_CP1251[0x005C] = 0x5C; // REVERSE SOLIDUS UTF16_to_CP1251[0x005D] = 0x5D; // RIGHT SQUARE BRACKET UTF16_to_CP1251[0x005E] = 0x5E; // CIRCUMFLEX ACCENT UTF16_to_CP1251[0x005F] = 0x5F; // LOW LINE UTF16_to_CP1251[0x0060] = 0x60; // GRAVE ACCENT UTF16_to_CP1251[0x0061] = 0x61; // LATIN SMALL LETTER A UTF16_to_CP1251[0x0062] = 0x62; // LATIN SMALL LETTER B UTF16_to_CP1251[0x0063] = 0x63; // LATIN SMALL LETTER C UTF16_to_CP1251[0x0064] = 0x64; // LATIN SMALL LETTER D UTF16_to_CP1251[0x0065] = 0x65; // LATIN SMALL LETTER E UTF16_to_CP1251[0x0066] = 0x66; // LATIN SMALL LETTER F UTF16_to_CP1251[0x0067] = 0x67; // LATIN SMALL LETTER G UTF16_to_CP1251[0x0068] = 0x68; // LATIN SMALL LETTER H UTF16_to_CP1251[0x0069] = 0x69; // LATIN SMALL LETTER I UTF16_to_CP1251[0x006A] = 0x6A; // LATIN SMALL LETTER J UTF16_to_CP1251[0x006B] = 0x6B; // LATIN SMALL LETTER K UTF16_to_CP1251[0x006C] = 0x6C; // LATIN SMALL LETTER L UTF16_to_CP1251[0x006D] = 0x6D; // LATIN SMALL LETTER M UTF16_to_CP1251[0x006E] = 0x6E; // LATIN SMALL LETTER N UTF16_to_CP1251[0x006F] = 0x6F; // LATIN SMALL LETTER O UTF16_to_CP1251[0x0070] = 0x70; // LATIN SMALL LETTER P UTF16_to_CP1251[0x0071] = 0x71; // LATIN SMALL LETTER Q UTF16_to_CP1251[0x0072] = 0x72; // LATIN SMALL LETTER R UTF16_to_CP1251[0x0073] = 0x73; // LATIN SMALL LETTER S UTF16_to_CP1251[0x0074] = 0x74; // LATIN SMALL LETTER T UTF16_to_CP1251[0x0075] = 0x75; // LATIN SMALL LETTER U UTF16_to_CP1251[0x0076] = 0x76; // LATIN SMALL LETTER V UTF16_to_CP1251[0x0077] = 0x77; // LATIN SMALL LETTER W UTF16_to_CP1251[0x0078] = 0x78; // LATIN SMALL LETTER X UTF16_to_CP1251[0x0079] = 0x79; // LATIN SMALL LETTER Y UTF16_to_CP1251[0x007A] = 0x7A; // LATIN SMALL LETTER Z UTF16_to_CP1251[0x007B] = 0x7B; // LEFT CURLY BRACKET UTF16_to_CP1251[0x007C] = 0x7C; // VERTICAL LINE UTF16_to_CP1251[0x007D] = 0x7D; // RIGHT CURLY BRACKET UTF16_to_CP1251[0x007E] = 0x7E; // TILDE UTF16_to_CP1251[0x0401] = 0xF0; // CYRILLIC CAPITAL LETTER IO UTF16_to_CP1251[0x0410] = 0xC0; // CYRILLIC CAPITAL LETTER A UTF16_to_CP1251[0x0411] = 0xC1; // CYRILLIC CAPITAL LETTER BE UTF16_to_CP1251[0x0412] = 0xC2; // CYRILLIC CAPITAL LETTER VE UTF16_to_CP1251[0x0413] = 0xC3; // CYRILLIC CAPITAL LETTER GHE UTF16_to_CP1251[0x0414] = 0xC4; // CYRILLIC CAPITAL LETTER DE UTF16_to_CP1251[0x0415] = 0xC5; // CYRILLIC CAPITAL LETTER IE UTF16_to_CP1251[0x0416] = 0xC6; // CYRILLIC CAPITAL LETTER ZHE UTF16_to_CP1251[0x0417] = 0xC7; // CYRILLIC CAPITAL LETTER ZE UTF16_to_CP1251[0x0418] = 0xC8; // CYRILLIC CAPITAL LETTER I UTF16_to_CP1251[0x0419] = 0xC9; // CYRILLIC CAPITAL LETTER SHORT I UTF16_to_CP1251[0x041A] = 0xCA; // CYRILLIC CAPITAL LETTER KA UTF16_to_CP1251[0x041B] = 0xCB; // CYRILLIC CAPITAL LETTER EL UTF16_to_CP1251[0x041C] = 0xCC; // CYRILLIC CAPITAL LETTER EM UTF16_to_CP1251[0x041D] = 0xCD; // CYRILLIC CAPITAL LETTER EN UTF16_to_CP1251[0x041E] = 0xCE; // CYRILLIC CAPITAL LETTER O UTF16_to_CP1251[0x041F] = 0xCF; // CYRILLIC CAPITAL LETTER PE UTF16_to_CP1251[0x0420] = 0xD0; // CYRILLIC CAPITAL LETTER ER UTF16_to_CP1251[0x0421] = 0xD1; // CYRILLIC CAPITAL LETTER ES UTF16_to_CP1251[0x0422] = 0xD2; // CYRILLIC CAPITAL LETTER TE UTF16_to_CP1251[0x0423] = 0xD3; // CYRILLIC CAPITAL LETTER U UTF16_to_CP1251[0x0424] = 0xD4; // CYRILLIC CAPITAL LETTER EF UTF16_to_CP1251[0x0425] = 0xD5; // CYRILLIC CAPITAL LETTER HA UTF16_to_CP1251[0x0426] = 0xD6; // CYRILLIC CAPITAL LETTER TSE UTF16_to_CP1251[0x0427] = 0xD7; // CYRILLIC CAPITAL LETTER CHE UTF16_to_CP1251[0x0428] = 0xD8; // CYRILLIC CAPITAL LETTER SHA UTF16_to_CP1251[0x0429] = 0xD9; // CYRILLIC CAPITAL LETTER SHCHA UTF16_to_CP1251[0x042A] = 0xDA; // CYRILLIC CAPITAL LETTER HARD SIGN UTF16_to_CP1251[0x042B] = 0xDB; // CYRILLIC CAPITAL LETTER YERU UTF16_to_CP1251[0x042C] = 0xDC; // CYRILLIC CAPITAL LETTER SOFT SIGN UTF16_to_CP1251[0x042D] = 0xDD; // CYRILLIC CAPITAL LETTER E UTF16_to_CP1251[0x042E] = 0xDE; // CYRILLIC CAPITAL LETTER YU UTF16_to_CP1251[0x042F] = 0xDF; // CYRILLIC CAPITAL LETTER YA UTF16_to_CP1251[0x0430] = 0xE0; // CYRILLIC SMALL LETTER A UTF16_to_CP1251[0x0431] = 0xE1; // CYRILLIC SMALL LETTER BE UTF16_to_CP1251[0x0432] = 0xE2; // CYRILLIC SMALL LETTER VE UTF16_to_CP1251[0x0433] = 0xE3; // CYRILLIC SMALL LETTER GHE UTF16_to_CP1251[0x0434] = 0xE4; // CYRILLIC SMALL LETTER DE UTF16_to_CP1251[0x0435] = 0xE5; // CYRILLIC SMALL LETTER IE UTF16_to_CP1251[0x0436] = 0xE6; // CYRILLIC SMALL LETTER ZHE UTF16_to_CP1251[0x0437] = 0xE7; // CYRILLIC SMALL LETTER ZE UTF16_to_CP1251[0x0438] = 0xE8; // CYRILLIC SMALL LETTER I UTF16_to_CP1251[0x0439] = 0xE9; // CYRILLIC SMALL LETTER SHORT I UTF16_to_CP1251[0x043A] = 0xEA; // CYRILLIC SMALL LETTER KA UTF16_to_CP1251[0x043B] = 0xEB; // CYRILLIC SMALL LETTER EL UTF16_to_CP1251[0x043C] = 0xEC; // CYRILLIC SMALL LETTER EM UTF16_to_CP1251[0x043D] = 0xED; // CYRILLIC SMALL LETTER EN UTF16_to_CP1251[0x043E] = 0xEE; // CYRILLIC SMALL LETTER O UTF16_to_CP1251[0x043F] = 0xEF; // CYRILLIC SMALL LETTER PE UTF16_to_CP1251[0x0440] = 0xF0; // CYRILLIC SMALL LETTER ER UTF16_to_CP1251[0x0441] = 0xF1; // CYRILLIC SMALL LETTER ES UTF16_to_CP1251[0x0442] = 0xF2; // CYRILLIC SMALL LETTER TE UTF16_to_CP1251[0x0443] = 0xF3; // CYRILLIC SMALL LETTER U UTF16_to_CP1251[0x0444] = 0xF4; // CYRILLIC SMALL LETTER EF UTF16_to_CP1251[0x0445] = 0xF5; // CYRILLIC SMALL LETTER HA UTF16_to_CP1251[0x0446] = 0xF6; // CYRILLIC SMALL LETTER TSE UTF16_to_CP1251[0x0447] = 0xF7; // CYRILLIC SMALL LETTER CHE UTF16_to_CP1251[0x0448] = 0xF8; // CYRILLIC SMALL LETTER SHA UTF16_to_CP1251[0x0449] = 0xF9; // CYRILLIC SMALL LETTER SHCHA UTF16_to_CP1251[0x044A] = 0xFA; // CYRILLIC SMALL LETTER HARD SIGN UTF16_to_CP1251[0x044B] = 0xFB; // CYRILLIC SMALL LETTER YERU UTF16_to_CP1251[0x044C] = 0xFC; // CYRILLIC SMALL LETTER SOFT SIGN UTF16_to_CP1251[0x044D] = 0xFD; // CYRILLIC SMALL LETTER E UTF16_to_CP1251[0x044E] = 0xFE; // CYRILLIC SMALL LETTER YU UTF16_to_CP1251[0x044F] = 0xFF; // CYRILLIC SMALL LETTER YA UTF16_to_CP1251[0x0a] = 0x0a; // Keep newline character UTF16_to_CP1251[0x0d] = 0x0d; // Keep carriage return character UTF16_to_CP1251[0x401] = 0xA8; // Ё UTF16_to_CP1251[0x451] = 0xB8 ; // ё UTF16_to_CP1251[0x2013] = 0x96; // малый пробел UTF16_to_CP1251[0x2014] = 0x97; // большой пробел return UTF16_to_CP1251; }; //ok UTF16TEXT cp1251_to_utf16[256] = { // 0x00 - 0x7F: Соответствуют символам ASCII 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, // 0x80 - 0xFF: Символы CP1251 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0020, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F }; //ok /* std::map UTF16_to_CP1251 = { {0x0020, 0x20}, // SPACE {0x0021, 0x21}, // EXCLAMATION MARK {0x0022, 0x22}, // QUOTATION MARK {0x0023, 0x23}, // NUMBER SIGN {0x0024, 0x24}, // DOLLAR SIGN {0x0025, 0x25}, // PERCENT SIGN {0x0026, 0x26}, // AMPERSAND {0x0027, 0x27}, // APOSTROPHE {0x0028, 0x28}, // LEFT PARENTHESIS {0x0029, 0x29}, // RIGHT PARENTHESIS {0x002A, 0x2A}, // ASTERISK {0x002B, 0x2B}, // PLUS SIGN {0x002C, 0x2C}, // COMMA {0x002D, 0x2D}, // HYPHEN-MINUS {0x002E, 0x2E}, // FULL STOP {0x002F, 0x2F}, // SOLIDUS {0x0030, 0x30}, // DIGIT ZERO {0x0031, 0x31}, // DIGIT ONE {0x0032, 0x32}, // DIGIT TWO {0x0033, 0x33}, // DIGIT THREE {0x0034, 0x34}, // DIGIT FOUR {0x0035, 0x35}, // DIGIT FIVE {0x0036, 0x36}, // DIGIT SIX {0x0037, 0x37}, // DIGIT SEVEN {0x0038, 0x38}, // DIGIT EIGHT {0x0039, 0x39}, // DIGIT NINE {0x003A, 0x3A}, // COLON {0x003B, 0x3B}, // SEMICOLON {0x003C, 0x3C}, // LESS-THAN SIGN {0x003D, 0x3D}, // EQUALS SIGN {0x003E, 0x3E}, // GREATER-THAN SIGN {0x003F, 0x3F}, // QUESTION MARK {0x0040, 0x40}, // COMMERCIAL AT {0x0041, 0x41}, // LATIN CAPITAL LETTER A {0x0042, 0x42}, // LATIN CAPITAL LETTER B {0x0043, 0x43}, // LATIN CAPITAL LETTER C {0x0044, 0x44}, // LATIN CAPITAL LETTER D {0x0045, 0x45}, // LATIN CAPITAL LETTER E {0x0046, 0x46}, // LATIN CAPITAL LETTER F {0x0047, 0x47}, // LATIN CAPITAL LETTER G {0x0048, 0x48}, // LATIN CAPITAL LETTER H {0x0049, 0x49}, // LATIN CAPITAL LETTER I {0x004A, 0x4A}, // LATIN CAPITAL LETTER J {0x004B, 0x4B}, // LATIN CAPITAL LETTER K {0x004C, 0x4C}, // LATIN CAPITAL LETTER L {0x004D, 0x4D}, // LATIN CAPITAL LETTER M {0x004E, 0x4E}, // LATIN CAPITAL LETTER N {0x004F, 0x4F}, // LATIN CAPITAL LETTER O {0x0050, 0x50}, // LATIN CAPITAL LETTER P {0x0051, 0x51}, // LATIN CAPITAL LETTER Q {0x0052, 0x52}, // LATIN CAPITAL LETTER R {0x0053, 0x53}, // LATIN CAPITAL LETTER S {0x0054, 0x54}, // LATIN CAPITAL LETTER T {0x0055, 0x55}, // LATIN CAPITAL LETTER U {0x0056, 0x56}, // LATIN CAPITAL LETTER V {0x0057, 0x57}, // LATIN CAPITAL LETTER W {0x0058, 0x58}, // LATIN CAPITAL LETTER X {0x0059, 0x59}, // LATIN CAPITAL LETTER Y {0x005A, 0x5A}, // LATIN CAPITAL LETTER Z {0x005B, 0x5B}, // LEFT SQUARE BRACKET {0x005C, 0x5C}, // REVERSE SOLIDUS {0x005D, 0x5D}, // RIGHT SQUARE BRACKET {0x005E, 0x5E}, // CIRCUMFLEX ACCENT {0x005F, 0x5F}, // LOW LINE {0x0060, 0x60}, // GRAVE ACCENT {0x0061, 0x61}, // LATIN SMALL LETTER A {0x0062, 0x62}, // LATIN SMALL LETTER B {0x0063, 0x63}, // LATIN SMALL LETTER C {0x0064, 0x64}, // LATIN SMALL LETTER D {0x0065, 0x65}, // LATIN SMALL LETTER E {0x0066, 0x66}, // LATIN SMALL LETTER F {0x0067, 0x67}, // LATIN SMALL LETTER G {0x0068, 0x68}, // LATIN SMALL LETTER H {0x0069, 0x69}, // LATIN SMALL LETTER I {0x006A, 0x6A}, // LATIN SMALL LETTER J {0x006B, 0x6B}, // LATIN SMALL LETTER K {0x006C, 0x6C}, // LATIN SMALL LETTER L {0x006D, 0x6D}, // LATIN SMALL LETTER M {0x006E, 0x6E}, // LATIN SMALL LETTER N {0x006F, 0x6F}, // LATIN SMALL LETTER O {0x0070, 0x70}, // LATIN SMALL LETTER P {0x0071, 0x71}, // LATIN SMALL LETTER Q {0x0072, 0x72}, // LATIN SMALL LETTER R {0x0073, 0x73}, // LATIN SMALL LETTER S {0x0074, 0x74}, // LATIN SMALL LETTER T {0x0075, 0x75}, // LATIN SMALL LETTER U {0x0076, 0x76}, // LATIN SMALL LETTER V {0x0077, 0x77}, // LATIN SMALL LETTER W {0x0078, 0x78}, // LATIN SMALL LETTER X {0x0079, 0x79}, // LATIN SMALL LETTER Y {0x007A, 0x7A}, // LATIN SMALL LETTER Z {0x007B, 0x7B}, // LEFT CURLY BRACKET {0x007C, 0x7C}, // VERTICAL LINE {0x007D, 0x7D}, // RIGHT CURLY BRACKET {0x007E, 0x7E}, // TILDE {0x0401, 0xF0}, // CYRILLIC CAPITAL LETTER IO {0x0410, 0xC0}, // CYRILLIC CAPITAL LETTER A {0x0411, 0xC1}, // CYRILLIC CAPITAL LETTER BE {0x0412, 0xC2}, // CYRILLIC CAPITAL LETTER VE {0x0413, 0xC3}, // CYRILLIC CAPITAL LETTER GHE {0x0414, 0xC4}, // CYRILLIC CAPITAL LETTER DE {0x0415, 0xC5}, // CYRILLIC CAPITAL LETTER IE {0x0416, 0xC6}, // CYRILLIC CAPITAL LETTER ZHE {0x0417, 0xC7}, // CYRILLIC CAPITAL LETTER ZE {0x0418, 0xC8}, // CYRILLIC CAPITAL LETTER I {0x0419, 0xC9}, // CYRILLIC CAPITAL LETTER SHORT I {0x041A, 0xCA}, // CYRILLIC CAPITAL LETTER KA {0x041B, 0xCB}, // CYRILLIC CAPITAL LETTER EL {0x041C, 0xCC}, // CYRILLIC CAPITAL LETTER EM {0x041D, 0xCD}, // CYRILLIC CAPITAL LETTER EN {0x041E, 0xCE}, // CYRILLIC CAPITAL LETTER O {0x041F, 0xCF}, // CYRILLIC CAPITAL LETTER PE {0x0420, 0xD0}, // CYRILLIC CAPITAL LETTER ER {0x0421, 0xD1}, // CYRILLIC CAPITAL LETTER ES {0x0422, 0xD2}, // CYRILLIC CAPITAL LETTER TE {0x0423, 0xD3}, // CYRILLIC CAPITAL LETTER U {0x0424, 0xD4}, // CYRILLIC CAPITAL LETTER EF {0x0425, 0xD5}, // CYRILLIC CAPITAL LETTER HA {0x0426, 0xD6}, // CYRILLIC CAPITAL LETTER TSE {0x0427, 0xD7}, // CYRILLIC CAPITAL LETTER CHE {0x0428, 0xD8}, // CYRILLIC CAPITAL LETTER SHA {0x0429, 0xD9}, // CYRILLIC CAPITAL LETTER SHCHA {0x042A, 0xDA}, // CYRILLIC CAPITAL LETTER HARD SIGN {0x042B, 0xDB}, // CYRILLIC CAPITAL LETTER YERU {0x042C, 0xDC}, // CYRILLIC CAPITAL LETTER SOFT SIGN {0x042D, 0xDD}, // CYRILLIC CAPITAL LETTER E {0x042E, 0xDE}, // CYRILLIC CAPITAL LETTER YU {0x042F, 0xDF}, // CYRILLIC CAPITAL LETTER YA {0x0430, 0xE0}, // CYRILLIC SMALL LETTER A {0x0431, 0xE1}, // CYRILLIC SMALL LETTER BE {0x0432, 0xE2}, // CYRILLIC SMALL LETTER VE {0x0433, 0xE3}, // CYRILLIC SMALL LETTER GHE {0x0434, 0xE4}, // CYRILLIC SMALL LETTER DE {0x0435, 0xE5}, // CYRILLIC SMALL LETTER IE {0x0436, 0xE6}, // CYRILLIC SMALL LETTER ZHE {0x0437, 0xE7}, // CYRILLIC SMALL LETTER ZE {0x0438, 0xE8}, // CYRILLIC SMALL LETTER I {0x0439, 0xE9}, // CYRILLIC SMALL LETTER SHORT I {0x043A, 0xEA}, // CYRILLIC SMALL LETTER KA {0x043B, 0xEB}, // CYRILLIC SMALL LETTER EL {0x043C, 0xEC}, // CYRILLIC SMALL LETTER EM {0x043D, 0xED}, // CYRILLIC SMALL LETTER EN {0x043E, 0xEE}, // CYRILLIC SMALL LETTER O {0x043F, 0xEF}, // CYRILLIC SMALL LETTER PE {0x0440, 0xF0}, // CYRILLIC SMALL LETTER ER {0x0441, 0xF1}, // CYRILLIC SMALL LETTER ES {0x0442, 0xF2}, // CYRILLIC SMALL LETTER TE {0x0443, 0xF3}, // CYRILLIC SMALL LETTER U {0x0444, 0xF4}, // CYRILLIC SMALL LETTER EF {0x0445, 0xF5}, // CYRILLIC SMALL LETTER HA {0x0446, 0xF6}, // CYRILLIC SMALL LETTER TSE {0x0447, 0xF7}, // CYRILLIC SMALL LETTER CHE {0x0448, 0xF8}, // CYRILLIC SMALL LETTER SHA {0x0449, 0xF9}, // CYRILLIC SMALL LETTER SHCHA {0x044A, 0xFA}, // CYRILLIC SMALL LETTER HARD SIGN {0x044B, 0xFB}, // CYRILLIC SMALL LETTER YERU {0x044C, 0xFC}, // CYRILLIC SMALL LETTER SOFT SIGN {0x044D, 0xFD}, // CYRILLIC SMALL LETTER E {0x044E, 0xFE}, // CYRILLIC SMALL LETTER YU {0x044F, 0xFF}, // CYRILLIC SMALL LETTER YA { u'\n', '\n' }, // Keep newline character { u'\r', '\r' }, // Keep carriage return character }; */ std::map UTF16_to_CP1251 = create_UTF16_to_CP1251(); UTF16TEXT koi8r_to_utf16[256] = { // От 0x00 до 0x7F: Соответствуют символам ASCII 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, // От 0x80 до 0xFF: Символы KOI8-R 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, // Символы диапазона 0x80-0x87 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, // Символы диапазона 0x88-0x8F 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, // Символы диапазона 0x90-0x97 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, // Символы диапазона 0x98-0x9F 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, // Символы диапазона 0xA0-0xA7 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, // Символы диапазона 0xA8-0xAF 0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, // Символы диапазона 0xB0-0xB7 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9, // Символы диапазона 0xB8-0xBF 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, // Символы диапазона 0xC0-0xC7 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, // Символы диапазона 0xC8-0xCF 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, // Символы диапазона 0xD0-0xD7 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, // Символы диапазона 0xD8-0xDF 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, // Символы диапазона 0xE0-0xE7 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, // Символы диапазона 0xE8-0xEF 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, // Символы диапазона 0xF0-0xF7 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A // Символы диапазона 0xF8-0xFF }; //ok UTF16TEXT* CTextConverter::ConvertFromCP1251ToUTF16 (const char* cp1251Text) { size_t len = strlen (cp1251Text); // UTF16TEXT* utf16Text = new UTF16TEXT[2 * len + 1]; // Максимальная длина UTF-16 символа - 2 байта, учитываем нулевой символ UTF16TEXT* utf16Text = new UTF16TEXT[len + 1]; // Максимальная длина UTF-16 символа - 2 байта, учитываем нулевой символ UTF16TEXT* p = utf16Text; while (*cp1251Text) { unsigned char c = *cp1251Text++; *p++ = cp1251_to_utf16[c]; // Преобразование всех символов } // *p = u'\0'; *p = '\0'; return utf16Text; } //ok, но дополнить таблицу char* CTextConverter::ConvertFromUTF16ToCP1251 (const UTF16TEXT *s) { std::string cp1251Text; // for (const char16_t *p = s; *p != u'\0'; ++p) for (const UTF16TEXT *p = s; *p != '\0'; ++p) { // auto it = UTF16_to_CP1251.find (*p); //if (*p <= 0x7F) // cp1251Text += *p; std::map::iterator it = UTF16_to_CP1251.find(*p); if (it != UTF16_to_CP1251.end()) cp1251Text += it->second; else // If character not found in mapping table, add placeholder or handle differently cp1251Text += '?'; // Placeholder for unknown characters } char *result = new char[cp1251Text.length() + 1]; //std::strcpy (result, cp1251Text.c_str()); std::strncpy (result, cp1251Text.c_str(), cp1251Text.size()); result [cp1251Text.size()] = '\0'; return result; } //OK UTF16TEXT* CTextConverter::ConvertFromKOI8RToUTF16(const char* koi8rText) { size_t len = strlen(koi8rText); UTF16TEXT* utf16Text = new UTF16TEXT[len + 1]; // Учитываем нулевой символ UTF16TEXT* p = utf16Text; while (*koi8rText) { unsigned char c = *koi8rText++; *p++ = koi8r_to_utf16[c]; } // *p = u'\0'; *p = '\0'; return utf16Text; } /* std::map unicode_to_koi8r = { // Кириллица {0x2500, 0x80}, {0x2502, 0x81}, {0x250C, 0x82}, {0x2510, 0x83}, {0x2514, 0x84}, {0x2518, 0x85}, {0x251C, 0x86}, {0x2524, 0x87}, {0x252C, 0x88}, {0x2534, 0x89}, {0x253C, 0x8A}, {0x2580, 0x8B}, {0x2584, 0x8C}, {0x2588, 0x8D}, {0x258C, 0x8E}, {0x2590, 0x8F}, //второй ряд {0x2591, 0x90}, {0x2592, 0x91}, {0x2593, 0x92}, {0x2320, 0x93}, {0x25A0, 0x94}, {0x2219, 0x95}, {0x221A, 0x96}, {0x2248, 0x97}, {0x2264, 0x98}, {0x2265, 0x99}, {0xA0, 0x9A}, {0x2321, 0x9B}, {0xB0, 0x9C}, {0xB2, 0x9D}, {0xB7, 0x9E}, {0xF7, 0x9F}, //третий ряд {0x2550, 0xA0}, {0x2551, 0xA1}, {0x2552, 0xA2}, {0x451, 0xA3}, {0x2553, 0xA4}, {0x2554, 0xA5}, {0x2555, 0xA6}, {0x2556, 0xA7}, {0x2557, 0xA8}, {0x2558, 0xA9}, {0x2559, 0xAA}, {0x255A, 0xAB}, {0x255B, 0xAC}, {0x255C, 0xAD}, {0x255D, 0xAE}, {0x255E, 0xAF}, //четвертый ряд {0x255F, 0xB0}, {0x2560, 0xB1}, {0x2561, 0xB2}, {0x401, 0xB3}, {0x2562, 0xB4}, {0x2563, 0xB5}, {0x2564, 0xB6}, {0x2565, 0xB7}, {0x2566, 0xB8}, {0x2567, 0xB9}, {0x2568, 0xBA}, {0x2569, 0xBB}, {0x256A, 0xBC}, {0x256B, 0xBD}, {0x256C, 0xBE}, {0x25A9, 0xBF}, //пятый ряд {0x44E, 0xC0}, {0x430, 0xC1}, {0x431, 0xC2}, {0x446, 0xC3}, {0x434, 0xC4}, {0x435, 0xC5}, {0x444, 0xC6}, {0x433, 0xC7}, {0x445, 0xC8}, {0x438, 0xC9}, {0x439, 0xCA}, {0x43A, 0xCB}, {0x43B, 0xCC}, {0x43C, 0xCD}, {0x43D, 0xCE}, {0x43E, 0xCF}, //шестой ряд {0x43F, 0xD0}, {0x44F, 0xD1}, {0x440, 0xD2}, {0x441, 0xD3}, {0x442, 0xD4}, {0x443, 0xD5}, {0x436, 0xD6}, {0x432, 0xD7}, {0x44C, 0xD8}, {0x44B, 0xD9}, {0x437, 0xDA}, {0x448, 0xDB}, {0x44D, 0xDC}, {0x449, 0xDD}, {0x447, 0xDE}, {0x44A, 0xDF}, //седьмой ряд {0x42E, 0xE0}, {0x410, 0xE1}, {0x411, 0xE2}, {0x426, 0xE3}, {0x414, 0xE4}, {0x415, 0xE5}, {0x424, 0xE6}, {0x413, 0xE7}, {0x425, 0xE8}, {0x418, 0xE9}, {0x419, 0xEA}, {0x41A, 0xEB}, {0x41B, 0xEC}, {0x41C, 0xED}, {0x41D, 0xEE}, {0x41E, 0xEF}, //восьмой ряд {0x41F, 0xF0}, {0x42F, 0xF1}, {0x420, 0xF2}, {0x421, 0xF3}, {0x422, 0xF4}, {0x423, 0xF5}, {0x416, 0xF6}, {0x412, 0xF7}, {0x42C, 0xF8}, {0x42B, 0xF9}, {0x417, 0xFA}, {0x428, 0xFB}, {0x42D, 0xFC}, {0x429, 0xFD}, {0x427, 0xFE}, {0x42A, 0xEF}, }; */ std::map create_unicode_to_koi8r() { std::map unicode_to_koi8r; unicode_to_koi8r[0x2500] = 0x80; unicode_to_koi8r[0x2502] = 0x81; unicode_to_koi8r[0x250C] = 0x82; unicode_to_koi8r[0x2510] = 0x83; unicode_to_koi8r[0x2514] = 0x84; unicode_to_koi8r[0x2518] = 0x85; unicode_to_koi8r[0x251C] = 0x86; unicode_to_koi8r[0x2524] = 0x87; unicode_to_koi8r[0x252C] = 0x88; unicode_to_koi8r[0x2534] = 0x89; unicode_to_koi8r[0x253C] = 0x8A; unicode_to_koi8r[0x2580] = 0x8B; unicode_to_koi8r[0x2584] = 0x8C; unicode_to_koi8r[0x2588] = 0x8D; unicode_to_koi8r[0x258C] = 0x8E; unicode_to_koi8r[0x2590] = 0x8F; // Второй ряд unicode_to_koi8r[0x2591] = 0x90; unicode_to_koi8r[0x2592] = 0x91; unicode_to_koi8r[0x2593] = 0x92; unicode_to_koi8r[0x2320] = 0x93; unicode_to_koi8r[0x25A0] = 0x94; unicode_to_koi8r[0x2219] = 0x95; unicode_to_koi8r[0x221A] = 0x96; unicode_to_koi8r[0x2248] = 0x97; unicode_to_koi8r[0x2264] = 0x98; unicode_to_koi8r[0x2265] = 0x99; unicode_to_koi8r[0xA0] = 0x9A; unicode_to_koi8r[0x2321] = 0x9B; unicode_to_koi8r[0xB0] = 0x9C; unicode_to_koi8r[0xB2] = 0x9D; unicode_to_koi8r[0xB7] = 0x9E; unicode_to_koi8r[0xF7] = 0x9F; // Третий ряд unicode_to_koi8r[0x2550] = 0xA0; unicode_to_koi8r[0x2551] = 0xA1; unicode_to_koi8r[0x2552] = 0xA2; unicode_to_koi8r[0x451] = 0xA3; unicode_to_koi8r[0x2553] = 0xA4; unicode_to_koi8r[0x2554] = 0xA5; unicode_to_koi8r[0x2555] = 0xA6; unicode_to_koi8r[0x2556] = 0xA7; unicode_to_koi8r[0x2557] = 0xA8; unicode_to_koi8r[0x2558] = 0xA9; unicode_to_koi8r[0x2559] = 0xAA; unicode_to_koi8r[0x255A] = 0xAB; unicode_to_koi8r[0x255B] = 0xAC; unicode_to_koi8r[0x255C] = 0xAD; unicode_to_koi8r[0x255D] = 0xAE; unicode_to_koi8r[0x255E] = 0xAF; // Четвертый ряд unicode_to_koi8r[0x255F] = 0xB0; unicode_to_koi8r[0x2560] = 0xB1; unicode_to_koi8r[0x2561] = 0xB2; unicode_to_koi8r[0x401] = 0xB3; unicode_to_koi8r[0x2562] = 0xB4; unicode_to_koi8r[0x2563] = 0xB5; unicode_to_koi8r[0x2564] = 0xB6; unicode_to_koi8r[0x2565] = 0xB7; unicode_to_koi8r[0x2566] = 0xB8; unicode_to_koi8r[0x2567] = 0xB9; unicode_to_koi8r[0x2568] = 0xBA; unicode_to_koi8r[0x2569] = 0xBB; unicode_to_koi8r[0x256A] = 0xBC; unicode_to_koi8r[0x256B] = 0xBD; unicode_to_koi8r[0x256C] = 0xBE; unicode_to_koi8r[0x25A9] = 0xBF; // Пятый ряд unicode_to_koi8r[0x44E] = 0xC0; unicode_to_koi8r[0x430] = 0xC1; unicode_to_koi8r[0x431] = 0xC2; unicode_to_koi8r[0x446] = 0xC3; unicode_to_koi8r[0x434] = 0xC4; unicode_to_koi8r[0x435] = 0xC5; unicode_to_koi8r[0x444] = 0xC6; unicode_to_koi8r[0x433] = 0xC7; unicode_to_koi8r[0x445] = 0xC8; unicode_to_koi8r[0x438] = 0xC9; unicode_to_koi8r[0x439] = 0xCA; unicode_to_koi8r[0x43A] = 0xCB; unicode_to_koi8r[0x43B] = 0xCC; unicode_to_koi8r[0x43C] = 0xCD; unicode_to_koi8r[0x43D] = 0xCE; unicode_to_koi8r[0x43E] = 0xCF; // Шестой ряд unicode_to_koi8r[0x43F] = 0xD0; unicode_to_koi8r[0x44F] = 0xD1; unicode_to_koi8r[0x440] = 0xD2; unicode_to_koi8r[0x441] = 0xD3; unicode_to_koi8r[0x442] = 0xD4; unicode_to_koi8r[0x443] = 0xD5; unicode_to_koi8r[0x436] = 0xD6; unicode_to_koi8r[0x432] = 0xD7; unicode_to_koi8r[0x44C] = 0xD8; unicode_to_koi8r[0x44B] = 0xD9; unicode_to_koi8r[0x437] = 0xDA; unicode_to_koi8r[0x448] = 0xDB; unicode_to_koi8r[0x44D] = 0xDC; unicode_to_koi8r[0x449] = 0xDD; unicode_to_koi8r[0x447] = 0xDE; unicode_to_koi8r[0x44A] = 0xDF; // Седьмой ряд unicode_to_koi8r[0x42E] = 0xE0; unicode_to_koi8r[0x410] = 0xE1; unicode_to_koi8r[0x411] = 0xE2; unicode_to_koi8r[0x426] = 0xE3; unicode_to_koi8r[0x414] = 0xE4; unicode_to_koi8r[0x415] = 0xE5; unicode_to_koi8r[0x424] = 0xE6; unicode_to_koi8r[0x413] = 0xE7; unicode_to_koi8r[0x425] = 0xE8; unicode_to_koi8r[0x418] = 0xE9; unicode_to_koi8r[0x419] = 0xEA; unicode_to_koi8r[0x41A] = 0xEB; unicode_to_koi8r[0x41B] = 0xEC; unicode_to_koi8r[0x41C] = 0xED; unicode_to_koi8r[0x41D] = 0xEE; unicode_to_koi8r[0x41E] = 0xEF; // Восьмой ряд unicode_to_koi8r[0x41F] = 0xF0; unicode_to_koi8r[0x42F] = 0xF1; unicode_to_koi8r[0x420] = 0xF2; unicode_to_koi8r[0x421] = 0xF3; unicode_to_koi8r[0x422] = 0xF4; unicode_to_koi8r[0x423] = 0xF5; unicode_to_koi8r[0x416] = 0xF6; unicode_to_koi8r[0x412] = 0xF7; unicode_to_koi8r[0x42C] = 0xF8; unicode_to_koi8r[0x42B] = 0xF9; unicode_to_koi8r[0x417] = 0xFA; unicode_to_koi8r[0x428] = 0xFB; unicode_to_koi8r[0x42D] = 0xFC; unicode_to_koi8r[0x429] = 0xFD; unicode_to_koi8r[0x427] = 0xFE; unicode_to_koi8r[0x42A] = 0xEF; return unicode_to_koi8r; }; std::map unicode_to_koi8r = create_unicode_to_koi8r(); //ок char* CTextConverter::ConvertFromUTF16ToKOI8R(const UTF16TEXT* utf16Text) { size_t len = 0; const UTF16TEXT* p = utf16Text; while (*p++) { ++len; } std::string s; for (int i = 0; i < len; i++) { UTF16TEXT uc = utf16Text[i]; char c; // if (uc <= 0x7F) if (uc <= 0x80) c = (char) uc; else c = unicode_to_koi8r [uc]; s += c; } //char *result = new char[s.length() + 1]; char *result = new char[s.length() + 1]; std::strncpy (result, s.c_str(), s.size()); result[len] = '\0'; return result; } /* //NOT NEEDED UTF16TEXT* CTextConverter::ConvertFromUTF8ToUTF16(const char* utf8Text) { size_t len = strlen(utf8Text); UTF16TEXT* utf16Text = new UTF16TEXT[len + 1]; // Учитываем нулевой символ UTF16TEXT* p = utf16Text; while (*utf8Text) { unsigned char c = *utf8Text++; if (c < 0x80) { *p++ = c; } else if ((c & 0xE0) == 0xC0) { unsigned char c2 = *utf8Text++; *p++ = ((c & 0x1F) << 6) | (c2 & 0x3F); } else if ((c & 0xF0) == 0xE0) { unsigned char c2 = *utf8Text++; unsigned char c3 = *utf8Text++; *p++ = ((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); } else { // Некорректный символ, заменяем на '?' *p++ = u'?'; } } *p = u'\0'; return utf16Text; } */ //OK const UTF16TEXT CP866_to_UTF16[] = { /* 0x00 */ 0x0000, /* NUL */ /* 0x01 */ 0x0001, /* SOH */ /* 0x02 */ 0x0002, /* STX */ /* 0x03 */ 0x0003, /* ETX */ /* 0x04 */ 0x0004, /* EOT */ /* 0x05 */ 0x0005, /* ENQ */ /* 0x06 */ 0x0006, /* ACK */ /* 0x07 */ 0x0007, /* BEL */ /* 0x08 */ 0x0008, /* BS */ /* 0x09 */ 0x0009, /* TAB */ /* 0x0A */ 0x000A, /* LF */ /* 0x0B */ 0x000B, /* VT */ /* 0x0C */ 0x000C, /* FF */ /* 0x0D */ 0x000D, /* CR */ /* 0x0E */ 0x000E, /* SO */ /* 0x0F */ 0x000F, /* SI */ /* 0x10 */ 0x0010, /* DLE */ /* 0x11 */ 0x0011, /* DC1 */ /* 0x12 */ 0x0012, /* DC2 */ /* 0x13 */ 0x0013, /* DC3 */ /* 0x14 */ 0x0014, /* DC4 */ /* 0x15 */ 0x0015, /* NAK */ /* 0x16 */ 0x0016, /* SYN */ /* 0x17 */ 0x0017, /* ETB */ /* 0x18 */ 0x0018, /* CAN */ /* 0x19 */ 0x0019, /* EM */ /* 0x1A */ 0x001A, /* SUB */ /* 0x1B */ 0x001B, /* ESC */ /* 0x1C */ 0x001C, /* FS */ /* 0x1D */ 0x001D, /* GS */ /* 0x1E */ 0x001E, /* RS */ /* 0x1F */ 0x001F, /* US */ /* 0x20 */ 0x0020, /* Space */ /* 0x21 */ 0x0021, /* ! */ /* 0x22 */ 0x0022, /* " */ /* 0x23 */ 0x0023, /* # */ /* 0x24 */ 0x0024, /* $ */ /* 0x25 */ 0x0025, /* % */ /* 0x26 */ 0x0026, /* & */ /* 0x27 */ 0x0027, /* ' */ /* 0x28 */ 0x0028, /* ( */ /* 0x29 */ 0x0029, /* ) */ /* 0x2A */ 0x002A, /* * */ /* 0x2B */ 0x002B, /* + */ /* 0x2C */ 0x002C, /* , */ /* 0x2D */ 0x002D, /* - */ /* 0x2E */ 0x002E, /* . */ /* 0x2F */ 0x002F, /* / */ /* 0x30 */ 0x0030, /* 0 */ /* 0x31 */ 0x0031, /* 1 */ /* 0x32 */ 0x0032, /* 2 */ /* 0x33 */ 0x0033, /* 3 */ /* 0x34 */ 0x0034, /* 4 */ /* 0x35 */ 0x0035, /* 5 */ /* 0x36 */ 0x0036, /* 6 */ /* 0x37 */ 0x0037, /* 7 */ /* 0x38 */ 0x0038, /* 8 */ /* 0x39 */ 0x0039, /* 9 */ /* 0x3A */ 0x003A, /* : */ /* 0x3B */ 0x003B, /* ; */ /* 0x3C */ 0x003C, /* < */ /* 0x3D */ 0x003D, /* = */ /* 0x3E */ 0x003E, /* > */ /* 0x3F */ 0x003F, /* ? */ /* 0x40 */ 0x0040, /* @ */ /* 0x41 */ 0x0041, /* A */ /* 0x42 */ 0x0042, /* B */ /* 0x43 */ 0x0043, /* C */ /* 0x44 */ 0x0044, /* D */ /* 0x45 */ 0x0045, /* E */ /* 0x46 */ 0x0046, /* F */ /* 0x47 */ 0x0047, /* G */ /* 0x48 */ 0x0048, /* H */ /* 0x49 */ 0x0049, /* I */ /* 0x4A */ 0x004A, /* J */ /* 0x4B */ 0x004B, /* K */ /* 0x4C */ 0x004C, /* L */ /* 0x4D */ 0x004D, /* M */ /* 0x4E */ 0x004E, /* N */ /* 0x4F */ 0x004F, /* O */ /* 0x50 */ 0x0050, /* P */ /* 0x51 */ 0x0051, /* Q */ /* 0x52 */ 0x0052, /* R */ /* 0x53 */ 0x0053, /* S */ /* 0x54 */ 0x0054, /* T */ /* 0x55 */ 0x0055, /* U */ /* 0x56 */ 0x0056, /* V */ /* 0x57 */ 0x0057, /* W */ /* 0x58 */ 0x0058, /* X */ /* 0x59 */ 0x0059, /* Y */ /* 0x5A */ 0x005A, /* Z */ /* 0x5B */ 0x005B, /* [ */ /* 0x5C */ 0x005C, /* \ */ /* 0x5D */ 0x005D, /* ] */ /* 0x5E */ 0x005E, /* ^ */ /* 0x5F */ 0x005F, /* _ */ /* 0x60 */ 0x0060, /* ` */ /* 0x61 */ 0x0061, /* a */ /* 0x62 */ 0x0062, /* b */ /* 0x63 */ 0x0063, /* c */ /* 0x64 */ 0x0064, /* d */ /* 0x65 */ 0x0065, /* e */ /* 0x66 */ 0x0066, /* f */ /* 0x67 */ 0x0067, /* g */ /* 0x68 */ 0x0068, /* h */ /* 0x69 */ 0x0069, /* i */ /* 0x6A */ 0x006A, /* j */ /* 0x6B */ 0x006B, /* k */ /* 0x6C */ 0x006C, /* l */ /* 0x6D */ 0x006D, /* m */ /* 0x6E */ 0x006E, /* n */ /* 0x6F */ 0x006F, /* o */ /* 0x70 */ 0x0070, /* p */ /* 0x71 */ 0x0071, /* q */ /* 0x72 */ 0x0072, /* r */ /* 0x73 */ 0x0073, /* s */ /* 0x74 */ 0x0074, /* t */ /* 0x75 */ 0x0075, /* u */ /* 0x76 */ 0x0076, /* v */ /* 0x77 */ 0x0077, /* w */ /* 0x78 */ 0x0078, /* x */ /* 0x79 */ 0x0079, /* y */ /* 0x7A */ 0x007A, /* z */ /* 0x7B */ 0x007B, /* { */ /* 0x7C */ 0x007C, /* | */ /* 0x7D */ 0x007D, /* } */ /* 0x7E */ 0x007E, /* ~ */ /* 0x7F */ 0x007F, /* DEL */ /* 0x80 */ 0x0410, /* А */ /* 0x81 */ 0x0411, /* Б */ /* 0x82 */ 0x0412, /* В */ /* 0x83 */ 0x0413, /* Г */ /* 0x84 */ 0x0414, /* Д */ /* 0x85 */ 0x0415, /* Е */ /* 0x86 */ 0x0416, /* Ж */ /* 0x87 */ 0x0417, /* З */ /* 0x88 */ 0x0418, /* И */ /* 0x89 */ 0x0419, /* Й */ /* 0x8A */ 0x041A, /* К */ /* 0x8B */ 0x041B, /* Л */ /* 0x8C */ 0x041C, /* М */ /* 0x8D */ 0x041D, /* Н */ /* 0x8E */ 0x041E, /* О */ /* 0x8F */ 0x041F, /* П */ /* 0x90 */ 0x0420, /* Р */ /* 0x91 */ 0x0421, /* С */ /* 0x92 */ 0x0422, /* Т */ /* 0x93 */ 0x0423, /* У */ /* 0x94 */ 0x0424, /* Ф */ /* 0x95 */ 0x0425, /* Х */ /* 0x96 */ 0x0426, /* Ц */ /* 0x97 */ 0x0427, /* Ч */ /* 0x98 */ 0x0428, /* Ш */ /* 0x99 */ 0x0429, /* Щ */ /* 0x9A */ 0x042A, /* Ъ */ /* 0x9B */ 0x042B, /* Ы */ /* 0x9C */ 0x042C, /* Ь */ /* 0x9D */ 0x042D, /* Э */ /* 0x9E */ 0x042E, /* Ю */ /* 0x9F */ 0x042F, /* Я */ /* 0xA0 */ 0x0430, /* а */ /* 0xA1 */ 0x0431, /* б */ /* 0xA2 */ 0x0432, /* в */ /* 0xA3 */ 0x0433, /* г */ /* 0xA4 */ 0x0434, /* д */ /* 0xA5 */ 0x0435, /* е */ /* 0xA6 */ 0x0436, /* ж */ /* 0xA7 */ 0x0437, /* з */ /* 0xA8 */ 0x0438, /* и */ /* 0xA9 */ 0x0439, /* й */ /* 0xAA */ 0x043A, /* к */ /* 0xAB */ 0x043B, /* л */ /* 0xAC */ 0x043C, /* м */ /* 0xAD */ 0x043D, /* н */ /* 0xAE */ 0x043E, /* о */ /* 0xAF */ 0x043F, /* п */ /* 0xB0 */ 0x2591, /* ░ */ /* 0xB1 */ 0x2592, /* ▒ */ /* 0xB2 */ 0x2593, /* ▓ */ /* 0xB3 */ 0x2502, /* │ */ /* 0xB4 */ 0x2524, /* ┤ */ /* 0xB5 */ 0x2561, /* ╡ */ /* 0xB6 */ 0x2562, /* ╢ */ /* 0xB7 */ 0x2556, /* ╖ */ /* 0xB8 */ 0x2555, /* ╕ */ /* 0xB9 */ 0x2563, /* ╣ */ /* 0xBA */ 0x2551, /* ║ */ /* 0xBB */ 0x2557, /* ╗ */ /* 0xBC */ 0x255D, /* ╝ */ /* 0xBD */ 0x255C, /* ╜ */ /* 0xBE */ 0x255B, /* ╛ */ /* 0xBF */ 0x2510, /* ┐ */ /* 0xC0 */ 0x2514, /* └ */ /* 0xC1 */ 0x2534, /* ┴ */ /* 0xC2 */ 0x252C, /* ┬ */ /* 0xC3 */ 0x251C, /* ├ */ /* 0xC4 */ 0x2500, /* ─ */ /* 0xC5 */ 0x253C, /* ┼ */ /* 0xC6 */ 0x255E, /* ╞ */ /* 0xC7 */ 0x255F, /* ╟ */ /* 0xC8 */ 0x255A, /* ╚ */ /* 0xC9 */ 0x2554, /* ╔ */ /* 0xCA */ 0x2569, /* ╩ */ /* 0xCB */ 0x2566, /* ╦ */ /* 0xCC */ 0x2560, /* ╠ */ /* 0xCD */ 0x2550, /* ═ */ /* 0xCE */ 0x256C, /* ╬ */ /* 0xCF */ 0x2567, /* ╧ */ /* 0xD0 */ 0x2568, /* ╨ */ /* 0xD1 */ 0x2564, /* ╤ */ /* 0xD2 */ 0x2565, /* ╥ */ /* 0xD3 */ 0x2559, /* ╙ */ /* 0xD4 */ 0x2558, /* ╘ */ /* 0xD5 */ 0x2552, /* ╒ */ /* 0xD6 */ 0x2553, /* ╓ */ /* 0xD7 */ 0x256B, /* ╫ */ /* 0xD8 */ 0x256A, /* ╪ */ /* 0xD9 */ 0x2518, /* ┘ */ /* 0xDA */ 0x250C, /* ┌ */ /* 0xDB */ 0x2588, /* █ */ /* 0xDC */ 0x2584, /* ▄ */ /* 0xDD */ 0x258C, /* ▌ */ /* 0xDE */ 0x2590, /* ▐ */ /* 0xDF */ 0x2580, /* ▀ */ /* 0xE0 */ 0x440, /* р */ /* 0xE1 */ 0x441, /* с */ /* 0xE2 */ 0x442, /* т */ /* 0xE3 */ 0x443, /* у */ /* 0xE4 */ 0x440, /* ф */ /* 0xE5 */ 0x445, /* х */ /* 0xE6 */ 0x446, /* ц */ /* 0xE7 */ 0x447, /* ч */ /* 0xE8 */ 0x448, /* ш */ /* 0xE8 */ 0x449, /* щ */ /* 0xE9 */ 0x44A, /* ъ */ /* 0xEA */ 0x44B, /* ы */ /* 0xEB */ 0x44C, /* ь */ /* 0xEC */ 0x44D, /* э */ /* 0xED */ 0x44E, /* ю */ /* 0xEE */ 0x44F, /* я */ /* 0xEF */ 0x0401, /* Ё */ /* 0xF0 */ 0x0451, /* ё */ /* 0xF1 */ 0x0404, /* Є */ /* 0xF2 */ 0x0454, /* є */ /* 0xF3 */ 0x0407, /* Ї */ /* 0xF4 */ 0x0457, /* ї */ /* 0xF5 */ 0x040E, /* Ў */ /* 0xF6 */ 0x045E, /* ў */ /* 0xF8 */ 0x00B0, /* ° */ /* 0xF9 */ 0x2219, /* · */ /* 0xFA */ 0x00B7, /* · */ /* 0xFB */ 0x221A, /* √ */ /* 0xFC */ 0x2116, /* № */ /* 0xFD */ 0x00A4, /* ¤ */ /* 0xFE */ 0x25A0, /* ■ */ /* 0xFF */ 0x00A0 /* */ }; //ok UTF16TEXT* CTextConverter::ConvertFromDOS866ToUTF16(const char* dos866Text) { size_t len = strlen (dos866Text); UTF16TEXT* utf16Text = new UTF16TEXT[len + 1]; // +1 for null terminator utf16Text[len] = 0; // Null-terminate the string for (size_t i = 0; i < len; i++) { unsigned char from = (unsigned char) dos866Text [i]; UTF16TEXT to = CP866_to_UTF16[from]; utf16Text[i] = to; //unsigned char c = static_cast(dos866Text[i]); // UTF16TEXT c = dos866Text[i]; // utf16Text[i] = CP866_to_UTF16[c]; } return utf16Text; } QStringList CTextConverter::get_charsets() { QStringList result; result.append ("UTF-8"); result.append ("UTF-16"); result.append ("CP-1251"); result.append ("CP-866"); result.append ("KOI8-R"); return result; } /* std::map UTF16_to_CP866 = { {0x0000, 0x00}, // NUL {0x0001, 0x01}, // SOH {0x0002, 0x02}, // STX {0x0003, 0x03}, // ETX {0x0004, 0x04}, // EOT {0x0005, 0x05}, // ENQ {0x0006, 0x06}, // ACK {0x0007, 0x07}, // BEL {0x0008, 0x08}, // BS {0x0009, 0x09}, // TAB {0x000A, 0x0A}, // LF {0x000B, 0x0B}, // VT {0x000C, 0x0C}, // FF {0x000D, 0x0D}, // CR {0x000E, 0x0E}, // SO {0x000F, 0x0F}, // SI {0x0010, 0x10}, // DLE {0x0011, 0x11}, // DC1 {0x0012, 0x12}, // DC2 {0x0013, 0x13}, // DC3 {0x0014, 0x14}, // DC4 {0x0015, 0x15}, // NAK {0x0016, 0x16}, // SYN {0x0017, 0x17}, // ETB {0x0018, 0x18}, // CAN {0x0019, 0x19}, // EM {0x001A, 0x1A}, // SUB {0x001B, 0x1B}, // ESC {0x001C, 0x1C}, // FS {0x001D, 0x1D}, // GS {0x001E, 0x1E}, // RS {0x001F, 0x1F}, // US {0x0020, 0x20}, // Space {0x0021, 0x21}, // ! {0x0022, 0x22}, // " {0x0023, 0x23}, // # {0x0024, 0x24}, // $ {0x0025, 0x25}, // % {0x0026, 0x26}, // & {0x0027, 0x27}, // ' {0x0028, 0x28}, // ( {0x0029, 0x29}, // ) {0x002A, 0x2A}, // * {0x002B, 0x2B}, // + {0x002C, 0x2C}, // , {0x002D, 0x2D}, // - {0x002E, 0x2E}, // . {0x002F, 0x2F}, // / {0x0030, 0x30}, // 0 {0x0031, 0x31}, // 1 {0x0032, 0x32}, // 2 {0x0033, 0x33}, // 3 {0x0034, 0x34}, // 4 {0x0035, 0x35}, // 5 {0x0036, 0x36}, // 6 {0x0037, 0x37}, // 7 {0x0038, 0x38}, // 8 {0x0039, 0x39}, // 9 {0x003A, 0x3A}, // : {0x003B, 0x3B}, // ; {0x003C, 0x3C}, // < {0x003D, 0x3D}, // = {0x003E, 0x3E}, // > {0x003F, 0x3F}, // ? {0x0040, 0x40}, // @ {0x0041, 0x41}, // A {0x0042, 0x42}, // B {0x0043, 0x43}, // C {0x0044, 0x44}, // D {0x0045, 0x45}, // E {0x0046, 0x46}, // F {0x0047, 0x47}, // G {0x0048, 0x48}, // H {0x0049, 0x49}, // I {0x004A, 0x4A}, // J {0x004B, 0x4B}, // K {0x004C, 0x4C}, // L {0x004D, 0x4D}, // M {0x004E, 0x4E}, // N {0x004F, 0x4F}, // O {0x0050, 0x50}, // P {0x0051, 0x51}, // Q {0x0052, 0x52}, // R {0x0053, 0x53}, // S {0x0054, 0x54}, // T {0x0055, 0x55}, // U {0x0056, 0x56}, // V {0x0057, 0x57}, // W {0x0058, 0x58}, // X {0x0059, 0x59}, // Y {0x005A, 0x5A}, // Z {0x005B, 0x5B}, // [ {0x005C, 0x5C}, // slash {0x005D, 0x5D}, // ] {0x005E, 0x5E}, // ^ {0x005F, 0x5F}, // _ {0x0060, 0x60}, // ` {0x0061, 0x61}, // a {0x0062, 0x62}, // b {0x0063, 0x63}, // c {0x0064, 0x64}, // d {0x0065, 0x65}, // e {0x0066, 0x66}, // f {0x0067, 0x67}, // g {0x0068, 0x68}, // h {0x0069, 0x69}, // i {0x006A, 0x6A}, // j {0x006B, 0x6B}, // k {0x006C, 0x6C}, // l {0x006D, 0x6D}, // m {0x006E, 0x6E}, // n {0x006F, 0x6F}, // o {0x0070, 0x70}, // p {0x0071, 0x71}, // q {0x0072, 0x72}, // r {0x0073, 0x73}, // s {0x0074, 0x74}, // t {0x0075, 0x75}, // u {0x0076, 0x76}, // v {0x0077, 0x77}, // w {0x0078, 0x78}, // x {0x0079, 0x79}, // y {0x007A, 0x7A}, // z {0x007B, 0x7B}, // { {0x007C, 0x7C}, // | {0x007D, 0x7D}, // } {0x007E, 0x7E}, // ~ {0x007F, 0x7F}, // DEL {0x0410, 0x80}, // А {0x0411, 0x81}, // Б {0x0412, 0x82}, // В {0x0413, 0x83}, // Г {0x0414, 0x84}, // Д {0x0415, 0x85}, // Е {0x0416, 0x86}, // Ж {0x0417, 0x87}, // З {0x0418, 0x88}, // И {0x0419, 0x89}, // Й {0x041A, 0x8A}, // К {0x041B, 0x8B}, // Л {0x041C, 0x8C}, // М {0x041D, 0x8D}, // Н {0x041E, 0x8E}, // О {0x041F, 0x8F}, // П {0x0420, 0x90}, // Р {0x0421, 0x91}, // С {0x0422, 0x92}, // Т {0x0423, 0x93}, // У {0x0424, 0x94}, // Ф {0x0425, 0x95}, // Х {0x0426, 0x96}, // Ц {0x0427, 0x97}, // Ч {0x0428, 0x98}, // Ш {0x0429, 0x99}, // Щ {0x042A, 0x9A}, // Ъ {0x042B, 0x9B}, // Ы {0x042C, 0x9C}, // Ь {0x042D, 0x9D}, // Э {0x042E, 0x9E}, // Ю {0x042F, 0x9F}, // Я {0x0430, 0xA0}, // а {0x0431, 0xA1}, // б {0x0432, 0xA2}, // в {0x0433, 0xA3}, // г {0x0434, 0xA4}, // д {0x0435, 0xA5}, // е {0x0436, 0xA6}, // ж {0x0437, 0xA7}, // з {0x0438, 0xA8}, // и {0x0439, 0xA9}, // й {0x043A, 0xAA}, // к {0x043B, 0xAB}, // л {0x043C, 0xAC}, // м {0x043D, 0xAD}, // н {0x043E, 0xAE}, // о {0x043F, 0xAF}, // п {0x2591, 0xB0}, // ░ {0x2592, 0xB1}, // ▒ {0x2593, 0xB2}, // ▓ {0x2502, 0xB3}, // │ {0x2524, 0xB4}, // ┤ {0x2561, 0xB5}, // ╡ {0x2562, 0xB6}, // ╢ {0x2556, 0xB7}, // ╖ {0x2555, 0xB8}, // ╕ {0x2563, 0xB9}, // ╣ {0x2551, 0xBA}, // ║ {0x2557, 0xBB}, // ╗ {0x255D, 0xBC}, // ╝ {0x255C, 0xBD}, // ╜ {0x255B, 0xBE}, // ╛ {0x2510, 0xBF}, // ┐ {0x2514, 0xC0}, // └ {0x2534, 0xC1}, // ┴ {0x252C, 0xC2}, // ┬ {0x251C, 0xC3}, // ├ {0x2500, 0xC4}, // ─ {0x253C, 0xC5}, // ┼ {0x255E, 0xC6}, // ╞ {0x255F, 0xC7}, // ╟ {0x255A, 0xC8}, // ╚ {0x2554, 0xC9}, // ╔ {0x2569, 0xCA}, // ╩ {0x2566, 0xCB}, // ╦ {0x2560, 0xCC}, // ╠ {0x2550, 0xCD}, // ═ {0x256C, 0xCE}, // ╬ {0x2567, 0xCF}, // ╧ {0x2568, 0xD0}, // ╨ {0x2564, 0xD1}, // ╤ {0x2565, 0xD2}, // ╥ {0x2559, 0xD3}, // ╙ {0x2558, 0xD4}, // ╘ {0x2552, 0xD5}, // ╒ {0x2553, 0xD6}, // ╓ {0x256B, 0xD7}, // ╫ {0x256A, 0xD8}, // ╪ {0x2518, 0xD9}, // ┘ {0x250C, 0xDA}, // ┌ {0x2588, 0xDB}, // █ {0x2584, 0xDC}, // ▄ {0x258C, 0xDD}, // ▌ {0x2590, 0xDE}, // ▐ {0x2580, 0xDF}, // ▀ {0x0440, 0xE0}, // р {0x0441, 0xE1}, // с {0x0442, 0xE2}, // т {0x0443, 0xE3}, // у {0x0444, 0xE4}, // ф {0x0445, 0xE5}, // х {0x0446, 0xE6}, // ц {0x0447, 0xE7}, // ч {0x0448, 0xE8}, // ш {0x0449, 0xE9}, // щ {0x044A, 0xEA}, // ъ {0x044B, 0xEB}, // ы {0x044C, 0xEC}, // ь {0x044D, 0xED}, // э {0x044E, 0xEE}, // ю {0x044F, 0xEF}, // я {0x0401, 0xF0}, // Ё {0x0451, 0xF1}, // ё {0x0404, 0xF2}, // Є {0x0454, 0xF3}, // є {0x0407, 0xF4}, // Ї {0x0457, 0xF5}, // ї {0x040E, 0xF6}, // Ў {0x045E, 0xF7}, // ў {0x00B0, 0xF8}, // ° {0x2219, 0xF9}, // ∙ {0x00B7, 0xFA}, // · {0x221A, 0xFB}, // √ {0x2116, 0xFC}, // № {0x00A4, 0xFD}, // ¤ {0x25A0, 0xFE}, // ■ {0x00A0, 0xFF} // NBSP }; */ std::map create_UTF16_to_CP866() { std::map UTF16_to_CP866; // Заполнение map оператором [] UTF16_to_CP866[0x0000] = 0x00; // NUL UTF16_to_CP866[0x0001] = 0x01; // SOH UTF16_to_CP866[0x0002] = 0x02; // STX UTF16_to_CP866[0x0003] = 0x03; // ETX UTF16_to_CP866[0x0004] = 0x04; // EOT UTF16_to_CP866[0x0005] = 0x05; // ENQ UTF16_to_CP866[0x0006] = 0x06; // ACK UTF16_to_CP866[0x0007] = 0x07; // BEL UTF16_to_CP866[0x0008] = 0x08; // BS UTF16_to_CP866[0x0009] = 0x09; // TAB UTF16_to_CP866[0x000A] = 0x0A; // LF UTF16_to_CP866[0x000B] = 0x0B; // VT UTF16_to_CP866[0x000C] = 0x0C; // FF UTF16_to_CP866[0x000D] = 0x0D; // CR UTF16_to_CP866[0x000E] = 0x0E; // SO UTF16_to_CP866[0x000F] = 0x0F; // SI UTF16_to_CP866[0x0010] = 0x10; // DLE UTF16_to_CP866[0x0011] = 0x11; // DC1 UTF16_to_CP866[0x0012] = 0x12; // DC2 UTF16_to_CP866[0x0013] = 0x13; // DC3 UTF16_to_CP866[0x0014] = 0x14; // DC4 UTF16_to_CP866[0x0015] = 0x15; // NAK UTF16_to_CP866[0x0016] = 0x16; // SYN UTF16_to_CP866[0x0017] = 0x17; // ETB UTF16_to_CP866[0x0018] = 0x18; // CAN UTF16_to_CP866[0x0019] = 0x19; // EM UTF16_to_CP866[0x001A] = 0x1A; // SUB UTF16_to_CP866[0x001B] = 0x1B; // ESC UTF16_to_CP866[0x001C] = 0x1C; // FS UTF16_to_CP866[0x001D] = 0x1D; // GS UTF16_to_CP866[0x001E] = 0x1E; // RS UTF16_to_CP866[0x001F] = 0x1F; // US UTF16_to_CP866[0x0020] = 0x20; // Space UTF16_to_CP866[0x0021] = 0x21; // ! UTF16_to_CP866[0x0022] = 0x22; // " UTF16_to_CP866[0x0023] = 0x23; // # UTF16_to_CP866[0x0024] = 0x24; // $ UTF16_to_CP866[0x0025] = 0x25; // % UTF16_to_CP866[0x0026] = 0x26; // & UTF16_to_CP866[0x0027] = 0x27; // ' UTF16_to_CP866[0x0028] = 0x28; // ( UTF16_to_CP866[0x0029] = 0x29; // ) UTF16_to_CP866[0x002A] = 0x2A; // * UTF16_to_CP866[0x002B] = 0x2B; // + UTF16_to_CP866[0x002C] = 0x2C; // , UTF16_to_CP866[0x002D] = 0x2D; // - UTF16_to_CP866[0x002E] = 0x2E; // . UTF16_to_CP866[0x002F] = 0x2F; // / UTF16_to_CP866[0x0030] = 0x30; // 0 UTF16_to_CP866[0x0031] = 0x31; // 1 UTF16_to_CP866[0x0032] = 0x32; // 2 UTF16_to_CP866[0x0033] = 0x33; // 3 UTF16_to_CP866[0x0034] = 0x34; // 4 UTF16_to_CP866[0x0035] = 0x35; // 5 UTF16_to_CP866[0x0036] = 0x36; // 6 UTF16_to_CP866[0x0037] = 0x37; // 7 UTF16_to_CP866[0x0038] = 0x38; // 8 UTF16_to_CP866[0x0039] = 0x39; // 9 UTF16_to_CP866[0x003A] = 0x3A; // : UTF16_to_CP866[0x003B] = 0x3B; // ; UTF16_to_CP866[0x003C] = 0x3C; // < UTF16_to_CP866[0x003D] = 0x3D; // = UTF16_to_CP866[0x003E] = 0x3E; // > UTF16_to_CP866[0x003F] = 0x3F; // ? UTF16_to_CP866[0x0040] = 0x40; // @ UTF16_to_CP866[0x0041] = 0x41; // A UTF16_to_CP866[0x0042] = 0x42; // B UTF16_to_CP866[0x0043] = 0x43; // C UTF16_to_CP866[0x0044] = 0x44; // D UTF16_to_CP866[0x0045] = 0x45; // E UTF16_to_CP866[0x0046] = 0x46; // F UTF16_to_CP866[0x0047] = 0x47; // G UTF16_to_CP866[0x0048] = 0x48; // H UTF16_to_CP866[0x0049] = 0x49; // I UTF16_to_CP866[0x004A] = 0x4A; // J UTF16_to_CP866[0x004B] = 0x4B; // K UTF16_to_CP866[0x004C] = 0x4C; // L UTF16_to_CP866[0x004D] = 0x4D; // M UTF16_to_CP866[0x004E] = 0x4E; // N UTF16_to_CP866[0x004F] = 0x4F; // O UTF16_to_CP866[0x0050] = 0x50; // P UTF16_to_CP866[0x0051] = 0x51; // Q UTF16_to_CP866[0x0052] = 0x52; // R UTF16_to_CP866[0x0053] = 0x53; // S UTF16_to_CP866[0x0054] = 0x54; // T UTF16_to_CP866[0x0055] = 0x55; // U UTF16_to_CP866[0x0056] = 0x56; // V UTF16_to_CP866[0x0057] = 0x57; // W UTF16_to_CP866[0x0058] = 0x58; // X UTF16_to_CP866[0x0059] = 0x59; // Y UTF16_to_CP866[0x005A] = 0x5A; // Z UTF16_to_CP866[0x005B] = 0x5B; // [ UTF16_to_CP866[0x005C] = 0x5C; // slash UTF16_to_CP866[0x005D] = 0x5D; // ] UTF16_to_CP866[0x005E] = 0x5E; // ^ UTF16_to_CP866[0x005F] = 0x5F; // _ UTF16_to_CP866[0x0060] = 0x60; // ` UTF16_to_CP866[0x0061] = 0x61; // a UTF16_to_CP866[0x0062] = 0x62; // b UTF16_to_CP866[0x0063] = 0x63; // c UTF16_to_CP866[0x0064] = 0x64; // d UTF16_to_CP866[0x0065] = 0x65; // e UTF16_to_CP866[0x0066] = 0x66; // f UTF16_to_CP866[0x0067] = 0x67; // g UTF16_to_CP866[0x0068] = 0x68; // h UTF16_to_CP866[0x0069] = 0x69; // i UTF16_to_CP866[0x006A] = 0x6A; // j UTF16_to_CP866[0x006B] = 0x6B; // k UTF16_to_CP866[0x006C] = 0x6C; // l UTF16_to_CP866[0x006D] = 0x6D; // m UTF16_to_CP866[0x006E] = 0x6E; // n UTF16_to_CP866[0x006F] = 0x6F; // o UTF16_to_CP866[0x0070] = 0x70; // p UTF16_to_CP866[0x0071] = 0x71; // q UTF16_to_CP866[0x0072] = 0x72; // r UTF16_to_CP866[0x0073] = 0x73; // s UTF16_to_CP866[0x0074] = 0x74; // t UTF16_to_CP866[0x0075] = 0x75; // u UTF16_to_CP866[0x0076] = 0x76; // v UTF16_to_CP866[0x0077] = 0x77; // w UTF16_to_CP866[0x0078] = 0x78; // x UTF16_to_CP866[0x0079] = 0x79; // y UTF16_to_CP866[0x007A] = 0x7A; // z UTF16_to_CP866[0x007B] = 0x7B; // { UTF16_to_CP866[0x007C] = 0x7C; // | UTF16_to_CP866[0x007D] = 0x7D; // } UTF16_to_CP866[0x007E] = 0x7E; // ~ UTF16_to_CP866[0x007F] = 0x7F; // DEL UTF16_to_CP866[0x0410] = 0x80; // А UTF16_to_CP866[0x0411] = 0x81; // Б UTF16_to_CP866[0x0412] = 0x82; // В UTF16_to_CP866[0x0413] = 0x83; // Г UTF16_to_CP866[0x0414] = 0x84; // Д UTF16_to_CP866[0x0415] = 0x85; // Е UTF16_to_CP866[0x0416] = 0x86; // Ж UTF16_to_CP866[0x0417] = 0x87; // З UTF16_to_CP866[0x0418] = 0x88; // И UTF16_to_CP866[0x0419] = 0x89; // Й UTF16_to_CP866[0x041A] = 0x8A; // К UTF16_to_CP866[0x041B] = 0x8B; // Л UTF16_to_CP866[0x041C] = 0x8C; // М UTF16_to_CP866[0x041D] = 0x8D; // Н UTF16_to_CP866[0x041E] = 0x8E; // О UTF16_to_CP866[0x041F] = 0x8F; // П UTF16_to_CP866[0x0420] = 0x90; // Р UTF16_to_CP866[0x0421] = 0x91; // С UTF16_to_CP866[0x0422] = 0x92; // Т UTF16_to_CP866[0x0423] = 0x93; // У UTF16_to_CP866[0x0424] = 0x94; // Ф UTF16_to_CP866[0x0425] = 0x95; // Х UTF16_to_CP866[0x0426] = 0x96; // Ц UTF16_to_CP866[0x0427] = 0x97; // Ч UTF16_to_CP866[0x0428] = 0x98; // Ш UTF16_to_CP866[0x0429] = 0x99; // Щ UTF16_to_CP866[0x042A] = 0x9A; // Ъ UTF16_to_CP866[0x042B] = 0x9B; // Ы UTF16_to_CP866[0x042C] = 0x9C; // Ь UTF16_to_CP866[0x042D] = 0x9D; // Э UTF16_to_CP866[0x042E] = 0x9E; // Ю UTF16_to_CP866[0x042F] = 0x9F; // Я UTF16_to_CP866[0x0430] = 0xA0; // а UTF16_to_CP866[0x0431] = 0xA1; // б UTF16_to_CP866[0x0432] = 0xA2; // в UTF16_to_CP866[0x0433] = 0xA3; // г UTF16_to_CP866[0x0434] = 0xA4; // д UTF16_to_CP866[0x0435] = 0xA5; // е UTF16_to_CP866[0x0436] = 0xA6; // ж UTF16_to_CP866[0x0437] = 0xA7; // з UTF16_to_CP866[0x0438] = 0xA8; // и UTF16_to_CP866[0x0439] = 0xA9; // й UTF16_to_CP866[0x043A] = 0xAA; // к UTF16_to_CP866[0x043B] = 0xAB; // л UTF16_to_CP866[0x043C] = 0xAC; // м UTF16_to_CP866[0x043D] = 0xAD; // н UTF16_to_CP866[0x043E] = 0xAE; // о UTF16_to_CP866[0x043F] = 0xAF; // п UTF16_to_CP866[0x2591] = 0xB0; // ░ UTF16_to_CP866[0x2592] = 0xB1; // ▒ UTF16_to_CP866[0x2593] = 0xB2; // ▓ UTF16_to_CP866[0x2502] = 0xB3; // │ UTF16_to_CP866[0x2524] = 0xB4; // ┤ UTF16_to_CP866[0x2561] = 0xB5; // ╡ UTF16_to_CP866[0x2562] = 0xB6; // ╢ UTF16_to_CP866[0x2556] = 0xB7; // ╖ UTF16_to_CP866[0x2555] = 0xB8; // ╕ UTF16_to_CP866[0x2563] = 0xB9; // ╣ UTF16_to_CP866[0x2551] = 0xBA; // ║ UTF16_to_CP866[0x2557] = 0xBB; // ╗ UTF16_to_CP866[0x255D] = 0xBC; // ╝ UTF16_to_CP866[0x255C] = 0xBD; // ╜ UTF16_to_CP866[0x255B] = 0xBE; // ╛ UTF16_to_CP866[0x2510] = 0xBF; // ┐ UTF16_to_CP866[0x2514] = 0xC0; // └ UTF16_to_CP866[0x2534] = 0xC1; // ┴ UTF16_to_CP866[0x252C] = 0xC2; // ┬ UTF16_to_CP866[0x251C] = 0xC3; // ├ UTF16_to_CP866[0x2500] = 0xC4; // ─ UTF16_to_CP866[0x253C] = 0xC5; // ┼ UTF16_to_CP866[0x255E] = 0xC6; // ╞ UTF16_to_CP866[0x255F] = 0xC7; // ╟ UTF16_to_CP866[0x255A] = 0xC8; // ╚ UTF16_to_CP866[0x2554] = 0xC9; // ╔ UTF16_to_CP866[0x2569] = 0xCA; // ╩ UTF16_to_CP866[0x2566] = 0xCB; // ╦ UTF16_to_CP866[0x2560] = 0xCC; // ╠ UTF16_to_CP866[0x2550] = 0xCD; // ═ UTF16_to_CP866[0x256C] = 0xCE; // ╬ UTF16_to_CP866[0x2567] = 0xCF; // ╧ UTF16_to_CP866[0x2568] = 0xD0; // ╨ UTF16_to_CP866[0x2564] = 0xD1; // ╤ UTF16_to_CP866[0x2565] = 0xD2; // ╥ UTF16_to_CP866[0x2559] = 0xD3; // ╙ UTF16_to_CP866[0x2558] = 0xD4; // ╘ UTF16_to_CP866[0x2552] = 0xD5; // ╒ UTF16_to_CP866[0x2553] = 0xD6; // ╓ UTF16_to_CP866[0x256B] = 0xD7; // ╫ UTF16_to_CP866[0x256A] = 0xD8; // ╪ UTF16_to_CP866[0x2518] = 0xD9; // ┘ UTF16_to_CP866[0x250C] = 0xDA; // ┌ UTF16_to_CP866[0x2588] = 0xDB; // █ UTF16_to_CP866[0x2584] = 0xDC; // ▄ UTF16_to_CP866[0x258C] = 0xDD; // ▌ UTF16_to_CP866[0x2590] = 0xDE; // ▐ UTF16_to_CP866[0x2580] = 0xDF; // ▀ UTF16_to_CP866[0x0440] = 0xE0; // р UTF16_to_CP866[0x0441] = 0xE1; // с UTF16_to_CP866[0x0442] = 0xE2; // т UTF16_to_CP866[0x0443] = 0xE3; // у UTF16_to_CP866[0x0444] = 0xE4; // ф UTF16_to_CP866[0x0445] = 0xE5; // х UTF16_to_CP866[0x0446] = 0xE6; // ц UTF16_to_CP866[0x0447] = 0xE7; // ч UTF16_to_CP866[0x0448] = 0xE8; // ш UTF16_to_CP866[0x0449] = 0xE9; // щ UTF16_to_CP866[0x044A] = 0xEA; // ъ UTF16_to_CP866[0x044B] = 0xEB; // ы UTF16_to_CP866[0x044C] = 0xEC; // ь UTF16_to_CP866[0x044D] = 0xED; // э UTF16_to_CP866[0x044E] = 0xEE; // ю UTF16_to_CP866[0x044F] = 0xEF; // я UTF16_to_CP866[0x0401] = 0xF0; // Ё UTF16_to_CP866[0x0451] = 0xF1; // ё UTF16_to_CP866[0x0404] = 0xF2; // Є UTF16_to_CP866[0x0454] = 0xF3; // є UTF16_to_CP866[0x0407] = 0xF4; // Ї UTF16_to_CP866[0x0457] = 0xF5; // ї UTF16_to_CP866[0x040E] = 0xF6; // Ў UTF16_to_CP866[0x045E] = 0xF7; // ў UTF16_to_CP866[0x00B0] = 0xF8; // ° UTF16_to_CP866[0x2219] = 0xF9; // ∙ UTF16_to_CP866[0x00B7] = 0xFA; // · UTF16_to_CP866[0x221A] = 0xFB; // √ UTF16_to_CP866[0x2116] = 0xFC; // № UTF16_to_CP866[0x00A4] = 0xFD; // ¤ UTF16_to_CP866[0x25A0] = 0xFE; // ■ UTF16_to_CP866[0x00A0] = 0xFF; // // Вывод содержимого map //for (const auto& pair : UTF16_to_CP866) { // std::cout << std::hex << pair.first << ": " << std::hex << static_cast(pair.second) << std::endl; //} return UTF16_to_CP866; } std::map UTF16_to_CP866 = create_UTF16_to_CP866(); //портится верхняя часть таблицы? char* CTextConverter::ConvertFromUTF16ToCP866(const UTF16TEXT* utf16Text) { size_t len = 0; const UTF16TEXT* p = utf16Text; while (*p++) { ++len; } std::string s; for (int i = 0; i < len; i++) { UTF16TEXT uc = utf16Text[i]; char c; c = UTF16_to_CP866 [uc]; s += c; } char *result = new char[s.length() + 1]; // std::cout << s << std::endl; std::strncpy (result, s.c_str(), s.size()); result[len] = '\0'; // std::cout << result << std::endl; return result; } std::string ConvertUTF16ToUTF8(const UTF16TEXT* utf16_text) { std::string utf8_text; // Итерируемся по массиву UTF-16 до тех пор, пока не достигнем нулевого символа //for (int i = 0; utf16_text[i] != u'\0'; ++i) { for (int i = 0; utf16_text[i] != '\0'; ++i) { UTF16TEXT unicode_value = utf16_text[i]; // Если символ меньше или равен 0x7F, он в кодировке ASCII и представляется в UTF-8 одним байтом if (unicode_value <= 0x7F) { utf8_text.push_back(static_cast(unicode_value)); } else if (unicode_value <= 0x7FF) { // Если символ в диапазоне 0x80 - 0x7FF, он представляется в UTF-8 двумя байтами utf8_text.push_back(static_cast(0xC0 | (unicode_value >> 6))); // Первый байт utf8_text.push_back(static_cast(0x80 | (unicode_value & 0x3F))); // Второй байт } else { // В противном случае символ представляется в UTF-8 тремя байтами utf8_text.push_back(static_cast(0xE0 | (unicode_value >> 12))); // Первый байт utf8_text.push_back(static_cast(0x80 | ((unicode_value >> 6) & 0x3F))); // Второй байт utf8_text.push_back(static_cast(0x80 | (unicode_value & 0x3F))); // Третий байт } } return utf8_text; } tea-qt-63.3.0/src/enc.h000066400000000000000000000017521476733534200145260ustar00rootroot00000000000000#ifndef ENC_H #define ENC_H #include //#define char16_t uint16_t /* #if QT_VERSION >= 0x050000 #include #define UTF16TEXT char16_t #else #define UTF16TEXT unsigned short #endif */ #if (__cplusplus < 201103L) //C++11 specific stuff here #include #define char16_t uint16_t #endif #define UTF16TEXT char16_t class CTextConverter { public: static UTF16TEXT* ConvertFromCP1251ToUTF16 (const char* cp1251Text); //ok static char* ConvertFromUTF16ToCP1251 (const UTF16TEXT* utf16Text); //ok static UTF16TEXT* ConvertFromDOS866ToUTF16 (const char* dos866Text); //ok static char* ConvertFromUTF16ToCP866(const UTF16TEXT* utf16Text); //НОЛЬ static UTF16TEXT* ConvertFromKOI8RToUTF16(const char* koi8rText); //ok static char* ConvertFromUTF16ToKOI8R(const UTF16TEXT* utf16Text); //ok static UTF16TEXT* ConvertFromUTF8ToUTF16(const char* utf8Text); static QStringList get_charsets(); }; std::string ConvertUTF16ToUTF8(const UTF16TEXT* utf16_text); #endif tea-qt-63.3.0/src/exif_reader.cpp000066400000000000000000000275231476733534200165750ustar00rootroot00000000000000//#include #include #include #include #include #include "exif_reader.h" rint8u readByte (QFile &file) { char a; file.getChar (&a); return (rint8u)a; } //-------------------------------------------------------------------------- // Parse the marker stream until SOS or EOI is seen; //-------------------------------------------------------------------------- int Exif::readJpegSections (QFile &file, int *Orientation) { QByteArray *data; rint8u a = readByte (file); if (a != 0xff) return -1; else { rint8u b = readByte (file); if (b != M_SOI) return -1; } //int SectionsRead=0; for (;;) { int itemlen; int prev; rint8u marker = 0; rint8u ll, lh; prev = 0; for (int i = 0; ; i++) { marker = readByte (file); if (marker != 0xff && prev == 0xff) break; prev = marker; } // Read the length of the section. lh = readByte (file); ll = readByte (file); itemlen = (lh << 8) | ll; if (itemlen < 2) // Invalid marker return -1; data = new QByteArray (file.read (itemlen - 2)); // Read the whole section. if (data->isEmpty()) return -1; // Could not allocate memory if(data->size() != itemlen - 2) return -1; // Premature end of file? switch (marker) { case M_SOS: // stop before hitting compressed data return 0; case M_EOI: // in case it's a tables-only JPEG stream return -1; case M_COM: // Comment section delete (data); break; case M_JFIF: // Regular jpegs always have this tag, exif images have the exif // marker instead, althogh ACDsee will write images with both markers. // this program will re-create this marker on absence of exif marker. // hence no need to keep the copy from the file. if (itemlen >= 16){ // if Jfif header not too short // skipped } delete (data); break; case M_EXIF: // There can be different section using the same marker. if (data->left(4) == "Exif") { processEXIF (data, itemlen, Orientation); break; } // Oterwise, discard this section. delete (data); break; case M_IPTC: delete (data); break; default: // Skip any other sections. break; } // switch } // for(;;) return 0; } // Convert a 16 bit unsigned value from file's native byte order int Get16u (const void * Short, int MotorolaOrder) { if (MotorolaOrder) return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; else return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; } // Convert a 32 bit signed value from file's native byte order int Get32s(const void * Long, int MotorolaOrder) { if (MotorolaOrder) return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); else return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); } // Convert a 32 bit unsigned value from file's native byte order unsigned Get32u (const void * Long, int MotorolaOrder) { return (unsigned)Get32s(Long, MotorolaOrder) & 0xffffffff; } #define NUM_FORMATS 12 #define FMT_BYTE 1 #define FMT_STRING 2 #define FMT_USHORT 3 #define FMT_ULONG 4 #define FMT_URATIONAL 5 #define FMT_SBYTE 6 #define FMT_UNDEFINED 7 #define FMT_SSHORT 8 #define FMT_SLONG 9 #define FMT_SRATIONAL 10 #define FMT_SINGLE 11 #define FMT_DOUBLE 12 // Evaluate number, be it int, rational, or float from directory. double ConvertAnyFormat (const void * ValuePtr, int Format, int MotorolaOrder) { double Value = 0.0; switch (Format) { case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; case FMT_BYTE: Value = *(uchar *)ValuePtr; break; case FMT_USHORT: Value = Get16u(ValuePtr, MotorolaOrder); break; case FMT_ULONG: Value = Get32u(ValuePtr, MotorolaOrder); break; case FMT_URATIONAL: case FMT_SRATIONAL: { int Num = Get32s(ValuePtr, MotorolaOrder); int Den = Get32s(4+(char *)ValuePtr, MotorolaOrder); if (Den == 0) Value = 0; else Value = (double) Num / Den; break; } case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr, MotorolaOrder); break; case FMT_SLONG: Value = Get32s(ValuePtr, MotorolaOrder); break; // Not sure if this is correct (never seen float used in Exif format) case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; case FMT_DOUBLE: Value = *(double *)ValuePtr; break; default: Value = 100;// Illegal format code } return Value; } #define TAG_ORIENTATION 0x0112 #define TAG_INTEROP_OFFSET 0xA005 #define TAG_EXIF_OFFSET 0x8769 #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; // Process one of the nested EXIF directories. int Exif::processEXIFDir (const char *DirStart, const char *OffsetBase, rint32u exifSize, rint32u nesting, int MotorolaOrder, int *NumOrientations, int *Orientation) { int numDirEntries; if(nesting > 4) return -1; // Maximum Exif directory nesting exceeded (corrupt Exif header) numDirEntries = Get16u (DirStart, MotorolaOrder); //qDebug() << "num entries: " << numDirEntries; for (int de=0; de= NUM_FORMATS) continue; // (-1) catches illegal zero case as unsigned underflows to positive large. if ((unsigned)Components > 0x10000) continue; // Too many components int ByteCount = Components * BytesPerFormat[Format]; //qDebug() << "byte count" << ByteCount; if (ByteCount > 4) { // If its bigger than 4 bytes, the dir entry contains an offset. unsigned OffsetVal = Get32u (DirEntry + 8, MotorolaOrder); if (OffsetVal+ByteCount > exifSize) continue; // Bogus pointer offset and / or bytecount value ValuePtr = OffsetBase + OffsetVal; } else // 4 bytes or less and value is in the dir entry itself ValuePtr = DirEntry+8; // Extract useful components of tag switch (Tag) { case TAG_ORIENTATION: if (*NumOrientations >= 2) // Can have another orientation tag for the thumbnail, but if there's // a third one, things are stringae. break; if (*NumOrientations == 0) *Orientation = (int)ConvertAnyFormat(ValuePtr, Format, MotorolaOrder); //qDebug() << "orientation:" << *Orientation; if (*Orientation < 0 || *Orientation > 8) // Undefined rotation value *Orientation = 0; *NumOrientations += 1; break; case TAG_EXIF_OFFSET: case TAG_INTEROP_OFFSET: const char *SubdirStart = OffsetBase + Get32u(ValuePtr, MotorolaOrder); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ exifSize) ; // Illegal Exif or interop ofset directory link else processEXIFDir (SubdirStart, OffsetBase, exifSize, nesting+ 1, MotorolaOrder, NumOrientations, Orientation); } } return 0; } // Process a EXIF marker // Describes all the drivel that most digital cameras include... int Exif::processEXIF(QByteArray *data, int itemlen, int *Orientation) { int MotorolaOrder = 0; // Check the EXIF header component if (data->left(6) == "Exif\0\0") qDebug() << data->left(4); if(data->mid(6,2) == "II") // Exif section in Intel order //qDebug() << data->mid(6,2); MotorolaOrder = 0; else { if(data->mid(6,2) == "II") // Exif section in Motorola order //qDebug() << data->mid(6,2); MotorolaOrder = 1; else return -1; // Invalid Exif alignment marker. } // get first offset QByteArray ttt (data->mid (10, 4)); const char *ttt2 = ttt.constData(); rint32u FirstOffset = Get32u (ttt2, MotorolaOrder); //qDebug() << "fist offset: " << FirstOffset; if (FirstOffset < 8 || FirstOffset > 16) if (FirstOffset < 16 || int (FirstOffset) > itemlen - 16) return -1; // invalid offset for first Exif IFD value ; const char *dirStart = data->constData(); const char *offsetBase = data->constData(); dirStart += 6 + FirstOffset; offsetBase += 6; int numOrientations = 0; // First directory starts 16 bytes in. All offset are relative to 8 bytes in. processEXIFDir (dirStart, offsetBase, itemlen - 8, 0, MotorolaOrder, &numOrientations, Orientation); //qDebug() << "num orientations:" << numOrientations; return 0; } int Exif::readJpegFile (QFile &file, int *Orientation) { readJpegSections (file, Orientation); return 0; } int Exif::getExifOrientation (QFile &file) { int r = 0; readJpegFile (file, &r); return r; } int get_exif_orientation (const QString &fname) { Exif exif; int o = 0; QFile file (fname); if (file.open(QIODevice::ReadOnly)) { o = exif.getExifOrientation(file/*, &o*/); file.close(); } return o; } tea-qt-63.3.0/src/exif_reader.h000066400000000000000000000043341476733534200162350ustar00rootroot00000000000000#ifndef EXIF_READER_H #define EXIF_READER_H // This implementation is based on http://www.sentex.net/~mwandel/jhead/ // Rewritten and published in public domain like // the original code by http://imonad.com //-------------------------------------------------------------------------- // JPEG markers consist of one or more 0xFF bytes, followed by a marker // code byte (which is not an FF). Here are the marker codes of interest // in this program. (See jdmarker.c for a more complete list.) //-------------------------------------------------------------------------- #define M_SOF0 0xC0 // Start Of Frame N #define M_SOF1 0xC1 // N indicates which compression process #define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use #define M_SOF3 0xC3 #define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers #define M_SOF6 0xC6 #define M_SOF7 0xC7 #define M_SOF9 0xC9 #define M_SOF10 0xCA #define M_SOF11 0xCB #define M_SOF13 0xCD #define M_SOF14 0xCE #define M_SOF15 0xCF #define M_SOI 0xD8 // Start Of Image (beginning of datastream) #define M_EOI 0xD9 // End Of Image (end of datastream) #define M_SOS 0xDA // Start Of Scan (begins compressed data) #define M_JFIF 0xE0 // Jfif marker #define M_EXIF 0xE1 // Exif marker. Also used for XMP data! #define M_XMP 0x10E1 // Not a real tag (same value in file as Exif!) #define M_COM 0xFE // COMment #define M_DQT 0xDB #define M_DHT 0xC4 #define M_DRI 0xDD #define M_IPTC 0xED // IPTC marker #include #include #include typedef unsigned char rint8u; typedef char rint8; typedef unsigned int rint32u; class Exif { public: int getExifOrientation (QFile &file); int readJpegFile (QFile &file, int *Orientation); int readJpegSections (QFile &file, int *Orientation); int processEXIF (QByteArray *barr, int itemlen, int *Orientation); int processEXIFDir (const char *dirStart, const char *offsetBase, rint32u size, rint32u nesting, int MotorolaOrder, int *numOrientations, int *Orientation); }; int get_exif_orientation (const QString &fname); #endif // EXIF_READER_H tea-qt-63.3.0/src/fman.cpp000066400000000000000000000341471476733534200152410ustar00rootroot00000000000000 /************************************************************************** * 2007-2024 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include "fman.h" #include "utils.h" //#include "logmemo.h" extern QSettings *settings; void CFMan::dir_up() { if (dir.isRoot()) return; QString oldcurdir = dir.dirName(); dir.cdUp(); nav (dir.path()); QModelIndex index = index_from_name (oldcurdir); selectionModel()->setCurrentIndex(index, QItemSelectionModel::Rows | QItemSelectionModel::NoUpdate); scrollTo (index); } void CFMan::nav (const QString &path) { if (path.isEmpty()) return; QString p = path; p = p.remove ("file://"); if (p.contains ("%")) p = QUrl::fromPercentEncoding (p.toLatin1().constData()); // if (path.startsWith ("file://")) // p = p.remove (0, 7); dir.setPath (p); if (! dir.exists()) return; setModel (0); QDir::SortFlags sort_flags;// = 0; if (sort_order == Qt::DescendingOrder) sort_flags |= QDir::Reversed; if (sort_mode == 0) sort_flags |= QDir::Name; if (sort_mode == 1) sort_flags |= QDir::Size; if (sort_mode == 2) sort_flags |= QDir::Time; sort_flags |= QDir::DirsFirst; sort_flags |= QDir::IgnoreCase; sort_flags |= QDir::LocaleAware; mymodel->removeRows (0, mymodel->rowCount()); QFileInfoList lst = dir.entryInfoList (QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot| QDir::Files | QDir::Drives, sort_flags); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) if (path != "/") append_dot_entry (".."); #else if (path.size() != 2) append_dot_entry (".."); #endif /* NOT GOOD FOR OS/2 if (! dir.isRoot()) append_dot_entry (".."); */ for (int i = 0; i < lst.size(); i++) add_entry (lst.at(i)); setModel (mymodel); connect (selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(fman_currentChanged(QModelIndex,QModelIndex))); emit dir_changed (p); } const QModelIndex CFMan::index_from_name (const QString &name) { QList lst = mymodel->findItems (name); if (lst.size() > 0) return mymodel->indexFromItem (lst[0]); else return QModelIndex(); } void CFMan::tv_activated (const QModelIndex &index) { QString item_string = index.data().toString(); QString dpath = dir.path(); if (dpath.size() > 1) if (dpath.endsWith("/") || dpath.endsWith("\\")) dpath.truncate(dpath.size() - 1); QString full_path; if (dpath == "/") full_path = "/" + item_string; else full_path = dpath + "/" + item_string; if (item_string == ".." && dir.path() != "/") { dir_up(); return; } if (is_dir (full_path)) { nav (full_path); QModelIndex idx = mymodel->index (0, 0); selectionModel()->setCurrentIndex (idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); } else emit file_activated (full_path); } void CFMan::add_entry (const QFileInfo &fi) { QList items; QStandardItem *item = new QStandardItem (fi.fileName()); if (fi.isDir()) { QFont f = item->font(); f.setBold (true); item->setFont(f); } item->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); items.append (item); item = new QStandardItem (QString::number (fi.size())); item->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEnabled); items.append (item); item = new QStandardItem (fi.lastModified().toString ("yyyy-MM-dd")); item->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEnabled); items.append (item); mymodel->appendRow (items); } void CFMan::append_dot_entry (const QString &fname) { QList items; QStandardItem *item = new QStandardItem (fname); item->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEnabled); items.append (item); item = new QStandardItem ("-"); item->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEnabled); items.append (item); item = new QStandardItem ("-"); item->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEnabled); items.append (item); mymodel->appendRow (items); } void CFMan::header_view_sortIndicatorChanged (int logicalIndex, Qt::SortOrder order) { sort_order = order; sort_mode = logicalIndex; settings->setValue ("fman_sort_mode", sort_mode); settings->setValue ("fman_sort_order", sort_order); refresh(); } CFMan::CFMan (QWidget *parent): QTreeView (parent) { sort_mode = settings->value ("fman_sort_mode", 0).toInt(); sort_order = Qt::SortOrder (settings->value ("fman_sort_order", 0).toInt()); mymodel = new QStandardItemModel (0, 3, parent); mymodel->setHeaderData (0, Qt::Horizontal, QObject::tr ("Name")); mymodel->setHeaderData (1, Qt::Horizontal, QObject::tr ("Size")); mymodel->setHeaderData (2, Qt::Horizontal, QObject::tr ("Modified at")); setRootIsDecorated (false); setAlternatingRowColors (true); setAllColumnsShowFocus (true); setModel (mymodel); setDragEnabled (true); #if QT_VERSION >= 0x050000 header()->setSectionResizeMode (QHeaderView::ResizeToContents); header()->setSectionsClickable (true); #else header()->setResizeMode (QHeaderView::ResizeToContents); header()->setClickable (true); #endif header()->setSortIndicator (sort_mode, sort_order); header()->setSortIndicatorShown (true); connect (header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(header_view_sortIndicatorChanged(int,Qt::SortOrder))); header()->setStretchLastSection (false); setSelectionMode (QAbstractItemView::ExtendedSelection); setSelectionBehavior (QAbstractItemView::SelectRows); connect (this, SIGNAL(activated(QModelIndex)), this, SLOT(tv_activated(QModelIndex))); } void CFMan::fman_currentChanged (const QModelIndex ¤t, const QModelIndex &previous ) { int row = current.row(); if (row < 0) { emit current_file_changed ("", ""); return; } QModelIndex i = model()->index (row, 0); QString item_string = i.data().toString(); QString full_path = dir.path() + "/" + item_string; emit current_file_changed (full_path, item_string); } QString CFMan::get_sel_fname() { if (! selectionModel()->hasSelection()) return QString(); QModelIndex index = selectionModel()->currentIndex(); QString item_string = index.data().toString(); return dir.path() + "/" + item_string; //return the full path } QStringList CFMan::get_sel_fnames() { if (! selectionModel()->hasSelection()) return QStringList(); QModelIndexList il = selectionModel()->QItemSelectionModel::selectedRows (0); QStringList li; for (QList ::iterator i = il.begin(); i != il.end(); ++i) { QString item_string = i->data().toString(); if (item_string != "..") { QString full_path = dir.path() + "/" + item_string; li.append (full_path); } } return li; } void CFMan::refresh() { QString current; if (selectionModel()->hasSelection()) { QModelIndex index = selectionModel()->currentIndex(); current = index.data().toString(); } nav (dir.path()); QModelIndex index = index_from_name (current); selectionModel()->setCurrentIndex (index, QItemSelectionModel::Select | QItemSelectionModel::Rows); scrollTo (index); } const QModelIndex CFMan::index_from_idx (int idx) { QStandardItem *item = mymodel->item (idx); if (item) return mymodel->indexFromItem (item); else return QModelIndex(); } int CFMan::get_sel_index() { if (! selectionModel()->hasSelection()) return -1; QModelIndex index = selectionModel()->currentIndex(); return index.row(); } void CFMan::mouseMoveEvent (QMouseEvent *event) { if (! (event->buttons() & Qt::LeftButton)) return; QStringList l = get_sel_fnames(); if (l.size() < 1) return; QDrag *drag = new QDrag (this); QMimeData *mimeData = new QMimeData; QList url_list; for (int i = 0; i < l.size(); i++) url_list.append (QUrl::fromLocalFile (l[i])); mimeData->setUrls (url_list); drag->setMimeData (mimeData); if (drag->exec (Qt::CopyAction | Qt::MoveAction | Qt::LinkAction) == Qt::MoveAction) refresh(); event->accept(); } void CFMan::keyPressEvent (QKeyEvent *event) { //заменить это фуфло на selectionModel()->setCurrentIndex (indexBelow (currentIndex()), QItemSelectionModel::Rows | QItemSelectionModel::Toggle); /* if (event->key() == Qt::Key_Insert) { bool sel = false; QModelIndex index = selectionModel()->currentIndex(); int row = index.row(); if (selectionModel()->isSelected (index)) sel = true; sel = ! sel; if (sel) selectionModel()->select (index, QItemSelectionModel::Select | QItemSelectionModel::Rows); else selectionModel()->select (index, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); if (row < mymodel->rowCount() - 1) { QModelIndex newindex = mymodel->index (++row, 0); selectionModel()->setCurrentIndex (newindex, QItemSelectionModel::Current | QItemSelectionModel::Rows); scrollTo (newindex); } event->accept(); return; } */ if (event->key() == Qt::Key_Insert) { if (currentIndex().row() == mymodel->rowCount() - 1) { event->accept(); return; } selectionModel()->setCurrentIndex (indexBelow (currentIndex()), QItemSelectionModel::Rows | QItemSelectionModel::Toggle); event->accept(); return; } if (event->key() == Qt::Key_Backspace) { dir_up(); event->accept(); return; } if (event->key() == Qt::Key_Return) { tv_activated (currentIndex()); event->accept(); return; } if (event->key() == Qt::Key_Up) { if (currentIndex().row() == 0) { event->accept(); return; } if (event->modifiers() & Qt::ShiftModifier) selectionModel()->setCurrentIndex (indexAbove (currentIndex()), QItemSelectionModel::Rows | QItemSelectionModel::Toggle); else selectionModel()->setCurrentIndex (indexAbove (currentIndex()), QItemSelectionModel::Rows | QItemSelectionModel::NoUpdate); event->accept(); return; } if (event->key() == Qt::Key_Down) { if (currentIndex().row() == mymodel->rowCount() - 1) { event->accept(); return; } if (event->modifiers() & Qt::ShiftModifier) selectionModel()->setCurrentIndex (indexBelow (currentIndex()), QItemSelectionModel::Rows | QItemSelectionModel::Toggle); else selectionModel()->setCurrentIndex (indexBelow(currentIndex()), QItemSelectionModel::Rows | QItemSelectionModel::NoUpdate); event->accept(); return; } if (event->key() == Qt::Key_PageUp) { QModelIndex idx = moveCursor (QAbstractItemView::MovePageUp, Qt::NoModifier); selectionModel()->setCurrentIndex (idx, QItemSelectionModel::Rows | QItemSelectionModel::NoUpdate); event->accept(); return; } if (event->key() == Qt::Key_PageDown) { QModelIndex idx = moveCursor (QAbstractItemView::MovePageDown, Qt::NoModifier); selectionModel()->setCurrentIndex (idx, QItemSelectionModel::Rows | QItemSelectionModel::NoUpdate); event->accept(); return; } if (event->key() == Qt::Key_End) { QModelIndex idx = mymodel->index (mymodel->rowCount() - 1, 0); selectionModel()->setCurrentIndex (idx, QItemSelectionModel::Rows | QItemSelectionModel::NoUpdate ); event->accept(); return; } if (event->key() == Qt::Key_Home) { QModelIndex idx = mymodel->index (0, 0); selectionModel()->setCurrentIndex (idx, QItemSelectionModel::Rows | QItemSelectionModel::NoUpdate); event->accept(); return; } QTreeView::keyPressEvent (event); } void CFMan::drawRow (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.row() == currentIndex().row()) { QStyleOptionViewItem current_option = option; QTreeView::drawRow (painter, current_option, index); QStyleOptionFocusRect o; o.rect = option.rect.adjusted(1,1,-1,-1); o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; //o.backgroundColor = palette().color(QPalette::Background); //o.backgroundColor = QColor ("red"); QApplication::style()->drawPrimitive (QStyle::PE_FrameFocusRect, &o, painter); QRect r = option.rect.adjusted (1, 1, -1,-1); painter->drawRect (r); } else QTreeView::drawRow (painter, option, index); } tea-qt-63.3.0/src/fman.h000066400000000000000000000054011476733534200146750ustar00rootroot00000000000000 /************************************************************************** * 2007-2021 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * **************************************************************************/ #ifndef FMAN_H #define FMAN_H #include #include #include #include #include #include #include class CFMan: public QTreeView { Q_OBJECT public: // CZipper zipper; QDir dir; int sort_mode; Qt::SortOrder sort_order; QStandardItemModel *mymodel; CFMan (QWidget *parent = 0); void add_entry (const QFileInfo &fi); void append_dot_entry (const QString &fname); const QModelIndex index_from_name (const QString &name); const QModelIndex index_from_idx (int idx); int get_sel_index(); void nav (const QString &path); QString get_sel_fname(); QStringList get_sel_fnames(); public slots: void tv_activated (const QModelIndex &index); void refresh(); void dir_up(); void fman_currentChanged (const QModelIndex ¤t, const QModelIndex &previous); void header_view_sortIndicatorChanged (int logicalIndex, Qt::SortOrder order); signals: void file_activated (const QString &path); void dir_changed (const QString &path); void current_file_changed (const QString &path, const QString &just_name); protected: void mouseMoveEvent (QMouseEvent *event); void keyPressEvent (QKeyEvent *event); void drawRow (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; #endif tea-qt-63.3.0/src/gui_utils.cpp000066400000000000000000000155151476733534200163220ustar00rootroot00000000000000/* this code is Public Domain */ #include #include #include #include #include #include #include #include "gui_utils.h" #include "utils.h" /* void create_menu_from_list (QObject *handler, QMenu *menu, const QStringList &list, const char *method ) { menu->setTearOffEnabled (true); for (QList ::const_iterator i = list.begin(); i != list.end(); ++i) { if (! i->startsWith ("#")) { QAction *act = new QAction (*i, menu); act->setData (*i); //QAction *act = menu->addAction (*i); //act->setData (*i); handler->connect (act, SIGNAL(triggered()), handler, method); menu->addAction (act); } } } */ void create_menu_from_list (QObject *handler, QMenu *menu, const QStringList &list, const char *method ) { menu->setTearOffEnabled (true); // qDebug() << "=========== create_menu_from_list START========="; for (QList ::const_iterator i = list.begin(); i != list.end(); ++i) { if (! i->startsWith ("#")) { // qDebug() << "*i" << *i; QAction *act = menu->addAction (*i); act->setData (*i); handler->connect (act, SIGNAL(triggered()), handler, method); } } //qDebug() << "=========== create_menu_from_list END========="; } //uses dir name as menuitem, no recursion void create_menu_from_themes (QObject *handler, QMenu *menu, const QString &dir, const char *method ) { menu->setTearOffEnabled (true); QDir d (dir); QFileInfoList lst_fi = d.entryInfoList (QDir::NoDotAndDotDot | QDir::Dirs, QDir::IgnoreCase | QDir::LocaleAware | QDir::Name); for (QList ::iterator fi = lst_fi.begin(); fi != lst_fi.end(); ++fi) { if (fi->isDir()) { if (has_css_file (fi->absoluteFilePath())) { QAction *act = menu->addAction (fi->fileName()); act->setData (fi->filePath()); handler->connect (act, SIGNAL(triggered()), handler, method); } else { QMenu *mni_temp = menu->addMenu (fi->fileName()); create_menu_from_themes (handler, mni_temp, fi->filePath(), method); } } } } void create_menu_from_dir (QObject *handler, QMenu *menu, const QString &dir, const char *method ) { menu->setTearOffEnabled (true); QDir d (dir); if (! d.exists()) return; QFileInfoList lst_fi = d.entryInfoList (QDir::NoDotAndDotDot | QDir::AllEntries, QDir::DirsFirst | QDir::IgnoreCase | QDir::LocaleAware | QDir::Name); for (QList ::iterator fi = lst_fi.begin(); fi != lst_fi.end(); ++fi) { if (fi->isDir()) { QMenu *mni_temp = menu->addMenu (fi->fileName()); create_menu_from_dir (handler, mni_temp, fi->filePath(), method); } else { QAction *act = menu->addAction (fi->fileName()); act->setData (fi->filePath()); handler->connect (act, SIGNAL(triggered()), handler, method); } } } QImage image_scale_by (const QImage &source, bool by_side, int value, Qt::TransformationMode mode) { if (source.isNull()) return source; bool horisontal = (source.width() > source.height()); int width; int height; if (by_side) { width = value; height = value; } else { width = get_value (source.width(), value); height = get_value (source.height(), value); } if (horisontal) return source.scaledToWidth (width, mode); else return source.scaledToHeight (height, mode); } QLineEdit* new_line_edit (QBoxLayout *layout, const QString &label, const QString &def_value) { QHBoxLayout *lt_h = new QHBoxLayout; QLabel *l = new QLabel (label); QLineEdit *r = new QLineEdit; r->setText (def_value); lt_h->insertWidget (-1, l, 0, Qt::AlignLeft); lt_h->insertWidget (-1, r, 1, Qt::AlignLeft); layout->addLayout (lt_h); return r; } QSpinBox* new_spin_box (QBoxLayout *layout, const QString &label, int min, int max, int value, int step) { QHBoxLayout *lt_h = new QHBoxLayout; QLabel *l = new QLabel (label); QSpinBox *r = new QSpinBox; r->setSingleStep (step); r->setRange (min, max); r->setValue (value); lt_h->insertWidget (-1, l, 0, Qt::AlignLeft); lt_h->insertWidget (-1, r, 1, Qt::AlignLeft); layout->addLayout (lt_h, 1); return r; } QComboBox* new_combobox (QBoxLayout *layout, const QString &label, const QStringList &items, const QString &def_value) { QHBoxLayout *lt_h = new QHBoxLayout; QLabel *l = new QLabel (label); QComboBox *r = new QComboBox; r->addItems (items); r->setCurrentIndex (r->findText (def_value)); lt_h->insertWidget (-1, l, 0, Qt::AlignLeft); lt_h->insertWidget (-1, r, 1, Qt::AlignLeft); layout->addLayout (lt_h); return r; } QComboBox* new_combobox (QBoxLayout *layout, const QString &label, const QStringList &items, int index) { QHBoxLayout *lt_h = new QHBoxLayout; QLabel *l = new QLabel (label); QComboBox *r = new QComboBox; r->addItems (items); r->setCurrentIndex (index); lt_h->insertWidget (-1, l, 0, Qt::AlignLeft); lt_h->insertWidget (-1, r, 1, Qt::AlignLeft); layout->addLayout (lt_h); return r; } QComboBox* new_combobox_from_vector (QBoxLayout *layout, const QString &label, std::vector items, int index) { //qDebug() << "new_combobox_from_vector items.size:" << items.size(); QHBoxLayout *lt_h = new QHBoxLayout; QLabel *l = new QLabel (label); QComboBox *r = new QComboBox; //r->addItems (items); for (size_t i = 0; i < items.size(); i++) { r->addItem (QString::fromStdString (items[i])); } if (index < items.size() || index > -1) r->setCurrentIndex (index); lt_h->insertWidget (-1, l, 0, Qt::AlignLeft); lt_h->insertWidget (-1, r, 1, Qt::AlignLeft); layout->addLayout (lt_h); return r; } tea-qt-63.3.0/src/gui_utils.h000066400000000000000000000034141476733534200157620ustar00rootroot00000000000000#ifndef GUI_UTILS_H #define GUI_UTILS_H #include #include #include #include #include #include #include void create_menu_from_list (QObject *handler, QMenu *menu, const QStringList &list, const char *method ); void create_menu_from_themes (QObject *handler, QMenu *menu, const QString &dir, const char *method ); void create_menu_from_dir (QObject *handler, QMenu *menu, const QString &dir, const char *method ); QImage image_scale_by (const QImage &source, bool by_side, int value, Qt::TransformationMode mode); QLineEdit* new_line_edit (QBoxLayout *layout, const QString &label, const QString &def_value); QSpinBox* new_spin_box (QBoxLayout *layout, const QString &label, int min, int max, int value, int step = 1); QComboBox* new_combobox (QBoxLayout *layout, const QString &label, const QStringList &items, const QString &def_value); QComboBox* new_combobox (QBoxLayout *layout, const QString &label, const QStringList &items, int index); QComboBox* new_combobox_from_vector (QBoxLayout *layout, const QString &label, std::vector items, int index); #endif // GUI_UTILS_H tea-qt-63.3.0/src/img_viewer.cpp000066400000000000000000000123711476733534200164500ustar00rootroot00000000000000/* this code is Public Domain */ #include #include #include #include #include #include #include #include #include #include "img_viewer.h" #include "exif_reader.h" #include "utils.h" extern QSettings *settings; CImgViewer::CImgViewer (QObject *parent): QObject (parent) { window_mini.setWindowFlags (Qt::Tool); window_mini.resize (200, 200); img_mini = new QLabel (tr ("preview")); img_mini->setAlignment (Qt::AlignCenter); QVBoxLayout *lt = new QVBoxLayout; lt->addWidget (img_mini); window_mini.setLayout (lt); } void CImgViewer::set_image_mini (const QString &fname) { window_mini.resize (200, 200); window_mini.setWindowTitle (QFileInfo (fname).fileName()); QString fn = get_the_thumb_name (fname); if (! fn.isEmpty()) { QPixmap pm (fn); img_mini->setPixmap (pm); } else { QPixmap pm (fname); if ((pm.width() > (window_mini.width() - 10)) || (pm.height() > (window_mini.height() - 10))) img_mini->setPixmap (pm.scaled (190, 190, Qt::KeepAspectRatio)); else img_mini->setPixmap (pm); } } void CImgViewer::set_image_full (const QString &fname) { window_full.load_image (fname); } QString CImgViewer::get_the_thumb_name (const QString &img_fname) { #if !defined(Q_OS_WIN) || !defined(Q_OS_OS2) QCryptographicHash h (QCryptographicHash::Md5); QString uri (img_fname); uri.prepend ("file://"); h.addData (uri.toUtf8()); QString digest = h.result().toHex(); QString fname (QDir::homePath()); fname.append ("/.thumbnails/large"); fname.append ("/").append (digest).append (".png"); if (file_exists (fname)) return fname; fname.clear(); fname.append (QDir::homePath()); fname.append ("/.thumbnails/normal"); fname.append ("/").append (digest).append (".png"); if (file_exists (fname)) return fname; #endif return QString(); } void CZORWindow::closeEvent (QCloseEvent *event) { event->accept(); } CZORWindow::CZORWindow (QWidget *parent): QWidget (parent) { setMinimumSize (16, 16); } CZORWindow::~CZORWindow() { } void CZORWindow::paintEvent (QPaintEvent *event) { QPainter painter (this); painter.drawImage (0, 0, source_image); } void CZORWindow::load_image (const QString &fname) { if (! file_exists (fname)) return; if (! source_image.load (fname)) return; fname_image = fname; bool orientation_portrait = false; int exif_orientation = get_exif_orientation (fname); if (settings->value ("zor_use_exif_orientation", 0).toInt()) if (exif_orientation == 6 || exif_orientation == 8) orientation_portrait = true; bool need_to_scale = false; if (source_image.size().height() > source_image.size().height()) orientation_portrait = true; if (source_image.size().height() > 600 || source_image.size().width() > 800) need_to_scale = true; if (exif_orientation == 3) { QTransform transform; transform.rotate (180); QImage transformed_image = source_image.transformed (transform); transformed_image = transformed_image.scaled (size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); source_image = transformed_image; } if (orientation_portrait) { QTransform transform; qreal angle = 0.0; if (exif_orientation == 6) angle = 90; else if (exif_orientation == 8) //make clockwise angle = 270; transform.rotate (angle); QImage transformed_image = source_image.transformed (transform); transformed_image = transformed_image.scaled (size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); source_image = transformed_image; } if (need_to_scale) { if (orientation_portrait) source_image = source_image.scaled (QSize (600, 800), Qt::KeepAspectRatio, Qt::SmoothTransformation); else source_image = source_image.scaled (QSize (800, 600), Qt::KeepAspectRatio, Qt::SmoothTransformation); } resize (source_image.size()); QString s_wnd_title (QFileInfo (fname_image).fileName()); s_wnd_title.append (" "); s_wnd_title.append (tr ("scaled to: ")); s_wnd_title.append (QString ("%1x%2").arg (source_image.width()).arg(source_image.height())); setWindowTitle (s_wnd_title); update(); raise(); activateWindow(); } void CZORWindow::keyPressEvent (QKeyEvent * event) { if (event->key() == Qt::Key_Escape) { event->accept(); close(); } event->accept(); } void CGIFWindow::keyPressEvent (QKeyEvent * event) { if (event->key() == Qt::Key_Escape) { event->accept(); close(); } event->accept(); } void CGIFWindow::load_image (const QString &fname) { if (! file_exists (fname)) return; if (movie) delete movie; movie = new QMovie (fname); setMovie (movie); movie->jumpToFrame (0); resize (movie->currentImage().size()); show(); movie->start(); } CGIFWindow::CGIFWindow (QWidget *parent): QLabel (parent) { movie = 0; } CGIFWindow::~CGIFWindow() { if (movie) delete movie; } void CGIFWindow::closeEvent (QCloseEvent *event) { event->accept(); } tea-qt-63.3.0/src/img_viewer.h000066400000000000000000000020611476733534200161100ustar00rootroot00000000000000#ifndef IMG_VIEWER_H #define IMG_VIEWER_H #include #include #include #include #include class CGIFWindow: public QLabel { Q_OBJECT public: QMovie *movie; CGIFWindow (QWidget *parent = 0); ~CGIFWindow(); void load_image (const QString &fname); void keyPressEvent ( QKeyEvent * event); void closeEvent (QCloseEvent *event); }; class CZORWindow : public QWidget { Q_OBJECT public: QString fname_image; QImage source_image; CZORWindow (QWidget *parent = 0); ~CZORWindow(); void load_image (const QString &fname); void paintEvent (QPaintEvent *event); void keyPressEvent (QKeyEvent *event); void closeEvent (QCloseEvent *event); }; class CImgViewer: public QObject { Q_OBJECT public: QWidget window_mini; CZORWindow window_full; QLabel *img_mini; CImgViewer (QObject *parent = 0); void set_image_mini (const QString &fname); void set_image_full (const QString &fname); QString get_the_thumb_name (const QString &img_fname); }; #endif // IMG_VIEWER_H tea-qt-63.3.0/src/libretta_calc.cpp000066400000000000000000000115701476733534200171030ustar00rootroot00000000000000#include #include #include #include //#include //#include #include //#include #include #include "utils.h" using namespace std; class CItem { public: char op; double val; CItem (char a_op, double a_val); }; CItem::CItem (char a_op, double a_val) { op = a_op; val = a_val; } double calculate (string expression) { list items; lconv *l = localeconv(); string sep_need = l->decimal_point; string sep_find; if (sep_need == ".") sep_find = ","; else sep_find = "."; size_t position = expression.find (sep_find); while (position != string::npos) { expression.replace (position, 1, sep_need); position = expression.find (sep_find, position + 1); } //open braces size_t end_pos = expression.find (')'); // cout << " end pos = " << end_pos << endl; size_t start_pos = 0; if (end_pos != string::npos) do { for (size_t i = end_pos; i-- > 0 ;) { if (expression[i] == '(') { start_pos = i; break; } } string s_temp_value = expression.substr (start_pos + 1, end_pos - start_pos - 1); //cout << "start_pos = " << start_pos << " end pos = " << end_pos << endl; //cout << "s_temp_value = " << s_temp_value << endl; double f_temp_value = calculate (s_temp_value); std::ostringstream float_stream; float_stream << f_temp_value; string temp_s (float_stream.str()); //cout << "temp_s = " << temp_s << endl; expression = expression.replace (start_pos + 1, end_pos - start_pos - 1, temp_s); } while (end_pos == string::npos); //parse expression to list: bool new_operator = false; string t_operand; char t_operator = '0'; size_t stop_size = expression.length() - 1; for (size_t i = 0; i < expression.length(); i++) { char t = expression[i]; if (isdigit (t) || t == '.' || t == ',') t_operand += t; if (t == '+' || t == '-' || t == '/' || t == '*' || t == '^' || t == '%' || i == stop_size) { new_operator = true; t_operator = t; if (i == stop_size) t_operator = '0'; } if (new_operator) { //добавляем в items double f = atof (t_operand.c_str()); items.push_back (CItem (t_operator, f)); t_operand = ""; t_operator = '0'; new_operator = false; } } list::iterator p = items.begin(); //степень и процент do { CItem current = *p; list::iterator t = p; ++t; CItem next = *t; if (current.op == '^' || current.op == '%') { if (current.op == '^') { next.val = pow (current.val, next.val); *t = next; } else if (current.op == '%') { next.val = (float) get_value (current.val, next.val); *t = next; } p = items.erase (p); continue; } ++p; } while (p != items.end()); //умножаем и делим p = items.begin(); do { CItem current = *p; list::iterator t = p; ++t; CItem next = *t; if (current.op == '*' || current.op == '/') { if (current.op == '*') { next.val = current.val * next.val; *t = next; } else if (current.op == '/') { next.val = current.val / next.val; *t = next; } p = items.erase (p); continue; } ++p; } while (p != items.end()); //складываем и вычитаем p = items.begin(); do { CItem current = *p; list::iterator t = p; ++t; CItem next = *t; if (current.op == '+' || current.op == '-') { if (current.op == '+') { next.val = current.val + next.val; *t = next; } else if (current.op == '-') { next.val = current.val - next.val; *t = next; } p = items.erase (p); continue; } ++p; } while (p != items.end()); list::iterator start = items.begin(); CItem item = *start; return item.val; } tea-qt-63.3.0/src/libretta_calc.h000066400000000000000000000002111476733534200165360ustar00rootroot00000000000000#ifndef LIBRETTA_CALC_H #define LIBRETTA_CALC_H #include using namespace std; double calculate (string expression); #endif tea-qt-63.3.0/src/logmemo.cpp000066400000000000000000000101041476733534200157420ustar00rootroot00000000000000/*************************************************************************** * 2007-2021 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #if QT_VERSION >= 0x050000 #include #include #endif #include "logmemo.h" CLogMemo::CLogMemo (QWidget *parent): QPlainTextEdit (parent) { setObjectName ("logmemo"); no_jump = false; terminal_output = false; setFocusPolicy (Qt::ClickFocus); setUndoRedoEnabled (false); setReadOnly (true); setTextInteractionFlags (Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); } #if QT_VERSION >= 0x050000 void CLogMemo::log_terminal (const QString &text) { if (no_jump) return; QString txt = text; QString tb = txt; QRegularExpression re ("\\w+\\.\\w+:\\d+:\\d+:"); QRegularExpressionMatch match = re.match (txt, 1); if (match.hasMatch()) { QString matched = match.captured (0); matched = matched.remove (matched.size() - 1, 1); tb.replace (matched, "" + matched + ""); } txt = tb; QTextCursor cr = textCursor(); cr.movePosition (QTextCursor::Start); cr.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, 0); setTextCursor (cr); textCursor().insertHtml (txt + "
"); cr = textCursor(); cr.movePosition (QTextCursor::Start); cr.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, 0); setTextCursor (cr); } #endif void CLogMemo::log (const QString &text) { #if QT_VERSION >= 0x050000 if (terminal_output) { log_terminal (text); return; } #endif if (no_jump) return; QTextCursor cr = textCursor(); cr.movePosition (QTextCursor::Start); cr.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, 0); setTextCursor (cr); if (! terminal_output) { QTime t = QTime::currentTime(); textCursor().insertHtml ("[" + t.toString("hh:mm:ss") + "] " + text + "
"); } else textCursor().insertHtml (text + "
"); cr = textCursor(); cr.movePosition (QTextCursor::Start); cr.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, 0); setTextCursor (cr); } void CLogMemo::mouseDoubleClickEvent (QMouseEvent *event) { QTextCursor cur = cursorForPosition (event->pos()); QString txt = toPlainText(); int pos = cur.position(); int idx_right = txt.indexOf (" ", pos); if (idx_right == -1) { event->accept(); return; } int idx_left = 0; for (int i = pos; i != -1; i--) { if (txt[i] == ' ') { idx_left = i; break; } } txt = txt.mid (idx_left, idx_right - idx_left + 1); emit double_click (txt.simplified()); event->accept(); } tea-qt-63.3.0/src/logmemo.h000066400000000000000000000036321476733534200154170ustar00rootroot00000000000000/*************************************************************************** * 2007-2021 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef LOGMEMO_H #define LOGMEMO_H #include #include #include class CLogMemo: public QPlainTextEdit { Q_OBJECT public: bool no_jump; bool terminal_output; CLogMemo (QWidget *parent = 0); void log (const QString &text); #if QT_VERSION >= 0x050000 void log_terminal (const QString &text); #endif void mouseDoubleClickEvent(QMouseEvent *event); signals: void double_click (const QString &text); }; #endif // LOGMEMO_H tea-qt-63.3.0/src/main.cpp000066400000000000000000000054121476733534200152350ustar00rootroot00000000000000/*************************************************************************** * Copyleft 2007-2024 by Peter Semiletov * * * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include "tea.h" #include "single_application_shared.h" extern CTEA *main_window; int main (int argc, char *argv[]) { #if defined (NO_SINGLE_APP) QApplication app (argc, argv); qApp->setApplicationName ("TEA"); main_window = new CTEA(); main_window->show(); return app.exec(); #else CSingleApplicationShared app (argc, argv, "tea unique id 1977"); bool single_mode = true; if (argc > 1) if (strcmp (argv[1], "--m") == 0) single_mode = false; //QStringList l = qApp->arguments(); // int size = l.size(); //if (size < 2) // return; /* if (single_mode && app.alreadyExists()) { if (argc > 1) for (int i = 1; i < argc; i++) { app.sendMessage (QString (argv[i])); } return 0; } */ if (single_mode && app.alreadyExists()) { if (argc > 1) for (int i = 1; i < argc; i++) { app.sendMessage (QString (argv[i])); } return 0; } #endif main_window = new CTEA(); #if !defined (NO_SINGLE_APP) QObject::connect (&app, SIGNAL(messageAvailable(QStringList)), main_window, SLOT(receiveMessageShared(QStringList))); #endif main_window->show(); return app.exec(); } tea-qt-63.3.0/src/miniz.h000066400000000000000000014130061476733534200151070ustar00rootroot00000000000000#ifndef MINIZ_EXPORT #define MINIZ_EXPORT #endif /* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateReset/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #pragma once /* Defines to completely disable specific portions of miniz.c: If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on * stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ /* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able * to get the current time, or */ /* get/set file times, and the C run-time funcs that get/set times won't be * called. */ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ /* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ /*#define MINIZ_NO_DEFLATE_APIS */ /* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ /*#define MINIZ_NO_INFLATE_APIS */ /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ /* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP * archive API's. */ /*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ /* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression * API's. */ /*#define MINIZ_NO_ZLIB_APIS */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent * conflicts against stock zlib. */ /*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ /* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ #ifdef MINIZ_NO_INFLATE_APIS #define MINIZ_NO_ARCHIVE_APIS #endif #ifdef MINIZ_NO_DEFLATE_APIS #define MINIZ_NO_ARCHIVE_WRITING_APIS #endif #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc * on Linux */ #define MINIZ_NO_TIME #endif #include #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ defined(__i386) || defined(__i486__) || defined(__i486) || \ defined(i386) || defined(__ia64__) || defined(__x86_64__) /* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ #define MINIZ_X86_OR_X64_CPU 1 #else #define MINIZ_X86_OR_X64_CPU 0 #endif /* Set MINIZ_LITTLE_ENDIAN only if not set */ #if !defined(MINIZ_LITTLE_ENDIAN) #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #else #if MINIZ_X86_OR_X64_CPU #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #endif #endif /* Using unaligned loads and stores causes errors when using UBSan */ #if defined(__has_feature) #if __has_feature(undefined_behavior_sanitizer) #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient * integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #define MINIZ_UNALIGNED_USE_MEMCPY #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || \ defined(_LP64) || defined(__LP64__) || defined(__ia64__) || \ defined(__x86_64__) /* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are * reasonably fast (and don't involve compiler generated calls to helper * functions). */ #define MINIZ_HAS_64BIT_REGISTERS 1 #else #define MINIZ_HAS_64BIT_REGISTERS 0 #endif #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API Definitions. */ /* For more compatibility with zlib, miniz.c uses unsigned long for some * parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ typedef unsigned long mz_ulong; /* mz_free() internally uses the MZ_FREE() macro (which by default calls free() * unless you've modified the MZ_MALLOC macro) to release a block allocated from * the heap. */ MINIZ_EXPORT void mz_free(void *p); #define MZ_ADLER32_INIT (1) /* mz_adler32() returns the initial adler-32 value to use when called with * ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) /* mz_crc32() returns the initial CRC-32 value to use when called with * ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); /* Compression strategies. */ enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; /* Method */ #define MZ_DEFLATED 8 /* Heap allocation callbacks. Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); /* Compression levels: 0-9 are the standard zlib-style levels, 10 is best * possible compression (not zlib compatible, and may be very slow), * MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; #define MZ_VERSION "11.0.2" #define MZ_VERNUM 0xB002 #define MZ_VER_MAJOR 11 #define MZ_VER_MINOR 2 #define MZ_VER_REVISION 0 #define MZ_VER_SUBREVISION 0 #ifndef MINIZ_NO_ZLIB_APIS /* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The * other values are for advanced use (refer to the zlib docs). */ enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; /* Return status codes. MZ_PARAM_ERROR is non-standard. */ enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; /* Window bits */ #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; /* Compression/decompression stream struct. */ typedef struct mz_stream_s { const unsigned char *next_in; /* pointer to next byte to read */ unsigned int avail_in; /* number of bytes available at next_in */ mz_ulong total_in; /* total number of bytes consumed so far */ unsigned char *next_out; /* pointer to next byte to write */ unsigned int avail_out; /* number of bytes that can be written to next_out */ mz_ulong total_out; /* total number of bytes produced so far */ char *msg; /* error msg (unused) */ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ mz_free_func zfree; /* optional heap free function (defaults to free) */ void *opaque; /* heap alloc function user pointer */ int data_type; /* data_type (unused) */ mz_ulong adler; /* adler32 of the source or uncompressed data */ mz_ulong reserved; /* not used */ } mz_stream; typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ MINIZ_EXPORT const char *mz_version(void); #ifndef MINIZ_NO_DEFLATE_APIS /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ /* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ /* level 1 enables a specially optimized compression function that's been * optimized purely for performance, not ratio. */ /* (This special func. is currently only enabled when * MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if the input parameters are bogus. */ /* MZ_MEM_ERROR on out of memory. */ MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); /* mz_deflateInit2() is like mz_deflate(), except with more control: */ /* Additional parameters: */ /* method must be MZ_DEFLATED */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with * zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no * header or footer) */ /* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); /* Quickly resets a compressor without having to reallocate anything. Same as * calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); /* mz_deflate() compresses the input to output, consuming as much of the input * and producing as much output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update * the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or * MZ_FINISH. */ /* Return values: */ /* MZ_OK on success (when flushing, or if more input is needed but not * available, and/or there's more output to be written but the output buffer is * full). */ /* MZ_STREAM_END if all input has been consumed and all output bytes have been * written. Don't call mz_deflate() on the stream anymore. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input and/or * output buffers are empty. (Fill up the input buffer or free up some output * space and try again.) */ MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); /* mz_deflateEnd() deinitializes a compressor: */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); /* mz_deflateBound() returns a (very) conservative upper bound on the amount of * data that could be generated by deflate(), assuming flush is set to only * MZ_NO_FLUSH or MZ_FINISH. */ MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); /* Single-call compression functions mz_compress() and mz_compress2(): */ /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on * failure. */ MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); /* mz_compressBound() returns a (very) conservative upper bound on the amount of * data that could be generated by calling mz_compress(). */ MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS /* Initializes a decompressor. */ MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); /* mz_inflateInit2() is like mz_inflateInit() with an additional option that * controls the window size and whether or not the stream has been wrapped with * a zlib header/footer: */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or * -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); /* Quickly resets a compressor without having to reallocate anything. Same as * calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); /* Decompresses the input stream to the output, consuming only as much of the * input as needed, and writing as much to the output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update * the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ /* On the first call, if flush is MZ_FINISH it's assumed the input and output * buffers are both sized large enough to decompress the entire stream in a * single call (this is slightly faster). */ /* MZ_FINISH implies that there are no more source bytes available beside * what's already in the input buffer, and that the output buffer is large * enough to hold the rest of the decompressed data. */ /* Return values: */ /* MZ_OK on success. Either more input is needed but not available, and/or * there's more output to be written but the output buffer is full. */ /* MZ_STREAM_END if all needed input has been consumed and all output bytes * have been written. For zlib streams, the adler-32 of the decompressed data * has also been verified. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_DATA_ERROR if the deflate stream is invalid. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input buffer is * empty but the inflater needs more input to continue, or if the output buffer * is not large enough. Call mz_inflate() again */ /* with more input data, or with more room in the output buffer (except when * using single call decompression, described above). */ MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); /* Deinitializes a decompressor. */ MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); /* Single-call decompression. */ /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on * failure. */ MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /* Returns a string description of the specified error code, or NULL if the * error code is invalid. */ MINIZ_EXPORT const char *mz_error(int err); /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used * as a drop-in replacement for the subset of zlib that miniz.c supports. */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you * use zlib in the same project. */ #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #ifndef MINIZ_NO_DEFLATE_APIS #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflateReset mz_inflateReset #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define uncompress2 mz_uncompress2 #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ #endif /* MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif #pragma once #include #include #include #include /* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef int64_t mz_int64; typedef uint64_t mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) /* Works around MSVC's spammy "warning C4127: conditional expression is * constant" message. */ #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #define MZ_FILE FILE #endif /* #ifdef MINIZ_NO_STDIO */ #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { mz_uint32 m_dummy1; mz_uint32 m_dummy2; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else #define MZ_TIME_T time_t #endif #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) #define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) \ ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) \ ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | \ ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | \ ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #define MZ_READ_LE64(p) \ (((mz_uint64)MZ_READ_LE32(p)) | \ (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) \ << 32U)) #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); #define MZ_UINT16_MAX (0xFFFFU) #define MZ_UINT32_MAX (0xFFFFFFFFU) #ifdef __cplusplus } #endif #pragma once #ifndef MINIZ_NO_DEFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression API Definitions */ /* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly * slower, and raw/dynamic blocks will be output more frequently). */ #define TDEFL_LESS_MEMORY 0 /* tdefl_init() compression flags logically OR'd together (low 12 bits contain * the max. number of probes per dictionary search): */ /* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes * per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap * compression), 4095=Huffman+LZ (slowest/best compression). */ enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; /* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before * the deflate data, and the Adler-32 of the source data at the end. Otherwise, * you'll get raw deflate data. */ /* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even * when not writing zlib headers). */ /* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more * efficient lazy parsing. */ /* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's * initialization time to the minimum, but the output may vary from run to run * given the same input (depending on the contents of memory). */ /* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ /* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ /* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ /* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ /* The low 12 bits are reserved to control the max # of hash probes per * dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; /* High level compression functions: */ /* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block * allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ /* flags: The max match finder probes (default is 128) logically OR'd against * the above flags. Higher probes are slower but improve compression. */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pOut_len will be set to the compressed data's size, which could be larger * than src_buf_len on uncompressible data. */ /* The caller must free() the returned block when it's no longer needed. */ MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in * memory. */ /* Returns 0 on failure. */ MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* Compresses an image to a compressed PNG file in memory. */ /* On entry: */ /* pImage, w, h, and num_chans describe the image to compress. num_chans may be * 1, 2, 3, or 4. */ /* The image pitch in bytes per scanline will be w*num_chans. The leftmost * pixel on the top scanline is stored first in memory. */ /* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ /* If flip is true, the image will be flipped on the Y axis (useful for OpenGL * apps). */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pLen_out will be set to the size of the PNG image file. */ /* The caller must mz_free() the returned heap block (which will typically be * larger than *pLen_out) when it's no longer needed. */ MINIZ_EXPORT void * tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); /* Output stream interface. The compressor uses this interface to write * compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); /* tdefl_compress_mem_to_output() compresses a block to an output stream. The * above helpers use this function internally. */ MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output( const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; /* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed * output block (using static/fixed Huffman codes). */ #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif /* The low-level tdefl functions below may be used directly if the above helper * functions aren't flexible enough. The low-level functions don't make any heap * allocations, unlike the above helper functions. */ typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1 } tdefl_status; /* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; /* tdefl's compression state structure. */ typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; /* Initializes the compressor. */ /* There is no corresponding deinit() function because the tdefl API's do not * dynamically allocate memory. */ /* pBut_buf_func: If NULL, output data will be supplied to the specified * callback. In this case, the user should call the tdefl_compress_buffer() API * for compression. */ /* If pBut_buf_func is NULL the user should always call the tdefl_compress() * API. */ /* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, * etc.) */ MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); /* Compresses a block of data, consuming as much of the specified input buffer * as possible, and writing as much compressed data to the specified output * buffer as possible. */ MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a * non-NULL tdefl_put_buf_func_ptr. */ /* tdefl_compress_buffer() always consumes the entire input buffer. */ MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); /* Create tdefl_compress() flags given zlib-style compression parameters. */ /* level may range from [0,10] (where 10 is absolute max compression, but may be * much slower on some files) */ /* window_bits may be -15 (raw deflate) or 15 (zlib) */ /* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, * MZ_RLE, or MZ_FIXED */ MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor structure in C so that */ /* non-C language bindings to tdefl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #pragma once /* ------------------- Low-level Decompression API Definitions */ #ifndef MINIZ_NO_INFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* Decompression flags used by tinfl_decompress(). */ /* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and * ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the * input is a raw deflate stream. */ /* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available * beyond the end of the supplied input buffer. If clear, the input buffer * contains all remaining input. */ /* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large * enough to hold the entire decompressed stream. If clear, the output buffer is * at least the size of the dictionary (typically 32KB). */ /* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the * decompressed bytes. */ enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; /* High level decompression functions: */ /* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block * allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data * to decompress. */ /* On return: */ /* Function returns a pointer to the decompressed data, or NULL on failure. */ /* *pOut_len will be set to the decompressed data's size, which could be larger * than src_buf_len on uncompressible data. */ /* The caller must call mz_free() on the returned block when it's no longer * needed. */ MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block * in memory. */ /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes * written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an * internal 32KB buffer, and a user provided callback function will be called to * flush the buffer. */ /* Returns 1 on success or 0 on failure. */ typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; #ifndef MINIZ_NO_MALLOC /* Allocate the tinfl_decompressor structure in C so that */ /* non-C language bindings to tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); #endif /* Max size of LZ dictionary. */ #define TINFL_LZ_DICT_SIZE 32768 /* Return status. */ typedef enum { /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ TINFL_STATUS_BAD_PARAM = -3, /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ TINFL_STATUS_ADLER32_MISMATCH = -2, /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ TINFL_STATUS_FAILED = -1, /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ TINFL_STATUS_DONE = 0, /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ TINFL_STATUS_NEEDS_MORE_INPUT = 1, /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ /* so I may need to add some code to address this. */ TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; /* Initializes the decompressor to its initial state. */ #define tinfl_init(r) \ do { \ (r)->m_state = 0; \ } \ MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 /* Main low-level decompressor coroutine function. This is the only function * actually needed for decompression. All the other functions are just * high-level helpers for improved usability. */ /* This is a universal API, i.e. it can be used as a building block to build any * desired higher level decompression API. In the limit case, it can be called * once per every byte input or output. */ MINIZ_EXPORT tinfl_status tinfl_decompress( tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); /* Internal/private bits follow. */ enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else #define TINFL_USE_64BIT_BITBUF 0 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ #pragma once /* ------------------- ZIP archive reading/writing */ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif enum { /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 }; typedef struct { /* Central directory file index. */ mz_uint32 m_file_index; /* Byte offset of this entry in the archive's central directory. Note we * currently only support up to UINT_MAX or less bytes in the central dir. */ mz_uint64 m_central_dir_ofs; /* These fields are copied directly from the zip's central dir. */ mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; /* File's compressed size. */ mz_uint64 m_comp_size; /* File's uncompressed size. Note, I've seen some old archives where directory * entries had 512 bytes for their uncompressed sizes, but when you try to * unpack them you actually get 0 bytes. */ mz_uint64 m_uncomp_size; /* Zip internal and external file attributes. */ mz_uint16 m_internal_attr; mz_uint32 m_external_attr; /* Entry's local header file offset in bytes. */ mz_uint64 m_local_header_ofs; /* Size of comment in bytes. */ mz_uint32 m_comment_size; /* MZ_TRUE if the entry appears to be a directory. */ mz_bool m_is_directory; /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip * doesn't support) */ mz_bool m_is_encrypted; /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a * compression method we support. */ mz_bool m_is_supported; /* Filename. If string ends in '/' it's a subdirectory entry. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; /* Comment field. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; #ifdef MINIZ_NO_TIME MZ_TIME_T m_padding; #else MZ_TIME_T m_time; #endif } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, /*After adding a compressed file, seek back to local file header and set the correct sizes*/ MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 } mz_zip_flags; typedef enum { MZ_ZIP_TYPE_INVALID = 0, MZ_ZIP_TYPE_USER, MZ_ZIP_TYPE_MEMORY, MZ_ZIP_TYPE_HEAP, MZ_ZIP_TYPE_FILE, MZ_ZIP_TYPE_CFILE, MZ_ZIP_TOTAL_TYPES } mz_zip_type; /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or * modify this enum. */ typedef enum { MZ_ZIP_NO_ERROR = 0, MZ_ZIP_UNDEFINED_ERROR, MZ_ZIP_TOO_MANY_FILES, MZ_ZIP_FILE_TOO_LARGE, MZ_ZIP_UNSUPPORTED_METHOD, MZ_ZIP_UNSUPPORTED_ENCRYPTION, MZ_ZIP_UNSUPPORTED_FEATURE, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, MZ_ZIP_NOT_AN_ARCHIVE, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, MZ_ZIP_UNSUPPORTED_MULTIDISK, MZ_ZIP_DECOMPRESSION_FAILED, MZ_ZIP_COMPRESSION_FAILED, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, MZ_ZIP_CRC_CHECK_FAILED, MZ_ZIP_UNSUPPORTED_CDIR_SIZE, MZ_ZIP_ALLOC_FAILED, MZ_ZIP_FILE_OPEN_FAILED, MZ_ZIP_FILE_CREATE_FAILED, MZ_ZIP_FILE_WRITE_FAILED, MZ_ZIP_FILE_READ_FAILED, MZ_ZIP_FILE_CLOSE_FAILED, MZ_ZIP_FILE_SEEK_FAILED, MZ_ZIP_FILE_STAT_FAILED, MZ_ZIP_INVALID_PARAMETER, MZ_ZIP_INVALID_FILENAME, MZ_ZIP_BUF_TOO_SMALL, MZ_ZIP_INTERNAL_ERROR, MZ_ZIP_FILE_NOT_FOUND, MZ_ZIP_ARCHIVE_TOO_LARGE, MZ_ZIP_VALIDATION_FAILED, MZ_ZIP_WRITE_CALLBACK_FAILED, MZ_ZIP_TOTAL_ERRORS } mz_zip_error; typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; /* We only support up to UINT32_MAX files in zip64 mode. */ mz_uint32 m_total_files; mz_zip_mode m_zip_mode; mz_zip_type m_zip_type; mz_zip_error m_last_error; mz_uint64 m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; mz_file_needs_keepalive m_pNeeds_keepalive; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef struct { mz_zip_archive *pZip; mz_uint flags; int status; mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; void *pWrite_buf; size_t out_blk_remain; tinfl_decompressor inflator; #ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint padding; #else mz_uint file_crc32; #endif } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ /* Inits a ZIP archive reader. */ /* These functions read and validate the archive's central directory. */ MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); #ifndef MINIZ_NO_STDIO /* Read a archive from a disk file. */ /* file_start_ofs is the file offset where the archive actually begins, or 0. */ /* actual_archive_size is the true total size of the archive, which may be * smaller than the file's actual size on disk. If zero the entire file is * treated as the archive. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); /* Read an archive from an already opened FILE, beginning at the current file * position. */ /* The archive is assumed to be archive_size bytes long. If archive_size is 0, * then the entire rest of the file is assumed to contain the archive. */ /* The FILE will NOT be closed when mz_zip_reader_end() is called. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); #endif /* Ends archive reading, freeing all allocations, and closing the input archive * file if mz_zip_reader_init_file() was used. */ MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); /* -------- ZIP reading or writing */ /* Clears a mz_zip_archive struct to all zeros. */ /* Important: This must be done before passing the struct to any mz_zip * functions. */ MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); /* Returns the total number of files in the archive. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. * These functions retrieve/manipulate this field. */ /* Note that the m_last_error functionality is not thread safe. */ MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); /* MZ_TRUE if the archive file entry is a directory entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the file is encrypted/strong encrypted. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the compression method is supported, and the file is not * encrypted, and the file is not a compressed patch file. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); /* Retrieves the filename of an archive file entry. */ /* Returns the number of bytes written to pFilename, or if filename_buf_size is * 0 this function returns the number of bytes needed to fully store the * filename. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); /* Attempts to locates a file in the archive's central directory. */ /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ /* Returns -1 if the file cannot be found. */ MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); /* Returns detailed information about an archive file entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); /* MZ_TRUE if the file is in zip64 format. */ /* A file is considered zip64 if it contained a zip64 end of central directory * marker, or if it contained any zip64 extended file information fields in the * central directory. */ MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); /* Returns the total central directory size in bytes. */ /* The current max supported size is <= MZ_UINT32_MAX. */ MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); /* Extracts a archive file to a memory buffer using no memory allocation. */ /* There must be at least enough room on the stack to store the inflator's state * (~34KB or so). */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc( mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); /* Extracts a archive file to a memory buffer. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); /* Extracts a archive file to a dynamically allocated heap buffer. */ /* The memory will be allocated via the mz_zip_archive's alloc/realloc * functions. */ /* Returns NULL and sets the last error on failure. */ MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); /* Extracts a archive file using a callback function to output the file's data. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback( mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback( mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); /* Extract a file iteratively */ MINIZ_EXPORT mz_zip_reader_extract_iter_state * mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); MINIZ_EXPORT mz_zip_reader_extract_iter_state * mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read( mz_zip_reader_extract_iter_state *pState, void *pvBuf, size_t buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state *pState); #ifndef MINIZ_NO_STDIO /* Extracts a archive file to a disk file and sets its last accessed and * modified times. */ /* This function only extracts files, not archive directory records. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file( mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); /* Extracts a archive file starting at the current position in the destination * FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile( mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); #endif #if 0 /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif /* This function compares the archive's local headers, the optional local zip64 * extended information block, and the optional descriptor following the * compressed data vs. the data in the central directory. */ /* It also validates that each file can be successfully uncompressed unless the * MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); /* Validates an entire archive by calling mz_zip_validate_file() on each file. */ MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); /* Misc utils/helpers, valid for ZIP reading or writing */ MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); #ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); #endif /* Universal end function - calls either mz_zip_reader_end() or * mz_zip_writer_end(). */ MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); /* -------- ZIP writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS /* Inits a ZIP archive writer. */ /*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init * or mz_zip_writer_init_v2*/ /*The output is streamable, i.e. file_ofs in mz_file_write_func always increases * only by n*/ MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap( mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2( mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); #ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2( mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif /* Converts a ZIP archive reader object into a writer object, to allow efficient * in-place file appends to occur on an existing archive. */ /* For archives opened using mz_zip_reader_init_file, pFilename must be the * archive's filename so it can be reopened for writing. If the file can't be * reopened, mz_zip_reader_end() will be called. */ /* For archives opened using mz_zip_reader_init_mem, the memory block must be * growable using the realloc callback (which defaults to realloc unless you've * overridden it). */ /* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's * user provided m_pWrite function cannot be NULL. */ /* Note: In-place archive modification is not recommended unless you know what * you're doing, because if execution stops or something goes wrong before */ /* the archive is finalized the file's central directory will be hosed. */ MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2_noreopen( mz_zip_archive *pZip, const char *pFilename, mz_uint flags); /* Adds the contents of a memory buffer to an archive. These functions record * the current local time into the archive. */ /* To add a directory entry, call this method with an archive name ending in a * forwardslash with an empty buffer. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or * just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_mem(), except you can specify a file comment field, * and optionally supply the function with already compressed data. */ /* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA * flag is specified. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex( mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2( mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); /* Adds the contents of a file to an archive. This function also records the * disk file's modified time into the archive. */ /* File data is supplied via a read callback function. User * mz_zip_writer_add_(c)file to add a file directly.*/ MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback( mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void *callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #ifndef MINIZ_NO_STDIO /* Adds the contents of a disk file to an archive. This function also records * the disk file's modified time into the archive. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or * just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_file( mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes); /* Like mz_zip_writer_add_file(), except the file data is read from the * specified FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile( mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif /* Adds a file to an archive by fully cloning the data from another archive. */ /* This function fully clones the source file's compressed data (no * recompression), along with its full filename, extra data (it may add or * modify the zip64 local header extra data field), and the optional descriptor * following the compressed data. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader( mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); /* Finalizes the archive by writing the central directory records followed by * the end of central directory record. */ /* After an archive is finalized, the only valid call on the mz_zip_archive * struct is mz_zip_writer_end(). */ /* An archive must be manually finalized by calling this function for it to be * valid. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); /* Finalizes a heap archive, returning a pointer to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc * callbacks. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); /* Ends archive writing, freeing all allocations, and closing the output file if * mz_zip_writer_init_file() was used. */ /* Note for the archive to be valid, it *must* have been finalized before ending * (this function will not do it for you). */ MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); /* -------- Misc. high-level helper functions: */ /* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) * appends a memory blob to a ZIP archive. */ /* Note this is NOT a fully safe operation. If it crashes or dies in some way * your archive can be left in a screwed up state (without a central directory). */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, * MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or * just set to MZ_DEFAULT_COMPRESSION. */ /* TODO: Perhaps add an option to leave the existing central dir in place in * case the add dies? We could then truncate the file (so the old central dir * would be at the end) if something goes wrong. */ MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); #ifndef MINIZ_NO_STDIO /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be * extracted. */ /* Returns NULL on failure. */ MINIZ_EXPORT void * mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2( const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); #endif #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifdef __cplusplus } #endif #endif /* MINIZ_NO_ARCHIVE_APIS */ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API's */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } /* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C * implementation that balances processor cache usage against speed": * http://www.geocities.com/malbrain/ */ #if 0 mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } #elif defined(USE_EXTERNAL_MZCRC) /* If USE_EXTERNAL_CRC is defined, an external module will export the * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. * Depending on the impl, it may be necessary to ~ the input/output crc values. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); #else /* Faster, but larger CPU cache footprint. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; while (buf_len >= 4) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; pByte_buf += 4; buf_len -= 4; } while (buf_len) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++pByte_buf; --buf_len; } return ~crc32; } #endif void mz_free(void *p) { MZ_FREE(p); } MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } #ifndef MINIZ_NO_ZLIB_APIS #ifndef MINIZ_NO_DEFLATE_APIS int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for (;;) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; /* Can't make forward progress without some input. */ } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; /* This is really over conservative. (And lame, but it's actually pretty * tricky to compute a true upper bound given the way tdefl's blocking works.) */ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong pSource_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__) /* In case mz_ulong is 64-bits (argh I hate longs). */ #else if ((mz_uint64)(pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; #endif stream.next_in = pSource; stream.avail_in = (mz_uint32)pSource_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflateReset(mz_streamp pStream) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; pDecomp = (inflate_state *)pStream->state; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; /* pDecomp->m_window_bits = window_bits */; return MZ_OK; } int mz_inflate(mz_streamp pStream, int flush) { inflate_state *pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state *)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { /* MZ_FINISH on the first call implies that the input and output buffers are * large enough to hold the entire compressed/decompressed file. */ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } /* flush != MZ_FINISH then we must assume there's more input. */ if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for (;;) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress( &pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ else if (flush == MZ_FINISH) { /* The output buffer MUST be large to hold the remaining uncompressed data * when flush==MZ_FINISH. */ if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's * at least 1 more byte on the way. If there's no more room left in the * output buffer then something is wrong. */ else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__) /* In case mz_ulong is 64-bits (argh I hate longs). */ #else if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; #endif stream.next_in = pSource; stream.avail_in = (mz_uint32)*pSource_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); *pSource_len = *pSource_len - stream.avail_in; if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_uncompress2(pDest, pDest_len, pSource, &source_len); } #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = {{MZ_OK, ""}, {MZ_STREAM_END, "stream end"}, {MZ_NEED_DICT, "need dictionary"}, {MZ_ERRNO, "file error"}, {MZ_STREAM_ERROR, "stream error"}, {MZ_DATA_ERROR, "data error"}, {MZ_MEM_ERROR, "out of memory"}, {MZ_BUF_ERROR, "buf error"}, {MZ_VERSION_ERROR, "version error"}, {MZ_PARAM_ERROR, "parameter error"}}; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif /*MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to */ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_DEFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression (independent from all decompression * API's) */ /* Purposely making these tables static for faster init and thread safety. */ static const mz_uint16 s_tdefl_len_sym[256] = { 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285}; static const mz_uint8 s_tdefl_len_extra[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0}; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17}; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; /* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted * values. */ typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_ARR(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32 *pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq *t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } /* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, * alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next = 1; next < n - 1; next++) { if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key; if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); } A[n - 2].m_key = 0; for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; while (avbl > 0) { while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } while (avbl > used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2 * used; dpth++; used = 0; } } /* Limits canonical Huffman code table's max code size. */ enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_ARR(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); MZ_CLEAR_ARR(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) \ do { \ mz_uint bits = b; \ mz_uint len = l; \ MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); \ d->m_bits_in += len; \ while (d->m_bits_in >= 8) { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } \ MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() \ { \ if (rle_repeat_count) { \ if (rle_repeat_count < 3) { \ d->m_huff_count[2][prev_code_size] = \ (mz_uint16)(d->m_huff_count[2][prev_code_size] + \ rle_repeat_count); \ while (rle_repeat_count--) \ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } else { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 16; \ packed_code_sizes[num_packed_code_sizes++] = \ (mz_uint8)(rle_repeat_count - 3); \ } \ rle_repeat_count = 0; \ } \ } #define TDEFL_RLE_ZERO_CODE_SIZE() \ { \ if (rle_z_count) { \ if (rle_z_count < 3) { \ d->m_huff_count[2][0] = \ (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ while (rle_z_count--) \ packed_code_sizes[num_packed_code_sizes++] = 0; \ } else if (rle_z_count <= 10) { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 17; \ packed_code_sizes[num_packed_code_sizes++] = \ (mz_uint8)(rle_z_count - 3); \ } else { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 18; \ packed_code_sizes[num_packed_code_sizes++] = \ (mz_uint8)(rle_z_count - 11); \ } \ rle_z_count = 0; \ } \ } static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes [2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS( d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && \ MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) \ { \ bit_buffer |= (((mz_uint64)(b)) << bits_in); \ bits_in += (l); \ } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0]; mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && \ MINIZ_HAS_64BIT_REGISTERS */ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static const mz_uint s_tdefl_num_probes[11] = {0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { const mz_uint8 cmf = 0x78; mz_uint8 flg, flevel = 3; mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); /* Determine compression level by reversing the process in * tdefl_create_comp_flags_from_zip_params() */ for (i = 0; i < mz_un; i++) if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; if (i < 2) flevel = 0; else if (i < 6) flevel = 1; else if (i == 6) flevel = 2; header = cmf << 8 | (flevel << 6); header += 31 - (header % 31); flg = header & 0xFF; TDEFL_PUT_BITS(cmf, 8); TDEFL_PUT_BITS(flg, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); /* If the block gets expanded, forget the current contents of the output * buffer and send a raw block instead. */ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS( d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN( (size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8 *p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16 *p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) #define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || \ ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16 *)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) continue; p = s; probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || \ ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && \ (d->m_dict[probe_pos + match_len - 1] == c1)) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8 *p) { mz_uint32 ret; memcpy(&ret, p, sizeof(mz_uint32)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) #endif static mz_bool tdefl_compress_fast(tdefl_compressor *d) { /* Faster, minimally featured LZRW1-style match+parse loop with better * register utilization. Intended for applications where raw throughput is * valued more highly than ratio. */ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32( d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); #else *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; #endif pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; /* Update dictionary and hash chains. Keeps the lookahead size equal to * TDEFL_MAX_MATCH_LEN. */ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; /* Simple lazy/greedy parsing state machine. */ len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } /* Move the lookahead forward by len_to_move bytes. */ d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); /* Check if it's time to flush the current LZ codes to the internal output * buffer. */ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_ARR(d->m_hash); MZ_CLEAR_ARR(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_ARR(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; *d->m_pLZ_flags = 0; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_ARR(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output( pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8 *)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output( pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } /* level may actually range from [0,10] (10 is a "hidden" max level, where we * want a bit more compression and it's fine if throughput to fall off a cliff * on some files). */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4204) /* nonstandard extension used : non-constant \ aggregate initializer (also supported by \ GNU C and C99, so no big deal) */ #endif /* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was * defined. */ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } /* write dummy header */ for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); /* compress image data */ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } /* write real header */ *pLen_out = out_buf.m_size - 41; { static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; mz_uint8 pnghdr[41] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x41, 0x54}; pnghdr[18] = (mz_uint8)(w >> 8); pnghdr[19] = (mz_uint8)w; pnghdr[22] = (mz_uint8)(h >> 8); pnghdr[23] = (mz_uint8)h; pnghdr[25] = chans[num_chans]; pnghdr[33] = (mz_uint8)(*pLen_out >> 24); pnghdr[34] = (mz_uint8)(*pLen_out >> 16); pnghdr[35] = (mz_uint8)(*pLen_out >> 8); pnghdr[36] = (mz_uint8)*pLen_out; c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); memcpy(out_buf.m_pBuf, pnghdr, 41); } /* write footer (IDAT CRC-32, followed by IEND chunk) */ if (!tdefl_output_buffer_putter( "\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); /* compute final size of file, grab compressed data buffer and return */ *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we * can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's * where #defined out) */ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc(void) { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } void tdefl_compressor_free(tdefl_compressor *pComp) { MZ_FREE(pComp); } #endif #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_INFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Decompression (completely independent from all * compression API's) */ #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN \ switch (r->m_state) { \ case 0: #define TINFL_CR_RETURN(state_index, result) \ do { \ status = result; \ r->m_state = state_index; \ goto common_exit; \ case state_index:; \ } \ MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) \ do { \ for (;;) { \ TINFL_CR_RETURN(state_index, result); \ } \ } \ MZ_MACRO_END #define TINFL_CR_FINISH } #define TINFL_GET_BYTE(state_index, c) \ do { \ while (pIn_buf_cur >= pIn_buf_end) { \ TINFL_CR_RETURN(state_index, \ (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) \ ? TINFL_STATUS_NEEDS_MORE_INPUT \ : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ } \ c = *pIn_buf_cur++; \ } \ MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) \ do { \ mz_uint c; \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) \ do { \ if (num_bits < (mz_uint)(n)) { \ TINFL_NEED_BITS(state_index, n); \ } \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) \ do { \ if (num_bits < (mz_uint)(n)) { \ TINFL_NEED_BITS(state_index, n); \ } \ b = bit_buf & ((1 << (n)) - 1); \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END /* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes * remaining in the input buffer falls below 2. */ /* It reads just enough bytes from the input stream that are needed to decode * the next Huffman code (and absolutely no more). It works by trying to fully * decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. * If this fails, it reads another byte, and tries again until it succeeds or * until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ #define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ do { \ temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ } \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < 15); /* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex * than you would initially expect because the zlib API expects the decompressor * to never read */ /* beyond the final byte of the deflate stream. (In other words, when this macro * wants to read another byte from the input, it REALLY needs another byte in * order to fully */ /* decode the next Huffman code.) Handling this properly is particularly * important on raw deflate (non-zlib) streams, which aren't followed by a byte * aligned adler-32. */ /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in * input buffer, but we also need to handle the case where the user passes in * 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't * kick in here on this code. This is much trickier. */ #define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ do { \ int temp; \ mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | \ (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ pIn_buf_cur += 2; \ num_bits += 16; \ } \ } \ if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ bit_buf >>= code_len; \ num_bits -= code_len; \ } \ MZ_MACRO_END static void tinfl_clear_tree(tinfl_decompressor *r) { if (r->m_type == 0) MZ_CLEAR_ARR(r->m_tree_0); else if (r->m_type == 1) MZ_CLEAR_ARR(r->m_tree_1); else MZ_CLEAR_ARR(r->m_tree_2); } tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const mz_uint8 s_length_extra[31] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0}; static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; static const mz_uint16 s_min_table_sizes[3] = {257, 1, 4}; mz_int16 *pTrees[3]; mz_uint8 *pCode_sizes[3]; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer * is large enough to hold the entire output file (in which case it doesn't * matter). */ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } pTrees[0] = r->m_tree_0; pTrees[1] = r->m_tree_1; pTrees[2] = r->m_tree_2; pCode_sizes[0] = r->m_code_size_0; pCode_sizes[1] = r->m_code_size_1; pCode_sizes[2] = r->m_code_size_2; num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_code_size_0; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_code_size_1, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_ARR(r->m_code_size_2); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; mz_int16 *pLookUp; mz_int16 *pTree; mz_uint8 *pCode_size; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pLookUp = r->m_look_up[r->m_type]; pTree = pTrees[r->m_type]; pCode_size = pCode_sizes[r->m_type]; MZ_CLEAR_ARR(total_syms); TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); tinfl_clear_tree(r); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pCode_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pLookUp[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTree[-tree_cur - 1]) { pTree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) { mz_uint8 *pSrc; for (;;) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32) * 2); #else ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; #endif pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif while (counter > 2) { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; counter -= 3; } if (counter > 0) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); /* Ensure byte alignment and put back any bytes from the bitbuf if we've * looked ahead too far on gzip, or other Deflate streams followed by * arbitrary data. */ /* I'm being super conservative here. A number of simplifications can be made * to the byte alignment part, and the Adler32 check shouldn't ever need to * worry about reading from the bitbuf now. */ TINFL_SKIP_BITS(32, num_bits & 7); while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: /* As long as we aren't telling the caller that we NEED more input to make * forward progress: */ /* Put back any bytes from the bitbuf in case we've looked ahead too far on * gzip, or other Deflate streams followed by arbitrary data. */ /* We need to be very careful here to NOT push back any bytes we definitely * know we need to make forward progress, though, or we'll lock the caller up * into an inf loop. */ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) { while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } } r->m_num_bits = num_bits; r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } /* Higher level helper functions. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for (;;) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress( &decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; memset(pDict, 0, TINFL_LZ_DICT_SIZE); tinfl_init(&decomp); for (;;) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } #ifndef MINIZ_NO_MALLOC tinfl_decompressor *tinfl_decompressor_alloc(void) { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) tinfl_init(pDecomp); return pDecomp; } void tinfl_decompressor_free(tinfl_decompressor *pDecomp) { MZ_FREE(pDecomp); } #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * Copyright 2016 Martin Raiber * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- .ZIP archive reading */ #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #if defined(_MSC_VER) || defined(__MINGW64__) #define WIN32_LEAN_AND_MEAN #include static WCHAR *mz_utf8z_to_widechar(const char *str) { int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); WCHAR *wStr = (WCHAR *)malloc(reqChars * sizeof(WCHAR)); MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, reqChars); return wStr; } static FILE *mz_fopen(const char *pFilename, const char *pMode) { WCHAR *wFilename = mz_utf8z_to_widechar(pFilename); WCHAR *wMode = mz_utf8z_to_widechar(pMode); FILE *pFile = NULL; #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN pFile = _wfopen(wFilename, wMode); #else errno_t err = _wfopen_s(&pFile, wFilename, wMode); #endif free(wFilename); free(wMode); #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN return pFile; #else return err ? NULL : pFile; #endif } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { WCHAR *wPath = mz_utf8z_to_widechar(pPath); WCHAR *wMode = mz_utf8z_to_widechar(pMode); FILE *pFile = NULL; #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN pFile = _wfreopen(wPath, wMode, pStream); #else errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); #endif free(wPath); free(wMode); #ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN return pFile; #else return err ? NULL : pFile; #endif } static int mz_stat64(const char *path, struct __stat64 *buffer) { WCHAR *wPath = mz_utf8z_to_widechar(path); int res = _wstat64(wPath, buffer); free(wPath); return res; } static int mz_mkdir(const char *pDirname) { WCHAR *wDirname = mz_utf8z_to_widechar(pDirname); int res = _wmkdir(wDirname); free(wDirname); return res; } #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat64 #define MZ_FILE_STAT mz_stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mz_mkdir(d) #elif defined(__MINGW32__) || defined(__WATCOMC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) _mkdir(d) #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #if defined(_WIN32) || defined(_WIN64) #define MZ_MKDIR(d) _mkdir(d) #else #define MZ_MKDIR(d) mkdir(d, 0755) #endif #elif defined(__USE_LARGEFILE64) /* gcc, clang */ #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mkdir(d, 0755) #elif defined(__APPLE__) || defined(__FreeBSD__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen(p, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mkdir(d, 0755) #else #pragma message( \ "Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") #ifndef MINIZ_NO_TIME #include #endif #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #ifdef __STRICT_ANSI__ #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #else #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #endif #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #define MZ_MKDIR(d) mkdir(d, 0755) #endif /* #ifdef _MSC_VER */ #endif /* #ifdef MINIZ_NO_STDIO */ #ifndef CHMOD // Upon successful completion, a value of 0 is returned. // Otherwise, a value of -1 is returned and errno is set to indicate the error. // int chmod(const char *path, mode_t mode); #define CHMOD(f, m) chmod(f, m) #endif #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) /* Various ZIP archive enums. To completely avoid cross platform compiler * alignment and platform endian issues, miniz.c doesn't use structs for any of * this stuff. */ enum { /* ZIP archive identifiers and record sizes */ MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, /* ZIP64 archive identifier and record sizes */ MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, /* Central directory header record offsets */ MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, /* Local directory header offsets */ MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, /* End of central directory offsets */ MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, /* ZIP64 End of central directory locator offsets */ MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ /* ZIP64 End of central directory header offsets */ MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; /* The flags passed in when the archive is initially opened. */ mz_uint32 m_init_flags; /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ mz_bool m_zip64; /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 * will also be slammed to true too, even if we didn't find a zip64 end of * central dir header, etc.) */ mz_bool m_zip64_has_extended_info_fields; /* These fields are used by the file, FILE, memory, and memory/heap read/write * helpers. */ MZ_FILE *m_pFile; mz_uint64 m_file_archive_start_ofs; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) \ (array_ptr)->m_element_size = element_size #if defined(DEBUG) || defined(_DEBUG) static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) { MZ_ASSERT(index < pArray->m_size); return index; } #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ ((element_type *)((array_ptr) \ ->m_p))[mz_zip_array_range_check(array_ptr, index)] #else #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ ((element_type *)((array_ptr)->m_p))[index] #endif static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) { memset(pArray, 0, sizeof(mz_zip_array)); pArray->m_element_size = element_size; } static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; if (n > 0) memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif /* #ifdef _MSC_VER */ *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifndef MINIZ_NO_STDIO #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) { struct MZ_FILE_STAT_STRUCT file_stat; /* On Linux with x86 glibc, this call will fail on large files (I think >= * 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; *pTime = file_stat.st_mtime; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) { struct utimbuf t; memset(&t, 0, sizeof(t)); t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_TIME */ static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) { if (pZip) pZip->m_last_error = err_num; return MZ_FALSE; } static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; pZip->m_last_error = MZ_ZIP_NO_ERROR; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_init_flags = flags; pZip->m_pState->m_zip64 = MZ_FALSE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; pZip->m_zip_mode = MZ_ZIP_MODE_READING; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT( pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) \ do { \ mz_uint32 t = a; \ a = b; \ b = t; \ } \ MZ_MACRO_END /* Heap sort of lowercased filenames, used to help accelerate plain central * directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), * but it could allocate memory.) */ static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices; mz_uint32 start, end; const mz_uint32 size = pZip->m_total_files; if (size <= 1U) return; pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); start = (size - 2U) >> 1U; for (;;) { mz_uint64 child, root = start; for (;;) { if ((child = (root << 1U) + 1U) >= size) break; child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } if (!start) break; start--; } end = size - 1; while (end > 0) { mz_uint64 child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for (;;) { if ((child = (root << 1U) + 1U) >= end) break; child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) { mz_int64 cur_file_ofs; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; /* Basic sanity checks - reject files which are too small */ if (pZip->m_archive_size < record_size) return MZ_FALSE; /* Find the record by scanning the file from the end towards the beginning. */ cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for (;;) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) { mz_uint s = MZ_READ_LE32(pBuf + i); if (s == record_sig) { if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) break; } } if (i >= 0) { cur_file_ofs += i; break; } /* Give up if we've searched the entire file, or we've gone back "too far" * (~64kb) */ if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } *pOfs = cur_file_ofs; return MZ_TRUE; } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) { mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; mz_uint64 cdir_ofs = 0; mz_int64 cur_file_ofs = 0; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); mz_uint32 zip64_end_of_central_dir_locator_u32 [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; mz_uint32 zip64_end_of_central_dir_header_u32 [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; mz_uint64 zip64_end_of_central_dir_ofs = 0; /* Basic sanity checks - reject files which are too small, and check the first * 4 bytes of the file to make sure a local header is there. */ if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_locate_header_sig( pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); /* Read and verify the end of central directory record. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { zip64_end_of_central_dir_ofs = MZ_READ_LE64( pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { pZip->m_pState->m_zip64 = MZ_TRUE; } } } } } pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if (pZip->m_pState->m_zip64) { mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64( pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64( pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64( pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (zip64_total_num_of_disks != 1U) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); /* Check for miniz's practical limits */ if (zip64_cdir_total_entries > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; /* Check for miniz's current practical limits (sorry, this should be enough * for millions of files) */ if (zip64_size_of_central_directory > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); cdir_size = (mz_uint32)zip64_size_of_central_directory; num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); } if (pZip->m_total_files != cdir_entries_on_this_disk) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; /* Read the entire central directory into a heap block, and allocate another * heap block to hold the unsorted central dir file record offsets, and * possibly another to hold the sorted indices. */ if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); /* Now create an index into the central directory file records, do some * basic sanity checking on each record */ p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; mz_uint64 comp_size, decomp_size, local_header_ofs; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && (ext_data_size) && (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) { /* Attempt to find zip64 extended information field in the entry's extra * data */ mz_uint32 extra_size_remaining = ext_data_size; if (extra_size_remaining) { const mz_uint8 *pExtra_data; void *buf = NULL; if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) { buf = MZ_MALLOC(ext_data_size); if (buf == NULL) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (mz_uint8 *)buf; } else { pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; } do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) { MZ_FREE(buf); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { /* Ok, the archive didn't have any zip64 headers but it uses a * zip64 extended information field so mark it as zip64 anyway * (this can occur with infozip's zip util when it reads * compresses files from stdin). */ pZip->m_pState->m_zip64 = MZ_TRUE; pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); MZ_FREE(buf); } } /* I've seen archives that aren't marked as zip64 that uses zip64 ext * data, argh */ if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); if (comp_size != MZ_UINT32_MAX) { if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } void mz_zip_zero_struct(mz_zip_archive *pZip) { if (pZip) MZ_CLEAR_PTR(pZip); } static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_bool status = MZ_TRUE; if (!pZip) return MZ_FALSE; if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) { if (set_last_error) pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { return mz_zip_reader_end_internal(pZip, MZ_TRUE); } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) { if ((!pZip) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) { if (!pMem) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pNeeds_keepalive = NULL; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); } mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { mz_uint64 file_size; MZ_FILE *pFile; if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); file_size = archive_size; if (!file_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); } file_size = MZ_FTELL64(pFile); } /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_zip_type = MZ_ZIP_TYPE_FILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { mz_uint64 file_size; MZ_FILE *pFile; if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pFile = MZ_FOPEN(pFilename, "r+b"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); file_size = archive_size; if (!file_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); } file_size = MZ_FTELL64(pFile); } /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { MZ_FCLOSE(pFile); return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_zip_type = MZ_ZIP_TYPE_FILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) { mz_uint64 cur_file_ofs; if ((!pZip) || (!pFile)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); cur_file_ofs = MZ_FTELL64(pFile); if (!archive_size) { if (MZ_FSEEK64(pFile, 0, SEEK_END)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); archive_size = MZ_FTELL64(pFile) - cur_file_ofs; if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = archive_size; pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end_internal(pZip, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; } mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) { mz_uint bit_flag; mz_uint method; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); if ((method != 0) && (method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); return MZ_FALSE; } if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); return MZ_FALSE; } if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, attribute_mapping_id, external_attr; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } /* Bugfix: This code was also checking if the internal attribute was non-zero, * which wasn't correct. */ /* Most/all zip writers (hopefully) set DOS file/directory attributes in the * low 16-bits, so check for the DOS directory flag and ignore the source OS * ID in the created by field. */ /* FIXME: Remove this check? Is it necessary - we already check the filename. */ attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; (void)attribute_mapping_id; external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) { return MZ_TRUE; } return MZ_FALSE; } static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) { mz_uint n; const mz_uint8 *p = pCentral_dir_header; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_FALSE; if ((!p) || (!pStat)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Extract fields from the central directory record. */ pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); /* Copy as much of the filename and comment as possible. */ n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; /* Set some flags for convienance */ pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); /* See if we need to read any zip64 extended information fields. */ /* Confusingly, these zip64 fields can be present even on non-zip64 archives * (Debian zip on a huge files from stdin piped to stdout creates them). */ if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) { /* Attempt to find zip64 extended information field in the entry's extra * data */ mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); if (extra_size_remaining) { const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); do { mz_uint32 field_id; mz_uint32 field_data_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; mz_uint32 field_data_remaining = field_data_size; if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_TRUE; if (pStat->m_uncomp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_uncomp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_comp_size == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_comp_size = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } if (pStat->m_local_header_ofs == MZ_UINT32_MAX) { if (field_data_remaining < sizeof(mz_uint64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); pField_data += sizeof(mz_uint64); field_data_remaining -= sizeof(mz_uint64); } break; } pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; } while (extra_size_remaining); } } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_sorted_central_dir_offsets, mz_uint32, 0); const mz_uint32 size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); if (pIndex) *pIndex = 0; if (size) { /* yes I could use uint32_t's, but then we would have to add some special * case checks in the loop, argh, and */ /* honestly the major expense here on 32-bit CPU's will still be the * filename compare */ mz_int64 l = 0, h = (mz_int64)size - 1; while (l <= h) { mz_int64 m = l + ((h - l) >> 1); mz_uint32 file_index = pIndices[(mz_uint32)m]; int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } else if (comp < 0) l = m + 1; else h = m - 1; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint32 index; if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) return -1; else return (int)index; } mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) { mz_uint file_index; size_t name_len, comment_len; if (pIndex) *pIndex = 0; if ((!pZip) || (!pZip->m_pState) || (!pName)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* See if we can use a binary search */ if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) { return mz_zip_locate_file_binary_search(pZip, pName, pIndex); } /* Locate the entry by scanning the entire central directory */ name_len = strlen(pName); if (name_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); comment_len = pComment ? strlen(pComment) : 0; if (comment_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) { if (pIndex) *pIndex = file_index; return MZ_TRUE; } } return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } static mz_bool mz_zip_reader_extract_to_mem_no_alloc1( mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (st) { file_stat = *st; } else if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Ensure supplied output buffer is large enough. */ needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); /* Read and parse the local directory entry. */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) { if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); } #endif return MZ_TRUE; } /* Decompress the file either directly from memory or from a file input * buffer. */ tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { /* Read directly from the archive in memory. */ pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { /* Use a user provided read buffer. */ if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { /* Temporarily allocate a read buffer. */ read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { /* The size_t cast here should be OK because we've verified that the output * buffer is >= file_stat.m_uncomp_size above */ size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress( &inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); status = TINFL_STATUS_FAILED; } #endif } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_zip_archive_file_stat file_stat; mz_uint64 alloc_size; void *pBuf; if (pSize) *pSize = 0; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return NULL; alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); return NULL; } if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint file_crc32 = MZ_CRC32_INIT; #endif mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); /* Read and do some minimal validation of the local directory entry (this * doesn't crack the zip64 stuff, which we already have from the central dir) */ cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); /* Decompress the file either directly from memory or from a file input * buffer. */ if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data. */ if (pZip->m_pState->m_pMem) { if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; } else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); #endif } cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { file_crc32 = (mz_uint32)mz_crc32( file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); } #endif if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); status = TINFL_STATUS_FAILED; } else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress( &inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); status = TINFL_STATUS_FAILED; break; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); #endif if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (out_buf_ofs != file_stat.m_uncomp_size) { mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (file_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); status = TINFL_STATUS_FAILED; } #endif } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } mz_zip_reader_extract_iter_state * mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_reader_extract_iter_state *pState; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; /* Argument sanity check */ if ((!pZip) || (!pZip->m_pState)) return NULL; /* Allocate an iterator status structure */ pState = (mz_zip_reader_extract_iter_state *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); if (!pState) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return NULL; } /* Fetch file details */ if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Encryption and patch files are not supported. */ if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* This function only supports decompressing stored and deflate. */ if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) { mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Init state - save args */ pState->pZip = pZip; pState->flags = flags; /* Init state - reset variables to defaults */ pState->status = TINFL_STATUS_DONE; #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS pState->file_crc32 = MZ_CRC32_INIT; #endif pState->read_buf_ofs = 0; pState->out_buf_ofs = 0; pState->pRead_buf = NULL; pState->pWrite_buf = NULL; pState->out_blk_remain = 0; /* Read and parse the local directory entry. */ pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } /* Decompress the file either directly from memory or from a file input * buffer. */ if (pZip->m_pState->m_pMem) { pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; pState->comp_remaining = pState->file_stat.m_comp_size; } else { if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, therefore intermediate read buffer required */ pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } else { /* Decompression not required - we will be reading directly into user * buffer, no temp buf required */ pState->read_buf_size = 0; } pState->read_buf_avail = 0; pState->comp_remaining = pState->file_stat.m_comp_size; } if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { /* Decompression required, init decompressor */ tinfl_init(&pState->inflator); /* Allocate write buffer */ if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pState->pRead_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); pZip->m_pFree(pZip->m_pAlloc_opaque, pState); return NULL; } } return pState; } mz_zip_reader_extract_iter_state * mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_uint32 file_index; /* Locate file index by name */ if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return NULL; /* Construct iterator */ return mz_zip_reader_extract_iter_new(pZip, file_index, flags); } size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state *pState, void *pvBuf, size_t buf_size) { size_t copied_to_caller = 0; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) return 0; if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) { /* The file is stored or the caller has requested the compressed data, calc * amount to return. */ copied_to_caller = (size_t)MZ_MIN(buf_size, pState->comp_remaining); /* Zip is in memory....or requires reading from a file? */ if (pState->pZip->m_pState->m_pMem) { /* Copy data to caller's buffer */ memcpy(pvBuf, pState->pRead_buf, copied_to_caller); pState->pRead_buf = ((mz_uint8 *)pState->pRead_buf) + copied_to_caller; } else { /* Read directly into caller's buffer */ if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) { /* Failed to read all that was asked for, flag failure and alert user */ mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; copied_to_caller = 0; } } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Compute CRC if not returning compressed data only */ if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) pState->file_crc32 = (mz_uint32)mz_crc32( pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); #endif /* Advance offsets, dec counters */ pState->cur_file_ofs += copied_to_caller; pState->out_buf_ofs += copied_to_caller; pState->comp_remaining -= copied_to_caller; } else { do { /* Calc ptr to write buffer - given current output pos and block size */ mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); /* Calc max output size - given current output pos and block size */ size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if (!pState->out_blk_remain) { /* Read more data from file if none available (and reading from file) */ if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) { /* Calc read size */ pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) { mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Advance offsets, dec counters */ pState->cur_file_ofs += pState->read_buf_avail; pState->comp_remaining -= pState->read_buf_avail; pState->read_buf_ofs = 0; } /* Perform decompression */ in_buf_size = (size_t)pState->read_buf_avail; pState->status = tinfl_decompress( &pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); pState->read_buf_avail -= in_buf_size; pState->read_buf_ofs += in_buf_size; /* Update current output block size remaining */ pState->out_blk_remain = out_buf_size; } if (pState->out_blk_remain) { /* Calc amount to return. */ size_t to_copy = MZ_MIN((buf_size - copied_to_caller), pState->out_blk_remain); /* Copy data to caller's buffer */ memcpy((mz_uint8 *)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Perform CRC */ pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); #endif /* Decrement data consumed from block */ pState->out_blk_remain -= to_copy; /* Inc output offset, while performing sanity check */ if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; break; } /* Increment counter of data copied to caller */ copied_to_caller += to_copy; } } while ((copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT))); } /* Return how many bytes were copied into user buffer */ return copied_to_caller; } mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state *pState) { int status; /* Argument sanity check */ if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) return MZ_FALSE; /* Was decompression completed and requested? */ if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { /* Make sure the entire file was decompressed, and check its CRC. */ if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) { mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); pState->status = TINFL_STATUS_FAILED; } #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS else if (pState->file_crc32 != pState->file_stat.m_crc32) { mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); pState->status = TINFL_STATUS_FAILED; } #endif } /* Free buffers */ if (!pState->pZip->m_pState->m_pMem) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); if (pState->pWrite_buf) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); /* Save status */ status = pState->status; /* Free context */ pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); return status == TINFL_STATUS_DONE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); status = mz_zip_reader_extract_to_callback( pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) { if (status) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) { mz_zip_archive_file_stat file_stat; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); return mz_zip_reader_extract_to_callback( pZip, file_index, mz_zip_file_write_callback, pFile, flags); } mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) return MZ_FALSE; return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); } #endif /* #ifndef MINIZ_NO_STDIO */ static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_uint32 *p = (mz_uint32 *)pOpaque; (void)file_ofs; *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); return n; } mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) { mz_zip_archive_file_stat file_stat; mz_zip_internal_state *pState; const mz_uint8 *pCentral_dir_header; mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint64 local_header_ofs = 0; mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; mz_bool has_data_descriptor; mz_uint32 local_header_bit_flags; mz_zip_array file_data_array; mz_zip_array_init(&file_data_array, 1); if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (file_index > pZip->m_total_files) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) return MZ_FALSE; /* A directory or zero length file */ if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) return MZ_TRUE; /* Encryption and patch files are not supported. */ if (file_stat.m_is_encrypted) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); /* This function only supports stored and deflate. */ if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); if (!file_stat.m_is_supported) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); /* Read and parse the local directory entry. */ local_header_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); has_data_descriptor = (local_header_bit_flags & 8) != 0; if (local_header_filename_len != strlen(file_stat.m_filename)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (!mz_zip_array_resize( pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); goto handle_failure; } if (local_header_filename_len) { if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } /* I've seen 1 archive that had the same pathname, but used backslashes in * the local dir and forward slashes in the central dir. Do we care about * this? For now, this case will fail validation. */ if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_uint32 extra_size_remaining = local_header_extra_len; const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); goto handle_failure; } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } /* TODO: parse local header extra data when local_header_comp_size is * 0xFFFFFFFF! (big_descriptor.zip) */ /* I've seen zips in the wild with the data descriptor bit set, but proper * local header values and bogus data descriptors */ if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) { mz_uint8 descriptor_buf[32]; mz_bool has_id; const mz_uint8 *pSrc; mz_uint32 file_crc32; mz_uint64 comp_size = 0, uncomp_size = 0; mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); goto handle_failure; } has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; file_crc32 = MZ_READ_LE32(pSrc); if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); } else { comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); } if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } else { if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); goto handle_failure; } } mz_zip_array_clear(pZip, &file_data_array); if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) { if (!mz_zip_reader_extract_to_callback( pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) return MZ_FALSE; /* 1 more check to be sure, although the extract checks too. */ if (uncomp_crc32 != file_stat.m_crc32) { mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); return MZ_FALSE; } } return MZ_TRUE; handle_failure: mz_zip_array_clear(pZip, &file_data_array); return MZ_FALSE; } mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) { mz_zip_internal_state *pState; mz_uint32 i; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Basic sanity checks */ if (!pState->m_zip64) { if (pZip->m_total_files > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (pZip->m_archive_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } else { if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } for (i = 0; i < pZip->m_total_files; i++) { if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) { mz_uint32 found_index; mz_zip_archive_file_stat stat; if (!mz_zip_reader_file_stat(pZip, i, &stat)) return MZ_FALSE; if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) return MZ_FALSE; /* This check can fail if there are duplicate filenames in the archive * (which we don't check for when writing - that's up to the user) */ if (found_index != i) return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); } if (!mz_zip_validate_file(pZip, i, flags)) return MZ_FALSE; } return MZ_TRUE; } mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if ((!pMem) || (!size)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) { mz_bool success = MZ_TRUE; mz_zip_archive zip; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; if (!pFilename) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } mz_zip_zero_struct(&zip); if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) { if (pErr) *pErr = zip.m_last_error; return MZ_FALSE; } if (!mz_zip_validate_archive(&zip, flags)) { actual_err = zip.m_last_error; success = MZ_FALSE; } if (!mz_zip_reader_end_internal(&zip, success)) { if (!actual_err) actual_err = zip.m_last_error; success = MZ_FALSE; } if (pErr) *pErr = actual_err; return success; } #endif /* #ifndef MINIZ_NO_STDIO */ /* ------------------- .ZIP archive writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) { mz_write_le32(p, (mz_uint32)v); mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); if (!n) return 0; /* An allocation this big is likely to just fail on 32-bit systems, so don't * even go there. */ if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); return 0; } if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc( pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) { mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); return 0; } pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return MZ_FALSE; } pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (MZ_FCLOSE(pState->m_pFile) == EOF) { if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); status = MZ_FALSE; } } pState->m_pFile = NULL; } #endif /* #ifndef MINIZ_NO_STDIO */ if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) { mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) { if (!pZip->m_pRead) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (pZip->m_file_offset_alignment) { /* Ensure user specified file offset alignment is a power of 2. */ if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); pZip->m_pState->m_zip64 = zip64; pZip->m_pState->m_zip64_has_extended_info_fields = zip64; pZip->m_zip_type = MZ_ZIP_TYPE_USER; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { return mz_zip_writer_init_v2(pZip, existing_size, 0); } mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); file_ofs += pZip->m_pState->m_file_archive_start_ofs; if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) { mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); return 0; } return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); } mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN( pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } pZip->m_pState->m_pFile = pFile; pZip->m_zip_type = MZ_ZIP_TYPE_FILE; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_ARR(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) { pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init_v2(pZip, 0, flags)) return MZ_FALSE; pZip->m_pState->m_pFile = pFile; pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; return MZ_TRUE; } #endif /* #ifndef MINIZ_NO_STDIO */ mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { /* We don't support converting a non-zip64 file to zip64 - this seems like * more trouble than it's worth. (What about the existing 32-bit data * descriptors that could follow the compressed data?) */ if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* No sense in trying to write to an archive that's already at the support max * size */ if (pZip->m_pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO (void)pFilename; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #else if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Archive is being read from stdio and was originally opened only for * reading. Try to reopen as writable. */ if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { /* The mz_zip_archive is now in a bogus state because pState->m_pFile is * NULL, so just close it. */ mz_zip_reader_end_internal(pZip, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); } } pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; #endif /* #ifdef MINIZ_NO_STDIO */ } else if (pState->m_pMem) { /* Archive lives in a memory block. Assume it's from the heap that we can * resize using the realloc callback. */ if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; } /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ else if (!pZip->m_pWrite) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Start writing new files at the archive's current central directory * location. */ /* TODO: We could add a flag that lets the user start writing immediately * AFTER the existing central dir - this would be safer. */ pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_central_directory_file_ofs = 0; /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ /* Even though we're now in write mode, files can still be extracted and * verified, but file locates will be slow. */ /* TODO: We could easily maintain the sorted central directory offsets. */ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init_from_reader_v2_noreopen(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { /* We don't support converting a non-zip64 file to zip64 - this seems like * more trouble than it's worth. (What about the existing 32-bit data * descriptors that could follow the compressed data?) */ if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* No sense in trying to write to an archive that's already at the support max * size */ if (pZip->m_pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO (void)pFilename; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); #else if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pNeeds_keepalive = NULL; #endif /* #ifdef MINIZ_NO_STDIO */ } else if (pState->m_pMem) { /* Archive lives in a memory block. Assume it's from the heap that we can * resize using the realloc callback. */ if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pNeeds_keepalive = NULL; } /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ else if (!pZip->m_pWrite) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Start writing new files at the archive's current central directory * location. */ /* TODO: We could add a flag that lets the user start writing immediately * AFTER the existing central dir - this would be safer. */ pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_central_directory_file_ofs = 0; /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ /* Even though we're now in write mode, files can still be extracted and * verified, but file locates will be slow. */ /* TODO: We could easily maintain the sorted central directory offsets. */ mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; return MZ_TRUE; } mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); } /* TODO: pArchive_name is a terrible name here! */ mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE \ (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE \ (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) { mz_uint8 *pDst = pBuf; mz_uint32 field_size = 0; MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); MZ_WRITE_LE16(pDst + 2, 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { MZ_WRITE_LE64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pComp_size) { MZ_WRITE_LE64(pDst, *pComp_size); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } if (pLocal_header_ofs) { MZ_WRITE_LE64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); field_size += sizeof(mz_uint64); } MZ_WRITE_LE16(pBuf + 2, field_size); return (mz_uint32)(pDst - pBuf); } static mz_bool mz_zip_writer_create_local_dir_header( mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header( mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir( mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; if (!pZip->m_pState->m_zip64) { if (local_header_ofs > 0xFFFFFFFF) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); } /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!mz_zip_writer_create_central_dir_header( pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { /* Try to resize the central directory array back into its original state. */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { /* Basic ZIP archive filename validity checks: Valid filenames cannot start * with a forward slash, cannot contain a drive letter, and cannot use * DOS-style backward slashes. */ if (*pArchive_name == '/') return MZ_FALSE; /* Making sure the name does not contain drive letters or DOS style backward * slashes is the responsibility of the program using miniz*/ return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { return mz_zip_writer_add_mem_ex_v2( pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); } mz_bool mz_zip_writer_add_mem_ex_v2( mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint16 bit_flags = 0; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); #ifndef MINIZ_NO_TIME if (last_modified != NULL) { mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); } else { MZ_TIME_T cur_time; time(&cur_time); mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); } #endif /* #ifndef MINIZ_NO_TIME */ if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { /* Set DOS Subdirectory attribute bit. */ ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; /* Subdirectories cannot contain data. */ if ((buf_size) || (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); } /* Try to do any allocations before writing to the archive, so if an * allocation fails the file remains unmodified. (A good idea if we're doing * an in-place modification.) */ if ((!mz_zip_array_ensure_room( pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes; MZ_CLEAR_ARR(local_dir_header); if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { method = MZ_DEFLATED; } if (pState->m_zip64) { if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pExtra_data != NULL) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += buf_size; comp_size = buf_size; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params( level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; if (uncomp_size) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir( pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_add_read_buf_callback( mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void *callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { mz_uint16 gen_flags; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_zip_internal_state *pState; mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) { /* Source file is too large for non-zip64 */ /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ pState->m_zip64 = MZ_TRUE; } /* We could support this, but why? */ if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_validate_archive_name(pArchive_name)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); if (pState->m_zip64) { if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if (pZip->m_total_files == MZ_UINT16_MAX) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } } archive_name_size = strlen(pArchive_name); if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); if (!pState->m_zip64) { /* Bail early if the archive would obviously become too large */ if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } } #ifndef MINIZ_NO_TIME if (pFile_time) { mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); } #endif if (max_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_archive_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (max_size && level) { method = MZ_DEFLATED; } MZ_CLEAR_ARR(local_dir_header); if (pState->m_zip64) { if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { pExtra_data = extra_data; if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); else extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, NULL, NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += extra_size; } else { if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_file_ofs += archive_name_size; } if (user_extra_data_len > 0) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_file_ofs += user_extra_data_len; } if (max_size) { void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!level) { while (1) { size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); if (n == 0) break; if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } file_ofs += n; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); cur_archive_file_ofs += n; } uncomp_size = file_ofs; comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params( level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); } for (;;) { tdefl_status status; tdefl_flush flush = TDEFL_NO_FLUSH; size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) { mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); break; } file_ofs += n; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) flush = TDEFL_FULL_FLUSH; if (n == 0) flush = TDEFL_FINISH; status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) { mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); break; } } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return MZ_FALSE; } uncomp_size = file_ofs; comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) { mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); if (pExtra_data == NULL) { if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(local_dir_footer + 8, comp_size); MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); } else { MZ_WRITE_LE64(local_dir_footer + 8, comp_size); MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) return MZ_FALSE; cur_archive_file_ofs += local_dir_footer_size; } if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) { if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_create_local_dir_header( pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date)) return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); cur_archive_header_file_ofs = local_dir_header_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); if (pExtra_data != NULL) { cur_archive_header_file_ofs += sizeof(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_archive_header_file_ofs += archive_name_size; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_archive_header_file_ofs += extra_size; } } if (pExtra_data != NULL) { extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } if (!mz_zip_writer_add_to_central_dir( pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, user_extra_data_central, user_extra_data_central_len)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pSrc_file); } mz_bool mz_zip_writer_add_cfile( mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { return mz_zip_writer_add_read_buf_callback( pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, ext_attributes, user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); } mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes) { MZ_FILE *pSrc_file = NULL; mz_uint64 uncomp_size = 0; MZ_TIME_T file_modified_time; MZ_TIME_T *pFile_time = NULL; mz_bool status; memset(&file_modified_time, 0, sizeof(file_modified_time)); #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) pFile_time = &file_modified_time; if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); #endif pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); status = mz_zip_writer_add_cfile( pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, ext_attributes, NULL, 0, NULL, 0); MZ_FCLOSE(pSrc_file); return status; } #endif /* #ifndef MINIZ_NO_STDIO */ static mz_bool mz_zip_writer_update_zip64_extension_block( mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { /* + 64 should be enough for any new zip64 data */ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) { mz_uint8 new_ext_block[64]; mz_uint8 *pDst = new_ext_block; mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); mz_write_le16(pDst + sizeof(mz_uint16), 0); pDst += sizeof(mz_uint16) * 2; if (pUncomp_size) { mz_write_le64(pDst, *pUncomp_size); pDst += sizeof(mz_uint64); } if (pComp_size) { mz_write_le64(pDst, *pComp_size); pDst += sizeof(mz_uint64); } if (pLocal_header_ofs) { mz_write_le64(pDst, *pLocal_header_ofs); pDst += sizeof(mz_uint64); } if (pDisk_start) { mz_write_le32(pDst, *pDisk_start); pDst += sizeof(mz_uint32); } mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if ((pExt) && (ext_len)) { mz_uint32 extra_size_remaining = ext_len; const mz_uint8 *pExtra_data = pExt; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); } return MZ_TRUE; } /* TODO: This func is now pretty freakin complex due to zip64, split it up? */ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; mz_zip_archive_file_stat src_file_stat; mz_uint32 src_filename_len, src_comment_len, src_ext_len; mz_uint32 local_header_filename_size, local_header_extra_len; mz_uint64 local_header_comp_size, local_header_uncomp_size; mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; /* Don't support copying files from zip64 archives to non-zip64, even though * in some cases this is possible */ if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); /* Get pointer to the source central dir header and crack it */ if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 * fudge factor in case we need to add more extra data) */ if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); if (!pState->m_zip64) { if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { /* TODO: Our zip64 support still has some 32-bit limits that may not be * worth fixing. */ if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) return MZ_FALSE; cur_src_file_ofs = src_file_stat.m_local_header_ofs; cur_dst_file_ofs = pZip->m_archive_size; /* Read the source archive's local dir header */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Compute the total size we need to copy (filename+extra data+compressed * data) */ local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; /* Try to find a zip64 extended information field */ if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { mz_zip_array file_data_array; const mz_uint8 *pExtra_data; mz_uint32 extra_size_remaining = local_header_extra_len; mz_zip_array_init(&file_data_array, 1); if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) { return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } pExtra_data = (const mz_uint8 *)file_data_array.m_p; do { mz_uint32 field_id, field_data_size, field_total_size; if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } field_id = MZ_READ_LE16(pExtra_data); field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); field_total_size = field_data_size + sizeof(mz_uint16) * 2; if (field_total_size > extra_size_remaining) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); if (field_data_size < sizeof(mz_uint64) * 2) { mz_zip_array_clear(pZip, &file_data_array); return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); } local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); local_header_comp_size = MZ_READ_LE64( pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ found_zip64_ext_data_in_ldir = MZ_TRUE; break; } pExtra_data += field_total_size; extra_size_remaining -= field_total_size; } while (extra_size_remaining); mz_zip_array_clear(pZip, &file_data_array); } if (!pState->m_zip64) { /* Try to detect if the new archive will most likely wind up too big and * bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which * could be present, +64 is a fudge factor). */ /* We also check when the archive is finalized so this doesn't need to be * perfect. */ mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; if (approx_new_archive_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } /* Write dest archive padding */ if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } /* The original zip's local header+ext block doesn't change, even with zip64, * so we can just copy it over to the dest zip */ if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; /* Copy over the source archive bytes to the dest archive, also ensure we have * enough buf space to handle optional data descriptor */ if (NULL == (pBuf = pZip->m_pAlloc( pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); while (src_archive_bytes_remaining) { n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_dst_file_ofs += n; src_archive_bytes_remaining -= n; } /* Now deal with the optional data descriptor */ bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { /* Copy data descriptor */ if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { /* src is zip64, dest must be zip64 */ /* name uint32_t's */ /* id 1 (optional in zip64?) */ /* crc 1 */ /* comp_size 2 */ /* uncomp_size 2 */ if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); } else { /* src is NOT zip64 */ mz_bool has_id; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); } has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); if (pZip->m_pState->m_zip64) { /* dest is zip64, so upgrade the data descriptor */ const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2 * sizeof(mz_uint32)); mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); n = sizeof(mz_uint32) * 6; } else { /* dest is NOT zip64, just copy it as-is */ n = sizeof(mz_uint32) * (has_id ? 4 : 3); } } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); /* Finally, add the new central dir header */ orig_central_dir_size = pState->m_central_dir.m_size; memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); if (pState->m_zip64) { /* This is the painful part: We need to write a new central dir header + ext * block with updated zip64 fields, and ensure the old fields (if any) are * not included. */ const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; mz_zip_array new_ext_block; mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); if (!mz_zip_writer_update_zip64_extension_block( &new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) { mz_zip_array_clear(pZip, &new_ext_block); return MZ_FALSE; } MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) { mz_zip_array_clear(pZip, &new_ext_block); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) { mz_zip_array_clear(pZip, &new_ext_block); mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } mz_zip_array_clear(pZip, &new_ext_block); } else { /* sanity checks */ if (cur_dst_file_ofs > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); if (local_dir_header_ofs >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } } /* This shouldn't trigger unless we screwed up during the initial sanity * checks */ if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { /* TODO: Support central dirs >= 32-bits in size */ mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); } n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[256]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); pState = pZip->m_pState; if (pState->m_zip64) { if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else { if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { /* Write central directory */ central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += central_dir_size; } if (pState->m_zip64) { /* Write zip64 end of central directory header */ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; /* Write zip64 end of central directory locator */ MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; } /* Write end of central directory record */ MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); #endif /* #ifndef MINIZ_NO_STDIO */ pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) { if ((!ppBuf) || (!pSize)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); *ppBuf = NULL; *pSize = 0; if ((!pZip) || (!pZip->m_pState)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (pZip->m_pWrite != mz_zip_heap_write_func) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *ppBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { return mz_zip_writer_end_internal(pZip, MZ_TRUE); } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { return mz_zip_add_mem_to_archive_file_in_place_v2( pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); } mz_bool mz_zip_add_mem_to_archive_file_in_place_v2( const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; mz_zip_error actual_err = MZ_ZIP_NO_ERROR; mz_zip_zero_struct(&zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return MZ_FALSE; } if (!mz_zip_writer_validate_archive_name(pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_FILENAME; return MZ_FALSE; } /* Important: The regular non-64 bit version of stat() can fail here if the * file is very large, which could cause the archive to be overwritten. */ /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { /* Create a new archive. */ if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } created_new_archive = MZ_TRUE; } else { /* Append to an existing archive. */ if (!mz_zip_reader_init_file_v2( &zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return MZ_FALSE; } if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) { if (pErr) *pErr = zip_archive.m_last_error; mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); actual_err = zip_archive.m_last_error; /* Always finalize, even if adding failed for some reason, so we have a valid * central directory. (This may not always succeed, but we can try.) */ if (!mz_zip_writer_finalize_archive(&zip_archive)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if (!mz_zip_writer_end_internal(&zip_archive, status)) { if (!actual_err) actual_err = zip_archive.m_last_error; status = MZ_FALSE; } if ((!status) && (created_new_archive)) { /* It's a new archive and something went wrong, so just delete it. */ int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } if (pErr) *pErr = actual_err; return status; } void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) { mz_uint32 file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) { if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; return NULL; } mz_zip_zero_struct(&zip_archive); if (!mz_zip_reader_init_file_v2( &zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { if (pErr) *pErr = zip_archive.m_last_error; return NULL; } if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) { p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); } mz_zip_reader_end_internal(&zip_archive, p != NULL); if (pErr) *pErr = zip_archive.m_last_error; return p; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); } #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ /* ------------------- Misc utils */ mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; } mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) { return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; } mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = err_num; return prev_err; } mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) { if (!pZip) return MZ_ZIP_INVALID_PARAMETER; return pZip->m_last_error; } mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) { return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); } mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) { mz_zip_error prev_err; if (!pZip) return MZ_ZIP_INVALID_PARAMETER; prev_err = pZip->m_last_error; pZip->m_last_error = MZ_ZIP_NO_ERROR; return prev_err; } const char *mz_zip_get_error_string(mz_zip_error mz_err) { switch (mz_err) { case MZ_ZIP_NO_ERROR: return "no error"; case MZ_ZIP_UNDEFINED_ERROR: return "undefined error"; case MZ_ZIP_TOO_MANY_FILES: return "too many files"; case MZ_ZIP_FILE_TOO_LARGE: return "file too large"; case MZ_ZIP_UNSUPPORTED_METHOD: return "unsupported method"; case MZ_ZIP_UNSUPPORTED_ENCRYPTION: return "unsupported encryption"; case MZ_ZIP_UNSUPPORTED_FEATURE: return "unsupported feature"; case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: return "failed finding central directory"; case MZ_ZIP_NOT_AN_ARCHIVE: return "not a ZIP archive"; case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: return "invalid header or archive is corrupted"; case MZ_ZIP_UNSUPPORTED_MULTIDISK: return "unsupported multidisk archive"; case MZ_ZIP_DECOMPRESSION_FAILED: return "decompression failed or archive is corrupted"; case MZ_ZIP_COMPRESSION_FAILED: return "compression failed"; case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: return "unexpected decompressed size"; case MZ_ZIP_CRC_CHECK_FAILED: return "CRC-32 check failed"; case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: return "unsupported central directory size"; case MZ_ZIP_ALLOC_FAILED: return "allocation failed"; case MZ_ZIP_FILE_OPEN_FAILED: return "file open failed"; case MZ_ZIP_FILE_CREATE_FAILED: return "file create failed"; case MZ_ZIP_FILE_WRITE_FAILED: return "file write failed"; case MZ_ZIP_FILE_READ_FAILED: return "file read failed"; case MZ_ZIP_FILE_CLOSE_FAILED: return "file close failed"; case MZ_ZIP_FILE_SEEK_FAILED: return "file seek failed"; case MZ_ZIP_FILE_STAT_FAILED: return "file stat failed"; case MZ_ZIP_INVALID_PARAMETER: return "invalid parameter"; case MZ_ZIP_INVALID_FILENAME: return "invalid filename"; case MZ_ZIP_BUF_TOO_SMALL: return "buffer too small"; case MZ_ZIP_INTERNAL_ERROR: return "internal error"; case MZ_ZIP_FILE_NOT_FOUND: return "file not found"; case MZ_ZIP_ARCHIVE_TOO_LARGE: return "archive is too large"; case MZ_ZIP_VALIDATION_FAILED: return "validation failed"; case MZ_ZIP_WRITE_CALLBACK_FAILED: return "write callback failed"; case MZ_ZIP_TOTAL_ERRORS: return "total errors"; default: break; } return "unknown error"; } /* Note: Just because the archive is not zip64 doesn't necessarily mean it * doesn't have Zip64 extended information extra field, argh. */ mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return MZ_FALSE; return pZip->m_pState->m_zip64; } size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_central_dir.m_size; } mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) { if (!pZip) return 0; return pZip->m_archive_size; } mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_file_archive_start_ofs; } MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState)) return 0; return pZip->m_pState->m_pFile; } size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { return mz_zip_file_stat_internal( pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); } mz_bool mz_zip_end(mz_zip_archive *pZip) { if (!pZip) return MZ_FALSE; if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) return mz_zip_reader_end(pZip); #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) return mz_zip_writer_end(pZip); #endif return MZ_FALSE; } #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ tea-qt-63.3.0/src/myjoystick.cpp000066400000000000000000000035741476733534200165250ustar00rootroot00000000000000//this code by Peter Semiletov is the public domain #include #if defined(JOYSTICK_SUPPORTED) #include #include #include "myjoystick.h" CJoystick::CJoystick (uint idn, QObject *upper_link) { receiver = upper_link; id = idn; initialized = false; number_of_axis = 0; number_of_buttons = 0; axis_pressed = false; etype = 0; QString filename = "/dev/input/js" + QString::number (id); if ((fd = open (filename.toUtf8().data(), O_NONBLOCK)) == -1) { qDebug() << "Cannot open " << filename; return; } initialized = true; char num_of_axis = 0; char num_of_buttons = 0; char jname[80]; ioctl (fd, JSIOCGAXES, &num_of_axis); ioctl (fd, JSIOCGBUTTONS, &num_of_buttons); ioctl (fd, JSIOCGNAME(80), &jname); number_of_axis = num_of_axis; number_of_buttons = num_of_buttons; description = jname; read_joystick(); } CJoystick::~CJoystick() { close (fd); } void CJoystick::process_event (js_event e) { // qDebug() << "e.type" << e.type << endl; if (e.type & JS_EVENT_BUTTON) { CJoystickButtonEvent *event = new CJoystickButtonEvent (evtJoystickButton); event->button = e.number; //pressed button number event->pressed = e.value; //1 if pressed QApplication::postEvent (receiver, reinterpret_cast(event)); } else if (e.type & JS_EVENT_AXIS) { CJoystickAxisEvent *event = new CJoystickAxisEvent (evtJoystickAxis); event->axis = e.number; event->value = e.value; QApplication::postEvent(receiver, reinterpret_cast(event)); } } void CJoystick::read_joystick() { if (! initialized) return; struct js_event e; while (read (fd, &e, sizeof(e)) > 0) { process_event (e); } if (errno != EAGAIN) { qDebug() << "Joystick read error"; initialized = false; } } #endif tea-qt-63.3.0/src/myjoystick.h000066400000000000000000000024111476733534200161570ustar00rootroot00000000000000//this code by Peter Semiletov is the public domain #include #include #ifndef MYJOYSTICK_H #define MYJOYSTICK_H #if defined(JOYSTICK_SUPPORTED) #include #include #include #include #include #include const QEvent::Type evtJoystickAxis = QEvent::Type (QEvent::User + 1); const QEvent::Type evtJoystickButton = QEvent::Type (QEvent::User + 2); class CJoystickButtonEvent: public QEvent { public: uint button; bool pressed; CJoystickButtonEvent (QEvent::Type type): QEvent (type), button (0), pressed (false) {} }; class CJoystickAxisEvent: public QEvent { public: uint axis; qint16 value; CJoystickAxisEvent (QEvent::Type type): QEvent (type), axis (0), value (0) {} }; class CJoystick: public QObject { Q_OBJECT public: QObject *receiver; //link to the object that handle joystick events int fd; //joystick file descriptor uint id; //joystick id int etype; bool axis_pressed; QString description; //joystick text description bool initialized; uint number_of_axis; uint number_of_buttons; CJoystick (uint idn, QObject *upper_link); ~CJoystick(); void process_event (js_event e); public slots: void read_joystick(); }; #endif #endif tea-qt-63.3.0/src/pugiconfig.hpp000066400000000000000000000055121476733534200164510ustar00rootroot00000000000000/** * pugixml parser - version 1.14 * -------------------------------------------------------- * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef HEADER_PUGICONFIG_HPP #define HEADER_PUGICONFIG_HPP // Uncomment this to enable wchar_t mode // #define PUGIXML_WCHAR_MODE // Uncomment this to enable compact mode // #define PUGIXML_COMPACT // Uncomment this to disable XPath #define PUGIXML_NO_XPATH // Uncomment this to disable STL // #define PUGIXML_NO_STL // Uncomment this to disable exceptions // #define PUGIXML_NO_EXCEPTIONS // Set this to control attributes for public classes/functions, i.e.: // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead // Tune these constants to adjust memory-related behavior // #define PUGIXML_MEMORY_PAGE_SIZE 32768 // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 // Tune this constant to adjust max nesting for XPath queries // #define PUGIXML_XPATH_DEPTH_LIMIT 1024 // Uncomment this to switch to header-only version #define PUGIXML_HEADER_ONLY // Uncomment this to enable long long support // #define PUGIXML_HAS_LONG_LONG #endif /** * Copyright (c) 2006-2023 Arseny Kapoulkine * * 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. */ tea-qt-63.3.0/src/pugixml.cpp000066400000000000000000012453131476733534200160050ustar00rootroot00000000000000/** * pugixml parser - version 1.14 * -------------------------------------------------------- * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ #ifndef SOURCE_PUGIXML_CPP #define SOURCE_PUGIXML_CPP #include "pugixml.hpp" #include #include #include #include #include #ifdef PUGIXML_WCHAR_MODE # include #endif #ifndef PUGIXML_NO_XPATH # include # include #endif #ifndef PUGIXML_NO_STL # include # include # include #endif // For placement new #include // For load_file #if defined(__linux__) || defined(__APPLE__) #include #endif #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4324) // structure was padded due to __declspec(align()) # pragma warning(disable: 4702) // unreachable code # pragma warning(disable: 4996) // this function or variable may be unsafe #endif #if defined(_MSC_VER) && defined(__c2__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated" // this function or variable may be unsafe #endif #ifdef __INTEL_COMPILER # pragma warning(disable: 177) // function was declared but never referenced # pragma warning(disable: 279) // controlling expression is constant # pragma warning(disable: 1478 1786) // function was declared "deprecated" # pragma warning(disable: 1684) // conversion from pointer to same-sized integral type #endif #if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) # pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away #endif #ifdef __BORLANDC__ # pragma option push # pragma warn -8008 // condition is always false # pragma warn -8066 // unreachable code #endif #ifdef __SNC__ // Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug # pragma diag_suppress=178 // function was declared but never referenced # pragma diag_suppress=237 // controlling expression is constant #endif #ifdef __TI_COMPILER_VERSION__ # pragma diag_suppress 179 // function was declared but never referenced #endif // Inlining controls #if defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGI_IMPL_NO_INLINE __declspec(noinline) #elif defined(__GNUC__) # define PUGI_IMPL_NO_INLINE __attribute__((noinline)) #else # define PUGI_IMPL_NO_INLINE #endif // Branch weight controls #if defined(__GNUC__) && !defined(__c2__) # define PUGI_IMPL_UNLIKELY(cond) __builtin_expect(cond, 0) #else # define PUGI_IMPL_UNLIKELY(cond) (cond) #endif // Simple static assertion #define PUGI_IMPL_STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } // Digital Mars C++ bug workaround for passing char loaded from memory via stack #ifdef __DMC__ # define PUGI_IMPL_DMC_VOLATILE volatile #else # define PUGI_IMPL_DMC_VOLATILE #endif // Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings #if defined(__clang__) && defined(__has_attribute) # if __has_attribute(no_sanitize) # define PUGI_IMPL_UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow"))) # else # define PUGI_IMPL_UNSIGNED_OVERFLOW # endif #else # define PUGI_IMPL_UNSIGNED_OVERFLOW #endif // Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) #if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) using std::memcpy; using std::memmove; using std::memset; #endif // Old versions of GCC do not define ::malloc and ::free depending on header include order #if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)) using std::malloc; using std::free; #endif // Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations #if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX) # define LLONG_MIN (-LLONG_MAX - 1LL) # define LLONG_MAX __LONG_LONG_MAX__ # define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) #endif // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features #if defined(_MSC_VER) && !defined(__S3E__) && !defined(_WIN32_WCE) # define PUGI_IMPL_MSVC_CRT_VERSION _MSC_VER #elif defined(_WIN32_WCE) # define PUGI_IMPL_MSVC_CRT_VERSION 1310 // MSVC7.1 #endif // Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size. #if __cplusplus >= 201103 # define PUGI_IMPL_SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) #elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 # define PUGI_IMPL_SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__) #elif defined(__APPLE__) && __clang_major__ >= 14 // Xcode 14 marks sprintf as deprecated while still using C++98 by default # define PUGI_IMPL_SNPRINTF(buf, fmt, arg1, arg2) snprintf(buf, sizeof(buf), fmt, arg1, arg2) #else # define PUGI_IMPL_SNPRINTF sprintf #endif // We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat. #ifdef PUGIXML_HEADER_ONLY # define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { # define PUGI_IMPL_NS_END } } # define PUGI_IMPL_FN inline # define PUGI_IMPL_FN_NO_INLINE inline #else # if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces # define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { # define PUGI_IMPL_NS_END } } # else # define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { namespace { # define PUGI_IMPL_NS_END } } } # endif # define PUGI_IMPL_FN # define PUGI_IMPL_FN_NO_INLINE PUGI_IMPL_NO_INLINE #endif // uintptr_t #if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561) namespace pugi { # ifndef _UINTPTR_T_DEFINED typedef size_t uintptr_t; # endif typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; } #else # include #endif // Memory allocation PUGI_IMPL_NS_BEGIN PUGI_IMPL_FN void* default_allocate(size_t size) { return malloc(size); } PUGI_IMPL_FN void default_deallocate(void* ptr) { free(ptr); } template struct xml_memory_management_function_storage { static allocation_function allocate; static deallocation_function deallocate; }; // Global allocation functions are stored in class statics so that in header mode linker deduplicates them // Without a template<> we'll get multiple definitions of the same static template allocation_function xml_memory_management_function_storage::allocate = default_allocate; template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; typedef xml_memory_management_function_storage xml_memory; PUGI_IMPL_NS_END // String utilities PUGI_IMPL_NS_BEGIN // Get string length PUGI_IMPL_FN size_t strlength(const char_t* s) { assert(s); #ifdef PUGIXML_WCHAR_MODE return wcslen(s); #else return strlen(s); #endif } // Compare two strings PUGI_IMPL_FN bool strequal(const char_t* src, const char_t* dst) { assert(src && dst); #ifdef PUGIXML_WCHAR_MODE return wcscmp(src, dst) == 0; #else return strcmp(src, dst) == 0; #endif } // Compare lhs with [rhs_begin, rhs_end) PUGI_IMPL_FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) { for (size_t i = 0; i < count; ++i) if (lhs[i] != rhs[i]) return false; return lhs[count] == 0; } // Get length of wide string, even if CRT lacks wide character support PUGI_IMPL_FN size_t strlength_wide(const wchar_t* s) { assert(s); #ifdef PUGIXML_WCHAR_MODE return wcslen(s); #else const wchar_t* end = s; while (*end) end++; return static_cast(end - s); #endif } PUGI_IMPL_NS_END // auto_ptr-like object for exception recovery PUGI_IMPL_NS_BEGIN template struct auto_deleter { typedef void (*D)(T*); T* data; D deleter; auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) { } ~auto_deleter() { if (data) deleter(data); } T* release() { T* result = data; data = 0; return result; } }; PUGI_IMPL_NS_END #ifdef PUGIXML_COMPACT PUGI_IMPL_NS_BEGIN class compact_hash_table { public: compact_hash_table(): _items(0), _capacity(0), _count(0) { } void clear() { if (_items) { xml_memory::deallocate(_items); _items = 0; _capacity = 0; _count = 0; } } void* find(const void* key) { if (_capacity == 0) return 0; item_t* item = get_item(key); assert(item); assert(item->key == key || (item->key == 0 && item->value == 0)); return item->value; } void insert(const void* key, void* value) { assert(_capacity != 0 && _count < _capacity - _capacity / 4); item_t* item = get_item(key); assert(item); if (item->key == 0) { _count++; item->key = key; } item->value = value; } bool reserve(size_t extra = 16) { if (_count + extra >= _capacity - _capacity / 4) return rehash(_count + extra); return true; } private: struct item_t { const void* key; void* value; }; item_t* _items; size_t _capacity; size_t _count; bool rehash(size_t count); item_t* get_item(const void* key) { assert(key); assert(_capacity > 0); size_t hashmod = _capacity - 1; size_t bucket = hash(key) & hashmod; for (size_t probe = 0; probe <= hashmod; ++probe) { item_t& probe_item = _items[bucket]; if (probe_item.key == key || probe_item.key == 0) return &probe_item; // hash collision, quadratic probing bucket = (bucket + probe + 1) & hashmod; } assert(false && "Hash table is full"); // unreachable return 0; } static PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash(const void* key) { unsigned int h = static_cast(reinterpret_cast(key) & 0xffffffff); // MurmurHash3 32-bit finalizer h ^= h >> 16; h *= 0x85ebca6bu; h ^= h >> 13; h *= 0xc2b2ae35u; h ^= h >> 16; return h; } }; PUGI_IMPL_FN_NO_INLINE bool compact_hash_table::rehash(size_t count) { size_t capacity = 32; while (count >= capacity - capacity / 4) capacity *= 2; compact_hash_table rt; rt._capacity = capacity; rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * capacity)); if (!rt._items) return false; memset(rt._items, 0, sizeof(item_t) * capacity); for (size_t i = 0; i < _capacity; ++i) if (_items[i].key) rt.insert(_items[i].key, _items[i].value); if (_items) xml_memory::deallocate(_items); _capacity = capacity; _items = rt._items; assert(_count == rt._count); return true; } PUGI_IMPL_NS_END #endif PUGI_IMPL_NS_BEGIN #ifdef PUGIXML_COMPACT static const uintptr_t xml_memory_block_alignment = 4; #else static const uintptr_t xml_memory_block_alignment = sizeof(void*); #endif // extra metadata bits static const uintptr_t xml_memory_page_contents_shared_mask = 64; static const uintptr_t xml_memory_page_name_allocated_mask = 32; static const uintptr_t xml_memory_page_value_allocated_mask = 16; static const uintptr_t xml_memory_page_type_mask = 15; // combined masks for string uniqueness static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; #ifdef PUGIXML_COMPACT #define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) // unused #define PUGI_IMPL_GETPAGE_IMPL(header) (header).get_page() #else #define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) (((reinterpret_cast(object) - reinterpret_cast(page)) << 8) | (flags)) // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings #define PUGI_IMPL_GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8)))) #endif #define PUGI_IMPL_GETPAGE(n) PUGI_IMPL_GETPAGE_IMPL((n)->header) #define PUGI_IMPL_NODETYPE(n) static_cast((n)->header & impl::xml_memory_page_type_mask) struct xml_allocator; struct xml_memory_page { static xml_memory_page* construct(void* memory) { xml_memory_page* result = static_cast(memory); result->allocator = 0; result->prev = 0; result->next = 0; result->busy_size = 0; result->freed_size = 0; #ifdef PUGIXML_COMPACT result->compact_string_base = 0; result->compact_shared_parent = 0; result->compact_page_marker = 0; #endif return result; } xml_allocator* allocator; xml_memory_page* prev; xml_memory_page* next; size_t busy_size; size_t freed_size; #ifdef PUGIXML_COMPACT char_t* compact_string_base; void* compact_shared_parent; uint32_t* compact_page_marker; #endif }; static const size_t xml_memory_page_size = #ifdef PUGIXML_MEMORY_PAGE_SIZE (PUGIXML_MEMORY_PAGE_SIZE) #else 32768 #endif - sizeof(xml_memory_page); struct xml_memory_string_header { uint16_t page_offset; // offset from page->data uint16_t full_size; // 0 if string occupies whole page }; struct xml_allocator { xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) { #ifdef PUGIXML_COMPACT _hash = 0; #endif } xml_memory_page* allocate_page(size_t data_size) { size_t size = sizeof(xml_memory_page) + data_size; // allocate block with some alignment, leaving memory for worst-case padding void* memory = xml_memory::allocate(size); if (!memory) return 0; // prepare page structure xml_memory_page* page = xml_memory_page::construct(memory); assert(page); assert(this == _root->allocator); page->allocator = this; return page; } static void deallocate_page(xml_memory_page* page) { xml_memory::deallocate(page); } void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); void* allocate_memory(size_t size, xml_memory_page*& out_page) { if (PUGI_IMPL_UNLIKELY(_busy_size + size > xml_memory_page_size)) return allocate_memory_oob(size, out_page); void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; _busy_size += size; out_page = _root; return buf; } #ifdef PUGIXML_COMPACT void* allocate_object(size_t size, xml_memory_page*& out_page) { void* result = allocate_memory(size + sizeof(uint32_t), out_page); if (!result) return 0; // adjust for marker ptrdiff_t offset = static_cast(result) - reinterpret_cast(out_page->compact_page_marker); if (PUGI_IMPL_UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) { // insert new marker uint32_t* marker = static_cast(result); *marker = static_cast(reinterpret_cast(marker) - reinterpret_cast(out_page)); out_page->compact_page_marker = marker; // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block // this will make sure deallocate_memory correctly tracks the size out_page->freed_size += sizeof(uint32_t); return marker + 1; } else { // roll back uint32_t part _busy_size -= sizeof(uint32_t); return result; } } #else void* allocate_object(size_t size, xml_memory_page*& out_page) { return allocate_memory(size, out_page); } #endif void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) { if (page == _root) page->busy_size = _busy_size; assert(ptr >= reinterpret_cast(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast(page) + sizeof(xml_memory_page) + page->busy_size); (void)!ptr; page->freed_size += size; assert(page->freed_size <= page->busy_size); if (page->freed_size == page->busy_size) { if (page->next == 0) { assert(_root == page); // top page freed, just reset sizes page->busy_size = 0; page->freed_size = 0; #ifdef PUGIXML_COMPACT // reset compact state to maximize efficiency page->compact_string_base = 0; page->compact_shared_parent = 0; page->compact_page_marker = 0; #endif _busy_size = 0; } else { assert(_root != page); assert(page->prev); // remove from the list page->prev->next = page->next; page->next->prev = page->prev; // deallocate deallocate_page(page); } } } char_t* allocate_string(size_t length) { static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment; PUGI_IMPL_STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); // allocate memory for string and header block size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); // round size up to block alignment boundary size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1); xml_memory_page* page; xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); if (!header) return 0; // setup header ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); assert(page_offset % xml_memory_block_alignment == 0); assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); header->page_offset = static_cast(static_cast(page_offset) / xml_memory_block_alignment); // full_size == 0 for large strings that occupy the whole page assert(full_size % xml_memory_block_alignment == 0); assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); header->full_size = static_cast(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0); // round-trip through void* to avoid 'cast increases required alignment of target type' warning // header is guaranteed a pointer-sized alignment, which should be enough for char_t return static_cast(static_cast(header + 1)); } void deallocate_string(char_t* string) { // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string // get header xml_memory_string_header* header = static_cast(static_cast(string)) - 1; assert(header); // deallocate size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment; xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); // if full_size == 0 then this string occupies the whole page size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment; deallocate_memory(header, full_size, page); } bool reserve() { #ifdef PUGIXML_COMPACT return _hash->reserve(); #else return true; #endif } xml_memory_page* _root; size_t _busy_size; #ifdef PUGIXML_COMPACT compact_hash_table* _hash; #endif }; PUGI_IMPL_FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) { const size_t large_allocation_threshold = xml_memory_page_size / 4; xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); out_page = page; if (!page) return 0; if (size <= large_allocation_threshold) { _root->busy_size = _busy_size; // insert page at the end of linked list page->prev = _root; _root->next = page; _root = page; _busy_size = size; } else { // insert page before the end of linked list, so that it is deleted as soon as possible // the last page is not deleted even if it's empty (see deallocate_memory) assert(_root->prev); page->prev = _root->prev; page->next = _root; _root->prev->next = page; _root->prev = page; page->busy_size = size; } return reinterpret_cast(page) + sizeof(xml_memory_page); } PUGI_IMPL_NS_END #ifdef PUGIXML_COMPACT PUGI_IMPL_NS_BEGIN static const uintptr_t compact_alignment_log2 = 2; static const uintptr_t compact_alignment = 1 << compact_alignment_log2; class compact_header { public: compact_header(xml_memory_page* page, unsigned int flags) { PUGI_IMPL_STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); ptrdiff_t offset = (reinterpret_cast(this) - reinterpret_cast(page->compact_page_marker)); assert(offset % compact_alignment == 0 && static_cast(offset) < 256 * compact_alignment); _page = static_cast(offset >> compact_alignment_log2); _flags = static_cast(flags); } void operator&=(uintptr_t mod) { _flags &= static_cast(mod); } void operator|=(uintptr_t mod) { _flags |= static_cast(mod); } uintptr_t operator&(uintptr_t mod) const { return _flags & mod; } xml_memory_page* get_page() const { // round-trip through void* to silence 'cast increases required alignment of target type' warnings const char* page_marker = reinterpret_cast(this) - (_page << compact_alignment_log2); const char* page = page_marker - *reinterpret_cast(static_cast(page_marker)); return const_cast(reinterpret_cast(static_cast(page))); } private: unsigned char _page; unsigned char _flags; }; PUGI_IMPL_FN xml_memory_page* compact_get_page(const void* object, int header_offset) { const compact_header* header = reinterpret_cast(static_cast(object) - header_offset); return header->get_page(); } template PUGI_IMPL_FN_NO_INLINE T* compact_get_value(const void* object) { return static_cast(compact_get_page(object, header_offset)->allocator->_hash->find(object)); } template PUGI_IMPL_FN_NO_INLINE void compact_set_value(const void* object, T* value) { compact_get_page(object, header_offset)->allocator->_hash->insert(object, value); } template class compact_pointer { public: compact_pointer(): _data(0) { } void operator=(const compact_pointer& rhs) { *this = rhs + 0; } void operator=(T* value) { if (value) { // value is guaranteed to be compact-aligned; 'this' is not // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to // compensate for arithmetic shift rounding for negative values ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start; if (static_cast(offset) <= 253) _data = static_cast(offset + 1); else { compact_set_value(this, value); _data = 255; } } else _data = 0; } operator T*() const { if (_data) { if (_data < 255) { uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); return reinterpret_cast(base + (_data - 1 + start) * compact_alignment); } else return compact_get_value(this); } else return 0; } T* operator->() const { return *this; } private: unsigned char _data; }; template class compact_pointer_parent { public: compact_pointer_parent(): _data(0) { } void operator=(const compact_pointer_parent& rhs) { *this = rhs + 0; } void operator=(T* value) { if (value) { // value is guaranteed to be compact-aligned; 'this' is not // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to // compensate for arithmetic shift behavior for negative values ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533; if (static_cast(offset) <= 65533) { _data = static_cast(offset + 1); } else { xml_memory_page* page = compact_get_page(this, header_offset); if (PUGI_IMPL_UNLIKELY(page->compact_shared_parent == 0)) page->compact_shared_parent = value; if (page->compact_shared_parent == value) { _data = 65534; } else { compact_set_value(this, value); _data = 65535; } } } else { _data = 0; } } operator T*() const { if (_data) { if (_data < 65534) { uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); return reinterpret_cast(base + (_data - 1 - 65533) * compact_alignment); } else if (_data == 65534) return static_cast(compact_get_page(this, header_offset)->compact_shared_parent); else return compact_get_value(this); } else return 0; } T* operator->() const { return *this; } private: uint16_t _data; }; template class compact_string { public: compact_string(): _data(0) { } void operator=(const compact_string& rhs) { *this = rhs + 0; } void operator=(char_t* value) { if (value) { xml_memory_page* page = compact_get_page(this, header_offset); if (PUGI_IMPL_UNLIKELY(page->compact_string_base == 0)) page->compact_string_base = value; ptrdiff_t offset = value - page->compact_string_base; if (static_cast(offset) < (65535 << 7)) { // round-trip through void* to silence 'cast increases required alignment of target type' warnings uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); if (*base == 0) { *base = static_cast((offset >> 7) + 1); _data = static_cast((offset & 127) + 1); } else { ptrdiff_t remainder = offset - ((*base - 1) << 7); if (static_cast(remainder) <= 253) { _data = static_cast(remainder + 1); } else { compact_set_value(this, value); _data = 255; } } } else { compact_set_value(this, value); _data = 255; } } else { _data = 0; } } operator char_t*() const { if (_data) { if (_data < 255) { xml_memory_page* page = compact_get_page(this, header_offset); // round-trip through void* to silence 'cast increases required alignment of target type' warnings const uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); assert(*base); ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); return page->compact_string_base + offset; } else { return compact_get_value(this); } } else return 0; } private: unsigned char _data; }; PUGI_IMPL_NS_END #endif #ifdef PUGIXML_COMPACT namespace pugi { struct xml_attribute_struct { xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0) { PUGI_IMPL_STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); } impl::compact_header header; uint16_t namevalue_base; impl::compact_string<4, 2> name; impl::compact_string<5, 3> value; impl::compact_pointer prev_attribute_c; impl::compact_pointer next_attribute; }; struct xml_node_struct { xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0) { PUGI_IMPL_STATIC_ASSERT(sizeof(xml_node_struct) == 12); } impl::compact_header header; uint16_t namevalue_base; impl::compact_string<4, 2> name; impl::compact_string<5, 3> value; impl::compact_pointer_parent parent; impl::compact_pointer first_child; impl::compact_pointer prev_sibling_c; impl::compact_pointer next_sibling; impl::compact_pointer first_attribute; }; } #else namespace pugi { struct xml_attribute_struct { xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0) { header = PUGI_IMPL_GETHEADER_IMPL(this, page, 0); } uintptr_t header; char_t* name; char_t* value; xml_attribute_struct* prev_attribute_c; xml_attribute_struct* next_attribute; }; struct xml_node_struct { xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) { header = PUGI_IMPL_GETHEADER_IMPL(this, page, type); } uintptr_t header; char_t* name; char_t* value; xml_node_struct* parent; xml_node_struct* first_child; xml_node_struct* prev_sibling_c; xml_node_struct* next_sibling; xml_attribute_struct* first_attribute; }; } #endif PUGI_IMPL_NS_BEGIN struct xml_extra_buffer { char_t* buffer; xml_extra_buffer* next; }; struct xml_document_struct: public xml_node_struct, public xml_allocator { xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) { } const char_t* buffer; xml_extra_buffer* extra_buffers; #ifdef PUGIXML_COMPACT compact_hash_table hash; #endif }; template inline xml_allocator& get_allocator(const Object* object) { assert(object); return *PUGI_IMPL_GETPAGE(object)->allocator; } template inline xml_document_struct& get_document(const Object* object) { assert(object); return *static_cast(PUGI_IMPL_GETPAGE(object)->allocator); } PUGI_IMPL_NS_END // Low-level DOM operations PUGI_IMPL_NS_BEGIN inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) { xml_memory_page* page; void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page); if (!memory) return 0; return new (memory) xml_attribute_struct(page); } inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) { xml_memory_page* page; void* memory = alloc.allocate_object(sizeof(xml_node_struct), page); if (!memory) return 0; return new (memory) xml_node_struct(page, type); } inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) { if (a->header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(a->name); if (a->header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(a->value); alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI_IMPL_GETPAGE(a)); } inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) { if (n->header & impl::xml_memory_page_name_allocated_mask) alloc.deallocate_string(n->name); if (n->header & impl::xml_memory_page_value_allocated_mask) alloc.deallocate_string(n->value); for (xml_attribute_struct* attr = n->first_attribute; attr; ) { xml_attribute_struct* next = attr->next_attribute; destroy_attribute(attr, alloc); attr = next; } for (xml_node_struct* child = n->first_child; child; ) { xml_node_struct* next = child->next_sibling; destroy_node(child, alloc); child = next; } alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI_IMPL_GETPAGE(n)); } inline void append_node(xml_node_struct* child, xml_node_struct* node) { child->parent = node; xml_node_struct* head = node->first_child; if (head) { xml_node_struct* tail = head->prev_sibling_c; tail->next_sibling = child; child->prev_sibling_c = tail; head->prev_sibling_c = child; } else { node->first_child = child; child->prev_sibling_c = child; } } inline void prepend_node(xml_node_struct* child, xml_node_struct* node) { child->parent = node; xml_node_struct* head = node->first_child; if (head) { child->prev_sibling_c = head->prev_sibling_c; head->prev_sibling_c = child; } else child->prev_sibling_c = child; child->next_sibling = head; node->first_child = child; } inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) { xml_node_struct* parent = node->parent; child->parent = parent; xml_node_struct* next = node->next_sibling; if (next) next->prev_sibling_c = child; else parent->first_child->prev_sibling_c = child; child->next_sibling = next; child->prev_sibling_c = node; node->next_sibling = child; } inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) { xml_node_struct* parent = node->parent; child->parent = parent; xml_node_struct* prev = node->prev_sibling_c; if (prev->next_sibling) prev->next_sibling = child; else parent->first_child = child; child->prev_sibling_c = prev; child->next_sibling = node; node->prev_sibling_c = child; } inline void remove_node(xml_node_struct* node) { xml_node_struct* parent = node->parent; xml_node_struct* next = node->next_sibling; xml_node_struct* prev = node->prev_sibling_c; if (next) next->prev_sibling_c = prev; else parent->first_child->prev_sibling_c = prev; if (prev->next_sibling) prev->next_sibling = next; else parent->first_child = next; node->parent = 0; node->prev_sibling_c = 0; node->next_sibling = 0; } inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) { xml_attribute_struct* head = node->first_attribute; if (head) { xml_attribute_struct* tail = head->prev_attribute_c; tail->next_attribute = attr; attr->prev_attribute_c = tail; head->prev_attribute_c = attr; } else { node->first_attribute = attr; attr->prev_attribute_c = attr; } } inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) { xml_attribute_struct* head = node->first_attribute; if (head) { attr->prev_attribute_c = head->prev_attribute_c; head->prev_attribute_c = attr; } else attr->prev_attribute_c = attr; attr->next_attribute = head; node->first_attribute = attr; } inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { xml_attribute_struct* next = place->next_attribute; if (next) next->prev_attribute_c = attr; else node->first_attribute->prev_attribute_c = attr; attr->next_attribute = next; attr->prev_attribute_c = place; place->next_attribute = attr; } inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) { xml_attribute_struct* prev = place->prev_attribute_c; if (prev->next_attribute) prev->next_attribute = attr; else node->first_attribute = attr; attr->prev_attribute_c = prev; attr->next_attribute = place; place->prev_attribute_c = attr; } inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) { xml_attribute_struct* next = attr->next_attribute; xml_attribute_struct* prev = attr->prev_attribute_c; if (next) next->prev_attribute_c = prev; else node->first_attribute->prev_attribute_c = prev; if (prev->next_attribute) prev->next_attribute = next; else node->first_attribute = next; attr->prev_attribute_c = 0; attr->next_attribute = 0; } PUGI_IMPL_FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) { if (!alloc.reserve()) return 0; xml_node_struct* child = allocate_node(alloc, type); if (!child) return 0; append_node(child, node); return child; } PUGI_IMPL_FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) { if (!alloc.reserve()) return 0; xml_attribute_struct* attr = allocate_attribute(alloc); if (!attr) return 0; append_attribute(attr, node); return attr; } PUGI_IMPL_NS_END // Helper classes for code generation PUGI_IMPL_NS_BEGIN struct opt_false { enum { value = 0 }; }; struct opt_true { enum { value = 1 }; }; PUGI_IMPL_NS_END // Unicode utilities PUGI_IMPL_NS_BEGIN inline uint16_t endian_swap(uint16_t value) { return static_cast(((value & 0xff) << 8) | (value >> 8)); } inline uint32_t endian_swap(uint32_t value) { return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); } struct utf8_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) return result + 1; // U+0080..U+07FF else if (ch < 0x800) return result + 2; // U+0800..U+FFFF else return result + 3; } static value_type high(value_type result, uint32_t) { // U+10000..U+10FFFF return result + 4; } }; struct utf8_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { // U+0000..U+007F if (ch < 0x80) { *result = static_cast(ch); return result + 1; } // U+0080..U+07FF else if (ch < 0x800) { result[0] = static_cast(0xC0 | (ch >> 6)); result[1] = static_cast(0x80 | (ch & 0x3F)); return result + 2; } // U+0800..U+FFFF else { result[0] = static_cast(0xE0 | (ch >> 12)); result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[2] = static_cast(0x80 | (ch & 0x3F)); return result + 3; } } static value_type high(value_type result, uint32_t ch) { // U+10000..U+10FFFF result[0] = static_cast(0xF0 | (ch >> 18)); result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); result[3] = static_cast(0x80 | (ch & 0x3F)); return result + 4; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf16_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 2; } }; struct utf16_writer { typedef uint16_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { uint32_t msh = static_cast(ch - 0x10000) >> 10; uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; result[0] = static_cast(0xD800 + msh); result[1] = static_cast(0xDC00 + lsh); return result + 2; } static value_type any(value_type result, uint32_t ch) { return (ch < 0x10000) ? low(result, ch) : high(result, ch); } }; struct utf32_counter { typedef size_t value_type; static value_type low(value_type result, uint32_t) { return result + 1; } static value_type high(value_type result, uint32_t) { return result + 1; } }; struct utf32_writer { typedef uint32_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type high(value_type result, uint32_t ch) { *result = ch; return result + 1; } static value_type any(value_type result, uint32_t ch) { *result = ch; return result + 1; } }; struct latin1_writer { typedef uint8_t* value_type; static value_type low(value_type result, uint32_t ch) { *result = static_cast(ch > 255 ? '?' : ch); return result + 1; } static value_type high(value_type result, uint32_t ch) { (void)ch; *result = '?'; return result + 1; } }; struct utf8_decoder { typedef uint8_t type; template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) { const uint8_t utf8_byte_mask = 0x3f; while (size) { uint8_t lead = *data; // 0xxxxxxx -> U+0000..U+007F if (lead < 0x80) { result = Traits::low(result, lead); data += 1; size -= 1; // process aligned single-byte (ascii) blocks if ((reinterpret_cast(data) & 3) == 0) { // round-trip through void* to silence 'cast increases required alignment of target type' warnings while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) { result = Traits::low(result, data[0]); result = Traits::low(result, data[1]); result = Traits::low(result, data[2]); result = Traits::low(result, data[3]); data += 4; size -= 4; } } } // 110xxxxx -> U+0080..U+07FF else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); data += 2; size -= 2; } // 1110xxxx -> U+0800-U+FFFF else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) { result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); data += 3; size -= 3; } // 11110xxx -> U+10000..U+10FFFF else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) { result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); data += 4; size -= 4; } // 10xxxxxx or 11111xxx -> invalid else { data += 1; size -= 1; } } return result; } }; template struct utf16_decoder { typedef uint16_t type; template static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits) { while (size) { uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+D7FF if (lead < 0xD800) { result = Traits::low(result, lead); data += 1; size -= 1; } // U+E000..U+FFFF else if (static_cast(lead - 0xE000) < 0x2000) { result = Traits::low(result, lead); data += 1; size -= 1; } // surrogate pair lead else if (static_cast(lead - 0xD800) < 0x400 && size >= 2) { uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; if (static_cast(next - 0xDC00) < 0x400) { result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); data += 2; size -= 2; } else { data += 1; size -= 1; } } else { data += 1; size -= 1; } } return result; } }; template struct utf32_decoder { typedef uint32_t type; template static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits) { while (size) { uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; // U+0000..U+FFFF if (lead < 0x10000) { result = Traits::low(result, lead); data += 1; size -= 1; } // U+10000..U+10FFFF else { result = Traits::high(result, lead); data += 1; size -= 1; } } return result; } }; struct latin1_decoder { typedef uint8_t type; template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) { while (size) { result = Traits::low(result, *data); data += 1; size -= 1; } return result; } }; template struct wchar_selector; template <> struct wchar_selector<2> { typedef uint16_t type; typedef utf16_counter counter; typedef utf16_writer writer; typedef utf16_decoder decoder; }; template <> struct wchar_selector<4> { typedef uint32_t type; typedef utf32_counter counter; typedef utf32_writer writer; typedef utf32_decoder decoder; }; typedef wchar_selector::counter wchar_counter; typedef wchar_selector::writer wchar_writer; struct wchar_decoder { typedef wchar_t type; template static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits) { typedef wchar_selector::decoder decoder; return decoder::process(reinterpret_cast(data), size, result, traits); } }; #ifdef PUGIXML_WCHAR_MODE PUGI_IMPL_FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) { for (size_t i = 0; i < length; ++i) result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); } #endif PUGI_IMPL_NS_END PUGI_IMPL_NS_BEGIN enum chartype_t { ct_parse_pcdata = 1, // \0, &, \r, < ct_parse_attr = 2, // \0, &, \r, ', " ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab ct_space = 8, // \r, \n, space, tab ct_parse_cdata = 16, // \0, ], >, \r ct_parse_comment = 32, // \0, -, >, \r ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : }; static const unsigned char chartype_table[256] = { 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 }; enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, ", ' ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . }; static const unsigned char chartypex_table[256] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 1, 0, // 48-63 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 }; #ifdef PUGIXML_WCHAR_MODE #define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) #else #define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) #endif #define PUGI_IMPL_IS_CHARTYPE(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartype_table) #define PUGI_IMPL_IS_CHARTYPEX(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartypex_table) PUGI_IMPL_FN bool is_little_endian() { unsigned int ui = 1; return *reinterpret_cast(&ui) == 1; } PUGI_IMPL_FN xml_encoding get_wchar_encoding() { PUGI_IMPL_STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); if (sizeof(wchar_t) == 2) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; else return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; } PUGI_IMPL_FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) { #define PUGI_IMPL_SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } #define PUGI_IMPL_SCANCHARTYPE(ct) { while (offset < size && PUGI_IMPL_IS_CHARTYPE(data[offset], ct)) offset++; } // check if we have a non-empty XML declaration if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI_IMPL_IS_CHARTYPE(data[5], ct_space))) return false; // scan XML declaration until the encoding field for (size_t i = 6; i + 1 < size; ++i) { // declaration can not contain ? in quoted values if (data[i] == '?') return false; if (data[i] == 'e' && data[i + 1] == 'n') { size_t offset = i; // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed PUGI_IMPL_SCANCHAR('e'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('c'); PUGI_IMPL_SCANCHAR('o'); PUGI_IMPL_SCANCHAR('d'); PUGI_IMPL_SCANCHAR('i'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('g'); // S? = S? PUGI_IMPL_SCANCHARTYPE(ct_space); PUGI_IMPL_SCANCHAR('='); PUGI_IMPL_SCANCHARTYPE(ct_space); // the only two valid delimiters are ' and " uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; PUGI_IMPL_SCANCHAR(delimiter); size_t start = offset; out_encoding = data + offset; PUGI_IMPL_SCANCHARTYPE(ct_symbol); out_length = offset - start; PUGI_IMPL_SCANCHAR(delimiter); return true; } } return false; #undef PUGI_IMPL_SCANCHAR #undef PUGI_IMPL_SCANCHARTYPE } PUGI_IMPL_FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size) { // skip encoding autodetection if input buffer is too small if (size < 4) return encoding_utf8; uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; // look for BOM in first few bytes if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; // look for <, (contents); return guess_buffer_encoding(data, size); } PUGI_IMPL_FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { size_t length = size / sizeof(char_t); if (is_mutable) { out_buffer = static_cast(const_cast(contents)); out_length = length; } else { char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; if (contents) memcpy(buffer, contents, length * sizeof(char_t)); else assert(length == 0); buffer[length] = 0; out_buffer = buffer; out_length = length + 1; } return true; } #ifdef PUGIXML_WCHAR_MODE PUGI_IMPL_FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) { return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); } PUGI_IMPL_FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const char_t* data = static_cast(contents); size_t length = size / sizeof(char_t); if (is_mutable) { char_t* buffer = const_cast(data); convert_wchar_endian_swap(buffer, data, length); out_buffer = buffer; out_length = length; } else { char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; convert_wchar_endian_swap(buffer, data, length); buffer[length] = 0; out_buffer = buffer; out_length = length + 1; } return true; } template PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { const typename D::type* data = static_cast(contents); size_t data_length = size / sizeof(typename D::type); // first pass: get length in wchar_t units size_t length = D::process(data, data_length, 0, wchar_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf16 input to wchar_t wchar_writer::value_type obegin = reinterpret_cast(buffer); wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer()); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // get native encoding xml_encoding wchar_encoding = get_wchar_encoding(); // fast path: no conversion required if (encoding == wchar_encoding) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // only endian-swapping is required if (need_endian_swap_utf(encoding, wchar_encoding)) return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf8 if (encoding == encoding_utf8) return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder()); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); assert(false && "Invalid encoding"); // unreachable return false; } #else template PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) { const typename D::type* data = static_cast(contents); size_t data_length = size / sizeof(typename D::type); // first pass: get length in utf8 units size_t length = D::process(data, data_length, 0, utf8_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert utf16 input to utf8 uint8_t* obegin = reinterpret_cast(buffer); uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI_IMPL_FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) { for (size_t i = 0; i < size; ++i) if (data[i] > 127) return i; return size; } PUGI_IMPL_FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) { const uint8_t* data = static_cast(contents); size_t data_length = size; // get size of prefix that does not need utf8 conversion size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); assert(prefix_length <= data_length); const uint8_t* postfix = data + prefix_length; size_t postfix_length = data_length - prefix_length; // if no conversion is needed, just return the original buffer if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // first pass: get length in utf8 units size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter()); // allocate buffer of suitable length char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!buffer) return false; // second pass: convert latin1 input to utf8 memcpy(buffer, data, prefix_length); uint8_t* obegin = reinterpret_cast(buffer); uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer()); assert(oend == obegin + length); *oend = 0; out_buffer = buffer; out_length = length + 1; return true; } PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) { // fast path: no conversion required if (encoding == encoding_utf8) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); // source encoding is utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); } // source encoding is utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return (native_encoding == encoding) ? convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); } // source encoding is latin1 if (encoding == encoding_latin1) return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); assert(false && "Invalid encoding"); // unreachable return false; } #endif PUGI_IMPL_FN size_t as_utf8_begin(const wchar_t* str, size_t length) { // get length in utf8 characters return wchar_decoder::process(str, length, 0, utf8_counter()); } PUGI_IMPL_FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) { // convert to utf8 uint8_t* begin = reinterpret_cast(buffer); uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); assert(begin + size == end); (void)!end; (void)!size; } #ifndef PUGIXML_NO_STL PUGI_IMPL_FN std::string as_utf8_impl(const wchar_t* str, size_t length) { // first pass: get length in utf8 characters size_t size = as_utf8_begin(str, length); // allocate resulting string std::string result; result.resize(size); // second pass: convert to utf8 if (size > 0) as_utf8_end(&result[0], size, str, length); return result; } PUGI_IMPL_FN std::basic_string as_wide_impl(const char* str, size_t size) { const uint8_t* data = reinterpret_cast(str); // first pass: get length in wchar_t units size_t length = utf8_decoder::process(data, size, 0, wchar_counter()); // allocate resulting string std::basic_string result; result.resize(length); // second pass: convert to wchar_t if (length > 0) { wchar_writer::value_type begin = reinterpret_cast(&result[0]); wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer()); assert(begin + length == end); (void)!end; } return result; } #endif template inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target) { // never reuse shared memory if (header & xml_memory_page_contents_shared_mask) return false; size_t target_length = strlength(target); // always reuse document buffer memory if possible if ((header & header_mask) == 0) return target_length >= length; // reuse heap memory if waste is not too great const size_t reuse_threshold = 32; return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); } template PUGI_IMPL_FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) { assert((header & header_mask) == 0 || dest); // header bit indicates whether dest was previously allocated if (source_length == 0) { // empty string and null pointer are equivalent, so just deallocate old memory xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator; if (header & header_mask) alloc->deallocate_string(dest); // mark the string as not allocated dest = 0; header &= ~header_mask; return true; } else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) { // we can reuse old buffer, so just copy the new data (including zero terminator) memcpy(dest, source, source_length * sizeof(char_t)); dest[source_length] = 0; return true; } else { xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator; if (!alloc->reserve()) return false; // allocate new buffer char_t* buf = alloc->allocate_string(source_length + 1); if (!buf) return false; // copy the string (including zero terminator) memcpy(buf, source, source_length * sizeof(char_t)); buf[source_length] = 0; // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) if (header & header_mask) alloc->deallocate_string(dest); // the string is now allocated, so set the flag dest = buf; header |= header_mask; return true; } } struct gap { char_t* end; size_t size; gap(): end(0), size(0) { } // Push new gap, move s count bytes further (skipping the gap). // Collapse previous gap. void push(char_t*& s, size_t count) { if (end) // there was a gap already; collapse it { // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); } s += count; // end of current gap // "merge" two gaps end = s; size += count; } // Collapse all gaps, return past-the-end pointer char_t* flush(char_t* s) { if (end) { // Move [old_gap_end, current_pos) to [old_gap_start, ...) assert(s >= end); memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); return s - size; } else return s; } }; PUGI_IMPL_FN char_t* strconv_escape(char_t* s, gap& g) { char_t* stre = s + 1; switch (*stre) { case '#': // &#... { unsigned int ucsc = 0; if (stre[1] == 'x') // &#x... (hex code) { stre += 2; char_t ch = *stre; if (ch == ';') return stre; for (;;) { if (static_cast(ch - '0') <= 9) ucsc = 16 * ucsc + (ch - '0'); else if (static_cast((ch | ' ') - 'a') <= 5) ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } else // &#... (dec code) { char_t ch = *++stre; if (ch == ';') return stre; for (;;) { if (static_cast(ch - '0') <= 9) ucsc = 10 * ucsc + (ch - '0'); else if (ch == ';') break; else // cancel return stre; ch = *++stre; } ++stre; } #ifdef PUGIXML_WCHAR_MODE s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); #else s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); #endif g.push(s, stre - s); return stre; } case 'a': // &a { ++stre; if (*stre == 'm') // &am { if (*++stre == 'p' && *++stre == ';') // & { *s++ = '&'; ++stre; g.push(s, stre - s); return stre; } } else if (*stre == 'p') // &ap { if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' { *s++ = '\''; ++stre; g.push(s, stre - s); return stre; } } break; } case 'g': // &g { if (*++stre == 't' && *++stre == ';') // > { *s++ = '>'; ++stre; g.push(s, stre - s); return stre; } break; } case 'l': // &l { if (*++stre == 't' && *++stre == ';') // < { *s++ = '<'; ++stre; g.push(s, stre - s); return stre; } break; } case 'q': // &q { if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " { *s++ = '"'; ++stre; g.push(s, stre - s); return stre; } break; } default: break; } return stre; } // Parser utilities #define PUGI_IMPL_ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) #define PUGI_IMPL_SKIPWS() { while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) ++s; } #define PUGI_IMPL_OPTSET(OPT) ( optmsk & (OPT) ) #define PUGI_IMPL_PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); } #define PUGI_IMPL_POPNODE() { cursor = cursor->parent; } #define PUGI_IMPL_SCANFOR(X) { while (*s != 0 && !(X)) ++s; } #define PUGI_IMPL_SCANWHILE(X) { while (X) ++s; } #define PUGI_IMPL_SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI_IMPL_UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 3; break; } s += 4; } } #define PUGI_IMPL_ENDSEG() { ch = *s; *s = 0; ++s; } #define PUGI_IMPL_THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) #define PUGI_IMPL_CHECK_ERROR(err, m) { if (*s == 0) PUGI_IMPL_THROW_ERROR(err, m); } PUGI_IMPL_FN char_t* strconv_comment(char_t* s, char_t endch) { gap g; while (true) { PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_comment)); if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')) // comment ends here { *g.flush(s) = 0; return s + (s[2] == '>' ? 3 : 2); } else if (*s == 0) { return 0; } else ++s; } } PUGI_IMPL_FN char_t* strconv_cdata(char_t* s, char_t endch) { gap g; while (true) { PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_cdata)); if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')) // CDATA ends here { *g.flush(s) = 0; return s + 1; } else if (*s == 0) { return 0; } else ++s; } } typedef char_t* (*strconv_pcdata_t)(char_t*); template struct strconv_pcdata_impl { static char_t* parse(char_t* s) { gap g; char_t* begin = s; while (true) { PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_pcdata)); if (*s == '<') // PCDATA ends here { char_t* end = g.flush(s); if (opt_trim::value) while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; return s + 1; } else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair { *s++ = '\n'; // replace first one with 0x0a if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (*s == 0) { char_t* end = g.flush(s); if (opt_trim::value) while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; return s; } else ++s; } } }; PUGI_IMPL_FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) { PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above { case 0: return strconv_pcdata_impl::parse; case 1: return strconv_pcdata_impl::parse; case 2: return strconv_pcdata_impl::parse; case 3: return strconv_pcdata_impl::parse; case 4: return strconv_pcdata_impl::parse; case 5: return strconv_pcdata_impl::parse; case 6: return strconv_pcdata_impl::parse; case 7: return strconv_pcdata_impl::parse; default: assert(false); return 0; // unreachable } } typedef char_t* (*strconv_attribute_t)(char_t*, char_t); template struct strconv_attribute_impl { static char_t* parse_wnorm(char_t* s, char_t end_quote) { gap g; // trim leading whitespaces if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { char_t* str = s; do ++str; while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)); g.push(s, str - s); } while (true) { PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); if (*s == end_quote) { char_t* str = g.flush(s); do *str-- = 0; while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)); return s + 1; } else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { *s++ = ' '; if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { char_t* str = s + 1; while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)) ++str; g.push(s, str - s); } } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_wconv(char_t* s, char_t end_quote) { gap g; while (true) { PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws)); if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) { if (*s == '\r') { *s++ = ' '; if (*s == '\n') g.push(s, 1); } else *s++ = ' '; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_eol(char_t* s, char_t end_quote) { gap g; while (true) { PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr)); if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (*s == '\r') { *s++ = '\n'; if (*s == '\n') g.push(s, 1); } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } static char_t* parse_simple(char_t* s, char_t end_quote) { gap g; while (true) { PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr)); if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (opt_escape::value && *s == '&') { s = strconv_escape(s, g); } else if (!*s) { return 0; } else ++s; } } }; PUGI_IMPL_FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) { PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above { case 0: return strconv_attribute_impl::parse_simple; case 1: return strconv_attribute_impl::parse_simple; case 2: return strconv_attribute_impl::parse_eol; case 3: return strconv_attribute_impl::parse_eol; case 4: return strconv_attribute_impl::parse_wconv; case 5: return strconv_attribute_impl::parse_wconv; case 6: return strconv_attribute_impl::parse_wconv; case 7: return strconv_attribute_impl::parse_wconv; case 8: return strconv_attribute_impl::parse_wnorm; case 9: return strconv_attribute_impl::parse_wnorm; case 10: return strconv_attribute_impl::parse_wnorm; case 11: return strconv_attribute_impl::parse_wnorm; case 12: return strconv_attribute_impl::parse_wnorm; case 13: return strconv_attribute_impl::parse_wnorm; case 14: return strconv_attribute_impl::parse_wnorm; case 15: return strconv_attribute_impl::parse_wnorm; default: assert(false); return 0; // unreachable } } inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) { xml_parse_result result; result.status = status; result.offset = offset; return result; } struct xml_parser { xml_allocator* alloc; char_t* error_offset; xml_parse_status error_status; xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) { } // DOCTYPE consists of nested sections of the following possible types: // , , "...", '...' // // // First group can not contain nested groups // Second group can contain nested groups of the same type // Third group can contain all other groups char_t* parse_doctype_primitive(char_t* s) { if (*s == '"' || *s == '\'') { // quoted string char_t ch = *s++; PUGI_IMPL_SCANFOR(*s == ch); if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); s++; } else if (s[0] == '<' && s[1] == '?') { // s += 2; PUGI_IMPL_SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); s += 2; } else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') { s += 4; PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); s += 3; } else PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_doctype_ignore(char_t* s) { size_t depth = 0; assert(s[0] == '<' && s[1] == '!' && s[2] == '['); s += 3; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] == '[') { // nested ignore section s += 3; depth++; } else if (s[0] == ']' && s[1] == ']' && s[2] == '>') { // ignore section end s += 3; if (depth == 0) return s; depth--; } else s++; } PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); } char_t* parse_doctype_group(char_t* s, char_t endch) { size_t depth = 0; assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); s += 2; while (*s) { if (s[0] == '<' && s[1] == '!' && s[2] != '-') { if (s[2] == '[') { // ignore s = parse_doctype_ignore(s); if (!s) return s; } else { // some control group s += 2; depth++; } } else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') { // unknown tag (forbidden), or some primitive group s = parse_doctype_primitive(s); if (!s) return s; } else if (*s == '>') { if (depth == 0) return s; depth--; s++; } else s++; } if (depth != 0 || endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); return s; } char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) { // parse node contents, starting with exclamation mark ++s; if (*s == '-') // 'value = s; // Save the offset. } if (PUGI_IMPL_OPTSET(parse_eol) && PUGI_IMPL_OPTSET(parse_comments)) { s = strconv_comment(s, endch); if (!s) PUGI_IMPL_THROW_ERROR(status_bad_comment, cursor->value); } else { // Scan for terminating '-->'. PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')); PUGI_IMPL_CHECK_ERROR(status_bad_comment, s); if (PUGI_IMPL_OPTSET(parse_comments)) *s = 0; // Zero-terminate this segment at the first terminating '-'. s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. } } else PUGI_IMPL_THROW_ERROR(status_bad_comment, s); } else if (*s == '[') { // 'value = s; // Save the offset. if (PUGI_IMPL_OPTSET(parse_eol)) { s = strconv_cdata(s, endch); if (!s) PUGI_IMPL_THROW_ERROR(status_bad_cdata, cursor->value); } else { // Scan for terminating ']]>'. PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')); PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s); *s++ = 0; // Zero-terminate this segment. } } else // Flagged for discard, but we still have to scan for the terminator. { // Scan for terminating ']]>'. PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')); PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s); ++s; } s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. } else PUGI_IMPL_THROW_ERROR(status_bad_cdata, s); } else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI_IMPL_ENDSWITH(s[6], 'E')) { s -= 2; if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); char_t* mark = s + 9; s = parse_doctype_group(s, endch); if (!s) return s; assert((*s == 0 && endch == '>') || *s == '>'); if (*s) *s++ = 0; if (PUGI_IMPL_OPTSET(parse_doctype)) { while (PUGI_IMPL_IS_CHARTYPE(*mark, ct_space)) ++mark; PUGI_IMPL_PUSHNODE(node_doctype); cursor->value = mark; } } else if (*s == 0 && endch == '-') PUGI_IMPL_THROW_ERROR(status_bad_comment, s); else if (*s == 0 && endch == '[') PUGI_IMPL_THROW_ERROR(status_bad_cdata, s); else PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s); return s; } char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) { // load into registers xml_node_struct* cursor = ref_cursor; char_t ch = 0; // parse node contents, starting with question mark ++s; // read PI target char_t* target = s; if (!PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); PUGI_IMPL_SCANWHILE(PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol)); PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); // determine node type; stricmp / strcasecmp is not portable bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; if (declaration ? PUGI_IMPL_OPTSET(parse_declaration) : PUGI_IMPL_OPTSET(parse_pi)) { if (declaration) { // disallow non top-level declarations if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); PUGI_IMPL_PUSHNODE(node_declaration); } else { PUGI_IMPL_PUSHNODE(node_pi); } cursor->name = target; PUGI_IMPL_ENDSEG(); // parse value/attributes if (ch == '?') { // empty node if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); s += (*s == '>'); PUGI_IMPL_POPNODE(); } else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { PUGI_IMPL_SKIPWS(); // scan for tag end char_t* value = s; PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>')); PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); if (declaration) { // replace ending ? with / so that 'element' terminates properly *s = '/'; // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES s = value; } else { // store value and step over > cursor->value = value; PUGI_IMPL_POPNODE(); PUGI_IMPL_ENDSEG(); s += (*s == '>'); } } else PUGI_IMPL_THROW_ERROR(status_bad_pi, s); } else { // scan for tag end PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>')); PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); s += (s[1] == '>' ? 2 : 1); } // store from registers ref_cursor = cursor; return s; } char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) { strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); char_t ch = 0; xml_node_struct* cursor = root; char_t* mark = s; char_t* merged_pcdata = s; while (*s != 0) { if (*s == '<') { ++s; LOC_TAG: if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' { PUGI_IMPL_PUSHNODE(node_element); // Append a new node to the tree. cursor->name = s; PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over. if (ch == '>') { // end of tag } else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { LOC_ATTRIBUTES: while (true) { PUGI_IMPL_SKIPWS(); // Eat any whitespace. if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // <... #... { xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. if (!a) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); a->name = s; // Save the offset. PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over. if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { PUGI_IMPL_SKIPWS(); // Eat any whitespace. ch = *s; ++s; } if (ch == '=') // '<... #=...' { PUGI_IMPL_SKIPWS(); // Eat any whitespace. if (*s == '"' || *s == '\'') // '<... #="...' { ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. ++s; // Step over the quote. a->value = s; // Save the offset. s = strconv_attribute(s, ch); if (!s) PUGI_IMPL_THROW_ERROR(status_bad_attribute, a->value); // After this line the loop continues from the start; // Whitespaces, / and > are ok, symbols and EOF are wrong, // everything else will be detected if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); } else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); } else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); } else if (*s == '/') { ++s; if (*s == '>') { PUGI_IMPL_POPNODE(); s++; break; } else if (*s == 0 && endch == '>') { PUGI_IMPL_POPNODE(); break; } else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } else if (*s == '>') { ++s; break; } else if (*s == 0 && endch == '>') { break; } else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } // !!! } else if (ch == '/') // '<#.../' { if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); PUGI_IMPL_POPNODE(); // Pop. s += (*s == '>'); } else if (ch == 0) { // we stepped over null terminator, backtrack & handle closing tag --s; if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); } else if (*s == '/') { ++s; mark = s; char_t* name = cursor->name; if (!name) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); while (PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol)) { if (*s++ != *name++) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); } if (*name) { if (*s == 0 && name[0] == endch && name[1] == 0) PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); else PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); } PUGI_IMPL_POPNODE(); // Pop. PUGI_IMPL_SKIPWS(); if (*s == 0) { if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); } else { if (*s != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); ++s; } } else if (*s == '?') // 'first_child) continue; } } if (!PUGI_IMPL_OPTSET(parse_trim_pcdata)) s = mark; if (cursor->parent || PUGI_IMPL_OPTSET(parse_fragment)) { char_t* parsed_pcdata = s; s = strconv_pcdata(s); if (PUGI_IMPL_OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) { cursor->value = parsed_pcdata; // Save the offset. } else if (PUGI_IMPL_OPTSET(parse_merge_pcdata) && cursor->first_child && PUGI_IMPL_NODETYPE(cursor->first_child->prev_sibling_c) == node_pcdata) { assert(merged_pcdata >= cursor->first_child->prev_sibling_c->value); // Catch up to the end of last parsed value; only needed for the first fragment. merged_pcdata += strlength(merged_pcdata); size_t length = strlength(parsed_pcdata); // Must use memmove instead of memcpy as this move may overlap memmove(merged_pcdata, parsed_pcdata, (length + 1) * sizeof(char_t)); merged_pcdata += length; } else { xml_node_struct* prev_cursor = cursor; PUGI_IMPL_PUSHNODE(node_pcdata); // Append a new node on the tree. cursor->value = parsed_pcdata; // Save the offset. merged_pcdata = parsed_pcdata; // Used for parse_merge_pcdata above, cheaper to save unconditionally cursor = prev_cursor; // Pop since this is a standalone. } if (!*s) break; } else { PUGI_IMPL_SCANFOR(*s == '<'); // '...<' if (!*s) break; ++s; } // We're after '<' goto LOC_TAG; } } // check that last tag is closed if (cursor != root) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, s); return s; } #ifdef PUGIXML_WCHAR_MODE static char_t* parse_skip_bom(char_t* s) { unsigned int bom = 0xfeff; return (s[0] == static_cast(bom)) ? s + 1 : s; } #else static char_t* parse_skip_bom(char_t* s) { return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; } #endif static bool has_element_node_siblings(xml_node_struct* node) { while (node) { if (PUGI_IMPL_NODETYPE(node) == node_element) return true; node = node->next_sibling; } return false; } static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) { // early-out for empty documents if (length == 0) return make_parse_result(PUGI_IMPL_OPTSET(parse_fragment) ? status_ok : status_no_document_element); // get last child of the root before parsing xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0; // create parser on stack xml_parser parser(static_cast(xmldoc)); // save last character and make buffer zero-terminated (speeds up parsing) char_t endch = buffer[length - 1]; buffer[length - 1] = 0; // skip BOM to make sure it does not end up as part of parse output char_t* buffer_data = parse_skip_bom(buffer); // perform actual parsing parser.parse_tree(buffer_data, root, optmsk, endch); xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); assert(result.offset >= 0 && static_cast(result.offset) <= length); if (result) { // since we removed last character, we have to handle the only possible false positive (stray <) if (endch == '<') return make_parse_result(status_unrecognized_tag, length - 1); // check if there are any element nodes parsed xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child + 0; if (!PUGI_IMPL_OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) return make_parse_result(status_no_document_element, length - 1); } else { // roll back offset if it occurs on a null terminator in the source buffer if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) result.offset--; } return result; } }; // Output facilities PUGI_IMPL_FN xml_encoding get_write_native_encoding() { #ifdef PUGIXML_WCHAR_MODE return get_wchar_encoding(); #else return encoding_utf8; #endif } PUGI_IMPL_FN xml_encoding get_write_encoding(xml_encoding encoding) { // replace wchar encoding with utf implementation if (encoding == encoding_wchar) return get_wchar_encoding(); // replace utf16 encoding with utf16 with specific endianness if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; // replace utf32 encoding with utf32 with specific endianness if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; // only do autodetection if no explicit encoding is requested if (encoding != encoding_auto) return encoding; // assume utf8 encoding return encoding_utf8; } template PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) { PUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); return static_cast(end - dest) * sizeof(*dest); } template PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) { PUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); if (opt_swap) { for (typename T::value_type i = dest; i != end; ++i) *i = endian_swap(*i); } return static_cast(end - dest) * sizeof(*dest); } #ifdef PUGIXML_WCHAR_MODE PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length) { if (length < 1) return 0; // discard last character if it's the lead of a surrogate pair return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; } PUGI_IMPL_FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { // only endian-swapping is required if (need_endian_swap_utf(encoding, get_wchar_encoding())) { convert_wchar_endian_swap(r_char, data, length); return length * sizeof(char_t); } // convert to utf8 if (encoding == encoding_utf8) return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); // convert to utf16 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding); } // convert to utf32 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding); } // convert to latin1 if (encoding == encoding_latin1) return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); assert(false && "Invalid encoding"); // unreachable return 0; } #else PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length) { if (length < 5) return 0; for (size_t i = 1; i <= 4; ++i) { uint8_t ch = static_cast(data[length - i]); // either a standalone character or a leading one if ((ch & 0xc0) != 0x80) return length - i; } // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk return length; } PUGI_IMPL_FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) { if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding); } if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) { xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding); } if (encoding == encoding_latin1) return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); assert(false && "Invalid encoding"); // unreachable return 0; } #endif class xml_buffered_writer { xml_buffered_writer(const xml_buffered_writer&); xml_buffered_writer& operator=(const xml_buffered_writer&); public: xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) { PUGI_IMPL_STATIC_ASSERT(bufcapacity >= 8); } size_t flush() { flush(buffer, bufsize); bufsize = 0; return 0; } void flush(const char_t* data, size_t size) { if (size == 0) return; // fast path, just write data if (encoding == get_write_native_encoding()) writer.write(data, size * sizeof(char_t)); else { // convert chunk size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); assert(result <= sizeof(scratch)); // write data writer.write(scratch.data_u8, result); } } void write_direct(const char_t* data, size_t length) { // flush the remaining buffer contents flush(); // handle large chunks if (length > bufcapacity) { if (encoding == get_write_native_encoding()) { // fast path, can just write data chunk writer.write(data, length * sizeof(char_t)); return; } // need to convert in suitable chunks while (length > bufcapacity) { // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) size_t chunk_size = get_valid_length(data, bufcapacity); assert(chunk_size); // convert chunk and write flush(data, chunk_size); // iterate data += chunk_size; length -= chunk_size; } // small tail is copied below bufsize = 0; } memcpy(buffer + bufsize, data, length * sizeof(char_t)); bufsize += length; } void write_buffer(const char_t* data, size_t length) { size_t offset = bufsize; if (offset + length <= bufcapacity) { memcpy(buffer + offset, data, length * sizeof(char_t)); bufsize = offset + length; } else { write_direct(data, length); } } void write_string(const char_t* data) { // write the part of the string that fits in the buffer size_t offset = bufsize; while (*data && offset < bufcapacity) buffer[offset++] = *data++; // write the rest if (offset < bufcapacity) { bufsize = offset; } else { // backtrack a bit if we have split the codepoint size_t length = offset - bufsize; size_t extra = length - get_valid_length(data - length, length); bufsize = offset - extra; write_direct(data - extra, strlength(data) + extra); } } void write(char_t d0) { size_t offset = bufsize; if (offset > bufcapacity - 1) offset = flush(); buffer[offset + 0] = d0; bufsize = offset + 1; } void write(char_t d0, char_t d1) { size_t offset = bufsize; if (offset > bufcapacity - 2) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; bufsize = offset + 2; } void write(char_t d0, char_t d1, char_t d2) { size_t offset = bufsize; if (offset > bufcapacity - 3) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; bufsize = offset + 3; } void write(char_t d0, char_t d1, char_t d2, char_t d3) { size_t offset = bufsize; if (offset > bufcapacity - 4) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; buffer[offset + 3] = d3; bufsize = offset + 4; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) { size_t offset = bufsize; if (offset > bufcapacity - 5) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; buffer[offset + 3] = d3; buffer[offset + 4] = d4; bufsize = offset + 5; } void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) { size_t offset = bufsize; if (offset > bufcapacity - 6) offset = flush(); buffer[offset + 0] = d0; buffer[offset + 1] = d1; buffer[offset + 2] = d2; buffer[offset + 3] = d3; buffer[offset + 4] = d4; buffer[offset + 5] = d5; bufsize = offset + 6; } // utf8 maximum expansion: x4 (-> utf32) // utf16 maximum expansion: x2 (-> utf32) // utf32 maximum expansion: x1 enum { bufcapacitybytes = #ifdef PUGIXML_MEMORY_OUTPUT_STACK PUGIXML_MEMORY_OUTPUT_STACK #else 10240 #endif , bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) }; char_t buffer[bufcapacity]; union { uint8_t data_u8[4 * bufcapacity]; uint16_t data_u16[2 * bufcapacity]; uint32_t data_u32[bufcapacity]; char_t data_char[bufcapacity]; } scratch; xml_writer& writer; size_t bufsize; xml_encoding encoding; }; PUGI_IMPL_FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { while (*s) { const char_t* prev = s; // While *s is a usual symbol PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPEX(ss, type)); writer.write_buffer(prev, static_cast(s - prev)); switch (*s) { case 0: break; case '&': writer.write('&', 'a', 'm', 'p', ';'); ++s; break; case '<': writer.write('&', 'l', 't', ';'); ++s; break; case '>': writer.write('&', 'g', 't', ';'); ++s; break; case '"': if (flags & format_attribute_single_quote) writer.write('"'); else writer.write('&', 'q', 'u', 'o', 't', ';'); ++s; break; case '\'': if (flags & format_attribute_single_quote) writer.write('&', 'a', 'p', 'o', 's', ';'); else writer.write('\''); ++s; break; default: // s is not a usual symbol { unsigned int ch = static_cast(*s++); assert(ch < 32); if (!(flags & format_skip_control_chars)) writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); } } } } PUGI_IMPL_FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { if (flags & format_no_escapes) writer.write_string(s); else text_output_escaped(writer, s, type, flags); } PUGI_IMPL_FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) { do { writer.write('<', '!', '[', 'C', 'D'); writer.write('A', 'T', 'A', '['); const char_t* prev = s; // look for ]]> sequence - we can't output it as is since it terminates CDATA while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; // skip ]] if we stopped at ]]>, > will go to the next CDATA section if (*s) s += 2; writer.write_buffer(prev, static_cast(s - prev)); writer.write(']', ']', '>'); } while (*s); } PUGI_IMPL_FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) { switch (indent_length) { case 1: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0]); break; } case 2: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0], indent[1]); break; } case 3: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0], indent[1], indent[2]); break; } case 4: { for (unsigned int i = 0; i < depth; ++i) writer.write(indent[0], indent[1], indent[2], indent[3]); break; } default: { for (unsigned int i = 0; i < depth; ++i) writer.write_buffer(indent, indent_length); } } } PUGI_IMPL_FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) { writer.write('<', '!', '-', '-'); while (*s) { const char_t* prev = s; // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; writer.write_buffer(prev, static_cast(s - prev)); if (*s) { assert(*s == '-'); writer.write('-', ' '); ++s; } } writer.write('-', '-', '>'); } PUGI_IMPL_FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) { while (*s) { const char_t* prev = s; // look for ?> sequence - we can't output it since ?> terminates PI while (*s && !(s[0] == '?' && s[1] == '>')) ++s; writer.write_buffer(prev, static_cast(s - prev)); if (*s) { assert(s[0] == '?' && s[1] == '>'); writer.write('?', ' ', '>'); s += 2; } } } PUGI_IMPL_FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"'; for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) { writer.write('\n'); text_output_indent(writer, indent, indent_length, depth + 1); } else { writer.write(' '); } writer.write_string(a->name ? a->name + 0 : default_name); writer.write('=', enquotation_char); if (a->value) text_output(writer, a->value, ctx_special_attr, flags); writer.write(enquotation_char); } } PUGI_IMPL_FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* name = node->name ? node->name + 0 : default_name; writer.write('<'); writer.write_string(name); if (node->first_attribute) node_output_attributes(writer, node, indent, indent_length, flags, depth); // element nodes can have value if parse_embed_pcdata was used if (!node->value) { if (!node->first_child) { if (flags & format_no_empty_element_tags) { writer.write('>', '<', '/'); writer.write_string(name); writer.write('>'); return false; } else { if ((flags & format_raw) == 0) writer.write(' '); writer.write('/', '>'); return false; } } else { writer.write('>'); return true; } } else { writer.write('>'); text_output(writer, node->value, ctx_special_pcdata, flags); if (!node->first_child) { writer.write('<', '/'); writer.write_string(name); writer.write('>'); return false; } else { return true; } } } PUGI_IMPL_FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); const char_t* name = node->name ? node->name + 0 : default_name; writer.write('<', '/'); writer.write_string(name); writer.write('>'); } PUGI_IMPL_FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); switch (PUGI_IMPL_NODETYPE(node)) { case node_pcdata: text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags); break; case node_cdata: text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); break; case node_comment: node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); break; case node_pi: writer.write('<', '?'); writer.write_string(node->name ? node->name + 0 : default_name); if (node->value) { writer.write(' '); node_output_pi_value(writer, node->value); } writer.write('?', '>'); break; case node_declaration: writer.write('<', '?'); writer.write_string(node->name ? node->name + 0 : default_name); node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); writer.write('?', '>'); break; case node_doctype: writer.write('<', '!', 'D', 'O', 'C'); writer.write('T', 'Y', 'P', 'E'); if (node->value) { writer.write(' '); writer.write_string(node->value); } writer.write('>'); break; default: assert(false && "Invalid node type"); // unreachable } } enum indent_flags_t { indent_newline = 1, indent_indent = 2 }; PUGI_IMPL_FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) { size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; unsigned int indent_flags = indent_indent; xml_node_struct* node = root; do { assert(node); // begin writing current node if (PUGI_IMPL_NODETYPE(node) == node_pcdata || PUGI_IMPL_NODETYPE(node) == node_cdata) { node_output_simple(writer, node, flags); indent_flags = 0; } else { if ((indent_flags & indent_newline) && (flags & format_raw) == 0) writer.write('\n'); if ((indent_flags & indent_indent) && indent_length) text_output_indent(writer, indent, indent_length, depth); if (PUGI_IMPL_NODETYPE(node) == node_element) { indent_flags = indent_newline | indent_indent; if (node_output_start(writer, node, indent, indent_length, flags, depth)) { // element nodes can have value if parse_embed_pcdata was used if (node->value) indent_flags = 0; node = node->first_child; depth++; continue; } } else if (PUGI_IMPL_NODETYPE(node) == node_document) { indent_flags = indent_indent; if (node->first_child) { node = node->first_child; continue; } } else { node_output_simple(writer, node, flags); indent_flags = indent_newline | indent_indent; } } // continue to the next node while (node != root) { if (node->next_sibling) { node = node->next_sibling; break; } node = node->parent; // write closing node if (PUGI_IMPL_NODETYPE(node) == node_element) { depth--; if ((indent_flags & indent_newline) && (flags & format_raw) == 0) writer.write('\n'); if ((indent_flags & indent_indent) && indent_length) text_output_indent(writer, indent, indent_length, depth); node_output_end(writer, node); indent_flags = indent_newline | indent_indent; } } } while (node != root); if ((indent_flags & indent_newline) && (flags & format_raw) == 0) writer.write('\n'); } PUGI_IMPL_FN bool has_declaration(xml_node_struct* node) { for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) { xml_node_type type = PUGI_IMPL_NODETYPE(child); if (type == node_declaration) return true; if (type == node_element) return false; } return false; } PUGI_IMPL_FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) { for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) if (a == attr) return true; return false; } PUGI_IMPL_FN bool allow_insert_attribute(xml_node_type parent) { return parent == node_element || parent == node_declaration; } PUGI_IMPL_FN bool allow_insert_child(xml_node_type parent, xml_node_type child) { if (parent != node_document && parent != node_element) return false; if (child == node_document || child == node_null) return false; if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; return true; } PUGI_IMPL_FN bool allow_move(xml_node parent, xml_node child) { // check that child can be a child of parent if (!allow_insert_child(parent.type(), child.type())) return false; // check that node is not moved between documents if (parent.root() != child.root()) return false; // check that new parent is not in the child subtree xml_node cur = parent; while (cur) { if (cur == child) return false; cur = cur.parent(); } return true; } template PUGI_IMPL_FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) { assert(!dest && (header & header_mask) == 0); // copies are performed into fresh nodes if (source) { if (alloc && (source_header & header_mask) == 0) { dest = source; // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared header |= xml_memory_page_contents_shared_mask; source_header |= xml_memory_page_contents_shared_mask; } else strcpy_insitu(dest, header, header_mask, source, strlength(source)); } } PUGI_IMPL_FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) { node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) { xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); if (da) { node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); } } } PUGI_IMPL_FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) { xml_allocator& alloc = get_allocator(dn); xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; node_copy_contents(dn, sn, shared_alloc); xml_node_struct* dit = dn; xml_node_struct* sit = sn->first_child; while (sit && sit != sn) { // loop invariant: dit is inside the subtree rooted at dn assert(dit); // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop if (sit != dn) { xml_node_struct* copy = append_new_node(dit, alloc, PUGI_IMPL_NODETYPE(sit)); if (copy) { node_copy_contents(copy, sit, shared_alloc); if (sit->first_child) { dit = copy; sit = sit->first_child; continue; } } } // continue to the next node do { if (sit->next_sibling) { sit = sit->next_sibling; break; } sit = sit->parent; dit = dit->parent; // loop invariant: dit is inside the subtree rooted at dn while sit is inside sn assert(sit == sn || dit); } while (sit != sn); } assert(!sit || dit == dn->parent); } PUGI_IMPL_FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) { xml_allocator& alloc = get_allocator(da); xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); } inline bool is_text_node(xml_node_struct* node) { xml_node_type type = PUGI_IMPL_NODETYPE(node); return type == node_pcdata || type == node_cdata; } // get value with conversion functions template PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv) { U result = 0; const char_t* s = value; while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) s++; bool negative = (*s == '-'); s += (*s == '+' || *s == '-'); bool overflow = false; if (s[0] == '0' && (s[1] | ' ') == 'x') { s += 2; // since overflow detection relies on length of the sequence skip leading zeros while (*s == '0') s++; const char_t* start = s; for (;;) { if (static_cast(*s - '0') < 10) result = result * 16 + (*s - '0'); else if (static_cast((*s | ' ') - 'a') < 6) result = result * 16 + ((*s | ' ') - 'a' + 10); else break; s++; } size_t digits = static_cast(s - start); overflow = digits > sizeof(U) * 2; } else { // since overflow detection relies on length of the sequence skip leading zeros while (*s == '0') s++; const char_t* start = s; for (;;) { if (static_cast(*s - '0') < 10) result = result * 10 + (*s - '0'); else break; s++; } size_t digits = static_cast(s - start); PUGI_IMPL_STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; const size_t high_bit = sizeof(U) * 8 - 1; overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); } if (negative) { // Workaround for crayc++ CC-3059: Expected no overflow in routine. #ifdef _CRAYC return (overflow || result > ~minv + 1) ? minv : ~result + 1; #else return (overflow || result > 0 - minv) ? minv : 0 - result; #endif } else return (overflow || result > maxv) ? maxv : result; } PUGI_IMPL_FN int get_value_int(const char_t* value) { return string_to_integer(value, static_cast(INT_MIN), INT_MAX); } PUGI_IMPL_FN unsigned int get_value_uint(const char_t* value) { return string_to_integer(value, 0, UINT_MAX); } PUGI_IMPL_FN double get_value_double(const char_t* value) { #ifdef PUGIXML_WCHAR_MODE return wcstod(value, 0); #else return strtod(value, 0); #endif } PUGI_IMPL_FN float get_value_float(const char_t* value) { #ifdef PUGIXML_WCHAR_MODE return static_cast(wcstod(value, 0)); #else return static_cast(strtod(value, 0)); #endif } PUGI_IMPL_FN bool get_value_bool(const char_t* value) { // only look at first char char_t first = *value; // 1*, t* (true), T* (True), y* (yes), Y* (YES) return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); } #ifdef PUGIXML_HAS_LONG_LONG PUGI_IMPL_FN long long get_value_llong(const char_t* value) { return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX); } PUGI_IMPL_FN unsigned long long get_value_ullong(const char_t* value) { return string_to_integer(value, 0, ULLONG_MAX); } #endif template PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) { char_t* result = end - 1; U rest = negative ? 0 - value : value; do { *result-- = static_cast('0' + (rest % 10)); rest /= 10; } while (rest); assert(result >= begin); (void)begin; *result = '-'; return result + !negative; } // set value with conversion functions template PUGI_IMPL_FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf) { #ifdef PUGIXML_WCHAR_MODE char_t wbuf[128]; assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0])); size_t offset = 0; for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; return strcpy_insitu(dest, header, header_mask, wbuf, offset); #else return strcpy_insitu(dest, header, header_mask, buf, strlen(buf)); #endif } template PUGI_IMPL_FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative) { char_t buf[64]; char_t* end = buf + sizeof(buf) / sizeof(buf[0]); char_t* begin = integer_to_string(buf, end, value, negative); return strcpy_insitu(dest, header, header_mask, begin, end - begin); } template PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision) { char buf[128]; PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, double(value)); return set_value_ascii(dest, header, header_mask, buf); } template PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision) { char buf[128]; PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, value); return set_value_ascii(dest, header, header_mask, buf); } template PUGI_IMPL_FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value) { return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); } PUGI_IMPL_FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) { // check input buffer if (!contents && size) return make_parse_result(status_io_error); // get actual encoding xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); // if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it auto_deleter contents_guard(own ? contents : 0, xml_memory::deallocate); // get private buffer char_t* buffer = 0; size_t length = 0; // coverity[var_deref_model] if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); // after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it contents_guard.release(); // delete original buffer if we performed a conversion if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself if (own || buffer != contents) *out_buffer = buffer; // store buffer for offset_debug doc->buffer = buffer; // parse xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); // remember encoding res.encoding = buffer_encoding; return res; } // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick PUGI_IMPL_FN xml_parse_status get_file_size(FILE* file, size_t& out_result) { #if defined(__linux__) || defined(__APPLE__) // this simultaneously retrieves the file size and file mode (to guard against loading non-files) struct stat st; if (fstat(fileno(file), &st) != 0) return status_io_error; // anything that's not a regular file doesn't have a coherent length if (!S_ISREG(st.st_mode)) return status_io_error; typedef off_t length_type; length_type length = st.st_size; #elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 // there are 64-bit versions of fseek/ftell, let's use them typedef __int64 length_type; _fseeki64(file, 0, SEEK_END); length_type length = _ftelli64(file); _fseeki64(file, 0, SEEK_SET); #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) // there are 64-bit versions of fseek/ftell, let's use them typedef off64_t length_type; fseeko64(file, 0, SEEK_END); length_type length = ftello64(file); fseeko64(file, 0, SEEK_SET); #else // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. typedef long length_type; fseek(file, 0, SEEK_END); length_type length = ftell(file); fseek(file, 0, SEEK_SET); #endif // check for I/O errors if (length < 0) return status_io_error; // check for overflow size_t result = static_cast(length); if (static_cast(result) != length) return status_out_of_memory; // finalize out_result = result; return status_ok; } // This function assumes that buffer has extra sizeof(char_t) writable bytes after size PUGI_IMPL_FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) { // We only need to zero-terminate if encoding conversion does not do it for us #ifdef PUGIXML_WCHAR_MODE xml_encoding wchar_encoding = get_wchar_encoding(); if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) { size_t length = size / sizeof(char_t); static_cast(buffer)[length] = 0; return (length + 1) * sizeof(char_t); } #else if (encoding == encoding_utf8) { static_cast(buffer)[size] = 0; return size + 1; } #endif return size; } PUGI_IMPL_FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) { if (!file) return make_parse_result(status_file_not_found); // get file size (can result in I/O errors) size_t size = 0; xml_parse_status size_status = get_file_size(file, size); if (size_status != status_ok) return make_parse_result(size_status); size_t max_suffix_size = sizeof(char_t); // allocate buffer for the whole file char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); if (!contents) return make_parse_result(status_out_of_memory); // read file in memory size_t read_size = fread(contents, 1, size, file); if (read_size != size) { xml_memory::deallocate(contents); return make_parse_result(status_io_error); } xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); } PUGI_IMPL_FN void close_file(FILE* file) { fclose(file); } #ifndef PUGIXML_NO_STL template struct xml_stream_chunk { static xml_stream_chunk* create() { void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); if (!memory) return 0; return new (memory) xml_stream_chunk(); } static void destroy(xml_stream_chunk* chunk) { // free chunk chain while (chunk) { xml_stream_chunk* next_ = chunk->next; xml_memory::deallocate(chunk); chunk = next_; } } xml_stream_chunk(): next(0), size(0) { } xml_stream_chunk* next; size_t size; T data[xml_memory_page_size / sizeof(T)]; }; template PUGI_IMPL_FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { auto_deleter > chunks(0, xml_stream_chunk::destroy); // read file to a chunk list size_t total = 0; xml_stream_chunk* last = 0; while (!stream.eof()) { // allocate new chunk xml_stream_chunk* chunk = xml_stream_chunk::create(); if (!chunk) return status_out_of_memory; // append chunk to list if (last) last = last->next = chunk; else chunks.data = last = chunk; // read data to chunk stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); chunk->size = static_cast(stream.gcount()) * sizeof(T); // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // guard against huge files (chunk size is small enough to make this overflow check work) if (total + chunk->size < total) return status_out_of_memory; total += chunk->size; } size_t max_suffix_size = sizeof(char_t); // copy chunk list to a contiguous buffer char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); if (!buffer) return status_out_of_memory; char* write = buffer; for (xml_stream_chunk* chunk = chunks.data; chunk; chunk = chunk->next) { assert(write + chunk->size <= buffer + total); memcpy(write, chunk->data, chunk->size); write += chunk->size; } assert(write == buffer + total); // return buffer *out_buffer = buffer; *out_size = total; return status_ok; } template PUGI_IMPL_FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) { // get length of remaining data in stream typename std::basic_istream::pos_type pos = stream.tellg(); stream.seekg(0, std::ios::end); std::streamoff length = stream.tellg() - pos; stream.seekg(pos); if (stream.fail() || pos < 0) return status_io_error; // guard against huge files size_t read_length = static_cast(length); if (static_cast(read_length) != length || length < 0) return status_out_of_memory; size_t max_suffix_size = sizeof(char_t); // read stream data into memory (guard against stream exceptions with buffer holder) auto_deleter buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); if (!buffer.data) return status_out_of_memory; stream.read(static_cast(buffer.data), static_cast(read_length)); // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; // return buffer size_t actual_length = static_cast(stream.gcount()); assert(actual_length <= read_length); *out_buffer = buffer.release(); *out_size = actual_length * sizeof(T); return status_ok; } template PUGI_IMPL_FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) { void* buffer = 0; size_t size = 0; xml_parse_status status = status_ok; // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) if (stream.fail()) return make_parse_result(status_io_error); // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) if (stream.tellg() < 0) { stream.clear(); // clear error flags that could be set by a failing tellg status = load_stream_data_noseek(stream, &buffer, &size); } else status = load_stream_data_seek(stream, &buffer, &size); if (status != status_ok) return make_parse_result(status); xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); } #endif #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { #if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 FILE* file = 0; return _wfopen_s(&file, path, mode) == 0 ? file : 0; #else return _wfopen(path, mode); #endif } #else PUGI_IMPL_FN char* convert_path_heap(const wchar_t* str) { assert(str); // first pass: get length in utf8 characters size_t length = strlength_wide(str); size_t size = as_utf8_begin(str, length); // allocate resulting string char* result = static_cast(xml_memory::allocate(size + 1)); if (!result) return 0; // second pass: convert to utf8 as_utf8_end(result, size, str, length); // zero-terminate result[size] = 0; return result; } PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { // there is no standard function to open wide paths, so our best bet is to try utf8 path char* path_utf8 = convert_path_heap(path); if (!path_utf8) return 0; // convert mode to ASCII (we mirror _wfopen interface) char mode_ascii[4] = {0}; for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); // try to open the utf8 path FILE* result = fopen(path_utf8, mode_ascii); // free dummy buffer xml_memory::deallocate(path_utf8); return result; } #endif PUGI_IMPL_FN FILE* open_file(const char* path, const char* mode) { #if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 FILE* file = 0; return fopen_s(&file, path, mode) == 0 ? file : 0; #else return fopen(path, mode); #endif } PUGI_IMPL_FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; xml_writer_file writer(file); doc.save(writer, indent, flags, encoding); return fflush(file) == 0 && ferror(file) == 0; } struct name_null_sentry { xml_node_struct* node; char_t* name; name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name) { node->name = 0; } ~name_null_sentry() { node->name = name; } }; PUGI_IMPL_NS_END namespace pugi { PUGI_IMPL_FN xml_writer::~xml_writer() { } PUGI_IMPL_FN xml_writer_file::xml_writer_file(void* file_): file(file_) { } PUGI_IMPL_FN void xml_writer_file::write(const void* data, size_t size) { size_t result = fwrite(data, 1, size, static_cast(file)); (void)!result; // unfortunately we can't do proper error handling here } #ifndef PUGIXML_NO_STL PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) { } PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) { } PUGI_IMPL_FN void xml_writer_stream::write(const void* data, size_t size) { if (narrow_stream) { assert(!wide_stream); narrow_stream->write(reinterpret_cast(data), static_cast(size)); } else { assert(wide_stream); assert(size % sizeof(wchar_t) == 0); wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); } } #endif PUGI_IMPL_FN xml_tree_walker::xml_tree_walker(): _depth(0) { } PUGI_IMPL_FN xml_tree_walker::~xml_tree_walker() { } PUGI_IMPL_FN int xml_tree_walker::depth() const { return _depth; } PUGI_IMPL_FN bool xml_tree_walker::begin(xml_node&) { return true; } PUGI_IMPL_FN bool xml_tree_walker::end(xml_node&) { return true; } PUGI_IMPL_FN xml_attribute::xml_attribute(): _attr(0) { } PUGI_IMPL_FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) { } PUGI_IMPL_FN static void unspecified_bool_xml_attribute(xml_attribute***) { } PUGI_IMPL_FN xml_attribute::operator xml_attribute::unspecified_bool_type() const { return _attr ? unspecified_bool_xml_attribute : 0; } PUGI_IMPL_FN bool xml_attribute::operator!() const { return !_attr; } PUGI_IMPL_FN bool xml_attribute::operator==(const xml_attribute& r) const { return (_attr == r._attr); } PUGI_IMPL_FN bool xml_attribute::operator!=(const xml_attribute& r) const { return (_attr != r._attr); } PUGI_IMPL_FN bool xml_attribute::operator<(const xml_attribute& r) const { return (_attr < r._attr); } PUGI_IMPL_FN bool xml_attribute::operator>(const xml_attribute& r) const { return (_attr > r._attr); } PUGI_IMPL_FN bool xml_attribute::operator<=(const xml_attribute& r) const { return (_attr <= r._attr); } PUGI_IMPL_FN bool xml_attribute::operator>=(const xml_attribute& r) const { return (_attr >= r._attr); } PUGI_IMPL_FN xml_attribute xml_attribute::next_attribute() const { if (!_attr) return xml_attribute(); return xml_attribute(_attr->next_attribute); } PUGI_IMPL_FN xml_attribute xml_attribute::previous_attribute() const { if (!_attr) return xml_attribute(); xml_attribute_struct* prev = _attr->prev_attribute_c; return prev->next_attribute ? xml_attribute(prev) : xml_attribute(); } PUGI_IMPL_FN const char_t* xml_attribute::as_string(const char_t* def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? value : def; } PUGI_IMPL_FN int xml_attribute::as_int(int def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? impl::get_value_int(value) : def; } PUGI_IMPL_FN unsigned int xml_attribute::as_uint(unsigned int def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? impl::get_value_uint(value) : def; } PUGI_IMPL_FN double xml_attribute::as_double(double def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? impl::get_value_double(value) : def; } PUGI_IMPL_FN float xml_attribute::as_float(float def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? impl::get_value_float(value) : def; } PUGI_IMPL_FN bool xml_attribute::as_bool(bool def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? impl::get_value_bool(value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI_IMPL_FN long long xml_attribute::as_llong(long long def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? impl::get_value_llong(value) : def; } PUGI_IMPL_FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const { if (!_attr) return def; const char_t* value = _attr->value; return value ? impl::get_value_ullong(value) : def; } #endif PUGI_IMPL_FN bool xml_attribute::empty() const { return !_attr; } PUGI_IMPL_FN const char_t* xml_attribute::name() const { if (!_attr) return PUGIXML_TEXT(""); const char_t* name = _attr->name; return name ? name : PUGIXML_TEXT(""); } PUGI_IMPL_FN const char_t* xml_attribute::value() const { if (!_attr) return PUGIXML_TEXT(""); const char_t* value = _attr->value; return value ? value : PUGIXML_TEXT(""); } PUGI_IMPL_FN size_t xml_attribute::hash_value() const { return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); } PUGI_IMPL_FN xml_attribute_struct* xml_attribute::internal_object() const { return _attr; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(const char_t* rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(int rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned int rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(double rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(float rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(bool rhs) { set_value(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long long rhs) { set_value(rhs); return *this; } PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) { set_value(rhs); return *this; } #endif PUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } PUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs, size_t size) { if (!_attr) return false; return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size); } PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs) { if (!_attr) return false; return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs, size_t size) { if (!_attr) return false; return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size); } PUGI_IMPL_FN bool xml_attribute::set_value(int rhs) { if (!_attr) return false; return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); } PUGI_IMPL_FN bool xml_attribute::set_value(unsigned int rhs) { if (!_attr) return false; return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); } PUGI_IMPL_FN bool xml_attribute::set_value(long rhs) { if (!_attr) return false; return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); } PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long rhs) { if (!_attr) return false; return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); } PUGI_IMPL_FN bool xml_attribute::set_value(double rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision); } PUGI_IMPL_FN bool xml_attribute::set_value(double rhs, int precision) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); } PUGI_IMPL_FN bool xml_attribute::set_value(float rhs) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision); } PUGI_IMPL_FN bool xml_attribute::set_value(float rhs, int precision) { if (!_attr) return false; return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); } PUGI_IMPL_FN bool xml_attribute::set_value(bool rhs) { if (!_attr) return false; return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); } #ifdef PUGIXML_HAS_LONG_LONG PUGI_IMPL_FN bool xml_attribute::set_value(long long rhs) { if (!_attr) return false; return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); } PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long long rhs) { if (!_attr) return false; return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); } #endif #ifdef __BORLANDC__ PUGI_IMPL_FN bool operator&&(const xml_attribute& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI_IMPL_FN bool operator||(const xml_attribute& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI_IMPL_FN xml_node::xml_node(): _root(0) { } PUGI_IMPL_FN xml_node::xml_node(xml_node_struct* p): _root(p) { } PUGI_IMPL_FN static void unspecified_bool_xml_node(xml_node***) { } PUGI_IMPL_FN xml_node::operator xml_node::unspecified_bool_type() const { return _root ? unspecified_bool_xml_node : 0; } PUGI_IMPL_FN bool xml_node::operator!() const { return !_root; } PUGI_IMPL_FN xml_node::iterator xml_node::begin() const { return iterator(_root ? _root->first_child + 0 : 0, _root); } PUGI_IMPL_FN xml_node::iterator xml_node::end() const { return iterator(0, _root); } PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_begin() const { return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root); } PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_end() const { return attribute_iterator(0, _root); } PUGI_IMPL_FN xml_object_range xml_node::children() const { return xml_object_range(begin(), end()); } PUGI_IMPL_FN xml_object_range xml_node::children(const char_t* name_) const { return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); } PUGI_IMPL_FN xml_object_range xml_node::attributes() const { return xml_object_range(attributes_begin(), attributes_end()); } PUGI_IMPL_FN bool xml_node::operator==(const xml_node& r) const { return (_root == r._root); } PUGI_IMPL_FN bool xml_node::operator!=(const xml_node& r) const { return (_root != r._root); } PUGI_IMPL_FN bool xml_node::operator<(const xml_node& r) const { return (_root < r._root); } PUGI_IMPL_FN bool xml_node::operator>(const xml_node& r) const { return (_root > r._root); } PUGI_IMPL_FN bool xml_node::operator<=(const xml_node& r) const { return (_root <= r._root); } PUGI_IMPL_FN bool xml_node::operator>=(const xml_node& r) const { return (_root >= r._root); } PUGI_IMPL_FN bool xml_node::empty() const { return !_root; } PUGI_IMPL_FN const char_t* xml_node::name() const { if (!_root) return PUGIXML_TEXT(""); const char_t* name = _root->name; return name ? name : PUGIXML_TEXT(""); } PUGI_IMPL_FN xml_node_type xml_node::type() const { return _root ? PUGI_IMPL_NODETYPE(_root) : node_null; } PUGI_IMPL_FN const char_t* xml_node::value() const { if (!_root) return PUGIXML_TEXT(""); const char_t* value = _root->value; return value ? value : PUGIXML_TEXT(""); } PUGI_IMPL_FN xml_node xml_node::child(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) { const char_t* iname = i->name; if (iname && impl::strequal(name_, iname)) return xml_node(i); } return xml_node(); } PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_) const { if (!_root) return xml_attribute(); for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) { const char_t* iname = i->name; if (iname && impl::strequal(name_, iname)) return xml_attribute(i); } return xml_attribute(); } PUGI_IMPL_FN xml_node xml_node::next_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) { const char_t* iname = i->name; if (iname && impl::strequal(name_, iname)) return xml_node(i); } return xml_node(); } PUGI_IMPL_FN xml_node xml_node::next_sibling() const { return _root ? xml_node(_root->next_sibling) : xml_node(); } PUGI_IMPL_FN xml_node xml_node::previous_sibling(const char_t* name_) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) { const char_t* iname = i->name; if (iname && impl::strequal(name_, iname)) return xml_node(i); } return xml_node(); } PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const { xml_attribute_struct* hint = hint_._attr; // if hint is not an attribute of node, behavior is not defined assert(!hint || (_root && impl::is_attribute_of(hint, _root))); if (!_root) return xml_attribute(); // optimistically search from hint up until the end for (xml_attribute_struct* i = hint; i; i = i->next_attribute) { const char_t* iname = i->name; if (iname && impl::strequal(name_, iname)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = i->next_attribute; return xml_attribute(i); } } // wrap around and search from the first attribute until the hint // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) { const char_t* jname = j->name; if (jname && impl::strequal(name_, jname)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = j->next_attribute; return xml_attribute(j); } } return xml_attribute(); } PUGI_IMPL_FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); xml_node_struct* prev = _root->prev_sibling_c; return prev->next_sibling ? xml_node(prev) : xml_node(); } PUGI_IMPL_FN xml_node xml_node::parent() const { return _root ? xml_node(_root->parent) : xml_node(); } PUGI_IMPL_FN xml_node xml_node::root() const { return _root ? xml_node(&impl::get_document(_root)) : xml_node(); } PUGI_IMPL_FN xml_text xml_node::text() const { return xml_text(_root); } PUGI_IMPL_FN const char_t* xml_node::child_value() const { if (!_root) return PUGIXML_TEXT(""); // element nodes can have value if parse_embed_pcdata was used if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value) return _root->value; for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) { const char_t* ivalue = i->value; if (impl::is_text_node(i) && ivalue) return ivalue; } return PUGIXML_TEXT(""); } PUGI_IMPL_FN const char_t* xml_node::child_value(const char_t* name_) const { return child(name_).child_value(); } PUGI_IMPL_FN xml_attribute xml_node::first_attribute() const { if (!_root) return xml_attribute(); return xml_attribute(_root->first_attribute); } PUGI_IMPL_FN xml_attribute xml_node::last_attribute() const { if (!_root) return xml_attribute(); xml_attribute_struct* first = _root->first_attribute; return first ? xml_attribute(first->prev_attribute_c) : xml_attribute(); } PUGI_IMPL_FN xml_node xml_node::first_child() const { if (!_root) return xml_node(); return xml_node(_root->first_child); } PUGI_IMPL_FN xml_node xml_node::last_child() const { if (!_root) return xml_node(); xml_node_struct* first = _root->first_child; return first ? xml_node(first->prev_sibling_c) : xml_node(); } PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs) { xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; if (type_ != node_element && type_ != node_pi && type_ != node_declaration) return false; return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); } PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs, size_t size) { xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; if (type_ != node_element && type_ != node_pi && type_ != node_declaration) return false; return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, size); } PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs) { xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) return false; return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); } PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs, size_t size) { xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) return false; return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, size); } PUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::append_attribute(a._attr, _root); a.set_name(name_); return a; } PUGI_IMPL_FN xml_attribute xml_node::prepend_attribute(const char_t* name_) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::prepend_attribute(a._attr, _root); a.set_name(name_); return a; } PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_after(a._attr, attr._attr, _root); a.set_name(name_); return a; } PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) { if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_before(a._attr, attr._attr, _root); a.set_name(name_); return a; } PUGI_IMPL_FN xml_attribute xml_node::append_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::append_attribute(a._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI_IMPL_FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::prepend_attribute(a._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI_IMPL_FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_after(a._attr, attr._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI_IMPL_FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) { if (!proto) return xml_attribute(); if (!impl::allow_insert_attribute(type())) return xml_attribute(); if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_attribute(); xml_attribute a(impl::allocate_attribute(alloc)); if (!a) return xml_attribute(); impl::insert_attribute_before(a._attr, attr._attr, _root); impl::node_copy_attribute(a._attr, proto._attr); return a; } PUGI_IMPL_FN xml_node xml_node::append_child(xml_node_type type_) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::append_node(n._root, _root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI_IMPL_FN xml_node xml_node::prepend_child(xml_node_type type_) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::prepend_node(n._root, _root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI_IMPL_FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_before(n._root, node._root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI_IMPL_FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) { if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_after(n._root, node._root); if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); return n; } PUGI_IMPL_FN xml_node xml_node::append_child(const char_t* name_) { xml_node result = append_child(node_element); result.set_name(name_); return result; } PUGI_IMPL_FN xml_node xml_node::prepend_child(const char_t* name_) { xml_node result = prepend_child(node_element); result.set_name(name_); return result; } PUGI_IMPL_FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) { xml_node result = insert_child_after(node_element, node); result.set_name(name_); return result; } PUGI_IMPL_FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) { xml_node result = insert_child_before(node_element, node); result.set_name(name_); return result; } PUGI_IMPL_FN xml_node xml_node::append_copy(const xml_node& proto) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::append_node(n._root, _root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI_IMPL_FN xml_node xml_node::prepend_copy(const xml_node& proto) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::prepend_node(n._root, _root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI_IMPL_FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_after(n._root, node._root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI_IMPL_FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) { xml_node_type type_ = proto.type(); if (!impl::allow_insert_child(type(), type_)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); xml_node n(impl::allocate_node(alloc, type_)); if (!n) return xml_node(); impl::insert_node_before(n._root, node._root); impl::node_copy_tree(n._root, proto._root); return n; } PUGI_IMPL_FN xml_node xml_node::append_move(const xml_node& moved) { if (!impl::allow_move(*this, moved)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::append_node(moved._root, _root); return moved; } PUGI_IMPL_FN xml_node xml_node::prepend_move(const xml_node& moved) { if (!impl::allow_move(*this, moved)) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::prepend_node(moved._root, _root); return moved; } PUGI_IMPL_FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) { if (!impl::allow_move(*this, moved)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); if (moved._root == node._root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::insert_node_after(moved._root, node._root); return moved; } PUGI_IMPL_FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) { if (!impl::allow_move(*this, moved)) return xml_node(); if (!node._root || node._root->parent != _root) return xml_node(); if (moved._root == node._root) return xml_node(); impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return xml_node(); // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; impl::remove_node(moved._root); impl::insert_node_before(moved._root, node._root); return moved; } PUGI_IMPL_FN bool xml_node::remove_attribute(const char_t* name_) { return remove_attribute(attribute(name_)); } PUGI_IMPL_FN bool xml_node::remove_attribute(const xml_attribute& a) { if (!_root || !a._attr) return false; if (!impl::is_attribute_of(a._attr, _root)) return false; impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return false; impl::remove_attribute(a._attr, _root); impl::destroy_attribute(a._attr, alloc); return true; } PUGI_IMPL_FN bool xml_node::remove_attributes() { if (!_root) return false; impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return false; for (xml_attribute_struct* attr = _root->first_attribute; attr; ) { xml_attribute_struct* next = attr->next_attribute; impl::destroy_attribute(attr, alloc); attr = next; } _root->first_attribute = 0; return true; } PUGI_IMPL_FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); } PUGI_IMPL_FN bool xml_node::remove_child(const xml_node& n) { if (!_root || !n._root || n._root->parent != _root) return false; impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return false; impl::remove_node(n._root); impl::destroy_node(n._root, alloc); return true; } PUGI_IMPL_FN bool xml_node::remove_children() { if (!_root) return false; impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return false; for (xml_node_struct* cur = _root->first_child; cur; ) { xml_node_struct* next = cur->next_sibling; impl::destroy_node(cur, alloc); cur = next; } _root->first_child = 0; return true; } PUGI_IMPL_FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { // append_buffer is only valid for elements/documents if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); // append buffer can not merge PCDATA into existing PCDATA nodes if ((options & parse_merge_pcdata) != 0 && last_child().type() == node_pcdata) return impl::make_parse_result(status_append_invalid_root); // get document node impl::xml_document_struct* doc = &impl::get_document(_root); // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense doc->header |= impl::xml_memory_page_contents_shared_mask; // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) impl::xml_memory_page* page = 0; impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page)); (void)page; if (!extra) return impl::make_parse_result(status_out_of_memory); #ifdef PUGIXML_COMPACT // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account extra = reinterpret_cast((reinterpret_cast(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1)); #endif // add extra buffer to the list extra->buffer = 0; extra->next = doc->extra_buffers; doc->extra_buffers = extra; // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level impl::name_null_sentry sentry(_root); return impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); } PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) { const char_t* iname = i->name; if (iname && impl::strequal(name_, iname)) { for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) { const char_t* aname = a->name; if (aname && impl::strequal(attr_name, aname)) { const char_t* avalue = a->value; if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) return xml_node(i); } } } } return xml_node(); } PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const { if (!_root) return xml_node(); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) { const char_t* aname = a->name; if (aname && impl::strequal(attr_name, aname)) { const char_t* avalue = a->value; if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) return xml_node(i); } } return xml_node(); } #ifndef PUGIXML_NO_STL PUGI_IMPL_FN string_t xml_node::path(char_t delimiter) const { if (!_root) return string_t(); size_t offset = 0; for (xml_node_struct* i = _root; i; i = i->parent) { const char_t* iname = i->name; offset += (i != _root); offset += iname ? impl::strlength(iname) : 0; } string_t result; result.resize(offset); for (xml_node_struct* j = _root; j; j = j->parent) { if (j != _root) result[--offset] = delimiter; const char_t* jname = j->name; if (jname) { size_t length = impl::strlength(jname); offset -= length; memcpy(&result[offset], jname, length * sizeof(char_t)); } } assert(offset == 0); return result; } #endif PUGI_IMPL_FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const { xml_node context = path_[0] == delimiter ? root() : *this; if (!context._root) return xml_node(); const char_t* path_segment = path_; while (*path_segment == delimiter) ++path_segment; const char_t* path_segment_end = path_segment; while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; if (path_segment == path_segment_end) return context; const char_t* next_segment = path_segment_end; while (*next_segment == delimiter) ++next_segment; if (*path_segment == '.' && path_segment + 1 == path_segment_end) return context.first_element_by_path(next_segment, delimiter); else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) return context.parent().first_element_by_path(next_segment, delimiter); else { for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling) { const char_t* jname = j->name; if (jname && impl::strequalrange(jname, path_segment, static_cast(path_segment_end - path_segment))) { xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); if (subsearch) return subsearch; } } return xml_node(); } } PUGI_IMPL_FN bool xml_node::traverse(xml_tree_walker& walker) { walker._depth = -1; xml_node arg_begin(_root); if (!walker.begin(arg_begin)) return false; xml_node_struct* cur = _root ? _root->first_child + 0 : 0; if (cur) { ++walker._depth; do { xml_node arg_for_each(cur); if (!walker.for_each(arg_for_each)) return false; if (cur->first_child) { ++walker._depth; cur = cur->first_child; } else if (cur->next_sibling) cur = cur->next_sibling; else { while (!cur->next_sibling && cur != _root && cur->parent) { --walker._depth; cur = cur->parent; } if (cur != _root) cur = cur->next_sibling; } } while (cur && cur != _root); } assert(walker._depth == -1); xml_node arg_end(_root); return walker.end(arg_end); } PUGI_IMPL_FN size_t xml_node::hash_value() const { return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); } PUGI_IMPL_FN xml_node_struct* xml_node::internal_object() const { return _root; } PUGI_IMPL_FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { if (!_root) return; impl::xml_buffered_writer buffered_writer(writer, encoding); impl::node_output(buffered_writer, _root, indent, flags, depth); buffered_writer.flush(); } #ifndef PUGIXML_NO_STL PUGI_IMPL_FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding, depth); } PUGI_IMPL_FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const { xml_writer_stream writer(stream); print(writer, indent, flags, encoding_wchar, depth); } #endif PUGI_IMPL_FN ptrdiff_t xml_node::offset_debug() const { if (!_root) return -1; impl::xml_document_struct& doc = impl::get_document(_root); // we can determine the offset reliably only if there is exactly once parse buffer if (!doc.buffer || doc.extra_buffers) return -1; switch (type()) { case node_document: return 0; case node_element: case node_declaration: case node_pi: return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; case node_pcdata: case node_cdata: case node_comment: case node_doctype: return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; default: assert(false && "Invalid node type"); // unreachable return -1; } } #ifdef __BORLANDC__ PUGI_IMPL_FN bool operator&&(const xml_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI_IMPL_FN bool operator||(const xml_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI_IMPL_FN xml_text::xml_text(xml_node_struct* root): _root(root) { } PUGI_IMPL_FN xml_node_struct* xml_text::_data() const { if (!_root || impl::is_text_node(_root)) return _root; // element nodes can have value if parse_embed_pcdata was used if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value) return _root; for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) if (impl::is_text_node(node)) return node; return 0; } PUGI_IMPL_FN xml_node_struct* xml_text::_data_new() { xml_node_struct* d = _data(); if (d) return d; return xml_node(_root).append_child(node_pcdata).internal_object(); } PUGI_IMPL_FN xml_text::xml_text(): _root(0) { } PUGI_IMPL_FN static void unspecified_bool_xml_text(xml_text***) { } PUGI_IMPL_FN xml_text::operator xml_text::unspecified_bool_type() const { return _data() ? unspecified_bool_xml_text : 0; } PUGI_IMPL_FN bool xml_text::operator!() const { return !_data(); } PUGI_IMPL_FN bool xml_text::empty() const { return _data() == 0; } PUGI_IMPL_FN const char_t* xml_text::get() const { xml_node_struct* d = _data(); if (!d) return PUGIXML_TEXT(""); const char_t* value = d->value; return value ? value : PUGIXML_TEXT(""); } PUGI_IMPL_FN const char_t* xml_text::as_string(const char_t* def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? value : def; } PUGI_IMPL_FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? impl::get_value_int(value) : def; } PUGI_IMPL_FN unsigned int xml_text::as_uint(unsigned int def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? impl::get_value_uint(value) : def; } PUGI_IMPL_FN double xml_text::as_double(double def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? impl::get_value_double(value) : def; } PUGI_IMPL_FN float xml_text::as_float(float def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? impl::get_value_float(value) : def; } PUGI_IMPL_FN bool xml_text::as_bool(bool def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? impl::get_value_bool(value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI_IMPL_FN long long xml_text::as_llong(long long def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? impl::get_value_llong(value) : def; } PUGI_IMPL_FN unsigned long long xml_text::as_ullong(unsigned long long def) const { xml_node_struct* d = _data(); if (!d) return def; const char_t* value = d->value; return value ? impl::get_value_ullong(value) : def; } #endif PUGI_IMPL_FN bool xml_text::set(const char_t* rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; } PUGI_IMPL_FN bool xml_text::set(const char_t* rhs, size_t size) { xml_node_struct* dn = _data_new(); return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false; } PUGI_IMPL_FN bool xml_text::set(int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } PUGI_IMPL_FN bool xml_text::set(unsigned int rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; } PUGI_IMPL_FN bool xml_text::set(long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } PUGI_IMPL_FN bool xml_text::set(unsigned long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; } PUGI_IMPL_FN bool xml_text::set(float rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision) : false; } PUGI_IMPL_FN bool xml_text::set(float rhs, int precision) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; } PUGI_IMPL_FN bool xml_text::set(double rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision) : false; } PUGI_IMPL_FN bool xml_text::set(double rhs, int precision) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; } PUGI_IMPL_FN bool xml_text::set(bool rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; } #ifdef PUGIXML_HAS_LONG_LONG PUGI_IMPL_FN bool xml_text::set(long long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; } PUGI_IMPL_FN bool xml_text::set(unsigned long long rhs) { xml_node_struct* dn = _data_new(); return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; } #endif PUGI_IMPL_FN xml_text& xml_text::operator=(const char_t* rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(int rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned int rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(long rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(double rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(float rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(bool rhs) { set(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI_IMPL_FN xml_text& xml_text::operator=(long long rhs) { set(rhs); return *this; } PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long long rhs) { set(rhs); return *this; } #endif PUGI_IMPL_FN xml_node xml_text::data() const { return xml_node(_data()); } #ifdef __BORLANDC__ PUGI_IMPL_FN bool operator&&(const xml_text& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI_IMPL_FN bool operator||(const xml_text& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI_IMPL_FN xml_node_iterator::xml_node_iterator() { } PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) { } PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI_IMPL_FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI_IMPL_FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI_IMPL_FN xml_node& xml_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI_IMPL_FN xml_node* xml_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC5 workaround } PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator++() { assert(_wrap._root); _wrap._root = _wrap._root->next_sibling; return *this; } PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator++(int) { xml_node_iterator temp = *this; ++*this; return temp; } PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator--() { _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); return *this; } PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator--(int) { xml_node_iterator temp = *this; --*this; return temp; } PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator() { } PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) { } PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI_IMPL_FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const { return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; } PUGI_IMPL_FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const { return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; } PUGI_IMPL_FN xml_attribute& xml_attribute_iterator::operator*() const { assert(_wrap._attr); return _wrap; } PUGI_IMPL_FN xml_attribute* xml_attribute_iterator::operator->() const { assert(_wrap._attr); return const_cast(&_wrap); // BCC5 workaround } PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator++() { assert(_wrap._attr); _wrap._attr = _wrap._attr->next_attribute; return *this; } PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator++(int) { xml_attribute_iterator temp = *this; ++*this; return temp; } PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator--() { _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); return *this; } PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator--(int) { xml_attribute_iterator temp = *this; --*this; return temp; } PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) { } PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) { } PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) { } PUGI_IMPL_FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI_IMPL_FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI_IMPL_FN xml_node& xml_named_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI_IMPL_FN xml_node* xml_named_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC5 workaround } PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator++() { assert(_wrap._root); _wrap = _wrap.next_sibling(_name); return *this; } PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator++(int) { xml_named_node_iterator temp = *this; ++*this; return temp; } PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator--() { if (_wrap._root) _wrap = _wrap.previous_sibling(_name); else { _wrap = _parent.last_child(); if (!impl::strequal(_wrap.name(), _name)) _wrap = _wrap.previous_sibling(_name); } return *this; } PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator--(int) { xml_named_node_iterator temp = *this; --*this; return temp; } PUGI_IMPL_FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) { } PUGI_IMPL_FN xml_parse_result::operator bool() const { return status == status_ok; } PUGI_IMPL_FN const char* xml_parse_result::description() const { switch (status) { case status_ok: return "No error"; case status_file_not_found: return "File was not found"; case status_io_error: return "Error reading from file/stream"; case status_out_of_memory: return "Could not allocate memory"; case status_internal_error: return "Internal error occurred"; case status_unrecognized_tag: return "Could not determine tag type"; case status_bad_pi: return "Error parsing document declaration/processing instruction"; case status_bad_comment: return "Error parsing comment"; case status_bad_cdata: return "Error parsing CDATA section"; case status_bad_doctype: return "Error parsing document type declaration"; case status_bad_pcdata: return "Error parsing PCDATA section"; case status_bad_start_element: return "Error parsing start element tag"; case status_bad_attribute: return "Error parsing element attribute"; case status_bad_end_element: return "Error parsing end element tag"; case status_end_element_mismatch: return "Start-end tags mismatch"; case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; case status_no_document_element: return "No document element found"; default: return "Unknown error"; } } PUGI_IMPL_FN xml_document::xml_document(): _buffer(0) { _create(); } PUGI_IMPL_FN xml_document::~xml_document() { _destroy(); } #ifdef PUGIXML_HAS_MOVE PUGI_IMPL_FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0) { _create(); _move(rhs); } PUGI_IMPL_FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT { if (this == &rhs) return *this; _destroy(); _create(); _move(rhs); return *this; } #endif PUGI_IMPL_FN void xml_document::reset() { _destroy(); _create(); } PUGI_IMPL_FN void xml_document::reset(const xml_document& proto) { reset(); impl::node_copy_tree(_root, proto._root); } PUGI_IMPL_FN void xml_document::_create() { assert(!_root); #ifdef PUGIXML_COMPACT // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit const size_t page_offset = sizeof(void*); #else const size_t page_offset = 0; #endif // initialize sentinel page PUGI_IMPL_STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory)); // prepare page structure impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory); assert(page); page->busy_size = impl::xml_memory_page_size; // setup first page marker #ifdef PUGIXML_COMPACT // round-trip through void* to avoid 'cast increases required alignment of target type' warning page->compact_page_marker = reinterpret_cast(static_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page))); *page->compact_page_marker = sizeof(impl::xml_memory_page); #endif // allocate new root _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page); _root->prev_sibling_c = _root; // setup sentinel page page->allocator = static_cast(_root); // setup hash table pointer in allocator #ifdef PUGIXML_COMPACT page->allocator->_hash = &static_cast(_root)->hash; #endif // verify the document allocation assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); } PUGI_IMPL_FN void xml_document::_destroy() { assert(_root); // destroy static storage if (_buffer) { impl::xml_memory::deallocate(_buffer); _buffer = 0; } // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) { if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); } // destroy dynamic storage, leave sentinel page (it's in static memory) impl::xml_memory_page* root_page = PUGI_IMPL_GETPAGE(_root); assert(root_page && !root_page->prev); assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); for (impl::xml_memory_page* page = root_page->next; page; ) { impl::xml_memory_page* next = page->next; impl::xml_allocator::deallocate_page(page); page = next; } #ifdef PUGIXML_COMPACT // destroy hash table static_cast(_root)->hash.clear(); #endif _root = 0; } #ifdef PUGIXML_HAS_MOVE PUGI_IMPL_FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT { impl::xml_document_struct* doc = static_cast(_root); impl::xml_document_struct* other = static_cast(rhs._root); // save first child pointer for later; this needs hash access xml_node_struct* other_first_child = other->first_child; #ifdef PUGIXML_COMPACT // reserve space for the hash table up front; this is the only operation that can fail // if it does, we have no choice but to throw (if we have exceptions) if (other_first_child) { size_t other_children = 0; for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) other_children++; // in compact mode, each pointer assignment could result in a hash table request // during move, we have to relocate document first_child and parents of all children // normally there's just one child and its parent has a pointerless encoding but // we assume the worst here if (!other->_hash->reserve(other_children + 1)) { #ifdef PUGIXML_NO_EXCEPTIONS return; #else throw std::bad_alloc(); #endif } } #endif // move allocation state // note that other->_root may point to the embedded document page, in which case we should keep original (empty) state if (other->_root != PUGI_IMPL_GETPAGE(other)) { doc->_root = other->_root; doc->_busy_size = other->_busy_size; } // move buffer state doc->buffer = other->buffer; doc->extra_buffers = other->extra_buffers; _buffer = rhs._buffer; #ifdef PUGIXML_COMPACT // move compact hash; note that the hash table can have pointers to other but they will be "inactive", similarly to nodes removed with remove_child doc->hash = other->hash; doc->_hash = &doc->hash; // make sure we don't access other hash up until the end when we reinitialize other document other->_hash = 0; #endif // move page structure impl::xml_memory_page* doc_page = PUGI_IMPL_GETPAGE(doc); assert(doc_page && !doc_page->prev && !doc_page->next); impl::xml_memory_page* other_page = PUGI_IMPL_GETPAGE(other); assert(other_page && !other_page->prev); // relink pages since root page is embedded into xml_document if (impl::xml_memory_page* page = other_page->next) { assert(page->prev == other_page); page->prev = doc_page; doc_page->next = page; other_page->next = 0; } // make sure pages point to the correct document state for (impl::xml_memory_page* page = doc_page->next; page; page = page->next) { assert(page->allocator == other); page->allocator = doc; #ifdef PUGIXML_COMPACT // this automatically migrates most children between documents and prevents ->parent assignment from allocating if (page->compact_shared_parent == other) page->compact_shared_parent = doc; #endif } // move tree structure assert(!doc->first_child); doc->first_child = other_first_child; for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) { #ifdef PUGIXML_COMPACT // most children will have migrated when we reassigned compact_shared_parent assert(node->parent == other || node->parent == doc); node->parent = doc; #else assert(node->parent == other); node->parent = doc; #endif } // reset other document new (other) impl::xml_document_struct(PUGI_IMPL_GETPAGE(other)); rhs._buffer = 0; } #endif #ifndef PUGIXML_NO_STL PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) { reset(); return impl::load_stream_impl(static_cast(_root), stream, options, encoding, &_buffer); } PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) { reset(); return impl::load_stream_impl(static_cast(_root), stream, options, encoding_wchar, &_buffer); } #endif PUGI_IMPL_FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) { // Force native encoding (skip autodetection) #ifdef PUGIXML_WCHAR_MODE xml_encoding encoding = encoding_wchar; #else xml_encoding encoding = encoding_utf8; #endif return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); } PUGI_IMPL_FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) { return load_string(contents, options); } PUGI_IMPL_FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) { reset(); using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file(path_, "rb"), impl::close_file); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } PUGI_IMPL_FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) { reset(); using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file_wide(path_, L"rb"), impl::close_file); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } PUGI_IMPL_FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); } PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); } PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) { reset(); return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); } PUGI_IMPL_FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const { impl::xml_buffered_writer buffered_writer(writer, encoding); if ((flags & format_write_bom) && encoding != encoding_latin1) { // BOM always represents the codepoint U+FEFF, so just write it in native encoding #ifdef PUGIXML_WCHAR_MODE unsigned int bom = 0xfeff; buffered_writer.write(static_cast(bom)); #else buffered_writer.write('\xef', '\xbb', '\xbf'); #endif } if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) { buffered_writer.write_string(PUGIXML_TEXT("'); if (!(flags & format_raw)) buffered_writer.write('\n'); } impl::node_output(buffered_writer, _root, indent, flags, 0); buffered_writer.flush(); } #ifndef PUGIXML_NO_STL PUGI_IMPL_FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding); } PUGI_IMPL_FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const { xml_writer_stream writer(stream); save(writer, indent, flags, encoding_wchar); } #endif PUGI_IMPL_FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; } PUGI_IMPL_FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; } PUGI_IMPL_FN xml_node xml_document::document_element() const { assert(_root); for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (PUGI_IMPL_NODETYPE(i) == node_element) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) { assert(str); return impl::as_utf8_impl(str, impl::strlength_wide(str)); } PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) { return impl::as_utf8_impl(str.c_str(), str.size()); } PUGI_IMPL_FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) { assert(str); return impl::as_wide_impl(str, strlen(str)); } PUGI_IMPL_FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) { return impl::as_wide_impl(str.c_str(), str.size()); } #endif PUGI_IMPL_FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) { impl::xml_memory::allocate = allocate; impl::xml_memory::deallocate = deallocate; } PUGI_IMPL_FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() { return impl::xml_memory::allocate; } PUGI_IMPL_FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() { return impl::xml_memory::deallocate; } } #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) { return std::bidirectional_iterator_tag(); } } #endif #ifndef PUGIXML_NO_XPATH // STL replacements PUGI_IMPL_NS_BEGIN struct equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } }; struct not_equal_to { template bool operator()(const T& lhs, const T& rhs) const { return lhs != rhs; } }; struct less { template bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } }; struct less_equal { template bool operator()(const T& lhs, const T& rhs) const { return lhs <= rhs; } }; template inline void swap(T& lhs, T& rhs) { T temp = lhs; lhs = rhs; rhs = temp; } template PUGI_IMPL_FN I min_element(I begin, I end, const Pred& pred) { I result = begin; for (I it = begin + 1; it != end; ++it) if (pred(*it, *result)) result = it; return result; } template PUGI_IMPL_FN void reverse(I begin, I end) { while (end - begin > 1) swap(*begin++, *--end); } template PUGI_IMPL_FN I unique(I begin, I end) { // fast skip head while (end - begin > 1 && *begin != *(begin + 1)) begin++; if (begin == end) return begin; // last written element I write = begin++; // merge unique elements while (begin != end) { if (*begin != *write) *++write = *begin++; else begin++; } // past-the-end (write points to live element) return write + 1; } template PUGI_IMPL_FN void insertion_sort(T* begin, T* end, const Pred& pred) { if (begin == end) return; for (T* it = begin + 1; it != end; ++it) { T val = *it; T* hole = it; // move hole backwards while (hole > begin && pred(val, *(hole - 1))) { *hole = *(hole - 1); hole--; } // fill hole with element *hole = val; } } template inline I median3(I first, I middle, I last, const Pred& pred) { if (pred(*middle, *first)) swap(middle, first); if (pred(*last, *middle)) swap(last, middle); if (pred(*middle, *first)) swap(middle, first); return middle; } template PUGI_IMPL_FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) { // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups) T* eq = begin; T* lt = begin; T* gt = end; while (lt < gt) { if (pred(*lt, pivot)) lt++; else if (*lt == pivot) swap(*eq++, *lt++); else swap(*lt, *--gt); } // we now have just 4 groups: = < >; move equal elements to the middle T* eqbeg = gt; for (T* it = begin; it != eq; ++it) swap(*it, *--eqbeg); *out_eqbeg = eqbeg; *out_eqend = gt; } template PUGI_IMPL_FN void sort(I begin, I end, const Pred& pred) { // sort large chunks while (end - begin > 16) { // find median element I middle = begin + (end - begin) / 2; I median = median3(begin, middle, end - 1, pred); // partition in three chunks (< = >) I eqbeg, eqend; partition3(begin, end, *median, pred, &eqbeg, &eqend); // loop on larger half if (eqbeg - begin > end - eqend) { sort(eqend, end, pred); end = eqbeg; } else { sort(begin, eqbeg, pred); begin = eqend; } } // insertion sort small chunk insertion_sort(begin, end, pred); } PUGI_IMPL_FN bool hash_insert(const void** table, size_t size, const void* key) { assert(key); unsigned int h = static_cast(reinterpret_cast(key)); // MurmurHash3 32-bit finalizer h ^= h >> 16; h *= 0x85ebca6bu; h ^= h >> 13; h *= 0xc2b2ae35u; h ^= h >> 16; size_t hashmod = size - 1; size_t bucket = h & hashmod; for (size_t probe = 0; probe <= hashmod; ++probe) { if (table[bucket] == 0) { table[bucket] = key; return true; } if (table[bucket] == key) return false; // hash collision, quadratic probing bucket = (bucket + probe + 1) & hashmod; } assert(false && "Hash table is full"); // unreachable return false; } PUGI_IMPL_NS_END // Allocator used for AST and evaluation stacks PUGI_IMPL_NS_BEGIN static const size_t xpath_memory_page_size = #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE PUGIXML_MEMORY_XPATH_PAGE_SIZE #else 4096 #endif ; static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); struct xpath_memory_block { xpath_memory_block* next; size_t capacity; union { char data[xpath_memory_page_size]; double alignment; }; }; struct xpath_allocator { xpath_memory_block* _root; size_t _root_size; bool* _error; xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error) { } void* allocate(size_t size) { // round size up to block alignment boundary size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); if (_root_size + size <= _root->capacity) { void* buf = &_root->data[0] + _root_size; _root_size += size; return buf; } else { // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests size_t block_capacity_base = sizeof(_root->data); size_t block_capacity_req = size + block_capacity_base / 4; size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; size_t block_size = block_capacity + offsetof(xpath_memory_block, data); xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); if (!block) { if (_error) *_error = true; return 0; } block->next = _root; block->capacity = block_capacity; _root = block; _root_size = size; return block->data; } } void* reallocate(void* ptr, size_t old_size, size_t new_size) { // round size up to block alignment boundary old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); // we can only reallocate the last object assert(ptr == 0 || static_cast(ptr) + old_size == &_root->data[0] + _root_size); // try to reallocate the object inplace if (ptr && _root_size - old_size + new_size <= _root->capacity) { _root_size = _root_size - old_size + new_size; return ptr; } // allocate a new block void* result = allocate(new_size); if (!result) return 0; // we have a new block if (ptr) { // copy old data (we only support growing) assert(new_size >= old_size); memcpy(result, ptr, old_size); // free the previous page if it had no other objects assert(_root->data == result); assert(_root->next); if (_root->next->data == ptr) { // deallocate the whole page, unless it was the first one xpath_memory_block* next = _root->next->next; if (next) { xml_memory::deallocate(_root->next); _root->next = next; } } } return result; } void revert(const xpath_allocator& state) { // free all new pages xpath_memory_block* cur = _root; while (cur != state._root) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } // restore state _root = state._root; _root_size = state._root_size; } void release() { xpath_memory_block* cur = _root; assert(cur); while (cur->next) { xpath_memory_block* next = cur->next; xml_memory::deallocate(cur); cur = next; } } }; struct xpath_allocator_capture { xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) { } ~xpath_allocator_capture() { _target->revert(_state); } xpath_allocator* _target; xpath_allocator _state; }; struct xpath_stack { xpath_allocator* result; xpath_allocator* temp; }; struct xpath_stack_data { xpath_memory_block blocks[2]; xpath_allocator result; xpath_allocator temp; xpath_stack stack; bool oom; xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false) { blocks[0].next = blocks[1].next = 0; blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); stack.result = &result; stack.temp = &temp; } ~xpath_stack_data() { result.release(); temp.release(); } }; PUGI_IMPL_NS_END // String class PUGI_IMPL_NS_BEGIN class xpath_string { const char_t* _buffer; bool _uses_heap; size_t _length_heap; static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) { char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); if (!result) return 0; memcpy(result, string, length * sizeof(char_t)); result[length] = 0; return result; } xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) { } public: static xpath_string from_const(const char_t* str) { return xpath_string(str, false, 0); } static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) { assert(begin <= end && *end == 0); return xpath_string(begin, true, static_cast(end - begin)); } static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) { assert(begin <= end); if (begin == end) return xpath_string(); size_t length = static_cast(end - begin); const char_t* data = duplicate_string(begin, length, alloc); return data ? xpath_string(data, true, length) : xpath_string(); } xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) { } void append(const xpath_string& o, xpath_allocator* alloc) { // skip empty sources if (!*o._buffer) return; // fast append for constant empty target and constant source if (!*_buffer && !_uses_heap && !o._uses_heap) { _buffer = o._buffer; } else { // need to make heap copy size_t target_length = length(); size_t source_length = o.length(); size_t result_length = target_length + source_length; // allocate new buffer char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); if (!result) return; // append first string to the new buffer in case there was no reallocation if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); // append second string to the new buffer memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); result[result_length] = 0; // finalize _buffer = result; _uses_heap = true; _length_heap = result_length; } } const char_t* c_str() const { return _buffer; } size_t length() const { return _uses_heap ? _length_heap : strlength(_buffer); } char_t* data(xpath_allocator* alloc) { // make private heap copy if (!_uses_heap) { size_t length_ = strlength(_buffer); const char_t* data_ = duplicate_string(_buffer, length_, alloc); if (!data_) return 0; _buffer = data_; _uses_heap = true; _length_heap = length_; } return const_cast(_buffer); } bool empty() const { return *_buffer == 0; } bool operator==(const xpath_string& o) const { return strequal(_buffer, o._buffer); } bool operator!=(const xpath_string& o) const { return !strequal(_buffer, o._buffer); } bool uses_heap() const { return _uses_heap; } }; PUGI_IMPL_NS_END PUGI_IMPL_NS_BEGIN PUGI_IMPL_FN bool starts_with(const char_t* string, const char_t* pattern) { while (*pattern && *string == *pattern) { string++; pattern++; } return *pattern == 0; } PUGI_IMPL_FN const char_t* find_char(const char_t* s, char_t c) { #ifdef PUGIXML_WCHAR_MODE return wcschr(s, c); #else return strchr(s, c); #endif } PUGI_IMPL_FN const char_t* find_substring(const char_t* s, const char_t* p) { #ifdef PUGIXML_WCHAR_MODE // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) return (*p == 0) ? s : wcsstr(s, p); #else return strstr(s, p); #endif } // Converts symbol to lower case, if it is an ASCII one PUGI_IMPL_FN char_t tolower_ascii(char_t ch) { return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; } PUGI_IMPL_FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) { if (na.attribute()) return xpath_string::from_const(na.attribute().value()); else { xml_node n = na.node(); switch (n.type()) { case node_pcdata: case node_cdata: case node_comment: case node_pi: return xpath_string::from_const(n.value()); case node_document: case node_element: { xpath_string result; // element nodes can have value if parse_embed_pcdata was used if (n.value()[0]) result.append(xpath_string::from_const(n.value()), alloc); xml_node cur = n.first_child(); while (cur && cur != n) { if (cur.type() == node_pcdata || cur.type() == node_cdata) result.append(xpath_string::from_const(cur.value()), alloc); if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur != n) cur = cur.parent(); if (cur != n) cur = cur.next_sibling(); } } return result; } default: return xpath_string(); } } } PUGI_IMPL_FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) { assert(ln->parent == rn->parent); // there is no common ancestor (the shared parent is null), nodes are from different documents if (!ln->parent) return ln < rn; // determine sibling order xml_node_struct* ls = ln; xml_node_struct* rs = rn; while (ls && rs) { if (ls == rn) return true; if (rs == ln) return false; ls = ls->next_sibling; rs = rs->next_sibling; } // if rn sibling chain ended ln must be before rn return !rs; } PUGI_IMPL_FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) { // find common ancestor at the same depth, if any xml_node_struct* lp = ln; xml_node_struct* rp = rn; while (lp && rp && lp->parent != rp->parent) { lp = lp->parent; rp = rp->parent; } // parents are the same! if (lp && rp) return node_is_before_sibling(lp, rp); // nodes are at different depths, need to normalize heights bool left_higher = !lp; while (lp) { lp = lp->parent; ln = ln->parent; } while (rp) { rp = rp->parent; rn = rn->parent; } // one node is the ancestor of the other if (ln == rn) return left_higher; // find common ancestor... again while (ln->parent != rn->parent) { ln = ln->parent; rn = rn->parent; } return node_is_before_sibling(ln, rn); } PUGI_IMPL_FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) { while (node && node != parent) node = node->parent; return parent && node == parent; } PUGI_IMPL_FN const void* document_buffer_order(const xpath_node& xnode) { xml_node_struct* node = xnode.node().internal_object(); if (node) { if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) { if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; } return 0; } xml_attribute_struct* attr = xnode.attribute().internal_object(); if (attr) { if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) { if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; } return 0; } return 0; } struct document_order_comparator { bool operator()(const xpath_node& lhs, const xpath_node& rhs) const { // optimized document order based check const void* lo = document_buffer_order(lhs); const void* ro = document_buffer_order(rhs); if (lo && ro) return lo < ro; // slow comparison xml_node ln = lhs.node(), rn = rhs.node(); // compare attributes if (lhs.attribute() && rhs.attribute()) { // shared parent if (lhs.parent() == rhs.parent()) { // determine sibling order for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) if (a == rhs.attribute()) return true; return false; } // compare attribute parents ln = lhs.parent(); rn = rhs.parent(); } else if (lhs.attribute()) { // attributes go after the parent element if (lhs.parent() == rhs.node()) return false; ln = lhs.parent(); } else if (rhs.attribute()) { // attributes go after the parent element if (rhs.parent() == lhs.node()) return true; rn = rhs.parent(); } if (ln == rn) return false; if (!ln || !rn) return ln < rn; return node_is_before(ln.internal_object(), rn.internal_object()); } }; PUGI_IMPL_FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) PUGI_IMPL_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t)); typedef uint32_t UI; // BCC5 workaround union { float f; UI i; } u; u.i = 0x7fc00000; return double(u.f); #else // fallback const volatile double zero = 0.0; return zero / zero; #endif } PUGI_IMPL_FN bool is_nan(double value) { #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) return !!_isnan(value); #elif defined(fpclassify) && defined(FP_NAN) return fpclassify(value) == FP_NAN; #else // fallback const volatile double v = value; return v != v; #endif } PUGI_IMPL_FN const char_t* convert_number_to_string_special(double value) { #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; if (_isnan(value)) return PUGIXML_TEXT("NaN"); return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) switch (fpclassify(value)) { case FP_NAN: return PUGIXML_TEXT("NaN"); case FP_INFINITE: return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); case FP_ZERO: return PUGIXML_TEXT("0"); default: return 0; } #else // fallback const volatile double v = value; if (v == 0) return PUGIXML_TEXT("0"); if (v != v) return PUGIXML_TEXT("NaN"); if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); return 0; #endif } PUGI_IMPL_FN bool convert_number_to_boolean(double value) { return (value != 0 && !is_nan(value)); } PUGI_IMPL_FN void truncate_zeros(char* begin, char* end) { while (begin != end && end[-1] == '0') end--; *end = 0; } // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent #if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) { // get base values int sign, exponent; _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign); // truncate redundant zeros truncate_zeros(buffer, buffer + strlen(buffer)); // fill results *out_mantissa = buffer; *out_exponent = exponent; } #else PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) { // get a scientific notation value with IEEE DBL_DIG decimals PUGI_IMPL_SNPRINTF(buffer, "%.*e", DBL_DIG, value); // get the exponent (possibly negative) char* exponent_string = strchr(buffer, 'e'); assert(exponent_string); int exponent = atoi(exponent_string + 1); // extract mantissa string: skip sign char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; assert(mantissa[0] != '0' && (mantissa[1] == '.' || mantissa[1] == ',')); // divide mantissa by 10 to eliminate integer part mantissa[1] = mantissa[0]; mantissa++; exponent++; // remove extra mantissa digits and zero-terminate mantissa truncate_zeros(mantissa, exponent_string); // fill results *out_mantissa = mantissa; *out_exponent = exponent; } #endif PUGI_IMPL_FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) { // try special number conversion const char_t* special = convert_number_to_string_special(value); if (special) return xpath_string::from_const(special); // get mantissa + exponent form char mantissa_buffer[32]; char* mantissa; int exponent; convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent); // allocate a buffer of suitable length for the number size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); if (!result) return xpath_string(); // make the number! char_t* s = result; // sign if (value < 0) *s++ = '-'; // integer part if (exponent <= 0) { *s++ = '0'; } else { while (exponent > 0) { assert(*mantissa == 0 || static_cast(*mantissa - '0') <= 9); *s++ = *mantissa ? *mantissa++ : '0'; exponent--; } } // fractional part if (*mantissa) { // decimal point *s++ = '.'; // extra zeroes from negative exponent while (exponent < 0) { *s++ = '0'; exponent++; } // extra mantissa digits while (*mantissa) { assert(static_cast(*mantissa - '0') <= 9); *s++ = *mantissa++; } } // zero-terminate assert(s < result + result_size); *s = 0; return xpath_string::from_heap_preallocated(result, s); } PUGI_IMPL_FN bool check_string_to_number_format(const char_t* string) { // parse leading whitespace while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string; // parse sign if (*string == '-') ++string; if (!*string) return false; // if there is no integer part, there should be a decimal part with at least one digit if (!PUGI_IMPL_IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI_IMPL_IS_CHARTYPEX(string[1], ctx_digit))) return false; // parse integer part while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string; // parse decimal part if (*string == '.') { ++string; while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string; } // parse trailing whitespace while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string; return *string == 0; } PUGI_IMPL_FN double convert_string_to_number(const char_t* string) { // check string format if (!check_string_to_number_format(string)) return gen_nan(); // parse string #ifdef PUGIXML_WCHAR_MODE return wcstod(string, 0); #else return strtod(string, 0); #endif } PUGI_IMPL_FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return false; } // copy string to zero-terminated buffer and perform conversion memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; *out_result = convert_string_to_number(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return true; } PUGI_IMPL_FN double round_nearest(double value) { return floor(value + 0.5); } PUGI_IMPL_FN double round_nearest_nzero(double value) { // same as round_nearest, but returns -0 for [-0.5, -0] // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); } PUGI_IMPL_FN const char_t* qualified_name(const xpath_node& node) { return node.attribute() ? node.attribute().name() : node.node().name(); } PUGI_IMPL_FN const char_t* local_name(const xpath_node& node) { const char_t* name = qualified_name(node); const char_t* p = find_char(name, ':'); return p ? p + 1 : name; } struct namespace_uri_predicate { const char_t* prefix; size_t prefix_length; namespace_uri_predicate(const char_t* name) { const char_t* pos = find_char(name, ':'); prefix = pos ? name : 0; prefix_length = pos ? static_cast(pos - name) : 0; } bool operator()(xml_attribute a) const { const char_t* name = a.name(); if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; } }; PUGI_IMPL_FN const char_t* namespace_uri(xml_node node) { namespace_uri_predicate pred = node.name(); xml_node p = node; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI_IMPL_FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) { namespace_uri_predicate pred = attr.name(); // Default namespace does not apply to attributes if (!pred.prefix) return PUGIXML_TEXT(""); xml_node p = parent; while (p) { xml_attribute a = p.find_attribute(pred); if (a) return a.value(); p = p.parent(); } return PUGIXML_TEXT(""); } PUGI_IMPL_FN const char_t* namespace_uri(const xpath_node& node) { return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); } PUGI_IMPL_FN char_t* normalize_space(char_t* buffer) { char_t* write = buffer; for (char_t* it = buffer; *it; ) { char_t ch = *it++; if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) { // replace whitespace sequence with single space while (PUGI_IMPL_IS_CHARTYPE(*it, ct_space)) it++; // avoid leading spaces if (write != buffer) *write++ = ' '; } else *write++ = ch; } // remove trailing space if (write != buffer && PUGI_IMPL_IS_CHARTYPE(write[-1], ct_space)) write--; // zero-terminate *write = 0; return write; } PUGI_IMPL_FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) { char_t* write = buffer; while (*buffer) { PUGI_IMPL_DMC_VOLATILE char_t ch = *buffer++; const char_t* pos = find_char(from, ch); if (!pos) *write++ = ch; // do not process else if (static_cast(pos - from) < to_length) *write++ = to[pos - from]; // replace } // zero-terminate *write = 0; return write; } PUGI_IMPL_FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) { unsigned char table[128] = {0}; while (*from) { unsigned int fc = static_cast(*from); unsigned int tc = static_cast(*to); if (fc >= 128 || tc >= 128) return 0; // code=128 means "skip character" if (!table[fc]) table[fc] = static_cast(tc ? tc : 128); from++; if (tc) to++; } for (int i = 0; i < 128; ++i) if (!table[i]) table[i] = static_cast(i); void* result = alloc->allocate(sizeof(table)); if (!result) return 0; memcpy(result, table, sizeof(table)); return static_cast(result); } PUGI_IMPL_FN char_t* translate_table(char_t* buffer, const unsigned char* table) { char_t* write = buffer; while (*buffer) { char_t ch = *buffer++; unsigned int index = static_cast(ch); if (index < 128) { unsigned char code = table[index]; // code=128 means "skip character" (table size is 128 so 128 can be a special value) // this code skips these characters without extra branches *write = static_cast(code); write += 1 - (code >> 7); } else { *write++ = ch; } } // zero-terminate *write = 0; return write; } inline bool is_xpath_attribute(const char_t* name) { return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); } struct xpath_variable_boolean: xpath_variable { xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) { } bool value; char_t name[1]; }; struct xpath_variable_number: xpath_variable { xpath_variable_number(): xpath_variable(xpath_type_number), value(0) { } double value; char_t name[1]; }; struct xpath_variable_string: xpath_variable { xpath_variable_string(): xpath_variable(xpath_type_string), value(0) { } ~xpath_variable_string() { if (value) xml_memory::deallocate(value); } char_t* value; char_t name[1]; }; struct xpath_variable_node_set: xpath_variable { xpath_variable_node_set(): xpath_variable(xpath_type_node_set) { } xpath_node_set value; char_t name[1]; }; static const xpath_node_set dummy_node_set; PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str) { // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) unsigned int result = 0; while (*str) { result += static_cast(*str++); result += result << 10; result ^= result >> 6; } result += result << 3; result ^= result >> 11; result += result << 15; return result; } template PUGI_IMPL_FN T* new_xpath_variable(const char_t* name) { size_t length = strlength(name); if (length == 0) return 0; // empty variable names are invalid // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); if (!memory) return 0; T* result = new (memory) T(); memcpy(result->name, name, (length + 1) * sizeof(char_t)); return result; } PUGI_IMPL_FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) { switch (type) { case xpath_type_node_set: return new_xpath_variable(name); case xpath_type_number: return new_xpath_variable(name); case xpath_type_string: return new_xpath_variable(name); case xpath_type_boolean: return new_xpath_variable(name); default: return 0; } } template PUGI_IMPL_FN void delete_xpath_variable(T* var) { var->~T(); xml_memory::deallocate(var); } PUGI_IMPL_FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) { switch (type) { case xpath_type_node_set: delete_xpath_variable(static_cast(var)); break; case xpath_type_number: delete_xpath_variable(static_cast(var)); break; case xpath_type_string: delete_xpath_variable(static_cast(var)); break; case xpath_type_boolean: delete_xpath_variable(static_cast(var)); break; default: assert(false && "Invalid variable type"); // unreachable } } PUGI_IMPL_FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) { switch (rhs->type()) { case xpath_type_node_set: return lhs->set(static_cast(rhs)->value); case xpath_type_number: return lhs->set(static_cast(rhs)->value); case xpath_type_string: return lhs->set(static_cast(rhs)->value); case xpath_type_boolean: return lhs->set(static_cast(rhs)->value); default: assert(false && "Invalid variable type"); // unreachable return false; } } PUGI_IMPL_FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) { size_t length = static_cast(end - begin); char_t* scratch = buffer; if (length >= sizeof(buffer) / sizeof(buffer[0])) { // need to make dummy on-heap copy scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); if (!scratch) return false; } // copy string to zero-terminated buffer and perform lookup memcpy(scratch, begin, length * sizeof(char_t)); scratch[length] = 0; *out_result = set->get(scratch); // free dummy buffer if (scratch != buffer) xml_memory::deallocate(scratch); return true; } PUGI_IMPL_NS_END // Internal node set class PUGI_IMPL_NS_BEGIN PUGI_IMPL_FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) { if (end - begin < 2) return xpath_node_set::type_sorted; document_order_comparator cmp; bool first = cmp(begin[0], begin[1]); for (const xpath_node* it = begin + 1; it + 1 < end; ++it) if (cmp(it[0], it[1]) != first) return xpath_node_set::type_unsorted; return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; } PUGI_IMPL_FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) { xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; if (type == xpath_node_set::type_unsorted) { xpath_node_set::type_t sorted = xpath_get_order(begin, end); if (sorted == xpath_node_set::type_unsorted) { sort(begin, end, document_order_comparator()); type = xpath_node_set::type_sorted; } else type = sorted; } if (type != order) reverse(begin, end); return order; } PUGI_IMPL_FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) { if (begin == end) return xpath_node(); switch (type) { case xpath_node_set::type_sorted: return *begin; case xpath_node_set::type_sorted_reverse: return *(end - 1); case xpath_node_set::type_unsorted: return *min_element(begin, end, document_order_comparator()); default: assert(false && "Invalid node set type"); // unreachable return xpath_node(); } } class xpath_node_set_raw { xpath_node_set::type_t _type; xpath_node* _begin; xpath_node* _end; xpath_node* _eos; public: xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) { } xpath_node* begin() const { return _begin; } xpath_node* end() const { return _end; } bool empty() const { return _begin == _end; } size_t size() const { return static_cast(_end - _begin); } xpath_node first() const { return xpath_first(_begin, _end, _type); } void push_back_grow(const xpath_node& node, xpath_allocator* alloc); void push_back(const xpath_node& node, xpath_allocator* alloc) { if (_end != _eos) *_end++ = node; else push_back_grow(node, alloc); } void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) { if (begin_ == end_) return; size_t size_ = static_cast(_end - _begin); size_t capacity = static_cast(_eos - _begin); size_t count = static_cast(end_ - begin_); if (size_ + count > capacity) { // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); if (!data) return; // finalize _begin = data; _end = data + size_; _eos = data + size_ + count; } memcpy(_end, begin_, count * sizeof(xpath_node)); _end += count; } void sort_do() { _type = xpath_sort(_begin, _end, _type, false); } void truncate(xpath_node* pos) { assert(_begin <= pos && pos <= _end); _end = pos; } void remove_duplicates(xpath_allocator* alloc) { if (_type == xpath_node_set::type_unsorted && _end - _begin > 2) { xpath_allocator_capture cr(alloc); size_t size_ = static_cast(_end - _begin); size_t hash_size = 1; while (hash_size < size_ + size_ / 2) hash_size *= 2; const void** hash_data = static_cast(alloc->allocate(hash_size * sizeof(void**))); if (!hash_data) return; memset(hash_data, 0, hash_size * sizeof(const void**)); xpath_node* write = _begin; for (xpath_node* it = _begin; it != _end; ++it) { const void* attr = it->attribute().internal_object(); const void* node = it->node().internal_object(); const void* key = attr ? attr : node; if (key && hash_insert(hash_data, hash_size, key)) { *write++ = *it; } } _end = write; } else { _end = unique(_begin, _end); } } xpath_node_set::type_t type() const { return _type; } void set_type(xpath_node_set::type_t value) { _type = value; } }; PUGI_IMPL_FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) { size_t capacity = static_cast(_eos - _begin); // get new capacity (1.5x rule) size_t new_capacity = capacity + capacity / 2 + 1; // reallocate the old array or allocate a new one xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); if (!data) return; // finalize _begin = data; _end = data + capacity; _eos = data + new_capacity; // push *_end++ = node; } PUGI_IMPL_NS_END PUGI_IMPL_NS_BEGIN struct xpath_context { xpath_node n; size_t position, size; xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) { } }; enum lexeme_t { lex_none = 0, lex_equal, lex_not_equal, lex_less, lex_greater, lex_less_or_equal, lex_greater_or_equal, lex_plus, lex_minus, lex_multiply, lex_union, lex_var_ref, lex_open_brace, lex_close_brace, lex_quoted_string, lex_number, lex_slash, lex_double_slash, lex_open_square_brace, lex_close_square_brace, lex_string, lex_comma, lex_axis_attribute, lex_dot, lex_double_dot, lex_double_colon, lex_eof }; struct xpath_lexer_string { const char_t* begin; const char_t* end; xpath_lexer_string(): begin(0), end(0) { } bool operator==(const char_t* other) const { size_t length = static_cast(end - begin); return strequalrange(other, begin, length); } }; class xpath_lexer { const char_t* _cur; const char_t* _cur_lexeme_pos; xpath_lexer_string _cur_lexeme_contents; lexeme_t _cur_lexeme; public: explicit xpath_lexer(const char_t* query): _cur(query) { next(); } const char_t* state() const { return _cur; } void next() { const char_t* cur = _cur; while (PUGI_IMPL_IS_CHARTYPE(*cur, ct_space)) ++cur; // save lexeme position for error reporting _cur_lexeme_pos = cur; switch (*cur) { case 0: _cur_lexeme = lex_eof; break; case '>': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_greater_or_equal; } else { cur += 1; _cur_lexeme = lex_greater; } break; case '<': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_less_or_equal; } else { cur += 1; _cur_lexeme = lex_less; } break; case '!': if (*(cur+1) == '=') { cur += 2; _cur_lexeme = lex_not_equal; } else { _cur_lexeme = lex_none; } break; case '=': cur += 1; _cur_lexeme = lex_equal; break; case '+': cur += 1; _cur_lexeme = lex_plus; break; case '-': cur += 1; _cur_lexeme = lex_minus; break; case '*': cur += 1; _cur_lexeme = lex_multiply; break; case '|': cur += 1; _cur_lexeme = lex_union; break; case '$': cur += 1; if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':' && PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // qname { cur++; // : while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_var_ref; } else { _cur_lexeme = lex_none; } break; case '(': cur += 1; _cur_lexeme = lex_open_brace; break; case ')': cur += 1; _cur_lexeme = lex_close_brace; break; case '[': cur += 1; _cur_lexeme = lex_open_square_brace; break; case ']': cur += 1; _cur_lexeme = lex_close_square_brace; break; case ',': cur += 1; _cur_lexeme = lex_comma; break; case '/': if (*(cur+1) == '/') { cur += 2; _cur_lexeme = lex_double_slash; } else { cur += 1; _cur_lexeme = lex_slash; } break; case '.': if (*(cur+1) == '.') { cur += 2; _cur_lexeme = lex_double_dot; } else if (PUGI_IMPL_IS_CHARTYPEX(*(cur+1), ctx_digit)) { _cur_lexeme_contents.begin = cur; // . ++cur; while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else { cur += 1; _cur_lexeme = lex_dot; } break; case '@': cur += 1; _cur_lexeme = lex_axis_attribute; break; case '"': case '\'': { char_t terminator = *cur; ++cur; _cur_lexeme_contents.begin = cur; while (*cur && *cur != terminator) cur++; _cur_lexeme_contents.end = cur; if (!*cur) _cur_lexeme = lex_none; else { cur += 1; _cur_lexeme = lex_quoted_string; } break; } case ':': if (*(cur+1) == ':') { cur += 2; _cur_lexeme = lex_double_colon; } else { _cur_lexeme = lex_none; } break; default: if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) { _cur_lexeme_contents.begin = cur; while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; if (*cur == '.') { cur++; while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':') { if (cur[1] == '*') // namespace test ncname:* { cur += 2; // :* } else if (PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname { cur++; // : while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; } } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_string; } else { _cur_lexeme = lex_none; } } _cur = cur; } lexeme_t current() const { return _cur_lexeme; } const char_t* current_pos() const { return _cur_lexeme_pos; } const xpath_lexer_string& contents() const { assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); return _cur_lexeme_contents; } }; enum ast_type_t { ast_unknown, ast_op_or, // left or right ast_op_and, // left and right ast_op_equal, // left = right ast_op_not_equal, // left != right ast_op_less, // left < right ast_op_greater, // left > right ast_op_less_or_equal, // left <= right ast_op_greater_or_equal, // left >= right ast_op_add, // left + right ast_op_subtract, // left - right ast_op_multiply, // left * right ast_op_divide, // left / right ast_op_mod, // left % right ast_op_negate, // left - right ast_op_union, // left | right ast_predicate, // apply predicate to set; next points to next predicate ast_filter, // select * from left where right ast_string_constant, // string constant ast_number_constant, // number constant ast_variable, // variable ast_func_last, // last() ast_func_position, // position() ast_func_count, // count(left) ast_func_id, // id(left) ast_func_local_name_0, // local-name() ast_func_local_name_1, // local-name(left) ast_func_namespace_uri_0, // namespace-uri() ast_func_namespace_uri_1, // namespace-uri(left) ast_func_name_0, // name() ast_func_name_1, // name(left) ast_func_string_0, // string() ast_func_string_1, // string(left) ast_func_concat, // concat(left, right, siblings) ast_func_starts_with, // starts_with(left, right) ast_func_contains, // contains(left, right) ast_func_substring_before, // substring-before(left, right) ast_func_substring_after, // substring-after(left, right) ast_func_substring_2, // substring(left, right) ast_func_substring_3, // substring(left, right, third) ast_func_string_length_0, // string-length() ast_func_string_length_1, // string-length(left) ast_func_normalize_space_0, // normalize-space() ast_func_normalize_space_1, // normalize-space(left) ast_func_translate, // translate(left, right, third) ast_func_boolean, // boolean(left) ast_func_not, // not(left) ast_func_true, // true() ast_func_false, // false() ast_func_lang, // lang(left) ast_func_number_0, // number() ast_func_number_1, // number(left) ast_func_sum, // sum(left) ast_func_floor, // floor(left) ast_func_ceiling, // ceiling(left) ast_func_round, // round(left) ast_step, // process set left with step ast_step_root, // select root node ast_opt_translate_table, // translate(left, right, third) where right/third are constants ast_opt_compare_attribute // @name = 'string' }; enum axis_t { axis_ancestor, axis_ancestor_or_self, axis_attribute, axis_child, axis_descendant, axis_descendant_or_self, axis_following, axis_following_sibling, axis_namespace, axis_parent, axis_preceding, axis_preceding_sibling, axis_self }; enum nodetest_t { nodetest_none, nodetest_name, nodetest_type_node, nodetest_type_comment, nodetest_type_pi, nodetest_type_text, nodetest_pi, nodetest_all, nodetest_all_in_namespace }; enum predicate_t { predicate_default, predicate_posinv, predicate_constant, predicate_constant_one }; enum nodeset_eval_t { nodeset_eval_all, nodeset_eval_any, nodeset_eval_first }; template struct axis_to_type { static const axis_t axis; }; template const axis_t axis_to_type::axis = N; class xpath_ast_node { private: // node type char _type; char _rettype; // for ast_step char _axis; // for ast_step/ast_predicate/ast_filter char _test; // tree node structure xpath_ast_node* _left; xpath_ast_node* _right; xpath_ast_node* _next; union { // value for ast_string_constant const char_t* string; // value for ast_number_constant double number; // variable for ast_variable xpath_variable* variable; // node test for ast_step (node name/namespace/node type/pi target) const char_t* nodetest; // table for ast_opt_translate_table const unsigned char* table; } _data; xpath_ast_node(const xpath_ast_node&); xpath_ast_node& operator=(const xpath_ast_node&); template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) { if (lt == xpath_type_boolean || rt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number || rt == xpath_type_number) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_string || rt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string ls = lhs->eval_string(c, stack); xpath_string rs = rhs->eval_string(c, stack); return comp(ls, rs); } } else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) return true; } return false; } else { if (lt == xpath_type_node_set) { swap(lhs, rhs); swap(lt, rt); } if (lt == xpath_type_boolean) return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); else if (lt == xpath_type_number) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_string) { xpath_allocator_capture cr(stack.result); xpath_string l = lhs->eval_string(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, string_value(*ri, stack.result))) return true; } return false; } } assert(false && "Wrong types"); // unreachable return false; } static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) { return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; } template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) { xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); if (lt != xpath_type_node_set && rt != xpath_type_node_set) return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); else if (lt == xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); double l = convert_string_to_number(string_value(*li, stack.result).c_str()); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture crii(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } } return false; } else if (lt != xpath_type_node_set && rt == xpath_type_node_set) { xpath_allocator_capture cr(stack.result); double l = lhs->eval_number(c, stack); xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) { xpath_allocator_capture cri(stack.result); if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) return true; } return false; } else if (lt == xpath_type_node_set && rt != xpath_type_node_set) { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); double r = rhs->eval_number(c, stack); for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) { xpath_allocator_capture cri(stack.result); if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) return true; } return false; } else { assert(false && "Wrong types"); // unreachable return false; } } static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) { assert(ns.size() >= first); assert(expr->rettype() != xpath_type_number); size_t i = 1; size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; // remove_if... or well, sort of for (xpath_node* it = last; it != ns.end(); ++it, ++i) { xpath_context c(*it, i, size); if (expr->eval_boolean(c, stack)) { *last++ = *it; if (once) break; } } ns.truncate(last); } static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) { assert(ns.size() >= first); assert(expr->rettype() == xpath_type_number); size_t i = 1; size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; // remove_if... or well, sort of for (xpath_node* it = last; it != ns.end(); ++it, ++i) { xpath_context c(*it, i, size); if (expr->eval_number(c, stack) == static_cast(i)) { *last++ = *it; if (once) break; } } ns.truncate(last); } static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) { assert(ns.size() >= first); assert(expr->rettype() == xpath_type_number); size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; xpath_node cn; xpath_context c(cn, 1, size); double er = expr->eval_number(c, stack); if (er >= 1.0 && er <= static_cast(size)) { size_t eri = static_cast(er); if (er == static_cast(eri)) { xpath_node r = last[eri - 1]; *last++ = r; } } ns.truncate(last); } void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) { if (ns.size() == first) return; assert(_type == ast_filter || _type == ast_predicate); if (_test == predicate_constant || _test == predicate_constant_one) apply_predicate_number_const(ns, first, _right, stack); else if (_right->rettype() == xpath_type_number) apply_predicate_number(ns, first, _right, stack, once); else apply_predicate_boolean(ns, first, _right, stack, once); } void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) { if (ns.size() == first) return; bool last_once = eval_once(ns.type(), eval); for (xpath_ast_node* pred = _right; pred; pred = pred->_next) pred->apply_predicate(ns, first, stack, !pred->_next && last_once); } bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) { assert(a); const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT(""); switch (_test) { case nodetest_name: if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) { ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); return true; } break; case nodetest_type_node: case nodetest_all: if (is_xpath_attribute(name)) { ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); return true; } break; case nodetest_all_in_namespace: if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) { ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); return true; } break; default: ; } return false; } bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) { assert(n); xml_node_type type = PUGI_IMPL_NODETYPE(n); switch (_test) { case nodetest_name: if (type == node_element && n->name && strequal(n->name, _data.nodetest)) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_type_node: ns.push_back(xml_node(n), alloc); return true; case nodetest_type_comment: if (type == node_comment) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_type_text: if (type == node_pcdata || type == node_cdata) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_type_pi: if (type == node_pi) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_pi: if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_all: if (type == node_element) { ns.push_back(xml_node(n), alloc); return true; } break; case nodetest_all_in_namespace: if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) { ns.push_back(xml_node(n), alloc); return true; } break; default: assert(false && "Unknown axis"); // unreachable } return false; } template void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) { const axis_t axis = T::axis; switch (axis) { case axis_attribute: { for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) if (step_push(ns, a, n, alloc) & once) return; break; } case axis_child: { for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; break; } case axis_descendant: case axis_descendant_or_self: { if (axis == axis_descendant_or_self) if (step_push(ns, n, alloc) & once) return; xml_node_struct* cur = n->first_child; while (cur) { if (step_push(ns, cur, alloc) & once) return; if (cur->first_child) cur = cur->first_child; else { while (!cur->next_sibling) { cur = cur->parent; if (cur == n) return; } cur = cur->next_sibling; } } break; } case axis_following_sibling: { for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) if (step_push(ns, c, alloc) & once) return; break; } case axis_preceding_sibling: { for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) if (step_push(ns, c, alloc) & once) return; break; } case axis_following: { xml_node_struct* cur = n; // exit from this node so that we don't include descendants while (!cur->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->next_sibling; while (cur) { if (step_push(ns, cur, alloc) & once) return; if (cur->first_child) cur = cur->first_child; else { while (!cur->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->next_sibling; } } break; } case axis_preceding: { xml_node_struct* cur = n; // exit from this node so that we don't include descendants while (!cur->prev_sibling_c->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->prev_sibling_c; while (cur) { if (cur->first_child) cur = cur->first_child->prev_sibling_c; else { // leaf node, can't be ancestor if (step_push(ns, cur, alloc) & once) return; while (!cur->prev_sibling_c->next_sibling) { cur = cur->parent; if (!cur) return; if (!node_is_ancestor(cur, n)) if (step_push(ns, cur, alloc) & once) return; } cur = cur->prev_sibling_c; } } break; } case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self) if (step_push(ns, n, alloc) & once) return; xml_node_struct* cur = n->parent; while (cur) { if (step_push(ns, cur, alloc) & once) return; cur = cur->parent; } break; } case axis_self: { step_push(ns, n, alloc); break; } case axis_parent: { if (n->parent) step_push(ns, n->parent, alloc); break; } default: assert(false && "Unimplemented axis"); // unreachable } } template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) { const axis_t axis = T::axis; switch (axis) { case axis_ancestor: case axis_ancestor_or_self: { if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test if (step_push(ns, a, p, alloc) & once) return; xml_node_struct* cur = p; while (cur) { if (step_push(ns, cur, alloc) & once) return; cur = cur->parent; } break; } case axis_descendant_or_self: case axis_self: { if (_test == nodetest_type_node) // reject attributes based on principal node type test step_push(ns, a, p, alloc); break; } case axis_following: { xml_node_struct* cur = p; while (cur) { if (cur->first_child) cur = cur->first_child; else { while (!cur->next_sibling) { cur = cur->parent; if (!cur) return; } cur = cur->next_sibling; } if (step_push(ns, cur, alloc) & once) return; } break; } case axis_parent: { step_push(ns, p, alloc); break; } case axis_preceding: { // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding step_fill(ns, p, alloc, once, v); break; } default: assert(false && "Unimplemented axis"); // unreachable } } template void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) { const axis_t axis = T::axis; const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); if (xn.node()) step_fill(ns, xn.node().internal_object(), alloc, once, v); else if (axis_has_attributes && xn.attribute() && xn.parent()) step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); } template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) { const axis_t axis = T::axis; const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; bool once = (axis == axis_attribute && _test == nodetest_name) || (!_right && eval_once(axis_type, eval)) || // coverity[mixed_enums] (_right && !_right->_next && _right->_test == predicate_constant_one); xpath_node_set_raw ns; ns.set_type(axis_type); if (_left) { xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); // self axis preserves the original order if (axis == axis_self) ns.set_type(s.type()); for (const xpath_node* it = s.begin(); it != s.end(); ++it) { size_t size = ns.size(); // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); step_fill(ns, *it, stack.result, once, v); if (_right) apply_predicates(ns, size, stack, eval); } } else { step_fill(ns, c.n, stack.result, once, v); if (_right) apply_predicates(ns, 0, stack, eval); } // child, attribute and self axes always generate unique set of nodes // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) ns.remove_duplicates(stack.temp); return ns; } public: xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_string_constant); _data.string = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_number_constant); _data.number = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) { assert(type == ast_variable); _data.variable = value; } xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) { } xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) { assert(type == ast_step); _data.nodetest = contents; } xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) { assert(type == ast_filter || type == ast_predicate); } void set_next(xpath_ast_node* value) { _next = value; } void set_right(xpath_ast_node* value) { _right = value; } bool eval_boolean(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_or: return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); case ast_op_and: return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); case ast_op_equal: return compare_eq(_left, _right, c, stack, equal_to()); case ast_op_not_equal: return compare_eq(_left, _right, c, stack, not_equal_to()); case ast_op_less: return compare_rel(_left, _right, c, stack, less()); case ast_op_greater: return compare_rel(_right, _left, c, stack, less()); case ast_op_less_or_equal: return compare_rel(_left, _right, c, stack, less_equal()); case ast_op_greater_or_equal: return compare_rel(_right, _left, c, stack, less_equal()); case ast_func_starts_with: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return starts_with(lr.c_str(), rr.c_str()); } case ast_func_contains: { xpath_allocator_capture cr(stack.result); xpath_string lr = _left->eval_string(c, stack); xpath_string rr = _right->eval_string(c, stack); return find_substring(lr.c_str(), rr.c_str()) != 0; } case ast_func_boolean: return _left->eval_boolean(c, stack); case ast_func_not: return !_left->eval_boolean(c, stack); case ast_func_true: return true; case ast_func_false: return false; case ast_func_lang: { if (c.n.attribute()) return false; xpath_allocator_capture cr(stack.result); xpath_string lang = _left->eval_string(c, stack); for (xml_node n = c.n.node(); n; n = n.parent()) { xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); if (a) { const char_t* value = a.value(); // strnicmp / strncasecmp is not portable for (const char_t* lit = lang.c_str(); *lit; ++lit) { if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; ++value; } return *value == 0 || *value == '-'; } } return false; } case ast_opt_compare_attribute: { const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_boolean) return _data.variable->get_boolean(); // variable needs to be converted to the correct type, this is handled by the fallthrough block below break; } default: ; } // none of the ast types that return the value directly matched, we need to perform type conversion switch (_rettype) { case xpath_type_number: return convert_number_to_boolean(eval_number(c, stack)); case xpath_type_string: { xpath_allocator_capture cr(stack.result); return !eval_string(c, stack).empty(); } case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); return !eval_node_set(c, stack, nodeset_eval_any).empty(); } default: assert(false && "Wrong expression for return type boolean"); // unreachable return false; } } double eval_number(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_op_add: return _left->eval_number(c, stack) + _right->eval_number(c, stack); case ast_op_subtract: return _left->eval_number(c, stack) - _right->eval_number(c, stack); case ast_op_multiply: return _left->eval_number(c, stack) * _right->eval_number(c, stack); case ast_op_divide: return _left->eval_number(c, stack) / _right->eval_number(c, stack); case ast_op_mod: return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); case ast_op_negate: return -_left->eval_number(c, stack); case ast_number_constant: return _data.number; case ast_func_last: return static_cast(c.size); case ast_func_position: return static_cast(c.position); case ast_func_count: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); } case ast_func_string_length_0: { xpath_allocator_capture cr(stack.result); return static_cast(string_value(c.n, stack.result).length()); } case ast_func_string_length_1: { xpath_allocator_capture cr(stack.result); return static_cast(_left->eval_string(c, stack).length()); } case ast_func_number_0: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(string_value(c.n, stack.result).c_str()); } case ast_func_number_1: return _left->eval_number(c, stack); case ast_func_sum: { xpath_allocator_capture cr(stack.result); double r = 0; xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) { xpath_allocator_capture cri(stack.result); r += convert_string_to_number(string_value(*it, stack.result).c_str()); } return r; } case ast_func_floor: { double r = _left->eval_number(c, stack); return r == r ? floor(r) : r; } case ast_func_ceiling: { double r = _left->eval_number(c, stack); return r == r ? ceil(r) : r; } case ast_func_round: return round_nearest_nzero(_left->eval_number(c, stack)); case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_number) return _data.variable->get_number(); // variable needs to be converted to the correct type, this is handled by the fallthrough block below break; } default: ; } // none of the ast types that return the value directly matched, we need to perform type conversion switch (_rettype) { case xpath_type_boolean: return eval_boolean(c, stack) ? 1 : 0; case xpath_type_string: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } case xpath_type_node_set: { xpath_allocator_capture cr(stack.result); return convert_string_to_number(eval_string(c, stack).c_str()); } default: assert(false && "Wrong expression for return type number"); // unreachable return 0; } } xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) { assert(_type == ast_func_concat); xpath_allocator_capture ct(stack.temp); // count the string number size_t count = 1; for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; // allocate a buffer for temporary string objects xpath_string* buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); if (!buffer) return xpath_string(); // evaluate all strings to temporary stack xpath_stack swapped_stack = {stack.temp, stack.result}; buffer[0] = _left->eval_string(c, swapped_stack); size_t pos = 1; for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); assert(pos == count); // get total length size_t length = 0; for (size_t i = 0; i < count; ++i) length += buffer[i].length(); // create final string char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); if (!result) return xpath_string(); char_t* ri = result; for (size_t j = 0; j < count; ++j) for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) *ri++ = *bi; *ri = 0; return xpath_string::from_heap_preallocated(result, ri); } xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) { switch (_type) { case ast_string_constant: return xpath_string::from_const(_data.string); case ast_func_local_name_0: { xpath_node na = c.n; return xpath_string::from_const(local_name(na)); } case ast_func_local_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); return xpath_string::from_const(local_name(na)); } case ast_func_name_0: { xpath_node na = c.n; return xpath_string::from_const(qualified_name(na)); } case ast_func_name_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); return xpath_string::from_const(qualified_name(na)); } case ast_func_namespace_uri_0: { xpath_node na = c.n; return xpath_string::from_const(namespace_uri(na)); } case ast_func_namespace_uri_1: { xpath_allocator_capture cr(stack.result); xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); xpath_node na = ns.first(); return xpath_string::from_const(namespace_uri(na)); } case ast_func_string_0: return string_value(c.n, stack.result); case ast_func_string_1: return _left->eval_string(c, stack); case ast_func_concat: return eval_string_concat(c, stack); case ast_func_substring_before: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); } case ast_func_substring_after: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); xpath_string p = _right->eval_string(c, swapped_stack); const char_t* pos = find_substring(s.c_str(), p.c_str()); if (!pos) return xpath_string(); const char_t* rbegin = pos + p.length(); const char_t* rend = s.c_str() + s.length(); return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); } case ast_func_substring_2: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); if (is_nan(first)) return xpath_string(); // NaN else if (first >= static_cast(s_length + 1)) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + s.length(); return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); } case ast_func_substring_3: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, swapped_stack); size_t s_length = s.length(); double first = round_nearest(_right->eval_number(c, stack)); double last = first + round_nearest(_right->_next->eval_number(c, stack)); if (is_nan(first) || is_nan(last)) return xpath_string(); else if (first >= static_cast(s_length + 1)) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); size_t end = last >= static_cast(s_length + 1) ? s_length + 1 : static_cast(last); assert(1 <= pos && pos <= end && end <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); const char_t* rend = s.c_str() + (end - 1); return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); } case ast_func_normalize_space_0: { xpath_string s = string_value(c.n, stack.result); char_t* begin = s.data(stack.result); if (!begin) return xpath_string(); char_t* end = normalize_space(begin); return xpath_string::from_heap_preallocated(begin, end); } case ast_func_normalize_space_1: { xpath_string s = _left->eval_string(c, stack); char_t* begin = s.data(stack.result); if (!begin) return xpath_string(); char_t* end = normalize_space(begin); return xpath_string::from_heap_preallocated(begin, end); } case ast_func_translate: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_string s = _left->eval_string(c, stack); xpath_string from = _right->eval_string(c, swapped_stack); xpath_string to = _right->_next->eval_string(c, swapped_stack); char_t* begin = s.data(stack.result); if (!begin) return xpath_string(); char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); return xpath_string::from_heap_preallocated(begin, end); } case ast_opt_translate_table: { xpath_string s = _left->eval_string(c, stack); char_t* begin = s.data(stack.result); if (!begin) return xpath_string(); char_t* end = translate_table(begin, _data.table); return xpath_string::from_heap_preallocated(begin, end); } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_string) return xpath_string::from_const(_data.variable->get_string()); // variable needs to be converted to the correct type, this is handled by the fallthrough block below break; } default: ; } // none of the ast types that return the value directly matched, we need to perform type conversion switch (_rettype) { case xpath_type_boolean: return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); case xpath_type_number: return convert_number_to_string(eval_number(c, stack), stack.result); case xpath_type_node_set: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); } default: assert(false && "Wrong expression for return type string"); // unreachable return xpath_string(); } } xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) { switch (_type) { case ast_op_union: { xpath_allocator_capture cr(stack.temp); xpath_stack swapped_stack = {stack.temp, stack.result}; xpath_node_set_raw ls = _left->eval_node_set(c, stack, eval); xpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval); // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother ls.set_type(xpath_node_set::type_unsorted); ls.append(rs.begin(), rs.end(), stack.result); ls.remove_duplicates(stack.temp); return ls; } case ast_filter: { xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); // either expression is a number or it contains position() call; sort by document order if (_test != predicate_posinv) set.sort_do(); bool once = eval_once(set.type(), eval); apply_predicate(set, 0, stack, once); return set; } case ast_func_id: return xpath_node_set_raw(); case ast_step: { switch (_axis) { case axis_ancestor: return step_do(c, stack, eval, axis_to_type()); case axis_ancestor_or_self: return step_do(c, stack, eval, axis_to_type()); case axis_attribute: return step_do(c, stack, eval, axis_to_type()); case axis_child: return step_do(c, stack, eval, axis_to_type()); case axis_descendant: return step_do(c, stack, eval, axis_to_type()); case axis_descendant_or_self: return step_do(c, stack, eval, axis_to_type()); case axis_following: return step_do(c, stack, eval, axis_to_type()); case axis_following_sibling: return step_do(c, stack, eval, axis_to_type()); case axis_namespace: // namespaced axis is not supported return xpath_node_set_raw(); case axis_parent: return step_do(c, stack, eval, axis_to_type()); case axis_preceding: return step_do(c, stack, eval, axis_to_type()); case axis_preceding_sibling: return step_do(c, stack, eval, axis_to_type()); case axis_self: return step_do(c, stack, eval, axis_to_type()); default: assert(false && "Unknown axis"); // unreachable return xpath_node_set_raw(); } } case ast_step_root: { assert(!_right); // root step can't have any predicates xpath_node_set_raw ns; ns.set_type(xpath_node_set::type_sorted); if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); return ns; } case ast_variable: { assert(_rettype == _data.variable->type()); if (_rettype == xpath_type_node_set) { const xpath_node_set& s = _data.variable->get_node_set(); xpath_node_set_raw ns; ns.set_type(s.type()); ns.append(s.begin(), s.end(), stack.result); return ns; } // variable needs to be converted to the correct type, this is handled by the fallthrough block below break; } default: ; } // none of the ast types that return the value directly matched, but conversions to node set are invalid assert(false && "Wrong expression for return type node set"); // unreachable return xpath_node_set_raw(); } void optimize(xpath_allocator* alloc) { if (_left) _left->optimize(alloc); if (_right) _right->optimize(alloc); if (_next) _next->optimize(alloc); // coverity[var_deref_model] optimize_self(alloc); } void optimize_self(xpath_allocator* alloc) { // Rewrite [position()=expr] with [expr] // Note that this step has to go before classification to recognize [position()=1] if ((_type == ast_filter || _type == ast_predicate) && _right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) { _right = _right->_right; } // Classify filter/predicate ops to perform various optimizations during evaluation if ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) { assert(_test == predicate_default); if (_right->_type == ast_number_constant && _right->_data.number == 1.0) _test = predicate_constant_one; else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) _test = predicate_constant; else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) _test = predicate_posinv; } // Rewrite descendant-or-self::node()/child::foo with descendant::foo // The former is a full form of //foo, the latter is much faster since it executes the node test immediately // Do a similar kind of rewrite for self/descendant/descendant-or-self axes // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && is_posinv_step()) { if (_axis == axis_child || _axis == axis_descendant) _axis = axis_descendant; else _axis = axis_descendant_or_self; _left = _left->_left; } // Use optimized lookup table implementation for translate() with constant arguments if (_type == ast_func_translate && _right && // workaround for clang static analyzer (_right is never null for ast_func_translate) _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) { unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); if (table) { _type = ast_opt_translate_table; _data.table = table; } } // Use optimized path for @attr = 'value' or @attr = $value if (_type == ast_op_equal && _left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal) // coverity[mixed_enums] _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) { _type = ast_opt_compare_attribute; } } bool is_posinv_expr() const { switch (_type) { case ast_func_position: case ast_func_last: return false; case ast_string_constant: case ast_number_constant: case ast_variable: return true; case ast_step: case ast_step_root: return true; case ast_predicate: case ast_filter: return true; default: if (_left && !_left->is_posinv_expr()) return false; for (xpath_ast_node* n = _right; n; n = n->_next) if (!n->is_posinv_expr()) return false; return true; } } bool is_posinv_step() const { assert(_type == ast_step); for (xpath_ast_node* n = _right; n; n = n->_next) { assert(n->_type == ast_predicate); if (n->_test != predicate_posinv) return false; } return true; } xpath_value_type rettype() const { return static_cast(_rettype); } }; static const size_t xpath_ast_depth_limit = #ifdef PUGIXML_XPATH_DEPTH_LIMIT PUGIXML_XPATH_DEPTH_LIMIT #else 1024 #endif ; struct xpath_parser { xpath_allocator* _alloc; xpath_lexer _lexer; const char_t* _query; xpath_variable_set* _variables; xpath_parse_result* _result; char_t _scratch[32]; size_t _depth; xpath_ast_node* error(const char* message) { _result->error = message; _result->offset = _lexer.current_pos() - _query; return 0; } xpath_ast_node* error_oom() { assert(_alloc->_error); *_alloc->_error = true; return 0; } xpath_ast_node* error_rec() { return error("Exceeded maximum allowed query depth"); } void* alloc_node() { return _alloc->allocate(sizeof(xpath_ast_node)); } xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value) { void* memory = alloc_node(); return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; } xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value) { void* memory = alloc_node(); return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; } xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value) { void* memory = alloc_node(); return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; } xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0) { void* memory = alloc_node(); return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0; } xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents) { void* memory = alloc_node(); return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0; } xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test) { void* memory = alloc_node(); return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0; } const char_t* alloc_string(const xpath_lexer_string& value) { if (!value.begin) return PUGIXML_TEXT(""); size_t length = static_cast(value.end - value.begin); char_t* c = static_cast(_alloc->allocate((length + 1) * sizeof(char_t))); if (!c) return 0; memcpy(c, value.begin, length * sizeof(char_t)); c[length] = 0; return c; } xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) { switch (name.begin[0]) { case 'b': if (name == PUGIXML_TEXT("boolean") && argc == 1) return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]); break; case 'c': if (name == PUGIXML_TEXT("count") && argc == 1) { if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); return alloc_node(ast_func_count, xpath_type_number, args[0]); } else if (name == PUGIXML_TEXT("contains") && argc == 2) return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); else if (name == PUGIXML_TEXT("concat") && argc >= 2) return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("ceiling") && argc == 1) return alloc_node(ast_func_ceiling, xpath_type_number, args[0]); break; case 'f': if (name == PUGIXML_TEXT("false") && argc == 0) return alloc_node(ast_func_false, xpath_type_boolean); else if (name == PUGIXML_TEXT("floor") && argc == 1) return alloc_node(ast_func_floor, xpath_type_number, args[0]); break; case 'i': if (name == PUGIXML_TEXT("id") && argc == 1) return alloc_node(ast_func_id, xpath_type_node_set, args[0]); break; case 'l': if (name == PUGIXML_TEXT("last") && argc == 0) return alloc_node(ast_func_last, xpath_type_number); else if (name == PUGIXML_TEXT("lang") && argc == 1) return alloc_node(ast_func_lang, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("local-name") && argc <= 1) { if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]); } break; case 'n': if (name == PUGIXML_TEXT("name") && argc <= 1) { if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]); } else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) { if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]); } else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("not") && argc == 1) return alloc_node(ast_func_not, xpath_type_boolean, args[0]); else if (name == PUGIXML_TEXT("number") && argc <= 1) return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); break; case 'p': if (name == PUGIXML_TEXT("position") && argc == 0) return alloc_node(ast_func_position, xpath_type_number); break; case 'r': if (name == PUGIXML_TEXT("round") && argc == 1) return alloc_node(ast_func_round, xpath_type_number, args[0]); break; case 's': if (name == PUGIXML_TEXT("string") && argc <= 1) return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); else if (name == PUGIXML_TEXT("string-length") && argc <= 1) return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); else if (name == PUGIXML_TEXT("starts-with") && argc == 2) return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); else if (name == PUGIXML_TEXT("substring-before") && argc == 2) return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("substring-after") && argc == 2) return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("sum") && argc == 1) { if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); return alloc_node(ast_func_sum, xpath_type_number, args[0]); } break; case 't': if (name == PUGIXML_TEXT("translate") && argc == 3) return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]); else if (name == PUGIXML_TEXT("true") && argc == 0) return alloc_node(ast_func_true, xpath_type_boolean); break; default: break; } return error("Unrecognized function or wrong parameter count"); } axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) { specified = true; switch (name.begin[0]) { case 'a': if (name == PUGIXML_TEXT("ancestor")) return axis_ancestor; else if (name == PUGIXML_TEXT("ancestor-or-self")) return axis_ancestor_or_self; else if (name == PUGIXML_TEXT("attribute")) return axis_attribute; break; case 'c': if (name == PUGIXML_TEXT("child")) return axis_child; break; case 'd': if (name == PUGIXML_TEXT("descendant")) return axis_descendant; else if (name == PUGIXML_TEXT("descendant-or-self")) return axis_descendant_or_self; break; case 'f': if (name == PUGIXML_TEXT("following")) return axis_following; else if (name == PUGIXML_TEXT("following-sibling")) return axis_following_sibling; break; case 'n': if (name == PUGIXML_TEXT("namespace")) return axis_namespace; break; case 'p': if (name == PUGIXML_TEXT("parent")) return axis_parent; else if (name == PUGIXML_TEXT("preceding")) return axis_preceding; else if (name == PUGIXML_TEXT("preceding-sibling")) return axis_preceding_sibling; break; case 's': if (name == PUGIXML_TEXT("self")) return axis_self; break; default: break; } specified = false; return axis_child; } nodetest_t parse_node_test_type(const xpath_lexer_string& name) { switch (name.begin[0]) { case 'c': if (name == PUGIXML_TEXT("comment")) return nodetest_type_comment; break; case 'n': if (name == PUGIXML_TEXT("node")) return nodetest_type_node; break; case 'p': if (name == PUGIXML_TEXT("processing-instruction")) return nodetest_type_pi; break; case 't': if (name == PUGIXML_TEXT("text")) return nodetest_type_text; break; default: break; } return nodetest_none; } // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall xpath_ast_node* parse_primary_expression() { switch (_lexer.current()) { case lex_var_ref: { xpath_lexer_string name = _lexer.contents(); if (!_variables) return error("Unknown variable: variable set is not provided"); xpath_variable* var = 0; if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) return error_oom(); if (!var) return error("Unknown variable: variable set does not contain the given name"); _lexer.next(); return alloc_node(ast_variable, var->type(), var); } case lex_open_brace: { _lexer.next(); xpath_ast_node* n = parse_expression(); if (!n) return 0; if (_lexer.current() != lex_close_brace) return error("Expected ')' to match an opening '('"); _lexer.next(); return n; } case lex_quoted_string: { const char_t* value = alloc_string(_lexer.contents()); if (!value) return 0; _lexer.next(); return alloc_node(ast_string_constant, xpath_type_string, value); } case lex_number: { double value = 0; if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) return error_oom(); _lexer.next(); return alloc_node(ast_number_constant, xpath_type_number, value); } case lex_string: { xpath_ast_node* args[2] = {0}; size_t argc = 0; xpath_lexer_string function = _lexer.contents(); _lexer.next(); xpath_ast_node* last_arg = 0; if (_lexer.current() != lex_open_brace) return error("Unrecognized function call"); _lexer.next(); size_t old_depth = _depth; while (_lexer.current() != lex_close_brace) { if (argc > 0) { if (_lexer.current() != lex_comma) return error("No comma between function arguments"); _lexer.next(); } if (++_depth > xpath_ast_depth_limit) return error_rec(); xpath_ast_node* n = parse_expression(); if (!n) return 0; if (argc < 2) args[argc] = n; else last_arg->set_next(n); argc++; last_arg = n; } _lexer.next(); _depth = old_depth; return parse_function(function, argc, args); } default: return error("Unrecognizable primary expression"); } } // FilterExpr ::= PrimaryExpr | FilterExpr Predicate // Predicate ::= '[' PredicateExpr ']' // PredicateExpr ::= Expr xpath_ast_node* parse_filter_expression() { xpath_ast_node* n = parse_primary_expression(); if (!n) return 0; size_t old_depth = _depth; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); if (++_depth > xpath_ast_depth_limit) return error_rec(); if (n->rettype() != xpath_type_node_set) return error("Predicate has to be applied to node set"); xpath_ast_node* expr = parse_expression(); if (!expr) return 0; n = alloc_node(ast_filter, n, expr, predicate_default); if (!n) return 0; if (_lexer.current() != lex_close_square_brace) return error("Expected ']' to match an opening '['"); _lexer.next(); } _depth = old_depth; return n; } // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep // AxisSpecifier ::= AxisName '::' | '@'? // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' // NameTest ::= '*' | NCName ':' '*' | QName // AbbreviatedStep ::= '.' | '..' xpath_ast_node* parse_step(xpath_ast_node* set) { if (set && set->rettype() != xpath_type_node_set) return error("Step has to be applied to node set"); bool axis_specified = false; axis_t axis = axis_child; // implied child axis if (_lexer.current() == lex_axis_attribute) { axis = axis_attribute; axis_specified = true; _lexer.next(); } else if (_lexer.current() == lex_dot) { _lexer.next(); if (_lexer.current() == lex_open_square_brace) return error("Predicates are not allowed after an abbreviated step"); return alloc_node(ast_step, set, axis_self, nodetest_type_node, 0); } else if (_lexer.current() == lex_double_dot) { _lexer.next(); if (_lexer.current() == lex_open_square_brace) return error("Predicates are not allowed after an abbreviated step"); return alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0); } nodetest_t nt_type = nodetest_none; xpath_lexer_string nt_name; if (_lexer.current() == lex_string) { // node name test nt_name = _lexer.contents(); _lexer.next(); // was it an axis name? if (_lexer.current() == lex_double_colon) { // parse axis name if (axis_specified) return error("Two axis specifiers in one step"); axis = parse_axis_name(nt_name, axis_specified); if (!axis_specified) return error("Unknown axis"); // read actual node test _lexer.next(); if (_lexer.current() == lex_multiply) { nt_type = nodetest_all; nt_name = xpath_lexer_string(); _lexer.next(); } else if (_lexer.current() == lex_string) { nt_name = _lexer.contents(); _lexer.next(); } else { return error("Unrecognized node test"); } } if (nt_type == nodetest_none) { // node type test or processing-instruction if (_lexer.current() == lex_open_brace) { _lexer.next(); if (_lexer.current() == lex_close_brace) { _lexer.next(); nt_type = parse_node_test_type(nt_name); if (nt_type == nodetest_none) return error("Unrecognized node type"); nt_name = xpath_lexer_string(); } else if (nt_name == PUGIXML_TEXT("processing-instruction")) { if (_lexer.current() != lex_quoted_string) return error("Only literals are allowed as arguments to processing-instruction()"); nt_type = nodetest_pi; nt_name = _lexer.contents(); _lexer.next(); if (_lexer.current() != lex_close_brace) return error("Unmatched brace near processing-instruction()"); _lexer.next(); } else { return error("Unmatched brace near node type test"); } } // QName or NCName:* else { if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* { nt_name.end--; // erase * nt_type = nodetest_all_in_namespace; } else { nt_type = nodetest_name; } } } } else if (_lexer.current() == lex_multiply) { nt_type = nodetest_all; _lexer.next(); } else { return error("Unrecognized node test"); } const char_t* nt_name_copy = alloc_string(nt_name); if (!nt_name_copy) return 0; xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); if (!n) return 0; size_t old_depth = _depth; xpath_ast_node* last = 0; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); if (++_depth > xpath_ast_depth_limit) return error_rec(); xpath_ast_node* expr = parse_expression(); if (!expr) return 0; xpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default); if (!pred) return 0; if (_lexer.current() != lex_close_square_brace) return error("Expected ']' to match an opening '['"); _lexer.next(); if (last) last->set_next(pred); else n->set_right(pred); last = pred; } _depth = old_depth; return n; } // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) { xpath_ast_node* n = parse_step(set); if (!n) return 0; size_t old_depth = _depth; while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); if (l == lex_double_slash) { n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); if (!n) return 0; ++_depth; } if (++_depth > xpath_ast_depth_limit) return error_rec(); n = parse_step(n); if (!n) return 0; } _depth = old_depth; return n; } // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath xpath_ast_node* parse_location_path() { if (_lexer.current() == lex_slash) { _lexer.next(); xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); if (!n) return 0; // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path lexeme_t l = _lexer.current(); if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) return parse_relative_location_path(n); else return n; } else if (_lexer.current() == lex_double_slash) { _lexer.next(); xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); if (!n) return 0; n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); if (!n) return 0; return parse_relative_location_path(n); } // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 return parse_relative_location_path(0); } // PathExpr ::= LocationPath // | FilterExpr // | FilterExpr '/' RelativeLocationPath // | FilterExpr '//' RelativeLocationPath // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr // UnaryExpr ::= UnionExpr | '-' UnaryExpr xpath_ast_node* parse_path_or_unary_expression() { // Clarification. // PathExpr begins with either LocationPath or FilterExpr. // FilterExpr begins with PrimaryExpr // PrimaryExpr begins with '$' in case of it being a variable reference, // '(' in case of it being an expression, string literal, number constant or // function call. if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || _lexer.current() == lex_string) { if (_lexer.current() == lex_string) { // This is either a function call, or not - if not, we shall proceed with location path const char_t* state = _lexer.state(); while (PUGI_IMPL_IS_CHARTYPE(*state, ct_space)) ++state; if (*state != '(') return parse_location_path(); // This looks like a function call; however this still can be a node-test. Check it. if (parse_node_test_type(_lexer.contents()) != nodetest_none) return parse_location_path(); } xpath_ast_node* n = parse_filter_expression(); if (!n) return 0; if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); if (l == lex_double_slash) { if (n->rettype() != xpath_type_node_set) return error("Step has to be applied to node set"); n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); if (!n) return 0; } // select from location path return parse_relative_location_path(n); } return n; } else if (_lexer.current() == lex_minus) { _lexer.next(); // precedence 7+ - only parses union expressions xpath_ast_node* n = parse_expression(7); if (!n) return 0; return alloc_node(ast_op_negate, xpath_type_number, n); } else { return parse_location_path(); } } struct binary_op_t { ast_type_t asttype; xpath_value_type rettype; int precedence; binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) { } binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) { } static binary_op_t parse(xpath_lexer& lexer) { switch (lexer.current()) { case lex_string: if (lexer.contents() == PUGIXML_TEXT("or")) return binary_op_t(ast_op_or, xpath_type_boolean, 1); else if (lexer.contents() == PUGIXML_TEXT("and")) return binary_op_t(ast_op_and, xpath_type_boolean, 2); else if (lexer.contents() == PUGIXML_TEXT("div")) return binary_op_t(ast_op_divide, xpath_type_number, 6); else if (lexer.contents() == PUGIXML_TEXT("mod")) return binary_op_t(ast_op_mod, xpath_type_number, 6); else return binary_op_t(); case lex_equal: return binary_op_t(ast_op_equal, xpath_type_boolean, 3); case lex_not_equal: return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); case lex_less: return binary_op_t(ast_op_less, xpath_type_boolean, 4); case lex_greater: return binary_op_t(ast_op_greater, xpath_type_boolean, 4); case lex_less_or_equal: return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); case lex_greater_or_equal: return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); case lex_plus: return binary_op_t(ast_op_add, xpath_type_number, 5); case lex_minus: return binary_op_t(ast_op_subtract, xpath_type_number, 5); case lex_multiply: return binary_op_t(ast_op_multiply, xpath_type_number, 6); case lex_union: return binary_op_t(ast_op_union, xpath_type_node_set, 7); default: return binary_op_t(); } } }; xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) { binary_op_t op = binary_op_t::parse(_lexer); while (op.asttype != ast_unknown && op.precedence >= limit) { _lexer.next(); if (++_depth > xpath_ast_depth_limit) return error_rec(); xpath_ast_node* rhs = parse_path_or_unary_expression(); if (!rhs) return 0; binary_op_t nextop = binary_op_t::parse(_lexer); while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) { rhs = parse_expression_rec(rhs, nextop.precedence); if (!rhs) return 0; nextop = binary_op_t::parse(_lexer); } if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) return error("Union operator has to be applied to node sets"); lhs = alloc_node(op.asttype, op.rettype, lhs, rhs); if (!lhs) return 0; op = binary_op_t::parse(_lexer); } return lhs; } // Expr ::= OrExpr // OrExpr ::= AndExpr | OrExpr 'or' AndExpr // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr // EqualityExpr ::= RelationalExpr // | EqualityExpr '=' RelationalExpr // | EqualityExpr '!=' RelationalExpr // RelationalExpr ::= AdditiveExpr // | RelationalExpr '<' AdditiveExpr // | RelationalExpr '>' AdditiveExpr // | RelationalExpr '<=' AdditiveExpr // | RelationalExpr '>=' AdditiveExpr // AdditiveExpr ::= MultiplicativeExpr // | AdditiveExpr '+' MultiplicativeExpr // | AdditiveExpr '-' MultiplicativeExpr // MultiplicativeExpr ::= UnaryExpr // | MultiplicativeExpr '*' UnaryExpr // | MultiplicativeExpr 'div' UnaryExpr // | MultiplicativeExpr 'mod' UnaryExpr xpath_ast_node* parse_expression(int limit = 0) { size_t old_depth = _depth; if (++_depth > xpath_ast_depth_limit) return error_rec(); xpath_ast_node* n = parse_path_or_unary_expression(); if (!n) return 0; n = parse_expression_rec(n, limit); _depth = old_depth; return n; } xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0) { } xpath_ast_node* parse() { xpath_ast_node* n = parse_expression(); if (!n) return 0; assert(_depth == 0); // check if there are unparsed tokens left if (_lexer.current() != lex_eof) return error("Incorrect query"); return n; } static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) { xpath_parser parser(query, variables, alloc, result); return parser.parse(); } }; struct xpath_query_impl { static xpath_query_impl* create() { void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); if (!memory) return 0; return new (memory) xpath_query_impl(); } static void destroy(xpath_query_impl* impl) { // free all allocated pages impl->alloc.release(); // free allocator memory (with the first page) xml_memory::deallocate(impl); } xpath_query_impl(): root(0), alloc(&block, &oom), oom(false) { block.next = 0; block.capacity = sizeof(block.data); } xpath_ast_node* root; xpath_allocator alloc; xpath_memory_block block; bool oom; }; PUGI_IMPL_FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) { if (!impl) return 0; if (impl->root->rettype() != xpath_type_node_set) { #ifdef PUGIXML_NO_EXCEPTIONS return 0; #else xpath_parse_result res; res.error = "Expression does not evaluate to node set"; throw xpath_exception(res); #endif } return impl->root; } PUGI_IMPL_NS_END namespace pugi { #ifndef PUGIXML_NO_EXCEPTIONS PUGI_IMPL_FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) { assert(_result.error); } PUGI_IMPL_FN const char* xpath_exception::what() const throw() { return _result.error; } PUGI_IMPL_FN const xpath_parse_result& xpath_exception::result() const { return _result; } #endif PUGI_IMPL_FN xpath_node::xpath_node() { } PUGI_IMPL_FN xpath_node::xpath_node(const xml_node& node_): _node(node_) { } PUGI_IMPL_FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) { } PUGI_IMPL_FN xml_node xpath_node::node() const { return _attribute ? xml_node() : _node; } PUGI_IMPL_FN xml_attribute xpath_node::attribute() const { return _attribute; } PUGI_IMPL_FN xml_node xpath_node::parent() const { return _attribute ? _node : _node.parent(); } PUGI_IMPL_FN static void unspecified_bool_xpath_node(xpath_node***) { } PUGI_IMPL_FN xpath_node::operator xpath_node::unspecified_bool_type() const { return (_node || _attribute) ? unspecified_bool_xpath_node : 0; } PUGI_IMPL_FN bool xpath_node::operator!() const { return !(_node || _attribute); } PUGI_IMPL_FN bool xpath_node::operator==(const xpath_node& n) const { return _node == n._node && _attribute == n._attribute; } PUGI_IMPL_FN bool xpath_node::operator!=(const xpath_node& n) const { return _node != n._node || _attribute != n._attribute; } #ifdef __BORLANDC__ PUGI_IMPL_FN bool operator&&(const xpath_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI_IMPL_FN bool operator||(const xpath_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI_IMPL_FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) { assert(begin_ <= end_); size_t size_ = static_cast(end_ - begin_); // use internal buffer for 0 or 1 elements, heap buffer otherwise xpath_node* storage = (size_ <= 1) ? _storage : static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); if (!storage) { #ifdef PUGIXML_NO_EXCEPTIONS return; #else throw std::bad_alloc(); #endif } // deallocate old buffer if (_begin != _storage) impl::xml_memory::deallocate(_begin); // size check is necessary because for begin_ = end_ = nullptr, memcpy is UB if (size_) memcpy(storage, begin_, size_ * sizeof(xpath_node)); _begin = storage; _end = storage + size_; _type = type_; } #ifdef PUGIXML_HAS_MOVE PUGI_IMPL_FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT { _type = rhs._type; _storage[0] = rhs._storage[0]; _begin = (rhs._begin == rhs._storage) ? _storage : rhs._begin; _end = _begin + (rhs._end - rhs._begin); rhs._type = type_unsorted; rhs._begin = rhs._storage; rhs._end = rhs._storage; } #endif PUGI_IMPL_FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage) { } PUGI_IMPL_FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(begin_, end_, type_); } PUGI_IMPL_FN xpath_node_set::~xpath_node_set() { if (_begin != _storage) impl::xml_memory::deallocate(_begin); } PUGI_IMPL_FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(ns._begin, ns._end, ns._type); } PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) { if (this == &ns) return *this; _assign(ns._begin, ns._end, ns._type); return *this; } #ifdef PUGIXML_HAS_MOVE PUGI_IMPL_FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage) { _move(rhs); } PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT { if (this == &rhs) return *this; if (_begin != _storage) impl::xml_memory::deallocate(_begin); _move(rhs); return *this; } #endif PUGI_IMPL_FN xpath_node_set::type_t xpath_node_set::type() const { return _type; } PUGI_IMPL_FN size_t xpath_node_set::size() const { return _end - _begin; } PUGI_IMPL_FN bool xpath_node_set::empty() const { return _begin == _end; } PUGI_IMPL_FN const xpath_node& xpath_node_set::operator[](size_t index) const { assert(index < size()); return _begin[index]; } PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::begin() const { return _begin; } PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::end() const { return _end; } PUGI_IMPL_FN void xpath_node_set::sort(bool reverse) { _type = impl::xpath_sort(_begin, _end, _type, reverse); } PUGI_IMPL_FN xpath_node xpath_node_set::first() const { return impl::xpath_first(_begin, _end, _type); } PUGI_IMPL_FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) { } PUGI_IMPL_FN xpath_parse_result::operator bool() const { return error == 0; } PUGI_IMPL_FN const char* xpath_parse_result::description() const { return error ? error : "No error"; } PUGI_IMPL_FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) { } PUGI_IMPL_FN const char_t* xpath_variable::name() const { switch (_type) { case xpath_type_node_set: return static_cast(this)->name; case xpath_type_number: return static_cast(this)->name; case xpath_type_string: return static_cast(this)->name; case xpath_type_boolean: return static_cast(this)->name; default: assert(false && "Invalid variable type"); // unreachable return 0; } } PUGI_IMPL_FN xpath_value_type xpath_variable::type() const { return _type; } PUGI_IMPL_FN bool xpath_variable::get_boolean() const { return (_type == xpath_type_boolean) ? static_cast(this)->value : false; } PUGI_IMPL_FN double xpath_variable::get_number() const { return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); } PUGI_IMPL_FN const char_t* xpath_variable::get_string() const { const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; return value ? value : PUGIXML_TEXT(""); } PUGI_IMPL_FN const xpath_node_set& xpath_variable::get_node_set() const { return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; } PUGI_IMPL_FN bool xpath_variable::set(bool value) { if (_type != xpath_type_boolean) return false; static_cast(this)->value = value; return true; } PUGI_IMPL_FN bool xpath_variable::set(double value) { if (_type != xpath_type_number) return false; static_cast(this)->value = value; return true; } PUGI_IMPL_FN bool xpath_variable::set(const char_t* value) { if (_type != xpath_type_string) return false; impl::xpath_variable_string* var = static_cast(this); // duplicate string size_t size = (impl::strlength(value) + 1) * sizeof(char_t); char_t* copy = static_cast(impl::xml_memory::allocate(size)); if (!copy) return false; memcpy(copy, value, size); // replace old string if (var->value) impl::xml_memory::deallocate(var->value); var->value = copy; return true; } PUGI_IMPL_FN bool xpath_variable::set(const xpath_node_set& value) { if (_type != xpath_type_node_set) return false; static_cast(this)->value = value; return true; } PUGI_IMPL_FN xpath_variable_set::xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; } PUGI_IMPL_FN xpath_variable_set::~xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _destroy(_data[i]); } PUGI_IMPL_FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; _assign(rhs); } PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) { if (this == &rhs) return *this; _assign(rhs); return *this; } #ifdef PUGIXML_HAS_MOVE PUGI_IMPL_FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { _data[i] = rhs._data[i]; rhs._data[i] = 0; } } PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { _destroy(_data[i]); _data[i] = rhs._data[i]; rhs._data[i] = 0; } return *this; } #endif PUGI_IMPL_FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) { xpath_variable_set temp; for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) return; _swap(temp); } PUGI_IMPL_FN void xpath_variable_set::_swap(xpath_variable_set& rhs) { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) { xpath_variable* chain = _data[i]; _data[i] = rhs._data[i]; rhs._data[i] = chain; } } PUGI_IMPL_FN xpath_variable* xpath_variable_set::_find(const char_t* name) const { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var; return 0; } PUGI_IMPL_FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) { xpath_variable* last = 0; while (var) { // allocate storage for new variable xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); if (!nvar) return false; // link the variable to the result immediately to handle failures gracefully if (last) last->_next = nvar; else *out_result = nvar; last = nvar; // copy the value; this can fail due to out-of-memory conditions if (!impl::copy_xpath_variable(nvar, var)) return false; var = var->_next; } return true; } PUGI_IMPL_FN void xpath_variable_set::_destroy(xpath_variable* var) { while (var) { xpath_variable* next = var->_next; impl::delete_xpath_variable(var->_type, var); var = next; } } PUGI_IMPL_FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) { const size_t hash_size = sizeof(_data) / sizeof(_data[0]); size_t hash = impl::hash_string(name) % hash_size; // look for existing variable for (xpath_variable* var = _data[hash]; var; var = var->_next) if (impl::strequal(var->name(), name)) return var->type() == type ? var : 0; // add new variable xpath_variable* result = impl::new_xpath_variable(type, name); if (result) { result->_next = _data[hash]; _data[hash] = result; } return result; } PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, bool value) { xpath_variable* var = add(name, xpath_type_boolean); return var ? var->set(value) : false; } PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, double value) { xpath_variable* var = add(name, xpath_type_number); return var ? var->set(value) : false; } PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const char_t* value) { xpath_variable* var = add(name, xpath_type_string); return var ? var->set(value) : false; } PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) { xpath_variable* var = add(name, xpath_type_node_set); return var ? var->set(value) : false; } PUGI_IMPL_FN xpath_variable* xpath_variable_set::get(const char_t* name) { return _find(name); } PUGI_IMPL_FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { return _find(name); } PUGI_IMPL_FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) { impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); if (!qimpl) { #ifdef PUGIXML_NO_EXCEPTIONS _result.error = "Out of memory"; #else throw std::bad_alloc(); #endif } else { using impl::auto_deleter; // MSVC7 workaround auto_deleter impl(qimpl, impl::xpath_query_impl::destroy); qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); if (qimpl->root) { qimpl->root->optimize(&qimpl->alloc); _impl = impl.release(); _result.error = 0; } else { #ifdef PUGIXML_NO_EXCEPTIONS if (qimpl->oom) _result.error = "Out of memory"; #else if (qimpl->oom) throw std::bad_alloc(); throw xpath_exception(_result); #endif } } } PUGI_IMPL_FN xpath_query::xpath_query(): _impl(0) { } PUGI_IMPL_FN xpath_query::~xpath_query() { if (_impl) impl::xpath_query_impl::destroy(static_cast(_impl)); } #ifdef PUGIXML_HAS_MOVE PUGI_IMPL_FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT { _impl = rhs._impl; _result = rhs._result; rhs._impl = 0; rhs._result = xpath_parse_result(); } PUGI_IMPL_FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT { if (this == &rhs) return *this; if (_impl) impl::xpath_query_impl::destroy(static_cast(_impl)); _impl = rhs._impl; _result = rhs._result; rhs._impl = 0; rhs._result = xpath_parse_result(); return *this; } #endif PUGI_IMPL_FN xpath_value_type xpath_query::return_type() const { if (!_impl) return xpath_type_none; return static_cast(_impl)->root->rettype(); } PUGI_IMPL_FN bool xpath_query::evaluate_boolean(const xpath_node& n) const { if (!_impl) return false; impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; bool r = static_cast(_impl)->root->eval_boolean(c, sd.stack); if (sd.oom) { #ifdef PUGIXML_NO_EXCEPTIONS return false; #else throw std::bad_alloc(); #endif } return r; } PUGI_IMPL_FN double xpath_query::evaluate_number(const xpath_node& n) const { if (!_impl) return impl::gen_nan(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; double r = static_cast(_impl)->root->eval_number(c, sd.stack); if (sd.oom) { #ifdef PUGIXML_NO_EXCEPTIONS return impl::gen_nan(); #else throw std::bad_alloc(); #endif } return r; } #ifndef PUGIXML_NO_STL PUGI_IMPL_FN string_t xpath_query::evaluate_string(const xpath_node& n) const { if (!_impl) return string_t(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; impl::xpath_string r = static_cast(_impl)->root->eval_string(c, sd.stack); if (sd.oom) { #ifdef PUGIXML_NO_EXCEPTIONS return string_t(); #else throw std::bad_alloc(); #endif } return string_t(r.c_str(), r.length()); } #endif PUGI_IMPL_FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const { impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; impl::xpath_string r = _impl ? static_cast(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string(); if (sd.oom) { #ifdef PUGIXML_NO_EXCEPTIONS r = impl::xpath_string(); #else throw std::bad_alloc(); #endif } size_t full_size = r.length() + 1; if (capacity > 0) { size_t size = (full_size < capacity) ? full_size : capacity; assert(size > 0); memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); buffer[size - 1] = 0; } return full_size; } PUGI_IMPL_FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const { impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); if (!root) return xpath_node_set(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); if (sd.oom) { #ifdef PUGIXML_NO_EXCEPTIONS return xpath_node_set(); #else throw std::bad_alloc(); #endif } return xpath_node_set(r.begin(), r.end(), r.type()); } PUGI_IMPL_FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const { impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); if (!root) return xpath_node(); impl::xpath_context c(n, 1, 1); impl::xpath_stack_data sd; impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); if (sd.oom) { #ifdef PUGIXML_NO_EXCEPTIONS return xpath_node(); #else throw std::bad_alloc(); #endif } return r.first(); } PUGI_IMPL_FN const xpath_parse_result& xpath_query::result() const { return _result; } PUGI_IMPL_FN static void unspecified_bool_xpath_query(xpath_query***) { } PUGI_IMPL_FN xpath_query::operator xpath_query::unspecified_bool_type() const { return _impl ? unspecified_bool_xpath_query : 0; } PUGI_IMPL_FN bool xpath_query::operator!() const { return !_impl; } PUGI_IMPL_FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return q.evaluate_node(*this); } PUGI_IMPL_FN xpath_node xml_node::select_node(const xpath_query& query) const { return query.evaluate_node(*this); } PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return q.evaluate_node_set(*this); } PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const { return query.evaluate_node_set(*this); } PUGI_IMPL_FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const { xpath_query q(query, variables); return q.evaluate_node(*this); } PUGI_IMPL_FN xpath_node xml_node::select_single_node(const xpath_query& query) const { return query.evaluate_node(*this); } } #endif #ifdef __BORLANDC__ # pragma option pop #endif // Intel C++ does not properly keep warning state for function templates, // so popping warning state at the end of translation unit leads to warnings in the middle. #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) # pragma warning(pop) #endif #if defined(_MSC_VER) && defined(__c2__) # pragma clang diagnostic pop #endif // Undefine all local macros (makes sure we're not leaking macros in header-only mode) #undef PUGI_IMPL_NO_INLINE #undef PUGI_IMPL_UNLIKELY #undef PUGI_IMPL_STATIC_ASSERT #undef PUGI_IMPL_DMC_VOLATILE #undef PUGI_IMPL_UNSIGNED_OVERFLOW #undef PUGI_IMPL_MSVC_CRT_VERSION #undef PUGI_IMPL_SNPRINTF #undef PUGI_IMPL_NS_BEGIN #undef PUGI_IMPL_NS_END #undef PUGI_IMPL_FN #undef PUGI_IMPL_FN_NO_INLINE #undef PUGI_IMPL_GETHEADER_IMPL #undef PUGI_IMPL_GETPAGE_IMPL #undef PUGI_IMPL_GETPAGE #undef PUGI_IMPL_NODETYPE #undef PUGI_IMPL_IS_CHARTYPE_IMPL #undef PUGI_IMPL_IS_CHARTYPE #undef PUGI_IMPL_IS_CHARTYPEX #undef PUGI_IMPL_ENDSWITH #undef PUGI_IMPL_SKIPWS #undef PUGI_IMPL_OPTSET #undef PUGI_IMPL_PUSHNODE #undef PUGI_IMPL_POPNODE #undef PUGI_IMPL_SCANFOR #undef PUGI_IMPL_SCANWHILE #undef PUGI_IMPL_SCANWHILE_UNROLL #undef PUGI_IMPL_ENDSEG #undef PUGI_IMPL_THROW_ERROR #undef PUGI_IMPL_CHECK_ERROR #endif /** * Copyright (c) 2006-2023 Arseny Kapoulkine * * 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. */ tea-qt-63.3.0/src/pugixml.hpp000066400000000000000000001520311476733534200160030ustar00rootroot00000000000000/** * pugixml parser - version 1.14 * -------------------------------------------------------- * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. * * This work is based on the pugxml parser, which is: * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) */ // Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons // Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits #ifndef PUGIXML_VERSION # define PUGIXML_VERSION 1140 // 1.14 #endif // Include user configuration file (this can define various configuration macros) #include "pugiconfig.hpp" #ifndef HEADER_PUGIXML_HPP #define HEADER_PUGIXML_HPP // Include stddef.h for size_t and ptrdiff_t #include // Include exception header for XPath #if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) # include #endif // Include STL headers #ifndef PUGIXML_NO_STL # include # include # include #endif // Macro for deprecated features #ifndef PUGIXML_DEPRECATED # if defined(__GNUC__) # define PUGIXML_DEPRECATED __attribute__((deprecated)) # elif defined(_MSC_VER) && _MSC_VER >= 1300 # define PUGIXML_DEPRECATED __declspec(deprecated) # else # define PUGIXML_DEPRECATED # endif #endif // If no API is defined, assume default #ifndef PUGIXML_API # define PUGIXML_API #endif // If no API for classes is defined, assume default #ifndef PUGIXML_CLASS # define PUGIXML_CLASS PUGIXML_API #endif // If no API for functions is defined, assume default #ifndef PUGIXML_FUNCTION # define PUGIXML_FUNCTION PUGIXML_API #endif // If the platform is known to have long long support, enable long long functions #ifndef PUGIXML_HAS_LONG_LONG # if __cplusplus >= 201103 # define PUGIXML_HAS_LONG_LONG # elif defined(_MSC_VER) && _MSC_VER >= 1400 # define PUGIXML_HAS_LONG_LONG # endif #endif // If the platform is known to have move semantics support, compile move ctor/operator implementation #ifndef PUGIXML_HAS_MOVE # if __cplusplus >= 201103 # define PUGIXML_HAS_MOVE # elif defined(_MSC_VER) && _MSC_VER >= 1600 # define PUGIXML_HAS_MOVE # endif #endif // If C++ is 2011 or higher, add 'noexcept' specifiers #ifndef PUGIXML_NOEXCEPT # if __cplusplus >= 201103 # define PUGIXML_NOEXCEPT noexcept # elif defined(_MSC_VER) && _MSC_VER >= 1900 # define PUGIXML_NOEXCEPT noexcept # else # define PUGIXML_NOEXCEPT # endif #endif // Some functions can not be noexcept in compact mode #ifdef PUGIXML_COMPACT # define PUGIXML_NOEXCEPT_IF_NOT_COMPACT #else # define PUGIXML_NOEXCEPT_IF_NOT_COMPACT PUGIXML_NOEXCEPT #endif // If C++ is 2011 or higher, add 'override' qualifiers #ifndef PUGIXML_OVERRIDE # if __cplusplus >= 201103 # define PUGIXML_OVERRIDE override # elif defined(_MSC_VER) && _MSC_VER >= 1700 # define PUGIXML_OVERRIDE override # else # define PUGIXML_OVERRIDE # endif #endif // If C++ is 2011 or higher, use 'nullptr' #ifndef PUGIXML_NULL # if __cplusplus >= 201103 # define PUGIXML_NULL nullptr # elif defined(_MSC_VER) && _MSC_VER >= 1600 # define PUGIXML_NULL nullptr # else # define PUGIXML_NULL 0 # endif #endif // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t # define PUGIXML_CHAR wchar_t #else # define PUGIXML_TEXT(t) t # define PUGIXML_CHAR char #endif namespace pugi { // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE typedef PUGIXML_CHAR char_t; #ifndef PUGIXML_NO_STL // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE typedef std::basic_string, std::allocator > string_t; #endif } // The PugiXML namespace namespace pugi { // Tree node types enum xml_node_type { node_null, // Empty (null) node handle node_document, // A document tree's absolute root node_element, // Element tag, i.e. '' node_pcdata, // Plain character data, i.e. 'text' node_cdata, // Character data, i.e. '' node_comment, // Comment tag, i.e. '' node_pi, // Processing instruction, i.e. '' node_declaration, // Document declaration, i.e. '' node_doctype // Document type declaration, i.e. '' }; // Parsing options // Minimal parsing mode (equivalent to turning all other flags off). // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. const unsigned int parse_minimal = 0x0000; // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. const unsigned int parse_pi = 0x0001; // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. const unsigned int parse_comments = 0x0002; // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. const unsigned int parse_cdata = 0x0004; // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata = 0x0008; // This flag determines if character and entity references are expanded during parsing. This flag is on by default. const unsigned int parse_escapes = 0x0010; // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. const unsigned int parse_eol = 0x0020; // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. const unsigned int parse_wconv_attribute = 0x0040; // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. const unsigned int parse_wnorm_attribute = 0x0080; // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. const unsigned int parse_declaration = 0x0100; // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. const unsigned int parse_doctype = 0x0200; // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only // of whitespace is added to the DOM tree. // This flag is off by default; turning it on may result in slower parsing and more memory consumption. const unsigned int parse_ws_pcdata_single = 0x0400; // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. const unsigned int parse_trim_pcdata = 0x0800; // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document // is a valid document. This flag is off by default. const unsigned int parse_fragment = 0x1000; // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments. // This flag is off by default. const unsigned int parse_embed_pcdata = 0x2000; // This flag determines whether determines whether the the two pcdata should be merged or not, if no intermediatory data are parsed in the document. // This flag is off by default. const unsigned int parse_merge_pcdata = 0x4000; // The default parsing mode. // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; // The full parsing mode. // Nodes of all types are added to the DOM tree, character/reference entities are expanded, // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; // These flags determine the encoding of input data for XML document enum xml_encoding { encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range { public: typedef It const_iterator; typedef It iterator; xml_object_range(It b, It e): _begin(b), _end(e) { } It begin() const { return _begin; } It end() const { return _end; } bool empty() const { return _begin == _end; } private: It _begin, _end; }; // Writer interface for node printing (see xml_node::print) class PUGIXML_CLASS xml_writer { public: virtual ~xml_writer(); // Write memory chunk into stream/file/whatever virtual void write(const void* data, size_t size) = 0; }; // xml_writer implementation for FILE* class PUGIXML_CLASS xml_writer_file: public xml_writer { public: // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio xml_writer_file(void* file); virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; private: void* file; }; #ifndef PUGIXML_NO_STL // xml_writer implementation for streams class PUGIXML_CLASS xml_writer_stream: public xml_writer { public: // Construct writer from an output stream object xml_writer_stream(std::basic_ostream >& stream); xml_writer_stream(std::basic_ostream >& stream); virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; private: std::basic_ostream >* narrow_stream; std::basic_ostream >* wide_stream; }; #endif // A light-weight handle for manipulating attributes in DOM tree class PUGIXML_CLASS xml_attribute { friend class xml_attribute_iterator; friend class xml_node; private: xml_attribute_struct* _attr; typedef void (*unspecified_bool_type)(xml_attribute***); public: // Default constructor. Constructs an empty attribute. xml_attribute(); // Constructs attribute from internal pointer explicit xml_attribute(xml_attribute_struct* attr); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped attribute pointers) bool operator==(const xml_attribute& r) const; bool operator!=(const xml_attribute& r) const; bool operator<(const xml_attribute& r) const; bool operator>(const xml_attribute& r) const; bool operator<=(const xml_attribute& r) const; bool operator>=(const xml_attribute& r) const; // Check if attribute is empty bool empty() const; // Get attribute name/value, or "" if attribute is empty const char_t* name() const; const char_t* value() const; // Get attribute value, or the default value if attribute is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; #ifdef PUGIXML_HAS_LONG_LONG long long as_llong(long long def = 0) const; unsigned long long as_ullong(unsigned long long def = 0) const; #endif // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty bool as_bool(bool def = false) const; // Set attribute name/value (returns false if attribute is empty or there is not enough memory) bool set_name(const char_t* rhs); bool set_name(const char_t* rhs, size_t size); bool set_value(const char_t* rhs); bool set_value(const char_t* rhs, size_t size); // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set_value(int rhs); bool set_value(unsigned int rhs); bool set_value(long rhs); bool set_value(unsigned long rhs); bool set_value(double rhs); bool set_value(double rhs, int precision); bool set_value(float rhs); bool set_value(float rhs, int precision); bool set_value(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG bool set_value(long long rhs); bool set_value(unsigned long long rhs); #endif // Set attribute value (equivalent to set_value without error checking) xml_attribute& operator=(const char_t* rhs); xml_attribute& operator=(int rhs); xml_attribute& operator=(unsigned int rhs); xml_attribute& operator=(long rhs); xml_attribute& operator=(unsigned long rhs); xml_attribute& operator=(double rhs); xml_attribute& operator=(float rhs); xml_attribute& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG xml_attribute& operator=(long long rhs); xml_attribute& operator=(unsigned long long rhs); #endif // Get next/previous attribute in the attribute list of the parent node xml_attribute next_attribute() const; xml_attribute previous_attribute() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_attribute_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); #endif // A light-weight handle for manipulating nodes in DOM tree class PUGIXML_CLASS xml_node { friend class xml_attribute_iterator; friend class xml_node_iterator; friend class xml_named_node_iterator; protected: xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_node***); public: // Default constructor. Constructs an empty node. xml_node(); // Constructs node from internal pointer explicit xml_node(xml_node_struct* p); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators (compares wrapped node pointers) bool operator==(const xml_node& r) const; bool operator!=(const xml_node& r) const; bool operator<(const xml_node& r) const; bool operator>(const xml_node& r) const; bool operator<=(const xml_node& r) const; bool operator>=(const xml_node& r) const; // Check if node is empty. bool empty() const; // Get node type xml_node_type type() const; // Get node name, or "" if node is empty or it has no name const char_t* name() const; // Get node value, or "" if node is empty or it has no value // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. const char_t* value() const; // Get attribute list xml_attribute first_attribute() const; xml_attribute last_attribute() const; // Get children list xml_node first_child() const; xml_node last_child() const; // Get next/previous sibling in the children list of the parent node xml_node next_sibling() const; xml_node previous_sibling() const; // Get parent node xml_node parent() const; // Get root of DOM tree this node belongs to xml_node root() const; // Get text object for the current node xml_text text() const; // Get child, attribute or next/previous sibling with the specified name xml_node child(const char_t* name) const; xml_attribute attribute(const char_t* name) const; xml_node next_sibling(const char_t* name) const; xml_node previous_sibling(const char_t* name) const; // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) xml_attribute attribute(const char_t* name, xml_attribute& hint) const; // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA const char_t* child_value() const; // Get child value of child with specified name. Equivalent to child(name).child_value(). const char_t* child_value(const char_t* name) const; // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) bool set_name(const char_t* rhs); bool set_name(const char_t* rhs, size_t size); bool set_value(const char_t* rhs); bool set_value(const char_t* rhs, size_t size); // Add attribute with specified name. Returns added attribute, or empty attribute on errors. xml_attribute append_attribute(const char_t* name); xml_attribute prepend_attribute(const char_t* name); xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. xml_attribute append_copy(const xml_attribute& proto); xml_attribute prepend_copy(const xml_attribute& proto); xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); // Add child node with specified type. Returns added node, or empty node on errors. xml_node append_child(xml_node_type type = node_element); xml_node prepend_child(xml_node_type type = node_element); xml_node insert_child_after(xml_node_type type, const xml_node& node); xml_node insert_child_before(xml_node_type type, const xml_node& node); // Add child element with specified name. Returns added node, or empty node on errors. xml_node append_child(const char_t* name); xml_node prepend_child(const char_t* name); xml_node insert_child_after(const char_t* name, const xml_node& node); xml_node insert_child_before(const char_t* name, const xml_node& node); // Add a copy of the specified node as a child. Returns added node, or empty node on errors. xml_node append_copy(const xml_node& proto); xml_node prepend_copy(const xml_node& proto); xml_node insert_copy_after(const xml_node& proto, const xml_node& node); xml_node insert_copy_before(const xml_node& proto, const xml_node& node); // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. xml_node append_move(const xml_node& moved); xml_node prepend_move(const xml_node& moved); xml_node insert_move_after(const xml_node& moved, const xml_node& node); xml_node insert_move_before(const xml_node& moved, const xml_node& node); // Remove specified attribute bool remove_attribute(const xml_attribute& a); bool remove_attribute(const char_t* name); // Remove all attributes bool remove_attributes(); // Remove specified child bool remove_child(const xml_node& n); bool remove_child(const char_t* name); // Remove all children bool remove_children(); // Parses buffer as an XML document fragment and appends all nodes as children of the current node. // Copies/converts the buffer, so it may be deleted or changed after the function returns. // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Find attribute using predicate. Returns first attribute for which predicate returned true. template xml_attribute find_attribute(Predicate pred) const { if (!_root) return xml_attribute(); for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) if (pred(attrib)) return attrib; return xml_attribute(); } // Find child node using predicate. Returns first child for which predicate returned true. template xml_node find_child(Predicate pred) const { if (!_root) return xml_node(); for (xml_node node = first_child(); node; node = node.next_sibling()) if (pred(node)) return node; return xml_node(); } // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. template xml_node find_node(Predicate pred) const { if (!_root) return xml_node(); xml_node cur = first_child(); while (cur._root && cur._root != _root) { if (pred(cur)) return cur; if (cur.first_child()) cur = cur.first_child(); else if (cur.next_sibling()) cur = cur.next_sibling(); else { while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); if (cur._root != _root) cur = cur.next_sibling(); } } return xml_node(); } // Find child node by attribute name/value xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; #ifndef PUGIXML_NO_STL // Get the absolute node path from root as a text string. string_t path(char_t delimiter = '/') const; #endif // Search for a node by path consisting of node names and . or .. elements. xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; // Recursively traverse subtree with xml_tree_walker bool traverse(xml_tree_walker& walker); #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. xpath_node select_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node select_node(const xpath_query& query) const; // Select node set by evaluating XPath query xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node_set select_nodes(const xpath_query& query) const; // (deprecated: use select_node instead) Select single node by evaluating XPath query. PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const; #endif // Print subtree using a writer object void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; #ifndef PUGIXML_NO_STL // Print subtree to stream void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; #endif // Child nodes iterators typedef xml_node_iterator iterator; iterator begin() const; iterator end() const; // Attribute iterators typedef xml_attribute_iterator attribute_iterator; attribute_iterator attributes_begin() const; attribute_iterator attributes_end() const; // Range-based for support xml_object_range children() const; xml_object_range attributes() const; // Range-based for support for all children with the specified name // Note: name pointer must have a longer lifetime than the returned object; be careful with passing temporaries! xml_object_range children(const char_t* name) const; // Get node offset in parsed file/string (in char_t units) for debugging purposes ptrdiff_t offset_debug() const; // Get hash value (unique for handles to the same object) size_t hash_value() const; // Get internal pointer xml_node_struct* internal_object() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); #endif // A helper for working with text inside PCDATA nodes class PUGIXML_CLASS xml_text { friend class xml_node; xml_node_struct* _root; typedef void (*unspecified_bool_type)(xml_text***); explicit xml_text(xml_node_struct* root); xml_node_struct* _data_new(); xml_node_struct* _data() const; public: // Default constructor. Constructs an empty object. xml_text(); // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Check if text object is empty bool empty() const; // Get text, or "" if object is empty const char_t* get() const; // Get text, or the default value if object is empty const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; // Get text as a number, or the default value if conversion did not succeed or object is empty int as_int(int def = 0) const; unsigned int as_uint(unsigned int def = 0) const; double as_double(double def = 0) const; float as_float(float def = 0) const; #ifdef PUGIXML_HAS_LONG_LONG long long as_llong(long long def = 0) const; unsigned long long as_ullong(unsigned long long def = 0) const; #endif // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty bool as_bool(bool def = false) const; // Set text (returns false if object is empty or there is not enough memory) bool set(const char_t* rhs); bool set(const char_t* rhs, size_t size); // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") bool set(int rhs); bool set(unsigned int rhs); bool set(long rhs); bool set(unsigned long rhs); bool set(double rhs); bool set(double rhs, int precision); bool set(float rhs); bool set(float rhs, int precision); bool set(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG bool set(long long rhs); bool set(unsigned long long rhs); #endif // Set text (equivalent to set without error checking) xml_text& operator=(const char_t* rhs); xml_text& operator=(int rhs); xml_text& operator=(unsigned int rhs); xml_text& operator=(long rhs); xml_text& operator=(unsigned long rhs); xml_text& operator=(double rhs); xml_text& operator=(float rhs); xml_text& operator=(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG xml_text& operator=(long long rhs); xml_text& operator=(unsigned long long rhs); #endif // Get the data node (node_pcdata or node_cdata) for this object xml_node data() const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); #endif // Child node iterator (a bidirectional iterator over a collection of xml_node) class PUGIXML_CLASS xml_node_iterator { friend class xml_node; private: mutable xml_node _wrap; xml_node _parent; xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_node_iterator(); // Construct an iterator which points to the specified node xml_node_iterator(const xml_node& node); // Iterator operators bool operator==(const xml_node_iterator& rhs) const; bool operator!=(const xml_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; xml_node_iterator& operator++(); xml_node_iterator operator++(int); xml_node_iterator& operator--(); xml_node_iterator operator--(int); }; // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) class PUGIXML_CLASS xml_attribute_iterator { friend class xml_node; private: mutable xml_attribute _wrap; xml_node _parent; xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_attribute value_type; typedef xml_attribute* pointer; typedef xml_attribute& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_attribute_iterator(); // Construct an iterator which points to the specified attribute xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); // Iterator operators bool operator==(const xml_attribute_iterator& rhs) const; bool operator!=(const xml_attribute_iterator& rhs) const; xml_attribute& operator*() const; xml_attribute* operator->() const; xml_attribute_iterator& operator++(); xml_attribute_iterator operator++(int); xml_attribute_iterator& operator--(); xml_attribute_iterator operator--(int); }; // Named node range helper class PUGIXML_CLASS xml_named_node_iterator { friend class xml_node; public: // Iterator traits typedef ptrdiff_t difference_type; typedef xml_node value_type; typedef xml_node* pointer; typedef xml_node& reference; #ifndef PUGIXML_NO_STL typedef std::bidirectional_iterator_tag iterator_category; #endif // Default constructor xml_named_node_iterator(); // Construct an iterator which points to the specified node // Note: name pointer is stored in the iterator and must have a longer lifetime than iterator itself xml_named_node_iterator(const xml_node& node, const char_t* name); // Iterator operators bool operator==(const xml_named_node_iterator& rhs) const; bool operator!=(const xml_named_node_iterator& rhs) const; xml_node& operator*() const; xml_node* operator->() const; xml_named_node_iterator& operator++(); xml_named_node_iterator operator++(int); xml_named_node_iterator& operator--(); xml_named_node_iterator operator--(int); private: mutable xml_node _wrap; xml_node _parent; const char_t* _name; xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); }; // Abstract tree walker class (see xml_node::traverse) class PUGIXML_CLASS xml_tree_walker { friend class xml_node; private: int _depth; protected: // Get current traversal depth int depth() const; public: xml_tree_walker(); virtual ~xml_tree_walker(); // Callback that is called when traversal begins virtual bool begin(xml_node& node); // Callback that is called for each node traversed virtual bool for_each(xml_node& node) = 0; // Callback that is called when traversal ends virtual bool end(xml_node& node); }; // Parsing status, returned as part of xml_parse_result object enum xml_parse_status { status_ok = 0, // No error status_file_not_found, // File was not found during load_file() status_io_error, // Error reading from file/stream status_out_of_memory, // Could not allocate memory status_internal_error, // Internal error occurred status_unrecognized_tag, // Parser could not determine tag type status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction status_bad_comment, // Parsing error occurred while parsing comment status_bad_cdata, // Parsing error occurred while parsing CDATA section status_bad_doctype, // Parsing error occurred while parsing document type declaration status_bad_pcdata, // Parsing error occurred while parsing PCDATA section status_bad_start_element, // Parsing error occurred while parsing start element tag status_bad_attribute, // Parsing error occurred while parsing element attribute status_bad_end_element, // Parsing error occurred while parsing end element tag status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) status_no_document_element // Parsing resulted in a document without element nodes }; // Parsing result struct PUGIXML_CLASS xml_parse_result { // Parsing status (see xml_parse_status) xml_parse_status status; // Last parsed offset (in char_t units from start of input data) ptrdiff_t offset; // Source document encoding xml_encoding encoding; // Default constructor, initializes object to failed state xml_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // Document class (DOM tree root) class PUGIXML_CLASS xml_document: public xml_node { private: char_t* _buffer; char _memory[192]; // Non-copyable semantics xml_document(const xml_document&); xml_document& operator=(const xml_document&); void _create(); void _destroy(); void _move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; public: // Default constructor, makes empty document xml_document(); // Destructor, invalidates all node/attribute handles to this document ~xml_document(); #ifdef PUGIXML_HAS_MOVE // Move semantics support xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; xml_document& operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; #endif // Removes all nodes, leaving the empty document void reset(); // Removes all nodes, then copies the entire contents of the specified document void reset(const xml_document& proto); #ifndef PUGIXML_NO_STL // Load document from stream. xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); #endif // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. PUGIXML_DEPRECATED xml_parse_result load(const char_t* contents, unsigned int options = parse_default); // Load document from zero-terminated string. No encoding conversions are applied. xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); // Load document from file xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; #ifndef PUGIXML_NO_STL // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; #endif // Save XML to file bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; // Get document element xml_node document_element() const; }; #ifndef PUGIXML_NO_XPATH // XPath query return type enum xpath_value_type { xpath_type_none, // Unknown type (query failed to compile) xpath_type_node_set, // Node set (xpath_node_set) xpath_type_number, // Number xpath_type_string, // String xpath_type_boolean // Boolean }; // XPath parsing result struct PUGIXML_CLASS xpath_parse_result { // Error message (0 if no error) const char* error; // Last parsed offset (in char_t units from string start) ptrdiff_t offset; // Default constructor, initializes object to failed state xpath_parse_result(); // Cast to bool operator operator bool() const; // Get error description const char* description() const; }; // A single XPath variable class PUGIXML_CLASS xpath_variable { friend class xpath_variable_set; protected: xpath_value_type _type; xpath_variable* _next; xpath_variable(xpath_value_type type); // Non-copyable semantics xpath_variable(const xpath_variable&); xpath_variable& operator=(const xpath_variable&); public: // Get variable name const char_t* name() const; // Get variable type xpath_value_type type() const; // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error bool get_boolean() const; double get_number() const; const char_t* get_string() const; const xpath_node_set& get_node_set() const; // Set variable value; no type conversion is performed, false is returned on type mismatch error bool set(bool value); bool set(double value); bool set(const char_t* value); bool set(const xpath_node_set& value); }; // A set of XPath variables class PUGIXML_CLASS xpath_variable_set { private: xpath_variable* _data[64]; void _assign(const xpath_variable_set& rhs); void _swap(xpath_variable_set& rhs); xpath_variable* _find(const char_t* name) const; static bool _clone(xpath_variable* var, xpath_variable** out_result); static void _destroy(xpath_variable* var); public: // Default constructor/destructor xpath_variable_set(); ~xpath_variable_set(); // Copy constructor/assignment operator xpath_variable_set(const xpath_variable_set& rhs); xpath_variable_set& operator=(const xpath_variable_set& rhs); #ifdef PUGIXML_HAS_MOVE // Move semantics support xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; xpath_variable_set& operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; #endif // Add a new variable or get the existing one, if the types match xpath_variable* add(const char_t* name, xpath_value_type type); // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch bool set(const char_t* name, bool value); bool set(const char_t* name, double value); bool set(const char_t* name, const char_t* value); bool set(const char_t* name, const xpath_node_set& value); // Get existing variable by name xpath_variable* get(const char_t* name); const xpath_variable* get(const char_t* name) const; }; // A compiled XPath query object class PUGIXML_CLASS xpath_query { private: void* _impl; xpath_parse_result _result; typedef void (*unspecified_bool_type)(xpath_query***); // Non-copyable semantics xpath_query(const xpath_query&); xpath_query& operator=(const xpath_query&); public: // Construct a compiled object from XPath expression. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. explicit xpath_query(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL); // Constructor xpath_query(); // Destructor ~xpath_query(); #ifdef PUGIXML_HAS_MOVE // Move semantics support xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT; xpath_query& operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT; #endif // Get query expression return type xpath_value_type return_type() const; // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. bool evaluate_boolean(const xpath_node& n) const; // Evaluate expression as double value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. double evaluate_number(const xpath_node& n) const; #ifndef PUGIXML_NO_STL // Evaluate expression as string value in the specified context; performs type conversion if necessary. // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. string_t evaluate_string(const xpath_node& n) const; #endif // Evaluate expression as string value in the specified context; performs type conversion if necessary. // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; // Evaluate expression as node set in the specified context. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. xpath_node_set evaluate_node_set(const xpath_node& n) const; // Evaluate expression as node set in the specified context. // Return first node in document order, or empty node if node set is empty. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. xpath_node evaluate_node(const xpath_node& n) const; // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) const xpath_parse_result& result() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; }; #ifndef PUGIXML_NO_EXCEPTIONS #if defined(_MSC_VER) // C4275 can be ignored in Visual C++ if you are deriving // from a type in the Standard C++ Library #pragma warning(push) #pragma warning(disable: 4275) #endif // XPath exception class class PUGIXML_CLASS xpath_exception: public std::exception { private: xpath_parse_result _result; public: // Construct exception from parse result explicit xpath_exception(const xpath_parse_result& result); // Get error message virtual const char* what() const throw() PUGIXML_OVERRIDE; // Get parse result const xpath_parse_result& result() const; }; #if defined(_MSC_VER) #pragma warning(pop) #endif #endif // XPath node class (either xml_node or xml_attribute) class PUGIXML_CLASS xpath_node { private: xml_node _node; xml_attribute _attribute; typedef void (*unspecified_bool_type)(xpath_node***); public: // Default constructor; constructs empty XPath node xpath_node(); // Construct XPath node from XML node/attribute xpath_node(const xml_node& node); xpath_node(const xml_attribute& attribute, const xml_node& parent); // Get node/attribute, if any xml_node node() const; xml_attribute attribute() const; // Get parent of contained node/attribute xml_node parent() const; // Safe bool conversion operator operator unspecified_bool_type() const; // Borland C++ workaround bool operator!() const; // Comparison operators bool operator==(const xpath_node& n) const; bool operator!=(const xpath_node& n) const; }; #ifdef __BORLANDC__ // Borland C++ workaround bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); #endif // A fixed-size collection of XPath nodes class PUGIXML_CLASS xpath_node_set { public: // Collection type enum type_t { type_unsorted, // Not ordered type_sorted, // Sorted by document order (ascending) type_sorted_reverse // Sorted by document order (descending) }; // Constant iterator type typedef const xpath_node* const_iterator; // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work typedef const xpath_node* iterator; // Default constructor. Constructs empty set. xpath_node_set(); // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); // Destructor ~xpath_node_set(); // Copy constructor/assignment operator xpath_node_set(const xpath_node_set& ns); xpath_node_set& operator=(const xpath_node_set& ns); #ifdef PUGIXML_HAS_MOVE // Move semantics support xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; xpath_node_set& operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; #endif // Get collection type type_t type() const; // Get collection size size_t size() const; // Indexing operator const xpath_node& operator[](size_t index) const; // Collection iterators const_iterator begin() const; const_iterator end() const; // Sort the collection in ascending/descending order by document order void sort(bool reverse = false); // Get first node in the collection by document order xpath_node first() const; // Check if collection is empty bool empty() const; private: type_t _type; xpath_node _storage[1]; xpath_node* _begin; xpath_node* _end; void _assign(const_iterator begin, const_iterator end, type_t type); void _move(xpath_node_set& rhs) PUGIXML_NOEXCEPT; }; #endif #ifndef PUGIXML_NO_STL // Convert wide string to UTF8 std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); // Convert UTF8 to wide string std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); #endif // Memory allocation function interface; returns pointer to allocated memory or NULL on failure typedef void* (*allocation_function)(size_t size); // Memory deallocation function interface typedef void (*deallocation_function)(void* ptr); // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); // Get current memory management functions allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); } #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) namespace std { // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); } #endif #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) namespace std { // Workarounds for (non-standard) iterator category detection std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); } #endif #endif // Make sure implementation is included in header-only mode // Use macro expansion in #include to work around QMake (QTBUG-11923) #if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) # define PUGIXML_SOURCE "pugixml.cpp" # include PUGIXML_SOURCE #endif /** * Copyright (c) 2006-2023 Arseny Kapoulkine * * 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. */ tea-qt-63.3.0/src/shortcuts.cpp000066400000000000000000000130231476733534200163440ustar00rootroot00000000000000/*************************************************************************** * Copyleft 2007-2019 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include "shortcuts.h" #include "utils.h" //#include "gui_utils.h" QString mod_to_string (Qt::KeyboardModifiers k) { QString s; if (k & Qt::ShiftModifier) s += "Shift+"; if (k & Qt::ControlModifier) s += "Ctrl+"; if (k & Qt::AltModifier) s += "Alt+"; if (k & Qt::MetaModifier) s+= "Meta+"; return s; } QString keycode_to_string (int k) { // return QKeySequence(k).toString(QKeySequence::NativeText); QString s; switch (k) { case Qt::Key_F1: s = "F1"; break; case Qt::Key_F2: s = "F2"; break; case Qt::Key_F3: s = "F3"; break; case Qt::Key_F4: s = "F4"; break; case Qt::Key_F5: s = "F5"; break; case Qt::Key_F6: s = "F6"; break; case Qt::Key_F7: s = "F7"; break; case Qt::Key_F8: s = "F8"; break; case Qt::Key_F9: s = "F9"; break; case Qt::Key_F10: s = "F10"; break; case Qt::Key_F11: s = "F11"; break; case Qt::Key_F12: s = "F12"; break; default: s = QChar (k); } return s; } CShortcuts::CShortcuts (QWidget *widget) { w = widget; } //FIXME void CShortcuts::captions_iterate() { captions.clear(); QList a = w->findChildren (); for (int i = 0; i < a.size(); i++) if (a.at(i)) if (! a.at(i)->text().isEmpty()) captions.prepend (a.at(i)->text()); captions.sort(); captions.removeDuplicates(); //nasty hack } QAction* CShortcuts::find_by_caption (const QString &text) { QList a = w->findChildren(); for (int i = 0; i < a.size(); i++) if (a.at(i)->text() == text) return a.at(i); return 0; } QAction* CShortcuts::find_by_shortcut (const QString &shcut) { QList a = w->findChildren(); for (int i = 0; i < a.size(); i++) if (a.at(i)->shortcut().toString() == shcut) return a.at(i); return 0; } QKeySequence CShortcuts::find_seq_by_caption (const QString &text) { QAction *a = find_by_caption (text); if (a) return a->shortcut(); return QKeySequence::fromString ("Ctrl+Alt+Z"); } void CShortcuts::set_new_shortcut (const QString &menuitem, const QString &shcut) { QAction *b = find_by_shortcut (shcut); if (b) b->setShortcut (QKeySequence("")); QAction *a = find_by_caption (menuitem); if (a) a->setShortcut (QKeySequence (shcut)); } void CShortcuts::save_to_file (const QString &file_name) { QList a = w->findChildren(); QString s; for (int i = 0; i < a.size(); i++) if (! a.at(i)->shortcut().toString().isEmpty()) s.append (a.at(i)->text()).append ("=").append (a.at(i)->shortcut().toString()).append ("\n"); qstring_save (file_name, s); } void CShortcuts::load_from_file (const QString &file_name) { if (! file_exists (file_name)) return; QHash hash = hash_load_keyval (file_name); QList a = w->findChildren(); for (int i = 0; i < a.size(); i++) { QAction *at = a.at(i); if (hash.contains (at->text())) at->setShortcut (QKeySequence (hash.value (at->text()))); } } void CShortcutEntry::keyPressEvent (QKeyEvent *event) { event->accept(); QString text = mod_to_string (event->modifiers()) + keycode_to_string (event->key()); setText (text); } tea-qt-63.3.0/src/shortcuts.h000066400000000000000000000042561476733534200160210ustar00rootroot00000000000000/*************************************************************************** * 2007-2021 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SHORTCUTS_H #define SHORTCUTS_H #include #include #include class CShortcutEntry: public QLineEdit { Q_OBJECT public: void keyPressEvent (QKeyEvent *event); }; class CShortcuts: public QObject { Q_OBJECT public: QWidget *w; QString fname; QHash hash; QStringList captions; CShortcuts (QWidget *widget); void captions_iterate(); QAction* find_by_caption (const QString &text); QAction* find_by_shortcut (const QString &shcut); QKeySequence find_seq_by_caption (const QString &text); void set_new_shortcut (const QString &menuitem, const QString &shcut); void save_to_file (const QString &file_name); void load_from_file (const QString &file_name); }; #endif tea-qt-63.3.0/src/single_application_shared.cpp000066400000000000000000000042541476733534200215060ustar00rootroot00000000000000// "single_application.cpp" /* taken from http://berenger.eu/blog/c-qt-singleapplication-single-app-instance/ code by Berenger Bramas modified by Peter Semiletov */ #include #include #include #include "single_application_shared.h" CSingleApplicationShared::CSingleApplicationShared (int &argc, char *argv[], const QString &uniqueKey): QApplication (argc, argv) { sharedMemory.setKey (uniqueKey); // when can create it only if it doesn't exist if (sharedMemory.create (8192)) { sharedMemory.lock(); *(char*)sharedMemory.data() = '\0'; sharedMemory.unlock(); bAlreadyExists = false; // start checking for messages of other instances. QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(checkForMessage())); timer->start (200); } // it exits, so we can attach it?! else if (sharedMemory.attach()) bAlreadyExists = true; } // public slots void CSingleApplicationShared::checkForMessage() { QStringList arguments; sharedMemory.lock(); char *from = (char*)sharedMemory.data(); while (*from != '\0') { int sizeToRead = int(*from); ++from; QByteArray byteArray = QByteArray (from, sizeToRead); byteArray[sizeToRead] = '\0'; from += sizeToRead; arguments << QString::fromUtf8 (byteArray.constData()); } *(char*)sharedMemory.data() = '\0'; sharedMemory.unlock(); if (arguments.size()) emit messageAvailable (arguments); } bool CSingleApplicationShared::sendMessage (const QString &message) { //we cannot send message if we are master process! if (isMasterApp()) return false; QByteArray byteArray; byteArray.append (char(message.size())); byteArray.append (message.toUtf8()); byteArray.append ('\0'); sharedMemory.lock(); char *to = (char*)sharedMemory.data(); while (*to != '\0') { int sizeToRead = int(*to); to += sizeToRead + 1; } const char *from = byteArray.data(); memcpy (to, from, qMin(sharedMemory.size(), byteArray.size())); sharedMemory.unlock(); return true; } tea-qt-63.3.0/src/single_application_shared.h000066400000000000000000000020651476733534200211510ustar00rootroot00000000000000/* * taken from http://berenger.eu/blog/c-qt-singleapplication-single-app-instance/ * code by Berenger Bramas * modified by Peter Semiletov * */ #ifndef SINGLE_APP_SHARED_H #define SINGLE_APP_SHARED_H #include #include #include class CSingleApplicationShared: public QApplication { Q_OBJECT private: QSharedMemory sharedMemory; bool bAlreadyExists; public: CSingleApplicationShared (int &argc, char *argv[], const QString &uniqueKey); ~CSingleApplicationShared() {/*qDebug() << "~CSingleApplicationShared";*/}; static int cursorFlashTime() { return 0; } bool alreadyExists() const { return bAlreadyExists; } bool isMasterApp() const { return ! alreadyExists(); } bool sendMessage (const QString &message); public slots: void checkForMessage(); signals: void messageAvailable(const QStringList &messages); void signal_commit_data(); }; #endif // SINGLE_APP_SHARED_H tea-qt-63.3.0/src/speech.cpp000066400000000000000000000133451476733534200155640ustar00rootroot00000000000000#ifdef SPEECH_ENABLE #include #include "speech.h" int g_state; int g_position; void f_signal_handler (int signal) { std::cout << "f_signal_handler " << std::endl; g_signal = signal; // shutdown (sockfd, 2); //close (sockfd); std::cout << "Exiting by the signal" << std::endl; } /* Callback for Speech Dispatcher notifications */ void cbk_end_of_speech (size_t msg_id, size_t client_id, SPDNotificationType type) { //if (type == SPD_EVENT_END) // g_position++; // sem_post(&g_semaphore); if (type == SPD_EVENT_END) g_state = SPCH_STATE_STOPPED; } void cbk_cancel_of_speech (size_t msg_id, size_t client_id, SPDNotificationType type) { // addstr("*cbk_cancel_of_speech*"); if (type == SPD_EVENT_CANCEL) { // addstr("*SPD_EVENT_CANCEL*"); } } CSpeech::CSpeech() { initialized = false; spd_connection = 0; locale_only = 0; current_voice_index = -1; //g_state = SPCH_STATE_STOPPED; g_state = SPCH_STATE_STOPPED; char* locval = setlocale(LC_ALL, ""); std::string temp_locale; if (locval) { temp_locale = locval; locale_name = temp_locale.substr (0, 2); } else locale_name = "en"; // std::cout << "LOC: " << locale_name << std::endl; //sem_init (&g_semaphore, 0, 0); } CSpeech::~CSpeech() { // if (initialized) if (spd_connection) spd_close (spd_connection); } void CSpeech::done() { //if (initialized) if (spd_connection) { spd_close (spd_connection); spd_connection = 0; initialized = false; //sem_close (&g_semaphore); //sem_destroy (&g_semaphore); } } void CSpeech::init (const char* client_name) { spd_connection = spd_open (client_name, "main", NULL, //username SPD_MODE_THREADED); if (spd_connection) { initialized = true; spd_set_language (spd_connection, locale_name.c_str()); spd_connection->callback_end = cbk_end_of_speech; // spd_connection->callback_cancel = cbk_cancel_of_speech; spd_set_notification_on(spd_connection, SPD_END); char *s = NULL; s = spd_get_output_module (spd_connection); if (s) { output_module_name = s; free (s); } s = spd_get_language (spd_connection); if (s) { language_name = s; free (s); } // spd_set_language(spd_connection, "ru"); // get_voices(); current_voice_index = 0; } } void CSpeech::say (const char* text) { if (! initialized) return; g_state = SPCH_STATE_SAYING; int result = spd_say (spd_connection, SPD_TEXT, text); if (result == -1) std::cout << "say error!" << std::endl; //А ЕСЛИ НЕ ЖДАТЬ? //sem_wait (&g_semaphore); } void CSpeech::stop() { if (! initialized) return; spd_stop (spd_connection); g_position = 0; g_state = SPCH_STATE_STOPPED; } void CSpeech::pause() { if (! initialized) return; if (spd_pause (spd_connection) != -1) g_state = SPCH_STATE_PAUSED; } void CSpeech::play() { if (! initialized) return; g_state = SPCH_STATE_SAYING; g_position = 0; } void CSpeech::resume() { if (! initialized) return; if (! g_state == SPCH_STATE_PAUSED) return; if (spd_resume (spd_connection) != -1) g_state = SPCH_STATE_SAYING; } void CSpeech::cancel() { if (! initialized) return; spd_cancel (spd_connection); // spd_stop (spd_connection); g_state = SPCH_STATE_STOPPED; } void CSpeech::get_voices (int locale_only) { // char **voices_array = spd_list_synthesis_voices2 (spd_connection, // setlocale(LC_ALL, NULL), // if (! initialized) return; initialized = false; voices.clear(); char **voices_array = (char**)spd_list_synthesis_voices (spd_connection); //for > 0.15 API //char **voices_array = (char**)spd_list_synthesis_voices2 (spd_connection, "ru", NULL); if (voices_array == NULL) return; std::string lang_name_short; //SPDVoice **voice = arr_voices; int i = 0; while (voices_array[i] != NULL) { SPDVoice* voice = (SPDVoice*)voices_array[i]; // Приведение типа к SPDVoice* if (locale_only == 1) { std::string voice_name = voice->name; lang_name_short = voice->language; lang_name_short = lang_name_short.substr(0, 2); if (lang_name_short == language_name) voices.push_back (voice_name); } else { std::string voice_name = voice->name; voices.push_back (voice_name); } current_voice_index = 0; // Вывод информации о голосе /* printf("Voice %d:\n", i + 1); printf("Name: %s\n", voice->name); printf("Language: %s\n", voice->language); printf("Variant: %s\n", voice->variant); printf("\n"); */ ++i; } //std::cout << "voices count: " << voices.size() << std::endl; free_spd_voices((SPDVoice**)voices_array); if (voices.size() > 0) initialized = true; } void CSpeech::set_voice_by_index (int index) { if (index == -1 || ! initialized) return; if (index > voices.size() - 1) return; // if (spd_set_synthesis_voice (spd_connection, voices[index].name.c_str())) // std::cout << "ERRRRR" << std::endl; if (spd_set_synthesis_voice (spd_connection, voices[index].c_str())) std::cout << "ERRRRR" << std::endl; // std::cout << "spd_set_synthesis_voice: " << voices[index].name << std::endl; } // int spd_set_synthesis_voice(SPDConnection* connection, const char* voice_name); #endiftea-qt-63.3.0/src/speech.h000066400000000000000000000026611476733534200152300ustar00rootroot00000000000000#ifndef SPEECH_H #define SPEECH_H #ifdef SPEECH_ENABLE /* usr/include/speech-dispatcher/libspeechd.h usr/include/speech-dispatcher/libspeechd_version.h usr/include/speech-dispatcher/spd_audio_plugin.h usr/include/speech-dispatcher/speechd_defines.h usr/include/speech-dispatcher/speechd_types.h */ #include #include #include #include #include #define SPCH_STATE_STOPPED 0 #define SPCH_STATE_SAYING 1 #define SPCH_STATE_PAUSED 2 #define SPCH_STATE_NEXT 3 /* class CVoice { public: std::string name; std::string language; }; */ class CSpeech { public: int locale_only; //int state; SPDConnection *spd_connection; std::vector voices; //module-specific // std::vector voices; //module-specific std::string locale_name; bool initialized; std::string output_module_name; std::string language_name; int current_voice_index; CSpeech(); ~CSpeech(); void init (const char* client_name); void say (const char* text); void done(); void stop(); void pause(); void play(); void resume(); void cancel(); void get_voices (int locale_only = 1); //fills voices vector void set_voice_by_index (int index); }; //sig_atomic_t g_signal; // namespace { // volatile std::sig_atomic_t g_signal; volatile sig_atomic_t g_signal; // volatile int sockfd; } void f_signal_handler (int signal); #endif #endif tea-qt-63.3.0/src/spellchecker.cpp000066400000000000000000000367401476733534200167650ustar00rootroot00000000000000/*************************************************************************** * 2007-2024 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifdef HUNSPELL_ENABLE #ifdef H_DEPRECATED #include #include #endif #include #endif #ifdef NUSPELL_ENABLE #include #include #include #endif #include #include #include #include "spellchecker.h" #include "utils.h" #include "enc.h" #ifdef ASPELL_ENABLE QString aspell_default_dict_path() { QString r; r = "/usr/lib/aspell-0.60"; #if defined(Q_OS_OS2) r = qEnvironmentVariable ("unixroot", "c:") + "\\usr\\lib\\aspell-0.60"; #endif #if defined(Q_OS_WIN) r = "C:\\Program Files\\Aspell"; #endif return r; } void CAspellchecker::load_dict() { loaded = false; if (! spell_config) return; if (speller) { delete_aspell_speller (speller); speller = 0; } aspell_config_replace (spell_config, "lang", language.toUtf8().data()); aspell_config_replace (spell_config, "encoding", "UTF-8"); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) QString data_path = dir_dicts + "\\data"; QString dict_path = dir_dicts + "\\dict"; data_path = data_path.replace ("/", "\\"); dict_path = dict_path.replace ("/", "\\"); aspell_config_replace (spell_config, "data-dir", data_path.toLatin1().data()); aspell_config_replace (spell_config, "dict-dir", dict_path.toLatin1().data()); #endif ret = new_aspell_speller (spell_config); if (! ret) return; if (aspell_error (ret) != 0) { //g_print ("Error: %s\n", aspell_error_message (ret)); delete_aspell_can_have_error (ret); return; } speller = to_aspell_speller (ret); loaded = true; } CAspellchecker::CAspellchecker (const QString &lang, const QString &path, const QString &user_path): CSpellchecker (lang, path, user_path) { ret = 0; speller = 0; spell_config = 0; loaded = false; spell_config = new_aspell_config(); if (! spell_config) return; } CAspellchecker::~CAspellchecker() { if (speller) delete_aspell_speller (speller); if (spell_config) delete_aspell_config (spell_config); } void CAspellchecker::change_lang (const QString &lang) { if (lang.isEmpty() || language == lang) return; language = lang; } void CAspellchecker::get_speller_modules_list() { modules_list.clear(); if (! spell_config) return; AspellDictInfoList *dlist; AspellDictInfoEnumeration *dels; const AspellDictInfo *entry; dlist = get_aspell_dict_info_list (spell_config); dels = aspell_dict_info_list_elements (dlist); while ((entry = aspell_dict_info_enumeration_next (dels)) != 0) { // if (entry) modules_list.prepend (entry->name); } delete_aspell_dict_info_enumeration (dels); } bool CAspellchecker::check (const QString &word) { if (modules_list.size() == 0) { //QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page")); return false; } if (word.isEmpty()) return false; if (! loaded) load_dict(); if (! loaded) false; //still not loaded if (user_dict_checker.check (word)) return true; if (speller) return aspell_speller_check (speller, word.toUtf8().data(), -1); else return false; } QStringList CAspellchecker::get_suggestions_list (const QString &word) { QStringList l; if (! loaded || word.isEmpty() || ! speller) return l; const AspellWordList *suggestions = aspell_speller_suggest (speller, word.toUtf8().data(), -1); if (! suggestions) return l; AspellStringEnumeration *elements = aspell_word_list_elements (suggestions); const char *wrd; while ((wrd = aspell_string_enumeration_next (elements))) l.prepend (QString::fromUtf8 (wrd)); delete_aspell_string_enumeration (elements); return l; } void CAspellchecker::add_to_user_dict (const QString &word) { if (! loaded || ! speller) return; if (word.isEmpty()) return; user_dict_checker.add_word (word); } void CAspellchecker::remove_from_user_dict (const QString &word) { if (word.isEmpty()) return; user_dict_checker.remove_word (word); } #endif /****************** HUNSPELL **********************************/ #ifdef HUNSPELL_ENABLE void CHunspellChecker::add_to_user_dict (const QString &word) { if (! loaded || ! speller) return; if (word.isEmpty()) return; user_dict_checker.add_word (word); } void CHunspellChecker::remove_from_user_dict (const QString &word) { if (word.isEmpty()) return; user_dict_checker.remove_word (word); } void CHunspellChecker::load_dict() { loaded = false; if (! dir_exists (dir_dicts) || language.isEmpty()) return; if (speller) delete speller; #if defined(Q_OS_WIN) || defined(Q_OS_OS2) QString fname_aff = dir_dicts + QDir::separator() + language + ".aff"; QString fname_dict = dir_dicts + QDir::separator() + language + ".dic"; fname_aff = fname_aff.replace ("/", "\\"); fname_dict = fname_dict.replace ("/", "\\"); #else QString fname_aff = dir_dicts + QDir::separator() + language + ".aff"; QString fname_dict = dir_dicts + QDir::separator() + language + ".dic"; #endif //need for Unicode path #if defined(Q_OS_WIN) fname_aff = "\\\\?\\" + fname_aff; fname_dict = "\\\\?\\" + fname_dict; #endif speller = new Hunspell (fname_aff.toUtf8().data(), fname_dict.toUtf8().data()); #if !defined (H_DEPRECATED) encoding = speller->get_dic_encoding(); #else str_encoding = speller->get_dict_encoding(); // std::cout << "str_encoding : " << str_encoding << std::endl; //qDebug() << "str_encoding : " << str_encoding; #endif loaded = true; } CHunspellChecker::CHunspellChecker (const QString &lang, const QString &dir_path, const QString &dir_user): CSpellchecker (lang, dir_path, dir_user) { speller = 0; encoding = 0; loaded = false; } CHunspellChecker::~CHunspellChecker() { delete speller; } void CHunspellChecker::change_lang (const QString &lang) { if (language == lang) return; language = lang; } /* void CHunspellChecker::add_to_user_dict (const QString &word) { if (! loaded || word.isEmpty()) return; QByteArray es = word.toUtf8(); speller->add (es.data()); user_words.append (word); save_user_dict(); } */ bool CHunspellChecker::check (const QString &word) { if (modules_list.size() == 0) { // QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page")); return false; } if (word.isEmpty()) return false; if (! loaded) load_dict(); if (! loaded) { qDebug() << "still not loaded!"; return false; } if (user_dict_checker.check (word)) return true; QByteArray es = word.toUtf8(); #ifndef H_DEPRECATED return speller->spell (es.constData()); //return speller->spell (es.data()); //old way #else return speller->spell (QString(es).toStdString()); // return speller->spell (word.toStdString()); #endif } void CHunspellChecker::get_speller_modules_list() { modules_list.clear(); QDir dir (dir_dicts); if (! dir.exists()) return; QStringList filters; filters << "*.dic"; dir.setSorting (QDir::Name); QFileInfoList fil = dir.entryInfoList (filters); for (int i = 0; i < fil.size(); i++) { modules_list.append (fil[i].baseName()); } } QStringList CHunspellChecker::get_suggestions_list (const QString &word) { QStringList sl; if (! loaded || word.isEmpty()) return sl; QByteArray es = word.toUtf8(); #ifndef H_DEPRECATED char **slst; int size = speller->suggest (&slst, es.data()); for (int i = 0; i < size; i++) sl.append (QString::fromUtf8 (slst[i])); speller->free_list (&slst, size); #else std::vector suglist = speller->suggest (QString(es).toStdString()); sl.reserve (suglist.size()); for (size_t i = 0, sz = suglist.size(); i < sz; ++i) sl.append (QString::fromStdString (suglist[i])); #endif return sl; } QString hunspell_default_dict_path() { QString r; r = "/usr/share/hunspell"; #if defined(Q_OS_OS2) r = qEnvironmentVariable ("unixroot", "c:") + "\\usr\\share\\myspell"; #endif #if defined(Q_OS_WIN) #endif return r; } #endif #ifdef NUSPELL_ENABLE void CNuspellChecker::add_to_user_dict (const QString &word) { if (! loaded || ! speller) return; if (word.isEmpty()) return; user_dict_checker.add_word (word); } void CNuspellChecker::remove_from_user_dict (const QString &word) { if (word.isEmpty()) return; user_dict_checker.remove_word (word); } void CNuspellChecker::load_dict() { loaded = false; //qDebug() << "true CNuspellChecker::load_dict"; dict_path = nuspell::search_dirs_for_one_dict (dirs, language.toStdString()); //qDebug() << "dict_path: " << dict_path.string(); if (dict_path.empty() ) { qDebug() << "dict_path.empty()"; return; // Return error because we can not find the requested // dictionary. } speller = new nuspell::Dictionary; try { speller->load_aff_dic(dict_path); } catch (const nuspell::Dictionary_Loading_Error& e) { std::cout << e.what() << '\n'; return; } loaded = true; } CNuspellChecker::CNuspellChecker (const QString &lang, const QString &dir_path, const QString &dir_user): CSpellchecker (lang, dir_path, dir_user) { // qDebug() << "CNuspellChecker::CNuspellChecker "; loaded = false; speller = 0; nuspell::append_default_dir_paths (dirs); nuspell::append_libreoffice_dir_paths (dirs); //qDebug << "dirs.size: " << dirs.size(); //auto dict_list = nuspell::search_default_dirs_for_dicts(); } CNuspellChecker::~CNuspellChecker() { delete speller; } void CNuspellChecker::change_lang (const QString &lang) { if (language.isEmpty() || language == lang) return; language = lang; } bool CNuspellChecker::check (const QString &word) { if (modules_list.size() == 0) return false; if (! loaded) load_dict(); if (! loaded) //still not loaded! return false; if (user_dict_checker.check (word)) return true; return speller->spell (word.toStdString()); } void CNuspellChecker::get_speller_modules_list() { modules_list.clear(); auto dict_list = nuspell::search_default_dirs_for_dicts(); for (int i = 0; i < dict_list.size(); i++) { QString dictname = QString::fromStdString (dict_list[i].stem().string()); // qDebug() << "CNuspellChecker::get_speller_modules_list() - " << dictname; modules_list.append (dictname); } } QStringList CNuspellChecker::get_suggestions_list (const QString &word) { QStringList sl; if (! loaded || word.isEmpty()) return sl; // QByteArray es = word.toUtf8(); auto suggestions = std::vector(); speller->suggest(word.toStdString(), suggestions); if (suggestions.empty()) return sl; sl.reserve (suggestions.size()); for (size_t i = 0, sz = suggestions.size(); i < sz; ++i) sl.append (QString::fromStdString (suggestions[i])); return sl; } #endif void CPlainSpellchecker::add_word (const QString &word) { //qDebug() << " CPlainSpellchecker::add_word: " << word; if (word.isEmpty()) return; UTF16TEXT key = word.at(0).unicode(); map[key].append (word); //qDebug() << "map.size() " << map.size(); //qDebug() << "map[key].size() " << map[key].size(); } void CPlainSpellchecker::remove_word (const QString &word) { if (word.isEmpty()) return; UTF16TEXT key = word.at(0).unicode(); int i = map[key].indexOf (word); if (i != -1) map[key].removeAt (i); } bool CPlainSpellchecker::check (const QString &word) { UTF16TEXT key = word[0].unicode(); return map.value(key).contains (word, Qt::CaseInsensitive); } void CPlainSpellchecker::load_from_file (const QString &fname) { if (fname.isEmpty()) return; if (! file_exists (fname)) return; user_dict_filename = fname; QString text = qstring_load (fname); QStringList sl = text.split ("\n"); for (int i = 0; i < sl.size(); i++) { QString s = sl.at(i); if (s.isEmpty() || s == "\n") continue; UTF16TEXT key = s[0].unicode(); map[key].append (s); } // std::cout << "map.size() " << map.size() << std::endl; } void CPlainSpellchecker::save_to_file (const QString &fname) { QString text; /* for (QStringList value : std::as_const (map)) { if (value.size() > 0) { for (int i = 0; i < value.size(); i++) { QString t = value.at(i); if (! t.isEmpty()) { text += value.at(i); text += "\n"; qDebug () << value.at (i); } } } } */ /* QList values = map.values(); for (int i = 0; i < values.size(); i++) { qDebug() << "i:" << i; if (values.at(i).size() > 0) { for (int j = 0; j < values.at(j).size(); j++) { QString t = values.at(i).at(j); if (! t.isEmpty()) { text += t; text += "\n"; qDebug () << "at j:" << j << " t:" << t; } } } } qstring_save (user_dict_filename, text);*/ QList values = map.values(); for (int i = 0; i < values.size(); i++) { // qDebug() << "i:" << i; QStringList sl_char = values.at(i); //qDebug() << "sl_char.size():" << sl_char.size(); if (sl_char.size() == 0) continue; for (int j = 0; j < sl_char.size(); j++) { QString t = sl_char.at(j); // qDebug () << "at j:" << j << " t:" << t; if (! t.isEmpty()) { text += t; text += "\n"; } } } qstring_save (user_dict_filename, text); } tea-qt-63.3.0/src/spellchecker.h000066400000000000000000000141341476733534200164230ustar00rootroot00000000000000/*************************************************************************** * 2007-2021 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SPELLCHECKER_H #define SPELLCHECKER_H #ifdef ASPELL_ENABLE #include "aspell.h" #endif #ifdef NUSPELL_ENABLE #include #include #include #endif //#include #ifdef __APPLE__ #include #define UTF16TEXT char16_t #else #if QT_VERSION >= 0x050000 #include #define UTF16TEXT char16_t #else #define UTF16TEXT ushort #endif #endif #ifdef HUNSPELL_ENABLE #include #endif #include #include #include #include #include "utils.h" class CPlainSpellchecker { public: QString user_dict_filename; //ok QMap map; //ok void add_word (const QString &word); //ok void remove_word (const QString &word); //ok bool check (const QString &word); //ok void load_from_file (const QString &fname); //ok void save_to_file (const QString &fname); //ok }; class CSpellchecker { public: QString user_dict_filename; QString language; //current language QString dir_dicts; QString dir_user_dicts; QStringList modules_list; CPlainSpellchecker user_dict_checker; bool loaded; CSpellchecker (const QString &lang, const QString &dir_path, const QString &dir_user): language (lang), dir_dicts (dir_path), dir_user_dicts (dir_user), loaded(false) { user_dict_filename = dir_user + "/user_dict.txt"; // std::cout << "user_dict_checker.map.size() " << user_dict_checker.map.size() << std::endl; if (file_exists (user_dict_filename)) { //QString file_content = qstring_load (user_dict_filename); //user_dict = file_content.split ("\n"); user_dict_checker.load_from_file (user_dict_filename); } }; virtual ~CSpellchecker() { //if (user_dict.size() > 0) // qstring_save (user_dict_filename, user_dict.join ("\n")); // std::cout << "virtual ~CSpellchecker\n"; user_dict_checker.save_to_file (user_dict_filename); }; virtual void load_dict() = 0; //uses current language virtual void change_lang (const QString &lang) = 0; //set current language virtual void add_to_user_dict (const QString &word) = 0; virtual void remove_from_user_dict (const QString &word) = 0; virtual bool check (const QString &word) = 0; virtual void get_speller_modules_list() = 0; virtual QStringList get_suggestions_list (const QString &word) = 0; }; #ifdef ASPELL_ENABLE class CAspellchecker: public CSpellchecker { public: AspellConfig *spell_config; AspellCanHaveError *ret; AspellSpeller *speller; CAspellchecker (const QString &lang, const QString &dir_path = "", const QString &dir_user = ""); ~CAspellchecker(); void add_to_user_dict (const QString &word); void remove_from_user_dict (const QString &word); void load_dict(); void change_lang (const QString &lang); bool check (const QString &word); void get_speller_modules_list(); QStringList get_suggestions_list (const QString &word); }; QString aspell_default_dict_path(); #endif #ifdef HUNSPELL_ENABLE class CHunspellChecker: public CSpellchecker { public: Hunspell *speller; QStringList user_words; const char *encoding; //depercated (old Hunspell versions) but supported std::string str_encoding; //new approach CHunspellChecker (const QString &lang, const QString &dir_path = "", const QString &dir_user = ""); ~CHunspellChecker(); void add_to_user_dict (const QString &word); void remove_from_user_dict (const QString &word); void load_dict(); void change_lang (const QString &lang); bool check (const QString &word); void get_speller_modules_list(); QStringList get_suggestions_list (const QString &word); }; QString hunspell_default_dict_path(); #endif #ifdef NUSPELL_ENABLE class CNuspellChecker: public CSpellchecker { public: nuspell::Dictionary *speller; std::vector dirs; std::filesystem::path dict_path; QStringList user_words; std::string str_encoding; CNuspellChecker (const QString &lang, const QString &dir_path = "", const QString &dir_user = ""); ~CNuspellChecker(); void add_to_user_dict (const QString &word); void remove_from_user_dict (const QString &word); void load_dict(); void change_lang (const QString &lang); bool check (const QString &word); void get_speller_modules_list(); QStringList get_suggestions_list (const QString &word); }; #endif #endif tea-qt-63.3.0/src/tea.cpp000066400000000000000000010340471476733534200150710ustar00rootroot00000000000000/*************************************************************************** * 2000-2025 by Peter Semiletov * * peter.semiletov@gmail.com * C++/Qt branch started at 08 November 2007 * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "document.h" #include #include #include #include #include //ВОТ ЭТО НИЖЕ ВСПОМНИТЬ #if QT_VERSION >= 0x050000 #include #else #include #endif #if QT_VERSION < 0x050500 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #ifdef PRINTER_ENABLE #include #include #include #endif #include "tea.h" #include "utils.h" #include "gui_utils.h" #include "libretta_calc.h" #include "textproc.h" #include "logmemo.h" #include "wavinfo.h" #include "exif_reader.h" #include "spellchecker.h" #include "enc.h" //′ const int UQS = 8242; //″ const int UQD = 8243; //° const int UQDG = 176; bool MyProxyStyle::b_altmenu = false; int MyProxyStyle::cursor_blink_time = 1; extern QSettings *settings; extern QMenu *menu_current_files; extern QHash global_palette; extern bool b_recent_off; extern bool b_destroying_all; extern int recent_list_max_items; extern bool boring; extern int g_state; CTEA *main_window; CDox *documents; //QVariantMap hs_path; QString current_version_number; std::vector text_window_list; QHash > hash_markup; enum { FM_ENTRY_MODE_NONE = 0, FM_ENTRY_MODE_OPEN, FM_ENTRY_MODE_SAVE }; /* =========================== CDarkerWindow =========================== */ void CDarkerWindow::closeEvent (QCloseEvent *event) { event->accept(); } CDarkerWindow::CDarkerWindow() { setAttribute (Qt::WA_DeleteOnClose); setWindowFlags (Qt::Tool); setWindowTitle (tr ("Darker palette")); slider = new QSlider (Qt::Horizontal); slider->setMinimum (0); slider->setMaximum (200); QVBoxLayout *v_box = new QVBoxLayout; setLayout (v_box); v_box->addWidget (slider); slider->setValue (settings->value ("darker_val", "100").toInt() - 100); connect (slider, SIGNAL(valueChanged(int)), this, SLOT(slot_valueChanged(int))); } void CDarkerWindow::slot_valueChanged (int value) { settings->setValue ("darker_val", value + 100); documents->apply_settings(); main_window->update_stylesheet (main_window->fname_stylesheet); } /* =========================== CTextListWnd =========================== */ void CTextListWnd::closeEvent (QCloseEvent *event) { event->accept(); } CTextListWnd::~CTextListWnd() { vector::iterator it = std::find (text_window_list.begin(), text_window_list.end(), this); text_window_list.erase (it); } CTextListWnd::CTextListWnd (const QString &title, const QString &label_text) { setAttribute (Qt::WA_DeleteOnClose); QVBoxLayout *lt = new QVBoxLayout; QLabel *l = new QLabel (label_text); list = new QListWidget (this); lt->addWidget (l); lt->addWidget (list); setLayout (lt); setWindowTitle (title); text_window_list.push_back (this); } /* =========================== About window =========================== */ void CAboutWindow::closeEvent (QCloseEvent *event) { event->accept(); } void CAboutWindow::update_image() { QImage img (400, 100, QImage::Format_ARGB32); QPainter painter (&img); QFont f; f.setPixelSize (25); painter.setPen (Qt::gray); painter.setFont (f); for (int y = 1; y < 100; y += 25) for (int x = 1; x < 400; x += 25) { QColor color; int i = rand() % 5; switch (i) { case 0: color = 0xfff3f9ff; break; case 1: color = 0xffbfffb0; break; case 2: color = 0xffa5a5a6; break; case 3: color = 0xffebffe9; break; case 4: color = 0xffbbf6ff; break; } painter.fillRect (x, y, 25, 25, QBrush (color)); if (i == 0) painter.drawText (x, y + 25, "0"); if (i == 1) painter.drawText (x, y + 25, "1"); } QString txt = "TEA"; QFont f2 ("Monospace"); f2.setPixelSize (75); painter.setFont (f2); painter.setPen (Qt::black); painter.drawText (4, 80, txt); painter.setPen (Qt::red); painter.drawText (2, 76, txt); logo->setPixmap (QPixmap::fromImage (img)); } CAboutWindow::CAboutWindow() { // setAttribute (Qt::WA_DeleteOnClose); QStringList sl_t = qstring_load (":/AUTHORS").split ("##"); logo = new QLabel; update_image(); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update_image())); timer->start (1000); QTabWidget *tw = new QTabWidget (this); QPlainTextEdit *page_code = new QPlainTextEdit(); QPlainTextEdit *page_thanks = new QPlainTextEdit(); QPlainTextEdit *page_translators = new QPlainTextEdit(); if (sl_t.size() == 3) { page_code->setPlainText (sl_t[0].trimmed()); page_thanks->setPlainText (sl_t[2].trimmed()); page_translators->setPlainText (sl_t[1].trimmed()); } tw->addTab (page_code, tr ("Code")); tw->addTab (page_thanks, tr ("Acknowledgements")); tw->addTab (page_translators, tr ("Translations")); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(logo); layout->addWidget(tw); setLayout (layout); setWindowTitle (tr ("About")); } /* =========================== Local utility functions =========================== */ //for the further compatibility QTabWidget::TabPosition int_to_tabpos (int i) { QTabWidget::TabPosition p = QTabWidget::North; switch (i) { case 0: p = QTabWidget::North; break; case 1: p = QTabWidget::South; break; case 2: p = QTabWidget::West; break; case 3: p = QTabWidget::East; break; } return p; } QString toggle_fname_header_source (const QString &fileName) { QFileInfo f (fileName); QString ext = f.suffix(); if (ext == "c" || ext == "cpp" || ext == "cxx" || ext == "cc" || ext == "c++" || ext == "m" || ext == "mm") { if (file_exists (f.absolutePath() + "/" + f.baseName () + ".h")) return f.absolutePath() + "/" + f.baseName () + ".h"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".hxx")) return f.absolutePath() + "/" + f.baseName () + ".hxx"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".h++")) return f.absolutePath() + "/" + f.baseName () + ".h++"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".hh")) return f.absolutePath() + "/" + f.baseName () + ".hh"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".hpp")) return f.absolutePath() + "/" + f.baseName () + ".hpp"; } else if (ext == "h" || ext == "h++" || ext == "hxx" || ext == "hh" || ext == "hpp") { if (file_exists (f.absolutePath() + "/" + f.baseName () + ".c")) return f.absolutePath() + "/" + f.baseName () + ".c"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".cpp")) return f.absolutePath() + "/" + f.baseName () + ".cpp"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".cxx")) return f.absolutePath() + "/" + f.baseName () + ".cxx"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".c++")) return f.absolutePath() + "/" + f.baseName () + ".c++"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".cc")) return f.absolutePath() + "/" + f.baseName () + ".cc"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".m")) return f.absolutePath() + "/" + f.baseName () + ".m"; else if (file_exists (f.absolutePath() + "/" + f.baseName () + ".mm")) return f.absolutePath() + "/" + f.baseName () + ".mm"; } return fileName; } void CTEA::closeEvent (QCloseEvent *event) { if (main_tab_widget->currentIndex() == idx_tab_tune) leaving_options(); // qstring_save (dir_config + "/last_used_charsets", sl_last_used_charsets.join ("\n").trimmed()); if (settings->value("session_restore", false).toBool()) { QString fname_session = dir_sessions + "/def-session-777"; documents->save_to_session (fname_session); } if (settings->value("save_buffers", true).toBool()) documents->save_buffers (fname_saved_buffers); write_search_options(); write_settings(); qstring_save (fname_fif, sl_fif_history.join ("\n")); hash_save_keyval (fname_autosaving_files, documents->autosave_files); #ifdef SPEECH_ENABLE speech.done(); #endif delete documents; delete img_viewer; if (wnd_about) { wnd_about->close(); delete wnd_about; } #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) || defined (NUSPELL_ENABLE) delete spellchecker; #endif delete shortcuts; if (text_window_list.size() > 0) for (vector ::size_type i = 0; i < text_window_list.size(); i++) text_window_list[i]->close(); event->accept(); deleteLater(); } void CTEA::dragEnterEvent (QDragEnterEvent *event) { if (event->mimeData()->hasFormat ("text/uri-list")) event->acceptProposedAction(); } void CTEA::dropEvent (QDropEvent *event) { if (! event->mimeData()->hasUrls()) return; QString fName; QFileInfo info; QList l = event->mimeData()->urls(); for (QList ::iterator u = l.begin(); u != l.end(); ++u) { fName = u->toLocalFile(); info.setFile (fName); if (info.isFile()) documents->open_file (fName, cb_fman_codecs->currentText()); } event->accept(); } /* =========================== Main window slots =========================== */ void CTEA::pageChanged (int index) { if (b_destroying_all || index == -1) return; CDocument *d = documents->items[index]; if (! d) return; d->update_title (settings->value ("full_path_at_window_title", 1).toBool()); d->update_status(); if (file_exists (d->file_name)) documents->dir_last = get_file_path (d->file_name); documents->update_project (d->file_name); update_labels_menu(); } void CTEA::logmemo_double_click (const QString &txt) { if (documents->hash_project.isEmpty() || documents->fname_current_project.isEmpty()) return; QStringList parsed = txt.split (":"); if (parsed.size() == 0) return; QString source_fname; QString source_line; QString source_col; source_fname = parsed[0]; if (parsed.size() > 1) source_line = parsed[1]; if (parsed.size() > 2) source_col = parsed[2]; if (source_fname.startsWith("..")) source_fname.remove (0, 2); QFileInfo dir_source (documents->fname_current_project); QString source_dir = dir_source.absolutePath(); source_fname = source_dir + "/" + source_fname; log->no_jump = true; CDocument *d = documents->open_file_without_reload (source_fname, "UTF-8"); log->no_jump = false; if (! d) return; QTextCursor cur = d->textCursor(); if (cur.isNull()) return; cur.movePosition (QTextCursor::Start); cur.movePosition (QTextCursor::Down, QTextCursor::MoveAnchor, source_line.toInt() - 1); cur.movePosition (QTextCursor::Right, QTextCursor::MoveAnchor, source_col.toInt() - 1); cur.select (QTextCursor::WordUnderCursor); d->setTextCursor (cur); d->setFocus(); } void CTEA::receiveMessage (const QString &msg) { if (! msg.isEmpty()) documents->open_file (msg, "UTF-8"); } void CTEA::receiveMessageShared (const QStringList &msg) { for (int i = 0; i < msg.size(); i++) { QFileInfo f (msg.at(i)); if (! f.isAbsolute()) { QString fullname (QDir::currentPath()); fullname.append ("/").append (msg.at(i)); documents->open_file (fullname, charset); } else documents->open_file (msg.at(i), charset); } show(); activateWindow(); raise(); } void CTEA::tb_stop_clicked() { boring = true; } void CTEA::read_settings() { MyProxyStyle::cursor_blink_time = settings->value ("cursor_blink_time", 0).toInt(); qApp->setCursorFlashTime (MyProxyStyle::cursor_blink_time); MyProxyStyle::b_altmenu = settings->value ("b_altmenu", "0").toBool(); recent_list_max_items = settings->value ("recent_list.max_items", 21).toInt(); main_tab_widget->setTabPosition (int_to_tabpos (settings->value ("ui_tabs_align", "0").toInt())); tab_editor->setTabPosition (int_to_tabpos (settings->value ("docs_tabs_align", "0").toInt())); markup_mode = settings->value ("markup_mode", "HTML").toString(); charset = settings->value ("charset", "UTF-8").toString(); fname_def_palette = settings->value ("fname_def_palette", ":/palettes/TEA").toString(); QPoint pos = settings->value ("pos", QPoint (1, 200)).toPoint(); QSize size = settings->value ("size", QSize (800, 512)).toSize(); if (mainSplitter) mainSplitter->restoreState (settings->value ("splitterSizes").toByteArray()); resize (size); move (pos); pos = settings->value ("md_viewer_pos", QPoint (1, 200)).toPoint(); size = settings->value ("md_viewer_size", QSize (800, 512)).toSize(); md_viewer.resize (size); md_viewer.move (pos); } void CTEA::write_settings() { if (mainSplitter) settings->setValue ("splitterSizes", mainSplitter->saveState()); settings->setValue ("pos", pos()); settings->setValue ("size", size()); settings->setValue ("charset", charset); settings->setValue ("spl_fman", spl_fman->saveState()); settings->setValue ("dir_last", documents->dir_last); settings->setValue ("fname_def_palette", fname_def_palette); settings->setValue ("markup_mode", markup_mode); settings->setValue ("VER_NUMBER", QString (current_version_number)); settings->setValue ("state", saveState()); settings->setValue ("word_wrap", cb_wordwrap->isChecked()); settings->setValue ("show_linenums", cb_show_linenums->isChecked()); settings->setValue ("fif_at_toolbar", cb_fif_at_toolbar->isChecked()); settings->setValue ("save_buffers", cb_save_buffers->isChecked()); settings->setValue ("md_viewer_pos", md_viewer.pos()); settings->setValue ("md_viewer_size", md_viewer.size()); delete settings; } void CTEA::read_search_options() { menu_find_whole_words->setChecked (settings->value ("find_whole_words", "0").toBool()); menu_find_case->setChecked (settings->value ("find_case", "0").toBool()); menu_find_regexp->setChecked (settings->value ("find_regexp", "0").toBool()); menu_find_fuzzy->setChecked (settings->value ("find_fuzzy", "0").toBool()); menu_find_from_cursor->setChecked (settings->value ("find_from_cursor", "1").toBool()); } void CTEA::write_search_options() { settings->setValue ("find_whole_words", menu_find_whole_words->isChecked()); settings->setValue ("find_case", menu_find_case->isChecked()); settings->setValue ("find_regexp", menu_find_regexp->isChecked()); settings->setValue ("find_fuzzy", menu_find_fuzzy->isChecked()); settings->setValue ("find_from_cursor", menu_find_from_cursor->isChecked()); } /* =================== File manager slots =================== */ void CTEA::fman_drives_changed (const QString & path) { if (! ui_update) fman->nav (path); } void CTEA::fman_current_file_changed (const QString &full_path, const QString &just_name) { if (! is_dir (full_path)) ed_fman_fname->setText (just_name); else ed_fman_fname->setText (""); if (b_preview && is_image (full_path)) { if (! img_viewer->window_mini.isVisible()) { img_viewer->window_mini.show(); activateWindow(); fman->setFocus(); } img_viewer->set_image_mini (full_path); } } void CTEA::fman_file_activated (const QString &full_path) { /* if (file_get_ext (full_path) == ("zip")) { CZipper z; QStringList sl = z.unzip_list (full_path); for (int i = 0; i < sl.size(); i++) sl[i] = sl[i] + "
"; log->log (sl.join("\n")); return; } */ if (is_image (full_path)) { CDocument *d = documents->get_current(); if (! d) return; d->insert_image (full_path); main_tab_widget->setCurrentIndex (idx_tab_edit); return; } CDocument *d = documents->open_file (full_path, cb_fman_codecs->currentText()); if (d) charset = d->charset; //add_to_last_used_charsets (cb_fman_codecs->currentText()); main_tab_widget->setCurrentIndex (idx_tab_edit); } void CTEA::fman_dir_changed (const QString &full_path) { ui_update = true; ed_fman_path->setText (full_path); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) cb_fman_drives->setCurrentIndex (cb_fman_drives->findText (full_path.left(3).toUpper())); #endif ui_update = false; } void CTEA::fman_fname_entry_confirm() { if (fm_entry_mode == FM_ENTRY_MODE_OPEN) fman_open(); if (fm_entry_mode == FM_ENTRY_MODE_SAVE) cb_button_saves_as(); } void CTEA::fman_naventry_confirm() { fman->nav (ed_fman_path->text()); } void CTEA::fman_add_bmk() { sl_places_bmx.prepend (ed_fman_path->text()); qstring_save (fname_places_bookmarks, sl_places_bmx.join ("\n")); update_places_bookmarks(); } void CTEA::fman_del_bmk() { int i = lv_places->currentRow(); if (i < 5) //TEA built-in bookmark, don't remove return; if (i > sl_places_bmx.size() + 4) // -- GTK places { // qDebug() << "GTK bookmarks, i: " << i; return; } //else TEA places // qDebug() << "TEA places, i: " << i; QString s = lv_places->item(i)->text(); if (s.isEmpty()) return; sl_places_bmx.removeAt (sl_places_bmx.indexOf (s)); // поменять на индекс i + 5 //sl_places_bmx.removeAt (i); qstring_save (fname_places_bookmarks, sl_places_bmx.join ("\n")); update_places_bookmarks(); } void CTEA::fman_open() { QString f = ed_fman_fname->text().trimmed(); QStringList li = fman->get_sel_fnames(); if (path_is_abs (f)) //if file name entry is the full path and not empty { CDocument *d = documents->open_file (f, cb_fman_codecs->currentText()); if (d) { charset = d->charset; //add_to_last_used_charsets (cb_fman_codecs->currentText()); main_tab_widget->setCurrentIndex (idx_tab_edit); } return; } //if file name entry == just filename if (li.size() == 0 && ! f.isEmpty()) { QString fname (fman->dir.path()); fname.append ("/").append (f); CDocument *d = documents->open_file (fname, cb_fman_codecs->currentText()); if (d) { charset = d->charset; //add_to_last_used_charsets (cb_fman_codecs->currentText()); main_tab_widget->setCurrentIndex (idx_tab_edit); } return; } //if file[s] selected at files list bool opened = false; for (int i = 0; i < li.size(); i++) { CDocument *d = 0; d = documents->open_file (li.at(i), cb_fman_codecs->currentText()); if (d) { charset = d->charset; opened = true; } } //add_to_last_used_charsets (cb_fman_codecs->currentText()); if (opened) main_tab_widget->setCurrentIndex (idx_tab_edit); } void CTEA::fman_places_itemActivated (QListWidgetItem *item) { int i = lv_places->currentRow(); QString s = item->text(); vector v; v.push_back (dir_templates); v.push_back (dir_snippets); v.push_back (dir_scripts); v.push_back (dir_tables); v.push_back (dir_config); if (i < 5) s = v[i]; //if (i > sl_places_bmx.size() + 4) // s = sl_gtk_bookmarks[i - (sl_places_bmx.size() + 6)]; fman->nav (s); } void CTEA::cb_button_saves_as() { CDocument *d = documents->get_current(); if (! d || ed_fman_fname->text().isEmpty()) return; QString fn = ed_fman_fname->text(); QString filename; if (path_is_abs (fn)) //if file name entry is the full path and not empty filename = fn; else { filename = fman->dir.path(); filename.append ("/").append (ed_fman_fname->text()); } if (path_is_dir (filename)) return; if (file_exists (filename)) if (QMessageBox::warning (this, "TEA", tr ("%1 already exists\n" "Do you want to overwrite?") .arg (filename), QMessageBox::Yes, QMessageBox::Cancel) == QMessageBox::Cancel) return; // qDebug() << "CTEA::cb_button_saves_as(): " << filename << " - " << cb_fman_codecs->currentText(); d->file_save_with_name (filename, cb_fman_codecs->currentText()); d->set_markup_mode(); //add_to_last_used_charsets (cb_fman_codecs->currentText()); d->set_hl(); QFileInfo f (d->file_name); documents->dir_last = f.path(); update_dyn_menus(); shortcuts->load_from_file (shortcuts->fname); fman->refresh(); main_tab_widget->setCurrentIndex (idx_tab_edit); } /* void CTEA::ide_ctags() { if (documents->hash_project.isEmpty()) return; if (documents->fname_current_project.isEmpty()) return; QFileInfo source_dir (documents->fname_current_project); QString command_ctags = hash_get_val (documents->hash_project, "command_ctags", "ctags -R"); QProcess *process = new QProcess (this); process->setWorkingDirectory (source_dir.absolutePath()); connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(process_readyReadStandardOutput())); process->setProcessChannelMode (QProcess::MergedChannels) ; process->start (command_ctags, QIODevice::ReadWrite); if (! process->waitForFinished()) return; //else parse ctags file } void CTEA::ide_gtags() { if (documents->hash_project.isEmpty()) return; if (documents->fname_current_project.isEmpty()) return; QFileInfo source_dir (documents->fname_current_project); QString command_gtags = hash_get_val (documents->hash_project, "command_gtags", "gtags"); QProcess *process = new QProcess (this); process->setWorkingDirectory (source_dir.absolutePath()); // connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(process_readyReadStandardOutput())); if (file_exists (source_dir.absolutePath() + "/GTAGS")) command_gtags = "global -u"; process->setProcessChannelMode (QProcess::MergedChannels) ; process->start (command_gtags, QIODevice::ReadWrite); if (! process->waitForStarted()) return; if (! process->waitForFinished()) return; } void CTEA::ide_global_definition() { if (documents->hash_project.isEmpty()) return; if (documents->fname_current_project.isEmpty()) return; CDocument *d = documents->get_current(); if (! d) return; QString sel_text = d->get(); QFileInfo source_dir (documents->fname_current_project); QString command = hash_get_val (documents->hash_project, "command_global_definition", "global -a -x --result=grep"); command = command + " " + sel_text; QProcess *process = new QProcess (this); process->setWorkingDirectory (source_dir.absolutePath()); process->setProcessChannelMode (QProcess::MergedChannels) ; process->start (command, QIODevice::ReadWrite); if (! process->waitForStarted()) return; if (! process->waitForFinished()) return; QByteArray a = process->readAll(); if (a.isEmpty()) return; QString s(a); //value_t 28 lib/optional/libffi/ffi.cpp struct value_t { //if (s.indexOf ('\t') != -1) // qDebug() << "TAB!"; //qDebug() << "4"; //parse output QStringList sl_output = s.split ("\n"); if (sl_output.size() == -1) return; ///home/rox/devel/tea-qt/main.cpp:36:int main (int argc, char *argv[]) foreach (QString str, sl_output) { if (! str.isEmpty()) { int idx_path = str.indexOf (":"); QString str_path = str.left (idx_path); int idx_line = str.indexOf (":", idx_path + 1); QString str_line = str.mid (idx_path + 1, idx_line - idx_path); // int idx_pattern = str.indexOf (":", idx_line + 1); QString str_pattern = str.right (str.size() - idx_line + 1); qDebug() << "path: " << str_path; qDebug() << "line: " << str_line; qDebug() << "pattern: " << str_pattern; } } qDebug() << "3"; } void CTEA::ide_global_references() { } */ /* =================== File menu callbacks =================== */ void CTEA::test() { //documents->speech_thing.print_engines(); //documents->speech_thing.print_languages(); /* CIconvCharsetConverter c; QByteArray ba = file_load ("/home/rox/devel/test/1251.txt"); QString s = c.to_utf16 ("CP1251", ba.data(), ba.size()); qDebug() << s;*/ // QIconvCodec c; } void CTEA::file_new() { last_action = sender(); documents->create_new(); main_tab_widget->setCurrentIndex (idx_tab_edit); } void CTEA::file_open() { last_action = sender(); if (! settings->value ("use_trad_dialogs", "1").toBool()) { CDocument *d = documents->get_current(); if (d) { if (file_exists (d->file_name)) fman->nav (get_file_path (d->file_name)); } else fman->nav (documents->dir_last); main_tab_widget->setCurrentIndex (idx_tab_fman); fm_entry_mode = FM_ENTRY_MODE_OPEN; return; } //ELSE use the traditional dialog QFileDialog dialog (this); QSize size = settings->value ("dialog_size", QSize (width(), height())).toSize(); dialog.resize (size); dialog.setFilter (QDir::AllEntries | QDir::Hidden); dialog.setOption (QFileDialog::DontUseNativeDialog, true); #if QT_VERSION >= 0x050200 dialog.setOption (QFileDialog::DontUseCustomDirectoryIcons, true); #endif QList sidebarUrls = dialog.sidebarUrls(); QList sidebarUrls_old = dialog.sidebarUrls(); sidebarUrls.append (QUrl::fromLocalFile (dir_templates)); sidebarUrls.append (QUrl::fromLocalFile (dir_snippets)); sidebarUrls.append (QUrl::fromLocalFile (dir_sessions)); sidebarUrls.append (QUrl::fromLocalFile (dir_scripts)); sidebarUrls.append (QUrl::fromLocalFile (dir_tables)); #ifdef Q_OS_UNIX QDir volDir ("/mnt"); QStringList volumes (volDir.entryList (volDir.filter() | QDir::NoDotAndDotDot)); QDir volDir2 ("/media"); QStringList volumes2 (volDir2.entryList (volDir.filter() | QDir::NoDotAndDotDot)); for (int i = 0; i < volumes.size(); i++) sidebarUrls.append (QUrl::fromLocalFile ("/mnt/" + volumes.at(i))); for (int i = 0; i < volumes2.size(); i++) sidebarUrls.append (QUrl::fromLocalFile ("/media/" + volumes2.at(i))); #endif dialog.setSidebarUrls (sidebarUrls); dialog.setFileMode (QFileDialog::ExistingFiles); dialog.setAcceptMode (QFileDialog::AcceptOpen); CDocument *d = documents->get_current(); if (d) { if (file_exists (d->file_name)) dialog.setDirectory (get_file_path (d->file_name)); } else dialog.setDirectory (documents->dir_last); dialog.setNameFilter (tr ("All (*);;Text files (*.txt);;Markup files (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)")); QLabel *l = new QLabel (tr ("Charset")); QComboBox *cb_codecs = new QComboBox (&dialog); dialog.layout()->addWidget (l); dialog.layout()->addWidget (cb_codecs); //if (sl_last_used_charsets.size () > 0) // cb_codecs->addItems (sl_last_used_charsets + sl_charsets); // else cb_codecs->addItems (sl_charsets); int charset_index = sl_charsets.indexOf ("UTF-8"); if (charset_index == -1) charset_index = 0; cb_codecs->setCurrentIndex (charset_index); QStringList fileNames; if (dialog.exec()) { dialog.setSidebarUrls (sidebarUrls_old); fileNames = dialog.selectedFiles(); for (int i = 0; i < fileNames.size(); i++) { CDocument *dc = documents->open_file (fileNames.at(i), cb_codecs->currentText()); if (dc) charset = dc->charset; // add_to_last_used_charsets (cb_codecs->currentText()); } } else dialog.setSidebarUrls (sidebarUrls_old); settings->setValue ("dialog_size", dialog.size()); update_dyn_menus(); } void CTEA::file_open_at_cursor() { last_action = sender(); if (main_tab_widget->currentIndex() == idx_tab_fman) { fman_preview_image(); return; } CDocument *d = documents->get_current(); if (! d) return; QString fname = d->get_filename_at_cursor(); if (fname.isEmpty()) return; if (is_image (fname)) { if (settings->value ("override_img_viewer", 0).toBool()) //external image viewer { QString command = settings->value ("img_viewer_override_command", "display %s").toString(); command = command.replace ("%s", fname); //QProcess::startDetached (command, QStringList()); system (command.toUtf8().data()); return; } else { if (file_get_ext (fname) == "gif") { CGIFWindow *w = new CGIFWindow; w->load_image (fname); return; } else //not GIF { if (! img_viewer->window_full.isVisible()) { img_viewer->window_full.show(); activateWindow(); } img_viewer->set_image_full (fname); return; } } } if (fname.startsWith ("#")) //HTML inner label { QString t = fname; t.remove (0, 1); t.prepend ("name=\""); if (d->find (t)) return; t = fname; t.remove (0, 1); t.prepend ("id=\""); d->find (t); return; } documents->open_file_without_reload (fname, d->charset); } void CTEA::file_last_opened() { last_action = sender(); if (documents->recent_files.size() > 0) { documents->open_file_triplex (documents->recent_files[0]); documents->recent_files.removeAt (0); documents->update_recent_menu(); } } void CTEA::file_crapbook() { last_action = sender(); if (! QFile::exists (fname_crapbook)) qstring_save (fname_crapbook, tr ("you can put here notes, etc")); documents->open_file_without_reload (fname_crapbook, "UTF-8"); } void CTEA::file_notes() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! file_exists (d->file_name)) return; QString fname = d->file_name + ".notes"; if (! file_exists (fname)) qstring_save (fname, tr ("put your notes (for this file) here and they will be saved automatically")); documents->open_file_without_reload (fname, "UTF-8"); } bool CTEA::file_save() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return false; if (d->isReadOnly()) { log->log (tr ("This file is opened in the read-only mode. You can save it with another name using Save as")); return false; } if (file_exists (d->file_name)) d->file_save_with_name (d->file_name, d->charset); else return file_save_as(); if (d->file_name == fname_bookmarks) update_bookmarks(); if (d->file_name == fname_programs) update_programs(); return true; } bool CTEA::file_save_as() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return false; if (! settings->value ("use_trad_dialogs", "1").toBool()) { main_tab_widget->setCurrentIndex (idx_tab_fman); fm_entry_mode = FM_ENTRY_MODE_SAVE; if (file_exists (d->file_name)) fman->nav (get_file_path (d->file_name)); else fman->nav (documents->dir_last); ed_fman_fname->setFocus(); return true; } //ELSE standard dialog QFileDialog dialog (this); QSize size = settings->value ("dialog_size", QSize (width(), height())).toSize(); dialog.resize (size); dialog.setFilter (QDir::AllEntries | QDir::Hidden); dialog.setOption (QFileDialog::DontUseNativeDialog, true); QList sidebarUrls = dialog.sidebarUrls(); QList sidebarUrls_old = dialog.sidebarUrls(); sidebarUrls.append (QUrl::fromLocalFile(dir_templates)); sidebarUrls.append (QUrl::fromLocalFile(dir_snippets)); sidebarUrls.append (QUrl::fromLocalFile(dir_sessions)); sidebarUrls.append (QUrl::fromLocalFile(dir_scripts)); sidebarUrls.append (QUrl::fromLocalFile(dir_tables)); #ifdef Q_OS_LINUX QDir volDir ("/mnt"); QStringList volumes (volDir.entryList (volDir.filter() | QDir::NoDotAndDotDot)); QDir volDir2 ("/media"); QStringList volumes2 (volDir2.entryList (volDir2.filter() | QDir::NoDotAndDotDot)); for (int i = 0; i < volumes.size(); i++) sidebarUrls.append (QUrl::fromLocalFile ("/mnt/" + volumes.at(i))); for (int i = 0; i < volumes2.size(); i++) sidebarUrls.append (QUrl::fromLocalFile ("/media/" + volumes2.at(i))); #endif dialog.setSidebarUrls (sidebarUrls); dialog.setFileMode (QFileDialog::AnyFile); dialog.setAcceptMode (QFileDialog::AcceptSave); dialog.setDirectory (documents->dir_last); QLabel *l = new QLabel (tr ("Charset")); QComboBox *cb_codecs = new QComboBox (&dialog); dialog.layout()->addWidget (l); dialog.layout()->addWidget (cb_codecs); //if (sl_last_used_charsets.size () > 0) // cb_codecs->addItems (sl_last_used_charsets + sl_charsets); // else int charset_index = sl_charsets.indexOf ("UTF-8"); if (charset_index == -1) charset_index = 0; cb_codecs->addItems (sl_charsets); cb_codecs->setCurrentIndex (charset_index); if (dialog.exec()) { dialog.setSidebarUrls (sidebarUrls_old); QString fileName = dialog.selectedFiles().at(0); if (file_exists (fileName)) { int ret = QMessageBox::warning (this, "TEA", tr ("%1 already exists\n" "Do you want to overwrite?") .arg (fileName), QMessageBox::Yes, QMessageBox::Cancel); if (ret == QMessageBox::Cancel) return false; } d->file_save_with_name (fileName, cb_codecs->currentText()); d->set_markup_mode(); d->set_hl(); // add_to_last_used_charsets (cb_codecs->currentText()); update_dyn_menus(); QFileInfo f (d->file_name); documents->dir_last = f.path(); } else dialog.setSidebarUrls (sidebarUrls_old); settings->setValue ("dialog_size", dialog.size()); return true; } void CTEA::file_save_bak() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! file_exists (d->file_name)) return; QString fname = d->file_name + ".bak"; d->file_save_with_name_plain (fname); log->log (tr ("%1 is saved").arg (fname)); } void CTEA::file_save_version() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! file_exists (d->file_name)) return; QDate date = QDate::currentDate(); QFileInfo fi; fi.setFile (d->file_name); QString version_timestamp_fmt = settings->value ("version_timestamp_fmt", "yyyy-MM-dd").toString(); QTime t = QTime::currentTime(); QString fname = fi.absoluteDir().absolutePath() + "/" + fi.baseName() + "-" + date.toString (version_timestamp_fmt) + "-" + t.toString ("hh-mm-ss") + "." + fi.suffix(); if (d->file_save_with_name_plain (fname)) log->log (tr ("%1 - saved").arg (fname)); else log->log (tr ("Cannot save %1").arg (fname)); } void CTEA::file_save_all_existing() { last_action = sender(); if (documents->items.size() == 0) return; for (int i = 0; i < documents->items.size(); i++) { CDocument *d = documents->items[i]; if (file_exists (d->file_name) && d->document()->isModified()) d->file_save_with_name_plain (d->file_name); } } void CTEA::file_session_save_as() { last_action = sender(); if (documents->items.size() == 0) return; bool ok; QString name = QInputDialog::getText (this, tr ("Enter the name"), tr ("Name:"), QLineEdit::Normal, tr ("new_session"), &ok); if (! ok || name.isEmpty()) return; QString fname = dir_sessions + "/" + name; documents->save_to_session (fname); update_sessions(); } void CTEA::file_reload() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->reload (d->charset); } void CTEA::file_reload_enc_itemDoubleClicked (QListWidgetItem *item) { CDocument *d = documents->get_current(); if (d) d->reload (item->text()); } void CTEA::file_reload_enc() { last_action = sender(); CTextListWnd *w = new CTextListWnd (tr ("Reload with encoding"), tr ("Charset")); //if (sl_last_used_charsets.size () > 0) // w->list->addItems (sl_last_used_charsets + sl_charsets); // else w->list->addItems (sl_charsets); connect (w->list, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(file_reload_enc_itemDoubleClicked(QListWidgetItem*))); w->show(); } void CTEA::file_set_eol_unix() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->eol = "\n"; } void CTEA::file_set_eol_win() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->eol = "\r\n"; } void CTEA::file_set_eol_mac() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->eol = "\r"; } void CTEA::file_set_autosaving_file() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! file_exists (d->file_name)) return; documents->autosave_files.insert (d->file_name, d->file_name); } void CTEA::file_unset_autosaving_file() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! file_exists (d->file_name)) return; documents->autosave_files.remove (d->file_name); } #ifdef PRINTER_ENABLE void CTEA::file_print() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QPrintDialog *dialog = new QPrintDialog (&printer, this); dialog->setWindowTitle (tr ("Print document")); //if (d->textCursor().hasSelection()) // dialog->addEnabledOption (QAbstractPrintDialog::PrintSelection); if (dialog->exec() != QDialog::Accepted) return; d->print (&printer); } #endif void CTEA::file_add_to_bookmarks() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! file_exists (d->file_name)) return; bool found = false; QStringList l_bookmarks = qstring_load (fname_bookmarks).split("\n"); for (int i = 0; i < l_bookmarks.size(); i++) { if (l_bookmarks.at(i).contains (d->file_name)) //update the bookmark { l_bookmarks[i] = d->get_triplex(); found = true; break; } } if (! found) //else just add new bookbmark l_bookmarks.prepend (d->get_triplex()); QString bookmarks = l_bookmarks.join ("\n").trimmed(); qstring_save (fname_bookmarks, bookmarks); update_bookmarks(); } void CTEA::file_find_obsolete_paths() { QStringList l_bookmarks = qstring_load (fname_bookmarks).split ("\n"); for (int i = 0; i < l_bookmarks.size(); i++) { QStringList t = l_bookmarks[i].split ("*"); if (! file_exists (t[0])) l_bookmarks[i] = "#" + l_bookmarks[i]; //comment out the bookmark line } QString bookmarks = l_bookmarks.join ("\n").trimmed(); qstring_save (fname_bookmarks, bookmarks); update_bookmarks(); } void CTEA::file_open_bookmarks_file() { last_action = sender(); documents->open_file_without_reload (fname_bookmarks, "UTF-8"); } void CTEA::file_open_programs_file() { last_action = sender(); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) if (! file_exists (fname_programs)) qstring_save (fname_programs, tr ("#external programs list. example:\nopera=\"C:\\Program Files\\Opera\\opera.exe \" \"%s\"")); #else if (! file_exists (fname_programs)) qstring_save (fname_programs, tr ("#external programs list. example:\nff=firefox file:///%s")); #endif documents->open_file_without_reload (fname_programs, "UTF-8"); } void CTEA::file_open_bookmark() { last_action = sender(); documents->open_file_triplex (qobject_cast(last_action)->data().toString()); main_tab_widget->setCurrentIndex (idx_tab_edit); } void CTEA::file_use_template() { last_action = sender(); QAction *a = qobject_cast(sender()); QString txt = qstring_load (a->data().toString()); CDocument *d = documents->create_new(); if (d) d->put (txt); } void CTEA::file_open_session() { last_action = sender(); QAction *a = qobject_cast(sender()); documents->load_from_session (a->data().toString()); } void CTEA::file_recent_off() { last_action = sender(); b_recent_off = ! b_recent_off; } void CTEA::file_close() { last_action = sender(); documents->close_current(); } /* =================== Edit menu callbacks =================== */ void CTEA::ed_copy() { last_action = sender(); if (main_tab_widget->currentIndex() == idx_tab_edit) { CDocument *d = documents->get_current(); if (d) d->copy(); } else if (main_tab_widget->currentIndex() == idx_tab_learn) man->copy(); } void CTEA::ed_paste() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->paste(); } void CTEA::ed_cut() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->cut(); } void CTEA::ed_select_all() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->selectAll(); } void CTEA::ed_block_start() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->rect_block_start(); } void CTEA::ed_block_end() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->rect_block_end(); } void CTEA::ed_block_copy() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! d->has_rect_selection()) return; QApplication::clipboard()->setText (d->rect_sel_get()); } void CTEA::ed_block_paste() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->rect_sel_replace (QApplication::clipboard()->text()); } void CTEA::ed_block_cut() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->rect_sel_cut(); } void CTEA::ed_copy_current_fname() { last_action = sender(); CDocument *d = documents->get_current(); if (d) QApplication::clipboard()->setText (d->file_name); } void CTEA::ed_undo() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->undo(); } void CTEA::ed_redo() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->redo(); } void CTEA::ed_indent() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->indent(); } void CTEA::ed_unindent() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->un_indent(); } void CTEA::ed_indent_by_first_line() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList sl = d->get().split (QChar::ParagraphSeparator); if (sl.size() == 0) return; QString x = sl[0]; QChar c = x[0]; int pos = 0; if (c == ' ' || c == '\t') for (int i = 0; i < x.size(); i++) if (x[i] != c) { pos = i; break; } QString fill_string; fill_string.fill (c, pos); for (int i = 0; i < sl.size(); i++) { QString s = sl[i].trimmed(); s.prepend (fill_string); sl[i] = s; } QString t = sl.join ("\n"); d->put (t); } void CTEA::ed_comment() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! d->highlighter) return; if (d->highlighter->comment_mult.isEmpty() && d->highlighter->comment_single.isEmpty()) return; QString t = d->get(); QString result; bool is_multiline = true; int sep_pos = t.indexOf (QChar::ParagraphSeparator); if (sep_pos == -1 || sep_pos == t.size() - 1) is_multiline = false; if (is_multiline) result = d->highlighter->comment_mult; else result = d->highlighter->comment_single; if (is_multiline && result.isEmpty()) { QStringList sl = t.split (QChar::ParagraphSeparator); for (int i = 0; i < sl.size(); i++) { QString x = d->highlighter->comment_single; sl[i] = x.replace ("%s", sl[i]); } QString z = sl.join("\n"); d->put (z); return; } d->put (result.replace ("%s", t)); } void CTEA::ed_set_as_storage_file() { last_action = sender(); CDocument *d = documents->get_current(); if (d) fname_storage_file = d->file_name; } void CTEA::ed_copy_to_storage_file() { last_action = sender(); CDocument *dsource = documents->get_current(); if (! dsource) return; CDocument *ddest = documents->get_document_by_fname (fname_storage_file); if (ddest) { QString t = dsource->get(); ddest->put (t); ddest->put ("\n"); } else log->log (tr ("The storage file is closed or not set.")); } void CTEA::ed_capture_clipboard_to_storage_file() { last_action = sender(); capture_to_storage_file = qobject_cast(sender())->isChecked(); //was capture_to_storage_file = ! capture_to_storage_file; } /* =================== Markup menu callbacks =================== */ void CTEA::mrkup_mode_choosed() { last_action = sender(); QAction *a = qobject_cast(sender()); markup_mode = a->data().toString(); documents->markup_mode = markup_mode; CDocument *d = documents->get_current(); if (d) d->markup_mode = markup_mode; } void CTEA::mrkup_header() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QAction *a = qobject_cast(sender()); QString r; if (documents->markup_mode == "Markdown") { QString t; int n = a->data().toString().toLower()[1].digitValue(); t.fill ('#', n); r = t + " " + d->get(); } else r = QString ("<%1>%2").arg ( a->data().toString().toLower()).arg ( d->get()); d->put (r); } void CTEA::mrkup_align_center() { last_action = sender(); markup_text ("align_center"); } void CTEA::mrkup_align_left() { last_action = sender(); markup_text ("align_left"); } void CTEA::mrkup_align_right() { last_action = sender(); markup_text ("align_right"); } void CTEA::mrkup_align_justify() { last_action = sender(); markup_text ("align_justify"); } void CTEA::mrkup_bold() { last_action = sender(); markup_text ("bold"); } void CTEA::mrkup_italic() { last_action = sender(); markup_text ("italic"); } void CTEA::mrkup_underline() { last_action = sender(); markup_text ("underline"); } void CTEA::mrkup_link() { last_action = sender(); markup_text ("link"); } void CTEA::mrkup_para() { last_action = sender(); markup_text ("para"); } void CTEA::mrkup_color() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QColor color = QColorDialog::getColor (Qt::green, this); if (! color.isValid()) return; QString s; if (d->textCursor().hasSelection()) s = QString ("%2") .arg (color.name()) .arg (d->get()); else s = color.name(); d->put (s); } void CTEA::mrkup_br() { last_action = sender(); markup_text ("newline"); } void CTEA::mrkup_nbsp() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (" "); } void CTEA::markup_ins_image() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; main_tab_widget->setCurrentIndex (idx_tab_fman); if (file_exists (d->file_name)) fman->nav (get_file_path (d->file_name)); } void CTEA::mrkup_text_to_html() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList l; if (d->textCursor().hasSelection()) l = d->get().split (QChar::ParagraphSeparator); else l = d->toPlainText().split("\n"); QString result; if (d->markup_mode == "HTML") result += "\n"; else result += "\n"; result += "\n" "\n" "\n" "\n" "\n" "\n" "\n"; for (int i = 0; i < l.size(); i++) { QString t = l.at(i).simplified(); if (t.isEmpty()) { if (d->markup_mode == "HTML") result += "
\n"; else result += "
\n"; } else result += "

" + t + "

\n"; } result += "\n"; CDocument *doc = documents->create_new(); if (doc) doc->put (result); } void CTEA::mrkup_tags_to_entities() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (str_to_entities (d->get())); } void CTEA::mrkup_antispam_email() { last_action = qobject_cast(sender()); CDocument *d = documents->get_current(); if (! d) return; QString s = d->get(); QString result; for (int i = 0; i < s.size(); i++) result = result + "&#" + QString::number (s.at (i).unicode()) + ";"; d->put (result); } void CTEA::mrkup_document_weight() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString result; QStringList l = html_get_by_patt (d->toPlainText(), "src=\""); QFileInfo f (d->file_name); QUrl baseUrl (d->file_name); vector > files; files.push_back(make_pair(d->file_name, f.size())); int size_total = 0; int files_total = 1; for (int i = 0; i < l.size(); i++) { QUrl relativeUrl (l.at(i)); QString resolved = baseUrl.resolved (relativeUrl).toString(); QFileInfo info (resolved); if (! info.exists()) files.push_back(std::make_pair(tr ("%1 is not found
").arg (resolved), info.size())); else { files.push_back(std::make_pair(resolved, info.size())); size_total += info.size(); ++files_total; } } std::sort (files.begin(), files.end()); for (vector >::iterator p = files.begin(); p != files.end(); ++p) { result += tr ("%1 kbytes %2
").arg (QString::number (p->second / 1024)).arg (p->first); } result.prepend (tr ("Total size = %1 kbytes in %2 files
").arg (QString::number (size_total / 1024)) .arg (QString::number (files_total))); log->log (result); } void CTEA::mrkup_preview_color() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! d->textCursor().hasSelection()) return; QString color = d->get(); if (QColor::colorNames().indexOf (color) == -1) { color = color.remove (";"); if (! color.startsWith ("#")) color = "#" + color; } else { QColor c (color); color = c.name(); } QString style = QString ("color:%1; font-weight:bold;").arg (color); log->log (tr ("COLOR SAMPLE").arg (style)); } void CTEA::mrkup_strip_html_tags() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString text; if (d->textCursor().hasSelection()) text = d->get(); else text = d->toPlainText(); if (d->textCursor().hasSelection()) d->put (strip_html (text)); else d->setPlainText (strip_html (text)); } void CTEA::mrkup_rename_selected() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! d->textCursor().hasSelection()) { log->log (tr ("Select the file name first!")); return; } QString fname = d->get_filename_at_cursor(); if (fname.isEmpty()) return; if (documents->get_document_by_fname (fname)) { log->log (tr("You are trying to rename the opened file, please close it first!")); return; } QString newname = fif_get_text(); if (newname.isEmpty()) return; QFileInfo fi (fname); if (! fi.exists() && ! fi.isWritable()) return; QString newfpath = fi.path() + "/" + newname; QFile::rename (fname, newfpath); update_dyn_menus(); fman->refresh(); QDir dir (d->file_name); QString new_name = dir.relativeFilePath (newfpath); if (new_name.startsWith ("..")) new_name = new_name.remove (0, 1); if (d->get().startsWith ("./") && ! new_name.startsWith ("./")) new_name = "./" + new_name; if (! d->get().startsWith ("./") && new_name.startsWith ("./")) new_name = new_name.remove (0, 2); if (d->textCursor().hasSelection()) d->put (new_name.trimmed()); } /* =================== Search menu callbacks =================== */ void CTEA::search_find() { last_action = sender(); if (main_tab_widget->currentIndex() == idx_tab_edit) { CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr; int from = 0; if (settings->value ("find_from_cursor", "1").toBool()) from = d->textCursor().position(); d->text_to_search = fif_get_text(); #if QT_VERSION >= 0x050500 if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegularExpression (d->text_to_search), from, get_search_options()); #else if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegExp (d->text_to_search), from, get_search_options()); #endif /* #if QT_VERSION < 0x050000 cr = d->document()->find (QRegExp (d->text_to_search), from, get_search_options()); #else cr = d->document()->find (QRegularExpression (d->text_to_search), from, get_search_options()); #endif */ if (menu_find_fuzzy->isChecked()) { int pos = str_fuzzy_search (d->toPlainText(), d->text_to_search, from, settings->value ("fuzzy_q", "60").toInt()); if (pos != -1) { from = pos + d->text_to_search.length() - 1; //set selection: cr = d->textCursor(); cr.setPosition (from, QTextCursor::MoveAnchor); cr.movePosition (QTextCursor::Right, QTextCursor::KeepAnchor, d->text_to_search.length()); if (! cr.isNull()) d->setTextCursor (cr); } return; } else //normal search cr = d->document()->find (d->text_to_search, from, get_search_options()); if (! cr.isNull()) d->setTextCursor (cr); else log->log(tr ("not found!")); } else if (main_tab_widget->currentIndex() == idx_tab_learn) man_find_find(); else if (main_tab_widget->currentIndex() == idx_tab_tune) opt_shortcuts_find(); else if (main_tab_widget->currentIndex() == idx_tab_fman) fman_find(); } void CTEA::search_find_next() { last_action = sender(); if (main_tab_widget->currentIndex() == idx_tab_edit) { CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr; if (menu_find_regexp->isChecked()) #if QT_VERSION >= 0x050500 if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegularExpression (d->text_to_search), d->textCursor().position(), get_search_options()); #else //#if QT_VERSION < 0x050500 if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegExp (d->text_to_search), d->textCursor().position(), get_search_options()); //#endif #endif if (menu_find_fuzzy->isChecked()) { int pos = str_fuzzy_search (d->toPlainText(), d->text_to_search, d->textCursor().position(), settings->value ("fuzzy_q", "60").toInt()); if (pos != -1) { cr = d->textCursor(); cr.setPosition (pos, QTextCursor::MoveAnchor); cr.movePosition (QTextCursor::Right, QTextCursor::KeepAnchor, d->text_to_search.length()); if (! cr.isNull()) d->setTextCursor (cr); } return; } else cr = d->document()->find (d->text_to_search, d->textCursor().position(), get_search_options()); if (! cr.isNull()) d->setTextCursor (cr); } else if (main_tab_widget->currentIndex() == idx_tab_learn) man_find_next(); else if (main_tab_widget->currentIndex() == idx_tab_tune) opt_shortcuts_find_next(); else if (main_tab_widget->currentIndex() == idx_tab_fman) fman_find_next(); } void CTEA::search_find_prev() { last_action = sender(); if (main_tab_widget->currentIndex() == idx_tab_edit) { CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr; #if QT_VERSION >= 0x050500 if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegularExpression (d->text_to_search), d->textCursor().position(), get_search_options() | QTextDocument::FindBackward); #endif #if QT_VERSION < 0x050500 if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegExp (d->text_to_search), d->textCursor().position(), get_search_options() | QTextDocument::FindBackward); #endif if (! menu_find_regexp->isChecked()) cr = d->document()->find (d->text_to_search, d->textCursor(), get_search_options() | QTextDocument::FindBackward); if (! cr.isNull()) d->setTextCursor (cr); } else if (main_tab_widget->currentIndex() == idx_tab_learn) man_find_prev(); else if (main_tab_widget->currentIndex() == idx_tab_tune) opt_shortcuts_find_prev(); else if (main_tab_widget->currentIndex() == idx_tab_fman) fman_find_prev(); } void CTEA::search_mark_all() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; int darker_val = settings->value ("darker_val", 100).toInt(); QString text_color = hash_get_val (global_palette, "text", "black"); QString back_color = hash_get_val (global_palette, "background", "white"); QString t_text_color = QColor (text_color).darker(darker_val).name(); QString t_back_color = QColor (back_color).darker(darker_val).name(); bool cont_search = true; int pos_save = d->textCursor().position(); d->selectAll(); QTextCharFormat f = d->currentCharFormat(); f.setBackground (QColor (t_back_color)); f.setForeground (QColor (t_text_color)); d->mergeCurrentCharFormat (f); d->textCursor().clearSelection(); int from; if (settings->value ("find_from_cursor", "1").toBool()) from = d->textCursor().position(); else from = 0; d->text_to_search = fif_get_text(); QTextCursor cr; while (cont_search) { #if (QT_VERSION_MAJOR < 5) if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegExp (d->text_to_search), from, get_search_options()); #else if (menu_find_regexp->isChecked()) cr = d->document()->find (QRegularExpression (d->text_to_search), from, get_search_options()); #endif else if (menu_find_fuzzy->isChecked()) //fuzzy search { int pos = str_fuzzy_search (d->toPlainText(), d->text_to_search, from, settings->value ("fuzzy_q", "60").toInt()); if (pos != -1) { //set selection: cr = d->textCursor(); cr.setPosition (pos, QTextCursor::MoveAnchor); cr.movePosition (QTextCursor::Right, QTextCursor::KeepAnchor, d->text_to_search.length()); if (! cr.isNull()) d->setTextCursor (cr); } else cont_search = false; } else //normal search cr = d->document()->find (d->text_to_search, from, get_search_options()); if (! cr.isNull()) { d->setTextCursor (cr); QTextCharFormat fm = cr.blockCharFormat(); fm.setBackground (QColor (hash_get_val (global_palette, "backgroundmark", "red"))); fm.setForeground (QColor (hash_get_val (global_palette, "foregroundmark", "blue"))); cr.mergeCharFormat (fm); d->setTextCursor (cr); } else cont_search = false; from = d->textCursor().position(); } d->document()->setModified (false); d->goto_pos (pos_save); } void CTEA::search_unmark() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; int darker_val = settings->value ("darker_val", 100).toInt(); QString text_color = hash_get_val (global_palette, "text", "black"); QString back_color = hash_get_val (global_palette, "background", "white"); QString t_text_color = QColor (text_color).darker(darker_val).name(); QString t_back_color = QColor (back_color).darker(darker_val).name(); d->selectAll(); QTextCharFormat f = d->currentCharFormat(); f.setBackground (QColor (t_back_color)); f.setForeground (QColor (t_text_color)); d->mergeCurrentCharFormat (f); d->textCursor().clearSelection(); } void CTEA::search_in_files_results_dclicked (QListWidgetItem *item) { documents->open_file_triplex (item->text()); main_tab_widget->setCurrentIndex (idx_tab_edit); } void CTEA::search_in_files() { last_action = sender(); if (main_tab_widget->currentIndex() != idx_tab_fman) return; QString text_to_search = fif_get_text(); if (text_to_search.isEmpty()) return; QStringList lresult; QString search_charset = cb_fman_codecs->currentText(); QString path = fman->dir.path(); CFTypeChecker fc; progress_show(); log->log (tr ("Getting files list...")); qApp->processEvents(); CFilesList lf; lf.get (path); log->log (tr ("Searching...")); qApp->processEvents(); //pb_status->show(); progress_show(); pb_status->setRange (0, lf.list.size()); pb_status->setFormat (tr ("%p% completed")); pb_status->setTextVisible (true); for (int i = 0; i < lf.list.size(); i++) { if (i % 100 == 0) qApp->processEvents(); if (boring) break; pb_status->setValue (i); QString fname = lf.list[i]; if (! fc.check (fname)) continue; log->log (fname); CTio *tio = documents->tio_handler.get_for_fname (fname); tio->charset = search_charset; if (! tio->load (fname)) log->log (tr ("cannot open %1 because of: %2") .arg (fname) .arg (tio->error_string)); Qt::CaseSensitivity cs = Qt::CaseInsensitive; if (menu_find_case->isChecked()) cs = Qt::CaseSensitive; int index = tio->data.indexOf (text_to_search, 0, cs); if (index != -1) lresult.append (fname + "*" + charset + "*" + QString::number (index)); } progress_hide(); CTextListWnd *w = new CTextListWnd (tr ("Search results"), tr ("Files")); w->move (this->x(), this->y()); w->list->addItems (lresult); connect (w->list, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(search_in_files_results_dclicked(QListWidgetItem*))); w->resize (width() - 10, (int) height() / 2); w->show(); } void CTEA::search_whole_words_mode() { menu_find_fuzzy->setChecked (false); } void CTEA::search_from_cursor_mode() { settings->setValue ("find_from_cursor", menu_find_from_cursor->isChecked()); } void CTEA::search_regexp_mode() { menu_find_fuzzy->setChecked (false); } void CTEA::search_fuzzy_mode() { menu_find_whole_words->setChecked (false); menu_find_regexp->setChecked (false);; } void CTEA::search_replace_with() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (fif_get_text()); } void CTEA::search_replace_all() { last_action = sender(); Qt::CaseSensitivity cs = Qt::CaseInsensitive; if (menu_find_case->isChecked()) cs = Qt::CaseSensitive; QStringList l = fif_get_text().split ("~"); if (l.size() < 2) return; if (main_tab_widget->currentIndex() == idx_tab_edit) { CDocument *d = documents->get_current(); if (! d) return; QString s = d->get(); #if (QT_VERSION_MAJOR < 5) if (menu_find_regexp->isChecked()) s = s.replace (QRegExp (l[0]), l[1]); else s = s.replace (l[0], l[1], cs); #else if (menu_find_regexp->isChecked()) s = s.replace (QRegularExpression (l[0]), l[1]); else s = s.replace (l[0], l[1], cs); #endif d->put (s); } else if (main_tab_widget->currentIndex() == idx_tab_fman) { QStringList sl = fman->get_sel_fnames(); if (sl.size() < 1) return; //QString search_charset = cb_fman_codecs->currentText(); for (QList ::iterator fname = sl.begin(); fname != sl.end(); ++fname) { QString f = qstring_load ((*fname)/*, search_charset*/); QString r; #if (QT_VERSION_MAJOR < 5) if (menu_find_regexp->isChecked()) r = f.replace (QRegExp (l[0]), l[1]); else r = f.replace (l[0], l[1], cs); #else if (menu_find_regexp->isChecked()) r = f.replace (QRegularExpression (l[0]), l[1]); else r = f.replace (l[0], l[1], cs); #endif qstring_save ((*fname), r/*, search_charset*/); log->log (tr ("%1 is processed and saved").arg ((*fname))); } } } void CTEA::search_replace_all_at_ofiles() { last_action = sender(); QStringList l = fif_get_text().split ("~"); if (l.size() < 2) return; int c = documents->items.size(); if (c == 0) return; Qt::CaseSensitivity cs = Qt::CaseInsensitive; if (menu_find_case->isChecked()) cs = Qt::CaseSensitive; for (vector ::size_type i = 0; i < documents->items.size(); i++) { CDocument *d = documents->items[i]; QString s; #if QT_VERSION < 0x050000 if (menu_find_regexp->isChecked()) s = d->toPlainText().replace (QRegExp (l[0]), l[1]); else s = d->toPlainText().replace (l[0], l[1], cs); #else if (menu_find_regexp->isChecked()) s = d->toPlainText().replace (QRegularExpression (l[0]), l[1]); else s = d->toPlainText().replace (l[0], l[1], cs); #endif d->selectAll(); d->put (s); } } /* =================== Fn menu callbacks =================== */ void CTEA::fn_repeat() { if (last_action) qobject_cast(last_action)->trigger(); } void CTEA::fn_scale_image() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString fname = d->get_filename_at_cursor(); if (! is_image (fname)) return; QImage source (fname); if (source.isNull()) return; QString t = fif_get_text(); if (t.indexOf ("~") == -1) return; QFileInfo fi (fname); QStringList params = t.split ("~"); if (params.size() < 2) { log->log (tr("Incorrect parameters at FIF")); return; } QString fnameout = params[0].replace ("%filename", fi.fileName()); fnameout = fnameout.replace ("%basename", fi.baseName()); fnameout = fnameout.replace ("%s", fname); fnameout = fnameout.replace ("%ext", fi.suffix()); fnameout = fi.absolutePath() + "/" + fnameout; bool scale_by_side = true; if (params[1].indexOf("%") != -1) scale_by_side = false; int side = 800; int percent = 100; if (scale_by_side) side = params[1].toInt(); else { params[1].chop (1); percent = params[1].toInt(); } Qt::TransformationMode transformMode = Qt::FastTransformation; if (settings->value ("img_filter", 0).toBool()) transformMode = Qt::SmoothTransformation; int quality = settings->value ("img_quality", "-1").toInt(); if (settings->value ("cb_exif_rotate", 1).toBool()) { int exif_orientation = get_exif_orientation (fname); QTransform transform; qreal angle = 0; if (exif_orientation == 3) angle = 180; else if (exif_orientation == 6) angle = 90; else if (exif_orientation == 8) angle = 270; if (angle != 0) { transform.rotate (angle); source = source.transformed (transform); } } QImage dest; if (scale_by_side && side) dest = image_scale_by (source, true, side, transformMode); else if (percent) dest = image_scale_by (source, false, percent, transformMode); QString fmt (settings->value ("output_image_fmt", "jpg").toString()); fnameout = change_file_ext (fnameout, fmt); if (! dest.save (fnameout, fmt.toLatin1().constData(), quality)) log->log (tr("Cannot save: %1").arg (fnameout)); else log->log (tr("Saved: %1").arg (fnameout)); } void CTEA::fn_use_snippet() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QAction *a = qobject_cast(sender()); QString s = qstring_load (a->data().toString()); if (s.isEmpty()) return; if (s.contains ("%s")) s = s.replace ("%s", d->get()); d->put (s); } void CTEA::view_use_keyboard() { last_action = sender(); QAction *a = qobject_cast(sender()); QString fname = a->data().toString(); QWidget *w = create_keyboard (fname); if (w) w->show(); } #if QT_VERSION >= 0x051400 void CTEA::view_preview_md() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; //qDebug() << d->toPlainText(); md_viewer.setMarkdown (d->toPlainText()); md_viewer.show(); } #endif void CTEA::fn_run_script() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QAction *a = qobject_cast(sender()); QString fname = a->data().toString(); QString ext = file_get_ext (fname); if (! d->textCursor().hasSelection()) return; QString intrp; if (ext == "rb") intrp = "ruby"; else if (ext == "py") intrp = "python"; else if (ext == "pl") intrp = "perl"; else if (ext == "sh") intrp = "sh"; else if (ext == "lua") intrp = "lua"; else if (ext == "bat" || ext == "btm" || ext == "cmd") intrp = "cmd.exe"; if (intrp.isEmpty()) return; qstring_save (fname_tempfile, d->get()); qstring_save (fname_tempparamfile, fif_get_text()); QString command = QString ("%1 %2 %3 %4").arg ( intrp).arg ( fname).arg ( fname_tempfile).arg ( fname_tempparamfile); QProcess *process = new QProcess (this); connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(cb_script_finished(int,QProcess::ExitStatus))); //process->start (command, QStringList()); #if QT_VERSION >= 0x060000 process->startCommand (command); #else process->start (command); #endif } void CTEA::cb_script_finished (int exitCode, QProcess::ExitStatus exitStatus) { CDocument *d = documents->get_current(); if (! d) return; QString s = qstring_load (fname_tempfile); if (! s.isEmpty()) d->put(s); QFile f (fname_tempfile); f.remove(); f.setFileName (fname_tempparamfile); f.remove(); } void CTEA::fn_use_table() { last_action = sender(); QAction *a = qobject_cast(sender()); if (main_tab_widget->currentIndex() == idx_tab_edit) { CDocument *d = documents->get_current(); if (! d) return; QString text; if (d->textCursor().hasSelection()) text = d->get(); else text = d->toPlainText(); if (d->textCursor().hasSelection()) d->put (apply_table (text, a->data().toString(), menu_find_regexp->isChecked())); else d->setPlainText (apply_table (text, a->data().toString(), menu_find_regexp->isChecked())); } else if (main_tab_widget->currentIndex() == idx_tab_fman) { QStringList sl = fman->get_sel_fnames(); if (sl.size() < 1) return; //char *charset = cb_fman_codecs->currentText().toLatin1().data(); // QString search_charset = cb_fman_codecs->currentText(); for (QList ::const_iterator fname = sl.begin(); fname != sl.end(); ++fname) { QString f = qstring_load ((*fname)/*, search_charset*/); QString r = apply_table (f, a->data().toString(), menu_find_regexp->isChecked()); qstring_save ((*fname), r/*, search_charset*/); log->log (tr ("%1 is processed and saved").arg ((*fname))); } } } void CTEA::fn_insert_loremipsum() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstring_load (":/text-data/lorem-ipsum")); } void CTEA::fn_insert_template_tea() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstring_load (":/text-data/template-teaproject")); } void CTEA::fn_insert_template_html() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstring_load (":/text-data/template-html")); } void CTEA::fn_insert_template_html5() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstring_load (":/text-data/template-html5")); } void CTEA::fn_insert_cpp() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstring_load (":/text-data/tpl_cpp.cpp")); } void CTEA::fn_insert_c() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstring_load (":/text-data/tpl_c.c")); } void CTEA::fn_insert_date() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (QDate::currentDate ().toString (settings->value("date_format", "dd/MM/yyyy").toString())); } void CTEA::fn_insert_time() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (QTime::currentTime ().toString (settings->value("time_format", "hh:mm:ss").toString())); } void CTEA::fn_case_up() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (d->get().toUpper()); } void CTEA::fn_case_down() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (d->get().toLower()); } void CTEA::fn_case_inverse() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! d->has_selection()) return; QString s = d->get(); for (int i = 0; i < s.size(); i++) { if (s[i].isUpper()) s[i] = s[i].toLower(); else s[i] = s[i].toUpper(); } d->put (s); } void CTEA::fn_case_cap_sentences() { last_action = sender(); CDocument *d = documents->get_current(); if (!d) return; QString t = d->toPlainText(); bool cap = false; for (int i = 0; i < t.size(); i++) { QChar c = t.at(i); if (c.isDigit()) { cap = false; //probably timecode continue; } if (c == '.' || c == '?' || c == '!') { cap = true; continue; } if (t.at(i).isLetter() && cap) { t[i] = t.at(i).toUpper(); cap = false; } } d->put (t); } void CTEA::fn_sort_casecare() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_WITH_SORTCASECARE)); } void CTEA::fn_sort_casecareless() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_WITH_SORTNOCASECARE)); } void CTEA::fn_sort_casecare_sep() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_WITH_SORTCASECARE_SEP)); } void CTEA::fn_sort_length() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_WITH_SORTLEN)); } void CTEA::fn_flip_a_list() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_LIST_FLIP)); } void CTEA::fn_flip_a_list_sep() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_LIST_FLIP_SEP)); } int latex_table_sort_col; bool latex_table_sort_fn (const QStringList &l1, const QStringList &l2) { return l1.at (latex_table_sort_col) < l2.at (latex_table_sort_col); } void CTEA::fn_cells_latex_table_sort_by_col_abc() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString t = d->get(); if (t.isEmpty()) return; QStringList fiftxt = fif_get_text().split("~"); if (fiftxt.size() < 2) return; QString sep = fiftxt[0]; latex_table_sort_col = fiftxt[1].toInt(); if (t.indexOf (sep) == -1) return; QStringList sl_temp = t.split (QChar::ParagraphSeparator); QList output; for (QList ::iterator s = sl_temp.begin(); s != sl_temp.end(); ++s) { if (! s->isEmpty()) { QStringList sl_parsed = s->split (sep); if (latex_table_sort_col + 1 <= sl_parsed.size()) output.append (sl_parsed); } } std::sort (output.begin(), output.end(), latex_table_sort_fn); sl_temp.clear(); for (int i = 0; i < output.size(); i++) { sl_temp.append (output.at(i).join (sep)); } t = sl_temp.join ("\n"); d->put (t); } void CTEA::fn_cells_swap_cells() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList fiftxt = fif_get_text().split("~"); if (fiftxt.size() < 3) return; int col1 = fiftxt[1].toInt(); int col2 = fiftxt[2].toInt(); QString sep = fiftxt[0]; QString t = d->get(); if (t.isEmpty()) return; if (t.indexOf (sep) == -1) return; int imax = int (fmax (col1, col2)); QStringList sl_temp = t.split (QChar::ParagraphSeparator); QList output; for (QList ::iterator v = sl_temp.begin(); v != sl_temp.end(); ++v) { if (! v->isEmpty()) { QStringList sl_parsed = v->split (sep); if (imax + 1 <= sl_parsed.size()) { strlist_swap (sl_parsed, col1, col2); output.append (sl_parsed); } } } sl_temp.clear(); for (int i = 0; i < output.size(); i++) sl_temp.append (output.at(i).join (sep)); t = sl_temp.join ("\n"); d->put (t); } void CTEA::fn_cells_delete_by_col() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList fiftxt = fif_get_text().split("~"); if (fiftxt.size() < 2) return; int col1 = fiftxt[1].toInt(); QString sep = fiftxt[0]; QString t = d->get(); if (t.isEmpty()) return; if (t.indexOf (sep) == -1) return; QStringList sl_temp = t.split (QChar::ParagraphSeparator); QList output; for (QList ::iterator v = sl_temp.begin(); v != sl_temp.end(); ++v) { if (! v->isEmpty()) { QStringList sl_parsed = v->split (sep); if (col1 + 1 <= sl_parsed.size()) { sl_parsed.removeAt (col1); output.append (sl_parsed); } } } sl_temp.clear(); for (int i = 0; i < output.size(); i++) sl_temp.append (output.at(i).join (sep)); t = sl_temp.join ("\n"); d->put (t); } void CTEA::fn_cells_copy_by_col() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList fiftxt = fif_get_text().split("~"); if (fiftxt.size() < 2) return; QString sep = fiftxt[0]; int col1 = fiftxt[1].toInt(); int col2 = 0; if (fiftxt.size() == 3) col2 = fiftxt[2].toInt(); QString t = d->get(); if (t.isEmpty()) return; if (t.indexOf (sep) == -1) return; QStringList sl_temp = t.split (QChar::ParagraphSeparator); QList output; if (col2 > 0) for (QList ::iterator v = sl_temp.begin(); v != sl_temp.end(); ++v) { if (! v->isEmpty()) { QStringList sl_parsed = v->split (sep); if (col2 + 1 <= sl_parsed.size()) { QStringList tl = sl_parsed.mid (col1, col2 - col1 + 1); output.append (tl); } } } else for (QList ::iterator v = sl_temp.begin(); v != sl_temp.end(); ++v) { if (! v->isEmpty()) { QStringList sl_parsed = v->split (sep); if (col1 + 1 <= sl_parsed.size()) { QStringList tl = sl_parsed.mid (col1, 1); output.append (tl); } } } sl_temp.clear(); for (int i = 0; i < output.size(); i++) sl_temp.append (output.at(i).join (sep)); t = sl_temp.join ("\n"); QApplication::clipboard()->setText (t); } void CTEA::fn_filter_rm_duplicates() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_REMOVE_DUPS)); } void CTEA::fn_filter_rm_empty() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_REMOVE_EMPTY)); } void CTEA::fn_filter_rm_less_than() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_LESS)); } void CTEA::fn_filter_rm_greater_than() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_GREATER)); } void CTEA::fn_filter_delete_before_sep() { last_action = sender(); fn_filter_delete_by_sep (true); } void CTEA::fn_filter_delete_after_sep() { last_action = sender(); fn_filter_delete_by_sep (false); } void CTEA::fn_filter_with_regexp() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), fif_get_text(), QSTRL_PROC_FLT_WITH_REGEXP)); } void CTEA::fn_filter_by_repetitions() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString result; QString pattern = fif_get_text(); vector positions; for (int i = 0; i < pattern.size(); ++i) { if (pattern[i] == '1') positions.push_back (i); } QStringList words = d->get().split (QChar::ParagraphSeparator); for (int i = 0; i < words.size(); ++i) { QString wrd = words[i]; if (pattern.size() > wrd.size()) continue; QChar ch = wrd [positions[0]]; size_t count = 0; for (size_t j = 0; j < positions.size(); ++j) { if (wrd[positions[j]] == ch) count++; } if (count == positions.size()) { result += wrd; result += "\n"; } } if (! result.isEmpty()) d->put (result); } void CTEA::fn_math_evaluate() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString s = d->get(); std::string utf8_text = s.toUtf8().constData(); double f = calculate (utf8_text); QString fs = s.setNum (f); log->log (fs); } void CTEA::fn_math_number_arabic_to_roman() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (arabicToRoman (d->get().toUInt())); } void CTEA::fn_math_number_roman_to_arabic() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (QString::number(get_arab_num (d->get().toUpper().toStdString()))); } void CTEA::fn_math_number_dec_to_bin() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (int_to_binary (d->get().toInt())); } void CTEA::fn_math_number_bin_to_dec() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (QString::number (bin_to_decimal (d->get()))); } void CTEA::fn_math_number_flip_bits() { last_action = sender(); CDocument *d = documents->get_current(); if (!d) return; QString s = d->get(); for (int i = 0; i < s.size(); i++) { if (s[i] == '1') s[i] = '0'; else if (s[i] == '0') s[i] = '1'; } d->put (s); } void CTEA::fn_math_sum_by_last_col() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString t = d->get(); if (t.isEmpty()) return; t = t.replace (",", "."); double sum = 0.0; QStringList l = t.split (QChar::ParagraphSeparator); for (int i = 0; i < l.size(); i++) { if (l[i].isNull()) continue; if (l[i].startsWith ("//") || l[i].startsWith ("#") || l[i].startsWith (";")) continue; QStringList lt = l[i].split (" "); if (lt.size() > 0) { const QString s = lt.at (lt.size() - 1); if (! s.isNull()) { std::string utf8_text = s.toUtf8().constData(); double f = calculate (utf8_text); sum += f; } } } log->log (tr ("sum: %1").arg (sum)); } void CTEA::fn_math_enum() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList source = d->get().split (QChar::ParagraphSeparator); int pad = 0; int end = source.size() - 1; int step = 1; QString result; QString prefix; QStringList params = fif_get_text().split ("~"); if (params.size() > 0) step = params[0].toInt(); if (params.size() > 1) pad = params[1].toInt(); if (params.size() > 2) prefix = params[2]; if (step == 0) step = 1; for (int c = 0; c <= end; c++) { QString n; n = n.setNum (((c + 1) * step)); if (pad != 0) n = n.rightJustified (pad, '0'); result = result + n + prefix + source.at(c) + '\n'; } d->put (result); } //UTF-16BE, UTF-32BE //′ //#define UQS 8242 //″ //#define UQD 8243 //° //#define UQDG 176 //degrees minutes seconds: 40° 26′ 46″ N 79° 58′ 56″ W //to //decimal degrees: 40.446° N 79.982° W void CTEA::fn_math_number_dms2dc() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString t = d->get(); t = t.remove (" "); t = t.replace ('\'', QChar (UQS)); t = t.replace ('"', QChar (UQD)); QChar north_or_south = ('N'); if (t.contains ('S')) north_or_south = 'S'; QChar east_or_west = ('E'); if (t.contains ('W')) east_or_west = 'W'; QStringList l = t.split (north_or_south); QString latitude = l[0]; QString longtitude = l[1]; // qDebug() << "latitude " << latitude; // qDebug() << "longtitude " << longtitude; int iqdg = latitude.indexOf (QChar (UQDG)); int iqs = latitude.indexOf (QChar (UQS)); int iqd = latitude.indexOf (QChar (UQD)); QString degrees1 = latitude.left (iqdg); QString minutes1 = latitude.mid (iqdg + 1, iqs - iqdg - 1); QString seconds1 = latitude.mid (iqs + 1, iqd - iqs - 1); double lat_decimal_degrees = degrees1.toDouble() + (double) (minutes1.toDouble() / 60) + (double) (seconds1.toDouble() / 3600); QString lat_decimal_degrees_N = QString::number (lat_decimal_degrees, 'f', 3) + QChar (UQDG) + north_or_south; iqdg = longtitude.indexOf (QChar (UQDG)); iqs = longtitude.indexOf (QChar (UQS)); iqd = longtitude.indexOf (QChar (UQD)); degrees1 = longtitude.left (iqdg); minutes1 = longtitude.mid (iqdg + 1, iqs - iqdg - 1); seconds1 = longtitude.mid (iqs + 1, iqd - iqs - 1); double longt_decimal_degrees = degrees1.toDouble() + (double) (minutes1.toDouble() / 60) + (double) (seconds1.toDouble() / 3600); QString longt_decimal_degrees_N = QString::number (longt_decimal_degrees, 'f', 3) + QChar (UQDG) + east_or_west; log->log (lat_decimal_degrees_N + " " + longt_decimal_degrees_N); // qDebug() << "decimal_degrees " << decimal_degrees; // qDebug() << "decimal_degrees_N " << decimal_degrees_N; } /* degrees = floor (decimal_degrees) minutes = floor (60 * (decimal_degrees - degrees)) seconds = 3600 * (decimal_degrees - degrees) - 60 * minites */ //decimal degrees: 40.446° N 79.982° W //to //degrees minutes seconds: 40° 26′ 46″ N 79° 58′ 56″ W void CTEA::fn_math_number_dd2dms() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString t = d->get(); t = t.remove (" "); t = t.remove (QChar (UQDG)); QChar north_or_south = ('N'); if (t.contains ('S')) north_or_south = 'S'; QChar east_or_west = ('E'); if (t.contains ('W')) east_or_west = 'W'; QStringList l = t.split (north_or_south); QString latitude = l[0]; #if QT_VERSION < 0x050000 QString longtitude = l[1].remove (QRegExp("[a-zA-Z\\s]")); #else QString longtitude = l[1].remove (QRegularExpression("[a-zA-Z\\s]")); #endif double degrees = floor (latitude.toDouble()); double minutes = floor (60 * (latitude.toDouble() - degrees)); double seconds = round (3600 * (latitude.toDouble() - degrees) - 60 * minutes); double degrees2 = floor (longtitude.toDouble()); double minutes2 = floor (60 * (longtitude.toDouble() - degrees2)); double seconds2 = round (3600 * (longtitude.toDouble() - degrees2) - 60 * minutes2); QString result = QString::number (degrees) + QChar (UQDG) + QString::number (minutes) + QChar (UQS) + QString::number (seconds) + QChar (UQD) + north_or_south + " " + QString::number (degrees2) + QChar (UQDG) + QString::number (minutes2) + QChar (UQS) + QString::number (seconds2) + QChar (UQD) + east_or_west; log->log (result); } #if QT_VERSION >= 0x060000 void CTEA::fn_math_srt_shift() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString s_msecs = fif_get_text(); if (s_msecs.isEmpty()) return; int msecs = s_msecs.toInt(); QString text = d->get(); if (text.isEmpty()) return; QString output = text; QRegularExpression re ("\\d{1,}:\\d{1,}:\\d{1,}(\\,|\\.)\\d{1,}"); QString format; if (d->file_name.endsWith (".sbv")) format = "h:mm:ss.zzz"; else format = "hh:mm:ss,zzz"; QRegularExpressionMatchIterator i = re.globalMatch (text); while (i.hasNext()) { QRegularExpressionMatch match = i.next(); QString t_in (match.captured()); QTime tm = QTime::fromString (t_in, format); if (! tm.isValid()) continue; tm = tm.addMSecs (msecs); QString t_out = tm.toString (format); output.replace (t_in, t_out); } d->put (output); } #endif void CTEA::fn_morse_from_ru() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (morse_from_lang (d->get().toUpper(), "ru")); } void CTEA::fn_morse_to_ru() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (morse_to_lang (d->get(), "ru")); } void CTEA::fn_morse_from_en() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (morse_from_lang (d->get().toUpper(), "en")); } void CTEA::fn_morse_to_en() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (morse_to_lang (d->get(), "en")); } void CTEA::fn_analyze_text_stat() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; bool b_sel = d->textCursor().hasSelection(); QString s; if (b_sel) s = d->get(); else s = d->toPlainText(); int c = s.length(); int purechars = 0; int lines = 1; for (int i = 0; i < c; ++ i) { QChar ch = s.at (i); if (ch.isLetterOrNumber() || ch.isPunct()) purechars++; if (! b_sel) { if (ch == '\n') lines++; } else if (ch == QChar::ParagraphSeparator) lines++; } QString result = tr ("chars: %1
chars without spaces: %2
lines: %3
author's sheets: %4") .arg (QString::number (c)) .arg (QString::number (purechars)) .arg (QString::number (lines)) .arg (QString::number (c / 40000)); log->log (result); } void CTEA::fn_analyze_extract_words() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList w = d->get_words(); CDocument *nd = documents->create_new(); if (nd) nd->put (w.join("\n")); } void CTEA::fn_analyze_stat_words_lengths() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; unsigned long lengths[33] = { }; QStringList w = d->get_words(); for (int i =0; i < w.size(); i++) { int len = w.at(i).length(); if (len <= 32) lengths[len]++; } QStringList l; QString col1 = tr ("Word length: "); QString col2 = tr ("Number:"); l.append (col1 + col2); for (int i = 1; i <= 32; i++) { QString s = QString::number (i) + col1.fill ('_', col1.length()) + QString::number (lengths[i]); l.append (s); } CDocument *nd = documents->create_new(); if (nd) nd->put (l.join("\n")); } void CTEA::fn_analyze_count() { last_action = sender(); count_substring (false); } void CTEA::fn_analyze_count_rx() { last_action = sender(); count_substring (true); } bool pr_bigger_than (const pair &a, const pair &b) { return (a.second > b.second); } bool pr_bigger_than_str (const pair &a, const pair &b) { return (a.first < b.first); } bool pr_bigger_than_str_len (const pair &a, const pair &b) { return (a.first.size() < b.first.size()); } void CTEA::fn_analyze_get_words_count() { last_action = sender(); run_unitaz (0); } void CTEA::fn_analyze_unitaz_abc() { last_action = sender(); run_unitaz (1); } void CTEA::fn_analyze_unitaz_len() { last_action = sender(); run_unitaz (2); } void CTEA::fn_text_apply_to_each_line() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList sl = d->get().split (QChar::ParagraphSeparator); QString t = fif_get_text(); if (t.isEmpty()) return; if (t.startsWith ("@@")) { QString fname = dir_snippets + QDir::separator() + t; if (! file_exists (fname)) { log->log (tr ("snippet %1 is not exists").arg (fname)); return; } t = t.remove (0, 2); t = qstring_load (fname); } for (QList ::iterator i = sl.begin(); i != sl.end(); ++i) { QString ts (t); (*i) = ts.replace ("%s", (*i)); } QString x = sl.join ("\n"); d->put (x); } void CTEA::fn_text_reverse() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString s = d->get(); if (! s.isEmpty()) d->put (string_reverse (s)); } void CTEA::fn_text_escape() { last_action = sender(); CDocument *d = documents->get_current(); #if (QT_VERSION_MAJOR < 5) if (d) d->put (QRegExp::escape (d->get())); #else if (d) d->put (QRegularExpression::escape (d->get())); #endif } void CTEA::fn_text_remove_formatting() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (d->get().simplified()); } void CTEA::fn_text_compress() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString s = d->get(); s = s.remove ('\n'); s = s.remove ('\t'); s = s.remove (' '); s = s.remove (QChar::ParagraphSeparator); d->put (s); } void CTEA::fn_text_compare_two_strings() { last_action = sender(); QStringList l = fif_get_text().split ("~"); if (l.size() < 2) return; if (l[0].size() < l[1].size()) return; QString s; for (int i = 0; i < l[0].size(); i++) { if (l[0][i] == l[1][i]) s = QString::number (i + 1) + ": " + l[0][i] + " == " + l[1][i]; else s = QString::number (i + 1) + ": " + l[0][i] + " != " + l[1][i]; log->log (s); } } void CTEA::fn_text_remove_formatting_at_each_line() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->put (qstringlist_process (d->get(), "", QSTRL_PROC_REMOVE_FORMATTING)); } void CTEA::fn_text_remove_trailing_spaces() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QStringList sl = d->get().split (QChar::ParagraphSeparator); for (QList ::iterator s = sl.begin(); s != sl.end(); ++s) { if (s->isEmpty()) continue; if (s->at (s->size() - 1).isSpace()) { int index = s->size() - 1; while (s->at (--index).isSpace()) ; s->truncate (index + 1); } } QString x = sl.join ("\n"); d->put (x); } void CTEA::fn_text_anagram() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString t = d->get(); if (t.isEmpty()) return; QString txt = anagram (t).join("\n"); d = documents->create_new(); if (d) d->put (txt); } void CTEA::fn_text_regexp_match_check() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString t = d->get(); if (t.isEmpty()) return; QString fiftxt = fif_get_text(); #if QT_VERSION >= 0x050000 QRegularExpression r (fiftxt); QRegularExpressionMatch match = r.match(t); if (match.hasMatch()) log->log (tr ("matched")); else log->log (tr ("does not")); #else QRegExp r (fiftxt); if (r.exactMatch(t)) log->log (tr ("matched")); else log->log (tr ("does not")); #endif } void CTEA::fn_quotes_to_angle() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString source = d->get(); if (! source.isEmpty()) d->put (conv_quotes (source, "\u00AB", "\u00BB")); } void CTEA::fn_quotes_curly() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString source = d->get(); if (! source.isEmpty()) d->put (conv_quotes (source, "\u201C", "\u201D")); } void CTEA::fn_quotes_tex_curly() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString source = d->get(); if (! source.isEmpty()) d->put (conv_quotes (source, "``", "\'\'")); } void CTEA::fn_quotes_tex_angle_01() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString source = d->get(); if (! source.isEmpty()) d->put (conv_quotes (source, "<<", ">>")); } void CTEA::fn_quotes_tex_angle_02() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QString source = d->get(); if (! source.isEmpty()) d->put (conv_quotes (source, "\\glqq", "\\grqq")); } #ifdef SPEECH_ENABLE void CTEA::fn_speech_say_selection() { last_action = sender(); if (! speech.initialized) return; // qDebug() << "speech.locale_name: " << speech.locale_name; // qDebug() << "speech.language_name: " << speech.language_name; // qDebug() << "voice: " << speech.voices [speech.current_voice_index]; CDocument *d = documents->get_current(); if (! d) return; QString text = d->get(); if (text.isEmpty()) return; speech.say (text.toUtf8().data()); } void CTEA::fn_speech_stop() { last_action = sender(); if (! speech.initialized) return; CDocument *d = documents->get_current(); if (! d) return; //QString text = d->get(); //if (text.isEmpty()) // return; speech.stop(); } void CTEA::fn_speech_pause_resume() { last_action = sender(); if (! speech.initialized) return; if (g_state == SPCH_STATE_PAUSED) speech.resume(); else speech.pause(); //speech.stop(); } void CTEA::cb_locale_only_stateChanged (int state) { if (state == Qt::Unchecked) speech.locale_only = 0; else speech.locale_only = 1; speech.get_voices (speech.locale_only); speech.current_voice_index = 0; if (speech.voices.size() != 0) { cmb_cpeech_voices->clear(); for (int i = 0; i < speech.voices.size(); i++) { QString item = QString::fromStdString (speech.voices.at(i)); cmb_cpeech_voices->addItem(item); } } speech.set_voice_by_index (0); cmb_cpeech_voices->setCurrentIndex (0); } #endif #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) || defined (NUSPELL_ENABLE) void CTEA::fn_change_spell_lang() { last_action = sender(); QAction *a = qobject_cast(sender()); settings->setValue ("spell_lang", a->data().toString()); spellchecker->change_lang (a->data().toString()); spellchecker->load_dict(); fn_spell_check(); } bool ends_with_badchar (const QString &s) { if (s.endsWith ("\"")) return true; if (s.endsWith ("»")) return true; if (s.endsWith ("\\")) return true; return false; } void CTEA::fn_spell_check() { last_action = sender(); if (spellchecker->modules_list.size() == 0) { QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page")); return; } CDocument *d = documents->get_current(); if (! d) return; QColor color_error = QColor (hash_get_val (global_palette, "error", "red")); QElapsedTimer time_start; time_start.start(); pb_status->show(); pb_status->setRange (0, d->toPlainText().size() - 1); pb_status->setFormat (tr ("%p% completed")); pb_status->setTextVisible (true); int i = 0; QTextCursor cr = d->textCursor(); int pos = cr.position(); int savepos = pos; QString text = d->toPlainText(); int text_size = text.size(); //delete all underlines cr.setPosition (0); cr.movePosition (QTextCursor::End, QTextCursor::KeepAnchor); QTextCharFormat f = cr.blockCharFormat(); f.setFontUnderline (false); cr.mergeCharFormat (f); cr.setPosition (0); cr.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor); do { pos = cr.position(); if (pos >= text_size) break; QChar c = text.at (pos); if (char_is_bad (c)) while (char_is_bad (c)) { cr.movePosition (QTextCursor::NextCharacter); pos = cr.position(); if (pos < text_size) c = text.at (pos); else break; } cr.movePosition (QTextCursor::EndOfWord, QTextCursor::KeepAnchor); QString stext = cr.selectedText(); if (! stext.isEmpty() && ends_with_badchar (stext)) //extend selection { cr.movePosition (QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); stext = cr.selectedText(); } if (! stext.isEmpty()) if (! spellchecker->check (stext)) { f = cr.blockCharFormat(); #if defined(Q_OS_WIN) f.setUnderlineStyle (QTextCharFormat::SingleUnderline); #else f.setUnderlineStyle (QTextCharFormat::SpellCheckUnderline); #endif f.setUnderlineColor (color_error); cr.mergeCharFormat (f); } i++; if (i % 512 == 0) pb_status->setValue (i); } while (cr.movePosition (QTextCursor::NextWord)); cr.setPosition (savepos); d->document()->setModified (false); pb_status->hide(); log->log (tr("elapsed milliseconds: %1").arg (time_start.elapsed())); } void CTEA::fn_spell_add_to_dict() { last_action = sender(); if (spellchecker->modules_list.size() == 0) { QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page")); return; } CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr = d->textCursor(); cr.select (QTextCursor::WordUnderCursor); //плохо работает spellchecker->add_to_user_dict (cr.selectedText()); } void CTEA::fn_spell_remove_from_dict() { last_action = qobject_cast(sender()); if (spellchecker->modules_list.size() == 0) { QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page")); return; } CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr = d->textCursor(); cr.select (QTextCursor::WordUnderCursor); spellchecker->remove_from_user_dict (cr.selectedText()); } void CTEA::fn_spell_suggest_callback() { last_action = sender(); if (spellchecker->modules_list.size() == 0) { QMessageBox::about (0, "!", QObject::tr ("Please set up spell checker dictionaries at\n Options - Functions page")); return; } CDocument *d = documents->get_current(); if (! d) return; QAction *a = qobject_cast(sender()); QString new_text = a->data().toString(); QTextCursor cr = d->textCursor(); cr.select (QTextCursor::WordUnderCursor); QString s = cr.selectedText(); if (s.isEmpty()) return; if (s[0].isUpper()) new_text[0] = new_text[0].toUpper(); cr.insertText (new_text); d->setTextCursor (cr); } void CTEA::fn_spell_suggest() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr = d->textCursor(); cr.select (QTextCursor::WordUnderCursor); QString s = cr.selectedText(); if (s.isEmpty()) return; QStringList l = spellchecker->get_suggestions_list (s); QMenu *m = new QMenu (this); create_menu_from_list (this, m, l, SLOT (fn_spell_suggest_callback())); m->popup (mapToGlobal (d->cursorRect().topLeft())); } #endif /* ==================== Cal menu =================== */ void CTEA::calendar_update() { if (settings->value ("start_week_on_sunday", "0").toBool()) calendar->setFirstDayOfWeek (Qt::Sunday); else calendar->setFirstDayOfWeek (Qt::Monday); int year = calendar->yearShown(); int month = calendar->monthShown(); QDate dbase (year, month, 1); QTextCharFormat format_past; QTextCharFormat format_future; QTextCharFormat format_normal; format_past.setFontStrikeOut (true); format_future.setFontUnderline (true); format_future.setForeground (Qt::red); format_future.setBackground (Qt::darkBlue); format_normal.setForeground (Qt::white); format_normal.setBackground (Qt::black); format_past.setForeground (Qt::white); format_past.setBackground (Qt::darkGray); int days_count = dbase.daysInMonth(); for (int day = 1; day <= days_count; day++) { QDate date (year, month, day); QString sdate; // sdate = sdate.sprintf ("%02d-%02d-%02d", year, month, day); sdate += QString("%1").arg (year, 2, 10, QChar('0')); sdate += "-"; sdate += QString("%1").arg (month, 2, 10, QChar('0')); sdate += "-"; sdate += QString("%1").arg (day, 2, 10, QChar('0')); QString fname = dir_days + "/" + sdate; if (file_exists (fname)) { if (date < QDate::currentDate()) calendar->setDateTextFormat (date, format_past); else if (date >= QDate::currentDate()) calendar->setDateTextFormat (date, format_future); } else calendar->setDateTextFormat (date, format_normal); } } /* void CTEA::create_moon_phase_algos() { moon_phase_algos.insert (MOON_PHASE_TRIG2, tr ("Trigonometric 2")); moon_phase_algos.insert (MOON_PHASE_TRIG1, tr ("Trigonometric 1")); moon_phase_algos.insert (MOON_PHASE_CONWAY, tr ("Conway")); moon_phase_algos.insert (MOON_PHASE_LEUESHKANOV, tr ("Leueshkanov")); } */ void CTEA::cal_moon_mode() { calendar->moon_mode = ! calendar->moon_mode; calendar->do_update(); settings->setValue ("moon_mode", calendar->moon_mode); } void CTEA::cal_set_date_a() { date1 = calendar->selectedDate(); } void CTEA::cal_set_date_b() { date2 = calendar->selectedDate(); } void CTEA::cal_add_days() { QDate selected = calendar->selectedDate(); selected = selected.addDays (fif_get_text().toInt()); calendar->setSelectedDate (selected); } void CTEA::cal_add_months() { QDate selected = calendar->selectedDate(); selected = selected.addMonths (fif_get_text().toInt()); calendar->setSelectedDate (selected); } void CTEA::cal_add_years() { QDate selected = calendar->selectedDate(); selected = selected.addYears (fif_get_text().toInt()); calendar->setSelectedDate (selected); } void CTEA::cal_set_to_current() { calendar->showToday(); calendar->setSelectedDate (QDate::currentDate()); } void CTEA::cal_gen_mooncal() { int jdate1 = date1.toJulianDay(); int jdate2 = date2.toJulianDay(); QString s; QString date_format = settings->value("date_format", "dd/MM/yyyy").toString(); for (int d = jdate1; d <= jdate2; d++) { QDate date = QDate::fromJulianDay (d); int moon_day = moon_phase_trig2 (date.year(), date.month(), date.day()); s += date.toString (date_format); s += " = "; s += QString::number (moon_day); s += "\n"; } CDocument *nd = documents->create_new(); nd->put (s); main_tab_widget->setCurrentIndex (idx_tab_edit); } void CTEA::cal_diff_days() { int days = date2.daysTo (date1); if (days < 0) days = ~ days; log->log (QString::number (days)); } void CTEA::cal_remove() { QString fname = dir_days + "/" + calendar->selectedDate().toString ("yyyy-MM-dd"); QFile::remove (fname); calendar_update(); } /* =================== IDE menu callbacks =================== */ void CTEA::ide_run() { last_action = sender(); if (documents->hash_project.isEmpty()) return; if (documents->fname_current_project.isEmpty()) return; QFileInfo source_dir (documents->fname_current_project); QString dir_build = hash_get_val (documents->hash_project, "dir_build", source_dir.absolutePath()); if (! path_is_abs (dir_build)) //dir is not absolute path dir_build = source_dir.absolutePath() + "/" + dir_build; QString command_run = hash_get_val (documents->hash_project, "command_run", ""); QProcess *process = new QProcess (this); process->setWorkingDirectory (dir_build); connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(process_readyReadStandardOutput())); process->setProcessChannelMode (QProcess::MergedChannels) ; #if QT_VERSION >= 0x060000 process->startCommand (command_run); #else process->start (command_run); #endif //process->start (command_run, QStringList()); } void CTEA::ide_build() { last_action = sender(); if (documents->hash_project.isEmpty()) return; if (documents->fname_current_project.isEmpty()) return; QFileInfo source_dir (documents->fname_current_project); QString dir_build = hash_get_val (documents->hash_project, "dir_build", source_dir.absolutePath()); if (! path_is_abs (dir_build)) //dir is not absolute path dir_build = source_dir.absolutePath() + "/" + dir_build; QString command_build = hash_get_val (documents->hash_project, "command_build", "make"); QProcess *process = new QProcess (this); process->setWorkingDirectory (dir_build); connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(process_readyReadStandardOutput())); process->setProcessChannelMode (QProcess::MergedChannels) ; // process->start (command_build, QStringList()); #if QT_VERSION >= 0x060000 process->startCommand (command_build); #else process->start (command_build); #endif } void CTEA::ide_clean() { last_action = sender(); if (documents->hash_project.isEmpty()) return; if (documents->fname_current_project.isEmpty()) return; QFileInfo source_dir (documents->fname_current_project); QString dir_build = hash_get_val (documents->hash_project, "dir_build", source_dir.absolutePath()); if (! path_is_abs (dir_build)) //dir is not absolute path dir_build = source_dir.absolutePath() + "/" + dir_build; QString command_clean = hash_get_val (documents->hash_project, "command_clean", "make"); QProcess *process = new QProcess (this); process->setWorkingDirectory (dir_build); connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(process_readyReadStandardOutput())); process->setProcessChannelMode (QProcess::MergedChannels) ; // process->start (command_clean, QStringList()); #if QT_VERSION >= 0x060000 process->startCommand (command_clean); #else process->start (command_clean); #endif } void CTEA::ide_toggle_hs() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (file_exists (d->file_name)) documents->open_file_without_reload (toggle_fname_header_source (d->file_name), d->charset); } /* =================== Nav menu callbacks =================== */ void CTEA::nav_save_pos() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->position = d->textCursor().position(); } void CTEA::nav_goto_pos() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr = d->textCursor(); cr.setPosition (d->position); d->setTextCursor (cr); } void CTEA::nav_goto_line() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr = d->textCursor(); cr.movePosition (QTextCursor::Start); cr.movePosition (QTextCursor::NextBlock, QTextCursor::MoveAnchor, fif_get_text().toInt() - 1); d->setTextCursor (cr); d->setFocus(); } void CTEA::nav_goto_right_tab() { last_action = sender(); int i; if (tab_editor->currentIndex() == (tab_editor->count() - 1)) i = 0; else i = tab_editor->currentIndex() + 1; if (tab_editor->count() > 0) tab_editor->setCurrentIndex (i); } void CTEA::nav_goto_left_tab() { last_action = sender(); int i; if (tab_editor->currentIndex() == 0) i = tab_editor->count() - 1; else i = tab_editor->currentIndex() - 1; if (tab_editor->count() > 0) tab_editor->setCurrentIndex (i); } void CTEA::nav_focus_to_fif() { last_action = sender(); fif->setFocus (Qt::OtherFocusReason); } void CTEA::nav_focus_to_editor() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->setFocus (Qt::OtherFocusReason); } void CTEA::nav_labels_update_list() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; d->update_labels(); update_labels_menu(); } /* =================== Fm menu callbacks =================== */ void CTEA::fman_multi_rename_zeropad() { last_action = sender(); QString fiftxt = fif_get_text(); int finalsize = fiftxt.toInt(); if (finalsize < 1) finalsize = 10; QStringList sl = fman->get_sel_fnames(); if (sl.size() < 1) return; for (int i = 0; i < sl.size(); i++) { QString fname = sl[i]; QFileInfo fi (fname); if (fi.exists() && fi.isWritable()) { int zeroes_to_add = finalsize - fi.baseName().length(); QString newname = fi.baseName(); QString ext = file_get_ext (fname); #if (QT_VERSION_MAJOR < 5) newname.remove(QRegExp("[a-zA-Z\\s]")); #else newname.remove(QRegularExpression("[a-zA-Z\\s]")); #endif if (newname.isEmpty()) continue; QString pad = "0"; pad = pad.repeated (zeroes_to_add); newname = pad + newname; QString newfpath (fi.path()); newfpath.append ("/").append (newname); newfpath.append ("."); newfpath.append (ext); QFile::rename (fname, newfpath); } } update_dyn_menus(); fman->refresh(); } void CTEA::fman_multi_rename_del_n_first_chars() { last_action = sender(); QString fiftxt = fif_get_text(); int todel = fiftxt.toInt(); if (todel < 1) todel = 1; QStringList sl = fman->get_sel_fnames(); if (sl.size() < 1) return; for (int i = 0; i < sl.size(); i++) { QFileInfo fi (sl.at(i)); if (fi.exists() && fi.isWritable()) { QString newname = fi.fileName(); newname = newname.mid (todel); QString newfpath (fi.path()); newfpath.append ("/").append (newname); QFile::rename (sl.at(i), newfpath); } } update_dyn_menus(); fman->refresh(); } void CTEA::fman_multi_rename_replace() { last_action = sender(); QStringList l = fif_get_text().split ("~"); if (l.size() < 2) return; QStringList sl = fman->get_sel_fnames(); if (sl.size() < 1) return; for (int i = 0; i < sl.size(); i++) { QFileInfo fi (sl.at(i)); if (fi.exists() && fi.isWritable()) { QString newname = fi.fileName(); newname = newname.replace (l[0], l[1]); QString newfpath (fi.path()); newfpath.append ("/").append (newname); QFile::rename (sl.at(i), newfpath); } } update_dyn_menus(); fman->refresh(); } void CTEA::fman_multi_rename_apply_template() { last_action = sender(); QString fiftxt = fif_get_text(); QStringList sl = fman->get_sel_fnames(); if (sl.size() < 1) return; for (int i = 0; i < sl.size(); i++) { QFileInfo fi (sl.at(i)); if (fi.exists() && fi.isWritable()) { QString ext = file_get_ext (sl.at(i)); QString newname = fiftxt; newname = newname.replace ("%filename", fi.fileName()); newname = newname.replace ("%ext", ext); newname = newname.replace ("%basename", fi.baseName()); QString newfpath (fi.path()); newfpath.append ("/").append (newname); QFile::rename (sl.at(i), newfpath); } } update_dyn_menus(); fman->refresh(); } void CTEA::fman_fileop_create_dir() { last_action = sender(); bool ok; QString newdir = QInputDialog::getText (this, tr ("Enter the name"), tr ("Name:"), QLineEdit::Normal, tr ("new_directory"), &ok); if (! ok || newdir.isEmpty()) return; QString dname = fman->dir.path() + "/" + newdir; QDir d; if (d.mkpath (dname)) fman->nav (dname); } void CTEA::fman_fileop_rename() { last_action = sender(); QString fname = fman->get_sel_fname(); if (fname.isEmpty()) return; QFileInfo fi (fname); if (! fi.exists() && ! fi.isWritable()) return; bool ok; QString newname = QInputDialog::getText (this, tr ("Enter the name"), tr ("Name:"), QLineEdit::Normal, tr ("new"), &ok); if (! ok || newname.isEmpty()) return; QString newfpath = fi.path() + "/" + newname; QFile::rename (fname, newfpath); update_dyn_menus(); fman->refresh(); QModelIndex index = fman->index_from_name (newname); fman->selectionModel()->setCurrentIndex (index, QItemSelectionModel::Select | QItemSelectionModel::Rows); fman->scrollTo (index, QAbstractItemView::PositionAtCenter); } void CTEA::fman_fileop_delete() { last_action = sender(); QString fname = fman->get_sel_fname(); if (fname.isEmpty()) return; int i = fman->get_sel_index(); //save the index QFileInfo fi (fname); if (! fi.exists() && ! fi.isWritable()) return; if (QMessageBox::warning (this, "TEA", tr ("Are you sure to delete\n" "%1?").arg (fname), QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) return; QFile::remove (fname); update_dyn_menus(); fman->refresh(); QModelIndex index = fman->index_from_idx (i); if (! index.isValid()) index = fman->index_from_idx (0); fman->selectionModel()->setCurrentIndex (index, QItemSelectionModel::Select | QItemSelectionModel::Rows); fman->scrollTo (index, QAbstractItemView::PositionAtCenter); } void CTEA::fm_fileinfo_info() { last_action = sender(); QString fname; if (main_tab_widget->currentIndex() == idx_tab_fman) fname = fman->get_sel_fname(); else { CDocument *d = documents->get_current(); if (d) fname = d->file_name; } QFileInfo fi (fname); if (! fi.exists()) return; QStringList l; //detect EOL QFile f (fname); if (f.open (QIODevice::ReadOnly)) { QString n (tr("End of line: ")); QByteArray barr = f.readAll(); int nl = barr.count ('\n'); int cr = barr.count ('\r'); if (nl > 0 && cr == 0) n += "UNIX"; if (nl > 0 && cr > 0) n += "Windows"; if (nl == 0 && cr > 0) n += "Mac"; l.append (n); } l.append (tr ("file name: %1").arg (fi.absoluteFilePath())); l.append (tr ("size: %1 kbytes").arg (QString::number (fi.size() / 1024))); l.append (tr ("last modified: %1").arg (fi.lastModified().toString ("yyyy-MM-dd@hh:mm:ss"))); if (file_get_ext (fname) == "wav") { CWavReader wr; wr.get_info (fname); l.append (tr ("bits per sample: %1").arg (wr.wav_chunk_fmt.bits_per_sample)); l.append (tr ("number of channels: %1").arg (wr.wav_chunk_fmt.num_channels)); l.append (tr ("sample rate: %1").arg (wr.wav_chunk_fmt.sample_rate)); if (wr.wav_chunk_fmt.bits_per_sample == 16) l.append (tr ("RMS for all channels: %1 dB").arg (wr.rms)); } log->log (l.join ("
")); } void CTEA::fman_fileinfo_count_lines_in_selected_files() { last_action = sender(); QString ft = fif_get_text(); if (ft.isEmpty()) return; QStringList sl = fman->get_sel_fnames(); if (sl.size() < 1) return; long int sum = 0; for (int i = 0; i < sl.size(); i++) { QByteArray f = file_load (sl.at(i)); sum += f.count ('\n'); } log->log (tr ("There are %1 lines at %2 files").arg (sum).arg (sl.size())); } /* void CTEA::fman_zip_create() { last_action = sender(); bool ok; QString name = QInputDialog::getText (this, tr ("Enter the archive name"), tr ("Name:"), QLineEdit::Normal, tr ("new_archive"), &ok); if (! ok) return; fman->zipper.files_list.clear(); fman->zipper.archive_name = name; if (! name.endsWith (".zip")) name.append (".zip"); fman->zipper.archive_fullpath = fman->dir.path() + "/" + name; } void CTEA::fman_zip_add() { last_action = sender(); QString f = ed_fman_fname->text().trimmed(); QStringList li = fman->get_sel_fnames(); if (! f.isEmpty()) if (f[0] == '/') { fman->zipper.files_list.append (f); return; } if (li.size() == 0) { QString fname = fman->dir.path() + "/" + f; fman->zipper.files_list.append (fname); return; } for (int i = 0; i < li.size(); i++) fman->zipper.files_list.append (li.at(i)); } void CTEA::fman_zip_save() { last_action = sender(); fman->zipper.pack_prepared(); fman->refresh(); } void CTEA::fman_zip_info() { last_action = sender(); QString fn = fman->get_sel_fname(); if (fn.isEmpty()) return; CZipper z; QStringList sl = z.unzip_list (fn); for (int i = 0; i < sl.size(); i++) sl[i] = sl[i].append ("
"); log->log (sl.join("\n")); } void CTEA::fman_zip_unpack() { last_action = sender(); CZipper z; QString f = ed_fman_fname->text().trimmed(); QStringList li = fman->get_sel_fnames(); if (! f.isEmpty()) if (f[0] == '/') { z.unzip (f, fman->dir.path()); return; } if (li.size() == 0) { QString fname (fman->dir.path()); fname.append ("/").append (f); z.unzip (fname, fman->dir.path()); return; } for (QList ::iterator fname = li.begin(); fname != li.end(); ++fname) { z.unzip ((*fname), fman->dir.path()); log->log ((*fname) + tr (" is unpacked")); } } */ void CTEA::fman_img_conv_by_side() { last_action = sender(); int side = fif_get_text().toInt(); if (side != 0) fman_convert_images (true, side); } void CTEA::fman_img_conv_by_percent() { last_action = sender(); int percent = fif_get_text().toInt(); if (percent != 0) fman_convert_images (false, percent); } void CTEA::fman_img_make_gallery() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; if (! file_exists (d->file_name)) return; int side = settings->value ("ed_side_size", 110).toInt(); int thumbs_per_row = settings->value ("ed_thumbs_per_row", 4).toInt(); QString link_options = settings->value ("ed_link_options", "target=\"_blank\"").toString(); if (! link_options.startsWith (" ")) link_options.prepend (" "); QString dir_out (fman->dir.absolutePath()); QString table ("\n\n"); Qt::TransformationMode transformMode = Qt::FastTransformation; pb_status->show(); pb_status->setFormat (tr ("%p% completed")); pb_status->setTextVisible (true); QStringList li = fman->get_sel_fnames(); int quality = settings->value ("img_quality", "-1").toInt(); pb_status->setRange (0, li.size() - 1 ); int x = 0; int col = 0; for (int i = 0; i < li.size(); i++) { QString fname = li[i]; if (is_image (fname)) { QFileInfo fi (fname); if (fi.baseName().startsWith ("tmb_")) continue; QImage source (fname); if (! source.isNull()) { qApp->processEvents(); QImage dest = image_scale_by (source, true, side, transformMode); QString dest_fname (dir_out); dest_fname.append ("/"); dest_fname.append ("tmb_"); dest_fname.append (fi.fileName()); dest_fname = change_file_ext (dest_fname, "jpg"); dest.save (dest_fname, 0, quality); QFileInfo inf (d->file_name); QDir dir (inf.absolutePath()); QString tmb = get_insert_image (d->file_name, dest_fname, d->markup_mode); QString cell = "%thumb"; cell.replace ("%source", dir.relativeFilePath (fname)); cell.replace ("%thumb", tmb); if (col == 0) table += "\n\n"; table += "\n"; col++; if (col == thumbs_per_row) { table += "\n\n"; col = 0; } pb_status->setValue (x++); } } } pb_status->hide(); fman->refresh(); if (! table.endsWith ("\n\n")) table += "\n\n"; table += "
\n"; table += cell; table += "
\n"; if (d) d->put (table); } void CTEA::fman_home() { last_action = sender(); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) fman->nav ("c:\\"); #else fman->nav (QDir::homePath()); #endif } void CTEA::fman_refresh() { last_action = sender(); fman->refresh(); } void CTEA::fman_preview_image() { last_action = sender(); QString fname = fman->get_sel_fname(); if (fname.isEmpty()) return; if (is_image (fname)) { if (file_get_ext (fname) == "gif") { CGIFWindow *w = new CGIFWindow; w->load_image (fname); return; } img_viewer->window_full.show(); img_viewer->set_image_full (fname); } } void CTEA::fman_select_by_regexp() { last_action = sender(); fman_items_select_by_regexp (true); } void CTEA::fman_deselect_by_regexp() { last_action = sender(); fman_items_select_by_regexp (false); } /* =================== View menu callbacks =================== */ void CTEA::view_use_theme() { last_action = sender(); QAction *a = qobject_cast(sender()); QString css_fname = a->data().toString() + "/" + "stylesheet.css"; if (! file_exists (css_fname)) { log->log (tr ("There is no stylesheet file")); return; } update_stylesheet (css_fname); fname_stylesheet = css_fname; settings->setValue ("fname_stylesheet", fname_stylesheet); } void CTEA::view_use_palette() { last_action = sender(); QAction *a = qobject_cast(sender()); // QString tf = qstring_clear (a->text()); QString fname = dir_palettes + "/" + a->data().toString(); if (! file_exists (fname)) fname = ":/palettes/" + a->data().toString(); fname_def_palette = fname; load_palette (fname); update_stylesheet (fname_stylesheet); documents->apply_settings(); } void CTEA::view_use_profile() { last_action = sender(); QAction *a = qobject_cast(sender()); QSettings s (a->data().toString(), QSettings::IniFormat); QPoint pos = s.value ("pos", QPoint (1, 200)).toPoint(); QSize size = s.value ("size", QSize (600, 420)).toSize(); if (mainSplitter/* && ! settings->value ("ui_mode", 0).toBool()*/) mainSplitter->restoreState (s.value ("splitterSizes").toByteArray()); resize (size); move (pos); fname_def_palette = s.value ("fname_def_palette", ":/palettes/TEA").toString(); load_palette (fname_def_palette); settings->setValue ("fname_def_palette", fname_def_palette); settings->setValue ("word_wrap", s.value ("word_wrap", "1").toBool()); settings->setValue ("show_linenums", s.value ("show_linenums", "0").toBool()); settings->setValue ("additional_hl", s.value ("additional_hl", "0").toBool()); settings->setValue ("show_margin", s.value ("show_margin", "0").toBool()); settings->setValue ("editor_font_name", s.value ("editor_font_name", "Serif").toString()); settings->setValue ("editor_font_size", s.value ("editor_font_size", "14").toInt()); settings->setValue ("logmemo_font", s.value ("logmemo_font", "Monospace").toString()); settings->setValue ("logmemo_font_size", s.value ("logmemo_font_size", "14").toInt()); settings->setValue ("app_font_name", s.value ("app_font_name", "Sans").toString()); settings->setValue ("app_font_size", s.value ("app_font_size", "12").toInt()); cb_wordwrap->setChecked (s.value ("word_wrap", "1").toBool()); cb_show_linenums->setChecked (s.value ("show_linenums", "0").toBool()); cb_hl_current_line->setChecked (s.value ("additional_hl", "0").toBool()); cb_show_margin->setChecked (s.value ("show_margin", "0").toBool()); update_stylesheet (fname_stylesheet); documents->apply_settings(); } void CTEA::view_profile_save_as() { last_action = sender(); bool ok; QString name = QInputDialog::getText (this, tr ("Enter the name"), tr ("Name:"), QLineEdit::Normal, tr ("new_profile"), &ok); if (! ok || name.isEmpty()) return; QString fname (dir_profiles); fname.append ("/").append (name); QSettings s (fname, QSettings::IniFormat); s.setValue ("fname_def_palette", fname_def_palette); s.setValue ("word_wrap", settings->value ("word_wrap", "1").toBool()); s.setValue ("show_linenums", settings->value ("show_linenums", "0").toBool()); s.setValue ("additional_hl", settings->value ("additional_hl", "0").toBool()); s.setValue ("pos", pos()); s.setValue ("size", size()); if (mainSplitter /*&& ! settings->value ("ui_mode", 0).toBool()*/) s.setValue ("splitterSizes", mainSplitter->saveState()); s.setValue ("editor_font_name", settings->value ("editor_font_name", "Monospace").toString()); s.setValue ("editor_font_size", settings->value ("editor_font_size", "16").toInt()); s.setValue ("logmemo_font", settings->value ("logmemo_font", "Monospace").toString()); s.setValue ("logmemo_font_size", settings->value ("logmemo_font_size", "12").toInt()); s.setValue ("app_font_name", settings->value ("app_font_name", "Sans").toString()); s.setValue ("app_font_size", settings->value ("app_font_size", "12").toInt()); s.sync(); update_profiles(); shortcuts->load_from_file (shortcuts->fname); } void CTEA::view_toggle_wrap() { last_action = sender(); CDocument *d = documents->get_current(); if (d) d->set_word_wrap (! d->get_word_wrap()); } void CTEA::view_hide_error_marks() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr = d->textCursor(); //delete all underlines cr.setPosition (0); cr.movePosition (QTextCursor::End, QTextCursor::KeepAnchor); QTextCharFormat f = cr.blockCharFormat(); f.setFontUnderline (false); cr.mergeCharFormat (f); d->document()->setModified (false); } void CTEA::view_toggle_fs() { last_action = sender(); setWindowState(windowState() ^ Qt::WindowFullScreen); } void CTEA::view_stay_on_top() { last_action = sender(); Qt::WindowFlags flags = windowFlags(); flags ^= Qt::WindowStaysOnTopHint; setWindowFlags (flags ); show(); activateWindow(); } void CTEA::view_darker() { last_action = sender(); CDarkerWindow *wd = new CDarkerWindow; wd->show(); } /* =================== ? menu callbacks =================== */ void CTEA::help_show_about() { //CAboutWindow *a = new CAboutWindow(); if (! wnd_about) wnd_about = new CAboutWindow(); wnd_about->move (x() + 20, y() + 20); wnd_about->show(); } void CTEA::help_show_news() { QString fname = ":/NEWS"; if (QLocale::system().name().left(2) == "ru") fname = ":/NEWS-RU"; CDocument *d = documents->open_file_without_reload (fname, "UTF-8"); if (d) d->setReadOnly (true); } void CTEA::help_show_todo() { CDocument *d = documents->open_file_without_reload (":/TODO", "UTF-8"); if (d) d->setReadOnly (true); } void CTEA::help_show_changelog() { CDocument *d = documents->open_file_without_reload (":/ChangeLog", "UTF-8"); if (d) d->setReadOnly (true); } void CTEA::help_show_gpl() { CDocument *d = documents->open_file_without_reload (":/COPYING", "UTF-8"); if (d) d->setReadOnly (true); } /* ==================================== Application stuff inits and updates ==================================== */ CTEA::CTEA() { wnd_about = 0; mainSplitter = 0; ui_update = true; boring = false; b_destroying_all = false; last_action = 0; b_recent_off = false; lv_menuitems = NULL; fm_entry_mode = FM_ENTRY_MODE_NONE; calendar = 0; capture_to_storage_file = false; date1 = QDate::currentDate(); date2 = QDate::currentDate(); idx_tab_edit = 0; idx_tab_tune = 0; idx_tab_fman = 0; idx_tab_learn = 0; idx_tab_calendar = 0; idx_tab_keyboard = 0; create_paths(); QString sfilename = dir_config + "/tea.conf"; settings = new QSettings (sfilename, QSettings::IniFormat); QString lng = settings->value ("lng", QLocale::system().name()).toString().left(2).toLower(); if (! file_exists (":/translations/" + lng + ".qm")) lng = "en"; #if QT_VERSION >= 0x060000 if (transl_app.load (QString ("qt_%1").arg (lng), QLibraryInfo::path (QLibraryInfo::TranslationsPath))) qApp->installTranslator (&transl_app); #else if (transl_system.load (QString ("qt_%1").arg (lng), QLibraryInfo::location (QLibraryInfo::TranslationsPath))) qApp->installTranslator (&transl_system); #endif if (transl_app.load (":/translations/" + lng)) qApp->installTranslator (&transl_app); fname_stylesheet = settings->value ("fname_stylesheet", ":/themes/TEA").toString(); theme_dir = get_file_path (fname_stylesheet) + "/"; l_charset = new QLabel; l_status = new QLabel; pb_status = new QProgressBar; pb_status->setRange (0, 0); tb_stop = new QToolButton; tb_stop->setIcon (style()->standardIcon(QStyle::SP_MediaStop)); connect (tb_stop, SIGNAL(clicked()), this, SLOT(tb_stop_clicked())); statusBar()->addWidget (l_status); statusBar()->addPermanentWidget (pb_status); statusBar()->addPermanentWidget (tb_stop); statusBar()->addPermanentWidget (l_charset); // pb_status->hide(); progress_hide(); create_actions(); create_menus(); create_toolbars(); update_styles(); update_bookmarks(); update_templates(); update_tables(); update_snippets(); update_sessions(); update_scripts(); update_programs(); update_palettes(); update_themes(); update_keyboards(); update_charsets(); update_profiles(); create_markup_hash(); setMinimumSize (12, 12); if (! settings->value ("ui_mode", 0).toBool()) create_main_widget_splitter(); else create_main_widget_docked(); idx_prev = 0; connect (main_tab_widget, SIGNAL(currentChanged(int)), this, SLOT(main_tab_page_changed(int))); read_settings(); read_search_options(); documents = new CDox(); documents->parent_wnd = this; documents->tab_widget = tab_editor; documents->main_tab_widget = main_tab_widget; documents->menu_recent = menu_file_recent; documents->recent_list_fname = dir_config + "/recentfiles"; documents->reload_recent_list(); documents->update_recent_menu(); documents->log = log; documents->markup_mode = markup_mode; documents->dir_config = dir_config; documents->todo.dir_days = dir_days; documents->fname_crapbook = fname_crapbook; documents->fname_saved_buffers = fname_saved_buffers; documents->l_status_bar = l_status; documents->l_charset = l_charset; documents->autosave_files = hash_load_keyval (fname_autosaving_files); load_palette (fname_def_palette); update_stylesheet (fname_stylesheet); documents->apply_settings(); documents->todo.load_dayfile(); update_hls(); #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) || defined (NUSPELL_ENABLE) setup_spellcheckers(); #endif shortcuts = new CShortcuts (this); shortcuts->fname = dir_config + "/shortcuts"; shortcuts->load_from_file (shortcuts->fname); sl_fif_history = qstring_load (fname_fif).split ("\n"); cmb_fif->addItems (sl_fif_history); cmb_fif->clearEditText(); #ifdef SPEECH_ENABLE create_speech(); #endif create_fman(); create_options(); create_calendar(); create_manual(); update_fonts(); documents->dir_last = settings->value ("dir_last", QDir::homePath()).toString(); b_preview = settings->value ("b_preview", false).toBool(); img_viewer = new CImgViewer; restoreState (settings->value ("state", QByteArray()).toByteArray()); current_version_number = VERSION_NUMBER; if (current_version_number.indexOf ('\"') != -1) { current_version_number.remove (0, 1); current_version_number.remove (current_version_number.size() - 1, 1); } QString vn = settings->value ("VER_NUMBER", "").toString(); if (vn.isEmpty() || vn != QString (current_version_number)) help_show_news(); if (settings->value ("session_restore", false).toBool()) { QString fname_session (dir_sessions); fname_session.append ("/def-session-777"); documents->load_from_session (fname_session); } if (settings->value ("save_buffers", true).toBool()) documents->load_from_buffers (fname_saved_buffers); handle_args(); ui_update = false; setIconSize (QSize (icon_size, icon_size)); tb_fman_dir->setIconSize (QSize (icon_size, icon_size)); QClipboard *clipboard = QApplication::clipboard(); connect (clipboard , SIGNAL(dataChanged()), this, SLOT(clipboard_dataChanged())); setAcceptDrops (true); log->log (tr ("TEA %1 by Peter Semiletov | tea.ourproject.org
Support TEA via PayPal: peter.semiletov@gmail.com
Git: github.com/psemiletov/tea-qt
AUR: aur.archlinux.org/packages/tea-qt-git").arg (QString (current_version_number))); QTextCursor cr = log->textCursor(); cr.movePosition (QTextCursor::Start); log->setTextCursor (cr); QString icon_fname = ":/icons/tea-icon-v3-0" + settings->value ("icon_fname", "1").toString() + ".png"; qApp->setWindowIcon (QIcon (icon_fname)); idx_tab_edit_activate(); } void CTEA::handle_args() { QStringList l = qApp->arguments(); int size = l.size(); if (size < 2) return; QString cmdline_charset = settings->value ("cmdline_default_charset", "UTF-8").toString();//"UTF-8"; for (int i = 1; i < size; i++) { QString t = l.at(i); if (t.startsWith ("--charset")) { QStringList pair = t.split ("="); if (pair.size() > 1) cmdline_charset = pair[1]; } else { QFileInfo f (l.at(i)); if (! f.isAbsolute()) { QString fullname (QDir::currentPath()); fullname.append ("/").append (l.at(i)); documents->open_file (fullname, cmdline_charset); } else documents->open_file (l.at(i), cmdline_charset); } } } void CTEA::create_paths() { portable_mode = false; QStringList l = qApp->arguments(); if (l.contains ("--p")) portable_mode = true; QDir dr; if (! portable_mode) dir_config = dr.homePath(); else dir_config = QCoreApplication::applicationDirPath(); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) dir_config.append ("/tea"); #else dir_config.append ("/.config/tea"); #endif // hs_path["dir_config"] = dir_config; dr.setPath (dir_config); if (! dr.exists()) dr.mkpath (dir_config); fname_crapbook = dir_config + "/crapbook.txt"; // hs_path["fname_crapbook"] = fname_crapbook; fname_saved_buffers = dir_config + "/saved_buffers.txt"; //hs_path["saved_buffers"] = fname_saved_buffers; fname_autosaving_files = dir_config + "/autosaving_files.txt"; //hs_path["autosaving_files"] = fname_autosaving_files; fname_fif = dir_config + "/fif"; //hs_path["fname_fif"] = fname_fif; QString old_bmx_file = dir_config + "/tea_bmx"; fname_bookmarks = dir_config + "/bookmarks"; //hs_path["fname_bookmarks"] = fname_bookmarks; if (file_exists (old_bmx_file)) { QString file_content = qstring_load (old_bmx_file); file_content = file_content.replace (",", "*"); qstring_save (fname_bookmarks, file_content); QFile f (old_bmx_file); f.rename (old_bmx_file + ".bak"); } fname_programs = dir_config + "/programs"; //hs_path["fname_programs"] = fname_programs; fname_places_bookmarks = dir_config + "/places_bookmarks"; //hs_path["fname_places_bookmarks"] = fname_places_bookmarks; fname_tempfile = QDir::tempPath() + "/tea.tmp"; //hs_path["fname_tempfile"] = fname_tempfile; fname_tempparamfile = QDir::tempPath() + "/teaparam.tmp"; //hs_path["fname_tempparamfile"] = fname_tempparamfile; dir_tables = dir_config + "/tables"; dr.setPath (dir_tables); if (! dr.exists()) dr.mkpath (dir_tables); dir_user_dict = dir_config + "/dictionaries"; dr.setPath (dir_user_dict); if (! dr.exists()) dr.mkpath (dir_user_dict); dir_profiles = dir_config + "/profiles"; dr.setPath (dir_profiles); if (! dr.exists()) dr.mkpath (dir_profiles); dir_templates = dir_config + "/templates"; dr.setPath (dir_templates); if (! dr.exists()) dr.mkpath (dir_templates); dir_snippets = dir_config + "/snippets"; dr.setPath (dir_snippets); if (! dr.exists()) dr.mkpath (dir_snippets); dir_scripts = dir_config + "/scripts"; dr.setPath (dir_scripts); if (! dr.exists()) dr.mkpath (dir_scripts); dir_days = dir_config + "/days"; dr.setPath (dir_days); if (! dr.exists()) dr.mkpath (dir_days); dir_sessions = dir_config + "/sessions"; dr.setPath (dir_sessions); if (! dr.exists()) dr.mkpath (dir_sessions); dir_keyboards = dir_config + "/keyboards"; dr.setPath (dir_keyboards); if (! dr.exists()) dr.mkpath (dir_keyboards); dir_themes = dir_config + "/themes"; dr.setPath (dir_themes); if (! dr.exists()) dr.mkpath (dir_themes); dir_hls = dir_config + "/hls"; dr.setPath (dir_hls); if (! dr.exists()) dr.mkpath (dir_hls); dir_palettes = dir_config + "/palettes"; dr.setPath (dir_palettes); if (! dr.exists()) dr.mkpath (dir_palettes); } #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) || defined (NUSPELL_ENABLE) void CTEA::setup_spellcheckers() { QString default_speller; #ifdef ASPELL_ENABLE spellcheckers.append ("Aspell"); default_speller = "Aspell"; #endif #ifdef NUSPELL_ENABLE spellcheckers.append ("Nuspell"); default_speller = "Nuspell"; #endif #ifdef HUNSPELL_ENABLE spellcheckers.append ("Hunspell"); default_speller = "Hunspell"; #endif cur_spellchecker = settings->value ("cur_spellchecker", default_speller).toString(); if (spellcheckers.size() > 0) if (! spellcheckers.contains (cur_spellchecker)) cur_spellchecker = spellcheckers[0]; #ifdef ASPELL_ENABLE if (cur_spellchecker == "Aspell") { QString lang = settings->value ("spell_lang", QLocale::system().name().left(2)).toString(); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) QString win32_aspell_path = settings->value ("win32_aspell_path", aspell_default_dict_path()).toString(); spellchecker = new CAspellchecker (lang, win32_aspell_path); #else spellchecker = new CAspellchecker (lang); #endif } #endif #ifdef HUNSPELL_ENABLE if (cur_spellchecker == "Hunspell") spellchecker = new CHunspellChecker (settings->value ("spell_lang", QLocale::system().name().left(2)).toString(), settings->value ("hunspell_dic_path", hunspell_default_dict_path()).toString(), dir_user_dict); #endif #ifdef NUSPELL_ENABLE if (cur_spellchecker == "Nuspell") { spellchecker = new CNuspellChecker (settings->value ("spell_lang", QLocale::system().name().left(2)).toString(), settings->value ("hunspell_dic_path", hunspell_default_dict_path()).toString(), dir_user_dict); spellchecker->change_lang (settings->value ("spell_lang", QLocale::system().name().left(2)).toString()); } #endif create_spellcheck_menu(); } void CTEA::create_spellcheck_menu() { menu_spell_langs->clear(); spellchecker->get_speller_modules_list(); if (spellchecker->modules_list.size() > 0) create_menu_from_list (this, menu_spell_langs, spellchecker->modules_list, SLOT(fn_change_spell_lang())); } #endif void CTEA::create_main_widget_splitter() { QWidget *main_widget = new QWidget; QVBoxLayout *v_box = new QVBoxLayout; main_widget->setLayout (v_box); main_tab_widget = new QTabWidget; main_tab_widget->setObjectName ("main_tab_widget"); main_tab_widget->setTabShape (QTabWidget::Triangular); tab_editor = new QTabWidget; tab_editor->setUsesScrollButtons (true); #if (QT_VERSION_MAJOR >= 4 && QT_VERSION_MINOR >= 5) //tab_editor->setMovable (true); #endif tab_editor->setObjectName ("tab_editor"); QPushButton *bt_close = new QPushButton ("X", this); connect (bt_close, SIGNAL(clicked()), this, SLOT(file_close())); tab_editor->setCornerWidget (bt_close); log = new CLogMemo; connect (log, SIGNAL(double_click(QString)), this, SLOT(logmemo_double_click(QString))); mainSplitter = new QSplitter (Qt::Vertical); v_box->addWidget (mainSplitter); main_tab_widget->setMinimumHeight (10); log->setMinimumHeight (10); mainSplitter->addWidget (main_tab_widget); mainSplitter->addWidget (log); // FIF creation code if (! settings->value ("fif_at_toolbar", 0).toBool()) { cmb_fif = new QComboBox; cmb_fif->setInsertPolicy (QComboBox::InsertAtTop); cmb_fif->setObjectName ("FIF"); cmb_fif->setEditable (true); cmb_fif->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed); fif = cmb_fif->lineEdit(); fif->setStatusTip (tr ("The famous input field. Use for search/replace, function parameters")); connect (fif, SIGNAL(returnPressed()), this, SLOT(search_find())); QHBoxLayout *lt_fte = new QHBoxLayout; v_box->addLayout (lt_fte, 0); int tleft = 1; int tright = 1; int ttop = 1; int tbottom = 1; v_box->getContentsMargins(&tleft, &ttop, &tright, &tbottom); v_box->setContentsMargins(tleft, 1, tright, 1); QToolBar *tb_fif = new QToolBar; QAction *act_fif_find = tb_fif->addAction (style()->standardIcon(QStyle::SP_ArrowForward), ""); act_fif_find->setToolTip (tr ("Find")); connect (act_fif_find, SIGNAL(triggered()), this, SLOT(search_find())); QAction *act_fif_find_prev = tb_fif->addAction (style()->standardIcon(QStyle::SP_ArrowUp), ""); act_fif_find_prev->setToolTip (tr ("Find previous")); connect (act_fif_find_prev, SIGNAL(triggered()), this, SLOT(search_find_prev())); QAction *act_fif_find_next = tb_fif->addAction (style()->standardIcon(QStyle::SP_ArrowDown), ""); act_fif_find_next->setToolTip (tr ("Find next")); connect (act_fif_find_next, SIGNAL(triggered()), this, SLOT(search_find_next())); QLabel *l_fif = new QLabel (tr ("FIF")); lt_fte->addWidget (l_fif, 0, Qt::AlignRight); lt_fte->addWidget (cmb_fif, 0); lt_fte->addWidget (tb_fif, 0); } mainSplitter->setStretchFactor (1, 1); idx_tab_edit = main_tab_widget->addTab (tab_editor, tr ("editor")); setCentralWidget (main_widget); connect (tab_editor, SIGNAL(currentChanged(int)), this, SLOT(pageChanged(int))); } void CTEA::create_main_widget_docked() { setDockOptions (QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks); QWidget *main_widget = new QWidget; QVBoxLayout *v_box = new QVBoxLayout; main_widget->setLayout (v_box); setCentralWidget (main_widget); main_tab_widget = new QTabWidget; main_tab_widget->setObjectName ("main_tab_widget"); v_box->addWidget (main_tab_widget); main_tab_widget->setTabShape (QTabWidget::Triangular); tab_editor = new QTabWidget; tab_editor->setUsesScrollButtons (true); //#if QT_VERSION >= 0x040500 #if (QT_VERSION_MAJOR >= 4 && QT_VERSION_MINOR >= 5) tab_editor->setMovable (true); #endif tab_editor->setObjectName ("tab_editor"); QPushButton *bt_close = new QPushButton ("X", this); connect (bt_close, SIGNAL(clicked()), this, SLOT(file_close())); tab_editor->setCornerWidget (bt_close); QDockWidget *dock_logmemo = new QDockWidget (tr ("logmemo"), this); dock_logmemo->setFeatures (QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); dock_logmemo->setAllowedAreas (Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); log = new CLogMemo (dock_logmemo); connect (log, SIGNAL(double_click(QString)), this, SLOT(logmemo_double_click(QString))); dock_logmemo->setWidget (log); dock_logmemo->setObjectName ("dock_log"); addDockWidget (Qt::BottomDockWidgetArea, dock_logmemo); // FIF creation code if (! settings->value ("fif_at_toolbar", 0).toBool()) { QDockWidget *dock_fif = new QDockWidget (tr ("famous input field"), this); dock_fif->setAllowedAreas (Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); dock_fif->setObjectName ("dock_fif"); dock_fif->setFeatures (QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); QWidget *w_fif = new QWidget (dock_fif); w_fif->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::Maximum); cmb_fif = new QComboBox; cmb_fif->setObjectName ("FIF"); cmb_fif->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed); cmb_fif->setEditable (true); fif = cmb_fif->lineEdit(); fif->setStatusTip (tr ("The famous input field. Use for search/replace, function parameters")); connect (fif, SIGNAL(returnPressed()), this, SLOT(search_find())); QHBoxLayout *lt_fte = new QHBoxLayout; w_fif->setLayout (lt_fte); QToolBar *tb_fif = new QToolBar; QAction *act_fif_find = tb_fif->addAction (style()->standardIcon(QStyle::SP_ArrowForward), ""); act_fif_find->setToolTip (tr ("Find")); connect (act_fif_find, SIGNAL(triggered()), this, SLOT(search_find())); QAction *act_fif_find_next = tb_fif->addAction (style()->standardIcon(QStyle::SP_ArrowDown), ""); act_fif_find_next->setToolTip (tr ("Find next")); connect (act_fif_find_next, SIGNAL(triggered()), this, SLOT(search_find_next())); QAction *act_fif_find_prev = tb_fif->addAction (style()->standardIcon(QStyle::SP_ArrowUp), ""); act_fif_find_prev->setToolTip (tr ("Find previous")); connect (act_fif_find_prev, SIGNAL(triggered()), this, SLOT(search_find_prev())); QLabel *l_fif = new QLabel (tr ("FIF")); lt_fte->addWidget (l_fif, 0, Qt::AlignRight); lt_fte->addWidget (cmb_fif, 0); lt_fte->addWidget (tb_fif, 0); dock_fif->setWidget (w_fif); addDockWidget (Qt::BottomDockWidgetArea, dock_fif); } idx_tab_edit = main_tab_widget->addTab (tab_editor, tr ("editor")); connect (tab_editor, SIGNAL(currentChanged(int)), this, SLOT(pageChanged(int))); } void CTEA::create_actions() { icon_size = settings->value ("icon_size", "32").toInt(); // act_test = new QAction (get_theme_icon("file-save.png"), tr ("Test"), this); //connect (act_test, SIGNAL(triggered()), this, SLOT(test())); filesAct = new QAction (get_theme_icon ("current-list.png"), tr ("Files"), this); act_labels = new QAction (get_theme_icon ("labels.png"), tr ("Labels"), this); connect (act_labels, SIGNAL(triggered()), this, SLOT(nav_labels_update_list())); newAct = new QAction (get_theme_icon ("file-new.png"), tr ("New"), this); newAct->setShortcut (QKeySequence ("Ctrl+N")); newAct->setStatusTip (tr ("Create a new file")); connect (newAct, SIGNAL(triggered()), this, SLOT(file_new())); QIcon ic_file_open = get_theme_icon ("file-open.png"); ic_file_open.addFile (get_theme_icon_fname ("file-open-active.png"), QSize(), QIcon::Active); openAct = new QAction (ic_file_open, tr ("Open file"), this); openAct->setStatusTip (tr ("Open an existing file")); connect (openAct, SIGNAL(triggered()), this, SLOT(file_open())); QIcon ic_file_save = get_theme_icon ("file-save.png"); ic_file_save.addFile (get_theme_icon_fname ("file-save-active.png"), QSize(), QIcon::Active); saveAct = new QAction (ic_file_save, tr ("Save"), this); saveAct->setShortcut (QKeySequence ("Ctrl+S")); saveAct->setStatusTip (tr ("Save the document to disk")); connect (saveAct, SIGNAL(triggered()), this, SLOT(file_save())); saveAsAct = new QAction (get_theme_icon ("file-save-as.png"), tr ("Save As"), this); saveAsAct->setStatusTip (tr ("Save the document under a new name")); connect (saveAsAct, SIGNAL(triggered()), this, SLOT(file_save_as())); exitAct = new QAction (tr ("Exit"), this); exitAct->setShortcut (QKeySequence ("Ctrl+Q")); exitAct->setStatusTip (tr ("Exit the application")); connect (exitAct, SIGNAL(triggered()), this, SLOT(close())); QIcon ic_edit_cut = get_theme_icon ("edit-cut.png"); ic_edit_cut.addFile (get_theme_icon_fname ("edit-cut-active.png"), QSize(), QIcon::Active); cutAct = new QAction (ic_edit_cut, tr ("Cut"), this); cutAct->setShortcut (QKeySequence ("Ctrl+X")); cutAct->setStatusTip (tr ("Cut the current selection's contents to the clipboard")); connect (cutAct, SIGNAL(triggered()), this, SLOT(ed_cut())); QIcon ic_edit_copy = get_theme_icon ("edit-copy.png"); ic_edit_copy.addFile (get_theme_icon_fname ("edit-copy-active.png"), QSize(), QIcon::Active); copyAct = new QAction (ic_edit_copy, tr("Copy"), this); copyAct->setShortcut (QKeySequence ("Ctrl+C")); copyAct->setStatusTip (tr ("Copy the current selection's contents to the clipboard")); connect (copyAct, SIGNAL(triggered()), this, SLOT(ed_copy())); QIcon ic_edit_paste = get_theme_icon ("edit-paste.png"); ic_edit_paste.addFile (get_theme_icon_fname ("edit-paste-active.png"), QSize(), QIcon::Active); pasteAct = new QAction (ic_edit_paste, tr("Paste"), this); pasteAct->setShortcut (QKeySequence ("Ctrl+V")); pasteAct->setStatusTip (tr ("Paste the clipboard's contents into the current selection")); connect (pasteAct, SIGNAL(triggered()), this, SLOT(ed_paste())); undoAct = new QAction (tr ("Undo"), this); undoAct->setShortcut (QKeySequence ("Ctrl+Z")); connect (undoAct, SIGNAL(triggered()), this, SLOT(ed_undo())); redoAct = new QAction (tr ("Redo"), this); connect (redoAct, SIGNAL(triggered()), this, SLOT(ed_redo())); aboutAct = new QAction (tr ("About"), this); connect (aboutAct, SIGNAL(triggered()), this, SLOT(help_show_about())); aboutQtAct = new QAction (tr ("About Qt"), this); connect (aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); } void CTEA::create_menus() { /* =================== File menu =================== */ #if QT_VERSION < 0x050000 QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar); #endif menu_file = menuBar()->addMenu (tr ("File")); menu_file->setTearOffEnabled (true); //menu_file->addAction (act_test); menu_file->addAction (newAct); add_to_menu (menu_file, tr ("Open"), SLOT(file_open()), "Ctrl+O", get_theme_icon_fname ("file-open.png")); add_to_menu (menu_file, tr ("Last closed file"), SLOT(file_last_opened())); add_to_menu (menu_file, tr ("Open at cursor"), SLOT(file_open_at_cursor()), "F2"); add_to_menu (menu_file, tr ("Crapbook"), SLOT(file_crapbook())); add_to_menu (menu_file, tr ("Notes"), SLOT(file_notes())); menu_file->addSeparator(); menu_file->addAction (saveAct); menu_file->addAction (saveAsAct); QMenu *tm = menu_file->addMenu (tr ("Save more")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Save .bak"), SLOT(file_save_bak()), "Ctrl+B"); add_to_menu (tm, tr ("Save timestamped version"), SLOT(file_save_version())); add_to_menu (tm, tr ("Save session"), SLOT(file_session_save_as())); add_to_menu (tm, tr ("Save all existing"), SLOT(file_save_all_existing)); menu_file->addSeparator(); menu_file_actions = menu_file->addMenu (tr ("File actions")); add_to_menu (menu_file_actions, tr ("Reload"), SLOT(file_reload())); add_to_menu (menu_file_actions, tr ("Reload with encoding"), SLOT(file_reload_enc())); menu_file_actions->addSeparator(); add_to_menu (menu_file_actions, tr ("Set UNIX end of line"), SLOT(file_set_eol_unix())); add_to_menu (menu_file_actions, tr ("Set Windows end of line"), SLOT(file_set_eol_win())); add_to_menu (menu_file_actions, tr ("Set old Mac end of line (CR)"), SLOT(file_set_eol_mac())); menu_file_actions->addSeparator(); add_to_menu (menu_file_actions, tr ("Set as autosaving file"), SLOT(file_set_autosaving_file())); add_to_menu (menu_file_actions, tr ("Unset the autosaving file"), SLOT(file_unset_autosaving_file())); menu_file_recent = menu_file->addMenu (tr ("Recent files")); menu_file_bookmarks = menu_file->addMenu (tr ("Bookmarks")); menu_file_edit_bookmarks = menu_file->addMenu (tr ("Edit bookmarks")); add_to_menu (menu_file_edit_bookmarks, tr ("Add to bookmarks"), SLOT(file_add_to_bookmarks())); add_to_menu (menu_file_edit_bookmarks, tr ("Find obsolete paths"), SLOT(file_find_obsolete_paths())); menu_file_templates = menu_file->addMenu (tr ("Templates")); menu_file_sessions = menu_file->addMenu (tr ("Sessions")); menu_file_configs = menu_file->addMenu (tr ("Configs")); add_to_menu (menu_file_configs, tr ("Bookmarks list"), SLOT(file_open_bookmarks_file())); add_to_menu (menu_file_configs, tr ("Programs list"), SLOT(file_open_programs_file())); menu_file->addSeparator(); add_to_menu (menu_file, tr ("Do not add to recent"), SLOT(file_recent_off()))->setCheckable (true); #ifdef PRINTER_ENABLE add_to_menu (menu_file, tr ("Print"), SLOT(file_print())); #endif add_to_menu (menu_file, tr ("Close current"), SLOT(file_close()), "Ctrl+W"); menu_file->addAction (exitAct); /* =================== Edit menu =================== */ menu_edit = menuBar()->addMenu (tr ("Edit")); menu_edit->setTearOffEnabled (true); menu_edit->addAction (cutAct); menu_edit->addAction (copyAct); menu_edit->addAction (pasteAct); menu_edit->addSeparator(); add_to_menu (menu_edit, tr ("Select all"), SLOT(ed_select_all())); menu_edit->addSeparator(); add_to_menu (menu_edit, tr ("Block start"), SLOT(ed_block_start())); add_to_menu (menu_edit, tr ("Block end"), SLOT(ed_block_end())); add_to_menu (menu_edit, tr ("Copy block"), SLOT(ed_block_copy())); add_to_menu (menu_edit, tr ("Paste block"), SLOT(ed_block_paste())); add_to_menu (menu_edit, tr ("Cut block"), SLOT(ed_block_cut())); menu_edit->addSeparator(); add_to_menu (menu_edit, tr ("Copy current file name"), SLOT(ed_copy_current_fname())); menu_edit->addSeparator(); menu_edit->addAction (undoAct); menu_edit->addAction (redoAct); menu_edit->addSeparator(); add_to_menu (menu_edit, tr ("Indent (tab)"), SLOT(ed_indent())); add_to_menu (menu_edit, tr ("Un-indent (shift+tab)"), SLOT(ed_unindent())); add_to_menu (menu_edit, tr ("Indent by first line"), SLOT(ed_indent_by_first_line())); menu_edit->addSeparator(); add_to_menu (menu_edit, tr ("Comment selection"), SLOT(ed_comment())); menu_edit->addSeparator(); add_to_menu (menu_edit, tr ("Set as storage file"), SLOT(ed_set_as_storage_file())); add_to_menu (menu_edit, tr ("Copy to storage file"), SLOT(ed_copy_to_storage_file())); add_to_menu (menu_edit, tr ("Start/stop capture clipboard to storage file"), SLOT(ed_capture_clipboard_to_storage_file()))->setCheckable (true); /* =================== Markup menu =================== */ menu_markup = menuBar()->addMenu (tr ("Markup")); menu_markup->setTearOffEnabled (true); tm = menu_markup->addMenu (tr ("Mode")); tm->setTearOffEnabled (true); create_menu_from_list (this, tm, QString ("HTML XHTML Docbook LaTeX Markdown Lout DokuWiki MediaWiki").split (" "), SLOT (mrkup_mode_choosed())); tm = menu_markup->addMenu (tr ("Header")); tm->setTearOffEnabled (true); create_menu_from_list (this, tm, QString ("H1 H2 H3 H4 H5 H6").split (" "), SLOT (mrkup_header())); tm = menu_markup->addMenu (tr ("Align")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Center"), SLOT(mrkup_align_center())); add_to_menu (tm, tr ("Left"), SLOT(mrkup_align_left())); add_to_menu (tm, tr ("Right"), SLOT(mrkup_align_right())); add_to_menu (tm, tr ("Justify"), SLOT(mrkup_align_justify())); add_to_menu (menu_markup, tr ("Bold"), SLOT(mrkup_bold()), "Alt+B"); add_to_menu (menu_markup, tr ("Italic"), SLOT(mrkup_italic()), "Alt+I"); add_to_menu (menu_markup, tr ("Underline"), SLOT(mrkup_underline())); add_to_menu (menu_markup, tr ("Link"), SLOT(mrkup_link()), "Alt+L"); add_to_menu (menu_markup, tr ("Paragraph"), SLOT(mrkup_para()), "Alt+P"); add_to_menu (menu_markup, tr ("Color"), SLOT(mrkup_color())); add_to_menu (menu_markup, tr ("Break line"), SLOT(mrkup_br()), "Ctrl+Return"); add_to_menu (menu_markup, tr ("Non-breaking space"), SLOT(mrkup_nbsp()), "Ctrl+Space"); add_to_menu (menu_markup, tr ("Insert image"), SLOT(markup_ins_image())); tm = menu_markup->addMenu (tr ("[X]HTML tools")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Text to [X]HTML"), SLOT(mrkup_text_to_html())); add_to_menu (tm, tr ("Convert tags to entities"), SLOT(mrkup_tags_to_entities())); add_to_menu (tm, tr ("Antispam e-mail"), SLOT(mrkup_antispam_email())); add_to_menu (tm, tr ("Document weight"), SLOT(mrkup_document_weight())); add_to_menu (tm, tr ("Preview selected color"), SLOT(mrkup_preview_color())); add_to_menu (tm, tr ("Strip HTML tags"), SLOT(mrkup_strip_html_tags())); add_to_menu (tm, tr ("Rename selected file"), SLOT(mrkup_rename_selected())); /* =================== Search menu =================== */ menu_search = menuBar()->addMenu (tr ("Search")); menu_search->setTearOffEnabled (true); add_to_menu (menu_search, tr ("Find"), SLOT(search_find()), "Ctrl+F"); add_to_menu (menu_search, tr ("Find next"), SLOT(search_find_next()), "F3"); add_to_menu (menu_search, tr ("Find previous"), SLOT(search_find_prev()),"Ctrl+F3"); menu_search->addSeparator(); add_to_menu (menu_search, tr ("Find in files"), SLOT(search_in_files())); menu_search->addSeparator(); add_to_menu (menu_search, tr ("Replace with"), SLOT(search_replace_with())); add_to_menu (menu_search, tr ("Replace all"), SLOT(search_replace_all())); add_to_menu (menu_search, tr ("Replace all in opened files"), SLOT(search_replace_all_at_ofiles())); menu_search->addSeparator(); add_to_menu (menu_search, tr ("Mark all found"), SLOT(search_mark_all())); add_to_menu (menu_search, tr ("Unmark"), SLOT(search_unmark())); menu_search->addSeparator(); menu_find_case = menu_search->addAction (tr ("Case sensitive")); menu_find_case->setCheckable (true); menu_find_whole_words = menu_search->addAction (tr ("Whole words")); menu_find_whole_words->setCheckable (true); connect (menu_find_whole_words, SIGNAL(triggered()), this, SLOT(search_whole_words_mode())); menu_find_from_cursor = menu_search->addAction (tr ("From cursor")); menu_find_from_cursor->setCheckable (true); connect (menu_find_from_cursor, SIGNAL(triggered()), this, SLOT(search_from_cursor_mode())); menu_find_regexp = menu_search->addAction (tr ("Regexp mode")); menu_find_regexp->setCheckable (true); connect (menu_find_regexp, SIGNAL(triggered()), this, SLOT(search_regexp_mode())); menu_find_fuzzy = menu_search->addAction (tr ("Fuzzy mode")); menu_find_fuzzy->setCheckable (true); connect (menu_find_fuzzy, SIGNAL(triggered()), this, SLOT(search_fuzzy_mode())); /* =================== Functions menu =================== */ menu_functions = menuBar()->addMenu (tr ("Functions")); menu_functions->setTearOffEnabled (true); add_to_menu (menu_functions, tr ("Repeat last"), SLOT(fn_repeat())); menu_instr = menu_functions->addMenu (tr ("Tools")); menu_instr->setTearOffEnabled (true); add_to_menu (menu_instr, tr ("Scale image"), SLOT(fn_scale_image())); menu_fn_snippets = menu_functions->addMenu (tr ("Snippets")); menu_fn_scripts = menu_functions->addMenu (tr ("Scripts")); menu_fn_tables = menu_functions->addMenu (tr ("Tables")); tm = menu_functions->addMenu (tr ("Place")); tm->setTearOffEnabled (true); add_to_menu (tm, "Lorem ipsum", SLOT(fn_insert_loremipsum())); add_to_menu (tm, tr ("TEA project template"), SLOT(fn_insert_template_tea())); add_to_menu (tm, tr ("HTML template"), SLOT(fn_insert_template_html())); add_to_menu (tm, tr ("HTML5 template"), SLOT(fn_insert_template_html5())); add_to_menu (tm, tr ("C++ template"), SLOT(fn_insert_cpp())); add_to_menu (tm, tr ("C template"), SLOT(fn_insert_c())); add_to_menu (tm, tr ("Date"), SLOT(fn_insert_date())); add_to_menu (tm, tr ("Time"), SLOT(fn_insert_time())); tm = menu_functions->addMenu (tr ("Case")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("UPCASE"), SLOT(fn_case_up()),"Ctrl+Up"); add_to_menu (tm, tr ("lower case"), SLOT(fn_case_down()),"Ctrl+Down"); add_to_menu (tm, tr ("Case inverse"), SLOT(fn_case_inverse())); add_to_menu (tm, tr ("Capitalize sentences"), SLOT(fn_case_cap_sentences())); tm = menu_functions->addMenu (tr ("Sort")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Sort case sensitively"), SLOT(fn_sort_casecare())); add_to_menu (tm, tr ("Sort case insensitively"), SLOT(fn_sort_casecareless())); add_to_menu (tm, tr ("Sort case sensitively, with separator"), SLOT(fn_sort_casecare_sep())); add_to_menu (tm, tr ("Sort by length"), SLOT(fn_sort_length())); add_to_menu (tm, tr ("Flip a list"), SLOT(fn_flip_a_list())); add_to_menu (tm, tr ("Flip a list with separator"), SLOT(fn_flip_a_list_sep())); tm = menu_functions->addMenu (tr ("Cells")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Sort table by column ABC"), SLOT(fn_cells_latex_table_sort_by_col_abc())); add_to_menu (tm, tr ("Swap cells"), SLOT(fn_cells_swap_cells())); add_to_menu (tm, tr ("Delete by column"), SLOT(fn_cells_delete_by_col())); add_to_menu (tm, tr ("Copy by column[s]"), SLOT(fn_cells_copy_by_col())); tm = menu_functions->addMenu (tr ("Filter")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Remove duplicates"), SLOT(fn_filter_rm_duplicates())); add_to_menu (tm, tr ("Remove empty lines"), SLOT(fn_filter_rm_empty())); add_to_menu (tm, tr ("Remove lines < N size"), SLOT(fn_filter_rm_less_than())); add_to_menu (tm, tr ("Remove lines > N size"), SLOT(fn_filter_rm_greater_than())); add_to_menu (tm, tr ("Remove before delimiter at each line"), SLOT(fn_filter_delete_before_sep())); add_to_menu (tm, tr ("Remove after delimiter at each line"), SLOT(fn_filter_delete_after_sep())); add_to_menu (tm, tr ("Filter with regexp"), SLOT(fn_filter_with_regexp())); add_to_menu (tm, tr ("Filter by repetitions"), SLOT(fn_filter_by_repetitions())); tm = menu_functions->addMenu (tr ("Math")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Evaluate"), SLOT(fn_math_evaluate()), "F4"); add_to_menu (tm, tr ("Arabic to Roman"), SLOT(fn_math_number_arabic_to_roman())); add_to_menu (tm, tr ("Roman to Arabic"), SLOT(fn_math_number_roman_to_arabic())); add_to_menu (tm, tr ("Decimal to binary"), SLOT(fn_math_number_dec_to_bin())); add_to_menu (tm, tr ("Binary to decimal"), SLOT(fn_math_number_bin_to_dec())); add_to_menu (tm, tr ("Flip bits (bitwise complement)"), SLOT(fn_math_number_flip_bits())); add_to_menu (tm, tr ("Enumerate"), SLOT(fn_math_enum())); add_to_menu (tm, tr ("Sum by last column"), SLOT(fn_math_sum_by_last_col())); add_to_menu (tm, tr ("deg min sec > dec degrees"), SLOT(fn_math_number_dms2dc())); add_to_menu (tm, tr ("dec degrees > deg min sec"), SLOT(fn_math_number_dd2dms())); #if QT_VERSION >= 0x060000 add_to_menu (tm, tr ("Subtitles: shift timecodes by msecs"), SLOT(fn_math_srt_shift())); #endif tm = menu_functions->addMenu (tr ("Morse code")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("From Russian to Morse"), SLOT(fn_morse_from_ru())); add_to_menu (tm, tr ("From Morse To Russian"), SLOT(fn_morse_to_ru())); add_to_menu (tm, tr ("From English to Morse"), SLOT(fn_morse_from_en())); add_to_menu (tm, tr ("From Morse To English"), SLOT(fn_morse_to_en())); tm = menu_functions->addMenu (tr ("Analyze")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Text statistics"), SLOT(fn_analyze_text_stat())); add_to_menu (tm, tr ("Extract words"), SLOT(fn_analyze_extract_words())); add_to_menu (tm, tr ("Words lengths"), SLOT(fn_analyze_stat_words_lengths())); add_to_menu (tm, tr ("Count the substring"), SLOT(fn_analyze_count())); add_to_menu (tm, tr ("Count the substring (regexp)"), SLOT(fn_analyze_count_rx())); add_to_menu (tm, tr ("UNITAZ quantity sorting"), SLOT(fn_analyze_get_words_count())); add_to_menu (tm, tr ("UNITAZ sorting by alphabet"), SLOT(fn_analyze_unitaz_abc())); add_to_menu (tm, tr ("UNITAZ sorting by length"), SLOT(fn_analyze_unitaz_len())); tm = menu_functions->addMenu (tr ("Text")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Apply to each line"), SLOT(fn_text_apply_to_each_line()),"Alt+E"); add_to_menu (tm, tr ("Remove formatting"), SLOT(fn_text_remove_formatting())); add_to_menu (tm, tr ("Remove formatting at each line"), SLOT(fn_text_remove_formatting_at_each_line())); add_to_menu (tm, tr ("Remove trailing spaces"), SLOT(fn_text_remove_trailing_spaces())); add_to_menu (tm, tr ("Compress"), SLOT(fn_text_compress())); add_to_menu (tm, tr ("Anagram"), SLOT(fn_text_anagram())); add_to_menu (tm, tr ("Escape regexp"), SLOT(fn_text_escape())); add_to_menu (tm, tr ("Reverse"), SLOT(fn_text_reverse())); add_to_menu (tm, tr ("Compare two strings"), SLOT(fn_text_compare_two_strings())); add_to_menu (tm, tr ("Check regexp match"), SLOT(fn_text_regexp_match_check())); tm = menu_functions->addMenu (tr ("Quotes")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Straight to double angle quotes"), SLOT(fn_quotes_to_angle())); add_to_menu (tm, tr ("Straight to curly double quotes"), SLOT(fn_quotes_curly())); add_to_menu (tm, tr ("LaTeX: Straight to curly double quotes"), SLOT(fn_quotes_tex_curly())); add_to_menu (tm, tr ("LaTeX: Straight to double angle quotes"), SLOT(fn_quotes_tex_angle_01())); add_to_menu (tm, tr ("LaTeX: Straight to double angle quotes v2"), SLOT(fn_quotes_tex_angle_02())); #ifdef SPEECH_ENABLE tm = menu_functions->addMenu (tr ("Speech")); tm->setTearOffEnabled (true); add_to_menu (tm, tr ("Say the selected text"), SLOT(fn_speech_say_selection())); add_to_menu (tm, tr ("Speech pause/resume"), SLOT(fn_speech_pause_resume())); add_to_menu (tm, tr ("Stop speech"), SLOT(fn_speech_stop())); #endif #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) || defined (NUSPELL_ENABLE ) menu_functions->addSeparator(); menu_spell_langs = menu_functions->addMenu (tr ("Spell-checker languages")); menu_spell_langs->setTearOffEnabled (true); add_to_menu (menu_functions, tr ("Spell check"), SLOT(fn_spell_check()), "", get_theme_icon_fname ("fn-spell-check.png")); add_to_menu (menu_functions, tr ("Suggest"), SLOT(fn_spell_suggest())); add_to_menu (menu_functions, tr ("Add to dictionary"), SLOT(fn_spell_add_to_dict())); add_to_menu (menu_functions, tr ("Remove from dictionary"), SLOT(fn_spell_remove_from_dict())); menu_functions->addSeparator(); #endif /* ==================== Cal menu =================== */ menu_cal = menuBar()->addMenu (tr ("Calendar")); menu_cal->setTearOffEnabled (true); add_to_menu (menu_cal, tr ("Moon mode on/off"), SLOT(cal_moon_mode())); add_to_menu (menu_cal, tr ("Mark first date"), SLOT(cal_set_date_a())); add_to_menu (menu_cal, tr ("Mark last date"), SLOT(cal_set_date_b())); menu_cal_add = menu_cal->addMenu (tr ("Add or subtract")); menu_cal_add->setTearOffEnabled (true); add_to_menu (menu_cal_add, tr ("Days"), SLOT(cal_add_days())); add_to_menu (menu_cal_add, tr ("Months"), SLOT(cal_add_months())); add_to_menu (menu_cal_add, tr ("Years"), SLOT(cal_add_years())); menu_cal->addSeparator(); add_to_menu (menu_cal, tr ("Go to current date"), SLOT(cal_set_to_current())); add_to_menu (menu_cal, tr ("Calculate moon days between dates"), SLOT(cal_gen_mooncal())); add_to_menu (menu_cal, tr ("Number of days between two dates"), SLOT(cal_diff_days())); add_to_menu (menu_cal, tr ("Remove day record"), SLOT(cal_remove())); /* ==================== Run menu =================== */ menu_programs = menuBar()->addMenu (tr ("Run")); /* ==================== IDE menu =================== */ menu_ide = menuBar()->addMenu (tr ("IDE"));; menu_ide->setTearOffEnabled (true); add_to_menu (menu_ide, tr ("Run program"), SLOT(ide_run())); add_to_menu (menu_ide, tr ("Build program"), SLOT(ide_build())); add_to_menu (menu_ide, tr ("Clean program"), SLOT(ide_clean())); menu_ide->addSeparator(); add_to_menu (menu_ide, tr ("Toggle header/source"), SLOT(ide_toggle_hs())); /* =================== Nav menu =================== */ menu_nav = menuBar()->addMenu (tr ("Nav")); menu_nav->setTearOffEnabled (true); add_to_menu (menu_nav, tr ("Save position"), SLOT(nav_save_pos())); add_to_menu (menu_nav, tr ("Go to saved position"), SLOT(nav_goto_pos())); add_to_menu (menu_nav, tr ("Go to line"), SLOT(nav_goto_line()),"Alt+G"); add_to_menu (menu_nav, tr ("Next tab"), SLOT(nav_goto_right_tab())); add_to_menu (menu_nav, tr ("Prev tab"), SLOT(nav_goto_left_tab())); add_to_menu (menu_nav, tr ("Focus the Famous input field"), SLOT(nav_focus_to_fif()), "Ctrl+F"); add_to_menu (menu_nav, tr ("Focus the editor"), SLOT(nav_focus_to_editor())); menu_labels = menu_nav->addMenu (tr ("Labels")); add_to_menu (menu_nav, tr ("Refresh labels"), SLOT(nav_labels_update_list())); menu_current_files = menu_nav->addMenu (tr ("Current files")); /* =================== Fm menu callbacks =================== */ menu_fm = menuBar()->addMenu (tr ("Fm")); menu_fm->setTearOffEnabled (true); menu_fm_multi_rename = menu_fm->addMenu (tr ("Multi-rename")); menu_fm_multi_rename->setTearOffEnabled (true); add_to_menu (menu_fm_multi_rename, tr ("Zero pad file names"), SLOT(fman_multi_rename_zeropad())); add_to_menu (menu_fm_multi_rename, tr ("Delete N first chars at file names"), SLOT(fman_multi_rename_del_n_first_chars())); add_to_menu (menu_fm_multi_rename, tr ("Replace in file names"), SLOT(fman_multi_rename_replace())); add_to_menu (menu_fm_multi_rename, tr ("Apply template"), SLOT(fman_multi_rename_apply_template())); menu_fm_file_ops = menu_fm->addMenu (tr ("File operations")); menu_fm_file_ops->setTearOffEnabled (true); add_to_menu (menu_fm_file_ops, tr ("Create new directory"), SLOT(fman_fileop_create_dir())); add_to_menu (menu_fm_file_ops, tr ("Rename"), SLOT(fman_fileop_rename())); add_to_menu (menu_fm_file_ops, tr ("Delete file"), SLOT(fman_fileop_delete())); menu_fm_file_infos = menu_fm->addMenu (tr ("File information")); menu_fm_file_infos->setTearOffEnabled (true); add_to_menu (menu_fm_file_infos, tr ("Count lines in selected files"), SLOT(fman_fileinfo_count_lines_in_selected_files())); add_to_menu (menu_fm_file_infos, tr ("Full info"), SLOT(fm_fileinfo_info())); /*menu_fm_zip = menu_fm->addMenu (tr ("ZIP")); menu_fm_zip->setTearOffEnabled (true); menu_fm_zip->addSeparator(); add_to_menu (menu_fm_zip, tr ("Create new ZIP"), SLOT(fman_zip_create())); add_to_menu (menu_fm_zip, tr ("Add to ZIP"), SLOT(fman_zip_add())); add_to_menu (menu_fm_zip, tr ("Save ZIP"), SLOT(fman_zip_save())); menu_fm_zip->addSeparator(); add_to_menu (menu_fm_zip, tr ("List ZIP content"), SLOT(fman_zip_info())); add_to_menu (menu_fm_zip, tr ("Unpack ZIP to current directory"), SLOT(fman_zip_unpack())); */ menu_fm_img_conv = menu_fm->addMenu (tr ("Images")); menu_fm_img_conv->setTearOffEnabled (true); add_to_menu (menu_fm_img_conv, tr ("Scale by side"), SLOT(fman_img_conv_by_side())); add_to_menu (menu_fm_img_conv, tr ("Scale by percentages"), SLOT(fman_img_conv_by_percent())); add_to_menu (menu_fm_img_conv, tr ("Create web gallery"), SLOT(fman_img_make_gallery())); add_to_menu (menu_fm, tr ("Go to home dir"), SLOT(fman_home())); add_to_menu (menu_fm, tr ("Refresh current dir"), SLOT(fman_refresh())); add_to_menu (menu_fm, tr ("Preview image"), SLOT(fman_preview_image())); add_to_menu (menu_fm, tr ("Select by regexp"), SLOT(fman_select_by_regexp())); add_to_menu (menu_fm, tr ("Deselect by regexp"), SLOT(fman_deselect_by_regexp())); /* =================== View menu =================== */ menu_view = menuBar()->addMenu (tr ("View")); menu_view->setTearOffEnabled (true); menu_view_themes = menu_view->addMenu (tr ("Themes")); menu_view_themes->setTearOffEnabled (true); menu_view_palettes = menu_view->addMenu (tr ("Palettes")); menu_view_palettes->setTearOffEnabled (true); menu_view_keyboards = menu_view->addMenu (tr ("Keyboards")); menu_view_keyboards->setTearOffEnabled (true); //menu_view_hl = menu_view->addMenu (tr ("Highlighting mode")); //menu_view_hl->setTearOffEnabled (true); menu_view_profiles = menu_view->addMenu (tr ("Profiles")); menu_view_profiles->setTearOffEnabled (true); #if QT_VERSION >= 0x051400 add_to_menu (menu_view, tr ("Preview Markdown"), SLOT(view_preview_md())); #endif add_to_menu (menu_view, tr ("Save profile"), SLOT(view_profile_save_as())); add_to_menu (menu_view, tr ("Toggle word wrap"), SLOT(view_toggle_wrap())); add_to_menu (menu_view, tr ("Hide error marks"), SLOT(view_hide_error_marks())); add_to_menu (menu_view, tr ("Toggle fullscreen"), SLOT(view_toggle_fs())); add_to_menu (menu_view, tr ("Stay on top"), SLOT(view_stay_on_top())); add_to_menu (menu_view, tr ("Darker"), SLOT(view_darker())); /* =================== ? menu =================== */ helpMenu = menuBar()->addMenu ("?"); helpMenu->setTearOffEnabled (true); helpMenu->addAction (aboutAct); helpMenu->addAction (aboutQtAct); add_to_menu (helpMenu, tr ("NEWS"), SLOT(help_show_news())); add_to_menu (helpMenu, "TODO", SLOT(help_show_todo())); add_to_menu (helpMenu, "ChangeLog", SLOT(help_show_changelog())); add_to_menu (helpMenu, tr ("License"), SLOT(help_show_gpl())); } /************************* ---------------------- OPTIONS PAGE UI ---------------------- *************************/ void CTEA::create_options() { tab_options = new QTabWidget; idx_tab_tune = main_tab_widget->addTab (tab_options, tr ("options")); /* ---------------------- OPTIONS::INTERFACE ---------------------- */ QWidget *page_interface = new QWidget (tab_options); page_interface->setObjectName ("page_interface"); QVBoxLayout *page_interface_layout = new QVBoxLayout; page_interface_layout->setAlignment (Qt::AlignTop); QStringList sl_ui_modes; sl_ui_modes << tr ("Classic") << tr ("Docked"); cmb_ui_mode = new_combobox (page_interface_layout, tr ("UI mode (TEA restart is needed)"), sl_ui_modes, settings->value ("ui_mode", 0).toInt()); QStringList sl_lngs = read_dir_entries (":/translations"); for (QList ::iterator i = sl_lngs.begin(); i != sl_lngs.end(); ++i) { (*i) = i->left(2); } sl_lngs.append ("en"); QString lng = settings->value ("lng", QLocale::system().name()).toString().left(2).toLower(); if (! file_exists (":/translations/" + lng + ".qm")) lng = "en"; cmb_lng = new_combobox (page_interface_layout, tr ("UI language (TEA restart is needed)"), sl_lngs, settings->value ("lng", lng).toString()); QString default_style = qApp->style()->objectName(); if (default_style == "GTK+") //can be buggy, so disable it default_style = "Cleanlooks"; cmb_styles = new_combobox (page_interface_layout, tr ("UI style (TEA restart is needed)"), QStyleFactory::keys(), settings->value ("ui_style", default_style).toString()); connect (cmb_styles, SIGNAL(currentIndexChanged(int)), this, SLOT(slot_style_currentIndexChanged(int))); QPushButton *bt_font_interface = new QPushButton (tr ("Interface font"), this); connect (bt_font_interface, SIGNAL(clicked()), this, SLOT(slot_font_interface_select())); QPushButton *bt_font_editor = new QPushButton (tr ("Editor font"), this); connect (bt_font_editor, SIGNAL(clicked()), this, SLOT(slot_font_editor_select())); QPushButton *bt_font_logmemo = new QPushButton (tr ("Logmemo font"), this); connect (bt_font_logmemo, SIGNAL(clicked()), this, SLOT(slot_font_logmemo_select())); page_interface_layout->addWidget (bt_font_interface); page_interface_layout->addWidget (bt_font_editor); page_interface_layout->addWidget (bt_font_logmemo); QStringList sl_tabs_align; sl_tabs_align.append (tr ("Up")); sl_tabs_align.append (tr ("Bottom")); sl_tabs_align.append (tr ("Left")); sl_tabs_align.append (tr ("Right")); int ui_tab_align = settings->value ("ui_tabs_align", "3").toInt(); main_tab_widget->setTabPosition (int_to_tabpos (ui_tab_align )); QComboBox *cmb_ui_tabs_align = new_combobox (page_interface_layout, tr ("GUI tabs align"), sl_tabs_align, ui_tab_align); connect (cmb_ui_tabs_align, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_ui_tabs_currentIndexChanged(int))); int docs_tab_align = settings->value ("docs_tabs_align", "0").toInt(); tab_editor->setTabPosition (int_to_tabpos (docs_tab_align)); QComboBox *cmb_docs_tabs_align = new_combobox (page_interface_layout, tr ("Documents tabs align"), sl_tabs_align, docs_tab_align); connect (cmb_docs_tabs_align, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_docs_tabs_currentIndexChanged(int))); QStringList sl_icon_sizes; sl_icon_sizes << "16" << "24" << "32" << "48" << "64"; cmb_icon_size = new_combobox (page_interface_layout, tr ("Icons size"), sl_icon_sizes, settings->value ("icon_size", "32").toString()); connect (cmb_icon_size, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_icon_sizes_currentIndexChanged(int))); QStringList sl_tea_icons; sl_tea_icons.append ("1"); sl_tea_icons.append ("2"); sl_tea_icons.append ("3"); cmb_tea_icons = new_combobox (page_interface_layout, tr ("TEA program icon"), sl_tea_icons, settings->value ("icon_fname", "1").toString()); connect (cmb_tea_icons, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_tea_icons_currentIndexChanged(int))); cb_fif_at_toolbar = new QCheckBox (tr ("FIF at the top (restart is needed)"), tab_options); cb_fif_at_toolbar->setChecked (settings->value ("fif_at_toolbar", "0").toBool()); page_interface_layout->addWidget (cb_fif_at_toolbar); cb_show_linenums = new QCheckBox (tr ("Show line numbers"), tab_options); cb_show_linenums->setChecked (settings->value ("show_linenums", "0").toBool()); page_interface_layout->addWidget (cb_show_linenums); cb_wordwrap = new QCheckBox (tr ("Word wrap"), tab_options); cb_wordwrap->setChecked (settings->value ("word_wrap", "1").toBool()); page_interface_layout->addWidget (cb_wordwrap); cb_show_tabs_and_spaces = new QCheckBox (tr ("Show tabs and spaces"), tab_options); cb_show_tabs_and_spaces->setChecked (settings->value ("show_tabs_and_spaces", "0").toBool()); page_interface_layout->addWidget (cb_show_tabs_and_spaces); cb_hl_enabled = new QCheckBox (tr ("Syntax highlighting enabled"), tab_options); cb_hl_enabled->setChecked (settings->value ("hl_enabled", "1").toBool()); page_interface_layout->addWidget (cb_hl_enabled); cb_hl_current_line = new QCheckBox (tr ("Highlight current line"), tab_options); cb_hl_current_line->setChecked (settings->value ("additional_hl", "0").toBool()); page_interface_layout->addWidget (cb_hl_current_line); cb_hl_brackets = new QCheckBox (tr ("Highlight paired brackets"), tab_options); cb_hl_brackets->setChecked (settings->value ("hl_brackets", "0").toBool()); page_interface_layout->addWidget (cb_hl_brackets); cb_auto_indent = new QCheckBox (tr ("Automatic indent"), tab_options); cb_auto_indent->setChecked (settings->value ("auto_indent", "0").toBool()); page_interface_layout->addWidget (cb_auto_indent); cb_spaces_instead_of_tabs = new QCheckBox (tr ("Use spaces instead of tabs"), tab_options); cb_spaces_instead_of_tabs->setChecked (settings->value ("spaces_instead_of_tabs", "1").toBool()); page_interface_layout->addWidget (cb_spaces_instead_of_tabs); spb_tab_sp_width = new_spin_box (page_interface_layout, tr ("Tab width in spaces"), 1, 64, settings->value ("tab_sp_width", 8).toInt()); cb_cursor_xy_visible = new QCheckBox (tr ("Show cursor position"), tab_options); cb_cursor_xy_visible->setChecked (settings->value ("cursor_xy_visible", "1").toBool()); page_interface_layout->addWidget (cb_cursor_xy_visible); cb_center_on_cursor = new QCheckBox (tr ("Cursor center on scroll"), tab_options); cb_center_on_cursor->setChecked (settings->value ("center_on_scroll", "1").toBool()); page_interface_layout->addWidget (cb_center_on_cursor); spb_cursor_blink_time = new_spin_box (page_interface_layout, tr ("Cursor blink time (msecs, zero is OFF)"), 0, 10000, settings->value ("cursor_blink_time", 0).toInt()); spb_cursor_width = new_spin_box (page_interface_layout, tr ("Cursor width"), 1, 5, settings->value ("cursor_width", 2).toInt()); cb_show_margin = new QCheckBox (tr ("Show margin at"), tab_options); cb_show_margin->setChecked (settings->value ("show_margin", "0").toBool()); spb_margin_pos = new QSpinBox; spb_margin_pos->setValue (settings->value ("margin_pos", 72).toInt()); QHBoxLayout *lt_margin = new QHBoxLayout; lt_margin->insertWidget (-1, cb_show_margin, 0, Qt::AlignLeft); lt_margin->insertWidget (-1, spb_margin_pos, 1, Qt::AlignLeft); page_interface_layout->addLayout (lt_margin); cb_full_path_at_window_title = new QCheckBox (tr ("Show full path at window title"), tab_options); cb_full_path_at_window_title->setChecked (settings->value ("full_path_at_window_title", "1").toBool()); page_interface_layout->addWidget (cb_full_path_at_window_title); page_interface->setLayout (page_interface_layout); page_interface->show(); QScrollArea *scra_interface = new QScrollArea; scra_interface->setWidgetResizable (true); scra_interface->setWidget (page_interface); tab_options->addTab (scra_interface, tr ("Interface")); /* ---------------------- OPTIONS::COMMON ---------------------- */ QWidget *page_common = new QWidget (tab_options); QVBoxLayout *page_common_layout = new QVBoxLayout; page_common_layout->setAlignment (Qt::AlignTop); /* cb_altmenu = new QCheckBox (tr ("Use Alt key to access main menu"), tab_options); cb_altmenu->setChecked (MyProxyStyle::b_altmenu); connect (cb_altmenu, SIGNAL(stateChanged(int)), this, SLOT(cb_altmenu_stateChanged(int)));*/ /* cb_wasd = new QCheckBox (tr ("Use Left Alt + WASD as additional cursor keys"), tab_options); cb_wasd->setChecked (settings->value ("wasd", "0").toBool()); */ #if defined(JOYSTICK_SUPPORTED) cb_use_joystick = new QCheckBox (tr ("Use joystick as cursor keys"), tab_options); cb_use_joystick->setChecked (settings->value ("use_joystick", "0").toBool()); connect (cb_use_joystick, SIGNAL(stateChanged(int)), this, SLOT(cb_use_joystick_stateChanged(int))); #endif cb_auto_img_preview = new QCheckBox (tr ("Automatic preview images at file manager"), tab_options); cb_auto_img_preview->setChecked (settings->value ("b_preview", "0").toBool()); cb_session_restore = new QCheckBox (tr ("Restore the last session on start-up"), tab_options); cb_session_restore->setChecked (settings->value ("session_restore", "0").toBool()); //cb_use_enca_for_charset_detection = new QCheckBox (tr ("Use Enca for charset detection"), tab_options); //cb_use_enca_for_charset_detection->setChecked (settings->value ("use_enca_for_charset_detection", 0).toBool()); cb_override_img_viewer = new QCheckBox (tr ("Use external image viewer for F2"), tab_options); cb_override_img_viewer->setChecked (settings->value ("override_img_viewer", 0).toBool()); ed_img_viewer_override = new QLineEdit (this); ed_img_viewer_override->setText (settings->value ("img_viewer_override_command", "display %s").toString()); QHBoxLayout *hb_imgvovr = new QHBoxLayout; hb_imgvovr->addWidget (cb_override_img_viewer); hb_imgvovr->addWidget (ed_img_viewer_override); hb_imgvovr->insertWidget (-1, cb_override_img_viewer, 0, Qt::AlignLeft); hb_imgvovr->insertWidget (-1, ed_img_viewer_override, 1, Qt::AlignLeft); cb_use_trad_dialogs = new QCheckBox (tr ("Use traditional File Save/Open dialogs"), tab_options); cb_use_trad_dialogs->setChecked (settings->value ("use_trad_dialogs", "1").toBool()); cb_start_on_sunday = new QCheckBox (tr ("Start week on Sunday"), tab_options); cb_start_on_sunday->setChecked (settings->value ("start_week_on_sunday", "0").toBool()); cb_northern_hemisphere = new QCheckBox (tr ("Northern hemisphere"), this); cb_northern_hemisphere->setChecked (settings->value ("northern_hemisphere", "1").toBool()); page_common_layout->addWidget (cb_start_on_sunday); page_common_layout->addWidget (cb_northern_hemisphere); QStringList sl_moon_algos; sl_moon_algos << "0" << "1" << "2" << "3"; /* cmb_moon_phase_algos = new_combobox (page_common_layout, tr ("Moon phase algorithm"), moon_phase_algos.values(), settings->value ("moon_phase_algo", MOON_PHASE_TRIG2).toInt()); */ cmb_moon_phase_algos = new_combobox (page_common_layout, tr ("Moon phase algorithm"), sl_moon_algos, settings->value ("moon_phase_algo", MOON_PHASE_TRIG2).toInt()); cmb_moon_phase_algos->setToolTip (tr ("0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - Leueshkanov")); int cmdline_charset_index = sl_charsets.indexOf (settings->value ("cmdline_default_charset", "UTF-8").toString()); if (cmdline_charset_index < 0) cmdline_charset_index = 0; if (cmdline_charset_index > sl_charsets.size() - 1) cmdline_charset_index = 0; cmb_cmdline_default_charset = new_combobox (page_common_layout, tr ("Charset for file open from command line"), sl_charsets, cmdline_charset_index); /* cmb_zip_charset_in = new_combobox (page_common_layout, tr ("ZIP unpacking: file names charset"), sl_charsets, sl_charsets.indexOf (settings->value ("zip_charset_in", "UTF-8").toString())); cmb_zip_charset_out = new_combobox (page_common_layout, tr ("ZIP packing: file names charset"), sl_charsets, sl_charsets.indexOf (settings->value ("zip_charset_out", "UTF-8").toString())); */ //page_common_layout->addWidget (cb_altmenu); //page_common_layout->addWidget (cb_wasd); #if defined(JOYSTICK_SUPPORTED) page_common_layout->addWidget (cb_use_joystick); #endif page_common_layout->addWidget (cb_auto_img_preview); page_common_layout->addWidget (cb_session_restore); page_common_layout->addWidget (cb_use_trad_dialogs); //page_common_layout->addWidget (cb_use_enca_for_charset_detection); page_common_layout->addLayout (hb_imgvovr); QGroupBox *gb_common_autosave = new QGroupBox (tr ("Autosaving")); QVBoxLayout *vb_common_autosave = new QVBoxLayout; vb_common_autosave->setAlignment (Qt::AlignTop); gb_common_autosave->setLayout (vb_common_autosave); cb_save_buffers = new QCheckBox (tr ("Temporary save unsaved buffers on exit"), tab_options); cb_save_buffers->setChecked (settings->value ("save_buffers", "0").toBool()); vb_common_autosave->addWidget (cb_save_buffers); cb_autosave = new QCheckBox (tr ("Autosave buffers and autosaving files (each N seconds)"), tab_options); cb_autosave->setChecked (settings->value ("autosave", "0").toBool()); spb_autosave_period = new QSpinBox; spb_autosave_period->setMinimum (5); spb_autosave_period->setValue (settings->value ("autosave_period", 30).toInt()); QHBoxLayout *lt_autosave = new QHBoxLayout; lt_autosave->insertWidget (-1, cb_autosave, 0, Qt::AlignLeft); lt_autosave->insertWidget (-1, spb_autosave_period, 1, Qt::AlignLeft); vb_common_autosave->addLayout (lt_autosave); page_common_layout->addWidget (gb_common_autosave); page_common->setLayout (page_common_layout); page_common->show(); QScrollArea *scra_common = new QScrollArea; scra_common->setWidgetResizable (true); scra_common->setWidget (page_common); tab_options->addTab (scra_common, tr ("Common")); /* ---------------------- OPTIONS::FUNCTIONS ---------------------- */ QWidget *page_functions = new QWidget (tab_options); QVBoxLayout *page_functions_layout = new QVBoxLayout; page_functions_layout->setAlignment (Qt::AlignTop); QGroupBox *gb_labels = new QGroupBox (tr ("Labels")); QVBoxLayout *vb_labels = new QVBoxLayout; gb_labels->setLayout (vb_labels); ed_label_start = new_line_edit (vb_labels, tr ("Label starts with: "), settings->value ("label_start", "[?").toString()); ed_label_end = new_line_edit (vb_labels, tr ("Label ends with: "), settings->value ("label_end", "?]").toString()); page_functions_layout->addWidget (gb_labels); QGroupBox *gb_datetime = new QGroupBox (tr ("Date and time")); QVBoxLayout *vb_datetime = new QVBoxLayout; gb_datetime->setLayout (vb_datetime); ed_date_format = new_line_edit (vb_datetime, tr ("Date format"), settings->value ("date_format", "dd/MM/yyyy").toString()); ed_time_format = new_line_edit (vb_datetime, tr ("Time format"), settings->value ("time_format", "hh:mm:ss").toString()); page_functions_layout->addWidget (gb_datetime); QLabel *l_t = 0; #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) || defined (NUSPELL_ENABLE ) QGroupBox *gb_spell = new QGroupBox (tr ("Spell checking")); QVBoxLayout *vb_spell = new QVBoxLayout; gb_spell->setLayout(vb_spell); QHBoxLayout *hb_spellcheck_engine = new QHBoxLayout; cmb_spellcheckers = new_combobox (hb_spellcheck_engine, tr ("Spell checker engine"), spellcheckers, cur_spellchecker); vb_spell->addLayout (hb_spellcheck_engine); #ifdef HUNSPELL_ENABLE QHBoxLayout *hb_spellcheck_path = new QHBoxLayout; l_t = new QLabel (tr ("Hunspell dictionaries directory")); ed_spellcheck_path = new QLineEdit (this); ed_spellcheck_path->setText (settings->value ("hunspell_dic_path", hunspell_default_dict_path()).toString());/*QDir::homePath ()).toString()*/ ed_spellcheck_path->setReadOnly (true); QPushButton *pb_choose_path = new QPushButton (tr ("Select"), this); connect (pb_choose_path, SIGNAL(clicked()), this, SLOT(pb_choose_hunspell_path_clicked())); hb_spellcheck_path->addWidget (l_t); hb_spellcheck_path->addWidget (ed_spellcheck_path); hb_spellcheck_path->addWidget (pb_choose_path); vb_spell->addLayout (hb_spellcheck_path); #endif #ifdef ASPELL_ENABLE #if defined(Q_OS_WIN) || defined(Q_OS_OS2) QHBoxLayout *hb_aspellcheck_path = new QHBoxLayout; l_t = new QLabel (tr ("Aspell directory")); ed_aspellcheck_path = new QLineEdit (this); ed_aspellcheck_path->setText (settings->value ("win32_aspell_path", aspell_default_dict_path()).toString()); ed_aspellcheck_path->setReadOnly (true); QPushButton *pb_choose_path2 = new QPushButton (tr ("Select"), this); connect (pb_choose_path2, SIGNAL(clicked()), this, SLOT(pb_choose_aspell_path_clicked())); hb_aspellcheck_path->addWidget (l_t); hb_aspellcheck_path->addWidget (ed_aspellcheck_path); hb_aspellcheck_path->addWidget (pb_choose_path2); vb_spell->addLayout (hb_aspellcheck_path); #endif #endif connect (cmb_spellcheckers, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_spellchecker_currentIndexChanged(int))); page_functions_layout->addWidget (gb_spell); #endif QGroupBox *gb_func_misc = new QGroupBox (tr ("Miscellaneous")); QVBoxLayout *vb_func_misc = new QVBoxLayout; vb_func_misc->setAlignment (Qt::AlignTop); gb_func_misc->setLayout (vb_func_misc); spb_fuzzy_q = new_spin_box (vb_func_misc, tr ("Fuzzy search factor"), 10, 100, settings->value ("fuzzy_q", "60").toInt()); page_functions_layout->addWidget (gb_func_misc); cb_show_ebooks_fine = new QCheckBox (tr ("Show ebooks fine"), tab_options); cb_show_ebooks_fine->setChecked (settings->value ("show_ebooks_fine", "0").toBool()); vb_func_misc->addWidget (cb_show_ebooks_fine); page_functions->setLayout (page_functions_layout); page_functions->show(); QScrollArea *scra_functions = new QScrollArea; scra_functions->setWidgetResizable (true); scra_functions->setWidget (page_functions); tab_options->addTab (scra_functions, tr ("Functions")); /* ---------------------- OPTIONS::IMAGES ---------------------- */ QWidget *page_images = new QWidget (tab_options); QVBoxLayout *page_images_layout = new QVBoxLayout; page_images_layout->setAlignment (Qt::AlignTop); QGroupBox *gb_images = new QGroupBox (tr ("Miscellaneous")); QVBoxLayout *vb_images = new QVBoxLayout; vb_images->setAlignment (Qt::AlignTop); gb_images->setLayout (vb_images); cmb_output_image_fmt = new_combobox (vb_images, tr ("Image conversion output format"), bytearray_to_stringlist (QImageWriter::supportedImageFormats()), settings->value ("output_image_fmt", "jpg").toString()); cb_output_image_flt = new QCheckBox (tr ("Scale images with bilinear filtering"), this); cb_output_image_flt->setChecked (settings->value ("img_filter", 0).toBool()); vb_images->addWidget (cb_output_image_flt); spb_img_quality = new_spin_box (vb_images, tr ("Output images quality"), -1, 100, settings->value ("img_quality", "-1").toInt()); cb_exif_rotate = new QCheckBox (tr ("Apply hard rotation by EXIF data"), this); cb_exif_rotate->setChecked (settings->value ("cb_exif_rotate", 1).toBool()); cb_output_image_flt = new QCheckBox (tr ("Scale images with bilinear filtering"), this); cb_output_image_flt->setChecked (settings->value ("img_filter", 0).toBool()); vb_images->addWidget (cb_output_image_flt); //cb_zip_after_scale = new QCheckBox (tr ("Zip directory with processed images"), this); //cb_zip_after_scale->setChecked (settings->value ("img_post_proc", 0).toBool()); //vb_images->addWidget (cb_zip_after_scale); vb_images->addWidget (cb_exif_rotate); page_images_layout->addWidget (gb_images); QGroupBox *gb_webgallery = new QGroupBox (tr ("Web gallery options")); QVBoxLayout *vb_webgal = new QVBoxLayout; vb_webgal->setAlignment (Qt::AlignTop); ed_side_size = new_line_edit (vb_webgal, tr ("Size of the side"), settings->value ("ed_side_size", "110").toString()); ed_link_options = new_line_edit (vb_webgal, tr ("Link options"), settings->value ("ed_link_options", "target=\"_blank\"").toString()); ed_cols_per_row = new_line_edit (vb_webgal, tr ("Columns per row"), settings->value ("ed_cols_per_row", "4").toString()); gb_webgallery->setLayout(vb_webgal); page_images_layout->addWidget (gb_webgallery); QGroupBox *gb_exif = new QGroupBox (tr ("EXIF")); QVBoxLayout *vb_exif = new QVBoxLayout; gb_exif->setLayout(vb_exif); page_images_layout->addWidget (gb_exif); cb_zor_use_exif = new QCheckBox (tr ("Use EXIF orientation at image viewer"), this); cb_zor_use_exif->setChecked (settings->value ("zor_use_exif_orientation", 0).toBool()); vb_exif->addWidget (cb_zor_use_exif); page_images->setLayout (page_images_layout); QScrollArea *scra_images = new QScrollArea; scra_images->setWidgetResizable (true); scra_images->setWidget (page_images); tab_options->addTab (scra_images, tr ("Images")); /* ---------------------- OPTIONS::SPEECH ---------------------- */ #ifdef SPEECH_ENABLE QWidget *page_speech = new QWidget (tab_options); page_speech->setObjectName ("page_speech"); QVBoxLayout *page_speech_layout = new QVBoxLayout; page_speech_layout->setAlignment (Qt::AlignTop); page_speech->setLayout (page_speech_layout); cb_speech_enable = new QCheckBox (tr ("Enable speech (TEA restart is needed)"), this); cb_speech_enable->setChecked (settings->value ("speech_enabled", 0).toBool()); page_speech_layout->addWidget (cb_speech_enable); cb_locale_only = new QCheckBox (tr ("Enable locale only voices"), this); cb_locale_only->setChecked (settings->value ("locale_only", 1).toBool()); page_speech_layout->addWidget (cb_locale_only); connect (cb_locale_only, SIGNAL(stateChanged(int)), this, SLOT(cb_locale_only_stateChanged(int))); cmb_cpeech_voices = new_combobox_from_vector (page_speech_layout, tr ("Speach voice"), speech.voices, speech.current_voice_index); connect (cmb_cpeech_voices, SIGNAL(currentIndexChanged(int)), this, SLOT(cmb_cpeech_voices_currentIndexChanged(int))); idx_tab_speech = tab_options->addTab (page_speech, tr ("Speech")); #endif /* ---------------------- OPTIONS::KEYBOARD ---------------------- */ QWidget *page_keyboard = new QWidget (tab_options); QHBoxLayout *lt_h = new QHBoxLayout; QHBoxLayout *lt_shortcut = new QHBoxLayout; QVBoxLayout *lt_vkeys = new QVBoxLayout; QVBoxLayout *lt_vbuttons = new QVBoxLayout; lv_menuitems = new QListWidget; lt_vkeys->addWidget (lv_menuitems); connect (lv_menuitems, SIGNAL(currentItemChanged (QListWidgetItem*,QListWidgetItem*)), this, SLOT(slot_lv_menuitems_currentItemChanged(QListWidgetItem*,QListWidgetItem*))); ent_shtcut = new CShortcutEntry; QLabel *l_shortcut = new QLabel (tr ("Shortcut")); lt_shortcut->addWidget (l_shortcut); lt_shortcut->addWidget (ent_shtcut); lt_vbuttons->addLayout (lt_shortcut); QPushButton *pb_assign_hotkey = new QPushButton (tr ("Assign"), this); QPushButton *pb_remove_hotkey = new QPushButton (tr ("Remove"), this); connect (pb_assign_hotkey, SIGNAL(clicked()), this, SLOT(pb_assign_hotkey_clicked())); connect (pb_remove_hotkey, SIGNAL(clicked()), this, SLOT(pb_remove_hotkey_clicked())); lt_vbuttons->addWidget (pb_assign_hotkey); lt_vbuttons->addWidget (pb_remove_hotkey, 0, Qt::AlignTop); lt_h->addLayout (lt_vkeys); lt_h->addLayout (lt_vbuttons); page_keyboard->setLayout (lt_h); page_keyboard->show(); idx_tab_keyboard = tab_options->addTab (page_keyboard, tr ("Keyboard")); } void CTEA::create_calendar() { calendar = new CCalendarWidget (this, dir_days); calendar->moon_mode = settings->value ("moon_mode", "0").toBool(); calendar->northern_hemisphere = settings->value ("northern_hemisphere", "1").toBool(); calendar->moon_phase_algo = settings->value ("moon_phase_algo", MOON_PHASE_TRIG2).toInt(); calendar->setGridVisible (true); calendar->setVerticalHeaderFormat (QCalendarWidget::NoVerticalHeader); if (settings->value ("start_on_sunday", "0").toBool()) calendar->setFirstDayOfWeek (Qt::Sunday); else calendar->setFirstDayOfWeek (Qt::Monday); connect (calendar, SIGNAL(clicked(QDate)), this, SLOT(calendar_clicked(QDate))); connect (calendar, SIGNAL(activated(QDate)), this, SLOT(calendar_activated(QDate))); connect (calendar, SIGNAL(currentPageChanged(int,int)), this, SLOT(calendar_currentPageChanged(int,int))); idx_tab_calendar = main_tab_widget->addTab (calendar, tr ("dates")); } void CTEA::create_toolbars() { openAct->setMenu (menu_file_recent); filesAct->setMenu (menu_current_files); act_labels->setMenu (menu_labels); fileToolBar = addToolBar (tr ("File")); fileToolBar->setObjectName ("fileToolBar"); fileToolBar->addAction (newAct); fileToolBar->addAction (openAct); fileToolBar->addAction (saveAct); editToolBar = addToolBar (tr ("Edit")); editToolBar->setObjectName ("editToolBar"); editToolBar->addAction (cutAct); editToolBar->addAction (copyAct); editToolBar->addAction (pasteAct); if (! settings->value ("fif_at_toolbar", 0).toBool()) { editToolBar->addSeparator(); editToolBar->addAction (act_labels); } filesToolBar = addToolBar (tr ("Files")); filesToolBar->setObjectName ("filesToolBar"); filesToolBar->setIconSize (QSize (icon_size, icon_size)); QToolButton *tb_current_list = new QToolButton(); tb_current_list->setIcon (get_theme_icon ("current-list.png")); tb_current_list->setMenu (menu_current_files); tb_current_list->setPopupMode(QToolButton::InstantPopup); filesToolBar->addWidget (tb_current_list); if (settings->value ("fif_at_toolbar", 0).toBool()) { fifToolBar = addToolBar (tr ("FIF")); fifToolBar->setObjectName ("fifToolBar"); cmb_fif = new QComboBox; cmb_fif->setInsertPolicy (QComboBox::InsertAtTop); cmb_fif->setObjectName ("FIF"); cmb_fif->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed); cmb_fif->setEditable (true); fif = cmb_fif->lineEdit(); connect (fif, SIGNAL(returnPressed()), this, SLOT(search_find())); fifToolBar->addWidget (cmb_fif); QAction *act_fif_find = fifToolBar->addAction (style()->standardIcon(QStyle::SP_ArrowForward), ""); act_fif_find->setToolTip (tr ("Find")); connect (act_fif_find, SIGNAL(triggered()), this, SLOT(search_find())); QAction *act_fif_find_next = fifToolBar->addAction (style()->standardIcon(QStyle::SP_ArrowDown), ""); act_fif_find_next->setToolTip (tr ("Find next")); connect (act_fif_find_next, SIGNAL(triggered()), this, SLOT(search_find_next())); QAction *act_fif_find_prev = fifToolBar->addAction (style()->standardIcon(QStyle::SP_ArrowUp), ""); act_fif_find_prev->setToolTip (tr ("Find previous")); connect (act_fif_find_prev, SIGNAL(triggered()), this, SLOT(search_find_prev())); } } #ifdef SPEECH_ENABLE void CTEA::create_speech() { // qDebug() << "---------CTEA::create_speech()--------1"; speech.locale_only = settings->value ("locale_only", 1).toInt(); if (! settings->value ("speech_enabled", false).toBool()) return; //else init speech.init ("tea"); speech.get_voices (speech.locale_only); speech.current_voice_index = settings->value ("current_voice_index", 0).toInt(); if (speech.current_voice_index < speech.voices.size() && speech.current_voice_index > -1) speech.set_voice_by_index (speech.current_voice_index); //qDebug() << "speech.voices.size(): " << speech.voices.size(); // qDebug() << "---------CTEA::create_speech()--------2"; } #endif void CTEA::create_manual() { QWidget *wd_man = new QWidget (this); QVBoxLayout *lv_t = new QVBoxLayout; QString loc = QLocale::system().name().left (2).toLower(); QString ts = settings->value ("lng", loc).toString(); QString filename (":/manuals/"); filename = filename + ts + ".html"; if (! file_exists (filename)) filename = ":/manuals/en.html"; man_search_value = ""; QHBoxLayout *lh_controls = new QHBoxLayout(); QPushButton *bt_back = new QPushButton ("<"); QPushButton *bt_forw = new QPushButton (">"); lh_controls->addWidget (bt_back); lh_controls->addWidget (bt_forw); man = new QTextBrowser; man->setOpenExternalLinks (true); man->setSource (QUrl ("qrc" + filename)); connect (bt_back, SIGNAL(clicked()), man, SLOT(backward())); connect (bt_forw, SIGNAL(clicked()), man, SLOT(forward())); lv_t->addLayout (lh_controls); lv_t->addWidget (man); wd_man->setLayout (lv_t); idx_tab_learn = main_tab_widget->addTab (wd_man, tr ("manual")); } void CTEA::create_fman() { QWidget *wd_fman = new QWidget (this); QVBoxLayout *lav_main = new QVBoxLayout; QVBoxLayout *lah_controls = new QVBoxLayout; QHBoxLayout *lah_topbar = new QHBoxLayout; QLabel *l_t = new QLabel (tr ("Name")); ed_fman_fname = new QLineEdit; connect (ed_fman_fname, SIGNAL(returnPressed()), this, SLOT(fman_fname_entry_confirm())); ed_fman_path = new QLineEdit; connect (ed_fman_path, SIGNAL(returnPressed()), this, SLOT(fman_naventry_confirm())); tb_fman_dir = new QToolBar; tb_fman_dir->setObjectName ("tb_fman_dir"); QAction *act_fman_go = new QAction (style()->standardIcon(QStyle::SP_ArrowForward), tr ("Go"), this); connect (act_fman_go, SIGNAL(triggered()), this, SLOT(fman_naventry_confirm())); QAction *act_fman_home = new QAction (style()->standardIcon(QStyle::SP_DirHomeIcon), tr ("Home"), this); connect (act_fman_home, SIGNAL(triggered()), this, SLOT(fman_home())); QAction *act_fman_refresh = new QAction (style()->standardIcon(QStyle::SP_BrowserReload), tr ("Refresh"), this); QAction *act_fman_ops = new QAction (style()->standardIcon(QStyle::SP_DriveHDIcon), tr ("Actions"), this); act_fman_ops->setMenu (menu_fm_file_ops); tb_fman_dir->addAction (act_fman_go); tb_fman_dir->addAction (act_fman_home); tb_fman_dir->addAction (act_fman_refresh); tb_fman_dir->addAction (act_fman_ops); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) cb_fman_drives = new QComboBox; lah_topbar->addWidget (cb_fman_drives); QFileInfoList l_drives = QDir::drives(); for (QList ::iterator fi = l_drives.begin(); fi != l_drives.end(); ++fi) cb_fman_drives->addItem (fi->path()); #endif lah_topbar->addWidget (ed_fman_path); lah_topbar->addWidget (tb_fman_dir); lah_controls->addWidget (l_t); lah_controls->addWidget (ed_fman_fname); l_t = new QLabel (tr ("Charset")); // QPushButton *bt_magicenc = new QPushButton ("?", this); //bt_magicenc->setToolTip (tr ("Guess encoding!")); // connect (bt_magicenc, SIGNAL(clicked()), this, SLOT(guess_enc())); /* #if QT_VERSION >= 0x051100 bt_magicenc->setMaximumWidth (QApplication::fontMetrics().horizontalAdvance("???")); #else bt_magicenc->setMaximumWidth (QApplication::fontMetrics().width ("???")); #endif */ cb_fman_codecs = new QComboBox; //if (sl_last_used_charsets.size () > 0) // cb_fman_codecs->addItems (sl_last_used_charsets + sl_charsets); //else cb_fman_codecs->addItems (sl_charsets); int charset_index = sl_charsets.indexOf ("UTF-8"); cb_fman_codecs->setCurrentIndex (charset_index); QPushButton *bt_fman_open = new QPushButton (tr ("Open"), this); connect (bt_fman_open, SIGNAL(clicked()), this, SLOT(fman_open())); bt_fman_open->setToolTip (tr ("Open a file from the file name provided above")); QPushButton *bt_fman_save_as = new QPushButton (tr ("Save as"), this); connect (bt_fman_save_as, SIGNAL(clicked()), this, SLOT(cb_button_saves_as())); bt_fman_save_as->setToolTip (tr ("Save the current opened file with the name provided above")); lah_controls->addWidget (l_t); QHBoxLayout *lt_hb = new QHBoxLayout; lt_hb->addWidget (cb_fman_codecs); //lt_hb->addWidget (bt_magicenc); lah_controls->addLayout (lt_hb); lah_controls->addWidget (bt_fman_open); lah_controls->addWidget (bt_fman_save_as); fman = new CFMan; connect (fman, SIGNAL(file_activated(QString)), this, SLOT(fman_file_activated(QString))); connect (fman, SIGNAL(dir_changed(QString)), this, SLOT(fman_dir_changed(QString))); connect (fman, SIGNAL(current_file_changed(QString,QString)), this, SLOT(fman_current_file_changed(QString,QString))); connect (act_fman_refresh, SIGNAL(triggered()), fman, SLOT(refresh())); #if defined(Q_OS_WIN) || defined(Q_OS_OS2) connect (cb_fman_drives, SIGNAL(currentIndexChanged (QString)), this, SLOT(fman_drives_changed(QString))); #endif w_right = new QWidget (this); w_right->setMinimumWidth (10); QVBoxLayout *lw_right = new QVBoxLayout; w_right->setLayout (lw_right); lw_right->addLayout (lah_controls); QFrame *vline = new QFrame; vline->setFrameStyle (QFrame::HLine); lw_right->addWidget (vline); QLabel *l_bookmarks = new QLabel (tr ("Bookmarks")); lw_right->addWidget (l_bookmarks); QHBoxLayout *lah_places_bar = new QHBoxLayout; QPushButton *bt_add_bmk = new QPushButton ("+"); QPushButton *bt_del_bmk = new QPushButton ("-"); lah_places_bar->addWidget (bt_add_bmk); lah_places_bar->addWidget (bt_del_bmk); connect (bt_add_bmk, SIGNAL(clicked()), this, SLOT(fman_add_bmk())); connect (bt_del_bmk, SIGNAL(clicked()), this, SLOT(fman_del_bmk())); lv_places = new QListWidget; //lv_places->setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn); update_places_bookmarks(); connect (lv_places, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(fman_places_itemActivated(QListWidgetItem*))); QVBoxLayout *vbox = new QVBoxLayout; vbox->addLayout (lah_places_bar); vbox->addWidget (lv_places); lw_right->addLayout (vbox); //commented out with Qt6 // fman->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::Maximum); spl_fman = new QSplitter (this); spl_fman->setChildrenCollapsible (true); spl_fman->addWidget (fman); spl_fman->addWidget (w_right); spl_fman->restoreState (settings->value ("spl_fman").toByteArray()); lav_main->addLayout (lah_topbar); lav_main->addWidget (spl_fman); wd_fman->setLayout (lav_main); fman_home(); idx_tab_fman = main_tab_widget->addTab (wd_fman, tr ("files")); } void CTEA::create_markup_hash() { QHash h1; h1["Docbook"] = "%s"; h1["LaTeX"] = "\\textbf{%s}"; h1["HTML"] = "%s"; h1["XHTML"] = "%s"; h1["Lout"] = "@B{%s}"; h1["MediaWiki"] = "'''%s'''"; h1["DokuWiki"] = "**%s**"; h1["Markdown"] = "**%s**"; hash_markup.insert ("bold", h1); QHash h2; h2["Docbook"] = "%s"; h2["LaTeX"] = "\\textit{%s}"; h2["HTML"] = "%s"; h2["XHTML"] = "%s"; h2["Lout"] = "@I{%s}"; h2["MediaWiki"] = "''%s''"; h2["DokuWiki"] = "//%s//"; h2["Markdown"] = "*%s*"; hash_markup.insert ("italic", h2); QHash h3; h3["HTML"] = "

%s

"; h3["XHTML"] = "

%s

"; hash_markup.insert ("align_justify", h3); QHash h4; h4["LaTeX"] = "\\begin{center}%s\\end{center}"; h4["HTML"] = "

%s

"; h4["XHTML"] = "

%s

"; hash_markup.insert ("align_center", h4); QHash h5; h5["LaTeX"] = "\\begin{flushleft}%s\\end{flushleft}"; h5["HTML"] = "

%s

"; h5["XHTML"] = "

%s

"; hash_markup.insert ("align_left", h5); QHash h6; h6["LaTeX"] = "\\begin{flushright}%s\\end{flushright}"; h6["HTML"] = "

%s

"; h6["XHTML"] = "

%s

"; hash_markup.insert ("align_right", h6); QHash h7; h7["Docbook"] = "%s"; h7["LaTeX"] = "\\underline{%s}"; h7["HTML"] = "%s"; h7["XHTML"] = "%s"; h7["Lout"] = "@Underline{%s}"; h7["MediaWiki"] = "%s"; h7["DokuWiki"] = "__%s__"; hash_markup.insert ("underline", h7); QHash h8; h8["Docbook"] = "%s"; h8["HTML"] = "

%s

"; h8["XHTML"] = "

%s

"; h8["Lout"] = "@PP%s"; hash_markup.insert ("para", h8); QHash h9; h9["Docbook"] = "%s"; h9["HTML"] = "%s"; h9["XHTML"] = "%s"; h9["LaTeX"] = "\\href{}{%s}"; h9["Markdown"] = "[%s]()"; hash_markup.insert ("link", h9); QHash h10; h10["LaTeX"] = "\\newline"; h10["HTML"] = "
"; h10["XHTML"] = "
"; h10["Lout"] = "@LLP"; h10["MediaWiki"] = "
"; h10["DokuWiki"] = "\\\\ "; h10["Markdown"] = " \n"; hash_markup.insert ("newline", h10); } void CTEA::update_stylesheet (const QString &f) { //Update paletted stuff int darker_val = settings->value ("darker_val", 100).toInt(); QFontInfo fi = QFontInfo (qApp->font()); QString fontsize = "font-size:" + settings->value ("app_font_size", fi.pointSize()).toString() + "pt;"; QString fontfamily = "font-family:" + settings->value ("app_font_name", qApp->font().family()).toString() + ";"; QString edfontsize = "font-size:" + settings->value ("editor_font_size", "16").toString() + "pt;"; QString edfontfamily = "font-family:" + settings->value ("editor_font_name", "Serif").toString() + ";"; QString logmemo_fontsize = "font-size:" + settings->value ("logmemo_font_size", "12").toString() + "pt;"; QString logmemo_font = "font-family:" + settings->value ("logmemo_font", "Monospace").toString() + ";"; QString stylesheet; stylesheet = "QWidget, QWidget * {" + fontfamily + fontsize + "}\n"; stylesheet += "QPlainTextEdit, QPlainTextEdit * {" + edfontfamily + edfontsize + "}\n"; stylesheet += "QTextEdit {" + edfontfamily + edfontsize + "}\n"; stylesheet += "CLogMemo {" + logmemo_font + logmemo_fontsize + "}\n"; stylesheet += "CLineNumberArea {" + edfontfamily + edfontsize + "}\n"; QString text_color = hash_get_val (global_palette, "text", "black"); QString t_text_color = QColor (text_color).darker(darker_val).name(); QString back_color = hash_get_val (global_palette, "background", "white"); QString t_back_color = QColor (back_color).darker(darker_val).name(); QString sel_back_color = hash_get_val (global_palette, "sel-background", "black"); QString sel_text_color = hash_get_val (global_palette, "sel-text", "white"); QString t_sel_text_color = QColor (sel_text_color).darker(darker_val).name(); QString t_sel_back_color = QColor (sel_back_color).darker(darker_val).name(); QString css_plain_text_edit = QString ("QPlainTextEdit {color: %1; background-color: %2; selection-color: %3; selection-background-color: %4;}\n").arg ( t_text_color).arg ( t_back_color).arg ( t_sel_text_color).arg ( t_sel_back_color); stylesheet += css_plain_text_edit; QString css_tea_edit = QString ("CTEAEdit {color: %1; background-color: %2; selection-color: %3; selection-background-color: %4;}\n").arg ( t_text_color).arg ( t_back_color).arg ( t_sel_text_color).arg ( t_sel_back_color); stylesheet += css_tea_edit; QString css_tea_man = QString ("QTextBrowser {color: %1; background-color: %2; selection-color: %3; selection-background-color: %4;}\n").arg ( t_text_color).arg ( t_back_color).arg ( t_sel_text_color).arg ( t_sel_back_color); stylesheet += css_tea_man; QString css_fif = QString ("QComboBox#FIF { color: %1; background-color: %2; selection-color: %3; selection-background-color: %4;}\n").arg ( t_text_color).arg ( t_back_color).arg ( t_sel_text_color).arg ( t_sel_back_color); stylesheet += css_fif; //Update themed stuff QString cssfile = qstring_load (f); QString css_path = get_file_path (f) + "/"; cssfile = cssfile.replace ("./", css_path); cssfile += stylesheet; qApp->setStyleSheet (""); qApp->setStyleSheet (cssfile); } void CTEA::update_styles() { #if QT_VERSION >= 0x050000 QString default_style = qApp->style()->objectName(); if (default_style == "GTK+") //can be buggy default_style = "Fusion"; #else QString default_style = qApp->style()->objectName(); if (default_style == "GTK+") //can be buggy default_style = "Cleanlooks"; #endif fname_stylesheet = settings->value ("fname_stylesheet", ":/themes/TEA").toString(); MyProxyStyle *ps = new MyProxyStyle (QStyleFactory::create (settings->value ("ui_style", default_style).toString())); QApplication::setStyle (ps); } void CTEA::update_dyn_menus() { update_templates(); update_snippets(); update_keyboards(); update_scripts(); update_palettes(); update_themes(); update_tables(); update_profiles(); update_labels_menu(); } void CTEA::update_fonts() { documents->apply_settings(); } void CTEA::update_bookmarks() { if (! file_exists (fname_bookmarks)) return; QString bookmarks = qstring_load (fname_bookmarks); if (bookmarks.isEmpty()) return; menu_file_bookmarks->clear(); create_menu_from_list (this, menu_file_bookmarks, bookmarks.split ("\n"), SLOT (file_open_bookmark())); } void CTEA::update_templates() { menu_file_templates->clear(); create_menu_from_dir (this, menu_file_templates, dir_templates, SLOT (file_use_template()) ); } void CTEA::update_snippets() { menu_fn_snippets->clear(); create_menu_from_dir (this, menu_fn_snippets, dir_snippets, SLOT (fn_use_snippet()) ); } void CTEA::update_keyboards() { // qDebug() << "update_keyboards()"; // qDebug() << dir_keyboards; menu_view_keyboards->clear(); create_menu_from_dir (this, menu_view_keyboards, dir_keyboards, SLOT (view_use_keyboard()) ); } void CTEA::update_sessions() { menu_file_sessions->clear(); create_menu_from_dir (this, menu_file_sessions, dir_sessions, SLOT (file_open_session()) ); } void CTEA::update_palettes() { menu_view_palettes->clear(); QStringList l1 = read_dir_entries (dir_palettes); QStringList l2 = read_dir_entries (":/palettes"); l1 += l2; create_menu_from_list (this, menu_view_palettes, l1, SLOT (view_use_palette())); } void CTEA::update_labels_menu() { menu_labels->clear(); CDocument *d = documents->get_current(); if (d) create_menu_from_list (this, menu_labels, d->labels, SLOT(select_label())); } void CTEA::update_themes() { menu_view_themes->clear(); create_menu_from_themes (this, menu_view_themes, ":/themes", SLOT (view_use_theme()) ); create_menu_from_themes (this, menu_view_themes, dir_themes, SLOT (view_use_theme()) ); } void CTEA::update_hls() { CFilesList lf; lf.get (":/hls"); QStringList l (lf.list); lf.get (dir_hls); l << lf.list; for (int i = 0; i < l.size(); i++) { QString fname = l[i]; QString buffer = qstring_load_first_line (fname); QString rgxp = string_between (buffer, "pattern=\"", "\""); if (! rgxp.isEmpty()) { #if QT_VERSION >= 0x050000 QRegularExpression re (rgxp, QRegularExpression::CaseInsensitiveOption); if (re.isValid()) documents->hl_files.push_back (std::make_pair (re, fname)); #else QRegExp re (rgxp, Qt::CaseInsensitive, QRegExp::RegExp2); if (re.isValid()) documents->hl_files.push_back(std::make_pair (re, fname)); #endif } } } void CTEA::update_tables() { menu_fn_tables->clear(); create_menu_from_dir (this, menu_fn_tables, dir_tables, SLOT (fn_use_table()) ); } void CTEA::update_scripts() { menu_fn_scripts->clear(); create_menu_from_dir (this, menu_fn_scripts, dir_scripts, SLOT (fn_run_script()) ); } //KDE: $HOME/.local/share/user-places.xbel GNOME $HOME/.config/gtk-3.0/bookmarks void CTEA::update_places_bookmarks() { lv_places->clear(); QStringList sl_items; sl_items << tr ("templates"); sl_items << tr ("snippets"); sl_items << tr ("scripts"); sl_items << tr ("tables"); sl_items << tr ("configs"); lv_places->addItems (sl_items); if (! file_exists (fname_places_bookmarks)) return; sl_places_bmx = qstring_load (fname_places_bookmarks).split ("\n"); sl_places_bmx.removeAll (QString ("")); if (sl_places_bmx.size() != 0) lv_places->addItems (sl_places_bmx); QString fname_gtk_bookmarks = QDir::homePath() + "/" + ".gtk-bookmarks"; if (! file_exists (fname_gtk_bookmarks)) return; lv_places->addItem (tr ("GTK Bookmarks:")); QStringList sl_gtk_bookmarks = qstring_load (fname_gtk_bookmarks).split ("\n"); QStringList sl_filtered; int sz = sl_gtk_bookmarks.size(); for (int i = 0; i < sz; i++) { QString s = sl_gtk_bookmarks.at(i); //int pos = s.lastIndexOf ('/'); //if (pos == -1) // continue; //QString t = s.right (s.size() - ++pos); s = s.remove ("file://"); if (s.contains ("%")) s = QUrl::fromPercentEncoding (s.toLatin1().constData()); sl_filtered.append (s); } if (sl_filtered.size() > 0) lv_places->addItems (sl_filtered); } void CTEA::update_programs() { if (! file_exists (fname_programs)) return; programs = hash_load_keyval (fname_programs); if (programs.count() < 0) return; menu_programs->clear(); QStringList sl = programs.keys(); sl.sort(); create_menu_from_list (this, menu_programs, sl, SLOT (run_program())); } void CTEA::update_logmemo_palette() { int darker_val = settings->value ("darker_val", 100).toInt(); QString text_color = hash_get_val (global_palette, "text", "black"); QString back_color = hash_get_val (global_palette, "background", "white"); QString sel_back_color = hash_get_val (global_palette, "sel-background", "black"); QString sel_text_color = hash_get_val (global_palette, "sel-text", "white"); QString t_text_color = QColor (text_color).darker(darker_val).name(); QString t_back_color = QColor (back_color).darker(darker_val).name(); QString t_sel_text_color = QColor (sel_text_color).darker(darker_val).name(); QString t_sel_back_color = QColor (sel_back_color).darker(darker_val).name(); QString sheet = QString ("QPlainTextEdit { color: %1; background-color: %2; selection-color: %3; selection-background-color: %4;}").arg ( t_text_color).arg ( t_back_color).arg ( t_sel_text_color).arg ( t_sel_back_color); log->setStyleSheet (sheet); sheet = QString ("QLineEdit { color: %1; background-color: %2; selection-color: %3; selection-background-color: %4;}").arg ( t_text_color).arg ( t_back_color).arg ( t_sel_text_color).arg ( t_sel_back_color); fif->setStyleSheet (sheet); } void CTEA::update_charsets() { // QString fname = dir_config + "/last_used_charsets"; // if (! file_exists (fname)) // qstring_save (fname, "UTF-8"); // sl_last_used_charsets = qstring_load (fname).split ("\n"); // QList acodecs = QTextCodec::availableCodecs(); //for (QList ::iterator codec = acodecs.begin(); codec != acodecs.end(); ++codec) // sl_charsets.prepend ((*codec)); sl_charsets = CTextConverter::get_charsets(); // sl_charsets.sort(); } void CTEA::update_profiles() { menu_view_profiles->clear(); create_menu_from_dir (this, menu_view_profiles, dir_profiles, SLOT (view_use_profile()) ); } void CTEA::opt_update_keyb() { if (! lv_menuitems) return; lv_menuitems->clear(); shortcuts->captions_iterate(); lv_menuitems->addItems (shortcuts->captions); } /* ===================== Misc callbacks ===================== */ void CTEA::select_label() { last_action = sender(); QAction *a = qobject_cast(sender()); CDocument *d = documents->get_current(); if (! d) return; QTextCursor cr; QString text_to_find = settings->value ("label_start", "[?").toString() + a->data().toString() + settings->value ("label_end", "?]").toString(); cr = d->document()->find (text_to_find); if (! cr.isNull()) d->setTextCursor (cr); } void CTEA::run_program() { last_action = sender(); CDocument *d = documents->get_current(); if (! d) return; QAction *a = qobject_cast(sender()); QString command = programs.value (a->data().toString()); if (command.isEmpty()) return; if (main_tab_widget->currentIndex() == idx_tab_edit) { if (! file_exists (d->file_name)) { QMessageBox::critical (this, "!", tr ("Save the file first!"), QMessageBox::Ok, QMessageBox::Ok); return; } QFileInfo fi (d->file_name); command = command.replace ("%s", d->file_name); command = command.replace ("%basename", fi.baseName()); command = command.replace ("%filename", fi.fileName()); command = command.replace ("%ext", fi.suffix()); command = command.replace ("%dir", fi.canonicalPath()); QString fname = d->get_filename_at_cursor(); if (! fname.isEmpty()) command = command.replace ("%i", fname); } else if (main_tab_widget->currentIndex() == idx_tab_fman) command = command.replace ("%s", fman->get_sel_fname()); QProcess *process = new QProcess (this); connect (process, SIGNAL(readyReadStandardOutput()), this, SLOT(process_readyReadStandardOutput())); process->setProcessChannelMode (QProcess::MergedChannels) ; #if QT_VERSION >= 0x060000 process->startCommand (command); #else process->start (command); #endif // process->start (command, QStringList()); //system (command.toUtf8().data()); } /* void CTEA::guess_enc() { QString enc; QString fn = fman->get_sel_fname(); if (! file_exists (fn)) return; if (settings->value ("use_enca_for_charset_detection", 0).toBool()) { enc = guess_enc_for_file (fn); if (enc == "err") { log->log (tr ("Enca is not installed, falling back to the built-in detection")); CCharsetMagic cm; enc = cm.guess_for_file (fn); } } else { CCharsetMagic cm; enc = cm.guess_for_file (fn); } if (! enc.isEmpty()) cb_fman_codecs->setCurrentIndex (cb_fman_codecs->findText (enc, Qt::MatchFixedString)); } */ void CTEA::clipboard_dataChanged() { if (! capture_to_storage_file) return; CDocument *ddest = documents->get_document_by_fname (fname_storage_file); if (ddest) { QString t = QApplication::clipboard()->text(); QString tpl = "%s\n"; QString ftemplate = dir_config + "/cliptpl.txt"; if (file_exists (ftemplate)) tpl = qstring_load (ftemplate); tpl = tpl.replace ("%time", QTime::currentTime().toString (settings->value("time_format", "hh:mm:ss").toString())); tpl = tpl.replace ("%date", QDate::currentDate().toString (settings->value("date_format", "dd/MM/yyyy").toString())); QString text_to_insert = tpl.replace ("%s", t); ddest->put (text_to_insert); } } void CTEA::main_tab_page_changed (int index) { if (idx_prev == idx_tab_fman) if (img_viewer && img_viewer->window_mini.isVisible()) img_viewer->window_mini.close(); if (idx_prev == idx_tab_tune) leaving_options(); idx_prev = index; if (index == idx_tab_fman) { fman->setFocus(); fm_entry_mode = FM_ENTRY_MODE_NONE; idx_tab_fman_activate(); } if (index == idx_tab_calendar) { calendar_update(); idx_tab_calendar_activate(); } if (index == idx_tab_edit) idx_tab_edit_activate(); if (index == idx_tab_tune) idx_tab_tune_activate(); if (index == idx_tab_learn) idx_tab_learn_activate(); } void CTEA::calendar_clicked (const QDate &date) { QString fname = dir_days + "/" + date.toString ("yyyy-MM-dd"); if (file_exists (fname)) { QString s = qstring_load (fname); log->log (s); } } void CTEA::calendar_activated (const QDate &date) { QString fname = dir_days + "/" + date.toString ("yyyy-MM-dd"); bool fresh = false; if (settings->value ("cal_run_1st", true).toBool()) { if (! file_exists (fname)) qstring_save (fname, tr ("Enter your daily notes here.\nTo use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.:\n[06:00]good morning!\n[20:10]go to theatre")); settings->setValue ("cal_run_1st", false); fresh = true; } else if (! file_exists (fname)) { qstring_save (fname, tr ("Enter your daily notes here.")); fresh = true; } CDocument *d = documents->open_file_without_reload (fname, "UTF-8"); if (! d) return; // if (fresh) // d->selectAll(); main_tab_widget->setCurrentIndex (idx_tab_edit); } void CTEA::calendar_currentPageChanged (int year, int month) { calendar_update(); } void CTEA::process_readyReadStandardOutput() { QProcess *p = qobject_cast(sender()); QByteArray a = p->readAllStandardOutput()/*.data()*/; // QTextCodec *c = QTextCodec::codecForLocale(); // QString t = c->toUnicode (a); QString t = QString::fromUtf8 (a.data()); log->terminal_output = true; log->log (t); log->terminal_output = false; } void CTEA::virt_keyb_clicked() { CDocument *d = documents->get_current(); if (! d) return; QPushButton* bsender = qobject_cast(sender()); QString t = bsender->text(); d->insertPlainText (t); } /* =========================== Application misc. methods =========================== */ QHash CTEA::load_eclipse_theme_xml (const QString &fname) { QHash result; QString temp = qstring_load (fname); QXmlStreamReader xml (temp); while (! xml.atEnd()) { xml.readNext(); QString tag_name = xml.name().toString(); if (xml.isStartElement()) { if (tag_name == "colorTheme") { log->log (xml.attributes().value ("id").toString()); log->log (xml.attributes().value ("name").toString()); log->log (xml.attributes().value ("modified").toString()); log->log (xml.attributes().value ("author").toString()); log->log (xml.attributes().value ("website").toString()); } if (tag_name == "singleLineComment") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("single comment", t); } if (tag_name == "class") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) { result.insert ("class", t); result.insert ("type", t); } } if (tag_name == "operator") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("operator", t); } if (tag_name == "string") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("quotes", t); } if (tag_name == "multiLineComment") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("mcomment-start", t); } if (tag_name == "foreground") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) { result.insert ("text", t); result.insert ("functions", t); result.insert ("modifiers", t); result.insert ("margin_color", t); result.insert ("digits", t); result.insert ("digits-float", t); result.insert ("label", t); result.insert ("include", t); result.insert ("preproc", t); } } if (tag_name == "background") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) { result.insert ("background", t); result.insert ("linenums_bg", t); } } if (tag_name == "selectionForeground") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("sel-text", t); } if (tag_name == "selectionBackground") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("sel-background", t); } if (tag_name == "keyword") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) { result.insert ("keywords", t); result.insert ("tags", t); } } if (tag_name == "currentLine") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("cur_line_color", t); } if (tag_name == "bracket") { QString t = xml.attributes().value ("color").toString(); if (! t.isEmpty()) result.insert ("brackets", t); } }//is start if (xml.hasError()) qDebug() << "xml parse error"; } //cycle result.insert ("error", "red"); return result; } void CTEA::load_palette (const QString &fileName) { if (! file_exists (fileName)) return; global_palette.clear(); if (file_get_ext (fileName) == "xml") global_palette = load_eclipse_theme_xml (fileName); else global_palette = hash_load_keyval (fileName); } void CTEA::fman_convert_images (bool by_side, int value) { srand (QTime::currentTime().msec()); QString dir_out ("images-out-"); dir_out.append (QString::number (rand() % 777)); dir_out.prepend ("/"); dir_out.prepend (fman->dir.absolutePath()); if (! fman->dir.mkpath (dir_out)) return; Qt::TransformationMode transformMode = Qt::FastTransformation; if (settings->value ("img_filter", 0).toBool()) transformMode = Qt::SmoothTransformation; pb_status->show(); pb_status->setFormat (tr ("%p% completed")); pb_status->setTextVisible (true); QStringList li = fman->get_sel_fnames(); int quality = settings->value ("img_quality", "-1").toInt(); pb_status->setRange (0, li.size() - 1 ); int i = 0; for (QList ::iterator fname = li.begin(); fname != li.end(); ++fname) { if (! is_image ((*fname))) continue; QImage source ((*fname)); if (source.isNull()) continue; qApp->processEvents(); if (settings->value ("cb_exif_rotate", 1).toBool()) { int exif_orientation = get_exif_orientation ((*fname)); QTransform transform; qreal angle = 0; if (exif_orientation == 3) angle = 180; else if (exif_orientation == 6) angle = 90; else if (exif_orientation == 8) angle = 270; if (angle != 0) { transform.rotate (angle); source = source.transformed (transform); } } QImage dest = image_scale_by (source, by_side, value, transformMode); QString fmt (settings->value ("output_image_fmt", "jpg").toString()); QFileInfo fi ((*fname)); QString dest_fname (dir_out); dest_fname.append ("/"); dest_fname.append (fi.fileName()); dest_fname = change_file_ext (dest_fname, fmt); if (! dest.save (dest_fname, fmt.toLatin1().constData(), quality)) qDebug() << "Cannot save " << dest_fname; pb_status->setValue (i++); } pb_status->hide(); if (settings->value ("img_post_proc", 0).toBool()) { /*CZipper zipper; zipper.zip_directory (fman->dir.absolutePath(), dir_out);*/ } fman->refresh(); } QTextDocument::FindFlags CTEA::get_search_options() { QTextDocument::FindFlags flags; //= 0; if (menu_find_whole_words->isChecked()) flags = flags | QTextDocument::FindWholeWords; if (menu_find_case->isChecked()) flags = flags | QTextDocument::FindCaseSensitively; return flags; } QString CTEA::fif_get_text() { QString t = fif->text(); int i = sl_fif_history.indexOf (t); if (i != -1) { sl_fif_history.removeAt (i); sl_fif_history.prepend (t); } else sl_fif_history.prepend (t); if (sl_fif_history.count() > 77) sl_fif_history.removeLast(); return t; } QWidget* CTEA::create_keyboard (const QString &fname) { if (! file_exists (fname)) return 0; QStringList l = qstring_load (fname).split ("\n"); if (l.size() == 0) return 0; QWidget *w = new QWidget; QFileInfo fi (fname); w->setWindowTitle (fi.fileName()); Qt::WindowFlags flags; flags = Qt::Window | Qt::Tool | Qt::WindowStaysOnTopHint; w->setWindowFlags (flags); QVBoxLayout *lv = new QVBoxLayout; w->setLayout (lv); for (int y = 0; y< l.size(); y++) { QStringList row = l[y].split ("|"); QHBoxLayout *lh = new QHBoxLayout; lv->addLayout (lh); for (int x = 0; x < row.size(); x++) { QPushButton *b = new QPushButton; connect (b, SIGNAL(clicked()), this, SLOT(virt_keyb_clicked())); b->setText (row[x]); lh->addWidget (b); } } return w; } QAction* CTEA::add_to_menu (QMenu *menu, const QString &caption, const char *method, const QString &shortkt, const QString &iconpath ) { QAction *act = new QAction (caption, this); if (! shortkt.isEmpty()) act->setShortcut (shortkt); if (! iconpath.isEmpty()) act->setIcon (QIcon (iconpath)); connect (act, SIGNAL(triggered()), this, method); menu->addAction (act); return act; } QIcon CTEA::get_theme_icon (const QString &name) { QString fname = theme_dir + "icons/" + name; if (file_exists (fname)) return QIcon (fname); return QIcon (":/icons/" + name); } QString CTEA::get_theme_icon_fname (const QString &name) { QString fname = theme_dir + "icons/" + name; if (file_exists (fname)) return fname; return ":/icons/" + name; } void CTEA::leaving_options() { settings->setValue ("date_format", ed_date_format->text()); settings->setValue ("time_format", ed_time_format->text()); settings->setValue ("img_viewer_8156override_command", ed_img_viewer_override->text()); //settings->setValue ("wasd", cb_wasd->isChecked()); settings->setValue ("ui_mode", cmb_ui_mode->currentIndex()); #if defined(JOYSTICK_SUPPORTED) settings->setValue ("use_joystick", cb_use_joystick->isChecked()); #endif settings->setValue ("full_path_at_window_title", cb_full_path_at_window_title->isChecked()); settings->setValue ("word_wrap", cb_wordwrap->isChecked()); #if QT_VERSION >= 0x050000 // settings->setValue ("qregexpsyntaxhl", cb_use_qregexpsyntaxhl->isChecked()); #endif #ifdef SPEECH_ENABLE settings->setValue ("speech_enabled", cb_speech_enable->isChecked()); settings->setValue ("locale_only", cb_locale_only->isChecked()); if (cb_locale_only->isChecked()) speech.locale_only = 1; else speech.locale_only = 0; //РАЗОБРАТЬСЯ В ПЕРЕЗАГРУЗКОЙ ГОЛОСОВ НА УРОВНЕ НАЖАТИЯ НА ЧЕКБОКСЫ speech.current_voice_index = cmb_cpeech_voices->currentIndex(); settings->setValue ("current_voice_index", speech.current_voice_index); /* if (cb_speech_enable->isChecked()) { speech.init ("tea"); speech.locale_only = settings->value ("locale_only", 1).toInt(); } else { speech.done(); //cmb_cpeech_voices->items.clear(); } */ #endif settings->setValue ("additional_hl", cb_hl_current_line->isChecked()); settings->setValue ("session_restore", cb_session_restore->isChecked()); settings->setValue ("show_linenums", cb_show_linenums->isChecked()); settings->setValue ("hl_enabled", cb_hl_enabled->isChecked()); settings->setValue ("hl_brackets", cb_hl_brackets->isChecked()); settings->setValue ("auto_indent", cb_auto_indent->isChecked()); settings->setValue ("spaces_instead_of_tabs", cb_spaces_instead_of_tabs->isChecked()); settings->setValue ("show_tabs_and_spaces", cb_show_tabs_and_spaces->isChecked()); settings->setValue ("cursor_xy_visible", cb_cursor_xy_visible->isChecked()); settings->setValue ("tab_sp_width", spb_tab_sp_width->value()); settings->setValue ("center_on_scroll", cb_center_on_cursor->isChecked()); settings->setValue ("show_margin", cb_show_margin->isChecked()); settings->setValue ("margin_pos", spb_margin_pos->value()); settings->setValue ("b_preview", cb_auto_img_preview->isChecked()); settings->setValue ("cursor_blink_time", spb_cursor_blink_time->value()); settings->setValue ("autosave_period", spb_autosave_period->value()); settings->setValue ("autosave", cb_autosave->isChecked()); documents->timer_autosave.stop(); documents->timer_autosave.setInterval (spb_autosave_period->value() * 1000); documents->timer_autosave.start(); MyProxyStyle::cursor_blink_time = spb_cursor_blink_time->value(); qApp->setCursorFlashTime (spb_cursor_blink_time->value()); settings->setValue ("cursor_width", spb_cursor_width->value()); settings->setValue ("override_img_viewer", cb_override_img_viewer->isChecked()); //settings->setValue ("use_enca_for_charset_detection", cb_use_enca_for_charset_detection->isChecked()); settings->setValue ("use_trad_dialogs", cb_use_trad_dialogs->isChecked()); settings->setValue ("start_week_on_sunday", cb_start_on_sunday->isChecked()); settings->setValue ("northern_hemisphere", cb_northern_hemisphere->isChecked()); calendar->northern_hemisphere = bool (cb_northern_hemisphere->isChecked()); /* int i = moon_phase_algos.key (cmb_moon_phase_algos->currentText()); settings->setValue ("moon_phase_algo", i); calendar->moon_phase_algo = i; */ int i = cmb_moon_phase_algos->currentText().toInt(); settings->setValue ("moon_phase_algo", i); calendar->moon_phase_algo = i; settings->setValue ("lng", cmb_lng->currentText()); // settings->setValue ("zip_charset_in", cmb_zip_charset_in->currentText()); // settings->setValue ("zip_charset_out", cmb_zip_charset_out->currentText()); settings->setValue ("cmdline_default_charset", cmb_cmdline_default_charset->currentText()); settings->setValue ("label_end", ed_label_end->text()); settings->setValue ("label_start", ed_label_start->text()); settings->setValue ("output_image_fmt", cmb_output_image_fmt->currentText()); settings->setValue ("img_filter", cb_output_image_flt->isChecked()); settings->setValue ("fuzzy_q", spb_fuzzy_q->value()); settings->setValue ("show_ebooks_fine", cb_show_ebooks_fine->isChecked()); settings->setValue ("img_quality", spb_img_quality->value()); //settings->setValue ("img_post_proc", cb_zip_after_scale->isChecked()); settings->setValue ("cb_exif_rotate", cb_exif_rotate->isChecked()); settings->setValue ("zor_use_exif_orientation", cb_zor_use_exif->isChecked()); settings->setValue ("ed_side_size", ed_side_size->text()); settings->setValue ("ed_link_options", ed_link_options->text()); settings->setValue ("ed_cols_per_row", ed_cols_per_row->text()); b_preview = settings->value ("b_preview", false).toBool(); calendar->do_update(); documents->apply_settings(); } /* void CTEA::add_to_last_used_charsets (const QString &s) { int i = sl_last_used_charsets.indexOf (s); if (i == -1) sl_last_used_charsets.prepend (s); else sl_last_used_charsets.move (i, 0); if (sl_last_used_charsets.size() > 3) sl_last_used_charsets.removeLast(); } */ void CTEA::count_substring (bool use_regexp) { CDocument *d = documents->get_current(); if (! d) return; QString text; if (d->textCursor().hasSelection()) text = d->get(); else text = d->toPlainText(); int count = 0; Qt::CaseSensitivity cs = Qt::CaseInsensitive; if (menu_find_case->isChecked()) cs = Qt::CaseSensitive; #if (QT_VERSION_MAJOR < 5) if (use_regexp) count = text.count (QRegExp (fif_get_text())); else count = text.count (fif_get_text(), cs); #else if (use_regexp) count = text.count (QRegularExpression (fif_get_text())); else count = text.count (fif_get_text(), cs); #endif log->log (tr ("%1 number of occurrences of %2 is found").arg (count).arg (fif->text())); } void CTEA::run_unitaz (int mode) { CDocument *d = documents->get_current(); if (! d) return; //pb_status->show(); progress_show(); pb_status->setFormat (tr ("%p% completed")); pb_status->setTextVisible (true); QElapsedTimer time_start; time_start.start(); int c = 0; QStringList total = d->get_words(); QHash h; pb_status->setRange (0, total.size() - 1); for (int j = 0; j < total.size(); j++) { if (c % 100 == 0) qApp->processEvents(); if (boring) break; QHash::iterator i = h.find (total.at(j).toLower()); if (i != h.end()) i.value() += 1; else h.insert(total.at(j).toLower(), 1); pb_status->setValue (c++); } vector > uwords; QList keys = h.keys(); int sz = keys.size(); uwords.reserve (sz); for (int i = 0; i < sz; i++) uwords.push_back (make_pair(keys.at(i),h.value (keys.at(i)))); if (mode == 0) std::sort (uwords.begin(), uwords.end(), pr_bigger_than); if (mode == 1) std::sort (uwords.begin(), uwords.end(), pr_bigger_than_str); if (mode == 2) std::sort (uwords.begin(), uwords.end(), pr_bigger_than_str_len); QStringList outp; for (size_t i = 0; i < uwords.size(); i++) outp.append (uwords.at(i).first + " = " + QString::number (uwords.at(i).second)); double diff = static_cast (total.size()) / static_cast (uwords.size()); double diff_per_cent = get_percent (static_cast (total.size()), static_cast (uwords.size())); outp.prepend (tr ("total to unique per cent diff: %1").arg (diff_per_cent, 0, 'f', 6)); outp.prepend (tr ("total / unique: %1").arg (diff, 0, 'f', 6)); outp.prepend (tr ("words unique: %1").arg (uwords.size())); outp.prepend (tr ("words total: %1").arg (total.size())); outp.prepend (tr ("text analysis of: %1").arg (d->file_name)); outp.prepend (tr ("UNITAZ: UNIverlsal Text AnalyZer")); log->log (tr("elapsed milliseconds: %1").arg (time_start.elapsed())); CDocument *nd = documents->create_new(); nd->put (outp.join ("\n")); //pb_status->hide(); progress_hide(); } void CTEA::markup_text (const QString &mode) { CDocument *d = documents->get_current(); if (! d) return; QString t = hash_markup[mode][d->markup_mode]; if (! t.isEmpty()) d->put (t.replace ("%s", d->get())); } void CTEA::fman_items_select_by_regexp (bool mode) { QString ft = fif_get_text(); if (ft.isEmpty()) return; #if QT_VERSION >= 0x050F00 l_fman_find = fman->mymodel->findItems (ft, Qt::MatchRegularExpression); #else l_fman_find = fman->mymodel->findItems (ft, Qt::MatchRegExp); #endif if (l_fman_find.size() < 1) return; QItemSelectionModel *m = fman->selectionModel(); for (int i = 0; i < l_fman_find.size(); i++) if (mode) m->select (fman->mymodel->indexFromItem (l_fman_find[i]), QItemSelectionModel::Select | QItemSelectionModel::Rows); else m->select (fman->mymodel->indexFromItem (l_fman_find[i]), QItemSelectionModel::Deselect | QItemSelectionModel::Rows); } void CTEA::fn_filter_delete_by_sep (bool mode) { CDocument *d = documents->get_current(); if (! d) return; QStringList sl = d->get().split (QChar::ParagraphSeparator); QString t = fif_get_text(); for (int i = 0; i < sl.size(); i++) { int n = sl[i].indexOf (t); if (n != -1) { QString s = sl[i]; if (mode) s = s.right (s.size() - n); else s = s.left (n); sl[i] = s; } } QString x = sl.join ("\n"); d->put (x); } void CTEA::fman_find() { QString ft = fif_get_text(); if (ft.isEmpty()) return; l_fman_find = fman->mymodel->findItems (ft, Qt::MatchStartsWith); if (l_fman_find.size() < 1) return; fman_find_idx = 0; fman->setCurrentIndex (fman->mymodel->indexFromItem (l_fman_find[fman_find_idx])); } void CTEA::fman_find_next() { QString ft = fif_get_text(); if (ft.isEmpty()) return; if (l_fman_find.size() < 1) return; if (fman_find_idx < (l_fman_find.size() - 1)) fman_find_idx++; fman->setCurrentIndex (fman->mymodel->indexFromItem (l_fman_find[fman_find_idx])); } void CTEA::fman_find_prev() { QString ft = fif_get_text(); if (ft.isEmpty()) return; if (l_fman_find.size() < 1) return; if (fman_find_idx != 0) fman_find_idx--; fman->setCurrentIndex (fman->mymodel->indexFromItem (l_fman_find[fman_find_idx])); } void CTEA::opt_shortcuts_find() { int from = 0; QString fiftxt = fif_get_text(); if (opt_shortcuts_string_to_find == fiftxt) from = lv_menuitems->currentRow(); opt_shortcuts_string_to_find = fiftxt; if (from == -1) from = 0; #if QT_VERSION < 0x050000 int index = shortcuts->captions.indexOf (QRegExp (opt_shortcuts_string_to_find + ".*", Qt::CaseInsensitive), from); #else int index = shortcuts->captions.indexOf (QRegularExpression (opt_shortcuts_string_to_find + ".*", QRegularExpression::CaseInsensitiveOption), from); #endif if (index != -1) lv_menuitems->setCurrentRow (index); } void CTEA::opt_shortcuts_find_next() { int from = lv_menuitems->currentRow(); if (from == -1) from = 0; #if QT_VERSION < 0x050000 int index = shortcuts->captions.indexOf (QRegExp (opt_shortcuts_string_to_find + ".*", Qt::CaseInsensitive), from + 1); #else int index = shortcuts->captions.indexOf (QRegularExpression (opt_shortcuts_string_to_find + ".*", QRegularExpression::CaseInsensitiveOption), from + 1); #endif if (index != -1) lv_menuitems->setCurrentRow (index); } void CTEA::opt_shortcuts_find_prev() { int from = lv_menuitems->currentRow(); if (from == -1) from = 0; #if QT_VERSION < 0x050000 int index = shortcuts->captions.lastIndexOf (QRegExp (opt_shortcuts_string_to_find + ".*", Qt::CaseInsensitive), from - 1); #else int index = shortcuts->captions.lastIndexOf (QRegularExpression (opt_shortcuts_string_to_find + ".*", QRegularExpression::CaseInsensitiveOption), from - 1); #endif if (index != -1) lv_menuitems->setCurrentRow (index); } void CTEA::idx_tab_edit_activate() { menu_file->menuAction()->setVisible (true); menu_edit->menuAction()->setVisible (true); menu_programs->menuAction()->setVisible (true); menu_cal->menuAction()->setVisible (false); menu_markup->menuAction()->setVisible (true); menu_functions->menuAction()->setVisible (true); menu_search->menuAction()->setVisible (true); menu_nav->menuAction()->setVisible (true); menu_fm->menuAction()->setVisible (false); menu_view->menuAction()->setVisible (true); helpMenu->menuAction()->setVisible (true); } void CTEA::idx_tab_calendar_activate() { menu_file->menuAction()->setVisible (true); menu_edit->menuAction()->setVisible (false); menu_cal->menuAction()->setVisible (true); menu_programs->menuAction()->setVisible (true); menu_markup->menuAction()->setVisible (false); menu_functions->menuAction()->setVisible (false); menu_search->menuAction()->setVisible (false); menu_nav->menuAction()->setVisible (false); menu_fm->menuAction()->setVisible (false); menu_view->menuAction()->setVisible (true); helpMenu->menuAction()->setVisible (true); } void CTEA::idx_tab_tune_activate() { opt_update_keyb(); menu_file->menuAction()->setVisible (true); menu_edit->menuAction()->setVisible (false); menu_programs->menuAction()->setVisible (true); menu_markup->menuAction()->setVisible (false); menu_functions->menuAction()->setVisible (true); menu_search->menuAction()->setVisible (false); menu_nav->menuAction()->setVisible (false); menu_fm->menuAction()->setVisible (false); menu_view->menuAction()->setVisible (true); helpMenu->menuAction()->setVisible (true); menu_cal->menuAction()->setVisible (false); } void CTEA::idx_tab_fman_activate() { menu_file->menuAction()->setVisible (true); menu_edit->menuAction()->setVisible (false); menu_programs->menuAction()->setVisible (true); menu_markup->menuAction()->setVisible (false); menu_functions->menuAction()->setVisible (true); menu_search->menuAction()->setVisible (true); menu_nav->menuAction()->setVisible (false); menu_fm->menuAction()->setVisible (true); menu_view->menuAction()->setVisible (true); helpMenu->menuAction()->setVisible (true); menu_cal->menuAction()->setVisible (false); } void CTEA::idx_tab_learn_activate() { menu_file->menuAction()->setVisible (true); menu_edit->menuAction()->setVisible (false); menu_programs->menuAction()->setVisible (true); menu_markup->menuAction()->setVisible (false); menu_functions->menuAction()->setVisible (false); menu_search->menuAction()->setVisible (true); menu_nav->menuAction()->setVisible (false); menu_fm->menuAction()->setVisible (false); menu_view->menuAction()->setVisible (true); helpMenu->menuAction()->setVisible (true); menu_cal->menuAction()->setVisible (false); } void CTEA::man_find_find() { QString fiftxt = fif_get_text(); man->find (fiftxt, get_search_options()); man_search_value = fiftxt; } void CTEA::man_find_next() { man->find (man_search_value, get_search_options()); } void CTEA::man_find_prev() { man->find (man_search_value, get_search_options() | QTextDocument::FindBackward); } void CTEA::progress_show() { pb_status->show(); tb_stop->show(); boring = false; } void CTEA::progress_hide() { pb_status->hide(); tb_stop->hide(); boring = false; } /* ==================== Tune page callbacks ==================== */ #if defined(JOYSTICK_SUPPORTED) void CTEA::cb_use_joystick_stateChanged (int state) { bool b; if (state == Qt::Unchecked) b = false; else b = true; settings->setValue ("use_joystick", b); if (b) documents->timer_joystick.start (100); else documents->timer_joystick.stop(); } #endif /* void CTEA::cb_altmenu_stateChanged (int state) { if (state == Qt::Unchecked) MyProxyStyle::b_altmenu = false; else MyProxyStyle::b_altmenu = true; settings->setValue ("b_altmenu", MyProxyStyle::b_altmenu); } */ void CTEA::cmb_ui_tabs_currentIndexChanged (int i) { main_tab_widget->setTabPosition (int_to_tabpos (i)); settings->setValue ("ui_tabs_align", i); } void CTEA::cmb_docs_tabs_currentIndexChanged (int i) { tab_editor->setTabPosition (int_to_tabpos (i)); settings->setValue ("docs_tabs_align", i); } void CTEA::cmb_icon_sizes_currentIndexChanged (int index) { QComboBox *cmb = qobject_cast(sender()); QString text = cmb->currentText(); settings->setValue ("icon_size", text); icon_size = settings->value ("icon_size", "32").toInt(); setIconSize (QSize (text.toInt(), text.toInt())); tb_fman_dir->setIconSize (QSize (text.toInt(), text.toInt())); filesToolBar->setIconSize (QSize (text.toInt(), text.toInt())); } void CTEA::cmb_tea_icons_currentIndexChanged (int) { QComboBox *cmb = qobject_cast(sender()); QString text = cmb->currentText(); settings->setValue ("icon_fname", text); QString icon_fname = ":/icons/tea-icon-v3-0" + text + ".png"; qApp->setWindowIcon (QIcon (icon_fname)); } void CTEA::pb_assign_hotkey_clicked() { if (! lv_menuitems->currentItem()) return; if (ent_shtcut->text().isEmpty()) return; shortcuts->set_new_shortcut (lv_menuitems->currentItem()->text(), ent_shtcut->text()); shortcuts->save_to_file (shortcuts->fname); } void CTEA::pb_remove_hotkey_clicked() { if (! lv_menuitems->currentItem()) return; shortcuts->set_new_shortcut (lv_menuitems->currentItem()->text(), ""); ent_shtcut->setText (""); shortcuts->save_to_file (shortcuts->fname); } void CTEA::slot_lv_menuitems_currentItemChanged (QListWidgetItem *current, QListWidgetItem *previous) { if (! current) return; QAction *a = shortcuts->find_by_caption (current->text()); if (a) ent_shtcut->setText (a->shortcut().toString()); } void CTEA::slot_font_logmemo_select() { bool ok; QFont font = QFontDialog::getFont(&ok, QFont(settings->value ("logmemo_font", "Monospace").toString(), settings->value ("logmemo_font_size", "12").toInt()), this); if (! ok) return; settings->setValue ("logmemo_font", font.family()); settings->setValue ("logmemo_font_size", font.pointSize()); update_stylesheet (fname_stylesheet); } void CTEA::slot_font_interface_select() { bool ok; QFontInfo fi = QFontInfo (qApp->font()); QFont font = QFontDialog::getFont (&ok, QFont (settings->value ("app_font_name", "Sans").toString(), settings->value ("app_font_size", fi.pointSize()).toInt()), this); if (! ok) return; settings->setValue ("app_font_name", font.family()); settings->setValue ("app_font_size", font.pointSize()); update_stylesheet (fname_stylesheet); } void CTEA::slot_font_editor_select() { bool ok; QFont font = QFontDialog::getFont(&ok, QFont(settings->value ("editor_font_name", "Serif").toString(), settings->value ("editor_font_size", "16").toInt()), this); if (! ok) return; settings->setValue ("editor_font_name", font.family()); settings->setValue ("editor_font_size", font.pointSize()); update_stylesheet (fname_stylesheet); } void CTEA::slot_style_currentIndexChanged (int) { QComboBox *cmb = qobject_cast(sender()); QString text = cmb->currentText(); if (text == "GTK+") //because it is buggy with some Qt versions. sorry! return; QStyle *style = QStyleFactory::create (text); if (style == 0) return; settings->setValue ("ui_style", text); } #ifdef SPEECH_ENABLE void CTEA::cmb_cpeech_voices_currentIndexChanged (int i) { QComboBox *cmb = qobject_cast(sender()); if (! speech.initialized) return; if (speech.current_voice_index > speech.voices.size() - 1) return; speech.current_voice_index = i; speech.set_voice_by_index (speech.current_voice_index); } #endif #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) void CTEA::cmb_spellchecker_currentIndexChanged (int) { QComboBox *cmb = qobject_cast(sender()); QString text = cmb->currentText(); cur_spellchecker = text; settings->setValue ("cur_spellchecker", cur_spellchecker); delete spellchecker; if (! spellcheckers.contains (cur_spellchecker) && spellcheckers.size() > 0) cur_spellchecker = spellcheckers[0]; #ifdef ASPELL_ENABLE if (cur_spellchecker == "Aspell") spellchecker = new CAspellchecker (settings->value ("spell_lang", QLocale::system().name().left(2)).toString()); #endif #ifdef HUNSPELL_ENABLE if (cur_spellchecker == "Hunspell") spellchecker = new CHunspellChecker (settings->value ("spell_lang", QLocale::system().name().left(2)).toString(), hunspell_default_dict_path()); #endif #ifdef NUSPELL_ENABLE if (cur_spellchecker == "Nuspell") spellchecker = new CNuspellChecker (settings->value ("spell_lang", QLocale::system().name().left(2)).toString(), hunspell_default_dict_path()); #endif settings->setValue ("spell_lang", ""); create_spellcheck_menu(); } #endif #ifdef HUNSPELL_ENABLE void CTEA::pb_choose_hunspell_path_clicked() { QString path = QFileDialog::getExistingDirectory (this, tr ("Open Directory"), hunspell_default_dict_path(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (path.isEmpty()) return; settings->setValue ("hunspell_dic_path", path); ed_spellcheck_path->setText (path); if (spellchecker) delete spellchecker; setup_spellcheckers(); create_spellcheck_menu(); } #endif #ifdef ASPELL_ENABLE void CTEA::pb_choose_aspell_path_clicked() { QString path = QFileDialog::getExistingDirectory (this, tr ("Open Directory"), "/", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (path.isEmpty()) return; settings->setValue ("win32_aspell_path", path); ed_aspellcheck_path->setText (path); if (spellchecker) delete spellchecker; setup_spellcheckers(); create_spellcheck_menu(); } #endif tea-qt-63.3.0/src/tea.h000066400000000000000000000615371476733534200145410ustar00rootroot00000000000000/************************************************************************** * 2007-2024 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef TEA_H #define TEA_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PRINTER_ENABLE #include #endif #include "document.h" #include "fman.h" #include "calendar.h" #include "shortcuts.h" #include "img_viewer.h" #include "spellchecker.h" #ifdef SPEECH_ENABLE #include "speech.h" #endif class CDarkerWindow: public QWidget { Q_OBJECT QSlider *slider; public: CDarkerWindow(); protected: void closeEvent (QCloseEvent *event); public slots: void slot_valueChanged (int value); }; class MyProxyStyle: public QProxyStyle { public: static bool b_altmenu; static int cursor_blink_time; int styleHint (StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const { if (hint == QStyle::SH_ItemView_ActivateItemOnSingleClick) return 0; if (! b_altmenu && hint == QStyle::SH_MenuBar_AltKeyNavigation) return 0; return QProxyStyle::styleHint (hint, option, widget, returnData); } MyProxyStyle (QStyle *style = 0): QProxyStyle (style){}; }; #if QT_VERSION >= 0x050000 class QStyleHints { public: int cursorFlashTime() const { return MyProxyStyle::cursor_blink_time; } }; #endif class CAboutWindow: public QWidget { Q_OBJECT public: QLabel *logo; CAboutWindow(); protected: void closeEvent (QCloseEvent *event); public slots: void update_image(); }; class CTextListWnd: public QWidget { Q_OBJECT public: QListWidget *list; CTextListWnd (const QString &title, const QString &label_text); ~CTextListWnd(); protected: void closeEvent (QCloseEvent *event); }; class CTEA: public QMainWindow { Q_OBJECT public: /* ========================= Variables ========================= */ #ifdef SPEECH_ENABLE CSpeech speech; #endif QString charset; QStringList sl_places_bmx; QStringList sl_urls; QStringList sl_charsets; //QStringList sl_last_used_charsets; QStringList sl_fif_history; //QStringList sl_gtk_bookmarks; int fm_entry_mode; QDate date1; QDate date2; bool portable_mode; CLogMemo *log; QObject *last_action; CShortcuts *shortcuts; CFMan *fman; CImgViewer *img_viewer; QTextBrowser md_viewer; QWidget *wnd_about; CSpellchecker *spellchecker; QStringList spellcheckers; QString cur_spellchecker; QString theme_dir; int icon_size; bool b_preview; bool capture_to_storage_file; int idx_tab_calendar; int idx_tab_edit; int idx_tab_tune; int idx_tab_fman; int idx_tab_learn; int idx_tab_keyboard; #ifdef SPEECH_ENABLE int idx_tab_speech; #endif int idx_prev; int fman_find_idx; QList l_fman_find; bool ui_update; QString fname_storage_file; QString fname_stylesheet; QString man_search_value; QString markup_mode; QHash programs; QHash places_bookmarks; QTranslator transl_app; QTranslator transl_system; QDir dir_lv; #ifdef PRINTER_ENABLE QPrinter printer; #endif QString opt_shortcuts_string_to_find; QString fman_fname_to_find; QString dir_user_dict; QString dir_profiles; QString dir_config; QString dir_templates; QString dir_sessions; QString dir_hls; QString dir_keyboards; QString dir_themes; QString dir_snippets; QString dir_scripts; QString dir_palettes; QString dir_tables; QString dir_days; QString fname_def_palette; QString fname_fif; QString fname_bookmarks; QString fname_programs; QString fname_places_bookmarks; QString fname_crapbook; QString fname_saved_buffers; QString fname_tempfile; QString fname_autosaving_files; QString fname_tempparamfile; /* =========================== Main menu items =========================== */ QMenu *menu_file; QMenu *menu_file_actions; QMenu *menu_file_recent; QMenu *menu_file_bookmarks; QMenu *menu_file_edit_bookmarks; QMenu *menu_file_templates; QMenu *menu_file_sessions; QMenu *menu_file_configs; QMenu *menu_edit; QMenu *menu_ide; QMenu *menu_cal; QMenu *menu_cal_add; QMenu *menu_cal_sub; QMenu *menu_cal_diff; QMenu *menu_programs; QMenu *menu_fn_snippets; QMenu *menu_fn_tables; QMenu *menu_view_palettes; QMenu *menu_view_profiles; QMenu *menu_fn_sessions; QMenu *menu_fn_scripts; QMenu *menu_view_themes; QMenu *menu_view_keyboards; QMenu *menu_markup; QMenu *menu_functions; QMenu *menu_functions_case; QMenu *menu_search; QMenu *menu_nav; QMenu *menu_instr; QMenu *menu_fm; QMenu *menu_fm_file_ops; QMenu *menu_fm_multi_rename; QMenu *menu_fm_file_infos; QMenu *menu_fm_img_conv; // QMenu *menu_fm_zip; QMenu *menu_fm_checksums; QMenu *menu_view; QMenu *menu_spell_langs; QMenu *helpMenu; QMenu *menu_labels; QToolBar *fileToolBar; QToolBar *editToolBar; QToolBar *filesToolBar; QToolBar *statusToolBar; QToolBar *fifToolBar; QAction *act_labels; QAction *act_test; QAction *filesAct; QAction *newAct; QAction *openAct; QAction *saveAct; QAction *saveAsAct; QAction *exitAct; QAction *cutAct; QAction *copyAct; QAction *closeAct; QAction *undoAct; QAction *redoAct; QAction *pasteAct; QAction *aboutAct; QAction *aboutQtAct; QAction *menu_find_whole_words; QAction *menu_find_from_cursor; QAction *menu_find_case; QAction *menu_find_regexp; QAction *menu_find_fuzzy; /* ========================= Main window widgets ========================= */ QSplitter *mainSplitter; QTextBrowser *man; QTabWidget *main_tab_widget; QTabWidget *tab_options; QTabWidget *tab_browser; QTabWidget *tab_editor; QLineEdit *fif; QWidget *w_right; QLineEdit *ed_fman_fname; QComboBox *cb_fman_codecs; QComboBox *cb_fman_drives; CCalendarWidget *calendar; /* ============================== Main tab UI elements ============================== */ QComboBox *cmb_fif; QLabel *l_status; QProgressBar *pb_status; QToolButton *tb_stop; /* ============================== FileManager tab UI elements ============================== */ QLineEdit *ed_fman_path; QListWidget *lv_places; QSplitter *spl_fman; QToolBar *tb_fman_dir; QLabel *l_fman_preview; QLabel *l_charset; /* ============================================= Preferences tab :: Interface page UI elements ============================================= */ QComboBox *cmb_ui_mode; QComboBox *cmb_lng; QComboBox *cmb_styles; QCheckBox *cb_fif_at_toolbar; QComboBox *cmb_icon_size; QComboBox *cmb_tea_icons; QCheckBox *cb_show_linenums; QCheckBox *cb_wordwrap; QCheckBox *cb_show_tabs_and_spaces; QCheckBox *cb_hl_enabled; QCheckBox *cb_hl_current_line; QCheckBox *cb_hl_brackets; QCheckBox *cb_auto_indent; QCheckBox *cb_spaces_instead_of_tabs; QSpinBox *spb_tab_sp_width; QCheckBox *cb_cursor_xy_visible; QCheckBox *cb_center_on_cursor; QSpinBox *spb_cursor_blink_time; QSpinBox *spb_cursor_width; QCheckBox *cb_show_margin; QSpinBox *spb_margin_pos; QCheckBox *cb_use_hl_wrap; QCheckBox *cb_full_path_at_window_title; /* ============================================= Preferences tab :: Common page UI elements ============================================= */ QCheckBox *cb_start_on_sunday; QCheckBox *cb_northern_hemisphere; QComboBox *cmb_moon_phase_algos; QComboBox *cmb_cmdline_default_charset; //QComboBox *cmb_zip_charset_in; //QComboBox *cmb_zip_charset_out; //QCheckBox *cb_altmenu; // QCheckBox *cb_wasd; #if defined(JOYSTICK_SUPPORTED) QCheckBox *cb_use_joystick; #endif QCheckBox *cb_auto_img_preview; QCheckBox *cb_session_restore; QCheckBox *cb_use_trad_dialogs; //QCheckBox *cb_use_enca_for_charset_detection; QCheckBox *cb_override_img_viewer; QLineEdit *ed_img_viewer_override; QCheckBox *cb_save_buffers; QCheckBox *cb_autosave; QSpinBox *spb_autosave_period; /* ============================================= Preferences tab :: Functions page UI elements ============================================= */ QLineEdit *ed_label_end; QLineEdit *ed_label_start; QLineEdit *ed_date_format; QLineEdit *ed_time_format; #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) QComboBox *cmb_spellcheckers; #endif // SPELLCHECKERS ENABLED QLineEdit *ed_spellcheck_path; QLineEdit *ed_aspellcheck_path; QSpinBox *spb_fuzzy_q; QCheckBox *cb_show_ebooks_fine; /* ============================================= Preferences tab :: Images page UI elements ============================================= */ QComboBox *cmb_output_image_fmt; QCheckBox *cb_output_image_flt; QSpinBox *spb_img_quality; //QCheckBox *cb_zip_after_scale; QCheckBox *cb_exif_rotate; QLineEdit *ed_side_size; QLineEdit *ed_link_options; QLineEdit *ed_cols_per_row; QCheckBox *cb_zor_use_exif; /* ============================================= Preferences tab :: Speech page UI elements ============================================= */ #ifdef SPEECH_ENABLE QCheckBox *cb_speech_enable; QCheckBox *cb_locale_only; QComboBox *cmb_cpeech_voices; #endif /* ============================================= Preferences tab :: Keyboard page UI elements ============================================= */ CShortcutEntry *ent_shtcut; QListWidget *lv_menuitems; /* ==================================== Application stuff inits and updates ==================================== */ public: CTEA(); ~CTEA() {}; void handle_args(); void create_paths(); #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) void setup_spellcheckers(); void create_spellcheck_menu(); #endif void create_main_widget_splitter(); void create_main_widget_docked(); void create_actions(); void create_menus(); void create_options(); void create_calendar(); void create_toolbars(); #ifdef SPEECH_ENABLE void create_speech(); #endif void create_manual(); void create_fman(); void create_markup_hash(); void update_stylesheet (const QString &f); void update_styles(); void update_dyn_menus(); void update_fonts(); void update_bookmarks(); void update_templates(); void update_snippets(); void update_keyboards(); void update_sessions(); void update_palettes(); void update_labels_menu(); void update_themes(); void update_hls(); void update_tables(); void update_scripts(); void update_places_bookmarks(); void update_programs(); void update_logmemo_palette(); void update_charsets(); void update_profiles(); void opt_update_keyb(); void read_settings(); void write_settings(); void read_search_options(); void write_search_options(); void calendar_update(); // void create_moon_phase_algos(); /* =========================== Application misc. methods =========================== */ QHash load_eclipse_theme_xml (const QString &fname); void load_palette (const QString &fileName); void fman_convert_images (bool by_side, int value); QTextDocument::FindFlags get_search_options(); QString fif_get_text(); QWidget* create_keyboard (const QString &fname); QAction* add_to_menu (QMenu *menu, const QString &caption, const char *method, const QString &shortkt = QString(), const QString &iconpath = QString() ); QIcon get_theme_icon (const QString &name); QString get_theme_icon_fname (const QString &name); void leaving_options(); // void add_to_last_used_charsets (const QString &s); void count_substring (bool use_regexp); void run_unitaz (int mode); void markup_text (const QString &mode); void fman_items_select_by_regexp (bool mode); void fn_filter_delete_by_sep (bool mode); void fman_find(); void fman_find_next(); void fman_find_prev(); void opt_shortcuts_find(); void opt_shortcuts_find_next(); void opt_shortcuts_find_prev(); void idx_tab_edit_activate(); void idx_tab_calendar_activate(); void idx_tab_tune_activate(); void idx_tab_fman_activate(); void idx_tab_learn_activate(); void man_find_find(); void man_find_next(); void man_find_prev(); void progress_show(); void progress_hide(); protected: void closeEvent (QCloseEvent *event); void dragEnterEvent (QDragEnterEvent *event); void dropEvent (QDropEvent *event); public slots: /* =========================== Main window slots =========================== */ void pageChanged (int index); void logmemo_double_click (const QString &txt); void receiveMessage (const QString &msg); void receiveMessageShared (const QStringList& msg); void tb_stop_clicked(); /* =================== File manager slots =================== */ void fman_drives_changed (const QString & path); void fman_current_file_changed (const QString &full_path, const QString &just_name); void fman_file_activated (const QString &full_path); void fman_dir_changed (const QString &full_path); void fman_fname_entry_confirm(); void fman_naventry_confirm(); void fman_add_bmk(); void fman_del_bmk(); void fman_open(); void fman_places_itemActivated (QListWidgetItem *item); void cb_button_saves_as(); /* =================== Main menu callbacks =================== */ /* =================== File menu callbacks =================== */ void test(); void file_new(); void file_open(); void file_open_at_cursor(); void file_last_opened(); void file_crapbook(); void file_notes(); bool file_save(); bool file_save_as(); void file_save_bak(); void file_save_version(); void file_save_all_existing(); void file_session_save_as(); void file_reload(); void file_reload_enc_itemDoubleClicked (QListWidgetItem *item); void file_reload_enc(); void file_set_eol_unix(); void file_set_eol_win(); void file_set_eol_mac(); void file_set_autosaving_file(); void file_unset_autosaving_file(); #ifdef PRINTER_ENABLE void file_print(); #endif void file_add_to_bookmarks(); void file_find_obsolete_paths(); void file_open_bookmarks_file(); void file_open_programs_file(); void file_open_bookmark(); void file_use_template(); void file_open_session(); void file_recent_off(); void file_close(); /* =================== Edit menu callbacks =================== */ void ed_copy(); void ed_paste(); void ed_cut(); void ed_select_all(); void ed_block_start(); void ed_block_end(); void ed_block_copy(); void ed_block_paste(); void ed_block_cut(); void ed_copy_current_fname(); void ed_undo(); void ed_redo(); void ed_indent(); void ed_unindent(); void ed_indent_by_first_line(); void ed_comment(); void ed_set_as_storage_file(); void ed_copy_to_storage_file(); void ed_capture_clipboard_to_storage_file(); /* =================== Markup menu callbacks =================== */ void mrkup_mode_choosed(); void mrkup_header(); void mrkup_align_center(); void mrkup_align_left(); void mrkup_align_right(); void mrkup_align_justify(); void mrkup_bold(); void mrkup_italic(); void mrkup_underline(); void mrkup_link(); void mrkup_para(); void mrkup_color(); void mrkup_br(); void mrkup_nbsp(); void markup_ins_image(); void mrkup_text_to_html(); void mrkup_tags_to_entities(); void mrkup_antispam_email(); void mrkup_document_weight(); void mrkup_preview_color(); void mrkup_strip_html_tags(); void mrkup_rename_selected(); /* =================== Search menu callbacks =================== */ void search_find(); void search_find_next(); void search_find_prev(); void search_mark_all(); void search_unmark(); void search_in_files_results_dclicked (QListWidgetItem *item); void search_in_files(); void search_whole_words_mode(); void search_from_cursor_mode(); void search_regexp_mode(); void search_fuzzy_mode(); void search_replace_with(); void search_replace_all(); void search_replace_all_at_ofiles(); /* =================== Fn menu callbacks =================== */ void fn_repeat(); void fn_scale_image(); void fn_use_snippet(); void fn_run_script(); void cb_script_finished (int exitCode, QProcess::ExitStatus exitStatus); void fn_use_table(); void fn_insert_loremipsum(); void fn_insert_template_tea(); void fn_insert_template_html(); void fn_insert_template_html5(); void fn_insert_cpp(); void fn_insert_c(); void fn_insert_date(); void fn_insert_time(); void fn_case_up(); void fn_case_down(); void fn_case_inverse(); void fn_case_cap_sentences(); void fn_sort_casecare(); void fn_sort_casecareless(); void fn_sort_casecare_sep(); void fn_sort_length(); void fn_flip_a_list(); void fn_flip_a_list_sep(); void fn_cells_latex_table_sort_by_col_abc(); void fn_cells_swap_cells(); void fn_cells_delete_by_col(); void fn_cells_copy_by_col(); void fn_filter_rm_duplicates(); void fn_filter_rm_empty(); void fn_filter_rm_less_than(); void fn_filter_rm_greater_than(); void fn_filter_delete_before_sep(); void fn_filter_delete_after_sep(); void fn_filter_with_regexp(); void fn_filter_by_repetitions(); void fn_math_evaluate(); void fn_math_number_arabic_to_roman(); void fn_math_number_roman_to_arabic(); void fn_math_number_dec_to_bin(); void fn_math_number_bin_to_dec(); void fn_math_number_flip_bits(); void fn_math_sum_by_last_col(); void fn_math_enum(); void fn_math_number_dms2dc(); void fn_math_number_dd2dms(); void fn_morse_from_ru(); void fn_morse_to_ru(); void fn_morse_from_en(); void fn_morse_to_en(); void fn_analyze_text_stat(); void fn_analyze_extract_words(); void fn_analyze_stat_words_lengths(); void fn_analyze_count(); void fn_analyze_count_rx(); void fn_analyze_get_words_count(); void fn_analyze_unitaz_abc(); void fn_analyze_unitaz_len(); void fn_text_apply_to_each_line(); void fn_text_reverse(); void fn_text_escape(); void fn_text_remove_formatting(); void fn_text_compress(); void fn_text_compare_two_strings(); void fn_text_remove_formatting_at_each_line(); void fn_text_remove_trailing_spaces(); void fn_text_anagram(); void fn_text_regexp_match_check(); #if QT_VERSION >= 0x060000 void fn_math_srt_shift(); #endif void fn_quotes_to_angle(); void fn_quotes_curly(); void fn_quotes_tex_curly(); void fn_quotes_tex_angle_01(); void fn_quotes_tex_angle_02(); #ifdef SPEECH_ENABLE void fn_speech_say_selection(); void fn_speech_stop(); void fn_speech_pause_resume(); #endif #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) void fn_change_spell_lang(); void fn_spell_check(); void fn_spell_suggest_callback(); void fn_spell_add_to_dict(); void fn_spell_remove_from_dict(); void fn_spell_suggest(); #endif // SPELLCHECKERS ENABLED /* ==================== Cal menu =================== */ void cal_moon_mode(); void cal_set_date_a(); void cal_set_date_b(); void cal_add_days(); void cal_add_months(); void cal_add_years(); void cal_set_to_current(); void cal_gen_mooncal(); void cal_diff_days(); void cal_remove(); /* =================== Run menu callbacks =================== */ /* =================== IDE menu callbacks =================== */ void ide_run(); void ide_build(); void ide_clean(); void ide_toggle_hs(); /* =================== Nav menu callbacks =================== */ void nav_save_pos(); void nav_goto_pos(); void nav_goto_line(); void nav_goto_right_tab(); void nav_goto_left_tab(); void nav_focus_to_fif(); void nav_focus_to_editor(); void nav_labels_update_list(); /* =================== Fm menu callbacks =================== */ void fman_multi_rename_zeropad(); void fman_multi_rename_del_n_first_chars(); void fman_multi_rename_replace(); void fman_multi_rename_apply_template(); void fman_fileop_create_dir(); void fman_fileop_rename(); void fman_fileop_delete(); void fm_fileinfo_info(); void fman_fileinfo_count_lines_in_selected_files(); /*void fman_zip_create(); void fman_zip_add(); void fman_zip_save(); void fman_zip_info(); void fman_zip_unpack();*/ void fman_img_conv_by_side(); void fman_img_conv_by_percent(); void fman_img_make_gallery(); void fman_home(); void fman_refresh(); void fman_preview_image(); void fman_select_by_regexp(); void fman_deselect_by_regexp(); /* =================== View menu callbacks =================== */ void view_use_theme(); void view_use_palette(); void view_use_profile(); void view_profile_save_as(); void view_use_keyboard(); void view_toggle_wrap(); void view_hide_error_marks(); void view_toggle_fs(); void view_stay_on_top(); void view_darker(); #if QT_VERSION >= 0x051400 void view_preview_md(); #endif /*-----------------------------*/ /* =================== ? menu callbacks =================== */ void help_show_about(); void help_show_news(); void help_show_todo(); void help_show_changelog(); void help_show_gpl(); /* ===================== Misc callbacks ===================== */ void select_label(); void run_program(); // void guess_enc(); void clipboard_dataChanged(); void main_tab_page_changed (int index); void calendar_clicked (const QDate &date); void calendar_activated (const QDate &date); void calendar_currentPageChanged (int year, int month); void process_readyReadStandardOutput(); void virt_keyb_clicked(); /* ==================== Tune page callbacks ==================== */ #if defined(JOYSTICK_SUPPORTED) void cb_use_joystick_stateChanged (int state); #endif //void cb_altmenu_stateChanged (int state); void cmb_ui_tabs_currentIndexChanged (int i); void cmb_docs_tabs_currentIndexChanged (int i); void cmb_icon_sizes_currentIndexChanged (int i); void cmb_tea_icons_currentIndexChanged (int i); void pb_assign_hotkey_clicked(); void pb_remove_hotkey_clicked(); void slot_lv_menuitems_currentItemChanged (QListWidgetItem *current, QListWidgetItem *previous); void slot_font_logmemo_select(); void slot_font_interface_select(); void slot_font_editor_select(); void slot_style_currentIndexChanged (int); #ifdef SPEECH_ENABLE void cmb_cpeech_voices_currentIndexChanged (int i); void cb_locale_only_stateChanged (int state); #endif #if defined (HUNSPELL_ENABLE) || defined (ASPELL_ENABLE) void cmb_spellchecker_currentIndexChanged (int); #endif #ifdef HUNSPELL_ENABLE void pb_choose_hunspell_path_clicked(); #endif #ifdef ASPELL_ENABLE void pb_choose_aspell_path_clicked(); #endif }; #endif tea-qt-63.3.0/src/textproc.cpp000066400000000000000000000506221476733534200161640ustar00rootroot00000000000000/*************************************************************************** * 2007-2022 by Peter Semiletov * * peter.semiletov@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ /*************************************************************************** some code is taken from Scribus::util.cpp: ------------------- begin : Fri Sep 14 2001 copyright : (C) 2001 by Franz Schmid email : Franz.Schmid@altmuehlnet.de ***************************************************************************/ /* roman.c by Adam Rogoyski (apoc@laker.net) Temperanc on EFNet irc * Copyright (C) 1998 Adam Rogoyski * Converts Decimal numbers to Roman Numerals and Roman Numberals to * Decimals on the command line or in Interactive mode. * Uses an expanded Roman Numeral set to handle numbers up to 999999999 */ #include #include #include #include //#include #if QT_VERSION < 0x050000 #include #else #include #endif #include "textproc.h" #include "utils.h" using namespace std; bool qstring_length_less_than (const QString& v1, const QString& v2) { return v1.length() < v2.length(); } /* QString int_to_binary (int n) { std::bitset bt (n); return QString::fromStdString (bt.to_string()); } */ int str_fuzzy_search (const QString &s, const QString &text_to_find, int start_pos, double q) { if (s.isEmpty() || text_to_find.isEmpty()) return -1; int counter; int result = -1; bool jump = false; int end_pos = s.length() - 1; for (int i = start_pos; i < end_pos; i++) { if (jump) break; counter = 0; for (int j = 0; j < text_to_find.length(); j++) { if (s[i + j] == text_to_find[j]) counter++; if (get_percent ((double)text_to_find.length(), (double)counter) >= q) { result = i; jump = true; break; } } } return result; } QString apply_table (const QString &s, const QString &fname, bool use_regexp) { QHash h = hash_load_keyval (fname); QString result = s; for (int i = 0; i < h.size(); i++) { QString key = h.keys()[i]; if (use_regexp) #if QT_VERSION < 0x050000 result.replace (QRegExp (key), h.value (key)); else #else result.replace (QRegularExpression (key), h.value (key)); else #endif result.replace (key, h.value (key)); } return result; } QString strip_html (const QString &source) { bool do_copy = true; QString dest; for (int i = 0; i < source.length(); i++) { if (source[i] == '<') do_copy = false; else if (source[i] == '>') { do_copy = true; if (i < source.length() - 1) i++; else break; } if (do_copy) dest += source[i]; //% from QStringBuilder // dest = dest % source[i]; //% from QStringBuilder } return dest; } QString qstringlist_process (const QString &s, const QString ¶ms, int mode) { QStringList sl; QStringList l; QString result; if (mode != QSTRL_PROC_FLT_WITH_SORTCASECARE_SEP && mode != QSTRL_PROC_LIST_FLIP_SEP) sl = s.split (QChar::ParagraphSeparator); switch (mode) { case QSTRL_PROC_FLT_WITH_SORTCASECARE_SEP: { if (s.indexOf (params) == -1) return s; QStringList t = s.split (params); t.sort(); result = t.join (params); return result; }; case QSTRL_PROC_LIST_FLIP_SEP: { if (s.indexOf (params) == -1) return s; QStringList t = s.split (params); t.sort(); for (int i = 0; i < t.size(); i++) l.prepend (t.at(i)); result = l.join (params); return result; }; case QSTRL_PROC_FLT_WITH_SORTNOCASECARE: { QMap map; for (int i = 0; i < sl.size(); i++) map.insert (sl[i].toLower(), sl[i]); for (QMap::const_iterator i = map.constBegin(); i != map.constEnd(); ++i) l.append (i.value()); break; } case QSTRL_PROC_FLT_WITH_SORTLEN: { l = sl; std::sort (l.begin(), l.end(), qstring_length_less_than); break; } case QSTRL_PROC_FLT_REMOVE_EMPTY: { for (QList ::iterator i = sl.begin(); i != sl.end(); ++i) if (! i->isEmpty()) l.append (*i); break; }; case QSTRL_PROC_FLT_REMOVE_DUPS: { for (QList ::iterator i = sl.begin(); i != sl.end(); ++i) if (! l.contains (*i)) l.append (*i); break; }; case QSTRL_PROC_REMOVE_FORMATTING: { for (QList ::iterator i = sl.begin(); i != sl.end(); ++i) l.append (i->simplified()); break; }; case QSTRL_PROC_FLT_WITH_REGEXP: { #if QT_VERSION < 0x050000 l = sl.filter (QRegExp (params)); #else l = sl.filter (QRegularExpression (params)); #endif break; } case QSTRL_PROC_FLT_WITH_SORTCASECARE: { l = sl; l.sort(); break; } case QSTRL_PROC_LIST_FLIP: { for (QList ::iterator i = sl.begin(); i != sl.end(); ++i) l.prepend (*i); break; } case QSTRL_PROC_FLT_LESS: { int t = params.toInt(); for (QList ::iterator i = sl.begin(); i != sl.end(); ++i) if (i->size() > t) l.append (*i); break; } case QSTRL_PROC_FLT_GREATER: { int t = params.toInt(); for (QList ::iterator i = sl.begin(); i != sl.end(); ++i) if (i->size() < t) l.append (*i); break; } } result = l.join ("\n"); return result; } QString string_reverse (const QString &s) { QString sn; int c = s.length() - 1; int x = 0; for (int i = c; i > -1; i--) sn[x++] = s.at(i); return sn; } QString conv_quotes (const QString &source, const QString &c1, const QString &c2) { QString x; QString dest; bool flag = true; int c = source.size() - 1; for (int i = 0; i <= c; i++) { if (source.at(i) == '\"') { if (flag) x = c1; else x = c2; flag = ! flag; dest += x; } else dest += source[i]; } return dest; } QStringList html_get_by_patt (const QString &s, const QString &spatt) { QStringList result; int c = s.size(); int i = 0; while (i < c) { int start = s.indexOf (spatt, i, Qt::CaseInsensitive); if (start == -1) break; int end = s.indexOf ('"', start + spatt.size()); if (end == -1) break; result.prepend (s.mid (start + spatt.size(), (end - start) - spatt.size())); i = end + 1; } return result; } QStringList anagram (const QString &s) { QString input = s; QStringList sl; sort (input.begin(), input.end()); do sl.append (input); while (next_permutation (input.begin(), input.end())); return sl; } /* from: * roman.c by Adam Rogoyski (apoc@laker.net) Temperanc on EFNet irc * Copyright (C) 1998 Adam Rogoyski * Converts Decimal numbers to Roman Numerals and Roman Numberals to * Decimals on the command line or in Interactive mode. * Uses an expanded Roman Numeral set to handle numbers up to 999999999 */ #define FROM_ROMAN_I 1 #define FROM_ROMAN_V 5 #define FROM_ROMAN_X 10 #define FROM_ROMAN_L 50 #define FROM_ROMAN_C 100 #define FROM_ROMAN_D 500 #define FROM_ROMAN_M 1000 #define FROM_ROMAN_P 5000 #define FROM_ROMAN_Q 10000 #define FROM_ROMAN_R 50000 #define FROM_ROMAN_S 100000 #define FROM_ROMAN_T 500000 #define FROM_ROMAN_U 1000000 #define FROM_ROMAN_B 5000000 #define FROM_ROMAN_W 10000000 #define FROM_ROMAN_N 50000000 #define FROM_ROMAN_Y 100000000 #define FROM_ROMAN_Z 500000000 int value (char c) { switch (c) { case 'I': return FROM_ROMAN_I; case 'V': return FROM_ROMAN_V; case 'X': return FROM_ROMAN_X; case 'L': return FROM_ROMAN_L; case 'C': return FROM_ROMAN_C; case 'D': return FROM_ROMAN_D; case 'M': return FROM_ROMAN_M; case 'P': return FROM_ROMAN_P; case 'Q': return FROM_ROMAN_Q; case 'R': return FROM_ROMAN_R; case 'S': return FROM_ROMAN_S; case 'T': return FROM_ROMAN_T; case 'U': return FROM_ROMAN_U; case 'B': return FROM_ROMAN_B; case 'W': return FROM_ROMAN_W; case 'N': return FROM_ROMAN_N; case 'Y': return FROM_ROMAN_Y; case 'Z': return FROM_ROMAN_Z; default: return 0; } } int romanToDecimal (const char *roman) { int decimal = 0; for (; *roman; roman++) { /* Check for four of a letter in a fow */ if ((*(roman + 1) && *(roman + 2) && *(roman + 3)) && (*roman == *(roman + 1)) && (*roman == *(roman + 2)) && (*roman == *(roman + 3))) return 0; /* Check for two five type numbers */ if ( ((*roman == 'V') && (*(roman + 1) == 'V')) || ((*roman == 'L') && (*(roman + 1) == 'L')) || ((*roman == 'D') && (*(roman + 1) == 'D')) || ((*roman == 'P') && (*(roman + 1) == 'P')) || ((*roman == 'R') && (*(roman + 1) == 'R')) || ((*roman == 'T') && (*(roman + 1) == 'T')) || ((*roman == 'B') && (*(roman + 1) == 'B')) || ((*roman == 'N') && (*(roman + 1) == 'N')) || ((*roman == 'Z') && (*(roman + 1) == 'Z'))) return 0; /* Check for two lower characters before a larger one */ if ((value(*roman) == value(*(roman + 1))) && (*(roman + 2)) && (value(*(roman + 1)) < value(*(roman + 2)))) return 0; /* Check for the same character on either side of a larger one */ if ((*(roman + 1) && *(roman + 2)) && (value(*roman) == value(*(roman + 2))) && (value(*roman) < value(*(roman + 1)))) return 0; /* Check for illegal nine type numbers */ if (!strncmp(roman, "LXL", 3) || !strncmp(roman, "DCD", 3) || !strncmp(roman, "PMP", 3) || !strncmp(roman, "RQR", 3) || !strncmp(roman, "TST", 3) || !strncmp(roman, "BUB", 3) || !strncmp(roman, "NWN", 3) || !strncmp(roman, "VIV", 3)) return 0; if (value(*roman) < value(*(roman + 1))) { /* check that subtracted value is at least 10% larger, i.e. 1990 is not MXM, but MCMXC */ if ((10 * value(*roman)) < value(*(roman + 1))) return 0; /* check for double subtraction, i.e. IVX */ if (value(*(roman + 1)) <= value(*(roman + 2))) return 0; /* check for subtracting by a number starting with a 5 ie. VX, LD LM */ if (*roman == 'V' || *roman == 'L' || *roman == 'D' || *roman == 'P' || *roman == 'R' || *roman == 'T' || *roman == 'B' || *roman == 'N') return 0; decimal += value (*(roman + 1)) - value (*roman); roman++; } else { decimal += value (*roman); } } return decimal; } //this code is taken from Scribus::util.cpp: QString arabicToRoman (int i) { QString roman; int arabic = i; while (arabic - 1000000 >= 0){ roman += "m"; arabic -= 1000000; } while (arabic - 900000 >= 0){ roman += "cm"; arabic -= 900000; } while (arabic - 500000 >= 0){ roman += "d"; arabic -= 500000; } while (arabic - 400000 >= 0){ roman += "cd"; arabic -= 400000; } while (arabic - 100000 >= 0){ roman += "c"; arabic -= 100000; } while (arabic - 90000 >= 0){ roman += "xc"; arabic -= 90000; } while (arabic - 50000 >= 0){ roman += "l"; arabic -= 50000; } while (arabic - 40000 >= 0){ roman += "xl"; arabic -= 40000; } while (arabic - 10000 >= 0){ roman += "x"; arabic -= 10000; } while (arabic - 9000 >= 0){ roman += "Mx"; arabic -= 9000; } while (arabic - 5000 >= 0){ roman += "v"; arabic -= 5000; } while (arabic - 4000 >= 0){ roman += "Mv"; arabic -= 4000; } while (arabic - 1000 >= 0){ roman += "M"; arabic -= 1000; } while (arabic - 900 >= 0){ roman += "CM"; arabic -= 900; } while (arabic - 500 >= 0){ roman += "D"; arabic -= 500; } while (arabic - 400 >= 0){ roman += "CD"; arabic -= 400; } while (arabic - 100 >= 0){ roman += "C"; arabic -= 100; } while (arabic - 90 >= 0){ roman += "XC"; arabic -= 90; } while (arabic - 50 >= 0){ roman += "L"; arabic -= 50; } while (arabic - 40 >= 0){ roman += "XL"; arabic -= 40; } while (arabic - 10 >= 0){ roman += "X"; arabic -= 10; } while (arabic - 9 >= 0){ roman += "IX"; arabic -= 9; } while (arabic - 5 >= 0){ roman += "V"; arabic -= 5; } while (arabic - 4 >= 0){ roman += "IV"; arabic -= 4; } while (arabic - 1 >= 0){ roman += "I"; arabic -= 1; } return roman; } QString int_to_binary (int n) { QString result; int sz = sizeof (n) * 8 - 1; for (int i = sz; i > -1; i--) { if (n & (1 << i)) result.append ("1"); else result.append ("0"); if (i % 4 == 0) result.append (" "); } return result; } unsigned int bin_to_decimal (const QString &s) { unsigned int table[31]; unsigned int c = 1; unsigned int result = 0; QString sn = string_reverse (s); table[0] = 1; for (int i = 1; i < 31; i++) { c *= 2; table[i] = c; } for (int i = 0; i < sn.size(); i++) if (sn[i] == '1') result += table[i]; return result; } QString str_to_entities (const QString &s) { QString t = s; t = t.replace ("&", "&"); t = t.replace ("\"", """); t = t.replace ("'", "'"); t = t.replace ("<", "<"); t = t.replace (">", ">"); return t; } QString morse_from_lang (const QString &s, const QString &lang) { QHash h = hash_load_keyval (":/text-data/morse-" + lang); QString result; QString x = s.toUpper(); int c = x.size(); for (int i = 0; i < c; i++) { QString t = h.value (QString (x[i])); if (! t.isEmpty()) result.append (t).append (" "); } return result; } QString morse_to_lang (const QString &s, const QString &lang) { QHash h = hash_load_keyval (":/text-data/morse-" + lang); QStringList sl = s.toUpper().split (" "); QString result; for (int i = 0; i < sl.size(); i++) { QString t = h.key (sl[i]); if (! t.isEmpty()) result.append (t); } return result; } //from http://www.cyberforum.ru/cpp-beginners/thread125615.html int get_arab_num (std::string rom_str) { int res = 0; for (size_t i = 0; i < rom_str.length(); ++i) { switch (rom_str[i]) { case 'M': res += 1000; break; case 'D': res += 500; break; case 'C': i + 1 < rom_str.length() && (rom_str[i + 1] == 'D' || rom_str[i + 1] == 'M') ? res -= 100 : res += 100; break; case 'L': res += 50; break; case 'X': i + 1 < rom_str.length() && (rom_str[i + 1] == 'L' || rom_str[i + 1] == 'C') ? res -= 10 : res += 10; break; case 'V': res += 5; break; case 'I': i + 1 < rom_str.length() && (rom_str[i + 1] == 'V' || rom_str[i + 1] == 'X') ? res -= 1 : res += 1; break; }//switch }//for return res; } tea-qt-63.3.0/src/textproc.h000066400000000000000000000026141476733534200156270ustar00rootroot00000000000000#ifndef TEXTPROC_H #define TEXTPROC_H enum { QSTRL_PROC_REMOVE_FORMATTING = 0, QSTRL_PROC_APPLY_2_EACH_LINE, QSTRL_PROC_FLT_WITH_REGEXP, QSTRL_PROC_FLT_WITH_SORTCASECARE, QSTRL_PROC_LIST_FLIP, QSTRL_PROC_FLT_LESS, QSTRL_PROC_FLT_GREATER, QSTRL_PROC_FLT_REMOVE_DUPS, QSTRL_PROC_FLT_REMOVE_EMPTY, QSTRL_PROC_FLT_WITH_SORTNOCASECARE, QSTRL_PROC_FLT_WITH_SORTCASECARE_SEP, QSTRL_PROC_LIST_FLIP_SEP, QSTRL_PROC_FLT_WITH_SORTLEN }; int str_fuzzy_search (const QString &s, const QString &text_to_find, int start_pos, double q); QString apply_table (const QString &s, const QString &fname, bool use_regexp); QString strip_html (const QString &text); QString qstringlist_process (const QString &s, const QString ¶ms, int mode); QString string_reverse (const QString &s); QString conv_quotes (const QString &source, const QString &c1, const QString &c2); QStringList html_get_by_patt (const QString &s, const QString &spatt); QStringList anagram (const QString &s); int romanToDecimal (const char *roman); QString arabicToRoman (int i); QString int_to_binary (int n); unsigned int bin_to_decimal (const QString &s); QString str_to_entities (const QString &s); QString morse_from_lang (const QString &s, const QString &lang); QString morse_to_lang (const QString &s, const QString &lang); int get_arab_num (std::string rom_str); #endif // TEXTPROC_H tea-qt-63.3.0/src/tio.cpp000066400000000000000000001321221476733534200151030ustar00rootroot00000000000000/* DJVU read code taken fromdvutxt.c: //C- DjVuLibre-3.5 //C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. //C- Copyright (c) 2001 AT&T //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from //C- Lizardtech Software. Lizardtech Software has authorized us to //C- replace the original DjVu(r) Reference Library notice by the following //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu): //C- //C- ------------------------------------------------------------------ //C- | DjVu (r) Reference Library (v. 3.5) //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. //C- | The DjVu Reference Library is protected by U.S. Pat. No. //C- | 6,058,214 and patents pending. //C- | //C- | This software is subject to, and may be distributed under, the //C- | GNU General Public License, either Version 2 of the license, //C- | or (at your option) any later version. The license should have //C- | accompanied the software or you may obtain a copy of the license //C- | from the Free Software Foundation at http://www.fsf.org . //C- | //C- | The computer code originally released by LizardTech under this //C- | license and unmodified by other parties is deemed "the LIZARDTECH //C- | ORIGINAL CODE." Subject to any third party intellectual property //C- | claims, LizardTech grants recipient a worldwide, royalty-free, //C- | non-exclusive license to make, use, sell, or otherwise dispose of //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU //C- | General Public License. This grant only confers the right to //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to //C- | the extent such infringement is reasonably necessary to enable //C- | recipient to make, have made, practice, sell, or otherwise dispose //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to //C- | any greater extent that may be necessary to utilize further //C- | modifications or combinations. //C- | //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. //C- +------------------------------------------------------------------ */ #include #include #include //#include #include #include #include //#include #include #include #if QT_VERSION >= 0x050000 #include #else #include #endif #include //////////////////FOR PDF #include ////////////////// #include #if defined (POPPLER_ENABLE) #include #include #endif #if defined (DJVU_ENABLE) #include #include #include #include #include #include #include #include #include #endif #include "pugixml.hpp" #include "tio.h" #include "utils.h" #include "zip.h" #include "enc.h" using namespace std; extern QSettings *settings; class CXML_walker: public pugi::xml_tree_walker { public: QString *text; QStringList paragraphs; bool fine_spaces; bool for_each (pugi::xml_node& node); }; bool CXML_walker::for_each (pugi::xml_node &node) { if (node.type() != pugi::node_element) return true; QString node_name = node.name(); if (paragraphs.contains (node_name, Qt::CaseInsensitive)) { QString t = node.text().as_string(); //QString t = QString::fromUtf8 (node.text().get()); if (! t.isEmpty()) { if (fine_spaces) text->append (" "); text->append (t); if (t.size() > 1) text->append ("\n"); } } return true; } std::vector split_string_to_vector (const string& s, const string& delimeter, const bool keep_empty) { vector result; if (delimeter.empty()) { result.push_back (s); return result; } string::const_iterator substart = s.begin(), subend; while (true) { subend = search (substart, s.end(), delimeter.begin(), delimeter.end()); string temp (substart, subend); if (keep_empty || ! temp.empty()) result.push_back (temp); if (subend == s.end()) break; substart = subend + delimeter.size(); } return result; } //from https://stackoverflow.com/questions/13739924/remove-all-xml-tags-from-a-stdstring std::string xml_strip(std::string &xmlBuffer) { bool copy = true; std::string plainString = ""; std::stringstream convertStream; // remove all xml tags for (int i=0; i < xmlBuffer.length(); i++) { convertStream << xmlBuffer[i]; if(convertStream.str().compare("<") == 0) copy = false; else if(convertStream.str().compare(">") == 0) { copy = true; convertStream.str(std::string()); continue; } if (copy) plainString.append(convertStream.str()); // convertStream.str(std::string()); } return plainString; } /* vector split_string_to_vector (const string& s, const string& delimeter, const bool keep_empty) { vector result; if (delimeter.empty()) { result.push_back (s); return result; } string::const_iterator substart = s.begin(), subend; while (true) { subend = search (substart, s.end(), delimeter.begin(), delimeter.end()); string temp (substart, subend); if (keep_empty || ! temp.empty()) result.push_back (temp); if (subend == s.end()) break; substart = subend + delimeter.size(); } return result; } */ std::string join_lines (const std::vector &lst, const std::string &delim) { std::string ret; //for (const auto &s : lst) for (size_t i = 0; i < lst.size(); i++) { if (! ret.empty()) ret += delim; //ret += s; ret += lst[i]; } return ret; } std::string xml_strip_remove_empty_lines (std::string &xmlBuffer) { std::string s = xml_strip (xmlBuffer); std::vector v = split_string_to_vector (s, "\n", false); return join_lines (v, "\n"); //str.find_first_not_of(" \t\n\v\f\r") != std::string::npos } std::string html_strip (const std::string &source) { std::string html = source; while (html.find ('<') != std::string::npos) { size_t startpos = html.find ('<'); size_t endpos = html.find ('>') + 1; if (endpos != std::string::npos) html.erase (startpos, endpos - startpos); } return html; } template void remove_duplicates(std::vector& vec) { std::sort(vec.begin(), vec.end()); vec.erase (std::unique (vec.begin(), vec.end()), vec.end()); } std::vector extract_hrefs (const std::string &source, const std::string &prefix) { std::vector result; size_t i = 0; size_t limit = source.size() - 1; std::string signature_str = "value ("show_ebooks_fine", "0").toBool(); walker.paragraphs.append (tags); doc.traverse (walker); return data; } QString extract_text_from_xml_pugi (const QString &string_data, const QStringList &tags) { QString data; pugi::xml_document doc; pugi::xml_parse_result result = doc.load_buffer (string_data.utf16(), string_data.size() * 2, pugi::parse_default, pugi::encoding_utf16); if (! result) { qDebug() << "NOT PARSED"; return data; } CXML_walker walker; walker.text = &data; walker.fine_spaces = settings->value ("show_ebooks_fine", "0").toBool(); walker.paragraphs.append (tags); doc.traverse (walker); return data; } /* QString extract_text_from_html (const QString &string_data) { QTextBrowser br; br.setHtml (string_data); return br.toPlainText(); } */ bool CTioPlainText::load (const QString &fname) { data.clear(); QFile file (fname); if (! file.open (QFile::ReadOnly)) { error_string = file.errorString(); return false; } QByteArray block = file.read (4096); eol = "\n"; if (block.indexOf ("\r\n") != -1) eol = "\r\n"; else if (block.indexOf ('\n') != -1) eol = "\n"; else if (block.indexOf ('\r') != -1) eol = "\r"; file.seek(0); /* QByteArray ba = file.readAll(); QTextCodec *codec = QTextCodec::codecForName (charset.toUtf8().data()); data = codec->toUnicode (ba); */ QByteArray ba = file.readAll(); //qDebug() << "charset:" << charset; //ok if (charset == "UTF-8") { //ushort* filedata = TextConverter::ConvertFromUTF8ToUTF16 (ba.data()); //data = QString::fromUtf16 (filedata); //delete [] filedata; data = QString::fromUtf8 (ba.data()); } if (charset == "UTF-16") data = QString::fromUtf16 ((UTF16TEXT *)ba.data()); //ok if (charset == "CP-1251") { UTF16TEXT* filedata = CTextConverter::ConvertFromCP1251ToUTF16 (ba.data()); data = QString::fromUtf16 (filedata); delete [] filedata; } //ok if (charset == "CP-866") { UTF16TEXT* filedata = CTextConverter::ConvertFromDOS866ToUTF16 (ba.data()); data = QString::fromUtf16 (filedata); delete [] filedata; // std::cout << *filedata << std::endl; // qDebug() << ba.data(); // std::wcout << reinterpret_cast(filedata) << std::endl; } if (charset == "KOI8-R") { UTF16TEXT* filedata = CTextConverter::ConvertFromKOI8RToUTF16 (ba.data()); data = QString::fromUtf16 (filedata); delete [] filedata; // std::cout << *filedata << std::endl; // qDebug() << ba.data(); // std::wcout << reinterpret_cast(filedata) << std::endl; } if (eol == "\r\n") data.replace (eol, "\n"); else if (eol == "\r") data.replace (eol, "\n"); file.close(); return true; } bool CTioPlainText::save (const QString &fname) { QFile file (fname); if (! file.open (QFile::WriteOnly)) { error_string = file.errorString(); return false; } qDebug() << "Saving at encoding: " << charset; // QTextCodec *codec = QTextCodec::codecForName(charset.toUtf8().data()); // QByteArray ba = codec->fromUnicode(data); if (charset == "UTF-8") { QByteArray ba = data.toUtf8(); file.write(ba); file.close(); return true; } if (charset == "UTF-16") { QByteArray ba = QByteArray((const char*)data.utf16(), (data.length() + 1) * 2); file.write (ba); file.close(); return true; } //ok if (charset == "CP-1251") { char* text = CTextConverter::ConvertFromUTF16ToCP1251 ((UTF16TEXT*)data.utf16()); file.write (text); file.close(); delete [] text; return true; } if (charset == "KOI8-R") //ok { char* text = CTextConverter::ConvertFromUTF16ToKOI8R ((UTF16TEXT*)data.utf16()); file.write (text); file.close(); delete [] text; return true; } if (charset == "CP-866") //ноль { char* text = CTextConverter::ConvertFromUTF16ToCP866 ((UTF16TEXT*)data.utf16()); file.write (text); file.close(); delete [] text; return true; } return false; } CTioHandler::CTioHandler() { default_handler = new CTioPlainText; list.push_back (default_handler); //list.push_back (new CTioGzip); list.push_back (new CTioXMLZipped); list.push_back (new CTioABW); list.push_back (new CTioFB2); list.push_back (new CTioRTF); list.push_back (new CTioEpub); #if defined (POPPLER_ENABLE) list.push_back (new CTioPDF); #endif #if defined (DJVU_ENABLE) list.push_back (new CTioDJVU); #endif } CTioHandler::~CTioHandler() { if (list.size() > 0) for (vector ::size_type i = 0; i < list.size(); i++) delete list[i]; } CTio* CTioHandler::get_for_fname (const QString &fname) { CTio *instance = 0; //с нуля лишено смысла, ибо list[0] содержит умолчальный обработчик //в котором не создан список расширений файлов для обработки for (vector ::size_type i = 0; i < list.size(); i++) { instance = list.at (i); for (int j = 0; j < instance->extensions.size(); j++) { QString ext = "." + instance->extensions[j]; if (fname.endsWith (ext)) return instance; } } return default_handler; } CTioPlainText::CTioPlainText() { ronly = false; } CTioGzip::CTioGzip() { ronly = true; extensions.append ("gz"); } bool CTioGzip::load (const QString &fname) { data.clear(); /* QByteArray a = gzip_deflateFile (fname); data = a.data(); return true;*/ return false; } bool CTioReadOnly::save (const QString &fname) { error_string = tr ("Saving for this format is not supported"); return false; } CTioABW::CTioABW() { ronly = true; extensions.append ("abw"); } bool CTioABW::load (const QString &fname) { data.clear(); QString temp = qstring_load (fname); QStringList tags; tags.append ("c"); data = extract_text_from_xml_pugi (temp, tags); return true; } CTioXMLZipped::CTioXMLZipped() { ronly = true; extensions.append ("kwd"); extensions.append ("docx"); extensions.append ("odt"); extensions.append ("sxw"); } bool CTioXMLZipped::load (const QString &fname) { data.clear(); std::string fn = fname.toStdString(); QStringList tags; std::string source_fname; QString ext = file_get_ext (fname); if (ext == "kwd") { source_fname = "maindoc.xml"; tags.append ("text"); } else if (ext == "docx") { source_fname = "word/document.xml"; tags.append ("w:t"); } else if (ext == "odt" || ext == "sxw" ) { source_fname = "content.xml"; tags.append ("text:p"); tags.append ("text:s"); } void *buf = NULL; size_t bufsize = 0; struct zip_t *zip = zip_open (fn.c_str(), 0, 'r'); if (! zip) return false; if (zip_entry_open (zip, source_fname.c_str()) < 0) return false; zip_entry_read (zip, &buf, &bufsize); zip_entry_close (zip); data = extract_text_from_xml_pugi ((char*)buf, bufsize, tags); zip_close (zip); free(buf); return true; } /* CCharsetMagic::CCharsetMagic() { QStringList fnames = read_dir_entries (":/encsign"); CSignaturesList *koi8u = NULL; CSignaturesList *koi8r = NULL; for (int i = 0; i < fnames.size(); i++) { QString fn = fnames.at(i); QString fname = ":/encsign/"; fname.append (fn); QByteArray a = file_load (fname); QList bsl = a.split (','); CSignaturesList *sl = new CSignaturesList; sl->encname = fn; if (fn == "KOI8-R") koi8r = sl; if (fn == "KOI8-U") koi8u = sl; //fill with signatures for (int j = 0; j < bsl.count(); j++) sl->words.append (bsl[j]); signatures.push_back (sl); } //the following is needed to correct detection KOI8-R/U std::vector::iterator it1 = std::find(signatures.begin(), signatures.end(), koi8u); std::vector::iterator it2 = std::find(signatures.begin(), signatures.end(), koi8r); std::swap (*it1, *it2); // qDebug() << "signatures.size: " << signatures.size(); } CCharsetMagic::~CCharsetMagic() { if (signatures.size() > 0) for (vector ::size_type i = 0; i < signatures.size(); i++) delete signatures[i]; } */ //from https://stackoverflow.com/questions/28270310/how-to-easily-detect-utf8-encoding-in-the-string bool is_valid_utf8 (const char *string) { if (! string) return true; const unsigned char * bytes = (const unsigned char *)string; unsigned int cp; int num; while (*bytes != 0x00) { if ((*bytes & 0x80) == 0x00) { // U+0000 to U+007F cp = (*bytes & 0x7F); num = 1; } else if ((*bytes & 0xE0) == 0xC0) { // U+0080 to U+07FF cp = (*bytes & 0x1F); num = 2; } else if ((*bytes & 0xF0) == 0xE0) { // U+0800 to U+FFFF cp = (*bytes & 0x0F); num = 3; } else if ((*bytes & 0xF8) == 0xF0) { // U+10000 to U+10FFFF cp = (*bytes & 0x07); num = 4; } else return false; bytes += 1; for (int i = 1; i < num; ++i) { if ((*bytes & 0xC0) != 0x80) return false; cp = (cp << 6) | (*bytes & 0x3F); bytes += 1; } if ((cp > 0x10FFFF) || ((cp >= 0xD800) && (cp <= 0xDFFF)) || ((cp <= 0x007F) && (num != 1)) || ((cp >= 0x0080) && (cp <= 0x07FF) && (num != 2)) || ((cp >= 0x0800) && (cp <= 0xFFFF) && (num != 3)) || ((cp >= 0x10000) && (cp <= 0x1FFFFF) && (num != 4))) return false; } return true; } //QString CCharsetMagic::guess_for_file (const QString &fname) //{ //String enc = "UTF-8"; /* QByteArray bafile = file_load (fname); QString ext = file_get_ext (fname); if (ext == "html" || ext == "htm" || ext == "xhtml") { QTextCodec *defcodec = QTextCodec::codecForName ("UTF-8"); QTextCodec *codec = QTextCodec::codecForHtml (bafile, defcodec); return codec->name(); } if (is_valid_utf8 (bafile.data())) return enc; if (signatures.size() > 0) for (vector ::size_type i = 0; i < signatures.size(); i++) for (int x = 0; x < signatures[i]->words.count(); x++) { if (bafile.contains (signatures[i]->words[x])) { enc = signatures[i]->encname; return enc; } } */ // return enc; //} CTioFB2::CTioFB2() { ronly = true; extensions.append ("fb2"); extensions.append ("fbz"); extensions.append ("fb2.zip"); } class CFB2_walker: public pugi::xml_tree_walker { public: QString *text; bool fine_spaces; bool for_each (pugi::xml_node& node); }; bool CFB2_walker::for_each (pugi::xml_node &node) { if (node.type() != pugi::node_element) return true; QString node_name = node.name(); if (node_name == "p") { if (fine_spaces) text->append (" "); QString t = node.text().as_string(); text->append (t); text->append ("\n"); } if (node_name == "title" || node_name == "section" || node_name == "empty-line") text->append ("\n"); //НЕ ДОБАВЛЯЕТСЯ? return true; } std::string string_between (const std::string &source, const std::string &sep1, const std::string &sep2) { std::string result; size_t pos1 = source.find(sep1); if (pos1 == std::string::npos) return result; size_t pos2 = source.find(sep2, pos1 + sep1.size()); if (pos2 == std::string::npos) return result; pos1 += sep1.size(); result = source.substr(pos1, pos2 - pos1); return result; } bool CTioFB2::load (const QString &fname) { // std::cout << "CTioFB2::load (const QString &fname" << std::endl; data.clear(); QStringList tags; tags.append ("p"); std::string zip_file_name = fname.toStdString(); if (fname.endsWith ("fb2")) { std::string stemp = string_file_load (zip_file_name); if (stemp.find ("encoding=\"windows-1251\"") != std::string::npos) { // std::cout << "1251^^^^^^^^^^^^^^^^^^^^^" << std::endl; UTF16TEXT *t_utf16 = CTextConverter::ConvertFromCP1251ToUTF16 (stemp.c_str()); QString td = QString::fromUtf16 (t_utf16); delete [] t_utf16; data = extract_text_from_xml_pugi (td, tags); if (data.isEmpty()) return false; else return true; } data = extract_text_from_xml_pugi (stemp.c_str(), stemp.size(), tags); if (data.isEmpty()) return false; else return true; } // else // if (fname.endsWith (".fb2.zip") || fname.endsWith (".fbz")) std::string source_fname; //достаем его из зипа //we can have malformed internal filename, so find the first fb2 at the archive struct zip_t *zip = zip_open (zip_file_name.c_str(), 0, 'r'); if (! zip) return false; int n = zip_entries_total(zip); for (int i = 0; i < n; ++i) { zip_entry_openbyindex (zip, i); const char *name = zip_entry_name (zip); std::string tname = name; if (ends_with (tname, "fb2")) { source_fname = tname; zip_entry_close(zip); break; } //int isdir = zip_entry_isdir(zip); //unsigned long long size = zip_entry_size(zip); //unsigned int crc32 = zip_entry_crc32(zip); zip_entry_close(zip); } if (source_fname.empty()) return false; void *buf = NULL; size_t bufsize; if (zip_entry_open (zip, source_fname.c_str()) < 0) return false; zip_entry_read (zip, &buf, &bufsize); zip_entry_close (zip); zip_close(zip); // QString enc = string_between (tmp, "encoding=\"", "\""); //bool need_to_recode = false; //if (strstr ((char*)buf, "encoding=\"windows-1251\"")) // need_to_recode = true; std::string stemp =(char*)buf; free (buf); if (stemp.find ("encoding=\"windows-1251\"") != std::string::npos) { // std::cout << "1251^^^^^^^^^^^^^^^^^^^^^ ZIPPED" << std::endl; UTF16TEXT *t_utf16 = CTextConverter::ConvertFromCP1251ToUTF16 (stemp.c_str()); QString td = QString::fromUtf16 (t_utf16); delete [] t_utf16; data = extract_text_from_xml_pugi (td, tags); if (data.isEmpty()) return false; else return true; } data = extract_text_from_xml_pugi (stemp.c_str(), stemp.size(), tags); if (data.isEmpty()) return false; else return true; } /* pugi::xml_document doc; pugi::xml_parse_result result = doc.load_buffer ((char*)buf, bufsize, pugi::parse_default, pugi::encoding_utf8); free(buf); if (! result) return false; // if (stemp.find ("encoding=\"windows-1251\"") != std::string::npos) CFB2_walker walker; walker.text = &data; walker.fine_spaces = settings->value ("show_ebooks_fine", "0").toBool(); doc.traverse (walker); */ /* bool CTioFB2::load (const QString &fname) { std::cout << "CTioFB2::load (const QString &fname" << std::endl; data.clear(); QStringList tags; tags.append ("p"); std::string zip_file_name = fname.toStdString(); if (fname.endsWith ("fb2")) { std::string stemp = string_file_load (zip_file_name); if (stemp.find ("encoding=\"windows-1251\"") != std::string::npos) { std::cout << "1251^^^^^^^^^^^^^^^^^^^^^" << std::endl; UTF16TEXT *t_utf16 = CTextConverter::ConvertFromCP1251ToUTF16 (stemp.c_str()); QString td = QString::fromUtf16 (t_utf16); delete [] t_utf16; data = extract_text_from_xml_pugi (td, tags); if (data.isEmpty()) return false; else return true; } data = extract_text_from_xml_pugi (stemp.c_str(), stemp.size(), tags); if (data.isEmpty()) return false; else return true; } // else // if (fname.endsWith (".fb2.zip") || fname.endsWith (".fbz")) std::string source_fname; //достаем его из зипа //we can have malformed internal filename, so find the first fb2 at the archive struct zip_t *zip = zip_open (zip_file_name.c_str(), 0, 'r'); if (! zip) return false; int n = zip_entries_total(zip); for (int i = 0; i < n; ++i) { zip_entry_openbyindex (zip, i); const char *name = zip_entry_name (zip); std::string tname = name; if (ends_with (tname, "fb2")) { source_fname = tname; zip_entry_close(zip); break; } //int isdir = zip_entry_isdir(zip); //unsigned long long size = zip_entry_size(zip); //unsigned int crc32 = zip_entry_crc32(zip); zip_entry_close(zip); } if (source_fname.empty()) return false; void *buf = NULL; size_t bufsize; if (zip_entry_open (zip, source_fname.c_str()) < 0) return false; zip_entry_read (zip, &buf, &bufsize); zip_entry_close (zip); zip_close(zip); // QString enc = string_between (tmp, "encoding=\"", "\""); bool need_to_recode = false; if (strstr (buf, "encoding=\"windows-1251\"")) need_to_recode = true; pugi::xml_document doc; pugi::xml_parse_result result = doc.load_buffer ((char*)buf, bufsize, pugi::parse_default, pugi::encoding_utf8); free(buf); if (! result) return false; // if (stemp.find ("encoding=\"windows-1251\"") != std::string::npos) CFB2_walker walker; walker.text = &data; walker.fine_spaces = settings->value ("show_ebooks_fine", "0").toBool(); doc.traverse (walker); return true; }*/ /* bool CTioFB2::load (const QString &fname) { data.clear(); QString temp; CZipper zipper; if (fname.endsWith (".fb2.zip") || fname.endsWith (".fbz")) //if (ext == "fb2.zip" || ext == "fbz") { CZipper zipper; QFileInfo f (fname); QString source_fname = f.baseName() + ".fb2"; if (! zipper.read_as_utf8 (fname, source_fname)) return false; temp = zipper.string_data; } if (fname.endsWith ("fb2")) { QByteArray ba = file_load (fname); if (ba.isEmpty()) return false; //read encoding: QString enc = string_between (QString (ba), "encoding=\"", "\""); if (enc.isEmpty()) enc = "UTF-8"; QTextCodec *codec = QTextCodec::codecForName (enc.toLatin1().data()); temp = codec->toUnicode (ba); } // qDebug() << temp; pugi::xml_document doc; // pugi::xml_parse_result result = doc.load_buffer (temp.utf16(), temp.size() * 2, // pugi::parse_default, // pugi::encoding_utf16); pugi::xml_parse_result result = doc.load_buffer (temp.utf16(), temp.size() * 2, pugi::parse_default, pugi::encoding_utf16); if (! result) return false; //qDebug () << "2"; CFB2_walker walker; walker.text = &data; walker.fine_spaces = settings->value ("show_ebooks_fine", "0").toBool(); doc.traverse (walker); return true; } */ //www.codeguru.com/forum/archive/index.php/t-201658.html //rewritten by Peter Semiletov QString rtf_strip (const QString &rtf) { int length = rtf.size(); if (length < 4) return QString(); int start = 0; start = rtf.indexOf ("\\pard"); if ( start < 1) return QString(); QString strCopy;strCopy.reserve (length); int k = 0; bool slash = false; //is backslash followed by the space bool figure_opened = false; //is opening figure brace followed by the space bool figure_closed = false; //is closing brace followed by the space bool first_space = false; //else spaces are in plain text and must be included to the result QChar ch; for (int j = start; j < length; j++) { ch = rtf.at (j); if (ch == '\\')//we are looking at the backslash { first_space = true; slash = true; } else if (ch == '{') { first_space = true; figure_opened = true; } else if (ch == '}') { first_space = true; figure_closed = true; } else if (ch == ' ')// && //(rtf.indexOf ("\\datafield", j - 10) + 10) != j) { slash = false; figure_opened = false; figure_closed = false; } if ( ch == '\\') { QChar chr = rtf.at (j + 1); if (chr == '{') //if the text contains symbol '{' { slash = false; figure_opened = false; figure_closed = false; first_space = false; strCopy += '{'; j++; k++; continue; } if (chr == '}') //if the text contains symbol '}' { slash = false; figure_opened = false; figure_closed = false; first_space = false; strCopy += '}'; j++; k++; continue; } if (chr == '\\')//if the text contains symbol '\' { slash = false; figure_opened = false; figure_closed = false; first_space = false; strCopy += '\\'; j++; continue; } } if (rtf.at (j) == '\\' && rtf.at (j + 1) == 'p' && rtf.at (j + 2) == 'a' && rtf.at (j + 3) == 'r' && rtf.at (j + 4) != 'd') { slash = false; figure_opened = false; figure_closed = false; first_space = false; strCopy += '\n'; j += 4; continue; } if (slash == false && figure_opened == false && figure_closed == false && ch != '\n') { if (! first_space) strCopy += ch; else first_space = false; } } return strCopy; } CTioRTF::CTioRTF() { ronly = true; extensions.append ("rtf"); } //BROKEN bool CTioRTF::load (const QString &fname) { data.clear(); QByteArray ba = file_load (fname); /* QString text; text.reserve (ba.size()); int i = 0; int l = ba.size(); QString ansicgp; int n = ba.indexOf ("ansicpg"); if (n != -1) { int m = ba.indexOf ('\\', n); n += 7; ansicgp = ba.mid (n, m - n); } if (ansicgp.isEmpty()) //assuming unicode { while (i < l) if ((ba.at(i) == '\\') && (ba.at(i + 1) == 'u')) { QByteArray ta = ba.mid (i, 7); ta = ta.mid (2, 4); QChar c (ta.toInt()); text.append (c); i += 7 + 3; } else { text.append (ba.at(i)); i++; } } else { ansicgp.prepend ("CP"); QTextCodec *codec = QTextCodec::codecForName (ansicgp.toUtf8().data()); //qDebug() << "not unicode!"; while (i < l) if ((ba.at(i) == '\\') && (ba.at(i + 1) == '\'')) { QByteArray ta = ba.mid (i, 4); ta = ta.mid (2, 2); QByteArray bh = ta.fromHex (ta); text.append (codec->toUnicode (bh)); i += 4; } else { text.append (ba.at(i)); i++; } } data = rtf_strip (text); return true;*/ return false; } #if defined (POPPLER_ENABLE) CTioPDF::CTioPDF() { ronly = true; extensions.append ("pdf"); } bool CTioPDF::load (const QString &fname) { data.clear(); poppler::document *d = poppler::document::load_from_file (fname.toStdString ()); if (! d) return false; if (d->is_locked()) { delete d; return false; } int pages_count = d->pages(); for (int i = 0; i < pages_count; i++) { poppler::page *p = d->create_page (i); if (! p) continue; poppler::ustring text_from_page = p->text(); poppler::byte_array ba = text_from_page.to_utf8(); char *str = &*ba.begin(); data += QString::fromUtf8 (str); delete p; } delete d; return true; } #endif #if defined (DJVU_ENABLE) const char *detail = 0; int escape = 0; QString temp_data_s; ddjvu_context_t *ctx; ddjvu_document_t *doc; void djvumsg_handle() { const ddjvu_message_t *msg; if (! ctx) return; msg = ddjvu_message_wait (ctx); while ((msg = ddjvu_message_peek (ctx))) { if (msg->m_any.tag == DDJVU_ERROR) { qDebug() << msg->m_error.message; qDebug() << msg->m_error.filename << ":" << msg->m_error.lineno; return; } ddjvu_message_pop(ctx); } } void dopage (int pageno) { miniexp_t r = miniexp_nil; const char *lvl = (detail) ? detail : "page"; while ((r = ddjvu_document_get_pagetext (doc, pageno, lvl)) == miniexp_dummy) djvumsg_handle(); if ((r = miniexp_nth (5, r)) && miniexp_stringp (r)) { const char *s = miniexp_to_str (r); if (s) { temp_data_s.append (s); temp_data_s.append ('\n'); } } } CTioDJVU::CTioDJVU() { ronly = true; extensions.append ("djvu"); } bool CTioDJVU::load (const QString &fname) { data.clear(); if (! (ctx = ddjvu_context_create ("tea"))) return false; if (! (doc = ddjvu_document_create_by_filename (ctx, fname.toUtf8().data(), TRUE))) return false; while (! ddjvu_document_decoding_done (doc)) djvumsg_handle(); int n = ddjvu_document_get_pagenum (doc); for (int i = 0; i < n; i++) dopage (i); if (doc) ddjvu_document_release (doc); if (ctx) ddjvu_context_release (ctx); data = temp_data_s; return true; } #endif CTioEpub::CTioEpub() { ronly = true; extensions.append ("epub"); } std::vector extract_src_from_toc (const std::string &source, const std::string &prefix) { std::vector result; size_t i = 0; size_t limit = source.size() - 1; std::string signature_str = "src=\""; size_t signature_size = signature_str.size(); while (i < limit) { size_t pos_start = source.find (signature_str, i); if (pos_start == std::string::npos) break; size_t pos_end = source.find ('\"', pos_start + signature_size); if (pos_end == std::string::npos) break; //else std::string url = source.substr (pos_start + signature_size, pos_end - (pos_start + signature_size)); // qDebug() << "url:" << url; //find "#" if any //remove after # to the end of string size_t pos_part = url.find ('#'); if (pos_end != std::string::npos) url = url.substr (0, pos_part); if (ends_with (url, "html") || ends_with (url, "htm") || ends_with (url, "xhtml") || ends_with (url, "xml")) { std::string url_to_add = prefix + url; result.push_back (url_to_add); } i = pos_end + 1; } return result; } std::vector extract_src_from_opf (const std::string &source, const std::string &prefix) { std::vector result; size_t i = 0; size_t limit = source.size() - 1; std::string signature_str = "href=\""; size_t signature_size = signature_str.size(); qDebug() << "limit: " << limit; while (i < limit) { size_t pos_start = source.find (signature_str, i); qDebug() << "pos_start: " << pos_start; if (pos_start == std::string::npos) break; size_t pos_end = source.find ('\"', pos_start + signature_size); qDebug() << "pos_end: " << pos_end; if (pos_end == std::string::npos) break; //else std::string url = source.substr (pos_start + signature_size, pos_end - (pos_start + signature_size)); // qDebug() << "url:" << url; //find "#" if any //remove after # to the end of string size_t pos_part = url.find ('#'); if (pos_end != std::string::npos) url = url.substr (0, pos_part); if (ends_with (url, "html") || ends_with (url, "htm") || ends_with (url, "xhtml") || ends_with (url, "xml")) { std::string url_to_add = prefix + url; result.push_back (url_to_add); } i = pos_end + 1; } return result; } /* переписать алгоритм! 1 делаем анзип 2 открываем META-INF/container.xml 3 читаем оттуда full-path: 4. Открываем full-path 5. Парсим все 6. проверяем является ли расширение href == xml, xhtml, html, htm 7. если да, создаем url и добавляем в список */ std::string str_between(const std::string &source, const std::string &sep1, const std::string &sep2) { std::string result; size_t pos1 = source.find(sep1); if (pos1 == std::string::npos) return result; size_t pos2 = source.find(sep2, pos1 + sep1.size()); if (pos2 == std::string::npos) return result; pos1 += sep1.size(); result = source.substr(pos1, pos2 - pos1); return result; } bool CTioEpub::load (const QString &fn) { qDebug() << "CTioEpub::load " << fn; data.clear(); std::string fname = fn.toStdString(); std::vector tags; struct zip_t *zip = zip_open (fname.c_str(), 0, 'r'); if (! zip) return false; //read META-INF/container.xml void *buffer = NULL; size_t bufsize; std::string subdir; if (zip_entry_open (zip, "META-INF/container.xml") < 0) //не можем открыть просто toc? return false; zip_entry_read (zip, &buffer, &bufsize); zip_entry_close (zip); if (bufsize == 0) return false; // done with container_xml std::string content ((char*)buffer); free (buffer); //qDebug() << "buffer:" << buffer; //читаем оттуда full-path: std::string rootfile_path = str_between (content, "rootfile full-path=\"", "\""); //get subdir! size_t separator_pos = rootfile_path.find ("/"); if (separator_pos != std::string::npos) { subdir = rootfile_path.substr (0, separator_pos + 1); // qDebug() << "subdir: " << subdir; } //read rootfile if (zip_entry_open (zip, rootfile_path.c_str()) < 0) //не можем открыть просто toc? return false; zip_entry_read (zip, &buffer, &bufsize); zip_entry_close (zip); if (bufsize == 0) return false; std::string root_file_buffer ((char*)buffer); free (buffer); std::vector urls = extract_src_from_opf (root_file_buffer, subdir); // remove_duplicates (urls); //HERE WE ALREADY PARSED URLS if (urls.size() == 0) return false; tags.push_back ("p"); //read urls from epub for (size_t i = 0; i < urls.size(); i++) { //check duplicated urls, skip dups if (i + 1 != urls.size()) if (urls[i] == urls[i+1]) continue; // qDebug() << "i: " << i; // std::cout << "open: " << urls[i] << std::endl; void *temp = NULL; if (zip_entry_open (zip, urls[i].c_str()) >= 0) { // qDebug() << "opened " << urls[i].c_str(); zip_entry_read (zip, &temp, &bufsize); zip_entry_close (zip); // qDebug() << "readed and closed"; std::string st; if (temp) st = (char*) temp; //std::string st_cleaned = html_strip (st); std::string st_cleaned; // if (ends_with (urls[i].c_str(), "xml") || ends_with (urls[i].c_str(), "xhtml")) //st_cleaned = xml_strip (st); st_cleaned = xml_strip_remove_empty_lines (st); //else // st_cleaned = html_strip (st); // qDebug() << "cleanded"; //print_lines (file_lines); //if (file_lines.size() > 0) // lines.insert(std::end(lines), std::begin(file_lines), std::end(file_lines)); data += QString::fromStdString (st_cleaned); data += "\n"; free (temp); } } zip_close(zip); return true; } /* bool CTioEpub::load (const QString &fn) { qDebug() << "CTioEpub::load " << fn; data.clear(); std::string fname = fn.toStdString(); std::vector tags; struct zip_t *zip = zip_open (fname.c_str(), 0, 'r'); qDebug() << "1"; if (! zip) return false; qDebug() << "2"; //read toc.ncx void *toc = NULL; size_t bufsize; std::string subdir; if (zip_entry_open (zip, "toc.ncx") < 0) //не можем открыть просто toc? { subdir = "OEBPS/"; qDebug() << "3"; if (zip_entry_open (zip, "OEBPS/toc.ncx") < 0) //пробуем еще так { qDebug() << "4"; if (zip_entry_open (zip, "OPS/toc.ncx") < 0) //и так return false; qDebug() << "5"; subdir = "OPS/"; } } qDebug() << "subdir:" << subdir; zip_entry_read (zip, &toc, &bufsize); zip_entry_close (zip); if (bufsize == 0) return false; // done with toc std::string content ((char*)toc); free (toc); std::vector urls = extract_src_from_toc (content, subdir); // remove_duplicates (urls); //HERE WE ALREADY PARSED URLS // qDebug() << "urls.size(): " << urls.size(); if (urls.size() == 0) return false; tags.push_back ("p"); //read urls from epub for (size_t i = 0; i < urls.size(); i++) { //check duplicated urls, skip dups if (i + 1 != urls.size()) if (urls[i] == urls[i+1]) continue; // qDebug() << "i: " << i; // std::cout << "open: " << urls[i] << std::endl; void *temp = NULL; if (zip_entry_open (zip, urls[i].c_str()) >= 0) { // qDebug() << "opened " << urls[i].c_str(); zip_entry_read (zip, &temp, &bufsize); zip_entry_close (zip); // qDebug() << "readed and closed"; std::string st; if (temp) st = (char*) temp; //std::string st_cleaned = html_strip (st); std::string st_cleaned; // if (ends_with (urls[i].c_str(), "xml") || ends_with (urls[i].c_str(), "xhtml")) //st_cleaned = xml_strip (st); st_cleaned = xml_strip_remove_empty_lines (st); //else // st_cleaned = html_strip (st); // qDebug() << "cleanded"; //print_lines (file_lines); //if (file_lines.size() > 0) // lines.insert(std::end(lines), std::begin(file_lines), std::end(file_lines)); data += QString::fromStdString (st_cleaned); data += "\n"; free (temp); } } zip_close(zip); return true; } */ /* QStringList CTioHandler::get_supported_exts() { QStringList l; for (std::vector ::iterator t = list.begin(); t != list.end(); ++t) for (int i = 0; i < (*t)->extensions.size(); i++) l.append ((*t)->extensions[i]); l.append ("txt"); return l; } */ tea-qt-63.3.0/src/tio.h000066400000000000000000000055201476733534200145510ustar00rootroot00000000000000#ifndef TIO_H #define TIO_H #include #include #include #include #include #include class CSignaturesList: public QObject { Q_OBJECT public: QString encname; QList words; }; /* class CCharsetMagic: public QObject { Q_OBJECT public: std::vector signatures; CCharsetMagic(); ~CCharsetMagic(); QString guess_for_file (const QString &fname); }; */ class CTio: public QObject { Q_OBJECT public: bool ronly; QString data; //текст при load читается сюда QString charset; QString error_string; QString eol; QStringList extensions; virtual bool load (const QString &fname) = 0; virtual bool save (const QString &fname) = 0; // virtual bool understand (const QString &fname) = 0; virtual ~CTio() {}; }; //default handler class CTioPlainText: public CTio { Q_OBJECT public: CTioPlainText(); bool load (const QString &fname); bool save (const QString &fname); //bool understand (const QString &fname) {return false}; }; class CTioReadOnly: public CTio { Q_OBJECT public: bool save (const QString &fname); }; class CTioGzip: public CTioReadOnly { Q_OBJECT public: CTioGzip(); bool load (const QString &fname); }; class CTioXMLZipped: public CTioReadOnly { Q_OBJECT public: CTioXMLZipped(); bool load (const QString &fname); }; class CTioEpub: public CTioReadOnly { Q_OBJECT public: CTioEpub(); bool load (const QString &fn); }; class CTioABW: public CTioReadOnly { Q_OBJECT public: CTioABW(); bool load (const QString &fname); }; class CTioFB2: public CTioReadOnly { Q_OBJECT public: CTioFB2(); bool load (const QString &fname); }; class CTioRTF: public CTioReadOnly { Q_OBJECT public: CTioRTF(); bool load (const QString &fname); }; #if defined(POPPLER_ENABLE) class CTioPDF: public CTioReadOnly { Q_OBJECT public: CTioPDF(); bool load (const QString &fname); }; #endif #if defined(DJVU_ENABLE) class CTioDJVU: public CTioReadOnly { Q_OBJECT public: CTioDJVU(); bool load (const QString &fname); }; #endif class CTioHandler: public QObject { Q_OBJECT public: std::vector list; CTioPlainText *default_handler; CTioHandler(); ~CTioHandler(); CTio* get_for_fname (const QString &fname); // QStringList get_supported_exts(); }; /* class CCharsetConverter { Q_OBJECT public: static virtual QStringList get_charsets() = 0; virtual ~CCharsetConverter() {}; }; */ /* class CIconvCharsetConverter: public QObject { Q_OBJECT public: QStringList charsets; QStringList charsets_for_locale (const QString &loc); void load_charsets(); QString to_utf16 (const QString &enc_from, char *inbuf, int len); // char* from_utf_16 (const QString &enc_to, const QString &data); }; */ #endif // TIO_H tea-qt-63.3.0/src/todo.cpp000066400000000000000000000027141476733534200152600ustar00rootroot00000000000000#include "utils.h" #include "todo.h" void CTodo::check_timeout() { QDate cur_date = QDate::currentDate(); if (cur_date.daysTo (prev_date) > 0) load_dayfile(); QString time = QTime::currentTime().toString ("HH:mm"); if (table.contains (time)) { text_info->clear(); text_info->insertHtml ("" + time + "
"); text_info->insertPlainText (table[time]); w->move (QPoint (1, 1)); w->show(); w->raise(); w->activateWindow(); } } CTodo::~CTodo() { delete w; } CTodo::CTodo() { w = new QWidget (0, Qt::Window); w->resize (QSize (320, 240)); w->setWindowTitle (tr ("Attention! Attention!")); text_info = new QTextEdit (w); text_info->setReadOnly (true); text_info->resize (w->size()); prev_date = QDate::currentDate(); connect (&timer, SIGNAL(timeout()), this, SLOT(check_timeout())); timer.setInterval (1000 * 60); timer.start(); } void CTodo::load_dayfile() { QString fname = dir_days + "/" + QDate::currentDate().toString ("yyyy-MM-dd"); if (! file_exists (fname)) { table.clear(); return; } QString ts = qstring_load (fname); QStringList sl = ts.split ('['); for (QList ::iterator s = sl.begin(); s != sl.end(); ++s) { int br = s->indexOf (']'); if (br == -1) continue; QString time = s->left (br); QString text = s->right (s->size() - br - 1); table.insert (time, text); } } tea-qt-63.3.0/src/todo.h000066400000000000000000000007711476733534200147260ustar00rootroot00000000000000#ifndef TODO_H #define TODO_H #include #include #include #include #include #include #include #include class CTodo: public QObject { Q_OBJECT public: QDate prev_date; QTimer timer; QString dir_days; QHash table; QMessageBox msgBox; QWidget *w; QTextEdit *text_info; CTodo(); ~CTodo(); void load_dayfile(); public slots: void check_timeout(); }; #endif // TODO_H tea-qt-63.3.0/src/utils.cpp000066400000000000000000000417751476733534200154650ustar00rootroot00000000000000/* this code is Public Domain Peter Semiletov */ #include //#include #include #include #include #include #include #include #include #include "utils.h" //#include "enc.h" using namespace std; bool boring; /* file utils */ bool has_css_file (const QString &path) { if (path.isEmpty()) return false; QDir d (path); QStringList l = d.entryList(); for (int i = 0; i < l.size(); i++) { if (l.at(i).endsWith(".css", Qt::CaseInsensitive)) return true; } return false; } /* QString guess_enc_for_file (const QString &fname) { if (fname.isEmpty()) return QString(); QString enc = "UTF-8"; QProcess p; p.start ("enca", QStringList() << "-i" << fname); if (! p.waitForStarted() || ! p.waitForFinished() ) return "err"; QString s = p.readAllStandardOutput(); if (! s.isEmpty()) enc = s.trimmed(); return enc; } */ bool file_is_writable (const QString &fname) { if (fname.isEmpty()) return false; QFile f (fname); return f.isWritable(); } bool file_is_readable (const QString &fname) { if (fname.isEmpty()) return false; QFile f (fname); return f.isReadable(); } bool path_is_file (const QString &fname) { if (fname.isEmpty()) return false; QFileInfo fi (fname); return fi.isFile(); } bool path_is_dir (const QString &fname) { if (fname.isEmpty()) return false; QFileInfo fi (fname); return fi.isDir(); } bool path_is_abs (const QString &fname) { if (fname.isEmpty()) return false; if (fname[0] == '/' || fname.indexOf (':') == 1) return true; else return false; } bool dir_exists (const QString &path) { if (path.isEmpty()) return false; QDir d (path); return d.exists(); } bool file_exists (const QString &fileName) { if (fileName.isEmpty()) return false; return QFile::exists (fileName); } QString change_file_ext (const QString &s, const QString &ext) { int i = s.lastIndexOf ("."); if (i == -1) return (s + "." + ext); QString r (s); r.truncate (++i); r.append (ext); return r; } QString file_get_ext (const QString &file_name) { if (file_name.isEmpty()) return QString(); /* int i = file_name.lastIndexOf ("."); if (i == -1) return QString(); return file_name.mid (i + 1).toLower(); */ QFileInfo fi (file_name); return fi.completeSuffix(); } QStringList read_dir_entries (const QString &path) { QDir dir (path); return dir.entryList (QDir::AllEntries | QDir::NoDotAndDotDot); } /* io utils */ bool qstring_save (const QString &fileName, const QString &data) { if (fileName.isEmpty()) return false; QFile file (fileName); if (! file.open (QFile::WriteOnly)) return false; // if (enc == "UTF-8") // { QByteArray ba = data.toUtf8(); file.write (ba); file.close(); return true; //} //return false; } QString qstring_load (const QString &fileName) { if (fileName.isEmpty()) return QString(); QFile file (fileName); QString result; if (! file.open (QFile::ReadOnly)) return QString(); QByteArray ba = file.readAll(); file.close(); // if (enc == "UTF-8") result = QString::fromUtf8 (ba.data()); //if (enc == "UTF-16") // result = QString::fromUtf16 ((char16_t *)ba.data()); return result; } /* bool qstring_save (const QString &fileName, const QString &data, const char *enc) { if (fileName.isEmpty()) return false; QFile file (fileName); if (! file.open (QFile::WriteOnly)) return false; QTextCodec *codec = QTextCodec::codecForName (enc); if (! codec) return false; QByteArray ba = codec->fromUnicode (data); file.write (ba); file.close(); return true; } QString qstring_load (const QString &fileName, const char *enc) { if (fileName.isEmpty()) return QString(); QFile file (fileName); if (! file.open (QFile::ReadOnly)) return QString(); QByteArray ba = file.readAll(); QTextCodec *codec = QTextCodec::codecForName (enc); if (! codec) return QString(); file.close(); return codec->toUnicode (ba); } */ QString qstring_load_first_line (const QString &fileName) { if (fileName.isEmpty()) return QString(); QFile file (fileName); if (! file.open (QFile::ReadOnly | QFile::Text)) return QString(); QTextStream in(&file); return in.readLine(); } QByteArray file_load (const QString &fileName) { if (fileName.isEmpty()) return QByteArray(); QFile file (fileName); QByteArray b; if (! file.open (QFile::ReadOnly)) return b; b = file.readAll(); return b; } /* string/stringlist utils */ bool ends_with (std::string const &value, std::string const &ending) { if (ending.size() > value.size()) return false; return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); } std::string string_file_load (const std::string &fname) { if (fname.empty()) return std::string(); std::ifstream t (fname.c_str()); std::string s ((std::istreambuf_iterator(t)), std::istreambuf_iterator()); return s; } void strlist_swap (QStringList &l, int a, int b) { QString t = l[a]; l[a] = l[b]; l[b] = t; } QString string_between (const QString &source, const QString &sep1, const QString &sep2) { QString result; int pos1 = source.indexOf (sep1); if (pos1 == -1) return result; int pos2 = source.indexOf (sep2, pos1 + sep1.size()); if (pos2 == -1) return result; pos1 += sep1.size(); result = source.mid (pos1, pos2 - pos1); return result; } bool char_is_bad (const QChar &c) { if (! c.isNull() && ! c.isLetter()) return true; return false; } void qstring_list_print (const QStringList &l) { for (int i = 0; i < l.size(); i++) qDebug() << l[i]; } QStringList bytearray_to_stringlist (const QList &a) { QStringList r; for (int i = 0; i < a.size(); i++) r.append (a.at(i).data()); return r; } /* hash utils */ QString hash_get_val (QHash &h, const QString &key, const QString &def_val) { QString result = h.value (key); if (result.isEmpty()) { result = def_val; h.insert (key, def_val); } return result; } QString qstring_load_value (const QString &fileName, const QString &key, const QString &def) { QHash h = hash_load_keyval (fileName); return hash_get_val (h, key, def); } QHash hash_load_keyval (const QString &fname) { QHash result; if (! file_exists (fname)) return result; QStringList l = qstring_load (fname).split ("\n"); for (QList ::iterator i = l.begin(); i != l.end(); ++i) { QStringList sl = i->split ("="); if (sl.size() > 1) result.insert (sl.at(0), sl.at(1)); } return result; } void hash_save_keyval (const QString &fname, const QHash &h) { QFile::remove (fname); QStringList l; QHash::const_iterator i = h.constBegin(); while (i != h.constEnd()) { l+= (i.key() + "=" + i.value()); ++i; } qstring_save (fname, l.join ("\n")); } /* image utils */ bool is_image (const QString &filename) { if (filename.isEmpty()) return false; QList a = QImageReader::supportedImageFormats(); for (QList ::iterator i = a.begin(); i != a.end(); ++i) { QString t (i->data()); if (t == "pdf") //hack for qt6 continue; if (filename.endsWith (t.prepend ("."), Qt::CaseInsensitive)) return true; } return false; } QString get_insert_image (const QString &file_name, const QString &full_path, const QString &markup_mode) { if (! is_image (full_path)) return QString(); QFileInfo inf (file_name); QDir dir (inf.absolutePath()); QImage img; img.load (full_path); QString result; if (markup_mode == "HTML") result = QString ("\"\"").arg ( dir.relativeFilePath (full_path)).arg (img.width()).arg (img.height()); else if (markup_mode == "XHTML") result = QString ("\"\"").arg ( dir.relativeFilePath (full_path)).arg (img.width()).arg (img.height()); else if (markup_mode == "Docbook") result = QString ("\n\n").arg ( dir.relativeFilePath (full_path)) ; else if (markup_mode == "LaTeX") result = QString ("\\includegraphics{%1}").arg (dir.relativeFilePath (full_path)); else if (markup_mode == "Lout") result = QString ("@IncludeGraphic {%1}").arg (dir.relativeFilePath (full_path)); else if (markup_mode == "Markdown") result = QString ("![alt_text](%1)").arg (dir.relativeFilePath (full_path)); return result; } /* class functions */ void CFilesList::iterate (QFileInfo &fi) { qApp->processEvents(); if (boring) return; if (fi.isDir()) { QDir d (fi.absoluteFilePath()); QFileInfoList lfi= d.entryInfoList (QDir::Dirs | QDir::Files | QDir::Readable | QDir::NoDotAndDotDot); for (int i = 0; i < lfi.count(); i++) iterate (lfi[i]); } else if (fi.isFile()) list.append (fi.absoluteFilePath()); } void CFilesList::get (const QString &path) { if (path.isEmpty()) return; list.clear(); QDir d (path); QFileInfoList lfi= d.entryInfoList (QDir::Dirs | QDir::Files | QDir::Readable | QDir::NoDotAndDotDot); for (int i = 0; i < lfi.count(); i++) iterate (lfi[i]); } #if QT_VERSION < 0x050000 #define ADDTEXTTYPE(expr) patterns.push_back (QRegExp (expr, Qt::CaseInsensitive)); #else #define ADDTEXTTYPE(expr) patterns.push_back (QRegularExpression (expr, QRegularExpression::CaseInsensitiveOption)); #endif CFTypeChecker::CFTypeChecker() { ADDTEXTTYPE (".*(readme|news|changelog|todo)$"); ADDTEXTTYPE ("^\\..*(rc)$"); ADDTEXTTYPE ("^.*\\.(txt|conf|md|ini|bat|cfg|sbv|log|odt|docx|kwd|fb2|fb2.zip|fbz|abw|rtf|epub|sxw)$"); ADDTEXTTYPE ("^.*\\.(cpp|c|h|hh|cxx|hpp|cc|m|mm)$"); ADDTEXTTYPE ("^.*\\.(htm|html|xml|xhtml|ts|osm|xsl)$"); #if defined(POPPLER_ENABLE) ADDTEXTTYPE ("^.*\\.(pdf)$"); #endif #if defined(DJVU_ENABLE) ADDTEXTTYPE ("^.*\\.(djvu)$"); #endif ADDTEXTTYPE ("^.*\\.(awk)$"); ADDTEXTTYPE ("^.*\\.(sh)$"); ADDTEXTTYPE ("^.*\\.(bas|bi|vbs|vbe)$"); ADDTEXTTYPE ("^.*\\.(d)$"); ADDTEXTTYPE ("^.*\\.(f|for|f90|f95)$"); ADDTEXTTYPE ("^.*\\.(java|js)$"); ADDTEXTTYPE ("^.*\\.(ly)$"); ADDTEXTTYPE ("^.*\\.(lout)$"); ADDTEXTTYPE ("^.*\\.(lua)$"); ADDTEXTTYPE ("^.*\\.(asm)$"); ADDTEXTTYPE ("^.*\\.(nsi)$"); ADDTEXTTYPE ("^.*\\.(pp|pas|dpr)$"); ADDTEXTTYPE ("^.*\\.(pl|pm)$"); ADDTEXTTYPE ("^.*\\.(php)$"); ADDTEXTTYPE ("^.*\\.(po)$"); ADDTEXTTYPE ("^.*\\.(py)$"); ADDTEXTTYPE ("^.*\\.(r)$"); ADDTEXTTYPE ("^.*\\.(sd7)$"); ADDTEXTTYPE ("^.*\\.(tex|lyx)$"); ADDTEXTTYPE ("^.*\\.(vala)$"); ADDTEXTTYPE ("^.*\\.(v)$"); ADDTEXTTYPE ("^.*\\.(wiki)$"); } /* CFTypeChecker::CFTypeChecker() { #if QT_VERSION < 0x050000 patterns.push_back (QRegExp (".*(readme|news|changelog|todo)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^\\..*(rc)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(txt|conf|md|ini|bat|cfg|sbv|log|odt|docx|kwd|fb2|abw|rtf|epub|sxw|fb2.zip)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(cpp|c|h|hh|cxx|hpp|cc|m|mm)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(htm|html|xml|xhtml|ts|osm|xsl)$", Qt::CaseInsensitive)); #if defined(POPPLER_ENABLE) patterns.push_back (QRegExp ("^.*\\.(pdf)$", Qt::CaseInsensitive)); #endif #if defined(DJVU_ENABLE) patterns.push_back (QRegExp ("^.*\\.(djvu)$", Qt::CaseInsensitive)); #endif patterns.push_back (QRegExp ("^.*\\.(awk)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(sh)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(bas|bi|vbs|vbe)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(d)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(f|for|f90|f95)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(java|js)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(ly)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(lout)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(lua)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(asm)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(nsi)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(pp|pas|dpr)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(pl|pm)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(php)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(po)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(py)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(r)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(sd7)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(tex|lyx)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(vala)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(v)$", Qt::CaseInsensitive)); patterns.push_back (QRegExp ("^.*\\.(wiki)$", Qt::CaseInsensitive)); #else patterns.push_back (QRegularExpression (".*(readme|news|changelog|todo)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^\\..*(rc)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(txt|conf|md|ini|bat|cfg|sbv|log|odt|docx|kwd|fb2|abw|rtf|epub|sxw)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(cpp|c|h|hh|cxx|hpp|cc|m|mm)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(htm|html|xml|xhtml|ts|osm|xsl)$", QRegularExpression::CaseInsensitiveOption)); #if defined(POPPLER_ENABLE) patterns.push_back (QRegularExpression ("^.*\\.(pdf)$", QRegularExpression::CaseInsensitiveOption)); #endif #if defined(DJVU_ENABLE) patterns.push_back (QRegularExpression ("^.*\\.(djvu)$", QRegularExpression::CaseInsensitiveOption)); #endif patterns.push_back (QRegularExpression ("^.*\\.(awk)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(sh)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(bas|bi|vbs|vbe)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(d)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(f|for|f90|f95)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(java|js)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(ly)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(lout)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(lua)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(asm)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(nsi)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(pp|pas|dpr)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(pl|pm)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(php)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(po)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(py)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(r)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(sd7)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(tex|lyx)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(vala)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(v)$", QRegularExpression::CaseInsensitiveOption)); patterns.push_back (QRegularExpression ("^.*\\.(wiki)$", QRegularExpression::CaseInsensitiveOption)); #endif } */ bool CFTypeChecker::check (const QString &fname) const { #if QT_VERSION < 0x050000 for (size_t i = 0; i < patterns.size(); ++i) { if (patterns[i].exactMatch(fname)) return true; } #else for (size_t i = 0; i < patterns.size(); ++i) { QRegularExpressionMatch match = patterns[i].match(fname); if (match.hasMatch()) return true; } #endif return false; } tea-qt-63.3.0/src/utils.h000066400000000000000000000061231476733534200151160ustar00rootroot00000000000000/* this code is Public Domain Peter Semiletov */ #ifndef UTILS_H #define UTILS_H #include #include #include #include #include #if QT_VERSION < 0x050000 #include #else #include #endif class CFilesList: public QObject { public: QStringList list; void get (const QString &path); void iterate (QFileInfo &fi); }; class CFTypeChecker: public QObject { public: #if QT_VERSION < 0x050000 std::vector patterns; #else std::vector patterns; #endif CFTypeChecker(); bool check (const QString &fname) const; }; /* file utils */ bool has_css_file (const QString &path); //QString guess_enc_for_file (const QString &fname); bool file_is_writable (const QString &fname); bool file_is_readable (const QString &fname); bool path_is_file (const QString &fname); bool path_is_dir (const QString &fname); bool path_is_abs (const QString &fname); bool dir_exists (const QString &path); bool file_exists (const QString &fileName); QString change_file_ext (const QString &s, const QString &ext); QString file_get_ext (const QString &file_name); QStringList read_dir_entries (const QString &path); /* io utils */ bool qstring_save (const QString &fileName, const QString &data); QString qstring_load (const QString &fileName); QString qstring_load_first_line (const QString &fileName); QByteArray file_load (const QString &fileName); /* string/stringlist utils */ bool ends_with (std::string const &value, std::string const &ending); std::string string_file_load (const std::string &fname); //bool str_check (char *s1, char *s2, int size); QString string_between (const QString &source, const QString &sep1, const QString &sep2); bool char_is_bad (const QChar &c); void qstring_list_print (const QStringList &l); QStringList bytearray_to_stringlist (const QList &a); void strlist_swap (QStringList &l, int a, int b); /* hash utils */ QString hash_get_val (QHash &h, const QString &key, const QString &def_val); QString qstring_load_value (const QString &fileName, const QString &key, const QString &def); QHash hash_load_keyval (const QString &fname); void hash_save_keyval (const QString &fname, const QHash &h); /* image utils */ bool is_image (const QString &filename); QString get_insert_image (const QString &file_name, const QString &full_path, const QString &markup_mode); /* inlines */ inline int get_value (int total, int perc) { return static_cast (total * perc / 100); } inline double get_value (double total, double perc) { return (total * perc / 100); } inline double get_percent (double total, double value) { return (value / total) * 100; } inline float get_percent (float total, float value) { return (value / total) * 100; } inline bool is_dir (const QString &path) { return QFileInfo(path).isDir(); } inline QString get_file_path (const QString &fileName) { return QFileInfo (fileName).path(); } inline QString qstring_clear (const QString &s) { QString t = s; return t.remove ("&"); } #endif tea-qt-63.3.0/src/wavinfo.cpp000066400000000000000000000032321476733534200157600ustar00rootroot00000000000000#include #include #include #include "wavinfo.h" #include "utils.h" //no range check, use in wavinfo only bool str_check (char *s1, char *s2, int size) { for (int i = 0; i < size; i++) if (s1[i] != s2[i]) return false; return true; } bool CWavReader::get_info (const QString &fname) { QFile fl (fname); if (! fl.open(QIODevice::ReadOnly)) return false; QDataStream ds (&fl); t_wav_chunk_hdr hdr; while (! ds.atEnd()) { ds.readRawData ((char *)&hdr, sizeof (hdr)); if (str_check (hdr.chunk_id, (char*)"RIFF", 4)) { char riff_type[4]; ds.readRawData ((char *)&riff_type, sizeof (riff_type)); } else if (str_check (hdr.chunk_id, (char*)"fmt ", 4)) { ds.readRawData ((char *)&wav_chunk_fmt, sizeof (wav_chunk_fmt)); if (wav_chunk_fmt.bits_per_sample != 16) return false; } else if (str_check (hdr.chunk_id, (char*)"data", 4)) { int nsamples = hdr.chunk_size / (wav_chunk_fmt.bits_per_sample / 8); double sqr_sum = 0.0; qint16 *ch_both = new qint16[nsamples]; ds.readRawData ((char *)ch_both, hdr.chunk_size); for (int i = 0; i < nsamples; i++) sqr_sum += (ch_both[i] * ch_both[i]); double srms = sqrt (sqr_sum / nsamples); rms = 20 * log10 (srms / 32767); delete [] ch_both; return true; } else ds.skipRawData (hdr.chunk_size); } return false; } tea-qt-63.3.0/src/wavinfo.h000066400000000000000000000015011476733534200154220ustar00rootroot00000000000000#ifndef WAVINFO_H #define WAVINFO_H #include #include #include typedef struct { char chunk_id[4]; quint32 chunk_size; } t_wav_chunk_hdr; typedef struct { quint16 format; // = 1 (PCM/uncompressed) quint16 num_channels; // mono = 1, stereo = 2, etc quint32 sample_rate; // 8000, 44100, 48000 etc quint32 byte_rate; // SampleRate * NumChannels * BitsPerSample/8 quint16 block_align; // NumChannels * BitsPerSample / 8 quint16 bits_per_sample; //8, 16 etc } t_wav_chunk_fmt; class CWavReader: public QObject { public: t_wav_chunk_fmt wav_chunk_fmt; double rms; bool get_info (const QString &fname); }; #endif // WAVINFO_H tea-qt-63.3.0/src/zip.c000066400000000000000000001551251476733534200145620ustar00rootroot00000000000000/* * 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 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. */ #define __STDC_WANT_LIB_EXT1__ 1 #include #include #include #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) /* Win32, DOS, MSVC, MSVS */ #include #define HAS_DEVICE(P) \ ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ (P)[1] == ':') #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) #else #include // needed for symlink() #endif #ifdef __MINGW32__ #include #include #endif #include "miniz.h" #include "zip.h" #ifdef _MSC_VER #include #define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) #define fileno _fileno #endif #if defined(__TINYC__) && (defined(_WIN32) || defined(_WIN64)) #include #define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) #define fileno _fileno #endif #ifndef HAS_DEVICE #define HAS_DEVICE(P) 0 #endif #ifndef FILESYSTEM_PREFIX_LEN #define FILESYSTEM_PREFIX_LEN(P) 0 #endif #ifndef ISSLASH #define ISSLASH(C) ((C) == '/' || (C) == '\\') #endif #define CLEANUP(ptr) \ do { \ if (ptr) { \ free((void *)ptr); \ ptr = NULL; \ } \ } while (0) #define UNX_IFDIR 0040000 /* Unix directory */ #define UNX_IFREG 0100000 /* Unix regular file */ #define UNX_IFSOCK 0140000 /* Unix socket (BSD, not SysV or Amiga) */ #define UNX_IFLNK 0120000 /* Unix symbolic link (not SysV, Amiga) */ #define UNX_IFBLK 0060000 /* Unix block special (not Amiga) */ #define UNX_IFCHR 0020000 /* Unix character special (not Amiga) */ #define UNX_IFIFO 0010000 /* Unix fifo (BCC, not MSC or Amiga) */ struct zip_entry_t { ssize_t index; char *name; mz_uint64 uncomp_size; mz_uint64 comp_size; mz_uint32 uncomp_crc32; mz_uint64 dir_offset; mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; mz_uint64 header_offset; mz_uint16 method; mz_zip_writer_add_state state; tdefl_compressor comp; mz_uint32 external_attr; time_t m_time; }; struct zip_t { mz_zip_archive archive; mz_uint level; struct zip_entry_t entry; }; enum zip_modify_t { MZ_KEEP = 0, MZ_DELETE = 1, MZ_MOVE = 2, }; struct zip_entry_mark_t { ssize_t file_index; enum zip_modify_t type; mz_uint64 m_local_header_ofs; size_t lf_length; }; static const char *const zip_errlist[33] = { NULL, "not initialized\0", "invalid entry name\0", "entry not found\0", "invalid zip mode\0", "invalid compression level\0", "no zip 64 support\0", "memset error\0", "cannot write data to entry\0", "cannot initialize tdefl compressor\0", "invalid index\0", "header not found\0", "cannot flush tdefl buffer\0", "cannot write entry header\0", "cannot create entry header\0", "cannot write to central dir\0", "cannot open file\0", "invalid entry type\0", "extracting data using no memory allocation\0", "file not found\0", "no permission\0", "out of memory\0", "invalid zip archive name\0", "make dir error\0", "symlink error\0", "close archive error\0", "capacity size too small\0", "fseek error\0", "fread error\0", "fwrite error\0", "cannot initialize reader\0", "cannot initialize writer\0", "cannot initialize writer from reader\0", }; const char *zip_strerror(int errnum) { errnum = -errnum; if (errnum <= 0 || errnum >= 33) { return NULL; } return zip_errlist[errnum]; } static const char *zip_basename(const char *name) { char const *p; char const *base = name += FILESYSTEM_PREFIX_LEN(name); int all_slashes = 1; for (p = name; *p; p++) { if (ISSLASH(*p)) base = p + 1; else all_slashes = 0; } /* If NAME is all slashes, arrange to return `/'. */ if (*base == '\0' && ISSLASH(*name) && all_slashes) --base; return base; } static int zip_mkpath(char *path) { char *p; char npath[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1]; int len = 0; int has_device = HAS_DEVICE(path); memset(npath, 0, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1); if (has_device) { // only on windows npath[0] = path[0]; npath[1] = path[1]; len = 2; } for (p = path + len; *p && len < MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE; p++) { if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) #else if ('\\' == *p) { *p = '/'; } #endif if (MZ_MKDIR(npath) == -1) { if (errno != EEXIST) { return ZIP_EMKDIR; } } } npath[len++] = *p; } return 0; } static char *zip_strclone(const char *str, size_t n) { char c; size_t i; char *rpl = (char *)calloc((1 + n), sizeof(char)); char *begin = rpl; if (!rpl) { return NULL; } for (i = 0; (i < n) && (c = *str++); ++i) { *rpl++ = c; } return begin; } static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) { char c; size_t i; char *rpl = (char *)calloc((1 + n), sizeof(char)); char *begin = rpl; if (!rpl) { return NULL; } for (i = 0; (i < n) && (c = *str++); ++i) { if (c == oldchar) { c = newchar; } *rpl++ = c; } return begin; } static inline int zip_strchr_match(const char *const str, size_t len, char c) { size_t i; for (i = 0; i < len; ++i) { if (str[i] != c) { return 0; } } return 1; } static char *zip_name_normalize(char *name, char *const nname, size_t len) { size_t offn = 0, ncpy = 0; char c; if (name == NULL || nname == NULL || len <= 0) { return NULL; } // skip trailing '/' while (ISSLASH(*name)) { name++; } while ((c = *name++)) { if (ISSLASH(c)) { if (ncpy > 0 && !zip_strchr_match(&nname[offn], ncpy, '.')) { offn += ncpy; nname[offn++] = c; // append '/' } ncpy = 0; } else { nname[offn + ncpy] = c; if (c) { ncpy++; } } } if (!zip_strchr_match(&nname[offn], ncpy, '.')) { nname[offn + ncpy] = '\0'; } else { nname[offn] = '\0'; } return nname; } static int zip_archive_truncate(mz_zip_archive *pzip) { mz_zip_internal_state *pState = pzip->m_pState; mz_uint64 file_size = pzip->m_archive_size; if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { return 0; } if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { if (pState->m_pFile) { int fd = fileno(pState->m_pFile); return ftruncate(fd, file_size); } } return 0; } static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { int err = 0; mz_uint i, n; char path[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1]; char symlink_to[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1]; mz_zip_archive_file_stat info; size_t dirlen = 0, filename_size = MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE; mz_uint32 xattr = 0; memset(path, 0, sizeof(path)); memset(symlink_to, 0, sizeof(symlink_to)); dirlen = strlen(dir); if (dirlen + 1 > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE) { return ZIP_EINVENTNAME; } memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); #if defined(_MSC_VER) strcpy_s(path, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, dir); #else strncpy(path, dir, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE); #endif if (!ISSLASH(path[dirlen - 1])) { #if defined(_WIN32) || defined(__WIN32__) path[dirlen] = '\\'; #else path[dirlen] = '/'; #endif ++dirlen; } if (filename_size > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - dirlen) { filename_size = MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - dirlen; } // Get and print information about each file in the archive. n = mz_zip_reader_get_num_files(zip_archive); for (i = 0; i < n; ++i) { if (!mz_zip_reader_file_stat(zip_archive, i, &info)) { // Cannot get information about zip archive; err = ZIP_ENOENT; goto out; } if (!zip_name_normalize(info.m_filename, info.m_filename, strlen(info.m_filename))) { // Cannot normalize file name; err = ZIP_EINVENTNAME; goto out; } #if defined(_MSC_VER) strncpy_s(&path[dirlen], filename_size, info.m_filename, filename_size); #else strncpy(&path[dirlen], info.m_filename, filename_size); #endif err = zip_mkpath(path); if (err < 0) { // Cannot make a path goto out; } if ((((info.m_version_made_by >> 8) == 3) || ((info.m_version_made_by >> 8) == 19)) // if zip is produced on Unix or macOS (3 and 19 from // section 4.4.2.2 of zip standard) && info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 // is directory) #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) #else if (info.m_uncomp_size > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE || !mz_zip_reader_extract_to_mem_no_alloc( zip_archive, i, symlink_to, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, 0, NULL, 0)) { err = ZIP_EMEMNOALLOC; goto out; } symlink_to[info.m_uncomp_size] = '\0'; if (symlink(symlink_to, path) != 0) { err = ZIP_ESYMLINK; goto out; } #endif } else { if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) { if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) { // Cannot extract zip archive to file err = ZIP_ENOFILE; goto out; } } #if defined(_MSC_VER) || defined(PS4) (void)xattr; // unused #else xattr = (info.m_external_attr >> 16) & 0xFFFF; if (xattr > 0 && xattr <= MZ_UINT16_MAX) { if (CHMOD(path, (mode_t)xattr) < 0) { err = ZIP_ENOPERM; goto out; } } #endif } if (on_extract) { if (on_extract(path, arg) < 0) { goto out; } } } out: // Close the archive, freeing any resources it was using if (!mz_zip_reader_end(zip_archive)) { // Cannot end zip reader err = ZIP_ECLSZIP; } return err; } static inline void zip_archive_finalize(mz_zip_archive *pzip) { mz_zip_writer_finalize_archive(pzip); zip_archive_truncate(pzip); } static ssize_t zip_entry_mark(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, const ssize_t n, char *const entries[], const size_t len) { ssize_t i = 0; ssize_t err = 0; if (!zip || !entry_mark || !entries) { return ZIP_ENOINIT; } mz_zip_archive_file_stat file_stat; mz_uint64 d_pos = UINT64_MAX; for (i = 0; i < n; ++i) { if ((err = zip_entry_openbyindex(zip, i))) { return (ssize_t)err; } mz_bool name_matches = MZ_FALSE; { size_t j; for (j = 0; j < len; ++j) { if (strcmp(zip->entry.name, entries[j]) == 0) { name_matches = MZ_TRUE; break; } } } if (name_matches) { entry_mark[i].type = MZ_DELETE; } else { entry_mark[i].type = MZ_KEEP; } if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { return ZIP_ENOENT; } zip_entry_close(zip); entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; entry_mark[i].file_index = (ssize_t)-1; entry_mark[i].lf_length = 0; if ((entry_mark[i].type) == MZ_DELETE && (d_pos > entry_mark[i].m_local_header_ofs)) { d_pos = entry_mark[i].m_local_header_ofs; } } for (i = 0; i < n; ++i) { if ((entry_mark[i].m_local_header_ofs > d_pos) && (entry_mark[i].type != MZ_DELETE)) { entry_mark[i].type = MZ_MOVE; } } return err; } static ssize_t zip_entry_markbyindex(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, const ssize_t n, size_t entries[], const size_t len) { ssize_t i = 0; ssize_t err = 0; if (!zip || !entry_mark || !entries) { return ZIP_ENOINIT; } mz_zip_archive_file_stat file_stat; mz_uint64 d_pos = UINT64_MAX; for (i = 0; i < n; ++i) { if ((err = zip_entry_openbyindex(zip, i))) { return (ssize_t)err; } mz_bool matches = MZ_FALSE; { size_t j; for (j = 0; j < len; ++j) { if ((size_t)i == entries[j]) { matches = MZ_TRUE; break; } } } if (matches) { entry_mark[i].type = MZ_DELETE; } else { entry_mark[i].type = MZ_KEEP; } if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { return ZIP_ENOENT; } zip_entry_close(zip); entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; entry_mark[i].file_index = (ssize_t)-1; entry_mark[i].lf_length = 0; if ((entry_mark[i].type) == MZ_DELETE && (d_pos > entry_mark[i].m_local_header_ofs)) { d_pos = entry_mark[i].m_local_header_ofs; } } for (i = 0; i < n; ++i) { if ((entry_mark[i].m_local_header_ofs > d_pos) && (entry_mark[i].type != MZ_DELETE)) { entry_mark[i].type = MZ_MOVE; } } return err; } static ssize_t zip_index_next(mz_uint64 *local_header_ofs_array, ssize_t cur_index) { ssize_t new_index = 0, i; for (i = cur_index - 1; i >= 0; --i) { if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) { new_index = i + 1; return new_index; } } return new_index; } static ssize_t zip_sort(mz_uint64 *local_header_ofs_array, ssize_t cur_index) { ssize_t nxt_index = zip_index_next(local_header_ofs_array, cur_index); if (nxt_index != cur_index) { mz_uint64 temp = local_header_ofs_array[cur_index]; ssize_t i; for (i = cur_index; i > nxt_index; i--) { local_header_ofs_array[i] = local_header_ofs_array[i - 1]; } local_header_ofs_array[nxt_index] = temp; } return nxt_index; } static int zip_index_update(struct zip_entry_mark_t *entry_mark, ssize_t last_index, ssize_t nxt_index) { ssize_t j; for (j = 0; j < last_index; j++) { if (entry_mark[j].file_index >= nxt_index) { entry_mark[j].file_index += 1; } } entry_mark[nxt_index].file_index = last_index; return 0; } static int zip_entry_finalize(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, const ssize_t n) { ssize_t i = 0; mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); if (!local_header_ofs_array) { return ZIP_EOOMEM; } for (i = 0; i < n; ++i) { local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs; ssize_t index = zip_sort(local_header_ofs_array, i); if (index != i) { zip_index_update(entry_mark, i, index); } entry_mark[i].file_index = index; } size_t *length = (size_t *)calloc(n, sizeof(size_t)); if (!length) { CLEANUP(local_header_ofs_array); return ZIP_EOOMEM; } for (i = 0; i < n - 1; i++) { length[i] = (size_t)(local_header_ofs_array[i + 1] - local_header_ofs_array[i]); } length[n - 1] = (size_t)(zip->archive.m_archive_size - local_header_ofs_array[n - 1]); for (i = 0; i < n; i++) { entry_mark[i].lf_length = length[entry_mark[i].file_index]; } CLEANUP(length); CLEANUP(local_header_ofs_array); return 0; } static ssize_t zip_entry_set(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, ssize_t n, char *const entries[], const size_t len) { ssize_t err = 0; if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) { return err; } if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { return err; } return 0; } static ssize_t zip_entry_setbyindex(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, ssize_t n, size_t entries[], const size_t len) { ssize_t err = 0; if ((err = zip_entry_markbyindex(zip, entry_mark, n, entries, len)) < 0) { return err; } if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { return err; } return 0; } static ssize_t zip_mem_move(void *pBuf, size_t bufSize, const mz_uint64 to, const mz_uint64 from, const size_t length) { uint8_t *dst = NULL, *src = NULL, *end = NULL; if (!pBuf) { return ZIP_EINVIDX; } end = (uint8_t *)pBuf + bufSize; if (to > bufSize) { return ZIP_EINVIDX; } if (from > bufSize) { return ZIP_EINVIDX; } dst = (uint8_t *)pBuf + to; src = (uint8_t *)pBuf + from; if (((dst + length) > end) || ((src + length) > end)) { return ZIP_EINVIDX; } memmove(dst, src, length); return length; } static ssize_t zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to, const mz_uint64 from, const size_t length, mz_uint8 *move_buf, const size_t capacity_size) { if (length > capacity_size) { return ZIP_ECAPSIZE; } if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) { return ZIP_EFSEEK; } if (fread(move_buf, 1, length, m_pFile) != length) { return ZIP_EFREAD; } if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) { return ZIP_EFSEEK; } if (fwrite(move_buf, 1, length, m_pFile) != length) { return ZIP_EFWRITE; } return (ssize_t)length; } static ssize_t zip_files_move(struct zip_t *zip, mz_uint64 writen_num, mz_uint64 read_num, size_t length) { ssize_t n = 0; const size_t page_size = 1 << 12; // 4K mz_zip_internal_state *pState = zip->archive.m_pState; mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size); if (!move_buf) { return ZIP_EOOMEM; } ssize_t moved_length = 0; ssize_t move_count = 0; while ((mz_int64)length > 0) { move_count = (length >= page_size) ? page_size : length; if (pState->m_pFile) { n = zip_file_move(pState->m_pFile, writen_num, read_num, move_count, move_buf, page_size); } else if (pState->m_pMem) { n = zip_mem_move(pState->m_pMem, pState->m_mem_size, writen_num, read_num, move_count); } else { return ZIP_ENOFILE; } if (n < 0) { moved_length = n; goto cleanup; } if (n != move_count) { goto cleanup; } writen_num += move_count; read_num += move_count; length -= move_count; moved_length += move_count; } cleanup: CLEANUP(move_buf); return moved_length; } static int zip_central_dir_move(mz_zip_internal_state *pState, int begin, int end, int entry_num) { if (begin == entry_num) { return 0; } size_t l_size = 0; size_t r_size = 0; mz_uint32 d_size = 0; mz_uint8 *next = NULL; mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin)); l_size = (size_t)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p)); if (end == entry_num) { r_size = 0; } else { next = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end)); r_size = pState->m_central_dir.m_size - (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p)); d_size = (mz_uint32)(next - deleted); } if (next && l_size == 0) { memmove(pState->m_central_dir.m_p, next, r_size); pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size); { int i; for (i = end; i < entry_num; i++) { MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= d_size; } } } if (next && l_size * r_size != 0) { memmove(deleted, next, r_size); { int i; for (i = end; i < entry_num; i++) { MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -= d_size; } } } pState->m_central_dir.m_size = l_size + r_size; return 0; } static int zip_central_dir_delete(mz_zip_internal_state *pState, int *deleted_entry_index_array, int entry_num) { int i = 0; int begin = 0; int end = 0; int d_num = 0; while (i < entry_num) { while ((i < entry_num) && (!deleted_entry_index_array[i])) { i++; } begin = i; while ((i < entry_num) && (deleted_entry_index_array[i])) { i++; } end = i; zip_central_dir_move(pState, begin, end, entry_num); } i = 0; while (i < entry_num) { while ((i < entry_num) && (!deleted_entry_index_array[i])) { i++; } begin = i; if (begin == entry_num) { break; } while ((i < entry_num) && (deleted_entry_index_array[i])) { i++; } end = i; int k = 0, j; for (j = end; j < entry_num; j++) { MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin + k) = (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, j); k++; } d_num += end - begin; } pState->m_central_dir_offsets.m_size = sizeof(mz_uint32) * (entry_num - d_num); return 0; } static ssize_t zip_entries_delete_mark(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, int entry_num) { mz_uint64 writen_num = 0; mz_uint64 read_num = 0; size_t deleted_length = 0; size_t move_length = 0; int i = 0; size_t deleted_entry_num = 0; ssize_t n = 0; mz_bool *deleted_entry_flag_array = (mz_bool *)calloc(entry_num, sizeof(mz_bool)); if (deleted_entry_flag_array == NULL) { return ZIP_EOOMEM; } mz_zip_internal_state *pState = zip->archive.m_pState; zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING; if (pState->m_pFile) { if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) { CLEANUP(deleted_entry_flag_array); return ZIP_ENOENT; } } while (i < entry_num) { while ((i < entry_num) && (entry_mark[i].type == MZ_KEEP)) { writen_num += entry_mark[i].lf_length; read_num = writen_num; i++; } while ((i < entry_num) && (entry_mark[i].type == MZ_DELETE)) { deleted_entry_flag_array[i] = MZ_TRUE; read_num += entry_mark[i].lf_length; deleted_length += entry_mark[i].lf_length; i++; deleted_entry_num++; } while ((i < entry_num) && (entry_mark[i].type == MZ_MOVE)) { move_length += entry_mark[i].lf_length; mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT( &pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i)); if (!p) { CLEANUP(deleted_entry_flag_array); return ZIP_ENOENT; } mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); offset -= (mz_uint32)deleted_length; MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset); i++; } n = zip_files_move(zip, writen_num, read_num, move_length); if (n != (ssize_t)move_length) { CLEANUP(deleted_entry_flag_array); return n; } writen_num += move_length; read_num += move_length; } zip->archive.m_archive_size -= (mz_uint64)deleted_length; zip->archive.m_total_files = (mz_uint32)entry_num - (mz_uint32)deleted_entry_num; zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num); CLEANUP(deleted_entry_flag_array); return (ssize_t)deleted_entry_num; } struct zip_t *zip_open(const char *zipname, int level, char mode) { int errnum = 0; return zip_openwitherror(zipname, level, mode, &errnum); } struct zip_t *zip_openwitherror(const char *zipname, int level, char mode, int *errnum) { struct zip_t *zip = NULL; *errnum = 0; if (!zipname || strlen(zipname) < 1) { // zip_t archive name is empty or NULL *errnum = ZIP_EINVZIPNAME; goto cleanup; } if (level < 0) level = MZ_DEFAULT_LEVEL; if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level *errnum = ZIP_EINVLVL; goto cleanup; } zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); if (!zip) { // out of memory *errnum = ZIP_EOOMEM; goto cleanup; } zip->level = (mz_uint)level; zip->entry.index = -1; switch (mode) { case 'w': // Create a new archive. if (!mz_zip_writer_init_file_v2(&(zip->archive), zipname, 0, MZ_ZIP_FLAG_WRITE_ZIP64)) { // Cannot initialize zip_archive writer *errnum = ZIP_EWINIT; goto cleanup; } break; case 'r': if (!mz_zip_reader_init_file_v2( &(zip->archive), zipname, zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { // An archive file does not exist or cannot initialize // zip_archive reader *errnum = ZIP_ERINIT; goto cleanup; } break; case 'a': case 'd': if (!mz_zip_reader_init_file_v2_rpb( &(zip->archive), zipname, zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { // An archive file does not exist or cannot initialize // zip_archive reader *errnum = ZIP_ERINIT; goto cleanup; } if ((mode == 'a' || mode == 'd')) { if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), zipname, 0)) { *errnum = ZIP_EWRINIT; mz_zip_reader_end(&(zip->archive)); goto cleanup; } } break; default: *errnum = ZIP_EINVMODE; goto cleanup; } return zip; cleanup: CLEANUP(zip); return NULL; } void zip_close(struct zip_t *zip) { if (zip) { mz_zip_archive *pZip = &(zip->archive); // Always finalize, even if adding failed for some reason, so we have a // valid central directory. if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) { mz_zip_writer_finalize_archive(pZip); } if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING || pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { zip_archive_truncate(pZip); mz_zip_writer_end(pZip); } if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) { mz_zip_reader_end(pZip); } CLEANUP(zip); } } int zip_is64(struct zip_t *zip) { if (!zip || !zip->archive.m_pState) { // zip_t handler or zip state is not initialized return ZIP_ENOINIT; } return (int)zip->archive.m_pState->m_zip64; } static int _zip_entry_open(struct zip_t *zip, const char *entryname, int case_sensitive) { size_t entrylen = 0; mz_zip_archive *pzip = NULL; mz_uint num_alignment_padding_bytes, level; mz_zip_archive_file_stat stats; int err = 0; mz_uint16 dos_time = 0, dos_date = 0; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint64 local_dir_header_ofs = 0; if (!zip) { return ZIP_ENOINIT; } local_dir_header_ofs = zip->archive.m_archive_size; if (!entryname) { return ZIP_EINVENTNAME; } entrylen = strlen(entryname); if (entrylen == 0) { return ZIP_EINVENTNAME; } if (zip->entry.name) { CLEANUP(zip->entry.name); } pzip = &(zip->archive); if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { zip->entry.name = zip_strclone(entryname, entrylen); if (!zip->entry.name) { // Cannot parse zip entry name return ZIP_EINVENTNAME; } zip->entry.index = (ssize_t)mz_zip_reader_locate_file( pzip, zip->entry.name, NULL, case_sensitive ? MZ_ZIP_FLAG_CASE_SENSITIVE : 0); if (zip->entry.index < (ssize_t)0) { err = ZIP_ENOENT; goto cleanup; } if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) { err = ZIP_ENOENT; goto cleanup; } zip->entry.comp_size = stats.m_comp_size; zip->entry.uncomp_size = stats.m_uncomp_size; zip->entry.uncomp_crc32 = stats.m_crc32; zip->entry.dir_offset = stats.m_central_dir_ofs; zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; #ifndef MINIZ_NO_TIME zip->entry.m_time = stats.m_time; #endif return 0; } /* .ZIP File Format Specification Version: 6.3.3 4.4.17.1 The name of the file, with optional relative path. The path stored MUST not contain a drive or device letter, or a leading slash. All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility with Amiga and UNIX file systems etc. If input came from standard input, there is no file name field. */ zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/'); if (!zip->entry.name) { // Cannot parse zip entry name return ZIP_EINVENTNAME; } level = zip->level & 0xF; zip->entry.index = (ssize_t)zip->archive.m_total_files; zip->entry.comp_size = 0; zip->entry.uncomp_size = 0; zip->entry.uncomp_crc32 = MZ_CRC32_INIT; zip->entry.dir_offset = zip->archive.m_archive_size; zip->entry.header_offset = zip->archive.m_archive_size; memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); zip->entry.method = level ? MZ_DEFLATED : 0; // UNIX or APPLE #if MZ_PLATFORM == 3 || MZ_PLATFORM == 19 // regular file with rw-r--r-- permissions zip->entry.external_attr = (mz_uint32)(0100644) << 16; #else zip->entry.external_attr = 0; #endif num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { // Invalid zip mode err = ZIP_EINVMODE; goto cleanup; } if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { // Invalid zip compression level err = ZIP_EINVLVL; goto cleanup; } if (!mz_zip_writer_write_zeros(pzip, zip->entry.dir_offset, num_alignment_padding_bytes)) { // Cannot memset zip entry header err = ZIP_EMEMSET; goto cleanup; } local_dir_header_ofs += num_alignment_padding_bytes; zip->entry.m_time = time(NULL); #ifndef MINIZ_NO_TIME mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); #endif // ZIP64 header with NULL sizes (sizes will be in the data descriptor, just // after file data) extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, NULL, NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); if (!mz_zip_writer_create_local_dir_header( pzip, zip->entry.header, entrylen, (mz_uint16)extra_size, 0, 0, 0, zip->entry.method, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 | MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR, dos_time, dos_date)) { // Cannot create zip entry header err = ZIP_EMEMSET; goto cleanup; } zip->entry.header_offset = zip->entry.dir_offset + num_alignment_padding_bytes; if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, zip->entry.header, sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { // Cannot write zip entry header err = ZIP_EMEMSET; goto cleanup; } if (pzip->m_file_offset_alignment) { MZ_ASSERT( (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0); } zip->entry.dir_offset += num_alignment_padding_bytes + sizeof(zip->entry.header); if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, zip->entry.name, entrylen) != entrylen) { // Cannot write data to zip entry err = ZIP_EWRTENT; goto cleanup; } zip->entry.dir_offset += entrylen; if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, extra_data, extra_size) != extra_size) { // Cannot write ZIP64 data to zip entry err = ZIP_EWRTENT; goto cleanup; } zip->entry.dir_offset += extra_size; if (level) { zip->entry.state.m_pZip = pzip; zip->entry.state.m_cur_archive_file_ofs = zip->entry.dir_offset; zip->entry.state.m_comp_size = 0; if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, &(zip->entry.state), (int)tdefl_create_comp_flags_from_zip_params( (int)level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { // Cannot initialize the zip compressor err = ZIP_ETDEFLINIT; goto cleanup; } } return 0; cleanup: CLEANUP(zip->entry.name); return err; } int zip_entry_open(struct zip_t *zip, const char *entryname) { return _zip_entry_open(zip, entryname, 0); } int zip_entry_opencasesensitive(struct zip_t *zip, const char *entryname) { return _zip_entry_open(zip, entryname, 1); } int zip_entry_openbyindex(struct zip_t *zip, size_t index) { mz_zip_archive *pZip = NULL; mz_zip_archive_file_stat stats; mz_uint namelen; const mz_uint8 *pHeader; const char *pFilename; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } pZip = &(zip->archive); if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) { // open by index requires readonly mode return ZIP_EINVMODE; } if (index >= (size_t)pZip->m_total_files) { // index out of range return ZIP_EINVIDX; } if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, index)))) { // cannot find header in central directory return ZIP_ENOHDR; } namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (zip->entry.name) { CLEANUP(zip->entry.name); } zip->entry.name = zip_strclone(pFilename, namelen); if (!zip->entry.name) { // local entry name is NULL return ZIP_EINVENTNAME; } if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) { return ZIP_ENOENT; } zip->entry.index = (ssize_t)index; zip->entry.comp_size = stats.m_comp_size; zip->entry.uncomp_size = stats.m_uncomp_size; zip->entry.uncomp_crc32 = stats.m_crc32; zip->entry.dir_offset = stats.m_central_dir_ofs; zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; #ifndef MINIZ_NO_TIME zip->entry.m_time = stats.m_time; #endif return 0; } int zip_entry_close(struct zip_t *zip) { mz_zip_archive *pzip = NULL; mz_uint level; tdefl_status done; mz_uint16 entrylen; mz_uint16 dos_time = 0, dos_date = 0; int err = 0; mz_uint8 *pExtra_data = NULL; mz_uint32 extra_size = 0; mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; if (!zip) { // zip_t handler is not initialized err = ZIP_ENOINIT; goto cleanup; } pzip = &(zip->archive); if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { goto cleanup; } level = zip->level & 0xF; if (level) { done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { // Cannot flush compressed buffer err = ZIP_ETDEFLBUF; goto cleanup; } zip->entry.comp_size = zip->entry.state.m_comp_size; zip->entry.dir_offset = zip->entry.state.m_cur_archive_file_ofs; zip->entry.method = MZ_DEFLATED; } entrylen = (mz_uint16)strlen(zip->entry.name); #ifndef MINIZ_NO_TIME mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); #endif MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); MZ_WRITE_LE32(local_dir_footer + 4, zip->entry.uncomp_crc32); MZ_WRITE_LE64(local_dir_footer + 8, zip->entry.comp_size); MZ_WRITE_LE64(local_dir_footer + 16, zip->entry.uncomp_size); if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) { // Cannot write zip entry header err = ZIP_EWRTHDR; goto cleanup; } zip->entry.dir_offset += local_dir_footer_size; pExtra_data = extra_data; extra_size = mz_zip_writer_create_zip64_extra_data( extra_data, (zip->entry.uncomp_size >= MZ_UINT32_MAX) ? &zip->entry.uncomp_size : NULL, (zip->entry.comp_size >= MZ_UINT32_MAX) ? &zip->entry.comp_size : NULL, (zip->entry.header_offset >= MZ_UINT32_MAX) ? &zip->entry.header_offset : NULL); if ((entrylen) && ISSLASH(zip->entry.name[entrylen - 1]) && !zip->entry.uncomp_size) { /* Set DOS Subdirectory attribute bit. */ zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; } if (!mz_zip_writer_add_to_central_dir( pzip, zip->entry.name, entrylen, pExtra_data, (mz_uint16)extra_size, "", 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 | MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR, dos_time, dos_date, zip->entry.header_offset, zip->entry.external_attr, NULL, 0)) { // Cannot write to zip central dir err = ZIP_EWRTDIR; goto cleanup; } pzip->m_total_files++; pzip->m_archive_size = zip->entry.dir_offset; cleanup: if (zip) { zip->entry.m_time = 0; zip->entry.index = -1; CLEANUP(zip->entry.name); } return err; } const char *zip_entry_name(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized return NULL; } return zip->entry.name; } ssize_t zip_entry_index(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized return (ssize_t)ZIP_ENOINIT; } return zip->entry.index; } int zip_entry_isdir(struct zip_t *zip) { mz_uint16 entrylen; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } if (zip->entry.index < (ssize_t)0) { // zip entry is not opened return ZIP_EINVIDX; } entrylen = (mz_uint16)strlen(zip->entry.name); return ISSLASH(zip->entry.name[entrylen - 1]); } unsigned long long zip_entry_size(struct zip_t *zip) { return zip_entry_uncomp_size(zip); } unsigned long long zip_entry_uncomp_size(struct zip_t *zip) { return zip ? zip->entry.uncomp_size : 0; } unsigned long long zip_entry_comp_size(struct zip_t *zip) { return zip ? zip->entry.comp_size : 0; } unsigned int zip_entry_crc32(struct zip_t *zip) { return zip ? zip->entry.uncomp_crc32 : 0; } unsigned long long zip_entry_dir_offset(struct zip_t *zip) { return zip ? zip->entry.dir_offset : 0; } unsigned long long zip_entry_header_offset(struct zip_t *zip) { return zip ? zip->entry.header_offset : 0; } int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { mz_uint level; mz_zip_archive *pzip = NULL; tdefl_status status; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } pzip = &(zip->archive); if (buf && bufsize > 0) { zip->entry.uncomp_size += bufsize; zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32( zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize); level = zip->level & 0xF; if (!level) { if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, buf, bufsize) != bufsize)) { // Cannot write buffer return ZIP_EWRTENT; } zip->entry.dir_offset += bufsize; zip->entry.comp_size += bufsize; } else { status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, TDEFL_NO_FLUSH); if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { // Cannot compress buffer return ZIP_ETDEFLBUF; } } } return 0; } int zip_entry_fwrite(struct zip_t *zip, const char *filename) { int err = 0; size_t n = 0; MZ_FILE *stream = NULL; mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE]; struct MZ_FILE_STAT_STRUCT file_stat; mz_uint16 modes; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE); memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); if (MZ_FILE_STAT(filename, &file_stat) != 0) { // problem getting information - check errno return ZIP_ENOENT; } #if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP) (void)modes; // unused #else /* Initialize with permission bits--which are not implementation-optional */ modes = file_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); if (S_ISDIR(file_stat.st_mode)) modes |= UNX_IFDIR; if (S_ISREG(file_stat.st_mode)) modes |= UNX_IFREG; if (S_ISLNK(file_stat.st_mode)) modes |= UNX_IFLNK; if (S_ISBLK(file_stat.st_mode)) modes |= UNX_IFBLK; if (S_ISCHR(file_stat.st_mode)) modes |= UNX_IFCHR; if (S_ISFIFO(file_stat.st_mode)) modes |= UNX_IFIFO; if (S_ISSOCK(file_stat.st_mode)) modes |= UNX_IFSOCK; zip->entry.external_attr = (modes << 16) | !(file_stat.st_mode & S_IWUSR); if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; } #endif zip->entry.m_time = file_stat.st_mtime; if (!(stream = MZ_FOPEN(filename, "rb"))) { // Cannot open filename return ZIP_EOPNFILE; } while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > 0) { if (zip_entry_write(zip, buf, n) < 0) { err = ZIP_EWRTENT; break; } } fclose(stream); return err; } ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { mz_zip_archive *pzip = NULL; mz_uint idx; size_t size = 0; if (!zip) { // zip_t handler is not initialized return (ssize_t)ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return (ssize_t)ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; if (mz_zip_reader_is_file_a_directory(pzip, idx)) { // the entry is a directory return (ssize_t)ZIP_EINVENTTYPE; } *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0); if (*buf && bufsize) { *bufsize = size; } return (ssize_t)size; } ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { mz_zip_archive *pzip = NULL; if (!zip) { // zip_t handler is not initialized return (ssize_t)ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return (ssize_t)ZIP_ENOENT; } if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, buf, bufsize, 0, NULL, 0)) { return (ssize_t)ZIP_EMEMNOALLOC; } return (ssize_t)zip->entry.uncomp_size; } int zip_entry_fread(struct zip_t *zip, const char *filename) { mz_zip_archive *pzip = NULL; mz_uint idx; mz_uint32 xattr = 0; mz_zip_archive_file_stat info; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; if (mz_zip_reader_is_file_a_directory(pzip, idx)) { // the entry is a directory return ZIP_EINVENTTYPE; } if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) { return ZIP_ENOFILE; } #if defined(_MSC_VER) || defined(PS4) (void)xattr; // unused #else if (!mz_zip_reader_file_stat(pzip, idx, &info)) { // Cannot get information about zip archive; return ZIP_ENOFILE; } xattr = (info.m_external_attr >> 16) & 0xFFFF; if (xattr > 0 && xattr <= MZ_UINT16_MAX) { if (CHMOD(filename, (mode_t)xattr) < 0) { return ZIP_ENOPERM; } } #endif return 0; } int zip_entry_extract(struct zip_t *zip, size_t (*on_extract)(void *arg, uint64_t offset, const void *buf, size_t bufsize), void *arg) { mz_zip_archive *pzip = NULL; mz_uint idx; if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < (ssize_t)0) { // the entry is not found or we do not have read access return ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) ? 0 : ZIP_EINVIDX; } ssize_t zip_entries_total(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized return ZIP_ENOINIT; } return (ssize_t)zip->archive.m_total_files; } ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[], size_t len) { ssize_t n = 0; ssize_t err = 0; struct zip_entry_mark_t *entry_mark = NULL; if (zip == NULL || (entries == NULL && len != 0)) { return ZIP_ENOINIT; } if (entries == NULL && len == 0) { return 0; } n = zip_entries_total(zip); entry_mark = (struct zip_entry_mark_t *)calloc( (size_t)n, sizeof(struct zip_entry_mark_t)); if (!entry_mark) { return ZIP_EOOMEM; } zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; err = zip_entry_set(zip, entry_mark, n, entries, len); if (err < 0) { CLEANUP(entry_mark); return err; } err = zip_entries_delete_mark(zip, entry_mark, (int)n); CLEANUP(entry_mark); return err; } ssize_t zip_entries_deletebyindex(struct zip_t *zip, size_t entries[], size_t len) { ssize_t n = 0; ssize_t err = 0; struct zip_entry_mark_t *entry_mark = NULL; if (zip == NULL || (entries == NULL && len != 0)) { return ZIP_ENOINIT; } if (entries == NULL && len == 0) { return 0; } n = zip_entries_total(zip); entry_mark = (struct zip_entry_mark_t *)calloc( (size_t)n, sizeof(struct zip_entry_mark_t)); if (!entry_mark) { return ZIP_EOOMEM; } zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; err = zip_entry_setbyindex(zip, entry_mark, n, entries, len); if (err < 0) { CLEANUP(entry_mark); return err; } err = zip_entries_delete_mark(zip, entry_mark, (int)n); CLEANUP(entry_mark); return err; } int zip_stream_extract(const char *stream, size_t size, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { mz_zip_archive zip_archive; if (!stream || !dir) { // Cannot parse zip archive stream return ZIP_ENOINIT; } if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { // Cannot memset zip archive return ZIP_EMEMSET; } if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) { // Cannot initialize zip_archive reader return ZIP_ENOINIT; } return zip_archive_extract(&zip_archive, dir, on_extract, arg); } struct zip_t *zip_stream_open(const char *stream, size_t size, int level, char mode) { int errnum = 0; return zip_stream_openwitherror(stream, size, level, mode, &errnum); } struct zip_t *zip_stream_openwitherror(const char *stream, size_t size, int level, char mode, int *errnum) { struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); if (!zip) { // out of memory *errnum = ZIP_EOOMEM; return NULL; } if (level < 0) { level = MZ_DEFAULT_LEVEL; } if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level *errnum = ZIP_EINVLVL; goto cleanup; } zip->level = (mz_uint)level; if ((stream != NULL) && (size > 0) && (mode == 'r')) { if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) { *errnum = ZIP_ERINIT; goto cleanup; } } else if ((stream == NULL) && (size == 0) && (mode == 'w')) { // Create a new archive. if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) { // Cannot initialize zip_archive writer *errnum = ZIP_EWINIT; goto cleanup; } } else { *errnum = ZIP_EINVMODE; goto cleanup; } *errnum = 0; return zip; cleanup: CLEANUP(zip); return NULL; } ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize) { size_t n; if (!zip) { return (ssize_t)ZIP_ENOINIT; } zip_archive_finalize(&(zip->archive)); n = (size_t)zip->archive.m_archive_size; if (bufsize != NULL) { *bufsize = n; } *buf = calloc(sizeof(unsigned char), n); memcpy(*buf, zip->archive.m_pState->m_pMem, n); return (ssize_t)n; } void zip_stream_close(struct zip_t *zip) { if (zip) { mz_zip_writer_end(&(zip->archive)); mz_zip_reader_end(&(zip->archive)); CLEANUP(zip); } } struct zip_t *zip_cstream_open(FILE *stream, int level, char mode) { int errnum = 0; return zip_cstream_openwitherror(stream, level, mode, &errnum); } struct zip_t *zip_cstream_openwitherror(FILE *stream, int level, char mode, int *errnum) { struct zip_t *zip = NULL; *errnum = 0; if (!stream) { // zip archive stream is NULL *errnum = ZIP_ENOFILE; goto cleanup; } if (level < 0) level = MZ_DEFAULT_LEVEL; if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level *errnum = ZIP_EINVLVL; goto cleanup; } zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); if (!zip) { // out of memory *errnum = ZIP_EOOMEM; goto cleanup; } zip->level = (mz_uint)level; switch (mode) { case 'w': // Create a new archive. if (!mz_zip_writer_init_cfile(&(zip->archive), stream, MZ_ZIP_FLAG_WRITE_ZIP64)) { // Cannot initialize zip_archive writer *errnum = ZIP_EWINIT; goto cleanup; } break; case 'r': if (!mz_zip_reader_init_cfile( &(zip->archive), stream, 0, zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { // An archive file does not exist or cannot initialize // zip_archive reader *errnum = ZIP_ERINIT; goto cleanup; } break; case 'a': case 'd': if (!mz_zip_reader_init_cfile( &(zip->archive), stream, 0, zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { // An archive file does not exist or cannot initialize // zip_archive reader *errnum = ZIP_ERINIT; goto cleanup; } if ((mode == 'a' || mode == 'd')) { if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), NULL, 0)) { *errnum = ZIP_EWRINIT; mz_zip_reader_end(&(zip->archive)); goto cleanup; } } break; default: *errnum = ZIP_EINVMODE; goto cleanup; } return zip; cleanup: CLEANUP(zip); return NULL; } void zip_cstream_close(struct zip_t *zip) { zip_close(zip); } int zip_create(const char *zipname, const char *filenames[], size_t len) { int err = 0; size_t i; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; mz_uint32 ext_attributes = 0; mz_uint16 modes; if (!zipname || strlen(zipname) < 1) { // zip_t archive name is empty or NULL return ZIP_EINVZIPNAME; } // Create a new archive. if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { // Cannot memset zip archive return ZIP_EMEMSET; } if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { // Cannot initialize zip_archive writer return ZIP_ENOINIT; } if (!memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT))) { return ZIP_EMEMSET; } for (i = 0; i < len; ++i) { const char *name = filenames[i]; if (!name) { err = ZIP_EINVENTNAME; break; } if (MZ_FILE_STAT(name, &file_stat) != 0) { // problem getting information - check errno err = ZIP_ENOFILE; break; } #if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP) (void)modes; // unused #else /* Initialize with permission bits--which are not implementation-optional */ modes = file_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); if (S_ISDIR(file_stat.st_mode)) modes |= UNX_IFDIR; if (S_ISREG(file_stat.st_mode)) modes |= UNX_IFREG; if (S_ISLNK(file_stat.st_mode)) modes |= UNX_IFLNK; if (S_ISBLK(file_stat.st_mode)) modes |= UNX_IFBLK; if (S_ISCHR(file_stat.st_mode)) modes |= UNX_IFCHR; if (S_ISFIFO(file_stat.st_mode)) modes |= UNX_IFIFO; if (S_ISSOCK(file_stat.st_mode)) modes |= UNX_IFSOCK; ext_attributes = (modes << 16) | !(file_stat.st_mode & S_IWUSR); if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; } #endif if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0, ZIP_DEFAULT_COMPRESSION_LEVEL, ext_attributes)) { // Cannot add file to zip_archive err = ZIP_ENOFILE; break; } } mz_zip_writer_finalize_archive(&zip_archive); mz_zip_writer_end(&zip_archive); return err; } int zip_extract(const char *zipname, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { mz_zip_archive zip_archive; if (!zipname || !dir) { // Cannot parse zip archive name return ZIP_EINVZIPNAME; } if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { // Cannot memset zip archive return ZIP_EMEMSET; } // Now try to open the archive. if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { // Cannot initialize zip_archive reader return ZIP_ENOINIT; } return zip_archive_extract(&zip_archive, dir, on_extract, arg); } tea-qt-63.3.0/src/zip.h000066400000000000000000000457011476733534200145650ustar00rootroot00000000000000/* * 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 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. */ #pragma once #ifndef ZIP_H #define ZIP_H #include #include #include #include #ifndef ZIP_SHARED #define ZIP_EXPORT #else #ifdef _WIN32 #ifdef ZIP_BUILD_SHARED #define ZIP_EXPORT __declspec(dllexport) #else #define ZIP_EXPORT __declspec(dllimport) #endif #else #define ZIP_EXPORT __attribute__((visibility("default"))) #endif #endif #ifdef __cplusplus extern "C" { #endif #if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER) // 64-bit Windows is the only mainstream platform // where sizeof(long) != sizeof(void*) #ifdef _WIN64 typedef long long ssize_t; /* byte count or error */ #else typedef long ssize_t; /* byte count or error */ #endif #endif /** * @mainpage * * Documentation for @ref zip. */ /** * @addtogroup zip * @{ */ /** * Default zip compression level. */ #define ZIP_DEFAULT_COMPRESSION_LEVEL 6 /** * Error codes */ #define ZIP_ENOINIT -1 // not initialized #define ZIP_EINVENTNAME -2 // invalid entry name #define ZIP_ENOENT -3 // entry not found #define ZIP_EINVMODE -4 // invalid zip mode #define ZIP_EINVLVL -5 // invalid compression level #define ZIP_ENOSUP64 -6 // no zip 64 support #define ZIP_EMEMSET -7 // memset error #define ZIP_EWRTENT -8 // cannot write data to entry #define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor #define ZIP_EINVIDX -10 // invalid index #define ZIP_ENOHDR -11 // header not found #define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer #define ZIP_ECRTHDR -13 // cannot create entry header #define ZIP_EWRTHDR -14 // cannot write entry header #define ZIP_EWRTDIR -15 // cannot write to central dir #define ZIP_EOPNFILE -16 // cannot open file #define ZIP_EINVENTTYPE -17 // invalid entry type #define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation #define ZIP_ENOFILE -19 // file not found #define ZIP_ENOPERM -20 // no permission #define ZIP_EOOMEM -21 // out of memory #define ZIP_EINVZIPNAME -22 // invalid zip archive name #define ZIP_EMKDIR -23 // make dir error #define ZIP_ESYMLINK -24 // symlink error #define ZIP_ECLSZIP -25 // close archive error #define ZIP_ECAPSIZE -26 // capacity size too small #define ZIP_EFSEEK -27 // fseek error #define ZIP_EFREAD -28 // fread error #define ZIP_EFWRITE -29 // fwrite error #define ZIP_ERINIT -30 // cannot initialize reader #define ZIP_EWINIT -31 // cannot initialize writer #define ZIP_EWRINIT -32 // cannot initialize writer from reader /** * Looks up the error message string corresponding to an error number. * @param errnum error number * @return error message string corresponding to errnum or NULL if error is not * found. */ extern ZIP_EXPORT const char *zip_strerror(int errnum); /** * @struct zip_t * * This data structure is used throughout the library to represent zip archive - * forward declaration. */ struct zip_t; /** * Opens zip archive with compression level using the given mode. * * @param zipname zip archive file name. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t *zip_open(const char *zipname, int level, char mode); /** * Opens zip archive with compression level using the given mode. * The function additionally returns @param errnum - * * @param zipname zip archive file name. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * @param errnum 0 on success, negative number (< 0) on error. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t * zip_openwitherror(const char *zipname, int level, char mode, int *errnum); /** * Closes the zip archive, releases resources - always finalize. * * @param zip zip archive handler. */ extern ZIP_EXPORT void zip_close(struct zip_t *zip); /** * Determines if the archive has a zip64 end of central directory headers. * * @param zip zip archive handler. * * @return the return code - 1 (true), 0 (false), negative number (< 0) on * error. */ extern ZIP_EXPORT int zip_is64(struct zip_t *zip); /** * Opens an entry by name in the zip archive. * * For zip archive opened in 'w' or 'a' mode the function will append * a new entry. In readonly mode the function tries to locate the entry * in global dictionary. * * @param zip zip archive handler. * @param entryname an entry name in local dictionary. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_open(struct zip_t *zip, const char *entryname); /** * Opens an entry by name in the zip archive. * * For zip archive opened in 'w' or 'a' mode the function will append * a new entry. In readonly mode the function tries to locate the entry * in global dictionary (case sensitive). * * @param zip zip archive handler. * @param entryname an entry name in local dictionary (case sensitive). * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_opencasesensitive(struct zip_t *zip, const char *entryname); /** * Opens a new entry by index in the zip archive. * * This function is only valid if zip archive was opened in 'r' (readonly) mode. * * @param zip zip archive handler. * @param index index in local dictionary. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_openbyindex(struct zip_t *zip, size_t index); /** * Closes a zip entry, flushes buffer and releases resources. * * @param zip zip archive handler. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_close(struct zip_t *zip); /** * Returns a local name of the current zip entry. * * The main difference between user's entry name and local entry name * is optional relative path. * Following .ZIP File Format Specification - the path stored MUST not contain * a drive or device letter, or a leading slash. * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' * for compatibility with Amiga and UNIX file systems etc. * * @param zip: zip archive handler. * * @return the pointer to the current zip entry name, or NULL on error. */ extern ZIP_EXPORT const char *zip_entry_name(struct zip_t *zip); /** * Returns an index of the current zip entry. * * @param zip zip archive handler. * * @return the index on success, negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entry_index(struct zip_t *zip); /** * Determines if the current zip entry is a directory entry. * * @param zip zip archive handler. * * @return the return code - 1 (true), 0 (false), negative number (< 0) on * error. */ extern ZIP_EXPORT int zip_entry_isdir(struct zip_t *zip); /** * Returns the uncompressed size of the current zip entry. * Alias for zip_entry_uncomp_size (for backward compatibility). * * @param zip zip archive handler. * * @return the uncompressed size in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_size(struct zip_t *zip); /** * Returns the uncompressed size of the current zip entry. * * @param zip zip archive handler. * * @return the uncompressed size in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_uncomp_size(struct zip_t *zip); /** * Returns the compressed size of the current zip entry. * * @param zip zip archive handler. * * @return the compressed size in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_comp_size(struct zip_t *zip); /** * Returns CRC-32 checksum of the current zip entry. * * @param zip zip archive handler. * * @return the CRC-32 checksum. */ extern ZIP_EXPORT unsigned int zip_entry_crc32(struct zip_t *zip); /** * Returns byte offset of the current zip entry * in the archive's central directory. * * @param zip zip archive handler. * * @return the offset in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_dir_offset(struct zip_t *zip); /** * Returns the current zip entry's local header file offset in bytes. * * @param zip zip archive handler. * * @return the entry's local header file offset in bytes. */ extern ZIP_EXPORT unsigned long long zip_entry_header_offset(struct zip_t *zip); /** * Compresses an input buffer for the current zip entry. * * @param zip zip archive handler. * @param buf input buffer. * @param bufsize input buffer size (in bytes). * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); /** * Compresses a file for the current zip entry. * * @param zip zip archive handler. * @param filename input file. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_fwrite(struct zip_t *zip, const char *filename); /** * Extracts the current zip entry into output buffer. * * The function allocates sufficient memory for a output buffer. * * @param zip zip archive handler. * @param buf output buffer. * @param bufsize output buffer size (in bytes). * * @note remember to release memory allocated for a output buffer. * for large entries, please take a look at zip_entry_extract function. * * @return the return code - the number of bytes actually read on success. * Otherwise a negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); /** * Extracts the current zip entry into a memory buffer using no memory * allocation. * * @param zip zip archive handler. * @param buf preallocated output buffer. * @param bufsize output buffer size (in bytes). * * @note ensure supplied output buffer is large enough. * zip_entry_size function (returns uncompressed size for the current * entry) can be handy to estimate how big buffer is needed. * For large entries, please take a look at zip_entry_extract function. * * @return the return code - the number of bytes actually read on success. * Otherwise a negative number (< 0) on error (e.g. bufsize is not large * enough). */ extern ZIP_EXPORT ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize); /** * Extracts the current zip entry into output file. * * @param zip zip archive handler. * @param filename output file. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_fread(struct zip_t *zip, const char *filename); /** * Extracts the current zip entry using a callback function (on_extract). * * @param zip zip archive handler. * @param on_extract callback function. * @param arg opaque pointer (optional argument, which you can pass to the * on_extract callback) * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_entry_extract(struct zip_t *zip, size_t (*on_extract)(void *arg, uint64_t offset, const void *data, size_t size), void *arg); /** * Returns the number of all entries (files and directories) in the zip archive. * * @param zip zip archive handler. * * @return the return code - the number of entries on success, negative number * (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entries_total(struct zip_t *zip); /** * Deletes zip archive entries. * * @param zip zip archive handler. * @param entries array of zip archive entries to be deleted. * @param len the number of entries to be deleted. * @return the number of deleted entries, or negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[], size_t len); /** * Deletes zip archive entries. * * @param zip zip archive handler. * @param entries array of zip archive entries indices to be deleted. * @param len the number of entries to be deleted. * @return the number of deleted entries, or negative number (< 0) on error. */ extern ZIP_EXPORT ssize_t zip_entries_deletebyindex(struct zip_t *zip, size_t entries[], size_t len); /** * Extracts a zip archive stream into directory. * * If on_extract is not NULL, the callback will be called after * successfully extracted each zip entry. * Returning a negative value from the callback will cause abort and return an * error. The last argument (void *arg) is optional, which you can use to pass * data to the on_extract callback. * * @param stream zip archive stream. * @param size stream size. * @param dir output directory. * @param on_extract on extract callback. * @param arg opaque pointer. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_stream_extract(const char *stream, size_t size, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg); /** * Opens zip archive stream into memory. * * @param stream zip archive stream. * @param size stream size. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t *zip_stream_open(const char *stream, size_t size, int level, char mode); /** * Opens zip archive stream into memory. * The function additionally returns @param errnum - * * @param stream zip archive stream. * @param size stream size.* * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * @param errnum 0 on success, negative number (< 0) on error. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t *zip_stream_openwitherror(const char *stream, size_t size, int level, char mode, int *errnum); /** * Copy zip archive stream output buffer. * * @param zip zip archive handler. * @param buf output buffer. User should free buf. * @param bufsize output buffer size (in bytes). * * @return copy size */ extern ZIP_EXPORT ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize); /** * Close zip archive releases resources. * * @param zip zip archive handler. * * @return */ extern ZIP_EXPORT void zip_stream_close(struct zip_t *zip); /** * Opens zip archive from existing FILE stream with compression level using the * given mode. The stream will not be closed when calling zip_close. * * @param stream C FILE stream. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. This mode should be equivalent to the mode * provided when opening the file. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t *zip_cstream_open(FILE *stream, int level, char mode); /** * Opens zip archive from existing FILE stream with compression level using the * given mode. The function additionally returns @param errnum - The stream will * not be closed when calling zip_close. * * @param stream C FILE stream. * @param level compression level (0-9 are the standard zlib-style levels). * @param mode file access mode. * - 'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing. * - 'a': appends to an existing archive. * @param errnum 0 on success, negative number (< 0) on error. * * @return the zip archive handler or NULL on error */ extern ZIP_EXPORT struct zip_t * zip_cstream_openwitherror(FILE *stream, int level, char mode, int *errnum); /** * Closes the zip archive, releases resources - always finalize. * This function is an alias for zip_close function. * * @param zip zip archive handler. */ extern ZIP_EXPORT void zip_cstream_close(struct zip_t *zip); /** * Creates a new archive and puts files into a single zip archive. * * @param zipname zip archive file. * @param filenames input files. * @param len: number of input files. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_create(const char *zipname, const char *filenames[], size_t len); /** * Extracts a zip archive file into directory. * * If on_extract_entry is not NULL, the callback will be called after * successfully extracted each zip entry. * Returning a negative value from the callback will cause abort and return an * error. The last argument (void *arg) is optional, which you can use to pass * data to the on_extract_entry callback. * * @param zipname zip archive file. * @param dir output directory. * @param on_extract_entry on extract callback. * @param arg opaque pointer. * * @return the return code - 0 on success, negative number (< 0) on error. */ extern ZIP_EXPORT int zip_extract(const char *zipname, const char *dir, int (*on_extract_entry)(const char *filename, void *arg), void *arg); /** @} */ #ifdef __cplusplus } #endif #endif tea-qt-63.3.0/tea-qmake.pro000066400000000000000000000176411476733534200154140ustar00rootroot00000000000000VERSION = 63.3.0 os2: { DEFINES += 'VERSION_NUMBER=\'"63.3.0"\'' } else: { DEFINES += 'VERSION_NUMBER=\\\"$${VERSION}\\\"' } exists("/usr/include/linux/joystick.h") { message ("+JOYSTICK_SUPPORTED") DEFINES += JOYSTICK_SUPPORTED } #QMAKE_CXXFLAGS += -std=c++11 #DEFINES += NOCRYPT \ # NOUNCRYPT \ # QUAZIP_STATIC USE_ASPELL = true USE_HUNSPELL = true win32:{ isEmpty(PREFIX) { PREFIX = /usr/local/bin } TARGET = bin/tea target.path = $$PREFIX } os2:{ isEmpty(PREFIX) { PREFIX = /usr/local/bin } TARGET = bin/tea target.path = $$PREFIX } unix: { isEmpty(PREFIX) { PREFIX = /usr/local } PREFIX = $$replace(PREFIX, bin,) TARGET = bin/tea target.path = $$PREFIX/bin desktop.path = $$PREFIX/share/applications desktop.files = desktop/tea.desktop icon128.path = $$PREFIX/share/icons/hicolor/128x128/apps/ icon128.files += icons/128/tea.png icon64.path = $$PREFIX/share/icons/hicolor/64x64/apps/ icon64.files += icons/64/tea.png icon48.path = $$PREFIX/share/icons/hicolor/48x48/apps/ icon48.files += icons/48/tea.png icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps/ icon32.files += icons/32/tea.png iconsvg.path = $$PREFIX/share/icons/hicolor/scalable/apps/ iconsvg.files += icons/svg/tea.svg } nohunspell { USE_HUNSPELL = false } noaspell { USE_ASPELL = false } useclang{ message ("Clang enabled") QMAKE_CC=clang QMAKE_CXX=clang++ QMAKE_CXXFLAGS += -std=c++11 } SOURCES += src/tea.cpp \ src/main.cpp \ src/todo.cpp \ src/textproc.cpp \ src/libretta_calc.cpp \ src/wavinfo.cpp \ src/calendar.cpp \ src/gui_utils.cpp \ src/document.cpp \ src/utils.cpp \ src/spellchecker.cpp \ src/fman.cpp \ src/shortcuts.cpp \ src/logmemo.cpp \ src/img_viewer.cpp \ src/tio.cpp \ src/single_application_shared.cpp \ src/exif_reader.cpp \ src/myjoystick.cpp \ src/pugixml.cpp \ src/zip.c \ src/enc.cpp HEADERS += src/tea.h \ src/todo.h \ src/document.h \ src/utils.h \ src/textproc.h \ src/calendar.h \ src/libretta_calc.h \ src/spellchecker.h \ src/fman.h \ src/shortcuts.h \ src/logmemo.h \ src/img_viewer.h \ src/gui_utils.h \ src/wavinfo.h \ src/tio.h \ src/single_application_shared.h \ src/exif_reader.h \ src/myjoystick.h \ src/pugixml.hpp \ src/pugiconfig.hpp \ src/miniz.h \ src/zip.h \ src/enc.h TEMPLATE = app CONFIG += warn_on \ thread \ qt \ release \ link_pkgconfig QT += core QT += gui greaterThan(QT_MAJOR_VERSION, 4) { QT += widgets } else { #QT += blah blah blah } nosingleapp{ DEFINES += NO_SINGLE_APP message ("No single application mode") } INSTALLS += target desktop icon128 icon64 icon48 icon32 iconsvg nodesktop{ INSTALLS -= desktop icon128 icon64 icon48 icon32 iconsvg } RESOURCES += resources.qrc TRANSLATIONS = translations/ru.ts \ translations/de.ts \ translations/fr.ts \ translations/pl.ts DISTFILES += ChangeLog \ COPYING \ README \ NEWS \ NEWS-RU \ AUTHORS \ TODO \ hls/* \ palettes/* \ encsign/* \ images/* \ desktop/* \ text-data/* \ translations/* \ manuals/en.html \ manuals/ru.html \ manuals/pl.html \ themes/Cotton/stylesheet.css \ themes/Plum/stylesheet.css \ themes/Smaragd/stylesheet.css \ themes/TEA/stylesheet.css \ themes/Turbo/stylesheet.css \ themes/Turbo/icons/* \ themes/Vegan/stylesheet.css \ themes/Yagodnaya/stylesheet.css \ icons/32/tea.png \ icons/48/tea.png \ icons/64/tea.png \ icons/128/tea.png \ icons/svg/tea.svg \ icons/current-list.png \ icons/edit-copy-active.png \ icons/edit-copy.png \ icons/edit-cut-active.png \ icons/edit-cut.png \ icons/edit-paste-active.png \ icons/edit-paste.png \ icons/file-new.png \ icons/file-open-active.png \ icons/file-open.png \ icons/file-save-active.png \ icons/file-save-as.png \ icons/file-save.png \ icons/labels.png \ icons/tea_icon_v2.png \ icons/tea-icon-v3-01.png \ icons/tea-icon-v3-02.png \ icons/tea-icon-v3-03.png unix: { # system (pkg-config --exists zlib) { # message ("Zlib found") # PKGCONFIG += zlib # } contains (USE_ASPELL,true) { exists ("/usr/include/aspell.h") { message ("ASpell enabled") LIBS += -laspell DEFINES += ASPELL_ENABLE } } contains (USE_HUNSPELL,true) { system (pkg-config --exists hunspell) { message ("hunspell enabled") PKGCONFIG += hunspell DEFINES += HUNSPELL_ENABLE } } usepoppler{ system(pkg-config --exists poppler-cpp) { message ("Poppler enabled") PKGCONFIG += poppler-cpp DEFINES += POPPLER_ENABLE } } usedjvu{ system(pkg-config --exists ddjvuapi) { message ("djvu enabled") PKGCONFIG += ddjvuapi DEFINES += DJVU_ENABLE } } } win32: { greaterThan(QT_MAJOR_VERSION, 4) { message ("QT > 4") # LIBS += zlib1.dll contains(USE_ASPELL,true) { exists ("C:\\Qt\\Qt5.3.1\\Tools\\mingw482_32\\include\\aspell.h") { message ("ASpell enabled") LIBS += -laspell-15 DEFINES += ASPELL_ENABLE } } contains(USE_HUNSPELL,true) { exists ("c:\\Qt\\Qt5.3.1\\5.3\\mingw482_32\\include\\hunspell\\hunspell.hxx") { message ("hunspell enabled") # LIBS += c:\dev\hunspell-mingw-master\lib\hunspell.dll # LIBS += libhunspell.dll LIBS += hunspell.dll DEFINES += HUNSPELL_ENABLE } } } else { # LIBS += zlib1.dll # INCLUDEPATH+=c:\\Qt\\4.8.4\\src\\3rdparty\\zlib contains(USE_ASPELL,true) { exists ("C:\\MinGw\\include\\aspell.h") { message ("ASpell enabled") LIBS += -laspell-15 DEFINES += ASPELL_ENABLE } } contains(USE_HUNSPELL,true) { exists ("C:\\MinGw\\include\\hunspell\\hunspell.hxx") { message ("hunspell enabled") LIBS += libhunspell.dll DEFINES += HUNSPELL_ENABLE } } } } os2: { #system(pkg-config --exists zlib) { # message ("Zlib found") # PKGCONFIG += zlib # } contains(USE_ASPELL,true){ exists("c:/usr/include/aspell.h") { message ("ASpell enabled") LIBS += -laspell_dll DEFINES += ASPELL_ENABLE } } contains(USE_HUNSPELL,true){ system(pkg-config --exists hunspell) { message ("hunspell enabled") PKGCONFIG += hunspell DEFINES += HUNSPELL_ENABLE } } usepoppler{ system(pkg-config --exists poppler-cpp) { message ("Poppler enabled") PKGCONFIG += poppler-cpp DEFINES += POPPLER_ENABLE } } usedjvu{ system(pkg-config --exists ddjvuapi) { message ("djvu enabled") PKGCONFIG += ddjvuapi DEFINES += DJVU_ENABLE } } } tea-qt-63.3.0/text-data/000077500000000000000000000000001476733534200147075ustar00rootroot00000000000000tea-qt-63.3.0/text-data/cm-tf-names000066400000000000000000000000531476733534200167370ustar00rootroot00000000000000readme news copying todo changelog install tea-qt-63.3.0/text-data/lorem-ipsum000066400000000000000000000010371476733534200171040ustar00rootroot00000000000000Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.tea-qt-63.3.0/text-data/morse-en000066400000000000000000000005721476733534200163630ustar00rootroot00000000000000A=.- B=-... C=-.-. D=-.. E=. F=..-. G=--. H=.... I=.. J=.--- K=-.- L=.-.. M=-- N=-. O=--- P=.--. Q=--.- R=.-. S=... T=- U=..- V=...- W=.-- X=-..- Y=-.-- Z=--.. 0=----- 1=.---- 2=..--- 3=...-- 4=....- 5=..... 6=-.... 7=--... 8=---.. 9=----. ,=--..-- .=.-.-.- ?=..--.. ;=-.-.- :=---... /=-..-. -=-....- '=.----. (=—·——· _=..--.- )=-.--.- /=-..-. +=.-.-. "=.-..-. $=...-..-tea-qt-63.3.0/text-data/morse-ru000066400000000000000000000007101476733534200164010ustar00rootroot00000000000000А=·- Б=-··· В=·-- Г=--· Д=-·· Е=· Ж=··- З=--·· И=·· Й=·--- К=-·- Л=·-·· М=-- Н=-· О=--- П=·--· Р=·-· С=··· Т=- У=··- Ф=··-· Х=···· Ц=-·-· Ч=---· Ш=---- Щ=--·- Ь=-··- Ы=-·-- Э=··-·· Ю=··-- Я=·-·- 1=·---- 2=··--- 3=···-- 4=····- 5=····· 6=-···· 7=--··· 8=---·· 9=----· 0=----- .=······ ,=·-·-·- /=-··-· ?=··--·· !=--··-- @=·--·-·tea-qt-63.3.0/text-data/template-html000066400000000000000000000003471476733534200174130ustar00rootroot00000000000000 tea-qt-63.3.0/text-data/template-html5000066400000000000000000000001431476733534200174720ustar00rootroot00000000000000 tea-qt-63.3.0/text-data/template-teaproject000066400000000000000000000000661476733534200206050ustar00rootroot00000000000000dir_build= command_build= command_clean= command_run= tea-qt-63.3.0/text-data/tpl_c.c000066400000000000000000000001471476733534200161560ustar00rootroot00000000000000#include int main (int argc, char *argv[]) { printf ("Hello World\n"); return 0; } tea-qt-63.3.0/text-data/tpl_cpp.cpp000066400000000000000000000002011476733534200170450ustar00rootroot00000000000000#include using namespace std; int main (int argc, char *argv[]) { cout << "hello world" << endl; return 0; } tea-qt-63.3.0/themes/000077500000000000000000000000001476733534200143015ustar00rootroot00000000000000tea-qt-63.3.0/themes/Cotton/000077500000000000000000000000001476733534200155475ustar00rootroot00000000000000tea-qt-63.3.0/themes/Cotton/stylesheet.css000066400000000000000000000013671476733534200204610ustar00rootroot00000000000000QWidget { background-color: #2f2d55; color: white; } QLineEdit { border: 2px solid gray; border-radius: 8px; background-color: #00557f; color: white; selection-background-color: darkgray; } QPlainTextEdit { border-radius: 15px; } QMenu { background-color: #2d436d; border: 1px solid grey; } QMenu::item { background-color: transparent; } QMenu::item:selected { background-color: #3e7dbc; } QTreeView { background-color: #3b4a7f; alternate-background-color: #3b4a7f; } QListView { background-color: #3b4a7f; alternate-background-color: #3b4a7f; } QScrollBar:vertical { width: 24px; border: 1px solid grey; } QTabWidget::pane { border: 0px; } QTabWidget::tab-bar#tab_editor { alignment: center; } tea-qt-63.3.0/themes/Plum/000077500000000000000000000000001476733534200152165ustar00rootroot00000000000000tea-qt-63.3.0/themes/Plum/stylesheet.css000066400000000000000000000027351476733534200201300ustar00rootroot00000000000000QWidget { background-color: #9a656c; color: white; } QLineEdit { border: 1px solid gray; border-radius: 6px; background-color: #704349; color: black; selection-background-color: darkgray; } QPlainTextEdit { border-radius: 15px; } QMenu { background-color: #8c727d; border: 1px solid grey; } QMenu::item { background-color: transparent; } QMenu::item:selected { background-color: #613c41; } QTreeView { alternate-background-color: #4f6384; } QHeaderView::section { background-color: #90476c; color: white; padding-left: 4px; border: 1px solid #6c6c6c; } QListView { alternate-background-color: #4f6384; } QScrollBar:vertical { width: 24px; border: 1px solid grey; } QTabWidget::pane { border: 0px; } QTabWidget::tab-bar#tab_editor { alignment: center; background-color: #9a656c; color: black; } QTabBar::tab { background: #9a656c; border: 1px solid lightgray; padding: 10px; } QTabBar::tab:selected { background: #9a656c; margin-bottom: -1px; color: #e6e6c6; } QPushButton { border: 2px solid #8f8f91; border-radius: 6px; background-color: #aaaa7f; min-width: 80px; } QPushButton:pressed { background-color: #aaaa00; } QPushButton:flat { border: none; /* no border for a flat push button */ } QPushButton:hover:!pressed { border: 2px solid #ffaaff; background-color: #c6c694; } QPushButton:default { border-color: navy; /* make the default button prominent */ }tea-qt-63.3.0/themes/Smaragd/000077500000000000000000000000001476733534200156575ustar00rootroot00000000000000tea-qt-63.3.0/themes/Smaragd/stylesheet.css000066400000000000000000000011331476733534200205600ustar00rootroot00000000000000QWidget { background-color: #0d553f; color: white; } QLineEdit { border: 1px solid black; border-radius: 3px; background-color: #005300; color: white; selection-background-color: darkgray; } QPlainTextEdit { border-radius: 3px; } QMenu { background-color: #33684f; border: 1px solid black; } QMenu::item { background-color: transparent; } QMenu::item:selected { background-color: #87a787; color: black; } QTreeView { alternate-background-color: #2d5e5e; } QScrollBar:vertical { width: 24px; border: 1px solid grey; } QTabWidget::pane { border: 0px; } tea-qt-63.3.0/themes/TEA/000077500000000000000000000000001476733534200147125ustar00rootroot00000000000000tea-qt-63.3.0/themes/TEA/stylesheet.css000066400000000000000000000001641476733534200176160ustar00rootroot00000000000000QTabWidget::pane#tab_editor { /* border-top: 0px solid grey; background-color: grey;*/ border-top: 0px solid; }tea-qt-63.3.0/themes/Turbo/000077500000000000000000000000001476733534200153745ustar00rootroot00000000000000tea-qt-63.3.0/themes/Turbo/icons/000077500000000000000000000000001476733534200165075ustar00rootroot00000000000000tea-qt-63.3.0/themes/Turbo/icons/edit-copy-active.png000066400000000000000000000020571476733534200223670ustar00rootroot00000000000000PNG  IHDR@@iqsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATx[HQƿ;B^)RZA)bK%AЅ|JȈ2 IEP)J`Jn/m;=kΌr2g7|g;$2n Ji>*U[4JhNpo wQ"P'K=Y9 jMf|6"FT}h݀po"XI88LsՄjtk563 J G{e"4$"dR@vis]81i @ՀB21bUt Edh#H;zΪY҅!k0ojt9*!_ڠd&j@5`wϙg1@#;\ʁT,mz_m{_|wqkcKͺv*?9 ^ h{bK֞W&ZC4kY>, NR}(8(]tiLlz3%"6a\+nS1{緈C|p X/@[\*Gfp^w'Q ͛ 3ydû:(] ^LM$CyhOϐP²xWz <xk8vF (ikM. 1WiTZՏ-+Zs},ݾ/WtO& VFprG@GiLypTO|9ϛ@+`{?W2^URv8n*7V@xK8ܫij{tSuxl,|+z+ p`4T)8\4Cu+ z @Є^ h`w51/4<2Jk9TgK~|2\%IENDB`tea-qt-63.3.0/themes/Turbo/stylesheet.css000066400000000000000000000030221476733534200202740ustar00rootroot00000000000000QWidget { background-color: #babbd4; color: black; } QLineEdit { border: 2px solid gray; padding: 0 2px; background-color: #1818b2; color: white; selection-background-color: darkgray; } QMenuBar { background-color: #b2b2b2; } QMenu { margin: 16px; background-color: #b2b2b2; border: 1px solid black; } QMenu::item { background-color: transparent; } QMenu::item:selected { background-color: #18b218; } QTreeView { alternate-background-color: #18b2b2; background-color: #18b2b2; selection-color: white; selection-background-color: #18b218; } QListView { alternate-background-color: #18b2b2; background-color: #18b2b2; selection-color: white; selection-background-color: #18b218; } QScrollBar:vertical { width: 24px; border: 1px solid grey; } QPushButton { border-width: 1px; color: black; background-color: #159d15; border-color: black; border-top: 4px solid black; border-right: 4px solid black; min-width: 24px; } QPushButton:pressed { border-width: 1px; color: black; background-color: #159d15; border-color: black; border-top: 1px solid black; min-width: 24px; } QTabBar::tab { background: #b2b2b2; border-top: 1px solid black; border-left: 1px solid black; border-right: 1px solid black; border-bottom: 0px; padding: 4px; color: grey; } QTabBar::tab:selected { background: #b2b2b2; border-top: 1px solid black; border-left: 1px solid black; border-right: 1px solid black; border-bottom: 0px; padding: 4px; color: yellow; } tea-qt-63.3.0/themes/Vegan/000077500000000000000000000000001476733534200153415ustar00rootroot00000000000000tea-qt-63.3.0/themes/Vegan/stylesheet.css000066400000000000000000000012051476733534200202420ustar00rootroot00000000000000QWidget { background-color: #5d8168; color: white; } QLineEdit { border: 2px solid gray; border-radius: 8px; background-color: #3a4d38; color: white; selection-background-color: darkgray; } QPlainTextEdit { border-radius: 15px; } QMenu { background-color: #436a61; border: 1px solid grey; } QMenu::item { background-color: transparent; } QMenu::item:selected { background-color: #639469; } QTreeView { alternate-background-color: #5f915b; } QScrollBar:vertical { width: 24px; border: 1px solid grey; } QTabWidget::pane { border: 0px; } QTabWidget::tab-bar#tab_editor { alignment: center; } tea-qt-63.3.0/themes/Yagodnaya/000077500000000000000000000000001476733534200162155ustar00rootroot00000000000000tea-qt-63.3.0/themes/Yagodnaya/stylesheet.css000066400000000000000000000016561476733534200211300ustar00rootroot00000000000000QWidget { background-color: #55557f; color: white; } QLineEdit { border: 2px solid gray; border-radius: 8px; background-color: #00557f; color: white; selection-background-color: darkgray; } QPlainTextEdit { border-radius: 7px; } QMenu { background-color: #55557f; border: 1px solid black; } QMenu::item { background-color: transparent; } QMenu::item:selected { background-color: #3e7dbc; } QTreeView { color: #ffaa21; background-color: #411a54; alternate-background-color: #46036f; } QListView { color: #ffaa21; background-color: #411a54; alternate-background-color: #46036f; } QScrollBar:vertical { width: 24px; border: 1px solid grey; } QTabWidget::pane { border: 0px; } QTabWidget::tab-bar#tab_editor { alignment: left; } QScrollBar::handle:vertical { border: 1px solid black; background: #004362; min-width: 24px; min-height: 8px; subcontrol-origin: margin; } tea-qt-63.3.0/translations/000077500000000000000000000000001476733534200155355ustar00rootroot00000000000000tea-qt-63.3.0/translations/de.qm000066400000000000000000000621061476733534200164710ustar00rootroot00000000000000JH`YRǁT>>+A߬yG *f=)Sqw=U=h#r+ & >w4?MBkS '(G'3(8*FZ+6G)J.Ly!QQ}S{e%_sM}|U45t2F3LH55j1z[N;+ϡ 6rӸ E"( 86^EQ,N ,/#?6NG=HAE2U43]HCgL|Y#N|wA~/T)`r9;j2W[p80D7*("$:!P尣B3.=Q >  '5c '5 ?B1 S&% TQVN ]1 la s5+ {a:s t8 v9 3 4p bE S ; S>Q ƕ' 4 虄 (d n9H (o <K D% G" K? LA: dσOX dϣ g K 0B2w S 8 S: B S ȗ& tI7 r U ZQ 5"$ JU $7G :Pu R g? j= m )<% 1T :8  ȮT T3 RRJN U  ` ' $8 E. ()`<{ </ >h)I AEQ C  RY.9 q0 s8 x Q ew  v < KUC6UM baipC@+{.CNLP3KA[ YK(0ɪ5-CMI?4 !5@E0eu uH2n 7U( s%,9r0~"aI3}YI iT={e2Ku)C\4ԼEiVberAbout CAboutWindowDanksagungenAcknowledgements CAboutWindowCodeCode CAboutWindowbersetzungen Translations CAboutWindowDarker palette CDarkerWindow7%1 has been modified. Do you want to save your changes? CDocument %1 is open CDocument(%1 wurde gespeichert %1 is saved CDocumentcannot open %1 because of: %2 CDocumentcannot save %1 because of: %2 CDocumentnew[%1] CDocumentVorschaupreview CImgViewer is unpackedCTEA7#external programs list. example: ff=firefox file:///%sCTEAP#external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s"CTEA %1 - gespeichert %1 - savedCTEA+%1 already exists Do you want to overwrite?CTEA%1 is not found
CTEA%1 is processed and savedCTEA(%1 wurde gespeichert %1 is savedCTEA%1 kbytes %2
CTEA'%1 number of occurrences of %2 is foundCTEA%p% fertig %p% completedCTEA$<b>Lesezeichen</b>BookmarksCTEA$COLOR SAMPLECTEAberAboutCTEAber QtAbout QtCTEAAdd or subtractCTEA Add to ZIPCTEA:Zu den Lesezeichen hinzufgenAdd to bookmarksCTEA2Zum Wrterbuch hinzufgenAdd to dictionaryCTEAAusrichtungAlignCTEAoAll (*);;Text files (*.txt);;Markup files (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)CTEAAnagramCTEAAnalyzeCTEAAntispam E-MailAntispam e-mailCTEA Apply hard rotation by EXIF dataCTEAApply templateCTEAApply to each lineCTEAArabic to RomanCTEAAre you sure to delete %1?CTEAAspell directoryCTEAZuweisenAssignCTEA.Automatisches EinrckenAutomatic indentCTEA(Automatic preview images at file managerCTEA$Binr nach dezimalBinary to decimalCTEA Block endCTEA Block startCTEAFettBoldCTEALesezeichen BookmarksCTEA*Liste der LesezeichenBookmarks listCTEABottomCTEA Break lineCTEA Build programCTEA C templateCTEA C++ templateCTEA!Calculate moon days between datesCTEACalendarCTEAD%1 konnte nicht gespeichert werdenCannot save %1CTEACannot save: %1CTEA*Gro-/KleinschreibungCaseCTEACase sensitiveCTEACellsCTEACenterCTEAZeichensatzCharsetCTEA'Charset for file open from command lineCTEAClassicCTEA Clean programCTEA0Aktuelle Datei schlieen Close currentCTEA FarbeColorCTEAColumns per rowCTEAComment selectionCTEACommonCTEACompare two stringsCTEACompressCTEAConfigsCTEA:Konvertiere Tags zu HTML-CodeConvert tags to entitiesCTEAKopierenCopyCTEA Copy blockCTEACopy by column[s]CTEA:Aktuellen Dateinamen kopierenCopy current file nameCTEA6Copy the current selection's contents to the clipboardCTEACopy to storage fileCTEACount lines in selected filesCTEACount the substringCTEACount the substring (regexp)CTEANotizbuchCrapbookCTEA(Neue Datei erstellenCreate a new fileCTEACreate new ZIPCTEACreate new directoryCTEACreate web galleryCTEA Current filesCTEA&Cursor blink time (msecs, zero is OFF)CTEACursor center on scrollCTEA Cursor widthCTEAAusschneidenCutCTEA Cut blockCTEA5Cut the current selection's contents to the clipboardCTEADarkerCTEA DatumDateCTEA Date and timeCTEADatumsformat Date formatCTEADaysCTEA$Dezimal nach binrDecimal to binaryCTEA"Delete N first chars at file namesCTEADelete by columnCTEADatei lschen Delete fileCTEADeselect by regexpCTEADo not add to recentCTEADockedCTEA,Speicherplatz anzeigenDocument weightCTEADocuments tabs alignCTEAEXIFCTEABearbeitenEditCTEA,Lesezeichen bearbeitenEdit bookmarksCTEA*Schriftart fr Editor Editor fontCTEA=Enca is not installed, falling back to the built-in detectionCTEA End of line: CTEAEnter the archive nameCTEAEnter the nameCTEAEnter your daily notes here.CTEAEnter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatreCTEA EnumerateCTEA Escape regexpCTEAEvaluateCTEABeendenExitCTEA.Beenden der ApplikationExit the applicationCTEA$Wrter extrahieren Extract wordsCTEAFIFCTEAFIF at the top (restart needed)CTEA DateiFileCTEA File actionsCTEA$DateiinformationenFile informationCTEAFile operationsCTEAFilesCTEA FilterFilterCTEAFilter by repetitionsCTEAFilter with regexpCTEA SuchenFindCTEA Find in filesCTEAVorwrts suchen Find nextCTEAFind obsolete pathsCTEA Rckwrts suchen Find previousCTEA Flip a listCTEAFlip a list with separatorCTEAFlip bits (bitwise complement)CTEAFmCTEAFocus the Famous input fieldCTEAFocus the editorCTEA.Englisch nach MorsecodeFrom English to MorseCTEA.Morsecode nach EnglischFrom Morse To EnglishCTEA.Morsecode nach RussischFrom Morse To RussianCTEA.Russisch nach MorsecodeFrom Russian to MorseCTEA From cursorCTEA Full infoCTEAFunktionen FunctionsCTEA Fuzzy modeCTEAFuzzy search factorCTEAGUI tabs alignCTEAGetting files list...CTEAGoCTEAGo to current dateCTEAGo to home dirCTEAGehe zu Zeile Go to lineCTEA<Gehe zu gespeicherter PositionGo to saved positionCTEAGuess encoding!CTEA HTML templateCTEAHTML5 templateCTEAKopfzeilenHeaderCTEA:Fehlermarkierungen versteckenHide error marksCTEA4Aktuelle Zeile hervorhebenHighlight current lineCTEA:Gepaarte Klammern hervorhebenHighlight paired bracketsCTEAHomeCTEAJVerzeichnis der Hunspell-WrterbcherHunspell dictionaries directoryCTEAIDECTEA Icons sizeCTEAImage conversion output formatCTEAImagesCTEAIncorrect parameters at FIFCTEAEinrcken (Tab) Indent (tab)CTEAIndent by first lineCTEABild einfgen Insert imageCTEA$Benutzeroberflche InterfaceCTEAInterface fontCTEA KursivItalicCTEAJustifyCTEATastaturKeyboardCTEA&LaTeX: Straight to curly double quotesCTEA&LaTeX: Straight to double angle quotesCTEA)LaTeX: Straight to double angle quotes v2CTEALabel ends with: CTEALabel starts with: CTEALabelsCTEA4Zuletzt geschlossene DateiLast closed fileCTEALeftCTEA LizenzLicenseCTEALinkLinkCTEA Link optionsCTEAList ZIP contentCTEA Logmemo fontCTEAMark all foundCTEAMark first dateCTEAMark last dateCTEA TextauszeichnungMarkupCTEAMathCTEA MiscellaneousCTEA ModusModeCTEAMonthsCTEAMoon mode on/offCTEAMoon phase algorithmCTEAMorsecode Morse codeCTEA Multi-renameCTEANEUIGKEITENNEWSCTEANameNameCTEA Name:Name:CTEANavNavCTEANeuNewCTEANext tabCTEA.Geschtztes LeerzeichenNon-breaking spaceCTEANorthern hemisphereCTEANotesCTEA Number of days between two datesCTEANumber:CTEA ffnenOpenCTEAOpen DirectoryCTEAOpen an existing fileCTEA0An Cursorposition ffnenOpen at cursorCTEADatei ffnen Open fileCTEAOutput images qualityCTEAPalettenPalettesCTEA Absatz ParagraphCTEAEinfgenPasteCTEA Paste blockCTEA9Paste the clipboard's contents into the current selectionCTEAPlaceCTEAPrev tabCTEA Preview imageCTEAPreview selected colorCTEADruckenPrintCTEAPrint documentCTEAProfilesCTEA&Liste der Programme Programs listCTEAQuotesCTEARMS for all channels: %1 dBCTEA2Zuletzt geffnete Dateien Recent filesCTEAWiederholenRedoCTEARefreshCTEARefresh current dirCTEARefresh labelsCTEA Regexp modeCTEANeu ladenReloadCTEAReload with encodingCTEAEntfernenRemoveCTEA#Remove after delimiter at each lineCTEA$Remove before delimiter at each lineCTEARemove day recordCTEA&Dubletten entfernenRemove duplicatesCTEARemove empty linesCTEARemove formattingCTEARemove formatting at each lineCTEA8Aus dem Wrterbuch entfernenRemove from dictionaryCTEARemove lines < N sizeCTEARemove lines > N sizeCTEARemove trailing spacesCTEAUmbenennenRenameCTEARename selected fileCTEA Repeat lastCTEAAlles ersetzen Replace allCTEAReplace all in opened filesCTEAReplace in file namesCTEAErsetzen durch Replace withCTEA$Restore the last session on start-upCTEAUmkehrenReverseCTEARightCTEARoman to ArabicCTEAAusfhrenRunCTEA Run programCTEASpeichernSaveCTEA Save .bakCTEASpeichern unterSave AsCTEASave ZIPCTEASpeichern unterSave asCTEASave as differentCTEA$Speichere Position Save positionCTEA Save profileCTEA Save sessionCTEASave the document to diskCTEA"Save the document under a new nameCTEASave the file first!CTEASave timestamped versionCTEA Saved: %1CTEAScale by percentagesCTEA Scale by sideCTEA Scale imageCTEA$Scale images with bilinear filteringCTEASkripteScriptsCTEA SucheSearchCTEASearch resultsCTEA Searching...CTEAAuswhlenSelectCTEASelect by regexpCTEASelect the file name first!CTEASitzungenSessionsCTEASet UNIX end of lineCTEASet Windows end of lineCTEASet as storage fileCTEASet old Mac end of line (CR)CTEAShortcutCTEA4Anzeige der CursorpositionShow cursor positionCTEAShow full path at window titleCTEAShow line numbersCTEAShow margin atCTEASize of the sideCTEACode-SchnipselSnippetsCTEASortierenSortCTEASort by lengthCTEASort case insensitivelyCTEASort case sensitivelyCTEA%Sort case sensitively, with separatorCTEASort table by column ABCCTEA&Rechtschreibprfung Spell checkCTEA@Programm fr RechtschreibprfungSpell checker engineCTEASpell checkingCTEA@Sprachen fr RechtschreibprfungSpell-checker languagesCTEAStart week on SundayCTEA,Start/stop capture clipboard to storage fileCTEA Stay on topCTEAStraight to curly double quotesCTEAStraight to double angle quotesCTEAStrip HTML tagsCTEAVorschlagenSuggestCTEASum by last columnCTEA Swap cellsCTEASyntax highlighting enabledCTEATEA program iconCTEATEA project templateCTEA<Tabulatorbreite in LeerzeichenTab width in spacesCTEATabellenTablesCTEAVorlagen TemplatesCTEATestCTEATextCTEAText statisticsCTEAText to [X]HTMLCTEACThe famous input field. Use for search/replace, function parametersCTEAThemesCTEAThere are %1 lines at %2 filesCTEAThere is no stylesheet fileCTEAZeitTimeCTEAZeitformat Time formatCTEAToggle fullscreenCTEAToggle header/sourceCTEAToggle word wrapCTEAToolsCTEA&Total size = %1 kbytes in %2 files
CTEA UI language (TEA restart needed)CTEAUI mode (TEA restart needed)CTEAUI style (TEA restart needed)CTEAUNITAZ quantity sortingCTEAUNITAZ sorting by alphabetCTEAUNITAZ sorting by lengthCTEA UNITAZ: UNIverlsal Text AnalyZerCTEA GROSS-SCHREIBUNGUPCASECTEAUn-indent (shift+tab)CTEAUnterstrichen UnderlineCTEARckgngigUndoCTEAUnmarkCTEAUnpack ZIP to current directoryCTEAUpCTEAUse Alt key to access main menuCTEA$Use EXIF orientation at image viewerCTEAUse Enca for charset detectionCTEA-Use Left Alt + WASD as additional cursor keysCTEA Use external image viewer for F2CTEAUse joystick as cursor keysCTEANLeerzeichen statt Tabulatoren verwendenUse spaces instead of tabsCTEA&Use traditional File Save/Open dialogsCTEAAnsichtViewCTEAWeb gallery optionsCTEA Whole wordsCTEA Word length: CTEAZeilenumbruch Word wrapCTEA Words lengthsCTEAYearsCTEAZIPCTEAZIP packing: file names charsetCTEA!ZIP unpacking: file names charsetCTEAZero pad file namesCTEA#Zip directory with processed imagesCTEA"[X]HTML-Werkzeuge [X]HTML toolsCTEAbits per sample: %1CTEAcannot open %1 because of: %2CTEAIchars: %1
chars without spaces: %2
lines: %3
author's sheets: %4CTEAconfigsCTEAdatesCTEAdec degrees > deg min secCTEAdeg min sec > dec degreesCTEAeditorCTEAelapsed milliseconds: %1CTEAfamous input fieldCTEA file name: %1CTEAfilesCTEAlast modified: %1CTEAlogmemoCTEAkleinschreibung lower caseCTEAmanualCTEAnewCTEA new_archiveCTEA new_directoryCTEA new_profileCTEA new_sessionCTEA not found!CTEAnumber of channels: %1CTEAoptionsCTEAHput your notes (for this file) here and they will be saved automaticallyCTEAsample rate: %1CTEAscriptsCTEAsize: %1 kbytesCTEAsnippet %1 is not existsCTEAsnippetsCTEAsum: %1CTEAtablesCTEA templatesCTEAtext analysis of: %1CTEAtotal / unique: %1CTEA!total to unique per cent diff: %1CTEAwords total: %1CTEAwords unique: %1CTEAyou can put here notes, etcCTEA'Saving for this format is not supported CTioReadOnlyAttention! Attention!CTodo scaled to:  CZORWindowGendert am Modified atQObjectNameNameQObject GreSizeQObjectZIP/UNZIP API error %1 QuaZipFiletea-qt-63.3.0/translations/de.ts000066400000000000000000001474641476733534200165150ustar00rootroot00000000000000 CAboutWindow Code Code Acknowledgements Danksagungen Translations Übersetzungen About Über CDarkerWindow Darker palette CDocument cannot open %1 because of: %2 %1 is open cannot save %1 because of: %2 %1 is saved %1 wurde gespeichert new[%1] %1 has been modified. Do you want to save your changes? Copy Kopieren Cut Ausschneiden Paste Einfügen Undo Rückgängig Redo Wiederholen CImgViewer preview Vorschau CTEA The famous input field. Use for search/replace, function parameters FIF editor logmemo famous input field Charset Zeichensatz %1 already exists Do you want to overwrite? Files Labels New Neu Create a new file Neue Datei erstellen Open file Datei öffnen Open an existing file Save Speichern Save the document to disk Save As Speichern unter Save the document under a new name Exit Beenden Exit the application Beenden der Applikation Cut Ausschneiden Cut the current selection's contents to the clipboard Copy Kopieren Copy the current selection's contents to the clipboard Paste Einfügen Paste the clipboard's contents into the current selection Undo Rückgängig Redo Wiederholen About Über About Qt Über Qt File Datei Open Öffnen Last closed file Zuletzt geschlossene Datei Open at cursor An Cursorposition öffnen Crapbook Notizbuch Notes Save .bak Save timestamped version Save session File actions Reload Neu laden Reload with encoding Set UNIX end of line Set Windows end of line Set old Mac end of line (CR) Recent files Zuletzt geöffnete Dateien Bookmarks Lesezeichen Edit bookmarks Lesezeichen bearbeiten Add to bookmarks Zu den Lesezeichen hinzufügen Find obsolete paths Templates Vorlagen Sessions Sitzungen Configs Bookmarks list Liste der Lesezeichen Programs list Liste der Programme Do not add to recent Print Drucken Close current Aktuelle Datei schließen Edit Bearbeiten Block start Block end Copy block Paste block Cut block Copy current file name Aktuellen Dateinamen kopieren Indent (tab) Einrücken (Tab) Un-indent (shift+tab) Indent by first line Comment selection Set as storage file Copy to storage file Start/stop capture clipboard to storage file Markup Textauszeichnung Mode Modus Header Kopfzeilen Align Ausrichtung Center Left Right Justify Bold Fett Italic Kursiv Underline Unterstrichen Link Link Paragraph Absatz Color Farbe Break line Non-breaking space Geschütztes Leerzeichen Insert image Bild einfügen [X]HTML tools [X]HTML-Werkzeuge Text to [X]HTML Convert tags to entities Konvertiere Tags zu HTML-Code Antispam e-mail Antispam E-Mail Document weight Speicherplatz anzeigen Preview selected color Strip HTML tags Rename selected file Search Suche Find Suchen Find next Vorwärts suchen Find previous Rückwärts suchen Find in files Replace with Ersetzen durch Replace all Alles ersetzen Replace all in opened files Mark all found Unmark Case sensitive Whole words From cursor Regexp mode Fuzzy mode Functions Funktionen Repeat last Tools Scale image Snippets Code-Schnipsel Scripts Skripte Tables Tabellen Place TEA project template HTML template HTML5 template C++ template C template Date Datum Time Zeit Case Groß-/Kleinschreibung UPCASE GROSS-SCHREIBUNG lower case kleinschreibung Sort Sortieren Sort case sensitively Sort case insensitively Sort case sensitively, with separator Sort by length Flip a list Flip a list with separator Cells Sort table by column ABC Swap cells Delete by column Copy by column[s] Filter Filter Remove duplicates Dubletten entfernen Remove empty lines Remove lines < N size Remove lines > N size Remove before delimiter at each line Remove after delimiter at each line Filter with regexp Filter by repetitions Math Evaluate Arabic to Roman Roman to Arabic Decimal to binary Dezimal nach binär Binary to decimal Binär nach dezimal Flip bits (bitwise complement) Enumerate Sum by last column deg min sec > dec degrees dec degrees > deg min sec Morse code Morsecode From Russian to Morse Russisch nach Morsecode From Morse To Russian Morsecode nach Russisch From English to Morse Englisch nach Morsecode From Morse To English Morsecode nach Englisch Analyze Text statistics Extract words Wörter extrahieren Words lengths Count the substring Count the substring (regexp) UNITAZ quantity sorting UNITAZ sorting by alphabet UNITAZ sorting by length Text Apply to each line Remove formatting Remove formatting at each line Compress Anagram Remove trailing spaces Escape regexp Reverse Umkehren Compare two strings Quotes Straight to double angle quotes Straight to curly double quotes LaTeX: Straight to curly double quotes LaTeX: Straight to double angle quotes LaTeX: Straight to double angle quotes v2 Spell-checker languages Sprachen für Rechtschreibprüfung Spell check Rechtschreibprüfung Suggest Vorschlagen Add to dictionary Zum Wörterbuch hinzufügen Remove from dictionary Aus dem Wörterbuch entfernen Calendar Moon mode on/off Mark first date Mark last date Add or subtract Days Months Years Go to current date Calculate moon days between dates Number of days between two dates Remove day record Run Ausführen IDE Run program Build program Clean program Toggle header/source Nav Nav Save position Speichere Position Go to saved position Gehe zu gespeicherter Position Go to line Gehe zu Zeile Next tab Prev tab Focus the Famous input field Focus the editor Refresh labels Current files Fm Multi-rename Zero pad file names Delete N first chars at file names Replace in file names Apply template File operations Create new directory Rename Umbenennen Delete file Datei löschen File information Dateiinformationen Count lines in selected files Full info Images Scale by side Scale by percentages Create web gallery Go to home dir Refresh current dir Preview image Select by regexp Deselect by regexp View Ansicht Themes Palettes Paletten Profiles Save profile Toggle word wrap Hide error marks Fehlermarkierungen verstecken Toggle fullscreen Stay on top Darker NEWS NEUIGKEITEN License Lizenz not found! %1 is saved %1 wurde gespeichert options Classic Docked Interface font Editor font Schriftart für Editor Logmemo font Up Bottom GUI tabs align Documents tabs align Icons size TEA program icon Show line numbers Word wrap Zeilenumbruch Syntax highlighting enabled Highlight current line Aktuelle Zeile hervorheben Highlight paired brackets Gepaarte Klammern hervorheben Automatic indent Automatisches Einrücken Use spaces instead of tabs Leerzeichen statt Tabulatoren verwenden Tab width in spaces Tabulatorbreite in Leerzeichen Show cursor position Anzeige der Cursorposition Cursor center on scroll Cursor blink time (msecs, zero is OFF) Cursor width Show margin at Show full path at window title Interface Benutzeroberfläche Use joystick as cursor keys Automatic preview images at file manager Restore the last session on start-up Use external image viewer for F2 Use traditional File Save/Open dialogs Start week on Sunday Northern hemisphere Moon phase algorithm Charset for file open from command line Common Label starts with: Label ends with: Date and time Date format Datumsformat Time format Zeitformat Spell checking Spell checker engine Programm für Rechtschreibprüfung Hunspell dictionaries directory Verzeichnis der Hunspell-Wörterbücher Select Auswählen Aspell directory Miscellaneous Fuzzy search factor Image conversion output format Scale images with bilinear filtering Output images quality Apply hard rotation by EXIF data Web gallery options Size of the side Link options Columns per row EXIF Use EXIF orientation at image viewer Shortcut Assign Zuweisen Remove Entfernen Keyboard Tastatur manual you can put here notes, etc snippet %1 is not exists Print document %p% completed %p% fertig elapsed milliseconds: %1 %1 - saved %1 - gespeichert Cannot save %1 %1 konnte nicht gespeichert werden chars: %1<br>chars without spaces: %2<br>lines: %3<br>author's sheets: %4 %1 is processed and saved #external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s" #external programs list. example: ff=firefox file:///%s Save the file first! templates snippets scripts tables configs Enter the name Name: Name: new_directory new_session <span style="%1">COLOR SAMPLE</span> Name Name Go Home Refresh Save as Speichern unter <b>Bookmarks</b> <b>Lesezeichen</b> files new Are you sure to delete %1? End of line: file name: %1 size: %1 kbytes last modified: %1 bits per sample: %1 number of channels: %1 sample rate: %1 RMS for all channels: %1 dB total to unique per cent diff: %1 total / unique: %1 words unique: %1 words total: %1 text analysis of: %1 UNITAZ: UNIverlsal Text AnalyZer %1 number of occurrences of %2 is found Open Directory Getting files list... Searching... cannot open %1 because of: %2 Search results new_profile There are %1 lines at %2 files Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatre Enter your daily notes here. dates Select the file name first! %1 is not found<br> %1 kbytes %2 <br> Total size = %1 kbytes in %2 files<br> Word length: Number: put your notes (for this file) here and they will be saved automatically There is no stylesheet file sum: %1 Incorrect parameters at FIF Cannot save: %1 Saved: %1 This file is opened in the read-only mode. You can save it with another name using <b>Save as</b> The storage file is closed or not set. You are trying to rename the opened file, please close it first! matched does not Check regexp match 0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - Leueshkanov Actions Open a file from the file name provided above Save the current opened file with the name provided above Capitalize sentences GTK Bookmarks: Set as autosaving file Unset the autosaving file Autosaving Temporary save unsaved buffers on exit Autosave buffers and autosaving files (each N seconds) Keyboards <b>TEA %1</b> by Peter Semiletov | tea.ourproject.org<br>Support TEA via PayPal: peter.semiletov@gmail.com<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-git Show ebooks fine Show tabs and spaces Subtitles: shift timecodes by msecs Preview Markdown All (*);;Text files (*.txt);;Markup files (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Select all Case inverse Enable locale only voices Speach voice Speech Say the selected text Speech pause/resume Stop speech UI mode (TEA restart is needed) UI language (TEA restart is needed) UI style (TEA restart is needed) FIF at the top (restart is needed) Enable speech (TEA restart is needed) Save more Save all existing CTioReadOnly Saving for this format is not supported CTodo Attention! Attention! CZORWindow scaled to: QObject Name Name Size Größe Modified at Geändert am Please set up spell checker dictionaries at Options - Functions page tea-qt-63.3.0/translations/es.qm000066400000000000000000001243671476733534200165200ustar00rootroot00000000000000TITIY\s^tq$y%&-))/s%0D3>5=P*I0+DX0KD8FEG[H5I_H5 fKR\jss5xaį sC9zE'/b?GG H/I3JcbM2"T31mTIUjJUVNW'OWTPX[[f3{z_*jlbMmxc/q=Ma$؉ۺotJ k FP( Psv`X(A\3oi.G[h8AIg nzL"j<~HʯA)a 3o*- U^D R3bnl0pQ098G F0~JYgǁu>h+n߬y|7Fz Dq5Vg@Uf<,+ ?>w49>HHQBmGRmD^T%Oɓ7(|I":2Pz;hw}?iiky @{'3C8FFZGG)0]J.6Ly:QJQS{e>3sM} |UW5tSFTH5XjQ[}c~+dA#tgvu:rv7&RbÌ K$t80sȡ4W%aG *+PK#C%w'&̰L0,QE!0{ GO5T?#X9;i[$kqm_sxjcrJ{#9| 4 4}6=9_9:CG!y|G%Dr!lx{T$ b+Wn }\N 7L TEcjXa; 9\\'Cntn!#7Ez%2+K927b8YG8[|?;NP&}?~u,ϷXMS m*so$ piPϡ YӸE;r( \^/E,NQ,N&?YG=HAEU4]Hpg|Y#|wm~NU)`'!!9bR[p]O[B$_!M尣p38s= i N 'W 'X0 ?BQ A S&?2 TQ= ]QJ la 4 m s5H {a` Ef[ t]d v] U> Vo bv  cP ƕ6v Ww 虄m C& n^ ( <' D>v G'7 Kj LmZ dσw dϣ g$ r5L 0BS S/ 8 Sa pb ȗ@y t r6! 8 1 Z/ 5:v J $7} : R# gkU j=+S m )d 1; `e 5 Ȯ)+ TU RR U,  B < EMt ()`dv <. >h)a AE CS RY.^ q0! s] v x e/D u Y " e_ No se pudo guardar %1 porque %2cannot save %1 because of: %2 CDocumentnuevo[%1]new[%1] CDocumentvista previapreview CImgViewerdescomprimidos is unpackedCTEA|#listado de programas externos. Ejemplo: ff=firefox file:///%s7#external programs list. example: ff=firefox file:///%sCTEA#listado de programas externos. Un ejemplo: opera="C:\Program Files\Opera\opera.exe " "%s"P#external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s"CTEA%1 - guardado %1 - savedCTEAH%1 ya existe Desea sobreescribirlo?+%1 already exists Do you want to overwrite?CTEA(%1 no localizado<br>%1 is not found
CTEA.%1 procesado y guardado%1 is processed and savedCTEA&%1 ha sido guardado %1 is savedCTEA"%1 kbytes %2 <br>%1 kbytes %2
CTEAD%1 coincidencias de %2 encontradas'%1 number of occurrences of %2 is foundCTEA%p% completado %p% completedCTEA0 - Trigonomtrico v.2, 1 - Trigonomtrico v.1, 2 - Conway, 3 - LeueshkanovK0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - LeueshkanovCTEA"<b>Marcadores</b>BookmarksCTEAj<b>TEA %1</b> por Peter Semiletov | tea.ourproject.org<br>Apoye a TEA en www.patreon.com/semiletov<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-gitTEA %1 by Peter Semiletov | tea.ourproject.org
Support TEA on www.patreon.com/semiletov
Git: github.com/psemiletov/tea-qt
AUR: aur.archlinux.org/packages/tea-qt-gitCTEAJ<span style="%1">COLOR MUESTRA</span>$COLOR SAMPLECTEAAcerca deAboutCTEAAcerca de QtAbout QtCTEAAccionesActionsCTEASumar o restarAdd or subtractCTEAAadir al ZIP Add to ZIPCTEA Agregar marcadorAdd to bookmarksCTEA*Aadir al diccionarioAdd to dictionaryCTEAAlineacinAlignCTEAn0gr0maAnagramCTEAAnlisisAnalyzeCTEA>Proteccin contra correo basuraAntispam e-mailCTEAPAplicar rotacin rigurosa por datos EXIF Apply hard rotation by EXIF dataCTEA"Aplicar plantillaApply templateCTEA(Aplicar a cada lneaApply to each lineCTEA$Arbigos a romanosArabic to RomanCTEABEst seguro que desea borrar %1?Are you sure to delete %1?CTEA"Directorio AspellAspell directoryCTEAAsignarAssignCTEA(Sangras automticasAutomatic indentCTEAzVisualizacin automtica de imgenes en el gestor de archivos(Automatic preview images at file managerCTEAtGuardar bferes y archivos de autoguardado cada N segundos6Autosave buffers and autosaving files (each N seconds)CTEAutoguardado AutosavingCTEA"Binario a decimalBinary to decimalCTEA Final del bloque Block endCTEA"Inicio del bloque Block startCTEANegritasBoldCTEAMarcadores BookmarksCTEA*Listado de marcadoresBookmarks listCTEA AbajoBottomCTEASalto de lnea Break lineCTEA"!>mpilar programa Build programCTEAPlantilla C C templateCTEAPlantilla C++ C++ templateCTEALCalcular das lunares entre las fechas!Calculate moon days between datesCTEACalendarioCalendarCTEA*No se pudo guardar %1Cannot save %1CTEA.No se puede guardar: %1Cannot save: %1CTEA.Oraciones en maysculasCapitalize sentencesCTEATipo de letraCaseCTEA.Reconocer mays./mins.Case sensitiveCTEA CeldasCellsCTEA!entradoCenterCTEACodificacinCharsetCTEAvCodificacin para abrir archivos desde la lnea de comandos'Charset for file open from command lineCTEAPVerificar coincidencias por exp. regularCheck regexp matchCTEAClsicoClassicCTEA Depurar programa Clean programCTEA&Cerrar este archivo Close currentCTEA ColorColorCTEA Columnas en filaColumns per rowCTEA$Comentar seleccinComment selectionCTEAGeneralCommonCTEA(!omparar dos cadenasCompare two stringsCTEA!omprimirCompressCTEAConfigsConfigsCTEA@Convertir etiquetas en entidadesConvert tags to entitiesCTEA CopiarCopyCTEACopiar bloque Copy blockCTEA*Copiar por columna(s)Copy by column[s]CTEA:Copiar este nombre de archivoCopy current file nameCTEA@Copiar seleccin al portapapeles6Copy the current selection's contents to the clipboardCTEA>Copiar a la carpeta de archivosCopy to storage fileCTEATConteo de lneas en archivos seleccionadosCount lines in selected filesCTEA(Conteo de subcadenasCount the substringCTEAJConteo de subcadenas por exp. regularCount the substring (regexp)CTEA*Registro de tonterasCrapbookCTEA&Crear nuevo archivoCreate a new fileCTEA!raer nuevo ZIPCreate new ZIPCTEA,!rear nuevo directorioCreate new directoryCTEA"!rear galera webCreate web galleryCTEA"Archivos actuales Current filesCTEAxTiempo de parpadeo del cursor en <s (cero para desactivarlo)&Cursor blink time (msecs, zero is OFF)CTEA@Centrar cursor en desplazamientoCursor center on scrollCTEA"Grosor del cursor Cursor widthCTEA CortarCutCTEACortar bloque Cut blockCTEA@Enviar seleccin al portapapeles5Cut the current selection's contents to the clipboardCTEAModo oscuroDarkerCTEA FechaDateCTEAFecha y hora Date and timeCTEA Formato de fecha Date formatCTEADasDaysCTEA"Decimal a binarioDecimal to binaryCTEA^Borrar los N primeros caracteres de los nombres"Delete N first chars at file namesCTEA&Borrar por columnasDelete by columnCTEA Eliminar archivo Delete fileCTEALQuitar seleccin por expresin regularDeselect by regexpCTEA>No agregar a Archivos recientesDo not add to recentCTEAAcopladoDockedCTEA(Tamao del documentoDocument weightCTEA<Alinear pestaas de documentosDocuments tabs alignCTEAEXIFEXIFCTEA EditarEditCTEA"Editar marcadoresEdit bookmarksCTEA$Fuentes del editor Editor fontCTEAEnca no est instalado, regreso al algoritmo de deteccin incorporado=Enca is not installed, falling back to the built-in detectionCTEA Final de lnea:  End of line: CTEA:Introduzca nombre del archivoEnter the archive nameCTEA(Introduzca el nombreEnter the nameCTEAJEscriba sus anotaciones diarias aqu.Enter your daily notes here.CTEAJEscriba sus notas aqu. Para habilitar el recordatorio, use el formato de tiempo de 24 horas [hh:mm], es decir: [06:00] Buenos das! [20:10] Es hora de ir al cine!Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatreCTEAEnumerar EnumerateCTEAEscape regexp Escape regexpCTEAEvaluarEvaluateCTEA SalirExitCTEA.Abandonar la aplicacinExit the applicationCTEA Extraer palabras Extract wordsCTEAFIFFIFCTEA\FIF en la parte superior (reincio obligatorio)FIF at the top (restart needed)CTEAArchivoFileCTEA,Acciones en el archivo File actionsCTEA8Informacin sobre el archivoFile informationCTEA,Operaciones en archivoFile operationsCTEAArchivosFilesCTEA FiltroFilterCTEA0Filtrar por repeticionesFilter by repetitionsCTEA:Filtrar por expresin regularFilter with regexpCTEA BuscarFindCTEA$Buscar en archivos Find in filesCTEA Buscar siguiente Find nextCTEA8Hallar trayectorias errneasFind obsolete pathsCTEABuscar atrs Find previousCTEAInvertir lista Flip a listCTEANInvertir lista delimitada con separadorFlip a list with separatorCTEAInvertir bitsFlip bits (bitwise complement)CTEAFmFmCTEA&Enfocarse en el FIFFocus the Famous input fieldCTEA,Centrarse en el editorFocus the editorCTEAIngls a >rseFrom English to MorseCTEAMorse a RusoFrom Morse To RussianCTEARuso a MorseFrom Russian to MorseCTEADesde el cursor From cursorCTEA*Informacin detallada Full infoCTEAFunciones FunctionsCTEAModo difuso Fuzzy modeCTEA<Coeficiente de bsqueda difusaFuzzy search factorCTEAMarcadores GTK:GTK Bookmarks:CTEA Alinear pestaasGUI tabs alignCTEABObteniendo listado de archivos...Getting files list...CTEAIrGoCTEA(Ir a la fecha actualGo to current dateCTEA2Ir al directorio personalGo to home dirCTEA"Saltar a la lnea Go to lineCTEA8Volver a posicin registradaGo to saved positionCTEA*Detectar codificacinGuess encoding!CTEAPlantilla HTML HTML templateCTEAPlantilla HTML5HTML5 templateCTEAEncabezadoHeaderCTEA.Ocultar marcas de errorHide error marksCTEA*Destacar lnea actualHighlight current lineCTEABDestacar emparejado de parntesisHighlight paired bracketsCTEA&Directorio personalHomeCTEAFDirectorio de diccionarios HunspellHunspell dictionaries directoryCTEAIDEIDECTEA Tamao de conos Icons sizeCTEAVFormato de salida para imgenes convertidasImage conversion output formatCTEAImgenesImagesCTEA4FIF: parmetros no vlidosIncorrect parameters at FIFCTEASangra (tab) Indent (tab)CTEA0Sangra de primera lneaIndent by first lineCTEAInsertar imagen Insert imageCTEAInterface InterfaceCTEA&Fuente de interfaceInterface fontCTEAItlicasItalicCTEAJustificadoJustifyCTEATecladoKeyboardCTEA@LaTeX:comillas rectas a inglesas&LaTeX: Straight to curly double quotesCTEALLaTeX: comillas rectas a espaolas v.1&LaTeX: Straight to double angle quotesCTEALLaTeX: comillas rectas a espaolas v.2)LaTeX: Straight to double angle quotes v2CTEA*Etiqueta termina con:Label ends with: CTEA,Etiqueta comienza con:Label starts with: CTEAEtiquetasLabelsCTEA,ltimo archivo cerradoLast closed fileCTEAIzquierdaLeftCTEALicenciaLicenseCTEA EnlaceLinkCTEA"Ajuste de enlaces Link optionsCTEA*Ver contenido del ZIPList ZIP contentCTEA2Fuentes del memo auxiliar Logmemo fontCTEA8Marcar todos los encontradosMark all foundCTEA"Dar primera fechaMark first dateCTEA"Dar segunda fechaMark last dateCTEA DiseoMarkupCTEAMatematicasMathCTEAMiscelnea MiscellaneousCTEAModoModeCTEA 5sesMonthsCTEA$Ciclo lunar on/offMoon mode on/offCTEA.Algoritmo de fase lunarMoon phase algorithmCTEAMorese a InglsMorse To EnglishCTEACdigo Morse Morse codeCTEA8Renombrar mltiples archivos Multi-renameCTEANovedadesNEWSCTEA NombreNameCTEANombre:Name:CTEANavNavCTEA NuevoNewCTEA"Pestaa siguienteNext tabCTEA"Espacio protegidoNon-breaking spaceCTEA Hemisferio norteNorthern hemisphereCTEA NotasNotesCTEAFNmero de das entre las dos fechas Number of days between two datesCTEACantidad:Number:CTEA AbrirOpenCTEA Abrir directorioOpen DirectoryCTEA^Abrir archivo con el nombre especificado arriba-Open a file from the file name provided aboveCTEA.Abrir archivo existenteOpen an existing fileCTEA.Abrir debajo del cursorOpen at cursorCTEAAbrir archivo Open fileCTEAHCalidad en la conversin de imgenesOutput images qualityCTEAPaletasPalettesCTEAPargrafo ParagraphCTEA PegarPasteCTEAPegar bloque Paste blockCTEA\Pegar del portapapeles a la presente seleccin9Paste the clipboard's contents into the current selectionCTEAInsertarPlaceCTEA Pestaa anteriorPrev tabCTEA,Vista previa de imagen Preview imageCTEAFVista previa del color seleccionadoPreview selected colorCTEAImprimirPrintCTEA$Imprimir documentoPrint documentCTEAPerfilesProfilesCTEA$Lista de programas Programs listCTEA CitasQuotesCTEA6RMS para los canales: %1 dBRMS for all channels: %1 dBCTEA$Archivos recientes Recent filesCTEARehacerRedoCTEAActualizarRefreshCTEA*Actualizar directorioRefresh current dirCTEA(Actualizar etiquetasRefresh labelsCTEA,Como expresin regular Regexp modeCTEARecargarReloadCTEA0Recargar en codificacinReload with encodingCTEARemoverRemoveCTEATBorrar despus del separador de cada lnea#Remove after delimiter at each lineCTEAPBorrar antes del separador de cada lnea$Remove before delimiter at each lineCTEA,Eliminar datos del daRemove day recordCTEA$Remover duplicadosRemove duplicatesCTEA(Quitar lneas vacasRemove empty linesCTEAQuitar formatoRemove formattingCTEA8Quitar formato de cada lneaRemove formatting at each lineCTEA,Quitar del diccionarioRemove from dictionaryCTEA0Quitar lneas < tamao NRemove lines < N sizeCTEA0Quitar lneas > tamao NRemove lines > N sizeCTEA2Eliminar espacios finalesRemove trailing spacesCTEACambiar nombreRenameCTEA<Renombrar archivo seleccionadoRename selected fileCTEARepetir Repeat lastCTEA Reemplazar todos Replace allCTEAHReemplazar todo en archivos abiertosReplace all in opened filesCTEA@Reemplazar en nombres de archivoReplace in file namesCTEAReemplazar con Replace withCTEAZRecargar la ltima sesin al siguiente inicio$Restore the last session on start-upCTEAInvertirReverseCTEADerechaRightCTEA$Romanos a arbigosRoman to ArabicCTEAEjecutarRunCTEA"Ejecutar programa Run programCTEAGuardarSaveCTEA2Guardar copia de respaldo Save .bakCTEAGuardar comoSave AsCTEAGuardar ZIPSave ZIPCTEAGuardar como...Save asCTEA.Guardar con otro nombreSave as differentCTEA$Registrar posicin Save positionCTEAGuardar perfil Save profileCTEA Conservar sesin Save sessionCTEAnGuardar archivo actual con el nombre especificado antes9Save the current opened file with the name provided aboveCTEA:Guardar documento en el discoSave the document to diskCTEAHGuardar el documento con otro nombre"Save the document under a new nameCTEA2Primero guarde el archivoSave the file first!CTEA6Guardar una versin fechadaSave timestamped versionCTEAGuardado: %1 Saved: %1CTEA*Escalar en porcentajeScale by percentagesCTEAEscala por lado Scale by sideCTEAEscalar imagen Scale imageCTEAPEscala de imgenes con filtrado bilineal$Scale images with bilinear filteringCTEAScriptsScriptsCTEABsquedaSearchCTEA2Resultados de la bsquedaSearch resultsCTEABuscando... Searching...CTEASeleccionarSelectCTEABSeleccionar por expresin regularSelect by regexpCTEAJPrimero seleccione nombre del archivoSelect the file name first!CTEASesionesSessionsCTEA4Final de lnea estilo UNIXSet UNIX end of lineCTEA:Final de lnea estilo WindowsSet Windows end of lineCTEAJDeclarar como archivo de autoguardadoSet as autosaving fileCTEA@Definir como carpeta de archivosSet as storage fileCTEALFinal de lnea estilo Mac antiguo (CR)Set old Mac end of line (CR)CTEA"Atajos de tecladoShortcutCTEA6Mostrar posicin del cursorShow cursor positionCTEAVMostrar ruta completa en cintillo de ttuloShow full path at window titleCTEA2Mostrar nmeros de lneasShow line numbersCTEA"Mostrar bordes enShow margin atCTEATamao por ladoSize of the sideCTEASnippetsSnippetsCTEAOrdenarSortCTEA,Ordenar segn el largoSort by lengthCTEA4Ordenar sin mays./minsc.Sort case insensitivelyCTEA4Ordenar con mays./minsc.Sort case sensitivelyCTEALOrdenar con mays./minsc. y separador%Sort case sensitively, with separatorCTEAHOrdenar alfabticamente por columnasSort table by column ABCCTEA$Revisar ortografa Spell checkCTEA:Motor de revisin ortogrficaSpell checker engineCTEA,Revisin de ortografaSpell checkingCTEA,Idioma para ortografaSpell-checker languagesCTEA6Inicio de semana en domingoStart week on SundayCTEAIniciar/detener captura del portapapeles en la carpeta de archivos,Start/stop capture clipboard to storage fileCTEA$Sobreponer ventana Stay on topCTEADComillas dobles rectas a anguladasStraight to curly double quotesCTEA2Dobles comillas anguladasStraight to double angle quotesCTEA*Quitar etiquetas HTMLStrip HTML tagsCTEASugerenciaSuggestCTEA.Suma por ltima columnaSum by last columnCTEA&Intercambiar celdas Swap cellsCTEA>Habilitar resaltado de sintaxisSyntax highlighting enabledCTEAcono de TEATEA program iconCTEA2Plantilla de proyecto TEATEA project templateCTEA,Espacios por tabuladorTab width in spacesCTEA "0blasTablesCTEAPlantillas TemplatesCTEAjConservar temporalmente bferes no guardados al salir&Temporary save unsaved buffers on exitCTEA PruebaTestCTEA "5xtoTextCTEA,Estadsitica del textoText statisticsCTEATexto a [X]HTMLText to [X]HTMLCTEAFIF (famoso campo de entrada): sirve para buscar/reemplazar o dar parmetros de funcinCThe famous input field. Use for search/replace, function parametersCTEAXCarpeta invlida, inexistente o no declarada&The storage file is closed or not set.CTEA "emasThemesCTEA8Hay %1 lneas en %2 archivosThere are %1 lines at %2 filesCTEA$Sin hoja de estiloThere is no stylesheet fileCTEAArchivo abierto para solo lectura. Puede guardarlo con un nombre distinto usando Guardar como...aThis file is opened in the read-only mode. You can save it with another name using Save asCTEAHoraTimeCTEAFormato de hora Time formatCTEAA5 (*);;"5:AB>2K5 D09;K (*.txt);;$09;K @07<5B:8 (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)Todos (*);;Archivos de texto (*.txt);;Archivos con marcas (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)CTEA6Cambiar a pantalla completaToggle fullscreenCTEA2Cambiar encabezado/fuenteToggle header/sourceCTEAFCambiar ajuste de lneas automticoToggle word wrapCTEAHerramientasToolsCTEAXTamao total = %1 kbytes en %2 archivos<br>&Total size = %1 kbytes in %2 files
CTEA6Idioma de UI (reinicie TEA) UI language (TEA restart needed)CTEA,Modo UI (reinicie TEA)UI mode (TEA restart needed)CTEA6Estilo de UI (reinicie TEA)UI style (TEA restart needed)CTEA4UNITAZ: orden por cantidadUNITAZ quantity sortingCTEA0UNITAZ: orden alfabticoUNITAZ sorting by alphabetCTEA:UNITAZ ordenadas por longitudUNITAZ sorting by lengthCTEALUNITAZ: Analizador Universal de Textos UNITAZ: UNIverlsal Text AnalyZerCTEAMAYSCULASUPCASECTEA4Quitar sangra (shift-tab)Un-indent (shift+tab)CTEASubrayado UnderlineCTEADeshacerUndoCTEADesmarcarUnmarkCTEAJDescomprimir ZIP en directorio actualUnpack ZIP to current directoryCTEAHExcluir como archivo de autoguardadoUnset the autosaving fileCTEA ArribaUpCTEA:Alt para ir al men principalUse Alt key to access main menuCTEANOrientacin EXIF en visor de imgenes$Use EXIF orientation at image viewerCTEAPUtilizar Enca para detectar codificacinUse Enca for charset detectionCTEAHAlt Izq + WASD como cursor opcional-Use Left Alt + WASD as additional cursor keysCTEA8Visor de imagenes externo F2 Use external image viewer for F2CTEALUtilizar el joystick para mover cursorUse joystick as cursor keysCTEA2Espacios en lugar de tabsUse spaces instead of tabsCTEA^Ventanas de dilogo tradicionales Guardar/Abrir&Use traditional File Save/Open dialogsCTEAVerViewCTEA,Ajustes de galera webWeb gallery optionsCTEA$Palabras completas Whole wordsCTEA(Longitud de palabra: Word length: CTEA Ajuste de lneas Word wrapCTEA(Longitud de palabras Words lengthsCTEAAosYearsCTEAlIntenta renombrar un archivo abierto, primero cirrelo@You are trying to rename the opened file, please close it first!CTEAZIPZIPCTEA`Comprimido ZIP: caracteres de nombres de archivoZIP packing: file names charsetCTEAfExtraccin de ZIP: caracteres de nombres de archivo!ZIP unpacking: file names charsetCTEA6Nombres de archivo con ceroZero pad file namesCTEAZComprimir directorio con imgenes convertidas#Zip directory with processed imagesCTEA(Herramientas [X]HTML [X]HTML toolsCTEA(bits por muestra: %1bits per sample: %1CTEA>no se puede abrir %1 por que %2cannot open %1 because of: %2CTEAcaracteres: %1<br>Aaracteres sin espacios: %2<br>lneas: %3<br>autor(es): %4Ichars: %1
chars without spaces: %2
lines: %3
author's sheets: %4CTEAconfigsconfigsCTEA fechasdatesCTEA8dd decimales > dd mm' ss''dec degrees > deg min secCTEA:dd mm' ss'' > dd decimalesdeg min sec > dec degreesCTEAno coincidedoes notCTEA editoreditorCTEA<milisegundos transcurridos: %1elapsed milliseconds: %1CTEA.famoso campo de entradafamous input fieldCTEA,nombre del archivo: %1 file name: %1CTEAarchivosfilesCTEA.ltima modificacin: %1last modified: %1CTEAmemo auxiliarlogmemoCTEAminsculas lower caseCTEA manualmanualCTEAAoincidematchedCTEA nuevonewCTEAarchivo nuevo new_archiveCTEA directorio nuevo new_directoryCTEAperfil nuevo new_profileCTEA(eso no se encontr! not found!CTEAnueva sesin nueva_sesiónCTEA*nmero de canales: %1number of channels: %1CTEAajustesoptionsCTEAponga sus anotaciones sobre este archivo y se guardarn automticamenteHput your notes (for this file) here and they will be saved automaticallyCTEA(tasa de muestreo: %1sample rate: %1CTEAscriptsscriptsCTEA"tamao: %1 kbytessize: %1 kbytesCTEA*snippet %1 no existesnippet %1 is not existsCTEAsnippetssnippetsCTEAsuma: %1sum: %1CTEA tablastablesCTEAplantillas templatesCTEA(0nlis del texto: %1text analysis of: %1CTEA$total / nicas: %1total / unique: %1CTEA\diferencia porcentual entre total y nicas: %1!total to unique per cent diff: %1CTEA,nmero de palabras: %1words total: %1CTEAHpalabras nicas (sin repeticin): %1words unique: %1CTEA:aqu puede poner notas, etc. you can put here notes, etcCTEA@Imposible guardar en ese formato'Saving for this format is not supported CTioReadOnly*Atencin! Atencin!Attention! Attention!CTodoescalado a: scaled to:  CZORWindowModificado en Modified atQObject NombreNameQObjectzInstalar diccionarios para ortografa en Opciones - FuncionesEPlease set up spell checker dictionaries at Options - Functions pageQObject TamaoSizeQObject4Error de ZIP/UNZIP API %1ZIP/UNZIP API error %1 QuaZipFiletea-qt-63.3.0/translations/es.ts000066400000000000000000005050321476733534200165210ustar00rootroot00000000000000 CAboutWindow Code Código Acknowledgements Agradecimientos Translations Traducciones Packages Paquetes About Acerca de CDarkerWindow Darker palette Paleta oscura CDocument cannot open %1 because of: %2 No se pudo abrir %1 porque %2 cannot save %1 because of: %2 No se pudo guardar %1 porque %2 %1 is saved %1 ha sido guardado %1 has been modified. Do you want to save your changes? %1 ha sido modificado. ¿Desea conservar los cambios? %1 is open %1 está abierto new[%1] nuevo[%1] CFontBox Example string Cadena de muestra Font gallery Catálogo de fuentes CImgViewer preview vista previa CTEA The famous input field. Use for search/replace, function parameters FIF (famoso campo de entrada): sirve para buscar/reemplazar o dar parámetros de función FIF FIF editor editor logmemo memo auxiliar famous input field famoso campo de entrada Todos (*);;Archivos de texto (*.txt);;Archivos con marcas (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Все (*);;Текстовые файлы (*.txt);;Файлы разметки (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Charset Codificación This file is open in the read-only mode. You can save it with another name using <b>Save as</b> Archivo abierto para solo lectura. Puede guardarlo con otro nombre mediante <b>Guardar como...</b> %1 already exists Do you want to overwrite? %1 ya existe ¿Desea sobreescribirlo? Test Prueba Files Archivos Labels Etiquetas New Nuevo Create a new file Crear nuevo archivo Open file Abrir archivo Open an existing file Abrir archivo existente Save Guardar Save the document to disk Guardar documento en el disco Save As Guardar como Save the document under a new name Guardar el documento con otro nombre Exit Salir Exit the application Abandonar la aplicación Cut Cortar Cut the current selection's contents to the clipboard Enviar selección al portapapeles Copy Copiar Copy the current selection's contents to the clipboard Copiar selección al portapapeles Paste Pegar Paste the clipboard's contents into the current selection Pegar del portapapeles a la presente selección Undo Deshacer Redo Rehacer About Acerca de About Qt Acerca de Qt File Archivo Open Abrir Last closed file Último archivo cerrado Open at cursor Abrir debajo del cursor Crapbook Registro de tonterías Notes Notas Save as different Guardar con otro nombre Save .bak Guardar copia de respaldo Save timestamped version Guardar una versión fechada Save session Conservar sesión File actions Acciones en el archivo Reload Recargar Reload with encoding Recargar en codificación <b>TEA %1</b> by Peter Semiletov | tea.ourproject.org<br>Support TEA on www.patreon.com/semiletov<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-git <b>TEA %1</b> por Peter Semiletov | tea.ourproject.org<br>Apoye a TEA en www.patreon.com/semiletov<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-git Set UNIX end of line Final de línea estilo UNIX Set Windows end of line Final de línea estilo Windows Set old Mac end of line (CR) Final de línea estilo Mac antiguo (CR) Recent files Archivos recientes Bookmarks Marcadores Edit bookmarks Editar marcadores Add to bookmarks Agregar marcador Find obsolete paths Hallar trayectorias erróneas Templates Plantillas Sessions Sesiones Configs Configs Bookmarks list Listado de marcadores Programs list Lista de programas Do not add to recent No agregar a Archivos recientes Print Imprimir Close current Cerrar este archivo Edit Editar Block start Inicio del bloque Block end Final del bloque Copy block Copiar bloque Paste block Pegar bloque Cut block Cortar bloque Copy current file name Copiar este nombre de archivo Indent (tab) Sangría (tab) Un-indent (shift+tab) Quitar sangría (shift-tab) Indent by first line Sangría de primera línea Comment selection Comentar selección Set as storage file Definir como carpeta de archivos Copy to storage file Copiar a la carpeta de archivos Start/stop capture clipboard to storage file Iniciar/detener captura del portapapeles en la carpeta de archivos Markup Diseño Mode Modo Header Encabezado Align Alineación Center Сentrado Left Izquierda Right Derecha Justify Justificado Bold Negritas Italic Itálicas Underline Subrayado Link Enlace Paragraph Parágrafo Color Color Break line Salto de línea Non-breaking space Espacio protegido Insert image Insertar imagen [X]HTML tools Herramientas [X]HTML Text to [X]HTML Texto a [X]HTML Convert tags to entities Convertir etiquetas en entidades Antispam e-mail Protección contra correo basura Document weight Tamaño del documento Preview selected color Vista previa del color seleccionado Strip HTML tags Quitar etiquetas HTML Rename selected file Renombrar archivo seleccionado Search Búsqueda Find Buscar Find next Buscar siguiente Find previous Buscar atrás Find in files Buscar en archivos Replace with Reemplazar con Replace all Reemplazar todos Replace all in opened files Reemplazar todo en archivos abiertos Mark all found Marcar todos los encontrados Unmark Desmarcar Case sensitive Reconocer mayús./minús. Whole words Palabras completas From cursor Desde el cursor Regexp mode Como expresión regular Fuzzy mode Modo difuso Functions Funciones Repeat last Repetir Tools Herramientas Scale image Escalar imagen Plugins Plugins Snippets Snippets Scripts Scripts Tables Таblas Place Insertar TEA project template Plantilla de proyecto TEA HTML template Plantilla HTML HTML5 template Plantilla HTML5 C++ template Plantilla C++ C template Plantilla C Date Fecha Time Hora Case Tipo de letra UPCASE MAYÚSCULAS lower case minúsculas Sort Ordenar Sort case sensitively Ordenar con mayús./minúsc. Sort case insensitively Ordenar sin mayús./minúsc. Sort case sensitively, with separator Ordenar con mayús./minúsc. y separador Sort by length Ordenar según el largo Flip a list Invertir lista Flip a list with separator Invertir lista delimitada con separador Cells Celdas Sort table by column ABC Ordenar alfabéticamente por columnas Swap cells Intercambiar celdas Delete by column Borrar por columnas Copy by column[s] Copiar por columna(s) Filter Filtro Remove duplicates Remover duplicados Remove empty lines Quitar líneas vacías Remove lines < N size Quitar líneas < tamaño N Remove lines > N size Quitar líneas > tamaño N Remove before delimiter at each line Borrar antes del separador de cada línea Remove after delimiter at each line Borrar después del separador de cada línea Filter with regexp Filtrar por expresión regular Filter by repetitions Filtrar por repeticiones Math Matematicas Evaluate Evaluar Arabic to Roman Arábigos a romanos Roman to Arabic Romanos a arábigos Decimal to binary Decimal a binario Binary to decimal Binario a decimal Flip bits (bitwise complement) Invertir bits Enumerate Enumerar Sum by last column Suma por última columna deg min sec > dec degrees dd° mm' ss'' > dd° decimales dec degrees > deg min sec dd° decimales > dd° mm' ss'' Morse code Código Morse From Russian to Morse Ruso a Morse From Morse To Russian Morse a Ruso From English to Morse Inglés a Моrse Morse To English Morese a Inglés Analyze Análisis Text statistics Estadísitica del texto Extract words Extraer palabras Words lengths Longitud de palabras Count the substring Conteo de subcadenas Count the substring (regexp) Conteo de subcadenas por exp. regular UNITAZ quantity sorting UNITAZ: orden por cantidad UNITAZ sorting by alphabet UNITAZ: orden alfabético UNITAZ sorting by length UNITAZ ordenadas por longitud Text Теxto Apply to each line Aplicar a cada línea Remove formatting Quitar formato Remove formatting at each line Quitar formato de cada línea Compress Сomprimir Anagram Аnаgrаma Remove trailing spaces Eliminar espacios finales Escape regexp Escape regexp Reverse Invertir Compare two strings Сomparar dos cadenas Quotes Citas Straight to double angle quotes Dobles comillas anguladas Straight to curly double quotes Comillas dobles rectas a anguladas LaTeX: Straight to curly double quotes LaTeX:comillas rectas a inglesas LaTeX: Straight to double angle quotes LaTeX: comillas rectas a españolas v.1 LaTeX: Straight to double angle quotes v2 LaTeX: comillas rectas a españolas v.2 Spell-checker languages Idioma para ortografía Spell check Revisar ortografía Suggest Sugerencia Add to dictionary Añadir al diccionario Remove from dictionary Quitar del diccionario Calendar Calendario Moon mode on/off Ciclo lunar on/off Mark first date Dar primera fecha Mark last date Dar segunda fecha Add or subtract Sumar o restar Days Días Months Меses Years Años Go to current date Ir a la fecha actual Calculate moon days between dates Calcular días lunares entre las fechas Number of days between two dates Número de días entre las dos fechas Remove day record Eliminar datos del día Run Ejecutar IDE IDE Run program Ejecutar programa Build program Соmpilar programa Clean program Depurar programa Toggle header/source Cambiar encabezado/fuente Nav Nav Save position Registrar posición Go to saved position Volver a posición registrada Go to line Saltar a la línea Next tab Pestaña siguiente Prev tab Pestaña anterior Focus the Famous input field Enfocarse en el FIF Focus the editor Centrarse en el editor Refresh labels Actualizar etiquetas Current files Archivos actuales Fm Fm Multi-rename Renombrar múltiples archivos Zero pad file names Nombres de archivo con cero Delete N first chars at file names Borrar los N primeros caracteres de los nombres Replace in file names Reemplazar en nombres de archivo Apply template Aplicar plantilla File operations Operaciones en archivo Create new directory Сrear nuevo directorio Rename Cambiar nombre Delete file Eliminar archivo File information Información sobre el archivo Count lines in selected files Conteo de líneas en archivos seleccionados Full info Información detallada Checksum Suma de comprobación ZIP ZIP Create new ZIP Сraer nuevo ZIP Add to ZIP Añadir al ZIP Save ZIP Guardar ZIP List ZIP content Ver contenido del ZIP Unpack ZIP to current directory Descomprimir ZIP en directorio actual Images Imágenes Scale by side Escala por lado Scale by percentages Escalar en porcentaje Create web gallery Сrear galería web Go to home dir Ir al directorio personal Refresh current dir Actualizar directorio Preview image Vista previa de imagen Select by regexp Seleccionar por expresión regular Deselect by regexp Quitar selección por expresión regular View Ver Themes Тemas Palettes Paletas Highlighting mode Modo resaltado Profiles Perfiles Save profile Guardar perfil Toggle word wrap Cambiar ajuste de líneas automático Hide error marks Ocultar marcas de error Toggle fullscreen Cambiar a pantalla completa Stay on top Sobreponer ventana Darker Modo oscuro NEWS Novedades License Licencia not found! ¡eso no se encontró! This file is opened in the read-only mode. You can save it with another name using <b>Save as</b> Archivo abierto para solo lectura. Puede guardarlo con un nombre distinto usando Guardar como... %1 is saved %1 ha sido guardado The storage file is closed or not set. Carpeta inválida, inexistente o no declarada You are trying to rename the opened file, please close it first! Intenta renombrar un archivo abierto, primero ciérrelo matched сoincide does not no coincide <b>TEA %1</b> by Peter Semiletov | tea.ourproject.org<br>Support TEA on www.patreon.com/semiletov<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt <b>TEA %1</b> por Peter Semiletov | tea.ourproject.org<br> Apoye el desarrollo de TEA en www.patreon.com/semiletov<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt Set as autosaving file Declarar como archivo de autoguardado Unset the autosaving file Excluir como archivo de autoguardado Capitalize sentences Oraciones en mayúsculas Check regexp match Verificar coincidencias por exp. regular options ajustes Classic Clásico Docked Acoplado UI mode (TEA restart needed) Modo UI (reinicie TEA) UI language (TEA restart needed) Idioma de UI (reinicie TEA) UI style (TEA restart needed) Estilo de UI (reinicie TEA) Interface font Fuente de interface Editor font Fuentes del editor Logmemo font Fuentes del memo auxiliar Up Arriba Bottom Abajo GUI tabs align Alinear pestañas Documents tabs align Alinear pestañas de documentos Icons size Tamaño de íconos TEA program icon Ícono de TEA FIF at the top (restart needed) FIF en la parte superior (reincio obligatorio) Show line numbers Mostrar números de líneas Word wrap Ajuste de líneas Syntax highlighting enabled Habilitar resaltado de sintaxis Highlight current line Destacar línea actual Highlight paired brackets Destacar emparejado de paréntesis Automatic indent Sangrías automáticas Use spaces instead of tabs Espacios en lugar de tabs Tab width in spaces Espacios por tabulador Show cursor position Mostrar posición del cursor Cursor center on scroll Centrar cursor en desplazamiento Cursor blink time (msecs, zero is OFF) Tiempo de parpadeo del cursor en мs (cero para desactivarlo) Cursor width Grosor del cursor Show margin at Mostrar bordes en Show full path at window title Mostrar ruta completa en cintillo de título Interface Interface Use Alt key to access main menu Alt para ir al menú principal Use Left Alt + WASD as additional cursor keys Alt Izq + WASD como cursor opcional Use joystick as cursor keys Utilizar el joystick para mover cursor Automatic preview images at file manager Visualización automática de imágenes en el gestor de archivos Restore the last session on start-up Recargar la última sesión al siguiente inicio Use Enca for charset detection Utilizar Enca para detectar codificación Use external image viewer for F2 Visor de imagenes externo F2 Use traditional File Save/Open dialogs Ventanas de diálogo tradicionales Guardar/Abrir Start week on Sunday Inicio de semana en domingo Northern hemisphere Hemisferio norte Moon phase algorithm Algoritmo de fase lunar 0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - Leueshkanov 0 - Trigonométrico v.2, 1 - Trigonométrico v.1, 2 - Conway, 3 - Leueshkanov Charset for file open from command line Codificación para abrir archivos desde la línea de comandos ZIP unpacking: file names charset Extracción de ZIP: caracteres de nombres de archivo ZIP packing: file names charset Comprimido ZIP: caracteres de nombres de archivo Autosaving Аutoguardado Temporary save unsaved buffers on exit Conservar temporalmente búferes no guardados al salir Autosave buffers and autosaving files (each N seconds) Guardar búferes y archivos de autoguardado cada N segundos Common General Label starts with: Etiqueta comienza con: Label ends with: Etiqueta termina con: Date and time Fecha y hora Date format Formato de fecha Time format Formato de hora Spell checking Revisión de ortografía Spell checker engine Motor de revisión ortográfica Hunspell dictionaries directory Directorio de diccionarios Hunspell Select Seleccionar Aspell directory Directorio Aspell Miscellaneous Miscelánea Fuzzy search factor Coeficiente de búsqueda difusa Image conversion output format Formato de salida para imágenes convertidas Scale images with bilinear filtering Escala de imágenes con filtrado bilineal Output images quality Calidad en la conversión de imágenes Apply hard rotation by EXIF data Aplicar rotación rigurosa por datos EXIF Zip directory with processed images Comprimir directorio con imágenes convertidas Web gallery options Ajustes de galería web Size of the side Tamaño por lado Link options Ajuste de enlaces Columns per row Columnas en fila EXIF EXIF Use EXIF orientation at image viewer Orientación EXIF ​​en visor de imágenes Shortcut Atajos de teclado Assign Asignar Remove Remover Keyboard Teclado manual manual you can put here notes, etc aquí puede poner notas, etc. snippet %1 is not exists snippet %1 no existe Print document Imprimir documento %p% completed %p% completado elapsed milliseconds: %1 milisegundos transcurridos: %1 %1 - saved %1 - guardado Cannot save %1 No se pudo guardar %1 chars: %1<br>chars without spaces: %2<br>lines: %3<br>author's sheets: %4 caracteres: %1<br>сaracteres sin espacios: %2<br>líneas: %3<br>autor(es): %4 %1 is processed and saved %1 procesado y guardado #external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s" #listado de programas externos. Un ejemplo: opera="C:\Program Files\Opera\opera.exe " "%s" #external programs list. example: ff=firefox file:///%s #listado de programas externos. Ejemplo: ff=firefox file:///%s Save the file first! Primero guarde el archivo templates plantillas snippets snippets scripts scripts tables tablas configs configs Enter the name Introduzca el nombre Name: Nombre: new_directory directorio nuevo nueva_sesión nueva sesión <span style="%1">COLOR SAMPLE</span> <span style="%1">COLOR MUESTRA</span> Name Nombre Go Ir Home Directorio personal Refresh Actualizar Operations Acciones Guess encoding! Detectar codificación Save as Guardar como... <b>Bookmarks</b> <b>Marcadores</b> files archivos It seems that %1 contains TEA plugin. Do you want to install it? Al parecer, % 1 contiene un plugin TEA. ¿Quiere instalarlo? Cannot unzip and install plugin Imposible descomprimir e instalar el plugin new nuevo Are you sure to delete %1? ¿Está seguro que desea borrar %1? MD5 checksum for %1 is %2 La suma de comprobación MD5 de % 1 es la siguiente: %2 MD4 checksum for %1 is %2 La suma de comprobación MD4 de %1 es %2 SHA-1 checksum for %1 is %2 La suma de comprobación SHA-1 de %1 es %2 SHA-2 SHA-224 checksum for %1 is %2 La suma de comprobación SHA-2 SHA-224 de %1 es %2 SHA-2 SHA-384 checksum for %1 is %2 La suma de comprobación SHA-2 SHA-384 de %1 es %2 SHA-2 SHA-256 checksum for %1 is %2 La suma de comprobación SHA-2 SHA-256 de %1 es %2 SHA-2 SHA-512 checksum for %1 is %2 La suma de comprobación SHA-2 SHA-512 de %1 es %2 SHA-3 224 checksum for %1 is %2 La suma de comprobación SHA-3 224 de %1 es %2 SHA-3 256 checksum for %1 is %2 La suma de comprobación SHA-3 256 de %1 es %2 SHA-3 384 checksum for %1 is %2 La suma de comprobación SHA-3 384 de %1 es %2 SHA-3 512 checksum for %1 is %2 La suma de comprobación SHA-3 512 de %1 es %2 Keccak 224 checksum for %1 is %2 Проверочная сумма Keccak 224 для %1 is %2 Keccak 256 checksum for %1 is %2 La suma de comprobación Keccak 256 de %1 es %2 La suma de comprobación Keccak 384 de %1 es %2 Проверочная сумма Keccak 384 для %1 is %2 Keccak 512 checksum for %1 is %2 Проверочная сумма Keccak 512 для %1 is %2 End of line: Final de línea: file name: %1 nombre del archivo: %1 size: %1 kbytes tamaño: %1 kbytes last modified: %1 última modificación: %1 bits per sample: %1 bits por muestra: %1 number of channels: %1 número de canales: %1 sample rate: %1 tasa de muestreo: %1 RMS for all channels: %1 dB RMS para los canales: %1 dB Actions Acciones Open a file from the file name provided above Abrir archivo con el nombre especificado arriba Save the current opened file with the name provided above Guardar archivo actual con el nombre especificado antes GTK Bookmarks: Marcadores GTK: total to unique per cent diff: %1 diferencia porcentual entre total y únicas: %1 total / unique: %1 total / únicas: %1 words unique: %1 palabras únicas (sin repetición): %1 words total: %1 número de palabras: %1 text analysis of: %1 аnális del texto: %1 UNITAZ: UNIverlsal Text AnalyZer UNITAZ: Analizador Universal de Textos %1 number of occurrences of %2 is found %1 coincidencias de %2 encontradas Enter the archive name Introduzca nombre del archivo new_archive archivo nuevo Open Directory Abrir directorio Getting files list... Obteniendo listado de archivos... Searching... Buscando... cannot open %1 because of: %2 no se puede abrir %1 por que %2 Search results Resultados de la búsqueda new_profile perfil nuevo There are %1 lines at %2 files Hay %1 líneas en %2 archivos Enca is not installed, falling back to the built-in detection Enca no está instalado, regreso al algoritmo de detección incorporado Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatre Escriba sus notas aquí. Para habilitar el recordatorio, use el formato de tiempo de 24 horas [hh:mm], es decir: [06:00] ¡Buenos días! [20:10] ¡Es hora de ir al cine! Enter your daily notes here. Escriba sus anotaciones diarias aquí. dates fechas Select the file name first! Primero seleccione nombre del archivo %1 is not found<br> %1 no localizado<br> %1 kbytes %2 <br> %1 kbytes %2 <br> Total size = %1 kbytes in %2 files<br> Tamaño total = %1 kbytes en %2 archivos<br> is unpacked descomprimidos Trigonometric 2 Trigonométrico 2 Trigonometric 1 Trigonométrico 1 Conway Conway Leueshkanov Leueshkanov Word length: Longitud de palabra: Number: Cantidad: put your notes (for this file) here and they will be saved automatically ponga sus anotaciones sobre este archivo y se guardarán automáticamente There is no stylesheet file Sin hoja de estilo There is no plugin file Sin archivo de plugin <b>Error:</b> <b>Error:</b> sum: %1 suma: %1 Incorrect parameters at FIF FIF: parámetros no válidos Cannot save: %1 No se puede guardar: %1 Saved: %1 Guardado: %1 CTioReadOnly saving of this format is not supported no se puede guardar en este formato Saving for this format is not supported Imposible guardar en ese formato CTodo Attention! Attention! ¡Atención! ¡Atención! CViewerWindow preview vista previa CZORWindow scaled to escalado a scaled to: escalado a: QObject Name Nombre Size Tamaño Modified at Modificado en Please set up spell checker dictionaries at Options - Functions page Instalar diccionarios para ortografía en Opciones - Funciones QuaZipFile ZIP/UNZIP API error %1 Error de ZIP/UNZIP API %1 tea Create a new file Сrear nuevo archivo Open an existing file Abrir archivo existente Save the document to disk Guardar documento en el disco Save the document under a new name Guardar documento con otro nombre Exit the application Salir de la aplicación Cut the current selection's contents to the clipboard Enviar selección al portapapeles Copy the current selection's contents to the clipboard Copiar selección al portapapeles Paste the clipboard's contents into the current selection Pegar del portapapeles a la selección Open at cursor Abrir debajo del cursor Save .bak Guardar copia de respaldo .bak Insert image Insertar imagen Find Buscar Find next Buscar siguiente Find previous Buscar hacia atrás Case Letras mayús./minúsc. File Archivo Edit Editar Ready Listo Editor font Editor de fuentes UI style Estilo de interface Word wrap Ajuste de líneas Charset Codificación Test Prueba New Nuevo Open Abrir Save Guardar FIF FIF Save As Guardar como... Exit Salir Cut Cortar Copy Copiar Paste Pegar Undo Deshacer Redo Rehacer About Acerca de About Qt Acerca de Qt Crapbook Registro de tonterías Recent files Archivos recientes Print Imprimir Markup Diseño Search Búsqueda Functions Funciones Filter with regexp Filtrar por expresión regular Apply to each line Aplicar a cada línea Reverse Invertir UPCASE MAYÚSCULAS lower case minúsculas Add to dictionary Agregar al diccionario Suggest Sugerencia Spell check Revisión ortográfica Spell-checker languages Idioma para ortografía Nav Nav Save position Registrar posición Go to saved position Ir a posición registrada Go to line Saltar a la línea View Ver Help Ayuda Assign Asignar Remove Remover Keyboard Teclado edit editar Save as different Guardar diferente Save timestamped version Guardar versión fechada Templates Plantillas Bookmarks Marcadores <b>TEA %1 @ http://tea-editor.sourceforge.net</b><br>by Peter Semiletov (peter.semiletov@gmail.com)<br>read the Manual under the <i>Learn</i> tab! <b>TEA %1 @ http://tea-editor.sourceforge.net</b><br>разработка Peter Semiletov (peter.semiletov@gmail.com)<br>consulte el manual en la pestaña <i>Aprender</i>! All (*);;Text files (*.txt);;Markup files (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Todos (*);;Archivos de texto (*.txt);;Archivos con marcas (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Labels Etiquetas Notes Notas Set UNIX end of line Final de línea estilo UNIX Set Windows end of line Final de línea estilo Windows Set traditional Mac end of line Final de línea estilo Mac antiguo Edit bookmarks Editar marcadores Add to bookmarks Agregar en marcadores Find obsolete paths Наllar rutas inválidas Close current Cerrar archivo actual Copy current file name Copiar nombre de archivo actual Indent by first line Sangría de primera línea Comment selection Comentar selección Variants Variantes Set as storage file Definir como archivo de guardado Copy to storage file Copiar a la carpeta de archivos Start/stop capture clipboard to storage file Iniciar/detener captura del portapapeles en la carpeta de archivos Mode Modo Header Encabezado Align Alineación Center Сеntrado Justify Justificado [X]HTML tools Herramientas [X]HTML Text to [X]HTML Texto a [X]HTML Convert tags to entities Convertir etiquetas en entidades Antispam e-mail Protección contra correo basura Document weight Tamaño del documento Strip HTML tags Quitar etiquetas HTML Rename selected file Renombrar archivo seleccionado Find in files Bucar en archivos Replace with Reemplazar con Replace all Reemplazar todos Replace all in opened files Reemplazar en todos los archivos abiertos Mark all Marcar todos los encontrados Unmark Desmarcar From cursor Desde el cursor Regexp mode Como expresión regular Fuzzy mode Modo disfuso Repeat last Repetir Tools Herramientas Scale image Escalar imagen Plugins Plugins Snippets Snippets Tables Таblas TEA project template Plantilla de proyecto TEA HTML5 template Plantilla HTML5 C++ template Plantilla C++ C template Plantilla C Date Fecha Time Hora Sort Ordenar Sort case sensitively Ordenar con mayús./minúsc. Sort case sensitively, with separator Ordenar con mayús./minúsc. y separador Sort by length Ordenar según el largo Flip a list Invertir lista Flip a list with separator Invertir lista delimitada con separador Cells Celdas Sort table by column ABC Ordenar alfabéticamente por columnas Swap cells Intercambiar celdas Delete by column Borrar por columnas Copy by column[s] Copiar por columna(s) Filter Filtro Remove duplicates Remover duplicados Remove empty lines Quitar líneas vacías Remove lines < N size Quitar líneas < tamaño N Remove lines > N size Quitar líneas > tamaño N Remove before delimiter at each line Borrar antes del separador de cada línea Remove after delimiter at each line Borrar después del separador de cada línea Filter by repetitions Filtrar por repeticiones Decimal to binary Decimal a binario Binary to decimal Binario a decimal Flip bits (bitwise complement) Invertir bits Sum by last column Suma por última columna deg min sec > dec degrees dd° mm' ss'' > dd° decimales dec degrees > deg min sec dd° decimales > dd° mm' ss'' Morse code Código Morse From Russian to Morse Ruso a Morse From Morse To Russian Morse a Ruso From English to Morse Inglés a Morse From Morse To English Morse a Inglés Text statistics Estadística del texto Extract words Extraer palabras Words lengths Longitud de palabras Remove formatting at each line Quitar formato de cada línea Compress Сomprimir Anagram Аnagrama Compare two strings Сравнить два слова Сomparar dos cadenas IDE IDE Run program Ejecutar programa Build program Соmpilar programa Clean program Depurar programa Multi-rename Renombrar múltiples archivos Zero pad file names Nombres de archivo con cero Delete N first chars at file names Borrar los N primeros caracteres de los nombres Replace in file names Reemplazar en nombres de archivo Apply template Aplicar plantilla Checksum Suma de comprobación Themes Теmas not found! ¡eso no se encontró! options ajustes UI style (restart needed) Estilo UI (reiniciar TEA) Logmemo font Fuentes memo auxiliar GUI tabs align Alinear pestañas TEA program icon Ícono de TEA FIF at the top (restart needed) FIF en la parte superior (reincio obligatorio) Use colored console output (can crash) Salida de consola a color (puede fallar) Syntax highlighting enabled Habilitar resaltado de sintaxis Cursor width Grosor del cursor Show full path at window title Mostrar ruta completa en cintillo de título Use Alt key to access main menu Alt para ir al menú principal Use Left Alt + WASD as additional cursor keys Alt Izq - WASD como cursor opcional Use joystick as cursor keys Utilizar el joystick para mover cursor Apply hard rotation by EXIF data Aplicar rotación rigurosa por datos EXIF Use EXIF orientation at image viewer Orientación EXIF ​​en visor de imágenes manual manual #external programs list. example: ff=firefox file:///%s #listado de programas externos. Ejemplo: ff=firefox file:///%s Save the file first! Primero guarde el archivo Guess encoding! Detectar codificación files archivos It seems that %1 contains TEA plugin. Do you want to install it? Al parecer, % 1 contiene un plugin TEA. ¿Quiere instalarlo? Cannot unzip and install plugin Imposible descomprimir e instalar el plugin SHA-1 checksum for %1 is %2 Suma de comprobación SHA-1 de %1 es %2 SHA-2 SHA-224 checksum for %1 is %2 Suma de comprobación SHA-2 SHA-224 de %1 es %2 SHA-2 SHA-384 checksum for %1 is %2 Suma de comprobación SHA-2 SHA-384 de %1 es %2 SHA-2 SHA-256 checksum for %1 is %2 Suma de comprobación SHA-2 SHA-256 de %1 es %2 SHA-2 SHA-512 checksum for %1 is %2 Suma de comprobación SHA-2 SHA-512 de %1 es %2 SHA-3 224 checksum for %1 is %2 Suma de comprobación SHA-3 224 de %1 es %2 SHA-3 256 checksum for %1 is %2 Suma de comprobación SHA-3 256 de %1 es %2 SHA-3 384 checksum for %1 is %2 Suma de comprobación SHA-3 384 de %1 es %2 SHA-3 512 checksum for %1 is %2 Suma de comprobación SHA-3 512 de %1 es %2 Keccak 224 checksum for %1 is %2 Suma de comprobación 224 de %1 es %2 Keccak 256 checksum for %1 is %2 Suma de comprobación Keccak 256 de %1 es %2 Keccak 384 checksum for %1 is %2 Suma de comprobación 384 de %1 es %2 Keccak 512 checksum for %1 is %2 Suma de comprobación Keccak 512 de %1 es %2 last modified: %1 última modificación: %1 Enca is not installed, falling back to the built-in detection Enca no está instalado, regreso al algoritmo de detección incorporado put your notes (for this file) here and they will be saved automatically ponga sus anotaciones sobre este archivo y se guardarán automáticamente There is no stylesheet file Sin hoja de estilo sum: %1 suma: %1 Incorrect parameters at FIF FIF: parámetros erróneos Cannot save: %1 No se puede guardar: %1 Saved: %1 Guardado: %1 put your notes here and they will be saved automatically ponga sus anotaciones aquí y se guardarán automáticamente There is no plugin file Sin archivo de plugin <b>Error:</b> <b>Error:</b> variants variantes Word length: Longitud de palabra: Number: Cantidad: variants file %1 is not exist variantes del archivo % 1 inexistentes put your notes here anote aquí Double quotes to TeX double quotes Comillas a comillas dobles TeX Double quotes to TeX Russian quotes << >> Comillas a comillas dobles TeX a la rusa << >> Double quotes to TeX Russian quotes Comillas dobles a comillas TeX a la rusa Moon mode on/off Ciclo lunar on/off Mark first date Dar primera fecha Mark last date Dar segunda fecha Go to current date Ir a la fecha actual Calculate moon days between dates Calcular días lunares entre las fechas Number of days between two dates Número de días entre las dos fechas Instr Instr Font gallery Catálogo de fuentes Refresh labels Actualizar etiquetas Current files Archivos actuales List ZIP content Ver contenido del ZIP Unpack ZIP to current directory Descomprimir ZIP en directorio actual Add user font Agregar fuente propia Old syntax hl engine (restart TEA to apply) Motor de sintaxis hl antiguo (reinicie TEA) Use external image viewer for F2 Visor de imagenes externo F2 Northern hemisphere Hemisferio norte Moon phase algorithm Algoritmo de fase lunar UI tabs align Alinear pestañas Up Arriba Right Derecha Fuzzy search factor Coeficiente de búsqueda difusa snippet %1 is not exists snippet %1 no existe Open Directory Abrir directorio is unpacked descomprimidos Trigonometric 2 Trigonométrico 2 Trigonometric 1 Trigonométrico 1 Conway Conway Leueshkanov Leueshkanov Left Izquierda Documents tabs align Alinear pestañas de documentos Bottom Abajo ZIP unpacking: file names charset Extracción de ZIP: caracteres de nombres de archivo ZIP packing: file names charset Comprimido ZIP: caracteres de nombres de archivo Label starts with: Etiqueta comienza con: Label ends with: Etiqueta termina con: Shortcut Atajos de teclado %1 is processed and saved %1 procesado y guardado Select the file name first! Primero seleccione nombre del archivo Select font Seleccione fuente Fonts (*.ttf *.otf) Fuentes (*.ttf *.otf) Calendar Calendario LaTeX: Straight to curly double quotes LaTeX: comillas rectas a inglesas LaTeX: Straight to double angle quotes LaTeX: LaTeX: comillas rectas a españolas v.1 LaTeX: Straight to double angle quotes v2 LaTeX: comillas rectas a españolas v.2 Add or subtract Sumar o restar Days Días Months Мeses Years Años Remove day record Eliminar datos del día Start week on Sunday Inicio de semana en domingo Miscellaneous Miscelánea End of line: Final de línea: Getting files list... Obteniendo listado de archivos... Searching... Buscando... cannot open %1 because of: %2 no se puede abrir %1 por que %2 Search results Resultados de la búsqueda Files Archivos new_profile perfil nuevo There are %1 lines at %2 files Hay %1 líneas en %2 archivos Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatre Escriba sus notas aquí. Para habilitar el recordatorio, use el formato de tiempo de 24 horas [hh:mm], es decir: [06:00] ¡Buenos días! [20:10] ¡Es hora de ir al cine! Enter your daily notes here. Escriba sus anotaciones diarias aquí. Evaluate Evaluar Next tab Pestaña siguiente Prev tab Pestaña anterior Toggle header/source Cambiar encabezado/fuente Focus the editor Centrarse en el editor Interface font Fuente de interface Icons size Tamaño de íconos Date format Formato de fecha Time format Formato de día learn aprender dates fechas %1 kbytes %2 <br> %1 kbytes %2 <br> Total size = %1 kbytes in %2 files<br> Tamaño total = %1 kbytes en %2 archivos<br> %1 already exists Do you want to overwrite? %1 ya existe ¿Desea sobreescribirlo? Math Matematicas Arabic to Roman Аrábigos a romanos Show cursor position Mostrar posición del cursor %1 - saved %1 - guardado Cannot save %1 No se pudo guardar %1 Configs Configs Bookmarks list Lista de marcadores Programs list Lista de programas HTML template Plantilla HTML Run Ejecutar %1 is saved %1 ha sido guardado Place Insertar The famous input field. Use for search/replace, function parameters. FIF (famoso campo de entrada): sirve para buscar/reemplazar o dar parámetros de función editor editor Bold Negritas Italic Itálicas Link Enlace Paragraph Parágrafo Color Color Break line Salto de línea Non-breaking space Espacio protegido Roman to Arabic Romanos a Arábigos Text Texto Remove formatting Quitar formato #external programs list. example: opera=opera %s #listado de programas externos. Ejemplo: opera=opera %s %1 is not found<br> %1 no localizado<br> Toggle word wrap Cambiar ajuste de líneas automático Hide error marks Ocultar marcas de error Toggle fullscreen Cambiar a pantalla completa Preview with default browser Vista previa en el navegador License Licencia Scripts Scripts NEWS Novedades Home Directorio personal Refresh Actualizar Save as Guardar como... Use traditional File Save/Open dialogs Ventanas de diálogo tradicionales Guardar/Abrir templates plantillas snippets snippets scripts scripts Are you sure to delete %1? ¿Está seguro que desea borrar %1? Name Nombre Go Ir Create new directory Сrear nuevo directorio Quotes to facing quotes Comillas a comillas adornadas Override locale Anular configuración regional Save session Guardar sesión Sessions Sesiones Enumerate Enumerar Stay on top Sobreponer ventana Restore the last session on start-up Recargar la última sesión al siguiente inicio Common General caracteres: %1<br>caracteres sin espacios: %2<br>líneas: %3<br>author's sheets: %4 символов: %1<br>символов без пробелов: %2<br>строк: %3<br>аutor(es): %4 Last closed file Último archivo cerrado Preview selected color Vista previa del color seleccionado Palettes Paletas Highlight current line Destacar línea actual Highlight paired brackets Destacar emparejado de paréntesis <span style="%1">COLOR SAMPLE</span> <span style="%1">COLOR MUESTRA</span> Fm Fm Remove from dictionary Quitar del diccionario File operations Operaciones en archivo Rename Cambiar de nombre Delete file Eliminar archivo File information Información sobre el archivo MD5 checksum Suma de comprobación MD5 MD4 checksum Suma de comprobación MD4 SHA1 checksum Suma de comprobación SHA1 Create web gallery Сrear galería web Go to home dir Ir al directorio personal Refresh current dir Actualizar directorio Show margin at Mostrar bordes en Images Imágenes logmemo memo auxiliar famous input field famoso campo de entrada Count lines in selected files Conteo de líneas en archivos seleccionados Select by regexp Seleccionar por expresión regular Deselect by regexp Quitar selección por expresión regular Profiles Perfiles Save profile Guardar perfil Show line numbers Mostrar números de líneas Web gallery options Ajustes de galería web Size of the side Tamaño de lado Link options Ajuste de enlaces Columns per row Сolumnas por fila Date and time Fecha y hora The famous input field. Use for search/replace, function parameters FIF (famoso campo de entrada): sirve para buscar/reemplazar o dar parámetros de función Spell checking Revisión de ortografía Spell checker engine Motor de revisión ortográfica Hunspell dictionaries directory Directorio de diccionarios Hunspell Select Seleccionar Print document Imprimir documento elapsed milliseconds: %1 milisegundos transcurridos: %1 Enter the name Introduzca el nombre Name: Nombre: Operations Acciones new nuevo Full info Información detallada #external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s" #listado de programas externos. Ejemplo: opera="C:\Program Files\Opera\opera.exe " "%s" file name: %1 nombre del archivo: %1 size: %1 kbytes tamaño: %1 kbytes created: %1 сreado: %1 modified: %1 modificado: %1 File actions Acciones en el archivo Reload Recargar Reload with encoding Recargar en codificación you can put here notes, etc aquí puede poner notas, etc. Analyze Аnálisis total / unique: %1 total / únicas: %1 words unique: %1 palabras únicas (sin repetición): %1 words total: %1 número de palabras: %1 text analysis of: %1 аnális del texto: %1 UNITAZ: UNIverlsal Text AnalyZer UNITAZ: Analizador Universal de Textos Whole words Palabras completas Case sensitive Reconocer mayús./minús. UNITAZ quantity sorting UNITAZ: orden por cantidad UNITAZ sorting alphabet UNITAZ: orden alfabético tune refinar %p% completed %p% completado <b>Bookmarks</b> <b>Marcadores</b> Focus the Famous input field Enfocarse en el FIF Open file Abrir archivo This file is open in the read-only mode. You can save it with another name using <b>Save as</b> Archivo abierto para solo lectura. Puede guardarlo con otro nombre usando <b>Cuardar como...</b> Count the substring Conteo de subcadenas Count the substring (regexp) Conteo de subcadenas por expresión regular MD4 checksum for %1 is %2 Suma de comprobación MD4 de %1 es %2 SHA1 checksum for %1 is %2 Suma de comprobación SHA1 de %1 e %2 total to unique per cent diff: %1 diferencia porcentual entre total y únicas: %1 %1 number of occurrences of %2 is found %1 coincidencias de %2 encontradas MD5 checksum for %1 is %2 Suma de comprobación MD5 de %1 es %2 Set old Mac end of line (CR) Final de línea estilo Mac antiguo (CR) Do not add to recent No agregar a Archivos recientes Block start Inicio del bloque Block end Final del bloque Copy block Copiar bloque Paste block Pegar bloque Cut block Cortar bloque Mark all found Marcar todos los encontrados UNITAZ sorting by alphabet UNITAZ: orden alfabético UNITAZ sorting by length UNITAZ: orden por longitud Remove trailing spaces Eliminar espacios finales Escape regexp Escape regexp Quotes Citas Straight to double angle quotes Comillas rectas dobles a angulares Straight to curly double quotes Comillas rectas a españolas ZIP ZIP Create new ZIP Сrear nuevo ZIP Add to ZIP Añadir al ZIP Save ZIP Guardar ZIP Scale by side Escalar por lado Scale by percentages Escalar en porcentaje Darker Modo oscuro Classic Clásico Docked Acoplado UI mode (TEA restart needed) Modo UI (reinicie TEA) UI language (TEA restart needed) Idioma (reinicie TEA) UI style (TEA restart needed) Estilo de UI (reinicie TEA) Cursor center on scroll Centrar cursor en desplazamiento Cursor blink time (msecs, zero is OFF) Tiempo de parpadeo del cursor en мs (cero para desactivarlo) Interface Interface Use Enca for charset detection Utilizar Enca para detectar codificación Charset for file open from command line Codificación para abrir archivos desde la línea de comandos Aspell directory Directorio Aspell Image conversion output format Formato de salida para imágenes convertidas Scale images with bilinear filtering Escala de imágenes con filtrado bilineal Output images quality Calidad en la conversión de imágenes Zip directory with processed images Comprimir directorio con imágenes convertidas tables tablas new_session nueva sesión manage administrar It seems that %1 contains TEA plugin Do you want to install it? Al parecer, % 1 contiene un plugin TEA. ¿Quiere instalarlo? Enter the archive name Introduzca nombre del archivo new_archive nuevo archivo Indent (tab) Sangría (tab) Un-indent (shift+tab) Quitar sangría (shift-tab) Preview image Vista previa de imagen Use spaces instead of tabs Espacios en lugar de tabs Automatic indent Sangrías automáticas Automatic preview images at file manager Visualización automática de imágenes en el gestor de archivos Tab width in spaces Espacios por tabulador Use wrap setting from highlighting module Use ajuste de líneas del módulo de resaltado Sort case insensitively Ordenar sin mayús./minúsc. bits per sample: %1 bits por muestra: %1 number of channels: %1 número de canales: %1 sample rate: %1 tasa de muestreo: %1 RMS for all channels: %1 dB RMS para los canales: %1 dB Underline Subrayado Highlighting mode Modo resaltado configs configs tea-qt-63.3.0/translations/fr.qm000066400000000000000000001026201476733534200165040ustar00rootroot00000000000000W'?WTA6XLv[f3bt_jZjmlb=moox/]2>a[؉ ۺktyPs`ZX\3\i.V.AI T nav"Xl~:ʯ6a 3\d- UO2DR3hPbnl0]Qv9.4 9fhdZYrǁu3>W"+[߬yc^-9>a8 q*V,UV1+ 4V>w4A?M B?JTG= `HAE U4]H]\gi|Y#kD|wZ~>)`w9RxC3[pM@LF*,u6$O!o尣\36=pU W  'H 'I ?BBO S&4" TQwz ]A la + s5: {aP tM vN$ E G b` r S S>q% ƕ, HL 虄 7& nNr (3 <+ D3t G KX LZ dσm dϣ g  0BCg S% 8 \ SQ ], s ȗ5G te r+ '? Z$ 5/ $7c :o# R gYO j=! m )S 1t PN +; Ȯ TFB RRf U!  6 1 E= ()`TE <$ >h)f AEo C RY.O q0 sM] x p e%' @  T %1 is not found
CTEA%1 is processed and savedCTEA"%1 est sauvegard %1 is savedCTEA%1 kbytes %2
CTEAN%1 occurences de %2 est(sont) trouv(s)'%1 number of occurrences of %2 is foundCTEA%p% complt(e) %p% completedCTEA(<b>Marques-pages</b>BookmarksCTEA\<span style="%1">ECHANTILLON DE COULEUR</span>$COLOR SAMPLECTEAA proposAboutCTEAA propos de QtAbout QtCTEA*Ajouter ou soustraireAdd or subtractCTEAAjouter au ZIP Add to ZIPCTEA2Ajouter aux marques-pagesAdd to bookmarksCTEA.Ajouter au dictionnaireAdd to dictionaryCTEAAlignerAlignCTEAoAll (*);;Text files (*.txt);;Markup files (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)CTEAAnagramCTEAAnalyserAnalyzeCTEA$Courriel anti-spamAntispam e-mailCTEA Apply hard rotation by EXIF dataCTEAApply templateCTEA0Appliquer chaque ligneApply to each lineCTEA"Arabe vers romainArabic to RomanCTEALEtes-vous certain(e) de supprimer %1 ?Are you sure to delete %1?CTEAAspell directoryCTEAAssignerAssignCTEA.Indentation automatiqueAutomatic indentCTEAPrvisualisation d'image automatique dans le gestionnaire de fichiers(Automatic preview images at file managerCTEA(Binaire vers dcimalBinary to decimalCTEA Block endCTEA Block startCTEAGrasBoldCTEAMarques-pages BookmarksCTEA.Liste des marques-pagesBookmarks listCTEABottomCTEASaut de ligne Break lineCTEA Build programCTEA C templateCTEA C++ templateCTEA!Calculate moon days between datesCTEACalendrierCalendarCTEA4Ne peut pas sauvegarder %1Cannot save %1CTEACannot save: %1CTEACasCaseCTEA&Sensible la casseCase sensitiveCTEACellsCTEACenterCTEA"Jeu de caractresCharsetCTEA'Charset for file open from command lineCTEAClassicCTEA Clean programCTEA Fermer Close currentCTEACouleurColorCTEA$Colonnes par ligneColumns per rowCTEA,Commenter la slectionComment selectionCTEA CommunCommonCTEACompare two stringsCTEACompressCTEAConfigurationsConfigsCTEA@Convertir les balises en entitsConvert tags to entitiesCTEA CopierCopyCTEA Copy blockCTEACopy by column[s]CTEA@Copier le nom du fichier courantCopy current file nameCTEAnCopier le contenu de la slection dans le presse-papier6Copy the current selection's contents to the clipboardCTEACopy to storage fileCTEAbCompter les lignes dans les fichiers slectionnsCount lines in selected filesCTEA0Compter les sous-chanesCount the substringCTEA^Compter les sous-chanes (expression rgulire)Count the substring (regexp)CTEABrouillonCrapbookCTEA0Crer un nouveau fichierCreate a new fileCTEA(Crer un nouveau ZIPCreate new ZIPCTEA6Crer un nouveau rpertoireCreate new directoryCTEA*Crer une galerie webCreate web galleryCTEA Current filesCTEA&Cursor blink time (msecs, zero is OFF)CTEA>Centre du curseur au dfilementCursor center on scrollCTEA Cursor widthCTEA CouperCutCTEA Cut blockCTEAnCouper le contenu de la slection dans le presse-papier5Cut the current selection's contents to the clipboardCTEADarkerCTEADateDateCTEADate et horaire Date and timeCTEAFormat de date Date formatCTEA JoursDaysCTEA(Dcimal vers binaireDecimal to binaryCTEA"Delete N first chars at file namesCTEADelete by columnCTEA(Supprimer le fichier Delete fileCTEANDselectionner par expression rgulireDeselect by regexpCTEADo not add to recentCTEADockedCTEA$Taille du documentDocument weightCTEADocuments tabs alignCTEAEXIFCTEA EditerEditCTEA0Editer les marques-pagesEdit bookmarksCTEABPolice de caractres de l'diteur Editor fontCTEA=Enca is not installed, falling back to the built-in detectionCTEAFin de ligne : End of line: CTEA4Entrez le nom de l'archiveEnter the archive nameCTEAEntrez le nomEnter the nameCTEADEntrez vos notes journalires ici.Enter your daily notes here.CTEAdEntrez vos notes journalires ici. Pour utiliser des penses-btes horodats, spcifiez l'heure au format 24-hour [hh:mm], autrement dit.. [06:00]bonjour ! [20:10]aller au cinmaEnter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatreCTEAEnumrer EnumerateCTEAFEchapper les expressions rgulires Escape regexpCTEAEvaluerEvaluateCTEAQuitterExitCTEA*Quitter l'applicationExit the applicationCTEA"Extraire des mots Extract wordsCTEAFIFCTEAFIF at the top (restart needed)CTEAFichierFileCTEA$Actions du fichier File actionsCTEA.Informations du fichierFile informationCTEA*Oprations de fichierFile operationsCTEAFichiersFilesCTEA FiltreFilterCTEAFilter by repetitionsCTEABFiltrer avec expression rgulireFilter with regexpCTEATrouverFindCTEA2Trouver dans les fichiers Find in filesCTEA&Trouver le prochain Find nextCTEA:Trouver les chemins obsoltesFind obsolete pathsCTEA(Trouver le prcdent Find previousCTEA&Retourner une liste Flip a listCTEAFlip a list with separatorCTEAdRetourner les bits (complement au niveau des bits)Flip bits (bitwise complement)CTEAFmFmCTEARMettre en avant le champ de saisie rputFocus the Famous input fieldCTEA2Mettre en avant l'diteurFocus the editorCTEA*De l'anglais au morseFrom English to MorseCTEA(Du morse l'anglaisFrom Morse To EnglishCTEA"Du morse au russeFrom Morse To RussianCTEA"Du russe au morseFrom Russian to MorseCTEA From cursorCTEA,Informations compltes Full infoCTEAFonctions FunctionsCTEA Fuzzy modeCTEAFuzzy search factorCTEAGUI tabs alignCTEA<Avoir la liste des fichiers...Getting files list...CTEA AllerGoCTEAGo to current dateCTEA>Aller au rpertoire utilisateurGo to home dirCTEA Aller la ligne Go to lineCTEA>Aller la position sauvegardeGo to saved positionCTEAGuess encoding!CTEAGabarit HTML HTML templateCTEAHTML5 templateCTEAEn-tteHeaderCTEA4Cacher les notes d'erreursHide error marksCTEA6Surligner la ligne couranteHighlight current lineCTEAFSurligner les parenthses appariesHighlight paired bracketsCTEA,Rpertoire utilisateurHomeCTEAJRpertoire des dictionnaires HunspellHunspell dictionaries directoryCTEAIDECTEA"Taille des icnes Icons sizeCTEALFormat de sortie de conversion d'imageImage conversion output formatCTEA ImagesImagesCTEAIncorrect parameters at FIFCTEAIndenter (tab) Indent (tab)CTEA<Indenter par la premire ligneIndent by first lineCTEA"Insrer une image Insert imageCTEAInterface InterfaceCTEAFPolice de caractres de l'interfaceInterface fontCTEAItaliqueItalicCTEAJustifyCTEAClavierKeyboardCTEA&LaTeX: Straight to curly double quotesCTEA&LaTeX: Straight to double angle quotesCTEA)LaTeX: Straight to double angle quotes v2CTEALabel ends with: CTEALabel starts with: CTEALabelsCTEA*Dernier fichier fermLast closed fileCTEALeftCTEALicenceLicenseCTEALienLinkCTEAOptions du lien Link optionsCTEAList ZIP contentCTEA Logmemo fontCTEAMark all foundCTEAMark first dateCTEAMark last dateCTEABalisageMarkupCTEAMathMathCTEA Divers MiscellaneousCTEAModeModeCTEAMoisMonthsCTEAMoon mode on/offCTEAMoon phase algorithmCTEACode morse Morse codeCTEA Multi-renameCTEANOUVELLESNEWSCTEANomNameCTEA Nom :Name:CTEANavNavCTEANouveauNewCTEAOnglet suivantNext tabCTEA Espace inscableNon-breaking spaceCTEANorthern hemisphereCTEANotesCTEA Number of days between two datesCTEANumber:CTEA OuvrirOpenCTEAOpen DirectoryCTEA4Ouvrir un fichier existantOpen an existing fileCTEA"Ouvrir au curseurOpen at cursorCTEA"Ouvrir un fichier Open fileCTEA8Qualit des images en sortieOutput images qualityCTEAPalettesPalettesCTEAParagraphe ParagraphCTEA CollerPasteCTEA Paste blockCTEAhColler le contenu du presse-papier dans la slection9Paste the clipboard's contents into the current selectionCTEAEndroitPlaceCTEA Onglet prcdentPrev tabCTEA0Prvisualisation d'image Preview imageCTEAVPrvisualisation de la couleur slectionnePreview selected colorCTEAImprimerPrintCTEA(Imprimer le documentPrint documentCTEAProfilsProfilesCTEA(Liste des programmes Programs listCTEAQuotesCTEA@RMS pour tous les canaux : %1 dBRMS for all channels: %1 dBCTEA Fichiers rcents Recent filesCTEARefaireRedoCTEARafrachirRefreshCTEA@Rafrachir le rpertoire courantRefresh current dirCTEARefresh labelsCTEA2Mode expression rgulire Regexp modeCTEARechargerReloadCTEA2Recharger avec l'encodageReload with encodingCTEASupprimerRemoveCTEA\Supprimer les dlimiteurs aprs chaque ligne#Remove after delimiter at each lineCTEA\Supprimer les dlimiteurs avant chaque ligne$Remove before delimiter at each lineCTEADSupprimer l'enregistrement du jourRemove day recordCTEA,Supprimer les doublonsRemove duplicatesCTEA4Supprimer les lignes videsRemove empty linesCTEA4Supprimer la mise en formeRemove formattingCTEARSupprimer la mise en forme chaque ligneRemove formatting at each lineCTEA2Supprimer du dictionnaireRemove from dictionaryCTEADSupprimer les lignes de taille < NRemove lines < N sizeCTEADSupprimer les lignes de taille > NRemove lines > N sizeCTEA8Supprimer les espaces de finRemove trailing spacesCTEARenommerRenameCTEARename selected fileCTEA Repeat lastCTEARemplacer tout Replace allCTEAZRemplacer tout dans tous les fichiers ouvertsReplace all in opened filesCTEAReplace in file namesCTEARemplacer avec Replace withCTEATRestaurer la dernire session au lancement$Restore the last session on start-upCTEAInverserReverseCTEARightCTEA"Romain vers arabeRoman to ArabicCTEA LancerRunCTEA Run programCTEASauvegarderSaveCTEA Sauvegarder .bak Save .bakCTEA Sauvegarder sousSave AsCTEA$Sauvegarder au ZIPSave ZIPCTEA.Sauvegarder en tant queSave asCTEABSauvegarder en tant que diffrentSave as differentCTEA.Sauvegarder la position Save positionCTEA*Sauvegarder le profil Save profileCTEA,Sauvegarder la session Save sessionCTEAJSauvegarder le document sur le disqueSave the document to diskCTEAVSauvegarder le document sous un nouveau nom"Save the document under a new nameCTEASave the file first!CTEA@Sauvegarder la version horodateSave timestamped versionCTEA Saved: %1CTEA6Echelonner par pourcentagesScale by percentagesCTEA,Echelonner par le ct Scale by sideCTEA Scale imageCTEAbEchelonner les images avec un filtrage bilinaire$Scale images with bilinear filteringCTEAScriptsScriptsCTEARechercherSearchCTEA2Rsultats de la rechercheSearch resultsCTEARecherche... Searching...CTEASlectionnerSelectCTEAJSlectionner par expression rgulireSelect by regexpCTEASelect the file name first!CTEASessionsSessionsCTEA@Appliquer les fins de ligne UNIXSet UNIX end of lineCTEAFAppliquer les fins de ligne WindowsSet Windows end of lineCTEASet as storage fileCTEASet old Mac end of line (CR)CTEAShortcutCTEA<Montrer la position du curseurShow cursor positionCTEAShow full path at window titleCTEA8Montrer les numros de ligneShow line numbersCTEA$Montrer la marge Show margin atCTEATaille du ctSize of the sideCTEASnippetsSnippetsCTEA TrierSortCTEASort by lengthCTEA0Tri insensible la caseSort case insensitivelyCTEA.Tri sensible la casseSort case sensitivelyCTEA%Sort case sensitively, with separatorCTEASort table by column ABCCTEA6Vrification orthographique Spell checkCTEAJMoteur du vrificateur orthographiqueSpell checker engineCTEA6Vrification orthographiqueSpell checkingCTEALLangues du vrificateur orthographiqueSpell-checker languagesCTEAFCommencer les semaines par DimancheStart week on SundayCTEA,Start/stop capture clipboard to storage fileCTEA Rester au-dessus Stay on topCTEAStraight to curly double quotesCTEAStraight to double angle quotesCTEA0Enlever les balises HTMLStrip HTML tagsCTEASuggrerSuggestCTEASum by last columnCTEA Swap cellsCTEASyntax highlighting enabledCTEATEA program iconCTEATEA project templateCTEA6Largeur d'onglet en espacesTab width in spacesCTEATableauxTablesCTEAGabarit TemplatesCTEATestTestCTEA TexteTextCTEA*Statistiques du texteText statisticsCTEA$Texte vers [X]HTMLText to [X]HTMLCTEACThe famous input field. Use for search/replace, function parametersCTEAThemesCTEAFIl y a %1 de lignes aux %2 fichiersThere are %1 lines at %2 filesCTEAThere is no stylesheet fileCTEAHoraireTimeCTEAFormat horaire Time formatCTEA.Basculer en plein cranToggle fullscreenCTEA.Basculer en-tte/sourceToggle header/sourceCTEA"Retour la ligneToggle word wrapCTEAToolsCTEATTaille totale = %1 ko dans %2 fichiers<br>&Total size = %1 kbytes in %2 files
CTEA UI language (TEA restart needed)CTEAUI mode (TEA restart needed)CTEAUI style (TEA restart needed)CTEA.Tri par quantit UNITAZUNITAZ quantity sortingCTEAUNITAZ sorting by alphabetCTEAUNITAZ sorting by lengthCTEABUNITAZ : UNIverIsal Text AnalyZer UNITAZ: UNIverlsal Text AnalyZerCTEAMAJUSCULEUPCASECTEA2Dsindenter (shift + tab)Un-indent (shift+tab)CTEASouligner UnderlineCTEADfaireUndoCTEAUnmarkCTEAUnpack ZIP to current directoryCTEAUpCTEAUse Alt key to access main menuCTEA$Use EXIF orientation at image viewerCTEAUse Enca for charset detectionCTEA-Use Left Alt + WASD as additional cursor keysCTEA Use external image viewer for F2CTEAUse joystick as cursor keysCTEAVUtiliser des espaces au lieu de tabulationsUse spaces instead of tabsCTEA~Utiliser les dialogues traditionnaux Sauvegarder/Ouvrir Fichier&Use traditional File Save/Open dialogsCTEAVueViewCTEA0Option de la galerie webWeb gallery optionsCTEATous les mots Whole wordsCTEA Word length: CTEA"Retour la ligne Word wrapCTEA Words lengthsCTEA AnnesYearsCTEAZIPZIPCTEAZIP packing: file names charsetCTEA!ZIP unpacking: file names charsetCTEAZero pad file namesCTEAZZipper le rpertoire avec les images traites#Zip directory with processed imagesCTEAOutils [X]HTML [X]HTML toolsCTEA2bits par chantillon : %1bits per sample: %1CTEAcannot open %1 because of: %2CTEAcaractres : %1<br>caractres sans espaces : %2<br>lignes : %3<br>fiches de l'auteur : %4Ichars: %1
chars without spaces: %2
lines: %3
author's sheets: %4CTEAconfigsconfigsCTEAdatesCTEAdec degrees > deg min secCTEAdeg min sec > dec degreesCTEAeditorCTEA4milliseconds coules : %1elapsed milliseconds: %1CTEAfamous input fieldCTEA&nom du fichier : %1 file name: %1CTEAfilesCTEAlast modified: %1CTEAlogmemoCTEAminuscule lower caseCTEAmanualCTEAnouveaunewCTEA nouvelle_archive new_archiveCTEA$nouveau_rpertoire new_directoryCTEAnouveau_profile new_profileCTEA nouvelle_session new_sessionCTEA not found!CTEA*nombre de canaux : %1number of channels: %1CTEAoptionsCTEAHput your notes (for this file) here and they will be saved automaticallyCTEA6taux d'chantillonnage : %1sample rate: %1CTEAscriptsscriptsCTEAtaille : %1 kosize: %1 kbytesCTEAsnippet %1 is not existsCTEAsnippetssnippetsCTEAsum: %1CTEAtableauxtablesCTEAgabarits templatesCTEA0analyse de texte de : %1text analysis of: %1CTEA&total / unique : %1total / unique: %1CTEA2ratio total - unique : %1!total to unique per cent diff: %1CTEA$total de mots : %1words total: %1CTEA"mots uniques : %1words unique: %1CTEAJvous pouvez mettre des notes ici, etcyou can put here notes, etcCTEA'Saving for this format is not supported CTioReadOnly.Attention ! Attention !Attention! Attention!CTodoModifi le Modified atQObjectNomNameQObject TailleSizeQObject.erreur API ZIP/UNZIP %1ZIP/UNZIP API error %1 QuaZipFiletea-qt-63.3.0/translations/fr.ts000066400000000000000000001630311476733534200165200ustar00rootroot00000000000000 CAboutWindow Code Code Acknowledgements Remerciements Translations Traductions About A propos CDarkerWindow Darker palette CDocument cannot open %1 because of: %2 ne peut pas ouvrir %1 à cause de : %2 %1 is open %1 est ouvert cannot save %1 because of: %2 ne peut pas sauvegarder %1 à cause de : %2 %1 is saved %1 est sauvegardé new[%1] nouveau[%1] %1 has been modified. Do you want to save your changes? %1 a été modifié. Voulez-vous sauvegardez vos changements ? Copy Copier Cut Couper Paste Coller Undo Défaire Redo Refaire CImgViewer preview prévisualisation CTEA The famous input field. Use for search/replace, function parameters FIF editor logmemo famous input field Charset Jeu de caractères %1 already exists Do you want to overwrite? %1 existe déjà Voulez-vous le remplacer ? Test Test Files Fichiers Labels New Nouveau Create a new file Créer un nouveau fichier Open file Ouvrir un fichier Open an existing file Ouvrir un fichier existant Save Sauvegarder Save the document to disk Sauvegarder le document sur le disque Save As Sauvegarder sous Save the document under a new name Sauvegarder le document sous un nouveau nom Exit Quitter Exit the application Quitter l'application Cut Couper Cut the current selection's contents to the clipboard Couper le contenu de la sélection dans le presse-papier Copy Copier Copy the current selection's contents to the clipboard Copier le contenu de la sélection dans le presse-papier Paste Coller Paste the clipboard's contents into the current selection Coller le contenu du presse-papier dans la sélection Undo Défaire Redo Refaire About A propos About Qt A propos de Qt File Fichier Open Ouvrir Last closed file Dernier fichier fermé Open at cursor Ouvrir au curseur Crapbook Brouillon Notes Save as different Sauvegarder en tant que différent Save .bak Sauvegarder .bak Save timestamped version Sauvegarder la version horodatée Save session Sauvegarder la session File actions Actions du fichier Reload Recharger Reload with encoding Recharger avec l'encodage Set UNIX end of line Appliquer les fins de ligne UNIX Set Windows end of line Appliquer les fins de ligne Windows Set old Mac end of line (CR) Recent files Fichiers récents Bookmarks Marques-pages Edit bookmarks Editer les marques-pages Add to bookmarks Ajouter aux marques-pages Find obsolete paths Trouver les chemins obsolètes Templates Gabarit Sessions Sessions Configs Configurations Bookmarks list Liste des marques-pages Programs list Liste des programmes Do not add to recent Print Imprimer Close current Fermer Edit Editer Block start Block end Copy block Paste block Cut block Copy current file name Copier le nom du fichier courant Indent (tab) Indenter (tab) Un-indent (shift+tab) Désindenter (shift + tab) Indent by first line Indenter par la première ligne Comment selection Commenter la sélection Set as storage file Copy to storage file Start/stop capture clipboard to storage file Markup Balisage Mode Mode Header En-tête Align Aligner Center Left Right Justify Bold Gras Italic Italique Underline Souligner Link Lien Paragraph Paragraphe Color Couleur Break line Saut de ligne Non-breaking space Espace insécable Insert image Insérer une image [X]HTML tools Outils [X]HTML Text to [X]HTML Texte vers [X]HTML Convert tags to entities Convertir les balises en entités Antispam e-mail Courriel anti-spam Document weight Taille du document Preview selected color Prévisualisation de la couleur sélectionnée Strip HTML tags Enlever les balises HTML Rename selected file Search Rechercher Find Trouver Find next Trouver le prochain Find previous Trouver le précédent Find in files Trouver dans les fichiers Replace with Remplacer avec Replace all Remplacer tout Replace all in opened files Remplacer tout dans tous les fichiers ouverts Mark all found Unmark Case sensitive Sensible à la casse Whole words Tous les mots From cursor Regexp mode Mode expression régulière Fuzzy mode Functions Fonctions Repeat last Tools Scale image Snippets Snippets Scripts Scripts Tables Tableaux Place Endroit TEA project template HTML template Gabarit HTML HTML5 template C++ template C template Date Date Time Horaire Case Cas UPCASE MAJUSCULE lower case minuscule Sort Trier Sort case sensitively Tri sensible à la casse Sort case insensitively Tri insensible à la case Sort case sensitively, with separator Sort by length Flip a list Retourner une liste Flip a list with separator Cells Sort table by column ABC Swap cells Delete by column Copy by column[s] Filter Filtre Remove duplicates Supprimer les doublons Remove empty lines Supprimer les lignes vides Remove lines < N size Supprimer les lignes de taille < N Remove lines > N size Supprimer les lignes de taille > N Remove before delimiter at each line Supprimer les délimiteurs avant à chaque ligne Remove after delimiter at each line Supprimer les délimiteurs après à chaque ligne Filter with regexp Filtrer avec expression régulière Filter by repetitions Math Math Evaluate Evaluer Arabic to Roman Arabe vers romain Roman to Arabic Romain vers arabe Decimal to binary Décimal vers binaire Binary to decimal Binaire vers décimal Flip bits (bitwise complement) Retourner les bits (complement au niveau des bits) Enumerate Enumérer Sum by last column deg min sec > dec degrees dec degrees > deg min sec Morse code Code morse From Russian to Morse Du russe au morse From Morse To Russian Du morse au russe From English to Morse De l'anglais au morse From Morse To English Du morse à l'anglais Analyze Analyser Text statistics Statistiques du texte Extract words Extraire des mots Words lengths Count the substring Compter les sous-chaînes Count the substring (regexp) Compter les sous-chaînes (expression régulière) UNITAZ quantity sorting Tri par quantité UNITAZ UNITAZ sorting by alphabet UNITAZ sorting by length Text Texte Apply to each line Appliquer à chaque ligne Remove formatting Supprimer la mise en forme Remove formatting at each line Supprimer la mise en forme à chaque ligne Compress Anagram Remove trailing spaces Supprimer les espaces de fin Escape regexp Echapper les expressions régulières Reverse Inverser Compare two strings Quotes Straight to double angle quotes Straight to curly double quotes LaTeX: Straight to curly double quotes LaTeX: Straight to double angle quotes LaTeX: Straight to double angle quotes v2 Spell-checker languages Langues du vérificateur orthographique Spell check Vérification orthographique Suggest Suggérer Add to dictionary Ajouter au dictionnaire Remove from dictionary Supprimer du dictionnaire Calendar Calendrier Moon mode on/off Mark first date Mark last date Add or subtract Ajouter ou soustraire Days Jours Months Mois Years Années Go to current date Calculate moon days between dates Number of days between two dates Remove day record Supprimer l'enregistrement du jour Run Lancer IDE Run program Build program Clean program Toggle header/source Basculer en-tête/source Nav Nav Save position Sauvegarder la position Go to saved position Aller à la position sauvegardée Go to line Aller à la ligne Next tab Onglet suivant Prev tab Onglet précédent Focus the Famous input field Mettre en avant le champ de saisie réputé Focus the editor Mettre en avant l'éditeur Refresh labels Current files Fm Fm Multi-rename Zero pad file names Delete N first chars at file names Replace in file names Apply template File operations Opérations de fichier Create new directory Créer un nouveau répertoire Rename Renommer Delete file Supprimer le fichier File information Informations du fichier Count lines in selected files Compter les lignes dans les fichiers sélectionnés Full info Informations complètes ZIP ZIP Create new ZIP Créer un nouveau ZIP Add to ZIP Ajouter au ZIP Save ZIP Sauvegarder au ZIP Images Images Scale by side Echelonner par le côté Scale by percentages Echelonner par pourcentages Create web gallery Créer une galerie web Go to home dir Aller au répertoire utilisateur Refresh current dir Rafraîchir le répertoire courant Preview image Prévisualisation d'image Select by regexp Sélectionner par expression régulière Deselect by regexp Déselectionner par expression régulière View Vue Themes Palettes Palettes Profiles Profils Save profile Sauvegarder le profil Toggle word wrap Retour à la ligne Hide error marks Cacher les notes d'erreurs Toggle fullscreen Basculer en plein écran Stay on top Rester au-dessus Darker NEWS NOUVELLES License Licence not found! %1 is saved %1 est sauvegardé options Classic Docked Interface font Police de caractères de l'interface Editor font Police de caractères de l'éditeur Logmemo font Up Bottom GUI tabs align Documents tabs align Icons size Taille des icônes TEA program icon Show line numbers Montrer les numéros de ligne Word wrap Retour à la ligne Syntax highlighting enabled Highlight current line Surligner la ligne courante Highlight paired brackets Surligner les parenthèses appariées Automatic indent Indentation automatique Use spaces instead of tabs Utiliser des espaces au lieu de tabulations Tab width in spaces Largeur d'onglet en espaces Show cursor position Montrer la position du curseur Cursor center on scroll Centre du curseur au défilement Cursor blink time (msecs, zero is OFF) Cursor width Show margin at Montrer la marge à Show full path at window title Interface Interface Use joystick as cursor keys Automatic preview images at file manager Prévisualisation d'image automatique dans le gestionnaire de fichiers Restore the last session on start-up Restaurer la dernière session au lancement Use external image viewer for F2 Use traditional File Save/Open dialogs Utiliser les dialogues traditionnaux Sauvegarder/Ouvrir Fichier Start week on Sunday Commencer les semaines par Dimanche Northern hemisphere Moon phase algorithm Charset for file open from command line Common Commun Label starts with: Label ends with: Date and time Date et horaire Date format Format de date Time format Format horaire Spell checking Vérification orthographique Spell checker engine Moteur du vérificateur orthographique Hunspell dictionaries directory Répertoire des dictionnaires Hunspell Select Sélectionner Aspell directory Miscellaneous Divers Fuzzy search factor Image conversion output format Format de sortie de conversion d'image Scale images with bilinear filtering Echelonner les images avec un filtrage bilinéaire Output images quality Qualité des images en sortie Apply hard rotation by EXIF data Zip directory with processed images Zipper le répertoire avec les images traitées Web gallery options Option de la galerie web Size of the side Taille du côté Link options Options du lien Columns per row Colonnes par ligne EXIF Use EXIF orientation at image viewer Shortcut Assign Assigner Remove Supprimer Keyboard Clavier manual you can put here notes, etc vous pouvez mettre des notes ici, etc snippet %1 is not exists Print document Imprimer le document %p% completed %p% complété(e) elapsed milliseconds: %1 milliseconds écoulées : %1 %1 - saved %1 - sauvegardé Cannot save %1 Ne peut pas sauvegarder %1 chars: %1<br>chars without spaces: %2<br>lines: %3<br>author's sheets: %4 caractères : %1<br>caractères sans espaces : %2<br>lignes : %3<br>fiches de l'auteur : %4 %1 is processed and saved #external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s" #list des programmes externes. exemple : opera="C:\Program Files\Opera\opera.exe " "%s" #external programs list. example: ff=firefox file:///%s Save the file first! templates gabarits snippets snippets scripts scripts tables tableaux configs configs Enter the name Entrez le nom Name: Nom : new_directory nouveau_répertoire new_session nouvelle_session <span style="%1">COLOR SAMPLE</span> <span style="%1">ECHANTILLON DE COULEUR</span> Name Nom Go Aller Home Répertoire utilisateur Refresh Rafraîchir Save as Sauvegarder en tant que <b>Bookmarks</b> <b>Marques-pages</b> files new nouveau Are you sure to delete %1? Etes-vous certain(e) de supprimer %1 ? End of line: Fin de ligne : file name: %1 nom du fichier : %1 size: %1 kbytes taille : %1 ko last modified: %1 bits per sample: %1 bits par échantillon : %1 number of channels: %1 nombre de canaux : %1 sample rate: %1 taux d'échantillonnage : %1 RMS for all channels: %1 dB RMS pour tous les canaux : %1 dB total to unique per cent diff: %1 ratio total - unique : %1 total / unique: %1 total / unique : %1 words unique: %1 mots uniques : %1 words total: %1 total de mots : %1 text analysis of: %1 analyse de texte de : %1 UNITAZ: UNIverlsal Text AnalyZer UNITAZ : UNIverIsal Text AnalyZer %1 number of occurrences of %2 is found %1 occurences de %2 est(sont) trouvé(s) Enter the archive name Entrez le nom de l'archive new_archive nouvelle_archive Open Directory Getting files list... Avoir la liste des fichiers... Searching... Recherche... cannot open %1 because of: %2 Search results Résultats de la recherche new_profile nouveau_profile There are %1 lines at %2 files Il y a %1 de lignes aux %2 fichiers Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatre Entrez vos notes journalières ici. Pour utiliser des penses-bêtes horodatés, spécifiez l'heure au format 24-hour [hh:mm], autrement dit.. [06:00]bonjour ! [20:10]aller au cinéma Enter your daily notes here. Entrez vos notes journalières ici. dates Select the file name first! %1 is not found<br> %1 n'a pas été trouvé<br> %1 kbytes %2 <br> Total size = %1 kbytes in %2 files<br> Taille totale = %1 ko dans %2 fichiers<br> Word length: Number: put your notes (for this file) here and they will be saved automatically There is no stylesheet file sum: %1 Incorrect parameters at FIF Cannot save: %1 Saved: %1 This file is opened in the read-only mode. You can save it with another name using <b>Save as</b> The storage file is closed or not set. You are trying to rename the opened file, please close it first! matched does not Check regexp match 0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - Leueshkanov Actions Open a file from the file name provided above Save the current opened file with the name provided above Capitalize sentences GTK Bookmarks: Set as autosaving file Unset the autosaving file Autosaving Temporary save unsaved buffers on exit Autosave buffers and autosaving files (each N seconds) Keyboards <b>TEA %1</b> by Peter Semiletov | tea.ourproject.org<br>Support TEA via PayPal: peter.semiletov@gmail.com<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-git Show ebooks fine Show tabs and spaces Subtitles: shift timecodes by msecs Preview Markdown All (*);;Text files (*.txt);;Markup files (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Select all Case inverse Enable locale only voices Speach voice Speech Say the selected text Speech pause/resume Stop speech UI mode (TEA restart is needed) UI language (TEA restart is needed) UI style (TEA restart is needed) FIF at the top (restart is needed) Enable speech (TEA restart is needed) Save more Save all existing CTioReadOnly Saving for this format is not supported CTodo Attention! Attention! Attention ! Attention ! CZORWindow scaled to: QObject Name Nom Size Taille Modified at Modifié le Please set up spell checker dictionaries at Options - Functions page QuaZipFile ZIP/UNZIP API error %1 erreur API ZIP/UNZIP %1 tea-qt-63.3.0/translations/pl.qm000066400000000000000000001234651476733534200165220ustar00rootroot00000000000000*TITIY[^t$>y4$$''.<%/D25=*IR+D0KE*8GYEGH5I}H5jfKR\Guh~r8r]5wį~? s?zE/acGG H/ I3iJcbM0T30>TIUjJUVNeW'O7WTPGX[[[f3x_jlbMmuxe/p=Ma"؉|ۺt( PstX&\3ni.?f8 AI ` nwv"h~HʯA,a 3n- U^D !R3\bnl0pQ98N F\|pYǁ>go+m߬yz67FDq4eUeuw4E?M BLsM}|UV5tSmFTH5XojQ)[b+cQ#tvu}hqt$Ì K$8~hs#ȡ3%`q (jPK(#Cq%u&̰0*jE!0x[GZO5T?#X9di[#kqm_rxjcqq{#c| 3Z 4{659^:CGyy$zrw:{S bX)n h\NU 7_ TF(c.jXa;+9C\[Cntm!#7E%1+K927a8YH8[|^?;NP%-}?~u*ϷXMS 0 L(o$ pgϡ YIӸE;( \^.gE],N,N?YG=THAEU4]Hog|Y#'|wk~N3)`&'9aR:[p\Oa[)*6B$_A!尣o#3<= h  'WV 'W ?BQ\ S&?+ TQ ]P la # s5H {a` t\ v]A T U buM  bv S> ƕ5 W 虄 C n]w () <% D> G% Ki LlE dσ dϣ g r5L7 0BRh S. 8 Sa om ȗ@~ t~ r5L 8 0 Z-{ 5: J $7{ :1 R"' gi j=) m] )c 1 _ 4 Ȯ'z TUe RR U*' * BB = EMl ()`c~ <-G >h)~ AE% C RY.^F q0 I s\t x  e. t  !R d[ %1 is not found
CTEAB%1 jest przetwarzane i zapisywane%1 is processed and savedCTEAjest zapisywany %1 is savedCTEA&%1 kilobajt %2 <br>%1 kbytes %2
CTEAT%1 liczba wystpieD % 2 zostaBa znaleziona'%1 number of occurrences of %2 is foundCTEA%p% postpu %p% completedCTEA0 - Trygonometryczny wer.2, 1 - Trygonometryczny wer.1, 2 - Conway, 3 - LeueshkanovK0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - LeueshkanovCTEA<b>ZakBadki</b>BookmarksCTEAJ<span style="%1">PRBKA KOLORU</span>$COLOR SAMPLECTEAO programieAboutCTEAO QtAbout QtCTEADziaBaniaActionsCTEA"Dodaj lub odejmijAdd or subtractCTEA*Dodaj do archiwum ZIP Add to ZIPCTEA"Dodaj do zakBadekAdd to bookmarksCTEA"Dodaj do sBownikaAdd to dictionaryCTEAWyrwnanieAlignCTEAA5 (*);;Pliki tekstowe (*.txt);;Pliki znacznikw (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)oAll (*);;Text files (*.txt);;Markup files (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)CTEAAnagramAnagramCTEAAnalizaAnalyzeCTEAHKoduj swj adres e-mail przed spamemAntispam e-mailCTEATZastosuj tward rotacj wedBug danych EXIF Apply hard rotation by EXIF dataCTEA Zastosuj szablonApply templateCTEA6Zastosuj do ka|dego wierszaApply to each lineCTEA(Arabskie na rzymskieArabic to RomanCTEA.Czy na pewno usun %1?Are you sure to delete %1?CTEAKatalog AspellAspell directoryCTEAPrzypisaAssignCTEA(Wcicie automatyczneAutomatic indentCTEA`Automatyczny podgld obrazw w mened|erze plikw(Automatic preview images at file managerCTEA,Dwjkowe na dziesitneBinary to decimalCTEAKoniec bloku Block endCTEAPocztek bloku Block startCTEAPogrubienieBoldCTEAZakBadki BookmarksCTEALista zakBadekBookmarks listCTEA W dBBottomCTEA&Przeniesienie linii Break lineCTEA<Zbuduj program (Build program) Build programCTEASzablon C C templateCTEASzablon C++ C++ templateCTEAFOblicz dni ksi|ycowe midzy datami!Calculate moon days between datesCTEAKalendarzCalendarCTEA(Nie mo|na zapisa %1Cannot save %1CTEA*Nie mo|na zapisa: %1Cannot save: %1CTEA&Wielkie litery zdaDCapitalize sentencesCTEAWielko[ literCaseCTEA8Rozr|nianie wielko[ci literCase sensitiveCTEAKomrkiCellsCTEADo [rodkaCenterCTEAKodowanieCharsetCTEAfZestaw znakw dla pliku otwieranego z linii poleceD'Charset for file open from command lineCTEA`Sprawdz dopasowanie wedBug wyra|enia regularnegoCheck regexp matchCTEAKlasycznyClassicCTEALOczy[ pliki budowania (Clean program) Clean programCTEA(Zamknij bie|cy plik Close currentCTEA KolorColorCTEA"Kolumny na wierszColumns per rowCTEA(Skomentuj zaznaczoneComment selectionCTEA OglneCommonCTEA$Porwnaj dwa cigiCompare two stringsCTEAZci[nijCompressCTEAKonfiguracjeConfigsCTEABKonwertowanie znacznikw na encjeConvert tags to entitiesCTEA KopiujCopyCTEAKopiuj blok Copy blockCTEA.Kopiuj wedBug kolumn[y]Copy by column[s]CTEA8Kopiuj nazw bie|cego plikuCopy current file nameCTEALSkopiuj bie|ce zaznaczenie do schowka6Copy the current selection's contents to the clipboardCTEA0Skopiuj do pliku pamiciCopy to storage fileCTEADPolicz wiersze w wybranych plikachCount lines in selected filesCTEA&Zliczanie podcigwCount the substringCTEAhPolicz wystpienia podBaDcucha (wyra|enie regularne)Count the substring (regexp)CTEA(Brudnopis (Crapbook)CrapbookCTEA Utwrz nowy plikCreate a new fileCTEA0Utwrz nowe archiwum ZIPCreate new ZIPCTEA&Utwrz nowy katalogCreate new directoryCTEA4Utwrz galeri internetowCreate web galleryCTEABie|ce pliki Current filesCTEAVCzas migania kursora (ms, zero - wyBczone)&Cursor blink time (msecs, zero is OFF)CTEAHWy[rodkuj kursor podczas przewijaniaCursor center on scrollCTEA"Szeroko[ kursora Cursor widthCTEA WytnijCutCTEAWytnij blok Cut blockCTEAJWytnij bie|ce zaznaczenie do schowka5Cut the current selection's contents to the clipboardCTEACiemniejszeDarkerCTEADataDateCTEAData i godzina Date and timeCTEAFormat daty Date formatCTEADniDaysCTEA,Dziesitne na dwjkoweDecimal to binaryCTEA:UsuD pierwsze N znakw z nazw"Delete N first chars at file namesCTEA&UsuD wedBug kolumnyDelete by columnCTEAUsuD plik Delete fileCTEAHOdznacz wedBug wyra|enia regularnegoDeselect by regexpCTEA>Nie dodawaj do ostatnich plikwDo not add to recentCTEAZadokowanyDockedCTEAZwa| dokumentDocument weightCTEA8PoBo|enie zakBadek dokumentuDocuments tabs alignCTEAEXIFCTEA EdycjaEditCTEAEdytuj zakBadkiEdit bookmarksCTEA Czcionka edytora Editor fontCTEAEnca nie jest zainstalowana, cofam si do wbudowanego wykrywania=Enca is not installed, falling back to the built-in detectionCTEA Koniec wiersza:  End of line: CTEA.Wprowadz nazw archiwumEnter the archive nameCTEAWprowadz nazwEnter the nameCTEAJTutaj mo|esz zapisywa swoje notatki.Enter your daily notes here.CTEAWprowadz tutaj swoje codzienne notatki. Aby korzysta z przypomnieD czasowych, okre[l sygnatur czasu w formacie 24-godzinnym [hh:mm], to znaczy: [06:00]DzieD dobry! [20:10]kupi chleb, bo jutro sklepy zamkniteEnter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatreCTEAPonumeruj EnumerateCTEAbUciekaj przed znakami specjalnymi (Escape regexp) Escape regexpCTEAPrzelicznikiEvaluateCTEAWyj[cieExitCTEA Wyjdz z programuExit the applicationCTEA*Wyodrbnianie wyrazw Extract wordsCTEAFIFFIFCTEAnPole wej[ciowe Znane (FIF) na grze (potrzebny restart)FIF at the top (restart needed)CTEAPlikFileCTEA(DziaBania na plikach File actionsCTEA$Informacje o plikuFile informationCTEA(DziaBania na plikachFile operationsCTEA PlikiFilesCTEA FiltrFilterCTEA0Filtruj wedBug powtrzeDFilter by repetitionsCTEANFiltruj za pomoc wyra|enia regularnegoFilter with regexpCTEA ZnajdzFindCTEA Znajdz w plikach Find in filesCTEAZnajdz nastpny Find nextCTEA6Znajdz przestarzaBe [cie|kiFind obsolete pathsCTEA Znajdz poprzedni Find previousCTEAOdwr list Flip a listCTEAHOdwr list oddzielon separatoramiFlip a list with separatorCTEA@Odwr bity (dopeBnienie bitowe)Flip bits (bitwise complement)CTEAMened|er plikwFmCTEAVSkoncentruj si na znanym polu wprowadzaniaFocus the Famous input fieldCTEA6Skoncentruj si na edytorzeFocus the editorCTEABZ angielskiego na alfabet Morse'aFrom English to MorseCTEA>Z alfabetu Morse'a na angielskiFrom Morse To EnglishCTEA<Z alfabetu Morse'a na rosyjskiFrom Morse To RussianCTEA@Z rosyjskiego na alfabet Morse'aFrom Russian to MorseCTEAOd kursora From cursorCTEA PeBne informacje Full infoCTEAFunkcje FunctionsCTEA"W trybie rozmytym Fuzzy modeCTEABRozmyty wspBczynnik wyszukiwaniaFuzzy search factorCTEAZakBadki GTK:GTK Bookmarks:CTEA@Wyrwnywanie zakBadek interfejsuGUI tabs alignCTEA4Pobieranie listy plikw...Getting files list...CTEAIdzGoCTEA2Przejdz do aktualnej datyGo to current dateCTEA(Do katalogu domowegoGo to home dirCTEAIdz do linii Go to lineCTEA0Idz do zapisanej pozycjiGo to saved positionCTEAbSprbuj wykry sposb kodowania pliku tekstowego!Guess encoding!CTEASzablon HTML HTML templateCTEASzablon HTML5HTML5 templateCTEANagBwekHeaderCTEA4Ukryj oznaczone jako bBdyHide error marksCTEA0Pod[wietl bie|cy wierszHighlight current lineCTEA6Pod[wietl sparowane nawiasyHighlight paired bracketsCTEAStrona gBwnaHomeCTEA4Katalog sBownikw HunspellHunspell dictionaries directoryCTEAIDEIDECTEARozmiar ikon Icons sizeCTEABFormat wyj[ciowy konwersji obrazuImage conversion output formatCTEA ObrazyImagesCTEA:NieprawidBowe parametry w FIFIncorrect parameters at FIFCTEAWcicie (tab) Indent (tab)CTEA2Wcicie w pierwszej liniiIndent by first lineCTEA"Wstawianie obrazu Insert imageCTEAInterfejs InterfaceCTEA&Czcionka interfejsuInterface fontCTEAKursywaItalicCTEAWy[rodkowanieJustifyCTEAKlawiaturaKeyboardCTEAhLaTeX: cudzysBowy proste podwjne na dole i na grze&LaTeX: Straight to curly double quotesCTEAjLaTeX: cudzysBowy proste podwjne w jodeBk wariant 1&LaTeX: Straight to double angle quotesCTEAjLaTeX: cudzysBowy proste podwjne w jodeBk wariant 2)LaTeX: Straight to double angle quotes v2CTEA*Etykieta koDczy si: Label ends with: CTEA2Etykieta zaczyna si od: Label starts with: CTEAEtykietyLabelsCTEA,Ostatni zamknity plikLast closed fileCTEADo lewejLeftCTEALicencjaLicenseCTEAOdno[nikLinkCTEAOpcje linkw Link optionsCTEA&Poka| zawarto[ ZIPList ZIP contentCTEA Czcionka Logmemo Logmemo fontCTEA6Oznacz wszystkie znalezioneMark all foundCTEA*Zaznacz pierwsz datMark first dateCTEA*Zaznacz ostatni datMark last dateCTEAZnacznikiMarkupCTEAMatematykaMathCTEA R|ne MiscellaneousCTEATrybModeCTEAMiesiceMonthsCTEA,Tryb ksi|ycowy on/offMoon mode on/offCTEA,Algorytm fazy ksi|ycaMoon phase algorithmCTEAAlfabet Morse'a Morse codeCTEA0Wielokrotna zmiana nazwy Multi-renameCTEANowo[ciNEWSCTEA NazwaNameCTEA Nazwa:Name:CTEANawigacjaNavCTEANowyNewCTEA"Nastpna zakBadkaNext tabCTEA"NieBamliwa spacjaNon-breaking spaceCTEA PBkula pBnocnaNorthern hemisphereCTEANotatkiNotesCTEA>Liczba dni midzy dwiema datami Number of days between two datesCTEA Numer:Number:CTEA OtwrzOpenCTEAOtwrz katalogOpen DirectoryCTEAHOtwrz plik o nazwie podanej powy|ej-Open a file from the file name provided aboveCTEA,Otwrz istniejcy plikOpen an existing fileCTEA&Otwrz pod kursoremOpen at cursorCTEAOtwrz plik Open fileCTEA4Jako[ obrazw wyj[ciowychOutput images qualityCTEA PaletyPalettesCTEAParagraf ParagraphCTEA WklejPasteCTEAWklej blok Paste blockCTEA Wklej ze schowka9Paste the clipboard's contents into the current selectionCTEA Umie[PlaceCTEA&Poprzednia zakBadkaPrev tabCTEAPodgld obrazu Preview imageCTEA.Zobacz wyr|niony kolorPreview selected colorCTEA DrukujPrintCTEADrukuj dokumentPrint documentCTEAProfileProfilesCTEALista programw Programs listCTEACudzysBowyQuotesCTEABRMS dla wszystkich kanaBw: %1 dBRMS for all channels: %1 dBCTEAOstatnie pliki Recent filesCTEA PonwRedoCTEAOd[wie|RefreshCTEA.Od[wie| bie|cy katalogRefresh current dirCTEA(Zaktualizuj etykietyRefresh labelsCTEA8W trybie wyra|eD regularnych Regexp modeCTEA&Przeczytaj ponownieReloadCTEA:ZaBaduj ponownie z kodowaniemReload with encodingCTEAUsuD powizanieRemoveCTEAHUsuD po separatorze w ka|dym wierszu#Remove after delimiter at each lineCTEANUsuD przed separatorem w ka|dym wierszu$Remove before delimiter at each lineCTEAUsuD dane dniaRemove day recordCTEA UsuD powtrzeniaRemove duplicatesCTEA UsuD puste linieRemove empty linesCTEA"UsuD formatowanieRemove formattingCTEADUsuD formatowanie w ka|dym wierszuRemove formatting at each lineCTEA UsuD ze sBownikaRemove from dictionaryCTEA4UsuD linie < o rozmiarze NRemove lines < N sizeCTEA4UsuD linie > o rozmiarze NRemove lines > N sizeCTEA&UsuD koDcowe spacjeRemove trailing spacesCTEAZmieD nazwRenameCTEA6ZmieD nazw wybranego plikuRename selected fileCTEAPowtrz ostatni Repeat lastCTEA ZamieD wszystkie Replace allCTEAFZastp wszystko w otwartych plikachReplace all in opened filesCTEA.Zastp w nazwach plikwReplace in file namesCTEAZamieD na Replace withCTEARPrzywr ostatni sesj przy uruchomieniu$Restore the last session on start-upCTEAOdwrotnieReverseCTEADo prawejRightCTEA(Rzymskie na arabskieRoman to ArabicCTEAUruchomRunCTEAUruchom program Run programCTEAZachowajSaveCTEA*Zapisz kopi zapasow Save .bakCTEAZachowaj jakoSave AsCTEA&Zapisz archiwum ZIPSave ZIPCTEAZapisz jakoSave asCTEA Zapisz jako innySave as differentCTEA Zachowaj pozycj Save positionCTEAZapisz profil Save profileCTEAZapisz sesj Save sessionCTEAlZapisz aktualnie otwarty plik pod nazw podan powy|ej9Save the current opened file with the name provided aboveCTEA0Zapisz dokument na dyskuSave the document to diskCTEA<Zapisz dokument pod now nazw"Save the document under a new nameCTEA*Najpierw zapisz plik!Save the file first!CTEA4Zapisz wersj wedBug czasuSave timestamped versionCTEAZapisano: %1 Saved: %1CTEA&Skaluj w procentachScale by percentagesCTEA$Skaluj wedBug boku Scale by sideCTEASkaluj obraz Scale imageCTEAPSkaluj obrazy z filtrowaniem dwuliniowym$Scale images with bilinear filteringCTEASkryptyScriptsCTEA SzukajSearchCTEA&Wyniki wyszukiwaniaSearch resultsCTEASzuka... Searching...CTEAWybierzSelectCTEAFOznacz wedBug wyra|enia regularnegoSelect by regexpCTEA:Wybierz najpierw nazw pliku!Select the file name first!CTEA SesjeSessionsCTEAHUstaw koniec wiersza w systemie UNIXSet UNIX end of lineCTEAJUstaw koniec linii w systemie WindowsSet Windows end of lineCTEA.Ustaw jako plik pamiciSet as storage fileCTEAZUstaw koniec wiersza wedBug starego Maca (CR)Set old Mac end of line (CR)CTEA Skrt klawiszowyShortcutCTEA*Poka| pozycj kursoraShow cursor positionCTEABPoka| peBn [cie|k w tytule oknaShow full path at window titleCTEA(Poka| numery wierszyShow line numbersCTEA"Poka| margines naShow margin atCTEARozmiar bokuSize of the sideCTEAFragmentySnippetsCTEASortowanieSortCTEA,Sortuj wedBug dBugo[ciSort by lengthCTEAPSortuj bez uwzgldnienia wielko[ci literSort case insensitivelyCTEANSortuj z uwzgldnieniem wielko[ci literSort case sensitivelyCTEAxSortuj z uwzgldnieniem wielko[ci liter, u|ywajc separatora%Sort case sensitively, with separatorCTEAnSortuj tabel wedBug kolumny w kolejno[ci alfabetycznejSort table by column ABCCTEA Sprawdz pisowni Spell checkCTEA4Silnik sprawdzania pisowniSpell checker engineCTEA&Sprawdzanie pisowniSpell checkingCTEA4Jzyki sprawdzania pisowniSpell-checker languagesCTEA<Rozpocznij tydzieD w niedzielStart week on SundayCTEAtUruchom/zatrzymaj przechwytywanie schowka do pliku pamici,Start/stop capture clipboard to storage fileCTEA"Nad innymi oknami Stay on topCTEAZCudzysBowy proste podwjne na dole i na grzeStraight to curly double quotesCTEAHCudzysBowy proste do podwjnego ktaStraight to double angle quotesCTEA"Wyczy[ tagi HTMLStrip HTML tagsCTEASugestieSuggestCTEA:Suma wedBug ostatniej kolumnySum by last columnCTEA0ZamieD komrki miejscami Swap cellsCTEAFPod[wietlanie skBadni jest wBczoneSyntax highlighting enabledCTEA$Ikona programu TEATEA program iconCTEA(Szablon projektu TEATEA project templateCTEA>Szeroko[ tabulatora w spacjachTab width in spacesCTEA TabeleTablesCTEASzablony TemplatesCTEATestTestCTEA TekstTextCTEA"Statystyki tekstuText statisticsCTEA Tekst na [X]HTMLText to [X]HTMLCTEAZnane pole wprowadzania. SBu|y do wyszukiwania/zamiany, parametrw funkcjiCThe famous input field. Use for search/replace, function parametersCTEAfPlik pamici jest zamknity lub nie jest ustawiony.&The storage file is closed or not set.CTEAMotywy (Themes)ThemesCTEA.%1 wierszy w plikach %2There are %1 lines at %2 filesCTEA6Nie ma pliku arkusza stylwThere is no stylesheet fileCTEATen plik jest otwarty w trybie tylko do odczytu. Mo|esz zapisa go pod inn nazw za pomoc przycisku Zapisz jako.aThis file is opened in the read-only mode. You can save it with another name using Save asCTEACzasTimeCTEAFormat czasu Time formatCTEA6PrzeBcz tryb peBnoekranowyToggle fullscreenCTEAXPrzeBczanie nagBwka/zrdBa (header/source)Toggle header/sourceCTEA4PrzeBcz zawijanie wyrazwToggle word wrapCTEANarzdziaToolsCTEA^CaBkowita waga = %1 kilobajtw w %2 plikach<br>&Total size = %1 kbytes in %2 files
CTEAxJzyk interfejsu (konieczne bdzie ponowne uruchomienie TEA) UI language (TEA restart needed)CTEAvTryb interfejsu (konieczne bdzie ponowne uruchomienie TEA)UI mode (TEA restart needed)CTEAvStyl interfejsu (konieczne bdzie ponowne uruchomienie TEA)UI style (TEA restart needed)CTEA@UNITAZ posortowane wedBug ilo[ciUNITAZ quantity sortingCTEA@UNITAZ posortowane alfabetycznieUNITAZ sorting by alphabetCTEADUNITAZ posortowane wedBug dBugo[ciUNITAZ sorting by lengthCTEAJUNITAZ: UNIwersalny Tekstu AnaliZator UNITAZ: UNIverlsal Text AnalyZerCTEAwielkie literyUPCASECTEA4Cofnij wcicie (shift-tab)Un-indent (shift+tab)CTEAPodkre[lenie UnderlineCTEA CofnijUndoCTEA UsuD zaznaczeniaUnmarkCTEAVRozpakuj archiwum ZIP do bie|cego kataloguUnpack ZIP to current directoryCTEA W grUpCTEAlU|yj klawisza Alt, aby uzyska dostp do menu gBwnegoUse Alt key to access main menuCTEAVU|yj orientacji EXIF w przegldarce obrazw$Use EXIF orientation at image viewerCTEALU|yj Enca do wykrywania zestawu znakwUse Enca for charset detectionCTEAU|yj lewego klawisza Alt + WASD jako dodatkowych klawiszy kursora-Use Left Alt + WASD as additional cursor keysCTEAXU|yj zewntrznej przegldarki obrazw dla F2 Use external image viewer for F2CTEAHU|yj joysticka jako klawiszy kursoraUse joystick as cursor keysCTEA>U|yj spacji zamiast tabulatorwUse spaces instead of tabsCTEAlU|yj tradycyjnych okien dialogowych Zapisz/Otwrz plik&Use traditional File Save/Open dialogsCTEA WidokViewCTEA4Opcje galerii internetowejWeb gallery optionsCTEACaBe sBowa Whole wordsCTEADBugo[ sBowa: Word length: CTEA"Zawijanie wierszy Word wrapCTEADBugo[ci sBw Words lengthsCTEALataYearsCTEAzPrbujesz zmieni nazw otwartego pliku, zamknij go najpierw!@You are trying to rename the opened file, please close it first!CTEAKompresja ZIPZIPCTEAPPakowanie ZIP: zestaw znakw nazw plikwZIP packing: file names charsetCTEAZRozpakowywanie ZIP: zestaw znakw nazw plikw!ZIP unpacking: file names charsetCTEA$Dodaj zera do nazwZero pad file namesCTEAJKatalog Zip z przetworzonymi obrazami#Zip directory with processed imagesCTEA"Narzdzia [X]HTML [X]HTML toolsCTEA&bitw na prbk: %1bits per sample: %1CTEADnie mo|na otworzy %1 z powodu: %2cannot open %1 because of: %2CTEAznaki: %1<br>znaki bez spacji: %2<br>wiersze: %3<br>arkusze autorskie: %4Ichars: %1
chars without spaces: %2
lines: %3
author's sheets: %4CTEAkonfiguracjeconfigsCTEAdatydatesCTEAVstopnie dziesitne > stopnie minuty sekundydec degrees > deg min secCTEAVstopnie minuty sekundy > stopnie dziesitnedeg min sec > dec degreesCTEAnie pasujedoes notCTEA EdycjaeditorCTEA*minBo milisekund: %1elapsed milliseconds: %1CTEA(znane pole wej[ciowefamous input fieldCTEAnazwa pliku: %1 file name: %1CTEA plikifilesCTEA0ostatnia modyfikacja: %1last modified: %1CTEAlogmemologmemoCTEAmaBe litery lower caseCTEAPodrcznikmanualCTEA pasujematchedCTEAnowynewCTEAnowe_archiwum new_archiveCTEAnowy_katalog new_directoryCTEAnowy_profil new_profileCTEAnowa_sesja new_sessionCTEAnie znaleziono! not found!CTEA$liczba kanaBw: %1number of channels: %1CTEA opcjeoptionsCTEAnapisz tutaj swoje notatki (do tego pliku), a zostan one automatycznie zapisaneHput your notes (for this file) here and they will be saved automaticallyCTEA:czstotliwo[ prbkowania: %1sample rate: %1CTEAskryptyscriptsCTEA$rozmiar: %1 kbytessize: %1 kbytesCTEA2fragment % 1 nie istniejesnippet %1 is not existsCTEAfragmentysnippetsCTEAsuma: %1sum: %1CTEA tabeletablesCTEAszablony templatesCTEA,analiza tekstu dla: %1text analysis of: %1CTEA0caBkowity / unikalny: %1total / unique: %1CTEAXBcznie do unikalnej r|nicy procentowej: %1!total to unique per cent diff: %1CTEA"wyrazw razem: %1words total: %1CTEA@wyrazw, nie liczc powtrek: %1words unique: %1CTEA<mo|na tu umie[ci notatki, itpyou can put here notes, etcCTEA^Zapisywanie w tym formacie nie jest obsBugiwane'Saving for this format is not supported CTioReadOnlyUwaga! Uwaga!Attention! Attention!CTodo"przeskalowane do: scaled to:  CZORWindowZmieniono Modified atQObject NazwaNameQObjectUstaw sBowniki do sprawdzania pisowni w Opcje w zakBadce FunkcjeEPlease set up spell checker dictionaries at Options - Functions pageQObjectRozmiarSizeQObject*ZIP/UNZIP bBd API %1ZIP/UNZIP API error %1 QuaZipFile , tea-qt-63.3.0/translations/pl.ts000066400000000000000000002701431476733534200165270ustar00rootroot00000000000000 CAboutWindow Code Kod Acknowledgements Podziękowanie Translations Tłumaczenia About O programie CDarkerWindow Darker palette Ciemna paleta CDocument cannot open %1 because of: %2 Nie można otworzyć %1, z powodu %2 cannot save %1 because of: %2 nie można zapisać %1, z powodu %2 %1 is saved %1 jest zapisany %1 has been modified. Do you want to save your changes? %1 został zmodyfikowany. Czy chcesz zachować zmiany? Copy Kopiuj Cut Wytnij Paste Wklej Undo Cofnij Redo Ponów %1 is open %1 jest otwarty new[%1] nowy[%1] CImgViewer preview podgląd CTEA The famous input field. Use for search/replace, function parameters Znane pole wprowadzania. Służy do wyszukiwania/zamiany, parametrów funkcji FIF FIF editor Edycja logmemo logmemo famous input field znane pole wejściowe All (*);;Text files (*.txt);;Markup files (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Все (*);;Pliki tekstowe (*.txt);;Pliki znaczników (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Charset Kodowanie %1 already exists Do you want to overwrite? %1 już istnieje Chcesz nadpisać? Test Test Files Pliki Labels Etykiety New Nowy Create a new file Utwórz nowy plik Open file Otwórz plik Open an existing file Otwórz istniejący plik Save Zachowaj Save the document to disk Zapisz dokument na dysku Save As Zachowaj jako Save the document under a new name Zapisz dokument pod nową nazwą Exit Wyjście Exit the application Wyjdź z programu Cut Wytnij Cut the current selection's contents to the clipboard Wytnij bieżące zaznaczenie do schowka Copy Kopiuj Copy the current selection's contents to the clipboard Skopiuj bieżące zaznaczenie do schowka Paste Wklej Paste the clipboard's contents into the current selection Wklej ze schowka Undo Cofnij Redo Ponów About O programie About Qt O Qt File Plik Open Otwórz Last closed file Ostatni zamknięty plik Open at cursor Otwórz pod kursorem Crapbook Brudnopis (Crapbook) Notes Notatki Save as different Zapisz jako inny Save .bak Zapisz kopię zapasową Save timestamped version Zapisz wersję według czasu Save session Zapisz sesję File actions Działania na plikach Reload Przeczytaj ponownie Reload with encoding Załaduj ponownie z kodowaniem Set UNIX end of line Ustaw koniec wiersza w systemie UNIX Set Windows end of line Ustaw koniec linii w systemie Windows Set old Mac end of line (CR) Ustaw koniec wiersza według starego Maca (CR) Recent files Ostatnie pliki Bookmarks Zakładki Edit bookmarks Edytuj zakładki Add to bookmarks Dodaj do zakładek Find obsolete paths Znajdź przestarzałe ścieżki Templates Szablony Sessions Sesje Configs Konfiguracje Bookmarks list Lista zakładek Programs list Lista programów Do not add to recent Nie dodawaj do ostatnich plików Print Drukuj Close current Zamknij bieżący plik Edit Edycja Block start Początek bloku Block end Koniec bloku Copy block Kopiuj blok Paste block Wklej blok Cut block Wytnij blok Copy current file name Kopiuj nazwę bieżącego pliku Indent (tab) Wcięcie (tab) Un-indent (shift+tab) Cofnij wcięcie (shift-tab) Indent by first line Wcięcie w pierwszej linii Comment selection Skomentuj zaznaczone Set as storage file Ustaw jako plik pamięci Copy to storage file Skopiuj do pliku pamięci Start/stop capture clipboard to storage file Uruchom/zatrzymaj przechwytywanie schowka do pliku pamięci Markup Znaczniki Mode Tryb Header Nagłówek Align Wyrównanie Center Do środka Left Do lewej Right Do prawej Justify Wyśrodkowanie Bold Pogrubienie Italic Kursywa Underline Podkreślenie Link Odnośnik Paragraph Paragraf Color Kolor Break line Przeniesienie linii Non-breaking space Niełamliwa spacja Insert image Wstawianie obrazu [X]HTML tools Narzędzia [X]HTML Text to [X]HTML Tekst na [X]HTML Convert tags to entities Konwertowanie znaczników na encje Antispam e-mail Koduj swój adres e-mail przed spamem Document weight Zważ dokument Preview selected color Zobacz wyróżniony kolor Strip HTML tags Wyczyść tagi HTML Rename selected file Zmień nazwę wybranego pliku Search Szukaj Find Znajdź Find next Znajdź następny Find previous Znajdź poprzedni Save more Save all existing Find in files Znajdź w plikach Replace with Zamień na Replace all Zamień wszystkie Replace all in opened files Zastąp wszystko w otwartych plikach Mark all found Oznacz wszystkie znalezione Unmark Usuń zaznaczenia Case sensitive Rozróżnianie wielkości liter Whole words Całe słowa From cursor Od kursora Regexp mode W trybie wyrażeń regularnych Fuzzy mode W trybie rozmytym Functions Funkcje Repeat last Powtórz ostatni Tools Narzędzia Scale image Skaluj obraz Snippets Fragmenty Scripts Skrypty Tables Tabele Place Umieść TEA project template Szablon projektu TEA HTML template Szablon HTML HTML5 template Szablon HTML5 C++ template Szablon C++ C template Szablon C Date Data Time Czas Case Wielkość liter UPCASE wielkie litery lower case małe litery Sort Sortowanie Sort case sensitively Sortuj z uwzględnieniem wielkości liter Sort case insensitively Sortuj bez uwzględnienia wielkości liter Sort case sensitively, with separator Sortuj z uwzględnieniem wielkości liter, używając separatora Sort by length Sortuj według długości Flip a list Odwróć listę Flip a list with separator Odwróć listę oddzieloną separatorami Cells Komórki Sort table by column ABC Sortuj tabelę według kolumny w kolejności alfabetycznej Swap cells Zamień komórki miejscami Delete by column Usuń według kolumny Copy by column[s] Kopiuj według kolumn[y] Filter Filtr Remove duplicates Usuń powtórzenia Remove empty lines Usuń puste linie Remove lines < N size Usuń linie < o rozmiarze N Remove lines > N size Usuń linie > o rozmiarze N Remove before delimiter at each line Usuń przed separatorem w każdym wierszu Remove after delimiter at each line Usuń po separatorze w każdym wierszu Filter with regexp Filtruj za pomocą wyrażenia regularnego Filter by repetitions Filtruj według powtórzeń Math Matematyka Evaluate Przeliczniki Arabic to Roman Arabskie na rzymskie Roman to Arabic Rzymskie na arabskie Decimal to binary Dziesiętne na dwójkowe Binary to decimal Dwójkowe na dziesiętne Flip bits (bitwise complement) Odwróć bity (dopełnienie bitowe) Enumerate Ponumeruj Sum by last column Suma według ostatniej kolumny deg min sec > dec degrees stopnie minuty sekundy > stopnie dziesiętne dec degrees > deg min sec stopnie dziesiętne > stopnie minuty sekundy Morse code Alfabet Morse'a From Russian to Morse Z rosyjskiego na alfabet Morse'a From Morse To Russian Z alfabetu Morse'a na rosyjski From English to Morse Z angielskiego na alfabet Morse'a From Morse To English Z alfabetu Morse'a na angielski Analyze Analiza Text statistics Statystyki tekstu Extract words Wyodrębnianie wyrazów Words lengths Długości słów Count the substring Zliczanie podciągów Count the substring (regexp) Policz wystąpienia podłańcucha (wyrażenie regularne) UNITAZ quantity sorting UNITAZ posortowane według ilości UNITAZ sorting by alphabet UNITAZ posortowane alfabetycznie UNITAZ sorting by length UNITAZ posortowane według długości Text Tekst Apply to each line Zastosuj do każdego wiersza Remove formatting Usuń formatowanie Remove formatting at each line Usuń formatowanie w każdym wierszu Compress Ściśnij Anagram Anagram Remove trailing spaces Usuń końcowe spacje Subtitles: shift timecodes by msecs Escape regexp Uciekaj przed znakami specjalnymi (Escape regexp) Reverse Odwrotnie Compare two strings Porównaj dwa ciągi Quotes Cudzysłowy Straight to double angle quotes Cudzysłowy proste do podwójnego kąta Straight to curly double quotes Cudzysłowy proste podwójne na dole i na górze LaTeX: Straight to curly double quotes LaTeX: cudzysłowy proste podwójne na dole i na górze LaTeX: Straight to double angle quotes LaTeX: cudzysłowy proste podwójne w jodełkę wariant 1 LaTeX: Straight to double angle quotes v2 LaTeX: cudzysłowy proste podwójne w jodełkę wariant 2 Say the selected text Speech pause/resume Stop speech Spell-checker languages Języki sprawdzania pisowni Spell check Sprawdź pisownię Suggest Sugestie Add to dictionary Dodaj do słownika Remove from dictionary Usuń ze słownika Calendar Kalendarz Moon mode on/off Tryb księżycowy on/off Mark first date Zaznacz pierwszą datę Mark last date Zaznacz ostatnią datę Add or subtract Dodaj lub odejmij Days Dni Months Miesiące Years Lata Go to current date Przejdź do aktualnej daty Calculate moon days between dates Oblicz dni księżycowe między datami Number of days between two dates Liczba dni między dwiema datami Remove day record Usuń dane dnia Run Uruchom IDE IDE Run program Uruchom program Build program Zbuduj program (Build program) Clean program Oczyść pliki budowania (Clean program) Toggle header/source Przełączanie nagłówka/źródła (header/source) Nav Nawigacja Save position Zachowaj pozycję Go to saved position Idź do zapisanej pozycji Go to line Idź do linii Next tab Następna zakładka Prev tab Poprzednia zakładka Focus the Famous input field Skoncentruj się na znanym polu wprowadzania Focus the editor Skoncentruj się na edytorze Refresh labels Zaktualizuj etykiety Current files Bieżące pliki Fm Menedżer plików Multi-rename Wielokrotna zmiana nazwy Zero pad file names Dodaj zera do nazw Delete N first chars at file names Usuń pierwsze N znaków z nazw Replace in file names Zastąp w nazwach plików Apply template Zastosuj szablon File operations Działania na plikach Create new directory Utwórz nowy katalog Rename Zmień nazwę Delete file Usuń plik File information Informacje o pliku Count lines in selected files Policz wiersze w wybranych plikach Full info Pełne informacje ZIP Kompresja ZIP Create new ZIP Utwórz nowe archiwum ZIP Add to ZIP Dodaj do archiwum ZIP Save ZIP Zapisz archiwum ZIP List ZIP content Pokaż zawartość ZIP Unpack ZIP to current directory Rozpakuj archiwum ZIP do bieżącego katalogu Images Obrazy Scale by side Skaluj według boku Scale by percentages Skaluj w procentach Create web gallery Utwórz galerię internetową Go to home dir Do katalogu domowego Refresh current dir Odśwież bieżący katalog Preview image Podgląd obrazu Select by regexp Oznacz według wyrażenia regularnego Deselect by regexp Odznacz według wyrażenia regularnego View Widok Themes Motywy (Themes) Palettes Palety Profiles Profile Save profile Zapisz profil Toggle word wrap Przełącz zawijanie wyrazów Hide error marks Ukryj oznaczone jako błędy Toggle fullscreen Przełącz tryb pełnoekranowy Stay on top Nad innymi oknami Darker Ciemniejsze NEWS Nowości License Licencja not found! nie znaleziono! All (*);;Text files (*.txt);;Markup files (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) This file is opened in the read-only mode. You can save it with another name using <b>Save as</b> Ten plik jest otwarty w trybie tylko do odczytu. Możesz zapisać go pod inną nazwą za pomocą przycisku Zapisz jako. %1 is saved jest zapisywany The storage file is closed or not set. Plik pamięci jest zamknięty lub nie jest ustawiony. You are trying to rename the opened file, please close it first! Próbujesz zmienić nazwę otwartego pliku, zamknij go najpierw! matched pasuje does not nie pasuje <b>TEA %1</b> by Peter Semiletov | tea.ourproject.org<br>Support TEA via PayPal: peter.semiletov@gmail.com<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-git Set as autosaving file Unset the autosaving file Select all Case inverse Capitalize sentences Wielkie litery zdań Check regexp match Sprawdź dopasowanie według wyrażenia regularnego Keyboards Preview Markdown options opcje Classic Klasyczny Docked Zadokowany UI mode (TEA restart needed) Tryb interfejsu (konieczne będzie ponowne uruchomienie TEA) UI language (TEA restart needed) Język interfejsu (konieczne będzie ponowne uruchomienie TEA) UI style (TEA restart needed) Styl interfejsu (konieczne będzie ponowne uruchomienie TEA) Interface font Czcionka interfejsu Editor font Czcionka edytora Logmemo font Czcionka Logmemo Up W górę Bottom W dół GUI tabs align Wyrównywanie zakładek interfejsu Documents tabs align Położenie zakładek dokumentu Icons size Rozmiar ikon TEA program icon Ikona programu TEA FIF at the top (restart needed) Pole wejściowe Znane (FIF) na górze (potrzebny restart) Show line numbers Pokaż numery wierszy Word wrap Zawijanie wierszy Show tabs and spaces Syntax highlighting enabled Podświetlanie składni jest włączone Highlight current line Podświetl bieżący wiersz Highlight paired brackets Podświetl sparowane nawiasy Automatic indent Wcięcie automatyczne Use spaces instead of tabs Użyj spacji zamiast tabulatorów Tab width in spaces Szerokość tabulatora w spacjach Show cursor position Pokaż pozycję kursora Cursor center on scroll Wyśrodkuj kursor podczas przewijania Cursor blink time (msecs, zero is OFF) Czas migania kursora (ms, zero - wyłączone) Cursor width Szerokość kursora Show margin at Pokaż margines na Show full path at window title Pokaż pełną ścieżkę w tytule okna Interface Interfejs Use Alt key to access main menu Użyj klawisza Alt, aby uzyskać dostęp do menu głównego Use Left Alt + WASD as additional cursor keys Użyj lewego klawisza Alt + WASD jako dodatkowych klawiszy kursora Use joystick as cursor keys Użyj joysticka jako klawiszy kursora Automatic preview images at file manager Automatyczny podgląd obrazów w menedżerze plików Restore the last session on start-up Przywróć ostatnią sesję przy uruchomieniu Use Enca for charset detection Użyj Enca do wykrywania zestawu znaków Use external image viewer for F2 Użyj zewnętrznej przeglądarki obrazów dla F2 Use traditional File Save/Open dialogs Użyj tradycyjnych okien dialogowych Zapisz/Otwórz plik Start week on Sunday Rozpocznij tydzień w niedzielę Northern hemisphere Półkula północna Moon phase algorithm Algorytm fazy księżyca 0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - Leueshkanov 0 - Trygonometryczny wer.2, 1 - Trygonometryczny wer.1, 2 - Conway, 3 - Leueshkanov Charset for file open from command line Zestaw znaków dla pliku otwieranego z linii poleceń ZIP unpacking: file names charset Rozpakowywanie ZIP: zestaw znaków nazw plików ZIP packing: file names charset Pakowanie ZIP: zestaw znaków nazw plików Autosaving Temporary save unsaved buffers on exit Autosave buffers and autosaving files (each N seconds) Common Ogólne Label starts with: Etykieta zaczyna się od: Label ends with: Etykieta kończy się: Date and time Data i godzina Date format Format daty Time format Format czasu Spell checking Sprawdzanie pisowni Spell checker engine Silnik sprawdzania pisowni Hunspell dictionaries directory Katalog słowników Hunspell Select Wybierz Aspell directory Katalog Aspell Miscellaneous Różne Fuzzy search factor Rozmyty współczynnik wyszukiwania Show ebooks fine Image conversion output format Format wyjściowy konwersji obrazu Scale images with bilinear filtering Skaluj obrazy z filtrowaniem dwuliniowym Output images quality Jakość obrazów wyjściowych Apply hard rotation by EXIF data Zastosuj twardą rotację według danych EXIF Zip directory with processed images Katalog Zip z przetworzonymi obrazami Web gallery options Opcje galerii internetowej Size of the side Rozmiar boku Link options Opcje linków Columns per row Kolumny na wiersz EXIF Use EXIF orientation at image viewer Użyj orientacji EXIF w przeglądarce obrazów Enable locale only voices Speach voice Speech Shortcut Skrót klawiszowy Assign Przypisać Remove Usuń powiązanie Keyboard Klawiatura manual Podręcznik you can put here notes, etc można tu umieścić notatki, itp snippet %1 is not exists fragment % 1 nie istnieje Print document Drukuj dokument %p% completed %p% postępu elapsed milliseconds: %1 minęło milisekund: %1 %1 - saved %1 - zapisano Cannot save %1 Nie można zapisać %1 chars: %1<br>chars without spaces: %2<br>lines: %3<br>author's sheets: %4 znaki: %1<br>znaki bez spacji: %2<br>wiersze: %3<br>arkusze autorskie: %4 %1 is processed and saved %1 jest przetwarzane i zapisywane #external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s" #lista programów zewnętrznych. Oto przykład: opera="C:\Program Files\Opera\opera.exe " "%s" #external programs list. example: ff=firefox file:///%s #lista programów zewnętrznych. Oto przykład: ff=firefox file:///%s Save the file first! Najpierw zapisz plik! templates szablony snippets fragmenty scripts skrypty tables tabele configs konfiguracje Enter the name Wprowadź nazwę Name: Nazwa: new_directory nowy_katalog new_session nowa_sesja <span style="%1">COLOR SAMPLE</span> <span style="%1">PRÓBKA KOLORU</span> Name Nazwa Go Idź Home Strona główna Refresh Odśwież Guess encoding! Spróbuj wykryć sposób kodowania pliku tekstowego! Save as Zapisz jako <b>Bookmarks</b> <b>Zakładki</b> files pliki new nowy Are you sure to delete %1? Czy na pewno usunąć %1? End of line: Koniec wiersza: file name: %1 nazwa pliku: %1 size: %1 kbytes rozmiar: %1 kbytes last modified: %1 ostatnia modyfikacja: %1 bits per sample: %1 bitów na próbkę: %1 number of channels: %1 liczba kanałów: %1 sample rate: %1 częstotliwość próbkowania: %1 RMS for all channels: %1 dB RMS dla wszystkich kanałów: %1 dB UI mode (TEA restart is needed) UI language (TEA restart is needed) UI style (TEA restart is needed) FIF at the top (restart is needed) Enable speech (TEA restart is needed) Actions Działania Open a file from the file name provided above Otwórz plik o nazwie podanej powyżej Save the current opened file with the name provided above Zapisz aktualnie otwarty plik pod nazwą podaną powyżej GTK Bookmarks: Zakładki GTK: total to unique per cent diff: %1 łącznie do unikalnej różnicy procentowej: %1 total / unique: %1 całkowity / unikalny: %1 words unique: %1 wyrazów, nie licząc powtórek: %1 words total: %1 wyrazów razem: %1 text analysis of: %1 analiza tekstu dla: %1 UNITAZ: UNIverlsal Text AnalyZer UNITAZ: UNIwersalny Tekstu AnaliZator %1 number of occurrences of %2 is found %1 liczba wystąpień % 2 została znaleziona Enter the archive name Wprowadź nazwę archiwum new_archive nowe_archiwum Open Directory Otwórz katalog Getting files list... Pobieranie listy plików... Searching... Szuka... cannot open %1 because of: %2 nie można otworzyć %1 z powodu: %2 Search results Wyniki wyszukiwania new_profile nowy_profil There are %1 lines at %2 files %1 wierszy w plikach %2 Enca is not installed, falling back to the built-in detection Enca nie jest zainstalowana, cofam się do wbudowanego wykrywania Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatre Wprowadź tutaj swoje codzienne notatki. Aby korzystać z przypomnień czasowych, określ sygnaturę czasu w formacie 24-godzinnym [hh:mm], to znaczy: [06:00]Dzień dobry! [20:10]kupić chleb, bo jutro sklepy zamknięte Enter your daily notes here. Tutaj możesz zapisywać swoje notatki. dates daty Select the file name first! Wybierz najpierw nazwę pliku! %1 is not found<br> %1 nie znaleziono<br> %1 kbytes %2 <br> %1 kilobajt %2 <br> Total size = %1 kbytes in %2 files<br> Całkowita waga = %1 kilobajtów w %2 plikach<br> is unpacked jest rozpakowany Word length: Długość słowa: Number: Numer: put your notes (for this file) here and they will be saved automatically napisz tutaj swoje notatki (do tego pliku), a zostaną one automatycznie zapisane There is no stylesheet file Nie ma pliku arkusza stylów sum: %1 suma: %1 Incorrect parameters at FIF Nieprawidłowe parametry w FIF Cannot save: %1 Nie można zapisać: %1 Saved: %1 Zapisano: %1 CTioReadOnly Saving for this format is not supported Zapisywanie w tym formacie nie jest obsługiwane CTodo Attention! Attention! Uwaga! Uwaga! CZORWindow scaled to: przeskalowane do: QObject Name Nazwa Size Rozmiar Modified at Zmieniono Please set up spell checker dictionaries at Options - Functions page Ustaw słowniki do sprawdzania pisowni w Opcje w zakładce Funkcje QuaZipFile ZIP/UNZIP API error %1 ZIP/UNZIP błąd API %1 tea-qt-63.3.0/translations/ru.qm000066400000000000000000001225561476733534200165350ustar00rootroot00000000000000J0M>2TITI7Y[ttr$byay&q'*3*P/%1@D3I5=*H+D0KD8FEG/H5HH5fKR\ikv5zį1į\ s)/bTGG TH/I3JcbM2IT31THUjJ?UVVMW'NWTPAX[[f3|_jBlbLm~x/t=Ma$؉ۺt( "PsxX(\3qi.;bCjBh<8AI n{W"kH~H`ʯA>a 3q- U^ZD *R3bn$l0sQ98 EY6ǁF>i+pO81FJ"Dq5UglUgw4tBTjY}DsM}_|UV5tScFSH5XjQ+[c+d\#tvumux'1 Ì J$8s(ȡ4@%`6~R *PJk#C%y&̰0,E!0|(G1T qX:i[%jkqm_vxjcu@{#| 4| 4M69^:CF&D[r"{{S batn \N 7 TEcjX2\[CnTp!#7Eh%2+K:27b8YG8[|G?;NP'}?~u-Ϸ|XMS ohpiϡ Y}Ӹ+E;( \^0 q2E,N,M?Y3G=HAEU4]HsgM|wo-~M)`("9bR6O[K*6B$_!尣rd3<u j ! 'Wn 'W 1]$ ?BQX A S&?G ]P ] la m s5H% {a` Ef t\ v] T U by@  c S>8 a ƕ6 W 虄 C (* <(O D> G( Kk Lo~ dσ dϣ\ g r5K 0BRf S0W 8& Sa sJ ȗ@ tI r6: 9^ 1 Z/l 5;1 JZ $7~ 8h :> R$ gli j=, m )d+ 1 ܥn ` 5 + Ȯ) d TUM RR U,\  Bj =+ c+ EL ()`d </6 AE C RY.^ s\ w] x e/ xK # e} 3@0<<5About CAboutWindow;03>40@=>AB8Acknowledgements CAboutWindow>4Code CAboutWindow5@52>4K Translations CAboutWindow0;8B@0 B5<=55Darker palette CDarkerWindowX%1 1K; 87<5=5=. 5;05B5 A>E@0=8BL 87<5=5=8O?7%1 has been modified. Do you want to save your changes? CDocument%1 >B:@KB %1 is open CDocument%1 A>E@0=Q= %1 is saved CDocument>?8@>20BLCopy CDocumentK@570BLCut CDocumentAB028BLPaste CDocument5@545;0BLRedo CDocumentB<5=8BLUndo CDocument45 <>3C >B:@KBL %1, 81> %2cannot open %1 because of: %2 CDocument8=5 <>3C A>E@0=8BL %1, 81> %2cannot save %1 because of: %2 CDocument=>2K9[%1]new[%1] CDocument?@>A<>B@preview CImgViewerr#A?8A>: 2=5H=8E ?@83@0<<. =0?@8<5@: ff=firefox file:///%s7#external programs list. example: ff=firefox file:///%sCTEA#A?8A>: 2=5H=8E ?@>3@0<<. 2>B ?@8<5@: opera="C:\Program Files\Opera\opera.exe " "%s"P#external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s"CTEA%1 - A>E@0=Q= %1 - savedCTEAN%1 C65 ACI5AB2C5B 5;05B5 ?5@570?8A0BL?+%1 already exists Do you want to overwrite?CTEA %1 =5 =0945=<br>%1 is not found
CTEA.%1 >1@01>B0= 8 A>E@0=5=%1 is processed and savedCTEA %1 is savedCTEA&%1 :8;>109B %2 <br>%1 kbytes %2
CTEA.%1 2E>645=89 %2 =0945=>'%1 number of occurrences of %2 is foundCTEA%p% 2K?>;=5=> %p% completedCTEA0 - "@83>=><5B@8G5A:89 2.2, 1 - "@83>=><5B@8G5A:89 2.1, 2 - >=2M9, 3 - 5NH:0=>2K0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - LeueshkanovCTEA<b>0:;04:8</b>BookmarksCTEA<b>TEA %1</b> >B 5B@0 !5<8;5B>20 | tea.ourproject.org<br>>445@60BL TEA G5@57 PayPal: peter.semiletov@gmail.com<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-gitTEA %1 by Peter Semiletov | tea.ourproject.org
Support TEA via PayPal: peter.semiletov@gmail.com
Git: github.com/psemiletov/tea-qt
AUR: aur.archlinux.org/packages/tea-qt-gitCTEAJ<span style="%1"> & &"</span>$COLOR SAMPLECTEA ?@>3@0<<5AboutCTEA QtAbout QtCTEA59AB28OActionsCTEA(>1028BL 8;8 2KG5ABLAdd or subtractCTEA&>1028BL 2 70:;04:8Add to bookmarksCTEA$>1028BL 2 A;>20@LAdd to dictionaryCTEAK@02=820=85AlignCTEAA5 (*);;1KG=K9 B5:AB (*.txt);; 07<QB:0 (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)yAll (*);;Text files (*.txt);;Markup files (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx)CTEA=03@0<<0AnagramCTEA =0;87AnalyzeCTEAL>48@>20BL 04@5A e-mail AC?@>B82 A?0<0Antispam e-mailCTEAN@8<5=OBL 2@0I5=85 A>3;0A=> 40==K< EXIF Apply hard rotation by EXIF dataCTEA @8<5=8BL H01;>=Apply templateCTEA2@8<5=8BL : :064>9 AB@>:5Apply to each lineCTEA$@01A:>5 2 @8<A:>5Arabic to RomanCTEADK C25@5=K, GB> E>B8B5 C40;8BL %1?Are you sure to delete %1?CTEA0?:0 AspellAspell directoryCTEA07=0G8BLAssignCTEA*2B><0B8G5A:89 >BABC?Automatic indentCTEAR2B>-?>:07 :0@B8=>: 2 D09;>2>< ?@8:07G8:5(Automatic preview images at file managerCTEAn!>E@0=OBL 1CD5@K 8 02B>A>E@0=O<K5 D09;K :064K5 N A5:C=46Autosave buffers and autosaving files (each N seconds)CTEA2B>A>E@0=5=85 AutosavingCTEA*2>8G=>5 2 45AOB8G=>5Binary to decimalCTEA>=5F 1;>:0 Block endCTEA0G0;> 1;>:0 Block startCTEA 8@=K9BoldCTEA0:;04:8 BookmarksCTEA!?8A>: 70:;04>:Bookmarks listCTEA !=87CBottomCTEA5@5=>A AB@>:8 Break lineCTEA"!>1@0BL ?@>3@0<<C Build programCTEA(01;>= C C templateCTEA(01;>= C++ C++ templateCTEABKG8A;8BL ;C==K5 4=8 <564C 40B0<8!Calculate moon days between datesCTEA0;5=40@LCalendarCTEA(5 <>3C A>E@0=8BL %1Cannot save %1CTEA*5 <>3C A>E@0=8BL: %1Cannot save: %1CTEA6@54;>65=8O A 1>;LH>9 1C:2KCapitalize sentencesCTEA 538AB@CaseCTEA&5@525@=CBL @538AB@ Case inverseCTEA&'CB:>ABL : @538AB@CCase sensitiveCTEA /G59:8CellsCTEA!5@548=0CenterCTEA>48@>2:0CharsetCTEA`>48@>2:0 4;O >B:@KB8O D09;0 87 :><0=4=>9 AB@>:8'Charset for file open from command lineCTEA@@>25@8BL A>2?045=85 ?> @53M:A?CCheck regexp matchCTEA;0AA8:0ClassicCTEAG8AB8BL A1>@:C Clean programCTEA(0:@KBL B5:CI89 D09; Close currentCTEA&25BColorCTEA!B>;1F>2 2 @O4CColumns per rowCTEA60:><<5=B8@>20BL 2K45;5==>5Comment selectionCTEA 1I85CommonCTEA$!@02=8BL 420 A;>20Compare two stringsCTEA !60BLCompressCTEA>=D838ConfigsCTEA25@525AB8 BM38 2 ACI=>AB8Convert tags to entitiesCTEA>?8@>20BLCopyCTEA>?8@>20BL 1;>: Copy blockCTEA4>?8@>20BL ?> AB>;1FC(F0<)Copy by column[s]CTEA:>?8@>20BL 8<O B5:CI53> D09;0Copy current file nameCTEAV>?8@>20BL B5:CI55 2K45;5=85 2 1CD5@ >1<5=06Copy the current selection's contents to the clipboardCTEA6>?8@>20BL 2 D09; E@0=8;8I0Copy to storage fileCTEAH>4AG8B0BL AB@>:8 2 2K1@0==KE D09;0ECount lines in selected filesCTEA<>4AG8B0BL 2E>645=85 ?>4AB@>:8Count the substringCTEAP>4AG8B0BL 2E>645=85 ?>4AB@>:8 (@53M:A?)Count the substring (regexp)CTEA $83=OCrapbookCTEA$!>740BL =>2K9 D09;Create a new fileCTEA*!>740BL =>2K9 :0B0;>3Create new directoryCTEA&!>740BL 251-30;5@5NCreate web galleryCTEA"5:CI85 D09;K Current filesCTEAX@5<O <830=8O :C@A>@0 (<A, =>;L - 2K:;NG8BL)&Cursor blink time (msecs, zero is OFF)CTEA<C@A>@ ?> F5=B@C ?@8 ?@>:@CB:5Cursor center on scrollCTEA(8@8=0 :C@A>@0 Cursor widthCTEAK@570BLCutCTEAK@570BL 1;>: Cut blockCTEARK@570BL B5:CI55 2K45;5=85 2 1CD5@ >1<5=05Cut the current selection's contents to the clipboardCTEA "5<=55DarkerCTEA0B0DateCTEA0B0 8 2@5<O Date and timeCTEA$>@<0B 40BK Date formatCTEA=8DaysCTEA*5AOB8G=>5 2 42>8G=>5Decimal to binaryCTEAB#40;8BL ?5@2K5 N A8<2>;>2 87 8<5="Delete N first chars at file namesCTEA$#40;8BL ?> AB>;1FCDelete by columnCTEA#40;8BL D09; Delete fileCTEA6!=OBL 2K45;5=85 ?> @53M:A?CDeselect by regexpCTEA<5 4>102;OBL 2 >A;54=85 D09;KDo not add to recentCTEA!BK:>2:0DockedCTEA"725A8BL 4>:C<5=BDocument weightCTEA8>;>65=85 2:;04>: 4>:C<5=B>2Documents tabs alignCTEAEXIFCTEA @02:0EditCTEA @028BL 70:;04:8Edit bookmarksCTEA(@8DB @540:B>@0 Editor fontCTEAN>:07K20BL B>;L:> 3>;>A0 B5:CI59 ;>:0;8Enable locale only voicesCTEAZ 07@5H8BL GB5=85 2A;CE (=C65= ?5@570?CA: ")%Enable speech (TEA restart is needed)CTEA>=5F AB@>:8:  End of line: CTEA2548B5 8<OEnter the nameCTEA@>65B5 2?8A0BL BCB A2>8 70<5B:8.Enter your daily notes here.CTEAp2548B5 BCB A2>8 70<5B:8. 'B>1K 2:;NG8BL =0?><8=0;:C, 8A?>;L7C9B5 2@5<5==K5 ?><5B:8 2 24-G0A>2>< D>@<0B5 [GG:<<], B> 5ABL: [06:00]4>1@>5 CB@>! [20:10]2K9B8 70 E;51><, :>340 2AQ 70:@KB>Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatreCTEAC<5@>20BL EnumerateCTEAEscape regexp Escape regexpCTEAKG8A;8BLEvaluateCTEA KE>4ExitCTEA$K9B8 87 ?@>3@0<<KExit the applicationCTEA72;5GL A;>20 Extract wordsCTEAFIFCTEAL =025@EC (B@51C5BAO ?5@570?CA: ")"FIF at the top (restart is needed)CTEA$09;FileCTEA&59AB28O =04 D09;>< File actionsCTEA !2545=8O > D09;5File informationCTEA(59AB28O =04 D09;0<8File operationsCTEA $09;KFilesCTEA $8;LB@FilterCTEA&$8;LB@>20BL ?>2B>@KFilter by repetitionsCTEA.$8;LB@>20BL ?> @53M:A?CFilter with regexpCTEA 09B8FindCTEA09B8 2 D09;0E Find in filesCTEA09B8 40;LH5 Find nextCTEA(09B8 >H81>G=K5 ?CB8Find obsolete pathsCTEA09B8 =0704 Find previousCTEA$5@525@=CBL A?8A>: Flip a listCTEA^5@525@=CBL A?8A>:, @07<56520==K9 @0745;8B5;O<8Flip a list with separatorCTEA 5@525@=CBL 18BKFlip bits (bitwise complement)CTEA$?FmCTEA:$>:CA 2 =0<5=8B>5 ?>;5 22>40Focus the Famous input fieldCTEA $>:CA 2 @540:B>@Focus the editorCTEA2! 0=3;89A:>3> 2 :>4 >@75From English to MorseCTEA67 :>40 >@75 =0 0=3;89A:89From Morse To EnglishCTEA*7 <>@7O=:8 2 @CAA:89From Morse To RussianCTEA,7 @CAA:>3> 2 <>@7O=:CFrom Russian to MorseCTEAB :C@A>@0 From cursorCTEA>;=K5 A2545=8O Full infoCTEA$C=:F88 FunctionsCTEA 5G5B:>5 CA;>285 Fuzzy modeCTEAN>MDD8F85=B ?>8A:0 ?> =5G5B:><C CA;>28NFuzzy search factorCTEA0:;04:8 GTK:GTK Bookmarks:CTEA>K@02=820=85 2:;04>: 8=B5@D59A0GUI tabs alignCTEA4>;CG5=85 A?8A:0 D09;>2...Getting files list...CTEA5@59B8GoCTEA,5@59B8 : B5:CI59 40B5Go to current dateCTEA$ 4><0H=89 :0B0;>3Go to home dirCTEA AB@>:5 Go to lineCTEA( A>E@0=Q==><C <5ABCGo to saved positionCTEA(01;>= HTML HTML templateCTEA(01;>= HTML5HTML5 templateCTEA03>;>2>:HeaderCTEA8!:@KBL ?><5G5==>5 :0: >H81:8Hide error marksCTEA6>4A25G820BL B5:CICN AB@>:CHighlight current lineCTEA4>4A25G820BL ?0@=K5 A:>1:8Highlight paired bracketsCTEA ><>9HomeCTEAB0B0;>3 A> A;>20@O<8 4;O HunspellHunspell dictionaries directoryCTEAIDECTEA 07<5@ 8:>=>: Icons sizeCTEAPKE>4=>9 D>@<0B ?@5>1@07>20==KE :0@B8=>:Image conversion output formatCTEA0@B8=:8ImagesCTEA85?@028;L=K5 ?0@0<5B@K 2 FIFIncorrect parameters at FIFCTEABABC? (tab) Indent (tab)CTEA.BABC? ?> ?5@2>9 AB@>:5Indent by first lineCTEA"AB028BL :0@B8=:C Insert imageCTEA=B5@D59A InterfaceCTEA (@8DB 8=B5@D59A0Interface fontCTEA0:;>==K9ItalicCTEA> H8@8=5JustifyCTEA;0280BC@0KeyboardCTEA;0280BC@K KeyboardsCTEAPLaTeX: ?@O<K5 42>9=K5 2 =86=85 8 25@E=85&LaTeX: Straight to curly double quotesCTEAJLaTeX: ?@O<K5 42>9=K5 2 5;>G:>9 20@.1&LaTeX: Straight to double angle quotesCTEAJLaTeX: ?@O<K5 42>9=K5 2 5;>G:>9 20@.2)LaTeX: Straight to double angle quotes v2CTEA&5B:0 7025@H05BAO: Label ends with: CTEA(5B:0 =0G8=05BAO A: Label starts with: CTEA 5B:8LabelsCTEA.>A;54=89 70:@KBK9 D09;Last closed fileCTEA !;520LeftCTEA8F5=78OLicenseCTEA !AK;:0LinkCTEA 0AB@>9:8 AAK;:8 Link optionsCTEA(@8DB ;>3<5<> Logmemo fontCTEA,><5B8BL 2AQ =0945==>5Mark all foundCTEA(><5B8BL ?5@2CN 40BCMark first dateCTEA.><5B8BL ?>A;54=NN 40BCMark last dateCTEAQ@AB:0MarkupCTEA0B5<0B8:0MathCTEA  07=>5 MiscellaneousCTEA  568<ModeCTEA 5AOFKMonthsCTEA*C==K9 @568< 2:;/2K:;Moon mode on/offCTEA$;3>@8B< D07K ;C=KMoon phase algorithmCTEA71C:0 >@75 Morse codeCTEA5@58<5=>20=85 Multi-renameCTEA>2>AB8NEWSCTEA<ONameCTEA<O:Name:CTEA02NavCTEA >2K9NewCTEA"!;54CNI0O 2:;04:0Next tabCTEA$5@07@K2=K9 ?@>15;Non-breaking spaceCTEA$!525@=>5 ?>;CH0@85Northern hemisphereCTEA0<5B:8NotesCTEAD>;8G5AB2> 4=59 <564C 42C<O 40B0<8 Number of days between two datesCTEA>;8G5AB2>:Number:CTEAB:@KBLOpenCTEAB:@KBL :0B0;>3Open DirectoryCTEAbB:@KBL D09; ?> 8<5=8 D09;0 ?@54>AB02;5==><C 2KH5-Open a file from the file name provided aboveCTEA2B:@KBL ACI5AB2CNI89 D09;Open an existing fileCTEA(B:@KBL ?>4 :C@A>@><Open at cursorCTEAB:@KBL D09; Open fileCTEA4KE>4=>5 :0G5AB2> :0@B8=>:Output images qualityCTEA0;8B@KPalettesCTEA0@03@0D ParagraphCTEAAB028BLPasteCTEAAB028BL 1;>: Paste blockCTEA2AB028BL 87 1CD5@0 >1<5=09Paste the clipboard's contents into the current selectionCTEA><5AB8BLPlaceCTEA$@54K4CI0O 2:;04:0Prev tabCTEA*@54?@>A<>B@ MarkdownPreview MarkdownCTEA"!<>B@5BL :0@B8=:C Preview imageCTEA0!<>B@5BL 2K45;5==K9 F25BPreview selected colorCTEA5G0B0BLPrintCTEA"5G0B0BL 4>:C<5=BPrint documentCTEA@>D8;8ProfilesCTEA!?8A>: ?@>3@0<< Programs listCTEA02KG:8QuotesCTEA6RMS 4;O 2A5E :0=0;>2: %1 dBRMS for all channels: %1 dBCTEA>A;54=85 D09;K Recent filesCTEA5@545;0BLRedoCTEAA2568BLRefreshCTEA01=>28BL B5:CI89 :0B0;>3Refresh current dirCTEA1=>28BL <5B:8Refresh labelsCTEA: @568<5 @53C;O@=KE 2K@065=89 Regexp modeCTEA5@5G8B0BLReloadCTEA,5@5G8B0BL 2 :>48@>2:5Reload with encodingCTEA #40;8BL ?@82O7:CRemoveCTEAR#40;8BL ?>A;5 @0745;8B5;O 2 :064>9 AB@>:5#Remove after delimiter at each lineCTEAT#40;8BL ?5@54 @0745;8B5;5< 2 :064>9 AB@>:5$Remove before delimiter at each lineCTEA(#40;8BL 40==K5 > 4=5Remove day recordCTEA$#40;8BL ?>2B>@5=8ORemove duplicatesCTEA*#40;8BL ?CABK5 AB@>:8Remove empty linesCTEA,#40;8BL D>@<0B8@>20=85Remove formattingCTEAL#40;8BL D>@<0B8@>20=85 2 :064>9 AB@>:5Remove formatting at each lineCTEA$#40;8BL 87 A;>20@ORemove from dictionaryCTEA4#40;8BL AB@>:8 < @07<5@0 NRemove lines < N sizeCTEA4#40;8BL AB@>:8 > @07<5@0 NRemove lines > N sizeCTEA2#40;8BL E2>AB>2K5 ?@>15;KRemove trailing spacesCTEA5@58<5=>20BLRenameCTEA:5@58<5=>20BL 2K45;5==K9 D09;Rename selected fileCTEA&>2B>@8BL ?>A;54=55 Repeat lastCTEA0<5=8BL 2AQ Replace allCTEA<0<5=8BL 2AQ 2 >B:@KBKE D09;0EReplace all in opened filesCTEA00<5=8BL 2 8<5=0E D09;>2Replace in file namesCTEA0<5=8BL =0 Replace withCTEAL03@C60BL ?>A;54=NN A5AA8N ?@8 70?CA:5$Restore the last session on start-upCTEA5@525@=CBLReverseCTEA !?@020RightCTEA$ 8<A:>5 2 0@01A:>5Roman to ArabicCTEA 0?CA:RunCTEA&0?CAB8BL ?@>3@0<<C Run programCTEA!>E@0=8BLSaveCTEA0!>E@0=8BL 70?0A=CN :>?8N Save .bakCTEA!>E@0=8BL :0:Save AsCTEAR!>E@0=8BL 2A5 ACI5AB2CNI85 >B:@KBK5 D09;KSave all existingCTEA!>E@0=8BL :0:Save asCTEA!>E@0=8BL 5I5 Save moreCTEA0?><=8BL <5AB> Save positionCTEA"!>E@0=8BL ?@>D8;L Save profileCTEA !>E@0=8BL A5AA8N Save sessionCTEA~!>E@0=8BL B5:CI89 >B:@KBK9 D09; ?> 8<5=8, ?@54>AB02;5==><C 2KH59Save the current opened file with the name provided aboveCTEA4!>E@0=8BL 4>:C<5=B =0 48A:Save the document to diskCTEA:!>E@0=8BL 4>:C<5=B ?>4 8<5=5<"Save the document under a new nameCTEA.!=0G0;0 A>E@0=8B5 D09;!Save the file first!CTEA6!>E@0=8BL 25@A8N ?> 2@5<5=8Save timestamped versionCTEA!>E@0=5=>: %1 Saved: %1CTEA6@>87=5AB8 2K45;5==K9 B5:ABSay the selected textCTEA40AHB018@>20BL 2 ?@>F5=B0EScale by percentagesCTEA20AHB018@>20BL ?> AB>@>=5 Scale by sideCTEA.0AHB018@>20BL :0@B8=:C Scale imageCTEAZ0AHB018@>20BL :0@B8=:8 A 18;8=59=K< D8;LB@><$Scale images with bilinear filteringCTEA!:@8?BKScriptsCTEA >8A:SearchCTEAB>38 ?>8A:0Search resultsCTEA IC... Searching...CTEAK1@0BLSelectCTEAK1@0BL 2AQ Select allCTEA(B<5B8BL ?> @53M:A?CSelect by regexpCTEA6K45;8B5 A=0G0;0 8<O D09;0!Select the file name first!CTEA !5AA88SessionsCTEAD#AB0=>28BL :>=5F AB@>:8 :0: 2 UNIXSet UNIX end of lineCTEAJ#AB0=>28BL :>=5F AB@>:8 :0: 2 WindowsSet Windows end of lineCTEAB><5B8BL :0: 02B>A>E@0=O5<K9 D09;Set as autosaving fileCTEA:#AB0=>28BL :0: D09; E@0=8;8I0Set as storage fileCTEAZ#AB0=>28BL AB0@K9 0:>2A:89 :>=5F AB@>:8 (CR)Set old Mac end of line (CR)CTEA !>G5B0=85 :;028HShortcutCTEA8B>1@060BL ?>;>65=85 :C@A>@0Show cursor positionCTEAJ>:07K20BL 'M;5:B@>==K5 :=838 :;0AA=>Show ebooks fineCTEAN>:07K20BL ?>;=K9 ?CBL 2 703>;>2:5 >:=0Show full path at window titleCTEA.>:07K20BL =><5@0 AB@>:Show line numbersCTEA*>:07K20BL 3@0=8FC =0Show margin atCTEA2>:07K20BL B01K 8 ?@>15;KShow tabs and spacesCTEA 07<5@ AB>@>=KSize of the sideCTEA!=8??5BKSnippetsCTEA!>@B8@>2:0SortCTEA(!>@B8@>20BL ?> 4;8=5Sort by lengthCTEA<!>@B8@>20BL 157 CGQB0 @538AB@0Sort case insensitivelyCTEA8!>@B8@>20BL CG8BK20O @538AB@Sort case sensitivelyCTEA`!>@B8@>20BL 7028A8<> >B @538AB@0, ?> @0745;8B5;N%Sort case sensitively, with separatorCTEAf#?>@O4>G8BL B01;8FC ?> AB>;1FC 2 0;D028B=>< ?>@O4:5Sort table by column ABCCTEA >;>A Speach voiceCTEA72CG820=85SpeechCTEAJ0G8BK20=85 2A;CE - ?0C70/2>7>1=>28BLSpeech pause/resumeCTEA,@>25@8BL ?@02>?8A0=85 Spell checkCTEA8286>: ?@>25@:8 ?@02>?8A0=8OSpell checker engineCTEA*@>25@:0 ?@02>?8A0=8OSpell checkingCTEA6/7K:8 ?@>25@:8 ?@02>?8A0=8OSpell-checker languagesCTEA>545;O =0G8=05BAO 2 2>A:@5A5=L5Start week on SundayCTEAV0E20B8BL/=5B 1CD5@ >1<5=0 2 D09; E@0=8;8I0,Start/stop capture clipboard to storage fileCTEA$>25@E 4@C38E >:>= Stay on topCTEA,AB0=>28BL 70G8BK20=85 Stop speechCTEAB@O<K5 42>9=K5 2 =86=85 8 25@E=85Straight to curly double quotesCTEA0@O<K5 42>9=K5 2 5;>G:>9Straight to double angle quotesCTEA,G8AB8BL >B HTML-BM3>2Strip HTML tagsCTEAV!C1B8B@K: A428=CBL B09<:>4K =0 <8;;8A5:C=4K#Subtitles: shift timecodes by msecsCTEA@54?>;>68BLSuggestCTEA:!;>68BL ?> ?>A;54=5<C AB>;1FCSum by last columnCTEA.><5=OBL <5AB0<8 OG59:8 Swap cellsCTEA:>4A25B:0 A8=B0:A8A0 2:;NG5=0Syntax highlighting enabledCTEA :>=:0 ?@>3@0<<KTEA program iconCTEA$(01;>= ?@>5:B0 TEATEA project templateCTEA,(8@8=0 B010 2 ?@>15;0ETab width in spacesCTEA"01;8FKTablesCTEA(01;>=K TemplatesCTEAd@5<5==> A>E@0=OBL =5A>E@0=5==K5 1CD5@K ?@8 2KE>45&Temporary save unsaved buffers on exitCTEA "5:ABTextCTEA(!B0B8AB8:0 ?> B5:ABCText statisticsCTEA&7 B5:AB0 2 [X]HTMLText to [X]HTMLCTEA=0<5=8B>5 ?>;5 22>40 () - 4;O ?>8A:0 8 70<5=K, 4;O ?0@0<5B@>2 DC=:F89CThe famous input field. Use for search/replace, function parametersCTEA0$09;-E@0=8;8I5 =5 7040=.&The storage file is closed or not set.CTEA"5<KThemesCTEA(%1 AB@>: 2 %2 D09;0EThere are %1 lines at %2 filesCTEA 5B D09;0 AB8;59There is no stylesheet fileCTEA-B>B D09; >B:@KB 2 @568<5 B>;L:> 4;O GB5=8O. K <>65B5 A>E@0=8BL 53> ?>4 4@C38< 8<5=5< ?>A@54AB2>< !>E@0=8BL :0:.aThis file is opened in the read-only mode. You can save it with another name using Save asCTEA @5<OTimeCTEA$>@<0B 2@5<5=8 Time formatCTEA>5@5:;NG8BL ?>;=>M:@0==K9 @568<Toggle fullscreenCTEA<5@5:;NG8BL 703>;>2>:/8AE>4=8:Toggle header/sourceCTEA25@5:;NG8BL ?5@5=>A AB@>:Toggle word wrapCTEA=AB@C<5=BKToolsCTEAN1I89 25A = %1 :8;>109B 2 %2 D09;0E<br>&Total size = %1 kbytes in %2 files
CTEAL/7K: 8=B5@D59A0 (=C65= ?5@570?CA: ")#UI language (TEA restart is needed)CTEAN 568< 8=B5@D59A0 (=C65= ?5@570?CA: ")UI mode (TEA restart is needed)CTEAN!B8;L 8=B5@D59A0 " (=C65= ?5@570?CA:) UI style (TEA restart is needed)CTEAD#" A A>@B8@>2:>9 ?> :>;8G5AB2CUNITAZ quantity sortingCTEA@#" A A>@B8@>2:>9 ?> 0;D028BCUNITAZ sorting by alphabetCTEA:#" A A>@B8@>2:>9 ?> 4;8=5UNITAZ sorting by lengthCTEAT#": #25@A0;L=K9 "5:AB>2K9 =0;80B>@ UNITAZ: UNIverlsal Text AnalyZerCTEA %UPCASECTEA6B<5=8BL >BABC? (shift-tab)Un-indent (shift+tab)CTEA>4G5@:=CBK9 UnderlineCTEAB<5=8BLUndoCTEA!=OBL ?><5B:8UnmarkCTEABA:;NG8BL D09; 87 02B>A>E@0=O5<KEUnset the autosaving fileCTEA 25@ECUpCTEAhA?>;L7>20BL >@85=B0F8N 87 EXIF 2 A<>B@5;:5 :0@B8=>:$Use EXIF orientation at image viewerCTEA\A?>;L7>20BL 2=5H=NN A<>B@5;:C :0@B8=>: 4;O F2 Use external image viewer for F2CTEAVA?>;L7>20BL 46>9AB8: :0: :C@A>@=K5 :;028H8Use joystick as cursor keysCTEABA?>;L7>20BL ?@>15;K 2<5AB> B01>2Use spaces instead of tabsCTEA`A?>;L7>20BL B@048F8>==K5 >:=0 B:@KBL/!>E@0=8BL&Use traditional File Save/Open dialogsCTEA84ViewCTEA*0AB@>9:8 251-30;5@58Web gallery optionsCTEA&5;K5 A;>20 Whole wordsCTEA;8=0 A;>20: Word length: CTEA5@5=>A AB@>: Word wrapCTEA;8=K A;>2 Words lengthsCTEA>4KYearsCTEA|K ?KB05B5AL ?5@58<5=>20BL >B:@KBK9 D09;, 70:@>9B5 53> A=0G0;0@You are trying to rename the opened file, please close it first!CTEA,>1028BL =C;8 : 8<5=0<Zero pad file namesCTEA&=AB@C<5=BK [X]HTML [X]HTML toolsCTEA@07@O4=>ABL: %1bits per sample: %1CTEA45 <>3C >B:@KBL %1, 81> %2cannot open %1 because of: %2CTEAA8<2>;>2: %1<br>A8<2>;>2 157 ?@>15;>2: %2<br>AB@>:: %3<br>02B>@A:8E ;8AB>2: %4Ichars: %1
chars without spaces: %2
lines: %3
author's sheets: %4CTEA:>=D838configsCTEA40BKdatesCTEA.5A 3@04 > 3@04 <8= A5:dec degrees > deg min secCTEA.@04 <8= A5: > 45A 3@04deg min sec > dec degreesCTEA=5 A>2?0405Bdoes notCTEA ?@02:0editorCTEA,?@>H;> <8;;8A5:C=4: %1elapsed milliseconds: %1CTEA*7=0<5=8B>5 ?>;5 22>40famous input fieldCTEA8<O D09;0: %1 file name: %1CTEA D09;KfilesCTEA?@028;>AL: %1last modified: %1CTEA;>3<5<>logmemoCTEA =86=89 lower caseCTEA@C:>2>4AB2>manualCTEAA>2?0405BmatchedCTEA =>2K9newCTEA new_directoryCTEA=>2K9 ?@>D8;L new_profileCTEA=>20O A5AA8O new_sessionCTEA=5 =0945=>! not found!CTEA,:>;8G5AB2> :0=0;>2: %1number of channels: %1CTEA=0;04:0optionsCTEA?8H8B5 A2>8 70<5B:8 (: MB><C D09;C) 745AL, 8 >=8 1C4CB 02B><0B8G5A:8 A>E@0=5=KHput your notes (for this file) here and they will be saved automaticallyCTEA*G0AB>B0 >F8D@>2:8: %1sample rate: %1CTEAA:@8?BKscriptsCTEA"@07<5@: %1 kbytessize: %1 kbytesCTEA0A=8??5B %1 =5 ACI5AB2C5Bsnippet %1 is not existsCTEAA=8??5BKsnippetsCTEAAC<<0: %1sum: %1CTEAB01;8FKtablesCTEAH01;>=K templatesCTEA*0=0;87 B5:AB0 4;O: %1text analysis of: %1CTEA,2A53> / C=8:0;L=KE: %1total / unique: %1CTEA@2A53> : C=8:0;L=K<, ?@>F5=BK: %1!total to unique per cent diff: %1CTEAA;>2 2A53>: %1words total: %1CTEA6A;>2, =5 AG8B0O ?>2B>@K: %1words unique: %1CTEAL2K <>65B5 70=>A8BL AN40 2AO:85 70<5B:8you can put here notes, etcCTEAT!>E@0=5=85 2 MB>B D>@<0B =5 ?>445@68205BAO'Saving for this format is not supported CTioReadOnly&=8<0=85! =8<0=85!Attention! Attention!CTodo"<0AHB018@>20=> :: scaled to:  CZORWindow7<5=5=> Modified atQObject<ONameQObjectp#AB0=>28BL A;>20@8 4;O ?@>25@O;LI8:0 2 0;04:0 - $C=:F88EPlease set up spell checker dictionaries at Options - Functions pageQObject  07<5@SizeQObject ) , tea-qt-63.3.0/translations/ru.ts000066400000000000000000003075731476733534200165520ustar00rootroot00000000000000 CAboutWindow Code Код Acknowledgements Благодарности Translations Переводы About О программе CDarkerWindow Darker palette Палитра темнее CDocument cannot open %1 because of: %2 Не могу открыть %1, ибо %2 cannot save %1 because of: %2 не могу сохранить %1, ибо %2 %1 is saved %1 сохранён %1 has been modified. Do you want to save your changes? %1 был изменен. Желаете сохранить изменения? Copy Копировать Cut Вырезать Paste Вставить Undo Отменить Redo Переделать %1 is open %1 открыт new[%1] новый[%1] CImgViewer preview просмотр CTEA The famous input field. Use for search/replace, function parameters Знаменитое поле ввода (ЗПВ) - для поиска и замены, для параметров функций FIF ЗПВ editor правка logmemo логмемо famous input field знаменитое поле ввода All (*);;Text files (*.txt);;Markup files (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Все (*);;Текстовые файлы (*.txt);;Файлы разметки (*.xml *.html *.htm *.);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Charset Кодировка %1 already exists Do you want to overwrite? %1 уже существует Желаете перезаписать? Test Проверка Files Файлы Labels Метки New Новый Create a new file Создать новый файл Open file Открыть файл Open an existing file Открыть существующий файл Save Сохранить Save the document to disk Сохранить документ на диск Save As Сохранить как Save the document under a new name Сохранить документ под именем Exit Выход Exit the application Выйти из программы Cut Вырезать Cut the current selection's contents to the clipboard Вырезать текущее выделение в буфер обмена Copy Копировать Copy the current selection's contents to the clipboard Копировать текущее выделение в буфер обмена Paste Вставить Paste the clipboard's contents into the current selection Вставить из буфера обмена Undo Отменить Redo Переделать About О программе About Qt О Qt File Файл Open Открыть Last closed file Последний закрытый файл Open at cursor Открыть под курсором Crapbook Фигня Notes Заметки Save as different Сохранить иначе Save .bak Сохранить запасную копию Save timestamped version Сохранить версию по времени Save session Сохранить сессию File actions Действия над файлом Reload Перечитать Reload with encoding Перечитать в кодировке Set UNIX end of line Установить конец строки как в UNIX Set Windows end of line Установить конец строки как в Windows Set old Mac end of line (CR) Установить старый Маковский конец строки (CR) Recent files Последние файлы Bookmarks Закладки Edit bookmarks Править закладки Add to bookmarks Добавить в закладки Find obsolete paths Найти ошибочные пути Templates Шаблоны Sessions Сессии Configs Конфиги Bookmarks list Список закладок Programs list Список программ Do not add to recent Не добавлять в Последние файлы Print Печатать Close current Закрыть текущий файл Edit Правка Block start Начало блока Block end Конец блока Copy block Копировать блок Paste block Вставить блок Cut block Вырезать блок Copy current file name Копировать имя текущего файла Indent (tab) Отступ (tab) Un-indent (shift+tab) Отменить отступ (shift-tab) Indent by first line Отступ по первой строке Comment selection Закомментировать выделенное Set as storage file Установить как файл хранилища Copy to storage file Копировать в файл хранилища Start/stop capture clipboard to storage file Захватить/нет буфер обмена в файл хранилища Markup Вёрстка Mode Режим Header Заголовок Align Выравнивание Center Середина Left Слева Right Справа Justify По ширине Bold Жирный Italic Наклонный Underline Подчеркнутый Link Ссылка Paragraph Параграф Color Цвет Break line Перенос строки Non-breaking space Неразрывный пробел Insert image Вставить картинку [X]HTML tools Инструменты [X]HTML Text to [X]HTML Из текста в [X]HTML Convert tags to entities Перевести тэги в сущности Antispam e-mail Кодировать адрес e-mail супротив спама Document weight Взвесить документ Preview selected color Смотреть выделенный цвет Strip HTML tags Очистить от HTML-тэгов Rename selected file Переименовать выделенный файл Search Поиск Find Найти Find next Найти дальше Find previous Найти назад Save more Сохранить еще Save all existing Сохранить все существующие открытые файлы Find in files Найти в файлах Replace with Заменить на Replace all Заменить всё Replace all in opened files Заменить всё в открытых файлах Mark all found Пометить всё найденное Unmark Снять пометки Case sensitive Чуткость к регистру Whole words Целые слова From cursor От курсора Regexp mode В режиме регулярных выражений Fuzzy mode Нечеткое условие Functions Функции Repeat last Повторить последнее Tools Инструменты Scale image Масштабировать картинку Snippets Сниппеты Scripts Скрипты Tables Таблицы Place Поместить TEA project template Шаблон проекта TEA HTML template Шаблон HTML HTML5 template Шаблон HTML5 C++ template Шаблон C++ C template Шаблон C Date Дата Time Время Case Регистр UPCASE ВЕРХНИЙ lower case нижний Sort Сортировка Sort case sensitively Сортировать учитывая регистр Sort case insensitively Сортировать без учёта регистра Sort case sensitively, with separator Сортировать зависимо от регистра, по разделителю Sort by length Сортировать по длине Flip a list Перевернуть список Flip a list with separator Перевернуть список, размежеванный разделителями Cells Ячейки Sort table by column ABC Упорядочить таблицу по столбцу в алфавитном порядке Swap cells Поменять местами ячейки Delete by column Удалить по столбцу Copy by column[s] Копировать по столбцу(цам) Filter Фильтр Remove duplicates Удалить повторения Remove empty lines Удалить пустые строки Remove lines < N size Удалить строки < размера N Remove lines > N size Удалить строки > размера N Remove before delimiter at each line Удалить перед разделителем в каждой строке Remove after delimiter at each line Удалить после разделителя в каждой строке Filter with regexp Фильтровать по регэкспу Filter by repetitions Фильтровать повторы Math Математика Evaluate Вычислить Arabic to Roman Арабское в римское Roman to Arabic Римское в арабское Decimal to binary Десятичное в двоичное Binary to decimal Двоичное в десятичное Flip bits (bitwise complement) Перевернуть биты Enumerate Нумеровать Sum by last column Сложить по последнему столбцу deg min sec > dec degrees Град мин сек > дес град dec degrees > deg min sec Дес град > град мин сек Morse code Азбука Морзе From Russian to Morse Из русского в морзянку From Morse To Russian Из морзянки в русский From English to Morse С английского в код Морзе From Morse To English Из кода Морзе на английский Analyze Анализ Text statistics Статистика по тексту Extract words Извлечь слова Words lengths Длины слов Count the substring Подсчитать вхождение подстроки Count the substring (regexp) Подсчитать вхождение подстроки (регэксп) UNITAZ quantity sorting УНИТАЗ с сортировкой по количеству UNITAZ sorting by alphabet УНИТАЗ с сортировкой по алфавиту UNITAZ sorting by length УНИТАЗ с сортировкой по длине Text Текст Apply to each line Применить к каждой строке Remove formatting Удалить форматирование Remove formatting at each line Удалить форматирование в каждой строке Compress Сжать Anagram Анаграмма Remove trailing spaces Удалить хвостовые пробелы All (*);;Text files (*.txt);;Markup files (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) Все (*);;Обычный текст (*.txt);;Размётка (*.xml *.html *.xhtml *.htm *.md);;C/C++ (*.c *.h *.cpp *.hh *.c++ *.h++ *.cxx) <b>TEA %1</b> by Peter Semiletov | tea.ourproject.org<br>Support TEA via PayPal: peter.semiletov@gmail.com<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-git <b>TEA %1</b> от Петра Семилетова | tea.ourproject.org<br>Поддержать TEA через PayPal: peter.semiletov@gmail.com<br>Git: github.com/psemiletov/tea-qt<br>AUR: aur.archlinux.org/packages/tea-qt-git Select all Выбрать всё Case inverse Перевернуть регистр Subtitles: shift timecodes by msecs Субтитры: сдвинуть таймкоды на миллисекунды Escape regexp Escape regexp Reverse Перевернуть Compare two strings Сравнить два слова Quotes Кавычки Straight to double angle quotes Прямые двойные в елочкой Straight to curly double quotes Прямые двойные в нижние и верхние LaTeX: Straight to curly double quotes LaTeX: прямые двойные в нижние и верхние LaTeX: Straight to double angle quotes LaTeX: прямые двойные в елочкой вар.1 LaTeX: Straight to double angle quotes v2 LaTeX: прямые двойные в елочкой вар.2 Say the selected text Произнести выделенный текст Speech pause/resume Зачитывание вслух - пауза/возобновить Stop speech Остановить зачитывание Spell-checker languages Языки проверки правописания Spell check Проверить правописание Suggest Предположить Add to dictionary Добавить в словарь Remove from dictionary Удалить из словаря Calendar Календарь Moon mode on/off Лунный режим вкл/выкл Mark first date Пометить первую дату Mark last date Пометить последнюю дату Add or subtract Добавить или вычесть Days Дни Months Месяцы Years Годы Go to current date Перейти к текущей дате Calculate moon days between dates Вычислить лунные дни между датами Number of days between two dates Количество дней между двумя датами Remove day record Удалить данные о дне Run Запуск IDE ИДЕ Run program Запустить программу Build program Собрать программу Clean program Очистить сборку Toggle header/source Переключить заголовок/исходник Nav Нав Save position Запомнить место Go to saved position К сохранённому месту Go to line К строке Next tab Следующая вкладка Prev tab Предыдущая вкладка Focus the Famous input field Фокус в Знаменитое поле ввода Focus the editor Фокус в редактор Refresh labels Обновить метки Current files Текущие файлы Fm Фп Multi-rename Переименование Zero pad file names Добавить нули к именам Delete N first chars at file names Удалить первые N символов из имен Replace in file names Заменить в именах файлов Apply template Применить шаблон File operations Действия над файлами Create new directory Создать новый каталог Rename Переименовать Delete file Удалить файл File information Сведения о файле Count lines in selected files Подсчитать строки в выбранных файлах Full info Полные сведения ZIP ZIP Create new ZIP Создать новый ZIP Add to ZIP Добавить в ZIP Save ZIP Сохранить ZIP List ZIP content Показать содержимое ZIP Unpack ZIP to current directory Распаковать ZIP в текущий каталог Images Картинки Scale by side Масштабировать по стороне Scale by percentages Масштабировать в процентах Create web gallery Создать веб-галерею Go to home dir В домашний каталог Refresh current dir Обновить текущий каталог Preview image Смотреть картинку Select by regexp Отметить по регэкспу Deselect by regexp Снять выделение по регэкспу View Вид Themes Темы Palettes Палитры Profiles Профили Preview Markdown Предпросмотр Markdown Save profile Сохранить профиль Toggle word wrap Переключить перенос строк Hide error marks Скрыть помеченное как ошибки Toggle fullscreen Переключить полноэкранный режим Stay on top Поверх других окон Darker Темнее NEWS Новости License Лицензия not found! не найдено! This file is opened in the read-only mode. You can save it with another name using <b>Save as</b> Этот файл открыт в режиме только для чтения. Вы можете сохранить его под другим именем посредством Сохранить как. %1 is saved The storage file is closed or not set. Файл-хранилище не задан. You are trying to rename the opened file, please close it first! Вы пытаетесь переименовать открытый файл, закройте его сначала matched совпадает does not не совпадает Set as autosaving file Пометить как автосохраняемый файл Unset the autosaving file Исключить файл из автосохраняемых Capitalize sentences Предложения с большой буквы Subtitles: shift timecode by msecs Субтитры: сдвинуть таймкоды на миллисекунды Check regexp match Проверить совпадение по регэкспу Keyboards Клавиатуры options наладка Classic Классика Docked Стыковка UI mode (TEA restart needed) Режим интерфейса (ТИА надо будет перезапустить) UI language (TEA restart needed) Язык интерфейса (ТИА надо будет перезапустить) UI style (TEA restart needed) Стиль интерфейса (ТИА надо будет перезапустить) Interface font Шрифт интерфейса Editor font Шрифт редактора Logmemo font Шрифт логмемо Up Вверху Bottom Снизу GUI tabs align Выравнивание вкладок интерфейса Documents tabs align Положение вкладок документов Icons size Размер иконок TEA program icon Иконка программы FIF at the top (restart needed) ЗПВ наверху (нужен перезапуск) Show line numbers Показывать номера строк Word wrap Перенос строк Show tabs and spaces Показывать табы и пробелы Syntax highlighting enabled Подсветка синтаксиса включена Highlight current line Подсвечивать текущую строку Highlight paired brackets Подсвечивать парные скобки Automatic indent Автоматический отступ Use spaces instead of tabs Использовать пробелы вместо табов Tab width in spaces Ширина таба в пробелах Show cursor position Отображать положение курсора Cursor center on scroll Курсор по центру при прокрутке Cursor blink time (msecs, zero is OFF) Время мигания курсора (мс, ноль - выключить) Cursor width Ширина курсора Show margin at Показывать границу на Show full path at window title Показывать полный путь в заголовке окна Interface Интерфейс Use Alt key to access main menu Использовать клавишу Alt для доступа к главному меню Use Left Alt + WASD as additional cursor keys Использовать левый Alt - WASD как доп. курсорные Use joystick as cursor keys Использовать джойстик как курсорные клавиши Automatic preview images at file manager Авто-показ картинок в файловом приказчике Restore the last session on start-up Загружать последнюю сессию при запуске Use Enca for charset detection Использовать Enca для определения кодировки Use external image viewer for F2 Использовать внешнюю смотрелку картинок для F2 Use traditional File Save/Open dialogs Использовать традиционные окна Открыть/Сохранить Start week on Sunday Неделя начинается в воскресенье Northern hemisphere Северное полушарие Moon phase algorithm Алгоритм фазы луны 0 - Trigonometrical v2, 1 - Trigonometrical v1, 2 - Conway, 3 - Leueshkanov 0 - Тригонометрический в.2, 1 - Тригонометрический в.1, 2 - Конвэй, 3 - Леюшканов Charset for file open from command line Кодировка для открытия файла из командной строки ZIP unpacking: file names charset ZIP-распаковка: кодировка имен файлов ZIP packing: file names charset ZIP-упаковка: кодировка имен файлов Autosaving Автосохранение Temporary save unsaved buffers on exit Временно сохранять несохраненные буферы при выходе Autosave buffers and autosaving files (each N seconds) Сохранять буферы и автосохранямые файлы каждые N секунд Common Общие Label starts with: Метка начинается с: Label ends with: Метка завершается: Date and time Дата и время Date format Формат даты Time format Формат времени Spell checking Проверка правописания Spell checker engine Движок проверки правописания Hunspell dictionaries directory Каталог со словарями для Hunspell Select Выбрать Aspell directory Папка Aspell Miscellaneous Разное Fuzzy search factor Коэффициент поиска по нечеткому условию Show ebooks fine Показывать 'электронные книги классно Image conversion output format Выходной формат преобразованных картинок Scale images with bilinear filtering Масштабировать картинки с билинейным фильтром Output images quality Выходное качество картинок Apply hard rotation by EXIF data Применять вращение согласно данным EXIF Zip directory with processed images Упаковать каталог с преобразованными картинками Web gallery options Настройки веб-галереи Size of the side Размер стороны Link options Настройки ссылки Columns per row Столбцов в ряду EXIF Use EXIF orientation at image viewer Использовать ориентацию из EXIF в смотрелке картинок Enable locale only voices Показывать только голоса текущей локали Speach voice Голос Speech Озвучивание Shortcut Сочетание клавиш Assign Назначить Remove Удалить привязку Keyboard Клавиатура manual руководство you can put here notes, etc вы можете заносить сюда всякие заметки snippet %1 is not exists сниппет %1 не существует Print document Печатать документ %p% completed %p% выполнено elapsed milliseconds: %1 прошло миллисекунд: %1 %1 - saved %1 - сохранён Cannot save %1 Не могу сохранить %1 chars: %1<br>chars without spaces: %2<br>lines: %3<br>author's sheets: %4 символов: %1<br>символов без пробелов: %2<br>строк: %3<br>авторских листов: %4 %1 is processed and saved %1 обработан и сохранен #external programs list. example: opera="C:\Program Files\Opera\opera.exe " "%s" #список внешних программ. вот пример: opera="C:\Program Files\Opera\opera.exe " "%s" #external programs list. example: ff=firefox file:///%s #список внешних приграмм. например: ff=firefox file:///%s Save the file first! Сначала сохраните файл! templates шаблоны snippets сниппеты scripts скрипты tables таблицы configs конфиги Enter the name Введите имя Name: Имя: new_directory new_session новая сессия <span style="%1">COLOR SAMPLE</span> <span style="%1">ОБРАЗЕЦ ЦВЕТА</span> Name Имя Go Перейти Home Домой Refresh Освежить Guess encoding! Предположить кодировку! Save as Сохранить как <b>Bookmarks</b> <b>Закладки</b> files файлы new новый Are you sure to delete %1? Вы уверены, что хотите удалить %1? End of line: Конец строки: file name: %1 имя файла: %1 size: %1 kbytes размер: %1 kbytes last modified: %1 правилось: %1 bits per sample: %1 разрядность: %1 number of channels: %1 количество каналов: %1 sample rate: %1 частота оцифровки: %1 RMS for all channels: %1 dB RMS для всех каналов: %1 dB UI mode (TEA restart is needed) Режим интерфейса (нужен перезапуск ТИА) UI language (TEA restart is needed) Язык интерфейса (нужен перезапуск ТИА) UI style (TEA restart is needed) Стиль интерфейса ТИА (нужен перезапуск) FIF at the top (restart is needed) ЗПВ наверху (требуется перезапуск ТИА) Enable speech (TEA restart is needed) Разрешить чтение вслух (нужен перезапуск ТИА) Actions Действия Open a file from the file name provided above Открыть файл по имени файла предоставленному выше Save the current opened file with the name provided above Сохранить текущий открытый файл по имени, предоставленному выше GTK Bookmarks: Закладки GTK: total to unique per cent diff: %1 всего к уникальным, проценты: %1 total / unique: %1 всего / уникальных: %1 words unique: %1 слов, не считая повторы: %1 words total: %1 слов всего: %1 text analysis of: %1 анализ текста для: %1 UNITAZ: UNIverlsal Text AnalyZer УНИТАЗ: УНИверсальный Текстовый АналиЗатор %1 number of occurrences of %2 is found %1 вхождений %2 найдено Enter the archive name Введите имя архива new_archive new_archive Open Directory Открыть каталог Getting files list... Получение списка файлов... Searching... Ищу... cannot open %1 because of: %2 Не могу открыть %1, ибо %2 Search results Итоги поиска new_profile новый профиль There are %1 lines at %2 files %1 строк в %2 файлах Enca is not installed, falling back to the built-in detection Enca не установлена, откатываюсь ко встроенному алгоритму определения Enter your daily notes here. To use time-based reminders, specify the time signature in 24-hour format [hh:mm], i.e.: [06:00]good morning! [20:10]go to theatre Введите тут свои заметки. Чтобы включить напоминалку, используйте временные пометки в 24-часовом формате [чч:мм], то есть: [06:00]доброе утро! [20:10]выйти за хлебом, когда всё закрыто Enter your daily notes here. Можете вписать тут свои заметки. dates даты Select the file name first! Выделите сначала имя файла! %1 is not found<br> %1 не найден<br> %1 kbytes %2 <br> %1 килобайт %2 <br> Total size = %1 kbytes in %2 files<br> Общий вес = %1 килобайт в %2 файлах<br> is unpacked распакованы Word length: Длина слова: Number: Количество: put your notes (for this file) here and they will be saved automatically пишите свои заметки (к этому файлу) здесь, и они будут автоматически сохранены There is no stylesheet file Нет файла стилей sum: %1 сумма: %1 Incorrect parameters at FIF Неправильные параметры в FIF Cannot save: %1 Не могу сохранить: %1 Saved: %1 Сохранено: %1 CTioReadOnly Saving for this format is not supported Сохранение в этот формат не поддерживается CTodo Attention! Attention! Внимание! Внимание! CZORWindow scaled to: масштабировано к: QObject Name Имя Size Размер Modified at Изменено Please set up spell checker dictionaries at Options - Functions page Установить словари для проверяльщика в Наладка - Функции QuaZipFile ZIP/UNZIP API error %1 ZIP/UNZIP ошибка API %1